|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "config.h" |
|
#include "error.h" |
|
#include "tools.h" |
|
|
|
#include "binom.h" |
|
#include "cephes/gamma.h" |
|
#include "cephes/lanczos.h" |
|
#include "cephes/poch.h" |
|
#include "cephes/hyp2f1.h" |
|
#include "digamma.h" |
|
|
|
namespace xsf { |
|
namespace detail { |
|
constexpr double hyp2f1_EPS = 1e-15; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constexpr std::uint64_t hyp2f1_MAXITER = 3000; |
|
|
|
XSF_HOST_DEVICE inline double four_gammas_lanczos(double u, double v, double w, double x) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((u == std::trunc(u) && u <= 0) || (v == std::trunc(v) && v <= 0)) { |
|
|
|
|
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if ((w == std::trunc(w) && w <= 0) || (x == std::trunc(x) && x <= 0)) { |
|
|
|
return 0.0; |
|
} |
|
|
|
double result = 1.0; |
|
double ugh, vgh, wgh, xgh, u_prime, v_prime, w_prime, x_prime; |
|
|
|
if (u >= 0.5) { |
|
result *= cephes::lanczos_sum_expg_scaled(u); |
|
ugh = u + cephes::lanczos_g - 0.5; |
|
u_prime = u; |
|
} else { |
|
result /= cephes::lanczos_sum_expg_scaled(1 - u) * std::sin(M_PI * u) * M_1_PI; |
|
ugh = 0.5 - u + cephes::lanczos_g; |
|
u_prime = 1 - u; |
|
} |
|
|
|
if (v >= 0.5) { |
|
result *= cephes::lanczos_sum_expg_scaled(v); |
|
vgh = v + cephes::lanczos_g - 0.5; |
|
v_prime = v; |
|
} else { |
|
result /= cephes::lanczos_sum_expg_scaled(1 - v) * std::sin(M_PI * v) * M_1_PI; |
|
vgh = 0.5 - v + cephes::lanczos_g; |
|
v_prime = 1 - v; |
|
} |
|
|
|
if (w >= 0.5) { |
|
result /= cephes::lanczos_sum_expg_scaled(w); |
|
wgh = w + cephes::lanczos_g - 0.5; |
|
w_prime = w; |
|
} else { |
|
result *= cephes::lanczos_sum_expg_scaled(1 - w) * std::sin(M_PI * w) * M_1_PI; |
|
wgh = 0.5 - w + cephes::lanczos_g; |
|
w_prime = 1 - w; |
|
} |
|
|
|
if (x >= 0.5) { |
|
result /= cephes::lanczos_sum_expg_scaled(x); |
|
xgh = x + cephes::lanczos_g - 0.5; |
|
x_prime = x; |
|
} else { |
|
result *= cephes::lanczos_sum_expg_scaled(1 - x) * std::sin(M_PI * x) * M_1_PI; |
|
xgh = 0.5 - x + cephes::lanczos_g; |
|
x_prime = 1 - x; |
|
} |
|
|
|
if (std::abs(u) >= std::abs(w)) { |
|
|
|
if (std::abs((v_prime - u_prime) * (v - 0.5)) < 100 * ugh and v > 100) { |
|
|
|
|
|
result *= std::exp((v - 0.5) * std::log1p((v_prime - u_prime) / ugh)); |
|
} else { |
|
result *= std::pow(vgh / ugh, v - 0.5); |
|
} |
|
|
|
if (std::abs((u_prime - w_prime) * (w - 0.5)) < 100 * wgh and u > 100) { |
|
result *= std::exp((w - 0.5) * std::log1p((u_prime - w_prime) / wgh)); |
|
} else { |
|
result *= std::pow(ugh / wgh, w - 0.5); |
|
} |
|
|
|
if (std::abs((u_prime - x_prime) * (x - 0.5)) < 100 * xgh and u > 100) { |
|
result *= std::exp((x - 0.5) * std::log1p((u_prime - x_prime) / xgh)); |
|
} else { |
|
result *= std::pow(ugh / xgh, x - 0.5); |
|
} |
|
} else { |
|
|
|
if (std::abs((u_prime - w_prime) * (u - 0.5)) < 100 * wgh and u > 100) { |
|
result *= std::exp((u - 0.5) * std::log1p((u_prime - w_prime) / wgh)); |
|
} else { |
|
result *= pow(ugh / wgh, u - 0.5); |
|
} |
|
if (std::abs((v_prime - w_prime) * (v - 0.5)) < 100 * wgh and v > 100) { |
|
result *= std::exp((v - 0.5) * std::log1p((v_prime - w_prime) / wgh)); |
|
} else { |
|
result *= std::pow(vgh / wgh, v - 0.5); |
|
} |
|
if (std::abs((w_prime - x_prime) * (x - 0.5)) < 100 * xgh and x > 100) { |
|
result *= std::exp((x - 0.5) * std::log1p((w_prime - x_prime) / xgh)); |
|
} else { |
|
result *= std::pow(wgh / xgh, x - 0.5); |
|
} |
|
} |
|
|
|
|
|
return result; |
|
} |
|
|
|
XSF_HOST_DEVICE inline double four_gammas(double u, double v, double w, double x) { |
|
double result; |
|
|
|
|
|
if (std::abs(v) > std::abs(u)) { |
|
std::swap(u, v); |
|
} |
|
if (std::abs(x) > std::abs(w)) { |
|
std::swap(x, w); |
|
} |
|
|
|
|
|
|
|
if (std::abs(u) <= 100 && std::abs(v) <= 100 && std::abs(w) <= 100 && std::abs(x) <= 100) { |
|
result = cephes::Gamma(u) * cephes::Gamma(v) * (cephes::rgamma(w) * cephes::rgamma(x)); |
|
if (std::isfinite(result) && result != 0.0) { |
|
return result; |
|
} |
|
} |
|
result = four_gammas_lanczos(u, v, w, x); |
|
if (std::isfinite(result) && result != 0.0) { |
|
return result; |
|
} |
|
|
|
result = std::exp(cephes::lgam(v) - cephes::lgam(x) + cephes::lgam(u) - cephes::lgam(w)); |
|
result *= cephes::gammasgn(u) * cephes::gammasgn(w) * cephes::gammasgn(v) * cephes::gammasgn(x); |
|
return result; |
|
} |
|
|
|
class HypergeometricSeriesGenerator { |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
XSF_HOST_DEVICE HypergeometricSeriesGenerator(double a, double b, double c, std::complex<double> z) |
|
: a_(a), b_(b), c_(c), z_(z), term_(1.0), k_(0) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
std::complex<double> output = term_; |
|
term_ = term_ * (a_ + k_) * (b_ + k_) / ((k_ + 1) * (c_ + k_)) * z_; |
|
++k_; |
|
return output; |
|
} |
|
|
|
private: |
|
double a_, b_, c_; |
|
std::complex<double> z_, term_; |
|
std::uint64_t k_; |
|
}; |
|
|
|
class Hyp2f1Transform1Generator { |
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform1Generator(double a, double b, double c, std::complex<double> z) |
|
: factor1_(four_gammas(c, c - a - b, c - a, c - b)), |
|
factor2_(four_gammas(c, a + b - c, a, b) * std::pow(1.0 - z, c - a - b)), |
|
generator1_(HypergeometricSeriesGenerator(a, b, a + b - c + 1, 1.0 - z)), |
|
generator2_(HypergeometricSeriesGenerator(c - a, c - b, c - a - b + 1, 1.0 - z)) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
return factor1_ * generator1_() + factor2_ * generator2_(); |
|
} |
|
|
|
private: |
|
std::complex<double> factor1_, factor2_; |
|
HypergeometricSeriesGenerator generator1_, generator2_; |
|
}; |
|
|
|
class Hyp2f1Transform1LimitSeriesGenerator { |
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform1LimitSeriesGenerator(double a, double b, double m, std::complex<double> z) |
|
: d1_(xsf::digamma(a)), d2_(xsf::digamma(b)), d3_(xsf::digamma(1 + m)), |
|
d4_(xsf::digamma(1.0)), a_(a), b_(b), m_(m), z_(z), log_1_z_(std::log(1.0 - z)), |
|
factor_(cephes::rgamma(m + 1)), k_(0) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
std::complex<double> term_ = (d1_ + d2_ - d3_ - d4_ + log_1_z_) * factor_; |
|
|
|
d1_ += 1 / (a_ + k_); |
|
d2_ += 1 / (b_ + k_); |
|
d3_ += 1 / (1.0 + m_ + k_); |
|
d4_ += 1 / (1.0 + k_); |
|
factor_ *= (a_ + k_) * (b_ + k_) / ((k_ + 1.0) * (m_ + k_ + 1)) * (1.0 - z_); |
|
++k_; |
|
return term_; |
|
} |
|
|
|
private: |
|
double d1_, d2_, d3_, d4_, a_, b_, m_; |
|
std::complex<double> z_, log_1_z_, factor_; |
|
int k_; |
|
}; |
|
|
|
class Hyp2f1Transform2Generator { |
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform2Generator(double a, double b, double c, std::complex<double> z) |
|
: factor1_(four_gammas(c, b - a, b, c - a) * std::pow(-z, -a)), |
|
factor2_(four_gammas(c, a - b, a, c - b) * std::pow(-z, -b)), |
|
generator1_(HypergeometricSeriesGenerator(a, a - c + 1, a - b + 1, 1.0 / z)), |
|
generator2_(HypergeometricSeriesGenerator(b, b - c + 1, b - a + 1, 1.0 / z)) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
return factor1_ * generator1_() + factor2_ * generator2_(); |
|
} |
|
|
|
private: |
|
std::complex<double> factor1_, factor2_; |
|
HypergeometricSeriesGenerator generator1_, generator2_; |
|
}; |
|
|
|
class Hyp2f1Transform2LimitSeriesGenerator { |
|
|
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform2LimitSeriesGenerator(double a, double b, double c, double m, |
|
std::complex<double> z) |
|
: d1_(xsf::digamma(1.0)), d2_(xsf::digamma(1 + m)), d3_(xsf::digamma(a)), |
|
d4_(xsf::digamma(c - a)), a_(a), b_(b), c_(c), m_(m), z_(z), log_neg_z_(std::log(-z)), |
|
factor_(xsf::cephes::poch(b, m) * xsf::cephes::poch(1 - c + b, m) * |
|
xsf::cephes::rgamma(m + 1)), |
|
k_(0) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
std::complex<double> term = (d1_ + d2_ - d3_ - d4_ + log_neg_z_) * factor_; |
|
|
|
d1_ += 1 / (1.0 + k_); |
|
d2_ += 1 / (1.0 + m_ + k_); |
|
d3_ += 1 / (a_ + k_); |
|
d4_ -= 1 / (c_ - a_ - k_ - 1); |
|
factor_ *= (b_ + m_ + k_) * (1 - c_ + b_ + m_ + k_) / ((k_ + 1) * (m_ + k_ + 1)) / z_; |
|
++k_; |
|
return term; |
|
} |
|
|
|
private: |
|
double d1_, d2_, d3_, d4_, a_, b_, c_, m_; |
|
std::complex<double> z_, log_neg_z_, factor_; |
|
std::uint64_t k_; |
|
}; |
|
|
|
class Hyp2f1Transform2LimitSeriesCminusAIntGenerator { |
|
|
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform2LimitSeriesCminusAIntGenerator(double a, double b, double c, double m, |
|
double n, std::complex<double> z) |
|
: d1_(xsf::digamma(1.0)), d2_(xsf::digamma(1 + m)), d3_(xsf::digamma(a)), |
|
d4_(xsf::digamma(n)), a_(a), b_(b), c_(c), m_(m), n_(n), z_(z), log_neg_z_(std::log(-z)), |
|
factor_(xsf::cephes::poch(b, m) * xsf::cephes::poch(1 - c + b, m) * |
|
xsf::cephes::rgamma(m + 1)), |
|
k_(0) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
std::complex<double> term; |
|
if (k_ < n_) { |
|
term = (d1_ + d2_ - d3_ - d4_ + log_neg_z_) * factor_; |
|
|
|
d1_ += 1 / (1.0 + k_); |
|
d2_ += 1 / (1 + m_ + k_); |
|
d3_ += 1 / (a_ + k_); |
|
d4_ -= 1 / (n_ - k_ - 1); |
|
factor_ *= (b_ + m_ + k_) * (1 - c_ + b_ + m_ + k_) / ((k_ + 1) * (m_ + k_ + 1)) / z_; |
|
++k_; |
|
return term; |
|
} |
|
if (k_ == n_) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
factor_ = std::pow(-1, m_ + n_) * xsf::binom(c_ - 1, b_ - 1) * |
|
xsf::cephes::poch(c_ - a_ + 1, m_ - 1) / std::pow(z_, static_cast<double>(k_)); |
|
} |
|
term = factor_; |
|
factor_ *= (b_ + m_ + k_) * (k_ + a_ - c_ + 1) / ((k_ + 1) * (m_ + k_ + 1)) / z_; |
|
++k_; |
|
return term; |
|
} |
|
|
|
private: |
|
double d1_, d2_, d3_, d4_, a_, b_, c_, m_, n_; |
|
std::complex<double> z_, log_neg_z_, factor_; |
|
std::uint64_t k_; |
|
}; |
|
|
|
class Hyp2f1Transform2LimitFinitePartGenerator { |
|
|
|
|
|
|
|
public: |
|
XSF_HOST_DEVICE Hyp2f1Transform2LimitFinitePartGenerator(double b, double c, double m, |
|
std::complex<double> z) |
|
: b_(b), c_(c), m_(m), z_(z), term_(cephes::Gamma(m) * cephes::rgamma(c - b)), k_(0) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
std::complex<double> output = term_; |
|
term_ = term_ * (b_ + k_) * (c_ - b_ - k_ - 1) / ((k_ + 1) * (m_ - k_ - 1)) / z_; |
|
++k_; |
|
return output; |
|
} |
|
|
|
private: |
|
double b_, c_, m_; |
|
std::complex<double> z_, term_; |
|
std::uint64_t k_; |
|
}; |
|
|
|
class LopezTemmeSeriesGenerator { |
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
XSF_HOST_DEVICE LopezTemmeSeriesGenerator(double a, double b, double c, std::complex<double> z) |
|
: n_(0), a_(a), b_(b), c_(c), phi_previous_(1.0), phi_(1 - 2 * b / c), z_(z), Z_(a * z / (z - 2.0)) {} |
|
|
|
XSF_HOST_DEVICE std::complex<double> operator()() { |
|
if (n_ == 0) { |
|
++n_; |
|
return 1.0; |
|
} |
|
if (n_ > 1) { |
|
double new_phi = ((n_ - 1) * phi_previous_ - (2.0 * b_ - c_) * phi_) / (c_ + (n_ - 1)); |
|
phi_previous_ = phi_; |
|
phi_ = new_phi; |
|
Z_ = Z_ * z_ / (z_ - 2.0) * ((a_ + (n_ - 1)) / n_); |
|
} |
|
++n_; |
|
return Z_ * phi_; |
|
} |
|
|
|
private: |
|
std::uint64_t n_; |
|
double a_, b_, c_, phi_previous_, phi_; |
|
std::complex<double> z_, Z_; |
|
}; |
|
|
|
XSF_HOST_DEVICE std::complex<double> hyp2f1_transform1_limiting_case(double a, double b, double c, double m, |
|
std::complex<double> z) { |
|
|
|
std::complex<double> result = 0.0; |
|
if (m >= 0) { |
|
if (m != 0) { |
|
auto series_generator = HypergeometricSeriesGenerator(a, b, 1 - m, 1.0 - z); |
|
result += four_gammas(m, c, a + m, b + m) * series_eval_fixed_length(series_generator, |
|
std::complex<double>{0.0, 0.0}, |
|
static_cast<std::uint64_t>(m)); |
|
} |
|
std::complex<double> prefactor = std::pow(-1.0, m + 1) * xsf::cephes::Gamma(c) / |
|
(xsf::cephes::Gamma(a) * xsf::cephes::Gamma(b)) * |
|
std::pow(1.0 - z, m); |
|
auto series_generator = Hyp2f1Transform1LimitSeriesGenerator(a + m, b + m, m, z); |
|
result += prefactor * series_eval(series_generator, std::complex<double>{0.0, 0.0}, hyp2f1_EPS, |
|
hyp2f1_MAXITER, "hyp2f1"); |
|
return result; |
|
} else { |
|
result = four_gammas(-m, c, a, b) * std::pow(1.0 - z, m); |
|
auto series_generator1 = HypergeometricSeriesGenerator(a + m, b + m, 1 + m, 1.0 - z); |
|
result *= series_eval_fixed_length(series_generator1, std::complex<double>{0.0, 0.0}, |
|
static_cast<std::uint64_t>(-m)); |
|
double prefactor = std::pow(-1.0, m + 1) * xsf::cephes::Gamma(c) * |
|
(xsf::cephes::rgamma(a + m) * xsf::cephes::rgamma(b + m)); |
|
auto series_generator2 = Hyp2f1Transform1LimitSeriesGenerator(a, b, -m, z); |
|
result += prefactor * series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS, |
|
hyp2f1_MAXITER, "hyp2f1"); |
|
return result; |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE std::complex<double> hyp2f1_transform2_limiting_case(double a, double b, double c, double m, |
|
std::complex<double> z) { |
|
|
|
|
|
auto series_generator1 = Hyp2f1Transform2LimitFinitePartGenerator(b, c, m, z); |
|
std::complex<double> result = cephes::Gamma(c) * cephes::rgamma(a) * std::pow(-z, -b); |
|
result *= |
|
series_eval_fixed_length(series_generator1, std::complex<double>{0.0, 0.0}, static_cast<std::uint64_t>(m)); |
|
std::complex<double> prefactor = cephes::Gamma(c) * (cephes::rgamma(a) * cephes::rgamma(c - b) * std::pow(-z, -a)); |
|
double n = c - a; |
|
if (abs(n - std::round(n)) < hyp2f1_EPS) { |
|
auto series_generator2 = Hyp2f1Transform2LimitSeriesCminusAIntGenerator(a, b, c, m, n, z); |
|
result += prefactor * series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS, |
|
hyp2f1_MAXITER, "hyp2f1"); |
|
return result; |
|
} |
|
auto series_generator2 = Hyp2f1Transform2LimitSeriesGenerator(a, b, c, m, z); |
|
result += prefactor * |
|
series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS, hyp2f1_MAXITER, "hyp2f1"); |
|
return result; |
|
} |
|
|
|
} |
|
|
|
XSF_HOST_DEVICE inline std::complex<double> hyp2f1(double a, double b, double c, std::complex<double> z) { |
|
|
|
|
|
|
|
|
|
if (a == 0 || b == 0) { |
|
return 1.0; |
|
} |
|
double z_abs = std::abs(z); |
|
|
|
if (z_abs == 0) { |
|
if (c != 0) { |
|
return 1.0; |
|
} else { |
|
|
|
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(), 0}; |
|
} |
|
} |
|
bool a_neg_int = a == std::trunc(a) && a < 0; |
|
bool b_neg_int = b == std::trunc(b) && b < 0; |
|
bool c_non_pos_int = c == std::trunc(c) and c <= 0; |
|
|
|
|
|
|
|
|
|
|
|
if (c_non_pos_int && !((a_neg_int && c <= a && a < 0) || (b_neg_int && c <= b && b < 0))) { |
|
return std::complex<double>{std::numeric_limits<double>::infinity(), 0}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::uint64_t max_degree; |
|
if (a_neg_int || b_neg_int) { |
|
if (a_neg_int && b_neg_int) { |
|
max_degree = a > b ? std::abs(a) : std::abs(b); |
|
} else if (a_neg_int) { |
|
max_degree = std::abs(a); |
|
} else { |
|
max_degree = std::abs(b); |
|
} |
|
if (max_degree <= UINT64_MAX) { |
|
auto series_generator = detail::HypergeometricSeriesGenerator(a, b, c, z); |
|
return detail::series_eval_fixed_length(series_generator, std::complex<double>{0.0, 0.0}, max_degree + 1); |
|
} else { |
|
set_error("hyp2f1", SF_ERROR_NO_RESULT, NULL); |
|
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(), |
|
std::numeric_limits<double>::quiet_NaN()}; |
|
} |
|
} |
|
|
|
if (std::abs(z + 1.0) < detail::hyp2f1_EPS && std::abs(1 + a - b - c) < detail::hyp2f1_EPS && !c_non_pos_int) { |
|
return detail::four_gammas(a - b + 1, 0.5 * a + 1, a + 1, 0.5 * a - b + 1); |
|
} |
|
std::complex<double> result; |
|
bool c_minus_a_neg_int = c - a == std::trunc(c - a) && c - a < 0; |
|
bool c_minus_b_neg_int = c - b == std::trunc(c - b) && c - b < 0; |
|
|
|
|
|
|
|
if (c_minus_a_neg_int || c_minus_b_neg_int) { |
|
max_degree = c_minus_b_neg_int ? std::abs(c - b) : std::abs(c - a); |
|
if (max_degree <= UINT64_MAX) { |
|
result = std::pow(1.0 - z, c - a - b); |
|
auto series_generator = detail::HypergeometricSeriesGenerator(c - a, c - b, c, z); |
|
result *= |
|
detail::series_eval_fixed_length(series_generator, std::complex<double>{0.0, 0.0}, max_degree + 2); |
|
return result; |
|
} else { |
|
set_error("hyp2f1", SF_ERROR_NO_RESULT, NULL); |
|
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(), |
|
std::numeric_limits<double>::quiet_NaN()}; |
|
} |
|
} |
|
|
|
|
|
|
|
if (std::abs(1 - z.real()) < detail::hyp2f1_EPS && z.imag() == 0 && c - a - b <= 0 && !c_non_pos_int) { |
|
return std::complex<double>{std::numeric_limits<double>::infinity(), 0}; |
|
} |
|
|
|
if (z == 1.0 && c - a - b > 0 && !c_non_pos_int) { |
|
return detail::four_gammas(c, c - a - b, c - a, c - b); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (z_abs < 0.9 && z.real() >= 0) { |
|
if (c - a < a && c - b < b) { |
|
result = std::pow(1.0 - z, c - a - b); |
|
auto series_generator = detail::HypergeometricSeriesGenerator(c - a, c - b, c, z); |
|
result *= detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
return result; |
|
} |
|
auto series_generator = detail::HypergeometricSeriesGenerator(a, b, c, z); |
|
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
} |
|
|
|
|
|
|
|
|
|
if (0.9 <= z_abs && z_abs < 1.1 && std::abs(1.0 - z) >= 0.9 && z.real() >= 0) { |
|
|
|
|
|
|
|
|
|
if ((c - a <= a && c - b < b) || (c - a < a && c - b <= b)) { |
|
auto series_generator = detail::LopezTemmeSeriesGenerator(c - a, c - b, c, z); |
|
result = std::pow(1.0 - 0.5 * z, a - c); |
|
result *= detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
return std::pow(1.0 - z, c - a - b) * result; |
|
} |
|
auto series_generator = detail::LopezTemmeSeriesGenerator(a, b, c, z); |
|
result = detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
return std::pow(1.0 - 0.5 * z, -a) * result; |
|
} |
|
|
|
|
|
|
|
if (z_abs < 1.1 && z.real() < 0) { |
|
if (0 < b && b < a && a < c) { |
|
std::swap(a, b); |
|
} |
|
auto series_generator = detail::HypergeometricSeriesGenerator(a, c - b, c, z / (z - 1.0)); |
|
return std::pow(1.0 - z, -a) * detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, |
|
detail::hyp2f1_EPS, detail::hyp2f1_MAXITER, "hyp2f1"); |
|
} |
|
|
|
if (0.9 <= z_abs && z_abs < 1.1) { |
|
if (std::abs(c - a - b - std::round(c - a - b)) < detail::hyp2f1_EPS) { |
|
|
|
double m = std::round(c - a - b); |
|
return detail::hyp2f1_transform1_limiting_case(a, b, c, m, z); |
|
} |
|
auto series_generator = detail::Hyp2f1Transform1Generator(a, b, c, z); |
|
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
} |
|
|
|
if (std::abs(a - b - std::round(a - b)) < detail::hyp2f1_EPS) { |
|
if (b > a) { |
|
std::swap(a, b); |
|
} |
|
double m = std::round(a - b); |
|
return detail::hyp2f1_transform2_limiting_case(a, b, c, m, z); |
|
} |
|
auto series_generator = detail::Hyp2f1Transform2Generator(a, b, c, z); |
|
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS, |
|
detail::hyp2f1_MAXITER, "hyp2f1"); |
|
} |
|
|
|
XSF_HOST_DEVICE inline std::complex<float> hyp2f1(float a, float b, float c, std::complex<float> x) { |
|
return static_cast<std::complex<float>>(hyp2f1(static_cast<double>(a), static_cast<double>(b), |
|
static_cast<double>(c), static_cast<std::complex<double>>(x))); |
|
} |
|
|
|
XSF_HOST_DEVICE inline double hyp2f1(double a, double b, double c, double x) { return cephes::hyp2f1(a, b, c, x); } |
|
|
|
XSF_HOST_DEVICE inline float hyp2f1(float a, float b, float c, float x) { |
|
return hyp2f1(static_cast<double>(a), static_cast<double>(b), static_cast<double>(c), static_cast<double>(x)); |
|
} |
|
|
|
} |
|
|