use std::cmp::Eq;
use std::hash::Hash;
use hashbrown::HashMap;
use petgraph::visit::{
GraphProp, IntoEdges, IntoNodeIdentifiers, NodeCount, NodeIndexable, VisitMap, Visitable,
};
use petgraph::Undirected;
use crate::traversal::{depth_first_search, DfsEvent};
fn _build_chain<G, VM: VisitMap<G::NodeId>>(
graph: G,
parent: &[usize],
mut u_id: G::NodeId,
mut v_id: G::NodeId,
visited: &mut VM,
) -> Vec<(G::NodeId, G::NodeId)>
where
G: Visitable + NodeIndexable,
{
let mut chain = Vec::new();
while visited.visit(v_id) {
chain.push((u_id, v_id));
u_id = v_id;
let u = graph.to_index(u_id);
let v = parent[u];
v_id = graph.from_index(v);
}
chain.push((u_id, v_id));
chain
}
pub fn chain_decomposition<G>(
graph: G,
source: Option<G::NodeId>,
) -> Vec<Vec<(G::NodeId, G::NodeId)>>
where
G: IntoNodeIdentifiers
+ IntoEdges
+ Visitable
+ NodeIndexable
+ NodeCount
+ GraphProp<EdgeType = Undirected>,
G::NodeId: Eq + Hash,
{
let roots = match source {
Some(node) => vec![node],
None => graph.node_identifiers().collect(),
};
let mut parent = vec![usize::MAX; graph.node_bound()];
let mut back_edges: HashMap<G::NodeId, Vec<G::NodeId>> = HashMap::new();
let mut nodes = Vec::with_capacity(graph.node_count());
depth_first_search(graph, roots, |event| match event {
DfsEvent::Discover(u, _) => {
nodes.push(u);
}
DfsEvent::TreeEdge(u, v, _) => {
let u = graph.to_index(u);
let v = graph.to_index(v);
parent[v] = u;
}
DfsEvent::BackEdge(u_id, v_id, _) => {
let u = graph.to_index(u_id);
let v = graph.to_index(v_id);
if parent[u] != v {
back_edges
.entry(v_id)
.and_modify(|v_edges| v_edges.push(u_id))
.or_insert(vec![u_id]);
}
}
_ => {}
});
let visited = &mut graph.visit_map();
nodes
.into_iter()
.filter_map(|u| {
visited.visit(u);
back_edges.get(&u).map(|vs| {
vs.iter()
.map(|v| _build_chain(graph, &parent, u, *v, visited))
.collect::<Vec<Vec<(G::NodeId, G::NodeId)>>>()
})
})
.flatten()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use petgraph::graph::node_index as ni;
use petgraph::prelude::*;
#[test]
fn test_decomposition() {
let graph = UnGraph::<(), ()>::from_edges([
(1, 2),
(2, 3),
(3, 4),
(3, 5),
(5, 6),
(6, 7),
(7, 8),
(5, 9),
(9, 10),
(1, 3),
(1, 4),
(2, 5),
(5, 10),
(6, 8),
]);
let chains = chain_decomposition(&graph, Some(NodeIndex::new(1)));
let expected: Vec<Vec<(NodeIndex<usize>, NodeIndex<usize>)>> = vec![
vec![(ni(1), ni(3)), (ni(3), ni(2)), (ni(2), ni(1))],
vec![(ni(1), ni(4)), (ni(4), ni(3))],
vec![(ni(2), ni(5)), (ni(5), ni(3))],
vec![(ni(5), ni(10)), (ni(10), ni(9)), (ni(9), ni(5))],
vec![(ni(6), ni(8)), (ni(8), ni(7)), (ni(7), ni(6))],
];
assert_eq!(chains.len(), expected.len());
}
}