morton-arithmetic 0.1.0
Fast Morton (Z-order) codes with O(1) arithmetic
Loading...
Searching...
No Matches
morton.hpp
Go to the documentation of this file.
1
18
19#ifndef MORTON_MORTON_HPP
20#define MORTON_MORTON_HPP
21
22#include <array>
23#include <climits>
24#include <cstddef>
25#include <cstdint>
26#include <type_traits>
27
28#include "morton/wide_uint.hpp"
29
30// ---- x86 ISA feature plumbing ---------------------------------------------
31// MORTON_X86 : building for x86-64 with GCC/Clang for the host (not CUDA/HIP).
32// Gates the runtime CPU-feature queries and the AVX-512 batch kernels, which
33// use per-function `target` attributes so they need no global -mavx512f/-mbmi2
34// and never run unless the executing CPU actually supports the instructions.
35#if (defined(__x86_64__) || defined(_M_X64)) && (defined(__GNUC__) || defined(__clang__)) && \
36 !defined(__CUDACC__) && !defined(__HIPCC__)
37#define MORTON_X86 1
38#else
39#define MORTON_X86 0
40#endif
41
42// MORTON_ENABLE_RUNTIME_DISPATCH (opt-in, default 0): build a single binary
43// *without* -mbmi2 that still uses PDEP/PEXT when the running CPU has BMI2 and
44// the software fallback otherwise -- removing the build-time portability/speed
45// trade-off (e.g. for distributable wheels). Left off by default so the plain
46// non-BMI2 build stays strictly software (and pdep/pext-free). It self-disables
47// when -mbmi2 is already on (the compile-time intrinsic path is used instead).
48#ifndef MORTON_ENABLE_RUNTIME_DISPATCH
49#define MORTON_ENABLE_RUNTIME_DISPATCH 0
50#endif
51#if MORTON_X86 && MORTON_ENABLE_RUNTIME_DISPATCH && !defined(__BMI2__)
52#define MORTON_X86_RUNTIME_DISPATCH 1
53#else
54#define MORTON_X86_RUNTIME_DISPATCH 0
55#endif
56
57#if MORTON_X86 || defined(__BMI2__)
58#include <immintrin.h>
59#endif
60
61#if defined(__SIZEOF_INT128__)
62#define MORTON_HAS_INT128 1
63#endif
64
65// Maximum supported code width (Dim * Bits). Codes <= 64 use a built-in
66// integer, <= 128 use __uint128_t (where available), and wider codes use the
67// software wide_uint<W> backend.
68#ifndef MORTON_MAX_BITS
69#define MORTON_MAX_BITS 256
70#endif
71
72// Mark functions callable from device code. Expands to nothing for an ordinary
73// host C++ build, so the CPU library is completely unchanged.
74//
75// MORTON_HD (the host/device function marker) lives in its own header so that headers included
76// above — wide_uint.hpp in particular — can annotate their operators device-callable too. It is
77// already pulled in transitively via wide_uint.hpp; include it here as well for the
78// direct/standalone case. See morton/hd.hpp for the Kokkos-vs-raw-CUDA-vs-host resolution.
79#include "morton/hd.hpp"
80
81namespace morton {
82
83namespace detail {
84
85#if defined(MORTON_HAS_INT128)
86using uint128_t = unsigned __int128;
87#endif
88
89// Smallest unsigned type that holds at least NBits bits: a built-in where one
90// exists, otherwise a wide_uint of the right number of 64-bit words.
91template <unsigned NBits>
92struct uint_for {
93#if defined(MORTON_HAS_INT128)
94 static constexpr unsigned builtin_max = 128;
95#else
96 static constexpr unsigned builtin_max = 64;
97#endif
98 using builtin = std::conditional_t<
99 (NBits <= 8), std::uint8_t,
100 std::conditional_t<
101 (NBits <= 16), std::uint16_t,
102 std::conditional_t<(NBits <= 32), std::uint32_t,
103#if defined(MORTON_HAS_INT128)
104 std::conditional_t<(NBits <= 64), std::uint64_t, uint128_t>
105#else
106 std::uint64_t
107#endif
108 >>>;
109 using type = std::conditional_t<(NBits <= builtin_max), builtin, wide_uint<(NBits + 63) / 64>>;
110};
111template <unsigned NBits>
113
114// True during constant evaluation. Lets a constexpr function avoid the
115// (non-constexpr) BMI2 intrinsics when evaluated at compile time.
116MORTON_HD constexpr bool is_consteval() noexcept {
117#if defined(__cpp_if_consteval)
118 if consteval {
119 return true;
120 } else {
121 return false;
122 }
123#elif defined(__GNUC__) || defined(__clang__)
124 return __builtin_is_constant_evaluated();
125#else
126 return false;
127#endif
128}
129
130// 3^n, as a compile-time value (size of a Moore neighbourhood including self).
131MORTON_HD constexpr unsigned pow3(unsigned n) {
132 unsigned r = 1;
133 for (unsigned i = 0; i < n; ++i)
134 r *= 3;
135 return r;
136}
137
138// Build the mask selecting the bits that belong to axis `d` in a code with
139// `Dim` interleaved axes of `Bits` bits each: positions d, d+Dim, d+2*Dim, ...
140template <unsigned Dim, unsigned Bits, typename Code>
141MORTON_HD constexpr Code axis_mask(unsigned d) {
142 Code m = 0;
143 for (unsigned i = 0; i < Bits; ++i)
144 m |= (Code(1) << (i * Dim + d));
145 return m;
146}
147
148// Software deposit: spread the low `Bits` bits of x across positions
149// d, d+Dim, ... (the portable fallback for PDEP). Works for any code width.
150template <unsigned Dim, unsigned Bits, typename Code, typename Coord>
151MORTON_HD constexpr Code spread_sw(Coord x, unsigned d) {
152 Code r = 0;
153 for (unsigned i = 0; i < Bits; ++i)
154 r |= Code(Code(x >> i) & Code(1)) << (i * Dim + d);
155 return r;
156}
157
158// Software extract: inverse of spread_sw (the portable fallback for PEXT).
159template <unsigned Dim, unsigned Bits, typename Code, typename Coord>
160MORTON_HD constexpr Coord compact_sw(Code c, unsigned d) {
161 Coord r = 0;
162 for (unsigned i = 0; i < Bits; ++i)
163 r |= Coord(Coord(c >> (i * Dim + d)) & Coord(1)) << i;
164 return r;
165}
166
167#if MORTON_X86
168// Cached runtime CPU-feature queries. __builtin_cpu_supports returns a nonzero
169// bitfield when the feature is present, so compare against 0. The first call
170// triggers detection; results are memoised in function-local statics.
171inline bool cpu_has_bmi2() {
172 static const bool v = __builtin_cpu_supports("bmi2") != 0;
173 return v;
174}
175inline bool cpu_has_avx2() {
176 static const bool v = __builtin_cpu_supports("avx2") != 0;
177 return v;
178}
179inline bool cpu_has_avx512f() {
180 static const bool v = __builtin_cpu_supports("avx512f") != 0;
181 return v;
182}
183#endif
184
185#if MORTON_X86_RUNTIME_DISPATCH
186// Compiled with BMI2 codegen for just these two functions (via the `target`
187// attribute), so the rest of the binary stays portable; only ever called from
188// the !is_consteval() runtime path guarded by cpu_has_bmi2().
189__attribute__((target("bmi2"))) inline std::uint64_t pdep_u64_hw(std::uint64_t v, std::uint64_t m) {
190 return _pdep_u64(v, m);
191}
192__attribute__((target("bmi2"))) inline std::uint64_t pext_u64_hw(std::uint64_t v, std::uint64_t m) {
193 return _pext_u64(v, m);
194}
195#endif
196
197} // namespace detail
198
202template <unsigned Dim, unsigned Bits>
203class Morton {
204 static_assert(Dim >= 1, "Dim must be >= 1");
205 static_assert(Bits >= 1, "Bits must be >= 1");
206 static_assert(Dim * Bits <= MORTON_MAX_BITS,
207 "Dim * Bits exceeds MORTON_MAX_BITS (raise it if you need wider codes)");
208
209 public:
210 static constexpr unsigned dimensions = Dim;
211 static constexpr unsigned bits_per_axis = Bits;
212 static constexpr unsigned code_bits = Dim * Bits;
213
216
217 private:
218 static constexpr unsigned code_type_bits = sizeof(code_type) * CHAR_BIT;
219 static constexpr unsigned coord_type_bits = sizeof(coord_type) * CHAR_BIT;
220
221 public:
223 static constexpr code_type field_mask = (code_bits >= code_type_bits)
224 ? code_type(~code_type(0))
225 : code_type((code_type(1) << code_bits) - 1);
226
228 static constexpr coord_type coord_max = (Bits >= coord_type_bits)
230 : coord_type((coord_type(1) << Bits) - 1);
231
232 static constexpr unsigned octants = unsigned(1) << Dim; // children per node
233
234 // ---- construction -----------------------------------------------------
235
236 MORTON_HD constexpr Morton() noexcept : code_(0) {}
237
239 MORTON_HD static constexpr Morton from_code(code_type raw) noexcept {
240 Morton m;
241 m.code_ = raw & field_mask;
242 return m;
243 }
244
246 template <typename... Cs, typename = std::enable_if_t<sizeof...(Cs) == Dim>>
247 MORTON_HD static constexpr Morton encode(Cs... coords) {
248 coord_type tmp[Dim] = {static_cast<coord_type>(coords)...};
249 Morton m;
250 for (unsigned d = 0; d < Dim; ++d)
251 m.code_ |= deposit(tmp[d], d);
252 return m;
253 }
254
256 MORTON_HD static constexpr Morton encode(const std::array<coord_type, Dim>& c) {
257 Morton m;
258 for (unsigned d = 0; d < Dim; ++d)
259 m.code_ |= deposit(c[d], d);
260 return m;
261 }
262
263 // ---- access -----------------------------------------------------------
264
265 MORTON_HD constexpr code_type code() const noexcept { return code_; }
266
268 MORTON_HD constexpr coord_type get(unsigned d) const { return extract(code_, d); }
269
271 MORTON_HD constexpr std::array<coord_type, Dim> decode() const {
272 std::array<coord_type, Dim> out{};
273 for (unsigned d = 0; d < Dim; ++d)
274 out[d] = extract(code_, d);
275 return out;
276 }
277
279 MORTON_HD constexpr void set(unsigned d, coord_type value) {
280 code_ = (code_ & keep_mask(d)) | deposit(value, d);
281 }
282
283 // ---- the headline: arithmetic in Morton space -------------------------
284 //
285 // Each of these touches only the bits of one axis and leaves the other
286 // axes' interleaved bits exactly where they are -- no decode/encode.
287 // All wrap modulo 2^Bits on the affected axis.
288
290 MORTON_HD constexpr void add(unsigned d, coord_type k) {
291 const code_type M = axis_mask(d);
292 code_type s = (code_ | ~M) + deposit(k, d);
293 code_ = (s & M) | (code_ & M_complement(d));
294 }
295
297 MORTON_HD constexpr void sub(unsigned d, coord_type k) {
298 const code_type M = axis_mask(d);
299 code_type s = (code_ & M) - deposit(k, d);
300 code_ = (s & M) | (code_ & M_complement(d));
301 }
302
304 MORTON_HD constexpr void inc(unsigned d) {
305 const code_type M = axis_mask(d);
306 code_type s = (code_ | ~M) + lsb(d);
307 code_ = (s & M) | (code_ & M_complement(d));
308 }
309
311 MORTON_HD constexpr void dec(unsigned d) {
312 const code_type M = axis_mask(d);
313 code_type s = (code_ & M) - lsb(d);
314 code_ = (s & M) | (code_ & M_complement(d));
315 }
316
318 MORTON_HD constexpr Morton neighbor(unsigned d, int dir) const {
319 Morton m = *this;
320 if (dir >= 0)
321 m.inc(d);
322 else
323 m.dec(d);
324 return m;
325 }
326
327 // ---- saturating / checked arithmetic (do not wrap) --------------------
328
330 MORTON_HD constexpr void add_sat(unsigned d, coord_type k) {
331 coord_type cur = get(d);
333 set(d, (k > room) ? coord_max : coord_type(cur + k));
334 }
335
337 MORTON_HD constexpr void sub_sat(unsigned d, coord_type k) {
338 coord_type cur = get(d);
339 set(d, (k > cur) ? coord_type(0) : coord_type(cur - k));
340 }
341
344 MORTON_HD constexpr bool try_add(unsigned d, coord_type k) {
345 coord_type cur = get(d);
346 if (k > coord_type(coord_max - cur))
347 return false;
348 set(d, coord_type(cur + k));
349 return true;
350 }
351
353 MORTON_HD constexpr bool try_sub(unsigned d, coord_type k) {
354 coord_type cur = get(d);
355 if (k > cur)
356 return false;
357 set(d, coord_type(cur - k));
358 return true;
359 }
360
361 // ---- neighbour sets ----------------------------------------------------
362
365 MORTON_HD constexpr std::array<Morton, 2 * Dim> face_neighbors() const {
366 std::array<Morton, 2 * Dim> out{};
367 for (unsigned d = 0; d < Dim; ++d) {
368 out[2 * d] = neighbor(d, -1);
369 out[2 * d + 1] = neighbor(d, +1);
370 }
371 return out;
372 }
373
376 MORTON_HD constexpr std::array<Morton, detail::pow3(Dim) - 1> all_neighbors() const {
377 std::array<Morton, detail::pow3(Dim) - 1> out{};
378 unsigned n = 0;
379 for (unsigned i = 0; i < detail::pow3(Dim); ++i) {
380 int off[Dim];
381 unsigned t = i;
382 bool zero = true;
383 for (unsigned d = 0; d < Dim; ++d) {
384 off[d] = int(t % 3) - 1;
385 if (off[d] != 0)
386 zero = false;
387 t /= 3;
388 }
389 if (zero)
390 continue; // skip self
391 Morton m = *this;
392 for (unsigned d = 0; d < Dim; ++d) {
393 if (off[d] > 0)
394 m.inc(d);
395 else if (off[d] < 0)
396 m.dec(d);
397 }
398 out[n++] = m;
399 }
400 return out;
401 }
402
403 // ---- octree / hierarchy navigation ------------------------------------
404 //
405 // A cell at `level` covers a 2^level block per axis and has its low
406 // level*Dim code bits zero. These helpers express that hierarchy directly.
407
410 MORTON_HD constexpr Morton ancestor(unsigned level) const {
411 if (level * Dim >= code_bits)
412 return Morton{};
413 code_type lowmask = (code_type(1) << (level * Dim)) - 1;
414 return from_code(code_ & ~lowmask);
415 }
416
419 MORTON_HD constexpr unsigned child_index(unsigned level) const {
420 return unsigned((code_ >> (level * Dim)) & (octants - 1));
421 }
422
425 MORTON_HD constexpr Morton child(unsigned level, unsigned octant) const {
426 unsigned shift = (level - 1) * Dim;
427 code_type cleared = code_ & ~(code_type(octants - 1) << shift);
428 return from_code(cleared | (code_type(octant) << shift));
429 }
430
431 // ---- Z-order successor / predecessor ----------------------------------
432
434 code_ = (code_ + 1) & field_mask;
435 return *this;
436 }
437 MORTON_HD constexpr Morton operator++(int) noexcept {
438 Morton t = *this;
439 ++*this;
440 return t;
441 }
443 code_ = (code_ - 1) & field_mask;
444 return *this;
445 }
446 MORTON_HD constexpr Morton operator--(int) noexcept {
447 Morton t = *this;
448 --*this;
449 return t;
450 }
451
452 // ---- comparison (Z-order) ---------------------------------------------
453
454 friend MORTON_HD constexpr bool operator==(Morton a, Morton b) noexcept {
455 return a.code_ == b.code_;
456 }
457 friend MORTON_HD constexpr bool operator!=(Morton a, Morton b) noexcept {
458 return a.code_ != b.code_;
459 }
460 friend MORTON_HD constexpr bool operator<(Morton a, Morton b) noexcept {
461 return a.code_ < b.code_;
462 }
463 friend MORTON_HD constexpr bool operator<=(Morton a, Morton b) noexcept {
464 return a.code_ <= b.code_;
465 }
466 friend MORTON_HD constexpr bool operator>(Morton a, Morton b) noexcept {
467 return a.code_ > b.code_;
468 }
469 friend MORTON_HD constexpr bool operator>=(Morton a, Morton b) noexcept {
470 return a.code_ >= b.code_;
471 }
472
473 // ---- low-level deposit/extract ----------------------------------------
474
475 MORTON_HD static constexpr code_type deposit(coord_type x, unsigned d) {
476 if constexpr (code_bits <= 64) {
477 // PDEP is a host x86 instruction; never emit it in CUDA device code
478 // (where __CUDA_ARCH__ is defined), even if the host was -mbmi2.
479#if defined(__BMI2__) && !defined(__CUDA_ARCH__)
481 return code_type(_pdep_u64(std::uint64_t(x), std::uint64_t(axis_mask(d))));
482#elif MORTON_X86_RUNTIME_DISPATCH
483 // Single-binary path: use PDEP when the running CPU has BMI2.
484 if (!detail::is_consteval() && detail::cpu_has_bmi2())
485 return code_type(detail::pdep_u64_hw(std::uint64_t(x), std::uint64_t(axis_mask(d))));
486#endif
487 }
488 return detail::spread_sw<Dim, Bits, code_type, coord_type>(x, d);
489 }
490
491 MORTON_HD static constexpr coord_type extract(code_type c, unsigned d) {
492 if constexpr (code_bits <= 64) {
493#if defined(__BMI2__) && !defined(__CUDA_ARCH__)
495 return coord_type(_pext_u64(std::uint64_t(c), std::uint64_t(axis_mask(d))));
496#elif MORTON_X86_RUNTIME_DISPATCH
497 if (!detail::is_consteval() && detail::cpu_has_bmi2())
498 return coord_type(detail::pext_u64_hw(std::uint64_t(c), std::uint64_t(axis_mask(d))));
499#endif
500 }
501 return detail::compact_sw<Dim, Bits, code_type, coord_type>(c, d);
502 }
503
505 MORTON_HD static constexpr code_type axis_mask(unsigned d) {
506#if defined(__CUDA_ARCH__)
507 // On device, compute the mask (avoids referencing the host static
508 // array from device code); it is a short compile-time-sized loop.
509 return detail::axis_mask<Dim, Bits, code_type>(d);
510#else
511 return axis_masks_[d];
512#endif
513 }
514
515 private:
516 MORTON_HD static constexpr code_type M_complement(unsigned d) {
517 return field_mask & ~axis_mask(d);
518 }
519 MORTON_HD static constexpr code_type keep_mask(unsigned d) { return M_complement(d); }
520 MORTON_HD static constexpr code_type lsb(unsigned d) { return code_type(1) << d; }
521
522 MORTON_HD static constexpr std::array<code_type, Dim> make_masks() {
523 std::array<code_type, Dim> a{};
524 for (unsigned d = 0; d < Dim; ++d)
525 a[d] = detail::axis_mask<Dim, Bits, code_type>(d);
526 return a;
527 }
528 static constexpr std::array<code_type, Dim> axis_masks_ = make_masks();
529
530 code_type code_;
531};
532
533// Convenient aliases for the common cases.
534using Morton2D32 = Morton<2, 32>; // 2D, 32 bits/axis -> 64-bit code
535using Morton2D16 = Morton<2, 16>; // 2D, 16 bits/axis -> 32-bit code
536using Morton3D21 = Morton<3, 21>; // 3D, 21 bits/axis -> 63-bit code
537using Morton3D16 = Morton<3, 16>; // 3D, 16 bits/axis -> 48-bit code
538#if defined(MORTON_HAS_INT128)
539using Morton3D32 = Morton<3, 32>; // 3D, 32 bits/axis -> 96-bit code
540using Morton2D64 = Morton<2, 64>; // 2D, 64 bits/axis -> 128-bit code
541#endif
542
543} // namespace morton
544
545#endif // MORTON_MORTON_HPP
A Morton (Z-order) code interleaving Dim unsigned coordinates of Bits bits each.
Definition morton.hpp:203
friend MORTON_HD constexpr bool operator>=(Morton a, Morton b) noexcept
Definition morton.hpp:469
MORTON_HD constexpr Morton & operator++() noexcept
Definition morton.hpp:433
static MORTON_HD constexpr coord_type extract(code_type c, unsigned d)
Definition morton.hpp:491
friend MORTON_HD constexpr bool operator<=(Morton a, Morton b) noexcept
Definition morton.hpp:463
MORTON_HD constexpr Morton operator--(int) noexcept
Definition morton.hpp:446
MORTON_HD constexpr void inc(unsigned d)
Increment axis d by one. O(1).
Definition morton.hpp:304
MORTON_HD constexpr void sub_sat(unsigned d, coord_type k)
Subtract k from axis d, clamping at 0 instead of wrapping.
Definition morton.hpp:337
static constexpr code_type field_mask
Mask of all valid code bits (handles code_bits == width without UB).
Definition morton.hpp:223
MORTON_HD constexpr void dec(unsigned d)
Decrement axis d by one. O(1).
Definition morton.hpp:311
detail::uint_for_t< Bits > coord_type
Definition morton.hpp:215
static constexpr unsigned bits_per_axis
Definition morton.hpp:211
static MORTON_HD constexpr Morton encode(Cs... coords)
Encode Dim coordinates into a Morton code.
Definition morton.hpp:247
friend MORTON_HD constexpr bool operator!=(Morton a, Morton b) noexcept
Definition morton.hpp:457
MORTON_HD constexpr code_type code() const noexcept
Definition morton.hpp:265
MORTON_HD constexpr std::array< coord_type, Dim > decode() const
Decode all coordinates.
Definition morton.hpp:271
MORTON_HD constexpr void set(unsigned d, coord_type value)
Replace one axis' coordinate (other axes untouched). Wraps mod 2^Bits.
Definition morton.hpp:279
MORTON_HD constexpr Morton & operator--() noexcept
Definition morton.hpp:442
MORTON_HD constexpr Morton() noexcept
Definition morton.hpp:236
MORTON_HD constexpr void sub(unsigned d, coord_type k)
Subtract k from axis d (wraps). O(1), branchless.
Definition morton.hpp:297
friend MORTON_HD constexpr bool operator==(Morton a, Morton b) noexcept
Definition morton.hpp:454
detail::uint_for_t< Dim *Bits > code_type
Definition morton.hpp:214
static MORTON_HD constexpr code_type axis_mask(unsigned d)
Mask of bits belonging to axis d.
Definition morton.hpp:505
MORTON_HD constexpr void add(unsigned d, coord_type k)
Add k to axis d (wraps). O(1), branchless.
Definition morton.hpp:290
static MORTON_HD constexpr Morton from_code(code_type raw) noexcept
Wrap a raw, already-interleaved code value.
Definition morton.hpp:239
MORTON_HD constexpr std::array< Morton, 2 *Dim > face_neighbors() const
The 2*Dim von Neumann (face) neighbours, ordered {axis0-, axis0+, axis1-, axis1+, ....
Definition morton.hpp:365
MORTON_HD constexpr Morton operator++(int) noexcept
Definition morton.hpp:437
MORTON_HD constexpr coord_type get(unsigned d) const
Decode coordinate of a single axis.
Definition morton.hpp:268
MORTON_HD constexpr Morton neighbor(unsigned d, int dir) const
Morton code of the neighbour one step along ± axis d (wraps).
Definition morton.hpp:318
static MORTON_HD constexpr Morton encode(const std::array< coord_type, Dim > &c)
Encode from an array of coordinates.
Definition morton.hpp:256
MORTON_HD constexpr void add_sat(unsigned d, coord_type k)
Add k to axis d, clamping at coord_max instead of wrapping.
Definition morton.hpp:330
MORTON_HD constexpr bool try_add(unsigned d, coord_type k)
Add k to axis d only if it does not overflow past coord_max.
Definition morton.hpp:344
MORTON_HD constexpr bool try_sub(unsigned d, coord_type k)
Subtract k from axis d only if it does not underflow past 0.
Definition morton.hpp:353
MORTON_HD constexpr std::array< Morton, detail::pow3(Dim) - 1 > all_neighbors() const
The 3^Dim - 1 Moore neighbours (all cells differing by -1/0/+1 on each axis, excluding self),...
Definition morton.hpp:376
MORTON_HD constexpr Morton child(unsigned level, unsigned octant) const
The octant-th child origin of a cell that is an ancestor at level (i.e.
Definition morton.hpp:425
static MORTON_HD constexpr code_type deposit(coord_type x, unsigned d)
Definition morton.hpp:475
friend MORTON_HD constexpr bool operator>(Morton a, Morton b) noexcept
Definition morton.hpp:466
MORTON_HD constexpr unsigned child_index(unsigned level) const
Index (0 .
Definition morton.hpp:419
static constexpr coord_type coord_max
Largest representable coordinate value per axis.
Definition morton.hpp:228
static constexpr unsigned octants
Definition morton.hpp:232
static constexpr unsigned code_bits
Definition morton.hpp:212
friend MORTON_HD constexpr bool operator<(Morton a, Morton b) noexcept
Definition morton.hpp:460
MORTON_HD constexpr Morton ancestor(unsigned level) const
Ancestor cell origin at the given level (clears the low level*Dim bits).
Definition morton.hpp:410
static constexpr unsigned dimensions
Definition morton.hpp:210
The MORTON_HD host/device function marker, shared by every core header.
#define MORTON_HD
Definition hd.hpp:27
#define MORTON_HAS_INT128
Definition morton.hpp:62
#define MORTON_MAX_BITS
Definition morton.hpp:69
typename uint_for< NBits >::type uint_for_t
Definition morton.hpp:112
unsigned __int128 uint128_t
Definition morton.hpp:86
MORTON_HD constexpr Coord compact_sw(Code c, unsigned d)
Definition morton.hpp:160
MORTON_HD constexpr bool is_consteval() noexcept
Definition morton.hpp:116
MORTON_HD constexpr Code axis_mask(unsigned d)
Definition morton.hpp:141
MORTON_HD constexpr Code spread_sw(Coord x, unsigned d)
Definition morton.hpp:151
MORTON_HD constexpr unsigned pow3(unsigned n)
Definition morton.hpp:131
Definition batch.hpp:26
Definition morton.hpp:92
std::conditional_t<(NBits<=8), std::uint8_t, std::conditional_t<(NBits<=16), std::uint16_t, std::conditional_t<(NBits<=32), std::uint32_t, std::conditional_t<(NBits<=64), std::uint64_t, uint128_t > > > > builtin
Definition morton.hpp:108
std::conditional_t<(NBits<=builtin_max), builtin, wide_uint<(NBits+63)/64 > > type
Definition morton.hpp:109
static constexpr unsigned builtin_max
Definition morton.hpp:94
Fixed-width unsigned integer of.
Definition wide_uint.hpp:29
Minimal fixed-width unsigned integer backing Morton codes wider than 128 bits.