|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "cephes/gamma.h" |
|
#include "cephes/rgamma.h" |
|
#include "config.h" |
|
#include "error.h" |
|
#include "evalpoly.h" |
|
#include "trig.h" |
|
#include "zlog1.h" |
|
|
|
namespace xsf { |
|
|
|
namespace detail { |
|
constexpr double loggamma_SMALLX = 7; |
|
constexpr double loggamma_SMALLY = 7; |
|
constexpr double loggamma_HLOG2PI = 0.918938533204672742; |
|
constexpr double loggamma_LOGPI = 1.1447298858494001741434262; |
|
constexpr double loggamma_TAYLOR_RADIUS = 0.2; |
|
|
|
XSF_HOST_DEVICE std::complex<double> loggamma_stirling(std::complex<double> z) { |
|
|
|
|
|
|
|
|
|
|
|
double coeffs[] = {-2.955065359477124183E-2, 6.4102564102564102564E-3, -1.9175269175269175269E-3, |
|
8.4175084175084175084E-4, -5.952380952380952381E-4, 7.9365079365079365079E-4, |
|
-2.7777777777777777778E-3, 8.3333333333333333333E-2}; |
|
std::complex<double> rz = 1.0 / z; |
|
std::complex<double> rzz = rz / z; |
|
|
|
return (z - 0.5) * std::log(z) - z + loggamma_HLOG2PI + rz * cevalpoly(coeffs, 7, rzz); |
|
} |
|
|
|
XSF_HOST_DEVICE std::complex<double> loggamma_recurrence(std::complex<double> z) { |
|
|
|
|
|
|
|
|
|
|
|
int signflips = 0; |
|
int sb = 0; |
|
std::complex<double> shiftprod = z; |
|
|
|
z += 1.0; |
|
int nsb; |
|
while (z.real() <= loggamma_SMALLX) { |
|
shiftprod *= z; |
|
nsb = std::signbit(shiftprod.imag()); |
|
signflips += nsb != 0 && sb == 0 ? 1 : 0; |
|
sb = nsb; |
|
z += 1.0; |
|
} |
|
return loggamma_stirling(z) - std::log(shiftprod) - signflips * 2 * M_PI * std::complex<double>(0, 1); |
|
} |
|
|
|
XSF_HOST_DEVICE std::complex<double> loggamma_taylor(std::complex<double> z) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double coeffs[] = { |
|
-4.3478266053040259361E-2, 4.5454556293204669442E-2, -4.7619070330142227991E-2, 5.000004769810169364E-2, |
|
-5.2631679379616660734E-2, 5.5555767627403611102E-2, -5.8823978658684582339E-2, 6.2500955141213040742E-2, |
|
-6.6668705882420468033E-2, 7.1432946295361336059E-2, -7.6932516411352191473E-2, 8.3353840546109004025E-2, |
|
-9.0954017145829042233E-2, 1.0009945751278180853E-1, -1.1133426586956469049E-1, 1.2550966952474304242E-1, |
|
-1.4404989676884611812E-1, 1.6955717699740818995E-1, -2.0738555102867398527E-1, 2.7058080842778454788E-1, |
|
-4.0068563438653142847E-1, 8.2246703342411321824E-1, -5.7721566490153286061E-1}; |
|
|
|
z -= 1.0; |
|
return z * cevalpoly(coeffs, 22, z); |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE inline double loggamma(double x) { |
|
if (x < 0.0) { |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
return cephes::lgam(x); |
|
} |
|
|
|
XSF_HOST_DEVICE inline float loggamma(float x) { return loggamma(static_cast<double>(x)); } |
|
|
|
XSF_HOST_DEVICE inline std::complex<double> loggamma(std::complex<double> z) { |
|
|
|
|
|
if (std::isnan(z.real()) || std::isnan(z.imag())) { |
|
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()}; |
|
} |
|
if (z.real() <= 0 and z == std::floor(z.real())) { |
|
set_error("loggamma", SF_ERROR_SINGULAR, NULL); |
|
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()}; |
|
} |
|
if (z.real() > detail::loggamma_SMALLX || std::abs(z.imag()) > detail::loggamma_SMALLY) { |
|
return detail::loggamma_stirling(z); |
|
} |
|
if (std::abs(z - 1.0) < detail::loggamma_TAYLOR_RADIUS) { |
|
return detail::loggamma_taylor(z); |
|
} |
|
if (std::abs(z - 2.0) < detail::loggamma_TAYLOR_RADIUS) { |
|
|
|
return detail::zlog1(z - 1.0) + detail::loggamma_taylor(z - 1.0); |
|
} |
|
if (z.real() < 0.1) { |
|
|
|
double tmp = std::copysign(2 * M_PI, z.imag()) * std::floor(0.5 * z.real() + 0.25); |
|
return std::complex<double>(detail::loggamma_LOGPI, tmp) - std::log(sinpi(z)) - loggamma(1.0 - z); |
|
} |
|
if (std::signbit(z.imag()) == 0) { |
|
|
|
return detail::loggamma_recurrence(z); |
|
} |
|
return std::conj(detail::loggamma_recurrence(std::conj(z))); |
|
} |
|
|
|
XSF_HOST_DEVICE inline std::complex<float> loggamma(std::complex<float> z) { |
|
return static_cast<std::complex<float>>(loggamma(static_cast<std::complex<double>>(z))); |
|
} |
|
|
|
XSF_HOST_DEVICE inline double rgamma(double z) { return cephes::rgamma(z); } |
|
|
|
XSF_HOST_DEVICE inline float rgamma(float z) { return rgamma(static_cast<double>(z)); } |
|
|
|
XSF_HOST_DEVICE inline std::complex<double> rgamma(std::complex<double> z) { |
|
|
|
if (z.real() <= 0 && z == std::floor(z.real())) { |
|
|
|
return 0.0; |
|
} |
|
return std::exp(-loggamma(z)); |
|
} |
|
|
|
XSF_HOST_DEVICE inline std::complex<float> rgamma(std::complex<float> z) { |
|
return static_cast<std::complex<float>>(rgamma(static_cast<std::complex<double>>(z))); |
|
} |
|
|
|
} |
|
|