pub trait Word: Copy + Eq + core::fmt::Debug {
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]
fn count_ones(self) -> u32;
#[must_use]
fn trailing_zeros(self) -> u32;
#[must_use]
fn leading_zeros(self) -> u32;
#[must_use]
fn clear_lowest_set(self) -> Self {
self.and(self.wrapping_sub(Self::ONE))
}
#[must_use]
fn pext(self, mask: Self) -> Self {
compress_broadword(self, mask)
}
#[must_use]
fn pdep(self, mask: Self) -> Self {
expand_broadword(self, mask)
}
#[must_use]
fn select_lowest(self, k: u32) -> u32 {
let mut x = self;
for _ in 0..k {
x = x.clear_lowest_set();
}
x.trailing_zeros()
}
#[must_use]
fn xor_scan(self) -> Self {
xor_smear(self)
}
#[must_use]
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 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 {
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]
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
}
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]
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);
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()
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
#[allow(unsafe_code)]
mod bmi2 {
use core::arch::x86_64::{_pdep_u32, _pdep_u64, _pext_u32, _pext_u64};
#[inline]
pub(super) fn pext32(x: u32, m: u32) -> u32 {
unsafe { _pext_u32(x, m) }
}
#[inline]
pub(super) fn pdep32(x: u32, m: u32) -> u32 {
unsafe { _pdep_u32(x, m) }
}
#[inline]
pub(super) fn pext64(x: u64, m: u64) -> u64 {
unsafe { _pext_u64(x, m) }
}
#[inline]
pub(super) fn pdep64(x: u64, m: u64) -> u64 {
unsafe { _pdep_u64(x, m) }
}
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "pclmulqdq",
not(feature = "portable")
))]
#[allow(unsafe_code)]
mod clmul {
use core::arch::x86_64::{__m128i, _mm_clmulepi64_si128, _mm_cvtsi128_si64, _mm_set_epi64x};
#[inline]
pub(super) fn prefix_xor64(x: u64) -> u64 {
unsafe {
let a: __m128i = _mm_set_epi64x(0, x.cast_signed());
let ones: __m128i = _mm_set_epi64x(0, -1);
_mm_cvtsi128_si64(_mm_clmulepi64_si128(a, ones, 0)).cast_unsigned()
}
}
}
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(self, mask: Self) -> Self {
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
{
bmi2::pext64(self, mask)
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
)))]
{
compress_broadword(self, mask)
}
}
#[inline]
fn pdep(self, mask: Self) -> Self {
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
{
bmi2::pdep64(self, mask)
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
)))]
{
expand_broadword(self, mask)
}
}
#[inline]
fn select_lowest(self, k: u32) -> u32 {
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
{
bmi2::pdep64(1 << k, self).trailing_zeros()
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
)))]
{
select_broadword64(self, k)
}
}
#[inline]
fn xor_scan(self) -> Self {
#[cfg(all(
target_arch = "x86_64",
target_feature = "pclmulqdq",
not(feature = "portable")
))]
{
clmul::prefix_xor64(self)
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "pclmulqdq",
not(feature = "portable")
)))]
{
xor_smear(self)
}
}
}
impl Word for u32 {
impl_word_core!(u32);
#[inline]
fn pext(self, mask: Self) -> Self {
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
{
bmi2::pext32(self, mask)
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
)))]
{
compress_broadword(self, mask)
}
}
#[inline]
fn pdep(self, mask: Self) -> Self {
#[cfg(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
))]
{
bmi2::pdep32(self, mask)
}
#[cfg(not(all(
target_arch = "x86_64",
target_feature = "bmi2",
not(feature = "portable")
)))]
{
expand_broadword(self, mask)
}
}
#[inline]
fn select_lowest(self, k: u32) -> u32 {
u64::from(self).select_lowest(k)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan(self) -> Self {
u64::from(self).xor_scan() as Self
}
}
impl Word for u128 {
impl_word_core!(u128);
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pext(self, mask: Self) -> 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(mlo)) | (Self::from(hi.pext(mhi)) << mlo.count_ones())
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pdep(self, mask: Self) -> Self {
let (mlo, mhi) = (mask as u64, (mask >> 64) as u64);
let lo = (self as u64).pdep(mlo);
let hi = ((self >> mlo.count_ones()) as u64).pdep(mhi);
Self::from(lo) | (Self::from(hi) << 64)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn select_lowest(self, k: u32) -> u32 {
let (lo, hi) = (self as u64, (self >> 64) as u64);
let n = lo.count_ones();
if k < n {
lo.select_lowest(k)
} else {
64 + hi.select_lowest(k - n)
}
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan(self) -> 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()) | (Self::from(hi.xor_scan() ^ carry) << 64)
}
}
macro_rules! impl_word_narrow {
($($t:ty),*) => {$(
impl Word for $t {
impl_word_core!($t);
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pext(self, mask: Self) -> Self {
u32::from(self).pext(u32::from(mask)) as $t
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn pdep(self, mask: Self) -> Self {
u32::from(self).pdep(u32::from(mask)) as $t
}
#[inline]
fn select_lowest(self, k: u32) -> u32 {
u64::from(self).select_lowest(k)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn xor_scan(self) -> Self {
u64::from(self).xor_scan() as $t
}
}
)*};
}
impl_word_narrow!(u8, u16);