use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use itertools::Itertools;
use crate::interval::{ii, iu, ui, uu};
use crate::utils::invalid_interval_panic;
use crate::{Interval, IntervalType, NoditMap, PointType};
pub trait IdType: Eq + Ord + Copy {}
impl<D> IdType for D where D: Eq + Ord + Copy {}
#[derive(Clone, Debug)]
pub struct Gqdit<I, K, D> {
inner: NoditMap<I, K, BTreeSet<D>>,
}
impl<I, K, D> Gqdit<I, K, D>
where
I: PointType,
K: IntervalType<I>,
D: IdType,
{
pub fn new() -> Self {
Self::default()
}
pub fn gaps_no_identifier<Q>(&self, interval: &Q) -> Vec<K>
where
Q: IntervalType<I>,
{
invalid_interval_panic(interval);
self.inner
.overlapping(interval)
.filter_map(|(inner_interval, other_specifiers)| {
if other_specifiers.is_empty() {
Some(inner_interval)
} else {
None
}
})
.cloned()
.collect()
}
pub fn gaps_with_identifier<Q>(&self, identifier: D, interval: &Q) -> Vec<K>
where
Q: IntervalType<I>,
D: IdType,
{
invalid_interval_panic(interval);
let valid_gaps = self
.inner
.overlapping(interval)
.filter_map(move |(inner_interval, other_identifiers)| {
if valid_identifier(Some(identifier), other_identifiers) {
Some(inner_interval)
} else {
None
}
})
.cloned();
let non_end_gaps = valid_gaps.filter(|gap| {
!gap.contains_point(interval.start())
&& !gap.contains_point(interval.end())
});
let mut left_gap =
self.expand_gaps_at_point_left(identifier, interval.start());
let mut right_gap =
self.expand_gaps_at_point_right(identifier, interval.end());
if let (Some(left), Some(right)) = (left_gap.as_mut(), right_gap.clone()) {
if overlaps_ordered(left, &right) {
*left = K::from(merge_ordered(left, &right));
right_gap = None;
}
}
let all_non_merged_gaps =
left_gap.into_iter().chain(non_end_gaps).chain(right_gap);
all_non_merged_gaps
.coalesce(|x, y| {
if touches_ordered(&x, &y) {
Ok(K::from(merge_ordered(&x, &y)))
} else {
Err((x, y))
}
})
.collect()
}
pub fn cut_with_identifiers<Q>(
&mut self,
identifiers: BTreeSet<D>,
interval: &Q,
) where
Q: IntervalType<I>,
{
invalid_interval_panic(interval);
if identifiers.is_empty() {
return;
}
for (cut_interval, mut cut_identifiers) in self
.inner
.cut(interval)
.collect::<Vec<_>>()
{
cut_identifiers.retain(|i| !identifiers.contains(i));
self.inner
.insert_merge_touching_if_values_equal(
cut_interval,
cut_identifiers,
)
.unwrap_or_else(|_| panic!());
}
}
pub fn cut_all_identifiers<Q>(&mut self, interval: &Q)
where
Q: IntervalType<I>,
{
invalid_interval_panic(interval);
for (cut_interval, mut cut_identifiers) in self
.inner
.cut(interval)
.collect::<Vec<_>>()
{
cut_identifiers.clear();
self.inner
.insert_merge_touching_if_values_equal(
cut_interval,
cut_identifiers,
)
.unwrap_or_else(|_| panic!());
}
}
pub fn insert(&mut self, identifiers: BTreeSet<D>, interval: &K) {
invalid_interval_panic(interval);
if identifiers.is_empty() {
return;
}
let cut = self
.inner
.cut(interval)
.collect::<Vec<_>>()
.into_iter();
let extended_cut = cut.map(|(cut_interval, mut cut_identifiers)| {
cut_identifiers.extend(identifiers.clone());
(cut_interval, cut_identifiers)
});
for (extended_interval, extended_identifiers) in extended_cut {
self.inner
.insert_merge_touching_if_values_equal(
extended_interval,
extended_identifiers,
)
.unwrap_or_else(|_| panic!());
}
}
pub fn append(&mut self, other: &mut Self) {
for (interval, identifiers) in other.inner.remove_overlapping(&uu()) {
self.insert(identifiers, &interval);
}
}
pub fn identifiers_at_point(&self, point: &I) -> BTreeSet<D> {
self.inner
.get_at_point(point)
.cloned()
.unwrap_or(BTreeSet::new())
}
fn expand_gaps_at_point_right(&self, identifier: D, point: &I) -> Option<K> {
let overlapping_right = self.inner.overlapping(&iu(point.clone()));
overlapping_right
.take_while(|(_, other_identifiers)| {
valid_identifier(Some(identifier), other_identifiers)
})
.map(|(x, _)| (*x).clone())
.coalesce(|x, y| {
Ok(K::from(merge_ordered(&x, &y)))
})
.next()
}
fn expand_gaps_at_point_left(&self, identifier: D, point: &I) -> Option<K> {
let overlapping_left = self.inner.overlapping(&ui(point.clone())).rev();
overlapping_left
.take_while(|(_, other_identifiers)| {
valid_identifier(Some(identifier), other_identifiers)
})
.map(|(x, _)| (*x).clone())
.coalesce(|x, y| {
Ok(K::from(merge_ordered(&y, &x)))
})
.next()
}
}
fn valid_identifier<I>(
with_identifier: Option<I>,
other_identifiers: &BTreeSet<I>,
) -> bool
where
I: Eq + Ord,
{
match with_identifier {
Some(identifier) => {
other_identifiers.is_empty()
|| (other_identifiers.len() == 1
&& other_identifiers.contains(&identifier))
}
None => other_identifiers.is_empty(),
}
}
fn merge_ordered<I, A, B>(a: &A, b: &B) -> Interval<I>
where
I: PointType,
A: IntervalType<I>,
B: IntervalType<I>,
{
ii(a.start().clone(), b.end().clone())
}
fn overlaps_ordered<I, A, B>(a: &A, b: &B) -> bool
where
I: PointType,
A: IntervalType<I>,
B: IntervalType<I>,
{
a.contains_point(b.start()) || a.contains_point(b.end())
}
fn touches_ordered<I, A, B>(a: &A, b: &B) -> bool
where
I: PointType,
A: IntervalType<I>,
B: IntervalType<I>,
{
a.end() == &b.start().down().unwrap()
}
impl<I, K, D> PartialEq for Gqdit<I, K, D>
where
I: PartialEq,
K: PartialEq,
BTreeSet<D>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<I, K, D> Default for Gqdit<I, K, D>
where
I: PointType,
K: IntervalType<I>,
{
fn default() -> Self {
let mut map = NoditMap::new();
map.insert_strict(K::from(uu()), BTreeSet::new())
.unwrap_or_else(|_| panic!());
Self { inner: map }
}
}
#[cfg(feature = "serde")]
mod serde {
use alloc::collections::BTreeSet;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{Gqdit, IdType, IntervalType, NoditMap, PointType};
impl<I, K, D> Serialize for Gqdit<I, K, D>
where
I: PointType,
K: IntervalType<I> + Serialize,
D: IdType,
BTreeSet<D>: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.inner.serialize(serializer)
}
}
impl<'de, I, K, D> Deserialize<'de> for Gqdit<I, K, D>
where
I: PointType,
K: IntervalType<I> + Deserialize<'de>,
D: IdType,
BTreeSet<D>: Deserialize<'de>,
{
fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>
where
De: Deserializer<'de>,
{
NoditMap::deserialize(deserializer).map(|x| Gqdit { inner: x })
}
}
}