/* Translated from Cython into C++ by SciPy developers in 2024. * Original header with Copyright information appears below. */ /* Implementation of sin/cos/sinh/cosh integrals for complex arguments * * Sources * [1] Fredrik Johansson and others. mpmath: a Python library for * arbitrary-precision floating-point arithmetic (version 0.19), * December 2013. http://mpmath.org/. * [2] NIST, "Digital Library of Mathematical Functions", * https://dlmf.nist.gov/ */ #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 z, std::complex *s, std::complex *c) { /* DLMF 6.6.5 and 6.6.6. If sgn = -1 computes si/ci, and if sgn = 1 * computes shi/chi. */ std::complex fac = z; *s = fac; *c = 0; std::complex term1, term2; for (int n = 1; n < 100; n++) { fac *= static_cast(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::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 z, std::complex *si, std::complex *ci) { /* Compute sin/cos integrals at complex arguments. The algorithm * largely follows that of [1]. */ constexpr double EULER = xsf::cephes::detail::SCIPY_EULER; if (z == std::numeric_limits::infinity()) { *si = M_PI_2; *ci = 0; return 0; } if (z == -std::numeric_limits::infinity()) { *si = -M_PI_2; *ci = {0.0, M_PI}; return 0; } if (std::abs(z) < 0.8) { // Use the series to avoid cancellation in si detail::sici_power_series(-1, z, si, ci); if (z == 0.0) { set_error("sici", SF_ERROR_DOMAIN, NULL); *ci = {-std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}; } else { *ci += EULER + std::log(z); } return 0; } // DLMF 6.5.5/6.5.6 plus DLMF 6.4.4/6.4.6/6.4.7 std::complex jz = std::complex(0.0, 1.0) * z; std::complex term1 = expi(jz); std::complex term2 = expi(-jz); *si = std::complex(0.0, -0.5)*(term1 - term2); *ci = 0.5*(term1 + term2); if (z.real() == 0) { if (z.imag() > 0) { *ci += std::complex(0.0, M_PI_2); } else if (z.imag() < 0) { *ci -= std::complex(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(0.0, M_PI); } else { *ci -= std::complex(0.0, M_PI); } } return 0; } XSF_HOST_DEVICE inline int sici(std::complex z, std::complex *si_f, std::complex *ci_f) { std::complex si; std::complex ci; int res = sici(z, &si, &ci); *si_f = si; *ci_f = ci; return res; } XSF_HOST_DEVICE inline int shichi(std::complex z, std::complex *shi, std::complex *chi) { /* Compute sinh/cosh integrals at complex arguments. The algorithm * largely follows that of [1]. */ constexpr double EULER = xsf::cephes::detail::SCIPY_EULER; if (z == std::numeric_limits::infinity()) { *shi = std::numeric_limits::infinity(); *chi = std::numeric_limits::infinity(); return 0; } if (z == -std::numeric_limits::infinity()) { *shi = -std::numeric_limits::infinity(); *chi = std::numeric_limits::infinity(); return 0; } if (std::abs(z) < 0.8) { // Use the series to avoid cancellation in shi detail::sici_power_series(1, z, shi, chi); if (z == 0.0) { set_error("shichi", SF_ERROR_DOMAIN, NULL); *chi = {-std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}; } else { *chi += EULER + std::log(z); } return 0; } std::complex term1 = expi(z); std::complex term2 = expi(-z); *shi = 0.5*(term1 - term2); *chi = 0.5*(term1 + term2); if (z.imag() > 0) { *shi -= std::complex(0.0, 0.5*M_PI); *chi += std::complex(0.0, 0.5*M_PI); } else if (z.imag() < 0) { *shi += std::complex(0.0, 0.5*M_PI); *chi -= std::complex(0.0, 0.5*M_PI); } else if (z.real() < 0) { *chi += std::complex(0.0, M_PI); } return 0; } XSF_HOST_DEVICE inline int shichi(std::complex z, std::complex *shi_f, std::complex *chi_f) { std::complex shi; std::complex 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; } }