BoundOrd now DiscreteBoundOrd, created Stepable trait, and DiscreteBounds struct and enum

This commit is contained in:
ripytide 2023-04-20 12:29:06 +01:00
parent fc23e9b96b
commit e5739cedf7
No known key found for this signature in database
GPG Key ID: B2629F9EC7C2FE8C
7 changed files with 262 additions and 259 deletions

View File

@ -22,19 +22,14 @@ use std::ops::Bound;
use serde::{Deserialize, Serialize};
/// An newtype of [`Bound`] to implement [`Ord`].
use crate::stepable::Stepable;
/// An newtype of [`Bound`] to implement [`Ord`] on types that
/// implement [`Step`].
///
/// This type is used to circumvent [`BTreeMap`]s (and rust collections
/// in general) lack of methods for searching with custom
/// [`comparator`] functions and/or it's lack of a [`Cursor`]-like
/// API.
///
/// [`start_bound()`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html#tymethod.start_bound
/// [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
/// [`comparator`]: https://stackoverflow.com/q/34028324
/// [`Cursor`]: https://github.com/rust-lang/rfcs/issues/1778
/// [`Step`]: std::iter::Step
#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) enum BoundOrd<T> {
pub(crate) enum DiscreteBoundOrd<T> {
/// Mirror of [`Bound::Included`]
/// There is no need for different Start and End variations as the
/// Ord implementations are equivalent.
@ -49,108 +44,91 @@ pub(crate) enum BoundOrd<T> {
EndUnbounded,
}
impl<T> BoundOrd<T> {
impl<T> DiscreteBoundOrd<T> {
pub(crate) fn start(bound: Bound<T>) -> Self {
match bound {
Bound::Included(point) => BoundOrd::Included(point),
Bound::Excluded(point) => BoundOrd::StartExcluded(point),
Bound::Unbounded => BoundOrd::StartUnbounded,
Bound::Included(point) => DiscreteBoundOrd::Included(point),
Bound::Excluded(point) => DiscreteBoundOrd::StartExcluded(point),
Bound::Unbounded => DiscreteBoundOrd::StartUnbounded,
}
}
pub(crate) fn end(bound: Bound<T>) -> Self {
match bound {
Bound::Included(point) => BoundOrd::Included(point),
Bound::Excluded(point) => BoundOrd::EndExcluded(point),
Bound::Unbounded => BoundOrd::EndUnbounded,
Bound::Included(point) => DiscreteBoundOrd::Included(point),
Bound::Excluded(point) => DiscreteBoundOrd::EndExcluded(point),
Bound::Unbounded => DiscreteBoundOrd::EndUnbounded,
}
}
}
impl<T> Ord for BoundOrd<T>
impl<T> Ord for DiscreteBoundOrd<T>
where
T: Ord,
T: Ord + Stepable,
{
#[rustfmt::skip]
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(BoundOrd::Included(start1), BoundOrd::Included(start2)) => start1.cmp(start2),
(BoundOrd::Included(start1), BoundOrd::StartExcluded(start2)) => cmp_with_priority(start1, start2, true),
(BoundOrd::Included(start1), BoundOrd::EndExcluded(start2)) => cmp_with_priority(start1, start2, false),
(BoundOrd::Included(_), BoundOrd::EndUnbounded) => Ordering::Less,
(BoundOrd::Included(_), BoundOrd::StartUnbounded) => Ordering::Greater,
(DiscreteBoundOrd::Included(start1), DiscreteBoundOrd::Included(start2)) => start1.cmp(start2),
(DiscreteBoundOrd::Included(start1), DiscreteBoundOrd::StartExcluded(start2)) => start1.cmp(&start2.up().unwrap()),
(DiscreteBoundOrd::Included(start1), DiscreteBoundOrd::EndExcluded(start2)) => start1.cmp(&start2.down().unwrap()),
(DiscreteBoundOrd::Included(_), DiscreteBoundOrd::EndUnbounded) => Ordering::Less,
(DiscreteBoundOrd::Included(_), DiscreteBoundOrd::StartUnbounded) => Ordering::Greater,
(BoundOrd::StartExcluded(start1), BoundOrd::StartExcluded(start2)) => start1.cmp(start2),
(BoundOrd::StartExcluded(start1), BoundOrd::Included(start2)) => cmp_with_priority(start1, start2, false),
(BoundOrd::StartExcluded(start1), BoundOrd::EndExcluded(start2)) => cmp_with_priority(start1, start2, false),
(BoundOrd::StartExcluded(_), BoundOrd::StartUnbounded) => Ordering::Greater,
(BoundOrd::StartExcluded(_), BoundOrd::EndUnbounded) => Ordering::Less,
(DiscreteBoundOrd::StartExcluded(start1), DiscreteBoundOrd::StartExcluded(start2)) => start1.cmp(start2),
(DiscreteBoundOrd::StartExcluded(start1), DiscreteBoundOrd::Included(start2)) => start1.up().unwrap().cmp(start2),
(DiscreteBoundOrd::StartExcluded(start1), DiscreteBoundOrd::EndExcluded(start2)) => start1.up().unwrap().cmp(&start2.down().unwrap()),
(DiscreteBoundOrd::StartExcluded(_), DiscreteBoundOrd::StartUnbounded) => Ordering::Greater,
(DiscreteBoundOrd::StartExcluded(_), DiscreteBoundOrd::EndUnbounded) => Ordering::Less,
(BoundOrd::StartUnbounded, BoundOrd::Included(_)) => Ordering::Less,
(BoundOrd::StartUnbounded, BoundOrd::StartExcluded(_)) => Ordering::Less,
(BoundOrd::StartUnbounded, BoundOrd::EndExcluded(_)) => Ordering::Less,
(BoundOrd::StartUnbounded, BoundOrd::StartUnbounded) => Ordering::Equal,
(BoundOrd::StartUnbounded, BoundOrd::EndUnbounded) => Ordering::Less,
(DiscreteBoundOrd::StartUnbounded, DiscreteBoundOrd::Included(_)) => Ordering::Less,
(DiscreteBoundOrd::StartUnbounded, DiscreteBoundOrd::StartExcluded(_)) => Ordering::Less,
(DiscreteBoundOrd::StartUnbounded, DiscreteBoundOrd::EndExcluded(_)) => Ordering::Less,
(DiscreteBoundOrd::StartUnbounded, DiscreteBoundOrd::StartUnbounded) => Ordering::Equal,
(DiscreteBoundOrd::StartUnbounded, DiscreteBoundOrd::EndUnbounded) => Ordering::Less,
(BoundOrd::EndExcluded(start1), BoundOrd::EndExcluded(start2)) => start1.cmp(start2),
(BoundOrd::EndExcluded(start1), BoundOrd::Included(start2)) => cmp_with_priority(start1, start2, true),
(BoundOrd::EndExcluded(start1), BoundOrd::StartExcluded(start2)) => cmp_with_priority(start1, start2, true),
(BoundOrd::EndExcluded(_), BoundOrd::StartUnbounded) => Ordering::Greater,
(BoundOrd::EndExcluded(_), BoundOrd::EndUnbounded) => Ordering::Less,
(DiscreteBoundOrd::EndExcluded(start1), DiscreteBoundOrd::EndExcluded(start2)) => start1.cmp(start2),
(DiscreteBoundOrd::EndExcluded(start1), DiscreteBoundOrd::Included(start2)) => start1.down().unwrap().cmp(&start2),
(DiscreteBoundOrd::EndExcluded(start1), DiscreteBoundOrd::StartExcluded(start2)) => start1.down().unwrap().cmp(&start2.up().unwrap()),
(DiscreteBoundOrd::EndExcluded(_), DiscreteBoundOrd::StartUnbounded) => Ordering::Greater,
(DiscreteBoundOrd::EndExcluded(_), DiscreteBoundOrd::EndUnbounded) => Ordering::Less,
(BoundOrd::EndUnbounded, BoundOrd::Included(_)) => Ordering::Greater,
(BoundOrd::EndUnbounded, BoundOrd::StartExcluded(_)) => Ordering::Greater,
(BoundOrd::EndUnbounded, BoundOrd::EndExcluded(_)) => Ordering::Greater,
(BoundOrd::EndUnbounded, BoundOrd::EndUnbounded) => Ordering::Equal,
(BoundOrd::EndUnbounded, BoundOrd::StartUnbounded) => Ordering::Greater,
(DiscreteBoundOrd::EndUnbounded, DiscreteBoundOrd::Included(_)) => Ordering::Greater,
(DiscreteBoundOrd::EndUnbounded, DiscreteBoundOrd::StartExcluded(_)) => Ordering::Greater,
(DiscreteBoundOrd::EndUnbounded, DiscreteBoundOrd::EndExcluded(_)) => Ordering::Greater,
(DiscreteBoundOrd::EndUnbounded, DiscreteBoundOrd::EndUnbounded) => Ordering::Equal,
(DiscreteBoundOrd::EndUnbounded, DiscreteBoundOrd::StartUnbounded) => Ordering::Greater,
}
}
}
impl<T> PartialOrd for BoundOrd<T>
impl<T> PartialOrd for DiscreteBoundOrd<T>
where
T: Ord,
T: Ord + Stepable,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> PartialEq for BoundOrd<T>
impl<T> PartialEq for DiscreteBoundOrd<T>
where
T: Ord,
T: Ord + Stepable,
{
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl<T> Eq for BoundOrd<T> where T: Ord {}
impl<T> Eq for DiscreteBoundOrd<T> where T: Ord + Stepable {}
/// If they are equal say the item with priority is larger
/// where false means left has priority and true means right.
fn cmp_with_priority<T>(left: &T, right: &T, priority: bool) -> Ordering
where
T: Ord,
{
let result = left.cmp(right);
match result {
Ordering::Equal => match priority {
false => Ordering::Greater,
true => Ordering::Less,
},
x => x,
}
}
impl<T> From<BoundOrd<T>> for Bound<T> {
fn from(start_bound: BoundOrd<T>) -> Bound<T> {
impl<T> From<DiscreteBoundOrd<T>> for Bound<T> {
fn from(start_bound: DiscreteBoundOrd<T>) -> Bound<T> {
match start_bound {
BoundOrd::Included(point) => Bound::Included(point),
BoundOrd::StartExcluded(point) => Bound::Excluded(point),
BoundOrd::StartUnbounded => Bound::Unbounded,
BoundOrd::EndExcluded(point) => Bound::Excluded(point),
BoundOrd::EndUnbounded => Bound::Unbounded,
DiscreteBoundOrd::Included(point) => Bound::Included(point),
DiscreteBoundOrd::StartExcluded(point) => Bound::Excluded(point),
DiscreteBoundOrd::StartUnbounded => Bound::Unbounded,
DiscreteBoundOrd::EndExcluded(point) => Bound::Excluded(point),
DiscreteBoundOrd::EndUnbounded => Bound::Unbounded,
}
}
}
@ -162,58 +140,137 @@ mod tests {
#[test]
fn mass_start_bound_partial_ord_test() {
//Included
assert!(BoundOrd::Included(2) == BoundOrd::Included(2));
assert!(BoundOrd::Included(2) <= BoundOrd::Included(2));
assert!(BoundOrd::Included(2) >= BoundOrd::Included(2));
assert!(BoundOrd::Included(0) < BoundOrd::Included(2));
assert!(BoundOrd::Included(2) > BoundOrd::Included(0));
assert!(DiscreteBoundOrd::Included(2) == DiscreteBoundOrd::Included(2));
assert!(DiscreteBoundOrd::Included(2) <= DiscreteBoundOrd::Included(2));
assert!(DiscreteBoundOrd::Included(2) >= DiscreteBoundOrd::Included(2));
assert!(DiscreteBoundOrd::Included(0) < DiscreteBoundOrd::Included(2));
assert!(DiscreteBoundOrd::Included(2) > DiscreteBoundOrd::Included(0));
assert!(BoundOrd::Included(2) < BoundOrd::StartExcluded(2));
assert!(BoundOrd::Included(0) < BoundOrd::StartExcluded(2));
assert!(BoundOrd::Included(2) > BoundOrd::StartExcluded(0));
assert!(
DiscreteBoundOrd::Included(2) < DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::Included(0) < DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::Included(2) > DiscreteBoundOrd::StartExcluded(0)
);
assert!(BoundOrd::Included(2) > BoundOrd::StartUnbounded);
assert!(
DiscreteBoundOrd::Included(2) > DiscreteBoundOrd::StartUnbounded
);
assert!(BoundOrd::Included(2) > BoundOrd::EndExcluded(2));
assert!(BoundOrd::Included(0) < BoundOrd::EndExcluded(2));
assert!(BoundOrd::Included(2) > BoundOrd::EndExcluded(0));
assert!(
DiscreteBoundOrd::Included(2) > DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::Included(0) < DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::Included(2) > DiscreteBoundOrd::EndExcluded(0)
);
assert!(BoundOrd::Included(2) < BoundOrd::EndUnbounded);
assert!(DiscreteBoundOrd::Included(2) < DiscreteBoundOrd::EndUnbounded);
//StartExcluded
assert!(BoundOrd::StartExcluded(2) == BoundOrd::StartExcluded(2));
assert!(BoundOrd::StartExcluded(2) <= BoundOrd::StartExcluded(2));
assert!(BoundOrd::StartExcluded(2) >= BoundOrd::StartExcluded(2));
assert!(BoundOrd::StartExcluded(0) < BoundOrd::StartExcluded(2));
assert!(BoundOrd::StartExcluded(2) > BoundOrd::StartExcluded(0));
assert!(
DiscreteBoundOrd::StartExcluded(2)
== DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::StartExcluded(2)
<= DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::StartExcluded(2)
>= DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::StartExcluded(0)
< DiscreteBoundOrd::StartExcluded(2)
);
assert!(
DiscreteBoundOrd::StartExcluded(2)
> DiscreteBoundOrd::StartExcluded(0)
);
assert!(BoundOrd::StartExcluded(2) > BoundOrd::StartUnbounded);
assert!(
DiscreteBoundOrd::StartExcluded(2)
> DiscreteBoundOrd::StartUnbounded
);
assert!(BoundOrd::StartExcluded(2) > BoundOrd::EndExcluded(2));
assert!(BoundOrd::StartExcluded(2) > BoundOrd::EndExcluded(0));
assert!(BoundOrd::StartExcluded(0) < BoundOrd::EndExcluded(2));
assert!(
DiscreteBoundOrd::StartExcluded(2)
> DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::StartExcluded(2)
> DiscreteBoundOrd::EndExcluded(0)
);
assert!(
DiscreteBoundOrd::StartExcluded(0)
< DiscreteBoundOrd::EndExcluded(2)
);
assert!(BoundOrd::StartExcluded(2) < BoundOrd::EndUnbounded);
assert!(
DiscreteBoundOrd::StartExcluded(2) < DiscreteBoundOrd::EndUnbounded
);
//StartUnbounded
assert!(BoundOrd::StartUnbounded::<i8> == BoundOrd::StartUnbounded);
assert!(BoundOrd::StartUnbounded::<i8> <= BoundOrd::StartUnbounded);
assert!(BoundOrd::StartUnbounded::<i8> >= BoundOrd::StartUnbounded);
assert!(
DiscreteBoundOrd::StartUnbounded::<i8>
== DiscreteBoundOrd::StartUnbounded
);
assert!(
DiscreteBoundOrd::StartUnbounded::<i8>
<= DiscreteBoundOrd::StartUnbounded
);
assert!(
DiscreteBoundOrd::StartUnbounded::<i8>
>= DiscreteBoundOrd::StartUnbounded
);
assert!(BoundOrd::StartUnbounded < BoundOrd::EndExcluded(2));
assert!(
DiscreteBoundOrd::StartUnbounded < DiscreteBoundOrd::EndExcluded(2)
);
assert!(BoundOrd::StartUnbounded::<i8> < BoundOrd::EndUnbounded);
assert!(
DiscreteBoundOrd::StartUnbounded::<i8>
< DiscreteBoundOrd::EndUnbounded
);
//EndExcluded
assert!(BoundOrd::EndExcluded(2) == BoundOrd::EndExcluded(2));
assert!(BoundOrd::EndExcluded(2) <= BoundOrd::EndExcluded(2));
assert!(BoundOrd::EndExcluded(2) >= BoundOrd::EndExcluded(2));
assert!(BoundOrd::EndExcluded(0) < BoundOrd::EndExcluded(2));
assert!(BoundOrd::EndExcluded(2) > BoundOrd::EndExcluded(0));
assert!(
DiscreteBoundOrd::EndExcluded(2)
== DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::EndExcluded(2)
<= DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::EndExcluded(2)
>= DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::EndExcluded(0) < DiscreteBoundOrd::EndExcluded(2)
);
assert!(
DiscreteBoundOrd::EndExcluded(2) > DiscreteBoundOrd::EndExcluded(0)
);
//EndUnbounded
assert!(BoundOrd::EndUnbounded::<i8> == BoundOrd::EndUnbounded);
assert!(BoundOrd::EndUnbounded::<i8> <= BoundOrd::EndUnbounded);
assert!(BoundOrd::EndUnbounded::<i8> >= BoundOrd::EndUnbounded);
assert!(
DiscreteBoundOrd::EndUnbounded::<i8>
== DiscreteBoundOrd::EndUnbounded
);
assert!(
DiscreteBoundOrd::EndUnbounded::<i8>
<= DiscreteBoundOrd::EndUnbounded
);
assert!(
DiscreteBoundOrd::EndUnbounded::<i8>
>= DiscreteBoundOrd::EndUnbounded
);
}
}

28
src/discrete_bounds.rs Normal file
View File

@ -0,0 +1,28 @@
/*
Copyright 2022 James Forster
This file is part of range_bounds_map.
range_bounds_map is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
range_bounds_map is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
*/
pub struct DiscreteBounds<I> {
start: DiscreteBound<I>,
end: DiscreteBound<I>,
}
pub enum DiscreteBound<I> {
Included(I),
Unbounded,
}

View File

@ -219,6 +219,7 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
#![feature(let_chains)]
#![feature(btree_cursors)]
#![feature(step_trait)]
#![allow(clippy::tabs_in_doc_comments)]
#![allow(clippy::needless_return)]
@ -226,6 +227,9 @@ pub(crate) mod bound_ord;
pub mod test_ranges;
pub(crate) mod utils;
pub mod stepable;
pub mod discrete_bounds;
pub mod range_bounds_map;
pub mod range_bounds_set;
pub mod try_from_bounds;

View File

@ -32,7 +32,7 @@ use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::bound_ord::BoundOrd;
use crate::bound_ord::DiscreteBoundOrd;
use crate::utils::{
cmp_range_with_bound_ord, cut_range, flip_bound, is_valid_range, overlaps,
};
@ -1669,8 +1669,8 @@ where
I: Ord,
{
|inner_range: &K, new_range: &K| {
BoundOrd::start(new_range.start())
.cmp(&BoundOrd::start(inner_range.start()))
DiscreteBoundOrd::start(new_range.start())
.cmp(&DiscreteBoundOrd::start(inner_range.start()))
}
}
fn overlapping_start_comp<I, K>(start: Bound<I>) -> impl FnMut(&K) -> Ordering
@ -1679,7 +1679,7 @@ where
K: NiceRange<I>,
{
move |inner_range: &K| {
cmp_range_with_bound_ord(*inner_range, BoundOrd::start(start))
cmp_range_with_bound_ord(*inner_range, DiscreteBoundOrd::start(start))
}
}
fn overlapping_end_comp<I, K>(end: Bound<I>) -> impl FnMut(&K) -> Ordering
@ -1688,7 +1688,7 @@ where
K: NiceRange<I>,
{
move |inner_range: &K| {
cmp_range_with_bound_ord(*inner_range, BoundOrd::end(end))
cmp_range_with_bound_ord(*inner_range, DiscreteBoundOrd::end(end))
}
}
fn touching_start_comp<I, K>(start: Bound<I>) -> impl FnMut(&K) -> Ordering
@ -1707,7 +1707,7 @@ where
}
(end, start) => {
let normal_result = BoundOrd::start(start).cmp(&BoundOrd::end(end));
let normal_result = DiscreteBoundOrd::start(start).cmp(&DiscreteBoundOrd::end(end));
//we overide any Equals to a random non-Equal since we
//don't want non-touching matches
@ -1735,7 +1735,7 @@ where
(end, _start) => {
let normal_result =
BoundOrd::end(end).cmp(&BoundOrd::start(inner_range.start()));
DiscreteBoundOrd::end(end).cmp(&DiscreteBoundOrd::start(inner_range.start()));
//we overide any Equals to a random non-Equal since we
//don't want non-touching matches
@ -1881,7 +1881,7 @@ mod tests {
use pretty_assertions::assert_eq;
use super::*;
use crate::bound_ord::BoundOrd;
use crate::bound_ord::DiscreteBoundOrd;
use crate::test_ranges::{ee, ei, ie, ii, iu, u, ue, ui, uu, AnyRange};
use crate::utils::{config, Config, CutResult};
@ -2077,8 +2077,8 @@ mod tests {
}
//make our expected_overlapping the correct order
if expected_overlapping.len() > 1 {
if BoundOrd::start(expected_overlapping[0].start())
> BoundOrd::start(expected_overlapping[1].start())
if DiscreteBoundOrd::start(expected_overlapping[0].start())
> DiscreteBoundOrd::start(expected_overlapping[1].start())
{
expected_overlapping.swap(0, 1);
}

41
src/stepable.rs Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright 2022 James Forster
This file is part of range_bounds_map.
range_bounds_map is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
range_bounds_map is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
*/
use std::iter::Step;
pub trait Stepable {
fn up(self) -> Option<Self>
where
Self: Sized;
fn down(self) -> Option<Self>
where
Self: Sized;
}
impl<T> Stepable for T
where
T: Sized + Step,
{
fn up(self) -> Option<Self> {
<T as Step>::forward_checked(self, 1)
}
fn down(self) -> Option<Self> {
<T as Step>::backward_checked(self, 1)
}
}

View File

@ -1,127 +0,0 @@
/*
Copyright 2022 James Forster
This file is part of range_bounds_map.
range_bounds_map is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
range_bounds_map is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
*/
use std::ops::{
Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
RangeToInclusive,
};
use crate::TryFromBoundsError;
/// A "newtype" trait to copy [`TryFrom`].
///
/// I am forced to use this "newtype" instead of [`TryFrom`] because
/// [`Range`] and friends don't implement `TryFrom<(Bound, Bound)>`
///
/// I personally think they should since then I wouldn't have to make
/// a trait just for this. I have made a post about it here should you
/// wish to comment your view.
/// <https://internals.rust-lang.org/t/range-should-impl-tryfrom-bound-bound>
///
/// [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
/// [`Range`]: https://doc.rust-lang.org/std/ops/struct.Range.html
pub trait TryFromBounds<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError>
where
Self: Sized;
}
impl<I> TryFromBounds<I> for (Bound<I>, Bound<I>) {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
Ok((start_bound, end_bound))
}
}
impl<I> TryFromBounds<I> for Range<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Included(start), Bound::Excluded(end)) => Ok(start..end),
_ => Err(TryFromBoundsError),
}
}
}
impl<I> TryFromBounds<I> for RangeInclusive<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Included(start), Bound::Included(end)) => Ok(start..=end),
_ => Err(TryFromBoundsError),
}
}
}
impl<I> TryFromBounds<I> for RangeFrom<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Included(start), Bound::Unbounded) => Ok(start..),
_ => Err(TryFromBoundsError),
}
}
}
impl<I> TryFromBounds<I> for RangeTo<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Unbounded, Bound::Excluded(end)) => Ok(..end),
_ => Err(TryFromBoundsError),
}
}
}
impl<I> TryFromBounds<I> for RangeToInclusive<I> {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Unbounded, Bound::Included(end)) => Ok(..=end),
_ => Err(TryFromBoundsError),
}
}
}
impl<I> TryFromBounds<I> for RangeFull {
fn try_from_bounds(
start_bound: Bound<I>,
end_bound: Bound<I>,
) -> Result<Self, TryFromBoundsError> {
match (start_bound, end_bound) {
(Bound::Unbounded, Bound::Unbounded) => Ok(..),
_ => Err(TryFromBoundsError),
}
}
}

View File

@ -20,20 +20,20 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
use std::cmp::Ordering;
use std::ops::Bound;
use crate::bound_ord::BoundOrd;
use crate::bound_ord::DiscreteBoundOrd;
use crate::range_bounds_map::NiceRange;
pub(crate) fn cmp_range_with_bound_ord<A, B>(
range: A,
bound_ord: BoundOrd<B>,
bound_ord: DiscreteBoundOrd<B>,
) -> Ordering
where
A: NiceRange<B>,
B: Ord,
{
if bound_ord < BoundOrd::start(range.start()) {
if bound_ord < DiscreteBoundOrd::start(range.start()) {
Ordering::Less
} else if bound_ord > BoundOrd::end(range.end()) {
} else if bound_ord > DiscreteBoundOrd::end(range.end()) {
Ordering::Greater
} else {
Ordering::Equal
@ -56,11 +56,11 @@ where
B: NiceRange<I>,
I: Ord,
{
match BoundOrd::start(a.start()) < BoundOrd::start(b.start()) {
match DiscreteBoundOrd::start(a.start()) < DiscreteBoundOrd::start(b.start()) {
true => {
match (
contains_bound_ord(a, BoundOrd::start(b.start())),
contains_bound_ord(a, BoundOrd::end(b.end())),
contains_bound_ord(a, DiscreteBoundOrd::start(b.start())),
contains_bound_ord(a, DiscreteBoundOrd::end(b.end())),
) {
(false, false) => Config::LeftFirstNonOverlapping,
(true, false) => Config::LeftFirstPartialOverlap,
@ -70,8 +70,8 @@ where
}
false => {
match (
contains_bound_ord(b, BoundOrd::start(a.start())),
contains_bound_ord(b, BoundOrd::end(a.end())),
contains_bound_ord(b, DiscreteBoundOrd::start(a.start())),
contains_bound_ord(b, DiscreteBoundOrd::end(a.end())),
) {
(false, false) => Config::RightFirstNonOverlapping,
(true, false) => Config::RightFirstPartialOverlap,
@ -110,13 +110,13 @@ where
}
}
pub(crate) fn contains_bound_ord<I, A>(range: A, bound_ord: BoundOrd<I>) -> bool
pub(crate) fn contains_bound_ord<I, A>(range: A, bound_ord: DiscreteBoundOrd<I>) -> bool
where
A: NiceRange<I>,
I: Ord,
{
let start_bound_ord = BoundOrd::start(range.start());
let end_bound_ord = BoundOrd::end(range.end());
let start_bound_ord = DiscreteBoundOrd::start(range.start());
let end_bound_ord = DiscreteBoundOrd::end(range.end());
return bound_ord >= start_bound_ord && bound_ord <= end_bound_ord;
}