use drawset::ReservoirSampler;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
fn main() {
let stream_len: usize = 1_000_000;
let k: usize = 100;
println!("Reservoir sampling (Algorithm L)");
println!(" stream length: {stream_len}");
println!(" reservoir size: {k}");
println!(" memory: O({k}) regardless of stream length");
println!();
let mut sampler = ReservoirSampler::new(k);
let mut rng = ChaCha8Rng::seed_from_u64(42);
for i in 0..stream_len {
sampler.add_with_rng(i, &mut rng);
}
assert_eq!(sampler.samples().len(), k);
assert_eq!(sampler.seen(), stream_len);
let mut reservoir: Vec<usize> = sampler.samples().to_vec();
reservoir.sort_unstable();
println!("Reservoir (sorted, first 20 of {k}):");
for (pos, &val) in reservoir.iter().enumerate().take(20) {
println!(" [{pos:>3}] = {val}");
}
println!(" ...");
println!();
let min = reservoir.iter().min().copied().unwrap_or(0);
let max = reservoir.iter().max().copied().unwrap_or(0);
let mean: f64 = reservoir.iter().map(|&v| v as f64).sum::<f64>() / k as f64;
let expected_mean = (stream_len - 1) as f64 / 2.0;
println!("Statistics:");
println!(" min: {min}");
println!(" max: {max}");
println!(" mean: {mean:.0}");
println!(" expected mean: {expected_mean:.0}");
println!();
let n_trials: usize = 5_000;
let n_buckets: usize = 10;
let bucket_size = stream_len / n_buckets;
let mut bucket_counts = vec![0u64; n_buckets];
for trial in 0..n_trials {
let mut s = ReservoirSampler::new(k);
let mut rng = ChaCha8Rng::seed_from_u64(trial as u64);
for i in 0..stream_len {
s.add_with_rng(i, &mut rng);
}
for &val in s.samples() {
let bucket = (val / bucket_size).min(n_buckets - 1);
bucket_counts[bucket] += 1;
}
}
let expected_per_bucket = n_trials as f64 * k as f64 / n_buckets as f64;
println!("Uniformity check ({n_trials} trials, {n_buckets} buckets):");
println!(
"{:>12} {:>12} {:>12} {:>10}",
"bucket", "count", "expected", "ratio"
);
println!("{}", "-".repeat(50));
let mut chi2: f64 = 0.0;
for (b, &bc) in bucket_counts.iter().enumerate().take(n_buckets) {
let lo = b * bucket_size;
let hi = lo + bucket_size - 1;
let count = bc as f64;
let ratio = count / expected_per_bucket;
chi2 += (count - expected_per_bucket).powi(2) / expected_per_bucket;
println!(
"{:>6}..{:<5} {:>12} {:>12.0} {:>10.4}",
lo, hi, bc, expected_per_bucket, ratio
);
}
println!();
println!(
"Chi-squared statistic: {chi2:.2} (df={}, expect ~{})",
n_buckets - 1,
n_buckets - 1
);
println!("Ratios near 1.0 confirm uniform sampling across the stream.");
}