use core::cmp::Ordering;
use core::fmt::Debug;
use core::hash::Hash;
use core::ops::RangeBounds;
#[cfg(feature = "dev")]
use arbitrary::{Arbitrary, size_hint};
use order_theory::GreatestElement;
use crate::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Range3d {
subspaces: SubspaceRange,
paths: PathRange,
times: TimeRange,
}
#[cfg(feature = "dev")]
impl<'a> Arbitrary<'a> for Range3d {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let subspaces = SubspaceRange::arbitrary(u)?;
let paths = PathRange::arbitrary(u)?;
let times = TimeRange::arbitrary(u)?;
Ok(Self {
subspaces,
paths,
times,
})
}
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
Ok(size_hint::and_all(&[
SubspaceRange::try_size_hint(depth)?,
PathRange::try_size_hint(depth)?,
TimeRange::try_size_hint(depth)?,
]))
}
}
impl Grouping for Range3d {
fn includes<Coord>(&self, coord: &Coord) -> bool
where
Coord: Coordinatelike + ?Sized,
{
self.times().includes_value(&coord.timestamp())
&& self.subspaces().includes_value(coord.subspace_id())
&& self.paths().includes_value(coord.path())
}
fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping> {
Ok(Self {
subspaces: self
.subspaces()
.intersection_willow_range(other.subspaces())?,
paths: self.paths().intersection_willow_range(other.paths())?,
times: self.times().intersection_willow_range(other.times())?,
})
}
}
impl PartialOrd<Self> for Range3d {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let cmp_subspaces = self.subspaces().partial_cmp(other.subspaces())?;
let cmp_paths = self.paths().partial_cmp(other.paths())?;
let cmp_times = self.times().partial_cmp(other.times())?;
if cmp_subspaces == Ordering::Equal
&& cmp_paths == Ordering::Equal
&& cmp_times == Ordering::Equal
{
Some(Ordering::Equal)
} else if cmp_subspaces.is_le() && cmp_paths.is_le() && cmp_times.is_le() {
Some(Ordering::Less)
} else if cmp_subspaces.is_ge() && cmp_paths.is_ge() && cmp_times.is_ge() {
Some(Ordering::Greater)
} else {
None
}
}
}
impl GreatestElement for Range3d {
fn greatest() -> Self {
Self::new(SubspaceRange::full(), PathRange::full(), TimeRange::full())
}
fn is_greatest(&self) -> bool {
self.times().is_full() && self.subspaces().is_full() && self.paths().is_full()
}
}
impl From<Area> for Range3d {
fn from(value: Area) -> Self {
let subspaces = match value.subspace() {
None => WillowRange::full(),
Some(s) => WillowRange::singleton(s.clone()),
};
let paths = match value.path().greater_but_not_prefixed() {
Some(succ) => WillowRange::new_closed(value.path().clone(), succ),
None => WillowRange::new_open(value.path().clone()),
};
Self::new(subspaces, paths, *value.times())
}
}
impl RangeBounds<SubspaceId> for Range3d {
fn start_bound(&self) -> core::ops::Bound<&SubspaceId> {
self.subspaces().start_bound()
}
fn end_bound(&self) -> core::ops::Bound<&SubspaceId> {
self.subspaces().end_bound()
}
}
impl RangeBounds<Path> for Range3d {
fn start_bound(&self) -> core::ops::Bound<&Path> {
self.paths().start_bound().map(Into::into)
}
fn end_bound(&self) -> core::ops::Bound<&Path> {
self.paths().end_bound().map(Into::into)
}
}
impl RangeBounds<Timestamp> for Range3d {
fn start_bound(&self) -> core::ops::Bound<&Timestamp> {
self.times().start_bound()
}
fn end_bound(&self) -> core::ops::Bound<&Timestamp> {
self.times().end_bound()
}
}
impl Range3d {
pub fn new<SR, PR, TR>(subspaces: SR, paths: PR, times: TR) -> Self
where
SR: Into<SubspaceRange>,
PR: Into<PathRange>,
TR: Into<TimeRange>,
{
Self {
subspaces: subspaces.into(),
paths: paths.into(),
times: times.into(),
}
}
pub fn subspaces(&self) -> &SubspaceRange {
&self.subspaces
}
pub fn paths(&self) -> &PathRange {
&self.paths
}
pub fn times(&self) -> &TimeRange {
&self.times
}
pub fn set_subspaces<SR>(&mut self, new_range: SR)
where
SR: Into<SubspaceRange>,
{
self.subspaces = new_range.into();
}
pub fn set_paths<PR>(&mut self, new_range: PR)
where
PR: Into<PathRange>,
{
self.paths = new_range.into();
}
pub fn set_times<TR>(&mut self, new_range: TR)
where
TR: Into<TimeRange>,
{
self.times = new_range.into();
}
pub fn singleton<Coord>(coord: &Coord) -> Self
where
Coord: Coordinatelike,
{
Self::new(
SubspaceRange::singleton(coord.subspace_id().clone()),
PathRange::singleton(coord.path().clone()),
TimeRange::singleton(coord.timestamp()),
)
}
pub fn full() -> Self {
Self::greatest()
}
pub fn is_full(&self) -> bool {
self.is_greatest()
}
}