1use crate::data::bounds::DataBounds;
4use crate::data::point::DataPoint;
5use crate::error::{DataError, DataResult};
6use heapless::Vec;
7
8pub struct StaticDataSeriesIter<T, const N: usize> {
10 data: heapless::Vec<T, N>,
11 index: usize,
12}
13
14impl<T: Clone, const N: usize> Iterator for StaticDataSeriesIter<T, N> {
15 type Item = T;
16
17 fn next(&mut self) -> Option<Self::Item> {
18 if self.index < self.data.len() {
19 let item = self.data.get(self.index)?.clone();
20 self.index += 1;
21 Some(item)
22 } else {
23 None
24 }
25 }
26
27 fn size_hint(&self) -> (usize, Option<usize>) {
28 let remaining = self.data.len() - self.index;
29 (remaining, Some(remaining))
30 }
31}
32
33impl<T: Clone, const N: usize> ExactSizeIterator for StaticDataSeriesIter<T, N> {}
34
35pub struct StaticDataSeriesRefIter<'a, T> {
37 data: &'a [T],
38 index: usize,
39}
40
41impl<'a, T> Iterator for StaticDataSeriesRefIter<'a, T> {
42 type Item = &'a T;
43
44 fn next(&mut self) -> Option<Self::Item> {
45 if self.index < self.data.len() {
46 let item = &self.data[self.index];
47 self.index += 1;
48 Some(item)
49 } else {
50 None
51 }
52 }
53
54 fn size_hint(&self) -> (usize, Option<usize>) {
55 let remaining = self.data.len() - self.index;
56 (remaining, Some(remaining))
57 }
58}
59
60impl<'a, T> ExactSizeIterator for StaticDataSeriesRefIter<'a, T> {}
61
62pub trait DataSeries {
64 type Item: DataPoint;
66 type Iter: Iterator<Item = Self::Item>;
68
69 fn iter(&self) -> Self::Iter;
71
72 fn len(&self) -> usize;
74
75 fn is_empty(&self) -> bool {
77 self.len() == 0
78 }
79
80 fn calculate_bounds(&self) -> DataResult<()> {
82 Ok(())
85 }
86
87 fn get(&self, index: usize) -> Option<Self::Item>;
89}
90
91#[derive(Debug, Clone)]
93pub struct StaticDataSeries<T, const N: usize>
94where
95 T: DataPoint,
96{
97 data: Vec<T, N>,
98 label: Option<heapless::String<32>>,
99}
100
101impl<T, const N: usize> StaticDataSeries<T, N>
102where
103 T: DataPoint,
104{
105 pub fn new() -> Self {
107 Self {
108 data: Vec::new(),
109 label: None,
110 }
111 }
112
113 pub fn with_label(label: &str) -> Self {
115 let mut series = Self::new();
116 series.set_label(label);
117 series
118 }
119
120 pub fn set_label(&mut self, label: &str) {
122 let mut string = heapless::String::new();
123 if string.push_str(label).is_ok() {
124 self.label = Some(string);
125 }
126 }
127
128 pub fn label(&self) -> Option<&str> {
130 self.label.as_ref().map(|s| s.as_str())
131 }
132
133 pub fn push(&mut self, point: T) -> DataResult<()> {
135 self.data
136 .push(point)
137 .map_err(|_| DataError::buffer_full("push data point", N))
138 }
139
140 pub fn extend<I>(&mut self, points: I) -> DataResult<()>
142 where
143 I: IntoIterator<Item = T>,
144 {
145 for point in points {
146 self.push(point)?;
147 }
148 Ok(())
149 }
150
151 pub fn from_tuples(tuples: &[(T::X, T::Y)]) -> DataResult<Self>
153 where
154 T: DataPoint,
155 {
156 let mut series = Self::new();
157 for &(x, y) in tuples {
158 series.push(T::new(x, y))?;
159 }
160 Ok(series)
161 }
162
163 pub fn clear(&mut self) {
165 self.data.clear();
166 }
167
168 pub fn capacity(&self) -> usize {
170 N
171 }
172
173 pub fn remaining_capacity(&self) -> usize {
175 N - self.data.len()
176 }
177
178 pub fn is_full(&self) -> bool {
180 self.data.len() == N
181 }
182
183 pub fn as_slice(&self) -> &[T] {
185 &self.data
186 }
187
188 pub fn sort_by_x(&mut self)
190 where
191 T::X: Ord,
192 T: Clone,
193 {
194 if self.data.len() <= 16 {
196 self.insertion_sort_by_x();
197 } else {
198 self.merge_sort_by_x();
199 }
200 }
201
202 fn insertion_sort_by_x(&mut self)
204 where
205 T::X: Ord,
206 T: Clone,
207 {
208 for i in 1..self.data.len() {
209 let key = self.data[i];
210 let mut j = i;
211
212 while j > 0 && self.data[j - 1].x() > key.x() {
213 self.data[j] = self.data[j - 1];
214 j -= 1;
215 }
216 self.data[j] = key;
217 }
218 }
219
220 fn merge_sort_by_x(&mut self)
222 where
223 T::X: Ord,
224 T: Clone,
225 {
226 let len = self.data.len();
227 if len <= 1 {
228 return;
229 }
230
231 let mut temp = heapless::Vec::<T, N>::new();
233 for _ in 0..len {
234 if temp.push(self.data[0]).is_err() {
235 self.insertion_sort_by_x();
237 return;
238 }
239 }
240
241 self.merge_sort_recursive(0, len, &mut temp);
242 }
243
244 fn merge_sort_recursive(&mut self, start: usize, end: usize, temp: &mut heapless::Vec<T, N>)
246 where
247 T::X: Ord,
248 T: Clone,
249 {
250 if end - start <= 1 {
251 return;
252 }
253
254 let mid = start + (end - start) / 2;
255 self.merge_sort_recursive(start, mid, temp);
256 self.merge_sort_recursive(mid, end, temp);
257
258 let mut i = start;
260 let mut j = mid;
261 let mut k = start;
262
263 for idx in start..end {
265 temp[idx] = self.data[idx];
266 }
267
268 while i < mid && j < end {
269 if temp[i].x() <= temp[j].x() {
270 self.data[k] = temp[i];
271 i += 1;
272 } else {
273 self.data[k] = temp[j];
274 j += 1;
275 }
276 k += 1;
277 }
278
279 while i < mid {
281 self.data[k] = temp[i];
282 i += 1;
283 k += 1;
284 }
285
286 while j < end {
287 self.data[k] = temp[j];
288 j += 1;
289 k += 1;
290 }
291 }
292
293 pub fn sort_by_y(&mut self)
295 where
296 T::Y: Ord,
297 T: Clone,
298 {
299 if self.data.len() <= 16 {
301 self.insertion_sort_by_y();
302 } else {
303 self.merge_sort_by_y();
304 }
305 }
306
307 fn insertion_sort_by_y(&mut self)
309 where
310 T::Y: Ord,
311 T: Clone,
312 {
313 for i in 1..self.data.len() {
314 let key = self.data[i];
315 let mut j = i;
316
317 while j > 0 && self.data[j - 1].y() > key.y() {
318 self.data[j] = self.data[j - 1];
319 j -= 1;
320 }
321 self.data[j] = key;
322 }
323 }
324
325 fn merge_sort_by_y(&mut self)
327 where
328 T::Y: Ord,
329 T: Clone,
330 {
331 let len = self.data.len();
332 if len <= 1 {
333 return;
334 }
335
336 let mut temp = heapless::Vec::<T, N>::new();
338 for _ in 0..len {
339 if temp.push(self.data[0]).is_err() {
340 self.insertion_sort_by_y();
342 return;
343 }
344 }
345
346 self.merge_sort_by_y_recursive(0, len, &mut temp);
347 }
348
349 fn merge_sort_by_y_recursive(
351 &mut self,
352 start: usize,
353 end: usize,
354 temp: &mut heapless::Vec<T, N>,
355 ) where
356 T::Y: Ord,
357 T: Clone,
358 {
359 if end - start <= 1 {
360 return;
361 }
362
363 let mid = start + (end - start) / 2;
364 self.merge_sort_by_y_recursive(start, mid, temp);
365 self.merge_sort_by_y_recursive(mid, end, temp);
366
367 let mut i = start;
369 let mut j = mid;
370 let mut k = start;
371
372 for idx in start..end {
374 temp[idx] = self.data[idx];
375 }
376
377 while i < mid && j < end {
378 if temp[i].y() <= temp[j].y() {
379 self.data[k] = temp[i];
380 i += 1;
381 } else {
382 self.data[k] = temp[j];
383 j += 1;
384 }
385 k += 1;
386 }
387
388 while i < mid {
390 self.data[k] = temp[i];
391 i += 1;
392 k += 1;
393 }
394
395 while j < end {
396 self.data[k] = temp[j];
397 j += 1;
398 k += 1;
399 }
400 }
401}
402
403impl<T, const N: usize> Default for StaticDataSeries<T, N>
404where
405 T: DataPoint,
406{
407 fn default() -> Self {
408 Self::new()
409 }
410}
411
412impl<T, const N: usize> StaticDataSeries<T, N>
413where
414 T: DataPoint + Clone,
415{
416 pub fn iter_ref(&self) -> StaticDataSeriesRefIter<'_, T> {
418 StaticDataSeriesRefIter {
419 data: self.data.as_slice(),
420 index: 0,
421 }
422 }
423
424 pub fn data(&self) -> &[T] {
426 self.data.as_slice()
427 }
428}
429
430impl<T, const N: usize> DataSeries for StaticDataSeries<T, N>
431where
432 T: DataPoint + Clone,
433{
434 type Item = T;
435 type Iter = StaticDataSeriesIter<T, N>;
436
437 fn iter(&self) -> Self::Iter {
438 StaticDataSeriesIter {
442 data: self.data.clone(),
443 index: 0,
444 }
445 }
446
447 fn len(&self) -> usize {
448 self.data.len()
449 }
450
451 fn get(&self, index: usize) -> Option<Self::Item> {
452 self.data.get(index).copied()
453 }
454}
455
456impl<T, const N: usize> StaticDataSeries<T, N>
457where
458 T: DataPoint + Clone,
459 T::X: PartialOrd + Copy,
460 T::Y: PartialOrd + Copy,
461{
462 pub fn bounds(&self) -> DataResult<crate::data::bounds::DataBounds<T::X, T::Y>> {
464 use crate::data::bounds::calculate_bounds;
465 calculate_bounds(self.iter())
466 }
467}
468
469#[derive(Debug, Clone)]
471pub struct MultiSeries<T, const SERIES: usize, const POINTS: usize>
472where
473 T: DataPoint,
474{
475 series: Vec<StaticDataSeries<T, POINTS>, SERIES>,
476}
477
478impl<T, const SERIES: usize, const POINTS: usize> MultiSeries<T, SERIES, POINTS>
479where
480 T: DataPoint,
481{
482 pub fn new() -> Self {
484 Self { series: Vec::new() }
485 }
486
487 pub fn add_series(&mut self, series: StaticDataSeries<T, POINTS>) -> DataResult<usize> {
489 let index = self.series.len();
490 self.series
491 .push(series)
492 .map_err(|_| DataError::buffer_full("add data series", SERIES))?;
493 Ok(index)
494 }
495
496 pub fn get_series(&self, index: usize) -> Option<&StaticDataSeries<T, POINTS>> {
498 self.series.get(index)
499 }
500
501 pub fn get_series_mut(&mut self, index: usize) -> Option<&mut StaticDataSeries<T, POINTS>> {
503 self.series.get_mut(index)
504 }
505
506 pub fn series_count(&self) -> usize {
508 self.series.len()
509 }
510
511 pub fn is_empty(&self) -> bool {
513 self.series.is_empty()
514 }
515
516 pub fn iter_series(&self) -> core::slice::Iter<StaticDataSeries<T, POINTS>> {
518 self.series.iter()
519 }
520
521 pub fn combined_bounds(&self) -> DataResult<DataBounds<T::X, T::Y>>
523 where
524 T: DataPoint + Clone,
525 T::X: PartialOrd + Copy,
526 T::Y: PartialOrd + Copy,
527 {
528 if self.series.is_empty() {
529 return Err(DataError::insufficient_data(
530 "calculate combined bounds",
531 1,
532 0,
533 ));
534 }
535
536 let mut combined_bounds = self.series[0].bounds()?;
537
538 for series in self.series.iter().skip(1) {
539 let series_bounds = series.bounds()?;
540 combined_bounds = combined_bounds.merge(&series_bounds);
541 }
542
543 Ok(combined_bounds)
544 }
545
546 pub fn clear(&mut self) {
548 self.series.clear();
549 }
550}
551
552impl<T, const SERIES: usize, const POINTS: usize> Default for MultiSeries<T, SERIES, POINTS>
553where
554 T: DataPoint,
555{
556 fn default() -> Self {
557 Self::new()
558 }
559}
560
561#[cfg(feature = "animations")]
563#[derive(Debug, Clone)]
564pub struct SlidingWindowSeries<T, const N: usize>
565where
566 T: DataPoint + Copy,
567{
568 buffer: [Option<T>; N],
569 head: usize,
570 count: usize,
571 full: bool,
572 label: Option<heapless::String<32>>,
573}
574
575#[cfg(feature = "animations")]
576impl<T, const N: usize> SlidingWindowSeries<T, N>
577where
578 T: DataPoint + Copy,
579{
580 pub fn new() -> Self {
582 Self {
583 buffer: [None; N],
584 head: 0,
585 count: 0,
586 full: false,
587 label: None,
588 }
589 }
590
591 pub fn with_label(label: &str) -> Self {
593 let mut series = Self::new();
594 series.set_label(label);
595 series
596 }
597
598 pub fn set_label(&mut self, label: &str) {
600 let mut string = heapless::String::new();
601 if string.push_str(label).is_ok() {
602 self.label = Some(string);
603 }
604 }
605
606 pub fn label(&self) -> Option<&str> {
608 self.label.as_ref().map(|s| s.as_str())
609 }
610
611 pub fn push(&mut self, point: T) {
613 self.buffer[self.head] = Some(point);
614 self.head = (self.head + 1) % N;
615
616 if self.full {
617 } else {
619 self.count += 1;
620 if self.count == N {
621 self.full = true;
622 }
623 }
624 }
625
626 pub fn current_len(&self) -> usize {
628 self.count
629 }
630
631 pub fn is_full(&self) -> bool {
633 self.full
634 }
635
636 pub fn capacity(&self) -> usize {
638 N
639 }
640
641 pub fn clear(&mut self) {
643 self.buffer = [None; N];
644 self.head = 0;
645 self.count = 0;
646 self.full = false;
647 }
648
649 pub fn iter_chronological(&self) -> impl Iterator<Item = T> + '_ {
651 let start_idx = if self.full { self.head } else { 0 };
652 let len = if self.full { N } else { self.count };
653
654 (0..len).filter_map(move |i| {
655 let idx = (start_idx + i) % N;
656 self.buffer[idx]
657 })
658 }
659}
660
661#[cfg(feature = "animations")]
662impl<T, const N: usize> Default for SlidingWindowSeries<T, N>
663where
664 T: DataPoint + Copy,
665{
666 fn default() -> Self {
667 Self::new()
668 }
669}
670
671#[cfg(feature = "animations")]
672impl<T, const N: usize> DataSeries for SlidingWindowSeries<T, N>
673where
674 T: DataPoint + Copy,
675{
676 type Item = T;
677 type Iter = <heapless::Vec<T, N> as IntoIterator>::IntoIter;
678
679 fn iter(&self) -> Self::Iter {
680 let mut vec = heapless::Vec::new();
681 for point in self.iter_chronological() {
682 let _ = vec.push(point);
683 }
684 vec.into_iter()
685 }
686
687 fn len(&self) -> usize {
688 self.current_len()
689 }
690
691 fn get(&self, index: usize) -> Option<Self::Item> {
692 if index >= self.current_len() {
693 return None;
694 }
695
696 let start_idx = if self.full { self.head } else { 0 };
697 let actual_idx = (start_idx + index) % N;
698 self.buffer[actual_idx]
699 }
700}
701
702#[cfg(test)]
703mod tests {
704 use super::*;
705 use crate::data::point::Point2D;
706
707 #[test]
708 fn test_static_series_creation() {
709 let series: StaticDataSeries<Point2D, 10> = StaticDataSeries::new();
710 assert_eq!(series.len(), 0);
711 assert!(series.is_empty());
712 assert_eq!(series.capacity(), 10);
713 }
714
715 #[test]
716 fn test_static_series_push() {
717 let mut series: StaticDataSeries<Point2D, 10> = StaticDataSeries::new();
718 let point = Point2D::new(1.0, 2.0);
719
720 series.push(point).unwrap();
721 assert_eq!(series.len(), 1);
722 assert_eq!(series.get(0), Some(point));
723 }
724
725 #[test]
726 fn test_static_series_from_tuples() {
727 let tuples = [(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)];
728 let series: StaticDataSeries<Point2D, 10> = StaticDataSeries::from_tuples(&tuples).unwrap();
729
730 assert_eq!(series.len(), 3);
731 assert_eq!(series.get(0), Some(Point2D::new(1.0, 2.0)));
732 assert_eq!(series.get(1), Some(Point2D::new(3.0, 4.0)));
733 assert_eq!(series.get(2), Some(Point2D::new(5.0, 6.0)));
734 }
735
736 #[test]
737 fn test_multi_series() {
738 let mut multi: MultiSeries<Point2D, 5, 10> = MultiSeries::new();
739 let mut series1 = StaticDataSeries::with_label("Series 1");
740 series1.push(Point2D::new(1.0, 2.0)).unwrap();
741
742 let index = multi.add_series(series1).unwrap();
743 assert_eq!(index, 0);
744 assert_eq!(multi.series_count(), 1);
745
746 let retrieved_series = multi.get_series(0).unwrap();
747 assert_eq!(retrieved_series.label(), Some("Series 1"));
748 assert_eq!(retrieved_series.len(), 1);
749 }
750
751 #[cfg(feature = "animations")]
752 #[test]
753 fn test_sliding_window_series() {
754 let mut series: SlidingWindowSeries<Point2D, 3> = SlidingWindowSeries::new();
755
756 series.push(Point2D::new(1.0, 1.0));
757 series.push(Point2D::new(2.0, 2.0));
758 series.push(Point2D::new(3.0, 3.0));
759
760 assert_eq!(series.current_len(), 3);
761 assert!(series.is_full());
762
763 series.push(Point2D::new(4.0, 4.0));
765 assert_eq!(series.current_len(), 3);
766
767 let points: Vec<Point2D, 3> = series.iter().collect();
769 assert_eq!(points.len(), 3);
770 assert_eq!(points[0], Point2D::new(2.0, 2.0));
771 assert_eq!(points[1], Point2D::new(3.0, 3.0));
772 assert_eq!(points[2], Point2D::new(4.0, 4.0));
773 }
774}