use std::hash::{Hash, Hasher};
#[inline]
fn parity(word: u64) -> bool {
word.count_ones() & 1 == 1
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Width {
W1,
W2,
W4,
W8,
Wide,
}
impl Width {
pub(crate) fn for_words(words: usize) -> Self {
match words {
0 | 1 => Width::W1,
2 => Width::W2,
3 | 4 => Width::W4,
5..=8 => Width::W8,
_ => Width::Wide,
}
}
}
pub(crate) trait LabelKey: Clone + Eq + Hash + std::fmt::Debug {
fn zeros(words: usize) -> Self;
fn from_words(src: &[u64], words: usize) -> Self;
fn as_slice(&self) -> &[u64];
fn as_mut_slice(&mut self) -> &mut [u64];
fn xor(&self, mask: &Self) -> Self;
fn dot_parity(&self, mask: &Self) -> bool;
fn get(&self, index: usize) -> bool;
fn flip(&mut self, index: usize);
fn first_set_bit(&self) -> Option<usize>;
fn is_zero(&self) -> bool;
#[cfg(test)]
fn mask_from_support(words: usize, support: impl Iterator<Item = usize>) -> Self
where
Self: Sized,
{
let mut mask = Self::zeros(words);
for index in support {
mask.flip(index);
}
mask
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct Key<const W: usize>([u64; W]);
impl<const W: usize> Hash for Key<W> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
for &word in &self.0 {
state.write_u64(word);
}
}
}
impl<const W: usize> LabelKey for Key<W> {
#[inline]
fn zeros(words: usize) -> Self {
debug_assert!(words <= W, "register of {words} words needs a wider key");
Key([0; W])
}
#[inline]
fn from_words(src: &[u64], words: usize) -> Self {
debug_assert!(words <= W, "register of {words} words needs a wider key");
debug_assert!(src.len() <= W, "source label is wider than the key");
let mut out = [0u64; W];
out[..src.len()].copy_from_slice(src);
Key(out)
}
#[inline]
fn as_slice(&self) -> &[u64] {
&self.0
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [u64] {
&mut self.0
}
#[inline]
fn xor(&self, mask: &Self) -> Self {
Key(std::array::from_fn(|i| self.0[i] ^ mask.0[i]))
}
#[inline]
fn dot_parity(&self, mask: &Self) -> bool {
let mut acc = 0u64;
for i in 0..W {
acc ^= self.0[i] & mask.0[i];
}
parity(acc)
}
#[inline]
fn get(&self, index: usize) -> bool {
(self.0[index >> 6] >> (index & 63)) & 1 == 1
}
#[inline]
fn flip(&mut self, index: usize) {
self.0[index >> 6] ^= 1u64 << (index & 63);
}
#[inline]
fn first_set_bit(&self) -> Option<usize> {
for (i, &word) in self.0.iter().enumerate() {
if word != 0 {
return Some(i * 64 + word.trailing_zeros() as usize);
}
}
None
}
#[inline]
fn is_zero(&self) -> bool {
self.0.iter().fold(0u64, |acc, &word| acc | word) == 0
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct Label(Box<[u64]>);
impl Hash for Label {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
for &word in self.0.iter() {
state.write_u64(word);
}
}
}
impl LabelKey for Label {
fn zeros(words: usize) -> Self {
Label(vec![0u64; words].into_boxed_slice())
}
fn from_words(src: &[u64], words: usize) -> Self {
debug_assert!(src.len() <= words, "source label is wider than the target");
let mut out = vec![0u64; words];
out[..src.len()].copy_from_slice(src);
Label(out.into_boxed_slice())
}
#[inline]
fn as_slice(&self) -> &[u64] {
&self.0
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [u64] {
&mut self.0
}
fn xor(&self, mask: &Self) -> Self {
debug_assert_eq!(self.0.len(), mask.0.len());
let mut out = self.0.clone();
for (o, &m) in out.iter_mut().zip(mask.0.iter()) {
*o ^= m;
}
Label(out)
}
#[inline]
fn dot_parity(&self, mask: &Self) -> bool {
debug_assert_eq!(self.0.len(), mask.0.len());
let mut acc = 0u64;
for (&a, &b) in self.0.iter().zip(mask.0.iter()) {
acc ^= a & b;
}
parity(acc)
}
#[inline]
fn get(&self, index: usize) -> bool {
(self.0[index >> 6] >> (index & 63)) & 1 == 1
}
#[inline]
fn flip(&mut self, index: usize) {
self.0[index >> 6] ^= 1u64 << (index & 63);
}
#[inline]
fn first_set_bit(&self) -> Option<usize> {
self.0
.iter()
.position(|&word| word != 0)
.map(|i| i * 64 + self.0[i].trailing_zeros() as usize)
}
#[inline]
fn is_zero(&self) -> bool {
self.0.iter().all(|&word| word == 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn padding_words_are_transparent() {
let bits = [3usize, 64, 65, 100];
let narrow = Key::<2>::mask_from_support(2, bits.into_iter());
let padded = Key::<8>::mask_from_support(2, bits.into_iter());
assert_eq!(narrow.first_set_bit(), padded.first_set_bit());
assert_eq!(narrow.is_zero(), padded.is_zero());
let other = [3usize, 65];
let narrow_mask = Key::<2>::mask_from_support(2, other.into_iter());
let padded_mask = Key::<8>::mask_from_support(2, other.into_iter());
assert_eq!(
narrow.dot_parity(&narrow_mask),
padded.dot_parity(&padded_mask)
);
assert_eq!(
narrow.xor(&narrow_mask).as_slice(),
&padded.xor(&padded_mask).as_slice()[..2]
);
}
#[test]
fn wide_labels_agree_with_fixed_width_keys() {
let bits = [0usize, 63, 64, 127];
let fixed = Key::<2>::mask_from_support(2, bits.into_iter());
let wide = Label::mask_from_support(2, bits.into_iter());
assert_eq!(fixed.as_slice(), wide.as_slice());
assert_eq!(fixed.first_set_bit(), wide.first_set_bit());
let mask_bits = [63usize, 64];
let fixed_mask = Key::<2>::mask_from_support(2, mask_bits.into_iter());
let wide_mask = Label::mask_from_support(2, mask_bits.into_iter());
assert_eq!(fixed.dot_parity(&fixed_mask), wide.dot_parity(&wide_mask));
assert_eq!(
fixed.xor(&fixed_mask).as_slice(),
wide.xor(&wide_mask).as_slice()
);
}
#[test]
fn width_classes_round_up() {
for (words, want) in [
(1usize, Width::W1),
(2, Width::W2),
(3, Width::W4),
(4, Width::W4),
(5, Width::W8),
(8, Width::W8),
(9, Width::Wide),
] {
assert_eq!(Width::for_words(words), want, "{words} words");
}
}
}