1pub fn hilbert_d2(mut x: u32, mut y: u32, order: u32) -> u64 {
23 if order == 0 {
24 return 0;
25 }
26 let mut d: u64 = 0;
27 let mut s = 1u32 << (order - 1);
28 while s > 0 {
29 let rx = u32::from((x & s) > 0);
30 let ry = u32::from((y & s) > 0);
31 d += (s as u64) * (s as u64) * ((3 * rx) ^ ry) as u64;
32 if ry == 0 {
34 if rx == 1 {
35 x = s.wrapping_sub(1).wrapping_sub(x);
36 y = s.wrapping_sub(1).wrapping_sub(y);
37 }
38 std::mem::swap(&mut x, &mut y);
39 }
40 s >>= 1;
41 }
42 d
43}
44
45pub fn hilbert_d2_inverse(mut d: u64, order: u32) -> (u32, u32) {
56 if order == 0 {
57 return (0, 0);
58 }
59 let mut x: u32 = 0;
60 let mut y: u32 = 0;
61 let mut s: u32 = 1;
63 while s < (1u32 << order) {
64 let rx = ((d >> 1) & 1) as u32;
65 let ry = (d ^ ((d >> 1) & 1)) as u32 & 1;
66 if ry == 0 {
67 if rx == 1 {
68 x = s.wrapping_sub(1).wrapping_sub(x);
69 y = s.wrapping_sub(1).wrapping_sub(y);
70 }
71 std::mem::swap(&mut x, &mut y);
72 }
73 x += s * rx;
74 y += s * ry;
75 d >>= 2;
76 s <<= 1;
77 }
78 (x, y)
79}
80
81const D3_INDEX: [[u64; 8]; 24] = [
95 [0, 1, 3, 2, 7, 6, 4, 5],
96 [0, 7, 1, 6, 3, 4, 2, 5],
97 [0, 3, 7, 4, 1, 2, 6, 5],
98 [2, 3, 1, 0, 5, 4, 6, 7],
99 [4, 3, 5, 2, 7, 0, 6, 1],
100 [4, 5, 7, 6, 3, 2, 0, 1],
101 [6, 5, 3, 0, 7, 2, 4, 1], [6, 7, 5, 4, 1, 0, 2, 3],
103 [0, 1, 7, 6, 3, 2, 4, 5],
104 [2, 1, 3, 0, 5, 6, 4, 7],
105 [4, 7, 5, 6, 3, 0, 2, 1],
106 [6, 1, 7, 0, 5, 2, 4, 3],
107 [0, 7, 3, 4, 1, 6, 2, 5],
108 [2, 5, 3, 4, 1, 6, 0, 7], [4, 3, 7, 0, 5, 2, 6, 1],
110 [6, 5, 1, 2, 7, 4, 0, 3],
111 [0, 1, 3, 2, 7, 6, 4, 5], [2, 3, 5, 4, 7, 6, 0, 1],
113 [4, 5, 3, 2, 1, 0, 6, 7],
114 [6, 7, 1, 0, 3, 2, 4, 5],
115 [0, 3, 1, 2, 7, 4, 6, 5],
116 [2, 1, 7, 4, 5, 6, 0, 3],
117 [4, 7, 3, 0, 1, 6, 2, 5], [6, 5, 7, 4, 1, 2, 0, 3],
119];
120
121const D3_STATE: [[u8; 8]; 24] = [
122 [1, 2, 3, 4, 5, 6, 7, 8],
123 [9, 10, 11, 12, 13, 14, 15, 16],
124 [17, 18, 19, 20, 21, 22, 23, 0],
125 [0, 5, 14, 19, 3, 10, 23, 16],
126 [2, 9, 20, 15, 4, 11, 22, 17],
127 [3, 12, 13, 0, 7, 18, 17, 6],
128 [4, 13, 12, 1, 6, 19, 16, 7],
129 [5, 0, 3, 6, 15, 20, 21, 14],
130 [6, 1, 2, 7, 14, 21, 20, 13],
131 [7, 4, 5, 2, 13, 22, 23, 12],
132 [8, 7, 6, 11, 0, 1, 2, 3],
133 [9, 2, 1, 10, 19, 20, 21, 18],
134 [10, 3, 0, 9, 18, 21, 20, 19],
135 [11, 8, 9, 6, 17, 22, 23, 20],
136 [12, 9, 8, 13, 2, 3, 0, 1],
137 [13, 14, 15, 10, 1, 0, 3, 2],
138 [14, 15, 12, 11, 0, 3, 2, 1],
139 [15, 0, 11, 14, 5, 2, 1, 4],
140 [16, 11, 10, 17, 6, 5, 4, 7],
141 [17, 6, 7, 12, 15, 10, 9, 14],
142 [18, 19, 16, 21, 8, 11, 10, 9],
143 [19, 16, 17, 22, 11, 8, 9, 10],
144 [20, 21, 22, 17, 10, 9, 8, 11],
145 [21, 22, 19, 16, 9, 10, 11, 8],
146];
147
148pub fn hilbert_d3(x: u32, y: u32, z: u32, order: u32) -> u64 {
161 if order == 0 {
162 return 0;
163 }
164 let mut state: usize = 0;
165 let mut index: u64 = 0;
166 let top = order - 1;
168 for i in (0..order).rev() {
169 let xi = ((x >> i) & 1) as usize;
170 let yi = ((y >> i) & 1) as usize;
171 let zi = ((z >> i) & 1) as usize;
172 let octant = (zi << 2) | (yi << 1) | xi;
173 let contribution = D3_INDEX[state][octant];
174 index = (index << 3) | contribution;
175 state = D3_STATE[state][octant] as usize;
176 let _ = top; }
178 index
179}
180
181pub fn hilbert_d3_inverse(idx: u64, order: u32) -> (u32, u32, u32) {
185 if order == 0 {
186 return (0, 0, 0);
187 }
188 let mut inverse_index: [[(usize, u8); 8]; 24] = [[(0, 0); 8]; 24];
191 for s in 0..24usize {
192 for oct in 0..8usize {
193 let h = D3_INDEX[s][oct] as usize;
194 inverse_index[s][h] = (oct, D3_STATE[s][oct]);
195 }
196 }
197
198 let mut state: usize = 0;
199 let mut x: u32 = 0;
200 let mut y: u32 = 0;
201 let mut z: u32 = 0;
202 for i in (0..order).rev() {
204 let h = ((idx >> (i * 3)) & 7) as usize;
205 let (octant, next_state) = inverse_index[state][h];
206 let xi = (octant & 1) as u32;
207 let yi = ((octant >> 1) & 1) as u32;
208 let zi = ((octant >> 2) & 1) as u32;
209 x |= xi << i;
210 y |= yi << i;
211 z |= zi << i;
212 state = next_state as usize;
213 }
214 (x, y, z)
215}
216
217pub fn hilbert_d2_f64(x: f64, y: f64, bbox: (f64, f64, f64, f64), order: u32) -> u64 {
229 let (xmin, ymin, xmax, ymax) = bbox;
230 let n = ((1u32 << order) - 1) as f64;
231 let dx = xmax - xmin;
232 let dy = ymax - ymin;
233 let xi = if dx == 0.0 {
234 0u32
235 } else {
236 (((x - xmin) / dx) * n).round().clamp(0.0, n) as u32
237 };
238 let yi = if dy == 0.0 {
239 0u32
240 } else {
241 (((y - ymin) / dy) * n).round().clamp(0.0, n) as u32
242 };
243 hilbert_d2(xi, yi, order)
244}
245
246pub fn hilbert_d3_f64(
251 x: f64,
252 y: f64,
253 z: f64,
254 bbox: (f64, f64, f64, f64, f64, f64),
255 order: u32,
256) -> u64 {
257 let (xmin, ymin, zmin, xmax, ymax, zmax) = bbox;
258 let n = ((1u32 << order) - 1) as f64;
259 let dx = xmax - xmin;
260 let dy = ymax - ymin;
261 let dz = zmax - zmin;
262 let xi = if dx == 0.0 {
263 0u32
264 } else {
265 (((x - xmin) / dx) * n).round().clamp(0.0, n) as u32
266 };
267 let yi = if dy == 0.0 {
268 0u32
269 } else {
270 (((y - ymin) / dy) * n).round().clamp(0.0, n) as u32
271 };
272 let zi = if dz == 0.0 {
273 0u32
274 } else {
275 (((z - zmin) / dz) * n).round().clamp(0.0, n) as u32
276 };
277 hilbert_d3(xi, yi, zi, order)
278}
279
280pub fn hilbert_sort_2d(points: &mut [(f64, f64)]) {
299 if points.is_empty() {
300 return;
301 }
302 let order = 16u32;
303 let mut xmin = f64::INFINITY;
304 let mut xmax = f64::NEG_INFINITY;
305 let mut ymin = f64::INFINITY;
306 let mut ymax = f64::NEG_INFINITY;
307 for &(px, py) in points.iter() {
308 if px < xmin {
309 xmin = px;
310 }
311 if px > xmax {
312 xmax = px;
313 }
314 if py < ymin {
315 ymin = py;
316 }
317 if py > ymax {
318 ymax = py;
319 }
320 }
321 let bbox = (xmin, ymin, xmax.max(xmin + 1e-10), ymax.max(ymin + 1e-10));
323 points.sort_unstable_by_key(|&(px, py)| hilbert_d2_f64(px, py, bbox, order));
324}
325
326pub fn hilbert_sort_3d(points: &mut [(f64, f64, f64)]) {
339 if points.is_empty() {
340 return;
341 }
342 let order = 10u32;
343 let mut xmin = f64::INFINITY;
344 let mut xmax = f64::NEG_INFINITY;
345 let mut ymin = f64::INFINITY;
346 let mut ymax = f64::NEG_INFINITY;
347 let mut zmin = f64::INFINITY;
348 let mut zmax = f64::NEG_INFINITY;
349 for &(px, py, pz) in points.iter() {
350 if px < xmin {
351 xmin = px;
352 }
353 if px > xmax {
354 xmax = px;
355 }
356 if py < ymin {
357 ymin = py;
358 }
359 if py > ymax {
360 ymax = py;
361 }
362 if pz < zmin {
363 zmin = pz;
364 }
365 if pz > zmax {
366 zmax = pz;
367 }
368 }
369 let bbox = (
370 xmin,
371 ymin,
372 zmin,
373 xmax.max(xmin + 1e-10),
374 ymax.max(ymin + 1e-10),
375 zmax.max(zmin + 1e-10),
376 );
377 points.sort_unstable_by_key(|&(px, py, pz)| hilbert_d3_f64(px, py, pz, bbox, order));
378}
379
380#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn d2_round_trip_small() {
390 let order = 4u32;
391 let n = 1u32 << order;
392 for x in 0..n {
393 for y in 0..n {
394 let idx = hilbert_d2(x, y, order);
395 let (rx, ry) = hilbert_d2_inverse(idx, order);
396 assert_eq!(
397 (rx, ry),
398 (x, y),
399 "2D round-trip failed for ({x},{y}) order={order}"
400 );
401 }
402 }
403 }
404
405 #[test]
406 fn d2_all_distinct_order3() {
407 let order = 3u32;
408 let n = 1u32 << order;
409 let mut indices: Vec<u64> = (0..n)
410 .flat_map(|x| (0..n).map(move |y| hilbert_d2(x, y, order)))
411 .collect();
412 indices.sort_unstable();
413 indices.dedup();
414 assert_eq!(indices.len(), (n * n) as usize);
415 }
416
417 #[test]
418 fn d2_origin_is_zero() {
419 assert_eq!(hilbert_d2(0, 0, 1), 0);
420 assert_eq!(hilbert_d2(0, 0, 6), 0);
421 }
422
423 #[test]
424 fn d3_round_trip_small() {
425 let order = 3u32;
426 let n = 1u32 << order;
427 for x in 0..n {
428 for y in 0..n {
429 for z in 0..n {
430 let idx = hilbert_d3(x, y, z, order);
431 let (rx, ry, rz) = hilbert_d3_inverse(idx, order);
432 assert_eq!(
433 (rx, ry, rz),
434 (x, y, z),
435 "3D round-trip failed for ({x},{y},{z}) order={order}"
436 );
437 }
438 }
439 }
440 }
441
442 #[test]
443 fn sort_2d_empty() {
444 let mut pts: Vec<(f64, f64)> = vec![];
445 hilbert_sort_2d(&mut pts);
446 assert!(pts.is_empty());
447 }
448
449 #[test]
450 fn sort_2d_length_preserved() {
451 let mut pts: Vec<(f64, f64)> = (0..50)
452 .map(|i: i32| (i as f64, (i * 7 % 50) as f64))
453 .collect();
454 hilbert_sort_2d(&mut pts);
455 assert_eq!(pts.len(), 50);
456 }
457
458 #[test]
459 fn sort_3d_length_preserved() {
460 let mut pts: Vec<(f64, f64, f64)> = (0..30)
461 .map(|i: i32| (i as f64, (i * 3 % 30) as f64, (i * 7 % 30) as f64))
462 .collect();
463 hilbert_sort_3d(&mut pts);
464 assert_eq!(pts.len(), 30);
465 }
466}