Add Incidence struct for GraphTopology to use
This commit is contained in:
+11
-8
@@ -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
@@ -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
@@ -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};
|
||||||
|
|||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user