|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "../config.h" |
|
#include "../error.h" |
|
#include "const.h" |
|
#include "polevl.h" |
|
|
|
namespace xsf { |
|
namespace cephes { |
|
namespace detail { |
|
constexpr double psi_A[] = {8.33333333333333333333E-2, -2.10927960927960927961E-2, 7.57575757575757575758E-3, |
|
-4.16666666666666666667E-3, 3.96825396825396825397E-3, -8.33333333333333333333E-3, |
|
8.33333333333333333333E-2}; |
|
|
|
constexpr float psi_Y = 0.99558162689208984f; |
|
|
|
constexpr double psi_root1 = 1569415565.0 / 1073741824.0; |
|
constexpr double psi_root2 = (381566830.0 / 1073741824.0) / 1073741824.0; |
|
constexpr double psi_root3 = 0.9016312093258695918615325266959189453125e-19; |
|
|
|
constexpr double psi_P[] = {-0.0020713321167745952, -0.045251321448739056, -0.28919126444774784, |
|
-0.65031853770896507, -0.32555031186804491, 0.25479851061131551}; |
|
constexpr double psi_Q[] = {-0.55789841321675513e-6, |
|
0.0021284987017821144, |
|
0.054151797245674225, |
|
0.43593529692665969, |
|
1.4606242909763515, |
|
2.0767117023730469, |
|
1.0}; |
|
|
|
XSF_HOST_DEVICE double digamma_imp_1_2(double x) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double r, g; |
|
|
|
g = x - psi_root1; |
|
g -= psi_root2; |
|
g -= psi_root3; |
|
r = xsf::cephes::polevl(x - 1.0, psi_P, 5) / xsf::cephes::polevl(x - 1.0, psi_Q, 6); |
|
|
|
return g * psi_Y + g * r; |
|
} |
|
|
|
XSF_HOST_DEVICE double psi_asy(double x) { |
|
double y, z; |
|
|
|
if (x < 1.0e17) { |
|
z = 1.0 / (x * x); |
|
y = z * xsf::cephes::polevl(z, psi_A, 6); |
|
} else { |
|
y = 0.0; |
|
} |
|
|
|
return std::log(x) - (0.5 / x) - y; |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE double psi(double x) { |
|
double y = 0.0; |
|
double q, r; |
|
int i, n; |
|
|
|
if (std::isnan(x)) { |
|
return x; |
|
} else if (x == std::numeric_limits<double>::infinity()) { |
|
return x; |
|
} else if (x == -std::numeric_limits<double>::infinity()) { |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} else if (x == 0) { |
|
set_error("psi", SF_ERROR_SINGULAR, NULL); |
|
return std::copysign(std::numeric_limits<double>::infinity(), -x); |
|
} else if (x < 0.0) { |
|
|
|
r = std::modf(x, &q); |
|
if (r == 0.0) { |
|
set_error("psi", SF_ERROR_SINGULAR, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
y = -M_PI / std::tan(M_PI * r); |
|
x = 1.0 - x; |
|
} |
|
|
|
|
|
if ((x <= 10.0) && (x == std::floor(x))) { |
|
n = static_cast<int>(x); |
|
for (i = 1; i < n; i++) { |
|
y += 1.0 / i; |
|
} |
|
y -= detail::SCIPY_EULER; |
|
return y; |
|
} |
|
|
|
|
|
if (x < 1.0) { |
|
y -= 1.0 / x; |
|
x += 1.0; |
|
} else if (x < 10.0) { |
|
while (x > 2.0) { |
|
x -= 1.0; |
|
y += 1.0 / x; |
|
} |
|
} |
|
if ((1.0 <= x) && (x <= 2.0)) { |
|
y += detail::digamma_imp_1_2(x); |
|
return y; |
|
} |
|
|
|
|
|
y += detail::psi_asy(x); |
|
return y; |
|
} |
|
} |
|
} |
|
|