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
No known key found for this signature in database
GPG Key ID: B2629F9EC7C2FE8C
6 changed files with 341 additions and 90 deletions

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
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
At the moment this crate is also designed to work only with [`Finite`]

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/>.
*/
//! The module containing [`DiscreteRangeMap`] and related types.
//! A module containing [`DiscreteRangeMap`] and related types.
use alloc::vec::Vec;
use core::cmp::Ordering;
@ -35,7 +35,7 @@ use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
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};
/// An ordered map of non-overlapping ranges based on [`BTreeMap`].
@ -671,7 +671,7 @@ where
end: second.0.down().unwrap(),
})
})
.filter(|range| is_valid_range(*range));
.filter(|range| range.is_valid());
//possibly add the trimmed start and end gaps
return trimmed_start_gap
@ -1261,7 +1261,7 @@ where
///
/// let map: DiscreteRangeMap<_, _, _> =
/// DiscreteRangeMap::from_iter_strict(
/// slice.into_iter().filter(|(range, _)| range.start > 2),
/// slice.into_iter().filter(|(range, _)| range.start() > 2),
/// )
/// .unwrap();
/// ```
@ -1428,12 +1428,12 @@ impl<I, K, V> DiscreteRangeMap<I, K, V> {
// Helper Functions ==========================
fn invalid_range_panic<Q, I>(range: Q)
pub(crate) fn invalid_range_panic<Q, I>(range: Q)
where
I: PointType,
Q: RangeType<I>,
{
if !is_valid_range(range) {
if !range.is_valid() {
panic!(
"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() {
let result: CutResult<i8> = cut_range(ie(3, 8), ie(5, 8));
if let Some(x) = result.before_cut {
assert!(is_valid_range(x));
assert!(x.is_valid());
}
if let Some(x) = result.inside_cut {
assert!(is_valid_range(x));
assert!(x.is_valid());
}
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));
if let Some(x) = result.before_cut {
assert!(is_valid_range(x));
assert!(x.is_valid());
}
if let Some(x) = result.inside_cut {
assert!(is_valid_range(x));
assert!(x.is_valid());
}
if let Some(x) = result.after_cut {
assert!(is_valid_range(x));
assert!(x.is_valid());
}
}

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/>.
*/
//! The module containing [`DiscreteRangeSet`] and related types. Since
//! [`DiscreteRangeSet`] is just a wrapper around [`DiscreteRangeMap`], most of
//! the methods' docs will point towards the equivalent method's docs on
//! [`DiscreteRangeMap`] to prevent inconsistency.
//! A module containing [`DiscreteRangeSet`] and related types.
//!
//! Since [`DiscreteRangeSet`] is just a wrapper around
//! [`DiscreteRangeMap`], most of the methods' docs will point towards the
//! equivalent method's docs on [`DiscreteRangeMap`] to prevent
//! inconsistency.
use core::fmt;
use core::marker::PhantomData;

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/>.
*/
//! 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
/// for use by library users if they don't wish to create their own
/// interval types.
/// An inclusive interval, only valid intervals can be constructed.
///
/// 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)]
pub struct InclusiveInterval<I> {
/// The start of the interval, inclusive.
pub start: I,
pub(crate) start: I,
/// 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>
where
I: PointType,
@ -60,63 +116,262 @@ where
}
}
/// An unbounded-unbounded interval
pub fn uu() -> InclusiveInterval<i8> {
InclusiveInterval {
start: i8::MIN,
end: i8::MAX,
}
/// Create an new Unbounded-Unbounded interval.
///
/// # 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::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
pub fn ui(x: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
start: i8::MIN,
/// Create an new Unbounded-Included interval.
///
/// # 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::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,
}
};
invalid_range_panic(interval);
interval
}
/// An unbounded-excluded interval
pub fn ue(x: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
start: i8::MIN,
/// Create an new Unbounded-Excluded interval.
///
/// # 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::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(),
}
};
invalid_range_panic(interval);
interval
}
/// An included-unbounded interval
pub fn iu(x: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
/// Create an new Included-Unbounded interval.
///
/// # 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,
end: i8::MAX,
}
end: I::MAX,
};
invalid_range_panic(interval);
interval
}
/// An excluded-unbounded interval
pub fn eu(x: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
/// Create an new Excluded-Unbounded interval.
///
/// # 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(),
end: i8::MAX,
}
end: I::MAX,
};
invalid_range_panic(interval);
interval
}
/// An included-included interval
pub fn ii(x1: i8, x2: i8) -> InclusiveInterval<i8> {
InclusiveInterval { start: x1, end: x2 }
/// Create an new Included-Included interval.
///
/// # 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
pub fn ie(x1: i8, x2: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
/// Create an new Included-Excluded interval.
///
/// # 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,
end: x2.down().unwrap(),
}
};
invalid_range_panic(interval);
interval
}
/// An excluded-included interval
pub fn ei(x1: i8, x2: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
/// Create an new Excluded-Included interval.
///
/// # 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(),
end: x2,
}
};
invalid_range_panic(interval);
interval
}
/// An excluded-excluded interval
pub fn ee(x1: i8, x2: i8) -> InclusiveInterval<i8> {
InclusiveInterval {
/// Create an new Excluded-Excluded interval.
///
/// # 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(),
end: x2.down().unwrap(),
}
};
invalid_range_panic(interval);
interval
}

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>>
//! impl From<InclusiveInterval<i8>> for Reservation {
//! fn from(value: InclusiveInterval<i8>) -> Self {
//! if value.end == i8::MAX {
//! Reservation::Infinite(value.start)
//! if value.end() == i8::MAX {
//! Reservation::Infinite(value.start())
//! } else {
//! Reservation::Finite(
//! value.start,
//! value.end.up().unwrap(),
//! value.start(),
//! 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
//! 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
//!
//! 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
//! // returned by `get_entry_at_point()`, for example:
//!
//! use discrete_range_map::inclusive_interval::uu;
//! use discrete_range_map::{DiscreteRangeMap, InclusiveInterval};
//!
//! 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));
//!
//! assert_eq!(
//! gap,
//! Err(InclusiveInterval {
//! start: WithInfinity::Finite(0),
//! end: WithInfinity::Infinity,
//! })
//! );
//! assert_eq!(gap, Err(uu()));
//! ```
//!
//! ### Invalid Ranges
@ -392,4 +391,3 @@ pub use crate::discrete_range_map::{
};
pub use crate::discrete_range_set::DiscreteRangeSet;
pub use crate::inclusive_interval::InclusiveInterval;

View File

@ -19,7 +19,7 @@ along with discrete_range_map. If not, see <https://www.gnu.org/licenses/>.
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
where
@ -190,20 +190,12 @@ where
//only return valid ranges
return CutResult {
before_cut: result.before_cut.filter(|x| is_valid_range(*x)),
inside_cut: result.inside_cut.filter(|x| is_valid_range(*x)),
after_cut: result.after_cut.filter(|x| is_valid_range(*x)),
before_cut: result.before_cut.filter(|x| x.is_valid()),
inside_cut: result.inside_cut.filter(|x| x.is_valid()),
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
where
I: PointType,