int_interval_stack/int_co_stack/impls_for_iter.rs
1use std::num::NonZeroUsize;
2
3use either::Either;
4
5use super::*;
6
7impl<I> IntCOStack<I>
8where
9 I: IntCO,
10{
11 /// Iterates over positive-height segments by scanning canonical change points.
12 ///
13 /// This is the height-preserving fallback path used when the covered set alone
14 /// is not sufficient. Unlike `covered.iter_intervals()`, this iterator keeps
15 /// every height boundary, so adjacent covered ranges with different heights are
16 /// yielded as separate segments.
17 ///
18 /// Zero-height gaps are skipped. For each adjacent change-point pair
19 /// `(p[i], p[i + 1])`, the segment height is `p[i].height_after`.
20 #[inline]
21 fn iter_segments_from_change_points(&self) -> impl Iterator<Item = HeightSegment<I>> + '_ {
22 self.change_points.windows(2).filter_map(|w| {
23 let start = w[0].at;
24 let end_excl = w[1].at;
25
26 NonZeroUsize::new(w[0].height_after).map(|height| HeightSegment {
27 // SAFETY:
28 // Canonical change points are strictly increasing, so every
29 // adjacent pair forms a valid non-empty interval.
30 interval: unsafe { I::new_unchecked(start, end_excl) },
31 height,
32 })
33 })
34 }
35
36 /// Iterates over positive-height stack segments.
37 ///
38 /// Each item is a closed-open interval together with the stack height that
39 /// applies throughout that interval.
40 ///
41 /// If all positive-height regions have the same height, the segment intervals
42 /// are exactly the covered set intervals and the shared height is supplied
43 /// from `height_stats`. Otherwise, the height-preserving segmentation is
44 /// reconstructed from change points.
45 #[inline]
46 pub fn iter_height_segments(&self) -> impl Iterator<Item = HeightSegment<I>> + '_ {
47 if let Some(height) = self.height_stats.uniform_positive_height() {
48 Either::Left(
49 self.covered()
50 .iter_intervals()
51 .map(move |interval| HeightSegment { interval, height }),
52 )
53 } else {
54 Either::Right(self.iter_segments_from_change_points())
55 }
56 }
57
58 /// Iterates over positive-height stack segments whose height is at least
59 /// `min_height`.
60 ///
61 /// Each item is a closed-open interval together with the stack height that
62 /// applies throughout that interval.
63 ///
64 /// This method uses `height_stats` for cheap fast paths:
65 ///
66 /// - if `min_height` is greater than the observed maximum height, the iterator
67 /// is empty;
68 /// - if `min_height` is less than or equal to the observed minimum positive
69 /// height, every positive-height segment matches and `iter_height_segments`
70 /// is reused;
71 /// - otherwise, canonical change points are scanned and filtered.
72 ///
73 /// A `min_height` of zero is treated the same as requesting all positive-height
74 /// segments, because zero-height gaps are never yielded.
75 #[inline]
76 pub fn iter_height_segments_at_least(
77 &self,
78 min_height: usize,
79 ) -> impl Iterator<Item = HeightSegment<I>> + '_ {
80 let stack_max = self.height_stats.max_height();
81
82 if min_height > stack_max {
83 Either::Left(std::iter::empty())
84 } else if min_height <= self.height_stats.min_positive_height_or_zero() {
85 Either::Right(Either::Left(self.iter_height_segments()))
86 } else {
87 Either::Right(Either::Right(
88 self.iter_segments_from_change_points()
89 .filter(move |segment| segment.height.get() >= min_height),
90 ))
91 }
92 }
93
94 /// Iterates over positive-height stack segments whose height is at most
95 /// `max_height`.
96 ///
97 /// Each item is a closed-open interval together with the stack height that
98 /// applies throughout that interval.
99 ///
100 /// This method uses `height_stats` for cheap fast paths:
101 ///
102 /// - if `max_height` is zero, the iterator is empty because zero-height gaps
103 /// are never yielded;
104 /// - if `max_height` is smaller than the observed minimum positive height, the
105 /// iterator is empty;
106 /// - if `max_height` is greater than or equal to the observed maximum height,
107 /// every positive-height segment matches and `iter_height_segments` is reused;
108 /// - otherwise, canonical change points are scanned and filtered.
109 #[inline]
110 pub fn iter_height_segments_at_most(
111 &self,
112 max_height: usize,
113 ) -> impl Iterator<Item = HeightSegment<I>> + '_ {
114 let stack_min = self.height_stats.min_positive_height_or_zero();
115
116 if max_height == 0 || max_height < stack_min {
117 Either::Left(std::iter::empty())
118 } else if max_height >= self.height_stats.max_height() {
119 Either::Right(Either::Left(self.iter_height_segments()))
120 } else {
121 Either::Right(Either::Right(
122 self.iter_segments_from_change_points()
123 .filter(move |segment| segment.height.get() <= max_height),
124 ))
125 }
126 }
127
128 /// Iterates over positive-height stack segments whose height is exactly
129 /// `target_height`.
130 ///
131 /// Each item is a closed-open interval together with the stack height that
132 /// applies throughout that interval.
133 ///
134 /// This method uses `height_stats` for cheap fast paths:
135 ///
136 /// - if `target_height` is zero, the iterator is empty because zero-height gaps
137 /// are never yielded;
138 /// - if `target_height` is outside the observed positive-height range, the
139 /// iterator is empty;
140 /// - if all positive-height regions share the same height and `target_height`
141 /// equals that height, the intervals are exactly the covered set intervals;
142 /// - otherwise, canonical change points are scanned and filtered.
143 #[inline]
144 pub fn iter_height_segments_exactly(
145 &self,
146 target_height: usize,
147 ) -> impl Iterator<Item = HeightSegment<I>> + '_ {
148 let Some(target_height) = NonZeroUsize::new(target_height) else {
149 return Either::Left(std::iter::empty());
150 };
151
152 let target = target_height.get();
153
154 if target < self.height_stats.min_positive_height_or_zero()
155 || target > self.height_stats.max_height()
156 {
157 Either::Left(std::iter::empty())
158 } else if self.height_stats.uniform_positive_height() == Some(target_height) {
159 Either::Right(Either::Left(self.covered().iter_intervals().map(
160 move |interval| HeightSegment {
161 interval,
162 height: target_height,
163 },
164 )))
165 } else {
166 Either::Right(Either::Right(
167 self.iter_segments_from_change_points()
168 .filter(move |segment| segment.height == target_height),
169 ))
170 }
171 }
172
173 /// Iterates over positive-height stack segments whose height is within
174 /// `min_height..=max_height`.
175 ///
176 /// Each item is a closed-open interval together with the stack height that
177 /// applies throughout that interval.
178 ///
179 /// Zero-height gaps are never yielded. Therefore, a `min_height` of zero is
180 /// treated as if it included all positive heights.
181 ///
182 /// This method uses `height_stats` for cheap fast paths:
183 ///
184 /// - if `min_height > max_height`, the iterator is empty;
185 /// - if the stack has no positive-height segments, the iterator is empty;
186 /// - if the requested range does not overlap the observed positive-height
187 /// range, the iterator is empty;
188 /// - if the requested range covers the full observed positive-height range,
189 /// every segment matches and `iter_height_segments` is reused;
190 /// - otherwise, canonical change points are scanned and filtered.
191 #[inline]
192 pub fn iter_height_segments_between(
193 &self,
194 min_height: usize,
195 max_height: usize,
196 ) -> impl Iterator<Item = HeightSegment<I>> + '_ {
197 let stack_min = self.height_stats.min_positive_height_or_zero();
198 let stack_max = self.height_stats.max_height();
199 let query_min = min_height.max(1);
200
201 if min_height > max_height
202 || !self.height_stats.has_positive_height()
203 || max_height < stack_min
204 || query_min > stack_max
205 {
206 Either::Left(std::iter::empty())
207 } else if query_min <= stack_min && max_height >= stack_max {
208 Either::Right(Either::Left(self.iter_height_segments()))
209 } else {
210 Either::Right(Either::Right(
211 self.iter_segments_from_change_points()
212 .filter(move |segment| {
213 query_min <= segment.height.get() && segment.height.get() <= max_height
214 }),
215 ))
216 }
217 }
218
219 /// Iterates over positive-height stack segments whose height is the observed
220 /// maximum stack height.
221 ///
222 /// Each item is a closed-open interval together with the peak height that
223 /// applies throughout that interval.
224 ///
225 /// If the stack has no positive-height segments, the iterator is empty.
226 ///
227 /// This is equivalent to:
228 ///
229 /// ```text
230 /// iter_height_segments_exactly(height_stats.max_height())
231 /// ```
232 #[inline]
233 pub fn iter_peak_height_segments(&self) -> impl Iterator<Item = HeightSegment<I>> + '_ {
234 self.iter_height_segments_exactly(self.height_stats.max_height())
235 }
236}
237
238#[cfg(test)]
239mod tests_for_iter_segments_from_change_points;
240
241#[cfg(test)]
242mod tests_for_iter_height_segments;
243
244#[cfg(test)]
245mod tests_for_iter_height_segments_at_least;
246
247#[cfg(test)]
248mod tests_for_iter_height_segments_at_most;
249
250#[cfg(test)]
251mod tests_for_iter_height_segments_exactly;
252
253#[cfg(test)]
254mod tests_for_iter_height_segments_between;