use super::*;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
const BATCH_SIZE: usize = 128;
impl<I> Default for IntCOStack<I>
where
I: IntCO,
{
#[inline]
fn default() -> Self {
Self {
points: Arc::from([]),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EndpointKind {
Enter,
Leave,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Endpoint<C> {
at: C,
kind: EndpointKind,
}
fn build_points_from_endpoints<C>(mut endpoints: Vec<Endpoint<C>>) -> Vec<ChangePoint<C>>
where
C: Copy + Ord,
{
endpoints.sort_unstable_by_key(|endpoint| endpoint.at);
let mut points = Vec::with_capacity(endpoints.len());
let mut height_after = 0usize;
let mut cursor = 0usize;
while cursor < endpoints.len() {
let at = endpoints[cursor].at;
let mut enters = 0usize;
let mut leaves = 0usize;
while cursor < endpoints.len() && endpoints[cursor].at == at {
match endpoints[cursor].kind {
EndpointKind::Enter => enters += 1,
EndpointKind::Leave => leaves += 1,
}
cursor += 1;
}
let next_height = if enters >= leaves {
height_after.checked_add(enters - leaves)
} else {
height_after.checked_sub(leaves - enters)
}
.expect("valid intervals must never produce a negative stack height");
if next_height != height_after {
points.push(ChangePoint {
at,
height_after: next_height,
});
}
height_after = next_height;
}
debug_assert_eq!(
height_after, 0,
"all finite half-open intervals must eventually close"
);
points
}
#[cfg(test)]
mod tests_for_build_points_from_endpoints;
fn merge_points<C>(lhs: &[ChangePoint<C>], rhs: &[ChangePoint<C>]) -> Vec<ChangePoint<C>>
where
C: Copy + Ord,
{
let mut out = Vec::with_capacity(lhs.len() + rhs.len());
let mut lhs_height = 0usize;
let mut rhs_height = 0usize;
let mut merged_height = 0usize;
let mut lhs_cursor = 0usize;
let mut rhs_cursor = 0usize;
while lhs_cursor < lhs.len() || rhs_cursor < rhs.len() {
let at = match (lhs.get(lhs_cursor), rhs.get(rhs_cursor)) {
(Some(l), Some(r)) if l.at < r.at => {
lhs_height = l.height_after;
lhs_cursor += 1;
l.at
}
(Some(l), Some(r)) if r.at < l.at => {
rhs_height = r.height_after;
rhs_cursor += 1;
r.at
}
(Some(l), Some(r)) => {
lhs_height = l.height_after;
rhs_height = r.height_after;
lhs_cursor += 1;
rhs_cursor += 1;
l.at
}
(Some(l), None) => {
lhs_height = l.height_after;
lhs_cursor += 1;
l.at
}
(None, Some(r)) => {
rhs_height = r.height_after;
rhs_cursor += 1;
r.at
}
(None, None) => unreachable!(),
};
let next_merged_height = lhs_height
.checked_add(rhs_height)
.expect("stack height overflow");
if next_merged_height != merged_height {
out.push(ChangePoint {
at,
height_after: next_merged_height,
});
merged_height = next_merged_height;
}
}
debug_assert_eq!(merged_height, 0);
out
}
#[cfg(test)]
mod tests_for_merge_points;
#[derive(Debug)]
struct StackBuildAcc<C>
where
C: Copy + Ord,
{
endpoints: Vec<Endpoint<C>>,
levels: Vec<Option<Vec<ChangePoint<C>>>>,
}
impl<C> StackBuildAcc<C>
where
C: Copy + Ord,
{
#[inline]
fn new() -> Self {
Self {
endpoints: Vec::with_capacity(BATCH_SIZE.saturating_mul(2)),
levels: Vec::new(),
}
}
#[inline]
fn push_interval<I>(&mut self, interval: I)
where
I: IntCO<CoordType = C>,
{
self.endpoints.push(Endpoint {
at: interval.start(),
kind: EndpointKind::Enter,
});
self.endpoints.push(Endpoint {
at: interval.end_excl(),
kind: EndpointKind::Leave,
});
if self.endpoints.len() >= BATCH_SIZE.saturating_mul(2) {
self.flush();
}
}
#[inline]
fn finish(mut self) -> Vec<ChangePoint<C>> {
self.flush();
self.levels
.into_iter()
.flatten()
.reduce(|lhs, rhs| merge_points(&lhs, &rhs))
.unwrap_or_default()
}
#[inline]
fn flush(&mut self) {
if self.endpoints.is_empty() {
return;
}
let endpoints = core::mem::replace(
&mut self.endpoints,
Vec::with_capacity(BATCH_SIZE.saturating_mul(2)),
);
self.push_points(build_points_from_endpoints(endpoints));
}
fn push_points(&mut self, mut carry: Vec<ChangePoint<C>>) {
let mut level = 0usize;
loop {
if level == self.levels.len() {
self.levels.push(Some(carry));
break;
}
match self.levels[level].take() {
None => {
self.levels[level] = Some(carry);
break;
}
Some(points) => {
carry = merge_points(&points, &carry);
level += 1;
}
}
}
}
}
#[cfg(test)]
mod tests_for_stack_build_acc;
impl<I> FromIterator<I> for IntCOStack<I>
where
I: IntCO,
{
#[inline]
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = I>,
{
let mut acc = StackBuildAcc::new();
for interval in iter {
acc.push_interval(interval);
}
Self {
points: acc.finish().into(),
}
}
}
impl<I> FromParallelIterator<I> for IntCOStack<I>
where
I: IntCO + Send,
I::CoordType: Send,
{
#[inline]
fn from_par_iter<T>(par_iter: T) -> Self
where
T: IntoParallelIterator<Item = I>,
{
let points = par_iter
.into_par_iter()
.fold(StackBuildAcc::new, |mut acc, interval| {
acc.push_interval(interval);
acc
})
.map(StackBuildAcc::finish)
.reduce(Vec::new, |lhs, rhs| merge_points(&lhs, &rhs));
Self {
points: points.into(),
}
}
}
#[cfg(test)]
mod tests_for_from_iter_and_from_par_iter;