1use crate::error::{SpotError, SpotResult};
8
9#[derive(Debug, Clone)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct Ubend {
18 cursor: usize,
20 capacity: usize,
22 #[cfg_attr(feature = "serde", serde(with = "crate::ser::nan_safe_f64"))]
24 last_erased_data: f64,
25 filled: bool,
27 data: Vec<f64>,
29}
30
31impl Ubend {
32 pub fn new(capacity: usize) -> SpotResult<Self> {
34 if capacity == 0 {
35 return Err(SpotError::MemoryAllocationFailed);
36 }
37
38 Ok(Self {
39 cursor: 0,
40 filled: false,
41 capacity,
42 last_erased_data: f64::NAN,
43 data: vec![0.0; capacity],
44 })
45 }
46
47 pub fn size(&self) -> usize {
50 if self.filled {
51 self.capacity
52 } else {
53 self.cursor
54 }
55 }
56
57 pub fn push(&mut self, x: f64) -> f64 {
60 if self.filled {
63 self.last_erased_data = self.data[self.cursor];
64 }
65
66 self.data[self.cursor] = x;
68
69 if self.cursor == self.capacity - 1 {
71 self.cursor = 0;
72 self.filled = true;
73 } else {
74 self.cursor += 1;
75 }
76
77 self.last_erased_data
78 }
79
80 pub fn iter(&self) -> UbendIterator<'_> {
82 UbendIterator {
83 ubend: self,
84 index: 0,
85 }
86 }
87
88 pub fn get(&self, index: usize) -> Option<f64> {
90 let size = self.size();
91 if index >= size {
92 return None;
93 }
94
95 if !self.filled {
96 Some(self.data[index])
98 } else {
99 let real_index = (self.cursor + index) % self.capacity;
101 Some(self.data[real_index])
102 }
103 }
104
105 pub fn raw_data(&self) -> &[f64] {
107 &self.data
108 }
109
110 pub fn capacity(&self) -> usize {
112 self.capacity
113 }
114
115 pub fn is_filled(&self) -> bool {
117 self.filled
118 }
119
120 pub fn cursor(&self) -> usize {
122 self.cursor
123 }
124
125 pub fn last_erased_data(&self) -> f64 {
127 self.last_erased_data
128 }
129
130 pub fn data(&self) -> Vec<f64> {
132 self.iter().collect()
133 }
134}
135
136pub struct UbendIterator<'a> {
138 ubend: &'a Ubend,
139 index: usize,
140}
141
142impl<'a> Iterator for UbendIterator<'a> {
143 type Item = f64;
144
145 fn next(&mut self) -> Option<Self::Item> {
146 let result = self.ubend.get(self.index);
147 self.index += 1;
148 result
149 }
150}
151
152impl<'a> ExactSizeIterator for UbendIterator<'a> {
153 fn len(&self) -> usize {
154 self.ubend.size()
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use approx::assert_relative_eq;
162
163 #[test]
164 fn test_ubend_creation() {
165 let ubend = Ubend::new(5).unwrap();
166 assert_eq!(ubend.capacity(), 5);
167 assert_eq!(ubend.size(), 0);
168 assert!(!ubend.is_filled());
169 assert_eq!(ubend.cursor(), 0);
170 assert!(ubend.last_erased_data().is_nan());
171 }
172
173 #[test]
174 fn test_ubend_zero_capacity() {
175 let result = Ubend::new(0);
176 assert!(result.is_err());
177 assert_eq!(result.unwrap_err(), SpotError::MemoryAllocationFailed);
178 }
179
180 #[test]
181 fn test_ubend_push_before_full() {
182 let mut ubend = Ubend::new(3).unwrap();
183
184 let erased = ubend.push(1.0);
186 assert!(erased.is_nan());
187 assert_eq!(ubend.size(), 1);
188 assert!(!ubend.is_filled());
189 assert_eq!(ubend.cursor(), 1);
190
191 let erased = ubend.push(2.0);
193 assert!(erased.is_nan());
194 assert_eq!(ubend.size(), 2);
195 assert!(!ubend.is_filled());
196 assert_eq!(ubend.cursor(), 2);
197
198 let erased = ubend.push(3.0);
200 assert!(erased.is_nan());
201 assert_eq!(ubend.size(), 3);
202 assert!(ubend.is_filled());
203 assert_eq!(ubend.cursor(), 0);
204 }
205
206 #[test]
207 fn test_ubend_push_after_full() {
208 let mut ubend = Ubend::new(3).unwrap();
209
210 ubend.push(1.0);
212 ubend.push(2.0);
213 ubend.push(3.0);
214
215 let erased = ubend.push(4.0);
217 assert_relative_eq!(erased, 1.0);
218 assert_eq!(ubend.size(), 3);
219 assert!(ubend.is_filled());
220 assert_eq!(ubend.cursor(), 1);
221
222 let erased = ubend.push(5.0);
223 assert_relative_eq!(erased, 2.0);
224 assert_eq!(ubend.size(), 3);
225 assert!(ubend.is_filled());
226 assert_eq!(ubend.cursor(), 2);
227 }
228
229 #[test]
230 fn test_ubend_get() {
231 let mut ubend = Ubend::new(3).unwrap();
232
233 assert!(ubend.get(0).is_none());
235
236 ubend.push(10.0);
238 ubend.push(20.0);
239
240 assert_relative_eq!(ubend.get(0).unwrap(), 10.0);
241 assert_relative_eq!(ubend.get(1).unwrap(), 20.0);
242 assert!(ubend.get(2).is_none());
243
244 ubend.push(30.0);
246 ubend.push(40.0); assert_relative_eq!(ubend.get(0).unwrap(), 20.0);
249 assert_relative_eq!(ubend.get(1).unwrap(), 30.0);
250 assert_relative_eq!(ubend.get(2).unwrap(), 40.0);
251 }
252
253 #[test]
254 fn test_ubend_iterator() {
255 let mut ubend = Ubend::new(3).unwrap();
256
257 ubend.push(1.0);
258 ubend.push(2.0);
259 ubend.push(3.0);
260
261 let values: Vec<f64> = ubend.iter().collect();
262 assert_eq!(values, vec![1.0, 2.0, 3.0]);
263
264 ubend.push(4.0);
266 let values: Vec<f64> = ubend.iter().collect();
267 assert_eq!(values, vec![2.0, 3.0, 4.0]);
268 }
269
270 #[test]
271 fn test_ubend_exact_size_iterator() {
272 let mut ubend = Ubend::new(3).unwrap();
273
274 assert_eq!(ubend.iter().len(), 0);
275
276 ubend.push(1.0);
277 assert_eq!(ubend.iter().len(), 1);
278
279 ubend.push(2.0);
280 ubend.push(3.0);
281 assert_eq!(ubend.iter().len(), 3);
282
283 ubend.push(4.0);
284 assert_eq!(ubend.iter().len(), 3);
285 }
286}