// 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) #include #include #include #include #include // test file for octonion.hpp namespace local { 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)) { 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 void multiplication_test() { using ::std::numeric_limits; using ::boost::math::abs; // Testing multiplication. const auto one_by_one = ::boost::math::octonion(1,0,0,0,0,0,0,0) * ::boost::math::octonion(1,0,0,0,0,0,0,0); const T delta { abs(one_by_one - static_cast(1)) }; const auto result_mul_one_is_ok = (delta < numeric_limits::epsilon()); BOOST_TEST(result_mul_one_is_ok); for (int idx = 1; idx < 8; ++idx) { ::boost::math::octonion toto = local::index_i_element(idx); const T tabs { abs(toto*toto+static_cast(1)) }; const auto result_mul_toto_is_ok = (tabs < numeric_limits::epsilon()); BOOST_TEST(result_mul_toto_is_ok); } { 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(8),T(7),T(6),T(5),T(4),T(3),T(2),T(1)); const boost::math::octonion prod = lhs * rhs; const boost::math::octonion ctrl(T(-104), T(14), T(12), T(10), T(152), T(42), T(4), T(74)); 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 = 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; const boost::math::octonion ctrl(T(4.5), T(0.25), T(0.5), T(0.75), T(-1), T(0.75), T(1.5), T(0.75)); BOOST_TEST(quot == ctrl); } } template void division_test() { { 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 quot = lhs / rhs; const boost::math::octonion ctrl(T(4.5), T(0.25), T(0.5), T(0.75), T(-1), T(0.75), T(1.5), T(0.75)); BOOST_TEST(quot == ctrl); } { const std::complex one_one(T(1), T(1)); 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(one_one, one_one, one_one, one_one); const boost::math::octonion quot = lhs / rhs; const boost::math::octonion ctrl(T(4.5), T(0.25), T(0.5), T(0.75), T(-1), T(0.75), T(1.5), T(0.75)); BOOST_TEST(quot == ctrl); } } void octonion_original_manual_test() { // tests for evaluation by humans // using default constructor ::boost::math::octonion o0; ::boost::math::octonion oa[2]; // using constructor "O seen as R^8" ::boost::math::octonion o1(1,2,3,4,5,6,7,8); ::std::complex c0(9,10); // using constructor "O seen as C^4" ::boost::math::octonion o2(c0); ::boost::math::quaternion q0(11,12,13,14); // using constructor "O seen as H^2" ::boost::math::octonion o3(q0); // using UNtemplated copy constructor ::boost::math::octonion o4(o1); // using templated copy constructor ::boost::math::octonion o5(o2); // using UNtemplated assignment operator o5 = o3; oa[0] = o0; // using templated assignment operator o5 = o2; oa[1] = o5; float f0(15); // using converting assignment operator o0 = f0; // using converting assignment operator o2 = c0; // using converting assignment operator o5 = q0; // using += (const T &) o4 += f0; // using == (const octonion &,const octonion &) BOOST_TEST(o0 != o4); // using += (const ::std::complex &) o2 += c0; // using == (const ::boost::math::quaternion &, const octonion &) BOOST_TEST(q0 == o3); // using += (const ::boost::math::quaternion &) o3 += q0; BOOST_TEST(2 * q0 == o3); // using += (const quaternion &) o5 += o4; // using -= (const T &) o1 -= f0; // using -= (const ::std::complex &) o2 -= c0; // using -= (const ::boost::math::quaternion &) o5 -= q0; // using -= (const octonion &) o3 -= o4; // using == (const ::std::complex &, const octonion &) BOOST_TEST(c0 == o2); // using == (const octonion &, const ::std::complex &) BOOST_TEST(o2 == c0); double d0(16); ::std::complex c1(17,18); ::boost::math::quaternion q1(19,20,21,22); // using *= (const T &) o2 *= d0; // using *= (const ::std::complex &) o2 *= c1; // using *= (const ::boost::math::quaternion &) o2 *= q1; // using *= (const octonion &) o2 *= o4; long double l0(23); ::std::complex c2(24,25); // using /= (const T &) o5 /= l0; // using /= (const ::std::complex &) o5 /= c2; // using /= (const quaternion &) o5 /= q0; // using /= (const octonion &) o5 /= o5; // using + (const T &, const octonion &) ::boost::math::octonion o6 = f0+o0; // using + (const octonion &, const T &) ::boost::math::octonion o7 = o0+f0; // using + (const ::std::complex &, const quaternion &) ::boost::math::octonion o8 = c0+o2; // using + (const octonion &, const ::std::complex &) ::boost::math::octonion o9 = o2+c0; // using + (const ::boost::math::quaternion, const octonion &) ::boost::math::octonion o10 = q0+o3; // using + (const octonion &, const ::boost::math::quaternion &) ::boost::math::octonion o11 = o3+q0; // using + (const quaternion &,const quaternion &) ::boost::math::octonion o12 = o0+o4; // using - (const T &, const octonion &) o6 = f0-o0; // using - (const octonion &, const T &) o7 = o0-f0; // using - (const ::std::complex &, const octonion &) o8 = c0-o2; // using - (const octonion &, const ::std::complex &) o9 = o2-c0; // using - (const quaternion &,const octonion &) o10 = q0-o3; // using - (const octonion &,const quaternion &) o11 = o3-q0; // using - (const octonion &,const octonion &) o12 = o0-o4; // using * (const T &, const octonion &) o6 = f0*o0; // using * (const octonion &, const T &) o7 = o0*f0; // using * (const ::std::complex &, const octonion &) o8 = c0*o2; // using * (const octonion &, const ::std::complex &) o9 = o2*c0; // using * (const quaternion &,const octonion &) o10 = q0*o3; // using * (const octonion &,const quaternion &) o11 = o3*q0; // using * (const octonion &,const octonion &) o12 = o0*o4; // using / (const T &, const octonion &) o6 = f0/o0; // using / (const octonion &, const T &) o7 = o0/f0; // using / (const ::std::complex &, const octonion &) o8 = c0/o2; // using / (const octonion &, const ::std::complex &) o9 = o2/c0; // using / (const ::boost::math::quaternion &, const octonion &) o10 = q0/o3; // using / (const octonion &, const ::boost::math::quaternion &) o11 = o3/q0; // using / (const octonion &,const octonion &) o12 = o0/o4; // using + (const octonion &) o4 = +o0; // using == (const T &, const octonion &) BOOST_TEST(f0 == o0); // using == (const octonion &, const T &) BOOST_TEST(o0 == f0); // using - (const octonion &) o0 = -o4; // using != (const T &, const octonion &) BOOST_TEST(f0 != o0); // using != (const octonion &, const T &) BOOST_TEST(o0 != f0); // using != (const ::std::complex &, const octonion &) BOOST_TEST(c0 != o2); // using != (const octonion &, const ::std::complex &) BOOST_TEST(o2 != c0); // using != (const ::boost::math::quaternion &, const octonion &) BOOST_TEST(q0 != o3); // using != (const octonion &, const ::boost::math::quaternion &) BOOST_TEST(o3 != q0); // using != (const octonion &,const octonion &) BOOST_TEST(o0 != o4); } template void elem_func_test() { using ::std::numeric_limits; using ::std::atan; using ::boost::math::abs; // Testing exp. for(int idx = 1; idx < 8; ++idx) { ::boost::math::octonion toto = static_cast(4)*atan(static_cast(1)) * local::index_i_element(idx); const T tabs { abs(exp(toto)+static_cast(1)) }; const auto result_exp_toto_is_ok = (tabs < 2*numeric_limits::epsilon()); 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())); { 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); } } { 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())); } { 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 { multiplication_test(); multiplication_test(); division_test(); elem_func_test(); elem_func_test(); octonion_original_manual_test(); const auto result_is_ok = (boost::report_errors() == 0); return (result_is_ok ? 0 : -1); }