@cortex-js/compute-engine 0.11.0 → 0.12.1

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 (86) hide show
  1. package/dist/compute-engine.esm.js +1607 -1357
  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/common/utils.d.ts +1 -1
  10. package/dist/types/compute-engine/assume.d.ts +1 -1
  11. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +3 -2
  12. package/dist/types/compute-engine/boxed-expression/box.d.ts +2 -2
  13. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  14. package/dist/types/compute-engine/boxed-expression/boxed-domain.d.ts +1 -1
  15. package/dist/types/compute-engine/boxed-expression/boxed-function-definition.d.ts +1 -1
  16. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  17. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  18. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  19. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  20. package/dist/types/compute-engine/boxed-expression/boxed-symbol-definition.d.ts +1 -1
  21. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  22. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  23. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  24. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  25. package/dist/types/compute-engine/boxed-expression/utils.d.ts +8 -3
  26. package/dist/types/compute-engine/boxed-expression/validate.d.ts +4 -3
  27. package/dist/types/compute-engine/compute-engine.d.ts +15 -32
  28. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  29. package/dist/types/compute-engine/domain-utils.d.ts +3 -3
  30. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  31. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  32. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  33. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  34. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-inequalities.d.ts +1 -1
  35. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  36. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  37. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  38. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  39. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  40. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  41. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  42. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  43. package/dist/types/compute-engine/latex-syntax/public.d.ts +2 -2
  44. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -2
  45. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  46. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  47. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  48. package/dist/types/compute-engine/library/arithmetic-add.d.ts +1 -1
  49. package/dist/types/compute-engine/library/arithmetic-divide.d.ts +1 -1
  50. package/dist/types/compute-engine/library/arithmetic-multiply.d.ts +1 -1
  51. package/dist/types/compute-engine/library/arithmetic-power.d.ts +2 -4
  52. package/dist/types/compute-engine/library/arithmetic.d.ts +2 -2
  53. package/dist/types/compute-engine/library/calculus.d.ts +2 -2
  54. package/dist/types/compute-engine/library/collections.d.ts +2 -2
  55. package/dist/types/compute-engine/library/core.d.ts +2 -2
  56. package/dist/types/compute-engine/library/domains.d.ts +1 -1
  57. package/dist/types/compute-engine/library/library.d.ts +5 -5
  58. package/dist/types/compute-engine/library/logic.d.ts +2 -2
  59. package/dist/types/compute-engine/library/polynomials.d.ts +2 -2
  60. package/dist/types/compute-engine/library/random-expression.d.ts +2 -0
  61. package/dist/types/compute-engine/library/relational-operator.d.ts +2 -2
  62. package/dist/types/compute-engine/library/sets.d.ts +2 -2
  63. package/dist/types/compute-engine/library/trigonometry.d.ts +2 -2
  64. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  65. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +12 -0
  66. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -9
  67. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  68. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  69. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  70. package/dist/types/compute-engine/numerics/rationals.d.ts +10 -12
  71. package/dist/types/compute-engine/public.d.ts +31 -47
  72. package/dist/types/compute-engine/rules.d.ts +1 -1
  73. package/dist/types/compute-engine/simplify-rules.d.ts +1 -1
  74. package/dist/types/compute-engine/solve.d.ts +1 -1
  75. package/dist/types/compute-engine/symbolic/expand.d.ts +1 -1
  76. package/dist/types/compute-engine/symbolic/flatten.d.ts +3 -2
  77. package/dist/types/compute-engine/symbolic/negate.d.ts +4 -6
  78. package/dist/types/compute-engine/symbolic/polynomials.d.ts +1 -1
  79. package/dist/types/compute-engine/symbolic/product.d.ts +1 -1
  80. package/dist/types/compute-engine/symbolic/sum.d.ts +3 -2
  81. package/dist/types/compute-engine/symbolic/utils.d.ts +1 -1
  82. package/dist/types/compute-engine.d.ts +2 -3
  83. package/dist/types/math-json/math-json-format.d.ts +1 -1
  84. package/dist/types/math-json/utils.d.ts +2 -1
  85. package/dist/types/math-json.d.ts +2 -3
  86. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- /** CortexJS Compute Engine 0.11.0 */
1
+ /** CortexJS Compute Engine 0.12.1 */
2
2
  /** @internal */
3
3
  function isSymbolEntry(entry) {
4
4
  return !('kind' in entry) || entry.kind === 'symbol';
@@ -6801,6 +6801,17 @@ const DEFINITIONS_ALGEBRA = [
6801
6801
  },
6802
6802
  ];
6803
6803
 
6804
+ function isNumberExpression(expr) {
6805
+ if (expr === null)
6806
+ return false;
6807
+ if (typeof expr === 'number')
6808
+ return true;
6809
+ if (isNumberObject(expr))
6810
+ return true;
6811
+ if (typeof expr === 'string' && /^[+-]?[0-9]/.test(expr))
6812
+ return true;
6813
+ return false;
6814
+ }
6804
6815
  function isNumberObject(expr) {
6805
6816
  return expr !== null && typeof expr === 'object' && 'num' in expr;
6806
6817
  }
@@ -7158,7 +7169,7 @@ function countLeaves(expr) {
7158
7169
  return 0;
7159
7170
  if (typeof expr === 'number' || typeof expr === 'string')
7160
7171
  return 1;
7161
- if (isNumberObject(expr) || isSymbolObject(expr) || isStringObject(expr))
7172
+ if (isNumberExpression(expr) || isSymbolObject(expr) || isStringObject(expr))
7162
7173
  return 1;
7163
7174
  if (Array.isArray(expr))
7164
7175
  return countFunctionLeaves(expr);
@@ -7462,7 +7473,7 @@ function serializeMultiply(serializer, expr) {
7462
7473
  //
7463
7474
  // 1. Should the terms be separated by an explicit 'Multiply'?
7464
7475
  //
7465
- if (typeof arg === 'number' || isNumberObject(arg)) {
7476
+ if (isNumberExpression(arg)) {
7466
7477
  term = serializer.serialize(arg);
7467
7478
  if (term === '-1' && !result) {
7468
7479
  result = '';
@@ -8349,6 +8360,26 @@ const DEFINITIONS_CORE = [
8349
8360
  return `\\mathbf{${serializer.serialize(op(expr, 1))}}`;
8350
8361
  },
8351
8362
  },
8363
+ {
8364
+ trigger: ['\\mathtip'],
8365
+ parse: (parser) => {
8366
+ const op1 = parser.matchRequiredLatexArgument();
8367
+ parser.matchRequiredLatexArgument();
8368
+ return op1;
8369
+ },
8370
+ },
8371
+ {
8372
+ trigger: ['\\texttip'],
8373
+ parse: (parser) => {
8374
+ const op1 = parser.matchRequiredLatexArgument();
8375
+ parser.matchRequiredLatexArgument();
8376
+ return op1;
8377
+ },
8378
+ },
8379
+ {
8380
+ trigger: ['\\error'],
8381
+ parse: (parser) => parser.matchRequiredLatexArgument(),
8382
+ },
8352
8383
  {
8353
8384
  name: 'Error',
8354
8385
  serialize: (serializer, expr) => {
@@ -8360,6 +8391,9 @@ const DEFINITIONS_CORE = [
8360
8391
  if (code === 'incompatible-domain') {
8361
8392
  return `\\mathtip{\\error{${where}}}{\\in ${serializer.serialize(op(op1, 3))}\\notin ${serializer.serialize(op(op1, 2))}}`;
8362
8393
  }
8394
+ if (code === 'missing') {
8395
+ return `\\mathtip{\\error{${where}}}{${serializer.serialize(op(op1, 2))}\\text{ missing}}`;
8396
+ }
8363
8397
  if (typeof code === 'string')
8364
8398
  return `\\error{${where}}`;
8365
8399
  return `\\error{${where}}`;
@@ -12923,7 +12957,7 @@ class _Parser {
12923
12957
  // - 123.456 = significand
12924
12958
  // - 123 = wholePart
12925
12959
  // - 456 = fractionalPart
12926
- // - 79 = exponent
12960
+ // - 78 = exponent
12927
12961
  //
12928
12962
  // Avoid using mantissa which has several definitions and is ambiguous.
12929
12963
  /**
@@ -12934,17 +12968,19 @@ class _Parser {
12934
12968
  function formatFractionalPart(m, options) {
12935
12969
  const originalLength = m.length;
12936
12970
  const originalM = m;
12937
- // The last digit may have been rounded off, if it exceeds the precision,
12938
- // which could throw off the repeating pattern detection. Ignore it.
12939
- m = m.slice(0, -1);
12940
- for (let i = 0; i < m.length - 16; i++) {
12941
- // Offset is the part of the fractional part that is not repeating
12942
- const offset = m.substring(0, i);
12943
- // Try to find a repeating pattern of length j
12944
- for (let j = 0; j < 17; j++) {
12945
- const cycle = m.substring(i, i + j + 1);
12946
- const times = Math.floor((m.length - offset.length) / cycle.length);
12947
- if (times > 1) {
12971
+ if (options.beginRepeatingDigits && options.endRepeatingDigits) {
12972
+ // The last digit may have been rounded off, if it exceeds the precision,
12973
+ // which could throw off the repeating pattern detection. Ignore it.
12974
+ m = m.slice(0, -1);
12975
+ for (let i = 0; i < m.length - 16; i++) {
12976
+ // Offset is the part of the fractional part that is not repeating
12977
+ const offset = m.substring(0, i);
12978
+ // Try to find a repeating pattern of length j
12979
+ for (let j = 0; j < 17; j++) {
12980
+ const cycle = m.substring(i, i + j + 1);
12981
+ const times = Math.floor((m.length - offset.length) / cycle.length);
12982
+ if (times <= 3)
12983
+ break;
12948
12984
  if ((offset + cycle.repeat(times + 1)).startsWith(m)) {
12949
12985
  // We've found a repeating pattern!
12950
12986
  if (cycle === '0') {
@@ -12965,9 +13001,8 @@ function formatFractionalPart(m, options) {
12965
13001
  // Display a truncation marker.
12966
13002
  const extraDigits = originalLength > options.precision - 1;
12967
13003
  m = originalM;
12968
- if (extraDigits) {
13004
+ if (extraDigits)
12969
13005
  m = m.substring(0, options.precision - 1);
12970
- }
12971
13006
  // Insert group separators if necessary
12972
13007
  if (options.groupSeparator) {
12973
13008
  m = m.replace(/(\d{3})/g, '$1' + options.groupSeparator);
@@ -13012,9 +13047,12 @@ function serializeNumber(expr, options) {
13012
13047
  return options.negativeInfinity;
13013
13048
  else if (Number.isNaN(num))
13014
13049
  return options.notANumber;
13050
+ let result = undefined;
13015
13051
  if (options.notation === 'engineering')
13016
- return serializeEngineeringNotationNumber(num, options);
13017
- return serializeAutoNotationNumber(num.toString(), options);
13052
+ result = serializeScientificNotationNumber(num.toExponential(), options, 3);
13053
+ else if (options.notation === 'scientific')
13054
+ result = serializeScientificNotationNumber(num.toExponential(), options);
13055
+ return result ?? serializeAutoNotationNumber(num.toString(), options);
13018
13056
  }
13019
13057
  num = num.toLowerCase().replace(/[\u0009-\u000d\u0020\u00a0]/g, '');
13020
13058
  if (num === 'infinity' || num === '+infinity')
@@ -13027,6 +13065,7 @@ function serializeNumber(expr, options) {
13027
13065
  return '';
13028
13066
  num = num.replace(/[nd]$/, '');
13029
13067
  // Do we have repeating digits?
13068
+ // If so, "unrepeat" (expand) them
13030
13069
  if (/\([0-9]+\)/.test(num)) {
13031
13070
  const [_, body, repeat, trail] = num.match(/(.+)\(([0-9]+)\)(.*)$/) ?? [];
13032
13071
  num =
@@ -13046,128 +13085,130 @@ function serializeNumber(expr, options) {
13046
13085
  while (num[0] === '0')
13047
13086
  num = num.substring(1);
13048
13087
  if (num.length === 0)
13049
- return sign + '0';
13050
- if (num[0] === '.')
13051
- num = '0' + num;
13052
- let exponent = '';
13053
- if (num.indexOf('.') >= 0) {
13054
- const m = num.match(/(\d*)\.(\d*)([e|E]([-+]?[0-9]*))?/);
13055
- if (!m)
13056
- return '';
13057
- const base = m[1];
13058
- const fractionalPart = m[2];
13059
- exponent = m[4] ?? '';
13060
- if (base === '0') {
13061
- let p = 0; // Index of the first non-zero digit after the decimal
13062
- while (fractionalPart[p] === '0' && p < fractionalPart.length)
13063
- p += 1;
13064
- let r = '';
13065
- if (p <= 4) {
13066
- r = '0' + options.decimalMarker;
13067
- r += fractionalPart.substring(0, p);
13068
- r += formatFractionalPart(num.substring(r.length), options);
13069
- }
13070
- else if (p + 1 >= options.precision) {
13071
- r = '0';
13072
- sign = '';
13088
+ num = sign + '0';
13089
+ else if (num[0] === '.')
13090
+ num = sign + '0' + num;
13091
+ let result = undefined;
13092
+ if (options.notation === 'engineering')
13093
+ result = serializeScientificNotationNumber(num, options, 3);
13094
+ else if (options.notation === 'scientific')
13095
+ result = serializeScientificNotationNumber(num, options);
13096
+ return sign + (result ?? serializeAutoNotationNumber(num, options));
13097
+ }
13098
+ /**
13099
+ * Scientific notation has:
13100
+ * - a whole part [1..9]
13101
+ * - an optional fractional part (many digits)
13102
+ * - an optional exponent
13103
+ * @param valString
13104
+ * @param options
13105
+ * @returns
13106
+ */
13107
+ function serializeScientificNotationNumber(valString, options, expMultiple = 1) {
13108
+ // For '7' returns '7e+0'
13109
+ let m = valString.match(/^(.*)[e|E]([-+]?[0-9]+)$/);
13110
+ if (!m) {
13111
+ // Valstring wasn't in exponential form, convert it.
13112
+ // Remove the sign
13113
+ let sign = '';
13114
+ if (valString[0] === '-') {
13115
+ sign = '-';
13116
+ valString = valString.substring(1);
13117
+ }
13118
+ else if (valString[0] === '+') {
13119
+ valString = valString.substring(1);
13120
+ }
13121
+ if (valString.indexOf('.') < 0) {
13122
+ if (valString.length === 1) {
13123
+ valString = sign + valString + 'e+0';
13073
13124
  }
13074
13125
  else {
13075
- r = num[p];
13076
- const f = formatFractionalPart(num.substring(p + 1), options);
13077
- if (f) {
13078
- r += options.decimalMarker + f;
13079
- }
13126
+ // A long integer, convert to exponential form
13127
+ valString =
13128
+ sign +
13129
+ valString[0] +
13130
+ '.' +
13131
+ valString.slice(1) +
13132
+ 'e+' +
13133
+ (valString.length - 1).toString();
13080
13134
  }
13081
- if (r !== '0') {
13082
- if (num.length - 1 > options.precision &&
13083
- !(options.endRepeatingDigits && r.endsWith(options.endRepeatingDigits)) &&
13084
- options.truncationMarker &&
13085
- !r.endsWith(options.truncationMarker)) {
13086
- r += options.truncationMarker;
13087
- }
13088
- if (p > 4) {
13089
- r +=
13090
- options.exponentProduct +
13091
- formatExponent((1 - p).toString(), options);
13092
- }
13093
- }
13094
- num = r;
13095
13135
  }
13096
13136
  else {
13097
- num = base.replace(/\B(?=(\d{3})+(?!\d))/g, options.groupSeparator);
13098
- const f = formatFractionalPart(fractionalPart, options);
13099
- if (f) {
13100
- num += options.decimalMarker + f;
13101
- // if (num.length - 1 > config.precision && !num.endsWith('}') && !num.endsWith('\\ldots')) {
13102
- // num += '\\ldots';
13103
- // }
13104
- }
13105
- }
13106
- }
13107
- else if (num.length > options.precision) {
13108
- const len = num.length;
13109
- if (len > options.avoidExponentsInRange[1]) {
13110
- let r = num[0];
13111
- const f = formatFractionalPart(num.substring(1), options);
13112
- if (f) {
13113
- r += options.decimalMarker + f;
13114
- if (options.truncationMarker && !r.endsWith(options.truncationMarker)) {
13115
- if (options.endRepeatingDigits &&
13116
- !r.endsWith(options.endRepeatingDigits)) {
13117
- r += options.truncationMarker;
13118
- }
13119
- }
13120
- }
13121
- if (r !== '1') {
13122
- r += options.exponentProduct;
13137
+ // A decimal number, convert to exponential form
13138
+ // eslint-disable-next-line prefer-const
13139
+ let [_, whole, fraction] = valString.match(/^(.*)\.(.*)$/);
13140
+ if (!fraction)
13141
+ fraction = '';
13142
+ while (whole.startsWith('0'))
13143
+ whole = whole.substring(1);
13144
+ if (!whole) {
13145
+ // .123 -> 0.123e+0
13146
+ // .0123 -> 0.0123e+0
13147
+ valString = sign + '0.' + fraction + 'e+0';
13123
13148
  }
13124
13149
  else {
13125
- r = '';
13150
+ // 1.234 -> 1.234e+0
13151
+ // 12.345 -> 1.2345e+1
13152
+ valString =
13153
+ sign +
13154
+ whole[0] +
13155
+ '.' +
13156
+ whole.slice(1) +
13157
+ fraction +
13158
+ 'e+' +
13159
+ (whole.length - 1).toString();
13126
13160
  }
13127
- num = r + formatExponent((len - 1).toString(), options);
13128
13161
  }
13162
+ m = valString.match(/^(.*)[e|E]([-+]?[0-9]+)$/);
13163
+ }
13164
+ console.assert(m);
13165
+ if (!m)
13166
+ return serializeAutoNotationNumber(valString, options);
13167
+ let exponent = parseInt(m[2]);
13168
+ let mantissa = m[1];
13169
+ if (Math.abs(exponent) % expMultiple !== 0) {
13170
+ // Need to adjust the exponent and values, e.g. for engineering notation
13171
+ const adjust = exponent > 0
13172
+ ? exponent % expMultiple
13173
+ : -((expMultiple + exponent) % expMultiple);
13174
+ exponent = exponent >= 0 ? exponent - adjust : exponent + adjust;
13175
+ // Don't use numeric operations, which may introduce artifacting
13176
+ // eslint-disable-next-line prefer-const
13177
+ let [_, whole, fraction] = mantissa.match(/^(.*)\.(.*)$/) ?? [
13178
+ '',
13179
+ mantissa,
13180
+ '',
13181
+ ];
13182
+ mantissa =
13183
+ whole +
13184
+ (fraction + '00000000000000000').slice(0, Math.abs(adjust)) +
13185
+ '.' +
13186
+ fraction.slice(Math.abs(adjust));
13187
+ }
13188
+ // Is the exponent in a range to be avoided?
13189
+ const avoid = options.avoidExponentsInRange;
13190
+ if (avoid && exponent >= avoid[0] && exponent <= avoid[1])
13191
+ return undefined;
13192
+ let fractionalPart = '';
13193
+ let wholePart = mantissa;
13194
+ m = wholePart.match(/^(.*)\.(.*)$/);
13195
+ if (m) {
13196
+ wholePart = m[1];
13197
+ fractionalPart = m[2];
13129
13198
  }
13130
- else {
13131
- const m = num.match(/([0-9]*)\.?([0-9]*)([e|E]([-+]?[0-9]+))?/);
13132
- if (m) {
13133
- num = m[1];
13134
- if (m[2])
13135
- num += options.decimalMarker + m[2];
13136
- exponent = m[4] ?? '';
13137
- }
13138
- num = num.replace(/\B(?=(\d{3})+(?!\d))/g, options.groupSeparator);
13139
- }
13140
- const exponentString = formatExponent(exponent, options);
13141
- if (num === '1' && exponentString)
13142
- return sign + exponentString;
13143
- if (exponentString)
13144
- num = num + options.exponentProduct + exponentString;
13145
- return sign + num;
13146
- }
13147
- function serializeEngineeringNotationNumber(value, options) {
13148
- if (value === 0)
13149
- return '0';
13150
- // Ensure the exponent is a multiple of 3
13151
- const y = Math.abs(value);
13152
- let exponent = Math.round(Math.log10(y));
13153
- exponent = exponent - (exponent % 3);
13154
- if (y > Math.pow(10, options.avoidExponentsInRange[0]) &&
13155
- y < Math.pow(10, options.avoidExponentsInRange[1]))
13156
- exponent = 0;
13157
- const significand = y / Math.pow(10, exponent);
13158
- let significandString = '';
13159
- const m = significand.toString().match(/^(.*)\.(.*)$/);
13160
- if (m?.[1] && m[2]) {
13161
- significandString = m[1] + options.decimalMarker + m[2];
13162
- }
13199
+ const expString = exponent !== 0 ? formatExponent(Number(exponent).toString(), options) : '';
13163
13200
  if (options.groupSeparator) {
13164
- significandString = formatFractionalPart(significand.toExponential(), options);
13165
- }
13166
- let exponentString = '';
13167
- if (exponent !== 0) {
13168
- exponentString = formatExponent(exponent.toString(), options);
13201
+ wholePart = wholePart.replace(/\B(?=(\d{3})+(?!\d))/g, options.groupSeparator);
13202
+ fractionalPart = formatFractionalPart(fractionalPart, options);
13169
13203
  }
13170
- return (value < 0 ? '-' : '') + significandString + exponentString;
13204
+ if (fractionalPart)
13205
+ fractionalPart = options.decimalMarker + fractionalPart;
13206
+ // @todo: does not respect the options.precision option
13207
+ if (!expString)
13208
+ return wholePart + fractionalPart;
13209
+ if (wholePart === '1' && !fractionalPart)
13210
+ return expString;
13211
+ return wholePart + fractionalPart + options.exponentProduct + expString;
13171
13212
  }
13172
13213
  function serializeAutoNotationNumber(valString, options) {
13173
13214
  let m = valString.match(/^(.*)[e|E]([-+]?[0-9]+)$/i);
@@ -13322,9 +13363,7 @@ class Serializer {
13322
13363
  const exprStr = this.serialize(expr);
13323
13364
  if (head(expr) === 'Delimiter' && nops(expr) === 1)
13324
13365
  return exprStr;
13325
- if (typeof expr !== 'number' &&
13326
- !isNumberObject(expr) &&
13327
- !/(^(.|\\[a-zA-Z*]+))$/.test(exprStr)) {
13366
+ if (!isNumberExpression(expr) && !/(^(.|\\[a-zA-Z*]+))$/.test(exprStr)) {
13328
13367
  // It's a long expression, wrap it
13329
13368
  return this.wrapString(exprStr, this.options.groupStyle(expr, this.level + 1));
13330
13369
  }
@@ -13970,8 +14009,10 @@ function primeFactors$1(n) {
13970
14009
  }
13971
14010
  }
13972
14011
  }
13973
- console.assert(result[n] === undefined);
13974
- result[n] = 1;
14012
+ if (result[n] !== undefined)
14013
+ result[n] += 1;
14014
+ else
14015
+ result[n] = 1;
13975
14016
  return result;
13976
14017
  }
13977
14018
  /* @todo Consider https://cp-algorithms.com/algebra/factorization.html */
@@ -14140,7 +14181,7 @@ function asFloat(expr) {
14140
14181
  const [n, d] = num;
14141
14182
  if (typeof n === 'number' && typeof d === 'number')
14142
14183
  return n / d;
14143
- return n.div(d).toNumber();
14184
+ return Number(n) / Number(d);
14144
14185
  }
14145
14186
  console.assert(!(num instanceof Complex) || num.im !== 0);
14146
14187
  return null;
@@ -14157,7 +14198,7 @@ function asBignum(expr) {
14157
14198
  const [n, d] = num;
14158
14199
  if (typeof n === 'number' && typeof d === 'number')
14159
14200
  return expr.engine.bignum(n / d);
14160
- return n.div(d);
14201
+ return expr.engine.bignum(n).div(d.toString());
14161
14202
  }
14162
14203
  console.assert(!(num instanceof Complex) || num.im !== 0);
14163
14204
  return null;
@@ -14190,7 +14231,7 @@ function asSmallInteger(expr) {
14190
14231
  if (typeof n === 'number' && typeof d === 'number')
14191
14232
  v = n / d;
14192
14233
  else
14193
- v = n.div(d).toNumber();
14234
+ v = Number(n) / Number(d);
14194
14235
  if (Number.isInteger(v) && v >= -SMALL_INTEGER && v <= SMALL_INTEGER)
14195
14236
  return v;
14196
14237
  return null;
@@ -14209,6 +14250,124 @@ function chop(n, tolerance) {
14209
14250
  return n;
14210
14251
  }
14211
14252
 
14253
+ function bigint(a) {
14254
+ if (typeof a === 'bigint')
14255
+ return a;
14256
+ if (a instanceof Decimal)
14257
+ return bigint(a.toString());
14258
+ // BigInt constructor does not deal well with e.g. `1e30` or `1.2e5`
14259
+ let s = a.toString();
14260
+ const m = s.match(/([^\.]+)(?:\.([0-9]+))?e(.+)$/);
14261
+ if (m) {
14262
+ s =
14263
+ m[1] +
14264
+ (m[2] ?? '') +
14265
+ '0'.repeat(parseInt(m[3]) - (m[2] ? m[2].length : 0));
14266
+ }
14267
+ return BigInt(s);
14268
+ }
14269
+ function gcd(a, b) {
14270
+ while (b !== BigInt(0))
14271
+ [a, b] = [b, a % b];
14272
+ return a < 0 ? -a : a;
14273
+ }
14274
+ // Difference between primes from 7 to 31
14275
+ const PRIME_WHEEL_INC = [
14276
+ BigInt(4),
14277
+ BigInt(2),
14278
+ BigInt(4),
14279
+ BigInt(2),
14280
+ BigInt(4),
14281
+ BigInt(6),
14282
+ BigInt(2),
14283
+ BigInt(6),
14284
+ ];
14285
+ function primeFactors(d) {
14286
+ if (d < Number.MAX_SAFE_INTEGER) {
14287
+ const factors = primeFactors$1(Number(d));
14288
+ const result = new Map();
14289
+ for (const f of Object.keys(factors))
14290
+ result.set(bigint(f), factors[f]);
14291
+ return result;
14292
+ }
14293
+ //https:rosettacode.org/wiki/Prime_decomposition#JavaScript
14294
+ let n = d;
14295
+ const result = new Map();
14296
+ // Wheel factorization
14297
+ // @todo: see https://github.com/Fairglow/prime-factor/blob/main/src/lib.rs
14298
+ let count2 = 0;
14299
+ let count3 = 0;
14300
+ let count5 = 0;
14301
+ let k = BigInt(10);
14302
+ while (n % k === BigInt(0)) {
14303
+ count2 += 1;
14304
+ count5 += 1;
14305
+ n = n / k;
14306
+ }
14307
+ k = BigInt(5);
14308
+ while (n % k === BigInt(0)) {
14309
+ count5 += 1;
14310
+ n = n / k;
14311
+ }
14312
+ k = BigInt(3);
14313
+ while (n % k === BigInt(0)) {
14314
+ count3 += 1;
14315
+ n = n / k;
14316
+ }
14317
+ k = BigInt(2);
14318
+ while (n % k === BigInt(0)) {
14319
+ count2 += 1;
14320
+ n = n / k;
14321
+ }
14322
+ if (count2 > 0)
14323
+ result.set('2', count2);
14324
+ if (count3 > 0)
14325
+ result.set('3', count3);
14326
+ if (count5 > 0)
14327
+ result.set('5', count5);
14328
+ k = BigInt(7);
14329
+ let kIndex = '';
14330
+ let i = 0;
14331
+ while (k * k < n) {
14332
+ if (n % k === BigInt(0)) {
14333
+ if (!kIndex)
14334
+ kIndex = k.toString();
14335
+ result.set(kIndex, (result.get(kIndex) ?? 0) + 1);
14336
+ n = n / k;
14337
+ }
14338
+ else {
14339
+ k = k + PRIME_WHEEL_INC[i];
14340
+ kIndex = '';
14341
+ i = i < 7 ? i + 1 : 0;
14342
+ }
14343
+ }
14344
+ if (n !== BigInt(1))
14345
+ result.set(n.toString(), (result.get(n.toString()) ?? 0) + 1);
14346
+ const r = new Map();
14347
+ for (const [k, v] of result)
14348
+ r.set(bigint(k), v);
14349
+ return r;
14350
+ }
14351
+ /** Return `[factor, root]` such that
14352
+ * pow(n, 1/exponent) = factor * pow(root, 1/exponent)
14353
+ *
14354
+ * factorPower(75, 2) -> [5, 3] = 5^2 * 3
14355
+ *
14356
+ */
14357
+ function factorPower(n, exponent) {
14358
+ // @todo: handle negative n
14359
+ const factors = primeFactors(n);
14360
+ let f = BigInt(1);
14361
+ let r = BigInt(1);
14362
+ const exp = bigint(exponent);
14363
+ for (const [k, v] of factors) {
14364
+ const v2 = bigint(v);
14365
+ f = f * k ** (v2 / exp);
14366
+ r = r * k ** (v2 % exp);
14367
+ }
14368
+ return [f, r];
14369
+ }
14370
+
14212
14371
  function isLatexString(s) {
14213
14372
  if (typeof s === 'string')
14214
14373
  return s.startsWith('$') && s.endsWith('$');
@@ -14250,37 +14409,41 @@ function getImaginaryCoef(expr) {
14250
14409
  /**
14251
14410
  * Return the free symbols in the expression, recursively.
14252
14411
  * A variable, or free symbol, is a symbol that is not bound to a value.
14412
+ * Note: do not use `isFree`: it has a side effect of creating a definition
14413
+ * if one does not exist, and we want to avoid that. For example, `assume()`
14414
+ * relies on `expr.freeVars` *not* creating a definition.
14253
14415
  */
14254
- function getVars(expr) {
14416
+ function getFreeVars(expr, set) {
14255
14417
  if (expr.symbol) {
14256
- const def = expr.symbolDefinition;
14257
- return def?.constant ? [] : [expr.symbol];
14418
+ const def = expr.engine.lookupSymbol(expr.symbol);
14419
+ if (def?.value === undefined)
14420
+ set.add(expr.symbol);
14421
+ return;
14258
14422
  }
14259
14423
  if (!expr.ops && !expr.keys)
14260
- return [];
14261
- const result = [];
14424
+ return;
14262
14425
  if (expr.ops)
14263
14426
  for (const op of expr.ops)
14264
- result.push(...getVars(op));
14427
+ getFreeVars(op, set);
14265
14428
  if (expr.keys)
14266
14429
  for (const key of expr.keys)
14267
- result.push(...getVars(expr.getKey(key)));
14268
- return result;
14430
+ getFreeVars(expr.getKey(key), set);
14431
+ return;
14269
14432
  }
14270
14433
  function getSymbols(expr, set) {
14271
14434
  if (expr.symbol) {
14272
14435
  set.add(expr.symbol);
14273
- return set;
14436
+ return;
14274
14437
  }
14275
14438
  if (!expr.ops && !expr.keys)
14276
- return set;
14439
+ return;
14277
14440
  if (expr.ops)
14278
14441
  for (const op of expr.ops)
14279
14442
  getSymbols(op, set);
14280
14443
  if (expr.keys)
14281
14444
  for (const key of expr.keys)
14282
14445
  getSymbols(expr.getKey(key), set);
14283
- return set;
14446
+ return;
14284
14447
  }
14285
14448
  function getSubexpressions(expr, head) {
14286
14449
  const result = !head || expr.head === head ? [expr] : [];
@@ -14313,20 +14476,14 @@ function hashCode(s) {
14313
14476
  hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; // | 0 to convert to 32-bit int
14314
14477
  return Math.abs(hash);
14315
14478
  }
14316
- /**
14317
- * If `expr` is a number, return it as a Decimal (it might be
14318
- * in the machine value range or not). Use `isInMachineRange()` to check.
14319
- *
14320
- * Use this instead of `machineValue()` when possible, as `machineValue` will
14321
- * truncate bignums to machine numbers
14322
- */
14323
- function bignumValue(ce, expr) {
14479
+ function bigintValue(ce, expr) {
14324
14480
  if (expr === null || expr === undefined)
14325
14481
  return null;
14326
14482
  if (typeof expr === 'number')
14327
- return ce.bignum(expr);
14328
- if (isNumberObject(expr)) {
14329
- let s = expr.num
14483
+ return Number.isInteger(expr) ? bigint(expr) : null;
14484
+ if (isNumberExpression(expr)) {
14485
+ const num = isNumberObject(expr) ? expr.num.toString() : expr;
14486
+ let s = num
14330
14487
  .toLowerCase()
14331
14488
  .replace(/[nd]$/g, '')
14332
14489
  .replace(/[\u0009-\u000d\u0020\u00a0]/g, '');
@@ -14338,14 +14495,26 @@ function bignumValue(ce, expr) {
14338
14495
  (trail ?? '');
14339
14496
  }
14340
14497
  if (s === 'nan')
14341
- return ce.bignum('NaN');
14498
+ return null;
14342
14499
  if (s === 'infinity' || s === '+infinity')
14343
- return ce.bignum('+Infinity');
14500
+ return null;
14344
14501
  if (s === '-infinity')
14345
- return ce.bignum('-Infinity');
14346
- return ce.bignum(s);
14502
+ return null;
14503
+ if (s.includes('.'))
14504
+ return null;
14505
+ return bigint(s);
14347
14506
  }
14348
14507
  return null;
14508
+ }
14509
+ function asBigint(expr) {
14510
+ const num = expr.numericValue;
14511
+ if (num === null)
14512
+ return null;
14513
+ if (typeof num === 'number' && Number.isInteger(num))
14514
+ return bigint(num);
14515
+ if (num instanceof Decimal && num.isInteger())
14516
+ return bigint(num);
14517
+ return null;
14349
14518
  }
14350
14519
 
14351
14520
  /**
@@ -14500,586 +14669,148 @@ function getWildcardName(s) {
14500
14669
  // @todo: ['Repeated',...] : repeating match
14501
14670
  // @todo _x:Head or _x:RealNumber
14502
14671
 
14672
+ function isRational(x) {
14673
+ return x !== null && Array.isArray(x);
14674
+ }
14675
+ function isMachineRational(x) {
14676
+ return x !== null && Array.isArray(x) && typeof x[0] === 'number';
14677
+ }
14678
+ function isBigRational(x) {
14679
+ return x !== null && Array.isArray(x) && typeof x[0] === 'bigint';
14680
+ }
14681
+ function isRationalZero(x) {
14682
+ // Note '==' to convert bigint to number
14683
+ return x[0] == 0;
14684
+ }
14685
+ function isRationalOne(x) {
14686
+ return x[0] === x[1];
14687
+ }
14688
+ function isRationalNegativeOne(x) {
14689
+ return x[0] === -x[1];
14690
+ }
14691
+ function machineNumerator(x) {
14692
+ return Number(x[0]);
14693
+ }
14694
+ function machineDenominator(x) {
14695
+ return Number(x[1]);
14696
+ }
14697
+ function isNeg(x) {
14698
+ return x[0] < 0;
14699
+ }
14700
+ function neg(x) {
14701
+ return [-x[0], x[1]];
14702
+ }
14703
+ function inverse(x) {
14704
+ return x[0] < 0 ? [-x[1], -x[0]] : [x[1], x[0]];
14705
+ }
14706
+ function asRational(expr) {
14707
+ const num = expr.numericValue;
14708
+ if (num === null)
14709
+ return undefined;
14710
+ if (Array.isArray(num))
14711
+ return num;
14712
+ if (typeof num === 'number' && Number.isInteger(num))
14713
+ return [num, 1];
14714
+ if (num instanceof Decimal && num.isInteger())
14715
+ return [bigint(num), BigInt(1)];
14716
+ return undefined;
14717
+ }
14718
+ function asMachineRational(r) {
14719
+ return [Number(r[0]), Number(r[1])];
14720
+ }
14503
14721
  /**
14504
- * Flatten the arguments.
14505
- * If `expr` was canonical, the result it canonical.
14722
+ * Add a literal numeric value to a rational.
14723
+ * If the rational is a bignum, this is a hint to do the calculation in bignum
14724
+ * (no need to check `bignumPreferred()`).
14725
+ * @param lhs
14726
+ * @param rhs
14727
+ * @returns
14506
14728
  */
14507
- function flattenOps(ops, head) {
14508
- const result = [];
14509
- for (const arg of ops) {
14510
- if (!arg.ops || arg.head !== head)
14511
- result.push(arg);
14512
- else {
14513
- // ["f", a, ["f", b, c]] -> ["f", a, b, c]
14514
- // or ["f", ["f", a]] -> ["f", a]
14515
- result.push(...(flattenOps(arg.ops, head) ?? arg.ops));
14729
+ function add(lhs, rhs) {
14730
+ console.assert(Array.isArray(rhs) ||
14731
+ (rhs.numericValue !== null && !(rhs instanceof Complex)));
14732
+ if (Array.isArray(rhs)) {
14733
+ if (isBigRational(rhs)) {
14734
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14735
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14516
14736
  }
14517
- }
14518
- // If number of arguments didn't change, we didn't flatten
14519
- if (result.length === ops.length)
14520
- return null;
14521
- return result;
14522
- }
14523
- function flattenSequence(xs) {
14524
- const ys = [];
14525
- for (const x of xs) {
14526
- if (x.head === 'Sequence') {
14527
- if (x.ops)
14528
- ys.push(...x.ops);
14737
+ if (isBigRational(lhs)) {
14738
+ rhs = [bigint(rhs[0]), bigint(rhs[1])];
14739
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14529
14740
  }
14530
- else
14531
- ys.push(x);
14741
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14532
14742
  }
14533
- return ys;
14534
- }
14535
-
14536
- function validateArgumentCount(ce, ops, count) {
14537
- if (ops.length === count)
14538
- return ops;
14539
- const xs = [...ops.slice(0, count)];
14540
- let i = Math.min(count, ops.length);
14541
- while (i < count) {
14542
- xs.push(ce.error('missing'));
14543
- i += 1;
14743
+ let rhsNum = rhs.numericValue;
14744
+ console.assert(rhs.isInteger);
14745
+ if (rhsNum !== null && typeof rhsNum === 'number') {
14746
+ if (isMachineRational(lhs))
14747
+ return [lhs[0] + lhs[1] * rhsNum, lhs[1]];
14748
+ return [lhs[0] + lhs[1] * bigint(rhsNum), lhs[1]];
14544
14749
  }
14545
- while (i < ops.length) {
14546
- xs.push(ce.error('unexpected-argument', ops[i]));
14547
- i += 1;
14750
+ if (rhsNum instanceof Decimal) {
14751
+ if (isMachineRational(lhs))
14752
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14753
+ return [lhs[0] + lhs[1] * bigint(rhsNum.toString()), lhs[1]];
14548
14754
  }
14549
- return xs;
14550
- }
14551
- /**
14552
- * Validation of arguments is normally done by checking the signature of the
14553
- * function vs the arguments of the expression. However, we have a fastpath
14554
- * for some common operations (add, multiply, power, neg, etc...) that bypasses
14555
- * the regular checks. This is its replacements. Since all those fastpath
14556
- * functions are numeric (i.e. have numeric arguments and return a numeric
14557
- * value), we do a simple numeric check of all arguments, and verify we have
14558
- * the number of expected arguments.
14559
- */
14560
- function validateNumericArgs(ce, ops, count) {
14561
- // @fastpath
14562
- if (!ce.strict)
14563
- return ops.map((x) => ce.box(x));
14564
- let xs = [];
14565
- if (count === undefined) {
14566
- xs = ops.map((x) => ce.box(x));
14755
+ if (Array.isArray(rhsNum)) {
14756
+ if (isMachineRational(rhsNum))
14757
+ rhsNum = [bigint(rhsNum[0]), bigint(rhsNum[1])];
14758
+ if (isMachineRational(lhs))
14759
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14760
+ return [rhsNum[1] * lhs[0] + rhsNum[0] * lhs[1], rhsNum[1] * lhs[1]];
14567
14761
  }
14568
- else
14569
- for (let i = 0; i <= Math.max(count - 1, ops.length - 1); i++) {
14570
- if (i > count - 1)
14571
- xs.push(ce.error('unexpected-argument', ops[i]));
14572
- else
14573
- xs.push(ops[i] !== undefined
14574
- ? ce.box(ops[i])
14575
- : ce.error(['missing', 'Number']));
14576
- }
14577
- return flattenSequence(xs).map((op) => !op.isValid || op.isNumber
14578
- ? op
14579
- : ce.error(['incompatible-domain', 'Number', op.domain], op));
14580
- }
14581
- /** Return `null` if the `ops` match the sig. Otherwise, return an array
14582
- * of expressions indicating the mismatched arguments.
14583
- *
14584
- */
14585
- function validateSignature(sig, ops, codomain) {
14586
- const ce = sig.engine;
14587
- // @fastpath
14588
- if (!ce.strict)
14589
- return ops;
14590
- const opsDomain = ops.map((x) => x.domain);
14591
- const targetSig = ce.domain([
14592
- 'Function',
14593
- ...opsDomain,
14594
- codomain ?? 'Anything',
14595
- ]);
14596
- if (sig.isCompatible(targetSig))
14597
- return null;
14598
- //
14599
- // There was a problem:
14600
- // 1/ not enough arguments
14601
- // 2/ too many arguments
14602
- // 3/ incompatible argument domain
14603
- //
14604
- // Iterate over each arg, and replace with error expression when appropriate
14605
- //
14606
- const expectedArgs = sig.domainArgs.slice(0, -1);
14607
- const count = Math.max(expectedArgs.length, opsDomain.length);
14608
- let newOps = [];
14609
- let rest = [...ops];
14610
- for (let i = 0; i <= count - 1; i++)
14611
- [newOps, rest] = validateNextArgument(ce, expectedArgs[i], newOps, rest);
14612
- // Remove any 'Nothing' at the end
14613
- while (newOps.length > 0 && newOps[newOps.length - 1].symbol === 'Nothing')
14614
- newOps.pop();
14615
- return newOps;
14616
- }
14617
- function validateArgument(ce, arg, expect) {
14618
- if (expect === undefined)
14619
- return ce.error('unexpected-argument', arg);
14620
- if (arg === undefined)
14621
- return ce.error(['missing', expect]);
14622
- if (!arg.isValid)
14623
- return arg;
14624
- if (arg?.domain.isCompatible(ce.domain(expect)))
14625
- return arg;
14626
- return ce.error(['incompatible-domain', expect, arg.domain], arg);
14627
- }
14628
- function validateNextArgument(ce, expect, matched, ops) {
14629
- let next = ops.shift();
14630
- if (expect === undefined)
14631
- return [[...matched, ce.error('unexpected-argument', next)], ops];
14632
- if (!Array.isArray(expect)) {
14633
- if (!next)
14634
- return [[...matched, ce.error(['missing', expect])], ops];
14635
- if (!next.domain.isCompatible(ce.domain(expect))) {
14636
- return [
14637
- [
14638
- ...matched,
14639
- ce.error(['incompatible-domain', expect, next.domain], next),
14640
- ],
14641
- ops,
14642
- ];
14643
- }
14644
- return [[...matched, next], ops];
14645
- }
14646
- const ctor = expect[0];
14647
- if (next === undefined) {
14648
- //
14649
- // An expected argument is missing. Is that OK?
14650
- //
14651
- let valid = false;
14652
- if (ctor === 'Union') {
14653
- // If an `Union`, was `Nothing` an option?
14654
- for (let k = 1; k <= expect.length - 1; k++) {
14655
- if (expect[k] === 'Nothing') {
14656
- valid = true;
14657
- break;
14658
- }
14659
- }
14660
- }
14661
- else if (ctor === 'Maybe')
14662
- valid = true;
14663
- if (valid)
14664
- return [[...matched, ce.symbol('Nothing')], ops];
14665
- return [[...matched, ce.error(['missing', expect])], ops];
14666
- }
14667
- if (ctor === 'Union') {
14668
- //
14669
- // We expect one of several domains. Check if at least one matches
14670
- //
14671
- let found = false;
14672
- for (let k = 1; k <= expect.length - 1; k++) {
14673
- if (next.domain.isCompatible(ce.domain(expect[k]))) {
14674
- found = true;
14675
- break;
14676
- }
14677
- }
14678
- if (found)
14679
- return [[...matched, next], ops];
14680
- return [
14681
- [
14682
- ...matched,
14683
- ce.error(['incompatible-domain', expect, next.domain], next),
14684
- ],
14685
- ops,
14686
- ];
14687
- }
14688
- if (ctor === 'Sequence') {
14689
- const seq = ce.domain(expect[1]);
14690
- if (!next || !next.domain.isCompatible(seq)) {
14691
- return [
14692
- [...matched, ce.error(['incompatible-domain', seq, next.domain], next)],
14693
- ops,
14694
- ];
14695
- }
14696
- let done = false;
14697
- const result = [...matched, next];
14698
- while (!done) {
14699
- next = ops.shift();
14700
- if (!next)
14701
- done = false;
14702
- else if (!next.domain.isCompatible(seq)) {
14703
- ops.unshift(next);
14704
- done = false;
14705
- }
14706
- else
14707
- result.push(next);
14708
- }
14709
- return [result, ops];
14710
- }
14711
- if (ctor === 'Maybe') {
14712
- if (next === undefined || next.symbol === 'Nothing')
14713
- return [[...matched, ce.symbol('Nothing')], ops];
14714
- return validateNextArgument(ce, expect[1], matched, [next, ...ops]);
14715
- }
14716
- console.error('Unhandled ctor', ctor);
14717
- return [[...matched, next], ops];
14718
- }
14719
-
14720
- function gcd(a, b) {
14721
- //@todo: https://github.com/Yaffle/bigint-gcd/blob/main/gcd.js
14722
- console.assert(a.isInteger() && b.isInteger());
14723
- while (!b.isZero())
14724
- [a, b] = [b, a.modulo(b)];
14725
- return a.abs();
14726
- }
14727
- // Difference between primes from 7 to 31
14728
- const PRIME_WHEEL_INC = [4, 2, 4, 2, 4, 6, 2, 6];
14729
- function primeFactors(ce, n) {
14730
- if (n.lt(Number.MAX_SAFE_INTEGER)) {
14731
- const factors = primeFactors$1(n.toNumber());
14732
- const result = new Map();
14733
- for (const f of Object.keys(factors))
14734
- result.set(ce.bignum(f), factors[f]);
14735
- return result;
14736
- }
14737
- //https:rosettacode.org/wiki/Prime_decomposition#JavaScript
14738
- const result = new Map();
14739
- // Wheel factorization
14740
- // @todo: see https://github.com/Fairglow/prime-factor/blob/main/src/lib.rs
14741
- // @todo: rewrite using Bignum
14742
- let count = 0;
14743
- while (n.mod(2).isZero()) {
14744
- count += 1;
14745
- n = n.div(2);
14746
- }
14747
- if (count > 0)
14748
- result.set('2', count);
14749
- count = 0;
14750
- while (n.mod(3).isZero()) {
14751
- count += 1;
14752
- n = n.div(3);
14753
- }
14754
- if (count > 0)
14755
- result.set('3', count);
14756
- while (n.mod(5).isZero()) {
14757
- count += 1;
14758
- n = n.div(5);
14759
- }
14760
- if (count > 0)
14761
- result.set('5', count);
14762
- let k = ce.bignum(7);
14763
- let kIndex = k.toString();
14764
- let i = 0;
14765
- while (k.mul(k).lt(n)) {
14766
- if (n.mod(k).isZero()) {
14767
- result.set(kIndex, (result.get(kIndex) ?? 0) + 1);
14768
- n = n.div(k);
14769
- }
14770
- else {
14771
- k = k.add(PRIME_WHEEL_INC[i]);
14772
- kIndex = k.toString();
14773
- i = i < 7 ? i + 1 : 0;
14774
- }
14775
- }
14776
- if (!n.eq(1))
14777
- result.set(n.toString(), 1);
14778
- const r = new Map();
14779
- for (const [k, v] of result)
14780
- r.set(ce.bignum(k), v);
14781
- return r;
14782
- }
14783
- /** Return `[factor, root]` such that
14784
- * pow(n, 1/exponent) = factor * pow(root, 1/exponent)
14785
- *
14786
- * factorPower(75, 2) -> [5, 3] = 5^2 * 3
14787
- *
14788
- */
14789
- function factorPower(ce, n, exponent) {
14790
- // @todo: handle negative n
14791
- console.assert(n.isInteger() && n.isPositive());
14792
- const factors = primeFactors(ce, n);
14793
- let f = ce.bignum(1);
14794
- let r = ce.bignum(1);
14795
- for (const [k, v] of factors) {
14796
- const v2 = ce.bignum(v);
14797
- f = f.mul(k.pow(v2.div(exponent).floor()));
14798
- r = r.mul(k.pow(v2.mod(exponent)));
14799
- }
14800
- return [f, r];
14801
- }
14802
- function factorial(ce, n) {
14803
- if (!n.isInteger() || n.isNegative())
14804
- return ce._BIGNUM_NAN;
14805
- if (n.lessThan(10))
14806
- return ce.bignum([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800][n.toNumber()]);
14807
- if (n.gt(Number.MAX_SAFE_INTEGER)) {
14808
- let val = ce._BIGNUM_ONE;
14809
- let i = ce._BIGNUM_TWO;
14810
- while (i.lessThan(n)) {
14811
- val = val.mul(i);
14812
- i = i.add(1);
14813
- }
14814
- return val;
14815
- }
14816
- if (n.modulo(2).eq(1)) {
14817
- return n.times(factorial(ce, n.minus(1)));
14818
- }
14819
- let loop = n.toNumber();
14820
- let sum = n;
14821
- let val = n;
14822
- while (loop > 2) {
14823
- loop -= 2;
14824
- sum = sum.add(loop);
14825
- val = val.mul(sum);
14826
- }
14827
- return val;
14828
- }
14829
- const gammaG = 7;
14830
- // Spouge approximation (suitable for large arguments)
14831
- function lngamma$1(ce, z) {
14832
- if (z.isNegative())
14833
- return ce._BIGNUM_NAN;
14834
- const GAMMA_P_LN = ce.cache('gamma-p-ln', () => {
14835
- return [
14836
- '0.99999999999999709182',
14837
- '57.156235665862923517',
14838
- '-59.597960355475491248',
14839
- '14.136097974741747174',
14840
- '-0.49191381609762019978',
14841
- '0.33994649984811888699e-4',
14842
- '0.46523628927048575665e-4',
14843
- '-0.98374475304879564677e-4',
14844
- '0.15808870322491248884e-3',
14845
- '-0.21026444172410488319e-3',
14846
- '0.2174396181152126432e-3',
14847
- '-0.16431810653676389022e-3',
14848
- '0.84418223983852743293e-4',
14849
- '-0.2619083840158140867e-4',
14850
- '0.36899182659531622704e-5',
14851
- ].map((x) => ce.bignum(x));
14852
- });
14853
- let x = GAMMA_P_LN[0];
14854
- for (let i = GAMMA_P_LN.length - 1; i > 0; --i) {
14855
- x = x.add(GAMMA_P_LN[i].div(z.add(i)));
14856
- }
14857
- const GAMMA_G_LN = ce.cache('gamma-g-ln', () => ce.bignum(607).div(128));
14858
- const t = z.add(GAMMA_G_LN).add(ce._BIGNUM_HALF);
14859
- return ce._BIGNUM_NEGATIVE_ONE
14860
- .acos()
14861
- .mul(ce._BIGNUM_TWO)
14862
- .log()
14863
- .mul(ce._BIGNUM_HALF)
14864
- .add(t.log().mul(z.add(ce._BIGNUM_HALF)).minus(t).add(x.log()).minus(z.log()));
14865
- }
14866
- // From https://github.com/substack/gamma.js/blob/master/index.js
14867
- function gamma$1(ce, z) {
14868
- if (z.lessThan(ce._BIGNUM_HALF)) {
14869
- const pi = ce._BIGNUM_NEGATIVE_ONE.acos();
14870
- return pi.div(pi
14871
- .mul(z)
14872
- .sin()
14873
- .mul(gamma$1(ce, ce._BIGNUM_ONE.sub(z))));
14874
- }
14875
- if (z.greaterThan(100))
14876
- return lngamma$1(ce, z).exp();
14877
- z = z.sub(1);
14878
- // coefficients for gamma=7, kmax=8 Lanczos method
14879
- // Source: GSL/specfunc/gamma.c
14880
- const LANCZOS_7_C = ce.cache('lanczos-7-c', () => {
14881
- return [
14882
- '0.99999999999980993227684700473478',
14883
- '676.520368121885098567009190444019',
14884
- '-1259.13921672240287047156078755283',
14885
- '771.3234287776530788486528258894',
14886
- '-176.61502916214059906584551354',
14887
- '12.507343278686904814458936853',
14888
- '-0.13857109526572011689554707',
14889
- '9.984369578019570859563e-6',
14890
- '1.50563273514931155834e-7',
14891
- ].map((x) => ce.bignum(x));
14892
- });
14893
- let x = LANCZOS_7_C[0];
14894
- for (let i = 1; i < gammaG + 2; i++)
14895
- x = x.add(LANCZOS_7_C[i].div(z.add(i)));
14896
- const t = z.add(gammaG).add(ce._BIGNUM_HALF);
14897
- return ce._BIGNUM_NEGATIVE_ONE
14898
- .acos()
14899
- .times(ce._BIGNUM_TWO)
14900
- .sqrt()
14901
- .mul(x.mul(t.neg().exp()).mul(t.pow(z.add(ce._BIGNUM_HALF))));
14902
- }
14903
- /**
14904
- * If the exponent of the bignum is in the range of the exponents
14905
- * for machine numbers,return true.
14906
- */
14907
- function isInMachineRange(d) {
14908
- if (!d.isFinite())
14909
- return true; // Infinity and NaN are in machine range
14910
- // Are there too many significant digits?
14911
- // Maximum Safe Integer is 9007199254740991
14912
- // Digits in Decimal are stored by blocks of 7.
14913
- // Three blocks, with the first block = 90 is close to the maximum
14914
- if (d.d.length > 3 || (d.d.length === 3 && d.d[0] >= 90))
14915
- return false;
14916
- console.assert(d.precision() <= 16);
14917
- // Is the exponent within range?
14918
- // With a binary 64 IEEE 754 number:
14919
- // significant bits: 53 -> 15 digits
14920
- // exponent bits: 11. emax = 307, emin = -306)
14921
- return d.e < 308 && d.e > -306;
14922
- }
14923
- // export function asMachineNumber(d: Decimal): number | null {
14924
- // if (d.precision() < 15 && d.e < 308 && d.e > -306) return d.toNumber();
14925
- // return null;
14926
- // }
14927
-
14928
- function isRational(x) {
14929
- return x !== null && Array.isArray(x);
14930
- }
14931
- function isMachineRational(x) {
14932
- return x !== null && Array.isArray(x) && typeof x[0] === 'number';
14933
- }
14934
- function isBigRational(x) {
14935
- return x !== null && Array.isArray(x) && x[0] instanceof Decimal;
14936
- }
14937
- function isRationalZero(x) {
14938
- if (x[0] === 0)
14939
- return true;
14940
- return x[0] instanceof Decimal && x[0].isZero();
14941
- }
14942
- function isRationalOne(x) {
14943
- if (x[0] === x[1])
14944
- return true;
14945
- if (typeof x[0] === 'number')
14946
- return false;
14947
- return x[0].eq(x[1]);
14948
- }
14949
- function isRationalNegativeOne(x) {
14950
- if (typeof x[0] === 'number')
14951
- return x[0] === -x[1];
14952
- return x[0].eq(x[1].neg());
14953
- }
14954
- function machineNumerator(x) {
14955
- return typeof x[0] === 'number' ? x[0] : x[0].toNumber();
14956
- }
14957
- function machineDenominator(x) {
14958
- return typeof x[1] === 'number' ? x[1] : x[1].toNumber();
14959
- }
14960
- function isNeg(lhs) {
14961
- if (isMachineRational(lhs))
14962
- return lhs[0] < 0;
14963
- return lhs[0].isNeg();
14964
- }
14965
- function neg(lhs) {
14966
- if (isMachineRational(lhs))
14967
- return [-lhs[0], lhs[1]];
14968
- return [lhs[0].neg(), lhs[1]];
14969
- }
14970
- function inverse(lhs) {
14971
- if (isMachineRational(lhs))
14972
- return lhs[0] < 0 ? [-lhs[1], -lhs[0]] : [lhs[1], lhs[0]];
14973
- return lhs[0].isNeg() ? [lhs[1].neg(), lhs[0].neg()] : [lhs[1], lhs[0]];
14974
- }
14975
- function asRational(expr) {
14976
- const num = expr.numericValue;
14977
- if (num === null)
14978
- return undefined;
14979
- if (Array.isArray(num))
14980
- return num;
14981
- if (typeof num === 'number' && Number.isInteger(num))
14982
- return [num, 1];
14983
- if (num instanceof Decimal && num.isInteger())
14984
- return [num, expr.engine._BIGNUM_ONE];
14985
- return undefined;
14986
- }
14987
- function asMachineRational(r) {
14988
- if (isMachineRational(r))
14989
- return r;
14990
- return [r[0].toNumber(), r[1].toNumber()];
14991
- }
14992
- /**
14993
- * Add a literal numeric value to a rational.
14994
- * If the rational is a bignum, this is a hint to do the calculation in bignum
14995
- * (no need to check `bignumPreferred()`).
14996
- * @param lhs
14997
- * @param rhs
14998
- * @returns
14999
- */
15000
- function add(lhs, rhs) {
15001
- console.assert(Array.isArray(rhs) ||
15002
- (rhs.numericValue !== null && !(rhs instanceof Complex)));
15003
- if (Array.isArray(rhs)) {
15004
- if (isBigRational(rhs))
15005
- return [rhs[1].mul(lhs[0]).add(rhs[0].mul(lhs[1])), rhs[1].mul(lhs[1])];
15006
- if (isBigRational(lhs))
15007
- return [lhs[0].mul(rhs[1]).add(lhs[1].mul(rhs[0])), lhs[1].mul(rhs[1])];
15008
- return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
15009
- }
15010
- const rhsNum = rhs.numericValue;
15011
- if (rhsNum !== null && typeof rhsNum === 'number') {
15012
- if (isMachineRational(lhs))
15013
- return [lhs[0] + lhs[1] * rhsNum, lhs[1]];
15014
- return [lhs[0].add(lhs[1].mul(rhsNum)), lhs[1]];
15015
- }
15016
- if (rhsNum instanceof Decimal) {
15017
- if (isMachineRational(lhs)) {
15018
- const ce = rhs.engine;
15019
- return [ce.bignum(rhsNum.mul(lhs[1]).add(lhs[0])), ce.bignum(lhs[1])];
15020
- }
15021
- return [lhs[0].add(lhs[1].mul(rhsNum)), lhs[1]];
15022
- }
15023
- if (Array.isArray(rhsNum)) {
15024
- if (isBigRational(rhsNum))
15025
- return [
15026
- rhsNum[1].mul(lhs[0]).add(rhsNum[0].mul(lhs[1])),
15027
- rhsNum[1].mul(lhs[1]),
15028
- ];
15029
- if (isBigRational(lhs))
15030
- return [
15031
- lhs[0].mul(rhsNum[1]).add(lhs[1].mul(rhsNum[0])),
15032
- lhs[1].mul(rhsNum[1]),
15033
- ];
15034
- return [rhsNum[1] * lhs[0] + rhsNum[0] * lhs[1], rhsNum[1] * lhs[1]];
15035
- }
15036
- debugger;
15037
- return lhs;
14762
+ debugger;
14763
+ return lhs;
15038
14764
  }
15039
14765
  function mul(lhs, rhs) {
15040
14766
  console.assert(Array.isArray(rhs) ||
15041
14767
  (rhs.numericValue !== null && !(rhs instanceof Complex)));
15042
14768
  if (Array.isArray(rhs)) {
15043
- if (isBigRational(lhs))
15044
- return [lhs[0].mul(rhs[0]), lhs[1].mul(rhs[1])];
15045
- if (isBigRational(rhs))
15046
- return [rhs[0].mul(lhs[0]), rhs[1].mul(lhs[1])];
15047
- return [rhs[0] * lhs[0], rhs[1] * lhs[1]];
14769
+ if (isMachineRational(lhs) && isMachineRational(rhs))
14770
+ return [lhs[0] * rhs[0], lhs[1] * rhs[1]];
14771
+ if (isMachineRational(lhs))
14772
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14773
+ if (isMachineRational(rhs))
14774
+ rhs = [bigint(rhs[0]), bigint(rhs[1])];
14775
+ return [lhs[0] * rhs[0], lhs[1] * rhs[1]];
15048
14776
  }
15049
14777
  const rhsNum = rhs.numericValue;
15050
14778
  if (rhsNum !== null && typeof rhsNum === 'number') {
14779
+ console.assert(Number.isInteger(rhsNum));
15051
14780
  if (isMachineRational(lhs))
15052
14781
  return [lhs[0] * rhsNum, lhs[1]];
15053
- return [lhs[0].mul(rhsNum), lhs[1]];
14782
+ return [lhs[0] * bigint(rhsNum), lhs[1]];
15054
14783
  }
15055
14784
  if (rhsNum instanceof Decimal) {
14785
+ console.assert(rhsNum.isInteger());
15056
14786
  if (isMachineRational(lhs))
15057
- return [rhsNum.mul(lhs[0]), rhs.engine.bignum(lhs[1])];
15058
- return [rhsNum.mul(lhs[0]), lhs[1]];
14787
+ return [bigint(rhsNum.toString()) * bigint(lhs[0]), bigint(lhs[1])];
14788
+ return [bigint(rhsNum.toString()) * lhs[0], lhs[1]];
15059
14789
  }
15060
14790
  if (Array.isArray(rhsNum)) {
15061
14791
  if (isBigRational(rhsNum))
15062
- return [rhsNum[0].mul(lhs[0]), rhsNum[1].mul(lhs[1])];
14792
+ return [rhsNum[0] * bigint(lhs[0]), rhsNum[1] * bigint(lhs[1])];
15063
14793
  else if (isMachineRational(lhs))
15064
14794
  return [lhs[0] * rhsNum[0], lhs[1] * rhsNum[1]];
15065
- return [lhs[0].mul(rhsNum[0]), lhs[1].mul(rhsNum[1])];
14795
+ return [lhs[0] * bigint(rhsNum[0]), lhs[1] * bigint(rhsNum[1])];
15066
14796
  }
15067
14797
  debugger;
15068
14798
  return lhs;
15069
14799
  }
15070
14800
  function pow(r, exp) {
15071
14801
  console.assert(Number.isInteger(exp));
14802
+ if (exp === 0)
14803
+ return [1, 1];
15072
14804
  if (exp < 0) {
15073
14805
  r = inverse(r);
15074
14806
  exp = -exp;
15075
14807
  }
15076
- if (exp === 0)
15077
- return [1, 1];
15078
14808
  if (exp === 1)
15079
14809
  return r;
15080
14810
  if (isMachineRational(r))
15081
14811
  return [Math.pow(r[0], exp), Math.pow(r[1], exp)];
15082
- return [r[0].pow(exp), r[1].pow(exp)];
14812
+ const bigexp = bigint(exp);
14813
+ return [r[0] ** bigexp, r[1] ** bigexp];
15083
14814
  }
15084
14815
  function reducedRational(r) {
15085
14816
  if (isMachineRational(r)) {
@@ -15087,19 +14818,21 @@ function reducedRational(r) {
15087
14818
  return r;
15088
14819
  if (r[1] < 0)
15089
14820
  r = [-r[0], -r[1]];
14821
+ if (!Number.isFinite(r[1]))
14822
+ return [0, 1];
15090
14823
  const g = gcd$1(r[0], r[1]);
15091
14824
  // If the gcd is 0, return the rational unchanged
15092
14825
  return g <= 1 ? r : [r[0] / g, r[1] / g];
15093
14826
  }
15094
- if (r[0].equals(1) || r[1].equals(1))
14827
+ if (r[0] === BigInt(1) || r[1] === BigInt(1))
15095
14828
  return r;
15096
- if (r[1].isNegative())
15097
- r = [r[0].neg(), r[1].neg()];
14829
+ if (r[1] < 0)
14830
+ r = [-r[0], -r[1]];
15098
14831
  const g = gcd(r[0], r[1]);
15099
14832
  // If the gcd is 0, return the rational unchanged
15100
- if (g.lessThanOrEqualTo(1))
14833
+ if (g <= 1)
15101
14834
  return r;
15102
- return [r[0].div(g), r[1].div(g)];
14835
+ return [r[0] / g, r[1] / g];
15103
14836
  }
15104
14837
  /** Return a rational approximation of x */
15105
14838
  function rationalize(x) {
@@ -15168,12 +14901,14 @@ function asCoefficient(expr) {
15168
14901
  const rest = [];
15169
14902
  let coef = [1, 1];
15170
14903
  for (const arg of expr.ops) {
15171
- // Only consider the value of literals
15172
14904
  const n = arg.numericValue;
15173
- if (n === null || n instanceof Complex)
15174
- rest.push(arg);
15175
- else
14905
+ if (n !== null &&
14906
+ ((typeof n === 'number' && Number.isInteger(n)) ||
14907
+ (n instanceof Decimal && n.isInteger()) ||
14908
+ isRational(n)))
15176
14909
  coef = mul(coef, arg);
14910
+ else
14911
+ rest.push(arg);
15177
14912
  }
15178
14913
  coef = reducedRational(coef);
15179
14914
  if (isRationalOne(coef))
@@ -15192,11 +14927,9 @@ function asCoefficient(expr) {
15192
14927
  let [coef1, numer] = asCoefficient(expr.op1);
15193
14928
  const [coef2, denom] = asCoefficient(expr.op2);
15194
14929
  const coef = reducedRational(mul(coef1, inverse(coef2)));
15195
- if (numer.isOne && denom.isOne)
15196
- return [coef, ce._ONE];
15197
14930
  if (denom.isOne)
15198
14931
  return [coef, numer];
15199
- return [coef, ce.fn('Divide', [numer, denom])];
14932
+ return [coef, ce.div(numer, denom)];
15200
14933
  }
15201
14934
  //
15202
14935
  // Power
@@ -15212,9 +14945,9 @@ function asCoefficient(expr) {
15212
14945
  const exponent = expr.op2;
15213
14946
  const e = asSmallInteger(exponent);
15214
14947
  if (e === -1)
15215
- return [inverse(coef), ce.inverse(base)];
14948
+ return [inverse(coef), ce.inv(base)];
15216
14949
  if (e !== null)
15217
- return [pow(coef, e), ce.power(base, exponent)];
14950
+ return [pow(coef, e), ce.pow(base, exponent)];
15218
14951
  // The exponent might be a rational (square root, cubic root...)
15219
14952
  if (exponent.numericValue !== null &&
15220
14953
  Array.isArray(exponent.numericValue)) {
@@ -15228,13 +14961,17 @@ function asCoefficient(expr) {
15228
14961
  // en = -1 -> inverse the extracted coef
15229
14962
  return [
15230
14963
  en === 1 ? [nCoef, dCoef] : [dCoef, nCoef],
15231
- ce.power(ce.mul([ce.number([nRest, dRest]), base]), exponent),
14964
+ ce.pow(ce.mul([ce.number([nRest, dRest]), base]), exponent),
15232
14965
  ];
15233
14966
  }
15234
14967
  }
15235
14968
  return [[1, 1], expr];
15236
14969
  }
15237
14970
  //
14971
+ // Add
14972
+ //
14973
+ if (expr.head === 'Add') ;
14974
+ //
15238
14975
  // Negate
15239
14976
  //
15240
14977
  if (expr.head === 'Negate') {
@@ -15248,8 +14985,8 @@ function asCoefficient(expr) {
15248
14985
  const n = expr.numericValue;
15249
14986
  if (n !== null) {
15250
14987
  if (n instanceof Decimal) {
15251
- if (n.isInteger() && isInMachineRange(n))
15252
- return [[n.toNumber(), 1], ce._ONE];
14988
+ if (n.isInteger())
14989
+ return [[bigint(n.toString()), BigInt(1)], ce._ONE];
15253
14990
  if (n.isNegative())
15254
14991
  return [[-1, 1], ce.number(n.neg())];
15255
14992
  }
@@ -15259,7 +14996,7 @@ function asCoefficient(expr) {
15259
14996
  if (n < 0)
15260
14997
  return [[-1, 1], ce.number(-n)];
15261
14998
  }
15262
- if (Array.isArray(n))
14999
+ if (isRational(n))
15263
15000
  return [n, ce._ONE];
15264
15001
  // Make the part positive if the real part is negative
15265
15002
  if (n instanceof Complex && n.re < 0)
@@ -15281,7 +15018,7 @@ function signDiff(lhs, rhs, tolerance) {
15281
15018
  const lhsNum = lhsN.numericValue;
15282
15019
  const rhsNum = rhsN.numericValue;
15283
15020
  if (lhsNum === null || rhsNum === null) {
15284
- // Couldn't calculate a numeric value, use the `sgn`
15021
+ // Couldn't calculate numeric value, use the `sgn` property
15285
15022
  const lhsS = lhs.sgn;
15286
15023
  const rhsS = rhs.sgn;
15287
15024
  if (typeof lhsS !== 'number' || typeof rhsS !== 'number')
@@ -15304,20 +15041,68 @@ function signDiff(lhs, rhs, tolerance) {
15304
15041
  return undefined;
15305
15042
  // In general, it is impossible to always prove equality
15306
15043
  // (Richardson's theorem) but this works often...
15307
- const rhsR = asRational(rhsN);
15308
- if (!rhsR)
15044
+ // At this point, lhsNum and rhsNum are either number or Decimal
15045
+ // (it can't be a rational, because lhs.N() simplifies rationals to number or Decimal)
15046
+ if (isRational(lhsNum) || isRational(rhsNum))
15309
15047
  return undefined;
15310
- const diff = add(neg(rhsR), lhsN);
15311
- const delta = isMachineRational(diff)
15312
- ? chop(diff[0] / diff[1], tolerance)
15313
- : chop(diff[0].div(diff[1]), tolerance);
15314
- if (delta === 0)
15048
+ if (typeof lhsNum === 'number' && typeof rhsNum === 'number') {
15049
+ if (chop(rhsNum - lhsNum, tolerance) === 0)
15050
+ return 0;
15051
+ return lhsNum < rhsNum ? -1 : 1;
15052
+ }
15053
+ const ce = lhs.engine;
15054
+ const delta = ce.bignum(rhsNum).sub(ce.bignum(lhsNum));
15055
+ if (chop(delta, tolerance) === 0)
15315
15056
  return 0;
15316
- if (typeof delta === 'number')
15317
- return delta > 0 ? 1 : -1;
15318
15057
  return delta.isPos() ? 1 : -1;
15319
15058
  }
15320
15059
 
15060
+ /**
15061
+ * Flatten the arguments.
15062
+ * If `expr` was canonical, the result it canonical.
15063
+ */
15064
+ function flattenOps(ops, head) {
15065
+ if (!head)
15066
+ return ops;
15067
+ // Bypass memory allocation for the common case where there is nothing to flatten
15068
+ if (ops.every((x) => !x.ops || x.head !== head))
15069
+ return ops;
15070
+ const result = [];
15071
+ for (const arg of ops) {
15072
+ if (!arg.ops || arg.head !== head)
15073
+ result.push(arg);
15074
+ else {
15075
+ // ["f", a, ["f", b, c]] -> ["f", a, b, c]
15076
+ // or ["f", ["f", a]] -> ["f", a]
15077
+ result.push(...flattenOps(arg.ops, head));
15078
+ }
15079
+ }
15080
+ // If number of arguments didn't change, we didn't flatten
15081
+ console.assert(result.length !== ops.length); // @todo check below may not be necessary
15082
+ if (result.length === ops.length)
15083
+ return ops;
15084
+ return result;
15085
+ }
15086
+ function flattenSequence(xs) {
15087
+ // Bypass memory allocation for the common case where there are no sequences
15088
+ if (xs.every((x) => x.head !== 'Sequence'))
15089
+ return xs;
15090
+ const ys = [];
15091
+ for (const x of xs) {
15092
+ if (x.isValid && x.head === 'Sequence') {
15093
+ if (x.ops)
15094
+ ys.push(...x.ops);
15095
+ }
15096
+ else
15097
+ ys.push(x);
15098
+ }
15099
+ return ys;
15100
+ }
15101
+ function canonical(xs) {
15102
+ // Avoid memory allocation if possible
15103
+ return xs.every((x) => x.isCanonical) ? xs : xs.map((x) => x.canonical);
15104
+ }
15105
+
15321
15106
  function negateLiteral(expr, metadata) {
15322
15107
  // Applying negation is safe (doesn't introduce numeric errors)
15323
15108
  // even on floating point numbers
@@ -15336,65 +15121,36 @@ function negateLiteral(expr, metadata) {
15336
15121
  }
15337
15122
  /**
15338
15123
  * Distribute `Negate` (multiply by -1) if expr is a number literal, an
15339
- * addition or another `Negate`.
15340
- *
15341
- * This is appropriate to call during a `canonical` chain.
15124
+ * addition or multiplication or another `Negate`.
15342
15125
  *
15343
- * For more thorough distribution (including multiplication), see `distributeNegate`,
15344
- * applicable during a `simplify` or `evaluate` chain.
15126
+ * It is important to do all these to handle cases like
15127
+ * `-3x` -> ["Negate, ["Multiply", 3, "x"]] -> ["Multiply, -3, x]
15345
15128
  */
15346
15129
  function canonicalNegate(expr, metadata) {
15347
15130
  // Negate(Negate(x)) -> x
15348
- if (expr.head === 'Negate')
15349
- return validateArgument(expr.engine, expr.op1?.canonical, 'Number');
15350
- expr = validateArgument(expr.engine, expr.canonical, 'Number');
15351
- if (expr.numericValue !== null)
15352
- return negateLiteral(expr, metadata);
15353
- // Distribute over addition
15354
- // Negate(Add(a, b)) -> Add(Negate(a), Negate(b))
15355
- if (expr.head === 'Add') {
15356
- let ops = expr.ops.map((x) => canonicalNegate(x));
15357
- ops = flattenOps(ops, 'Add') ?? ops;
15358
- return expr.engine.add(ops, metadata);
15359
- }
15360
- // 'Subtract' is canonicalized into `Add`, so don't have to worry about it
15361
- console.assert(expr.head !== 'Subtract');
15362
- return expr.engine._fn('Negate', [expr], metadata);
15363
- }
15364
- /**
15365
- * Return the additive opposite of the expression.
15366
- *
15367
- * Applies to `Add`, `Multiply`, `Negate` and number literals.
15368
- *
15369
- * If none can be produced (the expression is a symbol for example),
15370
- * return `null`.
15371
- *
15372
- * Call during a `simplify` or `evaluate` chain. Use `caonnicalNegate` during a
15373
- * `canonical` chain.
15374
- */
15375
- function distributeNegate(expr) {
15376
- if (expr.numericValue !== null)
15377
- return negateLiteral(expr);
15378
15131
  if (expr.head === 'Negate')
15379
15132
  return expr.op1;
15380
- const ce = expr.engine;
15133
+ if (expr.numericValue !== null)
15134
+ return negateLiteral(expr, metadata);
15381
15135
  // Distribute over addition
15382
15136
  // Negate(Add(a, b)) -> Add(Negate(a), Negate(b))
15383
15137
  if (expr.head === 'Add') {
15384
- let ops = expr.ops.map((x) => distributeNegate(x));
15385
- ops = flattenOps(ops, 'Add') ?? ops;
15386
- return ce.add(ops);
15138
+ let ops = expr.ops.map((x) => canonicalNegate(x));
15139
+ ops = flattenOps(ops, 'Add');
15140
+ return expr.engine.add(ops, metadata);
15387
15141
  }
15388
15142
  // Distribute over multiplication
15389
15143
  // Negate(Multiply(a, b)) -> Multiply(Negate(a), b)
15390
15144
  if (expr.head === 'Multiply') {
15391
- return negateProduct(ce, expr.ops);
15145
+ return negateProduct(expr.engine, expr.ops);
15392
15146
  }
15393
15147
  // Distribute over division
15394
15148
  // Negate(Divide(a, b)) -> Divide(Negate(a), b)
15395
15149
  if (expr.head === 'Divide')
15396
- return ce.divide(distributeNegate(expr.op1), expr.op2);
15397
- return ce._fn('Negate', [expr]);
15150
+ return expr.engine._fn('Divide', [canonicalNegate(expr.op1), expr.op2]);
15151
+ // 'Subtract' is canonicalized into `Add`, so don't have to worry about it
15152
+ console.assert(expr.head !== 'Subtract');
15153
+ return expr.engine._fn('Negate', [expr], metadata);
15398
15154
  }
15399
15155
  // Given a list of terms in a product, find the "best" one to negate in
15400
15156
  // order to negate the entire product:
@@ -15422,7 +15178,7 @@ function negateProduct(ce, args) {
15422
15178
  result.push(arg);
15423
15179
  else {
15424
15180
  done = true;
15425
- result.push(distributeNegate(arg));
15181
+ result.push(canonicalNegate(arg));
15426
15182
  }
15427
15183
  }
15428
15184
  if (done)
@@ -15434,7 +15190,7 @@ function negateProduct(ce, args) {
15434
15190
  result.push(arg);
15435
15191
  else {
15436
15192
  done = true;
15437
- result.push(distributeNegate(arg));
15193
+ result.push(canonicalNegate(arg));
15438
15194
  }
15439
15195
  }
15440
15196
  if (done)
@@ -15442,7 +15198,7 @@ function negateProduct(ce, args) {
15442
15198
  return ce._fn('Negate', [ce._fn('Multiply', args)]);
15443
15199
  }
15444
15200
  function processNegate(_ce, x, _mode = 'simplify') {
15445
- return distributeNegate(x);
15201
+ return canonicalNegate(x);
15446
15202
  }
15447
15203
 
15448
15204
  /**
@@ -15499,7 +15255,7 @@ function expand(expr) {
15499
15255
  .add([expand(expr.op1), expand2(ce._NEGATIVE_ONE, expr.op1)])
15500
15256
  .simplify();
15501
15257
  if (expr.head === 'Divide')
15502
- return ce.divide(expand(expr.op1), expand(expr.op2)).simplify();
15258
+ return ce.div(expand(expr.op1), expand(expr.op2)).simplify();
15503
15259
  if (expr.head === 'Multiply') {
15504
15260
  if (expr.nops === 2)
15505
15261
  return expand2(expr.op1, expr.op2);
@@ -15508,13 +15264,13 @@ function expand(expr) {
15508
15264
  if (expr.head === 'Power') {
15509
15265
  const op1head = expr.op1.head;
15510
15266
  if (op1head === 'Multiply')
15511
- return ce.mul(expr.op1.ops.map((x) => ce.power(x, expr.op2))).simplify();
15267
+ return ce.mul(expr.op1.ops.map((x) => ce.pow(x, expr.op2))).simplify();
15512
15268
  if (op1head === 'Negate') {
15513
15269
  const n = asSmallInteger(expr.op2);
15514
15270
  if (n !== null && n > 0) {
15515
15271
  if (n % 2 === 0)
15516
- return ce.power(expr.op1.op1, expr.op2).simplify();
15517
- return ce.negate(ce.power(expr.op1.op1, expr.op2)).simplify();
15272
+ return ce.pow(expr.op1.op1, expr.op2).simplify();
15273
+ return ce.neg(ce.pow(expr.op1.op1, expr.op2)).simplify();
15518
15274
  }
15519
15275
  }
15520
15276
  if (op1head === 'Add') {
@@ -15522,7 +15278,7 @@ function expand(expr) {
15522
15278
  if (n !== null) {
15523
15279
  if (n > 0)
15524
15280
  return expandN(expr.op1, n).simplify();
15525
- return ce.inverse(expandN(expr.op1, -n)).simplify();
15281
+ return ce.inv(expandN(expr.op1, -n)).simplify();
15526
15282
  }
15527
15283
  }
15528
15284
  }
@@ -15641,9 +15397,7 @@ const UNIVARIATE_ROOTS = [
15641
15397
  function findUnivariateRoots(expr, x) {
15642
15398
  const ce = expr.engine;
15643
15399
  if (expr.head === 'Equal') {
15644
- expr = ce
15645
- .add([expr.op1.canonical, ce.negate(expr.op2.canonical)])
15646
- .simplify();
15400
+ expr = ce.add([expr.op1.canonical, ce.neg(expr.op2.canonical)]).simplify();
15647
15401
  }
15648
15402
  const rules = ce.cache('univariate-roots-rules', () => boxRules(ce, UNIVARIATE_ROOTS));
15649
15403
  const result = matchRules(expand(expr).subs({ [x]: '_x' }, { canonical: false }), rules, { _x: ce.symbol('_x') });
@@ -15687,27 +15441,27 @@ function assume(proposition) {
15687
15441
  function assumeEquality(proposition) {
15688
15442
  console.assert(proposition.head === 'Equal');
15689
15443
  // Four cases:
15690
- // 1/ proposition contains no free variable without value
15444
+ // 1/ proposition contains no free variables
15691
15445
  // e.g. `2 + 1 = 3`, `\pi + 1 = \pi`
15692
15446
  // => evaluate and return
15693
- // 2/ lhs is a single free variable with no value and `rhs` does not
15694
- // contain `lhs`
15447
+ // 2/ lhs is a single free variable and `rhs` does not contain `lhs`
15695
15448
  // e.g. `x = 2`, `x = 2\pi`
15696
15449
  // => if `lhs` has a definition, set its value to `rhs`, otherwise
15697
15450
  // define a new symbol with a value of `rhs`
15698
- // 3/ proposition contains a single free variable without value
15451
+ // 3/ proposition contains a single free variable
15699
15452
  // => solve for the free variable, create new def or set value of the
15700
15453
  // free variable with the root(s) as value
15701
- // 4/ proposition contains multiple free variables with no value
15454
+ // 4/ proposition contains multiple free variables
15702
15455
  // => add (lhs - rhs = 0) to assumptions DB
15703
15456
  // Case 1
15704
- const unvals = unvaluedIdentifiers(proposition);
15705
- if (unvals.length === 0) {
15457
+ const freeVars = proposition.freeVars;
15458
+ if (freeVars.length === 0) {
15706
15459
  const val = proposition.evaluate();
15707
15460
  if (val.symbol === 'True')
15708
15461
  return 'tautology';
15709
15462
  if (val.symbol === 'False')
15710
15463
  return 'contradiction';
15464
+ console.log(proposition.canonical.evaluate());
15711
15465
  return 'not-a-predicate';
15712
15466
  }
15713
15467
  const ce = proposition.engine;
@@ -15728,17 +15482,14 @@ function assumeEquality(proposition) {
15728
15482
  return 'ok';
15729
15483
  }
15730
15484
  // Case 3
15731
- if (unvals.length === 1) {
15732
- const lhs = unvals[0];
15485
+ if (freeVars.length === 1) {
15486
+ const lhs = freeVars[0];
15733
15487
  const sols = findUnivariateRoots(proposition, lhs);
15734
15488
  if (sols.length === 0) {
15735
15489
  ce.assumptions.set(ce.box([
15736
15490
  'Equal',
15737
15491
  ce
15738
- .add([
15739
- proposition.op1.canonical,
15740
- ce.negate(proposition.op2.canonical),
15741
- ])
15492
+ .add([proposition.op1.canonical, ce.neg(proposition.op2.canonical)])
15742
15493
  .simplify(),
15743
15494
  0,
15744
15495
  ]), true);
@@ -15759,17 +15510,16 @@ function assumeEquality(proposition) {
15759
15510
  }
15760
15511
  function assumeInequality(proposition) {
15761
15512
  //
15762
- // 1/ lhs is a single free var with no def
15763
- // e.g. x < 0
15513
+ // 1/ lhs is a single **undefined** free var e.g. "x < 0"
15764
15514
  // => define a new var, if the domain can be inferred set it, otherwise
15765
15515
  // RealNumber and add to assumptions (e.g. x < 5)
15766
- // 2/ (lhs - rhs) is an expression with no free var with no value
15767
- // e.g. \pi < 5
15516
+ // 2/ (lhs - rhs) is an expression with no free vars
15517
+ // e.g. "\pi < 5"
15768
15518
  // => evaluate
15769
- // 3/ (lhs - rhs) is an expression with a single free var with no value
15770
- // e.g. x + 1 < \pi
15519
+ // 3/ (lhs - rhs) is an expression with a single **undefined** free var
15520
+ // e.g. "x + 1 < \pi"
15771
15521
  // => add def as RealNumber, add to assumptions
15772
- // 4/ (lhs - rhs) is an expression with multiple free vars with no value
15522
+ // 4/ (lhs - rhs) is an expression with multiple free vars
15773
15523
  // e.g. x + y < 0
15774
15524
  // => add to assumptions
15775
15525
  const ce = proposition.engine;
@@ -15805,6 +15555,7 @@ function assumeInequality(proposition) {
15805
15555
  }
15806
15556
  return 'ok';
15807
15557
  }
15558
+ // @todo: handle if proposition.op1 *has* a def (and no value)
15808
15559
  // Normalize to Less, LessEqual
15809
15560
  let op = '';
15810
15561
  let lhs;
@@ -15831,20 +15582,20 @@ function assumeInequality(proposition) {
15831
15582
  }
15832
15583
  if (!op)
15833
15584
  return 'internal-error';
15834
- const p = ce.add([lhs.canonical, ce.negate(rhs.canonical)]).simplify();
15835
- const unvals = unvaluedIdentifiers(p);
15585
+ const p = ce.add([lhs.canonical, ce.neg(rhs.canonical)]).simplify();
15836
15586
  // Case 2
15837
15587
  const result = ce.box([op === '<' ? 'Less' : 'LessEqual', p, 0]).evaluate();
15838
- if (unvals.length === 0) {
15839
- if (result.symbol === 'True')
15840
- return 'tautology';
15841
- if (result.symbol === 'False')
15842
- return 'contradiction';
15588
+ if (result.symbol === 'True')
15589
+ return 'tautology';
15590
+ if (result.symbol === 'False')
15591
+ return 'contradiction';
15592
+ const freeVars = result.freeVars;
15593
+ if (freeVars.length === 0)
15843
15594
  return 'not-a-predicate';
15844
- }
15845
15595
  // Case 3
15846
- if (unvals.length === 1) {
15847
- ce.defineSymbol(unvals[0], { domain: 'ExtendedRealNumber' });
15596
+ if (freeVars.length === 1) {
15597
+ if (!ce.lookupSymbol(freeVars[0]))
15598
+ ce.defineSymbol(freeVars[0], { domain: 'ExtendedRealNumber' });
15848
15599
  }
15849
15600
  // Case 3, 4
15850
15601
  console.assert(result.head === 'Less' || result.head === 'LessEqual');
@@ -15916,20 +15667,7 @@ function hasDef(ce, s) {
15916
15667
  return (ce.lookupSymbol(s) ?? ce.lookupFunction(s)) !== undefined;
15917
15668
  }
15918
15669
  function undefinedIdentifiers(expr) {
15919
- const syms = expr.symbols;
15920
- if (syms.length === 0)
15921
- return [];
15922
- return syms
15923
- .filter((x) => !hasDef(expr.engine, x.symbol))
15924
- .map((x) => x.symbol);
15925
- }
15926
- function unvaluedIdentifiers(expr) {
15927
- const syms = expr.symbols;
15928
- if (syms.length === 0)
15929
- return [];
15930
- return syms
15931
- .filter((x) => !hasValue(expr.engine, x.symbol))
15932
- .map((x) => x.symbol);
15670
+ return expr.symbols.filter((x) => !hasDef(expr.engine, x));
15933
15671
  }
15934
15672
  function hasValue(ce, s) {
15935
15673
  if (ce.lookupFunction(s))
@@ -15941,20 +15679,7 @@ function isInequality(expr) {
15941
15679
  if (typeof h !== 'string')
15942
15680
  return false;
15943
15681
  return ['Less', 'Greater', 'LessEqual', 'GreaterEqual'].includes(h);
15944
- }
15945
- // export function getAssumptionsAbout(
15946
- // ce: ComputeEngineInterface,
15947
- // symbol: string
15948
- // ): BoxedExpression[] {
15949
- // const result: BoxedExpression[] = [];
15950
- // for (const [assumption, val] of ce.assumptions) {
15951
- // const vars = getVars(assumption);
15952
- // if (vars.includes(symbol)) {
15953
- // result.push(val ? assumption : ce.boxFunction('Not', [assumption]));
15954
- // }
15955
- // }
15956
- // return [];
15957
- // }
15682
+ }
15958
15683
 
15959
15684
  /**
15960
15685
  * AbstractBoxedExpression
@@ -16044,7 +15769,14 @@ class AbstractBoxedExpression {
16044
15769
  return this.getSubexpressions('');
16045
15770
  }
16046
15771
  get symbols() {
16047
- return [...getSymbols(this, new Set())].map((x) => this.engine.symbol(x, { canonical: false }));
15772
+ const set = new Set();
15773
+ getSymbols(this, set);
15774
+ return Array.from(set);
15775
+ }
15776
+ get freeVars() {
15777
+ const set = new Set();
15778
+ getFreeVars(this, set);
15779
+ return Array.from(set);
16048
15780
  }
16049
15781
  get errors() {
16050
15782
  return this.getSubexpressions('Error');
@@ -16274,6 +16006,132 @@ class AbstractBoxedExpression {
16274
16006
  }
16275
16007
  }
16276
16008
 
16009
+ function factorial(ce, n) {
16010
+ if (!n.isInteger() || n.isNegative())
16011
+ return ce._BIGNUM_NAN;
16012
+ if (n.lessThan(10))
16013
+ return ce.bignum([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800][n.toNumber()]);
16014
+ if (n.gt(Number.MAX_SAFE_INTEGER)) {
16015
+ let val = ce._BIGNUM_ONE;
16016
+ let i = ce._BIGNUM_TWO;
16017
+ while (i.lessThan(n)) {
16018
+ val = val.mul(i);
16019
+ i = i.add(1);
16020
+ }
16021
+ return val;
16022
+ }
16023
+ if (n.modulo(2).eq(1)) {
16024
+ return n.times(factorial(ce, n.minus(1)));
16025
+ }
16026
+ let loop = n.toNumber();
16027
+ let sum = n;
16028
+ let val = n;
16029
+ while (loop > 2) {
16030
+ loop -= 2;
16031
+ sum = sum.add(loop);
16032
+ val = val.mul(sum);
16033
+ }
16034
+ return val;
16035
+ }
16036
+ const gammaG = 7;
16037
+ // Spouge approximation (suitable for large arguments)
16038
+ function lngamma$1(ce, z) {
16039
+ if (z.isNegative())
16040
+ return ce._BIGNUM_NAN;
16041
+ const GAMMA_P_LN = ce.cache('gamma-p-ln', () => {
16042
+ return [
16043
+ '0.99999999999999709182',
16044
+ '57.156235665862923517',
16045
+ '-59.597960355475491248',
16046
+ '14.136097974741747174',
16047
+ '-0.49191381609762019978',
16048
+ '0.33994649984811888699e-4',
16049
+ '0.46523628927048575665e-4',
16050
+ '-0.98374475304879564677e-4',
16051
+ '0.15808870322491248884e-3',
16052
+ '-0.21026444172410488319e-3',
16053
+ '0.2174396181152126432e-3',
16054
+ '-0.16431810653676389022e-3',
16055
+ '0.84418223983852743293e-4',
16056
+ '-0.2619083840158140867e-4',
16057
+ '0.36899182659531622704e-5',
16058
+ ].map((x) => ce.bignum(x));
16059
+ });
16060
+ let x = GAMMA_P_LN[0];
16061
+ for (let i = GAMMA_P_LN.length - 1; i > 0; --i) {
16062
+ x = x.add(GAMMA_P_LN[i].div(z.add(i)));
16063
+ }
16064
+ const GAMMA_G_LN = ce.cache('gamma-g-ln', () => ce.bignum(607).div(128));
16065
+ const t = z.add(GAMMA_G_LN).add(ce._BIGNUM_HALF);
16066
+ return ce._BIGNUM_NEGATIVE_ONE
16067
+ .acos()
16068
+ .mul(ce._BIGNUM_TWO)
16069
+ .log()
16070
+ .mul(ce._BIGNUM_HALF)
16071
+ .add(t.log().mul(z.add(ce._BIGNUM_HALF)).minus(t).add(x.log()).minus(z.log()));
16072
+ }
16073
+ // From https://github.com/substack/gamma.js/blob/master/index.js
16074
+ function gamma$1(ce, z) {
16075
+ if (z.lessThan(ce._BIGNUM_HALF)) {
16076
+ const pi = ce._BIGNUM_NEGATIVE_ONE.acos();
16077
+ return pi.div(pi
16078
+ .mul(z)
16079
+ .sin()
16080
+ .mul(gamma$1(ce, ce._BIGNUM_ONE.sub(z))));
16081
+ }
16082
+ if (z.greaterThan(100))
16083
+ return lngamma$1(ce, z).exp();
16084
+ z = z.sub(1);
16085
+ // coefficients for gamma=7, kmax=8 Lanczos method
16086
+ // Source: GSL/specfunc/gamma.c
16087
+ const LANCZOS_7_C = ce.cache('lanczos-7-c', () => {
16088
+ return [
16089
+ '0.99999999999980993227684700473478',
16090
+ '676.520368121885098567009190444019',
16091
+ '-1259.13921672240287047156078755283',
16092
+ '771.3234287776530788486528258894',
16093
+ '-176.61502916214059906584551354',
16094
+ '12.507343278686904814458936853',
16095
+ '-0.13857109526572011689554707',
16096
+ '9.984369578019570859563e-6',
16097
+ '1.50563273514931155834e-7',
16098
+ ].map((x) => ce.bignum(x));
16099
+ });
16100
+ let x = LANCZOS_7_C[0];
16101
+ for (let i = 1; i < gammaG + 2; i++)
16102
+ x = x.add(LANCZOS_7_C[i].div(z.add(i)));
16103
+ const t = z.add(gammaG).add(ce._BIGNUM_HALF);
16104
+ return ce._BIGNUM_NEGATIVE_ONE
16105
+ .acos()
16106
+ .times(ce._BIGNUM_TWO)
16107
+ .sqrt()
16108
+ .mul(x.mul(t.neg().exp()).mul(t.pow(z.add(ce._BIGNUM_HALF))));
16109
+ }
16110
+ /**
16111
+ * If the exponent of the bignum is in the range of the exponents
16112
+ * for machine numbers,return true.
16113
+ */
16114
+ function isInMachineRange(d) {
16115
+ if (!d.isFinite())
16116
+ return true; // Infinity and NaN are in machine range
16117
+ // Are there too many significant digits?
16118
+ // Maximum Safe Integer is 9007199254740991
16119
+ // Digits in Decimal are stored by blocks of 7.
16120
+ // Three blocks, with the first block = 90 is close to the maximum
16121
+ if (d.d.length > 3 || (d.d.length === 3 && d.d[0] >= 90))
16122
+ return false;
16123
+ console.assert(d.precision() <= 16);
16124
+ // Is the exponent within range?
16125
+ // With a binary 64 IEEE 754 number:
16126
+ // significant bits: 53 -> 15 digits
16127
+ // exponent bits: 11. emax = 307, emin = -306)
16128
+ return d.e < 308 && d.e > -306;
16129
+ }
16130
+ // export function asMachineNumber(d: Decimal): number | null {
16131
+ // if (d.precision() < 15 && d.e < 308 && d.e > -306) return d.toNumber();
16132
+ // return null;
16133
+ // }
16134
+
16277
16135
  /**
16278
16136
  * The total degree of an expression is the sum of the
16279
16137
  * of the positive integer degrees of the factors in the expression:
@@ -16326,11 +16184,15 @@ function maxDegree(expr) {
16326
16184
  function lex(expr) {
16327
16185
  if (expr.symbol)
16328
16186
  return expr.symbol;
16329
- if (expr.ops)
16330
- return expr.ops
16331
- .map((x) => lex(x))
16332
- .filter((x) => x.length > 0)
16333
- .join('"');
16187
+ if (expr.ops) {
16188
+ const h = typeof expr.head === 'string' ? expr.head : lex(expr.head);
16189
+ return (h +
16190
+ '"' +
16191
+ expr.ops
16192
+ .map((x) => lex(x))
16193
+ .filter((x) => x.length > 0)
16194
+ .join('"'));
16195
+ }
16334
16196
  return '';
16335
16197
  }
16336
16198
 
@@ -16343,6 +16205,12 @@ function sortAdd(ce, ops) {
16343
16205
  return ops.sort((a, b) => {
16344
16206
  const aLex = lex(a);
16345
16207
  const bLex = lex(b);
16208
+ if (!aLex && !bLex)
16209
+ return order(a, b);
16210
+ if (!aLex)
16211
+ return +1;
16212
+ if (!bLex)
16213
+ return -1;
16346
16214
  if (aLex < bLex)
16347
16215
  return -1;
16348
16216
  if (aLex > bLex)
@@ -16395,7 +16263,11 @@ function sortAdd(ce, ops) {
16395
16263
  *
16396
16264
  */
16397
16265
  function order(a, b) {
16398
- console.assert(a.isCanonical && b.isCanonical);
16266
+ // console.assert(a.isCanonical && b.isCanonical);
16267
+ if (a === b)
16268
+ return 0;
16269
+ if (a.numericValue !== null && a.numericValue === b.numericValue)
16270
+ return 0;
16399
16271
  //
16400
16272
  // 1/ Literal numeric values
16401
16273
  //
@@ -16423,6 +16295,17 @@ function order(a, b) {
16423
16295
  return +1;
16424
16296
  return -1;
16425
16297
  }
16298
+ if (a.numericValue) {
16299
+ if (b.numericValue) {
16300
+ return +1;
16301
+ }
16302
+ return -1;
16303
+ }
16304
+ if (a.head === 'Sqrt' && a.op1.numericValue) {
16305
+ if (b.head === 'Sqrt' && b.op1.numericValue)
16306
+ return order(a.op1, b.op1);
16307
+ return -1;
16308
+ }
16426
16309
  //
16427
16310
  // 3/ Symbols
16428
16311
  //
@@ -16540,9 +16423,7 @@ class Product {
16540
16423
  this._isCanonical = options.canonical;
16541
16424
  this.engine = ce;
16542
16425
  this._sign = 1;
16543
- this._rational = bignumPreferred(ce)
16544
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
16545
- : [1, 1];
16426
+ this._rational = bignumPreferred(ce) ? [BigInt(1), BigInt(1)] : [1, 1];
16546
16427
  // this._squareRootRational = this._rational;
16547
16428
  this._complex = Complex.ONE;
16548
16429
  this._bignum = ce._BIGNUM_ONE;
@@ -16573,16 +16454,14 @@ class Product {
16573
16454
  */
16574
16455
  addTerm(term) {
16575
16456
  console.assert(term.isCanonical);
16457
+ if (term.head === 'Multiply') {
16458
+ for (const t of term.ops)
16459
+ this.addTerm(t);
16460
+ return;
16461
+ }
16576
16462
  if (this._isCanonical) {
16577
16463
  if (term.isNothing)
16578
16464
  return;
16579
- // if (term.head === 'Sqrt') {
16580
- // const r = asRational(term.op1);
16581
- // if (r) {
16582
- // this._squareRootRational = mul(this._squareRootRational, r);
16583
- // return;
16584
- // }
16585
- // }
16586
16465
  // If we're calculation a canonical product, fold exact literals into
16587
16466
  // running terms
16588
16467
  if (term.numericValue !== null) {
@@ -16622,10 +16501,7 @@ class Product {
16622
16501
  num = num.neg();
16623
16502
  }
16624
16503
  if (num.isInteger())
16625
- this._rational = mul(this._rational, [
16626
- num,
16627
- this.engine._BIGNUM_ONE,
16628
- ]);
16504
+ this._rational = mul(this._rational, [bigint(num), BigInt(1)]);
16629
16505
  else if (bignumPreferred(this.engine))
16630
16506
  this._bignum = this._bignum.mul(num);
16631
16507
  else
@@ -16670,6 +16546,11 @@ class Product {
16670
16546
  rest = rest.op1;
16671
16547
  }
16672
16548
  }
16549
+ else if (rest.head === 'Divide') {
16550
+ this.addTerm(rest.op1);
16551
+ exponent = [-1, 1];
16552
+ rest = rest.op2;
16553
+ }
16673
16554
  // Look for the base, and add the exponent if already in the list of terms
16674
16555
  let found = false;
16675
16556
  for (const x of this._terms) {
@@ -16692,25 +16573,12 @@ class Product {
16692
16573
  let b = ce._BIGNUM_ONE;
16693
16574
  if (!isRationalOne(this._rational)) {
16694
16575
  if (isBigRational(this._rational))
16695
- b = this._rational[0].div(this._rational[1]);
16576
+ b = ce
16577
+ .bignum(this._rational[0].toString())
16578
+ .div(ce.bignum(this._rational[1].toString()));
16696
16579
  else
16697
16580
  b = ce.bignum(this._rational[0]).div(this._rational[1]);
16698
16581
  }
16699
- // if (!isRationalOne(this._squareRootRational)) {
16700
- // if (isBigRational(this._squareRootRational))
16701
- // b = b.mul(
16702
- // this._squareRootRational[0]
16703
- // .div(this._squareRootRational[1])
16704
- // .sqrt()
16705
- // );
16706
- // else
16707
- // b = b.mul(
16708
- // ce
16709
- // .bignum(this._squareRootRational[0])
16710
- // .div(this._squareRootRational[1])
16711
- // .sqrt()
16712
- // );
16713
- // }
16714
16582
  b = b.mul(this._bignum).mul(this._sign * this._number);
16715
16583
  if (this._complex.im !== 0) {
16716
16584
  const z = this._complex.mul(b.toNumber());
@@ -16727,21 +16595,10 @@ class Product {
16727
16595
  let n = 1;
16728
16596
  if (!isRationalOne(this._rational)) {
16729
16597
  if (isBigRational(this._rational))
16730
- n = this._rational[0].toNumber() / this._rational[1].toNumber();
16598
+ n = Number(this._rational[0]) / Number(this._rational[1]);
16731
16599
  else
16732
16600
  n = this._rational[0] / this._rational[1];
16733
16601
  }
16734
- // if (!isRationalOne(this._squareRootRational)) {
16735
- // if (isBigRational(this._squareRootRational))
16736
- // n *= Math.sqrt(
16737
- // this._squareRootRational[0].toNumber() /
16738
- // this._squareRootRational[1].toNumber()
16739
- // );
16740
- // else
16741
- // n *= Math.sqrt(
16742
- // this._squareRootRational[0] / this._squareRootRational[1]
16743
- // );
16744
- // }
16745
16602
  n *= this._sign * this._number * this._bignum.toNumber();
16746
16603
  if (this._complex.im !== 0) {
16747
16604
  const z = this._complex.mul(n);
@@ -16779,7 +16636,7 @@ class Product {
16779
16636
  if (mode === 'rational') {
16780
16637
  if (machineNumerator(this._rational) !== 1) {
16781
16638
  if (isBigRational(this._rational))
16782
- b = b.mul(this._rational[0]);
16639
+ b = b.mul(ce.bignum(this._rational[0]));
16783
16640
  else
16784
16641
  n *= this._rational[0];
16785
16642
  }
@@ -16798,19 +16655,6 @@ class Product {
16798
16655
  unitTerms.push(ce.number(this._rational));
16799
16656
  }
16800
16657
  }
16801
- // if (!isRationalOne(this._squareRootRational)) {
16802
- // if (mode === 'rational') {
16803
- // if (machineNumerator(this._squareRootRational) !== 1)
16804
- // unitTerms.push(ce.sqrt(ce.number(this._squareRootRational[0])));
16805
- // if (machineDenominator(this._squareRootRational) !== 1)
16806
- // xs.push({
16807
- // exponent: [-1, 1],
16808
- // terms: [ce.sqrt(ce.number(this._squareRootRational[1]))],
16809
- // });
16810
- // } else {
16811
- // unitTerms.push(ce.sqrt(ce.number(this._squareRootRational)));
16812
- // }
16813
- // }
16814
16658
  // Literal
16815
16659
  if (!b.equals(ce._BIGNUM_ONE))
16816
16660
  unitTerms.push(ce.number(b.mul(n)));
@@ -16882,8 +16726,7 @@ class Product {
16882
16726
  });
16883
16727
  if (groupedTerms === null)
16884
16728
  return ce._NAN;
16885
- let terms = termsAsExpressions(ce, groupedTerms);
16886
- terms = flattenOps(terms, 'Multiply') ?? terms;
16729
+ const terms = termsAsExpressions(ce, groupedTerms);
16887
16730
  if (terms.length === 0)
16888
16731
  return ce._ONE;
16889
16732
  if (terms.length === 1)
@@ -16898,8 +16741,7 @@ class Product {
16898
16741
  const xsNumerator = [];
16899
16742
  const xsDenominator = [];
16900
16743
  for (const x of xs)
16901
- if ((typeof x.exponent[0] === 'number' && x.exponent[0] >= 0) ||
16902
- (typeof x.exponent[0] !== 'number' && x.exponent[0].isPositive()))
16744
+ if (x.exponent[0] >= 0)
16903
16745
  xsNumerator.push(x);
16904
16746
  else
16905
16747
  xsDenominator.push({
@@ -16907,16 +16749,13 @@ class Product {
16907
16749
  terms: x.terms,
16908
16750
  });
16909
16751
  const ce = this.engine;
16910
- let numeratorTerms = termsAsExpressions(ce, xsNumerator);
16911
- numeratorTerms = flattenOps(numeratorTerms, 'Multiply') ?? numeratorTerms;
16752
+ const numeratorTerms = termsAsExpressions(ce, xsNumerator);
16912
16753
  let numerator = ce._ONE;
16913
16754
  if (numeratorTerms.length === 1)
16914
16755
  numerator = numeratorTerms[0];
16915
16756
  else if (numeratorTerms.length > 0)
16916
16757
  numerator = ce._fn('Multiply', numeratorTerms);
16917
- let denominatorTerms = termsAsExpressions(ce, xsDenominator);
16918
- denominatorTerms =
16919
- flattenOps(denominatorTerms, 'Multiply') ?? denominatorTerms;
16758
+ const denominatorTerms = termsAsExpressions(ce, xsDenominator);
16920
16759
  let denominator = ce._ONE;
16921
16760
  if (denominatorTerms.length === 1)
16922
16761
  denominator = denominatorTerms[0];
@@ -16930,7 +16769,7 @@ class Product {
16930
16769
  if (denominator.isOne)
16931
16770
  return numerator;
16932
16771
  if (denominator.isNegativeOne)
16933
- return this.engine.negate(numerator);
16772
+ return this.engine.neg(numerator);
16934
16773
  }
16935
16774
  return this.engine._fn('Divide', [numerator, denominator]);
16936
16775
  }
@@ -16958,35 +16797,23 @@ function degreeOrder(a, b) {
16958
16797
  const keyB = degreeKey(b.exponent);
16959
16798
  if (keyA !== keyB)
16960
16799
  return keyA - keyB;
16961
- if (isBigRational(a.exponent) && isBigRational(b.exponent)) {
16962
- return a.exponent[0]
16963
- .div(a.exponent[1])
16964
- .sub(b.exponent[0].div(b.exponent[1]))
16965
- .toNumber();
16966
- }
16967
- if (isBigRational(a.exponent) && isMachineRational(b.exponent)) {
16968
- return a.exponent[0]
16969
- .div(a.exponent[1])
16970
- .sub(b.exponent[0] / b.exponent[1])
16971
- .toNumber();
16972
- }
16973
- if (isMachineRational(a.exponent) && isBigRational(b.exponent)) {
16974
- return b.exponent[0]
16975
- .div(b.exponent[1])
16976
- .add(-a.exponent[0] / a.exponent[1])
16977
- .toNumber();
16978
- }
16979
- return (a.exponent[0] / a.exponent[1] -
16980
- b.exponent[0] / b.exponent[1]);
16800
+ const [a_n, a_d] = [
16801
+ machineNumerator(a.exponent),
16802
+ machineDenominator(a.exponent),
16803
+ ];
16804
+ const [b_n, b_d] = [
16805
+ machineNumerator(b.exponent),
16806
+ machineDenominator(b.exponent),
16807
+ ];
16808
+ return a_n / a_d - b_n / b_d;
16981
16809
  }
16982
16810
  function termsAsExpressions(ce, terms) {
16983
- terms = terms.sort(degreeOrder);
16984
- const result = terms.map((x) => {
16985
- const t = flattenOps(x.terms, 'Multiply') ?? x.terms;
16811
+ const result = terms.sort(degreeOrder).map((x) => {
16812
+ const t = flattenOps(x.terms, 'Multiply');
16986
16813
  const base = t.length <= 1 ? t[0] : ce._fn('Multiply', t.sort(order));
16987
16814
  if (isRationalOne(x.exponent))
16988
16815
  return base;
16989
- return ce.power(base, x.exponent);
16816
+ return ce.pow(base, x.exponent);
16990
16817
  });
16991
16818
  return flattenOps(result, 'Multiply') ?? result;
16992
16819
  }
@@ -17045,7 +16872,7 @@ function serializeJsonCanonicalFunction(ce, head, args, metadata) {
17045
16872
  if (exp === 2 && !exclusions.includes('Square'))
17046
16873
  return serializeJsonFunction(ce, 'Square', [args[0]], metadata);
17047
16874
  if (exp !== null && exp < 0 && !exclusions.includes('Divide')) {
17048
- return serializeJsonFunction(ce, 'Divide', [ce._ONE, exp === -1 ? args[0] : ce.power(args[0], -exp)], metadata);
16875
+ return serializeJsonFunction(ce, 'Divide', [ce._ONE, exp === -1 ? args[0] : ce.pow(args[0], -exp)], metadata);
17049
16876
  }
17050
16877
  const r = args[1].numericValue;
17051
16878
  if (!exclusions.includes('Sqrt') && r === 0.5)
@@ -17328,17 +17155,17 @@ function repeatingDecimals(ce, s) {
17328
17155
  for (let j = 0; j <= MAX_REPEATING_PATTERN_LENGTH; j++) {
17329
17156
  const repetend = fractionalPart.substring(i, i + j + 1);
17330
17157
  const times = Math.floor((fractionalPart.length - prefix.length) / repetend.length);
17331
- if (times > 1) {
17332
- if ((prefix + repetend.repeat(times + 1)).startsWith(fractionalPart)) {
17333
- // Found a repeating pattern
17334
- // Aktually...
17335
- if (repetend === '0') {
17336
- if (lastDigit === '0')
17337
- return wholepart + '.' + prefix + (exponent ?? '');
17338
- return s;
17339
- }
17340
- return (wholepart + '.' + prefix + '(' + repetend + ')' + (exponent ?? ''));
17158
+ if (times < 3)
17159
+ break;
17160
+ if ((prefix + repetend.repeat(times + 1)).startsWith(fractionalPart)) {
17161
+ // Found a repeating pattern
17162
+ // Aktually...
17163
+ if (repetend === '0') {
17164
+ if (lastDigit === '0')
17165
+ return wholepart + '.' + prefix + (exponent ?? '');
17166
+ return s;
17341
17167
  }
17168
+ return (wholepart + '.' + prefix + '(' + repetend + ')' + (exponent ?? ''));
17342
17169
  }
17343
17170
  }
17344
17171
  }
@@ -17818,10 +17645,202 @@ const SIMPLIFY_RULES = [];
17818
17645
  // return expr;
17819
17646
  // }
17820
17647
 
17648
+ function validateArgumentCount(ce, ops, count) {
17649
+ if (ops.length === count)
17650
+ return ops;
17651
+ const xs = [...ops.slice(0, count)];
17652
+ let i = Math.min(count, ops.length);
17653
+ while (i < count) {
17654
+ xs.push(ce.error('missing'));
17655
+ i += 1;
17656
+ }
17657
+ while (i < ops.length) {
17658
+ xs.push(ce.error('unexpected-argument', ops[i]));
17659
+ i += 1;
17660
+ }
17661
+ return xs;
17662
+ }
17663
+ /**
17664
+ * Validation of arguments is normally done by checking the signature of the
17665
+ * function vs the arguments of the expression. However, we have a fastpath
17666
+ * for some common operations (add, multiply, power, neg, etc...) that bypasses
17667
+ * the regular checks. This is its replacements. Since all those fastpath
17668
+ * functions are numeric (i.e. have numeric arguments and return a numeric
17669
+ * value), we do a simple numeric check of all arguments, and verify we have
17670
+ * the number of expected arguments.
17671
+ */
17672
+ function validateNumericArgs(ce, ops, count) {
17673
+ // @fastpath
17674
+ if (!ce.strict)
17675
+ return ops;
17676
+ let xs;
17677
+ if (count === undefined)
17678
+ xs = ops;
17679
+ else {
17680
+ xs = [];
17681
+ for (let i = 0; i <= Math.max(count - 1, ops.length - 1); i++) {
17682
+ if (i > count - 1)
17683
+ xs.push(ce.error('unexpected-argument', ops[i]));
17684
+ else
17685
+ xs.push(ops[i] !== undefined
17686
+ ? ce.box(ops[i])
17687
+ : ce.error(['missing', 'Number']));
17688
+ }
17689
+ }
17690
+ return xs.map((op) => !op.isValid || op.isNumber
17691
+ ? op
17692
+ : ce.error(['incompatible-domain', 'Number', op.domain], op));
17693
+ }
17694
+ /** Return `null` if the `ops` match the sig. Otherwise, return an array
17695
+ * of expressions indicating the mismatched arguments.
17696
+ *
17697
+ */
17698
+ function validateSignature(sig, ops, codomain) {
17699
+ const ce = sig.engine;
17700
+ // @fastpath
17701
+ if (!ce.strict)
17702
+ return ops;
17703
+ const opsDomain = ops.map((x) => x.domain);
17704
+ const targetSig = ce.domain([
17705
+ 'Function',
17706
+ ...opsDomain,
17707
+ codomain ?? 'Anything',
17708
+ ]);
17709
+ if (sig.isCompatible(targetSig))
17710
+ return null;
17711
+ //
17712
+ // There was a problem:
17713
+ // 1/ not enough arguments
17714
+ // 2/ too many arguments
17715
+ // 3/ incompatible argument domain
17716
+ //
17717
+ // Iterate over each arg, and replace with error expression when appropriate
17718
+ //
17719
+ const expectedArgs = sig.domainArgs.slice(0, -1);
17720
+ const count = Math.max(expectedArgs.length, opsDomain.length);
17721
+ let newOps = [];
17722
+ let rest = [...ops];
17723
+ for (let i = 0; i <= count - 1; i++)
17724
+ [newOps, rest] = validateNextArgument(ce, expectedArgs[i], newOps, rest);
17725
+ // Remove any 'Nothing' at the end
17726
+ while (newOps.length > 0 && newOps[newOps.length - 1].symbol === 'Nothing')
17727
+ newOps.pop();
17728
+ return newOps;
17729
+ }
17730
+ function validateArgument(ce, arg, dom) {
17731
+ if (dom === undefined)
17732
+ return ce.error('unexpected-argument', arg);
17733
+ if (arg === undefined)
17734
+ return ce.error(['missing', dom]);
17735
+ if (!arg.isValid)
17736
+ return arg;
17737
+ if (arg?.domain.isCompatible(dom))
17738
+ return arg;
17739
+ return ce.error(['incompatible-domain', dom, arg.domain], arg);
17740
+ }
17741
+ function validateNextArgument(ce, dom, matched, ops) {
17742
+ let next = ops.shift();
17743
+ if (dom === undefined)
17744
+ return [[...matched, ce.error('unexpected-argument', next)], ops];
17745
+ if (!Array.isArray(dom)) {
17746
+ if (!next)
17747
+ return [[...matched, ce.error(['missing', dom])], ops];
17748
+ if (!next.domain.isCompatible(dom)) {
17749
+ return [
17750
+ [...matched, ce.error(['incompatible-domain', dom, next.domain], next)],
17751
+ ops,
17752
+ ];
17753
+ }
17754
+ return [[...matched, next], ops];
17755
+ }
17756
+ const ctor = dom[0];
17757
+ if (next === undefined) {
17758
+ //
17759
+ // An expected argument is missing. Is that OK?
17760
+ //
17761
+ let valid = false;
17762
+ if (ctor === 'Union') {
17763
+ // If an `Union`, was `Nothing` an option?
17764
+ for (let k = 1; k <= dom.length - 1; k++) {
17765
+ if (dom[k] === 'Nothing') {
17766
+ valid = true;
17767
+ break;
17768
+ }
17769
+ }
17770
+ }
17771
+ else if (ctor === 'Maybe')
17772
+ valid = true;
17773
+ if (valid)
17774
+ return [[...matched, ce.symbol('Nothing')], ops];
17775
+ return [[...matched, ce.error(['missing', dom])], ops];
17776
+ }
17777
+ if (ctor === 'Union') {
17778
+ //
17779
+ // We expect one of several domains. Check if at least one matches
17780
+ //
17781
+ let found = false;
17782
+ for (let k = 1; k <= dom.length - 1; k++) {
17783
+ if (next.domain.isCompatible(dom[k])) {
17784
+ found = true;
17785
+ break;
17786
+ }
17787
+ }
17788
+ if (found)
17789
+ return [[...matched, next], ops];
17790
+ return [
17791
+ [...matched, ce.error(['incompatible-domain', dom, next.domain], next)],
17792
+ ops,
17793
+ ];
17794
+ }
17795
+ if (ctor === 'Sequence') {
17796
+ const seq = dom[1];
17797
+ if (!next || !next.domain.isCompatible(seq)) {
17798
+ return [
17799
+ [...matched, ce.error(['incompatible-domain', seq, next.domain], next)],
17800
+ ops,
17801
+ ];
17802
+ }
17803
+ let done = false;
17804
+ const result = [...matched, next];
17805
+ while (!done) {
17806
+ next = ops.shift();
17807
+ if (!next)
17808
+ done = false;
17809
+ else if (!next.domain.isCompatible(seq)) {
17810
+ ops.unshift(next);
17811
+ done = false;
17812
+ }
17813
+ else
17814
+ result.push(next);
17815
+ }
17816
+ return [result, ops];
17817
+ }
17818
+ if (ctor === 'Maybe') {
17819
+ if (next === undefined || next.symbol === 'Nothing')
17820
+ return [[...matched, ce.symbol('Nothing')], ops];
17821
+ return validateNextArgument(ce, dom[1], matched, [next, ...ops]);
17822
+ }
17823
+ console.error('Unhandled ctor', ctor);
17824
+ return [[...matched, next], ops];
17825
+ }
17826
+ function validateArguments(ce, args, doms) {
17827
+ // Do a quick check for the common case where everything is as expected.
17828
+ // Avoid allocating arrays and objects
17829
+ if (args.length === doms.length &&
17830
+ args.every((x, i) => x.domain.isCompatible(doms[i])))
17831
+ return args;
17832
+ const xs = [];
17833
+ for (let i = 0; i <= doms.length - 1; i++)
17834
+ xs.push(validateArgument(ce, args[i], doms[i]));
17835
+ for (let i = doms.length; i <= args.length - 1; i++)
17836
+ xs.push(ce.error('unexpected-argument', args[i]));
17837
+ return xs;
17838
+ }
17839
+
17821
17840
  /**
17822
17841
  * Considering an old (existing) expression and a new (simplified) one,
17823
17842
  * return the cheapest of the two, with a bias towards the new (which can
17824
- * actually be a bit mor expensive than the old one, and still be picked).
17843
+ * actually be a bit more expensive than the old one, and still be picked).
17825
17844
  */
17826
17845
  function cheapest(oldExpr, newExpr) {
17827
17846
  if (newExpr === null || newExpr === undefined)
@@ -17831,9 +17850,14 @@ function cheapest(oldExpr, newExpr) {
17831
17850
  const ce = oldExpr.engine;
17832
17851
  const boxedNewExpr = ce.box(newExpr);
17833
17852
  if (ce.costFunction(boxedNewExpr) <= 1.7 * ce.costFunction(oldExpr)) {
17853
+ // console.log(
17854
+ // 'Picked new' + boxedNewExpr.toString() + ' over ' + oldExpr.toString()
17855
+ // );
17834
17856
  return boxedNewExpr;
17835
17857
  }
17836
- console.log('Cheapest: Rejected ', boxedNewExpr.toString(), 'in favor of ', oldExpr.toString()); // @debug
17858
+ // console.log(
17859
+ // 'Picked old ' + oldExpr.toString() + ' over ' + newExpr.toString()
17860
+ // );
17837
17861
  return oldExpr;
17838
17862
  }
17839
17863
  /**
@@ -18340,10 +18364,16 @@ class BoxedFunction extends AbstractBoxedExpression {
18340
18364
  //
18341
18365
  if (!this.isValid)
18342
18366
  return this;
18343
- if (!this.isCanonical)
18344
- return this.canonical.simplify(options);
18367
+ if (!this.isCanonical) {
18368
+ const canonical = this.canonical;
18369
+ if (!canonical.isCanonical || !canonical.isValid)
18370
+ return this;
18371
+ return canonical.simplify(options);
18372
+ }
18345
18373
  //
18346
18374
  // 2/ Simplify the applicable operands
18375
+ // @todo not clear if this is always the best strategy. Might be better to
18376
+ // defer to the handler.
18347
18377
  //
18348
18378
  const def = this.functionDefinition;
18349
18379
  const tail = holdMap(this._ops, def?.hold ?? 'none', def?.associative ? def.name : '', (x) => x.simplify(options));
@@ -18371,6 +18401,9 @@ class BoxedFunction extends AbstractBoxedExpression {
18371
18401
  }
18372
18402
  if (!expr)
18373
18403
  expr = this.engine.fn(this._head, tail);
18404
+ else
18405
+ expr = cheapest(this.engine.fn(this._head, tail), expr);
18406
+ expr = cheapest(this, expr);
18374
18407
  //
18375
18408
  // 5/ Apply rules, until no rules can be applied
18376
18409
  //
@@ -18405,7 +18438,7 @@ class BoxedFunction extends AbstractBoxedExpression {
18405
18438
  // console.error('Iteration Limit reached simplifying', this.toJSON());
18406
18439
  // }
18407
18440
  // @debug-end
18408
- return expr;
18441
+ return cheapest(this, expr);
18409
18442
  }
18410
18443
  evaluate(options) {
18411
18444
  //
@@ -18413,8 +18446,12 @@ class BoxedFunction extends AbstractBoxedExpression {
18413
18446
  //
18414
18447
  if (!this.isValid)
18415
18448
  return this;
18416
- if (!this.isCanonical)
18417
- return this.canonical.evaluate(options);
18449
+ if (!this.isCanonical) {
18450
+ const canonical = this.canonical;
18451
+ if (!canonical.isCanonical || !canonical.isValid)
18452
+ return this;
18453
+ return canonical.evaluate(options);
18454
+ }
18418
18455
  //
18419
18456
  // 2/ Evaluate the applicable operands
18420
18457
  //
@@ -18460,8 +18497,12 @@ class BoxedFunction extends AbstractBoxedExpression {
18460
18497
  return this._numericValue;
18461
18498
  if (this.engine.strict && !this.isValid)
18462
18499
  return this;
18463
- if (!this.isCanonical)
18464
- return this.canonical.N(options);
18500
+ if (!this.isCanonical) {
18501
+ const canonical = this.canonical;
18502
+ if (!canonical.isCanonical || !canonical.isValid)
18503
+ return this;
18504
+ return canonical.N(options);
18505
+ }
18465
18506
  //
18466
18507
  // 2/ Evaluate the applicable operands
18467
18508
  //
@@ -18510,11 +18551,11 @@ class BoxedFunction extends AbstractBoxedExpression {
18510
18551
  function makeNumericFunction(ce, head, semiOps, metadata) {
18511
18552
  let ops = [];
18512
18553
  if (head === 'Add' || head === 'Multiply')
18513
- ops = validateNumericArgs(ce, semiOps);
18554
+ ops = validateNumericArgs(ce, flattenOps(flattenSequence(ce.canonical(semiOps)), head));
18514
18555
  else if (head === 'Negate' || head === 'Square' || head === 'Sqrt')
18515
- ops = validateNumericArgs(ce, semiOps, 1);
18556
+ ops = validateNumericArgs(ce, flattenSequence(ce.canonical(semiOps)), 1);
18516
18557
  else if (head === 'Divide' || head === 'Power')
18517
- ops = validateNumericArgs(ce, semiOps, 2);
18558
+ ops = validateNumericArgs(ce, flattenSequence(ce.canonical(semiOps)), 2);
18518
18559
  else
18519
18560
  return null;
18520
18561
  // If some of the arguments are not valid, make a non-canonical expression
@@ -18527,25 +18568,23 @@ function makeNumericFunction(ce, head, semiOps, metadata) {
18527
18568
  if (head === 'Add')
18528
18569
  return ce.add(ops, metadata);
18529
18570
  if (head === 'Negate')
18530
- return ce.negate(ops[0] ?? ce.error('missing'), metadata);
18571
+ return ce.neg(ops[0] ?? ce.error('missing'), metadata);
18531
18572
  if (head === 'Multiply')
18532
18573
  return ce.mul(ops, metadata);
18533
18574
  if (head === 'Divide')
18534
- return ce.divide(ops[0], ops[1], metadata);
18575
+ return ce.div(ops[0], ops[1], metadata);
18535
18576
  if (head === 'Power')
18536
- return ce.power(ops[0], ops[1], metadata);
18577
+ return ce.pow(ops[0], ops[1], metadata);
18537
18578
  if (head === 'Square')
18538
- return ce.power(ops[0], ce.number(2), metadata);
18579
+ return ce.pow(ops[0], ce.number(2), metadata);
18539
18580
  if (head === 'Sqrt') {
18540
18581
  const op = ops[0].canonical;
18541
18582
  if (isRational(op.numericValue))
18542
18583
  return new BoxedFunction(ce, 'Sqrt', [op], { metadata, canonical: true });
18543
- return ce.power(op, ce.number([1, 2]), metadata);
18584
+ return ce.pow(op, ce._HALF, metadata);
18544
18585
  }
18545
- if (head === 'Pair')
18546
- return ce.pair(ops[0], ops[1], metadata);
18547
- if (head === 'Tuple')
18548
- return ce.tuple(ops, metadata);
18586
+ // if (head === 'Pair') return ce.pair(ops[0], ops[1], metadata);
18587
+ // if (head === 'Tuple') return ce.tuple(ops, metadata);
18549
18588
  return null;
18550
18589
  }
18551
18590
  function makeCanonicalFunction(ce, head, ops, metadata) {
@@ -18592,9 +18631,14 @@ function makeCanonicalFunction(ce, head, ops, metadata) {
18592
18631
  // the expression.
18593
18632
  //
18594
18633
  if (sig.canonical) {
18595
- const result = sig.canonical(ce, xs);
18596
- if (result)
18597
- return result;
18634
+ try {
18635
+ const result = sig.canonical(ce, xs);
18636
+ if (result)
18637
+ return result;
18638
+ }
18639
+ catch (e) {
18640
+ console.error(e);
18641
+ }
18598
18642
  return new BoxedFunction(ce, head, xs, { metadata, canonical: false });
18599
18643
  }
18600
18644
  //
@@ -18603,7 +18647,7 @@ function makeCanonicalFunction(ce, head, ops, metadata) {
18603
18647
  //
18604
18648
  xs = flattenSequence(xs);
18605
18649
  if (def.associative)
18606
- xs = flattenOps(xs, head) ?? xs;
18650
+ xs = flattenOps(xs, head);
18607
18651
  // If some of the arguments are not valid, can't make a canonical expression
18608
18652
  if (!xs.every((x) => x.isValid))
18609
18653
  return new BoxedFunction(ce, head, xs, { metadata, canonical: false });
@@ -18667,12 +18711,31 @@ function holdMap(xs, skip, associativeHead, f) {
18667
18711
  if (xs.length === 0)
18668
18712
  return [];
18669
18713
  // f(a, f(b, c), d) -> f(a, b, c, d)
18670
- if (associativeHead)
18671
- xs = flattenOps(xs, associativeHead) ?? xs;
18672
- const result = [];
18714
+ xs = flattenOps(xs, associativeHead);
18673
18715
  //
18674
18716
  // Apply the hold as necessary
18675
18717
  //
18718
+ // @fastpath
18719
+ if (skip === 'all')
18720
+ return xs;
18721
+ if (skip === 'none') {
18722
+ const result = [];
18723
+ for (const x of xs) {
18724
+ const h = x.head;
18725
+ if (h === 'Hold')
18726
+ result.push(x);
18727
+ else {
18728
+ const op = h === 'ReleaseHold' ? x.op1 : x;
18729
+ if (op) {
18730
+ const y = f(op);
18731
+ if (y !== null)
18732
+ result.push(y);
18733
+ }
18734
+ }
18735
+ }
18736
+ return flattenOps(result, associativeHead);
18737
+ }
18738
+ const result = [];
18676
18739
  for (let i = 0; i < xs.length; i++) {
18677
18740
  if (xs[i].head === 'Hold') {
18678
18741
  result.push(xs[i]);
@@ -18692,9 +18755,7 @@ function holdMap(xs, skip, associativeHead, f) {
18692
18755
  }
18693
18756
  }
18694
18757
  }
18695
- if (associativeHead)
18696
- return flattenOps(result, associativeHead) ?? result;
18697
- return result;
18758
+ return flattenOps(result, associativeHead);
18698
18759
  }
18699
18760
  function applicable(skip, count, index) {
18700
18761
  if (skip === 'all')
@@ -18781,7 +18842,7 @@ function inferNumericDomain(value) {
18781
18842
  //
18782
18843
  // 4. Is it a rational? (machine or bignum)
18783
18844
  //
18784
- if (Array.isArray(value)) {
18845
+ if (isRational(value)) {
18785
18846
  const [numer, denom] = value;
18786
18847
  // The value is a rational number
18787
18848
  console.assert(typeof numer !== 'number' ||
@@ -18905,8 +18966,8 @@ class BoxedNumber extends AbstractBoxedExpression {
18905
18966
  const [n, d] = value;
18906
18967
  console.assert(typeof n !== 'number' ||
18907
18968
  (Number.isInteger(n) && Number.isInteger(d) && d !== n && d !== 1));
18908
- console.assert(!(n instanceof Decimal && d instanceof Decimal) ||
18909
- (n.isInteger() && d.isInteger() && !d.eq(n) && !d.eq(1)));
18969
+ console.assert(!(typeof n === 'bigint' && typeof d == 'bigint') ||
18970
+ (d !== n && d !== BigInt(1)));
18910
18971
  if (options?.canonical ?? true) {
18911
18972
  this._value = canonicalNumber(ce, value);
18912
18973
  this._isCanonical = true;
@@ -19304,7 +19365,7 @@ class BoxedNumber extends AbstractBoxedExpression {
19304
19365
  typeof denom === 'number' &&
19305
19366
  !bignumPreferred(ce))
19306
19367
  return ce.number(numer / denom);
19307
- return ce.number(ce.bignum(numer).div(denom));
19368
+ return ce.number(ce.bignum(numer).div(ce.bignum(denom)));
19308
19369
  }
19309
19370
  }
19310
19371
  function canonicalNumber(ce, value) {
@@ -19315,31 +19376,30 @@ function canonicalNumber(ce, value) {
19315
19376
  return value.toNumber();
19316
19377
  if (!isRational(value))
19317
19378
  return value;
19318
- let [n, d] = value;
19319
- if (n instanceof Decimal && d instanceof Decimal) {
19320
- if (isInMachineRange(n) && isInMachineRange(d))
19321
- [n, d] = [n.toNumber(), d.toNumber()];
19379
+ value = reducedRational(value);
19380
+ if (isBigRational(value)) {
19381
+ let [n, d] = value;
19382
+ if (n > Number.MIN_SAFE_INTEGER &&
19383
+ n < Number.MAX_SAFE_INTEGER &&
19384
+ d > Number.MIN_SAFE_INTEGER &&
19385
+ d < Number.MAX_SAFE_INTEGER)
19386
+ value = [Number(n), Number(d)];
19322
19387
  else {
19323
- if (n.isNaN() || d.isNaN())
19324
- return NaN;
19325
- [n, d] = reducedRational([n, d]);
19326
- if (d.isNegative())
19327
- [n, d] = [n.neg(), d.neg()];
19328
- if (d.eq(ce._BIGNUM_ONE))
19329
- return n;
19330
- if (d.isZero()) {
19331
- if (n.isZero() || !n.isFinite())
19388
+ if (d < 0)
19389
+ [n, d] = [-n, -d];
19390
+ if (d === BigInt(1))
19391
+ return ce.bignum(n);
19392
+ if (d === BigInt(0)) {
19393
+ if (n === d)
19332
19394
  return NaN;
19333
- if (n.isNegative())
19334
- return -Infinity;
19335
- return +Infinity;
19395
+ return n < 0 ? -Infinity : +Infinity;
19336
19396
  }
19337
19397
  return [n, d];
19338
19398
  }
19339
19399
  }
19400
+ let [n, d] = value;
19340
19401
  if (Number.isNaN(n) || Number.isNaN(d))
19341
19402
  return NaN;
19342
- [n, d] = reducedRational([n, d]);
19343
19403
  if (d < 0)
19344
19404
  [n, d] = [-n, -d];
19345
19405
  if (d === 1)
@@ -19474,21 +19534,26 @@ function boxNumber(ce, num, options) {
19474
19534
  //
19475
19535
  // Do we have a rational or big rational?
19476
19536
  //
19537
+ if (Array.isArray(num) &&
19538
+ num.length === 2 &&
19539
+ num[0] instanceof Decimal &&
19540
+ num[1] instanceof Decimal) {
19541
+ if (!num[0].isInteger() || !num[1].isInteger())
19542
+ throw new Error('Array argument to `boxNumber()` should be two integers');
19543
+ num = [bigint(num[0].toString()), bigint(num[1].toString())];
19544
+ }
19477
19545
  if (isRational(num)) {
19478
19546
  if (num.length !== 2)
19479
19547
  throw new Error('Array argument to `boxNumber()` should be two integers or two bignums');
19480
19548
  const [n, d] = num;
19481
- if (n instanceof Decimal && d instanceof Decimal) {
19482
- // We have a big rational
19483
- if (!n.isInteger() || !d.isInteger())
19484
- throw new Error('Array argument to `boxNumber()` should be two integers');
19485
- if (n.eq(d))
19486
- return d.isZero() ? ce._NAN : ce._ONE;
19487
- if (d.eq(1))
19549
+ if (typeof n === 'bigint' && typeof d === 'bigint') {
19550
+ if (n === d)
19551
+ return d === BigInt(0) ? ce._NAN : ce._ONE;
19552
+ if (d === BigInt(1))
19488
19553
  return ce.number(n, options);
19489
- if (d.eq(-1))
19490
- return ce.number(n.negated(), options);
19491
- if (n.eq(1) && d.eq(2))
19554
+ if (d === BigInt(-1))
19555
+ return ce.number(-n, options);
19556
+ if (n === BigInt(1) && d === BigInt(2))
19492
19557
  return ce._HALF;
19493
19558
  return new BoxedNumber(ce, [n, d], options);
19494
19559
  }
@@ -19633,16 +19698,16 @@ function boxFunction(ce, head, ops, options) {
19633
19698
  if ((head === 'Divide' || head === 'Rational') && ops.length === 2) {
19634
19699
  if (ops[0] instanceof AbstractBoxedExpression &&
19635
19700
  ops[1] instanceof AbstractBoxedExpression) {
19636
- const [n, d] = [asBignum(ops[0]), asBignum(ops[1])];
19637
- if (n?.isInteger() && d?.isInteger())
19701
+ const [n, d] = [asBigint(ops[0]), asBigint(ops[1])];
19702
+ if (n && d)
19638
19703
  return ce.number([n, d], options);
19639
19704
  }
19640
19705
  else {
19641
19706
  const [n, d] = [
19642
- bignumValue(ce, ops[0]),
19643
- bignumValue(ce, ops[1]),
19707
+ bigintValue(ce, ops[0]),
19708
+ bigintValue(ce, ops[1]),
19644
19709
  ];
19645
- if (n?.isInteger() && d?.isInteger())
19710
+ if (n && d)
19646
19711
  return ce.number([n, d], options);
19647
19712
  }
19648
19713
  head = 'Divide';
@@ -19777,19 +19842,17 @@ function box(ce, expr, options) {
19777
19842
  // This wasn't a valid rational, turn it into a `Divide`
19778
19843
  return boxFunction(ce, 'Divide', expr, options);
19779
19844
  }
19780
- if (isBigRational(expr)) {
19781
- if (expr[0].isInteger() && expr[1].isInteger())
19782
- return ce.number(expr);
19783
- // This wasn't a valid rational, turn it into a `Divide`
19784
- return boxFunction(ce, 'Divide', expr, options);
19785
- }
19845
+ if (isBigRational(expr))
19846
+ return ce.number(expr);
19786
19847
  if (typeof expr[0] === 'string')
19787
19848
  return boxFunction(ce, expr[0], expr.slice(1), options);
19788
19849
  // It's a function with a head expression
19789
19850
  // Try to evaluate to something simpler
19790
19851
  const ops = expr.slice(1).map((x) => box(ce, x, options));
19791
- const head = apply$1(box(ce, expr[0], options), ops);
19792
- return head.symbol ? new BoxedFunction(ce, head.symbol, ops) : head;
19852
+ const head = box(ce, expr[0], options);
19853
+ if (head.symbol)
19854
+ return new BoxedFunction(ce, head.symbol, ops);
19855
+ return apply$1(head, ops);
19793
19856
  }
19794
19857
  //
19795
19858
  // Box a number (other than a rational)
@@ -19867,20 +19930,21 @@ function lngamma(c) {
19867
19930
 
19868
19931
  class Sum {
19869
19932
  constructor(ce, xs, options) {
19933
+ // If `false`, the running sums are not calculated
19934
+ this._isCanonical = true;
19870
19935
  this._imaginary = 0; // integers only
19871
19936
  this._posInfinityCount = 0;
19872
19937
  this._negInfinityCount = 0;
19938
+ this._naNCount = 0;
19939
+ // Each term is factored as the product of a rational and an expression
19940
+ // For now, only rationals are factored, so `1.2x + 2.5x` are not combined.
19873
19941
  this._terms = [];
19874
- // If `false`, the running sums are not calculated
19875
- this._isCanonical = true;
19876
19942
  options ?? (options = {});
19877
19943
  if (!('canonical' in options))
19878
19944
  options.canonical = true;
19879
19945
  this._isCanonical = options.canonical;
19880
19946
  this.engine = ce;
19881
- this._rational = bignumPreferred(ce)
19882
- ? [ce._BIGNUM_ZERO, ce._BIGNUM_ONE]
19883
- : [0, 1];
19947
+ this._rational = bignumPreferred(ce) ? [BigInt(0), BigInt(1)] : [0, 1];
19884
19948
  this._bignum = ce._BIGNUM_ZERO;
19885
19949
  this._number = 0;
19886
19950
  if (xs)
@@ -19896,7 +19960,8 @@ class Sum {
19896
19960
  this._number === 0 &&
19897
19961
  this._bignum.isZero() &&
19898
19962
  this._negInfinityCount === 0 &&
19899
- this._posInfinityCount === 0);
19963
+ this._posInfinityCount === 0 &&
19964
+ this._naNCount === 0);
19900
19965
  }
19901
19966
  /**
19902
19967
  * Add a term to the sum.
@@ -19911,9 +19976,13 @@ class Sum {
19911
19976
  * -> [['x', [3, 1]], ['y', [1, 5]]]
19912
19977
  */
19913
19978
  addTerm(term, c) {
19979
+ if (term.isNothing)
19980
+ return;
19981
+ if (term.isNaN || (term.isImaginary && !complexAllowed(this.engine))) {
19982
+ this._naNCount += 1;
19983
+ return;
19984
+ }
19914
19985
  if (this._isCanonical) {
19915
- if (term.isNothing)
19916
- return;
19917
19986
  if (term.numericValue !== null) {
19918
19987
  if (term.isInfinity) {
19919
19988
  if (term.isPositive)
@@ -19961,7 +20030,11 @@ class Sum {
19961
20030
  else if (isMachineRational(c))
19962
20031
  this._imaginary += (im * c[0]) / c[1];
19963
20032
  else
19964
- this._imaginary += c[0].mul(im).div(c[1]).toNumber();
20033
+ this._imaginary += this.engine
20034
+ .bignum(c[0])
20035
+ .mul(im)
20036
+ .div(this.engine.bignum(c[1]))
20037
+ .toNumber();
19965
20038
  im = 0;
19966
20039
  }
19967
20040
  if (re === 0 && im === 0)
@@ -20017,25 +20090,27 @@ class Sum {
20017
20090
  }
20018
20091
  terms(mode) {
20019
20092
  const ce = this.engine;
20093
+ if (this._naNCount > 0)
20094
+ return [ce._NAN];
20095
+ if (this._imaginary !== 0 && !complexAllowed(ce))
20096
+ return [ce._NAN];
20020
20097
  if (this._posInfinityCount > 0 && this._negInfinityCount > 0)
20021
20098
  return [ce._NAN];
20022
20099
  if (this._posInfinityCount > 0)
20023
20100
  return [ce._POSITIVE_INFINITY];
20024
20101
  if (this._negInfinityCount > 0)
20025
20102
  return [ce._NEGATIVE_INFINITY];
20026
- if (this._imaginary !== 0 && !complexAllowed(ce))
20027
- return [ce._NAN];
20028
20103
  const xs = [];
20029
20104
  for (const { coef, term } of this._terms) {
20030
20105
  if (!isRationalZero(coef)) {
20031
20106
  if (isRationalOne(coef))
20032
20107
  xs.push(term);
20033
20108
  else if (isRationalNegativeOne(coef))
20034
- xs.push(ce.negate(term));
20109
+ xs.push(ce.neg(term));
20035
20110
  else if (machineDenominator(coef) === 1)
20036
20111
  xs.push(ce.mul([ce.number(coef[0]), term]));
20037
20112
  else if (machineNumerator(coef) === 1)
20038
- xs.push(ce.divide(term, ce.number(coef[1])));
20113
+ xs.push(ce.div(term, ce.number(coef[1])));
20039
20114
  else
20040
20115
  xs.push(ce.mul([ce.number(coef), term]));
20041
20116
  }
@@ -20044,7 +20119,7 @@ class Sum {
20044
20119
  if (bignumPreferred(this.engine)) {
20045
20120
  let sum = this._bignum.add(this._number);
20046
20121
  if (!isRationalZero(this._rational))
20047
- sum = sum.add(ce.bignum(this._rational[0]).div(this._rational[1]));
20122
+ sum = sum.add(ce.bignum(this._rational[0]).div(ce.bignum(this._rational[1])));
20048
20123
  if (this._imaginary !== 0)
20049
20124
  xs.push(ce.number(ce.complex(sum.toNumber(), this._imaginary)));
20050
20125
  else if (ce.chop(sum) !== 0)
@@ -20082,7 +20157,7 @@ class Sum {
20082
20157
  xs.push(ce.number(this._number));
20083
20158
  }
20084
20159
  }
20085
- return flattenOps(xs, 'Add') ?? xs;
20160
+ return flattenOps(xs, 'Add');
20086
20161
  }
20087
20162
  asExpression(mode) {
20088
20163
  const ce = this.engine;
@@ -21015,7 +21090,6 @@ function isEqual(lhs, rhs) {
21015
21090
  * */
21016
21091
  function canonicalAdd(ce, ops) {
21017
21092
  console.assert(ops.every((x) => x.isCanonical));
21018
- ops = flattenOps(flattenSequence(ops.map((x) => x.canonical)), 'Add') ?? ops;
21019
21093
  // Remove literal 0
21020
21094
  ops = ops.filter((x) => x.numericValue === null || !x.isZero);
21021
21095
  if (ops.length === 0)
@@ -21100,7 +21174,6 @@ function evalAdd(ce, ops, mode = 'evaluate') {
21100
21174
  if (!arg.isExact)
21101
21175
  mode = 'N';
21102
21176
  }
21103
- console.assert(flattenOps(ops, 'Add') === null);
21104
21177
  if (mode === 'N')
21105
21178
  ops = ops.map((x) => x.N());
21106
21179
  else
@@ -21163,7 +21236,7 @@ function evalSummation(ce, expr, range, mode) {
21163
21236
  lower = asSmallInteger(range.op2) ?? 1;
21164
21237
  upper = asSmallInteger(range.op3) ?? MAX_ITERATION;
21165
21238
  }
21166
- if (lower >= upper || upper - lower >= MAX_SYMBOLIC_TERMS)
21239
+ if (mode !== 'N' && (lower >= upper || upper - lower >= MAX_SYMBOLIC_TERMS))
21167
21240
  return undefined;
21168
21241
  const savedContext = ce.context;
21169
21242
  ce.context = fn.scope ?? ce.context;
@@ -21193,9 +21266,7 @@ function evalSummation(ce, expr, range, mode) {
21193
21266
  ce.context = savedContext;
21194
21267
  return ce.add(terms).evaluate();
21195
21268
  }
21196
- let sum = bignumPreferred(ce)
21197
- ? [ce._BIGNUM_ZERO, ce._BIGNUM_ONE]
21198
- : [0, 1];
21269
+ let sum = bignumPreferred(ce) ? [BigInt(1), BigInt(1)] : [0, 1];
21199
21270
  if (!fn.scope)
21200
21271
  for (let i = lower; i <= upper; i++) {
21201
21272
  const term = fn.N();
@@ -21216,7 +21287,7 @@ function evalSummation(ce, expr, range, mode) {
21216
21287
  ce.context = savedContext;
21217
21288
  if (isMachineRational(sum))
21218
21289
  return ce.number(sum[0] / sum[1]);
21219
- return ce.number(sum[0].div(sum[1]));
21290
+ return ce.number(ce.bignum(sum[0]).div(ce.bignum(sum[1])));
21220
21291
  }
21221
21292
 
21222
21293
  // @todo: replace usage with asCoefficient():
@@ -21237,34 +21308,35 @@ function makePositive(expr) {
21237
21308
  return [-1, ce.number(ce.complex(-n.re, -n.im))];
21238
21309
  if (isMachineRational(n) && n[0] < 0)
21239
21310
  return [-1, ce.number([-n[0], n[1]])];
21240
- if (isBigRational(n) && n[0].isNegative())
21241
- return [-1, ce.number([n[0].neg(), n[1]])];
21311
+ if (isBigRational(n) && n[0] < 0)
21312
+ return [-1, ce.number([-n[0], n[1]])];
21242
21313
  return [1, expr];
21243
21314
  }
21244
21315
  function apply(expr, fn, bigFn, complexFn) {
21245
21316
  const n = expr.numericValue;
21317
+ const ce = expr.engine;
21246
21318
  console.assert(n !== null);
21247
21319
  if (typeof n === 'number') {
21248
- if (bignumPreferred(expr.engine) && bigFn)
21249
- return expr.engine.chop(bigFn(expr.engine.bignum(n)));
21250
- return expr.engine.chop(fn(n));
21320
+ if (bignumPreferred(ce) && bigFn)
21321
+ return ce.chop(bigFn(ce.bignum(n)));
21322
+ return ce.chop(fn(n));
21251
21323
  }
21252
21324
  if (n instanceof Decimal)
21253
- return expr.engine.chop(bigFn?.(n) ?? fn(n.toNumber()));
21325
+ return ce.chop(bigFn?.(n) ?? fn(n.toNumber()));
21254
21326
  if (isMachineRational(n)) {
21255
- if (!bignumPreferred(expr.engine) || !bigFn)
21256
- return expr.engine.chop(fn(n[0] / n[1]));
21257
- return expr.engine.chop(bigFn(expr.engine.bignum(n[0]).div(n[1])));
21327
+ if (!bignumPreferred(ce) || !bigFn)
21328
+ return ce.chop(fn(n[0] / n[1]));
21329
+ return ce.chop(bigFn(ce.bignum(n[0]).div(n[1])));
21258
21330
  }
21259
21331
  if (isBigRational(n)) {
21260
21332
  if (bigFn)
21261
- return expr.engine.chop(bigFn(n[0].div(n[1])));
21262
- return expr.engine.chop(fn(n[0].toNumber() / n[1].toNumber()));
21333
+ return ce.chop(bigFn(ce.bignum(n[0]).div(ce.bignum(n[1]))));
21334
+ return ce.chop(fn(Number(n[0]) / Number(n[1])));
21263
21335
  }
21264
21336
  if (n instanceof Complex) {
21265
- if (!complexFn || !complexAllowed(expr.engine))
21337
+ if (!complexFn || !complexAllowed(ce))
21266
21338
  return NaN;
21267
- return expr.engine.chop(complexFn(n));
21339
+ return ce.chop(complexFn(n));
21268
21340
  }
21269
21341
  debugger;
21270
21342
  return NaN;
@@ -21289,14 +21361,14 @@ function apply2(expr1, expr2, fn, bigFn, complexFn) {
21289
21361
  if (m1 instanceof Decimal)
21290
21362
  b1 = m1;
21291
21363
  else if (isBigRational(m1))
21292
- b1 = m1[0].div(m1[1]);
21364
+ b1 = ce.bignum(m1[0]).div(ce.bignum(m1[1]));
21293
21365
  else if (m1 !== null && typeof m1 === 'number')
21294
21366
  b1 = ce.bignum(m1);
21295
21367
  let b2 = undefined;
21296
21368
  if (m2 instanceof Decimal)
21297
21369
  b2 = m2;
21298
21370
  else if (isBigRational(m2))
21299
- b2 = m2[0].div(m2[1]);
21371
+ b1 = ce.bignum(m2[0]).div(ce.bignum(m2[1]));
21300
21372
  else if (m2 !== null && typeof m2 === 'number')
21301
21373
  b2 = ce.bignum(m2);
21302
21374
  if (b1 && b2)
@@ -21317,17 +21389,17 @@ function apply2N(expr1, expr2, fn, bigFn, complexFn) {
21317
21389
 
21318
21390
  /**
21319
21391
  *
21320
- * Return `null` if there is no canonicalization necessary and the result is
21321
- * simply `ce._fn('Power', [base, exponent])`
21322
21392
  */
21323
21393
  function canonicalPower(ce, base, exponent, metadata) {
21324
- base = validateArgument(ce, base?.canonical, 'Number');
21325
- exponent = validateArgument(ce, exponent?.canonical, 'Number');
21326
21394
  if (exponent.symbol === 'ComplexInfinity')
21327
21395
  return ce._NAN;
21396
+ if (exponent.isZero)
21397
+ return ce._ONE;
21398
+ if (exponent.isOne)
21399
+ return base;
21400
+ if (exponent.isNegativeOne)
21401
+ return ce.inv(base);
21328
21402
  if (exponent.numericValue !== null) {
21329
- if (exponent.isZero)
21330
- return ce._ONE;
21331
21403
  if (base.numericValue !== null) {
21332
21404
  const numBase = asFloat(base);
21333
21405
  //
@@ -21346,27 +21418,9 @@ function canonicalPower(ce, base, exponent, metadata) {
21346
21418
  if (exponent.isNegative)
21347
21419
  return ce._COMPLEX_INFINITY; // Unsigned Infinity...
21348
21420
  }
21349
- if (exponent.isOne)
21350
- return base;
21351
- if (exponent.isNegativeOne) {
21352
- // x^(-1)
21353
- // if (base.isOne) return ce._ONE;
21354
- // if (base.isNegativeOne) return ce._NEGATIVE_ONE;
21355
- if (numBase === 1)
21356
- return ce._ONE;
21357
- if (numBase === -1)
21358
- return ce._NEGATIVE_ONE;
21359
- if (base.isInfinity)
21360
- return ce._ZERO;
21361
- const r = base.numericValue;
21362
- if (typeof r === 'number' && Number.isInteger(r))
21363
- return ce.number([1, r], { metadata });
21364
- if (r instanceof Decimal && r.isInteger())
21365
- return ce.number([ce._BIGNUM_ONE, r], { metadata });
21366
- if (isRational(r))
21367
- return ce.number(inverse(r), { metadata });
21368
- return ce._fn('Power', [base, ce._NEGATIVE_ONE], metadata);
21369
- }
21421
+ // x^(-1)
21422
+ if (exponent.isNegativeOne)
21423
+ return ce.inv(base);
21370
21424
  // x^{0.5}, x^{1/2} -> Square Root
21371
21425
  const e = asFloat(exponent);
21372
21426
  if (e === 0.5 || e === -0.5) {
@@ -21382,9 +21436,12 @@ function canonicalPower(ce, base, exponent, metadata) {
21382
21436
  return ce.number(e >= 0 ? coef : [1, coef]);
21383
21437
  return ce.mul([
21384
21438
  ce.number(coef),
21385
- ce.power(ce.number(radicand), ce._HALF),
21439
+ ce._fn('Sqrt', [ce.number(radicand)]),
21386
21440
  ]);
21387
21441
  }
21442
+ if (e > 0)
21443
+ return ce._fn('Sqrt', [base], metadata);
21444
+ return ce.inv(ce._fn('Sqrt', [base]), metadata);
21388
21445
  }
21389
21446
  if (e > 0)
21390
21447
  return ce._fn('Power', [base, ce._HALF], metadata);
@@ -21415,17 +21472,6 @@ function canonicalPower(ce, base, exponent, metadata) {
21415
21472
  }
21416
21473
  if (exponent.isInfinity && (base.isOne || base.isNegativeOne))
21417
21474
  return ce._NAN;
21418
- const r = asRational(base);
21419
- if (r) {
21420
- const e = asSmallInteger(exponent);
21421
- if (e !== null) {
21422
- if (e === -1)
21423
- return ce.number(inverse(r));
21424
- // if (e > 0) return ce.number([Math.pow(n, e), Math.pow(d, e)]);
21425
- // return ce.number([Math.pow(d, -e), Math.pow(n, -e)]);
21426
- }
21427
- // @todo: could call factorPower to handle \sqrt and \sqrt[3]
21428
- }
21429
21475
  }
21430
21476
  }
21431
21477
  //
@@ -21436,7 +21482,7 @@ function canonicalPower(ce, base, exponent, metadata) {
21436
21482
  if (a !== null) {
21437
21483
  const b = asSmallInteger(base.op2);
21438
21484
  if (b !== null) {
21439
- return ce.power(base.op1, ce.number(a * b));
21485
+ return ce.pow(base.op1, ce.number(a * b));
21440
21486
  }
21441
21487
  }
21442
21488
  if (base.op1.isNonNegative) {
@@ -21444,7 +21490,7 @@ function canonicalPower(ce, base, exponent, metadata) {
21444
21490
  if (ar) {
21445
21491
  const br = asRational(base.op2);
21446
21492
  if (br)
21447
- return ce.power(base.op1, ce.number(mul(ar, br)));
21493
+ return ce.pow(base.op1, ce.number(mul(ar, br)));
21448
21494
  }
21449
21495
  }
21450
21496
  }
@@ -21453,9 +21499,9 @@ function canonicalPower(ce, base, exponent, metadata) {
21453
21499
  if (base.head === 'Multiply') {
21454
21500
  const e = asSmallInteger(exponent);
21455
21501
  if (e !== null)
21456
- return ce._fn('Multiply', base.ops.map((x) => ce.power(x, exponent))); // Don't call ce.mul() to avoid infinite loops
21502
+ return ce._fn('Multiply', base.ops.map((x) => ce.pow(x, exponent))); // Don't call ce.mul() to avoid infinite loops
21457
21503
  }
21458
- return null;
21504
+ return ce._fn('Power', [base, exponent], metadata);
21459
21505
  }
21460
21506
  function square(ce, base) {
21461
21507
  const num = base.numericValue;
@@ -21468,16 +21514,16 @@ function square(ce, base) {
21468
21514
  if (isMachineRational(num))
21469
21515
  return ce.number([num[1] * num[1], num[0] * num[0]]);
21470
21516
  if (isBigRational(num))
21471
- return ce.number([num[1].pow(2), num[0].pow(2)]);
21517
+ return ce.number([num[1] * num[1], num[0] * num[0]]);
21472
21518
  if (base.head === 'Multiply')
21473
21519
  return ce._fn('Multiply', base.ops.map((x) => square(ce, x))); // Don't call ce.mul() to avoid infinite loops
21474
21520
  if (base.head === 'Power') {
21475
21521
  const exp = asSmallInteger(base.op2);
21476
21522
  if (exp !== null)
21477
- return ce.power(base.op1, ce.number(exp * 2));
21478
- return ce.power(base.op1, ce.mul([ce.number(2), base.op2]));
21523
+ return ce.pow(base.op1, ce.number(exp * 2));
21524
+ return ce.pow(base.op1, ce.mul([ce.number(2), base.op2]));
21479
21525
  }
21480
- return ce.power(base, ce.number(2));
21526
+ return ce.pow(base, ce.number(2));
21481
21527
  }
21482
21528
  function numEvalPower(ce, base, exponent) {
21483
21529
  if (base.numericValue === null || exponent.numericValue === null)
@@ -21510,7 +21556,7 @@ function numEvalPower(ce, base, exponent) {
21510
21556
  if (invExp === 2) {
21511
21557
  if (bigBase.isNeg())
21512
21558
  return complexAllowed(ce)
21513
- ? ce.mul([ce._I, ce.number(bigBase.neg().sqrt())])
21559
+ ? ce.number(ce.complex(0, bigBase.neg().sqrt().toNumber()))
21514
21560
  : ce._NAN;
21515
21561
  return ce.number(bigBase.sqrt());
21516
21562
  }
@@ -21548,9 +21594,7 @@ function numEvalPower(ce, base, exponent) {
21548
21594
  }
21549
21595
  function processPower(ce, base, exponent, mode) {
21550
21596
  if (base.head === 'Multiply') {
21551
- let c = bignumPreferred(ce)
21552
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
21553
- : [1, 1];
21597
+ let c = bignumPreferred(ce) ? [BigInt(1), BigInt(1)] : [1, 1];
21554
21598
  const xs = [];
21555
21599
  for (const op of base.ops) {
21556
21600
  const r = asRational(op);
@@ -21562,7 +21606,7 @@ function processPower(ce, base, exponent, mode) {
21562
21606
  if (!isRationalOne(c))
21563
21607
  return ce.mul([
21564
21608
  processSqrt(ce, ce.number(c), mode) ?? ce._ONE,
21565
- ce.power(processPower(ce, ce.mul(xs), exponent, mode) ?? ce.mul(xs), exponent),
21609
+ ce.pow(processPower(ce, ce.mul(xs), exponent, mode) ?? ce.mul(xs), exponent),
21566
21610
  ]);
21567
21611
  }
21568
21612
  if (base.head === 'Power') {
@@ -21577,7 +21621,7 @@ function processPower(ce, base, exponent, mode) {
21577
21621
  return ce._ONE;
21578
21622
  if (isRationalOne(e))
21579
21623
  return base.op1;
21580
- return ce.power(base.op1, e);
21624
+ return ce.pow(base.op1, e);
21581
21625
  }
21582
21626
  if (mode === 'N') {
21583
21627
  const ef1 = asFloat(base.op2);
@@ -21588,7 +21632,7 @@ function processPower(ce, base, exponent, mode) {
21588
21632
  return ce._ONE;
21589
21633
  if (ef === 1)
21590
21634
  return base.op1;
21591
- return ce.power(base.op1, ef);
21635
+ return ce.pow(base.op1, ef);
21592
21636
  }
21593
21637
  }
21594
21638
  }
@@ -21605,28 +21649,24 @@ function processPower(ce, base, exponent, mode) {
21605
21649
  const [n, d] = [machineNumerator(r), machineDenominator(r)];
21606
21650
  if ((n === 1 || n === -1) && (d === 2 || d === 3)) {
21607
21651
  if (bignumPreferred(ce) || base.numericValue instanceof Decimal) {
21608
- const bigBase = asBignum(base);
21609
- if (d % 2 === 0 && bigBase.isNeg() && !complexAllowed(ce))
21652
+ const bigBase = asBigint(base);
21653
+ if (d % 2 === 0 && bigBase < 0 && !complexAllowed(ce))
21610
21654
  return ce._NAN;
21611
- const sign = bigBase.isNegative()
21612
- ? d % 2 === 0
21613
- ? ce._I
21614
- : ce._NEGATIVE_ONE
21615
- : ce._ONE;
21616
- const [factor, root] = factorPower(ce, bigBase.abs(), d);
21617
- if (root.eq(1) && factor.eq(1))
21655
+ const sign = bigBase < 0 ? (d % 2 === 0 ? ce._I : ce._NEGATIVE_ONE) : ce._ONE;
21656
+ const [factor, root] = factorPower(bigBase > 0 ? bigBase : -bigBase, d);
21657
+ if (root === BigInt(1) && factor === BigInt(1))
21618
21658
  return sign;
21619
21659
  // If factor === 1, nothing special to do, fall through
21620
- if (!factor.eq(1)) {
21621
- if (root.eq(1))
21660
+ if (factor !== BigInt(1)) {
21661
+ if (root === BigInt(1))
21622
21662
  return ce.mul([
21623
21663
  sign,
21624
- ce.number(n >= 0 ? factor : [ce.bignum(1), factor]),
21664
+ ce.number(n >= 0 ? factor : [BigInt(1), factor]),
21625
21665
  ]);
21626
21666
  return ce.mul([
21627
21667
  sign,
21628
21668
  ce.number(factor),
21629
- ce.power(ce.number(root), exponent),
21669
+ ce.pow(ce.number(root), exponent),
21630
21670
  ]);
21631
21671
  }
21632
21672
  }
@@ -21648,7 +21688,7 @@ function processPower(ce, base, exponent, mode) {
21648
21688
  return ce.mul([
21649
21689
  sign,
21650
21690
  ce.number(factor),
21651
- ce.power(ce.number(root), exponent),
21691
+ ce.pow(ce.number(root), exponent),
21652
21692
  ]);
21653
21693
  }
21654
21694
  }
@@ -21657,7 +21697,7 @@ function processPower(ce, base, exponent, mode) {
21657
21697
  if (base.isNegative) {
21658
21698
  if (!complexAllowed)
21659
21699
  return ce._NAN;
21660
- return ce.mul([ce._I, ce.fn('Sqrt', [ce.negate(base)])]);
21700
+ return ce.mul([ce._I, ce.fn('Sqrt', [ce.neg(base)])]);
21661
21701
  }
21662
21702
  return undefined;
21663
21703
  }
@@ -21715,10 +21755,10 @@ function processSqrt(ce, base, mode) {
21715
21755
  }
21716
21756
  }
21717
21757
  if (isBigRational(r) || bignumPreferred(ce)) {
21718
- const n = ce.bignum(r[0]);
21719
- const [nFactor, nRoot] = factorPower(ce, n.abs(), 2);
21720
- const [dFactor, dRoot] = factorPower(ce, ce.bignum(r[1]), 2);
21721
- if (n.isNeg())
21758
+ const n = bigint(r[0]);
21759
+ const [nFactor, nRoot] = factorPower(n > 0 ? n : -n, 2);
21760
+ const [dFactor, dRoot] = factorPower(bigint(r[1]), 2);
21761
+ if (n < 0)
21722
21762
  return ce.mul([
21723
21763
  ce.number([nFactor, dFactor]),
21724
21764
  ce.sqrt(ce.number([nRoot, dRoot])),
@@ -21770,9 +21810,6 @@ function rootExp(exponent) {
21770
21810
  */
21771
21811
  function canonicalMultiply(ce, ops) {
21772
21812
  console.assert(ops.every((x) => x.isCanonical));
21773
- // Apply associativity
21774
- ops =
21775
- flattenOps(flattenSequence(ops).map((x) => x.canonical), 'Multiply') ?? ops;
21776
21813
  if (ops.length === 0)
21777
21814
  return ce.number(1);
21778
21815
  if (ops.length === 1)
@@ -21788,7 +21825,7 @@ function canonicalMultiply(ce, ops) {
21788
21825
  return product.asExpression();
21789
21826
  }
21790
21827
  function simplifyMultiply(ce, ops) {
21791
- console.assert(flattenOps(ops, 'Multiply') === null);
21828
+ console.assert(ops.every((x) => x.head !== 'Multiply'));
21792
21829
  const product = new Product(ce);
21793
21830
  for (let op of ops) {
21794
21831
  op = op.simplify();
@@ -21798,25 +21835,20 @@ function simplifyMultiply(ce, ops) {
21798
21835
  }
21799
21836
  return product.asExpression();
21800
21837
  }
21801
- function fastEvalMultiply(ops) {
21802
- let prod = 1;
21803
- for (const op of ops) {
21804
- if (typeof op.numericValue !== 'number')
21805
- return null;
21806
- prod *= op.numericValue;
21807
- }
21808
- return prod;
21809
- }
21810
21838
  function evalMultiply(ce, ops, mode = 'evaluate') {
21811
21839
  console.assert(ops.length > 1, 'evalMultiply(): no arguments');
21812
21840
  //
21813
21841
  // @fastpath
21814
21842
  //
21815
- if (mode === 'N' && ce.numericMode === 'machine') {
21843
+ if (mode === 'N') {
21816
21844
  ops = ops.map((x) => x.N());
21817
- const result = fastEvalMultiply(ops);
21818
- if (result !== null)
21819
- return ce.number(result);
21845
+ if (ce.numericMode === 'machine' &&
21846
+ ops.every((x) => typeof x.numericValue === 'number')) {
21847
+ let prod = 1;
21848
+ for (const op of ops)
21849
+ prod *= op.numericValue;
21850
+ return ce.number(prod);
21851
+ }
21820
21852
  }
21821
21853
  //
21822
21854
  // First pass: looking for early exits
@@ -21827,7 +21859,7 @@ function evalMultiply(ce, ops, mode = 'evaluate') {
21827
21859
  if (!op.isExact)
21828
21860
  mode = 'N';
21829
21861
  }
21830
- console.assert(flattenOps(ops, 'Multiply') === null);
21862
+ console.assert(ops.every((x) => x.head !== 'Multiply'));
21831
21863
  if (mode === 'N')
21832
21864
  ops = ops.map((x) => x.N());
21833
21865
  else
@@ -21906,9 +21938,8 @@ function multiply2(op1, op2, metadata) {
21906
21938
  return ce._fn('Multiply', [c, t], metadata);
21907
21939
  }
21908
21940
  }
21909
- if (c.hash === t.hash && c.isSame(t)) {
21941
+ if (c.hash === t.hash && c.isSame(t))
21910
21942
  return square(ce, c);
21911
- }
21912
21943
  const product = new Product(ce, [c, t]);
21913
21944
  if (sign > 0)
21914
21945
  return product.asExpression();
@@ -21984,9 +22015,7 @@ function evalMultiplication(ce, expr, range, mode) {
21984
22015
  const product = ce.mul(terms);
21985
22016
  return mode === 'simplify' ? product.simplify() : product.evaluate();
21986
22017
  }
21987
- let product = bignumPreferred(ce)
21988
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
21989
- : [1, 1];
22018
+ let product = bignumPreferred(ce) ? [BigInt(1), BigInt(1)] : [1, 1];
21990
22019
  for (let i = lower; i <= upper; i++) {
21991
22020
  const n = ce.number(i);
21992
22021
  const r = fn.subs({ _1: n, _: n });
@@ -21997,7 +22026,7 @@ function evalMultiplication(ce, expr, range, mode) {
21997
22026
  }
21998
22027
  if (isMachineRational(product))
21999
22028
  return ce.number(product[0] / product[1]);
22000
- return ce.number(product[0].div(product[1]));
22029
+ return ce.number(ce.bignum(product[0]).div(ce.bignum(product[1])));
22001
22030
  }
22002
22031
 
22003
22032
  /**
@@ -22010,19 +22039,21 @@ function evalMultiplication(ce, expr, range, mode) {
22010
22039
  * - if Divide, transform into Multiply/Power
22011
22040
  */
22012
22041
  function canonicalDivide(ce, op1, op2) {
22013
- op1 = validateArgument(ce, op1, 'Number');
22014
- op2 = validateArgument(ce, op2, 'Number');
22015
22042
  if (!op1.isValid || !op2.isValid)
22016
22043
  return ce._fn('Divide', [op1, op2]);
22044
+ if (op1.head === 'Negate' && op2.head === 'Negate') {
22045
+ op1 = op1.op1;
22046
+ op2 = op2.op1;
22047
+ }
22017
22048
  if (op1.numericValue !== null && op2.numericValue !== null) {
22018
22049
  if (op2.isOne)
22019
22050
  return op1;
22020
22051
  if (op2.isNegativeOne)
22021
- return canonicalNegate(op1);
22052
+ return ce.neg(op1);
22022
22053
  if (op1.isOne)
22023
- return ce.inverse(op2);
22054
+ return ce.inv(op2);
22024
22055
  if (op1.isNegativeOne)
22025
- return canonicalNegate(ce.inverse(op2));
22056
+ return ce.neg(ce.inv(op2));
22026
22057
  const r1 = asRational(op1);
22027
22058
  const r2 = asRational(op2);
22028
22059
  if (r1 && r2 && !isRationalZero(r2))
@@ -22058,6 +22089,10 @@ function canonicalDivide(ce, op1, op2) {
22058
22089
  return canonicalDivide(ce, ce.mul([op1.op1, op2]), op1.op2);
22059
22090
  if (op2.head === 'Divide' || op2.head === 'Rational')
22060
22091
  return canonicalDivide(ce, ce.mul([op1, op2.op2]), op2.op1);
22092
+ const [c1, t1] = asCoefficient(op1);
22093
+ const [c2, t2] = asCoefficient(op2);
22094
+ if (!isRationalOne(c1) || !isRationalOne(c2))
22095
+ return ce.mul([ce.number(mul(c1, inverse(c2))), ce.div(t1, t2)]);
22061
22096
  // eslint-disable-next-line prefer-const
22062
22097
  let [nSign, n] = makePositive(op1);
22063
22098
  // eslint-disable-next-line prefer-const
@@ -22066,17 +22101,11 @@ function canonicalDivide(ce, op1, op2) {
22066
22101
  d = d.canonical;
22067
22102
  if (d.numericValue !== null && d.isOne)
22068
22103
  return nSign * dSign < 0 ? canonicalNegate(n) : n;
22069
- // Divide: transform into multiply/power
22070
- d = ce.inverse(d);
22071
- if (n.numericValue !== null) {
22072
- if (n.isOne)
22073
- return d;
22074
- if (n.isNegativeOne)
22075
- return canonicalNegate(d);
22076
- }
22077
22104
  if (nSign * dSign > 0)
22078
- return ce.mul([n, d]);
22079
- return canonicalNegate(ce.mul([n, d]));
22105
+ return ce._fn('Divide', [n, d]);
22106
+ if (n.numericValue)
22107
+ return ce._fn('Divide', [canonicalNegate(n), d]);
22108
+ return canonicalNegate(ce._fn('Divide', [n, d]));
22080
22109
  }
22081
22110
  /**
22082
22111
  * Simplify form of 'Divide' (and 'Rational')
@@ -22088,7 +22117,11 @@ function simplifyDivide(ce, op1, op2) {
22088
22117
  if (r1 && r2 && !isRationalZero(r2))
22089
22118
  return ce.number(mul(r1, inverse(r2)));
22090
22119
  }
22091
- return undefined;
22120
+ const [c1, t1] = asCoefficient(op1);
22121
+ const [c2, t2] = asCoefficient(op2);
22122
+ if (!isRationalOne(c1) || !isRationalOne(c2))
22123
+ return ce.mul([ce.number(mul(c1, inverse(c2))), ce.div(t1, t2)]);
22124
+ return new Product(ce, [op1, ce.inv(op2)]).asExpression();
22092
22125
  }
22093
22126
 
22094
22127
  // @todo Future additions to the dictionary
@@ -22146,7 +22179,7 @@ const ARITHMETIC_LIBRARY = [
22146
22179
  signature: {
22147
22180
  domain: 'NumericFunction',
22148
22181
  codomain: (ce, args) => domainAdd(ce, args.map((x) => x.domain)),
22149
- canonical: (ce, args) => canonicalAdd(ce, args),
22182
+ // canonical: (ce, args) => canonicalAdd(ce, args), // never called: shortpath
22150
22183
  simplify: (ce, ops) => simplifyAdd(ce, ops),
22151
22184
  evaluate: (ce, ops) => evalAdd(ce, ops),
22152
22185
  N: (ce, ops) => evalAdd(ce, ops, 'N'),
@@ -22182,8 +22215,17 @@ const ARITHMETIC_LIBRARY = [
22182
22215
  // i.e. √2x/2 -> 0.707x, 2/√2x -> 1.4142x
22183
22216
  signature: {
22184
22217
  domain: ['Function', 'Number', 'Number', 'Number'],
22185
- canonical: (ce, args) => canonicalDivide(ce, args[0], args[1]),
22218
+ canonical: (ce, args) => {
22219
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22220
+ 'Number',
22221
+ 'Number',
22222
+ ]);
22223
+ if (args.length !== 2)
22224
+ return ce._fn('Divide', args);
22225
+ return ce.div(args[0], args[1]);
22226
+ },
22186
22227
  simplify: (ce, args) => simplifyDivide(ce, args[0], args[1]),
22228
+ evaluate: (ce, ops) => apply2N(ops[0], ops[1], (n, d) => n / d, (n, d) => n.div(d), (n, d) => n.div(d)),
22187
22229
  },
22188
22230
  },
22189
22231
  Exp: {
@@ -22193,7 +22235,14 @@ const ARITHMETIC_LIBRARY = [
22193
22235
  // Exp(x) -> e^x
22194
22236
  signature: {
22195
22237
  domain: ['Function', 'Number', 'Number'],
22196
- canonical: (ce, args) => ce.power(ce.symbol('ExponentialE'), validateArgument(ce, args[0], 'Number')),
22238
+ canonical: (ce, args) => {
22239
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22240
+ 'Number',
22241
+ ]);
22242
+ if (args.length !== 1)
22243
+ return ce._fn('Power', args);
22244
+ return ce.pow(ce.symbol('ExponentialE'), args[0]);
22245
+ },
22197
22246
  },
22198
22247
  },
22199
22248
  Erf: {
@@ -22266,21 +22315,15 @@ const ARITHMETIC_LIBRARY = [
22266
22315
  signature: {
22267
22316
  domain: ['Function', 'Number', ['Maybe', 'Number'], 'Number'],
22268
22317
  canonical: (ce, ops) => {
22269
- ops = flattenSequence(ops);
22318
+ ops = canonical(flattenSequence(ops));
22270
22319
  if (ops.length === 1)
22271
- return ce._fn('Log', [
22272
- validateArgument(ce, ops[0].canonical, 'Number'),
22273
- ]);
22320
+ return ce._fn('Log', [validateArgument(ce, ops[0], 'Number')]);
22274
22321
  if (ops.length === 2) {
22275
- const base = validateArgument(ce, ops[1].canonical, 'Number');
22322
+ const arg = validateArgument(ce, ops[0], 'Number');
22323
+ const base = validateArgument(ce, ops[1], 'Number');
22276
22324
  if (base.numericValue === 10)
22277
- return ce._fn('Log', [
22278
- validateArgument(ce, ops[0].canonical, 'Number'),
22279
- ]);
22280
- return ce._fn('Log', [
22281
- validateArgument(ce, ops[0].canonical, 'Number'),
22282
- base,
22283
- ]);
22325
+ return ce._fn('Log', [arg]);
22326
+ return ce._fn('Log', [arg, base]);
22284
22327
  }
22285
22328
  return ce._fn('Log', validateArgumentCount(ce, ops, 2));
22286
22329
  },
@@ -22382,7 +22425,10 @@ const ARITHMETIC_LIBRARY = [
22382
22425
  hold: 'all',
22383
22426
  signature: {
22384
22427
  domain: 'NumericFunction',
22385
- canonical: (ce, args) => canonicalMultiply(ce, args),
22428
+ // Never called: fastpath
22429
+ // canonical: (ce, args) => {
22430
+ // return canonicalMultiply(ce, args);
22431
+ // },
22386
22432
  simplify: (ce, ops) => simplifyMultiply(ce, ops),
22387
22433
  evaluate: (ce, ops) => evalMultiply(ce, ops),
22388
22434
  N: (ce, ops) => evalMultiply(ce, ops, 'N'),
@@ -22412,7 +22458,14 @@ const ARITHMETIC_LIBRARY = [
22412
22458
  return ce.domain(negDomain);
22413
22459
  return arg;
22414
22460
  },
22415
- canonical: (_ce, args) => canonicalNegate(args[0]),
22461
+ canonical: (ce, args) => {
22462
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22463
+ 'Number',
22464
+ ]);
22465
+ if (args.length !== 1)
22466
+ return ce._fn('Negate', args);
22467
+ return canonicalNegate(args[0]);
22468
+ },
22416
22469
  simplify: (ce, ops) => processNegate(ce, ops[0], 'simplify'),
22417
22470
  evaluate: (ce, ops) => processNegate(ce, ops[0], 'evaluate'),
22418
22471
  N: (ce, ops) => processNegate(ce, ops[0], 'N'),
@@ -22436,7 +22489,15 @@ const ARITHMETIC_LIBRARY = [
22436
22489
  complexity: 3500,
22437
22490
  signature: {
22438
22491
  domain: ['Function', 'Number', 'Number', 'Number'],
22439
- canonical: (ce, args) => canonicalPower(ce, args[0], args[1]) ?? ce._fn('Power', args),
22492
+ canonical: (ce, args) => {
22493
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22494
+ 'Number',
22495
+ 'Number',
22496
+ ]);
22497
+ if (args.length !== 2)
22498
+ return ce._fn('Power', args);
22499
+ return ce.pow(args[0], args[1]);
22500
+ },
22440
22501
  simplify: (ce, ops) => processPower(ce, ops[0], ops[1], 'simplify'),
22441
22502
  evaluate: (ce, ops) => processPower(ce, ops[0], ops[1], 'evaluate'),
22442
22503
  N: (ce, ops) => {
@@ -22471,6 +22532,7 @@ const ARITHMETIC_LIBRARY = [
22471
22532
  'Number',
22472
22533
  ],
22473
22534
  // codomain: (ce, args) => domainAdd(ce, args),
22535
+ // The 'body' and 'range' need to be interpreted by canonicalMultiplication(). Don't canonicalize them yet.
22474
22536
  canonical: (ce, ops) => canonicalMultiplication(ce, ops[0], ops[1]),
22475
22537
  simplify: (ce, ops) => evalMultiplication(ce, ops[0], ops[1], 'simplify'),
22476
22538
  evaluate: (ce, ops) => evalMultiplication(ce, ops[0], ops[1], 'evaluate'),
@@ -22482,18 +22544,17 @@ const ARITHMETIC_LIBRARY = [
22482
22544
  signature: {
22483
22545
  domain: ['Function', 'Number', ['Maybe', 'Number'], 'RationalNumber'],
22484
22546
  canonical: (ce, args) => {
22485
- args = flattenSequence(args);
22547
+ args = canonical(flattenSequence(args));
22486
22548
  if (args.length === 0)
22487
22549
  return ce._fn('Rational', [ce.error(['missing', 'Number'])]);
22488
22550
  if (args.length === 1)
22489
22551
  return ce._fn('Rational', [
22490
- validateArgument(ce, args[0].canonical, 'ExtendedRealNumber'),
22552
+ validateArgument(ce, args[0], 'ExtendedRealNumber'),
22491
22553
  ]);
22492
- args =
22493
- validateSignature(ce.domain(['Function', 'Integer', 'Integer', 'RationalNumber']), args) ?? args;
22494
- if (args.length !== 2 || !args[0].isValid || !args[1].isValid)
22554
+ args = validateArguments(ce, args, ['Integer', 'Integer']);
22555
+ if (args.length !== 2)
22495
22556
  return ce._fn('Rational', args);
22496
- return canonicalDivide(ce, args[0], args[1]);
22557
+ return ce.div(args[0], args[1]);
22497
22558
  },
22498
22559
  simplify: (ce, ops) => {
22499
22560
  if (ops.length !== 2)
@@ -22528,17 +22589,16 @@ const ARITHMETIC_LIBRARY = [
22528
22589
  signature: {
22529
22590
  domain: ['Function', 'Number', 'Number', 'Number'],
22530
22591
  canonical: (ce, args) => {
22531
- args = flattenSequence(args);
22592
+ args = canonical(flattenSequence(args));
22532
22593
  if (args.length > 2)
22533
22594
  return ce._fn('Root', validateArgumentCount(ce, args, 2));
22534
22595
  const [base, exp] = [
22535
- validateArgument(ce, args[0]?.canonical, 'Number'),
22536
- validateArgument(ce, args[1]?.canonical, 'Number'),
22596
+ validateArgument(ce, args[0], 'Number'),
22597
+ validateArgument(ce, args[1], 'Number'),
22537
22598
  ];
22538
22599
  if (!exp.isValid || !base.isValid)
22539
22600
  return ce._fn('Root', [base, exp]);
22540
- return (canonicalPower(ce, base, ce.inverse(exp)) ??
22541
- ce._fn('Power', [base, ce.inverse(exp)]));
22601
+ return ce.pow(base, ce.inv(exp));
22542
22602
  },
22543
22603
  },
22544
22604
  },
@@ -22596,8 +22656,12 @@ const ARITHMETIC_LIBRARY = [
22596
22656
  complexity: 3000,
22597
22657
  signature: {
22598
22658
  domain: ['Function', 'Number', 'Number'],
22599
- canonical: (ce, args) => canonicalPower(ce, args[0], ce._HALF) ??
22600
- ce._fn('Power', [args[0], ce._HALF]),
22659
+ canonical: (ce, args) => {
22660
+ args = canonical(flattenSequence(args));
22661
+ if (args.length !== 1)
22662
+ return ce._fn('Sqrt', args);
22663
+ return ce.pow(args[0], ce._HALF);
22664
+ },
22601
22665
  simplify: (ce, ops) => processSqrt(ce, ops[0], 'simplify'),
22602
22666
  evaluate: (ce, ops) => processSqrt(ce, ops[0], 'evaluate'),
22603
22667
  N: (ce, ops) => processSqrt(ce, ops[0], 'N'),
@@ -22610,8 +22674,12 @@ const ARITHMETIC_LIBRARY = [
22610
22674
  complexity: 3100,
22611
22675
  signature: {
22612
22676
  domain: ['Function', 'Number', 'Number'],
22613
- canonical: (ce, args) => canonicalPower(ce, args[0], ce.number(2)) ??
22614
- ce._fn('Power', [args[0], ce.number(2)]),
22677
+ canonical: (ce, args) => {
22678
+ args = canonical(flattenSequence(args));
22679
+ if (args.length !== 1)
22680
+ return ce._fn('Square', args);
22681
+ return ce.pow(args[0], ce.number(2));
22682
+ },
22615
22683
  },
22616
22684
  },
22617
22685
  Subtract: {
@@ -22622,7 +22690,7 @@ const ARITHMETIC_LIBRARY = [
22622
22690
  canonical: (ce, args) => {
22623
22691
  // Not necessarily legal, but probably what was intended:
22624
22692
  // ['Subtract', 'x'] -> ['Negate', 'x']
22625
- args = flattenSequence(args.map((x) => x.canonical));
22693
+ args = canonical(flattenSequence(args));
22626
22694
  if (args.length === 1)
22627
22695
  return canonicalNegate(args[0]);
22628
22696
  args = validateArgumentCount(ce, args, 2);
@@ -22630,7 +22698,7 @@ const ARITHMETIC_LIBRARY = [
22630
22698
  return ce._fn('Subtract', args);
22631
22699
  if (!args.every((x) => x.isValid))
22632
22700
  return ce._fn('Subtract', args);
22633
- return canonicalAdd(ce, [args[0], canonicalNegate(args[1])]);
22701
+ return ce.add([args[0], canonicalNegate(args[1])]);
22634
22702
  },
22635
22703
  },
22636
22704
  },
@@ -22795,14 +22863,16 @@ function processAbs(ce, arg, mode) {
22795
22863
  return ce.number(mode === 'N' ? Math.abs(num[0] / num[1]) : [Math.abs(num[0]), num[1]]);
22796
22864
  if (isBigRational(num)) {
22797
22865
  const [n, d] = num;
22798
- return ce.number(mode === 'N' ? n.div(d).abs() : [n.abs(), d]);
22866
+ return ce.number(mode === 'N'
22867
+ ? ce.bignum(n).div(ce.bignum(d)).abs()
22868
+ : [n > 0 ? n : -n, d]);
22799
22869
  }
22800
22870
  }
22801
22871
  }
22802
22872
  if (arg.isNonNegative)
22803
22873
  return arg;
22804
22874
  if (arg.isNegative)
22805
- return ce.negate(arg);
22875
+ return ce.neg(arg);
22806
22876
  return undefined;
22807
22877
  }
22808
22878
 
@@ -22906,6 +22976,164 @@ const COLLECTIONS_LIBRARY = {
22906
22976
  // sort
22907
22977
  // contains / find
22908
22978
 
22979
+ function oneOf(xs) {
22980
+ return xs[Math.floor(Math.random() * xs.length)];
22981
+ }
22982
+ function randomExpressionWithHead(head, level) {
22983
+ if (head === 'Add' || head === 'Multiply') {
22984
+ const ops = [];
22985
+ let count = 1 + Math.floor(Math.random() * 12);
22986
+ while (count > 0) {
22987
+ ops.push(randomExpression(level + 1));
22988
+ count -= 1;
22989
+ }
22990
+ return [head, ...ops];
22991
+ }
22992
+ if (head === 'Divide' || head === 'Power') {
22993
+ return [head, randomExpression(level + 1), randomExpression(level + 1)];
22994
+ }
22995
+ if (head === 'Root') {
22996
+ return [head, randomExpression(level + 1), randomExpression(10)];
22997
+ }
22998
+ if (head === 'trig')
22999
+ return randomTrig();
23000
+ return [head, randomExpression(level + 1)];
23001
+ }
23002
+ function randomTrig() {
23003
+ return [
23004
+ oneOf([
23005
+ 'Cos',
23006
+ 'Sin',
23007
+ 'Tan',
23008
+ 'Sinh',
23009
+ 'Arccos',
23010
+ 'Arsinh',
23011
+ ['InverseFunction', 'Cos'],
23012
+ ]),
23013
+ oneOf([
23014
+ 'Pi',
23015
+ '-1',
23016
+ '0',
23017
+ '1',
23018
+ ['Divide', 'Pi', -5],
23019
+ ['Multiply', -2, ['Divide', 'Pi', 11]],
23020
+ ['Multiply', 'Half', 'Pi'],
23021
+ ['Multiply', 5, 'Pi'],
23022
+ ['Multiply', 12, 'Pi'],
23023
+ ['Divide', 'Pi', 5],
23024
+ ['Divide', 'Pi', 9],
23025
+ ['Multiply', 5, ['Divide', 'Pi', 9]],
23026
+ ['Multiply', 2, ['Divide', 'Pi', 11]],
23027
+ ['Multiply', 2, ['Divide', 'Pi', 3]],
23028
+ ]),
23029
+ ];
23030
+ }
23031
+ function randomExpression(level) {
23032
+ level ?? (level = 1);
23033
+ if (level === 1) {
23034
+ const h = oneOf([
23035
+ 'Add',
23036
+ 'Add',
23037
+ 'Add',
23038
+ 'Add',
23039
+ 'Add',
23040
+ 'Multiply',
23041
+ 'Multiply',
23042
+ 'Multiply',
23043
+ 'Multiply',
23044
+ 'Divide',
23045
+ 'Divide',
23046
+ 'Divide',
23047
+ 'Root',
23048
+ 'Sqrt',
23049
+ 'Subtract',
23050
+ 'Negate',
23051
+ 'trig',
23052
+ ]);
23053
+ return randomExpressionWithHead(h, 1);
23054
+ }
23055
+ if (level === 2) {
23056
+ if (Math.random() > 0.5)
23057
+ return randomExpression(3);
23058
+ if (Math.random() > 0.75)
23059
+ return randomExpression(1);
23060
+ const h = oneOf([
23061
+ 'Multiply',
23062
+ 'Multiply',
23063
+ 'Add',
23064
+ 'Power',
23065
+ 'trig',
23066
+ 'Ln',
23067
+ 'Exp',
23068
+ ]);
23069
+ return randomExpressionWithHead(h, 2);
23070
+ }
23071
+ return oneOf([
23072
+ -0.000012345,
23073
+ -2,
23074
+ -2,
23075
+ -2,
23076
+ -3,
23077
+ -5,
23078
+ -6,
23079
+ -12,
23080
+ -1.654e-57,
23081
+ 0,
23082
+ 0,
23083
+ 0.00012345,
23084
+ 1.654e-57,
23085
+ 1,
23086
+ 2,
23087
+ 2,
23088
+ 2,
23089
+ 2,
23090
+ 3,
23091
+ 3,
23092
+ 5,
23093
+ 5,
23094
+ 6,
23095
+ 6,
23096
+ 1234.5678,
23097
+ 5678.1234,
23098
+ 10,
23099
+ 15,
23100
+ 18,
23101
+ 30,
23102
+ 60,
23103
+ 1.234e57,
23104
+ '123456789.12345678912345e200',
23105
+ '987654321.12345678912345',
23106
+ ['Rational', -6, 10],
23107
+ ['Rational', -12, 15],
23108
+ ['Rational', -15, 12],
23109
+ ['Rational', 3, 5],
23110
+ ['Rational', 12, 15],
23111
+ ['Rational', 15, 12],
23112
+ 'ExponentialE',
23113
+ 'ImaginaryUnit',
23114
+ ['Sqrt', 3],
23115
+ ['Sqrt', 5],
23116
+ ['Sqrt', 15],
23117
+ ['Sqrt', 25],
23118
+ ['Complex', -1.1, 1.1],
23119
+ ['Complex', 4, 5],
23120
+ 'x',
23121
+ 'x',
23122
+ 'x',
23123
+ 'x',
23124
+ ['Add', 'x', 1],
23125
+ ['Divide', 'x', 3],
23126
+ ['Square', 'x'],
23127
+ ['Power', 'x', 3],
23128
+ ['Power', 'x', 4],
23129
+ ['Subtract', 'x', 1],
23130
+ ['Add', 'x', 1],
23131
+ 'a',
23132
+ 'b',
23133
+ 'Pi',
23134
+ ]);
23135
+ }
23136
+
22909
23137
  // // := assign 80 // @todo
22910
23138
  const CORE_LIBRARY = [
22911
23139
  {
@@ -22945,7 +23173,7 @@ const CORE_LIBRARY = [
22945
23173
  signature: {
22946
23174
  domain: ['Function', 'Anything', ['Tuple', 'Anything']],
22947
23175
  codomain: (ce, args) => ce.domain(['Tuple', args[0].domain]),
22948
- canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, ops.map((x) => x.canonical), 1)),
23176
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 1)),
22949
23177
  },
22950
23178
  },
22951
23179
  Pair: {
@@ -22959,7 +23187,7 @@ const CORE_LIBRARY = [
22959
23187
  ['Tuple', 'Anything', 'Anything'],
22960
23188
  ],
22961
23189
  codomain: (ce, args) => ce.domain(['Tuple', args[0].domain, args[1].domain]),
22962
- canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, ops.map((x) => x.canonical), 2)),
23190
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 2)),
22963
23191
  },
22964
23192
  },
22965
23193
  Triple: {
@@ -22974,7 +23202,7 @@ const CORE_LIBRARY = [
22974
23202
  ['Tuple', 'Anything', 'Anything', 'Anything'],
22975
23203
  ],
22976
23204
  codomain: (ce, args) => ce.domain(['Tuple', args[0].domain, args[1].domain, args[2].domain]),
22977
- canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, ops.map((x) => x.canonical), 3)),
23205
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 3)),
22978
23206
  },
22979
23207
  },
22980
23208
  Tuple: {
@@ -22986,7 +23214,7 @@ const CORE_LIBRARY = [
22986
23214
  ['Sequence', 'Anything'],
22987
23215
  ['Tuple', ['Sequence', 'Anything']],
22988
23216
  ],
22989
- canonical: (ce, ops) => ce.tuple(ops.map((x) => x.canonical)),
23217
+ canonical: (ce, ops) => ce.tuple(canonical(ops)),
22990
23218
  codomain: (ce, args) => ce.domain(['Tuple', ...args.map((x) => x.domain)]),
22991
23219
  },
22992
23220
  },
@@ -23114,7 +23342,7 @@ const CORE_LIBRARY = [
23114
23342
  /** Return the domain of an expression */
23115
23343
  signature: {
23116
23344
  domain: ['Function', 'Anything', 'Domain'],
23117
- canonical: (ce, ops) => ce.domain(validateArgumentCount(ce, ops.map((x) => x.canonical), 1)[0]),
23345
+ canonical: (ce, ops) => ce.domain(validateArgumentCount(ce, canonical(ops), 1)[0]),
23118
23346
  },
23119
23347
  },
23120
23348
  Evaluate: {
@@ -23122,7 +23350,7 @@ const CORE_LIBRARY = [
23122
23350
  signature: {
23123
23351
  domain: ['Function', 'Anything', 'Anything'],
23124
23352
  codomain: (_ce, args) => args[0].domain,
23125
- canonical: (ce, ops) => ce._fn('Evaluate', validateArgumentCount(ce, ops.map((x) => x.canonical), 1)),
23353
+ canonical: (ce, ops) => ce._fn('Evaluate', validateArgumentCount(ce, canonical(ops), 1)),
23126
23354
  evaluate: (_ce, ops) => ops[0].evaluate(),
23127
23355
  },
23128
23356
  },
@@ -23351,8 +23579,7 @@ const CORE_LIBRARY = [
23351
23579
  const op1 = ops[0];
23352
23580
  const val = asFloat(op1) ?? NaN;
23353
23581
  if (Number.isNaN(val) || !Number.isInteger(val)) {
23354
- ce.signal(ce._fn('IntegerString', ops), `Expected first argument as an integer. Got \\(${op1.latex}$\\)`);
23355
- return undefined;
23582
+ return ce.error(['incompatible-domain', 'Integer', op1.domain], op1);
23356
23583
  }
23357
23584
  const op2 = ops[1];
23358
23585
  if (op2.isNothing) {
@@ -23364,14 +23591,11 @@ const CORE_LIBRARY = [
23364
23591
  return ce.string(Math.abs(Math.round(asFloat(op1) ?? NaN)).toString());
23365
23592
  }
23366
23593
  if (asSmallInteger(op2) === null) {
23367
- ce.signal(ce._fn('IntegerString', ops), `Expected \`base\` as an integer between 2 and 36. Got \\(${op2.latex}$\\)`);
23368
- return undefined;
23594
+ return ce.error(['incompatible-domain', 'Integer', op2.domain], op2);
23369
23595
  }
23370
23596
  const base = asSmallInteger(op2);
23371
- if (base < 2 || base > 36) {
23372
- ce.signal(ce._fn('IntegerString', ops), `Expected \`base\` as an integer between 2 and 36. Got ${base}`);
23373
- return undefined;
23374
- }
23597
+ if (base < 2 || base > 36)
23598
+ return ce.error(['out-of-range', 2, 36, base], op2);
23375
23599
  return ce.string(Math.abs(val).toString(base));
23376
23600
  },
23377
23601
  },
@@ -23455,6 +23679,14 @@ const CORE_LIBRARY = [
23455
23679
  },
23456
23680
  },
23457
23681
  },
23682
+ {
23683
+ RandomExpression: {
23684
+ signature: {
23685
+ domain: 'Function',
23686
+ evaluate: (ce, _ops) => ce.box(randomExpression()),
23687
+ },
23688
+ },
23689
+ },
23458
23690
  ];
23459
23691
  // xcas/gias https://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/cascmd_en/cascmd_en.html
23460
23692
  // https://www.haskell.org/onlinereport/haskell2010/haskellch9.html#x16-1720009.1
@@ -23795,9 +24027,7 @@ const RELOP_LIBRARY = {
23795
24027
  signature: {
23796
24028
  domain: 'RelationalOperator',
23797
24029
  canonical: (ce, ops) => {
23798
- ops =
23799
- flattenOps(flattenSequence(ops).map((x) => x.canonical), 'Equal') ?? ops;
23800
- return ce._fn('Equal', ops);
24030
+ return ce._fn('Equal', flattenOps(canonical(flattenSequence(ops)), 'Equal'));
23801
24031
  },
23802
24032
  evaluate: (ce, ops) => {
23803
24033
  if (ops.length < 2)
@@ -23843,11 +24073,7 @@ const RELOP_LIBRARY = {
23843
24073
  complexity: 11000,
23844
24074
  signature: {
23845
24075
  domain: 'RelationalOperator',
23846
- canonical: (ce, ops) => {
23847
- ops =
23848
- flattenOps(flattenSequence(ops).map((x) => x.canonical), 'Less') ?? ops;
23849
- return ce._fn('Less', ops);
23850
- },
24076
+ canonical: (ce, ops) => ce._fn('Less', flattenOps(canonical(flattenSequence(ops)), 'Less')),
23851
24077
  evaluate: (ce, ops) => {
23852
24078
  if (ops.length < 2)
23853
24079
  return ce.symbol('True');
@@ -24085,7 +24311,7 @@ const SETS_LIBRARY = {
24085
24311
  signature: {
24086
24312
  domain: 'Predicate',
24087
24313
  canonical: (ce, args) => {
24088
- args = validateArgumentCount(ce, flattenSequence(args).map((x) => x.canonical), 2);
24314
+ args = validateArgumentCount(ce, flattenSequence(canonical(args)), 2);
24089
24315
  if (args.length === 2 && isDomain(args[1]))
24090
24316
  return ce._fn('Element', [args[0], ce.domain(args[1])]);
24091
24317
  return ce._fn('Element', args);
@@ -24299,13 +24525,15 @@ const TRIGONOMETRY_LIBRARY = [
24299
24525
  signature: {
24300
24526
  domain: ['Function', 'Number', 'Number'],
24301
24527
  canonical: (ce, ops) => {
24302
- ops = validateArgumentCount(ce, flattenSequence(ops), 1);
24528
+ ops = validateArguments(ce, flattenSequence(canonical(ops)), [
24529
+ 'Number',
24530
+ ]);
24303
24531
  if (ops.length !== 1)
24304
24532
  return ce.box(['Degrees', ops]);
24305
- const arg = validateArgument(ce, ops[0].canonical, 'Number');
24533
+ const arg = ops[0];
24306
24534
  if (arg.numericValue === null || !arg.isValid)
24307
24535
  return ce.box(['Degrees', arg]);
24308
- return ce.mul([arg, ce.box(['Divide', 'Pi', 180])]);
24536
+ return ce.div(ce.mul([arg, ce.symbol('Pi')]), ce.number(180));
24309
24537
  },
24310
24538
  evaluate: (ce, ops) => ce.mul([ops[0], ce.box(['Divide', 'Pi', 180])]),
24311
24539
  },
@@ -24664,7 +24892,7 @@ const TRIGONOMETRY_LIBRARY = [
24664
24892
  signature: {
24665
24893
  domain: ['Function', 'Function', 'Function'],
24666
24894
  canonical: (ce, ops) => {
24667
- ops = validateArgumentCount(ce, flattenSequence(ops), 1).map((x) => x.canonical);
24895
+ ops = validateArgumentCount(ce, flattenSequence(canonical(ops)), 1);
24668
24896
  return (processInverseFunction(ce, ops) ?? ce._fn('InverseFunction', ops));
24669
24897
  },
24670
24898
  simplify: (ce, ops) => processInverseFunction(ce, ops),
@@ -24898,7 +25126,7 @@ function constructibleValues(ce, head, x) {
24898
25126
  [sign, head] = TRIG_IDENTITIES[head]?.[quadrant] ?? [1, head];
24899
25127
  sign = sign * identitySign;
24900
25128
  for (const [[n, d], result] of specialValues) {
24901
- if (ce.chop(theta - (Math.PI * n) / d) === 0) {
25129
+ if (result[head] && ce.chop(theta - (Math.PI * n) / d) === 0) {
24902
25130
  // Cos and Sec are even functions, the others are odd
24903
25131
  return sign < 0 ? canonicalNegate(result[head]) : result[head];
24904
25132
  }
@@ -25922,12 +26150,11 @@ function setCurrentContextSymbolTable(engine, table) {
25922
26150
  if (engine.strict && entry.wikidata) {
25923
26151
  for (const [_, d] of idTable) {
25924
26152
  if (d.wikidata === entry.wikidata)
25925
- throw new Error(`Duplicate entry with wikidata "${entry.wikidata}": "${name}" and "${d.name}"`);
26153
+ throw new Error(`Duplicate entries with wikidata "${entry.wikidata}": "${name}" and "${d.name}"`);
25926
26154
  }
25927
26155
  }
25928
- if (idTable.has(name)) {
25929
- throw new Error(`Duplicate symbol definition "${name}":\n${JSON.stringify(idTable.get(name))}\n${JSON.stringify(entry)}`);
25930
- }
26156
+ if (idTable.has(name))
26157
+ throw new Error(`Duplicate symbol definition "${name}"`);
25931
26158
  idTable.set(name, def);
25932
26159
  }
25933
26160
  else {
@@ -25977,7 +26204,7 @@ function setCurrentContextSymbolTable(engine, table) {
25977
26204
  * ```
25978
26205
  */
25979
26206
  function numericCostFunction(n) {
25980
- if (Number.isInteger(n)) {
26207
+ if (Number.isInteger(n) && n !== 0) {
25981
26208
  return Math.floor(Math.log2(Math.abs(n)) / Math.log2(10)) + (n > 0 ? 1 : 2);
25982
26209
  }
25983
26210
  return 2;
@@ -26001,8 +26228,8 @@ function costFunction(expr) {
26001
26228
  if (isMachineRational(num))
26002
26229
  return numericCostFunction(num[0]) + numericCostFunction(num[1]) + 1;
26003
26230
  else
26004
- return (numericCostFunction(num[0].toNumber()) +
26005
- numericCostFunction(num[1].toNumber()) +
26231
+ return (numericCostFunction(Number(num[0])) +
26232
+ numericCostFunction(Number(num[1])) +
26006
26233
  1);
26007
26234
  }
26008
26235
  if (num instanceof Complex)
@@ -26011,8 +26238,24 @@ function costFunction(expr) {
26011
26238
  return 2;
26012
26239
  }
26013
26240
  const head = expr.head;
26014
- return ((typeof head === 'string' ? 1 : costFunction(head)) +
26015
- (expr.ops?.reduce((acc, x) => acc + costFunction(x), 0) ?? 0));
26241
+ let headCost = 2;
26242
+ if (typeof head === 'string') {
26243
+ if (['Add', 'Divide'].includes(head))
26244
+ headCost = 3;
26245
+ else if (['Subtract', 'Negate'].includes(head))
26246
+ headCost = 4;
26247
+ else if (['Square', 'Sqrt', 'Multiply', 'Root'].includes(head))
26248
+ headCost = 5;
26249
+ else if (['Power'].includes(head))
26250
+ headCost = 6;
26251
+ else if (['Ln', 'Exp', 'Log'].includes(head))
26252
+ headCost = 7;
26253
+ else
26254
+ headCost = 8;
26255
+ }
26256
+ else
26257
+ headCost = costFunction(head);
26258
+ return (headCost + (expr.ops?.reduce((acc, x) => acc + costFunction(x), 0) ?? 0));
26016
26259
  }
26017
26260
  const DEFAULT_COST_FUNCTION = costFunction;
26018
26261
 
@@ -26468,10 +26711,16 @@ class BoxedSymbol extends AbstractBoxedExpression {
26468
26711
  }
26469
26712
  /** A free variable either has no definition, or it has a definition, but no value */
26470
26713
  get isFree() {
26471
- return !this.symbolDefinition?.value;
26714
+ // Don't use `.symbolDefinition` as this has a side effect of creating
26715
+ // a def, which is not desirable whn we're just doing a test.
26716
+ const def = this._def ?? this.engine.lookupSymbol(this._name, this._wikidata);
26717
+ return !isSymbolDefinition(def) || def.value === undefined;
26472
26718
  }
26473
26719
  get isConstant() {
26474
- return this.symbolDefinition?.constant ?? false;
26720
+ // Don't use `.symbolDefinition` as this has a side effect of creating
26721
+ // a def, which is not desirable whn we're just doing a test.
26722
+ const def = this._def ?? this.engine.lookupSymbol(this._name, this._wikidata);
26723
+ return !isSymbolDefinition(def) || def.constant;
26475
26724
  }
26476
26725
  get isCanonical() {
26477
26726
  return this._scope !== null;
@@ -27051,7 +27300,7 @@ class ComputeEngine {
27051
27300
  shorthands: ['function', 'symbol', 'string', 'dictionary', 'number'],
27052
27301
  metadata: [],
27053
27302
  precision: 'max',
27054
- repeatingDecimals: true,
27303
+ repeatingDecimals: false,
27055
27304
  };
27056
27305
  this._stats = {
27057
27306
  highwaterMark: 0,
@@ -27082,20 +27331,10 @@ class ComputeEngine {
27082
27331
  //
27083
27332
  this.context = {
27084
27333
  assumptions: new ExpressionMap(),
27085
- warn: (sigs) => {
27086
- for (const sig of sigs) {
27087
- if (typeof sig.message === 'string') {
27088
- console.warn(sig.message);
27089
- }
27090
- else {
27091
- console.warn(...sig.message);
27092
- }
27093
- }
27094
- },
27095
27334
  timeLimit: 2.0,
27096
27335
  memoryLimit: 1.0,
27097
27336
  recursionLimit: 1024,
27098
- // iterationLimit: no iteration limit
27337
+ iterationLimit: Number.POSITIVE_INFINITY,
27099
27338
  };
27100
27339
  const tables = options?.ids ?? ComputeEngine.getStandardLibrary();
27101
27340
  for (const table of tables)
@@ -27361,6 +27600,8 @@ class ComputeEngine {
27361
27600
  }
27362
27601
  /** @internal */
27363
27602
  bignum(a) {
27603
+ if (typeof a === 'bigint')
27604
+ return new this._bignum(a.toString());
27364
27605
  return new this._bignum(a);
27365
27606
  }
27366
27607
  /** @internal */
@@ -27508,6 +27749,10 @@ class ComputeEngine {
27508
27749
  if (this.context === null)
27509
27750
  throw Error('No parent scope available');
27510
27751
  this.context = {
27752
+ timeLimit: this.context.timeLimit,
27753
+ memoryLimit: this.context.memoryLimit,
27754
+ recursionLimit: this.context.recursionLimit,
27755
+ iterationLimit: this.context.iterationLimit,
27511
27756
  ...(scope ?? {}),
27512
27757
  parentScope: this.context,
27513
27758
  // We always copy the current assumptions in the new scope.
@@ -27535,29 +27780,6 @@ class ComputeEngine {
27535
27780
  if (!this.context)
27536
27781
  throw Error('No scope available');
27537
27782
  const parentScope = this.context?.parentScope;
27538
- // If there are some warnings, handle them
27539
- if (this.context.warnings) {
27540
- const warnings = [...this.context.warnings];
27541
- this.context.warnings = [];
27542
- if (this.context.warn) {
27543
- this.context.warn(warnings);
27544
- }
27545
- }
27546
- // If there are some unhandled warnings, or warnings signaled during the
27547
- // warning handler, propagate them.
27548
- if (parentScope &&
27549
- this.context.warnings &&
27550
- this.context.warnings.length > 0) {
27551
- if (!parentScope.warnings) {
27552
- parentScope.warnings = [...this.context.warnings];
27553
- }
27554
- else {
27555
- parentScope.warnings = [
27556
- ...parentScope.warnings,
27557
- ...this.context.warnings,
27558
- ];
27559
- }
27560
- }
27561
27783
  this.context = parentScope ?? null;
27562
27784
  console.assert(this.context !== null);
27563
27785
  }
@@ -27571,6 +27793,7 @@ class ComputeEngine {
27571
27793
  if (def)
27572
27794
  def.value = idk ?? undefined;
27573
27795
  else if (idk !== undefined && idk !== null) {
27796
+ // Unknown identifier, define a new one
27574
27797
  const val = this.box(idk);
27575
27798
  if (val.domain.isNumeric)
27576
27799
  this.defineSymbol(k, { value: val, domain: 'Number' });
@@ -27610,10 +27833,12 @@ class ComputeEngine {
27610
27833
  for (const k of Object.keys(identifiers)) {
27611
27834
  if (k !== 'Nothing') {
27612
27835
  const def = identifiers[k];
27613
- if ('value' in def || ('domain' in def && def.domain !== 'Function'))
27836
+ if (isSymbolDefinition$1(def))
27614
27837
  this.defineSymbol(k, def);
27615
- else
27838
+ else if (isFunctionDefinition$1(def))
27616
27839
  this.defineFunction(k, def);
27840
+ else
27841
+ this.set({ [k]: identifiers[k] });
27617
27842
  }
27618
27843
  }
27619
27844
  }
@@ -27649,31 +27874,14 @@ class ComputeEngine {
27649
27874
  throw new Error('timeout');
27650
27875
  }
27651
27876
  }
27652
- assert(condition, expr, msg, code) {
27653
- if (!condition)
27654
- this.signal(expr, msg, code);
27655
- }
27656
- signal(arg1, msg, code) {
27657
- let subject = '';
27658
- let message = '';
27659
- // @todo: store the warnings
27660
- if (typeof arg1 === 'object' && 'message' in arg1) {
27661
- code = arg1.message;
27662
- }
27663
- else {
27664
- subject = arg1.latex;
27665
- message = msg ?? '';
27666
- }
27667
- const codeString = code === undefined
27668
- ? ''
27669
- : typeof code === 'string'
27670
- ? `[${code}]`
27671
- : Array.isArray(code)
27672
- ? '[' + code.map((x) => x.toString()).join(', ') + ']'
27673
- : '';
27674
- console.error(`${subject}: ${message ?? ''} ${codeString}`);
27675
- return;
27676
- }
27877
+ // assert(
27878
+ // condition: boolean,
27879
+ // expr: BoxedExpression,
27880
+ // msg: string,
27881
+ // code?: SignalMessage
27882
+ // ) {
27883
+ // if (!condition) this.signal(expr, msg, code);
27884
+ // }
27677
27885
  /** @internal */
27678
27886
  cache(cacheName, build, purge) {
27679
27887
  if (this._cache[cacheName] === undefined) {
@@ -27689,6 +27897,12 @@ class ComputeEngine {
27689
27897
  box(expr, options) {
27690
27898
  return box(this, expr, options);
27691
27899
  }
27900
+ canonical(xs) {
27901
+ if (!xs.every((x) => x instanceof AbstractBoxedExpression))
27902
+ return xs.map((x) => this.box(x));
27903
+ const bxs = xs;
27904
+ return bxs.every((x) => x.isCanonical) ? bxs : bxs.map((x) => x.canonical);
27905
+ }
27692
27906
  fn(head, ops, metadata) {
27693
27907
  return boxFunction(this, head, ops, { metadata, canonical: true });
27694
27908
  }
@@ -27731,29 +27945,27 @@ class ComputeEngine {
27731
27945
  }
27732
27946
  add(ops, metadata) {
27733
27947
  // Short path. Note that are arguments are **not** validated.
27734
- // ops = flattenSequence(ops);
27735
- const result = canonicalAdd(this, ops);
27948
+ const result = canonicalAdd(this, flattenOps(flattenSequence(ops), 'Add'));
27736
27949
  if (metadata?.latex !== undefined)
27737
27950
  result.latex = metadata.latex;
27738
27951
  if (metadata?.wikidata !== undefined)
27739
27952
  result.wikidata = metadata.wikidata;
27740
27953
  return result;
27741
27954
  }
27742
- negate(expr, metadata) {
27955
+ neg(expr, metadata) {
27743
27956
  // Short path. Note that are arguments are **not** validated.
27744
27957
  return canonicalNegate(expr, metadata);
27745
27958
  }
27746
27959
  mul(ops, metadata) {
27747
27960
  // Short path. Note that are arguments are **not** validated.
27748
- ops = flattenSequence(ops);
27749
- const result = canonicalMultiply(this, ops);
27961
+ const result = canonicalMultiply(this, flattenOps(flattenSequence(ops), ' Multiply'));
27750
27962
  if (metadata?.latex !== undefined)
27751
27963
  result.latex = metadata.latex;
27752
27964
  if (metadata?.wikidata !== undefined)
27753
27965
  result.wikidata = metadata.wikidata;
27754
27966
  return result;
27755
27967
  }
27756
- divide(num, denom, metadata) {
27968
+ div(num, denom, metadata) {
27757
27969
  // Short path. Note that are arguments are **not** validated.
27758
27970
  const result = canonicalDivide(this, num, denom);
27759
27971
  if (metadata?.latex !== undefined)
@@ -27763,11 +27975,11 @@ class ComputeEngine {
27763
27975
  return result;
27764
27976
  }
27765
27977
  sqrt(base, metadata) {
27766
- return this.power(base, [1, 2], metadata);
27978
+ return canonicalPower(this, base, this._HALF, metadata);
27767
27979
  }
27768
- power(base, exponent, metadata) {
27980
+ pow(base, exponent, metadata) {
27769
27981
  // Short path. Note that are arguments are **not** validated.
27770
- let e = null;
27982
+ // The logic here handles the cases where the exponent is a number or Rational
27771
27983
  if (exponent instanceof AbstractBoxedExpression) {
27772
27984
  const num = exponent.numericValue;
27773
27985
  if (num !== null) {
@@ -27777,13 +27989,15 @@ class ComputeEngine {
27777
27989
  exponent = num;
27778
27990
  }
27779
27991
  }
27992
+ let e = null;
27780
27993
  if (typeof exponent === 'number')
27781
27994
  e = exponent;
27782
27995
  else if (isRational(exponent)) {
27996
+ // Is the denominator 1?
27783
27997
  if (isMachineRational(exponent) && exponent[1] === 1)
27784
27998
  e = exponent[0];
27785
- if (isBigRational(exponent) && exponent[1].equals(1))
27786
- e = exponent[0].toNumber();
27999
+ else if (isBigRational(exponent) && exponent[1] === BigInt(1))
28000
+ e = Number(exponent[0]);
27787
28001
  }
27788
28002
  // x^1
27789
28003
  if (e === 1)
@@ -27793,18 +28007,40 @@ class ComputeEngine {
27793
28007
  if (e === -1 && r !== null) {
27794
28008
  if (typeof r === 'number' && Number.isInteger(r))
27795
28009
  return this.number([1, r]);
27796
- if (r instanceof Decimal && r.isInteger())
27797
- return this.number([base.engine._BIGNUM_ONE, r]);
27798
- if (isRational(r))
27799
- return this.number(isBigRational(r) ? [r[1], r[0]] : [r[1], r[0]]);
28010
+ else if (r instanceof Decimal && r.isInteger())
28011
+ return this.number([BigInt(1), bigint(r)]);
28012
+ else if (isRational(r))
28013
+ return this.number([r[1], r[0]]);
27800
28014
  }
27801
28015
  if (typeof exponent === 'number' || isRational(exponent))
27802
28016
  exponent = this.number(exponent);
27803
- return (canonicalPower(this, base, exponent, metadata) ??
27804
- this._fn('Power', [base, exponent], metadata));
28017
+ return canonicalPower(this, base, exponent, metadata);
27805
28018
  }
27806
- inverse(expr, metadata) {
28019
+ inv(expr, metadata) {
27807
28020
  // Short path. Note that are arguments are **not** validated.
28021
+ if (expr.isOne)
28022
+ return this._ONE;
28023
+ if (expr.isNegativeOne)
28024
+ return this._NEGATIVE_ONE;
28025
+ if (expr.isInfinity)
28026
+ return this._ZERO;
28027
+ const n = expr.numericValue;
28028
+ if (n !== null) {
28029
+ if (isRational(n))
28030
+ return this.number(inverse(n), { metadata });
28031
+ if (typeof n === 'number' && Number.isInteger(n))
28032
+ return this.number([1, n], { metadata });
28033
+ if (n instanceof Decimal && n.isInteger())
28034
+ return this.number([BigInt(1), bigint(n)], { metadata });
28035
+ return this._fn('Divide', [this._ONE, expr], metadata);
28036
+ }
28037
+ if (expr.head === 'Sqrt')
28038
+ return this._fn('Sqrt', [this.inv(expr.op1)], metadata);
28039
+ if (expr.head === 'Divide')
28040
+ return this._fn('Divide', [expr[1], expr[0]], metadata);
28041
+ if (expr.head === 'Rational')
28042
+ return this.number([expr[1], expr[0]], { metadata });
28043
+ // Inverse(expr) -> expr^{-1}
27808
28044
  let e = this._NEGATIVE_ONE;
27809
28045
  if (expr.head === 'Power') {
27810
28046
  // Inverse(x^{-1}) -> x
@@ -27814,21 +28050,20 @@ class ComputeEngine {
27814
28050
  e = canonicalNegate(expr.op2);
27815
28051
  expr = expr.op1;
27816
28052
  }
27817
- // Inverse(expr) -> expr^{-1}
27818
- // Will take care of literals, i.e. Inverse(n/d) -> d/n
27819
- return (canonicalPower(this, expr, e, metadata) ??
27820
- this._fn('Power', [expr, e], metadata));
28053
+ if (e.isNegativeOne)
28054
+ return this._fn('Divide', [this._ONE, expr], metadata);
28055
+ return this._fn('Power', [expr, e], metadata);
27821
28056
  }
27822
28057
  pair(first, second, metadata) {
27823
28058
  // Short path
27824
- return new BoxedFunction(this, 'Tuple', [first.canonical, second.canonical], {
28059
+ return new BoxedFunction(this, 'Tuple', [first, second], {
27825
28060
  metadata,
27826
28061
  canonical: true,
27827
28062
  });
27828
28063
  }
27829
28064
  tuple(elements, metadata) {
27830
28065
  // Short path
27831
- return new BoxedFunction(this, 'Tuple', elements.map((x) => x.canonical), {
28066
+ return new BoxedFunction(this, 'Tuple', canonical(elements), {
27832
28067
  metadata,
27833
28068
  canonical: true,
27834
28069
  });
@@ -27905,24 +28140,36 @@ class ComputeEngine {
27905
28140
  //
27906
28141
  // Is this number eligible to be a cached number expression?
27907
28142
  //
27908
- if (options.metadata === undefined && typeof value === 'number') {
27909
- const n = value;
27910
- if (n === 1)
27911
- return this._ONE;
27912
- if (n === 0)
27913
- return this._ZERO;
27914
- if (n === -1)
27915
- return this._NEGATIVE_ONE;
27916
- if (Number.isInteger(n) && this._commonNumbers[n] !== undefined) {
27917
- if (this._commonNumbers[n] === null)
27918
- this._commonNumbers[n] = boxNumber(this, value) ?? this._NAN;
27919
- return this._commonNumbers[n];
28143
+ if (options.metadata === undefined) {
28144
+ if (typeof value === 'bigint') {
28145
+ if (value === BigInt(1))
28146
+ return this._ONE;
28147
+ if (value === BigInt(0))
28148
+ return this._ZERO;
28149
+ if (value === BigInt(-1))
28150
+ return this._NEGATIVE_ONE;
28151
+ }
28152
+ if (typeof value === 'number') {
28153
+ const n = value;
28154
+ if (n === 1)
28155
+ return this._ONE;
28156
+ if (n === 0)
28157
+ return this._ZERO;
28158
+ if (n === -1)
28159
+ return this._NEGATIVE_ONE;
28160
+ if (Number.isInteger(n) && this._commonNumbers[n] !== undefined) {
28161
+ if (this._commonNumbers[n] === null)
28162
+ this._commonNumbers[n] = boxNumber(this, value) ?? this._NAN;
28163
+ return this._commonNumbers[n];
28164
+ }
28165
+ if (Number.isNaN(n))
28166
+ return this._NAN;
28167
+ if (!Number.isFinite(n))
28168
+ return n < 0 ? this._NEGATIVE_INFINITY : this._POSITIVE_INFINITY;
27920
28169
  }
27921
- if (Number.isNaN(n))
27922
- return this._NAN;
27923
- if (!Number.isFinite(n))
27924
- return n < 0 ? this._NEGATIVE_INFINITY : this._POSITIVE_INFINITY;
27925
28170
  }
28171
+ if (typeof value === 'bigint')
28172
+ value = this.bignum(value);
27926
28173
  return boxNumber(this, value, options) ?? this._NAN;
27927
28174
  }
27928
28175
  rules(rules) {
@@ -28094,8 +28341,7 @@ class ComputeEngine {
28094
28341
  // it in a parent scope. However, when the current scope exits,
28095
28342
  // any previous assumptions about the symbol will be restored).
28096
28343
  for (const [assumption, _val] of this.assumptions) {
28097
- const vars = getVars(assumption);
28098
- if (vars.includes(symbol))
28344
+ if (assumption.symbols.includes(symbol))
28099
28345
  this.assumptions.delete(assumption);
28100
28346
  }
28101
28347
  }
@@ -28103,6 +28349,10 @@ class ComputeEngine {
28103
28349
  }
28104
28350
 
28105
28351
  // This file is the root of the `compute-engine` package
28106
- const version = '0.11.0';
28352
+ const version = '0.12.1';
28353
+ globalThis[Symbol.for('io.cortexjs.compute-engine')] = {
28354
+ ComputeEngine: ComputeEngine.constructor,
28355
+ version: '0.12.1',
28356
+ };
28107
28357
 
28108
- export { ComputeEngine, getVars, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };
28358
+ export { ComputeEngine, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };