2210 lines
60 KiB
Rust
2210 lines
60 KiB
Rust
/*
|
|
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 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
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
use std::cmp::Ordering;
|
|
use std::collections::BTreeMap;
|
|
use std::fmt::Debug;
|
|
use std::iter::once;
|
|
use std::ops::{Bound, RangeBounds};
|
|
|
|
use either::Either;
|
|
use itertools::Itertools;
|
|
use labels::{tested, trivial};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::bound_ord::BoundOrd;
|
|
use crate::TryFromBounds;
|
|
|
|
/// An ordered map of [`RangeBounds`] based on [`BTreeMap`]
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// // Make a map of ranges to booleans
|
|
/// let mut map = RangeBoundsMap::try_from([
|
|
/// (4..8, false),
|
|
/// (8..18, true),
|
|
/// (20..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// // Change a value in the map
|
|
/// *map.get_at_point_mut(&(7)).unwrap() = true;
|
|
///
|
|
/// // Get a value in the map
|
|
/// if map.contains_point(&99) {
|
|
/// println!("Map contains value at 99 :)");
|
|
/// }
|
|
///
|
|
/// // Iterate over the entries in the map
|
|
/// for (range, value) in map.iter() {
|
|
/// println!("{range:?}, {value:?}");
|
|
/// }
|
|
/// ```
|
|
/// Example using a custom [`RangeBounds`] type:
|
|
/// ```
|
|
/// use std::ops::{Bound, RangeBounds};
|
|
///
|
|
/// use ordered_float::NotNan;
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// // An Exlusive-Exlusive range of [`f32`]s not provided by any
|
|
/// // std::ops ranges
|
|
/// // We use [`ordered_float::NotNan`]s as the inner type must be Ord
|
|
/// // similar to a normal [`BTreeMap`]
|
|
/// #[derive(Debug, PartialEq)]
|
|
/// struct ExEx {
|
|
/// start: NotNan<f32>,
|
|
/// end: NotNan<f32>,
|
|
/// }
|
|
/// # impl ExEx {
|
|
/// # fn new(start: f32, end: f32) -> ExEx {
|
|
/// # ExEx {
|
|
/// # start: NotNan::new(start).unwrap(),
|
|
/// # end: NotNan::new(end).unwrap(),
|
|
/// # }
|
|
/// # }
|
|
/// # }
|
|
///
|
|
/// // Implement RangeBounds<f32> on our new type
|
|
/// impl RangeBounds<NotNan<f32>> for ExEx {
|
|
/// fn start_bound(&self) -> Bound<&NotNan<f32>> {
|
|
/// Bound::Excluded(&self.start)
|
|
/// }
|
|
/// fn end_bound(&self) -> Bound<&NotNan<f32>> {
|
|
/// Bound::Excluded(&self.end)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// // Now we can make a [`RangeBoundsMap`] of [`ExEx`]s to `u8`
|
|
/// let mut map = RangeBoundsMap::new();
|
|
///
|
|
/// map.insert_platonic(ExEx::new(0.0, 5.0), 8).unwrap();
|
|
/// map.insert_platonic(ExEx::new(5.0, 7.5), 32).unwrap();
|
|
///
|
|
/// assert_eq!(map.contains_point(&NotNan::new(5.0).unwrap()), false);
|
|
///
|
|
/// assert_eq!(map.get_at_point(&NotNan::new(9.0).unwrap()), None);
|
|
/// assert_eq!(
|
|
/// map.get_at_point(&NotNan::new(7.0).unwrap()),
|
|
/// Some(&32)
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// map.get_entry_at_point(&NotNan::new(2.0).unwrap()),
|
|
/// Some((&ExEx::new(0.0, 5.0), &8))
|
|
/// );
|
|
/// ```
|
|
///
|
|
/// [`RangeBounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
|
/// [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
|
pub struct RangeBoundsMap<I, K, V>
|
|
where
|
|
I: PartialOrd,
|
|
{
|
|
starts: BTreeMap<BoundOrd<I>, (K, V)>,
|
|
}
|
|
|
|
/// An error type to represent a `RangeBounds` overlapping another
|
|
/// `RangeBounds` when it should not have.
|
|
#[derive(PartialEq, Debug)]
|
|
pub struct OverlapError;
|
|
|
|
/// An error type to represent a failed [`TryFromBounds`] within a
|
|
/// method.
|
|
///
|
|
/// There are several methods that return this error, and some of the
|
|
/// causes of this error can be very subtle, so here are some examples
|
|
/// showing all the reasons this error might be returned.
|
|
///
|
|
/// # Example with [`RangeBoundsMap::cut()`]
|
|
///
|
|
/// The first way you may recieve [`TryFromBoundsError`] is from
|
|
/// [`RangeBoundsMap::cut()`].
|
|
///
|
|
/// In this example we try to cut `4..=6` out of a `RangeBoundsMap`
|
|
/// that contains `2..8`. If this was successful then the
|
|
/// `RangeBoundsMap` would hold `2..4` and `(Bound::Exclusive(6),
|
|
/// Bound::Exclusive(8))`. However, since the `RangeBounds` type of
|
|
/// this `RangeBoundsMap` is `Range<{integer}>` the latter of the two
|
|
/// new `RangeBounds` is "unrepresentable", and hence will fail to be
|
|
/// created via [`TryFromBounds`].
|
|
///
|
|
/// ```
|
|
/// use range_bounds_map::{RangeBoundsMap, TryFromBoundsError};
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(2..8, true)]).unwrap();
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.cut(&(4..=6)),
|
|
/// Err(TryFromBoundsError)
|
|
/// );
|
|
/// ```
|
|
///
|
|
/// # Example with `insert_coalesce_*` functions.
|
|
///
|
|
/// The second and final way you may recieve a [`TryFromBoundsError`]
|
|
/// is via coalescing methods such as
|
|
/// [`RangeBoundsMap::insert_coalesce_touching`].
|
|
///
|
|
/// In the first example it was fairly easy to create an invalid
|
|
/// `RangeBounds` by cutting with a different `RangeBounds` than the
|
|
/// underlying `RangeBoundsMap`'s `RangeBounds` type. However, the
|
|
/// `insert_coalesce_*` functions all take `range_bounds: K` as an
|
|
/// argument so it is not possible to create an invalid `K` type
|
|
/// directly. However upon "coalescing" of two `RangeBounds` (even if
|
|
/// both of them are type `K`), you can create a `RangeBounds` that *cannot* be
|
|
/// of type `K`.
|
|
///
|
|
/// In this example we use a `RangeBounds` type that can be either
|
|
/// Inclusive-Inclusive OR Exlusive-Exlusive. We then try to use
|
|
/// [`RangeBoundsMap::insert_coalesce_touching()`] to "coalesce" an
|
|
/// Inclusive-Inclusive and a Exlusive-Exlusive `MultiBounds`. This
|
|
/// will however fail as the resulting "coalesced" `RangeBounds` would
|
|
/// have to be Inclusive-Exlusive which `MultiBounds` does not support.
|
|
///
|
|
/// ```
|
|
/// use std::ops::{Bound, RangeBounds};
|
|
///
|
|
/// use range_bounds_map::{
|
|
/// OverlapOrTryFromBoundsError, RangeBoundsMap, TryFromBounds,
|
|
/// TryFromBoundsError,
|
|
/// };
|
|
///
|
|
/// #[derive(Debug, PartialEq)]
|
|
/// enum MultiBounds {
|
|
/// Inclusive(u8, u8),
|
|
/// Exclusive(u8, u8),
|
|
/// }
|
|
///
|
|
/// impl RangeBounds<u8> for MultiBounds {
|
|
/// fn start_bound(&self) -> Bound<&u8> {
|
|
/// match self {
|
|
/// MultiBounds::Inclusive(start, _) => {
|
|
/// Bound::Included(start)
|
|
/// }
|
|
/// MultiBounds::Exclusive(start, _) => {
|
|
/// Bound::Excluded(start)
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// fn end_bound(&self) -> Bound<&u8> {
|
|
/// match self {
|
|
/// MultiBounds::Inclusive(_, end) => {
|
|
/// Bound::Included(end)
|
|
/// }
|
|
/// MultiBounds::Exclusive(_, end) => {
|
|
/// Bound::Excluded(end)
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// }
|
|
///
|
|
/// impl TryFromBounds<u8> for MultiBounds {
|
|
/// fn try_from_bounds(
|
|
/// start_bound: Bound<u8>,
|
|
/// end_bound: Bound<u8>,
|
|
/// ) -> Option<Self> {
|
|
/// match (start_bound, end_bound) {
|
|
/// (Bound::Included(start), Bound::Included(end)) => {
|
|
/// Some(MultiBounds::Inclusive(start, end))
|
|
/// }
|
|
/// (Bound::Excluded(start), Bound::Excluded(end)) => {
|
|
/// Some(MultiBounds::Exclusive(start, end))
|
|
/// }
|
|
/// _ => None,
|
|
/// }
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let mut range_bounds_map = RangeBoundsMap::try_from([(
|
|
/// MultiBounds::Inclusive(2, 4),
|
|
/// true,
|
|
/// )])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_touching(
|
|
/// MultiBounds::Exclusive(4, 6),
|
|
/// false
|
|
/// ),
|
|
/// Err(OverlapOrTryFromBoundsError::TryFromBounds(
|
|
/// TryFromBoundsError
|
|
/// ))
|
|
/// );
|
|
/// ```
|
|
#[derive(PartialEq, Debug)]
|
|
pub struct TryFromBoundsError;
|
|
|
|
/// An error type to represent either an [`OverlapError`] or a
|
|
/// [`TryFromBoundsError`].
|
|
#[derive(PartialEq, Debug)]
|
|
pub enum OverlapOrTryFromBoundsError {
|
|
Overlap(OverlapError),
|
|
TryFromBounds(TryFromBoundsError),
|
|
}
|
|
|
|
impl<I, K, V> RangeBoundsMap<I, K, V>
|
|
where
|
|
K: RangeBounds<I>,
|
|
I: Ord + Clone,
|
|
{
|
|
/// Makes a new, empty `RangeBoundsMap`.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use std::ops::Range;
|
|
///
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map: RangeBoundsMap<u8, Range<u8>, bool> =
|
|
/// RangeBoundsMap::new();
|
|
/// ```
|
|
#[trivial]
|
|
pub fn new() -> Self {
|
|
RangeBoundsMap {
|
|
starts: BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Returns the number of `RangeBounds` in the map.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map = RangeBoundsMap::new();
|
|
///
|
|
/// assert_eq!(range_bounds_map.len(), 0);
|
|
/// range_bounds_map.insert_platonic(0..1, false).unwrap();
|
|
/// assert_eq!(range_bounds_map.len(), 1);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn len(&self) -> usize {
|
|
self.starts.len()
|
|
}
|
|
|
|
/// Adds a new (`RangeBounds`, `Value`) pair to the map without
|
|
/// modifying other entries.
|
|
///
|
|
/// If the new `RangeBounds` overlaps one or more `RangeBounds`
|
|
/// already in the map rather than just touching then an
|
|
/// [`OverlapError`] is returned and the map is not updated.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::{OverlapError, RangeBoundsMap};
|
|
///
|
|
/// let mut range_bounds_map = RangeBoundsMap::new();
|
|
///
|
|
/// assert_eq!(range_bounds_map.insert_platonic(5..10, 9), Ok(()));
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_platonic(5..10, 2),
|
|
/// Err(OverlapError)
|
|
/// );
|
|
/// assert_eq!(range_bounds_map.len(), 1);
|
|
/// ```
|
|
#[tested]
|
|
pub fn insert_platonic(
|
|
&mut self,
|
|
range_bounds: K,
|
|
value: V,
|
|
) -> Result<(), OverlapError> {
|
|
if self.overlaps(&range_bounds) {
|
|
return Err(OverlapError);
|
|
}
|
|
|
|
let start = BoundOrd::start(range_bounds.start_bound());
|
|
let end = BoundOrd::end(range_bounds.end_bound());
|
|
|
|
if start > end {
|
|
panic!("Invalid search range bounds!");
|
|
}
|
|
|
|
self.starts.insert(
|
|
BoundOrd::start(range_bounds.start_bound().cloned()),
|
|
(range_bounds, value),
|
|
);
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
/// Returns `true` if the given `RangeBounds` overlaps any of the
|
|
/// `RangeBounds` in the map.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map = RangeBoundsMap::new();
|
|
///
|
|
/// range_bounds_map.insert_platonic(5..10, false);
|
|
///
|
|
/// assert_eq!(range_bounds_map.overlaps(&(1..=3)), false);
|
|
/// assert_eq!(range_bounds_map.overlaps(&(4..5)), false);
|
|
///
|
|
/// assert_eq!(range_bounds_map.overlaps(&(4..=5)), true);
|
|
/// assert_eq!(range_bounds_map.overlaps(&(4..6)), true);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn overlaps<Q>(&self, search_range_bounds: &Q) -> bool
|
|
where
|
|
Q: RangeBounds<I>,
|
|
{
|
|
self.overlapping(search_range_bounds).next().is_some()
|
|
}
|
|
|
|
/// Returns an iterator over every (`RangeBounds`, `Value`) pair
|
|
/// in the map which overlap the given `range_bounds` in
|
|
/// ascending order.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// let mut overlapping = range_bounds_map.overlapping(&(2..8));
|
|
///
|
|
/// assert_eq!(
|
|
/// overlapping.collect::<Vec<_>>(),
|
|
/// [(&(1..4), &false), (&(4..8), &true)]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn overlapping<Q>(
|
|
&self,
|
|
range_bounds: &Q,
|
|
) -> impl DoubleEndedIterator<Item = (&K, &V)>
|
|
where
|
|
Q: RangeBounds<I>,
|
|
{
|
|
if !is_valid_range_bounds(range_bounds) {
|
|
panic!("Invalid range bounds!");
|
|
}
|
|
|
|
let start = BoundOrd::start(range_bounds.start_bound().cloned());
|
|
let end = BoundOrd::end(range_bounds.end_bound().cloned());
|
|
|
|
let start_range_bounds = (
|
|
//Included is lossless regarding meta-bounds searches
|
|
//which is what we want
|
|
Bound::Included(start),
|
|
Bound::Included(end),
|
|
);
|
|
//this range will hold all the ranges we want except possibly
|
|
//the first RangeBounds in the range
|
|
let most_range_bounds = self.starts.range(start_range_bounds);
|
|
|
|
//then we check for this possibly missing range_bounds
|
|
if let Some(missing_entry @ (_, (possible_missing_range_bounds, _))) =
|
|
//Excluded is lossy regarding meta-bounds searches because
|
|
//we don't want equal bounds as they would have be covered
|
|
//in the previous step and we don't want duplicates
|
|
self.starts
|
|
.range((
|
|
Bound::Unbounded,
|
|
Bound::Excluded(BoundOrd::start(
|
|
range_bounds.start_bound().cloned(),
|
|
)),
|
|
))
|
|
.next_back()
|
|
{
|
|
if overlaps(possible_missing_range_bounds, range_bounds) {
|
|
return Either::Left(
|
|
once(missing_entry)
|
|
.chain(most_range_bounds)
|
|
.map(|(_, (key, value))| (key, value)),
|
|
);
|
|
}
|
|
}
|
|
|
|
return Either::Right(
|
|
most_range_bounds.map(|(_, (key, value))| (key, value)),
|
|
);
|
|
}
|
|
|
|
/// Returns a reference to the `Value` corresponding to the
|
|
/// `RangeBounds` in the map that overlaps the given point, if
|
|
/// any.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(range_bounds_map.get_at_point(&3), Some(&false));
|
|
/// assert_eq!(range_bounds_map.get_at_point(&4), Some(&true));
|
|
/// assert_eq!(range_bounds_map.get_at_point(&101), None);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn get_at_point(&self, point: &I) -> Option<&V> {
|
|
self.get_entry_at_point(point).map(|(_, value)| value)
|
|
}
|
|
|
|
/// Returns `true` if the map contains a `RangeBounds` that
|
|
/// overlaps a given point, and `false` if not.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(range_bounds_map.contains_point(&3), true);
|
|
/// assert_eq!(range_bounds_map.contains_point(&4), true);
|
|
/// assert_eq!(range_bounds_map.contains_point(&101), false);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn contains_point(&self, point: &I) -> bool {
|
|
self.get_at_point(point).is_some()
|
|
}
|
|
|
|
/// Returns a mutable reference to the `Value` corresponding to
|
|
/// the `RangeBounds` that overlaps the given point, if any.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(1..4, false)]).unwrap();
|
|
///
|
|
/// if let Some(x) = range_bounds_map.get_at_point_mut(&2) {
|
|
/// *x = true;
|
|
/// }
|
|
///
|
|
/// assert_eq!(range_bounds_map.get_at_point(&1), Some(&true));
|
|
/// ```
|
|
#[tested]
|
|
pub fn get_at_point_mut(&mut self, point: &I) -> Option<&mut V> {
|
|
if let Some(overlapping_start_bound) = self
|
|
.get_entry_at_point(point)
|
|
.map(|(key, _)| key.start_bound())
|
|
{
|
|
return self
|
|
.starts
|
|
.get_mut(&BoundOrd::start(overlapping_start_bound.cloned()))
|
|
.map(|(_, value)| value);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
/// Returns an (`RangeBounds`, `Value`) pair corresponding to the
|
|
/// `RangeBounds` that overlaps the given point, if any.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.get_entry_at_point(&3),
|
|
/// Some((&(1..4), &false))
|
|
/// );
|
|
/// assert_eq!(
|
|
/// range_bounds_map.get_entry_at_point(&4),
|
|
/// Some((&(4..8), &true))
|
|
/// );
|
|
/// assert_eq!(range_bounds_map.get_entry_at_point(&101), None);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn get_entry_at_point(&self, point: &I) -> Option<(&K, &V)> {
|
|
//a zero-range included-included range is equivalent to a point
|
|
return self
|
|
.overlapping(&(
|
|
Bound::Included(point.clone()),
|
|
Bound::Included(point.clone()),
|
|
))
|
|
.next();
|
|
}
|
|
|
|
/// Returns an iterator over every (`RangeBounds`, `Value`) pair
|
|
/// in the map in ascending order.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// let mut iter = range_bounds_map.iter();
|
|
///
|
|
/// assert_eq!(iter.next(), Some((&(1..4), &false)));
|
|
/// assert_eq!(iter.next(), Some((&(4..8), &true)));
|
|
/// assert_eq!(iter.next(), Some((&(8..100), &false)));
|
|
/// assert_eq!(iter.next(), None);
|
|
/// ```
|
|
#[trivial]
|
|
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (&K, &V)> {
|
|
self.starts.iter().map(|(_, (key, value))| (key, value))
|
|
}
|
|
|
|
/// Removes every (`RangeBounds`, `Value`) pair in the map which
|
|
/// overlaps the given `range_bounds` and returns them in
|
|
/// an iterator.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// let mut removed = range_bounds_map.remove_overlapping(&(2..8));
|
|
///
|
|
/// assert_eq!(
|
|
/// removed.collect::<Vec<_>>(),
|
|
/// [(1..4, false), (4..8, true)]
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.iter().collect::<Vec<_>>(),
|
|
/// [(&(8..100), &false)]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn remove_overlapping<Q>(
|
|
&mut self,
|
|
range_bounds: &Q,
|
|
) -> impl DoubleEndedIterator<Item = (K, V)>
|
|
where
|
|
Q: RangeBounds<I>,
|
|
{
|
|
//optimisation do this whole function without cloning anything
|
|
//or collectiong anything, may depend on a nicer upstream
|
|
//BTreeMap remove_range function
|
|
|
|
let to_remove: Vec<BoundOrd<I>> = self
|
|
.overlapping(range_bounds)
|
|
.map(|(key, _)| (BoundOrd::start(key.start_bound().cloned())))
|
|
.collect();
|
|
|
|
let mut output = Vec::new();
|
|
|
|
for start_bound in to_remove {
|
|
output.push(self.starts.remove(&start_bound).unwrap());
|
|
}
|
|
|
|
return output.into_iter();
|
|
}
|
|
|
|
/// Cuts a given `RangeBounds` out of the map.
|
|
///
|
|
/// If the remaining `RangeBounds` left after the cut are not able
|
|
/// to be created with the [`TryFromBounds`] trait then a
|
|
/// [`TryFromBoundsError`] will be returned.
|
|
///
|
|
/// `V` must implement `Clone` as if you try to cut out the center
|
|
/// of a `RangeBounds` in the map it will split into two different
|
|
/// (`RangeBounds`, `Value`) pairs using `Clone`.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::{RangeBoundsMap, TryFromBoundsError};
|
|
///
|
|
/// let mut base = RangeBoundsMap::try_from([
|
|
/// (1..4, false),
|
|
/// (4..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// let after_cut =
|
|
/// RangeBoundsMap::try_from([(1..2, false), (40..100, false)])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(base.cut(&(2..40)), Ok(()));
|
|
/// assert_eq!(base, after_cut);
|
|
/// assert_eq!(base.cut(&(60..=80)), Err(TryFromBoundsError));
|
|
/// ```
|
|
#[tested]
|
|
pub fn cut<Q>(&mut self, range_bounds: &Q) -> Result<(), TryFromBoundsError>
|
|
where
|
|
Q: RangeBounds<I>,
|
|
K: TryFromBounds<I>,
|
|
V: Clone,
|
|
{
|
|
let mut to_insert = Vec::new();
|
|
|
|
{
|
|
// only the first and last range_bounds in overlapping stand a
|
|
// change of remaining after the cut so we don't need to
|
|
// collect the iterator and can just look at the first and
|
|
// last elements since range is a double ended iterator ;p
|
|
let mut overlapping = self.overlapping(range_bounds);
|
|
|
|
let first_last = (overlapping.next(), overlapping.next_back());
|
|
|
|
match first_last {
|
|
(Some(first), Some(last)) => {
|
|
match cut_range_bounds(first.0, range_bounds) {
|
|
CutResult::Nothing => {}
|
|
CutResult::Single(left_section) => {
|
|
to_insert.push((left_section, first.1.clone()));
|
|
}
|
|
CutResult::Double(_, _) => unreachable!(),
|
|
}
|
|
match cut_range_bounds(last.0, range_bounds) {
|
|
CutResult::Nothing => {}
|
|
CutResult::Single(right_section) => {
|
|
to_insert.push((right_section, last.1.clone()));
|
|
}
|
|
CutResult::Double(_, _) => unreachable!(),
|
|
}
|
|
}
|
|
(Some(first), None) => {
|
|
match cut_range_bounds(first.0, range_bounds) {
|
|
CutResult::Nothing => {}
|
|
CutResult::Single(section) => {
|
|
to_insert.push((section, first.1.clone()));
|
|
}
|
|
CutResult::Double(left_section, right_section) => {
|
|
to_insert.push((left_section, first.1.clone()));
|
|
to_insert.push((right_section, first.1.clone()));
|
|
}
|
|
}
|
|
}
|
|
(None, None) => {}
|
|
(None, Some(_)) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
// Make sure that the inserts will work before we try to do
|
|
// them, so if one fails the map remains unchanged
|
|
if to_insert.iter().all(|(x, _)| K::is_valid(x)) {
|
|
self.remove_overlapping(range_bounds).next();
|
|
for ((start, end), value) in to_insert.into_iter() {
|
|
self.insert_platonic(
|
|
K::try_from_bounds(start, end).unwrap(),
|
|
value.clone(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
return Ok(());
|
|
} else {
|
|
return Err(TryFromBoundsError);
|
|
}
|
|
}
|
|
|
|
/// Returns an iterator of `(Bound<&I>, Bound<&I>)` over all the
|
|
/// maximally-sized gaps in the map that are also within the given
|
|
/// `outer_range_bounds`.
|
|
///
|
|
/// To get all possible gaps just call `gaps()` with an unbounded
|
|
/// `RangeBounds` such as `&(..)` or `&(Bound::Unbounded,
|
|
/// Bound::Unbounded)`.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use std::ops::Bound;
|
|
///
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..3, false),
|
|
/// (5..7, true),
|
|
/// (9..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// let mut gaps = range_bounds_map.gaps(&(2..));
|
|
///
|
|
/// assert_eq!(
|
|
/// gaps.collect::<Vec<_>>(),
|
|
/// [
|
|
/// (Bound::Included(&3), Bound::Excluded(&5)),
|
|
/// (Bound::Included(&7), Bound::Excluded(&9)),
|
|
/// (Bound::Included(&100), Bound::Unbounded)
|
|
/// ]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn gaps<'a, Q>(
|
|
&'a self,
|
|
outer_range_bounds: &'a Q,
|
|
) -> impl Iterator<Item = (Bound<&I>, Bound<&I>)>
|
|
where
|
|
Q: RangeBounds<I>,
|
|
{
|
|
// I'm in love with how clean/mindblowing this entire function is
|
|
let overlapping = self
|
|
.overlapping(outer_range_bounds)
|
|
.map(|(key, _)| (key.start_bound(), key.end_bound()));
|
|
|
|
// If the start or end point of outer_range_bounds is not
|
|
// contained within a RangeBounds in the map then we need to
|
|
// generate a artificial RangeBounds to use instead.
|
|
//
|
|
// We also have to flip the artificial ones ahead of time as
|
|
// we actually want the search_range_bounds endpoints included
|
|
// not excluded unlike with other bounds in artificials
|
|
|
|
let artificial_start = (
|
|
flip_bound(outer_range_bounds.start_bound()),
|
|
flip_bound(outer_range_bounds.start_bound()),
|
|
);
|
|
let artificial_end = (
|
|
flip_bound(outer_range_bounds.end_bound()),
|
|
flip_bound(outer_range_bounds.end_bound()),
|
|
);
|
|
let mut artificials = once(artificial_start)
|
|
.chain(overlapping)
|
|
.chain(once(artificial_end));
|
|
|
|
let start_contained = match outer_range_bounds.start_bound() {
|
|
Bound::Included(point) => self.contains_point(point),
|
|
Bound::Excluded(point) => self.contains_point(point),
|
|
Bound::Unbounded => self.starts.first_key_value().is_some_and(
|
|
|(_, (range_bounds, _))| {
|
|
range_bounds.start_bound() == Bound::Unbounded
|
|
},
|
|
),
|
|
};
|
|
let end_contained = match outer_range_bounds.end_bound() {
|
|
Bound::Included(point) => self.contains_point(point),
|
|
Bound::Excluded(point) => self.contains_point(point),
|
|
Bound::Unbounded => self.starts.last_key_value().is_some_and(
|
|
|(_, (range_bounds, _))| {
|
|
range_bounds.end_bound() == Bound::Unbounded
|
|
},
|
|
),
|
|
};
|
|
|
|
if start_contained {
|
|
artificials.next();
|
|
}
|
|
if end_contained {
|
|
artificials.next_back();
|
|
}
|
|
|
|
return artificials
|
|
.tuple_windows()
|
|
.map(|((_, first_end), (second_start, _))| {
|
|
(flip_bound(first_end), flip_bound(second_start))
|
|
})
|
|
.filter(is_valid_range_bounds::<(Bound<&I>, Bound<&I>), I>);
|
|
}
|
|
|
|
/// Returns `true` if the map covers every point in the given
|
|
/// `RangeBounds`, and `false` if it doesn't.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let range_bounds_map = RangeBoundsMap::try_from([
|
|
/// (1..3, false),
|
|
/// (5..8, true),
|
|
/// (8..100, false),
|
|
/// ])
|
|
/// .unwrap();
|
|
///
|
|
/// assert_eq!(range_bounds_map.contains_range_bounds(&(1..3)), true);
|
|
/// assert_eq!(
|
|
/// range_bounds_map.contains_range_bounds(&(2..6)),
|
|
/// false
|
|
/// );
|
|
/// assert_eq!(
|
|
/// range_bounds_map.contains_range_bounds(&(6..50)),
|
|
/// true
|
|
/// );
|
|
/// ```
|
|
#[trivial]
|
|
pub fn contains_range_bounds<Q>(&self, range_bounds: &Q) -> bool
|
|
where
|
|
Q: RangeBounds<I>,
|
|
{
|
|
// Soooo clean and mathematical 🥰!
|
|
self.gaps(range_bounds).next().is_none()
|
|
}
|
|
|
|
/// Adds a new (`RangeBounds`, `Value`) pair to the map and
|
|
/// coalesces into other `RangeBounds` in the map which touch it.
|
|
///
|
|
/// The `Value` of the coalesced `RangeBounds` is set to the given
|
|
/// `Value`.
|
|
///
|
|
/// If successful then a reference to the newly inserted
|
|
/// `RangeBounds` is returned.
|
|
///
|
|
/// If the new `RangeBounds` overlaps one or more `RangeBounds`
|
|
/// already in the map rather than just touching then an
|
|
/// [`OverlapError`] is returned and the map is not updated.
|
|
/// `RangeBounds` is returned.
|
|
///
|
|
/// If the coalesced `RangeBounds` cannot be created with the
|
|
/// [`TryFromBounds`] trait then a [`TryFromBoundsError`] will be
|
|
/// returned.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::{
|
|
/// OverlapError, OverlapOrTryFromBoundsError, RangeBoundsMap,
|
|
/// };
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(1..4, false)]).unwrap();
|
|
///
|
|
/// // Touching
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_touching(4..6, true),
|
|
/// Ok(&(1..6))
|
|
/// );
|
|
///
|
|
/// // Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_touching(4..8, false),
|
|
/// Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)),
|
|
/// );
|
|
///
|
|
/// // Neither Touching or Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_touching(10..16, false),
|
|
/// Ok(&(10..16))
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.iter().collect::<Vec<_>>(),
|
|
/// [(&(1..6), &true), (&(10..16), &false),]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn insert_coalesce_touching(
|
|
&mut self,
|
|
range_bounds: K,
|
|
value: V,
|
|
) -> Result<&K, OverlapOrTryFromBoundsError>
|
|
where
|
|
K: TryFromBounds<I>,
|
|
{
|
|
if self.overlaps(&range_bounds) {
|
|
return Err(OverlapOrTryFromBoundsError::Overlap(OverlapError));
|
|
}
|
|
|
|
let touching_left_start_bound = self
|
|
.touching_left(&range_bounds)
|
|
.map(|x| BoundOrd::start(x.start_bound().cloned()));
|
|
let touching_right_start_bound = self
|
|
.touching_right(&range_bounds)
|
|
.map(|x| BoundOrd::start(x.start_bound().cloned()));
|
|
|
|
let start_bound = match touching_left_start_bound {
|
|
Some(ref x) => self.starts.get(x).unwrap().0.start_bound().cloned(),
|
|
None => range_bounds.start_bound().cloned(),
|
|
};
|
|
let end_bound = match touching_right_start_bound {
|
|
Some(ref x) => self.starts.get(x).unwrap().0.end_bound(),
|
|
None => range_bounds.end_bound(),
|
|
};
|
|
|
|
let new_range_bounds =
|
|
K::try_from_bounds(start_bound.clone(), end_bound.cloned()).ok_or(
|
|
OverlapOrTryFromBoundsError::TryFromBounds(TryFromBoundsError),
|
|
)?;
|
|
|
|
// Out with the old!
|
|
if let Some(ref left) = touching_left_start_bound {
|
|
self.starts.remove(left);
|
|
}
|
|
if let Some(ref right) = touching_right_start_bound {
|
|
self.starts.remove(right);
|
|
}
|
|
|
|
// In with the new!
|
|
self.starts.insert(
|
|
BoundOrd::start(new_range_bounds.start_bound().cloned()),
|
|
(new_range_bounds, value),
|
|
);
|
|
|
|
return Ok(&self.starts.get(&BoundOrd::start(start_bound)).unwrap().0);
|
|
}
|
|
fn touching_left(&self, range_bounds: &K) -> Option<&K> {
|
|
return self
|
|
.starts
|
|
.range((
|
|
Bound::Unbounded,
|
|
Bound::Excluded(BoundOrd::start(
|
|
range_bounds.start_bound().cloned(),
|
|
)),
|
|
))
|
|
.next_back()
|
|
.map(|x| &x.1.0)
|
|
.filter(|x| touches(range_bounds, *x));
|
|
}
|
|
fn touching_right(&self, range_bounds: &K) -> Option<&K> {
|
|
return self
|
|
.starts
|
|
.range((
|
|
Bound::Excluded(BoundOrd::start(
|
|
range_bounds.start_bound().cloned(),
|
|
)),
|
|
Bound::Unbounded,
|
|
))
|
|
.next()
|
|
.map(|x| &x.1.0)
|
|
.filter(|x| touches(range_bounds, *x));
|
|
}
|
|
|
|
/// Adds a new (`RangeBounds`, `Value`) pair to the map and
|
|
/// coalesces into other `RangeBounds` in the map which overlap
|
|
/// it.
|
|
///
|
|
/// The `Value` of the coalesced `RangeBounds` is set to the given
|
|
/// `Value`.
|
|
///
|
|
/// If successful then a reference to the newly inserted
|
|
/// `RangeBounds` is returned.
|
|
///
|
|
/// If the coalesced `RangeBounds` cannot be created with the
|
|
/// [`TryFromBounds`] trait then a [`TryFromBoundsError`] will be
|
|
/// returned.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(1..4, false)]).unwrap();
|
|
///
|
|
/// // Touching
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_overlapping(-4..1, true),
|
|
/// Ok(&(-4..1))
|
|
/// );
|
|
///
|
|
/// // Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_overlapping(2..8, true),
|
|
/// Ok(&(1..8))
|
|
/// );
|
|
///
|
|
/// // Neither Touching or Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map.insert_coalesce_overlapping(10..16, false),
|
|
/// Ok(&(10..16))
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.iter().collect::<Vec<_>>(),
|
|
/// [(&(-4..1), &true), (&(1..8), &true), (&(10..16), &false)]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn insert_coalesce_overlapping(
|
|
&mut self,
|
|
range_bounds: K,
|
|
value: V,
|
|
) -> Result<&K, TryFromBoundsError>
|
|
where
|
|
K: TryFromBounds<I>,
|
|
{
|
|
let (start_bound, end_bound) = {
|
|
let overlapping_swell = self.overlapping_swell(&range_bounds);
|
|
(overlapping_swell.0.cloned(), overlapping_swell.1.cloned())
|
|
};
|
|
|
|
let new_range_bounds =
|
|
K::try_from_bounds(start_bound.clone(), end_bound)
|
|
.ok_or(TryFromBoundsError)?;
|
|
|
|
// Out with the old!
|
|
self.remove_overlapping(&range_bounds).next();
|
|
|
|
// In with the new!
|
|
self.starts.insert(
|
|
BoundOrd::start(new_range_bounds.start_bound().cloned()),
|
|
(new_range_bounds, value),
|
|
);
|
|
|
|
return Ok(&self.starts.get(&BoundOrd::start(start_bound)).unwrap().0);
|
|
}
|
|
fn overlapping_swell<'a>(
|
|
&'a self,
|
|
range_bounds: &'a K,
|
|
) -> (Bound<&I>, Bound<&I>) {
|
|
let mut overlapping = self.overlapping(range_bounds).peekable();
|
|
|
|
let start_bound = match overlapping.peek() {
|
|
Some((first, _)) => std::cmp::min(
|
|
BoundOrd::start(first.start_bound()),
|
|
BoundOrd::start(range_bounds.start_bound()),
|
|
),
|
|
None => BoundOrd::start(range_bounds.start_bound()),
|
|
};
|
|
let end_bound = match overlapping.next_back() {
|
|
Some((last, _)) => std::cmp::max(
|
|
BoundOrd::end(last.end_bound()),
|
|
BoundOrd::end(range_bounds.end_bound()),
|
|
),
|
|
None => BoundOrd::start(range_bounds.end_bound()),
|
|
};
|
|
|
|
return (Bound::from(start_bound), Bound::from(end_bound));
|
|
}
|
|
|
|
/// Adds a new (`RangeBounds`, `Value`) pair to the map and
|
|
/// coalesces into other `RangeBounds` in the map which touch or
|
|
/// overlap it.
|
|
///
|
|
/// The `Value` of the coalesced `RangeBounds` is set to the given
|
|
/// `Value`.
|
|
///
|
|
/// If successful then a reference to the newly inserted
|
|
/// `RangeBounds` is returned.
|
|
///
|
|
/// If the coalesced `RangeBounds` cannot be created with the
|
|
/// [`TryFromBounds`] trait then a [`TryFromBoundsError`] will be
|
|
/// returned.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(1..4, false)]).unwrap();
|
|
///
|
|
/// // Touching
|
|
/// assert_eq!(
|
|
/// range_bounds_map
|
|
/// .insert_coalesce_touching_or_overlapping(-4..1, true),
|
|
/// Ok(&(-4..4))
|
|
/// );
|
|
///
|
|
/// // Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map
|
|
/// .insert_coalesce_touching_or_overlapping(2..8, true),
|
|
/// Ok(&(-4..8))
|
|
/// );
|
|
///
|
|
/// // Neither Touching or Overlapping
|
|
/// assert_eq!(
|
|
/// range_bounds_map
|
|
/// .insert_coalesce_touching_or_overlapping(10..16, false),
|
|
/// Ok(&(10..16))
|
|
/// );
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.iter().collect::<Vec<_>>(),
|
|
/// [(&(-4..8), &true), (&(10..16), &false)]
|
|
/// );
|
|
/// ```
|
|
#[tested]
|
|
pub fn insert_coalesce_touching_or_overlapping(
|
|
&mut self,
|
|
range_bounds: K,
|
|
value: V,
|
|
) -> Result<&K, TryFromBoundsError>
|
|
where
|
|
K: TryFromBounds<I>,
|
|
{
|
|
let overlapping_swell = self.overlapping_swell(&range_bounds);
|
|
let start_bound = match self.touching_left(&range_bounds) {
|
|
Some(touching_left) => {
|
|
Bound::from(touching_left.start_bound().cloned())
|
|
}
|
|
None => overlapping_swell.0.cloned(),
|
|
};
|
|
let end_bound = match self.touching_right(&range_bounds) {
|
|
Some(touching_right) => {
|
|
Bound::from(touching_right.end_bound().cloned())
|
|
}
|
|
None => overlapping_swell.1.cloned(),
|
|
};
|
|
|
|
let new_range_bounds =
|
|
K::try_from_bounds(start_bound.clone(), end_bound)
|
|
.ok_or(TryFromBoundsError)?;
|
|
|
|
self.remove_overlapping(&new_range_bounds).next();
|
|
self.starts.insert(
|
|
BoundOrd::start(start_bound.clone()),
|
|
(new_range_bounds, value),
|
|
);
|
|
|
|
return Ok(&self.starts.get(&BoundOrd::start(start_bound)).unwrap().0);
|
|
}
|
|
|
|
/// Adds a new (`RangeBounds`, `Value`) pair to the map and
|
|
/// overwrites any other `RangeBounds` that overlap the new
|
|
/// `RangeBounds`.
|
|
///
|
|
/// This is equivalent to using [`RangeBoundsMap::cut()`]
|
|
/// followed by [`RangeBoundsMap::insert_platonic()`]. Hence the
|
|
/// same `V: Clone` trait bound applies.
|
|
///
|
|
/// If the remaining `RangeBounds` left after the cut are not able
|
|
/// to be created with the [`TryFromBounds`] trait then a
|
|
/// [`TryFromBoundsError`] will be returned.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use range_bounds_map::RangeBoundsMap;
|
|
///
|
|
/// let mut range_bounds_map =
|
|
/// RangeBoundsMap::try_from([(2..8, false)]).unwrap();
|
|
///
|
|
/// assert_eq!(range_bounds_map.overwrite(4..6, true), Ok(()));
|
|
///
|
|
/// assert_eq!(
|
|
/// range_bounds_map.iter().collect::<Vec<_>>(),
|
|
/// [(&(2..4), &false), (&(4..6), &true), (&(6..8), &false)]
|
|
/// );
|
|
/// ```
|
|
#[trivial]
|
|
pub fn overwrite(
|
|
&mut self,
|
|
range_bounds: K,
|
|
value: V,
|
|
) -> Result<(), TryFromBoundsError>
|
|
where
|
|
V: Clone,
|
|
K: TryFromBounds<I>,
|
|
{
|
|
self.cut(&range_bounds)?;
|
|
self.insert_platonic(range_bounds, value).unwrap();
|
|
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
impl<const N: usize, I, K, V> TryFrom<[(K, V); N]> for RangeBoundsMap<I, K, V>
|
|
where
|
|
K: RangeBounds<I>,
|
|
I: Ord + Clone,
|
|
{
|
|
type Error = OverlapError;
|
|
#[trivial]
|
|
fn try_from(pairs: [(K, V); N]) -> Result<Self, Self::Error> {
|
|
let mut range_bounds_map = RangeBoundsMap::new();
|
|
for (range_bounds, value) in pairs {
|
|
range_bounds_map.insert_platonic(range_bounds, value)?;
|
|
}
|
|
|
|
return Ok(range_bounds_map);
|
|
}
|
|
}
|
|
impl<I, K, V> TryFrom<Vec<(K, V)>> for RangeBoundsMap<I, K, V>
|
|
where
|
|
K: RangeBounds<I>,
|
|
I: Ord + Clone,
|
|
{
|
|
type Error = OverlapError;
|
|
#[trivial]
|
|
fn try_from(pairs: Vec<(K, V)>) -> Result<Self, Self::Error> {
|
|
let mut range_bounds_map = RangeBoundsMap::new();
|
|
for (range_bounds, value) in pairs {
|
|
range_bounds_map.insert_platonic(range_bounds, value)?;
|
|
}
|
|
|
|
return Ok(range_bounds_map);
|
|
}
|
|
}
|
|
|
|
impl<I, K, V> Default for RangeBoundsMap<I, K, V>
|
|
where
|
|
I: PartialOrd,
|
|
{
|
|
#[trivial]
|
|
fn default() -> Self {
|
|
RangeBoundsMap {
|
|
starts: BTreeMap::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum CutResult<I> {
|
|
Nothing,
|
|
Single((Bound<I>, Bound<I>)),
|
|
Double((Bound<I>, Bound<I>), (Bound<I>, Bound<I>)),
|
|
}
|
|
|
|
#[tested]
|
|
fn cut_range_bounds<I, B, C>(
|
|
base_range_bounds: &B,
|
|
cut_range_bounds: &C,
|
|
) -> CutResult<I>
|
|
where
|
|
B: RangeBounds<I>,
|
|
C: RangeBounds<I>,
|
|
I: PartialOrd + Clone,
|
|
{
|
|
if !overlaps(base_range_bounds, cut_range_bounds) {
|
|
// if they don't overlap just return the original
|
|
// base_range_bounds
|
|
return CutResult::Single((
|
|
base_range_bounds.start_bound().cloned(),
|
|
base_range_bounds.end_bound().cloned(),
|
|
));
|
|
}
|
|
|
|
let (base_start_bound, base_end_bound) = (
|
|
base_range_bounds.start_bound(),
|
|
base_range_bounds.end_bound(),
|
|
);
|
|
let (cut_start_bound, cut_end_bound) =
|
|
(cut_range_bounds.start_bound(), cut_range_bounds.end_bound());
|
|
|
|
let left_section = match BoundOrd::start(cut_start_bound)
|
|
> BoundOrd::start(base_start_bound)
|
|
{
|
|
false => None,
|
|
true => Some((
|
|
base_start_bound.cloned(),
|
|
flip_bound(cut_start_bound).cloned(),
|
|
)),
|
|
};
|
|
let right_section = match BoundOrd::end(cut_end_bound)
|
|
< BoundOrd::end(base_end_bound)
|
|
{
|
|
false => None,
|
|
true => {
|
|
Some((flip_bound(cut_end_bound).cloned(), base_end_bound.cloned()))
|
|
}
|
|
};
|
|
|
|
match (left_section, right_section) {
|
|
(Some(left), Some(right)) => CutResult::Double(left, right),
|
|
(Some(left), None) => CutResult::Single(left),
|
|
(None, Some(right)) => CutResult::Single(right),
|
|
(None, None) => CutResult::Nothing,
|
|
}
|
|
}
|
|
|
|
#[trivial]
|
|
fn is_valid_range_bounds<Q, I>(range_bounds: &Q) -> bool
|
|
where
|
|
Q: RangeBounds<I>,
|
|
I: std::cmp::PartialOrd,
|
|
{
|
|
match (range_bounds.start_bound(), range_bounds.end_bound()) {
|
|
(Bound::Included(start), Bound::Included(end)) => start <= end,
|
|
(Bound::Included(start), Bound::Excluded(end)) => start < end,
|
|
(Bound::Excluded(start), Bound::Included(end)) => start < end,
|
|
(Bound::Excluded(start), Bound::Excluded(end)) => start < end,
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
#[tested]
|
|
fn overlaps<I, A, B>(a: &A, b: &B) -> bool
|
|
where
|
|
A: RangeBounds<I>,
|
|
B: RangeBounds<I>,
|
|
I: PartialOrd,
|
|
{
|
|
// optimisation, do this with much less operations
|
|
let a_start = a.start_bound();
|
|
let a_end = a.end_bound();
|
|
|
|
let b_start = b.start_bound();
|
|
let b_end = b.end_bound();
|
|
|
|
let (left_end, right_start) =
|
|
match BoundOrd::start(a_start).cmp(&BoundOrd::start(b_start)) {
|
|
Ordering::Less => (a_end, b_start),
|
|
Ordering::Greater => (b_end, a_start),
|
|
Ordering::Equal => return true,
|
|
};
|
|
|
|
match (left_end, right_start) {
|
|
(Bound::Included(end), Bound::Included(start)) => end >= start,
|
|
|
|
(Bound::Excluded(end), Bound::Excluded(start)) => end > start,
|
|
(Bound::Included(end), Bound::Excluded(start)) => end > start,
|
|
(Bound::Excluded(end), Bound::Included(start)) => end > start,
|
|
|
|
(Bound::Unbounded, _) => true,
|
|
|
|
(_, Bound::Unbounded) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[tested]
|
|
fn touches<I, A, B>(a: &A, b: &B) -> bool
|
|
where
|
|
A: RangeBounds<I>,
|
|
B: RangeBounds<I>,
|
|
I: PartialOrd,
|
|
{
|
|
// optimisation, do this with much less operations
|
|
let a_start = a.start_bound();
|
|
let a_end = a.end_bound();
|
|
|
|
let b_start = b.start_bound();
|
|
let b_end = b.end_bound();
|
|
|
|
let (left_end, right_start) =
|
|
match BoundOrd::start(a_start).cmp(&BoundOrd::start(b_start)) {
|
|
Ordering::Less => (a_end, b_start),
|
|
Ordering::Greater => (b_end, a_start),
|
|
Ordering::Equal => return false,
|
|
};
|
|
|
|
match (left_end, right_start) {
|
|
(Bound::Included(end), Bound::Excluded(start)) => end == start,
|
|
(Bound::Excluded(end), Bound::Included(start)) => end == start,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
#[trivial]
|
|
fn flip_bound<I>(bound: Bound<&I>) -> Bound<&I> {
|
|
match bound {
|
|
Bound::Included(point) => Bound::Excluded(point),
|
|
Bound::Excluded(point) => Bound::Included(point),
|
|
Bound::Unbounded => Bound::Unbounded,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::ops::{Bound, Range, RangeBounds};
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::*;
|
|
use crate::bound_ord::BoundOrd;
|
|
|
|
type TestBounds = (Bound<u8>, Bound<u8>);
|
|
|
|
//only every other number to allow mathematical_overlapping_definition
|
|
//to test between bounds in finite using smaller intervalled finite
|
|
pub(crate) const NUMBERS: &'static [u8] = &[2, 4, 6, 8, 10];
|
|
//go a bit around on either side to compensate for Unbounded
|
|
pub(crate) const NUMBERS_DOMAIN: &'static [u8] =
|
|
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
|
|
fn basic() -> RangeBoundsMap<u8, TestBounds, bool> {
|
|
RangeBoundsMap::try_from([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
])
|
|
.unwrap()
|
|
}
|
|
|
|
fn special() -> RangeBoundsMap<u8, MultiBounds, bool> {
|
|
RangeBoundsMap::try_from([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 12), false),
|
|
])
|
|
.unwrap()
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
enum MultiBounds {
|
|
Inclusive(u8, u8),
|
|
Exclusive(u8, u8),
|
|
}
|
|
impl RangeBounds<u8> for MultiBounds {
|
|
fn start_bound(&self) -> Bound<&u8> {
|
|
match self {
|
|
MultiBounds::Inclusive(start, _) => Bound::Included(start),
|
|
MultiBounds::Exclusive(start, _) => Bound::Excluded(start),
|
|
}
|
|
}
|
|
fn end_bound(&self) -> Bound<&u8> {
|
|
match self {
|
|
MultiBounds::Inclusive(_, end) => Bound::Included(end),
|
|
MultiBounds::Exclusive(_, end) => Bound::Excluded(end),
|
|
}
|
|
}
|
|
}
|
|
impl TryFromBounds<u8> for MultiBounds {
|
|
fn try_from_bounds(
|
|
start_bound: Bound<u8>,
|
|
end_bound: Bound<u8>,
|
|
) -> Option<Self> {
|
|
match (start_bound, end_bound) {
|
|
(Bound::Included(start), Bound::Included(end)) => {
|
|
Some(MultiBounds::Inclusive(start, end))
|
|
}
|
|
(Bound::Excluded(start), Bound::Excluded(end)) => {
|
|
Some(MultiBounds::Exclusive(start, end))
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[test]
|
|
fn insert_platonic_tests() {
|
|
assert_insert_platonic(basic(), (ii(0, 4), false), Err(OverlapError), None::<[_; 0]>);
|
|
assert_insert_platonic(basic(), (ii(5, 6), false), Err(OverlapError), None::<[_; 0]>);
|
|
assert_insert_platonic(basic(), (ee(7, 8), false), Ok(()), Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ee(7, 8), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_insert_platonic(basic(), (ii(4, 5), true), Err(OverlapError), None::<[_; 0]>);
|
|
assert_insert_platonic(basic(), (ei(4, 5), true), Ok(()), Some([
|
|
(ui(4), false),
|
|
(ei(4, 5), true),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
}
|
|
fn assert_insert_platonic<const N: usize>(
|
|
mut before: RangeBoundsMap<u8, TestBounds, bool>,
|
|
to_insert: (TestBounds, bool),
|
|
result: Result<(), OverlapError>,
|
|
after: Option<[(TestBounds, bool); N]>,
|
|
) {
|
|
let clone = before.clone();
|
|
assert_eq!(before.insert_platonic(to_insert.0, to_insert.1), result);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn overlapping_tests() {
|
|
//case zero
|
|
for overlap_range in all_valid_test_bounds() {
|
|
//you can't overlap nothing
|
|
assert!(
|
|
RangeBoundsMap::<u8, Range<u8>, ()>::new()
|
|
.overlapping(&overlap_range)
|
|
.next()
|
|
.is_none()
|
|
);
|
|
}
|
|
|
|
//case one
|
|
for overlap_range in all_valid_test_bounds() {
|
|
for inside_range in all_valid_test_bounds() {
|
|
let mut range_bounds_set = RangeBoundsMap::new();
|
|
range_bounds_set.insert_platonic(inside_range, ()).unwrap();
|
|
|
|
let mut expected_overlapping = Vec::new();
|
|
if overlaps(&overlap_range, &inside_range) {
|
|
expected_overlapping.push(inside_range);
|
|
}
|
|
|
|
let overlapping = range_bounds_set
|
|
.overlapping(&overlap_range)
|
|
.map(|(key, _)| key)
|
|
.copied()
|
|
.collect::<Vec<_>>();
|
|
|
|
if overlapping != expected_overlapping {
|
|
dbg!(overlap_range, inside_range);
|
|
dbg!(overlapping, expected_overlapping);
|
|
panic!(
|
|
"Discrepency in .overlapping() with single inside range detected!"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//case two
|
|
for overlap_range in all_valid_test_bounds() {
|
|
for (inside_range1, inside_range2) in
|
|
all_non_overlapping_test_bound_pairs()
|
|
{
|
|
let mut range_bounds_set = RangeBoundsMap::new();
|
|
range_bounds_set.insert_platonic(inside_range1, ()).unwrap();
|
|
range_bounds_set.insert_platonic(inside_range2, ()).unwrap();
|
|
|
|
let mut expected_overlapping = Vec::new();
|
|
if overlaps(&overlap_range, &inside_range1) {
|
|
expected_overlapping.push(inside_range1);
|
|
}
|
|
if overlaps(&overlap_range, &inside_range2) {
|
|
expected_overlapping.push(inside_range2);
|
|
}
|
|
//make our expected_overlapping the correct order
|
|
if expected_overlapping.len() > 1 {
|
|
if BoundOrd::start(expected_overlapping[0].start_bound())
|
|
> BoundOrd::start(expected_overlapping[1].start_bound())
|
|
{
|
|
expected_overlapping.swap(0, 1);
|
|
}
|
|
}
|
|
|
|
let overlapping = range_bounds_set
|
|
.overlapping(&overlap_range)
|
|
.map(|(key, _)| key)
|
|
.copied()
|
|
.collect::<Vec<_>>();
|
|
|
|
if overlapping != expected_overlapping {
|
|
dbg!(overlap_range, inside_range1, inside_range2);
|
|
dbg!(overlapping, expected_overlapping);
|
|
panic!(
|
|
"Discrepency in .overlapping() with two inside ranges detected!"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[test]
|
|
fn remove_overlapping_tests() {
|
|
assert_remove_overlapping(basic(), ii(5, 5), [], None::<[_; 0]>);
|
|
assert_remove_overlapping(basic(), uu(), [
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
], Some([]));
|
|
assert_remove_overlapping(basic(), ii(6, 7), [
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
], Some([
|
|
(ui(4), false), (ie(14, 16), true)
|
|
]));
|
|
assert_remove_overlapping(basic(), iu(6), [
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
], Some([
|
|
(ui(4), false),
|
|
]));
|
|
}
|
|
fn assert_remove_overlapping<const N: usize, const Y: usize>(
|
|
mut before: RangeBoundsMap<u8, TestBounds, bool>,
|
|
to_remove: TestBounds,
|
|
result: [(TestBounds, bool); N],
|
|
after: Option<[(TestBounds, bool); Y]>,
|
|
) {
|
|
let clone = before.clone();
|
|
assert_eq!(
|
|
before.remove_overlapping(&to_remove).collect::<Vec<_>>(),
|
|
result
|
|
);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[test]
|
|
fn cut_tests() {
|
|
assert_cut(basic(), ii(50, 60), Ok(()), None::<[_; 0]>);
|
|
assert_cut(basic(), uu(), Ok(()), Some([]));
|
|
assert_cut(basic(), ui(6), Ok(()), Some([
|
|
(ee(6, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_cut(basic(), iu(6), Ok(()), Some([
|
|
(ui(4), false),
|
|
(ee(5, 6), true),
|
|
]));
|
|
|
|
assert_cut(special(), MultiBounds::Exclusive(5, 7), Ok(()), Some([
|
|
(MultiBounds::Inclusive(4, 5), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 12), false),
|
|
]));
|
|
assert_cut(special(), MultiBounds::Exclusive(6, 7), Ok(()), None::<[_; 0]>);
|
|
assert_cut(special(), MultiBounds::Inclusive(5, 6), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_cut(special(), MultiBounds::Inclusive(6, 7), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_cut(special(), 7..8, Ok(()), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Inclusive(8, 12), false),
|
|
]));
|
|
assert_cut(special(), MultiBounds::Inclusive(7, 10), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_cut(special(), MultiBounds::Exclusive(4, 6), Ok(()), Some([
|
|
(MultiBounds::Inclusive(4, 4), false),
|
|
(MultiBounds::Inclusive(6, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 12), false),
|
|
]));
|
|
}
|
|
fn assert_cut<const N: usize, Q, I, K, V>(
|
|
mut before: RangeBoundsMap<I, K, V>,
|
|
to_cut: Q,
|
|
result: Result<(), TryFromBoundsError>,
|
|
after: Option<[(K, V); N]>,
|
|
) where
|
|
I: Ord + Clone + Debug,
|
|
K: Clone + TryFromBounds<I> + Debug + PartialEq,
|
|
V: Clone + Debug + PartialEq,
|
|
K: RangeBounds<I>,
|
|
Q: RangeBounds<I>,
|
|
{
|
|
let clone = before.clone();
|
|
assert_eq!(before.cut(&to_cut), result);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn gaps_tests() {
|
|
assert_gaps(basic(), ii(50, 60), [ii(50, 60)]);
|
|
assert_gaps(basic(), iu(50), [iu(50)]);
|
|
assert_gaps(basic(), ee(3, 16), [ei(4, 5), ee(7, 14)]);
|
|
assert_gaps(basic(), ei(3, 16), [ei(4, 5), ee(7, 14), ii(16, 16)]);
|
|
assert_gaps(basic(), ue(5), [ee(4, 5)]);
|
|
assert_gaps(basic(), ui(3), []);
|
|
assert_gaps(basic(), ii(5, 5), [ii(5, 5)]);
|
|
assert_gaps(basic(), ii(6, 6), []);
|
|
assert_gaps(basic(), ii(7, 7), []);
|
|
assert_gaps(basic(), ii(8, 8), [ii(8, 8)]);
|
|
}
|
|
fn assert_gaps<const N: usize>(
|
|
range_bounds_map: RangeBoundsMap<u8, TestBounds, bool>,
|
|
outer_range_bounds: TestBounds,
|
|
result: [TestBounds; N],
|
|
) {
|
|
assert_eq!(
|
|
range_bounds_map
|
|
.gaps(&outer_range_bounds)
|
|
.map(|(start, end)| (start.cloned(), end.cloned()))
|
|
.collect::<Vec<_>>(),
|
|
result
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_coalesce_touching_tests() {
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ii(0, 4), false),
|
|
Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)),
|
|
None::<[_; 0]>,
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ee(7, 10), false),
|
|
Ok(&ie(7, 10)),
|
|
Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ie(7, 10), false),
|
|
(ie(14, 16), true),
|
|
]),
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ee(7, 11), true),
|
|
Ok(&ie(7, 11)),
|
|
Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ie(7, 11), true),
|
|
(ie(14, 16), true),
|
|
]),
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ee(12, 13), true),
|
|
Ok(&ee(12, 13)),
|
|
Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ee(12, 13), true),
|
|
(ie(14, 16), true),
|
|
]),
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ee(13, 14), false),
|
|
Ok(&ee(13, 16)),
|
|
Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ee(13, 16), false),
|
|
]),
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
basic(),
|
|
(ee(7, 14), false),
|
|
Ok(&ie(7, 16)),
|
|
Some([(ui(4), false), (ee(5, 7), true), (ie(7, 16), false)]),
|
|
);
|
|
|
|
assert_insert_coalesce_touching(
|
|
special(),
|
|
(MultiBounds::Exclusive(6, 7), true),
|
|
Err(OverlapOrTryFromBoundsError::TryFromBounds(
|
|
TryFromBoundsError,
|
|
)),
|
|
None::<[_; 0]>,
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
special(),
|
|
(MultiBounds::Inclusive(6, 7), true),
|
|
Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)),
|
|
None::<[_; 0]>,
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
special(),
|
|
(MultiBounds::Exclusive(12, 15), true),
|
|
Err(OverlapOrTryFromBoundsError::TryFromBounds(
|
|
TryFromBoundsError,
|
|
)),
|
|
None::<[_; 0]>,
|
|
);
|
|
assert_insert_coalesce_touching(
|
|
special(),
|
|
(MultiBounds::Inclusive(12, 15), true),
|
|
Err(OverlapOrTryFromBoundsError::Overlap(OverlapError)),
|
|
None::<[_; 0]>,
|
|
);
|
|
}
|
|
fn assert_insert_coalesce_touching<const N: usize, I, K, V>(
|
|
mut before: RangeBoundsMap<I, K, V>,
|
|
to_insert: (K, V),
|
|
result: Result<&K, OverlapOrTryFromBoundsError>,
|
|
after: Option<[(K, V); N]>,
|
|
) where
|
|
I: Ord + Clone + Debug,
|
|
K: Clone + TryFromBounds<I> + Debug + PartialEq,
|
|
V: Clone + Debug + PartialEq,
|
|
K: RangeBounds<I>,
|
|
{
|
|
let clone = before.clone();
|
|
assert_eq!(
|
|
before.insert_coalesce_touching(to_insert.0, to_insert.1),
|
|
result
|
|
);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[test]
|
|
fn insert_coalesce_overlapping_tests() {
|
|
assert_insert_coalesce_overlapping(basic(), (ii(0, 2), true), Ok(&(ui(4))), Some([
|
|
(ui(4), true),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_insert_coalesce_overlapping(basic(), (ie(14, 16), false), Ok(&ie(14, 16)), Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), false),
|
|
]));
|
|
assert_insert_coalesce_overlapping(basic(), (ii(6, 11), false), Ok(&ei(5, 11)), Some([
|
|
(ui(4), false),
|
|
(ei(5, 11), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_insert_coalesce_overlapping(basic(), (ii(15, 18), true), Ok(&ii(14, 18)), Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ii(14, 18), true),
|
|
]));
|
|
assert_insert_coalesce_overlapping(basic(), (uu(), false), Ok(&uu()), Some([
|
|
(uu(), false),
|
|
]));
|
|
|
|
assert_insert_coalesce_overlapping(special(), (MultiBounds::Inclusive(10, 18), true), Ok(&MultiBounds::Inclusive(8,18)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 18), true),
|
|
]));
|
|
assert_insert_coalesce_overlapping(special(), (MultiBounds::Exclusive(10, 18), true), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_insert_coalesce_overlapping(special(), (MultiBounds::Exclusive(8, 12), true), Ok(&MultiBounds::Inclusive(8, 12)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 12), true),
|
|
]));
|
|
assert_insert_coalesce_overlapping(special(), (MultiBounds::Exclusive(7, 8), false), Ok(&MultiBounds::Exclusive(7,8)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), false),
|
|
(MultiBounds::Inclusive(8, 12), false),
|
|
]));
|
|
assert_insert_coalesce_overlapping(special(), (MultiBounds::Inclusive(7, 8), false), Ok(&MultiBounds::Inclusive(7, 12)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Inclusive(7, 12), false),
|
|
]));
|
|
}
|
|
fn assert_insert_coalesce_overlapping<const N: usize, I, K, V>(
|
|
mut before: RangeBoundsMap<I, K, V>,
|
|
to_insert: (K, V),
|
|
result: Result<&K, TryFromBoundsError>,
|
|
after: Option<[(K, V); N]>,
|
|
) where
|
|
I: Ord + Clone + Debug,
|
|
K: Clone + TryFromBounds<I> + Debug + PartialEq,
|
|
V: Clone + Debug + PartialEq,
|
|
K: RangeBounds<I>,
|
|
{
|
|
let clone = before.clone();
|
|
assert_eq!(
|
|
before.insert_coalesce_overlapping(to_insert.0, to_insert.1),
|
|
result
|
|
);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[test]
|
|
fn insert_coalesce_touching_or_overlapping_tests() {
|
|
assert_insert_coalesce_touching_or_overlapping(RangeBoundsMap::try_from([(1..4, false)]).unwrap(), (-4..1, true), Ok(&(-4..4)), Some([(-4..4, true)]));
|
|
|
|
//copied from insert_coalesce_overlapping_tests
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (ii(0, 2), true), Ok(&(ui(4))), Some([
|
|
(ui(4), true),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (ie(14, 16), false), Ok(&ie(14, 16)), Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ie(14, 16), false),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (ii(6, 11), false), Ok(&ei(5, 11)), Some([
|
|
(ui(4), false),
|
|
(ei(5, 11), false),
|
|
(ie(14, 16), true),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (ii(15, 18), true), Ok(&ii(14, 18)), Some([
|
|
(ui(4), false),
|
|
(ee(5, 7), true),
|
|
(ii(7, 7), false),
|
|
(ii(14, 18), true),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (uu(), false), Ok(&uu()), Some([
|
|
(uu(), false),
|
|
]));
|
|
//the only difference from the insert_coalesce_overlapping
|
|
assert_insert_coalesce_touching_or_overlapping(basic(), (ii(7, 14), false), Ok(&ee(5, 16)), Some([
|
|
(ui(4), false),
|
|
(ee(5, 16), false),
|
|
]));
|
|
|
|
//copied from insert_coalesce_overlapping_tests
|
|
assert_insert_coalesce_touching_or_overlapping(special(), (MultiBounds::Inclusive(10, 18), true), Ok(&MultiBounds::Inclusive(8,18)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 18), true),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(special(), (MultiBounds::Exclusive(10, 18), true), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_insert_coalesce_touching_or_overlapping(special(), (MultiBounds::Exclusive(8, 12), true), Ok(&MultiBounds::Inclusive(8, 12)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Exclusive(7, 8), true),
|
|
(MultiBounds::Inclusive(8, 12), true),
|
|
]));
|
|
assert_insert_coalesce_touching_or_overlapping(special(), (MultiBounds::Exclusive(7, 8), false), Err(TryFromBoundsError), None::<[_; 0]>);
|
|
assert_insert_coalesce_touching_or_overlapping(special(), (MultiBounds::Inclusive(7, 8), false), Ok(&MultiBounds::Inclusive(7, 12)), Some([
|
|
(MultiBounds::Inclusive(4, 6), false),
|
|
(MultiBounds::Inclusive(7, 12), false),
|
|
]));
|
|
//copied from insert_coalesce_touching_tests
|
|
assert_insert_coalesce_touching_or_overlapping(
|
|
special(),
|
|
(MultiBounds::Exclusive(6, 7), true),
|
|
Err(TryFromBoundsError),
|
|
None::<[_; 0]>,
|
|
);
|
|
assert_insert_coalesce_touching_or_overlapping(
|
|
special(),
|
|
(MultiBounds::Exclusive(12, 15), true),
|
|
Err(TryFromBoundsError),
|
|
None::<[_; 0]>,
|
|
);
|
|
}
|
|
fn assert_insert_coalesce_touching_or_overlapping<const N: usize, I, K, V>(
|
|
mut before: RangeBoundsMap<I, K, V>,
|
|
to_insert: (K, V),
|
|
result: Result<&K, TryFromBoundsError>,
|
|
after: Option<[(K, V); N]>,
|
|
) where
|
|
I: Ord + Clone + Debug,
|
|
K: Clone + TryFromBounds<I> + Debug + PartialEq,
|
|
V: Clone + Debug + PartialEq,
|
|
K: RangeBounds<I>,
|
|
{
|
|
let clone = before.clone();
|
|
assert_eq!(
|
|
before.insert_coalesce_touching_or_overlapping(
|
|
to_insert.0,
|
|
to_insert.1
|
|
),
|
|
result
|
|
);
|
|
match after {
|
|
Some(after) => {
|
|
assert_eq!(before, RangeBoundsMap::try_from(after).unwrap())
|
|
}
|
|
None => assert_eq!(before, clone),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn overlaps_tests() {
|
|
for range_bounds1 in all_valid_test_bounds() {
|
|
for range_bounds2 in all_valid_test_bounds() {
|
|
let our_answer = overlaps(&range_bounds1, &range_bounds2);
|
|
|
|
let mathematical_definition_of_overlap =
|
|
NUMBERS_DOMAIN.iter().any(|x| {
|
|
range_bounds1.contains(x) && range_bounds2.contains(x)
|
|
});
|
|
|
|
if our_answer != mathematical_definition_of_overlap {
|
|
dbg!(range_bounds1, range_bounds2);
|
|
dbg!(mathematical_definition_of_overlap, our_answer);
|
|
panic!("Discrepency in overlaps() detected!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn cut_range_bounds_tests() {
|
|
for base in all_valid_test_bounds() {
|
|
for cut in all_valid_test_bounds() {
|
|
let cut_result = cut_range_bounds(&base, &cut);
|
|
|
|
// The definition of a cut is: A && NOT B
|
|
for x in NUMBERS_DOMAIN {
|
|
let result_contains = cut_result.contains(x);
|
|
let base_contains = base.contains(x);
|
|
let cut_contains = cut.contains(x);
|
|
|
|
let invariant =
|
|
result_contains == (base_contains && !cut_contains);
|
|
|
|
if !invariant {
|
|
dbg!(result_contains);
|
|
dbg!(base_contains);
|
|
dbg!(cut_contains);
|
|
|
|
dbg!(base);
|
|
dbg!(cut);
|
|
dbg!(cut_result);
|
|
|
|
dbg!(x);
|
|
|
|
panic!("Invariant Broken!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl<I> CutResult<I> {
|
|
fn contains(&self, point: &I) -> bool
|
|
where
|
|
I: PartialOrd,
|
|
{
|
|
match self {
|
|
CutResult::Nothing => false,
|
|
CutResult::Single(range_bounds) => range_bounds.contains(point),
|
|
CutResult::Double(first_range_bounds, second_range_bounds) => {
|
|
first_range_bounds.contains(point)
|
|
|| second_range_bounds.contains(point)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn touches_tests() {
|
|
for range_bounds1 in all_valid_test_bounds() {
|
|
for range_bounds2 in all_valid_test_bounds() {
|
|
let our_answer = touches(&range_bounds1, &range_bounds2);
|
|
|
|
let mathematical_definition_of_touches =
|
|
NUMBERS_DOMAIN.iter().tuple_windows().any(|(x1, x2)| {
|
|
(range_bounds1.contains(x1)
|
|
&& !range_bounds1.contains(x2)
|
|
&& range_bounds2.contains(x2)
|
|
&& !range_bounds2.contains(x1))
|
|
|| (range_bounds1.contains(x2)
|
|
&& !range_bounds1.contains(x1) && range_bounds2
|
|
.contains(x1) && !range_bounds2.contains(x2))
|
|
});
|
|
|
|
if our_answer != mathematical_definition_of_touches {
|
|
dbg!(range_bounds1, range_bounds2);
|
|
dbg!(mathematical_definition_of_touches, our_answer);
|
|
panic!("Discrepency in touches() detected!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test Helper Functions
|
|
//======================
|
|
fn all_non_overlapping_test_bound_pairs() -> Vec<(TestBounds, TestBounds)> {
|
|
let mut output = Vec::new();
|
|
for test_bounds1 in all_valid_test_bounds() {
|
|
for test_bounds2 in all_valid_test_bounds() {
|
|
if !overlaps(&test_bounds1, &test_bounds2) {
|
|
output.push((test_bounds1, test_bounds2));
|
|
}
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
fn all_valid_test_bounds() -> Vec<TestBounds> {
|
|
let mut output = Vec::new();
|
|
|
|
//bounded-bounded
|
|
output.append(&mut all_finite_bounded_pairs());
|
|
//bounded-unbounded
|
|
for start_bound in all_finite_bounded() {
|
|
output.push((start_bound, u()));
|
|
}
|
|
//unbounded-bounded
|
|
for end_bound in all_finite_bounded() {
|
|
output.push((u(), end_bound));
|
|
}
|
|
//unbounded-unbounded
|
|
output.push(uu());
|
|
|
|
return output;
|
|
}
|
|
|
|
fn all_finite_bounded_pairs() -> Vec<(Bound<u8>, Bound<u8>)> {
|
|
let mut output = Vec::new();
|
|
for i in NUMBERS {
|
|
for j in NUMBERS {
|
|
for i_ex in [false, true] {
|
|
for j_ex in [false, true] {
|
|
if j > i || (j == i && !i_ex && !j_ex) {
|
|
output.push((
|
|
finite_bound(*i, i_ex),
|
|
finite_bound(*j, j_ex),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
fn all_finite_bounded() -> Vec<Bound<u8>> {
|
|
let mut output = Vec::new();
|
|
for i in NUMBERS {
|
|
for j in 0..=1 {
|
|
output.push(finite_bound(*i, j == 1));
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
fn finite_bound(x: u8, included: bool) -> Bound<u8> {
|
|
match included {
|
|
false => Bound::Included(x),
|
|
true => Bound::Excluded(x),
|
|
}
|
|
}
|
|
|
|
fn uu() -> TestBounds {
|
|
(Bound::Unbounded, Bound::Unbounded)
|
|
}
|
|
fn ui(x: u8) -> TestBounds {
|
|
(Bound::Unbounded, Bound::Included(x))
|
|
}
|
|
fn ue(x: u8) -> TestBounds {
|
|
(Bound::Unbounded, Bound::Excluded(x))
|
|
}
|
|
fn iu(x: u8) -> TestBounds {
|
|
(Bound::Included(x), Bound::Unbounded)
|
|
}
|
|
//fn eu(x: u8) -> TestBounds {
|
|
//(Bound::Excluded(x), Bound::Unbounded)
|
|
//}
|
|
fn ii(x1: u8, x2: u8) -> TestBounds {
|
|
(Bound::Included(x1), Bound::Included(x2))
|
|
}
|
|
fn ie(x1: u8, x2: u8) -> TestBounds {
|
|
(Bound::Included(x1), Bound::Excluded(x2))
|
|
}
|
|
fn ei(x1: u8, x2: u8) -> TestBounds {
|
|
(Bound::Excluded(x1), Bound::Included(x2))
|
|
}
|
|
fn ee(x1: u8, x2: u8) -> TestBounds {
|
|
(Bound::Excluded(x1), Bound::Excluded(x2))
|
|
}
|
|
fn u() -> Bound<u8> {
|
|
Bound::Unbounded
|
|
}
|
|
}
|