( ce: ComputeEngine, u: Expression, x: string )
| 3003 | if (!ops || ops.length === 0) return e; |
| 3004 | const newOps = ops.map((o) => reciprocalToPowerRec(ce, o, frozen, keepRaw)); |
| 3005 | if (newOps.every((o, i) => o === ops[i])) return e; |
| 3006 | return ce.function(e.operator, newOps); |
| 3007 | } |
| 3008 | |
| 3009 | /** True if a `csc`/`sec` reciprocal head appears anywhere in the tree. */ |
| 3010 | function hasReciprocalTrig(e: Expression): boolean { |
| 3011 | if (RECIP_BASE[e.operator] !== undefined) return true; |
| 3012 | return e.ops?.some(hasReciprocalTrig) ?? false; |
| 3013 | } |
| 3014 | |
| 3015 | /** True if a `csc`/`sec` head appears raised to a NON-integer power (including |
| 3016 | * under `Sqrt`, or as `(coef·sec)^(1/2)`). Converting such a reciprocal to a |
| 3017 | * cosine/sine power is branch-unsafe — `√(b·sec) ≠ √(b/cos)` off the principal |
| 3018 | * branch — and even a branch-safe INTEGER `csc`/`sec` factor sharing the |
| 3019 | * integrand with such a half-integer reciprocal exposes it to elliptic rules |
| 3020 | * that return branch-wrong forms. So skip the whole rewrite for those |
| 3021 | * (R3 half-integer-power territory). A fractional power of a *plain* sin/cos |
| 3022 | * (with only integer csc/sec present) is unaffected — that conversion is safe |
| 3023 | * and closes real cases (e.g. `csc/(d·cos)^(7/2)`). */ |
| 3024 | function hasFractionalReciprocalTrig(e: Expression): boolean { |
| 3025 | if (e.operator === 'Sqrt') |
| 3026 | return e.ops?.[0] ? hasReciprocalTrig(e.ops[0]) : false; |
| 3027 | if (e.operator === 'Power' && e.ops?.length === 2) { |
| 3028 | const [base, exp] = e.ops; |
| 3029 | if (!isLiteralInteger(exp) && hasReciprocalTrig(base)) return true; |
| 3030 | } |
| 3031 | return e.ops?.some(hasFractionalReciprocalTrig) ?? false; |
| 3032 | } |
| 3033 | |
| 3034 | /** Rewrite inert reciprocal trig heads to negative powers: `csc[θ]→sin[θ]^-1`, |
| 3035 | * `sec[θ]→cos[θ]^-1` (identity when neither appears). See the block comment. |
| 3036 | * Only branch-safe (integer-exponent) occurrences are converted, and the whole |
| 3037 | * rewrite is skipped for integrands carrying a half-integer csc/sec power. */ |
| 3038 | export function reciprocalToPower( |
| 3039 | ce: ComputeEngine, |
no test coverage detected