rust_intervals/
bounds.rs

1use crate::nothing_between::NothingBetween;
2use crate::step::Step;
3use ::core::cmp::{Ordering, PartialOrd};
4
5/// One bound of an interval
6/// LeftOf, applied to value, represents a conceptual point halfway between
7/// the value and its predecessor value.
8/// Likewise, RightOf represents a conceptual point halfway between the value
9/// and its successor.
10pub(crate) enum Bound<T> {
11    LeftUnbounded,
12    LeftOf(T),
13    RightOf(T),
14    RightUnbounded,
15}
16
17impl<T> Bound<T> {
18    /// True if value is to the right of the bound
19    pub(crate) fn left_of(&self, value: &T) -> bool
20    where
21        T: PartialOrd,
22    {
23        match self {
24            Bound::LeftUnbounded => true,
25            Bound::LeftOf(point) => *point <= *value,
26            Bound::RightOf(point) => *point < *value,
27            Bound::RightUnbounded => false,
28        }
29    }
30
31    /// True if the value is to the left of the bound
32    pub(crate) fn right_of(&self, value: &T) -> bool
33    where
34        T: PartialOrd,
35    {
36        match self {
37            Bound::LeftUnbounded => false,
38            Bound::LeftOf(point) => *value < *point,
39            Bound::RightOf(point) => *value <= *point,
40            Bound::RightUnbounded => true,
41        }
42    }
43
44    /// Return the bound's value (which might be included in the interval
45    /// or not).  This returns None for an unbounded bound.
46    pub(crate) fn value(&self) -> Option<&T> {
47        match self {
48            Bound::LeftUnbounded | Bound::RightUnbounded => None,
49            Bound::LeftOf(p) | Bound::RightOf(p) => Some(p),
50        }
51    }
52
53    /// Converts from `Bound<T>` to `Bound<&T>`
54    pub(crate) fn as_ref(&self) -> Bound<&T> {
55        match self {
56            Bound::LeftUnbounded => Bound::LeftUnbounded,
57            Bound::LeftOf(point) => Bound::LeftOf(point),
58            Bound::RightOf(point) => Bound::RightOf(point),
59            Bound::RightUnbounded => Bound::RightUnbounded,
60        }
61    }
62}
63
64impl<T> ::core::fmt::Debug for Bound<T>
65where
66    T: ::core::fmt::Debug,
67{
68    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
69        match self {
70            Bound::LeftUnbounded => write!(f, "-infinity")?,
71            Bound::LeftOf(point) => write!(f, "LeftOf({point:?})")?,
72            Bound::RightOf(point) => write!(f, "RightOf({point:?})")?,
73            Bound::RightUnbounded => write!(f, "+infinity")?,
74        }
75        Ok(())
76    }
77}
78
79impl<T> ::core::hash::Hash for Bound<T>
80where
81    T: ::core::hash::Hash + Step,
82{
83    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
84        // Hash cannot be implemented for f32.
85        // One of the strong requirements imposed by Rust is that if two
86        // intervals are equal, they must also have the same hash.  So we must
87        // normalize the bounds.
88        // For instance, "[1," should hash the same as "(0," for integers.
89        // But there is no equivalent for floats: adding EPSILON might return
90        // the same value if we have a large enough float, so we cannot
91        // normalize.
92        match self {
93            Bound::LeftUnbounded | Bound::RightUnbounded => {
94                core::mem::discriminant(self).hash(state);
95            }
96            Bound::LeftOf(point) => {
97                core::mem::discriminant(self).hash(state);
98                point.hash(state);
99            }
100            Bound::RightOf(point) => {
101                let next = point.forward(1);
102                if let Some(next) = next {
103                    Bound::LeftOf(next).hash(state);
104                    return;
105                }
106                core::mem::discriminant(self).hash(state);
107                point.hash(state);
108            }
109        }
110    }
111}
112
113impl<T> PartialEq<Bound<&T>> for Bound<T>
114where
115    T: PartialOrd + NothingBetween,
116{
117    //  Bound is never equal to an exact value.  Doesn't matter since we only
118    //  compare for strict inequality
119    fn eq(&self, other: &Bound<&T>) -> bool {
120        match (self, other) {
121            (Bound::LeftUnbounded, Bound::LeftUnbounded)
122            | (Bound::RightUnbounded, Bound::RightUnbounded) => true,
123            (Bound::LeftOf(s), Bound::LeftOf(o))
124            | (Bound::RightOf(s), Bound::RightOf(o)) => *s == **o,
125            (Bound::LeftOf(s), Bound::RightOf(o)) => match s.partial_cmp(o) {
126                None | Some(Ordering::Less | Ordering::Equal) => false,
127                Some(Ordering::Greater) => (*o).nothing_between(s),
128            },
129            (Bound::RightOf(s), Bound::LeftOf(o)) => match s.partial_cmp(o) {
130                None | Some(Ordering::Equal | Ordering::Greater) => false,
131                Some(Ordering::Less) => s.nothing_between(o),
132            },
133            (Bound::LeftUnbounded, _)
134            | (_, Bound::LeftUnbounded)
135            | (_, Bound::RightUnbounded)
136            | (Bound::RightUnbounded, _) => false,
137        }
138    }
139}
140
141impl<T> PartialEq for Bound<T>
142where
143    T: PartialOrd + NothingBetween,
144{
145    //  Bound is never equal to an exact value.  Doesn't matter since we only
146    //  compare for strict inequality
147    fn eq(&self, other: &Bound<T>) -> bool {
148        self.eq(&other.as_ref())
149    }
150}
151
152impl<T> Eq for Bound<T> where T: PartialOrd + NothingBetween {}
153
154impl<T> PartialOrd for Bound<T>
155where
156    T: PartialOrd + NothingBetween,
157{
158    fn partial_cmp(&self, other: &Bound<T>) -> Option<Ordering> {
159        self.partial_cmp(&other.as_ref())
160    }
161}
162
163impl<T> PartialOrd<Bound<&T>> for Bound<T>
164where
165    T: PartialOrd + NothingBetween,
166{
167    /// Two bounds (either lower and upper of same interval, or possibly
168    /// lowers from two intervals) might be equivalent if there is nothing
169    /// between them.
170    /// For instance, for f32:
171    ///     RightOf(1.0) is equivalent to LeftOf(1.0 + EPSILON)
172    ///     since there is nothing between 1.0 and 1.0 + EPSILON
173    /// (this would not be true when talking about mathematical reals for
174    /// instance).
175    /// This function returns Equal if there is nothing between the two
176    /// bounds.
177    fn partial_cmp(&self, other: &Bound<&T>) -> Option<Ordering> {
178        match (self, other) {
179            (Bound::LeftUnbounded, Bound::LeftUnbounded)
180            | (Bound::RightUnbounded, Bound::RightUnbounded) => {
181                Some(Ordering::Equal)
182            }
183            (Bound::LeftOf(s), Bound::LeftOf(o))
184            | (Bound::RightOf(s), Bound::RightOf(o)) => s.partial_cmp(*o),
185            (Bound::LeftOf(s), Bound::RightOf(o)) => match s.partial_cmp(o) {
186                None => None,
187                Some(Ordering::Less | Ordering::Equal) => Some(Ordering::Less),
188                Some(Ordering::Greater) => Some(if (*o).nothing_between(s) {
189                    Ordering::Equal
190                } else {
191                    Ordering::Greater
192                }),
193            },
194            (Bound::RightOf(s), Bound::LeftOf(o)) => match s.partial_cmp(*o) {
195                None => None,
196                Some(Ordering::Less) => Some(if s.nothing_between(*o) {
197                    Ordering::Equal
198                } else {
199                    Ordering::Less
200                }),
201                Some(Ordering::Equal | Ordering::Greater) => {
202                    Some(Ordering::Greater)
203                }
204            },
205            (Bound::LeftUnbounded, _) => Some(Ordering::Less),
206            (_, Bound::LeftUnbounded) => Some(Ordering::Greater),
207            (_, Bound::RightUnbounded) => Some(Ordering::Less),
208            (Bound::RightUnbounded, _) => Some(Ordering::Greater),
209        }
210    }
211}
212
213impl<T> ::core::clone::Clone for Bound<T>
214where
215    T: Clone,
216{
217    fn clone(&self) -> Self {
218        match self {
219            Bound::LeftUnbounded => Bound::LeftUnbounded,
220            Bound::RightUnbounded => Bound::RightUnbounded,
221            Bound::LeftOf(point) => Bound::LeftOf(point.clone()),
222            Bound::RightOf(point) => Bound::RightOf(point.clone()),
223        }
224    }
225}
226
227impl<T> Copy for Bound<T> where T: Copy {}