use crate::core::error::Result;
use crate::core::types::{EdgeId, NodeId};
pub trait GraphQuery<A, W> {
fn is_directed(&self) -> bool;
fn is_empty(&self) -> bool;
fn node_count(&self) -> usize;
fn edge_count(&self) -> usize;
fn contains_node(&self, node: NodeId) -> bool;
fn contains_edge(&self, source: NodeId, target: NodeId) -> bool;
fn node_attr(&self, node: NodeId) -> Option<&A>;
fn edge_weight(&self, source: NodeId, target: NodeId) -> Option<&W>;
fn density(&self) -> f64 {
let n = self.node_count();
if n < 2 {
return 0.0;
}
let m = self.edge_count() as f64;
let max_edges = (n * (n - 1)) as f64;
if self.is_directed() {
m / max_edges
} else {
(2.0 * m) / max_edges
}
}
}
pub trait GraphMutate<A, W>: GraphQuery<A, W> {
fn add_node(&mut self, attr: A) -> NodeId;
fn add_edge(&mut self, source: NodeId, target: NodeId, weight: W) -> Result<EdgeId>;
fn update_node(&mut self, node: NodeId, new_attr: A) -> Result<()>;
fn remove_node(&mut self, node: NodeId) -> Result<A>;
fn remove_edge(&mut self, edge: EdgeId) -> Result<W>;
fn clear(&mut self);
}
pub trait GraphTraversal<A, W>: GraphQuery<A, W> {
type NodeIter<'a>: Iterator<Item = NodeId>
where
Self: 'a,
A: 'a,
W: 'a;
type NeighborIter<'a>: Iterator<Item = NodeId>
where
Self: 'a,
A: 'a,
W: 'a;
fn node_ids(&self) -> Self::NodeIter<'_>;
fn neighbors(&self, node: NodeId) -> Self::NeighborIter<'_>;
fn degree(&self, node: NodeId) -> Option<usize>;
fn in_degree(&self, node: NodeId) -> Option<usize>;
fn out_degree(&self, node: NodeId) -> Option<usize>;
}
pub trait GraphBulkOps<A, W>: GraphMutate<A, W> {
fn add_nodes_bulk(&mut self, attributes: &[A]) -> Vec<NodeId>
where
A: Clone;
fn add_edges_bulk(&mut self, edges: &[(NodeId, NodeId, W)]) -> Result<Vec<EdgeId>>
where
W: Clone;
}
pub trait GraphAlgorithms<A, W>: GraphTraversal<A, W> {
fn is_connected(&self) -> bool;
fn is_acyclic(&self) -> bool;
fn validate(&self) -> Result<()>;
}
pub trait WeightedGraph<A, W>: GraphQuery<A, W>
where
W: PartialOrd,
{
fn min_edge_weight(&self) -> Option<&W>;
fn max_edge_weight(&self) -> Option<&W>;
fn total_weight(&self) -> W
where
W: Clone + std::ops::Add<Output = W> + Default;
}
pub trait GraphSerialization<A, W>: GraphQuery<A, W>
where
A: serde::Serialize + serde::de::DeserializeOwned,
W: serde::Serialize + serde::de::DeserializeOwned,
{
fn save_json(&self, path: &str) -> Result<()>;
fn load_json(path: &str) -> Result<Self>
where
Self: Sized;
fn save_binary(&self, path: &str) -> Result<()>;
fn load_binary(path: &str) -> Result<Self>
where
Self: Sized;
}
#[cfg(test)]
mod tests {
use super::*;
struct MockGraph {
node_count: usize,
edge_count: usize,
is_directed: bool,
}
impl<A, W> GraphQuery<A, W> for MockGraph {
fn is_directed(&self) -> bool {
self.is_directed
}
fn is_empty(&self) -> bool {
self.node_count == 0
}
fn node_count(&self) -> usize {
self.node_count
}
fn edge_count(&self) -> usize {
self.edge_count
}
fn contains_node(&self, _node: NodeId) -> bool {
true
}
fn contains_edge(&self, _source: NodeId, _target: NodeId) -> bool {
true
}
fn node_attr(&self, _node: NodeId) -> Option<&A> {
None
}
fn edge_weight(&self, _source: NodeId, _target: NodeId) -> Option<&W> {
None
}
}
#[test]
fn test_mock_graph_query() {
let graph: MockGraph = MockGraph {
node_count: 10,
edge_count: 20,
is_directed: true,
};
assert_eq!(GraphQuery::<i32, f64>::node_count(&graph), 10);
assert_eq!(GraphQuery::<i32, f64>::edge_count(&graph), 20);
assert!(GraphQuery::<i32, f64>::is_directed(&graph));
assert!(!GraphQuery::<i32, f64>::is_empty(&graph));
}
#[test]
fn test_density_calculation() {
let directed_graph: MockGraph = MockGraph {
node_count: 4,
edge_count: 6,
is_directed: true,
};
assert_eq!(GraphQuery::<i32, f64>::density(&directed_graph), 0.5);
let undirected_graph: MockGraph = MockGraph {
node_count: 4,
edge_count: 3,
is_directed: false,
};
assert_eq!(GraphQuery::<i32, f64>::density(&undirected_graph), 0.5);
}
}