mod cases;
mod properties;
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Delta {
Birth { dot: Dot, label: char, locus: Locus },
Delete { target: Dot },
Move(Movement),
}
fn judge(raw: &State, record: &[Movement], window: &[Delta]) -> (State, Vec<Decision>) {
let mut state = raw.clone();
let mut movements = record.to_vec();
for delta in window {
match *delta {
Delta::Birth { dot, label, locus } => state.insert(
dot,
Node {
label,
locus,
visible: true,
},
),
Delta::Delete { target } => {
state
.nodes
.get_mut(&target)
.expect("a delete names a woven dot")
.visible = false;
}
Delta::Move(movement) => movements.push(movement),
}
}
let decisions = replay(&mut state, &movements);
(state, decisions)
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct DotMap {
next_epoch: Epoch,
compacted: BTreeMap<Dot, Dot>,
n_live: u8,
cut_ceiling: u8,
}
impl DotMap {
fn at_declaration(effective: &State) -> Self {
let next_epoch = Epoch(effective.epoch.0.checked_add(1).expect("bounded toy epoch"));
let cut_ceiling = effective
.nodes
.keys()
.map(|dot| dot.index)
.max()
.unwrap_or(0);
let mut compacted = BTreeMap::new();
for dot in effective.walk() {
if effective.nodes[&dot].visible {
let index = u8::try_from(compacted.len() + 1).expect("bounded toy state");
let _ = compacted.insert(
dot,
Dot {
epoch: next_epoch,
index,
},
);
}
}
let n_live = u8::try_from(compacted.len()).expect("bounded toy state");
Self {
next_epoch,
compacted,
n_live,
cut_ceiling,
}
}
fn translate(&self, old: Dot) -> Dot {
self.compacted.get(&old).copied().unwrap_or_else(|| {
assert!(
old.index > self.cut_ceiling,
"a swept tombstone stays in the shadow; only live-at-cut and window-born dots cross"
);
Dot {
epoch: self.next_epoch,
index: self
.n_live
.checked_add(old.index - self.cut_ceiling)
.expect("bounded toy state"),
}
})
}
}
struct Shadow {
raw: State,
record: Vec<Movement>,
map: DotMap,
window: Vec<Delta>,
judged: State,
decisions: Vec<Decision>,
}
impl Shadow {
fn declare(raw: &State, record: &[Movement]) -> Self {
let (judged, decisions) = judge(raw, record, &[]);
let map = DotMap::at_declaration(&judged);
Self {
raw: raw.clone(),
record: record.to_vec(),
map,
window: Vec::new(),
judged,
decisions,
}
}
fn deliver(&mut self, delta: Delta) {
match delta {
Delta::Birth { dot, label, locus } => {
self.window.push(delta);
self.judged.insert(
dot,
Node {
label,
locus,
visible: true,
},
);
}
Delta::Delete { target } => {
self.window.push(delta);
self.judged
.nodes
.get_mut(&target)
.expect("a delete names a woven dot")
.visible = false;
}
Delta::Move(movement) => {
let key = (movement.to.rank, movement.testimony);
let last = self
.record
.iter()
.chain(self.window.iter().filter_map(|delta| match delta {
Delta::Move(movement) => Some(movement),
_ => None,
}))
.map(|movement| (movement.to.rank, movement.testimony))
.max();
self.window.push(delta);
if last.is_none_or(|last| key > last) {
let verdict = self.judged.apply(movement);
self.decisions.push(Decision {
testimony: movement.testimony,
verdict,
});
} else {
let (judged, decisions) = judge(&self.raw, &self.record, &self.window);
self.judged = judged;
self.decisions = decisions;
}
}
}
}
fn projected_reading(&self) -> Vec<char> {
self.judged.reading()
}
fn projected_identities(&self) -> Vec<(Dot, char)> {
eager_projection(&self.map, &self.judged)
}
}
fn eager_projection(map: &DotMap, judged: &State) -> Vec<(Dot, char)> {
judged
.walk()
.into_iter()
.filter(|dot| judged.nodes[dot].visible)
.map(|dot| (map.translate(dot), judged.nodes[&dot].label))
.collect()
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct ShadowBoundaryResult {
fold_then_refound: Vec<char>,
shadow_projection: Vec<char>,
old_decisions: Vec<Decision>,
shadow_decisions: Vec<Decision>,
}
impl ShadowBoundaryResult {
fn commutes(&self) -> bool {
self.fold_then_refound == self.shadow_projection
&& self.old_decisions == self.shadow_decisions
}
}
fn cross_boundary_shadow(
raw_at_cut: &State,
record: &[Movement],
window: &[Delta],
) -> ShadowBoundaryResult {
let (folded, old_decisions) = judge(raw_at_cut, record, window);
let fold_then_refound = folded
.refound(BoundarySupport::PositionOnly)
.state
.reading();
let mut shadow = Shadow::declare(raw_at_cut, record);
for delta in window {
shadow.deliver(*delta);
}
ShadowBoundaryResult {
fold_then_refound,
shadow_projection: shadow.projected_reading(),
old_decisions,
shadow_decisions: shadow.decisions,
}
}