dvcompute_utils 2.0.0

Discrete event simulation library (utilities)
Documentation
// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::f64;
use std::fmt;
use std::result;
use std::borrow::Borrow;

use super::data::StatData;

/// An observations-based statistics.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct SamplingStats<T> {

    /// The total number of samples.
    pub count: usize,

    /// The minimum value among samples.
    pub min: T,

    /// The maximum value among samples.
    pub max: T,

    /// The average value.
    pub mean: f64,

    /// The average square value.
    pub mean2: f64
}

impl<T> SamplingStats<T> where T: Copy + PartialOrd + StatData {

    /// Create from the sample.
    #[inline]
    pub fn from_sample(sample: T) -> Self {
        let x: f64 = <T as StatData>::into_f64(sample);
        SamplingStats {
            count: 1,
            min: sample,
            max: sample,
            mean: x,
            mean2: x * x
        }
    }

    /// Create from the samples.
    #[inline]
    pub fn from_samples<I, Q>(samples: I) -> Self
        where I: IntoIterator<Item = Q>,
              Q: Borrow<T>
    {
        samples.into_iter().fold(Self::empty(), |acc, x| acc.add(*x.borrow()))
    }

    /// An empty statistics that has no samples.
    #[inline]
    pub fn empty() -> Self {
        SamplingStats {
            count: 0,
            min: <T as StatData>::max(),
            max: <T as StatData>::min(),
            mean: f64::NAN,
            mean2: f64::NAN
        }
    }

    /// Add a new sample to the statistics.
    pub fn add(&self, sample: T) -> Self {
        let x: f64 = <T as StatData>::into_f64(sample);
        if f64::is_nan(x) {
            *self
        } else if self.count == 0 {
            Self::from_sample(sample)
        } else {
            let count  = 1 + self.count;
            let n: f64 = count as f64;
            let k1     = 1.0 / n;
            let k2     = (n - 1.0) / n;
            let min    = if sample < self.min { sample } else { self.min };
            let max    = if sample > self.max { sample } else { self.max };
            let mean   = k1 * x + k2 * self.mean;
            let mean2  = k1 * x * x + k2 * self.mean2;

            SamplingStats {
                count: count,
                min: min,
                max: max,
                mean: mean,
                mean2: mean2
            }
        }
    }

    /// Combine two statistics.
    #[inline]
    pub fn combine(&self, other: &Self) -> Self {
        let c1 = self.count;
        let c2 = other.count;
        if c1 == 0 {
            *other
        } else if c2 == 0 {
            *self
        } else {
            let count = c1 + c2;
            let n1    = c1 as f64;
            let n2    = c2 as f64;
            let n     = n1 + n2;
            let k1    = n1 / n;
            let k2    = n2 / n;
            let min   = if self.min < other.min { self.min } else { other.min };
            let max   = if self.max > other.max { self.max } else { other.max };
            let mean  = k1 * self.mean + k2 * other.mean;
            let mean2 = k1 * self.mean2 + k2 * other.mean2;

            SamplingStats {
                count: count,
                min: min,
                max: max,
                mean: mean,
                mean2: mean2
            }
        }
    }

    /// Return the variance.
    #[inline]
    pub fn variance(&self) -> f64 {
        if self.count == 1 {
            0.0
        } else {
            let n = self.count as f64;
            (self.mean2 - self.mean * self.mean) * (n / (n - 1.0))
        }
    }

    /// Return the deviation.
    #[inline]
    pub fn deviation(&self) -> f64 {
        f64::sqrt(self.variance())
    }
}

impl<T> fmt::Display for SamplingStats<T>
    where T: fmt::Display + Copy + PartialOrd + StatData
{
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(f, "{{ count = {}, mean = {}, std = {}, min = {}, max = {} }}",
            self.count, self.mean, self.deviation(), self.min, self.max)
    }
}

impl From<SamplingStats<i32>> for SamplingStats<f32> {
    fn from(stats: SamplingStats<i32>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}

impl From<SamplingStats<i32>> for SamplingStats<f64> {
    fn from(stats: SamplingStats<i32>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}

impl From<SamplingStats<i64>> for SamplingStats<f32> {
    fn from(stats: SamplingStats<i64>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}

impl From<SamplingStats<i64>> for SamplingStats<f64> {
    fn from(stats: SamplingStats<i64>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}

impl From<SamplingStats<isize>> for SamplingStats<f32> {
    fn from(stats: SamplingStats<isize>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}

impl From<SamplingStats<isize>> for SamplingStats<f64> {
    fn from(stats: SamplingStats<isize>) -> Self {
        SamplingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            mean: stats.mean,
            mean2: stats.mean2
        }
    }
}