pub trait BitCount {
const BITS: u32;
const BITS_ILOG2: u32 = Self::BITS.trailing_zeros();
}
macro_rules! impl_bit_count {
($($t:ty),*) => {
$(
impl BitCount for $t {
const BITS: u32 = Self::BITS;
}
)*
};
}
impl_bit_count! {
u8, u16, u32, u64, u128
}
#[inline]
pub(crate) const fn interleave_shift(dim: u32, i: u32) -> u32 {
(dim - 1) << i
}
#[inline]
pub(crate) fn interleave_mask<T: num_traits::PrimInt + BitCount>(dim: u32, bits: u32) -> T {
let mut acc = <T as num_traits::Zero>::zero();
let mask = bit_mask::<T>(bits);
let ceil_div_dim = <T as BitCount>::BITS.div_ceil(dim);
let ceil_div_bits = ceil_div_dim.div_ceil(bits);
for i in 0..ceil_div_bits {
acc = acc | mask.unsigned_shl(i * dim * bits);
}
acc
}
#[inline]
pub(crate) fn bit_mask<T: num_traits::PrimInt + BitCount>(bits: u32) -> T {
<T as num_traits::Bounded>::max_value().unsigned_shr(<T as BitCount>::BITS - bits)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allowed_masks() {
assert_eq!(bit_mask::<u8>(4), 0xF);
assert_eq!(bit_mask::<u16>(4), 0xF);
assert_eq!(bit_mask::<u32>(4), 0xF);
assert_eq!(bit_mask::<u64>(4), 0xF);
assert_eq!(bit_mask::<u128>(4), 0xF);
assert_eq!(bit_mask::<u8>(8), 0xFF);
assert_eq!(bit_mask::<u16>(16), 0xFFFF);
assert_eq!(bit_mask::<u32>(32), 0xFFFF_FFFF);
assert_eq!(bit_mask::<u64>(64), 0xFFFF_FFFF_FFFF_FFFF);
assert_eq!(
bit_mask::<u128>(128),
0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
);
}
#[test]
#[should_panic]
fn zero_mask() {
bit_mask::<u8>(0);
}
#[test]
#[should_panic]
fn too_large_mask() {
bit_mask::<u8>(u8::BITS + 1);
}
#[test]
fn ilog2() {
assert_eq!(u8::BITS_ILOG2, 3);
assert_eq!(u16::BITS_ILOG2, 4);
assert_eq!(u32::BITS_ILOG2, 5);
assert_eq!(u64::BITS_ILOG2, 6);
assert_eq!(u128::BITS_ILOG2, 7);
}
#[test]
fn interleave_mask_dim2() {
assert_eq!(
interleave_mask::<u128>(2, 32),
0x0000_0000_FFFF_FFFF_00000000_FFFF_FFFF
);
assert_eq!(
interleave_mask::<u128>(2, 16),
0x0000_FFFF_0000_FFFF_0000_FFFF_0000_FFFF
);
assert_eq!(
interleave_mask::<u128>(2, 8),
0x00FF_00FF_00FF_00FF_00FF_00FF_00FF_00FF
);
assert_eq!(
interleave_mask::<u128>(2, 4),
0x0F0F_0F0F_0F0F_0F0F_0F0F_0F0F_0F0F_0F0F
);
assert_eq!(
interleave_mask::<u128>(2, 2),
0x3333_3333_3333_3333_3333_3333_3333_3333
);
assert_eq!(
interleave_mask::<u128>(2, 1),
0x5555_5555_5555_5555_5555_5555_5555_5555
);
}
#[test]
fn interleave_mask_dim3() {
assert_eq!(
interleave_mask::<u128>(3, 16),
0x0000_FFFF_0000_0000_FFFF_0000_0000_FFFF
);
assert_eq!(
interleave_mask::<u128>(3, 8),
0xFF00_00FF_0000_FF00_00FF_0000_FF00_00FF
);
assert_eq!(
interleave_mask::<u128>(3, 4),
0x0F00_F00F_00F0_0F00_F00F_00F0_0F00_F00F
);
assert_eq!(
interleave_mask::<u128>(3, 2),
0xC30C_30C3_0C30_C30C_30C3_0C30_C30C_30C3
);
assert_eq!(
interleave_mask::<u128>(3, 1),
0x4924_9249_2492_4924_9249_2492_4924_9249
);
}
#[test]
fn interleave_mask_dim4() {
assert_eq!(
interleave_mask::<u128>(4, 16),
0x0000_0000_0000_FFFF_0000_0000_0000_FFFF
);
assert_eq!(
interleave_mask::<u128>(4, 8),
0x0000_00FF_0000_00FF_0000_00FF_0000_00FF
);
assert_eq!(
interleave_mask::<u128>(4, 4),
0x000F_000F_000F_000F_000F_000F_000F_000F
);
assert_eq!(
interleave_mask::<u128>(4, 2),
0x0303_0303_0303_0303_0303_0303_0303_0303
);
assert_eq!(
interleave_mask::<u128>(4, 1),
0x1111_1111_1111_1111_1111_1111_1111_1111
);
}
#[test]
fn truncated_interleave_mask() {
assert_eq!(interleave_mask::<u32>(2, 32), 0xFFFF_FFFF);
assert_eq!(interleave_mask::<u32>(2, 16), 0x0000_FFFF);
assert_eq!(interleave_mask::<u32>(2, 8), 0x00FF_00FF);
assert_eq!(interleave_mask::<u32>(2, 4), 0x0F0F_0F0F);
assert_eq!(interleave_mask::<u32>(2, 2), 0x3333_3333);
assert_eq!(interleave_mask::<u32>(2, 1), 0x5555_5555);
}
#[test]
fn odd_large_dimension_mask() {
assert_eq!(
interleave_mask::<u32>(13, 1),
0b1_0000_0000_0000_1_0000_0000_0000_1
)
}
}