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 union_tests;
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
17pub struct U8CO {
18 start: u8,
19 end_excl: u8,
20}
21
22impl U8CO {
27 #[inline]
28 pub const fn try_new(start: u8, end_excl: u8) -> Option<Self> {
29 if start < end_excl {
30 Some(Self { start, end_excl })
31 } else {
32 None
33 }
34 }
35
36 #[inline]
37 pub const fn new_unchecked(start: u8, end_excl: u8) -> Self {
38 debug_assert!(start < end_excl);
39 Self { start, end_excl }
40 }
41
42 #[inline]
43 pub const fn start(self) -> u8 {
44 self.start
45 }
46
47 #[inline]
48 pub const fn end_excl(self) -> u8 {
49 self.end_excl
50 }
51
52 #[inline]
53 pub const fn len(self) -> u8 {
54 self.end_excl - self.start
55 }
56
57 #[inline]
58 pub const fn contains(self, x: u8) -> bool {
59 self.start <= x && x < self.end_excl
60 }
61
62 #[inline]
63 pub const fn iter(self) -> core::ops::Range<u8> {
64 self.start..self.end_excl
65 }
66
67 #[inline]
68 pub const fn intersects(self, other: Self) -> bool {
69 !(self.end_excl <= other.start || other.end_excl <= self.start)
70 }
71
72 #[inline]
73 pub const fn is_adjacent(self, other: Self) -> bool {
74 self.end_excl == other.start || other.end_excl == self.start
75 }
76
77 #[inline]
78 pub const fn is_contiguous_with(self, other: Self) -> bool {
79 self.intersects(other) || self.is_adjacent(other)
80 }
81}
82
83impl U8CO {
88 #[inline]
89 pub const fn intersection(self, other: Self) -> Option<Self> {
90 let start = if self.start >= other.start {
91 self.start
92 } else {
93 other.start
94 };
95
96 let end_excl = if self.end_excl <= other.end_excl {
97 self.end_excl
98 } else {
99 other.end_excl
100 };
101
102 Self::try_new(start, end_excl)
103 }
104}
105
106impl U8CO {
107 #[inline]
108 pub const fn convex_hull(self, other: Self) -> Self {
109 let start = if self.start <= other.start {
110 self.start
111 } else {
112 other.start
113 };
114
115 let end_excl = if self.end_excl >= other.end_excl {
116 self.end_excl
117 } else {
118 other.end_excl
119 };
120
121 Self { start, end_excl }
122 }
123}
124
125impl U8CO {
126 #[inline]
127 pub const fn between(self, other: Self) -> Option<Self> {
128 let (left, right) = if self.start <= other.start {
129 (self, other)
130 } else {
131 (other, self)
132 };
133
134 Self::try_new(left.end_excl, right.start)
135 }
136}
137
138impl U8CO {
139 #[inline]
140 pub const fn union(self, other: Self) -> OneTwo<Self> {
141 if self.is_contiguous_with(other) {
142 OneTwo::One(self.convex_hull(other))
143 } else if self.start <= other.start {
144 OneTwo::Two(self, other)
145 } else {
146 OneTwo::Two(other, self)
147 }
148 }
149}
150
151impl U8CO {
152 #[inline]
153 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
154 match self.intersection(other) {
155 None => ZeroOneTwo::One(self),
156 Some(inter) => {
157 let left = Self::try_new(self.start, inter.start);
158 let right = Self::try_new(inter.end_excl, self.end_excl);
159
160 match (left, right) {
161 (None, None) => ZeroOneTwo::Zero,
162 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
163 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
164 }
165 }
166 }
167 }
168}
169
170impl U8CO {
171 #[inline]
172 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
173 match self.intersection(other) {
174 None => {
175 if self.start <= other.start {
176 ZeroOneTwo::Two(self, other)
177 } else {
178 ZeroOneTwo::Two(other, self)
179 }
180 }
181 Some(inter) => {
182 let hull = self.convex_hull(other);
183 let left = Self::try_new(hull.start, inter.start);
184 let right = Self::try_new(inter.end_excl, hull.end_excl);
185
186 match (left, right) {
187 (None, None) => ZeroOneTwo::Zero,
188 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
189 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
190 }
191 }
192 }
193 }
194}