use core::cmp::Ordering;
use core::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use crate::Domain;
#[cfg(feature = "arbitrary")]
mod arbitrary;
mod contains;
mod difference;
mod empty;
mod format;
mod hash;
mod intersect;
mod into_iter;
pub(crate) mod invert;
#[cfg(any(feature = "proptest", test))]
mod proptest;
pub(crate) mod relation;
mod singleton;
mod symmetric_difference;
mod union;
#[derive(Copy, Clone, Debug)]
pub struct GenericRange<T> {
pub(crate) start: Bound<T>,
pub(crate) end: Bound<T>,
}
impl<T: Domain> GenericRange<T> {
#[must_use]
pub fn full() -> Self {
Self {
start: T::minimum(),
end: T::maximum(),
}
}
#[must_use]
pub fn is_full(&self) -> bool {
*self == Self::full()
}
#[must_use]
pub fn is_left_open(&self) -> bool {
match self.start {
Bound::Excluded(_) => true,
Bound::Included(_) | Bound::Unbounded => false,
}
}
#[must_use]
pub fn is_left_closed(&self) -> bool {
match self.start {
Bound::Included(_) => true,
Bound::Excluded(_) | Bound::Unbounded => false,
}
}
#[must_use]
pub fn is_left_unbounded(&self) -> bool {
match self.start {
Bound::Unbounded => true,
Bound::Excluded(_) | Bound::Included(_) => false,
}
}
#[must_use]
pub fn is_right_open(&self) -> bool {
match self.end {
Bound::Excluded(_) => true,
Bound::Included(_) | Bound::Unbounded => false,
}
}
#[must_use]
pub fn is_right_closed(&self) -> bool {
match self.end {
Bound::Included(_) => true,
Bound::Excluded(_) | Bound::Unbounded => false,
}
}
#[must_use]
pub fn is_right_unbounded(&self) -> bool {
match self.end {
Bound::Unbounded => true,
Bound::Excluded(_) | Bound::Included(_) => false,
}
}
#[must_use]
pub fn new_greater_than(start: T) -> Self {
Self::new_left_open_right_unbounded(start)
}
#[must_use]
pub fn new_left_open_right_unbounded(start: T) -> Self {
Self::new_with_bounds(Bound::Excluded(start), Bound::Unbounded)
}
#[must_use]
pub fn is_left_open_right_unbounded(&self) -> bool {
self.is_left_open() && self.is_right_unbounded()
}
#[must_use]
pub fn new_at_least(start: T) -> Self {
Self::new_left_closed_right_unbounded(start)
}
#[must_use]
pub fn new_left_closed_right_unbounded(start: T) -> Self {
Self::new_with_bounds(Bound::Included(start), Bound::Unbounded)
}
#[must_use]
pub fn is_left_closed_right_unbounded(&self) -> bool {
self.is_left_closed() && self.is_right_unbounded()
}
#[must_use]
pub fn new_less_than(end: T) -> Self {
Self::new_left_unbounded_right_open(end)
}
#[must_use]
pub fn new_left_unbounded_right_open(end: T) -> Self {
Self::new_with_bounds(Bound::Unbounded, Bound::Excluded(end))
}
#[must_use]
pub fn is_left_unbounded_right_open(&self) -> bool {
self.is_left_unbounded() && self.is_right_open()
}
#[must_use]
pub fn new_at_most(end: T) -> Self {
Self::new_left_unbounded_right_closed(end)
}
#[must_use]
pub fn new_left_unbounded_right_closed(end: T) -> Self {
Self::new_with_bounds(Bound::Unbounded, Bound::Included(end))
}
#[must_use]
pub fn is_left_unbounded_right_closed(&self) -> bool {
self.is_left_unbounded() && self.is_right_closed()
}
#[must_use]
pub fn new_open(start: T, end: T) -> Self {
Self::new_left_open_right_open(start, end)
}
#[must_use]
pub fn new_left_open_right_open(start: T, end: T) -> Self {
Self::new_with_bounds(Bound::Excluded(start), Bound::Excluded(end))
}
#[must_use]
pub fn is_left_open_right_open(&self) -> bool {
self.is_left_open() && self.is_right_open()
}
#[must_use]
pub fn new_closed(start: T, end: T) -> Self {
Self::new_left_closed_right_closed(start, end)
}
#[must_use]
pub fn new_left_closed_right_closed(start: T, end: T) -> Self {
Self::new_with_bounds(Bound::Included(start), Bound::Included(end))
}
#[must_use]
pub fn is_left_closed_right_closed(&self) -> bool {
self.is_left_closed() && self.is_right_closed()
}
#[must_use]
pub fn new_closed_open(start: T, end: T) -> Self {
Self::new_left_closed_right_open(start, end)
}
#[must_use]
pub fn new_left_closed_right_open(start: T, end: T) -> Self {
Self::new_with_bounds(Bound::Included(start), Bound::Excluded(end))
}
#[must_use]
pub fn is_left_closed_right_open(&self) -> bool {
self.is_left_closed() && self.is_right_open()
}
#[must_use]
pub fn new_open_closed(start: T, end: T) -> Self {
Self::new_left_open_right_closed(start, end)
}
#[must_use]
pub fn new_left_open_right_closed(start: T, end: T) -> Self {
Self::new_with_bounds(Bound::Excluded(start), Bound::Included(end))
}
#[must_use]
pub fn is_left_open_right_closed(&self) -> bool {
self.is_left_open() && self.is_right_closed()
}
#[must_use]
#[allow(clippy::panic)]
pub fn new_with_bounds(start: Bound<T>, end: Bound<T>) -> Self {
let domain_range = Self::full();
let start = match Self::cmp_start_start(bound_owned_to_ref(&start), domain_range.start_bound()) {
Ordering::Less | Ordering::Equal => T::minimum(),
Ordering::Greater => start,
};
let end = match Self::cmp_end_end(bound_owned_to_ref(&end), domain_range.end_bound()) {
Ordering::Less | Ordering::Equal => end,
Ordering::Greater => T::maximum(),
};
match (bound_owned_to_ref(&start), bound_owned_to_ref(&end)) {
(Bound::Unbounded, _) | (_, Bound::Unbounded) => (),
(Bound::Included(start), Bound::Included(end))
| (Bound::Included(start), Bound::Excluded(end))
| (Bound::Excluded(start), Bound::Included(end))
| (Bound::Excluded(start), Bound::Excluded(end)) => {
assert!(start <= end, "range start has to be less or equal to the end");
}
}
Self { start, end }
}
}
impl<T> RangeBounds<T> for GenericRange<T> {
#[must_use]
fn start_bound(&self) -> Bound<&T> {
bound_owned_to_ref(&self.start)
}
#[must_use]
fn end_bound(&self) -> Bound<&T> {
bound_owned_to_ref(&self.end)
}
}
fn bound_owned_to_ref<T>(bound: &Bound<T>) -> Bound<&T> {
match bound {
Bound::Included(ref end) => Bound::Included(end),
Bound::Excluded(ref end) => Bound::Excluded(end),
Bound::Unbounded => Bound::Unbounded,
}
}
impl<T: Domain> PartialEq for GenericRange<T> {
fn eq(&self, other: &Self) -> bool {
self.is_equal(other)
}
}
impl<T: Domain> Eq for GenericRange<T> {}
impl<T: Domain> From<Range<T>> for GenericRange<T> {
#[must_use]
fn from(range: Range<T>) -> Self {
Self::new_closed_open(range.start, range.end)
}
}
impl<T: Domain> From<RangeFrom<T>> for GenericRange<T> {
#[must_use]
fn from(range: RangeFrom<T>) -> Self {
Self::new_at_least(range.start)
}
}
impl<T: Domain> From<RangeFull> for GenericRange<T> {
#[must_use]
fn from(_range: RangeFull) -> Self {
Self::full()
}
}
impl<T: Domain> From<RangeInclusive<T>> for GenericRange<T> {
#[must_use]
fn from(range: RangeInclusive<T>) -> Self {
let (start, end) = range.into_inner();
Self::new_closed(start, end)
}
}
impl<T: Domain> From<RangeTo<T>> for GenericRange<T> {
#[must_use]
fn from(range: RangeTo<T>) -> Self {
Self::new_less_than(range.end)
}
}
impl<T: Domain> From<RangeToInclusive<T>> for GenericRange<T> {
#[must_use]
fn from(range: RangeToInclusive<T>) -> Self {
Self::new_at_most(range.end)
}
}
impl<T: Domain> From<(Bound<T>, Bound<T>)> for GenericRange<T> {
#[must_use]
fn from(range: (Bound<T>, Bound<T>)) -> Self {
Self::new_with_bounds(range.0, range.1)
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[must_use = "a range operation might not always return another range and should be handled"]
pub enum OperationResult<T: Domain> {
Empty,
Single(GenericRange<T>),
Double(GenericRange<T>, GenericRange<T>),
}
#[cfg(test)]
pub(crate) mod tests {
use core::cmp::Ordering;
use core::iter::once;
use core::ops::{Bound, RangeBounds};
use proptest::prelude::*;
use crate::GenericRange;
#[test]
fn from_core_ranges() {
let generic = GenericRange::from(1..5);
assert_eq!(generic.start_bound(), Bound::Included(&1));
assert_eq!(generic.end_bound(), Bound::Excluded(&5));
let generic = GenericRange::from(6..=10);
assert_eq!(generic.start_bound(), Bound::Included(&6));
assert_eq!(generic.end_bound(), Bound::Included(&10));
let generic = GenericRange::from(12..);
assert_eq!(generic.start_bound(), Bound::Included(&12));
assert_eq!(generic.end_bound(), Bound::Included(&2_147_483_647));
let generic = GenericRange::from(..15);
assert_eq!(generic.start_bound(), Bound::Included(&-2_147_483_648));
assert_eq!(generic.end_bound(), Bound::Excluded(&15));
let generic = GenericRange::from(..=20);
assert_eq!(generic.start_bound(), Bound::Included(&-2_147_483_648));
assert_eq!(generic.end_bound(), Bound::Included(&20));
let generic: GenericRange<usize> = GenericRange::from(..);
assert_eq!(generic.start_bound(), Bound::Included(&0));
assert_eq!(generic.end_bound(), Bound::Included(&18_446_744_073_709_551_615));
let generic = GenericRange::from((Bound::Excluded(30), Bound::Excluded(42)));
assert_eq!(generic.start_bound(), Bound::Excluded(&30));
assert_eq!(generic.end_bound(), Bound::Excluded(&42));
let generic = GenericRange::from((Bound::Excluded(45), Bound::Included(50)));
assert_eq!(generic.start_bound(), Bound::Excluded(&45));
assert_eq!(generic.end_bound(), Bound::Included(&50));
let generic = GenericRange::from((Bound::Excluded(55), Bound::Unbounded));
assert_eq!(generic.start_bound(), Bound::Excluded(&55));
assert_eq!(generic.end_bound(), Bound::Included(&2_147_483_647));
}
pub(crate) fn exhaustive_discrete_range() -> impl Iterator<Item = GenericRange<Ordering>> {
exhaustive_discrete_bound()
.flat_map(|x| core::iter::repeat(x).zip(exhaustive_discrete_bound()))
.filter_map(|(start, end)| match (start, end) {
(Bound::Unbounded, _) | (_, Bound::Unbounded) => Some(GenericRange::new_with_bounds(start, end)),
(Bound::Included(s), Bound::Included(e))
| (Bound::Included(s), Bound::Excluded(e))
| (Bound::Excluded(s), Bound::Included(e))
| (Bound::Excluded(s), Bound::Excluded(e)) => {
if s <= e {
Some(GenericRange::new_with_bounds(start, end))
} else {
None
}
}
})
}
fn exhaustive_discrete_bound() -> impl Iterator<Item = Bound<Ordering>> {
let unbound_iter = once(Bound::Unbounded);
let included_iter = [Ordering::Less, Ordering::Equal, Ordering::Greater]
.iter()
.copied()
.map(Bound::Included);
let excluded_iter = [Ordering::Less, Ordering::Equal, Ordering::Greater]
.iter()
.copied()
.map(Bound::Excluded);
unbound_iter.chain(included_iter).chain(excluded_iter)
}
#[test]
fn check_exhaustive_bound() {
assert_eq!(exhaustive_discrete_bound().count(), 7);
}
#[test]
fn check_exhaustive_range() {
assert_eq!(exhaustive_discrete_range().count(), 37);
}
prop_compose! {
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> {
match range_type {
0 => GenericRange::new_open(start, end),
1 => GenericRange::new_closed(start, end),
2 => GenericRange::new_open_closed(start, end),
3 => GenericRange::new_closed_open(start, end),
4 => GenericRange::new_greater_than(start),
5 => GenericRange::new_at_least(start),
6 => GenericRange::new_less_than(end),
7 => GenericRange::new_at_most(end),
_ => unreachable!(),
}
}
}
}