oxihuman_core/
sliding_window.rs1#![allow(dead_code)]
4
5use std::collections::VecDeque;
8
9#[allow(dead_code)]
11pub struct SlidingWindow {
12 capacity: usize,
13 samples: VecDeque<f32>,
14 sum: f32,
15 min_val: f32,
16 max_val: f32,
17}
18
19#[allow(dead_code)]
20impl SlidingWindow {
21 pub fn new(capacity: usize) -> Self {
22 let cap = capacity.max(1);
23 Self {
24 capacity: cap,
25 samples: VecDeque::with_capacity(cap),
26 sum: 0.0,
27 min_val: f32::INFINITY,
28 max_val: f32::NEG_INFINITY,
29 }
30 }
31
32 pub fn push(&mut self, value: f32) {
34 if self.samples.len() == self.capacity {
35 self.samples.pop_front();
36 self.sum = self.samples.iter().sum();
38 self.min_val = self.samples.iter().cloned().fold(f32::INFINITY, f32::min);
39 self.max_val = self
40 .samples
41 .iter()
42 .cloned()
43 .fold(f32::NEG_INFINITY, f32::max);
44 }
45 self.samples.push_back(value);
46 self.sum += value;
47 self.min_val = self.min_val.min(value);
48 self.max_val = self.max_val.max(value);
49 }
50
51 pub fn mean(&self) -> f32 {
52 if self.samples.is_empty() {
53 0.0
54 } else {
55 self.sum / self.samples.len() as f32
56 }
57 }
58
59 pub fn sum(&self) -> f32 {
60 self.sum
61 }
62
63 pub fn min(&self) -> Option<f32> {
64 if self.samples.is_empty() {
65 None
66 } else {
67 Some(self.min_val)
68 }
69 }
70
71 pub fn max(&self) -> Option<f32> {
72 if self.samples.is_empty() {
73 None
74 } else {
75 Some(self.max_val)
76 }
77 }
78
79 pub fn len(&self) -> usize {
80 self.samples.len()
81 }
82
83 pub fn capacity(&self) -> usize {
84 self.capacity
85 }
86
87 pub fn is_empty(&self) -> bool {
88 self.samples.is_empty()
89 }
90
91 pub fn is_full(&self) -> bool {
92 self.samples.len() == self.capacity
93 }
94
95 pub fn as_slice(&self) -> Vec<f32> {
96 self.samples.iter().cloned().collect()
97 }
98
99 pub fn clear(&mut self) {
100 self.samples.clear();
101 self.sum = 0.0;
102 self.min_val = f32::INFINITY;
103 self.max_val = f32::NEG_INFINITY;
104 }
105
106 pub fn variance(&self) -> f32 {
108 if self.samples.len() < 2 {
109 return 0.0;
110 }
111 let m = self.mean();
112 self.samples.iter().map(|&x| (x - m) * (x - m)).sum::<f32>() / self.samples.len() as f32
113 }
114}
115
116impl Default for SlidingWindow {
117 fn default() -> Self {
118 Self::new(16)
119 }
120}
121
122pub fn new_sliding_window(capacity: usize) -> SlidingWindow {
123 SlidingWindow::new(capacity)
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn basic_push_and_mean() {
132 let mut w = new_sliding_window(4);
133 w.push(1.0);
134 w.push(2.0);
135 w.push(3.0);
136 w.push(4.0);
137 assert!((w.mean() - 2.5).abs() < 1e-5);
138 }
139
140 #[test]
141 fn drops_oldest_when_full() {
142 let mut w = new_sliding_window(3);
143 w.push(1.0);
144 w.push(2.0);
145 w.push(3.0);
146 w.push(4.0); assert_eq!(w.len(), 3);
148 assert!((w.sum() - 9.0).abs() < 1e-5);
149 }
150
151 #[test]
152 fn min_max() {
153 let mut w = new_sliding_window(5);
154 w.push(3.0);
155 w.push(1.0);
156 w.push(5.0);
157 assert_eq!(w.min(), Some(1.0));
158 assert_eq!(w.max(), Some(5.0));
159 }
160
161 #[test]
162 fn empty_window() {
163 let w = new_sliding_window(4);
164 assert!(w.is_empty());
165 assert_eq!(w.min(), None);
166 assert_eq!(w.max(), None);
167 assert!((w.mean()).abs() < 1e-6);
168 }
169
170 #[test]
171 fn is_full() {
172 let mut w = new_sliding_window(2);
173 w.push(1.0);
174 assert!(!w.is_full());
175 w.push(2.0);
176 assert!(w.is_full());
177 }
178
179 #[test]
180 fn clear() {
181 let mut w = new_sliding_window(4);
182 w.push(5.0);
183 w.clear();
184 assert!(w.is_empty());
185 assert!((w.sum()).abs() < 1e-6);
186 }
187
188 #[test]
189 fn variance_uniform() {
190 let mut w = new_sliding_window(4);
191 for _ in 0..4 {
192 w.push(2.0);
193 }
194 assert!(w.variance().abs() < 1e-5);
195 }
196
197 #[test]
198 fn capacity_constant() {
199 let w = new_sliding_window(8);
200 assert_eq!(w.capacity(), 8);
201 }
202
203 #[test]
204 fn as_slice_ordered() {
205 let mut w = new_sliding_window(3);
206 w.push(10.0);
207 w.push(20.0);
208 w.push(30.0);
209 assert_eq!(w.as_slice(), vec![10.0, 20.0, 30.0]);
210 }
211
212 #[test]
213 fn single_element_variance_zero() {
214 let mut w = new_sliding_window(5);
215 w.push(7.0);
216 assert!(w.variance().abs() < 1e-6);
217 }
218}