* Shared evaluate logic for `Binomial` and `Choose` — the two names must * agree everywhere both are defined, so both handlers delegate here. * * - Exact integers (any sign of n): exact bigint result (see * `binomialBigint`), regardless of `numericApproximation`. * - Exact non-integers (ratio
( nExpr: Expression, kExpr: Expression, numericApproximation: boolean | undefined, ce: Expression['engine'] )
| 103 | if ((++steps & 0xffff) === 0) checkDeadline(deadline); |
| 104 | result = (result * (n - kk + i)) / i; |
| 105 | } |
| 106 | return result; |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Shared evaluate logic for `Binomial` and `Choose` — the two names must |
| 111 | * agree everywhere both are defined, so both handlers delegate here. |
| 112 | * |
| 113 | * - Exact integers (any sign of n): exact bigint result (see |
| 114 | * `binomialBigint`), regardless of `numericApproximation`. |
| 115 | * - Exact non-integers (rationals, radicals, symbolic constants like π): |
| 116 | * no closed form, so stay symbolic under plain `evaluate()`; under `.N()` |
| 117 | * numericize via the Gamma form Γ(n+1)/(Γ(k+1)·Γ(n−k+1)). |
| 118 | * - Inexact (float) operands numericize under both `evaluate()` and `.N()`, |
| 119 | * per the exactness contract (an inexact argument always numericizes). |
| 120 | * - Complex or non-numeric (symbolic) operands: stay symbolic (no closed |
| 121 | * form implemented for complex args; symbolic args can't be evaluated). |
| 122 | */ |
| 123 | /** |
| 124 | * Result type shared by `Binomial` and `Choose` (they share |
| 125 | * `evaluateBinomial` and must agree). Integer n, k → an integer (also for |
| 126 | * negative n, via the falling factorial). Real arguments go through the Γ |
| 127 | * ratio: finite real unless the numerator Γ(n+1) sits on a pole (negative |
| 128 | * integer n with a non-integer k) — there, and for non-finite or non-real |
| 129 | * arguments, nothing narrower than `number` is sound (`Binomial(∞, 2)` is |
| 130 | * NaN). |
| 131 | */ |
| 132 | function binomialType( |
| 133 | n: Expression | undefined, |
| 134 | k: Expression | undefined |
| 135 | ): Type { |
| 136 | if (!n || !k || n.isNaN || k.isNaN) return 'number'; |
| 137 | if (n.isFinite === false || k.isFinite === false) return 'number'; |
| 138 | if (n.isInteger === true && k.isInteger === true) return 'finite_integer'; |
| 139 | if (n.isReal === true && k.isReal === true) { |
| 140 | if (n.isInteger === true && n.isNegative === true) return 'number'; |
| 141 | return 'finite_real'; |
| 142 | } |
| 143 | return 'number'; |
| 144 | } |
| 145 | |
| 146 | function evaluateBinomial( |
| 147 | nExpr: Expression, |
| 148 | kExpr: Expression, |
| 149 | numericApproximation: boolean | undefined, |
| 150 | ce: Expression['engine'] |
| 151 | ): Expression | undefined { |
| 152 | // Exact integers: exact bigint arithmetic (handles negative n). |
| 153 | if ( |
| 154 | isNumber(nExpr) && |
| 155 | isNumber(kExpr) && |
| 156 | nExpr.im === 0 && |
no test coverage detected