Skip to content

Commit

Permalink
implemented the very tricky is_zero_overlap() function and added test…
Browse files Browse the repository at this point in the history
…s to verify it
  • Loading branch information
ripytide committed Jan 20, 2024
1 parent cfda2b2 commit 28fba3d
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 107 deletions.
122 changes: 91 additions & 31 deletions src/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with nodit. If not, see <https://www.gnu.org/licenses/>.
*/

//! A module containing [`InclusiveInterval`] and its constructors.
//! A module containing [`Interval`] and [`InclusiveInterval`].
//!
//! The constructors are not associated functions as then you must write
//! `InclusiveInterval` before it every time you want create an interval
Expand Down Expand Up @@ -402,45 +402,96 @@ where
/// A interval that has **Inclusive** end-points.
pub trait InclusiveInterval<I> {
/// The start of the interval, inclusive.
///
/// # Examples
/// ```
/// use nodit::interval::{ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(3, 4).start(), 3);
/// assert_eq!(ii(4, 5).start(), 4);
/// assert_eq!(ie(5, 6).start(), 5);
/// ```
fn start(&self) -> I;
/// The end of the interval, inclusive.
///
/// # Examples
/// ```
/// use nodit::interval::{ei, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(3, 4).end(), 4);
/// assert_eq!(ii(4, 5).end(), 5);
/// assert_eq!(ei(5, 6).end(), 6);
/// ```
fn end(&self) -> I;

/// Does the interval contain the given point?
///
/// # Examples
/// ```
/// use nodit::interval::{ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 5).contains(3), false);
/// assert_eq!(ii(4, 5).contains(4), true);
/// assert_eq!(ii(4, 5).contains(5), true);
/// assert_eq!(ii(4, 5).contains(6), false);
/// ```
fn contains(&self, point: I) -> bool
where
I: PointType,
{
point >= self.start() && point <= self.end()
}

/// Is the interval is valid, which according to this crate means `start()`
/// <= `end()`
/// Is the interval is valid, which according to this crate means `start()` <= `end()`
///
/// # Examples
/// ```
/// use nodit::interval::{ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 4).is_valid(), true);
/// assert_eq!(ii(4, 5).is_valid(), true);
/// assert_eq!(ie(4, 5).is_valid(), true);
/// ```
fn is_valid(&self) -> bool
where
I: PointType,
{
self.start() <= self.end()
}

/// Requires that self comes before other and they don't overlap
fn touches_ordered(&self, other: &Self) -> bool
where
I: PointType,
{
self.end() == other.start().down().unwrap()
}

/// Requires that self comes before other
fn overlaps_ordered(&self, other: &Self) -> bool
/// Is `self` an interval over a single point?
///
/// # Examples
/// ```
/// use nodit::interval::{ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 4).is_singular(), true);
/// assert_eq!(ii(4, 5).is_singular(), false);
/// assert_eq!(ie(4, 5).is_singular(), true);
/// ```
fn is_singular(&self) -> bool
where
I: PointType,
{
self.contains(other.start()) || self.contains(other.end())
self.start() == self.end()
}

/// Intersect the interval with the other one, and return Some if the intersection is not empty.
fn intersect(&self, other: &Self) -> Option<Self>
/// Returns the intersection between `self` and `other` if there is any.
///
/// # Examples
/// ```
/// use nodit::interval::{ee, ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 6).intersection(&ie(6, 8)), Some(ii(6, 6)));
/// assert_eq!(ii(4, 6).intersection(&ee(6, 8)), None);
/// ```
fn intersection(&self, other: &Self) -> Option<Self>
where
I: PointType,
Self: From<Interval<I>>,
Expand All @@ -457,7 +508,16 @@ pub trait InclusiveInterval<I> {
}
}

/// Move the entire interval by the given amount.
/// Move the entire interval by the given `delta` amount upwards.
///
/// # Examples
/// ```
/// use nodit::interval::{ee, ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 6).translate(8), ii(12, 14));
/// assert_eq!(ee(4, 6).translate(-4), ee(0, 2));
/// ```
fn translate(&self, delta: I) -> Self
where
I: PointType,
Expand All @@ -470,23 +530,23 @@ pub trait InclusiveInterval<I> {
})
}

/// The amount between the start and the end points of the interval.
fn size(&self) -> I
/// The amount between the start and the end of the interval.
///
/// # Examples
/// ```
/// use nodit::interval::{ee, ei, ie, ii};
/// use nodit::InclusiveInterval;
///
/// assert_eq!(ii(4, 6).width(), 2);
/// assert_eq!(ie(4, 6).width(), 1);
/// assert_eq!(ei(4, 6).width(), 1);
/// assert_eq!(ee(4, 6).width(), 0);
/// ```
fn width(&self) -> I
where
I: PointType,
I: core::ops::Sub<Output = I>,
{
(self.end() - self.start()).up().unwrap()
}

/// Requires that self comes before other
fn merge_ordered(&self, other: &Self) -> Self
where
Self: From<Interval<I>>,
{
Self::from(Interval {
start: self.start(),
end: other.end(),
})
self.end() - self.start()
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,5 @@ pub use crate::discrete_finite::DiscreteFinite;
pub use crate::interval::{InclusiveInterval, Interval};
pub use crate::nodit::map::{IntervalType, NoditMap, OverlapError, PointType};
pub use crate::nodit::set::NoditSet;
pub use crate::zosdit::map::{NonZeroOverlapError, ZosditMap};
pub use crate::zosdit::set::ZosditSet;
59 changes: 7 additions & 52 deletions src/nodit/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ You should have received a copy of the GNU Affero General Public License
along with nodit. If not, see <https://www.gnu.org/licenses/>.
*/

//! A module containing [`NoditMap`] and related types.
//! A module containing [`NoditMap`].
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::fmt::{self, Debug};
use core::iter::once;
use core::marker::PhantomData;
Expand All @@ -36,7 +35,8 @@ use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::utils::{
cmp_point_with_interval, cut_interval, invalid_interval_panic, overlaps,
cut_interval, double_comp, invalid_interval_panic, overlapping_comp,
overlaps, touching_end_comp, touching_start_comp,
};
use crate::{DiscreteFinite, InclusiveInterval, Interval};

Expand Down Expand Up @@ -118,7 +118,7 @@ where
K: IntervalType<I>,
{
/// Returns `true` if the given interval overlaps any of the
/// other intervals in the map, and `false` if not.
/// intervals in the map, and `false` if not.
///
/// # Panics
///
Expand Down Expand Up @@ -1533,45 +1533,6 @@ impl<I, K, V> NoditMap<I, K, V> {
}
}

// Helper Functions ==========================

fn double_comp<K, I>() -> impl FnMut(&K, &K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
|inner_interval: &K, new_interval: &K| {
new_interval.start().cmp(&inner_interval.start())
}
}
fn overlapping_comp<I, K>(point: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| cmp_point_with_interval(point, *inner_interval)
}
fn touching_start_comp<I, K>(start: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| match inner_interval.end().up() {
Some(touching_position) => start.cmp(&touching_position),
None => Ordering::Less,
}
}
fn touching_end_comp<I, K>(end: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| match inner_interval.start().down() {
Some(touching_position) => end.cmp(&touching_position),
None => Ordering::Greater,
}
}

// Trait Impls ==========================

impl<I, K, V> IntoIterator for NoditMap<I, K, V> {
Expand Down Expand Up @@ -2374,14 +2335,14 @@ mod tests {
fn test_intersection() {
let input = Interval { start: 5, end: 10 };
assert_eq!(
input.intersect(&Interval { start: 8, end: 13 }),
input.intersection(&Interval { start: 8, end: 13 }),
Some(Interval { start: 8, end: 10 })
);
assert_eq!(
input.intersect(&Interval { start: 10, end: 13 }),
input.intersection(&Interval { start: 10, end: 13 }),
Some(Interval { start: 10, end: 10 })
);
assert_eq!(input.intersect(&Interval { start: 11, end: 13 }), None);
assert_eq!(input.intersection(&Interval { start: 11, end: 13 }), None);
}

#[test]
Expand All @@ -2391,12 +2352,6 @@ mod tests {
assert_eq!(input.translate(-2), Interval { start: 3, end: 8 });
}

#[test]
fn test_size() {
assert_eq!(Interval { start: 5, end: 10 }.size(), 6);
assert_eq!(Interval { start: 6, end: 6 }.size(), 1);
}

// Test Helper Functions
//======================
fn all_non_overlapping_test_bound_entries()
Expand Down
2 changes: 2 additions & 0 deletions src/nodit/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
//! A module containing the [`NoditMap`] and [`NoditSet`] data-structures.
pub mod map;
pub mod set;
2 changes: 1 addition & 1 deletion src/nodit/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with nodit. If not, see <https://www.gnu.org/licenses/>.
*/

//! A module containing [`NoditSet`] and related types.
//! A module containing [`NoditSet`].
//!
//! Since [`NoditSet`] is just a wrapper around
//! [`NoditMap`], most of the methods' docs will point towards the
Expand Down
37 changes: 37 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,43 @@ where
!matches!(sorted_config(a, b), SortedConfig::NonOverlapping(_, _))
}

pub(crate) fn double_comp<K, I>() -> impl FnMut(&K, &K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
|inner_interval: &K, new_interval: &K| {
new_interval.start().cmp(&inner_interval.start())
}
}
pub(crate) fn overlapping_comp<I, K>(point: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| cmp_point_with_interval(point, *inner_interval)
}
pub(crate) fn touching_start_comp<I, K>(start: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| match inner_interval.end().up() {
Some(touching_position) => start.cmp(&touching_position),
None => Ordering::Less,
}
}
pub(crate) fn touching_end_comp<I, K>(end: I) -> impl FnMut(&K) -> Ordering
where
I: PointType,
K: IntervalType<I>,
{
move |inner_interval: &K| match inner_interval.start().down() {
Some(touching_position) => end.cmp(&touching_position),
None => Ordering::Greater,
}
}

pub(crate) fn invalid_interval_panic<Q, I>(interval: Q)
where
I: PointType,
Expand Down
Loading

0 comments on commit 28fba3d

Please sign in to comment.