( P: Expression, L: Expression, x: string )
| 367 | return monomialsX(ex, x); |
| 368 | } |
| 369 | case 'Multiply': { |
| 370 | let acc: [Expression, number][] = [[ce.One, 0]]; |
| 371 | for (const f of ops) { |
| 372 | const m = monomialsX(f, x); |
| 373 | if (m === null) return null; |
| 374 | const next: [Expression, number][] = []; |
| 375 | for (const [c1, d1] of acc) |
| 376 | for (const [c2, d2] of m) next.push([c1.mul(c2), d1 + d2]); |
| 377 | if (next.length > 256) return null; |
| 378 | acc = next; |
| 379 | } |
| 380 | return acc; |
| 381 | } |
| 382 | } |
| 383 | return null; |
| 384 | } |
| 385 | |
| 386 | /** ascending coefficient array, or null if not a polynomial in x */ |
| 387 | export function polyCoeffsX(u: Expression, x: string): Expression[] | null { |
| 388 | const ce = u.engine; |
| 389 | const ms = monomialsX(u, x); |
| 390 | if (ms === null) return null; |
| 391 | const deg = ms.reduce((m, [, d]) => Math.max(m, d), 0); |
| 392 | const coeffs: Expression[] = new Array(deg + 1).fill(ce.Zero); |
| 393 | for (const [c, d] of ms) coeffs[d] = coeffs[d].add(c); |
| 394 | return coeffs.map((c) => c.evaluate()); |
| 395 | } |
| 396 | |
| 397 | /** effective degree after dropping provably-zero leading coefficients */ |
| 398 | function trimZeros(coeffs: Expression[]): Expression[] { |
| 399 | let last = coeffs.length - 1; |
| 400 | while (last > 0 && zeroQ(coeffs[last])) last--; |
| 401 | return coeffs.slice(0, last + 1); |
| 402 | } |
| 403 | |
| 404 | /** long division P / L over symbolic coefficients → [quotient, remainder] */ |
| 405 | export function polyDivideX( |
no test coverage detected