closed_interval_set/
intersection.rs

1use crate::slice_sequence::Sequence;
2use crate::ClosedRange;
3use crate::ClosedRangeEnd;
4use crate::ClosedRangeVal;
5use crate::Endpoint;
6use crate::NormalizedRangeIter;
7use crate::Pair;
8use crate::RangeVec;
9
10/// The core [`IntersectorImpl`] works over [`Sequence`] rather than
11/// raw slices because the standard slice interface is full of hidden
12/// panics; the [`Sequence`] trait is small enough to audit, and
13/// doesn't itself hide panics in its interface.
14///
15/// The underlying [`Sequence`] must come from a normalized `RangeVec`.
16struct IntersectorImpl<Seq: Sequence> {
17    seq: Seq,
18    search_cursor: Seq,
19    intersect_cursor: Seq,
20}
21
22impl<Seq: Sequence> IntersectorImpl<Seq> {
23    #[inline(always)]
24    fn new(seq: Seq) -> Self {
25        Self {
26            seq,
27            search_cursor: seq,
28            intersect_cursor: seq,
29        }
30    }
31
32    #[inline(always)]
33    fn start_search(&mut self, start: Seq::EndT) {
34        self.search_cursor = self.seq.skip_to((start, start), self.search_cursor);
35        self.intersect_cursor = self.search_cursor;
36    }
37
38    fn pump(&mut self, needle: Pair<Seq::EndT>) -> Option<Pair<Seq::EndT>> {
39        // Safe to compare because `seq` and its cursors are normalized.
40        use core::cmp::Ordering;
41
42        let (x_start, x_stop) = needle;
43
44        while let Some(((y_start, y_stop), rest)) = self.intersect_cursor.uncons() {
45            self.intersect_cursor = rest;
46
47            if y_start.cmp_end(x_stop) == Ordering::Greater {
48                return None;
49            }
50
51            let start = x_start.top_end(y_start);
52            let stop = x_stop.bot_end(y_stop);
53
54            if start.cmp_end(stop) <= Ordering::Equal {
55                return Some((start, stop));
56            }
57        }
58
59        None
60    }
61}
62
63#[repr(transparent)]
64pub struct Intersector<'a, T: Endpoint>(IntersectorImpl<&'a [(T, T)]>);
65
66impl<'a, T: Endpoint> Intersector<'a, T> {
67    #[inline(always)]
68    fn new(seq: &'a [(T, T)]) -> Self {
69        Self(IntersectorImpl::new(seq))
70    }
71
72    #[inline(always)]
73    fn start_search(&mut self, start: T) {
74        self.0.start_search(start)
75    }
76
77    #[inline(always)]
78    fn pump(&mut self, needle: (T, T)) -> Option<(T, T)> {
79        self.0.pump(needle)
80    }
81}
82
83pub struct IntersectionIterator<'a, Xs: NormalizedRangeIter> {
84    // No need to fuse: we only call `next` after `None` when
85    // we're called after returning `None`
86    xs: Xs,
87    intersector: Intersector<'a, ClosedRangeEnd<<Xs as Iterator>::Item>>,
88    curr: Option<ClosedRangeVal<<Xs as Iterator>::Item>>,
89}
90
91impl<'a, Xs: NormalizedRangeIter> IntersectionIterator<'a, Xs> {
92    #[inline(always)]
93    fn new(xs: Xs, ys: &'a [ClosedRangeVal<<Xs as Iterator>::Item>]) -> Self {
94        Self {
95            xs,
96            intersector: Intersector::new(ys),
97            curr: None,
98        }
99    }
100}
101
102impl<Xs: NormalizedRangeIter> Iterator for IntersectionIterator<'_, Xs> {
103    type Item = ClosedRangeVal<<Xs as Iterator>::Item>;
104
105    fn next(&mut self) -> Option<Self::Item> {
106        loop {
107            let curr = match self.curr {
108                Some(curr) => curr,
109                None => {
110                    let next = self.xs.next()?.get();
111                    self.intersector.start_search(next.0);
112                    self.curr = Some(next);
113                    next
114                }
115            };
116
117            if let Some(ret) = self.intersector.pump(curr) {
118                return Some(ret);
119            }
120
121            self.curr = None;
122        }
123    }
124
125    #[inline]
126    fn size_hint(&self) -> (usize, Option<usize>) {
127        // The intersection can always be empty, but each output range
128        // consumed (at least) one input range.
129        let (_, x_max) = self.xs.size_hint();
130        let y_max = self.intersector.0.search_cursor.len();
131
132        if (x_max == Some(0)) | (y_max == 0) {
133            (0, Some(0))
134        } else {
135            (0, x_max.unwrap_or(usize::MAX).checked_add(y_max))
136        }
137    }
138}
139
140impl<Xs: NormalizedRangeIter> crate::private::Sealed for IntersectionIterator<'_, Xs> {}
141
142impl<Xs: NormalizedRangeIter> crate::NormalizedRangeIter for IntersectionIterator<'_, Xs> {}
143
144impl<Xs: NormalizedRangeIter + core::iter::FusedIterator> core::iter::FusedIterator
145    for IntersectionIterator<'_, Xs>
146{
147}
148
149/// This internal implementation assumes `ys` is normalised.
150#[inline(always)]
151pub(crate) unsafe fn intersect<Xs: NormalizedRangeIter>(
152    xs: Xs,
153    ys: &[ClosedRangeVal<<Xs as Iterator>::Item>],
154) -> IntersectionIterator<'_, Xs> {
155    IntersectionIterator::new(xs, ys)
156}
157
158impl<T: Endpoint> RangeVec<T> {
159    /// Constructs the intersection of this [`RangeVec`] and another
160    /// [`RangeVec`].
161    ///
162    /// This operation takes at most \\(\mathcal{O}(\min(m + n, m \log n))\\)
163    /// time, where \\(m\\) is the size of `self`, and \\(n\\) that of `other`.
164    #[inline(always)]
165    #[must_use]
166    pub fn intersect_vec<'a>(&'a self, other: &'a RangeVec<T>) -> Self {
167        intersect_vec(self, other)
168    }
169}
170/// Constructs the intersection of two normalized vectors of ranges.
171///
172/// Since both arguments are guaranteed to be normalized, we can
173/// iterate over the shorter one and binary search over the longer
174/// one, which is usually a good idea.
175///
176/// This operation takes at most \\(\mathcal{O}(\min(m + n, m \log n))\\)
177/// time, where \\(m\\) is the size of the shorter [`RangeVec`], and
178/// \\(n\\) that of the longer [`RangeVec`].
179#[inline(never)]
180#[must_use]
181pub fn intersect_vec<'a, T: Endpoint>(
182    mut xs: &'a RangeVec<T>,
183    mut ys: &'a RangeVec<T>,
184) -> RangeVec<T> {
185    #[cfg(feature = "internal_checks")]
186    let expected = (
187        xs.iter().intersect(ys.iter()).collect_range_vec(),
188        ys.iter().intersect(xs.iter()).collect_range_vec(),
189        xs.iter().intersect_vec(ys).collect_range_vec(),
190        ys.iter().intersect_vec(xs).collect_range_vec(),
191    );
192
193    if xs.len() > ys.len() {
194        core::mem::swap(&mut xs, &mut ys);
195    }
196
197    debug_assert!(xs.len() <= ys.len());
198    let intersection = xs.iter().intersect_vec(ys);
199    let size_hint = intersection.size_hint();
200
201    let ret = intersection.collect_range_vec();
202
203    // If any input is empty -> we know the intersection is empty.
204    debug_assert!((!(xs.is_empty() | ys.is_empty())) | (size_hint == (0, Some(0))));
205    debug_assert!(size_hint.0 <= ret.len());
206    debug_assert!(ret.len() <= size_hint.1.unwrap());
207
208    #[cfg(feature = "internal_checks")]
209    {
210        assert!(&expected.0.eqv(&ret));
211        assert!(&expected.1.eqv(&ret));
212        assert!(&expected.2.eqv(&ret));
213        assert!(&expected.3.eqv(&ret));
214    }
215
216    ret
217}
218
219#[cfg(test)]
220#[cfg_attr(coverage_nightly, coverage(off))]
221mod test {
222    use super::*;
223    use alloc::vec;
224    use alloc::vec::Vec;
225
226    #[test]
227    fn test_smoke() {
228        // intersection of two empty sets -> empty set
229        assert!(crate::normalize_vec(Vec::<(u8, u8)>::new())
230            .iter()
231            .intersect_vec(&crate::normalize_vec(Vec::<(u8, u8)>::new()))
232            .collect_range_vec()
233            .is_empty());
234
235        let xs = crate::normalize_vec(vec![(1u8, 1u8), (3u8, 4u8), (10u8, 20u8), (30u8, 100u8)]);
236        let ys = crate::normalize_vec(vec![
237            (0u8, 6u8),
238            (8u8, 15u8),
239            (17u8, 18u8),
240            (20u8, 22u8),
241            (200u8, 200u8),
242        ]);
243
244        // intersection of anything with empty set -> empty set
245        assert!(crate::normalize_vec(Vec::<(u8, u8)>::new())
246            .iter()
247            .intersect_vec(&xs)
248            .collect_range_vec()
249            .is_empty());
250        assert!(ys
251            .clone()
252            .intersect(&crate::normalize_vec(Vec::<(u8, u8)>::new()))
253            .is_empty());
254
255        assert_eq!(
256            xs.clone().intersect(&ys).into_vec(),
257            vec![
258                (1u8, 1u8),
259                (3u8, 4u8),
260                (10u8, 15u8),
261                (17u8, 18u8),
262                (20u8, 20u8)
263            ]
264        );
265
266        assert_eq!(
267            xs.intersect_vec(&ys).into_vec(),
268            vec![
269                (1u8, 1u8),
270                (3u8, 4u8),
271                (10u8, 15u8),
272                (17u8, 18u8),
273                (20u8, 20u8)
274            ]
275        );
276
277        assert_eq!(
278            intersect_vec(&ys, &xs).into_vec(),
279            vec![
280                (1u8, 1u8),
281                (3u8, 4u8),
282                (10u8, 15u8),
283                (17u8, 18u8),
284                (20u8, 20u8)
285            ]
286        );
287
288        assert_eq!(
289            xs.iter().intersect_vec(&ys).collect::<Vec<_>>(),
290            vec![
291                (1u8, 1u8),
292                (3u8, 4u8),
293                (10u8, 15u8),
294                (17u8, 18u8),
295                (20u8, 20u8)
296            ]
297        );
298
299        assert_eq!(
300            ys.iter().intersect_vec(&xs).collect::<Vec<_>>(),
301            vec![
302                (1u8, 1u8),
303                (3u8, 4u8),
304                (10u8, 15u8),
305                (17u8, 18u8),
306                (20u8, 20u8)
307            ]
308        );
309
310        assert_eq!(
311            intersect_vec(
312                &crate::normalize_vec(xs[..0].to_vec()),
313                &crate::normalize_vec(vec![])
314            )
315            .into_vec(),
316            vec![]
317        );
318
319        {
320            let empty = crate::normalize_vec(xs[..0].to_vec());
321
322            assert_eq!(
323                unsafe { intersect(empty.iter(), &[]) }
324                    .collect_range_vec()
325                    .into_vec(),
326                vec![]
327            );
328        }
329
330        assert_eq!(
331            intersect_vec(
332                &crate::normalize_vec(xs[1..2].to_vec()),
333                &crate::normalize_vec(vec![(0u8, 0u8)])
334            )
335            .into_vec(),
336            vec![]
337        );
338    }
339
340    proptest::proptest! {
341        #[test]
342        fn test_intersection(xs: Vec<(u8, u8)>, ys: Vec<(u8, u8)>)
343        {
344            use crate::ranges_to_bits;
345
346            let bits = {
347                let x_bits = ranges_to_bits(&xs);
348                let y_bits = ranges_to_bits(&ys);
349
350                x_bits.into_iter().zip(y_bits.into_iter()).map(|(x, y)| x & y).collect::<Vec<bool>>()
351            };
352
353            {
354                let out = intersect_vec(&RangeVec::from_vec(xs.clone()),
355                                        &RangeVec::from_vec(ys.clone()));
356                assert_eq!(&bits, &ranges_to_bits(&out));
357            }
358
359            {
360                let out = RangeVec::from_vec(ys.clone()).intersect(&RangeVec::from_vec(xs.clone()));
361                assert_eq!(&bits, &ranges_to_bits(&out));
362            }
363
364            {
365                let out = RangeVec::from_vec(xs.clone()).into_iter().intersect_vec(&RangeVec::from_vec(ys.clone())).collect_range_vec();
366                assert_eq!(&bits, &ranges_to_bits(&out));
367            }
368
369            {
370                let out = RangeVec::from_vec(ys.clone()).into_iter().intersect_vec(&RangeVec::from_vec(xs.clone())).collect_range_vec();
371                assert_eq!(&bits, &ranges_to_bits(&out));
372            }
373        }
374
375        #[test]
376        fn test_intersection_identity(xs: Vec<(u8, u8)>, ys: Vec<(u8, u8)>)
377        {
378            // Increase interesting coverage with valid ranges
379            let xs = RangeVec::from_vec(xs.into_iter().map(|(a, b)| (a.min(b), a.max(b))).collect::<Vec<_>>());
380            let ys = RangeVec::from_vec(ys.into_iter().map(|(a, b)| (a.min(b), a.max(b))).collect::<Vec<_>>());
381
382            //  (X & Y) = -(-x | -y),
383            // or
384            // -(X & Y) =  (-x | -y).
385
386            // Try that with iterators.
387            {
388                let not_and = xs.iter().intersect_vec(&ys).complement().collect_range_vec();
389                let or_not = crate::union_vec(xs.iter().complement().collect_range_vec(),
390                                              ys.iter().complement());
391
392                assert_eq!(not_and, or_not);
393            }
394
395            {
396                let and = xs.iter().intersect_vec(&ys).collect_range_vec();
397                let not_or_not = crate::union_vec(xs.iter().complement().collect_range_vec(),
398                                                  ys.iter().complement()).iter().complement().collect_range_vec();
399
400                assert_eq!(and, not_or_not);
401            }
402
403            // Now work with RangeVec as much as possible.
404            {
405                let not_and = intersect_vec(&xs, &ys).into_complement();
406                let or_not = crate::union_vec(xs.clone().into_complement(),
407                                              ys.clone().into_complement());
408
409                assert_eq!(not_and, or_not);
410            }
411
412            {
413                let not_and = intersect_vec(&xs, &ys).into_complement();
414                let or_not = xs.clone().into_complement().union(
415                    ys.clone().into_complement());
416
417                assert_eq!(not_and, or_not);
418            }
419
420            {
421                let and = intersect_vec(&xs, &ys);
422                let not_or_not = xs.clone().into_complement()
423                    .union(ys.clone().into_complement())
424                    .into_complement();
425
426                assert_eq!(and, not_or_not);
427            }
428        }
429    }
430}