mirror of https://github.com/aziis98/asd-2024.git
mid thorugh refactoring
parent
f19862206e
commit
37bc313f20
@ -0,0 +1,103 @@
|
||||
# code
|
||||
import random
|
||||
|
||||
|
||||
class Graph:
|
||||
# instance variables
|
||||
def __init__(self, v):
|
||||
# v is the number of nodes/vertices
|
||||
self.time = 0
|
||||
self.traversal_array = []
|
||||
self.v = v
|
||||
# e is the number of edge (randomly chosen between 9 to 45)
|
||||
self.e = random.randint(9, 45)
|
||||
# adj. list for graph
|
||||
self.graph_list = [[] for _ in range(v)]
|
||||
# adj. matrix for graph
|
||||
self.graph_matrix = [[0 for _ in range(v)] for _ in range(v)]
|
||||
|
||||
# function to create random graph
|
||||
def create_random_graph(self):
|
||||
# add edges upto e
|
||||
for i in range(self.e):
|
||||
# choose src and dest of each edge randomly
|
||||
src = random.randrange(0, self.v)
|
||||
dest = random.randrange(0, self.v)
|
||||
# re-choose if src and dest are same or src and dest already has an edge
|
||||
while src == dest and self.graph_matrix[src][dest] == 1:
|
||||
src = random.randrange(0, self.v)
|
||||
dest = random.randrange(0, self.v)
|
||||
# add the edge to graph
|
||||
self.add_edge(src, dest)
|
||||
|
||||
def add_edge(self, src, dest):
|
||||
self.graph_list[src].append(dest)
|
||||
self.graph_matrix[src][dest] = 1
|
||||
|
||||
# function to print adj list
|
||||
def print_graph_list(self):
|
||||
print("Adjacency List Representation:")
|
||||
for i in range(self.v):
|
||||
print(i, "-->", *self.graph_list[i])
|
||||
print()
|
||||
|
||||
# function to print adj matrix
|
||||
def print_graph_matrix(self):
|
||||
print("Adjacency Matrix Representation:")
|
||||
for i in self.graph_matrix:
|
||||
print(i)
|
||||
print()
|
||||
|
||||
# function the get number of edges
|
||||
def number_of_edges(self):
|
||||
return self.e
|
||||
|
||||
# function for dfs
|
||||
def dfs(self):
|
||||
self.visited = [False]*self.v
|
||||
self.start_time = [0]*self.v
|
||||
self.end_time = [0]*self.v
|
||||
|
||||
for node in range(self.v):
|
||||
if not self.visited[node]:
|
||||
self.traverse_dfs(node)
|
||||
print()
|
||||
print("DFS Traversal: ", self.traversal_array)
|
||||
print()
|
||||
|
||||
def traverse_dfs(self, node):
|
||||
self.visited[node] = True
|
||||
self.traversal_array.append(node)
|
||||
self.start_time[node] = self.time
|
||||
self.time += 1
|
||||
for neighbour in self.graph_list[node]:
|
||||
print('Edge:', str(node)+'-->'+str(neighbour))
|
||||
|
||||
if not self.visited[neighbour]:
|
||||
print(' => Tree Edge')
|
||||
self.traverse_dfs(neighbour)
|
||||
else:
|
||||
print(f"Times: ({self.start_time[node]}, {self.end_time[node]}) ({self.start_time[neighbour]}, {self.end_time[neighbour]})")
|
||||
|
||||
if self.start_time[node] > self.start_time[neighbour] and self.end_time[node] < self.end_time[neighbour]:
|
||||
print(' => Back Edge')
|
||||
elif self.start_time[node] < self.start_time[neighbour] and self.end_time[node] > self.end_time[neighbour]:
|
||||
print(' => Forward Edge')
|
||||
else:
|
||||
print(' => Cross Edge')
|
||||
self.end_time[node] = self.time
|
||||
self.time += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
g = Graph(4)
|
||||
# g.create_random_graph()
|
||||
|
||||
g.add_edge(0, 1)
|
||||
g.add_edge(1, 2)
|
||||
g.add_edge(2, 3)
|
||||
g.add_edge(3, 1)
|
||||
|
||||
g.print_graph_list()
|
||||
g.print_graph_matrix()
|
||||
g.dfs()
|
@ -0,0 +1,68 @@
|
||||
class DFSResult:
|
||||
def __init__(self):
|
||||
self.parent = {}
|
||||
self.start_time = {}
|
||||
self.finish_time = {}
|
||||
self.edges = {} # Edge classification for directed graph.
|
||||
self.order = []
|
||||
self.t = 0
|
||||
|
||||
def dfs(g):
|
||||
results = DFSResult()
|
||||
for vertex in g.vertices():
|
||||
if vertex not in results.parent:
|
||||
dfs_visit(g, vertex, results)
|
||||
return results
|
||||
|
||||
def dfs_visit(g, v, results, parent=None):
|
||||
results.parent[v] = parent
|
||||
results.t += 1
|
||||
results.start_time[v] = results.t
|
||||
if parent is not None:
|
||||
results.edges[(parent, v)] = 'tree'
|
||||
|
||||
for n in g.neighbors(v):
|
||||
if n not in results.parent: # n is not visited
|
||||
dfs_visit(g, n, results, v)
|
||||
elif n not in results.finish_time:
|
||||
results.edges[(v, n)] = 'back'
|
||||
elif results.start_time[v] < results.start_time[n]:
|
||||
results.edges[(v, n)] = 'forward'
|
||||
else:
|
||||
results.edges[(v, n)] = 'cross'
|
||||
|
||||
results.t += 1
|
||||
results.finish_time[v] = results.t
|
||||
results.order.append(v)
|
||||
|
||||
# Graph structure
|
||||
class Graph:
|
||||
def __init__(self):
|
||||
self.adjacency_list = {}
|
||||
|
||||
def add_edge(self, u, v):
|
||||
if u not in self.adjacency_list:
|
||||
self.adjacency_list[u] = []
|
||||
self.adjacency_list[u].append(v)
|
||||
|
||||
def vertices(self):
|
||||
return self.adjacency_list.keys()
|
||||
|
||||
def neighbors(self, v):
|
||||
return self.adjacency_list.get(v, [])
|
||||
|
||||
# Example usage:
|
||||
g = Graph()
|
||||
g.add_edge(0, 1)
|
||||
g.add_edge(1, 2)
|
||||
g.add_edge(2, 3)
|
||||
g.add_edge(3, 0) # Creating the cycle 0 -> 1 -> 2 -> 3 -> 0
|
||||
|
||||
# Running DFS
|
||||
results = dfs(g)
|
||||
|
||||
print("Parent Map:", results.parent)
|
||||
print("Start Times:", results.start_time)
|
||||
print("Finish Times:", results.finish_time)
|
||||
print("Edge Classifications:", results.edges)
|
||||
print("DFS Order:", results.order)
|
@ -1,16 +0,0 @@
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
struct GraphEdge {
|
||||
from: u32,
|
||||
to: u32,
|
||||
}
|
||||
|
||||
pub struct Graph<V, E>
|
||||
where
|
||||
V: Hash + Eq + Clone,
|
||||
{
|
||||
nodes: HashMap<V, u32>,
|
||||
edges: HashMap<(u32, u32), E>,
|
||||
|
||||
adjacency_list: HashMap<u32, Vec<(u32, u32)>>,
|
||||
}
|
@ -0,0 +1,393 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{BTreeMap, HashMap, HashSet, VecDeque},
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use indicatif::ProgressIterator;
|
||||
|
||||
use super::{AdjacencyGraph, UndirectedGraph};
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<V> AdjacencyGraph<V>
|
||||
where
|
||||
V: Hash + Eq + Clone + Debug,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
AdjacencyGraph {
|
||||
nodes: HashSet::new(),
|
||||
adjacencies: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_node(&mut self, node: V) {
|
||||
// O(1)
|
||||
self.nodes.insert(node);
|
||||
}
|
||||
|
||||
pub fn add_edge(&mut self, from: V, to: V) {
|
||||
// O(1)
|
||||
self.add_node(from.clone());
|
||||
self.add_node(to.clone());
|
||||
|
||||
// O(1)
|
||||
self.adjacencies
|
||||
.entry(from)
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(to);
|
||||
}
|
||||
|
||||
pub fn get_adjacencies(&self, node: &V) -> Option<&HashSet<V>> {
|
||||
self.adjacencies.get(node)
|
||||
}
|
||||
|
||||
pub fn adjacencies(&self) -> &HashMap<V, HashSet<V>> {
|
||||
&self.adjacencies
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> &HashSet<V> {
|
||||
&self.nodes
|
||||
}
|
||||
|
||||
pub fn edges(&self) -> impl Iterator<Item = (&V, &V)> {
|
||||
self.adjacencies
|
||||
.iter()
|
||||
.flat_map(|(from, tos)| tos.iter().map(move |to| (from, to)))
|
||||
}
|
||||
|
||||
pub fn opposite(&self) -> AdjacencyGraph<&V> {
|
||||
let mut opposite = AdjacencyGraph::new();
|
||||
|
||||
// O(|E|)
|
||||
for (from, to) in self.edges() {
|
||||
opposite.add_edge(to, from);
|
||||
}
|
||||
|
||||
opposite
|
||||
}
|
||||
|
||||
pub fn undirected(&self) -> UndirectedGraph<&V> {
|
||||
let mut undirected = AdjacencyGraph::new();
|
||||
|
||||
// O(|E|)
|
||||
for (from, to) in self.edges() {
|
||||
undirected.add_edge(from, to);
|
||||
undirected.add_edge(to, from);
|
||||
}
|
||||
|
||||
UndirectedGraph { graph: undirected }
|
||||
}
|
||||
|
||||
pub fn has_edge(&self, from: &V, to: &V) -> bool {
|
||||
// O(1)
|
||||
if let Some(adjacencies) = self.get_adjacencies(from) {
|
||||
// O(1)
|
||||
adjacencies.contains(&to.to_owned())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dfs<'a>(&'a self, node: &'a V) -> impl Iterator<Item = V> + 'a {
|
||||
let mut visited = HashSet::new();
|
||||
let mut stack = VecDeque::from([node]);
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
while let Some(node) = stack.pop_back() {
|
||||
if !visited.insert(node.clone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(node) {
|
||||
stack.extend(adjacencies);
|
||||
}
|
||||
|
||||
return Some(node.clone());
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let mut remaining_nodes = self.nodes.iter().collect::<HashSet<_>>();
|
||||
|
||||
// let progress_bar = ProgressBar::new(self.nodes.len() as u64);
|
||||
// let mut visited_count = 0;
|
||||
|
||||
while !remaining_nodes.is_empty() {
|
||||
let start: &V = remaining_nodes.iter().next().unwrap();
|
||||
|
||||
// visited_count += 1;
|
||||
remaining_nodes.remove(start);
|
||||
// progress_bar.inc(1);
|
||||
|
||||
let mut dfs_visited = HashSet::new();
|
||||
let mut stack = VecDeque::new();
|
||||
stack.push_back(start);
|
||||
|
||||
// start a new dfs from the current node
|
||||
while let Some(node) = stack.pop_back() {
|
||||
if dfs_visited.contains(node) {
|
||||
// println!("Found cycle after {} nodes", visited_count);
|
||||
// progress_bar.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
// visited_count += 1;
|
||||
remaining_nodes.remove(node);
|
||||
// progress_bar.inc(1);
|
||||
|
||||
dfs_visited.insert(node.clone());
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(node) {
|
||||
stack.extend(adjacencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// println!("Found cycle after {} nodes", visited_count);
|
||||
// progress_bar.finish();
|
||||
false
|
||||
}
|
||||
|
||||
pub fn shortest_path_matrix(&self) -> HashMap<&V, HashMap<&V, usize>> {
|
||||
let mut result = HashMap::new();
|
||||
|
||||
for node in self.nodes.iter() {
|
||||
let mut distances = HashMap::new();
|
||||
let mut visited = HashSet::new();
|
||||
let mut queue = VecDeque::from([node]);
|
||||
|
||||
distances.insert(node, 0);
|
||||
|
||||
while let Some(node) = queue.pop_front() {
|
||||
if visited.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.insert(node.clone());
|
||||
|
||||
let distance = *distances.get(node).unwrap();
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(node) {
|
||||
for adj in adjacencies {
|
||||
if !distances.contains_key(adj) {
|
||||
distances.insert(adj, distance + 1);
|
||||
queue.push_back(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.insert(node, distances);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn compute_ccs(&self) -> Vec<Vec<V>> {
|
||||
let mut visited = HashSet::new();
|
||||
let mut result = Vec::new();
|
||||
|
||||
let op = self.opposite();
|
||||
|
||||
for node in self
|
||||
.nodes
|
||||
.iter()
|
||||
.progress()
|
||||
.with_style(
|
||||
indicatif::ProgressStyle::default_bar()
|
||||
.template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}")
|
||||
.unwrap(),
|
||||
)
|
||||
.with_prefix("computing connected components")
|
||||
{
|
||||
if visited.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut cc: HashSet<V> = HashSet::new();
|
||||
let mut stack: Vec<&V> = vec![node];
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
if cc.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cc.insert(node.clone());
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(&node) {
|
||||
for adj in adjacencies {
|
||||
stack.push(adj);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(adjacencies) = op.get_adjacencies(&node) {
|
||||
for adj in adjacencies {
|
||||
stack.push(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visited.extend(cc.iter().map(|x| x.to_owned()));
|
||||
result.push(cc.iter().map(|x| x.to_owned()).collect());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn compute_ccs_2(&self) -> Vec<Vec<V>> {
|
||||
let mut cc: HashMap<V, Rc<RefCell<HashSet<V>>>> = HashMap::new();
|
||||
|
||||
for node in self.nodes.iter() {
|
||||
if cc.contains_key(&node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// println!("All CC: {:?}", cc);
|
||||
|
||||
let new_cc = Rc::new(RefCell::new(HashSet::new()));
|
||||
|
||||
let mut stack: Vec<&V> = vec![node];
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
// println!("New CC: {:?}", new_cc.borrow());
|
||||
|
||||
if cc.contains_key(&node) {
|
||||
// merge the two connected components and go to the next node
|
||||
|
||||
let old_cc: &Rc<RefCell<HashSet<V>>> = cc.get(&node).unwrap();
|
||||
|
||||
// println!(
|
||||
// "Merging {:?} with {:?} due to link to {:?}",
|
||||
// new_cc.borrow(),
|
||||
// old_cc.borrow(),
|
||||
// node
|
||||
// );
|
||||
|
||||
new_cc
|
||||
.borrow_mut()
|
||||
.extend(old_cc.borrow().iter().map(|x| x.to_owned()));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if new_cc.borrow().contains(&node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_cc.borrow_mut().insert(node.clone());
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(&node) {
|
||||
for adj in adjacencies {
|
||||
stack.push(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for n in new_cc.borrow().iter() {
|
||||
cc.insert(n.to_owned(), new_cc.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// extract the unique connected components by pointers
|
||||
let mut result = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
|
||||
for node in self.nodes.iter() {
|
||||
if seen.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cc = cc.get(node).unwrap();
|
||||
seen.extend(cc.borrow().iter().map(|x| x.to_owned()));
|
||||
|
||||
result.push(cc.borrow().iter().map(|x| x.to_owned()).collect());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub fn print_stats(&self) {
|
||||
let mut vertices_degrees = HashMap::new();
|
||||
|
||||
for (from, tos) in self
|
||||
.adjacencies
|
||||
.iter()
|
||||
.progress()
|
||||
.with_style(
|
||||
indicatif::ProgressStyle::default_bar()
|
||||
.template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}")
|
||||
.unwrap(),
|
||||
)
|
||||
.with_prefix("computing nodes degrees")
|
||||
{
|
||||
*vertices_degrees.entry(from).or_insert(0) += tos.len();
|
||||
|
||||
for to in tos {
|
||||
*vertices_degrees.entry(to).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let histogram: BTreeMap<usize, usize> = vertices_degrees
|
||||
.iter()
|
||||
.map(|(_, degree)| *degree)
|
||||
.fold(BTreeMap::new(), |mut acc, degree| {
|
||||
*acc.entry(degree).or_insert(0) += 1;
|
||||
acc
|
||||
});
|
||||
|
||||
println!("Stats:");
|
||||
println!("Nodes: {}", self.nodes.len());
|
||||
println!("Edges: {}", self.edges().count());
|
||||
|
||||
println!("Histogram:");
|
||||
for (degree, count) in histogram.iter() {
|
||||
println!("{}: {}", degree, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> UndirectedGraph<V>
|
||||
where
|
||||
V: Hash + Eq + Clone + Debug,
|
||||
{
|
||||
pub fn connected_components(&self) -> Vec<Vec<V>> {
|
||||
let mut visited = HashSet::new();
|
||||
let mut result = Vec::new();
|
||||
|
||||
for node in self.graph.nodes.iter() {
|
||||
if visited.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut cc: HashSet<V> = HashSet::new();
|
||||
let mut stack: Vec<&V> = vec![node];
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
if cc.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cc.insert(node.clone());
|
||||
|
||||
if let Some(adjacencies) = self.graph.get_adjacencies(&node) {
|
||||
for adj in adjacencies {
|
||||
stack.push(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visited.extend(cc.iter().map(|x| x.to_owned()));
|
||||
result.push(cc.iter().map(|x| x.to_owned()).collect());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
use indicatif::ProgressBar;
|
||||
|
||||
use super::AdjacencyGraph;
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EdgeType {
|
||||
TreeEdge,
|
||||
BackEdge,
|
||||
ForwardEdge,
|
||||
CrossEdge,
|
||||
}
|
||||
|
||||
impl<V> AdjacencyGraph<V>
|
||||
where
|
||||
V: Hash + Eq + Clone + Debug,
|
||||
{
|
||||
pub fn compute_edge_types(&self) -> HashMap<(&V, &V), EdgeType> {
|
||||
/// To correctly compute the start and end times of the nodes in the
|
||||
/// graph, we need to keep do work before and after the recursion call
|
||||
enum RecurseState<'a, V> {
|
||||
Before(&'a V),
|
||||
BeforeNeighbor(&'a V, &'a V),
|
||||
AfterNeighbor(&'a V),
|
||||
}
|
||||
|
||||
let mut edge_types = HashMap::new();
|
||||
|
||||
let mut visited = HashSet::new();
|
||||
let mut start_times = HashMap::new();
|
||||
let mut finished_nodes = HashSet::new();
|
||||
|
||||
let mut time = 0;
|
||||
|
||||
let progress_bar = ProgressBar::new(self.nodes().len() as u64);
|
||||
|
||||
for node in self.nodes().iter() {
|
||||
if visited.contains(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut stack = Vec::new();
|
||||
|
||||
stack.push(RecurseState::Before(node));
|
||||
|
||||
while let Some(state) = stack.pop() {
|
||||
match state {
|
||||
RecurseState::Before(node) => {
|
||||
progress_bar.inc(1);
|
||||
visited.insert(node.clone());
|
||||
start_times.insert(node, time);
|
||||
time += 1;
|
||||
|
||||
// this is extremely important that is before the adjacencies to correctly
|
||||
// iterate over the graph
|
||||
|
||||
if let Some(adjacencies) = self.get_adjacencies(node) {
|
||||
for adj in adjacencies {
|
||||
println!("Node: {:?} Adj: {:?}", node, adj,);
|
||||
|
||||
stack.push(RecurseState::AfterNeighbor(node));
|
||||
|
||||
if !visited.contains(adj) {
|
||||
edge_types.insert((node, adj), EdgeType::TreeEdge);
|
||||
stack.push(RecurseState::Before(adj));
|
||||
} else {
|
||||
stack.push(RecurseState::BeforeNeighbor(node, adj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RecurseState::AfterNeighbor(node) => {
|
||||
finished_nodes.insert(node, time);
|
||||
time += 1;
|
||||
}
|
||||
RecurseState::BeforeNeighbor(node, adj) => {
|
||||
let start_time_node = start_times.get(node).unwrap();
|
||||
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 (
|
||||
start_time_node.cmp(start_time_adj),
|
||||
end_time_node.cmp(end_time_adj),
|
||||
) {
|
||||
(Ordering::Less, Ordering::Greater) => {
|
||||
edge_types.insert((node, adj), EdgeType::ForwardEdge);
|
||||
}
|
||||
(Ordering::Greater, Ordering::Less) => {
|
||||
edge_types.insert((node, adj), EdgeType::BackEdge);
|
||||
}
|
||||
_ => {
|
||||
edge_types.insert((node, adj), EdgeType::CrossEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
edge_types
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdjacencyGraph<V>
|
||||
where
|
||||
V: Hash + Eq + Clone,
|
||||
{
|
||||
nodes: HashSet<V>,
|
||||
adjacencies: HashMap<V, HashSet<V>>,
|
||||
}
|
||||
|
||||
pub struct UndirectedGraph<V>
|
||||
where
|
||||
V: Hash + Eq + Clone,
|
||||
{
|
||||
graph: AdjacencyGraph<V>,
|
||||
}
|
||||
|
||||
pub mod algorithms;
|
||||
pub mod edge_types;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compute_edge_types() {
|
||||
let mut g = AdjacencyGraph::new();
|
||||
|
||||
g.add_edge(1, 2);
|
||||
g.add_edge(2, 3);
|
||||
g.add_edge(3, 4);
|
||||
g.add_edge(4, 1);
|
||||
|
||||
let edge_types = g.compute_edge_types();
|
||||
let edge_type_dict =
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue