1use std::collections::VecDeque;
2use std::fmt::{Display, Formatter};
3use std::iter::Sum;
4use std::marker::PhantomData;
5use std::ops::{AddAssign, Div, SubAssign};
6
7pub trait SMA<T> {
11 fn push(&mut self, sample: T);
13
14 fn average(&self) -> Option<T>;
17
18 fn window_size(&self) -> usize;
20
21 fn len(&self) -> usize;
24
25 fn is_window_full(&self) -> bool {
28 self.len() == self.window_size()
29 }
30
31 fn is_empty(&self) -> bool;
33
34 fn clear(&mut self);
36}
37
38#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
45pub struct NoSumSMA<T, D = T> {
46 window: VecDeque<T>,
47 window_size: usize,
48 _div: PhantomData<D>,
49}
50
51impl<T, D> SMA<T> for NoSumSMA<T, D>
52where
53 T: for<'a> Sum<&'a T> + Div<D, Output = T>,
54 D: From<u32>,
55{
56 fn push(&mut self, sample: T) {
57 if self.is_window_full() {
58 self.window.pop_front();
59 }
60 self.window.push_back(sample);
61 }
62
63 fn average(&self) -> Option<T> {
64 if !self.is_empty() {
65 Some(self.window.iter().sum::<T>() / D::from(self.window.len() as u32))
66 } else {
67 None
68 }
69 }
70
71 fn window_size(&self) -> usize {
72 self.window_size
73 }
74
75 fn len(&self) -> usize {
76 self.window.len()
77 }
78
79 fn is_empty(&self) -> bool {
80 self.window.is_empty()
81 }
82
83 fn clear(&mut self) {
84 self.window.clear()
85 }
86}
87
88impl<T, D> NoSumSMA<T, D>
89where
90 T: for<'a> Sum<&'a T> + Div<D, Output = T>,
91 D: From<u32>,
92{
93 pub fn new(window_size: usize) -> Self {
96 assert!(window_size > 1, "window size must be greater than 1");
97 Self {
98 window: VecDeque::with_capacity(window_size),
99 window_size,
100 _div: PhantomData,
101 }
102 }
103
104 pub fn new_with_samples<I>(window_size: usize, initial_samples: I) -> Self
106 where
107 I: IntoIterator<Item = T>,
108 {
109 let mut ret = Self::new(window_size);
110 initial_samples.into_iter().for_each(|s| ret.push(s));
111 ret
112 }
113}
114
115impl<T, D> Display for NoSumSMA<T, D>
116where
117 T: for<'a> Sum<&'a T> + Div<D, Output = T> + Default + Display,
118 D: From<u32>,
119{
120 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121 write!(f, "{}", self.average().unwrap_or_default())
122 }
123}
124
125#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
132pub struct SingleSumSMA<T, D = T> {
133 window: VecDeque<T>,
134 window_size: usize,
135 sum: T,
136 _div: PhantomData<D>,
137}
138
139impl<T, D> SMA<T> for SingleSumSMA<T, D>
140where
141 T: AddAssign + SubAssign + Div<D, Output = T> + Copy,
142 D: From<u32>,
143{
144 fn push(&mut self, sample: T) {
145 self.sum += sample;
146
147 if self.is_window_full() {
148 if let Some(shifted_sample) = self.window.pop_front() {
149 self.sum -= shifted_sample;
150 }
151 }
152
153 self.window.push_back(sample);
154 }
155
156 fn average(&self) -> Option<T> {
157 if !self.is_empty() {
158 Some(self.sum / D::from(self.window.len() as u32))
159 } else {
160 None
161 }
162 }
163
164 fn window_size(&self) -> usize {
165 self.window_size
166 }
167
168 fn len(&self) -> usize {
169 self.window.len()
170 }
171
172 fn is_empty(&self) -> bool {
173 self.window.is_empty()
174 }
175
176 fn clear(&mut self) {
177 self.sum -= self.sum;
178 self.window.clear();
179 }
180}
181
182impl<T, D> SingleSumSMA<T, D>
183where
184 T: AddAssign + SubAssign + Div<D, Output = T> + Copy + Default,
185 D: From<u32>,
186{
187 pub fn new(window_size: usize) -> Self {
190 assert!(window_size > 1, "window size must be greater than 1");
191 Self {
192 window: VecDeque::with_capacity(window_size),
193 window_size,
194 sum: T::default(),
195 _div: PhantomData,
196 }
197 }
198
199 pub fn new_with_samples<I>(window_size: usize, initial_samples: I) -> Self
201 where
202 I: IntoIterator<Item = T>,
203 {
204 let mut ret = Self::new(window_size);
205 initial_samples.into_iter().for_each(|s| ret.push(s));
206 ret
207 }
208}
209
210impl<T, D> Display for SingleSumSMA<T, D>
211where
212 T: AddAssign + SubAssign + Div<D, Output = T> + Copy + Display + Default,
213 D: From<u32>,
214{
215 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216 write!(f, "{}", self.average().unwrap_or_default())
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use crate::sma::{NoSumSMA, SingleSumSMA, SMA};
223
224 fn test_sma<T: SMA<u32>>(mut sma: T, window_size: usize) {
225 assert_eq!(window_size, sma.window_size(), "invalid windows size");
226 assert!(sma.average().is_none(), "invalid empty average");
227
228 assert!(sma.is_empty(), "should be empty");
229 assert_eq!(0, sma.len(), "len is invalid");
230
231 sma.push(0);
232 sma.push(1);
233 sma.push(2);
234 sma.push(3);
235
236 assert_eq!(Some(2), sma.average(), "invalid average");
237 assert_eq!(3, sma.window_size(), "window size is invalid");
238
239 assert!(!sma.is_empty(), "should not be empty");
240 assert_eq!(3, sma.len(), "len is invalid");
241 }
242
243 #[test]
244 fn test_nosum_sma_should_calculate_avg_correctly() {
245 test_sma(NoSumSMA::<u32, u32>::new(3), 3);
246 }
247
248 #[test]
249 fn test_single_sum_sma_should_calculate_avg_correctly() {
250 test_sma(SingleSumSMA::<u32, u32>::new(3), 3);
251 }
252}