extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use super::identity::IdentityPlane;
use super::placement::{Anchor, Dot};
use crate::metis::dot::RawDot;
pub(super) fn sibling_cmp(plane: &IdentityPlane, a: Dot, b: Dot) -> core::cmp::Ordering {
let (ra, rb) = (
plane.get(&a).expect("left sibling has a locus").rank,
plane.get(&b).expect("right sibling has a locus").rank,
);
rb.cmp(&ra).then_with(|| a.cmp(&b))
}
fn chain_child(plane: &IdentityPlane, anchor: Anchor) -> Option<Dot> {
let Anchor::After(RawDot {
station,
counter: index,
}) = anchor
else {
return None;
};
let successor = Dot::from_parts(station, index.checked_add(1)?).ok()?;
(plane.anchor_of(&successor) == Some(anchor)).then_some(successor)
}
fn is_chain_child(dot: Dot, anchor: Anchor) -> bool {
matches!(
anchor,
Anchor::After(RawDot { station, counter: index })
if station == dot.station() && index.checked_add(1) == Some(dot.counter())
)
}
#[derive(Clone, Copy, Debug)]
pub(super) enum Bucket<'a> {
Explicit(&'a [Dot]),
Implicit(Dot),
}
impl<'a> Bucket<'a> {
pub(super) const fn first(&self) -> Dot {
match self {
Self::Explicit(bucket) => bucket[0],
Self::Implicit(dot) => *dot,
}
}
pub(super) const fn last(&self) -> Dot {
match self {
Self::Explicit(bucket) => bucket[bucket.len() - 1],
Self::Implicit(dot) => *dot,
}
}
pub(super) const fn len(&self) -> usize {
match self {
Self::Explicit(bucket) => bucket.len(),
Self::Implicit(_) => 1,
}
}
pub(super) fn get(&self, at: usize) -> Option<Dot> {
match self {
Self::Explicit(bucket) => bucket.get(at).copied(),
Self::Implicit(dot) => (at == 0).then_some(*dot),
}
}
pub(super) fn iter(&self) -> BucketIter<'a> {
match self {
Self::Explicit(bucket) => BucketIter::Slice(bucket.iter()),
Self::Implicit(dot) => BucketIter::One(Some(*dot)),
}
}
pub(super) fn suffix(&self, at: usize) -> BucketIter<'a> {
match self {
Self::Explicit(bucket) => BucketIter::Slice(bucket[at + 1..].iter()),
Self::Implicit(_) => BucketIter::One(None),
}
}
pub(super) fn prefix(&self, at: usize) -> BucketIter<'a> {
match self {
Self::Explicit(bucket) => BucketIter::Slice(bucket[..at].iter()),
Self::Implicit(_) => BucketIter::One(None),
}
}
}
#[derive(Clone, Debug)]
pub(super) enum BucketIter<'a> {
Slice(core::slice::Iter<'a, Dot>),
One(Option<Dot>),
}
impl BucketIter<'_> {
pub(super) const fn empty() -> Self {
Self::One(None)
}
}
impl Iterator for BucketIter<'_> {
type Item = Dot;
fn next(&mut self) -> Option<Dot> {
match self {
Self::Slice(iter) => iter.next().copied(),
Self::One(dot) => dot.take(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = match self {
Self::Slice(iter) => iter.len(),
Self::One(dot) => usize::from(dot.is_some()),
};
(len, Some(len))
}
}
impl DoubleEndedIterator for BucketIter<'_> {
fn next_back(&mut self) -> Option<Dot> {
match self {
Self::Slice(iter) => iter.next_back().copied(),
Self::One(dot) => dot.take(),
}
}
}
impl ExactSizeIterator for BucketIter<'_> {}
impl core::iter::FusedIterator for BucketIter<'_> {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum TailChange {
Unchanged,
Replaced(Option<Dot>),
}
#[derive(Clone, Debug, Default)]
pub(super) struct ChildPlane {
explicit: BTreeMap<Anchor, Vec<Dot>>,
}
impl ChildPlane {
pub(super) const fn new() -> Self {
Self {
explicit: BTreeMap::new(),
}
}
pub(super) fn bucket<'a>(
&'a self,
plane: &IdentityPlane,
anchor: Anchor,
) -> Option<Bucket<'a>> {
if let Some(bucket) = self.explicit.get(&anchor) {
debug_assert!(
!bucket.is_empty(),
"an explicit bucket is never stored empty"
);
return Some(Bucket::Explicit(bucket));
}
chain_child(plane, anchor).map(Bucket::Implicit)
}
pub(super) fn iter<'a>(&'a self, plane: &IdentityPlane, anchor: Anchor) -> BucketIter<'a> {
self.bucket(plane, anchor)
.map_or(BucketIter::empty(), |bucket| bucket.iter())
}
pub(super) fn insert(&mut self, plane: &IdentityPlane, anchor: Anchor, dot: Dot) {
if let Some(bucket) = self.explicit.get_mut(&anchor) {
let pos = bucket.partition_point(|&other| sibling_cmp(plane, other, dot).is_lt());
bucket.insert(pos, dot);
return;
}
match chain_child(plane, anchor) {
Some(candidate) if candidate == dot => {}
Some(candidate) => {
let mut bucket = alloc::vec![candidate];
let pos = bucket.partition_point(|&other| sibling_cmp(plane, other, dot).is_lt());
bucket.insert(pos, dot);
let _ = self.explicit.insert(anchor, bucket);
}
None => {
let _ = self.explicit.insert(anchor, alloc::vec![dot]);
}
}
}
pub(super) fn remove(&mut self, plane: &IdentityPlane, anchor: Anchor, dot: Dot) -> TailChange {
let Some(bucket) = self.explicit.get_mut(&anchor) else {
debug_assert_eq!(
chain_child(plane, anchor),
None,
"an implicit bucket holds exactly the removed chain child"
);
return TailChange::Replaced(None);
};
let was_last = bucket.last() == Some(&dot);
if let Some(pos) = bucket.iter().position(|&child| child == dot) {
let _ = bucket.remove(pos);
}
let new_last = bucket.last().copied();
if bucket.len() <= 1 && new_last == chain_child(plane, anchor) {
let _ = self.explicit.remove(&anchor);
}
if was_last {
TailChange::Replaced(new_last)
} else {
TailChange::Unchanged
}
}
pub(super) fn build(plane: &IdentityPlane) -> Self {
let mut explicit: BTreeMap<Anchor, Vec<Dot>> = BTreeMap::new();
for (dot, locus) in plane.iter() {
if !is_chain_child(dot, locus.anchor) {
explicit.entry(locus.anchor).or_default().push(dot);
}
}
for (&anchor, bucket) in &mut explicit {
if let Some(candidate) = chain_child(plane, anchor) {
bucket.push(candidate);
}
bucket.sort_unstable_by(|a, b| sibling_cmp(plane, *a, *b));
}
Self { explicit }
}
pub(super) fn last_children<'a>(
&'a self,
plane: &'a IdentityPlane,
) -> impl Iterator<Item = (Anchor, Dot)> + 'a {
let explicit = self.explicit.iter().map(|(&anchor, bucket)| {
let last = *bucket
.last()
.expect("an explicit bucket is never stored empty");
(anchor, last)
});
let implicit = plane.iter().filter_map(move |(dot, locus)| {
(is_chain_child(dot, locus.anchor) && !self.explicit.contains_key(&locus.anchor))
.then_some((locus.anchor, dot))
});
explicit.chain(implicit)
}
#[cfg(test)]
pub(super) fn explicit_buckets(&self) -> usize {
self.explicit.len()
}
#[cfg(test)]
pub(super) fn explicit_children(&self) -> usize {
self.explicit.values().map(Vec::len).sum()
}
}
#[cfg(test)]
mod tests;