lset 0.3.0

Data types describing linear sets
Documentation
// SPDX-License-Identifier: Apache-2.0

use super::*;
use core::{cmp::Ordering, ops::*};

/// Expresses a linear set by its starting and termination points
///
/// This type is fully isomorphic with `core::ops::Range` and `Span`. However,
/// unlike `core::ops::Range`, this type is not an iterator and therefore can
/// implement `Copy`. Points may have any number of dimensions.
#[repr(C)]
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct Line<T> {
    /// The start point
    pub start: T,

    /// The first point excluded by the set
    pub end: T,
}

impl<T> Line<T> {
    /// Create a new line
    ///
    /// # Example
    ///
    /// ```
    /// let line = lset::Line::new(5, 10);
    /// assert_eq!(line.start, 5);
    /// assert_eq!(line.end, 10);
    /// ```
    #[inline(always)]
    pub const fn new(start: T, end: T) -> Self {
        Self { start, end }
    }

    /// Indicates whether the line is empty
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert!(Line::from(2..2).is_empty());
    /// assert!(!Line::from(2..3).is_empty());
    /// ```
    #[inline(always)]
    pub fn is_empty(&self) -> bool
    where
        T: PartialEq,
    {
        self.start == self.end
    }
}

impl<T: PartialOrd> Line<T> {
    /// Returns the intersection between the sets, if any.
    ///
    /// ```
    /// use lset::*;
    ///
    /// let a = Line::new(0, 5);
    /// let b = Line::new(2, 7);
    /// let c = Line::new(5, 10);
    ///
    /// assert_eq!(a.intersection(b), Some(Line::new(2, 5)));
    /// assert_eq!(b.intersection(c), Some(Line::new(5, 7)));
    /// assert_eq!(a.intersection(a), Some(a));
    /// assert_eq!(b.intersection(b), Some(b));
    /// assert_eq!(c.intersection(c), Some(c));
    /// assert_eq!(a.intersection(c), None);
    /// ```
    pub fn intersection(self, other: Self) -> Option<Self> {
        let start = if other.start >= self.start && other.start < self.end {
            Some(other.start)
        } else {
            None
        };

        let end = if other.end > self.start && other.end <= self.end {
            Some(other.end)
        } else {
            None
        };

        match (start, end) {
            (Some(start), Some(end)) => Some(Line::new(start, end)),
            (Some(start), None) => Some(Line::new(start, self.end)),
            (None, Some(end)) => Some(Line::new(self.start, end)),
            (None, None) => None,
        }
    }
}

impl<T: Add<T, Output = T>> Add<T> for Line<T> {
    type Output = Self;

    /// Grows the line by the size of the operand
    ///
    /// # Example
    ///
    /// ```
    /// let before = lset::Line::new(5, 10);
    /// let after = before + 5;
    /// assert_eq!(after.start, 5);
    /// assert_eq!(after.end, 15);
    /// ```
    #[inline(always)]
    fn add(self, rhs: T) -> Self::Output {
        Self {
            start: self.start,
            end: self.end + rhs,
        }
    }
}

impl<T: AddAssign<T>> AddAssign<T> for Line<T> {
    /// Grows the line by the size of the operand
    ///
    /// # Example
    ///
    /// ```
    /// let mut line = lset::Line::new(5, 10);
    /// line += 5;
    /// assert_eq!(line.start, 5);
    /// assert_eq!(line.end, 15);
    /// ```
    #[inline(always)]
    fn add_assign(&mut self, rhs: T) {
        self.end += rhs;
    }
}

impl<T: Sub<T, Output = T>> Sub<T> for Line<T> {
    type Output = Self;

    /// Shrinks the line by the size of the operand
    ///
    /// # Example
    ///
    /// ```
    /// let before = lset::Line::new(5, 10);
    /// let after = before - 5;
    /// assert_eq!(after.start, 5);
    /// assert_eq!(after.end, 5);
    /// ```
    #[inline(always)]
    fn sub(self, rhs: T) -> Self::Output {
        Self {
            start: self.start,
            end: self.end - rhs,
        }
    }
}

impl<T: SubAssign<T>> SubAssign<T> for Line<T> {
    /// Shrinks the line by the size of the operand
    ///
    /// # Example
    ///
    /// ```
    /// let mut line = lset::Line::new(5, 10);
    /// line -= 5;
    /// assert_eq!(line.start, 5);
    /// assert_eq!(line.end, 5);
    /// ```
    #[inline(always)]
    fn sub_assign(&mut self, rhs: T) {
        self.end -= rhs;
    }
}

impl<T: Copy + Sub<T, Output = T>> Shl<T> for Line<T> {
    type Output = Self;

    /// Shifts the line downwards without changing size
    ///
    /// # Example
    ///
    /// ```
    /// let before = lset::Line::new(5, 10);
    /// let after = before << 5;
    /// assert_eq!(after.start, 0);
    /// assert_eq!(after.end, 5);
    /// ```
    #[inline(always)]
    fn shl(self, rhs: T) -> Self::Output {
        Self {
            start: self.start - rhs,
            end: self.end - rhs,
        }
    }
}

impl<T: Copy + SubAssign<T>> ShlAssign<T> for Line<T> {
    /// Shifts the line downwards without changing size
    ///
    /// # Example
    ///
    /// ```
    /// let mut line = lset::Line::new(5, 10);
    /// line <<= 5;
    /// assert_eq!(line.start, 0);
    /// assert_eq!(line.end, 5);
    /// ```
    #[inline(always)]
    fn shl_assign(&mut self, rhs: T) {
        self.start -= rhs;
        self.end -= rhs;
    }
}

impl<T: Copy + Add<T, Output = T>> Shr<T> for Line<T> {
    type Output = Self;

    /// Shifts the line upwards without changing size
    ///
    /// # Example
    ///
    /// ```
    /// let before = lset::Line::new(5, 10);
    /// let after = before >> 5;
    /// assert_eq!(after.start, 10);
    /// assert_eq!(after.end, 15);
    /// ```
    #[inline(always)]
    fn shr(self, rhs: T) -> Self::Output {
        Self {
            start: self.start + rhs,
            end: self.end + rhs,
        }
    }
}

impl<T: Copy + AddAssign<T>> ShrAssign<T> for Line<T> {
    /// Shifts the line upwards without changing size
    ///
    /// # Example
    ///
    /// ```
    /// let mut line = lset::Line::new(5, 10);
    /// line >>= 5;
    /// assert_eq!(line.start, 10);
    /// assert_eq!(line.end, 15);
    /// ```
    #[inline(always)]
    fn shr_assign(&mut self, rhs: T) {
        self.start += rhs;
        self.end += rhs;
    }
}

impl<T: PartialOrd> PartialOrd for Line<T> {
    /// Compares two `Line` types.
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert!(Line::new(5, 10) <= Line::new(5, 10));
    /// assert!(Line::new(5, 10) >= Line::new(5, 10));
    /// assert!(Line::new(5, 10) < Line::new(10, 15));
    /// assert!(Line::new(10, 15) > Line::new(5, 10));
    /// ```
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self == other {
            Some(Ordering::Equal)
        } else if self.start >= other.end {
            Some(Ordering::Greater)
        } else if self.end <= other.start {
            Some(Ordering::Less)
        } else {
            None
        }
    }
}

impl<T> From<Range<T>> for Line<T> {
    /// Converts a `Range` into a `Line`
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert_eq!(Line::new(5, 10), Line::from(5..10));
    /// assert_eq!(Line::new(5, 10), (5..10).into());
    /// ```
    #[inline(always)]
    fn from(value: Range<T>) -> Self {
        Self {
            start: value.start,
            end: value.end,
        }
    }
}

impl<T> From<Line<T>> for Range<T> {
    /// Converts a `Line` into a `Range`
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert_eq!(5..10, std::ops::Range::from(Line::new(5, 10)));
    /// assert_eq!(5..10, Line::new(5, 10).into());
    /// ```
    #[inline(always)]
    fn from(value: Line<T>) -> Self {
        Self {
            start: value.start,
            end: value.end,
        }
    }
}

impl<T: Clone + Add<U, Output = T>, U> From<Span<T, U>> for Line<T> {
    /// Converts a `Span` into a `Line`
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert_eq!(Line::new(5, 10), Line::from(Span::new(5, 5)));
    /// assert_eq!(Line::new(5, 10), Span::new(5, 5).into());
    /// ```
    #[inline(always)]
    fn from(value: Span<T, U>) -> Self {
        Self {
            start: value.start.clone(),
            end: value.start + value.count,
        }
    }
}

impl<T: PartialOrd> Contains<T> for Line<T> {
    /// Indicates whether the line contains a point
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert!(!Line::from(2..3).contains(&1));
    /// assert!(Line::from(2..3).contains(&2));
    /// assert!(!Line::from(2..3).contains(&3));

    /// assert!(!Line::from(3..2).contains(&1));
    /// assert!(!Line::from(3..2).contains(&2));
    /// assert!(!Line::from(3..2).contains(&3));
    /// ```
    #[inline(always)]
    fn contains(&self, value: &T) -> bool {
        if self.start <= self.end {
            &self.start <= value && value < &self.end
        } else {
            false
        }
    }
}

impl<T: PartialOrd> Contains<Self> for Line<T> {
    /// Indicates whether the line contains another line
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// assert!(Line::from(4..8).contains(&Line::from(5..7)));
    /// assert!(Line::from(4..8).contains(&Line::from(4..7)));
    /// assert!(Line::from(4..8).contains(&Line::from(5..8)));
    /// assert!(Line::from(4..8).contains(&Line::from(4..8)));
    /// assert!(!Line::from(4..8).contains(&Line::from(3..8)));
    /// assert!(!Line::from(4..8).contains(&Line::from(4..9)));
    /// assert!(!Line::from(4..8).contains(&Line::from(3..9)));
    /// assert!(!Line::from(4..8).contains(&Line::from(2..10)));
    /// assert!(!Line::from(4..8).contains(&Line::from(6..5)));
    /// assert!(!Line::from(7..3).contains(&Line::from(5..6)));
    /// ```
    #[inline(always)]
    fn contains(&self, value: &Self) -> bool {
        if self.start <= self.end && value.start <= value.end {
            self.start <= value.start && value.end <= self.end
        } else {
            false
        }
    }
}

impl<T: PartialOrd> Split<Self> for Line<T> {
    /// Splits a line by another line
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// let line = Line::from(2..5);
    /// assert_eq!(line.split(Line::from(1..4)), None);
    /// assert_eq!(line.split(Line::from(3..6)), None);
    /// assert_eq!(line.split(Line::from(2..2)), None);
    /// assert_eq!(line.split(Line::from(2..3)), Some((Line::from(2..2), Line::from(3..5))));
    /// assert_eq!(line.split(Line::from(3..3)), None);
    /// assert_eq!(line.split(Line::from(3..4)), Some((Line::from(2..3), Line::from(4..5))));
    /// assert_eq!(line.split(Line::from(4..4)), None);
    /// assert_eq!(line.split(Line::from(4..5)), Some((Line::from(2..4), Line::from(5..5))));
    /// assert_eq!(line.split(Line::from(5..5)), None);
    /// assert_eq!(line.split(line), Some((Line::from(2..2), Line::from(5..5))));
    /// ```
    #[inline(always)]
    fn split(self, at: Self) -> Option<(Self, Self)> {
        if at.start == at.end {
            return None;
        }

        if !self.contains(&at) {
            return None;
        }

        let l = Self {
            start: self.start,
            end: at.start,
        };

        let r = Self {
            start: at.end,
            end: self.end,
        };

        Some((l, r))
    }
}

impl<T: PartialOrd + Copy> Split<T> for Line<T> {
    /// Splits a line at a point
    ///
    /// # Example
    ///
    /// ```
    /// use lset::*;
    /// let line = Line::from(2..4);
    /// assert_eq!(line.split(1), None);
    /// assert_eq!(line.split(2), Some((Line::from(2..2), line)));
    /// assert_eq!(line.split(3), Some((Line::from(2..3), Line::from(3..4))));
    /// assert_eq!(line.split(4), Some((line, Line::from(4..4))));
    /// assert_eq!(line.split(5), None);
    /// ```
    #[inline(always)]
    fn split(self, at: T) -> Option<(Self, Self)> {
        if at < self.start || at > self.end {
            return None;
        }

        let l = Self {
            start: self.start,
            end: at,
        };

        let r = Self {
            start: at,
            end: self.end,
        };

        Some((l, r))
    }
}