From 35bea9095581e57ef87a07031d6cd5db65a0c62d Mon Sep 17 00:00:00 2001 From: Gregory Sobol Date: Wed, 29 Nov 2023 21:20:46 +0100 Subject: [PATCH] feat(core): Add crate for Numerated (#3471) --- Cargo.lock | 13 + Cargo.toml | 2 + common/numerated/Cargo.toml | 26 + common/numerated/src/interval.rs | 505 +++++++++++++++++ common/numerated/src/lib.rs | 46 ++ common/numerated/src/mock.rs | 147 +++++ common/numerated/src/numerated.rs | 197 +++++++ common/numerated/src/tests.rs | 90 ++++ common/numerated/src/tree.rs | 866 ++++++++++++++++++++++++++++++ 9 files changed, 1892 insertions(+) create mode 100644 common/numerated/Cargo.toml create mode 100644 common/numerated/src/interval.rs create mode 100644 common/numerated/src/lib.rs create mode 100644 common/numerated/src/mock.rs create mode 100644 common/numerated/src/numerated.rs create mode 100644 common/numerated/src/tests.rs create mode 100644 common/numerated/src/tree.rs diff --git a/Cargo.lock b/Cargo.lock index 808b9c2cac2..704102c92dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7264,6 +7264,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "numerated" +version = "1.0.3" +dependencies = [ + "derive_more", + "env_logger", + "log", + "num-traits", + "parity-scale-codec", + "proptest", + "scale-info", +] + [[package]] name = "object" version = "0.28.4" diff --git a/Cargo.toml b/Cargo.toml index 52caa73631f..fafcaeda702 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ default-members = ["node/cli"] members = [ "common", "common/codegen", + "common/numerated", "core", "core-backend", "core-processor", @@ -192,6 +193,7 @@ tempfile = "3.8.1" gwasm-instrument = { version = "0.2.3", default-features = false } # Internal deps +numerated = { path = "common/numerated" } authorship = { package = "gear-authorship", path = "node/authorship" } common = { package = "gear-common", path = "common", default-features = false } core-processor = { package = "gear-core-processor", path = "core-processor", default-features = false } diff --git a/common/numerated/Cargo.toml b/common/numerated/Cargo.toml new file mode 100644 index 00000000000..994f9f7813f --- /dev/null +++ b/common/numerated/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "numerated" +description = "A library for working with intervals and sets of numerated values" +keywords = ["gear", "tree", "interval", "numerated", "no-std", "math"] +categories = ["mathematics", "no-std"] +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +num-traits.workspace = true +derive_more.workspace = true +scale-info = { workspace = true, features = ["derive"] } +parity-scale-codec = { workspace = true, features = ["derive"] } +log = { workspace = true, optional = true } + +[dev-dependencies] +env_logger.workspace = true +proptest.workspace = true +log.workspace = true + +[features] +mock = ["log"] diff --git a/common/numerated/src/interval.rs b/common/numerated/src/interval.rs new file mode 100644 index 00000000000..8a5d7a20dd7 --- /dev/null +++ b/common/numerated/src/interval.rs @@ -0,0 +1,505 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! [NonEmptyInterval], [Interval] implementations. + +use core::{ + fmt::{self, Debug, Display, Formatter}, + ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, +}; +use num_traits::{ + bounds::{LowerBounded, UpperBounded}, + CheckedAdd, One, Zero, +}; + +use crate::{ + numerated::{BoundValue, Numerated}, + Bound, +}; + +/// Describes not empty interval start..=end. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct NonEmptyInterval { + start: T, + end: T, +} + +/// Describes interval start..=end, where end can be None, +/// which means that interval is empty. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Interval { + start: T, + end: Option, +} + +impl From> for (T, T) { + fn from(interval: NonEmptyInterval) -> (T, T) { + (interval.start, interval.end) + } +} + +impl From> for RangeInclusive { + fn from(interval: NonEmptyInterval) -> Self { + interval.start..=interval.end + } +} + +impl NonEmptyInterval { + /// Creates new interval start..=end with checks only in debug mode. + /// # Safety + /// Unsafe, because allows to create invalid interval. + /// Safe, when start ≤ end. + #[track_caller] + pub unsafe fn new_unchecked(start: T, end: T) -> Self { + debug_assert!(start <= end); + Self { start, end } + } + + /// Creates new interval start..=end if start ≤ end, else returns None. + pub fn new(start: T, end: T) -> Option { + (start <= end).then_some(Self { start, end }) + } + + /// Interval start (the smallest value inside interval) + pub fn start(&self) -> T { + self.start + } + + /// Interval end (the biggest value inside interval) + pub fn end(&self) -> T { + self.end + } + + /// Converts to [Interval], which implements iterator. + pub fn iter(&self) -> Interval { + (*self).into() + } + + /// Into (start, end) + pub fn into_inner(self) -> (T, T) { + self.into() + } +} + +/// Error which occurs when trying to convert empty [Interval] into [NonEmptyInterval]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IntoNonEmptyIntervalError; + +impl TryFrom<&Interval> for NonEmptyInterval { + type Error = IntoNonEmptyIntervalError; + + fn try_from(interval: &Interval) -> Result { + interval + .end + .map(|end| unsafe { + // Guaranteed by `Self` that start ≤ end + NonEmptyInterval::new_unchecked(interval.start, end) + }) + .ok_or(IntoNonEmptyIntervalError) + } +} + +impl TryFrom> for NonEmptyInterval { + type Error = IntoNonEmptyIntervalError; + + fn try_from(interval: Interval) -> Result { + TryFrom::try_from(&interval) + } +} + +impl TryFrom> for RangeInclusive { + type Error = IntoNonEmptyIntervalError; + + fn try_from(interval: Interval) -> Result { + NonEmptyInterval::try_from(interval).map(Into::into) + } +} + +impl Interval { + /// Creates new interval start..end if start ≤ end, else returns None. + /// If start == end, then returns empty interval. + pub fn new, E: Into>(start: S, end: E) -> Option { + Self::try_from((start, end)).ok() + } + /// Returns interval start. + /// - if interval is empty, then returns any existed `T` point, + /// which user set when creates this interval. + /// - if interval is not empty, then returns the smallest point inside interval. + pub fn start(&self) -> T { + self.start + } + /// Returns whether interval is empty. + pub fn is_empty(&self) -> bool { + self.end.is_none() + } + /// Tries to convert into (start, end). + pub fn into_inner(self) -> Option<(T, T)> { + NonEmptyInterval::try_from(self).ok().map(Into::into) + } + /// Tries to convert into range inclusive. + pub fn into_range_inclusive(self) -> Option> { + RangeInclusive::try_from(self).ok() + } +} + +impl From> for Interval { + fn from(interval: NonEmptyInterval) -> Self { + Self { + start: interval.start, + end: Some(interval.end), + } + } +} + +impl Iterator for Interval { + type Item = T; + + fn next(&mut self) -> Option { + if let Some((start, end)) = self.into_inner() { + if start == end { + self.end = None; + Some(start) + } else { + // Guaranteed by `Self` + debug_assert!(start < end); + + let result = start; + let start = start.inc_if_lt(end).unwrap_or_else(|| { + unreachable!("`T: Numerated` impl error: for each s: T, e: T, e > s ⇔ s.inc_if_lt(e) == Some(_)") + }); + self.start = start; + Some(result) + } + } else { + None + } + } +} + +impl From for Interval { + fn from(point: T) -> Self { + unsafe { + // Safe cause `point` == `point` + NonEmptyInterval::new_unchecked(point, point).into() + } + } +} + +impl From<&T> for Interval { + fn from(point: &T) -> Self { + Self::from(*point) + } +} + +impl From> for Interval { + fn from(range: RangeToInclusive) -> Self { + NonEmptyInterval::new(T::min_value(), range.end) + .unwrap_or_else(|| { + unreachable!( + "`T: LowerBounded` impl error: for any x: T must be T::min_value() ≤ x" + ) + }) + .into() + } +} + +impl> From> for Interval { + fn from(range: RangeFrom) -> Self { + let start: T::B = range.start.into(); + match start.unbound() { + BoundValue::Value(start) => NonEmptyInterval::new(start, T::max_value()) + .unwrap_or_else(|| { + unreachable!( + "`T: UpperBounded` impl error: for any x: T must be x ≤ T::max_value()" + ) + }) + .into(), + BoundValue::Upper(start) => Self { start, end: None }, + } + } +} + +impl From for Interval { + fn from(_: RangeFull) -> Self { + NonEmptyInterval::new(T::min_value(), T::max_value()).unwrap_or_else(|| { + unreachable!("`T: UpperBounded + LowerBounded` impl error: must be T::min_value() ≤ T::max_value()") + }).into() + } +} + +impl> From> for Interval { + fn from(range: RangeTo) -> Self { + let end: T::B = range.end.into(); + let start = T::min_value(); + match end.unbound() { + BoundValue::Value(end) => { + debug_assert!(end >= T::min_value()); + let end = end.dec_if_gt(T::min_value()); + Self { start, end } + } + BoundValue::Upper(end) => Self { + start, + end: Some(end), + }, + } + } +} + +/// Error, which occurs, when trying to convert `start` > `end` into `Interval`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IntoIntervalError; + +impl, E: Into> TryFrom<(S, E)> for Interval { + type Error = IntoIntervalError; + + fn try_from((start, end): (S, E)) -> Result { + use BoundValue::*; + + let start: T::B = start.into(); + let end: T::B = end.into(); + + match (start.unbound(), end.unbound()) { + (Upper(start), Upper(_)) => Ok(Self { start, end: None }), + (Upper(_), Value(_)) => Err(IntoIntervalError), + (Value(start), Upper(end)) => Ok(Self { + start, + end: Some(end), + }), + (Value(start), Value(end)) => { + if let Some(end) = end.dec_if_gt(start) { + debug_assert!(start <= end); + Ok(Self { + start, + end: Some(end), + }) + } else if start == end { + Ok(Self { start, end: None }) + } else { + Err(IntoIntervalError) + } + } + } + } +} + +impl> TryFrom> for Interval { + type Error = IntoIntervalError; + + fn try_from(range: Range) -> Result { + Self::try_from((range.start, range.end)) + } +} + +impl TryFrom> for Interval { + type Error = IntoIntervalError; + + fn try_from(range: RangeInclusive) -> Result { + let (start, end) = range.into_inner(); + NonEmptyInterval::new(start, end) + .ok_or(IntoIntervalError) + .map(Into::into) + } +} + +impl NonEmptyInterval { + /// Returns amount of elements in interval in `T::N` if it's possible. + /// None means that interval size is bigger, than `T::N::max_value()`. + pub fn raw_size(&self) -> Option { + let (start, end) = self.into_inner(); + + // guarantied by `Self` + debug_assert!(start <= end); + + let distance = end.distance(start).unwrap_or_else(|| { + unreachable!( + "`T: Numerated` impl error: for any s: T, e: T, e ≥ s ⇔ e.distance(s) == Some(_)" + ) + }); + + distance.checked_add(&T::N::one()) + } +} + +impl NonEmptyInterval { + /// Returns size of interval in `T` if it's possible. + /// - If interval size is bigger than `T` possible elements amount, then returns `None`. + /// - If interval size is equal to some `T::N`, then returns `T` of corresponding numeration: + /// ````ignore + /// { T::N::zero() -> T::min_value(), T::N::one() -> T::min_value() + 1, ... } + /// ```` + pub fn size(&self) -> Option { + let raw_size = self.raw_size()?; + // It's ok if `add_if_enclosed_by` returns `None`, + // because `raw_size` can be bigger than `T` possible elements amount, + // in that case `size` must return `None`. + T::min_value().add_if_enclosed_by(raw_size, T::max_value()) + } +} + +impl Interval { + /// Returns amount of elements in interval in `T::N` if it's possible. + /// None means that interval size is bigger, than `T::N::max_value()`. + /// If interval is empty, then returns `Some(T::min_value())`, + /// which is actually equal to `Some(T::zero())`. + pub fn raw_size(&self) -> Option { + let Ok(interval) = NonEmptyInterval::try_from(self) else { + return Some(T::N::min_value()); + }; + + interval.raw_size() + } +} + +impl Interval { + /// Returns size of interval in `T` if it's possible. + /// - if interval is empty, then returns `Some(T::min_value())`. + /// - if interval size is bigger than `T` possible elements amount, then returns `None`. + /// - if interval size is equal to some `T::N`, then returns `T` of corresponding numeration. + pub fn size(&self) -> Option { + let Ok(interval) = NonEmptyInterval::try_from(self) else { + return Some(T::min_value()); + }; + + interval.size() + } +} + +impl Interval { + /// Returns interval [`start`..`start` + `count`) if it's possible. + /// Size of result interval is equal to `count`. + /// - if `count` is None, then it is supposed, that interval size must be `T::N::max_value()`. + /// - if `start` + `count` - 1 is out of `T`, then returns `None`. + /// - if `count` is zero, then returns empty interval. + pub fn count_from, C: Into>>(start: S, count: C) -> Option { + use BoundValue::*; + let start: T::B = start.into(); + let count: Option = count.into(); + match (start.unbound(), count) { + (Value(start), Some(c)) | (Upper(start), Some(c)) if c == T::N::zero() => { + Some(Self { start, end: None }) + } + (Upper(_), _) => None, + (Value(s), c) => { + // subtraction is safe, because c != 0 + let c = c.map(|c| c - T::N::one()).unwrap_or(T::N::max_value()); + s.add_if_enclosed_by(c, T::max_value()) + .map(|e| NonEmptyInterval::new(s, e).unwrap_or_else(|| { + unreachable!("`T: Numerated` impl error: for each s: T, c: T::N ⇔ s.add_if_between(c, _) ≥ s") + }).into()) + } + } + } +} + +impl Debug for NonEmptyInterval { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "({:?}..={:?})", self.start, self.end) + } +} + +impl Display for NonEmptyInterval { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "({}..={})", self.start, self.end) + } +} + +impl Debug for Interval { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "({:?}..={:?})", self.start, self.end) + } +} + +impl Display for Interval { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(end) = self.end.as_ref() { + write!(f, "({}..={})", self.start, end) + } else { + write!(f, "∅({})", self.start) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn size() { + assert_eq!(Interval::::try_from(11..111).unwrap().size(), Some(100),); + assert_eq!(Interval::::try_from(..1).unwrap().size(), Some(1),); + assert_eq!(Interval::::from(..=1).size(), Some(2)); + assert_eq!(Interval::::from(1..).size(), Some(255)); + assert_eq!(Interval::::from(0..).size(), None); + assert_eq!(Interval::::from(..).size(), None); + assert_eq!(Interval::::try_from(1..1).unwrap().size(), Some(0)); + + assert_eq!( + Interval::::try_from(11..111).unwrap().raw_size(), + Some(100), + ); + assert_eq!(Interval::::try_from(..1).unwrap().raw_size(), Some(1),); + assert_eq!(Interval::::from(..=1).raw_size(), Some(2)); + assert_eq!(Interval::::from(1..).raw_size(), Some(255)); + assert_eq!(Interval::::from(0..).raw_size(), None); + assert_eq!(Interval::::from(..).raw_size(), None); + assert_eq!(Interval::::try_from(1..1).unwrap().raw_size(), Some(0)); + + assert_eq!(Interval::::try_from(-1..99).unwrap().size(), Some(-28)); // corresponds to 100 numeration + assert_eq!(Interval::::try_from(..1).unwrap().size(), Some(1)); // corresponds to 129 numeration + assert_eq!(Interval::::from(..=1).size(), Some(2)); // corresponds to 130 numeration + assert_eq!(Interval::::from(1..).size(), Some(-1)); // corresponds to 127 numeration + assert_eq!(Interval::::from(0..).size(), Some(0)); // corresponds to 128 numeration + assert_eq!(Interval::::from(..).size(), None); // corresponds to 256 numeration + assert_eq!(Interval::::try_from(1..1).unwrap().size(), Some(-128)); // corresponds to 0 numeration + + assert_eq!( + Interval::::try_from(-1..99).unwrap().raw_size(), + Some(100) + ); + assert_eq!(Interval::::try_from(..1).unwrap().raw_size(), Some(129)); + assert_eq!(Interval::::from(..=1).raw_size(), Some(130)); + assert_eq!(Interval::::from(1..).raw_size(), Some(127)); + assert_eq!(Interval::::from(0..).raw_size(), Some(128)); + assert_eq!(Interval::::from(..).raw_size(), None); + assert_eq!(Interval::::try_from(1..1).unwrap().raw_size(), Some(0)); + } + + #[test] + fn count_from() { + assert_eq!( + Interval::::count_from(0, 100).and_then(Interval::into_range_inclusive), + Some(0..=99) + ); + assert_eq!( + Interval::::count_from(0, 255).and_then(Interval::into_range_inclusive), + Some(0..=254) + ); + assert_eq!( + Interval::::count_from(0, None).and_then(Interval::into_range_inclusive), + Some(0..=255) + ); + assert_eq!( + Interval::::count_from(1, 255).and_then(Interval::into_range_inclusive), + Some(1..=255) + ); + + assert!(Interval::::count_from(1, 0).unwrap().is_empty()); + assert_eq!(Interval::::count_from(1, None), None); + assert_eq!(Interval::::count_from(2, 255), None); + } +} diff --git a/common/numerated/src/lib.rs b/common/numerated/src/lib.rs new file mode 100644 index 00000000000..8b1e5633498 --- /dev/null +++ b/common/numerated/src/lib.rs @@ -0,0 +1,46 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Crate for working with [Numerated] types and their sets: [Interval], [NonEmptyInterval] and [IntervalsTree]. + +#![no_std] +#![deny(missing_docs)] + +extern crate alloc; + +mod interval; +mod numerated; +mod tree; + +pub use crate::{ + interval::{Interval, IntoIntervalError, NonEmptyInterval}, + numerated::{Bound, BoundValue, Numerated}, + tree::{IntervalsTree, VoidsIterator}, +}; + +pub use num_traits::{ + self, + bounds::{LowerBounded, UpperBounded}, + CheckedAdd, One, Zero, +}; + +#[cfg(any(feature = "mock", test))] +pub mod mock; + +#[cfg(test)] +mod tests; diff --git a/common/numerated/src/mock.rs b/common/numerated/src/mock.rs new file mode 100644 index 00000000000..f134d73826f --- /dev/null +++ b/common/numerated/src/mock.rs @@ -0,0 +1,147 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Mock for crate property testing and also can be used in other crates for their numerated types impls. + +use crate::{Bound, BoundValue, Interval, IntervalsTree, NonEmptyInterval, Numerated, One, Zero}; +use alloc::{collections::BTreeSet, fmt::Debug, vec::Vec}; + +/// Mock function for any [Numerated] implementation testing. +pub fn test_numerated(x: T, y: T) +where + T: Numerated + Debug, + T::N: Debug, +{ + assert_eq!(x.add_if_enclosed_by(T::N::one(), y), x.inc_if_lt(y)); + assert_eq!(x.sub_if_enclosed_by(T::N::one(), y), x.dec_if_gt(y)); + assert_eq!(y.add_if_enclosed_by(T::N::one(), x), y.inc_if_lt(x)); + assert_eq!(y.sub_if_enclosed_by(T::N::one(), x), y.dec_if_gt(x)); + + assert_eq!(x.add_if_enclosed_by(T::N::zero(), y), Some(x)); + assert_eq!(x.sub_if_enclosed_by(T::N::zero(), y), Some(x)); + assert_eq!(y.add_if_enclosed_by(T::N::zero(), x), Some(y)); + assert_eq!(y.sub_if_enclosed_by(T::N::zero(), x), Some(y)); + + let (x, y) = (x.min(y), x.max(y)); + if x == y { + assert_eq!(x.inc_if_lt(y), None); + assert_eq!(x.dec_if_gt(y), None); + assert_eq!(x.distance(y), Some(T::N::zero())); + } else { + assert!(x.inc_if_lt(y).is_some()); + assert!(x.dec_if_gt(y).is_none()); + assert!(y.inc_if_lt(y).is_none()); + assert!(y.dec_if_gt(x).is_some()); + assert!(x.distance(y).is_none()); + let d = y.distance(x).unwrap(); + assert_eq!(x.add_if_enclosed_by(d, y), Some(y)); + assert_eq!(y.sub_if_enclosed_by(d, x), Some(x)); + } +} + +/// [Interval] testing action. +#[derive(Debug)] +pub enum IntervalAction { + /// Try to create interval from correct start..end. + Correct(T::B, T::B), + /// Try to create interval from incorrect start..end. + Incorrect(T::B, T::B), +} + +/// Mock function for [Interval] testing for any [Numerated] implementation. +pub fn test_interval(action: IntervalAction) +where + T: Numerated + Debug, + T::B: Debug, +{ + log::debug!("{:?}", action); + match action { + IntervalAction::Incorrect(start, end) => { + assert!(Interval::::new(start, end).is_none()); + assert!(Interval::::try_from(start..end).is_err()); + assert!(Interval::::try_from((start, end)).is_err()); + } + IntervalAction::Correct(start, end) => { + let i = Interval::::new(start, end).unwrap(); + if start.get() == end.get() { + assert!(i.is_empty()); + assert_eq!(i.into_range_inclusive(), None); + assert_eq!(i.into_inner(), None); + assert_eq!(NonEmptyInterval::try_from(i).ok(), None); + } else { + assert_eq!(i.start(), start.get().unwrap()); + assert!(!i.is_empty()); + let i = NonEmptyInterval::try_from(i).unwrap(); + match end.unbound() { + BoundValue::Value(e) => assert_eq!(i.end(), e.dec_if_gt(i.start()).unwrap()), + BoundValue::Upper(e) => assert_eq!(i.end(), e), + } + } + } + } +} + +/// [IntervalsTree] testing action. +#[derive(Debug)] +pub enum TreeAction { + /// Inserts interval into tree action. + Insert(Interval), + /// Removes interval from tree action. + Remove(Interval), + /// Check voids iterator. + Voids(Interval), + /// Check and not iterator. + AndNotIterator(BTreeSet), +} + +fn btree_set_voids(set: &BTreeSet, interval: Interval) -> BTreeSet { + interval.filter(|p| !set.contains(p)).collect() +} + +/// Mock function for [IntervalsTree] testing for any [Numerated] implementation. +pub fn test_tree(initial: BTreeSet, actions: Vec>) { + let mut tree: IntervalsTree = initial.iter().collect(); + let mut expected: BTreeSet = tree.points_iter().collect(); + assert_eq!(expected, initial); + + for action in actions { + log::debug!("{:?}", action); + match action { + TreeAction::Insert(interval) => { + tree.remove(interval); + interval.for_each(|i| { + expected.remove(&i); + }); + } + TreeAction::Remove(interval) => { + tree.insert(interval); + expected.extend(interval); + } + TreeAction::Voids(interval) => { + let voids: BTreeSet = tree.voids(interval).flat_map(|i| i.iter()).collect(); + assert_eq!(voids, btree_set_voids(&expected, interval)); + } + TreeAction::AndNotIterator(x) => { + let y = x.iter().collect(); + let z: BTreeSet = tree.and_not_iter(&y).flat_map(|i| i.iter()).collect(); + assert_eq!(z, expected.difference(&x).copied().collect()); + } + } + assert_eq!(expected, tree.points_iter().collect()); + } +} diff --git a/common/numerated/src/numerated.rs b/common/numerated/src/numerated.rs new file mode 100644 index 00000000000..df139ee6016 --- /dev/null +++ b/common/numerated/src/numerated.rs @@ -0,0 +1,197 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! [Numerated], [Bound] traits definition and implementation for integer types. + +use num_traits::{bounds::UpperBounded, One, PrimInt, Unsigned}; + +/// Represents a value or upper bound. Can be in two states: +/// - Value: contains value. +/// - Upper: contains max value for `T`. +/// +/// See also trait [Bound]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BoundValue { + /// The bound is a value. Contains `T` value. + Value(T), + /// The bound is an upper bound. Contains `T` max value. + Upper(T), +} + +/// For any type `T`, `Bound` is a type, which has set of values bigger than `T` by one element. +/// - Each value from `T` has unambiguous mapping to `Bound`. +/// - Each value from `Bound`, except one called __upper__, has unambiguous mapping to `T`. +/// - __upper__ value has no mapping to `T`, but can be used to get `T` max value. +/// +/// # Examples +/// 1) For any `T`, which max value can be get by calling some static live time function, +/// `Option`` can be used as `Bound`. `None` is __upper__. Mapping: Some(t) -> t, t -> Some(t). +/// +/// 2) When `inner` field max value is always smaller than `inner` type max value, then we can use this variant: +/// ``` +/// use numerated::{Bound, BoundValue}; +/// +/// /// `inner` is a value from 0 to 99. +/// struct Number { inner: u32 } +/// +/// /// `inner` is a value from 0 to 100. +/// #[derive(Clone, Copy)] +/// struct BoundForNumber { inner: u32 } +/// +/// impl From for BoundForNumber { +/// fn from(t: Number) -> Self { +/// Self { inner: t.inner } +/// } +/// } +/// +/// impl Bound for BoundForNumber { +/// fn unbound(self) -> BoundValue { +/// if self.inner == 100 { +/// BoundValue::Upper(Number { inner: 99 }) +/// } else { +/// BoundValue::Value(Number { inner: self.inner }) +/// } +/// } +/// } +/// ``` +pub trait Bound: From + Copy { + /// Unbound means mapping bound back to value if possible. + /// - In case bound is __upper__, then returns Upper(max), where `max` is `T` max value. + /// - Otherwise returns Value(value). + fn unbound(self) -> BoundValue; + /// Returns `T` if `self` is value, otherwise (self is __upper__) returns `None`. + fn get(self) -> Option { + match self.unbound() { + BoundValue::Value(v) => Some(v), + BoundValue::Upper(_) => None, + } + } +} + +/// Numerated type is a type, which has type for distances between any two values of `Self`, +/// and provide an interface to add/subtract distance to/from value. +/// +/// Default implementation is provided for all integer types: +/// [i8] [u8] [i16] [u16] [i32] [u32] [i64] [u64] [i128] [u128] [isize] [usize]. +pub trait Numerated: Copy + Sized + Ord + Eq { + /// Numerate type: type that describes the distances between two values of `Self`. + type N: PrimInt + Unsigned; + /// Bound type: type for which any value can be mapped to `Self`, + /// and also has __upper__ value, which is bigger than any value of `Self`. + type B: Bound; + /// Adds `num` to `self`, if `self + num` is enclosed by `self` and `other`. + /// + /// # Guaranties + /// - iff `self + num` is enclosed by `self` and `other`, then returns `Some(_)`. + /// - iff `self.add_if_enclosed_by(num, other) == Some(a)`, + /// then `a.sub_if_enclosed_by(num, self) == Some(self)`. + fn add_if_enclosed_by(self, num: Self::N, other: Self) -> Option; + /// Subtracts `num` from `self`, if `self - num` is enclosed by `self` and `other`. + /// + /// # Guaranties + /// - iff `self - num` is enclosed by `self` and `other`, then returns `Some(_)`. + /// - iff `self.sub_if_enclosed_by(num, other) == Some(a)`, + /// then `a.add_if_enclosed_by(num, self) == Some(self)`. + fn sub_if_enclosed_by(self, num: Self::N, other: Self) -> Option; + /// Returns `self - other`, if `self ≥ other`. + /// + /// # Guaranties + /// - iff `self ≥ other`, then returns `Some(_)`. + /// - iff `self == other`, then returns `Some(0)`. + /// - iff `self.distance(other) == Some(a)`, then + /// - `self.sub_if_enclosed_by(a, other) == Some(other)` + /// - `other.add_if_enclosed_by(a, self) == Some(self)` + fn distance(self, other: Self) -> Option; + /// Increments `self`, if `self < other`. + fn inc_if_lt(self, other: Self) -> Option { + self.add_if_enclosed_by(Self::N::one(), other) + } + /// Decrements `self`, if `self` > `other`. + fn dec_if_gt(self, other: Self) -> Option { + self.sub_if_enclosed_by(Self::N::one(), other) + } + /// Returns `true`, if `self` is enclosed by `a` and `b`. + fn enclosed_by(self, a: &Self, b: &Self) -> bool { + self <= *a.max(b) && self >= *a.min(b) + } +} + +impl From for BoundValue { + fn from(value: T) -> Self { + Self::Value(value) + } +} + +impl From> for BoundValue { + fn from(value: Option) -> Self { + match value { + Some(value) => Self::Value(value), + None => Self::Upper(T::max_value()), + } + } +} + +impl Bound for BoundValue { + fn unbound(self) -> BoundValue { + self + } +} + +macro_rules! impl_for_unsigned { + ($($t:ty)*) => ($( + impl Numerated for $t { + type N = $t; + type B = BoundValue<$t>; + fn add_if_enclosed_by(self, num: Self::N, other: Self) -> Option { + self.checked_add(num).and_then(|res| res.enclosed_by(&self, &other).then_some(res)) + } + fn sub_if_enclosed_by(self, num: Self::N, other: Self) -> Option { + self.checked_sub(num).and_then(|res| res.enclosed_by(&self, &other).then_some(res)) + } + fn distance(self, other: Self) -> Option<$t> { + self.checked_sub(other) + } + } + )*) +} + +impl_for_unsigned!(u8 u16 u32 u64 u128 usize); + +macro_rules! impl_for_signed { + ($($s:ty => $u:ty),*) => { + $( + impl Numerated for $s { + type N = $u; + type B = BoundValue<$s>; + fn add_if_enclosed_by(self, num: $u, other: Self) -> Option { + let res = (self as $u).wrapping_add(num) as $s; + res.enclosed_by(&self, &other).then_some(res) + } + fn sub_if_enclosed_by(self, num: Self::N, other: Self) -> Option { + let res = (self as $u).wrapping_sub(num) as $s; + res.enclosed_by(&self, &other).then_some(res) + } + fn distance(self, other: Self) -> Option<$u> { + (self >= other).then_some(self.abs_diff(other)) + } + } + )* + }; +} + +impl_for_signed!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128, isize => usize); diff --git a/common/numerated/src/tests.rs b/common/numerated/src/tests.rs new file mode 100644 index 00000000000..123701885ec --- /dev/null +++ b/common/numerated/src/tests.rs @@ -0,0 +1,90 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Property testing for Numerated, Interval and IntervalsTree. + +use crate::{ + mock::{self, test_interval, test_numerated, IntervalAction, TreeAction}, + BoundValue, Interval, +}; +use alloc::{collections::BTreeSet, vec::Vec}; +use proptest::{ + arbitrary::any, prop_oneof, proptest, strategy::Strategy, test_runner::Config as ProptestConfig, +}; + +fn rand_interval() -> impl Strategy> { + any::() + .prop_flat_map(|start| (start..).prop_map(move |end| (start..=end).try_into().unwrap())) +} + +fn rand_set() -> impl Strategy> { + proptest::collection::btree_set(any::(), 0..1000) +} + +fn tree_actions() -> impl Strategy>> { + let action = prop_oneof![ + rand_interval().prop_map(TreeAction::Insert), + rand_interval().prop_map(TreeAction::Remove), + rand_interval().prop_map(TreeAction::Voids), + rand_set().prop_map(TreeAction::AndNotIterator), + ]; + proptest::collection::vec(action, 10..20) +} + +fn interval_action() -> impl Strategy> { + let start = any::>(); + let end = any::>(); + (start, end).prop_map(|(start, end)| { + let start: BoundValue = start.into(); + let end: BoundValue = end.into(); + match (start, end) { + (_, BoundValue::Upper(_)) => IntervalAction::Correct(start, end), + (BoundValue::Value(s), BoundValue::Value(e)) => { + if s > e { + IntervalAction::Incorrect(start, end) + } else { + IntervalAction::Correct(start, end) + } + } + (BoundValue::Upper(_), BoundValue::Value(_)) => IntervalAction::Incorrect(start, end), + } + }) +} + +proptest! { + #![proptest_config(ProptestConfig::with_cases(10_000))] + + #[test] + fn proptest_numerated(x in any::(), y in any::()) { + test_numerated(x, y); + } + + #[test] + fn proptest_interval(action in interval_action()) { + test_interval(action); + } +} + +proptest! { + #![proptest_config(ProptestConfig::with_cases(128))] + + #[test] + fn proptest_tree(actions in tree_actions(), initial in rand_set()) { + mock::test_tree(initial, actions); + } +} diff --git a/common/numerated/src/tree.rs b/common/numerated/src/tree.rs new file mode 100644 index 00000000000..b5557141fef --- /dev/null +++ b/common/numerated/src/tree.rs @@ -0,0 +1,866 @@ +// This file is part of Gear. + +// Copyright (C) 2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! [IntervalsTree] implementation. + +use crate::{Interval, NonEmptyInterval, Numerated}; +use alloc::{collections::BTreeMap, fmt, fmt::Debug, vec::Vec}; +use core::{fmt::Formatter, ops::RangeInclusive}; +use num_traits::{CheckedAdd, Zero}; +use scale_info::{ + scale::{Decode, Encode}, + TypeInfo, +}; + +/// # Non overlapping intervals tree +/// Can be considered as set of points, but with possibility to work with +/// continuous sets of this points (the same as interval) as fast as with points. +/// Insert and remove operations has complexity between `[O(log(n)), O(n)]`, +/// where `n` is amount of intervals in tree. +/// So, even if you insert for example points from [`0u64`] to [`u64::MAX`], +/// then removing all of them or any part of them is as fast as removing one point. +/// +/// # Examples +/// ``` +/// use numerated::IntervalsTree; +/// use std::collections::BTreeSet; +/// use std::ops::RangeInclusive; +/// +/// let mut tree = IntervalsTree::new(); +/// let mut set = BTreeSet::new(); +/// +/// tree.insert(1i32); +/// // now `tree` contains only one interval: [1..=1] +/// set.insert(1i32); +/// // `points_iter()` - is iterator over all points in `tree`. +/// assert_eq!(set, tree.points_iter().collect()); +/// +/// // We can add points from 3 to 100_000 only by one insert operation. +/// // `try` is only for range check, that it has start ≤ end. +/// tree.try_insert(3..=100_000).unwrap(); +/// // now `tree` contains two intervals: [1..=1] and [3..=100_000] +/// set.extend(3..=100_000); +/// // extend complexity is O(n), where n is amount of elements in range. +/// assert_eq!(set, tree.points_iter().collect()); +/// +/// // We can remove points from 1 to 99_000 not inclusive only by one remove operation. +/// // `try` is only for range check, that it has start ≤ end. +/// tree.try_remove(1..99_000).unwrap(); +/// // now `tree` contains two intervals: [99_000..=100_000] +/// (1..99_000).for_each(|i| { set.remove(&i); }); +/// // remove complexity for set is O(n*log(m)), +/// // where `n` is amount of elements in range and `m` size of `tree`. +/// assert_eq!(set, tree.points_iter().collect()); +/// +/// // Can insert or remove all possible points just by one operation: +/// tree.insert(..); +/// tree.remove(..); +/// +/// // Iterate over voids (intervals between intervals in tree): +/// tree.try_insert(1..=3).unwrap(); +/// tree.try_insert(5..=7).unwrap(); +/// let voids = tree.voids(..).map(RangeInclusive::from).collect::>(); +/// assert_eq!(voids, vec![i32::MIN..=0, 4..=4, 8..=i32::MAX]); +/// +/// // AndNot iterator: iterate over intervals from `tree` which are not in `other_tree`. +/// let other_tree: IntervalsTree = [3, 4, 5, 7, 8, 9].iter().collect(); +/// let and_not: Vec<_> = tree.and_not_iter(&other_tree).map(RangeInclusive::from).collect(); +/// assert_eq!(and_not, vec![1..=2, 6..=6]); +/// ``` +/// +/// # Possible panic cases +/// Using `IntervalsTree` for type `T: Numerated` cannot cause panics, +/// if implementation [Numerated], [Copy], [Ord], [Eq] are correct for `T`. +/// In other cases `IntervalsTree` does not guarantees execution without panics. +#[derive(Clone, PartialEq, Eq, TypeInfo, Encode, Decode)] +pub struct IntervalsTree { + inner: BTreeMap, +} + +impl IntervalsTree { + /// Creates new empty intervals tree. + pub const fn new() -> Self { + Self { + inner: BTreeMap::new(), + } + } + /// Returns amount of not empty intervals in tree. + /// + /// Complexity: O(1). + pub fn intervals_amount(&self) -> usize { + self.inner.len() + } +} + +impl IntervalsTree { + /// Returns the biggest point in tree. + pub fn end(&self) -> Option { + self.inner.last_key_value().map(|(_, &e)| e) + } + /// Returns the smallest point in tree. + pub fn start(&self) -> Option { + self.inner.first_key_value().map(|(&s, _)| s) + } +} + +impl Default for IntervalsTree { + fn default() -> Self { + Self::new() + } +} + +impl Debug for IntervalsTree { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "{:?}", + self.inner.iter().map(|(s, e)| s..=e).collect::>() + ) + } +} + +impl IntervalsTree { + fn into_start_end>>(interval: I) -> Option<(T, T)> { + Into::>::into(interval).into_inner() + } + + /// Returns iterator over all intervals in tree. + pub fn iter(&self) -> impl Iterator> + '_ { + self.inner.iter().map(|(&start, &end)| unsafe { + // Safe, because `Self` guaranties, that inner contains only `start` ≤ `end`. + NonEmptyInterval::::new_unchecked(start, end) + }) + } + + /// Returns true if for each `p` ∈ `interval` ⇒ `p` ∈ `self`, otherwise returns false. + pub fn contains>>(&self, interval: I) -> bool { + let Some((start, end)) = Self::into_start_end(interval) else { + // Empty interval is always contained. + return true; + }; + if let Some((&s, &e)) = self.inner.range(..=end).next_back() { + if s <= start { + return e >= end; + } + } + false + } + + /// The same as [`Self::contains`], but returns [`I::Error`] if `try_into` [Interval] fails. + pub fn try_contains>>(&self, interval: I) -> Result { + let interval: Interval = interval.try_into()?; + Ok(self.contains(interval)) + } + + /// Insert interval into tree. + /// - if `interval` is empty, then nothing will be inserted. + /// - if `interval` is not empty, then after inserting: for each `p` ∈ `interval` ⇒ `p` ∈ `self`. + /// + /// Complexity: `O(log(n) + m)`, where + /// - `n` is amount of intervals in `self` + /// - `m` is amount of intervals in `self` ⋂ `interval` + pub fn insert>>(&mut self, interval: I) { + let Some((start, end)) = Self::into_start_end(interval) else { + // Empty interval - nothing to insert. + return; + }; + + let Some(last) = self.end() else { + // No other intervals, so can just insert as is. + self.inner.insert(start, end); + return; + }; + + let mut iter = if let Some(point_after_end) = end.inc_if_lt(last) { + // If `end` < `last`, then we must take in account next point after `end`, + // because there can be neighbor interval which must be merged with `interval`. + self.inner.range(..=point_after_end) + } else { + self.inner.range(..=end) + } + .map(|(&s, &e)| (s, e)); + + let Some((right_start, right_end)) = iter.next_back() else { + // No neighbor or intersected intervals, so can just insert as is. + self.inner.insert(start, end); + return; + }; + + if let Some(right_end) = right_end.inc_if_lt(start) { + if right_end == start { + self.inner.insert(right_start, end); + } else { + self.inner.insert(start, end); + } + return; + } else if right_start <= start { + if right_end < end { + self.inner.insert(right_start, end); + } else { + // nothing to do: our interval is completely inside "right interval". + } + return; + } + + let mut left_interval = None; + let mut intervals_to_remove = Vec::new(); + while let Some((s, e)) = iter.next_back() { + if s <= start { + left_interval = Some((s, e)); + break; + } + intervals_to_remove.push(s); + } + for start in intervals_to_remove { + self.inner.remove(&start); + } + + // In this point `start` < `right_start` ≤ `end`, so in any cases it will be removed. + self.inner.remove(&right_start); + + let end = right_end.max(end); + + let Some((left_start, left_end)) = left_interval else { + self.inner.insert(start, end); + return; + }; + + debug_assert!(left_end < right_start); + debug_assert!(left_start <= start); + let Some(left_end) = left_end.inc_if_lt(right_start) else { + unreachable!( + "`T: Numerated` impl error: for each x: T, y: T, x < y ↔ x.inc_if_lt(y) == Some(_)" + ); + }; + + if left_end >= start { + self.inner.insert(left_start, end); + } else { + self.inner.insert(start, end); + } + } + + /// The same as [`Self::insert`], but returns [`I::Error`] if `try_into` [Interval] fails. + pub fn try_insert>>(&mut self, interval: I) -> Result<(), I::Error> { + let interval: Interval = interval.try_into()?; + self.insert(interval); + Ok(()) + } + + /// Remove `interval` from tree. + /// - if `interval` is empty, then nothing will be removed. + /// - if `interval` is not empty, then after removing for each `p` ∈ `interval` ⇒ `p` ∉ `self`. + /// + /// Complexity: `O(log(n) + m)`, where + /// - `n` is amount of intervals in `self` + /// - `m` is amount of intervals in `self` ⋂ `interval` + pub fn remove>>(&mut self, interval: I) { + let Some((start, end)) = Self::into_start_end(interval) else { + // Empty interval - nothing to remove. + return; + }; + + let mut iter = self.inner.range(..=end); + + let Some((&right_start, &right_end)) = iter.next_back() else { + return; + }; + + if right_end < start { + return; + } + + let mut left_interval = None; + let mut intervals_to_remove = Vec::new(); + while let Some((&s, &e)) = iter.next_back() { + if s < start { + if e >= start { + left_interval = Some(s); + } + break; + } + + intervals_to_remove.push(s) + } + + for start in intervals_to_remove { + self.inner.remove(&start); + } + + if let Some(start) = start.dec_if_gt(right_start) { + debug_assert!(start >= right_start); + self.inner.insert(right_start, start); + } else { + debug_assert!(right_start <= end); + self.inner.remove(&right_start); + } + + if let Some(end) = end.inc_if_lt(right_end) { + debug_assert!(end <= right_end); + self.inner.insert(end, right_end); + } else { + debug_assert!(start <= right_end); + } + + if let Some(left_start) = left_interval { + // `left_start` < `start` cause of method it was found. + debug_assert!(left_start < start); + if let Some(start) = start.dec_if_gt(left_start) { + self.inner.insert(left_start, start); + } else { + unreachable!("`T: Numerated` impl error: for each x: T, y: T, x > y ⇔ x.dec_if_gt(y) == Some(_)"); + } + } + } + + /// The same as [`Self::remove`], but returns [`I::Error`] if `try_into` [Interval] fails. + pub fn try_remove>>(&mut self, interval: I) -> Result<(), I::Error> { + let interval: Interval = interval.try_into()?; + self.remove(interval); + Ok(()) + } + + /// Returns iterator over non empty intervals, that consist of points `p: T` + /// where each `p` ∉ `self` and `p` ∈ `interval`. + /// Intervals in iterator are sorted in ascending order. + /// + /// Iterating complexity: `O(log(n) + m)`, where + /// - `n` is amount of intervals in `self` + /// - `m` is amount of intervals in `self` ⋂ `interval` + pub fn voids>>( + &self, + interval: I, + ) -> VoidsIterator> + '_> { + let Some((mut start, end)) = Self::into_start_end(interval) else { + // Empty interval. + return VoidsIterator { inner: None }; + }; + + if let Some((_, &e)) = self.inner.range(..=start).next_back() { + if let Some(e) = e.inc_if_lt(end) { + if e > start { + start = e; + } + } else { + // `interval` is inside of one of `self` interval - no voids. + return VoidsIterator { inner: None }; + } + } + + let iter = self.inner.range(start..=end).map(|(&start, &end)| { + // Safe, because `Self` guaranties, that inner contains only `start` ≤ `end`. + unsafe { NonEmptyInterval::new_unchecked(start, end) } + }); + + // Safe, because we have already checked, that `start` ≤ `end`. + let interval = unsafe { NonEmptyInterval::new_unchecked(start, end) }; + + VoidsIterator { + inner: Some((iter, interval)), + } + } + + /// The same as [`Self::voids`], but returns [`I::Error`] if `try_into` [Interval] fails. + pub fn try_voids>>( + &self, + interval: I, + ) -> Result> + '_>, I::Error> { + let interval: Interval = interval.try_into()?; + Ok(self.voids(interval)) + } + + /// Returns iterator over intervals which consist of points `p: T`, + /// where each `p` ∈ `self` and `p` ∉ `other`. + /// + /// Iterating complexity: `O(n + m)`, where + /// - `n` is amount of intervals in `self` + /// - `m` is amount of intervals in `other` + pub fn and_not_iter<'a: 'b, 'b: 'a>( + &'a self, + other: &'b Self, + ) -> impl Iterator> + '_ { + AndNotIterator { + iter1: self.iter(), + iter2: other.iter(), + interval1: None, + interval2: None, + } + } + + /// Number of points in tree set. + /// + /// Complexity: `O(n)`, where `n` is amount of intervals in `self`. + pub fn points_amount(&self) -> Option { + let mut res = T::N::zero(); + for interval in self.iter() { + res = res.checked_add(&interval.raw_size()?)?; + } + Some(res) + } + + /// Iterator over all points in tree set. + pub fn points_iter(&self) -> impl Iterator + '_ { + self.inner.iter().flat_map(|(&s, &e)| unsafe { + // Safe, because `Self` guaranties, that it contains only `start` ≤ `end` + NonEmptyInterval::new_unchecked(s, e).iter() + }) + } + + /// Convert tree to vector of inclusive ranges. + pub fn to_vec(&self) -> Vec> { + self.iter().map(Into::into).collect() + } +} + +/// Helper struct to iterate over voids in tree. +/// +/// See also [`IntervalsTree::voids`]. +pub struct VoidsIterator>> { + inner: Option<(I, NonEmptyInterval)>, +} + +impl>> Iterator for VoidsIterator { + type Item = NonEmptyInterval; + + fn next(&mut self) -> Option { + let (iter, interval) = self.inner.as_mut()?; + if let Some(next) = iter.next() { + let (start, end) = next.into_inner(); + + // Guaranties by tree: between two intervals always exists void. + debug_assert!(interval.start() < start); + + let void_end = start.dec_if_gt(interval.start()).unwrap_or_else(|| { + unreachable!("`T: Numerated` impl error: for each x: T, y: T, x > y ↔ x.dec_if_gt(y) == Some(_)"); + }); + + let res = NonEmptyInterval::new(interval.start(), void_end); + if res.is_none() { + unreachable!( + "`T: Numerated` impl error: for each x: T, y: T, x > y ↔ x.dec_if_gt(y) ≥ y" + ); + } + + if let Some(new_start) = end.inc_if_lt(interval.end()) { + *interval = NonEmptyInterval::new(new_start, interval.end()).unwrap_or_else(|| { + unreachable!("`T: Numerated` impl error: for each x: T, y: T, x < y ↔ x.inc_if_lt(y) ≤ y"); + }); + } else { + self.inner = None; + } + + res + } else { + let res = Some(*interval); + self.inner = None; + res + } + } +} + +/// Helper struct to iterate over intervals from `tree`, which are not in `other_tree`. +/// +/// See also [`IntervalsTree::and_not_iter`]. +pub struct AndNotIterator< + T: Numerated, + I1: Iterator>, + I2: Iterator>, +> { + iter1: I1, + iter2: I2, + interval1: Option>, + interval2: Option>, +} + +impl< + T: Numerated, + I1: Iterator>, + I2: Iterator>, + > Iterator for AndNotIterator +{ + type Item = NonEmptyInterval; + + fn next(&mut self) -> Option { + loop { + let interval1 = if let Some(interval1) = self.interval1 { + interval1 + } else { + let interval1 = self.iter1.next()?; + self.interval1 = Some(interval1); + interval1 + }; + + let interval2 = if let Some(interval2) = self.interval2 { + interval2 + } else if let Some(interval2) = self.iter2.next() { + interval2 + } else { + self.interval1 = None; + return Some(interval1); + }; + + if interval2.end() < interval1.start() { + self.interval2 = None; + continue; + } + + self.interval2 = Some(interval2); + + if interval1.end() < interval2.start() { + self.interval1 = None; + return Some(interval1); + } else { + if let Some(new_start) = interval2.end().inc_if_lt(interval1.end()) { + self.interval1 = NonEmptyInterval::new(new_start, interval1.end()); + if self.interval1.is_none() { + unreachable!("`T: Numerated` impl error: for each x: T, y: T, x < y ⇔ x.inc_if_lt(y) ≤ y"); + } + } else if interval1.end() == interval2.end() { + self.interval1 = None; + self.interval2 = None; + } else { + self.interval1 = None; + } + + if let Some(new_end) = interval2.start().dec_if_gt(interval1.start()) { + let res = NonEmptyInterval::new(interval1.start(), new_end); + if res.is_none() { + unreachable!("`T: Numerated` impl error: for each x: T, y: T, x > y ⇔ x.dec_if_gt(y) ≥ y"); + } + return res; + } else { + continue; + } + } + } + } +} + +impl>> FromIterator for IntervalsTree { + fn from_iter>(iter: I) -> Self { + let mut tree = Self::new(); + for interval in iter { + tree.insert(interval); + } + tree + } +} + +#[allow(clippy::reversed_empty_ranges)] +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec; + + #[test] + fn insert() { + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=2]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=2, 4..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(3..=4).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=4]); + + let mut tree = IntervalsTree::new(); + tree.insert(1); + tree.insert(2); + assert_eq!(tree.to_vec(), vec![1..=2]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=3).unwrap(); + tree.try_insert(5..=7).unwrap(); + tree.try_insert(2..=6).unwrap(); + tree.try_insert(7..=7).unwrap(); + tree.try_insert(19..=25).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=7, 19..=25]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=3).unwrap(); + tree.try_insert(10..=14).unwrap(); + tree.try_insert(4..=9).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=14]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-111..=3).unwrap(); + tree.try_insert(10..=14).unwrap(); + tree.try_insert(3..=10).unwrap(); + assert_eq!(tree.to_vec(), vec![-111..=14]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(i32::MIN..=10).unwrap(); + tree.try_insert(3..=4).unwrap(); + assert_eq!(tree.to_vec(), vec![i32::MIN..=10]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=10).unwrap(); + tree.try_insert(3..=4).unwrap(); + tree.try_insert(5..=6).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=10]); + } + + #[test] + fn remove() { + let mut tree = IntervalsTree::new(); + tree.insert(1); + tree.remove(1); + assert_eq!(tree.to_vec(), vec![]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + tree.try_remove(1..=2).unwrap(); + assert_eq!(tree.to_vec(), vec![]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(-1..=2).unwrap(); + assert_eq!(tree.to_vec(), vec![4..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(4..=5).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=2]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(2..=4).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=1, 5..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(3..=4).unwrap(); + assert_eq!(tree.to_vec(), vec![-1..=2, 5..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(-1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(-1..=5).unwrap(); + assert_eq!(tree.to_vec(), vec![]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(2..=5).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=1]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(1..=4).unwrap(); + assert_eq!(tree.to_vec(), vec![5..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=2).unwrap(); + tree.try_insert(4..=5).unwrap(); + tree.try_remove(1..=3).unwrap(); + assert_eq!(tree.to_vec(), vec![4..=5]); + + let mut tree = IntervalsTree::new(); + tree.try_insert(1..=10).unwrap(); + assert_eq!(tree.clone().to_vec(), vec![1..=10]); + tree.remove(10..); + assert_eq!(tree.clone().to_vec(), vec![1..=9]); + tree.remove(2..); + assert_eq!(tree.clone().to_vec(), vec![1..=1]); + tree.try_insert(3..6).unwrap(); + assert_eq!(tree.clone().to_vec(), vec![1..=1, 3..=5]); + tree.remove(..=3); + assert_eq!(tree.clone().to_vec(), vec![4..=5]); + tree.try_insert(1..=2).unwrap(); + assert_eq!(tree.clone().to_vec(), vec![1..=2, 4..=5]); + tree.remove(..); + assert_eq!(tree.clone().to_vec(), vec![]); + tree.insert(..); + assert_eq!(tree.clone().to_vec(), vec![i32::MIN..=i32::MAX]); + tree.remove(..=9); + assert_eq!(tree.clone().to_vec(), vec![10..=i32::MAX]); + tree.remove(21..); + assert_eq!(tree.clone().to_vec(), vec![10..=20]); + } + + #[test] + fn try_voids() { + let mut tree = IntervalsTree::new(); + tree.try_insert(1u32..=7).unwrap(); + tree.try_insert(19..=25).unwrap(); + assert_eq!(tree.clone().to_vec(), vec![1..=7, 19..=25]); + assert_eq!( + tree.try_voids(0..100) + .unwrap() + .map(RangeInclusive::from) + .collect::>(), + vec![0..=0, 8..=18, 26..=99] + ); + assert_eq!( + tree.try_voids((0, None)) + .unwrap() + .map(RangeInclusive::from) + .collect::>(), + vec![0..=0, 8..=18, 26..=u32::MAX] + ); + assert_eq!( + tree.try_voids((None, None)) + .unwrap() + .map(RangeInclusive::from) + .collect::>(), + vec![] + ); + assert_eq!( + tree.try_voids(1..1) + .unwrap() + .map(RangeInclusive::from) + .collect::>(), + vec![] + ); + assert_eq!( + tree.try_voids(0..=0) + .unwrap() + .map(RangeInclusive::from) + .collect::>(), + vec![0..=0] + ); + + assert!(tree.try_voids(1..0).is_err()); + } + + #[test] + fn try_insert() { + let mut tree = IntervalsTree::new(); + tree.try_insert(1u32..=2).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=2]); + tree.try_insert(4..=5).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=2, 4..=5]); + tree.try_insert(4..4).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=2, 4..=5]); + assert!(tree.try_insert(4..3).is_err()); + tree.try_insert(None..None).unwrap(); + assert_eq!(tree.to_vec(), vec![1..=2, 4..=5]); + tree.try_insert((0, None)).unwrap(); + assert_eq!(tree.to_vec(), vec![0..=u32::MAX]); + } + + #[test] + fn try_remove() { + let mut tree = [1u32, 2, 5, 6, 7, 9, 10, 11] + .into_iter() + .collect::>(); + assert_eq!(tree.to_vec(), vec![1..=2, 5..=7, 9..=11]); + assert!(tree.try_remove(0..0).is_ok()); + assert_eq!(tree.to_vec(), vec![1..=2, 5..=7, 9..=11]); + assert!(tree.try_remove(1..1).is_ok()); + assert_eq!(tree.to_vec(), vec![1..=2, 5..=7, 9..=11]); + assert!(tree.try_remove(1..2).is_ok()); + assert_eq!(tree.to_vec(), vec![2..=2, 5..=7, 9..=11]); + assert!(tree.try_remove(..7).is_ok()); + assert_eq!(tree.to_vec(), vec![7..=7, 9..=11]); + assert!(tree.try_remove(None..None).is_ok()); + assert_eq!(tree.to_vec(), vec![7..=7, 9..=11]); + assert!(tree.try_remove(1..0).is_err()); + assert_eq!(tree.to_vec(), vec![7..=7, 9..=11]); + assert!(tree.try_remove((1, None)).is_ok()); + assert_eq!(tree.to_vec(), vec![]); + } + + #[test] + fn contains() { + let tree: IntervalsTree = [0, 100, 101, 102, 45678, 45679, 1, 2, 3] + .into_iter() + .collect(); + assert_eq!(tree.to_vec(), vec![0..=3, 100..=102, 45678..=45679]); + assert!(tree.contains(0)); + assert!(tree.contains(1)); + assert!(tree.contains(2)); + assert!(tree.contains(3)); + assert!(!tree.contains(4)); + assert!(!tree.contains(99)); + assert!(tree.contains(100)); + assert!(tree.contains(101)); + assert!(tree.contains(102)); + assert!(!tree.contains(103)); + assert!(!tree.contains(45677)); + assert!(tree.contains(45678)); + assert!(tree.contains(45679)); + assert!(!tree.contains(45680)); + assert!(!tree.contains(141241)); + assert!(tree.try_contains(0..=3).unwrap()); + assert!(tree.try_contains(0..4).unwrap()); + assert!(!tree.try_contains(0..5).unwrap()); + assert!(tree.try_contains(1..1).unwrap()); + assert!(!tree.contains(..)); + assert!(tree.contains(..1)); + } + + #[test] + fn amount() { + let tree: IntervalsTree = [-100, -99, 100, 101, 102, 1000].into_iter().collect(); + assert_eq!(tree.intervals_amount(), 3); + assert_eq!(tree.points_amount(), Some(6)); + + let tree: IntervalsTree = [..].into_iter().collect(); + assert_eq!(tree.intervals_amount(), 1); + assert_eq!(tree.points_amount(), None); + + let tree: IntervalsTree = Default::default(); + assert_eq!(tree.intervals_amount(), 0); + assert_eq!(tree.points_amount(), Some(0)); + } + + #[test] + fn start_end() { + let tree: IntervalsTree = [0u64, 100, 101, 102, 45678, 45679, 1, 2, 3] + .into_iter() + .collect(); + assert_eq!(tree.to_vec(), vec![0..=3, 100..=102, 45678..=45679]); + assert_eq!(tree.start(), Some(0)); + assert_eq!(tree.end(), Some(45679)); + } + + #[test] + fn and_not_iter() { + let tree: IntervalsTree = [0, 1, 2, 3, 4, 8, 9, 100, 101, 102].into_iter().collect(); + let tree1: IntervalsTree = [3, 4, 7, 8, 9, 10, 45, 46, 100, 102].into_iter().collect(); + let v: Vec> = tree.and_not_iter(&tree1).map(Into::into).collect(); + assert_eq!(v, vec![0..=2, 101..=101]); + + let tree1: IntervalsTree = [..].into_iter().collect(); + let v: Vec> = tree.and_not_iter(&tree1).map(Into::into).collect(); + assert_eq!(v, vec![]); + + let tree1: IntervalsTree = [..=100].into_iter().collect(); + let v: Vec> = tree.and_not_iter(&tree1).map(Into::into).collect(); + assert_eq!(v, vec![101..=102]); + + let tree1: IntervalsTree = [101..].into_iter().collect(); + let v: Vec> = tree.and_not_iter(&tree1).map(Into::into).collect(); + assert_eq!(v, vec![0..=4, 8..=9, 100..=100]); + + let tree1: IntervalsTree = [6, 10, 110].into_iter().collect(); + let v: Vec> = tree.and_not_iter(&tree1).map(Into::into).collect(); + assert_eq!(v, vec![0..=4, 8..=9, 100..=102]); + } +}