use crate::error::NumRs2Error;
use std::collections::HashMap;
use std::fmt;
pub mod flow;
pub mod shortest_path;
pub mod spanning_tree;
pub mod traversal;
pub use flow::*;
pub use shortest_path::*;
pub use spanning_tree::*;
pub use traversal::*;
pub type NodeId = usize;
pub type EdgeId = usize;
pub type Weight = f64;
pub type GraphResult<T> = Result<T, GraphError>;
#[derive(Debug, Clone)]
pub enum GraphError {
NodeNotFound(NodeId),
EdgeNotFound(EdgeId),
InvalidOperation(String),
CycleDetected(String),
NotConnected(String),
NegativeCycle(String),
InvalidPath(String),
EmptyGraph,
InvalidWeight(String),
}
impl fmt::Display for GraphError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphError::NodeNotFound(id) => write!(f, "Node {} not found in graph", id),
GraphError::EdgeNotFound(id) => write!(f, "Edge {} not found in graph", id),
GraphError::InvalidOperation(msg) => write!(f, "Invalid graph operation: {}", msg),
GraphError::CycleDetected(msg) => write!(f, "Cycle detected: {}", msg),
GraphError::NotConnected(msg) => write!(f, "Graph is not connected: {}", msg),
GraphError::NegativeCycle(msg) => write!(f, "Negative cycle detected: {}", msg),
GraphError::InvalidPath(msg) => write!(f, "Invalid path: {}", msg),
GraphError::EmptyGraph => write!(f, "Graph is empty"),
GraphError::InvalidWeight(msg) => write!(f, "Invalid weight: {}", msg),
}
}
}
impl std::error::Error for GraphError {}
impl From<GraphError> for NumRs2Error {
fn from(err: GraphError) -> Self {
NumRs2Error::InvalidOperation(err.to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Edge {
pub id: EdgeId,
pub from: NodeId,
pub to: NodeId,
pub weight: Weight,
}
impl Edge {
pub fn new(id: EdgeId, from: NodeId, to: NodeId, weight: Weight) -> Self {
Self {
id,
from,
to,
weight,
}
}
}
#[derive(Debug, Clone)]
pub struct Graph {
directed: bool,
adj_list: HashMap<NodeId, Vec<(NodeId, EdgeId)>>,
edges: HashMap<EdgeId, Edge>,
next_node_id: NodeId,
next_edge_id: EdgeId,
node_count: usize,
}
impl Graph {
pub fn new(directed: bool) -> Self {
Self {
directed,
adj_list: HashMap::new(),
edges: HashMap::new(),
next_node_id: 0,
next_edge_id: 0,
node_count: 0,
}
}
pub fn add_node(&mut self) -> NodeId {
let id = self.next_node_id;
self.next_node_id += 1;
self.adj_list.insert(id, Vec::new());
self.node_count += 1;
id
}
pub fn add_edge(&mut self, from: NodeId, to: NodeId, weight: Weight) -> GraphResult<EdgeId> {
if !self.has_node(from) {
return Err(GraphError::NodeNotFound(from));
}
if !self.has_node(to) {
return Err(GraphError::NodeNotFound(to));
}
let edge_id = self.next_edge_id;
self.next_edge_id += 1;
let edge = Edge::new(edge_id, from, to, weight);
self.edges.insert(edge_id, edge);
self.adj_list
.get_mut(&from)
.ok_or(GraphError::NodeNotFound(from))?
.push((to, edge_id));
if !self.directed {
self.adj_list
.get_mut(&to)
.ok_or(GraphError::NodeNotFound(to))?
.push((from, edge_id));
}
Ok(edge_id)
}
pub fn has_node(&self, node: NodeId) -> bool {
self.adj_list.contains_key(&node)
}
pub fn has_edge(&self, edge_id: EdgeId) -> bool {
self.edges.contains_key(&edge_id)
}
pub fn node_count(&self) -> usize {
self.node_count
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
pub fn is_directed(&self) -> bool {
self.directed
}
pub fn nodes(&self) -> Vec<NodeId> {
self.adj_list.keys().copied().collect()
}
pub fn neighbors(&self, node: NodeId) -> GraphResult<&[(NodeId, EdgeId)]> {
self.adj_list
.get(&node)
.map(|v| v.as_slice())
.ok_or(GraphError::NodeNotFound(node))
}
pub fn get_edge(&self, edge_id: EdgeId) -> GraphResult<&Edge> {
self.edges
.get(&edge_id)
.ok_or(GraphError::EdgeNotFound(edge_id))
}
pub fn edges(&self) -> Vec<&Edge> {
self.edges.values().collect()
}
pub fn degree(&self, node: NodeId) -> GraphResult<usize> {
Ok(self.neighbors(node)?.len())
}
pub fn in_degree(&self, node: NodeId) -> GraphResult<usize> {
if !self.has_node(node) {
return Err(GraphError::NodeNotFound(node));
}
if !self.directed {
return self.degree(node);
}
let count = self.edges.values().filter(|e| e.to == node).count();
Ok(count)
}
pub fn out_degree(&self, node: NodeId) -> GraphResult<usize> {
self.degree(node)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_graph_creation() {
let directed = Graph::new(true);
assert!(directed.is_directed());
assert_eq!(directed.node_count(), 0);
assert_eq!(directed.edge_count(), 0);
let undirected = Graph::new(false);
assert!(!undirected.is_directed());
}
#[test]
fn test_add_nodes() {
let mut graph = Graph::new(true);
let n0 = graph.add_node();
let n1 = graph.add_node();
let n2 = graph.add_node();
assert_eq!(n0, 0);
assert_eq!(n1, 1);
assert_eq!(n2, 2);
assert_eq!(graph.node_count(), 3);
assert!(graph.has_node(0));
assert!(graph.has_node(1));
assert!(graph.has_node(2));
assert!(!graph.has_node(3));
}
#[test]
fn test_add_edges_directed() {
let mut graph = Graph::new(true);
let n0 = graph.add_node();
let n1 = graph.add_node();
let e0 = graph
.add_edge(n0, n1, 1.5)
.expect("test: valid edge addition");
assert_eq!(graph.edge_count(), 1);
let edge = graph.get_edge(e0).expect("test: valid edge retrieval");
assert_eq!(edge.from, n0);
assert_eq!(edge.to, n1);
assert_eq!(edge.weight, 1.5);
let neighbors = graph.neighbors(n0).expect("test: valid neighbor retrieval");
assert_eq!(neighbors.len(), 1);
assert_eq!(neighbors[0].0, n1);
}
#[test]
fn test_add_edges_undirected() {
let mut graph = Graph::new(false);
let n0 = graph.add_node();
let n1 = graph.add_node();
graph
.add_edge(n0, n1, 2.0)
.expect("test: valid edge addition");
let neighbors0 = graph.neighbors(n0).expect("test: valid neighbor retrieval");
let neighbors1 = graph.neighbors(n1).expect("test: valid neighbor retrieval");
assert_eq!(neighbors0.len(), 1);
assert_eq!(neighbors1.len(), 1);
assert_eq!(neighbors0[0].0, n1);
assert_eq!(neighbors1[0].0, n0);
}
#[test]
fn test_degree() {
let mut graph = Graph::new(true);
let n0 = graph.add_node();
let n1 = graph.add_node();
let n2 = graph.add_node();
graph
.add_edge(n0, n1, 1.0)
.expect("test: valid edge addition");
graph
.add_edge(n0, n2, 1.0)
.expect("test: valid edge addition");
graph
.add_edge(n1, n2, 1.0)
.expect("test: valid edge addition");
assert_eq!(graph.out_degree(n0).expect("test: valid out-degree"), 2);
assert_eq!(graph.in_degree(n0).expect("test: valid in-degree"), 0);
assert_eq!(graph.out_degree(n1).expect("test: valid out-degree"), 1);
assert_eq!(graph.in_degree(n1).expect("test: valid in-degree"), 1);
assert_eq!(graph.out_degree(n2).expect("test: valid out-degree"), 0);
assert_eq!(graph.in_degree(n2).expect("test: valid in-degree"), 2);
}
#[test]
fn test_error_handling() {
let mut graph = Graph::new(true);
let n0 = graph.add_node();
assert!(graph.add_edge(n0, 999, 1.0).is_err());
assert!(graph.add_edge(999, n0, 1.0).is_err());
assert!(graph.neighbors(999).is_err());
}
}