use crate::geometry::Geometry;
use crate::lattice::PaddedTileLattice;
#[inline]
fn splitmix64(seed: u64) -> u64 {
let mut z = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
#[derive(Clone)]
pub struct CountMinSketch {
d: usize,
w: usize,
seed: u64,
geom: Geometry,
table: Vec<f32>,
total: u64,
}
impl CountMinSketch {
pub fn new(d: usize, w: usize, seed: u64) -> Self {
assert!(d > 0 && w > 0, "d and w must be positive");
CountMinSketch {
d,
w,
seed,
geom: Geometry::TPU_V,
table: vec![0.0; d * w],
total: 0,
}
}
#[inline]
pub fn rows(&self) -> usize {
self.d
}
#[inline]
pub fn width(&self) -> usize {
self.w
}
#[inline]
pub fn total(&self) -> u64 {
self.total
}
fn column(&self, r: usize, item: u64) -> usize {
(splitmix64(item ^ self.seed ^ (r as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15))
% self.w as u64) as usize
}
pub fn add(&mut self, item: u64, n: u64) {
for r in 0..self.d {
let c = self.column(r, item);
self.table[r * self.w + c] += n as f32;
}
self.total += n;
}
pub fn insert(&mut self, item: u64) {
self.add(item, 1);
}
pub fn estimate(&self, item: u64) -> f32 {
(0..self.d)
.map(|r| self.table[r * self.w + self.column(r, item)])
.fold(f32::INFINITY, f32::min)
}
pub fn batch_estimate(&self, items: &[u64]) -> Vec<f32> {
let b = items.len();
if b == 0 {
return Vec::new();
}
let mut est = vec![f32::INFINITY; b];
for r in 0..self.d {
let mut sel = vec![0.0f32; b * self.w];
for (i, &item) in items.iter().enumerate() {
sel[i * self.w + self.column(r, item)] = 1.0;
}
let sel_lat = PaddedTileLattice::from_dense(b, self.w, &sel, self.geom).unwrap();
let row = &self.table[r * self.w..(r + 1) * self.w];
let row_lat = PaddedTileLattice::from_dense(self.w, 1, row, self.geom).unwrap();
let counts = sel_lat.matmul(&row_lat).unwrap().to_dense();
for (e, &c) in est.iter_mut().zip(&counts) {
*e = e.min(c);
}
}
est
}
}
impl core::fmt::Debug for CountMinSketch {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"CountMinSketch {{ d: {}, w: {}, total: {} }}",
self.d, self.w, self.total
)
}
}