/* Translated from Cython into C++ by SciPy developers in 2024. * * Original authors: Pauli Virtanen, Eric Moore */ // Binomial coefficient #pragma once #include "config.h" #include "cephes/beta.h" #include "cephes/gamma.h" namespace xsf { XSF_HOST_DEVICE inline double binom(double n, double k) { double kx, nx, num, den, dk, sgn; if (n < 0) { nx = std::floor(n); if (n == nx) { // Undefined return std::numeric_limits::quiet_NaN(); } } kx = std::floor(k); if (k == kx && (std::abs(n) > 1E-8 || n == 0)) { /* Integer case: use multiplication formula for less rounding * error for cases where the result is an integer. * * This cannot be used for small nonzero n due to loss of * precision. */ nx = std::floor(n); if (nx == n && kx > nx / 2 && nx > 0) { // Reduce kx by symmetry kx = nx - kx; } if (kx >= 0 && kx < 20) { num = 1.0; den = 1.0; for (int i = 1; i < 1 + static_cast(kx); i++) { num *= i + n - kx; den *= i; if (std::abs(num) > 1E50) { num /= den; den = 1.0; } } return num / den; } } // general case if (n >= 1E10 * k and k > 0) { // avoid under/overflows intermediate results return std::exp(-cephes::lbeta(1 + n - k, 1 + k) - std::log(n + 1)); } if (k > 1E8 * std::abs(n)) { // avoid loss of precision num = cephes::Gamma(1 + n) / std::abs(k) + cephes::Gamma(1 + n) * n / (2 * k * k); // + ... num /= M_PI * std::pow(std::abs(k), n); if (k > 0) { kx = std::floor(k); if (static_cast(kx) == kx) { dk = k - kx; sgn = (static_cast(kx) % 2 == 0) ? 1 : -1; } else { dk = k; sgn = 1; } return num * std::sin((dk - n) * M_PI) * sgn; } kx = std::floor(k); if (static_cast(kx) == kx) { return 0; } return num * std::sin(k * M_PI); } return 1 / (n + 1) / cephes::beta(1 + n - k, 1 + k); } XSF_HOST_DEVICE inline float binom(float n, float k) { return binom(static_cast(n), static_cast(k)); } } // namespace xsf