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