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;

/// Base implementation of `Domain` for a primitive type.
mod i128;
/// Base implementation of `Domain` for a primitive type.
mod i16;
/// Base implementation of `Domain` for a primitive type.
mod i32;
/// Base implementation of `Domain` for a primitive type.
mod i64;
/// Base implementation of `Domain` for a primitive type.
mod i8;
/// Base implementation of `Domain` for a primitive type.
mod isize;

/// Base implementation of `Domain` for a primitive type.
mod u128;
/// Base implementation of `Domain` for a primitive type.
mod u16;
/// Base implementation of `Domain` for a primitive type.
mod u32;
/// Base implementation of `Domain` for a primitive type.
mod u64;
/// Base implementation of `Domain` for a primitive type.
mod u8;
/// Base implementation of `Domain` for a primitive type.
mod usize;

/// Base implementation of `Domain` for a primitive type.
mod bool;
/// Base implementation of `Domain` for a primitive type.
mod char;
/// Base implementation of `Domain` for the `Ordering` enum.
mod ordering;

#[cfg(feature = "noisy_float")]
/// Base implementation of `Domain` for a `NoisyFloat`.
mod noisy_float;

#[cfg(feature = "num-bigint")]
/// Base implementation of `Domain` for a `BigInt`.
mod bigint;
#[cfg(feature = "num-bigint")]
/// Base implementation of `Domain` for a `BigUint`.
mod biguint;

/// Provides otherwise unknown information about the type it is being implemented for.
///
/// There are five properties which can be combined into six categories a domain can be in,
/// each with their own implementation requirements and assumptions.
///
/// Should the type you're trying to implement not fit into any of these, please open an issue!
///
/// # Properties
/// Requirements to be recognized as
/// - `discrete`:
///   - `DISCRETE` is set to true
///   - `predecessor()` and `successor()` are implemented and do not panic
/// - `continuous`:
///   - `DISCRETE` is set to false (default)
///   - `predecessor()` and `successor()` are never to be called (they panic by default)
/// - `limited`:
///   - `minimum()` and `maximum()` do not return `Bound::Unbounded`
/// - `bidirectional`
///   - `minimum()` and `maximum()` return `Bound::Unbounded` (default)
/// - `unidirectional`
///   - either `minimum()` or `maximum()` does not return `Bound::Unbounded`
///
/// # Categories
/// ## discrete and limited
/// This is your typical primitive integer like `u8` or `i32`.
///
/// ## discrete, unlimited and bidirectional
/// An example for this kind of domain is `BigInt`. The `num-bigint` crate is used to optionally
/// provide an implementation.
///
/// ## discrete, unlimited and unidirectional
/// Like `BigInt`, but limited in one direction: `BigUint`.
///
/// ## continuous and limited
/// Because `f32` or `f64` do not implement `Ord`, this trait can not be implemented for them directly.
/// Instead, using `noisy_float`, all four types (`N32`, `N64`, `R32` and `R64`) have an optional
/// implementation. In terms of concrete values, an example domain could be `[0.0, 1.0]`.
///
/// ## continuous, unlimited and bidirectional
/// This category requires an arbitrarily large float. So far no base/default implementation exists.
/// If you would like to suggest a crate that provides this type (it must implement `Ord`!), please
/// leave an issue.
///
/// ## continuous, unlimited and unidirectional
/// This category requires an arbitrarily large float. So far no base/default implementation exists.
/// If you would like to suggest a crate that provides this type (it must implement `Ord`!), please
/// leave an issue.
pub trait Domain: Ord + Sized {
    /// Defines if the domain is discrete or continuous.
    const DISCRETE: bool = false;

    /// Returns the predecessor of `self`.
    ///
    /// # Note
    /// This method is only required if implemented on a discrete type and therefore has a default
    /// implementation which panics.
    ///
    /// # Panics
    /// This function should panic if implemented on continuous types!
    #[must_use]
    #[allow(clippy::panic)]
    fn predecessor(&self) -> Option<Self> {
        panic!("method called on continuous type or method not yet implemented");
    }

    /// Returns the successor of `self`.
    ///
    /// # Note
    /// This method is only required if implemented on a discrete type and therefore has a default
    /// implementation which panics.
    ///
    /// # Panics
    /// This function should panic if implemented on continuous types!
    #[must_use]
    #[allow(clippy::panic)]
    fn successor(&self) -> Option<Self> {
        panic!("method called on continuous type or method not yet implemented");
    }

    /// Returns the smallest possible start bound.
    /// The reason this returns a `Bound` instead of `Option<Self>` is that domains with this range
    /// exist: `(0.0, 1.0]`, making it necessary to allow for excluded values.
    ///
    /// # Note
    /// By default it is assumed no minimum value exists and therefore `Unbounded` is returned.
    ///
    /// # Assertion
    /// It is assumed, that the full `Domain` range is _not_ empty, and therefore `Self` should be
    /// smaller or equal to the value contained in `Self::maximum()`.
    #[must_use]
    fn minimum() -> Bound<Self> {
        Bound::Unbounded
    }

    /// Returns the greatest possible end bound.
    /// The reason this returns a `Bound` instead of `Option<Self>` is that domains with this range
    /// exist: `[0.0, 1.0)`, making it necessary to allow for excluded values.
    ///
    /// # Note
    /// By default it is assumed no maximum value exists and therefore `Unbounded` is returned.
    ///
    /// # Assertion
    /// It is assumed, that the full `Domain` range is _not_ empty, and therefore `Self` should be
    /// greater or equal to the value contained in `Self::minimum()`.
    #[must_use]
    fn maximum() -> Bound<Self> {
        Bound::Unbounded
    }

    /// Calculates or statically defines if two values in a domain are next to each other.
    ///
    /// # Panics
    /// This function should panic if implemented on continuous types!
    ///
    /// # Note
    /// The default implementation uses `predecessor()` and `successor()`,
    /// which should always panic if the domain is continuous.
    #[must_use]
    fn is_next_to(&self, other: &Self) -> bool {
        if self == other {
            return false;
        }

        Some(other) == self.predecessor().as_ref() || Some(other) == self.successor().as_ref()
    }

    /// Returns true if there exists a `b` so that with inputs `a` and `c`:
    /// - `b.is_next_to(a) == true`
    /// - `b.is_next_to(c) == true`
    ///
    /// # Panics
    /// This function should panic if implemented on continuous types!
    #[must_use]
    fn shares_neighbour_with(&self, other: &Self) -> bool {
        self.predecessor().map_or(false, |p| p.is_next_to(other))
            || self.successor().map_or(false, |s| s.is_next_to(other))
    }
}

/// `Domain`-driven trait required to implement iterators on discrete types.
pub trait Iterable: Domain {
    /// Type to be yielded for the iterator generated by `IntoIter`.
    type Output;
    /// State transition function from one element in the domain to the next.
    fn next(&self) -> Option<Self::Output>;
}