autocore-std 3.3.30

Standard library for AutoCore control programs - shared memory, IPC, and logging utilities
Documentation
use std::fmt::Debug;
use std::ops::{AddAssign, Div};

/// Trait for types that can be used with [`RunningAverage`].
///
/// Provides conversion from a `usize` count, needed to compute the average.
/// Implemented for all standard numeric primitives.
pub trait Averageable: Copy + Default + Debug + AddAssign + Div<Output = Self> {
    /// Convert a `usize` count to `Self`.
    fn from_count(count: usize) -> Self;
}

macro_rules! impl_averageable {
    ($($t:ty),*) => {
        $(
            impl Averageable for $t {
                fn from_count(count: usize) -> Self {
                    count as $t
                }
            }
        )*
    };
}

impl_averageable!(f32, f64, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);

/// Running Average (FB_RunningAverage)
///
/// Accumulates values and computes their arithmetic mean.
/// Stores only the sum and count internally — no heap allocation.
///
/// Generic over any numeric type that implements [`Averageable`]
/// (all standard primitives: `f32`, `f64`, `i32`, `u64`, etc.).
///
/// # Example
///
/// ```
/// use autocore_std::fb::RunningAverage;
///
/// let mut avg = RunningAverage::<f64>::new();
///
/// avg.append(10.0);
/// avg.append(20.0);
/// avg.append(30.0);
///
/// assert_eq!(avg.count(), 3);
/// assert!((avg.average() - 20.0).abs() < f64::EPSILON);
///
/// avg.reset();
/// assert_eq!(avg.count(), 0);
/// assert_eq!(avg.average(), 0.0);
/// ```
///
/// # Use Cases
///
/// - Smoothing noisy sensor readings
/// - Computing mean cycle times
/// - Averaging analog input samples over N scans
#[derive(Debug, Clone)]
pub struct RunningAverage<T: Averageable> {
    sum: T,
    count: usize,
}

impl<T: Averageable> RunningAverage<T> {
    /// Creates a new running average with zero samples.
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::RunningAverage;
    ///
    /// let avg = RunningAverage::<f64>::new();
    /// assert_eq!(avg.count(), 0);
    /// assert_eq!(avg.average(), 0.0);
    /// ```
    pub fn new() -> Self {
        Self {
            sum: T::default(),
            count: 0,
        }
    }

    /// Appends a value to the running average.
    ///
    /// # Arguments
    ///
    /// * `value` - The value to include in the average
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::RunningAverage;
    ///
    /// let mut avg = RunningAverage::<f64>::new();
    /// avg.append(5.0);
    /// avg.append(15.0);
    /// assert_eq!(avg.count(), 2);
    /// assert!((avg.average() - 10.0).abs() < f64::EPSILON);
    /// ```
    pub fn append(&mut self, value: T) {
        self.sum += value;
        self.count += 1;
    }

    /// Resets the running average, clearing all accumulated data.
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::RunningAverage;
    ///
    /// let mut avg = RunningAverage::<f64>::new();
    /// avg.append(42.0);
    /// assert_eq!(avg.count(), 1);
    ///
    /// avg.reset();
    /// assert_eq!(avg.count(), 0);
    /// assert_eq!(avg.average(), 0.0);
    /// ```
    pub fn reset(&mut self) {
        self.sum = T::default();
        self.count = 0;
    }

    /// Returns the arithmetic mean of all appended values.
    ///
    /// Returns the default value for `T` (zero) when no values have been
    /// appended. For integer types, the result is truncated (integer division).
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::RunningAverage;
    ///
    /// let mut avg = RunningAverage::<f64>::new();
    /// assert_eq!(avg.average(), 0.0); // No values yet
    ///
    /// avg.append(10.0);
    /// avg.append(20.0);
    /// assert!((avg.average() - 15.0).abs() < f64::EPSILON);
    /// ```
    pub fn average(&self) -> T {
        if self.count == 0 {
            return T::default();
        }
        self.sum / T::from_count(self.count)
    }

    /// Returns the number of values appended since creation or the last reset.
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::RunningAverage;
    ///
    /// let mut avg = RunningAverage::<f32>::new();
    /// assert_eq!(avg.count(), 0);
    ///
    /// avg.append(1.0);
    /// avg.append(2.0);
    /// assert_eq!(avg.count(), 2);
    /// ```
    pub fn count(&self) -> usize {
        self.count
    }
}

impl<T: Averageable> Default for RunningAverage<T> {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_empty_average() {
        let avg = RunningAverage::<f64>::new();
        assert_eq!(avg.count(), 0);
        assert_eq!(avg.average(), 0.0);
    }

    #[test]
    fn test_single_value() {
        let mut avg = RunningAverage::<f64>::new();
        avg.append(42.0);
        assert_eq!(avg.count(), 1);
        assert!((avg.average() - 42.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_multiple_values() {
        let mut avg = RunningAverage::<f64>::new();
        avg.append(10.0);
        avg.append(20.0);
        avg.append(30.0);
        assert_eq!(avg.count(), 3);
        assert!((avg.average() - 20.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_reset() {
        let mut avg = RunningAverage::<f64>::new();
        avg.append(100.0);
        avg.append(200.0);
        assert_eq!(avg.count(), 2);

        avg.reset();
        assert_eq!(avg.count(), 0);
        assert_eq!(avg.average(), 0.0);

        avg.append(50.0);
        assert_eq!(avg.count(), 1);
        assert!((avg.average() - 50.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_f32() {
        let mut avg = RunningAverage::<f32>::new();
        avg.append(1.0);
        avg.append(2.0);
        avg.append(3.0);
        assert!((avg.average() - 2.0).abs() < f32::EPSILON);
    }

    #[test]
    fn test_integer_truncation() {
        let mut avg = RunningAverage::<i32>::new();
        avg.append(1);
        avg.append(2);
        // Integer division: (1 + 2) / 2 = 1
        assert_eq!(avg.average(), 1);
    }

    #[test]
    fn test_default_trait() {
        let avg = RunningAverage::<f64>::default();
        assert_eq!(avg.count(), 0);
        assert_eq!(avg.average(), 0.0);
    }
}