| 174 | * This requires base to be positive for real results. |
| 175 | */ |
| 176 | export function powInterval( |
| 177 | base: Interval | IntervalResult, |
| 178 | exp: Interval | IntervalResult |
| 179 | ): IntervalResult { |
| 180 | const unwrapped = unwrapOrPropagate(base, exp); |
| 181 | if (!Array.isArray(unwrapped)) return unwrapped; |
| 182 | const [baseVal, expVal] = unwrapped; |
| 183 | |
| 184 | // Special case: exponent is a point interval with an integer value. |
| 185 | // For integer exponents, negative bases are well-defined (parity matters). |
| 186 | // This is critical for patterns like (-1)^k in summations. |
| 187 | if (expVal.lo === expVal.hi && Number.isInteger(expVal.lo)) { |
| 188 | return pow(baseVal, expVal.lo); |
| 189 | } |
| 190 | |
| 191 | // For real-valued results with non-integer exponents, base must be positive |
| 192 | if (baseVal.hi <= 0) { |
| 193 | // Special case: base is exactly -1 and exponent spans at least two |
| 194 | // consecutive integers. (-1)^n alternates between -1 and 1, so the |
| 195 | // tightest enclosure is [-1, 1]. |
| 196 | if ( |
| 197 | baseVal.lo === -1 && |
| 198 | baseVal.hi === -1 && |
| 199 | Math.floor(expVal.hi) > Math.floor(expVal.lo) |
| 200 | ) { |
| 201 | return ok({ lo: -1, hi: 1 }); |
| 202 | } |
| 203 | return { kind: 'empty' }; |
| 204 | } |
| 205 | if (baseVal.lo <= 0) { |
| 206 | // Straddles or touches zero - complex behavior |
| 207 | // For safety, restrict to positive part |
| 208 | const posBase = { |
| 209 | lo: Math.max(baseVal.lo, Number.EPSILON), |
| 210 | hi: baseVal.hi, |
| 211 | }; |
| 212 | const corners = [ |
| 213 | Math.pow(posBase.lo, expVal.lo), |
| 214 | Math.pow(posBase.lo, expVal.hi), |
| 215 | Math.pow(posBase.hi, expVal.lo), |
| 216 | Math.pow(posBase.hi, expVal.hi), |
| 217 | ]; |
| 218 | return { |
| 219 | kind: 'partial', |
| 220 | value: { lo: Math.min(...corners), hi: Math.max(...corners) }, |
| 221 | domainClipped: 'lo', |
| 222 | }; |
| 223 | } |
| 224 | |
| 225 | // Both base values are positive |
| 226 | const corners = [ |
| 227 | Math.pow(baseVal.lo, expVal.lo), |
| 228 | Math.pow(baseVal.lo, expVal.hi), |
| 229 | Math.pow(baseVal.hi, expVal.lo), |
| 230 | Math.pow(baseVal.hi, expVal.hi), |
| 231 | ]; |
| 232 | return ok({ lo: Math.min(...corners), hi: Math.max(...corners) }); |
| 233 | } |