|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "../config.h" |
|
#include "../error.h" |
|
|
|
#include "const.h" |
|
#include "gamma.h" |
|
#include "rgamma.h" |
|
|
|
namespace xsf { |
|
namespace cephes { |
|
|
|
namespace detail { |
|
|
|
|
|
XSF_HOST_DEVICE inline double hyp2f0(double a, double b, double x, int type, double *err) { |
|
double a0, alast, t, tlast, maxt; |
|
double n, an, bn, u, sum, temp; |
|
|
|
an = a; |
|
bn = b; |
|
a0 = 1.0e0; |
|
alast = 1.0e0; |
|
sum = 0.0; |
|
n = 1.0e0; |
|
t = 1.0e0; |
|
tlast = 1.0e9; |
|
maxt = 0.0; |
|
|
|
do { |
|
if (an == 0) |
|
goto pdone; |
|
if (bn == 0) |
|
goto pdone; |
|
|
|
u = an * (bn * x / n); |
|
|
|
|
|
temp = std::abs(u); |
|
if ((temp > 1.0) && (maxt > (std::numeric_limits<double>::max() / temp))) |
|
goto error; |
|
|
|
a0 *= u; |
|
t = std::abs(a0); |
|
|
|
|
|
|
|
|
|
|
|
if (t > tlast) |
|
goto ndone; |
|
|
|
tlast = t; |
|
sum += alast; |
|
alast = a0; |
|
|
|
if (n > 200) |
|
goto ndone; |
|
|
|
an += 1.0e0; |
|
bn += 1.0e0; |
|
n += 1.0e0; |
|
if (t > maxt) |
|
maxt = t; |
|
} while (t > MACHEP); |
|
|
|
pdone: |
|
|
|
|
|
*err = std::abs(MACHEP * (n + maxt)); |
|
|
|
alast = a0; |
|
goto done; |
|
|
|
ndone: |
|
|
|
|
|
|
|
|
|
n -= 1.0; |
|
x = 1.0 / x; |
|
|
|
switch (type) { |
|
case 1: |
|
alast *= (0.5 + (0.125 + 0.25 * b - 0.5 * a + 0.25 * x - 0.25 * n) / x); |
|
break; |
|
|
|
case 2: |
|
alast *= 2.0 / 3.0 - b + 2.0 * a + x - n; |
|
break; |
|
|
|
default:; |
|
} |
|
|
|
|
|
*err = MACHEP * (n + maxt) + std::abs(a0); |
|
|
|
done: |
|
sum += alast; |
|
return (sum); |
|
|
|
|
|
error: |
|
*err = std::numeric_limits<double>::infinity(); |
|
set_error("hyperg", SF_ERROR_NO_RESULT, NULL); |
|
return (sum); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XSF_HOST_DEVICE inline double hy1f1a(double a, double b, double x, double *err) { |
|
double h1, h2, t, u, temp, acanc, asum, err1, err2; |
|
|
|
if (x == 0) { |
|
acanc = 1.0; |
|
asum = std::numeric_limits<double>::infinity(); |
|
goto adone; |
|
} |
|
temp = std::log(std::abs(x)); |
|
t = x + temp * (a - b); |
|
u = -temp * a; |
|
|
|
if (b > 0) { |
|
temp = xsf::cephes::lgam(b); |
|
t += temp; |
|
u += temp; |
|
} |
|
|
|
h1 = hyp2f0(a, a - b + 1, -1.0 / x, 1, &err1); |
|
|
|
temp = std::exp(u) * xsf::cephes::rgamma(b - a); |
|
h1 *= temp; |
|
err1 *= temp; |
|
|
|
h2 = hyp2f0(b - a, 1.0 - a, 1.0 / x, 2, &err2); |
|
|
|
if (a < 0) |
|
temp = std::exp(t) * xsf::cephes::rgamma(a); |
|
else |
|
temp = std::exp(t - xsf::cephes::lgam(a)); |
|
|
|
h2 *= temp; |
|
err2 *= temp; |
|
|
|
if (x < 0.0) |
|
asum = h1; |
|
else |
|
asum = h2; |
|
|
|
acanc = std::abs(err1) + std::abs(err2); |
|
|
|
if (b < 0) { |
|
temp = xsf::cephes::Gamma(b); |
|
asum *= temp; |
|
acanc *= std::abs(temp); |
|
} |
|
|
|
if (asum != 0.0) |
|
acanc /= std::abs(asum); |
|
|
|
if (acanc != acanc) |
|
|
|
acanc = 1.0; |
|
|
|
if (std::isinf(asum)) |
|
|
|
acanc = 0; |
|
|
|
acanc *= 30.0; |
|
|
|
adone: |
|
*err = acanc; |
|
return (asum); |
|
} |
|
|
|
|
|
XSF_HOST_DEVICE inline double hy1f1p(double a, double b, double x, double *err) { |
|
double n, a0, sum, t, u, temp, maxn; |
|
double an, bn, maxt; |
|
double y, c, sumc; |
|
|
|
|
|
an = a; |
|
bn = b; |
|
a0 = 1.0; |
|
sum = 1.0; |
|
c = 0.0; |
|
n = 1.0; |
|
t = 1.0; |
|
maxt = 0.0; |
|
*err = 1.0; |
|
|
|
maxn = 200.0 + 2 * fabs(a) + 2 * fabs(b); |
|
|
|
while (t > MACHEP) { |
|
if (bn == 0) { |
|
sf_error("hyperg", SF_ERROR_SINGULAR, NULL); |
|
return (std::numeric_limits<double>::infinity()); |
|
} |
|
if (an == 0) |
|
return (sum); |
|
if (n > maxn) { |
|
|
|
c = std::abs(c) + std::abs(t) * 50.0; |
|
goto pdone; |
|
} |
|
u = x * (an / (bn * n)); |
|
|
|
|
|
temp = std::abs(u); |
|
if ((temp > 1.0) && (maxt > (std::numeric_limits<double>::max() / temp))) { |
|
*err = 1.0; |
|
return sum; |
|
} |
|
|
|
a0 *= u; |
|
|
|
y = a0 - c; |
|
sumc = sum + y; |
|
c = (sumc - sum) - y; |
|
sum = sumc; |
|
|
|
t = std::abs(a0); |
|
|
|
an += 1.0; |
|
bn += 1.0; |
|
n += 1.0; |
|
} |
|
|
|
pdone: |
|
|
|
|
|
if (sum != 0.0) { |
|
*err = std::abs(c / sum); |
|
} else { |
|
*err = std::abs(c); |
|
} |
|
|
|
if (*err != *err) { |
|
|
|
*err = 1.0; |
|
} |
|
|
|
return (sum); |
|
} |
|
|
|
} |
|
|
|
XSF_HOST_DEVICE inline double hyperg(double a, double b, double x) { |
|
double asum, psum, acanc, pcanc, temp; |
|
|
|
|
|
temp = b - a; |
|
if (std::abs(temp) < 0.001 * std::abs(a)) |
|
return (exp(x) * hyperg(temp, b, -x)); |
|
|
|
|
|
if (std::abs(x) < 10 + std::abs(a) + std::abs(b)) { |
|
psum = detail::hy1f1p(a, b, x, &pcanc); |
|
if (pcanc < 1.0e-15) |
|
goto done; |
|
asum = detail::hy1f1a(a, b, x, &acanc); |
|
} else { |
|
psum = detail::hy1f1a(a, b, x, &pcanc); |
|
if (pcanc < 1.0e-15) |
|
goto done; |
|
asum = detail::hy1f1p(a, b, x, &acanc); |
|
} |
|
|
|
|
|
|
|
if (acanc < pcanc) { |
|
pcanc = acanc; |
|
psum = asum; |
|
} |
|
|
|
done: |
|
if (pcanc > 1.0e-12) |
|
set_error("hyperg", SF_ERROR_LOSS, NULL); |
|
|
|
return (psum); |
|
} |
|
|
|
} |
|
} |
|
|