Richardson-extrapolated central difference F′(x) at a (possibly * complex) point, differencing along the real axis: combines steps h and * h/2 for O(h⁴) truncation — high-order poles like x⁻¹⁰⁰ and degree-20 * polynomial antiderivatives are far outside plain central-difference * accuracy at REL_
( F: BoxedExpression, x: string, xv: number | C, h: number )
| 162 | if (!Number.isFinite(re) || !Number.isFinite(im)) return null; |
| 163 | return { re, im }; |
| 164 | } |
| 165 | |
| 166 | /** Richardson-extrapolated central difference F′(x) at a (possibly |
| 167 | * complex) point, differencing along the real axis: combines steps h and |
| 168 | * h/2 for O(h⁴) truncation — high-order poles like x⁻¹⁰⁰ and degree-20 |
| 169 | * polynomial antiderivatives are far outside plain central-difference |
| 170 | * accuracy at REL_TOL. */ |
| 171 | function dF( |
| 172 | F: BoxedExpression, |
| 173 | x: string, |
| 174 | xv: number | C, |
| 175 | h: number |
| 176 | ): C | null { |
| 177 | const at = (d: number): C | null => |
| 178 | complexAt( |
| 179 | F, |
| 180 | x, |
| 181 | typeof xv === 'number' ? xv + d : { re: xv.re + d, im: xv.im } |
| 182 | ); |
| 183 | const Fp = at(h); |
| 184 | const Fm = at(-h); |
| 185 | if (!Fp || !Fm) return null; |
| 186 | // catastrophic-cancellation guard: when F(x±h) agree to ~9+ digits the |
| 187 | // difference is double-precision noise (degree-20 expanded polynomial |
| 188 | // antiderivatives evaluated near a root of the integrand) — no usable |
| 189 | // derivative signal at this point |
| 190 | const fMag = Math.max( |
| 191 | Math.hypot(Fp.re, Fp.im), |
| 192 | Math.hypot(Fm.re, Fm.im) |
| 193 | ); |
| 194 | if (Math.hypot(Fp.re - Fm.re, Fp.im - Fm.im) < 1e-9 * fMag) return null; |
| 195 | const d1 = { re: (Fp.re - Fm.re) / (2 * h), im: (Fp.im - Fm.im) / (2 * h) }; |
| 196 | const Fp2 = at(h / 2); |
| 197 | const Fm2 = at(-h / 2); |
no test coverage detected