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::cmp::Ordering;
use core::ops::{Bound, RangeBounds};

use crate::{Domain, GenericRange};

/// This enum represents all possible arrangements of two `GenericRange`s.
/// The original values were moved, possibly modified, and then stored.
///
/// # Note
/// The following information is additionally contained if applicable:
///   - which of the two ranges was `self` and `other`
///   - if the overlap is a singleton
///   - if `start` or `other` is a singleton
///   - which of `self` and `other` is shorter
///
/// This information is the result of the comparison of the starts and ends of both ranges and is
/// obtained "for free" and should thus be used preferably to other methods like
/// `GenericRange::is_singleton()`.
// Allow clippy lint because `Debug` produces it.
#[allow(clippy::missing_inline_in_public_items, clippy::exhaustive_enums)]
#[derive(Debug, PartialEq, Eq)]
pub enum Relation<T: Domain> {
    /// The two ranges have no overlap.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second:               |----------|
    /// ```
    Disjoint {
        /// The end of this range is less than the start of `second`.
        first: GenericRange<T>,
        /// The start of this range is greater than the end of `first`.
        second: GenericRange<T>,
        /// When `GenericRange::relation(self, other)` was invoked, either `self` or `other`
        /// were less.
        self_less: bool,
    },

    /// The two ranges have no overlap but the end of the first touches the start of the second.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second:            |----------|
    /// ```
    Touching {
        /// The end of this range touches the start of `second`.
        first: GenericRange<T>,
        /// The start of this range touches the end of `first`.
        second: GenericRange<T>,
        /// When `GenericRange::relation(self, other)` was invoked, either `self` or `other`
        /// were less when sorted using `Ord`.
        self_less: bool,
    },

    /// The two ranges have an overlap of one or more elements.
    /// # Diagram
    /// ```text
    /// first          : |----------|
    /// second         :         |----------|
    /// first disjoint : |-------|
    /// second disjoint:            |-------|
    /// overlap        :         |--|
    /// ```
    Overlapping {
        /// Range representing the disjoint part of two ranges before the overlap.
        first_disjoint: GenericRange<T>,
        /// Range representing the disjoint part of two ranges after the overlap.
        second_disjoint: GenericRange<T>,
        /// Range representing the overlapping part of two ranges.
        overlap: GenericRange<T>,
        /// When `GenericRange::relation(self, other)` was invoked, either `self` or `other`
        /// was less.
        self_less: bool,
        /// Indicates whether or not the overlap is by a single element, allowing optimization.
        overlap_is_singleton: bool,
    },

    /// One range is contained in the other.
    /// # Diagram
    /// ```text
    /// first          : |--------------------|
    /// second         :      |----------|
    /// first disjoint : |----|
    /// second disjoint:                 |----|
    /// overlap        :      |----------|
    /// ```
    Containing {
        /// Range representing the disjoint part of two ranges before the overlap.
        first_disjoint: GenericRange<T>,
        /// Range representing the disjoint part of two ranges after the overlap.
        second_disjoint: GenericRange<T>,
        /// Range representing the overlapping part of two ranges.
        overlap: GenericRange<T>,
        /// When `GenericRange::relation(self, other)` was invoked, either `self` or `other`
        /// was shorter.
        self_shorter: bool,
    },

    /// Both ranges have the same start but varying endings.
    /// # Diagram
    /// ```text
    /// first   : |-----|
    /// second  : |----------|
    /// overlap : |-----|
    /// disjoint:       |----|
    /// ```
    Starting {
        /// Range representing the overlapping part of two ranges.
        overlap: GenericRange<T>,
        /// Range representing the disjoint part of two ranges.
        disjoint: GenericRange<T>,
        /// Indicates if `self` or `other` in `GenericRange::relation(self, other)`
        /// is shorter.
        self_shorter: bool,
        /// Depending on which of `self` or `other` is shorter, this boolean indicates that it is a
        /// singleton.
        shorter_is_singleton: bool,
    },

    /// Both ranges have the same end but varying starts.
    /// # Diagram
    /// ```text
    /// first   : |----------|
    /// second  :       |----|
    /// disjoint: |-----|
    /// overlap :       |----|
    /// ```
    Ending {
        /// Range representing the disjoint part of two ranges.
        disjoint: GenericRange<T>,
        /// Range representing the overlapping part of two ranges.
        overlap: GenericRange<T>,
        /// Indicates if `self` or `other` in `GenericRange::relation(self, other)`
        /// is shorter.
        self_shorter: bool,
        /// Depending on which of `self` or `other` is shorter, this boolean indicates that it is a
        /// singleton.
        shorter_is_singleton: bool,
    },

    /// The starts and ends of both ranges are equal.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second: |----------|
    /// equal : |----------|
    /// ```
    Equal(GenericRange<T>),

    /// One or both ranges are empty and can therefore not be compared.
    /// # Diagram
    /// ```text
    /// first    :
    /// second   :    |----|
    /// non_empty:    |----|
    /// ```
    Empty {
        /// The non empty range of the two ranges, if it exists.
        non_empty: Option<GenericRange<T>>,
        /// Indicates which of the two inputs of `GenericRange::arrangement(&self, &other)` were
        /// empty. Returns `None` if both were empty.
        self_empty: Option<bool>,
    },
}

/// This enum represents all possible arrangements of two `GenericRange`s.
///
/// # Note
/// The following information is additionally contained if applicable:
///   - which of the two ranges was `self` and `other`
///   - if the overlap is a singleton
///   - if `start` or `other` is a singleton
///   - which of `self` and `other` is shorter
///
/// This information is the result of the comparison of the starts and ends of both ranges and is
/// obtained "for free" and should thus be used preferably to other methods like
/// `GenericRange::is_singleton()`.
// Allow clippy lint because `Debug` produces it.
#[allow(clippy::missing_inline_in_public_items, clippy::exhaustive_enums)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Arrangement {
    /// The two ranges have no overlap.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second:               |----------|
    /// ```
    Disjoint {
        /// When `GenericRange::arrangement(&self, &other)` was invoked, either `self` or `other`
        /// was less.
        self_less: bool,
    },

    /// The two ranges have no overlap but the end of the first touches the start of the second.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second:            |----------|
    /// ```
    Touching {
        /// When `GenericRange::arrangement(&self, &other)` was invoked, either `self` or `other`
        /// was less.
        self_less: bool,
    },

    /// The two ranges have an overlap of one or more elements.
    /// # Diagram
    /// ```text
    /// first          : |----------|
    /// second         :         |----------|
    /// first disjoint : |-------|
    /// second disjoint:            |-------|
    /// overlap        :         |--|
    /// ```
    Overlapping {
        /// When `GenericRange::arrangement(&self, &other)` was invoked, either `self` or `other`
        /// was less.
        self_less: bool,
        /// Indicates whether or not the overlap is by a single element, allowing optimization.
        overlap_is_singleton: bool,
    },

    /// One range is contained in the other.
    /// # Diagram
    /// ```text
    /// first          : |--------------------|
    /// second         :      |----------|
    /// first disjoint : |----|
    /// second disjoint:                 |----|
    /// overlap        :      |----------|
    /// ```
    Containing {
        /// When `GenericRange::arrangement(&self, &other)` was invoked, either `self` or `other`
        /// was shorter.
        self_shorter: bool,
    },

    /// Both ranges have the same start but varying endings.
    /// # Diagram
    /// ```text
    /// first   : |-----|
    /// second  : |----------|
    /// overlap : |-----|
    /// disjoint:       |----|
    /// ```
    Starting {
        /// Indicates whether `self` or `other` in `GenericRange::arrangement(&self, &other)`
        /// was shorter.
        self_shorter: bool,
        /// Depending on which of `self` or `other` is shorter, this boolean indicates that it is a
        /// singleton.
        shorter_is_singleton: bool,
    },

    /// Both ranges have the same end but varying starts.
    /// # Diagram
    /// ```text
    /// first   : |----------|
    /// second  :       |----|
    /// disjoint: |-----|
    /// overlap :       |----|
    /// ```
    Ending {
        /// Indicates whether `self` or `other` in `GenericRange::arrangement(&self, &other)`
        /// was shorter.
        self_shorter: bool,
        /// Depending on which of `self` or `other` is shorter, this boolean indicates that it is a
        /// singleton.
        shorter_is_singleton: bool,
    },

    /// The starts and ends of both ranges are equal.
    /// # Diagram
    /// ```text
    /// first : |----------|
    /// second: |----------|
    /// equal : |----------|
    /// ```
    Equal,

    /// One or both ranges are empty and can therefore not be compared.
    /// # Diagram
    /// ```text
    /// first :
    /// second:    |----|
    /// ```
    Empty {
        /// Indicates which of the two inputs of `GenericRange::arrangement(&self, &other)` were
        /// empty. Returns `None` if both were empty.
        self_empty: Option<bool>,
    },
}

/// This is the "core" of this library. Everything basically relies on this one way or another.
///
/// # Note
/// If you need to know only one of the possible outcomes it is generally better to use `is_*()`
/// methods instead of matching on `arrangement()` or worse `relation()`. The more specific methods
/// are, as one would expect, only using the bare minimum of information to figure out if the
/// requested arrangement is the case. If, however, a broad knowledge about or even computation with
/// two ranges is required, `arrangement()` and `relation()` are the way to go.
impl<T> GenericRange<T> {
    /// Compares a start bound with another.
    ///
    /// # Note
    /// Because the generic `Bound` enum is used, it is not verified if the two bounds are actually
    /// starts. This responsibility rests on the caller.
    #[must_use]
    pub fn cmp_start_start(start: Bound<&T>, other_start: Bound<&T>) -> Ordering
    where
        T: Domain,
    {
        match (start, other_start) {
            (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
            (Bound::Unbounded, _) => Ordering::Less,
            (_, Bound::Unbounded) => Ordering::Greater,

            (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(b),

            (Bound::Included(a), Bound::Excluded(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Greater => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Greater
                            }
                        }
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Greater => Ordering::Greater,
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                }
            }
            (Bound::Excluded(a), Bound::Included(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Less => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Less
                            }
                        }
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Less => Ordering::Less,
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                }
            }
        }
    }

    /// Compares a start bound with an end bound.
    ///
    /// # Note
    /// Because the generic `Bound` enum is used, it is not verified if the two bounds are actually
    /// start and end. This responsibility rests on the caller.
    #[must_use]
    pub fn cmp_start_end(start: Bound<&T>, end: Bound<&T>) -> Ordering
    where
        T: Domain,
    {
        match (start, end) {
            (Bound::Unbounded, _) | (_, Bound::Unbounded) => Ordering::Less,
            (Bound::Included(a), Bound::Included(b)) => a.cmp(b),
            (Bound::Excluded(a), Bound::Excluded(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Less => {
                            if a.is_next_to(b) {
                                Ordering::Greater
                            } else if a.shares_neighbour_with(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Less
                            }
                        }
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Less => Ordering::Less,
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                }
            }
            (Bound::Included(a), Bound::Excluded(b)) | (Bound::Excluded(a), Bound::Included(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Less => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Less
                            }
                        }
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Greater | Ordering::Equal => Ordering::Greater,
                        Ordering::Less => Ordering::Less,
                    }
                }
            }
        }
    }

    /// Compares an end bound with a start bound.
    ///
    /// # Note
    /// Because the generic `Bound` enum is used, it is not verified if the two bounds are actually
    /// end and start. This responsibility rests on the caller.
    #[must_use]
    pub fn cmp_end_start(end: Bound<&T>, start: Bound<&T>) -> Ordering
    where
        T: Domain,
    {
        match (end, start) {
            (Bound::Unbounded, _) | (_, Bound::Unbounded) => Ordering::Greater,
            (Bound::Included(a), Bound::Included(b)) => a.cmp(b),
            (Bound::Excluded(a), Bound::Excluded(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Greater => {
                            if a.is_next_to(b) {
                                Ordering::Less
                            } else if a.shares_neighbour_with(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Greater
                            }
                        }
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Greater => Ordering::Greater,
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                }
            }

            (Bound::Included(a), Bound::Excluded(b)) | (Bound::Excluded(a), Bound::Included(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Greater => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Greater
                            }
                        }
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Less | Ordering::Equal => Ordering::Less,
                        Ordering::Greater => Ordering::Greater,
                    }
                }
            }
        }
    }

    /// Compares an end bound with another.
    ///
    /// # Note
    /// Because the generic `Bound` enum is used, it is not verified if the two bounds are actually
    /// ends. This responsibility rests on the caller.
    #[must_use]
    pub fn cmp_end_end(end: Bound<&T>, other_end: Bound<&T>) -> Ordering
    where
        T: Domain,
    {
        match (end, other_end) {
            (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
            (Bound::Unbounded, _) => Ordering::Greater,
            (_, Bound::Unbounded) => Ordering::Less,

            (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(b),

            (Bound::Included(a), Bound::Excluded(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Less => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Less
                            }
                        }
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Less => Ordering::Less,
                        Ordering::Equal | Ordering::Greater => Ordering::Greater,
                    }
                }
            }
            (Bound::Excluded(a), Bound::Included(b)) => {
                if <T as Domain>::DISCRETE {
                    match a.cmp(b) {
                        Ordering::Greater => {
                            if a.is_next_to(b) {
                                Ordering::Equal
                            } else {
                                Ordering::Greater
                            }
                        }
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                } else {
                    match a.cmp(b) {
                        Ordering::Greater => Ordering::Greater,
                        Ordering::Equal | Ordering::Less => Ordering::Less,
                    }
                }
            }
        }
    }

    /// Returns true if two bounds are touching.
    #[must_use]
    pub fn is_bound_touching(bound: Bound<&T>, other: Bound<&T>) -> bool
    where
        T: PartialEq,
    {
        match (bound, other) {
            (Bound::Excluded(x), Bound::Included(y)) | (Bound::Included(x), Bound::Excluded(y)) => x == y,
            _ => false,
        }
    }

    /// Returns true if two ranges are disjoint.
    ///
    /// # Note
    /// No information regarding their arrangement is obtained or returned.
    /// Please refer to `GenericRange::arrangement()` or `GenericRange::relation()` and match the
    /// result on their respective `Disjoint` discriminant.
    #[must_use]
    pub fn is_disjoint(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        if self.is_empty() || other.is_empty() {
            return true;
        }

        let end_start = Self::cmp_end_start(self.end_bound(), other.start_bound());
        let end_start_touching = Self::is_bound_touching(self.end_bound(), other.start_bound());
        let start_end = Self::cmp_start_end(self.start_bound(), other.end_bound());
        let start_end_touching = Self::is_bound_touching(self.start_bound(), other.end_bound());

        (end_start == Ordering::Less && !end_start_touching) || (start_end == Ordering::Greater && !start_end_touching)
    }

    /// Returns true if two ranges are touching.
    ///
    /// # Note
    /// No information regarding their arrangement is obtained or returned.
    /// Please refer to `GenericRange::arrangement()` or `GenericRange::relation()` and match the
    /// result on their respective `Touching` discriminant.
    #[must_use]
    pub fn is_touching(&self, other: &Self) -> bool
    where
        T: PartialEq + Domain,
    {
        if self.is_empty() || other.is_empty() {
            return false;
        }

        Self::is_bound_touching(self.end_bound(), other.start_bound())
            || Self::is_bound_touching(self.start_bound(), other.end_bound())
    }

    /// Returns true if two ranges are overlapping.
    ///
    /// # Note
    /// No information regarding their arrangement is obtained or returned.
    /// Please refer to `GenericRange::arrangement()` or `GenericRange::relation()` and match the
    /// result on their respective `Overlapping` discriminant.
    #[must_use]
    pub fn is_overlapping(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        let start_start = Self::cmp_start_start(self.start_bound(), other.start_bound());

        match start_start {
            // it is either `Relation::Equal` or `Relation::Starting`
            Ordering::Equal => false,
            Ordering::Less => {
                // first we need to figure out if the end of `self` is in `other`
                let end_start = Self::cmp_end_start(self.end_bound(), other.start_bound());

                // if it is not, it can not be overlapping
                if end_start == Ordering::Less
                    || (end_start == Ordering::Equal && Self::is_bound_touching(self.end_bound(), other.start_bound()))
                {
                    return false;
                }

                // finally, the end of `other` has to be outside of `self`
                let end_end = Self::cmp_end_end(other.end_bound(), self.end_bound());

                // which means it has to be greater than the end of `self`
                end_end == Ordering::Greater
            }
            Ordering::Greater => {
                // first we need to figure out if the end of `other` is in `self`
                let end_start = Self::cmp_end_start(other.end_bound(), self.start_bound());

                // if it is not, it can not be overlapping
                if end_start == Ordering::Less
                    || (end_start == Ordering::Equal && Self::is_bound_touching(other.end_bound(), self.start_bound()))
                {
                    return false;
                }

                // finally, the end of `self` has to be outside of `other`
                let end_end = Self::cmp_end_end(self.end_bound(), other.end_bound());

                // which means it has to be greater than the end of `other`
                end_end == Ordering::Greater
            }
        }
    }

    /// Returns true if `self` contains `other`.
    ///
    /// # Note
    /// This method and `is_contained_by()` are the only two that differentiate by arrangement.
    /// If knowledge about containment (in either orientation) is the only information you need,
    /// it is still generally faster to call these two instead of using
    /// `GenericRange::arrangement()` or `GenericRange::relation()`.
    #[must_use]
    pub fn is_containing(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        if other.is_empty() {
            return true;
        }

        Self::cmp_start_start(other.start_bound(), self.start_bound()) == Ordering::Greater
            && Self::cmp_end_end(other.end_bound(), self.end_bound()) == Ordering::Less
    }

    /// Returns true if `self` is contained in `other`.
    ///
    /// # Note
    /// This method and `is_containing()` are the only two that differentiate by arrangement.
    /// If knowledge about containment (in either orientation) is the only information you need,
    /// it is still generally faster to call these two instead of using
    /// `GenericRange::arrangement()` or `GenericRange::relation()`.
    #[must_use]
    pub fn is_contained_by(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        if self.is_empty() {
            return true;
        }

        Self::cmp_start_start(self.start_bound(), other.start_bound()) == Ordering::Greater
            && Self::cmp_end_end(self.end_bound(), other.end_bound()) == Ordering::Less
    }

    /// Returns true if two ranges are starting on the same element.
    ///
    /// # Note
    /// No information regarding their arrangement is obtained or returned.
    /// Please refer to `GenericRange::arrangement()` or `GenericRange::relation()` and match the
    /// result on their respective `Starting` discriminant.
    #[must_use]
    pub fn is_starting_with(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        if self.is_empty() || other.is_empty() {
            return false;
        }

        Self::cmp_start_start(self.start_bound(), other.start_bound()) == Ordering::Equal
            && Self::cmp_end_end(self.end_bound(), other.end_bound()) != Ordering::Equal
    }

    /// Returns true if two ranges are ending on the same element.
    ///
    /// # Note
    /// No information regarding their arrangement is obtained or returned.
    /// Please refer to `GenericRange::arrangement()` or `GenericRange::relation()` and match the
    /// result on their respective `Ending` discriminant.
    #[must_use]
    pub fn is_ending_with(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        if self.is_empty() || other.is_empty() {
            return false;
        }

        Self::cmp_start_start(self.start_bound(), other.start_bound()) != Ordering::Equal
            && Self::cmp_end_end(self.end_bound(), other.end_bound()) == Ordering::Equal
    }

    /// Returns true if the two ranges are equal.
    ///
    /// This method is the basis for the `PartialEq` and `Eq` trait implementations.
    ///
    /// # Note
    /// For discrete types, this may have counter-intuitive results. See examples.
    ///
    /// # Examples
    /// ```
    /// use ranges::GenericRange;
    ///
    /// assert_eq!(GenericRange::from(1..3), (1..=2).into());
    /// assert_eq!(GenericRange::new_open_closed(1, 3), (2..=3).into());
    /// assert_eq!(GenericRange::new_open(0, 5), (1..=4).into());
    /// ```
    #[must_use]
    pub fn is_equal(&self, other: &Self) -> bool
    where
        T: Domain,
    {
        Self::cmp_start_start(other.start_bound(), self.start_bound()) == Ordering::Equal
            && Self::cmp_end_end(other.end_bound(), self.end_bound()) == Ordering::Equal
    }

    /// Compares this `GenericRange<T>` with another one.
    /// The relation is expressed as an enum containing only the arrangement in form of the
    /// discriminant and some additionally obtained information about the original inputs and,
    /// if applicable, either one is less, shorter, or a singleton.
    ///
    /// In addition to the same data obtained by `GenericRange::arrangement()`, the original ranges
    /// are moved into the struct to allow direct processing.
    #[must_use]
    pub fn relation(self, other: Self) -> Relation<T>
    where
        T: Clone + Domain,
    {
        match self.arrangement(&other) {
            Arrangement::Disjoint { self_less } => {
                let (first, second) = if self_less { (self, other) } else { (other, self) };

                Relation::Disjoint {
                    first,
                    second,
                    self_less,
                }
            }
            Arrangement::Touching { self_less } => {
                let (first, second) = if self_less { (self, other) } else { (other, self) };

                Relation::Touching {
                    first,
                    second,
                    self_less,
                }
            }
            Arrangement::Overlapping {
                self_less,
                overlap_is_singleton,
            } => {
                let (first, second) = if self_less { (self, other) } else { (other, self) };

                let first_disjoint_end = Self::invert_border_cloned(&second.start);
                let second_disjoint_start = Self::invert_border_cloned(&first.end);

                let first_disjoint = (first.start, first_disjoint_end).into();
                let second_disjoint = (second_disjoint_start, second.end).into();

                Relation::Overlapping {
                    first_disjoint,
                    second_disjoint,
                    overlap: (second.start, first.end).into(),
                    self_less,
                    overlap_is_singleton,
                }
            }
            Arrangement::Containing { self_shorter } => {
                let (bigger, smaller) = if self_shorter { (other, self) } else { (self, other) };

                let first_end = Self::invert_border_cloned(&smaller.start);
                let second_start = Self::invert_border_cloned(&smaller.end);

                Relation::Containing {
                    first_disjoint: (bigger.start, first_end).into(),
                    second_disjoint: (second_start, bigger.end).into(),
                    overlap: smaller,
                    self_shorter,
                }
            }
            Arrangement::Starting {
                self_shorter,
                shorter_is_singleton,
            } => {
                let (longer, shorter) = if self_shorter { (other, self) } else { (self, other) };

                let disjoint_start = Self::invert_border_cloned(&shorter.end);

                Relation::Starting {
                    overlap: shorter,
                    disjoint: (disjoint_start, longer.end).into(),
                    self_shorter,
                    shorter_is_singleton,
                }
            }
            Arrangement::Ending {
                self_shorter,
                shorter_is_singleton,
            } => {
                let (longer, shorter) = if self_shorter { (other, self) } else { (self, other) };

                let disjoint_end = Self::invert_border_cloned(&shorter.start);

                Relation::Ending {
                    overlap: shorter,
                    disjoint: (longer.start, disjoint_end).into(),
                    self_shorter,
                    shorter_is_singleton,
                }
            }
            Arrangement::Equal => Relation::Equal(self),
            Arrangement::Empty { self_empty } => Relation::Empty {
                non_empty: self_empty.map(|empty| if empty { other } else { self }),
                self_empty,
            },
        }
    }

    /// Compares this `GenericRange<T>` with another one.
    /// The relation is expressed as an enum containing only the arrangement in form of the
    /// discriminant and some additionally obtained information about the original inputs and,
    /// if applicable, either one is less, shorter, or a singleton.
    #[allow(clippy::too_many_lines)]
    #[must_use]
    pub fn arrangement(&self, other: &Self) -> Arrangement
    where
        T: Domain,
    {
        match (self.is_empty(), other.is_empty()) {
            (true, true) => return Arrangement::Empty { self_empty: None },
            (true, false) => return Arrangement::Empty { self_empty: Some(true) },
            (false, true) => {
                return Arrangement::Empty {
                    self_empty: Some(false),
                }
            }
            (false, false) => (),
        }

        let (self_start, self_end) = (self.start_bound(), self.end_bound());
        let (other_start, other_end) = (other.start_bound(), other.end_bound());

        let start_end = Self::cmp_start_end(self_start, other_end);
        let end_start = Self::cmp_end_start(self_end, other_start);
        let start_start = Self::cmp_start_start(self_start, other_start);
        let end_end = Self::cmp_end_end(self_end, other_end);

        match (start_end, end_start, start_start, end_end) {
            /* DISJOINT/TOUCHING */
            (Ordering::Less, Ordering::Less, Ordering::Less, Ordering::Less) => {
                if Self::is_bound_touching(self_end, other_start) {
                    Arrangement::Touching { self_less: true }
                } else {
                    Arrangement::Disjoint { self_less: true }
                }
            }

            (Ordering::Greater, Ordering::Greater, Ordering::Greater, Ordering::Greater) => {
                if Self::is_bound_touching(other_end, self_start) {
                    Arrangement::Touching { self_less: false }
                } else {
                    Arrangement::Disjoint { self_less: false }
                }
            }

            /* OVERLAPPING */
            (Ordering::Less, Ordering::Greater, Ordering::Less, Ordering::Less) => Arrangement::Overlapping {
                self_less: true,
                overlap_is_singleton: false,
            },

            (Ordering::Less, Ordering::Greater, Ordering::Greater, Ordering::Greater) => Arrangement::Overlapping {
                self_less: false,
                overlap_is_singleton: false,
            },

            (Ordering::Less, Ordering::Equal, Ordering::Less, Ordering::Less) => Arrangement::Overlapping {
                self_less: true,
                overlap_is_singleton: true,
            },

            (Ordering::Equal, Ordering::Greater, Ordering::Greater, Ordering::Greater) => Arrangement::Overlapping {
                self_less: false,
                overlap_is_singleton: true,
            },

            /* CONTAINING */
            (Ordering::Less, Ordering::Greater, Ordering::Less, Ordering::Greater) => {
                Arrangement::Containing { self_shorter: false }
            }

            (Ordering::Less, Ordering::Greater, Ordering::Greater, Ordering::Less) => {
                Arrangement::Containing { self_shorter: true }
            }

            /* STARTING */
            (Ordering::Less, Ordering::Greater, Ordering::Equal, Ordering::Less) => Arrangement::Starting {
                self_shorter: true,
                shorter_is_singleton: false,
            },

            (Ordering::Less, Ordering::Greater, Ordering::Equal, Ordering::Greater) => Arrangement::Starting {
                self_shorter: false,
                shorter_is_singleton: false,
            },

            (Ordering::Less, Ordering::Equal, Ordering::Equal, Ordering::Less) => Arrangement::Starting {
                self_shorter: true,
                shorter_is_singleton: true,
            },

            (Ordering::Equal, Ordering::Greater, Ordering::Equal, Ordering::Greater) => Arrangement::Starting {
                self_shorter: false,
                shorter_is_singleton: true,
            },

            /* ENDING */
            (Ordering::Less, Ordering::Greater, Ordering::Less, Ordering::Equal) => Arrangement::Ending {
                self_shorter: false,
                shorter_is_singleton: false,
            },

            (Ordering::Less, Ordering::Greater, Ordering::Greater, Ordering::Equal) => Arrangement::Ending {
                self_shorter: true,
                shorter_is_singleton: false,
            },

            (Ordering::Less, Ordering::Equal, Ordering::Less, Ordering::Equal) => Arrangement::Ending {
                self_shorter: false,
                shorter_is_singleton: true,
            },

            (Ordering::Equal, Ordering::Greater, Ordering::Greater, Ordering::Equal) => Arrangement::Ending {
                self_shorter: true,
                shorter_is_singleton: true,
            },
            /* EQUAL */
            (Ordering::Less, Ordering::Greater, Ordering::Equal, Ordering::Equal)
            | (Ordering::Equal, Ordering::Equal, Ordering::Equal, Ordering::Equal) => Arrangement::Equal,

            _ => unreachable!(
                "start_end: {:?}, end_start: {:?}, start_start: {:?}, end_end: {:?}",
                start_end, end_start, start_start, end_end
            ),
        }
    }
}

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

    use proptest::prelude::*;

    use crate::{Arrangement, GenericRange, Relation};

    #[test]
    fn ordering_start_start_unbounded() {
        assert_eq!(
            GenericRange::cmp_start_start(Bound::<&usize>::Unbounded, Bound::Unbounded),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Unbounded, Bound::Included(&5)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&5), Bound::Unbounded),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_start_same() {
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&42), Bound::Included(&42)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&7), Bound::Included(&42)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&42), Bound::Included(&7)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_start_in_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&0), Bound::Excluded(&2)),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&2), Bound::Excluded(&2)),
            Ordering::Less
        );
        // greater, but touching
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&3), Bound::Excluded(&2)),
            Ordering::Equal
        );
        // greater, but not touching
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&4), Bound::Excluded(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_start_ex_in() {
        // less, but not touching
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&0), Bound::Included(&2)),
            Ordering::Less
        );
        // less, but touching
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&1), Bound::Included(&2)),
            Ordering::Equal
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&2), Bound::Included(&2)),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&3), Bound::Included(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_end_unbounded() {
        assert_eq!(
            GenericRange::cmp_end_end(Bound::<&usize>::Unbounded, Bound::Unbounded),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Unbounded, Bound::Included(&5)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&5), Bound::Unbounded),
            Ordering::Less
        );
    }

    #[test]
    fn ordering_end_end_same() {
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&42), Bound::Included(&42)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&7), Bound::Included(&42)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&42), Bound::Included(&7)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_end_in_ex() {
        // less, but not touching
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&0), Bound::Excluded(&2)),
            Ordering::Less
        );
        // less, but touching
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&1), Bound::Excluded(&2)),
            Ordering::Equal
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&2), Bound::Excluded(&2)),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&3), Bound::Excluded(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_end_ex_in() {
        // less
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&0), Bound::Included(&2)),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&2), Bound::Included(&2)),
            Ordering::Less
        );
        // greater, but touching
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&3), Bound::Included(&2)),
            Ordering::Equal
        );
        // greater, but not touching
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&4), Bound::Included(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_unbounded() {
        assert_eq!(
            GenericRange::cmp_start_end(Bound::<&usize>::Unbounded, Bound::Unbounded),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Unbounded, Bound::Included(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&3), Bound::Unbounded),
            Ordering::Less
        );
    }

    #[test]
    fn ordering_start_end_same_in() {
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&2), Bound::Included(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&3), Bound::Included(&3)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&4), Bound::Included(&3)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_same_ex() {
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&0), Bound::Excluded(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&1), Bound::Excluded(&3)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&2), Bound::Excluded(&3)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&3), Bound::Excluded(&3)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&4), Bound::Excluded(&3)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_in_ex() {
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&0), Bound::Excluded(&2)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&1), Bound::Excluded(&2)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&2), Bound::Excluded(&2)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&3), Bound::Excluded(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_ex_in() {
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&0), Bound::Included(&2)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&1), Bound::Included(&2)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&2), Bound::Included(&2)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&3), Bound::Included(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_unbounded() {
        assert_eq!(
            GenericRange::cmp_end_start(Bound::<&usize>::Unbounded, Bound::Unbounded),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Unbounded, Bound::Included(&3)),
            Ordering::Greater
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&3), Bound::Unbounded),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_same_in() {
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&2), Bound::Included(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&3), Bound::Included(&3)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&4), Bound::Included(&3)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_same_ex() {
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&2), Bound::Excluded(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&3), Bound::Excluded(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&4), Bound::Excluded(&3)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&5), Bound::Excluded(&3)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&6), Bound::Excluded(&3)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_in_ex() {
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&2), Bound::Excluded(&2)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&3), Bound::Excluded(&2)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&4), Bound::Excluded(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_ex_in() {
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&2), Bound::Included(&2)),
            Ordering::Less
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&3), Bound::Included(&2)),
            Ordering::Equal
        );
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&4), Bound::Included(&2)),
            Ordering::Greater
        );
    }

    #[test]
    fn range_disjoint_range() {
        let range = GenericRange::from(0..4);
        let range2 = GenericRange::from(5..10);

        assert!(range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Disjoint {
                first: range,
                second: range2,
                self_less: true
            }
        );
        assert_eq!(
            range2.relation(range),
            Relation::Disjoint {
                first: range,
                second: range2,
                self_less: false
            }
        )
    }

    #[test]
    fn range_disjoint_singleton() {
        let range = GenericRange::from(3..9);
        let singleton = GenericRange::singleton(10);

        assert!(range.is_disjoint(&singleton));
        assert!(!range.is_touching(&singleton));
        assert!(!range.is_overlapping(&singleton));
        assert!(!range.is_containing(&singleton));
        assert!(!range.is_contained_by(&singleton));
        assert!(!range.is_starting_with(&singleton));
        assert!(!range.is_ending_with(&singleton));
        assert!(!range.is_equal(&singleton));

        assert_eq!(
            range.relation(singleton),
            Relation::Disjoint {
                first: range,
                second: singleton,
                self_less: true
            }
        );
    }

    #[test]
    fn singleton_disjoint_range() {
        let singleton = GenericRange::singleton(1);
        let range = GenericRange::from(2..5);

        assert!(singleton.is_disjoint(&range));
        assert!(!singleton.is_touching(&range));
        assert!(!singleton.is_overlapping(&range));
        assert!(!singleton.is_containing(&range));
        assert!(!singleton.is_contained_by(&range));
        assert!(!singleton.is_starting_with(&range));
        assert!(!singleton.is_ending_with(&range));
        assert!(!singleton.is_equal(&range));

        assert_eq!(
            singleton.relation(range),
            Relation::Disjoint {
                first: singleton,
                second: range,
                self_less: true
            }
        )
    }

    #[test]
    fn range_touching_range() {
        let range = GenericRange::from(0..5);
        let range2 = GenericRange::from(5..10);

        assert!(!range.is_disjoint(&range2));
        assert!(range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Touching {
                first: range,
                second: range2,
                self_less: true
            }
        );
    }

    #[test]
    fn range_touching_singleton() {
        let range = GenericRange::from(3..10);
        let singleton = GenericRange::singleton(10);

        assert!(!range.is_disjoint(&singleton));
        assert!(range.is_touching(&singleton));
        assert!(!range.is_overlapping(&singleton));
        assert!(!range.is_containing(&singleton));
        assert!(!range.is_contained_by(&singleton));
        assert!(!range.is_starting_with(&singleton));
        assert!(!range.is_ending_with(&singleton));
        assert!(!range.is_equal(&singleton));

        assert_eq!(
            range.relation(singleton),
            Relation::Touching {
                first: range,
                second: singleton,
                self_less: true
            }
        )
    }

    #[test]
    fn singleton_touching_range() {
        let singleton = GenericRange::singleton(3);
        let range = GenericRange::new_open_closed(3, 10);

        assert!(!singleton.is_disjoint(&range));
        assert!(singleton.is_touching(&range));
        assert!(!singleton.is_overlapping(&range));
        assert!(!singleton.is_containing(&range));
        assert!(!singleton.is_contained_by(&range));
        assert!(!singleton.is_starting_with(&range));
        assert!(!singleton.is_ending_with(&range));
        assert!(!singleton.is_equal(&range));

        assert_eq!(
            range.relation(singleton),
            Relation::Touching {
                first: singleton,
                second: range,
                self_less: false
            }
        )
    }

    #[test]
    fn range_overlapping_range() {
        let range = GenericRange::from(0..=10);
        let range2 = GenericRange::from(5..=15);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Overlapping {
                first_disjoint: GenericRange::from(0..5),
                second_disjoint: GenericRange::new_open_closed(10, 15),
                overlap: GenericRange::from(5..=10),
                self_less: true,
                overlap_is_singleton: false
            }
        );
        assert_eq!(
            range2.relation(range),
            Relation::Overlapping {
                first_disjoint: GenericRange::from(0..5),
                second_disjoint: GenericRange::new_open_closed(10, 15),
                overlap: GenericRange::from(5..=10),
                self_less: false,
                overlap_is_singleton: false
            }
        )
    }

    #[test]
    fn range_overlap_range_one_element() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(5..10);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Overlapping {
                first_disjoint: GenericRange::from(0..5),
                second_disjoint: GenericRange::new_open(5, 10),
                overlap: GenericRange::singleton(5),
                self_less: true,
                overlap_is_singleton: true
            }
        );
    }

    #[test]
    fn range_overlap_range_one_element_reverse() {
        let range = GenericRange::from(5..10);
        let range2 = GenericRange::from(0..=5);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Overlapping {
                first_disjoint: GenericRange::from(0..5),
                second_disjoint: GenericRange::new_open(5, 10),
                overlap: GenericRange::singleton(5),
                self_less: false,
                overlap_is_singleton: true
            }
        );
    }

    #[test]
    fn range_contains_singleton() {
        let range = GenericRange::from(0..=5);
        let singleton = GenericRange::singleton(3);

        assert!(!range.is_disjoint(&singleton));
        assert!(!range.is_touching(&singleton));
        assert!(!range.is_overlapping(&singleton));
        assert!(range.is_containing(&singleton));
        assert!(!range.is_contained_by(&singleton));
        assert!(!range.is_starting_with(&singleton));
        assert!(!range.is_ending_with(&singleton));
        assert!(!range.is_equal(&singleton));

        assert_eq!(
            range.relation(singleton),
            Relation::Containing {
                first_disjoint: GenericRange::from(0..3),
                second_disjoint: GenericRange::new_open_closed(3, 5),
                overlap: singleton,
                self_shorter: false
            }
        )
    }

    #[test]
    fn singleton_contained_in_range() {
        let singleton = GenericRange::singleton(3);
        let range = GenericRange::from(0..=5);

        assert!(!singleton.is_disjoint(&range));
        assert!(!singleton.is_touching(&range));
        assert!(!singleton.is_overlapping(&range));
        assert!(!singleton.is_containing(&range));
        assert!(singleton.is_contained_by(&range));
        assert!(!singleton.is_starting_with(&range));
        assert!(!singleton.is_ending_with(&range));
        assert!(!singleton.is_equal(&range));

        assert_eq!(
            singleton.relation(range),
            Relation::Containing {
                first_disjoint: GenericRange::from(0..3),
                second_disjoint: GenericRange::new_open_closed(3, 5),
                overlap: singleton,
                self_shorter: true
            }
        )
    }

    #[test]
    fn range_contains_range() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(1..3);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Containing {
                first_disjoint: GenericRange::from(0..1),
                second_disjoint: GenericRange::from(3..=5),
                overlap: range2,
                self_shorter: false
            }
        )
    }

    #[test]
    fn range_contained_in_range() {
        let range = GenericRange::from(1..3);
        let range2 = GenericRange::from(0..=5);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Containing {
                first_disjoint: GenericRange::from(0..1),
                second_disjoint: GenericRange::from(3..=5),
                overlap: range,
                self_shorter: true
            }
        )
    }

    #[test]
    fn range_starts_with_longer_range() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(0..10);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Starting {
                overlap: range,
                disjoint: GenericRange::new_open(5, 10),
                self_shorter: true,
                shorter_is_singleton: false
            }
        )
    }

    #[test]
    fn range_starts_with_shorter_range() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(0..3);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Starting {
                overlap: range2,
                disjoint: GenericRange::from(3..=5),
                self_shorter: false,
                shorter_is_singleton: false
            }
        )
    }

    #[test]
    fn range_starts_with_singleton() {
        let range = GenericRange::from(0..=5);
        let singleton = GenericRange::singleton(0);

        assert!(!range.is_disjoint(&singleton));
        assert!(!range.is_touching(&singleton));
        assert!(!range.is_overlapping(&singleton));
        assert!(!range.is_containing(&singleton));
        assert!(!range.is_contained_by(&singleton));
        assert!(range.is_starting_with(&singleton));
        assert!(!range.is_ending_with(&singleton));
        assert!(!range.is_equal(&singleton));

        assert_eq!(
            range.relation(singleton),
            Relation::Starting {
                overlap: singleton,
                disjoint: GenericRange::new_open_closed(0, 5),
                self_shorter: false,
                shorter_is_singleton: true
            }
        )
    }

    #[test]
    fn singleton_starting_in_range() {
        let singleton = GenericRange::singleton(0);
        let range = GenericRange::from(0..=5);

        assert!(!singleton.is_disjoint(&range));
        assert!(!singleton.is_touching(&range));
        assert!(!singleton.is_overlapping(&range));
        assert!(!singleton.is_containing(&range));
        assert!(!singleton.is_contained_by(&range));
        assert!(singleton.is_starting_with(&range));
        assert!(!singleton.is_ending_with(&range));
        assert!(!singleton.is_equal(&range));

        assert_eq!(
            singleton.relation(range),
            Relation::Starting {
                overlap: singleton,
                disjoint: GenericRange::new_open_closed(0, 5),
                self_shorter: true,
                shorter_is_singleton: true
            }
        )
    }

    #[test]
    fn range_ends_with_shorter_range() {
        let range = GenericRange::from(0..10);
        let range2 = GenericRange::from(5..=9);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Ending {
                disjoint: GenericRange::from(0..5),
                overlap: GenericRange::from(5..=9),
                self_shorter: false,
                shorter_is_singleton: false
            }
        )
    }

    #[test]
    fn range_ends_with_longer_range() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(-5..6);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(range.is_ending_with(&range2));
        assert!(!range.is_equal(&range2));

        assert_eq!(
            range.relation(range2),
            Relation::Ending {
                disjoint: GenericRange::from(-5..0),
                overlap: GenericRange::from(0..=5),
                self_shorter: true,
                shorter_is_singleton: false
            }
        )
    }

    #[test]
    fn range_ends_with_singleton() {
        let range = GenericRange::from(0..=5);
        let singleton = GenericRange::singleton(5);

        assert!(!range.is_disjoint(&singleton));
        assert!(!range.is_touching(&singleton));
        assert!(!range.is_overlapping(&singleton));
        assert!(!range.is_containing(&singleton));
        assert!(!range.is_contained_by(&singleton));
        assert!(!range.is_starting_with(&singleton));
        assert!(range.is_ending_with(&singleton));
        assert!(!range.is_equal(&singleton));

        assert_eq!(
            range.relation(singleton),
            Relation::Ending {
                disjoint: GenericRange::from(0..5),
                overlap: singleton,
                self_shorter: false,
                shorter_is_singleton: true
            }
        )
    }

    #[test]
    fn singleton_ends_with_range() {
        let singleton = GenericRange::singleton(5);
        let range = GenericRange::from(0..=5);

        assert!(!singleton.is_disjoint(&range));
        assert!(!singleton.is_touching(&range));
        assert!(!singleton.is_overlapping(&range));
        assert!(!singleton.is_containing(&range));
        assert!(!singleton.is_contained_by(&range));
        assert!(!singleton.is_starting_with(&range));
        assert!(singleton.is_ending_with(&range));
        assert!(!singleton.is_equal(&range));

        assert_eq!(
            singleton.relation(range),
            Relation::Ending {
                disjoint: GenericRange::from(0..5),
                overlap: singleton,
                self_shorter: true,
                shorter_is_singleton: true
            }
        )
    }

    #[test]
    fn ranges_are_equal() {
        let range = GenericRange::from(0..=5);
        let range2 = GenericRange::from(0..6);

        assert!(!range.is_disjoint(&range2));
        assert!(!range.is_touching(&range2));
        assert!(!range.is_overlapping(&range2));
        assert!(!range.is_containing(&range2));
        assert!(!range.is_contained_by(&range2));
        assert!(!range.is_starting_with(&range2));
        assert!(!range.is_ending_with(&range2));
        assert!(range.is_equal(&range2));

        assert_eq!(range.relation(range2), Relation::Equal(range))
    }

    #[test]
    fn singletons_are_equal() {
        let singleton = GenericRange::singleton(3);
        let singleton2 = GenericRange::singleton(3);

        assert!(!singleton.is_disjoint(&singleton2));
        assert!(!singleton.is_touching(&singleton2));
        assert!(!singleton.is_overlapping(&singleton2));
        assert!(!singleton.is_containing(&singleton2));
        assert!(!singleton.is_contained_by(&singleton2));
        assert!(!singleton.is_starting_with(&singleton2));
        assert!(!singleton.is_ending_with(&singleton2));
        assert!(singleton.is_equal(&singleton2));

        assert_eq!(singleton.relation(singleton2), Relation::Equal(singleton))
    }

    #[test]
    fn equality() {
        // exact
        assert_eq!(GenericRange::from(1..3), GenericRange::from(1..3));
        assert_eq!(GenericRange::from(1..=3), GenericRange::from(1..=3));

        // discrete-only
        assert_eq!(GenericRange::from(1..3), GenericRange::from(1..=2));
        assert_eq!(GenericRange::new_open_closed(1, 3), GenericRange::from(2..=3));
        assert_eq!(GenericRange::new_open(0, 5), GenericRange::from(1..=4));
    }

    proptest! {
        #[ignore]
        #[test]
        fn verify_is_methods(range in any::<GenericRange<u8>>(), range2 in any::<GenericRange<u8>>()) {
            let arrangement = range.arrangement(&range2);
            match arrangement {
                Arrangement::Disjoint {..} => {
                    prop_assert!(range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Touching {..} => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Overlapping {..} => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Containing { self_shorter } => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    if self_shorter {
                        prop_assert!(!range.is_containing(&range2));
                        prop_assert!(range.is_contained_by(&range2));
                    } else {
                        prop_assert!(range.is_containing(&range2));
                        prop_assert!(!range.is_contained_by(&range2));
                    }
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Starting {..} => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Ending {..} => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Equal {..} => {
                    prop_assert!(!range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_containing(&range2));
                    prop_assert!(!range.is_contained_by(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(range.is_equal(&range2));
                    prop_assert!(!range.is_empty());
                    prop_assert!(!range2.is_empty());
                },
                Arrangement::Empty { self_empty } => {
                    prop_assert!(range.is_disjoint(&range2));
                    prop_assert!(!range.is_touching(&range2));
                    prop_assert!(!range.is_overlapping(&range2));
                    prop_assert!(!range.is_starting_with(&range2));
                    prop_assert!(!range.is_ending_with(&range2));
                    prop_assert!(!range.is_equal(&range2));

                    if let Some(self_empty) = self_empty {
                        if self_empty {
                            prop_assert!(!range.is_containing(&range2));
                            prop_assert!(range.is_contained_by(&range2));
                            prop_assert!(range.is_empty());
                            prop_assert!(!range2.is_empty());
                        } else {
                            prop_assert!(range.is_containing(&range2));
                            prop_assert!(!range.is_contained_by(&range2));
                            prop_assert!(!range.is_empty());
                            prop_assert!(range2.is_empty());
                        }
                    } else {
                        prop_assert!(range.is_empty());
                        prop_assert!(range2.is_empty());
                        prop_assert!(range.is_containing(&range2));
                        prop_assert!(range.is_contained_by(&range2));
                    }
                },
            }
        }
    }

    #[test]
    fn proptest_regression_ef1f382ef046c89c98c6650bf4e5e089142b0384f653a2eedeb618c1b828d3bf() {
        let range1 = GenericRange::from(34..189);
        let range2 = GenericRange::from(0..=0);

        assert_eq!(range1.arrangement(&range2), Arrangement::Disjoint { self_less: false });
        assert!(!range1.is_overlapping(&range2));
    }

    #[test]
    fn proptest_regression_6b871bef71a563b2b17b385dc62a2c6b911d8ecf35c3767035ed8fe5e4cca5bf() {
        // empty
        let range1 = GenericRange::from(191..191);
        let range2 = GenericRange::from(0..191);

        assert_eq!(
            range1.arrangement(&range2),
            Arrangement::Empty { self_empty: Some(true) }
        );

        assert!(!range1.is_touching(&range2));
    }

    #[test]
    fn proptest_regression_88449f8d7ce537174697fa2d7549720bac21b91f8f9d5926c279a57ea5bbc9f8() {
        let range1 = GenericRange::from(0..132);
        // singleton
        let range2 = GenericRange::new_open_closed(131, 132);

        assert_eq!(range1.arrangement(&range2), Arrangement::Disjoint { self_less: true });
        assert!(!range1.is_overlapping(&range2))
    }

    #[test]
    fn proptest_regression_cb8f7fdcf3afe98e147cdfeda5493d83c57ac71537d80836d5392974d3f1cde4() {
        let range1 = GenericRange::new_open_closed(81, 82);
        let range2 = GenericRange::new_open(0, 82);

        assert_eq!(range1.arrangement(&range2), Arrangement::Disjoint { self_less: false });
        assert!(!range1.is_overlapping(&range2));
    }

    #[test]
    fn proptest_regression_be3d67f34187b919ce7a8552b8f05814011fb47291e523b32737dce1b6b4c934() {
        let range1 = GenericRange::from(0..=0);
        // empty
        let range2 = GenericRange::from(0..0);

        assert_eq!(
            range1.arrangement(&range2),
            Arrangement::Empty {
                self_empty: Some(false)
            }
        );
        assert!(!range1.is_starting_with(&range2));
    }
}

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

    use noisy_float::types::N64;

    use crate::GenericRange;

    #[test]
    fn ordering_start_start_in_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Included(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_start_ex_in() {
        // less
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&N64::new(0.1)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&N64::new(0.2)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_start(Bound::Excluded(&N64::new(0.3)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_end_in_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Included(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_end_ex_in() {
        // less
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&N64::new(0.1)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&N64::new(0.2)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_end(Bound::Excluded(&N64::new(0.3)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_same_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_in_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Included(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_start_end_ex_in() {
        // less
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.1)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.2)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
        // greater
        assert_eq!(
            GenericRange::cmp_start_end(Bound::Excluded(&N64::new(0.3)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_same_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_in_ex() {
        // less
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&N64::new(0.1)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&N64::new(0.2)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Less
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Included(&N64::new(0.3)), Bound::Excluded(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn ordering_end_start_ex_in() {
        // less
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.1)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // equal
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.2)), Bound::Included(&N64::new(0.2))),
            Ordering::Less
        );
        // greater
        assert_eq!(
            GenericRange::cmp_end_start(Bound::Excluded(&N64::new(0.3)), Bound::Included(&N64::new(0.2))),
            Ordering::Greater
        );
    }

    #[test]
    fn equality() {
        let zero = N64::new(0.);
        let one = N64::new(1.);
        let two = N64::new(2.);
        let three = N64::new(3.);
        let four = N64::new(4.);
        let five = N64::new(5.);

        // exact
        assert_eq!(GenericRange::from(one..three), GenericRange::from(one..three));
        assert_eq!(GenericRange::from(one..=three), GenericRange::from(one..=three));

        // discrete-only
        assert_ne!(GenericRange::from(one..three), GenericRange::from(one..=two));
        assert_ne!(GenericRange::new_open_closed(one, two), GenericRange::from(two..=three));
        assert_ne!(GenericRange::new_open(zero, five), GenericRange::from(one..=four));
    }
}