@tty-pt/islet 0.0.0-stage → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,536 @@
1
+ #ifndef MORTON_H
2
+ #define MORTON_H
3
+
4
+ /**
5
+ * @file morton.h
6
+ * @brief Morton code (Z-order) encoding and decoding utilities.
7
+ *
8
+ * Morton codes, also known as Z-order codes, are a way to map multi-dimensional
9
+ * coordinates to a single dimension while preserving spatial locality. They
10
+ * interleave the binary representations of each coordinate dimension, creating
11
+ * a space-filling curve that visits all points in a recursive Z pattern.
12
+ *
13
+ * Benefits:
14
+ * - Preserves spatial locality: nearby points in N-D space have nearby Morton codes
15
+ * - Enables efficient range queries on 1D data structures (hash tables, B-trees)
16
+ * - Simple bitwise operations for encoding/decoding
17
+ *
18
+ * This implementation is optimized for 3D coordinates (int16_t per dimension)
19
+ * and produces 64-bit Morton codes (48 bits used for 3D, 16 bits reserved).
20
+ * 4D coordinates use a dense stride-4 packing that fills all 64 bits.
21
+ * The 2D x 32-bit config (int32_t per dimension) is the dense
22
+ * stride-2 partner: 2 x 32 = 64 bits, no reserved bits.
23
+ *
24
+ * Prefer the config objects (pointcfg.h: Point1_2..Point4_2, Point2_4)
25
+ * in application code; these inlines remain the tight-loop fast path.
26
+ *
27
+ * References:
28
+ * - Morton, G.M. (1966). "A computer Oriented Geodetic Data Base"
29
+ * - http://www.vision-tools.com/h-tropf/multidimensionalrangequery.pdf
30
+ * - https://en.wikipedia.org/wiki/Z-order_curve
31
+ *
32
+ * @see islet_core
33
+ */
34
+
35
+ #include <stdint.h>
36
+ #include <limits.h>
37
+
38
+ /* Optimization tunable — a 0/1 flag. ISLET_USE_PDEP gates the BMI2
39
+ * PDEP/PEXT Morton kernels; it defaults to 1 and only activates when the
40
+ * TU is compiled with -mbmi2 (macro __BMI2__). Override with
41
+ * -DISLET_USE_PDEP=0/1. Outputs are bit-identical to the scalar kernels
42
+ * either way; see docs/PERF.md. On AMD Zen ≤3 the PDEP instruction
43
+ * is microcoded and may be slower than the scalar path — opt out
44
+ * there. */
45
+ #ifndef ISLET_USE_PDEP
46
+ #define ISLET_USE_PDEP 1
47
+ #endif
48
+
49
+ /** @defgroup islet_morton Morton code helpers
50
+ * @brief Encode and decode Morton (Z-order) codes for spatial indexing.
51
+ *
52
+ * Morton codes interleave the bits of multi-dimensional coordinates to create
53
+ * a single linear ordering that preserves spatial locality. For example, in 2D:
54
+ *
55
+ * Coordinate (x=5, y=3):
56
+ * - x = 5 = 0b0101 (binary)
57
+ * - y = 3 = 0b0011 (binary)
58
+ * - Interleaved: 0b00110011 = 51 (y bits in odd positions, x in even)
59
+ *
60
+ * In 3D, the pattern extends with z bits in every third position.
61
+ *
62
+ * Coordinate System:
63
+ * - Input: int16_t signed coordinates (-32768 to 32767)
64
+ * - Internal: uint16_t unsigned coordinates (0 to 65535) via offset mapping
65
+ * - Output: uint64_t Morton code (48 bits for 3D with the upper 16 bits
66
+ * reserved; all 64 bits for 4D)
67
+ *
68
+ * The conversion from signed to unsigned preserves ordering:
69
+ * -32768 → 0, -32767 → 1, ..., 0 → 32768, ..., 32767 → 65535
70
+ *
71
+ * @note Currently optimized for 3D/4D with fast bit-manipulation
72
+ * algorithms (stride-3 packing for dims 1-3, dense stride-4
73
+ * packing for dim 4, dense stride-2 packing for the 2D x 32-bit
74
+ * config). 1D/2D/3D codes are bit-identical to
75
+ * v0.5.0; 4D codes are new. When compiled with -mbmi2 the encode
76
+ * and decode kernels use PDEP/PEXT for a 2-3× speedup over the
77
+ * scalar path (default-on, opt out with -DISLET_USE_PDEP=0).
78
+ *
79
+ * @note All dimensions share the uint64 key space: use one dimension
80
+ * per database.
81
+ *
82
+ * @see islet_core
83
+ * @see islet_point
84
+ * @{
85
+ */
86
+
87
+ /* When libislet.c builds the external ABI wrappers it defines
88
+ * ISLET_MORTON_RENAME_FOR_WRAPPERS before including this header, so the
89
+ * static inline versions take the _il names and don't clash with the
90
+ * extern definitions in that TU. Consumers never define it. */
91
+ #ifdef ISLET_MORTON_RENAME_FOR_WRAPPERS
92
+ #define morton_set_1 morton_set_1_il
93
+ #define morton_set_2 morton_set_2_il
94
+ #define morton_set_3 morton_set_3_il
95
+ #define morton_set_4 morton_set_4_il
96
+ #define morton_set_2_32 morton_set_2_32_il
97
+ #define morton_get_1 morton_get_1_il
98
+ #define morton_get_2 morton_get_2_il
99
+ #define morton_get_3 morton_get_3_il
100
+ #define morton_get_4 morton_get_4_il
101
+ #define morton_get_2_32 morton_get_2_32_il
102
+ #endif
103
+
104
+ static inline uint16_t
105
+ islet_unsign(int16_t n)
106
+ {
107
+ return (uint16_t)(n + SHRT_MAX + 1);
108
+ }
109
+
110
+ static inline int16_t
111
+ islet_sign(uint16_t n)
112
+ {
113
+ return (int16_t)(n - SHRT_MAX - 1);
114
+ }
115
+
116
+ /* 32-bit-lane bias helpers for the 2D x 32-bit dense config. The
117
+ * unsigned lane is the full uint32 range: -2^31 -> 0, ..., 0 ->
118
+ * 0x80000000, ..., 2^31-1 -> 0xFFFFFFFF. All arithmetic is modulo
119
+ * 2^32, so the add/sub wrap exactly like the 16-bit pair above. */
120
+ static inline uint32_t
121
+ islet_unsign32(int32_t n)
122
+ {
123
+ return (uint32_t)n + 0x80000000u;
124
+ }
125
+
126
+ static inline int32_t
127
+ islet_sign32(uint32_t n)
128
+ {
129
+ return (int32_t)(n - 0x80000000u);
130
+ }
131
+
132
+ static inline uint64_t
133
+ islet_spread3(uint32_t x)
134
+ {
135
+ uint64_t v = x & 0xFFFFu;
136
+
137
+ v = (v | (v << 32)) & 0x1F00000000FFFFULL;
138
+ v = (v | (v << 16)) & 0x1F0000FF0000FFULL;
139
+ v = (v | (v << 8)) & 0x100F00F00F00F00FULL;
140
+ v = (v | (v << 4)) & 0x10C30C30C30C30C3ULL;
141
+ v = (v | (v << 2)) & 0x1249249249249249ULL;
142
+
143
+ return v;
144
+ }
145
+
146
+ static inline uint64_t
147
+ islet_compact_axis(uint64_t code, uint32_t shift)
148
+ {
149
+ code >>= shift;
150
+ code &= 0x1249249249249249ULL;
151
+ code = (code ^ (code >> 2)) & 0x10C30C30C30C30C3ULL;
152
+ code = (code ^ (code >> 4)) & 0x100F00F00F00F00FULL;
153
+ code = (code ^ (code >> 8)) & 0x1F0000FF0000FFULL;
154
+ code = (code ^ (code >> 16)) & 0x1F00000000FFFFULL;
155
+ code = (code ^ (code >> 32)) & 0x00000000001FFFFFULL;
156
+ return (uint32_t) code;
157
+ }
158
+
159
+ /* spread2(x):
160
+ * Take x in [0..0xFFFFFFFF] and produce a 64-bit word where its
161
+ * bit-i goes to bit-(2*i) in the result (even positions). 2D x 32-bit
162
+ * needs all 64 bits (2 x 32), so there are no reserved top bits.
163
+ *
164
+ * Part of a Morton-2D32 encode: code = spread2(x) | spread2(y)<<1
165
+ */
166
+ static inline uint64_t
167
+ islet_spread2(uint32_t x)
168
+ {
169
+ uint64_t v = x;
170
+
171
+ v = (v | (v << 16)) & 0x0000FFFF0000FFFFULL;
172
+ v = (v | (v << 8)) & 0x00FF00FF00FF00FFULL;
173
+ v = (v | (v << 4)) & 0x0F0F0F0F0F0F0F0FULL;
174
+ v = (v | (v << 2)) & 0x3333333333333333ULL;
175
+ v = (v | (v << 1)) & 0x5555555555555555ULL;
176
+
177
+ return v;
178
+ }
179
+
180
+ /* compact_axis2(): collect one out of every 2 bits from 'code',
181
+ * starting at 'shift' (0 = x, 1 = y).
182
+ * Returns the low-order 32 bits containing that coordinate.
183
+ */
184
+ static inline uint64_t
185
+ islet_compact_axis2(uint64_t code, uint32_t shift)
186
+ {
187
+ code >>= shift;
188
+ code &= 0x5555555555555555ULL;
189
+ code = (code ^ (code >> 1)) & 0x3333333333333333ULL;
190
+ code = (code ^ (code >> 2)) & 0x0F0F0F0F0F0F0F0FULL;
191
+ code = (code ^ (code >> 4)) & 0x00FF00FF00FF00FFULL;
192
+ code = (code ^ (code >> 8)) & 0x0000FFFF0000FFFFULL;
193
+ code = (code ^ (code >> 16)) & 0x00000000FFFFFFFFULL;
194
+ return (uint32_t) code;
195
+ }
196
+
197
+ /* spread4(x):
198
+ * Take x ∈ [0..0xFFFF] and produce a 64-bit word where
199
+ * its bit-i goes to bit-(4*i) in the result. 4D needs all 64 bits
200
+ * (4 × 16), so unlike the 3D path there are no reserved top bits.
201
+ *
202
+ * Part of a Morton-4D encode: code = spread4(x)
203
+ * | spread4(y)<<1
204
+ * | spread4(z)<<2
205
+ * | spread4(w)<<3
206
+ */
207
+ static inline uint64_t
208
+ islet_spread4(uint32_t x)
209
+ {
210
+ uint64_t v = x & 0xFFFFu;
211
+
212
+ /* separate high/low bytes with a 24-bit gap */
213
+ v = (v | (v << 24)) & 0xFF000000FFULL;
214
+ /* down to nibbles */
215
+ v = (v | (v << 12)) & 0x000F000F000F000FULL;
216
+ /* down to bit pairs */
217
+ v = (v | (v << 6)) & 0x0303030303030303ULL;
218
+ /* final 4-bit interleave */
219
+ v = (v | (v << 3)) & 0x1111111111111111ULL;
220
+
221
+ return v;
222
+ }
223
+
224
+ /* compact_axis4(): collect one out of every 4 bits from 'code',
225
+ * starting at 'shift' (0 = x, 1 = y, 2 = z, 3 = w).
226
+ * Returns the low-order 16 bits containing that coordinate.
227
+ */
228
+ static inline uint64_t
229
+ islet_compact_axis4(uint64_t code, uint32_t shift)
230
+ {
231
+ code >>= shift;
232
+ code &= 0x1111111111111111ULL;
233
+ code = (code ^ (code >> 3)) & 0x0303030303030303ULL;
234
+ code = (code ^ (code >> 6)) & 0x000F000F000F000FULL;
235
+ code = (code ^ (code >> 12)) & 0x000000FF000000FFULL;
236
+ code = (code ^ (code >> 24)) & 0x000000000000FFFFULL;
237
+ return (uint16_t) code;
238
+ }
239
+
240
+ static inline void
241
+ islet_decode3(uint64_t code, uint32_t *x, uint32_t *y, uint32_t *z)
242
+ {
243
+ static const uint64_t mask_off = 0x0000FFFFFFFFFFFFULL;
244
+ code &= mask_off;
245
+ *x = islet_compact_axis(code, 0);
246
+ *y = islet_compact_axis(code, 1);
247
+ *z = islet_compact_axis(code, 2);
248
+ }
249
+
250
+ static inline void
251
+ islet_decode4(uint64_t code,
252
+ uint32_t *x, uint32_t *y, uint32_t *z, uint32_t *w)
253
+ {
254
+ *x = (uint32_t)islet_compact_axis4(code, 0);
255
+ *y = (uint32_t)islet_compact_axis4(code, 1);
256
+ *z = (uint32_t)islet_compact_axis4(code, 2);
257
+ *w = (uint32_t)islet_compact_axis4(code, 3);
258
+ }
259
+
260
+ /**
261
+ * @brief Encode multi-dimensional coordinates into Morton codes.
262
+ *
263
+ * These per-dimension specializations are the encode API. Each encodes
264
+ * exactly one dimension count; there is no runtime `dim` argument to
265
+ * branch on, so the compiler sees a fully unrolled expression.
266
+ *
267
+ * Algorithm (dims 1-3):
268
+ * 1. Convert signed int16_t to unsigned uint16_t (add 32768)
269
+ * 2. Spread each 16-bit coordinate across 48 bits (every 3rd bit)
270
+ * 3. Combine: code = spread(x) | spread(y)<<1 | spread(z)<<2
271
+ *
272
+ * Dim 4 uses dense stride-4 packing that fills all 64 bits. Dims 1-3
273
+ * code values are bit-identical to v0.5.0; 4D codes are new.
274
+ *
275
+ * @param[in] p Input point. Array of int16_t with at least the
276
+ * dimension count of the function used. Coordinates range
277
+ * from -32768 to 32767.
278
+ *
279
+ * @return 64-bit Morton code. For 1-3D, uses 48 bits (16 bits per
280
+ * dimension) with the upper 16 bits reserved; 4D fills all 64
281
+ * bits.
282
+ *
283
+ * Example (3D):
284
+ * @code
285
+ * int16_t point[3] = {10, -5, 100};
286
+ * uint64_t code = morton_set_3(point);
287
+ * // code now contains the interleaved bit representation
288
+ * @endcode
289
+ *
290
+ * Example (round-trip verification):
291
+ * @code
292
+ * int16_t original[3] = {123, -456, 789};
293
+ * uint64_t code = morton_set_3(original);
294
+ * int16_t decoded[3];
295
+ * morton_get_3(decoded, code);
296
+ * // decoded[0]==123, decoded[1]==-456, decoded[2]==789
297
+ * @endcode
298
+ *
299
+ * @see morton_set_1 morton_set_2 morton_set_4
300
+ * @see morton_get_1 morton_get_2 morton_get_3 morton_get_4
301
+ * @see islet_put
302
+ * @see islet_get
303
+ */
304
+ static inline uint64_t
305
+ morton_set_1(int16_t *p)
306
+ {
307
+ uint16_t up0 = islet_unsign(p[0]);
308
+
309
+ #if ISLET_USE_PDEP && defined(__BMI2__)
310
+ return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL);
311
+ #else
312
+ return islet_spread3(up0);
313
+ #endif
314
+ }
315
+
316
+ /**
317
+ * @brief Encode a 2D point. See morton_set_1() for the family docs.
318
+ */
319
+ static inline uint64_t
320
+ morton_set_2(int16_t *p)
321
+ {
322
+ uint16_t up0 = islet_unsign(p[0]);
323
+ uint16_t up1 = islet_unsign(p[1]);
324
+
325
+ #if ISLET_USE_PDEP && defined(__BMI2__)
326
+ return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL)
327
+ | __builtin_ia32_pdep_di(up1, 0x2492492492492492ULL);
328
+ #else
329
+ return islet_spread3(up0) | (islet_spread3(up1) << 1);
330
+ #endif
331
+ }
332
+
333
+ /**
334
+ * @brief Encode a 3D point. See morton_set_1() for the family docs.
335
+ */
336
+ static inline uint64_t
337
+ morton_set_3(int16_t *p)
338
+ {
339
+ uint16_t up0 = islet_unsign(p[0]);
340
+ uint16_t up1 = islet_unsign(p[1]);
341
+ uint16_t up2 = islet_unsign(p[2]);
342
+
343
+ #if ISLET_USE_PDEP && defined(__BMI2__)
344
+ return __builtin_ia32_pdep_di(up0, 0x1249249249249249ULL)
345
+ | __builtin_ia32_pdep_di(up1, 0x2492492492492492ULL)
346
+ | __builtin_ia32_pdep_di(up2, 0x4924924924924924ULL);
347
+ #else
348
+ return islet_spread3(up0)
349
+ | (islet_spread3(up1) << 1)
350
+ | (islet_spread3(up2) << 2);
351
+ #endif
352
+ }
353
+
354
+ /**
355
+ * @brief Encode a 4D point. See morton_set_1() for the family docs.
356
+ */
357
+ static inline uint64_t
358
+ morton_set_4(int16_t *p)
359
+ {
360
+ uint16_t up0 = islet_unsign(p[0]);
361
+ uint16_t up1 = islet_unsign(p[1]);
362
+ uint16_t up2 = islet_unsign(p[2]);
363
+ uint16_t up3 = islet_unsign(p[3]);
364
+
365
+ #if ISLET_USE_PDEP && defined(__BMI2__)
366
+ return __builtin_ia32_pdep_di(up0, 0x1111111111111111ULL)
367
+ | __builtin_ia32_pdep_di(up1, 0x2222222222222222ULL)
368
+ | __builtin_ia32_pdep_di(up2, 0x4444444444444444ULL)
369
+ | __builtin_ia32_pdep_di(up3, 0x8888888888888888ULL);
370
+ #else
371
+ return islet_spread4(up0)
372
+ | (islet_spread4(up1) << 1)
373
+ | (islet_spread4(up2) << 2)
374
+ | (islet_spread4(up3) << 3);
375
+ #endif
376
+ }
377
+
378
+ /**
379
+ * @brief Encode a 2D x 32-bit point. Dense full-64-bit layout
380
+ * (2 x 32 bits); part of the dense config family, not the
381
+ * legacy 16-bit-lane sparse layout that morton_set_2() uses.
382
+ *
383
+ * Algorithm:
384
+ * 1. Convert signed int32_t to unsigned uint32_t (add 2^31)
385
+ * 2. Spread each 32-bit coordinate across 64 bits (every 2nd bit)
386
+ * 3. Combine: code = spread2(x) | spread2(y)<<1
387
+ *
388
+ * @param[in] p Input point. Array of int32_t with at least 2 elements.
389
+ * Coordinates range from -2147483648 to 2147483647.
390
+ *
391
+ * @return 64-bit Morton code filling all 64 bits (no reserved bits).
392
+ */
393
+ static inline uint64_t
394
+ morton_set_2_32(int32_t *p)
395
+ {
396
+ uint32_t up0 = islet_unsign32(p[0]);
397
+ uint32_t up1 = islet_unsign32(p[1]);
398
+
399
+ #if ISLET_USE_PDEP && defined(__BMI2__)
400
+ return __builtin_ia32_pdep_di(up0, 0x5555555555555555ULL)
401
+ | __builtin_ia32_pdep_di(up1, 0xAAAAAAAAAAAAAAAAULL);
402
+ #else
403
+ return islet_spread2(up0) | (islet_spread2(up1) << 1);
404
+ #endif
405
+ }
406
+
407
+ /**
408
+ * @brief Decode a Morton code into a multi-dimensional coordinate.
409
+ *
410
+ * These per-dimension specializations are the decode API. Each decodes
411
+ * exactly one dimension count; there is no runtime `dim` argument.
412
+ * Inverse of the matching morton_set_N().
413
+ *
414
+ * @param[out] pos Output point. Array of int16_t with space for the
415
+ * dimension count of the function used.
416
+ * @param[in] code Morton code to decode (64-bit).
417
+ *
418
+ * @note The output coordinates will be in range -32768 to 32767 (int16_t).
419
+ *
420
+ * @warning Decoding a Morton code that wasn't created by morton_set_N()
421
+ * with valid coordinates may produce unexpected results (garbage
422
+ * coordinates).
423
+ *
424
+ * Example:
425
+ * @code
426
+ * uint64_t code = 0x123456789ABCULL; // Some Morton code
427
+ * int16_t point[3];
428
+ * morton_get_3(point, code);
429
+ * // point now contains the decoded coordinates
430
+ * @endcode
431
+ *
432
+ * @see morton_get_1 morton_get_2 morton_get_4
433
+ * @see morton_set_1 morton_set_2 morton_set_3 morton_set_4
434
+ * @see islet_iter
435
+ */
436
+ static inline void
437
+ morton_get_1(int16_t *pos, uint64_t code)
438
+ {
439
+ #if ISLET_USE_PDEP && defined(__BMI2__)
440
+ pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
441
+ 0x1249249249249249ULL));
442
+ #else
443
+ pos[0] = islet_sign((uint16_t)islet_compact_axis(code, 0));
444
+ #endif
445
+ }
446
+
447
+ /**
448
+ * @brief Decode a 2D Morton code. See morton_get_1() for the family docs.
449
+ */
450
+ static inline void
451
+ morton_get_2(int16_t *pos, uint64_t code)
452
+ {
453
+ #if ISLET_USE_PDEP && defined(__BMI2__)
454
+ pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
455
+ 0x1249249249249249ULL));
456
+ pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
457
+ 0x2492492492492492ULL));
458
+ #else
459
+ pos[0] = islet_sign((uint16_t)islet_compact_axis(code, 0));
460
+ pos[1] = islet_sign((uint16_t)islet_compact_axis(code, 1));
461
+ #endif
462
+ }
463
+
464
+ /**
465
+ * @brief Decode a 3D Morton code. See morton_get_1() for the family docs.
466
+ */
467
+ static inline void
468
+ morton_get_3(int16_t *pos, uint64_t code)
469
+ {
470
+ #if ISLET_USE_PDEP && defined(__BMI2__)
471
+ pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
472
+ 0x1249249249249249ULL));
473
+ pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
474
+ 0x2492492492492492ULL));
475
+ pos[2] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
476
+ 0x4924924924924924ULL));
477
+ #else
478
+ uint32_t uup[] = { 0, 0, 0 };
479
+
480
+ islet_decode3(code, &uup[0], &uup[1], &uup[2]);
481
+ pos[0] = islet_sign((uint16_t)uup[0]);
482
+ pos[1] = islet_sign((uint16_t)uup[1]);
483
+ pos[2] = islet_sign((uint16_t)uup[2]);
484
+ #endif
485
+ }
486
+
487
+ /**
488
+ * @brief Decode a 4D Morton code. See morton_get_1() for the family docs.
489
+ */
490
+ static inline void
491
+ morton_get_4(int16_t *pos, uint64_t code)
492
+ {
493
+ #if ISLET_USE_PDEP && defined(__BMI2__)
494
+ pos[0] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
495
+ 0x1111111111111111ULL));
496
+ pos[1] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
497
+ 0x2222222222222222ULL));
498
+ pos[2] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
499
+ 0x4444444444444444ULL));
500
+ pos[3] = islet_sign((uint16_t)__builtin_ia32_pext_di(code,
501
+ 0x8888888888888888ULL));
502
+ #else
503
+ uint32_t uup[] = { 0, 0, 0, 0 };
504
+
505
+ islet_decode4(code, &uup[0], &uup[1], &uup[2], &uup[3]);
506
+ pos[0] = islet_sign((uint16_t)uup[0]);
507
+ pos[1] = islet_sign((uint16_t)uup[1]);
508
+ pos[2] = islet_sign((uint16_t)uup[2]);
509
+ pos[3] = islet_sign((uint16_t)uup[3]);
510
+ #endif
511
+ }
512
+
513
+ /**
514
+ * @brief Decode a 2D x 32-bit Morton code. Inverse of morton_set_2_32().
515
+ *
516
+ * @param[out] pos Output point. Array of int32_t with space for
517
+ * 2 elements.
518
+ * @param[in] code Morton code to decode (64-bit, dense layout).
519
+ */
520
+ static inline void
521
+ morton_get_2_32(int32_t *pos, uint64_t code)
522
+ {
523
+ #if ISLET_USE_PDEP && defined(__BMI2__)
524
+ pos[0] = islet_sign32((uint32_t)__builtin_ia32_pext_di(code,
525
+ 0x5555555555555555ULL));
526
+ pos[1] = islet_sign32((uint32_t)__builtin_ia32_pext_di(code,
527
+ 0xAAAAAAAAAAAAAAAAULL));
528
+ #else
529
+ pos[0] = islet_sign32((uint32_t)islet_compact_axis2(code, 0));
530
+ pos[1] = islet_sign32((uint32_t)islet_compact_axis2(code, 1));
531
+ #endif
532
+ }
533
+
534
+ /** @} */
535
+
536
+ #endif