|
1 | 1 | #pragma once |
2 | 2 |
|
| 3 | +#include <concepts> |
| 4 | +#include <type_traits> |
| 5 | + |
| 6 | +#include "base/traits.hpp" |
| 7 | + |
3 | 8 | namespace principia { |
4 | 9 | namespace geometry { |
5 | 10 | namespace _concepts { |
6 | 11 | namespace internal { |
7 | 12 |
|
| 13 | +using namespace base::_traits; |
| 14 | + |
| 15 | +// TODO(egg): additive_group should subsume affine, but we use it there. |
| 16 | +// We use `convertible_to` here because we want this concept to work with |
| 17 | +// Boost multiprecision types which heavily use implicit conversions. |
| 18 | +template<typename G> |
| 19 | +concept additive_group = requires(G x, G y, int n) { |
| 20 | + G{}; |
| 21 | + { +x } -> std::convertible_to<G>; |
| 22 | + { -x } -> std::convertible_to<G>; |
| 23 | + { x + y } -> std::convertible_to<G>; |
| 24 | + { x - y } -> std::convertible_to<G>; |
| 25 | + { x += y } -> std::convertible_to<G&>; |
| 26 | + { x -= y } -> std::convertible_to<G&>; |
| 27 | + // An abelian group is a ℤ-module; we require the corresponding operations. |
| 28 | + { n * x } -> std::convertible_to<G>; |
| 29 | + { x * n } -> std::convertible_to<G>; |
| 30 | + { x *= n } -> std::convertible_to<G&>; |
| 31 | +}; |
| 32 | + |
| 33 | +// A set acted upon simply transitively by an additive group. |
| 34 | +template<typename A> |
| 35 | +concept affine = requires(A x, A y) { |
| 36 | + { x - y } -> additive_group; |
| 37 | + { y + (x - y) } -> std::same_as<A>; |
| 38 | + { y += (x - y) } -> std::same_as<A&>; |
| 39 | + { y - (x - y) } -> std::same_as<A>; |
| 40 | + { y -= (x - y) } -> std::same_as<A&>; |
| 41 | +}; |
| 42 | + |
| 43 | +// A graded ring restricted to its homogeneous elements; multiplication can |
| 44 | +// alter the type, and addition is only defined between homogeneous elements. |
| 45 | +template<typename A> |
| 46 | +concept homogeneous_ring = |
| 47 | + additive_group<A> && requires(A x, A y, A z) { |
| 48 | + // Really, multiplication should return a homogeneous_ring. |
| 49 | + { x * y } -> additive_group; |
| 50 | + { (x * y) * z } -> additive_group; |
| 51 | + { (x * y) * z } -> std::same_as<decltype(x * (y * z))>; |
| 52 | +}; |
| 53 | + |
| 54 | +template<typename A> |
| 55 | +concept ring = homogeneous_ring<A> && requires(A x, A y) { |
| 56 | + { x * y } -> std::same_as<A>; |
| 57 | + { x *= y } -> std::same_as<A&>; |
| 58 | +}; |
| 59 | + |
| 60 | +// TODO(egg): field should subsume homogeneous_field, but we use it in |
| 61 | +// homogeneous_field. |
| 62 | + |
| 63 | +template<typename K> |
| 64 | +concept field = ring<K> && !std::integral<K> && requires(K x, K y, K z) { |
| 65 | + { 1 } -> std::convertible_to<K>; |
| 66 | + { 1 / y } -> std::same_as<K>; |
| 67 | + { x / y } -> std::same_as<K>; |
| 68 | + { x /= y } -> std::same_as<K&>; |
| 69 | +}; |
| 70 | + |
| 71 | +// TODO(egg): vector_space should subsume homogeneous_vector_space, but we use |
| 72 | +// it in homogeneous_vector_space. |
| 73 | + |
| 74 | +template<typename V, typename K> |
| 75 | +concept vector_space = field<K> && requires(K λ, V v) { |
| 76 | + { λ * v } -> std::same_as<V>; |
| 77 | + { v * λ } -> std::same_as<V>; |
| 78 | + { v / λ } -> std::same_as<V>; |
| 79 | + { v *= λ } -> std::same_as<V&>; |
| 80 | + { v /= λ } -> std::same_as<V&>; |
| 81 | + }; |
| 82 | + |
| 83 | +// A graded field restricted to its homogeneous elements; multiplication and |
| 84 | +// division can alter the type, and addition is only defined between homogeneous |
| 85 | +// elements. |
| 86 | +template<typename K> |
| 87 | +concept homogeneous_field = homogeneous_ring<K> && requires(K x, K y, K z) { |
| 88 | + { x / y } -> field; |
| 89 | + requires vector_space<K, decltype(x / y)>; |
| 90 | + { (1 / x) } -> homogeneous_ring; |
| 91 | + { 1 / (x * y) } -> std::same_as<decltype((1 / x) * (1 / y))>; |
| 92 | + { (x * y) / z } -> std::same_as<K>; |
| 93 | + { (x / y) * z } -> std::same_as<K>; |
| 94 | +}; |
| 95 | + |
| 96 | +template<typename V, typename K> |
| 97 | +concept homogeneous_vector_space = |
| 98 | + additive_group<V> && homogeneous_field<K> && requires(K λ, K μ, V v) { |
| 99 | + // Really, these operations should return a homogeneous_vector_space. |
| 100 | + requires vector_space<V, decltype(λ / μ)>; |
| 101 | + { λ * v } -> additive_group; |
| 102 | + { v * λ } -> std::same_as<decltype(λ * v)>; |
| 103 | + { v / λ } -> additive_group; |
| 104 | + { λ * v / λ } -> std::same_as<V>; |
| 105 | + { (λ * μ) * v } -> additive_group; |
| 106 | + { λ * (μ * v) } -> std::same_as<decltype((λ * μ) * v)>; |
| 107 | + }; |
| 108 | + |
| 109 | +template<typename V> |
| 110 | +concept real_vector_space = vector_space<V, double>; |
| 111 | + |
| 112 | +template<typename A, typename K> |
| 113 | +concept affine_space = affine<A> && requires(A x, A y) { |
| 114 | + { x - y } -> vector_space<K>; |
| 115 | +}; |
| 116 | + |
| 117 | +template<typename V> |
| 118 | +concept real_affine_space = affine_space<V, double>; |
| 119 | + |
8 | 120 | template<typename T1, typename T2> |
9 | 121 | concept hilbert = requires(T1 const& t1, T2 const& t2) { |
10 | 122 | InnerProduct(t1, t2); |
11 | 123 | }; |
12 | 124 |
|
13 | 125 | } // namespace internal |
14 | 126 |
|
| 127 | +using internal::additive_group; |
| 128 | +using internal::affine; |
| 129 | +using internal::affine_space; |
15 | 130 | using internal::hilbert; |
| 131 | +using internal::field; |
| 132 | +using internal::homogeneous_field; |
| 133 | +using internal::homogeneous_ring; |
| 134 | +using internal::homogeneous_vector_space; |
| 135 | +using internal::real_affine_space; |
| 136 | +using internal::real_vector_space; |
| 137 | +using internal::ring; |
| 138 | +using internal::vector_space; |
16 | 139 |
|
17 | 140 | } // namespace _concepts |
18 | 141 | } // namespace geometry |
|
0 commit comments