@cortex-js/compute-engine 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/compute-engine.esm.js +746 -671
  2. package/dist/compute-engine.min.esm.js +2 -2
  3. package/dist/compute-engine.min.js +2 -2
  4. package/dist/math-json.esm.js +2 -2
  5. package/dist/math-json.min.esm.js +2 -2
  6. package/dist/math-json.min.js +2 -2
  7. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  8. package/dist/types/common/signals.d.ts +1 -1
  9. package/dist/types/compute-engine/assume.d.ts +1 -1
  10. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +65 -62
  11. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  12. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  13. package/dist/types/compute-engine/boxed-expression/boxed-domain.d.ts +1 -1
  14. package/dist/types/compute-engine/boxed-expression/boxed-function-definition.d.ts +1 -1
  15. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +38 -31
  16. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +4 -2
  17. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  18. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  19. package/dist/types/compute-engine/boxed-expression/boxed-symbol-definition.d.ts +6 -6
  20. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +6 -6
  21. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  22. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  23. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  24. package/dist/types/compute-engine/boxed-expression/utils.d.ts +3 -3
  25. package/dist/types/compute-engine/compute-engine.d.ts +2 -3
  26. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  27. package/dist/types/compute-engine/domain-utils.d.ts +1 -1
  28. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  29. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  30. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  31. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  32. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-inequalities.d.ts +1 -1
  33. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  34. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  35. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  36. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  37. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  38. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  39. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  40. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  41. package/dist/types/compute-engine/latex-syntax/public.d.ts +1 -1
  42. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  43. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  44. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  45. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  46. package/dist/types/compute-engine/library/arithmetic-add.d.ts +1 -1
  47. package/dist/types/compute-engine/library/arithmetic-divide.d.ts +1 -1
  48. package/dist/types/compute-engine/library/arithmetic-multiply.d.ts +1 -1
  49. package/dist/types/compute-engine/library/arithmetic-power.d.ts +1 -1
  50. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  51. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  52. package/dist/types/compute-engine/library/collections.d.ts +1 -1
  53. package/dist/types/compute-engine/library/core.d.ts +1 -1
  54. package/dist/types/compute-engine/library/domains.d.ts +1 -1
  55. package/dist/types/compute-engine/library/library.d.ts +1 -1
  56. package/dist/types/compute-engine/library/logic.d.ts +1 -1
  57. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  58. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  59. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  60. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  61. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  62. package/dist/types/compute-engine/numerics/numeric-decimal.d.ts +1 -1
  63. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  64. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  65. package/dist/types/compute-engine/public.d.ts +514 -351
  66. package/dist/types/compute-engine/rules.d.ts +1 -1
  67. package/dist/types/compute-engine/simplify-rules.d.ts +1 -1
  68. package/dist/types/compute-engine/symbolic/expand.d.ts +1 -1
  69. package/dist/types/compute-engine/symbolic/flatten.d.ts +1 -1
  70. package/dist/types/compute-engine/symbolic/negate.d.ts +1 -1
  71. package/dist/types/compute-engine/symbolic/polynomials.d.ts +1 -1
  72. package/dist/types/compute-engine/symbolic/product.d.ts +1 -1
  73. package/dist/types/compute-engine/symbolic/sum.d.ts +1 -1
  74. package/dist/types/compute-engine/symbolic/utils.d.ts +1 -1
  75. package/dist/types/compute-engine.d.ts +2 -2
  76. package/dist/types/math-json/math-json-format.d.ts +1 -1
  77. package/dist/types/math-json/utils.d.ts +1 -1
  78. package/dist/types/math-json.d.ts +2 -2
  79. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- /** CortexJS Compute Engine 0.7.0 */
1
+ /** CortexJS Compute Engine 0.8.0 */
2
2
  /** @internal */
3
3
  function isSymbolEntry(entry) {
4
4
  return !('kind' in entry) || entry.kind === 'symbol';
@@ -29,7 +29,7 @@ function isEnvironmentEntry(entry) {
29
29
  }
30
30
 
31
31
  /*!
32
- * decimal.js v10.4.0
32
+ * decimal.js v10.4.1
33
33
  * An arbitrary-precision Decimal type for JavaScript.
34
34
  * https://github.com/MikeMcl/decimal.js
35
35
  * Copyright (c) 2022 Michael Mclaughlin <M8ch88l@gmail.com>
@@ -7287,8 +7287,23 @@ function numeratorDenominator(expr) {
7287
7287
  }
7288
7288
  }
7289
7289
  }
7290
+ else if (head(arg) === 'Rational' && nops(arg) === 2) {
7291
+ const op1 = op(arg, 1);
7292
+ const op2 = op(arg, 2);
7293
+ if (machineValue(op1) !== 1)
7294
+ numerator.push(op1);
7295
+ if (machineValue(op2) !== 1)
7296
+ denominator.push(op2);
7297
+ }
7290
7298
  else {
7291
- numerator.push(arg);
7299
+ const [n, d] = rationalValue(arg);
7300
+ if (n !== null) {
7301
+ if (n !== 1)
7302
+ numerator.push(n);
7303
+ denominator.push(d);
7304
+ }
7305
+ else
7306
+ numerator.push(arg);
7292
7307
  }
7293
7308
  }
7294
7309
  return [numerator, denominator];
@@ -7422,15 +7437,12 @@ function serializeMultiply(serializer, expr) {
7422
7437
  const [numer, denom] = numeratorDenominator(expr);
7423
7438
  if (denom.length > 0) {
7424
7439
  if (denom.length === 1 && denom[0] === 1) {
7425
- if (numer.length === 0) {
7440
+ if (numer.length === 0)
7426
7441
  result = '1';
7427
- }
7428
- else if (numer.length === 1) {
7442
+ else if (numer.length === 1)
7429
7443
  result = serializer.serialize(numer[0]);
7430
- }
7431
- else {
7444
+ else
7432
7445
  result = serializeMultiply(serializer, [MULTIPLY, ...numer]);
7433
- }
7434
7446
  }
7435
7447
  else {
7436
7448
  result = serializer.serialize([
@@ -7986,7 +7998,11 @@ const DEFINITIONS_ARITHMETIC = [
7986
7998
  {
7987
7999
  name: 'Rational',
7988
8000
  precedence: 660,
7989
- serialize: serializeFraction,
8001
+ serialize: (serializer, expr) => {
8002
+ if (expr && nops(expr) === 1)
8003
+ return '\\mathrm{Rational}' + serializer.wrapArguments(expr);
8004
+ return serializeFraction(serializer, expr);
8005
+ },
7990
8006
  },
7991
8007
  {
7992
8008
  name: ROOT,
@@ -14914,8 +14930,8 @@ function preferDecimal(ce) {
14914
14930
  return (ce.numericMode === 'decimal' ||
14915
14931
  (ce.numericMode === 'auto' && ce.precision > Math.floor(MACHINE_PRECISION)));
14916
14932
  }
14917
- /** If result of a numeric evaluation is a complex number,
14918
- * return `NaN` if `ce.useComplex` is false
14933
+ /** When result of a numeric evaluation is a complex number,
14934
+ * return `NaN` if not `complexallowed()`
14919
14935
  */
14920
14936
  function complexAllowed(ce) {
14921
14937
  return ce.numericMode === 'auto' || ce.numericMode === 'complex';
@@ -14968,20 +14984,32 @@ class AbstractBoxedExpression {
14968
14984
  if (metadata?.wikidata !== undefined)
14969
14985
  this._wikidata = metadata.wikidata;
14970
14986
  }
14971
- /** Object.toJSON(), called by JSON.Stringify */
14972
- toJSON() {
14973
- return JSON.stringify(this.json);
14987
+ /** `Object.valueOf()`: return a primitive value for the object
14988
+ *
14989
+ */
14990
+ valueOf() {
14991
+ if (this.symbol === 'True')
14992
+ return true;
14993
+ if (this.symbol === 'False')
14994
+ return false;
14995
+ return (this.asFloat ?? this.string ?? this.symbol ?? JSON.stringify(this.json));
14974
14996
  }
14975
14997
  /** Object.toString() */
14976
14998
  toString() {
14977
- return this.latex;
14999
+ return String(this.valueOf());
14978
15000
  }
14979
- /** Object.valueOf(): return a primitive value for the object */
14980
- valueOf() {
14981
- const [n, d] = this.rationalValue;
14982
- if (n !== null && d !== null)
14983
- return [n, d];
14984
- return this.asFloat ?? this.string ?? this.symbol ?? this.toString();
15001
+ [Symbol.toPrimitive](hint) {
15002
+ if (hint === 'string')
15003
+ return this.toString();
15004
+ if (hint === 'number') {
15005
+ const v = this.valueOf();
15006
+ return typeof v === 'number' ? v : null;
15007
+ }
15008
+ return this.toString();
15009
+ }
15010
+ /** Called by `JSON.stringify()` when serializing to json */
15011
+ toJSON() {
15012
+ return this.json;
14985
15013
  }
14986
15014
  /** Object.is() */
14987
15015
  is(rhs) {
@@ -14989,49 +15017,20 @@ class AbstractBoxedExpression {
14989
15017
  return false;
14990
15018
  return this.isSame(this.engine.box(rhs));
14991
15019
  }
14992
- isCompatible(_dom, _kind) {
14993
- return false;
14994
- }
14995
- has(_v) {
14996
- return false;
14997
- }
14998
- get description() {
14999
- return [];
15000
- }
15001
- get url() {
15002
- return '';
15003
- }
15004
- get isPure() {
15005
- return false;
15006
- }
15007
- /** For a symbol, true if the symbol is a free variable (no value) */
15008
- get isFree() {
15009
- return false;
15010
- }
15011
- /** For a symbol, true if the symbol is a constant (unchangeable value) */
15012
- get isConstant() {
15013
- return false;
15014
- }
15015
- get isLiteral() {
15016
- return false;
15017
- }
15018
15020
  get latex() {
15019
15021
  return this._latex ?? this.engine.serialize(this);
15020
15022
  }
15021
15023
  set latex(val) {
15022
15024
  this._latex = val;
15023
15025
  }
15024
- get wikidata() {
15025
- return this._wikidata ?? '';
15026
- }
15027
- set wikidata(val) {
15028
- this._wikidata = val;
15026
+ get symbol() {
15027
+ return null;
15029
15028
  }
15030
- get complexity() {
15031
- return 1;
15029
+ get isNothing() {
15030
+ return false;
15032
15031
  }
15033
- get symbols() {
15034
- return [...getSymbols(this, new Set())].map((x) => this.engine.symbol(x));
15032
+ get string() {
15033
+ return null;
15035
15034
  }
15036
15035
  getSubexpressions(head) {
15037
15036
  return getSubexpressions(this, head);
@@ -15039,6 +15038,9 @@ class AbstractBoxedExpression {
15039
15038
  get subexpressions() {
15040
15039
  return this.getSubexpressions('');
15041
15040
  }
15041
+ get symbols() {
15042
+ return [...getSymbols(this, new Set())].map((x) => this.engine.symbol(x));
15043
+ }
15042
15044
  get errors() {
15043
15045
  return this.getSubexpressions('Error');
15044
15046
  }
@@ -15058,88 +15060,94 @@ class AbstractBoxedExpression {
15058
15060
  get op3() {
15059
15061
  return this.engine.symbol('Nothing');
15060
15062
  }
15061
- get basedDefinition() {
15062
- return undefined;
15063
+ get isValid() {
15064
+ return true;
15063
15065
  }
15064
- get symbolDefinition() {
15065
- return undefined;
15066
+ get isPure() {
15067
+ return false;
15066
15068
  }
15067
- get functionDefinition() {
15068
- return undefined;
15069
+ /** For a symbol, true if the symbol is a free variable (no value) */
15070
+ get isFree() {
15071
+ return false;
15069
15072
  }
15070
- bind(_scope) {
15071
- return;
15073
+ /** For a symbol, true if the symbol is a constant (unchangeable value) */
15074
+ get isConstant() {
15075
+ return false;
15072
15076
  }
15073
- unbind() {
15074
- return;
15077
+ get canonical() {
15078
+ return this;
15075
15079
  }
15076
- get keys() {
15077
- return null;
15080
+ apply(_fn, _head) {
15081
+ return this;
15078
15082
  }
15079
- get keysCount() {
15080
- return 0;
15083
+ subs(_sub) {
15084
+ return this;
15081
15085
  }
15082
- getKey(_key) {
15083
- return undefined;
15086
+ solve(_vars) {
15087
+ return null;
15084
15088
  }
15085
- hasKey(_key) {
15089
+ replace(_rules) {
15090
+ return null;
15091
+ }
15092
+ has(_v) {
15086
15093
  return false;
15087
15094
  }
15088
- get machineValue() {
15089
- return this.numericValue?.machineValue ?? null;
15095
+ get isNaN() {
15096
+ return undefined;
15090
15097
  }
15091
- get rationalValue() {
15092
- return this.numericValue?.rationalValue ?? [null, null];
15098
+ get isZero() {
15099
+ return undefined;
15093
15100
  }
15094
- get decimalValue() {
15095
- return this.numericValue?.decimalValue ?? null;
15101
+ get isNotZero() {
15102
+ return undefined;
15096
15103
  }
15097
- get complexValue() {
15098
- return this.numericValue?.complexValue ?? null;
15104
+ get isOne() {
15105
+ return undefined;
15099
15106
  }
15100
- get asFloat() {
15101
- return this.numericValue?.asFloat ?? null;
15107
+ get isNegativeOne() {
15108
+ return undefined;
15102
15109
  }
15103
- get asSmallInteger() {
15104
- return this.numericValue?.asSmallInteger ?? null;
15110
+ get isInfinity() {
15111
+ return undefined;
15105
15112
  }
15106
- get asRational() {
15107
- return this.numericValue?.asRational ?? [null, null];
15113
+ // Not +- Infinity, not NaN
15114
+ get isFinite() {
15115
+ return undefined;
15108
15116
  }
15109
- get sgn() {
15110
- return this.numericValue?.sgn ?? null;
15117
+ get isEven() {
15118
+ return undefined;
15111
15119
  }
15112
- get symbol() {
15113
- return null;
15120
+ get isOdd() {
15121
+ return undefined;
15114
15122
  }
15115
- get isNothing() {
15116
- return false;
15123
+ get isPrime() {
15124
+ return undefined;
15117
15125
  }
15118
- get isValid() {
15119
- return true;
15126
+ get isComposite() {
15127
+ return undefined;
15120
15128
  }
15121
- get value() {
15122
- return this;
15129
+ get machineValue() {
15130
+ return null;
15123
15131
  }
15124
- set value(_value) {
15125
- throw new Error(`Can't change the value of \\(${this.latex}\\)`);
15132
+ get rationalValue() {
15133
+ return [null, null];
15126
15134
  }
15127
- get numericValue() {
15128
- return undefined;
15135
+ get decimalValue() {
15136
+ return null;
15129
15137
  }
15130
- isSubdomainOf(_d) {
15131
- return undefined;
15138
+ get complexValue() {
15139
+ return null;
15132
15140
  }
15133
- get domain() {
15134
- return this.engine.domain('Void');
15141
+ get asFloat() {
15142
+ return null;
15135
15143
  }
15136
- set domain(_domain) {
15137
- throw new Error(`Can't change the domain of \\(${this.latex}\\)`);
15144
+ get asSmallInteger() {
15145
+ return null;
15138
15146
  }
15139
- get explicitDomain() {
15140
- return this.domain;
15147
+ get asRational() {
15148
+ return [null, null];
15141
15149
  }
15142
- get string() {
15150
+ get sgn() {
15143
15151
  return null;
15144
15152
  }
15145
15153
  isLess(_rhs) {
@@ -15154,12 +15162,6 @@ class AbstractBoxedExpression {
15154
15162
  isGreaterEqual(_rhs) {
15155
15163
  return undefined;
15156
15164
  }
15157
- get isZero() {
15158
- return undefined;
15159
- }
15160
- get isNotZero() {
15161
- return undefined;
15162
- }
15163
15165
  // x > 0
15164
15166
  get isPositive() {
15165
15167
  return undefined;
@@ -15176,16 +15178,80 @@ class AbstractBoxedExpression {
15176
15178
  get isNonPositive() {
15177
15179
  return undefined;
15178
15180
  }
15179
- get isInfinity() {
15181
+ //
15182
+ //
15183
+ //
15184
+ //
15185
+ //
15186
+ isCompatible(_dom, _kind) {
15187
+ return false;
15188
+ }
15189
+ get description() {
15180
15190
  return undefined;
15181
15191
  }
15182
- get isNaN() {
15192
+ get url() {
15183
15193
  return undefined;
15184
15194
  }
15185
- // Not +- Infinity, not NaN
15186
- get isFinite() {
15195
+ get isLiteral() {
15196
+ return false;
15197
+ }
15198
+ get wikidata() {
15199
+ return this._wikidata;
15200
+ }
15201
+ set wikidata(val) {
15202
+ this._wikidata = val;
15203
+ }
15204
+ get complexity() {
15205
+ return undefined;
15206
+ }
15207
+ get basedDefinition() {
15208
+ return undefined;
15209
+ }
15210
+ get symbolDefinition() {
15211
+ return undefined;
15212
+ }
15213
+ get functionDefinition() {
15214
+ return undefined;
15215
+ }
15216
+ bind(_scope) {
15217
+ return;
15218
+ }
15219
+ unbind() {
15220
+ return;
15221
+ }
15222
+ get keys() {
15223
+ return null;
15224
+ }
15225
+ get keysCount() {
15226
+ return 0;
15227
+ }
15228
+ getKey(_key) {
15229
+ return undefined;
15230
+ }
15231
+ hasKey(_key) {
15232
+ return false;
15233
+ }
15234
+ get value() {
15235
+ return undefined;
15236
+ }
15237
+ set value(_value) {
15238
+ throw new Error(`Can't change the value of \\(${this.latex}\\)`);
15239
+ }
15240
+ get numericValue() {
15241
+ return undefined;
15242
+ }
15243
+ isSubdomainOf(_d) {
15187
15244
  return undefined;
15188
15245
  }
15246
+ get domain() {
15247
+ return this.engine.domain('Void');
15248
+ }
15249
+ set domain(_domain) {
15250
+ throw new Error(`Can't change the domain of \\(${this.latex}\\)`);
15251
+ }
15252
+ get explicitDomain() {
15253
+ return this.domain;
15254
+ }
15189
15255
  get isNumber() {
15190
15256
  return undefined;
15191
15257
  }
@@ -15214,47 +15280,14 @@ class AbstractBoxedExpression {
15214
15280
  get isExtendedComplex() {
15215
15281
  return undefined;
15216
15282
  }
15217
- get isOne() {
15218
- return undefined;
15219
- }
15220
- get isNegativeOne() {
15221
- return undefined;
15222
- }
15223
- get isEven() {
15224
- return undefined;
15225
- }
15226
- get isOdd() {
15227
- return undefined;
15228
- }
15229
- get isPrime() {
15230
- return undefined;
15231
- }
15232
- get isComposite() {
15233
- return undefined;
15234
- }
15235
- get canonical() {
15236
- return this;
15237
- }
15238
- apply(_fn, _head) {
15283
+ simplify(_options) {
15239
15284
  return this;
15240
15285
  }
15241
15286
  evaluate(options) {
15242
15287
  return this.simplify(options);
15243
15288
  }
15244
- simplify(_options) {
15245
- return this;
15246
- }
15247
15289
  N(_options) {
15248
- return this;
15249
- }
15250
- replace(_rules) {
15251
- return null;
15252
- }
15253
- subs(_sub) {
15254
- return this;
15255
- }
15256
- solve(_vars) {
15257
- return null;
15290
+ return this.evaluate();
15258
15291
  }
15259
15292
  }
15260
15293
 
@@ -15642,12 +15675,12 @@ function order(a, b) {
15642
15675
  let bComplexity = 0;
15643
15676
  let aComplexity = 0;
15644
15677
  for (const key of b.keys)
15645
- bComplexity += b.getKey(key).complexity;
15678
+ bComplexity += b.getKey(key).complexity ?? DEFAULT_COMPLEXITY;
15646
15679
  for (const key of a.keys)
15647
- aComplexity += a.getKey(key).complexity;
15680
+ aComplexity += a.getKey(key).complexity ?? DEFAULT_COMPLEXITY;
15648
15681
  return aComplexity - bComplexity;
15649
15682
  }
15650
- return a.complexity - b.complexity;
15683
+ return ((a.complexity ?? DEFAULT_COMPLEXITY) - (b.complexity ?? DEFAULT_COMPLEXITY));
15651
15684
  }
15652
15685
  /** Get the number of atomic elements in the expression */
15653
15686
  function getLeafCount(expr) {
@@ -16304,7 +16337,7 @@ function serializeJsonNumber(ce, value, metadata) {
16304
16337
  num =
16305
16338
  value.isInteger() && value.e < value.precision() + 4
16306
16339
  ? value.toFixed(0)
16307
- : repeatingDecimal(ce, value.toJSON());
16340
+ : repeatingDecimal(ce, value.toString());
16308
16341
  }
16309
16342
  if (ce.jsonSerializationOptions.metadata.includes('latex'))
16310
16343
  metadata.latex = metadata.latex ?? ce.serialize({ num });
@@ -16903,20 +16936,26 @@ function includesDomain(xs, y) {
16903
16936
  return false;
16904
16937
  }
16905
16938
  function serialize(ce, dom) {
16939
+ if (dom instanceof AbstractBoxedExpression)
16940
+ return dom.json;
16906
16941
  if (typeof dom === 'string')
16907
- return serializeJsonSymbol(ce, dom);
16942
+ return dom;
16908
16943
  if (dom[0] === 'Error') {
16909
16944
  if (dom[2])
16910
- return ['Error', dom[1], dom[2]];
16911
- return ['Error', dom[1]];
16945
+ return [
16946
+ 'Error',
16947
+ serialize(ce, dom[1]),
16948
+ serialize(ce, dom[2]),
16949
+ ];
16950
+ return [
16951
+ 'Error',
16952
+ serialize(ce, dom[1]),
16953
+ ];
16912
16954
  }
16913
16955
  const result = [serializeJsonSymbol(ce, dom[0])];
16914
- for (let i = 1; i <= dom.length - 1; i++) {
16915
- if (dom[i] instanceof AbstractBoxedExpression)
16916
- result.push(dom[i].json);
16917
- else
16918
- result.push(serialize(ce, dom[i]));
16919
- }
16956
+ if (dom.length > 1)
16957
+ for (let i = 1; i <= dom.length - 1; i++)
16958
+ serialize(ce, dom[i]);
16920
16959
  return result;
16921
16960
  }
16922
16961
  function hash(dom) {
@@ -17739,17 +17778,40 @@ function cheapest(oldExpr, newExpr) {
17739
17778
  * BoxedFunction
17740
17779
  */
17741
17780
  class BoxedFunction extends AbstractBoxedExpression {
17742
- constructor(ce, head, ops, metadata) {
17743
- super(ce, metadata);
17781
+ constructor(ce, head, ops, options) {
17782
+ options ?? (options = {});
17783
+ options.canonical ?? (options.canonical = false);
17784
+ super(ce, options.metadata);
17744
17785
  this._scope = ce.context;
17745
- this._head = typeof head === 'string' ? head : head.symbol ?? head;
17786
+ this._head = head;
17746
17787
  this._ops = ops;
17747
- this._def = null; // Mark the def as not cached
17788
+ if (options.canonical)
17789
+ this._canonical = this;
17790
+ this._def = options.def ?? null; // Mark the def as not yet cached if none is provided
17748
17791
  this._codomain = null;
17749
- this._isCanonical = false;
17792
+ if (typeof this._head !== 'string')
17793
+ this._codomain = this._head.domain.codomain;
17794
+ else if (this._def) {
17795
+ const sig = this._def.signature;
17796
+ if (typeof sig.codomain === 'function') {
17797
+ this._codomain =
17798
+ sig.codomain(ce, this._ops.map((x) => x.domain)) ?? null;
17799
+ }
17800
+ else {
17801
+ this._codomain = sig.codomain ?? null;
17802
+ }
17803
+ }
17804
+ if (!this._codomain)
17805
+ this._codomain = ce.defaultDomain ?? ce.domain('Void');
17750
17806
  // Note: _isPure is computed on demand and cached
17751
17807
  ce._register(this);
17752
17808
  }
17809
+ //
17810
+ // NON-CANONICAL OR CANONICAL OPERATIONS
17811
+ //
17812
+ // Those operations/properties can be applied to a canonical or
17813
+ // non-canonical expression
17814
+ //
17753
17815
  get hash() {
17754
17816
  if (this._hash !== undefined)
17755
17817
  return this._hash;
@@ -17763,88 +17825,41 @@ class BoxedFunction extends AbstractBoxedExpression {
17763
17825
  this._hash = h;
17764
17826
  return h;
17765
17827
  }
17766
- unbind() {
17767
- if (typeof this._head !== 'string')
17768
- this._head.unbind();
17769
- for (const arg of this._ops)
17770
- arg.unbind();
17771
- if (this._value)
17772
- this._value.unbind();
17773
- if (this._numericValue)
17774
- this._numericValue.unbind();
17775
- }
17776
- get wikidata() {
17777
- return this._wikidata ?? this.functionDefinition?.wikidata ?? '';
17778
- }
17779
- get description() {
17780
- const def = this.functionDefinition;
17781
- if (!def)
17782
- return [];
17783
- if (!def.description)
17784
- return [];
17785
- if (typeof def.description === 'string')
17786
- return [def.description];
17787
- return def.description;
17788
- }
17789
- get url() {
17790
- return this.functionDefinition?.url ?? '';
17791
- }
17792
- get complexity() {
17793
- return this.functionDefinition?.complexity ?? DEFAULT_COMPLEXITY;
17794
- }
17795
- get head() {
17796
- return this._head;
17828
+ get isCanonical() {
17829
+ return this._canonical === this;
17797
17830
  }
17798
- get value() {
17799
- if (!this.isPure)
17800
- return undefined;
17801
- // Use cached value if the function is pure
17802
- if (this._value)
17803
- return this._value;
17804
- this._value = this.evaluate();
17805
- return this._value;
17831
+ set isCanonical(val) {
17832
+ this._canonical = val ? this : undefined;
17806
17833
  }
17807
- get numericValue() {
17808
- if (!this.isPure)
17809
- return undefined;
17810
- if (this._numericValue)
17811
- return this._numericValue;
17812
- const val = this.N();
17813
- this._numericValue = val.isLiteral ? val : undefined;
17814
- return this._numericValue;
17834
+ get isLiteral() {
17835
+ return false;
17815
17836
  }
17816
17837
  get isPure() {
17838
+ if (!this.isCanonical)
17839
+ return false;
17817
17840
  if (this._isPure !== undefined)
17818
17841
  return this._isPure;
17819
17842
  let result = undefined;
17820
17843
  if (this.functionDefinition?.pure !== undefined)
17821
17844
  result = this.functionDefinition.pure;
17822
- if (result !== false) {
17823
- // The function might be pure. Let's check that all its arguments are pure.
17845
+ // The function might be pure. Let's check that all its arguments are pure.
17846
+ if (result !== false)
17824
17847
  result = this._ops.every((x) => x.isPure);
17825
- }
17826
17848
  this._isPure = result;
17827
17849
  return result;
17828
17850
  }
17829
- get isLiteral() {
17830
- return false;
17851
+ get json() {
17852
+ // If this expression is canonical, apply some transformations to the
17853
+ // JSON serialization to "reverse" some of the effects of canonicalization.
17854
+ if (this._canonical === this)
17855
+ return serializeJsonCanonicalFunction(this.engine, this._head, this._ops, { latex: this._latex, wikidata: this._wikidata });
17856
+ return serializeJsonFunction(this.engine, this._head, this._ops, {
17857
+ latex: this._latex,
17858
+ wikidata: this._wikidata,
17859
+ });
17831
17860
  }
17832
- get isValid() {
17833
- if (typeof this._head !== 'string') {
17834
- if (this._head.isValid === false)
17835
- return false;
17836
- }
17837
- else {
17838
- if (this._head === 'Error')
17839
- return false;
17840
- // Need to check function definition before arguments: binding
17841
- // as the side effect of normalizing arguments
17842
- if (this.functionDefinition === undefined)
17843
- return false;
17844
- if (!this._ops.every((x) => x.isValid))
17845
- return false;
17846
- }
17847
- return this._ops.every((x) => x.isValid);
17861
+ get head() {
17862
+ return this._head;
17848
17863
  }
17849
17864
  get ops() {
17850
17865
  return this._ops;
@@ -17861,70 +17876,251 @@ class BoxedFunction extends AbstractBoxedExpression {
17861
17876
  get op3() {
17862
17877
  return this._ops[2] ?? this.engine.symbol('Nothing');
17863
17878
  }
17864
- get functionDefinition() {
17865
- if (this._def !== null)
17866
- return this._def;
17867
- this.bind(this._scope);
17868
- return this._def;
17869
- }
17870
- bind(scope) {
17871
- this._def = undefined;
17872
- const ce = this.engine;
17873
- // Flatten the sequence, canonicalize
17874
- this._ops = flattenSequence(this._ops.map((x) => x.canonical));
17875
- // Is the head an expression?
17876
- // For example, `['InverseFunction', 'Sin']`
17879
+ get isValid() {
17877
17880
  if (typeof this._head !== 'string') {
17878
- const head = this._head.evaluate().symbol ?? this._head;
17879
- this._head = head;
17880
- if (typeof this._head !== 'string') {
17881
- this._codomain = this._head.domain.codomain ?? ce.domain('Void');
17882
- return;
17883
- }
17881
+ if (this._head.isValid === false)
17882
+ return false;
17883
+ return this._ops.every((x) => x.isValid);
17884
17884
  }
17885
- scope = scope ?? this._scope;
17886
- if (scope === null)
17887
- return;
17888
- this._def = ce.lookupFunction(this._head, scope);
17889
- if (this._def) {
17890
- if (this._def.scoped)
17891
- ce.pushScope();
17892
- // In case the def was found by the wikidata, and the name does not
17893
- // match the one in our dictionary, make sure to update it.
17894
- this._head = this._def.name;
17895
- // Apply Sequence, Symbol
17896
- this._ops = normalizeList(this._ops, this._def.hold);
17897
- this._ops =
17898
- validateSignature(ce, this._def.signature, this._ops) ?? this._ops;
17899
- // Calculate the effective codomain
17900
- if (!this._ops.every((x) => x.isValid)) {
17901
- this._codomain = ce.defaultDomain ?? ce.domain('Void');
17902
- if (this._def.scoped)
17903
- ce.popScope();
17904
- return;
17905
- }
17906
- const sig = this._def.signature;
17907
- if (typeof sig.codomain === 'function') {
17908
- // If the signature was a match, the `sig.domain()` function must succeed
17909
- const opsDomain = this._ops.map((x) => x.domain);
17910
- this._codomain = sig.codomain(ce, opsDomain);
17885
+ if (this._head === 'Error')
17886
+ return false;
17887
+ // If this expression is not canonical, nothing else to check:
17888
+ // non-canonical expression are never bound
17889
+ if (this._canonical !== this)
17890
+ return true;
17891
+ // Need to check function definition before arguments: binding
17892
+ // as the side effect of normalizing arguments
17893
+ if (this.functionDefinition === undefined)
17894
+ return false;
17895
+ if (!this._ops.every((x) => x.isValid))
17896
+ return false;
17897
+ return true;
17898
+ }
17899
+ get canonical() {
17900
+ if (this._canonical)
17901
+ return this._canonical;
17902
+ if (this.isValid)
17903
+ this._canonical = makeCanonicalFunction(this.engine, this._head, this._ops);
17904
+ else
17905
+ this._canonical = this;
17906
+ return this._canonical;
17907
+ }
17908
+ *map(fn) {
17909
+ let i = 0;
17910
+ while (i < this._ops.length)
17911
+ yield fn(this._ops[i++]);
17912
+ }
17913
+ apply(fn, head) {
17914
+ const newHead = head ?? this.head;
17915
+ let opsChanged = false;
17916
+ const ops = [];
17917
+ for (const arg of this._ops) {
17918
+ const newArg = fn(arg);
17919
+ if (arg !== newArg)
17920
+ opsChanged = true;
17921
+ ops.push(this.engine.box(newArg));
17922
+ }
17923
+ if (!opsChanged && this.head === newHead)
17924
+ return this;
17925
+ return this.engine.fn(newHead, ops);
17926
+ }
17927
+ subs(sub) {
17928
+ return makeCanonicalFunction(this.engine, this._head, this._ops.map((x) => x.subs(sub)));
17929
+ }
17930
+ replace(rules, options) {
17931
+ return replace(this, rules, options);
17932
+ }
17933
+ has(x) {
17934
+ if (typeof this._head === 'string') {
17935
+ if (typeof x === 'string') {
17936
+ if (this._head === x)
17937
+ return true;
17911
17938
  }
17939
+ else if (x.includes(this._head))
17940
+ return true;
17941
+ }
17942
+ for (const arg of this._ops)
17943
+ if (arg.has(x))
17944
+ return true;
17945
+ return false;
17946
+ }
17947
+ /** `isSame` is structural/symbolic equality */
17948
+ isSame(rhs) {
17949
+ if (this === rhs)
17950
+ return true;
17951
+ if (!(rhs instanceof BoxedFunction))
17952
+ return false;
17953
+ // Number of arguments must match
17954
+ if (this.nops !== rhs.nops)
17955
+ return false;
17956
+ // Head must match
17957
+ if (typeof this.head === 'string') {
17958
+ if (this.head !== rhs.head)
17959
+ return false;
17960
+ }
17961
+ else {
17962
+ if (typeof rhs.head === 'string')
17963
+ return false;
17964
+ else if (!rhs.head || !this.head.isSame(rhs.head))
17965
+ return false;
17966
+ }
17967
+ // Each argument must match
17968
+ const lhsTail = this._ops;
17969
+ const rhsTail = rhs._ops;
17970
+ for (let i = 0; i < lhsTail.length; i++)
17971
+ if (!lhsTail[i].isSame(rhsTail[i]))
17972
+ return false;
17973
+ return true;
17974
+ }
17975
+ match(rhs, options) {
17976
+ if (!(rhs instanceof BoxedFunction))
17977
+ return null;
17978
+ let result = {};
17979
+ // Head must match
17980
+ if (typeof this.head === 'string') {
17981
+ if (this.head !== rhs.head)
17982
+ return null;
17983
+ }
17984
+ else {
17985
+ if (typeof rhs.head === 'string')
17986
+ return null;
17912
17987
  else {
17913
- this._codomain = sig.codomain ?? ce.defaultDomain ?? ce.domain('Void');
17988
+ if (!rhs.head)
17989
+ return null;
17990
+ const m = this.head.match(rhs.head, options);
17991
+ if (m === null)
17992
+ return null;
17993
+ result = { ...result, ...m };
17914
17994
  }
17915
- if (this._def.scoped)
17916
- ce.popScope();
17917
17995
  }
17918
- else
17919
- this._codomain = ce.defaultDomain ?? ce.domain('Void');
17996
+ // Each argument must match
17997
+ const lhsTail = this._ops;
17998
+ const rhsTail = rhs._ops;
17999
+ for (let i = 0; i < lhsTail.length; i++) {
18000
+ const m = lhsTail[i].match(rhsTail[i], options);
18001
+ if (m === null)
18002
+ return null;
18003
+ result = { ...result, ...m };
18004
+ }
18005
+ return result;
18006
+ }
18007
+ //
18008
+ // CANONICAL OPERATIONS
18009
+ //
18010
+ // These operations apply only to canonical expressions
18011
+ //
18012
+ unbind() {
18013
+ // Note: a non-canonical expression is never bound
18014
+ this._value = undefined;
18015
+ this._numericValue = undefined;
18016
+ // this._def = null;
18017
+ }
18018
+ get wikidata() {
18019
+ // Since the canonical and non-canonical version of the expression
18020
+ // may have different heads, not applicable to non-canonical expressions.
18021
+ if (!this.isCanonical)
18022
+ return undefined;
18023
+ return this._wikidata ?? this.functionDefinition?.wikidata ?? undefined;
18024
+ }
18025
+ get description() {
18026
+ // Since the canonical and non-canonical version of the expression
18027
+ // may have different heads, not applicable to non-canonical expressions.
18028
+ if (!this.isCanonical)
18029
+ return undefined;
18030
+ const def = this.functionDefinition;
18031
+ if (!def)
18032
+ return [];
18033
+ if (!def.description)
18034
+ return undefined;
18035
+ if (typeof def.description === 'string')
18036
+ return [def.description];
18037
+ return def.description;
18038
+ }
18039
+ get url() {
18040
+ // Since the canonical and non-canonical version of the expression
18041
+ // may have different heads, not applicable to non-canonical expressions.
18042
+ if (!this.isCanonical)
18043
+ return '';
18044
+ return this.functionDefinition?.url ?? undefined;
18045
+ }
18046
+ get complexity() {
18047
+ // Since the canonical and non-canonical version of the expression
18048
+ // may have different heads, not applicable to non-canonical expressions.
18049
+ if (!this.isCanonical)
18050
+ return undefined;
18051
+ return this.functionDefinition?.complexity ?? DEFAULT_COMPLEXITY;
18052
+ }
18053
+ get functionDefinition() {
18054
+ if (!this.isCanonical)
18055
+ return undefined;
18056
+ if (this._def !== null)
18057
+ return this._def;
18058
+ return undefined;
18059
+ }
18060
+ bind(_scope) {
18061
+ debugger;
18062
+ }
18063
+ //
18064
+ // AUTO-CANONICAL OPERATIONS
18065
+ //
18066
+ // The operations are automatically done on the canonical form of the
18067
+ // expression
18068
+ //
18069
+ get value() {
18070
+ if (!this.isCanonical)
18071
+ return this.canonical.value ?? this.canonical;
18072
+ if (!this.isPure)
18073
+ return undefined;
18074
+ // Use cached value if the function is pure
18075
+ if (this._value)
18076
+ return this._value;
18077
+ this._value = this.evaluate();
18078
+ return this._value;
18079
+ }
18080
+ get numericValue() {
18081
+ if (this._numericValue)
18082
+ return this._numericValue;
18083
+ if (!this.isCanonical)
18084
+ return this.canonical.numericValue;
18085
+ if (!this.isPure)
18086
+ this._numericValue = undefined;
18087
+ else {
18088
+ const v = this.N();
18089
+ this._numericValue = v === this ? undefined : v;
18090
+ }
18091
+ return this._numericValue;
17920
18092
  }
17921
18093
  get domain() {
17922
- if (this._codomain === null)
17923
- this.bind(this._scope);
18094
+ if (!this.isCanonical)
18095
+ return this.canonical.domain;
17924
18096
  console.assert(this._codomain);
17925
18097
  return this._codomain;
17926
18098
  }
18099
+ /** `isEqual` is mathematical equality */
18100
+ isEqual(rhs) {
18101
+ if (!this.isCanonical)
18102
+ return this.canonical.isEqual(rhs);
18103
+ rhs = rhs.canonical.numericValue ?? rhs.canonical;
18104
+ if (rhs.isNumber && this.isNumber) {
18105
+ const ce = this.engine;
18106
+ // In general, it is impossible to always prove equality
18107
+ // (Richardson's theorem) but this works often...
18108
+ const diff = ce.add([this, ce.negate(rhs)]).N();
18109
+ if (diff.isZero)
18110
+ return true;
18111
+ const v = diff.asFloat;
18112
+ if (v !== null && ce.chop(v) === 0)
18113
+ return true;
18114
+ return (this.value ?? this.evaluate()).isSame(rhs);
18115
+ }
18116
+ if (this.domain.isRelationalOperator && rhs.domain.isRelationalOperator) {
18117
+ return this.isSame(rhs);
18118
+ }
18119
+ return this.isSame(rhs);
18120
+ }
17927
18121
  isLess(rhs) {
18122
+ if (!this.isCanonical)
18123
+ return this.canonical.isLess(rhs);
17928
18124
  if (rhs.isZero) {
17929
18125
  const s = this.sgn;
17930
18126
  if (s === null)
@@ -17933,9 +18129,15 @@ class BoxedFunction extends AbstractBoxedExpression {
17933
18129
  return s < 0;
17934
18130
  }
17935
18131
  // @todo: use this.functionDefinition.range
17936
- return undefined;
18132
+ const ce = this.engine;
18133
+ rhs = rhs.canonical.numericValue ?? rhs.canonical;
18134
+ const diff = ce.add([this, ce.negate(rhs)]).N();
18135
+ const v = diff.asFloat;
18136
+ return v === null ? undefined : v < 0;
17937
18137
  }
17938
18138
  isLessEqual(rhs) {
18139
+ if (!this.isCanonical)
18140
+ return this.canonical.isLess(rhs);
17939
18141
  if (rhs.isZero) {
17940
18142
  const s = this.sgn;
17941
18143
  if (s === null)
@@ -17943,10 +18145,16 @@ class BoxedFunction extends AbstractBoxedExpression {
17943
18145
  if (s !== undefined)
17944
18146
  return s <= 0;
17945
18147
  }
17946
- return undefined;
17947
18148
  // @todo: use this.functionDefinition.range
18149
+ const ce = this.engine;
18150
+ rhs = rhs.canonical.numericValue ?? rhs.canonical;
18151
+ const diff = ce.add([this, ce.negate(rhs)]).N();
18152
+ const v = diff.asFloat;
18153
+ return v === null ? undefined : v <= 0 || ce.chop(v) === 0;
17948
18154
  }
17949
18155
  isGreater(rhs) {
18156
+ if (!this.isCanonical)
18157
+ return this.canonical.isLess(rhs);
17950
18158
  if (rhs.isZero) {
17951
18159
  const s = this.sgn;
17952
18160
  if (s === null)
@@ -17954,10 +18162,16 @@ class BoxedFunction extends AbstractBoxedExpression {
17954
18162
  if (s !== undefined)
17955
18163
  return s > 0;
17956
18164
  }
17957
- return undefined;
17958
18165
  // @todo: use this.functionDefinition.range
18166
+ const ce = this.engine;
18167
+ rhs = rhs.canonical.numericValue ?? rhs.canonical;
18168
+ const diff = ce.add([this, ce.negate(rhs)]).N();
18169
+ const v = diff.asFloat;
18170
+ return v === null ? undefined : v > 0;
17959
18171
  }
17960
18172
  isGreaterEqual(rhs) {
18173
+ if (!this.isCanonical)
18174
+ return this.canonical.isLess(rhs);
17961
18175
  if (rhs.isZero) {
17962
18176
  const s = this.sgn;
17963
18177
  if (s === null)
@@ -17965,8 +18179,12 @@ class BoxedFunction extends AbstractBoxedExpression {
17965
18179
  if (s !== undefined)
17966
18180
  return s >= 0;
17967
18181
  }
17968
- return undefined;
17969
18182
  // @todo: use this.functionDefinition.range
18183
+ const ce = this.engine;
18184
+ rhs = rhs.canonical.numericValue ?? rhs.canonical;
18185
+ const diff = ce.add([this, ce.negate(rhs)]).N();
18186
+ const v = diff.asFloat;
18187
+ return v === null ? undefined : v >= 0 || ce.chop(v) === 0;
17970
18188
  }
17971
18189
  get isZero() {
17972
18190
  const s = this.sgn;
@@ -17987,12 +18205,10 @@ class BoxedFunction extends AbstractBoxedExpression {
17987
18205
  // @todo: use this.functionDefinition.range
17988
18206
  }
17989
18207
  get isOne() {
17990
- return undefined;
17991
- // @todo: use this.functionDefinition.range
18208
+ return this.isEqual(this.engine.number(1));
17992
18209
  }
17993
18210
  get isNegativeOne() {
17994
- return undefined;
17995
- // @todo: use this.functionDefinition.range
18211
+ return this.isEqual(this.engine.number(-1));
17996
18212
  }
17997
18213
  // x > 0
17998
18214
  get isPositive() {
@@ -18000,9 +18216,8 @@ class BoxedFunction extends AbstractBoxedExpression {
18000
18216
  if (s === null)
18001
18217
  return false;
18002
18218
  if (typeof s === 'number')
18003
- return s !== 0;
18219
+ return s > 0;
18004
18220
  return undefined;
18005
- // @todo: use this.functionDefinition.range
18006
18221
  }
18007
18222
  // x <= 0
18008
18223
  get isNonPositive() {
@@ -18012,7 +18227,6 @@ class BoxedFunction extends AbstractBoxedExpression {
18012
18227
  if (typeof s === 'number')
18013
18228
  return s <= 0;
18014
18229
  return undefined;
18015
- // @todo: use this.functionDefinition.range
18016
18230
  }
18017
18231
  // x < 0
18018
18232
  get isNegative() {
@@ -18022,7 +18236,6 @@ class BoxedFunction extends AbstractBoxedExpression {
18022
18236
  if (typeof s === 'number')
18023
18237
  return s < 0;
18024
18238
  return undefined;
18025
- // @todo: use this.functionDefinition.range
18026
18239
  }
18027
18240
  // x >= 0
18028
18241
  get isNonNegative() {
@@ -18032,7 +18245,6 @@ class BoxedFunction extends AbstractBoxedExpression {
18032
18245
  if (typeof s === 'number')
18033
18246
  return s >= 0;
18034
18247
  return undefined;
18035
- // @todo: use this.functionDefinition.range
18036
18248
  }
18037
18249
  get isNumber() {
18038
18250
  return this.domain.isCompatible('Number');
@@ -18046,124 +18258,21 @@ class BoxedFunction extends AbstractBoxedExpression {
18046
18258
  get isAlgebraic() {
18047
18259
  return this.domain.isCompatible('AlgebraicNumber');
18048
18260
  }
18049
- get isReal() {
18050
- return this.domain.isCompatible('RealNumber');
18051
- }
18052
- get isExtendedReal() {
18053
- return this.domain.isCompatible('ExtendedRealNumber');
18054
- }
18055
- get isComplex() {
18056
- return this.domain.isCompatible('ComplexNumber');
18057
- }
18058
- get isImaginary() {
18059
- return this.domain.isCompatible('ImaginaryNumber');
18060
- }
18061
- get json() {
18062
- // If this expression is canonical, apply some transformations to the
18063
- // JSON serialization to "reverse" some of the effects of canonicalization.
18064
- if (this._isCanonical)
18065
- return serializeJsonCanonicalFunction(this.engine, this._head, this._ops, { latex: this._latex, wikidata: this._wikidata });
18066
- return serializeJsonFunction(this.engine, this._head, this._ops, {
18067
- latex: this._latex,
18068
- wikidata: this._wikidata,
18069
- });
18070
- }
18071
- has(x) {
18072
- if (typeof this._head === 'string') {
18073
- if (typeof x === 'string') {
18074
- if (this._head === x)
18075
- return true;
18076
- }
18077
- else if (x.includes(this._head))
18078
- return true;
18079
- }
18080
- for (const arg of this._ops)
18081
- if (arg.has(x))
18082
- return true;
18083
- return false;
18084
- }
18085
- /** `isSame` is structural/symbolic equality */
18086
- isSame(rhs) {
18087
- if (this === rhs)
18088
- return true;
18089
- if (!(rhs instanceof BoxedFunction))
18090
- return false;
18091
- // Number of arguments must match
18092
- if (this.nops !== rhs.nops)
18093
- return false;
18094
- // Head must match
18095
- if (typeof this.head === 'string') {
18096
- if (this.head !== rhs.head)
18097
- return false;
18098
- }
18099
- else {
18100
- if (typeof rhs.head === 'string')
18101
- return false;
18102
- else if (!rhs.head || !this.head.isSame(rhs.head))
18103
- return false;
18104
- }
18105
- // Each argument must match
18106
- const lhsTail = this._ops;
18107
- const rhsTail = rhs._ops;
18108
- for (let i = 0; i < lhsTail.length; i++)
18109
- if (!lhsTail[i].isSame(rhsTail[i]))
18110
- return false;
18111
- return true;
18261
+ get isReal() {
18262
+ return this.domain.isCompatible('RealNumber');
18112
18263
  }
18113
- match(rhs, options) {
18114
- if (!(rhs instanceof BoxedFunction))
18115
- return null;
18116
- let result = {};
18117
- // Head must match
18118
- if (typeof this.head === 'string') {
18119
- if (this.head !== rhs.head)
18120
- return null;
18121
- }
18122
- else {
18123
- if (typeof rhs.head === 'string')
18124
- return null;
18125
- else {
18126
- if (!rhs.head)
18127
- return null;
18128
- const m = this.head.match(rhs.head, options);
18129
- if (m === null)
18130
- return null;
18131
- result = { ...result, ...m };
18132
- }
18133
- }
18134
- // Each argument must match
18135
- const lhsTail = this._ops;
18136
- const rhsTail = rhs._ops;
18137
- for (let i = 0; i < lhsTail.length; i++) {
18138
- const m = lhsTail[i].match(rhsTail[i], options);
18139
- if (m === null)
18140
- return null;
18141
- result = { ...result, ...m };
18142
- }
18143
- return result;
18264
+ get isExtendedReal() {
18265
+ return this.domain.isCompatible('ExtendedRealNumber');
18144
18266
  }
18145
- /** `isEqual` is mathematical equality */
18146
- isEqual(rhs) {
18147
- if (!this.isCanonical)
18148
- return this.canonical.isEqual(rhs);
18149
- rhs = rhs.canonical;
18150
- if (rhs.isNumber && this.isNumber) {
18151
- const ce = this.engine;
18152
- // In general, it is impossible to always prove equality
18153
- // (Richardson's theorem) but this works often...
18154
- const diff = ce.add([this, ce.negate(rhs)]).N();
18155
- if (diff.isZero)
18156
- return true;
18157
- if (diff.asFloat !== null && ce.chop(diff.asFloat) === 0)
18158
- return true;
18159
- return this.evaluate().isSame(rhs.evaluate());
18160
- }
18161
- if (this.domain.isRelationalOperator && rhs.domain.isRelationalOperator) {
18162
- return this.evaluate().isSame(rhs.evaluate());
18163
- }
18164
- return this.isSame(rhs);
18267
+ get isComplex() {
18268
+ return this.domain.isCompatible('ComplexNumber');
18269
+ }
18270
+ get isImaginary() {
18271
+ return this.domain.isCompatible('ImaginaryNumber');
18165
18272
  }
18166
18273
  get sgn() {
18274
+ if (!this.isCanonical)
18275
+ return this.canonical.sgn;
18167
18276
  // @todo: if there is a this.functionDefinition.range, use it
18168
18277
  // @todo if inconclusive, and there is a this.def._sgn, call it
18169
18278
  // @todo: add sgn() function to FunctionDefinition
@@ -18241,81 +18350,14 @@ class BoxedFunction extends AbstractBoxedExpression {
18241
18350
  return +1;
18242
18351
  }
18243
18352
  // @todo: trig functions, geometric functions
18244
- return null;
18245
- }
18246
- *map(fn) {
18247
- let i = 0;
18248
- while (i < this._ops.length)
18249
- yield fn(this._ops[i++]);
18250
- }
18251
- get isCanonical() {
18252
- return this._isCanonical;
18253
- }
18254
- set isCanonical(val) {
18255
- this._isCanonical = val;
18256
- }
18257
- apply(fn, head) {
18258
- const newHead = head ?? this.head;
18259
- let opsChanged = false;
18260
- const ops = [];
18261
- for (const arg of this._ops) {
18262
- const newArg = fn(arg);
18263
- if (arg !== newArg)
18264
- opsChanged = true;
18265
- ops.push(this.engine.box(newArg));
18266
- }
18267
- if (!opsChanged && this.head === newHead)
18268
- return this;
18269
- return this.engine.fn(newHead, ops);
18270
- }
18271
- get canonical() {
18272
- if (this.isCanonical || !this.isValid)
18273
- return this;
18274
- // 1/ If no definition (i.e. function `g`...), apply canonical to each op
18275
- const def = this.functionDefinition;
18276
- if (!def) {
18277
- const tail = this._ops.map((x) => x.canonical);
18278
- if (tail.length === this._ops.length &&
18279
- tail.every((x, i) => this._ops[i] === x)) {
18280
- // We were already canonical! No need to create a new expression
18281
- this._isCanonical = true;
18282
- return this;
18283
- }
18284
- return this.engine._fn(this._head, tail);
18285
- }
18286
- //
18287
- // 2/ Get the canonical form of the arguments, accounting for `Hold`,
18288
- // `ReleaseHold`, `Sequence` and `Symbol`
18289
- //
18290
- let tail = canonicalHoldMap(def.name, this._ops, def.hold, def.associative);
18291
- //
18292
- // 3/ Apply `canonical` handler
18293
- //
18294
- const sig = def.signature;
18295
- if (sig?.canonical)
18296
- return sig.canonical(this.engine, tail);
18297
- //
18298
- // 4/ Apply `idempotent` and `involution`
18299
- //
18300
- if (tail.length === 1 && tail[0].head === this._head) {
18301
- // f(f(x)) -> x
18302
- if (def.involution)
18303
- return tail[0].op1;
18304
- // f(f(x)) -> f(x)
18305
- if (def.idempotent)
18306
- tail = tail[0].ops;
18307
- }
18308
- //
18309
- // 5/ Sort the arguments
18310
- //
18311
- if (tail.length > 1 && def.commutative === true)
18312
- tail = tail.sort(order);
18313
- if (tail.length === this._ops.length &&
18314
- tail.every((x, i) => this._ops[i] === x)) {
18315
- this._isCanonical = true;
18316
- return this;
18317
- }
18318
- return this.engine._fn(this._head, tail);
18353
+ const v = (this.numericValue ?? this.N()).asSmallInteger;
18354
+ if (v === null)
18355
+ return undefined;
18356
+ if (v === 0)
18357
+ return 0;
18358
+ if (v < 0)
18359
+ return -1;
18360
+ return +1;
18319
18361
  }
18320
18362
  simplify(options) {
18321
18363
  //
@@ -18483,14 +18525,75 @@ class BoxedFunction extends AbstractBoxedExpression {
18483
18525
  // @todo
18484
18526
  return null;
18485
18527
  }
18486
- replace(rules, options) {
18487
- return replace(this, rules, options);
18528
+ }
18529
+ function makeCanonicalFunction(ce, head, ops, options) {
18530
+ options ?? (options = {});
18531
+ options.canonical = true;
18532
+ //
18533
+ // Canonicalize the arguments and flatten any sequence
18534
+ //
18535
+ ops = ops.map((x) => x.canonical);
18536
+ ops = flattenSequence(ops);
18537
+ //
18538
+ // Is the head an expression? For example, `['InverseFunction', 'Sin']`
18539
+ //
18540
+ if (typeof head !== 'string')
18541
+ head = head.evaluate().symbol ?? head;
18542
+ if (typeof head !== 'string')
18543
+ return new BoxedFunction(ce, head, ops, options);
18544
+ if (ce.context === null)
18545
+ return new BoxedFunction(ce, head, ops, options);
18546
+ const def = ce.lookupFunction(head, ce.context);
18547
+ if (!def)
18548
+ return new BoxedFunction(ce, head, ops, options);
18549
+ // f(a, f(b, c), d) -> f(a, b, c, d)
18550
+ if (def.associative)
18551
+ ops = flattenOps(ops, head) ?? ops;
18552
+ // Scoped function automatically create a new scope
18553
+ // @todo: this needs to be done when evaluating, not here...
18554
+ if (def.scoped)
18555
+ ce.pushScope();
18556
+ // Apply Sequence, Symbol
18557
+ // this._ops = normalizeList(this._ops, this._def.hold);
18558
+ const sig = def.signature;
18559
+ ops = validateSignature(ce, sig, ops) ?? ops;
18560
+ if (!ops.every((x) => x.isValid)) {
18561
+ if (def.scoped)
18562
+ ce.popScope();
18563
+ return new BoxedFunction(ce, head, ops, { ...options, def });
18488
18564
  }
18489
- subs(sub) {
18490
- // Call `fn().canonical` (and not `new BoxedFunction()`) so that the
18491
- // function may be reconstructed as a canonical expression
18492
- return this.engine.fn(this._head, this._ops.map((x) => x.subs(sub))).canonical;
18565
+ //
18566
+ // 3/ Apply `canonical` handler
18567
+ //
18568
+ if (sig.canonical) {
18569
+ const fn = sig.canonical(ce, ops);
18570
+ if (fn.isValid && !fn.isCanonical)
18571
+ debugger;
18572
+ console.assert(!fn.isValid || fn.isCanonical);
18573
+ return fn;
18574
+ }
18575
+ //
18576
+ // 4/ No handler, apply idempotent and involution
18577
+ //
18578
+ //
18579
+ // 4/ Apply `idempotent` and `involution`
18580
+ //
18581
+ if (ops.length === 1 && ops[0].head === head) {
18582
+ // f(f(x)) -> x
18583
+ if (def.involution)
18584
+ return ops[0].op1;
18585
+ // f(f(x)) -> f(x)
18586
+ if (def.idempotent)
18587
+ ops = ops[0].ops;
18493
18588
  }
18589
+ //
18590
+ // 5/ Sort the arguments
18591
+ //
18592
+ if (ops.length > 1 && def.commutative === true)
18593
+ ops = ops.sort(order);
18594
+ if (def.scoped)
18595
+ ce.popScope();
18596
+ return new BoxedFunction(ce, head, ops, { ...options, def });
18494
18597
  }
18495
18598
  function lambda(ce, fn, args) {
18496
18599
  // 'fn' is a lambda expression.
@@ -18503,9 +18606,7 @@ function lambda(ce, fn, args) {
18503
18606
  subs[`_${n++}`] = op;
18504
18607
  subs['_'] = subs['_1'];
18505
18608
  // Substitute the arguments in the lambda expression
18506
- const result = fn.subs(subs);
18507
- result.bind(ce.context);
18508
- return result;
18609
+ return fn.subs(subs);
18509
18610
  }
18510
18611
  // export function ungroup(expr: BoxedExpression): BoxedExpression {
18511
18612
  // if (!expr.ops) return expr;
@@ -18548,20 +18649,9 @@ function holdMap(head, xs, skip, associative, f) {
18548
18649
  else
18549
18650
  result.push(xs[i]);
18550
18651
  if (y) {
18551
- if (y.head === 'Sequence') {
18552
- if (y.ops)
18553
- result.push(...y.ops.map((x) => f(x)).filter((x) => x !== null));
18554
- }
18555
- else if (y.domain.literal === 'Symbol') {
18556
- const x = f(y.evaluate());
18557
- if (x !== null)
18558
- result.push(x);
18559
- }
18560
- else {
18561
- const x = f(y);
18562
- if (x !== null)
18563
- result.push(x);
18564
- }
18652
+ const x = f(y);
18653
+ if (x !== null)
18654
+ result.push(x);
18565
18655
  }
18566
18656
  }
18567
18657
  }
@@ -18581,37 +18671,6 @@ function flattenSequence(xs) {
18581
18671
  }
18582
18672
  return ys;
18583
18673
  }
18584
- // Like `HoldMap` but preserves `Hold` and `ReleaseHold`
18585
- function canonicalHoldMap(head, xs, skip, associative) {
18586
- if (xs.length === 0)
18587
- return [];
18588
- // f(a, f(b, c), d) -> f(a, b, c, d)
18589
- if (associative)
18590
- xs = flattenOps(xs, head) ?? xs;
18591
- //
18592
- // Apply the hold as necessary
18593
- //
18594
- const result = [];
18595
- for (let i = 0; i < xs.length; i++) {
18596
- const x = xs[i];
18597
- if (x.head === 'Hold' ||
18598
- x.head === 'ReleaseHold' ||
18599
- !applicable(skip, xs.length - 1, i)) {
18600
- result.push(x);
18601
- }
18602
- else if (x.head === 'Sequence') {
18603
- if (x.ops)
18604
- result.push(...x.ops.map((x) => x.canonical));
18605
- }
18606
- else if (x.head === 'Symbol' || x.domain.literal === 'Symbol')
18607
- result.push(x.evaluate());
18608
- else
18609
- result.push(x.canonical);
18610
- }
18611
- if (associative)
18612
- return flattenOps(result, head) ?? result;
18613
- return result;
18614
- }
18615
18674
  function applicable(skip, count, index) {
18616
18675
  if (skip === 'all')
18617
18676
  return false;
@@ -18627,24 +18686,6 @@ function applicable(skip, count, index) {
18627
18686
  return index === count;
18628
18687
  return false;
18629
18688
  }
18630
- function normalizeList(xs, skip) {
18631
- // Fold 'Sequence'
18632
- const result = [];
18633
- for (let i = 0; i < xs.length; i++) {
18634
- if (applicable(skip, xs.length - 1, i)) {
18635
- const x = xs[i];
18636
- if (x.head === 'Sequence')
18637
- result.push(...(x.ops ?? []));
18638
- else if (x.head === 'Symbol')
18639
- result.push(x.evaluate());
18640
- else
18641
- result.push(x);
18642
- }
18643
- else
18644
- result.push(xs[i]);
18645
- }
18646
- return result;
18647
- }
18648
18689
  /** Return `null` if the `ops` match the sig. Otherwise, return an array
18649
18690
  * of expressions indicating the mismatched arguments.
18650
18691
  *
@@ -18974,6 +19015,12 @@ class BoxedNumber extends AbstractBoxedExpression {
18974
19015
  set isCanonical(val) {
18975
19016
  this._isCanonical = val;
18976
19017
  }
19018
+ get complexity() {
19019
+ return 1;
19020
+ }
19021
+ get value() {
19022
+ return this;
19023
+ }
18977
19024
  get numericValue() {
18978
19025
  if (!Array.isArray(this._value))
18979
19026
  return this;
@@ -19007,8 +19054,7 @@ class BoxedNumber extends AbstractBoxedExpression {
19007
19054
  return Number.POSITIVE_INFINITY;
19008
19055
  return Number.NEGATIVE_INFINITY;
19009
19056
  }
19010
- if (isInMachineRange(this._value))
19011
- return this._value.toNumber();
19057
+ return this._value.toNumber();
19012
19058
  }
19013
19059
  if (Array.isArray(this._value))
19014
19060
  return this._value[0] / this._value[1];
@@ -19644,7 +19690,7 @@ function box(ce, expr) {
19644
19690
  if ('fn' in expr) {
19645
19691
  if (typeof expr.fn[0] === 'string')
19646
19692
  return boxFunction(ce, expr.fn[0], expr.fn.slice(1), metadata);
19647
- return new BoxedFunction(ce, box(ce, expr.fn[0]), expr.fn.slice(1).map((x) => box(ce, x)), metadata);
19693
+ return new BoxedFunction(ce, box(ce, expr.fn[0]), expr.fn.slice(1).map((x) => box(ce, x)), { metadata });
19648
19694
  }
19649
19695
  if ('str' in expr)
19650
19696
  return new BoxedString(ce, expr.str, metadata);
@@ -19672,7 +19718,7 @@ function boxNumber(ce, num, metadata) {
19672
19718
  if (Array.isArray(num)) {
19673
19719
  if (num.length !== 2)
19674
19720
  throw new Error('Array argument to boxNumber() should be two integers');
19675
- const [n, d] = num;
19721
+ let [n, d] = num;
19676
19722
  if (typeof n !== 'number' || typeof d !== 'number')
19677
19723
  throw new Error('Array argument to boxNumber() should be two integers');
19678
19724
  if (!Number.isInteger(n) || !Number.isInteger(d))
@@ -19822,7 +19868,9 @@ function boxFunction(ce, head, ops, metadata) {
19822
19868
  // Hold
19823
19869
  //
19824
19870
  if (head === 'Hold') {
19825
- const result = new BoxedFunction(ce, 'Hold', [boxHold(ce, ops[0])], metadata);
19871
+ const result = new BoxedFunction(ce, 'Hold', [boxHold(ce, ops[0])], {
19872
+ metadata,
19873
+ });
19826
19874
  // Hold is always canonical
19827
19875
  result.isCanonical = true;
19828
19876
  return result;
@@ -19859,7 +19907,9 @@ function boxFunction(ce, head, ops, metadata) {
19859
19907
  ];
19860
19908
  if (n !== null && d !== null)
19861
19909
  return ce.number([n, d], metadata);
19862
- return new BoxedFunction(ce, 'Rational', [ops[0], ops[1]], metadata);
19910
+ return new BoxedFunction(ce, 'Rational', [ops[0], ops[1]], {
19911
+ metadata,
19912
+ });
19863
19913
  }
19864
19914
  }
19865
19915
  else {
@@ -19869,7 +19919,9 @@ function boxFunction(ce, head, ops, metadata) {
19869
19919
  if (n?.isInteger() && d?.isInteger()) {
19870
19920
  if (isInMachineRange(n) && isInMachineRange(d))
19871
19921
  return ce.number([n.toNumber(), d.toNumber()], metadata);
19872
- return new BoxedFunction(ce, 'Rational', [ce.box(op1), ce.box(op2)], metadata);
19922
+ return new BoxedFunction(ce, 'Rational', [ce.box(op1), ce.box(op2)], {
19923
+ metadata,
19924
+ });
19873
19925
  }
19874
19926
  }
19875
19927
  head = 'Divide';
@@ -19927,14 +19979,16 @@ function boxFunction(ce, head, ops, metadata) {
19927
19979
  return ce.error('expected-argument', "'Single");
19928
19980
  if (ops.length > 1)
19929
19981
  return ce.error('unexpected-argument', "'Single");
19930
- return new BoxedFunction(ce, 'Tuple', [ce.box(ops[0])], metadata);
19982
+ return new BoxedFunction(ce, 'Tuple', [ce.box(ops[0])], { metadata });
19931
19983
  }
19932
19984
  if (head === 'Pair') {
19933
19985
  if (ops.length < 2)
19934
19986
  return ce.error('expected-argument', "'Pair");
19935
19987
  if (ops.length > 2)
19936
19988
  return ce.error('unexpected-argument', "'Pair");
19937
- return new BoxedFunction(ce, 'Tuple', [ce.box(ops[0]), ce.box(ops[1])], metadata);
19989
+ return new BoxedFunction(ce, 'Tuple', [ce.box(ops[0]), ce.box(ops[1])], {
19990
+ metadata,
19991
+ });
19938
19992
  }
19939
19993
  // KeyValuePair is not normalized to Tuple
19940
19994
  if (head === 'KeyValuePair') {
@@ -19942,14 +19996,14 @@ function boxFunction(ce, head, ops, metadata) {
19942
19996
  return ce.error('expected-argument', "'KeyValuePair");
19943
19997
  if (ops.length > 2)
19944
19998
  return ce.error('unexpected-argument', "'KeyValuePair");
19945
- return new BoxedFunction(ce, 'KeyValuePair', [ce.box(ops[0]), ce.box(ops[1])], metadata);
19999
+ return new BoxedFunction(ce, 'KeyValuePair', [ce.box(ops[0]), ce.box(ops[1])], { metadata });
19946
20000
  }
19947
20001
  if (head === 'Triple') {
19948
20002
  if (ops.length < 3)
19949
20003
  return ce.error('expected-argument', "'Triple");
19950
20004
  if (ops.length > 3)
19951
20005
  return ce.error('unexpected-argument', "'Triple");
19952
- return new BoxedFunction(ce, 'Tuple', ops.map((x) => ce.box(x)), metadata);
20006
+ return new BoxedFunction(ce, 'Tuple', ops.map((x) => ce.box(x)), { metadata });
19953
20007
  }
19954
20008
  //
19955
20009
  // Dictionary
@@ -19978,7 +20032,7 @@ function boxFunction(ce, head, ops, metadata) {
19978
20032
  }
19979
20033
  return new BoxedDictionary(ce, dict, metadata);
19980
20034
  }
19981
- return new BoxedFunction(ce, head, ops.map((x) => box(ce, x)), metadata);
20035
+ return new BoxedFunction(ce, head, ops.map((x) => box(ce, x)), { metadata });
19982
20036
  }
19983
20037
  function asString(expr) {
19984
20038
  if (typeof expr === 'string')
@@ -20789,10 +20843,14 @@ function square(ce, base) {
20789
20843
  function processPower(ce, base, exponent, mode) {
20790
20844
  if (mode !== 'simplify' && base.isLiteral && exponent.isLiteral) {
20791
20845
  if (mode === 'N' || !base.isInteger) {
20792
- if (base.complexValue)
20846
+ if (base.complexValue) {
20793
20847
  return ce.number(base.complexValue.pow(exponent.complexValue ?? exponent.asFloat ?? NaN));
20794
- if (exponent.complexValue && base.asFloat !== null)
20795
- return ce.number(ce.complex(base.asFloat).pow(exponent.complexValue));
20848
+ }
20849
+ if (exponent.complexValue) {
20850
+ const b = base.asFloat ?? base.decimalValue?.toNumber() ?? null;
20851
+ if (b !== null)
20852
+ return ce.number(ce.complex(b).pow(exponent.complexValue));
20853
+ }
20796
20854
  if (base.decimalValue) {
20797
20855
  return ce.number(base.decimalValue.pow(exponent.decimalValue ?? exponent.asFloat));
20798
20856
  }
@@ -20929,15 +20987,25 @@ function evalMultiply(ce, ops, mode = 'evaluate') {
20929
20987
  product.addTerm(arg);
20930
20988
  }
20931
20989
  }
20932
- if (!complexAllowed(ce) && complexProduct.im !== 0)
20933
- return ce._NAN;
20990
+ if (complexProduct.im !== 0) {
20991
+ if (!complexAllowed(ce))
20992
+ return ce._NAN;
20993
+ // We have an imaginary number: fold Decimal into machine numbers
20994
+ machineProduct *= decimalProduct.toNumber();
20995
+ decimalProduct = ce._DECIMAL_ONE;
20996
+ numer *= decimalNumer.toNumber();
20997
+ denom *= decimalDenom.toNumber();
20998
+ decimalNumer = ce._DECIMAL_ONE;
20999
+ decimalDenom = ce._DECIMAL_ONE;
21000
+ }
20934
21001
  if (decimalDenom.eq(ce._DECIMAL_ONE) && isInMachineRange(decimalNumer)) {
20935
21002
  numer = denom * decimalNumer.toNumber();
20936
21003
  decimalNumer = ce._DECIMAL_ONE;
20937
21004
  }
20938
- if (preferDecimal(ce) ||
20939
- !decimalProduct.eq(ce._DECIMAL_ONE) ||
20940
- !(decimalNumer.eq(ce._DECIMAL_ONE) && decimalDenom.eq(ce._DECIMAL_ONE))) {
21005
+ if (complexProduct.im === 0 &&
21006
+ (preferDecimal(ce) ||
21007
+ !decimalProduct.eq(ce._DECIMAL_ONE) ||
21008
+ !(decimalNumer.eq(ce._DECIMAL_ONE) && decimalDenom.eq(ce._DECIMAL_ONE)))) {
20941
21009
  // Fold into decimal
20942
21010
  let d = decimalProduct.mul(machineProduct);
20943
21011
  if (mode === 'N') {
@@ -21179,7 +21247,7 @@ function canonicalDivide(ce, op1, op2) {
21179
21247
  return canonicalNegate(op1);
21180
21248
  const [n, d] = [op1.asSmallInteger, op2.asSmallInteger];
21181
21249
  if (n !== null && d !== null && d !== 0)
21182
- return ce.number([n, d]);
21250
+ return ce.number(reducedRational$1([n, d]));
21183
21251
  if (op1.isInteger && op2.isInteger) {
21184
21252
  // eslint-disable-next-line prefer-const
21185
21253
  let [nSign, dn] = makePositive(op1);
@@ -21476,7 +21544,7 @@ const ARITHMETIC_LIBRARY = [
21476
21544
  wikidata: 'Q204037',
21477
21545
  complexity: 4000,
21478
21546
  signature: {
21479
- domain: ['Function', 'Number', 'Number', 'Number'],
21547
+ domain: ['Function', 'Number', 'Number'],
21480
21548
  N: (ce, ops) => {
21481
21549
  if (ops[0].decimalValue)
21482
21550
  return ce.number(ops[0].decimalValue.log());
@@ -21494,7 +21562,7 @@ const ARITHMETIC_LIBRARY = [
21494
21562
  wikidata: 'Q11197',
21495
21563
  complexity: 4100,
21496
21564
  signature: {
21497
- domain: ['Function', 'Number', 'Number', 'Number'],
21565
+ domain: ['Function', 'Number', ['Maybe', 'Number'], 'Number'],
21498
21566
  N: (ce, ops) => {
21499
21567
  const exponent = ops[0];
21500
21568
  const base = ops[1] ?? ce.number(10);
@@ -21572,7 +21640,7 @@ const ARITHMETIC_LIBRARY = [
21572
21640
  let result = undefined;
21573
21641
  const rest = [];
21574
21642
  for (const op of ops) {
21575
- if (!op.isNumber || op.value === undefined)
21643
+ if (!op.isNumber || op.numericValue === undefined)
21576
21644
  rest.push(op);
21577
21645
  else if (!result || op.isGreater(result))
21578
21646
  result = op;
@@ -21602,7 +21670,7 @@ const ARITHMETIC_LIBRARY = [
21602
21670
  let result = undefined;
21603
21671
  const rest = [];
21604
21672
  for (const op of ops) {
21605
- if (!op.isNumber || op.value === undefined)
21673
+ if (!op.isNumber || op.numericValue === undefined)
21606
21674
  rest.push(op);
21607
21675
  else if (!result || op.isLess(result))
21608
21676
  result = op;
@@ -21737,11 +21805,13 @@ const ARITHMETIC_LIBRARY = [
21737
21805
  return ce.number([n, d]);
21738
21806
  return undefined;
21739
21807
  }
21808
+ //
21740
21809
  // If there is a single argument, i.e. `['Rational', 'Pi']`
21741
21810
  // the function evaluates to a rational expression of the argument
21742
- const f = ops[0].asFloat ?? ops[0].decimalValue?.toNumber() ?? null;
21811
+ //
21812
+ const f = ops[0].N().asFloat ?? null;
21743
21813
  if (f === null)
21744
- return ops[0];
21814
+ return undefined;
21745
21815
  const r = rationalize(f);
21746
21816
  if (typeof r === 'number')
21747
21817
  return ce.number(r);
@@ -21944,10 +22014,8 @@ const ARITHMETIC_LIBRARY = [
21944
22014
  }
21945
22015
  // Maybe a compound symbol
21946
22016
  let sub = op2.string ?? op2.symbol;
21947
- if (!sub) {
21948
- if (op2.asSmallInteger !== null)
21949
- sub = op2.asSmallInteger.toString();
21950
- }
22017
+ if (!sub && op2.isLiteral && op2.asSmallInteger !== null)
22018
+ sub = op2.asSmallInteger.toString();
21951
22019
  if (sub)
21952
22020
  return ce.symbol(op1.symbol + '_' + sub);
21953
22021
  }
@@ -22656,29 +22724,22 @@ const CORE_LIBRARY = [
22656
22724
  threadable: true,
22657
22725
  hold: 'all',
22658
22726
  signature: {
22659
- domain: ['Function', ['Sequence', 'Anything'], 'Symbol'],
22660
- evaluate: (ce, ops) => {
22727
+ domain: ['Function', ['Sequence', 'Anything'], 'Anything'],
22728
+ canonical: (ce, ops) => {
22661
22729
  if (ops.length === 0)
22662
22730
  return ce.symbol('Nothing');
22663
- const args = ops;
22664
- const arg = args
22665
- .map((x) => {
22666
- const symName = x.symbol;
22667
- if (symName !== null)
22668
- return symName;
22669
- const stringValue = arg.string;
22670
- if (stringValue !== null)
22671
- return stringValue;
22672
- const numValue = arg.smallIntegerValue;
22673
- if (numValue !== null)
22674
- return numValue.toString();
22675
- return '';
22676
- })
22731
+ const arg = ops
22732
+ .map((x) => x.symbol ??
22733
+ x.string ??
22734
+ (x.isLiteral ? x.asSmallInteger?.toString() : null) ??
22735
+ '')
22677
22736
  .join('');
22678
22737
  if (arg.length > 0)
22679
22738
  return ce.symbol(arg);
22680
22739
  return ce.symbol('Nothing');
22681
22740
  },
22741
+ // Note: a `["Symbol"]` expression is never evaluated, it gets
22742
+ // transformed into something else (a symbol) during canonicalization
22682
22743
  },
22683
22744
  },
22684
22745
  {
@@ -24622,7 +24683,7 @@ class BoxedSymbolDefinitionImpl {
24622
24683
  this._domain = null;
24623
24684
  }
24624
24685
  bind() {
24625
- this._value = undefined;
24686
+ this._value = null;
24626
24687
  // this._domain = this._domain?._purge();
24627
24688
  const def = this._def;
24628
24689
  const ce = this._engine;
@@ -24650,10 +24711,10 @@ class BoxedSymbolDefinitionImpl {
24650
24711
  // unit: def.unit ? ce.box(def.unit) : undefined,
24651
24712
  });
24652
24713
  //
24653
- // 1/ Is it a number?
24714
+ // 1/ Is it defined as a simple machine number?
24654
24715
  //
24655
24716
  if ('value' in def && typeof def.value === 'number') {
24656
- // If the dictionary entry is provided as a number, assume it's a
24717
+ // If the definition entry is provided as a number, assume it's a
24657
24718
  // variable, and infer its domain based on its value.
24658
24719
  const value = ce.number(def.value);
24659
24720
  let domain;
@@ -24672,12 +24733,15 @@ class BoxedSymbolDefinitionImpl {
24672
24733
  //
24673
24734
  // 2/ It's a full definition with no value or a non-numeric value
24674
24735
  //
24675
- let value;
24676
- if (def.hold === false) {
24677
- value = this.value;
24678
- if (!value)
24679
- throw new Error(`Symbol definition "${def.name}": Expected a value "hold=false" `);
24680
- }
24736
+ let value = undefined;
24737
+ if (isLatexString(def.value))
24738
+ value = ce.parse(def.value);
24739
+ else if (typeof def.value === 'function')
24740
+ value = ce.box(def.value(ce) ?? 'Undefined');
24741
+ else if (def.value)
24742
+ value = ce.box(def.value);
24743
+ if (!value && def.hold === false)
24744
+ throw new Error(`Symbol definition "${def.name}": Expected a value when "hold=false" `);
24681
24745
  value = value?.canonical;
24682
24746
  //
24683
24747
  // If there is a domain specified in the definition, and it is compatible
@@ -24695,38 +24759,27 @@ class BoxedSymbolDefinitionImpl {
24695
24759
  domain = defDomain;
24696
24760
  else
24697
24761
  domain = value?.domain ?? ce.defaultDomain;
24698
- if (!value) {
24699
- this._value = undefined;
24700
- this._domain = domain;
24701
- this.setProps(domainToFlags(domain));
24702
- this.setProps(result);
24703
- return;
24704
- }
24705
24762
  this._value = value;
24706
24763
  this._domain = domain;
24707
- this.setProps(valueToFlags(value));
24764
+ if (value)
24765
+ this.setProps(valueToFlags(value));
24708
24766
  this.setProps(domainToFlags(domain));
24709
24767
  this.setProps(result);
24710
24768
  }
24711
24769
  get value() {
24712
24770
  if (this._value === null)
24713
24771
  this.bind();
24714
- if (this._value === undefined) {
24715
- if (isLatexString(this._def.value))
24716
- this._value = this._engine.parse(this._def.value);
24717
- else if (typeof this._def.value === 'function')
24718
- this._value = this._engine.box(this._def.value(this._engine) ?? 'Undefined');
24719
- else if (this._def.value)
24720
- this._value = this._engine.box(this._def.value);
24721
- }
24722
24772
  return this._value ?? undefined;
24723
24773
  }
24724
24774
  set value(val) {
24775
+ // Need to bind first to check, e.g. `this.constant`
24776
+ if (this._value === null)
24777
+ this.bind();
24725
24778
  if (this.constant)
24726
24779
  throw new Error(`The value of the constant "${this.name}" cannot be changed`);
24727
24780
  if (typeof val === 'number')
24728
24781
  val = this._engine.box(val);
24729
- this._value = val;
24782
+ this._value = val ?? null;
24730
24783
  if (val)
24731
24784
  this.setProps(valueToFlags(val));
24732
24785
  }
@@ -25766,26 +25819,26 @@ class BoxedSymbol extends AbstractBoxedExpression {
25766
25819
  return;
25767
25820
  }
25768
25821
  get canonical() {
25769
- if (this.symbolDefinition?.hold === false)
25770
- return (this.symbolDefinition.value?.value ??
25771
- this.symbolDefinition.value ??
25772
- this);
25773
- return this;
25822
+ if (this.symbolDefinition?.hold === true)
25823
+ return this;
25824
+ return (this.symbolDefinition?.value?.value ??
25825
+ this.symbolDefinition?.value ??
25826
+ this);
25774
25827
  }
25775
25828
  get wikidata() {
25776
- return this._wikidata ?? this.baseDefinition?.wikidata ?? '';
25829
+ return this._wikidata ?? this.baseDefinition?.wikidata ?? undefined;
25777
25830
  }
25778
25831
  get description() {
25779
25832
  if (!this.baseDefinition)
25780
- return [];
25833
+ return undefined;
25781
25834
  if (!this.baseDefinition.description)
25782
- return [];
25835
+ return undefined;
25783
25836
  if (typeof this.baseDefinition.description === 'string')
25784
25837
  return [this.baseDefinition.description];
25785
25838
  return this.baseDefinition.description;
25786
25839
  }
25787
25840
  get url() {
25788
- return this.baseDefinition?.url ?? '';
25841
+ return this.baseDefinition?.url ?? undefined;
25789
25842
  }
25790
25843
  get complexity() {
25791
25844
  return 7;
@@ -25865,7 +25918,7 @@ class BoxedSymbol extends AbstractBoxedExpression {
25865
25918
  }
25866
25919
  }
25867
25920
  get value() {
25868
- return this.symbolDefinition?.value;
25921
+ return this.symbolDefinition?.value ?? this;
25869
25922
  }
25870
25923
  set value(value) {
25871
25924
  // Symbols starting with `_` are wildcards and never have an associated
@@ -25912,7 +25965,7 @@ class BoxedSymbol extends AbstractBoxedExpression {
25912
25965
  }
25913
25966
  }
25914
25967
  get numericValue() {
25915
- return this.symbolDefinition?.value?.numericValue;
25968
+ return this.symbolDefinition?.value?.numericValue ?? undefined;
25916
25969
  }
25917
25970
  get domain() {
25918
25971
  if (this.functionDefinition)
@@ -25949,7 +26002,7 @@ class BoxedSymbol extends AbstractBoxedExpression {
25949
26002
  get explicitDomain() {
25950
26003
  if (this.functionDefinition)
25951
26004
  return this.engine.domain('Function');
25952
- return this.symbolDefinition?.domain ?? null;
26005
+ return this.symbolDefinition?.domain ?? undefined;
25953
26006
  }
25954
26007
  get json() {
25955
26008
  return serializeJsonSymbol(this.engine, this._name, {
@@ -25960,17 +26013,23 @@ class BoxedSymbol extends AbstractBoxedExpression {
25960
26013
  get sgn() {
25961
26014
  // If available, use the value associated with this symbol.
25962
26015
  // Note that `null` is an acceptable and valid value
25963
- const s = this.value?.sgn;
25964
- if (s !== undefined)
25965
- return s;
26016
+ const v = this.numericValue;
26017
+ if (v && v !== this) {
26018
+ const s = v.sgn;
26019
+ if (s !== undefined)
26020
+ return s;
26021
+ }
25966
26022
  // We didn't get a definitive answer from the value
25967
26023
  // of this symbol. Check flags.
25968
- if (this.symbolDefinition?.zero === true)
25969
- return 0;
25970
- if (this.symbolDefinition?.positive === true)
25971
- return 1;
25972
- if (this.symbolDefinition?.negative === true)
25973
- return -1;
26024
+ const def = this.symbolDefinition;
26025
+ if (def) {
26026
+ if (def.zero === true)
26027
+ return 0;
26028
+ if (def.positive === true)
26029
+ return 1;
26030
+ if (def.negative === true)
26031
+ return -1;
26032
+ }
25974
26033
  return undefined;
25975
26034
  }
25976
26035
  has(x) {
@@ -25993,15 +26052,22 @@ class BoxedSymbol extends AbstractBoxedExpression {
25993
26052
  return null;
25994
26053
  }
25995
26054
  isEqual(rhs) {
26055
+ if (!this.isCanonical)
26056
+ return this.canonical.isEqual(rhs);
26057
+ rhs = rhs.canonical;
26058
+ // Boxed Identity
25996
26059
  if (this === rhs)
25997
26060
  return true;
25998
26061
  // Idempotency ('x' = 'x')
25999
26062
  if (rhs.symbol !== null)
26000
26063
  return rhs.symbol === this._name;
26001
26064
  // Mathematical/numeric equality
26002
- const val = this.symbolDefinition?.value;
26003
- if (val)
26004
- return val.isEqual(rhs);
26065
+ const lhsVal = this.symbolDefinition?.value?.numericValue;
26066
+ if (lhsVal) {
26067
+ const rhsVal = rhs.numericValue;
26068
+ if (rhsVal)
26069
+ return lhsVal.isEqual(rhsVal);
26070
+ }
26005
26071
  if (rhs.isZero) {
26006
26072
  if (this.isZero)
26007
26073
  return true;
@@ -26012,6 +26078,8 @@ class BoxedSymbol extends AbstractBoxedExpression {
26012
26078
  return false;
26013
26079
  // @todo could test other contradictory properties: prime vs composite, etc...
26014
26080
  // Direct assumptions
26081
+ if (this.engine.ask(['Equal', this, rhs]).length > 0)
26082
+ return true;
26015
26083
  if (this.engine.ask(['NotEqual', this, rhs]).length > 0)
26016
26084
  return false;
26017
26085
  //@todo: could use range
@@ -26982,15 +27050,22 @@ class ComputeEngine {
26982
27050
  return this.divide(ops[0] ?? this.error('missing'), ops[1] ?? this.error('missing'), metadata);
26983
27051
  if (head === 'Power')
26984
27052
  return this.power(ops[0] ?? this.error('missing'), ops[1] ?? this.error('missing'), metadata);
26985
- const result = new BoxedFunction(this, head, ops, metadata);
26986
- return result.canonical;
27053
+ return makeCanonicalFunction(this, head, ops, { metadata });
26987
27054
  }
26988
27055
  /** @internal */
26989
27056
  _fn(head, ops, metadata) {
26990
27057
  // if (!ops.every((x) => x.isCanonical)) debugger;
26991
- const result = new BoxedFunction(this, head, ops, metadata);
26992
- result.isCanonical = true;
26993
- return result;
27058
+ // return makeCanonicalFunction(this, head, ops, {
27059
+ // metadata,
27060
+ // canonical: true,
27061
+ // });
27062
+ return new BoxedFunction(this, head, ops, {
27063
+ metadata,
27064
+ canonical: true,
27065
+ def: typeof head === 'string' && this.context
27066
+ ? this.lookupFunction(head, this.context)
27067
+ : undefined,
27068
+ });
26994
27069
  }
26995
27070
  error(message, where) {
26996
27071
  if (where && Array.isArray(where) && where[0] === 'Latex') {
@@ -27321,6 +27396,6 @@ class ComputeEngine {
27321
27396
  }
27322
27397
 
27323
27398
  // This file is the root of the `compute-engine` package
27324
- const version = '0.7.0';
27399
+ const version = '0.8.0';
27325
27400
 
27326
27401
  export { ComputeEngine, getVars, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };