diff --git a/geometry/BUILD.bazel b/geometry/BUILD.bazel index 729589a..c756d5b 100644 --- a/geometry/BUILD.bazel +++ b/geometry/BUILD.bazel @@ -41,6 +41,7 @@ cc_library( name = "geometry", srcs = [ "src/algebra.hpp", + "src/algebra_core.hpp", "src/algebra_type.hpp", "src/common_algebra_type.hpp", "src/detail/blade_list.hpp", @@ -58,6 +59,7 @@ cc_library( "src/exterior_product.hpp", "src/geometric_product.hpp", "src/get.hpp", + "src/interior_product.hpp", "src/multivector_for.hpp", "src/regressive_product.hpp", "src/sum.hpp", diff --git a/geometry/geometry.hpp b/geometry/geometry.hpp index 874b779..cb6d38f 100644 --- a/geometry/geometry.hpp +++ b/geometry/geometry.hpp @@ -8,6 +8,7 @@ #include "geometry/src/exterior_product.hpp" #include "geometry/src/geometric_product.hpp" #include "geometry/src/get.hpp" +#include "geometry/src/interior_product.hpp" #include "geometry/src/multivector_for.hpp" #include "geometry/src/regressive_product.hpp" #include "geometry/src/sum.hpp" diff --git a/geometry/src/algebra.hpp b/geometry/src/algebra.hpp index b1eb0e7..3a82572 100644 --- a/geometry/src/algebra.hpp +++ b/geometry/src/algebra.hpp @@ -1,17 +1,10 @@ #pragma once +#include "geometry/src/algebra_core.hpp" #include "geometry/src/detail/blade_list.hpp" -#include "geometry/src/detail/contract_dimensions.hpp" -#include "geometry/src/detail/ordered.hpp" -#include "geometry/src/detail/strictly_increasing.hpp" -#include "geometry/src/get.hpp" #include "geometry/type_metaprogramming.hpp" #include -#include -#include -#include -#include namespace geometry { @@ -25,105 +18,28 @@ namespace geometry { /// P(S^*_{N,0,1}) /// @f] /// +/// @note Defines a project geometric algebra, including common constants. +/// template -struct algebra +struct algebra : algebra_core { - /// scalar type + /// algebra core type /// - using scalar_type = S; + using algebra_core_type = typename algebra::algebra_core; - /// algebra dimension + /// registration of algebra type for associated core type + /// see declaration in algebra_core /// - static constexpr auto dimension = N + 1; + constexpr friend auto algebra_for(algebra_core_type) { return algebra{}; } /// dual algebra type /// using dual_algebra_type = algebra; - /// blade - /// @tparam Is dimensions - /// - /// Defines a blade for the given algebra. - /// - template - struct blade; - - /// multivector - /// @tparam Bs blade types - /// - /// A linear combination of blades. - /// - /// @note a multivector may be composed of only blades with the same grade - /// - template - struct multivector; - - /// determines if a type is a blade - /// @tparam T type - /// - /// @{ - template - struct is_blade : std::false_type - {}; - template - struct is_blade> : std::true_type - {}; - template - static constexpr auto is_blade_v = is_blade::value; - /// @} - - /// determines if a type is a multivector - /// @tparam T type - /// - /// @{ - template - struct is_multivector : std::false_type - {}; - template - struct is_multivector> : std::true_type - {}; - template - static constexpr auto is_multivector_v = is_multivector::value; - /// @} - /// list of blades in the algebra /// - using blade_list_type = detail::blade_list_t; - - /// blade product - /// - /// @{ - template - struct reified_blade - { - using type = tmp::convert_to_sequence_t< - detail::contract_dimensions_t< - detail::contraction_map::projective, - tmp::sort_t>>, - blade>; - }; - template - using reified_blade_t = typename reified_blade::type; - /// @} - - /// coefficient for blade product - /// - /// @{ - template - struct reified_blade_coefficient - : std::integral_constant< - int, - (tmp::sort_swap_count_v> % 2 == 0 - ? 1 - : -1) * - detail::contract_dimensions_coefficient_v< - detail::contraction_map::projective, - tmp::sort_t>>> - {}; - template - static constexpr auto reified_blade_coefficient_v = - reified_blade_coefficient::value; - /// @} + using blade_list_type = + detail::blade_list_t; /// unit blade /// @tparam Is dimensions @@ -147,282 +63,14 @@ struct algebra /// for `i == 0` and `1` for `i != 0`. /// template - static constexpr auto e = - reified_blade_t{scalar_type{reified_blade_coefficient_v}}; + static constexpr auto e = typename algebra_core_type:: + template reified_blade_t{typename algebra_core_type::scalar_type{ + algebra_core_type::template reified_blade_coefficient_v}}; /// pseudoscalar /// - static constexpr auto i = tmp::last_t{scalar_type{1}}; - - template - struct blade - { - static_assert(sizeof...(Is) <= N + 1, "number of `Is` exceeds rank"); - static_assert(((Is <= N) and ...), "`Is` value exceeds rank"); - static_assert( - detail::strictly_increasing(Is...), - "`Is` values must be unique and sorted"); - - /// algebra type - /// - using algebra_type = algebra; - - /// scalar type - /// - using scalar_type = typename algebra::scalar_type; - - /// grade - /// - static constexpr auto grade = sizeof...(Is); - - /// k-vector coefficient - /// - scalar_type coefficient{}; - - /// construct a zero blade - /// - blade() = default; - - /// construct a blade - /// @param a coefficient - /// @note this constructor is explicit if and only if grade != 0 - /// - /// @{ - template = true> - constexpr blade(scalar_type a) : coefficient{a} - {} - template = true> - constexpr explicit blade(scalar_type a) : coefficient{a} - {} - /// @} - - /// comparison operators - /// - /// @{ - [[nodiscard]] - friend constexpr auto - operator==(blade x, blade y) noexcept -> bool - { - return x.coefficient == y.coefficient; - } - [[nodiscard]] - friend constexpr auto - operator!=(blade x, blade y) noexcept -> bool - { - return x.coefficient != y.coefficient; - } - [[nodiscard]] - friend constexpr auto - operator<(blade x, blade y) noexcept -> bool - { - return x.coefficient < y.coefficient; - } - [[nodiscard]] - friend constexpr auto - operator>(blade x, blade y) noexcept -> bool - { - return x.coefficient > y.coefficient; - } - [[nodiscard]] - friend constexpr auto - operator<=(blade x, blade y) noexcept -> bool - { - return x.coefficient <= y.coefficient; - } - [[nodiscard]] - friend constexpr auto - operator>=(blade x, blade y) noexcept -> bool - { - return x.coefficient >= y.coefficient; - } - /// @} - - /// unary negation - /// - [[nodiscard]] - friend constexpr auto - operator-(blade x) -> blade - { - return blade{-x.coefficient}; - } - - /// addition - /// - /// @{ - [[nodiscard]] - friend constexpr auto - operator+(blade x, blade y) -> blade - { - return blade{x.coefficient + y.coefficient}; - } - /// @} - - /// compound assignment operators - /// - /// @{ - friend constexpr auto& operator+=(blade& x, blade y) { return x = x + y; } - friend constexpr auto& operator-=(blade& x, blade y) { return x = x - y; } - /// @} - - /// geometric product - /// - template - [[nodiscard]] - friend constexpr auto - operator*(blade x, blade y) - { - constexpr auto unit_blade_coeff = - reified_blade_coefficient_v; - using B = reified_blade_t; - - if constexpr (unit_blade_coeff == 0) { - return B{}; - } else { - const auto coeff = x.coefficient * y.coefficient; - return B{unit_blade_coeff == 1 ? coeff : -coeff}; - } - } - - /// stream insertion - /// - friend auto operator<<(std::ostream& os, blade x) -> std::enable_if_t< - (N < 10), - decltype(os << std::declval(), os)> - { - if (sizeof...(Is) == 0) { - os << x.coefficient; - return os; - } - - if (x.coefficient != scalar_type{1}) { - os << x.coefficient << "*"; - } - - os << "e"; - std::ignore = ((os << Is, true) and ...); - - return os; - } - }; - - template - struct multivector : Bs... - { - static_assert( - (is_blade_v and ...), "`Bs` must be a specialization of blade"); - static_assert( - tmp::same_v, - "`Bs` must have the same scalar type"); - static_assert( - detail::strictly_increasing(detail::ordered{}...), - "`Bs` must be lexicographically sorted"); - - /// algebra type - /// - using algebra_type = algebra; - - /// determine if a blade is contained in this multivector - /// - template - static constexpr auto contains = (std::is_same_v or ...); - - /// construct a zero multivector - /// - multivector() = default; - - /// construct a multivector from blades - /// - constexpr explicit multivector(Bs... bs) : Bs{bs}... {} - - /// construct a multivector from a scalar - /// - template < - class T = multivector>, - class = std::enable_if_t>> - constexpr explicit multivector(scalar_type s) : multivector(blade<>{s}) - {} - - /// access a specific blade - /// - /// @{ - template - friend constexpr auto - get(multivector& x) -> std::enable_if_t, B&> - { - return static_cast(x); - } - template - friend constexpr auto - get(const multivector& x) -> std::enable_if_t, const B&> - { - return static_cast(x); - } - template - friend constexpr auto - // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) - get(multivector&& x) -> std::enable_if_t, B&&> - { - return static_cast(x); - } - template - friend constexpr auto - get(const multivector&& x) -> std::enable_if_t, const B&&> - { - return static_cast(x); - } - /// @} - - /// access a specific blade or return a default value - /// - /// @{ - template - friend constexpr auto - get_or(const multivector& x, const B& value) -> const B& - { - if constexpr (contains) { - return get(x); - } else { - return value; - } - } - /// @} - - /// equality comparison - /// - template - [[nodiscard]] - friend constexpr auto - operator==(const multivector& x, const multivector& y) -> bool - { - return ((get_or(x, Bs{}) == get_or(y, Bs{})) and ...) and - ((get_or(x, B2s{}) == get_or(y, B2s{})) and ...); - } - - /// unary negation - /// - [[nodiscard]] - friend constexpr auto - operator-(const multivector& x) -> multivector - { - return multivector{-get(x)...}; - } - - /// stream insertion - /// - friend auto operator<<(std::ostream& os, const multivector& x) - -> decltype(((os << std::declval(), true) and ...), os) - { - auto first = true; - - std::ignore = - ((os << (std::exchange(first, false) ? "" : " + "), - os << get(x), - true) and - ...); - - return os; - } - }; + static constexpr auto i = + tmp::last_t{typename algebra_core_type::scalar_type{1}}; }; namespace detail { diff --git a/geometry/src/algebra_core.hpp b/geometry/src/algebra_core.hpp new file mode 100644 index 0000000..f481d02 --- /dev/null +++ b/geometry/src/algebra_core.hpp @@ -0,0 +1,414 @@ +#pragma once + +#include "geometry/src/detail/contract_dimensions.hpp" +#include "geometry/src/detail/ordered.hpp" +#include "geometry/src/detail/strictly_increasing.hpp" +#include "geometry/src/get.hpp" +#include "geometry/type_metaprogramming.hpp" + +#include +#include +#include +#include +#include + +namespace geometry { + +/// represents a projective geometric algebra +/// @tparam S scalar type +/// @tparam N number of euclidean geometry dimensions +/// @tparam Dual true if the algebra is the dual (e.g. G* vs G) +/// +/// Models an N-dimensional projective geometric algebra denoted as +/// @f[ +/// P(S^*_{N,0,1}) +/// @f] +/// +/// @note This type defines the `blade` and `multivector` types of an algebra +/// does not define common constants. +/// +template +struct algebra_core +{ +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif // __clang__ + /// registration of algebra type for associated core type + /// see declaration in algebra + /// + constexpr friend auto algebra_for(algebra_core); +#pragma GCC diagnostic pop + + /// scalar type + /// + using scalar_type = S; + + /// algebra dimension + /// + static constexpr auto dimension = N + 1; + + /// dual algebra type + /// + using dual_algebra_core_type = algebra_core; + + /// blade + /// @tparam Is dimensions + /// + /// Defines a blade for the given algebra. + /// + template + struct blade; + + /// multivector + /// @tparam Bs blade types + /// + /// A linear combination of blades. + /// + /// @note a multivector may be composed of only blades with the same grade + /// + template + struct multivector; + + /// reified blade product + /// @tparam Is dimensions + /// + /// Returns the blade type after sorting `Is` and removing duplicates. + /// + /// @{ + template + struct reified_blade + { + using type = tmp::convert_to_sequence_t< + detail::contract_dimensions_t< + detail::contraction_map::projective, + tmp::sort_t>>, + blade>; + }; + template + using reified_blade_t = typename reified_blade::type; + /// @} + + /// coefficient for blade product + /// @tparam Is dimensions + /// + /// Returns the scalar coefficient for the reified blade type after sorting + /// `Is` and removing duplicates. + /// + /// @{ + template + struct reified_blade_coefficient + : std::integral_constant< + int, + (tmp::sort_swap_count_v> % 2 == 0 + ? 1 + : -1) * + detail::contract_dimensions_coefficient_v< + detail::contraction_map::projective, + tmp::sort_t>>> + {}; + template + static constexpr auto reified_blade_coefficient_v = + reified_blade_coefficient::value; + /// @} + + /// determines if a type is a blade + /// @tparam T type + /// + /// @{ + template + struct is_blade : std::false_type + {}; + template + struct is_blade> : std::true_type + {}; + template + static constexpr auto is_blade_v = is_blade::value; + /// @} + + /// determines if a type is a multivector + /// @tparam T type + /// + /// @{ + template + struct is_multivector : std::false_type + {}; + template + struct is_multivector> : std::true_type + {}; + template + static constexpr auto is_multivector_v = is_multivector::value; + /// @} + + template + struct blade + { + static_assert(sizeof...(Is) <= N + 1, "number of `Is` exceeds rank"); + static_assert(((Is <= N) and ...), "`Is` value exceeds rank"); + static_assert( + detail::strictly_increasing(Is...), + "`Is` values must be unique and sorted"); + + /// algebra core type + /// + using algebra_core_type = algebra_core; + + /// scalar type + /// + using scalar_type = typename algebra_core_type::scalar_type; + + /// grade + /// + static constexpr auto grade = sizeof...(Is); + + /// k-vector coefficient + /// + scalar_type coefficient{}; + + /// construct a zero blade + /// + blade() = default; + + /// construct a blade + /// @param a coefficient + /// @note this constructor is explicit if and only if grade != 0 + /// + /// @{ + template = true> + constexpr blade(scalar_type a) : coefficient{a} + {} + template = true> + constexpr explicit blade(scalar_type a) : coefficient{a} + {} + /// @} + + /// comparison operators + /// + /// @{ + [[nodiscard]] + friend constexpr auto + operator==(blade x, blade y) noexcept -> bool + { + return x.coefficient == y.coefficient; + } + [[nodiscard]] + friend constexpr auto + operator!=(blade x, blade y) noexcept -> bool + { + return x.coefficient != y.coefficient; + } + [[nodiscard]] + friend constexpr auto + operator<(blade x, blade y) noexcept -> bool + { + return x.coefficient < y.coefficient; + } + [[nodiscard]] + friend constexpr auto + operator>(blade x, blade y) noexcept -> bool + { + return x.coefficient > y.coefficient; + } + [[nodiscard]] + friend constexpr auto + operator<=(blade x, blade y) noexcept -> bool + { + return x.coefficient <= y.coefficient; + } + [[nodiscard]] + friend constexpr auto + operator>=(blade x, blade y) noexcept -> bool + { + return x.coefficient >= y.coefficient; + } + /// @} + + /// unary negation + /// + [[nodiscard]] + friend constexpr auto + operator-(blade x) -> blade + { + return blade{-x.coefficient}; + } + + /// addition + /// + /// @{ + [[nodiscard]] + friend constexpr auto + operator+(blade x, blade y) -> blade + { + return blade{x.coefficient + y.coefficient}; + } + /// @} + + /// compound assignment operators + /// + /// @{ + friend constexpr auto& operator+=(blade& x, blade y) { return x = x + y; } + friend constexpr auto& operator-=(blade& x, blade y) { return x = x - y; } + /// @} + + /// geometric product + /// + template + [[nodiscard]] + friend constexpr auto + operator*(blade x, blade y) + { + constexpr auto unit_blade_coeff = + reified_blade_coefficient_v; + using B = reified_blade_t; + + if constexpr (unit_blade_coeff == 0) { + return B{}; + } else { + const auto coeff = x.coefficient * y.coefficient; + return B{unit_blade_coeff == 1 ? coeff : -coeff}; + } + } + + /// stream insertion + /// + friend auto operator<<(std::ostream& os, blade x) -> std::enable_if_t< + (N < 10), + decltype(os << std::declval(), os)> + { + if (sizeof...(Is) == 0) { + os << x.coefficient; + return os; + } + + if (x.coefficient != scalar_type{1}) { + os << x.coefficient << "*"; + } + + os << "e"; + std::ignore = ((os << Is, true) and ...); + + return os; + } + }; + + template + struct multivector : Bs... + { + static_assert( + (is_blade_v and ...), "`Bs` must be a specialization of blade"); + static_assert( + tmp::same_v, + "`Bs` must have the same scalar type"); + static_assert( + detail::strictly_increasing(detail::ordered{}...), + "`Bs` must be lexicographically sorted"); + + /// algebra core type + /// + using algebra_core_type = algebra_core; + + /// determine if a blade is contained in this multivector + /// + template + static constexpr auto contains = (std::is_same_v or ...); + + /// construct a zero multivector + /// + multivector() = default; + + /// construct a multivector from blades + /// + constexpr explicit multivector(Bs... bs) : Bs{bs}... {} + + /// construct a multivector from a scalar + /// + template < + class T = multivector>, + class = std::enable_if_t>> + constexpr explicit multivector(scalar_type s) : multivector(blade<>{s}) + {} + + /// access a specific blade + /// + /// @{ + template + friend constexpr auto + get(multivector& x) -> std::enable_if_t, B&> + { + return static_cast(x); + } + template + friend constexpr auto + get(const multivector& x) -> std::enable_if_t, const B&> + { + return static_cast(x); + } + template + friend constexpr auto + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + get(multivector&& x) -> std::enable_if_t, B&&> + { + return static_cast(x); + } + template + friend constexpr auto + get(const multivector&& x) -> std::enable_if_t, const B&&> + { + return static_cast(x); + } + /// @} + + /// access a specific blade or return a default value + /// + /// @{ + template + friend constexpr auto + get_or(const multivector& x, const B& value) -> const B& + { + if constexpr (contains) { + return get(x); + } else { + return value; + } + } + /// @} + + /// equality comparison + /// + template + [[nodiscard]] + friend constexpr auto + operator==(const multivector& x, const multivector& y) -> bool + { + return ((get_or(x, Bs{}) == get_or(y, Bs{})) and ...) and + ((get_or(x, B2s{}) == get_or(y, B2s{})) and ...); + } + + /// unary negation + /// + [[nodiscard]] + friend constexpr auto + operator-(const multivector& x) -> multivector + { + return multivector{-get(x)...}; + } + + /// stream insertion + /// + friend auto operator<<(std::ostream& os, const multivector& x) + -> decltype(((os << std::declval(), true) and ...), os) + { + auto first = true; + + std::ignore = + ((os << (std::exchange(first, false) ? "" : " + "), + os << get(x), + true) and + ...); + + return os; + } + }; +}; + +} // namespace geometry diff --git a/geometry/src/algebra_type.hpp b/geometry/src/algebra_type.hpp index d45cd8d..db34e4f 100644 --- a/geometry/src/algebra_type.hpp +++ b/geometry/src/algebra_type.hpp @@ -5,14 +5,39 @@ namespace geometry { namespace detail { +auto algebra_for() -> void = delete; + +inline constexpr class +{ + template + static constexpr auto impl(int) -> typename T::algebra_type + { + return {}; + } + template + static constexpr auto + impl(float) -> decltype(algebra_for(typename T::algebra_core_type{})) + { + return {}; + } + +public: + template + constexpr auto operator()(const T&) const -> decltype(impl(int{})) + { + return impl(int{}); + } + +} algebra_for_fn{}; + template struct algebra_type {}; template -struct algebra_type> +struct algebra_type()))>> { - using type = typename T::algebra_type; + using type = decltype(algebra_for_fn(std::declval())); }; } // namespace detail diff --git a/geometry/src/detail/blade_list.hpp b/geometry/src/detail/blade_list.hpp index 445ed6e..0d8ea01 100644 --- a/geometry/src/detail/blade_list.hpp +++ b/geometry/src/detail/blade_list.hpp @@ -43,7 +43,7 @@ struct grade template