21 Commits

Author SHA1 Message Date
warrence 8f9f0bf4c8 Add language tag to readme example code for syntax highlighting 2026-07-17 11:21:31 +02:00
warrence 7345684f00 Add documentation for traits module 2026-07-17 11:04:08 +02:00
warrence 2c53d92943 Fix readme typo 2026-07-17 11:00:02 +02:00
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
20 changed files with 523 additions and 79 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"
+58 -3
View File
@@ -1,14 +1,69 @@
# 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
```rust
// 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
This project is dual-licensed under the terms of both: This project is dual-licensed under the terms of both:
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit) - MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit)
You may choose either license for your use of this software. You may choose either license for your use of this software.
+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);
} }
+209
View File
@@ -2,35 +2,244 @@ use crate::maps::{EdgeMap, VertexMap};
// TODO: Add functions to reserve memory for vertices and edges. // TODO: Add functions to reserve memory for vertices and edges.
// TODO: Split out GraphTopologyAddition trait. // TODO: Split out GraphTopologyAddition trait.
/// A trait representing an undirected graph topology.
///
/// An undirected graph is a set of vertices and undirected edges, where each edge connects either
/// exactly two vertices or one vertex with itself (loop edge). This trait provides methods for
/// querying a graph topology, iterating over vertices and edges, and adding new vertices and edges.
///
/// # Vertices and Edges
///
/// Vertices and edges are identified by opaque handles ([`Vertex`] and [`Edge`]) that implement
/// [`Copy`] and [`Eq`]. Handles remain valid for the lifetime of the graph unless the graph also
/// implements [`GraphTopologyDeletion`] and the item identified by the handle is explicitly
/// deleted.
///
/// Methods accepting vertices or edges as parameters panic if the handle was invalidated by a
/// deletion, and return incorrect results if the handle was not produced by this graph instance.
///
/// # Deletion
///
/// This trait covers graph construction and querying only. To delete vertices and edges, see
/// [`GraphTopologyDeletion`].
///
/// [`Edge`]: GraphTopology::Edge
/// [`Vertex`]: GraphTopology::Vertex
pub trait GraphTopology { pub trait GraphTopology {
/// An opaque, stable handle identifying a vertex.
type Vertex: Copy + Eq; type Vertex: Copy + Eq;
/// An opaque, stable handle identifying an edge.
type Edge: Copy + Eq; type Edge: Copy + Eq;
/// A resumable position in the incidence list of a vertex.
///
/// Prefer [`incidences`](Self::incidences) for straightforward iteration. A cursor is useful
/// when an algorithm needs to pause traversal, perform other graph queries or mutations, and
/// then continue from where it left off. This cursor type can be obtained via
/// [`incidence_cursor`](Self::incidence_cursor).
type IncidenceCursor: IncidenceCursor<Self> + Copy; type IncidenceCursor: IncidenceCursor<Self> + Copy;
/// Returns the number of vertices in the graph.
fn vertex_count(&self) -> usize; fn vertex_count(&self) -> usize;
/// Returns the total number of vertices the graph can hold without reallocating.
fn vertex_capacity(&self) -> usize; fn vertex_capacity(&self) -> usize;
/// Creates and returns a [`VertexMap`] with every slot initialised to `default`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let v1 = graph.add_vertex();
/// let mut labels = graph.vertex_map("z");
/// assert_eq!(labels[v1], "z");
/// labels[v1] = "a";
/// assert_eq!(labels[v1], "a");
///
/// // A new vertex is immediately available for read and write.
/// let v2 = graph.add_vertex();
/// assert_eq!(labels[v2], "z");
/// labels[v2] = "b";
/// assert_eq!(labels[v2], "b");
/// ```
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T>; fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T>;
/// Returns the number of edges in the graph.
fn edge_count(&self) -> usize; fn edge_count(&self) -> usize;
/// Returns the total number of edges the graph can hold without reallocating.
fn edge_capacity(&self) -> usize; fn edge_capacity(&self) -> usize;
/// Creates and returns an [`EdgeMap`] with every slot initialised to `default`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let v1 = graph.add_vertex();
/// let v2 = graph.add_vertex();
/// let e1 = graph.add_edge(v1, v2);
/// let mut weights = graph.edge_map(5);
/// assert_eq!(weights[e1], 5);
/// weights[e1] = 1;
/// assert_eq!(weights[e1], 1);
///
/// // A new edge is immediately available for read and write.
/// let e2 = graph.add_edge(v1, v2);
/// assert_eq!(weights[e2], 5);
/// weights[e2] = 2;
/// assert_eq!(weights[e2], 2);
/// ```
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T>; fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T>;
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn degree(&self, v: Self::Vertex) -> usize; fn degree(&self, v: Self::Vertex) -> usize;
/// Returns `true` if there is at least one edge between `v1` and `v2`, and `false` otherwise.
///
/// A vertex is adjacent to itself if and only if it has a loop edge.
///
/// # Panics
///
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool; fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool;
/// Returns an iterator over all vertices in the graph.
fn vertices(&self) -> impl Iterator<Item = Self::Vertex>; fn vertices(&self) -> impl Iterator<Item = Self::Vertex>;
/// Returns an iterator over all vertices adjacent to `v`.
///
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the edges are not
/// required, since implementors may be able to provide an iterator faster than the trivial
/// mapping. The complexity of this method must not exceed that of
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex>; fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex>;
/// Returns the two endpoints of edge `e`, which are identical if and only if `e` is a loop
/// edge.
///
/// # Panics
///
/// Panics if `e` is not a valid edge of this graph.
fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex); fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex);
/// Returns an iterator over all edges in the graph.
fn edges(&self) -> impl Iterator<Item = Self::Edge>; fn edges(&self) -> impl Iterator<Item = Self::Edge>;
/// Returns an iterator over all edges incident to `v`.
///
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the vertices are not
/// required, since implementors may be able to provide an iterator faster than the trivial
/// mapping. The complexity of this method must not exceed that of
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>; fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>;
/// Returns an iterator over all incidences of `v`, i.e. vertex-edge pairs `(u, e)` such that
/// `u` is adjacent to `v` and `e` is an edge between them.
///
/// Use [`incidence_cursor`](Self::incidence_cursor) instead for a traversal that needs to
/// suspend and resume across other state updates.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>; fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>;
/// Returns a cursor over all incidences of `v`, analogously to
/// [`incidences`](Self::incidences), initially positioned before the first one.
///
/// See [`IncidenceCursor`](Self::IncidenceCursor) for when to prefer a cursor over
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor; fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor;
/// Adds a new isolated vertex and returns its handle.
fn add_vertex(&mut self) -> Self::Vertex; fn add_vertex(&mut self) -> Self::Vertex;
/// Adds a new edge between `v1` and `v2` and returns its handle.
///
/// # Panics
///
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
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;
} }
/// A trait that adds deletion operations to an undirected graph topology.
///
/// This trait provides methods for deletion of vertices and edges in an undirected graph. These
/// operations will invalidate handles to all deleted vertices and edges. Methods accepting invalid
/// vertices or edges as parameters panic.
pub trait GraphTopologyDeletion: GraphTopology { pub trait GraphTopologyDeletion: GraphTopology {
/// Deletes the vertex `v` and all its incident edges from the graph. Note that this also
/// invalidates the handles of all edges incident to `v`.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn delete_vertex(&mut self, v: Self::Vertex); fn delete_vertex(&mut self, v: Self::Vertex);
/// Deletes the edge `e` from the graph. This operation only invalidates `e` and no other vertex
/// or edge handles.
///
/// # Panics
///
/// Panics if `e` is not a valid edge of this graph.
fn delete_edge(&mut self, e: Self::Edge); fn delete_edge(&mut self, e: Self::Edge);
} }
/// A cursor for traversing the incidences of a vertex one step at a time.
///
/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
/// [`GraphTopology::incidences`].
pub trait IncidenceCursor<G: GraphTopology + ?Sized> { pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
/// traversal is exhausted.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// // Constructs a graph with two vertices connected to `v`.
/// let mut graph = Graph::new();
/// let v = graph.add_vertex();
/// for _ in 0..2 {
/// let u = graph.add_vertex();
/// graph.add_edge(u, v);
/// }
///
/// // Iterates over the incidences of `v` with a cursor.
/// let mut c1 = graph.incidence_cursor(v);
/// assert!(c1.next(&graph).is_some());
/// let mut c2 = c1;
/// // Continues iteration with original cursor.
/// assert!(c1.next(&graph).is_some());
/// assert!(c1.next(&graph).is_none());
/// // Iterates over the last incidence again with the copied cursor.
/// assert!(c2.next(&graph).is_some());
/// assert!(c2.next(&graph).is_none());
/// ```
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>; fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>;
} }
+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);