use std::fmt::Debug;
use std::ops::{AddAssign, Div};
pub trait Averageable: Copy + Default + Debug + AddAssign + Div<Output = Self> {
fn from_count(count: usize) -> Self;
}
macro_rules! impl_averageable {
($($t:ty),*) => {
$(
impl Averageable for $t {
fn from_count(count: usize) -> Self {
count as $t
}
}
)*
};
}
impl_averageable!(f32, f64, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);
#[derive(Debug, Clone)]
pub struct RunningAverage<T: Averageable> {
sum: T,
count: usize,
}
impl<T: Averageable> RunningAverage<T> {
pub fn new() -> Self {
Self {
sum: T::default(),
count: 0,
}
}
pub fn append(&mut self, value: T) {
self.sum += value;
self.count += 1;
}
pub fn reset(&mut self) {
self.sum = T::default();
self.count = 0;
}
pub fn average(&self) -> T {
if self.count == 0 {
return T::default();
}
self.sum / T::from_count(self.count)
}
pub fn count(&self) -> usize {
self.count
}
}
impl<T: Averageable> Default for RunningAverage<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_average() {
let avg = RunningAverage::<f64>::new();
assert_eq!(avg.count(), 0);
assert_eq!(avg.average(), 0.0);
}
#[test]
fn test_single_value() {
let mut avg = RunningAverage::<f64>::new();
avg.append(42.0);
assert_eq!(avg.count(), 1);
assert!((avg.average() - 42.0).abs() < f64::EPSILON);
}
#[test]
fn test_multiple_values() {
let mut avg = RunningAverage::<f64>::new();
avg.append(10.0);
avg.append(20.0);
avg.append(30.0);
assert_eq!(avg.count(), 3);
assert!((avg.average() - 20.0).abs() < f64::EPSILON);
}
#[test]
fn test_reset() {
let mut avg = RunningAverage::<f64>::new();
avg.append(100.0);
avg.append(200.0);
assert_eq!(avg.count(), 2);
avg.reset();
assert_eq!(avg.count(), 0);
assert_eq!(avg.average(), 0.0);
avg.append(50.0);
assert_eq!(avg.count(), 1);
assert!((avg.average() - 50.0).abs() < f64::EPSILON);
}
#[test]
fn test_f32() {
let mut avg = RunningAverage::<f32>::new();
avg.append(1.0);
avg.append(2.0);
avg.append(3.0);
assert!((avg.average() - 2.0).abs() < f32::EPSILON);
}
#[test]
fn test_integer_truncation() {
let mut avg = RunningAverage::<i32>::new();
avg.append(1);
avg.append(2);
assert_eq!(avg.average(), 1);
}
#[test]
fn test_default_trait() {
let avg = RunningAverage::<f64>::default();
assert_eq!(avg.count(), 0);
assert_eq!(avg.average(), 0.0);
}
}