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 {
|
||||
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 {
|
||||
fn next(&mut self, graph: &Graph) -> Option<(Vertex, Edge)> {
|
||||
graph
|
||||
.step_incidence(&mut self.incidence)
|
||||
.map(|(vs, e)| (graph.vertices.get_idx(vs.0).unwrap(), e))
|
||||
graph.step_incidence(&mut self.incidence).map(|(vs, 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 {
|
||||
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!(
|
||||
vertices.iter().filter(|&x| *x == v).count(),
|
||||
1,
|
||||
"unexpected vertex {v:?} from the iterator"
|
||||
"unexpected vertex {v:?} from iterator"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -339,7 +339,7 @@ macro_rules! graph_topology_tests {
|
||||
.iter()
|
||||
.position(|w| *w == v)
|
||||
.expect(&format!(
|
||||
"unexpected adjacent vertex {v:?} of {:?} from the iterator",
|
||||
"unexpected adjacent vertex {v:?} of {:?} from iterator",
|
||||
vertices[4]
|
||||
));
|
||||
expected_adjacency.swap_remove(i);
|
||||
@@ -347,7 +347,7 @@ macro_rules! graph_topology_tests {
|
||||
assert_eq!(
|
||||
expected_adjacency.len(),
|
||||
0,
|
||||
"expected adjacent vertices {:?} of {:?} were not matched by the iterator",
|
||||
"expected adjacent vertices {:?} of {:?} were not matched by iterator",
|
||||
expected_adjacency,
|
||||
vertices[4]
|
||||
);
|
||||
@@ -484,7 +484,7 @@ macro_rules! graph_topology_tests {
|
||||
assert_eq!(
|
||||
edges.iter().filter(|&&(f, _, _)| f == e).count(),
|
||||
1,
|
||||
"unexpected edge {e:?} from the iterator"
|
||||
"unexpected edge {e:?} from iterator"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -518,14 +518,14 @@ macro_rules! graph_topology_tests {
|
||||
.iter()
|
||||
.position(|f| *f == e)
|
||||
.expect(&format!(
|
||||
"unexpected incident edge {e:?} of vertex {:?} from the iterator",
|
||||
"unexpected incident edge {e:?} of vertex {:?} from iterator",
|
||||
vertices[i]
|
||||
));
|
||||
expected.swap_remove(pos);
|
||||
}
|
||||
assert!(
|
||||
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,
|
||||
vertices[i]
|
||||
);
|
||||
@@ -572,7 +572,7 @@ macro_rules! graph_topology_tests {
|
||||
}
|
||||
assert!(
|
||||
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,
|
||||
vertices[i]
|
||||
);
|
||||
@@ -608,14 +608,14 @@ macro_rules! graph_topology_tests {
|
||||
.iter()
|
||||
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
|
||||
.expect(&format!(
|
||||
"unexpected incidence {incidence:?} of vertex {:?} from the iterator",
|
||||
"unexpected incidence {incidence:?} of vertex {:?} from iterator",
|
||||
vertices[i]
|
||||
));
|
||||
expected.swap_remove(pos);
|
||||
}
|
||||
assert!(
|
||||
expected.is_empty(),
|
||||
"expected incidences {:?} of vertex {:?} were not matched by the iterator",
|
||||
"expected incidences {:?} of vertex {:?} were not matched by iterator",
|
||||
expected,
|
||||
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]
|
||||
fn incident_vertices_incidences_consistency() {
|
||||
use $crate::traits::GraphTopology;
|
||||
@@ -1103,8 +1228,7 @@ macro_rules! graph_topology_deletion_tests {
|
||||
for i in 0..10 {
|
||||
let mut expected: Vec<_> = incidences[i]
|
||||
.iter()
|
||||
.filter(|(_, e)| *e != edges[2].0)
|
||||
.map(|(_, e)| *e)
|
||||
.filter_map(|(_, e)| (*e != edges[2].0).then_some(*e))
|
||||
.collect();
|
||||
assert_eq!(
|
||||
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] {
|
||||
let mut expected: Vec<_> = incidences[i]
|
||||
.iter()
|
||||
.filter(|(v, _)| *v != vertices[2])
|
||||
.map(|(_, e)| *e)
|
||||
.filter_map(|(v, e)| (*v != vertices[2]).then_some(*e))
|
||||
.collect();
|
||||
assert_eq!(
|
||||
graph.incident_edges(vertices[i]).count(),
|
||||
@@ -1213,14 +1336,73 @@ macro_rules! graph_topology_deletion_tests {
|
||||
.iter()
|
||||
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
|
||||
.expect(&format!(
|
||||
"unexpected incidence {incidence:?} of vertex {:?} after delete",
|
||||
"unexpected incidence {incidence:?} of vertex {:?} from iterator after delete",
|
||||
v
|
||||
));
|
||||
expected.swap_remove(pos);
|
||||
}
|
||||
assert!(
|
||||
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,
|
||||
v
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user