/rust/registry/src/index.crates.io-6f17d22bba15001f/wyz-0.5.1/src/range.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Range utilities. |
2 | | |
3 | | use core::ops::{ |
4 | | Bound, |
5 | | Range, |
6 | | RangeBounds, |
7 | | }; |
8 | | |
9 | | /// Extension methods for working with various range types. |
10 | | pub trait RangeExt<T>: RangeBounds<T> |
11 | | where T: Ord |
12 | | { |
13 | | /// Normalizes a range-like type to a canonical half-open `Range`. |
14 | | /// |
15 | | /// ## Parameters |
16 | | /// |
17 | | /// - `self`: The range to normalize. |
18 | | /// - `start`: An optional fallback *inclusive* lower bound. |
19 | | /// - `end`: An optional fallback *exclusive* upper bound. |
20 | | /// |
21 | | /// ## Returns |
22 | | /// |
23 | | /// A `Range` whose start and end values are the following, in order of |
24 | | /// decreasing priority: |
25 | | /// |
26 | | /// - `self.start()`, or if absent, the `start` parameter, or if it is |
27 | | /// `None`, `0`. |
28 | | /// - `self.end()`, or if absent, the `end` parameter, or if it is `None`, |
29 | | /// !0`. |
30 | | fn normalize( |
31 | | self, |
32 | | start: impl Into<Option<T>>, |
33 | | end: impl Into<Option<T>>, |
34 | | ) -> Range<T>; |
35 | | |
36 | | /// Finds the intersection between two range-likes. The produced `Range` |
37 | | /// spans only the elements common to both. |
38 | | /// |
39 | | /// This returns `None` if the ranges do not have an intersection (at least |
40 | | /// one element present in both ranges). |
41 | | fn intersection<R>(self, other: R) -> Option<Range<T>> |
42 | | where R: RangeExt<T>; |
43 | | |
44 | | /// Finds the union between two range-likes. The produced `Range` spans all |
45 | | /// elements present in at least one of them. |
46 | | /// |
47 | | /// This returns `None` if the ranges do not have an intersection (at least |
48 | | /// one element present in both ranges). |
49 | | fn union<R>(self, other: R) -> Option<Range<T>> |
50 | | where R: RangeExt<T>; |
51 | | } |
52 | | |
53 | | // TODO(myrrlyn): Use funty to extend this for all integers. |
54 | | impl<R> RangeExt<usize> for R |
55 | | where R: RangeBounds<usize> |
56 | | { |
57 | 29.8k | fn normalize( |
58 | 29.8k | self, |
59 | 29.8k | start: impl Into<Option<usize>>, |
60 | 29.8k | end: impl Into<Option<usize>>, |
61 | 29.8k | ) -> Range<usize> { |
62 | 29.8k | let start = match self.start_bound() { |
63 | 0 | Bound::Unbounded => start.into().unwrap_or(0), |
64 | 29.8k | Bound::Included(&v) => v, |
65 | 0 | Bound::Excluded(&v) => v.saturating_add(1), |
66 | | }; |
67 | 29.8k | let end = match self.end_bound() { |
68 | 0 | Bound::Unbounded => end.into().unwrap_or(!0), |
69 | 0 | Bound::Included(&v) => v.saturating_add(1), |
70 | 29.8k | Bound::Excluded(&v) => v, |
71 | | }; |
72 | 29.8k | if start > end { |
73 | 0 | end .. start |
74 | | } |
75 | | else { |
76 | 29.8k | start .. end |
77 | | } |
78 | 29.8k | } |
79 | | |
80 | | fn intersection<R2>(self, other: R2) -> Option<Range<usize>> |
81 | | where R2: RangeExt<usize> { |
82 | | let Range { start: a1, end: a2 } = self.normalize(None, None); |
83 | | let Range { start: b1, end: b2 } = other.normalize(None, None); |
84 | | if b1 < a1 { |
85 | | return (b1 .. b2).intersection(a1 .. a2); |
86 | | } |
87 | | if !(a1 .. a2).contains(&b1) { |
88 | | return None; |
89 | | } |
90 | | let start = a1.max(b1); |
91 | | let end = a2.min(b2); |
92 | | if start > end { |
93 | | Some(end .. start) |
94 | | } |
95 | | else { |
96 | | Some(start .. end) |
97 | | } |
98 | | } |
99 | | |
100 | | fn union<R2>(self, other: R2) -> Option<Range<usize>> |
101 | | where R2: RangeExt<usize> { |
102 | | let Range { start: a1, end: a2 } = self.normalize(None, None); |
103 | | let Range { start: b1, end: b2 } = other.normalize(None, None); |
104 | | if b1 < a1 { |
105 | | return (b1 .. b2).intersection(a1 .. a2); |
106 | | } |
107 | | if !(a1 .. a2).contains(&b1) { |
108 | | return None; |
109 | | } |
110 | | let start = a1.min(b1); |
111 | | let end = a2.max(b2); |
112 | | if start > end { |
113 | | Some(end .. start) |
114 | | } |
115 | | else { |
116 | | Some(start .. end) |
117 | | } |
118 | | } |
119 | | } |
120 | | |
121 | | #[cfg(test)] |
122 | | mod tests { |
123 | | use super::*; |
124 | | |
125 | | #[test] |
126 | | fn normalize() { |
127 | | let r = (..).normalize(1, 10); |
128 | | assert!(r.contains(&1)); |
129 | | assert!(r.contains(&9)); |
130 | | assert!(!r.contains(&0)); |
131 | | assert!(!r.contains(&10)); |
132 | | |
133 | | let r = (.. 10).normalize(1, 20); |
134 | | assert!(r.contains(&1)); |
135 | | assert!(r.contains(&9)); |
136 | | assert!(!r.contains(&0)); |
137 | | assert!(!r.contains(&10)); |
138 | | |
139 | | let r = (4 ..).normalize(6, 10); |
140 | | assert!(r.contains(&4)); |
141 | | assert!(r.contains(&9)); |
142 | | assert!(!r.contains(&3)); |
143 | | assert!(!r.contains(&10)); |
144 | | |
145 | | let r = (4 ..= 10).normalize(6, 8); |
146 | | assert!(r.contains(&4)); |
147 | | assert!(r.contains(&10)); |
148 | | assert!(!r.contains(&3)); |
149 | | assert!(!r.contains(&11)); |
150 | | |
151 | | let r = (..= 10).normalize(1, 8); |
152 | | assert!(r.contains(&1)); |
153 | | assert!(r.contains(&10)); |
154 | | assert!(!r.contains(&0)); |
155 | | assert!(!r.contains(&11)); |
156 | | } |
157 | | |
158 | | #[test] |
159 | | fn intersect() { |
160 | | let a = 3 .. 10; |
161 | | let b = 7 ..= 20; |
162 | | assert_eq!(a.intersection(b), Some(7 .. 10)); |
163 | | |
164 | | let c = 3 .. 10; |
165 | | let d = 13 ..= 20; |
166 | | assert!(c.intersection(d).is_none()); |
167 | | } |
168 | | |
169 | | #[test] |
170 | | fn union() { |
171 | | let a = 3 .. 10; |
172 | | let b = 7 ..= 20; |
173 | | assert_eq!(a.union(b), Some(3 .. 21)); |
174 | | |
175 | | let c = 3 .. 10; |
176 | | let d = 13 ..= 20; |
177 | | assert!(c.union(d).is_none()); |
178 | | } |
179 | | } |