use crate::rng::{mix64, SPLITMIX_GAMMA};
const FINGERPRINT_SALT: u64 = 0xA5A5_5A5A_C3C3_3C3C;
pub(crate) const FINGERPRINT_ROWS: usize = 64;
#[derive(Clone, Debug)]
pub(crate) struct FingerprintRows<const W: usize> {
rows_x: Vec<[u64; W]>,
rows_z: Vec<[u64; W]>,
}
impl<const W: usize> FingerprintRows<W> {
pub(crate) fn new(hash_seed: u64) -> Self {
let mut state = hash_seed ^ FINGERPRINT_SALT;
let mut next = || {
state = state.wrapping_add(SPLITMIX_GAMMA);
mix64(state)
};
let mut rows_x = Vec::with_capacity(FINGERPRINT_ROWS);
let mut rows_z = Vec::with_capacity(FINGERPRINT_ROWS);
for _ in 0..FINGERPRINT_ROWS {
rows_x.push(std::array::from_fn::<u64, W, _>(|_| next()));
rows_z.push(std::array::from_fn::<u64, W, _>(|_| next()));
}
Self { rows_x, rows_z }
}
pub(crate) fn fingerprint(&self, x: &[u64; W], z: &[u64; W]) -> u64 {
let mut out = 0u64;
for r in 0..FINGERPRINT_ROWS {
let mut parity = 0u64;
for w in 0..W {
parity ^= (x[w] & self.rows_x[r][w]) ^ (z[w] & self.rows_z[r][w]);
}
out |= u64::from(parity.count_ones() & 1) << r;
}
out
}
pub(crate) fn flat(&self) -> Vec<u64> {
let mut v = Vec::with_capacity(FINGERPRINT_ROWS * 2 * W);
for r in 0..FINGERPRINT_ROWS {
v.extend_from_slice(&self.rows_x[r]);
v.extend_from_slice(&self.rows_z[r]);
}
v
}
}
#[cfg(test)]
mod tests;