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 U8CO {
20 start: u8,
21 end_excl: u8,
22}
23
24mod construction_accessors_predicates {
29
30 use super::*;
31
32 impl U8CO {
33 #[inline]
34 pub const fn try_new(start: u8, end_excl: u8) -> Option<Self> {
35 if start < end_excl {
36 Some(Self { start, end_excl })
37 } else {
38 None
39 }
40 }
41
42 #[inline]
43 pub(super) const fn new_unchecked(start: u8, end_excl: u8) -> Self {
44 debug_assert!(start < end_excl);
45 Self { start, end_excl }
46 }
47
48 #[inline]
49 pub const fn start(self) -> u8 {
50 self.start
51 }
52
53 #[inline]
54 pub const fn end_excl(self) -> u8 {
55 self.end_excl
56 }
57
58 #[inline]
59 pub const fn end_incl(self) -> u8 {
60 self.end_excl - 1
62 }
63
64 #[inline]
65 pub const fn len(self) -> u8 {
66 self.end_excl - self.start
67 }
68
69 #[inline]
70 pub const fn contains(self, x: u8) -> bool {
71 self.start <= x && x < self.end_excl
72 }
73
74 #[inline]
75 pub const fn iter(self) -> core::ops::Range<u8> {
76 self.start..self.end_excl
77 }
78
79 #[inline]
80 pub const fn intersects(self, other: Self) -> bool {
81 !(self.end_excl <= other.start || other.end_excl <= self.start)
82 }
83
84 #[inline]
85 pub const fn is_adjacent(self, other: Self) -> bool {
86 self.end_excl == other.start || other.end_excl == self.start
87 }
88
89 #[inline]
90 pub const fn is_contiguous_with(self, other: Self) -> bool {
91 self.intersects(other) || self.is_adjacent(other)
92 }
93 }
94}
95
96mod interval_algebra {
101
102 use super::*;
103
104 impl U8CO {
105 #[inline]
109 pub const fn intersection(self, other: Self) -> Option<Self> {
110 let start = if self.start >= other.start {
111 self.start
112 } else {
113 other.start
114 };
115
116 let end_excl = if self.end_excl <= other.end_excl {
117 self.end_excl
118 } else {
119 other.end_excl
120 };
121
122 Self::try_new(start, end_excl)
123 }
124
125 #[inline]
129 pub const fn convex_hull(self, other: Self) -> Self {
130 let start = if self.start <= other.start {
131 self.start
132 } else {
133 other.start
134 };
135
136 let end_excl = if self.end_excl >= other.end_excl {
137 self.end_excl
138 } else {
139 other.end_excl
140 };
141
142 Self { start, end_excl }
143 }
144
145 #[inline]
149 pub const fn between(self, other: Self) -> Option<Self> {
150 let (left, right) = if self.start <= other.start {
151 (self, other)
152 } else {
153 (other, self)
154 };
155
156 Self::try_new(left.end_excl, right.start)
157 }
158
159 #[inline]
164 pub const fn union(self, other: Self) -> OneTwo<Self> {
165 if self.is_contiguous_with(other) {
166 OneTwo::One(self.convex_hull(other))
167 } else if self.start <= other.start {
168 OneTwo::Two(self, other)
169 } else {
170 OneTwo::Two(other, self)
171 }
172 }
173
174 #[inline]
180 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
181 match self.intersection(other) {
182 None => ZeroOneTwo::One(self),
183 Some(inter) => {
184 let left = Self::try_new(self.start, inter.start);
185 let right = Self::try_new(inter.end_excl, self.end_excl);
186
187 match (left, right) {
188 (None, None) => ZeroOneTwo::Zero,
189 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
190 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
191 }
192 }
193 }
194 }
195
196 #[inline]
202 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
203 match self.intersection(other) {
204 None => {
205 if self.start <= other.start {
206 ZeroOneTwo::Two(self, other)
207 } else {
208 ZeroOneTwo::Two(other, self)
209 }
210 }
211 Some(inter) => {
212 let hull = self.convex_hull(other);
213 let left = Self::try_new(hull.start, inter.start);
214 let right = Self::try_new(inter.end_excl, hull.end_excl);
215
216 match (left, right) {
217 (None, None) => ZeroOneTwo::Zero,
218 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
219 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
220 }
221 }
222 }
223 }
224 }
225}
226
227mod interval_arithmetic {
232
233 use super::*;
234
235 impl U8CO {
236 #[inline]
237 pub const fn scale(self, factor: u8) -> Option<Self> {
238 if factor == 0 || self.end_excl > u8::MAX / factor {
239 return None;
240 }
241 Some(U8CO::new_unchecked(
242 self.start * factor,
243 self.end_excl * factor,
244 ))
245 }
246
247 #[inline]
248 pub const fn shift(self, offset: u8) -> Option<Self> {
249 if self.end_excl > u8::MAX - offset {
250 return None;
251 }
252 Some(U8CO::new_unchecked(
253 self.start + offset,
254 self.end_excl + offset,
255 ))
256 }
257 }
258}