1#![warn(clippy::std_instead_of_core, clippy::std_instead_of_alloc)]
2#![no_std]
3use core::slice::Iter;
4
5mod num;
6pub use num::{Num, Float};
7
8pub struct SmoothBuffer<const CAP: usize, T: Num> {
10 data: [T; CAP],
11 head: usize,
12 sum: Option<T>,
13 max: Option<T>,
14 min: Option<T>,
15 filled_len: usize,
16}
17
18impl<const CAP: usize, T: Num> Default for SmoothBuffer<CAP, T> {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl<const CAP: usize, T: Num> SmoothBuffer<CAP, T> {
25 pub fn new() -> Self {
27 SmoothBuffer {
28 data: [T::default(); CAP],
29 head: 0,
30 sum: None,
31 max: None,
32 min: None,
33 filled_len: 0,
34 }
35 }
36
37 pub fn pre_filled(value: T) -> Self {
39 SmoothBuffer {
40 data: [value; CAP],
41 head: CAP - 1,
42 sum: Some(value * T::from_usize(CAP)),
43 max: Some(value),
44 min: Some(value),
45 filled_len: CAP,
46 }
47 }
48
49 pub fn average(&self) -> T {
51 if self.filled_len > 0 {
52 return self.sum.unwrap_or(T::zero()) / T::from_usize(self.filled_len);
53 }
54 T::zero()
55 }
56
57 pub fn clear(&mut self) {
59 for n in 0..self.data.len() {
60 self.data[n] = T::zero();
61 }
62 self.sum = None;
63 self.max = None;
64 self.min = None;
65 self.filled_len = 0;
66 self.head = 0;
67 }
68
69 pub fn is_empty(&self) -> bool {
71 self.filled_len == 0
72 }
73
74 pub fn max(&self) -> T {
76 self.max.unwrap_or(T::zero())
77 }
78
79 pub fn min(&self) -> T {
81 self.min.unwrap_or(T::zero())
82 }
83
84 pub fn capacity(&self) -> usize {
87 CAP
88 }
89
90 pub fn len(&self) -> usize {
92 self.filled_len
93 }
94
95 pub fn push(&mut self, value: T) {
97 match self.max {
98 None => self.max = Some(value),
99 Some(max) => self.max = Some(T::get_max(max, value)),
100 }
101 match self.min {
102 None => self.min = Some(value),
103 Some(min) => self.min = Some(T::get_min(min, value)),
104 }
105 match self.sum {
106 None => self.sum = Some(value),
107 Some(sum) => self.sum = Some(sum - self.data[self.head] + value),
108 }
109
110 self.data[self.head] = value;
112 self.head += 1;
113 if self.head == CAP {
114 self.head = 0
115 }
116 if self.filled_len < CAP {
117 self.filled_len += 1;
118 }
119 }
120
121 pub fn push_slice(&mut self, slice: &[T]) {
123 for item in slice {
124 self.push(*item);
125 }
126 }
127
128 pub fn iter(&self) -> Iter<T> {
130 self.data[0..self.filled_len].iter()
131 }
132}
133
134impl<const CAP: usize, T: Float> SmoothBuffer<CAP, T> {
135 pub fn gaussian_filter(&self) -> T {
138 if CAP == 0 {
139 return T::zero();
140 }
141
142 let sigma = T::from_usize(CAP) / T::four();
144 let mut weights = [T::zero(); CAP];
145
146 let mut total_weight = T::zero();
148 let center = T::from_usize(CAP - 1) / T::two();
149 for i in 0..CAP {
150 let distance = T::from_usize(i) - center;
151 let weight = T::exp(-distance * distance / (T::two() * sigma * sigma));
152 weights[i] = weight;
153 total_weight += weight;
154 }
155
156 for weight in weights.iter_mut() {
158 *weight /= total_weight;
159 }
160
161 let mut sum = T::zero();
163 self.data.iter()
164 .zip(weights.iter())
165 .for_each(|(value, weight)| sum += *value * *weight);
166 sum
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 const MARGIN: f64 = 0.000001;
174
175 #[test]
176 fn create_and_push() {
177 const CAP: usize = 10;
178 let mut buf = SmoothBuffer::<CAP, f32>::new();
179 for _ in 0..5 {
180 buf.push(10.0);
181 }
182
183 assert_eq!(buf.capacity(), CAP);
184 assert_eq!(buf.len(), 5);
185 assert_eq!(buf.average(), 10.0);
186
187 for _ in 0..10 {
188 buf.push(5.0);
189 }
190 assert_eq!(buf.len(), CAP);
191 assert_eq!(buf.average(), 5.0);
192 }
193
194 #[test]
195 fn clearing() {
196 let mut buf = SmoothBuffer::<10, f32>::new();
197 for n in 0..buf.capacity() {
198 buf.push(n as f32);
199 }
200 buf.clear();
201 assert_eq!(buf.capacity(), 10);
202 assert_eq!(buf.len(), 0);
203 assert_eq!(buf.average(), 0.0);
204 assert_eq!(buf.iter().next(), None);
205 }
206
207 #[test]
208 fn iteration() {
209 let mut buf = SmoothBuffer::<10, f64>::new();
210 let len = 7;
211 for n in 0..len {
212 buf.push(n as f64);
213 }
214
215 for (i, value) in buf.iter().enumerate() {
216 assert_eq!(i as f64, *value);
217 }
218
219 assert!(buf.iter().len() == len);
220 }
221
222 #[test]
223 fn gaussian_smoothing_simple() {
224 let mut buf = SmoothBuffer::<10, f64>::new();
225 for _ in 0..100 {
226 buf.push(3.0);
227 }
228 assert!(buf.gaussian_filter() - 3.0 < MARGIN);
231 }
232
233 #[test]
234 fn gaussian_smoothing_with_negative_values() {
235 let mut buf = SmoothBuffer::<10, f64>::new();
236 let mid = buf.len() / 2;
237 for v in 0..buf.len() {
238 buf.push(if v < mid {
239 1.0
240 } else if v > mid {
241 -1.0
242 } else {
243 0.0
244 });
245 }
246 assert!(buf.gaussian_filter().abs() < MARGIN);
248 }
249
250 #[test]
251 fn gaussian_smoothing_slice() {
252 let mut buf = SmoothBuffer::<10, f64>::new();
253 buf.push_slice(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]);
254 assert!(buf.gaussian_filter() - 0.55 < MARGIN);
255 }
256
257 #[test]
258 fn pre_filled_buffer() {
259 fn test_value(x:f64){
260 let buf = SmoothBuffer::<10, f64>::pre_filled(x);
262 assert!(buf.len() == 10);
263 assert!((buf.gaussian_filter() - x).abs() < MARGIN);
264 assert!((buf.average() - x).abs() < MARGIN);
265 }
266
267 for n in 0 ..= 10 {
268 test_value(n as f64 / 10.0);
269 }
270 }
271
272 #[test]
273 fn progressive_fill() {
274 let mut buf = SmoothBuffer::<10, f64>::pre_filled(0.0);
275 assert!(buf.gaussian_filter() < MARGIN);
277 for _n in 0..10 {
278 buf.push(1.0);
279 }
281 assert!(buf.gaussian_filter() - 1.0 < MARGIN);
282 }
283}