use crate::utils::Utils;
use mkutils_macros::Default as MkutilsDefault;
use num::{Zero, traits::SaturatingAdd};
use std::{
collections::{
BTreeMap,
btree_map::{IntoIter as BTreeMapIntoIter, Range as BTreeMapRange, Values as BTreeMapValues},
},
iter::Map,
};
pub trait Interval {
type Point: Ord + PartialEq + SaturatingAdd + Zero;
fn begin(&self) -> Self::Point;
fn len(&self) -> Self::Point;
fn expand_to_cover(&mut self, other: &Self);
fn end(&self) -> Self::Point {
self.begin().saturating_add(&self.len())
}
fn is_empty(&self) -> bool {
self.len().is_zero()
}
fn begins_at(&self, point: &Self::Point) -> bool {
&self.begin() == point
}
fn is_touching_or_contains(&self, point: &Self::Point) -> bool {
(self.begin()..=self.end()).contains(point)
}
}
#[derive(Clone, Debug, MkutilsDefault)]
pub struct IntervalSet<T: Interval> {
intervals: BTreeMap<T::Point, T>,
}
impl<T: Interval> IntervalSet<T> {
const fn from_intervals(intervals: BTreeMap<T::Point, T>) -> Self {
Self { intervals }
}
#[must_use]
pub const fn new() -> Self {
Self::from_intervals(BTreeMap::new())
}
#[must_use]
pub fn len(&self) -> usize {
self.intervals.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.intervals.is_empty()
}
pub fn clear(&mut self) {
self.intervals.clear();
}
#[must_use]
pub fn first(&self) -> Option<&T> {
self.intervals.first_key_value()?.into_second().some()
}
#[must_use]
pub fn last(&self) -> Option<&T> {
self.intervals.last_key_value()?.into_second().some()
}
pub fn pop_first(&mut self) -> Option<T> {
self.intervals.pop_first()?.into_second().some()
}
pub fn pop_last(&mut self) -> Option<T> {
self.intervals.pop_last()?.into_second().some()
}
pub fn iter(&self) -> BTreeMapValues<'_, T::Point, T> {
self.intervals.values()
}
#[allow(clippy::type_complexity)]
fn intervals_with_begin_leq<'a>(
&'a self,
point: &T::Point,
) -> Map<BTreeMapRange<'a, T::Point, T>, fn((&'a T::Point, &'a T)) -> &'a T> {
self.intervals.range(..=point).map(Utils::into_second)
}
pub fn is_touching_or_contains(&self, point: &T::Point) -> bool {
if let Some(furthest_leq_interval) = self.intervals_with_begin_leq(point).next_back() {
furthest_leq_interval.is_touching_or_contains(point)
} else {
false
}
}
fn insert_empty_interval(&mut self, empty_interval: T) {
let begin = empty_interval.begin();
if self.is_touching_or_contains(&begin) {
return;
}
self.intervals.insert(begin, empty_interval);
}
pub fn split(self, point: &T::Point) -> (Self, Self) {
let mut lt_intervals = self.intervals;
let geq_intervals = lt_intervals.split_off(point);
let lt_interval_set = Self::from_intervals(lt_intervals);
let geq_interval_set = Self::from_intervals(geq_intervals);
lt_interval_set.pair(geq_interval_set)
}
fn insert_non_empty_interval(&mut self, mut interval: T) {
let (mut lt_interval_set, mut geq_interval_set) = self.mem_take().split(&interval.begin());
if let Some(furthest_lt_interval) = lt_interval_set.last()
&& furthest_lt_interval.is_touching_or_contains(&interval.begin())
{
interval.expand_to_cover(furthest_lt_interval);
lt_interval_set.pop_last();
}
while let Some(nearest_geq_interval) = geq_interval_set.first() {
if interval.is_touching_or_contains(&nearest_geq_interval.begin()) {
interval.expand_to_cover(nearest_geq_interval);
geq_interval_set.pop_first();
continue;
}
break;
}
lt_interval_set.intervals.insert(interval.begin(), interval);
lt_interval_set.intervals.append(&mut geq_interval_set.intervals);
self.assign(lt_interval_set);
}
pub fn insert(&mut self, interval: T) -> &mut Self {
if interval.is_empty() {
self.insert_empty_interval(interval);
} else {
self.insert_non_empty_interval(interval);
}
self
}
}
impl<T: Interval> IntoIterator for IntervalSet<T> {
type Item = T;
type IntoIter = Map<BTreeMapIntoIter<T::Point, T>, fn((T::Point, T)) -> T>;
fn into_iter(self) -> Self::IntoIter {
self.intervals.into_iter().map(Utils::into_second)
}
}
impl<'a, T: Interval> IntoIterator for &'a IntervalSet<T> {
type Item = &'a T;
type IntoIter = BTreeMapValues<'a, T::Point, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}