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(crate) fn reset(&mut self) {
63 self.cursor = 0;
64 self.filled = false;
65 self.last_erased_data = f64::NAN;
66 }
67
68 pub fn push(&mut self, x: f64) -> f64 {
71 if self.filled {
74 self.last_erased_data = self.data[self.cursor];
75 }
76
77 self.data[self.cursor] = x;
79
80 if self.cursor == self.capacity - 1 {
82 self.cursor = 0;
83 self.filled = true;
84 } else {
85 self.cursor += 1;
86 }
87
88 self.last_erased_data
89 }
90
91 pub fn iter(&self) -> UbendIterator<'_> {
93 UbendIterator {
94 ubend: self,
95 index: 0,
96 }
97 }
98
99 pub fn get(&self, index: usize) -> Option<f64> {
101 let size = self.size();
102 if index >= size {
103 return None;
104 }
105
106 if !self.filled {
107 Some(self.data[index])
109 } else {
110 let real_index = (self.cursor + index) % self.capacity;
112 Some(self.data[real_index])
113 }
114 }
115
116 pub fn raw_data(&self) -> &[f64] {
118 &self.data
119 }
120
121 pub fn capacity(&self) -> usize {
123 self.capacity
124 }
125
126 pub fn is_filled(&self) -> bool {
128 self.filled
129 }
130
131 pub fn cursor(&self) -> usize {
133 self.cursor
134 }
135
136 pub fn last_erased_data(&self) -> f64 {
138 self.last_erased_data
139 }
140
141 pub fn data(&self) -> Vec<f64> {
143 self.iter().collect()
144 }
145}
146
147pub struct UbendIterator<'a> {
149 ubend: &'a Ubend,
150 index: usize,
151}
152
153impl<'a> Iterator for UbendIterator<'a> {
154 type Item = f64;
155
156 fn next(&mut self) -> Option<Self::Item> {
157 let result = self.ubend.get(self.index);
158 self.index += 1;
159 result
160 }
161}
162
163impl<'a> ExactSizeIterator for UbendIterator<'a> {
164 fn len(&self) -> usize {
165 self.ubend.size()
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use approx::assert_relative_eq;
173
174 #[test]
175 fn test_ubend_reset_clears_state_and_preserves_capacity() {
176 let mut ub = Ubend::new(3).unwrap();
177 let _ = ub.push(1.0);
179 let _ = ub.push(2.0);
180 let _ = ub.push(3.0);
181 let erased = ub.push(4.0);
182 assert_relative_eq!(erased, 1.0);
183 assert_eq!(ub.size(), 3);
184
185 ub.reset();
186
187 assert_eq!(ub.size(), 0);
189 assert_eq!(ub.capacity, 3);
190 assert_eq!(ub.cursor, 0);
191 assert!(!ub.filled);
192 assert!(ub.last_erased_data.is_nan());
193 assert_eq!(ub.data.len(), 3); let erased_after_reset = ub.push(10.0);
198 assert!(erased_after_reset.is_nan());
199 assert_eq!(ub.size(), 1);
200 }
201
202 #[test]
203 fn test_ubend_reset_is_idempotent() {
204 let mut ub = Ubend::new(2).unwrap();
205 ub.reset();
206 ub.reset();
207 assert_eq!(ub.size(), 0);
208 assert!(ub.last_erased_data.is_nan());
209 }
210
211 #[test]
212 fn test_ubend_creation() {
213 let ubend = Ubend::new(5).unwrap();
214 assert_eq!(ubend.capacity(), 5);
215 assert_eq!(ubend.size(), 0);
216 assert!(!ubend.is_filled());
217 assert_eq!(ubend.cursor(), 0);
218 assert!(ubend.last_erased_data().is_nan());
219 }
220
221 #[test]
222 fn test_ubend_zero_capacity() {
223 let result = Ubend::new(0);
224 assert!(result.is_err());
225 assert_eq!(result.unwrap_err(), SpotError::MemoryAllocationFailed);
226 }
227
228 #[test]
229 fn test_ubend_push_before_full() {
230 let mut ubend = Ubend::new(3).unwrap();
231
232 let erased = ubend.push(1.0);
234 assert!(erased.is_nan());
235 assert_eq!(ubend.size(), 1);
236 assert!(!ubend.is_filled());
237 assert_eq!(ubend.cursor(), 1);
238
239 let erased = ubend.push(2.0);
241 assert!(erased.is_nan());
242 assert_eq!(ubend.size(), 2);
243 assert!(!ubend.is_filled());
244 assert_eq!(ubend.cursor(), 2);
245
246 let erased = ubend.push(3.0);
248 assert!(erased.is_nan());
249 assert_eq!(ubend.size(), 3);
250 assert!(ubend.is_filled());
251 assert_eq!(ubend.cursor(), 0);
252 }
253
254 #[test]
255 fn test_ubend_push_after_full() {
256 let mut ubend = Ubend::new(3).unwrap();
257
258 ubend.push(1.0);
260 ubend.push(2.0);
261 ubend.push(3.0);
262
263 let erased = ubend.push(4.0);
265 assert_relative_eq!(erased, 1.0);
266 assert_eq!(ubend.size(), 3);
267 assert!(ubend.is_filled());
268 assert_eq!(ubend.cursor(), 1);
269
270 let erased = ubend.push(5.0);
271 assert_relative_eq!(erased, 2.0);
272 assert_eq!(ubend.size(), 3);
273 assert!(ubend.is_filled());
274 assert_eq!(ubend.cursor(), 2);
275 }
276
277 #[test]
278 fn test_ubend_get() {
279 let mut ubend = Ubend::new(3).unwrap();
280
281 assert!(ubend.get(0).is_none());
283
284 ubend.push(10.0);
286 ubend.push(20.0);
287
288 assert_relative_eq!(ubend.get(0).unwrap(), 10.0);
289 assert_relative_eq!(ubend.get(1).unwrap(), 20.0);
290 assert!(ubend.get(2).is_none());
291
292 ubend.push(30.0);
294 ubend.push(40.0); assert_relative_eq!(ubend.get(0).unwrap(), 20.0);
297 assert_relative_eq!(ubend.get(1).unwrap(), 30.0);
298 assert_relative_eq!(ubend.get(2).unwrap(), 40.0);
299 }
300
301 #[test]
302 fn test_ubend_iterator() {
303 let mut ubend = Ubend::new(3).unwrap();
304
305 ubend.push(1.0);
306 ubend.push(2.0);
307 ubend.push(3.0);
308
309 let values: Vec<f64> = ubend.iter().collect();
310 assert_eq!(values, vec![1.0, 2.0, 3.0]);
311
312 ubend.push(4.0);
314 let values: Vec<f64> = ubend.iter().collect();
315 assert_eq!(values, vec![2.0, 3.0, 4.0]);
316 }
317
318 #[test]
319 fn test_ubend_exact_size_iterator() {
320 let mut ubend = Ubend::new(3).unwrap();
321
322 assert_eq!(ubend.iter().len(), 0);
323
324 ubend.push(1.0);
325 assert_eq!(ubend.iter().len(), 1);
326
327 ubend.push(2.0);
328 ubend.push(3.0);
329 assert_eq!(ubend.iter().len(), 3);
330
331 ubend.push(4.0);
332 assert_eq!(ubend.iter().len(), 3);
333 }
334}