Skip to main content

helix_stdx/
range.rs

1//! Provides [Range] type expanding on [RangeBounds].
2
3use std::ops::{self, RangeBounds};
4
5/// A range of `char`s within the text.
6#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
7pub struct Range<T = usize> {
8    pub start: T,
9    pub end: T,
10}
11
12impl<T: PartialOrd> Range<T> {
13    pub fn contains(&self, other: Self) -> bool {
14        self.start <= other.start && other.end <= self.end
15    }
16    pub fn is_empty(&self) -> bool {
17        self.end <= self.start
18    }
19}
20
21impl<T> RangeBounds<T> for Range<T> {
22    fn start_bound(&self) -> ops::Bound<&T> {
23        ops::Bound::Included(&self.start)
24    }
25
26    fn end_bound(&self) -> ops::Bound<&T> {
27        ops::Bound::Excluded(&self.end)
28    }
29}
30
31/// Returns true if all ranges yielded by `sub_set` are contained by
32/// `super_set`. This is essentially an optimized implementation of
33/// `sub_set.all(|rb| super_set.any(|ra| ra.contains(rb)))` that runs in O(m+n)
34/// instead of O(mn) (and in many cases faster).
35///
36/// Both iterators must uphold a the following invariants:
37/// * ranges must not overlap (but they can be adjacent)
38/// * ranges must be sorted
39pub fn is_subset<const ALLOW_EMPTY: bool>(
40    mut super_set: impl Iterator<Item = Range>,
41    mut sub_set: impl Iterator<Item = Range>,
42) -> bool {
43    let (mut super_range, mut sub_range) = (super_set.next(), sub_set.next());
44    loop {
45        match (super_range, sub_range) {
46            // skip over irrelevant ranges
47            (Some(ra), Some(rb))
48                if ra.end <= rb.start && (ra.start != rb.start || !ALLOW_EMPTY) =>
49            {
50                super_range = super_set.next();
51            }
52            (Some(ra), Some(rb)) => {
53                if ra.contains(rb) {
54                    sub_range = sub_set.next();
55                } else {
56                    return false;
57                }
58            }
59            (None, Some(_)) => {
60                // exhausted `super_set`, we can't match the reminder of `sub_set`
61                return false;
62            }
63            (_, None) => {
64                // no elements from `sub_sut` left to match, `super_set` contains `sub_set`
65                return true;
66            }
67        }
68    }
69}
70
71/// Similar to is_subset but requires each element of `super_set` to be matched
72pub fn is_exact_subset(
73    mut super_set: impl Iterator<Item = Range>,
74    mut sub_set: impl Iterator<Item = Range>,
75) -> bool {
76    let (mut super_range, mut sub_range) = (super_set.next(), sub_set.next());
77    let mut super_range_matched = true;
78    loop {
79        match (super_range, sub_range) {
80            // skip over irrelevant ranges
81            (Some(ra), Some(rb)) if ra.end <= rb.start && ra.start < rb.start => {
82                if !super_range_matched {
83                    return false;
84                }
85                super_range_matched = false;
86                super_range = super_set.next();
87            }
88            (Some(ra), Some(rb)) => {
89                if ra.contains(rb) {
90                    super_range_matched = true;
91                    sub_range = sub_set.next();
92                } else {
93                    return false;
94                }
95            }
96            (None, Some(_)) => {
97                // exhausted `super_set`, we can't match the reminder of `sub_set`
98                return false;
99            }
100            (_, None) => {
101                // no elements from `sub_sut` left to match, `super_set` contains `sub_set`
102                return super_set.next().is_none();
103            }
104        }
105    }
106}