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)) || (!start
271 && v2
272 .succ()
273 .and_then(|succ| U::min().map(|m| succ == m))
274 .unwrap_or(false)),
275 ))
276 }
277 }
278 (Bound::Unbounded, Bound::Excluded(_)) => Some(BoundOrdering::Excluded(false)),
279 (Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(true)),
280 }
281}
282
283pub(crate) fn direct_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, start: bool) -> BoundOrdering
284where
285 T: Measure + PartialEnum + Ord,
286{
287 let included_ord = if start {
288 Ordering::Greater
289 } else {
290 Ordering::Less
291 };
292
293 match (b1, b2) {
294 (Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
295 Ordering::Equal => BoundOrdering::Included(true),
296 ord if ord == included_ord => BoundOrdering::Included(false),
297 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
298 },
299 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
300 Ordering::Equal => BoundOrdering::Excluded(true),
301 ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
302 _ => BoundOrdering::Excluded(false),
303 },
304 (Bound::Included(v1), Bound::Unbounded) => BoundOrdering::Included(
305 (start && Some(v1) == <T as MaybeBounded>::min().as_ref())
306 || (!start && Some(v1) == <T as MaybeBounded>::max().as_ref()),
307 ),
308 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
309 Ordering::Equal => BoundOrdering::Included(false),
310 ord if ord == included_ord => BoundOrdering::Included(false),
311 _ => match dist(v1, v2) {
312 Dist::Zero => BoundOrdering::Included(true),
313 Dist::One => BoundOrdering::Excluded(true),
314 _ => BoundOrdering::Excluded(false),
315 },
316 },
317 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
318 Ordering::Equal => BoundOrdering::Included(true),
319 ord if ord == included_ord => BoundOrdering::Included(false),
320 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
321 },
322 (Bound::Excluded(_), Bound::Unbounded) => BoundOrdering::Included(false),
323 (Bound::Unbounded, Bound::Included(v2)) => {
324 if (start && <T as MaybeBounded>::min().as_ref() == Some(v2))
325 || (!start && <T as MaybeBounded>::max().as_ref() == Some(v2))
326 {
327 BoundOrdering::Included(true)
328 } else {
329 BoundOrdering::Excluded(
330 (start
331 && v2
332 .pred()
333 .map(|pred| <T as MaybeBounded>::min() == Some(pred))
334 .unwrap_or(false)) || (!start
335 && v2
336 .succ()
337 .map(|succ| <T as MaybeBounded>::min() == Some(succ))
338 .unwrap_or(false)),
339 )
340 }
341 }
342 (Bound::Unbounded, Bound::Excluded(_)) => BoundOrdering::Excluded(false),
343 (Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(true),
344 }
345}
346
347pub(crate) fn direct_bound_partial_eq<T, U>(b1: Bound<&T>, b2: Bound<&U>, start: bool) -> bool
348where
349 T: Measure<U> + PartialOrd<U> + PartialEnum,
350 U: PartialEnum,
351{
352 match direct_bound_partial_cmp(b1, b2, start) {
353 Some(BoundOrdering::Included(eq)) => eq,
354 _ => false,
355 }
356}
357
358pub(crate) fn inverse_bound_partial_cmp<T, U>(
366 b1: Bound<&T>,
367 b2: Bound<&U>,
368 b2_start: bool,
369) -> Option<BoundOrdering>
370where
371 T: Measure<U> + PartialOrd<U> + PartialEnum,
372 U: PartialEnum,
373{
374 let included_ord = if b2_start {
375 Ordering::Greater
376 } else {
377 Ordering::Less
378 };
379
380 match (b1, b2) {
381 (Bound::Included(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
382 Some(Ordering::Equal) => Some(BoundOrdering::Included(true)),
383 Some(ord) if ord == included_ord => Some(BoundOrdering::Included(false)),
384 Some(_) => Some(BoundOrdering::Excluded(distance_zero(v1, v2))),
385 None => None,
386 },
387 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
388 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)),
389 Some(ord) if ord == included_ord => {
390 Some(BoundOrdering::Included(distance_zero(v1, v2)))
391 }
392 Some(_) => Some(BoundOrdering::Excluded(false)),
393 None => None,
394 },
395 (Bound::Included(_), Bound::Unbounded) => Some(BoundOrdering::Included(false)),
396 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.partial_cmp(v2) {
397 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(true)), Some(ord) if ord == included_ord => {
399 Some(BoundOrdering::Included(distance_zero(v1, v2)))
400 }
401 Some(_) => Some(BoundOrdering::Excluded(false)),
402 None => None,
403 },
404 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.partial_cmp(v2) {
405 Some(Ordering::Equal) => Some(BoundOrdering::Excluded(false)),
406 Some(ord) if ord == included_ord => match dist(v1, v2) {
407 Dist::Zero => Some(BoundOrdering::Excluded(true)), Dist::One => Some(BoundOrdering::Included(true)), _ => Some(BoundOrdering::Included(false)), },
411 Some(_) => Some(BoundOrdering::Excluded(false)), None => None,
413 },
414 (Bound::Excluded(v1), Bound::Unbounded) => {
415 if (!b2_start && U::max().map(|m| *v1 == m).unwrap_or(false))
416 || (b2_start && U::min().map(|m| *v1 == m).unwrap_or(false))
417 {
418 Some(BoundOrdering::Excluded(true))
419 } else {
420 Some(BoundOrdering::Included(
421 (!b2_start
422 && v1
423 .pred()
424 .map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
425 .unwrap_or(false)) || (b2_start
426 && v1
427 .succ()
428 .map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
429 .unwrap_or(false)),
430 ))
431 }
432 }
433 (Bound::Unbounded, Bound::Included(_)) => Some(BoundOrdering::Included(false)),
434 (Bound::Unbounded, Bound::Excluded(v2)) => {
435 if (!b2_start && U::min().map(|m| *v2 == m).unwrap_or(false))
436 || (b2_start && U::max().map(|m| *v2 == m).unwrap_or(false))
437 {
438 Some(BoundOrdering::Excluded(true))
439 } else {
440 Some(BoundOrdering::Included(
441 (!b2_start
442 && v2
443 .pred()
444 .map(|pred| U::min().map(|m| pred == m).unwrap_or(false))
445 .unwrap_or(false)) || (b2_start
446 && v2
447 .succ()
448 .map(|succ| U::max().map(|m| succ == m).unwrap_or(false))
449 .unwrap_or(false)),
450 ))
451 }
452 }
453 (Bound::Unbounded, Bound::Unbounded) => Some(BoundOrdering::Included(false)),
454 }
455}
456
457pub(crate) fn inverse_bound_cmp<T>(b1: Bound<&T>, b2: Bound<&T>, b2_start: bool) -> BoundOrdering
458where
459 T: Measure + Ord + PartialEnum,
460{
461 let included_ord = if b2_start {
462 Ordering::Greater
463 } else {
464 Ordering::Less
465 };
466
467 match (b1, b2) {
468 (Bound::Included(v1), Bound::Included(v2)) => match v1.cmp(v2) {
469 Ordering::Equal => BoundOrdering::Included(true),
470 ord if ord == included_ord => BoundOrdering::Included(false),
471 _ => BoundOrdering::Excluded(distance_zero(v1, v2)),
472 },
473 (Bound::Included(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
474 Ordering::Equal => BoundOrdering::Excluded(true),
475 ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
476 _ => BoundOrdering::Excluded(false),
477 },
478 (Bound::Included(_), Bound::Unbounded) => BoundOrdering::Included(false),
479 (Bound::Excluded(v1), Bound::Included(v2)) => match v1.cmp(v2) {
480 Ordering::Equal => BoundOrdering::Excluded(true), ord if ord == included_ord => BoundOrdering::Included(distance_zero(v1, v2)),
482 _ => BoundOrdering::Excluded(false),
483 },
484 (Bound::Excluded(v1), Bound::Excluded(v2)) => match v1.cmp(v2) {
485 Ordering::Equal => BoundOrdering::Excluded(false),
486 ord if ord == included_ord => match dist(v1, v2) {
487 Dist::Zero => BoundOrdering::Excluded(true), Dist::One => BoundOrdering::Included(true), _ => BoundOrdering::Included(false), },
491 _ => BoundOrdering::Excluded(false), },
493 (Bound::Excluded(v1), Bound::Unbounded) => {
494 if (!b2_start
495 && <T as MaybeBounded>::max()
496 .map(|m| *v1 == m)
497 .unwrap_or(false))
498 || (b2_start
499 && <T as MaybeBounded>::min()
500 .map(|m| *v1 == m)
501 .unwrap_or(false))
502 {
503 BoundOrdering::Excluded(true)
504 } else {
505 BoundOrdering::Included(
506 (!b2_start
507 && v1
508 .pred()
509 .map(|pred| {
510 <T as MaybeBounded>::min()
511 .map(|m| pred == m)
512 .unwrap_or(false)
513 })
514 .unwrap_or(false)) || (b2_start
515 && v1
516 .succ()
517 .map(|succ| {
518 <T as MaybeBounded>::max()
519 .map(|m| succ == m)
520 .unwrap_or(false)
521 })
522 .unwrap_or(false)),
523 )
524 }
525 }
526 (Bound::Unbounded, Bound::Included(_)) => BoundOrdering::Included(false),
527 (Bound::Unbounded, Bound::Excluded(v2)) => {
528 if (!b2_start
529 && <T as MaybeBounded>::min()
530 .map(|m| *v2 == m)
531 .unwrap_or(false))
532 || (b2_start
533 && <T as MaybeBounded>::max()
534 .map(|m| *v2 == m)
535 .unwrap_or(false))
536 {
537 BoundOrdering::Excluded(true)
538 } else {
539 BoundOrdering::Included(
540 (!b2_start
541 && v2
542 .pred()
543 .map(|pred| {
544 <T as MaybeBounded>::min()
545 .map(|m| pred == m)
546 .unwrap_or(false)
547 })
548 .unwrap_or(false)) || (b2_start
549 && v2
550 .succ()
551 .map(|succ| {
552 <T as MaybeBounded>::max()
553 .map(|m| succ == m)
554 .unwrap_or(false)
555 })
556 .unwrap_or(false)),
557 )
558 }
559 }
560 (Bound::Unbounded, Bound::Unbounded) => BoundOrdering::Included(false),
561 }
562}
563
564