@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.
- package/dist/compute-engine.esm.js +1592 -1352
- package/dist/compute-engine.min.esm.js +2 -2
- package/dist/compute-engine.min.js +2 -2
- package/dist/math-json.esm.js +2 -2
- package/dist/math-json.min.esm.js +2 -2
- package/dist/math-json.min.js +2 -2
- package/dist/types/common/grapheme-splitter.d.ts +1 -1
- package/dist/types/common/signals.d.ts +1 -1
- package/dist/types/common/utils.d.ts +1 -1
- package/dist/types/compute-engine/assume.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +3 -2
- package/dist/types/compute-engine/boxed-expression/box.d.ts +2 -2
- package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-domain.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-function-definition.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-symbol-definition.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
- package/dist/types/compute-engine/boxed-expression/utils.d.ts +8 -3
- package/dist/types/compute-engine/boxed-expression/validate.d.ts +4 -3
- package/dist/types/compute-engine/compute-engine.d.ts +15 -32
- package/dist/types/compute-engine/cost-function.d.ts +1 -1
- package/dist/types/compute-engine/domain-utils.d.ts +3 -3
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-inequalities.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/public.d.ts +2 -2
- package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -2
- package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
- package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
- package/dist/types/compute-engine/library/arithmetic-add.d.ts +1 -1
- package/dist/types/compute-engine/library/arithmetic-divide.d.ts +1 -1
- package/dist/types/compute-engine/library/arithmetic-multiply.d.ts +1 -1
- package/dist/types/compute-engine/library/arithmetic-power.d.ts +2 -4
- package/dist/types/compute-engine/library/arithmetic.d.ts +2 -2
- package/dist/types/compute-engine/library/calculus.d.ts +2 -2
- package/dist/types/compute-engine/library/collections.d.ts +2 -2
- package/dist/types/compute-engine/library/core.d.ts +2 -2
- package/dist/types/compute-engine/library/domains.d.ts +1 -1
- package/dist/types/compute-engine/library/library.d.ts +5 -5
- package/dist/types/compute-engine/library/logic.d.ts +2 -2
- package/dist/types/compute-engine/library/polynomials.d.ts +2 -2
- package/dist/types/compute-engine/library/random-expression.d.ts +2 -0
- package/dist/types/compute-engine/library/relational-operator.d.ts +2 -2
- package/dist/types/compute-engine/library/sets.d.ts +2 -2
- package/dist/types/compute-engine/library/trigonometry.d.ts +2 -2
- package/dist/types/compute-engine/library/utils.d.ts +1 -1
- package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +12 -0
- package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -9
- package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
- package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
- package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
- package/dist/types/compute-engine/numerics/rationals.d.ts +10 -12
- package/dist/types/compute-engine/public.d.ts +31 -47
- package/dist/types/compute-engine/rules.d.ts +1 -1
- package/dist/types/compute-engine/simplify-rules.d.ts +1 -1
- package/dist/types/compute-engine/solve.d.ts +1 -1
- package/dist/types/compute-engine/symbolic/expand.d.ts +1 -1
- package/dist/types/compute-engine/symbolic/flatten.d.ts +3 -2
- package/dist/types/compute-engine/symbolic/negate.d.ts +4 -6
- package/dist/types/compute-engine/symbolic/polynomials.d.ts +1 -1
- package/dist/types/compute-engine/symbolic/product.d.ts +1 -1
- package/dist/types/compute-engine/symbolic/sum.d.ts +3 -2
- package/dist/types/compute-engine/symbolic/utils.d.ts +1 -1
- package/dist/types/compute-engine.d.ts +2 -3
- package/dist/types/math-json/math-json-format.d.ts +1 -1
- package/dist/types/math-json/utils.d.ts +2 -1
- package/dist/types/math-json.d.ts +2 -3
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** CortexJS Compute Engine 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 (
|
|
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 (
|
|
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
|
-
// -
|
|
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
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
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
|
-
|
|
13017
|
-
|
|
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
|
-
|
|
13050
|
-
if (num[0] === '.')
|
|
13051
|
-
num = '0' + num;
|
|
13052
|
-
let
|
|
13053
|
-
if (
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
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
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
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
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
13974
|
-
|
|
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
|
|
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
|
|
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
|
|
14407
|
+
function getFreeVars(expr, set) {
|
|
14255
14408
|
if (expr.symbol) {
|
|
14256
|
-
const def = expr.
|
|
14257
|
-
|
|
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
|
-
|
|
14418
|
+
getFreeVars(op, set);
|
|
14265
14419
|
if (expr.keys)
|
|
14266
14420
|
for (const key of expr.keys)
|
|
14267
|
-
|
|
14268
|
-
return
|
|
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
|
|
14427
|
+
return;
|
|
14274
14428
|
}
|
|
14275
14429
|
if (!expr.ops && !expr.keys)
|
|
14276
|
-
return
|
|
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
|
|
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
|
|
14328
|
-
if (
|
|
14329
|
-
|
|
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
|
|
14489
|
+
return null;
|
|
14342
14490
|
if (s === 'infinity' || s === '+infinity')
|
|
14343
|
-
return
|
|
14491
|
+
return null;
|
|
14344
14492
|
if (s === '-infinity')
|
|
14345
|
-
return
|
|
14346
|
-
|
|
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
|
-
*
|
|
14505
|
-
* If
|
|
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
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
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
|
-
|
|
14519
|
-
|
|
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
|
-
|
|
14531
|
-
ys.push(x);
|
|
14732
|
+
return [rhs[1] * lhs[0] + rhs[0] * lhs[1], rhs[1] * lhs[1]];
|
|
14532
14733
|
}
|
|
14533
|
-
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
return
|
|
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
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
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
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
*
|
|
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
|
-
|
|
14569
|
-
|
|
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 (
|
|
15044
|
-
return [lhs[0]
|
|
15045
|
-
if (
|
|
15046
|
-
|
|
15047
|
-
|
|
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]
|
|
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.
|
|
15058
|
-
return [rhsNum.
|
|
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]
|
|
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]
|
|
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
|
-
|
|
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]
|
|
14818
|
+
if (r[0] === 1n || r[1] === 1n)
|
|
15095
14819
|
return r;
|
|
15096
|
-
if (r[1]
|
|
15097
|
-
r = [r[0]
|
|
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
|
|
14824
|
+
if (g <= 1)
|
|
15101
14825
|
return r;
|
|
15102
|
-
return [r[0]
|
|
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
|
|
15174
|
-
|
|
15175
|
-
|
|
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.
|
|
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.
|
|
14939
|
+
return [inverse(coef), ce.inv(base)];
|
|
15216
14940
|
if (e !== null)
|
|
15217
|
-
return [pow(coef, e), ce.
|
|
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.
|
|
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()
|
|
15252
|
-
return [[n.
|
|
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 (
|
|
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
|
|
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
|
-
|
|
15308
|
-
|
|
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
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
:
|
|
15314
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
15373
|
-
* `
|
|
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
|
|
15376
|
-
|
|
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
|
-
|
|
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) =>
|
|
15385
|
-
ops = flattenOps(ops, 'Add')
|
|
15386
|
-
return
|
|
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(
|
|
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
|
|
15397
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
15517
|
-
return ce.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15445
|
+
// 4/ proposition contains multiple free variables
|
|
15702
15446
|
// => add (lhs - rhs = 0) to assumptions DB
|
|
15703
15447
|
// Case 1
|
|
15704
|
-
const
|
|
15705
|
-
if (
|
|
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 (
|
|
15732
|
-
const lhs =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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 (
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
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 (
|
|
15847
|
-
ce.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
.
|
|
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 =
|
|
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]
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
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
|
-
|
|
16984
|
-
|
|
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.
|
|
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.
|
|
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
|
|
17332
|
-
|
|
17333
|
-
|
|
17334
|
-
|
|
17335
|
-
|
|
17336
|
-
|
|
17337
|
-
|
|
17338
|
-
return
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
18566
|
+
return ce.div(ops[0], ops[1], metadata);
|
|
18535
18567
|
if (head === 'Power')
|
|
18536
|
-
return ce.
|
|
18568
|
+
return ce.pow(ops[0], ops[1], metadata);
|
|
18537
18569
|
if (head === 'Square')
|
|
18538
|
-
return ce.
|
|
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.
|
|
18575
|
+
return ce.pow(op, ce._HALF, metadata);
|
|
18544
18576
|
}
|
|
18545
|
-
if (head === 'Pair')
|
|
18546
|
-
|
|
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
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
18909
|
-
(
|
|
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
|
-
|
|
19319
|
-
if (
|
|
19320
|
-
|
|
19321
|
-
|
|
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 (
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
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
|
-
|
|
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
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
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
|
|
19490
|
-
return ce.number(n
|
|
19491
|
-
if (n
|
|
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] = [
|
|
19637
|
-
if (n
|
|
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
|
-
|
|
19643
|
-
|
|
19698
|
+
bigintValue(ce, ops[0]),
|
|
19699
|
+
bigintValue(ce, ops[1]),
|
|
19644
19700
|
];
|
|
19645
|
-
if (n
|
|
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
|
-
|
|
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 =
|
|
19792
|
-
|
|
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 +=
|
|
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.
|
|
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.
|
|
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')
|
|
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]
|
|
21241
|
-
return [-1, ce.number([n[0]
|
|
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(
|
|
21249
|
-
return
|
|
21250
|
-
return
|
|
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
|
|
21316
|
+
return ce.chop(bigFn?.(n) ?? fn(n.toNumber()));
|
|
21254
21317
|
if (isMachineRational(n)) {
|
|
21255
|
-
if (!bignumPreferred(
|
|
21256
|
-
return
|
|
21257
|
-
return
|
|
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
|
|
21262
|
-
return
|
|
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(
|
|
21328
|
+
if (!complexFn || !complexAllowed(ce))
|
|
21266
21329
|
return NaN;
|
|
21267
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
21350
|
-
|
|
21351
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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]
|
|
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.
|
|
21478
|
-
return ce.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
21609
|
-
if (d % 2 === 0 && bigBase
|
|
21643
|
+
const bigBase = asBigint(base);
|
|
21644
|
+
if (d % 2 === 0 && bigBase < 0 && !complexAllowed(ce))
|
|
21610
21645
|
return ce._NAN;
|
|
21611
|
-
const sign = bigBase.
|
|
21612
|
-
|
|
21613
|
-
|
|
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 (
|
|
21621
|
-
if (root
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
21719
|
-
const [nFactor, nRoot] = factorPower(
|
|
21720
|
-
const [dFactor, dRoot] = factorPower(
|
|
21721
|
-
if (n
|
|
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(
|
|
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(
|
|
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
|
|
22046
|
+
return ce.neg(op1);
|
|
22022
22047
|
if (op1.isOne)
|
|
22023
|
-
return ce.
|
|
22048
|
+
return ce.inv(op2);
|
|
22024
22049
|
if (op1.isNegativeOne)
|
|
22025
|
-
return
|
|
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.
|
|
22079
|
-
|
|
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
|
-
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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: (
|
|
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) =>
|
|
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]
|
|
22546
|
+
validateArgument(ce, args[0], 'ExtendedRealNumber'),
|
|
22491
22547
|
]);
|
|
22492
|
-
args =
|
|
22493
|
-
|
|
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
|
|
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]
|
|
22536
|
-
validateArgument(ce, args[1]
|
|
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 (
|
|
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) =>
|
|
22600
|
-
|
|
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) =>
|
|
22614
|
-
|
|
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
|
|
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
|
|
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'
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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 =
|
|
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 =
|
|
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.
|
|
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)
|
|
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
|
|
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}"
|
|
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]
|
|
26005
|
-
numericCostFunction(num[1]
|
|
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
|
-
|
|
26015
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
27653
|
-
|
|
27654
|
-
|
|
27655
|
-
|
|
27656
|
-
|
|
27657
|
-
|
|
27658
|
-
|
|
27659
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
27972
|
+
return canonicalPower(this, base, this._HALF, metadata);
|
|
27767
27973
|
}
|
|
27768
|
-
|
|
27974
|
+
pow(base, exponent, metadata) {
|
|
27769
27975
|
// Short path. Note that are arguments are **not** validated.
|
|
27770
|
-
|
|
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]
|
|
27786
|
-
e = exponent[0]
|
|
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([
|
|
27798
|
-
if (isRational(r))
|
|
27799
|
-
return this.number(
|
|
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
|
|
27804
|
-
this._fn('Power', [base, exponent], metadata));
|
|
28011
|
+
return canonicalPower(this, base, exponent, metadata);
|
|
27805
28012
|
}
|
|
27806
|
-
|
|
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
|
-
|
|
27818
|
-
|
|
27819
|
-
return (
|
|
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
|
|
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
|
|
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
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
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
|
-
|
|
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.
|
|
28346
|
+
const version = '0.12.0';
|
|
28107
28347
|
|
|
28108
|
-
export { ComputeEngine,
|
|
28348
|
+
export { ComputeEngine, isEnvironmentEntry, isFunctionEntry, isInfixEntry, isMatchfixEntry, isPostfixEntry, isPrefixEntry, isSymbolEntry, version };
|