ix_rs/
lib.rs

1#![no_std]
2//! This crate provides a trait ([`Ix`]) for values that permit contiguous subranges.  
3
4/// A trait for values that permit contiguous subranges.
5///
6/// Implementations that override the provided functions must ensure
7/// their custom implementations are equivalent to the provided ones.
8///
9/// Implementations must uphold the following invariants:
10/// 1. `ix.in_range(min, max)` if and only if `Ix::range(min, max).any(|x| x == ix)`
11/// 2. If `ix.in_range(min, max)`, then `Ix::range(min, max).nth(ix.index(min, max)) == Some(ix)`
12/// 3. `Ix::range(min, max).map(|x| x.index(min, max))` yields equal items to `0..Ix::range_size(min, max)`
13/// 4. `Ix::range(min, max).map(|x| x.index_checked(min, max))` ever yields [`None`] if and only if `Ix::range_size_checked(min, max).is_none()`
14/// 5. `Ix::range_size(min, max) == Ix::range(min, max).count()`
15/// 6. `Ix::range_size_checked(min, max).is_none()` if and only if `Ix::range(min, max).count()` overflows or panics
16///
17/// Note that, for these properties, if one side of the equality panics or overflows the equality can be considered to hold.
18///
19/// # Examples
20///
21/// ```
22/// # use ix_rs::Ix;
23/// for (ix, i) in Ix::range(-45i128, 483).zip(0..) {
24///     assert_eq!(ix.index(-45, 483), i);
25/// }
26/// ```
27/// ```
28/// # use ix_rs::Ix;
29/// assert!(!(-30289i16).in_range(-746, 15564));
30/// ```
31/// ```
32/// # use ix_rs::Ix;
33/// assert_eq!(
34///     2410117514u32.in_range(2073922791, 3401563124),
35///     Ix::range(2073922791u32, 3401563124).any(|x| x == 2410117514)
36/// ); // Property 1
37/// ```
38/// ```
39/// # use ix_rs::Ix;
40/// assert!(20i32.in_range(17, 5432));
41/// assert_eq!(Ix::range(17i32, 5432).nth(20i32.index(17, 5432)).unwrap(), 20);
42/// // Property 2
43/// ```
44/// ```
45/// # use ix_rs::Ix;
46/// assert!(0.in_range(-31597i16, 16417));
47/// assert_eq!(Ix::range(-31597i16, 16417).nth(0.index(-31597i16, 16417)).unwrap(), 0);
48/// // Property 2
49/// ```
50/// ```
51/// # use ix_rs::Ix;
52/// assert!(
53///     Ix::range(-633i32, 151)
54///         .map(|x| x.index(-633, 151))
55///         .eq(0..Ix::range_size(-633, 151))
56/// ) // Property 3
57/// ```
58/// ```
59/// # use ix_rs::Ix;
60/// assert_eq!(Ix::range(8079u32, 1836091).count(), Ix::range_size(8079u32, 1836091))
61/// // Property 5
62/// ```
63pub trait Ix: PartialOrd + Sized {
64    /// An iterator over the elements in a range of the implementing type.
65    type Range: Iterator<Item = Self>;
66    /// Generate an iterator over a range starting from `min` and stopping at `max`.
67    /// The resulting iterator must produce `min` and `max` at some point, each.
68    ///
69    /// # Panics
70    ///
71    /// Should panic if `min` is greater than `max`.
72    fn range(min: Self, max: Self) -> Self::Range;
73    /// Get the position of a value inside a range.
74    ///
75    /// # Panics
76    ///
77    /// Should panic if `min` is greater than `max`.
78    ///
79    /// Should panic if the value is not in the range (as determined by [`in_range`]).
80    ///
81    /// Panics if the resulting index is not representable as a [`usize`] value.
82    /// The default implementation does this by unwrapping the return value of [`index_checked`].
83    ///
84    /// [`in_range`]: Ix::in_range
85    /// [`index_checked`]: Ix::index_checked
86    fn index(self, min: Self, max: Self) -> usize {
87        self.index_checked(min, max).expect("index too large")
88    }
89    /// Get the position of a value inside a range.
90    /// If this would overflow the range of [`usize`], returns [`None`].
91    /// Checked version of [`index`].
92    ///
93    /// # Panics
94    ///
95    /// Should panic if `min` is greater than `max`.
96    ///
97    /// Should panic if the value is not in the range (as determined by [`in_range`]).
98    ///
99    /// [`index`]: Ix::index
100    /// [`in_range`]: Ix::in_range
101    fn index_checked(self, min: Self, max: Self) -> Option<usize>;
102    /// Check if a given value is inside a range.
103    ///
104    /// # Panics
105    ///
106    /// Should panic if `min` is greater than `max`.
107    fn in_range(self, min: Self, max: Self) -> bool;
108    /// Get the length of a range.
109    ///
110    /// # Panics
111    ///
112    /// Should panic if `min` is greater than `max`.
113    ///
114    /// Panics if the resulting size is not representable as a [`usize`] value.
115    /// The default implementation does this by unwrapping the return value of [`range_size_checked`].
116    ///
117    /// [`range_size_checked`]: Ix::range_size_checked
118    fn range_size(min: Self, max: Self) -> usize {
119        Ix::range_size_checked(min, max).expect("range size too large")
120    }
121    /// Get the length of a range.
122    /// If this would overflow the range of [`usize`], returns [`None`].
123    /// Checked version of [`range_size`].
124    ///
125    /// # Panics
126    ///
127    /// Should panic if `min` is greater than `max`.
128    ///
129    /// [`range_size`]: Ix::range_size
130    fn range_size_checked(min: Self, max: Self) -> Option<usize>;
131}
132
133macro_rules! assert_ordered {
134    ($min: expr, $max: expr) => {
135        if $min > $max {
136            panic!("min is greater than max");
137        }
138    };
139}
140
141macro_rules! assert_in_range {
142    ($min: expr, $max: expr, $ix: expr) => {
143        if $ix < $min {
144            panic!("index is outside range (< min)");
145        } else if $ix > $max {
146            panic!("index is outside range (> max)");
147        }
148    };
149}
150
151macro_rules! impl_ix_numeric {
152    ($($t: ty),+) => {
153        $(
154            impl $crate::Ix for $t {
155                type Range = ::core::ops::RangeInclusive<$t>;
156                fn range(min: Self, max: Self) -> Self::Range {
157                    assert_ordered!(min, max);
158                    min..=max
159                }
160                fn index_checked(self, min: Self, max: Self) -> Option<usize> {
161                    assert_ordered!(min, max);
162                    assert_in_range!(min, max, self);
163                    usize::try_from(self - min).ok()
164                }
165                fn in_range(self, min: Self, max: Self) -> bool {
166                    assert_ordered!(min, max);
167                    min <= self && self <= max
168                }
169                fn range_size_checked(min: Self, max: Self) -> Option<usize> {
170                    assert_ordered!(min, max);
171                    usize::try_from(max - min)
172                        .ok()
173                        .and_then(|n| n.checked_add(1))
174                }
175            }
176        )+
177    };
178}
179
180impl_ix_numeric!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);