FunctionOfExponentialFunctionAux: u with F^v → x (the new integration * variable), using the registered $base$/$expon$.
(u: Expression, x: string, st: FoeState)
| 3309 | // (g Cot)^p (a+b Cos)^m == (-g Tan[θ−π/2])^p (a−b Sin[θ−π/2])^m |
| 3310 | if (g.head === 'cot') |
| 3311 | return ce.function('Multiply', [ |
| 3312 | buildMono(ce, g.coef.neg(), 'tan', argM, g.exp), |
| 3313 | binomM, |
| 3314 | ]); |
| 3315 | // (g Tan)^p (a+b Cos)^m == (-g Cot[θ+π/2])^p (a+b Sin[θ+π/2])^m |
| 3316 | if (g.head === 'tan') |
| 3317 | return ce.function('Multiply', [ |
| 3318 | buildMono(ce, g.coef.neg(), 'cot', argP, g.exp), |
| 3319 | binomP, |
| 3320 | ]); |
| 3321 | } |
| 3322 | } |
| 3323 | return null; |
| 3324 | } |
| 3325 | |
| 3326 | // Standalone-cosine leaf shift (the poly·cos generalization of cosBaseToSin). |
| 3327 | // Rubi's DeactivateTrig reflects a lone linear-argument cosine onto the sine |
| 3328 | // chapter (`cos[e+f·x] → sin[e+π/2+f·x]`, source line 6576) as a LEAF identity — |
| 3329 | // it applies regardless of what x-dependent factors (a polynomial (c+d·x)^m, a |
| 3330 | // reciprocal (c+d·x)^-k, …) multiply the cosine. `cosBaseToSin`/`unifyInertTrig` |
| 3331 | // only cover the base of a `(a+b·cos)^n` power (x-free coefficient), so a |
| 3332 | // poly·cos product (`∫(c+d·x)^m·cos`, `∫cos/(c+d·x)^k`) was NOT reflected and |
| 3333 | // stranded — the sine-chapter reduction `∫(c+d·x)^m·sin → …+∫(c+d·x)^(m-1)·cos` |
| 3334 | // (4.1.10 #1) bottoms out in exactly such a `poly·cos` sub-integral whose |
| 3335 | // closing rule lives in the unbundled Cosine chapter. This full-tree leaf |
| 3336 | // rewrite closes it. Gated to fire ONLY when cosine is the SOLE trig head: any |
| 3337 | // other inert trig (sin/tan/cot/sec/csc) means a mixed cross-pair form where a |
| 3338 | // blind reflection desyncs arguments or steals a mixed-rule match — left to the |
| 3339 | // two-factor clauses in `unifyInertTrig` and the bundled mixed rules. |
| 3340 | const NON_COS_INERT_TRIG = new Set(['sin', 'tan', 'cot', 'sec', 'csc']); |
| 3341 | function hasNonCosInertTrig(e: Expression): boolean { |
| 3342 | if (NON_COS_INERT_TRIG.has(e.operator)) return true; |
| 3343 | return e.ops?.some(hasNonCosInertTrig) ?? false; |
| 3344 | } |
| 3345 | function hasLinearArgCos(e: Expression, x: string): boolean { |
| 3346 | if ( |
| 3347 | e.operator === 'cos' && |
| 3348 | e.ops?.length === 1 && |
| 3349 | polyDegreeX(e.ops[0], x) === 1 |
| 3350 | ) |
| 3351 | return true; |
| 3352 | return e.ops?.some((o) => hasLinearArgCos(o, x)) ?? false; |
| 3353 | } |
| 3354 | function cosLeafShiftRec( |
| 3355 | ce: ComputeEngine, |
| 3356 | e: Expression, |
| 3357 | x: string |
| 3358 | ): Expression { |
| 3359 | if ( |
| 3360 | e.operator === 'cos' && |
| 3361 | e.ops?.length === 1 && |
| 3362 | polyDegreeX(e.ops[0], x) === 1 |
| 3363 | ) |
| 3364 | return ce.function('sin', [e.ops[0].add(ce.Pi.div(2))]); |
| 3365 | const ops = e.ops; |
| 3366 | if (!ops || ops.length === 0) return e; |
| 3367 | const newOps = ops.map((o) => cosLeafShiftRec(ce, o, x)); |
| 3368 | if (newOps.every((o, i) => o === ops[i])) return e; |
no test coverage detected