diff --git a/include/kyosu/functions.hpp b/include/kyosu/functions.hpp index a4a3f217..53f538e4 100644 --- a/include/kyosu/functions.hpp +++ b/include/kyosu/functions.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,5 +24,7 @@ #include #include #include +#include +#include #include #include diff --git a/include/kyosu/functions/abs.hpp b/include/kyosu/functions/abs.hpp index f2cf603b..fbbbcc64 100644 --- a/include/kyosu/functions/abs.hpp +++ b/include/kyosu/functions/abs.hpp @@ -1,47 +1,3 @@ -//! -//! **Defined in Header** -//! -//! @code -//! #include -//! @endcode -//! -//! @groupheader{Callable Signatures} -//! -//! @code -//! namespace eve -//! { -//! template< eve::value T > -//! T abs(T x) noexcept; //1 -//! -//! template< eve::floating_value T > -//! T abs(eve::as_complex z) noexcept; //2 -//! -//! } -//! @endcode -//! -//! **Parameters** -//! -//! * `x` : [real argument](@ref eve::value). -//! * `z` : [complex argument ](@ref eve::complex). -//! -//! **Return value** -//! -//! 1. value containing the [elementwise](@ref glossary_elementwise) -//! absolute value of `x` if it is representable in this type. -//! -//! More specifically, for signed integers : the absolute value of eve::valmin -//! is not representable and the result is undefined. -//! -//! -//! 2. -//! -//! @warning -//! abs is also a standard library function name and there possibly -//! exists a C macro version which may be called instead of the EVE version. -//! To avoid confusion, use the eve::abs notation. -//! - - //====================================================================================================================== /* Kyosu - Complex Without Complexes diff --git a/include/kyosu/functions/dist.hpp b/include/kyosu/functions/dist.hpp new file mode 100644 index 00000000..b1d4bf25 --- /dev/null +++ b/include/kyosu/functions/dist.hpp @@ -0,0 +1,79 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_dist : eve::elementwise + { + using callable_tag_type = callable_dist; + + KYOSU_DEFERS_CALLABLE(dist_); + + static KYOSU_FORCEINLINE auto deferred_call(auto + , eve::ordered_value auto const& v0 + , eve::ordered_value auto const& v1) noexcept + { + return eve::dist(v0, v1); + } + + KYOSU_FORCEINLINE auto operator()(auto const& target0, auto const& target1 ) const noexcept + -> decltype(eve::tag_invoke(*this, target0, target1)) + { + return eve::tag_invoke(*this, target0, target1); + } + + template + eve::unsupported_call operator()(T&&... x) const + requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var dist +//! @brief Computes the distance between the two parameters. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto dist(T0 z0, T1, z1) noexcept; +//! template > constexpr auto dist(T0 z0, T1, z1) noexcept; +//! template constexpr auto dist(T0 z0, T1, z1) noexcept; +//! template > constexpr auto dist(T0 z0, T1, z1) noexcept; +///! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z0, z1` : Value to process. +//! +//! **Return value** +//! +//! Returns the distance between the two arguments computed as the absolute value of the arguments difference. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/dist.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_dist dist = {}; +} diff --git a/include/kyosu/functions/reldist.hpp b/include/kyosu/functions/reldist.hpp new file mode 100644 index 00000000..b0d8cfb6 --- /dev/null +++ b/include/kyosu/functions/reldist.hpp @@ -0,0 +1,81 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_reldist : eve::elementwise + { + using callable_tag_type = callable_reldist; + + KYOSU_DEFERS_CALLABLE(reldist_); + + + static KYOSU_FORCEINLINE auto deferred_call(auto + , eve::ordered_value auto const& v0 + , eve::ordered_value auto const& v1) noexcept + { + return eve::reldist(v0, v1); + } + + KYOSU_FORCEINLINE auto operator()(auto const& target0, auto const& target1 ) const noexcept + -> decltype(eve::tag_invoke(*this, target0, target1)) + { + return eve::tag_invoke(*this, target0, target1); + } + + template + eve::unsupported_call operator()(T&&... x) const + requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var reldist +//! @brief Computes the relative distance between the two parameters. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto reldist(T0 z0, T1, z1) noexcept; +//! template > constexpr auto reldist(T0 z0, T1, z1) noexcept; +//! template constexpr auto reldist(T0 z0, T1, z1) noexcept; +//! template > constexpr auto reldist(T0 z0, T1, z1) noexcept; +///! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z0, z1` : Value to process. +//! +//! **Return value** +//! +//! Returns the the relative distance computed as the absolute value of the arguments difference +//! divided by the maximum of their absolute values and 1. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/reldist.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_reldist reldist = {}; +} diff --git a/include/kyosu/functions/sqr.hpp b/include/kyosu/functions/sqr.hpp new file mode 100644 index 00000000..acdb742b --- /dev/null +++ b/include/kyosu/functions/sqr.hpp @@ -0,0 +1,73 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_sqr : eve::elementwise + { + using callable_tag_type = callable_sqr; + + KYOSU_DEFERS_CALLABLE(sqr_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return v*v; } + + template + KYOSU_FORCEINLINE auto operator()(T const& target) const noexcept -> decltype(eve::tag_invoke(*this, target)) + { + return eve::tag_invoke(*this, target); + } + + template + eve::unsupported_call operator()(T&&... x) const + requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var sqr +//! @brief Computes the square value. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr T sqr(T z) noexcept; +//! template constexpr T sqr(T z) noexcept; +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to for which square is computed. +//! +//! **Return value** +//! +//! Returns the square of its argument. i.e. `z*z` in an optimized way. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/sqr.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_sqr sqr = {}; +} diff --git a/include/kyosu/types/impl/arithmetic.hpp b/include/kyosu/types/impl/arithmetic.hpp index 3417553a..ab0ca3c1 100644 --- a/include/kyosu/types/impl/arithmetic.hpp +++ b/include/kyosu/types/impl/arithmetic.hpp @@ -9,6 +9,7 @@ #include #include +#include namespace kyosu::_ { @@ -95,4 +96,38 @@ namespace kyosu::_ { return C{kumi::map([](auto const& e) { return eve::frac(e); }, c)}; } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C c) noexcept + { + if constexpr(kyosu::dimension_v <= 2) + { + return c*c; + }else + { + auto squares = kumi::map_index([](I, auto const& m) + { constexpr auto sgn = (I::value == 0)-(I::value > 0); + return sgn*m*m; }, c); + auto r = kumi::sum( squares, 0); + auto a = 2*real(c); + real(c) = 0; + return r+a*c; + } + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C0 const & c0, C1 const & c1) noexcept + { + return kyosu::abs(c0-c1); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C0 const & c0, C1 const & c1) noexcept + { + return kyosu::dist(c0, c1)/eve::max(kyosu::abs(c0), kyosu::abs(c1), eve::one(eve::as(abs(c0)))); + } + } diff --git a/include/kyosu/types/traits.hpp b/include/kyosu/types/traits.hpp index 8301a1f3..5ce85dc8 100644 --- a/include/kyosu/types/traits.hpp +++ b/include/kyosu/types/traits.hpp @@ -18,9 +18,14 @@ namespace kyosu namespace kyosu::_ { - // Force a type to be looked at as a wide so we can apply wide-like type presrving semantic in type computations - template struct sema { using type = T; }; - template struct sema { using type = eve::wide>; }; + // Force a type to be looked at as a wide so we can apply wide-like type preserving semantic in type computations + template struct sema { using type = T; }; + template struct sema> : sema {}; + template + struct sema> { using type = eve::wide>; }; + template + struct sema { using type = eve::wide>; }; + template using sema_t = typename sema::type; // Convert a Base type to a potential wide if any appear in T... diff --git a/test/doc/dist.cpp b/test/doc/dist.cpp new file mode 100644 index 00000000..7db417d4 --- /dev/null +++ b/test/doc/dist.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() +{ + using kyosu::dist; + using kyosu::complex; + using kyosu::quaternion; + using e_t = float; + using c_t = complex; + using q_t = quaternion; + using we_t = eve::wide>; + using wc_t = eve::wide, eve::fixed<2>>; + using wq_t = eve::wide, eve::fixed<2>>; + + std::cout << "Real: "<< "\n"; + e_t e0(1); + e_t e1(2); + std::cout << e0 << ", " << e1 << " -> " << dist(e0, e1) << "\n"; + we_t we0(e0); + we_t we1(e1); + std::cout << we0 << ", " << we1 << " -> " << dist(we0, we1) << "\n"; + + std::cout << "Complex: "<< "\n"; + c_t c0(1, 5); + c_t c1(5, 9); + std::cout << c0 << ", " << c1 << " -> " << dist(c0, c1) << "\n"; + wc_t wc0(c0); + wc_t wc1(c1); + std::cout << wc0 << ", " << wc1 << " -> " << dist(wc0, wc1) << "\n"; + + std::cout << "Quaternion: "<< "\n"; + q_t q0(1, 5, 2, 3); + q_t q1(5, 9, 6, 7); + std::cout << q0 << ", " << q1 << " -> " << dist(q0, q1) << "\n"; + wq_t wq0(q0); + wq_t wq1(q1); + std::cout << wq0 << ", " << wq1 << " -> " << dist(wq0, wq1) << "\n"; + + return 0; +} diff --git a/test/doc/reldist.cpp b/test/doc/reldist.cpp new file mode 100644 index 00000000..6ebc78c5 --- /dev/null +++ b/test/doc/reldist.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() +{ + using kyosu::reldist; + using kyosu::complex; + using kyosu::quaternion; + using e_t = float; + using c_t = complex; + using q_t = quaternion; + using we_t = eve::wide>; + using wc_t = eve::wide, eve::fixed<2>>; + using wq_t = eve::wide, eve::fixed<2>>; + + std::cout << "Real: "<< "\n"; + e_t e0(1); + e_t e1(2); + std::cout << e0 << ", " << e1 << " -> " << reldist(e0, e1) << "\n"; + we_t we0(e0); + we_t we1(e1); + std::cout << we0 << ", " << we1 << " -> " << reldist(we0, we1) << "\n"; + + std::cout << "Complex: "<< "\n"; + c_t c0(1, 5); + c_t c1(5, 9); + std::cout << c0 << ", " << c1 << " -> " << reldist(c0, c1) << "\n"; + wc_t wc0(c0); + wc_t wc1(c1); + std::cout << wc0 << ", " << wc1 << " -> " << reldist(wc0, wc1) << "\n"; + + std::cout << "Quaternion: "<< "\n"; + q_t q0(1, 5, 2, 3); + q_t q1(5, 9, 6, 7); + std::cout << q0 << ", " << q1 << " -> " << reldist(q0, q1) << "\n"; + wq_t wq0(q0); + wq_t wq1(q1); + std::cout << wq0 << ", " << wq1 << " -> " << reldist(wq0, wq1) << "\n"; + + return 0; +} diff --git a/test/doc/sqr.cpp b/test/doc/sqr.cpp new file mode 100644 index 00000000..c68c919c --- /dev/null +++ b/test/doc/sqr.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +int main() +{ + using kyosu::sqr; + using kyosu::complex; + using kyosu::quaternion; + + std::cout << "Real: "<< "\n"; + std::cout << 72.9f << " -> " << sqr(72.9f) << "\n"; + std::cout << 72.9f << " -> " << (72.9f)*(72.9f) << "\n"; + + std::cout << "Complex: "<< "\n"; + std::cout << complex(3.5f,-2.9f) << " -> " << sqr(complex(3.5f,-2.9f)) << "\n"; + std::cout << complex(3.5f,-2.9f) << " -> " << (complex(3.5f,-2.9f))*(complex(3.5f,-2.9f)) << "\n"; + + + std::cout << "Quaternion: "<< "\n"; + std::cout << quaternion(1.1,2.2,-3.3,4.4) << " -> " << sqr(quaternion(1.1,2.2,-3.3,4.4)) << "\n"; + std::cout << quaternion(1.1,2.2,-3.3,4.4) << " -> " << (quaternion(1.1,2.2,-3.3,4.4))*(quaternion(1.1,2.2,-3.3,4.4))<< "\n"; + + std::cout << "SIMD: "; + using wc_t = eve::wide, eve::fixed<2>>; + std::cout << wc_t(complex(1.3,-3.7)) << " -> " << sqr(wc_t(complex(1.3,-3.7))) << "\n"; + std::cout << wc_t(complex(1.3,-3.7)) << " -> " << (wc_t(complex(1.3,-3.7)))*(wc_t(complex(1.3,-3.7)))<< "\n"; + + return 0; +} diff --git a/test/test.hpp b/test/test.hpp index 666ebb5c..6f8e01ab 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -86,17 +86,15 @@ int main(int argc, char const **argv) namespace tts { - template auto relative_distance(T const &l, T const &r) - { - auto [rl,il] = l; - auto [rr,ir] = r; - return eve::max(relative_distance(rl,rr), relative_distance(il,ir)); + template double relative_distance(T const &l, T const &r) + { + return kyosu::reldist(l,r); } - template auto absolute_distance(T const &l, T const &r) + template double absolute_distance(T const &l, T const &r) { - return eve::maximum(eve::dist(l, r)); + return kyosu::dist(l, r); } template @@ -108,6 +106,7 @@ namespace tts return max_ulp; } + template inline double relative_distance(eve::wide const &l, eve::wide const &r) { diff --git a/test/unit/function/dist.cpp b/test/unit/function/dist.cpp new file mode 100644 index 00000000..2dd9e0f5 --- /dev/null +++ b/test/unit/function/dist.cpp @@ -0,0 +1,49 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include + +TTS_CASE_WITH ( "Check kyosu::dist over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10) + ,tts::between(-10,10) + ) + ) +(auto r0, auto r1) +{ + TTS_EQUAL(kyosu::dist(r0, r1), eve::dist(r0, r1)); +}; + +TTS_CASE_WITH ( "Check kyosu::dist over complex" + , kyosu::real_types + , tts::generate(tts::between(-10,10), tts::between(-10,10) + ,tts::between(-10,10), tts::between(-10,10) + ) + ) +(auto r0, auto i0, auto r1, auto i1) +{ + auto c0 = kyosu::to_complex(r0,i0); + auto c1 = kyosu::to_complex(r1,i1); + TTS_RELATIVE_EQUAL(kyosu::dist(c0, c1), kyosu::abs(c0-c1), 1e-7); +}; + +TTS_CASE_WITH ( "Check kyosu::dist over quaternion" + , kyosu::real_types + , tts::generate ( tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + ) + ) +(T r0, T i0, T j0, T k0, T r1, T i1, T j1, T k1) +{ + using type = kyosu::as_quaternion_t; + auto q0 = type(r0,i0,j0,k0); + auto q1 = type(r1,i1,j1,k1); + TTS_RELATIVE_EQUAL(kyosu::dist(q0, q1), kyosu::abs(q0-q1) , 1e-7); +}; diff --git a/test/unit/function/reldist.cpp b/test/unit/function/reldist.cpp new file mode 100644 index 00000000..fce28e95 --- /dev/null +++ b/test/unit/function/reldist.cpp @@ -0,0 +1,53 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include + +TTS_CASE_WITH ( "Check kyosu::reldist over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10) + ,tts::between(-10,10) + ) + ) +(auto r0, auto r1) +{ + TTS_EQUAL(kyosu::reldist(r0, r1), eve::reldist(r0, r1)); +}; + +TTS_CASE_WITH ( "Check kyosu::reldist over complex" + , kyosu::real_types + , tts::generate(tts::between(-10,10), tts::between(-10,10) + ,tts::between(-10,10), tts::between(-10,10) + ) + ) +(auto r0, auto i0, auto r1, auto i1) +{ + auto c0 = kyosu::to_complex(r0,i0); + auto c1 = kyosu::to_complex(r1,i1); + using kyosu::abs; + auto one = eve::one(eve::as(r0)); + TTS_RELATIVE_EQUAL(kyosu::reldist(c0, c1), abs(c0-c1)/eve::max(abs(c0), abs(c1), one), 1e-7); +}; + +TTS_CASE_WITH ( "Check kyosu::reldist over quaternion" + , kyosu::real_types + , tts::generate ( tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + ) + ) +(T r0, T i0, T j0, T k0, T r1, T i1, T j1, T k1) +{ + using type = kyosu::as_quaternion_t; + auto q0 = type(r0,i0,j0,k0); + auto q1 = type(r1,i1,j1,k1); + using kyosu::abs; + auto one = eve::one(eve::as(r0)); + TTS_RELATIVE_EQUAL(kyosu::reldist(q0, q1), abs(q0-q1)/eve::max(abs(q0), abs(q1), one), 1e-7); +}; diff --git a/test/unit/function/sqr.cpp b/test/unit/function/sqr.cpp new file mode 100644 index 00000000..51eea541 --- /dev/null +++ b/test/unit/function/sqr.cpp @@ -0,0 +1,41 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include + +TTS_CASE_WITH ( "Check kyosu::sqr over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10)) + ) +(auto data) +{ + TTS_ULP_EQUAL(kyosu::sqr(data), eve::sqr(data), 0.5); +}; + +TTS_CASE_WITH ( "Check kyosu::sqr over complex" + , kyosu::real_types + , tts::generate(tts::between(-10,10), tts::between(-10,10)) + ) +(auto r, auto i) +{ + auto c = kyosu::to_complex(r,i); + TTS_RELATIVE_EQUAL(kyosu::sqr(c), c*c, 1e-7); +}; + +TTS_CASE_WITH ( "Check kyosu::sqr over quaternion" + , kyosu::real_types + , tts::generate ( tts::between(-10,10), tts::between(-10,10) + , tts::between(-10,10), tts::between(-10,10) + ) + ) +(T r, T i, T j, T k) +{ + using type = kyosu::as_quaternion_t; + auto q = type(r,i,j,k); + TTS_RELATIVE_EQUAL(kyosu::sqr(q), q*q, 1e-7); +};