use crate::parse::StackEffectDiagram;
use petgraph::prelude::*;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Instruction {
Clear {
cell: isize,
},
Mov {
cell: isize,
to: Vec<isize>,
},
Start {
cell: isize,
},
Top {
cell: isize,
},
}
pub fn solve(diagram: &StackEffectDiagram) -> Vec<Instruction> {
let mapping = &diagram.mapping;
let inputs = diagram.inputs;
let temp = std::cmp::max(inputs, mapping.len()) as isize;
let edges: Vec<_> = mapping.iter().enumerate().map(|(i, j)| (*j, i)).collect();
let mut digraph: DiGraph<(), (), usize> = DiGraph::from_edges(edges);
if mapping.len() < inputs {
(mapping.len()..inputs).for_each(|_| {
digraph.add_node(());
})
}
let tarjan = petgraph::algo::tarjan_scc(&digraph);
println!("{:?}", tarjan);
let mut instructions = vec![Instruction::Start { cell: inputs as isize - 1 }];
for component in tarjan {
if component.len() == 1 {
let neighbors = get_neighbors(&digraph, component[0]);
let index = component[0].index() as isize;
if neighbors.is_empty() {
if index < inputs as isize {
instructions.push(Instruction::Clear { cell: index });
}
} else if neighbors.contains(&index) {
if neighbors.len() > 1 {
instructions.push(Instruction::Mov { cell: index, to: vec![temp] });
instructions.push(Instruction::Mov { cell: temp, to: neighbors });
}
} else {
instructions.push(Instruction::Mov{ cell: index, to: neighbors});
}
} else {
let mut iter = component.into_iter();
let last_index = iter.next().unwrap();
let last_neighbors = get_neighbors(&digraph, last_index);
instructions.push(Instruction::Mov{ cell: last_index.index() as isize, to: vec![temp]} );
for node in iter {
let neighbors = get_neighbors(&digraph, node);
instructions.push(Instruction::Mov{ cell: node.index() as isize, to: neighbors });
}
instructions.push(Instruction::Mov{ cell: temp, to: last_neighbors});
}
}
instructions.push(Instruction::Top { cell: mapping.len() as isize - 1 });
instructions
}
fn get_neighbors<N, E, Ty, Ix>(graph: &Graph<N, E, Ty, Ix>, index: NodeIndex<Ix>) -> Vec<isize>
where
Ty: petgraph::EdgeType,
Ix: petgraph::adj::IndexType,
{
graph.neighbors(index).map(|i| i.index() as isize).collect()
}