e_ring/
rescale.rs

1use crate::ring::RingIterator;
2use crate::Ring;
3use core::ops::{Add, Div, Mul, Sub};
4
5/// Contains min and max value in a `Ring`
6#[derive(Debug)]
7pub struct Range<T> {
8    /// Minimum value
9    pub min: T,
10    /// Maximum value
11    pub max: T,
12}
13
14impl<T: PartialOrd> Range<T> {
15    /// Creates a Range checking min <= max
16    pub fn new(min: T, max: T) -> Option<Self> {
17        if min <= max {
18            Some(Range { min, max })
19        } else {
20            None
21        }
22    }
23}
24
25/// Trait defining a `range` method to find min and max in one iteration
26pub trait FindRange<T> {
27    /// calculate min and max with one iteration
28    fn range(&self) -> Option<Range<T>>;
29}
30
31impl<T: PartialOrd + Copy + Default, const N: usize> FindRange<T> for Ring<T, N> {
32    fn range(&self) -> Option<Range<T>> {
33        if self.is_empty() {
34            return None;
35        }
36        let mut iter = self.iter();
37        let first = iter.next().unwrap(); // safe because len just checked;
38        let mut min_max = Range {
39            min: first,
40            max: first,
41        };
42        for el in iter {
43            if min_max.min.gt(&el) {
44                min_max.min = el;
45            }
46            if min_max.max.lt(&el) {
47                min_max.max = el;
48            }
49        }
50
51        Some(min_max)
52    }
53}
54
55#[derive(Debug)]
56pub struct RescaleIterator<'a, T, const N: usize> {
57    current: Range<T>,
58    desired: Range<T>,
59    ring_iter: RingIterator<'a, T, N>,
60}
61
62impl<
63        T: Copy
64            + Default
65            + PartialOrd
66            + Sub<Output = T>
67            + Add<Output = T>
68            + Mul<Output = T>
69            + Div<Output = T>
70            + Into<f64>,
71        const N: usize,
72    > Ring<T, N>
73{
74    /// Returns an iterator over the `Ring` on which values are rescaled according to the `desired`
75    /// range
76    pub fn rescaled_iter(&self, current: Range<T>, desired: Range<T>) -> RescaleIterator<T, N> {
77        RescaleIterator {
78            current,
79            desired,
80            ring_iter: self.iter(),
81        }
82    }
83}
84
85impl<
86        T: Copy
87            + Default
88            + PartialOrd
89            + Sub<Output = T>
90            + Add<Output = T>
91            + Mul<Output = T>
92            + Div<Output = T>
93            + Into<f64>,
94        const N: usize,
95    > Iterator for RescaleIterator<'_, T, N>
96{
97    type Item = f64;
98    // TODO would be nice if type returned is `T`
99
100    fn next(&mut self) -> Option<Self::Item> {
101        self.ring_iter.next().map(|el| {
102            let mut zero_one =
103                (el.into() - self.current.min.into()) / (self.current.delta().into());
104            if zero_one.is_nan() {
105                zero_one = 0.5;
106            }
107            zero_one * self.desired.delta().into() + self.desired.min.into()
108        })
109    }
110}
111
112impl<T: Sub<Output = T> + Copy> Range<T> {
113    /// Returns the range delta
114    pub fn delta(&self) -> T {
115        self.max - self.min
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use super::{FindRange, Range, Ring};
122    const RING_SIZE: usize = 128;
123
124    #[test]
125    pub fn test_range() {
126        let mut circ: Ring<i32, RING_SIZE> = Ring::new();
127        assert!(circ.range().is_none());
128        circ.append(0);
129        assert_eq!(circ.range().unwrap().min, 0);
130        assert_eq!(circ.range().unwrap().max, 0);
131        circ.append(1);
132        assert_eq!(circ.range().unwrap().min, 0);
133        assert_eq!(circ.range().unwrap().max, 1);
134        circ.append(-1);
135        assert_eq!(circ.range().unwrap().min, -1);
136        assert_eq!(circ.range().unwrap().max, 1);
137        for _ in 0..RING_SIZE {
138            circ.append(0);
139        }
140        assert_eq!(circ.range().unwrap().min, 0);
141        assert_eq!(circ.range().unwrap().max, 0);
142    }
143
144    #[test]
145    pub fn test_rescale() {
146        let mut circ: Ring<i16, RING_SIZE> = Ring::new();
147        circ.append(100i16);
148        circ.append(200);
149        circ.append(300);
150        let current = circ.range().unwrap();
151        let desired = Range { min: 20, max: 30 };
152        let mut rescaled = circ.rescaled_iter(current, desired);
153        assert_eq!(rescaled.next().map(|el| el as i16), Some(20i16));
154        assert_eq!(rescaled.next().map(|el| el as i16), Some(25i16));
155        assert_eq!(rescaled.next().map(|el| el as i16), Some(30i16));
156        assert_eq!(rescaled.next(), None);
157    }
158}