Fix IncidenceCursor for AppendGraph and Graph, and add tests
This commit is contained in:
@@ -34,7 +34,9 @@ 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<(Vertex, Edge)> {
|
||||||
graph.step_incidence(&mut self.incidence)
|
graph
|
||||||
|
.step_incidence(&mut self.incidence)
|
||||||
|
.map(|(v, e)| (v, e.normalize()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-4
@@ -43,9 +43,12 @@ 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<(Vertex, Edge)> {
|
||||||
graph
|
graph.step_incidence(&mut self.incidence).map(|(vs, e)| {
|
||||||
.step_incidence(&mut self.incidence)
|
(
|
||||||
.map(|(vs, e)| (graph.vertices.get_idx(vs.0).unwrap(), e))
|
graph.vertices.get_idx(vs.0).unwrap(),
|
||||||
|
graph.normalize_edge(e),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +143,12 @@ impl Graph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_edge(&self, e: Edge) -> Edge {
|
fn normalize_edge(&self, e: Edge) -> Edge {
|
||||||
self.incidences.get_idx(e.arr_idx() & !1).unwrap()
|
let i = e.arr_idx();
|
||||||
|
if i & 1 == 0 {
|
||||||
|
e
|
||||||
|
} else {
|
||||||
|
self.incidences.get_idx(i ^ 1).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ macro_rules! graph_topology_tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
vertices.iter().filter(|&x| *x == v).count(),
|
vertices.iter().filter(|&x| *x == v).count(),
|
||||||
1,
|
1,
|
||||||
"unexpected vertex {v:?} from the iterator"
|
"unexpected vertex {v:?} from iterator"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,7 +339,7 @@ macro_rules! graph_topology_tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.position(|w| *w == v)
|
.position(|w| *w == v)
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"unexpected adjacent vertex {v:?} of {:?} from the iterator",
|
"unexpected adjacent vertex {v:?} of {:?} from iterator",
|
||||||
vertices[4]
|
vertices[4]
|
||||||
));
|
));
|
||||||
expected_adjacency.swap_remove(i);
|
expected_adjacency.swap_remove(i);
|
||||||
@@ -347,7 +347,7 @@ macro_rules! graph_topology_tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_adjacency.len(),
|
expected_adjacency.len(),
|
||||||
0,
|
0,
|
||||||
"expected adjacent vertices {:?} of {:?} were not matched by the iterator",
|
"expected adjacent vertices {:?} of {:?} were not matched by iterator",
|
||||||
expected_adjacency,
|
expected_adjacency,
|
||||||
vertices[4]
|
vertices[4]
|
||||||
);
|
);
|
||||||
@@ -484,7 +484,7 @@ macro_rules! graph_topology_tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
edges.iter().filter(|&&(f, _, _)| f == e).count(),
|
edges.iter().filter(|&&(f, _, _)| f == e).count(),
|
||||||
1,
|
1,
|
||||||
"unexpected edge {e:?} from the iterator"
|
"unexpected edge {e:?} from iterator"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,14 +518,14 @@ macro_rules! graph_topology_tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.position(|f| *f == e)
|
.position(|f| *f == e)
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"unexpected incident edge {e:?} of vertex {:?} from the iterator",
|
"unexpected incident edge {e:?} of vertex {:?} from iterator",
|
||||||
vertices[i]
|
vertices[i]
|
||||||
));
|
));
|
||||||
expected.swap_remove(pos);
|
expected.swap_remove(pos);
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
expected.is_empty(),
|
expected.is_empty(),
|
||||||
"expected incident edges {:?} of vertex {:?} were not matched by the iterator",
|
"expected incident edges {:?} of vertex {:?} were not matched by iterator",
|
||||||
expected,
|
expected,
|
||||||
vertices[i]
|
vertices[i]
|
||||||
);
|
);
|
||||||
@@ -572,7 +572,7 @@ macro_rules! graph_topology_tests {
|
|||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
expected.is_empty(),
|
expected.is_empty(),
|
||||||
"expected incident edges {:?} of vertex {:?} were not matched by the iterator",
|
"expected incident edges {:?} of vertex {:?} were not matched by iterator",
|
||||||
expected,
|
expected,
|
||||||
vertices[i]
|
vertices[i]
|
||||||
);
|
);
|
||||||
@@ -608,14 +608,14 @@ macro_rules! graph_topology_tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
|
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"unexpected incidence {incidence:?} of vertex {:?} from the iterator",
|
"unexpected incidence {incidence:?} of vertex {:?} from iterator",
|
||||||
vertices[i]
|
vertices[i]
|
||||||
));
|
));
|
||||||
expected.swap_remove(pos);
|
expected.swap_remove(pos);
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
expected.is_empty(),
|
expected.is_empty(),
|
||||||
"expected incidences {:?} of vertex {:?} were not matched by the iterator",
|
"expected incidences {:?} of vertex {:?} were not matched by iterator",
|
||||||
expected,
|
expected,
|
||||||
vertices[i]
|
vertices[i]
|
||||||
);
|
);
|
||||||
@@ -710,6 +710,131 @@ macro_rules! graph_topology_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_empty() {
|
||||||
|
use $crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v = graph.add_vertex();
|
||||||
|
let mut cursor = graph.incidence_cursor(v);
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(&graph),
|
||||||
|
None,
|
||||||
|
"incidence cursor of vertex with degree 0 should immediately be exhausted"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor() {
|
||||||
|
use $crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
let (graph, vertices, _, incidences) = make_test_graph();
|
||||||
|
for i in 0..10 {
|
||||||
|
let mut expected = incidences[i].clone();
|
||||||
|
let mut cursor = graph.incidence_cursor(vertices[i]);
|
||||||
|
while let Some(incidence) = cursor.next(&graph) {
|
||||||
|
let pos = expected
|
||||||
|
.iter()
|
||||||
|
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
|
||||||
|
.expect(&format!(
|
||||||
|
"unexpected incidence {incidence:?} of vertex {:?} from cursor",
|
||||||
|
vertices[i]
|
||||||
|
));
|
||||||
|
expected.swap_remove(pos);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
expected.is_empty(),
|
||||||
|
"expected incidences {:?} of vertex {:?} were not matched by cursor",
|
||||||
|
expected,
|
||||||
|
vertices[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_loop_edge() {
|
||||||
|
use $crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v = graph.add_vertex();
|
||||||
|
let e = graph.add_edge(v, v);
|
||||||
|
let mut cursor = graph.incidence_cursor(v);
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(&graph),
|
||||||
|
Some((v, e)),
|
||||||
|
"vertex should be adjacent to itself"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(&graph),
|
||||||
|
Some((v, e)),
|
||||||
|
"vertex should be adjacent to itself twice"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(&graph),
|
||||||
|
None,
|
||||||
|
"too many incidences from cursor"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_multiple_edges() {
|
||||||
|
use $crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
let k = 3;
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let vertices = [graph.add_vertex(), graph.add_vertex()];
|
||||||
|
let mut edges = Vec::new();
|
||||||
|
for _ in 0..k {
|
||||||
|
edges.push(graph.add_edge(vertices[0], vertices[1]));
|
||||||
|
}
|
||||||
|
for i in 0..2 {
|
||||||
|
let mut cursor = graph.incidence_cursor(vertices[i]);
|
||||||
|
for j in 0..k {
|
||||||
|
let current = cursor.next(&graph).expect(&format!(
|
||||||
|
"incidence {j} missing, expected {k} incidences for vertex {:?}",
|
||||||
|
vertices[i]
|
||||||
|
));
|
||||||
|
assert_eq!(
|
||||||
|
current.0,
|
||||||
|
vertices[1 - i],
|
||||||
|
"unexpected adjacent vertex of vertex {:?} in incidence {j}",
|
||||||
|
vertices[i]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edges.iter().filter(|e| **e == current.1).count(),
|
||||||
|
1,
|
||||||
|
"unexpected incident edge {:?} of vertex {:?}",
|
||||||
|
current.1,
|
||||||
|
vertices[i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
cursor.next(&graph),
|
||||||
|
None,
|
||||||
|
"too many incidences of {:?} from cursor",
|
||||||
|
vertices[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_copy() {
|
||||||
|
use $crate::traits::{GraphTopology, IncidenceCursor};
|
||||||
|
// Constructs a graph with two vertices connected to `v`.
|
||||||
|
let mut graph = <$T>::new();
|
||||||
|
let v = graph.add_vertex();
|
||||||
|
for _ in 0..2 {
|
||||||
|
let u = graph.add_vertex();
|
||||||
|
graph.add_edge(u, v);
|
||||||
|
}
|
||||||
|
let mut c1 = graph.incidence_cursor(v);
|
||||||
|
assert!(c1.next(&graph).is_some(), "expected first incidence");
|
||||||
|
// Copies cursor mid-traversal.
|
||||||
|
let mut c2 = c1;
|
||||||
|
// Continues iteration with original cursor.
|
||||||
|
assert!(c1.next(&graph).is_some(), "expected second incidence from original cursor");
|
||||||
|
assert!(c1.next(&graph).is_none(), "expected original cursor to be exhausted");
|
||||||
|
// Replays from the copy point with the copied cursor.
|
||||||
|
assert!(c2.next(&graph).is_some(), "expected second incidence from copied cursor");
|
||||||
|
assert!(c2.next(&graph).is_none(), "expected copied cursor to be exhausted");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn incident_vertices_incidences_consistency() {
|
fn incident_vertices_incidences_consistency() {
|
||||||
use $crate::traits::GraphTopology;
|
use $crate::traits::GraphTopology;
|
||||||
@@ -1103,8 +1228,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(|(_, e)| *e != edges[2].0)
|
.filter_map(|(_, e)| (*e != edges[2].0).then_some(*e))
|
||||||
.map(|(_, e)| *e)
|
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.incident_edges(vertices[i]).count(),
|
graph.incident_edges(vertices[i]).count(),
|
||||||
@@ -1137,8 +1261,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(|(v, _)| *v != vertices[2])
|
.filter_map(|(v, e)| (*v != vertices[2]).then_some(*e))
|
||||||
.map(|(_, e)| *e)
|
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.incident_edges(vertices[i]).count(),
|
graph.incident_edges(vertices[i]).count(),
|
||||||
@@ -1213,14 +1336,73 @@ macro_rules! graph_topology_deletion_tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
|
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"unexpected incidence {incidence:?} of vertex {:?} after delete",
|
"unexpected incidence {incidence:?} of vertex {:?} from iterator after delete",
|
||||||
v
|
v
|
||||||
));
|
));
|
||||||
expected.swap_remove(pos);
|
expected.swap_remove(pos);
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
expected.is_empty(),
|
expected.is_empty(),
|
||||||
"expected incidences {:?} of vertex {:?} not matched after delete",
|
"expected incidences {:?} of vertex {:?} not matched by iterator after delete",
|
||||||
|
expected,
|
||||||
|
v
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_after_delete_vertex() {
|
||||||
|
use $crate::traits::GraphTopologyDeletion;
|
||||||
|
let (mut graph, vertices, _, incidences) = make_test_graph();
|
||||||
|
graph.delete_vertex(vertices[2]);
|
||||||
|
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
|
||||||
|
let remaining = incidences[i]
|
||||||
|
.iter()
|
||||||
|
.filter(|(v, _)| *v != vertices[2])
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incidence_cursor_after_delete_edge() {
|
||||||
|
use $crate::traits::GraphTopologyDeletion;
|
||||||
|
let (mut graph, vertices, edges, incidences) = make_test_graph();
|
||||||
|
// Deletes the edge from vertices[1] to vertices[2].
|
||||||
|
graph.delete_edge(edges[2].0);
|
||||||
|
for i in 0..10 {
|
||||||
|
let remaining = incidences[i]
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, e)| *e != edges[2].0)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_vertex_incidence_cursor(
|
||||||
|
graph: &$T,
|
||||||
|
v: <$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
|
mut expected: Vec<(
|
||||||
|
<$T as $crate::traits::GraphTopology>::Vertex,
|
||||||
|
<$T as $crate::traits::GraphTopology>::Edge,
|
||||||
|
)>,
|
||||||
|
) {
|
||||||
|
use $crate::traits::IncidenceCursor;
|
||||||
|
let mut cursor = graph.incidence_cursor(v);
|
||||||
|
while let Some(incidence) = cursor.next(graph) {
|
||||||
|
let pos = expected
|
||||||
|
.iter()
|
||||||
|
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
|
||||||
|
.expect(&format!(
|
||||||
|
"unexpected incidence {incidence:?} of vertex {:?} from cursor after delete",
|
||||||
|
v
|
||||||
|
));
|
||||||
|
expected.swap_remove(pos);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
expected.is_empty(),
|
||||||
|
"expected incidences {:?} of vertex {:?} not matched by cursor after delete",
|
||||||
expected,
|
expected,
|
||||||
v
|
v
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user