use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct RollingHash<const B: u64, const M: u64> {
hash: u64,
base_power: u64,
window_size: usize,
elements: VecDeque<u8>,
}
impl<const B: u64, const M: u64> Default for RollingHash<B, M> {
fn default() -> Self {
Self::new()
}
}
impl<const B: u64, const M: u64> RollingHash<B, M> {
pub fn new() -> Self {
Self {
hash: 0,
base_power: 1,
window_size: 0,
elements: VecDeque::new(),
}
}
#[inline]
pub fn current_hash(&self) -> u64 {
self.hash
}
#[inline]
pub fn window_size(&self) -> usize {
self.window_size
}
pub fn push(&mut self, byte: u8) {
let hash_term = (self.hash % M).wrapping_mul(B % M) % M;
let byte_term = byte as u64 % M;
self.hash = (hash_term + byte_term) % M;
self.elements.push_back(byte);
if self.window_size > 0 {
self.base_power = (self.base_power % M).wrapping_mul(B % M) % M;
}
self.window_size += 1;
}
pub fn pop(&mut self) -> Option<u8> {
if self.window_size == 0 {
return None;
}
let front = self.elements.pop_front()?;
self.window_size -= 1;
let front_term = (front as u64 % M).wrapping_mul(self.base_power % M) % M;
self.hash = if self.hash >= front_term {
self.hash - front_term
} else {
M - (front_term - self.hash) % M
};
if self.window_size > 0 {
let b_inv =
mod_inv(B % M, M).expect("B and M must be coprime for modular inverse to exist");
self.base_power = (self.base_power % M).wrapping_mul(b_inv) % M;
} else {
self.base_power = 1;
}
Some(front)
}
pub fn clear(&mut self) {
self.hash = 0;
self.base_power = 1;
self.window_size = 0;
self.elements.clear();
}
}
fn mod_inv(a: u64, m: u64) -> Option<u64> {
let (g, x, _) = extended_gcd(a as i64, m as i64);
if g != 1 {
return None;
}
Some((((x % m as i64) + m as i64) % m as i64) as u64)
}
fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) {
if b == 0 {
(a, 1, 0)
} else {
let (g, x1, y1) = extended_gcd(b, a % b);
(g, y1, x1 - (a / b) * y1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rolling_hash_basics() {
let mut rh: RollingHash<257, 1_000_000_007> = RollingHash::new();
rh.push(b'a');
assert_eq!(rh.window_size(), 1);
let h1 = rh.current_hash();
rh.push(b'b');
assert_eq!(rh.window_size(), 2);
let h2 = rh.current_hash();
assert_ne!(h1, h2);
let popped = rh.pop();
assert_eq!(popped, Some(b'a'));
assert_eq!(rh.window_size(), 1);
let h3 = rh.current_hash();
let mut rh_test: RollingHash<257, 1_000_000_007> = RollingHash::new();
rh_test.push(b'b');
assert_eq!(h3, rh_test.current_hash());
}
#[test]
fn test_pop_on_empty() {
let mut rh: RollingHash<257, 1_000_000_007> = RollingHash::new();
assert_eq!(rh.pop(), None);
}
#[test]
fn test_clear() {
let mut rh: RollingHash<257, 1_000_000_007> = RollingHash::new();
rh.push(b'a');
rh.push(b'b');
rh.clear();
assert_eq!(rh.window_size(), 0);
assert_eq!(rh.current_hash(), 0);
assert_eq!(rh.pop(), None);
}
#[test]
fn test_window_operations() {
let mut rh: RollingHash<257, 1_000_000_007> = RollingHash::new();
let text = b"abcdef";
for &byte in text {
rh.push(byte);
}
assert_eq!(rh.window_size(), 6);
for &expected in text {
assert_eq!(rh.pop(), Some(expected));
}
assert_eq!(rh.window_size(), 0);
}
#[test]
fn test_hash_consistency() {
let mut rh1: RollingHash<257, 1_000_000_007> = RollingHash::new();
let mut rh2: RollingHash<257, 1_000_000_007> = RollingHash::new();
for &byte in b"hello" {
rh1.push(byte);
rh2.push(byte);
}
assert_eq!(rh1.current_hash(), rh2.current_hash());
}
}