use num_traits::ToPrimitive;
use rand::{self, Rng};
use std::ops::Sub;
pub fn ewma<'a, T, I>(mut samples: I, alpha: f64) -> f64
where
T: ToPrimitive + 'a,
I: Iterator<Item = &'a T>,
{
let first = samples.next().map_or(0.0, |v| v.to_f64().unwrap());
samples
.map(|v| v.to_f64().unwrap())
.fold(first, |avg, sample| alpha * sample + (1.0 - alpha) * avg)
}
pub fn abs_diff<T: PartialOrd + Sub<Output = U>, U>(a: T, b: T) -> U {
if a > b {
a - b
} else {
b - a
}
}
pub fn generate_sequential_identifiers() -> (u16, u16) {
let mut rng = rand::thread_rng();
let id = rng.gen::<u16>();
if id.checked_add(1).is_some() {
(id, id + 1)
} else {
(id - 1, id)
}
}
#[cfg(test)]
mod test {
use std::f64::EPSILON;
use crate::util::*;
#[test]
fn test_ewma_empty_vector() {
let empty: Vec<u32> = vec![];
let alpha = 1.0 / 3.0;
assert!(ewma(empty.iter(), alpha).abs() < EPSILON);
}
#[test]
fn test_ewma_one_element() {
let input = vec![1u32];
let alpha = 1.0 / 3.0;
assert!(ewma(input.iter(), alpha) - 1.0 < EPSILON);
}
#[test]
fn test_exponential_smoothed_moving_average() {
let input = (1u32..11).collect::<Vec<u32>>();
let alpha = 1.0 / 3.0;
let expected = [
1.0,
4.0 / 3.0,
17.0 / 9.0,
70.0 / 27.0,
275.0 / 81.0,
1036.0 / 243.0,
3773.0 / 729.0,
13378.0 / 2187.0,
46439.0 / 6561.0,
158_488.0 / 19_683.0,
];
assert!(
ewma(input.iter(), alpha) - expected[expected.len() - 1] < EPSILON
);
}
#[test]
fn test_abs_diff() {
let a = 10;
let b = 5;
assert_eq!(abs_diff(a, b), 5);
assert_eq!(abs_diff(b, a), 5);
}
}