const DEFAULT_BASE: u64 = 257;
const DEFAULT_MODULUS: u64 = 0x1FFFFFFFFFFFFFFF;
#[derive(Debug, Clone)]
pub struct PolyHashBuilder {
base: u64,
modulus: u64,
}
impl Default for PolyHashBuilder {
fn default() -> Self {
Self {
base: DEFAULT_BASE,
modulus: DEFAULT_MODULUS,
}
}
}
impl PolyHashBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_base(mut self, base: u64) -> Self {
assert!(base > 1, "base must be > 1");
self.base = base;
self
}
pub fn with_modulus(mut self, modulus: u64) -> Self {
assert!(modulus > 1, "modulus must be > 1");
self.modulus = modulus;
self
}
pub fn build(self) -> PolynomialRollingHash {
PolynomialRollingHash {
base: self.base,
modulus: self.modulus,
current_hash: 0,
current_len: 0,
current_power: 1, }
}
}
#[derive(Debug, Clone)]
pub struct PolynomialRollingHash {
base: u64,
modulus: u64,
current_hash: u64,
current_len: usize,
current_power: u64,
}
impl Default for PolynomialRollingHash {
fn default() -> Self {
Self::new()
}
}
impl PolynomialRollingHash {
pub fn new() -> Self {
PolyHashBuilder::new().build()
}
pub fn clear(&mut self) {
self.current_hash = 0;
self.current_len = 0;
self.current_power = 1;
}
pub fn current_hash(&self) -> u64 {
self.current_hash
}
pub fn hash_slice(&mut self, data: &[u8]) {
for &b in data {
self.update(b as u64);
}
}
pub fn update(&mut self, x: u64) {
let new_hash = mul_mod(self.current_hash, self.base, self.modulus);
self.current_hash = add_mod(new_hash, x, self.modulus);
self.current_len += 1;
self.current_power = mul_mod(self.current_power, self.base, self.modulus);
}
pub fn remove_front(&mut self, x: u64) {
assert!(self.current_len > 0, "No items to remove");
let inv_base = mod_inv(self.base, self.modulus)
.expect("base is not invertible mod modulus (shouldn't happen if prime modulus and base < modulus).");
let exponent = mul_mod(self.current_power, inv_base, self.modulus);
let delta = mul_mod(x, exponent, self.modulus);
self.current_hash = sub_mod(self.current_hash, delta, self.modulus);
self.current_len -= 1;
self.current_power = mul_mod(self.current_power, inv_base, self.modulus);
}
}
#[inline]
fn add_mod(a: u64, b: u64, m: u64) -> u64 {
let s = a.wrapping_add(b);
if s >= m {
s - m
} else {
s
}
}
#[inline]
fn sub_mod(a: u64, b: u64, m: u64) -> u64 {
if a >= b {
a - b
} else {
(a + m) - b
}
}
#[inline]
fn mul_mod(a: u64, b: u64, m: u64) -> u64 {
let res = (a as u128).wrapping_mul(b as u128) % (m as u128);
res as u64
}
fn mod_inv(x: u64, m: u64) -> Option<u64> {
let (g, s, _) = extended_gcd(x as i128, m as i128);
if g != 1 {
return None;
}
let mm = m as i128;
let inv = ((s % mm) + mm) % mm;
Some(inv as u64)
}
fn extended_gcd(mut a: i128, mut b: i128) -> (i128, i128, i128) {
let (mut x0, mut x1) = (1i128, 0i128);
let (mut y0, mut y1) = (0i128, 1i128);
while b != 0 {
let q = a / b;
let r = a % b;
a = b;
b = r;
let tmpx = x0 - q * x1;
x0 = x1;
x1 = tmpx;
let tmpy = y0 - q * y1;
y0 = y1;
y1 = tmpy;
}
(a, x0, y0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_usage() {
let mut hasher = PolyHashBuilder::new()
.with_base(257)
.with_modulus(1_000_000_007)
.build();
hasher.hash_slice(b"hello");
let h1 = hasher.current_hash();
hasher.clear();
hasher.hash_slice(b"hello");
let h2 = hasher.current_hash();
assert_eq!(h1, h2);
assert_ne!(h1, 0, "Likely not zero for normal strings");
}
#[test]
fn test_remove_front() {
let mut hasher = PolynomialRollingHash::new();
hasher.hash_slice(b"abcd");
let _h1 = hasher.current_hash();
hasher.remove_front(b'a' as u64);
let h2 = hasher.current_hash();
let mut alt = PolynomialRollingHash::new();
alt.hash_slice(b"bcd");
assert_eq!(h2, alt.current_hash());
hasher.remove_front(b'b' as u64);
let h3 = hasher.current_hash();
alt.clear();
alt.hash_slice(b"cd");
assert_eq!(h3, alt.current_hash());
}
}