use crate::percentiles;
#[derive(Clone, Copy)]
pub struct SubMsSamples<'a> {
raw: &'a [u64],
}
impl<'a> SubMsSamples<'a> {
pub fn new(raw: &'a [u64]) -> Self {
Self { raw }
}
pub fn count(&self) -> usize {
self.raw.len()
}
pub fn is_empty(&self) -> bool {
self.raw.is_empty()
}
pub fn raw(&self) -> &'a [u64] {
self.raw
}
pub fn p50(&self) -> u64 {
self.percentile(0.50)
}
pub fn p90(&self) -> u64 {
self.percentile(0.90)
}
pub fn p99(&self) -> u64 {
self.percentile(0.99)
}
pub fn p999(&self) -> u64 {
self.percentile(0.999)
}
pub fn max(&self) -> u64 {
self.raw.iter().copied().max().unwrap_or(0)
}
pub fn mean(&self) -> u64 {
percentiles::mean(self.raw)
}
pub fn stddev(&self) -> u64 {
percentiles::stddev(self.raw)
}
pub fn percentile(&self, q: f64) -> u64 {
let mut sorted = self.raw.to_vec();
sorted.sort_unstable();
percentiles::percentile(&sorted, q)
}
pub fn percentile_sweep(&self, start: f64, end: f64, step: f64) -> Vec<(f64, u64)> {
percentiles::percentile_sweep(self.raw, start, end, step)
}
#[cfg(feature = "histogram")]
pub fn cdf_buckets(&self) -> Vec<u64> {
crate::histogram::cdf_buckets(self.raw)
}
#[cfg(feature = "jitter")]
pub fn jitter_score(&self) -> f64 {
crate::jitter::jitter_score(self.raw)
}
#[cfg(feature = "tail")]
pub fn conditional_tail_expectation(&self, q: f64) -> u64 {
crate::tail::conditional_tail_expectation(self.raw, q)
}
#[cfg(feature = "tail")]
pub fn tail_fatness_ratio(&self) -> f64 {
crate::tail::tail_fatness_ratio(self.raw)
}
#[cfg(feature = "tail")]
pub fn hill_tail_index(&self, k: usize) -> Option<f64> {
crate::tail::hill_tail_index(self.raw, k)
}
#[cfg(feature = "robust")]
pub fn iqr(&self) -> u64 {
crate::robust::iqr(self.raw)
}
#[cfg(feature = "robust")]
pub fn median_absolute_deviation(&self) -> u64 {
crate::robust::median_absolute_deviation(self.raw)
}
#[cfg(feature = "robust")]
pub fn coefficient_of_variation(&self) -> f64 {
crate::robust::coefficient_of_variation(self.raw)
}
#[cfg(feature = "robust")]
pub fn skewness(&self) -> f64 {
crate::robust::skewness(self.raw)
}
#[cfg(feature = "robust")]
pub fn kurtosis(&self) -> f64 {
crate::robust::kurtosis(self.raw)
}
#[cfg(feature = "bootstrap")]
pub fn bootstrap_percentile_ci(
&self,
q: f64,
iters: usize,
confidence: f64,
seed: u64,
) -> (u64, u64) {
crate::bootstrap::bootstrap_percentile_ci(self.raw, q, iters, confidence, seed)
}
}
impl<'a> From<&'a [u64]> for SubMsSamples<'a> {
fn from(raw: &'a [u64]) -> Self {
Self::new(raw)
}
}
impl<'a> From<&'a Vec<u64>> for SubMsSamples<'a> {
fn from(raw: &'a Vec<u64>) -> Self {
Self::new(raw.as_slice())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_samples_returns_zero() {
let raw: Vec<u64> = Vec::new();
let s = SubMsSamples::new(&raw);
assert_eq!(s.count(), 0);
assert!(s.is_empty());
assert_eq!(s.p99(), 0);
assert_eq!(s.mean(), 0);
assert_eq!(s.stddev(), 0);
assert_eq!(s.max(), 0);
}
#[test]
fn known_distribution_percentiles() {
let raw: Vec<u64> = (0..100).collect();
let s = SubMsSamples::new(&raw);
assert_eq!(s.count(), 100);
assert_eq!(s.p50(), 50);
assert_eq!(s.p99(), 99);
assert_eq!(s.max(), 99);
}
#[test]
fn from_slice_and_vec_both_work() {
let v: Vec<u64> = vec![100, 200, 300];
let by_slice: SubMsSamples<'_> = (&v[..]).into();
let by_vec: SubMsSamples<'_> = (&v).into();
assert_eq!(by_slice.p50(), by_vec.p50());
}
}