(ce: ComputeEngine, z: BigNum)
| 889 | const tol = new BigDecimal(10).pow(-(p + guard)); |
| 890 | const nTerms = Math.min(maxTerms, bernoulli.length); |
| 891 | for (let k = 0; k < nTerms; k++) { |
| 892 | const [bNum, bDen] = bernoulli[k]; |
| 893 | const twoK = BigInt(2 * (k + 1)); |
| 894 | const term = new BigDecimal(bNum.toString()).div( |
| 895 | new BigDecimal((bDen * twoK).toString()).mul(w2k) |
| 896 | ); |
| 897 | if (k > 0 && term.abs().lt(tol)) break; |
| 898 | result = result.sub(term); |
| 899 | w2k = w2k._mulToPrecision(w2, p); |
| 900 | } |
| 901 | |
| 902 | return result; |
| 903 | } |
| 904 | |
| 905 | /** |
| 906 | * Bignum Trigamma function ψ₁(z) = d/dz ψ(z) = d²/dz² ln(Γ(z)) |
| 907 | * Same recurrence/asymptotic structure as digamma but for the second derivative. |
| 908 | */ |
| 909 | export function bigTrigamma(ce: ComputeEngine, z: BigNum): BigNum { |
| 910 | if (!z.isFinite()) return BigDecimal.NAN; |
| 911 | return withGuardDigits(SPECIAL_FN_GUARD, () => trigammaCore(ce, z)); |
| 912 | } |
| 913 | |
| 914 | function trigammaCore(ce: ComputeEngine, z: BigNum): BigNum { |
| 915 | // Reflection formula: ψ₁(1-z) + ψ₁(z) = π²/sin²(πz) |
| 916 | if (z.isNegative()) { |
| 917 | if (z.isInteger()) return BigDecimal.NAN; |
| 918 | const pi = BigDecimal.PI; |
| 919 | const s = pi.mul(z).sin(); |
| 920 | return pi |
| 921 | .mul(pi) |
| 922 | .div(s.mul(s)) |
| 923 | .sub(trigammaCore(ce, BigDecimal.ONE.sub(z))); |
| 924 | } |
| 925 | |
| 926 | if (z.isZero()) return BigDecimal.NAN; // pole |
| 927 | |
| 928 | const p = BigDecimal.precision; |
| 929 | const guard = 10; |
| 930 | // Shift to w ≈ p so the asymptotic series converges in ≈0.4·p terms rather |
| 931 | // than running its full ≈π·w (see `gammalnCore`). |
| 932 | const shift = Math.max(7, Math.ceil(p)); |
| 933 | |
| 934 | // Recurrence: ψ₁(z+1) = ψ₁(z) - 1/z² |
| 935 | let result = new BigDecimal(0); |
| 936 | let w = z; |
| 937 | while (w.lt(shift)) { |
| 938 | result = result.add(BigDecimal.ONE.div(w.mul(w))); |
| 939 | w = w.add(BigDecimal.ONE); |
| 940 | } |
| 941 | |
| 942 | const maxTerms = Math.max(20, Math.ceil(0.6 * p) + 20); |
| 943 | const bernoulli = getBernoulliRationals(ce, maxTerms); |
| 944 |
no test coverage detected