use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
use super::algorithms;
use super::edge::{Edge, EdgeDirection};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Graph<E> {
edges: Vec<Edge<E>>,
}
impl<E> Default for Graph<E> {
fn default() -> Self {
Self { edges: Vec::new() }
}
}
impl<E> Graph<E> {
pub fn new() -> Self {
Self { edges: Vec::new() }
}
pub fn add_edge(&mut self, edge: Edge<E>) {
self.edges.push(edge);
}
pub fn remove_edge(&mut self, source: Uuid, target: Uuid) -> bool
where
E: Clone,
{
let initial_len = self.edges.len();
self.edges.retain(|e| !e.connects(source, target));
self.edges.len() < initial_len
}
pub fn remove_node(&mut self, node_id: Uuid) {
self.edges.retain(|e| !e.involves(node_id));
}
pub fn archive_node(&mut self, node_id: Uuid) {
for edge in &mut self.edges {
if edge.involves(node_id) {
edge.archive();
}
}
}
pub fn unarchive_node(&mut self, node_id: Uuid) {
for edge in &mut self.edges {
if edge.involves(node_id) {
edge.unarchive();
}
}
}
pub fn outgoing(&self, node_id: Uuid) -> Vec<&Edge<E>> {
self.edges.iter().filter(|e| e.source == node_id).collect()
}
pub fn incoming(&self, node_id: Uuid) -> Vec<&Edge<E>> {
self.edges.iter().filter(|e| e.target == node_id).collect()
}
pub fn outgoing_active(&self, node_id: Uuid) -> Vec<&Edge<E>> {
self.edges
.iter()
.filter(|e| e.source == node_id && e.is_active())
.collect()
}
pub fn incoming_active(&self, node_id: Uuid) -> Vec<&Edge<E>> {
self.edges
.iter()
.filter(|e| e.target == node_id && e.is_active())
.collect()
}
pub fn neighbors(&self, node_id: Uuid) -> Vec<Uuid>
where
E: Clone,
{
let mut neighbors = Vec::new();
for edge in &self.edges {
if edge.source == node_id {
neighbors.push(edge.target);
} else if edge.target == node_id {
match edge.direction {
EdgeDirection::Bidirectional => neighbors.push(edge.source),
EdgeDirection::Directed => {}
}
}
}
neighbors
}
pub fn neighbors_active(&self, node_id: Uuid) -> Vec<Uuid>
where
E: Clone,
{
let mut neighbors = Vec::new();
for edge in self.edges.iter().filter(|e| e.is_active()) {
if edge.source == node_id {
neighbors.push(edge.target);
} else if edge.target == node_id {
match edge.direction {
EdgeDirection::Bidirectional => neighbors.push(edge.source),
EdgeDirection::Directed => {}
}
}
}
neighbors
}
pub fn adjacency_list(&self) -> HashMap<Uuid, Vec<Uuid>>
where
E: Clone,
{
let mut adj_list: HashMap<Uuid, Vec<Uuid>> = HashMap::new();
for edge in self.edges.iter().filter(|e| e.is_active()) {
adj_list.entry(edge.source).or_default().push(edge.target);
if edge.direction == EdgeDirection::Bidirectional {
adj_list.entry(edge.target).or_default().push(edge.source);
}
}
adj_list
}
pub fn edges(&self) -> &[Edge<E>] {
&self.edges
}
pub fn active_edges(&self) -> Vec<&Edge<E>> {
self.edges.iter().filter(|e| e.is_active()).collect()
}
pub fn has_edge(&self, source: Uuid, target: Uuid) -> bool
where
E: Clone,
{
self.edges.iter().any(|e| e.connects(source, target))
}
pub fn would_create_cycle(&self, source: Uuid, target: Uuid) -> bool
where
E: Clone,
{
let adj_list = self.adjacency_list();
algorithms::would_create_cycle(&adj_list, source, target)
}
pub fn has_cycle(&self) -> bool
where
E: Clone,
{
let adj_list = self.adjacency_list();
algorithms::has_cycle(&adj_list)
}
pub fn reachable_from(&self, start: Uuid) -> std::collections::HashSet<Uuid>
where
E: Clone,
{
let adj_list = self.adjacency_list();
algorithms::reachable_from(&adj_list, start)
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
pub fn active_edge_count(&self) -> usize {
self.edges.iter().filter(|e| e.is_active()).count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::edge::EdgeDirection;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
enum TestEdgeType {
TypeA,
TypeB,
}
#[test]
fn test_graph_creation() {
let graph: Graph<TestEdgeType> = Graph::new();
assert_eq!(graph.edge_count(), 0);
}
#[test]
fn test_add_edge() {
let mut graph = Graph::new();
let source = Uuid::new_v4();
let target = Uuid::new_v4();
let edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
graph.add_edge(edge);
assert_eq!(graph.edge_count(), 1);
assert!(graph.has_edge(source, target));
}
#[test]
fn test_remove_edge() {
let mut graph = Graph::new();
let source = Uuid::new_v4();
let target = Uuid::new_v4();
let edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
graph.add_edge(edge);
assert!(graph.remove_edge(source, target));
assert_eq!(graph.edge_count(), 0);
assert!(!graph.has_edge(source, target));
}
#[test]
fn test_remove_node() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_b,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_c,
node_a,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
assert_eq!(graph.edge_count(), 3);
graph.remove_node(node_b);
assert_eq!(graph.edge_count(), 1);
assert!(graph.has_edge(node_c, node_a));
assert!(!graph.has_edge(node_a, node_b));
assert!(!graph.has_edge(node_b, node_c));
}
#[test]
fn test_archive_node() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_b,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
assert_eq!(graph.active_edge_count(), 2);
graph.archive_node(node_b);
assert_eq!(graph.edge_count(), 2);
assert_eq!(graph.active_edge_count(), 0);
}
#[test]
fn test_unarchive_node() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.archive_node(node_a);
assert_eq!(graph.active_edge_count(), 0);
graph.unarchive_node(node_a);
assert_eq!(graph.active_edge_count(), 1);
}
#[test]
fn test_outgoing_incoming() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_a,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_c,
node_a,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
assert_eq!(graph.outgoing(node_a).len(), 2);
assert_eq!(graph.incoming(node_a).len(), 1);
assert_eq!(graph.outgoing(node_b).len(), 0);
assert_eq!(graph.incoming(node_b).len(), 1);
}
#[test]
fn test_neighbors_directed() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_a,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
let neighbors = graph.neighbors(node_a);
assert_eq!(neighbors.len(), 2);
assert!(neighbors.contains(&node_b));
assert!(neighbors.contains(&node_c));
assert_eq!(graph.neighbors(node_b).len(), 0);
}
#[test]
fn test_neighbors_bidirectional() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Bidirectional,
));
let neighbors_a = graph.neighbors(node_a);
let neighbors_b = graph.neighbors(node_b);
assert_eq!(neighbors_a.len(), 1);
assert_eq!(neighbors_b.len(), 1);
assert!(neighbors_a.contains(&node_b));
assert!(neighbors_b.contains(&node_a));
}
#[test]
fn test_would_create_cycle() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_b,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
assert!(graph.would_create_cycle(node_c, node_a));
assert!(graph.would_create_cycle(node_c, node_b));
}
#[test]
fn test_adjacency_list() {
let mut graph = Graph::new();
let node_a = Uuid::new_v4();
let node_b = Uuid::new_v4();
let node_c = Uuid::new_v4();
graph.add_edge(Edge::new(
node_a,
node_b,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
graph.add_edge(Edge::new(
node_b,
node_c,
TestEdgeType::TypeA,
EdgeDirection::Directed,
));
let adj_list = graph.adjacency_list();
assert_eq!(adj_list.len(), 2);
assert_eq!(adj_list.get(&node_a).unwrap().len(), 1);
assert_eq!(adj_list.get(&node_b).unwrap().len(), 1);
}
}