1use super::{AsBound, AsRange, Directed, Measure};
2use range_traits::{MaybeBounded, PartialEnum};
3use std::{
4 cmp::{Ordering, PartialOrd},
5 ops::Bound,
6};
7
8#[derive(Debug)]
9pub enum RangeOrdering {
10 Before(bool),
11 Intersecting(bool, bool),
12 After(bool),
13}
14
15impl RangeOrdering {
16 pub fn is_before(&self, connected: bool) -> bool {
17 match self {
18 RangeOrdering::Before(c) => !*c || !connected,
19 _ => false,
20 }
21 }
22
23 pub fn is_after(&self, connected: bool) -> bool {
24 match self {
25 RangeOrdering::After(c) => !*c || !connected,
26 _ => false,
27 }
28 }
29
30 pub fn matches(&self, connected: bool) -> bool {
31 match self {
32 RangeOrdering::Before(c) => *c && connected,
33 RangeOrdering::After(c) => *c && connected,
34 RangeOrdering::Intersecting(_, _) => true,
35 }
36 }
37}
38
39pub trait RangePartialOrd<T = Self> {
40 fn range_partial_cmp<R: AsRange<Item = T>>(&self, other: &R) -> Option<RangeOrdering>;
41}
42
43impl<R: AsRange, U> RangePartialOrd<U> for R
44where
45 R::Item: PartialOrd<U> + Measure<U>,
46 U: PartialEnum,
47{
48 fn range_partial_cmp<S: AsRange<Item = U>>(&self, other: &S) -> Option<RangeOrdering> {
49 match direct_bound_partial_cmp(self.start(), other.start(), true) {
50 Some(BoundOrdering::Included(limit_before)) => {
51 match inverse_bound_partial_cmp(self.start(), other.end(), false) {
52 Some(BoundOrdering::Included(_)) => {
53 match direct_bound_partial_cmp(self.end(), other.end(), false) {
54 Some(BoundOrdering::Included(limit_after)) => {
55 Some(RangeOrdering::Intersecting(limit_before, limit_after))
56 }
57 Some(BoundOrdering::Excluded(_)) => {
58 Some(RangeOrdering::Intersecting(limit_before, false))
59 }
60 None => None,
61 }
62 }
63 Some(BoundOrdering::Excluded(limit_after)) => {
64 Some(RangeOrdering::After(limit_after))
65 }
66 None => None,
67 }
68 }
69 Some(BoundOrdering::Excluded(_)) => {
70 match inverse_bound_partial_cmp(self.end(), other.start(), true) {
71 Some(BoundOrdering::Included(_)) => {
72 match direct_bound_partial_cmp(self.end(), other.end(), false) {
73 Some(BoundOrdering::Included(limit_after)) => {
74 Some(RangeOrdering::Intersecting(false, limit_after))
75 }
76 Some(BoundOrdering::Excluded(_)) => {
77 Some(RangeOrdering::Intersecting(false, false))
78 }
79 None => None,
80 }
81 }
82 Some(BoundOrdering::Excluded(limit_before)) => {
83 Some(RangeOrdering::Before(limit_before))
84 }
85 None => None,
86 }
87 }
88 None => None,
89 }
90 }
91}
92
93pub enum BoundOrdering {
94 Included(bool),
95 Excluded(bool),
96}
97
98pub trait BoundPartialOrd<T = Self> {
99 fn bound_partial_cmp<B: AsBound<Item = T>>(&self, other: &Directed<B>)
100 -> Option<BoundOrdering>;
101}
102
103impl<B: AsBound, U> BoundPartialOrd<U> for Directed<B>
104where
105 B::Item: PartialOrd<U> + Measure<U> + PartialEnum,
106 U: PartialEnum,
107{
108 fn bound_partial_cmp<C: AsBound<Item = U>>(
109 &self,
110 other: &Directed<C>,
111 ) -> Option<BoundOrdering> {
112 match (self, other) {
113 (Directed::Start(a), Directed::Start(b)) => {
114 direct_bound_partial_cmp(a.bound(), b.bound(), true)
115 }
116 (Directed::Start(a), Directed::End(b)) => {
117 inverse_bound_partial_cmp(a.bound(), b.bound(), false)
118 }
119 (Directed::End(a), Directed::Start(b)) => {
120 inverse_bound_partial_cmp(a.bound(), b.bound(), true)
121 }
122 (Directed::End(a), Directed::End(b)) => {
123 direct_bound_partial_cmp(a.bound(), b.bound(), false)
124 }
125 }
126 }
127}
128
129pub trait BoundOrd<T = Self> {
130 fn bound_cmp<B: AsBound<Item = T>>(&self, other: &Directed<B>) -> BoundOrdering;
131}
132
133impl<B: AsBound> BoundOrd<B::Item> for Directed<B>
134where
135 B::Item: Ord + Measure + PartialEnum,
136{
137 fn bound_cmp<C: AsBound<Item = B::Item>>(&self, other: &Directed<C>) -> BoundOrdering {
138 match (self, other) {
139 (Directed::Start(a), Directed::Start(b)) => {
140 direct_bound_cmp(a.bound(), b.bound(), true)
141 }
142 (Directed::Start(a), Directed::End(b)) => {
143 inverse_bound_cmp(a.bound(), b.bound(), false)
144 }
145 (Directed::End(a), Directed::Start(b)) => inverse_bound_cmp(a.bound(), b.bound(), true),
146 (Directed::End(a), Directed::End(b)) => direct_bound_cmp(a.bound(), b.bound(), false),
147 }
148 }
149}
150
151pub enum Dist {
152 Equals,
153 Zero,
154 One,
155 More,
156}
157
158fn dist<T, U>(t: &T, u: &U) -> Dist
159where
160 T: PartialEnum + PartialEq<U>,
161{
162 if t == u {
163 return Dist::Equals;
164 }
165
166 if let Some(s) = t.succ() {
167 if s == *u {
168 return Dist::Zero;
169 } else {
170 match s.succ() {
171 Some(ss) if ss == *u => return Dist::One,
172 _ => (),
173 }
174 }
175 }
176
177 if let Some(s) = t.pred() {
178 if s == *u {
179 return Dist::Zero;
180 } else {
181 match s.pred() {
182 Some(ss) if ss == *u => return Dist::One,
183 _ => (),
184 }
185 }
186 }
187
188 Dist::More
189}
190
191fn distance_zero<T, U>(t: &T, u: &U) -> bool
192where
193 T: PartialEnum + PartialEq<U>,
194{
195 match t.succ() {
196 Some(s) if s == *u => return true,
197 _ => (),
198 }
199
200 match t.pred() {
201 Some(p) if p == *u => return true,
202 _ => (),
203 }
204
205 false
206}
207
208pub(crate) fn direct_bound_partial_cmp<T, U>(
209 b1: Bound<&T>,
210 b2: Bound<&U>,
211 start: bool,
212) -> Option<BoundOrdering>
213where
214 T: Measure<U> + PartialOrd<U> + PartialEnum,
215 U: PartialEnum,
216{
217 let included_ord = if start {
218 Ordering::Greater
219 } else {
220 Ordering::Less
221 };
222
223 match (b1, b2) {
224 (Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
225 Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
226 Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
227 Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
228 None => None,
229 },
230 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
231 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
232 Some(ord) if ord == included_ord => {
233 Some(BoundOrdering::Included(distance_zero(v1, v2)))
234 }
235 Some(_) => Some(BoundOrdering::Excluded(false)),
236 None => None,
237 },
238 (Bound::Included(v1), Bound::Unbounded) => Some(BoundOrdering::Included(
239 (start && U::min().map(|m| *v1 == m).unwrap_or(false))
240 || (!start && U::max().map(|m| *v1 == m).unwrap_or(false)),
241 )),
242 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
243 Some(Ordering::Equal) => Some(BoundOrdering::Included(false)),
244 Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
245 Some(_) => match dist(v1, v2) {
246 Dist::Zero => Some(BoundOrdering::Included(true)),
247 Dist::One => Some(BoundOrdering::Excluded(true)),
248 _ => Some(BoundOrdering::Excluded(false)),
249 },
250 None => None,
251 },
252 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
253 Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
254 Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
255 Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
256 None => None,
257 },
258 (Bound::Excluded(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
259 (Bound::Unbounded, Bound::Included(v2)) => {
260 if (start && U::min().map(|m| *v2 == m).unwrap_or(false))
261 || (!start && U::max().map(|m| *v2 == m).unwrap_or(false))
262 {
263 Some(BoundOrdering::Included(true))
264 } else {
265 Some(BoundOrdering::Excluded(
266 (start
267 && v2
268 .pred()
269 .and_then(|pred| U::min().map(|m| pred == m))
270 .unwrap_or(false))
271 || (!start
272 && v2
273 .succ()
274 .and_then(|succ| U::min().map(|m| succ == m))
275 .unwrap_or(false)),
276 ))
277 }
278 }
279 (Bound::Unbounded, Bound::Excluded(_)) => Some(BoundOrdering::Excluded(false)),
280 (Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(true)),
281 }
282}
283
284pub(crate) fn direct_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> BoundOrdering
285where
286 T: Measure + PartialEnum + Ord,
287{
288 let included_ord = if start {
289 Ordering::Greater
290 } else {
291 Ordering::Less
292 };
293
294 match (b1, b2) {
295 (Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
296 Ordering::Equal => BoundOrdering::Included(true),
297 ord if ord == included_ord => BoundOrdering::Included(false),
298 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
299 },
300 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
301 Ordering::Equal => BoundOrdering::Excluded(true),
302 ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
303 _ => BoundOrdering::Excluded(false),
304 },
305 (Bound::Included(v1), Bound::Unbounded) => BoundOrdering::Included(
306 (start && Some(v1) == <T as MaybeBounded>::min().as_ref())
307 || (!start && Some(v1) == <T as MaybeBounded>::max().as_ref()),
308 ),
309 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
310 Ordering::Equal => BoundOrdering::Included(false),
311 ord if ord == included_ord => BoundOrdering::Included(false),
312 _ => match dist(v1, v2) {
313 Dist::Zero => BoundOrdering::Included(true),
314 Dist::One => BoundOrdering::Excluded(true),
315 _ => BoundOrdering::Excluded(false),
316 },
317 },
318 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
319 Ordering::Equal => BoundOrdering::Included(true),
320 ord if ord == included_ord => BoundOrdering::Included(false),
321 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
322 },
323 (Bound::Excluded(_), Bound::Unbounded) => BoundOrdering::Included(false),
324 (Bound::Unbounded, Bound::Included(v2)) => {
325 if (start && <T as MaybeBounded>::min().as_ref() == Some(v2))
326 || (!start && <T as MaybeBounded>::max().as_ref() == Some(v2))
327 {
328 BoundOrdering::Included(true)
329 } else {
330 BoundOrdering::Excluded(
331 (start
332 && v2
333 .pred()
334 .map(|pred| <T as MaybeBounded>::min() == Some(pred))
335 .unwrap_or(false))
336 || (!start
337 && v2
338 .succ()
339 .map(|succ| <T as MaybeBounded>::min() == Some(succ))
340 .unwrap_or(false)),
341 )
342 }
343 }
344 (Bound::Unbounded, Bound::Excluded(_)) => BoundOrdering::Excluded(false),
345 (Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(true),
346 }
347}
348
349pub(crate) fn direct_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool
350where
351 T: Measure<U> + PartialOrd<U> + PartialEnum,
352 U: PartialEnum,
353{
354 match direct_bound_partial_cmp(b1, b2, start) {
355 Some(BoundOrdering::Included(eq)) => eq,
356 _ => false,
357 }
358}
359
360pub(crate) fn inverse_bound_partial_cmp<T, U>(
368 b1: Bound<&T>,
369 b2: Bound<&U>,
370 b2_start: bool,
371) -> Option<BoundOrdering>
372where
373 T: Measure<U> + PartialOrd<U> + PartialEnum,
374 U: PartialEnum,
375{
376 let included_ord = if b2_start {
377 Ordering::Greater
378 } else {
379 Ordering::Less
380 };
381
382 match (b1, b2) {
383 (Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
384 Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
385 Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
386 Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
387 None => None,
388 },
389 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
390 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
391 Some(ord) if ord == included_ord => {
392 Some(BoundOrdering::Included(distance_zero(v1, v2)))
393 }
394 Some(_) => Some(BoundOrdering::Excluded(false)),
395 None => None,
396 },
397 (Bound::Included(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
398 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
399 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)), Some(ord) if ord == included_ord => {
401 Some(BoundOrdering::Included(distance_zero(v1, v2)))
402 }
403 Some(_) => Some(BoundOrdering::Excluded(false)),
404 None => None,
405 },
406 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
407 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(false)),
408 Some(ord) if ord == included_ord => match dist(v1, v2) {
409 Dist::Zero => Some(BoundOrdering::Excluded(true)), Dist::One => Some(BoundOrdering::Included(true)), _ => Some(BoundOrdering::Included(false)), },
413 Some(_) => Some(BoundOrdering::Excluded(false)), None => None,
415 },
416 (Bound::Excluded(v1), Bound::Unbounded) => {
417 if (!b2_start && U::max().map(|m| *v1 == m).unwrap_or(false))
418 || (b2_start && U::min().map(|m| *v1 == m).unwrap_or(false))
419 {
420 Some(BoundOrdering::Excluded(true))
421 } else {
422 Some(BoundOrdering::Included(
423 (!b2_start
424 && v1
425 .pred()
426 .map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
427 .unwrap_or(false))
428 || (b2_start
429 && v1
430 .succ()
431 .map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
432 .unwrap_or(false)),
433 ))
434 }
435 }
436 (Bound::Unbounded, Bound::Included(_)) => Some(BoundOrdering::Included(false)),
437 (Bound::Unbounded, Bound::Excluded(v2)) => {
438 if (!b2_start && U::min().map(|m| *v2 == m).unwrap_or(false))
439 || (b2_start && U::max().map(|m| *v2 == m).unwrap_or(false))
440 {
441 Some(BoundOrdering::Excluded(true))
442 } else {
443 Some(BoundOrdering::Included(
444 (!b2_start
445 && v2
446 .pred()
447 .map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
448 .unwrap_or(false))
449 || (b2_start
450 && v2
451 .succ()
452 .map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
453 .unwrap_or(false)),
454 ))
455 }
456 }
457 (Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(false)),
458 }
459}
460
461pub(crate) fn inverse_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, b2_start: bool) -> BoundOrdering
462where
463 T: Measure + Ord + PartialEnum,
464{
465 let included_ord = if b2_start {
466 Ordering::Greater
467 } else {
468 Ordering::Less
469 };
470
471 match (b1, b2) {
472 (Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
473 Ordering::Equal => BoundOrdering::Included(true),
474 ord if ord == included_ord => BoundOrdering::Included(false),
475 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
476 },
477 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
478 Ordering::Equal => BoundOrdering::Excluded(true),
479 ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
480 _ => BoundOrdering::Excluded(false),
481 },
482 (Bound::Included(_), Bound::Unbounded) => BoundOrdering::Included(false),
483 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
484 Ordering::Equal => BoundOrdering::Excluded(true), ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
486 _ => BoundOrdering::Excluded(false),
487 },
488 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
489 Ordering::Equal => BoundOrdering::Excluded(false),
490 ord if ord == included_ord => match dist(v1, v2) {
491 Dist::Zero => BoundOrdering::Excluded(true), Dist::One => BoundOrdering::Included(true), _ => BoundOrdering::Included(false), },
495 _ => BoundOrdering::Excluded(false), },
497 (Bound::Excluded(v1), Bound::Unbounded) => {
498 if (!b2_start
499 && <T as MaybeBounded>::max()
500 .map(|m| *v1 == m)
501 .unwrap_or(false))
502 || (b2_start
503 && <T as MaybeBounded>::min()
504 .map(|m| *v1 == m)
505 .unwrap_or(false))
506 {
507 BoundOrdering::Excluded(true)
508 } else {
509 BoundOrdering::Included(
510 (!b2_start
511 && v1
512 .pred()
513 .map(|pred| {
514 <T as MaybeBounded>::min()
515 .map(|m| pred == m)
516 .unwrap_or(false)
517 })
518 .unwrap_or(false))
519 || (b2_start
520 && v1
521 .succ()
522 .map(|succ| {
523 <T as MaybeBounded>::max()
524 .map(|m| succ == m)
525 .unwrap_or(false)
526 })
527 .unwrap_or(false)),
528 )
529 }
530 }
531 (Bound::Unbounded, Bound::Included(_)) => BoundOrdering::Included(false),
532 (Bound::Unbounded, Bound::Excluded(v2)) => {
533 if (!b2_start
534 && <T as MaybeBounded>::min()
535 .map(|m| *v2 == m)
536 .unwrap_or(false))
537 || (b2_start
538 && <T as MaybeBounded>::max()
539 .map(|m| *v2 == m)
540 .unwrap_or(false))
541 {
542 BoundOrdering::Excluded(true)
543 } else {
544 BoundOrdering::Included(
545 (!b2_start
546 && v2
547 .pred()
548 .map(|pred| {
549 <T as MaybeBounded>::min()
550 .map(|m| pred == m)
551 .unwrap_or(false)
552 })
553 .unwrap_or(false))
554 || (b2_start
555 && v2
556 .succ()
557 .map(|succ| {
558 <T as MaybeBounded>::max()
559 .map(|m| succ == m)
560 .unwrap_or(false)
561 })
562 .unwrap_or(false)),
563 )
564 }
565 }
566 (Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(false),
567 }
568}
569
570