organix 0.1.3

Building organic application in rust, leveraging tokio and defining inter communication layer mechanism
Documentation
/// keep some stats based on [Welford's online algorithm]
///
/// [Welford's online algorithm]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
pub struct Stats {
    count: u64,
    mean: f64,
    m2: f64,
}

impl Stats {
    pub fn new() -> Self {
        Self {
            count: 0,
            mean: 0.0,
            m2: 0.0,
        }
    }

    pub fn push(&mut self, entry: f64) {
        self.count += 1;

        if self.count == 1 {
            self.mean = entry;
            self.m2 = 0.0;
        } else {
            let delta = entry - self.mean;
            self.mean += delta / self.count as f64;
            let delta2 = entry - self.mean;
            self.m2 += delta * delta2;
        }
    }

    pub fn count(&self) -> usize {
        self.count as usize
    }

    pub fn mean(&self) -> f64 {
        self.mean
    }

    pub fn variance(&self) -> f64 {
        if self.count() < 2 {
            0.0
        } else {
            self.m2 / (self.count - 1) as f64
        }
    }

    pub fn standard_derivation(&self) -> f64 {
        self.m2.sqrt()
    }
}

impl Default for Stats {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn no_entries() {
        let stats = Stats::new();

        assert_eq!(stats.count(), 0);
        approx::assert_relative_eq!(stats.mean(), 0.0);
        approx::assert_relative_eq!(stats.variance(), 0.0);
        approx::assert_relative_eq!(stats.standard_derivation(), 0.0);
    }

    #[test]
    fn one_entry() {
        let mut stats = Stats::new();

        stats.push(10.0);

        assert_eq!(stats.count(), 1);
        approx::assert_relative_eq!(stats.mean(), 10.0);
        approx::assert_relative_eq!(stats.variance(), 0.0);
        approx::assert_relative_eq!(stats.standard_derivation(), 0.0);
    }

    #[test]
    fn tow_entries() {
        let mut stats = Stats::new();

        stats.push(10.0);
        stats.push(20.0);

        assert_eq!(stats.count(), 2);
        approx::assert_relative_eq!(stats.mean(), 15.0);
        approx::assert_relative_eq!(stats.variance(), 50.0);
        approx::assert_relative_eq!(stats.standard_derivation(), 7.07, max_relative = 0.001,);
    }

    fn unit(entries: &[f64], mean: f64, variance: f64, standard_derivation: f64) {
        let count = entries.len();

        let mut stats = Stats::new();

        for i in entries {
            stats.push(*i);
        }

        assert_eq!(stats.count(), count);
        approx::assert_relative_eq!(stats.mean(), mean, max_relative = 0.001);
        approx::assert_relative_eq!(stats.variance(), variance, max_relative = 0.001);
        approx::assert_relative_eq!(
            stats.standard_derivation(),
            standard_derivation,
            max_relative = 0.001,
        );
    }

    #[test]
    fn units() {
        unit(&[15.0, 15.0], 15.0, 0.0, 0.0);
        unit(&[14.0, 16.0], 15.0, 2.0, 1.414);
        unit(&[10.0, 15.0, 20.0], 15.0, 25.0, 7.071);
        unit(&[10.0, 11.0, 15.0, 19.0, 20.0], 15.0, 20.5, 9.055);
    }
}