@@ -60,26 +60,32 @@ export function canonicalPower(
6060const unchanged = ( ) =>
6161ce . _fn ( 'Power' , [ a , b ] , { canonical :fullyCanonical } ) ;
6262
63- // Exclude cases - which may otherwise be valid - of the exponent either: being a function (e.g.
64- // '0 + 0'), a non-constant value (symbols with 'assumed' values, impure functions), or a
65- // non-numeric type (no concern for this).
63+ if ( a . operator === 'Power' ) {
64+ const [ base , aPow ] = a . ops ! ;
65+ return ce . _fn ( 'Power' , [
66+ base ,
67+ ce . box ( [ 'Multiply' , aPow , b ] , {
68+ canonical :fullyCanonical || 'Power' ,
69+ } ) ,
70+ ] ) ;
71+ }
72+
73+ // Onwards, the focus on operations is where is a *numeric* exponent.
74+ // Therefore, exclude cases - which may otherwise be valid - of the exponent either: being a function (e.g.
75+ // '0 + 0'), a symbol, or of a non-numeric type.
6676//
6777//@consider :possible exceptions where function-expressions are reasonable :Rational,Half,
6878// Negate... (However, provided that canonicalNumber provided prior, should not be missing anything
6979// here)
7080if (
7181b . isFunctionExpression ||
72- ! b . isConstant || //(!notably, should exclude symbol assumptions)
82+ b . symbol !== null ||
7383! b . type . matches ( 'number' as Type )
7484)
7585return unchanged ( ) ;
7686
77- // 'a' is constant and of type 'number'. Used often.
78- // (note that this could still be a numeric expression, too...)
79- const aIsNum = a . isConstant && a . type . matches ( 'number' as NumericType ) ;
80-
8187// Zero as base
82- if ( aIsNum && a . is ( 0 ) ) {
88+ if ( a . isNumberLiteral && a . is ( 0 ) ) {
8389if ( b . type . matches ( 'imaginary' as NumericType ) || b . isNaN ) return ce . NaN ;
8490
8591if ( b . is ( 0 ) ) return ce . NaN ;
@@ -98,9 +104,19 @@ export function canonicalPower(
98104return unchanged ( ) ; // No other canonicalization cases with this base
99105}
100106
107+ // 'a'/base has an associated number value (excludes numeric functions)
108+ // (this should at this stage include library-defined symbols such as 'Pi')
109+ //@note : include 'Negate', because this could be wrapped around a number-valued symbol, such as
110+ //'Pi'...
111+ //^there could exist other exceptions: perhaps consider a util. such as 'maybeNumber'?
112+ const aIsNum =
113+ a . type . matches ( 'number' as NumericType ) &&
114+ ( a . isFunctionExpression === false || a . operator === 'Negate' ) ;
115+
101116// Zero as exponent
102117if ( b . is ( 0 ) ) {
103- if ( aIsNum ) return a . isFinite ?ce . One :ce . NaN ;
118+ // If 'isFinite' is a boolean, then 'a' has a value.
119+ if ( aIsNum && a . isFinite !== undefined ) return a . isFinite ?ce . One :ce . NaN ;
104120return unchanged ( ) ;
105121}
106122
@@ -110,7 +126,8 @@ export function canonicalPower(
110126if ( aIsNum && a . is ( 1 ) ) return b . isFinite ?ce . One :ce . NaN ;
111127
112128// One as exponent
113- if ( b . is ( 1 ) ) return a ;
129+ // (Permit the base to be a FN-expr. here, too...)
130+ if ( b . is ( 1 ) && a . type . matches ( 'number' as NumericType ) ) return a ;
114131
115132// -1 exponent
116133if ( b . is ( - 1 ) ) {
@@ -177,8 +194,8 @@ export function canonicalPower(
177194return ce . NaN ;
178195}
179196
180- //'Infinity^Complex'
181- if ( a . isInfinity ) {
197+ //'AnyInfinity^{~oo}' (i.e. ComplexInfinity)
198+ if ( a . isNumberLiteral && a . isInfinity ) {
182199// If the exponent is pure imaginary, the result is NaN
183200//(↓fix?:ensure both these cases narrow down to 'b' being a num./symbol literal)
184201if ( b . type . matches ( 'imaginary' as NumericType ) ) return ce . NaN ;
@@ -273,93 +290,14 @@ export function pow(
273290
274291if ( typeof exp !== 'number' ) exp = exp . canonical ;
275292
276- const e = typeof exp === 'number' ?exp :exp . im === 0 ?exp . re :undefined ;
277-
278- // x^0 = 1
279- if ( e === 0 ) return ce . One ;
280- // x^1 = x
281- if ( e === 1 ) return x ;
282-
283- if ( e === - 1 ) {
284- // (-∞)^-1 = 0
285- if ( x . isInfinity && x . isNegative ) return ce . Zero ;
286-
287- // (-1)^-1 = -1
288- if ( x . is ( - 1 ) ) return ce . NegativeOne ;
289-
290- // 0^-1 = ~∞
291- // This is not strictly true, as 0^-1 may be undefined, but is convenient in some contexts where the base is assumed to be positive.
292- if ( x . is ( 0 ) ) return ce . ComplexInfinity ;
293+ // 'canonicalPower' deals with a set of basic operations.
294+ // If the result is not 'Power', can assume an op. has occurred
295+ // In some cases, an op. may apply, but a 'Power' expr. is still the result ('(a^b)^c -> a^(b*c)'
296+ // for instance). For these cases, proceed.
297+ const canonicalResult = canonicalPower ( x , ce . box ( exp ) ) ;
298+ if ( canonicalResult . operator !== 'Power' ) return canonicalResult ;
293299
294- // 1^-1 = 1
295- if ( x . is ( 1 ) ) return ce . One ;
296-
297- // ∞^-1 = 0
298- if ( x . isInfinity && x . isPositive ) return ce . Zero ;
299-
300- return x . inv ( ) ;
301- }
302-
303- if ( e === Number . POSITIVE_INFINITY ) {
304- // 0^∞ = 0
305- // Because for all complex numbers z near 0, z^∞ -> 0.
306- if ( x . is ( 0 ) ) return ce . Zero ;
307-
308- // 1^∞ = NaN
309- // Because there are various cases where lim(x(t),t)=1, lim(y(t),t)=∞ (or -∞), but lim( x(t)^y(t), t) != 1.
310- if ( x . is ( 1 ) ) return ce . NaN ;
311-
312- // (-1)^∞ = NaN
313- // Because of oscillations in the limit.
314- if ( x . is ( - 1 ) ) return ce . NaN ;
315-
316- if ( x . isInfinity ) {
317- if ( x . isPositive ) return ce . PositiveInfinity ;
318- if ( x . isNegative ) return ce . NaN ;
319- }
320- }
321-
322- if ( e === Number . NEGATIVE_INFINITY ) {
323- if ( x . is ( - 1 ) ) return ce . NaN ;
324- if ( x . isInfinity ) {
325- if ( x . isPositive ) return ce . Zero ;
326- if ( x . isNegative ) return ce . NegativeInfinity ;
327- }
328- }
329-
330- if ( typeof exp !== 'number' ) {
331- if ( exp . isInfinity && ! exp . isPositive && ! exp . isNegative ) {
332- // b^~∞ = NaN
333- // Because b^z has no limit as z -> ~∞.
334- return ce . NaN ;
335- }
336-
337- if ( x . isInfinity ) {
338- // If the exponent is pure imaginary, the result is NaN
339- if ( exp . type . matches ( 'imaginary' ) ) return ce . NaN ;
340- if ( exp . type . matches ( 'complex' ) && ! isNaN ( exp . re ) ) {
341- if ( exp . re > 0 ) return ce . ComplexInfinity ;
342- if ( exp . re < 0 ) return ce . Zero ;
343- }
344- }
345- }
346-
347- // // if (this.isNegative) {
348- // // if (exp % 2 === 1) return this.neg().pow(exp).neg();
349- // // if (exp % 2 === 0) return this.neg().pow(exp);
350- // // }
351-
352- if ( e === Number . POSITIVE_INFINITY ) {
353- if ( x . isGreater ( 1 ) ) return ce . PositiveInfinity ;
354- if ( x . isPositive && x . isLess ( 1 ) ) return ce . Zero ;
355- }
356- if ( e === Number . NEGATIVE_INFINITY ) {
357- if ( x . isGreater ( 1 ) ) return ce . Zero ;
358- if ( x . isPositive && x . isLess ( 1 ) ) return ce . PositiveInfinity ;
359- }
360-
361- if ( typeof exp !== 'number' && exp . operator === 'Negate' )
362- return pow ( x , exp . op1 , { numericApproximation} ) . inv ( ) ;
300+ const e = typeof exp === 'number' ?exp :exp . im === 0 ?exp . re :undefined ;
363301
364302//@todo : this should be canonicalized to a number, so it should never happen here
365303if ( x . symbol === 'ComplexInfinity' ) return ce . NaN ;