closed_interval_set/
intersection_iterator.rs

1//! Computes the union of two normalized iterators.
2use crate::ClosedRange;
3use crate::Endpoint;
4use crate::IntoNormalizedRangeIter;
5use crate::NormalizedRangeIter;
6use crate::RangeVec;
7
8use core::iter::Peekable;
9
10pub struct LinearIntersectionIterator<T, X, Y>
11where
12    T: Endpoint,
13    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
14    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
15{
16    left: Peekable<X>,
17    right: Peekable<Y>,
18}
19
20impl<T, X, Y> LinearIntersectionIterator<T, X, Y>
21where
22    T: Endpoint,
23    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
24    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
25{
26    pub fn new(left: X, right: Y) -> Self {
27        Self {
28            left: left.peekable(),
29            right: right.peekable(),
30        }
31    }
32}
33
34impl<T, X, Y> Iterator for LinearIntersectionIterator<T, X, Y>
35where
36    T: Endpoint,
37    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
38    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
39{
40    type Item = (T, T);
41
42    fn next(&mut self) -> Option<(T, T)> {
43        use core::cmp::Ordering; // Safe because both iterators are normalized
44
45        loop {
46            let (Some(left), Some(right)) = (
47                self.left.peek().map(|x| x.get()),
48                self.right.peek().map(|x| x.get()),
49            ) else {
50                return None;
51            };
52
53            // We always advance the smallest endpoint (or both if equal)
54            match left.1.cmp_end(right.1) {
55                Ordering::Less => {
56                    self.left.next();
57                }
58                Ordering::Equal => {
59                    self.left.next();
60                    self.right.next();
61                }
62                Ordering::Greater => {
63                    self.right.next();
64                }
65            }
66
67            // Find the intersection of left and right.
68            let start = left.0.top_end(right.0);
69            let stop = left.1.bot_end(right.1);
70
71            if start.cmp_end(stop) <= Ordering::Equal {
72                return Some((start, stop));
73            }
74        }
75    }
76
77    #[inline]
78    fn size_hint(&self) -> (usize, Option<usize>) {
79        // The intersection can always be empty, but each output range
80        // consumed (at least) one input range.
81        let (_, left_max) = self.left.size_hint();
82        let (_, right_max) = self.right.size_hint();
83
84        if (left_max == Some(0)) | (right_max == Some(0)) {
85            // Intersection with empty is always empty.
86            (0, Some(0))
87        } else {
88            (
89                0,
90                left_max
91                    .unwrap_or(usize::MAX)
92                    .checked_add(right_max.unwrap_or(usize::MAX)),
93            )
94        }
95    }
96}
97
98impl<T, X, Y> crate::private::Sealed for LinearIntersectionIterator<T, X, Y>
99where
100    T: Endpoint,
101    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
102    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
103{
104}
105
106impl<T, X, Y> NormalizedRangeIter for LinearIntersectionIterator<T, X, Y>
107where
108    T: Endpoint,
109    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
110    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
111{
112}
113
114// We use `Peekable` internally, so we'll reliably return None after None.
115impl<T, X, Y> core::iter::FusedIterator for LinearIntersectionIterator<T, X, Y>
116where
117    T: Endpoint,
118    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
119    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
120{
121}
122
123impl<T: Endpoint> RangeVec<T> {
124    /// Constructs the intersection of this [`RangeVec`] and any iterator of
125    /// ranges.
126    ///
127    /// This operation takes linear time in the size of the two inputs.
128    ///
129    /// See [`RangeVec::intersect_vec`] when the other argument is
130    /// also a [`RangeVec`].
131    #[inline(always)]
132    #[must_use]
133    pub fn intersect(
134        &self,
135        other: impl IntoNormalizedRangeIter<Item: ClosedRange<EndT = T>>,
136    ) -> Self {
137        #[inline(never)]
138        fn doit<T: Endpoint>(
139            this: &RangeVec<T>,
140            other: impl NormalizedRangeIter<Item: ClosedRange<EndT = T>>,
141        ) -> RangeVec<T> {
142            let iter = this.iter().intersect(other);
143            let size_hint = iter.size_hint();
144            if this.is_empty() {
145                debug_assert_eq!(size_hint, (0, Some(0)));
146            }
147
148            let ret = iter.collect_range_vec();
149
150            debug_assert!(ret.len() >= size_hint.0);
151            debug_assert!(ret.len() <= size_hint.1.unwrap_or(usize::MAX));
152
153            ret
154        }
155
156        doit(self, other.into_iter())
157    }
158}
159
160#[cfg(test)]
161#[cfg_attr(coverage_nightly, coverage(off))]
162mod test {
163    use super::*;
164    use alloc::vec;
165    use alloc::vec::Vec;
166
167    #[test]
168    fn smoke_test() {
169        // empty & empty -> empty.
170        let empty: RangeVec<u8> = RangeVec::default();
171        let other = RangeVec::from_vec(vec![(u8::MIN, u8::MAX)]);
172
173        {
174            let empty_empty = empty.iter().intersect(empty.iter());
175
176            assert_eq!(empty_empty.size_hint(), (0, Some(0)));
177            assert_eq!(&empty_empty.collect_range_vec(), &empty);
178        }
179
180        {
181            let empty_other = empty.iter().intersect(other.iter());
182
183            assert_eq!(empty_other.size_hint(), (0, Some(0)));
184            assert_eq!(&empty_other.collect_range_vec(), &empty);
185        }
186
187        {
188            let other_empty = other.iter().intersect(empty.iter());
189
190            assert_eq!(other_empty.size_hint(), (0, Some(0)));
191            assert_eq!(&other_empty.collect_range_vec(), &empty);
192        }
193
194        // We can pull multiple times and get `None`.
195        {
196            let mut other_empty = other.iter().intersect(other.iter());
197
198            // We can't know it's empty.
199            assert!(other_empty.size_hint() != (0, Some(0)));
200
201            assert!(other_empty.next().is_some());
202            // Should be done
203            assert_eq!(other_empty.next(), None);
204            // And still done.
205            assert_eq!(other_empty.next(), None);
206        }
207    }
208
209    #[cfg(test)]
210    proptest::proptest! {
211        #[test]
212        fn test_intersection_iterator(xs: Vec<(u8, u8)>, ys: Vec<(u8, u8)>)
213        {
214            use crate::ranges_to_bits;
215
216            let bits = {
217                let x_bits = ranges_to_bits(&xs);
218                let y_bits = ranges_to_bits(&ys);
219
220                x_bits.into_iter().zip(y_bits.into_iter()).map(|(x, y)| x & y).collect::<Vec<bool>>()
221            };
222
223            {
224                let out = RangeVec::from_vec(xs.clone()).iter().intersect(
225                    RangeVec::from_vec(ys.clone()).iter()).collect_range_vec();
226                assert_eq!(&bits, &ranges_to_bits(&out));
227            }
228
229            {
230                let out = RangeVec::from_vec(ys.clone()).iter().intersect(&RangeVec::from_vec(xs.clone())).collect_range_vec();
231                assert_eq!(&bits, &ranges_to_bits(&out));
232            }
233        }
234
235        #[test]
236        fn test_intersection_identity(xs: Vec<(u8, u8)>, ys: Vec<(u8, u8)>)
237        {
238            // Increase interesting coverage with valid ranges
239            let xs = RangeVec::from_vec(xs.into_iter().map(|(a, b)| (a.min(b), a.max(b))).collect::<Vec<_>>());
240            let ys = RangeVec::from_vec(ys.into_iter().map(|(a, b)| (a.min(b), a.max(b))).collect::<Vec<_>>());
241
242            //  (X & Y) = -(-x | -y),
243            // or
244            // -(X & Y) =  (-x | -y).
245
246            // Try that with iterators.
247            {
248                let not_and = xs.iter().intersect(&ys).complement().collect_range_vec();
249                let or_not = crate::union_vec(xs.iter().complement().collect_range_vec(),
250                                              ys.iter().complement());
251
252                assert_eq!(not_and, or_not);
253            }
254
255            {
256                let and = xs.iter().intersect(&ys).collect_range_vec();
257                let not_or_not = crate::union_vec(xs.iter().complement().collect_range_vec(),
258                                                  ys.iter().complement()).iter().complement().collect_range_vec();
259
260                assert_eq!(and, not_or_not);
261            }
262        }
263    }
264}