1use crate::res::{OneTwo, ZeroOneTwo};
2
3#[cfg(test)]
4mod between_tests;
5#[cfg(test)]
6mod convex_hull_tests;
7#[cfg(test)]
8mod difference_tests;
9#[cfg(test)]
10mod intersection_tests;
11#[cfg(test)]
12mod symmetric_difference_tests;
13#[cfg(test)]
14mod transform_tests;
15#[cfg(test)]
16mod union_tests;
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
19pub struct I8CO {
20 start: i8,
21 end_excl: i8,
22}
23
24mod construction_accessors_predicates {
28
29 use super::*;
30
31 impl I8CO {
32 #[inline]
33 pub const fn try_new(start: i8, end_excl: i8) -> Option<Self> {
34 if start < end_excl {
35 Some(Self { start, end_excl })
36 } else {
37 None
38 }
39 }
40
41 #[inline]
42 pub(super) const fn new_unchecked(start: i8, end_excl: i8) -> Self {
43 debug_assert!(start < end_excl);
44 Self { start, end_excl }
45 }
46
47 #[inline]
48 pub const fn start(self) -> i8 {
49 self.start
50 }
51
52 #[inline]
53 pub const fn end_excl(self) -> i8 {
54 self.end_excl
55 }
56
57 #[inline]
58 pub const fn end_incl(self) -> i8 {
59 self.end_excl - 1
61 }
62
63 #[inline]
64 pub const fn len(self) -> u8 {
65 (self.end_excl - self.start) as u8
66 }
67
68 #[inline]
69 pub const fn contains(self, x: i8) -> bool {
70 self.start <= x && x < self.end_excl
71 }
72
73 #[inline]
74 pub const fn iter(self) -> core::ops::Range<i8> {
75 self.start..self.end_excl
76 }
77
78 #[inline]
79 pub const fn intersects(self, other: Self) -> bool {
80 !(self.end_excl <= other.start || other.end_excl <= self.start)
81 }
82
83 #[inline]
84 pub const fn is_adjacent(self, other: Self) -> bool {
85 self.end_excl == other.start || other.end_excl == self.start
86 }
87
88 #[inline]
89 pub const fn is_contiguous_with(self, other: Self) -> bool {
90 self.intersects(other) || self.is_adjacent(other)
91 }
92 }
93}
94mod interval_algebra {
99
100 use super::*;
101
102 impl I8CO {
103 #[inline]
107 pub const fn intersection(self, other: Self) -> Option<Self> {
108 let start = if self.start >= other.start {
109 self.start
110 } else {
111 other.start
112 };
113
114 let end_excl = if self.end_excl <= other.end_excl {
115 self.end_excl
116 } else {
117 other.end_excl
118 };
119
120 Self::try_new(start, end_excl)
121 }
122
123 #[inline]
127 pub const fn convex_hull(self, other: Self) -> Self {
128 let start = if self.start <= other.start {
129 self.start
130 } else {
131 other.start
132 };
133
134 let end_excl = if self.end_excl >= other.end_excl {
135 self.end_excl
136 } else {
137 other.end_excl
138 };
139
140 Self { start, end_excl }
141 }
142
143 #[inline]
147 pub const fn between(self, other: Self) -> Option<Self> {
148 let (left, right) = if self.start <= other.start {
149 (self, other)
150 } else {
151 (other, self)
152 };
153
154 Self::try_new(left.end_excl, right.start)
155 }
156
157 #[inline]
162 pub const fn union(self, other: Self) -> OneTwo<Self> {
163 if self.is_contiguous_with(other) {
164 OneTwo::One(self.convex_hull(other))
165 } else if self.start <= other.start {
166 OneTwo::Two(self, other)
167 } else {
168 OneTwo::Two(other, self)
169 }
170 }
171
172 #[inline]
178 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
179 match self.intersection(other) {
180 None => ZeroOneTwo::One(self),
181 Some(inter) => {
182 let left = Self::try_new(self.start, inter.start);
183 let right = Self::try_new(inter.end_excl, self.end_excl);
184
185 match (left, right) {
186 (None, None) => ZeroOneTwo::Zero,
187 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
188 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
189 }
190 }
191 }
192 }
193
194 #[inline]
200 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
201 match self.intersection(other) {
202 None => {
203 if self.start <= other.start {
204 ZeroOneTwo::Two(self, other)
205 } else {
206 ZeroOneTwo::Two(other, self)
207 }
208 }
209 Some(inter) => {
210 let hull = self.convex_hull(other);
211 let left = Self::try_new(hull.start, inter.start);
212 let right = Self::try_new(inter.end_excl, hull.end_excl);
213
214 match (left, right) {
215 (None, None) => ZeroOneTwo::Zero,
216 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
217 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
218 }
219 }
220 }
221 }
222 }
223}
224
225mod interval_arithmetic {
230 use super::I8CO;
231
232 impl I8CO {
233 #[inline]
234 pub const fn scale(self, factor: i8) -> Option<Self> {
235 if factor == 0 {
236 return None;
237 }
238
239 let s0 = match self.start.checked_mul(factor) {
240 Some(v) => v,
241 None => return None,
242 };
243
244 let s1 = match self.end_excl.checked_mul(factor) {
245 Some(v) => v,
246 None => return None,
247 };
248
249 if s0 < s1 {
250 Some(I8CO::new_unchecked(s0, s1))
251 } else {
252 Some(I8CO::new_unchecked(s1, s0))
253 }
254 }
255
256 #[inline]
257 pub const fn shift(self, offset: i8) -> Option<Self> {
258 let a = match self.start.checked_add(offset) {
259 Some(v) => v,
260 None => return None,
261 };
262
263 let b = match self.end_excl.checked_add(offset) {
264 Some(v) => v,
265 None => return None,
266 };
267
268 Some(I8CO::new_unchecked(a, b))
269 }
270 }
271}