1use crate::parse::StackEffectDiagram;
2use petgraph::prelude::*;
3
4#[derive(Debug, PartialEq, Eq, Clone, Hash)]
6pub enum Instruction {
7 Clear {
9 cell: isize,
11 },
12 Mov {
14 cell: isize,
16 to: Vec<isize>,
20 },
21 Start {
23 cell: isize,
25 },
26 Top {
28 cell: isize,
30 },
31}
32
33pub fn solve(diagram: &StackEffectDiagram) -> Vec<Instruction> {
58 let mapping = &diagram.mapping;
59 let inputs = diagram.inputs;
60
61 let temp = std::cmp::max(inputs, mapping.len()) as isize;
63
64 let edges: Vec<_> = mapping.iter().enumerate().map(|(i, j)| (*j, i)).collect();
65
66 let mut digraph: DiGraph<(), (), usize> = DiGraph::from_edges(edges);
67
68 if mapping.len() < inputs {
70 (mapping.len()..inputs).for_each(|_| {
71 digraph.add_node(());
72 })
73 }
74
75 let tarjan = petgraph::algo::tarjan_scc(&digraph);
77 println!("{:?}", tarjan);
78
79 let mut instructions = vec![Instruction::Start { cell: inputs as isize - 1 }];
80
81 for component in tarjan {
82 if component.len() == 1 {
83 let neighbors = get_neighbors(&digraph, component[0]);
85 let index = component[0].index() as isize;
86
87 if neighbors.is_empty() {
88 if index < inputs as isize {
89 instructions.push(Instruction::Clear { cell: index });
90 }
91 } else if neighbors.contains(&index) {
92 if neighbors.len() > 1 {
93 instructions.push(Instruction::Mov { cell: index, to: vec![temp] });
94 instructions.push(Instruction::Mov { cell: temp, to: neighbors });
95 }
96 } else {
97 instructions.push(Instruction::Mov{ cell: index, to: neighbors});
98 }
99 } else {
100 let mut iter = component.into_iter();
101
102 let last_index = iter.next().unwrap();
103 let last_neighbors = get_neighbors(&digraph, last_index);
104
105 instructions.push(Instruction::Mov{ cell: last_index.index() as isize, to: vec![temp]} );
106
107 for node in iter {
108 let neighbors = get_neighbors(&digraph, node);
109 instructions.push(Instruction::Mov{ cell: node.index() as isize, to: neighbors });
110 }
111
112 instructions.push(Instruction::Mov{ cell: temp, to: last_neighbors});
113 }
114 }
115
116 instructions.push(Instruction::Top { cell: mapping.len() as isize - 1 });
117
118 instructions
119}
120
121fn get_neighbors<N, E, Ty, Ix>(graph: &Graph<N, E, Ty, Ix>, index: NodeIndex<Ix>) -> Vec<isize>
122where
123 Ty: petgraph::EdgeType,
124 Ix: petgraph::adj::IndexType,
125{
126 graph.neighbors(index).map(|i| i.index() as isize).collect()
127}