range_ranger/
continuous.rs

1use std::{
2    borrow::Borrow,
3    cmp::Ordering,
4    fmt,
5    ops::{self, Bound},
6};
7
8use crate::RangesRelation;
9
10#[allow(clippy::derive_partial_eq_without_eq)]
11#[derive(Clone, Hash, PartialEq)]
12pub enum ContinuousRange<Idx> {
13    /// A range containing no value
14    ///
15    /// `[]`
16    Empty,
17
18    /// A range containing a single value
19    ///
20    /// `value`
21    Single(Idx),
22
23    /// A range between `start` (inclusive) and `end` (inclusive)
24    ///
25    /// `[start..end]`
26    Inclusive(Idx, Idx),
27
28    /// A range between `start` (exclusive) and `end` (exclusive)
29    ///
30    /// `(start..end)`
31    Exclusive(Idx, Idx),
32
33    /// A range between `start` (exclusive) and `end` (inclusive)
34    ///
35    /// `(start..end]`
36    StartExclusive(Idx, Idx),
37
38    /// A range between `start` (inclusive) and `end` (exclusive)
39    ///
40    /// `[start..end)`
41    EndExclusive(Idx, Idx),
42
43    /// A range starting from `start` (inclusive)
44    ///
45    /// `[start..)`
46    From(Idx),
47
48    /// A range starting from `start` (exclusive)
49    ///
50    /// `(start..)`
51    FromExclusive(Idx),
52
53    /// A range ending with `end` (inclusive)
54    ///
55    /// `(..end]`
56    To(Idx),
57
58    /// A range ending with `end` (exclusive)
59    ///
60    /// `(..end)`
61    ToExclusive(Idx),
62
63    /// A range containing all values
64    Full,
65}
66
67#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
68enum BoundSide {
69    Start,
70    End,
71}
72
73fn partial_cmp_bounds<Idx: PartialOrd>(
74    this: &Bound<&Idx>,
75    this_side: BoundSide,
76    other: &Bound<&Idx>,
77    other_side: BoundSide,
78) -> Option<Ordering> {
79    match this {
80        Bound::Included(this_value) => match other {
81            Bound::Included(other_value) => this_value.partial_cmp(other_value),
82            Bound::Excluded(other_value) => match this_value.partial_cmp(other_value) {
83                Some(Ordering::Equal) => match (this_side, other_side) {
84                    (BoundSide::Start, BoundSide::Start) => Some(Ordering::Less),
85                    (BoundSide::End, BoundSide::End) => Some(Ordering::Greater),
86                    (BoundSide::Start, BoundSide::End) => Some(Ordering::Greater),
87                    (BoundSide::End, BoundSide::Start) => Some(Ordering::Less),
88                },
89                other => other,
90            },
91            Bound::Unbounded => match other_side {
92                BoundSide::Start => Some(Ordering::Greater), // -Inf
93                BoundSide::End => Some(Ordering::Less),      // +Inf
94            },
95        },
96        Bound::Excluded(this_value) => match other {
97            Bound::Included(other_value) => match this_value.partial_cmp(other_value) {
98                Some(Ordering::Equal) => match (this_side, other_side) {
99                    (BoundSide::Start, BoundSide::Start) => Some(Ordering::Greater),
100                    (BoundSide::End, BoundSide::End) => Some(Ordering::Less),
101                    (BoundSide::Start, BoundSide::End) => Some(Ordering::Greater),
102                    (BoundSide::End, BoundSide::Start) => Some(Ordering::Less),
103                },
104                other => other,
105            },
106            Bound::Excluded(other_value) => match this_value.partial_cmp(other_value) {
107                Some(Ordering::Equal) => match (this_side, other_side) {
108                    (BoundSide::Start, BoundSide::Start) => Some(Ordering::Equal),
109                    (BoundSide::End, BoundSide::End) => Some(Ordering::Equal),
110                    (BoundSide::Start, BoundSide::End) => Some(Ordering::Greater),
111                    (BoundSide::End, BoundSide::Start) => Some(Ordering::Less),
112                },
113                other => other,
114            },
115            Bound::Unbounded => match other_side {
116                BoundSide::Start => Some(Ordering::Greater), // -Inf
117                BoundSide::End => Some(Ordering::Less),      // +Inf
118            },
119        },
120        Bound::Unbounded => match other {
121            Bound::Included(_) | Bound::Excluded(_) => match this_side {
122                BoundSide::Start => Some(Ordering::Less),  // -Inf
123                BoundSide::End => Some(Ordering::Greater), // +Inf
124            },
125            Bound::Unbounded => match (this_side, other_side) {
126                (BoundSide::Start, BoundSide::Start) => Some(Ordering::Equal),
127                (BoundSide::End, BoundSide::End) => Some(Ordering::Equal),
128                (BoundSide::Start, BoundSide::End) => Some(Ordering::Less),
129                (BoundSide::End, BoundSide::Start) => Some(Ordering::Greater),
130            },
131        },
132    }
133}
134
135/// Get the Internal value of a bound or panics if [Unbounded][Bound::Unbounded].
136fn expect_bound<'a, Idx>(bound: Option<Bound<&'a Idx>>, msg: &'static str) -> &'a Idx {
137    match bound.expect(msg) {
138        Bound::Included(x) => x,
139        Bound::Excluded(x) => x,
140        Bound::Unbounded => panic!("{}", msg),
141    }
142}
143
144/// Reverse a bound between [`Bound::Included`] and [`Bound::Excluded`].
145///
146/// [`Bound::Unbounded`] is kept as-is.
147fn reverse_bound<Idx>(bound: Bound<&Idx>) -> Bound<&Idx> {
148    match bound {
149        Bound::Included(x) => Bound::Excluded(x),
150        Bound::Excluded(x) => Bound::Included(x),
151        Bound::Unbounded => Bound::Unbounded,
152    }
153}
154
155impl<Idx: PartialOrd + Clone> ContinuousRange<Idx> {
156    /// A range containing no value
157    ///
158    /// `[]`
159    #[must_use]
160    pub fn empty() -> ContinuousRange<Idx> {
161        ContinuousRange::Empty
162    }
163
164    /// A range containing a single value
165    ///
166    /// `value`
167    #[must_use]
168    pub fn single(value: Idx) -> ContinuousRange<Idx> {
169        ContinuousRange::Single(value)
170    }
171
172    /// A range between `start` (inclusive) and `end` (inclusive)
173    ///
174    /// `[start..end]`
175    #[must_use]
176    pub fn inclusive(start: Idx, end: Idx) -> ContinuousRange<Idx>
177    where
178        Idx: PartialOrd,
179    {
180        if start == end {
181            ContinuousRange::Single(start)
182        } else if start > end {
183            ContinuousRange::Empty
184        } else {
185            ContinuousRange::Inclusive(start, end)
186        }
187    }
188
189    /// A range between `start` (exclusive) and `end` (exclusive)
190    ///
191    /// `(start..end)`
192    #[must_use]
193    pub fn exclusive(start: Idx, end: Idx) -> ContinuousRange<Idx>
194    where
195        Idx: PartialOrd,
196    {
197        if start >= end {
198            ContinuousRange::Empty
199        } else {
200            ContinuousRange::Exclusive(start, end)
201        }
202    }
203
204    /// A range between `start` (exclusive) and `end` (inclusive)
205    ///
206    /// `(start..end]`
207    #[must_use]
208    pub fn start_exclusive(start: Idx, end: Idx) -> ContinuousRange<Idx>
209    where
210        Idx: PartialOrd,
211    {
212        if start >= end {
213            ContinuousRange::Empty
214        } else {
215            ContinuousRange::StartExclusive(start, end)
216        }
217    }
218
219    /// A range between `start` (inclusive) and `end` (exclusive)
220    ///
221    /// `[start..end)`
222    #[must_use]
223    pub fn end_exclusive(start: Idx, end: Idx) -> ContinuousRange<Idx>
224    where
225        Idx: PartialOrd,
226    {
227        if start >= end {
228            ContinuousRange::Empty
229        } else {
230            ContinuousRange::EndExclusive(start, end)
231        }
232    }
233
234    /// A range starting from `start` (inclusive)
235    ///
236    /// `[start..)`
237    #[must_use]
238    pub fn from(start: Idx) -> ContinuousRange<Idx> {
239        ContinuousRange::From(start)
240    }
241
242    /// A range starting from `start` (exclusive)
243    ///
244    /// `(start..)`
245    #[must_use]
246    pub fn from_exclusive(start: Idx) -> ContinuousRange<Idx> {
247        ContinuousRange::FromExclusive(start)
248    }
249
250    /// A range ending with `end` (inclusive)
251    ///
252    /// `(..end]`
253    #[must_use]
254    pub fn to(end: Idx) -> ContinuousRange<Idx> {
255        ContinuousRange::To(end)
256    }
257
258    /// A range ending with `end` (exclusive)
259    ///
260    /// `(..end)`
261    #[must_use]
262    pub fn to_exclusive(end: Idx) -> ContinuousRange<Idx> {
263        ContinuousRange::ToExclusive(end)
264    }
265
266    /// A range containing all values
267    #[must_use]
268    pub fn full() -> ContinuousRange<Idx> {
269        ContinuousRange::Full
270    }
271
272    /// Create a new range from the specified bounds
273    #[must_use]
274    pub fn from_bounds(bounds: (Bound<&Idx>, Bound<&Idx>)) -> Self {
275        match bounds {
276            (Bound::Unbounded, Bound::Unbounded) => Self::full(),
277            (Bound::Included(start), Bound::Included(end)) => {
278                Self::inclusive(start.clone(), end.clone())
279            }
280            (Bound::Included(start), Bound::Excluded(end)) => {
281                Self::end_exclusive(start.clone(), end.clone())
282            }
283            (Bound::Included(start), Bound::Unbounded) => Self::from(start.clone()),
284            (Bound::Excluded(start), Bound::Included(end)) => {
285                Self::start_exclusive(start.clone(), end.clone())
286            }
287            (Bound::Excluded(start), Bound::Excluded(end)) => {
288                Self::exclusive(start.clone(), end.clone())
289            }
290            (Bound::Excluded(start), Bound::Unbounded) => Self::from_exclusive(start.clone()),
291            (Bound::Unbounded, Bound::Included(end)) => Self::to(end.clone()),
292            (Bound::Unbounded, Bound::Excluded(end)) => Self::to_exclusive(end.clone()),
293        }
294    }
295
296    /// Get the bounds of the range or [None] if empty
297    #[must_use]
298    pub fn range_bounds(&self) -> Option<(Bound<&Idx>, Bound<&Idx>)> {
299        match self {
300            Self::Empty => {
301                // We can't implement RangeBounds due to this case, a possible trick would be to use a range with 2
302                // exclusive bounds on the default value.
303                // But as the result is a reference we would need a per-generic 'static to reference and so would
304                // require something like the 'typemap' crate just for that.
305                None
306            }
307            Self::Single(value) => Some((Bound::Included(value), Bound::Included(value))),
308            Self::Inclusive(start, end) => Some((Bound::Included(start), Bound::Included(end))),
309            Self::Exclusive(start, end) => Some((Bound::Excluded(start), Bound::Excluded(end))),
310            Self::StartExclusive(start, end) => {
311                Some((Bound::Excluded(start), Bound::Included(end)))
312            }
313            Self::EndExclusive(start, end) => Some((Bound::Included(start), Bound::Excluded(end))),
314            Self::From(start) => Some((Bound::Included(start), Bound::Unbounded)),
315            Self::FromExclusive(start) => Some((Bound::Excluded(start), Bound::Unbounded)),
316            Self::To(end) => Some((Bound::Unbounded, Bound::Included(end))),
317            Self::ToExclusive(end) => Some((Bound::Unbounded, Bound::Excluded(end))),
318            Self::Full => Some((Bound::Unbounded, Bound::Unbounded)),
319        }
320    }
321
322    #[must_use]
323    pub fn start(&self) -> Option<Bound<&Idx>> {
324        match self {
325            Self::Empty => None,
326            Self::Single(value) => Some(Bound::Included(value)),
327            Self::Inclusive(start, _) => Some(Bound::Included(start)),
328            Self::Exclusive(start, _) => Some(Bound::Excluded(start)),
329            Self::StartExclusive(start, _) => Some(Bound::Excluded(start)),
330            Self::EndExclusive(start, _) => Some(Bound::Included(start)),
331            Self::From(start) => Some(Bound::Included(start)),
332            Self::FromExclusive(start) => Some(Bound::Excluded(start)),
333            Self::To(_) | Self::ToExclusive(_) | Self::Full => Some(Bound::Unbounded),
334        }
335    }
336
337    #[must_use]
338    pub fn end(&self) -> Option<Bound<&Idx>> {
339        match self {
340            Self::Empty => None,
341            Self::Single(value) => Some(Bound::Included(value)),
342            Self::Inclusive(_, end) => Some(Bound::Included(end)),
343            Self::Exclusive(_, end) => Some(Bound::Excluded(end)),
344            Self::StartExclusive(_, end) => Some(Bound::Included(end)),
345            Self::EndExclusive(_, end) => Some(Bound::Excluded(end)),
346            Self::To(end) => Some(Bound::Included(end)),
347            Self::ToExclusive(end) => Some(Bound::Excluded(end)),
348            Self::From(_) | Self::FromExclusive(_) | Self::Full => Some(Bound::Unbounded),
349        }
350    }
351
352    /// Check if the range contains the provide value
353    #[must_use]
354    pub fn contains(&self, value: impl Borrow<Idx>) -> bool
355    where
356        Idx: PartialOrd + PartialEq,
357    {
358        match self {
359            Self::Empty => false,
360            Self::Single(single_value) => single_value == value.borrow(),
361            Self::Inclusive(start, end) => {
362                let value = value.borrow();
363                value >= start && value <= end
364            }
365            Self::Exclusive(start, end) => {
366                let value = value.borrow();
367                value > start && value < end
368            }
369            Self::StartExclusive(start, end) => {
370                let value = value.borrow();
371                value > start && value <= end
372            }
373            Self::EndExclusive(start, end) => {
374                let value = value.borrow();
375                value >= start && value < end
376            }
377            Self::From(start) => value.borrow() >= start,
378            Self::FromExclusive(start) => value.borrow() > start,
379            Self::To(end) => value.borrow() <= end,
380            Self::ToExclusive(end) => value.borrow() < end,
381            Self::Full => true,
382        }
383    }
384
385    #[must_use]
386    pub fn contains_range(&self, other: &ContinuousRange<Idx>) -> bool
387    where
388        Idx: std::fmt::Debug,
389    {
390        self.compare(other).map_or(false, |r| r.contains())
391    }
392
393    #[must_use]
394    pub fn disjoint_from_range(&self, other: &ContinuousRange<Idx>) -> bool
395    where
396        Idx: std::fmt::Debug,
397    {
398        self.compare(other).map_or(true, |r| r.disjoint())
399    }
400
401    #[must_use]
402    #[allow(clippy::missing_panics_doc)]
403    pub fn union(&self, other: &ContinuousRange<Idx>) -> Option<ContinuousRange<Idx>>
404    where
405        Idx: PartialOrd + std::fmt::Debug,
406    {
407        match (self, other) {
408            (ContinuousRange::Empty, r) | (r, ContinuousRange::Empty) => Some(r.clone()),
409            (ContinuousRange::Full, _) | (_, ContinuousRange::Full) => Some(ContinuousRange::Full),
410            _ => match self.compare(other) {
411                Some(cmp) => match cmp {
412                    RangesRelation::StrictlyBefore => None,
413                    RangesRelation::StrictlyAfter => None,
414                    RangesRelation::Meets => {
415                        let start = self.start().expect("Self meets without bounds");
416                        let end = other.end().expect("Other meets without bounds");
417                        Some(ContinuousRange::from_bounds((start, end)))
418                    }
419                    RangesRelation::IsMet => {
420                        let end = self.end().expect("Self meets without bounds");
421                        let start = other.start().expect("Other meets without bounds");
422                        Some(ContinuousRange::from_bounds((start, end)))
423                    }
424                    RangesRelation::Overlaps => {
425                        let start = self.start().expect("Self meets without bounds");
426                        let end = other.end().expect("Other meets without bounds");
427                        Some(ContinuousRange::from_bounds((start, end)))
428                    }
429                    RangesRelation::IsOverlapped => {
430                        let end = self.end().expect("Self meets without bounds");
431                        let start = other.start().expect("Other meets without bounds");
432                        Some(ContinuousRange::from_bounds((start, end)))
433                    }
434                    RangesRelation::Starts => Some(other.clone()),
435                    RangesRelation::IsStarted => Some(self.clone()),
436                    RangesRelation::StrictlyContains => Some(self.clone()),
437                    RangesRelation::IsStrictlyContained => Some(other.clone()),
438                    RangesRelation::Finishes => Some(other.clone()),
439                    RangesRelation::IsFinished => Some(self.clone()),
440                    RangesRelation::Equal => Some(self.clone()),
441                },
442                None => None,
443            },
444        }
445    }
446
447    #[must_use]
448    #[allow(clippy::missing_panics_doc)]
449    pub fn intersection(&self, other: &ContinuousRange<Idx>) -> ContinuousRange<Idx>
450    where
451        Idx: PartialOrd + std::fmt::Debug,
452    {
453        match (self, other) {
454            (ContinuousRange::Empty, _) | (_, ContinuousRange::Empty) => ContinuousRange::Empty,
455            (ContinuousRange::Full, r) | (r, ContinuousRange::Full) => r.clone(),
456            _ => match self.compare(other) {
457                Some(cmp) => match cmp {
458                    RangesRelation::StrictlyBefore => ContinuousRange::Empty,
459                    RangesRelation::StrictlyAfter => ContinuousRange::Empty,
460                    RangesRelation::Meets => {
461                        let end = expect_bound(self.end(), "Self meets without end bound");
462                        ContinuousRange::single(end.clone())
463                    }
464                    RangesRelation::IsMet => {
465                        let start = expect_bound(self.start(), "Self is met without start bound");
466                        ContinuousRange::single(start.clone())
467                    }
468                    RangesRelation::Overlaps => {
469                        let (_, end) = self.range_bounds().expect("Self overlaps without bounds");
470                        let (start, _) = other
471                            .range_bounds()
472                            .expect("Other is overlapped without bounds");
473                        ContinuousRange::from_bounds((start, end))
474                    }
475                    RangesRelation::IsOverlapped => {
476                        let (start, _) = self
477                            .range_bounds()
478                            .expect("Self is overlapped without bounds");
479                        let (_, end) = other.range_bounds().expect("Other overlaps without bounds");
480                        ContinuousRange::from_bounds((start, end))
481                    }
482                    RangesRelation::Starts => self.clone(),
483                    RangesRelation::IsStarted => other.clone(),
484
485                    RangesRelation::StrictlyContains => other.clone(),
486                    RangesRelation::IsStrictlyContained => self.clone(),
487                    RangesRelation::Finishes => self.clone(),
488                    RangesRelation::IsFinished => other.clone(),
489                    RangesRelation::Equal => self.clone(),
490                },
491                None => ContinuousRange::Empty,
492            },
493        }
494    }
495
496    #[must_use]
497    #[allow(clippy::missing_panics_doc)]
498    pub fn difference(&self, other: &ContinuousRange<Idx>) -> Option<ContinuousRange<Idx>>
499    where
500        Idx: PartialOrd + std::fmt::Debug,
501    {
502        match (self, other) {
503            (ContinuousRange::Empty, r) => Some(r.clone()),
504            (_, ContinuousRange::Empty) => Some(ContinuousRange::Empty),
505            _ => match self.compare(other) {
506                Some(cmp) => match cmp {
507                    RangesRelation::StrictlyBefore => Some(self.clone()),
508                    RangesRelation::StrictlyAfter => Some(self.clone()),
509                    RangesRelation::Equal => Some(ContinuousRange::Empty),
510                    RangesRelation::IsStrictlyContained => Some(ContinuousRange::Empty),
511                    RangesRelation::StrictlyContains => None,
512
513                    RangesRelation::Meets => {
514                        let (start, end) = self.range_bounds().expect("Self meets without bounds");
515                        let end = reverse_bound(end);
516                        Some(ContinuousRange::from_bounds((start, end)))
517                    }
518                    RangesRelation::IsMet => {
519                        let (start, end) = self.range_bounds().expect("Self is met without bounds");
520                        let start = reverse_bound(start);
521                        Some(ContinuousRange::from_bounds((start, end)))
522                    }
523                    RangesRelation::Overlaps => {
524                        let start = self.start().expect("Self overlaps without bounds");
525                        let end = other.start().expect("Other is overlapped without bounds");
526                        let end = reverse_bound(end);
527                        Some(ContinuousRange::from_bounds((start, end)))
528                    }
529                    RangesRelation::IsOverlapped => {
530                        let end = self.end().expect("Self is overlapped without bounds");
531                        let start = other.end().expect("Other overlaps without bounds");
532                        let start = reverse_bound(start);
533                        Some(ContinuousRange::from_bounds((start, end)))
534                    }
535                    RangesRelation::Starts => Some(ContinuousRange::Empty),
536                    RangesRelation::IsStarted => {
537                        let end = self.end().expect("Self is overlapped without bounds");
538                        let start = other.end().expect("Other overlaps without bounds");
539                        let start = reverse_bound(start);
540                        Some(ContinuousRange::from_bounds((start, end)))
541                    }
542                    RangesRelation::Finishes => Some(ContinuousRange::Empty),
543                    RangesRelation::IsFinished => {
544                        let start = self.start().expect("Self overlaps without bounds");
545                        let end = other.start().expect("Other is overlapped without bounds");
546                        let end = reverse_bound(end);
547                        Some(ContinuousRange::from_bounds((start, end)))
548                    }
549                },
550                None => None,
551            },
552        }
553    }
554
555    #[must_use]
556    pub fn intersects(&self, other: &ContinuousRange<Idx>) -> bool
557    where
558        Idx: std::fmt::Debug,
559    {
560        // Two empty ranges are 'equal' but we don't want to return true for them
561        if self.is_empty() && other.is_empty() {
562            false
563        } else {
564            match self.compare(other) {
565                Some(relation) => relation.intersects(),
566                None => false,
567            }
568        }
569    }
570
571    #[must_use]
572    /// Compare the bounds of two ranges. Returns [`Option::None`] if an empty range is
573    /// compared to a non-empty range.
574    ///
575    /// # Panics
576    ///
577    /// This function may panic if the [`PartialOrd`] contract isn't respected.
578    pub fn compare(&self, other: &ContinuousRange<Idx>) -> Option<RangesRelation>
579    where
580        Idx: std::fmt::Debug,
581    {
582        // Inspired by "Maintaining Knowledge about Temporal Intervals" by James F. Allen
583        // Communications of the ACM - November 1983 - Volume 26 - Number 11
584
585        // Empty ranges don't have bounds so we need to special case them before anything else
586        if self.is_empty() {
587            return if other.is_empty() {
588                Some(RangesRelation::Equal)
589            } else {
590                None
591            };
592        } else if other.is_empty() {
593            return None;
594        }
595
596        let (self_start, self_end) = self
597            .range_bounds()
598            .expect("Non-empty self should have bounds");
599        let (other_start, other_end) = other
600            .range_bounds()
601            .expect("Non-empty other should have bounds");
602
603        let cmp_end_start =
604            partial_cmp_bounds(&self_end, BoundSide::End, &other_start, BoundSide::Start)?;
605
606        if cmp_end_start == Ordering::Less {
607            return Some(RangesRelation::StrictlyBefore);
608        }
609
610        let cmp_start_end =
611            partial_cmp_bounds(&self_start, BoundSide::Start, &other_end, BoundSide::End)?;
612
613        if cmp_start_end == Ordering::Greater {
614            return Some(RangesRelation::StrictlyAfter);
615        }
616
617        let self_cmp =
618            partial_cmp_bounds(&self_start, BoundSide::Start, &self_end, BoundSide::End)?;
619
620        let other_cmp =
621            partial_cmp_bounds(&other_start, BoundSide::Start, &other_end, BoundSide::End)?;
622
623        if cmp_end_start == Ordering::Equal
624            && self_cmp != Ordering::Equal
625            && other_cmp != Ordering::Equal
626        {
627            return Some(RangesRelation::Meets);
628        }
629        if cmp_start_end == Ordering::Equal
630            && self_cmp != Ordering::Equal
631            && other_cmp != Ordering::Equal
632        {
633            return Some(RangesRelation::IsMet);
634        }
635
636        let cmp_start_start = partial_cmp_bounds(
637            &self_start,
638            BoundSide::Start,
639            &other_start,
640            BoundSide::Start,
641        )?;
642
643        let cmp_end_end =
644            partial_cmp_bounds(&self_end, BoundSide::End, &other_end, BoundSide::End)?;
645
646        if cmp_start_start == Ordering::Less
647            && cmp_end_start == Ordering::Greater
648            && cmp_end_end == Ordering::Less
649        {
650            return Some(RangesRelation::Overlaps);
651        }
652        if cmp_start_start == Ordering::Greater
653            && cmp_start_end == Ordering::Less
654            && cmp_end_end == Ordering::Greater
655        {
656            return Some(RangesRelation::IsOverlapped);
657        }
658        if cmp_start_start == Ordering::Equal && cmp_end_end == Ordering::Less {
659            return Some(RangesRelation::Starts);
660        }
661        if cmp_start_start == Ordering::Equal && cmp_end_end == Ordering::Greater {
662            return Some(RangesRelation::IsStarted);
663        }
664        if cmp_start_start == Ordering::Greater && cmp_end_end == Ordering::Equal {
665            return Some(RangesRelation::Finishes);
666        }
667        if cmp_start_start == Ordering::Less && cmp_end_end == Ordering::Equal {
668            return Some(RangesRelation::IsFinished);
669        }
670        if cmp_start_start == Ordering::Less && cmp_end_end == Ordering::Greater {
671            return Some(RangesRelation::StrictlyContains);
672        }
673        if cmp_start_start == Ordering::Greater && cmp_end_end == Ordering::Less {
674            return Some(RangesRelation::IsStrictlyContained);
675        }
676        if cmp_start_start == Ordering::Equal && cmp_end_end == Ordering::Equal {
677            return Some(RangesRelation::Equal);
678        }
679
680        // Should be unreachable if PartialOrd contract is correctly implemented
681        panic!(
682            r"PartialOrd contract isn't correctly implemented.
683No ordering can be found between {self:?} and {other:?}",
684            self = &self,
685            other = &other
686        );
687    }
688
689    pub fn simplify_mut(&mut self)
690    where
691        Idx: PartialOrd,
692    {
693        match self {
694            ContinuousRange::Inclusive(start, end) => {
695                if start == end {
696                    *self = ContinuousRange::Single(start.clone());
697                } else if start > end {
698                    *self = ContinuousRange::Empty;
699                }
700            }
701            ContinuousRange::Exclusive(start, end)
702            | ContinuousRange::StartExclusive(start, end)
703            | ContinuousRange::EndExclusive(start, end) => {
704                if start >= end {
705                    *self = ContinuousRange::Empty;
706                }
707            }
708            ContinuousRange::Empty
709            | ContinuousRange::Single(_)
710            | ContinuousRange::From(_)
711            | ContinuousRange::FromExclusive(_)
712            | ContinuousRange::To(_)
713            | ContinuousRange::ToExclusive(_)
714            | ContinuousRange::Full => {}
715        }
716    }
717
718    #[must_use]
719    pub fn simplify(&self) -> Self
720    where
721        Idx: PartialOrd,
722    {
723        let mut clone = (*self).clone();
724        clone.simplify_mut();
725        clone
726    }
727
728    #[must_use]
729    pub fn is_empty(&self) -> bool {
730        match self {
731            Self::Empty => true,
732
733            // bounded ranges with inverted bounds are considered empty
734            Self::Inclusive(start, end) => start > end,
735            Self::Exclusive(start, end)
736            | Self::StartExclusive(start, end)
737            | Self::EndExclusive(start, end) => start >= end,
738
739            Self::Single(_)
740
741            // unbounded ranges can't be empty
742            | Self::From(_)
743            | Self::FromExclusive(_)
744            | Self::To(_)
745            | Self::ToExclusive(_)
746            | Self::Full => false,
747        }
748    }
749
750    #[must_use]
751    pub fn is_full(&self) -> bool {
752        match self {
753            Self::Full => true,
754            _ => false,
755        }
756    }
757}
758
759impl<Idx: PartialOrd + Clone> From<()> for ContinuousRange<Idx> {
760    fn from(_: ()) -> Self {
761        Self::empty()
762    }
763}
764
765impl<Idx: PartialOrd + Clone> From<ops::RangeFull> for ContinuousRange<Idx> {
766    fn from(_: ops::RangeFull) -> Self {
767        Self::full()
768    }
769}
770
771impl<Idx: PartialOrd + Clone> From<ops::Range<Idx>> for ContinuousRange<Idx> {
772    fn from(r: ops::Range<Idx>) -> Self {
773        Self::end_exclusive(r.start, r.end)
774    }
775}
776
777impl<Idx: PartialOrd + Clone> From<ops::RangeInclusive<Idx>> for ContinuousRange<Idx> {
778    fn from(r: ops::RangeInclusive<Idx>) -> Self {
779        let (start, end) = r.into_inner();
780        Self::inclusive(start, end)
781    }
782}
783
784impl<Idx: PartialOrd + Clone> From<ops::RangeFrom<Idx>> for ContinuousRange<Idx> {
785    fn from(r: ops::RangeFrom<Idx>) -> Self {
786        Self::from(r.start)
787    }
788}
789
790impl<Idx: PartialOrd + Clone> From<ops::RangeToInclusive<Idx>> for ContinuousRange<Idx> {
791    fn from(r: ops::RangeToInclusive<Idx>) -> Self {
792        Self::to(r.end)
793    }
794}
795
796impl<Idx: PartialOrd + Clone> From<ops::RangeTo<Idx>> for ContinuousRange<Idx> {
797    fn from(r: ops::RangeTo<Idx>) -> Self {
798        Self::to_exclusive(r.end)
799    }
800}
801
802impl<Idx: fmt::Debug> fmt::Debug for ContinuousRange<Idx> {
803    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
804        match self {
805            ContinuousRange::Empty => write!(fmt, "[]")?,
806            ContinuousRange::Single(value) => value.fmt(fmt)?,
807            ContinuousRange::Full => write!(fmt, "(..)")?,
808            ContinuousRange::Inclusive(start, end) => {
809                write!(fmt, "[")?;
810                start.fmt(fmt)?;
811                write!(fmt, "..")?;
812                end.fmt(fmt)?;
813                write!(fmt, "]")?;
814            }
815            ContinuousRange::Exclusive(start, end) => {
816                write!(fmt, "(")?;
817                start.fmt(fmt)?;
818                write!(fmt, "..")?;
819                end.fmt(fmt)?;
820                write!(fmt, ")")?;
821            }
822            ContinuousRange::StartExclusive(start, end) => {
823                write!(fmt, "(")?;
824                start.fmt(fmt)?;
825                write!(fmt, "..")?;
826                end.fmt(fmt)?;
827                write!(fmt, "]")?;
828            }
829            ContinuousRange::EndExclusive(start, end) => {
830                write!(fmt, "[")?;
831                start.fmt(fmt)?;
832                write!(fmt, "..")?;
833                end.fmt(fmt)?;
834                write!(fmt, ")")?;
835            }
836            ContinuousRange::From(start) => {
837                write!(fmt, "[")?;
838                start.fmt(fmt)?;
839                write!(fmt, "..")?;
840                write!(fmt, ")")?;
841            }
842            ContinuousRange::FromExclusive(start) => {
843                write!(fmt, "(")?;
844                start.fmt(fmt)?;
845                write!(fmt, "..")?;
846                write!(fmt, ")")?;
847            }
848            ContinuousRange::To(end) => {
849                write!(fmt, "(")?;
850                write!(fmt, "..")?;
851                end.fmt(fmt)?;
852                write!(fmt, "]")?;
853            }
854            ContinuousRange::ToExclusive(end) => {
855                write!(fmt, "(")?;
856                write!(fmt, "..")?;
857                end.fmt(fmt)?;
858                write!(fmt, ")")?;
859            }
860        }
861        Ok(())
862    }
863}
864
865impl<Idx> Default for ContinuousRange<Idx> {
866    fn default() -> Self {
867        Self::Empty
868    }
869}