range_set/range_compare.rs
1//! Type and functions for comparing inclusive ranges
2
3use std;
4use num_traits::PrimInt;
5
6////////////////////////////////////////////////////////////////////////////////
7// enums //
8////////////////////////////////////////////////////////////////////////////////
9
10/// Result of comparing a pair of ranges `(A,B)`
11#[derive(Debug,Eq,PartialEq)]
12pub enum RangeCompare {
13 Disjoint (RangeDisjoint),
14 Intersect (RangeIntersect)
15}
16
17/// Ways in which a pair of ranges `(A,B)` can be disjoint
18#[derive(Debug,Eq,PartialEq)]
19pub enum RangeDisjoint {
20 /// `A = B = {}`
21 EmptyBoth,
22 /// `A = {}`
23 EmptyLhs,
24 /// `B = {}`
25 EmptyRhs,
26 /// `[ A ] [ B ]`
27 LessThanProper,
28 /// `[ A ][ B ]`
29 LessThanAdjacent,
30 /// `[ B ] [ A ]`
31 GreaterThanProper,
32 /// `[ B ][ A ]`
33 GreaterThanAdjacent
34}
35
36/// Ways in which a pair of ranges `(A,B)` can intersect
37#[derive(Debug,Eq,PartialEq)]
38pub enum RangeIntersect {
39 /// `[ A=B ]`
40 EqualTo,
41 /// `[ A [ ] B ]`
42 OverlapsLeft,
43 /// `[ B [ ] A ]`
44 OverlapsRight,
45 /// `[ B ] A ]`
46 ContainsFirst,
47 /// `[ A [ B ] ]`
48 ContainsProper,
49 /// `[ A [ B ]`
50 ContainsLast,
51 /// `[ A ] B ]`
52 ContainedByFirst,
53 /// `[ [ A ] B ]`
54 ContainedByProper,
55 /// `[ B [ A ]`
56 ContainedByLast
57}
58
59////////////////////////////////////////////////////////////////////////////////
60// functions //
61////////////////////////////////////////////////////////////////////////////////
62
63/// Compare two inclusive ranges.
64///
65/// ```
66/// # use range_set::*;
67/// assert_eq!(
68/// range_compare (&(0..=5), &(0..=5)),
69/// RangeCompare::Intersect (RangeIntersect::EqualTo));
70/// assert_eq!(
71/// range_compare (&(1..=0), &(1..=0)),
72/// RangeCompare::Disjoint (RangeDisjoint::EmptyBoth));
73/// ```
74pub fn range_compare <T : PrimInt> (
75 left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
76) -> RangeCompare {
77 if let Some (disjoint) = RangeDisjoint::compare (left, right) {
78 disjoint.into()
79 } else if let Some (intersect) = RangeIntersect::compare (left, right) {
80 intersect.into()
81 } else {
82 unreachable!()
83 }
84}
85
86/// Compute the intersection of two inclusive ranges. Returns an empty range
87/// if they are disjoint.
88///
89/// ```
90/// # use range_set::*;
91/// assert_eq!(intersection (&(0..=3), &(2..=5)), 2..=3);
92/// assert!(intersection (&(0..=2), &(3..=5)).is_empty());
93/// ```
94pub fn intersection <T : PrimInt> (
95 left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
96) -> std::ops::RangeInclusive <T> {
97 if RangeDisjoint::compare (left, right).is_none() {
98 std::cmp::max (*left.start(), *right.start())..=
99 std::cmp::min (*left.end(), *right.end())
100 } else {
101 T::one()..=T::zero()
102 }
103}
104
105////////////////////////////////////////////////////////////////////////////////
106// impls //
107////////////////////////////////////////////////////////////////////////////////
108
109impl From <RangeDisjoint> for RangeCompare {
110 fn from (disjoint : RangeDisjoint) -> Self {
111 RangeCompare::Disjoint (disjoint)
112 }
113}
114
115impl From <RangeIntersect> for RangeCompare {
116 fn from (intersect : RangeIntersect) -> Self {
117 RangeCompare::Intersect (intersect)
118 }
119}
120
121impl RangeDisjoint {
122 /// Tests two inclusive ranges for disjointness, returning `None` if they
123 /// intersect.
124 ///
125 /// ```
126 /// # use range_set::*;
127 /// assert_eq!(RangeDisjoint::compare (&(0..=5), &(0..=5)), None);
128 /// assert_eq!(
129 /// RangeDisjoint::compare (&(1..=0), &(1..=0)),
130 /// Some (RangeDisjoint::EmptyBoth));
131 /// assert_eq!(
132 /// RangeDisjoint::compare (&(1..=0), &(0..=5)),
133 /// Some (RangeDisjoint::EmptyLhs));
134 /// assert_eq!(
135 /// RangeDisjoint::compare (&(0..=5), &(1..=0)),
136 /// Some (RangeDisjoint::EmptyRhs));
137 /// assert_eq!(
138 /// RangeDisjoint::compare (&(0..=2), &(4..=5)),
139 /// Some (RangeDisjoint::LessThanProper));
140 /// assert_eq!(
141 /// RangeDisjoint::compare (&(0..=2), &(3..=5)),
142 /// Some (RangeDisjoint::LessThanAdjacent));
143 /// assert_eq!(
144 /// RangeDisjoint::compare (&(4..=5), &(0..=2)),
145 /// Some (RangeDisjoint::GreaterThanProper));
146 /// assert_eq!(
147 /// RangeDisjoint::compare (&(3..=5), &(0..=2)),
148 /// Some (RangeDisjoint::GreaterThanAdjacent));
149 /// ```
150 pub fn compare <T : PrimInt> (
151 left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
152 ) -> Option <Self> {
153 match (left.is_empty(), right.is_empty()) {
154 (true, true) => Some (RangeDisjoint::EmptyBoth),
155 (true, false) => Some (RangeDisjoint::EmptyLhs),
156 (false, true) => Some (RangeDisjoint::EmptyRhs),
157 (false, false) => if right.start() <= left.end()
158 && left.start() <= right.end()
159 || left.start() <= right.end() && right.start() <= left.end()
160 {
161 None // intersection
162 } else {
163 Some (
164 if left.end() < right.start() {
165 match *right.start() - *left.end() {
166 x if x == T::one() => RangeDisjoint::LessThanAdjacent,
167 _ => RangeDisjoint::LessThanProper
168 }
169 } else {
170 debug_assert!(right.end() < left.start());
171 match *left.start() - *right.end() {
172 x if x == T::one() => RangeDisjoint::GreaterThanAdjacent,
173 _ => RangeDisjoint::GreaterThanProper
174 }
175 }
176 )
177 }
178 }
179 }
180}
181
182impl RangeIntersect {
183 /// Test two inclusive ranges for intersection, returning `None` if the
184 /// ranges are disjoint.
185 ///
186 /// ```
187 /// # use range_set::*;
188 /// assert_eq!(RangeIntersect::compare (&(0..=1), &(4..=5)), None);
189 /// assert_eq!(
190 /// RangeIntersect::compare (&(0..=5), &(0..=5)),
191 /// Some (RangeIntersect::EqualTo));
192 /// assert_eq!(
193 /// RangeIntersect::compare (&(0..=5), &(1..=4)),
194 /// Some (RangeIntersect::ContainsProper));
195 /// assert_eq!(
196 /// RangeIntersect::compare (&(1..=4), &(0..=5)),
197 /// Some (RangeIntersect::ContainedByProper));
198 /// assert_eq!(
199 /// RangeIntersect::compare (&(0..=5), &(0..=3)),
200 /// Some (RangeIntersect::ContainsFirst));
201 /// assert_eq!(
202 /// RangeIntersect::compare (&(0..=5), &(4..=5)),
203 /// Some (RangeIntersect::ContainsLast));
204 /// assert_eq!(
205 /// RangeIntersect::compare (&(0..=2), &(0..=5)),
206 /// Some (RangeIntersect::ContainedByFirst));
207 /// assert_eq!(
208 /// RangeIntersect::compare (&(4..=5), &(0..=5)),
209 /// Some (RangeIntersect::ContainedByLast));
210 /// assert_eq!(
211 /// RangeIntersect::compare (&(0..=3), &(3..=5)),
212 /// Some (RangeIntersect::OverlapsLeft));
213 /// assert_eq!(
214 /// RangeIntersect::compare (&(3..=5), &(0..=3)),
215 /// Some (RangeIntersect::OverlapsRight));
216 /// ```
217 pub fn compare <T : PrimInt> (
218 left : &std::ops::RangeInclusive <T>, right : &std::ops::RangeInclusive <T>
219 ) -> Option <Self> {
220 match (left.is_empty(), right.is_empty()) {
221 (true, true) | (true, false) | (false, true) => None,
222 (false, false) => if left.end() < right.start() ||
223 right.end() < left.start()
224 {
225 None // disjoint
226 } else {
227 Some (if left == right {
228 RangeIntersect::EqualTo
229 } else {
230 match left.start().cmp (right.start()) {
231 std::cmp::Ordering::Less => match left.end().cmp (right.end()) {
232 std::cmp::Ordering::Less => RangeIntersect::OverlapsLeft,
233 std::cmp::Ordering::Equal => RangeIntersect::ContainsLast,
234 std::cmp::Ordering::Greater => RangeIntersect::ContainsProper
235 }
236 std::cmp::Ordering::Equal => match left.end().cmp (right.end()) {
237 std::cmp::Ordering::Less => RangeIntersect::ContainedByFirst,
238 std::cmp::Ordering::Equal => unreachable!(),
239 std::cmp::Ordering::Greater => RangeIntersect::ContainsFirst
240 }
241 std::cmp::Ordering::Greater => match left.end().cmp (right.end()) {
242 std::cmp::Ordering::Less => RangeIntersect::ContainedByProper,
243 std::cmp::Ordering::Equal => RangeIntersect::ContainedByLast,
244 std::cmp::Ordering::Greater => RangeIntersect::OverlapsRight
245 }
246 }
247 })
248 }
249 }
250 }
251}