nerdamer 1.1.13 → 2.0.0-rc.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/LICENSE.md +184 -0
- package/README.md +293 -312
- package/dist/bundle.js +2 -0
- package/dist/bundle.js.LICENSE.txt +7 -0
- package/dist/parser.js +2 -0
- package/dist/parser.js.LICENSE.txt +7 -0
- package/docs-data/parser-functions.json +1960 -0
- package/index.d.ts +10 -426
- package/output/algebra/adapters.d.ts +33 -0
- package/output/algebra/adapters.js +109 -0
- package/output/algebra/algorithms/arith.d.ts +84 -0
- package/output/algebra/algorithms/arith.js +323 -0
- package/output/algebra/algorithms/factor.d.ts +3 -0
- package/output/algebra/algorithms/factor.js +5 -0
- package/output/algebra/algorithms/factorMultivariate.d.ts +23 -0
- package/output/algebra/algorithms/factorMultivariate.js +2393 -0
- package/output/algebra/algorithms/factorUnivariate.d.ts +72 -0
- package/output/algebra/algorithms/factorUnivariate.js +1072 -0
- package/output/algebra/algorithms/gcd.d.ts +22 -0
- package/output/algebra/algorithms/gcd.js +690 -0
- package/output/algebra/algorithms/groebnerBase.d.ts +157 -0
- package/output/algebra/algorithms/groebnerBase.js +1166 -0
- package/output/algebra/algorithms/multiPoly/MultiPoly.d.ts +137 -0
- package/output/algebra/algorithms/multiPoly/MultiPoly.js +346 -0
- package/output/algebra/algorithms/poly.d.ts +228 -0
- package/output/algebra/algorithms/poly.js +1299 -0
- package/output/algebra/algorithms/rational.d.ts +17 -0
- package/output/algebra/algorithms/rational.js +115 -0
- package/output/algebra/factor/factor.d.ts +62 -0
- package/output/algebra/factor/factor.js +158 -0
- package/output/algebra/gcd/gcd.d.ts +29 -0
- package/output/algebra/gcd/gcd.js +141 -0
- package/output/algebra/groebner.d.ts +21 -0
- package/output/algebra/groebner.js +42 -0
- package/output/algebra/partfrac.d.ts +19 -0
- package/output/algebra/partfrac.js +398 -0
- package/output/algebra/polynomialize.d.ts +20 -0
- package/output/algebra/polynomialize.js +24 -0
- package/output/algebra/simplify/complexsimp.d.ts +7 -0
- package/output/algebra/simplify/complexsimp.js +24 -0
- package/output/algebra/simplify/factorCommon.d.ts +13 -0
- package/output/algebra/simplify/factorCommon.js +136 -0
- package/output/algebra/simplify/funcsimp.d.ts +26 -0
- package/output/algebra/simplify/funcsimp.js +633 -0
- package/output/algebra/simplify/invtrigrewrite.d.ts +10 -0
- package/output/algebra/simplify/invtrigrewrite.js +96 -0
- package/output/algebra/simplify/ratsimp.d.ts +21 -0
- package/output/algebra/simplify/ratsimp.js +140 -0
- package/output/algebra/simplify/simplify.d.ts +25 -0
- package/output/algebra/simplify/simplify.js +228 -0
- package/output/algebra/simplify/trigreduce.d.ts +38 -0
- package/output/algebra/simplify/trigreduce.js +424 -0
- package/output/algebra/simplify/trigrewrite.d.ts +17 -0
- package/output/algebra/simplify/trigrewrite.js +156 -0
- package/output/algebra/simplify/trigsimp.d.ts +11 -0
- package/output/algebra/simplify/trigsimp.js +369 -0
- package/output/algebra/simplify/utils.d.ts +32 -0
- package/output/algebra/simplify/utils.js +56 -0
- package/output/algebra/utils.d.ts +32 -0
- package/output/algebra/utils.js +48 -0
- package/output/api/advanced.d.ts +7 -0
- package/output/api/advanced.js +18 -0
- package/output/api/algebra.d.ts +17 -0
- package/output/api/algebra.js +32 -0
- package/output/api/assumptions.d.ts +4 -0
- package/output/api/assumptions.js +11 -0
- package/output/api/calculus.d.ts +9 -0
- package/output/api/calculus.js +21 -0
- package/output/api/core.d.ts +17 -0
- package/output/api/core.js +41 -0
- package/output/api/debug.d.ts +16 -0
- package/output/api/debug.js +23 -0
- package/output/api/parser.d.ts +85 -0
- package/output/api/parser.js +16 -0
- package/output/api/solve.d.ts +10 -0
- package/output/api/solve.js +16 -0
- package/output/api/structures.d.ts +10 -0
- package/output/api/structures.js +22 -0
- package/output/calculus/derivative/diff.d.ts +30 -0
- package/output/calculus/derivative/diff.js +238 -0
- package/output/calculus/fresnel.d.ts +16 -0
- package/output/calculus/fresnel.js +39 -0
- package/output/calculus/integrate/byParts.d.ts +18 -0
- package/output/calculus/integrate/byParts.js +339 -0
- package/output/calculus/integrate/bySubstitution.d.ts +135 -0
- package/output/calculus/integrate/bySubstitution.js +411 -0
- package/output/calculus/integrate/integrate.d.ts +38 -0
- package/output/calculus/integrate/integrate.js +652 -0
- package/output/calculus/integrate/integrationTable.d.ts +2 -0
- package/output/calculus/integrate/integrationTable.js +914 -0
- package/output/calculus/integrate/utils.d.ts +28 -0
- package/output/calculus/integrate/utils.js +96 -0
- package/output/calculus/laplace/ilaplace.d.ts +31 -0
- package/output/calculus/laplace/ilaplace.js +192 -0
- package/output/calculus/laplace/ilaplaceTable.d.ts +2 -0
- package/output/calculus/laplace/ilaplaceTable.js +200 -0
- package/output/calculus/laplace/laplace.d.ts +22 -0
- package/output/calculus/laplace/laplace.js +109 -0
- package/output/calculus/laplace/laplaceTable.d.ts +2 -0
- package/output/calculus/laplace/laplaceTable.js +187 -0
- package/output/calculus/limit/limit.d.ts +35 -0
- package/output/calculus/limit/limit.js +1183 -0
- package/output/calculus/limit/limitsTable.d.ts +2 -0
- package/output/calculus/limit/limitsTable.js +7 -0
- package/output/core/Settings.d.ts +32 -0
- package/output/core/Settings.js +68 -0
- package/output/core/adapters.d.ts +18 -0
- package/output/core/adapters.js +40 -0
- package/output/core/classes/assumption/Assumption.d.ts +218 -0
- package/output/core/classes/assumption/Assumption.js +605 -0
- package/output/core/classes/assumption/assertiveFunctions.d.ts +6 -0
- package/output/core/classes/assumption/assertiveFunctions.js +64 -0
- package/output/core/classes/assumption/assume.d.ts +90 -0
- package/output/core/classes/assumption/assume.js +145 -0
- package/output/core/classes/collection/Collection.d.ts +107 -0
- package/output/core/classes/collection/Collection.js +231 -0
- package/output/core/classes/complex/Complex.d.ts +99 -0
- package/output/core/classes/complex/Complex.js +135 -0
- package/output/core/classes/decimalSet/DecimalSet.d.ts +226 -0
- package/output/core/classes/decimalSet/DecimalSet.js +453 -0
- package/output/core/classes/dictionary/Dictionary.d.ts +102 -0
- package/output/core/classes/dictionary/Dictionary.js +213 -0
- package/output/core/classes/equation/Equation.d.ts +256 -0
- package/output/core/classes/equation/Equation.js +350 -0
- package/output/core/classes/expression/CoeffObject.d.ts +100 -0
- package/output/core/classes/expression/CoeffObject.js +198 -0
- package/output/core/classes/expression/Expression.d.ts +1674 -0
- package/output/core/classes/expression/Expression.js +2332 -0
- package/output/core/classes/expression/analysis.d.ts +25 -0
- package/output/core/classes/expression/analysis.js +218 -0
- package/output/core/classes/expression/collect.d.ts +9 -0
- package/output/core/classes/expression/collect.js +49 -0
- package/output/core/classes/expression/format.d.ts +45 -0
- package/output/core/classes/expression/format.js +217 -0
- package/output/core/classes/expression/products.d.ts +23 -0
- package/output/core/classes/expression/products.js +115 -0
- package/output/core/classes/expression/shortcuts.d.ts +35 -0
- package/output/core/classes/expression/shortcuts.js +129 -0
- package/output/core/classes/expression/traversal.d.ts +31 -0
- package/output/core/classes/expression/traversal.js +216 -0
- package/output/core/classes/expression/trig.d.ts +25 -0
- package/output/core/classes/expression/trig.js +40 -0
- package/output/core/classes/expression/utils.d.ts +52 -0
- package/output/core/classes/expression/utils.js +189 -0
- package/output/core/classes/lookupTable/LookupTable.d.ts +12 -0
- package/output/core/classes/lookupTable/LookupTable.js +55 -0
- package/output/core/classes/matrix/Matrix.d.ts +227 -0
- package/output/core/classes/matrix/Matrix.js +889 -0
- package/output/core/classes/matrix/Sylvester.d.ts +11 -0
- package/output/core/classes/matrix/Sylvester.js +54 -0
- package/output/core/classes/matrix/functions.d.ts +26 -0
- package/output/core/classes/matrix/functions.js +52 -0
- package/output/core/classes/matrix/utils.d.ts +9 -0
- package/output/core/classes/matrix/utils.js +22 -0
- package/output/core/classes/parser/Parser.d.ts +480 -0
- package/output/core/classes/parser/Parser.js +1812 -0
- package/output/core/classes/parser/Token.d.ts +58 -0
- package/output/core/classes/parser/Token.js +137 -0
- package/output/core/classes/parser/constants.d.ts +160 -0
- package/output/core/classes/parser/constants.js +227 -0
- package/output/core/classes/parser/helpers.d.ts +21 -0
- package/output/core/classes/parser/helpers.js +42 -0
- package/output/core/classes/parser/operations/add.d.ts +15 -0
- package/output/core/classes/parser/operations/add.js +238 -0
- package/output/core/classes/parser/operations/comma.d.ts +7 -0
- package/output/core/classes/parser/operations/comma.js +11 -0
- package/output/core/classes/parser/operations/compare.d.ts +21 -0
- package/output/core/classes/parser/operations/compare.js +461 -0
- package/output/core/classes/parser/operations/divide.d.ts +2 -0
- package/output/core/classes/parser/operations/divide.js +72 -0
- package/output/core/classes/parser/operations/functions.d.ts +46 -0
- package/output/core/classes/parser/operations/functions.js +430 -0
- package/output/core/classes/parser/operations/multiply.d.ts +11 -0
- package/output/core/classes/parser/operations/multiply.js +321 -0
- package/output/core/classes/parser/operations/power.d.ts +48 -0
- package/output/core/classes/parser/operations/power.js +673 -0
- package/output/core/classes/parser/operations/subtract.d.ts +3 -0
- package/output/core/classes/parser/operations/subtract.js +35 -0
- package/output/core/classes/parser/preprocess.d.ts +14 -0
- package/output/core/classes/parser/preprocess.js +259 -0
- package/output/core/classes/parser/scripting/controlFlow.d.ts +121 -0
- package/output/core/classes/parser/scripting/controlFlow.js +355 -0
- package/output/core/classes/parser/scripting/evaluate.d.ts +10 -0
- package/output/core/classes/parser/scripting/evaluate.js +112 -0
- package/output/core/classes/parser/scripting/functions.d.ts +14 -0
- package/output/core/classes/parser/scripting/functions.js +59 -0
- package/output/core/classes/parser/scripting/scope.d.ts +35 -0
- package/output/core/classes/parser/scripting/scope.js +155 -0
- package/output/core/classes/parser/types.d.ts +125 -0
- package/output/core/classes/parser/types.js +2 -0
- package/output/core/classes/parser/wrappers/IndexedReference.d.ts +31 -0
- package/output/core/classes/parser/wrappers/IndexedReference.js +68 -0
- package/output/core/classes/parser/wrappers/KeyValuePair.d.ts +24 -0
- package/output/core/classes/parser/wrappers/KeyValuePair.js +51 -0
- package/output/core/classes/polynomial/Polynomial.d.ts +500 -0
- package/output/core/classes/polynomial/Polynomial.js +1149 -0
- package/output/core/classes/polynomial/Term.d.ts +222 -0
- package/output/core/classes/polynomial/Term.js +430 -0
- package/output/core/classes/polynomial/functions.d.ts +69 -0
- package/output/core/classes/polynomial/functions.js +126 -0
- package/output/core/classes/polynomial/utils.d.ts +62 -0
- package/output/core/classes/polynomial/utils.js +206 -0
- package/output/core/classes/rational/Rational.d.ts +504 -0
- package/output/core/classes/rational/Rational.js +779 -0
- package/output/core/classes/seq/SEQ.d.ts +13 -0
- package/output/core/classes/seq/SEQ.js +137 -0
- package/output/core/classes/valuesSet/ValuesSet.d.ts +179 -0
- package/output/core/classes/valuesSet/ValuesSet.js +381 -0
- package/output/core/classes/vector/Vector.d.ts +222 -0
- package/output/core/classes/vector/Vector.js +497 -0
- package/output/core/classes/vector/functions.d.ts +28 -0
- package/output/core/classes/vector/functions.js +47 -0
- package/output/core/common/classes/MathematicalAggregate.d.ts +53 -0
- package/output/core/common/classes/MathematicalAggregate.js +140 -0
- package/output/core/common/classes/Scope.d.ts +62 -0
- package/output/core/common/classes/Scope.js +122 -0
- package/output/core/common/classes/StructuredEntity.d.ts +55 -0
- package/output/core/common/classes/StructuredEntity.js +151 -0
- package/output/core/common/common.d.ts +63 -0
- package/output/core/common/common.js +279 -0
- package/output/core/common/functions/functions.d.ts +14 -0
- package/output/core/common/functions/functions.js +33 -0
- package/output/core/common/functions/structuredEntityUtils.d.ts +27 -0
- package/output/core/common/functions/structuredEntityUtils.js +40 -0
- package/output/core/converters/BaseConverter.d.ts +97 -0
- package/output/core/converters/BaseConverter.js +405 -0
- package/output/core/converters/Converter.d.ts +111 -0
- package/output/core/converters/Converter.js +802 -0
- package/output/core/converters/Pattern.d.ts +71 -0
- package/output/core/converters/Pattern.js +302 -0
- package/output/core/dispatch.d.ts +34 -0
- package/output/core/dispatch.js +304 -0
- package/output/core/errors.d.ts +531 -0
- package/output/core/errors.js +621 -0
- package/output/core/functions/bigint/bigint.d.ts +121 -0
- package/output/core/functions/bigint/bigint.js +392 -0
- package/output/core/functions/bigint/primeFactor.d.ts +49 -0
- package/output/core/functions/bigint/primeFactor.js +267 -0
- package/output/core/functions/bigint/primes.d.ts +1 -0
- package/output/core/functions/bigint/primes.js +11 -0
- package/output/core/functions/build/definitions.d.ts +12 -0
- package/output/core/functions/build/definitions.js +133 -0
- package/output/core/functions/build/index.d.ts +32 -0
- package/output/core/functions/build/index.js +136 -0
- package/output/core/functions/complex.d.ts +90 -0
- package/output/core/functions/complex.js +482 -0
- package/output/core/functions/decimal.d.ts +24 -0
- package/output/core/functions/decimal.js +664 -0
- package/output/core/functions/expand/expand.d.ts +42 -0
- package/output/core/functions/expand/expand.js +376 -0
- package/output/core/functions/fresnelNumeric.d.ts +12 -0
- package/output/core/functions/fresnelNumeric.js +123 -0
- package/output/core/functions/numeric.d.ts +140 -0
- package/output/core/functions/numeric.js +790 -0
- package/output/core/functions/rationalNormalization.d.ts +11 -0
- package/output/core/functions/rationalNormalization.js +117 -0
- package/output/core/functions/setFunction.d.ts +19 -0
- package/output/core/functions/setFunction.js +75 -0
- package/output/core/functions/string.d.ts +26 -0
- package/output/core/functions/string.js +101 -0
- package/output/core/functions/subst.d.ts +90 -0
- package/output/core/functions/subst.js +498 -0
- package/output/core/functions/utils.d.ts +24 -0
- package/output/core/functions/utils.js +51 -0
- package/output/core/types.d.ts +48 -0
- package/output/core/types.js +2 -0
- package/output/index.d.ts +339 -0
- package/output/index.js +715 -0
- package/output/math/defint/defintDecimal.d.ts +37 -0
- package/output/math/defint/defintDecimal.js +320 -0
- package/output/math/defint/defintNative.d.ts +57 -0
- package/output/math/defint/defintNative.js +292 -0
- package/output/math/geometry.d.ts +8 -0
- package/output/math/geometry.js +32 -0
- package/output/math/math.d.ts +655 -0
- package/output/math/math.js +1811 -0
- package/output/math/trig.d.ts +518 -0
- package/output/math/trig.js +1444 -0
- package/output/math/trunc.d.ts +19 -0
- package/output/math/trunc.js +36 -0
- package/output/math/utils.d.ts +60 -0
- package/output/math/utils.js +180 -0
- package/output/solve/classes/DecimalMatrix.d.ts +17 -0
- package/output/solve/classes/DecimalMatrix.js +86 -0
- package/output/solve/classes/FunctionSolver.d.ts +135 -0
- package/output/solve/classes/FunctionSolver.js +433 -0
- package/output/solve/classes/MultivariateSolver.d.ts +84 -0
- package/output/solve/classes/MultivariateSolver.js +240 -0
- package/output/solve/classes/PolynomialSolver.d.ts +85 -0
- package/output/solve/classes/PolynomialSolver.js +292 -0
- package/output/solve/classes/SolutionSet.d.ts +153 -0
- package/output/solve/classes/SolutionSet.js +357 -0
- package/output/solve/classes/Solver.d.ts +17 -0
- package/output/solve/classes/Solver.js +132 -0
- package/output/solve/classes/SymbolicSolver.d.ts +74 -0
- package/output/solve/classes/SymbolicSolver.js +601 -0
- package/output/solve/linsolve.d.ts +51 -0
- package/output/solve/linsolve.js +225 -0
- package/output/solve/solve.d.ts +34 -0
- package/output/solve/solve.js +260 -0
- package/output/solve/solveSystem.d.ts +31 -0
- package/output/solve/solveSystem.js +290 -0
- package/output/solve/utils.d.ts +9 -0
- package/output/solve/utils.js +62 -0
- package/output/utils/array.d.ts +42 -0
- package/output/utils/array.js +98 -0
- package/output/utils/debug.d.ts +131 -0
- package/output/utils/debug.js +295 -0
- package/output/utils/decimal.d.ts +3 -0
- package/output/utils/decimal.js +16 -0
- package/output/utils/numeric.d.ts +14 -0
- package/output/utils/numeric.js +51 -0
- package/output/utils/object.d.ts +26 -0
- package/output/utils/object.js +56 -0
- package/package.json +213 -57
- package/.travis.yml +0 -3
- package/Algebra.js +0 -4568
- package/BREAKING_CHANGES.md +0 -5
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -14
- package/Calculus.js +0 -2675
- package/Extra.js +0 -622
- package/Solve.js +0 -1783
- package/all.js +0 -16
- package/all.min.js +0 -1
- package/gulpfile.js +0 -16
- package/index.html +0 -158
- package/license.txt +0 -19
- package/nerdamer.core.js +0 -12510
- package/spec/LaTeX.spec.js +0 -302
- package/spec/TeXConvert.spec.js +0 -8
- package/spec/algebra.spec.js +0 -287
- package/spec/basic_parser.spec.js +0 -134
- package/spec/build.spec.js +0 -131
- package/spec/calculus.spec.js +0 -183
- package/spec/core.spec.js +0 -2970
- package/spec/extra.spec.js +0 -54
- package/spec/solve.spec.js +0 -126
- package/spec/support/jasmine.json +0 -11
- package/spec/support/utils.js +0 -42
- package/spec/text.spec.js +0 -132
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.simplifyFactorials = simplifyFactorials;
|
|
4
|
+
exports.simplifyLogs = simplifyLogs;
|
|
5
|
+
const Expression_1 = require("../../core/classes/expression/Expression");
|
|
6
|
+
const shortcuts_1 = require("../../core/classes/expression/shortcuts");
|
|
7
|
+
const constants_1 = require("../../core/classes/parser/constants");
|
|
8
|
+
const constants_2 = require("../../core/classes/parser/constants");
|
|
9
|
+
const MAX_FACTORIAL_ARGUMENT_DIFFERENCE = 100;
|
|
10
|
+
/**
|
|
11
|
+
* Extracts factorial terms from a product expression.
|
|
12
|
+
* Returns the factorial info and the remaining non-factorial parts.
|
|
13
|
+
*/
|
|
14
|
+
function extractFactorials(x) {
|
|
15
|
+
const factorials = [];
|
|
16
|
+
const rest = [];
|
|
17
|
+
if (!x.isProduct()) {
|
|
18
|
+
if (x.isFunction(constants_1.FACTORIAL)) {
|
|
19
|
+
factorials.push({
|
|
20
|
+
arg: x.getArguments()[0],
|
|
21
|
+
power: x.getPower(),
|
|
22
|
+
multiplier: Expression_1.Expression.fromRational(x.getMultiplier()),
|
|
23
|
+
index: 0,
|
|
24
|
+
});
|
|
25
|
+
return { factorials, rest };
|
|
26
|
+
}
|
|
27
|
+
return { factorials: [], rest: [x] };
|
|
28
|
+
}
|
|
29
|
+
let idx = 0;
|
|
30
|
+
for (const element of x.elementsArray()) {
|
|
31
|
+
if (element.isFunction(constants_1.FACTORIAL)) {
|
|
32
|
+
factorials.push({
|
|
33
|
+
arg: element.getArguments()[0],
|
|
34
|
+
power: element.getPower(),
|
|
35
|
+
multiplier: Expression_1.Expression.fromRational(element.getMultiplier()),
|
|
36
|
+
index: idx,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
rest.push(element);
|
|
41
|
+
}
|
|
42
|
+
idx++;
|
|
43
|
+
}
|
|
44
|
+
return { factorials, rest };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Tries to compute the integer difference between two factorial arguments.
|
|
48
|
+
* If argA - argB = k (a positive integer), returns k.
|
|
49
|
+
* If argB - argA = k (a positive integer), returns -k.
|
|
50
|
+
* Otherwise returns null.
|
|
51
|
+
*
|
|
52
|
+
* Examples:
|
|
53
|
+
* diffFactorialArgs((n+2), n) → 2
|
|
54
|
+
* diffFactorialArgs(n, (n+3)) → -3
|
|
55
|
+
* diffFactorialArgs((2n+4), (2n)) → 4
|
|
56
|
+
* diffFactorialArgs(n, m) → null (symbolic, can't determine)
|
|
57
|
+
*/
|
|
58
|
+
function diffFactorialArgs(argA, argB) {
|
|
59
|
+
const diff = argA.minus(argB);
|
|
60
|
+
// The difference should be a pure integer (NUM type, integer multiplier)
|
|
61
|
+
if (!diff.isNUM()) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const rat = diff.getMultiplier();
|
|
65
|
+
if (!rat.isInteger()) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Get the integer value
|
|
69
|
+
const n = Number(rat.numerator);
|
|
70
|
+
// Guard against excessively large differences to avoid generating huge products
|
|
71
|
+
if (Math.abs(n) > MAX_FACTORIAL_ARGUMENT_DIFFERENCE) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return n;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Builds the product (base+1)(base+2)...(base+k) for positive k,
|
|
78
|
+
* or the product (base)(base-1)...(base-k+1) for negative k.
|
|
79
|
+
*
|
|
80
|
+
* For k > 0: returns (base+1)·(base+2)·...·(base+k)
|
|
81
|
+
* For k < 0: returns (base)·(base-1)·...·(base+k+1) [i.e., |k| terms going down]
|
|
82
|
+
*/
|
|
83
|
+
function consecutiveProduct(base, k) {
|
|
84
|
+
if (k === 0) {
|
|
85
|
+
return (0, shortcuts_1.one)();
|
|
86
|
+
}
|
|
87
|
+
let result = (0, shortcuts_1.one)();
|
|
88
|
+
if (k > 0) {
|
|
89
|
+
// (base+1)(base+2)...(base+k)
|
|
90
|
+
for (let i = 1; i <= k; i++) {
|
|
91
|
+
result = result.times(base.plus(Expression_1.Expression.Number(i)));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// k < 0: (base)(base-1)...(base+k+1) — that's |k| terms going down from base
|
|
96
|
+
const absK = -k;
|
|
97
|
+
for (let i = 0; i < absK; i++) {
|
|
98
|
+
result = result.times(base.minus(Expression_1.Expression.Number(i)));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Simplifies factorial ratios in a product expression.
|
|
105
|
+
*
|
|
106
|
+
* n! · (n+k)!^(-1) → 1 / ((n+1)(n+2)...(n+k)) when k > 0
|
|
107
|
+
* (n+k)! · n!^(-1) → (n+1)(n+2)...(n+k) when k > 0
|
|
108
|
+
*
|
|
109
|
+
* Works by finding pairs of factorials where one has power 1 and the other
|
|
110
|
+
* has power -1, computing their argument difference, and replacing with
|
|
111
|
+
* the product of consecutive integers.
|
|
112
|
+
*/
|
|
113
|
+
function simplifyFactorials(x) {
|
|
114
|
+
if (!x.isProduct()) {
|
|
115
|
+
return x;
|
|
116
|
+
}
|
|
117
|
+
const { factorials, rest } = extractFactorials(x);
|
|
118
|
+
// Need at least 2 factorials to simplify (one in num, one in den)
|
|
119
|
+
if (factorials.length < 2) {
|
|
120
|
+
return x;
|
|
121
|
+
}
|
|
122
|
+
// Separate into numerator factorials (power > 0) and denominator factorials (power < 0)
|
|
123
|
+
const numFacts = [];
|
|
124
|
+
const denFacts = [];
|
|
125
|
+
for (const f of factorials) {
|
|
126
|
+
if (f.power.sign() === 1 && f.power.isOne()) {
|
|
127
|
+
numFacts.push(f);
|
|
128
|
+
}
|
|
129
|
+
else if (f.power.sign() === -1) {
|
|
130
|
+
// Check it's exactly -1
|
|
131
|
+
const pRat = f.power.getMultiplier();
|
|
132
|
+
if (pRat.numerator === -1n && (pRat.denominator === 1n || pRat.denominator === -1n)) {
|
|
133
|
+
denFacts.push(f);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Non-unit power factorial — leave it alone
|
|
137
|
+
rest.push(f.multiplier.times(Expression_1.Expression.toFunction(constants_1.FACTORIAL, [f.arg]).pow(f.power)));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Power is not ±1. Leave it as-is.
|
|
142
|
+
rest.push(f.multiplier.times(Expression_1.Expression.toFunction(constants_1.FACTORIAL, [f.arg]).pow(f.power)));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const usedNum = new Set();
|
|
146
|
+
const usedDen = new Set();
|
|
147
|
+
const newFactors = [];
|
|
148
|
+
// Try to pair each numerator factorial with a denominator factorial
|
|
149
|
+
for (let i = 0; i < numFacts.length; i++) {
|
|
150
|
+
if (usedNum.has(i)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
for (let j = 0; j < denFacts.length; j++) {
|
|
154
|
+
if (usedDen.has(j)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const a = numFacts[i]; // e.g. n! in numerator
|
|
158
|
+
const b = denFacts[j]; // e.g. (n+k)! in denominator
|
|
159
|
+
// Compute diff = a.arg - b.arg
|
|
160
|
+
const diff = diffFactorialArgs(a.arg, b.arg);
|
|
161
|
+
if (diff !== null && diff !== 0) {
|
|
162
|
+
if (diff < 0) {
|
|
163
|
+
// a.arg < b.arg, so a.arg! / b.arg! = 1 / consecutive product
|
|
164
|
+
// e.g. n! / (n+3)! = 1 / ((n+1)(n+2)(n+3))
|
|
165
|
+
const prod = consecutiveProduct(a.arg, -diff);
|
|
166
|
+
newFactors.push(a.multiplier.times(b.multiplier).times(prod.invert()));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// a.arg > b.arg, so a.arg! / b.arg! = consecutive product
|
|
170
|
+
// e.g. (n+3)! / n! = (n+1)(n+2)(n+3)
|
|
171
|
+
const prod = consecutiveProduct(b.arg, diff);
|
|
172
|
+
newFactors.push(a.multiplier.times(b.multiplier).times(prod));
|
|
173
|
+
}
|
|
174
|
+
usedNum.add(i);
|
|
175
|
+
usedDen.add(j);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
else if (diff === 0) {
|
|
179
|
+
// Same argument: they cancel to 1 (times multipliers)
|
|
180
|
+
newFactors.push(a.multiplier.times(b.multiplier));
|
|
181
|
+
usedNum.add(i);
|
|
182
|
+
usedDen.add(j);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Reconstruct: remaining factorials + new factors + rest
|
|
188
|
+
let result = Expression_1.Expression.fromRational(x.getMultiplier());
|
|
189
|
+
// Unmatched numerator factorials
|
|
190
|
+
for (let i = 0; i < numFacts.length; i++) {
|
|
191
|
+
if (!usedNum.has(i)) {
|
|
192
|
+
const f = numFacts[i];
|
|
193
|
+
result = result.times(f.multiplier.times(Expression_1.Expression.toFunction(constants_1.FACTORIAL, [f.arg])));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Unmatched denominator factorials
|
|
197
|
+
for (let j = 0; j < denFacts.length; j++) {
|
|
198
|
+
if (!usedDen.has(j)) {
|
|
199
|
+
const f = denFacts[j];
|
|
200
|
+
result = result.times(f.multiplier.times(Expression_1.Expression.toFunction(constants_1.FACTORIAL, [f.arg]).invert()));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// New factors from cancellation
|
|
204
|
+
for (const factor of newFactors) {
|
|
205
|
+
result = result.times(factor);
|
|
206
|
+
}
|
|
207
|
+
// Other non-factorial elements
|
|
208
|
+
for (const element of rest) {
|
|
209
|
+
result = result.times(element);
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Extracts log(u) information from a sum term.
|
|
215
|
+
* Returns null if the term doesn't contain a log.
|
|
216
|
+
*
|
|
217
|
+
* Handles:
|
|
218
|
+
* log(u) → coeff = 1, arg = u
|
|
219
|
+
* 3*log(u) → coeff = 3, arg = u
|
|
220
|
+
* x*log(u) → coeff = x, arg = u
|
|
221
|
+
* log(u)^2 → null (not linear in log)
|
|
222
|
+
*/
|
|
223
|
+
function extractLogTerm(term) {
|
|
224
|
+
// Case 1: Direct log function with power 1
|
|
225
|
+
if (term.isFunction(constants_2.LOG) && term.isLinear()) {
|
|
226
|
+
const args = term.getArguments();
|
|
227
|
+
if (args.length === 0) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
coeff: Expression_1.Expression.fromRational(term.getMultiplier()),
|
|
232
|
+
arg: args[0],
|
|
233
|
+
original: term,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// Case 2: Product containing a log function
|
|
237
|
+
if (term.isProduct()) {
|
|
238
|
+
let logElement = null;
|
|
239
|
+
let logMultiplier = (0, shortcuts_1.one)();
|
|
240
|
+
const coeffParts = [];
|
|
241
|
+
for (const element of term.elementsArray()) {
|
|
242
|
+
if (!logElement && element.isFunction(constants_2.LOG) && element.isLinear()) {
|
|
243
|
+
logElement = element;
|
|
244
|
+
logMultiplier = Expression_1.Expression.fromRational(element.getMultiplier());
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
coeffParts.push(element);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (logElement) {
|
|
251
|
+
let coeff = Expression_1.Expression.fromRational(term.getMultiplier());
|
|
252
|
+
for (const part of coeffParts) {
|
|
253
|
+
coeff = coeff.times(part);
|
|
254
|
+
}
|
|
255
|
+
coeff = coeff.times(logMultiplier);
|
|
256
|
+
return {
|
|
257
|
+
coeff,
|
|
258
|
+
arg: logElement.getArguments()[0],
|
|
259
|
+
original: term,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Determines if a coefficient should be lifted into the log argument as a power.
|
|
267
|
+
*
|
|
268
|
+
* Integer coefficients: always lift (3·log(x) → log(x³))
|
|
269
|
+
* Rational coefficients: only lift when combining with other terms
|
|
270
|
+
* (standalone (1/2)·log(x) stays as-is)
|
|
271
|
+
*
|
|
272
|
+
* @param coeff The coefficient expression (must be NUM)
|
|
273
|
+
* @param isCombining Whether this term is being combined with other log terms
|
|
274
|
+
*/
|
|
275
|
+
function shouldLiftCoefficient(coeff, isCombining) {
|
|
276
|
+
if (!coeff.isNUM()) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const rat = coeff.getMultiplier();
|
|
280
|
+
// Coefficient of ±1: nothing to lift
|
|
281
|
+
if (rat.abs().isOne()) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const isInteger = rat.denominator === 1n || rat.denominator === -1n;
|
|
285
|
+
// Integer coefficients: always lift
|
|
286
|
+
if (isInteger) {
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
// Rational coefficients: only lift when combining with other terms
|
|
290
|
+
return isCombining;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Lifts a numeric coefficient into the log argument as a power.
|
|
294
|
+
*
|
|
295
|
+
* c·log(u) → log(u^c) for the magnitude
|
|
296
|
+
* Sign is tracked separately (positive → numerator, negative → denominator)
|
|
297
|
+
*
|
|
298
|
+
* Returns the new log argument with the coefficient absorbed as a power.
|
|
299
|
+
*/
|
|
300
|
+
function liftCoefficientIntoPower(arg, coeff) {
|
|
301
|
+
const absCoeff = coeff.abs();
|
|
302
|
+
return arg.pow(absCoeff);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Combines logarithmic terms in a sum expression when their arguments are
|
|
306
|
+
* provably positive real values. Unknown-sign logarithms are left unchanged
|
|
307
|
+
* so these real-domain identities do not cross principal complex branches.
|
|
308
|
+
*
|
|
309
|
+
* Power lifting + combination:
|
|
310
|
+
* log(x) + log(y) → log(x·y) [combine, coeff=1]
|
|
311
|
+
* 2·log(x) + 3·log(y) → log(x²·y³) [lift then combine]
|
|
312
|
+
* (1/2)·log(x) → (1/2)·log(x) [rational, standalone: keep]
|
|
313
|
+
* (1/2)·log(x) + (1/3)·log(y) → log(x^(1/2)·y^(1/3)) [rational, combining: lift]
|
|
314
|
+
* a·log(u) + a·log(v) → a·log(u·v) [symbolic, group by coeff]
|
|
315
|
+
* log(a) - log(b) → log(a/b) [sign handling]
|
|
316
|
+
*/
|
|
317
|
+
function simplifyLogs(x) {
|
|
318
|
+
if (!x.isSum()) {
|
|
319
|
+
return x;
|
|
320
|
+
}
|
|
321
|
+
// A sum expression may have an outer multiplier and power, e.g.
|
|
322
|
+
// (1/6)*(3*log(x)+2*log(y)) is stored as a SUM with multiplier 1/6.
|
|
323
|
+
// We need to preserve these when reconstructing.
|
|
324
|
+
const outerMultiplier = Expression_1.Expression.fromRational(x.getMultiplier());
|
|
325
|
+
const outerPower = x.getPower();
|
|
326
|
+
const logTerms = [];
|
|
327
|
+
const otherTerms = [];
|
|
328
|
+
for (const term of x.elementsArray()) {
|
|
329
|
+
const info = extractLogTerm(term);
|
|
330
|
+
if (info) {
|
|
331
|
+
logTerms.push(info);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
otherTerms.push(term);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Principal-log identities such as log(a)+log(b)=log(a*b) and
|
|
338
|
+
// c*log(a)=log(a^c) are only branch-safe when the log arguments are
|
|
339
|
+
// provably positive real values. Preserve unknown-sign terms unchanged.
|
|
340
|
+
const transformableLogTerms = logTerms.filter(t => !t.arg.isComplex() && t.arg.gt(0));
|
|
341
|
+
if (transformableLogTerms.length === 0) {
|
|
342
|
+
return x;
|
|
343
|
+
}
|
|
344
|
+
const preservedLogTerms = logTerms
|
|
345
|
+
.filter(t => !transformableLogTerms.includes(t))
|
|
346
|
+
.map(t => t.original);
|
|
347
|
+
const transformOtherTerms = [...otherTerms, ...preservedLogTerms];
|
|
348
|
+
// Split the branch-safe terms into numeric-coeff and symbolic-coeff groups.
|
|
349
|
+
const numericLogTerms = transformableLogTerms.filter(t => t.coeff.isNUM());
|
|
350
|
+
const symbolicLogTerms = transformableLogTerms.filter(t => !t.coeff.isNUM());
|
|
351
|
+
// Handle single numeric log term: only lift if integer coefficient
|
|
352
|
+
if (numericLogTerms.length === 1 && symbolicLogTerms.length === 0) {
|
|
353
|
+
const t = numericLogTerms[0];
|
|
354
|
+
if (shouldLiftCoefficient(t.coeff, false)) {
|
|
355
|
+
// Integer coeff, standalone: lift it
|
|
356
|
+
// e.g. 3·log(x) → log(x³)
|
|
357
|
+
const newArg = liftCoefficientIntoPower(t.arg, t.coeff);
|
|
358
|
+
const logExpr = Expression_1.Expression.toFunction(constants_2.LOG, [newArg]);
|
|
359
|
+
// Rebuild the sum with the lifted log term
|
|
360
|
+
let result = logExpr;
|
|
361
|
+
for (const other of transformOtherTerms) {
|
|
362
|
+
result = result.plus(other);
|
|
363
|
+
}
|
|
364
|
+
return applyOuterMultiplierAndPower(result, outerMultiplier, outerPower);
|
|
365
|
+
}
|
|
366
|
+
return x;
|
|
367
|
+
}
|
|
368
|
+
// Multiple numeric log terms: lift and combine
|
|
369
|
+
if (numericLogTerms.length >= 2) {
|
|
370
|
+
const combined = combineNumericLogTerms(numericLogTerms, transformOtherTerms, x);
|
|
371
|
+
if (combined !== x) {
|
|
372
|
+
let result = combined;
|
|
373
|
+
// If we also have symbolic terms, add them back
|
|
374
|
+
if (symbolicLogTerms.length > 0) {
|
|
375
|
+
for (const t of symbolicLogTerms) {
|
|
376
|
+
result = result.plus(t.original);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return applyOuterMultiplierAndPower(result, outerMultiplier, outerPower);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// For symbolic coefficients, try to group terms with equal coefficients
|
|
383
|
+
if (symbolicLogTerms.length >= 2) {
|
|
384
|
+
// Rebuild with any unhandled numeric terms
|
|
385
|
+
const remainingOther = [...transformOtherTerms];
|
|
386
|
+
for (const t of numericLogTerms) {
|
|
387
|
+
// Lift standalone integer coefficients even when symbolic terms exist
|
|
388
|
+
if (shouldLiftCoefficient(t.coeff, false)) {
|
|
389
|
+
const newArg = liftCoefficientIntoPower(t.arg, t.coeff);
|
|
390
|
+
remainingOther.push(Expression_1.Expression.toFunction(constants_2.LOG, [newArg]));
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
remainingOther.push(t.original);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const result = combineSymbolicLogTerms(symbolicLogTerms, remainingOther, x);
|
|
397
|
+
if (result !== x) {
|
|
398
|
+
return applyOuterMultiplierAndPower(result, outerMultiplier, outerPower);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return x;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Re-applies the outer multiplier and power that were on the original sum expression.
|
|
405
|
+
* Sums can carry an outer multiplier, e.g. (1/6)*(a+b) is a SUM with multiplier 1/6.
|
|
406
|
+
* After transforming the inner elements, we need to put these back.
|
|
407
|
+
*/
|
|
408
|
+
function applyOuterMultiplierAndPower(result, outerMultiplier, outerPower) {
|
|
409
|
+
if (!outerPower.isOne()) {
|
|
410
|
+
result = result.pow(outerPower);
|
|
411
|
+
}
|
|
412
|
+
if (!outerMultiplier.isOne()) {
|
|
413
|
+
result = result.times(outerMultiplier);
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Combines log terms that all have numeric (rational) coefficients
|
|
419
|
+
* using power lifting.
|
|
420
|
+
*
|
|
421
|
+
* Strategy: Lift each coefficient into the log argument as a power,
|
|
422
|
+
* then combine all into a single log term.
|
|
423
|
+
*
|
|
424
|
+
* Examples:
|
|
425
|
+
* log(x) + log(y) → log(x·y)
|
|
426
|
+
* 2·log(x) + 3·log(y) → log(x²·y³)
|
|
427
|
+
* log(x) - log(y) → log(x/y)
|
|
428
|
+
* 2·log(x) - 3·log(y) → log(x²/y³)
|
|
429
|
+
* (1/2)·log(x) + (1/3)·log(y) → log(x^(1/2)·y^(1/3))
|
|
430
|
+
* (1/2)·log(x) + (-1/2)·log(y) → (1/2)·log(x/y) [common factor preserved]
|
|
431
|
+
*/
|
|
432
|
+
function combineNumericLogTerms(logTerms, otherTerms, _original) {
|
|
433
|
+
// First, check if all terms share a common coefficient magnitude.
|
|
434
|
+
// If so, preserve that as an outer factor (cleaner output).
|
|
435
|
+
// e.g. (1/2)·log(x) + (-1/2)·log(y) → (1/2)·log(x/y)
|
|
436
|
+
// rather than log(x^(1/2) / y^(1/2))
|
|
437
|
+
const commonFactor = findCommonCoeffFactor(logTerms);
|
|
438
|
+
if (commonFactor && !commonFactor.isOne()) {
|
|
439
|
+
// All coefficients are multiples of commonFactor.
|
|
440
|
+
// Divide each coefficient by it, combine, then multiply back.
|
|
441
|
+
let innerArg = (0, shortcuts_1.one)();
|
|
442
|
+
for (const t of logTerms) {
|
|
443
|
+
const reducedCoeff = t.coeff.div(commonFactor);
|
|
444
|
+
const sign = reducedCoeff.sign();
|
|
445
|
+
const absReduced = reducedCoeff.abs();
|
|
446
|
+
// Lift the reduced coefficient if it's not 1
|
|
447
|
+
let termArg = t.arg;
|
|
448
|
+
if (!absReduced.isOne()) {
|
|
449
|
+
termArg = liftCoefficientIntoPower(t.arg, reducedCoeff);
|
|
450
|
+
}
|
|
451
|
+
if (sign >= 0) {
|
|
452
|
+
innerArg = innerArg.times(termArg);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
innerArg = innerArg.times(termArg.invert());
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const logExpr = Expression_1.Expression.toFunction(constants_2.LOG, [innerArg]);
|
|
459
|
+
const resultTerms = [...otherTerms, commonFactor.times(logExpr)];
|
|
460
|
+
if (resultTerms.length === 1) {
|
|
461
|
+
return resultTerms[0];
|
|
462
|
+
}
|
|
463
|
+
let result = (0, shortcuts_1.zero)();
|
|
464
|
+
for (const term of resultTerms) {
|
|
465
|
+
result = result.plus(term);
|
|
466
|
+
}
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
469
|
+
// No common factor (or it's 1): lift all coefficients into powers and combine
|
|
470
|
+
// e.g. 2·log(x) + 3·log(y) → log(x²·y³)
|
|
471
|
+
let innerArg = (0, shortcuts_1.one)();
|
|
472
|
+
for (const t of logTerms) {
|
|
473
|
+
const sign = t.coeff.sign();
|
|
474
|
+
const shouldLift = shouldLiftCoefficient(t.coeff, true);
|
|
475
|
+
let termArg = t.arg;
|
|
476
|
+
if (shouldLift) {
|
|
477
|
+
termArg = liftCoefficientIntoPower(t.arg, t.coeff);
|
|
478
|
+
}
|
|
479
|
+
if (sign >= 0) {
|
|
480
|
+
innerArg = innerArg.times(termArg);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
innerArg = innerArg.times(termArg.invert());
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const logExpr = Expression_1.Expression.toFunction(constants_2.LOG, [innerArg]);
|
|
487
|
+
const resultTerms = [...otherTerms, logExpr];
|
|
488
|
+
if (resultTerms.length === 1) {
|
|
489
|
+
return resultTerms[0];
|
|
490
|
+
}
|
|
491
|
+
let result = (0, shortcuts_1.zero)();
|
|
492
|
+
for (const term of resultTerms) {
|
|
493
|
+
result = result.plus(term);
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Finds the greatest common factor among all log term coefficients.
|
|
499
|
+
* Returns the GCD if all coefficients are integer multiples of it,
|
|
500
|
+
* or null if no useful common factor exists.
|
|
501
|
+
*
|
|
502
|
+
* For example:
|
|
503
|
+
* coeffs [2, -2] → 2
|
|
504
|
+
* coeffs [1/2, -1/2] → 1/2
|
|
505
|
+
* coeffs [2, 4, -6] → 2
|
|
506
|
+
* coeffs [2, 3] → 1 (trivial)
|
|
507
|
+
* coeffs [1/2, 1/3] → 1/6 (but reduced coeffs become 3, 2 — not simpler)
|
|
508
|
+
*
|
|
509
|
+
* We only return a common factor if it results in at least some coefficients
|
|
510
|
+
* becoming ±1 after division (i.e., the factor is useful for simplification).
|
|
511
|
+
*/
|
|
512
|
+
function findCommonCoeffFactor(logTerms) {
|
|
513
|
+
if (logTerms.length < 2) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
// Extract the rational values
|
|
517
|
+
const rats = logTerms.map(t => t.coeff.getMultiplier());
|
|
518
|
+
// Compute GCD of absolute numerators and LCM of denominators
|
|
519
|
+
let gcdNum = rats[0].numerator < 0n ? -rats[0].numerator : rats[0].numerator;
|
|
520
|
+
let lcmDen = rats[0].denominator < 0n ? -rats[0].denominator : rats[0].denominator;
|
|
521
|
+
for (let i = 1; i < rats.length; i++) {
|
|
522
|
+
const num = rats[i].numerator < 0n ? -rats[i].numerator : rats[i].numerator;
|
|
523
|
+
const den = rats[i].denominator < 0n ? -rats[i].denominator : rats[i].denominator;
|
|
524
|
+
gcdNum = bigintGCD(gcdNum, num);
|
|
525
|
+
lcmDen = bigintLCM(lcmDen, den);
|
|
526
|
+
}
|
|
527
|
+
if (gcdNum === 0n) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
// The common factor is gcdNum / lcmDen
|
|
531
|
+
// But this is only useful if it's not 1/1
|
|
532
|
+
if (gcdNum === 1n && lcmDen === 1n) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
// Check that after dividing by common factor, at least one coefficient becomes ±1
|
|
536
|
+
// This ensures the common factor actually simplifies the expression
|
|
537
|
+
const commonRat = `${gcdNum}/${lcmDen}`;
|
|
538
|
+
const commonExpr = Expression_1.Expression.create(commonRat);
|
|
539
|
+
let hasUnitCoeff = false;
|
|
540
|
+
for (const t of logTerms) {
|
|
541
|
+
const reduced = t.coeff.div(commonExpr);
|
|
542
|
+
if (reduced.abs().isOne()) {
|
|
543
|
+
hasUnitCoeff = true;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
// If no coefficient reduces to ±1, the common factor isn't very helpful
|
|
548
|
+
// unless all reduced coefficients are integers (cleaner than rationals)
|
|
549
|
+
if (!hasUnitCoeff) {
|
|
550
|
+
const allReducedInteger = logTerms.every(t => {
|
|
551
|
+
const reduced = t.coeff.div(commonExpr);
|
|
552
|
+
const rat = reduced.getMultiplier();
|
|
553
|
+
return rat.denominator === 1n || rat.denominator === -1n;
|
|
554
|
+
});
|
|
555
|
+
if (!allReducedInteger) {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return commonExpr;
|
|
560
|
+
}
|
|
561
|
+
/** BigInt GCD helper */
|
|
562
|
+
function bigintGCD(a, b) {
|
|
563
|
+
a = a < 0n ? -a : a;
|
|
564
|
+
b = b < 0n ? -b : b;
|
|
565
|
+
while (b > 0n) {
|
|
566
|
+
[a, b] = [b, a % b];
|
|
567
|
+
}
|
|
568
|
+
return a;
|
|
569
|
+
}
|
|
570
|
+
/** BigInt LCM helper */
|
|
571
|
+
function bigintLCM(a, b) {
|
|
572
|
+
a = a < 0n ? -a : a;
|
|
573
|
+
b = b < 0n ? -b : b;
|
|
574
|
+
if (a === 0n || b === 0n) {
|
|
575
|
+
return 0n;
|
|
576
|
+
}
|
|
577
|
+
return (a / bigintGCD(a, b)) * b;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Combines log terms with symbolic coefficients by grouping equal coefficients.
|
|
581
|
+
*
|
|
582
|
+
* Example:
|
|
583
|
+
* x·log(a) + x·log(b) → x·log(a·b)
|
|
584
|
+
*/
|
|
585
|
+
function combineSymbolicLogTerms(logTerms, otherTerms, original) {
|
|
586
|
+
const groups = new Map();
|
|
587
|
+
for (const t of logTerms) {
|
|
588
|
+
// Use abs of coeff as the group key
|
|
589
|
+
const absKey = t.coeff.abs().text();
|
|
590
|
+
const group = groups.get(absKey);
|
|
591
|
+
if (group) {
|
|
592
|
+
group.push(t);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
groups.set(absKey, [t]);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const resultTerms = [...otherTerms];
|
|
599
|
+
let combined = false;
|
|
600
|
+
for (const [, group] of groups) {
|
|
601
|
+
if (group.length < 2) {
|
|
602
|
+
for (const t of group) {
|
|
603
|
+
resultTerms.push(t.original);
|
|
604
|
+
}
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
combined = true;
|
|
608
|
+
// Separate by sign
|
|
609
|
+
const positive = group.filter(t => t.coeff.sign() >= 0);
|
|
610
|
+
const negative = group.filter(t => t.coeff.sign() < 0);
|
|
611
|
+
let innerArg = (0, shortcuts_1.one)();
|
|
612
|
+
for (const t of positive) {
|
|
613
|
+
innerArg = innerArg.times(t.arg);
|
|
614
|
+
}
|
|
615
|
+
for (const t of negative) {
|
|
616
|
+
innerArg = innerArg.times(t.arg.invert());
|
|
617
|
+
}
|
|
618
|
+
const commonCoeff = positive.length > 0 ? positive[0].coeff : negative[0].coeff.neg();
|
|
619
|
+
const logExpr = Expression_1.Expression.toFunction(constants_2.LOG, [innerArg]);
|
|
620
|
+
resultTerms.push(commonCoeff.times(logExpr));
|
|
621
|
+
}
|
|
622
|
+
if (!combined) {
|
|
623
|
+
return original;
|
|
624
|
+
}
|
|
625
|
+
if (resultTerms.length === 1) {
|
|
626
|
+
return resultTerms[0];
|
|
627
|
+
}
|
|
628
|
+
let result = (0, shortcuts_1.zero)();
|
|
629
|
+
for (const term of resultTerms) {
|
|
630
|
+
result = result.plus(term);
|
|
631
|
+
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Expression } from '../../core/classes/expression/Expression';
|
|
2
|
+
/**
|
|
3
|
+
* Determines whether an expression contains any asec, acsc, or acot functions.
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasDerivedInverseTrig(x: Expression): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Recursively rewrites all asec, acsc, acot in an expression tree to acos, asin, atan.
|
|
8
|
+
* Descends into sums, products, and function arguments.
|
|
9
|
+
*/
|
|
10
|
+
export declare function rewriteInverseTrig(x: Expression): Expression;
|