closed_interval_set/
union_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 UnionIterator<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    accumulator: Option<(T, T)>,
17    left: Peekable<X>,
18    right: Peekable<Y>,
19}
20
21impl<T, X, Y> UnionIterator<T, X, Y>
22where
23    T: Endpoint,
24    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
25    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
26{
27    pub fn new(left: X, right: Y) -> Self {
28        Self {
29            accumulator: None,
30            left: left.peekable(),
31            right: right.peekable(),
32        }
33    }
34}
35
36impl<T, X, Y> Iterator for UnionIterator<T, X, Y>
37where
38    T: Endpoint,
39    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
40    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
41{
42    type Item = (T, T);
43
44    fn next(&mut self) -> Option<(T, T)> {
45        use core::cmp::Ordering; // Safe because both iterators are normalized
46
47        loop {
48            let next;
49
50            match (
51                self.left.peek().map(|x| x.get()),
52                self.right.peek().map(|x| x.get()),
53            ) {
54                (Some(left), Some(right)) if T::cmp_range(left, right) <= Ordering::Equal => {
55                    next = left;
56                    self.left.next();
57                }
58                (Some(_), Some(right)) => {
59                    next = right;
60                    self.right.next();
61                }
62                (Some(x), None) => {
63                    next = x;
64                    self.left.next();
65                }
66                (None, Some(x)) => {
67                    next = x;
68                    self.right.next();
69                }
70                (None, None) => return self.accumulator.take(),
71            };
72
73            let (next_start, next_stop) = next;
74            let Some((acc_start, acc_stop)) = self.accumulator.take() else {
75                self.accumulator = Some(next);
76                continue;
77            };
78
79            // Try to join `acc <= next`.
80            if let Some(min_start) = acc_stop.increase_toward(next_start) {
81                // They're disjoint, produce `acc`, buffer `next.
82                if next_start.cmp_end(min_start) > Ordering::Equal {
83                    self.accumulator = Some(next);
84                    return Some((acc_start, acc_stop));
85                }
86            }
87
88            self.accumulator = Some((acc_start, acc_stop.top_end(next_stop)));
89        }
90    }
91
92    #[inline]
93    fn size_hint(&self) -> (usize, Option<usize>) {
94        let (left_min, left_max) = self.left.size_hint();
95        let (right_min, right_max) = self.right.size_hint();
96
97        let max = left_max
98            .unwrap_or(usize::MAX)
99            .checked_add(right_max.unwrap_or(usize::MAX));
100        // If both are empty, the union is empty.  Otherwise,
101        // they could end up as a single big range.
102        let min = ((left_min != 0) | (right_min != 0)) as usize;
103        (min, max)
104    }
105}
106
107impl<T, X, Y> crate::private::Sealed for UnionIterator<T, X, Y>
108where
109    T: Endpoint,
110    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
111    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
112{
113}
114
115impl<T, X, Y> NormalizedRangeIter for UnionIterator<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
123// We use `Peekable` internally, so we'll reliably return None after None.
124impl<T, X, Y> core::iter::FusedIterator for UnionIterator<T, X, Y>
125where
126    T: Endpoint,
127    X: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
128    Y: Sized + NormalizedRangeIter + Iterator<Item: ClosedRange<EndT = T>>,
129{
130}
131
132impl<T: Endpoint> RangeVec<T> {
133    /// Constructs the union of this [`RangeVec`] and any iterator of
134    /// ranges.
135    ///
136    /// This operation takes time linear in the total input size.  While
137    /// asymptotically better than [`RangeVec::into_union`], the latter's
138    /// constant factors may be better than this method's.
139    ///
140    /// See [`union_vec`] for more general types.
141    ///
142    /// [`union_vec`]: crate::union_vec
143    #[inline(always)]
144    pub fn union(&self, other: impl IntoNormalizedRangeIter<Item: ClosedRange<EndT = T>>) -> Self {
145        #[inline(never)]
146        fn doit<T: Endpoint>(
147            this: &RangeVec<T>,
148            other: impl NormalizedRangeIter<Item: ClosedRange<EndT = T>>,
149        ) -> RangeVec<T> {
150            this.iter().union(other).collect_range_vec()
151        }
152
153        doit(self, other.into_iter())
154    }
155}
156
157#[cfg(test)]
158#[cfg_attr(coverage_nightly, coverage(off))]
159mod test {
160    use super::*;
161    use alloc::vec;
162    use alloc::vec::Vec;
163
164    #[test]
165    fn test_union_iterator_smoke() {
166        use crate::NormalizedRangeIter;
167
168        let acc = vec![(1u8, 4u8), (5u8, 1u8), (5u8, 10u8)];
169        let src = [(0u8, 2u8), (11u8, 15u8)];
170
171        assert_eq!(
172            crate::normalize_vec(acc.clone())
173                .iter()
174                .union(crate::normalize_vec(src.to_vec()).into_iter())
175                .collect_range_vec()
176                .into_vec(),
177            vec![(0u8, 15u8)]
178        );
179
180        assert_eq!(
181            crate::normalize_vec(src.to_vec())
182                .iter()
183                .union(crate::normalize_vec(vec![]))
184                .collect_range_vec()
185                .into_vec(),
186            src.to_vec()
187        );
188
189        {
190            let empty = Vec::<(u8, u8)>::new();
191            let mut union = crate::normalize_vec(empty.clone())
192                .into_iter()
193                .union(crate::normalize_vec(empty).into_iter());
194
195            assert_eq!(union.next(), None);
196            // And still none
197            assert_eq!(union.next(), None);
198        }
199    }
200
201    proptest::proptest! {
202        #[test]
203        fn test_union_iterator(xs: Vec<(u8, u8)>, ys: Vec<(u8, u8)>)
204        {
205            use crate::RangeVec;
206            use crate::ranges_to_bits;
207
208            let bits = {
209                let mut all_ranges = xs.clone();
210                all_ranges.extend(&ys);
211                ranges_to_bits(&all_ranges)
212            };
213
214            let xs = RangeVec::from_vec(xs);
215            let ys = RangeVec::from_vec(ys);
216
217            {
218                let union = xs.iter().union(ys.iter());
219
220                let (min_size, max_size) = union.size_hint();
221                if !xs.is_empty() || !ys.is_empty() {
222                    assert!(min_size > 0);
223                } else {
224                    assert!(min_size == 0);
225                }
226
227                let max_size = max_size.expect("should have limits");
228                assert!(max_size == xs.len() + ys.len());
229
230                let union = union.collect_range_vec();
231                assert_eq!(bits, ranges_to_bits(&union));
232            }
233
234            {
235                let union = ys.iter().union(xs).collect_range_vec();
236                assert_eq!(bits, ranges_to_bits(&union));
237            }
238        }
239
240        #[test]
241        fn test_union_iterator_identities(xs: Vec<(u8, u8)>)
242        {
243            let xs = RangeVec::from_vec(xs);
244
245            // xs | xs = xs
246            {
247                let xxs = xs.iter().union(xs.iter()).collect_range_vec();
248                assert_eq!(&xxs, &xs);
249            }
250
251            // xs | -xs = universe
252            let universe = xs.iter().union(xs.iter().complement()).collect_range_vec();
253            assert_eq!(universe.inner(), &[(0, 255u8)]);
254        }
255    }
256}