peclet-morton 1.0.2
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// MORTON_HAS_INT128 and detail::uint128_t are defined by wide_uint.hpp (included above), which is
62// the lowest header that needs the built-in; the alias carries the `__extension__` marker that
63// keeps `-Wpedantic` consumers warning-free.
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// Smallest unsigned type that holds at least NBits bits: a built-in where one
86// exists, otherwise a wide_uint of the right number of 64-bit words.
87template <unsigned NBits>
88struct uint_for {
89#if defined(MORTON_HAS_INT128)
90 static constexpr unsigned builtin_max = 128;
91#else
92 static constexpr unsigned builtin_max = 64;
93#endif
94 using builtin = std::conditional_t<
95 (NBits <= 8), std::uint8_t,
96 std::conditional_t<
97 (NBits <= 16), std::uint16_t,
98 std::conditional_t<(NBits <= 32), std::uint32_t,
99#if defined(MORTON_HAS_INT128)
100 std::conditional_t<(NBits <= 64), std::uint64_t, uint128_t>
101#else
102 std::uint64_t
103#endif
104 >>>;
105 using type = std::conditional_t<(NBits <= builtin_max), builtin, wide_uint<(NBits + 63) / 64>>;
106};
107template <unsigned NBits>
109
110// True during constant evaluation. Lets a constexpr function avoid the
111// (non-constexpr) BMI2 intrinsics when evaluated at compile time.
112MORTON_HD constexpr bool is_consteval() noexcept {
113#if defined(__cpp_if_consteval)
114 if consteval {
115 return true;
116 } else {
117 return false;
118 }
119#elif defined(__GNUC__) || defined(__clang__)
120 return __builtin_is_constant_evaluated();
121#else
122 return false;
123#endif
124}
125
126// 3^n, as a compile-time value (size of a Moore neighbourhood including self).
127MORTON_HD constexpr unsigned pow3(unsigned n) {
128 unsigned r = 1;
129 for (unsigned i = 0; i < n; ++i)
130 r *= 3;
131 return r;
132}
133
134// Build the mask selecting the bits that belong to axis `d` in a code with
135// `Dim` interleaved axes of `Bits` bits each: positions d, d+Dim, d+2*Dim, ...
136template <unsigned Dim, unsigned Bits, typename Code>
137MORTON_HD constexpr Code axis_mask(unsigned d) {
138 Code m = 0;
139 for (unsigned i = 0; i < Bits; ++i)
140 m |= (Code(1) << (i * Dim + d));
141 return m;
142}
143
144// Software deposit: spread the low `Bits` bits of x across positions
145// d, d+Dim, ... (the portable fallback for PDEP). Works for any code width.
146template <unsigned Dim, unsigned Bits, typename Code, typename Coord>
147MORTON_HD constexpr Code spread_sw(Coord x, unsigned d) {
148 Code r = 0;
149 for (unsigned i = 0; i < Bits; ++i)
150 r |= Code(Code(x >> i) & Code(1)) << (i * Dim + d);
151 return r;
152}
153
154// Software extract: inverse of spread_sw (the portable fallback for PEXT).
155template <unsigned Dim, unsigned Bits, typename Code, typename Coord>
156MORTON_HD constexpr Coord compact_sw(Code c, unsigned d) {
157 Coord r = 0;
158 for (unsigned i = 0; i < Bits; ++i)
159 r |= Coord(Coord(c >> (i * Dim + d)) & Coord(1)) << i;
160 return r;
161}
162
163#if MORTON_X86
164// Cached runtime CPU-feature queries. __builtin_cpu_supports returns a nonzero
165// bitfield when the feature is present, so compare against 0. The first call
166// triggers detection; results are memoised in function-local statics.
167inline bool cpu_has_bmi2() {
168 static const bool v = __builtin_cpu_supports("bmi2") != 0;
169 return v;
170}
171inline bool cpu_has_avx2() {
172 static const bool v = __builtin_cpu_supports("avx2") != 0;
173 return v;
174}
175inline bool cpu_has_avx512f() {
176 static const bool v = __builtin_cpu_supports("avx512f") != 0;
177 return v;
178}
179#endif
180
181#if MORTON_X86_RUNTIME_DISPATCH
182// Compiled with BMI2 codegen for just these two functions (via the `target`
183// attribute), so the rest of the binary stays portable; only ever called from
184// the !is_consteval() runtime path guarded by cpu_has_bmi2().
185__attribute__((target("bmi2"))) inline std::uint64_t pdep_u64_hw(std::uint64_t v, std::uint64_t m) {
186 return _pdep_u64(v, m);
187}
188__attribute__((target("bmi2"))) inline std::uint64_t pext_u64_hw(std::uint64_t v, std::uint64_t m) {
189 return _pext_u64(v, m);
190}
191#endif
192
193} // namespace detail
194
198template <unsigned Dim, unsigned Bits>
199class Morton {
200 static_assert(Dim >= 1, "Dim must be >= 1");
201 static_assert(Bits >= 1, "Bits must be >= 1");
202 static_assert(Dim * Bits <= MORTON_MAX_BITS,
203 "Dim * Bits exceeds MORTON_MAX_BITS (raise it if you need wider codes)");
204
205 public:
206 static constexpr unsigned dimensions = Dim;
207 static constexpr unsigned bits_per_axis = Bits;
208 static constexpr unsigned code_bits = Dim * Bits;
209
212
213 private:
214 static constexpr unsigned code_type_bits = sizeof(code_type) * CHAR_BIT;
215 static constexpr unsigned coord_type_bits = sizeof(coord_type) * CHAR_BIT;
216
217 public:
219 static constexpr code_type field_mask = (code_bits >= code_type_bits)
220 ? code_type(~code_type(0))
221 : code_type((code_type(1) << code_bits) - 1);
222
224 static constexpr coord_type coord_max = (Bits >= coord_type_bits)
226 : coord_type((coord_type(1) << Bits) - 1);
227
228 static constexpr unsigned octants = unsigned(1) << Dim; // children per node
229
230 // ---- construction -----------------------------------------------------
231
232 MORTON_HD constexpr Morton() noexcept : code_(0) {}
233
235 MORTON_HD static constexpr Morton from_code(code_type raw) noexcept {
236 Morton m;
237 m.code_ = raw & field_mask;
238 return m;
239 }
240
242 template <typename... Cs, typename = std::enable_if_t<sizeof...(Cs) == Dim>>
243 MORTON_HD static constexpr Morton encode(Cs... coords) {
244 coord_type tmp[Dim] = {static_cast<coord_type>(coords)...};
245 Morton m;
246 for (unsigned d = 0; d < Dim; ++d)
247 m.code_ |= deposit(tmp[d], d);
248 return m;
249 }
250
252 MORTON_HD static constexpr Morton encode(const std::array<coord_type, Dim>& c) {
253 Morton m;
254 for (unsigned d = 0; d < Dim; ++d)
255 m.code_ |= deposit(c[d], d);
256 return m;
257 }
258
259 // ---- access -----------------------------------------------------------
260
261 MORTON_HD constexpr code_type code() const noexcept { return code_; }
262
264 MORTON_HD constexpr coord_type get(unsigned d) const { return extract(code_, d); }
265
267 MORTON_HD constexpr std::array<coord_type, Dim> decode() const {
268 std::array<coord_type, Dim> out{};
269 for (unsigned d = 0; d < Dim; ++d)
270 out[d] = extract(code_, d);
271 return out;
272 }
273
275 MORTON_HD constexpr void set(unsigned d, coord_type value) {
276 code_ = (code_ & keep_mask(d)) | deposit(value, d);
277 }
278
279 // ---- the headline: arithmetic in Morton space -------------------------
280 //
281 // Each of these touches only the bits of one axis and leaves the other
282 // axes' interleaved bits exactly where they are -- no decode/encode.
283 // All wrap modulo 2^Bits on the affected axis.
284
286 MORTON_HD constexpr void add(unsigned d, coord_type k) {
287 const code_type M = axis_mask(d);
288 code_type s = (code_ | ~M) + deposit(k, d);
289 code_ = (s & M) | (code_ & M_complement(d));
290 }
291
293 MORTON_HD constexpr void sub(unsigned d, coord_type k) {
294 const code_type M = axis_mask(d);
295 code_type s = (code_ & M) - deposit(k, d);
296 code_ = (s & M) | (code_ & M_complement(d));
297 }
298
300 MORTON_HD constexpr void inc(unsigned d) {
301 const code_type M = axis_mask(d);
302 code_type s = (code_ | ~M) + lsb(d);
303 code_ = (s & M) | (code_ & M_complement(d));
304 }
305
307 MORTON_HD constexpr void dec(unsigned d) {
308 const code_type M = axis_mask(d);
309 code_type s = (code_ & M) - lsb(d);
310 code_ = (s & M) | (code_ & M_complement(d));
311 }
312
314 MORTON_HD constexpr Morton neighbor(unsigned d, int dir) const {
315 Morton m = *this;
316 if (dir >= 0)
317 m.inc(d);
318 else
319 m.dec(d);
320 return m;
321 }
322
323 // ---- saturating / checked arithmetic (do not wrap) --------------------
324
326 MORTON_HD constexpr void add_sat(unsigned d, coord_type k) {
327 coord_type cur = get(d);
329 set(d, (k > room) ? coord_max : coord_type(cur + k));
330 }
331
333 MORTON_HD constexpr void sub_sat(unsigned d, coord_type k) {
334 coord_type cur = get(d);
335 set(d, (k > cur) ? coord_type(0) : coord_type(cur - k));
336 }
337
340 MORTON_HD constexpr bool try_add(unsigned d, coord_type k) {
341 coord_type cur = get(d);
342 if (k > coord_type(coord_max - cur))
343 return false;
344 set(d, coord_type(cur + k));
345 return true;
346 }
347
349 MORTON_HD constexpr bool try_sub(unsigned d, coord_type k) {
350 coord_type cur = get(d);
351 if (k > cur)
352 return false;
353 set(d, coord_type(cur - k));
354 return true;
355 }
356
357 // ---- neighbour sets ----------------------------------------------------
358
361 MORTON_HD constexpr std::array<Morton, 2 * Dim> face_neighbors() const {
362 std::array<Morton, 2 * Dim> out{};
363 for (unsigned d = 0; d < Dim; ++d) {
364 out[2 * d] = neighbor(d, -1);
365 out[2 * d + 1] = neighbor(d, +1);
366 }
367 return out;
368 }
369
372 MORTON_HD constexpr std::array<Morton, detail::pow3(Dim) - 1> all_neighbors() const {
373 std::array<Morton, detail::pow3(Dim) - 1> out{};
374 unsigned n = 0;
375 for (unsigned i = 0; i < detail::pow3(Dim); ++i) {
376 int off[Dim];
377 unsigned t = i;
378 bool zero = true;
379 for (unsigned d = 0; d < Dim; ++d) {
380 off[d] = int(t % 3) - 1;
381 if (off[d] != 0)
382 zero = false;
383 t /= 3;
384 }
385 if (zero)
386 continue; // skip self
387 Morton m = *this;
388 for (unsigned d = 0; d < Dim; ++d) {
389 if (off[d] > 0)
390 m.inc(d);
391 else if (off[d] < 0)
392 m.dec(d);
393 }
394 out[n++] = m;
395 }
396 return out;
397 }
398
399 // ---- octree / hierarchy navigation ------------------------------------
400 //
401 // A cell at `level` covers a 2^level block per axis and has its low
402 // level*Dim code bits zero. These helpers express that hierarchy directly.
403
406 MORTON_HD constexpr Morton ancestor(unsigned level) const {
407 if (level * Dim >= code_bits)
408 return Morton{};
409 code_type lowmask = (code_type(1) << (level * Dim)) - 1;
410 return from_code(code_ & ~lowmask);
411 }
412
415 MORTON_HD constexpr unsigned child_index(unsigned level) const {
416 return unsigned((code_ >> (level * Dim)) & (octants - 1));
417 }
418
421 MORTON_HD constexpr Morton child(unsigned level, unsigned octant) const {
422 unsigned shift = (level - 1) * Dim;
423 code_type cleared = code_ & ~(code_type(octants - 1) << shift);
424 return from_code(cleared | (code_type(octant) << shift));
425 }
426
427 // ---- Z-order successor / predecessor ----------------------------------
428
430 code_ = (code_ + 1) & field_mask;
431 return *this;
432 }
433 MORTON_HD constexpr Morton operator++(int) noexcept {
434 Morton t = *this;
435 ++*this;
436 return t;
437 }
439 code_ = (code_ - 1) & field_mask;
440 return *this;
441 }
442 MORTON_HD constexpr Morton operator--(int) noexcept {
443 Morton t = *this;
444 --*this;
445 return t;
446 }
447
448 // ---- comparison (Z-order) ---------------------------------------------
449
450 friend MORTON_HD constexpr bool operator==(Morton a, Morton b) noexcept {
451 return a.code_ == b.code_;
452 }
453 friend MORTON_HD constexpr bool operator!=(Morton a, Morton b) noexcept {
454 return a.code_ != b.code_;
455 }
456 friend MORTON_HD constexpr bool operator<(Morton a, Morton b) noexcept {
457 return a.code_ < b.code_;
458 }
459 friend MORTON_HD constexpr bool operator<=(Morton a, Morton b) noexcept {
460 return a.code_ <= b.code_;
461 }
462 friend MORTON_HD constexpr bool operator>(Morton a, Morton b) noexcept {
463 return a.code_ > b.code_;
464 }
465 friend MORTON_HD constexpr bool operator>=(Morton a, Morton b) noexcept {
466 return a.code_ >= b.code_;
467 }
468
469 // ---- low-level deposit/extract ----------------------------------------
470
471 MORTON_HD static constexpr code_type deposit(coord_type x, unsigned d) {
472 if constexpr (code_bits <= 64) {
473 // PDEP is a host x86 instruction; never emit it in CUDA device code
474 // (where __CUDA_ARCH__ is defined), even if the host was -mbmi2.
475#if defined(__BMI2__) && !defined(__CUDA_ARCH__)
477 return code_type(_pdep_u64(std::uint64_t(x), std::uint64_t(axis_mask(d))));
478#elif MORTON_X86_RUNTIME_DISPATCH
479 // Single-binary path: use PDEP when the running CPU has BMI2.
480 if (!detail::is_consteval() && detail::cpu_has_bmi2())
481 return code_type(detail::pdep_u64_hw(std::uint64_t(x), std::uint64_t(axis_mask(d))));
482#endif
483 }
484 return detail::spread_sw<Dim, Bits, code_type, coord_type>(x, d);
485 }
486
487 MORTON_HD static constexpr coord_type extract(code_type c, unsigned d) {
488 if constexpr (code_bits <= 64) {
489#if defined(__BMI2__) && !defined(__CUDA_ARCH__)
491 return coord_type(_pext_u64(std::uint64_t(c), std::uint64_t(axis_mask(d))));
492#elif MORTON_X86_RUNTIME_DISPATCH
493 if (!detail::is_consteval() && detail::cpu_has_bmi2())
494 return coord_type(detail::pext_u64_hw(std::uint64_t(c), std::uint64_t(axis_mask(d))));
495#endif
496 }
497 return detail::compact_sw<Dim, Bits, code_type, coord_type>(c, d);
498 }
499
501 MORTON_HD static constexpr code_type axis_mask(unsigned d) {
502#if defined(__CUDA_ARCH__)
503 // On device, compute the mask (avoids referencing the host static
504 // array from device code); it is a short compile-time-sized loop.
505 return detail::axis_mask<Dim, Bits, code_type>(d);
506#else
507 return axis_masks_[d];
508#endif
509 }
510
511 private:
512 MORTON_HD static constexpr code_type M_complement(unsigned d) {
513 return field_mask & ~axis_mask(d);
514 }
515 MORTON_HD static constexpr code_type keep_mask(unsigned d) { return M_complement(d); }
516 MORTON_HD static constexpr code_type lsb(unsigned d) { return code_type(1) << d; }
517
518 MORTON_HD static constexpr std::array<code_type, Dim> make_masks() {
519 std::array<code_type, Dim> a{};
520 for (unsigned d = 0; d < Dim; ++d)
521 a[d] = detail::axis_mask<Dim, Bits, code_type>(d);
522 return a;
523 }
524 static constexpr std::array<code_type, Dim> axis_masks_ = make_masks();
525
526 code_type code_;
527};
528
529// Convenient aliases for the common cases.
530using Morton2D32 = Morton<2, 32>; // 2D, 32 bits/axis -> 64-bit code
531using Morton2D16 = Morton<2, 16>; // 2D, 16 bits/axis -> 32-bit code
532using Morton3D21 = Morton<3, 21>; // 3D, 21 bits/axis -> 63-bit code
533using Morton3D16 = Morton<3, 16>; // 3D, 16 bits/axis -> 48-bit code
534#if defined(MORTON_HAS_INT128)
535using Morton3D32 = Morton<3, 32>; // 3D, 32 bits/axis -> 96-bit code
536using Morton2D64 = Morton<2, 64>; // 2D, 64 bits/axis -> 128-bit code
537#endif
538
539} // namespace morton
540
541#endif // MORTON_MORTON_HPP
A Morton (Z-order) code interleaving Dim unsigned coordinates of Bits bits each.
Definition morton.hpp:199
friend MORTON_HD constexpr bool operator>=(Morton a, Morton b) noexcept
Definition morton.hpp:465
MORTON_HD constexpr Morton & operator++() noexcept
Definition morton.hpp:429
static MORTON_HD constexpr coord_type extract(code_type c, unsigned d)
Definition morton.hpp:487
friend MORTON_HD constexpr bool operator<=(Morton a, Morton b) noexcept
Definition morton.hpp:459
MORTON_HD constexpr Morton operator--(int) noexcept
Definition morton.hpp:442
MORTON_HD constexpr void inc(unsigned d)
Increment axis d by one. O(1).
Definition morton.hpp:300
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:333
static constexpr code_type field_mask
Mask of all valid code bits (handles code_bits == width without UB).
Definition morton.hpp:219
MORTON_HD constexpr void dec(unsigned d)
Decrement axis d by one. O(1).
Definition morton.hpp:307
detail::uint_for_t< Bits > coord_type
Definition morton.hpp:211
static constexpr unsigned bits_per_axis
Definition morton.hpp:207
static MORTON_HD constexpr Morton encode(Cs... coords)
Encode Dim coordinates into a Morton code.
Definition morton.hpp:243
friend MORTON_HD constexpr bool operator!=(Morton a, Morton b) noexcept
Definition morton.hpp:453
MORTON_HD constexpr code_type code() const noexcept
Definition morton.hpp:261
MORTON_HD constexpr std::array< coord_type, Dim > decode() const
Decode all coordinates.
Definition morton.hpp:267
MORTON_HD constexpr void set(unsigned d, coord_type value)
Replace one axis' coordinate (other axes untouched). Wraps mod 2^Bits.
Definition morton.hpp:275
MORTON_HD constexpr Morton & operator--() noexcept
Definition morton.hpp:438
MORTON_HD constexpr Morton() noexcept
Definition morton.hpp:232
MORTON_HD constexpr void sub(unsigned d, coord_type k)
Subtract k from axis d (wraps). O(1), branchless.
Definition morton.hpp:293
friend MORTON_HD constexpr bool operator==(Morton a, Morton b) noexcept
Definition morton.hpp:450
detail::uint_for_t< Dim *Bits > code_type
Definition morton.hpp:210
static MORTON_HD constexpr code_type axis_mask(unsigned d)
Mask of bits belonging to axis d.
Definition morton.hpp:501
MORTON_HD constexpr void add(unsigned d, coord_type k)
Add k to axis d (wraps). O(1), branchless.
Definition morton.hpp:286
static MORTON_HD constexpr Morton from_code(code_type raw) noexcept
Wrap a raw, already-interleaved code value.
Definition morton.hpp:235
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:361
MORTON_HD constexpr Morton operator++(int) noexcept
Definition morton.hpp:433
MORTON_HD constexpr coord_type get(unsigned d) const
Decode coordinate of a single axis.
Definition morton.hpp:264
MORTON_HD constexpr Morton neighbor(unsigned d, int dir) const
Morton code of the neighbour one step along ± axis d (wraps).
Definition morton.hpp:314
static MORTON_HD constexpr Morton encode(const std::array< coord_type, Dim > &c)
Encode from an array of coordinates.
Definition morton.hpp:252
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:326
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:340
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:349
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:372
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:421
static MORTON_HD constexpr code_type deposit(coord_type x, unsigned d)
Definition morton.hpp:471
friend MORTON_HD constexpr bool operator>(Morton a, Morton b) noexcept
Definition morton.hpp:462
MORTON_HD constexpr unsigned child_index(unsigned level) const
Index (0 .
Definition morton.hpp:415
static constexpr coord_type coord_max
Largest representable coordinate value per axis.
Definition morton.hpp:224
static constexpr unsigned octants
Definition morton.hpp:228
static constexpr unsigned code_bits
Definition morton.hpp:208
friend MORTON_HD constexpr bool operator<(Morton a, Morton b) noexcept
Definition morton.hpp:456
MORTON_HD constexpr Morton ancestor(unsigned level) const
Ancestor cell origin at the given level (clears the low level*Dim bits).
Definition morton.hpp:406
static constexpr unsigned dimensions
Definition morton.hpp:206
The MORTON_HD host/device function marker, shared by every core header.
#define MORTON_HD
Definition hd.hpp:27
#define MORTON_MAX_BITS
Definition morton.hpp:69
typename uint_for< NBits >::type uint_for_t
Definition morton.hpp:108
MORTON_HD constexpr Coord compact_sw(Code c, unsigned d)
Definition morton.hpp:156
MORTON_HD constexpr bool is_consteval() noexcept
Definition morton.hpp:112
MORTON_HD constexpr Code axis_mask(unsigned d)
Definition morton.hpp:137
MORTON_HD constexpr Code spread_sw(Coord x, unsigned d)
Definition morton.hpp:147
MORTON_HD constexpr unsigned pow3(unsigned n)
Definition morton.hpp:127
Definition batch.hpp:26
Definition morton.hpp:88
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:104
std::conditional_t<(NBits<=builtin_max), builtin, wide_uint<(NBits+63)/64 > > type
Definition morton.hpp:105
static constexpr unsigned builtin_max
Definition morton.hpp:90
Fixed-width unsigned integer of.
Definition wide_uint.hpp:46
Minimal fixed-width unsigned integer backing Morton codes wider than 128 bits.
#define MORTON_HAS_INT128
Definition wide_uint.hpp:25