#[derive(Debug, Default, Copy, Clone)]
pub struct Stats {
count: usize,
min: f64,
arg_min: usize,
max: f64,
arg_max: usize,
avg: f64,
sum_squared: f64,
}
impl Stats {
#[inline]
pub fn count(&self) -> usize {
self.count
}
#[inline]
pub fn min(&self) -> Option<f64> {
match self.count {
0 => None,
_ => Some(self.min),
}
}
#[inline]
pub fn arg_min(&self) -> Option<usize> {
match self.count {
0 => None,
_ => Some(self.arg_min),
}
}
#[inline]
pub fn max(&self) -> Option<f64> {
match self.count {
0 => None,
_ => Some(self.max),
}
}
#[inline]
pub fn arg_max(&self) -> Option<usize> {
match self.count {
0 => None,
_ => Some(self.arg_max),
}
}
#[inline]
pub fn avg(&self) -> Option<f64> {
match self.count {
0 => None,
_ => Some(self.avg),
}
}
#[inline]
pub fn var(&self) -> Option<f64> {
match self.count {
0 => None,
1 => Some(0.0),
_ => Some(self.sum_squared / (self.count - 1) as f64),
}
}
#[inline]
pub fn std(&self) -> Option<f64> {
self.var().map(f64::sqrt)
}
#[inline]
pub fn record(&mut self, sample: f64) {
if self.count != 0 {
if sample < self.min {
self.min = sample;
self.arg_min = self.count;
}
if sample > self.max {
self.max = sample;
self.arg_max = self.count;
}
self.count += 1;
let delta = sample - self.avg;
self.avg += delta / self.count as f64;
self.sum_squared += delta * (sample - self.avg);
} else {
self.count = 1;
self.min = sample;
self.arg_min = 0;
self.max = sample;
self.arg_max = 0;
self.avg = sample;
self.sum_squared = 0.0;
}
}
pub fn snapshot(&self) -> Option<PopulatedStats> {
if self.count != 0 {
Some(PopulatedStats { stats: *self })
} else {
None
}
}
}
impl FromIterator<f64> for Stats {
fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> Self {
let mut samples = iter.into_iter();
let Some (first_sample) = samples.next() else {
return Default::default()
};
let mut count = 1;
let mut min = first_sample;
let mut arg_min = 0;
let mut max = first_sample;
let mut arg_max = 0;
let mut avg = first_sample;
let mut sum_squared = 0.0;
for sample in samples {
if sample < min {
min = sample;
arg_min = count;
}
if sample > max {
max = sample;
arg_max = count;
}
count += 1;
let delta = sample - avg;
avg += delta / count as f64;
sum_squared += delta * (sample - avg);
}
Self {
count,
min,
arg_min,
max,
arg_max,
avg,
sum_squared,
}
}
}
impl std::fmt::Display for Stats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.snapshot().map(|s| s.fmt(f)).unwrap_or_else(|| Ok(()))
}
}
#[derive(Debug, Copy, Clone)]
pub struct PopulatedStats {
stats: Stats,
}
impl PopulatedStats {
#[inline]
pub fn count(&self) -> usize {
self.stats.count
}
#[inline]
pub fn min(&self) -> f64 {
self.stats.min
}
#[inline]
pub fn arg_min(&self) -> usize {
self.stats.arg_min
}
#[inline]
pub fn max(&self) -> f64 {
self.stats.max
}
#[inline]
pub fn arg_max(&self) -> usize {
self.stats.arg_max
}
#[inline]
pub fn avg(&self) -> f64 {
self.stats.avg
}
#[inline]
pub fn var(&self) -> f64 {
if self.stats.count > 1 {
self.stats.sum_squared / (self.stats.count - 1) as f64
} else {
0.0
}
}
#[inline]
pub fn std(&self) -> f64 {
self.var().sqrt()
}
#[inline]
pub fn set_arg_min(&mut self, arg_min: usize) {
assert!(arg_min < self.count());
self.stats.arg_min = arg_min;
}
#[inline]
pub fn set_arg_max(&mut self, arg_max: usize) {
assert!(arg_max < self.count());
self.stats.arg_max = arg_max;
}
}
impl TryFrom<Stats> for PopulatedStats {
type Error = ();
#[inline]
fn try_from(stats: Stats) -> Result<Self, Self::Error> {
if stats.count != 0 {
Ok(Self { stats })
} else {
Err(())
}
}
}
impl std::fmt::Display for PopulatedStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(p) = f.precision() {
write!(f,
"Samples: {}; Min: {:.p$}; Max: {:.p$}; Average: {:.p$}; Variance: {:.p$}; STD: {:.p$}",
self.count(),
self.min(),
self.max(),
self.avg(),
self.var(),
self.std())
} else {
write!(
f,
"Samples: {}; Min: {}; Max: {}; Average: {}; Variance: {}; STD: {}",
self.count(),
self.min(),
self.max(),
self.avg(),
self.var(),
self.std()
)
}
}
}