use std::fmt::Debug;
use std::collections::{HashMap, HashSet};
use std::fmt::Formatter;
use std::hash::Hash;
#[derive(Debug, PartialEq)]
pub enum DGError {
EdgeCreatesCycle,
}
impl std::fmt::Display for DGError {
fn fmt(&self, w: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
DGError::EdgeCreatesCycle => w.write_str("Edge creates cycle"),
}
}
}
pub struct DirectedGraph<T> {
adj_list: HashMap<T, Vec<T>>,
}
impl<T: Hash + PartialEq + Eq + Clone> DirectedGraph<T> {
pub fn new() -> Self {
Self {
adj_list: HashMap::new(),
}
}
pub fn add_edge_with_check(&mut self, src: T, dest: T) -> Result<(), DGError> {
self.adj_list
.entry(src.clone())
.or_insert(vec![])
.push(dest.clone());
if self.is_cyclic() {
if let Some(edges) = self.adj_list.get_mut(&src) {
edges.retain(|x| *x != dest);
}
Err(DGError::EdgeCreatesCycle)
} else {
Ok(())
}
}
fn is_cyclic(&self) -> bool {
let mut visited = HashSet::new();
let mut rec_stack = HashSet::new();
for node in self.adj_list.keys() {
if !visited.contains(node) && self.is_cyclic_util(node, &mut visited, &mut rec_stack) {
return true;
}
}
false
}
fn is_cyclic_util(
&self,
node: &T,
visited: &mut HashSet<T>,
rec_stack: &mut HashSet<T>,
) -> bool {
if rec_stack.contains(&node) {
return true;
}
if visited.contains(&node) {
return false;
}
visited.insert(node.clone());
rec_stack.insert(node.clone());
if let Some(neighbors) = self.adj_list.get(&node) {
for neighbor in neighbors {
if self.is_cyclic_util(&neighbor, visited, rec_stack) {
return true;
}
}
}
rec_stack.remove(&node);
false
}
}
#[cfg(test)]
mod directed_graph_tests {
use super::*;
#[test]
fn test_adding_edge_no_cycle() {
let mut graph = DirectedGraph::new();
assert!(graph.add_edge_with_check(0, 1).is_ok());
assert!(graph.add_edge_with_check(1, 2).is_ok());
}
#[test]
fn test_adding_edge_creates_cycle() {
let mut graph = DirectedGraph::new();
graph.add_edge_with_check(0, 1).unwrap();
graph.add_edge_with_check(1, 2).unwrap();
assert!(graph.add_edge_with_check(2, 0).is_err());
}
#[test]
fn test_empty_graph() {
let graph = DirectedGraph::<i32>::new();
assert!(graph.adj_list.is_empty());
}
#[test]
fn test_single_node_self_loop() {
let mut graph = DirectedGraph::new();
assert!(graph.add_edge_with_check(0, 0).is_err());
}
}