oxihuman_core/
moving_average.rs1#![allow(dead_code)]
4
5use std::collections::VecDeque;
8
9pub struct SimpleMovingAverage {
11 window: VecDeque<f64>,
12 size: usize,
13 sum: f64,
14}
15
16pub fn new_sma(window_size: usize) -> SimpleMovingAverage {
18 SimpleMovingAverage {
19 window: VecDeque::new(),
20 size: window_size.max(1),
21 sum: 0.0,
22 }
23}
24
25impl SimpleMovingAverage {
26 pub fn push(&mut self, x: f64) -> f64 {
28 self.window.push_back(x);
29 self.sum += x;
30 if self.window.len() > self.size {
31 self.sum -= self.window.pop_front().unwrap_or(0.0);
32 }
33 self.current()
34 }
35
36 pub fn current(&self) -> f64 {
38 if self.window.is_empty() {
39 0.0
40 } else {
41 self.sum / self.window.len() as f64
42 }
43 }
44
45 pub fn len(&self) -> usize {
47 self.window.len()
48 }
49
50 pub fn is_empty(&self) -> bool {
52 self.window.is_empty()
53 }
54
55 pub fn is_full(&self) -> bool {
57 self.window.len() == self.size
58 }
59
60 pub fn reset(&mut self) {
62 self.window.clear();
63 self.sum = 0.0;
64 }
65}
66
67pub struct ExponentialMovingAverage {
69 alpha: f64,
70 value: Option<f64>,
71}
72
73pub fn new_ema(alpha: f64) -> ExponentialMovingAverage {
75 let alpha = alpha.clamp(1e-6, 1.0);
76 ExponentialMovingAverage { alpha, value: None }
77}
78
79impl ExponentialMovingAverage {
80 pub fn push(&mut self, x: f64) -> f64 {
82 self.value = Some(match self.value {
83 None => x,
84 Some(prev) => self.alpha * x + (1.0 - self.alpha) * prev,
85 });
86 self.value.unwrap_or_default()
87 }
88
89 pub fn current(&self) -> Option<f64> {
91 self.value
92 }
93
94 pub fn alpha(&self) -> f64 {
96 self.alpha
97 }
98
99 pub fn reset(&mut self) {
101 self.value = None;
102 }
103}
104
105pub struct WeightedMovingAverage {
107 window: VecDeque<f64>,
108 size: usize,
109}
110
111pub fn new_wma(window_size: usize) -> WeightedMovingAverage {
113 WeightedMovingAverage {
114 window: VecDeque::new(),
115 size: window_size.max(1),
116 }
117}
118
119impl WeightedMovingAverage {
120 pub fn push(&mut self, x: f64) -> f64 {
122 self.window.push_back(x);
123 if self.window.len() > self.size {
124 self.window.pop_front();
125 }
126 self.current()
127 }
128
129 pub fn current(&self) -> f64 {
131 let n = self.window.len();
132 if n == 0 {
133 return 0.0;
134 }
135 let denom = (n * (n + 1)) as f64 / 2.0;
136 self.window
137 .iter()
138 .enumerate()
139 .map(|(i, &v)| v * (i + 1) as f64)
140 .sum::<f64>()
141 / denom
142 }
143
144 pub fn reset(&mut self) {
146 self.window.clear();
147 }
148}
149
150pub fn apply_sma(data: &[f64], window_size: usize) -> Vec<f64> {
152 let mut sma = new_sma(window_size);
153 data.iter().map(|&x| sma.push(x)).collect()
154}
155
156pub fn apply_ema(data: &[f64], alpha: f64) -> Vec<f64> {
158 let mut ema = new_ema(alpha);
159 data.iter().map(|&x| ema.push(x)).collect()
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_sma_constant() {
168 let mut sma = new_sma(3);
170 sma.push(5.0);
171 sma.push(5.0);
172 let v = sma.push(5.0);
173 assert!((v - 5.0).abs() < 1e-12);
174 }
175
176 #[test]
177 fn test_sma_window_full() {
178 let mut sma = new_sma(3);
180 sma.push(1.0);
181 sma.push(2.0);
182 assert!(!sma.is_full());
183 sma.push(3.0);
184 assert!(sma.is_full());
185 }
186
187 #[test]
188 fn test_sma_sliding() {
189 let mut sma = new_sma(2);
191 sma.push(10.0);
192 sma.push(20.0);
193 let v = sma.push(30.0);
194 assert!((v - 25.0).abs() < 1e-12, "v={v}");
195 }
196
197 #[test]
198 fn test_ema_first_value() {
199 let mut ema = new_ema(0.5);
201 let v = ema.push(10.0);
202 assert!((v - 10.0).abs() < 1e-12);
203 }
204
205 #[test]
206 fn test_ema_smoothing() {
207 let mut ema = new_ema(0.1);
209 for _ in 0..100 {
210 ema.push(0.0);
211 }
212 let v = ema.push(100.0);
213 assert!(v < 20.0, "v={v}");
214 }
215
216 #[test]
217 fn test_wma_current() {
218 let mut wma = new_wma(3);
220 wma.push(1.0);
221 wma.push(2.0);
222 let v = wma.push(3.0);
223 assert!((v - 14.0 / 6.0).abs() < 1e-10, "v={v}");
225 }
226
227 #[test]
228 fn test_apply_sma_length() {
229 let data: Vec<f64> = (0..10).map(|i| i as f64).collect();
231 let out = apply_sma(&data, 3);
232 assert_eq!(out.len(), 10);
233 }
234
235 #[test]
236 fn test_apply_ema_length() {
237 let data = vec![1.0f64; 5];
239 assert_eq!(apply_ema(&data, 0.3).len(), 5);
240 }
241
242 #[test]
243 fn test_sma_reset() {
244 let mut sma = new_sma(3);
246 sma.push(1.0);
247 sma.reset();
248 assert_eq!(sma.len(), 0);
249 }
250}