commit
c65afb8237
4
.github/workflows/gitlab.yaml
vendored
4
.github/workflows/gitlab.yaml
vendored
@ -10,12 +10,12 @@ jobs:
|
||||
- name: Mirror and run GitLab CI
|
||||
uses: SvanBoxel/gitlab-mirror-and-ci-action@master
|
||||
with:
|
||||
args: "https://gitlab.com/ripytide/range_bounds_map"
|
||||
args: "https://gitlab.com/ripytide/discrete_range_map"
|
||||
env:
|
||||
FOLLOW_TAGS: "false"
|
||||
FORCE_PUSH: "false"
|
||||
GITLAB_HOSTNAME: "gitlab.com"
|
||||
GITLAB_USERNAME: "ripytide"
|
||||
GITLAB_PASSWORD: ${{ secrets.GITLAB_PASSWORD }}
|
||||
GITLAB_PROJECT_ID: "44894209"
|
||||
GITLAB_PROJECT_ID: "45384105"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -2,12 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "btree_monstrousity"
|
||||
version = "0.0.4"
|
||||
@ -40,6 +34,17 @@ version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "discrete_range_map"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"btree_monstrousity",
|
||||
"either",
|
||||
"itertools",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
@ -47,21 +52,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -103,17 +99,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range_bounds_map"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"btree_monstrousity",
|
||||
"either",
|
||||
"ordered-float",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
|
15
Cargo.toml
15
Cargo.toml
@ -1,16 +1,17 @@
|
||||
[package]
|
||||
name = "range_bounds_map"
|
||||
name = "discrete_range_map"
|
||||
version = "0.4.0"
|
||||
authors = ["James Forster <james.forsterer@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = """
|
||||
This crate provides RangeBoundsMap and RangeBoundsSet, Data
|
||||
Structures for storing non-overlapping intervals based of BTreeMap.
|
||||
This crate provides [`DiscreteRangeMap`] and [`DiscreteRangeSet`],
|
||||
Data Structures for storing non-overlapping discrete intervals based
|
||||
of [`BTreeMap`].
|
||||
"""
|
||||
documentation = "https://docs.rs/range_bounds_map"
|
||||
documentation = "https://docs.rs/discrete_range_map"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/ripytide/range_bounds_map"
|
||||
repository = "https://github.com/ripytide/range_bounds_map"
|
||||
homepage = "https://github.com/ripytide/discrete_range_map"
|
||||
repository = "https://github.com/ripytide/discrete_range_map"
|
||||
license = "AGPL-3.0-or-later"
|
||||
keywords = ["data-structures", "map", "data", "library"]
|
||||
categories = ["data-structures"]
|
||||
@ -19,7 +20,7 @@ categories = ["data-structures"]
|
||||
serde = {version = "1.0.148", features = ["derive"]}
|
||||
btree_monstrousity = {version ="0.0.4", features = ["btree_drain_filter", "btree_cursors"]}
|
||||
either = "1.8.1"
|
||||
itertools = "0.10.5"
|
||||
|
||||
[dev-dependencies]
|
||||
ordered-float = "3.4.0"
|
||||
pretty_assertions = "1.3.0"
|
||||
|
169
README.md
169
README.md
@ -1,76 +1,81 @@
|
||||
# range_bounds_map
|
||||
# discrete_range_map
|
||||
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
[](https://docs.rs/range_bounds_map)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
[](https://docs.rs/discrete_range_map)
|
||||
[](https://github.com/ripytide)
|
||||
[](https://crates.io/crates/range_bounds_map)
|
||||
[](https://crates.io/crates/discrete_range_map)
|
||||
|
||||
<p align="center">
|
||||
<img src="logo.png" alt="range_bounds_map_logo" width="350">
|
||||
<img src="logo.png" alt="discrete_range_map_logo" width="350">
|
||||
</p>
|
||||
|
||||
This crate provides [`DiscreteRangeMap`] and [`DiscreteRangeSet`],
|
||||
Data Structures for storing non-overlapping discrete intervals based
|
||||
off [`BTreeMap`].
|
||||
|
||||
## Example using [`Range`]s
|
||||
## You must implement `Copy`
|
||||
|
||||
Due to implementation complications with non-`Copy` types the
|
||||
datastructures currently require both the range type and the points
|
||||
the ranges are over to be `Copy`.
|
||||
|
||||
## Example using an Inclusive-Exclusive range
|
||||
|
||||
```rust
|
||||
use range_bounds_map::RangeBoundsMap;
|
||||
use discrete_range_map::test_ranges::ie;
|
||||
use discrete_range_map::DiscreteRangeMap;
|
||||
|
||||
let mut map = RangeBoundsMap::new();
|
||||
let mut map = DiscreteRangeMap::new();
|
||||
|
||||
map.insert_strict(0..5, true);
|
||||
map.insert_strict(5..10, false);
|
||||
map.insert_strict(ie(0, 5), true);
|
||||
map.insert_strict(ie(5, 10), false);
|
||||
|
||||
assert_eq!(map.overlaps(&(-2..12)), true);
|
||||
assert_eq!(map.contains_point(&20), false);
|
||||
assert_eq!(map.contains_point(&5), true);
|
||||
assert_eq!(map.overlaps(ie(-2, 12)), true);
|
||||
assert_eq!(map.contains_point(20), false);
|
||||
assert_eq!(map.contains_point(5), true);
|
||||
```
|
||||
|
||||
## Example using a custom [`RangeBounds`] type
|
||||
## Example using a custom range type
|
||||
|
||||
```rust
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
use discrete_range_map::test_ranges::ie;
|
||||
use discrete_range_map::DiscreteRangeMap;
|
||||
use discrete_range_map::FiniteRange;
|
||||
|
||||
use range_bounds_map::RangeBoundsMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Reservation {
|
||||
// Start, End (Inclusive-Inclusive)
|
||||
Finite(u8, u8),
|
||||
// Start (Exclusive)
|
||||
Infinite(u8),
|
||||
// Start, End (Inclusive-Exclusive)
|
||||
Finite(i8, i8),
|
||||
// Start (Inclusive-Forever)
|
||||
Infinite(i8),
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
// First, we need to implement FiniteRange
|
||||
impl FiniteRange<i8> for Reservation {
|
||||
fn start(&self) -> i8 {
|
||||
match self {
|
||||
Reservation::Finite(start, _) => *start,
|
||||
Reservation::Infinite(start) => *start,
|
||||
}
|
||||
}
|
||||
fn end(&self) -> i8 {
|
||||
match self {
|
||||
//the end is exclusive so we take off 1 with checking
|
||||
//for compile time error overflow detection
|
||||
Reservation::Finite(_, end) => end.checked_sub(1).unwrap(),
|
||||
Reservation::Infinite(_) => i8::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next we can create a custom typed RangeBoundsMap
|
||||
let reservation_map = RangeBoundsMap::try_from([
|
||||
// Next we can create a custom typed DiscreteRangeMap
|
||||
let reservation_map = DiscreteRangeMap::from_slice_strict([
|
||||
(Reservation::Finite(10, 20), "Ferris".to_string()),
|
||||
(Reservation::Infinite(20), "Corro".to_string()),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
for (reservation, name) in reservation_map.overlapping(&(16..17))
|
||||
for (reservation, name) in reservation_map.overlapping(ie(16, 17))
|
||||
{
|
||||
println!(
|
||||
"{name} has reserved {reservation:?} inside the range 16..17"
|
||||
@ -82,12 +87,36 @@ for (reservation, name) in reservation_map.iter() {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
reservation_map.overlaps(&Reservation::Infinite(0)),
|
||||
reservation_map.overlaps(Reservation::Infinite(0)),
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
## Key Definitions:
|
||||
## Key Understandings and Philosophies:
|
||||
|
||||
### Discrete-ness
|
||||
|
||||
This crate is designed to work with [`Discrete`] types as compared to
|
||||
[`Continuous`] types. For example, `u8` is a `Discrete` type, but
|
||||
`String` is a `Continuous` if you try to parse it as a decimal value.
|
||||
|
||||
The reason for this is that common [`interval-Mathematics`] operations
|
||||
differ depending on wether the underlying type is `Discrete` or
|
||||
`Continuous`. For example `5..=6` touches `7..=8` since integers are
|
||||
`Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the
|
||||
value `6.5` exists.
|
||||
|
||||
### Finite-ness
|
||||
|
||||
This crate is also designed to work with [`Finite`] types since it is
|
||||
much easier to implement and it is not restrictive to users since you
|
||||
can still represent `Infinite` numbers in `Finite` types paradoxically
|
||||
using the concept of [`Actual Infinity`].
|
||||
|
||||
For example you could define `Infinite` for `u8` as `u8::MAX` or if
|
||||
you still want to use `u8::MAX` as a `Finite` number you could define
|
||||
a wrapper type for `u8` that adds an [`Actual Infinity`] value to the
|
||||
`u8` set.
|
||||
|
||||
### Invalid Ranges
|
||||
|
||||
@ -104,17 +133,14 @@ values are greater than their end values. such as `5..2` or
|
||||
|
||||
Here are a few examples of ranges and whether they are valid:
|
||||
|
||||
| range | valid |
|
||||
| -------------- | ----- |
|
||||
| 0..=0 | YES |
|
||||
| 0..0 | NO |
|
||||
| 0..1 | YES |
|
||||
| 9..8 | NO |
|
||||
| (0.4)..=(-0.2) | NO |
|
||||
| ..(-3) | YES |
|
||||
| 0.0003.. | YES |
|
||||
| .. | YES |
|
||||
| 400..=400 | YES |
|
||||
| range | valid |
|
||||
| -------------------------------------- | ----- |
|
||||
| 0..=0 | YES |
|
||||
| 0..0 | NO |
|
||||
| 0..1 | YES |
|
||||
| 9..8 | NO |
|
||||
| (Bound::Exluded(3), Bound::Exluded(4)) | NO |
|
||||
| 400..=400 | YES |
|
||||
|
||||
### Overlap
|
||||
|
||||
@ -136,12 +162,6 @@ When a range "merges" other ranges it absorbs them to become larger.
|
||||
See Wikipedia's article on mathematical Intervals:
|
||||
<https://en.wikipedia.org/wiki/Interval_(mathematics)>
|
||||
|
||||
# Improvements/Caveats
|
||||
|
||||
- I had to create a new trait: [`TryFromBounds`] rather than using
|
||||
`TryFrom<(Bound, Bound)>` (relys on upstream to impl, see [this
|
||||
thread](https://internals.rust-lang.org/t/range-should-impl-tryfrom-bound-bound))
|
||||
|
||||
# Credit
|
||||
|
||||
I originally came up with the `StartBound`: [`Ord`] bodge on my own,
|
||||
@ -163,6 +183,11 @@ 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.
|
||||
|
||||
It is however worth noting the library eventually expanded and evolved
|
||||
from it's origins.
|
||||
|
||||
This crate was previously named [`range_bounds_map`].
|
||||
|
||||
# Similar Crates
|
||||
|
||||
Here are some relevant crates I found whilst searching around the
|
||||
@ -183,10 +208,10 @@ topic area:
|
||||
for [`Range`]s and not [`RangeInclusive`]s. And also no fancy
|
||||
merging 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.
|
||||
A data structure based off of a 2007 published paper! It supports
|
||||
any range as keys, unfortunately, it is implemented with a
|
||||
non-balancing `Box<Node>` based tree, however it also supports
|
||||
overlapping ranges 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
|
||||
@ -196,14 +221,18 @@ topic area:
|
||||
[`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
|
||||
[`ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html
|
||||
[`rangeboundsmap`]: https://docs.rs/range_bounds_map/latest/range_bounds_map/range_bounds_map/struct.RangeBoundsMap.html
|
||||
[`rangeboundsset`]: https://docs.rs/range_bounds_map/latest/range_bounds_map/range_bounds_set/struct.RangeBoundsSet.html
|
||||
[`discreteboundsmap`]: https://docs.rs/discrete_range_map/latest/discrete_range_map/discrete_range_map/struct.DiscreteRangeMap.html
|
||||
[`discreteboundsset`]: https://docs.rs/discrete_range_map/latest/discrete_range_map/range_bounds_set/struct.DiscreteRangeSet.html
|
||||
[`copse`]: https://github.com/eggyal/copse
|
||||
[`discrete`]: https://en.wikipedia.org/wiki/Discrete_mathematics
|
||||
[`continuous`]: https://en.wikipedia.org/wiki/List_of_continuity-related_mathematical_topics
|
||||
[`interval-mathematics`]: https://en.wikipedia.org/wiki/Interval_(mathematics)
|
||||
[`actual infinity`]: https://en.wikipedia.org/wiki/Actual_infinity
|
||||
[`finite`]: https://en.wiktionary.org/wiki/finite#Adjective
|
||||
[`range_bounds_map`]: https://docs.rs/range_bounds_map
|
||||
|
219
src/bound_ord.rs
219
src/bound_ord.rs
@ -1,219 +0,0 @@
|
||||
/*
|
||||
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 Affero 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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
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 newtype of [`Bound`] to implement [`Ord`].
|
||||
///
|
||||
/// This type is used to circumvent [`BTreeMap`]s (and rust collections
|
||||
/// in general) lack of methods for searching with custom
|
||||
/// [`comparator`] functions and/or it's lack of a [`Cursor`]-like
|
||||
/// API.
|
||||
///
|
||||
/// [`start_bound()`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html#tymethod.start_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(Debug, Serialize, Deserialize, Clone)]
|
||||
pub(crate) enum BoundOrd<T> {
|
||||
/// Mirror of [`Bound::Included`]
|
||||
/// There is no need for different Start and End variations as the
|
||||
/// Ord implementations are equivalent.
|
||||
Included(T),
|
||||
/// [`Bound::Excluded`] specific to Start bounds.
|
||||
StartExcluded(T),
|
||||
/// [`Bound::Unbounded`] specific to Start bounds.
|
||||
StartUnbounded,
|
||||
/// [`Bound::Excluded`] specific to End bounds.
|
||||
EndExcluded(T),
|
||||
/// [`Bound::Unbounded`] specific to End bounds.
|
||||
EndUnbounded,
|
||||
}
|
||||
|
||||
impl<T> BoundOrd<T> {
|
||||
pub(crate) fn start(bound: Bound<T>) -> Self {
|
||||
match bound {
|
||||
Bound::Included(point) => BoundOrd::Included(point),
|
||||
Bound::Excluded(point) => BoundOrd::StartExcluded(point),
|
||||
Bound::Unbounded => BoundOrd::StartUnbounded,
|
||||
}
|
||||
}
|
||||
pub(crate) fn end(bound: Bound<T>) -> Self {
|
||||
match bound {
|
||||
Bound::Included(point) => BoundOrd::Included(point),
|
||||
Bound::Excluded(point) => BoundOrd::EndExcluded(point),
|
||||
Bound::Unbounded => BoundOrd::EndUnbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for BoundOrd<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
#[rustfmt::skip]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(BoundOrd::Included(start1), BoundOrd::Included(start2)) => start1.cmp(start2),
|
||||
(BoundOrd::Included(start1), BoundOrd::StartExcluded(start2)) => cmp_with_priority(start1, start2, true),
|
||||
(BoundOrd::Included(start1), BoundOrd::EndExcluded(start2)) => cmp_with_priority(start1, start2, false),
|
||||
(BoundOrd::Included(_), BoundOrd::EndUnbounded) => Ordering::Less,
|
||||
(BoundOrd::Included(_), BoundOrd::StartUnbounded) => Ordering::Greater,
|
||||
|
||||
(BoundOrd::StartExcluded(start1), BoundOrd::StartExcluded(start2)) => start1.cmp(start2),
|
||||
(BoundOrd::StartExcluded(start1), BoundOrd::Included(start2)) => cmp_with_priority(start1, start2, false),
|
||||
(BoundOrd::StartExcluded(start1), BoundOrd::EndExcluded(start2)) => cmp_with_priority(start1, start2, false),
|
||||
(BoundOrd::StartExcluded(_), BoundOrd::StartUnbounded) => Ordering::Greater,
|
||||
(BoundOrd::StartExcluded(_), BoundOrd::EndUnbounded) => Ordering::Less,
|
||||
|
||||
(BoundOrd::StartUnbounded, BoundOrd::Included(_)) => Ordering::Less,
|
||||
(BoundOrd::StartUnbounded, BoundOrd::StartExcluded(_)) => Ordering::Less,
|
||||
(BoundOrd::StartUnbounded, BoundOrd::EndExcluded(_)) => Ordering::Less,
|
||||
(BoundOrd::StartUnbounded, BoundOrd::StartUnbounded) => Ordering::Equal,
|
||||
(BoundOrd::StartUnbounded, BoundOrd::EndUnbounded) => Ordering::Less,
|
||||
|
||||
(BoundOrd::EndExcluded(start1), BoundOrd::EndExcluded(start2)) => start1.cmp(start2),
|
||||
(BoundOrd::EndExcluded(start1), BoundOrd::Included(start2)) => cmp_with_priority(start1, start2, true),
|
||||
(BoundOrd::EndExcluded(start1), BoundOrd::StartExcluded(start2)) => cmp_with_priority(start1, start2, true),
|
||||
(BoundOrd::EndExcluded(_), BoundOrd::StartUnbounded) => Ordering::Greater,
|
||||
(BoundOrd::EndExcluded(_), BoundOrd::EndUnbounded) => Ordering::Less,
|
||||
|
||||
(BoundOrd::EndUnbounded, BoundOrd::Included(_)) => Ordering::Greater,
|
||||
(BoundOrd::EndUnbounded, BoundOrd::StartExcluded(_)) => Ordering::Greater,
|
||||
(BoundOrd::EndUnbounded, BoundOrd::EndExcluded(_)) => Ordering::Greater,
|
||||
(BoundOrd::EndUnbounded, BoundOrd::EndUnbounded) => Ordering::Equal,
|
||||
(BoundOrd::EndUnbounded, BoundOrd::StartUnbounded) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd for BoundOrd<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for BoundOrd<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other).is_eq()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for BoundOrd<T> where T: Ord {}
|
||||
|
||||
/// If they are equal say the item with priority is larger
|
||||
/// where false means left has priority and true means right.
|
||||
fn cmp_with_priority<T>(left: &T, right: &T, priority: bool) -> Ordering
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
let result = left.cmp(right);
|
||||
|
||||
match result {
|
||||
Ordering::Equal => match priority {
|
||||
false => Ordering::Greater,
|
||||
true => Ordering::Less,
|
||||
},
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<BoundOrd<T>> for Bound<T> {
|
||||
fn from(start_bound: BoundOrd<T>) -> Bound<T> {
|
||||
match start_bound {
|
||||
BoundOrd::Included(point) => Bound::Included(point),
|
||||
BoundOrd::StartExcluded(point) => Bound::Excluded(point),
|
||||
BoundOrd::StartUnbounded => Bound::Unbounded,
|
||||
BoundOrd::EndExcluded(point) => Bound::Excluded(point),
|
||||
BoundOrd::EndUnbounded => Bound::Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mass_start_bound_partial_ord_test() {
|
||||
//Included
|
||||
assert!(BoundOrd::Included(2) == BoundOrd::Included(2));
|
||||
assert!(BoundOrd::Included(2) <= BoundOrd::Included(2));
|
||||
assert!(BoundOrd::Included(2) >= BoundOrd::Included(2));
|
||||
assert!(BoundOrd::Included(0) < BoundOrd::Included(2));
|
||||
assert!(BoundOrd::Included(2) > BoundOrd::Included(0));
|
||||
|
||||
assert!(BoundOrd::Included(2) < BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::Included(0) < BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::Included(2) > BoundOrd::StartExcluded(0));
|
||||
|
||||
assert!(BoundOrd::Included(2) > BoundOrd::StartUnbounded);
|
||||
|
||||
assert!(BoundOrd::Included(2) > BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::Included(0) < BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::Included(2) > BoundOrd::EndExcluded(0));
|
||||
|
||||
assert!(BoundOrd::Included(2) < BoundOrd::EndUnbounded);
|
||||
|
||||
//StartExcluded
|
||||
assert!(BoundOrd::StartExcluded(2) == BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::StartExcluded(2) <= BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::StartExcluded(2) >= BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::StartExcluded(0) < BoundOrd::StartExcluded(2));
|
||||
assert!(BoundOrd::StartExcluded(2) > BoundOrd::StartExcluded(0));
|
||||
|
||||
assert!(BoundOrd::StartExcluded(2) > BoundOrd::StartUnbounded);
|
||||
|
||||
assert!(BoundOrd::StartExcluded(2) > BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::StartExcluded(2) > BoundOrd::EndExcluded(0));
|
||||
assert!(BoundOrd::StartExcluded(0) < BoundOrd::EndExcluded(2));
|
||||
|
||||
assert!(BoundOrd::StartExcluded(2) < BoundOrd::EndUnbounded);
|
||||
|
||||
//StartUnbounded
|
||||
assert!(BoundOrd::StartUnbounded::<i8> == BoundOrd::StartUnbounded);
|
||||
assert!(BoundOrd::StartUnbounded::<i8> <= BoundOrd::StartUnbounded);
|
||||
assert!(BoundOrd::StartUnbounded::<i8> >= BoundOrd::StartUnbounded);
|
||||
|
||||
assert!(BoundOrd::StartUnbounded < BoundOrd::EndExcluded(2));
|
||||
|
||||
assert!(BoundOrd::StartUnbounded::<i8> < BoundOrd::EndUnbounded);
|
||||
|
||||
//EndExcluded
|
||||
assert!(BoundOrd::EndExcluded(2) == BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::EndExcluded(2) <= BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::EndExcluded(2) >= BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::EndExcluded(0) < BoundOrd::EndExcluded(2));
|
||||
assert!(BoundOrd::EndExcluded(2) > BoundOrd::EndExcluded(0));
|
||||
|
||||
//EndUnbounded
|
||||
assert!(BoundOrd::EndUnbounded::<i8> == BoundOrd::EndUnbounded);
|
||||
assert!(BoundOrd::EndUnbounded::<i8> <= BoundOrd::EndUnbounded);
|
||||
assert!(BoundOrd::EndUnbounded::<i8> >= BoundOrd::EndUnbounded);
|
||||
}
|
||||
}
|
51
src/discrete_finite.rs
Normal file
51
src/discrete_finite.rs
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2022 James Forster
|
||||
|
||||
This file is part of discrete_range_map.
|
||||
|
||||
discrete_range_map is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
discrete_range_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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub trait DiscreteFinite {
|
||||
const MIN: Self;
|
||||
const MAX: Self;
|
||||
|
||||
fn up(self) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
fn down(self) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
macro_rules! foo {
|
||||
() => {};
|
||||
($ident:ident, $($t:tt)*) => {
|
||||
impl DiscreteFinite for $ident {
|
||||
const MIN: Self = $ident::MIN;
|
||||
const MAX: Self = $ident::MAX;
|
||||
|
||||
fn up(self) -> Option<Self> {
|
||||
self.checked_add(1)
|
||||
}
|
||||
fn down(self) -> Option<Self> {
|
||||
self.checked_sub(1)
|
||||
}
|
||||
}
|
||||
|
||||
foo!($($t)*);
|
||||
};
|
||||
}
|
||||
|
||||
foo!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128,);
|
40
src/discrete_finite_bounds.rs
Normal file
40
src/discrete_finite_bounds.rs
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2022 James Forster
|
||||
|
||||
This file is part of discrete_range_map.
|
||||
|
||||
discrete_range_map is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
discrete_range_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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::discrete_range_map::FiniteRange;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DiscreteFiniteBounds<I> {
|
||||
//both are always included
|
||||
pub start: I,
|
||||
pub end: I,
|
||||
}
|
||||
|
||||
impl<I> FiniteRange<I> for DiscreteFiniteBounds<I>
|
||||
where
|
||||
I: Copy,
|
||||
{
|
||||
fn start(&self) -> I {
|
||||
self.start
|
||||
}
|
||||
|
||||
fn end(&self) -> I {
|
||||
self.end
|
||||
}
|
||||
}
|
2235
src/discrete_range_map.rs
Normal file
2235
src/discrete_range_map.rs
Normal file
File diff suppressed because it is too large
Load Diff
259
src/discrete_range_set.rs
Normal file
259
src/discrete_range_set.rs
Normal file
@ -0,0 +1,259 @@
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::discrete_finite::DiscreteFinite;
|
||||
use crate::discrete_finite_bounds::DiscreteFiniteBounds;
|
||||
use crate::discrete_range_map::{
|
||||
FiniteRange, IntoIter as DiscreteRangeMapIntoIter,
|
||||
};
|
||||
use crate::{DiscreteRangeMap, OverlapError};
|
||||
|
||||
/// An ordered set of non-overlapping ranges based on [`DiscreteRangeMap`].
|
||||
///
|
||||
/// `I` is the generic type parameter for the [`Ord`] type the `K`
|
||||
/// type is range over.
|
||||
///
|
||||
/// `K` is the generic type parameter for the range implementing type
|
||||
/// in the set.
|
||||
///
|
||||
/// Phrasing it another way: `I` is the point type and `K` is the range type.
|
||||
///
|
||||
/// See [`DiscreteRangeMap`] for more details.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DiscreteRangeSet<I, K> {
|
||||
inner: DiscreteRangeMap<I, K, ()>,
|
||||
}
|
||||
|
||||
impl<I, K> DiscreteRangeSet<I, K>
|
||||
where
|
||||
I: Ord + Copy + DiscreteFinite,
|
||||
K: FiniteRange<I> + Copy + From<DiscreteFiniteBounds<I>>,
|
||||
{
|
||||
/// See [`DiscreteRangeMap::new()`] for more details.
|
||||
pub fn new() -> Self {
|
||||
DiscreteRangeSet {
|
||||
inner: DiscreteRangeMap::new(),
|
||||
}
|
||||
}
|
||||
/// See [`DiscreteRangeMap::len()`] for more details.
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
/// See [`DiscreteRangeMap::is_empty()`] for more details.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
/// See [`DiscreteRangeMap::overlaps()`] for more details.
|
||||
pub fn overlaps<Q>(&self, range: Q) -> bool
|
||||
where
|
||||
Q: FiniteRange<I> + Copy,
|
||||
{
|
||||
self.inner.overlaps(range)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::overlapping()`] for more details.
|
||||
pub fn overlapping<Q>(
|
||||
&self,
|
||||
range: Q,
|
||||
) -> impl DoubleEndedIterator<Item = &K>
|
||||
where
|
||||
Q: FiniteRange<I> + Copy,
|
||||
{
|
||||
self.inner.overlapping(range).map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::get_entry_at_point()`] for more details.
|
||||
pub fn get_at_point(&self, point: I) -> Result<&K, K> {
|
||||
self.inner.get_entry_at_point(point).map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::contains_point()`] for more details.
|
||||
pub fn contains_point(&self, point: I) -> bool {
|
||||
self.inner.contains_point(point)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::iter()`] for more details.
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &K> {
|
||||
self.inner.iter().map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::remove_overlapping()`] for more details.
|
||||
pub fn remove_overlapping<'a, Q>(
|
||||
&'a mut self,
|
||||
range: Q,
|
||||
) -> impl Iterator<Item = K> + '_
|
||||
where
|
||||
Q: FiniteRange<I> + Copy + 'a,
|
||||
{
|
||||
self.inner.remove_overlapping(range).map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::cut()`] for more details.
|
||||
pub fn cut<'a, Q>(&'a mut self, range: Q) -> impl Iterator<Item = K> + '_
|
||||
where
|
||||
Q: FiniteRange<I> + Copy + 'a,
|
||||
{
|
||||
self.inner.cut(range).map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::gaps()`] for more details.
|
||||
pub fn gaps<'a, Q>(&'a self, range: Q) -> impl Iterator<Item = K> + '_
|
||||
where
|
||||
Q: FiniteRange<I> + Copy + 'a,
|
||||
{
|
||||
self.inner.gaps(range)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::contains_range()`] for more details.
|
||||
pub fn contains_range<Q>(&self, range: Q) -> bool
|
||||
where
|
||||
Q: FiniteRange<I> + Copy,
|
||||
{
|
||||
self.inner.contains_range(range)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::insert_strict()`] for more details.
|
||||
pub fn insert_strict(&mut self, range: K) -> Result<(), OverlapError> {
|
||||
self.inner.insert_strict(range, ())
|
||||
}
|
||||
/// See [`DiscreteRangeMap::insert_merge_touching()`] for more details.
|
||||
pub fn insert_merge_touching(
|
||||
&mut self,
|
||||
range: K,
|
||||
) -> Result<K, OverlapError> {
|
||||
self.inner.insert_merge_touching(range, ())
|
||||
}
|
||||
/// See [`DiscreteRangeMap::insert_merge_overlapping()`] for more details.
|
||||
pub fn insert_merge_overlapping(&mut self, range: K) -> K {
|
||||
self.inner.insert_merge_overlapping(range, ())
|
||||
}
|
||||
/// See [`DiscreteRangeMap::insert_merge_touching_or_overlapping()`] for more details.
|
||||
pub fn insert_merge_touching_or_overlapping(&mut self, range: K) -> K {
|
||||
self.inner.insert_merge_touching_or_overlapping(range, ())
|
||||
}
|
||||
/// See [`DiscreteRangeMap::insert_overwrite()`] for more details.
|
||||
pub fn insert_overwrite(&mut self, range: K) {
|
||||
self.inner.insert_overwrite(range, ())
|
||||
}
|
||||
/// See [`DiscreteRangeMap::first_entry()`] for more details.
|
||||
pub fn first(&self) -> Option<&K> {
|
||||
self.inner.first_entry().map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::last_entry()`] for more details.
|
||||
pub fn last(&self) -> Option<&K> {
|
||||
self.inner.last_entry().map(first)
|
||||
}
|
||||
/// See [`DiscreteRangeMap::from_slice_strict()`] for more details.
|
||||
pub fn from_slice_strict<const N: usize>(
|
||||
slice: [K; N],
|
||||
) -> Result<DiscreteRangeSet<I, K>, OverlapError> {
|
||||
let mut set = DiscreteRangeSet::new();
|
||||
for range in slice {
|
||||
set.insert_strict(range)?;
|
||||
}
|
||||
return Ok(set);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Functions ==========================
|
||||
|
||||
fn first<A, B>((a, _): (A, B)) -> A {
|
||||
a
|
||||
}
|
||||
|
||||
// Trait Impls ==========================
|
||||
|
||||
impl<I, K> IntoIterator for DiscreteRangeSet<I, K> {
|
||||
type Item = K;
|
||||
type IntoIter = IntoIter<I, K>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
return IntoIter {
|
||||
inner: self.inner.into_iter(),
|
||||
};
|
||||
}
|
||||
}
|
||||
/// An owning iterator over the entries of a [`DiscreteRangeSet`].
|
||||
///
|
||||
/// This `struct` is created by the [`into_iter`] method on
|
||||
/// [`DiscreteRangeSet`] (provided by the [`IntoIterator`] trait). See
|
||||
/// its documentation for more.
|
||||
///
|
||||
/// [`into_iter`]: IntoIterator::into_iter
|
||||
/// [`IntoIterator`]: core::iter::IntoIterator
|
||||
pub struct IntoIter<I, K> {
|
||||
inner: DiscreteRangeMapIntoIter<I, K, ()>,
|
||||
}
|
||||
impl<I, K> Iterator for IntoIter<I, K> {
|
||||
type Item = K;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(first)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, K> Default for DiscreteRangeSet<I, K>
|
||||
where
|
||||
I: PartialOrd,
|
||||
{
|
||||
fn default() -> Self {
|
||||
DiscreteRangeSet {
|
||||
inner: DiscreteRangeMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, K> Serialize for DiscreteRangeSet<I, K>
|
||||
where
|
||||
I: Ord + Copy + DiscreteFinite,
|
||||
K: FiniteRange<I> + Copy + From<DiscreteFiniteBounds<I>> + Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(self.len()))?;
|
||||
for range_bounds in self.iter() {
|
||||
seq.serialize_element(&range_bounds)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, I, K> Deserialize<'de> for DiscreteRangeSet<I, K>
|
||||
where
|
||||
I: Ord + Copy + DiscreteFinite,
|
||||
K: FiniteRange<I> + Copy + From<DiscreteFiniteBounds<I>> + Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(DiscreteRangeSetVisitor {
|
||||
i: PhantomData,
|
||||
k: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct DiscreteRangeSetVisitor<I, K> {
|
||||
i: PhantomData<I>,
|
||||
k: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'de, I, K> Visitor<'de> for DiscreteRangeSetVisitor<I, K>
|
||||
where
|
||||
I: Ord + Copy + DiscreteFinite,
|
||||
K: FiniteRange<I> + Copy + From<DiscreteFiniteBounds<I>> + Deserialize<'de>,
|
||||
{
|
||||
type Value = DiscreteRangeSet<I, K>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a DiscreteRangeSet")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut set = DiscreteRangeSet::new();
|
||||
while let Some(range_bounds) = access.next_element()? {
|
||||
set.insert_strict(range_bounds)
|
||||
.map_err(|_| serde::de::Error::custom("ranges overlap"))?;
|
||||
}
|
||||
Ok(set)
|
||||
}
|
||||
}
|
169
src/lib.rs
169
src/lib.rs
@ -1,33 +1,39 @@
|
||||
/*
|
||||
Copyright 2022 James Forster
|
||||
|
||||
This file is part of range_bounds_map.
|
||||
This file is part of discrete_range_map.
|
||||
|
||||
range_bounds_map is free software: you can redistribute it and/or
|
||||
discrete_range_map is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU Affero 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,
|
||||
discrete_range_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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! This crate provides [`DiscreteRangeMap`] and [`DiscreteRangeSet`],
|
||||
//! Data Structures for storing non-overlapping discrete intervals based
|
||||
//! off [`BTreeMap`].
|
||||
//!
|
||||
//! ## Example using [`Range`]s
|
||||
//! ## You must implement `Copy`
|
||||
//!
|
||||
//! Due to implementation complications with non-`Copy` types the
|
||||
//! datastructures currently require both the range type and the points
|
||||
//! the ranges are over to be `Copy`.
|
||||
//!
|
||||
//! ## Example using an Inclusive-Exclusive range
|
||||
//!
|
||||
//! ```rust
|
||||
//! use range_bounds_map::test_ranges::ie;
|
||||
//! use range_bounds_map::RangeBoundsMap;
|
||||
//! use discrete_range_map::test_ranges::ie;
|
||||
//! use discrete_range_map::DiscreteRangeMap;
|
||||
//!
|
||||
//! let mut map = RangeBoundsMap::new();
|
||||
//! let mut map = DiscreteRangeMap::new();
|
||||
//!
|
||||
//! map.insert_strict(ie(0, 5), true);
|
||||
//! map.insert_strict(ie(5, 10), false);
|
||||
@ -37,44 +43,41 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! assert_eq!(map.contains_point(5), true);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Example using a custom [`RangeBounds`] type
|
||||
//! ## Example using a custom range type
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::ops::{Bound, RangeBounds};
|
||||
//!
|
||||
//! use range_bounds_map::test_ranges::ie;
|
||||
//! use range_bounds_map::RangeBoundsMap;
|
||||
//! use discrete_range_map::test_ranges::ie;
|
||||
//! use discrete_range_map::DiscreteRangeMap;
|
||||
//! use discrete_range_map::FiniteRange;
|
||||
//!
|
||||
//! #[derive(Debug, Copy, Clone)]
|
||||
//! enum Reservation {
|
||||
//! // Start, End (Inclusive-Inclusive)
|
||||
//! // Start, End (Inclusive-Exclusive)
|
||||
//! Finite(i8, i8),
|
||||
//! // Start (Exclusive)
|
||||
//! // Start (Inclusive-Forever)
|
||||
//! Infinite(i8),
|
||||
//! }
|
||||
//!
|
||||
//! // First, we need to implement RangeBounds
|
||||
//! impl RangeBounds<i8> for Reservation {
|
||||
//! fn start_bound(&self) -> Bound<&i8> {
|
||||
//! match self {
|
||||
//! Reservation::Finite(start, _) => {
|
||||
//! Bound::Included(start)
|
||||
//! }
|
||||
//! Reservation::Infinite(start) => {
|
||||
//! Bound::Excluded(start)
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! fn end_bound(&self) -> Bound<&i8> {
|
||||
//! match self {
|
||||
//! Reservation::Finite(_, end) => Bound::Included(end),
|
||||
//! Reservation::Infinite(_) => Bound::Unbounded,
|
||||
//! }
|
||||
//! }
|
||||
//! // First, we need to implement FiniteRange
|
||||
//! impl FiniteRange<i8> for Reservation {
|
||||
//! fn start(&self) -> i8 {
|
||||
//! match self {
|
||||
//! Reservation::Finite(start, _) => *start,
|
||||
//! Reservation::Infinite(start) => *start,
|
||||
//! }
|
||||
//! }
|
||||
//! fn end(&self) -> i8 {
|
||||
//! match self {
|
||||
//! //the end is exclusive so we take off 1 with checking
|
||||
//! //for compile time error overflow detection
|
||||
//! Reservation::Finite(_, end) => end.checked_sub(1).unwrap(),
|
||||
//! Reservation::Infinite(_) => i8::MAX,
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Next we can create a custom typed RangeBoundsMap
|
||||
//! let reservation_map = RangeBoundsMap::from_slice_strict([
|
||||
//! // Next we can create a custom typed DiscreteRangeMap
|
||||
//! let reservation_map = DiscreteRangeMap::from_slice_strict([
|
||||
//! (Reservation::Finite(10, 20), "Ferris".to_string()),
|
||||
//! (Reservation::Infinite(20), "Corro".to_string()),
|
||||
//! ])
|
||||
@ -97,7 +100,31 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! ## Key Definitions:
|
||||
//! ## Key Understandings and Philosophies:
|
||||
//!
|
||||
//! ### Discrete-ness
|
||||
//!
|
||||
//! This crate is designed to work with [`Discrete`] types as compared to
|
||||
//! [`Continuous`] types. For example, `u8` is a `Discrete` type, but
|
||||
//! `String` is a `Continuous` if you try to parse it as a decimal value.
|
||||
//!
|
||||
//! The reason for this is that common [`interval-Mathematics`] operations
|
||||
//! differ depending on wether the underlying type is `Discrete` or
|
||||
//! `Continuous`. For example `5..=6` touches `7..=8` since integers are
|
||||
//! `Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the
|
||||
//! value `6.5` exists.
|
||||
//!
|
||||
//! ### Finite-ness
|
||||
//!
|
||||
//! This crate is also designed to work with [`Finite`] types since it is
|
||||
//! much easier to implement and it is not restrictive to users since you
|
||||
//! can still represent `Infinite` numbers in `Finite` types paradoxically
|
||||
//! using the concept of [`Actual Infinity`].
|
||||
//!
|
||||
//! For example you could define `Infinite` for `u8` as `u8::MAX` or if
|
||||
//! you still want to use `u8::MAX` as a `Finite` number you could define
|
||||
//! a wrapper type for `u8` that adds an [`Actual Infinity`] value to the
|
||||
//! `u8` set.
|
||||
//!
|
||||
//! ### Invalid Ranges
|
||||
//!
|
||||
@ -114,17 +141,14 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
//! Here are a few examples of ranges and whether they are valid:
|
||||
//!
|
||||
//! | range | valid |
|
||||
//! | -------------- | ----- |
|
||||
//! | 0..=0 | YES |
|
||||
//! | 0..0 | NO |
|
||||
//! | 0..1 | YES |
|
||||
//! | 9..8 | NO |
|
||||
//! | (0.4)..=(-0.2) | NO |
|
||||
//! | ..(-3) | YES |
|
||||
//! | 0.0003.. | YES |
|
||||
//! | .. | YES |
|
||||
//! | 400..=400 | YES |
|
||||
//! | range | valid |
|
||||
//! | -------------------------------------- | ----- |
|
||||
//! | 0..=0 | YES |
|
||||
//! | 0..0 | NO |
|
||||
//! | 0..1 | YES |
|
||||
//! | 9..8 | NO |
|
||||
//! | (Bound::Exluded(3), Bound::Exluded(4)) | NO |
|
||||
//! | 400..=400 | YES |
|
||||
//!
|
||||
//! ### Overlap
|
||||
//!
|
||||
@ -146,12 +170,6 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! See Wikipedia's article on mathematical Intervals:
|
||||
//! <https://en.wikipedia.org/wiki/Interval_(mathematics)>
|
||||
//!
|
||||
//! # Improvements/Caveats
|
||||
//!
|
||||
//! - I had to create a new trait: [`TryFromBounds`] rather than using
|
||||
//! `TryFrom<(Bound, Bound)>` (relys on upstream to impl, see [this
|
||||
//! thread](https://internals.rust-lang.org/t/range-should-impl-tryfrom-bound-bound))
|
||||
//!
|
||||
//! # Credit
|
||||
//!
|
||||
//! I originally came up with the `StartBound`: [`Ord`] bodge on my own,
|
||||
@ -173,6 +191,11 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! keys before I realized it might be easier and simpler to just write it
|
||||
//! all from scratch.
|
||||
//!
|
||||
//! It is however worth noting the library eventually expanded and evolved
|
||||
//! from it's origins.
|
||||
//!
|
||||
//! This crate was previously named [`range_bounds_map`].
|
||||
//!
|
||||
//! # Similar Crates
|
||||
//!
|
||||
//! Here are some relevant crates I found whilst searching around the
|
||||
@ -193,10 +216,10 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! for [`Range`]s and not [`RangeInclusive`]s. And also no fancy
|
||||
//! merging 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.
|
||||
//! A data structure based off of a 2007 published paper! It supports
|
||||
//! any range as keys, unfortunately, it is implemented with a
|
||||
//! non-balancing `Box<Node>` based tree, however it also supports
|
||||
//! overlapping ranges 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
|
||||
@ -206,34 +229,38 @@ along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
//! [`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
|
||||
//! [`ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html
|
||||
//! [`rangeboundsmap`]: https://docs.rs/range_bounds_map/latest/range_bounds_map/range_bounds_map/struct.RangeBoundsMap.html
|
||||
//! [`rangeboundsset`]: https://docs.rs/range_bounds_map/latest/range_bounds_map/range_bounds_set/struct.RangeBoundsSet.html
|
||||
//! [`discreteboundsmap`]: https://docs.rs/discrete_range_map/latest/discrete_range_map/discrete_range_map/struct.DiscreteRangeMap.html
|
||||
//! [`discreteboundsset`]: https://docs.rs/discrete_range_map/latest/discrete_range_map/range_bounds_set/struct.DiscreteRangeSet.html
|
||||
//! [`copse`]: https://github.com/eggyal/copse
|
||||
//! [`discrete`]: https://en.wikipedia.org/wiki/Discrete_mathematics
|
||||
//! [`continuous`]: https://en.wikipedia.org/wiki/List_of_continuity-related_mathematical_topics
|
||||
//! [`interval-mathematics`]: https://en.wikipedia.org/wiki/Interval_(mathematics)
|
||||
//! [`actual infinity`]: https://en.wikipedia.org/wiki/Actual_infinity
|
||||
//! [`finite`]: https://en.wiktionary.org/wiki/finite#Adjective
|
||||
//! [`range_bounds_map`]: https://docs.rs/range_bounds_map
|
||||
|
||||
#![feature(let_chains)]
|
||||
#![feature(btree_cursors)]
|
||||
#![feature(step_trait)]
|
||||
#![allow(clippy::tabs_in_doc_comments)]
|
||||
#![allow(clippy::needless_return)]
|
||||
|
||||
pub(crate) mod bound_ord;
|
||||
pub mod test_ranges;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub mod range_bounds_map;
|
||||
pub mod range_bounds_set;
|
||||
pub mod try_from_bounds;
|
||||
pub mod discrete_finite;
|
||||
pub mod discrete_finite_bounds;
|
||||
|
||||
pub use crate::range_bounds_map::{
|
||||
OverlapError, OverlapOrTryFromBoundsError, RangeBoundsMap,
|
||||
TryFromBoundsError,
|
||||
};
|
||||
pub use crate::range_bounds_set::RangeBoundsSet;
|
||||
pub use crate::try_from_bounds::TryFromBounds;
|
||||
pub mod discrete_range_map;
|
||||
pub mod discrete_range_set;
|
||||
|
||||
pub use crate::discrete_finite::DiscreteFinite;
|
||||
pub use crate::discrete_finite_bounds::DiscreteFiniteBounds;
|
||||
pub use crate::discrete_range_map::{DiscreteRangeMap, FiniteRange, OverlapError};
|
||||
pub use crate::discrete_range_set::DiscreteRangeSet;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,292 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Bound;
|
||||
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::range_bounds_map::{IntoIter as RangeBoundsMapIntoIter, NiceRange};
|
||||
use crate::{
|
||||
OverlapError, OverlapOrTryFromBoundsError, RangeBoundsMap, TryFromBounds,
|
||||
TryFromBoundsError,
|
||||
};
|
||||
|
||||
/// An ordered set of non-overlapping ranges based on [`RangeBoundsMap`].
|
||||
///
|
||||
/// `I` is the generic type parameter for the [`Ord`] type the `K`
|
||||
/// type is [`RangeBounds`] over.
|
||||
///
|
||||
/// `K` is the generic type parameter for the [`RangeBounds`]
|
||||
/// implementing type in the set.
|
||||
///
|
||||
/// Phrasing it another way: `I` is the point type and `K` is the range type.
|
||||
///
|
||||
/// See [`RangeBoundsMap`] for more details.
|
||||
///
|
||||
/// [`RangeBounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RangeBoundsSet<I, K> {
|
||||
inner: RangeBoundsMap<I, K, ()>,
|
||||
}
|
||||
|
||||
impl<I, K> RangeBoundsSet<I, K>
|
||||
where
|
||||
I: Ord + Copy,
|
||||
K: NiceRange<I>,
|
||||
{
|
||||
/// See [`RangeBoundsMap::new()`] for more details.
|
||||
pub fn new() -> Self {
|
||||
RangeBoundsSet {
|
||||
inner: RangeBoundsMap::new(),
|
||||
}
|
||||
}
|
||||
/// See [`RangeBoundsMap::len()`] for more details.
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
/// See [`RangeBoundsMap::is_empty()`] for more details.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
/// See [`RangeBoundsMap::overlaps()`] for more details.
|
||||
pub fn overlaps<Q>(&self, range: Q) -> bool
|
||||
where
|
||||
Q: NiceRange<I>,
|
||||
{
|
||||
self.inner.overlaps(range)
|
||||
}
|
||||
/// See [`RangeBoundsMap::overlapping()`] for more details.
|
||||
pub fn overlapping<Q>(
|
||||
&self,
|
||||
range: Q,
|
||||
) -> impl DoubleEndedIterator<Item = &K>
|
||||
where
|
||||
Q: NiceRange<I>,
|
||||
{
|
||||
self.inner.overlapping(range).map(first)
|
||||
}
|
||||
/// See [`RangeBoundsMap::get_entry_at_point()`] for more details.
|
||||
pub fn get_at_point(&self, point: I) -> Result<K, (Bound<I>, Bound<I>)> {
|
||||
self.inner.get_entry_at_point(point).map(first).copied()
|
||||
}
|
||||
/// See [`RangeBoundsMap::contains_point()`] for more details.
|
||||
pub fn contains_point(&self, point: I) -> bool {
|
||||
self.inner.contains_point(point)
|
||||
}
|
||||
/// See [`RangeBoundsMap::iter()`] for more details.
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &K> {
|
||||
self.inner.iter().map(first)
|
||||
}
|
||||
/// See [`RangeBoundsMap::remove_overlapping()`] for more details.
|
||||
pub fn remove_overlapping<'a, Q>(
|
||||
&'a mut self,
|
||||
range: Q,
|
||||
) -> impl Iterator<Item = K> + '_
|
||||
where
|
||||
Q: NiceRange<I> + 'a,
|
||||
{
|
||||
self.inner.remove_overlapping(range).map(first)
|
||||
}
|
||||
/// See [`RangeBoundsMap::cut()`] for more details.
|
||||
pub fn cut<'a, Q>(
|
||||
&'a mut self,
|
||||
range: Q,
|
||||
) -> Result<
|
||||
impl Iterator<Item = (Bound<I>, Bound<I>)> + '_,
|
||||
TryFromBoundsError,
|
||||
>
|
||||
where
|
||||
Q: NiceRange<I> + 'a,
|
||||
K: TryFromBounds<I>,
|
||||
{
|
||||
self.inner.cut(range).map(|x| x.map(first))
|
||||
}
|
||||
/// See [`RangeBoundsMap::gaps()`] for more details.
|
||||
pub fn gaps<'a, Q>(
|
||||
&'a self,
|
||||
range: Q,
|
||||
) -> impl DoubleEndedIterator<Item = (Bound<I>, Bound<I>)> + '_
|
||||
where
|
||||
Q: NiceRange<I> + 'a,
|
||||
{
|
||||
self.inner.gaps(range)
|
||||
}
|
||||
/// See [`RangeBoundsMap::contains_range()`] for more details.
|
||||
pub fn contains_range<Q>(&self, range: Q) -> bool
|
||||
where
|
||||
Q: NiceRange<I>,
|
||||
{
|
||||
self.inner.contains_range(range)
|
||||
}
|
||||
/// See [`RangeBoundsMap::insert_strict()`] for more details.
|
||||
pub fn insert_strict(&mut self, range: K) -> Result<(), OverlapError> {
|
||||
self.inner.insert_strict(range, ())
|
||||
}
|
||||
/// See [`RangeBoundsMap::insert_merge_touching()`] for more details.
|
||||
pub fn insert_merge_touching(
|
||||
&mut self,
|
||||
range: K,
|
||||
) -> Result<K, OverlapOrTryFromBoundsError>
|
||||
where
|
||||
K: TryFromBounds<I>,
|
||||
{
|
||||
self.inner.insert_merge_touching(range, ())
|
||||
}
|
||||
/// See [`RangeBoundsMap::insert_merge_overlapping()`] for more details.
|
||||
pub fn insert_merge_overlapping(
|
||||
&mut self,
|
||||
range: K,
|
||||
) -> Result<K, TryFromBoundsError>
|
||||
where
|
||||
K: TryFromBounds<I>,
|
||||
{
|
||||
self.inner.insert_merge_overlapping(range, ())
|
||||
}
|
||||
/// See [`RangeBoundsMap::insert_merge_touching_or_overlapping()`] for more details.
|
||||
pub fn insert_merge_touching_or_overlapping(
|
||||
&mut self,
|
||||
range: K,
|
||||
) -> Result<K, TryFromBoundsError>
|
||||
where
|
||||
K: TryFromBounds<I>,
|
||||
{
|
||||
self.inner.insert_merge_touching_or_overlapping(range, ())
|
||||
}
|
||||
/// See [`RangeBoundsMap::insert_overwrite()`] for more details.
|
||||
pub fn insert_overwrite(
|
||||
&mut self,
|
||||
range: K,
|
||||
) -> Result<(), TryFromBoundsError>
|
||||
where
|
||||
K: TryFromBounds<I>,
|
||||
{
|
||||
self.inner.insert_overwrite(range, ())
|
||||
}
|
||||
/// See [`RangeBoundsMap::first_entry()`] for more details.
|
||||
pub fn first(&self) -> Option<&K> {
|
||||
self.inner.first_entry().map(first)
|
||||
}
|
||||
/// See [`RangeBoundsMap::last_entry()`] for more details.
|
||||
pub fn last(&self) -> Option<&K> {
|
||||
self.inner.last_entry().map(first)
|
||||
}
|
||||
/// See [`RangeBoundsMap::from_slice_strict()`] for more details.
|
||||
pub fn from_slice_strict<const N: usize>(
|
||||
slice: [K; N],
|
||||
) -> Result<RangeBoundsSet<I, K>, OverlapError> {
|
||||
let mut set = RangeBoundsSet::new();
|
||||
for range in slice {
|
||||
set.insert_strict(range)?;
|
||||
}
|
||||
return Ok(set);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Functions ==========================
|
||||
|
||||
fn first<A, B>((a, _): (A, B)) -> A {
|
||||
a
|
||||
}
|
||||
|
||||
// Trait Impls ==========================
|
||||
|
||||
impl<I, K> IntoIterator for RangeBoundsSet<I, K> {
|
||||
type Item = K;
|
||||
type IntoIter = IntoIter<I, K>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
return IntoIter {
|
||||
inner: self.inner.into_iter(),
|
||||
};
|
||||
}
|
||||
}
|
||||
/// An owning iterator over the entries of a [`RangeBoundsSet`].
|
||||
///
|
||||
/// This `struct` is created by the [`into_iter`] method on
|
||||
/// [`RangeBoundsSet`] (provided by the [`IntoIterator`] trait). See
|
||||
/// its documentation for more.
|
||||
///
|
||||
/// [`into_iter`]: IntoIterator::into_iter
|
||||
/// [`IntoIterator`]: core::iter::IntoIterator
|
||||
pub struct IntoIter<I, K> {
|
||||
inner: RangeBoundsMapIntoIter<I, K, ()>,
|
||||
}
|
||||
impl<I, K> Iterator for IntoIter<I, K> {
|
||||
type Item = K;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(first)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, K> Default for RangeBoundsSet<I, K>
|
||||
where
|
||||
I: PartialOrd,
|
||||
{
|
||||
fn default() -> Self {
|
||||
RangeBoundsSet {
|
||||
inner: RangeBoundsMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, K> Serialize for RangeBoundsSet<I, K>
|
||||
where
|
||||
I: Ord + Copy,
|
||||
K: NiceRange<I> + Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(self.len()))?;
|
||||
for range_bounds in self.iter() {
|
||||
seq.serialize_element(&range_bounds)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, I, K> Deserialize<'de> for RangeBoundsSet<I, K>
|
||||
where
|
||||
I: Ord + Copy,
|
||||
K: NiceRange<I> + Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(RangeBoundsSetVisitor {
|
||||
i: PhantomData,
|
||||
k: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct RangeBoundsSetVisitor<I, K> {
|
||||
i: PhantomData<I>,
|
||||
k: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'de, I, K> Visitor<'de> for RangeBoundsSetVisitor<I, K>
|
||||
where
|
||||
I: Ord + Copy,
|
||||
K: NiceRange<I> + Deserialize<'de>,
|
||||
{
|
||||
type Value = RangeBoundsSet<I, K>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a RangeBoundsSet")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut set = RangeBoundsSet::new();
|
||||
while let Some(range_bounds) = access.next_element()? {
|
||||
set.insert_strict(range_bounds)
|
||||
.map_err(|_| serde::de::Error::custom("RangeBounds overlap"))?;
|
||||
}
|
||||
Ok(set)
|
||||
}
|
||||
}
|
@ -1,76 +1,51 @@
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
use crate::discrete_finite_bounds::DiscreteFiniteBounds;
|
||||
use crate::discrete_finite::DiscreteFinite;
|
||||
|
||||
use crate::{TryFromBounds, TryFromBoundsError};
|
||||
|
||||
pub type AnyRange = (Bound<i8>, Bound<i8>);
|
||||
|
||||
pub fn uu() -> AnyRange {
|
||||
(Bound::Unbounded, Bound::Unbounded)
|
||||
pub fn uu() -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: i8::MIN,
|
||||
end: i8::MAX,
|
||||
}
|
||||
}
|
||||
pub fn ui(x: i8) -> AnyRange {
|
||||
(Bound::Unbounded, Bound::Included(x))
|
||||
pub fn ui(x: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: i8::MIN,
|
||||
end: x,
|
||||
}
|
||||
}
|
||||
pub fn ue(x: i8) -> AnyRange {
|
||||
(Bound::Unbounded, Bound::Excluded(x))
|
||||
pub fn ue(x: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: i8::MIN,
|
||||
end: x.down().unwrap(),
|
||||
}
|
||||
}
|
||||
pub fn iu(x: i8) -> AnyRange {
|
||||
(Bound::Included(x), Bound::Unbounded)
|
||||
pub fn iu(x: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: x,
|
||||
end: i8::MAX,
|
||||
}
|
||||
}
|
||||
//fn eu(x: i8) -> TestBounds {
|
||||
//(Bound::Excluded(x), Bound::Unbounded)
|
||||
//}
|
||||
pub fn ii(x1: i8, x2: i8) -> AnyRange {
|
||||
(Bound::Included(x1), Bound::Included(x2))
|
||||
pub fn ii(x1: i8, x2: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds { start: x1, end: x2 }
|
||||
}
|
||||
pub fn ie(x1: i8, x2: i8) -> AnyRange {
|
||||
(Bound::Included(x1), Bound::Excluded(x2))
|
||||
}
|
||||
pub fn ei(x1: i8, x2: i8) -> AnyRange {
|
||||
(Bound::Excluded(x1), Bound::Included(x2))
|
||||
}
|
||||
pub fn ee(x1: i8, x2: i8) -> AnyRange {
|
||||
(Bound::Excluded(x1), Bound::Excluded(x2))
|
||||
}
|
||||
|
||||
pub fn u() -> Bound<i8> {
|
||||
Bound::Unbounded
|
||||
}
|
||||
|
||||
/// a..b Inclusive-Exclusive range struct
|
||||
/// since the builtin range isn't Copy for some reason
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct InExRange {
|
||||
start: i8,
|
||||
end: i8,
|
||||
}
|
||||
|
||||
pub fn ie_strict(start: i8, end: i8) -> InExRange {
|
||||
InExRange { start, end }
|
||||
}
|
||||
|
||||
impl RangeBounds<i8> for InExRange {
|
||||
fn start_bound(&self) -> Bound<&i8> {
|
||||
Bound::Included(&self.start)
|
||||
}
|
||||
fn end_bound(&self) -> Bound<&i8> {
|
||||
Bound::Excluded(&self.end)
|
||||
pub fn ie(x1: i8, x2: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: x1,
|
||||
end: x2.down().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromBounds<i8> for InExRange {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<i8>,
|
||||
end_bound: Bound<i8>,
|
||||
) -> Result<Self, crate::TryFromBoundsError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Included(start), Bound::Excluded(end)) => {
|
||||
Ok(InExRange { start, end })
|
||||
}
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
pub fn ei(x1: i8, x2: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: x1.up().unwrap(),
|
||||
end: x2,
|
||||
}
|
||||
}
|
||||
pub fn ee(x1: i8, x2: i8) -> DiscreteFiniteBounds<i8> {
|
||||
DiscreteFiniteBounds {
|
||||
start: x1.up().unwrap(),
|
||||
end: x2.down().unwrap(),
|
||||
}
|
||||
}
|
||||
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
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 Affero 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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::ops::{
|
||||
Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
|
||||
RangeToInclusive,
|
||||
};
|
||||
|
||||
use crate::TryFromBoundsError;
|
||||
|
||||
/// A "newtype" trait to copy [`TryFrom`].
|
||||
///
|
||||
/// I am forced to use this "newtype" instead of [`TryFrom`] because
|
||||
/// [`Range`] and friends don't implement `TryFrom<(Bound, Bound)>`
|
||||
///
|
||||
/// I personally think they should since then I wouldn't have to make
|
||||
/// a trait just for this. I have made a post about it here should you
|
||||
/// wish to comment your view.
|
||||
/// <https://internals.rust-lang.org/t/range-should-impl-tryfrom-bound-bound>
|
||||
///
|
||||
/// [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||
/// [`Range`]: https://doc.rust-lang.org/std/ops/struct.Range.html
|
||||
pub trait TryFromBounds<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for (Bound<I>, Bound<I>) {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
Ok((start_bound, end_bound))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for Range<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Included(start), Bound::Excluded(end)) => Ok(start..end),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeInclusive<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Included(start), Bound::Included(end)) => Ok(start..=end),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeFrom<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Included(start), Bound::Unbounded) => Ok(start..),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeTo<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Unbounded, Bound::Excluded(end)) => Ok(..end),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeToInclusive<I> {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Unbounded, Bound::Included(end)) => Ok(..=end),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TryFromBounds<I> for RangeFull {
|
||||
fn try_from_bounds(
|
||||
start_bound: Bound<I>,
|
||||
end_bound: Bound<I>,
|
||||
) -> Result<Self, TryFromBoundsError> {
|
||||
match (start_bound, end_bound) {
|
||||
(Bound::Unbounded, Bound::Unbounded) => Ok(..),
|
||||
_ => Err(TryFromBoundsError),
|
||||
}
|
||||
}
|
||||
}
|
186
src/utils.rs
186
src/utils.rs
@ -1,39 +1,36 @@
|
||||
/*
|
||||
Copyright 2022 James Forster
|
||||
|
||||
This file is part of range_bounds_map.
|
||||
This file is part of discrete_range_map.
|
||||
|
||||
range_bounds_map is free software: you can redistribute it and/or
|
||||
discrete_range_map is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU Affero 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,
|
||||
discrete_range_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
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with range_bounds_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Bound;
|
||||
|
||||
use crate::bound_ord::BoundOrd;
|
||||
use crate::range_bounds_map::NiceRange;
|
||||
use crate::discrete_finite::DiscreteFinite;
|
||||
use crate::discrete_finite_bounds::DiscreteFiniteBounds;
|
||||
use crate::discrete_range_map::FiniteRange;
|
||||
|
||||
pub(crate) fn cmp_range_with_bound_ord<A, B>(
|
||||
range: A,
|
||||
bound_ord: BoundOrd<B>,
|
||||
) -> Ordering
|
||||
pub(crate) fn cmp_point_with_range<I, K>(point: I, range: K) -> Ordering
|
||||
where
|
||||
A: NiceRange<B>,
|
||||
B: Ord,
|
||||
I: Ord,
|
||||
K: FiniteRange<I>,
|
||||
{
|
||||
if bound_ord < BoundOrd::start(range.start()) {
|
||||
if point < range.start() {
|
||||
Ordering::Less
|
||||
} else if bound_ord > BoundOrd::end(range.end()) {
|
||||
} else if point > range.end() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
@ -52,49 +49,46 @@ pub(crate) enum Config {
|
||||
}
|
||||
pub(crate) fn config<I, A, B>(a: A, b: B) -> Config
|
||||
where
|
||||
A: NiceRange<I>,
|
||||
B: NiceRange<I>,
|
||||
A: FiniteRange<I> + Copy,
|
||||
B: FiniteRange<I> + Copy,
|
||||
I: Ord,
|
||||
{
|
||||
match BoundOrd::start(a.start()) < BoundOrd::start(b.start()) {
|
||||
true => {
|
||||
match (
|
||||
contains_bound_ord(a, BoundOrd::start(b.start())),
|
||||
contains_bound_ord(a, BoundOrd::end(b.end())),
|
||||
) {
|
||||
(false, false) => Config::LeftFirstNonOverlapping,
|
||||
(true, false) => Config::LeftFirstPartialOverlap,
|
||||
(true, true) => Config::LeftContainsRight,
|
||||
(false, true) => unreachable!(),
|
||||
}
|
||||
if a.start() < b.start() {
|
||||
match (contains_point(a, b.start()), contains_point(a, b.end())) {
|
||||
(false, false) => Config::LeftFirstNonOverlapping,
|
||||
(true, false) => Config::LeftFirstPartialOverlap,
|
||||
(true, true) => Config::LeftContainsRight,
|
||||
(false, true) => unreachable!(),
|
||||
}
|
||||
false => {
|
||||
match (
|
||||
contains_bound_ord(b, BoundOrd::start(a.start())),
|
||||
contains_bound_ord(b, BoundOrd::end(a.end())),
|
||||
) {
|
||||
(false, false) => Config::RightFirstNonOverlapping,
|
||||
(true, false) => Config::RightFirstPartialOverlap,
|
||||
(true, true) => Config::RightContainsLeft,
|
||||
(false, true) => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
match (contains_point(b, a.start()), contains_point(b, a.end())) {
|
||||
(false, false) => Config::RightFirstNonOverlapping,
|
||||
(true, false) => Config::RightFirstPartialOverlap,
|
||||
(true, true) => Config::RightContainsLeft,
|
||||
(false, true) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SortedConfig<I> {
|
||||
NonOverlapping((Bound<I>, Bound<I>), (Bound<I>, Bound<I>)),
|
||||
PartialOverlap((Bound<I>, Bound<I>), (Bound<I>, Bound<I>)),
|
||||
Swallowed((Bound<I>, Bound<I>), (Bound<I>, Bound<I>)),
|
||||
NonOverlapping(DiscreteFiniteBounds<I>, DiscreteFiniteBounds<I>),
|
||||
PartialOverlap(DiscreteFiniteBounds<I>, DiscreteFiniteBounds<I>),
|
||||
Swallowed(DiscreteFiniteBounds<I>, DiscreteFiniteBounds<I>),
|
||||
}
|
||||
fn sorted_config<I, A, B>(a: A, b: B) -> SortedConfig<I>
|
||||
where
|
||||
A: NiceRange<I>,
|
||||
B: NiceRange<I>,
|
||||
A: FiniteRange<I> + Copy,
|
||||
B: FiniteRange<I> + Copy,
|
||||
I: Ord,
|
||||
{
|
||||
let ae = (a.start(), a.end());
|
||||
let be = (b.start(), b.end());
|
||||
let ae = DiscreteFiniteBounds {
|
||||
start: a.start(),
|
||||
end: a.end(),
|
||||
};
|
||||
let be = DiscreteFiniteBounds {
|
||||
start: b.start(),
|
||||
end: b.end(),
|
||||
};
|
||||
match config(a, b) {
|
||||
Config::LeftFirstNonOverlapping => SortedConfig::NonOverlapping(ae, be),
|
||||
Config::LeftFirstPartialOverlap => SortedConfig::Swallowed(ae, be),
|
||||
@ -110,28 +104,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contains_bound_ord<I, A>(range: A, bound_ord: BoundOrd<I>) -> bool
|
||||
pub(crate) fn contains_point<I, A>(range: A, point: I) -> bool
|
||||
where
|
||||
A: NiceRange<I>,
|
||||
A: FiniteRange<I>,
|
||||
I: Ord,
|
||||
{
|
||||
let start_bound_ord = BoundOrd::start(range.start());
|
||||
let end_bound_ord = BoundOrd::end(range.end());
|
||||
|
||||
return bound_ord >= start_bound_ord && bound_ord <= end_bound_ord;
|
||||
cmp_point_with_range(point, range).is_eq()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CutResult<I> {
|
||||
pub(crate) before_cut: Option<(Bound<I>, Bound<I>)>,
|
||||
pub(crate) inside_cut: Option<(Bound<I>, Bound<I>)>,
|
||||
pub(crate) after_cut: Option<(Bound<I>, Bound<I>)>,
|
||||
pub(crate) before_cut: Option<DiscreteFiniteBounds<I>>,
|
||||
pub(crate) inside_cut: Option<DiscreteFiniteBounds<I>>,
|
||||
pub(crate) after_cut: Option<DiscreteFiniteBounds<I>>,
|
||||
}
|
||||
pub(crate) fn cut_range<I, B, C>(base: B, cut: C) -> CutResult<I>
|
||||
where
|
||||
B: NiceRange<I>,
|
||||
C: NiceRange<I>,
|
||||
I: Ord + Copy,
|
||||
B: FiniteRange<I> + Copy,
|
||||
C: FiniteRange<I> + Copy,
|
||||
I: Ord + Copy + DiscreteFinite,
|
||||
{
|
||||
let mut result = CutResult {
|
||||
before_cut: None,
|
||||
@ -141,34 +132,61 @@ where
|
||||
|
||||
match config(base, cut) {
|
||||
Config::LeftFirstNonOverlapping => {
|
||||
result.before_cut = Some((base.start(), base.end()));
|
||||
result.before_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: base.end(),
|
||||
});
|
||||
}
|
||||
Config::LeftFirstPartialOverlap => {
|
||||
result.before_cut = Some((base.start(), flip_bound(cut.start())));
|
||||
result.inside_cut = Some((cut.start(), base.end()));
|
||||
result.before_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: cut.start().down().unwrap(),
|
||||
});
|
||||
result.inside_cut = Some(DiscreteFiniteBounds {
|
||||
start: cut.start(),
|
||||
end: base.end(),
|
||||
});
|
||||
}
|
||||
Config::LeftContainsRight => {
|
||||
result.before_cut = Some((base.start(), flip_bound(cut.start())));
|
||||
result.inside_cut = Some((cut.start(), cut.end()));
|
||||
// exception for Unbounded-ending things
|
||||
match cut.end() {
|
||||
Bound::Unbounded => {}
|
||||
_ => {
|
||||
result.after_cut =
|
||||
Some((flip_bound(cut.end()), base.end()));
|
||||
}
|
||||
result.before_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: cut.start().down().unwrap(),
|
||||
});
|
||||
result.inside_cut = Some(DiscreteFiniteBounds {
|
||||
start: cut.start(),
|
||||
end: cut.end(),
|
||||
});
|
||||
//if cut is already max then we don't need to have an
|
||||
//after_cut
|
||||
if let Some(upped_end) = cut.end().up() {
|
||||
result.after_cut = Some(DiscreteFiniteBounds {
|
||||
start: upped_end,
|
||||
end: base.end(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Config::RightFirstNonOverlapping => {
|
||||
result.after_cut = Some((base.start(), base.end()));
|
||||
result.after_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: base.end(),
|
||||
});
|
||||
}
|
||||
Config::RightFirstPartialOverlap => {
|
||||
result.after_cut = Some((flip_bound(cut.end()), base.end()));
|
||||
result.inside_cut = Some((base.start(), cut.end()));
|
||||
result.after_cut = Some(DiscreteFiniteBounds {
|
||||
start: cut.end().up().unwrap(),
|
||||
end: base.end(),
|
||||
});
|
||||
result.inside_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: cut.end(),
|
||||
});
|
||||
}
|
||||
Config::RightContainsLeft => {
|
||||
result.inside_cut = Some((base.start(), base.end()));
|
||||
result.inside_cut = Some(DiscreteFiniteBounds {
|
||||
start: base.start(),
|
||||
end: base.end(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,30 +201,16 @@ where
|
||||
pub(crate) fn is_valid_range<I, K>(range: K) -> bool
|
||||
where
|
||||
I: Ord,
|
||||
K: NiceRange<I>,
|
||||
K: FiniteRange<I>,
|
||||
{
|
||||
match (range.start(), range.end()) {
|
||||
(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,
|
||||
}
|
||||
range.start() <= range.end()
|
||||
}
|
||||
|
||||
pub(crate) fn overlaps<I, A, B>(a: A, b: B) -> bool
|
||||
where
|
||||
A: NiceRange<I>,
|
||||
B: NiceRange<I>,
|
||||
A: FiniteRange<I> + Copy,
|
||||
B: FiniteRange<I> + Copy,
|
||||
I: Ord,
|
||||
{
|
||||
!matches!(sorted_config(a, b), SortedConfig::NonOverlapping(_, _))
|
||||
}
|
||||
|
||||
pub(crate) 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,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user