use std::sync::Mutex;
pub struct SumAverager {
state: Mutex<AverState>,
}
#[derive(Default)]
struct AverState {
sum: f64,
count: u64,
}
impl Default for SumAverager {
fn default() -> Self {
Self::new()
}
}
impl SumAverager {
pub fn new() -> Self {
Self {
state: Mutex::new(AverState::default()),
}
}
pub fn push(&self, sample: f64) {
let mut s = self.state.lock().unwrap();
s.sum += sample;
s.count += 1;
}
pub fn read_and_reset(&self) -> f64 {
let mut s = self.state.lock().unwrap();
let m = if s.count == 0 {
0.0
} else {
s.sum / s.count as f64
};
s.sum = 0.0;
s.count = 0;
m
}
pub fn peek(&self) -> f64 {
let s = self.state.lock().unwrap();
if s.count == 0 {
0.0
} else {
s.sum / s.count as f64
}
}
pub fn count(&self) -> u64 {
self.state.lock().unwrap().count
}
pub fn reset(&self) {
let mut s = self.state.lock().unwrap();
s.sum = 0.0;
s.count = 0;
}
pub fn read_and_reset_int32(&self) -> i32 {
let mut s = self.state.lock().unwrap();
let n = s.count;
let sum = s.sum;
s.sum = 0.0;
s.count = 0;
if n == 0 {
return 0;
}
let mut dval = sum / n as f64;
dval += if sum > 0.0 { 0.5 } else { -0.5 };
dval as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mean_of_four_samples() {
let a = SumAverager::new();
for v in [10.0, 20.0, 30.0, 40.0] {
a.push(v);
}
assert_eq!(a.count(), 4);
assert!((a.peek() - 25.0).abs() < 1e-9);
assert!((a.read_and_reset() - 25.0).abs() < 1e-9);
assert_eq!(a.count(), 0);
}
#[test]
fn empty_returns_zero_mean() {
let a = SumAverager::new();
assert_eq!(a.peek(), 0.0);
assert_eq!(a.read_and_reset(), 0.0);
}
#[test]
fn accumulator_unbounded_no_drop_on_overflow() {
let a = SumAverager::new();
for v in 1..=100 {
a.push(v as f64);
}
assert_eq!(a.count(), 100);
assert!((a.peek() - 50.5).abs() < 1e-9);
}
#[test]
fn read_and_reset_int32_rounds_positive() {
let a = SumAverager::new();
a.push(25.0);
a.push(26.0);
assert_eq!(a.read_and_reset_int32(), 26);
}
#[test]
fn read_and_reset_int32_rounds_negative() {
let a = SumAverager::new();
a.push(-25.0);
a.push(-26.0);
assert_eq!(a.read_and_reset_int32(), -26);
}
#[test]
fn read_and_reset_int32_empty_returns_zero() {
let a = SumAverager::new();
assert_eq!(a.read_and_reset_int32(), 0);
}
#[test]
fn reset_clears_without_computing() {
let a = SumAverager::new();
a.push(100.0);
a.push(200.0);
a.reset();
assert_eq!(a.count(), 0);
assert_eq!(a.peek(), 0.0);
}
}