Add Incidence struct for GraphTopology to use

This commit is contained in:
2026-08-06 09:57:51 +02:00
parent 110e6310aa
commit 2851cf60fd
8 changed files with 154 additions and 110 deletions
+11 -8
View File
@@ -34,7 +34,7 @@ use std::collections::VecDeque;
use std::hash::Hash; use std::hash::Hash;
use crate::maps::EntityMap; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, IncidenceCursor}; use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
/// 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> {
@@ -236,16 +236,16 @@ where
heap.push(source, Reverse(0u32)); heap.push(source, Reverse(0u32));
while let Some((v, Reverse(v_distance))) = heap.pop() { while let Some((v, Reverse(v_distance))) = heap.pop() {
for incidence in graph.incidences(v) { for Incidence { vertex: u, edge: e } in graph.incidences(v) {
let new_distance = v_distance + weights(incidence.1); let new_distance = v_distance + weights(e);
if match distances[incidence.0] { if match distances[u] {
None => true, None => true,
Some(old_distance) if old_distance > new_distance => true, Some(old_distance) if old_distance > new_distance => true,
_ => false, _ => false,
} { } {
distances[incidence.0] = Some(new_distance); distances[u] = Some(new_distance);
on_relax(incidence.0, v); on_relax(u, v);
heap.push_increase(incidence.0, Reverse(new_distance)); heap.push_increase(u, Reverse(new_distance));
} }
} }
} }
@@ -741,7 +741,10 @@ where
None => { None => {
stack.pop(); stack.pop();
} }
Some((neighbor, edge)) => { Some(Incidence {
vertex: neighbor,
edge,
}) => {
if predicate(neighbor) { if predicate(neighbor) {
let mut path: Vec<G::Edge> = let mut path: Vec<G::Edge> =
stack.iter().filter_map(|f| f.arrival_edge).collect(); stack.iter().filter_map(|f| f.arrival_edge).collect();
+1 -1
View File
@@ -136,7 +136,7 @@ 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, IncidenceCursor}; pub use crate::traits::{GraphTopology, GraphTopologyDeletion, Incidence, IncidenceCursor};
} }
mod testing; mod testing;
+4 -2
View File
@@ -5,5 +5,7 @@ pub mod graph;
// TODO: Compressed-sparse-row graph model. // TODO: Compressed-sparse-row graph model.
pub use append_graph::{AppendGraph, AppendGraphEdgeMap, AppendGraphVertexMap}; pub use append_graph::{
pub use graph::{Graph, GraphEdgeMap, GraphVertexMap}; AppendGraph, AppendGraphEdgeMap, AppendGraphIncidence, AppendGraphVertexMap,
};
pub use graph::{Graph, GraphEdgeMap, GraphIncidence, GraphVertexMap};
+14 -5
View File
@@ -1,7 +1,7 @@
//! [`AppendGraph`], an undirected graph topology supporting addition only. //! [`AppendGraph`], an undirected graph topology supporting addition only.
use crate::maps::EntityMap; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, IncidenceCursor}; use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
/// An opaque handle identifying a vertex in an [`AppendGraph`]. /// An opaque handle identifying a vertex in an [`AppendGraph`].
/// ///
@@ -23,6 +23,9 @@ pub type AppendGraphVertexMap<T> = EntityMap<Vertex, T>;
/// An [`EntityMap`] for [`AppendGraph`] edges. /// An [`EntityMap`] for [`AppendGraph`] edges.
pub type AppendGraphEdgeMap<T> = EntityMap<Edge, T>; pub type AppendGraphEdgeMap<T> = EntityMap<Edge, T>;
/// An [`Incidence`] for [`AppendGraph`].
pub type AppendGraphIncidence = Incidence<Vertex, Edge>;
impl Edge { impl Edge {
fn normalize(&self) -> Self { fn normalize(&self) -> Self {
Self(self.0 & !1) Self(self.0 & !1)
@@ -50,10 +53,13 @@ pub struct AppendGraphIncidenceCursor {
} }
impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor { impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor {
fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Edge)> { fn next(&mut self, graph: &AppendGraph) -> Option<AppendGraphIncidence> {
graph graph
.step_incidence(&mut self.incidence) .step_incidence(&mut self.incidence)
.map(|(v, e)| (v, e.normalize())) .map(|(v, e)| Incidence {
vertex: v,
edge: e.normalize(),
})
} }
} }
@@ -191,8 +197,11 @@ impl GraphTopology for AppendGraph {
self.raw_incidences(v).map(|(_, e)| e.normalize()) self.raw_incidences(v).map(|(_, e)| e.normalize())
} }
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)> { fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = AppendGraphIncidence> {
self.raw_incidences(v).map(|(v, e)| (v, e.normalize())) self.raw_incidences(v).map(|(v, e)| Incidence {
vertex: v,
edge: e.normalize(),
})
} }
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor {
+16 -11
View File
@@ -3,7 +3,7 @@
use typed_generational_arena::{Arena, Index}; use typed_generational_arena::{Arena, Index};
use crate::maps::EntityMap; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; use crate::traits::{GraphTopology, GraphTopologyDeletion, Incidence, IncidenceCursor};
/// An opaque handle identifying a vertex in a [`Graph`]. /// An opaque handle identifying a vertex in a [`Graph`].
/// ///
@@ -23,6 +23,9 @@ pub type GraphVertexMap<T> = EntityMap<Vertex, T>;
/// An [`EntityMap`] for [`Graph`] edges. /// An [`EntityMap`] for [`Graph`] edges.
pub type GraphEdgeMap<T> = EntityMap<Edge, T>; pub type GraphEdgeMap<T> = EntityMap<Edge, T>;
/// An [`Incidence`] for [`Graph`].
pub type GraphIncidence = Incidence<Vertex, Edge>;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct VertexSlot(usize); struct VertexSlot(usize);
@@ -66,13 +69,13 @@ pub struct GraphIncidenceCursor {
} }
impl IncidenceCursor<Graph> for GraphIncidenceCursor { impl IncidenceCursor<Graph> for GraphIncidenceCursor {
fn next(&mut self, graph: &Graph) -> Option<(Vertex, Edge)> { fn next(&mut self, graph: &Graph) -> Option<GraphIncidence> {
graph.step_incidence(&mut self.incidence).map(|(vs, e)| { graph
( .step_incidence(&mut self.incidence)
graph.vertices.get_idx(vs.0).unwrap(), .map(|(vs, e)| Incidence {
graph.normalize_edge(e), vertex: graph.vertices.get_idx(vs.0).unwrap(),
) edge: graph.normalize_edge(e),
}) })
} }
} }
@@ -297,9 +300,11 @@ impl GraphTopology for Graph {
self.raw_incidences(v).map(|(_, e)| self.normalize_edge(e)) self.raw_incidences(v).map(|(_, e)| self.normalize_edge(e))
} }
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)> { fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = GraphIncidence> {
self.raw_incidences(v) self.raw_incidences(v).map(|(vs, e)| Incidence {
.map(|(vs, e)| (self.vertices.get_idx(vs.0).unwrap(), self.normalize_edge(e))) vertex: self.vertices.get_idx(vs.0).unwrap(),
edge: self.normalize_edge(e),
})
} }
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor {
+6 -4
View File
@@ -296,10 +296,12 @@ macro_rules! dijkstra_tests {
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
); 18], ); 18],
[Vec<( [Vec<
<$T as $crate::traits::GraphTopology>::Vertex, $crate::traits::Incidence<
<$T as $crate::traits::GraphTopology>::Edge, <$T as $crate::traits::GraphTopology>::Vertex,
)>; 10], <$T as $crate::traits::GraphTopology>::Edge,
>,
>; 10],
$crate::maps::EntityMap<<$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;
+83 -74
View File
@@ -10,12 +10,12 @@ macro_rules! graph_topology_test_fixtures {
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
); 18], ); 18],
[Vec<( [Vec<$crate::traits::Incidence<
<$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],
) { ) {
use $crate::traits::GraphTopology; use $crate::traits::{GraphTopology, Incidence};
let mut graph = <$T>::new(); let mut graph = <$T>::new();
let vertices: [<$T as $crate::traits::GraphTopology>::Vertex; 10] = let vertices: [<$T as $crate::traits::GraphTopology>::Vertex; 10] =
core::array::from_fn(|_| graph.add_vertex()); core::array::from_fn(|_| graph.add_vertex());
@@ -40,50 +40,54 @@ macro_rules! graph_topology_test_fixtures {
(vertices[7], vertices[9]), (vertices[7], vertices[9]),
] ]
.map(|(v1, v2)| (graph.add_edge(v1, v2), v1, v2)); .map(|(v1, v2)| (graph.add_edge(v1, v2), v1, v2));
let incidences: [Vec<_>; 10] = [ let i = |vertex, edge| Incidence { vertex, edge };
vec![(vertices[1], edges[0].0), (vertices[1], edges[1].0)], let incidences: [Vec<Incidence<
<$T as GraphTopology>::Vertex,
<$T as GraphTopology>::Edge,
>>; 10] = [
vec![i(vertices[1], edges[0].0), i(vertices[1], edges[1].0)],
vec![ vec![
(vertices[0], edges[0].0), i(vertices[0], edges[0].0),
(vertices[0], edges[1].0), i(vertices[0], edges[1].0),
(vertices[2], edges[2].0), i(vertices[2], edges[2].0),
(vertices[3], edges[3].0), i(vertices[3], edges[3].0),
(vertices[4], edges[4].0), i(vertices[4], edges[4].0),
], ],
vec![ vec![
(vertices[2], edges[5].0), i(vertices[2], edges[5].0),
(vertices[2], edges[5].0), i(vertices[2], edges[5].0),
(vertices[1], edges[2].0), i(vertices[1], edges[2].0),
(vertices[4], edges[6].0), i(vertices[4], edges[6].0),
(vertices[4], edges[7].0), i(vertices[4], edges[7].0),
(vertices[5], edges[8].0), i(vertices[5], edges[8].0),
(vertices[6], edges[9].0), i(vertices[6], edges[9].0),
], ],
vec![(vertices[1], edges[3].0), (vertices[6], edges[10].0)], vec![i(vertices[1], edges[3].0), i(vertices[6], edges[10].0)],
vec![ vec![
(vertices[1], edges[4].0), i(vertices[1], edges[4].0),
(vertices[2], edges[6].0), i(vertices[2], edges[6].0),
(vertices[2], edges[7].0), i(vertices[2], edges[7].0),
(vertices[4], edges[11].0), i(vertices[4], edges[11].0),
(vertices[4], edges[11].0), i(vertices[4], edges[11].0),
(vertices[7], edges[12].0), i(vertices[7], edges[12].0),
(vertices[8], edges[13].0), i(vertices[8], edges[13].0),
], ],
vec![(vertices[2], edges[8].0), (vertices[9], edges[14].0)], vec![i(vertices[2], edges[8].0), i(vertices[9], edges[14].0)],
vec![ vec![
(vertices[2], edges[9].0), i(vertices[2], edges[9].0),
(vertices[3], edges[10].0), i(vertices[3], edges[10].0),
(vertices[9], edges[15].0), i(vertices[9], edges[15].0),
], ],
vec![ vec![
(vertices[4], edges[12].0), i(vertices[4], edges[12].0),
(vertices[8], edges[16].0), i(vertices[8], edges[16].0),
(vertices[9], edges[17].0), i(vertices[9], edges[17].0),
], ],
vec![(vertices[4], edges[13].0), (vertices[7], edges[16].0)], vec![i(vertices[4], edges[13].0), i(vertices[7], edges[16].0)],
vec![ vec![
(vertices[5], edges[14].0), i(vertices[5], edges[14].0),
(vertices[6], edges[15].0), i(vertices[6], edges[15].0),
(vertices[7], edges[17].0), i(vertices[7], edges[17].0),
], ],
]; ];
(graph, vertices, edges, incidences) (graph, vertices, edges, incidences)
@@ -540,7 +544,7 @@ macro_rules! graph_topology_tests {
"unexpected incident edge count for vertex {:?}", "unexpected incident edge count for vertex {:?}",
vertices[i] vertices[i]
); );
let mut expected: Vec<_> = incidences[i].iter().map(|(_, e)| *e).collect(); let mut expected: Vec<_> = incidences[i].iter().map(|x| x.edge).collect();
for e in graph.incident_edges(vertices[i]) { for e in graph.incident_edges(vertices[i]) {
let pos = expected let pos = expected
.iter() .iter()
@@ -634,7 +638,7 @@ macro_rules! graph_topology_tests {
for incidence in graph.incidences(vertices[i]) { for incidence in graph.incidences(vertices[i]) {
let pos = expected let pos = expected
.iter() .iter()
.position(|(v, e)| *v == incidence.0 && *e == incidence.1) .position(|x| *x == incidence)
.expect(&format!( .expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from iterator", "unexpected incidence {incidence:?} of vertex {:?} from iterator",
vertices[i] vertices[i]
@@ -657,17 +661,21 @@ macro_rules! graph_topology_tests {
// For loop edges (u == v), the edge must appear exactly twice in the incidences of u. // For loop edges (u == v), the edge must appear exactly twice in the incidences of u.
let (graph, _, _, _) = make_test_graph(); let (graph, _, _, _) = make_test_graph();
for u in graph.vertices() { for u in graph.vertices() {
for (v, e) in graph.incidences(u) { for incidence in graph.incidences(u) {
if u == v { if u == incidence.vertex {
assert_eq!( assert_eq!(
graph.incidences(u).filter(|(_, f)| *f == e).count(), graph.incidences(u).filter(|x| x.edge == incidence.edge).count(),
2, 2,
"loop edge {e:?} should appear exactly twice in incidences of vertex {u:?}" "loop edge {:?} should appear exactly twice in incidences of vertex {u:?}",
incidence.edge
); );
} else { } else {
assert!( assert!(
graph.incidences(v).any(|(w, f)| w == u && f == e), graph
"edge {e:?} from incidences of vertex {u:?} missing in incidences of adjacent vertex {v:?}" .incidences(incidence.vertex)
.any(|x| x.vertex == u && x.edge == incidence.edge),
"edge {:?} from incidences of vertex {u:?} missing in incidences of adjacent vertex {:?}",
incidence.edge, incidence.vertex
); );
} }
} }
@@ -676,19 +684,19 @@ macro_rules! graph_topology_tests {
#[test] #[test]
fn incidences_loop_edge() { fn incidences_loop_edge() {
use $crate::traits::GraphTopology; use $crate::traits::{GraphTopology, Incidence};
let mut graph = <$T>::new(); let mut graph = <$T>::new();
let v = graph.add_vertex(); let v = graph.add_vertex();
let e = graph.add_edge(v, v); let e = graph.add_edge(v, v);
let mut iter = graph.incidences(v); let mut iter = graph.incidences(v);
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some((v, e)), Some(Incidence { vertex: v, edge: e }),
"vertex should be adjacent to itself" "vertex should be adjacent to itself"
); );
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some((v, e)), Some(Incidence { vertex: v, edge: e }),
"vertex should be adjacent to itself twice" "vertex should be adjacent to itself twice"
); );
assert_eq!( assert_eq!(
@@ -716,16 +724,16 @@ macro_rules! graph_topology_tests {
vertices[i] vertices[i]
)); ));
assert_eq!( assert_eq!(
current.0, current.vertex,
vertices[1 - i], vertices[1 - i],
"unexpected adjacent vertex of vertex {:?} in incidence {j}", "unexpected adjacent vertex of vertex {:?} in incidence {j}",
vertices[i] vertices[i]
); );
assert_eq!( assert_eq!(
edges.iter().filter(|e| **e == current.1).count(), edges.iter().filter(|e| **e == current.edge).count(),
1, 1,
"unexpected incident edge {:?} of vertex {:?}", "unexpected incident edge {:?} of vertex {:?}",
current.1, current.edge,
vertices[i], vertices[i],
); );
} }
@@ -761,7 +769,7 @@ macro_rules! graph_topology_tests {
while let Some(incidence) = cursor.next(&graph) { while let Some(incidence) = cursor.next(&graph) {
let pos = expected let pos = expected
.iter() .iter()
.position(|(v, e)| *v == incidence.0 && *e == incidence.1) .position(|x| *x == incidence)
.expect(&format!( .expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from cursor", "unexpected incidence {incidence:?} of vertex {:?} from cursor",
vertices[i] vertices[i]
@@ -779,19 +787,19 @@ macro_rules! graph_topology_tests {
#[test] #[test]
fn incidence_cursor_loop_edge() { fn incidence_cursor_loop_edge() {
use $crate::traits::{GraphTopology, IncidenceCursor}; use $crate::traits::{GraphTopology, Incidence, IncidenceCursor};
let mut graph = <$T>::new(); let mut graph = <$T>::new();
let v = graph.add_vertex(); let v = graph.add_vertex();
let e = graph.add_edge(v, v); let e = graph.add_edge(v, v);
let mut cursor = graph.incidence_cursor(v); let mut cursor = graph.incidence_cursor(v);
assert_eq!( assert_eq!(
cursor.next(&graph), cursor.next(&graph),
Some((v, e)), Some(Incidence { vertex: v, edge: e }),
"vertex should be adjacent to itself" "vertex should be adjacent to itself"
); );
assert_eq!( assert_eq!(
cursor.next(&graph), cursor.next(&graph),
Some((v, e)), Some(Incidence { vertex: v, edge: e }),
"vertex should be adjacent to itself twice" "vertex should be adjacent to itself twice"
); );
assert_eq!( assert_eq!(
@@ -819,16 +827,16 @@ macro_rules! graph_topology_tests {
vertices[i] vertices[i]
)); ));
assert_eq!( assert_eq!(
current.0, current.vertex,
vertices[1 - i], vertices[1 - i],
"unexpected adjacent vertex of vertex {:?} in incidence {j}", "unexpected adjacent vertex of vertex {:?} in incidence {j}",
vertices[i] vertices[i]
); );
assert_eq!( assert_eq!(
edges.iter().filter(|e| **e == current.1).count(), edges.iter().filter(|e| **e == current.edge).count(),
1, 1,
"unexpected incident edge {:?} of vertex {:?}", "unexpected incident edge {:?} of vertex {:?}",
current.1, current.edge,
vertices[i], vertices[i],
); );
} }
@@ -868,11 +876,12 @@ macro_rules! graph_topology_tests {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
let (graph, _, _, _) = make_test_graph(); let (graph, _, _, _) = make_test_graph();
for u in graph.vertices() { for u in graph.vertices() {
for (v, e) in graph.incidences(u) { for incidence in graph.incidences(u) {
let (w1, w2) = graph.incident_vertices(e); let (w1, w2) = graph.incident_vertices(incidence.edge);
assert!( assert!(
(w1 == u && w2 == v) || (w1 == v && w2 == u), (w1 == u && w2 == incidence.vertex) || (w1 == incidence.vertex && w2 == u),
"incident vertices {w1:?} and {w2:?} of edge {e:?} are inconsistent with incidence ({v:?}, {e:?}) of vertex {u:?}" "edge endpoints {w1:?} and {w2:?} are inconsistent with incidence ({:?}, {:?}) of vertex {u:?}",
incidence.vertex, incidence.edge
); );
} }
} }
@@ -883,7 +892,7 @@ macro_rules! graph_topology_tests {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
let (graph, _, _, _) = make_test_graph(); let (graph, _, _, _) = make_test_graph();
for u in graph.vertices() { for u in graph.vertices() {
let mut expected: Vec<_> = graph.incidences(u).map(|(_, e)| e).collect(); let mut expected: Vec<_> = graph.incidences(u).map(|x| x.edge).collect();
for e in graph.incident_edges(u) { for e in graph.incident_edges(u) {
let pos = expected let pos = expected
.iter() .iter()
@@ -1256,7 +1265,7 @@ macro_rules! graph_topology_deletion_tests {
for i in 0..10 { for i in 0..10 {
let mut expected: Vec<_> = incidences[i] let mut expected: Vec<_> = incidences[i]
.iter() .iter()
.filter_map(|(_, e)| (*e != edges[2].0).then_some(*e)) .filter_map(|x| (x.edge != edges[2].0).then_some(x.edge))
.collect(); .collect();
assert_eq!( assert_eq!(
graph.incident_edges(vertices[i]).count(), graph.incident_edges(vertices[i]).count(),
@@ -1289,7 +1298,7 @@ macro_rules! graph_topology_deletion_tests {
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
let mut expected: Vec<_> = incidences[i] let mut expected: Vec<_> = incidences[i]
.iter() .iter()
.filter_map(|(v, e)| (*v != vertices[2]).then_some(*e)) .filter_map(|x| (x.vertex != vertices[2]).then_some(x.edge))
.collect(); .collect();
assert_eq!( assert_eq!(
graph.incident_edges(vertices[i]).count(), graph.incident_edges(vertices[i]).count(),
@@ -1321,7 +1330,7 @@ macro_rules! graph_topology_deletion_tests {
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
let remaining = incidences[i] let remaining = incidences[i]
.iter() .iter()
.filter(|(v, _)| *v != vertices[2]) .filter(|x| x.vertex != vertices[2])
.cloned() .cloned()
.collect(); .collect();
assert_vertex_incidences(&graph, vertices[i], remaining); assert_vertex_incidences(&graph, vertices[i], remaining);
@@ -1337,7 +1346,7 @@ macro_rules! graph_topology_deletion_tests {
for i in 0..10 { for i in 0..10 {
let remaining = incidences[i] let remaining = incidences[i]
.iter() .iter()
.filter(|(_, e)| *e != edges[2].0) .filter(|x| x.edge != edges[2].0)
.cloned() .cloned()
.collect(); .collect();
assert_vertex_incidences(&graph, vertices[i], remaining); assert_vertex_incidences(&graph, vertices[i], remaining);
@@ -1347,10 +1356,10 @@ macro_rules! graph_topology_deletion_tests {
fn assert_vertex_incidences( fn assert_vertex_incidences(
graph: &$T, graph: &$T,
v: <$T as $crate::traits::GraphTopology>::Vertex, v: <$T as $crate::traits::GraphTopology>::Vertex,
mut expected: Vec<( mut expected: Vec<$crate::traits::Incidence<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Edge, <$T as $crate::traits::GraphTopology>::Edge,
)>, >>,
) { ) {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
assert_eq!( assert_eq!(
@@ -1362,7 +1371,7 @@ macro_rules! graph_topology_deletion_tests {
for incidence in graph.incidences(v) { for incidence in graph.incidences(v) {
let pos = expected let pos = expected
.iter() .iter()
.position(|(u, e)| *u == incidence.0 && *e == incidence.1) .position(|x| *x == incidence)
.expect(&format!( .expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from iterator after delete", "unexpected incidence {incidence:?} of vertex {:?} from iterator after delete",
v v
@@ -1385,7 +1394,7 @@ macro_rules! graph_topology_deletion_tests {
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
let remaining = incidences[i] let remaining = incidences[i]
.iter() .iter()
.filter(|(v, _)| *v != vertices[2]) .filter(|x| x.vertex != vertices[2])
.cloned() .cloned()
.collect(); .collect();
assert_vertex_incidence_cursor(&graph, vertices[i], remaining); assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
@@ -1401,7 +1410,7 @@ macro_rules! graph_topology_deletion_tests {
for i in 0..10 { for i in 0..10 {
let remaining = incidences[i] let remaining = incidences[i]
.iter() .iter()
.filter(|(_, e)| *e != edges[2].0) .filter(|x| x.edge != edges[2].0)
.cloned() .cloned()
.collect(); .collect();
assert_vertex_incidence_cursor(&graph, vertices[i], remaining); assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
@@ -1411,17 +1420,17 @@ macro_rules! graph_topology_deletion_tests {
fn assert_vertex_incidence_cursor( fn assert_vertex_incidence_cursor(
graph: &$T, graph: &$T,
v: <$T as $crate::traits::GraphTopology>::Vertex, v: <$T as $crate::traits::GraphTopology>::Vertex,
mut expected: Vec<( mut expected: Vec<$crate::traits::Incidence<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Edge, <$T as $crate::traits::GraphTopology>::Edge,
)>, >>,
) { ) {
use $crate::traits::IncidenceCursor; use $crate::traits::IncidenceCursor;
let mut cursor = graph.incidence_cursor(v); let mut cursor = graph.incidence_cursor(v);
while let Some(incidence) = cursor.next(graph) { while let Some(incidence) = cursor.next(graph) {
let pos = expected let pos = expected
.iter() .iter()
.position(|(u, e)| *u == incidence.0 && *e == incidence.1) .position(|x| *x == incidence)
.expect(&format!( .expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from cursor after delete", "unexpected incidence {incidence:?} of vertex {:?} from cursor after delete",
v v
+19 -5
View File
@@ -4,7 +4,6 @@ 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.
// TODO: Introduce an Incidence struct.
/// A trait representing an undirected graph topology. /// A trait representing an undirected graph topology.
/// ///
/// An undirected graph is a set of vertices and undirected edges, where each edge connects either /// An undirected graph is a set of vertices and undirected edges, where each edge connects either
@@ -165,7 +164,10 @@ pub trait GraphTopology {
/// # Panics /// # Panics
/// ///
/// Panics if `v` is not a valid vertex of this graph. /// 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 = Incidence<Self::Vertex, Self::Edge>>;
/// Returns a cursor over all incidences of `v`, analogously to /// Returns a cursor over all incidences of `v`, analogously to
/// [`incidences`](Self::incidences), initially positioned before the first one. /// [`incidences`](Self::incidences), initially positioned before the first one.
@@ -221,8 +223,8 @@ pub trait GraphTopologyDeletion: GraphTopology {
/// [`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>: Copy { 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, or `None` if the traversal is
/// traversal is exhausted. /// exhausted.
/// ///
/// # Examples /// # Examples
/// ///
@@ -248,5 +250,17 @@ pub trait IncidenceCursor<G: GraphTopology + ?Sized>: Copy {
/// assert!(c2.next(&graph).is_some()); /// assert!(c2.next(&graph).is_some());
/// assert!(c2.next(&graph).is_none()); /// assert!(c2.next(&graph).is_none());
/// ``` /// ```
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>; fn next(&mut self, graph: &G) -> Option<Incidence<G::Vertex, G::Edge>>;
}
/// An incidence of some vertex *v* in a graph, i.e. a vertex-edge pair *(u, e)* such that *u* is
/// adjacent to *v* and *e* is an edge between them.
///
/// Return type of [`GraphTopology::incidences`] and [`IncidenceCursor::next`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Incidence<V, E> {
/// The vertex adjacent to *v*.
pub vertex: V,
/// The edge between *v* and [`vertex`](Self::vertex).
pub edge: E,
} }