odsek 0.1.0

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

/// An endpoint without an explicit open/closed tag.
///
/// On the left side of an interval it acts as **closed**, on the right side
/// as **open** — i.e. every interval is `[a, b)`. This is the lightweight
/// counterpart to [`EndpointOC`](crate::EndpointOC) for use cases that don't
/// need mixed open/closed flavors.
///
/// Construct with the tuple-struct form `EndpointSymmetric(value)` or the
/// helper [`endpoint_symmetric`].
#[derive(Debug, Clone, PartialEq)]
pub struct EndpointSymmetric<T>(pub T);

impl<T> EndpointToggle for EndpointSymmetric<T> {
    fn toggle(self) -> Self {
        self
    }
}

impl<E> EndpointMap for EndpointSymmetric<E> {
    type EndpointValue = E;
    fn map_endpoint(self, f: impl Fn(Self::EndpointValue) -> Self::EndpointValue) -> Self {
        EndpointSymmetric(f(self.0))
    }
}

impl<T, U> EndpointMapInto<U> for EndpointSymmetric<T> {
    type EndpointValue = T;
    type Output = EndpointSymmetric<U>;
    fn map_endpoint_into(self, f: impl Fn(T) -> U) -> EndpointSymmetric<U> {
        EndpointSymmetric(f(self.0))
    }
}

impl<T> EndpointSymmetric<T> {
    /// Borrow the underlying value.
    pub fn value(&self) -> &T {
        &self.0
    }

    /// Consume the endpoint and return the underlying value.
    pub fn into_value(self) -> T {
        self.0
    }
}

/// Convenience constructor for [`EndpointSymmetric`].
pub fn endpoint_symmetric<T>(value: T) -> EndpointSymmetric<T> {
    EndpointSymmetric(value)
}
// Left Left
impl<T> PartialOrd<LeftT<EndpointSymmetric<T>>> for LeftT<EndpointSymmetric<T>>
where
    T: Ord,
{
    fn partial_cmp(&self, other: &LeftT<EndpointSymmetric<T>>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

// Right Right
impl<T> PartialOrd<RightT<EndpointSymmetric<T>>> for RightT<EndpointSymmetric<T>>
where
    T: Ord,
{
    fn partial_cmp(&self, other: &RightT<EndpointSymmetric<T>>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T> Eq for LeftT<EndpointSymmetric<T>> where T: Eq {}
impl<T> Eq for RightT<EndpointSymmetric<T>> where T: Eq {}

impl<T> Ord for LeftT<EndpointSymmetric<T>>
where
    T: Ord,
{
    fn cmp(&self, other: &Self) -> Ordering {
        let Left(ref a) = self;
        let Left(ref b) = other;
        a.0.cmp(&b.0)
    }
}

impl<T> Ord for RightT<EndpointSymmetric<T>>
where
    T: Ord,
{
    fn cmp(&self, other: &Self) -> Ordering {
        let Right(ref a) = self;
        let Right(ref b) = other;
        a.0.cmp(&b.0)
    }
}

#[cfg(test)]
mod tests {
    use core::cmp::Ordering;

    use crate::EndpointSymmetric;
    use crate::LeftT;
    use crate::RightT;

    #[test]
    fn toggle_is_identity() {
        use crate::EndpointToggle;
        let e = EndpointSymmetric(5);
        let toggled = e.toggle();
        assert_eq!(toggled, EndpointSymmetric(5));
    }

    #[test]
    fn map_endpoint() {
        use crate::EndpointMap;
        let e = EndpointSymmetric(3);
        let mapped = e.map_endpoint(|x| x * 2);
        assert_eq!(mapped, EndpointSymmetric(6));
    }

    #[test]
    fn left_left_cmp() {
        let a = LeftT::Left(EndpointSymmetric(2));
        let b = LeftT::Left(EndpointSymmetric(2));
        assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));

        let a = LeftT::Left(EndpointSymmetric(1));
        let b = LeftT::Left(EndpointSymmetric(3));
        assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
        assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
    }

    #[test]
    fn right_right_cmp() {
        let a = RightT::Right(EndpointSymmetric(2));
        let b = RightT::Right(EndpointSymmetric(2));
        assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));

        let a = RightT::Right(EndpointSymmetric(1));
        let b = RightT::Right(EndpointSymmetric(3));
        assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
        assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
    }

    #[test]
    fn interval_with_symmetric_endpoints() {
        use crate::Interval;
        // Represents [2, 5) — closed left, open right
        let iv = Interval::new(EndpointSymmetric(2), EndpointSymmetric(5), "A");
        assert_eq!(iv.left().endpoint().0, 2);
        assert_eq!(iv.right().endpoint().0, 5);
        assert_eq!(iv.value(), "A");
    }
}