|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once |
|
|
|
#include "config.h" |
|
#include "error.h" |
|
|
|
#include "expint.h" |
|
#include "cephes/const.h" |
|
#include "cephes/sici.h" |
|
#include "cephes/shichi.h" |
|
|
|
namespace xsf { |
|
namespace detail { |
|
|
|
XSF_HOST_DEVICE inline void sici_power_series(int sgn, std::complex<double> z, |
|
std::complex<double> *s, std::complex<double> *c) { |
|
|
|
|
|
|
|
std::complex<double> fac = z; |
|
*s = fac; |
|
*c = 0; |
|
std::complex<double> term1, term2; |
|
for (int n = 1; n < 100; n++) { |
|
fac *= static_cast<double>(sgn)*z/(2.0*n); |
|
term2 = fac/(2.0*n); |
|
*c += term2; |
|
fac *= z/(2.0*n + 1.0); |
|
term1 = fac/(2.0*n + 1.0); |
|
*s += term1; |
|
constexpr double tol = std::numeric_limits<double>::epsilon(); |
|
if (std::abs(term1) < tol*std::abs(*s) && std::abs(term2) < tol*std::abs(*c)) { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
XSF_HOST_DEVICE inline int sici(std::complex<double> z, |
|
std::complex<double> *si, std::complex<double> *ci) { |
|
|
|
|
|
|
|
|
|
constexpr double EULER = xsf::cephes::detail::SCIPY_EULER; |
|
|
|
if (z == std::numeric_limits<double>::infinity()) { |
|
*si = M_PI_2; |
|
*ci = 0; |
|
return 0; |
|
} |
|
if (z == -std::numeric_limits<double>::infinity()) { |
|
*si = -M_PI_2; |
|
*ci = {0.0, M_PI}; |
|
return 0; |
|
} |
|
|
|
if (std::abs(z) < 0.8) { |
|
|
|
detail::sici_power_series(-1, z, si, ci); |
|
|
|
if (z == 0.0) { |
|
set_error("sici", SF_ERROR_DOMAIN, NULL); |
|
*ci = {-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::quiet_NaN()}; |
|
} else { |
|
*ci += EULER + std::log(z); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
std::complex<double> jz = std::complex<double>(0.0, 1.0) * z; |
|
std::complex<double> term1 = expi(jz); |
|
std::complex<double> term2 = expi(-jz); |
|
*si = std::complex<double>(0.0, -0.5)*(term1 - term2); |
|
*ci = 0.5*(term1 + term2); |
|
if (z.real() == 0) { |
|
if (z.imag() > 0) { |
|
*ci += std::complex<double>(0.0, M_PI_2); |
|
} else if (z.imag() < 0) { |
|
*ci -= std::complex<double>(0.0, M_PI_2); |
|
} |
|
} else if (z.real() > 0) { |
|
*si -= M_PI_2; |
|
} else { |
|
*si += M_PI_2; |
|
if (z.imag() >= 0) { |
|
*ci += std::complex<double>(0.0, M_PI); |
|
} else { |
|
*ci -= std::complex<double>(0.0, M_PI); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
XSF_HOST_DEVICE inline int sici(std::complex<float> z, |
|
std::complex<float> *si_f, std::complex<float> *ci_f) { |
|
std::complex<double> si; |
|
std::complex<double> ci; |
|
int res = sici(z, &si, &ci); |
|
*si_f = si; |
|
*ci_f = ci; |
|
return res; |
|
} |
|
|
|
XSF_HOST_DEVICE inline int shichi(std::complex<double> z, |
|
std::complex<double> *shi, std::complex<double> *chi) { |
|
|
|
|
|
|
|
constexpr double EULER = xsf::cephes::detail::SCIPY_EULER; |
|
if (z == std::numeric_limits<double>::infinity()) { |
|
*shi = std::numeric_limits<double>::infinity(); |
|
*chi = std::numeric_limits<double>::infinity(); |
|
return 0; |
|
} |
|
if (z == -std::numeric_limits<double>::infinity()) { |
|
*shi = -std::numeric_limits<double>::infinity(); |
|
*chi = std::numeric_limits<double>::infinity(); |
|
return 0; |
|
} |
|
if (std::abs(z) < 0.8) { |
|
|
|
detail::sici_power_series(1, z, shi, chi); |
|
if (z == 0.0) { |
|
set_error("shichi", SF_ERROR_DOMAIN, NULL); |
|
*chi = {-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::quiet_NaN()}; |
|
} else { |
|
*chi += EULER + std::log(z); |
|
} |
|
return 0; |
|
} |
|
|
|
std::complex<double> term1 = expi(z); |
|
std::complex<double> term2 = expi(-z); |
|
*shi = 0.5*(term1 - term2); |
|
*chi = 0.5*(term1 + term2); |
|
if (z.imag() > 0) { |
|
*shi -= std::complex<double>(0.0, 0.5*M_PI); |
|
*chi += std::complex<double>(0.0, 0.5*M_PI); |
|
} else if (z.imag() < 0) { |
|
*shi += std::complex<double>(0.0, 0.5*M_PI); |
|
*chi -= std::complex<double>(0.0, 0.5*M_PI); |
|
} else if (z.real() < 0) { |
|
*chi += std::complex<double>(0.0, M_PI); |
|
} |
|
return 0; |
|
} |
|
|
|
XSF_HOST_DEVICE inline int shichi(std::complex<float> z, |
|
std::complex<float> *shi_f, std::complex<float> *chi_f) { |
|
std::complex<double> shi; |
|
std::complex<double> chi; |
|
int res = shichi(z, &shi, &chi); |
|
*shi_f = shi; |
|
*chi_f = chi; |
|
return res; |
|
} |
|
|
|
XSF_HOST_DEVICE inline int sici(double x, double *si, double *ci) { |
|
return cephes::sici(x, si, ci); |
|
} |
|
|
|
XSF_HOST_DEVICE inline int shichi(double x, double *shi, double *chi) { |
|
return cephes::shichi(x, shi, chi); |
|
} |
|
|
|
XSF_HOST_DEVICE inline int sici(float x, float *si_f, float *ci_f) { |
|
double si; |
|
double ci; |
|
int res = cephes::sici(x, &si, &ci); |
|
*si_f = si; |
|
*ci_f = ci; |
|
return res; |
|
} |
|
|
|
XSF_HOST_DEVICE inline int shichi(float x, float *shi_f, float *chi_f) { |
|
double shi; |
|
double chi; |
|
int res = cephes::shichi(x, &shi, &chi); |
|
*shi_f = shi; |
|
*chi_f = chi; |
|
return res; |
|
} |
|
} |
|
|