@cortex-js/compute-engine 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/compute-engine.esm.js +1592 -1352
  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.0 */
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,115 @@ 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 !== 0n)
14271
+ [a, b] = [b, a % b];
14272
+ return a < 0n ? -a : a;
14273
+ }
14274
+ // Difference between primes from 7 to 31
14275
+ const PRIME_WHEEL_INC = [4n, 2n, 4n, 2n, 4n, 6n, 2n, 6n];
14276
+ function primeFactors(d) {
14277
+ if (d < Number.MAX_SAFE_INTEGER) {
14278
+ const factors = primeFactors$1(Number(d));
14279
+ const result = new Map();
14280
+ for (const f of Object.keys(factors))
14281
+ result.set(bigint(f), factors[f]);
14282
+ return result;
14283
+ }
14284
+ //https:rosettacode.org/wiki/Prime_decomposition#JavaScript
14285
+ let n = d;
14286
+ const result = new Map();
14287
+ // Wheel factorization
14288
+ // @todo: see https://github.com/Fairglow/prime-factor/blob/main/src/lib.rs
14289
+ let count2 = 0;
14290
+ let count3 = 0;
14291
+ let count5 = 0;
14292
+ let k = 10n;
14293
+ while (n % k === 0n) {
14294
+ count2 += 1;
14295
+ count5 += 1;
14296
+ n = n / k;
14297
+ }
14298
+ k = 5n;
14299
+ while (n % k === 0n) {
14300
+ count5 += 1;
14301
+ n = n / k;
14302
+ }
14303
+ k = 3n;
14304
+ while (n % k === 0n) {
14305
+ count3 += 1;
14306
+ n = n / k;
14307
+ }
14308
+ k = 2n;
14309
+ while (n % k === 0n) {
14310
+ count2 += 1;
14311
+ n = n / k;
14312
+ }
14313
+ if (count2 > 0)
14314
+ result.set('2', count2);
14315
+ if (count3 > 0)
14316
+ result.set('3', count3);
14317
+ if (count5 > 0)
14318
+ result.set('5', count5);
14319
+ k = 7n;
14320
+ let kIndex = '';
14321
+ let i = 0;
14322
+ while (k * k < n) {
14323
+ if (n % k === 0n) {
14324
+ if (!kIndex)
14325
+ kIndex = k.toString();
14326
+ result.set(kIndex, (result.get(kIndex) ?? 0) + 1);
14327
+ n = n / k;
14328
+ }
14329
+ else {
14330
+ k = k + PRIME_WHEEL_INC[i];
14331
+ kIndex = '';
14332
+ i = i < 7 ? i + 1 : 0;
14333
+ }
14334
+ }
14335
+ if (n !== 1n)
14336
+ result.set(n.toString(), (result.get(n.toString()) ?? 0) + 1);
14337
+ const r = new Map();
14338
+ for (const [k, v] of result)
14339
+ r.set(bigint(k), v);
14340
+ return r;
14341
+ }
14342
+ /** Return `[factor, root]` such that
14343
+ * pow(n, 1/exponent) = factor * pow(root, 1/exponent)
14344
+ *
14345
+ * factorPower(75, 2) -> [5, 3] = 5^2 * 3
14346
+ *
14347
+ */
14348
+ function factorPower(n, exponent) {
14349
+ // @todo: handle negative n
14350
+ const factors = primeFactors(n);
14351
+ let f = 1n;
14352
+ let r = 1n;
14353
+ const exp = bigint(exponent);
14354
+ for (const [k, v] of factors) {
14355
+ const v2 = bigint(v);
14356
+ f = f * k ** (v2 / exp);
14357
+ r = r * k ** (v2 % exp);
14358
+ }
14359
+ return [f, r];
14360
+ }
14361
+
14212
14362
  function isLatexString(s) {
14213
14363
  if (typeof s === 'string')
14214
14364
  return s.startsWith('$') && s.endsWith('$');
@@ -14250,37 +14400,41 @@ function getImaginaryCoef(expr) {
14250
14400
  /**
14251
14401
  * Return the free symbols in the expression, recursively.
14252
14402
  * A variable, or free symbol, is a symbol that is not bound to a value.
14403
+ * Note: do not use `isFree`: it has a side effect of creating a definition
14404
+ * if one does not exist, and we want to avoid that. For example, `assume()`
14405
+ * relies on `expr.freeVars` *not* creating a definition.
14253
14406
  */
14254
- function getVars(expr) {
14407
+ function getFreeVars(expr, set) {
14255
14408
  if (expr.symbol) {
14256
- const def = expr.symbolDefinition;
14257
- return def?.constant ? [] : [expr.symbol];
14409
+ const def = expr.engine.lookupSymbol(expr.symbol);
14410
+ if (def?.value === undefined)
14411
+ set.add(expr.symbol);
14412
+ return;
14258
14413
  }
14259
14414
  if (!expr.ops && !expr.keys)
14260
- return [];
14261
- const result = [];
14415
+ return;
14262
14416
  if (expr.ops)
14263
14417
  for (const op of expr.ops)
14264
- result.push(...getVars(op));
14418
+ getFreeVars(op, set);
14265
14419
  if (expr.keys)
14266
14420
  for (const key of expr.keys)
14267
- result.push(...getVars(expr.getKey(key)));
14268
- return result;
14421
+ getFreeVars(expr.getKey(key), set);
14422
+ return;
14269
14423
  }
14270
14424
  function getSymbols(expr, set) {
14271
14425
  if (expr.symbol) {
14272
14426
  set.add(expr.symbol);
14273
- return set;
14427
+ return;
14274
14428
  }
14275
14429
  if (!expr.ops && !expr.keys)
14276
- return set;
14430
+ return;
14277
14431
  if (expr.ops)
14278
14432
  for (const op of expr.ops)
14279
14433
  getSymbols(op, set);
14280
14434
  if (expr.keys)
14281
14435
  for (const key of expr.keys)
14282
14436
  getSymbols(expr.getKey(key), set);
14283
- return set;
14437
+ return;
14284
14438
  }
14285
14439
  function getSubexpressions(expr, head) {
14286
14440
  const result = !head || expr.head === head ? [expr] : [];
@@ -14313,20 +14467,14 @@ function hashCode(s) {
14313
14467
  hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; // | 0 to convert to 32-bit int
14314
14468
  return Math.abs(hash);
14315
14469
  }
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) {
14470
+ function bigintValue(ce, expr) {
14324
14471
  if (expr === null || expr === undefined)
14325
14472
  return null;
14326
14473
  if (typeof expr === 'number')
14327
- return ce.bignum(expr);
14328
- if (isNumberObject(expr)) {
14329
- let s = expr.num
14474
+ return Number.isInteger(expr) ? bigint(expr) : null;
14475
+ if (isNumberExpression(expr)) {
14476
+ const num = isNumberObject(expr) ? expr.num.toString() : expr;
14477
+ let s = num
14330
14478
  .toLowerCase()
14331
14479
  .replace(/[nd]$/g, '')
14332
14480
  .replace(/[\u0009-\u000d\u0020\u00a0]/g, '');
@@ -14338,14 +14486,26 @@ function bignumValue(ce, expr) {
14338
14486
  (trail ?? '');
14339
14487
  }
14340
14488
  if (s === 'nan')
14341
- return ce.bignum('NaN');
14489
+ return null;
14342
14490
  if (s === 'infinity' || s === '+infinity')
14343
- return ce.bignum('+Infinity');
14491
+ return null;
14344
14492
  if (s === '-infinity')
14345
- return ce.bignum('-Infinity');
14346
- return ce.bignum(s);
14493
+ return null;
14494
+ if (s.includes('.'))
14495
+ return null;
14496
+ return bigint(s);
14347
14497
  }
14348
14498
  return null;
14499
+ }
14500
+ function asBigint(expr) {
14501
+ const num = expr.numericValue;
14502
+ if (num === null)
14503
+ return null;
14504
+ if (typeof num === 'number' && Number.isInteger(num))
14505
+ return bigint(num);
14506
+ if (num instanceof Decimal && num.isInteger())
14507
+ return bigint(num);
14508
+ return null;
14349
14509
  }
14350
14510
 
14351
14511
  /**
@@ -14500,586 +14660,148 @@ function getWildcardName(s) {
14500
14660
  // @todo: ['Repeated',...] : repeating match
14501
14661
  // @todo _x:Head or _x:RealNumber
14502
14662
 
14663
+ function isRational(x) {
14664
+ return x !== null && Array.isArray(x);
14665
+ }
14666
+ function isMachineRational(x) {
14667
+ return x !== null && Array.isArray(x) && typeof x[0] === 'number';
14668
+ }
14669
+ function isBigRational(x) {
14670
+ return x !== null && Array.isArray(x) && typeof x[0] === 'bigint';
14671
+ }
14672
+ function isRationalZero(x) {
14673
+ // Note '==' to convert bigint to number
14674
+ return x[0] == 0;
14675
+ }
14676
+ function isRationalOne(x) {
14677
+ return x[0] === x[1];
14678
+ }
14679
+ function isRationalNegativeOne(x) {
14680
+ return x[0] === -x[1];
14681
+ }
14682
+ function machineNumerator(x) {
14683
+ return Number(x[0]);
14684
+ }
14685
+ function machineDenominator(x) {
14686
+ return Number(x[1]);
14687
+ }
14688
+ function isNeg(x) {
14689
+ return x[0] < 0;
14690
+ }
14691
+ function neg(x) {
14692
+ return [-x[0], x[1]];
14693
+ }
14694
+ function inverse(x) {
14695
+ return x[0] < 0 ? [-x[1], -x[0]] : [x[1], x[0]];
14696
+ }
14697
+ function asRational(expr) {
14698
+ const num = expr.numericValue;
14699
+ if (num === null)
14700
+ return undefined;
14701
+ if (Array.isArray(num))
14702
+ return num;
14703
+ if (typeof num === 'number' && Number.isInteger(num))
14704
+ return [num, 1];
14705
+ if (num instanceof Decimal && num.isInteger())
14706
+ return [bigint(num), 1n];
14707
+ return undefined;
14708
+ }
14709
+ function asMachineRational(r) {
14710
+ return [Number(r[0]), Number(r[1])];
14711
+ }
14503
14712
  /**
14504
- * Flatten the arguments.
14505
- * If `expr` was canonical, the result it canonical.
14713
+ * Add a literal numeric value to a rational.
14714
+ * If the rational is a bignum, this is a hint to do the calculation in bignum
14715
+ * (no need to check `bignumPreferred()`).
14716
+ * @param lhs
14717
+ * @param rhs
14718
+ * @returns
14506
14719
  */
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));
14720
+ function add(lhs, rhs) {
14721
+ console.assert(Array.isArray(rhs) ||
14722
+ (rhs.numericValue !== null && !(rhs instanceof Complex)));
14723
+ if (Array.isArray(rhs)) {
14724
+ if (isBigRational(rhs)) {
14725
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14726
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14516
14727
  }
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);
14728
+ if (isBigRational(lhs)) {
14729
+ rhs = [bigint(rhs[0]), bigint(rhs[1])];
14730
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14529
14731
  }
14530
- else
14531
- ys.push(x);
14732
+ return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
14532
14733
  }
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;
14734
+ let rhsNum = rhs.numericValue;
14735
+ console.assert(rhs.isInteger);
14736
+ if (rhsNum !== null && typeof rhsNum === 'number') {
14737
+ if (isMachineRational(lhs))
14738
+ return [lhs[0] + lhs[1] * rhsNum, lhs[1]];
14739
+ return [lhs[0] + lhs[1] * bigint(rhsNum), lhs[1]];
14544
14740
  }
14545
- while (i < ops.length) {
14546
- xs.push(ce.error('unexpected-argument', ops[i]));
14547
- i += 1;
14741
+ if (rhsNum instanceof Decimal) {
14742
+ if (isMachineRational(lhs))
14743
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14744
+ return [lhs[0] + lhs[1] * bigint(rhsNum.toString()), lhs[1]];
14548
14745
  }
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));
14746
+ if (Array.isArray(rhsNum)) {
14747
+ if (isMachineRational(rhsNum))
14748
+ rhsNum = [bigint(rhsNum[0]), bigint(rhsNum[1])];
14749
+ if (isMachineRational(lhs))
14750
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14751
+ return [rhsNum[1] * lhs[0] + rhsNum[0] * lhs[1], rhsNum[1] * lhs[1]];
14567
14752
  }
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;
14753
+ debugger;
14754
+ return lhs;
15038
14755
  }
15039
14756
  function mul(lhs, rhs) {
15040
14757
  console.assert(Array.isArray(rhs) ||
15041
14758
  (rhs.numericValue !== null && !(rhs instanceof Complex)));
15042
14759
  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]];
14760
+ if (isMachineRational(lhs) && isMachineRational(rhs))
14761
+ return [lhs[0] * rhs[0], lhs[1] * rhs[1]];
14762
+ if (isMachineRational(lhs))
14763
+ lhs = [bigint(lhs[0]), bigint(lhs[1])];
14764
+ if (isMachineRational(rhs))
14765
+ rhs = [bigint(rhs[0]), bigint(rhs[1])];
14766
+ return [lhs[0] * rhs[0], lhs[1] * rhs[1]];
15048
14767
  }
15049
14768
  const rhsNum = rhs.numericValue;
15050
14769
  if (rhsNum !== null && typeof rhsNum === 'number') {
14770
+ console.assert(Number.isInteger(rhsNum));
15051
14771
  if (isMachineRational(lhs))
15052
14772
  return [lhs[0] * rhsNum, lhs[1]];
15053
- return [lhs[0].mul(rhsNum), lhs[1]];
14773
+ return [lhs[0] * bigint(rhsNum), lhs[1]];
15054
14774
  }
15055
14775
  if (rhsNum instanceof Decimal) {
14776
+ console.assert(rhsNum.isInteger());
15056
14777
  if (isMachineRational(lhs))
15057
- return [rhsNum.mul(lhs[0]), rhs.engine.bignum(lhs[1])];
15058
- return [rhsNum.mul(lhs[0]), lhs[1]];
14778
+ return [bigint(rhsNum.toString()) * bigint(lhs[0]), bigint(lhs[1])];
14779
+ return [bigint(rhsNum.toString()) * lhs[0], lhs[1]];
15059
14780
  }
15060
14781
  if (Array.isArray(rhsNum)) {
15061
14782
  if (isBigRational(rhsNum))
15062
- return [rhsNum[0].mul(lhs[0]), rhsNum[1].mul(lhs[1])];
14783
+ return [rhsNum[0] * bigint(lhs[0]), rhsNum[1] * bigint(lhs[1])];
15063
14784
  else if (isMachineRational(lhs))
15064
14785
  return [lhs[0] * rhsNum[0], lhs[1] * rhsNum[1]];
15065
- return [lhs[0].mul(rhsNum[0]), lhs[1].mul(rhsNum[1])];
14786
+ return [lhs[0] * bigint(rhsNum[0]), lhs[1] * bigint(rhsNum[1])];
15066
14787
  }
15067
14788
  debugger;
15068
14789
  return lhs;
15069
14790
  }
15070
14791
  function pow(r, exp) {
15071
14792
  console.assert(Number.isInteger(exp));
14793
+ if (exp === 0)
14794
+ return [1, 1];
15072
14795
  if (exp < 0) {
15073
14796
  r = inverse(r);
15074
14797
  exp = -exp;
15075
14798
  }
15076
- if (exp === 0)
15077
- return [1, 1];
15078
14799
  if (exp === 1)
15079
14800
  return r;
15080
14801
  if (isMachineRational(r))
15081
14802
  return [Math.pow(r[0], exp), Math.pow(r[1], exp)];
15082
- return [r[0].pow(exp), r[1].pow(exp)];
14803
+ const bigexp = bigint(exp);
14804
+ return [r[0] ** bigexp, r[1] ** bigexp];
15083
14805
  }
15084
14806
  function reducedRational(r) {
15085
14807
  if (isMachineRational(r)) {
@@ -15087,19 +14809,21 @@ function reducedRational(r) {
15087
14809
  return r;
15088
14810
  if (r[1] < 0)
15089
14811
  r = [-r[0], -r[1]];
14812
+ if (!Number.isFinite(r[1]))
14813
+ return [0, 1];
15090
14814
  const g = gcd$1(r[0], r[1]);
15091
14815
  // If the gcd is 0, return the rational unchanged
15092
14816
  return g <= 1 ? r : [r[0] / g, r[1] / g];
15093
14817
  }
15094
- if (r[0].equals(1) || r[1].equals(1))
14818
+ if (r[0] === 1n || r[1] === 1n)
15095
14819
  return r;
15096
- if (r[1].isNegative())
15097
- r = [r[0].neg(), r[1].neg()];
14820
+ if (r[1] < 0)
14821
+ r = [-r[0], -r[1]];
15098
14822
  const g = gcd(r[0], r[1]);
15099
14823
  // If the gcd is 0, return the rational unchanged
15100
- if (g.lessThanOrEqualTo(1))
14824
+ if (g <= 1)
15101
14825
  return r;
15102
- return [r[0].div(g), r[1].div(g)];
14826
+ return [r[0] / g, r[1] / g];
15103
14827
  }
15104
14828
  /** Return a rational approximation of x */
15105
14829
  function rationalize(x) {
@@ -15168,12 +14892,14 @@ function asCoefficient(expr) {
15168
14892
  const rest = [];
15169
14893
  let coef = [1, 1];
15170
14894
  for (const arg of expr.ops) {
15171
- // Only consider the value of literals
15172
14895
  const n = arg.numericValue;
15173
- if (n === null || n instanceof Complex)
15174
- rest.push(arg);
15175
- else
14896
+ if (n !== null &&
14897
+ ((typeof n === 'number' && Number.isInteger(n)) ||
14898
+ (n instanceof Decimal && n.isInteger()) ||
14899
+ isRational(n)))
15176
14900
  coef = mul(coef, arg);
14901
+ else
14902
+ rest.push(arg);
15177
14903
  }
15178
14904
  coef = reducedRational(coef);
15179
14905
  if (isRationalOne(coef))
@@ -15192,11 +14918,9 @@ function asCoefficient(expr) {
15192
14918
  let [coef1, numer] = asCoefficient(expr.op1);
15193
14919
  const [coef2, denom] = asCoefficient(expr.op2);
15194
14920
  const coef = reducedRational(mul(coef1, inverse(coef2)));
15195
- if (numer.isOne && denom.isOne)
15196
- return [coef, ce._ONE];
15197
14921
  if (denom.isOne)
15198
14922
  return [coef, numer];
15199
- return [coef, ce.fn('Divide', [numer, denom])];
14923
+ return [coef, ce.div(numer, denom)];
15200
14924
  }
15201
14925
  //
15202
14926
  // Power
@@ -15212,9 +14936,9 @@ function asCoefficient(expr) {
15212
14936
  const exponent = expr.op2;
15213
14937
  const e = asSmallInteger(exponent);
15214
14938
  if (e === -1)
15215
- return [inverse(coef), ce.inverse(base)];
14939
+ return [inverse(coef), ce.inv(base)];
15216
14940
  if (e !== null)
15217
- return [pow(coef, e), ce.power(base, exponent)];
14941
+ return [pow(coef, e), ce.pow(base, exponent)];
15218
14942
  // The exponent might be a rational (square root, cubic root...)
15219
14943
  if (exponent.numericValue !== null &&
15220
14944
  Array.isArray(exponent.numericValue)) {
@@ -15228,13 +14952,17 @@ function asCoefficient(expr) {
15228
14952
  // en = -1 -> inverse the extracted coef
15229
14953
  return [
15230
14954
  en === 1 ? [nCoef, dCoef] : [dCoef, nCoef],
15231
- ce.power(ce.mul([ce.number([nRest, dRest]), base]), exponent),
14955
+ ce.pow(ce.mul([ce.number([nRest, dRest]), base]), exponent),
15232
14956
  ];
15233
14957
  }
15234
14958
  }
15235
14959
  return [[1, 1], expr];
15236
14960
  }
15237
14961
  //
14962
+ // Add
14963
+ //
14964
+ if (expr.head === 'Add') ;
14965
+ //
15238
14966
  // Negate
15239
14967
  //
15240
14968
  if (expr.head === 'Negate') {
@@ -15248,8 +14976,8 @@ function asCoefficient(expr) {
15248
14976
  const n = expr.numericValue;
15249
14977
  if (n !== null) {
15250
14978
  if (n instanceof Decimal) {
15251
- if (n.isInteger() && isInMachineRange(n))
15252
- return [[n.toNumber(), 1], ce._ONE];
14979
+ if (n.isInteger())
14980
+ return [[bigint(n.toString()), 1n], ce._ONE];
15253
14981
  if (n.isNegative())
15254
14982
  return [[-1, 1], ce.number(n.neg())];
15255
14983
  }
@@ -15259,7 +14987,7 @@ function asCoefficient(expr) {
15259
14987
  if (n < 0)
15260
14988
  return [[-1, 1], ce.number(-n)];
15261
14989
  }
15262
- if (Array.isArray(n))
14990
+ if (isRational(n))
15263
14991
  return [n, ce._ONE];
15264
14992
  // Make the part positive if the real part is negative
15265
14993
  if (n instanceof Complex && n.re < 0)
@@ -15281,7 +15009,7 @@ function signDiff(lhs, rhs, tolerance) {
15281
15009
  const lhsNum = lhsN.numericValue;
15282
15010
  const rhsNum = rhsN.numericValue;
15283
15011
  if (lhsNum === null || rhsNum === null) {
15284
- // Couldn't calculate a numeric value, use the `sgn`
15012
+ // Couldn't calculate numeric value, use the `sgn` property
15285
15013
  const lhsS = lhs.sgn;
15286
15014
  const rhsS = rhs.sgn;
15287
15015
  if (typeof lhsS !== 'number' || typeof rhsS !== 'number')
@@ -15304,20 +15032,68 @@ function signDiff(lhs, rhs, tolerance) {
15304
15032
  return undefined;
15305
15033
  // In general, it is impossible to always prove equality
15306
15034
  // (Richardson's theorem) but this works often...
15307
- const rhsR = asRational(rhsN);
15308
- if (!rhsR)
15035
+ // At this point, lhsNum and rhsNum are either number or Decimal
15036
+ // (it can't be a rational, because lhs.N() simplifies rationals to number or Decimal)
15037
+ if (isRational(lhsNum) || isRational(rhsNum))
15309
15038
  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)
15039
+ if (typeof lhsNum === 'number' && typeof rhsNum === 'number') {
15040
+ if (chop(rhsNum - lhsNum, tolerance) === 0)
15041
+ return 0;
15042
+ return lhsNum < rhsNum ? -1 : 1;
15043
+ }
15044
+ const ce = lhs.engine;
15045
+ const delta = ce.bignum(rhsNum).sub(ce.bignum(lhsNum));
15046
+ if (chop(delta, tolerance) === 0)
15315
15047
  return 0;
15316
- if (typeof delta === 'number')
15317
- return delta > 0 ? 1 : -1;
15318
15048
  return delta.isPos() ? 1 : -1;
15319
15049
  }
15320
15050
 
15051
+ /**
15052
+ * Flatten the arguments.
15053
+ * If `expr` was canonical, the result it canonical.
15054
+ */
15055
+ function flattenOps(ops, head) {
15056
+ if (!head)
15057
+ return ops;
15058
+ // Bypass memory allocation for the common case where there is nothing to flatten
15059
+ if (ops.every((x) => !x.ops || x.head !== head))
15060
+ return ops;
15061
+ const result = [];
15062
+ for (const arg of ops) {
15063
+ if (!arg.ops || arg.head !== head)
15064
+ result.push(arg);
15065
+ else {
15066
+ // ["f", a, ["f", b, c]] -> ["f", a, b, c]
15067
+ // or ["f", ["f", a]] -> ["f", a]
15068
+ result.push(...flattenOps(arg.ops, head));
15069
+ }
15070
+ }
15071
+ // If number of arguments didn't change, we didn't flatten
15072
+ console.assert(result.length !== ops.length); // @todo check below may not be necessary
15073
+ if (result.length === ops.length)
15074
+ return ops;
15075
+ return result;
15076
+ }
15077
+ function flattenSequence(xs) {
15078
+ // Bypass memory allocation for the common case where there are no sequences
15079
+ if (xs.every((x) => x.head !== 'Sequence'))
15080
+ return xs;
15081
+ const ys = [];
15082
+ for (const x of xs) {
15083
+ if (x.isValid && x.head === 'Sequence') {
15084
+ if (x.ops)
15085
+ ys.push(...x.ops);
15086
+ }
15087
+ else
15088
+ ys.push(x);
15089
+ }
15090
+ return ys;
15091
+ }
15092
+ function canonical(xs) {
15093
+ // Avoid memory allocation if possible
15094
+ return xs.every((x) => x.isCanonical) ? xs : xs.map((x) => x.canonical);
15095
+ }
15096
+
15321
15097
  function negateLiteral(expr, metadata) {
15322
15098
  // Applying negation is safe (doesn't introduce numeric errors)
15323
15099
  // even on floating point numbers
@@ -15329,72 +15105,43 @@ function negateLiteral(expr, metadata) {
15329
15105
  else if (n instanceof Decimal)
15330
15106
  n = n.neg();
15331
15107
  else if (n instanceof complex.exports.Complex)
15332
- n = n.neg();
15333
- else if (Array.isArray(n))
15334
- n = neg(n);
15335
- return expr.engine.number(n, { metadata });
15336
- }
15337
- /**
15338
- * 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.
15342
- *
15343
- * For more thorough distribution (including multiplication), see `distributeNegate`,
15344
- * applicable during a `simplify` or `evaluate` chain.
15345
- */
15346
- function canonicalNegate(expr, metadata) {
15347
- // 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);
15108
+ n = n.neg();
15109
+ else if (Array.isArray(n))
15110
+ n = neg(n);
15111
+ return expr.engine.number(n, { metadata });
15363
15112
  }
15364
15113
  /**
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`.
15114
+ * Distribute `Negate` (multiply by -1) if expr is a number literal, an
15115
+ * addition or multiplication or another `Negate`.
15371
15116
  *
15372
- * Call during a `simplify` or `evaluate` chain. Use `caonnicalNegate` during a
15373
- * `canonical` chain.
15117
+ * It is important to do all these to handle cases like
15118
+ * `-3x` -> ["Negate, ["Multiply", 3, "x"]] -> ["Multiply, -3, x]
15374
15119
  */
15375
- function distributeNegate(expr) {
15376
- if (expr.numericValue !== null)
15377
- return negateLiteral(expr);
15120
+ function canonicalNegate(expr, metadata) {
15121
+ // Negate(Negate(x)) -> x
15378
15122
  if (expr.head === 'Negate')
15379
15123
  return expr.op1;
15380
- const ce = expr.engine;
15124
+ if (expr.numericValue !== null)
15125
+ return negateLiteral(expr, metadata);
15381
15126
  // Distribute over addition
15382
15127
  // Negate(Add(a, b)) -> Add(Negate(a), Negate(b))
15383
15128
  if (expr.head === 'Add') {
15384
- let ops = expr.ops.map((x) => distributeNegate(x));
15385
- ops = flattenOps(ops, 'Add') ?? ops;
15386
- return ce.add(ops);
15129
+ let ops = expr.ops.map((x) => canonicalNegate(x));
15130
+ ops = flattenOps(ops, 'Add');
15131
+ return expr.engine.add(ops, metadata);
15387
15132
  }
15388
15133
  // Distribute over multiplication
15389
15134
  // Negate(Multiply(a, b)) -> Multiply(Negate(a), b)
15390
15135
  if (expr.head === 'Multiply') {
15391
- return negateProduct(ce, expr.ops);
15136
+ return negateProduct(expr.engine, expr.ops);
15392
15137
  }
15393
15138
  // Distribute over division
15394
15139
  // Negate(Divide(a, b)) -> Divide(Negate(a), b)
15395
15140
  if (expr.head === 'Divide')
15396
- return ce.divide(distributeNegate(expr.op1), expr.op2);
15397
- return ce._fn('Negate', [expr]);
15141
+ return expr.engine._fn('Divide', [canonicalNegate(expr.op1), expr.op2]);
15142
+ // 'Subtract' is canonicalized into `Add`, so don't have to worry about it
15143
+ console.assert(expr.head !== 'Subtract');
15144
+ return expr.engine._fn('Negate', [expr], metadata);
15398
15145
  }
15399
15146
  // Given a list of terms in a product, find the "best" one to negate in
15400
15147
  // order to negate the entire product:
@@ -15422,7 +15169,7 @@ function negateProduct(ce, args) {
15422
15169
  result.push(arg);
15423
15170
  else {
15424
15171
  done = true;
15425
- result.push(distributeNegate(arg));
15172
+ result.push(canonicalNegate(arg));
15426
15173
  }
15427
15174
  }
15428
15175
  if (done)
@@ -15434,7 +15181,7 @@ function negateProduct(ce, args) {
15434
15181
  result.push(arg);
15435
15182
  else {
15436
15183
  done = true;
15437
- result.push(distributeNegate(arg));
15184
+ result.push(canonicalNegate(arg));
15438
15185
  }
15439
15186
  }
15440
15187
  if (done)
@@ -15442,7 +15189,7 @@ function negateProduct(ce, args) {
15442
15189
  return ce._fn('Negate', [ce._fn('Multiply', args)]);
15443
15190
  }
15444
15191
  function processNegate(_ce, x, _mode = 'simplify') {
15445
- return distributeNegate(x);
15192
+ return canonicalNegate(x);
15446
15193
  }
15447
15194
 
15448
15195
  /**
@@ -15499,7 +15246,7 @@ function expand(expr) {
15499
15246
  .add([expand(expr.op1), expand2(ce._NEGATIVE_ONE, expr.op1)])
15500
15247
  .simplify();
15501
15248
  if (expr.head === 'Divide')
15502
- return ce.divide(expand(expr.op1), expand(expr.op2)).simplify();
15249
+ return ce.div(expand(expr.op1), expand(expr.op2)).simplify();
15503
15250
  if (expr.head === 'Multiply') {
15504
15251
  if (expr.nops === 2)
15505
15252
  return expand2(expr.op1, expr.op2);
@@ -15508,13 +15255,13 @@ function expand(expr) {
15508
15255
  if (expr.head === 'Power') {
15509
15256
  const op1head = expr.op1.head;
15510
15257
  if (op1head === 'Multiply')
15511
- return ce.mul(expr.op1.ops.map((x) => ce.power(x, expr.op2))).simplify();
15258
+ return ce.mul(expr.op1.ops.map((x) => ce.pow(x, expr.op2))).simplify();
15512
15259
  if (op1head === 'Negate') {
15513
15260
  const n = asSmallInteger(expr.op2);
15514
15261
  if (n !== null && n > 0) {
15515
15262
  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();
15263
+ return ce.pow(expr.op1.op1, expr.op2).simplify();
15264
+ return ce.neg(ce.pow(expr.op1.op1, expr.op2)).simplify();
15518
15265
  }
15519
15266
  }
15520
15267
  if (op1head === 'Add') {
@@ -15522,7 +15269,7 @@ function expand(expr) {
15522
15269
  if (n !== null) {
15523
15270
  if (n > 0)
15524
15271
  return expandN(expr.op1, n).simplify();
15525
- return ce.inverse(expandN(expr.op1, -n)).simplify();
15272
+ return ce.inv(expandN(expr.op1, -n)).simplify();
15526
15273
  }
15527
15274
  }
15528
15275
  }
@@ -15641,9 +15388,7 @@ const UNIVARIATE_ROOTS = [
15641
15388
  function findUnivariateRoots(expr, x) {
15642
15389
  const ce = expr.engine;
15643
15390
  if (expr.head === 'Equal') {
15644
- expr = ce
15645
- .add([expr.op1.canonical, ce.negate(expr.op2.canonical)])
15646
- .simplify();
15391
+ expr = ce.add([expr.op1.canonical, ce.neg(expr.op2.canonical)]).simplify();
15647
15392
  }
15648
15393
  const rules = ce.cache('univariate-roots-rules', () => boxRules(ce, UNIVARIATE_ROOTS));
15649
15394
  const result = matchRules(expand(expr).subs({ [x]: '_x' }, { canonical: false }), rules, { _x: ce.symbol('_x') });
@@ -15687,27 +15432,27 @@ function assume(proposition) {
15687
15432
  function assumeEquality(proposition) {
15688
15433
  console.assert(proposition.head === 'Equal');
15689
15434
  // Four cases:
15690
- // 1/ proposition contains no free variable without value
15435
+ // 1/ proposition contains no free variables
15691
15436
  // e.g. `2 + 1 = 3`, `\pi + 1 = \pi`
15692
15437
  // => evaluate and return
15693
- // 2/ lhs is a single free variable with no value and `rhs` does not
15694
- // contain `lhs`
15438
+ // 2/ lhs is a single free variable and `rhs` does not contain `lhs`
15695
15439
  // e.g. `x = 2`, `x = 2\pi`
15696
15440
  // => if `lhs` has a definition, set its value to `rhs`, otherwise
15697
15441
  // define a new symbol with a value of `rhs`
15698
- // 3/ proposition contains a single free variable without value
15442
+ // 3/ proposition contains a single free variable
15699
15443
  // => solve for the free variable, create new def or set value of the
15700
15444
  // free variable with the root(s) as value
15701
- // 4/ proposition contains multiple free variables with no value
15445
+ // 4/ proposition contains multiple free variables
15702
15446
  // => add (lhs - rhs = 0) to assumptions DB
15703
15447
  // Case 1
15704
- const unvals = unvaluedIdentifiers(proposition);
15705
- if (unvals.length === 0) {
15448
+ const freeVars = proposition.freeVars;
15449
+ if (freeVars.length === 0) {
15706
15450
  const val = proposition.evaluate();
15707
15451
  if (val.symbol === 'True')
15708
15452
  return 'tautology';
15709
15453
  if (val.symbol === 'False')
15710
15454
  return 'contradiction';
15455
+ console.log(proposition.canonical.evaluate());
15711
15456
  return 'not-a-predicate';
15712
15457
  }
15713
15458
  const ce = proposition.engine;
@@ -15728,17 +15473,14 @@ function assumeEquality(proposition) {
15728
15473
  return 'ok';
15729
15474
  }
15730
15475
  // Case 3
15731
- if (unvals.length === 1) {
15732
- const lhs = unvals[0];
15476
+ if (freeVars.length === 1) {
15477
+ const lhs = freeVars[0];
15733
15478
  const sols = findUnivariateRoots(proposition, lhs);
15734
15479
  if (sols.length === 0) {
15735
15480
  ce.assumptions.set(ce.box([
15736
15481
  'Equal',
15737
15482
  ce
15738
- .add([
15739
- proposition.op1.canonical,
15740
- ce.negate(proposition.op2.canonical),
15741
- ])
15483
+ .add([proposition.op1.canonical, ce.neg(proposition.op2.canonical)])
15742
15484
  .simplify(),
15743
15485
  0,
15744
15486
  ]), true);
@@ -15759,17 +15501,16 @@ function assumeEquality(proposition) {
15759
15501
  }
15760
15502
  function assumeInequality(proposition) {
15761
15503
  //
15762
- // 1/ lhs is a single free var with no def
15763
- // e.g. x < 0
15504
+ // 1/ lhs is a single **undefined** free var e.g. "x < 0"
15764
15505
  // => define a new var, if the domain can be inferred set it, otherwise
15765
15506
  // 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
15507
+ // 2/ (lhs - rhs) is an expression with no free vars
15508
+ // e.g. "\pi < 5"
15768
15509
  // => evaluate
15769
- // 3/ (lhs - rhs) is an expression with a single free var with no value
15770
- // e.g. x + 1 < \pi
15510
+ // 3/ (lhs - rhs) is an expression with a single **undefined** free var
15511
+ // e.g. "x + 1 < \pi"
15771
15512
  // => add def as RealNumber, add to assumptions
15772
- // 4/ (lhs - rhs) is an expression with multiple free vars with no value
15513
+ // 4/ (lhs - rhs) is an expression with multiple free vars
15773
15514
  // e.g. x + y < 0
15774
15515
  // => add to assumptions
15775
15516
  const ce = proposition.engine;
@@ -15805,6 +15546,7 @@ function assumeInequality(proposition) {
15805
15546
  }
15806
15547
  return 'ok';
15807
15548
  }
15549
+ // @todo: handle if proposition.op1 *has* a def (and no value)
15808
15550
  // Normalize to Less, LessEqual
15809
15551
  let op = '';
15810
15552
  let lhs;
@@ -15831,20 +15573,20 @@ function assumeInequality(proposition) {
15831
15573
  }
15832
15574
  if (!op)
15833
15575
  return 'internal-error';
15834
- const p = ce.add([lhs.canonical, ce.negate(rhs.canonical)]).simplify();
15835
- const unvals = unvaluedIdentifiers(p);
15576
+ const p = ce.add([lhs.canonical, ce.neg(rhs.canonical)]).simplify();
15836
15577
  // Case 2
15837
15578
  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';
15579
+ if (result.symbol === 'True')
15580
+ return 'tautology';
15581
+ if (result.symbol === 'False')
15582
+ return 'contradiction';
15583
+ const freeVars = result.freeVars;
15584
+ if (freeVars.length === 0)
15843
15585
  return 'not-a-predicate';
15844
- }
15845
15586
  // Case 3
15846
- if (unvals.length === 1) {
15847
- ce.defineSymbol(unvals[0], { domain: 'ExtendedRealNumber' });
15587
+ if (freeVars.length === 1) {
15588
+ if (!ce.lookupSymbol(freeVars[0]))
15589
+ ce.defineSymbol(freeVars[0], { domain: 'ExtendedRealNumber' });
15848
15590
  }
15849
15591
  // Case 3, 4
15850
15592
  console.assert(result.head === 'Less' || result.head === 'LessEqual');
@@ -15916,20 +15658,7 @@ function hasDef(ce, s) {
15916
15658
  return (ce.lookupSymbol(s) ?? ce.lookupFunction(s)) !== undefined;
15917
15659
  }
15918
15660
  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);
15661
+ return expr.symbols.filter((x) => !hasDef(expr.engine, x));
15933
15662
  }
15934
15663
  function hasValue(ce, s) {
15935
15664
  if (ce.lookupFunction(s))
@@ -15941,20 +15670,7 @@ function isInequality(expr) {
15941
15670
  if (typeof h !== 'string')
15942
15671
  return false;
15943
15672
  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
- // }
15673
+ }
15958
15674
 
15959
15675
  /**
15960
15676
  * AbstractBoxedExpression
@@ -16044,7 +15760,14 @@ class AbstractBoxedExpression {
16044
15760
  return this.getSubexpressions('');
16045
15761
  }
16046
15762
  get symbols() {
16047
- return [...getSymbols(this, new Set())].map((x) => this.engine.symbol(x, { canonical: false }));
15763
+ const set = new Set();
15764
+ getSymbols(this, set);
15765
+ return Array.from(set);
15766
+ }
15767
+ get freeVars() {
15768
+ const set = new Set();
15769
+ getFreeVars(this, set);
15770
+ return Array.from(set);
16048
15771
  }
16049
15772
  get errors() {
16050
15773
  return this.getSubexpressions('Error');
@@ -16274,6 +15997,132 @@ class AbstractBoxedExpression {
16274
15997
  }
16275
15998
  }
16276
15999
 
16000
+ function factorial(ce, n) {
16001
+ if (!n.isInteger() || n.isNegative())
16002
+ return ce._BIGNUM_NAN;
16003
+ if (n.lessThan(10))
16004
+ return ce.bignum([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800][n.toNumber()]);
16005
+ if (n.gt(Number.MAX_SAFE_INTEGER)) {
16006
+ let val = ce._BIGNUM_ONE;
16007
+ let i = ce._BIGNUM_TWO;
16008
+ while (i.lessThan(n)) {
16009
+ val = val.mul(i);
16010
+ i = i.add(1);
16011
+ }
16012
+ return val;
16013
+ }
16014
+ if (n.modulo(2).eq(1)) {
16015
+ return n.times(factorial(ce, n.minus(1)));
16016
+ }
16017
+ let loop = n.toNumber();
16018
+ let sum = n;
16019
+ let val = n;
16020
+ while (loop > 2) {
16021
+ loop -= 2;
16022
+ sum = sum.add(loop);
16023
+ val = val.mul(sum);
16024
+ }
16025
+ return val;
16026
+ }
16027
+ const gammaG = 7;
16028
+ // Spouge approximation (suitable for large arguments)
16029
+ function lngamma$1(ce, z) {
16030
+ if (z.isNegative())
16031
+ return ce._BIGNUM_NAN;
16032
+ const GAMMA_P_LN = ce.cache('gamma-p-ln', () => {
16033
+ return [
16034
+ '0.99999999999999709182',
16035
+ '57.156235665862923517',
16036
+ '-59.597960355475491248',
16037
+ '14.136097974741747174',
16038
+ '-0.49191381609762019978',
16039
+ '0.33994649984811888699e-4',
16040
+ '0.46523628927048575665e-4',
16041
+ '-0.98374475304879564677e-4',
16042
+ '0.15808870322491248884e-3',
16043
+ '-0.21026444172410488319e-3',
16044
+ '0.2174396181152126432e-3',
16045
+ '-0.16431810653676389022e-3',
16046
+ '0.84418223983852743293e-4',
16047
+ '-0.2619083840158140867e-4',
16048
+ '0.36899182659531622704e-5',
16049
+ ].map((x) => ce.bignum(x));
16050
+ });
16051
+ let x = GAMMA_P_LN[0];
16052
+ for (let i = GAMMA_P_LN.length - 1; i > 0; --i) {
16053
+ x = x.add(GAMMA_P_LN[i].div(z.add(i)));
16054
+ }
16055
+ const GAMMA_G_LN = ce.cache('gamma-g-ln', () => ce.bignum(607).div(128));
16056
+ const t = z.add(GAMMA_G_LN).add(ce._BIGNUM_HALF);
16057
+ return ce._BIGNUM_NEGATIVE_ONE
16058
+ .acos()
16059
+ .mul(ce._BIGNUM_TWO)
16060
+ .log()
16061
+ .mul(ce._BIGNUM_HALF)
16062
+ .add(t.log().mul(z.add(ce._BIGNUM_HALF)).minus(t).add(x.log()).minus(z.log()));
16063
+ }
16064
+ // From https://github.com/substack/gamma.js/blob/master/index.js
16065
+ function gamma$1(ce, z) {
16066
+ if (z.lessThan(ce._BIGNUM_HALF)) {
16067
+ const pi = ce._BIGNUM_NEGATIVE_ONE.acos();
16068
+ return pi.div(pi
16069
+ .mul(z)
16070
+ .sin()
16071
+ .mul(gamma$1(ce, ce._BIGNUM_ONE.sub(z))));
16072
+ }
16073
+ if (z.greaterThan(100))
16074
+ return lngamma$1(ce, z).exp();
16075
+ z = z.sub(1);
16076
+ // coefficients for gamma=7, kmax=8 Lanczos method
16077
+ // Source: GSL/specfunc/gamma.c
16078
+ const LANCZOS_7_C = ce.cache('lanczos-7-c', () => {
16079
+ return [
16080
+ '0.99999999999980993227684700473478',
16081
+ '676.520368121885098567009190444019',
16082
+ '-1259.13921672240287047156078755283',
16083
+ '771.3234287776530788486528258894',
16084
+ '-176.61502916214059906584551354',
16085
+ '12.507343278686904814458936853',
16086
+ '-0.13857109526572011689554707',
16087
+ '9.984369578019570859563e-6',
16088
+ '1.50563273514931155834e-7',
16089
+ ].map((x) => ce.bignum(x));
16090
+ });
16091
+ let x = LANCZOS_7_C[0];
16092
+ for (let i = 1; i < gammaG + 2; i++)
16093
+ x = x.add(LANCZOS_7_C[i].div(z.add(i)));
16094
+ const t = z.add(gammaG).add(ce._BIGNUM_HALF);
16095
+ return ce._BIGNUM_NEGATIVE_ONE
16096
+ .acos()
16097
+ .times(ce._BIGNUM_TWO)
16098
+ .sqrt()
16099
+ .mul(x.mul(t.neg().exp()).mul(t.pow(z.add(ce._BIGNUM_HALF))));
16100
+ }
16101
+ /**
16102
+ * If the exponent of the bignum is in the range of the exponents
16103
+ * for machine numbers,return true.
16104
+ */
16105
+ function isInMachineRange(d) {
16106
+ if (!d.isFinite())
16107
+ return true; // Infinity and NaN are in machine range
16108
+ // Are there too many significant digits?
16109
+ // Maximum Safe Integer is 9007199254740991
16110
+ // Digits in Decimal are stored by blocks of 7.
16111
+ // Three blocks, with the first block = 90 is close to the maximum
16112
+ if (d.d.length > 3 || (d.d.length === 3 && d.d[0] >= 90))
16113
+ return false;
16114
+ console.assert(d.precision() <= 16);
16115
+ // Is the exponent within range?
16116
+ // With a binary 64 IEEE 754 number:
16117
+ // significant bits: 53 -> 15 digits
16118
+ // exponent bits: 11. emax = 307, emin = -306)
16119
+ return d.e < 308 && d.e > -306;
16120
+ }
16121
+ // export function asMachineNumber(d: Decimal): number | null {
16122
+ // if (d.precision() < 15 && d.e < 308 && d.e > -306) return d.toNumber();
16123
+ // return null;
16124
+ // }
16125
+
16277
16126
  /**
16278
16127
  * The total degree of an expression is the sum of the
16279
16128
  * of the positive integer degrees of the factors in the expression:
@@ -16326,11 +16175,15 @@ function maxDegree(expr) {
16326
16175
  function lex(expr) {
16327
16176
  if (expr.symbol)
16328
16177
  return expr.symbol;
16329
- if (expr.ops)
16330
- return expr.ops
16331
- .map((x) => lex(x))
16332
- .filter((x) => x.length > 0)
16333
- .join('"');
16178
+ if (expr.ops) {
16179
+ const h = typeof expr.head === 'string' ? expr.head : lex(expr.head);
16180
+ return (h +
16181
+ '"' +
16182
+ expr.ops
16183
+ .map((x) => lex(x))
16184
+ .filter((x) => x.length > 0)
16185
+ .join('"'));
16186
+ }
16334
16187
  return '';
16335
16188
  }
16336
16189
 
@@ -16343,6 +16196,12 @@ function sortAdd(ce, ops) {
16343
16196
  return ops.sort((a, b) => {
16344
16197
  const aLex = lex(a);
16345
16198
  const bLex = lex(b);
16199
+ if (!aLex && !bLex)
16200
+ return order(a, b);
16201
+ if (!aLex)
16202
+ return +1;
16203
+ if (!bLex)
16204
+ return -1;
16346
16205
  if (aLex < bLex)
16347
16206
  return -1;
16348
16207
  if (aLex > bLex)
@@ -16395,7 +16254,11 @@ function sortAdd(ce, ops) {
16395
16254
  *
16396
16255
  */
16397
16256
  function order(a, b) {
16398
- console.assert(a.isCanonical && b.isCanonical);
16257
+ // console.assert(a.isCanonical && b.isCanonical);
16258
+ if (a === b)
16259
+ return 0;
16260
+ if (a.numericValue !== null && a.numericValue === b.numericValue)
16261
+ return 0;
16399
16262
  //
16400
16263
  // 1/ Literal numeric values
16401
16264
  //
@@ -16423,6 +16286,17 @@ function order(a, b) {
16423
16286
  return +1;
16424
16287
  return -1;
16425
16288
  }
16289
+ if (a.numericValue) {
16290
+ if (b.numericValue) {
16291
+ return +1;
16292
+ }
16293
+ return -1;
16294
+ }
16295
+ if (a.head === 'Sqrt' && a.op1.numericValue) {
16296
+ if (b.head === 'Sqrt' && b.op1.numericValue)
16297
+ return order(a.op1, b.op1);
16298
+ return -1;
16299
+ }
16426
16300
  //
16427
16301
  // 3/ Symbols
16428
16302
  //
@@ -16540,9 +16414,7 @@ class Product {
16540
16414
  this._isCanonical = options.canonical;
16541
16415
  this.engine = ce;
16542
16416
  this._sign = 1;
16543
- this._rational = bignumPreferred(ce)
16544
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
16545
- : [1, 1];
16417
+ this._rational = bignumPreferred(ce) ? [1n, 1n] : [1, 1];
16546
16418
  // this._squareRootRational = this._rational;
16547
16419
  this._complex = Complex.ONE;
16548
16420
  this._bignum = ce._BIGNUM_ONE;
@@ -16573,16 +16445,14 @@ class Product {
16573
16445
  */
16574
16446
  addTerm(term) {
16575
16447
  console.assert(term.isCanonical);
16448
+ if (term.head === 'Multiply') {
16449
+ for (const t of term.ops)
16450
+ this.addTerm(t);
16451
+ return;
16452
+ }
16576
16453
  if (this._isCanonical) {
16577
16454
  if (term.isNothing)
16578
16455
  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
16456
  // If we're calculation a canonical product, fold exact literals into
16587
16457
  // running terms
16588
16458
  if (term.numericValue !== null) {
@@ -16622,10 +16492,7 @@ class Product {
16622
16492
  num = num.neg();
16623
16493
  }
16624
16494
  if (num.isInteger())
16625
- this._rational = mul(this._rational, [
16626
- num,
16627
- this.engine._BIGNUM_ONE,
16628
- ]);
16495
+ this._rational = mul(this._rational, [bigint(num), 1n]);
16629
16496
  else if (bignumPreferred(this.engine))
16630
16497
  this._bignum = this._bignum.mul(num);
16631
16498
  else
@@ -16670,6 +16537,11 @@ class Product {
16670
16537
  rest = rest.op1;
16671
16538
  }
16672
16539
  }
16540
+ else if (rest.head === 'Divide') {
16541
+ this.addTerm(rest.op1);
16542
+ exponent = [-1, 1];
16543
+ rest = rest.op2;
16544
+ }
16673
16545
  // Look for the base, and add the exponent if already in the list of terms
16674
16546
  let found = false;
16675
16547
  for (const x of this._terms) {
@@ -16692,25 +16564,12 @@ class Product {
16692
16564
  let b = ce._BIGNUM_ONE;
16693
16565
  if (!isRationalOne(this._rational)) {
16694
16566
  if (isBigRational(this._rational))
16695
- b = this._rational[0].div(this._rational[1]);
16567
+ b = ce
16568
+ .bignum(this._rational[0].toString())
16569
+ .div(ce.bignum(this._rational[1].toString()));
16696
16570
  else
16697
16571
  b = ce.bignum(this._rational[0]).div(this._rational[1]);
16698
16572
  }
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
16573
  b = b.mul(this._bignum).mul(this._sign * this._number);
16715
16574
  if (this._complex.im !== 0) {
16716
16575
  const z = this._complex.mul(b.toNumber());
@@ -16727,21 +16586,10 @@ class Product {
16727
16586
  let n = 1;
16728
16587
  if (!isRationalOne(this._rational)) {
16729
16588
  if (isBigRational(this._rational))
16730
- n = this._rational[0].toNumber() / this._rational[1].toNumber();
16589
+ n = Number(this._rational[0]) / Number(this._rational[1]);
16731
16590
  else
16732
16591
  n = this._rational[0] / this._rational[1];
16733
16592
  }
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
16593
  n *= this._sign * this._number * this._bignum.toNumber();
16746
16594
  if (this._complex.im !== 0) {
16747
16595
  const z = this._complex.mul(n);
@@ -16779,7 +16627,7 @@ class Product {
16779
16627
  if (mode === 'rational') {
16780
16628
  if (machineNumerator(this._rational) !== 1) {
16781
16629
  if (isBigRational(this._rational))
16782
- b = b.mul(this._rational[0]);
16630
+ b = b.mul(ce.bignum(this._rational[0]));
16783
16631
  else
16784
16632
  n *= this._rational[0];
16785
16633
  }
@@ -16798,19 +16646,6 @@ class Product {
16798
16646
  unitTerms.push(ce.number(this._rational));
16799
16647
  }
16800
16648
  }
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
16649
  // Literal
16815
16650
  if (!b.equals(ce._BIGNUM_ONE))
16816
16651
  unitTerms.push(ce.number(b.mul(n)));
@@ -16882,8 +16717,7 @@ class Product {
16882
16717
  });
16883
16718
  if (groupedTerms === null)
16884
16719
  return ce._NAN;
16885
- let terms = termsAsExpressions(ce, groupedTerms);
16886
- terms = flattenOps(terms, 'Multiply') ?? terms;
16720
+ const terms = termsAsExpressions(ce, groupedTerms);
16887
16721
  if (terms.length === 0)
16888
16722
  return ce._ONE;
16889
16723
  if (terms.length === 1)
@@ -16898,8 +16732,7 @@ class Product {
16898
16732
  const xsNumerator = [];
16899
16733
  const xsDenominator = [];
16900
16734
  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()))
16735
+ if (x.exponent[0] >= 0)
16903
16736
  xsNumerator.push(x);
16904
16737
  else
16905
16738
  xsDenominator.push({
@@ -16907,16 +16740,13 @@ class Product {
16907
16740
  terms: x.terms,
16908
16741
  });
16909
16742
  const ce = this.engine;
16910
- let numeratorTerms = termsAsExpressions(ce, xsNumerator);
16911
- numeratorTerms = flattenOps(numeratorTerms, 'Multiply') ?? numeratorTerms;
16743
+ const numeratorTerms = termsAsExpressions(ce, xsNumerator);
16912
16744
  let numerator = ce._ONE;
16913
16745
  if (numeratorTerms.length === 1)
16914
16746
  numerator = numeratorTerms[0];
16915
16747
  else if (numeratorTerms.length > 0)
16916
16748
  numerator = ce._fn('Multiply', numeratorTerms);
16917
- let denominatorTerms = termsAsExpressions(ce, xsDenominator);
16918
- denominatorTerms =
16919
- flattenOps(denominatorTerms, 'Multiply') ?? denominatorTerms;
16749
+ const denominatorTerms = termsAsExpressions(ce, xsDenominator);
16920
16750
  let denominator = ce._ONE;
16921
16751
  if (denominatorTerms.length === 1)
16922
16752
  denominator = denominatorTerms[0];
@@ -16930,7 +16760,7 @@ class Product {
16930
16760
  if (denominator.isOne)
16931
16761
  return numerator;
16932
16762
  if (denominator.isNegativeOne)
16933
- return this.engine.negate(numerator);
16763
+ return this.engine.neg(numerator);
16934
16764
  }
16935
16765
  return this.engine._fn('Divide', [numerator, denominator]);
16936
16766
  }
@@ -16958,35 +16788,23 @@ function degreeOrder(a, b) {
16958
16788
  const keyB = degreeKey(b.exponent);
16959
16789
  if (keyA !== keyB)
16960
16790
  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]);
16791
+ const [a_n, a_d] = [
16792
+ machineNumerator(a.exponent),
16793
+ machineDenominator(a.exponent),
16794
+ ];
16795
+ const [b_n, b_d] = [
16796
+ machineNumerator(b.exponent),
16797
+ machineDenominator(b.exponent),
16798
+ ];
16799
+ return a_n / a_d - b_n / b_d;
16981
16800
  }
16982
16801
  function termsAsExpressions(ce, terms) {
16983
- terms = terms.sort(degreeOrder);
16984
- const result = terms.map((x) => {
16985
- const t = flattenOps(x.terms, 'Multiply') ?? x.terms;
16802
+ const result = terms.sort(degreeOrder).map((x) => {
16803
+ const t = flattenOps(x.terms, 'Multiply');
16986
16804
  const base = t.length <= 1 ? t[0] : ce._fn('Multiply', t.sort(order));
16987
16805
  if (isRationalOne(x.exponent))
16988
16806
  return base;
16989
- return ce.power(base, x.exponent);
16807
+ return ce.pow(base, x.exponent);
16990
16808
  });
16991
16809
  return flattenOps(result, 'Multiply') ?? result;
16992
16810
  }
@@ -17045,7 +16863,7 @@ function serializeJsonCanonicalFunction(ce, head, args, metadata) {
17045
16863
  if (exp === 2 && !exclusions.includes('Square'))
17046
16864
  return serializeJsonFunction(ce, 'Square', [args[0]], metadata);
17047
16865
  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);
16866
+ return serializeJsonFunction(ce, 'Divide', [ce._ONE, exp === -1 ? args[0] : ce.pow(args[0], -exp)], metadata);
17049
16867
  }
17050
16868
  const r = args[1].numericValue;
17051
16869
  if (!exclusions.includes('Sqrt') && r === 0.5)
@@ -17328,17 +17146,17 @@ function repeatingDecimals(ce, s) {
17328
17146
  for (let j = 0; j <= MAX_REPEATING_PATTERN_LENGTH; j++) {
17329
17147
  const repetend = fractionalPart.substring(i, i + j + 1);
17330
17148
  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 ?? ''));
17149
+ if (times < 3)
17150
+ break;
17151
+ if ((prefix + repetend.repeat(times + 1)).startsWith(fractionalPart)) {
17152
+ // Found a repeating pattern
17153
+ // Aktually...
17154
+ if (repetend === '0') {
17155
+ if (lastDigit === '0')
17156
+ return wholepart + '.' + prefix + (exponent ?? '');
17157
+ return s;
17341
17158
  }
17159
+ return (wholepart + '.' + prefix + '(' + repetend + ')' + (exponent ?? ''));
17342
17160
  }
17343
17161
  }
17344
17162
  }
@@ -17818,10 +17636,202 @@ const SIMPLIFY_RULES = [];
17818
17636
  // return expr;
17819
17637
  // }
17820
17638
 
17639
+ function validateArgumentCount(ce, ops, count) {
17640
+ if (ops.length === count)
17641
+ return ops;
17642
+ const xs = [...ops.slice(0, count)];
17643
+ let i = Math.min(count, ops.length);
17644
+ while (i < count) {
17645
+ xs.push(ce.error('missing'));
17646
+ i += 1;
17647
+ }
17648
+ while (i < ops.length) {
17649
+ xs.push(ce.error('unexpected-argument', ops[i]));
17650
+ i += 1;
17651
+ }
17652
+ return xs;
17653
+ }
17654
+ /**
17655
+ * Validation of arguments is normally done by checking the signature of the
17656
+ * function vs the arguments of the expression. However, we have a fastpath
17657
+ * for some common operations (add, multiply, power, neg, etc...) that bypasses
17658
+ * the regular checks. This is its replacements. Since all those fastpath
17659
+ * functions are numeric (i.e. have numeric arguments and return a numeric
17660
+ * value), we do a simple numeric check of all arguments, and verify we have
17661
+ * the number of expected arguments.
17662
+ */
17663
+ function validateNumericArgs(ce, ops, count) {
17664
+ // @fastpath
17665
+ if (!ce.strict)
17666
+ return ops;
17667
+ let xs;
17668
+ if (count === undefined)
17669
+ xs = ops;
17670
+ else {
17671
+ xs = [];
17672
+ for (let i = 0; i <= Math.max(count - 1, ops.length - 1); i++) {
17673
+ if (i > count - 1)
17674
+ xs.push(ce.error('unexpected-argument', ops[i]));
17675
+ else
17676
+ xs.push(ops[i] !== undefined
17677
+ ? ce.box(ops[i])
17678
+ : ce.error(['missing', 'Number']));
17679
+ }
17680
+ }
17681
+ return xs.map((op) => !op.isValid || op.isNumber
17682
+ ? op
17683
+ : ce.error(['incompatible-domain', 'Number', op.domain], op));
17684
+ }
17685
+ /** Return `null` if the `ops` match the sig. Otherwise, return an array
17686
+ * of expressions indicating the mismatched arguments.
17687
+ *
17688
+ */
17689
+ function validateSignature(sig, ops, codomain) {
17690
+ const ce = sig.engine;
17691
+ // @fastpath
17692
+ if (!ce.strict)
17693
+ return ops;
17694
+ const opsDomain = ops.map((x) => x.domain);
17695
+ const targetSig = ce.domain([
17696
+ 'Function',
17697
+ ...opsDomain,
17698
+ codomain ?? 'Anything',
17699
+ ]);
17700
+ if (sig.isCompatible(targetSig))
17701
+ return null;
17702
+ //
17703
+ // There was a problem:
17704
+ // 1/ not enough arguments
17705
+ // 2/ too many arguments
17706
+ // 3/ incompatible argument domain
17707
+ //
17708
+ // Iterate over each arg, and replace with error expression when appropriate
17709
+ //
17710
+ const expectedArgs = sig.domainArgs.slice(0, -1);
17711
+ const count = Math.max(expectedArgs.length, opsDomain.length);
17712
+ let newOps = [];
17713
+ let rest = [...ops];
17714
+ for (let i = 0; i <= count - 1; i++)
17715
+ [newOps, rest] = validateNextArgument(ce, expectedArgs[i], newOps, rest);
17716
+ // Remove any 'Nothing' at the end
17717
+ while (newOps.length > 0 && newOps[newOps.length - 1].symbol === 'Nothing')
17718
+ newOps.pop();
17719
+ return newOps;
17720
+ }
17721
+ function validateArgument(ce, arg, dom) {
17722
+ if (dom === undefined)
17723
+ return ce.error('unexpected-argument', arg);
17724
+ if (arg === undefined)
17725
+ return ce.error(['missing', dom]);
17726
+ if (!arg.isValid)
17727
+ return arg;
17728
+ if (arg?.domain.isCompatible(dom))
17729
+ return arg;
17730
+ return ce.error(['incompatible-domain', dom, arg.domain], arg);
17731
+ }
17732
+ function validateNextArgument(ce, dom, matched, ops) {
17733
+ let next = ops.shift();
17734
+ if (dom === undefined)
17735
+ return [[...matched, ce.error('unexpected-argument', next)], ops];
17736
+ if (!Array.isArray(dom)) {
17737
+ if (!next)
17738
+ return [[...matched, ce.error(['missing', dom])], ops];
17739
+ if (!next.domain.isCompatible(dom)) {
17740
+ return [
17741
+ [...matched, ce.error(['incompatible-domain', dom, next.domain], next)],
17742
+ ops,
17743
+ ];
17744
+ }
17745
+ return [[...matched, next], ops];
17746
+ }
17747
+ const ctor = dom[0];
17748
+ if (next === undefined) {
17749
+ //
17750
+ // An expected argument is missing. Is that OK?
17751
+ //
17752
+ let valid = false;
17753
+ if (ctor === 'Union') {
17754
+ // If an `Union`, was `Nothing` an option?
17755
+ for (let k = 1; k <= dom.length - 1; k++) {
17756
+ if (dom[k] === 'Nothing') {
17757
+ valid = true;
17758
+ break;
17759
+ }
17760
+ }
17761
+ }
17762
+ else if (ctor === 'Maybe')
17763
+ valid = true;
17764
+ if (valid)
17765
+ return [[...matched, ce.symbol('Nothing')], ops];
17766
+ return [[...matched, ce.error(['missing', dom])], ops];
17767
+ }
17768
+ if (ctor === 'Union') {
17769
+ //
17770
+ // We expect one of several domains. Check if at least one matches
17771
+ //
17772
+ let found = false;
17773
+ for (let k = 1; k <= dom.length - 1; k++) {
17774
+ if (next.domain.isCompatible(dom[k])) {
17775
+ found = true;
17776
+ break;
17777
+ }
17778
+ }
17779
+ if (found)
17780
+ return [[...matched, next], ops];
17781
+ return [
17782
+ [...matched, ce.error(['incompatible-domain', dom, next.domain], next)],
17783
+ ops,
17784
+ ];
17785
+ }
17786
+ if (ctor === 'Sequence') {
17787
+ const seq = dom[1];
17788
+ if (!next || !next.domain.isCompatible(seq)) {
17789
+ return [
17790
+ [...matched, ce.error(['incompatible-domain', seq, next.domain], next)],
17791
+ ops,
17792
+ ];
17793
+ }
17794
+ let done = false;
17795
+ const result = [...matched, next];
17796
+ while (!done) {
17797
+ next = ops.shift();
17798
+ if (!next)
17799
+ done = false;
17800
+ else if (!next.domain.isCompatible(seq)) {
17801
+ ops.unshift(next);
17802
+ done = false;
17803
+ }
17804
+ else
17805
+ result.push(next);
17806
+ }
17807
+ return [result, ops];
17808
+ }
17809
+ if (ctor === 'Maybe') {
17810
+ if (next === undefined || next.symbol === 'Nothing')
17811
+ return [[...matched, ce.symbol('Nothing')], ops];
17812
+ return validateNextArgument(ce, dom[1], matched, [next, ...ops]);
17813
+ }
17814
+ console.error('Unhandled ctor', ctor);
17815
+ return [[...matched, next], ops];
17816
+ }
17817
+ function validateArguments(ce, args, doms) {
17818
+ // Do a quick check for the common case where everything is as expected.
17819
+ // Avoid allocating arrays and objects
17820
+ if (args.length === doms.length &&
17821
+ args.every((x, i) => x.domain.isCompatible(doms[i])))
17822
+ return args;
17823
+ const xs = [];
17824
+ for (let i = 0; i <= doms.length - 1; i++)
17825
+ xs.push(validateArgument(ce, args[i], doms[i]));
17826
+ for (let i = doms.length; i <= args.length - 1; i++)
17827
+ xs.push(ce.error('unexpected-argument', args[i]));
17828
+ return xs;
17829
+ }
17830
+
17821
17831
  /**
17822
17832
  * Considering an old (existing) expression and a new (simplified) one,
17823
17833
  * 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).
17834
+ * actually be a bit more expensive than the old one, and still be picked).
17825
17835
  */
17826
17836
  function cheapest(oldExpr, newExpr) {
17827
17837
  if (newExpr === null || newExpr === undefined)
@@ -17831,9 +17841,14 @@ function cheapest(oldExpr, newExpr) {
17831
17841
  const ce = oldExpr.engine;
17832
17842
  const boxedNewExpr = ce.box(newExpr);
17833
17843
  if (ce.costFunction(boxedNewExpr) <= 1.7 * ce.costFunction(oldExpr)) {
17844
+ // console.log(
17845
+ // 'Picked new' + boxedNewExpr.toString() + ' over ' + oldExpr.toString()
17846
+ // );
17834
17847
  return boxedNewExpr;
17835
17848
  }
17836
- console.log('Cheapest: Rejected ', boxedNewExpr.toString(), 'in favor of ', oldExpr.toString()); // @debug
17849
+ // console.log(
17850
+ // 'Picked old ' + oldExpr.toString() + ' over ' + newExpr.toString()
17851
+ // );
17837
17852
  return oldExpr;
17838
17853
  }
17839
17854
  /**
@@ -18340,10 +18355,16 @@ class BoxedFunction extends AbstractBoxedExpression {
18340
18355
  //
18341
18356
  if (!this.isValid)
18342
18357
  return this;
18343
- if (!this.isCanonical)
18344
- return this.canonical.simplify(options);
18358
+ if (!this.isCanonical) {
18359
+ const canonical = this.canonical;
18360
+ if (!canonical.isCanonical || !canonical.isValid)
18361
+ return this;
18362
+ return canonical.simplify(options);
18363
+ }
18345
18364
  //
18346
18365
  // 2/ Simplify the applicable operands
18366
+ // @todo not clear if this is always the best strategy. Might be better to
18367
+ // defer to the handler.
18347
18368
  //
18348
18369
  const def = this.functionDefinition;
18349
18370
  const tail = holdMap(this._ops, def?.hold ?? 'none', def?.associative ? def.name : '', (x) => x.simplify(options));
@@ -18371,6 +18392,9 @@ class BoxedFunction extends AbstractBoxedExpression {
18371
18392
  }
18372
18393
  if (!expr)
18373
18394
  expr = this.engine.fn(this._head, tail);
18395
+ else
18396
+ expr = cheapest(this.engine.fn(this._head, tail), expr);
18397
+ expr = cheapest(this, expr);
18374
18398
  //
18375
18399
  // 5/ Apply rules, until no rules can be applied
18376
18400
  //
@@ -18405,7 +18429,7 @@ class BoxedFunction extends AbstractBoxedExpression {
18405
18429
  // console.error('Iteration Limit reached simplifying', this.toJSON());
18406
18430
  // }
18407
18431
  // @debug-end
18408
- return expr;
18432
+ return cheapest(this, expr);
18409
18433
  }
18410
18434
  evaluate(options) {
18411
18435
  //
@@ -18413,8 +18437,12 @@ class BoxedFunction extends AbstractBoxedExpression {
18413
18437
  //
18414
18438
  if (!this.isValid)
18415
18439
  return this;
18416
- if (!this.isCanonical)
18417
- return this.canonical.evaluate(options);
18440
+ if (!this.isCanonical) {
18441
+ const canonical = this.canonical;
18442
+ if (!canonical.isCanonical || !canonical.isValid)
18443
+ return this;
18444
+ return canonical.evaluate(options);
18445
+ }
18418
18446
  //
18419
18447
  // 2/ Evaluate the applicable operands
18420
18448
  //
@@ -18460,8 +18488,12 @@ class BoxedFunction extends AbstractBoxedExpression {
18460
18488
  return this._numericValue;
18461
18489
  if (this.engine.strict && !this.isValid)
18462
18490
  return this;
18463
- if (!this.isCanonical)
18464
- return this.canonical.N(options);
18491
+ if (!this.isCanonical) {
18492
+ const canonical = this.canonical;
18493
+ if (!canonical.isCanonical || !canonical.isValid)
18494
+ return this;
18495
+ return canonical.N(options);
18496
+ }
18465
18497
  //
18466
18498
  // 2/ Evaluate the applicable operands
18467
18499
  //
@@ -18510,11 +18542,11 @@ class BoxedFunction extends AbstractBoxedExpression {
18510
18542
  function makeNumericFunction(ce, head, semiOps, metadata) {
18511
18543
  let ops = [];
18512
18544
  if (head === 'Add' || head === 'Multiply')
18513
- ops = validateNumericArgs(ce, semiOps);
18545
+ ops = validateNumericArgs(ce, flattenOps(flattenSequence(ce.canonical(semiOps)), head));
18514
18546
  else if (head === 'Negate' || head === 'Square' || head === 'Sqrt')
18515
- ops = validateNumericArgs(ce, semiOps, 1);
18547
+ ops = validateNumericArgs(ce, flattenSequence(ce.canonical(semiOps)), 1);
18516
18548
  else if (head === 'Divide' || head === 'Power')
18517
- ops = validateNumericArgs(ce, semiOps, 2);
18549
+ ops = validateNumericArgs(ce, flattenSequence(ce.canonical(semiOps)), 2);
18518
18550
  else
18519
18551
  return null;
18520
18552
  // If some of the arguments are not valid, make a non-canonical expression
@@ -18527,25 +18559,23 @@ function makeNumericFunction(ce, head, semiOps, metadata) {
18527
18559
  if (head === 'Add')
18528
18560
  return ce.add(ops, metadata);
18529
18561
  if (head === 'Negate')
18530
- return ce.negate(ops[0] ?? ce.error('missing'), metadata);
18562
+ return ce.neg(ops[0] ?? ce.error('missing'), metadata);
18531
18563
  if (head === 'Multiply')
18532
18564
  return ce.mul(ops, metadata);
18533
18565
  if (head === 'Divide')
18534
- return ce.divide(ops[0], ops[1], metadata);
18566
+ return ce.div(ops[0], ops[1], metadata);
18535
18567
  if (head === 'Power')
18536
- return ce.power(ops[0], ops[1], metadata);
18568
+ return ce.pow(ops[0], ops[1], metadata);
18537
18569
  if (head === 'Square')
18538
- return ce.power(ops[0], ce.number(2), metadata);
18570
+ return ce.pow(ops[0], ce.number(2), metadata);
18539
18571
  if (head === 'Sqrt') {
18540
18572
  const op = ops[0].canonical;
18541
18573
  if (isRational(op.numericValue))
18542
18574
  return new BoxedFunction(ce, 'Sqrt', [op], { metadata, canonical: true });
18543
- return ce.power(op, ce.number([1, 2]), metadata);
18575
+ return ce.pow(op, ce._HALF, metadata);
18544
18576
  }
18545
- if (head === 'Pair')
18546
- return ce.pair(ops[0], ops[1], metadata);
18547
- if (head === 'Tuple')
18548
- return ce.tuple(ops, metadata);
18577
+ // if (head === 'Pair') return ce.pair(ops[0], ops[1], metadata);
18578
+ // if (head === 'Tuple') return ce.tuple(ops, metadata);
18549
18579
  return null;
18550
18580
  }
18551
18581
  function makeCanonicalFunction(ce, head, ops, metadata) {
@@ -18592,9 +18622,14 @@ function makeCanonicalFunction(ce, head, ops, metadata) {
18592
18622
  // the expression.
18593
18623
  //
18594
18624
  if (sig.canonical) {
18595
- const result = sig.canonical(ce, xs);
18596
- if (result)
18597
- return result;
18625
+ try {
18626
+ const result = sig.canonical(ce, xs);
18627
+ if (result)
18628
+ return result;
18629
+ }
18630
+ catch (e) {
18631
+ console.error(e);
18632
+ }
18598
18633
  return new BoxedFunction(ce, head, xs, { metadata, canonical: false });
18599
18634
  }
18600
18635
  //
@@ -18603,7 +18638,7 @@ function makeCanonicalFunction(ce, head, ops, metadata) {
18603
18638
  //
18604
18639
  xs = flattenSequence(xs);
18605
18640
  if (def.associative)
18606
- xs = flattenOps(xs, head) ?? xs;
18641
+ xs = flattenOps(xs, head);
18607
18642
  // If some of the arguments are not valid, can't make a canonical expression
18608
18643
  if (!xs.every((x) => x.isValid))
18609
18644
  return new BoxedFunction(ce, head, xs, { metadata, canonical: false });
@@ -18667,12 +18702,31 @@ function holdMap(xs, skip, associativeHead, f) {
18667
18702
  if (xs.length === 0)
18668
18703
  return [];
18669
18704
  // f(a, f(b, c), d) -> f(a, b, c, d)
18670
- if (associativeHead)
18671
- xs = flattenOps(xs, associativeHead) ?? xs;
18672
- const result = [];
18705
+ xs = flattenOps(xs, associativeHead);
18673
18706
  //
18674
18707
  // Apply the hold as necessary
18675
18708
  //
18709
+ // @fastpath
18710
+ if (skip === 'all')
18711
+ return xs;
18712
+ if (skip === 'none') {
18713
+ const result = [];
18714
+ for (const x of xs) {
18715
+ const h = x.head;
18716
+ if (h === 'Hold')
18717
+ result.push(x);
18718
+ else {
18719
+ const op = h === 'ReleaseHold' ? x.op1 : x;
18720
+ if (op) {
18721
+ const y = f(op);
18722
+ if (y !== null)
18723
+ result.push(y);
18724
+ }
18725
+ }
18726
+ }
18727
+ return flattenOps(result, associativeHead);
18728
+ }
18729
+ const result = [];
18676
18730
  for (let i = 0; i < xs.length; i++) {
18677
18731
  if (xs[i].head === 'Hold') {
18678
18732
  result.push(xs[i]);
@@ -18692,9 +18746,7 @@ function holdMap(xs, skip, associativeHead, f) {
18692
18746
  }
18693
18747
  }
18694
18748
  }
18695
- if (associativeHead)
18696
- return flattenOps(result, associativeHead) ?? result;
18697
- return result;
18749
+ return flattenOps(result, associativeHead);
18698
18750
  }
18699
18751
  function applicable(skip, count, index) {
18700
18752
  if (skip === 'all')
@@ -18781,7 +18833,7 @@ function inferNumericDomain(value) {
18781
18833
  //
18782
18834
  // 4. Is it a rational? (machine or bignum)
18783
18835
  //
18784
- if (Array.isArray(value)) {
18836
+ if (isRational(value)) {
18785
18837
  const [numer, denom] = value;
18786
18838
  // The value is a rational number
18787
18839
  console.assert(typeof numer !== 'number' ||
@@ -18905,8 +18957,8 @@ class BoxedNumber extends AbstractBoxedExpression {
18905
18957
  const [n, d] = value;
18906
18958
  console.assert(typeof n !== 'number' ||
18907
18959
  (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)));
18960
+ console.assert(!(typeof n === 'bigint' && typeof d == 'bigint') ||
18961
+ (d !== n && d !== 1n));
18910
18962
  if (options?.canonical ?? true) {
18911
18963
  this._value = canonicalNumber(ce, value);
18912
18964
  this._isCanonical = true;
@@ -19304,7 +19356,7 @@ class BoxedNumber extends AbstractBoxedExpression {
19304
19356
  typeof denom === 'number' &&
19305
19357
  !bignumPreferred(ce))
19306
19358
  return ce.number(numer / denom);
19307
- return ce.number(ce.bignum(numer).div(denom));
19359
+ return ce.number(ce.bignum(numer).div(ce.bignum(denom)));
19308
19360
  }
19309
19361
  }
19310
19362
  function canonicalNumber(ce, value) {
@@ -19315,31 +19367,30 @@ function canonicalNumber(ce, value) {
19315
19367
  return value.toNumber();
19316
19368
  if (!isRational(value))
19317
19369
  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()];
19370
+ value = reducedRational(value);
19371
+ if (isBigRational(value)) {
19372
+ let [n, d] = value;
19373
+ if (n > Number.MIN_SAFE_INTEGER &&
19374
+ n < Number.MAX_SAFE_INTEGER &&
19375
+ d > Number.MIN_SAFE_INTEGER &&
19376
+ d < Number.MAX_SAFE_INTEGER)
19377
+ value = [Number(n), Number(d)];
19322
19378
  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())
19379
+ if (d < 0)
19380
+ [n, d] = [-n, -d];
19381
+ if (d === 1n)
19382
+ return ce.bignum(n);
19383
+ if (d === 0n) {
19384
+ if (n === 0n)
19332
19385
  return NaN;
19333
- if (n.isNegative())
19334
- return -Infinity;
19335
- return +Infinity;
19386
+ return n < 0 ? -Infinity : +Infinity;
19336
19387
  }
19337
19388
  return [n, d];
19338
19389
  }
19339
19390
  }
19391
+ let [n, d] = value;
19340
19392
  if (Number.isNaN(n) || Number.isNaN(d))
19341
19393
  return NaN;
19342
- [n, d] = reducedRational([n, d]);
19343
19394
  if (d < 0)
19344
19395
  [n, d] = [-n, -d];
19345
19396
  if (d === 1)
@@ -19474,21 +19525,26 @@ function boxNumber(ce, num, options) {
19474
19525
  //
19475
19526
  // Do we have a rational or big rational?
19476
19527
  //
19528
+ if (Array.isArray(num) &&
19529
+ num.length === 2 &&
19530
+ num[0] instanceof Decimal &&
19531
+ num[1] instanceof Decimal) {
19532
+ if (!num[0].isInteger() || !num[1].isInteger())
19533
+ throw new Error('Array argument to `boxNumber()` should be two integers');
19534
+ num = [bigint(num[0].toString()), bigint(num[1].toString())];
19535
+ }
19477
19536
  if (isRational(num)) {
19478
19537
  if (num.length !== 2)
19479
19538
  throw new Error('Array argument to `boxNumber()` should be two integers or two bignums');
19480
19539
  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))
19540
+ if (typeof n === 'bigint' && typeof d === 'bigint') {
19541
+ if (n === d)
19542
+ return d === 0n ? ce._NAN : ce._ONE;
19543
+ if (d === 1n)
19488
19544
  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))
19545
+ if (d === -1n)
19546
+ return ce.number(-n, options);
19547
+ if (n === 1n && d === 2n)
19492
19548
  return ce._HALF;
19493
19549
  return new BoxedNumber(ce, [n, d], options);
19494
19550
  }
@@ -19633,16 +19689,16 @@ function boxFunction(ce, head, ops, options) {
19633
19689
  if ((head === 'Divide' || head === 'Rational') && ops.length === 2) {
19634
19690
  if (ops[0] instanceof AbstractBoxedExpression &&
19635
19691
  ops[1] instanceof AbstractBoxedExpression) {
19636
- const [n, d] = [asBignum(ops[0]), asBignum(ops[1])];
19637
- if (n?.isInteger() && d?.isInteger())
19692
+ const [n, d] = [asBigint(ops[0]), asBigint(ops[1])];
19693
+ if (n && d)
19638
19694
  return ce.number([n, d], options);
19639
19695
  }
19640
19696
  else {
19641
19697
  const [n, d] = [
19642
- bignumValue(ce, ops[0]),
19643
- bignumValue(ce, ops[1]),
19698
+ bigintValue(ce, ops[0]),
19699
+ bigintValue(ce, ops[1]),
19644
19700
  ];
19645
- if (n?.isInteger() && d?.isInteger())
19701
+ if (n && d)
19646
19702
  return ce.number([n, d], options);
19647
19703
  }
19648
19704
  head = 'Divide';
@@ -19777,19 +19833,17 @@ function box(ce, expr, options) {
19777
19833
  // This wasn't a valid rational, turn it into a `Divide`
19778
19834
  return boxFunction(ce, 'Divide', expr, options);
19779
19835
  }
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
- }
19836
+ if (isBigRational(expr))
19837
+ return ce.number(expr);
19786
19838
  if (typeof expr[0] === 'string')
19787
19839
  return boxFunction(ce, expr[0], expr.slice(1), options);
19788
19840
  // It's a function with a head expression
19789
19841
  // Try to evaluate to something simpler
19790
19842
  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;
19843
+ const head = box(ce, expr[0], options);
19844
+ if (head.symbol)
19845
+ return new BoxedFunction(ce, head.symbol, ops);
19846
+ return apply$1(head, ops);
19793
19847
  }
19794
19848
  //
19795
19849
  // Box a number (other than a rational)
@@ -19867,20 +19921,21 @@ function lngamma(c) {
19867
19921
 
19868
19922
  class Sum {
19869
19923
  constructor(ce, xs, options) {
19924
+ // If `false`, the running sums are not calculated
19925
+ this._isCanonical = true;
19870
19926
  this._imaginary = 0; // integers only
19871
19927
  this._posInfinityCount = 0;
19872
19928
  this._negInfinityCount = 0;
19929
+ this._naNCount = 0;
19930
+ // Each term is factored as the product of a rational and an expression
19931
+ // For now, only rationals are factored, so `1.2x + 2.5x` are not combined.
19873
19932
  this._terms = [];
19874
- // If `false`, the running sums are not calculated
19875
- this._isCanonical = true;
19876
19933
  options ?? (options = {});
19877
19934
  if (!('canonical' in options))
19878
19935
  options.canonical = true;
19879
19936
  this._isCanonical = options.canonical;
19880
19937
  this.engine = ce;
19881
- this._rational = bignumPreferred(ce)
19882
- ? [ce._BIGNUM_ZERO, ce._BIGNUM_ONE]
19883
- : [0, 1];
19938
+ this._rational = bignumPreferred(ce) ? [0n, 1n] : [0, 1];
19884
19939
  this._bignum = ce._BIGNUM_ZERO;
19885
19940
  this._number = 0;
19886
19941
  if (xs)
@@ -19896,7 +19951,8 @@ class Sum {
19896
19951
  this._number === 0 &&
19897
19952
  this._bignum.isZero() &&
19898
19953
  this._negInfinityCount === 0 &&
19899
- this._posInfinityCount === 0);
19954
+ this._posInfinityCount === 0 &&
19955
+ this._naNCount === 0);
19900
19956
  }
19901
19957
  /**
19902
19958
  * Add a term to the sum.
@@ -19911,9 +19967,13 @@ class Sum {
19911
19967
  * -> [['x', [3, 1]], ['y', [1, 5]]]
19912
19968
  */
19913
19969
  addTerm(term, c) {
19970
+ if (term.isNothing)
19971
+ return;
19972
+ if (term.isNaN || (term.isImaginary && !complexAllowed(this.engine))) {
19973
+ this._naNCount += 1;
19974
+ return;
19975
+ }
19914
19976
  if (this._isCanonical) {
19915
- if (term.isNothing)
19916
- return;
19917
19977
  if (term.numericValue !== null) {
19918
19978
  if (term.isInfinity) {
19919
19979
  if (term.isPositive)
@@ -19961,7 +20021,11 @@ class Sum {
19961
20021
  else if (isMachineRational(c))
19962
20022
  this._imaginary += (im * c[0]) / c[1];
19963
20023
  else
19964
- this._imaginary += c[0].mul(im).div(c[1]).toNumber();
20024
+ this._imaginary += this.engine
20025
+ .bignum(c[0])
20026
+ .mul(im)
20027
+ .div(this.engine.bignum(c[1]))
20028
+ .toNumber();
19965
20029
  im = 0;
19966
20030
  }
19967
20031
  if (re === 0 && im === 0)
@@ -20017,25 +20081,27 @@ class Sum {
20017
20081
  }
20018
20082
  terms(mode) {
20019
20083
  const ce = this.engine;
20084
+ if (this._naNCount > 0)
20085
+ return [ce._NAN];
20086
+ if (this._imaginary !== 0 && !complexAllowed(ce))
20087
+ return [ce._NAN];
20020
20088
  if (this._posInfinityCount > 0 && this._negInfinityCount > 0)
20021
20089
  return [ce._NAN];
20022
20090
  if (this._posInfinityCount > 0)
20023
20091
  return [ce._POSITIVE_INFINITY];
20024
20092
  if (this._negInfinityCount > 0)
20025
20093
  return [ce._NEGATIVE_INFINITY];
20026
- if (this._imaginary !== 0 && !complexAllowed(ce))
20027
- return [ce._NAN];
20028
20094
  const xs = [];
20029
20095
  for (const { coef, term } of this._terms) {
20030
20096
  if (!isRationalZero(coef)) {
20031
20097
  if (isRationalOne(coef))
20032
20098
  xs.push(term);
20033
20099
  else if (isRationalNegativeOne(coef))
20034
- xs.push(ce.negate(term));
20100
+ xs.push(ce.neg(term));
20035
20101
  else if (machineDenominator(coef) === 1)
20036
20102
  xs.push(ce.mul([ce.number(coef[0]), term]));
20037
20103
  else if (machineNumerator(coef) === 1)
20038
- xs.push(ce.divide(term, ce.number(coef[1])));
20104
+ xs.push(ce.div(term, ce.number(coef[1])));
20039
20105
  else
20040
20106
  xs.push(ce.mul([ce.number(coef), term]));
20041
20107
  }
@@ -20044,7 +20110,7 @@ class Sum {
20044
20110
  if (bignumPreferred(this.engine)) {
20045
20111
  let sum = this._bignum.add(this._number);
20046
20112
  if (!isRationalZero(this._rational))
20047
- sum = sum.add(ce.bignum(this._rational[0]).div(this._rational[1]));
20113
+ sum = sum.add(ce.bignum(this._rational[0]).div(ce.bignum(this._rational[1])));
20048
20114
  if (this._imaginary !== 0)
20049
20115
  xs.push(ce.number(ce.complex(sum.toNumber(), this._imaginary)));
20050
20116
  else if (ce.chop(sum) !== 0)
@@ -20082,7 +20148,7 @@ class Sum {
20082
20148
  xs.push(ce.number(this._number));
20083
20149
  }
20084
20150
  }
20085
- return flattenOps(xs, 'Add') ?? xs;
20151
+ return flattenOps(xs, 'Add');
20086
20152
  }
20087
20153
  asExpression(mode) {
20088
20154
  const ce = this.engine;
@@ -21015,7 +21081,6 @@ function isEqual(lhs, rhs) {
21015
21081
  * */
21016
21082
  function canonicalAdd(ce, ops) {
21017
21083
  console.assert(ops.every((x) => x.isCanonical));
21018
- ops = flattenOps(flattenSequence(ops.map((x) => x.canonical)), 'Add') ?? ops;
21019
21084
  // Remove literal 0
21020
21085
  ops = ops.filter((x) => x.numericValue === null || !x.isZero);
21021
21086
  if (ops.length === 0)
@@ -21100,7 +21165,6 @@ function evalAdd(ce, ops, mode = 'evaluate') {
21100
21165
  if (!arg.isExact)
21101
21166
  mode = 'N';
21102
21167
  }
21103
- console.assert(flattenOps(ops, 'Add') === null);
21104
21168
  if (mode === 'N')
21105
21169
  ops = ops.map((x) => x.N());
21106
21170
  else
@@ -21163,7 +21227,7 @@ function evalSummation(ce, expr, range, mode) {
21163
21227
  lower = asSmallInteger(range.op2) ?? 1;
21164
21228
  upper = asSmallInteger(range.op3) ?? MAX_ITERATION;
21165
21229
  }
21166
- if (lower >= upper || upper - lower >= MAX_SYMBOLIC_TERMS)
21230
+ if (mode !== 'N' && (lower >= upper || upper - lower >= MAX_SYMBOLIC_TERMS))
21167
21231
  return undefined;
21168
21232
  const savedContext = ce.context;
21169
21233
  ce.context = fn.scope ?? ce.context;
@@ -21193,9 +21257,7 @@ function evalSummation(ce, expr, range, mode) {
21193
21257
  ce.context = savedContext;
21194
21258
  return ce.add(terms).evaluate();
21195
21259
  }
21196
- let sum = bignumPreferred(ce)
21197
- ? [ce._BIGNUM_ZERO, ce._BIGNUM_ONE]
21198
- : [0, 1];
21260
+ let sum = bignumPreferred(ce) ? [1n, 1n] : [0, 1];
21199
21261
  if (!fn.scope)
21200
21262
  for (let i = lower; i <= upper; i++) {
21201
21263
  const term = fn.N();
@@ -21216,7 +21278,7 @@ function evalSummation(ce, expr, range, mode) {
21216
21278
  ce.context = savedContext;
21217
21279
  if (isMachineRational(sum))
21218
21280
  return ce.number(sum[0] / sum[1]);
21219
- return ce.number(sum[0].div(sum[1]));
21281
+ return ce.number(ce.bignum(sum[0]).div(ce.bignum(sum[1])));
21220
21282
  }
21221
21283
 
21222
21284
  // @todo: replace usage with asCoefficient():
@@ -21237,34 +21299,35 @@ function makePositive(expr) {
21237
21299
  return [-1, ce.number(ce.complex(-n.re, -n.im))];
21238
21300
  if (isMachineRational(n) && n[0] < 0)
21239
21301
  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]])];
21302
+ if (isBigRational(n) && n[0] < 0)
21303
+ return [-1, ce.number([-n[0], n[1]])];
21242
21304
  return [1, expr];
21243
21305
  }
21244
21306
  function apply(expr, fn, bigFn, complexFn) {
21245
21307
  const n = expr.numericValue;
21308
+ const ce = expr.engine;
21246
21309
  console.assert(n !== null);
21247
21310
  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));
21311
+ if (bignumPreferred(ce) && bigFn)
21312
+ return ce.chop(bigFn(ce.bignum(n)));
21313
+ return ce.chop(fn(n));
21251
21314
  }
21252
21315
  if (n instanceof Decimal)
21253
- return expr.engine.chop(bigFn?.(n) ?? fn(n.toNumber()));
21316
+ return ce.chop(bigFn?.(n) ?? fn(n.toNumber()));
21254
21317
  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])));
21318
+ if (!bignumPreferred(ce) || !bigFn)
21319
+ return ce.chop(fn(n[0] / n[1]));
21320
+ return ce.chop(bigFn(ce.bignum(n[0]).div(n[1])));
21258
21321
  }
21259
21322
  if (isBigRational(n)) {
21260
21323
  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()));
21324
+ return ce.chop(bigFn(ce.bignum(n[0]).div(ce.bignum(n[1]))));
21325
+ return ce.chop(fn(Number(n[0]) / Number(n[1])));
21263
21326
  }
21264
21327
  if (n instanceof Complex) {
21265
- if (!complexFn || !complexAllowed(expr.engine))
21328
+ if (!complexFn || !complexAllowed(ce))
21266
21329
  return NaN;
21267
- return expr.engine.chop(complexFn(n));
21330
+ return ce.chop(complexFn(n));
21268
21331
  }
21269
21332
  debugger;
21270
21333
  return NaN;
@@ -21289,14 +21352,14 @@ function apply2(expr1, expr2, fn, bigFn, complexFn) {
21289
21352
  if (m1 instanceof Decimal)
21290
21353
  b1 = m1;
21291
21354
  else if (isBigRational(m1))
21292
- b1 = m1[0].div(m1[1]);
21355
+ b1 = ce.bignum(m1[0]).div(ce.bignum(m1[1]));
21293
21356
  else if (m1 !== null && typeof m1 === 'number')
21294
21357
  b1 = ce.bignum(m1);
21295
21358
  let b2 = undefined;
21296
21359
  if (m2 instanceof Decimal)
21297
21360
  b2 = m2;
21298
21361
  else if (isBigRational(m2))
21299
- b2 = m2[0].div(m2[1]);
21362
+ b1 = ce.bignum(m2[0]).div(ce.bignum(m2[1]));
21300
21363
  else if (m2 !== null && typeof m2 === 'number')
21301
21364
  b2 = ce.bignum(m2);
21302
21365
  if (b1 && b2)
@@ -21317,17 +21380,17 @@ function apply2N(expr1, expr2, fn, bigFn, complexFn) {
21317
21380
 
21318
21381
  /**
21319
21382
  *
21320
- * Return `null` if there is no canonicalization necessary and the result is
21321
- * simply `ce._fn('Power', [base, exponent])`
21322
21383
  */
21323
21384
  function canonicalPower(ce, base, exponent, metadata) {
21324
- base = validateArgument(ce, base?.canonical, 'Number');
21325
- exponent = validateArgument(ce, exponent?.canonical, 'Number');
21326
21385
  if (exponent.symbol === 'ComplexInfinity')
21327
21386
  return ce._NAN;
21387
+ if (exponent.isZero)
21388
+ return ce._ONE;
21389
+ if (exponent.isOne)
21390
+ return base;
21391
+ if (exponent.isNegativeOne)
21392
+ return ce.inv(base);
21328
21393
  if (exponent.numericValue !== null) {
21329
- if (exponent.isZero)
21330
- return ce._ONE;
21331
21394
  if (base.numericValue !== null) {
21332
21395
  const numBase = asFloat(base);
21333
21396
  //
@@ -21346,27 +21409,9 @@ function canonicalPower(ce, base, exponent, metadata) {
21346
21409
  if (exponent.isNegative)
21347
21410
  return ce._COMPLEX_INFINITY; // Unsigned Infinity...
21348
21411
  }
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
- }
21412
+ // x^(-1)
21413
+ if (exponent.isNegativeOne)
21414
+ return ce.inv(base);
21370
21415
  // x^{0.5}, x^{1/2} -> Square Root
21371
21416
  const e = asFloat(exponent);
21372
21417
  if (e === 0.5 || e === -0.5) {
@@ -21382,9 +21427,12 @@ function canonicalPower(ce, base, exponent, metadata) {
21382
21427
  return ce.number(e >= 0 ? coef : [1, coef]);
21383
21428
  return ce.mul([
21384
21429
  ce.number(coef),
21385
- ce.power(ce.number(radicand), ce._HALF),
21430
+ ce._fn('Sqrt', [ce.number(radicand)]),
21386
21431
  ]);
21387
21432
  }
21433
+ if (e > 0)
21434
+ return ce._fn('Sqrt', [base], metadata);
21435
+ return ce.inv(ce._fn('Sqrt', [base]), metadata);
21388
21436
  }
21389
21437
  if (e > 0)
21390
21438
  return ce._fn('Power', [base, ce._HALF], metadata);
@@ -21415,17 +21463,6 @@ function canonicalPower(ce, base, exponent, metadata) {
21415
21463
  }
21416
21464
  if (exponent.isInfinity && (base.isOne || base.isNegativeOne))
21417
21465
  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
21466
  }
21430
21467
  }
21431
21468
  //
@@ -21436,7 +21473,7 @@ function canonicalPower(ce, base, exponent, metadata) {
21436
21473
  if (a !== null) {
21437
21474
  const b = asSmallInteger(base.op2);
21438
21475
  if (b !== null) {
21439
- return ce.power(base.op1, ce.number(a * b));
21476
+ return ce.pow(base.op1, ce.number(a * b));
21440
21477
  }
21441
21478
  }
21442
21479
  if (base.op1.isNonNegative) {
@@ -21444,7 +21481,7 @@ function canonicalPower(ce, base, exponent, metadata) {
21444
21481
  if (ar) {
21445
21482
  const br = asRational(base.op2);
21446
21483
  if (br)
21447
- return ce.power(base.op1, ce.number(mul(ar, br)));
21484
+ return ce.pow(base.op1, ce.number(mul(ar, br)));
21448
21485
  }
21449
21486
  }
21450
21487
  }
@@ -21453,9 +21490,9 @@ function canonicalPower(ce, base, exponent, metadata) {
21453
21490
  if (base.head === 'Multiply') {
21454
21491
  const e = asSmallInteger(exponent);
21455
21492
  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
21493
+ return ce._fn('Multiply', base.ops.map((x) => ce.pow(x, exponent))); // Don't call ce.mul() to avoid infinite loops
21457
21494
  }
21458
- return null;
21495
+ return ce._fn('Power', [base, exponent], metadata);
21459
21496
  }
21460
21497
  function square(ce, base) {
21461
21498
  const num = base.numericValue;
@@ -21468,16 +21505,16 @@ function square(ce, base) {
21468
21505
  if (isMachineRational(num))
21469
21506
  return ce.number([num[1] * num[1], num[0] * num[0]]);
21470
21507
  if (isBigRational(num))
21471
- return ce.number([num[1].pow(2), num[0].pow(2)]);
21508
+ return ce.number([num[1] * num[1], num[0] * num[0]]);
21472
21509
  if (base.head === 'Multiply')
21473
21510
  return ce._fn('Multiply', base.ops.map((x) => square(ce, x))); // Don't call ce.mul() to avoid infinite loops
21474
21511
  if (base.head === 'Power') {
21475
21512
  const exp = asSmallInteger(base.op2);
21476
21513
  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]));
21514
+ return ce.pow(base.op1, ce.number(exp * 2));
21515
+ return ce.pow(base.op1, ce.mul([ce.number(2), base.op2]));
21479
21516
  }
21480
- return ce.power(base, ce.number(2));
21517
+ return ce.pow(base, ce.number(2));
21481
21518
  }
21482
21519
  function numEvalPower(ce, base, exponent) {
21483
21520
  if (base.numericValue === null || exponent.numericValue === null)
@@ -21510,7 +21547,7 @@ function numEvalPower(ce, base, exponent) {
21510
21547
  if (invExp === 2) {
21511
21548
  if (bigBase.isNeg())
21512
21549
  return complexAllowed(ce)
21513
- ? ce.mul([ce._I, ce.number(bigBase.neg().sqrt())])
21550
+ ? ce.number(ce.complex(0, bigBase.neg().sqrt().toNumber()))
21514
21551
  : ce._NAN;
21515
21552
  return ce.number(bigBase.sqrt());
21516
21553
  }
@@ -21548,9 +21585,7 @@ function numEvalPower(ce, base, exponent) {
21548
21585
  }
21549
21586
  function processPower(ce, base, exponent, mode) {
21550
21587
  if (base.head === 'Multiply') {
21551
- let c = bignumPreferred(ce)
21552
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
21553
- : [1, 1];
21588
+ let c = bignumPreferred(ce) ? [1n, 1n] : [1, 1];
21554
21589
  const xs = [];
21555
21590
  for (const op of base.ops) {
21556
21591
  const r = asRational(op);
@@ -21562,7 +21597,7 @@ function processPower(ce, base, exponent, mode) {
21562
21597
  if (!isRationalOne(c))
21563
21598
  return ce.mul([
21564
21599
  processSqrt(ce, ce.number(c), mode) ?? ce._ONE,
21565
- ce.power(processPower(ce, ce.mul(xs), exponent, mode) ?? ce.mul(xs), exponent),
21600
+ ce.pow(processPower(ce, ce.mul(xs), exponent, mode) ?? ce.mul(xs), exponent),
21566
21601
  ]);
21567
21602
  }
21568
21603
  if (base.head === 'Power') {
@@ -21577,7 +21612,7 @@ function processPower(ce, base, exponent, mode) {
21577
21612
  return ce._ONE;
21578
21613
  if (isRationalOne(e))
21579
21614
  return base.op1;
21580
- return ce.power(base.op1, e);
21615
+ return ce.pow(base.op1, e);
21581
21616
  }
21582
21617
  if (mode === 'N') {
21583
21618
  const ef1 = asFloat(base.op2);
@@ -21588,7 +21623,7 @@ function processPower(ce, base, exponent, mode) {
21588
21623
  return ce._ONE;
21589
21624
  if (ef === 1)
21590
21625
  return base.op1;
21591
- return ce.power(base.op1, ef);
21626
+ return ce.pow(base.op1, ef);
21592
21627
  }
21593
21628
  }
21594
21629
  }
@@ -21605,28 +21640,21 @@ function processPower(ce, base, exponent, mode) {
21605
21640
  const [n, d] = [machineNumerator(r), machineDenominator(r)];
21606
21641
  if ((n === 1 || n === -1) && (d === 2 || d === 3)) {
21607
21642
  if (bignumPreferred(ce) || base.numericValue instanceof Decimal) {
21608
- const bigBase = asBignum(base);
21609
- if (d % 2 === 0 && bigBase.isNeg() && !complexAllowed(ce))
21643
+ const bigBase = asBigint(base);
21644
+ if (d % 2 === 0 && bigBase < 0 && !complexAllowed(ce))
21610
21645
  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))
21646
+ const sign = bigBase < 0 ? (d % 2 === 0 ? ce._I : ce._NEGATIVE_ONE) : ce._ONE;
21647
+ const [factor, root] = factorPower(bigBase > 0 ? bigBase : -bigBase, d);
21648
+ if (root === 1n && factor === 1n)
21618
21649
  return sign;
21619
21650
  // If factor === 1, nothing special to do, fall through
21620
- if (!factor.eq(1)) {
21621
- if (root.eq(1))
21622
- return ce.mul([
21623
- sign,
21624
- ce.number(n >= 0 ? factor : [ce.bignum(1), factor]),
21625
- ]);
21651
+ if (factor !== 1n) {
21652
+ if (root === 1n)
21653
+ return ce.mul([sign, ce.number(n >= 0 ? factor : [1n, factor])]);
21626
21654
  return ce.mul([
21627
21655
  sign,
21628
21656
  ce.number(factor),
21629
- ce.power(ce.number(root), exponent),
21657
+ ce.pow(ce.number(root), exponent),
21630
21658
  ]);
21631
21659
  }
21632
21660
  }
@@ -21648,7 +21676,7 @@ function processPower(ce, base, exponent, mode) {
21648
21676
  return ce.mul([
21649
21677
  sign,
21650
21678
  ce.number(factor),
21651
- ce.power(ce.number(root), exponent),
21679
+ ce.pow(ce.number(root), exponent),
21652
21680
  ]);
21653
21681
  }
21654
21682
  }
@@ -21657,7 +21685,7 @@ function processPower(ce, base, exponent, mode) {
21657
21685
  if (base.isNegative) {
21658
21686
  if (!complexAllowed)
21659
21687
  return ce._NAN;
21660
- return ce.mul([ce._I, ce.fn('Sqrt', [ce.negate(base)])]);
21688
+ return ce.mul([ce._I, ce.fn('Sqrt', [ce.neg(base)])]);
21661
21689
  }
21662
21690
  return undefined;
21663
21691
  }
@@ -21715,10 +21743,10 @@ function processSqrt(ce, base, mode) {
21715
21743
  }
21716
21744
  }
21717
21745
  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())
21746
+ const n = bigint(r[0]);
21747
+ const [nFactor, nRoot] = factorPower(n > 0 ? n : -n, 2);
21748
+ const [dFactor, dRoot] = factorPower(bigint(r[1]), 2);
21749
+ if (n < 0)
21722
21750
  return ce.mul([
21723
21751
  ce.number([nFactor, dFactor]),
21724
21752
  ce.sqrt(ce.number([nRoot, dRoot])),
@@ -21770,9 +21798,6 @@ function rootExp(exponent) {
21770
21798
  */
21771
21799
  function canonicalMultiply(ce, ops) {
21772
21800
  console.assert(ops.every((x) => x.isCanonical));
21773
- // Apply associativity
21774
- ops =
21775
- flattenOps(flattenSequence(ops).map((x) => x.canonical), 'Multiply') ?? ops;
21776
21801
  if (ops.length === 0)
21777
21802
  return ce.number(1);
21778
21803
  if (ops.length === 1)
@@ -21788,7 +21813,7 @@ function canonicalMultiply(ce, ops) {
21788
21813
  return product.asExpression();
21789
21814
  }
21790
21815
  function simplifyMultiply(ce, ops) {
21791
- console.assert(flattenOps(ops, 'Multiply') === null);
21816
+ console.assert(ops.every((x) => x.head !== 'Multiply'));
21792
21817
  const product = new Product(ce);
21793
21818
  for (let op of ops) {
21794
21819
  op = op.simplify();
@@ -21827,7 +21852,7 @@ function evalMultiply(ce, ops, mode = 'evaluate') {
21827
21852
  if (!op.isExact)
21828
21853
  mode = 'N';
21829
21854
  }
21830
- console.assert(flattenOps(ops, 'Multiply') === null);
21855
+ console.assert(ops.every((x) => x.head !== 'Multiply'));
21831
21856
  if (mode === 'N')
21832
21857
  ops = ops.map((x) => x.N());
21833
21858
  else
@@ -21984,9 +22009,7 @@ function evalMultiplication(ce, expr, range, mode) {
21984
22009
  const product = ce.mul(terms);
21985
22010
  return mode === 'simplify' ? product.simplify() : product.evaluate();
21986
22011
  }
21987
- let product = bignumPreferred(ce)
21988
- ? [ce._BIGNUM_ONE, ce._BIGNUM_ONE]
21989
- : [1, 1];
22012
+ let product = bignumPreferred(ce) ? [1n, 1n] : [1, 1];
21990
22013
  for (let i = lower; i <= upper; i++) {
21991
22014
  const n = ce.number(i);
21992
22015
  const r = fn.subs({ _1: n, _: n });
@@ -21997,7 +22020,7 @@ function evalMultiplication(ce, expr, range, mode) {
21997
22020
  }
21998
22021
  if (isMachineRational(product))
21999
22022
  return ce.number(product[0] / product[1]);
22000
- return ce.number(product[0].div(product[1]));
22023
+ return ce.number(ce.bignum(product[0]).div(ce.bignum(product[1])));
22001
22024
  }
22002
22025
 
22003
22026
  /**
@@ -22010,19 +22033,21 @@ function evalMultiplication(ce, expr, range, mode) {
22010
22033
  * - if Divide, transform into Multiply/Power
22011
22034
  */
22012
22035
  function canonicalDivide(ce, op1, op2) {
22013
- op1 = validateArgument(ce, op1, 'Number');
22014
- op2 = validateArgument(ce, op2, 'Number');
22015
22036
  if (!op1.isValid || !op2.isValid)
22016
22037
  return ce._fn('Divide', [op1, op2]);
22038
+ if (op1.head === 'Negate' && op2.head === 'Negate') {
22039
+ op1 = op1.op1;
22040
+ op2 = op2.op1;
22041
+ }
22017
22042
  if (op1.numericValue !== null && op2.numericValue !== null) {
22018
22043
  if (op2.isOne)
22019
22044
  return op1;
22020
22045
  if (op2.isNegativeOne)
22021
- return canonicalNegate(op1);
22046
+ return ce.neg(op1);
22022
22047
  if (op1.isOne)
22023
- return ce.inverse(op2);
22048
+ return ce.inv(op2);
22024
22049
  if (op1.isNegativeOne)
22025
- return canonicalNegate(ce.inverse(op2));
22050
+ return ce.neg(ce.inv(op2));
22026
22051
  const r1 = asRational(op1);
22027
22052
  const r2 = asRational(op2);
22028
22053
  if (r1 && r2 && !isRationalZero(r2))
@@ -22058,6 +22083,10 @@ function canonicalDivide(ce, op1, op2) {
22058
22083
  return canonicalDivide(ce, ce.mul([op1.op1, op2]), op1.op2);
22059
22084
  if (op2.head === 'Divide' || op2.head === 'Rational')
22060
22085
  return canonicalDivide(ce, ce.mul([op1, op2.op2]), op2.op1);
22086
+ const [c1, t1] = asCoefficient(op1);
22087
+ const [c2, t2] = asCoefficient(op2);
22088
+ if (!isRationalOne(c1) || !isRationalOne(c2))
22089
+ return ce.mul([ce.number(mul(c1, inverse(c2))), ce.div(t1, t2)]);
22061
22090
  // eslint-disable-next-line prefer-const
22062
22091
  let [nSign, n] = makePositive(op1);
22063
22092
  // eslint-disable-next-line prefer-const
@@ -22066,17 +22095,11 @@ function canonicalDivide(ce, op1, op2) {
22066
22095
  d = d.canonical;
22067
22096
  if (d.numericValue !== null && d.isOne)
22068
22097
  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
22098
  if (nSign * dSign > 0)
22078
- return ce.mul([n, d]);
22079
- return canonicalNegate(ce.mul([n, d]));
22099
+ return ce._fn('Divide', [n, d]);
22100
+ if (n.numericValue)
22101
+ return ce._fn('Divide', [canonicalNegate(n), d]);
22102
+ return canonicalNegate(ce._fn('Divide', [n, d]));
22080
22103
  }
22081
22104
  /**
22082
22105
  * Simplify form of 'Divide' (and 'Rational')
@@ -22088,7 +22111,11 @@ function simplifyDivide(ce, op1, op2) {
22088
22111
  if (r1 && r2 && !isRationalZero(r2))
22089
22112
  return ce.number(mul(r1, inverse(r2)));
22090
22113
  }
22091
- return undefined;
22114
+ const [c1, t1] = asCoefficient(op1);
22115
+ const [c2, t2] = asCoefficient(op2);
22116
+ if (!isRationalOne(c1) || !isRationalOne(c2))
22117
+ return ce.mul([ce.number(mul(c1, inverse(c2))), ce.div(t1, t2)]);
22118
+ return new Product(ce, [op1, ce.inv(op2)]).asExpression();
22092
22119
  }
22093
22120
 
22094
22121
  // @todo Future additions to the dictionary
@@ -22146,7 +22173,7 @@ const ARITHMETIC_LIBRARY = [
22146
22173
  signature: {
22147
22174
  domain: 'NumericFunction',
22148
22175
  codomain: (ce, args) => domainAdd(ce, args.map((x) => x.domain)),
22149
- canonical: (ce, args) => canonicalAdd(ce, args),
22176
+ // canonical: (ce, args) => canonicalAdd(ce, args), // never called: shortpath
22150
22177
  simplify: (ce, ops) => simplifyAdd(ce, ops),
22151
22178
  evaluate: (ce, ops) => evalAdd(ce, ops),
22152
22179
  N: (ce, ops) => evalAdd(ce, ops, 'N'),
@@ -22182,8 +22209,17 @@ const ARITHMETIC_LIBRARY = [
22182
22209
  // i.e. √2x/2 -> 0.707x, 2/√2x -> 1.4142x
22183
22210
  signature: {
22184
22211
  domain: ['Function', 'Number', 'Number', 'Number'],
22185
- canonical: (ce, args) => canonicalDivide(ce, args[0], args[1]),
22212
+ canonical: (ce, args) => {
22213
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22214
+ 'Number',
22215
+ 'Number',
22216
+ ]);
22217
+ if (args.length !== 2)
22218
+ return ce._fn('Divide', args);
22219
+ return ce.div(args[0], args[1]);
22220
+ },
22186
22221
  simplify: (ce, args) => simplifyDivide(ce, args[0], args[1]),
22222
+ evaluate: (ce, ops) => apply2N(ops[0], ops[1], (n, d) => n / d, (n, d) => n.div(d), (n, d) => n.div(d)),
22187
22223
  },
22188
22224
  },
22189
22225
  Exp: {
@@ -22193,7 +22229,14 @@ const ARITHMETIC_LIBRARY = [
22193
22229
  // Exp(x) -> e^x
22194
22230
  signature: {
22195
22231
  domain: ['Function', 'Number', 'Number'],
22196
- canonical: (ce, args) => ce.power(ce.symbol('ExponentialE'), validateArgument(ce, args[0], 'Number')),
22232
+ canonical: (ce, args) => {
22233
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22234
+ 'Number',
22235
+ ]);
22236
+ if (args.length !== 1)
22237
+ return ce._fn('Power', args);
22238
+ return ce.pow(ce.symbol('ExponentialE'), args[0]);
22239
+ },
22197
22240
  },
22198
22241
  },
22199
22242
  Erf: {
@@ -22266,21 +22309,15 @@ const ARITHMETIC_LIBRARY = [
22266
22309
  signature: {
22267
22310
  domain: ['Function', 'Number', ['Maybe', 'Number'], 'Number'],
22268
22311
  canonical: (ce, ops) => {
22269
- ops = flattenSequence(ops);
22312
+ ops = canonical(flattenSequence(ops));
22270
22313
  if (ops.length === 1)
22271
- return ce._fn('Log', [
22272
- validateArgument(ce, ops[0].canonical, 'Number'),
22273
- ]);
22314
+ return ce._fn('Log', [validateArgument(ce, ops[0], 'Number')]);
22274
22315
  if (ops.length === 2) {
22275
- const base = validateArgument(ce, ops[1].canonical, 'Number');
22316
+ const arg = validateArgument(ce, ops[0], 'Number');
22317
+ const base = validateArgument(ce, ops[1], 'Number');
22276
22318
  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
- ]);
22319
+ return ce._fn('Log', [arg]);
22320
+ return ce._fn('Log', [arg, base]);
22284
22321
  }
22285
22322
  return ce._fn('Log', validateArgumentCount(ce, ops, 2));
22286
22323
  },
@@ -22382,7 +22419,10 @@ const ARITHMETIC_LIBRARY = [
22382
22419
  hold: 'all',
22383
22420
  signature: {
22384
22421
  domain: 'NumericFunction',
22385
- canonical: (ce, args) => canonicalMultiply(ce, args),
22422
+ // Never called: fastpath
22423
+ // canonical: (ce, args) => {
22424
+ // return canonicalMultiply(ce, args);
22425
+ // },
22386
22426
  simplify: (ce, ops) => simplifyMultiply(ce, ops),
22387
22427
  evaluate: (ce, ops) => evalMultiply(ce, ops),
22388
22428
  N: (ce, ops) => evalMultiply(ce, ops, 'N'),
@@ -22412,7 +22452,14 @@ const ARITHMETIC_LIBRARY = [
22412
22452
  return ce.domain(negDomain);
22413
22453
  return arg;
22414
22454
  },
22415
- canonical: (_ce, args) => canonicalNegate(args[0]),
22455
+ canonical: (ce, args) => {
22456
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22457
+ 'Number',
22458
+ ]);
22459
+ if (args.length !== 1)
22460
+ return ce._fn('Negate', args);
22461
+ return canonicalNegate(args[0]);
22462
+ },
22416
22463
  simplify: (ce, ops) => processNegate(ce, ops[0], 'simplify'),
22417
22464
  evaluate: (ce, ops) => processNegate(ce, ops[0], 'evaluate'),
22418
22465
  N: (ce, ops) => processNegate(ce, ops[0], 'N'),
@@ -22436,7 +22483,15 @@ const ARITHMETIC_LIBRARY = [
22436
22483
  complexity: 3500,
22437
22484
  signature: {
22438
22485
  domain: ['Function', 'Number', 'Number', 'Number'],
22439
- canonical: (ce, args) => canonicalPower(ce, args[0], args[1]) ?? ce._fn('Power', args),
22486
+ canonical: (ce, args) => {
22487
+ args = validateArguments(ce, canonical(flattenSequence(args)), [
22488
+ 'Number',
22489
+ 'Number',
22490
+ ]);
22491
+ if (args.length !== 2)
22492
+ return ce._fn('Power', args);
22493
+ return ce.pow(args[0], args[1]);
22494
+ },
22440
22495
  simplify: (ce, ops) => processPower(ce, ops[0], ops[1], 'simplify'),
22441
22496
  evaluate: (ce, ops) => processPower(ce, ops[0], ops[1], 'evaluate'),
22442
22497
  N: (ce, ops) => {
@@ -22471,6 +22526,7 @@ const ARITHMETIC_LIBRARY = [
22471
22526
  'Number',
22472
22527
  ],
22473
22528
  // codomain: (ce, args) => domainAdd(ce, args),
22529
+ // The 'body' and 'range' need to be interpreted by canonicalMultiplication(). Don't canonicalize them yet.
22474
22530
  canonical: (ce, ops) => canonicalMultiplication(ce, ops[0], ops[1]),
22475
22531
  simplify: (ce, ops) => evalMultiplication(ce, ops[0], ops[1], 'simplify'),
22476
22532
  evaluate: (ce, ops) => evalMultiplication(ce, ops[0], ops[1], 'evaluate'),
@@ -22482,18 +22538,17 @@ const ARITHMETIC_LIBRARY = [
22482
22538
  signature: {
22483
22539
  domain: ['Function', 'Number', ['Maybe', 'Number'], 'RationalNumber'],
22484
22540
  canonical: (ce, args) => {
22485
- args = flattenSequence(args);
22541
+ args = canonical(flattenSequence(args));
22486
22542
  if (args.length === 0)
22487
22543
  return ce._fn('Rational', [ce.error(['missing', 'Number'])]);
22488
22544
  if (args.length === 1)
22489
22545
  return ce._fn('Rational', [
22490
- validateArgument(ce, args[0].canonical, 'ExtendedRealNumber'),
22546
+ validateArgument(ce, args[0], 'ExtendedRealNumber'),
22491
22547
  ]);
22492
- args =
22493
- validateSignature(ce.domain(['Function', 'Integer', 'Integer', 'RationalNumber']), args) ?? args;
22494
- if (args.length !== 2 || !args[0].isValid || !args[1].isValid)
22548
+ args = validateArguments(ce, args, ['Integer', 'Integer']);
22549
+ if (args.length !== 2)
22495
22550
  return ce._fn('Rational', args);
22496
- return canonicalDivide(ce, args[0], args[1]);
22551
+ return ce.div(args[0], args[1]);
22497
22552
  },
22498
22553
  simplify: (ce, ops) => {
22499
22554
  if (ops.length !== 2)
@@ -22528,17 +22583,16 @@ const ARITHMETIC_LIBRARY = [
22528
22583
  signature: {
22529
22584
  domain: ['Function', 'Number', 'Number', 'Number'],
22530
22585
  canonical: (ce, args) => {
22531
- args = flattenSequence(args);
22586
+ args = canonical(flattenSequence(args));
22532
22587
  if (args.length > 2)
22533
22588
  return ce._fn('Root', validateArgumentCount(ce, args, 2));
22534
22589
  const [base, exp] = [
22535
- validateArgument(ce, args[0]?.canonical, 'Number'),
22536
- validateArgument(ce, args[1]?.canonical, 'Number'),
22590
+ validateArgument(ce, args[0], 'Number'),
22591
+ validateArgument(ce, args[1], 'Number'),
22537
22592
  ];
22538
22593
  if (!exp.isValid || !base.isValid)
22539
22594
  return ce._fn('Root', [base, exp]);
22540
- return (canonicalPower(ce, base, ce.inverse(exp)) ??
22541
- ce._fn('Power', [base, ce.inverse(exp)]));
22595
+ return ce.pow(base, ce.inv(exp));
22542
22596
  },
22543
22597
  },
22544
22598
  },
@@ -22596,8 +22650,12 @@ const ARITHMETIC_LIBRARY = [
22596
22650
  complexity: 3000,
22597
22651
  signature: {
22598
22652
  domain: ['Function', 'Number', 'Number'],
22599
- canonical: (ce, args) => canonicalPower(ce, args[0], ce._HALF) ??
22600
- ce._fn('Power', [args[0], ce._HALF]),
22653
+ canonical: (ce, args) => {
22654
+ args = canonical(flattenSequence(args));
22655
+ if (args.length !== 1)
22656
+ return ce._fn('Sqrt', args);
22657
+ return ce.pow(args[0], ce._HALF);
22658
+ },
22601
22659
  simplify: (ce, ops) => processSqrt(ce, ops[0], 'simplify'),
22602
22660
  evaluate: (ce, ops) => processSqrt(ce, ops[0], 'evaluate'),
22603
22661
  N: (ce, ops) => processSqrt(ce, ops[0], 'N'),
@@ -22610,8 +22668,12 @@ const ARITHMETIC_LIBRARY = [
22610
22668
  complexity: 3100,
22611
22669
  signature: {
22612
22670
  domain: ['Function', 'Number', 'Number'],
22613
- canonical: (ce, args) => canonicalPower(ce, args[0], ce.number(2)) ??
22614
- ce._fn('Power', [args[0], ce.number(2)]),
22671
+ canonical: (ce, args) => {
22672
+ args = canonical(flattenSequence(args));
22673
+ if (args.length !== 1)
22674
+ return ce._fn('Square', args);
22675
+ return ce.pow(args[0], ce.number(2));
22676
+ },
22615
22677
  },
22616
22678
  },
22617
22679
  Subtract: {
@@ -22622,7 +22684,7 @@ const ARITHMETIC_LIBRARY = [
22622
22684
  canonical: (ce, args) => {
22623
22685
  // Not necessarily legal, but probably what was intended:
22624
22686
  // ['Subtract', 'x'] -> ['Negate', 'x']
22625
- args = flattenSequence(args.map((x) => x.canonical));
22687
+ args = canonical(flattenSequence(args));
22626
22688
  if (args.length === 1)
22627
22689
  return canonicalNegate(args[0]);
22628
22690
  args = validateArgumentCount(ce, args, 2);
@@ -22630,7 +22692,7 @@ const ARITHMETIC_LIBRARY = [
22630
22692
  return ce._fn('Subtract', args);
22631
22693
  if (!args.every((x) => x.isValid))
22632
22694
  return ce._fn('Subtract', args);
22633
- return canonicalAdd(ce, [args[0], canonicalNegate(args[1])]);
22695
+ return ce.add([args[0], canonicalNegate(args[1])]);
22634
22696
  },
22635
22697
  },
22636
22698
  },
@@ -22795,14 +22857,16 @@ function processAbs(ce, arg, mode) {
22795
22857
  return ce.number(mode === 'N' ? Math.abs(num[0] / num[1]) : [Math.abs(num[0]), num[1]]);
22796
22858
  if (isBigRational(num)) {
22797
22859
  const [n, d] = num;
22798
- return ce.number(mode === 'N' ? n.div(d).abs() : [n.abs(), d]);
22860
+ return ce.number(mode === 'N'
22861
+ ? ce.bignum(n).div(ce.bignum(d)).abs()
22862
+ : [n > 0 ? n : -n, d]);
22799
22863
  }
22800
22864
  }
22801
22865
  }
22802
22866
  if (arg.isNonNegative)
22803
22867
  return arg;
22804
22868
  if (arg.isNegative)
22805
- return ce.negate(arg);
22869
+ return ce.neg(arg);
22806
22870
  return undefined;
22807
22871
  }
22808
22872
 
@@ -22906,6 +22970,164 @@ const COLLECTIONS_LIBRARY = {
22906
22970
  // sort
22907
22971
  // contains / find
22908
22972
 
22973
+ function oneOf(xs) {
22974
+ return xs[Math.floor(Math.random() * xs.length)];
22975
+ }
22976
+ function randomExpressionWithHead(head, level) {
22977
+ if (head === 'Add' || head === 'Multiply') {
22978
+ const ops = [];
22979
+ let count = 1 + Math.floor(Math.random() * 12);
22980
+ while (count > 0) {
22981
+ ops.push(randomExpression(level + 1));
22982
+ count -= 1;
22983
+ }
22984
+ return [head, ...ops];
22985
+ }
22986
+ if (head === 'Divide' || head === 'Power') {
22987
+ return [head, randomExpression(level + 1), randomExpression(level + 1)];
22988
+ }
22989
+ if (head === 'Root') {
22990
+ return [head, randomExpression(level + 1), randomExpression(10)];
22991
+ }
22992
+ if (head === 'trig')
22993
+ return randomTrig();
22994
+ return [head, randomExpression(level + 1)];
22995
+ }
22996
+ function randomTrig() {
22997
+ return [
22998
+ oneOf([
22999
+ 'Cos',
23000
+ 'Sin',
23001
+ 'Tan',
23002
+ 'Sinh',
23003
+ 'Arccos',
23004
+ 'Arsinh',
23005
+ ['InverseFunction', 'Cos'],
23006
+ ]),
23007
+ oneOf([
23008
+ 'Pi',
23009
+ '-1',
23010
+ '0',
23011
+ '1',
23012
+ ['Divide', 'Pi', -5],
23013
+ ['Multiply', -2, ['Divide', 'Pi', 11]],
23014
+ ['Multiply', 'Half', 'Pi'],
23015
+ ['Multiply', 5, 'Pi'],
23016
+ ['Multiply', 12, 'Pi'],
23017
+ ['Divide', 'Pi', 5],
23018
+ ['Divide', 'Pi', 9],
23019
+ ['Multiply', 5, ['Divide', 'Pi', 9]],
23020
+ ['Multiply', 2, ['Divide', 'Pi', 11]],
23021
+ ['Multiply', 2, ['Divide', 'Pi', 3]],
23022
+ ]),
23023
+ ];
23024
+ }
23025
+ function randomExpression(level) {
23026
+ level ?? (level = 1);
23027
+ if (level === 1) {
23028
+ const h = oneOf([
23029
+ 'Add',
23030
+ 'Add',
23031
+ 'Add',
23032
+ 'Add',
23033
+ 'Add',
23034
+ 'Multiply',
23035
+ 'Multiply',
23036
+ 'Multiply',
23037
+ 'Multiply',
23038
+ 'Divide',
23039
+ 'Divide',
23040
+ 'Divide',
23041
+ 'Root',
23042
+ 'Sqrt',
23043
+ 'Subtract',
23044
+ 'Negate',
23045
+ 'trig',
23046
+ ]);
23047
+ return randomExpressionWithHead(h, 1);
23048
+ }
23049
+ if (level === 2) {
23050
+ if (Math.random() > 0.5)
23051
+ return randomExpression(3);
23052
+ if (Math.random() > 0.75)
23053
+ return randomExpression(1);
23054
+ const h = oneOf([
23055
+ 'Multiply',
23056
+ 'Multiply',
23057
+ 'Add',
23058
+ 'Power',
23059
+ 'trig',
23060
+ 'Ln',
23061
+ 'Exp',
23062
+ ]);
23063
+ return randomExpressionWithHead(h, 2);
23064
+ }
23065
+ return oneOf([
23066
+ -0.000012345,
23067
+ -2,
23068
+ -2,
23069
+ -2,
23070
+ -3,
23071
+ -5,
23072
+ -6,
23073
+ -12,
23074
+ -1.654e-57,
23075
+ 0,
23076
+ 0,
23077
+ 0.00012345,
23078
+ 1.654e-57,
23079
+ 1,
23080
+ 2,
23081
+ 2,
23082
+ 2,
23083
+ 2,
23084
+ 3,
23085
+ 3,
23086
+ 5,
23087
+ 5,
23088
+ 6,
23089
+ 6,
23090
+ 1234.5678,
23091
+ 5678.1234,
23092
+ 10,
23093
+ 15,
23094
+ 18,
23095
+ 30,
23096
+ 60,
23097
+ 1.234e57,
23098
+ '123456789.12345678912345e200',
23099
+ '987654321.12345678912345',
23100
+ ['Rational', -6, 10],
23101
+ ['Rational', -12, 15],
23102
+ ['Rational', -15, 12],
23103
+ ['Rational', 3, 5],
23104
+ ['Rational', 12, 15],
23105
+ ['Rational', 15, 12],
23106
+ 'ExponentialE',
23107
+ 'ImaginaryUnit',
23108
+ ['Sqrt', 3],
23109
+ ['Sqrt', 5],
23110
+ ['Sqrt', 15],
23111
+ ['Sqrt', 25],
23112
+ ['Complex', -1.1, 1.1],
23113
+ ['Complex', 4, 5],
23114
+ 'x',
23115
+ 'x',
23116
+ 'x',
23117
+ 'x',
23118
+ ['Add', 'x', 1],
23119
+ ['Divide', 'x', 3],
23120
+ ['Square', 'x'],
23121
+ ['Power', 'x', 3],
23122
+ ['Power', 'x', 4],
23123
+ ['Subtract', 'x', 1],
23124
+ ['Add', 'x', 1],
23125
+ 'a',
23126
+ 'b',
23127
+ 'Pi',
23128
+ ]);
23129
+ }
23130
+
22909
23131
  // // := assign 80 // @todo
22910
23132
  const CORE_LIBRARY = [
22911
23133
  {
@@ -22945,7 +23167,7 @@ const CORE_LIBRARY = [
22945
23167
  signature: {
22946
23168
  domain: ['Function', 'Anything', ['Tuple', 'Anything']],
22947
23169
  codomain: (ce, args) => ce.domain(['Tuple', args[0].domain]),
22948
- canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, ops.map((x) => x.canonical), 1)),
23170
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 1)),
22949
23171
  },
22950
23172
  },
22951
23173
  Pair: {
@@ -22959,7 +23181,7 @@ const CORE_LIBRARY = [
22959
23181
  ['Tuple', 'Anything', 'Anything'],
22960
23182
  ],
22961
23183
  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)),
23184
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 2)),
22963
23185
  },
22964
23186
  },
22965
23187
  Triple: {
@@ -22974,7 +23196,7 @@ const CORE_LIBRARY = [
22974
23196
  ['Tuple', 'Anything', 'Anything', 'Anything'],
22975
23197
  ],
22976
23198
  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)),
23199
+ canonical: (ce, ops) => ce.tuple(validateArgumentCount(ce, canonical(ops), 3)),
22978
23200
  },
22979
23201
  },
22980
23202
  Tuple: {
@@ -22986,7 +23208,7 @@ const CORE_LIBRARY = [
22986
23208
  ['Sequence', 'Anything'],
22987
23209
  ['Tuple', ['Sequence', 'Anything']],
22988
23210
  ],
22989
- canonical: (ce, ops) => ce.tuple(ops.map((x) => x.canonical)),
23211
+ canonical: (ce, ops) => ce.tuple(canonical(ops)),
22990
23212
  codomain: (ce, args) => ce.domain(['Tuple', ...args.map((x) => x.domain)]),
22991
23213
  },
22992
23214
  },
@@ -23114,7 +23336,7 @@ const CORE_LIBRARY = [
23114
23336
  /** Return the domain of an expression */
23115
23337
  signature: {
23116
23338
  domain: ['Function', 'Anything', 'Domain'],
23117
- canonical: (ce, ops) => ce.domain(validateArgumentCount(ce, ops.map((x) => x.canonical), 1)[0]),
23339
+ canonical: (ce, ops) => ce.domain(validateArgumentCount(ce, canonical(ops), 1)[0]),
23118
23340
  },
23119
23341
  },
23120
23342
  Evaluate: {
@@ -23122,7 +23344,7 @@ const CORE_LIBRARY = [
23122
23344
  signature: {
23123
23345
  domain: ['Function', 'Anything', 'Anything'],
23124
23346
  codomain: (_ce, args) => args[0].domain,
23125
- canonical: (ce, ops) => ce._fn('Evaluate', validateArgumentCount(ce, ops.map((x) => x.canonical), 1)),
23347
+ canonical: (ce, ops) => ce._fn('Evaluate', validateArgumentCount(ce, canonical(ops), 1)),
23126
23348
  evaluate: (_ce, ops) => ops[0].evaluate(),
23127
23349
  },
23128
23350
  },
@@ -23351,8 +23573,7 @@ const CORE_LIBRARY = [
23351
23573
  const op1 = ops[0];
23352
23574
  const val = asFloat(op1) ?? NaN;
23353
23575
  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;
23576
+ return ce.error(['incompatible-domain', 'Integer', op1.domain], op1);
23356
23577
  }
23357
23578
  const op2 = ops[1];
23358
23579
  if (op2.isNothing) {
@@ -23364,14 +23585,11 @@ const CORE_LIBRARY = [
23364
23585
  return ce.string(Math.abs(Math.round(asFloat(op1) ?? NaN)).toString());
23365
23586
  }
23366
23587
  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;
23588
+ return ce.error(['incompatible-domain', 'Integer', op2.domain], op2);
23369
23589
  }
23370
23590
  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
- }
23591
+ if (base < 2 || base > 36)
23592
+ return ce.error(['out-of-range', 2, 36, base], op2);
23375
23593
  return ce.string(Math.abs(val).toString(base));
23376
23594
  },
23377
23595
  },
@@ -23455,6 +23673,14 @@ const CORE_LIBRARY = [
23455
23673
  },
23456
23674
  },
23457
23675
  },
23676
+ {
23677
+ RandomExpression: {
23678
+ signature: {
23679
+ domain: 'Function',
23680
+ evaluate: (ce, _ops) => ce.box(randomExpression()),
23681
+ },
23682
+ },
23683
+ },
23458
23684
  ];
23459
23685
  // xcas/gias https://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/cascmd_en/cascmd_en.html
23460
23686
  // https://www.haskell.org/onlinereport/haskell2010/haskellch9.html#x16-1720009.1
@@ -23795,9 +24021,7 @@ const RELOP_LIBRARY = {
23795
24021
  signature: {
23796
24022
  domain: 'RelationalOperator',
23797
24023
  canonical: (ce, ops) => {
23798
- ops =
23799
- flattenOps(flattenSequence(ops).map((x) => x.canonical), 'Equal') ?? ops;
23800
- return ce._fn('Equal', ops);
24024
+ return ce._fn('Equal', flattenOps(canonical(flattenSequence(ops)), 'Equal'));
23801
24025
  },
23802
24026
  evaluate: (ce, ops) => {
23803
24027
  if (ops.length < 2)
@@ -23843,11 +24067,7 @@ const RELOP_LIBRARY = {
23843
24067
  complexity: 11000,
23844
24068
  signature: {
23845
24069
  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
- },
24070
+ canonical: (ce, ops) => ce._fn('Less', flattenOps(canonical(flattenSequence(ops)), 'Less')),
23851
24071
  evaluate: (ce, ops) => {
23852
24072
  if (ops.length < 2)
23853
24073
  return ce.symbol('True');
@@ -24085,7 +24305,7 @@ const SETS_LIBRARY = {
24085
24305
  signature: {
24086
24306
  domain: 'Predicate',
24087
24307
  canonical: (ce, args) => {
24088
- args = validateArgumentCount(ce, flattenSequence(args).map((x) => x.canonical), 2);
24308
+ args = validateArgumentCount(ce, flattenSequence(canonical(args)), 2);
24089
24309
  if (args.length === 2 && isDomain(args[1]))
24090
24310
  return ce._fn('Element', [args[0], ce.domain(args[1])]);
24091
24311
  return ce._fn('Element', args);
@@ -24299,13 +24519,15 @@ const TRIGONOMETRY_LIBRARY = [
24299
24519
  signature: {
24300
24520
  domain: ['Function', 'Number', 'Number'],
24301
24521
  canonical: (ce, ops) => {
24302
- ops = validateArgumentCount(ce, flattenSequence(ops), 1);
24522
+ ops = validateArguments(ce, flattenSequence(canonical(ops)), [
24523
+ 'Number',
24524
+ ]);
24303
24525
  if (ops.length !== 1)
24304
24526
  return ce.box(['Degrees', ops]);
24305
- const arg = validateArgument(ce, ops[0].canonical, 'Number');
24527
+ const arg = ops[0];
24306
24528
  if (arg.numericValue === null || !arg.isValid)
24307
24529
  return ce.box(['Degrees', arg]);
24308
- return ce.mul([arg, ce.box(['Divide', 'Pi', 180])]);
24530
+ return ce.div(ce.mul([arg, ce.symbol('Pi')]), ce.number(180));
24309
24531
  },
24310
24532
  evaluate: (ce, ops) => ce.mul([ops[0], ce.box(['Divide', 'Pi', 180])]),
24311
24533
  },
@@ -24664,7 +24886,7 @@ const TRIGONOMETRY_LIBRARY = [
24664
24886
  signature: {
24665
24887
  domain: ['Function', 'Function', 'Function'],
24666
24888
  canonical: (ce, ops) => {
24667
- ops = validateArgumentCount(ce, flattenSequence(ops), 1).map((x) => x.canonical);
24889
+ ops = validateArgumentCount(ce, flattenSequence(canonical(ops)), 1);
24668
24890
  return (processInverseFunction(ce, ops) ?? ce._fn('InverseFunction', ops));
24669
24891
  },
24670
24892
  simplify: (ce, ops) => processInverseFunction(ce, ops),
@@ -24898,7 +25120,7 @@ function constructibleValues(ce, head, x) {
24898
25120
  [sign, head] = TRIG_IDENTITIES[head]?.[quadrant] ?? [1, head];
24899
25121
  sign = sign * identitySign;
24900
25122
  for (const [[n, d], result] of specialValues) {
24901
- if (ce.chop(theta - (Math.PI * n) / d) === 0) {
25123
+ if (result[head] && ce.chop(theta - (Math.PI * n) / d) === 0) {
24902
25124
  // Cos and Sec are even functions, the others are odd
24903
25125
  return sign < 0 ? canonicalNegate(result[head]) : result[head];
24904
25126
  }
@@ -25922,12 +26144,11 @@ function setCurrentContextSymbolTable(engine, table) {
25922
26144
  if (engine.strict && entry.wikidata) {
25923
26145
  for (const [_, d] of idTable) {
25924
26146
  if (d.wikidata === entry.wikidata)
25925
- throw new Error(`Duplicate entry with wikidata "${entry.wikidata}": "${name}" and "${d.name}"`);
26147
+ throw new Error(`Duplicate entries with wikidata "${entry.wikidata}": "${name}" and "${d.name}"`);
25926
26148
  }
25927
26149
  }
25928
- if (idTable.has(name)) {
25929
- throw new Error(`Duplicate symbol definition "${name}":\n${JSON.stringify(idTable.get(name))}\n${JSON.stringify(entry)}`);
25930
- }
26150
+ if (idTable.has(name))
26151
+ throw new Error(`Duplicate symbol definition "${name}"`);
25931
26152
  idTable.set(name, def);
25932
26153
  }
25933
26154
  else {
@@ -25977,7 +26198,7 @@ function setCurrentContextSymbolTable(engine, table) {
25977
26198
  * ```
25978
26199
  */
25979
26200
  function numericCostFunction(n) {
25980
- if (Number.isInteger(n)) {
26201
+ if (Number.isInteger(n) && n !== 0) {
25981
26202
  return Math.floor(Math.log2(Math.abs(n)) / Math.log2(10)) + (n > 0 ? 1 : 2);
25982
26203
  }
25983
26204
  return 2;
@@ -26001,8 +26222,8 @@ function costFunction(expr) {
26001
26222
  if (isMachineRational(num))
26002
26223
  return numericCostFunction(num[0]) + numericCostFunction(num[1]) + 1;
26003
26224
  else
26004
- return (numericCostFunction(num[0].toNumber()) +
26005
- numericCostFunction(num[1].toNumber()) +
26225
+ return (numericCostFunction(Number(num[0])) +
26226
+ numericCostFunction(Number(num[1])) +
26006
26227
  1);
26007
26228
  }
26008
26229
  if (num instanceof Complex)
@@ -26011,8 +26232,24 @@ function costFunction(expr) {
26011
26232
  return 2;
26012
26233
  }
26013
26234
  const head = expr.head;
26014
- return ((typeof head === 'string' ? 1 : costFunction(head)) +
26015
- (expr.ops?.reduce((acc, x) => acc + costFunction(x), 0) ?? 0));
26235
+ let headCost = 2;
26236
+ if (typeof head === 'string') {
26237
+ if (['Add', 'Divide'].includes(head))
26238
+ headCost = 3;
26239
+ else if (['Subtract', 'Negate'].includes(head))
26240
+ headCost = 4;
26241
+ else if (['Square', 'Sqrt', 'Multiply', 'Root'].includes(head))
26242
+ headCost = 5;
26243
+ else if (['Power'].includes(head))
26244
+ headCost = 6;
26245
+ else if (['Ln', 'Exp', 'Log'].includes(head))
26246
+ headCost = 7;
26247
+ else
26248
+ headCost = 8;
26249
+ }
26250
+ else
26251
+ headCost = costFunction(head);
26252
+ return (headCost + (expr.ops?.reduce((acc, x) => acc + costFunction(x), 0) ?? 0));
26016
26253
  }
26017
26254
  const DEFAULT_COST_FUNCTION = costFunction;
26018
26255
 
@@ -26468,10 +26705,16 @@ class BoxedSymbol extends AbstractBoxedExpression {
26468
26705
  }
26469
26706
  /** A free variable either has no definition, or it has a definition, but no value */
26470
26707
  get isFree() {
26471
- return !this.symbolDefinition?.value;
26708
+ // Don't use `.symbolDefinition` as this has a side effect of creating
26709
+ // a def, which is not desirable whn we're just doing a test.
26710
+ const def = this._def ?? this.engine.lookupSymbol(this._name, this._wikidata);
26711
+ return !isSymbolDefinition(def) || def.value === undefined;
26472
26712
  }
26473
26713
  get isConstant() {
26474
- return this.symbolDefinition?.constant ?? false;
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.constant;
26475
26718
  }
26476
26719
  get isCanonical() {
26477
26720
  return this._scope !== null;
@@ -27051,7 +27294,7 @@ class ComputeEngine {
27051
27294
  shorthands: ['function', 'symbol', 'string', 'dictionary', 'number'],
27052
27295
  metadata: [],
27053
27296
  precision: 'max',
27054
- repeatingDecimals: true,
27297
+ repeatingDecimals: false,
27055
27298
  };
27056
27299
  this._stats = {
27057
27300
  highwaterMark: 0,
@@ -27082,20 +27325,10 @@ class ComputeEngine {
27082
27325
  //
27083
27326
  this.context = {
27084
27327
  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
27328
  timeLimit: 2.0,
27096
27329
  memoryLimit: 1.0,
27097
27330
  recursionLimit: 1024,
27098
- // iterationLimit: no iteration limit
27331
+ iterationLimit: Number.POSITIVE_INFINITY,
27099
27332
  };
27100
27333
  const tables = options?.ids ?? ComputeEngine.getStandardLibrary();
27101
27334
  for (const table of tables)
@@ -27361,6 +27594,8 @@ class ComputeEngine {
27361
27594
  }
27362
27595
  /** @internal */
27363
27596
  bignum(a) {
27597
+ if (typeof a === 'bigint')
27598
+ return new this._bignum(a.toString());
27364
27599
  return new this._bignum(a);
27365
27600
  }
27366
27601
  /** @internal */
@@ -27508,6 +27743,10 @@ class ComputeEngine {
27508
27743
  if (this.context === null)
27509
27744
  throw Error('No parent scope available');
27510
27745
  this.context = {
27746
+ timeLimit: this.context.timeLimit,
27747
+ memoryLimit: this.context.memoryLimit,
27748
+ recursionLimit: this.context.recursionLimit,
27749
+ iterationLimit: this.context.iterationLimit,
27511
27750
  ...(scope ?? {}),
27512
27751
  parentScope: this.context,
27513
27752
  // We always copy the current assumptions in the new scope.
@@ -27535,29 +27774,6 @@ class ComputeEngine {
27535
27774
  if (!this.context)
27536
27775
  throw Error('No scope available');
27537
27776
  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
27777
  this.context = parentScope ?? null;
27562
27778
  console.assert(this.context !== null);
27563
27779
  }
@@ -27571,6 +27787,7 @@ class ComputeEngine {
27571
27787
  if (def)
27572
27788
  def.value = idk ?? undefined;
27573
27789
  else if (idk !== undefined && idk !== null) {
27790
+ // Unknown identifier, define a new one
27574
27791
  const val = this.box(idk);
27575
27792
  if (val.domain.isNumeric)
27576
27793
  this.defineSymbol(k, { value: val, domain: 'Number' });
@@ -27610,10 +27827,12 @@ class ComputeEngine {
27610
27827
  for (const k of Object.keys(identifiers)) {
27611
27828
  if (k !== 'Nothing') {
27612
27829
  const def = identifiers[k];
27613
- if ('value' in def || ('domain' in def && def.domain !== 'Function'))
27830
+ if (isSymbolDefinition$1(def))
27614
27831
  this.defineSymbol(k, def);
27615
- else
27832
+ else if (isFunctionDefinition$1(def))
27616
27833
  this.defineFunction(k, def);
27834
+ else
27835
+ this.set({ [k]: identifiers[k] });
27617
27836
  }
27618
27837
  }
27619
27838
  }
@@ -27649,31 +27868,14 @@ class ComputeEngine {
27649
27868
  throw new Error('timeout');
27650
27869
  }
27651
27870
  }
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
- }
27871
+ // assert(
27872
+ // condition: boolean,
27873
+ // expr: BoxedExpression,
27874
+ // msg: string,
27875
+ // code?: SignalMessage
27876
+ // ) {
27877
+ // if (!condition) this.signal(expr, msg, code);
27878
+ // }
27677
27879
  /** @internal */
27678
27880
  cache(cacheName, build, purge) {
27679
27881
  if (this._cache[cacheName] === undefined) {
@@ -27689,6 +27891,12 @@ class ComputeEngine {
27689
27891
  box(expr, options) {
27690
27892
  return box(this, expr, options);
27691
27893
  }
27894
+ canonical(xs) {
27895
+ if (!xs.every((x) => x instanceof AbstractBoxedExpression))
27896
+ return xs.map((x) => this.box(x));
27897
+ const bxs = xs;
27898
+ return bxs.every((x) => x.isCanonical) ? bxs : bxs.map((x) => x.canonical);
27899
+ }
27692
27900
  fn(head, ops, metadata) {
27693
27901
  return boxFunction(this, head, ops, { metadata, canonical: true });
27694
27902
  }
@@ -27731,29 +27939,27 @@ class ComputeEngine {
27731
27939
  }
27732
27940
  add(ops, metadata) {
27733
27941
  // Short path. Note that are arguments are **not** validated.
27734
- // ops = flattenSequence(ops);
27735
- const result = canonicalAdd(this, ops);
27942
+ const result = canonicalAdd(this, flattenOps(flattenSequence(ops), 'Add'));
27736
27943
  if (metadata?.latex !== undefined)
27737
27944
  result.latex = metadata.latex;
27738
27945
  if (metadata?.wikidata !== undefined)
27739
27946
  result.wikidata = metadata.wikidata;
27740
27947
  return result;
27741
27948
  }
27742
- negate(expr, metadata) {
27949
+ neg(expr, metadata) {
27743
27950
  // Short path. Note that are arguments are **not** validated.
27744
27951
  return canonicalNegate(expr, metadata);
27745
27952
  }
27746
27953
  mul(ops, metadata) {
27747
27954
  // Short path. Note that are arguments are **not** validated.
27748
- ops = flattenSequence(ops);
27749
- const result = canonicalMultiply(this, ops);
27955
+ const result = canonicalMultiply(this, flattenOps(flattenSequence(ops), ' Multiply'));
27750
27956
  if (metadata?.latex !== undefined)
27751
27957
  result.latex = metadata.latex;
27752
27958
  if (metadata?.wikidata !== undefined)
27753
27959
  result.wikidata = metadata.wikidata;
27754
27960
  return result;
27755
27961
  }
27756
- divide(num, denom, metadata) {
27962
+ div(num, denom, metadata) {
27757
27963
  // Short path. Note that are arguments are **not** validated.
27758
27964
  const result = canonicalDivide(this, num, denom);
27759
27965
  if (metadata?.latex !== undefined)
@@ -27763,11 +27969,11 @@ class ComputeEngine {
27763
27969
  return result;
27764
27970
  }
27765
27971
  sqrt(base, metadata) {
27766
- return this.power(base, [1, 2], metadata);
27972
+ return canonicalPower(this, base, this._HALF, metadata);
27767
27973
  }
27768
- power(base, exponent, metadata) {
27974
+ pow(base, exponent, metadata) {
27769
27975
  // Short path. Note that are arguments are **not** validated.
27770
- let e = null;
27976
+ // The logic here handles the cases where the exponent is a number or Rational
27771
27977
  if (exponent instanceof AbstractBoxedExpression) {
27772
27978
  const num = exponent.numericValue;
27773
27979
  if (num !== null) {
@@ -27777,13 +27983,15 @@ class ComputeEngine {
27777
27983
  exponent = num;
27778
27984
  }
27779
27985
  }
27986
+ let e = null;
27780
27987
  if (typeof exponent === 'number')
27781
27988
  e = exponent;
27782
27989
  else if (isRational(exponent)) {
27990
+ // Is the denominator 1?
27783
27991
  if (isMachineRational(exponent) && exponent[1] === 1)
27784
27992
  e = exponent[0];
27785
- if (isBigRational(exponent) && exponent[1].equals(1))
27786
- e = exponent[0].toNumber();
27993
+ else if (isBigRational(exponent) && exponent[1] === 1n)
27994
+ e = Number(exponent[0]);
27787
27995
  }
27788
27996
  // x^1
27789
27997
  if (e === 1)
@@ -27793,18 +28001,40 @@ class ComputeEngine {
27793
28001
  if (e === -1 && r !== null) {
27794
28002
  if (typeof r === 'number' && Number.isInteger(r))
27795
28003
  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]]);
28004
+ else if (r instanceof Decimal && r.isInteger())
28005
+ return this.number([1n, bigint(r)]);
28006
+ else if (isRational(r))
28007
+ return this.number([r[1], r[0]]);
27800
28008
  }
27801
28009
  if (typeof exponent === 'number' || isRational(exponent))
27802
28010
  exponent = this.number(exponent);
27803
- return (canonicalPower(this, base, exponent, metadata) ??
27804
- this._fn('Power', [base, exponent], metadata));
28011
+ return canonicalPower(this, base, exponent, metadata);
27805
28012
  }
27806
- inverse(expr, metadata) {
28013
+ inv(expr, metadata) {
27807
28014
  // Short path. Note that are arguments are **not** validated.
28015
+ if (expr.isOne)
28016
+ return this._ONE;
28017
+ if (expr.isNegativeOne)
28018
+ return this._NEGATIVE_ONE;
28019
+ if (expr.isInfinity)
28020
+ return this._ZERO;
28021
+ const n = expr.numericValue;
28022
+ if (n !== null) {
28023
+ if (isRational(n))
28024
+ return this.number(inverse(n), { metadata });
28025
+ if (typeof n === 'number' && Number.isInteger(n))
28026
+ return this.number([1, n], { metadata });
28027
+ if (n instanceof Decimal && n.isInteger())
28028
+ return this.number([1n, bigint(n)], { metadata });
28029
+ return this._fn('Divide', [this._ONE, expr], metadata);
28030
+ }
28031
+ if (expr.head === 'Sqrt')
28032
+ return this._fn('Sqrt', [this.inv(expr.op1)], metadata);
28033
+ if (expr.head === 'Divide')
28034
+ return this._fn('Divide', [expr[1], expr[0]], metadata);
28035
+ if (expr.head === 'Rational')
28036
+ return this.number([expr[1], expr[0]], { metadata });
28037
+ // Inverse(expr) -> expr^{-1}
27808
28038
  let e = this._NEGATIVE_ONE;
27809
28039
  if (expr.head === 'Power') {
27810
28040
  // Inverse(x^{-1}) -> x
@@ -27814,21 +28044,20 @@ class ComputeEngine {
27814
28044
  e = canonicalNegate(expr.op2);
27815
28045
  expr = expr.op1;
27816
28046
  }
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));
28047
+ if (e.isNegativeOne)
28048
+ return this._fn('Divide', [this._ONE, expr], metadata);
28049
+ return this._fn('Power', [expr, e], metadata);
27821
28050
  }
27822
28051
  pair(first, second, metadata) {
27823
28052
  // Short path
27824
- return new BoxedFunction(this, 'Tuple', [first.canonical, second.canonical], {
28053
+ return new BoxedFunction(this, 'Tuple', [first, second], {
27825
28054
  metadata,
27826
28055
  canonical: true,
27827
28056
  });
27828
28057
  }
27829
28058
  tuple(elements, metadata) {
27830
28059
  // Short path
27831
- return new BoxedFunction(this, 'Tuple', elements.map((x) => x.canonical), {
28060
+ return new BoxedFunction(this, 'Tuple', canonical(elements), {
27832
28061
  metadata,
27833
28062
  canonical: true,
27834
28063
  });
@@ -27905,24 +28134,36 @@ class ComputeEngine {
27905
28134
  //
27906
28135
  // Is this number eligible to be a cached number expression?
27907
28136
  //
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];
28137
+ if (options.metadata === undefined) {
28138
+ if (typeof value === 'bigint') {
28139
+ if (value === 1n)
28140
+ return this._ONE;
28141
+ if (value === 0n)
28142
+ return this._ZERO;
28143
+ if (value === -1n)
28144
+ return this._NEGATIVE_ONE;
28145
+ }
28146
+ if (typeof value === 'number') {
28147
+ const n = value;
28148
+ if (n === 1)
28149
+ return this._ONE;
28150
+ if (n === 0)
28151
+ return this._ZERO;
28152
+ if (n === -1)
28153
+ return this._NEGATIVE_ONE;
28154
+ if (Number.isInteger(n) && this._commonNumbers[n] !== undefined) {
28155
+ if (this._commonNumbers[n] === null)
28156
+ this._commonNumbers[n] = boxNumber(this, value) ?? this._NAN;
28157
+ return this._commonNumbers[n];
28158
+ }
28159
+ if (Number.isNaN(n))
28160
+ return this._NAN;
28161
+ if (!Number.isFinite(n))
28162
+ return n < 0 ? this._NEGATIVE_INFINITY : this._POSITIVE_INFINITY;
27920
28163
  }
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
28164
  }
28165
+ if (typeof value === 'bigint')
28166
+ value = this.bignum(value);
27926
28167
  return boxNumber(this, value, options) ?? this._NAN;
27927
28168
  }
27928
28169
  rules(rules) {
@@ -28094,8 +28335,7 @@ class ComputeEngine {
28094
28335
  // it in a parent scope. However, when the current scope exits,
28095
28336
  // any previous assumptions about the symbol will be restored).
28096
28337
  for (const [assumption, _val] of this.assumptions) {
28097
- const vars = getVars(assumption);
28098
- if (vars.includes(symbol))
28338
+ if (assumption.symbols.includes(symbol))
28099
28339
  this.assumptions.delete(assumption);
28100
28340
  }
28101
28341
  }
@@ -28103,6 +28343,6 @@ class ComputeEngine {
28103
28343
  }
28104
28344
 
28105
28345
  // This file is the root of the `compute-engine` package
28106
- const version = '0.11.0';
28346
+ const version = '0.12.0';
28107
28347
 
28108
- export { ComputeEngine, getVars, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };
28348
+ export { ComputeEngine, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };