use std::fmt::Debug; use std::hash::Hash; use grapherity::algorithms; use grapherity::algorithms::DijkstraResult; use grapherity::maps::ElementMap; use grapherity::testing::fixtures; use grapherity::testing::fixtures::MakeTestGraph; use grapherity::traits::{GraphTopology, Incidence}; macro_rules! dijkstra_tests { ($T:ty) => { dijkstra_tests!(@wrap $T, dijkstra_single_vertex, dijkstra_disconnected, dijkstra_zero_weight_loop, dijkstra, dijkstra_distances, dijkstra_unweighted_single_vertex, dijkstra_unweighted_disconnected, dijkstra_unweighted, dijkstra_distances_unweighted_single_vertex, dijkstra_distances_unweighted_disconnected, dijkstra_distances_unweighted, ); }; (@wrap $T:ty, $($name:ident),* $(,)?) => { $( #[test] fn $name() { super::$name::<$T>(); } )* }; } mod append_graph { use grapherity::models::AppendGraph; dijkstra_tests!(AppendGraph); } mod graph { use grapherity::models::Graph; dijkstra_tests!(Graph); } fn dijkstra_single_vertex() where G::Vertex: Debug + Hash, { let (graph, v) = G::single_vertex(); let result = algorithms::dijkstra(&graph, v, |_| panic!("unexpected call of weight functor")); assert_single_vertex::(&result, v); } fn dijkstra_disconnected() where G::Vertex: Debug + Hash, { let (graph, vertices) = G::disconnected(); let result = algorithms::dijkstra(&graph, vertices[0], |_| { panic!("unexpected call of weight functor") }); assert_single_vertex::(&result, vertices[0]); assert_disconnected::(&result, &vertices[1..3]); } fn dijkstra_zero_weight_loop() where G::Vertex: Debug + Hash, { let (graph, vertices, _, weights) = make_two_edge_path_with_zero_weight_loops::(); let result = algorithms::dijkstra(&graph, vertices[0], |e| weights[e]); assert_single_vertex::(&result, vertices[0]); for i in 1..3 { assert_eq!( result.predecessors[vertices[i]], Some(vertices[i - 1]), "unexpected predecessor of vertex {:?}", vertices[i] ); assert_eq!( result.distances[vertices[i]], Some(i.try_into().unwrap()), "unexpected distance of vertex {:?}", vertices[i] ); } } fn dijkstra() where G::Vertex: Debug + Hash, { let (graph, vertices, _, _, weights) = make_standard_weighted::(); let result = algorithms::dijkstra(&graph, vertices[0], |e| weights[e]); assert_standard_weighted_distances_v0::(&result.distances, &vertices); assert_standard_weighted_predecessors_v0::(&result.predecessors, &vertices); } fn dijkstra_distances() where G::Vertex: Debug + Hash, { let (graph, vertices, _, _, weights) = make_standard_weighted::(); let distances = algorithms::dijkstra_distances(&graph, vertices[0], |e| weights[e]); assert_standard_weighted_distances_v0::(&distances, &vertices); } fn dijkstra_unweighted_single_vertex() where G::Vertex: Debug + Hash, { let (graph, v) = G::single_vertex(); let result = algorithms::dijkstra_unweighted(&graph, v); assert_single_vertex::(&result, v) } fn dijkstra_unweighted_disconnected() where G::Vertex: Debug + Hash, { let (graph, vertices) = G::disconnected(); let result = algorithms::dijkstra_unweighted(&graph, vertices[0]); assert_single_vertex::(&result, vertices[0]); assert_disconnected::(&result, &vertices[1..3]); } fn dijkstra_unweighted() where G::Vertex: Debug + Hash, { let (graph, vertices, _, _) = G::standard(); let result = algorithms::dijkstra_unweighted(&graph, vertices[0]); fixtures::assert_standard_unweighted_distances_v0(|v| result.distances[v], &vertices); fixtures::assert_standard_unweighted_predecessors_v0(&result.predecessors, &vertices); } fn dijkstra_distances_unweighted_single_vertex() where G::Vertex: Hash, { let (graph, v) = G::single_vertex(); let distances = algorithms::dijkstra_distances_unweighted(&graph, v); assert_distances_single_vertex::(&distances, v); } fn dijkstra_distances_unweighted_disconnected() where G::Vertex: Debug + Hash, { let (graph, vertices) = G::disconnected(); let distances = algorithms::dijkstra_distances_unweighted(&graph, vertices[0]); assert_distances_single_vertex::(&distances, vertices[0]); assert_distances_disconnected::(&distances, &vertices[1..3]); } fn dijkstra_distances_unweighted() where G::Vertex: Debug + Hash, { let (graph, vertices, _, _) = G::standard(); let distances = algorithms::dijkstra_distances_unweighted(&graph, vertices[0]); fixtures::assert_standard_unweighted_distances_v0(|v| distances[v], &vertices); } fn assert_single_vertex(result: &DijkstraResult, v: G::Vertex) where G::Vertex: Debug, { assert_distances_single_vertex::(&result.distances, v); assert_eq!( result.predecessors[v], None, "unexpected predecessor of source vertex", ); } fn assert_distances_single_vertex( distances: &ElementMap>, v: G::Vertex, ) { assert_eq!( distances[v], Some(0), "unexpected distance of source vertex" ); } fn assert_disconnected(result: &DijkstraResult, vertices: &[G::Vertex]) where G::Vertex: Debug, { assert_distances_disconnected::(&result.distances, &vertices); for &v in vertices { assert_eq!( result.predecessors[v], None, "unexpected predecessor of disconnected vertex {v:?}", ); } } fn assert_distances_disconnected( distances: &ElementMap>, vertices: &[G::Vertex], ) where G::Vertex: Debug, { for &v in vertices { assert_eq!( distances[v], None, "unexpected distance of disconnected vertex {v:?}", ); } } fn assert_standard_weighted_predecessors_v0( actual: &ElementMap>, vertices: &[G::Vertex], ) where G::Vertex: Debug, { let expected = [ vec![None], vec![Some(vertices[0])], vec![Some(vertices[4])], vec![Some(vertices[1])], vec![Some(vertices[7])], vec![Some(vertices[9])], vec![Some(vertices[3])], vec![Some(vertices[9])], vec![Some(vertices[7])], vec![Some(vertices[6])], ]; for i in 0..10 { assert!( expected[i].contains(&actual[vertices[i]]), "unexpected predecessor {:?} of {:?}", actual[vertices[i]], vertices[i] ); } } fn assert_standard_weighted_distances_v0( actual: &ElementMap>, vertices: &[G::Vertex], ) where G::Vertex: Debug, { let expected = [ Some(0), Some(1), Some(65), Some(4), Some(58), Some(43), Some(14), Some(46), Some(145), Some(29), ]; for i in 0..10 { assert_eq!( actual[vertices[i]], expected[i], "unexpected distance from {:?} to {:?}", vertices[0], vertices[i] ); } } fn make_standard_weighted() -> ( G, [G::Vertex; 10], [(G::Edge, G::Vertex, G::Vertex); 18], [Vec>; 10], ElementMap, ) { let (graph, vertices, edges, incidences) = G::standard(); let mut weights = graph.edge_map(99); for i in [1, 3, 7, 10, 12, 14, 15, 17] { weights[edges[i].0] = i.try_into().unwrap(); } (graph, vertices, edges, incidences, weights) } fn make_two_edge_path_with_zero_weight_loops() -> (G, [G::Vertex; 3], [G::Edge; 4], ElementMap) { let (graph, vertices, edges) = G::two_edge_path_with_loops(); let mut weights = graph.edge_map(1); weights[edges[2]] = 0; weights[edges[3]] = 0; (graph, vertices, edges, weights) }