19namespace sequoia::testing
21 template<
class T,
class U>
22 constexpr bool can_multiply{
23 requires(
const T& t,
const U& u) { t * u; }
26 template<
class T,
class U>
27 constexpr bool can_divide{
28 requires(
const T& t,
const U& u) { t / u; }
31 template<
class T,
class U>
32 constexpr bool can_add{
33 requires(
const T& t,
const U& u) { t + u; }
36 template<
class T,
class U>
37 constexpr bool can_subtract{
38 requires(
const T& t,
const U& u) { t - u; }
42 constexpr bool has_unary_plus{
43 requires(
const T& t) { { +t } -> std::convertible_to<T>; }
47 constexpr bool has_unary_minus{
48 requires(
const T& t) { { -t } -> std::convertible_to<T>; }
54 template<std::
floating_po
int T>
61 using is_complex_t =
typename is_complex<T>::type;
64 inline constexpr bool is_orthonormal_basis_v{
66 typename B::orthonormal;
67 requires std::same_as<typename B::orthonormal, std::true_type>;
71 template<
class Set, maths::weak_field Field, std::
size_t D>
75 using field_type = Field;
76 using is_vector_space = std::true_type;
77 constexpr static std::size_t dimension{D};
79 template<maths::basis Basis>
80 requires std::floating_point<field_type>&& is_orthonormal_basis_v<Basis>
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); });
87 template<maths::basis Basis>
88 requires is_complex_v<field_type>&& is_orthonormal_basis_v<Basis>
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); });
96 template<
class Set, maths::weak_field Field, std::
size_t D>
101 using is_affine_space = std::true_type;
104 template<
class Set, maths::weak_field Field, std::
size_t D>
108 using orthonormal = std::true_type;
111 template<
class Set, maths::weak_commutative_ring Ring, std::
size_t D>
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};
120 template<
class Set, maths::weak_commutative_ring Ring, std::
size_t D>
126 template<maths::convex_space ConvexSpace, maths::basis Basis,
class Origin,
class Val
idator>
131 using commutative_ring_type =
typename coord_type::commutative_ring_type;
132 constexpr static std::size_t D{coord_type::dimension};
134 template<test_mode Mode>
137 check(equality,
"Wrapped values", logger, actual.values(), prediction.values());
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));
145 for(
auto i : std::views::iota(0uz, D))
147 check(equality, std::format(
"Value at index {}", i), logger, actual[i], prediction[i]);
151 template<test_mode Mode>
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);
160 for(
auto i : std::views::iota(0uz, D))
162 check(equality,
"operator[]", logger, actual[i], prediction[i]);
171 template<
class Label>
172 requires std::convertible_to<Label, std::size_t>
174 std::weak_ordering to_ordering(Label From, Label To, inverted_ordering invert)
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;
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)
186 g.join(From, To, std::string{message}, f, ordering);
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)
193 g.join(From, To, std::string{message}, f);
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={})
200 using ring_t = Coords::commutative_ring_type;
201 constexpr static auto dimension{Coords::dimension};
203 if constexpr((dimension == 1) && std::totally_ordered<ring_t>)
205 add_transition(g, From, To, message, f, to_ordering(From, To, invert));
209 add_transition(g, From, To, message, f);
214 inline constexpr bool has_units_type{
215 requires {
typename T::units_type; }
219 template<
class Coordinates>
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 };
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>};
238 , m_Graph{make_graph(m_Test)}
248 if constexpr(has_units_type<coords_t>)
249 return do_make_graph(test,
typename coords_t::units_type{});
251 return do_make_graph(test);
254 template<
class... Units>
256 static graph_type do_make_graph(
regular_test& test, Units... units)
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...);
263 auto make_checker()
const
265 if constexpr(orderable)
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);
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);
284 template<
class... Units>
285 static graph_type make_dim_1_transition_graph(
regular_test& test, Units... units)
291 {coords_t{ring_t(2), units...}, coords_t{ring_t(1), units...}, coords_t{}}
294 add_dim_1_common_transitions(g, test, units...);
296 if constexpr(!maths::defines_half_line_v<typename Coordinates::validator_type>)
298 add_dim_1_negative_transitions(g, test, units...);
300 else if constexpr(std::is_signed_v<ring_t>)
302 add_dim_1_attempted_negative_transitions(g, test, units...);
305 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
307 add_dim_1_distinguished_origin_transitions(g, test);
310 if constexpr(Coordinates::has_freely_mutable_components)
312 add_dim_1_free_mutations(g, test);
318 template<
class... Units>
319 static graph_type make_dim_2_transition_graph(
regular_test& test, Units... units)
321 using edge_t = transition_checker<coords_t>::edge;
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...}; }}
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...}; }}
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...}; }}
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...}
358 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
360 add_dim_2_distinguished_origin_transitions(g, test, units...);
363 if constexpr(Coordinates::has_freely_mutable_components)
365 add_dim_2_free_mutations(g, test);
371 template<
class... Units>
375 add_transition<coords_t>(
379 test.report(
"(0) + (1)"),
380 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
383 add_transition<coords_t>(
387 test.report(
"(0) += (1)"),
388 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
393 add_transition<coords_t>(
397 test.report(
"(1) - (1)"),
398 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
401 add_transition<coords_t>(
405 test.report(
"(1) -= (1)"),
406 [&](coords_t p) -> coords_t { return p -= disp_t{ring_t(1), units...}; }
409 add_transition<coords_t>(
414 [](coords_t p) -> coords_t { return +p;}
417 add_transition<coords_t>(
421 test.report(
"(1) + (1)"),
422 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
425 add_transition<coords_t>(
429 test.report(
"(1) += (1)"),
430 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
435 add_transition<coords_t>(
439 test.report(
"(2) - (1)"),
440 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
444 template<
class... Units>
445 static void add_dim_1_negative_transitions(maths::network
auto& g, regular_test& test, Units... units)
447 g.add_node(ring_t(-1), units...);
450 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
452 add_transition<coords_t>(
455 dim_1_label::neg_one,
457 [](coords_t p) -> coords_t { return -p; },
458 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
462 add_transition<coords_t>(
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
473 if constexpr(coords_t::has_distinguished_origin && !std::is_unsigned_v<ring_t>)
475 add_transition<coords_t>(
477 dim_1_label::neg_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
485 add_transition<coords_t>(
487 dim_1_label::neg_one,
488 dim_1_label::neg_one,
489 test.report(
"+ (-1)"),
490 [](coords_t p) -> coords_t { return +p; }
493 if constexpr(Coordinates::has_freely_mutable_components)
495 add_transition<coords_t>(
497 dim_1_label::neg_one,
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
504 add_transition<coords_t>(
506 dim_1_label::neg_one,
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
515 template<
class... Units>
516 static void add_dim_1_attempted_negative_transitions(maths::network
auto& g, regular_test& test, Units... units)
518 add_transition<coords_t>(
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...};});
529 add_transition<coords_t>(
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...}); });
540 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
542 add_transition<coords_t>(
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}; });
553 add_transition<coords_t>(
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; });
564 add_transition<coords_t>(
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}; });
575 add_transition<coords_t>(
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}; });
588 static void add_dim_1_distinguished_origin_transitions(maths::network
auto& g, regular_test& test)
591 add_transition<coords_t>(
595 test.report(
"(1) * ring_t{}"),
596 [](coords_t v) -> coords_t { return v * ring_t{}; }
599 add_transition<coords_t>(
603 test.report(
"ring_t{} * (1)"),
604 [](coords_t v) -> coords_t { return ring_t{} *v; }
607 add_transition<coords_t>(
611 test.report(
"(1) *= ring_t{}"),
612 [](coords_t v) -> coords_t { return v *= ring_t{}; }
617 add_transition<coords_t>(
621 test.report(
"(1) * ring_t{2}"),
622 [](coords_t v) -> coords_t { return v * ring_t{2}; }
625 add_transition<coords_t>(
629 test.report(
"ring_t{2} * (1)"),
630 [](coords_t v) -> coords_t { return ring_t{2} *v; }
633 add_transition<coords_t>(
637 test.report(
"(1) *= ring_t{2}"),
638 [](coords_t v) -> coords_t { return v *= ring_t{2}; }
643 if constexpr(maths::vector_space<module_t>)
645 add_transition<coords_t>(
649 test.report(
"(2) / ring_t{2}"),
650 [](coords_t v) -> coords_t { return v / ring_t{2}; }
653 add_transition<coords_t>(
657 test.report(
"(2) /= ring_t{2}"),
658 [](coords_t v) -> coords_t { return v /= ring_t{2}; }
663 static void add_dim_1_free_mutations(maths::network
auto& g, regular_test& test)
666 add_transition<coords_t>(
670 test.report(
"(1)[0] * ring_t{}"),
671 [](coords_t v) -> coords_t { v[0] *= ring_t{};
return v; }
674 add_transition<coords_t>(
678 test.report(
"(1).begin[0] * ring_t{}"),
679 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{};
return v; }
682 add_transition<coords_t>(
686 test.report(
"(1).rbegin[0] * ring_t{}"),
687 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{};
return v; }
692 add_transition<coords_t>(
696 test.report(
"(1)[0] * ring_t{2}"),
697 [](coords_t v) -> coords_t { v[0] *= ring_t{2};
return v; }
700 add_transition<coords_t>(
704 test.report(
"(1).begin[0] * ring_t{2}"),
705 [](coords_t v) -> coords_t { v.begin()[0] *= ring_t{2};
return v; }
708 add_transition<coords_t>(
712 test.report(
"(1).rbegin[0] * ring_t{2}"),
713 [](coords_t v) -> coords_t { v.rbegin()[0] *= ring_t{2};
return v; }
717 static void add_dim_2_distinguished_origin_transitions(maths::network
auto& g, regular_test& test)
721 add_transition<coords_t>(
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; }
729 add_transition<coords_t>(
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}; }
737 add_transition<coords_t>(
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}; }
745 if constexpr(maths::vector_space<module_t>)
747 add_transition<coords_t>(
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}; }
755 add_transition<coords_t>(
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}; }
765 static void add_dim_2_free_mutations(maths::network
auto& g, regular_test& test)
769 add_transition<coords_t>(
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; }
777 add_transition<coords_t>(
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; }
785 add_transition<coords_t>(
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; }
794 add_transition<coords_t>(
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; }
802 add_transition<coords_t>(
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; }
810 add_transition<coords_t>(
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; }
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