odsek 0.1.0

Lazy, pull-based composition of mathematical interval sets with open/closed endpoints, no_std-compatible.
Documentation
use crate::interval::*;
use crate::intervals::*;

/// Memoizes the most recent [`Intervals::head`] call.
///
/// The combinators [`IntervalsAnd`](crate::IntervalsAnd) and
/// [`IntervalsOr`](crate::IntervalsOr) call `head` on each child multiple
/// times per output (once per side, sometimes more during boundary
/// alignment). Wrapping inputs in `IntervalsCache` means back-to-back queries
/// at the same or unchanged `pos` are served from a single-slot cache
/// instead of recomputing.
///
/// Construct directly with [`new`](Self::new), or implicitly via
/// [`and_cached`](crate::and_cached) / [`or_cached`](crate::or_cached).
pub struct IntervalsCache<Is>
where
    Is: Intervals,
{
    intervals: Is,
    pos: Option<Option<LeftT<Is::Endpoint>>>,
    value: Option<Interval<Is::Endpoint, Is::Value>>,
}

impl<Is> IntervalsCache<Is>
where
    Is: Intervals,
{
    /// Wrap an inner stream with a single-slot memoization layer.
    pub fn new(intervals: Is) -> IntervalsCache<Is> {
        IntervalsCache {
            intervals,
            pos: None,
            value: None,
        }
    }

    /// Discard the cache and return the inner stream.
    pub fn uncache(self) -> Is {
        self.intervals
    }
}

impl<Is> Intervals for IntervalsCache<Is>
where
    Is: Intervals,
    Is::Endpoint: Clone + EndpointToggle,
    Is::Value: Clone,
    LeftT<Is::Endpoint>: Ord,
{
    type Endpoint = Is::Endpoint;
    type Value = Is::Value;

    fn head(
        &mut self,
        pos: Option<LeftT<Self::Endpoint>>,
    ) -> Option<Interval<Self::Endpoint, Self::Value>> {
        if let Some(cached_pos) = &self.pos {
            if cached_pos == &pos {
                return self.value.clone();
            }
        }
        self.value = self.intervals.head(pos.clone());
        self.pos = Some(pos);
        self.value.clone()
    }
}