finished implementing some basic functions copied from rangemap

This commit is contained in:
ripytide 2022-11-24 17:57:27 +00:00
parent 1bd457f2d4
commit d3cef3c725
6 changed files with 142 additions and 112 deletions

View File

@ -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"

View File

@ -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;

View 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
// ==========================================================
}

View File

@ -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,

View 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))
}
}

View File

@ -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
}