#[derive(Debug, Clone, PartialEq)]
pub struct Quantile(f64, String);
impl Quantile {
pub fn new(quantile: f64) -> Quantile {
let clamped = quantile.max(0.0);
let clamped = clamped.min(1.0);
let display = clamped * 100.0;
let raw_label = format!("{}", clamped);
let label = match raw_label.as_str() {
"0" => "min".to_string(),
"1" => "max".to_string(),
_ => {
let raw = format!("p{}", display);
raw.replace('.', "")
}
};
Quantile(clamped, label)
}
pub fn label(&self) -> &str {
self.1.as_str()
}
pub fn value(&self) -> f64 {
self.0
}
}
pub fn parse_quantiles(quantiles: &[f64]) -> Vec<Quantile> {
quantiles.iter().map(|f| Quantile::new(*f)).collect()
}
#[cfg(test)]
mod tests {
use super::{parse_quantiles, Quantile};
#[test]
fn test_quantiles() {
let min = Quantile::new(0.0);
assert_eq!(min.value(), 0.0);
assert_eq!(min.label(), "min");
let max = Quantile::new(1.0);
assert_eq!(max.value(), 1.0);
assert_eq!(max.label(), "max");
let p99 = Quantile::new(0.99);
assert_eq!(p99.value(), 0.99);
assert_eq!(p99.label(), "p99");
let p999 = Quantile::new(0.999);
assert_eq!(p999.value(), 0.999);
assert_eq!(p999.label(), "p999");
let p9999 = Quantile::new(0.9999);
assert_eq!(p9999.value(), 0.9999);
assert_eq!(p9999.label(), "p9999");
let under = Quantile::new(-1.0);
assert_eq!(under.value(), 0.0);
assert_eq!(under.label(), "min");
let over = Quantile::new(1.2);
assert_eq!(over.value(), 1.0);
assert_eq!(over.label(), "max");
}
#[test]
fn test_parse_quantiles() {
let empty = vec![];
let result = parse_quantiles(&empty);
assert_eq!(result.len(), 0);
let normal = vec![0.0, 0.5, 0.99, 0.999, 1.0];
let result = parse_quantiles(&normal);
assert_eq!(result.len(), 5);
assert_eq!(result[0], Quantile::new(0.0));
assert_eq!(result[1], Quantile::new(0.5));
assert_eq!(result[2], Quantile::new(0.99));
assert_eq!(result[3], Quantile::new(0.999));
assert_eq!(result[4], Quantile::new(1.0));
}
}