Rubi BinomialParts[u,x] → {a,b,n} with u ≡ a + b·x^n (n ≠ 0, b ≠ 0)
(u: Expression, x: string)
| 1622 | case 'Negate': { |
| 1623 | const m = monoPartsX(ops[0], x); |
| 1624 | return m === null ? null : { coef: m.coef.neg(), exp: m.exp }; |
| 1625 | } |
| 1626 | case 'Multiply': { |
| 1627 | let coef = ce.One; |
| 1628 | let exp: Expression = ce.Zero; |
| 1629 | for (const f of ops) { |
| 1630 | if (!f.has(x)) { |
| 1631 | coef = coef.mul(f); |
| 1632 | continue; |
| 1633 | } |
| 1634 | const m = monoPartsX(f, x); |
| 1635 | if (m === null) return null; |
| 1636 | coef = coef.mul(m.coef); |
| 1637 | exp = exp.add(m.exp); |
| 1638 | } |
| 1639 | return { coef: coef.evaluate(), exp: exp.evaluate() }; |
| 1640 | } |
| 1641 | case 'Divide': { |
| 1642 | const mn = monoPartsX(ops[0], x); |
| 1643 | const md = monoPartsX(ops[1], x); |
| 1644 | if (mn === null || md === null) return null; |
| 1645 | return { |
| 1646 | coef: mn.coef.div(md.coef).evaluate(), |
| 1647 | exp: mn.exp.sub(md.exp).evaluate(), |
| 1648 | }; |
| 1649 | } |
| 1650 | } |
| 1651 | return null; |
| 1652 | } |
| 1653 | |
| 1654 | /** flatten a sum into its terms (Add/Subtract/Negate normalized) */ |
| 1655 | function sumTermsX(u: Expression): Expression[] { |
| 1656 | if (u.operator === 'Add' && u.ops) return u.ops.flatMap(sumTermsX); |
| 1657 | if (u.operator === 'Subtract' && u.ops) |
| 1658 | return [...sumTermsX(u.ops[0]), ...sumTermsX(u.ops[1]).map((t) => t.neg())]; |
| 1659 | if (u.operator === 'Negate' && u.ops) |
| 1660 | return sumTermsX(u.ops[0]).map((t) => t.neg()); |
| 1661 | return [u]; |
| 1662 | } |
| 1663 | |
| 1664 | /** group the structural sum terms of u into (exponent → summed coefficient) |
| 1665 | * classes; null when some term is not a structural monomial in x */ |
| 1666 | function monoClassesX( |
| 1667 | u: Expression, |
| 1668 | x: string |
| 1669 | ): { exp: Expression; coef: Expression }[] | null { |
| 1670 | const classes: { exp: Expression; coef: Expression }[] = []; |
| 1671 | for (const t of sumTermsX(u)) { |
| 1672 | const m = monoPartsX(t, x); |
| 1673 | if (m === null) return null; |
| 1674 | const cls = classes.find( |
| 1675 | (c) => c.exp.isSame(m.exp) || zeroQ(c.exp.sub(m.exp)) |
| 1676 | ); |
| 1677 | if (cls) cls.coef = cls.coef.add(m.coef).evaluate(); |
| 1678 | else classes.push({ exp: m.exp, coef: m.coef }); |
| 1679 | } |
| 1680 | return classes.filter((c) => !c.coef.isSame(0)); |
| 1681 | } |
no test coverage detected