use crate::isa::{Isa, Native};
pub trait Word:
Copy
+ Ord
+ core::hash::Hash
+ core::fmt::Debug
+ core::fmt::Binary
+ core::fmt::LowerHex
+ core::fmt::UpperHex
+ Send
+ Sync
+ 'static
{
const BITS: u32;
const ZERO: Self;
const ONE: Self;
const ONES: Self;
#[must_use]
fn and(self, other: Self) -> Self;
#[must_use]
fn or(self, other: Self) -> Self;
#[must_use]
fn xor(self, other: Self) -> Self;
#[must_use]
fn not(self) -> Self;
#[must_use]
fn shl(self, n: u32) -> Self;
#[must_use]
fn shr(self, n: u32) -> Self;
#[must_use]
fn wrapping_add(self, other: Self) -> Self;
#[must_use]
fn wrapping_sub(self, other: Self) -> Self;
#[must_use]
fn wrapping_mul(self, other: Self) -> Self;
#[must_use]
fn splat_byte(b: u8) -> Self;
#[must_use]
fn low_byte(self) -> u8;
#[must_use]
#[doc(alias("popcount", "popcnt"))]
fn count_ones(self) -> u32;
#[must_use]
#[doc(alias("tzcnt", "ctz"))]
fn trailing_zeros(self) -> u32;
#[must_use]
#[doc(alias("lzcnt", "clz"))]
fn leading_zeros(self) -> u32;
#[must_use]
#[doc(alias("blsr"))]
#[inline]
fn clear_lowest_set(self) -> Self {
self.and(self.wrapping_sub(Self::ONE))
}
#[must_use]
#[inline]
fn pext(self, mask: Self) -> Self {
self.pext_in(mask, Native)
}
#[must_use]
#[inline]
fn pdep(self, mask: Self) -> Self {
self.pdep_in(mask, Native)
}
#[must_use]
#[inline]
fn unzip(self) -> (Self, Self) {
self.unzip_in(Native)
}
#[must_use]
#[inline]
fn zip(self, other: Self) -> Self {
self.zip_in(other, Native)
}
#[must_use]
#[inline]
fn select_lowest(self, k: u32) -> u32 {
self.select_lowest_in(k, Native)
}
#[must_use]
#[inline]
fn xor_scan(self) -> Self {
self.xor_scan_in(Native)
}
#[must_use]
#[inline]
fn xor_scan_down(self) -> Self {
self.xor_scan_down_in(Native)
}
#[must_use]
#[inline]
fn pext_in<I: Isa>(self, mask: Self, isa: I) -> Self {
let _ = isa;
compress_broadword(self, mask)
}
#[must_use]
#[inline]
fn pdep_in<I: Isa>(self, mask: Self, isa: I) -> Self {
let _ = isa;
expand_broadword(self, mask)
}
#[must_use]
#[inline]
fn unzip_in<I: Isa>(self, isa: I) -> (Self, Self) {
let even = Self::splat_byte(0x55);
(self.pext_in(even, isa), self.shr(1).pext_in(even, isa))
}
#[must_use]
#[inline]
fn zip_in<I: Isa>(self, other: Self, isa: I) -> Self {
let even = Self::splat_byte(0x55);
self.pdep_in(even, isa).or(other.pdep_in(even, isa).shl(1))
}
#[must_use]
#[inline]
fn select_lowest_in<I: Isa>(self, k: u32, isa: I) -> u32 {
let _ = isa;
let mut x = self;
for _ in 0..k {
x = x.clear_lowest_set();
}
x.trailing_zeros()
}
#[must_use]
#[inline]
fn xor_scan_in<I: Isa>(self, isa: I) -> Self {
let _ = isa;
xor_smear(self)
}
#[must_use]
#[inline]
fn xor_scan_down_in<I: Isa>(self, isa: I) -> Self {
let _ = isa;
xor_smear_down(self)
}
#[must_use]
#[inline]
fn low_ones(n: u32) -> Self {
if n >= Self::BITS {
Self::ONES
} else {
Self::ONE.shl(n).wrapping_sub(Self::ONE)
}
}
#[inline]
#[must_use]
fn is_zero(self) -> bool {
self == Self::ZERO
}
#[inline]
#[must_use]
fn bit(self, i: u32) -> bool {
!self.shr(i).and(Self::ONE).is_zero()
}
#[inline]
#[must_use]
fn parity(self) -> bool {
self.count_ones() & 1 == 1
}
#[inline]
#[must_use]
fn trailing_ones(self) -> u32 {
self.not().trailing_zeros()
}
#[inline]
#[must_use]
fn leading_ones(self) -> u32 {
self.not().leading_zeros()
}
}
#[inline]
#[must_use]
pub(crate) const fn unzip_broadword(x: u32) -> (u32, u32) {
let z = x as u64;
let mut w = (z | (z >> 1) << 32) & 0x5555_5555_5555_5555;
w = (w | w >> 1) & 0x3333_3333_3333_3333;
w = (w | w >> 2) & 0x0F0F_0F0F_0F0F_0F0F;
w = (w | w >> 4) & 0x00FF_00FF_00FF_00FF;
w |= w >> 8;
#[allow(clippy::cast_possible_truncation)]
(w as u16 as u32, (w >> 32) as u16 as u32)
}
#[inline]
#[must_use]
pub(crate) const fn gather_even_u64(x: u64) -> u64 {
let mut x = x & 0x5555_5555_5555_5555;
x = (x | x >> 1) & 0x3333_3333_3333_3333;
x = (x | x >> 2) & 0x0F0F_0F0F_0F0F_0F0F;
x = (x | x >> 4) & 0x00FF_00FF_00FF_00FF;
x = (x | x >> 8) & 0x0000_FFFF_0000_FFFF;
(x | x >> 16) & 0xFFFF_FFFF
}
#[inline]
#[must_use]
pub fn compress_broadword<W: Word>(x: W, mut mask: W) -> W {
let mut x = x.and(mask);
let mut mk = mask.not().shl(1); let mut i = 0;
while (1u32 << i) < W::BITS {
let mp = mk.xor_scan(); let mv = mp.and(mask); mask = mask.xor(mv).or(mv.shr(1u32 << i));
let t = x.and(mv);
x = x.xor(t).or(t.shr(1u32 << i));
mk = mk.and(mp.not());
i += 1;
}
x
}
#[inline]
#[must_use]
pub fn expand_broadword<W: Word>(x: W, mask: W) -> W {
const { assert!(W::BITS <= 1 << 16, "expand_broadword: BITS above 2^16") };
let mut moves = [W::ZERO; 16];
let mut m = mask;
let mut mk = mask.not().shl(1);
let mut rounds = 0;
while (1u32 << rounds) < W::BITS {
let mp = mk.xor_scan();
let mv = mp.and(m);
moves[rounds] = mv;
m = m.xor(mv).or(mv.shr(1u32 << rounds));
mk = mk.and(mp.not());
rounds += 1;
}
let mut x = x;
for i in (0..rounds).rev() {
let mv = moves[i];
x = x.and(mv.not()).or(x.shl(1u32 << i).and(mv));
}
x.and(mask)
}
#[inline]
pub(crate) fn xor_smear<W: Word>(mut x: W) -> W {
let mut s = 1;
while s < W::BITS {
x = x.xor(x.shl(s));
s <<= 1;
}
x
}
#[inline]
pub(crate) fn xor_smear_down<W: Word>(mut x: W) -> W {
let mut s = 1;
while s < W::BITS {
x = x.xor(x.shr(s));
s <<= 1;
}
x
}
const ONES_STEP_4: u64 = 0x1111_1111_1111_1111;
const ONES_STEP_8: u64 = 0x0101_0101_0101_0101;
const MSBS_STEP_8: u64 = 0x8080_8080_8080_8080;
const INCR_STEP_8: u64 = 0x8040_2010_0804_0201;
#[must_use]
#[inline]
pub fn select_broadword64(x: u64, k: u32) -> u32 {
let mut s = x - ((x >> 1) & (0x5 * ONES_STEP_4));
s = (s & (0x3 * ONES_STEP_4)) + ((s >> 2) & (0x3 * ONES_STEP_4));
s = (s + (s >> 4)) & (0x0F * ONES_STEP_8);
let byte_sums = s.wrapping_mul(ONES_STEP_8);
if u64::from(k) >= byte_sums >> 56 {
return 64;
}
let k_step_8 = u64::from(k) * ONES_STEP_8;
let geq_k = ((k_step_8 | MSBS_STEP_8) - byte_sums) & MSBS_STEP_8;
let place = geq_k.count_ones() * 8;
let byte_rank = k - (((byte_sums << 8) >> place) & 0xFF) as u32;
let byte = (x >> place) & 0xFF;
let mut spread = byte.wrapping_mul(ONES_STEP_8) & INCR_STEP_8;
spread |= spread >> 4;
spread |= spread >> 2;
spread |= spread >> 1;
let bit_sums = (spread & ONES_STEP_8).wrapping_mul(ONES_STEP_8);
let byte_rank_step_8 = u64::from(byte_rank) * ONES_STEP_8;
let geq_r = ((byte_rank_step_8 | MSBS_STEP_8) - bit_sums) & MSBS_STEP_8;
place + geq_r.count_ones()
}
macro_rules! impl_word_core {
($t:ty) => {
const BITS: u32 = <$t>::BITS;
const ZERO: Self = 0;
const ONE: Self = 1;
const ONES: Self = <$t>::MAX;
#[inline]
fn and(self, other: Self) -> Self {
self & other
}
#[inline]
fn or(self, other: Self) -> Self {
self | other
}
#[inline]
fn xor(self, other: Self) -> Self {
self ^ other
}
#[inline]
fn not(self) -> Self {
!self
}
#[inline]
fn shl(self, n: u32) -> Self {
debug_assert!(n < Self::BITS, "shift {n} >= width {}", Self::BITS);
self << n
}
#[inline]
fn shr(self, n: u32) -> Self {
debug_assert!(n < Self::BITS, "shift {n} >= width {}", Self::BITS);
self >> n
}
#[inline]
fn wrapping_add(self, other: Self) -> Self {
<$t>::wrapping_add(self, other)
}
#[inline]
fn wrapping_sub(self, other: Self) -> Self {
<$t>::wrapping_sub(self, other)
}
#[inline]
fn wrapping_mul(self, other: Self) -> Self {
<$t>::wrapping_mul(self, other)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn low_byte(self) -> u8 {
self as u8
}
#[inline]
fn splat_byte(b: u8) -> Self {
Self::from(b).wrapping_mul(<$t>::MAX / 0xFF)
}
#[inline]
fn count_ones(self) -> u32 {
<$t>::count_ones(self)
}
#[inline]
fn trailing_zeros(self) -> u32 {
<$t>::trailing_zeros(self)
}
#[inline]
fn leading_zeros(self) -> u32 {
<$t>::leading_zeros(self)
}
#[inline]
fn clear_lowest_set(self) -> Self {
self & self.wrapping_sub(1)
}
#[inline]
fn low_ones(n: u32) -> Self {
debug_assert!(n <= Self::BITS, "mask width {n} > {}", Self::BITS);
if n >= Self::BITS {
Self::ONES
} else {
(1 << n) - 1
}
}
};
}
impl Word for u64 {
impl_word_core!(u64);
#[inline]
fn pext_in<I: Isa>(self, mask: Self, isa: I) -> Self {
isa.pext_u64(self, mask)
}
#[inline]
fn unzip_in<I: Isa>(self, isa: I) -> (Self, Self) {
isa.unzip_u64(self)
}
#[inline]
fn pdep_in<I: Isa>(self, mask: Self, isa: I) -> Self {
isa.pdep_u64(self, mask)
}
#[inline]
fn select_lowest_in<I: Isa>(self, k: u32, isa: I) -> u32 {
isa.select_u64(self, k)
}
#[inline]
fn xor_scan_in<I: Isa>(self, isa: I) -> Self {
isa.xor_scan_u64(self)
}
#[inline]
fn xor_scan_down_in<I: Isa>(self, isa: I) -> Self {
isa.xor_scan_down_u64(self)
}
}
impl Word for u32 {
impl_word_core!(u32);
#[inline]
fn pext_in<I: Isa>(self, mask: Self, isa: I) -> Self {
isa.pext_u32(self, mask)
}
#[inline]
fn unzip_in<I: Isa>(self, isa: I) -> (Self, Self) {
isa.unzip_u32(self)
}
#[inline]
fn pdep_in<I: Isa>(self, mask: Self, isa: I) -> Self {
isa.pdep_u32(self, mask)
}
#[inline]
fn select_lowest_in<I: Isa>(self, k: u32, isa: I) -> u32 {
u64::from(self).select_lowest_in(k, isa).min(Self::BITS)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_in<I: Isa>(self, isa: I) -> Self {
u64::from(self).xor_scan_in(isa) as Self
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_down_in<I: Isa>(self, isa: I) -> Self {
u64::from(self).xor_scan_down_in(isa) as Self
}
}
impl Word for u128 {
impl_word_core!(u128);
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pext_in<I: Isa>(self, mask: Self, isa: I) -> Self {
let (lo, hi) = (self as u64, (self >> 64) as u64);
let (mlo, mhi) = (mask as u64, (mask >> 64) as u64);
Self::from(lo.pext_in(mlo, isa)) | (Self::from(hi.pext_in(mhi, isa)) << mlo.count_ones())
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pdep_in<I: Isa>(self, mask: Self, isa: I) -> Self {
let (mlo, mhi) = (mask as u64, (mask >> 64) as u64);
let lo = (self as u64).pdep_in(mlo, isa);
let hi = ((self >> mlo.count_ones()) as u64).pdep_in(mhi, isa);
Self::from(lo) | (Self::from(hi) << 64)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn select_lowest_in<I: Isa>(self, k: u32, isa: I) -> u32 {
let (lo, hi) = (self as u64, (self >> 64) as u64);
let n = lo.count_ones();
if k < n {
lo.select_lowest_in(k, isa)
} else {
64 + hi.select_lowest_in(k - n, isa)
}
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_in<I: Isa>(self, isa: I) -> Self {
let (lo, hi) = (self as u64, (self >> 64) as u64);
let carry = 0u64.wrapping_sub(u64::from(lo.count_ones() & 1));
Self::from(lo.xor_scan_in(isa)) | (Self::from(hi.xor_scan_in(isa) ^ carry) << 64)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_down_in<I: Isa>(self, isa: I) -> Self {
let (lo, hi) = (self as u64, (self >> 64) as u64);
let carry = 0u64.wrapping_sub(u64::from(hi.count_ones() & 1));
Self::from(lo.xor_scan_down_in(isa) ^ carry) | (Self::from(hi.xor_scan_down_in(isa)) << 64)
}
}
macro_rules! impl_word_narrow {
($($t:ty),*) => {$(
impl Word for $t {
impl_word_core!($t);
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pext_in<I: Isa>(self, mask: Self, isa: I) -> Self {
u32::from(self).pext_in(u32::from(mask), isa) as $t
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn unzip_in<I: Isa>(self, isa: I) -> (Self, Self) {
let (even, odd) = u32::from(self).unzip_in(isa);
(even as $t, odd as $t)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pdep_in<I: Isa>(self, mask: Self, isa: I) -> Self {
u32::from(self).pdep_in(u32::from(mask), isa) as $t
}
#[inline]
fn select_lowest_in<I: Isa>(self, k: u32, isa: I) -> u32 {
u64::from(self).select_lowest_in(k, isa).min(Self::BITS)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_in<I: Isa>(self, isa: I) -> Self {
u64::from(self).xor_scan_in(isa) as $t
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan_down_in<I: Isa>(self, isa: I) -> Self {
u64::from(self).xor_scan_down_in(isa) as $t
}
}
)*};
}
impl_word_narrow!(u8, u16);