ranges 0.3.3

This crate provides a generic alternative to core/std ranges, set-operations to work with them and a range set that can efficiently store them with the least amount of memory possible.
Documentation
use core::ops::Bound;

use crate::{Domain, GenericRange};

impl<T: Domain> GenericRange<T> {
    /// Constructs a singleton by cloning the input storing it in included bounds for both start
    /// and end.
    ///
    /// # Example
    /// ```
    /// use core::ops::Bound;
    /// use ranges::GenericRange;
    ///
    /// assert_eq!(
    ///     GenericRange::singleton(42),
    ///     (Bound::Included(42), Bound::Included(42)).into()
    /// );
    /// ```
    #[must_use]
    pub fn singleton(value: T) -> Self
    where
        T: Clone,
    {
        Self::new_with_bounds(Bound::Included(value.clone()), Bound::Included(value))
    }

    /// Returns true if the range only contains a single item.
    ///
    /// # Examples
    /// ```
    /// use core::ops::Bound;
    /// use ranges::GenericRange;
    ///
    /// assert!(!GenericRange::from(1..1).is_singleton());
    /// assert!(GenericRange::from(1..2).is_singleton());
    ///
    /// assert!(GenericRange::from(1..=1).is_singleton());
    /// assert!(!GenericRange::from(1..=2).is_singleton());
    ///
    /// assert!(GenericRange::from((Bound::Excluded(1), Bound::Included(2))).is_singleton());
    /// ```
    #[must_use]
    pub fn is_singleton(&self) -> bool {
        if self.is_empty() {
            return false;
        }

        if <T as Domain>::DISCRETE {
            match (&self.start, &self.end) {
                (&Bound::Unbounded, _) | (_, &Bound::Unbounded) => false,

                (&Bound::Included(ref x), &Bound::Excluded(ref y))
                | (&Bound::Excluded(ref x), &Bound::Included(ref y)) => x < y && x.is_next_to(y),
                (&Bound::Included(ref x), &Bound::Included(ref y)) => x == y,

                (&Bound::Excluded(ref x), &Bound::Excluded(ref y)) => x.shares_neighbour_with(y),
            }
        } else {
            match (&self.start, &self.end) {
                (&Bound::Included(ref x), &Bound::Included(ref y)) => x == y,
                _ => false,
            }
        }
    }
}

impl<T: Domain + Clone> From<T> for GenericRange<T> {
    #[must_use]
    fn from(val: T) -> Self {
        Self::singleton(val)
    }
}

#[cfg(test)]
mod tests_discrete {
    use core::ops::Bound;

    use crate::GenericRange;

    #[test]
    fn in_ex() {
        assert!(!GenericRange::from(1..1).is_singleton());
        assert!(GenericRange::from(1..2).is_singleton());
        assert!(!GenericRange::from(1..42).is_singleton());
    }

    #[test]
    fn in_in() {
        assert!(GenericRange::from(1..=1).is_singleton());
        assert!(!GenericRange::from(1..=2).is_singleton());
        assert!(!GenericRange::from(1..=42).is_singleton());
    }

    #[test]
    fn unbound() {
        assert!(!GenericRange::from(1..).is_singleton());
        assert!(!GenericRange::from(..2).is_singleton());
        assert!(!GenericRange::from(..=2).is_singleton());
        let generic: GenericRange<usize> = GenericRange::from(..);
        assert!(!generic.is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Unbounded)).is_singleton());
    }

    #[test]
    fn ex_ex() {
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(1))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(2))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(42))).is_singleton());
    }

    #[test]
    fn ex_in() {
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(1))).is_singleton());
        assert!(GenericRange::from((Bound::Excluded(1), Bound::Included(2))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(42))).is_singleton());
    }
}

#[cfg(all(test, feature = "noisy_float"))]
mod tests_continuous {
    use core::ops::Bound;

    use noisy_float::types::N64;

    use crate::GenericRange;

    #[test]
    fn in_ex() {
        let n64_1 = N64::new(1.);
        let n64_2 = N64::new(2.);
        let n64_42 = N64::new(42.);
        assert!(!GenericRange::from(n64_1..n64_1).is_singleton());
        assert!(!GenericRange::from(n64_1..n64_2).is_singleton());
        assert!(!GenericRange::from(n64_1..n64_42).is_singleton());
    }

    #[test]
    fn in_in() {
        let n64_1 = N64::new(1.);
        let n64_2 = N64::new(2.);
        let n64_42 = N64::new(42.);
        assert!(GenericRange::from(n64_1..=n64_1).is_singleton());
        assert!(!GenericRange::from(n64_1..=n64_2).is_singleton());
        assert!(!GenericRange::from(n64_1..=n64_42).is_singleton());
    }

    #[test]
    fn unbound() {
        let n64_1 = N64::new(1.);
        let n64_2 = N64::new(2.);
        assert!(!GenericRange::from(n64_1..).is_singleton());
        assert!(!GenericRange::from(..n64_2).is_singleton());
        assert!(!GenericRange::from(..=n64_2).is_singleton());
        let generic: GenericRange<N64> = GenericRange::from(..);
        assert!(!generic.is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Unbounded)).is_singleton());
    }

    #[test]
    fn ex_ex() {
        let n64_1 = N64::new(1.);
        let n64_2 = N64::new(2.);
        let n64_42 = N64::new(42.);
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_1))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_2))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_42))).is_singleton());
    }

    #[test]
    fn ex_in() {
        let n64_1 = N64::new(1.);
        let n64_2 = N64::new(2.);
        let n64_42 = N64::new(42.);
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_1))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_2))).is_singleton());
        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_42))).is_singleton());
    }
}