1use geo_types::Coord;
2use wide::u32x8;
3
4use crate::decoder::Morton;
5use crate::encoder::model::CurveParams;
6use crate::{Decoder, MltError, MltResult};
7
8const LANES: usize = 8;
9
10#[must_use]
18#[inline]
19pub fn interleave_bits(coord: Coord<u32>) -> u32 {
20 let mut sx = coord.x & 0xFFFF;
24 sx = (sx | (sx << 8)) & 0x00FF_00FF;
25 sx = (sx | (sx << 4)) & 0x0F0F_0F0F;
26 sx = (sx | (sx << 2)) & 0x3333_3333;
27 sx = (sx | (sx << 1)) & 0x5555_5555;
28
29 let mut sy = coord.y & 0xFFFF;
30 sy = (sy | (sy << 8)) & 0x00FF_00FF;
31 sy = (sy | (sy << 4)) & 0x0F0F_0F0F;
32 sy = (sy | (sy << 2)) & 0x3333_3333;
33 sy = (sy | (sy << 1)) & 0x5555_5555;
34
35 sx | (sy << 1)
36}
37
38#[cfg(feature = "unstable-v2")]
40#[must_use]
41#[inline]
42pub fn interleave_u32(even: u32, odd: u32) -> u64 {
43 spread_u32(even) | (spread_u32(odd) << 1)
44}
45
46#[cfg(feature = "unstable-v2")]
48#[must_use]
49#[inline]
50pub fn deinterleave_u64(code: u64) -> (u32, u32) {
51 (compact_u64(code), compact_u64(code >> 1))
52}
53
54#[cfg(feature = "unstable-v2")]
55fn spread_u32(value: u32) -> u64 {
56 let mut s = u64::from(value);
57 s = (s | (s << 16)) & 0x0000_FFFF_0000_FFFF;
58 s = (s | (s << 8)) & 0x00FF_00FF_00FF_00FF;
59 s = (s | (s << 4)) & 0x0F0F_0F0F_0F0F_0F0F;
60 s = (s | (s << 2)) & 0x3333_3333_3333_3333;
61 s = (s | (s << 1)) & 0x5555_5555_5555_5555;
62 s
63}
64
65#[cfg(feature = "unstable-v2")]
66fn compact_u64(code: u64) -> u32 {
67 let mut c = code & 0x5555_5555_5555_5555;
68 c = (c | (c >> 1)) & 0x3333_3333_3333_3333;
69 c = (c | (c >> 2)) & 0x0F0F_0F0F_0F0F_0F0F;
70 c = (c | (c >> 4)) & 0x00FF_00FF_00FF_00FF;
71 c = (c | (c >> 8)) & 0x0000_FFFF_0000_FFFF;
72 c = (c | (c >> 16)) & 0x0000_0000_FFFF_FFFF;
73 u32::try_from(c).expect("infallible: the last mask keeps only the low 32 bits")
74}
75
76#[must_use]
87#[inline]
88pub fn morton_sort_key(c: Coord<i32>, params: CurveParams) -> u32 {
89 debug_assert!(params.bits >= 1);
90 #[expect(
91 clippy::cast_possible_truncation,
92 clippy::cast_sign_loss,
93 reason = "shift brings value into [0, extent]; masked to 16 bits immediately after"
94 )]
95 let sx = ((i64::from(c.x) + i64::from(params.shift)) as u32) & 0xFFFF;
96 #[expect(
97 clippy::cast_possible_truncation,
98 clippy::cast_sign_loss,
99 reason = "shift brings value into [0, extent]; masked to 16 bits immediately after"
100 )]
101 let sy = ((i64::from(c.y) + i64::from(params.shift)) as u32) & 0xFFFF;
102 interleave_bits((sx, sy).into())
103}
104
105impl Morton {
107 pub fn from_vertices(vertices: &[i32]) -> MltResult<Self> {
112 let min_v = vertices.iter().copied().min().unwrap_or(0);
113 let max_v = vertices.iter().copied().max().unwrap_or(0);
114 let shift: u32 = if min_v < 0 { min_v.unsigned_abs() } else { 0 };
115 let tile_extent = i64::from(max_v) + i64::from(shift);
116 let extent =
117 u32::try_from(tile_extent).expect("infallible: max_v + shift lies in 0..=u32::MAX");
118 let required_bits = extent.bit_width();
122 if required_bits > 16 {
123 return Err(MltError::VertexMortonNotCompatibleWithExtent {
124 extent,
125 required_bits,
126 });
127 }
128 Self::new(required_bits, shift)
129 }
130
131 #[inline]
136 pub fn encode_morton(self, x: i32, y: i32) -> MltResult<u32> {
137 let sx = u32::try_from(i64::from(x) + i64::from(self.shift))?;
138 let sy = u32::try_from(i64::from(y) + i64::from(self.shift))?;
139 let mut code = 0u32;
140 for i in 0..self.bits {
141 code |= ((sx >> i) & 1) << (2 * i);
143 code |= ((sy >> i) & 1) << (2 * i + 1);
144 }
145 Ok(code)
146 }
147}
148
149impl Morton {
150 #[inline]
152 fn decode_one(self, morton_code: u32) -> Coord<i32> {
153 let mut x = 0u32;
154 let mut y = 0u32;
155 for i in 0..self.bits {
156 let bit_mask = 1u32 << (2 * i);
157 x |= (morton_code & bit_mask) >> i;
158 y |= ((morton_code >> 1) & bit_mask) >> i;
159 }
160 Coord::<i32> {
161 x: x.wrapping_sub(self.shift).cast_signed(),
162 y: y.wrapping_sub(self.shift).cast_signed(),
163 }
164 }
165
166 pub fn decode_delta(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
173 let alloc_size = data.len() * 2;
174 let mut out = dec.alloc(alloc_size)?;
175 let shift_vec = u32x8::splat(self.shift);
176
177 let mut prev = 0i32;
178 let (chunks, remainder) = data.as_chunks::<LANES>();
179
180 for chunk in chunks {
181 let mut buf = [0u32; LANES];
183 for (b, &d) in buf.iter_mut().zip(chunk.iter()) {
184 prev = prev.wrapping_add(d.cast_signed());
185 *b = prev.cast_unsigned();
186 }
187 self.decode_chunk(buf, shift_vec, &mut out);
188 }
189
190 for &d in remainder {
192 prev = prev.wrapping_add(d.cast_signed());
193 let coord = self.decode_one(prev.cast_unsigned());
194 out.push(coord.x);
195 out.push(coord.y);
196 }
197
198 dec.adjust_alloc(&out, alloc_size)
199 .expect("infallible: two coordinates pushed per code fill alloc_size exactly");
200 Ok(out)
201 }
202
203 #[inline]
208 fn decode_chunk(self, buf: [u32; LANES], shift_vec: u32x8, out: &mut Vec<i32>) {
209 let codes = u32x8::from(buf);
210 let codes_y = codes >> 1;
212
213 let mut x_vec = u32x8::ZERO;
214 let mut y_vec = u32x8::ZERO;
215
216 for i in 0..self.bits {
217 let bit_mask = u32x8::splat(1u32 << (2 * i));
219 x_vec |= (codes & bit_mask) >> i;
221 y_vec |= (codes_y & bit_mask) >> i;
222 }
223
224 let xs: [u32; LANES] = (x_vec - shift_vec).into();
225 let ys: [u32; LANES] = (y_vec - shift_vec).into();
226
227 for lane in 0..LANES {
228 out.push(xs[lane].cast_signed());
229 out.push(ys[lane].cast_signed());
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236
237 use rstest::rstest;
238
239 use super::*;
240 use crate::test_helpers::{dec, starved_dec};
241
242 const fn c(x: i32, y: i32) -> Coord<i32> {
243 Coord::<i32> { x, y }
244 }
245
246 const fn p(shift: u32, bits: u32) -> CurveParams {
247 CurveParams { shift, bits }
248 }
249
250 fn spread_bits(mut tx: u32) -> u32 {
253 tx = (tx | (tx << 8)) & 0x00FF_00FF;
254 tx = (tx | (tx << 4)) & 0x0F0F_0F0F;
255 tx = (tx | (tx << 2)) & 0x3333_3333;
256 tx = (tx | (tx << 1)) & 0x5555_5555;
257 tx
258 }
259
260 fn compact_bits(mut tx: u32) -> u32 {
261 tx &= 0x5555_5555;
262 tx = (tx | (tx >> 1)) & 0x3333_3333;
263 tx = (tx | (tx >> 2)) & 0x0F0F_0F0F;
264 tx = (tx | (tx >> 4)) & 0x00FF_00FF;
265 tx = (tx | (tx >> 8)) & 0x0000_FFFF;
266 tx
267 }
268
269 #[test]
270 fn spread_then_compact_is_identity() {
271 for x in 0u32..=0xFFFF {
272 assert_eq!(compact_bits(spread_bits(x)), x, "round-trip failed for {x}");
273 }
274 }
275
276 #[test]
277 fn spread_bits_places_bit0_at_position0() {
278 assert_eq!(spread_bits(1), 1);
279 }
280
281 #[test]
282 fn spread_bits_places_bit1_at_position2() {
283 assert_eq!(spread_bits(2), 4);
284 }
285
286 #[test]
287 fn spread_bits_places_bit2_at_position4() {
288 assert_eq!(spread_bits(4), 16);
289 }
290
291 #[cfg(feature = "unstable-v2")]
292 #[rstest]
293 #[case::zero(0, 0, 0)]
294 #[case::even_only(1, 0, 1)]
295 #[case::odd_only(0, 1, 2)]
296 #[case::one_byte_varint(15, 7, 0x7F)]
297 #[case::u32_max_both(u32::MAX, u32::MAX, u64::MAX)]
298 #[case::u32_max_even(u32::MAX, 0, 0x5555_5555_5555_5555)]
299 fn interleave_u32_places_even_and_odd_bits(
300 #[case] even: u32,
301 #[case] odd: u32,
302 #[case] code: u64,
303 ) {
304 assert_eq!(interleave_u32(even, odd), code);
305 assert_eq!(deinterleave_u64(code), (even, odd));
306 }
307
308 #[test]
309 fn origin_maps_to_zero() {
310 assert_eq!(morton_sort_key(c(0, 0), p(0, 16)), 0);
311 }
312
313 #[test]
314 fn x_axis_produces_even_bits() {
315 assert_eq!(
316 morton_sort_key(c(1, 0), p(0, 16)),
317 1,
318 "x bit 0 lands at Morton bit 0"
319 );
320 assert_eq!(
321 morton_sort_key(c(2, 0), p(0, 16)),
322 4,
323 "x bit 1 lands at Morton bit 2"
324 );
325 }
326
327 #[test]
328 fn y_axis_produces_odd_bits() {
329 assert_eq!(
330 morton_sort_key(c(0, 1), p(0, 16)),
331 2,
332 "y bit 0 lands at Morton bit 1"
333 );
334 assert_eq!(
335 morton_sort_key(c(0, 2), p(0, 16)),
336 8,
337 "y bit 1 lands at Morton bit 3"
338 );
339 }
340
341 #[test]
342 fn negative_coords_shift_correctly() {
343 assert_eq!(
344 morton_sort_key(c(-1, -1), p(1, 16)),
345 0,
346 "a shift of 1 maps (-1, -1) onto (0, 0)"
347 );
348 assert_eq!(
349 morton_sort_key(c(-1, 0), p(1, 16)),
350 2,
351 "a shift of 1 maps (-1, 0) onto (0, 1)"
352 );
353 }
354
355 #[test]
356 fn spatial_locality_z_order() {
357 let k00 = morton_sort_key(c(0, 0), p(0, 16));
358 let k10 = morton_sort_key(c(1, 0), p(0, 16));
359 let k01 = morton_sort_key(c(0, 1), p(0, 16));
360 let k11 = morton_sort_key(c(1, 1), p(0, 16));
361 assert!(k00 < k10, "(0,0) precedes (1,0) in Z-order");
362 assert!(k10 < k01, "(1,0) precedes (0,1) in Z-order");
363 assert!(k01 < k11, "(0,1) precedes (1,1) in Z-order");
364 }
365
366 #[test]
367 fn interleave_round_trips_via_deinterleave() {
368 for x in 0u32..16 {
369 for y in 0u32..16 {
370 let code = interleave_bits((x, y).into());
371 let mut rx = 0u32;
372 let mut ry = 0u32;
373 for bit in 0..16 {
374 rx |= ((code >> (2 * bit)) & 1) << bit;
375 ry |= ((code >> (2 * bit + 1)) & 1) << bit;
376 }
377 assert_eq!(rx, x, "x mismatch for ({x}, {y})");
378 assert_eq!(ry, y, "y mismatch for ({x}, {y})");
379 }
380 }
381 }
382
383 const NUM_BITS: u32 = 15;
386 const COORD_SHIFT: u32 = 1 << (NUM_BITS - 1);
387 const MORTON: Morton = Morton {
388 bits: NUM_BITS,
389 shift: COORD_SHIFT,
390 };
391
392 #[must_use]
393 #[inline]
394 pub fn encode_morton_15(coord: Coord<u32>) -> u32 {
395 let mut code = 0u32;
396 for bit in 0..15 {
397 code |= ((coord.x >> bit) & 1) << (2 * bit);
398 code |= ((coord.y >> bit) & 1) << (2 * bit + 1);
399 }
400 code
401 }
402
403 #[test]
404 fn test_decode_morton_delta_origin() {
405 let code = encode_morton_15((COORD_SHIFT, COORD_SHIFT).into());
406 let decoded = MORTON.decode_delta(&[code], &mut dec()).unwrap();
407 assert_eq!(
408 decoded,
409 [0, 0],
410 "the code for (COORD_SHIFT, COORD_SHIFT) sits at the origin"
411 );
412 }
413
414 #[test]
415 fn test_decode_morton_delta_known_values() {
416 let x: u32 = 1;
417 let y: u32 = 2;
418 let code = encode_morton_15((x, y).into());
419 let expected_x = x.cast_signed() - COORD_SHIFT.cast_signed();
420 let expected_y = y.cast_signed() - COORD_SHIFT.cast_signed();
421 let decoded = MORTON.decode_delta(&[code], &mut dec()).unwrap();
422 assert_eq!(
423 decoded,
424 [expected_x, expected_y],
425 "decoding subtracts COORD_SHIFT from each axis"
426 );
427 }
428
429 #[test]
430 fn test_decode_morton_delta_scalar_tail() {
431 let pairs: [Coord<u32>; _] = [(0, 1).into(), (2, 3).into(), (4, 5).into()];
432 let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
433 let result = MORTON
434 .decode_delta(&signed_deltas(&codes), &mut dec())
435 .unwrap();
436 let expected = expected_coords(&pairs);
437 assert_eq!(result, expected, "3 codes take the scalar tail alone");
438 }
439
440 #[test]
441 fn test_decode_morton_delta_full_simd_chunk() {
442 let pairs: [Coord<u32>; _] = [
443 (0, 0).into(),
444 (1, 0).into(),
445 (0, 1).into(),
446 (1, 1).into(),
447 (2, 3).into(),
448 (7, 5).into(),
449 (10, 9).into(),
450 (15, 15).into(),
451 ];
452 let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
453 let result = MORTON
454 .decode_delta(&signed_deltas(&codes), &mut dec())
455 .unwrap();
456 let expected = expected_coords(&pairs);
457 assert_eq!(result, expected, "8 codes fill one SIMD chunk with no tail");
458 }
459
460 #[test]
461 fn test_decode_morton_delta_simd_plus_tail() {
462 let pairs: Vec<Coord<u32>> = (0..11u32)
463 .map(|i| (i * 3 % 100, i * 7 % 100).into())
464 .collect();
465 let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
466 let result = MORTON
467 .decode_delta(&signed_deltas(&codes), &mut dec())
468 .unwrap();
469 let expected = expected_coords(&pairs);
470 assert_eq!(
471 result, expected,
472 "11 codes take one SIMD chunk plus a tail of 3"
473 );
474 }
475
476 #[test]
477 fn test_decode_morton_delta_empty() {
478 assert_eq!(
479 MORTON.decode_delta(&[], &mut dec()).unwrap(),
480 [] as [i32; 0]
481 );
482 }
483
484 #[test]
485 fn test_decode_morton_delta_identity_with_zero_deltas() {
486 let deltas = vec![0u32; 3];
487 let result = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
488 let shift = -COORD_SHIFT.cast_signed();
489 assert_eq!(
490 result,
491 vec![shift, shift, shift, shift, shift, shift],
492 "zero deltas resolve every code to 0, which decodes to (-COORD_SHIFT, -COORD_SHIFT)"
493 );
494 }
495
496 #[test]
497 fn test_decode_morton_delta_matches_codes_after_prefix_sum() {
498 let pairs: Vec<Coord<u32>> = (0..11u32)
499 .map(|i| (i * 5 % 200, i * 9 % 200).into())
500 .collect();
501 let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
502 let result = MORTON
503 .decode_delta(&signed_deltas(&codes), &mut dec())
504 .unwrap();
505 assert_eq!(
506 result,
507 expected_coords(&pairs),
508 "the prefix sum rebuilds the absolute codes"
509 );
510 }
511
512 #[test]
513 fn test_decode_morton_delta_wrapping() {
514 let pairs: [Coord<u32>; _] = [(500, 300).into(), (10, 10).into()];
515 let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
516 assert_eq!(
517 MORTON
518 .decode_delta(&signed_deltas(&codes), &mut dec())
519 .unwrap(),
520 expected_coords(&pairs),
521 "a delta onto a smaller code wraps and still resolves"
522 );
523 }
524
525 #[test]
526 fn decode_delta_past_the_memory_budget_is_rejected() {
527 let err = MORTON
528 .decode_delta(&[0, 1], &mut starved_dec())
529 .unwrap_err();
530 assert!(
531 matches!(err, MltError::MemoryLimitExceeded { .. }),
532 "{err:?}"
533 );
534 }
535
536 #[rstest]
539 #[case::empty(&[], 0, 0)]
540 #[case::non_negative_needs_no_shift(&[0, 100], 0, 7)]
541 #[case::negative_min_shifts_onto_zero(&[-5, 10], 5, 4)]
542 #[case::widest_accepted_extent(&[0, 65_535], 0, 16)]
543 fn from_vertices_derives_shift_and_bits(
544 #[case] vertices: &[i32],
545 #[case] shift: u32,
546 #[case] bits: u32,
547 ) {
548 let morton = Morton::from_vertices(vertices).unwrap();
549 assert_eq!(
550 (morton.shift, morton.bits),
551 (shift, bits),
552 "shift cancels a negative min, bits covers max + shift"
553 );
554 }
555
556 #[rstest]
557 #[case::past_the_cap_outright(&[0, 65_536])]
558 #[case::pushed_past_the_cap_by_the_shift(&[-1, 65_535])]
559 fn from_vertices_rejects_an_extent_wider_than_sixteen_bits(#[case] vertices: &[i32]) {
560 let err = Morton::from_vertices(vertices).unwrap_err();
561 assert!(
562 matches!(
563 err,
564 MltError::VertexMortonNotCompatibleWithExtent {
565 extent: 65_536,
566 required_bits: 17,
567 }
568 ),
569 "{err:?}"
570 );
571 }
572
573 #[rstest]
574 #[case::origin(0, 0)]
575 #[case::x_only(1, 0)]
576 #[case::y_only(0, 1)]
577 #[case::both_axes(500, 300)]
578 #[case::negative_coords(-4000, -9000)]
579 fn encode_morton_round_trips_through_decode_delta(#[case] x: i32, #[case] y: i32) {
580 let code = MORTON.encode_morton(x, y).unwrap();
581 assert_eq!(MORTON.decode_delta(&[code], &mut dec()).unwrap(), [x, y]);
582 }
583
584 #[rstest]
585 #[case::x_below_the_shift(-20_000, 0)]
586 #[case::y_below_the_shift(0, -20_000)]
587 fn encode_morton_rejects_a_coordinate_the_shift_cannot_lift(#[case] x: i32, #[case] y: i32) {
588 let err = MORTON.encode_morton(x, y).unwrap_err();
589 assert!(matches!(err, MltError::TryFromIntError(_)), "{err:?}");
590 }
591
592 fn expected_coords(pairs: &[Coord<u32>]) -> Vec<i32> {
593 pairs
594 .iter()
595 .flat_map(|&Coord { x, y }| {
596 [
597 x.cast_signed() - COORD_SHIFT.cast_signed(),
598 y.cast_signed() - COORD_SHIFT.cast_signed(),
599 ]
600 })
601 .collect()
602 }
603
604 fn signed_deltas(codes: &[u32]) -> Vec<u32> {
605 let mut prev = 0i32;
606 codes
607 .iter()
608 .map(|&c| {
609 let delta = c.cast_signed().wrapping_sub(prev).cast_unsigned();
610 prev = c.cast_signed();
611 delta
612 })
613 .collect()
614 }
615}