diff --git a/include/kyosu/complex/acos.hpp b/include/kyosu/complex/acos.hpp new file mode 100644 index 00000000..cc4cfbd8 --- /dev/null +++ b/include/kyosu/complex/acos.hpp @@ -0,0 +1,97 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acos : eve::elementwise + { + using callable_tag_type = callable_acos; + + KYOSU_DEFERS_CALLABLE(acos_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_acos{}; + return fn(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 acos +//! @brief Computes the acosine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acos(T z) noexcept; //1 +//! template constexpr auto acos(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc cosine of the input. +//! Branch cuts exist outside the interval \f$[-1, +1]\f$ along the real axis. +//! +//! * for every z: eve::acos(eve::conj(z)) == eve::conj(std::acos(z)) +//! * If z is \f$\pm0\f$, the result is \f$\pi/2\f$ +//! * If z is \f$i NaN\f$, the result is \f$\pi/2+ i NaN\f$ +//! * If z is \f$x+i\infty\f$ (for any finite x), the result is \f$\pi/2-i\infty\f$ +//! * If z is \f$x+i NaN\f$ (for any nonzero finite x), the result is \f$NaN+i NaN\f$. +//! * If z is \f$-\infty+i y\f$ (for any positive finite y), the result is \f$\pi-i\infty\f$ +//! * If z is \f$+\infty+i y\f$ (for any positive finite y), the result is \f$+0-i\infty\f$ +//! * If z is \f$-\infty+i +\infty\f$, the result is \f$3\pi/4-i\infty\f$ +//! * If z is \f$\infty+i +\infty\f$, the result is \f$\pi/4-i\infty\f$ +//! * If z is \f$\pm\infty+i NaN\f$, the result is \f$NaN \pm i\infty\f$ (the sign +//! of the imaginary part is unspecified) +//! * If z is \f$NaN+i y\f$ (for any finite y), the result is \f$NaN+i NaN\f$ +//! * If z is \f$NaN+i\infty\f$, the result is \f$NaN-i\infty\f$ +//! * If z is \f$NaN+i NaN\f$, the result is \f$NaN+i NaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acos.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acos acos = {}; +} diff --git a/include/kyosu/complex/acosh.hpp b/include/kyosu/complex/acosh.hpp new file mode 100644 index 00000000..67b4896b --- /dev/null +++ b/include/kyosu/complex/acosh.hpp @@ -0,0 +1,96 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acosh : eve::elementwise + { + using callable_tag_type = callable_acosh; + + KYOSU_DEFERS_CALLABLE(acosh_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_acosh{}; + return fn(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 acosh +//! @brief Computes the acoshine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acosh(T z) noexcept; //1 +//! template constexpr auto acosh(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the complex arc hyperbolic sine of z, in the range of a half-strip mathematically +//! unbounded along the real axis and in the interval \f$i\times[-\pi/2, \pi/2]\f$ along +//! the imaginary axis. +//! +//! * for every z: eve::acosh(eve::conj(z)) == eve::conj(std::acosh(z)) +//! * for every z: eve::acosh(-z) == -eve::acosh(z) +//! * If z is \f$+0\f$, the result is \f$+0\f$ +//! * If z is \f$NaN\f$, the result is \f$NaN\f$ +//! * If z is \f$+1\f$, the result is \f$+\infty\f$ +//! * If z is \f$x+i \infty\f$ (for any finite positive x), the result is \f$+0,\pi/2\f$ +//! * If z is \f$x+i NaN\f$ (for any finite nonzero x), the result is \f$NaN+i NaN\f$ +//! * If z is \f$+\infty+i y\f$ (for any finite positive y), the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i \infty\f$, the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i NaN\f$, the result is \f$i NaN\f$ +//! * If z is \f$NaN+i y\f$ (for any finite y), the result is \f$NaN+i NaN\f$ +//! * If z is \f$NaN+i \infty\f$, the result is \f$i \pi/2\f$ (the sign of the real part is unspecified) +//! * If z is \f$NaN+i NaN\f$, the result is \f$NaN+i NaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acosh.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acosh acosh = {}; +} diff --git a/include/kyosu/complex/acot.hpp b/include/kyosu/complex/acot.hpp new file mode 100644 index 00000000..3c6bf955 --- /dev/null +++ b/include/kyosu/complex/acot.hpp @@ -0,0 +1,81 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acot : eve::elementwise + { + using callable_tag_type = callable_acot; + + KYOSU_DEFERS_CALLABLE(acot_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_acot{}; + return fn(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 acot +//! @brief Computes the acotine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acot(T z) noexcept; //1 +//! template constexpr auto acot(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc cotangent of the input as the arc tangent of the inverse of the input. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acot.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acot acot = {}; +} diff --git a/include/kyosu/complex/acoth.hpp b/include/kyosu/complex/acoth.hpp new file mode 100644 index 00000000..bae3455f --- /dev/null +++ b/include/kyosu/complex/acoth.hpp @@ -0,0 +1,80 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acoth : eve::elementwise + { + using callable_tag_type = callable_acoth; + + KYOSU_DEFERS_CALLABLE(acoth_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_acoth{}; + return fn(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 acoth +//! @brief Computes the acothine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acoth(T z) noexcept; //1 +//! template constexpr auto acoth(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the complex arc hyperbolic cotangent of z, computed as \f$\mathop{\mathrm{atanh}}(1/z)\f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acoth.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acoth acoth = {}; +} diff --git a/include/kyosu/complex/acsc.hpp b/include/kyosu/complex/acsc.hpp new file mode 100644 index 00000000..198d889d --- /dev/null +++ b/include/kyosu/complex/acsc.hpp @@ -0,0 +1,76 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acsc : eve::elementwise + { + using callable_tag_type = callable_acsc; + + KYOSU_DEFERS_CALLABLE(acsc_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return eve::acsc(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 acsc +//! @brief Computes the arccosecant of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acsc(T z) noexcept; //1 +//! template constexpr auto acsc(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) \f$\mathop{\mathrm{asin}}(1/z)\f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acsc.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acsc acsc = {}; +} diff --git a/include/kyosu/complex/acsch.hpp b/include/kyosu/complex/acsch.hpp new file mode 100644 index 00000000..81009f31 --- /dev/null +++ b/include/kyosu/complex/acsch.hpp @@ -0,0 +1,80 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acsch : eve::elementwise + { + using callable_tag_type = callable_acsch; + + KYOSU_DEFERS_CALLABLE(acsch_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_acsch{}; + return fn(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 acsch +//! @brief Computes the hyperbolic arccosecant of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acsch(T z) noexcept; //1 +//! template constexpr auto acsch(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) \f$\mathop{\mathrm{asinh}}(1/z)\f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acsch.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acsch acsch = {}; +} diff --git a/include/kyosu/complex/acsh.hpp b/include/kyosu/complex/acsh.hpp new file mode 100644 index 00000000..37af3d51 --- /dev/null +++ b/include/kyosu/complex/acsh.hpp @@ -0,0 +1,96 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_atanh : eve::elementwise + { + using callable_tag_type = callable_atanh; + + KYOSU_DEFERS_CALLABLE(atanh_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_atanh{}; + return fn(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 atanh +//! @brief Computes the atanhine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto atanh(T z) noexcept; //1 +//! template constexpr auto atanh(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the complex arc hyperbolic sine of z, in the range of a half-strip mathematically +//! unbounded along the real axis and in the interval \f$i\times[-\pi/2, \pi/2]\f$ along +//! the imaginary axis. +//! +//! * for every z: eve::atanh(eve::conj(z)) == eve::conj(std::atanh(z)) +//! * for every z: eve::atanh(-z) == -eve::atanh(z) +//! * If z is \f$+0\f$, the result is \f$+0\f$ +//! * If z is \f$NaN\f$, the result is \f$NaN\f$ +//! * If z is \f$+1\f$, the result is \f$+\infty\f$ +//! * If z is \f$x+i \infty\f$ (for any finite positive x), the result is \f$+0,\pi/2\f$ +//! * If z is \f$x+i NaN\f$ (for any finite nonzero x), the result is \f$NaN+i NaN\f$ +//! * If z is \f$+\infty+i y\f$ (for any finite positive y), the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i \infty\f$, the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i NaN\f$, the result is \f$i NaN\f$ +//! * If z is \f$NaN+i y\f$ (for any finite y), the result is \f$NaN+i NaN\f$ +//! * If z is \f$NaN+i \infty\f$, the result is \f$i \pi/2\f$ (the sign of the real part is unspecified) +//! * If z is \f$NaN+i NaN\f$, the result is \f$NaN+i NaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/atanh.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_atanh atanh = {}; +} diff --git a/include/kyosu/complex/asec.hpp b/include/kyosu/complex/asec.hpp new file mode 100644 index 00000000..6eb3a835 --- /dev/null +++ b/include/kyosu/complex/asec.hpp @@ -0,0 +1,76 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_asec : eve::elementwise + { + using callable_tag_type = callable_asec; + + KYOSU_DEFERS_CALLABLE(asec_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return eve::asec(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 asec +//! @brief Computes the arcsecant of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto asec(T z) noexcept; //1 +//! template constexpr auto asec(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) \f$\mathop{\mathrm{acos}}(1/z)\f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/asec.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_asec asec = {}; +} diff --git a/include/kyosu/complex/asech.hpp b/include/kyosu/complex/asech.hpp new file mode 100644 index 00000000..9802b6b7 --- /dev/null +++ b/include/kyosu/complex/asech.hpp @@ -0,0 +1,80 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_asech : eve::elementwise + { + using callable_tag_type = callable_asech; + + KYOSU_DEFERS_CALLABLE(asech_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_asech{}; + return fn(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 asech +//! @brief Computes the hyperbolic arcsecant of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto asech(T z) noexcept; //1 +//! template constexpr auto asech(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) \f$\mathop{\mathrm{acosh}}(1/z)\f$. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/asech.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_asech asech = {}; +} diff --git a/include/kyosu/complex/asin.hpp b/include/kyosu/complex/asin.hpp new file mode 100644 index 00000000..262c4a3f --- /dev/null +++ b/include/kyosu/complex/asin.hpp @@ -0,0 +1,84 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_asin : eve::elementwise + { + using callable_tag_type = callable_asin; + + KYOSU_DEFERS_CALLABLE(asin_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_asin{}; + return fn(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 asin +//! @brief Computes the asinine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto asin(T z) noexcept; //1 +//! template constexpr auto asin(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc sine of the input in the range of a strip unbounded along the imaginary axis +//! and in the interval \f$[-\pi/2, \pi/2]\f$ along the real axis. +//! +//! special cases are handled as if the operation was implemented by \f$-i \mathrm{asinh}(i z)\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/asin.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_asin asin = {}; +} diff --git a/include/kyosu/complex/asinh.hpp b/include/kyosu/complex/asinh.hpp new file mode 100644 index 00000000..cfff986b --- /dev/null +++ b/include/kyosu/complex/asinh.hpp @@ -0,0 +1,94 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_asinh : eve::elementwise + { + using callable_tag_type = callable_asinh; + + KYOSU_DEFERS_CALLABLE(asinh_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_asinh{}; + return fn(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 asinh +//! @brief Computes the asinhine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto asinh(T z) noexcept; //1 +//! template constexpr auto asinh(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the complex arc hyperbolic sine of z, with branch cuts outside the interval +//! \f$i\times[-\pi/2, \pi/2]\f$ along the imaginary axis. +//! +//! * for every z: eve::asinh(eve::conj(z)) == eve::conj(std::asinh(z)) +//! * for every z: eve::asinh(-z) == -eve::asinh(z) +//! * If z is \f$+0\f$, the result is \f$+0\f$ +//! * If z is \f$x+i \infty\f$ (for any positive finite x), the result is \f$+\infty+i \pi/2\f$ +//! * If z is \f$x,NaN\f$ (for any finite x), the result is \f$NaN+ iNaN\f$ +//! * If z is \f$+\infty+ iy\f$ (for any positive finite y), the result is \f$+\infty+i 0\f$ +//! * If z is \f$+\infty+i \infty\f$, the result is \f$+\infty+ i\pi/4\f$ +//! * If z is \f$+\infty+ iNaN\f$, the result is \f$+\infty+ iNaN\f$ +//! * If z is \f$NaN+i 0\f$, the result is \f$NaN+i 0\f$ +//! * If z is \f$NaN+ iy\f$ (for any finite nonzero y), the result is \f$NaN+ iNaN\f$ +//! * If z is \f$NaN+i \infty\f$, the result is \f$\pm \infty+ iNaN\f$ (the sign of the real part is unspecified) +//! * If z is \f$NaN+ iNaN\f$, the result is \f$NaN+ iNaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/asinh.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_asinh asinh = {}; +} diff --git a/include/kyosu/complex/atan.hpp b/include/kyosu/complex/atan.hpp new file mode 100644 index 00000000..1b183d5b --- /dev/null +++ b/include/kyosu/complex/atan.hpp @@ -0,0 +1,84 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_atan : eve::elementwise + { + using callable_tag_type = callable_atan; + + KYOSU_DEFERS_CALLABLE(atan_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_atan{}; + return fn(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 atan +//! @brief Computes the atanine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto atan(T z) noexcept; //1 +//! template constexpr auto atan(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc tangent of the input in the range of a strip unbounded along the imaginary axis +//! and in the interval \f$[-\pi/2, \pi/2]\f$ along the real axis. +//! +//! special cases are handled as if the operation was implemented by \f$-i \mathrm{atanh}(i z)\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/atan.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_atan atan = {}; +} diff --git a/include/kyosu/complex/atanh.hpp b/include/kyosu/complex/atanh.hpp new file mode 100644 index 00000000..37af3d51 --- /dev/null +++ b/include/kyosu/complex/atanh.hpp @@ -0,0 +1,96 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_atanh : eve::elementwise + { + using callable_tag_type = callable_atanh; + + KYOSU_DEFERS_CALLABLE(atanh_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept + { + auto fn = callable_atanh{}; + return fn(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 atanh +//! @brief Computes the atanhine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto atanh(T z) noexcept; //1 +//! template constexpr auto atanh(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the complex arc hyperbolic sine of z, in the range of a half-strip mathematically +//! unbounded along the real axis and in the interval \f$i\times[-\pi/2, \pi/2]\f$ along +//! the imaginary axis. +//! +//! * for every z: eve::atanh(eve::conj(z)) == eve::conj(std::atanh(z)) +//! * for every z: eve::atanh(-z) == -eve::atanh(z) +//! * If z is \f$+0\f$, the result is \f$+0\f$ +//! * If z is \f$NaN\f$, the result is \f$NaN\f$ +//! * If z is \f$+1\f$, the result is \f$+\infty\f$ +//! * If z is \f$x+i \infty\f$ (for any finite positive x), the result is \f$+0,\pi/2\f$ +//! * If z is \f$x+i NaN\f$ (for any finite nonzero x), the result is \f$NaN+i NaN\f$ +//! * If z is \f$+\infty+i y\f$ (for any finite positive y), the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i \infty\f$, the result is \f$i \pi/2\f$ +//! * If z is \f$+\infty+i NaN\f$, the result is \f$i NaN\f$ +//! * If z is \f$NaN+i y\f$ (for any finite y), the result is \f$NaN+i NaN\f$ +//! * If z is \f$NaN+i \infty\f$, the result is \f$i \pi/2\f$ (the sign of the real part is unspecified) +//! * If z is \f$NaN+i NaN\f$, the result is \f$NaN+i NaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/atanh.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_atanh atanh = {}; +} diff --git a/include/kyosu/functions.hpp b/include/kyosu/functions.hpp index 0d6a7849..79d6629e 100644 --- a/include/kyosu/functions.hpp +++ b/include/kyosu/functions.hpp @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -101,7 +102,19 @@ //! @brief Functions performing computations over complex or real elements only. //====================================================================================================================== +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/include/kyosu/functions/acos.hpp b/include/kyosu/functions/acos.hpp new file mode 100644 index 00000000..02a13ffe --- /dev/null +++ b/include/kyosu/functions/acos.hpp @@ -0,0 +1,93 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_acos : eve::elementwise + { + using callable_tag_type = callable_acos; + + KYOSU_DEFERS_CALLABLE(acos_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return eve::acos(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 acos +//! @brief Computes the acosine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto acos(T z) noexcept; //1 +//! template constexpr auto acos(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc cosine of the input. +//! Branch cuts exist outside the interval \f$[-1, +1]\f$ along the real axis. +//! +//! * for every z: eve::acos(eve::conj(z)) == eve::conj(std::acos(z)) +//! * If z is \f$\pm0\f$, the result is \f$\pi/2\f$ +//! * If z is \f$i NaN\f$, the result is \f$\pi/2+ i NaN\f$ +//! * If z is \f$x+i\infty\f$ (for any finite x), the result is \f$\pi/2-i\infty\f$ +//! * If z is \f$x+i NaN\f$ (for any nonzero finite x), the result is \f$NaN+i NaN\f$. +//! * If z is \f$-\infty+i y\f$ (for any positive finite y), the result is \f$\pi-i\infty\f$ +//! * If z is \f$+\infty+i y\f$ (for any positive finite y), the result is \f$+0-i\infty\f$ +//! * If z is \f$-\infty+i +\infty\f$, the result is \f$3\pi/4-i\infty\f$ +//! * If z is \f$\infty+i +\infty\f$, the result is \f$\pi/4-i\infty\f$ +//! * If z is \f$\pm\infty+i NaN\f$, the result is \f$NaN \pm i\infty\f$ (the sign +//! of the imaginary part is unspecified) +//! * If z is \f$NaN+i y\f$ (for any finite y), the result is \f$NaN+i NaN\f$ +//! * If z is \f$NaN+i\infty\f$, the result is \f$NaN-i\infty\f$ +//! * If z is \f$NaN+i NaN\f$, the result is \f$NaN+i NaN\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/acos.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_acos acos = {}; +} diff --git a/include/kyosu/functions/asin.hpp b/include/kyosu/functions/asin.hpp new file mode 100644 index 00000000..4381824d --- /dev/null +++ b/include/kyosu/functions/asin.hpp @@ -0,0 +1,80 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_asin : eve::elementwise + { + using callable_tag_type = callable_asin; + + KYOSU_DEFERS_CALLABLE(asin_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return eve::asin(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 asin +//! @brief Computes the asinine of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr auto asin(T z) noexcept; //1 +//! template constexpr auto asin(T z) noexcept; //2 +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! 1. a real input z is treated as if to_complex(z) was entered. +//! +//! 2. Returns the [elementwise](@ref glossary_elementwise) the complex principal value +//! of the arc sine of the input in the range of a strip unbounded along the imaginary axis +//! and in the interval \f$[-\pi/2, \pi/2]\f$ along the real axis. +//! +//! special cases are handled as if the operation was implemented by \f$-i \mathrm{asinh}(i z)\f$ +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/asin.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_asin asin = {}; +} diff --git a/include/kyosu/functions/sinc.hpp b/include/kyosu/functions/sinc.hpp new file mode 100644 index 00000000..d3112d96 --- /dev/null +++ b/include/kyosu/functions/sinc.hpp @@ -0,0 +1,74 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include +#include + +namespace kyosu::tags +{ + struct callable_sinc : eve::elementwise + { + using callable_tag_type = callable_sinc; + + KYOSU_DEFERS_CALLABLE(sinc_); + + template + static KYOSU_FORCEINLINE auto deferred_call(auto, T const& v) noexcept { return eve::sinc(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 sinc +//! @brief Computes the sine cardinal of the argument. +//! +//! **Defined in Header** +//! +//! @code +//! #include +//! @endcode +//! +//! @groupheader{Callable Signatures} +//! +//! @code +//! namespace kyosu +//! { +//! template constexpr T sinc(T z) noexcept; +//! template constexpr T sinc(T z) noexcept; +//! } +//! @endcode +//! +//! **Parameters** +//! +//! * `z` : Value to process. +//! +//! **Return value** +//! +//! Returns the sine cardinal of the argument. +//! +//! @groupheader{Example} +//! +//! @godbolt{doc/sinc.cpp} +//! @} +//====================================================================================================================== +inline constexpr tags::callable_sinc sinc = {}; +} diff --git a/include/kyosu/types/cayley_dickson.hpp b/include/kyosu/types/cayley_dickson.hpp index 780e5f6d..b6645260 100644 --- a/include/kyosu/types/cayley_dickson.hpp +++ b/include/kyosu/types/cayley_dickson.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/include/kyosu/types/impl/complex/invtrig.hpp b/include/kyosu/types/impl/complex/invtrig.hpp new file mode 100644 index 00000000..cbc70246 --- /dev/null +++ b/include/kyosu/types/impl/complex/invtrig.hpp @@ -0,0 +1,540 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#pragma once + +#include + +namespace kyosu::_ +{ + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + // This implementation is a simd transcription and adaptation of the boost_math code + // which itself is a transcription of the pseudo-code in: + // + // "Implementing the complex Arcsine and Arccosine Functions using Exception Handling." + // T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang. + // ACM Transactions on Mathematical Software, Vol 23, No 3, Sept 1997. + // + auto [a0r, a0i] = a0; + using rtype = decltype(a0r); + const rtype a_crossover(1.5); + const rtype b_crossover(0.6417); + auto ltzra0 = eve::is_negative(a0r); + auto ltzia0 = eve::is_negative(a0i); + + // + // Begin by insuring real(a0) >= 0 and imag(a0) >= 0 : + // + + rtype x = eve::abs(a0r); + rtype y = eve::abs(a0i); + rtype proper_real = eve::asin(x); + auto lexone = (x <= eve::one(eve::as(a0r))); + auto is_proper_real = is_real(a0) && lexone; + + auto s_min = eve::sqrtsmallestposval(eve::as(x))*4; + auto s_max = eve::sqrtvalmax(eve::as(x))/8; + rtype xp1 = eve::inc(x); + rtype xm1 = eve::dec(x); + auto not_in_safe_zone = (((x > s_max) || (x < s_min)) || ((y > s_max) || (y < s_min))); + //compute for safe zone + rtype r, i; + rtype yy = eve::sqr(y); + rtype tr = eve::sqrt(eve::sqr(xp1) + yy);//hypot for pedantic ? + rtype ts = eve::sqrt(eve::sqr(xm1) + yy);//hypot for pedantic ? + rtype a = eve::average(tr, ts); + rtype b = x/a; + //compute r for b > b_crossover + rtype apx = a + x; + r = eve::if_else(lexone, + eve::atan(x/eve::sqrt(eve::half(eve::as(a0r))*apx*(yy/(tr+xp1)+(ts-xm1)))), + eve::atan(x/(y*eve::sqrt(eve::half(eve::as(a0r))*(apx/(tr+xp1)+apx/(ts+xm1))))) + ); + // r is computed + r = eve::if_else((b <= b_crossover), r, eve::asin(b)); + //compute am 1 temporary for i for a <= a_crossover + rtype tmp = yy/(tr+xp1); + rtype am1 = eve::if_else(lexone, + eve::average(tmp, yy/(ts-xm1)), + eve::average(tmp, (ts+xm1))); + i = eve::if_else((a <= a_crossover), + eve::log1p(am1 + eve::sqrt(am1*(eve::inc(a)))), + eve::log(a + eve::sqrt(eve::dec(eve::sqr(a)))) + ); + // i is computed + //compute for exception zone + if (eve::any(not_in_safe_zone)) + { + auto zone1 = (y <= eve::eps(eve::as(a0r))*eve::abs(xm1)); + if (eve::any(eve::logical_and(zone1, not_in_safe_zone))) + { + rtype rr = eve::if_else(lexone, eve::asin(x), eve::pio_2(eve::as(a0r))); + rtype ii = eve::if_else(lexone, y/eve::sqrt(xp1*xm1), + eve::if_else((eve::valmax(eve::as(a0r))/xp1 > xm1), + eve::log1p(xm1 + eve::sqrt(xp1*xm1)), + eve::log_2(eve::as(a0r)) + eve::log(x) + ) + ); + r = eve::if_else(zone1, rr, r); + i = eve::if_else(zone1, ii, i); + } + auto zone2 = (y <= s_min); + auto not_treated = eve::logical_notand(zone1, not_in_safe_zone); + if (eve::any(eve::logical_and(zone2, not_treated))) + { + r = eve::if_else(zone2, eve::pio_2(eve::as(a0r)) - eve::sqrt(y), r); + i = eve::if_else(zone2, eve::sqrt(y), i); + } + auto zone3 = (eve::dec(eve::eps(eve::as(a0r))*y) >= x); + not_treated = eve::logical_notand(zone2, not_treated); + if (eve::any(eve::logical_and(zone3, not_treated))) + { + r = eve::if_else(zone3, x/y, r); + i = eve::if_else(zone3, eve::log_2(eve::as(a0r)) + eve::log(y), i); + } + auto zone4 = (x > eve::one(eve::as(a0r))); + not_treated = eve::logical_notand(zone3, not_treated); + if (eve::any(eve::logical_and(zone4, not_treated))) + { + r = eve::if_else(zone4, eve::atan(x/y), r); + i = eve::if_else(zone4, eve::log_2(eve::as(a0r)) + eve::log(y) + + eve::half(eve::as(a0r))*eve::log1p(eve::sqr(x/y)), i); + } + not_treated = eve::logical_notand(zone4, not_treated); + if (eve::any(not_treated)) + { + rtype aa = eve::sqrt(eve::inc(eve::sqr(y))); + r = eve::if_else(not_treated, x/aa, r); + i = eve::if_else(not_treated,eve::half(eve::as(a0r))*eve::log1p(2*y*(y+aa)), i); + } + } + if (eve::any(is_not_finite(a0))) + { + auto nanx = eve::is_nan(x); + auto nany = eve::is_nan(y); + auto infx = (x == eve::inf(eve::as(a0r))) ; + auto infy = (y == eve::inf(eve::as(a0r))) ; + if (eve::any(nanx)) + { + r = eve::if_else(nanx, x, r); + r = eve::if_else(nanx && infy, x, r); + i = eve::if_else(nanx, x, i); + i = eve::if_else(nanx && infy, y, i); + } + if (eve::any(nany)) + { + auto isimag = is_imag(a0); + r = eve::if_else(isimag && nany, eve::zero, r); + r = eve::if_else(eve::logical_and(nany, infx),y, r); + i = eve::if_else(isimag && nany, eve::allbits, i); + i = eve::if_else(nany && infx, x, i); + } + auto test = eve::logical_notand(eve::logical_or(nanx, nany), infx); + if (eve::any(test)) + { + r = eve::if_else(infx && test, + eve::if_else(infy, eve::pio_4(eve::as(a0r)), eve::pio_2(eve::as(a0r))), + r); + } + test = eve::logical_notand(eve::is_nan(x) || eve::is_nan(y), + eve::logical_andnot(infy, infx)); + r = eve::if_else(test,eve::zero,r); + } + // use proper real results + + r = eve::if_else(is_proper_real, proper_real, r); + i = eve::if_else(is_proper_real, eve::zero, i); + // restore signs + r = eve::if_else(ltzra0, -r, r); + i = eve::if_else(ltzia0, -i, i); + return to_complex(r, i); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + // This implementation is a simd transcription and adaptation of the boost_math code + // which itself is a transcription of the pseudo-code in: + // + // "Implementing the complex Arcsine and Arccosine Functions using Exception Handling." + // T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang. + // ACM Transactions on Mathematical Software, Vol 23, No 3, Sept 1997. + // + auto [a0r, a0i] = a0; + using rtype = decltype(a0r); + const rtype a_crossover(1.5); + const rtype b_crossover(0.6417); + auto ltzra0 = eve::is_negative(a0r); + auto gtzia0 = eve::is_positive(a0i); + // + // Begin by insuring a0r >= 0 and imag(a0) >= 0 : + // + rtype x = eve::abs(a0r); + rtype y = eve::abs(a0i); + rtype proper_real = eve::acos(x); + auto lexone = (x <= eve::one(eve::as(x))); + auto is_proper_real = eve::logical_and(is_real(a0), lexone); + + auto s_min = eve::sqrtsmallestposval(eve::as(x))*4; + auto s_max = eve::sqrtvalmax(eve::as(x))/8; + rtype xp1 = eve::inc(x); + rtype xm1 = eve::dec(x); + auto not_in_safe_zone = (((x > s_max) || (x < s_min)) || ((y > s_max) || (y < s_min))); + //compute for safe zone + rtype r, i; + rtype yy = eve::sqr(y); + rtype tr = eve::sqrt(eve::sqr(xp1) + yy); //hypot for pedantic ? + rtype ts = eve::sqrt(eve::sqr(xm1) + yy); //hypot for pedantic ? + rtype a = eve::average(tr, ts); + rtype b = x/a; + //compute r for b > b_crossover + rtype apx = a + x; + r = eve::if_else(lexone, + eve::atan(eve::sqrt(eve::half(eve::as(x))*apx*(yy/(tr+xp1)+(ts-xm1)))/x), + eve::atan((y*eve::sqrt(eve::half(eve::as(x))*(apx/(tr+xp1)+apx/(ts+xm1))))/x) + ); + // r is computed + r = eve::if_else((b <= b_crossover), eve::acos(b), r); + //compute am1 temporary for i for a <= a_crossover + rtype tmp = yy/(tr+xp1); + rtype am1 = eve::if_else(lexone, + eve::average(tmp, yy/(ts-xm1)), + eve::average(tmp, (ts+xm1))); + i = eve::if_else((a <= a_crossover), + eve::log1p(am1 + eve::sqrt(am1*(eve::inc(a)))), + eve::log(a + eve::sqrt(eve::dec(eve::sqr(a)))) + ); + // i is computed + //compute for exception zone + if (eve::any(not_in_safe_zone)) + { + auto zone1 = (y <= eve::eps(eve::as(x))*eve::abs(xm1)); + if (eve::any(eve::logical_and(zone1, not_in_safe_zone))) + { + rtype rr = eve::if_else(lexone, proper_real, eve::zero); + rtype ii = eve::if_else(lexone, y/eve::sqrt(-xp1*xm1), + eve::if_else((eve::valmax(eve::as(x))/xp1 > xm1), + eve::log1p(xm1 + eve::sqrt(xp1*xm1)), + eve::log_2(eve::as(x)) + eve::log(x) + ) + ); + r = eve::if_else(zone1, rr, r); + i = eve::if_else(zone1, ii, i); + } + auto zone2 = (y <= s_min); + auto not_treated = eve::logical_notand(zone1, not_in_safe_zone); + if (eve::any(eve::logical_and(zone2, not_treated))) + { + rtype sqrty = eve::sqrt(y); + r = eve::if_else(zone2, sqrty, r); + i = eve::if_else(zone2, sqrty, i); + } + auto zone3 = (eve::dec(eve::eps(eve::as(x))*y) >= x); + not_treated = eve::logical_notand(zone2, not_treated); + if (eve::any(eve::logical_and(zone3, not_treated))) + { + r = eve::if_else(zone3, eve::pio_2(eve::as(x)), r); + i = eve::if_else(zone3, eve::log_2(eve::as(x)) + eve::log(y), i); + } + auto zone4 = (x > eve::one(eve::as(x))); + not_treated = eve::logical_notand(zone3, not_treated); + if (eve::any(eve::logical_and(zone4, not_treated))) + { + r = eve::if_else(zone4, eve::atan(y/x), r); + i = eve::if_else(zone4, eve::log_2(eve::as(x)) + eve::log(y) + eve::half(eve::as(x))*eve::log1p(eve::sqr(x/y)), i); + } + not_treated = eve::logical_notand(zone4, not_treated); + if (eve::any(not_treated)) + { + rtype aa = eve::sqrt(eve::inc(sqr(y))); + r = eve::if_else(not_treated, eve::pio_2(eve::as(x)), r); + i = eve::if_else(not_treated, eve::half(eve::as(x))*eve::log1p(2*y*(y+aa)), i); + } + } + if (eve::any(kyosu::is_not_finite(a0))) + { + auto nanx = eve::is_nan(x); + auto nany = eve::is_nan(y); + auto infx = (x == eve::inf(eve::as(x))) ; + auto infy = (y == eve::inf(eve::as(x))) ; + if (eve::any(infx)) + { + r = eve::if_else(infx, eve::zero, r); + i = eve::if_else(infx, eve::inf(eve::as(x)), i); + r = eve::if_else(eve::logical_and(infx, infy), eve::pio_4(eve::as(x)), r); + i = eve::if_else(eve::logical_and(infx, infy), eve::inf(eve::as(x)), i); + + r = eve::if_else(eve::logical_and(infx, nany), y, r); + i = eve::if_else(eve::logical_and(infx, nany), eve::minf(eve::as(x)), i); + } + if (eve::any(nanx)) + { + r = eve::if_else(nanx, x, r); + i = eve::if_else(nanx, x, i); + i = eve::if_else(eve::logical_and(nanx, infy), y, i); + } + auto test = eve::logical_notand(eve::logical_or(infx, nanx), infy); + if (eve::any(test)) + { + r = eve::if_else(eve::logical_and(infy, test), eve::pio_2(eve::as(x)), r); + i = eve::if_else(eve::logical_and(infy, test), y, i); + } + test = eve::logical_notand(eve::logical_or(infx, nanx), nany); + r = eve::if_else(test,eve::if_else(is_imag(a0), eve::pio_2(eve::as(x)), y), r); + i = eve::if_else(test,y,i); + } + // use proper real results + r = eve::if_else(is_proper_real, proper_real, r); + i = eve::if_else(is_proper_real, eve::zero, i); + // restore signs + r = eve::if_else(ltzra0, eve::pi(eve::as(x))-r, r); + i = eve::if_else(gtzia0, -i, i); + return to_complex(r, i); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + // acosh(a0) = +/-i acos(a0) + // Choosing the sign of multiplier to give real(acosh(a0)) >= 0 + // we have compatibility with C99. + auto [r, i] = kyosu::acos(a0); + auto lez = eve::is_negative(i);; + auto res = to_complex(-i, r); + res = eve::if_else(lez, res, -res); + auto nani = is_nan(i); + if (eve::any(nani)) + return eve::if_else(nani && eve::is_finite(r) + , to_complex(eve::nan(eve::as(r)), eve::nan(eve::as(r))) + , res); + else + return res; + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + auto [r, i] = a0; + auto [r1, i1] = kyosu::asin(to_complex(-i, r)); + return to_complex(i1, -r1); // -(eve::i*asin(eve::i*z)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + // This implementation is a simd (i.e. no branch) transcription and adaptation of the + // boost_math code which itself is a transcription of the pseudo-code in: + // + // Eric W. Weisstein. "Inverse Hyperbolic Tangent." + // From MathWorld--A Wolfram Web Resource. + // http://mathworld.wolfram.com/InverseHyperbolicTangent.html + // + // Also: The Wolfram Functions Site, + // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/ + // + // Also "Abramowitz and Stegun. Handbook of Mathematical Functions." + // at : http://jove.prohosting.com/~skripty/toc.htm + // + auto [a0r, a0i] = a0; + auto realinf = eve::is_eqz(a0i) && eve::is_infinite(a0r); + using rtype = decltype(a0r); + const rtype alpha_crossover(0.3); + auto ltzra0 = eve::is_ltz(a0r); + auto ltzia0 = eve::is_ltz(a0i); + auto s_min = eve::sqrtsmallestposval(eve::as(a0r))*2; + auto s_max = eve::sqrtvalmax(eve::as(a0r))/2; + rtype const two = rtype(2); + rtype inf = eve::inf(eve::as(a0r)); + rtype x = eve::abs(a0r); + rtype y = eve::abs(a0i); + auto special = eve::is_eqz(y) && (x < eve::one(eve::as(a0r))); + auto sr = eve::atanh(a0r); + if (eve::all(special)) { + return to_complex(sr, eve::zero(eve::as(sr))); + } + + rtype r = eve::zero(eve::as(a0r)); + rtype i = eve::zero(eve::as(a0r)); + auto gtxmax = (x > s_max); + auto ltxmin = (x < s_min); + auto gtymax = (y > s_max); + auto ltymin = (y < s_min); + rtype xx = eve::sqr(x); + rtype yy = eve::sqr(y); + rtype sqrabs = xx + yy; + + auto not_in_safe_zone = ((gtxmax || ltxmin) || (gtymax || ltymin)); + if(eve::any(not_in_safe_zone)) + { + //treat underflow or overflow + // one or both of x and y are small, calculate divisor carefully: + rtype div = eve::one(eve::as(a0r)); + div += eve::if_else(ltxmin, xx, eve::zero); + div += eve::if_else(ltxmin, yy, eve::zero); + + rtype alpha = x/div; + alpha += alpha; + + auto test = gtymax; + // big y, medium x, divide through by y: + rtype tmp_alpha = (two*x/y) / (y + xx/y); + // small x and y, whatever alpha is, it's too small to calculate: + tmp_alpha = eve::if_else(x > eve::one(eve::as(a0r)), tmp_alpha, eve::zero); + alpha = eve::if_else(test && (x > eve::one(eve::as(a0r))), tmp_alpha, alpha); + + test = eve::logical_andnot(gtxmax, test); + + // big x small y, as above but neglect y^2/x: + tmp_alpha = two/x; + // big x: divide through by x: + tmp_alpha = eve::if_else((y > eve::one(eve::as(a0r))), two / (x + y*y/x), tmp_alpha); + // big x and y: divide alpha through by x*y: + tmp_alpha = eve::if_else(gtymax, (two/y) / (x/y + y/x), tmp_alpha); + // x or y are infinite: the result is 0 + tmp_alpha = eve::if_else((y == inf) || (x == inf), eve::zero, tmp_alpha); + + alpha = eve::if_else(test, tmp_alpha, alpha); + r = eve::if_else((alpha < alpha_crossover), + eve::log1p(alpha) - eve::log1p(-alpha), + eve::log(inc(two*x + xx)) - eve::log(sqr(dec(x))) + ); + test = (x == eve::one(eve::as(a0r))) && ltymin; + r = eve::if_else(test, -(two*(eve::log(y) - eve::log_2(eve::as(a0r)))), r); + r *= rtype(0.25); + //compute the imag part + // y^2 is negligible: + i = eve::atan2(two*y, eve::oneminus(xx)); + i = eve::if_else(gtymax || gtxmax, eve::pi(eve::as(a0r)), i); + rtype tmp_i = eve::if_else(ltymin, eve::atan2(two*y, eve::one(eve::as(a0r))), + eve::atan2(two*y, eve::oneminus(yy))); + i = eve::if_else(ltxmin, tmp_i, i); + } + auto test0 = (inf == x) && (inf == y); + if(eve::any(test0)) + { + //inf x, inf y + r = eve::if_else(test0, eve::zero, r); + i = eve::if_else(test0, eve::pi(eve::as(a0r)), r); + } + auto test = kyosu::is_nan(a0); + + if(eve::any(test)) + { + //nan x, inf y + r = eve::if_else(eve::is_nan(x) && (y == inf), eve::zero, r); + i = eve::if_else(eve::is_nan(x) && (y == inf), eve::pi(eve::as(a0r)), r); + + r = eve::if_else(is_nan(y) && (x == inf), eve::zero, r); + i = eve::if_else(is_nan(y) && (x == inf), y, i); + + r = eve::if_else(is_nan(y) && eve::is_eqz(x), eve::zero, r); + i = eve::if_else(is_nan(y) && is_eqz(x), eve::allbits, i); + } + //compute for safe zone::one + // the real part is given by: + // + // eve::real(atanh(z)) == eve::log((1 + x^2 + y^2 + 2x) / (1 + x^2 + y^2 - 2x)) + // + // however, when x is either large (x > 1/e) or very small + // (x < e) then this effectively simplifies + // to log(1), leading to wildly inaccurate results. + // by dividing the above (top and bottom) by (1 + x^2 + y^2) we get: + // + // eve::real(atanh(z)) == log((1 + (2x / (1 + x^2 + y^2))) / (1 - (-2x / (1 + x^2 + y^2)))) + // + // which is much more sensitive to the value of x, when x is not near 1 + // (remember we can compute log(1+x) for small x very accurately). + // + // the cross-over from eve::one method to the other has to be determined + // experimentally, the value used below appears correct to within a + // factor of 2 (and there are larger errors from other parts + // of the input domain anyway). + // + rtype alpha = x*two / (eve::inc(sqrabs)); + rtype sqrxm1 = eve::sqr(eve::dec(x)); + rtype tmp_r = eve::if_else((alpha < alpha_crossover), + eve::log1p(alpha) - eve::log1p(-alpha), + eve::log1p(x+x + sqrabs) - eve::log(sqrxm1 + yy) + )*rtype(0.25); + r = eve::if_else(not_in_safe_zone, r, tmp_r); + + // compute the imag part + i = eve::if_else(not_in_safe_zone, + i, + eve::atan2(y+y, (eve::oneminus(sqrabs))) + )*eve::half(eve::as(a0r)); + + r = eve::if_else( ltzra0,-r, r); + i = eve::if_else(eve::is_infinite(y), eve::pio_2(eve::as(a0r))*eve::sign(y), i); + i = eve::if_else( ltzia0,-i, i); + r = eve::if_else(realinf, eve::zero(eve::as(a0r)), r); + i = eve::if_else(realinf, -eve::sign(a0r)*eve::pio_2(eve::as(a0r)), i); + r = eve::if_else(special, sr, r); + i = eve::if_else(special, eve::zero, i); + return to_complex(r, i); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + + // C99 definition here; atan(z) = -i atanh(iz): + auto [r, i] = a0; + auto [r1, i1] = kyosu::atanh(to_complex(-i, r)); + return to_complex(i1, -r1); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& z) noexcept + { + return kyosu::acos(kyosu::rec(z)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& z) noexcept + { + return kyosu::asin(kyosu::rec(z)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& z) noexcept + { + return kyosu::acosh(kyosu::rec(z)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& z) noexcept + { + return kyosu::asinh(kyosu::rec(z)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + return kyosu::atan(kyosu::rec(a0)); + } + + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& a0) noexcept + { + return kyosu::atanh(kyosu::rec(a0)); + } +} diff --git a/include/kyosu/types/impl/trigo.hpp b/include/kyosu/types/impl/trigo.hpp index a2c3d651..78b7b30a 100644 --- a/include/kyosu/types/impl/trigo.hpp +++ b/include/kyosu/types/impl/trigo.hpp @@ -273,5 +273,12 @@ namespace kyosu::_ } } - + template + KYOSU_FORCEINLINE constexpr + auto dispatch(eve::tag_of const&, C const& z) noexcept + { + auto s = kyosu::sin(z); + using u_t = eve::underlying_type_t; + return kyosu::if_else(kyosu::abs(z) < eve::eps(eve::as(u_t())), eve::one(eve::as(u_t())), s/z); + } } diff --git a/test/unit/complex/acos.cpp b/test/unit/complex/acos.cpp new file mode 100644 index 00000000..fe51b494 --- /dev/null +++ b/test/unit/complex/acos.cpp @@ -0,0 +1,113 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +template < typename T > +auto cv(std::complex < T > const &sc) +{ + return kyosu::to_complex(sc.real(), sc.imag()); +} + +TTS_CASE_WITH( "Check behavior of acos on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10), tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = typename T::value_type; + using c_t = std::complex; + using kc_t = kyosu::as_complex_t; + for(size_t i = 0; i < a0.size(); ++i) + { + auto e = a0[i]; + auto f = a1[i]; + + TTS_RELATIVE_EQUAL(kyosu::acos(kc_t(e, f)), cv(std::acos(c_t(e, f))), 1.0e-6); + } +}; + +TTS_CASE_WITH( "Check behavior of acos on wide" + , kyosu::simd_real_types + , tts::generate( tts::between(-10, 10) + , tts::between(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = T; + using ke_t = kyosu::as_complex_t; + using c_t = std::complex>; + ke_t e([&](auto i, auto){return cv(std::acos(c_t(a0.get(i), a1.get(i)))); }); + TTS_RELATIVE_EQUAL(kyosu::acos(ke_t{a0,a1}), e, 1.0e-6); +}; + +TTS_CASE_TPL( "Check acos lilits", kyosu::real_types) +(tts::type) +{ + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + const int N = 22; + std::array inputs = + { tcx(eve::zero(as()),eve::zero(as())),//0* + tcx(eve::inf(as()),eve::zero(as())), //1* + tcx(eve::minf(as()),eve::zero(as())),//2* + tcx(eve::nan(as()),eve::zero(as())), //3* + tcx(eve::zero(as()),eve::inf(as())), //4* + tcx(eve::inf(as()),eve::inf(as())), //5* + tcx(eve::minf(as()),eve::inf(as())), //6* + tcx(eve::nan(as()),eve::inf(as())), //7* + tcx(eve::zero(as()),eve::minf(as())),//8* + tcx(eve::inf(as()),eve::minf(as())), //9* + tcx(eve::minf(as()),eve::minf(as())),//10* + tcx(eve::nan(as()),eve::minf(as())), //11* + tcx(eve::zero(as()),eve::nan(as())), //12* + tcx(eve::inf(as()),eve::nan(as())), //13* + tcx(eve::minf(as()),eve::nan(as())), //14* + tcx(eve::nan(as()),eve::nan(as())), //15* + tcx(eve::mzero(as()),eve::zero(as())),//16* + tcx(eve::one(as()),eve::inf(as())), //17* + tcx(eve::one(as()),eve::nan(as())), //18* + tcx(eve::minf(as()),eve::one(as())), //19* + tcx(eve::inf(as()),eve::one(as())), //20* + tcx(eve::nan(as()),eve::one(as())), //21* + }; + std::array expected = + {tcx( eve::pio_2(as()) ,eve::zero(as())), //0* + tcx( eve::zero(as()) , eve::minf(as())), //1* + tcx( eve::pi(as()) , eve::minf(as())), //2* + tcx( eve::nan(as()) , eve::nan(as())), //3* + tcx( eve::pio_2(as()) , eve::minf(as())), //4* + tcx( eve::pio_4(as()) , eve::minf(as())), //5* + tcx( 3*eve::pio_4(as()), eve::minf(as())) , //6* + tcx( eve::nan(as()) , eve::minf(as())), //7* + tcx( eve::pio_2(as()) , eve::inf(as())), //8* + tcx( eve::pio_4(as()) , eve::inf(as())), //9* + tcx( 3*eve::pio_4(as()), eve::inf(as())) , //10* + tcx( eve::nan(as()) , eve::inf(as())), //11* + tcx( eve::pio_2(as()) , eve::nan(as())), //12 + tcx( eve::nan(as()) , eve::minf(as())), //13* + tcx( eve::nan(as()) , eve::minf(as())), //14* + tcx( eve::nan(as()) , eve::nan(as())), //15* + tcx( eve::pio_2(as()) ,eve::zero(as())), //16* + tcx( eve::pio_2(as()) , eve::minf(as())), //17* + tcx( eve::nan(as()) , eve::nan(as())), //18* + tcx( eve::pi(as()) , eve::minf(as())), //19* + tcx( eve::zero(as()) , eve::minf(as())), //20* + tcx( eve::nan(as()) , eve::nan(as())), //21 + }; + + + for(int i=0; i < N; ++i) + { + TTS_IEEE_EQUAL(kyosu::acos(inputs[i]), expected[i]); + TTS_IEEE_EQUAL(kyosu::acos(kyosu::conj(inputs[i])), kyosu::conj(expected[i])); + } +}; diff --git a/test/unit/complex/acosh.cpp b/test/unit/complex/acosh.cpp new file mode 100644 index 00000000..913310f4 --- /dev/null +++ b/test/unit/complex/acosh.cpp @@ -0,0 +1,112 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +template < typename T > +auto cv(std::complex < T > const &sc) +{ + return kyosu::to_complex(sc.real(), sc.imag()); +} + +TTS_CASE_WITH( "Check behavior of acosh on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10), tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = typename T::value_type; + using c_t = std::complex; + using kc_t = kyosu::as_complex_t; + for(size_t i = 0; i < a0.size(); ++i) + { + auto e = a0[i]; + auto f = a1[i]; + + TTS_RELATIVE_EQUAL(kyosu::acosh(kc_t(e, f)), cv(std::acosh(c_t(e, f))), 1.0e-6); + } +}; + +TTS_CASE_WITH( "Check behavior of acosh on wide" + , kyosu::simd_real_types + , tts::generate( tts::between(-10, 10) + , tts::between(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = T; + using ke_t = kyosu::as_complex_t; + using c_t = std::complex>; + ke_t e([&](auto i, auto){return cv(std::acosh(c_t(a0.get(i), a1.get(i)))); }); + TTS_RELATIVE_EQUAL(kyosu::acosh(ke_t{a0,a1}), e, 1.0e-6); +}; + +TTS_CASE_TPL( "Check acosh lilits", kyosu::real_types) +(tts::type) +{ + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + const int N = 22; + std::array inputs = + { tcx(eve::zero(as()),eve::zero(as())),//0* + tcx(eve::inf(as()),eve::zero(as())), //1* + tcx(eve::minf(as()),eve::zero(as())),//2* + tcx(eve::nan(as()),eve::zero(as())), //3* + tcx(eve::zero(as()),eve::inf(as())), //4* + tcx(eve::inf(as()),eve::inf(as())), //5* + tcx(eve::minf(as()),eve::inf(as())), //6* + tcx(eve::nan(as()),eve::inf(as())), //7* + tcx(eve::zero(as()),eve::minf(as())),//8* + tcx(eve::inf(as()),eve::minf(as())), //9* + tcx(eve::minf(as()),eve::minf(as())),//10* + tcx(eve::nan(as()),eve::minf(as())), //11* + tcx(eve::zero(as()),eve::nan(as())), //12* + tcx(eve::inf(as()),eve::nan(as())), //13* + tcx(eve::minf(as()),eve::nan(as())), //14* + tcx(eve::nan(as()),eve::nan(as())), //15* + tcx(eve::mzero(as()),eve::zero(as())),//16* + tcx(eve::one(as()),eve::inf(as())), //17* + tcx(eve::one(as()),eve::nan(as())), //18* + tcx(eve::minf(as()),eve::one(as())), //19* + tcx(eve::inf(as()),eve::one(as())), //20* + tcx(eve::nan(as()),eve::one(as())), //21* + }; + std::array expected = + { tcx(eve::zero(as()),eve::pio_2(as())), //0* + tcx(eve::inf(as()),eve::zero(as())), //1* + tcx(eve::inf(as()),eve::pi(as())), //2* + tcx(eve::nan(as()),eve::nan(as())), //3* + tcx(eve::inf(as()),eve::pio_2(as())), //4* + tcx(eve::inf(as()),eve::pio_4(as())), //5* + tcx(eve::inf(as()),3*eve::pio_4(as())),//6* + tcx(eve::inf(as()),eve::nan(as())), //7* + tcx(eve::inf(as()),-eve::pio_2(as())), //8* + tcx(eve::inf(as()),-eve::pio_4(as())), //9* + tcx(eve::inf(as()),-3*eve::pio_4(as())),//10* + tcx(eve::inf(as()),eve::nan(as())), //11* + tcx(eve::nan(as()),eve::nan(as())), //12 + tcx(eve::inf(as()),eve::nan(as())), //13* + tcx(eve::inf(as()),eve::nan(as())), //14* + tcx(eve::nan(as()),eve::nan(as())), //15* + tcx(eve::zero(as()),eve::pio_2(as())), //16* + tcx(eve::inf(as()),eve::pio_2(as())), //17* + tcx(eve::nan(as()),eve::nan(as())), //18* + tcx(eve::inf(as()),eve::pi(as())), //19* + tcx(eve::inf(as()),eve::zero(as())), //20* + tcx(eve::nan(as()),eve::nan(as())), //21 + }; + + for(int i=0; i < N; ++i) + { + TTS_IEEE_EQUAL(kyosu::acosh(inputs[i]), expected[i]); + TTS_IEEE_EQUAL(kyosu::acosh(kyosu::conj(inputs[i])), kyosu::conj(expected[i])); + } +}; diff --git a/test/unit/complex/acot.cpp b/test/unit/complex/acot.cpp new file mode 100644 index 00000000..47c0f292 --- /dev/null +++ b/test/unit/complex/acot.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of acot on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::acot(z), kyosu::atan(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of acot on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::acot(z), kyosu::atan(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/acoth.cpp b/test/unit/complex/acoth.cpp new file mode 100644 index 00000000..9873b445 --- /dev/null +++ b/test/unit/complex/acoth.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of acoth on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::acoth(z), kyosu::atanh(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of acoth on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::acoth(z), kyosu::atanh(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/acsc.cpp b/test/unit/complex/acsc.cpp new file mode 100644 index 00000000..2ec7fd26 --- /dev/null +++ b/test/unit/complex/acsc.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of acsc on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::acsc(z), kyosu::asin(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of acsc on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::acsc(z), kyosu::asin(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/acsch.cpp b/test/unit/complex/acsch.cpp new file mode 100644 index 00000000..ec1f4d79 --- /dev/null +++ b/test/unit/complex/acsch.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of acsch on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::acsch(z), kyosu::asinh(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of acsch on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::acsch(z), kyosu::asinh(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/asec.cpp b/test/unit/complex/asec.cpp new file mode 100644 index 00000000..5cbfacb6 --- /dev/null +++ b/test/unit/complex/asec.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of asec on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::asec(z), kyosu::acos(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of asec on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::asec(z), kyosu::acos(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/asech.cpp b/test/unit/complex/asech.cpp new file mode 100644 index 00000000..87e97779 --- /dev/null +++ b/test/unit/complex/asech.cpp @@ -0,0 +1,39 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +TTS_CASE_WITH ( "Check behavior of asech on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10) + ) + ) + (T const& a0, T const& a1 ) +{ + for(auto e : a0) + { + for(auto f : a1) + { + auto z = kyosu::to_complex(e, f); + TTS_RELATIVE_EQUAL(kyosu::asech(z), kyosu::acosh(kyosu::rec(z)), 1.0e-50); + } + } +}; + +TTS_CASE_WITH( "Check behavior of asech on wide" + , kyosu::simd_real_types + , tts::generate( tts::randoms(-10, 10) + , tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1) +{ + auto z = kyosu::to_complex(a0, a1); + TTS_RELATIVE_EQUAL(kyosu::asech(z), kyosu::acosh(kyosu::rec(z)), 1.0e-5); +}; diff --git a/test/unit/complex/asin.cpp b/test/unit/complex/asin.cpp new file mode 100644 index 00000000..7570394f --- /dev/null +++ b/test/unit/complex/asin.cpp @@ -0,0 +1,136 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +template < typename T > +auto cv(std::complex < T > const &sc) +{ + return kyosu::to_complex(sc.real(), sc.imag()); +} + +TTS_CASE_WITH( "Check behavior of asin on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10), tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = typename T::value_type; + using c_t = std::complex; + using kc_t = kyosu::as_complex_t; + for(size_t i = 0; i < a0.size(); ++i) + { + auto e = a0[i]; + auto f = a1[i]; + + TTS_RELATIVE_EQUAL(kyosu::asin(kc_t(e, f)), cv(std::asin(c_t(e, f))), 1.0e-4); + } +}; + +TTS_CASE_WITH( "Check behavior of asin on wide" + , kyosu::simd_real_types + , tts::generate( tts::between(-10, 10) + , tts::between(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = T; + using ke_t = kyosu::as_complex_t; + using c_t = std::complex>; + ke_t e([&](auto i, auto){return cv(std::asin(c_t(a0.get(i), a1.get(i)))); }); + TTS_RELATIVE_EQUAL(kyosu::asin(ke_t{a0,a1}), e, 1.0e-6); +}; + + +auto sc( auto ec) +{ + return std::complex(kyosu::real(ec), kyosu::imag(ec)); +} + +TTS_CASE_TPL( "Check asin lilits", kyosu::scalar_real_types) +(tts::type) +{ + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + const int N = 12; + auto zer = eve::zero(as()); + auto mzer = eve::mzero(as()); + auto inf = eve::inf(as()); + auto nan = eve::nan(as()); + auto one = eve::one(as()); + + std::array inputs = + { + c_t(zer,zer), //0 + c_t(mzer,zer),//1 + c_t(one,inf), //2 + c_t(one,nan), //3 + c_t(inf,one), //4 + c_t(inf,inf), //5 + c_t(inf,nan), //6 + c_t(inf,one), //7 + c_t(nan,zer), //8 + c_t(nan, one),//9 + c_t(nan,inf), //10 + c_t(nan,nan), //11 + }; + + using kyosu::conj; + using kyosu::asin; + + auto test_asin = [](auto z){ return cv(std::asin(sc(z))); }; + for(int i=0; i < N; ++i) + { + TTS_IEEE_EQUAL(asin(inputs[i]), test_asin(inputs[i])); + TTS_IEEE_EQUAL(asin(-inputs[i]), -asin(inputs[i])); + TTS_IEEE_EQUAL(asin(conj(inputs[i])), conj(asin(inputs[i]))); + } +}; + +TTS_CASE_TPL( "Check corner casesof eve::asin", kyosu::scalar_real_types) +(tts::type) +{ + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + const int N = 12; + auto zer = eve::zero(as()); + auto mzer = eve::mzero(as()); + auto inf = eve::inf(as()); + auto nan = eve::nan(as()); + auto one = eve::one(as()); + std::array inputs = + { + c_t(zer,zer), //0 + c_t(mzer,zer),//1 + c_t(one,inf), //2 + c_t(one,nan), //3 + c_t(inf,one), //4 + c_t(inf,inf), //5 + c_t(inf,nan), //6 + c_t(inf,one), //7 + c_t(nan,zer), //8 + c_t(nan, one),//9 + c_t(nan,inf), //10 + c_t(nan,nan), //11 + }; + using kyosu::conj; + using kyosu::asin; + + auto test_asin = [](auto z){ return cv(std::asin(sc(z))); }; + for(int i=0; i < N; ++i) + { + TTS_IEEE_EQUAL(asin(inputs[i]), test_asin(inputs[i])); + TTS_IEEE_EQUAL(asin(-inputs[i]), -asin(inputs[i])); + TTS_IEEE_EQUAL(asin(conj(inputs[i])), conj(asin(inputs[i]))); + } +}; diff --git a/test/unit/complex/asinh.cpp b/test/unit/complex/asinh.cpp new file mode 100644 index 00000000..ad32ee66 --- /dev/null +++ b/test/unit/complex/asinh.cpp @@ -0,0 +1,106 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +template < typename T > +auto cv(std::complex < T > const &sc) +{ + return kyosu::to_complex(sc.real(), sc.imag()); +} + +TTS_CASE_WITH( "Check behavior of asinh on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10), tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = typename T::value_type; + using c_t = std::complex; + using kc_t = kyosu::as_complex_t; + for(size_t i = 0; i < a0.size(); ++i) + { + auto e = a0[i]; + auto f = a1[i]; + + TTS_RELATIVE_EQUAL(kyosu::asinh(kc_t(e, f)), cv(std::asinh(c_t(e, f))), 1.0e-4); + } +}; + +TTS_CASE_WITH( "Check behavior of asinh on wide" + , kyosu::simd_real_types + , tts::generate( tts::between(-10, 10) + , tts::between(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = T; + using ke_t = kyosu::as_complex_t; + using c_t = std::complex>; + ke_t e([&](auto i, auto){return cv(std::asinh(c_t(a0.get(i), a1.get(i)))); }); + TTS_RELATIVE_EQUAL(kyosu::asinh(ke_t{a0,a1}), e, 1.0e-5); +}; + +TTS_CASE_TPL( "Check asinh lilits", kyosu::real_types) +(tts::type) +{ + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + + auto zer = eve::zero(as()); + auto mzer = eve::mzero(as()); + auto inf = eve::inf(as()); + auto minf = eve::minf(as()); + auto nan = eve::nan(as()); + auto one = eve::one(as()); + auto pio_2 = eve::pio_2(as()); + auto pio_4 = eve::pio_4(as()); + const int N = 12; + std::array inputs = + { + tcx(zer,zer), //0 + tcx(mzer,zer),//1 + tcx(one,inf), //2 + tcx(one,nan), //3 + tcx(inf,one), //4 + tcx(inf,inf), //5 + tcx(inf,nan), //6 + tcx(inf,one), //7 + tcx(nan,zer),//8 + tcx(nan, one), //9 + tcx(nan,inf), //10 + tcx(nan,nan), //11 + }; + + std::array expected = + { + tcx(zer,zer), //0 + tcx(mzer,zer),//1 + tcx(inf, pio_2), //2 + tcx(nan,nan),//3 + tcx(inf,zer), //4 + tcx(inf,pio_4), //5 + tcx(inf,nan), //6 + tcx(inf,zer), //7 + tcx(nan,zer),//8 + tcx(nan, nan), //9 + tcx(minf, nan), //10 + tcx(nan,nan), //11 + }; + + + for(int i=0; i < N; ++i) + { + TTS_IEEE_EQUAL(kyosu::asinh(inputs[i]), expected[i]); + TTS_IEEE_EQUAL(kyosu::asinh(-inputs[i]), -kyosu::asinh(inputs[i])); + TTS_IEEE_EQUAL(kyosu::asinh(kyosu::conj(inputs[i])), kyosu::conj(expected[i])); + } +}; diff --git a/test/unit/complex/atanh.cpp b/test/unit/complex/atanh.cpp new file mode 100644 index 00000000..6264724a --- /dev/null +++ b/test/unit/complex/atanh.cpp @@ -0,0 +1,99 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include +#include + +template < typename T > +auto cv(std::complex < T > const &sc) +{ + return kyosu::to_complex(sc.real(), sc.imag()); +} + +TTS_CASE_WITH( "Check behavior of atanh on scalar" + , tts::bunch + , tts::generate( tts::randoms(-10, 10), tts::randoms(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = typename T::value_type; + using c_t = std::complex; + using kc_t = kyosu::as_complex_t; + for(size_t i = 0; i < a0.size(); ++i) + { + auto e = a0[i]; + auto f = a1[i]; + + TTS_RELATIVE_EQUAL(kyosu::atanh(kc_t(e, f)), cv(std::atanh(c_t(e, f))), 1.0e-4); + } +}; + +TTS_CASE_WITH( "Check behavior of atanh on wide" + , kyosu::simd_real_types + , tts::generate( tts::between(-10, 10) + , tts::between(-10, 10)) + ) + (T const& a0, T const& a1 ) +{ + using e_t = T; + using ke_t = kyosu::as_complex_t; + using c_t = std::complex>; + ke_t e([&](auto i, auto){return cv(std::atanh(c_t(a0.get(i), a1.get(i)))); }); + TTS_RELATIVE_EQUAL(kyosu::atanh(ke_t{a0,a1}), e, 1.0e-6); +}; + + +auto sc( auto ec) +{ + return std::complex(kyosu::real(ec), kyosu::imag(ec)); +} + +TTS_CASE_TPL( "Check corner casesof eve::atanh", kyosu::scalar_real_types) + (tts::type) +{ + if constexpr(spy::operating_system != spy::macos_) + { + using e_t = T; + auto tcx = kyosu::to_complex; + using c_t = decltype(tcx(e_t(0))); + using eve::as; + const int N = 16; + std::array inputs = + { c_t(eve::zero(as()),eve::zero(as())),//0 + c_t(eve::inf(as()),eve::zero(as())), //1 + c_t(eve::minf(as()),eve::zero(as())),//2 + c_t(eve::nan(as()),eve::zero(as())), //3 + c_t(eve::zero(as()),eve::inf(as())), //4 + c_t(eve::inf(as()),eve::inf(as())), //5 + c_t(eve::minf(as()),eve::inf(as())), //6 + c_t(eve::nan(as()),eve::inf(as())), //7 + c_t(eve::zero(as()),eve::minf(as())),//8-- + c_t(eve::inf(as()),eve::minf(as())), //9 + c_t(eve::minf(as()),eve::minf(as())),//10 + c_t(eve::nan(as()),eve::minf(as())), //11 + c_t(eve::zero(as()),eve::nan(as())), //12 + c_t(eve::inf(as()),eve::nan(as())), //13 + c_t(eve::minf(as()),eve::nan(as())), //14 + c_t(eve::nan(as()),eve::nan(as())), //15 + }; + + for(int i=0; i < N; ++i) + { + if((i != 2 && i != 1)){ + // this curious test corresponds to the fact that neither std::atanh nor std::atanh are correct for inputs (0, inf) or (0, -inf) + // peculiarly they contredict the C99 specification that atanh is odd + // atanh should behave "the same as C99 function catanh, defined in subclause 7.3.6.3 and G.5.2.3." + // the if clause has to be removed if/when libc++ will be corrected + TTS_IEEE_EQUAL(kyosu::atanh(inputs[i]), cv(std::atanh(sc(inputs[i])))); + TTS_IEEE_EQUAL(kyosu::atanh(-inputs[i]), cv(std::atanh(sc(-inputs[i])))); + } + } + } + else + TTS_PASS("no test for macosx: atanh corner cases are almost all false according C99"); +}; diff --git a/test/unit/function/sinc.cpp b/test/unit/function/sinc.cpp new file mode 100644 index 00000000..3c123e3c --- /dev/null +++ b/test/unit/function/sinc.cpp @@ -0,0 +1,49 @@ +//====================================================================================================================== +/* + Kyosu - Complex Without Complexes + Copyright : KYOSU Contributors & Maintainers + SPDX-License-Identifier: BSL-1.0 +*/ +//====================================================================================================================== +#include +#include + +# if __has_include () +# include +# define HAS_BOOST +# endif + +TTS_CASE_WITH ( "Check kyosu::sinc over real" + , kyosu::real_types + , tts::generate(tts::between(-10,10)) + ) +(auto data) +{ + TTS_ULP_EQUAL(kyosu::sinc(data), eve::sinc(data), 0.5); +}; + +#ifdef HAS_BOOST + +template < typename T > +auto cv(boost::math::quaternion const &bq) +{ + return kyosu::quaternion(bq.R_component_1(), bq.R_component_2(), + bq.R_component_3(), bq.R_component_4()); +} + +TTS_CASE_WITH ( "Check kyosu::sinc over quaternion" + , kyosu::simd_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 ke_t = kyosu::as_quaternion_t; + using bq_t = boost::math::quaternion>; + auto boost_sinc = [](auto x, auto y, auto z, auto t){return cv(boost::math::sin(bq_t(x, y, z, t))/bq_t(x, y, z, t)); }; + ke_t e([&](auto n, auto){return boost_sinc(r.get(n), i.get(n), j.get(n), k.get(n)); }); + auto q = ke_t(r,i,j,k); + TTS_RELATIVE_EQUAL(kyosu::sinc(q), e, 1e-5); +}; +# endif diff --git a/test/unit/quaternion/rotate_vec.cpp b/test/unit/quaternion/rotate_vec.cpp index 1150df53..e7e65f2f 100644 --- a/test/unit/quaternion/rotate_vec.cpp +++ b/test/unit/quaternion/rotate_vec.cpp @@ -19,18 +19,11 @@ TTS_CASE_WITH ( "Check behavior of rotate_vec on wide" ) (T const& a0, T const& a1, T const& a2, T const& a3 ) { - auto pr = [](auto name, auto v){std::cout << name << v[0] << ", " << v[1] << ", " << v[2] << std::endl; }; - { - std::array v{a0, a1, a2}; - auto rsq3 = eve::rec(eve::sqrt_3(eve::as(a3))); - std::array axis{rsq3, rsq3, rsq3}; - pr("axis = ", axis); - auto q = kyosu::from_angle_axis(eve::pi(eve::as(a0)), std::span(axis)); - auto vr = kyosu::rotate_vec(q, std::span(v), kyosu::normalize); - auto vr2= kyosu::rotate_vec(q, std::span(vr), kyosu::normalize); - pr("v = ", v); - pr("vr = ", vr); - pr("vr2 = ", vr2); - TTS_RELATIVE_EQUAL(v[0], vr2[0], 1.0e-4); - } + std::array v{a0, a1, a2}; + auto rsq3 = eve::rec(eve::sqrt_3(eve::as(a3))); + std::array axis{rsq3, rsq3, rsq3}; + auto q = kyosu::from_angle_axis(eve::pi(eve::as(a0)), std::span(axis)); + auto vr = kyosu::rotate_vec(q, std::span(v), kyosu::normalize); + auto vr2= kyosu::rotate_vec(q, std::span(vr), kyosu::normalize); + TTS_RELATIVE_EQUAL(v[0], vr2[0], 1.0e-4); };