2023-02-09 23:29:20 -05:00
|
|
|
// echo ALGORITHM.adoc | entr sh -c "podman run --rm -it --network none -v "${PWD}:/documents/" asciidoctor/docker-asciidoctor asciidoctor -r asciidoctor-mathematical -a mathematical-format=svg ALGORITHM.adoc; printf 'Done ($(date -Isecond))\n'"
|
|
|
|
|
|
|
|
:toc:
|
|
|
|
:nofooter:
|
|
|
|
:!webfonts:
|
|
|
|
:source-highlighter: rouge
|
|
|
|
:rouge-style: molokai
|
|
|
|
// :stem:
|
|
|
|
:sectlinks:
|
|
|
|
|
|
|
|
= Algorithm
|
|
|
|
|
2023-02-09 23:46:16 -05:00
|
|
|
== Data format
|
|
|
|
|
|
|
|
[source]
|
|
|
|
----
|
|
|
|
Example: Face 2, level 23
|
|
|
|
|
|
|
|
# Most significant 3 bits are for the face
|
|
|
|
face_number = 0b010
|
|
|
|
|
|
|
|
# This algorithm is always level 23
|
|
|
|
data_bits = level * 2 = 23 * 2 = 46
|
|
|
|
|
|
|
|
# The bit after the data bits is always 1
|
|
|
|
# All subsequent bits are always 0
|
|
|
|
|
|
|
|
Bit : 64 48 32 16 1
|
|
|
|
: | | | | |
|
|
|
|
: 01001011101010001011100010010011 1001001100100l001100000000000000
|
|
|
|
Face number : ^^^
|
|
|
|
Data bits : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
|
|
|
|
Bit after data bits (1) : ^
|
|
|
|
All remaining bits (0) : ^^^^^^^^^^^^^^
|
|
|
|
----
|
|
|
|
|
|
|
|
[source]
|
|
|
|
----
|
|
|
|
All remaining bits (0) : vvvvvvvvvvvvvv
|
|
|
|
Bit after data bits (1) : v
|
|
|
|
Data bits : vvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvv
|
|
|
|
Face number : vvv
|
|
|
|
Bit : 64 48 32 16 1
|
|
|
|
: | | | | |
|
|
|
|
: 01001011101010001011100010010011 1001001100100l001100000000000000
|
|
|
|
Not represented : ^^^^^^^^^^^^^^^
|
|
|
|
0000 (10 bits) : ^^^^^^^^^^
|
|
|
|
WORD1 (13 bits) : ^^^^^^ ^^^^^^^
|
|
|
|
WORD2 (13 bits) : ^^^^^^^^^^^^^
|
|
|
|
WORD3 (13 bits) : ^^^^^^^^^^^^^
|
|
|
|
----
|
|
|
|
|
|
|
|
Alternative format (generated using link:https://github.com/luismartingarcia/protocol[protocol^])
|
|
|
|
|
|
|
|
[source]
|
|
|
|
----
|
|
|
|
./protocol 'Face:3,Data:46,1:1,0:14?bits=32'
|
|
|
|
0 1 2 3
|
|
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
| Face| Data |
|
|
|
|
+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
| |1| 0 |
|
|
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
----
|
|
|
|
|
|
|
|
[source]
|
|
|
|
----
|
|
|
|
./protocol 'WORD3:13,WORD2:13,WORD1:13,Number:10?bits=32'
|
|
|
|
0 1 2 3
|
|
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
| WORD3 | WORD2 | |
|
|
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
| WORD1 | Number |
|
|
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
----
|
|
|
|
|
|
|
|
|
2023-02-09 23:29:20 -05:00
|
|
|
== Encoding
|
2023-02-09 23:46:16 -05:00
|
|
|
|
2023-02-09 23:29:20 -05:00
|
|
|
[%header,cols="2m,5a"]
|
|
|
|
|===
|
|
|
|
|Step |Code
|
|
|
|
|
|
|
|
|(lat, lon)
|
|
|
|
|Given
|
|
|
|
|
|
|
|
|(x, y, z)/Point
|
|
|
|
| [source,rust]
|
|
|
|
----
|
|
|
|
fn lat_lon_to_xyz(lat: f64, lon: f64) -> (f64, f64, f64) {
|
|
|
|
let x = cos(lon) * cos(lat);
|
|
|
|
let y = sin(lon) * cos(lat);
|
|
|
|
let z = sin(lat);
|
|
|
|
|
|
|
|
(x, y, z)
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
|(face, u, v)
|
|
|
|
| [source,rust]
|
|
|
|
----
|
|
|
|
fn face(x: f64, y: f64, z: f64) -> u8 {
|
|
|
|
let (x_abs, y_abs, z_abs) = (x.abs(), y.abs(), z.abs());
|
|
|
|
let mut id = 0;
|
|
|
|
let mut value = x;
|
|
|
|
if y_abs > x_abs {
|
|
|
|
id = 1;
|
|
|
|
value = y;
|
|
|
|
}
|
|
|
|
if z_abs > value.abs() {
|
|
|
|
id = 2;
|
|
|
|
value = z;
|
|
|
|
}
|
|
|
|
if value < 0. {
|
|
|
|
id += 3;
|
|
|
|
}
|
|
|
|
id
|
|
|
|
}
|
|
|
|
|
|
|
|
fn xyz_to_fuv(x: f64, y: f64, z: f64) -> (u8, f64, f64) {
|
|
|
|
let f: u8 = face(x, y, z);
|
|
|
|
let (u, v) = match face {
|
|
|
|
0 => (y / x, z / x),
|
|
|
|
1 => (-x / y, z / y),
|
|
|
|
2 => (-x / z, -y / z),
|
|
|
|
3 => (z / x, y / x),
|
|
|
|
4 => (z / y, -x / y),
|
|
|
|
5 => (-y / z, -x / z),
|
|
|
|
_ => panic!("Face {f} out of bounds"),
|
|
|
|
};
|
|
|
|
|
|
|
|
(f, u, v)
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
|(face, s, t)
|
|
|
|
| [source,rust]
|
|
|
|
----
|
|
|
|
pub fn u_or_v_to_s_or_t(u_or_v: f64) -> f64 {
|
|
|
|
if u_or_v >= 0.0 {
|
|
|
|
0.5 * (1.0 + 3.0 * u_or_v).sqrt()
|
|
|
|
} else {
|
|
|
|
1.0 - 0.5 * (1.0 - 3.0 * u_or_v).sqrt()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fuv_to_fst(f: u8, u: u64, v: u64) -> (u8, f64, f64) {
|
|
|
|
let s = u_or_v_to_s_or_t(u);
|
|
|
|
let t = u_or_v_to_s_or_t(v);
|
|
|
|
|
|
|
|
(f, s, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|(face, i, j)
|
|
|
|
| [source,rust]
|
|
|
|
----
|
|
|
|
fn st_to_ij(s: f64) -> u32 {
|
|
|
|
clamp((MAX_SIZE as f64 * s).floor() as u32, 0, MAX_SIZE_I32 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fst_to_fij(f: u8, s: u64, t: u64) -> (u8, u32, u32) {
|
|
|
|
let i = st_to_ij(s);
|
|
|
|
let j = st_to_ij(t);
|
|
|
|
|
|
|
|
(f, i, j)
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
|(cellid)
|
|
|
|
| [source,go]
|
|
|
|
----
|
|
|
|
func cellIDFromFaceIJ(f, i, j int) CellID {
|
|
|
|
// Note that this value gets shifted one bit to the left at the end
|
|
|
|
// of the function.
|
|
|
|
n := uint64(f) << (posBits - 1)
|
|
|
|
// Alternating faces have opposite Hilbert curve orientations; this
|
|
|
|
// is necessary in order for all faces to have a right-handed
|
|
|
|
// coordinate system.
|
|
|
|
bits := f & swapMask
|
|
|
|
// Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
|
|
|
|
// curve position. The lookup table transforms a 10-bit key of the form
|
|
|
|
// "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
|
|
|
|
// letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
|
|
|
|
// Hilbert curve orientation respectively.
|
|
|
|
for k := 7; k >= 0; k-- {
|
|
|
|
mask := (1 << lookupBits) - 1
|
|
|
|
bits += ((i >> uint(k*lookupBits)) & mask) << (lookupBits + 2)
|
|
|
|
bits += ((j >> uint(k*lookupBits)) & mask) << 2
|
|
|
|
bits = lookupPos[bits]
|
|
|
|
n \|= uint64(bits>>2) << (uint(k) * 2 * lookupBits)
|
|
|
|
bits &= (swapMask \| invertMask)
|
|
|
|
}
|
|
|
|
return CellID(n*2 + 1)
|
|
|
|
}
|
|
|
|
----
|
|
|
|
[source,rust]
|
|
|
|
----
|
|
|
|
fn fij_to_cellid(f: u8, s: u32, t: u32) -> u64 {
|
|
|
|
let mut n = u64::from(f) << (POS_BITS - 1);
|
|
|
|
let mut bits = u32::from(f & SWAP_MASK);
|
|
|
|
|
|
|
|
let mut k = 7;
|
|
|
|
let mask = (1 << LOOKUP_BITS) - 1;
|
|
|
|
loop {
|
|
|
|
bits += ((i >> (k * LOOKUP_BITS)) & mask) << (LOOKUP_BITS + 2);
|
|
|
|
bits += ((j >> (k * LOOKUP_BITS)) & mask) << 2;
|
|
|
|
bits = LOOKUP_POS[bits as usize] as u32;
|
|
|
|
n \|= ((bits >> 2) as u64) << ((k * 2 * LOOKUP_BITS) as u64);
|
|
|
|
bits &= u32::from(SWAP_MASK \| INVERT_MASK);
|
|
|
|
|
|
|
|
if k == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
k -= 1;
|
|
|
|
}
|
|
|
|
n * 2 + 1
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
|(number, word1, word2, word3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|===
|
|
|
|
|
|
|
|
== Decoding
|
|
|
|
[%header,cols="m,a,"]
|
|
|
|
|===
|
|
|
|
|Step |Formula |Description
|
|
|
|
|
|
|
|
|(number, word1, word2, word3)
|
|
|
|
|Given
|
|
|
|
|
|
|
|
|
|
|
|
|
|(cellid)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|(face, i, j)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|(face, s, t)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|(face, u, v)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|(x, y, z)/Point
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|(lat, lon)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|===
|
|
|
|
|
2023-02-09 23:46:16 -05:00
|
|
|
== Wordlist
|
|
|
|
|
2023-02-09 23:29:20 -05:00
|
|
|
++++
|
|
|
|
<style>
|
|
|
|
#header, #content, #footnotes, #footer {
|
|
|
|
max-width: unset !important;
|
|
|
|
}
|
|
|
|
.hll {
|
|
|
|
background-color: #ff0;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
++++
|