use std::fmt::Debug; use crate::maps::ElementMap; use crate::models::{AppendGraph, FrozenGraph}; use crate::traits::{GraphTopology, GraphTopologyAddition, GraphTopologyConversion, Incidence}; pub trait MakeTestGraph: GraphTopology + Sized { /// Returns a versatile, pre-defined standard test graph. // Since this is only a test fixture, the complex return type here is deemed acceptable. #[allow(clippy::type_complexity)] fn standard() -> ( Self, [Self::Vertex; 10], [(Self::Edge, Self::Vertex, Self::Vertex); 18], [Vec>; 10], ); /// Returns an empty graph. fn empty() -> Self; /// Returns a graph with a single vertex and no edges. fn single_vertex() -> (Self, Self::Vertex); /// Returns a graph with two vertices connected by a single edge. fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge); /// Returns a graph with vertices *v0*, *v1*, and *v2*, where *v0* is isolated, and the other /// two are connected by a single edge. fn disconnected() -> (Self, [Self::Vertex; 3]); /// Returns a graph with a single vertex that has a single loop edge. fn loop_edge() -> (Self, Self::Vertex, Self::Edge); /// Returns a graph with two vertices that are connected by `K` multi-edges. fn multiple_edges() -> (Self, [Self::Vertex; 2], [Self::Edge; K]); /// Returns a graph that is a path with three vertices and two edges between them, returned in /// order. fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]); /// Returns a graph that is a path with three vertices *v0*, *v1*, and *v2*, and two edges /// *e0 = (v0, v1)* and *e1 = (v1, v2)* between them, and two loops on the first two vertices /// *e2 = (v0, v0)* and *e3 = (v1, v1)*. fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]); } impl MakeTestGraph for G { fn standard() -> ( Self, [Self::Vertex; 10], [(Self::Edge, Self::Vertex, Self::Vertex); 18], [Vec>; 10], ) { let mut graph = Self::default(); graph.reserve_vertices(10); graph.reserve_edges(18); let vertices = core::array::from_fn(|_| graph.add_vertex()); let edges = [ (vertices[0], vertices[1]), (vertices[0], vertices[1]), (vertices[1], vertices[2]), (vertices[1], vertices[3]), (vertices[1], vertices[4]), (vertices[2], vertices[2]), (vertices[2], vertices[4]), (vertices[2], vertices[4]), (vertices[2], vertices[5]), (vertices[2], vertices[6]), (vertices[3], vertices[6]), (vertices[4], vertices[4]), (vertices[4], vertices[7]), (vertices[4], vertices[8]), (vertices[5], vertices[9]), (vertices[6], vertices[9]), (vertices[7], vertices[8]), (vertices[7], vertices[9]), ] .map(|(u, v)| (graph.add_edge(u, v), u, v)); let i = |vertex, edge| Incidence { vertex, edge }; let incidences = [ vec![i(vertices[1], edges[0].0), i(vertices[1], edges[1].0)], vec![ i(vertices[0], edges[0].0), i(vertices[0], edges[1].0), i(vertices[2], edges[2].0), i(vertices[3], edges[3].0), i(vertices[4], edges[4].0), ], vec![ i(vertices[2], edges[5].0), i(vertices[2], edges[5].0), i(vertices[1], edges[2].0), i(vertices[4], edges[6].0), i(vertices[4], edges[7].0), i(vertices[5], edges[8].0), i(vertices[6], edges[9].0), ], vec![i(vertices[1], edges[3].0), i(vertices[6], edges[10].0)], vec![ i(vertices[1], edges[4].0), i(vertices[2], edges[6].0), i(vertices[2], edges[7].0), i(vertices[4], edges[11].0), i(vertices[4], edges[11].0), i(vertices[7], edges[12].0), i(vertices[8], edges[13].0), ], vec![i(vertices[2], edges[8].0), i(vertices[9], edges[14].0)], vec![ i(vertices[2], edges[9].0), i(vertices[3], edges[10].0), i(vertices[9], edges[15].0), ], vec![ i(vertices[4], edges[12].0), i(vertices[8], edges[16].0), i(vertices[9], edges[17].0), ], vec![i(vertices[4], edges[13].0), i(vertices[7], edges[16].0)], vec![ i(vertices[5], edges[14].0), i(vertices[6], edges[15].0), i(vertices[7], edges[17].0), ], ]; (graph, vertices, edges, incidences) } fn empty() -> Self { Self::default() } fn single_vertex() -> (Self, Self::Vertex) { let mut graph = Self::default(); let v = graph.add_vertex(); (graph, v) } fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) { let mut graph = Self::default(); graph.reserve_vertices(2); let vertices = core::array::from_fn(|_| graph.add_vertex()); let e = graph.add_edge(vertices[0], vertices[1]); (graph, vertices, e) } fn disconnected() -> (Self, [Self::Vertex; 3]) { let mut graph = Self::default(); graph.reserve_vertices(3); let vertices = core::array::from_fn(|_| graph.add_vertex()); graph.add_edge(vertices[1], vertices[2]); (graph, vertices) } fn loop_edge() -> (Self, Self::Vertex, Self::Edge) { let mut graph = Self::default(); let v = graph.add_vertex(); let e = graph.add_edge(v, v); (graph, v, e) } fn multiple_edges() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) { let mut graph = Self::default(); graph.reserve_vertices(2); graph.reserve_edges(K); let vertices = core::array::from_fn(|_| graph.add_vertex()); let edges = core::array::from_fn(|_| graph.add_edge(vertices[0], vertices[1])); (graph, vertices, edges) } fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) { let mut graph = Self::default(); graph.reserve_vertices(3); graph.reserve_edges(2); let vertices = core::array::from_fn(|_| graph.add_vertex()); let edges = core::array::from_fn(|i| graph.add_edge(vertices[i], vertices[i + 1])); (graph, vertices, edges) } fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) { let mut graph = Self::default(); graph.reserve_vertices(3); graph.reserve_edges(4); let vertices = core::array::from_fn(|_| graph.add_vertex()); // Path edges. let e0 = graph.add_edge(vertices[0], vertices[1]); let e1 = graph.add_edge(vertices[1], vertices[2]); // Loops. let e2 = graph.add_edge(vertices[0], vertices[0]); let e3 = graph.add_edge(vertices[1], vertices[1]); (graph, vertices, [e0, e1, e2, e3]) } } impl MakeTestGraph for FrozenGraph { fn standard() -> ( Self, [Self::Vertex; 10], [(Self::Edge, Self::Vertex, Self::Vertex); 18], [Vec>; 10], ) { let (build, vertices, edges, incidences) = AppendGraph::standard(); let (graph, iso) = FrozenGraph::convert(&build); ( graph, vertices.map(|v| iso.vertex(v)), edges.map(|(e, u, v)| (iso.edge(e), iso.vertex(u), iso.vertex(v))), incidences.map(|vec| { vec.iter() .map(|i| Incidence { vertex: iso.vertex(i.vertex), edge: iso.edge(i.edge), }) .collect() }), ) } fn empty() -> Self { FrozenGraph::from(&AppendGraph::empty()) } fn single_vertex() -> (Self, Self::Vertex) { let (build, v) = AppendGraph::single_vertex(); let (graph, iso) = FrozenGraph::convert(&build); (graph, iso.vertex(v)) } fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) { let (build, vertices, e) = AppendGraph::single_edge(); let (graph, iso) = FrozenGraph::convert(&build); (graph, vertices.map(|v| iso.vertex(v)), iso.edge(e)) } fn disconnected() -> (Self, [Self::Vertex; 3]) { let (build, vertices) = AppendGraph::disconnected(); let (graph, iso) = FrozenGraph::convert(&build); (graph, vertices.map(|v| iso.vertex(v))) } fn loop_edge() -> (Self, Self::Vertex, Self::Edge) { let (build, v, e) = AppendGraph::loop_edge(); let (graph, iso) = FrozenGraph::convert(&build); (graph, iso.vertex(v), iso.edge(e)) } fn multiple_edges() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) { let (build, vertices, edges) = AppendGraph::multiple_edges(); let (graph, iso) = FrozenGraph::convert(&build); ( graph, vertices.map(|v| iso.vertex(v)), edges.map(|e| iso.edge(e)), ) } fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) { let (build, vertices, edges) = AppendGraph::two_edge_path(); let (graph, iso) = FrozenGraph::convert(&build); ( graph, vertices.map(|v| iso.vertex(v)), edges.map(|e| iso.edge(e)), ) } fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) { let (build, vertices, edges) = AppendGraph::two_edge_path_with_loops(); let (graph, iso) = FrozenGraph::convert(&build); ( graph, vertices.map(|v| iso.vertex(v)), edges.map(|e| iso.edge(e)), ) } } pub fn assert_standard_unweighted_distances_v0(actual_for: F, vertices: &[V]) where V: Debug + Copy, F: Fn(V) -> Option, { let expected = [0, 1, 2, 2, 2, 3, 3, 3, 3, 4]; for (i, v) in vertices.iter().enumerate() { assert_eq!( actual_for(*v), Some(expected[i]), "unexpected distance from vertex {:?} to vertex {:?}", vertices[0], vertices[i] ); } } pub fn assert_standard_unweighted_predecessors_v0( actual: &ElementMap>, vertices: &[V], ) { let expected = [ vec![None], vec![Some(vertices[0])], vec![Some(vertices[1])], vec![Some(vertices[1])], vec![Some(vertices[1])], vec![Some(vertices[2])], vec![Some(vertices[2]), Some(vertices[3])], vec![Some(vertices[4])], vec![Some(vertices[4])], vec![Some(vertices[5]), Some(vertices[6]), Some(vertices[7])], ]; assert_eq!(vertices.len(), expected.len()); for (&v, exp) in vertices.iter().zip(&expected) { assert!( exp.contains(&actual[v]), "unexpected predecessor {:?} of vertex {v:?}", actual[v] ); } } /// Asserts that the path given as `path_option` is a valid path in `graph` /// /// Walks the path and asserts that it is `Some`, that it is empty if and only if /// `source == target`, that its edges are pairwise incident in the order given, and that it starts /// at `source` and ends at `target`. pub fn assert_valid_path( graph: &G, path_option: Option>, source: G::Vertex, targets: &[G::Vertex], ) -> Vec where G::Vertex: Debug, G::Edge: Debug, { let path = path_option.unwrap_or_else(|| { panic!( "path should exist between source vertex {source:?} and connected targets {targets:?}" ) }); if targets.contains(&source) { assert!( path.is_empty(), "path from source to itself should be empty" ); } else { assert!(!path.is_empty(), "path should not be empty"); // Walks the path: tracks current vertex, confirms each edge is incident to it. let mut current = source; for (i, &e) in path.iter().enumerate() { let (u, v) = graph.incident_vertices(e); assert_ne!(u, v, "path should not contain loop edge {e:?}"); assert!( u == current || v == current, "path edge {e:?} (index {i}, vertices {u:?} to {v:?}) not incident to vertex {current:?}" ); current = if u == current { v } else { u }; } assert!( targets.contains(¤t), "path should end at a target vertex ({targets:?}), but ended at {current:?}" ); } path } /// Asserts that the two graphs are isomorphic. /// /// `vertices` must contain all vertices of the `original` graph, and `vertex_map` must for any /// vertex in the `original` graph return the corresponding vertex in the `other` graph. /// /// `edges` must contain all edges and their endpoints of the `original` graph, and `edge_map` must /// for any edge in the `original` graph return the corresponding edge in the `other` graph. pub fn assert_isomorphic( original: &G, other: &H, vertices: &[G::Vertex], vertex_map: impl Fn(G::Vertex) -> H::Vertex, edges: &[(G::Edge, G::Vertex, G::Vertex)], edge_map: impl Fn(G::Edge) -> H::Edge, ) where G::Vertex: Debug, G::Edge: Debug, H::Vertex: Debug, H::Edge: Debug, { assert_eq!( original.vertex_count(), vertices.len(), "precondition: all vertices of the original must be provided" ); assert_eq!( original.edge_count(), edges.len(), "precondition: all edges of the original must be provided" ); assert_eq!( other.vertex_count(), original.vertex_count(), "other graph should have the same vertex count as the original" ); assert_eq!( other.edge_count(), original.edge_count(), "other graph should have the same edge count as the original" ); for &v in vertices { assert_eq!( other.degree(vertex_map(v)), original.degree(v), "vertex {v:?} should have the same degree in the other graph as in the original" ); let mut expected: Vec<_> = original.incidences(v).collect(); for incidence in other.incidences(vertex_map(v)) { let pos = expected .iter() .position(|x| { vertex_map(x.vertex) == incidence.vertex && edge_map(x.edge) == incidence.edge }) .unwrap_or_else(|| { panic!("unexpected incidence {incidence:?} of vertex {v:?} in the other graph") }); expected.swap_remove(pos); } assert!( expected.is_empty(), "expected incidences {expected:?} of vertex {v:?} are missing in the other graph" ); } for &(e, u, v) in edges { assert_eq!( other.incident_vertices(edge_map(e)), (vertex_map(u), vertex_map(v)), "edge {e:?} should have the same incident vertices in the other graph as in the original" ); } } /// Asserts that mutation of the two graphs happens independently. /// /// `vertices` must contain all vertices of the `original` graph, which must not be empty, and /// `vertex_map` must for any vertex in the `original` graph return the corresponding vertex in the /// `other` graph. pub fn assert_mutation_independent( original: &mut G, other: &mut H, vertices: &[G::Vertex], vertex_map: impl Fn(G::Vertex) -> H::Vertex, ) { let edge_count = original.edge_count(); assert_eq!( original.vertex_count(), vertices.len(), "precondition: all vertices of the original must be provided" ); assert!( !vertices.is_empty(), "precondition: at least one vertex must be provided" ); let u = other.add_vertex(); other.add_edge(vertex_map(vertices[0]), u); assert_eq!( other.vertex_count(), vertices.len() + 1, "precondition: vertex count of other graph" ); assert_eq!( other.edge_count(), edge_count + 1, "precondition: edge count of other graph" ); assert_eq!( original.vertex_count(), vertices.len(), "mutating the other graph should not affect the vertex count of the original" ); assert_eq!( original.edge_count(), edge_count, "mutating the other graph should not affect the edge count of the original" ); let v = original.add_vertex(); original.add_edge(vertices[0], v); assert_eq!( original.vertex_count(), vertices.len() + 1, "precondition: vertex count of original" ); assert_eq!( original.edge_count(), edge_count + 1, "precondition: edge count of original" ); assert_eq!( other.vertex_count(), vertices.len() + 1, "mutating the original should not affect the vertex count of the other graph" ); assert_eq!( other.edge_count(), edge_count + 1, "mutating the original should not affect the edge count of the other graph" ); }