use crate::maybe_grow;
use hvmc::ast::{Book, Net, Tree};
use std::collections::{HashMap, HashSet};
pub fn add_recursive_priority(book: &mut Book) {
let deps = book.iter().map(|(nam, net)| (nam.clone(), dependencies(net))).collect::<HashMap<_, _>>();
let cycles = cycles(&deps);
for cycle in cycles {
for i in 0 .. cycle.len() {
let cur = book.get_mut(&cycle[i]).unwrap();
let nxt = &cycle[(i + 1) % cycle.len()];
add_priority_next_in_cycle(cur, nxt);
}
}
}
fn add_priority_next_in_cycle(net: &mut Net, nxt: &String) {
let mut count = 0;
for (_, a, b) in net.redexes.iter() {
if let Tree::Ref { nam } = a {
if nam == nxt {
count += 1;
}
}
if let Tree::Ref { nam } = b {
if nam == nxt {
count += 1;
}
}
}
if count > 1 {
for (pri, a, b) in net.redexes.iter_mut().rev() {
if let Tree::Ref { nam } = a {
if nam == nxt {
*pri = true;
}
}
if let Tree::Ref { nam } = b {
if nam == nxt {
*pri = true;
}
}
}
}
}
type DepGraph = HashMap<String, HashSet<String>>;
type Cycles = Vec<Vec<String>>;
pub fn cycles(deps: &DepGraph) -> Cycles {
let mut cycles = vec![];
let mut stack = vec![];
let mut visited = HashSet::new();
for nam in deps.keys() {
if !visited.contains(nam) {
find_cycles(deps, nam, &mut visited, &mut stack, &mut cycles);
}
}
cycles
}
fn find_cycles(
deps: &DepGraph,
nam: &String,
visited: &mut HashSet<String>,
stack: &mut Vec<String>,
cycles: &mut Cycles,
) {
maybe_grow(|| {
if let Some(cycle_start) = stack.iter().position(|n| n == nam) {
cycles.push(stack[cycle_start ..].to_vec());
return;
}
if visited.insert(nam.clone()) {
stack.push(nam.clone());
if let Some(dependencies) = deps.get(nam) {
for dep in dependencies {
find_cycles(deps, dep, visited, stack, cycles);
}
}
stack.pop();
}
})
}
fn dependencies(net: &Net) -> HashSet<String> {
let mut deps = HashSet::new();
dependencies_tree(&net.root, &mut deps);
for (_, a, b) in &net.redexes {
dependencies_tree(a, &mut deps);
dependencies_tree(b, &mut deps);
}
deps
}
fn dependencies_tree(tree: &Tree, deps: &mut HashSet<String>) {
if let Tree::Ref { nam, .. } = tree {
deps.insert(nam.clone());
} else {
for subtree in tree.children() {
dependencies_tree(subtree, deps);
}
}
}