Merge pull request #41 from ripytide/discrete

discrete_range_map
This commit is contained in:
ripytide 2023-04-24 09:40:44 +01:00 committed by GitHub
commit c65afb8237
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 2940 additions and 3921 deletions

View File

@ -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
View File

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

View File

@ -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
View File

@ -1,76 +1,81 @@
# range_bounds_map
# discrete_range_map
[![License](https://img.shields.io/github/license/ripytide/range_bounds_map)](https://www.gnu.org/licenses/agpl-3.0.en.html)
[![Docs](https://docs.rs/range_bounds_map/badge.svg)](https://docs.rs/range_bounds_map)
[![License](https://img.shields.io/github/license/ripytide/discrete_range_map)](https://www.gnu.org/licenses/agpl-3.0.en.html)
[![Docs](https://docs.rs/discrete_range_map/badge.svg)](https://docs.rs/discrete_range_map)
[![Maintained](https://img.shields.io/maintenance/yes/2023)](https://github.com/ripytide)
[![Crates.io](https://img.shields.io/crates/v/range_bounds_map)](https://crates.io/crates/range_bounds_map)
[![Crates.io](https://img.shields.io/crates/v/discrete_range_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

View File

@ -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
View 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,);

View 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

File diff suppressed because it is too large Load Diff

259
src/discrete_range_set.rs Normal file
View 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)
}
}

View File

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

View File

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

View File

@ -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(),
}
}

View File

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

View File

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