1use core::{
2 cmp::Ordering,
3 ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
4};
5
6pub use into_iter::GenericIterator;
7
8use crate::Domain;
9
10#[cfg(feature = "arbitrary")]
12mod arbitrary;
13mod contains;
15mod difference;
17mod empty;
20mod format;
22mod hash;
24mod intersect;
26mod into_iter;
28pub(crate) mod invert;
30#[cfg(any(feature = "proptest", test))]
32mod proptest;
33pub(crate) mod relation;
35mod singleton;
37mod symmetric_difference;
39mod union;
41
42#[derive(Copy, Clone, Debug)]
56pub struct GenericRange<T> {
57 pub(crate) start: Bound<T>,
59 pub(crate) end: Bound<T>,
61}
62
63impl<T: Domain> GenericRange<T> {
65 #[must_use]
73 pub fn full() -> Self {
74 Self {
75 start: T::minimum(),
76 end: T::maximum(),
77 }
78 }
79
80 #[must_use]
82 pub fn is_full(&self) -> bool {
83 *self == Self::full()
84 }
85
86 #[must_use]
88 pub const fn is_left_open(&self) -> bool {
89 match self.start {
90 Bound::Excluded(_) => true,
91 Bound::Included(_) | Bound::Unbounded => false,
92 }
93 }
94
95 #[must_use]
97 pub const fn is_left_closed(&self) -> bool {
98 match self.start {
99 Bound::Included(_) => true,
100 Bound::Excluded(_) | Bound::Unbounded => false,
101 }
102 }
103
104 #[must_use]
106 pub const fn is_left_unbounded(&self) -> bool {
107 match self.start {
108 Bound::Unbounded => true,
109 Bound::Excluded(_) | Bound::Included(_) => false,
110 }
111 }
112
113 #[must_use]
115 pub const fn is_right_open(&self) -> bool {
116 match self.end {
117 Bound::Excluded(_) => true,
118 Bound::Included(_) | Bound::Unbounded => false,
119 }
120 }
121
122 #[must_use]
124 pub const fn is_right_closed(&self) -> bool {
125 match self.end {
126 Bound::Included(_) => true,
127 Bound::Excluded(_) | Bound::Unbounded => false,
128 }
129 }
130
131 #[must_use]
133 pub const fn is_right_unbounded(&self) -> bool {
134 match self.end {
135 Bound::Unbounded => true,
136 Bound::Excluded(_) | Bound::Included(_) => false,
137 }
138 }
139
140 #[must_use]
144 pub fn new_greater_than(start: T) -> Self {
145 Self::new_left_open_right_unbounded(start)
146 }
147
148 #[must_use]
157 pub fn new_left_open_right_unbounded(start: T) -> Self {
158 Self::new_with_bounds(Bound::Excluded(start), Bound::Unbounded)
159 }
160
161 #[must_use]
163 pub const fn is_left_open_right_unbounded(&self) -> bool {
164 self.is_left_open() && self.is_right_unbounded()
165 }
166
167 #[must_use]
171 pub fn new_at_least(start: T) -> Self {
172 Self::new_left_closed_right_unbounded(start)
173 }
174
175 #[must_use]
184 pub fn new_left_closed_right_unbounded(start: T) -> Self {
185 Self::new_with_bounds(Bound::Included(start), Bound::Unbounded)
186 }
187
188 #[must_use]
190 pub const fn is_left_closed_right_unbounded(&self) -> bool {
191 self.is_left_closed() && self.is_right_unbounded()
192 }
193
194 #[must_use]
198 pub fn new_less_than(end: T) -> Self {
199 Self::new_left_unbounded_right_open(end)
200 }
201
202 #[must_use]
211 pub fn new_left_unbounded_right_open(end: T) -> Self {
212 Self::new_with_bounds(Bound::Unbounded, Bound::Excluded(end))
213 }
214
215 #[must_use]
217 pub const fn is_left_unbounded_right_open(&self) -> bool {
218 self.is_left_unbounded() && self.is_right_open()
219 }
220
221 #[must_use]
225 pub fn new_at_most(end: T) -> Self {
226 Self::new_left_unbounded_right_closed(end)
227 }
228
229 #[must_use]
238 pub fn new_left_unbounded_right_closed(end: T) -> Self {
239 Self::new_with_bounds(Bound::Unbounded, Bound::Included(end))
240 }
241
242 #[must_use]
244 pub const fn is_left_unbounded_right_closed(&self) -> bool {
245 self.is_left_unbounded() && self.is_right_closed()
246 }
247
248 #[must_use]
252 pub fn new_open(start: T, end: T) -> Self {
253 Self::new_left_open_right_open(start, end)
254 }
255
256 #[must_use]
265 pub fn new_left_open_right_open(start: T, end: T) -> Self {
266 Self::new_with_bounds(Bound::Excluded(start), Bound::Excluded(end))
267 }
268
269 #[must_use]
271 pub const fn is_left_open_right_open(&self) -> bool {
272 self.is_left_open() && self.is_right_open()
273 }
274
275 #[must_use]
279 pub fn new_closed(start: T, end: T) -> Self {
280 Self::new_left_closed_right_closed(start, end)
281 }
282
283 #[must_use]
292 pub fn new_left_closed_right_closed(start: T, end: T) -> Self {
293 Self::new_with_bounds(Bound::Included(start), Bound::Included(end))
294 }
295
296 #[must_use]
298 pub const fn is_left_closed_right_closed(&self) -> bool {
299 self.is_left_closed() && self.is_right_closed()
300 }
301
302 #[must_use]
306 pub fn new_closed_open(start: T, end: T) -> Self {
307 Self::new_left_closed_right_open(start, end)
308 }
309
310 #[must_use]
319 pub fn new_left_closed_right_open(start: T, end: T) -> Self {
320 Self::new_with_bounds(Bound::Included(start), Bound::Excluded(end))
321 }
322
323 #[must_use]
325 pub const fn is_left_closed_right_open(&self) -> bool {
326 self.is_left_closed() && self.is_right_open()
327 }
328
329 #[must_use]
333 pub fn new_open_closed(start: T, end: T) -> Self {
334 Self::new_left_open_right_closed(start, end)
335 }
336
337 #[must_use]
346 pub fn new_left_open_right_closed(start: T, end: T) -> Self {
347 Self::new_with_bounds(Bound::Excluded(start), Bound::Included(end))
348 }
349
350 #[must_use]
352 pub const fn is_left_open_right_closed(&self) -> bool {
353 self.is_left_open() && self.is_right_closed()
354 }
355
356 #[must_use]
363 #[allow(clippy::panic, clippy::shadow_reuse)]
364 pub fn new_with_bounds(start: Bound<T>, end: Bound<T>) -> Self {
365 let domain_range = Self::full();
366
367 let start = match Self::cmp_start_start(bound_owned_to_ref(&start), domain_range.start_bound()) {
368 Ordering::Less | Ordering::Equal => T::minimum(),
369 Ordering::Greater => start,
370 };
371
372 let end = match Self::cmp_end_end(bound_owned_to_ref(&end), domain_range.end_bound()) {
373 Ordering::Less | Ordering::Equal => end,
374 Ordering::Greater => T::maximum(),
375 };
376
377 match (bound_owned_to_ref(&start), bound_owned_to_ref(&end)) {
378 (Bound::Unbounded, _) | (_, Bound::Unbounded) => (),
379 (Bound::Included(start) | Bound::Excluded(start), Bound::Included(end) | Bound::Excluded(end)) => {
380 assert!(start <= end, "range start has to be less or equal to the end");
381 }
382 }
383
384 Self { start, end }
385 }
386}
387
388impl<T: Domain> RangeBounds<T> for GenericRange<T> {
389 #[must_use]
390 fn start_bound(&self) -> Bound<&T> {
391 bound_owned_to_ref(&self.start)
392 }
393
394 #[must_use]
395 fn end_bound(&self) -> Bound<&T> {
396 bound_owned_to_ref(&self.end)
397 }
398
399 fn contains<U>(&self, item: &U) -> bool
400 where
401 T: PartialOrd<U>,
402 U: ?Sized + PartialOrd<T>,
403 {
404 Self::generic_start_end_contains(&self.start, &self.end, item)
405 }
406}
407
408const fn bound_owned_to_ref<T>(bound: &Bound<T>) -> Bound<&T> {
410 match *bound {
411 Bound::Included(ref end) => Bound::Included(end),
412 Bound::Excluded(ref end) => Bound::Excluded(end),
413 Bound::Unbounded => Bound::Unbounded,
414 }
415}
416
417impl<T: Domain> PartialEq for GenericRange<T> {
418 fn eq(&self, other: &Self) -> bool {
419 self.is_equal(other)
420 }
421}
422
423impl<T: Domain> Eq for GenericRange<T> {}
424
425impl<T: Domain> From<Range<T>> for GenericRange<T> {
427 #[must_use]
428 fn from(range: Range<T>) -> Self {
429 Self::new_closed_open(range.start, range.end)
430 }
431}
432
433impl<T: Domain> From<RangeFrom<T>> for GenericRange<T> {
435 #[must_use]
436 fn from(range: RangeFrom<T>) -> Self {
437 Self::new_at_least(range.start)
438 }
439}
440
441impl<T: Domain> From<RangeFull> for GenericRange<T> {
443 #[must_use]
444 fn from(_range: RangeFull) -> Self {
445 Self::full()
446 }
447}
448
449impl<T: Domain> From<RangeInclusive<T>> for GenericRange<T> {
451 #[must_use]
452 fn from(range: RangeInclusive<T>) -> Self {
453 let (start, end) = range.into_inner();
454 Self::new_closed(start, end)
455 }
456}
457
458impl<T: Domain> From<RangeTo<T>> for GenericRange<T> {
460 #[must_use]
461 fn from(range: RangeTo<T>) -> Self {
462 Self::new_less_than(range.end)
463 }
464}
465
466impl<T: Domain> From<RangeToInclusive<T>> for GenericRange<T> {
468 #[must_use]
469 fn from(range: RangeToInclusive<T>) -> Self {
470 Self::new_at_most(range.end)
471 }
472}
473
474impl<T: Domain> From<(Bound<T>, Bound<T>)> for GenericRange<T> {
476 #[must_use]
477 fn from(range: (Bound<T>, Bound<T>)) -> Self {
478 Self::new_with_bounds(range.0, range.1)
479 }
480}
481
482#[allow(clippy::missing_inline_in_public_items, clippy::exhaustive_enums)]
484#[derive(Debug, Clone, Copy, Eq, PartialEq)]
485#[must_use = "a range operation might not always return another range and should be handled"]
486pub enum OperationResult<T: Domain> {
487 Empty,
489 Single(GenericRange<T>),
491 Double(GenericRange<T>, GenericRange<T>),
493}
494
495#[cfg(test)]
496pub(crate) mod tests {
497 use core::{
498 cmp::Ordering,
499 iter::once,
500 ops::{Bound, RangeBounds},
501 };
502
503 use proptest::prelude::*;
504
505 use crate::GenericRange;
506
507 #[test]
508 #[allow(clippy::shadow_unrelated)]
509 fn from_core_ranges() {
510 let generic = GenericRange::from(1..5);
512 assert_eq!(generic.start_bound(), Bound::Included(&1));
513 assert_eq!(generic.end_bound(), Bound::Excluded(&5));
514
515 let generic = GenericRange::from(6..=10);
517 assert_eq!(generic.start_bound(), Bound::Included(&6));
518 assert_eq!(generic.end_bound(), Bound::Included(&10));
519
520 let generic = GenericRange::from(12..);
522 assert_eq!(generic.start_bound(), Bound::Included(&12));
523 assert_eq!(generic.end_bound(), Bound::Included(&2_147_483_647));
524
525 let generic = GenericRange::from(..15);
527 assert_eq!(generic.start_bound(), Bound::Included(&-2_147_483_648));
528 assert_eq!(generic.end_bound(), Bound::Excluded(&15));
529
530 let generic = GenericRange::from(..=20);
532 assert_eq!(generic.start_bound(), Bound::Included(&-2_147_483_648));
533 assert_eq!(generic.end_bound(), Bound::Included(&20));
534
535 let generic: GenericRange<usize> = GenericRange::from(..);
537 assert_eq!(generic.start_bound(), Bound::Included(&0));
538 assert_eq!(generic.end_bound(), Bound::Included(&18_446_744_073_709_551_615));
539
540 let generic = GenericRange::from((Bound::Excluded(30), Bound::Excluded(42)));
542 assert_eq!(generic.start_bound(), Bound::Excluded(&30));
543 assert_eq!(generic.end_bound(), Bound::Excluded(&42));
544
545 let generic = GenericRange::from((Bound::Excluded(45), Bound::Included(50)));
547 assert_eq!(generic.start_bound(), Bound::Excluded(&45));
548 assert_eq!(generic.end_bound(), Bound::Included(&50));
549
550 let generic = GenericRange::from((Bound::Excluded(55), Bound::Unbounded));
552 assert_eq!(generic.start_bound(), Bound::Excluded(&55));
553 assert_eq!(generic.end_bound(), Bound::Included(&2_147_483_647));
554 }
555
556 #[allow(clippy::single_call_fn, clippy::absolute_paths)]
557 pub(crate) fn exhaustive_discrete_range() -> impl Iterator<Item = GenericRange<Ordering>> {
558 exhaustive_discrete_bound()
559 .flat_map(|x| core::iter::repeat(x).zip(exhaustive_discrete_bound()))
560 .filter_map(|(start, end)| match (start, end) {
561 (Bound::Unbounded, _) | (_, Bound::Unbounded) => Some(GenericRange::new_with_bounds(start, end)),
562 (Bound::Included(s) | Bound::Excluded(s), Bound::Included(e) | Bound::Excluded(e)) => {
563 (s <= e).then(|| GenericRange::new_with_bounds(start, end))
564 }
565 })
566 }
567
568 fn exhaustive_discrete_bound() -> impl Iterator<Item = Bound<Ordering>> {
569 let unbound_iter = once(Bound::Unbounded);
570 let included_iter = [Ordering::Less, Ordering::Equal, Ordering::Greater]
571 .iter()
572 .copied()
573 .map(Bound::Included);
574 let excluded_iter = [Ordering::Less, Ordering::Equal, Ordering::Greater]
575 .iter()
576 .copied()
577 .map(Bound::Excluded);
578
579 unbound_iter.chain(included_iter).chain(excluded_iter)
580 }
581
582 #[test]
583 fn check_exhaustive_bound() {
584 assert_eq!(exhaustive_discrete_bound().count(), 7);
585 }
586
587 #[test]
588 fn check_exhaustive_range() {
589 assert_eq!(exhaustive_discrete_range().count(), 37);
590 }
591
592 prop_compose! {
593 pub fn random_discrete_range()((start, end) in any::<usize>().prop_flat_map(|end| (0..=end, Just(end))), range_type in 0..=7_usize) -> GenericRange<usize> {
594 match range_type {
595 0 => GenericRange::new_open(start, end),
596 1 => GenericRange::new_closed(start, end),
597 2 => GenericRange::new_open_closed(start, end),
598 3 => GenericRange::new_closed_open(start, end),
599 4 => GenericRange::new_greater_than(start),
600 5 => GenericRange::new_at_least(start),
601 6 => GenericRange::new_less_than(end),
602 7 => GenericRange::new_at_most(end),
603 _ => unreachable!(),
604 }
605 }
606 }
607}