use crate::model::snapshot::{Node, Tree};
use crate::model::tree::{links_from, Link};
use crate::model::types::Edge;
use crate::view::lines::{quiet, BeadFacts};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Spine {
#[default]
EveryCopy,
FirstReached,
Shallowest,
ParentChild,
Deepest,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum Stand {
EveryCopy {
first: bool,
},
OneCopy {
chosen: usize,
on: bool,
over_work: bool,
},
}
impl Spine {
pub const EVERY: &'static [Spine] = &[
Spine::EveryCopy,
Spine::FirstReached,
Spine::Shallowest,
Spine::ParentChild,
Spine::Deepest,
];
pub fn next(self) -> Spine {
let at = Spine::EVERY
.iter()
.position(|rule| *rule == self)
.expect("every rule is in the cycle");
Spine::EVERY[(at + 1) % Spine::EVERY.len()]
}
pub(super) fn begins(self, tree: &Tree, at: usize, chosen: &mut Vec<Chosen>) -> Stand {
let Some(ways) = self.chooses(tree, at) else {
return Stand::EveryCopy { first: true };
};
let stand = Stand::OneCopy {
chosen: chosen.len(),
on: true,
over_work: ways.over_work(at),
};
chosen.push(ways);
stand
}
fn chooses(self, tree: &Tree, at: usize) -> Option<Chosen> {
let from = match self {
Spine::EveryCopy => return None,
Spine::FirstReached => first_reached(tree, at),
Spine::Shallowest => shallowest(tree, at),
Spine::ParentChild => parent_child(tree, at),
Spine::Deepest => deepest(tree, at),
};
Some(Chosen::of(tree, at, from))
}
}
impl Stand {
pub(super) fn over_nothing() -> Stand {
Stand::EveryCopy { first: true }
}
pub(super) fn beneath(self, from: usize, link: &Link, chosen: &[Chosen]) -> Stand {
match self {
Stand::EveryCopy { first } => Stand::EveryCopy {
first: first && link.first,
},
Stand::OneCopy {
chosen: which, on, ..
} => {
let ways = &chosen[which];
Stand::OneCopy {
chosen: which,
on: on && ways.chose(from, link.bead),
over_work: ways.over_work(link.bead),
}
}
}
}
pub(super) fn rests_open(self, bead: &BeadFacts) -> bool {
match self {
Stand::EveryCopy { first } => first && bead.opens_a_fold,
Stand::OneCopy { on, over_work, .. } => on && over_work,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct Chosen {
from: Vec<Option<usize>>,
over_work: Vec<bool>,
}
impl Chosen {
fn of(tree: &Tree, at: usize, from: Vec<Option<usize>>) -> Self {
let over_work = work_beneath(tree, at, &from);
Chosen { from, over_work }
}
fn chose(&self, from: usize, to: usize) -> bool {
self.from[to] == Some(from)
}
fn over_work(&self, at: usize) -> bool {
self.over_work[at]
}
}
fn first_reached(tree: &Tree, at: usize) -> Vec<Option<usize>> {
let mut from: Vec<Option<usize>> = vec![None; tree.beads.len()];
let mut reached = vec![false; tree.beads.len()];
reached[at] = true;
reach(tree, at, &mut reached, &mut from);
from
}
fn reach(tree: &Tree, at: usize, reached: &mut Vec<bool>, from: &mut Vec<Option<usize>>) {
for link in links_from(&tree.children, at, &[]) {
if reached[link.bead] {
continue;
}
reached[link.bead] = true;
from[link.bead] = Some(at);
reach(tree, link.bead, reached, from);
}
}
fn shallowest(tree: &Tree, at: usize) -> Vec<Option<usize>> {
let mut from: Vec<Option<usize>> = vec![None; tree.beads.len()];
let mut settled = vec![false; tree.beads.len()];
settled[at] = true;
let mut step = vec![at];
while !step.is_empty() {
let mut reached = Vec::new();
for above in &step {
for link in links_from(&tree.children, *above, &[]) {
if settled[link.bead] {
continue;
}
match from[link.bead] {
Some(already) => from[link.bead] = Some(already.min(*above)),
None => {
from[link.bead] = Some(*above);
reached.push(link.bead);
}
}
}
}
for bead in &reached {
settled[*bead] = true;
}
step = reached;
}
from
}
fn parent_child(tree: &Tree, at: usize) -> Vec<Option<usize>> {
let mut from = first_reached(tree, at);
for (parent, links) in tree.children.iter().enumerate() {
if parent != at && from[parent].is_none() {
continue;
}
for link in links {
if link.edge == Edge::ParentChild
&& from[link.bead].is_some()
&& runs_down_to(at, parent, link.bead, &from)
{
from[link.bead] = Some(parent);
}
}
}
from
}
fn runs_down_to(at: usize, parent: usize, child: usize, from: &[Option<usize>]) -> bool {
let mut above = parent;
while above != at {
if above == child {
return false;
}
let Some(further) = from[above] else {
return false;
};
above = further;
}
true
}
fn deepest(tree: &Tree, at: usize) -> Vec<Option<usize>> {
let mut deepest: Vec<Option<usize>> = vec![None; tree.beads.len()];
let mut from: Vec<Option<usize>> = vec![None; tree.beads.len()];
deepest[at] = Some(0);
deepen(tree, at, 0, &mut vec![at], &mut deepest, &mut from);
from
}
fn deepen(
tree: &Tree,
at: usize,
depth: usize,
path: &mut Vec<usize>,
deepest: &mut Vec<Option<usize>>,
from: &mut Vec<Option<usize>>,
) {
for link in links_from(&tree.children, at, path) {
let below = depth + 1;
match deepest[link.bead] {
Some(found) if found > below => {}
Some(found) if found == below => {
if from[link.bead] > Some(at) {
from[link.bead] = Some(at);
}
}
_ => {
deepest[link.bead] = Some(below);
from[link.bead] = Some(at);
path.push(link.bead);
deepen(tree, link.bead, below, path, deepest, from);
path.pop();
}
}
}
}
fn work_beneath(tree: &Tree, at: usize, from: &[Option<usize>]) -> Vec<bool> {
let mut under: Vec<Vec<usize>> = vec![Vec::new(); from.len()];
for (bead, stepped) in from.iter().enumerate() {
if let Some(stepped) = stepped {
under[*stepped].push(bead);
}
}
let mut over_work = vec![false; from.len()];
work_under(
tree,
at,
&under,
&mut vec![false; from.len()],
&mut over_work,
);
over_work
}
fn work_under(
tree: &Tree,
at: usize,
under: &[Vec<usize>],
seen: &mut Vec<bool>,
over_work: &mut Vec<bool>,
) -> bool {
if seen[at] {
return false;
}
seen[at] = true;
let mut found = false;
for bead in &under[at] {
found |= work_under(tree, *bead, under, seen, over_work);
}
over_work[at] = found;
found || wanted(&tree.beads[at])
}
fn wanted(bead: &Node) -> bool {
!quiet(bead) || bead.ready
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn every_rule_is_one_step_from_the_next_and_the_cycle_closes() {
let mut reached = vec![Spine::default()];
while reached.len() < Spine::EVERY.len() {
let next = reached.last().expect("the cycle starts somewhere").next();
assert!(
!reached.contains(&next),
"the cycle closed early: {reached:?}"
);
reached.push(next);
}
assert_eq!(
reached.last().expect("the cycle ends somewhere").next(),
Spine::default(),
"the cycle did not come back round"
);
}
}