willow_data_model/groupings/willow_range.rs
1use core::cmp::Ordering;
2use core::fmt::{self, Debug, Formatter};
3use core::hash::Hash;
4use core::ops::{
5 Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
6};
7
8#[cfg(feature = "dev")]
9use arbitrary::{Arbitrary, size_hint};
10
11use order_theory::{
12 GreatestElement, LeastElement, LowerSemilattice, PredecessorExceptForLeast,
13 SuccessorExceptForGreatest, UpperSemilattice, finite_range,
14};
15
16/// A continuous subset of a type implementing [`Ord`].
17///
18/// It is either a [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) consisting of an inclusive start value and an exclusive end value, or an [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range) consisting only of an inclusive start value.
19///
20/// ```
21/// use std::ops::RangeBounds;
22/// use willow_data_model::prelude::*;
23///
24/// assert!(WillowRange::<u8>::from(4..9).contains(&6));
25/// assert!(!WillowRange::<u8>::from(4..9).contains(&12));
26///
27/// // You can create `WillowRanges` from arbitrary Rust ranges.
28/// assert!(WillowRange::<u8>::from(4..=8) == WillowRange::<u8>::from(4..9));
29/// ```
30#[derive(Clone)]
31pub enum WillowRange<T> {
32 /// A [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) with an inclusive start value and an exclusive end value.
33 Closed(Range<T>),
34 /// An [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range) with an inclusive start value.
35 Open(RangeFrom<T>),
36}
37
38impl<T> RangeBounds<T> for WillowRange<T> {
39 fn start_bound(&self) -> Bound<&T> {
40 match self {
41 Self::Closed(inner) => inner.start_bound(),
42 Self::Open(inner) => inner.start_bound(),
43 }
44 }
45
46 fn end_bound(&self) -> Bound<&T> {
47 match self {
48 Self::Closed(inner) => inner.end_bound(),
49 Self::Open(inner) => inner.end_bound(),
50 }
51 }
52}
53
54impl<T> WillowRange<T>
55where
56 T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
57{
58 /// Returns whether every possible item that is [included](core::ops::RangeBounds::contains) in `other` is also [included](core::ops::RangeBounds::contains) in `self`.
59 ///
60 /// If `other` is a [`WillowRange`], you can equivalently check `self >= other`.
61 ///
62 /// ```
63 /// use willow_data_model::prelude::*;
64 ///
65 /// assert_eq!(WillowRange::<u8>::from(4..9).includes_range(&(5..7)), true);
66 /// assert_eq!(WillowRange::<u8>::from(4..9).includes_range(&(5..11)), false);
67 /// assert_eq!(WillowRange::<u8>::from(4..9).includes_range(&(2..7)), false);
68 /// assert_eq!(WillowRange::<u8>::from(4..9).includes_range(&(4..9)), true);
69 /// ```
70 #[doc(alias = "contains_range")]
71 pub fn includes_range<R: RangeBounds<T>>(&self, other: &R) -> bool {
72 match finite_range::partial_cmp(self, other) {
73 None => false,
74 Some(ord) => ord.is_ge(),
75 }
76 }
77
78 /// Returns whether every possible item that is [included](core::ops::RangeBounds::contains) in `other` is also [included](core::ops::RangeBounds::contains) in `self` and there exists at least one item [included](core::ops::RangeBounds::contains) in `self` but not [included](core::ops::RangeBounds::contains) in `other`.
79 ///
80 /// If `other` is a [`WillowRange`], you can equivalently check `self > other`.
81 ///
82 /// ```
83 /// use willow_data_model::prelude::*;
84 ///
85 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(5..7)), true);
86 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(5..11)), false);
87 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(2..7)), false);
88 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(4..9)), false);
89 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(5..9)), true);
90 /// assert_eq!(WillowRange::<u8>::from(4..9).strictly_includes_range(&(4..8)), true);
91 /// ```
92 #[doc(alias = "strictly_contains_range")]
93 pub fn strictly_includes_range<R: RangeBounds<T>>(&self, other: &R) -> bool {
94 match finite_range::partial_cmp(self, other) {
95 None => false,
96 Some(ord) => ord.is_gt(),
97 }
98 }
99
100 /// Returns whether the [`intersection`](WillowRange::intersection) of `self` and `other` [includes](core::ops::RangeBounds::contains) the given value.
101 ///
102 /// More efficient than actually creating the intersection and then calling [`includes`](core::ops::RangeBounds::contains).
103 ///
104 /// ```
105 /// use willow_data_model::prelude::*;
106 ///
107 /// assert!(WillowRange::<u8>::from(4..9).does_intersection_include_value(&(5..7), &6));
108 /// assert!(!WillowRange::<u8>::from(4..9).does_intersection_include_value(&(5..7), &4));
109 /// assert!(!WillowRange::<u8>::from(4..9).does_intersection_include_value(&(5..7), &8));
110 /// assert!(!WillowRange::<u8>::from(4..9).does_intersection_include_value(&(5..7), &99));
111 /// ```
112 pub fn does_intersection_include_value<R: RangeBounds<T>>(&self, other: &R, value: &T) -> bool {
113 finite_range::is_in_greatest_lower_bound(value, self, other)
114 }
115}
116
117impl<T> WillowRange<T>
118where
119 T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
120{
121 /// Converts any value which implements [`RangeBounds<T>`](core::ops::RangeBounds) into a [`WillowRange<T>`](WillowRange) containing the same values.
122 ///
123 /// ```
124 /// use willow_data_model::prelude::*;
125 ///
126 /// assert_eq!(WillowRange::<u8>::from_bounds(&(..)), (..).into());
127 /// assert_eq!(WillowRange::<u8>::from_bounds(&(0..)), (0..).into());
128 /// assert_eq!(WillowRange::<u8>::from_bounds(&(..254)), (..254).into());
129 /// assert_eq!(WillowRange::<u8>::from_bounds(&(..255)), (..255).into());
130 /// assert_eq!(WillowRange::<u8>::from_bounds(&(..=254)), (..=254).into());
131 /// assert_eq!(WillowRange::<u8>::from_bounds(&(..=255)), (..=255).into());
132 /// assert_eq!(WillowRange::<u8>::from_bounds(&(4..8)), (4..8).into());
133 /// assert_eq!(WillowRange::<u8>::from_bounds(&(4..=8)), (4..=8).into());
134 /// ```
135 pub fn from_bounds<R>(bounds: &R) -> Self
136 where
137 R: RangeBounds<T>,
138 {
139 let start = match bounds.start_bound() {
140 Bound::Unbounded => T::least(),
141 Bound::Included(t) => t.clone(),
142 Bound::Excluded(t) => t.try_predecessor().unwrap_or_else(|| T::least()),
143 };
144
145 match bounds.end_bound() {
146 Bound::Unbounded => return Self::Open(start..),
147 Bound::Included(t) => match t.try_successor() {
148 Some(succ) => return Self::Closed(start..succ),
149 None => return Self::Open(start..),
150 },
151 Bound::Excluded(t) => return Self::Closed(start..t.clone()),
152 }
153 }
154
155 /// Returns the [intersection](https://willowprotocol.org/specs/grouping-entries/index.html#range_intersection) of `self` and `other`, i.e., the [`WillowRange`] which [includes](core::ops::RangeBounds::contains) exactly those values [included](core::ops::RangeBounds::contains) in both `self` and `other`.
156 ///
157 /// If you want to check whether some value is [included](core::ops::RangeBounds::contains) in the intersecion of two ranges, prefer [`WillowRange::does_intersection_include_value`] over explicitly constructing the intersection.
158 ///
159 /// ```
160 /// use willow_data_model::prelude::*;
161 ///
162 /// assert_eq!(WillowRange::<u8>::from(4..9).intersection(&(5..7)), (5..7).into());
163 /// assert_eq!(WillowRange::<u8>::from(4..9).intersection(&(5..11)), (5..9).into());
164 /// assert_eq!(WillowRange::<u8>::from(4..9).intersection(&(2..7)), (4..7).into());
165 /// assert!(WillowRange::<u8>::from(4..9).intersection(&(11..14)).is_empty());
166 /// assert!(WillowRange::<u8>::from(4..9).intersection(&(6..6)).is_empty());
167 /// ```
168 #[doc(alias("intersect", "greatest_lower_bound", "glb", "meet"))]
169 pub fn intersection<R: RangeBounds<T>>(&self, other: &R) -> Self {
170 Self::from_bounds(&finite_range::greatest_lower_bound(self, other))
171 }
172}
173
174impl<T> From<Range<T>> for WillowRange<T> {
175 fn from(value: Range<T>) -> Self {
176 Self::Closed(value)
177 }
178}
179
180impl<T> From<RangeFrom<T>> for WillowRange<T> {
181 fn from(value: RangeFrom<T>) -> Self {
182 Self::Open(value)
183 }
184}
185
186impl<T> From<RangeFull> for WillowRange<T>
187where
188 T: LeastElement,
189{
190 fn from(_value: RangeFull) -> Self {
191 Self::Open(T::least()..)
192 }
193}
194
195impl<T> From<RangeInclusive<T>> for WillowRange<T>
196where
197 T: SuccessorExceptForGreatest,
198{
199 fn from(value: RangeInclusive<T>) -> Self {
200 let (start, end) = value.into_inner();
201 match end.try_successor() {
202 None => Self::Open(start..),
203 Some(succ) => Self::Closed(start..succ),
204 }
205 }
206}
207
208impl<T> From<RangeTo<T>> for WillowRange<T>
209where
210 T: LeastElement,
211{
212 fn from(value: RangeTo<T>) -> Self {
213 Self::Closed(T::least()..value.end)
214 }
215}
216
217impl<T> From<RangeToInclusive<T>> for WillowRange<T>
218where
219 T: LeastElement + SuccessorExceptForGreatest,
220{
221 fn from(value: RangeToInclusive<T>) -> Self {
222 match value.end.try_successor() {
223 None => Self::Open(T::least()..),
224 Some(succ) => Self::Closed(T::least()..succ),
225 }
226 }
227}
228
229impl<T: Debug> Debug for WillowRange<T> {
230 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
231 match self {
232 Self::Closed(r) => r.fmt(f),
233 Self::Open(r) => r.fmt(f),
234 }
235 }
236}
237
238/// Two [`WillowRanges`](WillowRange) are equal iff they contain the same values.
239impl<T> PartialEq<WillowRange<T>> for WillowRange<T>
240where
241 T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast,
242{
243 fn eq(&self, other: &WillowRange<T>) -> bool {
244 finite_range::eq(self, other)
245 }
246
247 #[allow(clippy::partialeq_ne_impl)]
248 fn ne(&self, other: &WillowRange<T>) -> bool {
249 finite_range::ne(self, other)
250 }
251}
252
253impl<T: Eq> Eq for WillowRange<T> where
254 T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast
255{
256}
257
258/// A range is less than another iff all values contained in the first are also contained in the other.
259impl<T> PartialOrd<WillowRange<T>> for WillowRange<T>
260where
261 T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast,
262{
263 fn partial_cmp(&self, other: &WillowRange<T>) -> Option<Ordering> {
264 finite_range::partial_cmp(self, other)
265 }
266}
267
268// Have to implement this manually beause Range and RangeFrom are not Clone.
269#[cfg(feature = "dev")]
270impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for WillowRange<T> {
271 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
272 if bool::arbitrary(u)? {
273 Ok(Self::Closed(T::arbitrary(u)?..T::arbitrary(u)?))
274 } else {
275 Ok(Self::Open(T::arbitrary(u)?..))
276 }
277 }
278
279 fn try_size_hint(
280 depth: usize,
281 ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
282 Ok(size_hint::or(
283 size_hint::and_all(&[
284 bool::try_size_hint(depth)?,
285 T::try_size_hint(depth)?,
286 T::try_size_hint(depth)?,
287 ]),
288 size_hint::and(bool::try_size_hint(depth)?, T::try_size_hint(depth)?),
289 ))
290 }
291}
292
293impl<T> WillowRange<T> {
294 /// Returns whether `self` is a [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range).
295 ///
296 /// ```
297 /// use willow_data_model::prelude::*;
298 ///
299 /// assert_eq!(WillowRange::<u8>::from(3..8).is_closed(), true);
300 /// assert_eq!(WillowRange::<u8>::from(3..255).is_closed(), true);
301 /// assert_eq!(WillowRange::<u8>::from(3..=255).is_closed(), false);
302 /// assert_eq!(WillowRange::<u8>::from(3..).is_closed(), false);
303 /// ```
304 pub fn is_closed(&self) -> bool {
305 matches!(self, Self::Closed(_))
306 }
307
308 /// Returns whether `self` is an [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range).
309 ///
310 /// ```
311 /// use willow_data_model::prelude::*;
312 ///
313 /// assert_eq!(WillowRange::<u8>::from(3..8).is_open(), false);
314 /// assert_eq!(WillowRange::<u8>::from(3..255).is_open(), false);
315 /// assert_eq!(WillowRange::<u8>::from(3..=255).is_open(), true);
316 /// assert_eq!(WillowRange::<u8>::from(3..).is_open(), true);
317 /// ```
318 pub fn is_open(&self) -> bool {
319 matches!(self, Self::Open(_))
320 }
321
322 /// Returns a reference to the [start value](https://willowprotocol.org/specs/grouping-entries/index.html#start_value) of `self`.
323 ///
324 /// ```
325 /// use willow_data_model::prelude::*;
326 ///
327 /// assert_eq!(WillowRange::<u8>::from(3..8).start(), &3);
328 /// assert_eq!(WillowRange::<u8>::from(..8).start(), &0);
329 /// ```
330 pub fn start(&self) -> &T {
331 match self {
332 Self::Closed(Range { start, .. }) => start,
333 Self::Open(RangeFrom { start }) => start,
334 }
335 }
336
337 /// Returns a mutable reference to the [start value](https://willowprotocol.org/specs/grouping-entries/index.html#start_value) of `self`.
338 ///
339 /// ```
340 /// use willow_data_model::prelude::*;
341 ///
342 /// assert_eq!(WillowRange::<u8>::from(3..8).start_mut(), &mut 3);
343 /// assert_eq!(WillowRange::<u8>::from(..8).start_mut(), &mut 0);
344 /// ```
345 pub fn start_mut(&mut self) -> &mut T {
346 match self {
347 Self::Closed(Range { start, .. }) => start,
348 Self::Open(RangeFrom { start }) => start,
349 }
350 }
351
352 /// Returns a reference to the [end value](https://willowprotocol.org/specs/grouping-entries/index.html#end_value) of `self`, or `None` if the range [is open](WillowRange::is_open).
353 ///
354 /// ```
355 /// use willow_data_model::prelude::*;
356 ///
357 /// assert_eq!(WillowRange::<u8>::from(3..8).end(), Some(&8));
358 /// assert_eq!(WillowRange::<u8>::from(3..=8).end(), Some(&9));
359 /// assert_eq!(WillowRange::<u8>::from(3..).end(), None);
360 /// ```
361 pub fn end(&self) -> Option<&T> {
362 match self {
363 Self::Closed(Range { end, .. }) => Some(end),
364 Self::Open(_) => None,
365 }
366 }
367
368 /// Returns a mutable reference to the [end value](https://willowprotocol.org/specs/grouping-entries/index.html#end_value) of `self`, or `None` if the range [is open](WillowRange::is_open).
369 ///
370 /// ```
371 /// use willow_data_model::prelude::*;
372 ///
373 /// assert_eq!(WillowRange::<u8>::from(3..8).end_mut(), Some(&mut 8));
374 /// assert_eq!(WillowRange::<u8>::from(3..=8).end_mut(), Some(&mut 9));
375 /// assert_eq!(WillowRange::<u8>::from(3..).end_mut(), None);
376 /// ```
377 pub fn end_mut(&mut self) -> Option<&mut T> {
378 match self {
379 Self::Closed(Range { end, .. }) => Some(end),
380 Self::Open(_) => None,
381 }
382 }
383
384 /// Converts this range into a different range by applying a function to all endpoints.
385 ///
386 /// ```
387 /// use willow_data_model::prelude::*;
388 ///
389 /// assert_eq!(
390 /// WillowRange::<u8>::from(3..8).map(|x| (x + 1) as u16),
391 /// WillowRange::<u16>::from(4..9),
392 /// );
393 /// ```
394 pub fn map<U, F>(self, mut f: F) -> WillowRange<U>
395 where
396 F: FnMut(T) -> U,
397 {
398 match self {
399 Self::Closed(Range { start, end }) => (f(start)..f(end)).into(),
400 Self::Open(RangeFrom { start }) => (f(start)..).into(),
401 }
402 }
403
404 /// Unsafely reinterprets a reference to a range as a reference to a range of a different type of boundaries.
405 ///
406 /// # Safety
407 ///
408 /// This is safe only if `&T` and `&U` have an identical layout in memory.
409 pub unsafe fn cast<U>(&self) -> &WillowRange<U> {
410 unsafe { &*(self as *const Self as *const WillowRange<U>) }
411 }
412}
413
414impl<T> WillowRange<T>
415where
416 T: Ord,
417{
418 /// Returns whether `self` is [empty](https://willowprotocol.org/specs/grouping-entries/index.html#range_empty), i.e., whether it [includes](core::ops::RangeBounds::contains) no values.
419 ///
420 /// ```
421 /// use willow_data_model::prelude::*;
422 ///
423 /// assert_eq!(WillowRange::<u8>::from(4..5).is_empty(), false);
424 /// assert_eq!(WillowRange::<u8>::from(255..).is_empty(), false);
425 /// assert_eq!(WillowRange::<u8>::from(4..4).is_empty(), true);
426 /// assert_eq!(WillowRange::<u8>::from(4..3).is_empty(), true);
427 /// ```
428 pub fn is_empty(&self) -> bool {
429 match self {
430 Self::Closed(r) => r.start >= r.end,
431 Self::Open(_) => false,
432 }
433 }
434}
435
436impl<T> WillowRange<T>
437where
438 T: SuccessorExceptForGreatest,
439{
440 /// Returns the [`WillowRange`] which [includes](core::ops::RangeBounds::contains) `t` but no other value.
441 ///
442 /// ```
443 /// use willow_data_model::prelude::*;
444 ///
445 /// assert_eq!(WillowRange::<u8>::singleton(5), (5..6).into());
446 /// assert_eq!(WillowRange::<u8>::singleton(255), (255..).into());
447 /// ```
448 pub fn singleton(t: T) -> Self {
449 match t.try_successor() {
450 Some(succ) => Self::Closed(t..succ),
451 None => Self::Open(t..),
452 }
453 }
454}
455
456impl<T> WillowRange<T>
457where
458 T: LeastElement,
459{
460 /// Returns the `WillowRange` which [includes](core::ops::RangeBounds::contains) every value of type `T`.
461 ///
462 /// ```
463 /// use willow_data_model::prelude::*;
464 ///
465 /// assert_eq!(WillowRange::<u8>::full(), (..).into());
466 /// ```
467 pub fn full() -> Self {
468 (T::least()..).into()
469 }
470
471 /// Returns whether `self` is the full range, i.e., the range which [includes](core::ops::RangeBounds::contains) every value of type `T`.
472 ///
473 /// ```
474 /// use willow_data_model::prelude::*;
475 ///
476 /// assert_eq!(WillowRange::<u8>::full().is_full(), true);
477 /// assert_eq!(WillowRange::<u8>::from(1..).is_full(), false);
478 /// assert_eq!(WillowRange::<u8>::from(0..255).is_full(), false);
479 /// ```
480 pub fn is_full(&self) -> bool {
481 match self {
482 Self::Closed(_) => false,
483 Self::Open(RangeFrom { start }) => start.is_least(),
484 }
485 }
486}
487
488impl<T> LeastElement for WillowRange<T>
489where
490 T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
491{
492 fn least() -> Self {
493 (T::least()..T::least()).into()
494 }
495}
496
497impl<T> GreatestElement for WillowRange<T>
498where
499 T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
500{
501 fn greatest() -> Self {
502 (..).into()
503 }
504}
505
506impl<T> LowerSemilattice for WillowRange<T>
507where
508 T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
509{
510 fn greatest_lower_bound(&self, other: &Self) -> Self {
511 Self::from_bounds(&finite_range::greatest_lower_bound(self, other))
512 }
513}
514
515impl<T> UpperSemilattice for WillowRange<T>
516where
517 T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
518{
519 fn least_upper_bound(&self, other: &Self) -> Self {
520 Self::from_bounds(&finite_range::least_upper_bound(self, other))
521 }
522}
523
524impl<T> Hash for WillowRange<T>
525where
526 T: Hash + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
527{
528 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
529 if self.is_least() {
530 255u8.hash(state);
531 } else if self.is_greatest() {
532 254u8.hash(state);
533 } else {
534 match self {
535 Self::Closed(inner) => {
536 253u8.hash(state);
537 inner.hash(state)
538 }
539 Self::Open(inner) => {
540 253u8.hash(state);
541 inner.hash(state)
542 }
543 }
544 }
545 }
546}