use hashbrown::{HashMap, HashSet};
use petgraph::visit::{
EdgeCount, GraphBase, IntoNeighborsDirected, IntoNodeIdentifiers, NodeCount,
};
use petgraph::Direction::Outgoing;
use std::hash::Hash;
pub fn find_cycle<G>(graph: G, source: Option<G::NodeId>) -> Vec<(G::NodeId, G::NodeId)>
where
G: GraphBase,
G: NodeCount,
G: EdgeCount,
for<'b> &'b G: GraphBase<NodeId = G::NodeId> + IntoNodeIdentifiers + IntoNeighborsDirected,
G::NodeId: Eq + Hash,
{
let mut graph_nodes: HashSet<G::NodeId> = graph.node_identifiers().collect();
let mut cycle: Vec<(G::NodeId, G::NodeId)> = Vec::with_capacity(graph.edge_count());
let temp_value: G::NodeId;
let source_index = match source {
Some(source_value) => source_value,
None => {
temp_value = *graph_nodes.iter().next().unwrap();
graph_nodes.remove(&temp_value);
temp_value
}
};
let mut stack: Vec<G::NodeId> = vec![source_index];
let mut pred: HashMap<G::NodeId, G::NodeId> = HashMap::new();
let mut visiting = HashSet::new();
let mut visited = HashSet::new();
while !stack.is_empty() {
let mut z = *stack.last().unwrap();
visiting.insert(z);
let children = graph.neighbors_directed(z, Outgoing);
for child in children {
if visiting.contains(&child) {
cycle.push((z, child));
loop {
if z == child {
cycle.reverse();
break;
}
cycle.push((pred[&z], z));
z = pred[&z];
}
return cycle;
}
if !visited.contains(&child) {
stack.push(child);
pred.insert(child, z);
}
}
let top = *stack.last().unwrap();
if top == z {
stack.pop();
visiting.remove(&z);
visited.insert(z);
}
}
cycle
}
#[cfg(test)]
mod tests {
use crate::connectivity::find_cycle;
use petgraph::prelude::*;
#[test]
fn test_find_cycle_source() {
let edge_list = vec![
(0, 1),
(3, 0),
(0, 5),
(8, 0),
(1, 2),
(1, 6),
(2, 3),
(3, 4),
(4, 5),
(6, 7),
(7, 8),
(8, 9),
];
let graph = DiGraph::<i32, i32>::from_edges(&edge_list);
let mut res: Vec<(usize, usize)> = find_cycle(&graph, Some(NodeIndex::new(0)))
.iter()
.map(|(s, t)| (s.index(), t.index()))
.collect();
assert_eq!(res, [(0, 1), (1, 2), (2, 3), (3, 0)]);
res = find_cycle(&graph, Some(NodeIndex::new(1)))
.iter()
.map(|(s, t)| (s.index(), t.index()))
.collect();
assert_eq!(res, [(1, 2), (2, 3), (3, 0), (0, 1)]);
res = find_cycle(&graph, Some(NodeIndex::new(5)))
.iter()
.map(|(s, t)| (s.index(), t.index()))
.collect();
assert_eq!(res, []);
}
#[test]
fn test_self_loop() {
let edge_list = vec![
(0, 1),
(3, 0),
(0, 5),
(8, 0),
(1, 2),
(1, 6),
(2, 3),
(3, 4),
(4, 5),
(6, 7),
(7, 8),
(8, 9),
];
let mut graph = DiGraph::<i32, i32>::from_edges(&edge_list);
graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
let res: Vec<(usize, usize)> = find_cycle(&graph, Some(NodeIndex::new(0)))
.iter()
.map(|(s, t)| (s.index(), t.index()))
.collect();
assert_eq!(res, [(1, 1)]);
}
}