1use std::ops::{Bound, RangeBounds, RangeInclusive, Sub};
2
3pub trait BasicNum {
5 const MIN_VALUE: Self;
6 const MAX_VALUE: Self;
7 fn dec(&self) -> Self;
8 fn inc(&self) -> Self;
9}
10macro_rules! impl_primitive_basic_num {
11 ($($t:ty),*) => {
12 $(
13 impl BasicNum for $t {
14 const MIN_VALUE: Self = Self::MIN;
15 const MAX_VALUE: Self = Self::MAX;
16 fn dec(&self) -> Self {
17 self - 1
18 }
19 fn inc(&self) -> Self {
20 self + 1
21 }
22 }
23 )*
24 };
25}
26impl_primitive_basic_num!(usize, isize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
28
29pub trait RangeUtil<T: Ord + Clone + BasicNum>: Sized + Clone {
31 fn starts_at(&self) -> T;
33 fn ends_at(&self) -> T;
35
36 fn len(&self) -> Option<T>
38 where
39 T: Sub<Output = T>,
40 {
41 (self.ends_at() >= self.starts_at()).then(|| self.ends_at().inc() - self.starts_at())
42 }
43 fn includes(&self, x: &T) -> bool {
45 &self.starts_at() <= x && x <= &self.ends_at()
46 }
47 fn intersects(&self, other: &impl RangeUtil<T>) -> bool {
51 self.ends_at() >= other.starts_at() && self.starts_at() <= other.ends_at()
52 }
53 fn intersection(&self, other: &impl RangeUtil<T>) -> Option<RangeInclusive<T>> {
57 self.intersects(other)
58 .then(|| self.starts_at().max(other.starts_at())..=self.ends_at().min(other.ends_at()))
59 }
60 fn setminus(
66 &self,
67 other: &impl RangeUtil<T>,
68 ) -> (Option<RangeInclusive<T>>, Option<RangeInclusive<T>>) {
69 let Some(other) = self.intersection(other) else {
70 return (
71 Some(self.starts_at().clone()..=self.ends_at().clone()),
72 None,
73 );
74 };
75 let (a, b) = (self.starts_at().clone(), self.ends_at().clone());
76 let (c, d) = (other.start().clone(), other.ends_at().clone());
77 (
78 (self.includes(&c))
79 .then(|| a..=c.dec())
80 .filter(|r| !r.is_empty()),
81 self.includes(&d)
82 .then(|| d.inc()..=b)
83 .filter(|r| !r.is_empty()),
84 )
85 }
86}
87impl<T: Ord + Clone + BasicNum, R: RangeBounds<T> + Clone> RangeUtil<T> for R {
88 fn starts_at(&self) -> T {
89 match self.start_bound() {
90 Bound::Excluded(x) => x.inc(),
91 Bound::Included(x) => x.clone(),
92 Bound::Unbounded => T::MIN_VALUE,
93 }
94 }
95 fn ends_at(&self) -> T {
96 match self.end_bound() {
97 Bound::Excluded(x) => x.dec(),
98 Bound::Included(x) => x.clone(),
99 Bound::Unbounded => T::MAX_VALUE,
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use std::ops::RangeFull;
107
108 use crate::RangeUtil;
109
110 #[test]
111 fn test_intersection_range_inclusive() {
112 assert_eq!((0..=3).intersection(&(1..=2)), Some(1..=2));
113 assert_eq!((0..=3).intersection(&(1..=30)), Some(1..=3));
114 assert_eq!((0..=3).intersection(&(-10..1)), Some(0..=0));
115 assert_eq!((0..=3).intersection(&(-10..=1)), Some(0..=1));
116 assert_eq!((0..=3).intersection(&(-10..=-1)), None);
117 assert_eq!((0..=3).intersection(&(4..=10)), None);
118 }
119
120 #[test]
121 fn test_difference_range_inclusive() {
122 assert_eq!((0..=3).setminus(&(4..=100)), (Some(0..=3), None));
123 assert_eq!((0..=3).setminus(&(-100..=-1)), (Some(0..=3), None));
124 assert_eq!((0..=3).setminus(&(1..=2)), (Some(0..=0), Some(3..=3)));
125 assert_eq!((0..=3).setminus(&(0..=2)), (None, Some(3..=3)));
126 assert_eq!((0..=3).setminus(&(1..=3)), (Some(0..=0), None));
127 }
128
129 #[test]
130 fn test_from_incl() {
131 assert_eq!((0..).starts_at(), 0);
132 assert_eq!((0..1).starts_at(), 0);
133 assert_eq!((0..=1).starts_at(), 0);
134 assert_eq!((..=10).starts_at(), 0usize);
135 assert_eq!((..=10).starts_at(), isize::MIN);
136 assert_eq!(RangeUtil::<usize>::starts_at(&RangeFull), 0);
137 assert_eq!(RangeUtil::<isize>::starts_at(&RangeFull), isize::MIN);
138 }
139
140 #[test]
141 fn test_to_incl() {
142 assert_eq!((0..).ends_at(), usize::MAX);
143 assert_eq!((0..1).ends_at(), 0);
144 assert_eq!((0..=1).ends_at(), 1);
145 assert_eq!((..=10).ends_at(), 10);
146 assert_eq!((10..).ends_at(), isize::MAX);
147 assert_eq!(RangeUtil::<usize>::ends_at(&RangeFull), usize::MAX);
148 assert_eq!(RangeUtil::<isize>::ends_at(&RangeFull), isize::MAX);
149 }
150}