Replace VertexMap and EdgeMap with now public EntityMap
This commit is contained in:
+14
-14
@@ -1,7 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BinaryHeap, VecDeque};
|
||||
|
||||
use crate::maps::VertexMap;
|
||||
use crate::maps::EntityMap;
|
||||
use crate::traits::{GraphTopology, IncidenceCursor};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
@@ -23,8 +23,8 @@ impl<V: Eq> Ord for DistanceOrderedVertex<V> {
|
||||
}
|
||||
|
||||
pub struct DijkstraResult<V: Copy> {
|
||||
pub distances: VertexMap<V, Option<u32>>,
|
||||
pub predecessors: VertexMap<V, Option<V>>,
|
||||
pub distances: EntityMap<V, Option<u32>>,
|
||||
pub predecessors: EntityMap<V, Option<V>>,
|
||||
}
|
||||
|
||||
// TODO: Generalize the return type of the weight function.
|
||||
@@ -54,7 +54,7 @@ pub fn dijkstra_distances<G, W>(
|
||||
graph: &G,
|
||||
source: G::Vertex,
|
||||
weight: W,
|
||||
) -> VertexMap<G::Vertex, Option<u32>>
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
W: Fn(G::Edge) -> u32,
|
||||
@@ -65,7 +65,7 @@ where
|
||||
pub fn dijkstra_distances_unweighted<G>(
|
||||
graph: &G,
|
||||
source: G::Vertex,
|
||||
) -> VertexMap<G::Vertex, Option<u32>>
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
{
|
||||
@@ -77,7 +77,7 @@ fn dijkstra_impl<G, W, F>(
|
||||
source: G::Vertex,
|
||||
weight: W,
|
||||
mut on_relax: F,
|
||||
) -> VertexMap<G::Vertex, Option<u32>>
|
||||
) -> EntityMap<G::Vertex, Option<u32>>
|
||||
where
|
||||
G: GraphTopology,
|
||||
W: Fn(G::Edge) -> u32,
|
||||
@@ -113,8 +113,8 @@ where
|
||||
}
|
||||
|
||||
pub struct BfsResult<V: Copy> {
|
||||
pub distances: VertexMap<V, Option<u32>>,
|
||||
pub predecessors: VertexMap<V, Option<V>>,
|
||||
pub distances: EntityMap<V, Option<u32>>,
|
||||
pub predecessors: EntityMap<V, Option<V>>,
|
||||
}
|
||||
|
||||
pub fn bfs<G>(graph: &G, source: G::Vertex) -> BfsResult<G::Vertex>
|
||||
@@ -132,7 +132,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
G: GraphTopology,
|
||||
{
|
||||
@@ -158,7 +158,7 @@ where
|
||||
}
|
||||
|
||||
struct BfsImplResult<V: Copy> {
|
||||
distances: VertexMap<V, Option<u32>>,
|
||||
distances: EntityMap<V, Option<u32>>,
|
||||
found: Option<(V, u32)>,
|
||||
}
|
||||
|
||||
@@ -195,8 +195,8 @@ where
|
||||
}
|
||||
|
||||
pub struct DfsResult<V: Copy> {
|
||||
pub visited: VertexMap<V, bool>,
|
||||
pub predecessors: VertexMap<V, Option<V>>,
|
||||
pub visited: EntityMap<V, bool>,
|
||||
pub predecessors: EntityMap<V, Option<V>>,
|
||||
}
|
||||
|
||||
pub fn dfs<G>(graph: &G, source: G::Vertex) -> DfsResult<G::Vertex>
|
||||
@@ -214,7 +214,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
G: GraphTopology,
|
||||
{
|
||||
@@ -240,7 +240,7 @@ where
|
||||
}
|
||||
|
||||
struct DfsImplResult<V: Copy> {
|
||||
visited: VertexMap<V, bool>,
|
||||
visited: EntityMap<V, bool>,
|
||||
found: Option<V>,
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! * 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,
|
||||
//! * [`EntityMap`] 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.
|
||||
@@ -118,8 +118,7 @@
|
||||
//! [BFS]: crate::algorithms::bfs
|
||||
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
|
||||
//! [DFS]: crate::algorithms::dfs
|
||||
//! [`EdgeMap`]: crate::maps::EdgeMap
|
||||
//! [`VertexMap`]: crate::maps::VertexMap
|
||||
//! [`EntityMap`]: crate::maps::EntityMap
|
||||
//! [`AppendGraph`]: crate::models::AppendGraph
|
||||
//! [`Graph`]: crate::models::Graph
|
||||
//! [`GraphTopology`]: crate::traits::GraphTopology
|
||||
|
||||
+30
-75
@@ -1,84 +1,32 @@
|
||||
//! Provides maps to associate custom data to graph vertices and edges.
|
||||
//!
|
||||
//! [`EntityMap`] provides a copy-on-write, [`Vec`]-backed map to associate data to either all
|
||||
//! vertices or all edges in a graph.
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::traits::GraphTopology;
|
||||
|
||||
pub struct VertexMap<V: Copy, T: Clone> {
|
||||
inner: EntityMap<V, T>,
|
||||
}
|
||||
|
||||
impl<V: Copy, T: Clone> VertexMap<V, T> {
|
||||
pub fn new(default: T, to_index: fn(V) -> usize, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner: EntityMap::new(default, to_index, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// Reads beyond 'capacity' are valid and return the default value.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.inner.capacity()
|
||||
}
|
||||
|
||||
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
|
||||
self.inner.resize(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]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EdgeMap<E: Copy, T: Clone> {
|
||||
inner: EntityMap<E, T>,
|
||||
}
|
||||
|
||||
impl<E: Copy, T: Clone> EdgeMap<E, T> {
|
||||
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner: EntityMap::new(default, to_index, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// Reads beyond 'capacity' are valid and return the default value.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.inner.capacity()
|
||||
}
|
||||
|
||||
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
|
||||
self.inner.resize(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> {
|
||||
/// A map to associate custom data to graph vertices or edges.
|
||||
///
|
||||
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph
|
||||
/// vertices or edges. The implementation uses a [`Vec`], allocating contiguous slots for the data,
|
||||
/// which means that the provided index conversion function should map entities 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 entities.
|
||||
///
|
||||
/// Use [`GraphTopology::vertex_map`] or [`GraphTopology::edge_map`] to obtain an `EntityMap` for
|
||||
/// vertices or edges, respectively.
|
||||
pub struct EntityMap<E: Copy, T: Clone> {
|
||||
data: Vec<T>,
|
||||
default: T,
|
||||
to_index: fn(E) -> usize,
|
||||
}
|
||||
|
||||
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 {
|
||||
Self {
|
||||
data: vec![default.clone(); capacity],
|
||||
@@ -87,11 +35,18 @@ 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 {
|
||||
self.data.len()
|
||||
self.data.capacity()
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, capacity: usize) {
|
||||
/// Extends the internal data storage of the map to `capacity`, pre-filling any new slots with
|
||||
/// the default value.
|
||||
///
|
||||
/// Use this before writing data for new graph entities to avoid incremental growth on the first
|
||||
/// write to each new entity.
|
||||
pub fn extend(&mut self, capacity: usize) {
|
||||
if capacity > self.data.len() {
|
||||
self.data.resize(capacity, self.default.clone());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::maps::{EdgeMap, VertexMap};
|
||||
use crate::maps::EntityMap;
|
||||
use crate::traits::{GraphTopology, IncidenceCursor};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
@@ -7,8 +7,8 @@ pub struct Vertex(usize);
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Edge(usize);
|
||||
|
||||
pub type AppendGraphVertexMap<T> = VertexMap<Vertex, T>;
|
||||
pub type AppendGraphEdgeMap<T> = EdgeMap<Edge, T>;
|
||||
pub type AppendGraphVertexMap<T> = EntityMap<Vertex, T>;
|
||||
pub type AppendGraphEdgeMap<T> = EntityMap<Edge, T>;
|
||||
|
||||
impl Edge {
|
||||
fn normalize(&self) -> Self {
|
||||
@@ -94,8 +94,8 @@ impl GraphTopology for AppendGraph {
|
||||
self.vertices.capacity()
|
||||
}
|
||||
|
||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
||||
VertexMap::new(default, |v| v.0, self.vertex_capacity())
|
||||
fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
|
||||
EntityMap::new(default, |v| v.0, self.vertex_capacity())
|
||||
}
|
||||
|
||||
fn edge_count(&self) -> usize {
|
||||
@@ -106,8 +106,8 @@ impl GraphTopology for AppendGraph {
|
||||
self.incidences.len() / 2
|
||||
}
|
||||
|
||||
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> {
|
||||
EdgeMap::new(default, |e| e.0 / 2, self.edge_capacity())
|
||||
fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
|
||||
EntityMap::new(default, |e| e.0 / 2, self.edge_capacity())
|
||||
}
|
||||
|
||||
fn degree(&self, v: Self::Vertex) -> usize {
|
||||
|
||||
+7
-7
@@ -1,13 +1,13 @@
|
||||
use typed_generational_arena::{Arena, Index};
|
||||
|
||||
use crate::maps::{EdgeMap, VertexMap};
|
||||
use crate::maps::EntityMap;
|
||||
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
||||
|
||||
pub type Vertex = Index<VertexIncidenceHeader, usize, usize>;
|
||||
pub type Edge = Index<IncidenceEntry, usize, usize>;
|
||||
|
||||
pub type GraphVertexMap<T> = VertexMap<Vertex, T>;
|
||||
pub type GraphEdgeMap<T> = EdgeMap<Edge, T>;
|
||||
pub type GraphVertexMap<T> = EntityMap<Vertex, T>;
|
||||
pub type GraphEdgeMap<T> = EntityMap<Edge, T>;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct VertexSlot(usize);
|
||||
@@ -163,8 +163,8 @@ impl GraphTopology for Graph {
|
||||
self.vertices.capacity()
|
||||
}
|
||||
|
||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
||||
VertexMap::new(default, |v| v.arr_idx(), self.vertex_capacity())
|
||||
fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
|
||||
EntityMap::new(default, |v| v.arr_idx(), self.vertex_capacity())
|
||||
}
|
||||
|
||||
fn edge_count(&self) -> usize {
|
||||
@@ -175,8 +175,8 @@ impl GraphTopology for Graph {
|
||||
self.incidences.capacity() / 2
|
||||
}
|
||||
|
||||
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> {
|
||||
EdgeMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity())
|
||||
fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
|
||||
EntityMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity())
|
||||
}
|
||||
|
||||
fn degree(&self, v: Self::Vertex) -> usize {
|
||||
|
||||
@@ -194,7 +194,7 @@ macro_rules! bfs_tests {
|
||||
}
|
||||
|
||||
fn assert_bfs_distances(
|
||||
distances: &$crate::maps::VertexMap<
|
||||
distances: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<u32>,
|
||||
>,
|
||||
@@ -222,7 +222,7 @@ macro_rules! bfs_tests {
|
||||
}
|
||||
|
||||
fn assert_bfs_predecessors(
|
||||
predecessors: &$crate::maps::VertexMap<
|
||||
predecessors: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
||||
>,
|
||||
|
||||
@@ -163,7 +163,7 @@ macro_rules! dfs_tests {
|
||||
}
|
||||
|
||||
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],
|
||||
) {
|
||||
for i in 0..10 {
|
||||
@@ -177,8 +177,8 @@ macro_rules! dfs_tests {
|
||||
|
||||
fn assert_dfs_predecessors(
|
||||
graph: &$T,
|
||||
visited: &$crate::maps::VertexMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
||||
predecessors: &$crate::maps::VertexMap<
|
||||
visited: &$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
|
||||
predecessors: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<<$T as $crate::traits::GraphTopology>::Vertex>,
|
||||
>,
|
||||
|
||||
@@ -131,7 +131,7 @@ macro_rules! dijkstra_tests {
|
||||
}
|
||||
|
||||
fn assert_distances_single_vertex(
|
||||
distances: &$crate::maps::VertexMap<
|
||||
distances: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<u32>,
|
||||
>,
|
||||
@@ -160,7 +160,7 @@ macro_rules! dijkstra_tests {
|
||||
}
|
||||
|
||||
fn assert_distances_disconnected(
|
||||
distances: &$crate::maps::VertexMap<
|
||||
distances: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<u32>,
|
||||
>,
|
||||
@@ -204,7 +204,7 @@ macro_rules! dijkstra_tests {
|
||||
}
|
||||
|
||||
fn assert_distances_test_graph(
|
||||
distances: &$crate::maps::VertexMap<
|
||||
distances: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<u32>,
|
||||
>,
|
||||
@@ -261,7 +261,7 @@ macro_rules! dijkstra_tests {
|
||||
}
|
||||
|
||||
fn assert_distances_unweighted_test_graph(
|
||||
distances: &$crate::maps::VertexMap<
|
||||
distances: &$crate::maps::EntityMap<
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
Option<u32>,
|
||||
>,
|
||||
@@ -300,7 +300,7 @@ macro_rules! dijkstra_tests {
|
||||
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||
<$T as $crate::traits::GraphTopology>::Edge,
|
||||
)>; 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;
|
||||
let (graph, vertices, edges, incidences) = make_test_graph();
|
||||
|
||||
+9
-161
@@ -1,6 +1,6 @@
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! vertex_map_tests {
|
||||
macro_rules! entity_map_tests {
|
||||
($T:ty) => {
|
||||
#[test]
|
||||
fn initial_values_are_default() {
|
||||
@@ -48,7 +48,7 @@ macro_rules! vertex_map_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_expands_to_new_vertices() {
|
||||
fn extend_to_new_vertices() {
|
||||
use $crate::traits::GraphTopology;
|
||||
let mut graph = <$T>::new();
|
||||
graph.add_vertex();
|
||||
@@ -59,21 +59,21 @@ macro_rules! vertex_map_tests {
|
||||
}
|
||||
assert!(
|
||||
map.capacity() < graph.vertex_capacity(),
|
||||
"precondition: map is stale before sync"
|
||||
"precondition: map is stale before extend"
|
||||
);
|
||||
map.sync(&graph);
|
||||
map.extend(graph.vertex_capacity());
|
||||
assert_eq!(map.capacity(), graph.vertex_capacity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_does_not_overwrite_existing_values() {
|
||||
fn extend_does_not_overwrite_existing_values() {
|
||||
use $crate::traits::GraphTopology;
|
||||
let mut graph = <$T>::new();
|
||||
let v = graph.add_vertex();
|
||||
let mut map = graph.vertex_map(0);
|
||||
map[v] = 5;
|
||||
graph.add_vertex();
|
||||
map.sync(&graph);
|
||||
map.extend(graph.vertex_capacity());
|
||||
assert_eq!(map[v], 5);
|
||||
}
|
||||
};
|
||||
@@ -81,7 +81,7 @@ macro_rules! vertex_map_tests {
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! vertex_map_deletion_tests {
|
||||
macro_rules! entity_map_deletion_tests {
|
||||
($T:ty) => {
|
||||
#[test]
|
||||
fn surviving_vertex_readable_after_delete() {
|
||||
@@ -108,7 +108,7 @@ macro_rules! vertex_map_deletion_tests {
|
||||
map[v1] = 5;
|
||||
let capacity_before = graph.vertex_capacity();
|
||||
graph.delete_vertex(v2);
|
||||
map.sync(&graph);
|
||||
map.extend(graph.vertex_capacity());
|
||||
assert_eq!(map.capacity(), capacity_before);
|
||||
assert_eq!(map[v1], 5);
|
||||
}
|
||||
@@ -124,162 +124,10 @@ macro_rules! vertex_map_deletion_tests {
|
||||
map[v1] = 99;
|
||||
graph.delete_vertex(v1);
|
||||
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
|
||||
// after deletion.
|
||||
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 sync_expands_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 sync"
|
||||
);
|
||||
map.sync(&graph);
|
||||
assert_eq!(map.capacity(), graph.edge_capacity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_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.sync(&graph);
|
||||
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 capacity_does_not_shrink_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] = 5;
|
||||
let capacity_before = graph.edge_capacity();
|
||||
graph.delete_edge(e2);
|
||||
map.sync(&graph);
|
||||
assert_eq!(map.capacity(), capacity_before);
|
||||
assert_eq!(map[e1], 5);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,4 +1,4 @@
|
||||
use crate::maps::{EdgeMap, VertexMap};
|
||||
use crate::maps::EntityMap;
|
||||
|
||||
// TODO: Add functions to reserve memory for vertices and edges.
|
||||
// TODO: Split out GraphTopologyAddition trait.
|
||||
@@ -46,7 +46,8 @@ pub trait GraphTopology {
|
||||
/// Returns the total number of vertices the graph can hold without reallocating.
|
||||
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
|
||||
///
|
||||
@@ -66,7 +67,7 @@ pub trait GraphTopology {
|
||||
/// 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.
|
||||
fn edge_count(&self) -> usize;
|
||||
@@ -74,7 +75,7 @@ pub trait GraphTopology {
|
||||
/// Returns the total number of edges the graph can hold without reallocating.
|
||||
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
|
||||
///
|
||||
@@ -96,7 +97,7 @@ pub trait GraphTopology {
|
||||
/// 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.
|
||||
///
|
||||
|
||||
+5
-18
@@ -1,25 +1,12 @@
|
||||
mod append_graph_vertex_map_tests {
|
||||
mod append_graph_entity_map_tests {
|
||||
use grapherity::models::AppendGraph;
|
||||
|
||||
grapherity::vertex_map_tests!(AppendGraph);
|
||||
grapherity::entity_map_tests!(AppendGraph);
|
||||
}
|
||||
|
||||
mod append_graph_edge_map_tests {
|
||||
use grapherity::models::AppendGraph;
|
||||
|
||||
grapherity::edge_map_tests!(AppendGraph);
|
||||
}
|
||||
|
||||
mod graph_vertex_map_tests {
|
||||
mod graph_entity_map_tests {
|
||||
use grapherity::models::Graph;
|
||||
|
||||
grapherity::vertex_map_tests!(Graph);
|
||||
grapherity::vertex_map_deletion_tests!(Graph);
|
||||
}
|
||||
|
||||
mod graph_edge_map_tests {
|
||||
use grapherity::models::Graph;
|
||||
|
||||
grapherity::edge_map_tests!(Graph);
|
||||
grapherity::edge_map_deletion_tests!(Graph);
|
||||
grapherity::entity_map_tests!(Graph);
|
||||
grapherity::entity_map_deletion_tests!(Graph);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user