copy lib.rs to readme and panic on invalid range in overlapping() and fill out cargo manifest metadata
This commit is contained in:
parent
fd30485ee8
commit
c801b896b4
20
Cargo.toml
20
Cargo.toml
@ -1,9 +1,27 @@
|
||||
[package]
|
||||
name = "range_bounds_map"
|
||||
version = "0.0.1"
|
||||
authors = ["James Forster"]
|
||||
edition = "2021"
|
||||
description = """
|
||||
This crate provides `RangeBoundsMap` and `RangeBoundsSet`.
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
`RangeBoundsMap` is similar to `BTreeMap` except `RangeBoundsMap`
|
||||
uses any type that implements the `RangeBounds` trait as keys, while
|
||||
maintaining two invariants:
|
||||
- No two keys may overlap
|
||||
- A keys' `start_bound()` <= its `end_bound()`
|
||||
|
||||
`RangeBoundsSet` is like `RangeBoundsMap` except it
|
||||
uses `()` as values, as `BTreeSet` does for `BTreeMap`
|
||||
"""
|
||||
documentation = "https://docs.rs/range_bounds_map"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/ripytide/range_bounds_map"
|
||||
repository = "https://github.com/ripytide/range_bounds_map"
|
||||
license = "GPL-3.0-or-later"
|
||||
keywords = ["data-structures", "map", "data", "library"]
|
||||
categories = ["data-structures"]
|
||||
|
||||
[dependencies]
|
||||
either = "1.8.0"
|
||||
|
182
README.md
182
README.md
@ -1,32 +1,176 @@
|
||||
- add documentation to EVERYTHING
|
||||
- test cargo docs and make sure docs.rs homepage also points toward
|
||||
the readme
|
||||
# range_bounds_map
|
||||
|
||||
- add issues to github for all the caveats and cloned() optimisations
|
||||
This crate provides [`RangeBoundsMap`] and [`RangeBoundsSet`].
|
||||
|
||||
- add examples for all functions in rust doc comments and test them with cargo doc-test
|
||||
[`RangeBoundsMap`] is similar to [`BTreeMap`] except [`RangeBoundsMap`]
|
||||
uses any type that implements the [`RangeBounds`] trait as keys, while
|
||||
maintaining two invariants:
|
||||
|
||||
- add github badges including lib.rs
|
||||
- make logo
|
||||
- No two keys may overlap
|
||||
- A keys' [`start_bound()`] <= its [`end_bound()`]
|
||||
|
||||
- write cargo toml attributes out full including badges
|
||||
[`RangeBoundsSet`] is like [`RangeBoundsMap`] except it
|
||||
uses `()` as values, as [`BTreeSet`] does for [`BTreeMap`]
|
||||
|
||||
- fix todo for checking insert reverse ranges and invalid ones
|
||||
# Example using [`Range`]s
|
||||
|
||||
- use it in robot_Sweet_graph for a bit before publishing
|
||||
```rust
|
||||
use range_bounds_map::RangeBoundsMap;
|
||||
|
||||
- copy docs from set to map and update appropriately
|
||||
let mut range_bounds_map = RangeBoundsMap::new();
|
||||
|
||||
- update lines of code figures on docs
|
||||
range_bounds_map.insert(0..5, true);
|
||||
range_bounds_map.insert(5..10, false);
|
||||
|
||||
- make all functions' trait bounds specific instead of at impl level
|
||||
assert_eq!(range_bounds_map.overlaps(&(-2..12)), true);
|
||||
assert_eq!(range_bounds_map.contains_point(&20), false);
|
||||
assert_eq!(range_bounds_map.contains_point(&5), true);
|
||||
```
|
||||
|
||||
- review caveats again
|
||||
# Example using a custom [`RangeBounds`] type
|
||||
|
||||
- run cargo fmt
|
||||
- run and fix cargo clippy
|
||||
- take a look around idiomatic rust for a bit first
|
||||
```rust
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
- add CI
|
||||
use range_bounds_map::RangeBoundsMap;
|
||||
|
||||
- PUBLISH
|
||||
#[derive(Debug)]
|
||||
enum Reservation {
|
||||
// Start, End (Inclusive-Inclusive)
|
||||
Finite(u8, u8),
|
||||
// Start (Exclusive)
|
||||
Infinite(u8),
|
||||
}
|
||||
|
||||
// First, we need to implement RangeBounds
|
||||
impl RangeBounds<u8> for Reservation {
|
||||
fn start_bound(&self) -> Bound<&u8> {
|
||||
match self {
|
||||
Reservation::Finite(start, _) => {
|
||||
Bound::Included(start)
|
||||
}
|
||||
Reservation::Infinite(start) => {
|
||||
Bound::Excluded(start)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn end_bound(&self) -> Bound<&u8> {
|
||||
match self {
|
||||
Reservation::Finite(_, end) => Bound::Included(end),
|
||||
Reservation::Infinite(_) => Bound::Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next we can create a custom typed RangeBoundsMap
|
||||
let reservation_map = RangeBoundsMap::try_from([
|
||||
(Reservation::Finite(10, 20), "Ferris".to_string()),
|
||||
(Reservation::Infinite(20), "Corro".to_string()),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
for (reservation, name) in reservation_map.overlapping(&(16..17))
|
||||
{
|
||||
println!(
|
||||
"{name} has reserved {reservation:?} inside the range 16..17"
|
||||
);
|
||||
}
|
||||
|
||||
for (reservation, name) in reservation_map.iter() {
|
||||
println!("{name} has reserved {reservation:?}");
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
reservation_map.overlaps(&Reservation::Infinite(0)),
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
# How
|
||||
|
||||
Most of the [`RangeBounds`]-specific methods on [`RangeBoundsMap`]
|
||||
utilize the [`RangeBoundsMap::overlapping()`] method which
|
||||
internally uses [`BTreeMap`]'s [`range()`] function. To allow
|
||||
using [`range()`] for this purpose a newtype wrapper is wrapped
|
||||
around the [`start_bound()`]s so that we can apply our custom [`Ord`]
|
||||
implementation onto all the [`start_bound()`]s.
|
||||
|
||||
# Improvements/Caveats
|
||||
|
||||
There are a few issues I can think of with this implementation,
|
||||
each of them are documented as GitHub Issues. If you would like
|
||||
any of these features added, drop a comment in a respective GitHub
|
||||
Issue (or even open a new one) and I'd be happy to implement it.
|
||||
|
||||
To summarise:
|
||||
|
||||
- No coalescing insert functions, yet
|
||||
- No `gaps()` iterator function, yet
|
||||
- Missing some functions common to BTreeMap and Set like:
|
||||
- `clear()`
|
||||
- `is_subset()`
|
||||
- etc... a bunch more
|
||||
- Sub-optimal use of unnecessary `cloned()` just to placate the borrow checker
|
||||
- The library needs some benchmarks
|
||||
- Insert should panic on "bad" Zero RangeBounds like when start_bound >
|
||||
end_bound or Excluded(x)-Excluded(x) Excluded(x)-Included(x) Included(x)-Excluded(x)
|
||||
- The data structures are lacking a lot of useful traits, such as:
|
||||
- Serde: Serialize and Deserialize
|
||||
- FromIterator
|
||||
- IntoIterator
|
||||
- Probably a bunch more
|
||||
|
||||
# Credit
|
||||
|
||||
I originally came up with the `StartBound`: [`Ord`] bodge on my
|
||||
own, however, I later stumbled across [`rangemap`] which also used
|
||||
a `StartBound`: [`Ord`] bodge. [`rangemap`] then became my main
|
||||
source of inspiration. The aim for my library was to become a more
|
||||
generic superset of [`rangemap`], following from
|
||||
[this issue](https://github.com/jeffparsons/rangemap/issues/56) and
|
||||
[this pull request](https://github.com/jeffparsons/rangemap/pull/57)
|
||||
in which I changed [`rangemap`]'s [`RangeMap`] to use
|
||||
[`RangeBounds`]s as keys before I realized it might be easier and
|
||||
simpler to just write it all from scratch. Which ended up working
|
||||
really well with some simplifications I made which ended up
|
||||
resulting in much less code (~600 lines over `rangemap`'s ~2700)
|
||||
|
||||
# Similar Crates
|
||||
|
||||
Here are some relevant crates I found whilst searching around the
|
||||
topic area:
|
||||
|
||||
- <https://docs.rs/rangemap>
|
||||
Very similar to this crate but can only use [`Range`]s and
|
||||
[`RangeInclusive`]s as keys in it's `map` and `set` structs (separately).
|
||||
- <https://docs.rs/ranges>
|
||||
Cool library for fully-generic ranges (unlike std::ops ranges), along
|
||||
with a `Ranges` datastructure for storing them (Vec-based
|
||||
unfortunately)
|
||||
- <https://docs.rs/intervaltree>
|
||||
Allows overlapping intervals but is immutable unfortunately
|
||||
- <https://docs.rs/nonoverlapping_interval_tree>
|
||||
Very similar to rangemap except without a `gaps()` function and only
|
||||
for [`Range`]s and not [`RangeInclusive`]s. And also no fancy coalescing
|
||||
functions.
|
||||
- <https://docs.rs/unbounded-interval-tree>
|
||||
A data structure based off of a 2007 published paper! It supports any
|
||||
RangeBounds as keys too, except it is implemented with a non-balancing
|
||||
`Box<Node>` based tree, however it also supports overlapping
|
||||
RangeBounds which my library does not.
|
||||
- <https://docs.rs/rangetree>
|
||||
I'm not entirely sure what this library is or isn't, but it looks like
|
||||
a custom red-black tree/BTree implementation used specifically for a
|
||||
Range Tree. Interesting but also quite old (5 years) and uses
|
||||
unsafe.
|
||||
|
||||
[`btreemap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
|
||||
[`btreeset`]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
|
||||
[`rangebounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
||||
[`start_bound()`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html#tymethod.start_bound
|
||||
[`end_bound()`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html#tymethod.end_bound
|
||||
[`range`]: https://doc.rust-lang.org/std/ops/struct.Range.html
|
||||
[`range()`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.range
|
||||
[`rangemap`]: https://docs.rs/rangemap/latest/rangemap/
|
||||
[`rangeinclusivemap`]: https://docs.rs/rangemap/latest/rangemap/inclusive_map/struct.RangeInclusiveMap.html#
|
||||
[`rangeinclusive`]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
|
||||
|
@ -103,7 +103,9 @@ use crate::bounds::StartBound;
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// map.get_range_bounds_value_at_point(&NotNan::new(2.0).unwrap()),
|
||||
/// map.get_range_bounds_value_at_point(
|
||||
/// &NotNan::new(2.0).unwrap()
|
||||
/// ),
|
||||
/// Some((&ExEx::new(0.0, 5.0), &8))
|
||||
/// );
|
||||
/// ```
|
||||
@ -237,8 +239,6 @@ where
|
||||
where
|
||||
Q: RangeBounds<I>,
|
||||
{
|
||||
//todo panic on invalid search range
|
||||
|
||||
//optimisation fix this without cloning
|
||||
let start =
|
||||
StartBound::from(search_range_bounds.start_bound().cloned());
|
||||
@ -246,6 +246,10 @@ where
|
||||
let end = StartBound::from(search_range_bounds.end_bound().cloned())
|
||||
.as_end_bound();
|
||||
|
||||
if start > end {
|
||||
panic!("Invalid search range bounds!");
|
||||
}
|
||||
|
||||
let start_range_bounds = (
|
||||
//Included is lossless regarding meta-bounds searches
|
||||
//which is what we want
|
||||
@ -375,9 +379,18 @@ where
|
||||
/// ])
|
||||
/// .unwrap();
|
||||
///
|
||||
/// assert_eq!(range_bounds_map.get_range_bounds_value_at_point(&3), Some((&(1..4), &false)));
|
||||
/// assert_eq!(range_bounds_map.get_range_bounds_value_at_point(&4), Some((&(4..8), &true)));
|
||||
/// assert_eq!(range_bounds_map.get_range_bounds_value_at_point(&101), None);
|
||||
/// assert_eq!(
|
||||
/// range_bounds_map.get_range_bounds_value_at_point(&3),
|
||||
/// Some((&(1..4), &false))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// range_bounds_map.get_range_bounds_value_at_point(&4),
|
||||
/// Some((&(4..8), &true))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// range_bounds_map.get_range_bounds_value_at_point(&101),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
pub fn get_range_bounds_value_at_point(
|
||||
&self,
|
||||
|
17
todo.txt
Normal file
17
todo.txt
Normal file
@ -0,0 +1,17 @@
|
||||
- copy lib.rs to the Readme
|
||||
|
||||
- add github badges including lib.rs
|
||||
- make logo
|
||||
|
||||
- update lines of code figures on docs
|
||||
|
||||
- use it in robot_Sweet_graph for a bit before publishing
|
||||
|
||||
- add issues to github for all the caveats and cloned() optimisations
|
||||
- review caveats again
|
||||
|
||||
- run cargo fmt
|
||||
- run and fix cargo clippy
|
||||
- take a look around idiomatic rust for a bit first
|
||||
|
||||
- PUBLISH
|
Loading…
x
Reference in New Issue
Block a user