use core::cmp::Ordering;
use core::fmt::{self, Debug, Formatter};
use core::hash::Hash;
use core::ops::{
Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
#[cfg(feature = "dev")]
use arbitrary::{Arbitrary, size_hint};
use order_theory::{
GreatestElement, LeastElement, LowerSemilattice, PredecessorExceptForLeast,
SuccessorExceptForGreatest, UpperSemilattice, finite_range,
};
#[derive(Clone)]
pub enum WillowRange<T> {
Closed(Range<T>),
Open(RangeFrom<T>),
}
impl<T> RangeBounds<T> for WillowRange<T> {
fn start_bound(&self) -> Bound<&T> {
match self {
Self::Closed(inner) => inner.start_bound(),
Self::Open(inner) => inner.start_bound(),
}
}
fn end_bound(&self) -> Bound<&T> {
match self {
Self::Closed(inner) => inner.end_bound(),
Self::Open(inner) => inner.end_bound(),
}
}
}
impl<T> WillowRange<T>
where
T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
#[doc(alias = "contains_range")]
pub fn includes_range<R: RangeBounds<T>>(&self, other: &R) -> bool {
match finite_range::partial_cmp(self, other) {
None => false,
Some(ord) => ord.is_ge(),
}
}
#[doc(alias = "strictly_contains_range")]
pub fn strictly_includes_range<R: RangeBounds<T>>(&self, other: &R) -> bool {
match finite_range::partial_cmp(self, other) {
None => false,
Some(ord) => ord.is_gt(),
}
}
pub fn does_intersection_include_value<R: RangeBounds<T>>(&self, other: &R, value: &T) -> bool {
finite_range::is_in_greatest_lower_bound(value, self, other)
}
}
impl<T> WillowRange<T>
where
T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
pub fn from_bounds<R>(bounds: &R) -> Self
where
R: RangeBounds<T>,
{
let start = match bounds.start_bound() {
Bound::Unbounded => T::least(),
Bound::Included(t) => t.clone(),
Bound::Excluded(t) => t.try_predecessor().unwrap_or_else(|| T::least()),
};
match bounds.end_bound() {
Bound::Unbounded => return Self::Open(start..),
Bound::Included(t) => match t.try_successor() {
Some(succ) => return Self::Closed(start..succ),
None => return Self::Open(start..),
},
Bound::Excluded(t) => return Self::Closed(start..t.clone()),
}
}
#[doc(alias("intersect", "greatest_lower_bound", "glb", "meet"))]
pub fn intersection<R: RangeBounds<T>>(&self, other: &R) -> Self {
Self::from_bounds(&finite_range::greatest_lower_bound(self, other))
}
}
impl<T> From<Range<T>> for WillowRange<T> {
fn from(value: Range<T>) -> Self {
Self::Closed(value)
}
}
impl<T> From<RangeFrom<T>> for WillowRange<T> {
fn from(value: RangeFrom<T>) -> Self {
Self::Open(value)
}
}
impl<T> From<RangeFull> for WillowRange<T>
where
T: LeastElement,
{
fn from(_value: RangeFull) -> Self {
Self::Open(T::least()..)
}
}
impl<T> From<RangeInclusive<T>> for WillowRange<T>
where
T: SuccessorExceptForGreatest,
{
fn from(value: RangeInclusive<T>) -> Self {
let (start, end) = value.into_inner();
match end.try_successor() {
None => Self::Open(start..),
Some(succ) => Self::Closed(start..succ),
}
}
}
impl<T> From<RangeTo<T>> for WillowRange<T>
where
T: LeastElement,
{
fn from(value: RangeTo<T>) -> Self {
Self::Closed(T::least()..value.end)
}
}
impl<T> From<RangeToInclusive<T>> for WillowRange<T>
where
T: LeastElement + SuccessorExceptForGreatest,
{
fn from(value: RangeToInclusive<T>) -> Self {
match value.end.try_successor() {
None => Self::Open(T::least()..),
Some(succ) => Self::Closed(T::least()..succ),
}
}
}
impl<T: Debug> Debug for WillowRange<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Closed(r) => r.fmt(f),
Self::Open(r) => r.fmt(f),
}
}
}
impl<T> PartialEq<WillowRange<T>> for WillowRange<T>
where
T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast,
{
fn eq(&self, other: &WillowRange<T>) -> bool {
finite_range::eq(self, other)
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &WillowRange<T>) -> bool {
finite_range::ne(self, other)
}
}
impl<T: Eq> Eq for WillowRange<T> where
T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast
{
}
impl<T> PartialOrd<WillowRange<T>> for WillowRange<T>
where
T: Ord + SuccessorExceptForGreatest + PredecessorExceptForLeast,
{
fn partial_cmp(&self, other: &WillowRange<T>) -> Option<Ordering> {
finite_range::partial_cmp(self, other)
}
}
#[cfg(feature = "dev")]
impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for WillowRange<T> {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
if bool::arbitrary(u)? {
Ok(Self::Closed(T::arbitrary(u)?..T::arbitrary(u)?))
} else {
Ok(Self::Open(T::arbitrary(u)?..))
}
}
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
Ok(size_hint::or(
size_hint::and_all(&[
bool::try_size_hint(depth)?,
T::try_size_hint(depth)?,
T::try_size_hint(depth)?,
]),
size_hint::and(bool::try_size_hint(depth)?, T::try_size_hint(depth)?),
))
}
}
impl<T> WillowRange<T> {
pub fn is_closed(&self) -> bool {
matches!(self, Self::Closed(_))
}
pub fn is_open(&self) -> bool {
matches!(self, Self::Open(_))
}
pub fn start(&self) -> &T {
match self {
Self::Closed(Range { start, .. }) => start,
Self::Open(RangeFrom { start }) => start,
}
}
pub fn start_mut(&mut self) -> &mut T {
match self {
Self::Closed(Range { start, .. }) => start,
Self::Open(RangeFrom { start }) => start,
}
}
pub fn end(&self) -> Option<&T> {
match self {
Self::Closed(Range { end, .. }) => Some(end),
Self::Open(_) => None,
}
}
pub fn end_mut(&mut self) -> Option<&mut T> {
match self {
Self::Closed(Range { end, .. }) => Some(end),
Self::Open(_) => None,
}
}
pub fn map<U, F>(self, mut f: F) -> WillowRange<U>
where
F: FnMut(T) -> U,
{
match self {
Self::Closed(Range { start, end }) => (f(start)..f(end)).into(),
Self::Open(RangeFrom { start }) => (f(start)..).into(),
}
}
pub unsafe fn cast<U>(&self) -> &WillowRange<U> {
unsafe { &*(self as *const Self as *const WillowRange<U>) }
}
}
impl<T> WillowRange<T>
where
T: Ord,
{
pub fn is_empty(&self) -> bool {
match self {
Self::Closed(r) => r.start >= r.end,
Self::Open(_) => false,
}
}
}
impl<T> WillowRange<T>
where
T: SuccessorExceptForGreatest,
{
pub fn singleton(t: T) -> Self {
match t.try_successor() {
Some(succ) => Self::Closed(t..succ),
None => Self::Open(t..),
}
}
}
impl<T> WillowRange<T>
where
T: LeastElement,
{
pub fn full() -> Self {
(T::least()..).into()
}
pub fn is_full(&self) -> bool {
match self {
Self::Closed(_) => false,
Self::Open(RangeFrom { start }) => start.is_least(),
}
}
}
impl<T> LeastElement for WillowRange<T>
where
T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn least() -> Self {
(T::least()..T::least()).into()
}
}
impl<T> GreatestElement for WillowRange<T>
where
T: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn greatest() -> Self {
(..).into()
}
}
impl<T> LowerSemilattice for WillowRange<T>
where
T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn greatest_lower_bound(&self, other: &Self) -> Self {
Self::from_bounds(&finite_range::greatest_lower_bound(self, other))
}
}
impl<T> UpperSemilattice for WillowRange<T>
where
T: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn least_upper_bound(&self, other: &Self) -> Self {
Self::from_bounds(&finite_range::least_upper_bound(self, other))
}
}
impl<T> Hash for WillowRange<T>
where
T: Hash + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
if self.is_least() {
255u8.hash(state);
} else if self.is_greatest() {
254u8.hash(state);
} else {
match self {
Self::Closed(inner) => {
253u8.hash(state);
inner.hash(state)
}
Self::Open(inner) => {
253u8.hash(state);
inner.hash(state)
}
}
}
}
}