finished implementing some basic functions copied from rangemap
This commit is contained in:
parent
1bd457f2d4
commit
d3cef3c725
@ -6,5 +6,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
derive-new = "0.5"
|
||||
either = "1.8.0"
|
||||
|
@ -1,8 +1,9 @@
|
||||
#![feature(is_some_and)]
|
||||
pub mod bounds;
|
||||
pub mod range_bounds;
|
||||
pub mod range_bounds_set;
|
||||
pub mod range_bounds_map;
|
||||
pub mod btree_ext;
|
||||
pub mod overlapping_tests;
|
||||
|
||||
pub use std::ops::RangeBounds as StdRangeBounds;
|
||||
pub use std::ops::Bound as StdBound;
|
||||
|
22
range_bounds_set/src/overlapping_tests.rs
Normal file
22
range_bounds_set/src/overlapping_tests.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::range_bounds_map::RangeBoundsMap;
|
||||
// So there are a lot of permutations for overlaps checks: like 100's even
|
||||
// for less than 3 ranges inside the range_bounds_map
|
||||
// Hence the strategy since I want thorough testing at least for
|
||||
// T = { 0, 1, 2 } where T is the number of ranges in the rangeset
|
||||
// upon query, and I want every valid (maintains invariant of no
|
||||
// overlaps (only relevant for T=2)) permutation of query to
|
||||
// interval set to have it's own test.
|
||||
//
|
||||
// This strategy is open-ended if anyone feels like manually
|
||||
// inputing the T=3 cases ;p
|
||||
//
|
||||
// And so rather than figuring all the valid cases manually I
|
||||
// wrote a script to do it for me, and then I also wrote a cli
|
||||
// prorgram to ask me what the answer should be for each case
|
||||
// then the script generated the rust tests you see below
|
||||
|
||||
// GENERATED
|
||||
// ==========================================================
|
||||
}
|
@ -9,6 +9,7 @@ where
|
||||
fn get_pair(&self) -> (StartBound<&T>, EndBound<&T>) {
|
||||
(self.start_bound(), self.end_bound())
|
||||
}
|
||||
fn dummy(start_bound: StartBound<T>, end_bound: EndBound<T>) -> Self;
|
||||
fn contains(&self, item: &T) -> bool {
|
||||
(match self.start_bound() {
|
||||
StartBound::Included(start) => start <= item,
|
||||
|
117
range_bounds_set/src/range_bounds_map.rs
Normal file
117
range_bounds_set/src/range_bounds_map.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter::once;
|
||||
|
||||
use either::Either;
|
||||
|
||||
use crate::bounds::{EndBound, StartBound};
|
||||
use crate::btree_ext::BTreeMapExt;
|
||||
use crate::range_bounds::RangeBounds;
|
||||
use crate::StdBound;
|
||||
|
||||
//todo switch to slot map thingy
|
||||
#[derive(Default)]
|
||||
pub struct RangeBoundsMap<I, K, V> {
|
||||
starts: BTreeMap<StartBound<I>, (K, V)>,
|
||||
}
|
||||
|
||||
impl<I, K, V> RangeBoundsMap<I, K, V>
|
||||
where
|
||||
K: RangeBounds<I>,
|
||||
I: Ord + Clone,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
RangeBoundsMap {
|
||||
starts: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
//returns Err(()) if the given range overlaps another range
|
||||
//does not coalesce ranges if they touch
|
||||
pub fn insert(&mut self, range_bounds: K, value: V) -> Result<(), ()> {
|
||||
if self.overlaps(&range_bounds) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
self.starts
|
||||
.insert(range_bounds.start_bound().cloned(), (range_bounds, value));
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn overlaps(&self, search_range_bounds: &K) -> bool {
|
||||
self.overlapping(search_range_bounds).next().is_some()
|
||||
}
|
||||
|
||||
pub fn overlapping(
|
||||
&self,
|
||||
search_range_bounds: &K,
|
||||
) -> Either<impl Iterator<Item = (&K, &V)>, impl Iterator<Item = (&K, &V)>>
|
||||
{
|
||||
let start_range_bounds = (
|
||||
//Included is lossless regarding meta-bounds searches
|
||||
StdBound::Included(search_range_bounds.start_bound().cloned()),
|
||||
StdBound::Included(StartBound::from(
|
||||
search_range_bounds.end_bound().cloned(),
|
||||
)),
|
||||
);
|
||||
//this range will hold all the ranges we want except possibly
|
||||
//the first RangeBound in the range
|
||||
let most_range_bounds = self.starts.range(start_range_bounds);
|
||||
|
||||
if let Some(missing_entry @ (_, (possible_missing_range_bounds, _))) =
|
||||
//Excluded is lossless regarding meta-bounds searches
|
||||
//because we don't want equal bound as they would have be
|
||||
//coverded in the previous step
|
||||
self.starts.next_below_upper_bound(StdBound::Excluded(
|
||||
//optimisation fix this without cloning
|
||||
&search_range_bounds.start_bound().cloned(),
|
||||
)) {
|
||||
if possible_missing_range_bounds.overlaps(&search_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)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get(&self, point: &I) -> Option<&V> {
|
||||
self.get_key_value(point).map(|(_, value)| value)
|
||||
}
|
||||
|
||||
pub fn get_key_value(&self, point: &I) -> Option<(&K, &V)> {
|
||||
//a zero-range included-included range is equivalent to a point
|
||||
return self
|
||||
.overlapping(&K::dummy(
|
||||
StartBound::Included(point.clone()),
|
||||
EndBound::Included(point.clone()),
|
||||
))
|
||||
.next();
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, point: &I) -> Option<&mut V> {
|
||||
if let Some(overlapping_start_bound) =
|
||||
self.get_key_value(point).map(|(key, _)| key.start_bound())
|
||||
{
|
||||
return self
|
||||
.starts
|
||||
//optimisation fix this without cloning
|
||||
.get_mut(&overlapping_start_bound.cloned())
|
||||
.map(|(_, value)| value);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: &I) -> bool {
|
||||
self.get(point).is_some()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
||||
self.starts.iter().map(|(_, (key, value))| (key, value))
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::iter::once;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use derive_new::new;
|
||||
use either::Either;
|
||||
|
||||
use crate::bounds::{EndBound, StartBound};
|
||||
use crate::btree_ext::BTreeMapExt;
|
||||
use crate::range_bounds::RangeBounds;
|
||||
use crate::StdBound;
|
||||
|
||||
type Id = u128;
|
||||
|
||||
//todo switch to slot map thingy
|
||||
#[derive(new)]
|
||||
pub struct RangeBoundsSet<T, I> {
|
||||
#[new(value="BTreeMap::new()")]
|
||||
starts: BTreeMap<StartBound<I>, T>,
|
||||
|
||||
#[new(default)]
|
||||
id: u128,
|
||||
}
|
||||
|
||||
impl<T, I> RangeBoundsSet<T, I>
|
||||
where
|
||||
T: RangeBounds<I>,
|
||||
I: Ord + Clone,
|
||||
{
|
||||
//returns Err(()) if the inserting the given range overlaps another range
|
||||
//coalesces ranges if they touch
|
||||
pub fn insert(&mut self, range_bounds: T) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn raw_insert(&mut self, range_bounds: T) {}
|
||||
|
||||
pub fn overlapping(
|
||||
&self,
|
||||
search_range_bounds: T,
|
||||
) -> Either<impl Iterator<Item = &T>, impl Iterator<Item = &T>> {
|
||||
let start_range_bounds = (
|
||||
//Included is lossless regarding meta-bounds searches
|
||||
StdBound::Included(search_range_bounds.start_bound().cloned()),
|
||||
StdBound::Included(StartBound::from(
|
||||
search_range_bounds.end_bound().cloned(),
|
||||
)),
|
||||
);
|
||||
//this range will hold all the ranges we want except possibly
|
||||
//the first RangeBound in the range
|
||||
let most_range_bounds = self.starts.range(start_range_bounds);
|
||||
|
||||
if let Some(missing_entry @ (_, possible_missing_range_bounds)) =
|
||||
//Excluded is lossless regarding meta-bounds searches
|
||||
//because we don't want equal bound as they would have be
|
||||
//coverded in the previous step
|
||||
self.starts.next_below_upper_bound(StdBound::Excluded(
|
||||
//optimisation fix this without cloning
|
||||
&search_range_bounds.start_bound().cloned(),
|
||||
)) {
|
||||
if possible_missing_range_bounds.overlaps(&search_range_bounds) {
|
||||
return Either::Left(
|
||||
once(missing_entry).chain(most_range_bounds).map(|x| x.1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Either::Right(most_range_bounds.map(|x| x.1));
|
||||
}
|
||||
|
||||
pub fn get(&self, point: &I) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
// So there are a lot of permutations for overlaps checks: like 100's even
|
||||
// for less than 3 ranges inside the see my picture in
|
||||
// notes/ for an idea
|
||||
// Hence the strategy since I want thorough testing at least for
|
||||
// T = { 0, 1, 2 } where T is the number of ranges in the rangeset
|
||||
// upon query, and I want every valid (maintains invariant of no
|
||||
// overlaps (only relevant for T=2)) permutation of query to
|
||||
// interval set to have it's own test.
|
||||
//
|
||||
// This strategy is open-ended if anyone feels like manually
|
||||
// inputing the T=3 cases ;p
|
||||
//
|
||||
// And so rather than figuring all the valid cases manually I
|
||||
// wrote a script to do it for me, and then I also wrote a helper
|
||||
// script to streamline the manual input process. The script
|
||||
// essentially enumerates all of the generated cases and asks me
|
||||
// to input the expected
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// Rusts Bounds types are overly ambiguous in RangeBouns, it
|
||||
// should return StartBound and EndBound so that you can
|
||||
// implement Ord on them
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user