1use crate::bound_point::BoundPoint;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub enum IntervalType {
5 Open,
6 StartOpen,
7 EndOpen,
8 Close,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub struct Interval<T>
13where
14 T: Ord,
15{
16 start: BoundPoint<T>,
17 end: BoundPoint<T>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub enum IntervalError {
22 StartMustBeMinorThanEnd,
23}
24
25impl<T: Ord> Interval<T> {
26 pub fn from_to(start: T, end: T, interval_type: IntervalType) -> Result<Self, IntervalError> {
27 Self::validate(&start, &end)?;
28 match interval_type {
29 IntervalType::Open => Ok(Interval {
30 start: BoundPoint::after(start),
31 end: BoundPoint::before(end),
32 }),
33 IntervalType::StartOpen => Ok(Interval {
34 start: BoundPoint::after(start),
35 end: BoundPoint::at(end),
36 }),
37 IntervalType::EndOpen => Ok(Interval {
38 start: BoundPoint::at(start),
39 end: BoundPoint::before(end),
40 }),
41 IntervalType::Close => Ok(Interval {
42 start: BoundPoint::at(start),
43 end: BoundPoint::at(end),
44 }),
45 }
46 }
47
48 pub fn since_exclusive(value: T) -> Self {
49 Interval {
50 start: BoundPoint::after(value),
51 end: BoundPoint::pos_infinity(),
52 }
53 }
54
55 pub fn since_inclusive(value: T) -> Self {
56 Interval {
57 start: BoundPoint::at(value),
58 end: BoundPoint::pos_infinity(),
59 }
60 }
61
62 pub fn until_exclusive(value: T) -> Self {
63 Interval {
64 start: BoundPoint::neg_infinity(),
65 end: BoundPoint::before(value),
66 }
67 }
68
69 pub fn until_inclusive(value: T) -> Self {
70 Interval {
71 start: BoundPoint::neg_infinity(),
72 end: BoundPoint::at(value),
73 }
74 }
75
76 fn validate(start: &T, end: &T) -> Result<(), IntervalError> {
77 if start > end {
78 Err(IntervalError::StartMustBeMinorThanEnd)
79 } else {
80 Ok(())
81 }
82 }
83
84 pub fn contains(&self, value: T) -> bool {
85 let bound_point = BoundPoint::at(value);
86 self.start <= bound_point && self.end >= bound_point
87 }
88
89 pub fn overlaps(&self, other: &Interval<T>) -> bool {
90 self.start <= other.end && self.end >= other.start
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use rstest::rstest;
98
99 #[rstest]
100 #[case(Interval::from_to(1, 3, IntervalType::Open).unwrap(), 1, false)]
102 #[case(Interval::from_to(1, 3, IntervalType::Open).unwrap(), 3, false)]
103 #[case(Interval::from_to(1, 3, IntervalType::Open).unwrap(), 2, true)]
104 #[case(Interval::from_to(1, 3, IntervalType::StartOpen).unwrap(), 1, false)]
105 #[case(Interval::from_to(1, 3, IntervalType::StartOpen).unwrap(), 3, true)]
106 #[case(Interval::from_to(1, 3, IntervalType::StartOpen).unwrap(), 2, true)]
107 #[case(Interval::from_to(1, 3, IntervalType::EndOpen).unwrap(), 1, true)]
108 #[case(Interval::from_to(1, 3, IntervalType::EndOpen).unwrap(), 3, false)]
109 #[case(Interval::from_to(1, 3, IntervalType::EndOpen).unwrap(), 2, true)]
110 #[case(Interval::from_to(1, 3, IntervalType::Close).unwrap(), 1, true)]
111 #[case(Interval::from_to(1, 3, IntervalType::Close).unwrap(), 3, true)]
112 #[case(Interval::from_to(1, 3, IntervalType::Close).unwrap(), 2, true)]
113 #[case(Interval::from_to(1, 3, IntervalType::Close).unwrap(), 0, false)]
114 #[case(Interval::from_to(1, 3, IntervalType::Close).unwrap(), 4, false)]
115 #[case(Interval::until_exclusive(1), 0, true)]
117 #[case(Interval::until_exclusive(1), 1, false)]
118 #[case(Interval::until_exclusive(1), 2, false)]
119 #[case(Interval::until_inclusive(1), 0, true)]
120 #[case(Interval::until_inclusive(1), 1, true)]
121 #[case(Interval::until_inclusive(1), 2, false)]
122 #[case(Interval::since_exclusive(1), 0, false)]
124 #[case(Interval::since_exclusive(1), 1, false)]
125 #[case(Interval::since_exclusive(1), 2, true)]
126 #[case(Interval::since_inclusive(1), 0, false)]
127 #[case(Interval::since_inclusive(1), 1, true)]
128 #[case(Interval::since_inclusive(1), 2, true)]
129 fn test_contains(#[case] interval: Interval<i32>, #[case] value: i32, #[case] expected: bool) {
130 let actual = interval.contains(value);
131 assert_eq!(
132 actual, expected,
133 "failed: {:?}, {} → got {}, expected {}",
134 interval, value, expected, actual
135 );
136 }
137
138 #[rstest]
139 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(0, 3, IntervalType::Open).unwrap(), true)]
154 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(1, 2, IntervalType::Open).unwrap(), true)]
155 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(-1, 0, IntervalType::Open).unwrap(), false)]
156 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(-2, -1, IntervalType::Open).unwrap(), false)]
157 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(3, 4, IntervalType::Open).unwrap(), false)]
158 #[case(Interval::from_to(0, 3, IntervalType::Open).unwrap(), Interval::from_to(-1, 2, IntervalType::Open).unwrap(), true)]
159 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(0, 3, IntervalType::Open).unwrap(), true)]
161 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(1, 2, IntervalType::Open).unwrap(), true)]
162 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-1, 0, IntervalType::Open).unwrap(), false)]
163 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-2, -1, IntervalType::Open).unwrap(), false)]
164 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(3, 4, IntervalType::Open).unwrap(), false)]
165 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-1, 2, IntervalType::Open).unwrap(), true)]
166 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(0, 3, IntervalType::Close).unwrap(), true)]
168 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(1, 2, IntervalType::Close).unwrap(), true)]
169 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-1, 0, IntervalType::Close).unwrap(), true)]
170 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-2, -1, IntervalType::Close).unwrap(), false)]
171 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(3, 4, IntervalType::Close).unwrap(), true)]
172 #[case(Interval::from_to(0, 3, IntervalType::Close).unwrap(), Interval::from_to(-1, 2, IntervalType::Close).unwrap(), true)]
173 fn test_overlaps(
174 #[case] interval: Interval<i32>,
175 #[case] other: Interval<i32>,
176 #[case] expected: bool,
177 ) {
178 let actual = interval.overlaps(&other);
179 assert_eq!(
180 actual, expected,
181 "failed: {:?}, {:?} → got {}, expected {}",
182 interval, other, expected, actual
183 );
184 }
185}