made InclusiveInterval validity a construction time invariant

This commit is contained in:
ripytide
2023-12-27 14:47:06 +00:00
parent 6de99232df
commit 3616225c13
6 changed files with 341 additions and 90 deletions
+4
View File
@@ -127,6 +127,10 @@ differ depending on whether the underlying type is `Discrete` or
`Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the `Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the
value `6.5` exists. value `6.5` exists.
Importantly, this also makes Inclusive/Exclusive ended ranges really
easy to work with as they can be losslessly converted between one
another. For example, `3..6` is equivalent to `3..=5`.
### Finite-ness ### Finite-ness
At the moment this crate is also designed to work only with [`Finite`] At the moment this crate is also designed to work only with [`Finite`]
+12 -12
View File
@@ -17,7 +17,7 @@ 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/>. along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
*/ */
//! The module containing [`DiscreteRangeMap`] and related types. //! A module containing [`DiscreteRangeMap`] and related types.
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cmp::Ordering; use core::cmp::Ordering;
@@ -35,7 +35,7 @@ use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq; use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::utils::{cmp_point_with_range, cut_range, is_valid_range, overlaps}; use crate::utils::{cmp_point_with_range, cut_range, overlaps};
use crate::{DiscreteFinite, InclusiveInterval}; use crate::{DiscreteFinite, InclusiveInterval};
/// An ordered map of non-overlapping ranges based on [`BTreeMap`]. /// An ordered map of non-overlapping ranges based on [`BTreeMap`].
@@ -671,7 +671,7 @@ where
end: second.0.down().unwrap(), end: second.0.down().unwrap(),
}) })
}) })
.filter(|range| is_valid_range(*range)); .filter(|range| range.is_valid());
//possibly add the trimmed start and end gaps //possibly add the trimmed start and end gaps
return trimmed_start_gap return trimmed_start_gap
@@ -1261,7 +1261,7 @@ where
/// ///
/// let map: DiscreteRangeMap<_, _, _> = /// let map: DiscreteRangeMap<_, _, _> =
/// DiscreteRangeMap::from_iter_strict( /// DiscreteRangeMap::from_iter_strict(
/// slice.into_iter().filter(|(range, _)| range.start > 2), /// slice.into_iter().filter(|(range, _)| range.start() > 2),
/// ) /// )
/// .unwrap(); /// .unwrap();
/// ``` /// ```
@@ -1428,12 +1428,12 @@ impl<I, K, V> DiscreteRangeMap<I, K, V> {
// Helper Functions ========================== // Helper Functions ==========================
fn invalid_range_panic<Q, I>(range: Q) pub(crate) fn invalid_range_panic<Q, I>(range: Q)
where where
I: PointType, I: PointType,
Q: RangeType<I>, Q: RangeType<I>,
{ {
if !is_valid_range(range) { if !range.is_valid() {
panic!( panic!(
"invalid range given to function see here for more details: https://docs.rs/discrete_range_map/latest/discrete_range_map/#invalid-ranges" "invalid range given to function see here for more details: https://docs.rs/discrete_range_map/latest/discrete_range_map/#invalid-ranges"
); );
@@ -2304,24 +2304,24 @@ mod tests {
fn cut_range_bounds_should_return_valid_ranges() { fn cut_range_bounds_should_return_valid_ranges() {
let result: CutResult<i8> = cut_range(ie(3, 8), ie(5, 8)); let result: CutResult<i8> = cut_range(ie(3, 8), ie(5, 8));
if let Some(x) = result.before_cut { if let Some(x) = result.before_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
if let Some(x) = result.inside_cut { if let Some(x) = result.inside_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
if let Some(x) = result.after_cut { if let Some(x) = result.after_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
let result = cut_range(ie(3, 8), ie(3, 5)); let result = cut_range(ie(3, 8), ie(3, 5));
if let Some(x) = result.before_cut { if let Some(x) = result.before_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
if let Some(x) = result.inside_cut { if let Some(x) = result.inside_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
if let Some(x) = result.after_cut { if let Some(x) = result.after_cut {
assert!(is_valid_range(x)); assert!(x.is_valid());
} }
} }
+6 -4
View File
@@ -17,10 +17,12 @@ 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/>. along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
*/ */
//! The module containing [`DiscreteRangeSet`] and related types. Since //! A module containing [`DiscreteRangeSet`] and related types.
//! [`DiscreteRangeSet`] is just a wrapper around [`DiscreteRangeMap`], most of //!
//! the methods' docs will point towards the equivalent method's docs on //! Since [`DiscreteRangeSet`] is just a wrapper around
//! [`DiscreteRangeMap`] to prevent inconsistency. //! [`DiscreteRangeMap`], most of the methods' docs will point towards the
//! equivalent method's docs on [`DiscreteRangeMap`] to prevent
//! inconsistency.
use core::fmt; use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
+305 -50
View File
@@ -17,24 +17,80 @@ 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/>. along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
*/ */
//! A module containing [`InclusiveInterval`] and it's various constructor functions. //! A module containing [`InclusiveInterval`] and its constructors.
//!
//! The constructors are not associated functions as then you must write
//! `InclusiveInterval` before it every time you want create an interval
//! which is a bit annoying as you can't import associated function in rust
//! yet. If you would still like the associated versions I would be happy to
//! add them as well, just open a PR/Issue.
use core::ops::{RangeBounds, Bound}; use core::ops::{Bound, RangeBounds};
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use crate::{DiscreteFinite, PointType, InclusiveRange}; use crate::discrete_range_map::invalid_range_panic;
use crate::{InclusiveRange, PointType};
/// The interval type used throughout this crate both for the examples and /// An inclusive interval, only valid intervals can be constructed.
/// for use by library users if they don't wish to create their own ///
/// interval types. /// This interval struct is used throughout this crate for the examples and
/// tests but can also be used by library users if they don't wish to create
/// their own interval types.
///
/// To create an `InclusiveInterval` use one of the various contrutor
/// functions which will all panic if you try to create an invalid range.
/// See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::{ee, ii};
///
/// let inclusive_interval = ii(4, 4);
/// let exclusive_interval = ee(3, 5);
///
/// assert_eq!(inclusive_interval, exclusive_interval);
/// ```
///
/// ```should_panic
/// use discrete_range_map::inclusive_interval::ee;
///
/// let invalid_interval = ee(4, 4);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct InclusiveInterval<I> { pub struct InclusiveInterval<I> {
/// The start of the interval, inclusive. /// The start of the interval, inclusive.
pub start: I, pub(crate) start: I,
/// The end of the interval, inclusive. /// The end of the interval, inclusive.
pub end: I, pub(crate) end: I,
} }
impl<I> InclusiveInterval<I>
where
I: PointType,
{
/// The start of the range, inclusive.
///
/// ```
/// use discrete_range_map::inclusive_interval::ii;
///
/// assert_eq!(ii(2, 4).start(), 2);
/// ```
pub fn start(&self) -> I {
self.start
}
/// The end of the range, inclusive.
///
/// ```
/// use discrete_range_map::inclusive_interval::ii;
///
/// assert_eq!(ii(2, 4).end(), 4);
/// ```
pub fn end(&self) -> I {
self.end
}
}
impl<I> RangeBounds<I> for InclusiveInterval<I> impl<I> RangeBounds<I> for InclusiveInterval<I>
where where
I: PointType, I: PointType,
@@ -60,63 +116,262 @@ where
} }
} }
/// An unbounded-unbounded interval /// Create an new Unbounded-Unbounded interval.
pub fn uu() -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
start: i8::MIN, ///
end: i8::MAX, /// Panics if the range is an invalid range. See [`Invalid
} /// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::uu;
/// use discrete_range_map::InclusiveInterval;
///
/// let interval1: InclusiveInterval<u8> = uu();
/// let interval2: InclusiveInterval<u8> = uu();
///
/// assert_eq!(interval1, interval2)
/// ```
pub fn uu<I>() -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: I::MIN,
end: I::MAX,
};
invalid_range_panic(interval);
interval
} }
/// An unbounded-included interval /// Create an new Unbounded-Included interval.
pub fn ui(x: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
start: i8::MIN, ///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ui;
///
/// let interval1 = ui(1);
/// let interval2 = ui(4);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ui<I>(x: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: I::MIN,
end: x, end: x,
} };
invalid_range_panic(interval);
interval
} }
/// An unbounded-excluded interval /// Create an new Unbounded-Excluded interval.
pub fn ue(x: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
start: i8::MIN, ///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ue;
///
/// let interval1 = ue(1);
/// let interval2 = ue(4);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ue<I>(x: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: I::MIN,
end: x.down().unwrap(), end: x.down().unwrap(),
} };
invalid_range_panic(interval);
interval
} }
/// An included-unbounded interval /// Create an new Included-Unbounded interval.
pub fn iu(x: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::iu;
///
/// let interval1 = iu(1);
/// let interval2 = iu(4);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn iu<I>(x: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: x, start: x,
end: i8::MAX, end: I::MAX,
} };
invalid_range_panic(interval);
interval
} }
/// An excluded-unbounded interval /// Create an new Excluded-Unbounded interval.
pub fn eu(x: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::eu;
///
/// let interval1 = eu(1);
/// let interval2 = eu(4);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn eu<I>(x: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: x.up().unwrap(), start: x.up().unwrap(),
end: i8::MAX, end: I::MAX,
} };
invalid_range_panic(interval);
interval
} }
/// An included-included interval /// Create an new Included-Included interval.
pub fn ii(x1: i8, x2: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { start: x1, end: x2 } /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ii;
///
/// let interval1 = ii(0, 4);
/// let interval2 = ii(2, 6);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ii<I>(x1: I, x2: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval { start: x1, end: x2 };
invalid_range_panic(interval);
interval
} }
/// An included-excluded interval /// Create an new Included-Excluded interval.
pub fn ie(x1: i8, x2: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ie;
///
/// let interval1 = ie(0, 4);
/// let interval2 = ie(2, 6);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ie<I>(x1: I, x2: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: x1, start: x1,
end: x2.down().unwrap(), end: x2.down().unwrap(),
} };
invalid_range_panic(interval);
interval
} }
/// An excluded-included interval /// Create an new Excluded-Included interval.
pub fn ei(x1: i8, x2: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ei;
///
/// let interval1 = ei(0, 4);
/// let interval2 = ei(2, 6);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ei<I>(x1: I, x2: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: x1.up().unwrap(), start: x1.up().unwrap(),
end: x2, end: x2,
} };
invalid_range_panic(interval);
interval
} }
/// An excluded-excluded interval /// Create an new Excluded-Excluded interval.
pub fn ee(x1: i8, x2: i8) -> InclusiveInterval<i8> { ///
InclusiveInterval { /// # Panics
///
/// Panics if the range is an invalid range. See [`Invalid
/// Ranges`](https://docs.rs/discrete_range_map/latest/discrete_range_map/index.html#invalid-ranges)
/// for more details.
///
/// ```
/// use discrete_range_map::inclusive_interval::ee;
///
/// let interval1 = ee(0, 4);
/// let interval2 = ee(2, 6);
///
/// assert_ne!(interval1, interval2)
/// ```
pub fn ee<I>(x1: I, x2: I) -> InclusiveInterval<I>
where
I: PointType,
{
let interval = InclusiveInterval {
start: x1.up().unwrap(), start: x1.up().unwrap(),
end: x2.down().unwrap(), end: x2.down().unwrap(),
} };
invalid_range_panic(interval);
interval
} }
+10 -12
View File
@@ -86,12 +86,12 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
//! // Second, we need to implement From<InclusiveInterval<i8>> //! // Second, we need to implement From<InclusiveInterval<i8>>
//! impl From<InclusiveInterval<i8>> for Reservation { //! impl From<InclusiveInterval<i8>> for Reservation {
//! fn from(value: InclusiveInterval<i8>) -> Self { //! fn from(value: InclusiveInterval<i8>) -> Self {
//! if value.end == i8::MAX { //! if value.end() == i8::MAX {
//! Reservation::Infinite(value.start) //! Reservation::Infinite(value.start())
//! } else { //! } else {
//! Reservation::Finite( //! Reservation::Finite(
//! value.start, //! value.start(),
//! value.end.up().unwrap(), //! value.end().up().unwrap(),
//! ) //! )
//! } //! }
//! } //! }
@@ -135,6 +135,10 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
//! `Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the //! `Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the
//! value `6.5` exists. //! value `6.5` exists.
//! //!
//! Importantly, this also makes Inclusive/Exclusive ended ranges really
//! easy to work with as they can be losslessly converted between one
//! another. For example, `3..6` is equivalent to `3..=5`.
//!
//! ### Finite-ness //! ### Finite-ness
//! //!
//! At the moment this crate is also designed to work only with [`Finite`] //! At the moment this crate is also designed to work only with [`Finite`]
@@ -231,6 +235,7 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
//! // Infinity is encountered such as when it might be //! // Infinity is encountered such as when it might be
//! // returned by `get_entry_at_point()`, for example: //! // returned by `get_entry_at_point()`, for example:
//! //!
//! use discrete_range_map::inclusive_interval::uu;
//! use discrete_range_map::{DiscreteRangeMap, InclusiveInterval}; //! use discrete_range_map::{DiscreteRangeMap, InclusiveInterval};
//! //!
//! let map: DiscreteRangeMap< //! let map: DiscreteRangeMap<
@@ -241,13 +246,7 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
//! //!
//! let mut gap = map.get_entry_at_point(WithInfinity::Finite(4)); //! let mut gap = map.get_entry_at_point(WithInfinity::Finite(4));
//! //!
//! assert_eq!( //! assert_eq!(gap, Err(uu()));
//! gap,
//! Err(InclusiveInterval {
//! start: WithInfinity::Finite(0),
//! end: WithInfinity::Infinity,
//! })
//! );
//! ``` //! ```
//! //!
//! ### Invalid Ranges //! ### Invalid Ranges
@@ -392,4 +391,3 @@ pub use crate::discrete_range_map::{
}; };
pub use crate::discrete_range_set::DiscreteRangeSet; pub use crate::discrete_range_set::DiscreteRangeSet;
pub use crate::inclusive_interval::InclusiveInterval; pub use crate::inclusive_interval::InclusiveInterval;
+4 -12
View File
@@ -19,7 +19,7 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
use core::cmp::Ordering; use core::cmp::Ordering;
use crate::{InclusiveInterval, PointType, RangeType}; use crate::{InclusiveInterval, PointType, RangeType, InclusiveRange};
pub(crate) fn cmp_point_with_range<I, K>(point: I, range: K) -> Ordering pub(crate) fn cmp_point_with_range<I, K>(point: I, range: K) -> Ordering
where where
@@ -190,20 +190,12 @@ where
//only return valid ranges //only return valid ranges
return CutResult { return CutResult {
before_cut: result.before_cut.filter(|x| is_valid_range(*x)), before_cut: result.before_cut.filter(|x| x.is_valid()),
inside_cut: result.inside_cut.filter(|x| is_valid_range(*x)), inside_cut: result.inside_cut.filter(|x| x.is_valid()),
after_cut: result.after_cut.filter(|x| is_valid_range(*x)), after_cut: result.after_cut.filter(|x| x.is_valid()),
}; };
} }
pub(crate) fn is_valid_range<I, K>(range: K) -> bool
where
I: PointType,
K: RangeType<I>,
{
range.start() <= range.end()
}
pub(crate) fn overlaps<I, A, B>(a: A, b: B) -> bool pub(crate) fn overlaps<I, A, B>(a: A, b: B) -> bool
where where
I: PointType, I: PointType,