From c5af0faafa822e9db410553e77df16d1cb7f7ee0 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 20 Jan 2021 21:31:27 +0700 Subject: [PATCH 01/51] basic qua headers --- headers/vmath.hpp/vmath.hpp | 3 +++ headers/vmath.hpp/vmath_fwd.hpp | 9 +++++++++ headers/vmath.hpp/vmath_qua.hpp | 17 +++++++++++++++++ headers/vmath.hpp/vmath_qua_fun.hpp | 24 ++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 17 +++++++++++++++++ untests/vmath_qua_tests.cpp | 17 +++++++++++++++++ 6 files changed, 87 insertions(+) create mode 100644 headers/vmath.hpp/vmath_qua.hpp create mode 100644 headers/vmath.hpp/vmath_qua_fun.hpp create mode 100644 untests/vmath_qua_fun_tests.cpp create mode 100644 untests/vmath_qua_tests.cpp diff --git a/headers/vmath.hpp/vmath.hpp b/headers/vmath.hpp/vmath.hpp index 4d04e19..64b7e5a 100644 --- a/headers/vmath.hpp/vmath.hpp +++ b/headers/vmath.hpp/vmath.hpp @@ -14,5 +14,8 @@ #include "vmath_mat.hpp" #include "vmath_mat_fun.hpp" +#include "vmath_qua.hpp" +#include "vmath_qua_fun.hpp" + #include "vmath_vec.hpp" #include "vmath_vec_fun.hpp" diff --git a/headers/vmath.hpp/vmath_fwd.hpp b/headers/vmath.hpp/vmath_fwd.hpp index 82c21c4..7ef864a 100644 --- a/headers/vmath.hpp/vmath_fwd.hpp +++ b/headers/vmath.hpp/vmath_fwd.hpp @@ -91,3 +91,12 @@ namespace vmath_hpp using ptrdiff3x3 = mat; using ptrdiff4x4 = mat; } + +namespace vmath_hpp +{ + template < typename T > + class qua; + + using fqua = qua; + using dqua = qua; +} diff --git a/headers/vmath.hpp/vmath_qua.hpp b/headers/vmath.hpp/vmath_qua.hpp new file mode 100644 index 0000000..efe3664 --- /dev/null +++ b/headers/vmath.hpp/vmath_qua.hpp @@ -0,0 +1,17 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020-2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "vmath_fwd.hpp" + +namespace vmath_hpp::detail +{ +} + +namespace vmath_hpp +{ +} diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp new file mode 100644 index 0000000..98ad9aa --- /dev/null +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020-2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "vmath_fwd.hpp" + +#include "vmath_fun.hpp" +#include "vmath_qua.hpp" + +namespace vmath_hpp::detail::impl +{ +} + +namespace vmath_hpp::detail +{ +} + +namespace vmath_hpp +{ +} diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp new file mode 100644 index 0000000..dff5436 --- /dev/null +++ b/untests/vmath_qua_fun_tests.cpp @@ -0,0 +1,17 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020-2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "vmath_tests.hpp" +#include "doctest/doctest.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/qua_fun") { +} diff --git a/untests/vmath_qua_tests.cpp b/untests/vmath_qua_tests.cpp new file mode 100644 index 0000000..bab357c --- /dev/null +++ b/untests/vmath_qua_tests.cpp @@ -0,0 +1,17 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020-2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "vmath_tests.hpp" +#include "doctest/doctest.hpp" + +namespace +{ + using namespace vmath_hpp; + using namespace vmath_tests; +} + +TEST_CASE("vmath/qua") { +} From 1d8ffaaa343b3447144e42f59ee8c91573c5b09e Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 20 Jan 2021 22:18:05 +0700 Subject: [PATCH 02/51] 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)); + } } From 41c21078da4a13ed39e4aa1849b8e04aa9d38260 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 25 Jan 2021 05:46:37 +0700 Subject: [PATCH 03/51] basic qua detail functions --- headers/vmath.hpp/vmath_qua_fun.hpp | 96 +++++++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 25 ++++++++ 2 files changed, 121 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 4f2c24d..b8f3280 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -13,6 +13,48 @@ namespace vmath_hpp::detail::impl { + template < typename A, typename F, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join_impl( + F&& f, + const qua& a, + std::index_sequence + ) -> qua()))> + { + return { f(a[Is])... }; + } + + template < typename A, typename B, typename F, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join_impl( + F&& f, + const qua& a, + const qua& b, + std::index_sequence + ) -> qua(), + std::declval()))> + { + return { f(a[Is], b[Is])... }; + } + + template < typename A, typename B, typename C, typename F, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join_impl( + F&& f, + const qua& a, + const qua& b, + const qua& c, + std::index_sequence + ) -> qua(), + std::declval(), + std::declval()))> + { + return { f(a[Is], b[Is], c[Is])... }; + } + template < typename A, typename B, typename F, std::size_t... Is > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto fold_join_impl( @@ -35,10 +77,54 @@ namespace vmath_hpp::detail::impl ) -> A { return ((init = f(std::move(init), b[Is], c[Is])), ...); } + + template < typename A, typename F, std::size_t I, std::size_t... Is > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold1_join_impl( + F&& f, + const qua& a, + std::index_sequence + ) -> A { + A init = a[I]; + return ((init = f(std::move(init), a[Is])), ...); + } } namespace vmath_hpp::detail { + template < typename A, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join( + F&& f, + const qua& a + ) { + return impl::map_join_impl( + std::forward(f), a, std::make_index_sequence<4>{}); + } + + template < typename A, typename B, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join( + F&& f, + const qua& a, + const qua& b + ) { + return impl::map_join_impl( + std::forward(f), a, b, std::make_index_sequence<4>{}); + } + + template < typename A, typename B, typename C, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto map_join( + F&& f, + const qua& a, + const qua& b, + const qua& c + ) { + return impl::map_join_impl( + std::forward(f), a, b, c, std::make_index_sequence<4>{}); + } + template < typename A, typename B, typename F > [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE auto fold_join( @@ -61,6 +147,16 @@ namespace vmath_hpp::detail return impl::fold_join_impl( std::forward(f), std::move(init), b, c, std::make_index_sequence<4>{}); } + + template < typename A, typename F > + [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE + auto fold1_join( + F&& f, + const qua& a + ) { + return impl::fold1_join_impl( + std::forward(f), a, std::make_index_sequence<4>{}); + } } // diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index dff5436..4f02c92 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -14,4 +14,29 @@ namespace } TEST_CASE("vmath/qua_fun") { + SUBCASE("Detail") { + STATIC_REQUIRE(map_join([](const int& x){ + return x * 2; + }, qua(1,2,3,4)) == qua(2,4,6,8)); + + STATIC_REQUIRE(map_join([](const int& x, const int& y){ + return x + y; + }, qua(1,2,3,4), qua(2,3,4,5)) == qua(3,5,7,9)); + + STATIC_REQUIRE(map_join([](const int& x, const int& y, const int& z){ + return x + y + z; + }, qua(1,2,3,4), qua(2,3,4,5), qua(3,4,5,6)) == qua(6,9,12,15)); + + STATIC_REQUIRE(fold_join([](int acc, const int& x){ + return acc + x; + }, 0, qua(1,2,3,4)) == 10); + + STATIC_REQUIRE(fold_join([](int acc, const int& x, const int& y){ + return acc + x + y; + }, 0, qua(1,2,3,4), qua(2,3,4,5)) == 24); + + STATIC_REQUIRE(fold1_join([](const int& acc, const int& x){ + return acc + x; + }, qua{1,2,3,4}) == 10); + } } From e26d759b6d8ce6fdc410cc570338b94bbc15e2bb Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 25 Jan 2021 05:56:52 +0700 Subject: [PATCH 04/51] qua +,-,+=,-= operators --- headers/vmath.hpp/vmath_qua_fun.hpp | 42 +++++++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 20 ++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index b8f3280..5c83b55 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -165,6 +165,48 @@ namespace vmath_hpp::detail namespace vmath_hpp { + // +operator + + template < typename T > + [[nodiscard]] constexpr qua operator+(const qua& xs) { + return xs; + } + + // -operator + + template < typename T > + [[nodiscard]] constexpr qua operator-(const qua& xs) { + return map_join([](T x){ return -x; }, xs); + } + + // operator+ + + template < typename T > + [[nodiscard]] constexpr qua operator+(const qua& xs, const qua& ys) { + return map_join([](T x, T y){ return x + y; }, xs, ys); + } + + // operator+= + + template < typename T > + constexpr qua& operator+=(qua& xs, const qua& ys) { + return (xs = (xs + ys)); + } + + // operator- + + template < typename T > + [[nodiscard]] constexpr qua operator-(const qua& xs, const qua& ys) { + return map_join([](T x, T y){ return x - y; }, xs, ys); + } + + // operator-= + + template < typename T > + constexpr qua& operator-=(qua& xs, const qua& ys) { + return (xs = (xs - ys)); + } + // operator== template < typename T > diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index 4f02c92..fc61a1c 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -39,4 +39,24 @@ TEST_CASE("vmath/qua_fun") { return acc + x; }, qua{1,2,3,4}) == 10); } + + SUBCASE("Operators") { + STATIC_REQUIRE(+qua(1,-2,3,-4) == qua(1,-2,3,-4)); + STATIC_REQUIRE(-qua(1,-2,3,-4) == qua(-1,2,-3,4)); + + STATIC_REQUIRE(qua(1,2,3,4) + qua(3,4,5,6) == qua(4,6,8,10)); + STATIC_REQUIRE(qua(1,2,3,4) - qua(3,5,7,9) == qua(-2,-3,-4,-5)); + + { + qua v{1,2,3,4}; + REQUIRE(&v == &(v += qua{3,4,5,6})); + REQUIRE(v == qua{4,6,8,10}); + } + + { + qua v{1,2,3,4}; + REQUIRE(&v == &(v -= qua{3,4,5,6})); + REQUIRE(v == qua{-2,-2,-2,-2}); + } + } } From 55d5dfd9a36157bf9fb59cc2a59f12218d7506f7 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 25 Jan 2021 06:10:53 +0700 Subject: [PATCH 05/51] hash and cast_to qua functions --- headers/vmath.hpp/vmath_ext.hpp | 18 ++++++++ untests/vmath_ext_tests.cpp | 73 +++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/headers/vmath.hpp/vmath_ext.hpp b/headers/vmath.hpp/vmath_ext.hpp index eb89ef2..de68b97 100644 --- a/headers/vmath.hpp/vmath_ext.hpp +++ b/headers/vmath.hpp/vmath_ext.hpp @@ -11,6 +11,7 @@ #include "vmath_fun.hpp" #include "vmath_vec_fun.hpp" #include "vmath_mat_fun.hpp" +#include "vmath_qua_fun.hpp" // // Units @@ -73,6 +74,11 @@ namespace vmath_hpp::detail [[nodiscard]] std::size_t hash(const mat& m) noexcept { return fold_join(hash_combiner{}, std::size_t{}, m); } + + template < typename T > + [[nodiscard]] std::size_t hash(const qua& q) noexcept { + return fold_join(hash_combiner{}, std::size_t{}, q); + } } namespace std @@ -90,6 +96,13 @@ namespace std return vmath_hpp::detail::hash(m); } }; + + template < typename T > + struct hash> { + size_t operator()(const vmath_hpp::qua& q) const noexcept { + return vmath_hpp::detail::hash(q); + } + }; } // @@ -115,6 +128,11 @@ namespace vmath_hpp [[nodiscard]] constexpr mat cast_to(const mat& m) { return detail::map_join([](const vec& v){ return cast_to(v); }, m); } + + template < typename To, typename From > + [[nodiscard]] constexpr qua cast_to(const qua& q) { + return detail::map_join([](From x){ return cast_to(x); }, q); + } } // diff --git a/untests/vmath_ext_tests.cpp b/untests/vmath_ext_tests.cpp index db3ea47..47014c0 100644 --- a/untests/vmath_ext_tests.cpp +++ b/untests/vmath_ext_tests.cpp @@ -52,7 +52,7 @@ TEST_CASE("vmath/ext") { STATIC_REQUIRE(identity4x4 == int4x4()); } - SUBCASE("hash") { + SUBCASE("vector hash") { REQUIRE(std::hash{}({1,2}) == std::hash{}({1,2})); REQUIRE_FALSE(std::hash{}({1,2}) == std::hash{}({2,1})); @@ -62,8 +62,6 @@ TEST_CASE("vmath/ext") { REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({3,2,1,4})); - REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); - REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,4,3})); { std::set s; s.insert(int2(1,2)); @@ -92,6 +90,70 @@ TEST_CASE("vmath/ext") { } } + SUBCASE("matrix hash") { + REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); + REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,4,3})); + + { + std::set s; + s.insert(int2x2(1,2,3,4)); + REQUIRE(s.count(int2x2(1,2,3,4)) > 0); + REQUIRE_FALSE(s.count(int2x2(1,1,1,1)) > 0); + } + { + std::map s; + s.emplace(int2x2(1,2,3,4),3); + s.emplace(int2x2(2,3,4,5),5); + REQUIRE(s[int2x2(1,2,3,4)] == 3); + REQUIRE(s[int2x2(2,3,4,5)] == 5); + } + { + std::unordered_set s; + s.insert(int2x2(1,2,3,4)); + REQUIRE(s.count(int2x2(1,2,3,4)) > 0); + REQUIRE_FALSE(s.count(int2x2(1,1,1,1)) > 0); + } + { + std::unordered_map s; + s.emplace(int2x2(1,2,3,4),3); + s.emplace(int2x2(2,3,4,5),5); + REQUIRE(s[int2x2(1,2,3,4)] == 3); + REQUIRE(s[int2x2(2,3,4,5)] == 5); + } + } + + SUBCASE("quaternion hash") { + REQUIRE(std::hash{}({1,2,3,4}) == std::hash{}({1,2,3,4})); + REQUIRE_FALSE(std::hash{}({1,2,3,4}) == std::hash{}({3,2,1,4})); + + { + std::set> s; + s.insert(qua(1,2,3,4)); + REQUIRE(s.count(qua(1,2,3,4)) > 0); + REQUIRE_FALSE(s.count(qua(1,1,1,1)) > 0); + } + { + std::map, int> s; + s.emplace(qua(1,2,3,4),3); + s.emplace(qua(2,3,4,5),5); + REQUIRE(s[qua(1,2,3,4)] == 3); + REQUIRE(s[qua(2,3,4,5)] == 5); + } + { + std::unordered_set> s; + s.insert(qua(1,2,3,4)); + REQUIRE(s.count(qua(1,2,3,4)) > 0); + REQUIRE_FALSE(s.count(qua(1,1,1,1)) > 0); + } + { + std::unordered_map, int> s; + s.emplace(qua(1,2,3,4),3); + s.emplace(qua(2,3,4,5),5); + REQUIRE(s[qua(1,2,3,4)] == 3); + REQUIRE(s[qua(2,3,4,5)] == 5); + } + } + SUBCASE("cast_to") { { constexpr auto i = cast_to(1.5f); @@ -108,6 +170,11 @@ TEST_CASE("vmath/ext") { STATIC_REQUIRE(m == int2x2(1)); STATIC_REQUIRE(std::is_same_v); } + { + constexpr auto v = cast_to(fqua{1.5f, 2.2f, 3.6f, 4.5f}); + STATIC_REQUIRE(v == qua(1,2,3,4)); + STATIC_REQUIRE(std::is_same_v); + } } SUBCASE("component") { From ec5d2d59e3e53875164c637d75cb0c74b055e478 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 01:45:56 +0700 Subject: [PATCH 06/51] qua without fold details --- headers/vmath.hpp/vmath_ext.hpp | 4 +- headers/vmath.hpp/vmath_qua.hpp | 95 +++++++-------- headers/vmath.hpp/vmath_qua_fun.hpp | 176 ++-------------------------- untests/vmath_qua_fun_tests.cpp | 26 ---- untests/vmath_qua_tests.cpp | 53 ++++----- 5 files changed, 77 insertions(+), 277 deletions(-) diff --git a/headers/vmath.hpp/vmath_ext.hpp b/headers/vmath.hpp/vmath_ext.hpp index de68b97..64c30aa 100644 --- a/headers/vmath.hpp/vmath_ext.hpp +++ b/headers/vmath.hpp/vmath_ext.hpp @@ -77,7 +77,7 @@ namespace vmath_hpp::detail template < typename T > [[nodiscard]] std::size_t hash(const qua& q) noexcept { - return fold_join(hash_combiner{}, std::size_t{}, q); + return hash(vec{q}); } } @@ -131,7 +131,7 @@ namespace vmath_hpp template < typename To, typename From > [[nodiscard]] constexpr qua cast_to(const qua& q) { - return detail::map_join([](From x){ return cast_to(x); }, q); + return qua(cast_to(vec{q})); } } diff --git a/headers/vmath.hpp/vmath_qua.hpp b/headers/vmath.hpp/vmath_qua.hpp index b316969..7b367d2 100644 --- a/headers/vmath.hpp/vmath_qua.hpp +++ b/headers/vmath.hpp/vmath_qua.hpp @@ -11,57 +11,15 @@ #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 { + class qua final { + public: + vec v{0}; + T s{1}; public: using self_type = qua; - using base_type = detail::qua_base; - public: using component_type = T; using pointer = component_type*; @@ -77,13 +35,26 @@ namespace vmath_hpp 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; + constexpr qua(T vx, T vy, T vz, T s) + : v{vx, vy, vz} + , s{s} {} + + constexpr qua(const vec& v, T s) + : v{v} + , s{s} {} + + constexpr explicit qua(const vec& vs) + : v{vs[0], vs[1], vs[2]} + , s{vs[3]} {} + + constexpr explicit operator vec() const { + return {(*this).v, (*this).s}; + } + void swap(qua& other) noexcept(std::is_nothrow_swappable_v) { for ( std::size_t i = 0; i < size; ++i ) { using std::swap; @@ -114,6 +85,26 @@ namespace vmath_hpp return &(*this)[0]; } + [[nodiscard]] constexpr reference operator[](std::size_t index) noexcept { + switch ( index ) { + default: + case 0: return v.x; + case 1: return v.y; + case 2: return v.z; + case 3: return s; + } + } + + [[nodiscard]] constexpr const_reference operator[](std::size_t index) const noexcept { + switch ( index ) { + default: + case 0: return v.x; + case 1: return v.y; + case 2: return v.z; + case 3: return s; + } + } + [[nodiscard]] constexpr reference at(std::size_t index) { if ( index >= size ) { throw std::out_of_range("qua::at"); @@ -132,10 +123,12 @@ namespace vmath_hpp namespace vmath_hpp { - // qua + // vec template < typename T > - qua(T) -> qua; + vec(const qua&) -> vec; + + // qua template < typename T > qua(T, T, T, T) -> qua; diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 5c83b55..f4ce40f 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -11,153 +11,8 @@ #include "vmath_fun.hpp" #include "vmath_qua.hpp" -namespace vmath_hpp::detail::impl -{ - template < typename A, typename F, std::size_t... Is > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join_impl( - F&& f, - const qua& a, - std::index_sequence - ) -> qua()))> - { - return { f(a[Is])... }; - } - - template < typename A, typename B, typename F, std::size_t... Is > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join_impl( - F&& f, - const qua& a, - const qua& b, - std::index_sequence - ) -> qua(), - std::declval()))> - { - return { f(a[Is], b[Is])... }; - } - - template < typename A, typename B, typename C, typename F, std::size_t... Is > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join_impl( - F&& f, - const qua& a, - const qua& b, - const qua& c, - std::index_sequence - ) -> qua(), - std::declval(), - std::declval()))> - { - return { f(a[Is], b[Is], c[Is])... }; - } - - 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])), ...); - } - - template < typename A, typename F, std::size_t I, std::size_t... Is > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto fold1_join_impl( - F&& f, - const qua& a, - std::index_sequence - ) -> A { - A init = a[I]; - return ((init = f(std::move(init), a[Is])), ...); - } -} - -namespace vmath_hpp::detail -{ - template < typename A, typename F > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join( - F&& f, - const qua& a - ) { - return impl::map_join_impl( - std::forward(f), a, std::make_index_sequence<4>{}); - } - - template < typename A, typename B, typename F > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join( - F&& f, - const qua& a, - const qua& b - ) { - return impl::map_join_impl( - std::forward(f), a, b, std::make_index_sequence<4>{}); - } - - template < typename A, typename B, typename C, typename F > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto map_join( - F&& f, - const qua& a, - const qua& b, - const qua& c - ) { - return impl::map_join_impl( - std::forward(f), a, b, c, std::make_index_sequence<4>{}); - } - - 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>{}); - } - - template < typename A, typename F > - [[nodiscard]] constexpr VMATH_HPP_FORCE_INLINE - auto fold1_join( - F&& f, - const qua& a - ) { - return impl::fold1_join_impl( - std::forward(f), a, std::make_index_sequence<4>{}); - } -} +#include "vmath_vec.hpp" +#include "vmath_vec_fun.hpp" // // Operators @@ -176,14 +31,14 @@ namespace vmath_hpp template < typename T > [[nodiscard]] constexpr qua operator-(const qua& xs) { - return map_join([](T x){ return -x; }, xs); + return qua(-vec{xs}); } // operator+ template < typename T > [[nodiscard]] constexpr qua operator+(const qua& xs, const qua& ys) { - return map_join([](T x, T y){ return x + y; }, xs, ys); + return qua(vec{xs} + vec{ys}); } // operator+= @@ -197,7 +52,7 @@ namespace vmath_hpp template < typename T > [[nodiscard]] constexpr qua operator-(const qua& xs, const qua& ys) { - return map_join([](T x, T y){ return x - y; }, xs, ys); + return qua(vec{xs} - vec{ys}); } // operator-= @@ -211,32 +66,23 @@ namespace vmath_hpp 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); + return vec{xs} == vec{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); + return vec{xs} != vec{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; + return vec{xs} < vec{ys}; + } +} + } } diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index fc61a1c..b3baf6c 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -14,32 +14,6 @@ namespace } TEST_CASE("vmath/qua_fun") { - SUBCASE("Detail") { - STATIC_REQUIRE(map_join([](const int& x){ - return x * 2; - }, qua(1,2,3,4)) == qua(2,4,6,8)); - - STATIC_REQUIRE(map_join([](const int& x, const int& y){ - return x + y; - }, qua(1,2,3,4), qua(2,3,4,5)) == qua(3,5,7,9)); - - STATIC_REQUIRE(map_join([](const int& x, const int& y, const int& z){ - return x + y + z; - }, qua(1,2,3,4), qua(2,3,4,5), qua(3,4,5,6)) == qua(6,9,12,15)); - - STATIC_REQUIRE(fold_join([](int acc, const int& x){ - return acc + x; - }, 0, qua(1,2,3,4)) == 10); - - STATIC_REQUIRE(fold_join([](int acc, const int& x, const int& y){ - return acc + x + y; - }, 0, qua(1,2,3,4), qua(2,3,4,5)) == 24); - - STATIC_REQUIRE(fold1_join([](const int& acc, const int& x){ - return acc + x; - }, qua{1,2,3,4}) == 10); - } - SUBCASE("Operators") { STATIC_REQUIRE(+qua(1,-2,3,-4) == qua(1,-2,3,-4)); STATIC_REQUIRE(-qua(1,-2,3,-4) == qua(-1,2,-3,4)); diff --git a/untests/vmath_qua_tests.cpp b/untests/vmath_qua_tests.cpp index 8456053..d67d1dc 100644 --- a/untests/vmath_qua_tests.cpp +++ b/untests/vmath_qua_tests.cpp @@ -23,35 +23,26 @@ TEST_CASE("vmath/qua") { } 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); + 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{}.v == uapprox3(0.f)); + STATIC_REQUIRE(fqua{}.s == uapprox(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}.v == uapprox3(1.f,2.f,3.f)); + STATIC_REQUIRE(fqua{1,2,3,4}.s == uapprox(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}.v == uapprox3(1.f,2.f,3.f)); + STATIC_REQUIRE(fqua{{1,2,3},4}.s == uapprox(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}}.v == uapprox3(1.f,2.f,3.f)); + STATIC_REQUIRE(fqua{{1,2,3,4}}.s == uapprox(4.f)); } { constexpr fqua q(1,2,3,4); @@ -64,9 +55,9 @@ TEST_CASE("vmath/qua") { 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)); + STATIC_REQUIRE(fqua(float4(1,2,3,4)) == fqua(1,2,3,4)); } } @@ -185,24 +176,20 @@ TEST_CASE("vmath/qua") { 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(qua(1,2,3,4).v == vec(1,2,3)); + STATIC_REQUIRE(qua(1,2,3,4).s == 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); + STATIC_REQUIRE(qua(1,2,3,4)[0] == 1); + STATIC_REQUIRE(qua(1,2,3,4)[1] == 2); + STATIC_REQUIRE(qua(1,2,3,4)[2] == 3); + STATIC_REQUIRE(qua(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)); + qua v; + v.v = vec(1,2,3); + v.s = 4; + REQUIRE(v == qua(1,2,3,4)); } } From 1acbb0dffbbdc826837d256ee565619fb4fb1722 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 01:50:34 +0700 Subject: [PATCH 07/51] qua * *= / /= operators --- headers/vmath.hpp/vmath_qua_fun.hpp | 61 +++++++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 30 ++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index f4ce40f..684734d 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -62,6 +62,67 @@ namespace vmath_hpp return (xs = (xs - ys)); } + // operator* + + template < typename T > + [[nodiscard]] constexpr qua operator*(const qua& xs, T y) { + return qua(vec{xs} * y); + } + + template < typename T > + [[nodiscard]] constexpr qua operator*(T x, const qua& ys) { + return qua(x * vec{ys}); + } + + template < typename T > + [[nodiscard]] constexpr vec operator*(const vec& xs, const qua& ys) { + const vec qv2 = cross(ys.v, xs) * T(2); + return xs + qv2 * ys.s + cross(ys.v, qv2); + } + + template < typename T > + [[nodiscard]] constexpr qua operator*(const qua& xs, const qua& ys) { + return { + cross(ys.v, xs.v) + ys.s * xs.v + xs.s * ys.v, + ys.s * xs.s - dot(ys.v, xs.v)}; + } + + // operator*= + + template < typename T > + constexpr qua& operator*=(qua& xs, T y) { + return (xs = (xs * y)); + } + + template < typename T > + constexpr vec& operator*=(vec& xs, const qua& ys) { + return (xs = (xs * ys)); + } + + template < typename T > + constexpr qua& operator*=(qua& xs, const qua& ys) { + return (xs = (xs * ys)); + } + + // operator/ + + template < typename T > + [[nodiscard]] constexpr qua operator/(const qua& xs, T y) { + return qua(vec{xs} / y); + } + + template < typename T > + [[nodiscard]] constexpr qua operator/(T x, const qua& ys) { + return qua(x / vec{ys}); + } + + // operator/= + + template < typename T > + constexpr qua& operator/=(qua& xs, T y) { + return (xs = (xs / y)); + } + // operator== template < typename T > diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index b3baf6c..fccd77c 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -32,5 +32,35 @@ TEST_CASE("vmath/qua_fun") { REQUIRE(&v == &(v -= qua{3,4,5,6})); REQUIRE(v == qua{-2,-2,-2,-2}); } + + STATIC_REQUIRE(qua(1,2,3,4) * 2 == qua(2,4,6,8)); + STATIC_REQUIRE(qua(2,4,6,8) / 2 == qua(1,2,3,4)); + + STATIC_REQUIRE(2 * qua(1,2,3,4) == qua(2,4,6,8)); + STATIC_REQUIRE(8 / qua(1,2,4,8) == qua(8,4,2,1)); + + { + qua v{1,2,3,4}; + REQUIRE(&v == &(v *= 2)); + REQUIRE(v == qua{2,4,6,8}); + REQUIRE(&v == &(v *= qua{})); + REQUIRE(v == qua{2,4,6,8}); + } + + { + qua v{2,4,6,8}; + REQUIRE(&v == &(v /= 2)); + REQUIRE(v == qua{1,2,3,4}); + } + + { + float3 v{1,0,0}; + REQUIRE(&v == &(v *= fqua{0,0,0.7071067812f,0.7071067812f})); + REQUIRE(v == uapprox3(0.f,1.f,0.f)); + } + + STATIC_REQUIRE(fqua{} * fqua{} == fqua{}); + STATIC_REQUIRE(float3{1,2,3} * fqua{} == uapprox3(1.f,2.f,3.f)); + STATIC_REQUIRE(float3{1,0,0} * fqua{0,0,0.7071067812f,0.7071067812f} == uapprox3(0.f,1.f,0.f)); } } From 341e3dd0a3750e6fb3a4b32088ef9bb146f30497 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 01:54:20 +0700 Subject: [PATCH 08/51] qua: isnan, isinf, isfinite --- headers/vmath.hpp/vmath_qua_fun.hpp | 22 ++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 684734d..4a09b99 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -145,5 +145,27 @@ namespace vmath_hpp } } +// +// Common Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] vec isnan(const qua& xs) { + return isnan(vec{xs}); + } + + template < typename T > + [[nodiscard]] vec isinf(const qua& xs) { + return isinf(vec{xs}); + } + + template < typename T > + [[nodiscard]] vec isfinite(const qua& xs) { + return isfinite(vec{xs}); + } +} + } } diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index fccd77c..d4b2737 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -63,4 +63,10 @@ TEST_CASE("vmath/qua_fun") { STATIC_REQUIRE(float3{1,2,3} * fqua{} == uapprox3(1.f,2.f,3.f)); STATIC_REQUIRE(float3{1,0,0} * fqua{0,0,0.7071067812f,0.7071067812f} == uapprox3(0.f,1.f,0.f)); } + + SUBCASE("Common Functions") { + REQUIRE_FALSE(any(isnan(fqua(1,1,1,1)))); + REQUIRE_FALSE(any(isinf(fqua(1,1,1,1)))); + REQUIRE(all(isfinite(fqua(1,1,1,1)))); + } } From 4a8c69e4f4b1d814803bcf61da0defc634cbc737 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 01:57:39 +0700 Subject: [PATCH 09/51] qua: dot, length, length2, normalize --- headers/vmath.hpp/vmath_qua_fun.hpp | 24 ++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 12 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 4a09b99..41149aa 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -167,5 +167,29 @@ namespace vmath_hpp } } +// +// Geometric Functions +// + +namespace vmath_hpp +{ + template < typename T > + [[nodiscard]] constexpr T dot(const qua& xs, const qua& ys) { + return dot(vec{xs}, vec{ys}); + } + + template < typename T > + [[nodiscard]] T length(const qua& xs) { + return length(vec{xs}); + } + + template < typename T > + [[nodiscard]] constexpr T length2(const qua& xs) { + return length2(vec{xs}); + } + + template < typename T > + [[nodiscard]] constexpr qua normalize(const qua& xs) { + return qua(normalize(vec{xs})); } } diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index d4b2737..2b085de 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -69,4 +69,16 @@ TEST_CASE("vmath/qua_fun") { REQUIRE_FALSE(any(isinf(fqua(1,1,1,1)))); REQUIRE(all(isfinite(fqua(1,1,1,1)))); } + + SUBCASE("Geometric Functions") { + STATIC_REQUIRE(dot(qua(1,2,3,4),qua(3,4,5,6)) == 50); + + REQUIRE(length(fqua(10.f,0.f,0.f,0.f)) == uapprox(10.f)); + REQUIRE(length(fqua(-10.f,0.f,0.f,0.f)) == uapprox(10.f)); + + STATIC_REQUIRE(length2(fqua(10.f,0.f,0.f,0.f)) == uapprox(100.f)); + STATIC_REQUIRE(length2(fqua(-10.f,0.f,0.f,0.f)) == uapprox(100.f)); + + REQUIRE(normalize(fqua(0.5f,0.f,0.f,0.f)).v == uapprox3(1.f,0.f,0.f)); + } } From 42795525223161f52addda6a77177db0b6be59a7 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 01:59:17 +0700 Subject: [PATCH 10/51] qua: approx, equal_to, not_equal_to --- headers/vmath.hpp/vmath_qua_fun.hpp | 33 +++++++++++++++++++++++++++++ untests/vmath_qua_fun_tests.cpp | 10 +++++++++ 2 files changed, 43 insertions(+) diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 41149aa..2c3fa4d 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -193,3 +193,36 @@ namespace vmath_hpp return qua(normalize(vec{xs})); } } + +// +// Relational Functions +// + +namespace vmath_hpp +{ + // approx + + template < typename T > + [[nodiscard]] constexpr vec approx(const qua& xs, const qua& ys) { + return approx(vec{xs}, vec{ys}); + } + + template < typename T > + [[nodiscard]] constexpr vec approx(const qua& xs, const qua& ys, T epsilon) { + return approx(vec{xs}, vec{ys}, epsilon); + } + + // equal_to + + template < typename T > + [[nodiscard]] constexpr vec equal_to(const qua& xs, const qua& ys) { + return equal_to(vec{xs}, vec{ys}); + } + + // not_equal_to + + template < typename T > + [[nodiscard]] constexpr vec not_equal_to(const qua& xs, const qua& ys) { + return not_equal_to(vec{xs}, vec{ys}); + } +} diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index 2b085de..ed3675d 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -81,4 +81,14 @@ TEST_CASE("vmath/qua_fun") { REQUIRE(normalize(fqua(0.5f,0.f,0.f,0.f)).v == uapprox3(1.f,0.f,0.f)); } + + SUBCASE("Relational Functions") { + STATIC_REQUIRE(approx(qua(1,1,1,1), qua(0,1,2,3)) == bool4(false, true, false, false)); + STATIC_REQUIRE(approx(qua(1,1,1,1), qua(0,1,2,3), 0) == bool4(false, true, false, false)); + STATIC_REQUIRE(approx(qua(1,1,1,1), qua(0,1,2,3), 1) == bool4(true, true, true, false)); + STATIC_REQUIRE(approx(qua(1,1,1,1), qua(0,1,2,3), 2) == bool4(true, true, true, true)); + + STATIC_REQUIRE(equal_to(qua(1,1,1,1), qua(0,1,2,3)) == bool4(false, true, false, false)); + STATIC_REQUIRE(not_equal_to(qua(1,1,1,1), qua(0,1,2,3)) == bool4(true, false, true, true)); + } } From 1d5ee209670fa1c1df611563529944592aa476ac Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 02:06:46 +0700 Subject: [PATCH 11/51] basic qua ext rotation functions --- headers/vmath.hpp/vmath_ext.hpp | 54 +++++++++++++++++++++++++++++ headers/vmath.hpp/vmath_qua_fun.hpp | 21 +++++++++++ untests/vmath_ext_tests.cpp | 24 +++++++++++++ untests/vmath_qua_fun_tests.cpp | 9 +++++ 4 files changed, 108 insertions(+) diff --git a/headers/vmath.hpp/vmath_ext.hpp b/headers/vmath.hpp/vmath_ext.hpp index 64c30aa..aeae410 100644 --- a/headers/vmath.hpp/vmath_ext.hpp +++ b/headers/vmath.hpp/vmath_ext.hpp @@ -662,3 +662,57 @@ namespace vmath_hpp return dot(v, normal) / length2(normal) * normal; } } + +// +// Quaternion Transform +// + +namespace vmath_hpp +{ + // qrotate + + template < typename T > + [[nodiscard]] qua qrotate(T angle, const vec& axis) { + const auto [s, c] = sincos(angle * T(0.5)); + const auto [x, y, z] = normalize(axis); + return {vec{x,y,z} * s, c}; + } + + template < typename T > + [[nodiscard]] qua qrotate(const qua& q, T angle, const vec& axis) { + return q * qrotate(angle, axis); + } + + template < typename T > + [[nodiscard]] qua qrotate_x(T angle) { + const auto [s, c] = sincos(angle * T(0.5)); + return {s, T(0), T(0), c}; + } + + template < typename T > + [[nodiscard]] qua qrotate_x(const qua& q, T angle) { + return qrotate(q, angle, unit3_x); + } + + template < typename T > + [[nodiscard]] qua qrotate_y(T angle) { + const auto [s, c] = sincos(angle * T(0.5)); + return {T(0), s, T(0), c}; + } + + template < typename T > + [[nodiscard]] qua qrotate_y(const qua& q, T angle) { + return qrotate(q, angle, unit3_y); + } + + template < typename T > + [[nodiscard]] qua qrotate_z(T angle) { + const auto [s, c] = sincos(angle * T(0.5)); + return {T(0), T(0), s, c}; + } + + template < typename T > + [[nodiscard]] qua qrotate_z(const qua& q, T angle) { + return qrotate(q, angle, unit3_z); + } +} diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index 2c3fa4d..cc679f3 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -226,3 +226,24 @@ namespace vmath_hpp return not_equal_to(vec{xs}, vec{ys}); } } + +// +// Quaternion Functions +// + +namespace vmath_hpp +{ + // conjugate + + template < typename T > + [[nodiscard]] constexpr qua conjugate(const qua& q) { + return {-q.v, q.s}; + } + + // inverse + + template < typename T > + [[nodiscard]] constexpr qua inverse(const qua& q) { + return conjugate(q) * rcp(dot(q, q)); + } +} diff --git a/untests/vmath_ext_tests.cpp b/untests/vmath_ext_tests.cpp index 47014c0..8a98209 100644 --- a/untests/vmath_ext_tests.cpp +++ b/untests/vmath_ext_tests.cpp @@ -293,4 +293,28 @@ TEST_CASE("vmath/ext") { REQUIRE(project(float2(2.f, 2.f), float2(0.f, 1.f)) == uapprox2(0.f, 2.f)); REQUIRE(project(float3(2.f, 2.f, 2.f), float3(0.f, 0.f, 1.f)) == uapprox3(0.f, 0.f, 2.f)); } + + SUBCASE("quaternion qrotate") { + constexpr float pi = radians(180.f); + constexpr float pi_2 = radians(90.f); + constexpr float pi_4 = radians(45.f); + + REQUIRE(float3(0.f,1.f,0.f) * qrotate_x(pi_2) == uapprox3(0.f,0.f,1.f)); + REQUIRE(float3(0.f,0.f,1.f) * qrotate_y(pi_2) == uapprox3(1.f,0.f,0.f)); + REQUIRE(float3(1.f,0.f,0.f) * qrotate_z(pi_2) == uapprox3(0.f,1.f,0.f)); + + REQUIRE(float3(0.f,1.f,0.f) * qrotate_x(qrotate_x(pi_4),pi_4) == uapprox3(0.f,0.f,1.f)); + REQUIRE(float3(0.f,0.f,1.f) * qrotate_y(qrotate_y(pi_4),pi_4) == uapprox3(1.f,0.f,0.f)); + REQUIRE(float3(1.f,0.f,0.f) * qrotate_z(qrotate_z(pi_4),pi_4) == uapprox3(0.f,1.f,0.f)); + + REQUIRE(float3(2.f,3.f,4.f) * qrotate(pi,{0.f,0.f,1.f}) == uapprox3(-2.f,-3.f,4.f)); + REQUIRE(float3(2.f,3.f,4.f) * qrotate(pi,float3{0.f,0.f,1.f}) == uapprox3(-2.f,-3.f,4.f)); + + REQUIRE(float3(2.f,3.f,4.f) * qrotate(qrotate(pi_2,{0.f,0.f,1.f}),pi_2,{0.f,0.f,1.f}) == uapprox3(-2.f,-3.f,4.f)); + REQUIRE(float3(2.f,3.f,4.f) * qrotate(qrotate(pi_2,float3{0.f,0.f,1.f}),pi_2,float3{0.f,0.f,1.f}) == uapprox3(-2.f,-3.f,4.f)); + + REQUIRE(qrotate_x(12.3f) == qrotate(12.3f, unit3_x * 2.f)); + REQUIRE(qrotate_y(12.3f) == qrotate(12.3f, unit3_y * 2.f)); + REQUIRE(qrotate_z(12.3f) == qrotate(12.3f, unit3_z * 2.f)); + } } diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index ed3675d..4036ee2 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -91,4 +91,13 @@ TEST_CASE("vmath/qua_fun") { STATIC_REQUIRE(equal_to(qua(1,1,1,1), qua(0,1,2,3)) == bool4(false, true, false, false)); STATIC_REQUIRE(not_equal_to(qua(1,1,1,1), qua(0,1,2,3)) == bool4(true, false, true, true)); } + + SUBCASE("Quaternion Functions") { + STATIC_REQUIRE(conjugate(qua(1,2,3,4)) == qua(-1,-2,-3,4)); + STATIC_REQUIRE(inverse(qua(0.f,0.f,0.7071067812f,0.7071067812f)).v == uapprox3(0.f,0.f,-0.7071067812f)); + STATIC_REQUIRE(inverse(qua(0.f,0.f,0.7071067812f,0.7071067812f)).s == uapprox(0.7071067812f)); + + REQUIRE(inverse(qrotate_x(10.f)) == qrotate_x(-10.f)); + REQUIRE(all(approx(inverse(qrotate_x(10.f) * qrotate_y(15.f)), qrotate_y(-15.f) * qrotate_x(-10.f)))); + } } From c440789bee0481f35ee4e9dd2dbd12cea97668fc Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 02:26:48 +0700 Subject: [PATCH 12/51] rotate vec4 by qua --- headers/vmath.hpp/vmath_ext.hpp | 18 ++++++++++++++---- headers/vmath.hpp/vmath_qua_fun.hpp | 10 ++++++++++ untests/vmath_ext_tests.cpp | 3 +++ untests/vmath_qua_fun_tests.cpp | 8 ++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/headers/vmath.hpp/vmath_ext.hpp b/headers/vmath.hpp/vmath_ext.hpp index aeae410..dce10f1 100644 --- a/headers/vmath.hpp/vmath_ext.hpp +++ b/headers/vmath.hpp/vmath_ext.hpp @@ -646,13 +646,23 @@ namespace vmath_hpp } template < typename T > - [[nodiscard]] vec rotate(const vec& v, T angle, const vec& normal) { - return v * mat(rotate(angle, normal)); + [[nodiscard]] vec rotate(const vec& v, const qua& q) { + return v * q; } template < typename T > - [[nodiscard]] vec rotate(const vec& v, T angle, const vec& normal) { - return v * rotate(angle, normal); + [[nodiscard]] vec rotate(const vec& v, T angle, const vec& axis) { + return v * qrotate(angle, axis); + } + + template < typename T > + [[nodiscard]] vec rotate(const vec& v, const qua& q) { + return v * q; + } + + template < typename T > + [[nodiscard]] vec rotate(const vec& v, T angle, const vec& axis) { + return v * qrotate(angle, axis); } // project diff --git a/headers/vmath.hpp/vmath_qua_fun.hpp b/headers/vmath.hpp/vmath_qua_fun.hpp index cc679f3..f57650d 100644 --- a/headers/vmath.hpp/vmath_qua_fun.hpp +++ b/headers/vmath.hpp/vmath_qua_fun.hpp @@ -80,6 +80,11 @@ namespace vmath_hpp return xs + qv2 * ys.s + cross(ys.v, qv2); } + template < typename T > + [[nodiscard]] constexpr vec operator*(const vec& xs, const qua& ys) { + return {vec{xs} * ys, xs.w}; + } + template < typename T > [[nodiscard]] constexpr qua operator*(const qua& xs, const qua& ys) { return { @@ -99,6 +104,11 @@ namespace vmath_hpp return (xs = (xs * ys)); } + template < typename T > + constexpr vec& operator*=(vec& xs, const qua& ys) { + return (xs = (xs * ys)); + } + template < typename T > constexpr qua& operator*=(qua& xs, const qua& ys) { return (xs = (xs * ys)); diff --git a/untests/vmath_ext_tests.cpp b/untests/vmath_ext_tests.cpp index 8a98209..974b538 100644 --- a/untests/vmath_ext_tests.cpp +++ b/untests/vmath_ext_tests.cpp @@ -285,7 +285,10 @@ TEST_CASE("vmath/ext") { REQUIRE(rotate(float2(2.f,0.f), radians(90.f)) == uapprox2(0.f,2.f)); REQUIRE(rotate(float2(1.5f,0.f), radians(-90.f)) == uapprox2(0.f,-1.5f)); + REQUIRE(rotate(float3(1.5f,0.f,0.f), qrotate_z(radians(90.f))) == uapprox3(0.f,1.5f,0.f)); REQUIRE(rotate(float3(1.5f,0.f,0.f), radians(90.f), float3(0,0,1)) == uapprox3(0.f,1.5f,0.f)); + + REQUIRE(rotate(float4(1.5f,0.f,0.f,1.f), qrotate_z(radians(90.f))) == uapprox4(0.f,1.5f,0.f,1.f)); REQUIRE(rotate(float4(1.5f,0.f,0.f,1.f), radians(90.f), float3(0,0,1)) == uapprox4(0.f,1.5f,0.f,1.f)); } diff --git a/untests/vmath_qua_fun_tests.cpp b/untests/vmath_qua_fun_tests.cpp index 4036ee2..c0b83f3 100644 --- a/untests/vmath_qua_fun_tests.cpp +++ b/untests/vmath_qua_fun_tests.cpp @@ -59,9 +59,17 @@ TEST_CASE("vmath/qua_fun") { REQUIRE(v == uapprox3(0.f,1.f,0.f)); } + { + float4 v{1,0,0,2}; + REQUIRE(&v == &(v *= fqua{0,0,0.7071067812f,0.7071067812f})); + REQUIRE(v == uapprox4(0.f,1.f,0.f,2.f)); + } + STATIC_REQUIRE(fqua{} * fqua{} == fqua{}); STATIC_REQUIRE(float3{1,2,3} * fqua{} == uapprox3(1.f,2.f,3.f)); + STATIC_REQUIRE(float4{1,2,3,4} * fqua{} == uapprox4(1.f,2.f,3.f,4.f)); STATIC_REQUIRE(float3{1,0,0} * fqua{0,0,0.7071067812f,0.7071067812f} == uapprox3(0.f,1.f,0.f)); + STATIC_REQUIRE(float4{1,0,0,2} * fqua{0,0,0.7071067812f,0.7071067812f} == uapprox4(0.f,1.f,0.f,2.f)); } SUBCASE("Common Functions") { From ecee465511992a69c2de44eda6a98d1954a77da1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 26 Jan 2021 20:57:19 +0700 Subject: [PATCH 13/51] back to catch --- untests/catch/catch.cpp | 2 + untests/catch/catch.hpp | 17881 ++++++++++++++++++++++++++++++ untests/doctest/doctest.cpp | 2 - untests/doctest/doctest.h | 6260 ----------- untests/doctest/doctest.hpp | 11 - untests/vmath_ext_tests.cpp | 36 +- untests/vmath_fun_tests.cpp | 12 +- untests/vmath_mat_fun_tests.cpp | 14 +- untests/vmath_mat_tests.cpp | 24 +- untests/vmath_qua_fun_tests.cpp | 12 +- untests/vmath_qua_tests.cpp | 24 +- untests/vmath_vec_fun_tests.cpp | 16 +- untests/vmath_vec_tests.cpp | 24 +- 13 files changed, 17964 insertions(+), 6354 deletions(-) create mode 100644 untests/catch/catch.cpp create mode 100644 untests/catch/catch.hpp delete mode 100644 untests/doctest/doctest.cpp delete mode 100644 untests/doctest/doctest.h delete mode 100644 untests/doctest/doctest.hpp diff --git a/untests/catch/catch.cpp b/untests/catch/catch.cpp new file mode 100644 index 0000000..0c7c351 --- /dev/null +++ b/untests/catch/catch.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/untests/catch/catch.hpp b/untests/catch/catch.hpp new file mode 100644 index 0000000..0384171 --- /dev/null +++ b/untests/catch/catch.hpp @@ -0,0 +1,17881 @@ +/* + * Catch v2.13.4 + * Generated: 2020-12-29 14:48:00.116107 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 4 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template