autocore_std/fb/running_average.rs
1use std::fmt::Debug;
2use std::ops::{AddAssign, Div};
3
4/// Trait for types that can be used with [`RunningAverage`].
5///
6/// Provides conversion from a `usize` count, needed to compute the average.
7/// Implemented for all standard numeric primitives.
8pub trait Averageable: Copy + Default + Debug + AddAssign + Div<Output = Self> {
9 /// Convert a `usize` count to `Self`.
10 fn from_count(count: usize) -> Self;
11}
12
13macro_rules! impl_averageable {
14 ($($t:ty),*) => {
15 $(
16 impl Averageable for $t {
17 fn from_count(count: usize) -> Self {
18 count as $t
19 }
20 }
21 )*
22 };
23}
24
25impl_averageable!(f32, f64, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);
26
27/// Running Average (FB_RunningAverage)
28///
29/// Accumulates values and computes their arithmetic mean.
30/// Stores only the sum and count internally — no heap allocation.
31///
32/// Generic over any numeric type that implements [`Averageable`]
33/// (all standard primitives: `f32`, `f64`, `i32`, `u64`, etc.).
34///
35/// # Example
36///
37/// ```
38/// use autocore_std::fb::RunningAverage;
39///
40/// let mut avg = RunningAverage::<f64>::new();
41///
42/// avg.append(10.0);
43/// avg.append(20.0);
44/// avg.append(30.0);
45///
46/// assert_eq!(avg.count(), 3);
47/// assert!((avg.average() - 20.0).abs() < f64::EPSILON);
48///
49/// avg.reset();
50/// assert_eq!(avg.count(), 0);
51/// assert_eq!(avg.average(), 0.0);
52/// ```
53///
54/// # Use Cases
55///
56/// - Smoothing noisy sensor readings
57/// - Computing mean cycle times
58/// - Averaging analog input samples over N scans
59#[derive(Debug, Clone)]
60pub struct RunningAverage<T: Averageable> {
61 sum: T,
62 count: usize,
63}
64
65impl<T: Averageable> RunningAverage<T> {
66 /// Creates a new running average with zero samples.
67 ///
68 /// # Example
69 ///
70 /// ```
71 /// use autocore_std::fb::RunningAverage;
72 ///
73 /// let avg = RunningAverage::<f64>::new();
74 /// assert_eq!(avg.count(), 0);
75 /// assert_eq!(avg.average(), 0.0);
76 /// ```
77 pub fn new() -> Self {
78 Self {
79 sum: T::default(),
80 count: 0,
81 }
82 }
83
84 /// Appends a value to the running average.
85 ///
86 /// # Arguments
87 ///
88 /// * `value` - The value to include in the average
89 ///
90 /// # Example
91 ///
92 /// ```
93 /// use autocore_std::fb::RunningAverage;
94 ///
95 /// let mut avg = RunningAverage::<f64>::new();
96 /// avg.append(5.0);
97 /// avg.append(15.0);
98 /// assert_eq!(avg.count(), 2);
99 /// assert!((avg.average() - 10.0).abs() < f64::EPSILON);
100 /// ```
101 pub fn append(&mut self, value: T) {
102 self.sum += value;
103 self.count += 1;
104 }
105
106 /// Resets the running average, clearing all accumulated data.
107 ///
108 /// # Example
109 ///
110 /// ```
111 /// use autocore_std::fb::RunningAverage;
112 ///
113 /// let mut avg = RunningAverage::<f64>::new();
114 /// avg.append(42.0);
115 /// assert_eq!(avg.count(), 1);
116 ///
117 /// avg.reset();
118 /// assert_eq!(avg.count(), 0);
119 /// assert_eq!(avg.average(), 0.0);
120 /// ```
121 pub fn reset(&mut self) {
122 self.sum = T::default();
123 self.count = 0;
124 }
125
126 /// Returns the arithmetic mean of all appended values.
127 ///
128 /// Returns the default value for `T` (zero) when no values have been
129 /// appended. For integer types, the result is truncated (integer division).
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// use autocore_std::fb::RunningAverage;
135 ///
136 /// let mut avg = RunningAverage::<f64>::new();
137 /// assert_eq!(avg.average(), 0.0); // No values yet
138 ///
139 /// avg.append(10.0);
140 /// avg.append(20.0);
141 /// assert!((avg.average() - 15.0).abs() < f64::EPSILON);
142 /// ```
143 pub fn average(&self) -> T {
144 if self.count == 0 {
145 return T::default();
146 }
147 self.sum / T::from_count(self.count)
148 }
149
150 /// Returns the number of values appended since creation or the last reset.
151 ///
152 /// # Example
153 ///
154 /// ```
155 /// use autocore_std::fb::RunningAverage;
156 ///
157 /// let mut avg = RunningAverage::<f32>::new();
158 /// assert_eq!(avg.count(), 0);
159 ///
160 /// avg.append(1.0);
161 /// avg.append(2.0);
162 /// assert_eq!(avg.count(), 2);
163 /// ```
164 pub fn count(&self) -> usize {
165 self.count
166 }
167}
168
169impl<T: Averageable> Default for RunningAverage<T> {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_empty_average() {
181 let avg = RunningAverage::<f64>::new();
182 assert_eq!(avg.count(), 0);
183 assert_eq!(avg.average(), 0.0);
184 }
185
186 #[test]
187 fn test_single_value() {
188 let mut avg = RunningAverage::<f64>::new();
189 avg.append(42.0);
190 assert_eq!(avg.count(), 1);
191 assert!((avg.average() - 42.0).abs() < f64::EPSILON);
192 }
193
194 #[test]
195 fn test_multiple_values() {
196 let mut avg = RunningAverage::<f64>::new();
197 avg.append(10.0);
198 avg.append(20.0);
199 avg.append(30.0);
200 assert_eq!(avg.count(), 3);
201 assert!((avg.average() - 20.0).abs() < f64::EPSILON);
202 }
203
204 #[test]
205 fn test_reset() {
206 let mut avg = RunningAverage::<f64>::new();
207 avg.append(100.0);
208 avg.append(200.0);
209 assert_eq!(avg.count(), 2);
210
211 avg.reset();
212 assert_eq!(avg.count(), 0);
213 assert_eq!(avg.average(), 0.0);
214
215 avg.append(50.0);
216 assert_eq!(avg.count(), 1);
217 assert!((avg.average() - 50.0).abs() < f64::EPSILON);
218 }
219
220 #[test]
221 fn test_f32() {
222 let mut avg = RunningAverage::<f32>::new();
223 avg.append(1.0);
224 avg.append(2.0);
225 avg.append(3.0);
226 assert!((avg.average() - 2.0).abs() < f32::EPSILON);
227 }
228
229 #[test]
230 fn test_integer_truncation() {
231 let mut avg = RunningAverage::<i32>::new();
232 avg.append(1);
233 avg.append(2);
234 // Integer division: (1 + 2) / 2 = 1
235 assert_eq!(avg.average(), 1);
236 }
237
238 #[test]
239 fn test_default_trait() {
240 let avg = RunningAverage::<f64>::default();
241 assert_eq!(avg.count(), 0);
242 assert_eq!(avg.average(), 0.0);
243 }
244}