use super::error::{IgraphError, IgraphResult};
pub type VertexId = u32;
#[derive(Debug, Clone, Default)]
pub struct Graph {
n: u32,
out_neighbors: Vec<Vec<VertexId>>,
edge_count: usize,
}
impl Graph {
pub fn with_vertices(n: u32) -> Self {
Self {
n,
out_neighbors: vec![Vec::new(); n as usize],
edge_count: 0,
}
}
pub fn vcount(&self) -> u32 {
self.n
}
pub fn ecount(&self) -> usize {
self.edge_count
}
pub fn add_vertices(&mut self, count: u32) -> (VertexId, VertexId) {
let first = self.n;
self.n = self.n.checked_add(count).expect("vertex count overflow");
self.out_neighbors.resize(self.n as usize, Vec::new());
(first, self.n.saturating_sub(1))
}
pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
self.check_vertex(u)?;
self.check_vertex(v)?;
self.out_neighbors[u as usize].push(v);
if u != v {
self.out_neighbors[v as usize].push(u);
}
self.edge_count += 1;
Ok(())
}
pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
where
I: IntoIterator<Item = (VertexId, VertexId)>,
{
for (u, v) in edges {
self.add_edge(u, v)?;
}
Ok(())
}
pub fn neighbors(&self, v: VertexId) -> IgraphResult<&[VertexId]> {
self.check_vertex(v)?;
Ok(&self.out_neighbors[v as usize])
}
pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
self.check_vertex(v)?;
Ok(self.out_neighbors[v as usize].len())
}
fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
if v >= self.n {
return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_counts() {
let g = Graph::with_vertices(0);
assert_eq!(g.vcount(), 0);
assert_eq!(g.ecount(), 0);
}
#[test]
fn add_vertices_then_edges() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
assert_eq!(g.neighbors(1).unwrap(), &[0, 2]);
}
#[test]
fn out_of_range_vertex_errors() {
let mut g = Graph::with_vertices(2);
let err = g.add_edge(0, 5).unwrap_err();
assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
}
#[test]
fn self_loop_counted_once_per_endpoint() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
assert_eq!(g.ecount(), 1);
assert_eq!(g.neighbors(0).unwrap(), &[0]);
}
}