diff --git a/geometry/BUILD.bazel b/geometry/BUILD.bazel index e9c04e7..1a5ff47 100644 --- a/geometry/BUILD.bazel +++ b/geometry/BUILD.bazel @@ -4,7 +4,9 @@ cc_library( name = "geometry", srcs = [ "src/algebra.hpp", + "src/detail/all_same.hpp", "src/detail/contract_dimensions.hpp", + "src/detail/ordered.hpp", "src/detail/sort_dimensions.hpp", "src/detail/strictly_increasing.hpp", ], diff --git a/geometry/src/algebra.hpp b/geometry/src/algebra.hpp index 9366446..4ad1bb5 100644 --- a/geometry/src/algebra.hpp +++ b/geometry/src/algebra.hpp @@ -1,6 +1,8 @@ #pragma once +#include "geometry/src/detail/all_same.hpp" #include "geometry/src/detail/contract_dimensions.hpp" +#include "geometry/src/detail/ordered.hpp" #include "geometry/src/detail/sort_dimensions.hpp" #include "geometry/src/detail/strictly_increasing.hpp" @@ -33,6 +35,20 @@ struct algebra template struct blade; + /// 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; + /// @} + private: template static auto rebind(std::index_sequence) -> blade; @@ -217,6 +233,51 @@ struct algebra return x.coefficient * y.coefficient * e; } }; + + /// 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 : Bs... + { + static_assert( + (is_blade_v and ...), "`Bs` must be a specialization of blade"); + static_assert( + detail::all_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; + + /// construct a multivector from blades + /// + constexpr multivector(Bs... bs) : Bs{bs}... {} + + /// equality comparison + /// + /// @{ + [[nodiscard]] + friend constexpr auto + operator==(const multivector& x, const multivector& y) -> bool + { + return ((static_cast(x) == static_cast(y)) and ...); + } + [[nodiscard]] + friend constexpr auto + operator!=(const multivector& x, const multivector& y) -> bool + { + return not(x == y); + } + /// @} + }; }; } // namespace geometry diff --git a/geometry/src/detail/all_same.hpp b/geometry/src/detail/all_same.hpp new file mode 100644 index 0000000..79f3499 --- /dev/null +++ b/geometry/src/detail/all_same.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace geometry::detail { + +template +struct all_same : std::false_type +{}; + +template <> +struct all_same<> : std::true_type +{}; +template +struct all_same : std::true_type +{}; +template +struct all_same : std::bool_constant<((std::is_same_v)and...)> +{}; + +template +inline constexpr auto all_same_v = all_same::value; + +} // namespace geometry::detail diff --git a/geometry/src/detail/ordered.hpp b/geometry/src/detail/ordered.hpp new file mode 100644 index 0000000..68aab64 --- /dev/null +++ b/geometry/src/detail/ordered.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace geometry::detail { + +template +class ordered +{ + template < + template + class blade, + std::size_t... Is, + std::size_t... Js> + static constexpr auto + compare_dimensions(ordered>, ordered>) + { + return std::tuple{Is...} < std::tuple{Js...}; + } + + template + friend constexpr auto operator<(ordered x, ordered y) -> bool + { + if constexpr (B::grade == B2::grade) { + return compare_dimensions(x, y); + } else { + return B::grade < B2::grade; + } + } +}; + +} // namespace geometry::detail diff --git a/geometry/src/detail/strictly_increasing.hpp b/geometry/src/detail/strictly_increasing.hpp index ba6e965..391e0bb 100644 --- a/geometry/src/detail/strictly_increasing.hpp +++ b/geometry/src/detail/strictly_increasing.hpp @@ -1,38 +1,31 @@ #pragma once -#include #include +#include #include namespace geometry::detail { inline constexpr class { - template - static constexpr auto impl(std::index_sequence<>, const std::array&) + template + static constexpr auto impl(std::index_sequence, const T& values) { - return true; - } - template < - std::size_t... Is, - class T, - class = std::enable_if_t<(sizeof...(Is) != 0)>> - static constexpr auto - impl(std::index_sequence, - const std::array& values) - { - return ((std::get(values) < std::get(values)) and ...); + if constexpr (sizeof...(Is) == 0) { + return true; + } else { + return (((std::get(values)) < (std::get(values))) and ...); + } } public: // 0-args constexpr auto operator()() const { return true; } // 1+-args - template - constexpr auto operator()(std::size_t t0, Ts... ts) const + template + constexpr auto operator()(T0 t0, Ts... ts) const { - static_assert((std::is_same_v and ...)); - return impl(std::index_sequence_for{}, std::array{t0, ts...}); + return impl(std::index_sequence_for{}, std::tuple{t0, ts...}); } } strictly_increasing{}; diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 7009c30..ee7904d 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -1,8 +1,17 @@ load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( - name = "algebra_test", - srcs = ["algebra_test.cpp"], + name = "algebra_blade_test", + srcs = ["algebra_blade_test.cpp"], + deps = [ + "//:geometry", + "@skytest", + ], +) + +cc_test( + name = "algebra_multivector_test", + srcs = ["algebra_multivector_test.cpp"], deps = [ "//:geometry", "@skytest", diff --git a/test/algebra_test.cpp b/test/algebra_blade_test.cpp similarity index 100% rename from test/algebra_test.cpp rename to test/algebra_blade_test.cpp diff --git a/test/algebra_multivector_test.cpp b/test/algebra_multivector_test.cpp new file mode 100644 index 0000000..cabeaa8 --- /dev/null +++ b/test/algebra_multivector_test.cpp @@ -0,0 +1,48 @@ +#include "geometry/geometry.hpp" +#include "skytest/skytest.hpp" + +#include +#include +#include + +template +constexpr auto e = ::geometry::algebra::e; + +/// construct a multivector from blades +/// +inline constexpr struct +{ + template < + class... Bs, + class = std::enable_if_t< + sizeof...(Bs) != 0 and + ::geometry::detail::all_same_v>> + [[nodiscard]] + constexpr auto + operator()(Bs... bs) const + { + using Algebra = std::common_type_t; + return typename Algebra::template multivector{bs...}; + } +} multivector{}; + +auto main() -> int +{ + using namespace ::skytest::literals; + using ::skytest::eq; + using ::skytest::expect; + using ::skytest::param_ref; + + static constexpr auto multivectors = std::tuple{ + multivector(e<>), + multivector(e<>, e<0>), + multivector(e<>, e<1>), + multivector(e<>, e<2>), + multivector(e<>, e<0>, e<1, 2>), + multivector(e<>, e<0, 1>, e<1, 2>), + multivector(e<>, e<0, 1, 2>)}; + + "multivector with same elements comparable"_ctest * + param_ref = // + [](auto v) { return expect(eq(v, v)); }; +}