// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // factorial_utils.hpp // Factorial-related utilities (header-only templates) // // Functions provided: // doubleFactorial(n) — double factorial n!! // risingFactorial(x, n) — rising factorial (Pochhammer symbol) (x)_n // fallingFactorial(x, n) — falling factorial x^{(n)} // binomialCoefficient(n, k) — binomial coefficient C(n,k) // // Supported types: // integer types — doubleFactorial, binomialCoefficient // float, double, long double — risingFactorial, fallingFactorial #ifndef SANGI_SPECIAL_FACTORIAL_UTILS_HPP #define SANGI_SPECIAL_FACTORIAL_UTILS_HPP #include #include #include #include #include namespace sangi { namespace special { // ================================================================ // Double factorial n!! = n·(n-2)·(n-4)·... // ================================================================ /// Integer version: 0!! = 1, 1!! = 1, (-1)!! = 1 [[nodiscard]] inline long long doubleFactorial(int n) { if (n <= 1) return 1; long long result = 1; for (int k = n; k >= 2; k -= 2) { result *= k; } return result; } // ================================================================ // Rising factorial (Pochhammer symbol) (x)_n = x·(x+1)·...·(x+n-1) // ================================================================ /// Native floating-point types template [[nodiscard]] T risingFactorial(T x, int n) { if (n == 0) return T(1); if (n < 0) { // (x)_{-n} is rarely used, but by definition it is Γ(x)/Γ(x+n) return std::numeric_limits::quiet_NaN(); } T result = T(1); for (int k = 0; k < n; k++) { result *= (x + static_cast(k)); } return result; } // Float type: to be added later as needed // ================================================================ // Falling factorial x^{(n)} = x·(x-1)·...·(x-n+1) // ================================================================ /// Native floating-point types template [[nodiscard]] T fallingFactorial(T x, int n) { if (n == 0) return T(1); if (n < 0) { return std::numeric_limits::quiet_NaN(); } T result = T(1); for (int k = 0; k < n; k++) { result *= (x - static_cast(k)); } return result; } // Float type: to be added later as needed // ================================================================ // Binomial coefficient C(n, k) = n! / (k! · (n-k)!) // ================================================================ /// Integer version (watch for overflow: n ≤ 62 or so is safe) [[nodiscard]] inline long long binomialCoefficient(int n, int k) { if (k < 0 || k > n) return 0; if (k == 0 || k == n) return 1; if (k > n - k) k = n - k; // exploit symmetry long long result = 1; for (int i = 0; i < k; i++) { result = result * (n - i) / (i + 1); } return result; } /// Floating-point version (handles large n, and real n too) template [[nodiscard]] T binomialCoefficientReal(T n, int k) { if (k < 0) return T(0); if (k == 0) return T(1); T result = T(1); for (int i = 0; i < k; i++) { result *= (n - static_cast(i)) / static_cast(i + 1); } return result; } } // namespace special } // namespace sangi #endif // SANGI_SPECIAL_FACTORIAL_UTILS_HPP