From f40c03b113a40d0812ff6fa3bbb7b969026b2895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Wed, 6 May 2026 20:28:00 +0200 Subject: [PATCH] Add VertexMap to attach data to vertices --- src/lib.rs | 1 + src/maps.rs | 122 +++++++++++++++++++++++++++++++++++++ src/models/append_graph.rs | 9 +++ src/models/graph.rs | 9 +++ src/traits.rs | 4 ++ tests/dijkstra.rs | 77 +++++++---------------- 6 files changed, 168 insertions(+), 54 deletions(-) create mode 100644 src/maps.rs diff --git a/src/lib.rs b/src/lib.rs index 43bdd7f..4ce7c62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod algorithms; +pub mod maps; pub mod models; pub mod traits; diff --git a/src/maps.rs b/src/maps.rs new file mode 100644 index 0000000..70406d5 --- /dev/null +++ b/src/maps.rs @@ -0,0 +1,122 @@ +use std::ops::{Index, IndexMut}; + +use crate::traits::GraphTopology; + +pub struct VertexMap { + data: Vec, + default: T, + to_index: fn(V) -> usize, +} + +impl VertexMap { + pub fn new(default: T, to_index: fn(V) -> usize, capacity: usize) -> Self { + Self { + data: vec![default.clone(); capacity], + default, + to_index, + } + } + + pub fn sync>(&mut self, graph: &G) { + let capacity = graph.vertex_capacity(); + if capacity > self.data.len() { + self.data.resize(capacity, self.default.clone()); + } + } +} + +impl Index for VertexMap { + type Output = T; + + fn index(&self, v: V) -> &T { + let i = (self.to_index)(v); + if i < self.data.len() { + &self.data[i] + } else { + &self.default + } + } +} + +impl IndexMut for VertexMap { + fn index_mut(&mut self, v: V) -> &mut T { + let i = (self.to_index)(v); + if i >= self.data.len() { + self.data.resize(i + 1, self.default.clone()); + } + &mut self.data[i] + } +} + +#[cfg(test)] +mod tests { + use crate::models::append_graph::AppendGraph; + use crate::traits::GraphTopology; + + #[test] + fn initial_values_are_default() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let map = graph.vertex_map(42); + assert_eq!(map[v1], 42); + assert_eq!(map[v2], 42); + } + + #[test] + fn write_and_read() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let mut map = graph.vertex_map(0); + map[v1] = 7; + assert_eq!(map[v1], 7); + assert_eq!(map[v2], 0); + } + + #[test] + fn lazy_growth_on_read() { + let mut graph = AppendGraph::new(); + graph.add_vertex(); + let map = graph.vertex_map(99); + let v = graph.add_vertex(); + assert_eq!(map[v], 99); + } + + #[test] + fn lazy_growth_on_write() { + let mut graph = AppendGraph::new(); + let v1 = graph.add_vertex(); + let mut map = graph.vertex_map(0); + let v2 = graph.add_vertex(); + map[v2] = 7; + assert_eq!(map[v1], 0); + assert_eq!(map[v2], 7); + } + + #[test] + fn sync_expands_to_new_vertices() { + let mut graph = AppendGraph::new(); + graph.add_vertex(); + let mut map = graph.vertex_map(42); + graph.add_vertex(); + graph.add_vertex(); + assert!( + map.data.len() < graph.vertex_capacity(), + "precondition: map is stale before sync" + ); + map.sync(&graph); + assert_eq!(map.data.len(), graph.vertex_capacity()); + } + + #[test] + fn sync_does_not_overwrite_existing_values() { + let mut graph = AppendGraph::new(); + let v = graph.add_vertex(); + let mut map = graph.vertex_map(0); + map[v] = 5; + graph.add_vertex(); + map.sync(&graph); + assert_eq!(map[v], 5); + } +} diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs index 7ee75c3..83f0127 100644 --- a/src/models/append_graph.rs +++ b/src/models/append_graph.rs @@ -1,3 +1,4 @@ +use crate::maps::VertexMap; use crate::traits::GraphTopology; #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -83,6 +84,14 @@ impl GraphTopology for AppendGraph { self.incidences.len() / 2 } + fn vertex_capacity(&self) -> usize { + self.vertices.len() + } + + fn vertex_map(&self, default: T) -> VertexMap { + VertexMap::new(default, |v| v.0, self.vertex_capacity()) + } + fn degree(&self, v: Self::Vertex) -> usize { self.vertices[v.0].incidence_count } diff --git a/src/models/graph.rs b/src/models/graph.rs index d3110cf..d69372a 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -1,5 +1,6 @@ use typed_generational_arena::{Arena, Index}; +use crate::maps::VertexMap; use crate::traits::{GraphTopology, GraphTopologyDeletion}; type Vertex = Index; @@ -162,6 +163,14 @@ impl GraphTopology for Graph { self.incidences.len() / 2 } + fn vertex_capacity(&self) -> usize { + self.vertices.capacity() + } + + fn vertex_map(&self, default: T) -> VertexMap { + VertexMap::new(default, |v| v.arr_idx(), self.vertex_capacity()) + } + fn degree(&self, v: Self::Vertex) -> usize { self.vertices[v].incidence_count } diff --git a/src/traits.rs b/src/traits.rs index a1eb4f5..515c006 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,3 +1,5 @@ +use crate::maps::VertexMap; + // TODO: Add functions to reserve memory for vertices and edges. // TODO: Add iterator of incident edges for a vertex. // TODO: Add finding incident vertices for an edge. @@ -8,6 +10,8 @@ pub trait GraphTopology { fn vertex_count(&self) -> usize; fn edge_count(&self) -> usize; + fn vertex_capacity(&self) -> usize; + fn vertex_map(&self, default: T) -> VertexMap; fn degree(&self, v: Self::Vertex) -> usize; fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool; fn vertices(&self) -> impl Iterator; diff --git a/tests/dijkstra.rs b/tests/dijkstra.rs index 1884970..d816106 100644 --- a/tests/dijkstra.rs +++ b/tests/dijkstra.rs @@ -8,22 +8,12 @@ fn dijkstra_single_vertex() { let v1 = graph.add_vertex(); let result = algorithms::dijkstra(&graph, v1); assert_eq!( - result.distances.len(), - 1, - "distances count must equal vertex count" - ); - assert_eq!( - result.predecessors.len(), - 1, - "predecessors count must equal vertex count" - ); - assert_eq!( - result.distances[0], + result.distances[v1], Some(0), "unexpected distance of source vertex" ); assert_eq!( - result.predecessors[0], None, + result.predecessors[v1], None, "unexpected predecessor of source vertex", ); } @@ -32,24 +22,23 @@ fn dijkstra_single_vertex() { fn dijkstra_disconnected() { let mut graph = AppendGraph::new(); let v1 = graph.add_vertex(); - graph.add_vertex(); + let v2 = graph.add_vertex(); let result = algorithms::dijkstra(&graph, v1); assert_eq!( - result.distances.len(), - 2, - "distances count must equal vertex count" + result.distances[v1], + Some(0), + "unexpected distance of source vertex" ); assert_eq!( - result.predecessors.len(), - 2, - "predecessors count must equal vertex count" - ); - assert_eq!( - result.distances[1], None, + result.distances[v2], None, "unexpected distance of disconnected vertex" ); assert_eq!( - result.predecessors[0], None, + result.predecessors[v1], None, + "unexpected predecessor of source vertex", + ); + assert_eq!( + result.predecessors[v2], None, "unexpected predecessor of disconnected vertex", ); } @@ -82,26 +71,16 @@ fn dijkstra() { vec![Some(vertices[4])], vec![Some(vertices[5]), Some(vertices[6]), Some(vertices[7])], ]; - assert_eq!( - result.distances.len(), - graph.vertex_count(), - "distances count must equal vertex count" - ); - assert_eq!( - result.predecessors.len(), - graph.vertex_count(), - "predecessors count must equal vertex count" - ); for i in 0..graph.vertex_count() { assert_eq!( - result.distances[i], expected_distances_from_v0[i], + result.distances[vertices[i]], expected_distances_from_v0[i], "unexpected distance from {:?} to {:?}", vertices[0], vertices[i] ); assert!( - expected_predecessors_from_v0[i].contains(&result.predecessors[i]), + expected_predecessors_from_v0[i].contains(&result.predecessors[vertices[i]]), "unexpected predecessor {:?} of {:?}", - result.predecessors[i], + result.predecessors[vertices[i]], vertices[i] ); } @@ -113,12 +92,7 @@ fn dijkstra_distances_single_vertex() { let v1 = graph.add_vertex(); let distances = algorithms::dijkstra_distances(&graph, v1); assert_eq!( - distances.len(), - 1, - "distances count must equal vertex count" - ); - assert_eq!( - distances[0], + distances[v1], Some(0), "unexpected distance of source vertex" ); @@ -128,16 +102,16 @@ fn dijkstra_distances_single_vertex() { fn dijkstra_distances_disconnected() { let mut graph = AppendGraph::new(); let v1 = graph.add_vertex(); - graph.add_vertex(); + let v2 = graph.add_vertex(); let distances = algorithms::dijkstra_distances(&graph, v1); assert_eq!( - distances.len(), - 2, - "distances count must equal vertex count" + distances[v1], + Some(0), + "unexpected distance of source vertex" ); assert_eq!( - distances[1], None, - "unexpected distance of disconnected vertex" + distances[v2], None, + "unexpected distance of disconnected vertex", ); } @@ -157,14 +131,9 @@ fn dijkstra_distances() { Some(3), Some(4), ]; - assert_eq!( - distances.len(), - graph.vertex_count(), - "distances count must equal vertex count" - ); for i in 0..graph.vertex_count() { assert_eq!( - distances[i], expected_distances_from_v0[i], + distances[vertices[i]], expected_distances_from_v0[i], "unexpected distance from {:?} to {:?}", vertices[0], vertices[i] );