Sequoia
Loading...
Searching...
No Matches
DynamicUndirectedGraphTestingUtilities.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2023. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
13
16
17namespace sequoia::testing
18{
19 namespace undirected_graph{
21 enum graph_description : std::size_t {
22 empty = 0,
23
24 // x
25 node,
26
27 // /\
28 // \/
29 // x
30 node_0,
31
32 // /\ /\
33 // \/ \/
34 // x
35 node_0_0,
36
37 // x x
38 node_node,
39
40 // x --- x
41 node_1_node_0,
42
43 // /\
44 // \/
45 // x --- x
46 node_0_1_node_0,
47
48 // /\
49 // \/
50 // x --- x
51 node_1_node_0_1,
52
53 // /\
54 // \/
55 // x x
56 node_0_node,
57
58 // /\
59 // \/
60 // x x
61 node_node_1,
62
63 // x === x
64 node_1_1_node_0_0,
65
66 // x x x
67 node_node_node,
68
69 // x --- x x
70 node_1_node_0_node,
71
72 // x x --- x
73 node_node_2_node_1,
74
75 // x --- x --- x
76 node_1_node_0_2_node_1,
77
78
79 // - x --- x --- x --
80 node_1_2_node_0_2_node_0_1,
81
82 // /\
83 // \/
84 // x x x
85 node_node_1_node,
86
87 // /\
88 // \/
89 // x --- x x
90 node_1_node_0_1_node,
91
92 // /\
93 // \/
94 // x --- x --- x
95 node_1_node_0_1_2_node_1,
96
97 // /\
98 // \/
99 // -- x x x -
100 node_2_node_node_0_2,
101
102 // [2]
103 // x
104 // // \
105 // // \
106 // // \
107 // x === x
108 // [0] [1]
109 node_1_1_2_2_node_0_0_2_node_0_0_1,
110
111 // x [3]
112 // |
113 // |
114 // |
115 // x --- x --- x
116 // [0] [1] [2]
117 node_3_1_node_0_2_node_1_node_0,
118
119 // x --- x x --- x
120 node_1_node_0_node_3_node_2,
121
122 // x -- x - - x -- x
123 node_2_node_3_node_0_node_1,
124
125 end
126 };
127 }
128
129 template
130 <
131 class EdgeWeight,
132 class NodeWeight,
133 class EdgeMetaData,
134 class EdgeStorageConfig,
135 class NodeWeightStorage
136 >
138 {
139 public:
141 using edge_t = typename graph_t::edge_init_type;
142 using edges_equivalent_t = std::initializer_list<std::initializer_list<edge_t>>;
143 using transition_graph = typename transition_checker<graph_t>::transition_graph;
144
145 static void execute_operations(regular_test& t)
146 {
147 auto trg{make_transition_graph(t)};
148
149 auto checker{
150 [&t](std::string_view description, const graph_t& obtained, const graph_t& prediction, const graph_t& parent, std::size_t host, std::size_t target) {
151 t.check(equality, {description, no_source_location}, obtained, prediction);
152 if(host != target) t.check_semantics({description, no_source_location}, prediction, parent);
153 }
154 };
155
157 }
158
159 [[nodiscard]]
160 static graph_t make_and_check(regular_test& t, std::string_view description, edges_equivalent_t init)
161 {
163 }
164
165 static void check_initialization_exceptions(regular_test& t)
166 {
167 using namespace maths;
168
169 // One node
170 t.check_exception_thrown<std::out_of_range>("Target index of edge out of range", [](){ return graph_t{{edge_t{1}}}; });
171 t.check_exception_thrown<std::logic_error>("Mismatched loop", [](){ return graph_t{{edge_t{0}}}; });
172
173 // Two nodes
174 t.check_exception_thrown<std::logic_error>("Mismatched partial edges", [](){ return graph_t{{edge_t{1}}, {edge_t{1}}}; });
175 t.check_exception_thrown<std::logic_error>("Mismatched loop", [](){ return graph_t{{edge_t{1}}, {edge_t{0}, edge_t{1}}}; });
176 }
177
178 [[nodiscard]]
179 static transition_graph make_transition_graph(regular_test& t)
180 {
181 using namespace undirected_graph;
182
183 check_initialization_exceptions(t);
184
185 return transition_graph{
186 {
187 { // begin, 'empty'
188 {
189 graph_description::empty,
190 t.report(""),
191 [&t](const graph_t& g) -> const graph_t& {
192 t.check_exception_thrown<std::out_of_range>("cbegin_edges throws for empty graph", [&g]() { return g.cbegin_edges(0); });
193 return g;
194 }
195 },
196 {
197 graph_description::empty,
198 t.report(""),
199 [&t](const graph_t& g) -> const graph_t& {
200 t.check_exception_thrown<std::out_of_range>("cend_edges throws for empty graph", [&g]() { return g.cend_edges(0); });
201 return g;
202 }
203 },
204 {
205 graph_description::empty,
206 t.report(""),
207 [&t](const graph_t& g) -> const graph_t& {
208 t.check_exception_thrown<std::out_of_range>("crbegin_edges throws for empty graph", [&g]() { return g.crbegin_edges(0); });
209 return g;
210 }
211 },
212 {
213 graph_description::empty,
214 t.report(""),
215 [&t](const graph_t& g) -> const graph_t& {
216 t.check_exception_thrown<std::out_of_range>("crend_edges throws for empty graph", [&g]() { return g.crend_edges(0); });
217 return g;
218 }
219 },
220 {
221 graph_description::empty,
222 t.report(""),
223 [&t](const graph_t& g) -> const graph_t& {
224 t.check_exception_thrown<std::out_of_range>("cedges throws for empty graph", [&g]() { return g.cedges(0); });
225 return g;
226 }
227 },
228 {
229 graph_description::empty,
230 t.report(""),
231 [&t](const graph_t& g) -> const graph_t& {
232 t.check_exception_thrown<std::out_of_range>("swapping nodes throws for empty graph", [g{g}]() mutable { g.swap_nodes(0, 0); });
233 return g;
234 }
235 },
236 {
237 graph_description::empty,
238 t.report(""),
239 [&t](const graph_t& g) -> const graph_t& {
240 t.check_exception_thrown<std::out_of_range>("swapping edges throws for empty graph", [g{g}]() mutable { g.swap_edges(0, 0, 0); });
241 return g;
242 }
243 },
244 {
245 graph_description::empty,
246 t.report(""),
247 [&t](const graph_t& g) -> const graph_t& {
248 t.check_exception_thrown<std::out_of_range>("joining nodes throws for empty graph", [g{g}]() mutable { g.join(0, 0); });
249 return g;
250 }
251 },
252 {
253 graph_description::empty,
254 t.report("Clear empty graph"),
255 [](graph_t g) -> graph_t {
256 g.clear();
257 return g;
258 }
259 },
260 {
261 graph_description::node,
262 t.report("Add node to empty graph"),
263 [&t](graph_t g) -> graph_t {
264 t.check(equality, "Index of added node is 0", g.add_node(), 0uz);
265 return g;
266 }
267 },
268 {
269 graph_description::node,
270 t.report("insert node into empty graph"),
271 [&t](graph_t g) -> graph_t {
272 t.check(equality, "Index of added node is 0", g.insert_node(0), 0uz);
273 return g;
274 }
275 }
276 }, // end 'empty'
277 { // begin 'node'
278 {
279 graph_description::node,
280 t.report(""),
281 [&t](const graph_t& g) -> const graph_t& {
282 t.check_exception_thrown<std::out_of_range>("cbegin_edges throws when index is out of range", [&g]() { return g.cbegin_edges(1); });
283 return g;
284 }
285 },
286 {
287 graph_description::node,
288 t.report(""),
289 [&t](const graph_t& g) -> const graph_t& {
290 t.check_exception_thrown<std::out_of_range>("cend_edges throws when index is out of range", [&g]() { return g.cend_edges(1); });
291 return g;
292 }
293 },
294 {
295 graph_description::node,
296 t.report(""),
297 [&t](const graph_t& g) -> const graph_t& {
298 t.check_exception_thrown<std::out_of_range>("crbegin_edges throws when index is out of range", [&g]() { return g.crbegin_edges(1); });
299 return g;
300 }
301 },
302 {
303 graph_description::node,
304 t.report(""),
305 [&t](const graph_t& g) -> const graph_t& {
306 t.check_exception_thrown<std::out_of_range>("crend_edges throws when index is out of range", [&g]() { return g.crend_edges(1); });
307 return g;
308 }
309 },
310 {
311 graph_description::node,
312 t.report(""),
313 [&t](const graph_t& g) -> const graph_t& {
314 t.check_exception_thrown<std::out_of_range>("cedges throws when index is out of range", [&g]() { return g.cedges(1); });
315 return g;
316 }
317 },
318 {
319 graph_description::node,
320 t.report(""),
321 [&t](const graph_t& g) -> const graph_t& {
322 t.check_exception_thrown<std::out_of_range>("swapping nodes throws if first index out of range", [g{g}]() mutable { g.swap_nodes(1, 0); });
323 return g;
324 }
325 },
326 {
327 graph_description::node,
328 t.report(""),
329 [&t](const graph_t& g) -> const graph_t& {
330 t.check_exception_thrown<std::out_of_range>("swapping nodes throws if second index out of range", [g{g}]() mutable { g.swap_nodes(0, 1); });
331 return g;
332 }
333 },
334 {
335 graph_description::node,
336 t.report(""),
337 [&t](const graph_t& g) -> const graph_t& {
338 t.check_exception_thrown<std::out_of_range>("joining nodes throws if first index out of range", [g{g}]() mutable { g.join(1, 0); });
339 return g;
340 }
341 },
342 {
343 graph_description::node,
344 t.report(""),
345 [&t](const graph_t& g) -> const graph_t& {
346 t.check_exception_thrown<std::out_of_range>("joining nodes throws if second index out of range", [g{g}]() mutable { g.join(0, 1); });
347 return g;
348 }
349 },
350 {
351 graph_description::empty,
352 t.report("Clear graph"),
353 [](graph_t g) -> graph_t {
354 g.clear();
355 return g;
356 }
357 },
358 {
359 graph_description::empty,
360 t.report("Erase node to give empty graph"),
361 [](graph_t g) -> graph_t {
362 g.erase_node(0);
363 return g;
364 }
365 },
366 {
367 graph_description::node,
368 t.report("Attempt to erase edge past the end"),
369 [](graph_t g) -> const graph_t {
370 g.erase_edge(g.cend_edges(0));
371 return g;
372 }
373 },
374 {
375 graph_description::node_0,
376 t.report("Add loop"),
377 [](graph_t g) -> graph_t {
378 g.join(0, 0);
379 return g;
380 }
381 },
382 {
383 graph_description::node_node,
384 t.report("Add second node"),
385 [&t](graph_t g) -> graph_t {
386 t.check(equality, "Index of added node is 1", g.add_node(), 1uz);
387 return g;
388 }
389 },
390 {
391 graph_description::node_node,
392 t.report("Insert second node"),
393 [&t](graph_t g) -> graph_t {
394 t.check(equality, "Index of added node is 0", g.insert_node(0), 0uz);
395 return g;
396 }
397 },
398 {
399 graph_description::node_node,
400 t.report("Insert second node at end"),
401 [&t](graph_t g) -> graph_t {
402 t.check(equality, "Index of added node is 1", g.insert_node(1), 1uz);
403 return g;
404 }
405 },
406 {
407 graph_description::node,
408 t.report("Swap node with self"),
409 [](graph_t g) -> graph_t {
410 g.swap_nodes(0,0);
411 return g;
412 }
413 }
414 }, // end 'node'
415 { // begin 'node_0'
416 {
417 graph_description::empty,
418 t.report("Clear graph"),
419 [](graph_t g) -> graph_t {
420 g.clear();
421 return g;
422 }
423 },
424 {
425 graph_description::node,
426 t.report("Remove loop"),
427 [](graph_t g) -> graph_t {
428 g.erase_edge(g.cbegin_edges(0));
429 return g;
430 }
431 },
432 {
433 graph_description::node,
434 t.report("Remove loop via second partial edge"),
435 [](graph_t g) -> graph_t {
436 g.erase_edge(std::ranges::next(g.cbegin_edges(0)));
437 return g;
438 }
439 },
440 {
441 graph_description::node_0_0,
442 t.report("Add a second loop"),
443 [](graph_t g) -> graph_t {
444 g.join(0, 0);
445 return g;
446 }
447 },
448 {
449 graph_description::node_node_1,
450 t.report("Insert node"),
451 [&t](graph_t g) -> graph_t {
452 t.check(equality, "Index of added node is 0", g.insert_node(0), 0uz);
453 return g;
454 }
455 },
456 {
457 graph_description::node_0_node,
458 t.report("Insert node at end"),
459 [&t](graph_t g) -> graph_t {
460 t.check(equality, "Index of added node is 1", g.insert_node(1), 1uz);
461 return g;
462 }
463 },
464 {
465 graph_description::node_0,
466 t.report("Swap node with self"),
467 [](graph_t g) -> graph_t {
468 g.swap_nodes(0,0);
469 return g;
470 }
471 },
472 {
473 graph_description::node_0,
474 t.report("Swap edge with self"),
475 [](graph_t g) -> graph_t {
476 g.swap_edges(0, 0, 0);
477 return g;
478 }
479 },
480 {
481 graph_description::node_0,
482 t.report("Swap first partial edge with partner"),
483 [](graph_t g) -> graph_t {
484 g.swap_edges(0, 0, 1);
485 return g;
486 }
487 },
488 {
489 graph_description::node_0,
490 t.report("Swap second partial edge with partner"),
491 [](graph_t g) -> graph_t {
492 g.swap_edges(0, 1, 0);
493 return g;
494 }
495 },
496 {
497 graph_description::node_0,
498 t.report(""),
499 [&t](const graph_t& g) -> const graph_t& {
500 t.check_exception_thrown<std::out_of_range>("swapping edges throws for first edge index out of range", [g{g}]() mutable { g.swap_edges(0, 2, 0); });
501 return g;
502 }
503 },
504 {
505 graph_description::node_0,
506 t.report(""),
507 [&t](const graph_t& g) -> const graph_t& {
508 t.check_exception_thrown<std::out_of_range>("swapping edges throws for second edge index out of range", [g{g}]() mutable { g.swap_edges(0, 0, 2); });
509 return g;
510 }
511 },
512 {
513 graph_description::node_0,
514 t.report(""),
515 [&t](const graph_t& g) -> const graph_t& {
516 t.check_exception_thrown<std::out_of_range>("swapping edges throws for node index out of range", [g{g}]() mutable { g.swap_edges(1, 0, 0); });
517 return g;
518 }
519 }
520 }, // end 'node_0'
521 { // begin 'node_0_0'
522 {
523 graph_description::empty,
524 t.report("Clear graph"),
525 [](graph_t g) -> graph_t {
526 g.clear();
527 return g;
528 }
529 },
530 {
531 graph_description::node_0,
532 t.report("Remove first loop, via first partial edge"),
533 [](graph_t g) -> graph_t {
534 g.erase_edge(g.cbegin_edges(0));
535 return g;
536 }
537 },
538 {
539 graph_description::node_0,
540 t.report("Remove first loop, via second partial edge"),
541 [](graph_t g) -> graph_t {
542 g.erase_edge(g.cbegin_edges(0) + 1);
543 return g;
544 }
545 },
546 {
547 graph_description::node_0,
548 t.report("Remove second loop, via first partial edge"),
549 [](graph_t g) -> graph_t {
550 g.erase_edge(g.cbegin_edges(0) + 2);
551 return g;
552 }
553 },
554 {
555 graph_description::node_0,
556 t.report("Remove second loop, via second partial edge"),
557 [](graph_t g) -> graph_t {
558 g.erase_edge(g.cbegin_edges(0) + 3);
559 return g;
560 }
561 },
562 {
563 graph_description::node_0_0,
564 t.report("Swap partial edges {0,1}"),
565 [](graph_t g) -> graph_t {
566 g.swap_edges(0, 0, 1);
567 return g;
568 }
569 },
570 {
571 graph_description::node_0_0,
572 t.report("Swap partial edges {0,2}"),
573 [](graph_t g) -> graph_t {
574 g.swap_edges(0, 0, 2);
575 return g;
576 }
577 },
578 {
579 graph_description::node_0_0,
580 t.report("Swap partial edges {0,3}"),
581 [](graph_t g) -> graph_t {
582 g.swap_edges(0, 0, 3);
583 return g;
584 }
585 },
586 {
587 graph_description::node_0_0,
588 t.report("Swap partial edges {1,2}"),
589 [](graph_t g) -> graph_t {
590 g.swap_edges(0, 1, 2);
591 return g;
592 }
593 },
594 {
595 graph_description::node_0_0,
596 t.report("Swap partial edges {2,1}"),
597 [](graph_t g) -> graph_t {
598 g.swap_edges(0, 2, 1);
599 return g;
600 }
601 }
602 }, // end 'node_0_0'
603 { // begin 'node_node'
604 {
605 graph_description::empty,
606 t.report("Clear graph"),
607 [](graph_t g) -> graph_t {
608 g.clear();
609 return g;
610 }
611 },
612 {
613 graph_description::node,
614 t.report("Erase node 0"),
615 [](graph_t g) -> graph_t {
616 g.erase_node(0);
617 return g;
618 }
619 },
620 {
621 graph_description::node,
622 t.report("Erase node 1"),
623 [](graph_t g) -> graph_t {
624 g.erase_node(1);
625 return g;
626 }
627 },
628 {
629 graph_description::node_1_node_0,
630 t.report("Join {0,1}"),
631 [](graph_t g) -> graph_t {
632 g.join(0, 1);
633 return g;
634 }
635 },
636 {
637 graph_description::node_1_node_0,
638 t.report("Join {1,0}"),
639 [](graph_t g) -> graph_t {
640 g.join(0, 1);
641 return g;
642 }
643 }
644 }, // end 'node_node'
645 { // begin 'node_1_node_0'
646 {
647 graph_description::empty,
648 t.report("Clear graph"),
649 [](graph_t g) -> graph_t {
650 g.clear();
651 return g;
652 }
653 },
654 {
655 graph_description::node,
656 t.report("Erase node 0"),
657 [](graph_t g) -> graph_t {
658 g.erase_node(0);
659 return g;
660 }
661 },
662 {
663 graph_description::node,
664 t.report("Erase node 1"),
665 [](graph_t g) -> graph_t {
666 g.erase_node(1);
667 return g;
668 }
669 },
670 {
671 graph_description::node_0_1_node_0,
672 t.report("Add loop to node 0 and then swap it with link"),
673 [](graph_t g) -> graph_t {
674 g.join(0, 0);
675 g.swap_edges(0, 0, 2);
676 return g;
677 }
678 },
679 {
680 graph_description::node_1_node_0,
681 t.report("Swap nodes {0,1}"),
682 [](graph_t g) -> graph_t {
683 g.swap_nodes(0,1);
684 return g;
685 }
686 },
687 {
688 graph_description::node_1_node_0,
689 t.report("Swap nodes {1,0}"),
690 [](graph_t g) -> graph_t {
691 g.swap_nodes(1,0);
692 return g;
693 }
694 },
695 {
696 graph_description::node_1_1_node_0_0,
697 t.report("Join {0,1}"),
698 [](graph_t g) {
699 g.join(0, 1);
700 return g;
701 }
702 }
703 }, // end 'node_1_node_0'
704 { // begin 'node_0_1_node_0'
705 {
706 graph_description::empty,
707 t.report("Clear graph"),
708 [](graph_t g) -> graph_t {
709 g.clear();
710 return g;
711 }
712 },
713 {
714 graph_description::node,
715 t.report("Erase node 0"),
716 [](graph_t g) -> graph_t {
717 g.erase_node(0);
718 return g;
719 }
720 },
721 {
722 graph_description::node_0,
723 t.report("Erase node 1"),
724 [](graph_t g) -> graph_t {
725 g.erase_node(1);
726 return g;
727 }
728 },
729 {
730 graph_description::node_1_node_0,
731 t.report("Remove loop via first partial edge"),
732 [](graph_t g) -> graph_t {
733 g.erase_edge(g.cbegin_edges(0));
734 return g;
735 }
736 },
737 {
738 graph_description::node_1_node_0,
739 t.report("Remove loop via second partial edge"),
740 [](graph_t g) -> graph_t {
741 g.erase_edge(++g.cbegin_edges(0));
742 return g;
743 }
744 },
745 {
746 graph_description::node_0_node,
747 t.report("Remove edge {0,1}"),
748 [](graph_t g) -> graph_t {
749 g.erase_edge(g.cbegin_edges(0) + 2);
750 return g;
751 }
752 },
753 {
754 graph_description::node_0_node,
755 t.report("Remove edge {1,0}"),
756 [](graph_t g) -> graph_t {
757 g.erase_edge(g.cbegin_edges(1));
758 return g;
759 }
760 }
761 }, // end 'node_0_1_node_0'
762 { // begin 'node_1_node_0_1'
763 {
764 graph_description::node_0,
765 t.report("Erase node 0"),
766 [](graph_t g) -> graph_t {
767 g.erase_node(0);
768 return g;
769 }
770 },
771 {
772 graph_description::node,
773 t.report("Erase node 1"),
774 [](graph_t g) -> graph_t {
775 g.erase_node(1);
776 return g;
777 }
778 },
779 {
780 graph_description::node_node_1,
781 t.report("Remove edge {0,1}"),
782 [](graph_t g) -> graph_t {
783 g.erase_edge(g.cbegin_edges(0));
784 return g;
785 }
786 },
787 {
788 graph_description::node_node_1,
789 t.report("Remove edge {1,0}"),
790 [](graph_t g) -> graph_t {
791 g.erase_edge(g.cbegin_edges(1));
792 return g;
793 }
794 },
795 {
796 graph_description::node_1_node_0,
797 t.report("Remove loop via first partial edge"),
798 [](graph_t g) -> graph_t {
799 g.erase_edge(g.cbegin_edges(1)+1);
800 return g;
801 }
802 },
803 {
804 graph_description::node_1_node_0,
805 t.report("Remove loop via second partial edge"),
806 [](graph_t g) -> graph_t {
807 g.erase_edge(g.cbegin_edges(1)+2);
808 return g;
809 }
810 }
811 }, // end 'node_1_node_0_1'
812 { // begin 'node_0_node'
813 {
814 graph_description::empty,
815 t.report("Clear graph"),
816 [](graph_t g) -> graph_t {
817 g.clear();
818 return g;
819 }
820 },
821 {
822 graph_description::node_node_1_node,
823 t.report("Insert node"),
824 [&t](graph_t g) -> graph_t {
825 t.check(equality, "Index of added node is 0", g.insert_node(0), 0uz);
826 return g;
827 }
828 },
829 {
830 graph_description::node_node_1,
831 t.report("Swap nodes"),
832 [](graph_t g) -> graph_t {
833 g.swap_nodes(0,1);
834 return g;
835 }
836 }
837 ,
838 {
839 graph_description::node_node_1,
840 t.report("Swap nodes"),
841 [](graph_t g) -> graph_t {
842 g.swap_nodes(1,0);
843 return g;
844 }
845 }
846 }, // end 'node_0_node'
847 { // begin 'node_node_1'
848 {
849 graph_description::empty,
850 t.report("Clear graph"),
851 [](graph_t g) -> graph_t {
852 g.clear();
853 return g;
854 }
855 },
856 {
857 graph_description::node_0,
858 t.report("Erase node 0"),
859 [](graph_t g) -> graph_t {
860 g.erase_node(0);
861 return g;
862 }
863 },
864 {
865 graph_description::node,
866 t.report("Erase node 1"),
867 [](graph_t g) -> graph_t {
868 g.erase_node(1);
869 return g;
870 }
871 },
872 {
873 graph_description::node_0_node,
874 t.report("Swap nodes"),
875 [](graph_t g) -> graph_t {
876 g.swap_nodes(0,1);
877 return g;
878 }
879 }
880 }, // end 'node_node_1'
881 { // begin 'node_1_1_node_0_0'
882 {
883 graph_description::empty,
884 t.report("Clear graph"),
885 [](graph_t g) -> graph_t {
886 g.clear();
887 return g;
888 }
889 },
890 {
891 graph_description::node,
892 t.report("Erase node 0"),
893 [](graph_t g) -> graph_t {
894 g.erase_node(0);
895 return g;
896 }
897 },
898 {
899 graph_description::node,
900 t.report("Erase node 1"),
901 [](graph_t g) -> graph_t {
902 g.erase_node(1);
903 return g;
904 }
905 },
906 {
907 graph_description::node_1_node_0,
908 t.report("Erase node 0 zeroth link"),
909 [](graph_t g) -> graph_t {
910 g.erase_edge(g.cbegin_edges(0));
911 return g;
912 }
913 },
914 {
915 graph_description::node_1_node_0,
916 t.report("Erase node 0 first link"),
917 [](graph_t g) -> graph_t {
918 g.erase_edge(g.cbegin_edges(0) + 1);
919 return g;
920 }
921 },
922 {
923 graph_description::node_1_node_0,
924 t.report("Erase node 1 zeroth link"),
925 [](graph_t g) -> graph_t {
926 g.erase_edge(g.cbegin_edges(1));
927 return g;
928 }
929 },
930 {
931 graph_description::node_1_node_0,
932 t.report("Erase node 1 first link"),
933 [](graph_t g) -> graph_t {
934 g.erase_edge(g.cbegin_edges(1) + 1);
935 return g;
936 }
937 },
938 {
939 graph_description::node_1_1_node_0_0,
940 t.report("Swap edges"),
941 [](graph_t g) -> graph_t {
942 g.swap_edges(0, 0, 1);
943 return g;
944 }
945 },
946 {
947 graph_description::node_1_1_node_0_0,
948 t.report("Swap node 0 edges"),
949 [](graph_t g) -> graph_t {
950 g.swap_edges(0, 1, 0);
951 return g;
952 }
953 },
954 {
955 graph_description::node_1_1_node_0_0,
956 t.report("Swap node 1 edges"),
957 [](graph_t g) -> graph_t {
958 g.swap_edges(1, 0, 1);
959 return g;
960 }
961 }
962 }, // end 'node_1_1_node_0_0'
963 { // begin 'node_node_node'
964 {
965 graph_description::empty,
966 t.report("Clear graph"),
967 [](graph_t g) -> graph_t {
968 g.clear();
969 return g;
970 }
971 },
972 {
973 graph_description::node_node,
974 t.report("Erase node 0"),
975 [](graph_t g) -> graph_t {
976 g.erase_node(0);
977 return g;
978 }
979 },
980 {
981 graph_description::node_node,
982 t.report("Erase node 1"),
983 [](graph_t g) -> graph_t {
984 g.erase_node(1);
985 return g;
986 }
987 },
988 {
989 graph_description::node_node,
990 t.report("Erase node 2"),
991 [](graph_t g) -> graph_t {
992 g.erase_node(2);
993 return g;
994 }
995 },
996 {
997 graph_description::node_1_node_0_node,
998 t.report("Join {0,1}"),
999 [](graph_t g) -> graph_t {
1000 g.join(0,1);
1001 return g;
1002 }
1003 },
1004 {
1005 graph_description::node_node_2_node_1,
1006 t.report("Join {2,1}"),
1007 [](graph_t g) -> graph_t {
1008 g.join(2,1);
1009 return g;
1010 }
1011 }
1012 }, // end 'node_node_node'
1013 { // begin 'node_1_node_0_node'
1014 {
1015 graph_description::empty,
1016 t.report("Clear graph"),
1017 [](graph_t g) -> graph_t {
1018 g.clear();
1019 return g;
1020 }
1021 },
1022 {
1023 graph_description::node_node,
1024 t.report("Erase node 0"),
1025 [](graph_t g) -> graph_t {
1026 g.erase_node(0);
1027 return g;
1028 }
1029 },
1030 {
1031 graph_description::node_node,
1032 t.report("Erase node 1"),
1033 [](graph_t g) -> graph_t {
1034 g.erase_node(1);
1035 return g;
1036 }
1037 },
1038 {
1039 graph_description::node_1_node_0,
1040 t.report("Erase node 2"),
1041 [](graph_t g) -> graph_t {
1042 g.erase_node(2);
1043 return g;
1044 }
1045 },
1046 {
1047 graph_description::node_node_node,
1048 t.report("Remove link {0,1}"),
1049 [](graph_t g) -> graph_t {
1050 g.erase_edge(g.cbegin_edges(0));
1051 return g;
1052 }
1053 },
1054 {
1055 graph_description::node_node_node,
1056 t.report("Remove link {1,0}"),
1057 [](graph_t g) -> graph_t {
1058 g.erase_edge(g.cbegin_edges(1));
1059 return g;
1060 }
1061 },
1062 {
1063 graph_description::node_1_node_0_2_node_1,
1064 t.report("Join {1,2}"),
1065 [](graph_t g) -> graph_t {
1066 g.join(1,2);
1067 return g;
1068 }
1069 },
1070 {
1071 graph_description::node_1_node_0_2_node_1,
1072 t.report("Join {2,1}"),
1073 [](graph_t g) -> graph_t {
1074 g.join(2,1);
1075 return g;
1076 }
1077 },
1078 {
1079 graph_description::node_node_2_node_1,
1080 t.report("Swap nodes {0,2}"),
1081 [](graph_t g) -> graph_t {
1082 g.swap_nodes(0,2);
1083 return g;
1084 }
1085 },
1086 {
1087 graph_description::node_node_2_node_1,
1088 t.report("Swap nodes {2,0}"),
1089 [](graph_t g) -> graph_t {
1090 g.swap_nodes(2,0);
1091 return g;
1092 }
1093 }
1094 }, // end 'node_1_node_0_node'
1095 { // begin 'node_node_2_node_1'
1096 {
1097 graph_description::empty,
1098 t.report("Clear graph"),
1099 [](graph_t g) -> graph_t {
1100 g.clear();
1101 return g;
1102 }
1103 },
1104 {
1105 graph_description::node_1_node_0,
1106 t.report("Erase node 0"),
1107 [](graph_t g) -> graph_t {
1108 g.erase_node(0);
1109 return g;
1110 }
1111 },
1112 {
1113 graph_description::node_node,
1114 t.report("Erase node 1"),
1115 [](graph_t g) -> graph_t {
1116 g.erase_node(1);
1117 return g;
1118 }
1119 },
1120 {
1121 graph_description::node_node,
1122 t.report("Erase node 2"),
1123 [](graph_t g) -> graph_t {
1124 g.erase_node(2);
1125 return g;
1126 }
1127 },
1128 {
1129 graph_description::node_node_node,
1130 t.report("Remove link {1,2}"),
1131 [](graph_t g) -> graph_t {
1132 g.erase_edge(g.cbegin_edges(1));
1133 return g;
1134 }
1135 },
1136 {
1137 graph_description::node_node_node,
1138 t.report("Remove link {2,1}"),
1139 [](graph_t g) -> graph_t {
1140 g.erase_edge(g.cbegin_edges(2));
1141 return g;
1142 }
1143 },
1144 {
1145 graph_description::node_1_node_0_2_node_1,
1146 t.report("Join {0,1}, the swap node 1's edge"),
1147 [](graph_t g) -> graph_t {
1148 g.join(0,1);
1149 g.swap_edges(1, 0, 1);
1150 return g;
1151 }
1152 },
1153 {
1154 graph_description::node_1_node_0_node,
1155 t.report("Swap nodes {0,2}"),
1156 [](graph_t g) -> graph_t {
1157 g.swap_nodes(0,2);
1158 return g;
1159 }
1160 },
1161 {
1162 graph_description::node_1_node_0_node,
1163 t.report("Swap nodes {2,0}"),
1164 [](graph_t g) -> graph_t {
1165 g.swap_nodes(2,0);
1166 return g;
1167 }
1168 }
1169 }, // end 'node_node_2_node_1'
1170 { // begin 'node_1_node_0_2_node_1'
1171 {
1172 graph_description::empty,
1173 t.report("Clear graph"),
1174 [](graph_t g) -> graph_t {
1175 g.clear();
1176 return g;
1177 }
1178 },
1179 {
1180 graph_description::node_1_node_0,
1181 t.report("Erase node 0"),
1182 [](graph_t g) -> graph_t {
1183 g.erase_node(0);
1184 return g;
1185 }
1186 },
1187 {
1188 graph_description::node_node,
1189 t.report("Erase node 1"),
1190 [](graph_t g) -> graph_t {
1191 g.erase_node(1);
1192 return g;
1193 }
1194 },
1195 {
1196 graph_description::node_1_node_0,
1197 t.report("Erase node 2"),
1198 [](graph_t g) -> graph_t {
1199 g.erase_node(2);
1200 return g;
1201 }
1202 },
1203 {
1204 graph_description::node_1_node_0_node,
1205 t.report("Remove link {1,2}"),
1206 [](graph_t g) -> graph_t {
1207 g.erase_edge(g.cbegin_edges(1) + 1);
1208 return g;
1209 }
1210 },
1211 {
1212 graph_description::node_1_node_0_2_node_1,
1213 t.report("Swap nodes {0,2} then swap node 1's edges"),
1214 [](graph_t g) -> graph_t {
1215 g.swap_nodes(0, 2);
1216 g.swap_edges(1, 0, 1);
1217 return g;
1218 }
1219 },
1220 {
1221 graph_description::node_1_node_0_2_node_1,
1222 t.report("Swap nodes {2,0} then swap node 1's edges"),
1223 [](graph_t g) -> graph_t {
1224 g.swap_nodes(2, 0);
1225 g.swap_edges(1, 1, 0);
1226 return g;
1227 }
1228 }
1229 }, // end 'node_1_node_0_2_node_1'
1230 { // begin 'node_1_2_node_2_0_node_0_1'
1231 {
1232 graph_description::empty,
1233 t.report("Clear graph"),
1234 [](graph_t g) -> graph_t {
1235 g.clear();
1236 return g;
1237 }
1238 },
1239 {
1240 graph_description::node_1_node_0,
1241 t.report("Erase node 0"),
1242 [](graph_t g) -> graph_t {
1243 g.erase_node(0);
1244 return g;
1245 }
1246 },
1247 {
1248 graph_description::node_1_node_0,
1249 t.report("Erase node 1"),
1250 [](graph_t g) -> graph_t {
1251 g.erase_node(1);
1252 return g;
1253 }
1254 },
1255 {
1256 graph_description::node_1_node_0,
1257 t.report("Erase node 2"),
1258 [](graph_t g) -> graph_t {
1259 g.erase_node(2);
1260 return g;
1261 }
1262 },
1263 {
1264 graph_description::node_1_node_0_2_node_1,
1265 t.report("Remove {2,0}"),
1266 [](graph_t g) -> graph_t {
1267 g.erase_edge(g.cbegin_edges(2));
1268 return g;
1269 }
1270 }
1271 }, // end 'node_1_2_node_0_2_node_0_1'
1272 { // begin 'node_node_1_node'
1273 {
1274 graph_description::empty,
1275 t.report("Clear graph"),
1276 [](graph_t g) -> graph_t {
1277 g.clear();
1278 return g;
1279 }
1280 },
1281 {
1282 graph_description::node_0_node,
1283 t.report("Erase node 0"),
1284 [](graph_t g) -> graph_t {
1285 g.erase_node(0);
1286 return g;
1287 }
1288 },
1289 {
1290 graph_description::node_node,
1291 t.report("Erase node 1"),
1292 [](graph_t g) -> graph_t {
1293 g.erase_node(1);
1294 return g;
1295 }
1296 },
1297 {
1298 graph_description::node_node_1,
1299 t.report("Erase node 2"),
1300 [](graph_t g) -> graph_t {
1301 g.erase_node(2);
1302 return g;
1303 }
1304 },
1305 }, // end 'node_node_1_node'
1306 { // begin 'node_1_node_0_1_node'
1307 {
1308 graph_description::node_0_node,
1309 t.report("Erase node 0"),
1310 [](graph_t g) -> graph_t {
1311 g.erase_node(0);
1312 return g;
1313 }
1314 },
1315 {
1316 graph_description::node_node,
1317 t.report("Erase node 1"),
1318 [](graph_t g) -> graph_t {
1319 g.erase_node(1);
1320 return g;
1321 }
1322 },
1323 {
1324 graph_description::node_node_1_node,
1325 t.report("Remove {0,1}"),
1326 [](graph_t g) -> graph_t {
1327 g.erase_edge(g.cbegin_edges(0));
1328 return g;
1329 }
1330 },
1331 {
1332 graph_description::node_1_node_0_1_2_node_1,
1333 t.report("Join {1,2}"),
1334 [](graph_t g) -> graph_t {
1335 g.join(1, 2);
1336 return g;
1337 }
1338 }
1339 }, // end 'node_1_node_0_1_node'
1340 { // begin 'node_1_node_0_1_2_node_1'
1341 {
1342 graph_description::node_0_1_node_0,
1343 t.report("Erase node 0"),
1344 [](graph_t g) -> graph_t {
1345 g.erase_node(0);
1346 return g;
1347 }
1348 },
1349 {
1350 graph_description::node_node,
1351 t.report("Erase node 1"),
1352 [](graph_t g) -> graph_t {
1353 g.erase_node(1);
1354 return g;
1355 }
1356 },
1357 {
1358 graph_description::node_1_node_0_1_node,
1359 t.report("Remove {1,2}"),
1360 [](graph_t g) -> graph_t {
1361 g.erase_edge(g.cbegin_edges(1) + 3);
1362 return g;
1363 }
1364 }
1365 }, // end 'node_1_node_0_1_2_node_1'
1366 { // begin 'node_2_node_node_0_2'
1367 {
1368 graph_description::node_1_node_0_1_node,
1369 t.report("Swap {1,2}"),
1370 [](graph_t g) -> graph_t {
1371 g.swap_nodes(1,2);
1372 return g;
1373 }
1374 }
1375 }, // end 'node_2_node_node_0_2'
1376 { // begin 'node_1_1_2_2_node_0_0_2_node_0_0_1'
1377 {
1378 graph_description::node_1_node_0,
1379 t.report("Erase node 0"),
1380 [](graph_t g) -> graph_t {
1381 g.erase_node(0);
1382 return g;
1383 }
1384 },
1385 {
1386 graph_description::node_1_1_node_0_0,
1387 t.report("Erase node 1"),
1388 [](graph_t g) -> graph_t {
1389 g.erase_node(1);
1390 return g;
1391 }
1392 },
1393 {
1394 graph_description::node_1_1_node_0_0,
1395 t.report("Erase node 2"),
1396 [](graph_t g) -> graph_t {
1397 g.erase_node(2);
1398 return g;
1399 }
1400 },
1401 {
1402 graph_description::node_1_1_2_2_node_0_0_2_node_0_0_1,
1403 t.report("Swap nodes {2, 1}"),
1404 [](graph_t g) -> graph_t {
1405 g.swap_nodes(2,1);
1406 g.sort_edges(g.cbegin_edges(0), g.cend_edges(0), [](const auto& lhs, const auto& rhs) { return lhs.target_node() < rhs.target_node(); });
1407 return g;
1408 }
1409 },
1410 {
1411 graph_description::node_1_1_2_2_node_0_0_2_node_0_0_1,
1412 t.report("Swap nodes {2, 1}"),
1413 [](graph_t g) -> graph_t {
1414 g.swap_nodes(2,1);
1415 g.stable_sort_edges(g.cbegin_edges(0), g.cend_edges(0), [](const auto& lhs, const auto& rhs) { return lhs.target_node() < rhs.target_node(); });
1416 return g;
1417 }
1418 }
1419 }, // end 'node_1_1_2_2_node_0_0_2_node_0_0_1'
1420 { // begin 'node_3_1_node_0_2_node_1_node_0'
1421 {
1422 graph_description::node_1_node_0_node,
1423 t.report("Erase node 0"),
1424 [](graph_t g) -> graph_t {
1425 g.erase_node(0);
1426 return g;
1427 }
1428 }
1429 }, // end 'node_3_1_node_0_2_node_1_node_0'
1430 { // begin 'node_1_node_0_node_3_node_2'
1431 {
1432 graph_description::node_1_node_0_node,
1433 t.report("Erase node 2"),
1434 [](graph_t g) -> graph_t {
1435 g.erase_node(2);
1436 return g;
1437 }
1438 },
1439 {
1440 graph_description::node_2_node_3_node_0_node_1,
1441 t.report("Swap {2,1}"),
1442 [](graph_t g) -> graph_t {
1443 g.swap_nodes(2,1);
1444 return g;
1445 }
1446 },
1447 {
1448 graph_description::node_2_node_3_node_0_node_1,
1449 t.report("Swap {0,3}"),
1450 [](graph_t g) -> graph_t {
1451 g.swap_nodes(0,3);
1452 return g;
1453 }
1454 }
1455 }, // end 'node_1_node_0_node_3_node_2'
1456 { // begin 'node_2_node_3_node_0_node_1'
1457 {
1458 graph_description::node_1_node_0_node_3_node_2,
1459 t.report("Swap {2,1}"),
1460 [](graph_t g) -> graph_t {
1461 g.swap_nodes(2,1);
1462 return g;
1463 }
1464 },
1465 {
1466 graph_description::node_1_node_0_node_3_node_2,
1467 t.report("Swap {0,3}"),
1468 [](graph_t g) -> graph_t {
1469 g.swap_nodes(0,3);
1470 return g;
1471 }
1472 }
1473 }, // end 'node_2_node_3_node_0_node_1'
1474 },
1475 {
1476 // 'empty'
1477 make_and_check(t, t.report(""), {}),
1478
1479 // 'node'
1480 make_and_check(t, t.report(""), {{}}),
1481
1482 // 'node_0'
1483 make_and_check(t, t.report(""), {{edge_t{0}, edge_t{0}}}),
1484
1485 // 'node_0_0'
1486 make_and_check(t, t.report(""), {{edge_t{0}, edge_t{0}, edge_t{0}, edge_t{0}}}),
1487
1488 // 'node_node'
1489 make_and_check(t, t.report(""), {{}, {}}),
1490
1491 // 'node_1_node_0'
1492 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}}}),
1493
1494 // 'node_0_1_node_0'
1495 make_and_check(t, t.report(""), {{edge_t{0}, edge_t{0}, edge_t{1}}, {edge_t{0}}}),
1496
1497 // 'node_1_node_0_1'
1498 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}}}),
1499
1500 // 'node_0_node'
1501 make_and_check(t, t.report(""), {{edge_t{0}, edge_t{0}}, {}}),
1502
1503 // 'node_node_1'
1504 make_and_check(t, t.report(""), {{}, {edge_t{1}, edge_t{1}}}),
1505
1506 // 'node_1_1_node_0_0'
1507 make_and_check(t, t.report(""), {{edge_t{1}, edge_t{1}}, {edge_t{0}, edge_t{0}}}),
1508
1509 // 'node_node_node'
1510 make_and_check(t, t.report(""), {{}, {}, {}}),
1511
1512 // 'node_1_node_0_node'
1513 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}}, {}}),
1514
1515 // 'node_node_2_node_1'
1516 make_and_check(t, t.report(""), {{}, {edge_t{2}}, {edge_t{1}}}),
1517
1518 // 'node_1_node_0_2_node_1'
1519 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}, edge_t{2}}, {edge_t{1}}}),
1520
1521 // 'node_1_2_node_0_2_node_0_1'
1522 [&t](){
1523 auto g{make_and_check(t, t.report(""), {{edge_t{1}, edge_t{2}}, {edge_t{0}, edge_t{2}}, {edge_t{0}, edge_t{1}}})};
1524 t.check(equality, "Check sorting of edges on construction", graph_t{{edge_t{2}, edge_t{1}}, {edge_t{2}, edge_t{0}}, {edge_t{1}, edge_t{0}}}, g);
1525
1526 return g;
1527 }(),
1528
1529 // 'node_node_1_node'
1530 make_and_check(t, t.report(""), {{}, {edge_t{1}, edge_t{1}}, {}}),
1531
1532 // 'node_1_node_0_1_node'
1533 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}}, {}}),
1534
1535 // 'node_1_node_0_1_2_node_1'
1536 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}, edge_t{1}, edge_t{1}, edge_t{2}}, {edge_t{1}}}),
1537
1538 // 'node_2_node_node_0_2'
1539 make_and_check(t, t.report(""), {{edge_t{2}}, {}, {edge_t{0}, edge_t{2}, edge_t{2}}}),
1540
1541 // 'node_1_1_2_2_node_0_0_2_node_0_0_1'
1542 [&t](){
1543 auto g{make_and_check(t, t.report(""), {{edge_t{1}, edge_t{1}, edge_t{2}, edge_t{2}}, {edge_t{0}, edge_t{0}, edge_t{2}}, {edge_t{0}, edge_t{0}, edge_t{1}}})};
1544 t.check(equality,
1545 t.report("Check sorting of edges on construction"),
1546 graph_t{{edge_t{2}, edge_t{1}, edge_t{2}, edge_t{1}}, {edge_t{0}, edge_t{2}, edge_t{0}}, {edge_t{1}, edge_t{0}, edge_t{0}}},
1547 g);
1548
1549 return g;
1550 }(),
1551
1552 // 'node_3_1_node_0_2_node_1_node_0'
1553 make_and_check(t, t.report(""), {{edge_t{1}, edge_t{3}}, {edge_t{0}, edge_t{2}}, {edge_t{1}}, {edge_t{0}}}),
1554
1555 // 'node_1_node_0_node_3_node_2'
1556 make_and_check(t, t.report(""), {{edge_t{1}}, {edge_t{0}}, {edge_t{3}}, {edge_t{2}}}),
1557
1558 // 'node_2_node_3_node_0_node_1'
1559 make_and_check(t, t.report(""), {{edge_t{2}}, {edge_t{3}}, {edge_t{0}}, {edge_t{1}}}),
1560 }
1561 };
1562 }
1563 };
1564}
graph_description
Convention: the indices following 'node' - separated by underscores - give the target node of the ass...
Definition: DynamicUndirectedGraphTestingUtilities.hpp:21
Utilities for checking regular semantics.
Facility to define tests via a graph comprising states of an object and transitions between them.
Definition: DynamicGraph.hpp:303
Definition: DynamicGraph.hpp:360
class template from which all concrete tests should derive.
Definition: FreeTestCore.hpp:144
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:708
Definition: DynamicUndirectedGraphTestingUtilities.hpp:138
Definition: DynamicGraphTestingUtilities.hpp:173
Definition: StateTransitionUtilities.hpp:77