* Corrections for verified bugs in the frozen Rubi 4.17.3.0 source. Each entry * is keyed by a path substring + rule index and rewrites the rule's `rhs`. * Kept here (not hand-edited into the corpus JSON) so a regeneration preserves * the fix. Every correction must cite the math and stay minimal.
(filePath: string, rules: RubiRule[])
| 134 | * the fix. Every correction must cite the math and stay minimal. |
| 135 | */ |
| 136 | function applyUpstreamCorrections(filePath: string, rules: RubiRule[]): void { |
| 137 | // 1.1.3.6 (rules #19/#20): splitting (e+f·x^n) out of (g·x)^m gives |
| 138 | // f·x^n·(g·x)^m = (f/g^n)·(g·x)^(m+n) — the second term's coefficient is |
| 139 | // f/g^n, but the Rubi source writes f/e^n (e is the *constant* of the third |
| 140 | // binomial, not the coefficient g of (g·x)^m). With the common default g=1 |
| 141 | // this should be just f, yet f/e^n divides by e^n. Surfaced as a wrong |
| 142 | // antiderivative for ∫x^m·(c+d·x³)^(k/2)/(8c−d·x³) (1.1.3.4 two-binomial |
| 143 | // family). Matched by content, not index (f/e^n is never a correct split |
| 144 | // coefficient, and only these rules carry the pattern). Fix: f/e^n → f/g^n. |
| 145 | if (filePath.includes('1.1.3.6 (g x)^m')) { |
| 146 | const fix = (node: Json): Json => { |
| 147 | if (Array.isArray(node)) { |
| 148 | if ( |
| 149 | node[0] === 'Divide' && |
| 150 | node[1] === 'f' && |
| 151 | Array.isArray(node[2]) && |
| 152 | node[2][0] === 'Power' && |
| 153 | node[2][1] === 'e_var' |
| 154 | ) |
| 155 | return ['Divide', 'f', ['Power', 'g', node[2][2]]]; |
| 156 | return node.map(fix); |
| 157 | } |
| 158 | return node; |
| 159 | }; |
| 160 | for (const r of rules) r.rhs = fix(r.rhs); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | function normalizeRule(index: number, expr: Json, source: string): RubiRule { |
| 165 | const setDelayed = asCall(expr, 'SetDelayed'); |