#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::intervals::absolute::{
AbsBoundPair,
AbsEndBound,
AbsStartBound,
EmptiableAbsBoundPair,
HasEmptiableAbsBoundPair,
};
use crate::intervals::ops::bound_overlap_ambiguity::{
BoundOverlapAmbiguity,
BoundOverlapDisambiguationRuleSet,
DisambiguatedBoundOverlap,
};
use crate::intervals::ops::{BoundOrd, BoundOrdering};
use crate::intervals::relative::{
EmptiableRelBoundPair,
HasEmptiableRelBoundPair,
RelBoundPair,
RelEndBound,
RelStartBound,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum BoundContainmentPos {
OutsideBefore,
OutsideAfter,
Outside,
OnStart(Option<BoundOverlapAmbiguity>),
OnEnd(Option<BoundOverlapAmbiguity>),
Equal,
Inside,
}
impl BoundContainmentPos {
#[must_use]
pub fn strip(self) -> DisambiguatedBoundContainmentPos {
match self {
Self::OutsideBefore => DisambiguatedBoundContainmentPos::OutsideBefore,
Self::OutsideAfter => DisambiguatedBoundContainmentPos::OutsideAfter,
Self::Outside => DisambiguatedBoundContainmentPos::Outside,
Self::OnStart(..) => DisambiguatedBoundContainmentPos::OnStart,
Self::OnEnd(..) => DisambiguatedBoundContainmentPos::OnEnd,
Self::Equal => DisambiguatedBoundContainmentPos::Equal,
Self::Inside => DisambiguatedBoundContainmentPos::Inside,
}
}
#[must_use]
pub fn disambiguate_using_rule_set(self, rule_set: BoundContainmentRuleSet) -> DisambiguatedBoundContainmentPos {
rule_set.disambiguate(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum DisambiguatedBoundContainmentPos {
OutsideBefore,
OutsideAfter,
Outside,
OnStart,
OnEnd,
Equal,
Inside,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum BoundContainmentRuleSet {
#[default]
Strict,
Lenient,
VeryLenient,
ContinuousToFuture,
ContinuousToPast,
}
impl BoundContainmentRuleSet {
#[must_use]
pub fn disambiguate(&self, bound_position: BoundContainmentPos) -> DisambiguatedBoundContainmentPos {
let disambiguation_rule_set = match self {
Self::Strict => BoundOverlapDisambiguationRuleSet::Strict,
Self::Lenient => BoundOverlapDisambiguationRuleSet::Lenient,
Self::VeryLenient => BoundOverlapDisambiguationRuleSet::VeryLenient,
Self::ContinuousToFuture => BoundOverlapDisambiguationRuleSet::ContinuousToFuture,
Self::ContinuousToPast => BoundOverlapDisambiguationRuleSet::ContinuousToPast,
};
bound_position_rule_set_disambiguation(bound_position, disambiguation_rule_set)
}
}
#[must_use]
pub fn bound_position_rule_set_disambiguation(
bound_position: BoundContainmentPos,
bound_overlap_disambiguation_rule_set: BoundOverlapDisambiguationRuleSet,
) -> DisambiguatedBoundContainmentPos {
match bound_position {
BoundContainmentPos::Outside => DisambiguatedBoundContainmentPos::Outside,
BoundContainmentPos::OutsideBefore => DisambiguatedBoundContainmentPos::OutsideBefore,
BoundContainmentPos::OutsideAfter => DisambiguatedBoundContainmentPos::OutsideAfter,
BoundContainmentPos::OnStart(None) => DisambiguatedBoundContainmentPos::OnStart,
BoundContainmentPos::OnStart(Some(ambiguity)) => {
match ambiguity.disambiguate(bound_overlap_disambiguation_rule_set) {
DisambiguatedBoundOverlap::Before => DisambiguatedBoundContainmentPos::OutsideBefore,
DisambiguatedBoundOverlap::Equal => DisambiguatedBoundContainmentPos::OnStart,
DisambiguatedBoundOverlap::After => DisambiguatedBoundContainmentPos::Inside,
}
},
BoundContainmentPos::OnEnd(None) => DisambiguatedBoundContainmentPos::OnEnd,
BoundContainmentPos::OnEnd(Some(ambiguity)) => {
match ambiguity.disambiguate(bound_overlap_disambiguation_rule_set) {
DisambiguatedBoundOverlap::Before => DisambiguatedBoundContainmentPos::Inside,
DisambiguatedBoundOverlap::Equal => DisambiguatedBoundContainmentPos::OnEnd,
DisambiguatedBoundOverlap::After => DisambiguatedBoundContainmentPos::OutsideAfter,
}
},
BoundContainmentPos::Equal => DisambiguatedBoundContainmentPos::Equal,
BoundContainmentPos::Inside => DisambiguatedBoundContainmentPos::Inside,
}
}
pub const DEFAULT_BOUND_CONTAINMENT_RULES: [BoundContainmentRule; 1] = [BoundContainmentRule::AllowOnBounds];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum BoundContainmentRule {
AllowOnStart,
AllowOnEnd,
AllowOnBounds,
DenyOnStart,
DenyOnEnd,
DenyOnBounds,
}
impl BoundContainmentRule {
#[must_use]
pub fn counts_as_contained(&self, running: bool, disambiguated_pos: DisambiguatedBoundContainmentPos) -> bool {
match self {
Self::AllowOnStart => allow_on_start_containment_rule_counts_as_contained(running, disambiguated_pos),
Self::AllowOnEnd => allow_on_end_containment_rule_counts_as_contained(running, disambiguated_pos),
Self::AllowOnBounds => allow_on_bounds_containment_rule_counts_as_contained(running, disambiguated_pos),
Self::DenyOnStart => deny_on_start_containment_rule_counts_as_contained(running, disambiguated_pos),
Self::DenyOnEnd => deny_on_end_containment_rule_counts_as_contained(running, disambiguated_pos),
Self::DenyOnBounds => deny_on_bounds_containment_rule_counts_as_contained(running, disambiguated_pos),
}
}
}
#[must_use]
pub fn check_bound_containment_rules<'a, RI>(disambiguated_pos: DisambiguatedBoundContainmentPos, rules: RI) -> bool
where
RI: IntoIterator<Item = &'a BoundContainmentRule>,
{
let common = matches!(
disambiguated_pos,
DisambiguatedBoundContainmentPos::Equal | DisambiguatedBoundContainmentPos::Inside,
);
rules.into_iter().fold(common, |is_contained, rule| {
rule.counts_as_contained(is_contained, disambiguated_pos)
})
}
#[must_use]
pub fn allow_on_start_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running || matches!(disambiguated_pos, DisambiguatedBoundContainmentPos::OnStart)
}
#[must_use]
pub fn allow_on_end_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running || matches!(disambiguated_pos, DisambiguatedBoundContainmentPos::OnEnd)
}
#[must_use]
pub fn allow_on_bounds_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running
|| matches!(
disambiguated_pos,
DisambiguatedBoundContainmentPos::OnStart | DisambiguatedBoundContainmentPos::OnEnd,
)
}
#[must_use]
pub fn deny_on_start_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running && !matches!(disambiguated_pos, DisambiguatedBoundContainmentPos::OnStart)
}
#[must_use]
pub fn deny_on_end_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running && !matches!(disambiguated_pos, DisambiguatedBoundContainmentPos::OnEnd)
}
#[must_use]
pub fn deny_on_bounds_containment_rule_counts_as_contained(
running: bool,
disambiguated_pos: DisambiguatedBoundContainmentPos,
) -> bool {
running
&& !matches!(
disambiguated_pos,
DisambiguatedBoundContainmentPos::OnStart | DisambiguatedBoundContainmentPos::OnEnd,
)
}
pub trait CanPositionBoundContainment<B> {
#[must_use]
fn bound_position(&self, bound: &B) -> BoundContainmentPos;
#[must_use]
fn disambiguated_bound_position(
&self,
bound: &B,
rule_set: BoundContainmentRuleSet,
) -> DisambiguatedBoundContainmentPos {
self.bound_position(bound).disambiguate_using_rule_set(rule_set)
}
#[must_use]
fn simple_contains_bound(&self, bound: &B) -> bool {
self.contains_bound(
bound,
BoundContainmentRuleSet::default(),
&DEFAULT_BOUND_CONTAINMENT_RULES,
)
}
#[must_use]
fn contains_bound<'a, RI>(&self, bound: &B, rule_set: BoundContainmentRuleSet, rules: RI) -> bool
where
RI: IntoIterator<Item = &'a BoundContainmentRule>,
{
check_bound_containment_rules(self.disambiguated_bound_position(bound, rule_set), rules)
}
#[must_use]
fn contains_bound_using<F>(&self, bound: &B, f: F) -> bool
where
F: FnOnce(BoundContainmentPos) -> bool,
{
(f)(self.bound_position(bound))
}
#[must_use]
fn contains_bound_using_simple<F>(&self, bound: &B, rule_set: BoundContainmentRuleSet, f: F) -> bool
where
F: FnOnce(DisambiguatedBoundContainmentPos) -> bool,
{
(f)(self.disambiguated_bound_position(bound, rule_set))
}
}
impl<T> CanPositionBoundContainment<AbsStartBound> for T
where
T: HasEmptiableAbsBoundPair,
{
fn bound_position(&self, bound: &AbsStartBound) -> BoundContainmentPos {
bound_position_abs_start_bound_on_emptiable_abs_bound_pair(&self.emptiable_abs_bound_pair(), bound)
}
}
#[must_use]
pub fn bound_position_abs_start_bound_on_emptiable_abs_bound_pair(
emptiable_abs_bound_pair: &EmptiableAbsBoundPair,
abs_start_bound: &AbsStartBound,
) -> BoundContainmentPos {
let EmptiableAbsBoundPair::Bound(abs_bound_pair) = emptiable_abs_bound_pair else {
return BoundContainmentPos::Outside;
};
bound_position_abs_start_bound_on_abs_bound_pair(abs_bound_pair, abs_start_bound)
}
#[must_use]
pub fn bound_position_abs_start_bound_on_abs_bound_pair(
abs_bound_pair: &AbsBoundPair,
abs_start_bound: &AbsStartBound,
) -> BoundContainmentPos {
match abs_start_bound.bound_cmp(&abs_bound_pair.start()) {
BoundOrdering::Less => BoundContainmentPos::OutsideBefore,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnStart(ambiguity),
BoundOrdering::Greater => match abs_start_bound.bound_cmp(&abs_bound_pair.end()) {
BoundOrdering::Less => BoundContainmentPos::Inside,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnEnd(ambiguity),
BoundOrdering::Greater => BoundContainmentPos::OutsideAfter,
},
}
}
impl<T> CanPositionBoundContainment<AbsEndBound> for T
where
T: HasEmptiableAbsBoundPair,
{
fn bound_position(&self, bound: &AbsEndBound) -> BoundContainmentPos {
bound_position_abs_end_bound_on_emptiable_abs_bound_pair(&self.emptiable_abs_bound_pair(), bound)
}
}
#[must_use]
pub fn bound_position_abs_end_bound_on_emptiable_abs_bound_pair(
emptiable_abs_bound_pair: &EmptiableAbsBoundPair,
abs_end_bound: &AbsEndBound,
) -> BoundContainmentPos {
let EmptiableAbsBoundPair::Bound(abs_bound_pair) = emptiable_abs_bound_pair else {
return BoundContainmentPos::Outside;
};
bound_position_abs_end_bound_on_abs_bound_pair(abs_bound_pair, abs_end_bound)
}
#[must_use]
pub fn bound_position_abs_end_bound_on_abs_bound_pair(
abs_bound_pair: &AbsBoundPair,
abs_end_bound: &AbsEndBound,
) -> BoundContainmentPos {
match abs_end_bound.bound_cmp(&abs_bound_pair.start()) {
BoundOrdering::Less => BoundContainmentPos::OutsideBefore,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnStart(ambiguity),
BoundOrdering::Greater => match abs_end_bound.bound_cmp(&abs_bound_pair.end()) {
BoundOrdering::Less => BoundContainmentPos::Inside,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnEnd(ambiguity),
BoundOrdering::Greater => BoundContainmentPos::OutsideAfter,
},
}
}
impl<T> CanPositionBoundContainment<RelStartBound> for T
where
T: HasEmptiableRelBoundPair,
{
fn bound_position(&self, bound: &RelStartBound) -> BoundContainmentPos {
bound_position_rel_start_bound_on_emptiable_rel_bound_pair(&self.emptiable_rel_bound_pair(), bound)
}
}
#[must_use]
pub fn bound_position_rel_start_bound_on_emptiable_rel_bound_pair(
emptiable_rel_bound_pair: &EmptiableRelBoundPair,
rel_start_bound: &RelStartBound,
) -> BoundContainmentPos {
let EmptiableRelBoundPair::Bound(rel_bound_pair) = emptiable_rel_bound_pair else {
return BoundContainmentPos::Outside;
};
bound_position_rel_start_bound_on_rel_bound_pair(rel_bound_pair, rel_start_bound)
}
#[must_use]
pub fn bound_position_rel_start_bound_on_rel_bound_pair(
rel_bound_pair: &RelBoundPair,
rel_start_bound: &RelStartBound,
) -> BoundContainmentPos {
match rel_start_bound.bound_cmp(&rel_bound_pair.start()) {
BoundOrdering::Less => BoundContainmentPos::OutsideBefore,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnStart(ambiguity),
BoundOrdering::Greater => match rel_start_bound.bound_cmp(&rel_bound_pair.end()) {
BoundOrdering::Less => BoundContainmentPos::Inside,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnEnd(ambiguity),
BoundOrdering::Greater => BoundContainmentPos::OutsideAfter,
},
}
}
impl<T> CanPositionBoundContainment<RelEndBound> for T
where
T: HasEmptiableRelBoundPair,
{
fn bound_position(&self, bound: &RelEndBound) -> BoundContainmentPos {
bound_position_rel_end_bound_on_emptiable_rel_bound_pair(&self.emptiable_rel_bound_pair(), bound)
}
}
#[must_use]
pub fn bound_position_rel_end_bound_on_emptiable_rel_bound_pair(
emptiable_rel_bound_pair: &EmptiableRelBoundPair,
rel_end_bound: &RelEndBound,
) -> BoundContainmentPos {
let EmptiableRelBoundPair::Bound(rel_bound_pair) = emptiable_rel_bound_pair else {
return BoundContainmentPos::Outside;
};
bound_position_rel_end_bound_on_rel_bound_pair(rel_bound_pair, rel_end_bound)
}
#[must_use]
pub fn bound_position_rel_end_bound_on_rel_bound_pair(
rel_bound_pair: &RelBoundPair,
rel_end_bound: &RelEndBound,
) -> BoundContainmentPos {
match rel_end_bound.bound_cmp(&rel_bound_pair.start()) {
BoundOrdering::Less => BoundContainmentPos::OutsideBefore,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnStart(ambiguity),
BoundOrdering::Greater => match rel_end_bound.bound_cmp(&rel_bound_pair.end()) {
BoundOrdering::Less => BoundContainmentPos::Inside,
BoundOrdering::Equal(ambiguity) => BoundContainmentPos::OnEnd(ambiguity),
BoundOrdering::Greater => BoundContainmentPos::OutsideAfter,
},
}
}