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 inline constexpr bool can_multiply{
23 requires(const T& t, const U& u) { t * u; }
24 };
25
26 template<class T, class U>
27 inline constexpr bool can_divide{
28 requires(const T& t, const U& u) { t / u; }
29 };
30
31 template<class T, class U>
32 inline constexpr bool can_add{
33 requires(const T& t, const U& u) { t + u; }
34 };
35
36 template<class T, class U>
37 inline constexpr bool can_subtract{
38 requires(const T& t, const U& u) { t - u; }
39 };
40
41 template<class T, class U>
42 inline constexpr bool addition_combinable{
43 requires(T& t, const U& u) { { t += u } -> std::convertible_to<T>; }
44 };
45
46 template<class T, class U>
47 inline constexpr bool subtraction_combinable{
48 requires(T& t, const U& u) { { t -= u } -> std::convertible_to<T>; }
49 };
50
51 template<class T>
52 inline constexpr bool has_unary_plus{
53 requires(const T& t) { { +t } -> std::convertible_to<T>; }
54 };
55
56 template<class T>
57 inline constexpr bool has_unary_minus{
58 requires(const T& t) { { -t } -> std::convertible_to<T>; }
59 };
60
61 template<class T>
62 struct is_complex : std::false_type {};
63
64 template<std::floating_point T>
65 struct is_complex<std::complex<T>> : std::true_type {};
66
67 template<class T>
68 inline constexpr bool is_complex_v{is_complex<T>::value};
69
70 template<class T>
71 using is_complex_t = typename is_complex<T>::type;
72
73 template<class B>
74 inline constexpr bool is_orthonormal_basis_v{
75 requires {
76 typename B::orthonormal;
77 requires std::same_as<typename B::orthonormal, std::true_type>;
78 }
79 };
80
81 template<class Set, maths::weak_field Field, std::size_t D>
83 {
84 using set_type = Set;
85 using field_type = Field;
86 using is_vector_space = std::true_type;
87 using admits_canonical_basis = std::true_type;
88 constexpr static std::size_t dimension{D};
89
90 template<maths::basis Basis>
91 requires std::floating_point<field_type>&& is_orthonormal_basis_v<Basis>
92 [[nodiscard]]
93 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
94 {
95 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); });
96 }
97
98 template<maths::basis Basis>
99 requires is_complex_v<field_type>&& is_orthonormal_basis_v<Basis>
100 [[nodiscard]]
101 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
102 {
103 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); });
104 }
105 };
106
107 template<class Set, maths::weak_field Field, std::size_t D>
109 {
110 using set_type = Set;
112 using is_affine_space = std::true_type;
113 };
114
115 template<class Set, maths::weak_field Field, std::size_t D>
117 {
119 using is_basis = std::true_type;
120 using orthonormal = std::true_type;
121 };
122
123 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
125 {
126 using set_type = Set;
127 using commutative_ring_type = Ring;
128 using is_free_module = std::true_type;
129 using admits_canonical_basis = std::true_type;
130 constexpr static std::size_t dimension{D};
131 };
132
133 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
135 {
136 using is_basis = std::true_type;
138 };
139
140 template<maths::convex_space ConvexSpace, maths::basis Basis, class... Ts>
142 struct value_tester<maths::coordinates<ConvexSpace, Basis, Ts...>>
143 {
144 using coord_type = maths::coordinates<ConvexSpace, Basis, Ts...>;
145 using commutative_ring_type = typename coord_type::commutative_ring_type;
146 constexpr static std::size_t D{coord_type::dimension};
147
148 template<test_mode Mode>
149 static void test(equality_check_t, test_logger<Mode>& logger, const coord_type& actual, const coord_type& prediction)
150 {
151 check(equality, "Wrapped values", logger, actual.values(), prediction.values());
152 if constexpr(D == 1)
153 {
154 check(equality, "Wrapped value", logger, actual.value(), prediction.value());
155 if constexpr(std::convertible_to<commutative_ring_type, bool>)
156 check(equality, "Conversion to bool", logger, static_cast<bool>(actual), static_cast<bool>(prediction));
157 }
158
159 for(auto i : std::views::iota(0uz, D))
160 {
161 check(equality, std::format("Value at index {}", i), logger, actual[i], prediction[i]);
162 }
163 }
164
165 template<test_mode Mode>
166 static void test(equivalence_check_t, test_logger<Mode>& logger, const coord_type& actual, const std::array<commutative_ring_type, D>& prediction)
167 {
168 check(equality, "Wrapped values", logger, actual.values(), std::span<const commutative_ring_type, D>{prediction});
169 check(equivalence, "Iterators", logger, std::ranges::subrange{actual.begin(), actual.end()}, prediction);
170 check(equivalence, "c-Iterators", logger, std::ranges::subrange{actual.cbegin(), actual.cend()}, prediction);
171 check(equivalence, "r-Iterators", logger, std::ranges::subrange{actual.rbegin(), actual.rend()}, prediction);
172 check(equivalence, "cr-Iterators", logger, std::ranges::subrange{actual.crbegin(), actual.crend()}, prediction);
173
174 for(auto i : std::views::iota(0uz, D))
175 {
176 check(equality, "operator[]", logger, actual[i], prediction[i]);
177 }
178 }
179 };
180
183 enum class inverted_ordering : bool {no, yes};
184
185 template<class Label>
186 requires std::convertible_to<Label, std::size_t>
187 [[nodiscard]]
188 std::weak_ordering to_ordering(Label From, Label To, inverted_ordering invert)
189 {
190 const bool inverted{invert == inverted_ordering::yes};
191 return (((From < To) && !inverted) || ((From > To) && inverted)) ? std::weak_ordering::less
192 : (((From > To) && !inverted) || ((From < To) && inverted)) ? std::weak_ordering::greater
193 : std::weak_ordering::equivalent;
194 }
195
196 template<maths::network Graph, class Label, class Fn>
197 requires std::convertible_to<Label, std::size_t>
198 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, std::weak_ordering ordering)
199 {
200 g.join(From, To, std::string{message}, f, ordering);
201 }
202
203 template<maths::network Graph, class Label, class Fn>
204 requires std::convertible_to<Label, std::size_t>
205 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f)
206 {
207 g.join(From, To, std::string{message}, f);
208 }
209
210 template<class Coords, maths::network Graph, class Label, class Fn>
211 requires std::is_invocable_r_v<Coords, Fn, Coords> && std::convertible_to<Label, std::size_t>
212 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, inverted_ordering invert={})
213 {
214 using ring_t = Coords::commutative_ring_type;
215 constexpr static auto dimension{Coords::dimension};
216
217 if constexpr((dimension == 1) && std::totally_ordered<ring_t>)
218 {
219 add_transition(g, From, To, message, f, to_ordering(From, To, invert));
220 }
221 else
222 {
223 add_transition(g, From, To, message, f);
224 }
225 }
226
227 template<class Coordinates>
229 {
230 enum dim_1_label{ two, one, zero, neg_one };
231 enum dim_2_label{ neg_one_neg_one, neg_one_zero, zero_neg_one, zero_zero, zero_one, one_zero, one_one, one_two };
232
234 using coords_t = Coordinates;
235 using space_t = Coordinates::space_type;
236 using disp_t = coords_t::displacement_coordinates_type;
237 using module_t = coords_t::free_module_type;
238 using ring_t = coords_t::commutative_ring_type;
239 using units_t = coords_t::basis_isomorphism_type;
240 constexpr static std::size_t dimension{Coordinates::dimension};
241 constexpr static bool orderable{(dimension == 1) && std::totally_ordered<ring_t>};
242 constexpr static bool has_distinguished_origin{maths::has_distinguished_origin_v<space_t>};
243
244 regular_test& m_Test;
245 graph_type m_Graph;
246 public:
248 : m_Test{t}
249 , m_Graph{make_graph(m_Test)}
250 {}
251
252 void execute()
253 {
254 transition_checker<coords_t>::check("", m_Graph, make_checker());
255 }
256 private:
257 static graph_type make_graph(regular_test& test)
258 {
259 return do_make_graph(test);
260 }
261
262 [[nodiscard]]
263 static graph_type do_make_graph(regular_test& test)
264 {
265 if constexpr (dimension == 1) return make_dim_1_transition_graph(test);
266 else if constexpr(dimension == 2) return make_dim_2_transition_graph(test);
267 }
268
269 [[nodiscard]]
270 auto make_checker() const
271 {
272 if constexpr(orderable)
273 {
274 return
275 [&test=m_Test](std::string_view description, const coords_t& obtained, const coords_t& prediction, const coords_t& parent, std::weak_ordering ordering) {
276 test.check(equality, description, obtained, prediction);
277 if(ordering != std::weak_ordering::equivalent)
278 test.check_semantics(description, prediction, parent, ordering);
279 };
280 }
281 else
282 {
283 return
284 [&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) {
285 test.check(equality, description, obtained, prediction);
286 if(host!= target) test.check_semantics(description, prediction, parent);
287 };
288 }
289 }
290
291 static graph_type make_dim_1_transition_graph(regular_test& test)
292 {
293 graph_type g{
294 {
295 {}, {}, {}
296 },
297 {coords_t{ring_t(2), units_t{}}, coords_t{ring_t(1), units_t{}}, coords_t{}}
298 };
299
300 add_dim_1_common_transitions(g, test);
301
302 if constexpr(has_unary_minus<Coordinates>)
303 {
304 add_dim_1_negative_transitions(g, test);
305 }
306 else if constexpr(maths::defines_half_line_validator_v<typename Coordinates::validator_type> && std::is_signed_v<ring_t>)
307 {
308 add_dim_1_attempted_negative_transitions(g, test);
309 }
310
311 if constexpr(has_distinguished_origin)
312 {
313 add_dim_1_distinguished_origin_transitions(g, test);
314 }
315
316 if constexpr(Coordinates::has_freely_mutable_components)
317 {
318 add_dim_1_free_mutations(g, test);
319 }
320
321 if constexpr(std::constructible_from<coords_t, ring_t, ring_t>)
322 {
323 add_dim_1_no_unit_construction(g, test);
324 }
325
326 return g;
327 }
328
329 static graph_type make_dim_2_transition_graph(regular_test& test)
330 {
331 using edge_t = transition_checker<coords_t>::edge;
332 graph_type g{
333 {
334 {
335 edge_t{dim_2_label::neg_one_neg_one, test.report("+ (-1, -1)"), [](coords_t v) -> coords_t { return +v; }},
336 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_t{}}; }},
337 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_t{}}; }},
338 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_t{}}; }},
339 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_t{}}; }}
340 }, // neg_one_neg_one
341 {
342 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_t{}}; }},
343 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_t{}}; }}
344 }, // neg_one_zero
345 {
346 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_t{}}; }},
347 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_t{}}; }}
348 }, // zero_neg_one
349 {
350 }, // zero_zero
351 {
352 }, // zero_one
353 {
354 }, // one_zero
355 {
356 }, // one_one
357 {
358 }, // one_two
359 },
360 {coords_t{std::array{ring_t(-1), ring_t(-1)}, units_t{}},
361 coords_t{std::array{ring_t(-1), ring_t{}}, units_t{}},
362 coords_t{std::array{ring_t{}, ring_t(-1)}, units_t{}},
363 coords_t{std::array{ring_t{}, ring_t{}}, units_t{}},
364 coords_t{std::array{ring_t{}, ring_t(1)}, units_t{}},
365 coords_t{std::array{ring_t(1), ring_t{}}, units_t{}},
366 coords_t{std::array{ring_t(1), ring_t(1)}, units_t{}},
367 coords_t{std::array{ring_t(1), ring_t(2)}, units_t{}}
368 }
369 };
370
371 if constexpr(has_distinguished_origin)
372 {
373 add_dim_2_distinguished_origin_transitions(g, test);
374 }
375
376 if constexpr(Coordinates::has_freely_mutable_components)
377 {
378 add_dim_2_free_mutations(g, test);
379 }
380
381 if constexpr(std::constructible_from<coords_t, ring_t, ring_t>)
382 {
383 add_dim_2_no_unit_construction(g, test);
384 }
385
386 return g;
387 }
388
389 static void add_dim_1_common_transitions(maths::network auto& g, regular_test& test)
390 {
391 // Joins from zero
392 add_transition<coords_t>(
393 g,
394 dim_1_label::zero,
395 dim_1_label::one,
396 test.report("(0) + (1)"),
397 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units_t{}}; }
398 );
399
400 add_transition<coords_t>(
401 g,
402 dim_1_label::zero,
403 dim_1_label::one,
404 test.report("(0) += (1)"),
405 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units_t{}}; }
406 );
407
408 // Joins from one
409
410 add_transition<coords_t>(
411 g,
412 dim_1_label::one,
413 dim_1_label::zero,
414 test.report("(1) - (1)"),
415 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units_t{}}; }
416 );
417
418 add_transition<coords_t>(
419 g,
420 dim_1_label::one,
421 dim_1_label::zero,
422 test.report("(1) -= (1)"),
423 [&](coords_t p) -> coords_t { return p -= disp_t{ring_t(1), units_t{}}; }
424 );
425
426 add_transition<coords_t>(
427 g,
428 dim_1_label::one,
429 dim_1_label::one,
430 test.report("+(1)"),
431 [](coords_t p) -> coords_t { return +p;}
432 );
433
434 add_transition<coords_t>(
435 g,
436 dim_1_label::one,
437 dim_1_label::two,
438 test.report("(1) + (1)"),
439 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units_t{}}; }
440 );
441
442 add_transition<coords_t>(
443 g,
444 dim_1_label::one,
445 dim_1_label::two,
446 test.report("(1) += (1)"),
447 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units_t{}}; }
448 );
449
450 // Joins from two
451
452 add_transition<coords_t>(
453 g,
454 dim_1_label::two,
455 dim_1_label::one,
456 test.report("(2) - (1)"),
457 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units_t{}}; }
458 );
459 }
460
461 template<class... Units>
462 static void add_dim_1_negative_transitions(maths::network auto& g, regular_test& test)
463 {
464 g.add_node(ring_t(-1), units_t{});
465
466 // Joins to neg_one
467 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
468 {
469 add_transition<coords_t>(
470 g,
471 dim_1_label::one,
472 dim_1_label::neg_one,
473 test.report("-(1)"),
474 [](coords_t p) -> coords_t { return -p; },
475 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
476 );
477 }
478
479 add_transition<coords_t>(
480 g,
481 dim_1_label::one,
482 dim_1_label::neg_one,
483 test.report("(1) - (2)"),
484 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(2), units_t{}}; },
485 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
486 );
487
488
489 // Joins from neg_one
490 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
491 {
492 add_transition<coords_t>(
493 g,
494 dim_1_label::neg_one,
495 dim_1_label::one,
496 test.report("- (-1)"),
497 [](coords_t p) -> coords_t { return -p; },
498 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
499 );
500 }
501
502 add_transition<coords_t>(
503 g,
504 dim_1_label::neg_one,
505 dim_1_label::neg_one,
506 test.report("+ (-1)"),
507 [](coords_t p) -> coords_t { return +p; }
508 );
509
510 if constexpr(Coordinates::has_freely_mutable_components)
511 {
512 add_transition<coords_t>(
513 g,
514 dim_1_label::neg_one,
515 dim_1_label::zero,
516 test.report("(-1) += 1"),
517 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1; return p; },
518 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
519 );
520
521 add_transition<coords_t>(
522 g,
523 dim_1_label::neg_one,
524 dim_1_label::zero,
525 test.report("(-1) + 1"),
526 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1; return p; },
527 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
528 );
529 }
530 }
531
532 static void add_dim_1_attempted_negative_transitions(maths::network auto& g, regular_test& test)
533 {
534 add_transition<coords_t>(
535 g,
536 dim_1_label::one,
537 dim_1_label::one,
538 test.report("(1) -= (2)"),
539 [&](coords_t p) -> coords_t {
540 test.check_exception_thrown<std::domain_error>("", [&](){ return p -= disp_t{ring_t(2), units_t{}};});
541 return p;
542 }
543 );
544
545 add_transition<coords_t>(
546 g,
547 dim_1_label::one,
548 dim_1_label::one,
549 test.report("(1) - (2)"),
550 [&](coords_t p) -> coords_t {
551 test.check_exception_thrown<std::domain_error>("", [&](){ return p = (p - disp_t{ring_t(2), units_t{}}); });
552 return p;
553 }
554 );
555
556 if constexpr(has_distinguished_origin)
557 {
558 add_transition<coords_t>(
559 g,
560 dim_1_label::one,
561 dim_1_label::one,
562 test.report("(1) *= ring_t{-1}"),
563 [&](coords_t v) -> coords_t {
564 test.check_exception_thrown<std::domain_error>("", [&v](){ return v *= ring_t{-1}; });
565 return v;
566 }
567 );
568
569 add_transition<coords_t>(
570 g,
571 dim_1_label::one,
572 dim_1_label::one,
573 test.report("ring_t{-1} * (1)"),
574 [&test](coords_t v) -> coords_t {
575 test.check_exception_thrown<std::domain_error>("", [&v](){ return v = ring_t{-1} * v; });
576 return v;
577 }
578 );
579
580 add_transition<coords_t>(
581 g,
582 dim_1_label::one,
583 dim_1_label::one,
584 test.report("(1) /= ring_t{-1}"),
585 [&test](coords_t v) -> coords_t {
586 test.check_exception_thrown<std::domain_error>("", [&v](){ return v /= ring_t{-1}; });
587 return v;
588 }
589 );
590
591 add_transition<coords_t>(
592 g,
593 dim_1_label::one,
594 dim_1_label::one,
595 test.report("(1) / ring_t{-1}"),
596 [&test](coords_t v) -> coords_t {
597 test.check_exception_thrown<std::domain_error>("", [&v](){ return v = v / ring_t{-1}; });
598 return v;
599 }
600 );
601 }
602 }
603
604 static void add_dim_1_distinguished_origin_transitions(maths::network auto& g, regular_test& test)
605 {
606 // (0) --> (1)
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 add_transition<coords_t>(
616 g,
617 dim_1_label::one,
618 dim_1_label::zero,
619 test.report("(1) * span{ring_t{}}"),
620 [](coords_t v) -> coords_t { return v * std::array{ring_t{}}; }
621 );
622
623 add_transition<coords_t>(
624 g,
625 dim_1_label::one,
626 dim_1_label::zero,
627 test.report("ring_t{} * (1)"),
628 [](coords_t v) -> coords_t { return ring_t{} * v; }
629 );
630
631 add_transition<coords_t>(
632 g,
633 dim_1_label::one,
634 dim_1_label::zero,
635 test.report("span{ring_t{}} * (1)"),
636 [](coords_t v) -> coords_t { return std::array{ring_t{}} * v; }
637 );
638
639 add_transition<coords_t>(
640 g,
641 dim_1_label::one,
642 dim_1_label::zero,
643 test.report("(1) *= ring_t{}"),
644 [](coords_t v) -> coords_t { return v *= ring_t{}; }
645 );
646
647 add_transition<coords_t>(
648 g,
649 dim_1_label::one,
650 dim_1_label::zero,
651 test.report("(1) *= span{ring_t{}}"),
652 [](coords_t v) -> coords_t { return v *= std::array{ring_t{}}; }
653 );
654
655 // (1) --> (2)
656
657 add_transition<coords_t>(
658 g,
659 dim_1_label::one,
660 dim_1_label::two,
661 test.report("(1) * ring_t{2}"),
662 [](coords_t v) -> coords_t { return v * ring_t{2}; }
663 );
664
665 add_transition<coords_t>(
666 g,
667 dim_1_label::one,
668 dim_1_label::two,
669 test.report("(1) * array{ring_t{2}}"),
670 [](coords_t v) -> coords_t { return v * std::array{ring_t{2}}; }
671 );
672
673 add_transition<coords_t>(
674 g,
675 dim_1_label::one,
676 dim_1_label::two,
677 test.report("ring_t{2} * (1)"),
678 [](coords_t v) -> coords_t { return ring_t{2} * v; }
679 );
680
681 add_transition<coords_t>(
682 g,
683 dim_1_label::one,
684 dim_1_label::two,
685 test.report("span{ring_t{2}} * (1)"),
686 [](coords_t v) -> coords_t { return std::array{ring_t{2}} * v; }
687 );
688
689 add_transition<coords_t>(
690 g,
691 dim_1_label::one,
692 dim_1_label::two,
693 test.report("(1) *= ring_t{2}"),
694 [](coords_t v) -> coords_t { return v *= ring_t{2}; }
695 );
696
697 add_transition<coords_t>(
698 g,
699 dim_1_label::one,
700 dim_1_label::two,
701 test.report("(1) *= span{ring_t{2}}"),
702 [](coords_t v) -> coords_t { return v *= std::array{ring_t{2}}; }
703 );
704
705 // (2) --> (1)
706
707 if constexpr(maths::vector_space<module_t>)
708 {
709 add_transition<coords_t>(
710 g,
711 dim_1_label::two,
712 dim_1_label::one,
713 test.report("(2) / ring_t{2}"),
714 [](coords_t v) -> coords_t { return v / ring_t{2}; }
715 );
716
717 add_transition<coords_t>(
718 g,
719 dim_1_label::two,
720 dim_1_label::one,
721 test.report("(2) / span{ring_t{2}}"),
722 [](coords_t v) -> coords_t { return v / std::array{ring_t{2}}; }
723 );
724
725 add_transition<coords_t>(
726 g,
727 dim_1_label::two,
728 dim_1_label::one,
729 test.report("(2) /= ring_t{2}"),
730 [](coords_t v) -> coords_t { return v /= ring_t{2}; }
731 );
732
733 add_transition<coords_t>(
734 g,
735 dim_1_label::two,
736 dim_1_label::one,
737 test.report("(2) /= span{ring_t{2}}"),
738 [](coords_t v) -> coords_t { return v /= std::array{ring_t{2}}; }
739 );
740 }
741 }
742
743 static void add_dim_1_free_mutations(maths::network auto& g, regular_test& test)
744 {
745 // (1) --> (0)
746 add_transition<coords_t>(
747 g,
748 dim_1_label::one,
749 dim_1_label::zero,
750 test.report("(1)[0] * ring_t{}"),
751 [](coords_t v) -> coords_t { v[0] *= ring_t{}; return v; }
752 );
753
754 add_transition<coords_t>(
755 g,
756 dim_1_label::one,
757 dim_1_label::zero,
758 test.report("(1).begin[0] * ring_t{}"),
759 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{}; return v; }
760 );
761
762 add_transition<coords_t>(
763 g,
764 dim_1_label::one,
765 dim_1_label::zero,
766 test.report("(1).rbegin[0] * ring_t{}"),
767 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{}; return v; }
768 );
769
770 // (1) --> (2)
771
772 add_transition<coords_t>(
773 g,
774 dim_1_label::one,
775 dim_1_label::two,
776 test.report("(1)[0] * ring_t{2}"),
777 [](coords_t v) -> coords_t { v[0] *= ring_t{2}; return v; }
778 );
779
780 add_transition<coords_t>(
781 g,
782 dim_1_label::one,
783 dim_1_label::two,
784 test.report("(1).begin[0] * ring_t{2}"),
785 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{2}; return v; }
786 );
787
788 add_transition<coords_t>(
789 g,
790 dim_1_label::one,
791 dim_1_label::two,
792 test.report("(1).rbegin[0] * ring_t{2}"),
793 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{2}; return v; }
794 );
795 }
796
797 static void add_dim_1_no_unit_construction(maths::network auto& g, regular_test& test)
798 {
799 // (0) --> (1)
800 add_transition<coords_t>(
801 g,
802 dim_1_label::zero,
803 dim_1_label::one,
804 test.report("(0) + (1)"),
805 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1)}; }
806 );
807 }
808
809 static void add_dim_2_distinguished_origin_transitions(maths::network auto& g, regular_test& test)
810 {
811 // (-1, -1) --> (1, 1)
812
813 add_transition<coords_t>(
814 g,
815 dim_2_label::neg_one_neg_one,
816 dim_2_label::one_one,
817 test.report("- (-1, -1)"),
818 [](coords_t v) -> coords_t { return -v; }
819 );
820
821 add_transition<coords_t>(
822 g,
823 dim_2_label::neg_one_neg_one,
824 dim_2_label::one_one,
825 test.report("(-1, -1) *= -1"),
826 [](coords_t v) -> coords_t { return v *= ring_t{-1}; }
827 );
828
829 add_transition<coords_t>(
830 g,
831 dim_2_label::neg_one_neg_one,
832 dim_2_label::one_one,
833 test.report("(-1, -1) *= span{-1, -1}"),
834 [](coords_t v) -> coords_t { return v *= std::array<ring_t, 2>{-1, -1}; }
835 );
836
837 add_transition<coords_t>(
838 g,
839 dim_2_label::neg_one_neg_one,
840 dim_2_label::one_one,
841 test.report("(-1, -1) * -1"),
842 [](coords_t v) -> coords_t { return v * ring_t{-1}; }
843 );
844
845 add_transition<coords_t>(
846 g,
847 dim_2_label::neg_one_neg_one,
848 dim_2_label::one_one,
849 test.report("(-1, -1) * span{-1, -1}"),
850 [](coords_t v) -> coords_t { return v * std::array<ring_t, 2>{-1, -1}; }
851 );
852
853 if constexpr(maths::vector_space<module_t>)
854 {
855 add_transition<coords_t>(
856 g,
857 dim_2_label::neg_one_neg_one,
858 dim_2_label::one_one,
859 test.report("(-1, -1) /= -1"),
860 [](coords_t v) -> coords_t { return v /= ring_t{-1}; }
861 );
862
863 add_transition<coords_t>(
864 g,
865 dim_2_label::neg_one_neg_one,
866 dim_2_label::one_one,
867 test.report("(-1, -1) /= span{-1, -1}"),
868 [](coords_t v) -> coords_t { return v /= std::array<ring_t, 2>{-1, -1}; }
869 );
870
871 add_transition<coords_t>(
872 g,
873 dim_2_label::neg_one_neg_one,
874 dim_2_label::one_one,
875 test.report("(-1, -1) / -1"),
876 [](coords_t v) -> coords_t { return v / ring_t{-1}; }
877 );
878
879 add_transition<coords_t>(
880 g,
881 dim_2_label::neg_one_neg_one,
882 dim_2_label::one_one,
883 test.report("(-1, -1) / span{-1, -1}"),
884 [](coords_t v) -> coords_t { return v / std::array<ring_t, 2>{-1, -1}; }
885 );
886 }
887
888 // (-1, -1) --> (1, 0)
889 add_transition<coords_t>(
890 g,
891 dim_2_label::neg_one_neg_one,
892 dim_2_label::one_zero,
893 test.report("(-1, -1) *= span{-1, 0}"),
894 [](coords_t v) -> coords_t { return v *= std::array<ring_t, 2>{-1, 0}; }
895 );
896
897 // (-1, -1) --> (1, 2)
898 if constexpr(maths::vector_space<module_t>)
899 {
900 add_transition<coords_t>(
901 g,
902 dim_2_label::neg_one_neg_one,
903 dim_2_label::one_two,
904 test.report("(-1, -1) /= span{-1, -0.5}"),
905 [](coords_t v) -> coords_t { return v /= std::array<ring_t, 2>{-1, -0.5}; }
906 );
907
908 add_transition<coords_t>(
909 g,
910 dim_2_label::neg_one_neg_one,
911 dim_2_label::one_two,
912 test.report("(-1, -1) / span{-1, -0.5}"),
913 [](coords_t v) -> coords_t { return v / std::array<ring_t, 2>{-1, -0.5}; }
914 );
915 }
916 }
917
918 static void add_dim_2_free_mutations(maths::network auto& g, regular_test& test)
919 {
920 // (-1, -1) --> (-1, 0)
921
922 add_transition<coords_t>(
923 g,
924 dim_2_label::neg_one_neg_one,
925 dim_2_label::neg_one_zero,
926 test.report("(-1, -1)[1] *= 0"),
927 [](coords_t v) -> coords_t { v[1] *= ring_t{}; return v; }
928 );
929
930 add_transition<coords_t>(
931 g,
932 dim_2_label::neg_one_neg_one,
933 dim_2_label::neg_one_zero,
934 test.report("(-1, -1).begin()[1] *= 0"),
935 [](coords_t v) -> coords_t { v.begin()[1] *= ring_t{}; return v; }
936 );
937
938 add_transition<coords_t>(
939 g,
940 dim_2_label::neg_one_neg_one,
941 dim_2_label::neg_one_zero,
942 test.report("(-1, -1).rbegin()[0] *= 0"),
943 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{}; return v; }
944 );
945
946 // (0, 1) --> (1, 1)
947 add_transition<coords_t>(
948 g,
949 dim_2_label::zero_one,
950 dim_2_label::one_one,
951 test.report("(0, 1)[0] += 1"),
952 [](coords_t v) -> coords_t { v[0] += ring_t{1}; return v; }
953 );
954
955 add_transition<coords_t>(
956 g,
957 dim_2_label::zero_one,
958 dim_2_label::one_one,
959 test.report("(0, 1).begin[0] += 1"),
960 [](coords_t v) -> coords_t { v.begin()[0] += ring_t{1}; return v; }
961 );
962
963 add_transition<coords_t>(
964 g,
965 dim_2_label::zero_one,
966 dim_2_label::one_one,
967 test.report("(0, 1).rbegin[1] += 1"),
968 [](coords_t v) -> coords_t { v.rbegin()[1] += ring_t{1}; return v; }
969 );
970 }
971
972 static void add_dim_2_no_unit_construction(maths::network auto& g, regular_test& test)
973 {
974 // (-1, -1) --> (-1, -1)
975
976 add_transition<coords_t>(
977 g,
978 dim_2_label::neg_one_neg_one,
979 dim_2_label::neg_one_neg_one,
980 test.report("(-1, -1) without units"),
981 [](coords_t v) -> coords_t { return {v[0], v[1]}; }
982 );
983
984 add_transition<coords_t>(
985 g,
986 dim_2_label::neg_one_neg_one,
987 dim_2_label::neg_one_neg_one,
988 test.report("(-1, -1) without units"),
989 [](coords_t v) -> coords_t { return coords_t{v.values()}; }
990 );
991 }
992 };
993}
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:379
inverted_ordering
Definition: GeometryTestingUtilities.hpp:183
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.
Type to indicate a distinguished origin, relevant for free modules.
Definition: Spaces.hpp:1239
Definition: DynamicGraph.hpp:303
class template from which all concrete tests should derive.
Definition: FreeTestCore.hpp:144
Definition: GeometryTestingUtilities.hpp:229
Definition: TestLogger.hpp:183
A concept to determine if a basis is appropriate for a particular free module.
Definition: Spaces.hpp:692
A basis must identify the free module to which it corresponds.
Definition: Spaces.hpp:644
concept for convex spaces
Definition: Spaces.hpp:518
Definition: GraphTraits.hpp:18
Definition: Spaces.hpp:1162
Definition: GeometryTestingUtilities.hpp:117
Definition: GeometryTestingUtilities.hpp:135
Definition: FreeCheckers.hpp:82
Definition: FreeCheckers.hpp:87
Definition: GeometryTestingUtilities.hpp:62
Definition: GeometryTestingUtilities.hpp:109
Definition: GeometryTestingUtilities.hpp:125
Definition: GeometryTestingUtilities.hpp:83
Definition: StateTransitionUtilities.hpp:77
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78