diff --git a/include/kyosu/functions.hpp b/include/kyosu/functions.hpp index 0cc002c8..49d915b8 100644 --- a/include/kyosu/functions.hpp +++ b/include/kyosu/functions.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/include/kyosu/functions/hypot.hpp b/include/kyosu/functions/hypot.hpp new file mode 100644 index 00000000..730f91ae --- /dev/null +++ b/include/kyosu/functions/hypot.hpp @@ -0,0 +1,76 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_hypot : eve::elementwise + { + using callable_tag_type = callable_hypot; + + KYOSU_DEFERS_CALLABLE(hypot_); + + static KYOSU_FORCEINLINE auto deferred_call(auto + , eve::ordered_value auto const&... vs) noexcept + { + return eve::hypot(vs...); + } + + KYOSU_FORCEINLINE auto operator()(auto const&... targets ) const noexcept + -> decltype(eve::tag_invoke(*this, targets...)) + { + return eve::tag_invoke(*this, targets...); + } + +// template +// eve::unsupported_call operator()(T&&... x) const +// requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var hypot +//! @brief Callable object computing the hypot operation. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template< floating_value P, typename ... Ts> +//! auto operator()(Ts ... zi ) const noexcept +///! } +//! @endcode +//! +//! **Parameters** +//! +//! * ` zi...` : Values to process. +//! +//! **Return value** +//! +//! Returns \f$ \sqrt\sum_{i = 0}^n//! |z_i|^2} \f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/hypot.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_hypot hypot = {}; +} diff --git a/include/kyosu/functions/lpnorm.hpp b/include/kyosu/functions/lpnorm.hpp new file mode 100644 index 00000000..f180a46f --- /dev/null +++ b/include/kyosu/functions/lpnorm.hpp @@ -0,0 +1,78 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_lpnorm : eve::elementwise + { + using callable_tag_type = callable_lpnorm; + + KYOSU_DEFERS_CALLABLE(lpnorm_); + + static KYOSU_FORCEINLINE auto deferred_call(auto + , eve::floating_value auto const& p + , eve::floating_ordered_value auto const&... vs) noexcept + { + return eve::lpnorm(p, vs...); + } + + KYOSU_FORCEINLINE auto operator()(auto const&... targets ) const noexcept + -> decltype(eve::tag_invoke(*this, targets...)) + { + return eve::tag_invoke(*this, targets...); + } + + template + eve::unsupported_call operator()(T&&... x) const + requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var lpnorm +//! @brief Callable object computing the lpnorm operation \f$ \left(\sum_{i = 0}^n +//! |x_i|^p\right)^{\frac1p} \f$. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template< floating_value P, typename T, typename ... Ts> +//! auto operator()(P p, T z,Ts ... zs ) const noexcept +///! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z, zs...` : Values to process. +//! +//! **Return value** +//! +//! Returns \f$ \left(\sum_{i = 0}^n//! |x_i|^p\right)^{\frac1p} \f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/lpnorm.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_lpnorm lpnorm = {}; +} diff --git a/include/kyosu/functions/manhattan.hpp b/include/kyosu/functions/manhattan.hpp new file mode 100644 index 00000000..940bbff7 --- /dev/null +++ b/include/kyosu/functions/manhattan.hpp @@ -0,0 +1,76 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::tags +{ + struct callable_manhattan : eve::elementwise + { + using callable_tag_type = callable_manhattan; + + KYOSU_DEFERS_CALLABLE(manhattan_); + + static KYOSU_FORCEINLINE auto deferred_call(auto + , eve::ordered_value auto const&... vs) noexcept + { + return eve::manhattan(vs...); + } + + KYOSU_FORCEINLINE auto operator()(auto const&... targets ) const noexcept + -> decltype(eve::tag_invoke(*this, targets...)) + { + return eve::tag_invoke(*this, targets...); + } + +// template +// eve::unsupported_call operator()(T&&... x) const +// requires(!requires { eve::tag_invoke(*this, KYOSU_FWD(x)...); }) = delete; + }; +} + +namespace kyosu +{ +//====================================================================================================================== +//! @addtogroup functions +//! @{ +//! @var manhattan +//! @brief Callable object computing the manhattan operation. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template< floating_value P, typename ... Ts> +//! auto operator()(Ts ... zi ) const noexcept +///! } +//! @endcode +//! +//! **Parameters** +//! +//! * ` zi...` : Values to process. +//! +//! **Return value** +//! +//! Returns the sum of the absolute values of all elements of all zi. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/manhattan.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_manhattan manhattan = {}; +} diff --git a/include/kyosu/types/impl/math.hpp b/include/kyosu/types/impl/math.hpp index e61a0c4c..2efac083 100644 --- a/include/kyosu/types/impl/math.hpp +++ b/include/kyosu/types/impl/math.hpp @@ -279,7 +279,6 @@ namespace kyosu::_ C res; auto rr1 = eve::if_else(is_real_z, sqrtx, w); auto ii1 = eve::if_else(is_real_z, eve::zero, iaz*eve::half(eve::as(r))/w); -// auto res = kyosu::to_complex(rr1, ii1); res = kyosu::if_else(gezrz , to_complex(rr1, ii1) , to_complex(ii1, rr1) @@ -314,4 +313,29 @@ namespace kyosu::_ } } + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, T const & p, Cs const &... zs) noexcept + { + if constexpr(sizeof...(zs) == 0) return 0.0f; + if constexpr(sizeof...(zs) == 1) return kyosu::abs(zs...); + else return eve::lpnorm(p, kyosu::abs(zs)...); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, Cs const &... zs) noexcept + { + if constexpr(sizeof...(zs) == 0) return 0.0f; + if constexpr(sizeof...(zs) == 1) return kyosu::abs(zs...); + else return eve::hypot(kumi::flatten(kumi::cat(zs...))); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, Cs const &... zs) noexcept + { + if constexpr(sizeof...(zs) == 0) return 0.0f; + else return eve::manhattan(kumi::flatten(kumi::cat(zs...))); + } } diff --git a/test/doc/hypot.cpp b/test/doc/hypot.cpp new file mode 100644 index 00000000..ea791aaa --- /dev/null +++ b/test/doc/hypot.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() +{ + using kyosu::hypot; + 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 << " -> " << hypot(e0, e1) << "\n"; + we_t we0(e0); + we_t we1(e1); + std::cout << we0 << ", " << we1 << " -> " << hypot(we0, we1) << "\n"; + + std::cout << "Complex: "<< "\n"; + c_t c0(5); + c_t c1(5, 9); + std::cout << c0 << ", " << c1 << " -> " << hypot(c0, c1) << "\n"; + wc_t wc0(c0); + wc_t wc1(c1); + std::cout << wc0 << ", " << wc1 << " -> " << hypot(wc0, wc1) << "\n"; + + std::cout << "Quaternion: "<< "\n"; + q_t q0(5, 2, 3); + q_t q1(5, 9, 6, 7); + std::cout << q0 << ", " << q1 << " -> " << hypot(q0, q1) << "\n"; + wq_t wq0(q0); + wq_t wq1(q1); + std::cout << wq0 << ", " << wq1 << " -> " << hypot(wq0, wq1) << "\n"; + + return 0; +} diff --git a/test/doc/lpnorm.cpp b/test/doc/lpnorm.cpp new file mode 100644 index 00000000..77df6a8f --- /dev/null +++ b/test/doc/lpnorm.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() +{ + using kyosu::lpnorm; + 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 << " -> " << lpnorm(e_t(1), e0, e1) << "\n"; + we_t we0(e0); + we_t we1(e1); + std::cout << we0 << ", " << we1 << " -> " << lpnorm(e_t(1), we0, we1) << "\n"; + + std::cout << "Complex: "<< "\n"; + c_t c0(e_t(1), 5); + c_t c1(5, 9); + std::cout << c0 << ", " << c1 << " -> " << lpnorm(e_t(1), c0, c1) << "\n"; + wc_t wc0(c0); + wc_t wc1(c1); + std::cout << wc0 << ", " << wc1 << " -> " << lpnorm(e_t(1), wc0, wc1) << "\n"; + + std::cout << "Quaternion: "<< "\n"; + q_t q0(e_t(1), 5, 2, 3); + q_t q1(5, 9, 6, 7); + std::cout << q0 << ", " << q1 << " -> " << lpnorm(e_t(1), q0, q1) << "\n"; + wq_t wq0(q0); + wq_t wq1(q1); + std::cout << wq0 << ", " << wq1 << " -> " << lpnorm(e_t(1), wq0, wq1) << "\n"; + + return 0; +} diff --git a/test/doc/manhattan.cpp b/test/doc/manhattan.cpp new file mode 100644 index 00000000..f8938642 --- /dev/null +++ b/test/doc/manhattan.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() +{ + using kyosu::manhattan; + 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 << " -> " << manhattan(e0, e1) << "\n"; + we_t we0(e0); + we_t we1(e1); + std::cout << we0 << ", " << we1 << " -> " << manhattan(we0, we1) << "\n"; + + std::cout << "Complex: "<< "\n"; + c_t c0(5); + c_t c1(5, 9); + std::cout << c0 << ", " << c1 << " -> " << manhattan(c0, c1) << "\n"; + wc_t wc0(c0); + wc_t wc1(c1); + std::cout << wc0 << ", " << wc1 << " -> " << manhattan(wc0, wc1) << "\n"; + + std::cout << "Quaternion: "<< "\n"; + q_t q0(5, 2, 3); + q_t q1(5, 9, 6, 7); + std::cout << q0 << ", " << q1 << " -> " << manhattan(q0, q1) << "\n"; + wq_t wq0(q0); + wq_t wq1(q1); + std::cout << wq0 << ", " << wq1 << " -> " << manhattan(wq0, wq1) << "\n"; + + return 0; +} diff --git a/test/unit/function/hypot.cpp b/test/unit/function/hypot.cpp new file mode 100644 index 00000000..47bd1438 --- /dev/null +++ b/test/unit/function/hypot.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::hypot over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10) + ,tts::between(-10,10) + ) + ) +(auto r0, auto r1) +{ + TTS_EQUAL(kyosu::hypot(r0, r1), eve::hypot(r0, r1)); +}; + +TTS_CASE_WITH ( "Check kyosu::hypot 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::hypot(c0, c1), eve::hypot(r0, i0, r1, i1), 2e-5); +}; + +TTS_CASE_WITH ( "Check kyosu::hypot 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::hypot(q0, q1), eve::hypot(r0, i0, j0, k0, r1, i1, j1, k1), 1e-5); +}; diff --git a/test/unit/function/lpnorm.cpp b/test/unit/function/lpnorm.cpp new file mode 100644 index 00000000..63030901 --- /dev/null +++ b/test/unit/function/lpnorm.cpp @@ -0,0 +1,52 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include + +TTS_CASE_WITH ( "Check kyosu::lpnorm over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10) + ,tts::between(-10,10) + ,tts::between(1, 3) + ) + ) +(auto r0, auto r1, auto p) +{ + TTS_EQUAL(kyosu::lpnorm(p, r0, r1), eve::lpnorm(p, r0, r1)); +}; + +TTS_CASE_WITH ( "Check kyosu::lpnorm over complex" + , kyosu::real_types + , tts::generate(tts::between(-10,10), tts::between(-10,10) + ,tts::between(-10,10), tts::between(-10,10) + ,tts::between(1, 3) + ) + ) +(auto r0, auto i0, auto r1, auto i1, auto p) +{ + auto c0 = kyosu::to_complex(r0,i0); + auto c1 = kyosu::to_complex(r1,i1); + TTS_RELATIVE_EQUAL(kyosu::lpnorm(p, c0, c1), eve::lpnorm(p, kyosu::abs(c0), kyosu::abs(c1)), 1e-7); +}; + +TTS_CASE_WITH ( "Check kyosu::lpnorm 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) + ,tts::between(1, 3) + ) + ) +(T r0, T i0, T j0, T k0, T r1, T i1, T j1, T k1, T p) +{ + using type = kyosu::as_quaternion_t; + auto q0 = type(r0,i0,j0,k0); + auto q1 = type(r1,i1,j1,k1); + TTS_RELATIVE_EQUAL(kyosu::lpnorm(p, q0, q1), eve::lpnorm(p, kyosu::abs(q0), kyosu::abs(q1)), 1e-7); +}; diff --git a/test/unit/function/manhattan.cpp b/test/unit/function/manhattan.cpp new file mode 100644 index 00000000..cd8819c1 --- /dev/null +++ b/test/unit/function/manhattan.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::manhattan over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10) + ,tts::between(-10,10) + ) + ) +(auto r0, auto r1) +{ + TTS_EQUAL(kyosu::manhattan(r0, r1), eve::manhattan(r0, r1)); +}; + +TTS_CASE_WITH ( "Check kyosu::manhattan 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::manhattan(c0, c1), eve::manhattan(r0, i0, r1, i1), 2e-5); +}; + +TTS_CASE_WITH ( "Check kyosu::manhattan 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::manhattan(q0, q1), eve::manhattan(r0, i0, j0, k0, r1, i1, j1, k1 ), 1e-5); +};