disperazione 1

main
Antonio De Lucreziis 4 weeks ago
parent 509f0a54e8
commit b0056571e2

@ -46,6 +46,7 @@ class Graph:
def add_edge(self, u, v): def add_edge(self, u, v):
if u not in self.adjacency_list: if u not in self.adjacency_list:
self.adjacency_list[u] = [] self.adjacency_list[u] = []
self.adjacency_list[u].append(v) self.adjacency_list[u].append(v)
def vertices(self): def vertices(self):
@ -56,10 +57,18 @@ class Graph:
# Example usage: # Example usage:
g = Graph() g = Graph()
# g.add_edge(0, 1)
# g.add_edge(1, 2)
# g.add_edge(2, 3)
# g.add_edge(3, 0)
# g.add_edge(3, 4)
# g.add_edge(4, 5)
# g.add_edge(5, 0)
# g.add_edge(4, 2)
g.add_edge(0, 1) g.add_edge(0, 1)
g.add_edge(1, 2) g.add_edge(1, 2)
g.add_edge(2, 3) g.add_edge(0, 2)
g.add_edge(3, 0) # Creating the cycle 0 -> 1 -> 2 -> 3 -> 0
# Running DFS # Running DFS
results = dfs(g) results = dfs(g)

@ -1,6 +1,6 @@
use std::fmt::Display; use std::fmt::Display;
#[derive(Debug, Hash, PartialEq, Eq, Clone)] #[derive(Debug, Hash, PartialEq, PartialOrd, Ord, Eq, Copy, Clone)]
pub enum Orientation { pub enum Orientation {
Forward, Forward,
Reverse, Reverse,

@ -1,6 +1,6 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{BTreeMap, HashMap, HashSet, VecDeque}, collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
fmt::Debug, fmt::Debug,
hash::Hash, hash::Hash,
rc::Rc, rc::Rc,
@ -13,41 +13,48 @@ use super::{AdjacencyGraph, UndirectedGraph};
#[allow(dead_code)] #[allow(dead_code)]
impl<V> AdjacencyGraph<V> impl<V> AdjacencyGraph<V>
where where
V: Hash + Eq + Clone + Debug, V: Ord + Clone + Debug,
{ {
pub fn new() -> Self { pub fn new() -> Self {
AdjacencyGraph { AdjacencyGraph {
nodes: HashSet::new(), nodes: BTreeSet::new(),
adjacencies: HashMap::new(), adjacencies: BTreeMap::new(),
} }
} }
pub fn from_edges(edges: &[(V, V)]) -> Self {
let mut graph = AdjacencyGraph::new();
for (from, to) in edges {
graph.add_edge(from.clone(), to.clone());
}
graph
}
pub fn add_node(&mut self, node: V) { pub fn add_node(&mut self, node: V) {
// O(1)
self.nodes.insert(node); self.nodes.insert(node);
} }
pub fn add_edge(&mut self, from: V, to: V) { pub fn add_edge(&mut self, from: V, to: V) {
// O(1)
self.add_node(from.clone()); self.add_node(from.clone());
self.add_node(to.clone()); self.add_node(to.clone());
// O(1)
self.adjacencies self.adjacencies
.entry(from) .entry(from)
.or_insert_with(HashSet::new) .or_insert_with(BTreeSet::new)
.insert(to); .insert(to);
} }
pub fn get_adjacencies(&self, node: &V) -> Option<&HashSet<V>> { pub fn get_adjacencies(&self, node: &V) -> Option<&BTreeSet<V>> {
self.adjacencies.get(node) self.adjacencies.get(node)
} }
pub fn adjacencies(&self) -> &HashMap<V, HashSet<V>> { pub fn adjacencies(&self) -> &BTreeMap<V, BTreeSet<V>> {
&self.adjacencies &self.adjacencies
} }
pub fn nodes(&self) -> &HashSet<V> { pub fn nodes(&self) -> &BTreeSet<V> {
&self.nodes &self.nodes
} }
@ -91,7 +98,7 @@ where
} }
pub fn dfs<'a>(&'a self, node: &'a V) -> impl Iterator<Item = V> + 'a { pub fn dfs<'a>(&'a self, node: &'a V) -> impl Iterator<Item = V> + 'a {
let mut visited = HashSet::new(); let mut visited = BTreeSet::new();
let mut stack = VecDeque::from([node]); let mut stack = VecDeque::from([node]);
std::iter::from_fn(move || { std::iter::from_fn(move || {
@ -113,7 +120,7 @@ where
/// This computes if this undirected graph is cyclic or not by searching for an oriented cycle in the graph /// This computes if this undirected graph is cyclic or not by searching for an oriented cycle in the graph
pub fn is_cyclic(&self) -> bool { pub fn is_cyclic(&self) -> bool {
let mut remaining_nodes = self.nodes.iter().collect::<HashSet<_>>(); let mut remaining_nodes = self.nodes.iter().collect::<BTreeSet<_>>();
// let progress_bar = ProgressBar::new(self.nodes.len() as u64); // let progress_bar = ProgressBar::new(self.nodes.len() as u64);
// let mut visited_count = 0; // let mut visited_count = 0;
@ -125,7 +132,7 @@ where
remaining_nodes.remove(start); remaining_nodes.remove(start);
// progress_bar.inc(1); // progress_bar.inc(1);
let mut dfs_visited = HashSet::new(); let mut dfs_visited = BTreeSet::new();
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
stack.push_back(start); stack.push_back(start);
@ -154,12 +161,12 @@ where
false false
} }
pub fn shortest_path_matrix(&self) -> HashMap<&V, HashMap<&V, usize>> { pub fn shortest_path_matrix(&self) -> BTreeMap<&V, BTreeMap<&V, usize>> {
let mut result = HashMap::new(); let mut result = BTreeMap::new();
for node in self.nodes.iter() { for node in self.nodes.iter() {
let mut distances = HashMap::new(); let mut distances = BTreeMap::new();
let mut visited = HashSet::new(); let mut visited = BTreeSet::new();
let mut queue = VecDeque::from([node]); let mut queue = VecDeque::from([node]);
distances.insert(node, 0); distances.insert(node, 0);
@ -190,7 +197,7 @@ where
} }
pub fn compute_ccs(&self) -> Vec<Vec<V>> { pub fn compute_ccs(&self) -> Vec<Vec<V>> {
let mut visited = HashSet::new(); let mut visited = BTreeSet::new();
let mut result = Vec::new(); let mut result = Vec::new();
let op = self.opposite(); let op = self.opposite();
@ -210,7 +217,7 @@ where
continue; continue;
} }
let mut cc: HashSet<V> = HashSet::new(); let mut cc: BTreeSet<V> = BTreeSet::new();
let mut stack: Vec<&V> = vec![node]; let mut stack: Vec<&V> = vec![node];
while let Some(node) = stack.pop() { while let Some(node) = stack.pop() {
@ -240,82 +247,82 @@ where
result result
} }
pub fn compute_ccs_2(&self) -> Vec<Vec<V>> { // pub fn compute_ccs_2(&self) -> Vec<Vec<V>> {
let mut cc: HashMap<V, Rc<RefCell<HashSet<V>>>> = HashMap::new(); // let mut cc: BTreeMap<V, Rc<RefCell<BTreeSet<V>>>> = BTreeMap::new();
for node in self.nodes.iter() { // for node in self.nodes.iter() {
if cc.contains_key(&node) { // if cc.contains_key(&node) {
continue; // continue;
} // }
// println!("All CC: {:?}", cc); // // println!("All CC: {:?}", cc);
let new_cc = Rc::new(RefCell::new(HashSet::new())); // let new_cc = Rc::new(RefCell::new(HashSet::new()));
let mut stack: Vec<&V> = vec![node]; // let mut stack: Vec<&V> = vec![node];
while let Some(node) = stack.pop() { // while let Some(node) = stack.pop() {
// println!("New CC: {:?}", new_cc.borrow()); // // println!("New CC: {:?}", new_cc.borrow());
if cc.contains_key(&node) { // if cc.contains_key(&node) {
// merge the two connected components and go to the next node // // merge the two connected components and go to the next node
let old_cc: &Rc<RefCell<HashSet<V>>> = cc.get(&node).unwrap(); // let old_cc: &Rc<RefCell<HashSet<V>>> = cc.get(&node).unwrap();
// println!( // // println!(
// "Merging {:?} with {:?} due to link to {:?}", // // "Merging {:?} with {:?} due to link to {:?}",
// new_cc.borrow(), // // new_cc.borrow(),
// old_cc.borrow(), // // old_cc.borrow(),
// node // // node
// ); // // );
new_cc // new_cc
.borrow_mut() // .borrow_mut()
.extend(old_cc.borrow().iter().map(|x| x.to_owned())); // .extend(old_cc.borrow().iter().map(|x| x.to_owned()));
break; // break;
} // }
if new_cc.borrow().contains(&node) { // if new_cc.borrow().contains(&node) {
continue; // continue;
} // }
new_cc.borrow_mut().insert(node.clone()); // new_cc.borrow_mut().insert(node.clone());
if let Some(adjacencies) = self.get_adjacencies(&node) { // if let Some(adjacencies) = self.get_adjacencies(&node) {
for adj in adjacencies { // for adj in adjacencies {
stack.push(adj); // stack.push(adj);
} // }
} // }
} // }
for n in new_cc.borrow().iter() { // for n in new_cc.borrow().iter() {
cc.insert(n.to_owned(), new_cc.clone()); // cc.insert(n.to_owned(), new_cc.clone());
} // }
} // }
// extract the unique connected components by pointers // // extract the unique connected components by pointers
let mut result = Vec::new(); // let mut result = Vec::new();
let mut seen = HashSet::new(); // let mut seen = HashSet::new();
for node in self.nodes.iter() { // for node in self.nodes.iter() {
if seen.contains(node) { // if seen.contains(node) {
continue; // continue;
} // }
let cc = cc.get(node).unwrap(); // let cc = cc.get(node).unwrap();
seen.extend(cc.borrow().iter().map(|x| x.to_owned())); // seen.extend(cc.borrow().iter().map(|x| x.to_owned()));
result.push(cc.borrow().iter().map(|x| x.to_owned()).collect()); // result.push(cc.borrow().iter().map(|x| x.to_owned()).collect());
} // }
result // result
} // }
/// This function prints the number of nodes, edges and a histogram of the degrees of the nodes /// This function prints the number of nodes, edges and a histogram of the degrees of the nodes
/// in the graph (computing the degrees might take a long time) /// in the graph (computing the degrees might take a long time)
pub fn print_stats(&self) { pub fn print_stats(&self) {
let mut vertices_degrees = HashMap::new(); let mut vertices_degrees = BTreeMap::new();
for (from, tos) in self for (from, tos) in self
.adjacencies .adjacencies
@ -356,10 +363,10 @@ where
impl<V> UndirectedGraph<V> impl<V> UndirectedGraph<V>
where where
V: Hash + Eq + Clone + Debug, V: Ord + Eq + Clone + Debug,
{ {
pub fn connected_components(&self) -> Vec<Vec<V>> { pub fn connected_components(&self) -> Vec<Vec<V>> {
let mut visited = HashSet::new(); let mut visited = BTreeSet::new();
let mut result = Vec::new(); let mut result = Vec::new();
for node in self.graph.nodes.iter() { for node in self.graph.nodes.iter() {
@ -367,7 +374,7 @@ where
continue; continue;
} }
let mut cc: HashSet<V> = HashSet::new(); let mut cc: BTreeSet<V> = BTreeSet::new();
let mut stack: Vec<&V> = vec![node]; let mut stack: Vec<&V> = vec![node];
while let Some(node) = stack.pop() { while let Some(node) = stack.pop() {

@ -1,6 +1,6 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{HashMap, HashSet}, collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt::Debug, fmt::Debug,
hash::Hash, hash::Hash,
}; };
@ -17,106 +17,202 @@ pub enum EdgeType {
CrossEdge, CrossEdge,
} }
impl<V> AdjacencyGraph<V> struct ClassifyState<V> {
progress_bar: ProgressBar,
edge_types: BTreeMap<(V, V), EdgeType>,
visited: BTreeSet<V>,
start_times: BTreeMap<V, i32>,
finished_nodes: BTreeSet<V>,
time: i32,
}
impl<V> ClassifyState<V>
where where
V: Hash + Eq + Clone + Debug, V: Ord + Eq + Clone + Debug,
{ {
pub fn compute_edge_types(&self) -> HashMap<(&V, &V), EdgeType> { pub fn classify_edges_rec(mut self, graph: &AdjacencyGraph<V>) -> BTreeMap<(V, V), EdgeType> {
let mut edge_types = HashMap::new(); for start in graph.nodes().iter() {
if self.visited.contains(start) {
continue;
}
// TODO: ... self.dfs(graph, start, None);
}
return edge_types; self.progress_bar.finish();
return self.edge_types;
} }
// pub fn compute_edge_types(&self) -> HashMap<(&V, &V), EdgeType> { pub fn dfs(&mut self, graph: &AdjacencyGraph<V>, node: &V, parent: Option<&V>) {
// /// To correctly compute the start and end times of the nodes in the if self.visited.contains(node) {
// /// graph, we need to keep do work before and after the recursion call return;
// enum RecurseState<'a, V> { }
// Before(&'a V),
// BeforeNeighbor(&'a V, &'a V), self.progress_bar.inc(1);
// AfterNeighbor(&'a V), self.visited.insert(node.clone());
// } self.time += 1;
self.start_times.insert(node.clone(), self.time);
if let Some(parent) = parent {
self.edge_types
.insert((parent.clone(), node.clone()), EdgeType::TreeEdge);
}
if let Some(adjacencies) = graph.get_adjacencies(node) {
for adj in adjacencies.iter() {
if !self.visited.contains(adj) {
self.dfs(graph, adj, Some(node));
} else {
if !self.finished_nodes.contains(adj) {
self.edge_types
.insert((node.clone(), adj.clone()), EdgeType::BackEdge);
} else if self.start_times.get(node) < self.start_times.get(adj) {
self.edge_types
.insert((node.clone(), adj.clone()), EdgeType::ForwardEdge);
} else {
self.edge_types
.insert((node.clone(), adj.clone()), EdgeType::CrossEdge);
}
}
}
}
self.time += 1;
self.finished_nodes.insert(node.clone());
}
}
impl<V> AdjacencyGraph<V>
where
V: Ord + Eq + Clone + Debug,
{
pub fn compute_edge_types_rec(&self) -> BTreeMap<(V, V), EdgeType> {
return ClassifyState {
progress_bar: ProgressBar::new(self.nodes().len() as u64),
edge_types: BTreeMap::new(),
visited: BTreeSet::new(),
start_times: BTreeMap::new(),
finished_nodes: BTreeSet::new(),
time: 0,
}
.classify_edges_rec(self);
}
// pub fn compute_edge_types(&self) -> BTreeMap<(V, V), EdgeType> {
// println!("{:?}", self);
// let mut edge_types: BTreeMap<(V, V), EdgeType> = BTreeMap::new();
// let mut visited: BTreeSet<V> = BTreeSet::new();
// let mut edge_types = HashMap::new(); // let mut start_times: BTreeMap<V, i32> = BTreeMap::new();
// let mut finished_nodes: BTreeSet<V> = BTreeSet::new();
// let mut visited = HashSet::new(); // #[derive(Debug)]
// let mut start_times = HashMap::new(); // enum RecurseState<V> {
// let mut finished_nodes = HashSet::new(); // Visit { node: V, parent: Option<V> },
// End { node: V },
// }
// let mut time = 0; // let mut time = 0;
// let progress_bar = ProgressBar::new(self.nodes().len() as u64); // // let progress_bar = ProgressBar::new(self.nodes().len() as u64);
// for node in self.nodes().iter() { // for start in self.nodes().iter() {
// if visited.contains(node) { // if visited.contains(start) {
// continue; // continue;
// } // }
// let mut stack = Vec::new(); // let mut stack: Vec<RecurseState<V>> = Vec::new();
// stack.push(RecurseState::Before(node));
// while let Some(state) = stack.pop() { // // The first node does not have a parent
// match state { // stack.push(RecurseState::End {
// RecurseState::Before(node) => { // node: start.clone(),
// progress_bar.inc(1); // });
// visited.insert(node.clone()); // stack.push(RecurseState::Visit {
// start_times.insert(node, time); // node: start.clone(),
// time += 1; // parent: None,
// });
// // it is extremely important that this before the adjacencies to correctly // println!("Starting DFS from {:?}", start);
// // iterate over the graph
// if let Some(adjacencies) = self.get_adjacencies(node) { // while let Some(state) = stack.pop() {
// for adj in adjacencies { // println!("Current: {:?}", state);
// println!("Node: {:?} Adj: {:?}", node, adj,); // println!("Finished Nodes: {:?}", finished_nodes);
// stack.push(RecurseState::AfterNeighbor(node)); // match state {
// RecurseState::Visit { node, parent } => {
// if visited.contains(&node) {
// // progress_bar.inc(1);
// }
// if !visited.contains(adj) { // if let Some(parent) = parent.clone() {
// edge_types.insert((node, adj), EdgeType::TreeEdge); // if !visited.contains(&node) {
// stack.push(RecurseState::Before(adj)); // println!("{:?} => TreeEdge", (parent.clone(), node.clone()));
// edge_types
// .insert((parent.clone(), node.clone()), EdgeType::TreeEdge);
// } else { // } else {
// stack.push(RecurseState::BeforeNeighbor(node, adj)); // if !finished_nodes.contains(&parent) {
// } // println!("{:?} => BackEdge", (parent.clone(), node.clone()));
// edge_types
// .insert((node.clone(), parent.clone()), EdgeType::BackEdge);
// } else if start_times.get(&node) < start_times.get(&parent) {
// println!("{:?} => ForwardEdge", (parent.clone(), node.clone()));
// edge_types.insert(
// (node.clone(), parent.clone()),
// EdgeType::ForwardEdge,
// );
// } else {
// println!("{:?} => CrossEdge", (parent.clone(), node.clone()));
// edge_types.insert(
// (node.clone(), parent.clone()),
// EdgeType::CrossEdge,
// );
// } // }
// } // }
// } // }
// RecurseState::AfterNeighbor(node) => {
// finished_nodes.insert(node);
// time += 1; // time += 1;
// } // start_times.insert(node.clone(), time);
// RecurseState::BeforeNeighbor(node, adj) => {
// let start_time_node = start_times.get(node).unwrap(); // visited.insert(node.clone());
// let start_time_adj = start_times.get(adj).unwrap();
// let end_time_node = finished_nodes.get(node).unwrap_or(&0);
// let end_time_adj = finished_nodes.get(adj).unwrap_or(&0);
// println!(
// "Times: ({:?}, {:?}) ({:?}, {:?})",
// start_time_node, end_time_node, start_time_adj, end_time_adj
// );
// match ( // // it is extremely important that this before the adjacencies to correctly
// start_time_node.cmp(start_time_adj), // // iterate over the graph
// end_time_node.cmp(end_time_adj), // // stack.push(RecurseState::AfterNeighbors { node });
// ) {
// (Ordering::Less, Ordering::Greater) => { // if let Some(adjacencies) = self.get_adjacencies(&node) {
// edge_types.insert((node, adj), EdgeType::ForwardEdge); // println!("adjacencies: {:?}", adjacencies);
// for adj in adjacencies.iter().rev() {
// if !visited.contains(&adj) {
// stack.push(RecurseState::End { node: adj.clone() });
// stack.push(RecurseState::Visit {
// node: adj.clone(),
// parent: Some(node.clone()),
// });
// } // }
// (Ordering::Greater, Ordering::Less) => {
// edge_types.insert((node, adj), EdgeType::BackEdge);
// } // }
// _ => {
// edge_types.insert((node, adj), EdgeType::CrossEdge);
// } // }
// } // }
// RecurseState::End { node } => {
// time += 1;
// finished_nodes.insert(node.clone());
// } // }
// } // }
// println!();
// // println!("after:");
// // println!("~> {:?}", stack);
// } // }
// } // }
// edge_types // // progress_bar.finish();
// return edge_types;
// } // }
} }

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{BTreeMap, BTreeSet, HashSet},
fmt::Debug, fmt::Debug,
hash::Hash, hash::Hash,
}; };
@ -7,15 +7,15 @@ use std::{
#[derive(Debug)] #[derive(Debug)]
pub struct AdjacencyGraph<V> pub struct AdjacencyGraph<V>
where where
V: Hash + Eq + Clone, V: Clone,
{ {
nodes: HashSet<V>, nodes: BTreeSet<V>,
adjacencies: HashMap<V, HashSet<V>>, adjacencies: BTreeMap<V, BTreeSet<V>>,
} }
pub struct UndirectedGraph<V> pub struct UndirectedGraph<V>
where where
V: Hash + Eq + Clone, V: Clone,
{ {
graph: AdjacencyGraph<V>, graph: AdjacencyGraph<V>,
} }
@ -29,29 +29,97 @@ mod tests {
use super::*; use super::*;
fn print_edge_types<T>(edge_types: &BTreeMap<(T, T), edge_types::EdgeType>)
where
T: Debug,
{
println!("");
println!("Edge types:");
for (edge, edge_type) in edge_types {
println!("{:?} -> {:?}: {:?}", edge.0, edge.1, edge_type);
}
// for (edge_type, edges) in edge_types
// .iter()
// .fold(BTreeMap::new(), |mut acc, (edge, edge_type)| {
// acc.entry(edge_type).or_insert_with(Vec::new).push(edge);
// acc
// })
// .iter()
// {
// println!("- {:?}", edge_type);
// for edge in edges {
// println!("{:?}", edge);
// }
// }
}
#[test]
fn test_compute_edge_types_cycle() {
let g = AdjacencyGraph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)]);
let edge_types = g.compute_edge_types_rec();
print_edge_types(&edge_types);
assert_eq!(edge_types.len(), 4);
assert_eq!(edge_types[&(0, 1)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(1, 2)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(2, 3)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(3, 0)], edge_types::EdgeType::BackEdge);
}
#[test] #[test]
fn test_compute_edge_types() { fn test_compute_edge_types_forward() {
let mut g = AdjacencyGraph::new(); let g = AdjacencyGraph::from_edges(&[(0, 1), (1, 2), (0, 2)]);
g.add_edge(1, 2); let edge_types = g.compute_edge_types_rec();
g.add_edge(2, 3); print_edge_types(&edge_types);
g.add_edge(3, 4);
g.add_edge(4, 1); assert_eq!(edge_types.len(), 3);
assert_eq!(edge_types[&(0, 1)], edge_types::EdgeType::TreeEdge);
let edge_types = g.compute_edge_types(); assert_eq!(edge_types[&(1, 2)], edge_types::EdgeType::TreeEdge);
let edge_type_dict = assert_eq!(edge_types[&(0, 2)], edge_types::EdgeType::ForwardEdge);
edge_types
.iter()
.fold(BTreeMap::new(), |mut acc, (edge, edge_type)| {
acc.entry(edge_type).or_insert_with(Vec::new).push(edge);
acc
});
for (edge_type, edges) in edge_type_dict.iter() {
println!("- {:?}", edge_type);
for edge in edges {
println!("Edge: {:?}", edge);
} }
#[test]
fn test_compute_edge_types_cross() {
let g = AdjacencyGraph::from_edges(&[(0, 1), (1, 2), (0, 3), (3, 4), (2, 4)]);
let edge_types = g.compute_edge_types_rec();
print_edge_types(&edge_types);
assert_eq!(edge_types.len(), 5);
assert_eq!(edge_types[&(0, 1)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(1, 2)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(0, 3)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(2, 4)], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&(3, 4)], edge_types::EdgeType::CrossEdge);
} }
#[test]
fn test_compute_edge_types_all() {
let g = AdjacencyGraph::from_edges(&[
//
("u", "v"),
("u", "x"),
("v", "y"),
("y", "x"),
("x", "v"),
("w", "y"),
("w", "z"),
]);
let edge_types = g.compute_edge_types_rec();
print_edge_types(&edge_types);
assert_eq!(edge_types.len(), 7);
assert_eq!(edge_types[&("u", "v")], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&("u", "x")], edge_types::EdgeType::ForwardEdge);
assert_eq!(edge_types[&("v", "y")], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&("y", "x")], edge_types::EdgeType::TreeEdge);
assert_eq!(edge_types[&("x", "v")], edge_types::EdgeType::BackEdge);
assert_eq!(edge_types[&("w", "y")], edge_types::EdgeType::CrossEdge);
assert_eq!(edge_types[&("w", "z")], edge_types::EdgeType::TreeEdge);
} }
} }

@ -94,7 +94,7 @@ fn main() -> std::io::Result<()> {
// println!("Graph has cycles: {}", graph.is_cyclic()); // println!("Graph has cycles: {}", graph.is_cyclic());
let edge_types = graph.compute_edge_types(); let edge_types = graph.compute_edge_types_rec();
let edge_type_histogram: BTreeMap<_, _> = edge_types let edge_type_histogram: BTreeMap<_, _> = edge_types
.iter() .iter()

Loading…
Cancel
Save