use crate::foundation::Result;
use crate::stats::{mean, percentile};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ReservoirSummary {
pub p90: f64,
pub p50: f64,
pub p10: f64,
pub mean: f64,
}
pub fn reservoir_summary(data: &[f64]) -> Result<ReservoirSummary> {
Ok(ReservoirSummary {
p90: percentile(data, 10.0)?,
p50: percentile(data, 50.0)?,
p10: percentile(data, 90.0)?,
mean: mean(data)?,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::foundation::AlgoError;
use approx::assert_relative_eq;
#[test]
fn empty_errors() {
assert!(matches!(
reservoir_summary(&[]),
Err(AlgoError::EmptyInput(_))
));
}
#[test]
fn known_values_follow_exceedance_convention() {
let data: Vec<f64> = (1..=11).map(|v| v as f64).collect();
let s = reservoir_summary(&data).unwrap();
assert_relative_eq!(s.p90, 2.0, epsilon = 1e-12);
assert_relative_eq!(s.p50, 6.0, epsilon = 1e-12);
assert_relative_eq!(s.p10, 10.0, epsilon = 1e-12);
assert_relative_eq!(s.mean, 6.0, epsilon = 1e-12);
assert!(s.p90 <= s.p50 && s.p50 <= s.p10);
}
#[test]
fn single_realization_collapses_to_that_value() {
let s = reservoir_summary(&[42.0]).unwrap();
assert_eq!((s.p90, s.p50, s.p10, s.mean), (42.0, 42.0, 42.0, 42.0));
}
}