From ee26b7a3e09ae030460165b779693713bca5edd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Wed, 22 Apr 2026 16:31:17 +0200 Subject: [PATCH] Move AppendGraph to its own module --- src/models.rs | 396 +------------------------------------ src/models/append_graph.rs | 390 ++++++++++++++++++++++++++++++++++++ src/traits.rs | 2 + tests/dijkstra.rs | 2 +- 4 files changed, 394 insertions(+), 396 deletions(-) create mode 100644 src/models/append_graph.rs diff --git a/src/models.rs b/src/models.rs index 58d0636..e53138e 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,395 +1 @@ -use crate::traits::GraphTopology; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Vertex(usize); - -impl From for usize { - fn from(v: Vertex) -> usize { - v.0 - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Incidence(usize); - -struct VertexIncidenceHeader { - incidence_count: usize, - first_incidence: Option, -} - -struct IncidenceEntry { - next: Option, - neighbor: Vertex, -} -struct VertexNeighborIterator<'a> { - graph: &'a AppendGraph, - incidence: Option, -} - -impl<'a> Iterator for VertexNeighborIterator<'a> { - type Item = Vertex; - - fn next(&mut self) -> Option { - let incidence = self.incidence?; - let entry = &self.graph.incidences[incidence.0]; - self.incidence = entry.next; - Some(entry.neighbor) - } -} - -// TODO: Add functions to reserve memory for vertices and edges. -// TODO: Graph cannot support deleting vertices because of the external indices "Vertex". They could be made stable with e.g. slotmap or generational arena. -// TODO: Add function to delete vertices. -// TODO: Add function to delete edges. -// TODO: Add iterator of edges. -pub struct AppendGraph { - // TODO: Index 'incidence_headers' by a 'Vertex' instead of 'Vertex.0'? - vertices: Vec, - incidences: Vec, -} - -impl AppendGraph { - pub fn new() -> Self { - Self { - vertices: vec![], - incidences: vec![], - } - } - - fn add_incidence(&mut self, v1: Vertex, v2: Vertex) { - self.incidences.push(IncidenceEntry { - next: self.vertices[v1.0].first_incidence.take(), - neighbor: v2, - }); - self.vertices[v1.0].incidence_count += 1; - self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); - } -} - -impl GraphTopology for AppendGraph { - type Vertex = Vertex; - - type Edge = Incidence; - - fn vertex_count(&self) -> usize { - self.vertices.len() - } - - fn edge_count(&self) -> usize { - self.incidences.len() / 2 - } - - fn degree(&self, v: Self::Vertex) -> usize { - self.vertices[v.0].incidence_count - } - - fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool { - self.neighbors(v1).find(|&x| x == v2).is_some() - } - - fn vertices(&self) -> impl Iterator { - (0..self.vertices.len()).map(Vertex) - } - - fn neighbors(&self, v: Self::Vertex) -> impl Iterator { - VertexNeighborIterator { - graph: self, - incidence: self.vertices[v.0].first_incidence, - } - } - - fn add_vertex(&mut self) -> Self::Vertex { - self.vertices.push(VertexIncidenceHeader { - incidence_count: 0, - first_incidence: None, - }); - Vertex(self.vertices.len() - 1) - } - - fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge { - self.add_incidence(v1, v2); - self.add_incidence(v2, v1); - Incidence(self.incidences.len() - 2) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn add_vertex() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert_ne!(graph.add_vertex(), v, "unexpected duplicate vertex"); - } - - #[test] - fn vertex_count_empty() { - let graph = AppendGraph::new(); - assert_eq!(graph.vertex_count(), 0, "unexpected vertex count"); - } - - #[test] - fn vertex_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); - } - - #[test] - fn add_edge() { - let mut graph = AppendGraph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - assert_ne!(graph.add_edge(v1, v2), e, "unexpected duplicate edge"); - } - - #[test] - fn edge_count_empty() { - let graph = AppendGraph::new(); - assert_eq!(graph.edge_count(), 0, "unexpected edge count"); - } - - #[test] - fn edge_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.edge_count(), 14, "unexpected edge count"); - } - - #[test] - fn degree_zero() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert_eq!(graph.degree(v), 0, "unexpected non-zero degree"); - } - - #[test] - fn degree() { - let (graph, vertices) = make_test_graph(); - let expected_degrees = [1, 4, 4, 2, 4, 2, 3, 3, 2, 3]; - for i in 0..graph.vertex_count() { - assert_eq!( - graph.degree(vertices[i]), - expected_degrees[i], - "unexpected degree of {:?}", - vertices[i] - ); - } - } - - #[test] - fn are_adjacent_vertex_self() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert_eq!( - graph.are_adjacent(v, v), - false, - "should not be adjacent to itself" - ); - } - - #[test] - fn are_adjacent_single_edge() { - let mut graph = AppendGraph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - assert!(!graph.are_adjacent(v1, v2), "should not be adjacent"); - assert!(!graph.are_adjacent(v2, v1), "should not be adjacent"); - graph.add_edge(v1, v2); - assert!(graph.are_adjacent(v1, v2), "should be adjacent"); - assert!(graph.are_adjacent(v2, v1), "should be adjacent"); - } - - #[test] - fn are_adjacent() { - let (graph, vertices) = make_test_graph(); - assert!( - graph.are_adjacent(vertices[0], vertices[1]), - "expected {:?} and {:?} to be adjacent", - vertices[0], - vertices[1] - ); - assert!( - graph.are_adjacent(vertices[9], vertices[5]), - "expected {:?} and {:?} to be adjacent", - vertices[0], - vertices[5] - ); - assert!( - !graph.are_adjacent(vertices[9], vertices[3]), - "unexpected adjacency of {:?} and {:?}", - vertices[9], - vertices[3] - ); - - for i in 0..graph.vertex_count() { - let exp = match i { - 2 => continue, - 1 | 4 | 5 | 6 => true, - _ => false, - }; - assert_eq!( - graph.are_adjacent(vertices[2], vertices[i]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[2], - vertices[i] - ); - assert_eq!( - graph.are_adjacent(vertices[i], vertices[2]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[i], - vertices[2] - ); - } - } - - #[test] - fn vertices_empty() { - let graph = AppendGraph::new(); - assert_eq!( - graph.vertices().count(), - 0, - "vertex iterator of empty graph should have no elements" - ); - } - - #[test] - fn vertices() { - let (graph, vertices) = make_test_graph(); - assert_eq!(graph.vertices().count(), 10, "unexpected vertex count"); - - // Expects each vertex to appear exactly once. - for v in graph.vertices() { - assert_eq!( - vertices.iter().filter(|&x| *x == v).count(), - 1, - "unexpected vertex {v:?} from the iterator" - ); - } - } - - #[test] - fn neighbors_empty() { - let mut graph = AppendGraph::new(); - let vertex = graph.add_vertex(); - assert_eq!( - graph.neighbors(vertex).count(), - 0, - "neighbor iterator of vertex with degree 0 should have no elements" - ); - } - - #[test] - fn neighbors() { - let (graph, vertices) = make_test_graph(); - // Checks neighbors of vertex 4. - assert_eq!( - graph.neighbors(vertices[4]).count(), - 4, - "unexpected vertex count" - ); - - // Expects each neighbor to appear exactly once. This will not work if there are multiple edges. - let neighbors: Vec = vec![vertices[1], vertices[2], vertices[7], vertices[8]]; - for v in graph.neighbors(vertices[4]) { - assert_eq!( - neighbors.iter().filter(|&x| *x == v).count(), - 1, - "unexpected neighbor {v:?} of {:?} from the iterator", - vertices[4] - ); - } - } - - #[test] - fn loop_edge() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - graph.add_edge(v, v); - assert_eq!(graph.vertex_count(), 1, "unexpected vertex count"); - assert_eq!(graph.edge_count(), 1, "unexpected edge count"); - assert_eq!(graph.degree(v), 2, "unexpected degree"); - assert_eq!( - graph.are_adjacent(v, v), - true, - "vertex with loop edge should be adjacent to itself" - ); - let mut neighbors = graph.neighbors(v); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself" - ); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself twice" - ); - assert_eq!(neighbors.next(), None, "two many neighbors from iterator"); - } - - #[test] - fn multiple_edges() { - let mult = 3; - let mut graph = AppendGraph::new(); - let vertices = [graph.add_vertex(), graph.add_vertex()]; - for _ in 0..mult { - graph.add_edge(vertices[0], vertices[1]); - } - assert_eq!( - graph.vertex_count(), - vertices.len(), - "unexpected vertex count" - ); - assert_eq!(graph.edge_count(), mult, "unexpected edge count"); - for v in vertices { - assert_eq!(graph.degree(v), mult, "unexpected degree of {v:?}"); - } - assert_eq!( - graph.are_adjacent(vertices[0], vertices[1]), - true, - "should be adjacent" - ); - for i in 0..2 { - let mut neighbors = graph.neighbors(vertices[i]); - for j in 0..mult { - assert_eq!( - neighbors.next(), - Some(vertices[1 - i]), - "neighbor {j} of vertex {:?} should be {:?}", - vertices[i], - vertices[1 - i] - ); - } - assert_eq!( - neighbors.next(), - None, - "two many neighbors of {:?} from iterator", - vertices[i] - ); - } - } - - fn make_test_graph() -> (AppendGraph, [Vertex; 10]) { - let mut graph = AppendGraph::new(); - let vertices: [Vertex; 10] = core::array::from_fn(|_| graph.add_vertex()); - graph.add_edge(vertices[0], vertices[1]); - graph.add_edge(vertices[1], vertices[2]); - graph.add_edge(vertices[1], vertices[3]); - graph.add_edge(vertices[1], vertices[4]); - graph.add_edge(vertices[2], vertices[4]); - graph.add_edge(vertices[2], vertices[5]); - graph.add_edge(vertices[2], vertices[6]); - graph.add_edge(vertices[3], vertices[6]); - graph.add_edge(vertices[4], vertices[7]); - graph.add_edge(vertices[4], vertices[8]); - graph.add_edge(vertices[5], vertices[9]); - graph.add_edge(vertices[6], vertices[9]); - graph.add_edge(vertices[7], vertices[8]); - graph.add_edge(vertices[7], vertices[9]); - (graph, vertices) - } -} +pub mod append_graph; diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs new file mode 100644 index 0000000..d702bed --- /dev/null +++ b/src/models/append_graph.rs @@ -0,0 +1,390 @@ +use crate::traits::GraphTopology; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Vertex(usize); + +impl From for usize { + fn from(v: Vertex) -> usize { + v.0 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Incidence(usize); + +struct VertexIncidenceHeader { + incidence_count: usize, + first_incidence: Option, +} + +struct IncidenceEntry { + next: Option, + neighbor: Vertex, +} +struct VertexNeighborIterator<'a> { + graph: &'a AppendGraph, + incidence: Option, +} + +impl<'a> Iterator for VertexNeighborIterator<'a> { + type Item = Vertex; + + fn next(&mut self) -> Option { + let incidence = self.incidence?; + let entry = &self.graph.incidences[incidence.0]; + self.incidence = entry.next; + Some(entry.neighbor) + } +} + +pub struct AppendGraph { + // TODO: Index 'vertices' by a 'Vertex' instead of 'Vertex.0'? + vertices: Vec, + incidences: Vec, +} + +impl AppendGraph { + pub fn new() -> Self { + Self { + vertices: vec![], + incidences: vec![], + } + } + + fn add_incidence(&mut self, v1: Vertex, v2: Vertex) { + self.incidences.push(IncidenceEntry { + next: self.vertices[v1.0].first_incidence.take(), + neighbor: v2, + }); + self.vertices[v1.0].incidence_count += 1; + self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); + } +} + +impl GraphTopology for AppendGraph { + type Vertex = Vertex; + + type Edge = Incidence; + + fn vertex_count(&self) -> usize { + self.vertices.len() + } + + fn edge_count(&self) -> usize { + self.incidences.len() / 2 + } + + fn degree(&self, v: Self::Vertex) -> usize { + self.vertices[v.0].incidence_count + } + + fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool { + self.neighbors(v1).find(|&x| x == v2).is_some() + } + + fn vertices(&self) -> impl Iterator { + (0..self.vertices.len()).map(Vertex) + } + + fn neighbors(&self, v: Self::Vertex) -> impl Iterator { + VertexNeighborIterator { + graph: self, + incidence: self.vertices[v.0].first_incidence, + } + } + + fn add_vertex(&mut self) -> Self::Vertex { + self.vertices.push(VertexIncidenceHeader { + incidence_count: 0, + first_incidence: None, + }); + Vertex(self.vertices.len() - 1) + } + + fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge { + self.add_incidence(v1, v2); + self.add_incidence(v2, v1); + Incidence(self.incidences.len() - 2) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add_vertex() { + let mut graph = AppendGraph::new(); + let v = graph.add_vertex(); + assert_ne!(graph.add_vertex(), v, "unexpected duplicate vertex"); + } + + #[test] + fn vertex_count_empty() { + let graph = AppendGraph::new(); + assert_eq!(graph.vertex_count(), 0, "unexpected vertex count"); + } + + #[test] + fn vertex_count() { + let (graph, _) = make_test_graph(); + assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); + } + + #[test] + fn add_edge() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let e = graph.add_edge(v1, v2); + assert_ne!(graph.add_edge(v1, v2), e, "unexpected duplicate edge"); + } + + #[test] + fn edge_count_empty() { + let graph = AppendGraph::new(); + assert_eq!(graph.edge_count(), 0, "unexpected edge count"); + } + + #[test] + fn edge_count() { + let (graph, _) = make_test_graph(); + assert_eq!(graph.edge_count(), 14, "unexpected edge count"); + } + + #[test] + fn degree_zero() { + let mut graph = AppendGraph::new(); + let v = graph.add_vertex(); + assert_eq!(graph.degree(v), 0, "unexpected non-zero degree"); + } + + #[test] + fn degree() { + let (graph, vertices) = make_test_graph(); + let expected_degrees = [1, 4, 4, 2, 4, 2, 3, 3, 2, 3]; + for i in 0..graph.vertex_count() { + assert_eq!( + graph.degree(vertices[i]), + expected_degrees[i], + "unexpected degree of {:?}", + vertices[i] + ); + } + } + + #[test] + fn are_adjacent_vertex_self() { + let mut graph = AppendGraph::new(); + let v = graph.add_vertex(); + assert_eq!( + graph.are_adjacent(v, v), + false, + "should not be adjacent to itself" + ); + } + + #[test] + fn are_adjacent_single_edge() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + assert!(!graph.are_adjacent(v1, v2), "should not be adjacent"); + assert!(!graph.are_adjacent(v2, v1), "should not be adjacent"); + graph.add_edge(v1, v2); + assert!(graph.are_adjacent(v1, v2), "should be adjacent"); + assert!(graph.are_adjacent(v2, v1), "should be adjacent"); + } + + #[test] + fn are_adjacent() { + let (graph, vertices) = make_test_graph(); + assert!( + graph.are_adjacent(vertices[0], vertices[1]), + "expected {:?} and {:?} to be adjacent", + vertices[0], + vertices[1] + ); + assert!( + graph.are_adjacent(vertices[9], vertices[5]), + "expected {:?} and {:?} to be adjacent", + vertices[0], + vertices[5] + ); + assert!( + !graph.are_adjacent(vertices[9], vertices[3]), + "unexpected adjacency of {:?} and {:?}", + vertices[9], + vertices[3] + ); + + for i in 0..graph.vertex_count() { + let exp = match i { + 2 => continue, + 1 | 4 | 5 | 6 => true, + _ => false, + }; + assert_eq!( + graph.are_adjacent(vertices[2], vertices[i]), + exp, + "unexpected adjacency of {:?} and {:?}", + vertices[2], + vertices[i] + ); + assert_eq!( + graph.are_adjacent(vertices[i], vertices[2]), + exp, + "unexpected adjacency of {:?} and {:?}", + vertices[i], + vertices[2] + ); + } + } + + #[test] + fn vertices_empty() { + let graph = AppendGraph::new(); + assert_eq!( + graph.vertices().count(), + 0, + "vertex iterator of empty graph should have no elements" + ); + } + + #[test] + fn vertices() { + let (graph, vertices) = make_test_graph(); + assert_eq!(graph.vertices().count(), 10, "unexpected vertex count"); + + // Expects each vertex to appear exactly once. + for v in graph.vertices() { + assert_eq!( + vertices.iter().filter(|&x| *x == v).count(), + 1, + "unexpected vertex {v:?} from the iterator" + ); + } + } + + #[test] + fn neighbors_empty() { + let mut graph = AppendGraph::new(); + let vertex = graph.add_vertex(); + assert_eq!( + graph.neighbors(vertex).count(), + 0, + "neighbor iterator of vertex with degree 0 should have no elements" + ); + } + + #[test] + fn neighbors() { + let (graph, vertices) = make_test_graph(); + // Checks neighbors of vertex 4. + assert_eq!( + graph.neighbors(vertices[4]).count(), + 4, + "unexpected vertex count" + ); + + // Expects each neighbor to appear exactly once. This will not work if there are multiple edges. + let neighbors: Vec = vec![vertices[1], vertices[2], vertices[7], vertices[8]]; + for v in graph.neighbors(vertices[4]) { + assert_eq!( + neighbors.iter().filter(|&x| *x == v).count(), + 1, + "unexpected neighbor {v:?} of {:?} from the iterator", + vertices[4] + ); + } + } + + #[test] + fn loop_edge() { + let mut graph = AppendGraph::new(); + let v = graph.add_vertex(); + graph.add_edge(v, v); + assert_eq!(graph.vertex_count(), 1, "unexpected vertex count"); + assert_eq!(graph.edge_count(), 1, "unexpected edge count"); + assert_eq!(graph.degree(v), 2, "unexpected degree"); + assert_eq!( + graph.are_adjacent(v, v), + true, + "vertex with loop edge should be adjacent to itself" + ); + let mut neighbors = graph.neighbors(v); + assert_eq!( + neighbors.next(), + Some(v), + "vertex should be neighbor of itself" + ); + assert_eq!( + neighbors.next(), + Some(v), + "vertex should be neighbor of itself twice" + ); + assert_eq!(neighbors.next(), None, "two many neighbors from iterator"); + } + + #[test] + fn multiple_edges() { + let mult = 3; + let mut graph = AppendGraph::new(); + let vertices = [graph.add_vertex(), graph.add_vertex()]; + for _ in 0..mult { + graph.add_edge(vertices[0], vertices[1]); + } + assert_eq!( + graph.vertex_count(), + vertices.len(), + "unexpected vertex count" + ); + assert_eq!(graph.edge_count(), mult, "unexpected edge count"); + for v in vertices { + assert_eq!(graph.degree(v), mult, "unexpected degree of {v:?}"); + } + assert_eq!( + graph.are_adjacent(vertices[0], vertices[1]), + true, + "should be adjacent" + ); + for i in 0..2 { + let mut neighbors = graph.neighbors(vertices[i]); + for j in 0..mult { + assert_eq!( + neighbors.next(), + Some(vertices[1 - i]), + "neighbor {j} of vertex {:?} should be {:?}", + vertices[i], + vertices[1 - i] + ); + } + assert_eq!( + neighbors.next(), + None, + "two many neighbors of {:?} from iterator", + vertices[i] + ); + } + } + + fn make_test_graph() -> (AppendGraph, [Vertex; 10]) { + let mut graph = AppendGraph::new(); + let vertices: [Vertex; 10] = core::array::from_fn(|_| graph.add_vertex()); + graph.add_edge(vertices[0], vertices[1]); + graph.add_edge(vertices[1], vertices[2]); + graph.add_edge(vertices[1], vertices[3]); + graph.add_edge(vertices[1], vertices[4]); + graph.add_edge(vertices[2], vertices[4]); + graph.add_edge(vertices[2], vertices[5]); + graph.add_edge(vertices[2], vertices[6]); + graph.add_edge(vertices[3], vertices[6]); + graph.add_edge(vertices[4], vertices[7]); + graph.add_edge(vertices[4], vertices[8]); + graph.add_edge(vertices[5], vertices[9]); + graph.add_edge(vertices[6], vertices[9]); + graph.add_edge(vertices[7], vertices[8]); + graph.add_edge(vertices[7], vertices[9]); + (graph, vertices) + } +} diff --git a/src/traits.rs b/src/traits.rs index d4de040..c86de3d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,3 +1,5 @@ +// TODO: Add functions to reserve memory for vertices and edges. +// TODO: Add iterator of edges. pub trait GraphTopology { type Vertex: Copy + Eq; type Edge; diff --git a/tests/dijkstra.rs b/tests/dijkstra.rs index 76383a4..384a6cb 100644 --- a/tests/dijkstra.rs +++ b/tests/dijkstra.rs @@ -1,5 +1,5 @@ use grapherity::algorithms; -use grapherity::models::AppendGraph; +use grapherity::models::append_graph::AppendGraph; use grapherity::traits::GraphTopology; #[test]