15 #include <catch2/catch.hpp>
22 auto fu1 = mrrg.
insert(
"fu1");
23 auto fu2 = mrrg.
insert(
"fu2");
25 auto i_a = mrrg.
insert(
"i_a");
26 auto i_b = mrrg.
insert(
"i_b");
38 auto fu1 = mrrg.
insert(
"fu1");
39 auto fu2 = mrrg.
insert(
"fu2");
41 auto f1out = mrrg.
insert(
"f1out");
42 auto r_a = mrrg.
insert(
"r_a");
43 auto r_b = mrrg.
insert(
"r_b");
44 auto f2in = mrrg.
insert(
"f2in");
46 mrrg.
link(fu1, f1out);
47 mrrg.
link(f1out, r_a);
48 mrrg.
link(f1out, r_b);
58 auto fu1 = g.
insert(
"fu1");
60 auto fu1_out_a = g.
insert(fu1,
"fu1_out_a");
61 auto fu1_out_b = g.
insert(fu1,
"fu1_out_b");
82 auto g = makeGraph_Loop3Node();
88 auto g = makeGraph_12x21();
95 auto fu1 = mrrg.
insert(
"fu1");
97 auto fu1_out_a = mrrg.
insert(fu1,
"fu1_out_a");
98 auto fu1_out_b = mrrg.
insert(fu1,
"fu1_out_b");
112 auto fu1 = mrrg.
insert(
"fu1");
114 auto fu1_out = mrrg.
insert(fu1,
"fu1_out");
115 auto xb_1_a = mrrg.
insert(fu1_out,
"xb_1_a");
116 auto xb_1_b = mrrg.
insert(fu1_out,
"xb_1_b");
121 auto fu2 = mrrg.
insert(fu2_in,
"fu2");
129 auto fu1 = mrrg.
insert(
"fu1");
131 auto fu1_out_a = mrrg.
insert(fu1,
"fu1_out_a");
132 auto fu1_out_b = mrrg.
insert(fu1,
"fu1_out_b");
148 auto fu1 = mrrg.
insert(
"fu1");
149 auto fu2 = mrrg.
insert(
"fu2");
151 auto split = mrrg.
insert(
"split");
152 auto b1_1 = mrrg.
insert(
"b1_1");
153 auto b1_2 = mrrg.
insert(
"b1_2");
154 auto b1_3 = mrrg.
insert(
"b1_3");
155 auto join = mrrg.
insert(
"join");
157 mrrg.
link(fu1, split);
158 mrrg.
link(split, b1_1);
159 mrrg.
link(b1_1, b1_2);
160 mrrg.
link(b1_2, b1_3);
161 mrrg.
link(b1_3, join);
162 mrrg.
link(split, join);
163 mrrg.
link(join, fu2);
170 auto fu1 = mrrg.
insert(
"fu1");
171 auto fu2 = mrrg.
insert(
"fu2");
173 auto split = mrrg.
insert(
"split");
174 auto c_start = mrrg.
insert(
"c_start");
175 auto c1 = mrrg.
insert(
"c1");
176 auto c_end = mrrg.
insert(
"c_end");
177 auto c_rev = mrrg.
insert(
"c_rev");
178 auto join = mrrg.
insert(
"join");
180 mrrg.
link(fu1, split);
181 mrrg.
link(split, join);
182 mrrg.
link(join, fu2);
184 mrrg.
link(split, c_start);
185 mrrg.
link(c_start, c1);
186 mrrg.
link(c1, c_end);
187 mrrg.
link(c_end, c_rev);
188 mrrg.
link(c_end, join);
190 mrrg.
link(c_rev, c_start);
195 template<
typename Result,
typename Expected>
196 void checkAllPathsFound(
const Result& result,
const Expected& expected_paths) {
197 std::unordered_set<std::ptrdiff_t> expected_paths_found;
199 for (
auto& path : result) {
200 std::ptrdiff_t i = 0;
201 for (
auto it =
begin(expected_paths); it !=
end(expected_paths); ++it, ++i) {
204 expected_paths_found.
insert(i);
209 CHECK((std::ptrdiff_t)expected_paths_found.size() == (std::ptrdiff_t)expected_paths.size());
210 CHECK((std::ptrdiff_t)result.size() == (std::ptrdiff_t)expected_paths.size());
218 GIVEN (
"A 121 graph") {
219 const auto graph = makeGraph_121();
224 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
225 {
"fu1",
"i_a",
"fu2", },
226 {
"fu1",
"i_b",
"fu2", },
229 checkAllPathsFound(result, expected_paths);
232 GIVEN (
"A 121 graph and choice A is ignored") {
233 const auto graph = makeGraph_121();
238 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
239 {
"fu1",
"i_b",
"fu2", },
242 checkAllPathsFound(result, expected_paths);
245 GIVEN (
"A 11211 graph") {
246 const auto graph = makeGraph_11211();
251 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
252 {
"fu1",
"f1out",
"r_a",
"f2in",
"fu2", },
253 {
"fu1",
"f1out",
"r_b",
"f2in",
"fu2", },
256 checkAllPathsFound(result, expected_paths);
259 GIVEN (
"A graph with uneven branches") {
260 const auto graph = makeGraph_2unevenBranches();
265 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
266 {
"fu1",
"split",
"b1_1",
"b1_2",
"b1_3",
"join",
"fu2", },
267 {
"fu1",
"split",
"join",
"fu2", },
270 checkAllPathsFound(result, expected_paths);
273 GIVEN (
"A graph with a cycle in one branch") {
274 const auto graph = makeGraph_2branches1withCycle();
279 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
280 {
"fu1",
"split",
"join",
"fu2", },
281 {
"fu1",
"split",
"c_start",
"c1",
"c_end",
"join",
"fu2", },
284 checkAllPathsFound(result, expected_paths);
287 GIVEN (
"A graph with uneven branches") {
288 const auto graph = makeGraph_2unevenBranches();
290 WHEN (
"Finding one path") {
294 THEN (
"the shortest path should be found") {
295 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
296 {
"fu1",
"split",
"join",
"fu2", },
299 checkAllPathsFound(result, expected_paths);
304 GIVEN (
"A graph with crossbars") {
305 const auto graph = makeGraph_12x2x2x21();
306 for (
int i = 0; i <= 35; i += 1) {
307 WHEN (
"Finding " << i <<
" path(s)") {
311 const auto expected_num_paths = std::min(i,16);
312 THEN (expected_num_paths <<
" path(s) should be found") {
313 CHECK(result.size() == expected_num_paths);
319 GIVEN(
"A 12x21 graph") {
320 const auto graph = makeGraph_12x21();
321 const int num_paths = GENERATE(range(0,4+1));
322 WHEN (
"Finding " << num_paths <<
" path(s), exept paths with both fu1_out_a and xb_1_a") {
325 const auto result = g_algos.findNShortestPaths(num_paths, graph, sources,
findSingleVertex(
"fu2"), v, [](
const auto& path) {
326 return std::find(path.begin(), path.end(),
"fu1_out_a") != path.end() || std::find(path.begin(), path.end(),
"xb_1_a") != path.end();
329 THEN (
"some paths may be found") {
330 CHECK(result.size() == std::min(3,num_paths));
335 GIVEN(
"A 112x211 graph") {
336 const auto graph = makeGraph_112x211();
337 const int num_paths = GENERATE(range(0,5+1));
338 WHEN (
"Finding " << num_paths <<
" path(s), exept paths with both xb_1_a and xb_2_a") {
341 const auto result = g_algos.findNShortestPaths(num_paths, graph, sources,
findSingleVertex(
"fu2"), v, [](
const auto& path) {
342 return std::find(path.begin(), path.end(),
"xb_1_a") != path.end() || std::find(path.begin(), path.end(),
"xb_2_a") != path.end();
345 THEN (
"some paths may be found") {
346 CHECK(result.size() == std::min(3,num_paths));
351 GIVEN(
"A 112x211 graph") {
352 const auto graph = makeGraph_112x211();
353 const int num_paths = GENERATE(range(0,5+1));
354 WHEN (
"Finding " << num_paths <<
" path(s), exept paths with xb_2_a") {
357 const auto result = g_algos.findNShortestPaths(num_paths, graph, sources,
findSingleVertex(
"fu2"), v, [](
const auto& path) {
358 return std::find(path.begin(), path.end(),
"xb_2_a") != path.end();
361 THEN (
"some paths may be found") {
362 CHECK(result.size() == std::min(2,num_paths));
367 GIVEN(
"A 12x2x21 graph") {
368 const auto graph = makeGraph_12x2x21();
369 const int num_paths = GENERATE(range(0,7+1));
370 WHEN (
"Finding " << num_paths <<
" path(s), exept paths with both fu1_out_a and xb_2_a") {
373 const auto result = g_algos.findNShortestPaths(num_paths, graph, sources,
findSingleVertex(
"fu2"), v, [](
const auto& path) {
374 return std::find(path.begin(), path.end(),
"fu1_out_a") != path.end() || std::find(path.begin(), path.end(),
"xb_2_a") != path.end();
377 THEN (
"some paths may be found") {
378 CHECK(result.size() == std::min(6,num_paths));
383 GIVEN(
"A 12x21 graph with loop") {
384 const auto graph = makeGraph_12x21_with_loop();
385 const int num_paths = GENERATE(range(0,5+1));
386 WHEN (
"Finding " << num_paths <<
" loop path(s)") {
389 const auto result = g_algos.findNShortestPaths(num_paths, graph, sources,
findSingleVertex(
"fu1"), v);
391 THEN (
"some paths may be found") {
392 CHECK(result.size() == std::min(4,num_paths));
398 SCENARIO (
"findNShortestSimplePaths Tests",
"[graph]") {
401 GIVEN (
"A 121 graph") {
402 const auto graph = makeGraph_121();
407 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
408 {
"fu1",
"i_a",
"fu2", },
409 {
"fu1",
"i_b",
"fu2", },
412 checkAllPathsFound(result, expected_paths);
415 GIVEN (
"A 121 graph and choice A is ignored") {
416 const auto graph = makeGraph_121();
421 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
422 {
"fu1",
"i_b",
"fu2", },
425 checkAllPathsFound(result, expected_paths);
428 GIVEN (
"A 11211 graph") {
429 const auto graph = makeGraph_11211();
434 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
435 {
"fu1",
"f1out",
"r_a",
"f2in",
"fu2", },
436 {
"fu1",
"f1out",
"r_b",
"f2in",
"fu2", },
439 checkAllPathsFound(result, expected_paths);
442 GIVEN (
"A graph with uneven branches") {
443 const auto graph = makeGraph_2unevenBranches();
448 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
449 {
"fu1",
"split",
"b1_1",
"b1_2",
"b1_3",
"join",
"fu2", },
450 {
"fu1",
"split",
"join",
"fu2", },
453 checkAllPathsFound(result, expected_paths);
456 GIVEN (
"A graph with a cycle in one branch") {
457 const auto graph = makeGraph_2branches1withCycle();
462 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
463 {
"fu1",
"split",
"join",
"fu2", },
464 {
"fu1",
"split",
"c_start",
"c1",
"c_end",
"join",
"fu2", },
467 checkAllPathsFound(result, expected_paths);
470 GIVEN (
"A graph with uneven branches") {
471 const auto graph = makeGraph_2unevenBranches();
473 WHEN (
"Finding one path") {
477 THEN (
"the shortest path should be found") {
478 std::vector<std::vector<StringIDGraph::VertexID>> expected_paths {
479 {
"fu1",
"split",
"join",
"fu2", },
482 checkAllPathsFound(result, expected_paths);
487 GIVEN (
"A graph with crossbars") {
488 const auto graph = makeGraph_12x2x2x21();
489 for (
int i = 0; i <= 35; i += 1) {
490 WHEN (
"Finding " << i <<
" path(s)") {
494 const auto expected_num_paths = std::min(i,16);
495 THEN (expected_num_paths <<
" path(s) should be found") {
496 CHECK(result.size() == expected_num_paths);
507 GIVEN (
"A 121 graph") {
508 const auto graph = makeGraph_121();
512 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
514 std::vector<StringIDGraph::VertexID> expected_path {
521 GIVEN (
"A 121 graph and choice A is ignored") {
522 const auto graph = makeGraph_121();
526 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
528 std::vector<StringIDGraph::VertexID> expected_path {
535 GIVEN (
"A 11211 graph") {
536 const auto graph = makeGraph_11211();
540 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
542 std::vector<StringIDGraph::VertexID> expected_path {
543 "fu1",
"f1out",
"r_a",
"f2in",
"fu2",
549 GIVEN (
"A graph with uneven branches") {
550 const auto graph = makeGraph_2unevenBranches();
554 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
556 std::vector<StringIDGraph::VertexID> expected_path {
557 "fu1",
"split",
"b1_1",
"b1_2",
"b1_3",
"join",
"fu2",
563 GIVEN (
"A graph with uneven branches - find the other path") {
564 const auto graph = makeGraph_2unevenBranches();
568 const auto vertex_data = g_algos.depthFirstVisit(graph, *
begin(sources),
findSingleVertex(
"fu2"), path_ignorer);
569 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
571 std::vector<StringIDGraph::VertexID> expected_path {
572 "fu1",
"split",
"join",
"fu2",
578 GIVEN (
"A graph with a cycle in one branch") {
579 const auto graph = makeGraph_2branches1withCycle();
583 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
585 std::vector<StringIDGraph::VertexID> expected_path {
586 "fu1",
"split",
"join",
"fu2",
593 SCENARIO (
"wavedBreadthFirstVisit Tests",
"[graph]") {
596 GIVEN (
"A 121 graph") {
597 const auto graph = makeGraph_121();
601 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
603 std::vector<StringIDGraph::VertexID> expected_path {
610 GIVEN (
"A 11211 graph") {
611 const auto graph = makeGraph_11211();
615 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
617 std::vector<StringIDGraph::VertexID> expected_path {
618 "fu1",
"f1out",
"r_a",
"f2in",
"fu2",
624 GIVEN (
"A graph with uneven branches") {
625 const auto graph = makeGraph_2unevenBranches();
629 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
631 std::vector<StringIDGraph::VertexID> expected_path {
632 "fu1",
"split",
"join",
"fu2",
638 GIVEN (
"A graph with a cycle in one branch") {
639 const auto graph = makeGraph_2branches1withCycle();
643 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
645 std::vector<StringIDGraph::VertexID> expected_path {
646 "fu1",
"split",
"join",
"fu2",
653 template <
typename VertexID = StringIDGraph::VertexID,
typename Cost =
int>
655 return [=](
const VertexID& v) {
656 auto lookup = costs.find(v);
657 return lookup == costs.end() ? default_ : lookup->second;
664 GIVEN (
"A 121 graph -- prefer b") {
665 const auto graph = makeGraph_121();
668 const auto vertex_coster = [](
const auto& v) {
return v ==
"i_b" ? 1 : 2; };
670 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
672 std::vector<StringIDGraph::VertexID> expected_path {
676 CHECK(result.path == expected_path);
679 GIVEN (
"A 121 graph -- prefer a") {
680 const auto graph = makeGraph_121();
683 const auto vertex_coster = [](
const auto& v) {
return v ==
"i_a" ? 1 : 2; };
685 const auto result = g_algos.singleTraceback(sources,
"fu2", vertex_data);
687 std::vector<StringIDGraph::VertexID> expected_path {
691 CHECK(result.path == expected_path);
694 GIVEN (
"A 12x21 graph -- neg cost to other source") {
695 const auto graph = makeGraph_12x21();
697 const auto sources = std::set<StringIDGraph::VertexID>{
"fu1",
"xb_1_a"};
698 const auto vertex_coster = [](
const auto& v) {
699 if (v ==
"fu1_out_a")
return -2;
700 if (v ==
"xb_1_b")
return 2;
704 const auto result = g_algos.singleTraceback(
singleItemSet(
"fu1"),
"fu2", vertex_data);
706 std::vector<StringIDGraph::VertexID> expected_path {
707 "fu1",
"fu1_out_a",
"xb_1_a",
"fu2",
710 CHECK(result.path == expected_path);
717 struct BFSCallbackTestDescription {
720 std::string graph_description;
721 std::set<std::string> sources;
722 int expected_num_edges_not_visited_and_not_skipped;
723 std::vector<std::set<std::string>> vertex_order;
727 struct BFSCallbackTest_UseWavedBreadthFirstVisit {
728 std::string name =
"wavedBreadthFirstVisit";
730 template<
typename GAlgo,
typename G,
typename Sources,
typename V>
731 decltype(
auto) invoke(GAlgo&& g_algos, G&& graph, Sources&& sources, V&& visitor)
const {
732 return g_algos.wavedBreadthFirstVisit(graph, sources,
visitAllVertices(), visitor);
735 template<
typename Test,
typename RV,
typename AllEdges>
736 void check_result(
const Test& test,
const RV& bfv_result,
const AllEdges& edges_visited_or_skipped)
const {
738 std::decay_t<decltype(edges_visited_or_skipped)> edges_in_result;
739 for (
const auto& v_and_vdata : bfv_result) {
740 for (
const auto& fanin : v_and_vdata.second.fanin) {
741 edges_in_result.
insert({fanin, v_and_vdata.first});
744 CHECK(edges_in_result == edges_visited_or_skipped);
748 struct BFSCallbackTest_UseBreadthFirstVisit {
749 std::string name =
"breadthFirstVisit";
751 template<
typename GAlgo,
typename G,
typename Sources,
typename V>
752 decltype(
auto) invoke(GAlgo&& g_algos, G&& graph, Sources&& sources, V&& visitor)
const {
753 return g_algos.breadthFirstVisit(graph, sources, visitor);
756 template<
typename Test,
typename RV,
typename AllEdges>
757 void check_result(
const Test& test,
const RV& bfv_result,
const AllEdges& edges_visited_or_skipped)
const {
758 (void)test; (void)bfv_result; (void)edges_visited_or_skipped;
762 struct BFSCallbackTest_UseDijkstraVisit_UniformCost {
763 std::string name =
"dijkstraVisit";
765 template<
typename GAlgo,
typename G,
typename Sources,
typename V>
766 decltype(
auto) invoke(GAlgo&& g_algos, G&& graph, Sources&& sources, V&& visitor)
const {
767 const auto vertex_coster = [](
auto&&) {
return 1; };
768 return g_algos.dijkstraVisit(graph, sources, vertex_coster, visitor);
771 template<
typename Test,
typename RV,
typename AllEdges>
772 void check_result(
const Test& test,
const RV& bfv_result,
const AllEdges& edges_visited_or_skipped)
const {
773 (void)test; (void)bfv_result; (void)edges_visited_or_skipped;
780 BFSCallbackTest_UseWavedBreadthFirstVisit,
781 BFSCallbackTest_UseBreadthFirstVisit,
782 BFSCallbackTest_UseDijkstraVisit_UniformCost
784 const auto& test = GENERATE(values<BFSCallbackTestDescription>({
785 {
"121", {
"fu1"}, 0, { {
"fu1"}, {
"i_a",
"i_b"}, {
"fu2"} }, makeGraph_121() },
786 {
"121", {
"fu1",
"i_a"}, 0, { {
"fu1",
"i_a"}, {
"i_b",
"fu2"} }, makeGraph_121() },
787 {
"121", {
"i_a"}, 3, { {
"i_a"}, {
"fu2"} }, makeGraph_121() },
788 {
"121", {
"fu1",
"i_a",
"i_b",
"fu2"}, 0, { {
"fu1",
"i_a",
"i_b",
"fu2"} }, makeGraph_121() },
789 {
"Loop3Node", {
"a"}, 0, { {
"a"}, {
"b"}, {
"c"} }, makeGraph_Loop3Node() },
790 {
"Loop3Node", {
"a",
"c"}, 0, { {
"a",
"c"}, {
"b"}, }, makeGraph_Loop3Node() },
791 {
"Loop3NodeWithExtraFanout", {
"a"}, 0, { {
"a"}, {
"b",
"d"}, {
"c"} }, makeGraph_Loop3NodeWithExtraFanout() },
792 {
"2unevenBranches", {
"fu1"}, 0, { {
"fu1"}, {
"split"}, {
"b1_1",
"join"}, {
"b1_2",
"fu2"}, {
"b1_3"} }, makeGraph_2unevenBranches() },
793 {
"2branches1withCycle", {
"fu1"}, 0, { {
"fu1"}, {
"split"}, {
"c_start",
"join"}, {
"fu2",
"c1"}, {
"c_end"}, {
"c_rev"} }, makeGraph_2branches1withCycle() },
796 using Catch::Detail::stringify;
797 const auto g_algos =
typename std::remove_reference_t<decltype(test)>::MyGraphAlgorithms();
798 using VertexID =
typename std::remove_reference_t<decltype(test)>::VertexID;
801 const TestType bfv_name_and_func;
803 GIVEN(
"A " << test.graph_description <<
" graph") {
804 const auto& graph = test.graph;
805 WHEN(
"Running " << bfv_name_and_func.name <<
", starting at " << stringify(test.sources)) {
806 auto eventVisitor = Visitor{};
807 const auto bfv_result = bfv_name_and_func.invoke(g_algos, graph, test.sources, eventVisitor);
809 THEN(
"Vertices and edges were visited in the right order") {
810 auto vertices_visited = std::set<VertexID>{};
811 auto edges_visited = std::set<std::pair<VertexID,VertexID>>{};
812 auto edges_skipped = std::set<std::pair<VertexID,VertexID>>{};
813 auto vertices_visited_this_wave = std::set<VertexID>{};
814 auto targets_of_edges_in_this_wave = std::set<VertexID>{};
815 auto targets_of_edges_visited = std::set<VertexID>{};
816 auto vertex_order_it = test.vertex_order.begin();
818 for (
const auto& event : eventVisitor.examine_order) {
819 INFO(
"Checking event " << event);
822 && event.type != Visitor::Event::onExamine
823 && event.type != Visitor::Event::onSkipped
824 && event.type != Visitor::Event::onExamineEdge
825 && event.type != Visitor::Event::onSkippedEdge
826 && event.type != Visitor::Event::onWaveEnd
830 REQUIRE(vertex_order_it != test.vertex_order.end());
833 "In wave " << 1 + std::distance(test.vertex_order.begin(), vertex_order_it)
834 <<
"/" << test.vertex_order.size() <<
": " << stringify(*vertex_order_it)
838 std::decay_t<decltype(*vertex_order_it)> expected_next_wave;
839 if (std::next(vertex_order_it) != test.vertex_order.end()) {
840 expected_next_wave = *std::next(vertex_order_it);
843 INFO(
"Expected next wave " << stringify(expected_next_wave));
845 const auto advance_wave = [&]{
847 CHECK(vertices_visited_this_wave == *vertex_order_it);
848 CHECK(expected_next_wave == targets_of_edges_in_this_wave);
852 vertices_visited_this_wave.clear();
853 targets_of_edges_in_this_wave.clear();
856 if (event.type == Visitor::Event::onExamine) {
857 const auto& v =
event.v1;
859 if (not vertex_order_it->count(v)) {
864 CHECK(vertex_order_it->count(v));
866 CHECK(vertices_visited_this_wave.insert(v).second);
868 CHECK(vertices_visited.insert(v).second);
871 }
else if (event.type == Visitor::Event::onSkipped) {
872 const auto& v =
event.v1;
875 CHECK(vertices_visited.count(v));
877 }
else if (event.type == Visitor::Event::onExamineEdge) {
878 const auto next_wave_it = std::next(vertex_order_it);
879 const auto& source =
event.v1;
880 const auto& target =
event.v2;
883 REQUIRE(next_wave_it != test.vertex_order.end());
885 CHECK(vertex_order_it->count(source));
887 CHECK(expected_next_wave.count(target));
889 CHECK(edges_visited.insert({source, target}).second);
891 CHECK(edges_skipped.count({source, target}) == 0);
893 CHECK(vertices_visited_this_wave.count(source));
895 CHECK(targets_of_edges_in_this_wave.insert(target).second);
897 CHECK(targets_of_edges_visited.insert(target).second);
899 }
else if (event.type == Visitor::Event::onSkippedEdge) {
900 const auto& source =
event.v1;
901 const auto& target =
event.v2;
904 CHECK(vertices_visited.count(source));
906 CHECK((targets_of_edges_visited.count(target) || test.sources.count(target)));
908 CHECK(edges_skipped.insert({source, target}).second);
910 CHECK(edges_visited.count({source, target}) == 0);
912 }
else if (event.type == Visitor::Event::onWaveEnd) {
918 CHECK((vertex_order_it == test.vertex_order.end() || std::next(vertex_order_it) == test.vertex_order.end()));
920 CHECK(targets_of_edges_in_this_wave.empty());
922 const auto edges_visited_or_skipped = [&]() {
923 auto result = edges_visited;
924 std::copy(edges_skipped.begin(), edges_skipped.end(), std::inserter(result, result.end()));
928 const auto num_edges_expected = std::accumulate(graph.fanout_lists.begin(), graph.fanout_lists.end(), 0,
929 [](
const auto& lhs,
const auto& elem) { return lhs + (std::ptrdiff_t)elem.second.size(); }
930 ) - test.expected_num_edges_not_visited_and_not_skipped;
932 CHECK(edges_visited_or_skipped.size() == num_edges_expected);
934 AND_THEN(
"the return value should be as expected") {
935 bfv_name_and_func.check_result(test, bfv_result, edges_visited_or_skipped);