18 Commits

Author SHA1 Message Date
warrence c131f520f4 Add crate doc front-page, and update readme 2026-07-14 20:01:08 +02:00
warrence b6c27ea605 Merge branch 'main' into docs 2026-07-14 09:54:17 +02:00
warrence 5b30151010 Merge pull request 'Minor fixes' (#5) from fixes-minor into main
Reviewed-on: #5
2026-07-13 10:58:50 +02:00
warrence 2f549cc506 Update package version 2026-07-13 10:49:25 +02:00
warrence cf31ee2f01 Replace VertexMap/EdgeMap.len with capacity
Rename EdgeMap.len to capacity.
Add VertexMap.capacity and EdgeMap.capacity.
Deprecate VertexMap.len and EdgeMap.len.
2026-07-13 10:48:16 +02:00
warrence e58261ba47 Add BfsImplResult and DfsImplResult as named return structs 2026-07-10 13:12:30 +02:00
warrence 818b37ec15 Add linter warning for missing docs 2026-07-10 10:18:39 +02:00
warrence dd0399dcdd Merge pull request 'Add prelude' (#4) from feature/prelude into main
Reviewed-on: #4
2026-07-07 11:45:41 +02:00
warrence 3c4391ac1d Update package version 2026-07-07 11:44:57 +02:00
warrence bbd9506bbc Add prelude module for common traits 2026-07-07 11:44:17 +02:00
warrence 4b3586b1eb Merge pull request 'Type additions and renames' (#3) from dev into main
Reviewed-on: #3
2026-07-06 19:13:13 +02:00
warrence c8dda39b98 Update package version 2026-07-06 18:50:22 +02:00
warrence 1446103d34 Add concrete type aliases of VertexMap and EdgeMap for Graph and AppendGraph 2026-07-06 18:49:02 +02:00
warrence 266dee783b Rename append_graph::Incidence to Edge to unify public naming 2026-07-06 17:52:42 +02:00
warrence e3a24894f6 Changed graph::Vertex and graph::Edge to be public to simplify Graph usage 2026-07-06 17:51:15 +02:00
warrence 6d2d1de8ea Hide testing macros from docs 2026-07-03 22:49:14 +02:00
warrence 2d400628f7 Re-publish AppendGraph and Graph in models 2026-07-03 21:49:28 +02:00
warrence 6a0c426c77 Rename find_path* algorithms to dfs_find_path* 2026-07-03 21:43:55 +02:00
19 changed files with 313 additions and 78 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "grapherity" name = "grapherity"
version = "0.1.0" version = "0.2.2"
authors = ["Stefan Müller"] authors = ["Stefan Müller"]
edition = "2024" edition = "2024"
rust-version = "1.85.0" rust-version = "1.85.0"
+57 -2
View File
@@ -1,8 +1,63 @@
# grapherity # grapherity
Library for graph models and algorithms. Graph models and algorithms.
This library is still experimental and its API may therefore change frequently. This crate provides data structures to model graphs and to perform algorithms on these data structures. The functionality presented is designed to be easy to use, flexible, and performant.
Currently supported are:
* Undirected graph types `Graph` and `AppendGraph`, built on a flat, index-based adjacency list,
* Maps to freely associate custom data with vertices and edges,
* Connectivity and pathing algorithms: Dijkstra's algorithm, DFS, and BFS in different variants,
* Exposed traits to implement custom graph data structures or algorithms.
For more information, see the [documentation](https://docs.rs/grapherity/latest/grapherity/).
## Example
```
// Brings commonly used traits into scope.
use grapherity::prelude::*;
use grapherity::models::Graph;
use grapherity::algorithms;
let mut graph = Graph::new();
// Creates a map for edge weights with a default weight.
let mut weights = graph.edge_map(1);
// Adds three vertices and two edges.
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let v3 = graph.add_vertex();
graph.add_edge(v1, v2);
let e = graph.add_edge(v2, v3);
// Sets a non-default weight for the second edge.
weights[e] = 5;
// Checks the number of vertices.
assert_eq!(graph.vertex_count(), 3);
// Checks the number of edges.
assert_eq!(graph.edge_count(), 2);
// Checks that v1 and v2 are adjacent.
assert!(graph.are_adjacent(v1, v2));
// Checks that v1 and v3 are not adjacent.
assert!(!graph.are_adjacent(v1, v3));
// Checks the sum of all vertex degrees.
let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
assert_eq!(sum, 1 + 2 + 1);
// Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
assert_eq!(result.distances[v3], Some(1 + 5));
assert_eq!(result.predecessors[v3], Some(v2));
// Deletes the middle vertex and its incident edges.
graph.delete_vertex(v2);
// Calls Dijkstra's algorithm again on the now disconnected graph.
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
assert_eq!(result.distances[v3], None);
assert_eq!(result.predecessors[v3], None);
```
## License ## License
+39 -25
View File
@@ -122,12 +122,12 @@ where
G: GraphTopology, G: GraphTopology,
{ {
let mut predecessors = graph.vertex_map(None); let mut predecessors = graph.vertex_map(None);
let (distances, _) = bfs_impl(graph, source, |neighbor, predecessor| { let result = bfs_impl(graph, source, |neighbor, predecessor| {
predecessors[neighbor] = Some(predecessor); predecessors[neighbor] = Some(predecessor);
true true
}); });
BfsResult { BfsResult {
distances, distances: result.distances,
predecessors, predecessors,
} }
} }
@@ -136,7 +136,7 @@ pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Op
where where
G: GraphTopology, G: GraphTopology,
{ {
bfs_impl(graph, source, |_, _| true).0 bfs_impl(graph, source, |_, _| true).distances
} }
pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32> pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32>
@@ -154,14 +154,15 @@ where
if predicate(source) { if predicate(source) {
return Some((source, 0)); return Some((source, 0));
} }
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1 bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
} }
fn bfs_impl<G, F>( struct BfsImplResult<V: Copy> {
graph: &G, distances: VertexMap<V, Option<u32>>,
source: G::Vertex, found: Option<(V, u32)>,
mut on_discover: F, }
) -> (VertexMap<G::Vertex, Option<u32>>, Option<(G::Vertex, u32)>)
fn bfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> BfsImplResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
F: FnMut(G::Vertex, G::Vertex) -> bool, F: FnMut(G::Vertex, G::Vertex) -> bool,
@@ -178,13 +179,19 @@ where
let distance = distances[v].unwrap() + 1; let distance = distances[v].unwrap() + 1;
distances[neighbor] = Some(distance); distances[neighbor] = Some(distance);
if !on_discover(neighbor, v) { if !on_discover(neighbor, v) {
return (distances, Some((neighbor, distance))); return BfsImplResult {
distances,
found: Some((neighbor, distance)),
};
} }
queue.push_back(neighbor); queue.push_back(neighbor);
} }
} }
} }
(distances, None) BfsImplResult {
distances,
found: None,
}
} }
pub struct DfsResult<V: Copy> { pub struct DfsResult<V: Copy> {
@@ -197,12 +204,12 @@ where
G: GraphTopology, G: GraphTopology,
{ {
let mut predecessors = graph.vertex_map(None); let mut predecessors = graph.vertex_map(None);
let (visited, _) = dfs_impl(graph, source, |neighbor, predecessor| { let result = dfs_impl(graph, source, |neighbor, predecessor| {
predecessors[neighbor] = Some(predecessor); predecessors[neighbor] = Some(predecessor);
true true
}); });
DfsResult { DfsResult {
visited, visited: result.visited,
predecessors, predecessors,
} }
} }
@@ -211,7 +218,7 @@ pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool
where where
G: GraphTopology, G: GraphTopology,
{ {
dfs_impl(graph, source, |_, _| true).0 dfs_impl(graph, source, |_, _| true).visited
} }
pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool
@@ -229,14 +236,15 @@ where
if predicate(source) { if predicate(source) {
return Some(source); return Some(source);
} }
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1 dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
} }
fn dfs_impl<G, F>( struct DfsImplResult<V: Copy> {
graph: &G, visited: VertexMap<V, bool>,
source: G::Vertex, found: Option<V>,
mut on_discover: F, }
) -> (VertexMap<G::Vertex, bool>, Option<G::Vertex>)
fn dfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> DfsImplResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
F: FnMut(G::Vertex, G::Vertex) -> bool, F: FnMut(G::Vertex, G::Vertex) -> bool,
@@ -248,7 +256,10 @@ where
while let Some((v, predecessor)) = stack.pop() { while let Some((v, predecessor)) = stack.pop() {
if let Some(p) = predecessor { if let Some(p) = predecessor {
if !on_discover(v, p) { if !on_discover(v, p) {
return (visited, Some(v)); return DfsImplResult {
visited,
found: Some(v),
};
} }
} }
for neighbor in graph.adjacent_vertices(v) { for neighbor in graph.adjacent_vertices(v) {
@@ -258,17 +269,20 @@ where
} }
} }
} }
(visited, None) DfsImplResult {
visited,
found: None,
}
} }
pub fn find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>> pub fn dfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>>
where where
G: GraphTopology, G: GraphTopology,
{ {
find_path_where(graph, source, |v| v == target) dfs_find_path_where(graph, source, |v| v == target)
} }
pub fn find_path_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<Vec<G::Edge>> pub fn dfs_find_path_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<Vec<G::Edge>>
where where
G: GraphTopology, G: GraphTopology,
P: Fn(G::Vertex) -> bool, P: Fn(G::Vertex) -> bool,
+135
View File
@@ -1,6 +1,141 @@
//! Graph models and algorithms.
//!
//! This crate provides data structures to model graphs and to perform algorithms on these data
//! structures. The functionality presented is designed to be easy to use, flexible, and performant.
//!
//! Currently supported are:
//!
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
//! list,
//! * [`VertexMap`] and [`EdgeMap`] to freely associate custom data with vertices and edges,
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
//! variants,
//! * Exposed traits to implement custom graph data structures or algorithms.
//!
//! # Usage example
//!
//! This example demonstrates some of this library's features.
//!
//! ```
//! // Brings commonly used traits into scope.
//! use grapherity::prelude::*;
//! use grapherity::models::Graph;
//! use grapherity::algorithms;
//!
//! let mut graph = Graph::new();
//! // Creates a map for edge weights with a default weight.
//! let mut weights = graph.edge_map(1);
//! // Adds three vertices and two edges.
//! let v1 = graph.add_vertex();
//! let v2 = graph.add_vertex();
//! let v3 = graph.add_vertex();
//! graph.add_edge(v1, v2);
//! let e = graph.add_edge(v2, v3);
//! // Sets a non-default weight for the second edge.
//! weights[e] = 5;
//!
//! // Checks the number of vertices.
//! assert_eq!(graph.vertex_count(), 3);
//! // Checks the number of edges.
//! assert_eq!(graph.edge_count(), 2);
//! // Checks that v1 and v2 are adjacent.
//! assert!(graph.are_adjacent(v1, v2));
//! // Checks that v1 and v3 are not adjacent.
//! assert!(!graph.are_adjacent(v1, v3));
//! // Checks the sum of all vertex degrees.
//! let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
//! assert_eq!(sum, 1 + 2 + 1);
//!
//! // Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
//! assert_eq!(result.distances[v3], Some(1 + 5));
//! assert_eq!(result.predecessors[v3], Some(v2));
//!
//! // Deletes the middle vertex and its incident edges.
//! graph.delete_vertex(v2);
//!
//! // Calls Dijkstra's algorithm again on the now disconnected graph.
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
//! assert_eq!(result.distances[v3], None);
//! assert_eq!(result.predecessors[v3], None);
//! ```
//!
//! # Graph types
//!
//! [`Graph`] and [`AppendGraph`] both implement [`GraphTopology`] using a flat, index-based adjacency
//! list to store edges, which means that vertex and edge insertions happen in `O(1)`. Degree
//! lookups are also `O(1)`.
//!
//! [`Graph`] additionally implements [`GraphTopologyDeletion`], thereby supporting deletion of
//! vertices and edges.
//!
//! [`AppendGraph`] does not support deletion, vertices and edges can only be added, allowing it to
//! be smaller and more performant compared to [`Graph`] by not requiring the per-element generation
//! tracking needed for stable handles after deletion.
//!
//! # Vertices and edges
//!
//! Graphs in this library use stable indices [`GraphTopology::Vertex`] and [`GraphTopology::Edge`]
//! to denote vertices and edges. This means that vertices and edges returned by graph functions
//! never change, even if the underlying graph data structure is mutated. However, vertices and
//! edges can become invalid when the denoted element is deleted. Code using an invalid vertex or
//! edge in a graph method will panic.
//!
//! ```should_panic
//! # use grapherity::prelude::*;
//! # use grapherity::models::Graph;
//! let mut graph = Graph::new();
//! let v = graph.add_vertex();
//! graph.delete_vertex(v);
//! // This will panic because 'v' was deleted and is not valid anymore.
//! graph.degree(v);
//! ```
//!
//! [`VertexMap`] and [`EdgeMap`] are not required to panic if provided with an invalid
//! vertex or edge.
//!
//! Vertices and edges are provided by the graph and not intended to be generated by users.
//!
//! # Loops and multi-edges
//!
//! Loops and multi edges are supported as well. In the following example, `v1` will have two loops
//! and two edges to `v2`.
//!
//! ```
//! # use grapherity::prelude::*;
//! # use grapherity::models::Graph;
//! let mut graph = Graph::new();
//! let v1 = graph.add_vertex();
//! let v2 = graph.add_vertex();
//! for _ in 0..2 {
//! graph.add_edge(v1, v1);
//! graph.add_edge(v1, v2);
//! }
//! assert_eq!(graph.edge_count(), 4);
//! assert_eq!(graph.degree(v1), 6);
//! ```
//!
//! [BFS]: crate::algorithms::bfs
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
//! [DFS]: crate::algorithms::dfs
//! [`EdgeMap`]: crate::maps::EdgeMap
//! [`VertexMap`]: crate::maps::VertexMap
//! [`AppendGraph`]: crate::models::AppendGraph
//! [`Graph`]: crate::models::Graph
//! [`GraphTopology`]: crate::traits::GraphTopology
//! [`GraphTopology::Edge`]: crate::traits::GraphTopology::Edge
//! [`GraphTopology::Vertex`]: crate::traits::GraphTopology::Vertex
//! [`GraphTopologyDeletion`]: crate::traits::GraphTopologyDeletion
#![warn(missing_docs)]
pub mod algorithms; pub mod algorithms;
pub mod maps; pub mod maps;
pub mod models; pub mod models;
pub mod traits; pub mod traits;
pub mod prelude {
pub use crate::traits::{GraphTopology, GraphTopologyDeletion};
}
mod testing; mod testing;
+15 -5
View File
@@ -13,9 +13,14 @@ impl<V: Copy, T: Clone> VertexMap<V, T> {
} }
} }
// Reads beyond len() are valid and return the default value. // Reads beyond 'capacity' are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.inner.len() self.capacity()
} }
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) { pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
@@ -48,9 +53,14 @@ impl<E: Copy, T: Clone> EdgeMap<E, T> {
} }
} }
// Reads beyond len() are valid and return the default value. // Reads beyond 'capacity' are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.inner.len() self.capacity()
} }
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) { pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
@@ -87,7 +97,7 @@ impl<E: Copy, T: Clone> EntityMap<E, T> {
} }
} }
pub fn len(&self) -> usize { pub fn capacity(&self) -> usize {
self.data.len() self.data.len()
} }
+5
View File
@@ -1,2 +1,7 @@
pub mod append_graph; pub mod append_graph;
pub mod graph; pub mod graph;
// TODO: Compressed-sparse-row graph model.
pub use append_graph::{AppendGraph, AppendGraphEdgeMap, AppendGraphVertexMap};
pub use graph::{Graph, GraphEdgeMap, GraphVertexMap};
+16 -13
View File
@@ -5,9 +5,12 @@ use crate::traits::{GraphTopology, IncidenceCursor};
pub struct Vertex(usize); pub struct Vertex(usize);
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Incidence(usize); pub struct Edge(usize);
impl Incidence { pub type AppendGraphVertexMap<T> = VertexMap<Vertex, T>;
pub type AppendGraphEdgeMap<T> = EdgeMap<Edge, T>;
impl Edge {
fn normalize(&self) -> Self { fn normalize(&self) -> Self {
Self(self.0 & !1) Self(self.0 & !1)
} }
@@ -15,22 +18,22 @@ impl Incidence {
struct VertexIncidenceHeader { struct VertexIncidenceHeader {
incidence_count: usize, incidence_count: usize,
first_incidence: Option<Incidence>, first_incidence: Option<Edge>,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct IncidenceEntry { struct IncidenceEntry {
next: Option<Incidence>, next: Option<Edge>,
adjacent: Vertex, adjacent: Vertex,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct AppendGraphIncidenceCursor { pub struct AppendGraphIncidenceCursor {
incidence: Option<Incidence>, incidence: Option<Edge>,
} }
impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor { impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor {
fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Incidence)> { fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Edge)> {
graph.step_incidence(&mut self.incidence) graph.step_incidence(&mut self.incidence)
} }
} }
@@ -56,15 +59,15 @@ impl AppendGraph {
adjacent: v2, adjacent: v2,
}); });
self.vertices[v1.0].incidence_count += 1; self.vertices[v1.0].incidence_count += 1;
self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); self.vertices[v1.0].first_incidence = Some(Edge(self.incidences.len() - 1));
} }
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = (Vertex, Incidence)> { fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = (Vertex, Edge)> {
let mut incidence = self.vertices[v.0].first_incidence; let mut incidence = self.vertices[v.0].first_incidence;
std::iter::from_fn(move || self.step_incidence(&mut incidence)) std::iter::from_fn(move || self.step_incidence(&mut incidence))
} }
fn step_incidence(&self, incidence: &mut Option<Incidence>) -> Option<(Vertex, Incidence)> { fn step_incidence(&self, incidence: &mut Option<Edge>) -> Option<(Vertex, Edge)> {
let current = (*incidence)?; let current = (*incidence)?;
let entry = self.incidences[current.0]; let entry = self.incidences[current.0];
*incidence = entry.next; *incidence = entry.next;
@@ -80,7 +83,7 @@ impl Default for AppendGraph {
impl GraphTopology for AppendGraph { impl GraphTopology for AppendGraph {
type Vertex = Vertex; type Vertex = Vertex;
type Edge = Incidence; type Edge = Edge;
type IncidenceCursor = AppendGraphIncidenceCursor; type IncidenceCursor = AppendGraphIncidenceCursor;
fn vertex_count(&self) -> usize { fn vertex_count(&self) -> usize {
@@ -130,7 +133,7 @@ impl GraphTopology for AppendGraph {
} }
fn edges(&self) -> impl Iterator<Item = Self::Edge> { fn edges(&self) -> impl Iterator<Item = Self::Edge> {
(0..self.incidences.len()).step_by(2).map(Incidence) (0..self.incidences.len()).step_by(2).map(Edge)
} }
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> { fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> {
@@ -158,7 +161,7 @@ impl GraphTopology for AppendGraph {
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge { fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge {
self.add_incidence(v1, v2); self.add_incidence(v1, v2);
self.add_incidence(v2, v1); self.add_incidence(v2, v1);
Incidence(self.incidences.len() - 2) Edge(self.incidences.len() - 2)
} }
} }
@@ -175,7 +178,7 @@ mod tests {
let v1 = graph.add_vertex(); let v1 = graph.add_vertex();
let v2 = graph.add_vertex(); let v2 = graph.add_vertex();
let e = graph.add_edge(v1, v2); let e = graph.add_edge(v1, v2);
let f = Incidence(e.0 + 1); let f = Edge(e.0 + 1);
let (u1, u2) = graph.incident_vertices(f); let (u1, u2) = graph.incident_vertices(f);
assert!( assert!(
(u1 == v1 && u2 == v2) || (u1 == v2 && u2 == v1), (u1 == v1 && u2 == v2) || (u1 == v2 && u2 == v1),
+4 -2
View File
@@ -3,9 +3,11 @@ use typed_generational_arena::{Arena, Index};
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::{EdgeMap, VertexMap};
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
type Vertex = Index<VertexIncidenceHeader, usize, usize>; pub type Vertex = Index<VertexIncidenceHeader, usize, usize>;
pub type Edge = Index<IncidenceEntry, usize, usize>;
type Edge = Index<IncidenceEntry, usize, usize>; pub type GraphVertexMap<T> = VertexMap<Vertex, T>;
pub type GraphEdgeMap<T> = EdgeMap<Edge, T>;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct VertexSlot(usize); struct VertexSlot(usize);
+1
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! bfs_tests { macro_rules! bfs_tests {
($T:ty) => { ($T:ty) => {
+1
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! dfs_tests { macro_rules! dfs_tests {
($T:ty) => { ($T:ty) => {
+1
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! dijkstra_tests { macro_rules! dijkstra_tests {
($T:ty) => { ($T:ty) => {
+9 -8
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! find_path_tests { macro_rules! find_path_tests {
($T:ty) => { ($T:ty) => {
@@ -7,7 +8,7 @@ macro_rules! find_path_tests {
let mut graph = <$T>::new(); let mut graph = <$T>::new();
let v = graph.add_vertex(); let v = graph.add_vertex();
assert_eq!( assert_eq!(
$crate::algorithms::find_path(&graph, v, v), $crate::algorithms::dfs_find_path(&graph, v, v),
Some(vec![]), Some(vec![]),
"path from source to itself should be empty" "path from source to itself should be empty"
); );
@@ -17,7 +18,7 @@ macro_rules! find_path_tests {
fn find_path_disconnected() { fn find_path_disconnected() {
let (graph, vertices) = make_test_graph_disconnected(); let (graph, vertices) = make_test_graph_disconnected();
assert_eq!( assert_eq!(
$crate::algorithms::find_path(&graph, vertices[0], vertices[1]), $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
None, None,
"no path should exist to disconnected vertex" "no path should exist to disconnected vertex"
); );
@@ -30,7 +31,7 @@ macro_rules! find_path_tests {
let v1 = graph.add_vertex(); let v1 = graph.add_vertex();
let v2 = graph.add_vertex(); let v2 = graph.add_vertex();
let e = graph.add_edge(v1, v2); let e = graph.add_edge(v1, v2);
let path = $crate::algorithms::find_path(&graph, v1, v2) let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
.expect("path should exist between adjacent vertices"); .expect("path should exist between adjacent vertices");
assert_eq!( assert_eq!(
path.len(), path.len(),
@@ -43,7 +44,7 @@ macro_rules! find_path_tests {
#[test] #[test]
fn find_path() { fn find_path() {
let (graph, vertices, _, _) = make_test_graph(); let (graph, vertices, _, _) = make_test_graph();
let path = $crate::algorithms::find_path(&graph, vertices[0], vertices[9]) let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
.expect(&format!( .expect(&format!(
"path should exist between connected vertices {:?} and {:?}", "path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9] vertices[0], vertices[9]
@@ -57,7 +58,7 @@ macro_rules! find_path_tests {
let mut graph = <$T>::new(); let mut graph = <$T>::new();
let v = graph.add_vertex(); let v = graph.add_vertex();
assert_eq!( assert_eq!(
$crate::algorithms::find_path_where(&graph, v, |u| u == v), $crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
Some(vec![]), Some(vec![]),
"path from source to itself should be empty" "path from source to itself should be empty"
); );
@@ -67,7 +68,7 @@ macro_rules! find_path_tests {
fn find_path_where_disconnected() { fn find_path_where_disconnected() {
let (graph, vertices) = make_test_graph_disconnected(); let (graph, vertices) = make_test_graph_disconnected();
assert_eq!( assert_eq!(
$crate::algorithms::find_path_where(&graph, vertices[0], |v| v == vertices[1]), $crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
None, None,
"no path should exist to disconnected vertex" "no path should exist to disconnected vertex"
); );
@@ -77,7 +78,7 @@ macro_rules! find_path_tests {
fn find_path_where_no_match() { fn find_path_where_no_match() {
let (graph, vertices, _, _) = make_test_graph(); let (graph, vertices, _, _) = make_test_graph();
assert_eq!( assert_eq!(
$crate::algorithms::find_path_where(&graph, vertices[0], |_| false), $crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
None, None,
"no path should exist when predicate never matches" "no path should exist when predicate never matches"
); );
@@ -87,7 +88,7 @@ macro_rules! find_path_tests {
fn find_path_where() { fn find_path_where() {
let (graph, vertices, _, _) = make_test_graph(); let (graph, vertices, _, _) = make_test_graph();
let path = let path =
$crate::algorithms::find_path_where(&graph, vertices[0], |v| v == vertices[9]) $crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
.expect(&format!( .expect(&format!(
"path should exist between connected vertices {:?} and {:?}", "path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9] vertices[0], vertices[9]
+3
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! graph_topology_test_fixtures { macro_rules! graph_topology_test_fixtures {
($T:ty) => { ($T:ty) => {
@@ -101,6 +102,7 @@ macro_rules! graph_topology_test_fixtures {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! graph_topology_tests { macro_rules! graph_topology_tests {
($T:ty) => { ($T:ty) => {
@@ -748,6 +750,7 @@ macro_rules! graph_topology_tests {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! graph_topology_deletion_tests { macro_rules! graph_topology_deletion_tests {
($T:ty) => { ($T:ty) => {
+14 -10
View File
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! vertex_map_tests { macro_rules! vertex_map_tests {
($T:ty) => { ($T:ty) => {
@@ -52,16 +53,16 @@ macro_rules! vertex_map_tests {
let mut graph = <$T>::new(); let mut graph = <$T>::new();
graph.add_vertex(); graph.add_vertex();
let mut map = graph.vertex_map(42); let mut map = graph.vertex_map(42);
let initial_len = map.len(); let capacity_before = map.capacity();
while graph.vertex_capacity() <= initial_len { while graph.vertex_capacity() <= capacity_before {
graph.add_vertex(); graph.add_vertex();
} }
assert!( assert!(
map.len() < graph.vertex_capacity(), map.capacity() < graph.vertex_capacity(),
"precondition: map is stale before sync" "precondition: map is stale before sync"
); );
map.sync(&graph); map.sync(&graph);
assert_eq!(map.len(), graph.vertex_capacity()); assert_eq!(map.capacity(), graph.vertex_capacity());
} }
#[test] #[test]
@@ -78,6 +79,7 @@ macro_rules! vertex_map_tests {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! vertex_map_deletion_tests { macro_rules! vertex_map_deletion_tests {
($T:ty) => { ($T:ty) => {
@@ -107,7 +109,7 @@ macro_rules! vertex_map_deletion_tests {
let capacity_before = graph.vertex_capacity(); let capacity_before = graph.vertex_capacity();
graph.delete_vertex(v2); graph.delete_vertex(v2);
map.sync(&graph); map.sync(&graph);
assert_eq!(map.len(), capacity_before); assert_eq!(map.capacity(), capacity_before);
assert_eq!(map[v1], 5); assert_eq!(map[v1], 5);
} }
@@ -130,6 +132,7 @@ macro_rules! vertex_map_deletion_tests {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! edge_map_tests { macro_rules! edge_map_tests {
($T:ty) => { ($T:ty) => {
@@ -194,16 +197,16 @@ macro_rules! edge_map_tests {
let v2 = graph.add_vertex(); let v2 = graph.add_vertex();
graph.add_edge(v1, v2); graph.add_edge(v1, v2);
let mut map = graph.edge_map(42); let mut map = graph.edge_map(42);
let initial_len = map.len(); let capacity_before = map.capacity();
while graph.edge_capacity() <= initial_len { while graph.edge_capacity() <= capacity_before {
graph.add_edge(v1, v2); graph.add_edge(v1, v2);
} }
assert!( assert!(
map.len() < graph.edge_capacity(), map.capacity() < graph.edge_capacity(),
"precondition: map is stale before sync" "precondition: map is stale before sync"
); );
map.sync(&graph); map.sync(&graph);
assert_eq!(map.len(), graph.edge_capacity()); assert_eq!(map.capacity(), graph.edge_capacity());
} }
#[test] #[test]
@@ -222,6 +225,7 @@ macro_rules! edge_map_tests {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! edge_map_deletion_tests { macro_rules! edge_map_deletion_tests {
($T:ty) => { ($T:ty) => {
@@ -255,7 +259,7 @@ macro_rules! edge_map_deletion_tests {
let capacity_before = graph.edge_capacity(); let capacity_before = graph.edge_capacity();
graph.delete_edge(e2); graph.delete_edge(e2);
map.sync(&graph); map.sync(&graph);
assert_eq!(map.len(), capacity_before); assert_eq!(map.capacity(), capacity_before);
assert_eq!(map[e1], 5); assert_eq!(map[e1], 5);
} }
+2 -2
View File
@@ -1,12 +1,12 @@
mod append_graph_tests { mod append_graph_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::graph_topology_test_fixtures!(AppendGraph); grapherity::graph_topology_test_fixtures!(AppendGraph);
grapherity::bfs_tests!(AppendGraph); grapherity::bfs_tests!(AppendGraph);
} }
mod graph_tests { mod graph_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::graph_topology_test_fixtures!(Graph); grapherity::graph_topology_test_fixtures!(Graph);
grapherity::bfs_tests!(Graph); grapherity::bfs_tests!(Graph);
+2 -2
View File
@@ -1,12 +1,12 @@
mod append_graph_tests { mod append_graph_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::graph_topology_test_fixtures!(AppendGraph); grapherity::graph_topology_test_fixtures!(AppendGraph);
grapherity::dfs_tests!(AppendGraph); grapherity::dfs_tests!(AppendGraph);
} }
mod graph_tests { mod graph_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::graph_topology_test_fixtures!(Graph); grapherity::graph_topology_test_fixtures!(Graph);
grapherity::dfs_tests!(Graph); grapherity::dfs_tests!(Graph);
+2 -2
View File
@@ -1,12 +1,12 @@
mod append_graph_tests { mod append_graph_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::graph_topology_test_fixtures!(AppendGraph); grapherity::graph_topology_test_fixtures!(AppendGraph);
grapherity::dijkstra_tests!(AppendGraph); grapherity::dijkstra_tests!(AppendGraph);
} }
mod graph_tests { mod graph_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::graph_topology_test_fixtures!(Graph); grapherity::graph_topology_test_fixtures!(Graph);
grapherity::dijkstra_tests!(Graph); grapherity::dijkstra_tests!(Graph);
+2 -2
View File
@@ -1,12 +1,12 @@
mod append_graph_tests { mod append_graph_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::graph_topology_test_fixtures!(AppendGraph); grapherity::graph_topology_test_fixtures!(AppendGraph);
grapherity::find_path_tests!(AppendGraph); grapherity::find_path_tests!(AppendGraph);
} }
mod graph_tests { mod graph_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::graph_topology_test_fixtures!(Graph); grapherity::graph_topology_test_fixtures!(Graph);
grapherity::find_path_tests!(Graph); grapherity::find_path_tests!(Graph);
+4 -4
View File
@@ -1,24 +1,24 @@
mod append_graph_vertex_map_tests { mod append_graph_vertex_map_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::vertex_map_tests!(AppendGraph); grapherity::vertex_map_tests!(AppendGraph);
} }
mod append_graph_edge_map_tests { mod append_graph_edge_map_tests {
use grapherity::models::append_graph::AppendGraph; use grapherity::models::AppendGraph;
grapherity::edge_map_tests!(AppendGraph); grapherity::edge_map_tests!(AppendGraph);
} }
mod graph_vertex_map_tests { mod graph_vertex_map_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::vertex_map_tests!(Graph); grapherity::vertex_map_tests!(Graph);
grapherity::vertex_map_deletion_tests!(Graph); grapherity::vertex_map_deletion_tests!(Graph);
} }
mod graph_edge_map_tests { mod graph_edge_map_tests {
use grapherity::models::graph::Graph; use grapherity::models::Graph;
grapherity::edge_map_tests!(Graph); grapherity::edge_map_tests!(Graph);
grapherity::edge_map_deletion_tests!(Graph); grapherity::edge_map_deletion_tests!(Graph);