From 1d8ffaaa343b3447144e42f59ee8c91573c5b09e Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 20 Jan 2021 22:18:05 +0700 Subject: [PATCH] basic qua data structure --- headers/vmath.hpp/vmath_qua.hpp | 141 +++++++++++++++++ headers/vmath.hpp/vmath_qua_fun.hpp | 80 ++++++++++ untests/vmath_qua_tests.cpp | 226 ++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua.hpp b/headers/vmath.hpp/vmath_qua.hpp index efe3664..b316969 100644 --- a/headers/vmath.hpp/vmath_qua.hpp +++ b/headers/vmath.hpp/vmath_qua.hpp @@ -8,10 +8,151 @@ #include "vmath_fwd.hpp" +#include "vmath_vec.hpp" +#include "vmath_vec_fun.hpp" + namespace vmath_hpp::detail { + template < typename T > + class qua_base { + public: + T x{0}, y{0}, z{0}, w{1}; + public: + constexpr qua_base() = default; + + constexpr explicit qua_base(T w) + : x{0}, y{0}, z{0}, w{w} {} + + constexpr qua_base(T x, T y, T z, T w) + : x{x}, y{y}, z{z}, w{w} {} + + constexpr qua_base(const vec& xyz, T w) + : x{xyz[0]}, y{xyz[1]}, z{xyz[2]}, w{w} {} + + constexpr qua_base(const vec& xyzw) + : x{xyzw[0]}, y{xyzw[1]}, z{xyzw[2]}, w{xyzw[3]} {} + + [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { + switch ( index ) { + default: + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + }; } namespace vmath_hpp { + template < typename T > + class qua final : public detail::qua_base { + public: + using self_type = qua; + using base_type = detail::qua_base; + public: + using component_type = T; + + using pointer = component_type*; + using const_pointer = const component_type*; + + using reference = component_type&; + using const_reference = const component_type&; + + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr std::size_t size = 4; + public: + using base_type::qua_base; + using base_type::operator[]; + + constexpr qua() = default; + constexpr qua(const qua&) = default; + constexpr qua& operator=(const qua&) = default; + + void swap(qua& other) noexcept(std::is_nothrow_swappable_v) { + for ( std::size_t i = 0; i < size; ++i ) { + using std::swap; + swap((*this)[i], other[i]); + } + } + + [[nodiscard]] iterator begin() noexcept { return iterator(data()); } + [[nodiscard]] const_iterator begin() const noexcept { return const_iterator(data()); } + [[nodiscard]] iterator end() noexcept { return iterator(data() + size); } + [[nodiscard]] const_iterator end() const noexcept { return const_iterator(data() + size); } + + [[nodiscard]] reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + [[nodiscard]] reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + [[nodiscard]] const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + + [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } + [[nodiscard]] const_iterator cend() const noexcept { return end(); } + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } + [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); } + + [[nodiscard]] pointer data() noexcept { + return &(*this)[0]; + } + + [[nodiscard]] const_pointer data() const noexcept { + return &(*this)[0]; + } + + [[nodiscard]] constexpr reference at(std::size_t index) { + if ( index >= size ) { + throw std::out_of_range("qua::at"); + } + return (*this)[index]; + } + + [[nodiscard]] constexpr const_reference at(std::size_t index) const { + if ( index >= size ) { + throw std::out_of_range("qua::at"); + } + return (*this)[index]; + } + }; +} + +namespace vmath_hpp +{ + // qua + + template < typename T > + qua(T) -> qua; + + template < typename T > + qua(T, T, T, T) -> qua; + + template < typename T > + qua(const vec&, T) -> qua; + + template < typename T > + qua(const vec&) -> qua; + + template < typename T > + qua(std::initializer_list) -> qua; + + // swap + + template < typename T > + void swap(qua& l, qua& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } } diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 98ad9aa..4f2c24d 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -13,12 +13,92 @@ namespace vmath_hpp::detail::impl { + template < typename A, typename B, typename F, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold_join_impl( + F&& f, + A init, + const qua& b, + std::index_sequence + ) -> A { + return ((init = f(std::move(init), b[Is])), ...); + } + + template < typename A, typename B, typename C, typename F, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold_join_impl( + F&& f, + A init, + const qua& b, + const qua& c, + std::index_sequence + ) -> A { + return ((init = f(std::move(init), b[Is], c[Is])), ...); + } } namespace vmath_hpp::detail { + template < typename A, typename B, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold_join( + F&& f, + A init, + const qua& b + ) { + return impl::fold_join_impl( + std::forward(f), std::move(init), b, std::make_index_sequence<4>{}); + } + + template < typename A, typename B, typename C, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold_join( + F&& f, + A init, + const qua& b, + const qua& c + ) { + return impl::fold_join_impl( + std::forward(f), std::move(init), b, c, std::make_index_sequence<4>{}); + } } +// +// Operators +// + namespace vmath_hpp { + // operator== + + template < typename T > + [[nodiscard]] constexpr bool operator==(const qua& xs, const qua& ys) { + return fold_join([](bool acc, T x, T y){ + return acc && x == y; + }, true, xs, ys); + } + + // operator!= + + template < typename T > + [[nodiscard]] constexpr bool operator!=(const qua& xs, const qua& ys) { + return fold_join([](bool acc, T x, T y){ + return acc || x != y; + }, false, xs, ys); + } + + // operator< + + template < typename T > + [[nodiscard]] constexpr bool operator<(const qua& xs, const qua& ys) { + for ( std::size_t i = 0; i < 4; ++i ) { + if ( xs[i] < ys[i] ) { + return true; + } + if ( ys[i] < xs[i] ) { + return false; + } + } + return false; + } } diff --git a/untests/vmath_qua_tests.cpp b/untests/vmath_qua_tests.cpp index bab357c..8456053 100644 --- a/untests/vmath_qua_tests.cpp +++ b/untests/vmath_qua_tests.cpp @@ -14,4 +14,230 @@ namespace } TEST_CASE("vmath/qua") { + SUBCASE("size/sizeof") { + STATIC_REQUIRE(fqua{}.size == 4); + STATIC_REQUIRE(dqua{}.size == 4); + + STATIC_REQUIRE(sizeof(fqua{}) == sizeof(float) * 4); + STATIC_REQUIRE(sizeof(dqua{}) == sizeof(double) * 4); + } + + SUBCASE("guides") { + STATIC_REQUIRE(qua{1}.size == 4); + STATIC_REQUIRE(qua{1,2,3,4}.size == 4); + STATIC_REQUIRE(qua{{1,2,3},4}.size == 4); + STATIC_REQUIRE(qua{vec{1,2,3},4}.size == 4); + STATIC_REQUIRE(qua{{1,2,3,4}}.size == 4); + STATIC_REQUIRE(qua{vec{1,2,3,4}}.size == 4); + } + + SUBCASE("ctors") { + { + STATIC_REQUIRE(fqua{}.x == 0.f); + STATIC_REQUIRE(fqua{}.y == 0.f); + STATIC_REQUIRE(fqua{}.z == 0.f); + STATIC_REQUIRE(fqua{}.w == 1.f); + + STATIC_REQUIRE(fqua{1,2,3,4}.x == 1.f); + STATIC_REQUIRE(fqua{1,2,3,4}.y == 2.f); + STATIC_REQUIRE(fqua{1,2,3,4}.z == 3.f); + STATIC_REQUIRE(fqua{1,2,3,4}.w == 4.f); + + STATIC_REQUIRE(fqua{{1,2,3},4}.x == 1.f); + STATIC_REQUIRE(fqua{{1,2,3},4}.y == 2.f); + STATIC_REQUIRE(fqua{{1,2,3},4}.z == 3.f); + STATIC_REQUIRE(fqua{{1,2,3},4}.w == 4.f); + + STATIC_REQUIRE(fqua{{1,2,3,4}}.x == 1.f); + STATIC_REQUIRE(fqua{{1,2,3,4}}.y == 2.f); + STATIC_REQUIRE(fqua{{1,2,3,4}}.z == 3.f); + STATIC_REQUIRE(fqua{{1,2,3,4}}.w == 4.f); + } + { + constexpr fqua q(1,2,3,4); + constexpr fqua q2 = q; + STATIC_REQUIRE(q2 == fqua(1,2,3,4)); + } + { + constexpr fqua q(1,2,3,4); + constexpr fqua q2 = std::move(q); + STATIC_REQUIRE(q2 == fqua(1,2,3,4)); + } + { + STATIC_REQUIRE(fqua(2) == fqua(0,0,0,2)); + STATIC_REQUIRE(fqua(1,2,3,4) == fqua(1,2,3,4)); + STATIC_REQUIRE(fqua(float3(1,2,3),4) == fqua(1,2,3,4)); + } + } + + SUBCASE("operator=") { + { + fqua v(1,2,3,4); + fqua v2; + v2 = v; + REQUIRE(v2 == fqua(1,2,3,4)); + } + { + fqua v(1,2,3,4); + fqua v2; + v2 = std::move(v); + REQUIRE(v2 == fqua(1,2,3,4)); + } + } + + SUBCASE("swap") { + { + fqua v1(1,2,3,4); + fqua v2(4,5,6,7); + v1.swap(v2); + REQUIRE(v1 == fqua(4,5,6,7)); + REQUIRE(v2 == fqua(1,2,3,4)); + } + { + fqua v1(1,2,3,4); + fqua v2(4,5,6,7); + swap(v1, v2); + REQUIRE(v1 == fqua(4,5,6,7)); + REQUIRE(v2 == fqua(1,2,3,4)); + } + } + + SUBCASE("iter") { + { + fqua v{1,2,3,4}; + + REQUIRE(*v.begin() == 1); + REQUIRE(*(v.begin() + 1) == 2); + REQUIRE(*(v.end() - 1) == 4); + REQUIRE(*(v.end() - 2) == 3); + REQUIRE(v.begin() + 4 == v.end()); + REQUIRE(v.end() - 4 == v.begin()); + + REQUIRE(*v.cbegin() == 1); + REQUIRE(*(v.cbegin() + 1) == 2); + REQUIRE(*(v.cend() - 1) == 4); + REQUIRE(*(v.cend() - 2) == 3); + REQUIRE(v.cbegin() + 4 == v.cend()); + REQUIRE(v.cend() - 4 == v.cbegin()); + + REQUIRE(*v.rbegin() == 4); + REQUIRE(*(v.rbegin() + 1) == 3); + REQUIRE(*(v.rend() - 1) == 1); + REQUIRE(*(v.rend() - 2) == 2); + REQUIRE(v.rbegin() + 4 == v.rend()); + REQUIRE(v.rend() - 4 == v.rbegin()); + + REQUIRE(*v.crbegin() == 4); + REQUIRE(*(v.crbegin() + 1) == 3); + REQUIRE(*(v.crend() - 1) == 1); + REQUIRE(*(v.crend() - 2) == 2); + REQUIRE(v.crbegin() + 4 == v.crend()); + REQUIRE(v.crend() - 4 == v.crbegin()); + + *v.begin() = 3; + REQUIRE(v == fqua{3,2,3,4}); + *v.rbegin() = 5; + REQUIRE(v == fqua{3,2,3,5}); + } + { + const fqua v{1,2,3,4}; + + REQUIRE(*v.begin() == 1); + REQUIRE(*(v.begin() + 1) == 2); + REQUIRE(*(v.end() - 1) == 4); + REQUIRE(*(v.end() - 2) == 3); + REQUIRE(v.begin() + 4 == v.end()); + REQUIRE(v.end() - 4 == v.begin()); + + REQUIRE(*v.cbegin() == 1); + REQUIRE(*(v.cbegin() + 1) == 2); + REQUIRE(*(v.cend() - 1) == 4); + REQUIRE(*(v.cend() - 2) == 3); + REQUIRE(v.cbegin() + 4 == v.cend()); + REQUIRE(v.cend() - 4 == v.cbegin()); + + REQUIRE(*v.rbegin() == 4); + REQUIRE(*(v.rbegin() + 1) == 3); + REQUIRE(*(v.rend() - 1) == 1); + REQUIRE(*(v.rend() - 2) == 2); + REQUIRE(v.rbegin() + 4 == v.rend()); + REQUIRE(v.rend() - 4 == v.rbegin()); + + REQUIRE(*v.crbegin() == 4); + REQUIRE(*(v.crbegin() + 1) == 3); + REQUIRE(*(v.crend() - 1) == 1); + REQUIRE(*(v.crend() - 2) == 2); + REQUIRE(v.crbegin() + 4 == v.crend()); + REQUIRE(v.crend() - 4 == v.crbegin()); + } + } + + SUBCASE("data") { + { + fqua i2; + REQUIRE(i2.data() == &i2[0]); + } + { + const fqua i2; + REQUIRE(i2.data() == &i2[0]); + } + } + + SUBCASE("operator[]") { + { + STATIC_REQUIRE(fqua(1,2,3,4).x == 1); + STATIC_REQUIRE(fqua(1,2,3,4).y == 2); + STATIC_REQUIRE(fqua(1,2,3,4).z == 3); + STATIC_REQUIRE(fqua(1,2,3,4).w == 4); + } + { + STATIC_REQUIRE(fqua(1,2,3,4)[0] == 1); + STATIC_REQUIRE(fqua(1,2,3,4)[1] == 2); + STATIC_REQUIRE(fqua(1,2,3,4)[2] == 3); + STATIC_REQUIRE(fqua(1,2,3,4)[3] == 4); + } + { + fqua v; + v.x = 1; + v.y = 2; + v.z = 3; + v.w = 4; + REQUIRE(v == fqua(1,2,3,4)); + } + } + + SUBCASE("at") { + STATIC_REQUIRE(fqua(1,2,3,4).at(0) == 1); + STATIC_REQUIRE(fqua(1,2,3,4).at(1) == 2); + STATIC_REQUIRE(fqua(1,2,3,4).at(2) == 3); + STATIC_REQUIRE(fqua(1,2,3,4).at(3) == 4); + REQUIRE_THROWS_AS((void)fqua(1,2,3,4).at(4), std::out_of_range); + } + + SUBCASE("operator==/operator!=") { + STATIC_REQUIRE(fqua(1,2,3,4) == fqua(1,2,3,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) == fqua(2,2,3,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) == fqua(1,3,3,4)); + + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) != fqua(1,2,3,4)); + STATIC_REQUIRE(fqua(1,2,3,4) != fqua(2,2,3,4)); + STATIC_REQUIRE(fqua(1,2,3,4) != fqua(1,3,3,4)); + } + + SUBCASE("operator<") { + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(1,2,3,4)); + + STATIC_REQUIRE(fqua(0,2,3,4) < fqua(1,2,3,4)); + STATIC_REQUIRE(fqua(1,1,3,4) < fqua(1,2,3,4)); + STATIC_REQUIRE(fqua(1,2,2,4) < fqua(1,2,3,4)); + STATIC_REQUIRE(fqua(1,2,3,3) < fqua(1,2,3,4)); + + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(0,2,3,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(1,1,3,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(1,2,2,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(1,2,3,3)); + + STATIC_REQUIRE(fqua(0,3,3,4) < fqua(1,2,3,4)); + STATIC_REQUIRE_FALSE(fqua(1,2,3,4) < fqua(0,3,3,4)); + } }