pub struct Distribution {
sorted: Vec<f64>,
}
impl Distribution {
pub fn from_unsorted(mut values: Vec<f64>) -> Self {
values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
Self { sorted: values }
}
pub fn from_sorted(values: Vec<f64>) -> Self {
Self { sorted: values }
}
pub fn quantile(&self, p: f64) -> f64 {
if self.sorted.is_empty() {
return 0.0;
}
let n = self.sorted.len();
let idx = ((n as f64 * p).floor() as usize).min(n - 1);
self.sorted[idx]
}
pub fn min(&self) -> f64 {
self.sorted.first().copied().unwrap_or(0.0)
}
pub fn max(&self) -> f64 {
self.sorted.last().copied().unwrap_or(0.0)
}
pub fn mean(&self) -> f64 {
if self.sorted.is_empty() {
return 0.0;
}
self.sorted.iter().sum::<f64>() / self.sorted.len() as f64
}
pub fn is_empty(&self) -> bool {
self.sorted.is_empty()
}
pub fn len(&self) -> usize {
self.sorted.len()
}
pub fn value_at(&self, idx: usize) -> f64 {
self.sorted[idx]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distribution_quantile_known_values() {
let values: Vec<f64> = (1..=100).map(|i| i as f64).collect();
let dist = Distribution::from_sorted(values);
assert_eq!(dist.quantile(0.0), 1.0);
assert_eq!(dist.quantile(0.5), 51.0);
assert_eq!(dist.quantile(0.99), 100.0);
assert_eq!(dist.quantile(1.0), 100.0);
}
#[test]
fn distribution_empty_returns_zero() {
let dist = Distribution::from_sorted(vec![]);
assert_eq!(dist.quantile(0.5), 0.0);
assert_eq!(dist.min(), 0.0);
assert_eq!(dist.max(), 0.0);
assert_eq!(dist.mean(), 0.0);
}
#[test]
fn distribution_single_element() {
let dist = Distribution::from_sorted(vec![42.0]);
assert_eq!(dist.quantile(0.0), 42.0);
assert_eq!(dist.quantile(0.5), 42.0);
assert_eq!(dist.quantile(1.0), 42.0);
assert_eq!(dist.min(), 42.0);
assert_eq!(dist.max(), 42.0);
assert_eq!(dist.mean(), 42.0);
}
#[test]
fn distribution_quantile_boundary_p100() {
let values: Vec<f64> = (1..=10).map(|i| i as f64).collect();
let dist = Distribution::from_sorted(values);
assert_eq!(dist.quantile(1.0), 10.0);
}
#[test]
fn distribution_avg_is_arithmetic_mean() {
let dist = Distribution::from_sorted(vec![1.0, 2.0, 3.0]);
assert_eq!(dist.mean(), 2.0);
}
#[test]
fn distribution_is_sorted_on_construction() {
let dist = Distribution::from_unsorted(vec![5.0, 1.0, 3.0, 2.0, 4.0]);
assert_eq!(dist.min(), 1.0);
assert_eq!(dist.max(), 5.0);
assert_eq!(dist.quantile(0.5), 3.0);
}
#[test]
fn distribution_len_and_is_empty() {
let empty = Distribution::from_sorted(vec![]);
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
let dist = Distribution::from_sorted(vec![1.0, 2.0, 3.0]);
assert!(!dist.is_empty());
assert_eq!(dist.len(), 3);
}
#[test]
fn distribution_value_at_returns_correct_element() {
let dist = Distribution::from_sorted(vec![10.0, 20.0, 30.0, 40.0, 50.0]);
assert_eq!(dist.value_at(0), 10.0);
assert_eq!(dist.value_at(2), 30.0);
assert_eq!(dist.value_at(4), 50.0);
}
}