use std::{convert::TryInto, time::Duration};
#[derive(Debug)]
pub struct SlidingAvg {
mean: i64,
deviation: i64,
sample_count: usize,
inverted_gain: usize,
}
impl SlidingAvg {
pub fn new(inverted_gain: usize) -> Self {
Self { mean: 0, deviation: 0, sample_count: 0, inverted_gain }
}
pub fn update(&mut self, mut sample: i64) {
sample *= 64;
let deviation =
if self.sample_count > 0 { (self.mean - sample).abs() } else { 0 };
if self.sample_count < self.inverted_gain {
self.sample_count += 1;
}
self.mean += (sample - self.mean) / self.sample_count as i64;
if self.sample_count > 1 {
self.deviation +=
(deviation - self.deviation) / (self.sample_count - 1) as i64;
}
}
pub fn mean(&self) -> i64 {
if self.sample_count == 0 {
0
} else {
(self.mean + 32) / 64
}
}
pub fn deviation(&self) -> i64 {
if self.sample_count == 0 {
0
} else {
(self.deviation + 32) / 64
}
}
}
impl Default for SlidingAvg {
fn default() -> Self {
Self::new(20)
}
}
#[derive(Debug)]
pub struct SlidingDurationAvg(SlidingAvg);
impl SlidingDurationAvg {
pub fn new(inverted_gain: usize) -> Self {
Self(SlidingAvg::new(inverted_gain))
}
pub fn update(&mut self, sample: Duration) {
let ms = sample.as_millis().try_into().expect("Millisecond overflow");
self.0.update(ms);
}
pub fn mean(&self) -> Duration {
let ms = self.0.mean() as u64;
Duration::from_millis(ms)
}
pub fn deviation(&self) -> Duration {
let ms = self.0.deviation() as u64;
Duration::from_millis(ms)
}
}
impl Default for SlidingDurationAvg {
fn default() -> Self {
Self(SlidingAvg::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sliding_average() {
let inverted_gain = 4;
let mut a = SlidingAvg::new(inverted_gain);
let sample = 10;
a.update(sample);
assert_eq!(a.sample_count, 1);
assert_eq!(a.mean(), sample);
let sample = 15;
a.update(sample);
assert_eq!(a.sample_count, 2);
assert_eq!(a.mean(), 13);
let sample = 20;
a.update(sample);
assert_eq!(a.sample_count, 3);
assert_eq!(a.mean(), 15);
let sample = 19;
a.update(sample);
assert_eq!(a.sample_count, 4);
assert_eq!(a.mean(), 16);
let sample = 20;
a.update(sample);
assert_eq!(a.sample_count, 4);
assert_eq!(a.mean(), 17);
let sample = 21;
a.update(sample);
assert_eq!(a.sample_count, 4);
assert_eq!(a.mean(), 18);
let sample = 118;
a.update(sample);
assert_eq!(a.mean(), 43);
}
#[test]
fn sliding_duration_average() {
let mut a = SlidingDurationAvg::default();
let sample = Duration::from_secs(10);
a.update(sample);
assert_eq!(a.0.sample_count, 1);
assert_eq!(a.mean(), sample);
}
}