use core::ops::RangeBounds;
use crate::bits::Bits;
use crate::cover::{Masks, Quadrants, Rect, small};
use crate::dilated::{Dilated, Morton2};
use crate::word::Word;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct Hilbert2<W: Word>(W);
impl<W: Word> Hilbert2<W> {
pub const LEVELS: u32 = W::BITS / 2;
#[inline]
#[must_use]
pub fn encode(x: W, y: W) -> Self {
Self::from_morton(Morton2::encode(x, y))
}
#[inline]
#[must_use]
pub fn decode(self) -> (W, W) {
self.to_morton().decode()
}
#[inline]
#[must_use]
pub fn encode_order(x: W, y: W, order: u32) -> Self {
debug_assert!(order <= Self::LEVELS, "order {order} > {}", Self::LEVELS);
debug_assert!(
x.and(W::low_ones(order).not()).is_zero() && y.and(W::low_ones(order).not()).is_zero(),
"coordinates above 2^{order}"
);
if (Self::LEVELS - order) & 1 == 0 {
Self::encode(x, y)
} else {
Self::encode(y, x)
}
}
#[inline]
#[must_use]
pub fn decode_order(self, order: u32) -> (W, W) {
debug_assert!(order <= Self::LEVELS, "order {order} > {}", Self::LEVELS);
debug_assert!(
self.0.and(W::low_ones(2 * order).not()).is_zero(),
"index above 4^{order}"
);
let (x, y) = self.decode();
if (Self::LEVELS - order) & 1 == 0 {
(x, y)
} else {
(y, x)
}
}
#[inline]
#[must_use]
pub const fn from_index(index: W) -> Self {
Self(index)
}
#[inline]
#[must_use]
pub const fn index(self) -> W {
self.0
}
#[inline]
#[must_use]
pub fn from_morton(morton: Morton2<W>) -> Self {
let code = morton.code();
let lanes = Dilated::<W, 2>::mask();
let x = code.and(lanes);
let y = code.shr(1).and(lanes);
let lo = x.xor(y);
let not_lo = lo.xor(lanes);
let neither = x.or(y).xor(lanes);
let x_only = x.and(y.xor(lanes));
let mut ma = lo.or(not_lo.shr(2));
let mut mb = lo.shr(2).xor(lo);
let mut ta = neither.shr(2).xor(not_lo.and(x_only.shr(2))).xor(neither);
let mut tb = lo.and(neither.shr(2)).xor(x_only.shr(2)).xor(x_only);
let mut step = 4;
while step < W::BITS {
let (pa, pb, pc, pd) = (ma, mb, ta, tb);
let (qa, qb, qc, qd) = (pa.shr(step), pb.shr(step), pc.shr(step), pd.shr(step));
ta = pc.xor(pa.and(qc)).xor(pb.and(qd));
tb = pd.xor(pb.and(qc)).xor(pa.xor(pb).and(qd));
if step * 2 < W::BITS {
ma = pa.and(qa).xor(pb.and(qb));
mb = pa.and(qb).xor(pb.and(qa.xor(qb)));
}
step <<= 1;
}
let swap_like = ta.xor(ta.shr(2));
let flip_like = tb.xor(tb.shr(2));
let hi = flip_like.or(lo.or(swap_like).xor(lanes));
Self(lo.or(hi.shl(1)))
}
#[inline]
#[must_use]
pub fn to_morton(self) -> Morton2<W> {
let lanes = Dilated::<W, 2>::mask();
let s_lo = self.0.and(lanes);
let s_hi = self.0.shr(1).and(lanes);
let swap = s_hi.xor(s_lo).xor(lanes).suffix_xor().shr(1).and(lanes);
let flip = s_hi.and(s_lo).suffix_xor().shr(1).and(lanes);
let x = flip.xor(s_hi).xor(swap.and(s_lo));
let y = x.xor(s_lo);
Morton2::from_code(x.or(y.shl(1)))
}
}
const fn level(frame: u8, x: u8, y: u8) -> (u8, u8) {
let (swap, flip) = (frame & 1, (frame >> 1) & 1);
let lo = x ^ y;
let hi = x ^ flip ^ (swap & lo);
let swap_below = swap ^ 1 ^ hi ^ lo;
let flip_below = flip ^ (hi & lo);
((hi << 1) | lo, swap_below | (flip_below << 1))
}
#[cfg_attr(
not(any(
all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
),
all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
)
)),
allow(dead_code)
)]
const fn reduced<const N: usize>() -> [u8; N] {
let levels = N.trailing_zeros() / 2;
let mut table = [0u8; N];
let mut i = 0;
while i < N {
#[allow(clippy::cast_possible_truncation)]
let (swap, cells) = ((i >> (2 * levels)) as u8, i as u8);
let mut frame = swap;
let mut entry = 0u8;
let mut j = levels;
while j > 0 {
j -= 1;
let x = (cells >> (2 * j)) & 1;
let y = x ^ ((cells >> (2 * j + 1)) & 1);
let (digit, below) = level(frame, x, y);
entry |= (digit >> 1) << (2 * j + 1);
frame = below;
}
entry |= (frame >> 1) * (0x55 >> (8 - 2 * levels));
table[i] = entry | (((frame & 1) ^ swap) << (2 * levels));
i += 1;
}
table
}
#[cfg_attr(
not(all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
)),
allow(dead_code)
)]
const TABLE4: [u8; 32] = reduced();
#[cfg_attr(
not(all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
)),
allow(dead_code)
)]
const TABLE6: [u8; 128] = reduced();
macro_rules! hilbert2_batch {
($($w:ty => $kernel:ident),* $(,)?) => {$(
impl Hilbert2<$w> {
#[allow(clippy::missing_inline_in_public_items)]
pub fn from_morton_in_place(keys: &mut [$w]) {
let done = batch::$kernel(keys);
for key in &mut keys[done..] {
*key = Self::from_morton(Morton2::from_code(*key)).index();
}
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn to_morton_in_place(keys: &mut [$w]) {
for key in keys {
*key = Self::from_index(*key).to_morton().code();
}
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn from_morton_in_place_order(keys: &mut [$w], order: u32) {
debug_assert!(order <= Self::LEVELS, "order {order} > {}", Self::LEVELS);
debug_assert!(
keys.iter().all(|&k| k.checked_shr(2 * order).unwrap_or(0) == 0),
"codes above 4^{order}"
);
if (Self::LEVELS - order) & 1 == 1 {
let even = Dilated::<$w, 2>::mask();
for key in keys.iter_mut() {
*key = ((*key & even) << 1) | ((*key >> 1) & even);
}
}
Self::from_morton_in_place(keys);
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn to_morton_in_place_order(keys: &mut [$w], order: u32) {
debug_assert!(order <= Self::LEVELS, "order {order} > {}", Self::LEVELS);
debug_assert!(
keys.iter().all(|&k| k.checked_shr(2 * order).unwrap_or(0) == 0),
"indices above 4^{order}"
);
Self::to_morton_in_place(keys);
if (Self::LEVELS - order) & 1 == 1 {
let even = Dilated::<$w, 2>::mask();
for key in keys.iter_mut() {
*key = ((*key & even) << 1) | ((*key >> 1) & even);
}
}
}
}
)*};
}
hilbert2_batch!(u32 => from_morton_u32, u64 => from_morton_u64);
macro_rules! hilbert2_columns {
($($w:ty),* $(,)?) => {$(
impl Hilbert2<$w> {
#[allow(clippy::missing_inline_in_public_items)]
pub fn encode_columns(xs: &[$w], ys: &[$w], out: &mut [$w]) {
Morton2::<$w>::encode_columns(xs, ys, out);
Self::from_morton_in_place(out);
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn decode_columns(keys: &[$w], xs: &mut [$w], ys: &mut [$w]) {
let n = keys.len();
assert!(
xs.len() == n && ys.len() == n,
"columns of {} and {} for {n} keys",
xs.len(),
ys.len()
);
let mut codes = [0 as $w; 256];
for ((k, x), y) in keys.chunks(256).zip(xs.chunks_mut(256)).zip(ys.chunks_mut(256)) {
let codes = &mut codes[..k.len()];
for (c, &h) in codes.iter_mut().zip(k) {
*c = Self::from_index(h).to_morton().code();
}
Morton2::<$w>::decode_columns(codes, x, y);
}
}
}
)*};
}
hilbert2_columns!(u32, u64);
pub(crate) struct HilbertQuadrants;
const DIGITS: [[(u8, u8); 4]; 4] = {
let mut t = [[(0, 0); 4]; 4];
let mut f = 0;
while f < 4 {
let mut q = 0;
while q < 4 {
t[f as usize][q as usize] = level(f, q & 1, q >> 1);
q += 1;
}
f += 1;
}
t
};
static H_MASKS: Masks<4> = Masks::build(DIGITS);
#[derive(Clone, Copy)]
struct Step {
axis: u8,
up: u8,
half: u8,
corner: u8,
}
const STEPS: [[Step; 3]; 4] = {
let mut t = [[Step {
axis: 0,
up: 0,
half: 0,
corner: 0,
}; 3]; 4];
let mut f = 0;
while f < 4 {
let mut d = 0;
while d < 3 {
let (ax, ay, ag) = CHILDREN[f][d];
let (bx, by, _) = CHILDREN[f][d + 1];
let (ex, ey, _) = CHILDREN[ag as usize][3];
t[f][d] = if ax == bx {
Step {
axis: 1,
up: by,
half: ax,
corner: ex,
}
} else {
Step {
axis: 0,
up: bx,
half: ay,
corner: ey,
}
};
d += 1;
}
f += 1;
}
t
};
#[allow(clippy::cast_possible_truncation)]
const CHILDREN: [[(u8, u8, u8); 4]; 4] = {
let mut t = [[(0, 0, 0); 4]; 4];
let mut f = 0;
while f < 4 {
let mut q = 0;
while q < 4 {
let (d, below) = DIGITS[f][q];
t[f][d as usize] = ((q & 1) as u8, (q >> 1) as u8, below);
q += 1;
}
f += 1;
}
t
};
const PER: [[[u8; 4]; 2]; 2] = {
let mut t = [[[0; 4]; 2]; 2];
let mut f = 0;
while f < 4 {
let mut d = 0;
while d < 3 {
let s = STEPS[f][d];
t[s.axis as usize][s.up as usize][f] += 1;
d += 1;
}
f += 1;
}
t
};
const AT: [[[[u8; 4]; 4]; 2]; 2] = {
let mut t = [[[[0; 4]; 4]; 2]; 2];
let mut f = 0;
while f < 4 {
let mut d = 0;
while d < 3 {
let s = STEPS[f][d];
t[s.axis as usize][s.up as usize][f][(s.half << 1 | s.corner) as usize] += 1;
d += 1;
}
f += 1;
}
t
};
#[allow(clippy::cast_possible_truncation)]
const NEXT: [[[[u8; 2]; 2]; 4]; 2] = {
let mut t = [[[[0; 2]; 2]; 4]; 2];
let mut f = 0;
while f < 4 {
let mut a = 0;
while a < 2 {
let mut b = 0;
while b < 2 {
t[0][f][a][b] = level(f as u8, a as u8, b as u8).1;
t[1][f][a][b] = level(f as u8, b as u8, a as u8).1;
b += 1;
}
a += 1;
}
f += 1;
}
t
};
fn frame<W: Word>(h: W, l: u32) -> u8 {
if l >= W::BITS / 2 {
return 0;
}
let even = W::splat_byte(0x55);
let (hi, lo) = (h.shr(1).and(even), h.and(even));
let above = W::low_ones(2 * l).not().and(even);
let swap = hi.xor(lo).not().and(above).parity();
let flip = hi.and(lo).and(above).parity();
u8::from(swap) | u8::from(flip) << 1
}
#[inline]
fn mask<W: Word>(b: bool) -> W {
W::ZERO.wrapping_sub(if b { W::ONE } else { W::ZERO })
}
#[allow(clippy::too_many_arguments, clippy::many_single_char_names)]
fn side<W: Word>(
levels: u32,
s: u32,
axis: usize,
line: W,
up: u8,
(lo, hi): (W, W),
(lo_corner, hi_corner): (W, W),
) -> W {
if line.is_zero() || !line.shr(levels).is_zero() {
return W::ZERO;
}
let lvl = line.trailing_zeros() + 1;
let node = line.shr(lvl);
let (c0, c1) = (lo.shr(lvl), hi.shr(lvl));
let next = &NEXT[axis];
let mut n = W::ZERO;
let first = c0.wrapping_add(W::ONE);
if c1 > first {
let depth = crate::cover::bitlen(c0.xor(c1));
let mut weight = PER[axis][up as usize].map(|v| small::<W>(v.into()));
let (mut to_last, mut to_first) = ([W::ZERO; 4], [W::ZERO; 4]);
for j in 0..depth {
let fixed = usize::from(node.bit(j));
let (a, b) = (c1.bit(j), first.bit(j));
let (mut w, mut l, mut f) = ([W::ZERO; 4], [W::ZERO; 4], [W::ZERO; 4]);
let (ma, mb) = (mask::<W>(a), mask::<W>(b));
for g in 0..4 {
let [zero, one] = next[g][fixed].map(|v| usize::from(v) & 3);
w[g] = weight[zero].wrapping_add(weight[one]);
let (la, lb) = (if a { one } else { zero }, if b { one } else { zero });
l[g] = weight[zero].and(ma).wrapping_add(to_last[la]);
f[g] = weight[zero].and(mb).wrapping_add(to_first[lb]);
}
(weight, to_last, to_first) = (w, l, f);
}
let top = frame(lo_corner, lvl + depth + s) as usize;
n = to_last[top].wrapping_sub(to_first[top]);
}
let h = W::ONE.shl(lvl - 1);
let offsets = [
W::ZERO,
h.wrapping_sub(W::ONE),
h,
h.shl(1).wrapping_sub(W::ONE),
];
let end = |c: W, corner: W| {
let at = AT[axis][usize::from(up)][frame(corner, lvl + s) as usize];
let origin = c.shl(lvl);
let mut n = W::ZERO;
for (count, offset) in at.into_iter().zip(offsets) {
let pos = origin.wrapping_add(offset);
let steps = if lo <= pos && pos <= hi { count } else { 0 };
n = n.wrapping_add(small::<W>(steps.into()));
}
n
};
n = n.wrapping_add(end(c0, lo_corner));
if c1 != c0 {
n = n.wrapping_add(end(c1, hi_corner));
}
n
}
impl Quadrants for HilbertQuadrants {
type Context<W> = [W; 4];
#[inline]
fn digit(frame: u8, dx: u8, dy: u8) -> (u8, u8) {
level(frame, dx, dy)
}
#[inline]
fn child(k: u32, frame: u8, p: u32) -> (u8, u8, u8) {
H_MASKS.child(k, frame, p)
}
#[inline]
fn cells(k: u32, frame: u8, cols: usize, rows: usize) -> u64 {
H_MASKS.cells(k, frame, cols, rows)
}
fn context<W: Word>(r: &Rect<W>) -> [W; 4] {
let h = |x, y| Hilbert2::<W>::encode(x, y).index();
[h(r.x0, r.y0), h(r.x1, r.y0), h(r.x0, r.y1), h(r.x1, r.y1)]
}
fn runs<W: Word>(levels: u32, r: &Rect<W>, s: u32, corners: &[W; 4]) -> W {
let levels = levels - s;
let (x0, x1, y0, y1) = (r.x0.shr(s), r.x1.shr(s), r.y0.shr(s), r.y1.shr(s));
let top = W::low_ones(levels);
let [h00, h10, h01, h11] = *corners;
let mut n = if x0.is_zero() && y0.is_zero() {
W::ONE
} else {
W::ZERO
};
n = n.wrapping_add(side(levels, s, 0, x0, 1, (y0, y1), (h00, h01)));
n = n.wrapping_add(side(levels, s, 1, y0, 1, (x0, x1), (h00, h10)));
if x1 < top {
let line = x1.wrapping_add(W::ONE);
n = n.wrapping_add(side(levels, s, 0, line, 0, (y0, y1), (h10, h11)));
}
if y1 < top {
let line = y1.wrapping_add(W::ONE);
n = n.wrapping_add(side(levels, s, 1, line, 0, (x0, x1), (h01, h11)));
}
n
}
}
impl<W: Word> Hilbert2<W> {
#[inline]
pub fn cover(x: impl RangeBounds<W>, y: impl RangeBounds<W>, out: &mut [(W, W)]) -> &[(W, W)] {
crate::cover::cover_ranges::<W, HilbertQuadrants>(Self::LEVELS, x, y, out)
}
#[must_use]
#[inline]
pub fn intersects(
keys: impl RangeBounds<W>,
x: impl RangeBounds<W>,
y: impl RangeBounds<W>,
) -> bool {
crate::cover::intersects_ranges::<W, HilbertQuadrants>(Self::LEVELS, keys, x, y)
}
}
mod batch {
#[cfg(all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
))]
#[allow(unsafe_code)]
mod vbmi {
use core::arch::x86_64::{
_mm512_loadu_si512, _mm512_permutex2var_epi8, _mm512_set1_epi8, _mm512_set1_epi32,
_mm512_set1_epi64, _mm512_setzero_si512, _mm512_slli_epi32, _mm512_slli_epi64,
_mm512_srli_epi32, _mm512_srli_epi64, _mm512_srlv_epi32, _mm512_srlv_epi64,
_mm512_storeu_si512, _mm512_ternarylogic_epi32,
};
use super::super::TABLE6;
const XOR_AND: i32 = 0x78;
const AND_XOR: i32 = 0x6C;
const OR_AND: i32 = 0xF8;
const XOR_THEN_AND: i32 = 0x28;
macro_rules! kernel {
(
$name:ident, $w:ty, $lanes:literal, $set1:ident, $srli:ident, $slli:ident,
$srlv:ident
) => {
#[target_feature(enable = "avx512f,avx512bw,avx512vbmi")]
pub(in crate::hilbert) fn $name(keys: &mut [$w]) -> usize {
const REGS: usize = 64 / $lanes;
const LEVELS: u32 = <$w>::BITS / 2;
#[allow(clippy::cast_possible_truncation)]
const STEPS: u8 = LEVELS.div_ceil(3) as u8;
#[allow(clippy::cast_possible_truncation)]
const START: u8 = (((3 * STEPS as u32 - LEVELS) & 1) << 6) as u8;
let (chunks, _) = keys.as_chunks_mut::<64>();
let done = chunks.len() * 64;
for chunk in chunks {
unsafe {
let lo = _mm512_loadu_si512(TABLE6.as_ptr().cast());
let hi = _mm512_loadu_si512(TABLE6.as_ptr().add(64).cast());
let cells = $set1(63);
let high_digits = $set1(0x2A);
let state_bits = $set1(0x55);
#[allow(clippy::cast_possible_wrap)]
let odd = _mm512_set1_epi8(0xAA_u8 as i8);
let even = _mm512_set1_epi8(0x55);
let mut code = [_mm512_setzero_si512(); REGS];
let mut acc = [_mm512_setzero_si512(); REGS];
let mut state = [$set1(START.into()); REGS];
for (r, c) in code.iter_mut().enumerate() {
let m = _mm512_loadu_si512(chunk.as_ptr().add($lanes * r).cast());
*c = _mm512_ternarylogic_epi32::<XOR_AND>(m, $slli::<1>(m), odd);
}
let mut step = STEPS;
while step > 0 {
step -= 1;
let shift = $set1((6 * step).into());
for r in 0..REGS {
let index = _mm512_ternarylogic_epi32::<AND_XOR>(
$srlv(code[r], shift),
state[r],
cells,
);
let entry = _mm512_permutex2var_epi8(lo, index, hi);
acc[r] = _mm512_ternarylogic_epi32::<OR_AND>(
$slli::<6>(acc[r]),
entry,
high_digits,
);
state[r] = _mm512_ternarylogic_epi32::<XOR_THEN_AND>(
state[r], entry, state_bits,
);
}
}
for (r, a) in acc.iter().enumerate() {
let a = _mm512_ternarylogic_epi32::<OR_AND>(
*a,
$srli::<1>(code[r]),
even,
);
_mm512_storeu_si512(chunk.as_mut_ptr().add($lanes * r).cast(), a);
}
}
}
done
}
};
}
kernel!(
from_morton_u32,
u32,
16,
_mm512_set1_epi32,
_mm512_srli_epi32,
_mm512_slli_epi32,
_mm512_srlv_epi32
);
kernel!(
from_morton_u64,
u64,
8,
_mm512_set1_epi64,
_mm512_srli_epi64,
_mm512_slli_epi64,
_mm512_srlv_epi64
);
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
))]
#[allow(unsafe_code)]
mod dispatch {
use super::vbmi;
macro_rules! keys {
($($name:ident: $w:ty;)*) => {$(
pub(in crate::hilbert) fn $name(keys: &mut [$w]) -> usize {
if crate::isa::batch().vbmi {
unsafe { vbmi::$name(keys) }
} else {
0
}
}
)*};
}
keys! {
from_morton_u32: u32;
from_morton_u64: u64;
}
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
))]
pub(super) use dispatch::{from_morton_u32, from_morton_u64};
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
))]
#[allow(unsafe_code)]
mod neon {
use core::arch::aarch64::{
vandq_u32, vandq_u64, vbslq_u32, vbslq_u64, vdupq_n_s32, vdupq_n_s64, vdupq_n_u8,
vdupq_n_u32, vdupq_n_u64, veorq_u32, veorq_u64, vld1q_u8_x2, vld1q_u32, vld1q_u64,
vqtbl2q_u8, vreinterpretq_u8_u32, vreinterpretq_u8_u64, vreinterpretq_u32_u8,
vreinterpretq_u64_u8, vshlq_n_u32, vshlq_n_u64, vshlq_u32, vshlq_u64, vshrq_n_u32,
vshrq_n_u64, vsliq_n_u32, vsliq_n_u64, vst1q_u32, vst1q_u64,
};
use super::super::TABLE4;
macro_rules! kernel {
(
$name:ident, $w:ty, $per_reg:literal, $dup:ident, $dup_s:ident, $s:ty,
$ld:ident, $st:ident, $and:ident, $bsl:ident, $eor:ident, $shl:ident,
$shl_n:ident, $shr_n:ident, $sli_n:ident, $to_u8:ident, $from_u8:ident
) => {
pub(in crate::hilbert) fn $name(keys: &mut [$w]) -> usize {
#[allow(clippy::cast_possible_wrap, clippy::cast_lossless)]
const STEPS: $s = (<$w>::BITS / 4) as $s;
let (chunks, _) = keys.as_chunks_mut::<{ 8 * $per_reg }>();
let done = chunks.len() * 8 * $per_reg;
for chunk in chunks {
unsafe {
let table = vld1q_u8_x2(TABLE4.as_ptr());
let cells = $dup(15);
let state_bits = $dup(0x15);
let odd = $from_u8(vdupq_n_u8(0xAA));
let mut code = [$dup(0); 8];
let mut acc = [$dup(0); 8];
let mut state = [$dup(0); 8];
for (r, c) in code.iter_mut().enumerate() {
let m = $ld(chunk.as_ptr().add($per_reg * r));
*c = $eor(m, $and($shl_n::<1>(m), odd));
}
let mut step = STEPS;
while step > 0 {
step -= 1;
let shift = $dup_s(-4 * step);
for r in 0..8 {
let index = $eor($and($shl(code[r], shift), cells), state[r]);
let entry = $from_u8(vqtbl2q_u8(table, $to_u8(index)));
acc[r] = $sli_n::<4>(entry, acc[r]);
state[r] = $and($eor(state[r], entry), state_bits);
}
}
for (r, a) in acc.iter().enumerate() {
let a = $bsl(odd, *a, $shr_n::<1>(code[r]));
$st(chunk.as_mut_ptr().add($per_reg * r), a);
}
}
}
done
}
};
}
kernel!(
from_morton_u64,
u64,
2,
vdupq_n_u64,
vdupq_n_s64,
i64,
vld1q_u64,
vst1q_u64,
vandq_u64,
vbslq_u64,
veorq_u64,
vshlq_u64,
vshlq_n_u64,
vshrq_n_u64,
vsliq_n_u64,
vreinterpretq_u8_u64,
vreinterpretq_u64_u8
);
kernel!(
from_morton_u32,
u32,
4,
vdupq_n_u32,
vdupq_n_s32,
i32,
vld1q_u32,
vst1q_u32,
vandq_u32,
vbslq_u32,
veorq_u32,
vshlq_u32,
vshlq_n_u32,
vshrq_n_u32,
vsliq_n_u32,
vreinterpretq_u8_u32,
vreinterpretq_u32_u8
);
}
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
))]
pub(super) use neon::{from_morton_u32, from_morton_u64};
#[cfg(not(any(
all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
),
all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
)
)))]
pub(super) const fn from_morton_u32(_: &mut [u32]) -> usize {
0
}
#[cfg(not(any(
all(
target_arch = "x86_64",
target_feature = "sse2",
not(feature = "portable")
),
all(
target_arch = "aarch64",
target_feature = "neon",
not(feature = "portable")
)
)))]
pub(super) const fn from_morton_u64(_: &mut [u64]) -> usize {
0
}
}