use std::num::NonZeroUsize;
use int_interval::traits::{COStartLenConstruct, IntCO, IntPrimitive};
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use crate::{HeightRun, IntCOStack};
#[derive(Debug, Clone, Copy)]
pub struct StackWindow<'a, I>
where
I: IntCO,
{
pub(crate) stack: &'a IntCOStack<I>,
pub(crate) interval: I,
pub(crate) point_start: usize,
pub(crate) point_end: usize,
pub(crate) height_at_start: usize,
}
impl<'a, I> StackWindow<'a, I>
where
I: IntCO,
{
pub(crate) fn new(stack: &'a IntCOStack<I>, interval: I) -> Self {
let points = stack.change_points();
let window_start = interval.start();
let window_end = interval.end_excl();
let point_start = points.partition_point(|point| point.at <= window_start);
let point_end =
point_start + points[point_start..].partition_point(|point| point.at < window_end);
let height_at_start = point_start
.checked_sub(1)
.map_or(0, |index| points[index].height_after);
Self {
stack,
interval,
point_start,
point_end,
height_at_start,
}
}
#[inline]
pub const fn stack(&self) -> &'a IntCOStack<I> {
self.stack
}
#[inline]
pub const fn interval(&self) -> &I {
&self.interval
}
#[inline]
fn height_run_count(&self) -> usize {
self.point_end - self.point_start + 1
}
#[inline]
fn height_run_at(&self, run_index: usize) -> HeightRun<I> {
debug_assert!(run_index < self.height_run_count());
let points = self.stack.change_points();
let point_index = self.point_start + run_index;
let start = if run_index == 0 {
self.interval.start()
} else {
points[point_index - 1].at
};
let end_excl = if point_index < self.point_end {
points[point_index].at
} else {
self.interval.end_excl()
};
let height = if run_index == 0 {
self.height_at_start
} else {
points[point_index - 1].height_after
};
HeightRun {
interval: unsafe { I::new_unchecked(start, end_excl) },
height,
}
}
}
impl<I> StackWindow<'_, I>
where
I: IntCO,
{
#[inline]
pub fn iter_height_runs(
&self,
) -> impl DoubleEndedIterator<Item = HeightRun<I>> + ExactSizeIterator {
(0..self.height_run_count()).map(move |run_index| self.height_run_at(run_index))
}
}
impl<I> StackWindow<'_, I>
where
I: IntCO + Send + Sync,
{
#[inline]
pub fn par_iter_height_runs(&self) -> impl IndexedParallelIterator<Item = HeightRun<I>> {
(0..self.height_run_count())
.into_par_iter()
.map(move |run_index| self.height_run_at(run_index))
}
}
#[derive(Debug, Clone)]
pub(crate) struct WindowIter<'a, I>
where
I: IntCO,
{
pub(crate) stack: &'a IntCOStack<I>,
pub(crate) from: I::CoordType,
pub(crate) interval: I,
pub(crate) point_start: usize,
pub(crate) point_end: usize,
pub(crate) height_at_start: usize,
pub(crate) remaining: usize,
pub(crate) total_count: usize,
pub(crate) consumed_back: usize,
}
impl<'a, I> WindowIter<'a, I>
where
I: IntCO + COStartLenConstruct + Copy,
{
pub(crate) fn new(
stack: &'a IntCOStack<I>,
from: I::CoordType,
len: I::MeasureType,
count: NonZeroUsize,
) -> Self {
let interval = I::checked_from_start_len(from, len)
.expect("validated window count guarantees a representable first window");
let sw = StackWindow::new(stack, interval);
Self {
stack,
from,
interval,
point_start: sw.point_start,
point_end: sw.point_end,
height_at_start: sw.height_at_start,
remaining: count.get(),
total_count: count.get(),
consumed_back: 0,
}
}
#[inline]
fn advance(&mut self) {
debug_assert!(self.remaining > 0);
let new_start = self
.interval
.start()
.checked_next()
.expect("remaining > 0 guarantees the next start coordinate fits");
let new_end = self
.interval
.end_excl()
.checked_next()
.expect("remaining > 0 guarantees the next end coordinate fits");
let new_interval = unsafe { I::new_unchecked(new_start, new_end) };
let points = self.stack.change_points();
while self.point_start < points.len() && points[self.point_start].at <= new_start {
self.height_at_start = points[self.point_start].height_after;
self.point_start += 1;
}
while self.point_end < points.len() && points[self.point_end].at < new_end {
self.point_end += 1;
}
self.interval = new_interval;
self.remaining -= 1;
}
}
impl<'a, I> Iterator for WindowIter<'a, I>
where
I: IntCO + COStartLenConstruct + Copy,
{
type Item = StackWindow<'a, I>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let window = StackWindow {
stack: self.stack,
interval: self.interval,
point_start: self.point_start,
point_end: self.point_end,
height_at_start: self.height_at_start,
};
self.advance();
Some(window)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<'a, I> ExactSizeIterator for WindowIter<'a, I>
where
I: IntCO + COStartLenConstruct + Copy,
{
#[inline]
fn len(&self) -> usize {
self.remaining
}
}
#[cfg(test)]
mod tests_for_window_iter;
#[cfg(test)]
mod tests_for_new;
#[cfg(test)]
mod tests_for_height_run_at;
#[cfg(test)]
mod tests_for_iter_height_runs;