From c52d39e44068ddf4384961ea10b70f959cf165e8 Mon Sep 17 00:00:00 2001 From: Christopher Kormanyos Date: Tue, 13 Feb 2024 08:44:03 +0100 Subject: [PATCH 1/2] Achieve more octonion coverage --- include/boost/math/octonion.hpp | 6 +++- test/octonion_test_simple.cpp | 52 +++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/include/boost/math/octonion.hpp b/include/boost/math/octonion.hpp index 09a01c3cb8..5d147df584 100644 --- a/include/boost/math/octonion.hpp +++ b/include/boost/math/octonion.hpp @@ -174,6 +174,10 @@ namespace boost type g; \ type h; \ + // LCOV_EXCL_START + // No code coverage for the generic case, like std::complex, + // the The behavior of octonion is unspecified if T is not + // one of float, double or long double. template class octonion @@ -658,7 +662,7 @@ namespace boost private: }; - + // LCOV_EXCL_STOP // declaration of octonion specialization diff --git a/test/octonion_test_simple.cpp b/test/octonion_test_simple.cpp index aa93dacebf..8cc78ad801 100644 --- a/test/octonion_test_simple.cpp +++ b/test/octonion_test_simple.cpp @@ -1,8 +1,8 @@ // Copyright Hubert Holin 2001. // Copyright Christopher Kormanyos 2024 -// 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) +// 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) #include @@ -361,7 +361,7 @@ void octonion_original_manual_test() } template -void exp_test() +void elem_func_test() { using ::std::numeric_limits; @@ -382,6 +382,46 @@ void exp_test() BOOST_TEST(result_exp_toto_is_ok); } + + { + const std::complex one_sixteenth_one_over_32 { T { 1 } / 16, T { 1 } / 32 }; + const ::boost::math::octonion + octo_small + { + one_sixteenth_one_over_32, + T { 2 } * one_sixteenth_one_over_32, + T { 3 } * one_sixteenth_one_over_32, + T { 4 } * one_sixteenth_one_over_32 + }; + + const auto octo_small_exp = exp(octo_small); + + const auto r0 = octo_small_exp.real(); + + using std::exp; + + BOOST_TEST(r0 < exp(one_sixteenth_one_over_32.real())); + } + + { + const std::complex one_sixteenth_one_over_32 { T { 1 } / 16, T { 1 } / 32 }; + const ::boost::math::octonion + octo_small + { + one_sixteenth_one_over_32, + T { 2 } * one_sixteenth_one_over_32, + T { 3 } * one_sixteenth_one_over_32, + T { 4 } * one_sixteenth_one_over_32 + }; + + const auto octo_small_sin = sin(octo_small); + + const auto r0 = octo_small_sin.real(); + + using std::sin; + + BOOST_TEST(r0 > sin(one_sixteenth_one_over_32.real())); + } } auto main() -> int @@ -391,8 +431,8 @@ auto main() -> int division_test(); - exp_test(); - exp_test(); + elem_func_test(); + elem_func_test(); octonion_original_manual_test(); From 77da18566eabdc18a029f169eedd67ea9100ca4a Mon Sep 17 00:00:00 2001 From: Christopher Kormanyos Date: Tue, 13 Feb 2024 10:04:28 +0100 Subject: [PATCH 2/2] Add more octonion tests --- test/octonion_test_simple.cpp | 244 +++++++++++++++++++++++++++++----- 1 file changed, 209 insertions(+), 35 deletions(-) diff --git a/test/octonion_test_simple.cpp b/test/octonion_test_simple.cpp index 8cc78ad801..53a9fc3d9e 100644 --- a/test/octonion_test_simple.cpp +++ b/test/octonion_test_simple.cpp @@ -4,47 +4,95 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include - #include #include #include +#include +#include + // test file for octonion.hpp -namespace +namespace local { - template - ::boost::math::octonion index_i_element(int idx) + std::mt19937 eng(static_cast(UINT8_C(42))); + std::uniform_int_distribution dst_one(1, 1); + + template + auto is_close_fraction(const NumericType& a, + const NumericType& b, + const NumericType& tol) noexcept -> bool + { + using std::fabs; + + auto result_is_ok = bool { }; + + if(b == static_cast(0)) { - return( - ::boost::math::octonion( - (idx == 0) ? - static_cast(1) : - static_cast(0), - (idx == 1) ? - static_cast(1) : - static_cast(0), - (idx == 2) ? - static_cast(1) : - static_cast(0), - (idx == 3) ? - static_cast(1) : - static_cast(0), - (idx == 4) ? - static_cast(1) : - static_cast(0), - (idx == 5) ? - static_cast(1) : - static_cast(0), - (idx == 6) ? - static_cast(1) : - static_cast(0), - (idx == 7) ? - static_cast(1) : - static_cast(0) - )); + result_is_ok = (fabs(a - b) < tol); } + else + { + const auto delta = fabs(1 - (a / b)); + + result_is_ok = (delta < tol); + } + + return result_is_ok; + } + + template + auto is_close_fraction(const ::boost::math::octonion& a, + const ::boost::math::octonion& b, + const T& tol) noexcept -> bool + { + using std::fabs; + + bool result_is_ok { true }; + + result_is_ok = (is_close_fraction(a.R_component_1(), b.R_component_1(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_2(), b.R_component_2(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_3(), b.R_component_3(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_4(), b.R_component_4(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_5(), b.R_component_5(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_6(), b.R_component_6(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_7(), b.R_component_7(), tol) && result_is_ok); + result_is_ok = (is_close_fraction(a.R_component_8(), b.R_component_8(), tol) && result_is_ok); + + return result_is_ok; + } + + template + ::boost::math::octonion index_i_element(int idx) + { + return( + ::boost::math::octonion( + (idx == 0) ? + static_cast(1) : + static_cast(0), + (idx == 1) ? + static_cast(1) : + static_cast(0), + (idx == 2) ? + static_cast(1) : + static_cast(0), + (idx == 3) ? + static_cast(1) : + static_cast(0), + (idx == 4) ? + static_cast(1) : + static_cast(0), + (idx == 5) ? + static_cast(1) : + static_cast(0), + (idx == 6) ? + static_cast(1) : + static_cast(0), + (idx == 7) ? + static_cast(1) : + static_cast(0) + )); + } } template @@ -66,7 +114,7 @@ void multiplication_test() for (int idx = 1; idx < 8; ++idx) { - ::boost::math::octonion toto = index_i_element(idx); + ::boost::math::octonion toto = local::index_i_element(idx); const T tabs { abs(toto*toto+static_cast(1)) }; @@ -86,9 +134,13 @@ void multiplication_test() BOOST_TEST(prod == ctrl); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) { const boost::math::octonion lhs(T(1),T(2),T(3),T(4),T(5),T(6),T(7),T(8)); - const boost::math::octonion rhs(T(1),T(1),T(1),T(1),T(1),T(1),T(1),T(1)); + + const boost::math::octonion rhs = + boost::math::octonion { T(1),T(1),T(1),T(1),T(1),T(1),T(1),T(1) } + * static_cast(local::dst_one(local::eng)); const boost::math::octonion quot = lhs / rhs; @@ -374,7 +426,7 @@ void elem_func_test() for(int idx = 1; idx < 8; ++idx) { ::boost::math::octonion toto = - static_cast(4)*atan(static_cast(1))*index_i_element(idx); + static_cast(4)*atan(static_cast(1)) * local::index_i_element(idx); const T tabs { abs(exp(toto)+static_cast(1)) }; @@ -401,6 +453,41 @@ void elem_func_test() using std::exp; BOOST_TEST(r0 < exp(one_sixteenth_one_over_32.real())); + + { + auto octo_small_exp_inv = ::boost::math::octonion(1); + octo_small_exp_inv /= octo_small_exp; + + const auto value_cosh = cosh(octo_small); + const auto value_cosh_ctrl = (octo_small_exp + octo_small_exp_inv) / T(2); + + const auto result_cosh_is_ok = local::is_close_fraction(value_cosh, value_cosh_ctrl, std::numeric_limits::epsilon() * 64); + + BOOST_TEST(result_cosh_is_ok); + } + + { + auto octo_small_exp_inv = ::boost::math::octonion(1); + octo_small_exp_inv /= octo_small_exp; + + const auto value_sinh = sinh(octo_small); + const auto value_sinh_ctrl = (octo_small_exp - octo_small_exp_inv) / T(2); + + const auto result_sinh_is_ok = local::is_close_fraction(value_sinh, value_sinh_ctrl, std::numeric_limits::epsilon() * 64); + + BOOST_TEST(result_sinh_is_ok); + } + + { + const auto value_sinh = sinh(octo_small); + const auto value_cosh = cosh(octo_small); + const auto value_tanh = tanh(octo_small); + const auto value_tanh_ctrl = value_sinh / value_cosh; + + const auto result_tanh_is_ok = local::is_close_fraction(value_tanh, value_tanh_ctrl, std::numeric_limits::epsilon() * 64); + + BOOST_TEST(result_tanh_is_ok); + } } { @@ -422,6 +509,93 @@ void elem_func_test() BOOST_TEST(r0 > sin(one_sixteenth_one_over_32.real())); } + + { + const std::complex one_sixteenth_one_over_32 { T { 1 } / 16, T { 1 } / 32 }; + const ::boost::math::octonion + octo_small + { + one_sixteenth_one_over_32, + T { 2 } * one_sixteenth_one_over_32, + T { 3 } * one_sixteenth_one_over_32, + T { 4 } * one_sixteenth_one_over_32 + }; + + const auto octo_small_cos = cos(octo_small); + + const auto r0 = octo_small_cos.real(); + + using std::cos; + + BOOST_TEST(r0 > cos(one_sixteenth_one_over_32.real())); + } + + { + const std::complex one_sixteenth_one_over_32 { T { 1 } / 16, T { 1 } / 32 }; + const ::boost::math::octonion + octo_small + { + one_sixteenth_one_over_32, + T { 2 } * one_sixteenth_one_over_32, + T { 3 } * one_sixteenth_one_over_32, + T { 4 } * one_sixteenth_one_over_32 + }; + + const auto octo_small_sin = sin(octo_small); + const auto octo_small_cos = cos(octo_small); + const auto octo_small_tan = tan(octo_small); + + const auto octo_small_tan_ctrl = octo_small_sin / octo_small_cos; + + const auto result_tan_is_ok = local::is_close_fraction(octo_small_tan, octo_small_tan_ctrl, std::numeric_limits::epsilon() * 64); + + BOOST_TEST(result_tan_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + ::boost::math::octonion b { T {1}, T {2}, T {3}, T {4}, T {5}, T {6}, T {7}, T {8} }; + + b *= static_cast(local::dst_one(local::eng)); + + { + ::boost::math::octonion bp0 = pow(b, 0); + ::boost::math::octonion bp0_ctrl { T { 1 } }; + + const auto result_b0_is_ok = local::is_close_fraction(bp0, bp0_ctrl, std::numeric_limits::epsilon() * 64); + } + + { + ::boost::math::octonion bp1 = pow(b, 1); + ::boost::math::octonion bp1_ctrl = b; + + const auto result_b1_is_ok = local::is_close_fraction(bp1, bp1_ctrl, std::numeric_limits::epsilon() * 64); + } + + { + ::boost::math::octonion bp2 = pow(b, 2); + ::boost::math::octonion bp2_ctrl = b * b; + + const auto result_b2_is_ok = local::is_close_fraction(bp2, bp2_ctrl, std::numeric_limits::epsilon() * 64); + } + + { + ::boost::math::octonion bp3 = pow(b, 3); + ::boost::math::octonion bp3_ctrl = (b * b) * b; + + const auto result_b3_is_ok = local::is_close_fraction(bp3, bp3_ctrl, std::numeric_limits::epsilon() * 64); + } + + { + ::boost::math::octonion bp4 = pow(b, 4); + ::boost::math::octonion bp2_ctrl = b * b; + ::boost::math::octonion bp4_ctrl = bp2_ctrl * bp2_ctrl; + + const auto result_b3_is_ok = local::is_close_fraction(bp4, bp4_ctrl, std::numeric_limits::epsilon() * 64); + } + } } auto main() -> int