range_set_blaze/
sym_diff_iter.rs

1use crate::sym_diff_iter_map::UsizeExtensions;
2use crate::{
3    Integer, Merge, SortedDisjoint, SortedStarts, SymDiffKMerge, SymDiffMerge, merge::KMerge,
4};
5use alloc::collections::BinaryHeap;
6use core::{cmp::Reverse, iter::FusedIterator, ops::RangeInclusive};
7
8/// This `struct` is created by the [`symmetric_difference`] method on [`SortedDisjoint`]. See [`symmetric_difference`]'s
9/// documentation for more.
10///
11/// [`SortedDisjoint`]: crate::SortedDisjoint
12/// [`symmetric_difference`]: crate::SortedDisjointMap::symmetric_difference
13#[derive(Clone, Debug)]
14#[must_use = "iterators are lazy and do nothing unless consumed"]
15pub struct SymDiffIter<T, I>
16where
17    T: Integer,
18    I: SortedStarts<T>,
19{
20    iter: I,
21    start_or_min_value: T,
22    end_heap: BinaryHeap<Reverse<T>>,
23    next_again: Option<RangeInclusive<T>>,
24    gather: Option<RangeInclusive<T>>,
25}
26
27impl<T, I> FusedIterator for SymDiffIter<T, I>
28where
29    T: Integer,
30    I: SortedStarts<T>,
31{
32}
33
34impl<T, I> Iterator for SymDiffIter<T, I>
35where
36    T: Integer,
37    I: SortedStarts<T>,
38{
39    type Item = RangeInclusive<T>;
40
41    fn next(&mut self) -> Option<RangeInclusive<T>> {
42        loop {
43            let count = self.end_heap.len();
44            let Some(next_range) = self.next_again.take().or_else(|| self.iter.next()) else {
45                // The workspace is empty and next is empty, so return everything gathered.
46                if count == 0 {
47                    return self.gather.take();
48                }
49
50                // The workspace is not empty (but next is empty) is process the next chunk of the workspace.
51                let end = self
52                    .end_heap
53                    .pop()
54                    .expect("Real Assert: the workspace is not empty")
55                    .0;
56                self.remove_same_end(end);
57                let result = self.start_or_min_value..=end;
58                if !self.end_heap.is_empty() {
59                    self.start_or_min_value = end.add_one(); // The 'if' prevents overflow.
60                }
61                if let Some(result) = self.process(count.is_odd(), result) {
62                    return result;
63                }
64                continue;
65            };
66
67            // Next has the same start as the workspace, so add it to the workspace.
68            // (or the workspace is empty, so add it to the workspace.)
69            let (next_start, next_end) = next_range.into_inner();
70            if count == 0 || self.start_or_min_value == next_start {
71                self.start_or_min_value = next_start;
72                self.end_heap.push(Reverse(next_end));
73                continue;
74            }
75
76            // Next start inside the workspace's first chunk, so process up to next_start.
77            let end = self
78                .end_heap
79                .peek()
80                .expect("Real Assert: The workspace has a first chunk.")
81                .0;
82            if next_start <= end {
83                let result = self.start_or_min_value..=next_start.sub_one();
84                self.start_or_min_value = next_start;
85                self.end_heap.push(Reverse(next_end));
86                if let Some(result) = self.process(count.is_odd(), result) {
87                    return result;
88                }
89                continue;
90            }
91
92            // Next start is after the workspaces end, but the workspace contains only one chuck,
93            // so process the workspace and set the workspace to next.
94            self.remove_same_end(end);
95            let result = self.start_or_min_value..=end;
96            if self.end_heap.is_empty() {
97                self.start_or_min_value = next_start;
98                self.end_heap.push(Reverse(next_end));
99                if let Some(result) = self.process(count.is_odd(), result) {
100                    return result;
101                }
102                continue;
103            }
104
105            // Next start is after the workspaces end, and the workspace contains more than one chuck,
106            // so process one chunk and then process next
107            self.start_or_min_value = end.add_one();
108            self.next_again = Some(next_start..=next_end);
109            if let Some(result) = self.process(count.is_odd(), result) {
110                return result;
111            }
112            // continue;
113        }
114    }
115}
116
117impl<T, I> SymDiffIter<T, I>
118where
119    T: Integer,
120    I: SortedStarts<T>,
121{
122    #[inline]
123    fn remove_same_end(&mut self, end: T) {
124        while let Some(end2) = self.end_heap.peek() {
125            if end2.0 == end {
126                self.end_heap.pop();
127            } else {
128                break;
129            }
130        }
131    }
132
133    #[inline]
134    #[allow(clippy::option_option)]
135    fn process(
136        &mut self,
137        keep: bool,
138        next: RangeInclusive<T>,
139    ) -> Option<Option<RangeInclusive<T>>> {
140        if !keep {
141            return None;
142        }
143        let Some(gather) = self.gather.take() else {
144            self.gather = Some(next);
145            return None;
146        };
147        // If there is no "next" then return gather if it exists.
148
149        // Take both next and gather apart.
150        let (next_start, next_end) = next.into_inner();
151        let (gather_start, gather_end) = gather.into_inner();
152
153        // We can assume gather_end < next_start.
154        debug_assert!(gather_end < next_start); // real assert
155
156        // If they touch, set gather to the union and loop.
157        if gather_end.add_one() == next_start {
158            self.gather = Some(gather_start..=next_end);
159            return None;
160        }
161
162        // Next is disjoint from gather, so return gather and set gather to next.
163        self.gather = Some(next_start..=next_end);
164        Some(Some(gather_start..=gather_end))
165    }
166
167    #[inline]
168    pub(crate) fn new(iter: I) -> Self {
169        Self {
170            iter,
171            start_or_min_value: T::min_value(),
172            end_heap: BinaryHeap::with_capacity(10),
173            next_again: None,
174            gather: None,
175        }
176    }
177}
178
179impl<T, L, R> SymDiffMerge<T, L, R>
180where
181    T: Integer,
182    L: SortedDisjoint<T>,
183    R: SortedDisjoint<T>,
184{
185    #[inline]
186    pub(crate) fn new2(left: L, right: R) -> Self {
187        let iter = Merge::new(left, right);
188        Self::new(iter)
189    }
190}
191
192impl<T, J> SymDiffKMerge<T, J>
193where
194    T: Integer,
195    J: SortedDisjoint<T>,
196{
197    #[inline]
198    pub(crate) fn new_k<K>(k: K) -> Self
199    where
200        K: IntoIterator<Item = J>,
201    {
202        let iter = KMerge::new(k);
203        Self::new(iter)
204    }
205}