( e0: Expression, x: string, ce: ComputeEngine, depth: number )
| 204 | |
| 205 | // ────────────────────────────────────────────────────────────────────────── |
| 206 | // Finite point |
| 207 | // ────────────────────────────────────────────────────────────────────────── |
| 208 | |
| 209 | /** |
| 210 | * Sign of `c_v·(x − a)^v` (the leading Laurent term) as `x → a` from `dir` |
| 211 | * (+1 right, −1 left, 0 two-sided), or `undefined` when indeterminate: |
| 212 | * a two-sided approach with odd `|v|` (the two sides disagree), or a |
| 213 | * leading coefficient whose sign the engine cannot decide. |
| 214 | */ |
| 215 | function laurentSignNear( |
| 216 | L: { v: number; coeff: (p: number) => Expression }, |
| 217 | dir: number |
| 218 | ): 1 | -1 | undefined { |
| 219 | const odd = Math.abs(L.v) % 2 === 1; |
| 220 | if (dir === 0 && odd) return undefined; |
| 221 | const c = L.coeff(L.v).evaluate(); |
| 222 | if (!c.isValid || c.isNaN === true) return undefined; |
| 223 | let s: 1 | -1; |
| 224 | if (c.isPositive === true) s = 1; |
| 225 | else if (c.isNegative === true) s = -1; |
| 226 | else return undefined; |
| 227 | // From the left, x − a = −t (t > 0): odd powers flip the sign. |
| 228 | if (dir === -1 && odd) s = s === 1 ? -1 : 1; |
| 229 | return s; |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Signed-infinity resolution of a pole (negative-valuation Laurent |
| 234 | * expansion) — the 2026-07-10 convention decision: a *directional* limit at |
| 235 | * a pole resolves to `±∞` from the sign of the leading coefficient and the |
| 236 | * parity of the valuation; a *two-sided* limit resolves only when both |
| 237 | * sides agree (even valuation, `lim 1/x² = +∞`). An odd-valuation two-sided |
| 238 | * limit (`lim 1/x` at 0) stays inert — the engine does not produce |
| 239 | * `ComplexInfinity` limits. |
| 240 | */ |
| 241 | function signedPoleInfinity( |
| 242 | L: { v: number; coeff: (p: number) => Expression }, |
| 243 | dir: number, |
| 244 | ce: ComputeEngine |
| 245 | ): Expression | undefined { |
| 246 | if (L.v >= 0) return undefined; |
| 247 | const s = laurentSignNear(L, dir); |
| 248 | if (s === undefined) return undefined; |
| 249 | return s > 0 ? ce.PositiveInfinity : ce.NegativeInfinity; |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * Operator heads with JUMP discontinuities: piecewise-constant-like |
| 254 | * functions whose value changes discontinuously as their argument crosses a |
| 255 | * threshold. Direct substitution is unsound for a subterm of one of these |
| 256 | * sitting ON its jump: the value AT the point is not the limit from either |
| 257 | * side (`sgn(0) = 0`, but `lim_{x→0⁻} sgn x = −1`). The finite-point |
| 258 | * strategies resolve such a subterm per DIRECTION instead — see |
| 259 | * `substituteAtFinitePoint`. Kink-continuous heads (`Abs`) do not belong |
| 260 | * here: their value at the point IS the two-sided limit. |
| 261 | */ |
| 262 | const JUMP_FNS = ['Sign', 'Heaviside', 'Floor', 'Ceil', 'Round', 'Mod']; |
| 263 |
no test coverage detected