use core::cmp::Ordering;
use core::fmt::{self, Debug, Formatter};
use core::hash::Hash;
use core::ops::{Bound, RangeBounds};
#[cfg(feature = "dev")]
use arbitrary::{Arbitrary, size_hint};
use order_theory::{
GreatestElement, LeastElement, SuccessorExceptForGreatest,
};
use crate::prelude::EmptyGrouping;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct ClosedRange<T> {
start: T,
end: T,
}
impl<T: Debug> Debug for ClosedRange<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.start.fmt(f)?;
write!(f, "..")?;
self.end.fmt(f)?;
Ok(())
}
}
impl<T> PartialOrd for ClosedRange<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (
self.start.partial_cmp(&other.start)?,
self.end.partial_cmp(&other.end)?,
) {
(Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
(cmp_start, cmp_end) => {
if cmp_start.is_le() && cmp_end.is_ge() {
Some(Ordering::Greater)
} else if cmp_start.is_ge() && cmp_end.is_le() {
Some(Ordering::Less)
} else {
None
}
}
}
}
}
impl<T> ClosedRange<T>
where
T: PartialOrd,
{
pub fn try_new(start: T, end: T) -> Result<Self, EmptyGrouping> {
let ret = Self { start, end };
if ret.is_empty() {
Err(EmptyGrouping)
} else {
Ok(ret)
}
}
pub fn new(start: T, end: T) -> Self {
Self::try_new(start, end).unwrap()
}
pub fn includes_value(&self, t: &T) -> bool {
self.start <= *t && self.end > *t
}
pub fn includes_value_in_intersection(&self, other: &Self, t: &T) -> bool {
self.includes_value(t) && other.includes_value(t)
}
pub fn includes_closed_range(&self, other: &Self) -> bool {
self.start <= other.start && self.end >= other.end
}
pub fn strictly_includes_closed_range(&self, other: &Self) -> bool {
self != other && self.includes_closed_range(other)
}
fn is_empty(&self) -> bool {
self.start >= self.end
}
}
impl<T> ClosedRange<T>
where
T: PartialOrd + Clone,
{
pub fn intersection_closed_range(&self, other: &Self) -> Result<Self, EmptyGrouping> {
let start = match self.start.partial_cmp(other.start()) {
None => return Err(EmptyGrouping),
Some(Ordering::Less) => other.start().clone(),
Some(_) => self.start().clone(),
};
let end = match self.end.partial_cmp(other.end()) {
None => return Err(EmptyGrouping),
Some(Ordering::Greater) => other.end().clone(),
Some(_) => self.end().clone(),
};
Self::try_new(start, end)
}
}
impl<T> ClosedRange<T> {
pub unsafe fn new_unchecked(start: T, end: T) -> Self {
Self { start, end }
}
pub fn start(&self) -> &T {
&self.start
}
pub fn end(&self) -> &T {
&self.end
}
pub fn into_parts(self) -> (T, T) {
(self.start, self.end)
}
}
impl<T> RangeBounds<T> for ClosedRange<T> {
fn start_bound(&self) -> Bound<&T> {
Bound::Included(&self.start)
}
fn end_bound(&self) -> Bound<&T> {
Bound::Excluded(&self.end)
}
}
#[cfg(feature = "dev")]
impl<'a, T> Arbitrary<'a> for ClosedRange<T>
where
T: Arbitrary<'a> + PartialOrd,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let start = T::arbitrary(u)?;
let end = T::arbitrary(u)?;
Self::try_new(start, end).map_err(|_| arbitrary::Error::IncorrectFormat)
}
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
Ok(size_hint::and_all(&[
T::try_size_hint(depth)?,
T::try_size_hint(depth)?,
]))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum WillowRange<T> {
Closed(ClosedRange<T>),
Open(T),
}
impl<T: Debug> Debug for WillowRange<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Closed(closed) => closed.fmt(f),
Self::Open(start) => {
start.fmt(f)?;
write!(f, "..")?;
Ok(())
}
}
}
}
impl<T> RangeBounds<T> for WillowRange<T> {
fn start_bound(&self) -> Bound<&T> {
match self {
Self::Closed(inner) => inner.start_bound(),
Self::Open(start) => Bound::Included(start),
}
}
fn end_bound(&self) -> Bound<&T> {
match self {
Self::Closed(inner) => inner.end_bound(),
Self::Open(_) => Bound::Unbounded,
}
}
}
#[cfg(feature = "dev")]
impl<'a, T> Arbitrary<'a> for WillowRange<T>
where
T: Arbitrary<'a> + PartialOrd,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
if bool::arbitrary(u)? {
Ok(Self::Closed(ClosedRange::<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)?,
ClosedRange::<T>::try_size_hint(depth)?,
]),
size_hint::and(bool::try_size_hint(depth)?, T::try_size_hint(depth)?),
))
}
}
impl<T> WillowRange<T> {
pub fn new_open(start: T) -> Self {
Self::Open(start)
}
pub fn start(&self) -> &T {
match self {
Self::Closed(inner) => inner.start(),
Self::Open(start) => start,
}
}
pub fn end(&self) -> Option<&T> {
match self {
Self::Closed(inner) => Some(inner.end()),
Self::Open(_) => None,
}
}
pub fn into_parts(self) -> (T, Option<T>) {
match self {
Self::Closed(inner) => (inner.start, Some(inner.end)),
Self::Open(start) => (start, None),
}
}
pub fn is_closed(&self) -> bool {
matches!(self, Self::Closed(_))
}
pub fn is_open(&self) -> bool {
matches!(self, Self::Open(_))
}
pub fn try_map<U, F>(self, mut f: F) -> Result<WillowRange<U>, EmptyGrouping>
where
F: FnMut(T) -> U,
U: PartialOrd,
{
match self {
Self::Closed(ClosedRange { start, end }) => {
Ok(WillowRange::Closed(ClosedRange::try_new(f(start), f(end))?))
}
Self::Open(start) => Ok(WillowRange::Open(f(start))),
}
}
pub fn map<U, F>(self, mut f: F) -> WillowRange<U>
where
F: FnMut(T) -> U,
U: PartialOrd,
{
match self {
Self::Closed(ClosedRange { start, end }) => {
WillowRange::Closed(ClosedRange::new(f(start), f(end)))
}
Self::Open(start) => WillowRange::Open(f(start)),
}
}
pub unsafe fn map_unchecked<U, F>(self, mut f: F) -> WillowRange<U>
where
F: FnMut(T) -> U,
U: PartialOrd,
{
match self {
Self::Closed(ClosedRange { start, end }) => {
WillowRange::Closed(unsafe { ClosedRange::new_unchecked(f(start), f(end)) })
}
Self::Open(start) => WillowRange::Open(f(start)),
}
}
pub unsafe fn cast<U>(&self) -> &WillowRange<U> {
unsafe { &*(self as *const Self as *const WillowRange<U>) }
}
pub unsafe fn new_unchecked(start: T, end: Option<T>) -> Self {
match end {
Some(end) => Self::Closed(unsafe { ClosedRange::new_unchecked(start, end) }),
None => Self::new_open(start),
}
}
pub unsafe fn new_closed_unchecked(start: T, end: T) -> Self {
Self::Closed(unsafe { ClosedRange::new_unchecked(start, end) })
}
}
impl<T> WillowRange<T>
where
T: PartialOrd,
{
pub fn try_new(start: T, end: Option<T>) -> Result<Self, EmptyGrouping> {
let ret = match end {
Some(end) => Self::Closed(ClosedRange { start, end }),
None => Self::Open(start),
};
if ret.is_empty() {
Err(EmptyGrouping)
} else {
Ok(ret)
}
}
pub fn new(start: T, end: Option<T>) -> Self {
Self::try_new(start, end).unwrap()
}
pub fn try_new_closed(start: T, end: T) -> Result<Self, EmptyGrouping> {
Ok(Self::Closed(ClosedRange::try_new(start, end)?))
}
pub fn new_closed(start: T, end: T) -> Self {
Self::Closed(ClosedRange::new(start, end))
}
pub fn includes_value(&self, t: &T) -> bool {
self.start() <= t && self.end().map(|end| end > t).unwrap_or(true)
}
pub fn includes_value_in_intersection(&self, other: &Self, t: &T) -> bool {
self.includes_value(t) && other.includes_value(t)
}
pub fn includes_willow_range(&self, other: &Self) -> bool {
self.start() <= other.start()
&& match (self.end(), other.end()) {
(None, None) => true,
(Some(_), None) => false,
(None, Some(_)) => true,
(Some(self_end), Some(other_end)) => self_end >= other_end,
}
}
pub fn strictly_includes_willow_range(&self, other: &Self) -> bool {
self != other && self.includes_willow_range(other)
}
fn is_empty(&self) -> bool {
match self {
WillowRange::Closed(inner) => inner.is_empty(),
WillowRange::Open(_) => false,
}
}
}
impl<T> WillowRange<T>
where
T: PartialOrd + Clone,
{
pub fn intersection_willow_range(&self, other: &Self) -> Result<Self, EmptyGrouping> {
let start = match self.start().partial_cmp(other.start()) {
None => return Err(EmptyGrouping),
Some(Ordering::Less) => other.start().clone(),
Some(_) => self.start().clone(),
};
let end = match (self.end(), other.end()) {
(None, None) => None,
(Some(start), None) | (None, Some(start)) => Some(start.clone()),
(Some(self_end), Some(other_end)) => match self_end.partial_cmp(other_end) {
None => return Err(EmptyGrouping),
Some(Ordering::Greater) => Some(other_end.clone()),
_ => Some(self_end.clone()),
},
};
Self::try_new(start, end)
}
}
impl<T> PartialOrd for WillowRange<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &WillowRange<T>) -> Option<Ordering> {
match (self, other) {
(Self::Closed(self_closed), Self::Closed(other_closed)) => {
self_closed.partial_cmp(other_closed)
}
(Self::Closed(self_closed), Self::Open(other_open)) => {
match self_closed.start().partial_cmp(other_open)? {
Ordering::Less => None,
Ordering::Equal | Ordering::Greater => Some(Ordering::Less),
}
}
(Self::Open(self_open), Self::Closed(other_closed)) => {
match self_open.partial_cmp(other_closed.start())? {
Ordering::Greater => None,
Ordering::Equal | Ordering::Less => Some(Ordering::Greater),
}
}
(Self::Open(self_open), Self::Open(other_open)) => {
match self_open.partial_cmp(other_open)? {
Ordering::Less => Some(Ordering::Greater),
Ordering::Equal => Some(Ordering::Equal),
Ordering::Greater => Some(Ordering::Less),
}
}
}
}
}
impl<T> WillowRange<T>
where
T: SuccessorExceptForGreatest,
{
pub fn singleton(t: T) -> Self {
match t.try_successor() {
Some(succ) => Self::Closed(ClosedRange::new(t, succ)),
None => Self::Open(t),
}
}
}
impl<T> WillowRange<T>
where
T: LeastElement,
{
pub fn full() -> Self {
Self::Open(T::least())
}
pub fn is_full(&self) -> bool {
match self {
Self::Closed(_) => false,
Self::Open(start) => start.is_least(),
}
}
}
impl<T> GreatestElement for WillowRange<T>
where
T: LeastElement,
{
fn greatest() -> Self {
Self::Open(T::least())
}
}