|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "../config.h" |
|
#include "../error.h" |
|
|
|
#include "const.h" |
|
|
|
namespace xsf { |
|
namespace cephes { |
|
|
|
namespace detail { |
|
|
|
constexpr int kn_MAXFAC = 31; |
|
|
|
} |
|
|
|
XSF_HOST_DEVICE inline double kn(int nn, double x) { |
|
double k, kf, nk1f, nkf, zn, t, s, z0, z; |
|
double ans, fn, pn, pk, zmn, tlg, tox; |
|
int i, n; |
|
|
|
if (nn < 0) |
|
n = -nn; |
|
else |
|
n = nn; |
|
|
|
if (n > detail::kn_MAXFAC) { |
|
overf: |
|
set_error("kn", SF_ERROR_OVERFLOW, NULL); |
|
return (std::numeric_limits<double>::infinity()); |
|
} |
|
|
|
if (x <= 0.0) { |
|
if (x < 0.0) { |
|
set_error("kn", SF_ERROR_DOMAIN, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} else { |
|
set_error("kn", SF_ERROR_SINGULAR, NULL); |
|
return std::numeric_limits<double>::infinity(); |
|
} |
|
} |
|
|
|
if (x > 9.55) |
|
goto asymp; |
|
|
|
ans = 0.0; |
|
z0 = 0.25 * x * x; |
|
fn = 1.0; |
|
pn = 0.0; |
|
zmn = 1.0; |
|
tox = 2.0 / x; |
|
|
|
if (n > 0) { |
|
|
|
pn = -detail::SCIPY_EULER; |
|
k = 1.0; |
|
for (i = 1; i < n; i++) { |
|
pn += 1.0 / k; |
|
k += 1.0; |
|
fn *= k; |
|
} |
|
|
|
zmn = tox; |
|
|
|
if (n == 1) { |
|
ans = 1.0 / x; |
|
} else { |
|
nk1f = fn / n; |
|
kf = 1.0; |
|
s = nk1f; |
|
z = -z0; |
|
zn = 1.0; |
|
for (i = 1; i < n; i++) { |
|
nk1f = nk1f / (n - i); |
|
kf = kf * i; |
|
zn *= z; |
|
t = nk1f * zn / kf; |
|
s += t; |
|
if ((std::numeric_limits<double>::max() - std::abs(t)) < std::abs(s)) { |
|
goto overf; |
|
} |
|
if ((tox > 1.0) && ((std::numeric_limits<double>::max() / tox) < zmn)) { |
|
goto overf; |
|
} |
|
zmn *= tox; |
|
} |
|
s *= 0.5; |
|
t = std::abs(s); |
|
if ((zmn > 1.0) && ((std::numeric_limits<double>::max() / zmn) < t)) { |
|
goto overf; |
|
} |
|
if ((t > 1.0) && ((std::numeric_limits<double>::max() / t) < zmn)) { |
|
goto overf; |
|
} |
|
ans = s * zmn; |
|
} |
|
} |
|
|
|
tlg = 2.0 * log(0.5 * x); |
|
pk = -detail::SCIPY_EULER; |
|
if (n == 0) { |
|
pn = pk; |
|
t = 1.0; |
|
} else { |
|
pn = pn + 1.0 / n; |
|
t = 1.0 / fn; |
|
} |
|
s = (pk + pn - tlg) * t; |
|
k = 1.0; |
|
do { |
|
t *= z0 / (k * (k + n)); |
|
pk += 1.0 / k; |
|
pn += 1.0 / (k + n); |
|
s += (pk + pn - tlg) * t; |
|
k += 1.0; |
|
} while (fabs(t / s) > detail::MACHEP); |
|
|
|
s = 0.5 * s / zmn; |
|
if (n & 1) { |
|
s = -s; |
|
} |
|
ans += s; |
|
|
|
return (ans); |
|
|
|
|
|
|
|
|
|
asymp: |
|
|
|
if (x > detail::MAXLOG) { |
|
set_error("kn", SF_ERROR_UNDERFLOW, NULL); |
|
return (0.0); |
|
} |
|
k = n; |
|
pn = 4.0 * k * k; |
|
pk = 1.0; |
|
z0 = 8.0 * x; |
|
fn = 1.0; |
|
t = 1.0; |
|
s = t; |
|
nkf = std::numeric_limits<double>::infinity(); |
|
i = 0; |
|
do { |
|
z = pn - pk * pk; |
|
t = t * z / (fn * z0); |
|
nk1f = std::abs(t); |
|
if ((i >= n) && (nk1f > nkf)) { |
|
goto adone; |
|
} |
|
nkf = nk1f; |
|
s += t; |
|
fn += 1.0; |
|
pk += 2.0; |
|
i += 1; |
|
} while (std::abs(t / s) > detail::MACHEP); |
|
|
|
adone: |
|
ans = std::exp(-x) * std::sqrt(M_PI / (2.0 * x)) * s; |
|
return (ans); |
|
} |
|
|
|
} |
|
} |
|
|