finished implementing new remove_overlapping() and cut() functions
This commit is contained in:
parent
49320f71aa
commit
1ca53523a0
56
Cargo.lock
generated
56
Cargo.lock
generated
@ -32,10 +32,66 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range_bounds_map"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"either",
|
||||
"ordered-float",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
@ -25,7 +25,7 @@ categories = ["data-structures"]
|
||||
|
||||
[dependencies]
|
||||
either = "1.8.0"
|
||||
ordered-float = "3.4.0"
|
||||
serde = {version = "1.0.148", features = ["derive"]}
|
||||
|
||||
[dev-dependencies]
|
||||
ordered-float = "3.4.0"
|
||||
|
@ -20,6 +20,8 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Bound;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An Ord newtype of [`Bound`] specific to [`start_bound()`].
|
||||
///
|
||||
/// This type is used to circumvent [`BTreeMap`]s (and rust collections
|
||||
@ -31,7 +33,7 @@ use std::ops::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
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub(crate) enum StartBound<T> {
|
||||
/// Mirror of [`Bound::Included`]
|
||||
Included(T),
|
||||
@ -68,6 +70,14 @@ impl<T> StartBound<T> {
|
||||
_ => panic!("unsuitable operation"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_opposite(self) -> StartBound<T> {
|
||||
match self {
|
||||
StartBound::Included(point) => StartBound::Excluded(point),
|
||||
StartBound::Excluded(point) => StartBound::Included(point),
|
||||
_ => panic!("unsuitable operation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for StartBound<T> where T: PartialEq {}
|
||||
|
@ -23,6 +23,7 @@ use std::iter::once;
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
use either::Either;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::bounds::StartBound;
|
||||
|
||||
@ -112,8 +113,11 @@ use crate::bounds::StartBound;
|
||||
///
|
||||
/// [`RangeBounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
||||
/// [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RangeBoundsMap<I, K, V> {
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct RangeBoundsMap<I, K, V>
|
||||
where
|
||||
I: PartialOrd,
|
||||
{
|
||||
starts: BTreeMap<StartBound<I>, (K, V)>,
|
||||
}
|
||||
|
||||
@ -136,6 +140,17 @@ pub enum InsertError {
|
||||
OverlapsPreexisting,
|
||||
}
|
||||
|
||||
/// An error type to represent the possible errors from the
|
||||
/// [`RangeBoundsMap::cut()`] function.
|
||||
pub enum CutError {
|
||||
/// When cutting out a `RangeBounds` from another `RangeBounds`
|
||||
/// you may need to change the base `RangeBounds`' start and end
|
||||
/// `Bound`s, when these need to change values that the underlying
|
||||
/// `K`: `RangeBounds` type can't handle then this error is
|
||||
/// returned.
|
||||
NonConvertibleRangeBoundsProduced,
|
||||
}
|
||||
|
||||
impl<I, K, V> RangeBoundsMap<I, K, V>
|
||||
where
|
||||
K: RangeBounds<I>,
|
||||
@ -219,8 +234,8 @@ where
|
||||
//optimisation fix this without cloning
|
||||
let start = StartBound::from(range_bounds.start_bound().cloned());
|
||||
//optimisation fix this without cloning
|
||||
let end =
|
||||
StartBound::from(range_bounds.end_bound().cloned()).into_end_bound();
|
||||
let end = StartBound::from(range_bounds.end_bound().cloned())
|
||||
.into_end_bound();
|
||||
|
||||
if start > end {
|
||||
panic!("Invalid search range bounds!");
|
||||
@ -283,7 +298,7 @@ where
|
||||
pub fn overlapping<Q>(
|
||||
&self,
|
||||
search_range_bounds: &Q,
|
||||
) -> impl Iterator<Item = (&K, &V)>
|
||||
) -> impl DoubleEndedIterator<Item = (&K, &V)>
|
||||
where
|
||||
Q: RangeBounds<I>,
|
||||
{
|
||||
@ -453,8 +468,8 @@ where
|
||||
.next();
|
||||
}
|
||||
|
||||
/// Returns an iterator over every (`RangeBounds`, `Value`) pair in the map in
|
||||
/// ascending order.
|
||||
/// Returns an iterator over every (`RangeBounds`, `Value`) pair
|
||||
/// in the map in ascending order.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -474,22 +489,106 @@ where
|
||||
/// assert_eq!(iter.next(), Some((&(8..100), &false)));
|
||||
/// assert_eq!(iter.next(), None);
|
||||
/// ```
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (&K, &V)> {
|
||||
self.starts.iter().map(|(_, (key, value))| (key, value))
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
///```
|
||||
/// panic!()
|
||||
/// ```
|
||||
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<StartBound<I>> = self
|
||||
.overlapping(range_bounds)
|
||||
.map(|(key, _)| (StartBound::from(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 converted into the `K` type with `TryFrom<(Bound,
|
||||
/// Bound)>` then a `CutError` will be returned.
|
||||
/// ```
|
||||
/// panic!();
|
||||
/// ```
|
||||
pub fn cut<Q>(&mut self, range_bounds: &Q) -> Result<(), CutError>
|
||||
where
|
||||
Q: RangeBounds<I>,
|
||||
K: TryFrom<(Bound<I>, Bound<I>)>,
|
||||
V: Clone,
|
||||
{
|
||||
// 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.remove_overlapping(range_bounds);
|
||||
|
||||
let first_last = (overlapping.next(), overlapping.next_back());
|
||||
|
||||
let mut attempt_insert = |section, value| -> Result<(), CutError> {
|
||||
match K::try_from(section)
|
||||
.map_err(|_| CutError::NonConvertibleRangeBoundsProduced)
|
||||
{
|
||||
Ok(key) => {
|
||||
self.insert(key, value).unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
Err(cut_error) => return Err(cut_error),
|
||||
}
|
||||
};
|
||||
|
||||
match first_last {
|
||||
(Some(first), Some(last)) => {
|
||||
match cut_range_bounds(&first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(left_section) => {
|
||||
attempt_insert(left_section, first.1)?;
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
match cut_range_bounds(&last.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(right_section) => {
|
||||
attempt_insert(right_section, last.1)?;
|
||||
}
|
||||
CutResult::Double(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
(Some(first), None) => {
|
||||
match cut_range_bounds(&first.0, range_bounds) {
|
||||
CutResult::Nothing => {}
|
||||
CutResult::Single(section) => {
|
||||
attempt_insert(section, first.1)?;
|
||||
}
|
||||
CutResult::Double(left_section, right_section) => {
|
||||
attempt_insert(left_section, first.1.clone())?;
|
||||
attempt_insert(right_section, first.1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
(None, Some(_)) => unreachable!(),
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,6 +608,75 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
enum CutResult<I> {
|
||||
Nothing,
|
||||
Single((Bound<I>, Bound<I>)),
|
||||
Double((Bound<I>, Bound<I>), (Bound<I>, Bound<I>)),
|
||||
}
|
||||
|
||||
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) {
|
||||
return CutResult::Nothing;
|
||||
}
|
||||
|
||||
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 StartBound::from(cut_start_bound)
|
||||
> StartBound::from(base_start_bound)
|
||||
{
|
||||
false => None,
|
||||
true => Some((
|
||||
base_start_bound.cloned(),
|
||||
Bound::from(StartBound::from(cut_start_bound).into_opposite())
|
||||
.cloned(),
|
||||
)),
|
||||
};
|
||||
let right_section = match StartBound::from(cut_end_bound).into_end_bound()
|
||||
< StartBound::from(base_end_bound).into_end_bound()
|
||||
{
|
||||
false => None,
|
||||
true => Some((
|
||||
base_start_bound.cloned(),
|
||||
Bound::from(StartBound::from(cut_start_bound).into_opposite())
|
||||
.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) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
fn overlaps<I, A, B>(a: &A, b: &B) -> bool
|
||||
where
|
||||
A: RangeBounds<I>,
|
||||
|
@ -19,6 +19,8 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::range_bounds_map::RangeBoundsMap;
|
||||
use crate::InsertError;
|
||||
|
||||
@ -89,8 +91,11 @@ use crate::InsertError;
|
||||
///
|
||||
/// [`RangeBounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
||||
/// [`BTreeSet`]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RangeBoundsSet<I, K> {
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct RangeBoundsSet<I, K>
|
||||
where
|
||||
I: PartialOrd,
|
||||
{
|
||||
map: RangeBoundsMap<I, K, ()>,
|
||||
}
|
||||
|
||||
|
15
todo.txt
15
todo.txt
@ -1,3 +1,13 @@
|
||||
- make sure all returned iterators are DoubleEndedIterators and the
|
||||
documentation mentions it
|
||||
|
||||
- write more tests for more complicated functions
|
||||
|
||||
- write docs for everything again
|
||||
|
||||
- check every function that takes range_Bounds to make sure they panic
|
||||
on invalid range_bounds
|
||||
|
||||
- use it in robot_Sweet_graph for a bit before publishing
|
||||
|
||||
- add issues to github for all the caveats and cloned() optimisations
|
||||
@ -7,10 +17,9 @@
|
||||
- run and fix cargo clippy
|
||||
- take a look around idiomatic rust for a bit first
|
||||
|
||||
- fill out github meta data
|
||||
|
||||
- update lines of code figures on docs
|
||||
|
||||
- PUBLISH
|
||||
|
||||
- add links to [`RangeBoundsSet`] and map after docs.rs is live with the docs
|
||||
- add links to [`RangeBoundsSet`] and map after docs.rs is live with
|
||||
the docs, and generally check for dead links on docs and readme
|
||||
|
Loading…
x
Reference in New Issue
Block a user