pub fn encode_hilbert(x: u32, y: u32, z: u32, bits: u32) -> u64 {
let mut axes = [x, y, z];
for shift in (1..bits).rev() {
let lower = (1u32 << shift) - 1;
for i in 0..3 {
let m = ((axes[i] >> shift) & 1).wrapping_neg();
let t = (axes[0] ^ axes[i]) & lower;
axes[0] ^= t ^ (m & (t ^ lower)); axes[i] ^= t & !m; }
}
axes[1] ^= axes[0];
axes[2] ^= axes[1];
let mut g = axes[2];
g ^= g >> 1;
g ^= g >> 2;
g ^= g >> 4;
g ^= g >> 8;
g ^= g >> 16;
let t = g >> 1;
axes[0] ^= t;
axes[1] ^= t;
axes[2] ^= t;
split3(axes[0] as u64) << 2 | split3(axes[1] as u64) << 1 | split3(axes[2] as u64)
}
fn split3(mut x: u64) -> u64 {
x &= 0x1fffff;
x = (x | x << 32) & 0x1f00000000ffff;
x = (x | x << 16) & 0x1f0000ff0000ff;
x = (x | x << 8) & 0x100f00f00f00f00f;
x = (x | x << 4) & 0x10c30c30c30c30c3;
x = (x | x << 2) & 0x1249249249249249;
x
}
#[cfg(test)]
mod tests {
use crate::encode_hilbert;
use std::collections::HashSet;
#[test]
fn matches_spec_example() {
assert_eq!(encode_hilbert(0, 0, 0, 1), 0);
assert_eq!(encode_hilbert(0, 1, 0, 1), 3);
assert_eq!(encode_hilbert(1, 1, 0, 1), 4);
assert_eq!(encode_hilbert(1, 0, 0, 1), 7);
}
#[test]
fn index_is_a_bijection_over_a_cube() {
let bits = 4;
let mut seen = HashSet::new();
for x in 0..16 {
for y in 0..16 {
for z in 0..16 {
assert!(seen.insert(encode_hilbert(x, y, z, bits)));
}
}
}
assert_eq!(seen.len(), 16 * 16 * 16);
}
}