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