|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "../config.h" |
|
#include "const.h" |
|
#include "gamma.h" |
|
#include "rgamma.h" |
|
|
|
namespace xsf { |
|
namespace cephes { |
|
|
|
XSF_HOST_DEVICE double beta(double, double); |
|
XSF_HOST_DEVICE double lbeta(double, double); |
|
|
|
namespace detail { |
|
constexpr double beta_ASYMP_FACTOR = 1e6; |
|
|
|
|
|
|
|
|
|
XSF_HOST_DEVICE inline double lbeta_asymp(double a, double b, int *sgn) { |
|
double r = lgam_sgn(b, sgn); |
|
r -= b * std::log(a); |
|
|
|
r += b * (1 - b) / (2 * a); |
|
r += b * (1 - b) * (1 - 2 * b) / (12 * a * a); |
|
r += -b * b * (1 - b) * (1 - b) / (12 * a * a * a); |
|
|
|
return r; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
XSF_HOST_DEVICE inline double beta_negint(int a, double b) { |
|
int sgn; |
|
if (b == static_cast<int>(b) && 1 - a - b > 0) { |
|
sgn = (static_cast<int>(b) % 2 == 0) ? 1 : -1; |
|
return sgn * xsf::cephes::beta(1 - a - b, b); |
|
} else { |
|
set_error("lbeta", SF_ERROR_OVERFLOW, NULL); |
|
return std::numeric_limits<double>::infinity(); |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE inline double lbeta_negint(int a, double b) { |
|
double r; |
|
if (b == static_cast<int>(b) && 1 - a - b > 0) { |
|
r = xsf::cephes::lbeta(1 - a - b, b); |
|
return r; |
|
} else { |
|
set_error("lbeta", SF_ERROR_OVERFLOW, NULL); |
|
return std::numeric_limits<double>::infinity(); |
|
} |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE inline double beta(double a, double b) { |
|
double y; |
|
int sign = 1; |
|
|
|
if (a <= 0.0) { |
|
if (a == std::floor(a)) { |
|
if (a == static_cast<int>(a)) { |
|
return detail::beta_negint(static_cast<int>(a), b); |
|
} else { |
|
goto overflow; |
|
} |
|
} |
|
} |
|
|
|
if (b <= 0.0) { |
|
if (b == std::floor(b)) { |
|
if (b == static_cast<int>(b)) { |
|
return detail::beta_negint(static_cast<int>(b), a); |
|
} else { |
|
goto overflow; |
|
} |
|
} |
|
} |
|
|
|
if (std::abs(a) < std::abs(b)) { |
|
y = a; |
|
a = b; |
|
b = y; |
|
} |
|
|
|
if (std::abs(a) > detail::beta_ASYMP_FACTOR * std::abs(b) && a > detail::beta_ASYMP_FACTOR) { |
|
|
|
y = detail::lbeta_asymp(a, b, &sign); |
|
return sign * std::exp(y); |
|
} |
|
|
|
y = a + b; |
|
if (std::abs(y) > detail::MAXGAM || std::abs(a) > detail::MAXGAM || std::abs(b) > detail::MAXGAM) { |
|
int sgngam; |
|
y = detail::lgam_sgn(y, &sgngam); |
|
sign *= sgngam; |
|
y = detail::lgam_sgn(b, &sgngam) - y; |
|
sign *= sgngam; |
|
y = detail::lgam_sgn(a, &sgngam) + y; |
|
sign *= sgngam; |
|
if (y > detail::MAXLOG) { |
|
goto overflow; |
|
} |
|
return (sign * std::exp(y)); |
|
} |
|
|
|
y = rgamma(y); |
|
a = Gamma(a); |
|
b = Gamma(b); |
|
if (std::isinf(y)) { |
|
goto overflow; |
|
} |
|
|
|
if (std::abs(std::abs(a*y) - 1.0) > std::abs(std::abs(b*y) - 1.0)) { |
|
y = b * y; |
|
y *= a; |
|
} else { |
|
y = a * y; |
|
y *= b; |
|
} |
|
|
|
return (y); |
|
|
|
overflow: |
|
set_error("beta", SF_ERROR_OVERFLOW, NULL); |
|
return (sign * std::numeric_limits<double>::infinity()); |
|
} |
|
|
|
|
|
|
|
XSF_HOST_DEVICE inline double lbeta(double a, double b) { |
|
double y; |
|
int sign; |
|
|
|
sign = 1; |
|
|
|
if (a <= 0.0) { |
|
if (a == std::floor(a)) { |
|
if (a == static_cast<int>(a)) { |
|
return detail::lbeta_negint(static_cast<int>(a), b); |
|
} else { |
|
goto over; |
|
} |
|
} |
|
} |
|
|
|
if (b <= 0.0) { |
|
if (b == std::floor(b)) { |
|
if (b == static_cast<int>(b)) { |
|
return detail::lbeta_negint(static_cast<int>(b), a); |
|
} else { |
|
goto over; |
|
} |
|
} |
|
} |
|
|
|
if (std::abs(a) < std::abs(b)) { |
|
y = a; |
|
a = b; |
|
b = y; |
|
} |
|
|
|
if (std::abs(a) > detail::beta_ASYMP_FACTOR * std::abs(b) && a > detail::beta_ASYMP_FACTOR) { |
|
|
|
y = detail::lbeta_asymp(a, b, &sign); |
|
return y; |
|
} |
|
|
|
y = a + b; |
|
if (std::abs(y) > detail::MAXGAM || std::abs(a) > detail::MAXGAM || std::abs(b) > detail::MAXGAM) { |
|
int sgngam; |
|
y = detail::lgam_sgn(y, &sgngam); |
|
sign *= sgngam; |
|
y = detail::lgam_sgn(b, &sgngam) - y; |
|
sign *= sgngam; |
|
y = detail::lgam_sgn(a, &sgngam) + y; |
|
sign *= sgngam; |
|
return (y); |
|
} |
|
|
|
y = rgamma(y); |
|
a = Gamma(a); |
|
b = Gamma(b); |
|
if (std::isinf(y)) { |
|
over: |
|
set_error("lbeta", SF_ERROR_OVERFLOW, NULL); |
|
return (sign * std::numeric_limits<double>::infinity()); |
|
} |
|
|
|
if (std::abs(std::abs(a*y) - 1.0) > std::abs(std::abs(b*y) - 1.0)) { |
|
y = b * y; |
|
y *= a; |
|
} else { |
|
y = a * y; |
|
y *= b; |
|
} |
|
|
|
if (y < 0) { |
|
y = -y; |
|
} |
|
|
|
return (std::log(y)); |
|
} |
|
} |
|
} |
|
|