#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct DepGraph {
pub nodes: Vec<String>,
pub edges: Vec<(usize, usize)>,
}
#[allow(dead_code)]
pub fn new_dep_graph() -> DepGraph {
DepGraph::default()
}
#[allow(dead_code)]
pub fn dep_add_node(g: &mut DepGraph, name: &str) -> usize {
if let Some(pos) = g.nodes.iter().position(|n| n == name) {
return pos;
}
g.nodes.push(name.to_string());
g.nodes.len() - 1
}
#[allow(dead_code)]
pub fn dep_add_edge(g: &mut DepGraph, from: usize, to: usize) {
if from < g.nodes.len() && to < g.nodes.len() && from != to {
g.edges.push((from, to));
}
}
#[allow(dead_code)]
pub fn topo_sort(g: &DepGraph) -> Result<Vec<usize>, String> {
let n = g.nodes.len();
let mut in_degree = vec![0usize; n];
for &(from, _to) in &g.edges {
in_degree[from] += 1;
}
let mut queue: std::collections::VecDeque<usize> = (0..n)
.filter(|&i| in_degree[i] == 0)
.collect();
let mut result = Vec::with_capacity(n);
while let Some(node) = queue.pop_front() {
result.push(node);
for &(from, to) in &g.edges {
if to == node {
in_degree[from] -= 1;
if in_degree[from] == 0 {
queue.push_back(from);
}
}
}
}
if result.len() == n {
Ok(result)
} else {
Err("cycle detected in dependency graph".to_string())
}
}
#[allow(dead_code)]
pub fn dep_node_count(g: &DepGraph) -> usize {
g.nodes.len()
}
#[allow(dead_code)]
pub fn has_cycle(g: &DepGraph) -> bool {
topo_sort(g).is_err()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_nodes() {
let mut g = new_dep_graph();
dep_add_node(&mut g, "A");
dep_add_node(&mut g, "B");
assert_eq!(dep_node_count(&g), 2);
}
#[test]
fn topo_sort_simple() {
let mut g = new_dep_graph();
let a = dep_add_node(&mut g, "A");
let b = dep_add_node(&mut g, "B");
let c = dep_add_node(&mut g, "C");
dep_add_edge(&mut g, b, a); dep_add_edge(&mut g, c, b); let sorted = topo_sort(&g).expect("should succeed");
let pa = sorted.iter().position(|&x| x == a).expect("should succeed");
let pb = sorted.iter().position(|&x| x == b).expect("should succeed");
let pc = sorted.iter().position(|&x| x == c).expect("should succeed");
assert!(pa < pb);
assert!(pb < pc);
}
#[test]
fn topo_sort_cycle_detected() {
let mut g = new_dep_graph();
let a = dep_add_node(&mut g, "A");
let b = dep_add_node(&mut g, "B");
dep_add_edge(&mut g, a, b);
dep_add_edge(&mut g, b, a);
assert!(topo_sort(&g).is_err());
}
#[test]
fn has_cycle_true() {
let mut g = new_dep_graph();
let a = dep_add_node(&mut g, "A");
let b = dep_add_node(&mut g, "B");
dep_add_edge(&mut g, a, b);
dep_add_edge(&mut g, b, a);
assert!(has_cycle(&g));
}
#[test]
fn has_cycle_false() {
let mut g = new_dep_graph();
let a = dep_add_node(&mut g, "A");
let b = dep_add_node(&mut g, "B");
dep_add_edge(&mut g, b, a);
assert!(!has_cycle(&g));
}
#[test]
fn duplicate_node_returns_same_index() {
let mut g = new_dep_graph();
let a1 = dep_add_node(&mut g, "A");
let a2 = dep_add_node(&mut g, "A");
assert_eq!(a1, a2);
assert_eq!(dep_node_count(&g), 1);
}
#[test]
fn empty_graph_topo_sort() {
let g = new_dep_graph();
let sorted = topo_sort(&g).expect("should succeed");
assert!(sorted.is_empty());
}
#[test]
fn single_node_topo_sort() {
let mut g = new_dep_graph();
dep_add_node(&mut g, "Solo");
let sorted = topo_sort(&g).expect("should succeed");
assert_eq!(sorted.len(), 1);
}
#[test]
fn topo_sort_result_has_all_nodes() {
let mut g = new_dep_graph();
dep_add_node(&mut g, "X");
dep_add_node(&mut g, "Y");
dep_add_node(&mut g, "Z");
let sorted = topo_sort(&g).expect("should succeed");
assert_eq!(sorted.len(), 3);
}
#[test]
fn dep_node_count_zero_initial() {
let g = new_dep_graph();
assert_eq!(dep_node_count(&g), 0);
}
}