use crate::optimizer::error::RouteError;
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::EdgeRef;
use std::collections::HashMap;
pub fn directed_eulerian_circuit<N, E: Clone>(
graph: &DiGraph<N, E>,
start_node: NodeIndex,
) -> Result<Vec<NodeIndex>, RouteError> {
if graph.node_count() == 0 {
return Ok(vec![]);
}
for node in graph.node_indices() {
let in_degree = graph.edges_directed(node, petgraph::Direction::Incoming).count();
let out_degree = graph.edges_directed(node, petgraph::Direction::Outgoing).count();
if in_degree != out_degree {
return Err(RouteError::NotEulerian {
node_id: format!("{node:?}"),
in_degree,
out_degree,
});
}
}
let mut edge_counts: HashMap<NodeIndex, Vec<NodeIndex>> = HashMap::new();
for node in graph.node_indices() {
let targets: Vec<NodeIndex> = graph.edges_directed(node, petgraph::Direction::Outgoing)
.map(|e| e.target())
.collect();
edge_counts.insert(node, targets);
}
let mut circuit = Vec::new();
let mut stack = vec![start_node];
while let Some(u) = stack.last().copied() {
if let Some(targets) = edge_counts.get_mut(&u) {
if let Some(v) = targets.pop() {
stack.push(v);
} else {
circuit.push(
stack.pop().expect("stack non-empty: just verified last element exists")
);
}
} else {
circuit.push(
stack.pop().expect("stack non-empty: u came from stack.last()")
);
}
}
circuit.reverse();
Ok(circuit)
}