@@ -240,12 +240,38 @@ describe('CANONICAL FORMS', () => {
240240
241241describe ( 'Power' , ( ) => {
242242const engine = new ComputeEngine ( ) ;
243- engine . declare ( 'x' , { constant :true , value :0 } ) ;
244- engine . declare ( 'y' , { constant :true , value :1 } ) ;
245243
246- engine . declare ( 'p' , { constant :true , value :Infinity } ) ;
247- engine . declare ( 'n' , { constant :true , value :- Infinity } ) ;
248- engine . declare ( 'c' , { constant :true , value :- Infinity } ) ;
244+ /*
245+ * 'Power'-scoped symbol declarations
246+ */
247+ //@note : declarations are necessarily marked with 'holdUntil: never' - for these test cases - in
248+ //order to permit substitution during (before) application of canonical-forms.
249+ //^Necessary because 'a^x' is not a valid simplification (cannot assume that 'x' is bound), but
250+ //if x is substituted, then that's fine...
251+ engine . declare ( 'x' , { constant :true , value :0 , holdUntil :'never' } ) ;
252+ engine . declare ( 'y' , { constant :true , value :1 , holdUntil :'never' } ) ;
253+
254+ engine . declare ( 'p' , {
255+ constant :true ,
256+ value :Infinity ,
257+ holdUntil :'never' ,
258+ } ) ;
259+ engine . declare ( 'n' , {
260+ constant :true ,
261+ value :- Infinity ,
262+ holdUntil :'never' ,
263+ } ) ;
264+ engine . declare ( 'c' , {
265+ constant :true ,
266+ value :- Infinity ,
267+ holdUntil :'never' ,
268+ } ) ;
269+
270+ //Control-case symbols (holdUntil value of default 'evaluate', and henceforth 'value' not to be
271+ //consulted at this stage)
272+ engine . declare ( 'j' , { constant :true , value :0 } ) ;
273+ engine . declare ( 'k' , { constant :true , value :1 } ) ;
274+
249275const checkPower = (
250276input :string ,
251277priorForms :CanonicalForm [ ] = [ 'Number' ]
@@ -267,12 +293,21 @@ describe('CANONICAL FORMS', () => {
267293 canonForms = ComplexInfinity
268294 canonical = ComplexInfinity
269295 ` ) ;
270- // x === 0
296+ // x === 0 (simplifies because is substituted beforehand ('holdUntil = never'))
271297expect ( checkPower ( 'x^3' ) ) . toMatchInlineSnapshot ( `
272298 box = ["Power", "x", 3]
273299 canonForms = 0
274300 canonical = 0
275301 ` ) ;
302+
303+ // Control
304+ //
305+ //'j=0', but should _not_ substitute (i.e. beforehand) & therefore no op. should take place
306+ //(its 'holdUntil' property is set to the default 'evaluate')
307+ expect ( checkPower ( 'j^3' ) ) . toMatchInlineSnapshot ( `
308+ box = ["Power", "j", 3]
309+ canonForms = ["Power", "j", 3]
310+ ` ) ;
276311} ) ;
277312
278313test ( 'x^0' , ( ) => {
@@ -286,11 +321,18 @@ describe('CANONICAL FORMS', () => {
286321 canonForms = NaN
287322 canonical = NaN
288323 ` ) ;
324+ //↓Only simplifies because 'x' substitutes with its value during partial-canonicalization;
325+ //prior to app. of CanonicalForms.
289326expect ( checkPower ( '16^x' ) ) . toMatchInlineSnapshot ( `
290327 box = ["Power", 16, "x"]
291328 canonForms = 1
292329 canonical = 1
293330 ` ) ;
331+ //!@note: this should be ok to apply, despite 'Pi' being a constant which does *not* have a
332+ //!'holdUntil' value set to 'never', because... this is a library/engine-level defined
333+ //!constant in which its type/value is always known: even in non-canonical expressions.
334+ //I.e., if this were a user-defined constant, this would not be OK, since its value would have
335+ //to be referenced.
294336expect ( checkPower ( '\\pi^0' ) ) . toMatchInlineSnapshot ( `
295337 box = ["Power", "Pi", 0]
296338 canonForms = 1
@@ -301,6 +343,15 @@ describe('CANONICAL FORMS', () => {
301343 canonForms = 1
302344 canonical = 1
303345 ` ) ;
346+
347+ /*
348+ * Control-case
349+ */
350+ //j===0, but is *held*
351+ expect ( checkPower ( '2^j' ) ) . toMatchInlineSnapshot ( `
352+ box = ["Power", 2, "j"]
353+ canonForms = ["Power", 2, "j"]
354+ ` ) ;
304355} ) ;
305356
306357test ( '1^x' , ( ) => {
@@ -342,15 +393,41 @@ describe('CANONICAL FORMS', () => {
342393 canonForms = PositiveInfinity
343394 canonical = PositiveInfinity
344395 ` ) ;
345- expect ( checkPower ( '{-a/4}^1' ) ) . toMatchInlineSnapshot ( `
346- box = ["Power", ["Divide", ["Negate", "a"], 4], 1]
347- canonForms = ["Divide", ["Negate", "a"], 4]
348- canonical = ["Multiply", ["Rational", -1, 4], "a"]
349- ` ) ;
350- expect ( checkPower ( '{-\\pi}^1' ) ) . toMatchInlineSnapshot ( `
351- box = ["Power", ["Negate", "Pi"], 1]
352- canonForms = ["Negate", "Pi"]
353- canonical = ["Negate", "Pi"]
396+
397+ //@note : This correctly simplifies, unlike would be expected of 'x^1' (where x is a
398+ //user-declared numeric constant with a default 'holdUntil' value of 'evaluate'), because 'Pi'
399+ //is a library-defined constant, consequently always being canonical & bound (and ∴ having a
400+ //type/value), even in *non-canonical* exprs.
401+ //^!Note that currently, 'x^1' *will simplify* at present, but this would not be considered
402+ //correct behaviour (compute-engine/pull/238#discussion_r2034335185). This is because
403+ //canonicalization of symbols (including partial-canonicalization) is presently, erroneously,
404+ //synonymous with 'binding': permitting its type/value to be determined at this stage.
405+ expect ( checkPower ( '{\\pi}^1' ) ) . toMatchInlineSnapshot ( `
406+ box = ["Power", "Pi", 1]
407+ canonForms = Pi
408+ canonical = Pi
409+ ` ) ;
410+
411+ //↓🙁: cannot be simplified, at least via the route of checking the 'type' of the base/'-\pi',
412+ //because is a non-canonical function-expr. (non-bound to 'Power' definition)
413+
414+ // expect(checkPower('{-\\pi}^1')).toMatchInlineSnapshot(`
415+ // box = ["Power", "Pi", 1]
416+ // canonForms = Pi
417+ // canonical = Pi
418+ // `);
419+
420+ /*
421+ * Control
422+ */
423+ //!@note:
424+ //Not currently simplified, despite 'j' being an 'integer'-typed constant, because the base is
425+ //a non-canonical function and therefore unbound to a definition; consequently with its 'type'
426+ //non-determinable.
427+ expect ( checkPower ( '{-j/4}^1' ) ) . toMatchInlineSnapshot ( `
428+ box = ["Power", ["Divide", ["Negate", "j"], 4], 1]
429+ canonForms = ["Power", ["Divide", ["Negate", "j"], 4], 1]
430+ canonical = ["Multiply", ["Rational", -1, 4], "j"]
354431 ` ) ;
355432} ) ;
356433
@@ -377,10 +454,10 @@ describe('CANONICAL FORMS', () => {
377454 canonForms = ["Rational", 1, 3]
378455 canonical = ["Rational", 1, 3]
379456 ` ) ;
380- expect ( checkPower ( '{x * 4}^{-1}' ) ) . toMatchInlineSnapshot ( `
381- box = ["Power", ["Multiply", "x ", 4], -1]
382- canonForms = ["Divide", 1, ["Multiply", 4, "x "]]
383- canonical = ["Divide", 1, ["Multiply", 4, "x "]]
457+ expect ( checkPower ( '{j * 4}^{-1}' ) ) . toMatchInlineSnapshot ( `
458+ box = ["Power", ["Multiply", "j ", 4], -1]
459+ canonForms = ["Divide", 1, ["Multiply", 4, "j "]]
460+ canonical = ["Divide", 1, ["Multiply", 4, "j "]]
384461 ` ) ;
385462} ) ;
386463
@@ -426,7 +503,10 @@ describe('CANONICAL FORMS', () => {
426503 canonical = ComplexInfinity
427504 ` ) ;
428505
429- //Constant-symbol value cases.: p=PositiveInfinity
506+ /*
507+ * Constant-symbol value cases.
508+ */
509+ // p === +Infinity (& 'holdUntil: never')
430510expect ( checkPower ( '1^{p}' ) ) . toMatchInlineSnapshot ( `
431511 box = ["Power", 1, "p"]
432512 canonForms = NaN
@@ -470,13 +550,17 @@ describe('CANONICAL FORMS', () => {
470550 canonForms = 0
471551 canonical = 0
472552 ` ) ;
473- expect ( checkPower ( '{-\\pi}^{-\\infty}' ) ) . toMatchInlineSnapshot ( `
474- box = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
475- canonForms = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
476- canonical = 0
477- ` ) ;
478-
479- //Constant-symbol value cases.: n=NegativeInfinity, x=0
553+ //🙁: cannot be simplified, base/'Negate' is non-canonical and 'type' cannot be determined.
554+ // expect(checkPower('{-\\pi}^{-\\infty}')).toMatchInlineSnapshot(`
555+ // box = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
556+ // canonForms = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
557+ // canonical = 0
558+ // `);
559+
560+ /*
561+ * Constant-valued symbol operands
562+ */
563+ //x=0, n=NegativeInfinity ('holdUntil: never'),
480564expect ( checkPower ( 'x^{n}' ) ) . toMatchInlineSnapshot ( `
481565 box = ["Power", "x", "n"]
482566 canonForms = ComplexInfinity
@@ -541,6 +625,7 @@ describe('CANONICAL FORMS', () => {
541625 ` ) ;
542626
543627//Include 'Add' in order that complex-numbers may be identified for these cases.
628+ //(^note that this may later be included as part of the 'Number' form)
544629check = ( input :string ) =>
545630checkPower ( input , [ 'InvisibleOperator' , 'Number' , 'Add' ] ) ;
546631