1use crate::res::{OneTwo, ZeroOneTwo};
8
9#[cfg(test)]
10mod between_tests;
11#[cfg(test)]
12mod convex_hull_tests;
13#[cfg(test)]
14mod difference_tests;
15#[cfg(test)]
16mod intersection_tests;
17#[cfg(test)]
18mod symmetric_difference_tests;
19#[cfg(test)]
20mod transform_tests;
21#[cfg(test)]
22mod union_tests;
23
24#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
25pub struct IsizeCO {
26 start: isize,
27 end_excl: isize,
28}
29
30mod construction_accessors_predicates {
34
35 use super::*;
36
37 impl IsizeCO {
38 #[inline]
39 pub const fn try_new(start: isize, end_excl: isize) -> Option<Self> {
40 if start < end_excl {
41 Some(Self { start, end_excl })
42 } else {
43 None
44 }
45 }
46
47 #[inline]
48 pub(super) const fn new_unchecked(start: isize, end_excl: isize) -> Self {
49 debug_assert!(start < end_excl);
50 Self { start, end_excl }
51 }
52
53 #[inline]
54 pub const fn start(self) -> isize {
55 self.start
56 }
57
58 #[inline]
59 pub const fn end_excl(self) -> isize {
60 self.end_excl
61 }
62
63 #[inline]
64 pub const fn end_incl(self) -> isize {
65 self.end_excl - 1
67 }
68
69 #[inline]
70 pub const fn len(self) -> usize {
71 (self.end_excl - self.start) as usize
72 }
73
74 #[inline]
75 pub const fn contains(self, x: isize) -> bool {
76 self.start <= x && x < self.end_excl
77 }
78
79 #[inline]
80 pub const fn iter(self) -> core::ops::Range<isize> {
81 self.start..self.end_excl
82 }
83
84 #[inline]
85 pub const fn intersects(self, other: Self) -> bool {
86 !(self.end_excl <= other.start || other.end_excl <= self.start)
87 }
88
89 #[inline]
90 pub const fn is_adjacent(self, other: Self) -> bool {
91 self.end_excl == other.start || other.end_excl == self.start
92 }
93
94 #[inline]
95 pub const fn is_contiguous_with(self, other: Self) -> bool {
96 self.intersects(other) || self.is_adjacent(other)
97 }
98 }
99}
100mod interval_algebra {
105
106 use super::*;
107
108 impl IsizeCO {
109 #[inline]
113 pub const fn intersection(self, other: Self) -> Option<Self> {
114 let start = if self.start >= other.start {
115 self.start
116 } else {
117 other.start
118 };
119
120 let end_excl = if self.end_excl <= other.end_excl {
121 self.end_excl
122 } else {
123 other.end_excl
124 };
125
126 Self::try_new(start, end_excl)
127 }
128
129 #[inline]
133 pub const fn convex_hull(self, other: Self) -> Self {
134 let start = if self.start <= other.start {
135 self.start
136 } else {
137 other.start
138 };
139
140 let end_excl = if self.end_excl >= other.end_excl {
141 self.end_excl
142 } else {
143 other.end_excl
144 };
145
146 Self { start, end_excl }
147 }
148
149 #[inline]
153 pub const fn between(self, other: Self) -> Option<Self> {
154 let (left, right) = if self.start <= other.start {
155 (self, other)
156 } else {
157 (other, self)
158 };
159
160 Self::try_new(left.end_excl, right.start)
161 }
162
163 #[inline]
168 pub const fn union(self, other: Self) -> OneTwo<Self> {
169 if self.is_contiguous_with(other) {
170 OneTwo::One(self.convex_hull(other))
171 } else if self.start <= other.start {
172 OneTwo::Two(self, other)
173 } else {
174 OneTwo::Two(other, self)
175 }
176 }
177
178 #[inline]
184 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
185 match self.intersection(other) {
186 None => ZeroOneTwo::One(self),
187 Some(inter) => {
188 let left = Self::try_new(self.start, inter.start);
189 let right = Self::try_new(inter.end_excl, self.end_excl);
190
191 match (left, right) {
192 (None, None) => ZeroOneTwo::Zero,
193 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
194 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
195 }
196 }
197 }
198 }
199
200 #[inline]
206 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
207 match self.intersection(other) {
208 None => {
209 if self.start <= other.start {
210 ZeroOneTwo::Two(self, other)
211 } else {
212 ZeroOneTwo::Two(other, self)
213 }
214 }
215 Some(inter) => {
216 let hull = self.convex_hull(other);
217 let left = Self::try_new(hull.start, inter.start);
218 let right = Self::try_new(inter.end_excl, hull.end_excl);
219
220 match (left, right) {
221 (None, None) => ZeroOneTwo::Zero,
222 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
223 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
224 }
225 }
226 }
227 }
228 }
229}
230
231mod interval_arithmetic {
236 use super::IsizeCO;
237
238 impl IsizeCO {
239 #[inline]
240 pub const fn scale(self, factor: isize) -> Option<Self> {
241 if factor == 0 {
242 return None;
243 }
244
245 let s0 = match self.start.checked_mul(factor) {
246 Some(v) => v,
247 None => return None,
248 };
249
250 let s1 = match self.end_excl.checked_mul(factor) {
251 Some(v) => v,
252 None => return None,
253 };
254
255 if s0 < s1 {
256 Some(IsizeCO::new_unchecked(s0, s1))
257 } else {
258 Some(IsizeCO::new_unchecked(s1, s0))
259 }
260 }
261
262 #[inline]
263 pub const fn shift(self, offset: isize) -> Option<Self> {
264 let a = match self.start.checked_add(offset) {
265 Some(v) => v,
266 None => return None,
267 };
268
269 let b = match self.end_excl.checked_add(offset) {
270 Some(v) => v,
271 None => return None,
272 };
273
274 Some(IsizeCO::new_unchecked(a, b))
275 }
276 }
277}