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; }
44 template<std::
floating_po
int T>
51 using is_complex_t =
typename is_complex<T>::type;
54 inline constexpr bool is_orthonormal_basis_v{
56 typename B::orthonormal;
57 requires std::same_as<typename B::orthonormal, std::true_type>;
61 template<
class Set, maths::weak_field Field, std::
size_t D>
65 using field_type = Field;
66 using is_vector_space = std::true_type;
67 constexpr static std::size_t dimension{D};
69 template<maths::basis Basis>
70 requires std::floating_point<field_type>&& is_orthonormal_basis_v<Basis>
74 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); });
77 template<maths::basis Basis>
78 requires is_complex_v<field_type>&& is_orthonormal_basis_v<Basis>
82 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); });
86 template<
class Set, maths::weak_field Field, std::
size_t D>
91 using is_affine_space = std::true_type;
94 template<
class Set, maths::weak_field Field, std::
size_t D>
98 using orthonormal = std::true_type;
101 template<
class Set, maths::weak_commutative_ring Ring, std::
size_t D>
104 using set_type = Set;
105 using commutative_ring_type = Ring;
106 using is_free_module = std::true_type;
107 constexpr static std::size_t dimension{D};
110 template<
class Set, maths::weak_commutative_ring Ring, std::
size_t D>
116 template<maths::convex_space ConvexSpace, maths::basis Basis,
class Origin,
class Val
idator>
121 using commutative_ring_type =
typename coord_type::commutative_ring_type;
122 constexpr static std::size_t D{coord_type::dimension};
124 template<test_mode Mode>
127 check(equality,
"Wrapped values", logger, actual.values(), prediction.values());
130 check(equality,
"Wrapped value", logger, actual.value(), prediction.value());
131 if constexpr(std::convertible_to<commutative_ring_type, bool>)
132 check(equality,
"Conversion to bool", logger,
static_cast<bool>(actual),
static_cast<bool>(prediction));
135 for(
auto i : std::views::iota(0uz, D))
137 check(equality, std::format(
"Value at index {}", i), logger, actual[i], prediction[i]);
141 template<test_mode Mode>
144 check(equality,
"Wrapped values", logger, actual.values(), std::span<const commutative_ring_type, D>{prediction});
152 template<
class Label>
153 requires std::convertible_to<Label, std::size_t>
155 std::weak_ordering to_ordering(Label From, Label To, inverted_ordering invert)
157 const bool inverted{invert == inverted_ordering::yes};
158 return (((From < To) && !inverted) || ((From > To) && inverted)) ? std::weak_ordering::less
159 : (((From > To) && !inverted) || ((From < To) && inverted)) ? std::weak_ordering::greater
160 : std::weak_ordering::equivalent;
163 template<maths::network Graph,
class Label,
class Fn>
164 requires std::convertible_to<Label, std::size_t>
165 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, std::weak_ordering ordering)
167 g.join(From, To, std::string{message}, f, ordering);
170 template<maths::network Graph,
class Label,
class Fn>
171 requires std::convertible_to<Label, std::size_t>
172 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f)
174 g.join(From, To, std::string{message}, f);
177 template<
class Coords, maths::network Graph,
class Label,
class Fn>
178 requires std::is_invocable_r_v<Coords, Fn, Coords> && std::convertible_to<Label, std::size_t>
179 void add_transition(Graph& g, Label From, Label To, std::string_view message, Fn f, inverted_ordering invert={})
181 using ring_t = Coords::commutative_ring_type;
182 constexpr static auto dimension{Coords::dimension};
184 if constexpr((dimension == 1) && std::totally_ordered<ring_t>)
186 add_transition(g, From, To, message, f, to_ordering(From, To, invert));
190 add_transition(g, From, To, message, f);
195 inline constexpr bool has_units_type{
196 requires {
typename T::units_type; }
200 template<
class Coordinates>
203 enum dim_1_label{ two, one, zero, neg_one };
204 enum dim_2_label{ neg_one_neg_one, neg_one_zero, zero_neg_one, zero_zero, zero_one, one_zero, one_one };
207 using coords_t = Coordinates;
208 using disp_t = coords_t::displacement_coordinates_type;
209 using module_t = coords_t::free_module_type;
210 using ring_t = coords_t::commutative_ring_type;
211 constexpr static std::size_t dimension{Coordinates::dimension};
212 constexpr static bool orderable{(dimension == 1) && std::totally_ordered<ring_t>};
219 , m_Graph{make_graph(m_Test)}
229 if constexpr(has_units_type<coords_t>)
230 return do_make_graph(test,
typename coords_t::units_type{});
232 return do_make_graph(test);
235 template<
class... Units>
237 static graph_type do_make_graph(
regular_test& test, Units... units)
239 if constexpr (dimension == 1)
return make_dim_1_transition_graph(test, units...);
240 else if constexpr(dimension == 2)
return make_dim_2_transition_graph(test, units...);
244 auto make_checker()
const
246 if constexpr(orderable)
249 [&test=m_Test](std::string_view description,
const coords_t& obtained,
const coords_t& prediction,
const coords_t& parent, std::weak_ordering ordering) {
250 test.check(equality, description, obtained, prediction);
251 if(ordering != std::weak_ordering::equivalent)
252 test.check_semantics(description, prediction, parent, ordering);
258 [&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) {
259 test.check(equality, description, obtained, prediction);
260 if(host!= target) test.check_semantics(description, prediction, parent);
265 template<
class... Units>
266 static graph_type make_dim_1_transition_graph(
regular_test& test, Units... units)
272 {coords_t{ring_t(2), units...}, coords_t{ring_t(1), units...}, coords_t{}}
275 add_dim_1_common_transitions(g, test, units...);
277 if constexpr(!maths::defines_half_line_v<typename Coordinates::validator_type>)
279 add_dim_1_negative_transitions(g, test, units...);
281 else if constexpr(std::is_signed_v<ring_t>)
283 add_dim_1_attempted_negative_transitions(g, test, units...);
286 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
288 add_dim_1_distinguished_origin_transitions(g, test, units...);
294 template<
class... Units>
295 static graph_type make_dim_2_transition_graph(
regular_test& test, Units... units)
297 using edge_t = transition_checker<coords_t>::edge;
301 edge_t{dim_2_label::one_one, test.report(
"- (-1, -1)"), [](coords_t v) -> coords_t {
return -v; }},
302 edge_t{dim_2_label::neg_one_neg_one, test.report(
"+ (-1, -1)"), [](coords_t v) -> coords_t {
return +v; }},
303 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...}; }},
304 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...}; }},
305 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...}; }},
306 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...}; }}
309 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...}; }},
310 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...}; }}
313 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...}; }},
314 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...}; }}
325 {coords_t{std::array{ring_t(-1), ring_t(-1)}, units...},
326 coords_t{std::array{ring_t(-1), ring_t{}}, units...},
327 coords_t{std::array{ring_t{}, ring_t(-1)}, units...},
328 coords_t{std::array{ring_t{}, ring_t{}}, units...},
329 coords_t{std::array{ring_t{}, ring_t(1)}, units...},
330 coords_t{std::array{ring_t(1), ring_t{}}, units...},
331 coords_t{std::array{ring_t(1), ring_t(1)}, units...}
335 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
337 add_dim_2_distinguished_origin_transitions(g, test, units...);
343 template<
class... Units>
347 add_transition<coords_t>(
351 test.report(
"(0) + (1)"),
352 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
355 add_transition<coords_t>(
359 test.report(
"(0) += (1)"),
360 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
365 add_transition<coords_t>(
369 test.report(
"(1) - (1)"),
370 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
373 add_transition<coords_t>(
377 test.report(
"(1) -= (1)"),
378 [&](coords_t p) -> coords_t { return p -= disp_t{ring_t(1), units...}; }
381 add_transition<coords_t>(
386 [](coords_t p) -> coords_t { return +p;}
389 add_transition<coords_t>(
393 test.report(
"(1) + (1)"),
394 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
397 add_transition<coords_t>(
401 test.report(
"(1) += (1)"),
402 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
407 add_transition<coords_t>(
411 test.report(
"(2) - (1)"),
412 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
416 template<
class... Units>
417 static void add_dim_1_negative_transitions(maths::network
auto& g, regular_test& test, Units... units)
419 g.add_node(ring_t(-1), units...);
421 add_transition<coords_t>(
424 dim_1_label::neg_one,
426 [](coords_t p) -> coords_t { return -p; },
427 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
430 add_transition<coords_t>(
433 dim_1_label::neg_one,
434 test.report(
"(1) - (2)"),
435 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(2), units...}; },
436 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
440 add_transition<coords_t>(
442 dim_1_label::neg_one,
444 test.report(
"- (-1)"),
445 [](coords_t p) -> coords_t { return -p; },
446 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
449 add_transition<coords_t>(
451 dim_1_label::neg_one,
452 dim_1_label::neg_one,
453 test.report(
"+ (-1)"),
454 [](coords_t p) -> coords_t { return +p; }
457 if constexpr(std::is_same_v<typename Coordinates::validator_type, std::identity>)
459 add_transition<coords_t>(
461 dim_1_label::neg_one,
463 test.report(
"(-1) += 1"),
464 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1;
return p; },
465 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
468 add_transition<coords_t>(
470 dim_1_label::neg_one,
472 test.report(
"(-1) + 1"),
473 [](coords_t p) -> coords_t { auto& v{p.value()}; v += 1;
return p; },
474 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
479 template<
class... Units>
480 static void add_dim_1_attempted_negative_transitions(maths::network
auto& g, regular_test& test, Units... units)
482 add_transition<coords_t>(
486 test.report(
"(1) -= (2)"),
487 [&](coords_t p) -> coords_t {
488 test.check_exception_thrown<std::domain_error>(
"", [&](){ return p -= disp_t{ring_t(2), units...};});
493 add_transition<coords_t>(
497 test.report(
"(1) - (2)"),
498 [&](coords_t p) -> coords_t {
499 test.check_exception_thrown<std::domain_error>(
"", [&](){ return p = (p - disp_t{ring_t(2), units...}); });
504 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
506 add_transition<coords_t>(
510 test.report(
"(1) *= ring_t{-1}"),
511 [&](coords_t v) -> coords_t {
512 test.check_exception_thrown<std::domain_error>(
"", [&v](){ return v *= ring_t{-1}; });
517 add_transition<coords_t>(
521 test.report(
"ring_t{-1} * (1)"),
522 [&test](coords_t v) -> coords_t {
523 test.check_exception_thrown<std::domain_error>(
"", [&v](){ return v = ring_t{-1} * v; });
528 add_transition<coords_t>(
532 test.report(
"(1) /= ring_t{-1}"),
533 [&test](coords_t v) -> coords_t {
534 test.check_exception_thrown<std::domain_error>(
"", [&v](){ return v /= ring_t{-1}; });
539 add_transition<coords_t>(
543 test.report(
"(1) / ring_t{-1}"),
544 [&test](coords_t v) -> coords_t {
545 test.check_exception_thrown<std::domain_error>(
"", [&v](){ return v = v / ring_t{-1}; });
552 template<
class... Units>
553 static void add_dim_1_distinguished_origin_transitions(maths::network
auto& g, regular_test& test, Units...)
556 add_transition<coords_t>(
560 test.report(
"(1) * ring_t{}"),
561 [](coords_t v) -> coords_t { return v * ring_t{}; }
564 add_transition<coords_t>(
568 test.report(
"ring_t{} * (1)"),
569 [](coords_t v) -> coords_t { return ring_t{} *v; }
572 add_transition<coords_t>(
576 test.report(
"(1) *= ring_t{}"),
577 [](coords_t v) -> coords_t { return v *= ring_t{}; }
582 add_transition<coords_t>(
586 test.report(
"(1) * ring_t{2}"),
587 [](coords_t v) -> coords_t { return v * ring_t{2}; }
590 add_transition<coords_t>(
594 test.report(
"ring_t{2} * (1)"),
595 [](coords_t v) -> coords_t { return ring_t{2} *v; }
598 add_transition<coords_t>(
602 test.report(
"(1) *= ring_t{2}"),
603 [](coords_t v) -> coords_t { return v *= ring_t{2}; }
608 if constexpr(maths::vector_space<module_t>)
610 add_transition<coords_t>(
614 test.report(
"(2) / ring_t{2}"),
615 [](coords_t v) -> coords_t { return v / ring_t{2}; }
618 add_transition<coords_t>(
622 test.report(
"(2) /= ring_t{2}"),
623 [](coords_t v) -> coords_t { return v /= ring_t{2}; }
628 template<
class... Units>
629 static void add_dim_2_distinguished_origin_transitions(maths::network
auto& g, regular_test& test, Units...)
633 add_transition<coords_t>(
635 dim_2_label::neg_one_neg_one,
636 dim_2_label::one_one,
637 test.report(
"(-1, -1) *= -1"),
638 [](coords_t v) -> coords_t { return v *= ring_t{-1}; }
641 add_transition<coords_t>(
643 dim_2_label::neg_one_neg_one,
644 dim_2_label::one_one,
645 test.report(
"(-1, -1) * -1"),
646 [](coords_t v) -> coords_t { return v * ring_t{-1}; }
649 if constexpr(maths::vector_space<module_t>)
651 add_transition<coords_t>(
653 dim_2_label::neg_one_neg_one,
654 dim_2_label::one_one,
655 test.report(
"(-1, -1) /= -1"),
656 [](coords_t v) -> coords_t { return v /= ring_t{-1}; }
659 add_transition<coords_t>(
661 dim_2_label::neg_one_neg_one,
662 dim_2_label::one_one,
663 test.report(
"(-1, -1) / -1"),
664 [](coords_t v) -> coords_t { return v / ring_t{-1}; }
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:150
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:1305
Definition: DynamicGraph.hpp:303
class template from which all concrete tests should derive.
Definition: FreeTestCore.hpp:144
Definition: GeometryTestingUtilities.hpp:202
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:96
Definition: GeometryTestingUtilities.hpp:112
Definition: FreeCheckers.hpp:82
Definition: FreeCheckers.hpp:87
Definition: GeometryTestingUtilities.hpp:42
Definition: GeometryTestingUtilities.hpp:88
Definition: GeometryTestingUtilities.hpp:103
Definition: GeometryTestingUtilities.hpp:63
Definition: StateTransitionUtilities.hpp:77
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78