use crate::percentiles::percentile;
pub fn bootstrap_percentile_ci(
samples: &[u64],
q: f64,
iters: usize,
confidence: f64,
seed: u64,
) -> (u64, u64) {
if samples.is_empty() || iters == 0 {
return (0, 0);
}
let mut state = seed | 1;
let mut estimates = Vec::with_capacity(iters);
for _ in 0..iters {
let mut resample = Vec::with_capacity(samples.len());
for _ in 0..samples.len() {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let idx = (state as usize) % samples.len();
resample.push(samples[idx]);
}
resample.sort_unstable();
estimates.push(percentile(&resample, q));
}
estimates.sort_unstable();
let lo_q = (1.0 - confidence) / 2.0;
let hi_q = 1.0 - lo_q;
(percentile(&estimates, lo_q), percentile(&estimates, hi_q))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn p99_ci_brackets_point_estimate() {
let v: Vec<u64> = (0..1000).collect();
let (lo, hi) = bootstrap_percentile_ci(&v, 0.99, 200, 0.95, 42);
let point = {
let mut s = v.clone();
s.sort_unstable();
percentile(&s, 0.99)
};
assert!(lo <= point && point <= hi);
}
#[test]
fn empty_returns_zero_pair() {
assert_eq!(bootstrap_percentile_ci(&[], 0.99, 100, 0.95, 0), (0, 0));
}
#[test]
fn deterministic_under_same_seed() {
let v: Vec<u64> = (0..200).collect();
let a = bootstrap_percentile_ci(&v, 0.99, 100, 0.95, 7);
let b = bootstrap_percentile_ci(&v, 0.99, 100, 0.95, 7);
assert_eq!(a, b);
}
#[test]
fn zero_iters_returns_zero_pair() {
let v: Vec<u64> = (0..100).collect();
assert_eq!(bootstrap_percentile_ci(&v, 0.99, 0, 0.95, 0), (0, 0));
}
}