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 struct is_complex : std::false_type {};
43
44 template<std::floating_point T>
45 struct is_complex<std::complex<T>> : std::true_type {};
46
47 template<class T>
48 inline constexpr bool is_complex_v{is_complex<T>::value};
49
50 template<class T>
51 using is_complex_t = typename is_complex<T>::type;
52
53 template<class B>
54 inline constexpr bool is_orthonormal_basis_v{
55 requires {
56 typename B::orthonormal;
57 requires std::same_as<typename B::orthonormal, std::true_type>;
58 }
59 };
60
61 template<class Set, maths::weak_field Field, std::size_t D>
63 {
64 using set_type = Set;
65 using field_type = Field;
66 using is_vector_space = std::true_type;
67 constexpr static std::size_t dimension{D};
68
69 template<maths::basis Basis>
70 requires std::floating_point<field_type>&& is_orthonormal_basis_v<Basis>
71 [[nodiscard]]
72 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
73 {
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); });
75 }
76
77 template<maths::basis Basis>
78 requires is_complex_v<field_type>&& is_orthonormal_basis_v<Basis>
79 [[nodiscard]]
80 friend constexpr field_type inner_product(const maths::vector_coordinates<my_vec_space, Basis>& lhs, const maths::vector_coordinates<my_vec_space, Basis>& rhs)
81 {
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); });
83 }
84 };
85
86 template<class Set, maths::weak_field Field, std::size_t D>
88 {
89 using set_type = Set;
91 using is_affine_space = std::true_type;
92 };
93
94 template<class Set, maths::weak_field Field, std::size_t D>
96 {
98 using orthonormal = std::true_type;
99 };
100
101 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
103 {
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};
108 };
109
110 template<class Set, maths::weak_commutative_ring Ring, std::size_t D>
112 {
114 };
115
116 template<maths::convex_space ConvexSpace, maths::basis Basis, class Origin, class Validator>
118 struct value_tester<maths::coordinates<ConvexSpace, Basis, Origin, Validator>>
119 {
121 using commutative_ring_type = typename coord_type::commutative_ring_type;
122 constexpr static std::size_t D{coord_type::dimension};
123
124 template<test_mode Mode>
125 static void test(equality_check_t, test_logger<Mode>& logger, const coord_type& actual, const coord_type& prediction)
126 {
127 check(equality, "Wrapped values", logger, actual.values(), prediction.values());
128 if constexpr(D == 1)
129 {
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));
133 }
134
135 for(auto i : std::views::iota(0uz, D))
136 {
137 check(equality, std::format("Value at index {}", i), logger, actual[i], prediction[i]);
138 }
139 }
140
141 template<test_mode Mode>
142 static void test(equivalence_check_t, test_logger<Mode>& logger, const coord_type& actual, const std::array<commutative_ring_type, D>& prediction)
143 {
144 check(equality, "Wrapped values", logger, actual.values(), std::span<const commutative_ring_type, D>{prediction});
145 }
146 };
147
150 enum class inverted_ordering : bool {no, yes};
151
152 template<class Label>
153 requires std::convertible_to<Label, std::size_t>
154 [[nodiscard]]
155 std::weak_ordering to_ordering(Label From, Label To, inverted_ordering invert)
156 {
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;
161 }
162
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)
166 {
167 g.join(From, To, std::string{message}, f, ordering);
168 }
169
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)
173 {
174 g.join(From, To, std::string{message}, f);
175 }
176
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={})
180 {
181 using ring_t = Coords::commutative_ring_type;
182 constexpr static auto dimension{Coords::dimension};
183
184 if constexpr((dimension == 1) && std::totally_ordered<ring_t>)
185 {
186 add_transition(g, From, To, message, f, to_ordering(From, To, invert));
187 }
188 else
189 {
190 add_transition(g, From, To, message, f);
191 }
192 }
193
194 template<class T>
195 inline constexpr bool has_units_type{
196 requires { typename T::units_type; }
197 };
198
199
200 template<class Coordinates>
202 {
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 };
205
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>};
213
214 regular_test& m_Test;
215 graph_type m_Graph;
216 public:
218 : m_Test{t}
219 , m_Graph{make_graph(m_Test)}
220 {}
221
222 void execute()
223 {
224 transition_checker<coords_t>::check("", m_Graph, make_checker());
225 }
226 private:
227 static graph_type make_graph(regular_test& test)
228 {
229 if constexpr(has_units_type<coords_t>)
230 return do_make_graph(test, typename coords_t::units_type{});
231 else
232 return do_make_graph(test);
233 }
234
235 template<class... Units>
236 [[nodiscard]]
237 static graph_type do_make_graph(regular_test& test, Units... units)
238 {
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...);
241 }
242
243 [[nodiscard]]
244 auto make_checker() const
245 {
246 if constexpr(orderable)
247 {
248 return
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);
253 };
254 }
255 else
256 {
257 return
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);
261 };
262 }
263 }
264
265 template<class... Units>
266 static graph_type make_dim_1_transition_graph(regular_test& test, Units... units)
267 {
268 graph_type g{
269 {
270 {}, {}, {}
271 },
272 {coords_t{ring_t(2), units...}, coords_t{ring_t(1), units...}, coords_t{}}
273 };
274
275 add_dim_1_common_transitions(g, test, units...);
276
277 if constexpr(!maths::defines_half_line_v<typename Coordinates::validator_type>)
278 {
279 add_dim_1_negative_transitions(g, test, units...);
280 }
281 else if constexpr(std::is_signed_v<ring_t>)
282 {
283 add_dim_1_attempted_negative_transitions(g, test, units...);
284 }
285
286 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
287 {
288 add_dim_1_distinguished_origin_transitions(g, test, units...);
289 }
290
291 return g;
292 }
293
294 template<class... Units>
295 static graph_type make_dim_2_transition_graph(regular_test& test, Units... units)
296 {
297 using edge_t = transition_checker<coords_t>::edge;
298 graph_type g{
299 {
300 {
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...}; }}
307 }, // neg_one_neg_one
308 {
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...}; }}
311 }, // neg_one_zero
312 {
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...}; }}
315 }, // zero_neg_one
316 {
317 }, // zero_zero
318 {
319 }, // zero_one
320 {
321 }, // one_zero
322 {
323 }, // one_one
324 },
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...}
332 }
333 };
334
335 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
336 {
337 add_dim_2_distinguished_origin_transitions(g, test, units...);
338 }
339
340 return g;
341 }
342
343 template<class... Units>
344 static void add_dim_1_common_transitions(maths::network auto& g, regular_test& test, Units... units)
345 {
346 // Joins from zero
347 add_transition<coords_t>(
348 g,
349 dim_1_label::zero,
350 dim_1_label::one,
351 test.report("(0) + (1)"),
352 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
353 );
354
355 add_transition<coords_t>(
356 g,
357 dim_1_label::zero,
358 dim_1_label::one,
359 test.report("(0) += (1)"),
360 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
361 );
362
363 // Joins from one
364
365 add_transition<coords_t>(
366 g,
367 dim_1_label::one,
368 dim_1_label::zero,
369 test.report("(1) - (1)"),
370 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
371 );
372
373 add_transition<coords_t>(
374 g,
375 dim_1_label::one,
376 dim_1_label::zero,
377 test.report("(1) -= (1)"),
378 [&](coords_t p) -> coords_t { return p -= disp_t{ring_t(1), units...}; }
379 );
380
381 add_transition<coords_t>(
382 g,
383 dim_1_label::one,
384 dim_1_label::one,
385 test.report("+(1)"),
386 [](coords_t p) -> coords_t { return +p;}
387 );
388
389 add_transition<coords_t>(
390 g,
391 dim_1_label::one,
392 dim_1_label::two,
393 test.report("(1) + (1)"),
394 [&](coords_t p) -> coords_t { return p + disp_t{ring_t(1), units...}; }
395 );
396
397 add_transition<coords_t>(
398 g,
399 dim_1_label::one,
400 dim_1_label::two,
401 test.report("(1) += (1)"),
402 [&](coords_t p) -> coords_t { return p += disp_t{ring_t(1), units...}; }
403 );
404
405 // Joins from two
406
407 add_transition<coords_t>(
408 g,
409 dim_1_label::two,
410 dim_1_label::one,
411 test.report("(2) - (1)"),
412 [&](coords_t p) -> coords_t { return p - disp_t{ring_t(1), units...}; }
413 );
414 }
415
416 template<class... Units>
417 static void add_dim_1_negative_transitions(maths::network auto& g, regular_test& test, Units... units)
418 {
419 g.add_node(ring_t(-1), units...);
420 // Joins to neg_one
421 add_transition<coords_t>(
422 g,
423 dim_1_label::one,
424 dim_1_label::neg_one,
425 test.report("-(1)"),
426 [](coords_t p) -> coords_t { return -p; },
427 std::is_unsigned_v<ring_t> ? inverted_ordering::yes : inverted_ordering::no
428 );
429
430 add_transition<coords_t>(
431 g,
432 dim_1_label::one,
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
437 );
438
439 // Joins from neg_one
440 add_transition<coords_t>(
441 g,
442 dim_1_label::neg_one,
443 dim_1_label::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
447 );
448
449 add_transition<coords_t>(
450 g,
451 dim_1_label::neg_one,
452 dim_1_label::neg_one,
453 test.report("+ (-1)"),
454 [](coords_t p) -> coords_t { return +p; }
455 );
456
457 if constexpr(std::is_same_v<typename Coordinates::validator_type, std::identity>)
458 {
459 add_transition<coords_t>(
460 g,
461 dim_1_label::neg_one,
462 dim_1_label::zero,
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
466 );
467
468 add_transition<coords_t>(
469 g,
470 dim_1_label::neg_one,
471 dim_1_label::zero,
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
475 );
476 }
477 }
478
479 template<class... Units>
480 static void add_dim_1_attempted_negative_transitions(maths::network auto& g, regular_test& test, Units... units)
481 {
482 add_transition<coords_t>(
483 g,
484 dim_1_label::one,
485 dim_1_label::one,
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...};});
489 return p;
490 }
491 );
492
493 add_transition<coords_t>(
494 g,
495 dim_1_label::one,
496 dim_1_label::one,
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...}); });
500 return p;
501 }
502 );
503
504 if constexpr(std::is_same_v<typename Coordinates::origin_type, maths::distinguished_origin>)
505 {
506 add_transition<coords_t>(
507 g,
508 dim_1_label::one,
509 dim_1_label::one,
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}; });
513 return v;
514 }
515 );
516
517 add_transition<coords_t>(
518 g,
519 dim_1_label::one,
520 dim_1_label::one,
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; });
524 return v;
525 }
526 );
527
528 add_transition<coords_t>(
529 g,
530 dim_1_label::one,
531 dim_1_label::one,
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}; });
535 return v;
536 }
537 );
538
539 add_transition<coords_t>(
540 g,
541 dim_1_label::one,
542 dim_1_label::one,
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}; });
546 return v;
547 }
548 );
549 }
550 }
551
552 template<class... Units>
553 static void add_dim_1_distinguished_origin_transitions(maths::network auto& g, regular_test& test, Units...)
554 {
555 // TO DO: add in negative transitions
556 add_transition<coords_t>(
557 g,
558 dim_1_label::one,
559 dim_1_label::zero,
560 test.report("(1) * ring_t{}"),
561 [](coords_t v) -> coords_t { return v * ring_t{}; }
562 );
563
564 add_transition<coords_t>(
565 g,
566 dim_1_label::one,
567 dim_1_label::zero,
568 test.report("ring_t{} * (1)"),
569 [](coords_t v) -> coords_t { return ring_t{} *v; }
570 );
571
572 add_transition<coords_t>(
573 g,
574 dim_1_label::one,
575 dim_1_label::zero,
576 test.report("(1) *= ring_t{}"),
577 [](coords_t v) -> coords_t { return v *= ring_t{}; }
578 );
579
580 // (1) --> (2)
581
582 add_transition<coords_t>(
583 g,
584 dim_1_label::one,
585 dim_1_label::two,
586 test.report("(1) * ring_t{2}"),
587 [](coords_t v) -> coords_t { return v * ring_t{2}; }
588 );
589
590 add_transition<coords_t>(
591 g,
592 dim_1_label::one,
593 dim_1_label::two,
594 test.report("ring_t{2} * (1)"),
595 [](coords_t v) -> coords_t { return ring_t{2} *v; }
596 );
597
598 add_transition<coords_t>(
599 g,
600 dim_1_label::one,
601 dim_1_label::two,
602 test.report("(1) *= ring_t{2}"),
603 [](coords_t v) -> coords_t { return v *= ring_t{2}; }
604 );
605
606 // (2) --> (1)
607
608 if constexpr(maths::vector_space<module_t>)
609 {
610 add_transition<coords_t>(
611 g,
612 dim_1_label::two,
613 dim_1_label::one,
614 test.report("(2) / ring_t{2}"),
615 [](coords_t v) -> coords_t { return v / ring_t{2}; }
616 );
617
618 add_transition<coords_t>(
619 g,
620 dim_1_label::two,
621 dim_1_label::one,
622 test.report("(2) /= ring_t{2}"),
623 [](coords_t v) -> coords_t { return v /= ring_t{2}; }
624 );
625 }
626 }
627
628 template<class... Units>
629 static void add_dim_2_distinguished_origin_transitions(maths::network auto& g, regular_test& test, Units...)
630 {
631 // (-1, -1) --> (1, 1)
632
633 add_transition<coords_t>(
634 g,
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}; }
639 );
640
641 add_transition<coords_t>(
642 g,
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}; }
647 );
648
649 if constexpr(maths::vector_space<module_t>)
650 {
651 add_transition<coords_t>(
652 g,
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}; }
657 );
658
659 add_transition<coords_t>(
660 g,
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}; }
665 );
666 }
667 }
668 };
669}
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