1use core::{
2 cmp::{self, min},
3 iter::FusedIterator,
4 ops::RangeInclusive,
5};
6
7use alloc::collections::BinaryHeap;
8
9use crate::{
10 Integer, MergeMap, SortedDisjointMap, SymDiffKMergeMap, SymDiffMergeMap,
11 map::ValueRef,
12 merge_map::KMergeMap,
13 sorted_disjoint_map::{Priority, PrioritySortedStartsMap},
14};
15
16#[derive(Clone, Debug)]
22#[must_use = "iterators are lazy and do nothing unless consumed"]
23pub struct SymDiffIterMap<T, VR, I> {
24 iter: I,
25 next_item: Option<Priority<T, VR>>,
26 workspace: BinaryHeap<Priority<T, VR>>,
27 workspace_next_end: Option<T>,
28 gather: Option<(RangeInclusive<T>, VR)>,
29 ready_to_go: Option<(RangeInclusive<T>, VR)>,
30}
31
32#[expect(clippy::ref_option)]
33fn min_next_end<T: Integer>(next_end: &Option<T>, next_item_end: T) -> T {
34 next_end.map_or_else(
35 || next_item_end,
36 |current_end| cmp::min(current_end, next_item_end),
37 )
38}
39
40impl<T, VR, I> FusedIterator for SymDiffIterMap<T, VR, I>
41where
42 T: Integer,
43 VR: ValueRef,
44 I: PrioritySortedStartsMap<T, VR>,
45{
46}
47
48impl<T, VR, I> Iterator for SymDiffIterMap<T, VR, I>
49where
50 T: Integer,
51 VR: ValueRef,
52 I: PrioritySortedStartsMap<T, VR>,
53{
54 type Item = (RangeInclusive<T>, VR);
55
56 fn next(&mut self) -> Option<(RangeInclusive<T>, VR)> {
57 loop {
59 if let Some(value) = self.ready_to_go.take() {
60 return Some(value);
62 }
63
64 if let Some(next_item) = self.next_item.take() {
66 let (next_start, next_end) = next_item.start_and_end();
67
68 let Some(best) = self.workspace.peek() else {
70 self.workspace_next_end =
71 Some(min_next_end(&self.workspace_next_end, next_end));
72 self.workspace.push(next_item);
73 self.next_item = self.iter.next();
74 continue; };
76 let best = best.range_value();
77 if next_start == *best.0.start() {
78 self.workspace_next_end =
80 Some(min_next_end(&self.workspace_next_end, next_end));
81 self.workspace.push(next_item);
82 self.next_item = self.iter.next();
83 continue; }
85
86 self.next_item = Some(next_item);
88 }
89
90 let Some(best) = self.workspace.peek() else {
92 debug_assert!(self.next_item.is_none());
93 debug_assert!(self.ready_to_go.is_none());
94 return self.gather.take();
95 };
96 let best = best.range_value();
97
98 let mut next_end = self
102 .workspace_next_end
103 .take()
104 .expect("Real Assert: safe because we know the workspace is not empty");
105 if let Some(next_item) = self.next_item.as_ref() {
106 next_end = min(next_item.start().sub_one(), next_end);
107 }
108
109 if let Some(mut gather) = self.gather.take() {
111 if gather.1.borrow() == best.1.borrow()
112 && (*gather.0.end()).add_one() == *best.0.start()
113 {
114 if self.workspace.len().is_odd() {
115 gather.0 = *gather.0.start()..=next_end;
117 self.gather = Some(gather);
118 } else {
119 self.ready_to_go = Some(gather);
121 debug_assert!(self.gather.is_none());
122 }
123 } else {
124 self.ready_to_go = Some(gather);
126 if self.workspace.len().is_odd() {
128 self.gather = Some((*best.0.start()..=next_end, best.1.clone()));
129 } else {
130 debug_assert!(self.gather.is_none());
131 }
132 }
133 } else {
134 if self.workspace.len().is_odd() {
137 self.gather = Some((*best.0.start()..=next_end, best.1.clone()));
138 } else {
139 debug_assert!(self.gather.is_none());
140 }
141 }
142
143 self.workspace_next_end = None;
146 self.workspace = self
147 .workspace
148 .drain()
149 .filter(|item| item.end() > next_end)
150 .map(|mut item| {
151 item.set_range(next_end.add_one()..=item.end());
152 self.workspace_next_end =
153 Some(min_next_end(&self.workspace_next_end, item.end()));
154 item
155 })
156 .collect();
157 } }
159}
160
161impl<T, VR, L, R> SymDiffMergeMap<T, VR, L, R>
162where
163 T: Integer,
164 VR: ValueRef,
165 L: SortedDisjointMap<T, VR>,
166 R: SortedDisjointMap<T, VR>,
167{
168 #[inline]
169 pub(crate) fn new2(left: L, right: R) -> Self {
170 let iter = MergeMap::new(left, right);
171 Self::new(iter)
172 }
173}
174
175impl<T, VR, J> SymDiffKMergeMap<T, VR, J>
176where
177 T: Integer,
178 VR: ValueRef,
179 J: SortedDisjointMap<T, VR>,
180{
181 #[inline]
182 pub(crate) fn new_k<K>(k: K) -> Self
183 where
184 K: IntoIterator<Item = J>,
185 {
186 let iter = KMergeMap::new(k);
187 Self::new(iter)
188 }
189}
190
191impl<T, VR, I> SymDiffIterMap<T, VR, I>
192where
193 T: Integer,
194 VR: ValueRef,
195 I: PrioritySortedStartsMap<T, VR>,
196{
197 #[inline]
198 pub(crate) fn new(mut iter: I) -> Self {
199 let item = iter.next();
200 Self {
201 iter,
202 next_item: item,
203 workspace: BinaryHeap::new(),
204 workspace_next_end: None,
205 gather: None,
206 ready_to_go: None,
207 }
208 }
209}
210
211#[allow(clippy::wrong_self_convention)]
212#[allow(clippy::redundant_pub_crate)]
213pub(crate) trait UsizeExtensions {
214 fn is_odd(self) -> bool;
215}
216
217impl UsizeExtensions for usize {
218 #[inline]
219 fn is_odd(self) -> bool {
220 self & 1 != 0
221 }
222}