/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * * Martin Renou * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XSIMD_GAMMA_HPP #define XSIMD_GAMMA_HPP #include "xsimd_basic_math.hpp" #include "xsimd_exponential.hpp" #include "xsimd_horner.hpp" #include "xsimd_logarithm.hpp" #include "xsimd_trigonometric.hpp" namespace xsimd { /** * Computes the gamma function of the batch \c x. * @param x batch of floating point values. * @return the gamma function of \c x. */ template batch_type_t tgamma(const simd_base& x); /** * Computes the natural logarithm of the gamma function of the batch \c x. * @param x batch of floating point values. * @return the natural logarithm of the gamma function of \c x. */ template batch_type_t lgamma(const simd_base& x); /************************* * tgamma implementation * *************************/ namespace detail { /* origin: boost/simd/arch/common/detail/generic/stirling_kernel.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ template struct stirling_kernel; template struct stirling_kernel { static inline B compute(const B& x) { return horner(x); } static inline B split_limit() { return B(detail::caster32_t(uint32_t(0x41d628f6)).f); } static inline B large_limit() { return B(detail::caster32_t(uint32_t(0x420c28f3)).f); } }; template struct stirling_kernel { static inline B compute(const B& x) { return horner(x); } static inline B split_limit() { return B(detail::caster64_t(uint64_t(0x4061e083ba3443d4)).f); } static inline B large_limit() { return B(detail::caster64_t(uint64_t(0x4065800000000000)).f); } }; /* origin: boost/simd/arch/common/simd/function/stirling.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ template inline B stirling(const B& a) { const B stirlingsplitlim = stirling_kernel::split_limit(); const B stirlinglargelim = stirling_kernel::large_limit(); B x = select(a >= B(0.), a, nan()); B w = B(1.) / x; w = fma(w, stirling_kernel::compute(w), B(1.)); B y = exp(-x); auto test = (x < stirlingsplitlim); B z = x - B(0.5); z = select(test, z, B(0.5) * z); B v = exp(z * log(abs(x))); y *= v; y = select(test, y, y * v); y *= sqrt_2pi() * w; #ifndef XSIMD_NO_INFINITIES y = select(xsimd::isinf(x), x, y); #endif return select(x > stirlinglargelim, infinity(), y); } } /* origin: boost/simd/arch/common/detail/generic/gamma_kernel.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ namespace detail { template struct tgamma_kernel; template struct tgamma_kernel { static inline B compute(const B& x) { return horner(x); } }; template struct tgamma_kernel { static inline B compute(const B& x) { return horner(x) / horner(x); } }; } /* origin: boost/simd/arch/common/simd/function/gamma.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ namespace detail { template B tgamma_large_negative(const B& a) { B st = stirling(a); B p = floor(a); B sgngam = select(is_even(p), -B(1.), B(1.)); B z = a - p; auto test2 = z < B(0.5); z = select(test2, z - B(1.), z); z = a * trigo_kernel::sin(z, trigo_pi_tag()); z = abs(z); return sgngam * pi() / (z * st); } template B tgamma_other(const B& a, const BB& test) { B x = select(test, B(2.), a); #ifndef XSIMD_NO_INFINITIES auto inf_result = (a == infinity()); x = select(inf_result, B(2.), x); #endif B z = B(1.); auto test1 = (x >= B(3.)); while (any(test1)) { x = select(test1, x - B(1.), x); z = select(test1, z * x, z); test1 = (x >= B(3.)); } test1 = (x < B(0.)); while (any(test1)) { z = select(test1, z / x, z); x = select(test1, x + B(1.), x); test1 = (x < B(0.)); } auto test2 = (x < B(2.)); while (any(test2)) { z = select(test2, z / x, z); x = select(test2, x + B(1.), x); test2 = (x < B(2.)); } x = z * tgamma_kernel::compute(x - B(2.)); #ifndef XSIMD_NO_INFINITIES return select(inf_result, a, x); #else return x; #endif } template inline B tgamma_impl(const B& a) { auto nan_result = (a < B(0.) && is_flint(a)); #ifndef XSIMD_NO_INVALIDS nan_result = xsimd::isnan(a) || nan_result; #endif B q = abs(a); auto test = (a < B(-33.)); B r = nan(); if (any(test)) { r = tgamma_large_negative(q); if (all(test)) return select(nan_result, nan(), r); } B r1 = tgamma_other(a, test); B r2 = select(test, r, r1); return select(a == B(0.), copysign(infinity(), a), select(nan_result, nan(), r2)); } } template inline batch_type_t tgamma(const simd_base& x) { return detail::tgamma_impl(x()); } /************************* * lgamma implementation * *************************/ /* origin: boost/simd/arch/common/detail/generic/gammaln_kernel.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ namespace detail { template struct lgamma_kernel; template struct lgamma_kernel { static inline B gammalnB(const B& x) { return horner(x); } static inline B gammalnC(const B& x) { return horner(x); } static inline B gammaln2(const B& x) { return horner(x); } }; template struct lgamma_kernel { static inline B gammaln1(const B& x) { return horner(x) / horner(x); } static inline B gammalnA(const B& x) { return horner(x); } }; } /* origin: boost/simd/arch/common/simd/function/gammaln.hpp */ /* * ==================================================== * copyright 2016 NumScale SAS * * Distributed under the Boost Software License, Version 1.0. * (See copy at http://boost.org/LICENSE_1_0.txt) * ==================================================== */ namespace detail { template struct lgamma_impl; template struct lgamma_impl { static inline B compute(const B& a) { auto inf_result = (a <= B(0.)) && is_flint(a); B x = select(inf_result, nan(), a); B q = abs(x); #ifndef XSIMD_NO_INFINITIES inf_result = (x == infinity()) || inf_result; #endif auto ltza = a < B(0.); B r; B r1 = other(q); if (any(ltza)) { r = select(inf_result, infinity(), negative(q, r1)); if (all(ltza)) return r; } B r2 = select(ltza, r, r1); return select(a == minusinfinity(), nan(), select(inf_result, infinity(), r2)); } private: static inline B negative(const B& q, const B& w) { B p = floor(q); B z = q - p; auto test2 = z < B(0.5); z = select(test2, z - B(1.), z); z = q * trigo_kernel::sin(z, trigo_pi_tag()); return -log(invpi() * abs(z)) - w; } static inline B other(const B& x) { auto xlt650 = (x < B(6.5)); B r0x = x; B r0z = x; B r0s = B(1.); B r1 = B(0.); B p = nan(); if (any(xlt650)) { B z = B(1.); B tx = select(xlt650, x, B(0.)); B nx = B(0.); const B _075 = B(0.75); const B _150 = B(1.50); const B _125 = B(1.25); const B _250 = B(2.50); auto xge150 = (x >= _150); auto txgt250 = (tx > _250); // x >= 1.5 while (any(xge150 && txgt250)) { nx = select(txgt250, nx - B(1.), nx); tx = select(txgt250, x + nx, tx); z = select(txgt250, z * tx, z); txgt250 = (tx > _250); } r0x = select(xge150, x + nx - B(2.), x); r0z = select(xge150, z, r0z); r0s = select(xge150, B(1.), r0s); // x >= 1.25 && x < 1.5 auto xge125 = (x >= _125); auto xge125t = xge125 && !xge150; if (any(xge125)) { r0x = select(xge125t, x - B(1.), r0x); r0z = select(xge125t, z * x, r0z); r0s = select(xge125t, B(-1.), r0s); } // x >= 0.75 && x < 1.5 auto kernelC = as_logical_t(false); auto xge075 = (x >= _075); auto xge075t = xge075 && !xge125; if (any(xge075t)) { kernelC = xge075t; r0x = select(xge075t, x - B(1.), x); r0z = select(xge075t, B(1.), r0z); r0s = select(xge075t, B(-1.), r0s); p = lgamma_kernel::gammalnC(r0x); } // tx < 1.5 && x < 0.75 auto txlt150 = (tx < _150) && !xge075; if (any(txlt150)) { auto orig = txlt150; while (any(txlt150)) { z = select(txlt150, z * tx, z); nx = select(txlt150, nx + B(1.), nx); tx = select(txlt150, x + nx, tx); txlt150 = (tx < _150) && !xge075; } r0x = select(orig, r0x + nx - B(2.), r0x); r0z = select(orig, z, r0z); r0s = select(orig, B(-1.), r0s); } p = select(kernelC, p, lgamma_kernel::gammalnB(r0x)); if (all(xlt650)) return fma(r0x, p, r0s * log(abs(r0z))); } r0z = select(xlt650, abs(r0z), x); B m = log(r0z); r1 = fma(r0x, p, r0s * m); B r2 = fma(x - B(0.5), m, logsqrt2pi() - x); r2 += lgamma_kernel::gammaln2(B(1.) / (x * x)) / x; return select(xlt650, r1, r2); } }; template struct lgamma_impl { static inline B compute(const B& a) { auto inf_result = (a <= B(0.)) && is_flint(a); B x = select(inf_result, nan(), a); B q = abs(x); #ifndef XSIMD_NO_INFINITIES inf_result = (q == infinity()); #endif auto test = (a < B(-34.)); B r = nan(); if (any(test)) { r = large_negative(q); if (all(test)) return select(inf_result, nan(), r); } B r1 = other(a); B r2 = select(test, r, r1); return select(a == minusinfinity(), nan(), select(inf_result, infinity(), r2)); } private: static inline B large_negative(const B& q) { B w = lgamma(q); B p = floor(q); B z = q - p; auto test2 = (z < B(0.5)); z = select(test2, z - B(1.), z); z = q * trigo_kernel::sin(z, trigo_pi_tag()); z = abs(z); return logpi() - log(z) - w; } static inline B other(const B& xx) { B x = xx; auto test = (x < B(13.)); B r1 = B(0.); if (any(test)) { B z = B(1.); B p = B(0.); B u = select(test, x, B(0.)); auto test1 = (u >= B(3.)); while (any(test1)) { p = select(test1, p - B(1.), p); u = select(test1, x + p, u); z = select(test1, z * u, z); test1 = (u >= B(3.)); } auto test2 = (u < B(2.)); while (any(test2)) { z = select(test2, z / u, z); p = select(test2, p + B(1.), p); u = select(test2, x + p, u); test2 = (u < B(2.)); } z = abs(z); x += p - B(2.); r1 = x * lgamma_kernel::gammaln1(x) + log(z); if (all(test)) return r1; } B r2 = fma(xx - B(0.5), log(xx), logsqrt2pi() - xx); B p = B(1.) / (xx * xx); r2 += lgamma_kernel::gammalnA(p) / xx; return select(test, r1, r2); } }; } template inline batch_type_t lgamma(const simd_base& x) { return detail::lgamma_impl>::compute(x()); } } #endif