Compare commits
16 Commits
0.2.4
...
release-0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 53a6bed932 | |||
| afaf161344 | |||
| 0169343ac8 | |||
| 399b36bb16 | |||
| 3708c1e0d1 | |||
| 9608796f23 | |||
| c253e4a840 | |||
| d711e68577 | |||
| 8db7a86187 | |||
| ad9850800d | |||
| a14202effc | |||
| 913a9e0baa | |||
| 8c6c98859c | |||
| 24c08438d6 | |||
| bf6a7142f3 | |||
| fb9dfe8280 |
+24
-22
@@ -31,7 +31,7 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::{BinaryHeap, VecDeque};
|
use std::collections::{BinaryHeap, VecDeque};
|
||||||
|
|
||||||
use crate::maps::VertexMap;
|
use crate::maps::EntityMap;
|
||||||
use crate::traits::{GraphTopology, IncidenceCursor};
|
use crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
@@ -55,16 +55,17 @@ impl<V: Eq> Ord for DistanceOrderedVertex<V> {
|
|||||||
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
||||||
pub struct DijkstraResult<V: Copy> {
|
pub struct DijkstraResult<V: Copy> {
|
||||||
/// Vertex map of minimum distances from a given `source` vertex.
|
/// Vertex map of minimum distances from a given `source` vertex.
|
||||||
pub distances: VertexMap<V, Option<u32>>,
|
pub distances: EntityMap<V, Option<u32>>,
|
||||||
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
||||||
pub predecessors: VertexMap<V, Option<V>>,
|
pub predecessors: EntityMap<V, Option<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Generalize the return type of the weight function.
|
// TODO: Generalize the return type of the weight function.
|
||||||
|
// TODO: Add complexity information for Dijkstra's algorithm variants.
|
||||||
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
||||||
///
|
///
|
||||||
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
||||||
/// by `weight` function. Returns the distances from `source` to each vertex, and the predecessors
|
/// by `weights` function. Returns the distances from `source` to each vertex, and the predecessors
|
||||||
/// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
|
/// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
|
||||||
/// not connected to `source`.
|
/// not connected to `source`.
|
||||||
///
|
///
|
||||||
@@ -91,13 +92,13 @@ pub struct DijkstraResult<V: Copy> {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
||||||
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weight: W) -> DijkstraResult<G::Vertex>
|
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weights: W) -> DijkstraResult<G::Vertex>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Fn(G::Edge) -> u32,
|
||||||
{
|
{
|
||||||
let mut predecessors = graph.vertex_map(None);
|
let mut predecessors = graph.vertex_map(None);
|
||||||
let distances = dijkstra_impl(graph, source, weight, |adjacent, predecessor| {
|
let distances = dijkstra_impl(graph, source, weights, |adjacent, predecessor| {
|
||||||
predecessors[adjacent] = Some(predecessor);
|
predecessors[adjacent] = Some(predecessor);
|
||||||
});
|
});
|
||||||
DijkstraResult {
|
DijkstraResult {
|
||||||
@@ -109,7 +110,7 @@ where
|
|||||||
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances.
|
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances.
|
||||||
///
|
///
|
||||||
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
|
||||||
/// by `weight` function. Returns the distances from `source` to each vertex. Returns `None` for any
|
/// by `weights` function. Returns the distances from `source` to each vertex. Returns `None` for any
|
||||||
/// vertex not connected to `source`.
|
/// vertex not connected to `source`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@@ -137,13 +138,13 @@ where
|
|||||||
pub fn dijkstra_distances<G, W>(
|
pub fn dijkstra_distances<G, W>(
|
||||||
graph: &G,
|
graph: &G,
|
||||||
source: G::Vertex,
|
source: G::Vertex,
|
||||||
weight: W,
|
weights: W,
|
||||||
) -> VertexMap<G::Vertex, Option<u32>>
|
) -> EntityMap<G::Vertex, Option<u32>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Fn(G::Edge) -> u32,
|
||||||
{
|
{
|
||||||
dijkstra_impl(graph, source, weight, |_, _| {})
|
dijkstra_impl(graph, source, weights, |_, _| {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors.
|
/// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors.
|
||||||
@@ -213,7 +214,7 @@ where
|
|||||||
pub fn dijkstra_distances_unweighted<G>(
|
pub fn dijkstra_distances_unweighted<G>(
|
||||||
graph: &G,
|
graph: &G,
|
||||||
source: G::Vertex,
|
source: G::Vertex,
|
||||||
) -> VertexMap<G::Vertex, Option<u32>>
|
) -> EntityMap<G::Vertex, Option<u32>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
@@ -223,9 +224,9 @@ where
|
|||||||
fn dijkstra_impl<G, W, F>(
|
fn dijkstra_impl<G, W, F>(
|
||||||
graph: &G,
|
graph: &G,
|
||||||
source: G::Vertex,
|
source: G::Vertex,
|
||||||
weight: W,
|
weights: W,
|
||||||
mut on_relax: F,
|
mut on_relax: F,
|
||||||
) -> VertexMap<G::Vertex, Option<u32>>
|
) -> EntityMap<G::Vertex, Option<u32>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Fn(G::Edge) -> u32,
|
||||||
@@ -242,7 +243,7 @@ where
|
|||||||
|
|
||||||
while let Some(v) = heap.pop() {
|
while let Some(v) = heap.pop() {
|
||||||
for incidence in graph.incidences(v.vertex) {
|
for incidence in graph.incidences(v.vertex) {
|
||||||
let new_distance = distances[v.vertex].unwrap() + weight(incidence.1);
|
let new_distance = distances[v.vertex].unwrap() + weights(incidence.1);
|
||||||
if match distances[incidence.0] {
|
if match distances[incidence.0] {
|
||||||
None => true,
|
None => true,
|
||||||
Some(old_distance) if old_distance > new_distance => true,
|
Some(old_distance) if old_distance > new_distance => true,
|
||||||
@@ -263,9 +264,9 @@ where
|
|||||||
/// Return data type for [`bfs`].
|
/// Return data type for [`bfs`].
|
||||||
pub struct BfsResult<V: Copy> {
|
pub struct BfsResult<V: Copy> {
|
||||||
/// Vertex map of minimum distances from a given `source` vertex.
|
/// Vertex map of minimum distances from a given `source` vertex.
|
||||||
pub distances: VertexMap<V, Option<u32>>,
|
pub distances: EntityMap<V, Option<u32>>,
|
||||||
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
||||||
pub predecessors: VertexMap<V, Option<V>>,
|
pub predecessors: EntityMap<V, Option<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Breadth-first search] traversal from `source`, returns distances and predecessors.
|
/// [Breadth-first search] traversal from `source`, returns distances and predecessors.
|
||||||
@@ -341,7 +342,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
|
||||||
pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Option<u32>>
|
pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> EntityMap<G::Vertex, Option<u32>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
@@ -423,7 +424,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct BfsImplResult<V: Copy> {
|
struct BfsImplResult<V: Copy> {
|
||||||
distances: VertexMap<V, Option<u32>>,
|
distances: EntityMap<V, Option<u32>>,
|
||||||
found: Option<(V, u32)>,
|
found: Option<(V, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,12 +460,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: 'visited' is already encoded in 'predecessors', except for 'source', which is visited, but has no predecessor.
|
||||||
/// Return data type for [`dfs`].
|
/// Return data type for [`dfs`].
|
||||||
pub struct DfsResult<V: Copy> {
|
pub struct DfsResult<V: Copy> {
|
||||||
/// Vertex map indicating which vertices were visited during the search.
|
/// Vertex map indicating which vertices were visited during the search.
|
||||||
pub visited: VertexMap<V, bool>,
|
pub visited: EntityMap<V, bool>,
|
||||||
/// Vertex map of predecessors on the DFS tree path from a given `source` vertex.
|
/// Vertex map of predecessors on the DFS tree path from a given `source` vertex.
|
||||||
pub predecessors: VertexMap<V, Option<V>>,
|
pub predecessors: EntityMap<V, Option<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Depth-first search] traversal from `source`, returns visited vertices and predecessors.
|
/// [Depth-first search] traversal from `source`, returns visited vertices and predecessors.
|
||||||
@@ -540,7 +542,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
|
||||||
pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool>
|
pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> EntityMap<G::Vertex, bool>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
@@ -620,7 +622,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DfsImplResult<V: Copy> {
|
struct DfsImplResult<V: Copy> {
|
||||||
visited: VertexMap<V, bool>,
|
visited: EntityMap<V, bool>,
|
||||||
found: Option<V>,
|
found: Option<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
use crate::traits::GraphTopology;
|
||||||
|
|
||||||
|
pub fn petersen<G>(
|
||||||
|
graph: &mut G,
|
||||||
|
) -> (
|
||||||
|
[<G as GraphTopology>::Vertex; 10],
|
||||||
|
[<G as GraphTopology>::Edge; 15],
|
||||||
|
)
|
||||||
|
where
|
||||||
|
G: GraphTopology,
|
||||||
|
{
|
||||||
|
const N: usize = 5;
|
||||||
|
const K: usize = 2;
|
||||||
|
|
||||||
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
||||||
|
let edges = core::array::from_fn(|j| {
|
||||||
|
let i = j / 3;
|
||||||
|
match j % 3 {
|
||||||
|
0 => {
|
||||||
|
let outer = if i < N - 1 { i + 1 } else { 0 };
|
||||||
|
graph.add_edge(vertices[i], vertices[outer])
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let inner = if i < N - K { i + N + K } else { i + K };
|
||||||
|
graph.add_edge(vertices[i + N], vertices[inner])
|
||||||
|
}
|
||||||
|
_ => graph.add_edge(vertices[i], vertices[i + N]),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(vertices, edges)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::models::AppendGraph;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn petersen() {
|
||||||
|
let mut graph = AppendGraph::new();
|
||||||
|
let (vertices, edges) = super::petersen(&mut graph);
|
||||||
|
assert_eq!(
|
||||||
|
graph.vertex_count(),
|
||||||
|
10,
|
||||||
|
"unexpected number of added vertices"
|
||||||
|
);
|
||||||
|
assert_eq!(graph.edge_count(), 15, "unexpected number of added edges");
|
||||||
|
assert!(
|
||||||
|
graph.vertices().all(|v| graph.degree(v) == 3),
|
||||||
|
"all vertices should have degree 3"
|
||||||
|
);
|
||||||
|
assert_handles_in_graph(&graph, &vertices, &edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_handles_in_graph<G: GraphTopology>(
|
||||||
|
graph: &G,
|
||||||
|
vertices: &[G::Vertex],
|
||||||
|
edges: &[G::Edge],
|
||||||
|
) {
|
||||||
|
assert!(
|
||||||
|
vertices.iter().all(|&v| graph.vertices().any(|u| u == v)),
|
||||||
|
"all returned vertices should be in the graph"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
edges.iter().all(|&e| graph.edges().any(|f| f == e)),
|
||||||
|
"all returned edges should be in the graph"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -7,7 +7,7 @@
|
|||||||
//!
|
//!
|
||||||
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
|
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
|
||||||
//! list,
|
//! list,
|
||||||
//! * [`VertexMap`] and [`EdgeMap`] to freely associate custom data with vertices and edges,
|
//! * [`EntityMap`] to freely associate custom data with vertices and edges,
|
||||||
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
|
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
|
||||||
//! variants,
|
//! variants,
|
||||||
//! * Exposed traits to implement custom graph data structures or algorithms.
|
//! * Exposed traits to implement custom graph data structures or algorithms.
|
||||||
@@ -118,8 +118,7 @@
|
|||||||
//! [BFS]: crate::algorithms::bfs
|
//! [BFS]: crate::algorithms::bfs
|
||||||
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
|
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
|
||||||
//! [DFS]: crate::algorithms::dfs
|
//! [DFS]: crate::algorithms::dfs
|
||||||
//! [`EdgeMap`]: crate::maps::EdgeMap
|
//! [`EntityMap`]: crate::maps::EntityMap
|
||||||
//! [`VertexMap`]: crate::maps::VertexMap
|
|
||||||
//! [`AppendGraph`]: crate::models::AppendGraph
|
//! [`AppendGraph`]: crate::models::AppendGraph
|
||||||
//! [`Graph`]: crate::models::Graph
|
//! [`Graph`]: crate::models::Graph
|
||||||
//! [`GraphTopology`]: crate::traits::GraphTopology
|
//! [`GraphTopology`]: crate::traits::GraphTopology
|
||||||
@@ -130,13 +129,14 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod algorithms;
|
pub mod algorithms;
|
||||||
|
pub mod generators;
|
||||||
pub mod maps;
|
pub mod maps;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
|
||||||
/// Convenience re-exports of graph topology traits for common use.
|
/// Convenience re-exports of graph topology traits for common use.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::traits::{GraphTopology, GraphTopologyDeletion};
|
pub use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
||||||
}
|
}
|
||||||
|
|
||||||
mod testing;
|
mod testing;
|
||||||
|
|||||||
+23
-140
@@ -1,156 +1,32 @@
|
|||||||
//! Provides maps to associate custom data to graph vertices and edges.
|
//! Provides maps to associate custom data to graph vertices and edges.
|
||||||
//!
|
//!
|
||||||
//! [`VertexMap`] and [`EdgeMap`] provide copy-on-write, [`Vec`]-backed maps to associate data to
|
//! [`EntityMap`] provides a copy-on-write, [`Vec`]-backed map to associate data to either all
|
||||||
//! all vertices or all edges in a graph, respectively.
|
//! vertices or all edges in a graph.
|
||||||
|
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use crate::traits::GraphTopology;
|
/// A map to associate custom data to graph vertices or edges.
|
||||||
|
|
||||||
/// A map to associate custom data to graph vertices.
|
|
||||||
///
|
///
|
||||||
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph
|
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph
|
||||||
/// vertices. The implementation uses a [`Vec`], allocating contiguous slots for the data, which
|
/// vertices or edges. The implementation uses a [`Vec`], allocating contiguous slots for the data,
|
||||||
/// means that the provided index conversion function should map vertices to contiguous indices, or
|
/// which means that the provided index conversion function should map entities to contiguous
|
||||||
/// indices with relatively few gaps.
|
/// indices, or indices with relatively few gaps.
|
||||||
///
|
///
|
||||||
/// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value
|
/// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value is
|
||||||
/// beyond current capacity is written, but the `default` value can transparently be read. No bound
|
/// written beyond current capacity, but the `default` value can transparently be read. No bound or
|
||||||
/// or validity checks are performed by the map on the provided vertex handles.
|
/// validity checks are performed by the map on the provided entity handles.
|
||||||
///
|
///
|
||||||
/// Use [`GraphTopology::vertex_map`] to obtain a `VertexMap`.
|
/// Use [`GraphTopology::vertex_map`] or [`GraphTopology::edge_map`] to obtain an `EntityMap` for
|
||||||
pub struct VertexMap<V: Copy, T: Clone> {
|
/// vertices or edges, respectively.
|
||||||
inner: EntityMap<V, T>,
|
pub struct EntityMap<E: Copy, T: Clone> {
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Copy, T: Clone> VertexMap<V, T> {
|
|
||||||
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
|
|
||||||
/// and an initial `capacity`.
|
|
||||||
pub fn new(default: T, to_index: fn(V) -> usize, capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: EntityMap::new(default, to_index, capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the total number of data entries the map can write without reallocating. 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")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.capacity()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands the internal data storage capacity of the map to `capacity`. Does nothing if
|
|
||||||
/// capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// Use this before writing data for new graph vertices to avoid incremental growth on the first
|
|
||||||
/// write to each new vertex.
|
|
||||||
pub fn expand(&mut self, capacity: usize) {
|
|
||||||
self.inner.expand(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(
|
|
||||||
since = "0.2.4",
|
|
||||||
note = "use 'expand(graph.vertex_capacity())' instead"
|
|
||||||
)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
|
|
||||||
self.inner.expand(graph.vertex_capacity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Copy, T: Clone> Index<V> for VertexMap<V, T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, v: V) -> &T {
|
|
||||||
&self.inner[v]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Copy, T: Clone> IndexMut<V> for VertexMap<V, T> {
|
|
||||||
fn index_mut(&mut self, v: V) -> &mut T {
|
|
||||||
&mut self.inner[v]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A map to associate custom data to graph edges.
|
|
||||||
///
|
|
||||||
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph edges.
|
|
||||||
/// The implementation uses a [`Vec`], allocating contiguous slots for the data, which means that
|
|
||||||
/// the provided index conversion function should map edges to contiguous indices, or indices with
|
|
||||||
/// relatively few gaps.
|
|
||||||
///
|
|
||||||
/// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value
|
|
||||||
/// beyond current capacity is written, but the `default` value can transparently be read. No bound
|
|
||||||
/// or validity checks are performed by the map on the provided edge handles.
|
|
||||||
///
|
|
||||||
/// Use [`GraphTopology::edge_map`] to obtain an `EdgeMap`.
|
|
||||||
pub struct EdgeMap<E: Copy, T: Clone> {
|
|
||||||
inner: EntityMap<E, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Copy, T: Clone> EdgeMap<E, T> {
|
|
||||||
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
|
|
||||||
/// and an initial `capacity`.
|
|
||||||
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: EntityMap::new(default, to_index, capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the total number of data entries the map can write without reallocating. 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")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.capacity()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expands the internal data storage capacity of the map to `capacity`. Does nothing if
|
|
||||||
/// capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// Use this before writing data for new graph edges to avoid incremental growth on the first
|
|
||||||
/// write to each new edge.
|
|
||||||
pub fn expand(&mut self, capacity: usize) {
|
|
||||||
self.inner.expand(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "0.2.4", note = "use 'expand(graph.edge_capacity())' instead")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
|
|
||||||
self.inner.expand(graph.edge_capacity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Copy, T: Clone> Index<E> for EdgeMap<E, T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, e: E) -> &T {
|
|
||||||
&self.inner[e]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Copy, T: Clone> IndexMut<E> for EdgeMap<E, T> {
|
|
||||||
fn index_mut(&mut self, e: E) -> &mut T {
|
|
||||||
&mut self.inner[e]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EntityMap<E: Copy, T: Clone> {
|
|
||||||
data: Vec<T>,
|
data: Vec<T>,
|
||||||
default: T,
|
default: T,
|
||||||
to_index: fn(E) -> usize,
|
to_index: fn(E) -> usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Copy, T: Clone> EntityMap<E, T> {
|
impl<E: Copy, T: Clone> EntityMap<E, T> {
|
||||||
|
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
|
||||||
|
/// and an initial `capacity`.
|
||||||
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
|
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: vec![default.clone(); capacity],
|
data: vec![default.clone(); capacity],
|
||||||
@@ -159,12 +35,19 @@ impl<E: Copy, T: Clone> EntityMap<E, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the total number of data entries the map can write without reallocating. Reads
|
||||||
|
/// beyond `capacity` are valid and return the default value.
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.data.len()
|
self.data.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands the internal data storage capacity of the map to `capacity`, Does nothing if
|
||||||
|
/// capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Use this before writing data for new graph entities to avoid incremental growth on the first
|
||||||
|
/// write to each new entity.
|
||||||
pub fn expand(&mut self, capacity: usize) {
|
pub fn expand(&mut self, capacity: usize) {
|
||||||
if capacity > self.data.capacity() {
|
if capacity > self.data.len() {
|
||||||
self.data.resize(capacity, self.default.clone());
|
self.data.resize(capacity, self.default.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! [`AppendGraph`], an undirected graph topology supporting addition only.
|
//! [`AppendGraph`], an undirected graph topology supporting addition only.
|
||||||
|
|
||||||
use crate::maps::{EdgeMap, VertexMap};
|
use crate::maps::EntityMap;
|
||||||
use crate::traits::{GraphTopology, IncidenceCursor};
|
use crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
|
||||||
/// An opaque handle identifying a vertex in an [`AppendGraph`].
|
/// An opaque handle identifying a vertex in an [`AppendGraph`].
|
||||||
@@ -17,11 +17,11 @@ pub struct Vertex(usize);
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Edge(usize);
|
pub struct Edge(usize);
|
||||||
|
|
||||||
/// A [`VertexMap`] for [`AppendGraph`] vertices.
|
/// An [`EntityMap`] for [`AppendGraph`] vertices.
|
||||||
pub type AppendGraphVertexMap<T> = VertexMap<Vertex, T>;
|
pub type AppendGraphVertexMap<T> = EntityMap<Vertex, T>;
|
||||||
|
|
||||||
/// An [`EdgeMap`] for [`AppendGraph`] edges.
|
/// An [`EntityMap`] for [`AppendGraph`] edges.
|
||||||
pub type AppendGraphEdgeMap<T> = EdgeMap<Edge, T>;
|
pub type AppendGraphEdgeMap<T> = EntityMap<Edge, T>;
|
||||||
|
|
||||||
impl Edge {
|
impl Edge {
|
||||||
fn normalize(&self) -> Self {
|
fn normalize(&self) -> Self {
|
||||||
@@ -145,8 +145,8 @@ impl GraphTopology for AppendGraph {
|
|||||||
self.vertices.capacity()
|
self.vertices.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
|
||||||
VertexMap::new(default, |v| v.0, self.vertex_capacity())
|
EntityMap::new(default, |v| v.0, self.vertex_capacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge_count(&self) -> usize {
|
fn edge_count(&self) -> usize {
|
||||||
@@ -157,8 +157,8 @@ impl GraphTopology for AppendGraph {
|
|||||||
self.incidences.capacity() / 2
|
self.incidences.capacity() / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> {
|
fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
|
||||||
EdgeMap::new(default, |e| e.0 / 2, self.edge_capacity())
|
EntityMap::new(default, |e| e.0 / 2, self.edge_capacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn degree(&self, v: Self::Vertex) -> usize {
|
fn degree(&self, v: Self::Vertex) -> usize {
|
||||||
|
|||||||
+9
-9
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use typed_generational_arena::{Arena, Index};
|
use typed_generational_arena::{Arena, Index};
|
||||||
|
|
||||||
use crate::maps::{EdgeMap, VertexMap};
|
use crate::maps::EntityMap;
|
||||||
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
||||||
|
|
||||||
/// An opaque handle identifying a vertex in a [`Graph`].
|
/// An opaque handle identifying a vertex in a [`Graph`].
|
||||||
@@ -17,11 +17,11 @@ pub type Vertex = Index<VertexIncidenceHeader, usize, usize>;
|
|||||||
/// deleted. Obtain via graph methods like [`Graph::add_edge`] and [`Graph::edges`].
|
/// deleted. Obtain via graph methods like [`Graph::add_edge`] and [`Graph::edges`].
|
||||||
pub type Edge = Index<IncidenceEntry, usize, usize>;
|
pub type Edge = Index<IncidenceEntry, usize, usize>;
|
||||||
|
|
||||||
/// A [`VertexMap`] for [`Graph`] vertices.
|
/// An [`EntityMap`] for [`Graph`] vertices.
|
||||||
pub type GraphVertexMap<T> = VertexMap<Vertex, T>;
|
pub type GraphVertexMap<T> = EntityMap<Vertex, T>;
|
||||||
|
|
||||||
/// An [`EdgeMap`] for [`Graph`] edges.
|
/// An [`EntityMap`] for [`Graph`] edges.
|
||||||
pub type GraphEdgeMap<T> = EdgeMap<Edge, T>;
|
pub type GraphEdgeMap<T> = EntityMap<Edge, T>;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
struct VertexSlot(usize);
|
struct VertexSlot(usize);
|
||||||
@@ -240,8 +240,8 @@ impl GraphTopology for Graph {
|
|||||||
self.vertices.capacity()
|
self.vertices.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
|
||||||
VertexMap::new(default, |v| v.arr_idx(), self.vertex_capacity())
|
EntityMap::new(default, |v| v.arr_idx(), self.vertex_capacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge_count(&self) -> usize {
|
fn edge_count(&self) -> usize {
|
||||||
@@ -252,8 +252,8 @@ impl GraphTopology for Graph {
|
|||||||
self.incidences.capacity() / 2
|
self.incidences.capacity() / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> {
|
fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
|
||||||
EdgeMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity())
|
EntityMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn degree(&self, v: Self::Vertex) -> usize {
|
fn degree(&self, v: Self::Vertex) -> usize {
|
||||||
|
|||||||
@@ -3,6 +3,5 @@
|
|||||||
pub(crate) mod bfs_testing;
|
pub(crate) mod bfs_testing;
|
||||||
pub(crate) mod dfs_testing;
|
pub(crate) mod dfs_testing;
|
||||||
pub(crate) mod dijkstra_testing;
|
pub(crate) mod dijkstra_testing;
|
||||||
pub(crate) mod find_path_testing;
|
|
||||||
pub(crate) mod graph_topology_testing;
|
pub(crate) mod graph_topology_testing;
|
||||||
pub(crate) mod maps_testing;
|
pub(crate) mod maps_testing;
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ macro_rules! bfs_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_bfs_distances(
|
fn assert_bfs_distances(
|
||||||
distances: &$crate::maps::VertexMap<
|
distances: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<u32>,
|
Option<u32>,
|
||||||
>,
|
>,
|
||||||
@@ -222,7 +222,7 @@ macro_rules! bfs_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_bfs_predecessors(
|
fn assert_bfs_predecessors(
|
||||||
predecessors: &$crate::maps::VertexMap<
|
predecessors: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
||||||
>,
|
>,
|
||||||
|
|||||||
+122
-3
@@ -163,7 +163,7 @@ macro_rules! dfs_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_dfs_visited(
|
fn assert_dfs_visited(
|
||||||
visited: &$crate::maps::VertexMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
visited: &$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
||||||
vertices: &[<$T as $crate::traits::GraphTopology>::Vertex],
|
vertices: &[<$T as $crate::traits::GraphTopology>::Vertex],
|
||||||
) {
|
) {
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
@@ -177,8 +177,8 @@ macro_rules! dfs_tests {
|
|||||||
|
|
||||||
fn assert_dfs_predecessors(
|
fn assert_dfs_predecessors(
|
||||||
graph: &$T,
|
graph: &$T,
|
||||||
visited: &$crate::maps::VertexMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
visited: &$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
||||||
predecessors: &$crate::maps::VertexMap<
|
predecessors: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
||||||
>,
|
>,
|
||||||
@@ -202,5 +202,124 @@ macro_rules! dfs_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_source_equals_target() {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v = graph.add_vertex();
|
||||||
|
assert_eq!(
|
||||||
|
$crate::algorithms::dfs_find_path(&graph, v, v),
|
||||||
|
Some(vec![]),
|
||||||
|
"path from source to itself should be empty"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_disconnected() {
|
||||||
|
let (graph, vertices) = make_test_graph_disconnected();
|
||||||
|
assert_eq!(
|
||||||
|
$crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
|
||||||
|
None,
|
||||||
|
"no path should exist to disconnected vertex"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_adjacent() {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v1 = graph.add_vertex();
|
||||||
|
let v2 = graph.add_vertex();
|
||||||
|
let e = graph.add_edge(v1, v2);
|
||||||
|
let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
|
||||||
|
.expect("path should exist between adjacent vertices");
|
||||||
|
assert_eq!(
|
||||||
|
path.len(),
|
||||||
|
1,
|
||||||
|
"unexpected path length between adjacent vertices"
|
||||||
|
);
|
||||||
|
assert_eq!(path[0], e, "path should use the connecting edge");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path() {
|
||||||
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
|
let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
|
||||||
|
.expect(&format!(
|
||||||
|
"path should exist between connected vertices {:?} and {:?}",
|
||||||
|
vertices[0], vertices[9]
|
||||||
|
));
|
||||||
|
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_where_source_matches() {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v = graph.add_vertex();
|
||||||
|
assert_eq!(
|
||||||
|
$crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
|
||||||
|
Some(vec![]),
|
||||||
|
"path from source to itself should be empty"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_where_disconnected() {
|
||||||
|
let (graph, vertices) = make_test_graph_disconnected();
|
||||||
|
assert_eq!(
|
||||||
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
|
||||||
|
None,
|
||||||
|
"no path should exist to disconnected vertex"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_where_no_match() {
|
||||||
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
|
assert_eq!(
|
||||||
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
|
||||||
|
None,
|
||||||
|
"no path should exist when predicate never matches"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dfs_find_path_where() {
|
||||||
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
|
let path =
|
||||||
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
|
||||||
|
.expect(&format!(
|
||||||
|
"path should exist between connected vertices {:?} and {:?}",
|
||||||
|
vertices[0], vertices[9]
|
||||||
|
));
|
||||||
|
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_valid_path(
|
||||||
|
graph: &$T,
|
||||||
|
path: &[<$T as $crate::traits::GraphTopology>::Edge],
|
||||||
|
source: <$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
|
target: <$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
|
) {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
assert!(!path.is_empty(), "path should be non-empty");
|
||||||
|
// Walks the path: tracks current vertex, confirm each edge is incident to it.
|
||||||
|
let mut current = source;
|
||||||
|
for (i, &e) in path.iter().enumerate() {
|
||||||
|
let (v1, v2) = graph.incident_vertices(e);
|
||||||
|
assert_ne!(v1, v2, "path should not contain loop edge {e:?}");
|
||||||
|
assert!(
|
||||||
|
v1 == current || v2 == current,
|
||||||
|
"path edge {e:?} (index {i}, from {v1:?} to {v2:?}) is not incident to current path vertex {current:?}"
|
||||||
|
);
|
||||||
|
current = if v1 == current { v2 } else { v1 };
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
current, target,
|
||||||
|
"path should end at target {target:?}, but ended at {current:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ macro_rules! dijkstra_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_distances_single_vertex(
|
fn assert_distances_single_vertex(
|
||||||
distances: &$crate::maps::VertexMap<
|
distances: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<u32>,
|
Option<u32>,
|
||||||
>,
|
>,
|
||||||
@@ -160,7 +160,7 @@ macro_rules! dijkstra_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_distances_disconnected(
|
fn assert_distances_disconnected(
|
||||||
distances: &$crate::maps::VertexMap<
|
distances: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<u32>,
|
Option<u32>,
|
||||||
>,
|
>,
|
||||||
@@ -204,7 +204,7 @@ macro_rules! dijkstra_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_distances_test_graph(
|
fn assert_distances_test_graph(
|
||||||
distances: &$crate::maps::VertexMap<
|
distances: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<u32>,
|
Option<u32>,
|
||||||
>,
|
>,
|
||||||
@@ -261,7 +261,7 @@ macro_rules! dijkstra_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_distances_unweighted_test_graph(
|
fn assert_distances_unweighted_test_graph(
|
||||||
distances: &$crate::maps::VertexMap<
|
distances: &$crate::maps::EntityMap<
|
||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
Option<u32>,
|
Option<u32>,
|
||||||
>,
|
>,
|
||||||
@@ -300,7 +300,7 @@ macro_rules! dijkstra_tests {
|
|||||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
<$T as $crate::traits::GraphTopology>::Edge,
|
<$T as $crate::traits::GraphTopology>::Edge,
|
||||||
)>; 10],
|
)>; 10],
|
||||||
$crate::maps::EdgeMap<<$T as $crate::traits::GraphTopology>::Edge, u32>,
|
$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Edge, u32>,
|
||||||
) {
|
) {
|
||||||
use $crate::traits::GraphTopology;
|
use $crate::traits::GraphTopology;
|
||||||
let (graph, vertices, edges, incidences) = make_test_graph();
|
let (graph, vertices, edges, incidences) = make_test_graph();
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! find_path_tests {
|
|
||||||
($T:ty) => {
|
|
||||||
#[test]
|
|
||||||
fn find_path_source_equals_target() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v = graph.add_vertex();
|
|
||||||
assert_eq!(
|
|
||||||
$crate::algorithms::dfs_find_path(&graph, v, v),
|
|
||||||
Some(vec![]),
|
|
||||||
"path from source to itself should be empty"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_disconnected() {
|
|
||||||
let (graph, vertices) = make_test_graph_disconnected();
|
|
||||||
assert_eq!(
|
|
||||||
$crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
|
|
||||||
None,
|
|
||||||
"no path should exist to disconnected vertex"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_adjacent() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e = graph.add_edge(v1, v2);
|
|
||||||
let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
|
|
||||||
.expect("path should exist between adjacent vertices");
|
|
||||||
assert_eq!(
|
|
||||||
path.len(),
|
|
||||||
1,
|
|
||||||
"unexpected path length between adjacent vertices"
|
|
||||||
);
|
|
||||||
assert_eq!(path[0], e, "path should use the connecting edge");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path() {
|
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
|
||||||
let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
|
|
||||||
.expect(&format!(
|
|
||||||
"path should exist between connected vertices {:?} and {:?}",
|
|
||||||
vertices[0], vertices[9]
|
|
||||||
));
|
|
||||||
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_where_source_matches() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v = graph.add_vertex();
|
|
||||||
assert_eq!(
|
|
||||||
$crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
|
|
||||||
Some(vec![]),
|
|
||||||
"path from source to itself should be empty"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_where_disconnected() {
|
|
||||||
let (graph, vertices) = make_test_graph_disconnected();
|
|
||||||
assert_eq!(
|
|
||||||
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
|
|
||||||
None,
|
|
||||||
"no path should exist to disconnected vertex"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_where_no_match() {
|
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
|
||||||
assert_eq!(
|
|
||||||
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
|
|
||||||
None,
|
|
||||||
"no path should exist when predicate never matches"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_path_where() {
|
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
|
||||||
let path =
|
|
||||||
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
|
|
||||||
.expect(&format!(
|
|
||||||
"path should exist between connected vertices {:?} and {:?}",
|
|
||||||
vertices[0], vertices[9]
|
|
||||||
));
|
|
||||||
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_valid_path(
|
|
||||||
graph: &$T,
|
|
||||||
path: &[<$T as $crate::traits::GraphTopology>::Edge],
|
|
||||||
source: <$T as $crate::traits::GraphTopology>::Vertex,
|
|
||||||
target: <$T as $crate::traits::GraphTopology>::Vertex,
|
|
||||||
) {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
assert!(!path.is_empty(), "path should be non-empty");
|
|
||||||
// Walks the path: tracks current vertex, confirm each edge is incident to it.
|
|
||||||
let mut current = source;
|
|
||||||
for (i, &e) in path.iter().enumerate() {
|
|
||||||
let (v1, v2) = graph.incident_vertices(e);
|
|
||||||
assert_ne!(v1, v2, "path should not contain loop edge {e:?}");
|
|
||||||
assert!(
|
|
||||||
v1 == current || v2 == current,
|
|
||||||
"path edge {e:?} (index {i}, from {v1:?} to {v2:?}) is not incident to current path vertex {current:?}"
|
|
||||||
);
|
|
||||||
current = if v1 == current { v2 } else { v1 };
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
current, target,
|
|
||||||
"path should end at target {target:?}, but ended at {current:?}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -128,6 +128,19 @@ macro_rules! graph_topology_tests {
|
|||||||
assert_eq!(graph.vertex_count(), 10, "unexpected vertex count");
|
assert_eq!(graph.vertex_count(), 10, "unexpected vertex count");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vertex_map() {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v1 = graph.add_vertex();
|
||||||
|
let mut map = graph.vertex_map(27);
|
||||||
|
assert_eq!(map[v1], 27, "unexpected value from default map read");
|
||||||
|
map[v1] = 9;
|
||||||
|
assert_eq!(map[v1], 9, "unexpected value from map after write");
|
||||||
|
let v2 = graph.add_vertex();
|
||||||
|
assert_eq!(map[v2], 27, "unexpected value from default map read for new vertex");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_edge() {
|
fn add_edge() {
|
||||||
use $crate::traits::GraphTopology;
|
use $crate::traits::GraphTopology;
|
||||||
@@ -152,6 +165,21 @@ macro_rules! graph_topology_tests {
|
|||||||
assert_eq!(graph.edge_count(), 18, "unexpected edge count");
|
assert_eq!(graph.edge_count(), 18, "unexpected edge count");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn edge_map() {
|
||||||
|
use $crate::traits::GraphTopology;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v1 = graph.add_vertex();
|
||||||
|
let v2 = graph.add_vertex();
|
||||||
|
let e1 = graph.add_edge(v1, v2);
|
||||||
|
let mut map = graph.edge_map(27);
|
||||||
|
assert_eq!(map[e1], 27, "unexpected value from default map read");
|
||||||
|
map[e1] = 9;
|
||||||
|
assert_eq!(map[e1], 9, "unexpected value from map after write");
|
||||||
|
let e2 = graph.add_edge(v1, v2);
|
||||||
|
assert_eq!(map[e2], 27, "unexpected value from default map read for new vertex");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn degree_zero() {
|
fn degree_zero() {
|
||||||
use $crate::traits::GraphTopology;
|
use $crate::traits::GraphTopology;
|
||||||
|
|||||||
+3
-137
@@ -1,6 +1,6 @@
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vertex_map_tests {
|
macro_rules! entity_map_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn initial_values_are_default() {
|
fn initial_values_are_default() {
|
||||||
@@ -81,7 +81,7 @@ macro_rules! vertex_map_tests {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vertex_map_deletion_tests {
|
macro_rules! entity_map_deletion_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn surviving_vertex_readable_after_delete() {
|
fn surviving_vertex_readable_after_delete() {
|
||||||
@@ -108,144 +108,10 @@ macro_rules! vertex_map_deletion_tests {
|
|||||||
map[v1] = 99;
|
map[v1] = 99;
|
||||||
graph.delete_vertex(v1);
|
graph.delete_vertex(v1);
|
||||||
let v2 = graph.add_vertex();
|
let v2 = graph.add_vertex();
|
||||||
// VertexMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot
|
// EntityMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot
|
||||||
// of previously deleted v1 sees the old value. Callers must reinitialize stale slots
|
// of previously deleted v1 sees the old value. Callers must reinitialize stale slots
|
||||||
// after deletion.
|
// after deletion.
|
||||||
assert_eq!(map[v2], 99);
|
assert_eq!(map[v2], 99);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! edge_map_tests {
|
|
||||||
($T:ty) => {
|
|
||||||
#[test]
|
|
||||||
fn initial_values_are_default() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e1 = graph.add_edge(v1, v2);
|
|
||||||
let e2 = graph.add_edge(v1, v2);
|
|
||||||
let map = graph.edge_map(42);
|
|
||||||
assert_eq!(map[e1], 42);
|
|
||||||
assert_eq!(map[e2], 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_and_read() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e1 = graph.add_edge(v1, v2);
|
|
||||||
let e2 = graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(0);
|
|
||||||
map[e1] = 7;
|
|
||||||
assert_eq!(map[e1], 7);
|
|
||||||
assert_eq!(map[e2], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lazy_growth_on_read() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
graph.add_edge(v1, v2);
|
|
||||||
let map = graph.edge_map(99);
|
|
||||||
let e = graph.add_edge(v1, v2);
|
|
||||||
assert_eq!(map[e], 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lazy_growth_on_write() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e1 = graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(0);
|
|
||||||
let e2 = graph.add_edge(v1, v2);
|
|
||||||
map[e2] = 7;
|
|
||||||
assert_eq!(map[e1], 0);
|
|
||||||
assert_eq!(map[e2], 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expand_to_new_edges() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(42);
|
|
||||||
let capacity_before = map.capacity();
|
|
||||||
while graph.edge_capacity() <= capacity_before {
|
|
||||||
graph.add_edge(v1, v2);
|
|
||||||
}
|
|
||||||
assert!(
|
|
||||||
map.capacity() < graph.edge_capacity(),
|
|
||||||
"precondition: map is stale before expand"
|
|
||||||
);
|
|
||||||
map.expand(graph.edge_capacity());
|
|
||||||
assert_eq!(map.capacity(), graph.edge_capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expand_does_not_overwrite_existing_values() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e = graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(0);
|
|
||||||
map[e] = 5;
|
|
||||||
graph.add_edge(v1, v2);
|
|
||||||
map.expand(graph.edge_capacity());
|
|
||||||
assert_eq!(map[e], 5);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! edge_map_deletion_tests {
|
|
||||||
($T:ty) => {
|
|
||||||
#[test]
|
|
||||||
fn surviving_edge_readable_after_delete() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
use $crate::traits::GraphTopologyDeletion;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
let e1 = graph.add_edge(v1, v2);
|
|
||||||
let e2 = graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(0);
|
|
||||||
map[e1] = 1;
|
|
||||||
map[e2] = 2;
|
|
||||||
graph.delete_edge(e2);
|
|
||||||
assert_eq!(map[e1], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reused_slot_returns_old_value() {
|
|
||||||
use $crate::traits::GraphTopology;
|
|
||||||
use $crate::traits::GraphTopologyDeletion;
|
|
||||||
let mut graph = <$T>::new();
|
|
||||||
let v1 = graph.add_vertex();
|
|
||||||
let v2 = graph.add_vertex();
|
|
||||||
graph.add_edge(v1, v2);
|
|
||||||
let e1 = graph.add_edge(v1, v2);
|
|
||||||
let mut map = graph.edge_map(0);
|
|
||||||
map[e1] = 99;
|
|
||||||
graph.delete_edge(e1);
|
|
||||||
let e2 = graph.add_edge(v1, v2);
|
|
||||||
// EdgeMap uses raw indices, not edge identity. Because to_index uses arr_idx/2,
|
|
||||||
// both halves of a deleted edge pair map to the same index, so a new edge reusing
|
|
||||||
// either slot sees the old value. Callers must reinitialize stale slots after deletion.
|
|
||||||
assert_eq!(map[e2], 99);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
+11
-8
@@ -1,6 +1,6 @@
|
|||||||
//! Core traits for undirected graph topologies.
|
//! Core traits for undirected graph topologies.
|
||||||
|
|
||||||
use crate::maps::{EdgeMap, VertexMap};
|
use crate::maps::EntityMap;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -41,7 +41,7 @@ pub trait GraphTopology {
|
|||||||
/// when an algorithm needs to pause traversal, perform other graph queries or mutations, and
|
/// 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
|
/// then continue from where it left off. This cursor type can be obtained via
|
||||||
/// [`incidence_cursor`](Self::incidence_cursor).
|
/// [`incidence_cursor`](Self::incidence_cursor).
|
||||||
type IncidenceCursor: IncidenceCursor<Self> + Copy;
|
type IncidenceCursor: IncidenceCursor<Self>;
|
||||||
|
|
||||||
/// Returns the number of vertices in the graph.
|
/// Returns the number of vertices in the graph.
|
||||||
fn vertex_count(&self) -> usize;
|
fn vertex_count(&self) -> usize;
|
||||||
@@ -49,7 +49,8 @@ pub trait GraphTopology {
|
|||||||
/// Returns the total number of vertices the graph can hold without reallocating.
|
/// 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`.
|
/// Creates and returns an [`EntityMap`] for the vertices with every slot initialised to
|
||||||
|
/// `default`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -69,7 +70,7 @@ pub trait GraphTopology {
|
|||||||
/// labels[v2] = "b";
|
/// labels[v2] = "b";
|
||||||
/// assert_eq!(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) -> EntityMap<Self::Vertex, T>;
|
||||||
|
|
||||||
/// Returns the number of edges in the graph.
|
/// Returns the number of edges in the graph.
|
||||||
fn edge_count(&self) -> usize;
|
fn edge_count(&self) -> usize;
|
||||||
@@ -77,7 +78,7 @@ pub trait GraphTopology {
|
|||||||
/// Returns the total number of edges the graph can hold without reallocating.
|
/// 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`.
|
/// Creates and returns an [`EntityMap`] for the edges with every slot initialised to `default`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -99,7 +100,7 @@ pub trait GraphTopology {
|
|||||||
/// weights[e2] = 2;
|
/// weights[e2] = 2;
|
||||||
/// assert_eq!(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) -> EntityMap<Self::Edge, T>;
|
||||||
|
|
||||||
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
|
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
|
||||||
///
|
///
|
||||||
@@ -213,10 +214,13 @@ pub trait GraphTopologyDeletion: GraphTopology {
|
|||||||
|
|
||||||
/// A cursor for traversing the incidences of a vertex one step at a time.
|
/// A cursor for traversing the incidences of a vertex one step at a time.
|
||||||
///
|
///
|
||||||
|
/// Because the cursor is [`Copy`], its state can be saved and restored to replay or branch a
|
||||||
|
/// traversal.
|
||||||
|
///
|
||||||
/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
|
/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
|
||||||
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
|
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
|
||||||
/// [`GraphTopology::incidences`].
|
/// [`GraphTopology::incidences`].
|
||||||
pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
|
pub trait IncidenceCursor<G: GraphTopology + ?Sized>: Copy {
|
||||||
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
|
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
|
||||||
/// traversal is exhausted.
|
/// traversal is exhausted.
|
||||||
///
|
///
|
||||||
@@ -225,7 +229,6 @@ pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
|
|||||||
/// ```
|
/// ```
|
||||||
/// # use grapherity::prelude::*;
|
/// # use grapherity::prelude::*;
|
||||||
/// # use grapherity::models::Graph;
|
/// # use grapherity::models::Graph;
|
||||||
/// # use grapherity::traits::IncidenceCursor;
|
|
||||||
/// // Constructs a graph with two vertices connected to `v`.
|
/// // Constructs a graph with two vertices connected to `v`.
|
||||||
/// let mut graph = Graph::new();
|
/// let mut graph = Graph::new();
|
||||||
/// let v = graph.add_vertex();
|
/// let v = graph.add_vertex();
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
mod append_graph_tests {
|
|
||||||
use grapherity::models::AppendGraph;
|
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
|
||||||
grapherity::find_path_tests!(AppendGraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
mod graph_tests {
|
|
||||||
use grapherity::models::Graph;
|
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(Graph);
|
|
||||||
grapherity::find_path_tests!(Graph);
|
|
||||||
}
|
|
||||||
+5
-18
@@ -1,25 +1,12 @@
|
|||||||
mod append_graph_vertex_map_tests {
|
mod append_graph_entity_map_tests {
|
||||||
use grapherity::models::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::vertex_map_tests!(AppendGraph);
|
grapherity::entity_map_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod append_graph_edge_map_tests {
|
mod graph_entity_map_tests {
|
||||||
use grapherity::models::AppendGraph;
|
|
||||||
|
|
||||||
grapherity::edge_map_tests!(AppendGraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
mod graph_vertex_map_tests {
|
|
||||||
use grapherity::models::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::vertex_map_tests!(Graph);
|
grapherity::entity_map_tests!(Graph);
|
||||||
grapherity::vertex_map_deletion_tests!(Graph);
|
grapherity::entity_map_deletion_tests!(Graph);
|
||||||
}
|
|
||||||
|
|
||||||
mod graph_edge_map_tests {
|
|
||||||
use grapherity::models::Graph;
|
|
||||||
|
|
||||||
grapherity::edge_map_tests!(Graph);
|
|
||||||
grapherity::edge_map_deletion_tests!(Graph);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user