use num_traits::Float;
pub trait Morton<const D: usize> {
const CHILDREN: usize;
const BITS: u32;
fn encode(coords: [u32; D]) -> u64;
fn decode(code: u64) -> [u32; D];
}
pub struct Dim<const D: usize>;
#[inline]
const fn part_1by1(mut x: u64) -> u64 {
x &= 0x0000_0000_ffff_ffff;
x = (x | (x << 16)) & 0x0000_ffff_0000_ffff;
x = (x | (x << 8)) & 0x00ff_00ff_00ff_00ff;
x = (x | (x << 4)) & 0x0f0f_0f0f_0f0f_0f0f;
x = (x | (x << 2)) & 0x3333_3333_3333_3333;
(x | (x << 1)) & 0x5555_5555_5555_5555
}
#[inline]
const fn compact_1by1(mut x: u64) -> u64 {
x &= 0x5555_5555_5555_5555;
x = (x | (x >> 1)) & 0x3333_3333_3333_3333;
x = (x | (x >> 2)) & 0x0f0f_0f0f_0f0f_0f0f;
x = (x | (x >> 4)) & 0x00ff_00ff_00ff_00ff;
x = (x | (x >> 8)) & 0x0000_ffff_0000_ffff;
(x | (x >> 16)) & 0x0000_0000_ffff_ffff
}
#[inline]
const fn part_1by2(mut x: u64) -> u64 {
x &= 0x1f_ffff;
x = (x | (x << 32)) & 0x001f_0000_0000_ffff;
x = (x | (x << 16)) & 0x001f_0000_ff00_00ff;
x = (x | (x << 8)) & 0x100f_00f0_0f00_f00f;
x = (x | (x << 4)) & 0x10c3_0c30_c30c_30c3;
(x | (x << 2)) & 0x1249_2492_4924_9249
}
#[inline]
const fn compact_1by2(mut x: u64) -> u64 {
x &= 0x1249_2492_4924_9249;
x = (x | (x >> 2)) & 0x10c3_0c30_c30c_30c3;
x = (x | (x >> 4)) & 0x100f_00f0_0f00_f00f;
x = (x | (x >> 8)) & 0x001f_0000_ff00_00ff;
x = (x | (x >> 16)) & 0x001f_0000_0000_ffff;
(x | (x >> 32)) & 0x001f_ffff
}
impl Morton<2> for Dim<2> {
const CHILDREN: usize = 4;
const BITS: u32 = 32;
fn encode([c0, c1]: [u32; 2]) -> u64 {
part_1by1(c0 as u64) | (part_1by1(c1 as u64) << 1)
}
fn decode(code: u64) -> [u32; 2] {
[compact_1by1(code) as u32, compact_1by1(code >> 1) as u32]
}
}
impl Morton<3> for Dim<3> {
const CHILDREN: usize = 8;
const BITS: u32 = 21;
fn encode([c0, c1, c2]: [u32; 3]) -> u64 {
part_1by2(c0 as u64) | (part_1by2(c1 as u64) << 1) | (part_1by2(c2 as u64) << 2)
}
fn decode(code: u64) -> [u32; 3] {
[
compact_1by2(code) as u32,
compact_1by2(code >> 1) as u32,
compact_1by2(code >> 2) as u32,
]
}
}
pub(crate) fn quantize<T: Float, const D: usize>(
point: &[T],
min: &[T; D],
inv_scale: &[T; D],
max_bucket: u32,
) -> [u32; D] {
std::array::from_fn(|axis| {
let scaled = ((point[axis] - min[axis]) * inv_scale[axis]).floor();
if scaled > T::zero() {
scaled
.to_u64()
.map_or(max_bucket, |value| value.min(max_bucket as u64) as u32)
} else {
0
}
})
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::StdRng};
use super::*;
#[test]
fn encode_decode_roundtrips_2d() {
let mut rng = StdRng::seed_from_u64(0x1234_5678);
for _ in 0..10_000 {
let x = rng.random::<u32>();
let y = rng.random::<u32>();
let code = Dim::<2>::encode([x, y]);
assert_eq!(Dim::<2>::decode(code), [x, y]);
}
}
#[test]
fn encode_decode_roundtrips_3d() {
let mut rng = StdRng::seed_from_u64(0x9abc_def0);
let mask = (1u32 << 21) - 1;
for _ in 0..10_000 {
let x = rng.random::<u32>() & mask;
let y = rng.random::<u32>() & mask;
let z = rng.random::<u32>() & mask;
let code = Dim::<3>::encode([x, y, z]);
assert_eq!(Dim::<3>::decode(code), [x, y, z]);
}
}
#[test]
fn encode_matches_known_z_order_2d() {
assert_eq!(Dim::<2>::encode([0, 0]), 0);
assert_eq!(Dim::<2>::encode([1, 0]), 1);
assert_eq!(Dim::<2>::encode([0, 1]), 2);
assert_eq!(Dim::<2>::encode([1, 1]), 3);
}
#[test]
fn encode_matches_known_z_order_3d() {
assert_eq!(Dim::<3>::encode([0, 0, 0]), 0);
assert_eq!(Dim::<3>::encode([1, 0, 0]), 1);
assert_eq!(Dim::<3>::encode([0, 1, 0]), 2);
assert_eq!(Dim::<3>::encode([0, 0, 1]), 4);
assert_eq!(Dim::<3>::encode([1, 1, 1]), 7);
}
#[test]
fn sorting_by_code_yields_z_order() {
let top = 1u32 << 31;
let lower_left = Dim::<2>::encode([1, 1]);
let lower_left_2 = Dim::<2>::encode([5, 7]);
let upper_right = Dim::<2>::encode([top, top]);
let mut codes = [upper_right, lower_left_2, lower_left];
codes.sort_unstable();
assert!(codes[0] < codes[2]);
assert_eq!(codes[2], upper_right);
assert!(codes[0] == lower_left && codes[1] == lower_left_2);
}
#[test]
fn quantize_spans_the_bucket_range() {
let min = [0.0f32, -2.0];
let extent = [4.0f32, 8.0];
let max_bucket = (1u64 << 32) - 1;
let inv_scale = [
(1u64 << 32) as f32 / extent[0],
(1u64 << 32) as f32 / extent[1],
];
let low = quantize::<f32, 2>(&[0.0, -2.0], &min, &inv_scale, max_bucket as u32);
assert_eq!(low, [0, 0]);
let high = quantize::<f32, 2>(&[4.0, 6.0], &min, &inv_scale, max_bucket as u32);
assert_eq!(high, [max_bucket as u32, max_bucket as u32]);
let mid = quantize::<f32, 2>(&[2.0, 2.0], &min, &inv_scale, max_bucket as u32);
assert!(mid[0] > 0 && mid[0] < max_bucket as u32);
assert!(mid[1] > 0 && mid[1] < max_bucket as u32);
}
#[test]
fn quantize_handles_zero_width_axis() {
let min = [1.0f32, 5.0];
let inv_scale = [0.0f32, (1u64 << 32) as f32 / 4.0];
let max_bucket = ((1u64 << 32) - 1) as u32;
let q = quantize::<f32, 2>(&[1.0, 7.0], &min, &inv_scale, max_bucket);
assert_eq!(q[0], 0);
assert!(q[1] > 0);
}
}