use crate::common::SketchError;
use rand::Rng;
#[derive(Clone, Debug)]
pub struct ReservoirSampling<T: Clone> {
k: usize,
reservoir: Vec<T>,
count: u64,
rng: rand::rngs::SmallRng,
}
impl<T: Clone> ReservoirSampling<T> {
pub fn new(k: usize) -> Result<Self, SketchError> {
if k == 0 {
return Err(SketchError::InvalidParameter {
param: "k".to_string(),
value: k.to_string(),
constraint: "must be greater than 0".to_string(),
});
}
use rand::SeedableRng;
Ok(ReservoirSampling {
k,
reservoir: Vec::with_capacity(k),
count: 0,
rng: rand::rngs::SmallRng::from_os_rng(),
})
}
pub fn with_seed(k: usize, seed: u64) -> Result<Self, SketchError> {
if k == 0 {
return Err(SketchError::InvalidParameter {
param: "k".to_string(),
value: k.to_string(),
constraint: "must be greater than 0".to_string(),
});
}
use rand::SeedableRng;
Ok(ReservoirSampling {
k,
reservoir: Vec::with_capacity(k),
count: 0,
rng: rand::rngs::SmallRng::seed_from_u64(seed),
})
}
pub fn update(&mut self, item: T) {
self.count += 1;
if self.reservoir.len() < self.k {
self.reservoir.push(item);
} else {
let r = self.rng.random_range(0..self.count);
if r < self.k as u64 {
self.reservoir[r as usize] = item;
}
}
}
pub fn sample(&self) -> &[T] {
&self.reservoir
}
pub fn into_sample(self) -> Vec<T> {
self.reservoir
}
pub fn is_empty(&self) -> bool {
self.reservoir.is_empty()
}
pub fn len(&self) -> usize {
self.reservoir.len()
}
pub fn capacity(&self) -> usize {
self.k
}
pub fn count(&self) -> u64 {
self.count
}
pub fn inclusion_probability(&self) -> f64 {
if self.count == 0 {
0.0
} else {
(self.k.min(self.count as usize) as f64) / (self.count as f64)
}
}
pub fn clear(&mut self) {
self.reservoir.clear();
self.count = 0;
}
}
impl<T: Clone> ReservoirSampling<T> {
pub fn merge(&mut self, other: &Self) -> Result<(), SketchError> {
if self.k != other.k {
return Err(SketchError::IncompatibleSketches {
reason: format!(
"Cannot merge reservoirs with different k: {} vs {}",
self.k, other.k
),
});
}
let total_count = self.count + other.count;
if total_count == 0 {
return Ok(());
}
let mut combined: Vec<T> = self.reservoir.clone();
combined.extend(other.reservoir.iter().cloned());
if combined.len() <= self.k {
self.reservoir = combined;
self.count = total_count;
return Ok(());
}
let mut new_reservoir = Vec::with_capacity(self.k);
use rand::seq::SliceRandom;
combined.shuffle(&mut self.rng);
new_reservoir.extend(combined.into_iter().take(self.k));
self.reservoir = new_reservoir;
self.count = total_count;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_reservoir() {
let reservoir: ReservoirSampling<i32> = ReservoirSampling::new(10).unwrap();
assert!(reservoir.is_empty());
assert_eq!(reservoir.capacity(), 10);
assert_eq!(reservoir.count(), 0);
}
#[test]
fn test_new_invalid_k() {
let result: Result<ReservoirSampling<i32>, _> = ReservoirSampling::new(0);
assert!(result.is_err());
}
#[test]
fn test_update_fills_reservoir() {
let mut reservoir: ReservoirSampling<i32> = ReservoirSampling::new(5).unwrap();
for i in 0..5 {
reservoir.update(i);
}
assert_eq!(reservoir.len(), 5);
assert_eq!(reservoir.count(), 5);
}
#[test]
fn test_update_beyond_capacity() {
let mut reservoir: ReservoirSampling<i32> = ReservoirSampling::with_seed(5, 42).unwrap();
for i in 0..100 {
reservoir.update(i);
}
assert_eq!(reservoir.len(), 5);
assert_eq!(reservoir.count(), 100);
}
#[test]
fn test_seeded_reproducibility() {
let mut r1: ReservoirSampling<i32> = ReservoirSampling::with_seed(10, 12345).unwrap();
let mut r2: ReservoirSampling<i32> = ReservoirSampling::with_seed(10, 12345).unwrap();
for i in 0..1000 {
r1.update(i);
r2.update(i);
}
assert_eq!(r1.sample(), r2.sample());
}
#[test]
fn test_inclusion_probability() {
let mut reservoir: ReservoirSampling<i32> = ReservoirSampling::new(10).unwrap();
assert_eq!(reservoir.inclusion_probability(), 0.0);
for i in 0..5 {
reservoir.update(i);
}
assert!((reservoir.inclusion_probability() - 1.0).abs() < 0.001);
for i in 5..100 {
reservoir.update(i);
}
assert!((reservoir.inclusion_probability() - 0.1).abs() < 0.001);
}
#[test]
fn test_clear() {
let mut reservoir: ReservoirSampling<i32> = ReservoirSampling::new(10).unwrap();
for i in 0..50 {
reservoir.update(i);
}
assert!(!reservoir.is_empty());
reservoir.clear();
assert!(reservoir.is_empty());
assert_eq!(reservoir.count(), 0);
}
#[test]
fn test_merge_basic() {
let mut r1: ReservoirSampling<i32> = ReservoirSampling::with_seed(5, 42).unwrap();
let mut r2: ReservoirSampling<i32> = ReservoirSampling::with_seed(5, 43).unwrap();
for i in 0..10 {
r1.update(i);
}
for i in 10..20 {
r2.update(i);
}
r1.merge(&r2).unwrap();
assert_eq!(r1.len(), 5);
assert_eq!(r1.count(), 20);
}
#[test]
fn test_merge_incompatible() {
let r1: ReservoirSampling<i32> = ReservoirSampling::new(5).unwrap();
let r2: ReservoirSampling<i32> = ReservoirSampling::new(10).unwrap();
let mut r1_clone = r1.clone();
let result = r1_clone.merge(&r2);
assert!(result.is_err());
}
}