use std::collections::HashMap;
use sva_samples::{Machine, NodeRenderer, Slot};
use super::Table;
use super::program::leaves;
use super::value::{Held, Kind, Value};
pub(crate) fn carried(new: &mut Table, old: Table, now: i64, live: bool) -> Vec<usize> {
let keys: HashMap<_, usize> = old
.values
.iter()
.enumerate()
.map(|(at, v)| (v.key, at))
.collect();
let old_root = old.root;
let shapes: Vec<Shape> = old
.values
.iter()
.map(|v| (v.reads.clone(), reads(v)))
.collect();
let mut old: Vec<Option<Value>> = old.values.into_iter().map(Some).collect();
let count = new.values.len();
let same: Vec<Option<usize>> = new
.values
.iter()
.map(|v| keys.get(&v.key).copied())
.collect();
let kept: Vec<usize> = same.iter().flatten().copied().collect();
let mut paired: Vec<Option<usize>> = vec![None; count];
let mut latest: Vec<Option<i64>> = vec![None; count];
let mut first: Vec<Option<i64>> = vec![None; count];
paired[new.root] = Some(old_root);
(latest[new.root], first[new.root]) = (Some(now), Some(now));
let mut dropped = Vec::new();
for at in (0..count).rev() {
let candidates = match same[at] {
Some(was) => vec![was],
None => predecessors(new, &shapes, &paired, at, &kept),
};
paired[at] = paired[at].or(candidates.first().copied());
let went_on = match same[at] {
Some(was) => {
let old = old[was].take().expect("an old value carries on once");
take(&mut new.values[at], old);
true
}
None if !stateful(&new.values[at]) => false,
None => {
let at_now = latest[at].unwrap_or(now);
let taken = candidates.iter().find_map(|was| {
let held = old[*was].as_ref()?;
continues(&new.values[at], held, at_now).then_some(*was)
});
if let Some(was) = taken {
let old = old[was].take().expect("a predecessor carries on once");
carry(&mut new.values[at], old);
paired[at] = Some(was);
} else if live && start_silent(&mut new.values[at], first[at].unwrap_or(now)) {
dropped.push(at);
}
taken.is_some()
}
};
let here = latest[at];
let from = if went_on { here } else { first[at] };
for (slot, map) in reads(&new.values[at]) {
let read = new.values[at].reads[slot];
latest[read] = latest[read].max(here.map(|n| map.at(n)));
first[read] = earliest(first[read], from.map(|n| map.at(n)));
}
for (slot, reach) in indexed(&new.values[at]) {
let read = new.values[at].reads[slot];
let least = reach.map_or(i64::MIN, |(least, _)| least);
first[read] = earliest(first[read], from.map(|n| n.saturating_add(least)));
}
if let Kind::Stored { .. } = new.values[at].kind {
for read in new.values[at].reads.clone() {
latest[read] = latest[read].max(here);
first[read] = earliest(first[read], from);
}
}
}
for at in 0..count {
let reads_carried = new.values[at].reads.iter().any(|r| !new.values[*r].pure);
new.values[at].pure &= !reads_carried;
}
dropped
}
fn earliest(held: Option<i64>, there: Option<i64>) -> Option<i64> {
match (held, there) {
(Some(a), Some(b)) => Some(a.min(b)),
(a, b) => a.or(b),
}
}
fn reads(value: &Value) -> Vec<(usize, sva_samples::Map)> {
let mut out = Vec::new();
if let Kind::Program(program) = &value.kind {
leaves(&program.renderer, &mut |leaf| {
if let NodeRenderer::Read {
slot: Slot::Read(at),
map,
} = leaf
{
out.push((at.0 as usize, *map));
}
});
}
out
}
fn indexed(value: &Value) -> Vec<(usize, Option<(i64, i64)>)> {
let mut out = Vec::new();
if let Kind::Program(program) = &value.kind {
leaves(&program.renderer, &mut |leaf| {
if let NodeRenderer::Indexed {
slot: Slot::Read(at),
reach,
..
} = leaf
{
out.push((at.0 as usize, *reach));
}
});
}
out
}
type Shape = (Vec<usize>, Vec<(usize, sva_samples::Map)>);
fn predecessors(
new: &Table,
old: &[Shape],
paired: &[Option<usize>],
at: usize,
kept: &[usize],
) -> Vec<usize> {
let mut ranked = Vec::new();
for (reader, was) in paired.iter().enumerate().skip(at + 1) {
let Some((before, theirs)) = was.map(|w| &old[w]) else {
continue;
};
for (slot, map) in reads(&new.values[reader]) {
if new.values[reader].reads[slot] != at {
continue;
}
for (their, their_map) in theirs {
let read = before[*their];
if *their_map == map && !kept.contains(&read) {
ranked.push((*their != slot, read));
}
}
}
}
ranked.sort_by_key(|(elsewhere, _)| *elsewhere);
let mut out = Vec::new();
for (_, read) in ranked {
if !out.contains(&read) {
out.push(read);
}
}
out
}
fn stateful(value: &Value) -> bool {
matches!(&value.kind, Kind::Program(program) if program.stateful())
}
fn continues(value: &Value, old: &Value, now: i64) -> bool {
let (Kind::Program(program), Kind::Program(was)) = (&value.kind, &old.kind) else {
return false;
};
let (Some(machine), Some(end), Held::Run(tape)) = (&was.machine, old.end(), &old.held) else {
return false;
};
let fresh = Machine::over(&program.spanned, end);
old.width == value.width
&& end <= now
&& tape.base() <= end.saturating_sub(program.own).max(tape.origin())
&& fresh.is_ok_and(|opened| opened.accepts(&machine.state()))
}
fn take(value: &mut Value, old: Value) {
value.pure = old.pure;
value.held = old.held;
value.evaluated = old.evaluated;
if let (Kind::Program(program), Kind::Program(was)) = (&mut value.kind, old.kind) {
program.machine = was.machine;
}
}
fn carry(value: &mut Value, old: Value) {
let end = old.end().expect("a stateful value stands somewhere");
let Kind::Program(was) = old.kind else {
unreachable!("a stateful predecessor is a program");
};
let state = was.machine.as_ref().expect("a machine that ran").state();
let Kind::Program(program) = &mut value.kind else {
unreachable!("a stateful value is a program");
};
let mut machine = Machine::over(&program.spanned, end).expect("its spans opened once already");
machine.carry(&state);
program.machine = Some(machine);
value.held = old.held;
value.pure = false;
}
fn start_silent(value: &mut Value, now: i64) -> bool {
let end = value.end();
let Kind::Program(program) = &value.kind else {
return false;
};
let start = program.start.expect("a stateful program");
if now <= start || end.is_some_and(|end| end > start) {
return false;
}
value.silent_from(now).is_ok()
}