Skip to main content

range_set_blaze/
unsorted_disjoint.rs

1use crate::{Integer, SortedDisjoint, SortedStarts};
2#[cfg(not(debug_assertions))]
3use core::marker::PhantomData;
4use core::{
5    cmp::{max, min},
6    iter::FusedIterator,
7    ops::RangeInclusive,
8};
9use num_traits::Zero;
10
11#[must_use = "iterators are lazy and do nothing unless consumed"]
12#[allow(clippy::redundant_pub_crate)]
13pub(crate) struct UnsortedDisjoint<T, I> {
14    iter: I,
15    option_range: Option<RangeInclusive<T>>,
16    min_value_plus_2: T,
17}
18
19impl<T, I> UnsortedDisjoint<T, I>
20where
21    T: Integer,
22    I: Iterator<Item = RangeInclusive<T>>, // Any iterator is fine
23{
24    #[inline]
25    pub(crate) fn new(iter: I) -> Self {
26        Self {
27            iter,
28            option_range: None,
29            min_value_plus_2: T::min_value().add_one().add_one(),
30        }
31    }
32}
33
34impl<T, I> FusedIterator for UnsortedDisjoint<T, I>
35where
36    T: Integer,
37    I: Iterator<Item = RangeInclusive<T>> + FusedIterator,
38{
39}
40
41impl<T, I> Iterator for UnsortedDisjoint<T, I>
42where
43    T: Integer,
44    I: Iterator<Item = RangeInclusive<T>>,
45{
46    type Item = RangeInclusive<T>;
47
48    fn next(&mut self) -> Option<Self::Item> {
49        loop {
50            let Some(range) = self.iter.next() else {
51                return self.option_range.take();
52            };
53            let (next_start, next_end) = range.into_inner();
54            if next_start > next_end {
55                continue;
56            }
57            let Some(self_range) = self.option_range.clone() else {
58                self.option_range = Some(next_start..=next_end);
59                continue;
60            };
61
62            let (self_start, self_end) = self_range.into_inner();
63            if (next_start >= self.min_value_plus_2 && self_end <= next_start.sub_one().sub_one())
64                || (self_start >= self.min_value_plus_2
65                    && next_end <= self_start.sub_one().sub_one())
66            {
67                let result = Some(self_start..=self_end);
68                self.option_range = Some(next_start..=next_end);
69                return result;
70            }
71            self.option_range = Some(min(self_start, next_start)..=max(self_end, next_end));
72            // continue;
73        }
74    }
75
76    // As few as one (or zero if iter is empty) and as many as iter.len()
77    // There could be one extra if option_range is Some.
78    fn size_hint(&self) -> (usize, Option<usize>) {
79        let (lower, upper) = self.iter.size_hint();
80        let lower = min(lower, 1);
81        if self.option_range.is_some() {
82            (lower, upper.map(|x| x + 1))
83        } else {
84            (lower, upper)
85        }
86    }
87}
88
89#[must_use = "iterators are lazy and do nothing unless consumed"]
90#[allow(clippy::redundant_pub_crate)]
91pub(crate) struct SortedDisjointWithLenSoFar<T: Integer, I> {
92    iter: I,
93    len: <T as Integer>::SafeLen,
94}
95
96impl<T, I> SortedDisjointWithLenSoFar<T, I>
97where
98    T: Integer,
99    I: Iterator<Item = RangeInclusive<T>> + SortedDisjoint<T>,
100{
101    #[inline]
102    pub(crate) fn new(iter: I) -> Self {
103        Self {
104            iter,
105            len: T::SafeLen::zero(),
106        }
107    }
108}
109
110impl<T: Integer, I> SortedDisjointWithLenSoFar<T, I>
111where
112    I: SortedDisjoint<T>,
113{
114    pub(crate) const fn len_so_far(&self) -> <T as Integer>::SafeLen {
115        self.len
116    }
117}
118
119impl<T: Integer, I> FusedIterator for SortedDisjointWithLenSoFar<T, I> where
120    I: SortedDisjoint<T> + FusedIterator
121{
122}
123
124impl<T: Integer, I> Iterator for SortedDisjointWithLenSoFar<T, I>
125where
126    I: SortedDisjoint<T>,
127{
128    type Item = (T, T);
129
130    fn next(&mut self) -> Option<Self::Item> {
131        if let Some(range) = self.iter.next() {
132            let (start, end) = range.clone().into_inner();
133            debug_assert!(start <= end);
134            self.len += T::safe_len(&range);
135            Some((start, end))
136        } else {
137            None
138        }
139    }
140    fn size_hint(&self) -> (usize, Option<usize>) {
141        self.iter.size_hint()
142    }
143}
144
145#[derive(Clone, Debug)]
146#[must_use = "iterators are lazy and do nothing unless consumed"]
147/// Gives any iterator of ranges the [`SortedStarts`] trait without any checking.
148pub struct AssumeSortedStarts<T, I> {
149    pub(crate) iter: I,
150    #[cfg(debug_assertions)]
151    last_start: Option<T>,
152    #[cfg(not(debug_assertions))]
153    last_start: PhantomData<T>,
154}
155
156impl<T, I> FusedIterator for AssumeSortedStarts<T, I>
157where
158    T: Integer,
159    I: Iterator<Item = RangeInclusive<T>> + FusedIterator,
160{
161}
162
163impl<T: Integer, I> SortedStarts<T> for AssumeSortedStarts<T, I> where
164    I: Iterator<Item = RangeInclusive<T>> + FusedIterator
165{
166}
167
168impl<T, I> AssumeSortedStarts<T, I>
169where
170    T: Integer,
171    I: Iterator<Item = RangeInclusive<T>> + FusedIterator,
172{
173    /// Construct [`AssumeSortedStarts`] from a range iterator.
174    #[inline]
175    pub fn new<J: IntoIterator<IntoIter = I>>(iter: J) -> Self {
176        Self {
177            iter: iter.into_iter(),
178            #[cfg(debug_assertions)]
179            last_start: None,
180            #[cfg(not(debug_assertions))]
181            last_start: PhantomData,
182        }
183    }
184}
185
186impl<T, I> Iterator for AssumeSortedStarts<T, I>
187where
188    T: Integer,
189    I: Iterator<Item = RangeInclusive<T>> + FusedIterator,
190{
191    type Item = RangeInclusive<T>;
192
193    fn next(&mut self) -> Option<Self::Item> {
194        let r = self.iter.next();
195        #[cfg(debug_assertions)]
196        if let Some(next) = &r {
197            let next_start = *next.start();
198            if let Some(last) = self.last_start {
199                assert!(
200                    last <= next_start,
201                    "AssumeSortedStarts contained items with starts which are not sorted: {last:?}<={next_start:?} is wrong"
202                );
203            }
204            self.last_start = Some(next_start);
205        }
206        r
207    }
208
209    fn size_hint(&self) -> (usize, Option<usize>) {
210        self.iter.size_hint()
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    #[cfg(debug_assertions)]
220    #[should_panic(
221        expected = "AssumeSortedStarts contained items with starts which are not sorted: 1<=0 is wrong"
222    )]
223    fn panic_if_unsorted_starts_in_assume_sorted_starts() {
224        AssumeSortedStarts::new([1..=10, 0..=20]).count();
225    }
226}