use core::fmt::Debug;
use core::hash::Hash;
use core::{cmp::Ordering, ops::RangeBounds};
#[cfg(feature = "dev")]
use arbitrary::Arbitrary;
use order_theory::{
GreatestElement, LeastElement, LowerSemilattice, PredecessorExceptForLeast,
SuccessorExceptForGreatest, UpperSemilattice,
};
use crate::prelude::*;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
pub struct Range3d<const MCL: usize, const MCC: usize, const MPL: usize, S> {
subspaces: SubspaceRange<S>,
paths: PathRange<MCL, MCC, MPL>,
times: TimeRange,
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Range3d<MCL, MCC, MPL, S> {
pub fn new<SR, PR, TR>(subspaces: SR, paths: PR, times: TR) -> Self
where
SR: Into<SubspaceRange<S>>,
PR: Into<PathRange<MCL, MCC, MPL>>,
TR: Into<TimeRange>,
{
Self {
subspaces: subspaces.into(),
paths: paths.into(),
times: times.into(),
}
}
pub fn subspaces(&self) -> &SubspaceRange<S> {
&self.subspaces
}
pub fn paths(&self) -> &PathRange<MCL, MCC, MPL> {
&self.paths
}
pub fn times(&self) -> &TimeRange {
&self.times
}
pub fn set_subspaces<SR>(&mut self, new_range: SR)
where
SR: Into<SubspaceRange<S>>,
{
self.subspaces = new_range.into();
}
pub fn set_paths<PR>(&mut self, new_range: PR)
where
PR: Into<PathRange<MCL, MCC, MPL>>,
{
self.paths = new_range.into();
}
pub fn set_times<TR>(&mut self, new_range: TR)
where
TR: Into<TimeRange>,
{
self.times = new_range.into();
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Grouping<MCL, MCC, MPL, S>
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn wdm_includes<Coord>(&self, coord: &Coord) -> bool
where
Coord: Coordinatelike<MCL, MCC, MPL, S> + ?Sized,
{
self.times().contains(&coord.wdm_timestamp())
&& self.subspaces().contains(coord.wdm_subspace_id())
&& self.paths().contains(coord.wdm_path())
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialEq<Self>
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn eq(&self, other: &Self) -> bool {
match (self.wdm_is_empty(), other.wdm_is_empty()) {
(true, true) => true,
(true, false) | (false, true) => false,
(false, false) => {
self.subspaces == other.subspaces
&& self.paths == other.paths
&& self.times == other.times
}
}
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Eq for Range3d<MCL, MCC, MPL, S> where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest
{
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialOrd<Self>
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self.wdm_is_empty(), other.wdm_is_empty()) {
(true, true) => Some(Ordering::Equal),
(true, false) => Some(Ordering::Less),
(false, true) => Some(Ordering::Greater),
(false, false) => {
match (
self.subspaces().partial_cmp(other.subspaces())?,
self.paths().partial_cmp(other.paths())?,
self.times().partial_cmp(other.times())?,
) {
(Ordering::Equal, Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
(subspaces_cmp, paths_cmp, times_cmp) => {
if subspaces_cmp.is_le() && paths_cmp.is_le() && times_cmp.is_le() {
Some(Ordering::Less)
} else if subspaces_cmp.is_ge() && paths_cmp.is_ge() && times_cmp.is_ge() {
Some(Ordering::Greater)
} else {
None
}
}
}
}
}
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> LeastElement
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn least() -> Self {
Self::new(
SubspaceRange::least(),
PathRange::least(),
TimeRange::least(),
)
}
fn is_least(&self) -> bool {
self.subspaces().is_empty() || self.paths().is_empty() || self.times().is_empty()
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> GreatestElement
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
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<const MCL: usize, const MCC: usize, const MPL: usize, S> LowerSemilattice
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn greatest_lower_bound(&self, other: &Self) -> Self {
Self::new(
self.subspaces().greatest_lower_bound(other.subspaces()),
self.paths().greatest_lower_bound(other.paths()),
self.times().greatest_lower_bound(other.times()),
)
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> UpperSemilattice
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
fn least_upper_bound(&self, other: &Self) -> Self {
if self.wdm_is_empty() {
other.clone()
} else if other.wdm_is_empty() {
self.clone()
} else {
Self::new(
self.subspaces().least_upper_bound(other.subspaces()),
self.paths().least_upper_bound(other.paths()),
self.times().least_upper_bound(other.times()),
)
}
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> From<Area<MCL, MCC, MPL, S>>
for Range3d<MCL, MCC, MPL, S>
where
S: Clone + SuccessorExceptForGreatest + LeastElement,
{
fn from(value: Area<MCL, MCC, MPL, S>) -> 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::from(value.path().clone()..succ),
None => WillowRange::from(value.path().clone()..),
};
Self::new(subspaces, paths, value.times().clone())
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Range3d<MCL, MCC, MPL, S>
where
S: Clone + SuccessorExceptForGreatest,
{
pub fn singleton<Coord>(coord: &Coord) -> Self
where
Coord: Coordinatelike<MCL, MCC, MPL, S>,
{
Self::new(
SubspaceRange::singleton(coord.wdm_subspace_id().clone()),
PathRange::singleton(coord.wdm_path().clone()),
TimeRange::singleton(coord.wdm_timestamp()),
)
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Range3d<MCL, MCC, MPL, S>
where
S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
{
pub fn full() -> Self {
Self::greatest()
}
pub fn is_full(&self) -> bool {
self.is_greatest()
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Hash for Range3d<MCL, MCC, MPL, S>
where
S: Hash + Clone + 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 {
253u8.hash(state);
self.subspaces.hash(state);
self.paths.hash(state);
self.times.hash(state);
}
}
}