1pub const MIN_CODE_POINT: u32 = 0;
9pub const MAX_CODE_POINT: u32 = 0x10FFFF;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
13pub struct IntervalSet {
14 intervals: Vec<(u32, u32)>,
15}
16
17impl IntervalSet {
18 #[must_use]
20 pub fn new(intervals: &[(u32, u32)]) -> Self {
21 let mut ordered: Vec<(u32, u32)> = intervals.to_vec();
22 ordered.sort_unstable();
23 let mut merged: Vec<(u32, u32)> = Vec::with_capacity(ordered.len());
24 for (low, high) in ordered {
25 match merged.last_mut() {
26 Some(last) if low <= last.1.saturating_add(1) => {
27 if high > last.1 {
28 last.1 = high;
29 }
30 }
31 _ => merged.push((low, high)),
32 }
33 }
34 Self { intervals: merged }
35 }
36
37 #[must_use]
39 pub fn empty() -> Self {
40 Self {
41 intervals: Vec::new(),
42 }
43 }
44
45 #[must_use]
47 pub fn full() -> Self {
48 Self {
49 intervals: vec![(MIN_CODE_POINT, MAX_CODE_POINT)],
50 }
51 }
52
53 #[must_use]
55 pub fn single(code_point: u32) -> Self {
56 Self {
57 intervals: vec![(code_point, code_point)],
58 }
59 }
60
61 #[must_use]
63 pub fn from_range(low: u32, high: u32) -> Self {
64 let high = high.min(MAX_CODE_POINT);
65 if low > high {
66 return Self::empty();
67 }
68 Self {
69 intervals: vec![(low, high)],
70 }
71 }
72
73 #[must_use]
75 pub fn from_normalized(intervals: Vec<(u32, u32)>) -> Self {
76 Self { intervals }
77 }
78
79 #[must_use]
81 pub fn intervals(&self) -> &[(u32, u32)] {
82 &self.intervals
83 }
84
85 #[must_use]
87 pub fn is_empty(&self) -> bool {
88 self.intervals.is_empty()
89 }
90
91 #[must_use]
93 pub fn contains(&self, code_point: u32) -> bool {
94 let intervals = &self.intervals;
95 let (mut low_bound, mut high_bound) = (0usize, intervals.len());
96 while low_bound < high_bound {
97 let mid = (low_bound + high_bound) / 2;
98 let (low, high) = intervals[mid];
99 if code_point < low {
100 high_bound = mid;
101 } else if code_point > high {
102 low_bound = mid + 1;
103 } else {
104 return true;
105 }
106 }
107 false
108 }
109
110 #[must_use]
112 pub fn first_member(&self) -> Option<u32> {
113 self.intervals.first().map(|(low, _)| *low)
114 }
115
116 #[must_use]
118 pub fn component_first_members(&self) -> Vec<u32> {
119 self.intervals.iter().map(|(low, _)| *low).collect()
120 }
121
122 #[must_use]
124 pub fn member_count(&self) -> u64 {
125 self.intervals
126 .iter()
127 .map(|(low, high)| u64::from(*high) - u64::from(*low) + 1)
128 .sum()
129 }
130
131 #[must_use]
133 pub fn union(&self, other: &IntervalSet) -> IntervalSet {
134 let mut intervals = Vec::with_capacity(self.intervals.len() + other.intervals.len());
135 intervals.extend_from_slice(&self.intervals);
136 intervals.extend_from_slice(&other.intervals);
137 Self::new(&intervals)
138 }
139
140 #[must_use]
142 pub fn intersection(&self, other: &IntervalSet) -> IntervalSet {
143 let mut result: Vec<(u32, u32)> = Vec::new();
144 let (left, right) = (&self.intervals, &other.intervals);
145 let (mut i, mut j) = (0usize, 0usize);
146 while i < left.len() && j < right.len() {
147 let (a_low, a_high) = left[i];
148 let (b_low, b_high) = right[j];
149 let low = a_low.max(b_low);
150 let high = a_high.min(b_high);
151 if low <= high {
152 result.push((low, high));
153 }
154 if a_high < b_high {
155 i += 1;
156 } else {
157 j += 1;
158 }
159 }
160 IntervalSet::from_normalized(result)
161 }
162
163 #[must_use]
165 pub fn complement(&self) -> IntervalSet {
166 let mut result: Vec<(u32, u32)> = Vec::new();
167 let mut cursor = MIN_CODE_POINT;
168 for (low, high) in &self.intervals {
169 if *low > cursor {
170 result.push((cursor, low - 1));
171 }
172 match high.checked_add(1) {
173 Some(next) => cursor = next,
174 None => return IntervalSet::from_normalized(result),
175 }
176 }
177 if cursor <= MAX_CODE_POINT {
178 result.push((cursor, MAX_CODE_POINT));
179 }
180 IntervalSet::from_normalized(result)
181 }
182
183 #[must_use]
185 pub fn difference(&self, other: &IntervalSet) -> IntervalSet {
186 self.intersection(&other.complement())
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn empty_set_has_no_members() {
196 let empty = IntervalSet::empty();
197 assert!(empty.is_empty());
198 assert_eq!(empty.first_member(), None);
199 assert!(!empty.contains(0));
200 }
201
202 #[test]
203 fn full_set_contains_the_entire_range() {
204 let full = IntervalSet::full();
205 assert!(!full.is_empty());
206 assert!(full.contains(MIN_CODE_POINT));
207 assert!(full.contains(MAX_CODE_POINT));
208 assert_eq!(full.first_member(), Some(MIN_CODE_POINT));
209 }
210
211 #[test]
212 fn single_builds_a_one_code_point_set() {
213 let single = IntervalSet::single(65);
214 assert!(single.contains(65));
215 assert!(!single.contains(64));
216 assert!(!single.contains(66));
217 assert_eq!(single.first_member(), Some(65));
218 }
219
220 #[test]
221 fn from_range_builds_an_inclusive_range() {
222 let interval = IntervalSet::from_range(10, 20);
223 assert!(interval.contains(10));
224 assert!(interval.contains(20));
225 assert!(!interval.contains(9));
226 assert!(!interval.contains(21));
227 }
228
229 #[test]
230 fn from_range_returns_empty_when_low_exceeds_high() {
231 assert!(IntervalSet::from_range(20, 10).is_empty());
232 }
233
234 #[test]
235 fn from_range_clips_to_the_valid_code_point_span() {
236 let interval = IntervalSet::from_range(0, u32::MAX);
237 assert!(interval.contains(MIN_CODE_POINT));
238 assert!(interval.contains(MAX_CODE_POINT));
239 assert_eq!(
240 interval.intervals().last().copied(),
241 Some((0, MAX_CODE_POINT))
242 );
243 }
244
245 #[test]
246 fn union_merges_adjacent_and_overlapping_intervals() {
247 let adjacent = IntervalSet::from_range(0, 10).union(&IntervalSet::from_range(11, 20));
248 assert!(adjacent.contains(10));
249 assert!(adjacent.contains(11));
250 assert_eq!(adjacent.first_member(), Some(0));
251
252 let overlapping = IntervalSet::from_range(0, 10).union(&IntervalSet::from_range(5, 15));
253 assert!(overlapping.contains(15));
254 }
255
256 #[test]
257 fn union_keeps_disjoint_intervals_sorted() {
258 let disjoint = IntervalSet::from_range(50, 60).union(&IntervalSet::from_range(1, 5));
259 assert_eq!(disjoint.first_member(), Some(1));
260 assert_eq!(disjoint.intervals(), &[(1, 5), (50, 60)]);
261 }
262
263 #[test]
264 fn intersection_overlaps_swept_intervals() {
265 let left = IntervalSet::new(&[(0, 10), (20, 30)]);
266 let right = IntervalSet::new(&[(5, 25), (28, 40)]);
267 let both = left.intersection(&right);
268 assert_eq!(both.intervals(), &[(5, 10), (20, 25), (28, 30)]);
269 }
270
271 #[test]
272 fn intersection_of_disjoint_sets_is_empty() {
273 let left = IntervalSet::from_range(0, 5);
274 let right = IntervalSet::from_range(6, 9);
275 assert!(left.intersection(&right).is_empty());
276 }
277
278 #[test]
279 fn complement_splits_around_members() {
280 let set = IntervalSet::new(&[(5, 10), (20, 30)]);
281 let complement = set.complement();
282 assert_eq!(
283 complement.intervals(),
284 &[(0, 4), (11, 19), (31, MAX_CODE_POINT)]
285 );
286 assert!(complement.contains(0));
287 assert!(!complement.contains(5));
288 assert!(complement.contains(MAX_CODE_POINT));
289 }
290
291 #[test]
292 fn complement_of_the_full_range_is_empty() {
293 assert!(IntervalSet::full().complement().is_empty());
294 }
295
296 #[test]
297 fn complement_of_empty_is_full() {
298 assert_eq!(
299 IntervalSet::empty().complement().intervals(),
300 &[(MIN_CODE_POINT, MAX_CODE_POINT)]
301 );
302 }
303
304 #[test]
305 fn difference_is_intersection_with_complement() {
306 let left = IntervalSet::from_range(0, 20);
307 let right = IntervalSet::from_range(10, 30);
308 assert_eq!(left.difference(&right).intervals(), &[(0, 9)]);
309 }
310
311 #[test]
312 fn contains_binary_searches_sorted_components() {
313 let set = IntervalSet::new(&[(10, 12), (100, 200), (1000, 1000)]);
314 assert!(set.contains(11));
315 assert!(set.contains(150));
316 assert!(set.contains(1000));
317 assert!(!set.contains(13));
318 assert!(!set.contains(999));
319 }
320
321 #[test]
322 fn component_first_members_lists_every_component_start() {
323 let set = IntervalSet::new(&[(10, 12), (100, 200)]);
324 assert_eq!(set.component_first_members(), vec![10, 100]);
325 }
326
327 #[test]
328 fn member_count_sums_inclusive_spans() {
329 let set = IntervalSet::new(&[(0, 9), (100, 104)]);
330 assert_eq!(set.member_count(), 15);
331 }
332
333 #[test]
334 fn new_normalizes_unordered_overlapping_input() {
335 let set = IntervalSet::new(&[(30, 40), (0, 5), (6, 10), (35, 50)]);
336 assert_eq!(set.intervals(), &[(0, 10), (30, 50)]);
337 }
338
339 #[test]
340 fn complement_at_the_top_boundary_does_not_overflow() {
341 let set = IntervalSet::from_range(MAX_CODE_POINT, MAX_CODE_POINT);
342 assert_eq!(set.complement().intervals(), &[(0, MAX_CODE_POINT - 1)]);
343 }
344
345 #[test]
346 fn complement_at_the_code_point_ceilings_is_well_defined() {
347 assert_eq!(
350 IntervalSet::single(u32::MAX).complement(),
351 IntervalSet::new(&[(0, u32::MAX - 1)])
352 );
353 let head = IntervalSet::from_range(1, u32::MAX);
354 assert_eq!(head.complement(), IntervalSet::single(0));
355 assert_eq!(IntervalSet::full().complement(), IntervalSet::empty());
356 }
357}