Skip to main content

int_interval_stack/int_co_stack/
impls_for_access.rs

1use super::*;
2
3/// Projects the covered interval set from canonical stack change points.
4///
5/// A stack change-point sequence describes a piecewise-constant height
6/// function. The covered set is exactly the union of all coordinate ranges
7/// whose height is positive.
8///
9/// This function scans positive-height runs:
10///
11/// ```text
12/// height: 0 -> positive    opens a covered interval
13/// height: positive -> 0    closes a covered interval
14/// positive -> positive     keeps the current covered interval open
15/// ```
16///
17/// # Input invariants
18///
19/// `points` must be the canonical output of stack construction:
20///
21/// - coordinates are strictly increasing;
22/// - adjacent points have different `height_after` values;
23/// - the final height, when non-empty, is zero.
24///
25/// # Output
26///
27/// The returned set is canonical. Positive-height runs are emitted in
28/// ascending order and are separated by zero-height gaps.
29#[inline]
30fn covered_from_change_points<I>(points: &[ChangePoint<I::CoordType>]) -> IntCOSet<I>
31where
32    I: IntCO,
33{
34    let mut out = Vec::new();
35    let mut start = None;
36
37    for p in points {
38        match (start, p.height_after) {
39            (None, h) if h > 0 => {
40                start = Some(p.at);
41            }
42            (Some(s), 0) => {
43                // SAFETY:
44                // Canonical change points are strictly increasing, and a
45                // positive-height run can only close at a later coordinate.
46                out.push(unsafe { I::new_unchecked(s, p.at) });
47                start = None;
48            }
49            _ => {}
50        }
51    }
52
53    debug_assert!(
54        start.is_none(),
55        "canonical stack change points must end at zero height"
56    );
57
58    // SAFETY:
59    // Positive-height runs are emitted in ascending order and are separated by
60    // zero-height gaps, so the result is canonical.
61    unsafe { IntCOSet::new_unchecked(out) }
62}
63
64impl<I> IntCOStack<I>
65where
66    I: IntCO,
67{
68    #[inline]
69    pub fn change_points(&self) -> &[ChangePoint<I::CoordType>] {
70        &self.change_points
71    }
72
73    #[inline]
74    pub fn covered(&self) -> &IntCOSet<I> {
75        self.covered
76            .get_or_init(|| covered_from_change_points::<I>(&self.change_points))
77    }
78
79    #[inline]
80    pub fn height_stats(&self) -> HeightStats {
81        self.height_stats
82    }
83
84    #[inline]
85    pub fn height_at(&self, x: I::CoordType) -> usize {
86        let i = self.change_points.partition_point(|p| p.at <= x);
87        if i == 0 {
88            0
89        } else {
90            self.change_points[i - 1].height_after
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests_for_access;
97
98#[cfg(test)]
99mod tests_for_covered_from_change_points;