extern crate alloc;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
use crate::kairos::Kairos;
use super::super::DotSet;
use super::super::metathesis::{Metatheses, Metathesis};
use super::ancestry::{
AncestryQueryError, AncestryRelation, Coordinate, CoordinateError, QueryError,
};
use super::identity::IdentityPlane;
use super::placement::Dot;
use super::possession::OccupancyPlane;
use super::{Anchor, Extent, Locus, OrderWalk, PlacementEffect, Rhapsody, Verge};
mod batch;
mod collation;
mod cycle;
#[cfg(feature = "timing")]
mod profile;
use crate::metis::dot::RawDot;
pub use batch::{MovementBatch, MovementBatchError};
use cycle::{WalkBudget, move_forms_cycle};
#[cfg(feature = "timing")]
pub use profile::{
MovementBatchMoveProfile, MovementCycleValidationProfile, MovementCycleValidationWork,
MovementTopologyPhases, MovementTopologyProfile, MovementTopologyWork, ProfiledMovement,
};
type PlayKey = (Kairos, Dot);
fn woven_target(target: RawDot) -> Dot {
Dot::try_from(target).expect("a participating target is woven, so its counter is nonzero")
}
fn as_identity(raw: RawDot) -> Option<Dot> {
Dot::try_from(raw).ok()
}
#[derive(Clone, Copy, Debug)]
enum Verdict {
Applied { displaced: Locus },
Refused,
}
#[derive(Clone, Debug)]
struct Play {
key: PlayKey,
target: RawDot,
locus: Locus,
verdict: Verdict,
}
#[derive(Clone, Debug)]
pub struct Recension {
performed: Rhapsody,
ancestry: Result<Coordinate, CoordinateError>,
refused: Vec<Dot>,
pending: Vec<Dot>,
plays: Vec<Play>,
anchored: BTreeSet<RawDot>,
dangling: BTreeMap<RawDot, Option<PlayKey>>,
folded: BTreeMap<Dot, Metathesis>,
woven_mark: DotSet,
visible_mark: OccupancyPlane,
#[cfg(test)]
replays: usize,
#[cfg(test)]
rebuilds: usize,
#[cfg(test)]
compacted: usize,
}
impl PartialEq for Recension {
fn eq(&self, other: &Self) -> bool {
self.performed == other.performed
&& self.refused == other.refused
&& self.pending == other.pending
}
}
impl Eq for Recension {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum AncestryAdmissionError {
#[error("exact ancestry requires a fully placed effective reading")]
UnplacedReading,
#[error("the exact ancestry coordinate is unavailable")]
Coordinate(#[source] CoordinateError),
}
pub struct ExactAncestry<'a> {
coordinate: &'a mut Coordinate,
}
#[cfg(feature = "instrumentation")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct RecensionProfile {
pub sequence: super::RhapsodyProfile,
pub ancestry: Result<super::ancestry::AncestryProfile, CoordinateError>,
pub refused: usize,
pub refused_capacity: usize,
pub pending: usize,
pub pending_capacity: usize,
pub plays: usize,
pub play_capacity: usize,
pub anchored: usize,
pub dangling: usize,
pub folded: usize,
pub woven_mark_stations: usize,
pub woven_mark_exceptions: usize,
pub visible_mark_stations: usize,
pub visible_mark_pages: usize,
}
impl ExactAncestry<'_> {
pub fn relation(
&mut self,
descendant: Dot,
ancestor: Dot,
) -> Result<AncestryRelation, AncestryQueryError> {
self.coordinate
.relation(descendant, ancestor)
.map_err(QueryError::into_public)
}
}
fn synchronize_coordinate(
coordinate: &mut Coordinate,
skeleton: &IdentityPlane,
first_unplaced: Option<Dot>,
effect: PlacementEffect,
) -> Result<(), CoordinateError> {
match effect {
PlacementEffect::Unchanged => {}
PlacementEffect::Reparented { dot, parent } => {
coordinate.replace_parent(dot, parent)?;
}
PlacementEffect::Parked(region) => {
for dot in region {
coordinate.park(dot)?;
}
}
PlacementEffect::Repaired(region) => {
for dot in region {
let parent = skeleton
.get(&dot)
.ok_or(CoordinateError::InvariantViolation)?
.anchor
.dot();
coordinate.repair(dot, parent)?;
}
}
}
coordinate.publish_state(first_unplaced);
Ok(())
}
impl Recension {
pub fn with_exact_ancestry<R>(
&mut self,
use_ancestry: impl FnOnce(&mut ExactAncestry<'_>) -> R,
) -> Result<R, AncestryAdmissionError> {
if !self.performed.unplaced.is_empty() {
return Err(AncestryAdmissionError::UnplacedReading);
}
let coordinate = self
.ancestry
.as_mut()
.map_err(|error| AncestryAdmissionError::Coordinate(*error))?;
Ok(use_ancestry(&mut ExactAncestry { coordinate }))
}
#[cfg(feature = "instrumentation")]
#[must_use]
pub fn profile(&self) -> RecensionProfile {
RecensionProfile {
sequence: self.performed.profile(),
ancestry: self
.ancestry
.as_ref()
.map(Coordinate::profile)
.map_err(|error| *error),
refused: self.refused.len(),
refused_capacity: self.refused.capacity(),
pending: self.pending.len(),
pending_capacity: self.pending.capacity(),
plays: self.plays.len(),
play_capacity: self.plays.capacity(),
anchored: self.anchored.len(),
dangling: self.dangling.len(),
folded: self.folded.len(),
woven_mark_stations: self.woven_mark.station_count(),
woven_mark_exceptions: self.woven_mark.exceptions_len(),
visible_mark_stations: self.visible_mark.station_count(),
visible_mark_pages: self.visible_mark.page_count(),
}
}
#[must_use]
pub fn order(&self) -> Vec<Dot> {
self.performed.order()
}
pub(in crate::metis::rhapsody) const fn performed(&self) -> &Rhapsody {
&self.performed
}
#[must_use]
pub fn order_walk(&self) -> OrderWalk<'_> {
self.performed.order_walk()
}
#[must_use]
pub fn order_walk_after(&self, dot: Dot) -> Option<OrderWalk<'_>> {
self.performed.order_walk_after(dot)
}
#[must_use]
pub fn order_len(&self) -> usize {
self.performed.order_len()
}
#[must_use]
pub fn order_at(&self, offset: usize) -> Option<Dot> {
self.performed.order_at(offset)
}
#[must_use]
pub fn offset_of(&self, dot: Dot) -> Option<usize> {
self.performed.offset_of(dot)
}
#[must_use]
pub fn order_walk_rev(&self) -> super::OrderWalkRev<'_> {
self.performed.order_walk_rev()
}
#[must_use]
pub fn order_walk_rev_before(&self, dot: Dot) -> Option<super::OrderWalkRev<'_>> {
self.performed.order_walk_rev_before(dot)
}
#[must_use]
pub fn extent(&self, start: Verge, end: Verge) -> Extent {
self.performed.extent(start, end)
}
#[must_use]
pub fn locus(&self, dot: Dot) -> Option<Locus> {
self.performed.locus(dot)
}
#[must_use]
pub fn is_visible(&self, dot: Dot) -> bool {
self.performed.is_visible(dot)
}
#[must_use]
pub fn is_reachable(&self, dot: Dot) -> bool {
self.performed.is_reachable(dot)
}
pub fn children_of(&self, anchor: Anchor) -> impl Iterator<Item = Dot> + '_ {
self.performed.children_of(anchor)
}
#[must_use]
pub fn anchor_for_visual_insert(&self, after: Option<RawDot>) -> Anchor {
self.performed.anchor_for_visual_insert(after)
}
#[must_use]
pub fn visible_len(&self) -> usize {
self.performed.visible_len()
}
#[must_use]
pub fn refused(&self) -> &[Dot] {
&self.refused
}
#[must_use]
pub fn pending(&self) -> &[Dot] {
&self.pending
}
}
impl Rhapsody {
#[must_use]
pub fn recension(&self, moves: &Metatheses) -> Recension {
Recension::replay_full(self, moves)
}
}
impl Recension {
fn replay_full(text: &Rhapsody, moves: &Metatheses) -> Self {
let mut effective: BTreeMap<Dot, Locus> = BTreeMap::new();
let mut anchored: BTreeSet<RawDot> = BTreeSet::new();
let mut dangling: BTreeMap<RawDot, Option<PlayKey>> = BTreeMap::new();
for (dot, locus) in text.skeleton.iter() {
if let Some(anchor_dot) = locus.anchor.dot() {
let _ = anchored.insert(anchor_dot);
if !as_identity(anchor_dot).is_some_and(|dot| text.skeleton.contains(dot)) {
let _ = dangling.insert(anchor_dot, None);
}
}
let _ = effective.insert(dot, locus);
}
let mut plays: Vec<Play> = Vec::with_capacity(moves.len());
let mut pending: Vec<Dot> = Vec::new();
let mut folded: BTreeMap<Dot, Metathesis> = BTreeMap::new();
for (dot, metathesis) in moves {
let _ = folded.insert(dot, *metathesis);
if as_identity(metathesis.target).is_some_and(|target| text.skeleton.contains(target)) {
plays.push(Play {
key: (metathesis.to.rank, dot),
target: metathesis.target,
locus: metathesis.to,
verdict: Verdict::Refused,
});
} else {
pending.push(dot);
}
}
plays.sort_unstable_by_key(|play| play.key);
let mut ancestry = if text.unplaced.is_empty() {
Coordinate::build(&text.skeleton, &text.unplaced).ok()
} else {
None
};
let mut refused: Vec<Dot> = Vec::new();
for play in &mut plays {
let cycles = move_forms_cycle(
ancestry.as_mut(),
play.target,
play.locus.anchor,
&anchored,
WalkBudget::for_skeleton(effective.len()),
|link| {
as_identity(link)
.and_then(|link| effective.get(&link))
.map(|locus| locus.anchor.dot())
},
)
.must_refuse();
if cycles {
play.verdict = Verdict::Refused;
refused.push(play.key.1);
} else {
let displaced = *effective
.get(&woven_target(play.target))
.expect("a participating target is born");
play.verdict = Verdict::Applied { displaced };
if let Some(anchor_dot) = play.locus.anchor.dot() {
let _ = anchored.insert(anchor_dot);
if !as_identity(anchor_dot).is_some_and(|dot| text.skeleton.contains(dot)) {
let earliest = dangling.entry(anchor_dot).or_insert(Some(play.key));
*earliest = (*earliest).min(Some(play.key));
}
}
if let Some(coordinate) = &mut ancestry
&& coordinate
.replace_parent(woven_target(play.target), play.locus.anchor.dot())
.is_err()
{
ancestry = None;
}
let _ = effective.insert(woven_target(play.target), play.locus);
}
}
let performed = Rhapsody::from_parts(effective, text.visible.clone());
let ancestry = ancestry.map_or_else(
|| Coordinate::build(&performed.skeleton, &performed.unplaced),
Ok,
);
Self {
performed,
ancestry,
refused,
pending,
plays,
anchored,
dangling,
folded,
woven_mark: text.woven.clone(),
visible_mark: text.visible_pages.clone(),
#[cfg(test)]
replays: 0,
#[cfg(test)]
rebuilds: 0,
#[cfg(test)]
compacted: 0,
}
}
}