Sequoia
Loading...
Searching...
No Matches
GeometryTestingUtilities.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2024. //
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
17#include <complex>
18
19namespace sequoia::testing
20{
21 template<class T, class U>
22 constexpr bool can_multiply{
23 requires(const T& t, const U& u) { t * u; }
24 };
25
26 template<class T, class U>
27 constexpr bool can_divide{
28 requires(const T& t, const U& u) { t / u; }
29 };
30
31 template<class T, class U>
32 constexpr bool can_add{
33 requires(const T& t, const U& u) { t + u; }
34 };
35
36 template<class T, class U>
37 constexpr bool can_subtract{
38 requires(const T& t, const U& u) { t - u; }
39 };
40
41 template<class T>
42 constexpr bool has_unary_plus{
43 requires(const T& t) { { +t } -> std::convertible_to<T>; }
44 };
45
46 template<class T>
47 constexpr bool has_unary_minus{
48 requires(const T& t) { { -t } -> std::convertible_to<T>; }
49 };
50
51 template<class T>
52 struct is_complex : std::false_type {};
53
54 template<std::floating_point T>
55 struct is_complex<std::complex<T>> : std::true_type {};
56
57 template<class T>
58 inline constexpr bool is_complex_v{is_complex<T>::value};
59
60 template<class T>
61 using is_complex_t = typename is_complex<T>::type;
62
63 template<class B>
64 inline constexpr bool is_orthonormal_basis_v{
65 requires {
66 typename B::orthonormal;
67 requires std::same_as<typename B::orthonormal, std::true_type>;
68 }
69 };
70
71 template<class Set, maths::weak_field Field, std::size_t D>
73 {
74 using set_type = Set;
75 using field_type = Field;
76 using is_vector_space = std::true_type;
77 constexpr static std::size_t dimension{D};
78
79 template<maths::basis Basis>
80 requires std::floating_point<field_type>&& is_orthonormal_basis_v<Basis>
81 [[nodiscard]]
82 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
83 {
84 return std::ranges::fold_left(std::views::zip(lhs.values(), rhs.values()), field_type{}, [](field_type f, const auto& z){ return f + std::get<0>(z) * std::get<1>(z); });
85 }
86
87 template<maths::basis Basis>
88 requires is_complex_v<field_type>&& is_orthonormal_basis_v<Basis>
89 [[nodiscard]]
90 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
91 {
92 return std::ranges::fold_left(std::views::zip(lhs.values(), rhs.values()), field_type{}, [](field_type f, const auto& z){ return f + conj(std::get<0>(z)) * std::get<1>(z); });
93 }
94 };
95
96 template<class Set, maths::weak_field Field, std::size_t D>
98 {
99 using set_type = Set;
101 using is_affine_space = std::true_type;
102 };
103
104 template<class Set, maths::weak_field Field, std::size_t D>
106 {
108 using orthonormal = std::true_type;
109 };
110
111 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
113 {
114 using set_type = Set;
115 using commutative_ring_type = Ring;
116 using is_free_module = std::true_type;
117 constexpr static std::size_t dimension{D};
118 };
119
120 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
122 {
124 };
125
126 template<maths::convex_space ConvexSpace, maths::basis Basis, class Origin, class Validator>
128 struct value_tester<maths::coordinates<ConvexSpace, Basis, Origin, Validator>>
129 {
131 using commutative_ring_type = typename coord_type::commutative_ring_type;
132 constexpr static std::size_t D{coord_type::dimension};
133
134 template<test_mode Mode>
135 static void test(equality_check_t, test_logger<Mode>& logger, const coord_type& actual, const coord_type& prediction)
136 {
137 check(equality, "Wrapped values", logger, actual.values(), prediction.values());
138 if constexpr(D == 1)
139 {
140 check(equality, "Wrapped value", logger, actual.value(), prediction.value());
141 if constexpr(std::convertible_to<commutative_ring_type, bool>)
142 check(equality, "Conversion to bool", logger, static_cast<bool>(actual), static_cast<bool>(prediction));
143 }
144
145 for(auto i : std::views::iota(0uz, D))
146 {
147 check(equality, std::format("Value at index {}", i), logger, actual[i], prediction[i]);
148 }
149 }
150
151 template<test_mode Mode>
152 static void test(equivalence_check_t, test_logger<Mode>& logger, const coord_type& actual, const std::array<commutative_ring_type, D>& prediction)
153 {
154 check(equality, "Wrapped values", logger, actual.values(), std::span<const commutative_ring_type, D>{prediction});
155 check(equivalence, "Iterators", logger, std::ranges::subrange{actual.begin(), actual.end()}, prediction);
156 check(equivalence, "c-Iterators", logger, std::ranges::subrange{actual.cbegin(), actual.cend()}, prediction);
157 check(equivalence, "r-Iterators", logger, std::ranges::subrange{actual.rbegin(), actual.rend()}, prediction);
158 check(equivalence, "cr-Iterators", logger, std::ranges::subrange{actual.crbegin(), actual.crend()}, prediction);
159
160 for(auto i : std::views::iota(0uz, D))
161 {
162 check(equality, "operator[]", logger, actual[i], prediction[i]);
163 }
164 }
165 };
166
169 enum class inverted_ordering : bool {no, yes};
170
171 template<class Label>
172 requires std::convertible_to<Label, std::size_t>
173 [[nodiscard]]
174 std::weak_ordering to_ordering(Label From, Label To, inverted_ordering invert)
175 {
176 const bool inverted{invert == inverted_ordering::yes};
177 return (((From < To) && !inverted) || ((From > To) && inverted)) ? std::weak_ordering::less
178 : (((From > To) && !inverted) || ((From < To) && inverted)) ? std::weak_ordering::greater
179 : std::weak_ordering::equivalent;
180 }
181
182 template<maths::network Graph, class Label, class Fn>
183 requires std::convertible_to<Label, std::size_t>
184 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, std::weak_ordering ordering)
185 {
186 g.join(From, To, std::string{message}, f, ordering);
187 }
188
189 template<maths::network Graph, class Label, class Fn>
190 requires std::convertible_to<Label, std::size_t>
191 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f)
192 {
193 g.join(From, To, std::string{message}, f);
194 }
195
196 template<class Coords, maths::network Graph, class Label, class Fn>
197 requires std::is_invocable_r_v<Coords, Fn, Coords> && std::convertible_to<Label, std::size_t>
198 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, inverted_ordering invert={})
199 {
200 using ring_t = Coords::commutative_ring_type;
201 constexpr static auto dimension{Coords::dimension};
202
203 if constexpr((dimension == 1) && std::totally_ordered<ring_t>)
204 {
205 add_transition(g, From, To, message, f, to_ordering(From, To, invert));
206 }
207 else
208 {
209 add_transition(g, From, To, message, f);
210 }
211 }
212
213 template<class T>
214 inline constexpr bool has_units_type{
215 requires { typename T::units_type; }
216 };
217
218
219 template<class Coordinates>
221 {
222 enum dim_1_label{ two, one, zero, neg_one };
223 enum dim_2_label{ neg_one_neg_one, neg_one_zero, zero_neg_one, zero_zero, zero_one, one_zero, one_one };
224
226 using coords_t = Coordinates;
227 using disp_t = coords_t::displacement_coordinates_type;
228 using module_t = coords_t::free_module_type;
229 using ring_t = coords_t::commutative_ring_type;
230 constexpr static std::size_t dimension{Coordinates::dimension};
231 constexpr static bool orderable{(dimension == 1) && std::totally_ordered<ring_t>};
232
233 regular_test& m_Test;
234 graph_type m_Graph;
235 public:
237 : m_Test{t}
238 , m_Graph{make_graph(m_Test)}
239 {}
240
241 void execute()
242 {
243 transition_checker<coords_t>::check("", m_Graph, make_checker());
244 }
245 private:
246 static graph_type make_graph(regular_test& test)
247 {
248 if constexpr(has_units_type<coords_t>)
249 return do_make_graph(test, typename coords_t::units_type{});
250 else
251 return do_make_graph(test);
252 }
253
254 template<class... Units>
255 [[nodiscard]]
256 static graph_type do_make_graph(regular_test& test, Units... units)
257 {
258 if constexpr (dimension == 1) return make_dim_1_transition_graph(test, units...);
259 else if constexpr(dimension == 2) return make_dim_2_transition_graph(test, units...);
260 }
261
262 [[nodiscard]]
263 auto make_checker() const
264 {
265 if constexpr(orderable)
266 {
267 return
268 [&test=m_Test](std::string_view description, const coords_t& obtained, const coords_t& prediction, const coords_t& parent, std::weak_ordering ordering) {
269 test.check(equality, description, obtained, prediction);
270 if(ordering != std::weak_ordering::equivalent)
271 test.check_semantics(description, prediction, parent, ordering);
272 };
273 }
274 else
275 {
276 return
277 [&test=m_Test](std::string_view description, const coords_t& obtained, const coords_t& prediction, const coords_t& parent, std::size_t host, std::size_t target) {
278 test.check(equality, description, obtained, prediction);
279 if(host!= target) test.check_semantics(description, prediction, parent);
280 };
281 }
282 }
283
284 template<class... Units>
285 static graph_type make_dim_1_transition_graph(regular_test& test, Units... units)
286 {
287 graph_type g{
288 {
289 {}, {}, {}
290 },
291 {coords_t{ring_t(2), units...}, coords_t{ring_t(1), units...}, coords_t{}}
292 };
293
294 add_dim_1_common_transitions(g, test, units...);
295
296 if constexpr(!maths::defines_half_line_v<typename Coordinates::validator_type>)
297 {
298 add_dim_1_negative_transitions(g, test, units...);
299 }
300 else if constexpr(std::is_signed_v<ring_t>)
301 {
302 add_dim_1_attempted_negative_transitions(g, test, units...);
303 }
304
305 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
306 {
307 add_dim_1_distinguished_origin_transitions(g, test);
308 }
309
310 if constexpr(Coordinates::has_freely_mutable_components)
311 {
312 add_dim_1_free_mutations(g, test);
313 }
314
315 return g;
316 }
317
318 template<class... Units>
319 static graph_type make_dim_2_transition_graph(regular_test& test, Units... units)
320 {
321 using edge_t = transition_checker<coords_t>::edge;
322 graph_type g{
323 {
324 {
325 edge_t{dim_2_label::neg_one_neg_one, test.report("+ (-1, -1)"), [](coords_t v) -> coords_t { return +v; }},
326 edge_t{dim_2_label::neg_one_zero, test.report("(-1, -1) + (0, 1)"), [&](coords_t v) -> coords_t { return v + disp_t{std::array{ring_t{}, ring_t(1)}, units...}; }},
327 edge_t{dim_2_label::neg_one_zero, test.report("(-1, -1) += (0, 1)"), [&](coords_t v) -> coords_t { return v += disp_t{std::array{ring_t{}, ring_t(1)}, units...}; }},
328 edge_t{dim_2_label::zero_neg_one, test.report("(-1, -1) + (1, 0)"), [&](coords_t v) -> coords_t { return v + disp_t{std::array{ring_t(1), ring_t{}}, units...}; }},
329 edge_t{dim_2_label::zero_neg_one, test.report("(-1, -1) += (1, 0)"), [&](coords_t v) -> coords_t { return v += disp_t{std::array{ring_t(1), ring_t{}}, units...}; }}
330 }, // neg_one_neg_one
331 {
332 edge_t{dim_2_label::neg_one_neg_one, test.report("(-1, 0) - (0, 1)"), [&](coords_t v) -> coords_t { return v - disp_t{std::array{ring_t{}, ring_t(1)}, units...}; }},
333 edge_t{dim_2_label::neg_one_neg_one, test.report("(-1, 0) -= (0, 1)"), [&](coords_t v) -> coords_t { return v -= disp_t{std::array{ring_t{}, ring_t(1)}, units...}; }}
334 }, // neg_one_zero
335 {
336 edge_t{dim_2_label::neg_one_neg_one, test.report("(0, -1) - (1, 0)"), [&](coords_t v) -> coords_t { return v - disp_t{std::array{ring_t{1}, ring_t(0)}, units...}; }},
337 edge_t{dim_2_label::neg_one_neg_one, test.report("(0, -1) -= (1, 0)"), [&](coords_t v) -> coords_t { return v -= disp_t{std::array{ring_t{1}, ring_t(0)}, units...}; }}
338 }, // zero_neg_one
339 {
340 }, // zero_zero
341 {
342 }, // zero_one
343 {
344 }, // one_zero
345 {
346 }, // one_one
347 },
348 {coords_t{std::array{ring_t(-1), ring_t(-1)}, units...},
349 coords_t{std::array{ring_t(-1), ring_t{}}, units...},
350 coords_t{std::array{ring_t{}, ring_t(-1)}, units...},
351 coords_t{std::array{ring_t{}, ring_t{}}, units...},
352 coords_t{std::array{ring_t{}, ring_t(1)}, units...},
353 coords_t{std::array{ring_t(1), ring_t{}}, units...},
354 coords_t{std::array{ring_t(1), ring_t(1)}, units...}
355 }
356 };
357
358 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
359 {
360 add_dim_2_distinguished_origin_transitions(g, test, units...);
361 }
362
363 if constexpr(Coordinates::has_freely_mutable_components)
364 {
365 add_dim_2_free_mutations(g, test);
366 }
367
368 return g;
369 }
370
371 template<class... Units>
372 static void add_dim_1_common_transitions(maths::network auto& g, regular_test& test, Units... units)
373 {
374 // Joins from zero
375 add_transition<coords_t>(
376 g,
377 dim_1_label::zero,
378 dim_1_label::one,
379 test.report("(0) + (1)"),
380 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
381 );
382
383 add_transition<coords_t>(
384 g,
385 dim_1_label::zero,
386 dim_1_label::one,
387 test.report("(0) += (1)"),
388 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
389 );
390
391 // Joins from one
392
393 add_transition<coords_t>(
394 g,
395 dim_1_label::one,
396 dim_1_label::zero,
397 test.report("(1) - (1)"),
398 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
399 );
400
401 add_transition<coords_t>(
402 g,
403 dim_1_label::one,
404 dim_1_label::zero,
405 test.report("(1) -= (1)"),
406 [&](coords_t p) -> coords_t { return p -= disp_t{ring_t(1), units...}; }
407 );
408
409 add_transition<coords_t>(
410 g,
411 dim_1_label::one,
412 dim_1_label::one,
413 test.report("+(1)"),
414 [](coords_t p) -> coords_t { return +p;}
415 );
416
417 add_transition<coords_t>(
418 g,
419 dim_1_label::one,
420 dim_1_label::two,
421 test.report("(1) + (1)"),
422 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
423 );
424
425 add_transition<coords_t>(
426 g,
427 dim_1_label::one,
428 dim_1_label::two,
429 test.report("(1) += (1)"),
430 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
431 );
432
433 // Joins from two
434
435 add_transition<coords_t>(
436 g,
437 dim_1_label::two,
438 dim_1_label::one,
439 test.report("(2) - (1)"),
440 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
441 );
442 }
443
444 template<class... Units>
445 static void add_dim_1_negative_transitions(maths::network auto& g, regular_test& test, Units... units)
446 {
447 g.add_node(ring_t(-1), units...);
448
449 // Joins to neg_one
450 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
451 {
452 add_transition<coords_t>(
453 g,
454 dim_1_label::one,
455 dim_1_label::neg_one,
456 test.report("-(1)"),
457 [](coords_t p) -> coords_t { return -p; },
458 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
459 );
460 }
461
462 add_transition<coords_t>(
463 g,
464 dim_1_label::one,
465 dim_1_label::neg_one,
466 test.report("(1) - (2)"),
467 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(2), units...}; },
468 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
469 );
470
471
472 // Joins from neg_one
473 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
474 {
475 add_transition<coords_t>(
476 g,
477 dim_1_label::neg_one,
478 dim_1_label::one,
479 test.report("- (-1)"),
480 [](coords_t p) -> coords_t { return -p; },
481 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
482 );
483 }
484
485 add_transition<coords_t>(
486 g,
487 dim_1_label::neg_one,
488 dim_1_label::neg_one,
489 test.report("+ (-1)"),
490 [](coords_t p) -> coords_t { return +p; }
491 );
492
493 if constexpr(Coordinates::has_freely_mutable_components)
494 {
495 add_transition<coords_t>(
496 g,
497 dim_1_label::neg_one,
498 dim_1_label::zero,
499 test.report("(-1) += 1"),
500 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1; return p; },
501 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
502 );
503
504 add_transition<coords_t>(
505 g,
506 dim_1_label::neg_one,
507 dim_1_label::zero,
508 test.report("(-1) + 1"),
509 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1; return p; },
510 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
511 );
512 }
513 }
514
515 template<class... Units>
516 static void add_dim_1_attempted_negative_transitions(maths::network auto& g, regular_test& test, Units... units)
517 {
518 add_transition<coords_t>(
519 g,
520 dim_1_label::one,
521 dim_1_label::one,
522 test.report("(1) -= (2)"),
523 [&](coords_t p) -> coords_t {
524 test.check_exception_thrown<std::domain_error>("", [&](){ return p -= disp_t{ring_t(2), units...};});
525 return p;
526 }
527 );
528
529 add_transition<coords_t>(
530 g,
531 dim_1_label::one,
532 dim_1_label::one,
533 test.report("(1) - (2)"),
534 [&](coords_t p) -> coords_t {
535 test.check_exception_thrown<std::domain_error>("", [&](){ return p = (p - disp_t{ring_t(2), units...}); });
536 return p;
537 }
538 );
539
540 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
541 {
542 add_transition<coords_t>(
543 g,
544 dim_1_label::one,
545 dim_1_label::one,
546 test.report("(1) *= ring_t{-1}"),
547 [&](coords_t v) -> coords_t {
548 test.check_exception_thrown<std::domain_error>("", [&v](){ return v *= ring_t{-1}; });
549 return v;
550 }
551 );
552
553 add_transition<coords_t>(
554 g,
555 dim_1_label::one,
556 dim_1_label::one,
557 test.report("ring_t{-1} * (1)"),
558 [&test](coords_t v) -> coords_t {
559 test.check_exception_thrown<std::domain_error>("", [&v](){ return v = ring_t{-1} * v; });
560 return v;
561 }
562 );
563
564 add_transition<coords_t>(
565 g,
566 dim_1_label::one,
567 dim_1_label::one,
568 test.report("(1) /= ring_t{-1}"),
569 [&test](coords_t v) -> coords_t {
570 test.check_exception_thrown<std::domain_error>("", [&v](){ return v /= ring_t{-1}; });
571 return v;
572 }
573 );
574
575 add_transition<coords_t>(
576 g,
577 dim_1_label::one,
578 dim_1_label::one,
579 test.report("(1) / ring_t{-1}"),
580 [&test](coords_t v) -> coords_t {
581 test.check_exception_thrown<std::domain_error>("", [&v](){ return v = v / ring_t{-1}; });
582 return v;
583 }
584 );
585 }
586 }
587
588 static void add_dim_1_distinguished_origin_transitions(maths::network auto& g, regular_test& test)
589 {
590 // (0) --> (1)
591 add_transition<coords_t>(
592 g,
593 dim_1_label::one,
594 dim_1_label::zero,
595 test.report("(1) * ring_t{}"),
596 [](coords_t v) -> coords_t { return v * ring_t{}; }
597 );
598
599 add_transition<coords_t>(
600 g,
601 dim_1_label::one,
602 dim_1_label::zero,
603 test.report("ring_t{} * (1)"),
604 [](coords_t v) -> coords_t { return ring_t{} *v; }
605 );
606
607 add_transition<coords_t>(
608 g,
609 dim_1_label::one,
610 dim_1_label::zero,
611 test.report("(1) *= ring_t{}"),
612 [](coords_t v) -> coords_t { return v *= ring_t{}; }
613 );
614
615 // (1) --> (2)
616
617 add_transition<coords_t>(
618 g,
619 dim_1_label::one,
620 dim_1_label::two,
621 test.report("(1) * ring_t{2}"),
622 [](coords_t v) -> coords_t { return v * ring_t{2}; }
623 );
624
625 add_transition<coords_t>(
626 g,
627 dim_1_label::one,
628 dim_1_label::two,
629 test.report("ring_t{2} * (1)"),
630 [](coords_t v) -> coords_t { return ring_t{2} *v; }
631 );
632
633 add_transition<coords_t>(
634 g,
635 dim_1_label::one,
636 dim_1_label::two,
637 test.report("(1) *= ring_t{2}"),
638 [](coords_t v) -> coords_t { return v *= ring_t{2}; }
639 );
640
641 // (2) --> (1)
642
643 if constexpr(maths::vector_space<module_t>)
644 {
645 add_transition<coords_t>(
646 g,
647 dim_1_label::two,
648 dim_1_label::one,
649 test.report("(2) / ring_t{2}"),
650 [](coords_t v) -> coords_t { return v / ring_t{2}; }
651 );
652
653 add_transition<coords_t>(
654 g,
655 dim_1_label::two,
656 dim_1_label::one,
657 test.report("(2) /= ring_t{2}"),
658 [](coords_t v) -> coords_t { return v /= ring_t{2}; }
659 );
660 }
661 }
662
663 static void add_dim_1_free_mutations(maths::network auto& g, regular_test& test)
664 {
665 // (1) --> (0)
666 add_transition<coords_t>(
667 g,
668 dim_1_label::one,
669 dim_1_label::zero,
670 test.report("(1)[0] * ring_t{}"),
671 [](coords_t v) -> coords_t { v[0] *= ring_t{}; return v; }
672 );
673
674 add_transition<coords_t>(
675 g,
676 dim_1_label::one,
677 dim_1_label::zero,
678 test.report("(1).begin[0] * ring_t{}"),
679 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{}; return v; }
680 );
681
682 add_transition<coords_t>(
683 g,
684 dim_1_label::one,
685 dim_1_label::zero,
686 test.report("(1).rbegin[0] * ring_t{}"),
687 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{}; return v; }
688 );
689
690 // (1) --> (2)
691
692 add_transition<coords_t>(
693 g,
694 dim_1_label::one,
695 dim_1_label::two,
696 test.report("(1)[0] * ring_t{2}"),
697 [](coords_t v) -> coords_t { v[0] *= ring_t{2}; return v; }
698 );
699
700 add_transition<coords_t>(
701 g,
702 dim_1_label::one,
703 dim_1_label::two,
704 test.report("(1).begin[0] * ring_t{2}"),
705 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{2}; return v; }
706 );
707
708 add_transition<coords_t>(
709 g,
710 dim_1_label::one,
711 dim_1_label::two,
712 test.report("(1).rbegin[0] * ring_t{2}"),
713 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{2}; return v; }
714 );
715 }
716
717 static void add_dim_2_distinguished_origin_transitions(maths::network auto& g, regular_test& test)
718 {
719 // (-1, -1) --> (1, 1)
720
721 add_transition<coords_t>(
722 g,
723 dim_2_label::neg_one_neg_one,
724 dim_2_label::one_one,
725 test.report("- (-1, -1)"),
726 [](coords_t v) -> coords_t { return -v; }
727 );
728
729 add_transition<coords_t>(
730 g,
731 dim_2_label::neg_one_neg_one,
732 dim_2_label::one_one,
733 test.report("(-1, -1) *= -1"),
734 [](coords_t v) -> coords_t { return v *= ring_t{-1}; }
735 );
736
737 add_transition<coords_t>(
738 g,
739 dim_2_label::neg_one_neg_one,
740 dim_2_label::one_one,
741 test.report("(-1, -1) * -1"),
742 [](coords_t v) -> coords_t { return v * ring_t{-1}; }
743 );
744
745 if constexpr(maths::vector_space<module_t>)
746 {
747 add_transition<coords_t>(
748 g,
749 dim_2_label::neg_one_neg_one,
750 dim_2_label::one_one,
751 test.report("(-1, -1) /= -1"),
752 [](coords_t v) -> coords_t { return v /= ring_t{-1}; }
753 );
754
755 add_transition<coords_t>(
756 g,
757 dim_2_label::neg_one_neg_one,
758 dim_2_label::one_one,
759 test.report("(-1, -1) / -1"),
760 [](coords_t v) -> coords_t { return v / ring_t{-1}; }
761 );
762 }
763 }
764
765 static void add_dim_2_free_mutations(maths::network auto& g, regular_test& test)
766 {
767 // (-1, -1) --> (-1, 0)
768
769 add_transition<coords_t>(
770 g,
771 dim_2_label::neg_one_neg_one,
772 dim_2_label::neg_one_zero,
773 test.report("(-1, -1)[1] *= 0"),
774 [](coords_t v) -> coords_t { v[1] *= ring_t{}; return v; }
775 );
776
777 add_transition<coords_t>(
778 g,
779 dim_2_label::neg_one_neg_one,
780 dim_2_label::neg_one_zero,
781 test.report("(-1, -1).begin()[1] *= 0"),
782 [](coords_t v) -> coords_t { v.begin()[1] *= ring_t{}; return v; }
783 );
784
785 add_transition<coords_t>(
786 g,
787 dim_2_label::neg_one_neg_one,
788 dim_2_label::neg_one_zero,
789 test.report("(-1, -1).rbegin()[0] *= 0"),
790 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{}; return v; }
791 );
792
793 // (0, 1) --> (1, 1)
794 add_transition<coords_t>(
795 g,
796 dim_2_label::zero_one,
797 dim_2_label::one_one,
798 test.report("(0, 1)[0] += 1"),
799 [](coords_t v) -> coords_t { v[0] += ring_t{1}; return v; }
800 );
801
802 add_transition<coords_t>(
803 g,
804 dim_2_label::zero_one,
805 dim_2_label::one_one,
806 test.report("(0, 1).begin[0] += 1"),
807 [](coords_t v) -> coords_t { v.begin()[0] += ring_t{1}; return v; }
808 );
809
810 add_transition<coords_t>(
811 g,
812 dim_2_label::zero_one,
813 dim_2_label::one_one,
814 test.report("(0, 1).rbegin[1] += 1"),
815 [](coords_t v) -> coords_t { v.rbegin()[1] += ring_t{1}; return v; }
816 );
817 }
818 };
819}
bool check(CheckType flavour, std::string description, test_logger< Mode > &logger, Iter first, Sentinel last, PredictionIter predictionFirst, PredictionSentinel predictionLast, tutor< Advisor > advisor={})
The workhorse for comparing the contents of ranges.
Definition: FreeCheckers.hpp:377
inverted_ordering
Definition: GeometryTestingUtilities.hpp:169
Utilities for checking regular semantics.
Abstractions pertaining to vector spaces, affine spaces and their generalizations.
Facility to define tests via a graph comprising states of an object and transitions between them.
Forward declaration for the coordinates class template.
Definition: Spaces.hpp:1372
Definition: DynamicGraph.hpp:303
class template from which all concrete tests should derive.
Definition: FreeTestCore.hpp:144
Definition: GeometryTestingUtilities.hpp:221
Definition: TestLogger.hpp:183
A concept to determine if a basis is appropriate for a particular free module.
Definition: Spaces.hpp:620
Definition: GraphTraits.hpp:18
Definition: GeometryTestingUtilities.hpp:106
Definition: GeometryTestingUtilities.hpp:122
Definition: FreeCheckers.hpp:82
Definition: FreeCheckers.hpp:87
Definition: GeometryTestingUtilities.hpp:52
Definition: GeometryTestingUtilities.hpp:98
Definition: GeometryTestingUtilities.hpp:113
Definition: GeometryTestingUtilities.hpp:73
Definition: StateTransitionUtilities.hpp:77
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78