(ce: ComputeEngine, nNum: number, z: BigNum)
| 955 | result = result.add(BigDecimal.ONE.div(w)); |
| 956 | result = result.add(BigDecimal.ONE.div(w.mul(w).mul(2))); |
| 957 | let w2kp1 = w.mul(w)._mulToPrecision(w, p); // w^3 |
| 958 | const w2 = w._mulToPrecision(w, p); |
| 959 | const tol = new BigDecimal(10).pow(-(p + guard)); |
| 960 | const nTerms = Math.min(maxTerms, bernoulli.length); |
| 961 | for (let k = 0; k < nTerms; k++) { |
| 962 | const [bNum, bDen] = bernoulli[k]; |
| 963 | const term = new BigDecimal(bNum.toString()).div( |
| 964 | new BigDecimal(bDen.toString()).mul(w2kp1) |
| 965 | ); |
| 966 | if (k > 0 && term.abs().lt(tol)) break; |
| 967 | result = result.add(term); |
| 968 | w2kp1 = w2kp1._mulToPrecision(w2, p); |
| 969 | } |
| 970 | |
| 971 | return result; |
| 972 | } |
| 973 | |
| 974 | /** |
| 975 | * Bignum Polygamma function ψₙ(z) = dⁿ/dzⁿ ψ(z) |
| 976 | * Delegates to bigDigamma/bigTrigamma for n=0,1. |
| 977 | * For n ≥ 2, uses recurrence + asymptotic expansion. |
| 978 | */ |
| 979 | export function bigPolygamma(ce: ComputeEngine, n: BigNum, z: BigNum): BigNum { |
| 980 | const nNum = n.toNumber(); |
| 981 | if (!Number.isInteger(nNum) || nNum < 0) return BigDecimal.NAN; |
| 982 | if (nNum === 0) return bigDigamma(ce, z); // already guarded |
| 983 | if (nNum === 1) return bigTrigamma(ce, z); // already guarded |
| 984 | if (!z.isFinite() || z.isZero()) return BigDecimal.NAN; |
| 985 | return withGuardDigits(SPECIAL_FN_GUARD, () => polygammaCore(ce, nNum, z)); |
| 986 | } |
| 987 | |
| 988 | function polygammaCore(ce: ComputeEngine, nNum: number, z: BigNum): BigNum { |
| 989 | // Bignum factorial helper (small n, simple loop) |
| 990 | const bigFactorial = (m: number): BigNum => { |
| 991 | let r: BigNum = BigDecimal.ONE; |
| 992 | for (let i = 2; i <= m; i++) r = r.mul(i); |
| 993 | return r; |
| 994 | }; |
| 995 | |
| 996 | const p = BigDecimal.precision; |
| 997 | const guard = 10; |
| 998 | // Shift to w ≈ p so the asymptotic series converges in ≈0.4·p terms rather |
| 999 | // than running its full ≈π·w (see `gammalnCore`). |
| 1000 | const shift = Math.max(7, Math.ceil(p)); |
| 1001 | |
| 1002 | // Poles at z = 0, −1, −2, … |
| 1003 | let w = z; |
| 1004 | let result = new BigDecimal(0); |
| 1005 | if (w.isNegative() && w.isInteger()) return BigDecimal.NAN; |
| 1006 | |
| 1007 | // Recurrence (from ψ(z+1) = ψ(z) + 1/z differentiated n times, DLMF 5.15.5): |
| 1008 | // ψ⁽ⁿ⁾(z) = ψ⁽ⁿ⁾(z+1) + (−1)^{n+1} n!/z^{n+1} |
| 1009 | // The single shift loop also lifts negative non-integer z past the poles. |
| 1010 | const sign = nNum % 2 === 0 ? -1 : 1; // (−1)^{n+1} |
| 1011 | const bigFactN = bigFactorial(nNum); |
| 1012 | while (w.lt(shift)) { |
| 1013 | result = result.add( |
| 1014 | new BigDecimal(sign).mul(bigFactN).div(w.pow(nNum + 1)) |
no test coverage detected