#![doc(html_root_url = "https://docs.rs/hilbert_2d/1.1.0")]
#[macro_use]
mod hilbert_macros;
hilbert_impl! { "8-bit", u8, U8_BITS }
hilbert_impl! { "16-bit", u16, U16_BITS }
hilbert_impl! { "32-bit", u32, U32_BITS }
hilbert_impl! { "64-bit", u64, U64_BITS }
hilbert_impl! { "128-bit", u128, U128_BITS }
hilbert_impl! { "pointer-sized", usize, USIZE_BITS }
pub use crate::usize::{h2xy_discrete, xy2h_discrete};
use crate::usize::{ORDER_MAX, USIZE_BITS};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Variant {
Hilbert,
Moore,
Liu1,
Liu2,
Liu3,
Liu4,
}
const fn next_lut_index(lut_index: usize, quadrant: usize) -> usize {
match quadrant {
0 => lut_index ^ 0b001,
3 => lut_index ^ 0b010,
_ => lut_index,
}
}
const fn next_lut_index_variant(
lut_index: usize,
cur_quadrant: usize,
variant: Variant,
) -> usize {
match variant {
Variant::Moore => match cur_quadrant {
0 | 1 => (lut_index ^ 0b10) ^ 0b111,
_ => (lut_index ^ 0b01) ^ 0b111,
},
Variant::Liu1 => match cur_quadrant {
0 | 3 => (lut_index ^ 0b01) ^ 0b10,
_ => lut_index,
},
Variant::Liu2 => match cur_quadrant {
1 => (lut_index ^ 0b10) ^ 0b111,
2 => (lut_index ^ 0b01) ^ 0b111,
_ => lut_index ^ 0b111,
},
Variant::Liu3 => match cur_quadrant {
0 => lut_index ^ 0b01,
3 => (lut_index ^ 0b01) ^ 0b10,
_ => lut_index,
},
Variant::Liu4 => match cur_quadrant {
0 => lut_index ^ 0b111,
1 => (lut_index ^ 0b10) ^ 0b111,
_ => (lut_index ^ 0b01) ^ 0b111,
},
_ => next_lut_index(lut_index, cur_quadrant),
}
}
pub fn h2xy_continuous_f32(h: f32, variant: Variant) -> (f32, f32) {
let (square_x, square_y) = h2xy_discrete(f32_to_binfrac(h), ORDER_MAX as usize, variant);
(
binfrac_to_f32(square_x << ORDER_MAX),
binfrac_to_f32(square_y << ORDER_MAX),
)
}
pub fn h2xy_continuous_f64(h: f64, variant: Variant) -> (f64, f64) {
let (square_x, square_y) = h2xy_discrete(f64_to_binfrac(h), ORDER_MAX as usize, variant);
(
binfrac_to_f64(square_x << ORDER_MAX),
binfrac_to_f64(square_y << ORDER_MAX),
)
}
pub fn xy2h_continuous_f32(x: f32, y: f32, variant: Variant) -> f32 {
let h = xy2h_discrete(
f32_to_binfrac(x) >> ORDER_MAX,
f32_to_binfrac(y) >> ORDER_MAX,
ORDER_MAX as usize,
variant,
);
binfrac_to_f32(h)
}
pub fn xy2h_continuous_f64(x: f64, y: f64, variant: Variant) -> f64 {
let h = xy2h_discrete(
f64_to_binfrac(x) >> ORDER_MAX,
f64_to_binfrac(y) >> ORDER_MAX,
ORDER_MAX as usize,
variant,
);
binfrac_to_f64(h)
}
fn lowest_decimal_f32() -> f32 {
2.0f32.powi(-(USIZE_BITS as i32))
}
fn lowest_decimal_f64() -> f64 {
2.0f64.powi(-(USIZE_BITS as i32))
}
fn binfrac_to_f32(frac: usize) -> f32 {
lowest_decimal_f32() * (frac as f32)
}
fn binfrac_to_f64(frac: usize) -> f64 {
lowest_decimal_f64() * (frac as f64)
}
fn f32_to_binfrac(dec: f32) -> usize {
(dec / lowest_decimal_f32()) as usize
}
fn f64_to_binfrac(dec: f64) -> usize {
(dec / lowest_decimal_f64()) as usize
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn lut_transition_standard() {
assert_eq!(next_lut_index(0b011, 0), 0b010);
assert_eq!(next_lut_index(0b011, 1), 0b011);
assert_eq!(next_lut_index(0b011, 2), 0b011);
assert_eq!(next_lut_index(0b011, 3), 0b001);
}
#[test]
fn lut_transition_variant() {
assert_eq!(next_lut_index_variant(0b111, 0, Variant::Moore), 0b010);
assert_eq!(next_lut_index_variant(0b111, 1, Variant::Moore), 0b010);
assert_eq!(next_lut_index_variant(0b111, 2, Variant::Moore), 0b001);
assert_eq!(next_lut_index_variant(0b111, 3, Variant::Moore), 0b001);
assert_eq!(next_lut_index_variant(0b111, 0, Variant::Liu1), 0b100);
assert_eq!(next_lut_index_variant(0b111, 1, Variant::Liu1), 0b111);
assert_eq!(next_lut_index_variant(0b111, 2, Variant::Liu1), 0b111);
assert_eq!(next_lut_index_variant(0b111, 3, Variant::Liu1), 0b100);
assert_eq!(next_lut_index_variant(0b111, 0, Variant::Liu2), 0b000);
assert_eq!(next_lut_index_variant(0b111, 1, Variant::Liu2), 0b010);
assert_eq!(next_lut_index_variant(0b111, 2, Variant::Liu2), 0b001);
assert_eq!(next_lut_index_variant(0b111, 3, Variant::Liu2), 0b000);
assert_eq!(next_lut_index_variant(0b111, 0, Variant::Liu3), 0b110);
assert_eq!(next_lut_index_variant(0b111, 1, Variant::Liu3), 0b111);
assert_eq!(next_lut_index_variant(0b111, 2, Variant::Liu3), 0b111);
assert_eq!(next_lut_index_variant(0b111, 3, Variant::Liu3), 0b100);
assert_eq!(next_lut_index_variant(0b111, 0, Variant::Liu4), 0b000);
assert_eq!(next_lut_index_variant(0b111, 1, Variant::Liu4), 0b010);
assert_eq!(next_lut_index_variant(0b111, 2, Variant::Liu4), 0b001);
assert_eq!(next_lut_index_variant(0b111, 3, Variant::Liu4), 0b001);
}
#[test]
fn map_continuous_hilbert() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.33333334, 0.33333334, 0.13333334);
let h = xy2h_continuous_f32(tx, ty, Variant::Hilbert);
let (x, y) = h2xy_continuous_f32(th, Variant::Hilbert);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.33333334, 0.6666667, 0.46666667);
let h = xy2h_continuous_f32(tx, ty, Variant::Hilbert);
let (x, y) = h2xy_continuous_f32(th, Variant::Hilbert);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.6666666666666666, 0.3333333333333333, 0.8666666666666667);
let h = xy2h_continuous_f64(tx, ty, Variant::Hilbert);
let (x, y) = h2xy_continuous_f64(th, Variant::Hilbert);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.6666666666666666, 0.6666666666666666, 0.5333333333333333);
let h = xy2h_continuous_f64(tx, ty, Variant::Hilbert);
let (x, y) = h2xy_continuous_f64(th, Variant::Hilbert);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn map_continuous_moore() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.33333334, 0.33333334, 0.21666667);
let h = xy2h_continuous_f32(tx, ty, Variant::Moore);
let (x, y) = h2xy_continuous_f32(th, Variant::Moore);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.33333334, 0.6666667, 0.28333333);
let h = xy2h_continuous_f32(tx, ty, Variant::Moore);
let (x, y) = h2xy_continuous_f32(th, Variant::Moore);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.6666666666666666, 0.3333333333333333, 0.7833333333333333);
let h = xy2h_continuous_f64(tx, ty, Variant::Moore);
let (x, y) = h2xy_continuous_f64(th, Variant::Moore);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.6666666666666666, 0.6666666666666666, 0.7166666666666667);
let h = xy2h_continuous_f64(tx, ty, Variant::Moore);
let (x, y) = h2xy_continuous_f64(th, Variant::Moore);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn map_continuous_liu1() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.22222222, 0.22222222, 0.12564103);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu1);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu1);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.22222222, 0.44444445, 0.22758524);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu1);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu1);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.4444444444444444, 0.2222222222222222, 0.06545535120101877);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu1);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu1);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.4444444444444444, 0.4444444444444444, 0.002564102564102564);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu1);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu1);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn map_continuous_liu2() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.22222222, 0.22222222, 0.124358974);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu2);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu2);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.22222222, 0.44444445, 0.02241476);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu2);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu2);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.4444444444444444, 0.2222222222222222, 0.18454464879898125);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu2);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu2);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.4444444444444444, 0.4444444444444444, 0.24743589743589745);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu2);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu2);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn map_continuous_liu3() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.22222222, 0.22222222, 0.041025642);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu3);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu3);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.22222222, 0.44444445, 0.18914248);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu3);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu3);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.4444444444444444, 0.2222222222222222, 0.10511851937285181);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu3);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu3);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.4444444444444444, 0.4444444444444444, 0.1641025641025641);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu3);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu3);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn map_continuous_liu4() {
let err32 = 0.000163;
let err64 = 0.00000000722;
let (tx, ty, th) = (0.22222222, 0.22222222, 0.124358974);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu4);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu4);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.22222222, 0.44444445, 0.02241476);
let h = xy2h_continuous_f32(tx, ty, Variant::Liu4);
let (x, y) = h2xy_continuous_f32(th, Variant::Liu4);
assert_approx_eq!(h, th, err32);
assert_approx_eq!(x, tx, err32);
assert_approx_eq!(y, ty, err32);
let (tx, ty, th) = (0.4444444444444444, 0.2222222222222222, 0.18454464879898125);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu4);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu4);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
let (tx, ty, th) = (0.4444444444444444, 0.4444444444444444, 0.24743589743589745);
let h = xy2h_continuous_f64(tx, ty, Variant::Liu4);
let (x, y) = h2xy_continuous_f64(th, Variant::Liu4);
assert_approx_eq!(h, th, err64);
assert_approx_eq!(x, tx, err64);
assert_approx_eq!(y, ty, err64);
}
#[test]
fn extract_binary_fractional_f32() {
let mut frac = 0b1usize.reverse_bits();
let mut dec = 0.5f32;
while frac != 0 {
assert_eq!(frac, f32_to_binfrac(dec));
assert_approx_eq!(dec, binfrac_to_f32(frac));
frac >>= 1;
dec *= 0.5;
}
}
#[test]
fn extract_binary_fractional_f64() {
let mut frac = 0b1usize.reverse_bits();
let mut dec = 0.5f64;
while frac != 0 {
assert_eq!(frac, f64_to_binfrac(dec));
assert_approx_eq!(dec, binfrac_to_f64(frac));
frac >>= 1;
dec *= 0.5;
}
}
}