Skip to main content

hexga_math/range/
step.rs

1use super::*;
2
3pub trait RangeStepExtension
4{
5    type Output: Iterator<Item = Self::Item> + DoubleEndedIterator + FusedIterator;
6    type Item;
7    /// Should emit an empty iterator if step is zero (not well defined)
8    fn step(self, step: Self::Item) -> Self::Output;
9    fn step_by_one(self) -> Self::Output
10    where
11        Self: Sized,
12        Self::Item: One,
13    {
14        self.step(Self::Item::ONE)
15    }
16}
17
18// Todo : Remove once the Step trait will be stabilized
19pub trait RangeStepIter: Primitive
20{
21    // Using Range, last value is excluded
22    fn iter(max_excluded: Self) -> RangeStep<Self>
23    {
24        RangeStep {
25            idx: Self::ZERO,
26            end: max_excluded,
27            step: Self::ONE,
28        }
29    }
30}
31impl<T> RangeStepIter for T where T: Primitive {}
32
33pub trait RangeDefaultStepExtension: RangeDefault
34where
35    Range<Self>: RangeStepExtension,
36{
37    /// Step using the [`RangeDefault`] : `Self::RANGE_MIN..Self::MAX`
38    fn step(
39        self,
40        step: <Range<Self> as RangeStepExtension>::Item,
41    ) -> <Range<Self> as RangeStepExtension>::Output;
42}
43impl<T> RangeDefaultStepExtension for T
44where
45    T: RangeDefault,
46    Range<T>: RangeStepExtension,
47{
48    fn step(
49        self,
50        step: <Range<Self> as RangeStepExtension>::Item,
51    ) -> <Range<Self> as RangeStepExtension>::Output
52    {
53        (Self::RANGE_MIN..Self::RANGE_MAX).step(step)
54    }
55}
56pub trait RangeDefaultStepInclusiveExtension: RangeDefault
57where
58    RangeInclusive<Self>: RangeStepExtension,
59{
60    /// Step using the [`RangeDefault`] : `Self::RANGE_MIN..=Self::MAX`
61    fn step_inclusive(
62        self,
63        step: <RangeInclusive<Self> as RangeStepExtension>::Item,
64    ) -> <RangeInclusive<Self> as RangeStepExtension>::Output;
65}
66impl<T> RangeDefaultStepInclusiveExtension for T
67where
68    T: RangeDefault,
69    RangeInclusive<T>: RangeStepExtension,
70{
71    fn step_inclusive(
72        self,
73        step: <RangeInclusive<Self> as RangeStepExtension>::Item,
74    ) -> <RangeInclusive<Self> as RangeStepExtension>::Output
75    {
76        (Self::RANGE_MIN..=Self::RANGE_MAX).step(step)
77    }
78}
79
80// Not [`Copy`] because Range<T> don't impl Copy because iterator are used by reference most of the time
81// See https://stackoverflow.com/questions/43416914/why-doesnt-opsranget-implement-copy-even-if-t-is-copy
82#[derive(Clone, PartialEq, Debug, Hash)]
83#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
84pub struct RangeStep<T>
85where
86    T: Primitive,
87{
88    pub idx: T,
89    pub end: T,
90    pub step: T,
91}
92
93impl<T> Iterator for RangeStep<T>
94where
95    T: Primitive,
96{
97    type Item = T;
98    fn next(&mut self) -> Option<Self::Item>
99    {
100        if self.idx <= self.end
101        {
102            let val = self.idx;
103            self.idx += self.step;
104
105            match T::PRIMITIVE_TYPE
106            {
107                NumberType::IntegerUnsigned | NumberType::IntegerSigned => Some(val),
108                NumberType::Float =>
109                {
110                    if self.idx == val
111                    {
112                        None
113                    }
114                    else
115                    {
116                        Some(val)
117                    }
118                }
119                NumberType::Bool =>
120                unsafe { unreachable_unchecked() },
121            }
122        }
123        else
124        {
125            None
126        }
127    }
128}
129impl<T> DoubleEndedIterator for RangeStep<T>
130where
131    T: Primitive,
132{
133    fn next_back(&mut self) -> Option<Self::Item>
134    {
135        if self.end >= self.idx
136        {
137            let val = self.end;
138            self.end -= self.step;
139
140            match T::PRIMITIVE_TYPE
141            {
142                NumberType::IntegerUnsigned | NumberType::IntegerSigned => Some(val),
143                // Reached the limit of floating-point precision
144                NumberType::Float =>
145                {
146                    if self.end == val
147                    {
148                        None
149                    }
150                    else
151                    {
152                        Some(val)
153                    }
154                }
155                NumberType::Bool =>
156                unsafe { unreachable_unchecked() },
157            }
158        }
159        else
160        {
161            match T::PRIMITIVE_TYPE
162            {
163                NumberType::IntegerUnsigned | NumberType::IntegerSigned => None,
164                // Reached the limit of floating-point precision
165                NumberType::Float =>
166                {
167                    // Force the iterator to stop at the first value
168                    if self.end + self.step > self.idx
169                    {
170                        self.step = T::ZERO;
171                        self.end = self.idx;
172                        Some(self.idx)
173                    }
174                    else
175                    {
176                        None
177                    }
178                }
179                NumberType::Bool =>
180                unsafe { unreachable_unchecked() },
181            }
182        }
183    }
184}
185impl<T> FusedIterator for RangeStep<T> where T: Primitive {}
186
187impl<T> RangeStepExtension for Range<T>
188where
189    T: Primitive,
190{
191    type Output = RangeStep<T>;
192    type Item = T;
193    fn step(self, step: T) -> Self::Output
194    {
195        RangeStep {
196            idx: self.start,
197            end: self.end - step,
198            step,
199        }
200    }
201}
202impl<T> RangeStepExtension for RangeTo<T>
203where
204    T: Primitive + RangeDefault,
205{
206    type Output = RangeStep<T>;
207    type Item = T;
208    fn step(self, step: T) -> Self::Output { (T::RANGE_MIN..self.end).step(step) }
209}
210
211// Not [`Copy`] because RangeInclusive<T> don't impl Copy because iterator are used by reference most of the time
212// See https://stackoverflow.com/questions/43416914/why-doesnt-opsranget-implement-copy-even-if-t-is-copy
213#[derive(Clone, PartialEq, Debug, Hash)]
214#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
215pub struct RangeStepInclusive<T>
216where
217    T: Primitive,
218{
219    pub idx: T,
220    pub end: T,
221    pub step: T,
222}
223
224impl<T> Iterator for RangeStepInclusive<T>
225where
226    T: Primitive,
227{
228    type Item = T;
229    fn next(&mut self) -> Option<Self::Item>
230    {
231        if self.idx <= self.end
232        {
233            let val = self.idx;
234            self.idx += self.step;
235
236            match T::PRIMITIVE_TYPE
237            {
238                NumberType::IntegerUnsigned | NumberType::IntegerSigned => Some(val),
239                // Reached the limit of floating-point precision
240                NumberType::Float =>
241                {
242                    if self.idx == val
243                    {
244                        None
245                    }
246                    else
247                    {
248                        Some(val)
249                    }
250                }
251                NumberType::Bool =>
252                unsafe { unreachable_unchecked() },
253            }
254        }
255        else
256        {
257            match T::PRIMITIVE_TYPE
258            {
259                NumberType::IntegerUnsigned | NumberType::IntegerSigned => None,
260                // Reached the limit of floating-point precision
261                NumberType::Float =>
262                {
263                    // Force the iterator to stop at the first value
264                    if self.idx - self.step < self.end
265                    {
266                        self.step = T::ZERO;
267                        self.idx = self.end;
268                        Some(self.end)
269                    }
270                    else
271                    {
272                        None
273                    }
274                }
275                NumberType::Bool =>
276                unsafe { unreachable_unchecked() },
277            }
278        }
279    }
280}
281impl<T> DoubleEndedIterator for RangeStepInclusive<T>
282where
283    T: Primitive,
284{
285    fn next_back(&mut self) -> Option<Self::Item>
286    {
287        if self.end >= self.idx
288        {
289            let val = self.end;
290            self.end -= self.step;
291
292            match T::PRIMITIVE_TYPE
293            {
294                NumberType::IntegerUnsigned | NumberType::IntegerSigned => Some(val),
295                // Reached the limit of floating-point precision
296                NumberType::Float =>
297                {
298                    if self.end == val
299                    {
300                        return None;
301                    }
302                    else
303                    {
304                        Some(val)
305                    }
306                }
307                NumberType::Bool =>
308                unsafe { unreachable_unchecked() },
309            }
310        }
311        else
312        {
313            match T::PRIMITIVE_TYPE
314            {
315                NumberType::IntegerUnsigned | NumberType::IntegerSigned => None,
316                // Reached the limit of floating-point precision
317                NumberType::Float =>
318                {
319                    // Force the iterator to stop at the first value
320                    if self.end + self.step > self.idx
321                    {
322                        self.step = T::ZERO;
323                        self.end = self.idx;
324                        Some(self.idx)
325                    }
326                    else
327                    {
328                        None
329                    }
330                }
331                NumberType::Bool =>
332                unsafe { unreachable_unchecked() },
333            }
334        }
335    }
336}
337impl<T> FusedIterator for RangeStepInclusive<T> where T: Primitive {}
338
339impl<T> RangeStepExtension for RangeFrom<T>
340where
341    T: Primitive + RangeDefault,
342{
343    type Output = RangeStepInclusive<T>;
344    type Item = T;
345    fn step(self, step: T) -> Self::Output { (self.start..=T::RANGE_MAX).step(step) }
346}
347impl<T> RangeStepExtension for RangeInclusive<T>
348where
349    T: Primitive,
350{
351    type Output = RangeStepInclusive<T>;
352    type Item = T;
353    fn step(self, step: T) -> Self::Output
354    {
355        let (start, end) = self.into_inner();
356        RangeStepInclusive {
357            idx: start,
358            end,
359            step,
360        }
361    }
362}
363impl<T> RangeStepExtension for RangeToInclusive<T>
364where
365    T: Primitive + RangeDefault,
366{
367    type Output = RangeStepInclusive<T>;
368    type Item = T;
369    fn step(self, step: T) -> Self::Output { (T::RANGE_MIN..=self.end).step(step) }
370}
371
372#[cfg(test)]
373mod range_test
374{
375    use super::*;
376
377    #[test]
378    fn range()
379    {
380        assert_eq!((-2..5).step(1).to_vec(), vec![-2, -1, 0, 1, 2, 3, 4]);
381    }
382
383    #[test]
384    fn range_rev()
385    {
386        assert_eq!((-2..5).step(1).rev().to_vec(), vec![4, 3, 2, 1, 0, -1, -2]);
387    }
388
389    #[test]
390    fn range_inclusive()
391    {
392        assert_eq!((-2..=5).step(1).to_vec(), vec![-2, -1, 0, 1, 2, 3, 4, 5]);
393    }
394
395    #[test]
396    fn range_inclusive_rev()
397    {
398        assert_eq!(
399            (-2..=5).step(1).rev().to_vec(),
400            vec![5, 4, 3, 2, 1, 0, -1, -2]
401        );
402    }
403
404    #[test]
405    fn range_float()
406    {
407        assert_eq!((0.5..2.5).step(1.).to_vec(), vec![0.5, 1.5]);
408    }
409
410    #[test]
411    fn range_rev_float()
412    {
413        assert_eq!((0.5..2.5).step(1.).rev().to_vec(), vec![1.5, 0.5]);
414    }
415
416    #[test]
417    fn range_inclusive_float()
418    {
419        assert_eq!((0.5..=2.5).step(1.).to_vec(), vec![0.5, 1.5, 2.5]);
420    }
421
422    #[test]
423    fn range_inclusive_rev_float()
424    {
425        assert_eq!((0.5..=2.5).step(1.).rev().to_vec(), vec![2.5, 1.5, 0.5]);
426    }
427
428    #[test]
429    fn range_float_2()
430    {
431        let values = (0.0..1.0).step(0.3).to_vec();
432        assert_eq!(values.len(), 3);
433        assert_eq!(values[0], 0.0);
434        assert!(values.last().copied().unwrap() <= 0.95);
435    }
436
437    #[test]
438    fn range_rev_float_2()
439    {
440        let values = (0.0..1.0f32).step(0.3).rev().to_vec();
441        assert_eq!(values.len(), 4);
442        assert!((values[0] - 0.7).abs() <= 0.00001);
443        assert_eq!(values.last().copied().unwrap(), 0.);
444    }
445
446    #[test]
447    fn range_inclusive_float_2()
448    {
449        let values = (0.0..=1.0).step(0.3).to_vec();
450        assert_eq!(values.len(), 5);
451        assert_eq!(values[0], 0.0);
452        assert_eq!(values.last().copied().unwrap(), 1.0);
453    }
454
455    #[test]
456    fn range_inclusive_rev_float_2()
457    {
458        let values = (0.0..=1.0).step(0.3).rev().to_vec();
459        assert_eq!(values.len(), 5);
460        assert_eq!(values[0], 1.0);
461        assert_eq!(values.last().copied().unwrap(), 0.);
462    }
463}