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,2332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Expression = void 0;
|
|
7
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
|
+
const math_1 = require("../../../math/math");
|
|
9
|
+
const common_1 = require("../../common/common");
|
|
10
|
+
const errors_1 = require("../../errors");
|
|
11
|
+
const build_1 = require("../../functions/build");
|
|
12
|
+
const complex_1 = require("../../functions/complex");
|
|
13
|
+
const expand_1 = require("../../functions/expand/expand");
|
|
14
|
+
const subst_1 = require("../../functions/subst");
|
|
15
|
+
const Settings_1 = require("../../Settings");
|
|
16
|
+
const constants_1 = require("../parser/constants");
|
|
17
|
+
const add_1 = require("../parser/operations/add");
|
|
18
|
+
const compare_1 = require("../parser/operations/compare");
|
|
19
|
+
const divide_1 = require("../parser/operations/divide");
|
|
20
|
+
// import { multiply, add, subtract, divide, power, equal, gt, gte } from '../parser';
|
|
21
|
+
const multiply_1 = require("../parser/operations/multiply");
|
|
22
|
+
const power_1 = require("../parser/operations/power");
|
|
23
|
+
const power_2 = require("../parser/operations/power");
|
|
24
|
+
const subtract_1 = require("../parser/operations/subtract");
|
|
25
|
+
const Parser_1 = require("../parser/Parser");
|
|
26
|
+
const Rational_1 = require("../rational/Rational");
|
|
27
|
+
const analysis_1 = require("./analysis");
|
|
28
|
+
const analysis_2 = require("./analysis");
|
|
29
|
+
const analysis_3 = require("./analysis");
|
|
30
|
+
const format_1 = require("./format");
|
|
31
|
+
const shortcuts_1 = require("./shortcuts");
|
|
32
|
+
const traversal_1 = require("./traversal");
|
|
33
|
+
const traversal_2 = require("./traversal");
|
|
34
|
+
const traversal_3 = require("./traversal");
|
|
35
|
+
const utils_1 = require("./utils");
|
|
36
|
+
const { NUM, VAR, EXP, FUN, GRP, PRD, SUM, INF } = constants_1.EXPRESSION_TYPES;
|
|
37
|
+
/**
|
|
38
|
+
* Represents a symbolic expression in Nerdamer's canonical expression tree.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* `Expression` is the central symbolic value type used by the parser and by the
|
|
42
|
+
* algebra, calculus, and solver layers. Parsed expressions are normalized into a
|
|
43
|
+
* small set of internal node categories such as numbers, variables, functions,
|
|
44
|
+
* powers, products, and sums. Numeric coefficients are normally stored in a
|
|
45
|
+
* {@link Rational} multiplier rather than as separate product elements.
|
|
46
|
+
*
|
|
47
|
+
* Most arithmetic and transformation methods return a new `Expression`, but the
|
|
48
|
+
* class itself is mutable because parser and algorithm internals rebuild nodes in
|
|
49
|
+
* place. Accessors such as {@link Expression.getArguments},
|
|
50
|
+
* {@link Expression.getMultiplier}, and {@link Expression.getPower} may lazily
|
|
51
|
+
* initialize and return internal objects. Use {@link Expression.copy} when an
|
|
52
|
+
* independently mutable expression tree is required.
|
|
53
|
+
*
|
|
54
|
+
* {@link Expression.create} is the preferred construction API. In particular,
|
|
55
|
+
* `Expression.create(existingExpression)` preserves object identity unless its
|
|
56
|
+
* `copy` argument is set to `true`.
|
|
57
|
+
*
|
|
58
|
+
* The internal expression groups are organizational categories used by Nerdamer's
|
|
59
|
+
* canonicalization logic. They should not be confused with general mathematical
|
|
60
|
+
* classifications:
|
|
61
|
+
*
|
|
62
|
+
* - `NUM` stores plain numeric values.
|
|
63
|
+
* - `VAR` stores symbols/variables whose powers fit the variable representation.
|
|
64
|
+
* - `EXP` stores bases with powers that require a separate exponential node; it is
|
|
65
|
+
* not the same thing as an exponential function.
|
|
66
|
+
* - `FUN` stores function calls.
|
|
67
|
+
* - `GRP` stores sums with a common base but differing powers, such as
|
|
68
|
+
* `x + x^y` or `cos(x) - 3*cos(x)^2`.
|
|
69
|
+
* - `PRD` stores products of non-numeric symbolic factors; numeric coefficients are
|
|
70
|
+
* moved to the outer multiplier during parsing.
|
|
71
|
+
* - `SUM` stores other sums. For example, `1 + x + x^2` is a `SUM` containing a
|
|
72
|
+
* numeric term and a grouped polynomial-like component.
|
|
73
|
+
* - `INF` stores infinite values.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const expression = Expression.create('2*x + 1');
|
|
78
|
+
*
|
|
79
|
+
* expression.text(); // "1+2*x"
|
|
80
|
+
* expression.evaluate({ x: 3 }).text(); // "7"
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
class Expression {
|
|
84
|
+
/**
|
|
85
|
+
* Controls whether addition first distributes an outer multiplier on a sum.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* This is a process-wide parser/algebra setting used by the addition operation.
|
|
89
|
+
* Changing it affects subsequent symbolic operations globally.
|
|
90
|
+
*/
|
|
91
|
+
static DISTRIBUTE_MULTIPLIER = true;
|
|
92
|
+
/**
|
|
93
|
+
* The symbol currently reserved for the imaginary unit.
|
|
94
|
+
*
|
|
95
|
+
* Use {@link Parser.setI} to change the symbol so the parser's restricted-name
|
|
96
|
+
* bookkeeping is updated at the same time.
|
|
97
|
+
*/
|
|
98
|
+
static imaginary = constants_1.DEFAULT_IMAGINARY;
|
|
99
|
+
/** Names used by the internal logarithm representation and converters. */
|
|
100
|
+
static LOG = constants_1.LOG;
|
|
101
|
+
static LOG10 = 'log_10';
|
|
102
|
+
/**
|
|
103
|
+
* Placeholder key used when numeric terms are grouped inside expression containers.
|
|
104
|
+
*
|
|
105
|
+
* This is part of the internal canonical-key representation rather than the
|
|
106
|
+
* rendered mathematical value of a number.
|
|
107
|
+
*/
|
|
108
|
+
static numberHash = '#';
|
|
109
|
+
/** Power operator used by expression text formatting. */
|
|
110
|
+
static POW_OPR = '^';
|
|
111
|
+
/**
|
|
112
|
+
* Symbols excluded from ordinary variable collection.
|
|
113
|
+
*
|
|
114
|
+
* @remarks
|
|
115
|
+
* This list differs from the parser's restricted-name list.
|
|
116
|
+
* It is used by expression traversal when deciding which `VAR` nodes should be
|
|
117
|
+
* reported as free variables. The distinction is historical and should not be
|
|
118
|
+
* interpreted as a general parser-reservation policy.
|
|
119
|
+
*/
|
|
120
|
+
static RESERVED = [constants_1.E].concat(constants_1.PI).concat(constants_1.INFINITY);
|
|
121
|
+
/**
|
|
122
|
+
* Comparator used when expression elements are emitted in canonical display order.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* The ordering is representational, not a mathematical ordering relation. It keeps
|
|
126
|
+
* the imaginary unit last, orders like node types by value and descending power,
|
|
127
|
+
* and otherwise falls back to the internal expression-type order. Replacing this
|
|
128
|
+
* function changes ordering globally for subsequent formatting and reconstruction.
|
|
129
|
+
*/
|
|
130
|
+
static sortFunction = (a, b) => {
|
|
131
|
+
// Ensure that i is last e.g. 1 + i and not i + 1;
|
|
132
|
+
if (a.isI()) {
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
// Ensure a,b,x,y,...
|
|
136
|
+
if (a.type === b.type) {
|
|
137
|
+
// Ensure that x^2 is before x
|
|
138
|
+
if (a.value === b.value) {
|
|
139
|
+
return Number(b.getPower()) - Number(a.getPower());
|
|
140
|
+
}
|
|
141
|
+
if (a.value > b.value) {
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
if (b.value > a.value) {
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
// The remaining order is per types
|
|
150
|
+
// Default: Exponential > Variable > Functions > Sums/Polynomials > Multivariate Monomials > Infinity
|
|
151
|
+
return a.type - b.type;
|
|
152
|
+
};
|
|
153
|
+
/** Internal expression-type constants used by parser and algebra code. */
|
|
154
|
+
static TYPES = constants_1.EXPRESSION_TYPES;
|
|
155
|
+
/**
|
|
156
|
+
* Arguments stored by a function node.
|
|
157
|
+
*
|
|
158
|
+
* Prefer {@link Expression.getArguments} when consuming this representation.
|
|
159
|
+
*/
|
|
160
|
+
args;
|
|
161
|
+
/**
|
|
162
|
+
* Explicit mathematical base stored by an `EXP` node.
|
|
163
|
+
*
|
|
164
|
+
* Non-`EXP` nodes derive their base through {@link Expression.getBase} instead.
|
|
165
|
+
*/
|
|
166
|
+
base;
|
|
167
|
+
/** Parser entity discriminator for expression values. */
|
|
168
|
+
dataType = constants_1.EXPRESSION;
|
|
169
|
+
/**
|
|
170
|
+
* Signals that the Expression was parsed with the deferred flag true
|
|
171
|
+
*/
|
|
172
|
+
deferred = false;
|
|
173
|
+
/**
|
|
174
|
+
* Canonically keyed child expressions for aggregate nodes such as sums and products.
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* The record is mutable representation state. {@link Expression.getElements}
|
|
178
|
+
* returns this object directly when it exists; callers that mutate it are
|
|
179
|
+
* responsible for preserving a valid expression representation and regenerating derived values.
|
|
180
|
+
*/
|
|
181
|
+
elements = undefined;
|
|
182
|
+
/**
|
|
183
|
+
* Let's the parser know not to treat it as a set of values
|
|
184
|
+
*/
|
|
185
|
+
isEnumerable = false;
|
|
186
|
+
/**
|
|
187
|
+
* Outer rational coefficient carried by this node when explicitly initialized.
|
|
188
|
+
*
|
|
189
|
+
* Use {@link Expression.getMultiplier} to obtain the effective multiplier.
|
|
190
|
+
*/
|
|
191
|
+
multiplier;
|
|
192
|
+
/**
|
|
193
|
+
* The function name if any
|
|
194
|
+
*/
|
|
195
|
+
name;
|
|
196
|
+
/**
|
|
197
|
+
* Outer power carried by this node when explicitly initialized.
|
|
198
|
+
*
|
|
199
|
+
* Use {@link Expression.getPower} to obtain the effective power.
|
|
200
|
+
*/
|
|
201
|
+
power;
|
|
202
|
+
/**
|
|
203
|
+
* If set, this is the maximum known precision of any of the intermediate operations
|
|
204
|
+
*/
|
|
205
|
+
precision;
|
|
206
|
+
/**
|
|
207
|
+
* The default type is a number for the expression since the default value
|
|
208
|
+
* for an expression is "1". The assert flag is used because TypeScript
|
|
209
|
+
* currently doesn't recognized that it's also set in the copyOver method
|
|
210
|
+
*/
|
|
211
|
+
type;
|
|
212
|
+
/**
|
|
213
|
+
* The value of the expression. This hash is used for comparing variable.
|
|
214
|
+
* This value is set in the constructor or the copyOver method
|
|
215
|
+
*/
|
|
216
|
+
value;
|
|
217
|
+
/**
|
|
218
|
+
* Constructs or copies an expression value.
|
|
219
|
+
*
|
|
220
|
+
* @remarks
|
|
221
|
+
* Direct construction is primarily a representation-level API. When `x` is an
|
|
222
|
+
* existing `Expression`, the constructor deep-copies its expression tree. When
|
|
223
|
+
* `plainConstruct` is `true` and `x` is a string, that string is stored as the
|
|
224
|
+
* raw node value without parsing. Other inputs are delegated to
|
|
225
|
+
* {@link Expression.create}, and JavaScript constructor return semantics therefore
|
|
226
|
+
* allow that factory-created expression to become the result of `new Expression(...)`.
|
|
227
|
+
*
|
|
228
|
+
* For ordinary user input, prefer {@link Expression.create}; it makes parsing and
|
|
229
|
+
* identity behavior explicit.
|
|
230
|
+
*
|
|
231
|
+
* @param x - The expression or Nerdamer input used to construct the value.
|
|
232
|
+
* @param plainConstruct - Store a string as a raw internal value instead of parsing it.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const expression = Expression.create('x + 1');
|
|
237
|
+
* const copy = new Expression(expression);
|
|
238
|
+
*
|
|
239
|
+
* copy === expression; // false
|
|
240
|
+
* copy.text(); // "1+x"
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
constructor(x, plainConstruct) {
|
|
244
|
+
// Allow for hooking of the input. The user can override the hook to modify
|
|
245
|
+
// how the input is handled.
|
|
246
|
+
x = Expression.hook(x);
|
|
247
|
+
if (plainConstruct && typeof x === 'string') {
|
|
248
|
+
this.value = x;
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
else if (Expression.isExpression(x)) {
|
|
252
|
+
(0, utils_1.copyOver)(x, this);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
return Expression.create(x);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Converts supported Nerdamer input into an `Expression`.
|
|
260
|
+
*
|
|
261
|
+
* @remarks
|
|
262
|
+
* Strings and primitive numeric inputs are parsed through {@link Parser}. An
|
|
263
|
+
* existing `Expression` is returned unchanged when no substitution `values` are
|
|
264
|
+
* supplied; pass `copy: true` to request an independent deep copy. A
|
|
265
|
+
* {@link Rational} is converted to a numeric expression while preserving its
|
|
266
|
+
* decimal-origin marker.
|
|
267
|
+
*
|
|
268
|
+
* An {@link Equation} is converted to its residual expression by moving the right
|
|
269
|
+
* side to the left on a copy of the equation. For example, `x = 2` becomes the
|
|
270
|
+
* expression `x - 2` in canonical form.
|
|
271
|
+
*
|
|
272
|
+
* @param x - The expression-compatible input to convert.
|
|
273
|
+
* @param values - Parser substitutions applied while parsing non-`Expression` input.
|
|
274
|
+
* @param copy - Copy an existing `Expression` instead of preserving its identity.
|
|
275
|
+
* @returns The parsed, converted, reused, or copied expression.
|
|
276
|
+
* @throws {@link core!UnexpectedDataType}
|
|
277
|
+
* Thrown when parsing produces another parser entity, such as a vector or matrix,
|
|
278
|
+
* where an `Expression` is required.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* const expression = Expression.create('x^2 + 1');
|
|
283
|
+
*
|
|
284
|
+
* Expression.create(expression) === expression; // true
|
|
285
|
+
* Expression.create(expression, undefined, true) === expression; // false
|
|
286
|
+
* Expression.create('a+b', { a: 2, b: 3 }).text(); // "5"
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
static create(x, values, copy) {
|
|
290
|
+
if (Expression.isExpression(x) && !values) {
|
|
291
|
+
return copy ? x.copy() : x;
|
|
292
|
+
}
|
|
293
|
+
else if (Rational_1.Rational.isRational(x)) {
|
|
294
|
+
return Expression.fromRational(x);
|
|
295
|
+
}
|
|
296
|
+
else if ((0, common_1.isNerdamerNativeType)(x, constants_1.EQUATION)) {
|
|
297
|
+
return x.toLHS().LHS;
|
|
298
|
+
}
|
|
299
|
+
const retval = Parser_1.Parser.parse(String(x), values);
|
|
300
|
+
if (!Expression.isExpression(retval)) {
|
|
301
|
+
throw new errors_1.UnexpectedDataType((0, errors_1.message)('expressionExpected', { type: constants_1.dataTypes[retval.dataType] }));
|
|
302
|
+
}
|
|
303
|
+
return retval;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Creates Euler's constant as a symbolic or evaluated expression.
|
|
307
|
+
*
|
|
308
|
+
* @param asNumericValue - Force the evaluated representation even when parser
|
|
309
|
+
* evaluation mode is disabled.
|
|
310
|
+
* @returns Symbolic `e`, or its current numeric representation when evaluation is enabled.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* Expression.E().text(); // "e"
|
|
315
|
+
* Expression.E(true).text(); // numeric approximation
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
static E(asNumericValue) {
|
|
319
|
+
if (Settings_1.Settings.EVALUATE || asNumericValue) {
|
|
320
|
+
return Expression.fromRational(Rational_1.Rational.E);
|
|
321
|
+
}
|
|
322
|
+
return Expression.Variable(constants_1.E);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Converts a rational value into a numeric expression.
|
|
326
|
+
*
|
|
327
|
+
* The conversion preserves the rational's `asDecimal` provenance flag so later
|
|
328
|
+
* formatting and decimal-contagion logic can distinguish decimal-origin values.
|
|
329
|
+
*
|
|
330
|
+
* @param x - Rational value to convert.
|
|
331
|
+
* @returns A new numeric expression with an independent multiplier.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* const rational = Rational.create('3/4');
|
|
336
|
+
* Expression.fromRational(rational).text(); // "3/4"
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
static fromRational(x) {
|
|
340
|
+
const value = x.denominator === 1n ? x.numerator.toString() : `${x.numerator}/${x.denominator}`;
|
|
341
|
+
const retval = Expression.Number(value);
|
|
342
|
+
retval.getMultiplier().asDecimal = x.asDecimal;
|
|
343
|
+
return retval;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Converts a structured entity carrying symbolic bracket access into the scalar
|
|
347
|
+
* Expression form used by ordinary algebra and function nodes.
|
|
348
|
+
*
|
|
349
|
+
* Structured entities without symbolic access are left unchanged by returning
|
|
350
|
+
* `undefined`; callers can then apply their normal conversion rules.
|
|
351
|
+
*/
|
|
352
|
+
static fromSymbolicAccess(x) {
|
|
353
|
+
let retval;
|
|
354
|
+
if (typeof x === 'object' && x !== null) {
|
|
355
|
+
const structured = x;
|
|
356
|
+
const target = structured.symbolicTarget;
|
|
357
|
+
const indices = structured.symbolicAccessor;
|
|
358
|
+
if (Expression.isExpression(target) &&
|
|
359
|
+
indices &&
|
|
360
|
+
indices.length > 0 &&
|
|
361
|
+
indices.every(index => Expression.isExpression(index))) {
|
|
362
|
+
retval = Expression.toAccessor(target, indices);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return retval;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Creates a symbolic function node with the given name.
|
|
369
|
+
*
|
|
370
|
+
* This creates a bare function symbol without arguments. To create a function
|
|
371
|
+
* with arguments, use {@link Expression.toFunction} instead.
|
|
372
|
+
*
|
|
373
|
+
* @param x - The function name.
|
|
374
|
+
* @returns A function-typed Expression.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```ts
|
|
378
|
+
* Expression.Function('f').text() // "f"
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
static Function(x) {
|
|
382
|
+
const retval = Expression.Type(x, FUN);
|
|
383
|
+
retval.name = x;
|
|
384
|
+
return retval;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Generates a canonical string representation for an array of sub-expressions,
|
|
388
|
+
* joined by `+` (for SUM/GRP) or `*` (for PRD), with sums wrapped in parentheses
|
|
389
|
+
* when inside a product.
|
|
390
|
+
*
|
|
391
|
+
* @param arr - The array of sub-expressions.
|
|
392
|
+
* @param f - The string method to call on each element: `'idString'`, `'keyValue'`, or `'text'`.
|
|
393
|
+
* @param expressionType - The parent expression type (SUM, GRP, or PRD).
|
|
394
|
+
* @returns The joined string representation.
|
|
395
|
+
*/
|
|
396
|
+
static getValue(arr, f, expressionType) {
|
|
397
|
+
return arr
|
|
398
|
+
.map(x => {
|
|
399
|
+
// This will call the toString or keyValue depending on what's requested
|
|
400
|
+
let retval = x[f]();
|
|
401
|
+
if ((x.type === SUM || x.type === GRP) && expressionType === PRD) {
|
|
402
|
+
//e.g. (x+1)*x; The x+1 should be wrapped in a brackets
|
|
403
|
+
retval = `(${retval})`;
|
|
404
|
+
}
|
|
405
|
+
return retval;
|
|
406
|
+
})
|
|
407
|
+
.sort()
|
|
408
|
+
.join(expressionType === SUM || expressionType === GRP ? '+' : '*')
|
|
409
|
+
.replace('+-', '-');
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Intercepts values passed through the `Expression` constructor.
|
|
413
|
+
*
|
|
414
|
+
* @remarks
|
|
415
|
+
* The default implementation is the identity function. Applications may replace
|
|
416
|
+
* this static hook, but doing so changes direct-construction behavior globally.
|
|
417
|
+
* {@link Expression.create} does not route every parsed input through this hook.
|
|
418
|
+
*
|
|
419
|
+
* @param x - Raw constructor input.
|
|
420
|
+
* @returns The value the constructor should continue processing.
|
|
421
|
+
*/
|
|
422
|
+
static hook(x) {
|
|
423
|
+
return x;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Creates the variable node representing the current imaginary-unit symbol.
|
|
427
|
+
*
|
|
428
|
+
* @returns A new variable expression using {@link Expression.imaginary}.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* Expression.Img().text(); // "i" with the default parser configuration
|
|
433
|
+
* ```
|
|
434
|
+
*
|
|
435
|
+
* @see {@link Parser.setI}
|
|
436
|
+
*/
|
|
437
|
+
static Img() {
|
|
438
|
+
return Expression.Variable(Expression.imaginary);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Creates a symbolic positive infinity Expression.
|
|
442
|
+
*
|
|
443
|
+
* @returns An Expression representing `+∞`.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* ```ts
|
|
447
|
+
* Expression.Inf().text() // "Infinity"
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
static Inf() {
|
|
451
|
+
return Expression.Type(constants_1.INFINITY[0], INF);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Type guard that checks whether an object is an Expression.
|
|
455
|
+
*
|
|
456
|
+
* @param obj - The value to test.
|
|
457
|
+
* @returns `true` if `obj` is an Expression instance.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* ```ts
|
|
461
|
+
* Expression.isExpression(Expression.create('x')) // true
|
|
462
|
+
* Expression.isExpression(42) // false
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
static isExpression(obj) {
|
|
466
|
+
return (0, common_1.isNerdamerNativeType)(obj, constants_1.EXPRESSION);
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Type guard that checks whether every element of an array is an Expression.
|
|
470
|
+
*
|
|
471
|
+
* @param obj - The value to test.
|
|
472
|
+
* @returns `true` if `obj` is an array and all elements are Expressions.
|
|
473
|
+
*/
|
|
474
|
+
static isExpressionArray(obj) {
|
|
475
|
+
if (!Array.isArray(obj)) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
for (let i = 0; i < obj.length; i++) {
|
|
479
|
+
if (!Expression.isExpression(obj[i])) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Creates the internal summation/product index placeholder variable `_n`.
|
|
487
|
+
*
|
|
488
|
+
* @returns An Expression representing the variable `_n`.
|
|
489
|
+
*/
|
|
490
|
+
static N() {
|
|
491
|
+
return Expression.Variable(constants_1.INDEX_VARIABLE);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Creates a symbolic negative infinity Expression.
|
|
495
|
+
*
|
|
496
|
+
* @returns An Expression representing `−∞`.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* Expression.NegInf().text() // "-Infinity"
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
static NegInf() {
|
|
504
|
+
return Expression.Inf().neg();
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Creates a numeric (`NUM`) expression node from a numeric representation.
|
|
508
|
+
*
|
|
509
|
+
* @remarks
|
|
510
|
+
* This is a low-level constructor for numeric expression nodes. The supplied value
|
|
511
|
+
* is stored as text and is interpreted as a {@link Rational} when the multiplier is
|
|
512
|
+
* first requested. Use {@link Expression.create} when the input should be parsed as
|
|
513
|
+
* a general mathematical expression.
|
|
514
|
+
*
|
|
515
|
+
* @param x - Numeric representation to store.
|
|
516
|
+
* @returns A new numeric expression.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```ts
|
|
520
|
+
* Expression.Number('42').text(); // "42"
|
|
521
|
+
* Expression.Number('3/4').text(); // "3/4"
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
static Number(x) {
|
|
525
|
+
return Expression.Type(String(x), NUM);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Creates π as a symbolic or evaluated expression.
|
|
529
|
+
*
|
|
530
|
+
* @param asNumericValue - Force the evaluated representation even when parser
|
|
531
|
+
* evaluation mode is disabled.
|
|
532
|
+
* @returns Symbolic `pi`, or its current numeric representation when evaluation is enabled.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* Expression.Pi().text(); // "pi"
|
|
537
|
+
* Expression.Pi(true).text(); // numeric approximation
|
|
538
|
+
* ```
|
|
539
|
+
*/
|
|
540
|
+
static Pi(asNumericValue) {
|
|
541
|
+
if (Settings_1.Settings.EVALUATE || asNumericValue) {
|
|
542
|
+
return Expression.fromRational(Rational_1.Rational.PI);
|
|
543
|
+
}
|
|
544
|
+
return Expression.Variable(constants_1.PI[0]);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Sets the power of an Expression. Use this instead of assigning `power` directly.
|
|
548
|
+
*
|
|
549
|
+
* @param x - The Expression whose power to set.
|
|
550
|
+
* @param power - The new power Expression.
|
|
551
|
+
* @returns The modified Expression `x`.
|
|
552
|
+
*/
|
|
553
|
+
static setPower(x, power) {
|
|
554
|
+
x.power = power;
|
|
555
|
+
return x;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Creates multiple Expressions at once from a list of inputs.
|
|
559
|
+
*
|
|
560
|
+
* @param values - One or more inputs to convert to Expressions.
|
|
561
|
+
* @returns An array of Expressions.
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```ts
|
|
565
|
+
* const [x, y] = Expression.symbols('x', 'y');
|
|
566
|
+
* x.text() // "x"
|
|
567
|
+
* y.text() // "y"
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
static symbols(...values) {
|
|
571
|
+
return values.map(x => Expression.create(x));
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Creates the internal scalar Expression used to represent symbolic bracket access.
|
|
575
|
+
* The formatter renders this as ordinary bracket notation rather than exposing the
|
|
576
|
+
* internal function name.
|
|
577
|
+
*/
|
|
578
|
+
static toAccessor(target, indices) {
|
|
579
|
+
return Expression.toFunction(constants_1.SYMBOLIC_ACCESSOR, [target, ...indices]);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Constructs an `EXP` node without applying the normal power simplification rules.
|
|
583
|
+
*
|
|
584
|
+
* @remarks
|
|
585
|
+
* This is an internal representation helper for bases whose exponent must be stored
|
|
586
|
+
* separately. When `powerLess` is `true`, the existing outer power is deleted from
|
|
587
|
+
* the converted base before it is wrapped. Because {@link Expression.create} may
|
|
588
|
+
* reuse an input `Expression`, callers must not use that option when the original
|
|
589
|
+
* object must remain unchanged.
|
|
590
|
+
*
|
|
591
|
+
* @param x - Base to store in the `EXP` node.
|
|
592
|
+
* @param pow - Exponent to store on the node.
|
|
593
|
+
* @param powerLess - Remove an existing outer power from the base before wrapping it.
|
|
594
|
+
* @returns The constructed power expression, or the base itself when the base is one.
|
|
595
|
+
*/
|
|
596
|
+
static toEXP(x, pow, powerLess = false) {
|
|
597
|
+
x = Expression.create(x);
|
|
598
|
+
pow = Expression.create(pow);
|
|
599
|
+
let retval;
|
|
600
|
+
// if (x.isZero() || x.isInf() || pow.isInf()) {
|
|
601
|
+
// retval = x.pow(pow);
|
|
602
|
+
// } else
|
|
603
|
+
if (x.isOne()) {
|
|
604
|
+
// Just return it untouched
|
|
605
|
+
retval = Expression.create(x);
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
if (powerLess) {
|
|
609
|
+
delete x.power;
|
|
610
|
+
}
|
|
611
|
+
// The value is now the entire Expression. Consider (x^x)^(1/3).
|
|
612
|
+
// The value of the Expression is x^x and not x since only x^x can directly operate on it.
|
|
613
|
+
retval = new Expression(x.idString(), true);
|
|
614
|
+
retval.base = Expression.create(x);
|
|
615
|
+
retval.power = pow;
|
|
616
|
+
retval.type = EXP;
|
|
617
|
+
}
|
|
618
|
+
return retval;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Constructs a function node and assigns its arguments.
|
|
622
|
+
*
|
|
623
|
+
* `undefined` entries are omitted. Existing `Expression` arguments may be retained
|
|
624
|
+
* by identity because conversion uses {@link Expression.create} without requesting
|
|
625
|
+
* copies. A Vector or Matrix carrying symbolic bracket access is first normalized
|
|
626
|
+
* to its scalar accessor Expression; ordinary structured values remain invalid
|
|
627
|
+
* function arguments where an Expression is required.
|
|
628
|
+
*
|
|
629
|
+
* @param name - Function name stored on the node.
|
|
630
|
+
* @param args - Function arguments; `undefined` entries are ignored.
|
|
631
|
+
* @returns The reconstructed function expression.
|
|
632
|
+
*/
|
|
633
|
+
static toFunction(name, args) {
|
|
634
|
+
const f = Expression.Function(name);
|
|
635
|
+
// Remove undefined from the array
|
|
636
|
+
f.args = args
|
|
637
|
+
.filter((x) => {
|
|
638
|
+
return x !== undefined;
|
|
639
|
+
})
|
|
640
|
+
.map(x => Expression.fromSymbolicAccess(x) ?? Expression.create(x));
|
|
641
|
+
// Set the name
|
|
642
|
+
f.name = name;
|
|
643
|
+
// TODO: This needs to be generated the same way as in text.
|
|
644
|
+
f.updateValue();
|
|
645
|
+
return f;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Low-level helper that creates an Expression with a specific internal type.
|
|
649
|
+
*
|
|
650
|
+
* @param x - The value string.
|
|
651
|
+
* @param type - The Expression type constant (NUM, VAR, FUN, EXP, etc.).
|
|
652
|
+
* @returns A new Expression of the specified type.
|
|
653
|
+
*/
|
|
654
|
+
static Type(x, type) {
|
|
655
|
+
const num = new Expression(x, true);
|
|
656
|
+
num.type = type;
|
|
657
|
+
return num;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Creates a symbolic variable Expression.
|
|
661
|
+
*
|
|
662
|
+
* @param x - The variable name.
|
|
663
|
+
* @returns A `VAR`-typed Expression.
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```ts
|
|
667
|
+
* Expression.Variable('x').text() // "x"
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
static Variable(x) {
|
|
671
|
+
return Expression.Type(x, VAR);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Returns the symbolic absolute value of this expression.
|
|
675
|
+
*
|
|
676
|
+
* The operation delegates to Nerdamer's `abs` implementation and may simplify
|
|
677
|
+
* values whose sign or complex magnitude can be determined.
|
|
678
|
+
*
|
|
679
|
+
* @returns The resulting expression; the receiver is not modified.
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```ts
|
|
683
|
+
* Expression.create(-5).abs().text(); // "5"
|
|
684
|
+
* Expression.create('x').abs().text(); // "abs(x)"
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
687
|
+
abs() {
|
|
688
|
+
return (0, math_1.abs)(this);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Legacy alias for {@link plus}.
|
|
692
|
+
*
|
|
693
|
+
* @param x - The value to add.
|
|
694
|
+
* @returns A new Expression representing `this + x`.
|
|
695
|
+
*/
|
|
696
|
+
add(x) {
|
|
697
|
+
return this.plus(x);
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Compiles this expression into a native JavaScript numeric function.
|
|
701
|
+
*
|
|
702
|
+
* @remarks
|
|
703
|
+
* The generated function uses JavaScript `number` arithmetic and the registered
|
|
704
|
+
* numerical implementations. It is intended for repeated, relatively low-precision
|
|
705
|
+
* scalar evaluation rather than arbitrary-precision symbolic computation. Functions
|
|
706
|
+
* without a faithful JavaScript-number equivalent are rejected instead of being
|
|
707
|
+
* compiled with approximate or unrelated semantics.
|
|
708
|
+
*
|
|
709
|
+
* When `args` is omitted, free variables are collected and sorted alphabetically.
|
|
710
|
+
* Supplying `args` defines the positional argument order explicitly.
|
|
711
|
+
*
|
|
712
|
+
* @param args - Variable names in the positional order expected by the compiled function.
|
|
713
|
+
* @returns A JavaScript function accepting numeric arguments and returning a number.
|
|
714
|
+
* @throws {@link core!UnsupportedOperationError} If a surviving function has no faithful
|
|
715
|
+
* JavaScript-number implementation.
|
|
716
|
+
*
|
|
717
|
+
* @example
|
|
718
|
+
* ```ts
|
|
719
|
+
* const fn = Expression.create('x^2 + y').buildFunction(['x', 'y']);
|
|
720
|
+
* fn(3, 1); // 10
|
|
721
|
+
* ```
|
|
722
|
+
*/
|
|
723
|
+
buildFunction(args) {
|
|
724
|
+
return (0, build_1.build)(this, args);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Collects coefficients with respect to one or more requested variables.
|
|
728
|
+
*
|
|
729
|
+
* @remarks
|
|
730
|
+
* The expression is expanded as part of coefficient collection. For multivariate
|
|
731
|
+
* input, coefficient keys encode the exponent tuple in the same order as
|
|
732
|
+
* `variables`. Terms that do not contain a requested variable are retained as
|
|
733
|
+
* coefficients rather than discarded.
|
|
734
|
+
*
|
|
735
|
+
* @param variables - Variables whose powers define the coefficient keys.
|
|
736
|
+
* @returns Nerdamer's coefficient object for the requested variable ordering.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```ts
|
|
740
|
+
* const coefficients = Expression.create('3*x^2 + 2*x + 1').coeffs('x');
|
|
741
|
+
* coefficients.toArray().map(value => value.text()); // ["1", "2", "3"]
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
coeffs(...variables) {
|
|
745
|
+
return (0, analysis_1.coeffs)(this, variables);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Deep-copies the expression tree.
|
|
749
|
+
*
|
|
750
|
+
* @remarks
|
|
751
|
+
* Multipliers, powers, function arguments, explicit exponential bases, and aggregate
|
|
752
|
+
* elements are recursively copied. Mutating those structures on the returned
|
|
753
|
+
* expression therefore does not mutate the corresponding structures on the source.
|
|
754
|
+
*
|
|
755
|
+
* @returns An independently mutable expression with the same symbolic representation.
|
|
756
|
+
*
|
|
757
|
+
* @example
|
|
758
|
+
* ```ts
|
|
759
|
+
* const source = Expression.create('x + 1');
|
|
760
|
+
* const copy = source.copy();
|
|
761
|
+
*
|
|
762
|
+
* copy === source; // false
|
|
763
|
+
* copy.text(); // "1+x"
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
copy() {
|
|
767
|
+
return new Expression(this);
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Legacy alias for {@link getDenominator}.
|
|
771
|
+
*
|
|
772
|
+
* @returns The denominator Expression.
|
|
773
|
+
*/
|
|
774
|
+
denominator() {
|
|
775
|
+
return this.getDenominator();
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Distributes this node's outer multiplier through a linear sum.
|
|
779
|
+
*
|
|
780
|
+
* @remarks
|
|
781
|
+
* A copy is created before any changes are made. Distribution is performed only
|
|
782
|
+
* when the expression is sum-like, has power one, and carries a non-unit outer
|
|
783
|
+
* multiplier. Nested sum terms encountered during the pass are handled recursively.
|
|
784
|
+
* Expressions that do not meet those conditions are returned as equivalent copies.
|
|
785
|
+
*
|
|
786
|
+
* @returns A new expression with the eligible multiplier distributed.
|
|
787
|
+
*
|
|
788
|
+
* @example
|
|
789
|
+
* ```ts
|
|
790
|
+
* Expression.create('2*(x+y)').distributeMultiplier().text(); // "2*x+2*y"
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
distributeMultiplier() {
|
|
794
|
+
const retval = this.copy();
|
|
795
|
+
if (this.isSum() && !this.getMultiplier().isOne() && this.isLinear()) {
|
|
796
|
+
const m = retval.getMultiplier();
|
|
797
|
+
// Remove it
|
|
798
|
+
retval.multiplier = undefined;
|
|
799
|
+
// multiply each element by the multiplier
|
|
800
|
+
const elements = retval.getElements();
|
|
801
|
+
for (const x in elements) {
|
|
802
|
+
const term = elements[x];
|
|
803
|
+
term.multiplier = term.getMultiplier().times(m);
|
|
804
|
+
if (term.isSum()) {
|
|
805
|
+
elements[x] = term.distributeMultiplier();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return retval;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Divides this Expression by the given value.
|
|
813
|
+
*
|
|
814
|
+
* @param x - The divisor.
|
|
815
|
+
* @returns A new Expression representing `this / x`.
|
|
816
|
+
*
|
|
817
|
+
* @example
|
|
818
|
+
* ```ts
|
|
819
|
+
* Expression.create('x^2').div('x').text() // "x"
|
|
820
|
+
* Expression.create(10).div(3).text() // "10/3"
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
div(x) {
|
|
824
|
+
return (0, divide_1.divide)(this, Expression.create(x));
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Legacy alias for {@link div}.
|
|
828
|
+
*
|
|
829
|
+
* @param x - The divisor.
|
|
830
|
+
* @returns A new Expression representing `this / x`.
|
|
831
|
+
*/
|
|
832
|
+
divide(x) {
|
|
833
|
+
return this.div(x);
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Visits the immediate terms represented by this expression.
|
|
837
|
+
*
|
|
838
|
+
* @remarks
|
|
839
|
+
* Aggregate nodes pass each stored element and its key to `fn`. Atomic nodes invoke
|
|
840
|
+
* the callback once with a unit-multiplier form of the expression and the node's
|
|
841
|
+
* value as the key. The callback's return value is ignored; this method does not
|
|
842
|
+
* rebuild the expression. Use {@link Expression.forEveryElement} when returned
|
|
843
|
+
* replacements should participate in reconstruction.
|
|
844
|
+
*
|
|
845
|
+
* For atomic nodes, creating the unit-multiplier argument does not mutate the receiver.
|
|
846
|
+
* Aggregate elements, however, are passed by reference, so the callback can mutate
|
|
847
|
+
* their internal objects if it chooses to do so.
|
|
848
|
+
*
|
|
849
|
+
* @param fn - Visitor receiving an immediate expression element and its key.
|
|
850
|
+
* @returns This expression for chaining.
|
|
851
|
+
*/
|
|
852
|
+
each(fn) {
|
|
853
|
+
// The callback assumes the multiplier is carried at the top level
|
|
854
|
+
// So if there are no elements then call this minus the multiplier
|
|
855
|
+
if (!this.elements) {
|
|
856
|
+
fn(this.toUnitMultiplier(), this.value);
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
const elements = this.getElements();
|
|
860
|
+
for (const x in elements) {
|
|
861
|
+
fn(elements[x], x);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return this;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Returns the immediate expression elements in canonical sort order.
|
|
868
|
+
*
|
|
869
|
+
* @remarks
|
|
870
|
+
* Sum-like nodes flatten nested linear sums one level while products retain nested
|
|
871
|
+
* sums. Atomic nodes contribute a unit-multiplier copy. When `withMultiplier` is
|
|
872
|
+
* `true`, a product's outer multiplier is appended as a numeric expression before
|
|
873
|
+
* sorting. The option has no effect on sums.
|
|
874
|
+
*
|
|
875
|
+
* Stored aggregate elements are returned by reference rather than deep-copied.
|
|
876
|
+
*
|
|
877
|
+
* @param withMultiplier - Include a product's outer coefficient as an array element.
|
|
878
|
+
* @returns The sorted immediate elements.
|
|
879
|
+
*/
|
|
880
|
+
elementsArray(withMultiplier) {
|
|
881
|
+
const elementsArray = [];
|
|
882
|
+
if (this.isProduct() || this.isSum()) {
|
|
883
|
+
const elements = this.getElements();
|
|
884
|
+
for (const c in elements) {
|
|
885
|
+
const element = elements[c];
|
|
886
|
+
// Check for nested sums. Don't expand if we're in a product.
|
|
887
|
+
if (element.isSum() && this.isSum() && element.isLinear()) {
|
|
888
|
+
const subElements = element.getElements();
|
|
889
|
+
for (const sc in subElements) {
|
|
890
|
+
elementsArray.push(subElements[sc]);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
elementsArray.push(element);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
elementsArray.push(this.toUnitMultiplier());
|
|
900
|
+
}
|
|
901
|
+
// Adding the multiplier makes no sense when getting sums.
|
|
902
|
+
if (withMultiplier && this.isProduct()) {
|
|
903
|
+
elementsArray.push(Expression.fromRational(this.getMultiplier()));
|
|
904
|
+
}
|
|
905
|
+
return elementsArray.sort(Expression.sortFunction);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Tests whether this expression is symbolically equal to another value.
|
|
909
|
+
*
|
|
910
|
+
* @remarks
|
|
911
|
+
* Equality is not a JavaScript identity or raw-tree comparison. Nerdamer first
|
|
912
|
+
* consults applicable assumptions and otherwise subtracts the expressions,
|
|
913
|
+
* canonicalizes numeric radicals where needed, expands the difference, and accepts
|
|
914
|
+
* equality when that result reduces to numeric zero. A `false` result therefore
|
|
915
|
+
* means equality was not established by the current comparison machinery; it is
|
|
916
|
+
* not a general theorem-prover result for arbitrary symbolic identities.
|
|
917
|
+
*
|
|
918
|
+
* @param x - Value to compare with this expression.
|
|
919
|
+
* @returns `true` when Nerdamer establishes equality, otherwise `false`.
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* ```ts
|
|
923
|
+
* Expression.create('x+1').eq('1+x'); // true
|
|
924
|
+
* Expression.create(3).eq(4); // false
|
|
925
|
+
* ```
|
|
926
|
+
*/
|
|
927
|
+
eq(x) {
|
|
928
|
+
x = Expression.create(x);
|
|
929
|
+
return (0, compare_1.equal)(this, x);
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Re-evaluates this expression numerically, optionally substituting variable values.
|
|
933
|
+
*
|
|
934
|
+
* @remarks
|
|
935
|
+
* Evaluation serializes the current expression and sends it through
|
|
936
|
+
* {@link Parser.evaluate}. That parser path enables Nerdamer's evaluation mode, so
|
|
937
|
+
* numeric constants and supported numeric functions are evaluated according to the
|
|
938
|
+
* parser's current precision and settings. The original expression is not modified.
|
|
939
|
+
*
|
|
940
|
+
* @param values - Variable substitutions applied during evaluation.
|
|
941
|
+
* @returns The evaluated expression.
|
|
942
|
+
*
|
|
943
|
+
* @example
|
|
944
|
+
* ```ts
|
|
945
|
+
* Expression.create('x^2 + 1').evaluate({ x: 3 }).text(); // "10"
|
|
946
|
+
* Expression.create('pi/2').evaluate().text(); // numeric approximation
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
evaluate(values) {
|
|
950
|
+
const source = this.hasFunction(constants_1.SYMBOLIC_ACCESSOR, true)
|
|
951
|
+
? (0, format_1.toText)(this, { internalAccessor: true }, undefined, Expression.POW_OPR, Expression.sortFunction)
|
|
952
|
+
: this.text();
|
|
953
|
+
const evaluated = Parser_1.Parser.evaluate(source, values);
|
|
954
|
+
const retval = Expression.fromSymbolicAccess(evaluated) ?? Expression.create(evaluated);
|
|
955
|
+
return retval;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Expands products and eligible powers into an equivalent symbolic expression.
|
|
959
|
+
*
|
|
960
|
+
* @remarks
|
|
961
|
+
* Expansion delegates to Nerdamer's canonical expansion algorithm. The result is
|
|
962
|
+
* independent of the receiver, but callers should treat object identity as an
|
|
963
|
+
* implementation detail rather than relying on expansion to allocate a particular
|
|
964
|
+
* representation. Branch-sensitive power rules are preserved rather than treating
|
|
965
|
+
* every algebraic power identity as universally valid over the complex domain.
|
|
966
|
+
*
|
|
967
|
+
* @returns The expanded expression.
|
|
968
|
+
*
|
|
969
|
+
* @example
|
|
970
|
+
* ```ts
|
|
971
|
+
* Expression.create('(x+1)^2').expand().text(); // "1+2*x+x^2"
|
|
972
|
+
* ```
|
|
973
|
+
*/
|
|
974
|
+
expand() {
|
|
975
|
+
return (0, expand_1.expand)(this);
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Applies a transformation while recursively rebuilding this expression.
|
|
979
|
+
*
|
|
980
|
+
* @remarks
|
|
981
|
+
* Unlike {@link Expression.each}, callback results are used as replacements. The
|
|
982
|
+
* traversal reconstructs functions, products, sums, and exponential powers while
|
|
983
|
+
* restoring the original outer multiplier and power. Numeric and variable roots are
|
|
984
|
+
* returned unchanged by the traversal helper rather than passed through `fn`, so the
|
|
985
|
+
* result may preserve the receiver's identity for those atomic cases.
|
|
986
|
+
*
|
|
987
|
+
* @param fn - Transformation applied to traversed symbolic components.
|
|
988
|
+
* @returns The rebuilt expression.
|
|
989
|
+
*/
|
|
990
|
+
forEveryElement(fn) {
|
|
991
|
+
return (0, traversal_3.forEveryElement)(this, fn);
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Collects distinct function occurrences from the expression tree.
|
|
995
|
+
*
|
|
996
|
+
* @remarks
|
|
997
|
+
* By default the returned strings are function names such as `sin` and `cos`.
|
|
998
|
+
* When `getValues` is `true`, the collector instead returns each function node's
|
|
999
|
+
* stored value string, such as `sin(x)`, while still deduplicating repeated values.
|
|
1000
|
+
* Traversal includes function arguments, aggregate elements, and `EXP` bases and
|
|
1001
|
+
* powers.
|
|
1002
|
+
*
|
|
1003
|
+
* If `fns` is supplied, results are appended to that same array.
|
|
1004
|
+
*
|
|
1005
|
+
* @param fns - Optional accumulator that receives unique strings.
|
|
1006
|
+
* @param getValues - Collect stored function-expression strings instead of names.
|
|
1007
|
+
* @returns The accumulator containing the collected function strings.
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```ts
|
|
1011
|
+
* Expression.create('sin(x) + cos(y)').functions(); // ["sin", "cos"]
|
|
1012
|
+
* ```
|
|
1013
|
+
*/
|
|
1014
|
+
functions(fns, getValues) {
|
|
1015
|
+
return (0, traversal_2.functions)(this, fns, getValues);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Returns the mutable argument array stored by this function node.
|
|
1019
|
+
*
|
|
1020
|
+
* @remarks
|
|
1021
|
+
* The array is lazily created when no arguments are present and is returned by
|
|
1022
|
+
* reference, not copied. Mutating the returned array therefore mutates this
|
|
1023
|
+
* expression. Call {@link Expression.updateValue} after structural changes when
|
|
1024
|
+
* the stored `value` string must be regenerated.
|
|
1025
|
+
*
|
|
1026
|
+
* @returns The internal function-argument array.
|
|
1027
|
+
*
|
|
1028
|
+
* @example
|
|
1029
|
+
* ```ts
|
|
1030
|
+
* Expression.create('sin(x)').getArguments()[0].text(); // "x"
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
1033
|
+
getArguments() {
|
|
1034
|
+
if (!this.args) {
|
|
1035
|
+
this.args = [];
|
|
1036
|
+
}
|
|
1037
|
+
return this.args;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Returns the mathematical base represented by this node.
|
|
1041
|
+
*
|
|
1042
|
+
* @remarks
|
|
1043
|
+
* `EXP` nodes return their stored base directly because signs and coefficients that
|
|
1044
|
+
* belong inside an exponential base must remain part of that base. Other node types
|
|
1045
|
+
* derive the same base from a deep structural copy with the outer multiplier and
|
|
1046
|
+
* outer power removed.
|
|
1047
|
+
*
|
|
1048
|
+
* The `EXP` branch returns the internal base by reference. The non-`EXP` branch
|
|
1049
|
+
* returns an independent expression and does not re-enter the parser.
|
|
1050
|
+
*
|
|
1051
|
+
* @returns The stored or derived base expression.
|
|
1052
|
+
*
|
|
1053
|
+
* @example
|
|
1054
|
+
* ```ts
|
|
1055
|
+
* Expression.create('3*x^2').getBase().text(); // "x"
|
|
1056
|
+
* Expression.create('2^(x+1)').getBase().text(); // "2"
|
|
1057
|
+
* ```
|
|
1058
|
+
*/
|
|
1059
|
+
getBase() {
|
|
1060
|
+
let retval;
|
|
1061
|
+
if (this.isEXP()) {
|
|
1062
|
+
retval = this.base;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
retval = this.copy();
|
|
1066
|
+
delete retval.multiplier;
|
|
1067
|
+
delete retval.power;
|
|
1068
|
+
}
|
|
1069
|
+
return retval;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Extracts the denominator represented by this expression's current structure.
|
|
1073
|
+
*
|
|
1074
|
+
* @remarks
|
|
1075
|
+
* The operation collects the rational multiplier's denominator and factors carried
|
|
1076
|
+
* by negative powers in a product. It does not first combine a sum over a common
|
|
1077
|
+
* denominator. For example, `a/x + 8` is a sum whose outer denominator is one, while
|
|
1078
|
+
* the individual term `a/x` has denominator `x`.
|
|
1079
|
+
*
|
|
1080
|
+
* @returns The denominator represented by the current expression structure.
|
|
1081
|
+
*
|
|
1082
|
+
* @example
|
|
1083
|
+
* ```ts
|
|
1084
|
+
* Expression.create('x/y').getDenominator().text() // "y"
|
|
1085
|
+
* Expression.create('3/4').getDenominator().text() // "4"
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
getDenominator() {
|
|
1089
|
+
return (0, analysis_3.getDenominator)(this);
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Returns the mutable child-element record stored by an aggregate expression.
|
|
1093
|
+
*
|
|
1094
|
+
* @remarks
|
|
1095
|
+
* When `elements` exists, the actual internal record is returned rather than a copy.
|
|
1096
|
+
* Mutating it therefore mutates this expression and can invalidate canonical keys or
|
|
1097
|
+
* the stored `value` string unless the caller restores the corresponding representation state. When this
|
|
1098
|
+
* node has no element record, a new empty object is returned and is not attached to
|
|
1099
|
+
* the expression.
|
|
1100
|
+
*
|
|
1101
|
+
* @returns The internal element record, or a detached empty record for atomic nodes.
|
|
1102
|
+
*/
|
|
1103
|
+
getElements() {
|
|
1104
|
+
if (!this.elements) {
|
|
1105
|
+
return {};
|
|
1106
|
+
}
|
|
1107
|
+
return this.elements;
|
|
1108
|
+
}
|
|
1109
|
+
getMultiplier(asExpression) {
|
|
1110
|
+
if (this.multiplier === undefined) {
|
|
1111
|
+
const value = this.type === NUM ? this.value : '1';
|
|
1112
|
+
this.multiplier = Rational_1.Rational.create(value);
|
|
1113
|
+
}
|
|
1114
|
+
if (asExpression) {
|
|
1115
|
+
return Expression.create(this.multiplier);
|
|
1116
|
+
}
|
|
1117
|
+
return this.multiplier;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Extracts the numerator represented by this expression's current structure.
|
|
1121
|
+
*
|
|
1122
|
+
* @remarks
|
|
1123
|
+
* The operation separates the numerator of the rational multiplier and recursively
|
|
1124
|
+
* collects numerator factors from linear products. It does not first rewrite a sum
|
|
1125
|
+
* over a common denominator; a sum such as `a/x + 8` therefore remains the numerator
|
|
1126
|
+
* of that top-level representation.
|
|
1127
|
+
*
|
|
1128
|
+
* @returns The numerator represented by the current expression structure.
|
|
1129
|
+
*
|
|
1130
|
+
* @example
|
|
1131
|
+
* ```ts
|
|
1132
|
+
* Expression.create('x/y').getNumerator().text() // "x"
|
|
1133
|
+
* Expression.create('3/4').getNumerator().text() // "3"
|
|
1134
|
+
* ```
|
|
1135
|
+
*/
|
|
1136
|
+
getNumerator() {
|
|
1137
|
+
return (0, analysis_3.getNumerator)(this);
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Returns this node's effective outer power.
|
|
1141
|
+
*
|
|
1142
|
+
* @remarks
|
|
1143
|
+
* The power is initialized lazily and stored on the expression. Numeric (`NUM`)
|
|
1144
|
+
* nodes default to power zero because their numeric value is carried by the
|
|
1145
|
+
* multiplier; other node types default to power one. The returned `Expression` is
|
|
1146
|
+
* the mutable internal power object, not a copy.
|
|
1147
|
+
*
|
|
1148
|
+
* @returns The internal power expression.
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```ts
|
|
1152
|
+
* Expression.create('x^3').getPower().text(); // "3"
|
|
1153
|
+
* Expression.create('x').getPower().text(); // "1"
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
getPower() {
|
|
1157
|
+
if (!this.power) {
|
|
1158
|
+
// Don't use shortcuts as this power may later be modified.
|
|
1159
|
+
if (this.isNUM()) {
|
|
1160
|
+
this.power = Expression.Number('0');
|
|
1161
|
+
}
|
|
1162
|
+
else {
|
|
1163
|
+
this.power = Expression.Number('1');
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return this.power;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Retrieves a matching variable factor from this node.
|
|
1170
|
+
*
|
|
1171
|
+
* The method returns this expression when its stored value matches `variable`, or
|
|
1172
|
+
* the matching immediate factor when this is a product. When no match is found it
|
|
1173
|
+
* returns the numeric zero expression rather than `undefined`.
|
|
1174
|
+
*
|
|
1175
|
+
* @param variable - Stored variable value to locate.
|
|
1176
|
+
* @returns The matching expression reference, or zero when no match exists.
|
|
1177
|
+
*/
|
|
1178
|
+
getVariable(variable) {
|
|
1179
|
+
return (0, analysis_2.getVariable)(this, variable);
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Tests whether this expression is `>` another value.
|
|
1183
|
+
*
|
|
1184
|
+
* @remarks
|
|
1185
|
+
* Nerdamer consults applicable assumptions and otherwise evaluates the difference
|
|
1186
|
+
* numerically/symbolically. These `Expression` comparison methods remain boolean for
|
|
1187
|
+
* compatibility. An unknown assumption result falls through to the ordinary comparison
|
|
1188
|
+
* path; if the relation still cannot be established, the method returns `false`. The
|
|
1189
|
+
* lower-level `Assumption` relations preserve unknown as `undefined`. Complex values are
|
|
1190
|
+
* not ordered and cause the comparison to throw.
|
|
1191
|
+
*
|
|
1192
|
+
* @param x - Value to compare with this expression.
|
|
1193
|
+
* @returns `true` when the requested ordering is established, otherwise `false`.
|
|
1194
|
+
* @throws {@link core!UnsupportedOperationError}
|
|
1195
|
+
* Thrown when either side is classified as complex.
|
|
1196
|
+
*/
|
|
1197
|
+
gt(x) {
|
|
1198
|
+
x = Expression.create(x);
|
|
1199
|
+
return (0, compare_1.gt)(this, x);
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Tests whether this expression is `>=` another value.
|
|
1203
|
+
*
|
|
1204
|
+
* @remarks
|
|
1205
|
+
* Nerdamer consults applicable assumptions and otherwise evaluates the difference
|
|
1206
|
+
* numerically/symbolically. These `Expression` comparison methods remain boolean for
|
|
1207
|
+
* compatibility. An unknown assumption result falls through to the ordinary comparison
|
|
1208
|
+
* path; if the relation still cannot be established, the method returns `false`. The
|
|
1209
|
+
* lower-level `Assumption` relations preserve unknown as `undefined`. Complex values are
|
|
1210
|
+
* not ordered and cause the comparison to throw.
|
|
1211
|
+
*
|
|
1212
|
+
* @param x - Value to compare with this expression.
|
|
1213
|
+
* @returns `true` when the requested ordering is established, otherwise `false`.
|
|
1214
|
+
* @throws {@link core!UnsupportedOperationError}
|
|
1215
|
+
* Thrown when either side is classified as complex.
|
|
1216
|
+
*/
|
|
1217
|
+
gte(x) {
|
|
1218
|
+
x = Expression.create(x);
|
|
1219
|
+
return (0, compare_1.gte)(this, x);
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Reports whether decimal-origin numeric data occurs anywhere in this expression.
|
|
1223
|
+
*
|
|
1224
|
+
* @remarks
|
|
1225
|
+
* The check follows the multiplier, explicit power, exponential base, function
|
|
1226
|
+
* arguments, and aggregate elements. It tests the rational `asDecimal` provenance
|
|
1227
|
+
* marker; it does not merely search rendered text for a decimal point.
|
|
1228
|
+
*
|
|
1229
|
+
* @returns `true` when any contained rational originated from decimal input.
|
|
1230
|
+
*/
|
|
1231
|
+
hasDecimal() {
|
|
1232
|
+
let retval = this.getMultiplier().asDecimal;
|
|
1233
|
+
if (!retval && this.power) {
|
|
1234
|
+
retval = this.power.hasDecimal();
|
|
1235
|
+
}
|
|
1236
|
+
if (!retval && this.base) {
|
|
1237
|
+
retval = this.base.hasDecimal();
|
|
1238
|
+
}
|
|
1239
|
+
if (!retval && this.args) {
|
|
1240
|
+
retval = this.args.some(argument => argument.hasDecimal());
|
|
1241
|
+
}
|
|
1242
|
+
if (!retval && this.elements) {
|
|
1243
|
+
retval = Object.values(this.elements).some(element => element.hasDecimal());
|
|
1244
|
+
}
|
|
1245
|
+
return retval;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Tests whether a named function occurs in this expression.
|
|
1249
|
+
*
|
|
1250
|
+
* @remarks
|
|
1251
|
+
* Aggregate elements are always searched. When `deep` is `true`, function arguments
|
|
1252
|
+
* and the explicit base and power of `EXP` nodes are searched recursively as well.
|
|
1253
|
+
* With `deep` disabled, nested function arguments and `EXP` components are not
|
|
1254
|
+
* traversed.
|
|
1255
|
+
*
|
|
1256
|
+
* @param name - Function name to locate.
|
|
1257
|
+
* @param deep - Include function arguments and `EXP` base/power traversal.
|
|
1258
|
+
* @returns `true` when the named function is found.
|
|
1259
|
+
*/
|
|
1260
|
+
hasFunction(name, deep = false) {
|
|
1261
|
+
return (0, traversal_1.hasFunction)(this, name, deep);
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Tests the node's outer power for a rational exponent with a non-unit denominator.
|
|
1265
|
+
*
|
|
1266
|
+
* @remarks
|
|
1267
|
+
* This predicate examines this node's effective power; it does not recursively scan
|
|
1268
|
+
* every descendant for radicals. When `checkIrrationalDenominator` is `true`, the
|
|
1269
|
+
* power must also be negative, which identifies a radical occurring in a denominator.
|
|
1270
|
+
*
|
|
1271
|
+
* @param checkIrrationalDenominator - Require the fractional power to be negative.
|
|
1272
|
+
* @returns `true` when the outer power meets the requested radical condition.
|
|
1273
|
+
*/
|
|
1274
|
+
hasRadical(checkIrrationalDenominator) {
|
|
1275
|
+
const p = this.getPower();
|
|
1276
|
+
const m = p.getMultiplier();
|
|
1277
|
+
const isRadical = p.isNUM() && m.denominator !== 1n;
|
|
1278
|
+
if (checkIrrationalDenominator) {
|
|
1279
|
+
return isRadical && m.isNegative();
|
|
1280
|
+
}
|
|
1281
|
+
return isRadical;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Tests whether a variable node with the requested value occurs in the expression tree.
|
|
1285
|
+
*
|
|
1286
|
+
* Function arguments, aggregate elements, and `EXP` bases and powers are searched
|
|
1287
|
+
* recursively. This predicate does not apply the free-variable filtering used by
|
|
1288
|
+
* {@link Expression.variables}; reserved constants can still match when requested
|
|
1289
|
+
* explicitly.
|
|
1290
|
+
*
|
|
1291
|
+
* @param variable - Variable value to locate.
|
|
1292
|
+
* @returns `true` when a matching `VAR` node occurs.
|
|
1293
|
+
*/
|
|
1294
|
+
hasVariable(variable) {
|
|
1295
|
+
return (0, traversal_1.hasVariable)(this, variable);
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Multiplies this Expression by the imaginary unit, converting it to an imaginary value.
|
|
1299
|
+
*
|
|
1300
|
+
* @returns A new Expression equal to `this * i`.
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```ts
|
|
1304
|
+
* Expression.create(3).i().text() // "3*i"
|
|
1305
|
+
* ```
|
|
1306
|
+
*/
|
|
1307
|
+
i() {
|
|
1308
|
+
return this.times(Expression.Img());
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Returns a string representation used internally for structural comparison
|
|
1312
|
+
* of expressions.
|
|
1313
|
+
*
|
|
1314
|
+
* @returns A string identifier suitable for equality checks.
|
|
1315
|
+
*/
|
|
1316
|
+
idString() {
|
|
1317
|
+
return this.text(undefined, true);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Returns Nerdamer's symbolic decomposition of the imaginary component.
|
|
1321
|
+
*
|
|
1322
|
+
* @remarks
|
|
1323
|
+
* The result excludes the imaginary-unit factor itself. Decomposition follows
|
|
1324
|
+
* principal-branch handling for powered complex values and preserves unresolved
|
|
1325
|
+
* complex function components symbolically instead of silently treating them as
|
|
1326
|
+
* zero. `realpart(...)` and `imagpart(...)` nodes are treated as real-valued component
|
|
1327
|
+
* expressions.
|
|
1328
|
+
*
|
|
1329
|
+
* @returns The symbolic imaginary coefficient.
|
|
1330
|
+
*/
|
|
1331
|
+
imagPart() {
|
|
1332
|
+
return (0, complex_1.imagPart)(this);
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Returns the multiplicative inverse of this expression.
|
|
1336
|
+
*
|
|
1337
|
+
* @remarks
|
|
1338
|
+
* Real symbolic nodes are copied and inverted by negating the outer power and
|
|
1339
|
+
* rational multiplier. Complex expressions use the general division path so real and
|
|
1340
|
+
* imaginary components are handled correctly. Simple radical denominators are
|
|
1341
|
+
* rationalized when the power operation identifies an eligible radical.
|
|
1342
|
+
*
|
|
1343
|
+
* The receiver is not modified.
|
|
1344
|
+
*
|
|
1345
|
+
* @returns The reciprocal expression.
|
|
1346
|
+
* @throws {@link core!DivisionByZeroError}
|
|
1347
|
+
* Thrown by the underlying rational or division operation when the expression is zero.
|
|
1348
|
+
*/
|
|
1349
|
+
invert() {
|
|
1350
|
+
let retval;
|
|
1351
|
+
if (this.isComplex()) {
|
|
1352
|
+
retval = (0, shortcuts_1.one)().div(this);
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
retval = new Expression(this);
|
|
1356
|
+
retval.power = retval.getPower().neg();
|
|
1357
|
+
retval.multiplier = retval.getMultiplier().invert();
|
|
1358
|
+
}
|
|
1359
|
+
if (retval.hasRadical()) {
|
|
1360
|
+
return (0, power_2.rationalizeRadical)(retval);
|
|
1361
|
+
}
|
|
1362
|
+
return retval;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Reports whether the current expression tree contains a complex-valued component.
|
|
1366
|
+
*
|
|
1367
|
+
* @remarks
|
|
1368
|
+
* The check is structural: it recognizes the current imaginary-unit symbol, recurses
|
|
1369
|
+
* through function arguments, `EXP` bases and powers, and aggregate elements. The
|
|
1370
|
+
* component functions `realpart(...)` and `imagpart(...)` are treated as real-valued
|
|
1371
|
+
* by definition even when their arguments contain complex values.
|
|
1372
|
+
*
|
|
1373
|
+
* This predicate does not impose an ordering or otherwise claim that an unrestricted
|
|
1374
|
+
* symbolic expression is provably real.
|
|
1375
|
+
*
|
|
1376
|
+
* @returns `true` when the represented expression contains a recognized complex component.
|
|
1377
|
+
*/
|
|
1378
|
+
isComplex() {
|
|
1379
|
+
let retval = false;
|
|
1380
|
+
if (this.isFunction(constants_1.SYMBOLIC_ACCESSOR)) {
|
|
1381
|
+
// Accessor arguments identify a selected value; they are not arithmetic
|
|
1382
|
+
// components of that value. In particular, an index named `i` must not
|
|
1383
|
+
// make an unresolved access appear complex merely because `i` is also the
|
|
1384
|
+
// configured imaginary-unit symbol.
|
|
1385
|
+
retval = false;
|
|
1386
|
+
}
|
|
1387
|
+
else if (!this.isComplexComponentFunction()) {
|
|
1388
|
+
if (this.isVAR() && this.isI()) {
|
|
1389
|
+
retval = true;
|
|
1390
|
+
}
|
|
1391
|
+
else if (this.isFunction()) {
|
|
1392
|
+
for (const argument of this.getArguments()) {
|
|
1393
|
+
if (argument.isComplex()) {
|
|
1394
|
+
retval = true;
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
else if (this.isEXP()) {
|
|
1400
|
+
retval = this.getBase().isComplex() || this.getPower().isComplex();
|
|
1401
|
+
}
|
|
1402
|
+
else if (this.elements) {
|
|
1403
|
+
for (const element of Object.values(this.getElements())) {
|
|
1404
|
+
if (element.isComplex()) {
|
|
1405
|
+
retval = true;
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return retval;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Tests whether this node is `realpart(...)` or `imagpart(...)`.
|
|
1415
|
+
*
|
|
1416
|
+
* These component functions are treated as real-valued by the complex-decomposition
|
|
1417
|
+
* logic even when their arguments are complex.
|
|
1418
|
+
*/
|
|
1419
|
+
isComplexComponentFunction() {
|
|
1420
|
+
return this.isFunction([constants_1.REALPART, constants_1.IMAGPART]);
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Tests whether this node belongs to Nerdamer's currently recognized constant forms.
|
|
1424
|
+
*
|
|
1425
|
+
* @remarks
|
|
1426
|
+
* This is narrower than the mathematical statement "contains no free
|
|
1427
|
+
* variables." It recognizes numeric nodes, parser constants such as `pi` and `e`,
|
|
1428
|
+
* constant-base/constant-power `EXP` nodes, and sums or products whose elements are
|
|
1429
|
+
* recursively recognized as constant. The imaginary unit is handled by
|
|
1430
|
+
* dedicated complex logic rather than classified here as a parser constant.
|
|
1431
|
+
*
|
|
1432
|
+
* Do not use this predicate as a general proof that an arbitrary function expression
|
|
1433
|
+
* is or is not mathematically constant.
|
|
1434
|
+
*
|
|
1435
|
+
* @returns `true` for the constant forms recognized by this predicate.
|
|
1436
|
+
*/
|
|
1437
|
+
isConstant() {
|
|
1438
|
+
if (this.isSum() || this.isProduct()) {
|
|
1439
|
+
const elements = this.getElements();
|
|
1440
|
+
for (const x in elements) {
|
|
1441
|
+
if (!elements[x].isConstant()) {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return true;
|
|
1446
|
+
}
|
|
1447
|
+
return (this.isNUM() ||
|
|
1448
|
+
// Test for cases like pi & e
|
|
1449
|
+
(this.value in constants_1.PARSER_CONSTANTS && this.isVAR()) ||
|
|
1450
|
+
// Test for cases like 3^(1/2)
|
|
1451
|
+
(this.isEXP() && this.getBase().isConstant() && this.getPower().isConstant()));
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Tests whether this node's stored value is the Euler-constant symbol `e`.
|
|
1455
|
+
*
|
|
1456
|
+
* The check is representation-level and does not require a unit multiplier or power.
|
|
1457
|
+
*
|
|
1458
|
+
* @returns `true` when `value` is the current `e` symbol.
|
|
1459
|
+
*/
|
|
1460
|
+
isE() {
|
|
1461
|
+
return this.value === constants_1.E;
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* Checks whether this Expression is an even integer.
|
|
1465
|
+
*
|
|
1466
|
+
* @returns `true` if the expression is `NUM` and its multiplier is even.
|
|
1467
|
+
*
|
|
1468
|
+
* @example
|
|
1469
|
+
* ```ts
|
|
1470
|
+
* Expression.create(4).isEven() // true
|
|
1471
|
+
* Expression.create(3).isEven() // false
|
|
1472
|
+
* ```
|
|
1473
|
+
*/
|
|
1474
|
+
isEven() {
|
|
1475
|
+
return this.isNUM() && this.getMultiplier().isEven();
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Checks whether this Expression has the EXP (exponential/power) internal type.
|
|
1479
|
+
*
|
|
1480
|
+
* @returns `true` if the type is EXP.
|
|
1481
|
+
*/
|
|
1482
|
+
isEXP() {
|
|
1483
|
+
return this.type === EXP;
|
|
1484
|
+
}
|
|
1485
|
+
isFunction(names) {
|
|
1486
|
+
let retval;
|
|
1487
|
+
if (names === undefined) {
|
|
1488
|
+
retval = this.type === FUN;
|
|
1489
|
+
}
|
|
1490
|
+
else if (typeof names === 'string') {
|
|
1491
|
+
retval = names === this.name;
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
retval = names.includes(String(this.name));
|
|
1495
|
+
}
|
|
1496
|
+
return retval;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Checks whether this Expression is exactly `1/2`.
|
|
1500
|
+
*
|
|
1501
|
+
* @returns `true` if the expression is the numeric value `1/2`.
|
|
1502
|
+
*/
|
|
1503
|
+
isHalf() {
|
|
1504
|
+
if (this.isNUM()) {
|
|
1505
|
+
const m = this.getMultiplier();
|
|
1506
|
+
return m.numerator === 1n && m.denominator === 2n;
|
|
1507
|
+
}
|
|
1508
|
+
return false;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Tests whether this node's stored value is the current imaginary-unit symbol.
|
|
1512
|
+
*
|
|
1513
|
+
* @remarks
|
|
1514
|
+
* This is a representation-level symbol check. It does not require the node to have
|
|
1515
|
+
* multiplier one or power one, so callers that require exactly the mathematical unit
|
|
1516
|
+
* `i` must impose those additional conditions themselves.
|
|
1517
|
+
*
|
|
1518
|
+
* @returns `true` when `value` matches {@link Expression.imaginary}.
|
|
1519
|
+
*/
|
|
1520
|
+
isI() {
|
|
1521
|
+
return this.value === Expression.imaginary;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Evaluates the expression and reports whether the result is classified as complex.
|
|
1525
|
+
*
|
|
1526
|
+
* @remarks
|
|
1527
|
+
* Despite the historical method name, this is not a test for a purely imaginary
|
|
1528
|
+
* value with zero real part. A value such as `3 + 2*i` also returns `true` because
|
|
1529
|
+
* the evaluated result contains a complex component.
|
|
1530
|
+
*
|
|
1531
|
+
* @returns `true` when the evaluated expression satisfies {@link Expression.isComplex}.
|
|
1532
|
+
*/
|
|
1533
|
+
isImaginary() {
|
|
1534
|
+
return this.evaluate().isComplex();
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Checks whether this Expression represents infinity (positive or negative).
|
|
1538
|
+
*
|
|
1539
|
+
* @returns `true` if the type is INF.
|
|
1540
|
+
*/
|
|
1541
|
+
isInf() {
|
|
1542
|
+
return this.type === INF;
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Checks whether this Expression is an exact integer.
|
|
1546
|
+
*
|
|
1547
|
+
* @returns `true` if the expression is `NUM` with an integer multiplier.
|
|
1548
|
+
*
|
|
1549
|
+
* @example
|
|
1550
|
+
* ```ts
|
|
1551
|
+
* Expression.create(5).isInteger() // true
|
|
1552
|
+
* Expression.create('3/2').isInteger() // false
|
|
1553
|
+
* ```
|
|
1554
|
+
*/
|
|
1555
|
+
isInteger() {
|
|
1556
|
+
if (this.isOne()) {
|
|
1557
|
+
return true;
|
|
1558
|
+
}
|
|
1559
|
+
return this.type === NUM && this.getMultiplier().isInteger();
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Checks whether this Expression has power equal to `1` (i.e. is linear in itself).
|
|
1563
|
+
*
|
|
1564
|
+
* @returns `true` if the power is `1`.
|
|
1565
|
+
*/
|
|
1566
|
+
isLinear() {
|
|
1567
|
+
return this.getPower().isOne();
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Checks whether this Expression is exactly `-1`.
|
|
1571
|
+
*
|
|
1572
|
+
* @returns `true` if the expression equals `-1`.
|
|
1573
|
+
*/
|
|
1574
|
+
isMinusOne() {
|
|
1575
|
+
// Avoid creating an object if not needed
|
|
1576
|
+
if (this.isNUM()) {
|
|
1577
|
+
if (this.multiplier === undefined && Number(this.value) === -1) {
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
return this.getMultiplier().isMinusOne();
|
|
1581
|
+
}
|
|
1582
|
+
return false;
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Tests whether both evaluated complex components are small relative to Decimal precision.
|
|
1586
|
+
*
|
|
1587
|
+
* @remarks
|
|
1588
|
+
* The tolerance is `10^(-Decimal.precision + k)`. This is a numerical-algorithm
|
|
1589
|
+
* convenience and must not be used as a replacement for exact symbolic zero testing;
|
|
1590
|
+
* use {@link Expression.isZero} when exact representation-level zero is required.
|
|
1591
|
+
*
|
|
1592
|
+
* @param k - Number of guard digits removed from the active Decimal precision.
|
|
1593
|
+
* @returns `true` when the magnitudes of both real and imaginary components are within the tolerance.
|
|
1594
|
+
*/
|
|
1595
|
+
isNearlyZero(k = 8) {
|
|
1596
|
+
const eps = new decimal_js_1.default(10).pow(-decimal_js_1.default.precision + k);
|
|
1597
|
+
const re = this.realPart();
|
|
1598
|
+
const im = this.imagPart();
|
|
1599
|
+
return re.abs().lte(eps) && im.abs().lte(eps);
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Tests whether this expression is established to be strictly less than zero.
|
|
1603
|
+
*
|
|
1604
|
+
* The result follows {@link Expression.lt}: unresolved symbolic sign information
|
|
1605
|
+
* currently produces `false`, while complex values cannot be ordered.
|
|
1606
|
+
*
|
|
1607
|
+
* @returns `true` when the current comparison machinery establishes a negative value.
|
|
1608
|
+
* @throws {@link core!UnsupportedOperationError}
|
|
1609
|
+
* Thrown when the expression is classified as complex.
|
|
1610
|
+
*/
|
|
1611
|
+
isNegative() {
|
|
1612
|
+
return this.lt((0, shortcuts_1.zero)());
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Checks whether this Expression is negative infinity (`−∞`).
|
|
1616
|
+
*
|
|
1617
|
+
* @returns `true` if the expression is `−∞`.
|
|
1618
|
+
*/
|
|
1619
|
+
isNegInf() {
|
|
1620
|
+
return this.isInf() && this.getMultiplier().lt('0');
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Checks whether this Expression has the `NUM` (numeric) internal type.
|
|
1624
|
+
*
|
|
1625
|
+
* This only matches explicit numbers, not symbolic constants like `pi` or `e`.
|
|
1626
|
+
* Use {@link isConstant} to check for all values that reduce to a constant.
|
|
1627
|
+
*
|
|
1628
|
+
* @returns `true` if the type is `NUM`.
|
|
1629
|
+
*
|
|
1630
|
+
* @example
|
|
1631
|
+
* ```ts
|
|
1632
|
+
* Expression.create(5).isNUM() // true
|
|
1633
|
+
* Expression.create('pi').isNUM() // false
|
|
1634
|
+
* ```
|
|
1635
|
+
*/
|
|
1636
|
+
isNUM() {
|
|
1637
|
+
return this.type === NUM;
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Tests whether this Expression is represented as a plain numeric value.
|
|
1641
|
+
*
|
|
1642
|
+
* @returns `true` for NUM expressions.
|
|
1643
|
+
*/
|
|
1644
|
+
isNumber() {
|
|
1645
|
+
return this.isNUM();
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Checks whether this Expression is an odd integer.
|
|
1649
|
+
*
|
|
1650
|
+
* @returns `true` only for numeric integer expressions whose multiplier is odd.
|
|
1651
|
+
*/
|
|
1652
|
+
isOdd() {
|
|
1653
|
+
return this.isInteger() && !this.isEven();
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Checks whether this Expression is exactly `1`.
|
|
1657
|
+
*
|
|
1658
|
+
* @returns `true` if the expression equals `1`.
|
|
1659
|
+
*/
|
|
1660
|
+
isOne() {
|
|
1661
|
+
// Avoid creating an object if not needed
|
|
1662
|
+
if (this.isNUM()) {
|
|
1663
|
+
const m = this.getMultiplier();
|
|
1664
|
+
return m.numerator === 1n && m.denominator === 1n;
|
|
1665
|
+
}
|
|
1666
|
+
return false;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Tests whether this node's stored value matches a recognized π symbol.
|
|
1670
|
+
*
|
|
1671
|
+
* The check is representation-level and does not require a unit multiplier or power.
|
|
1672
|
+
*
|
|
1673
|
+
* @returns `true` when `value` is one of Nerdamer's π aliases.
|
|
1674
|
+
*/
|
|
1675
|
+
isPi() {
|
|
1676
|
+
return constants_1.PI.includes(this.value);
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Checks whether this Expression is a plain variable with no multiplier and
|
|
1680
|
+
* no power (i.e. multiplier `1`, power `1`, type `VAR`).
|
|
1681
|
+
*
|
|
1682
|
+
* @returns `true` for a bare variable like `x`.
|
|
1683
|
+
*
|
|
1684
|
+
* @example
|
|
1685
|
+
* ```ts
|
|
1686
|
+
* Expression.create('x').isPlainVariable() // true
|
|
1687
|
+
* Expression.create('2*x').isPlainVariable() // false
|
|
1688
|
+
* Expression.create('x^2').isPlainVariable() // false
|
|
1689
|
+
* ```
|
|
1690
|
+
*/
|
|
1691
|
+
isPlainVariable() {
|
|
1692
|
+
return this.isVAR() && this.getMultiplier().isOne() && this.getPower().isOne();
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Tests whether this expression has the structural form of a polynomial over rational coefficients.
|
|
1696
|
+
*
|
|
1697
|
+
* @remarks
|
|
1698
|
+
* The predicate accepts recursively composed sums and products whose outer powers are
|
|
1699
|
+
* non-negative integers. Function nodes, `EXP` nodes, infinities, negative powers,
|
|
1700
|
+
* and fractional powers are rejected. This is a structural eligibility check used by
|
|
1701
|
+
* polynomial-oriented algorithms; it does not construct a {@link algebra!Polynomial}.
|
|
1702
|
+
*
|
|
1703
|
+
* @returns `true` when the expression satisfies the current polynomial-like restrictions.
|
|
1704
|
+
*/
|
|
1705
|
+
isPolynomialLike() {
|
|
1706
|
+
return (0, analysis_2.isPolynomialLike)(this);
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Checks whether this Expression is positive infinity (`+∞`).
|
|
1710
|
+
*
|
|
1711
|
+
* @returns `true` if the expression is `+∞`.
|
|
1712
|
+
*/
|
|
1713
|
+
isPosInf() {
|
|
1714
|
+
return this.isInf() && this.getMultiplier().gt((0, shortcuts_1.zero)());
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Checks whether this Expression has the PRD (product) internal type.
|
|
1718
|
+
*
|
|
1719
|
+
* @returns `true` if the type is PRD.
|
|
1720
|
+
*/
|
|
1721
|
+
isProduct() {
|
|
1722
|
+
return this.type === PRD;
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Checks whether this Expression is exactly `1/4`.
|
|
1726
|
+
*
|
|
1727
|
+
* @returns `true` if the expression is the numeric value `1/4`.
|
|
1728
|
+
*/
|
|
1729
|
+
isQuarter() {
|
|
1730
|
+
if (this.isNUM()) {
|
|
1731
|
+
const m = this.getMultiplier();
|
|
1732
|
+
return m.numerator === 1n && m.denominator === 4n;
|
|
1733
|
+
}
|
|
1734
|
+
return false;
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Checks whether this Expression is a summation-like type (SUM or GRP).
|
|
1738
|
+
*
|
|
1739
|
+
* @returns `true` if the type is SUM or GRP.
|
|
1740
|
+
*/
|
|
1741
|
+
isSum() {
|
|
1742
|
+
return this.type === SUM || this.type === GRP;
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Checks whether this Expression has the VAR (variable) internal type.
|
|
1746
|
+
*
|
|
1747
|
+
* @returns `true` if the type is VAR.
|
|
1748
|
+
*/
|
|
1749
|
+
isVAR() {
|
|
1750
|
+
return this.type === VAR;
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Checks whether this Expression is exactly `0`.
|
|
1754
|
+
*
|
|
1755
|
+
* @returns `true` if the multiplier is zero.
|
|
1756
|
+
*
|
|
1757
|
+
* @example
|
|
1758
|
+
* ```ts
|
|
1759
|
+
* Expression.create(0).isZero() // true
|
|
1760
|
+
* Expression.create(1).isZero() // false
|
|
1761
|
+
* ```
|
|
1762
|
+
*/
|
|
1763
|
+
isZero() {
|
|
1764
|
+
return this.getMultiplier().isZero();
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Builds the canonical lookup key used to group compatible expression terms.
|
|
1768
|
+
*
|
|
1769
|
+
* @remarks
|
|
1770
|
+
* This is an internal canonicalization key, not a user-facing serialization format.
|
|
1771
|
+
* Numeric nodes normally collapse to {@link Expression.numberHash}; variables,
|
|
1772
|
+
* functions, exponentials, and infinities use multiplier-free identifiers; aggregate
|
|
1773
|
+
* nodes derive a key from their canonical elements. Group (`GRP`) handling can use the
|
|
1774
|
+
* power directly when `isGroup` is requested.
|
|
1775
|
+
*
|
|
1776
|
+
* The exact key format is coupled to parser/algebra combination logic and should not
|
|
1777
|
+
* be persisted as an external interchange format.
|
|
1778
|
+
*
|
|
1779
|
+
* @param asSubExpression - Use the fuller sub-expression representation where supported.
|
|
1780
|
+
* @param isGroup - Build a group-member key from this expression's power.
|
|
1781
|
+
* @returns The canonical lookup key.
|
|
1782
|
+
* @throws Error
|
|
1783
|
+
* Thrown when no key-generation rule exists for the node's internal type.
|
|
1784
|
+
*/
|
|
1785
|
+
keyValue(asSubExpression = false, isGroup = false) {
|
|
1786
|
+
let retval = '';
|
|
1787
|
+
// For GRP the key is always the power
|
|
1788
|
+
if (isGroup) {
|
|
1789
|
+
retval = this.getPower().text();
|
|
1790
|
+
}
|
|
1791
|
+
else {
|
|
1792
|
+
switch (this.type) {
|
|
1793
|
+
case NUM:
|
|
1794
|
+
// If we're appending we need the hash. However when generating a hash, we want the actual value.
|
|
1795
|
+
retval = asSubExpression ? this.value : Expression.numberHash;
|
|
1796
|
+
break;
|
|
1797
|
+
case VAR:
|
|
1798
|
+
case FUN:
|
|
1799
|
+
case EXP:
|
|
1800
|
+
case INF:
|
|
1801
|
+
// Return the id string since we don't want the multiplier for comparison.
|
|
1802
|
+
retval = asSubExpression ? this.idString() : this.value;
|
|
1803
|
+
break;
|
|
1804
|
+
case GRP:
|
|
1805
|
+
retval = asSubExpression
|
|
1806
|
+
? this.text()
|
|
1807
|
+
: Object.values(this.getElements())[0].keyValue();
|
|
1808
|
+
break;
|
|
1809
|
+
case SUM:
|
|
1810
|
+
case PRD:
|
|
1811
|
+
// e.g. (1+x) can only directly operate on (1+x) and not (1+2*x), (2+x), etc without inspecting each one
|
|
1812
|
+
retval = Expression.getValue(Object.values(this.getElements()), 'text', this.type);
|
|
1813
|
+
break;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
if (!retval) {
|
|
1817
|
+
throw new Error(`The function 'keyValue' not yet implemented for type ${this.type}`);
|
|
1818
|
+
}
|
|
1819
|
+
return retval;
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Tests whether this expression is `<` another value.
|
|
1823
|
+
*
|
|
1824
|
+
* @remarks
|
|
1825
|
+
* Nerdamer consults applicable assumptions and otherwise evaluates the difference
|
|
1826
|
+
* numerically/symbolically. These `Expression` comparison methods remain boolean for
|
|
1827
|
+
* compatibility. An unknown assumption result falls through to the ordinary comparison
|
|
1828
|
+
* path; if the relation still cannot be established, the method returns `false`. The
|
|
1829
|
+
* lower-level `Assumption` relations preserve unknown as `undefined`. Complex values are
|
|
1830
|
+
* not ordered and cause the comparison to throw.
|
|
1831
|
+
*
|
|
1832
|
+
* @param x - Value to compare with this expression.
|
|
1833
|
+
* @returns `true` when the requested ordering is established, otherwise `false`.
|
|
1834
|
+
* @throws {@link core!UnsupportedOperationError}
|
|
1835
|
+
* Thrown when either side is classified as complex.
|
|
1836
|
+
*/
|
|
1837
|
+
lt(x) {
|
|
1838
|
+
x = Expression.create(x);
|
|
1839
|
+
return (0, compare_1.lt)(this, x);
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Tests whether this expression is `<=` another value.
|
|
1843
|
+
*
|
|
1844
|
+
* @remarks
|
|
1845
|
+
* Nerdamer consults applicable assumptions and otherwise evaluates the difference
|
|
1846
|
+
* numerically/symbolically. These `Expression` comparison methods remain boolean for
|
|
1847
|
+
* compatibility. An unknown assumption result falls through to the ordinary comparison
|
|
1848
|
+
* path; if the relation still cannot be established, the method returns `false`. The
|
|
1849
|
+
* lower-level `Assumption` relations preserve unknown as `undefined`. Complex values are
|
|
1850
|
+
* not ordered and cause the comparison to throw.
|
|
1851
|
+
*
|
|
1852
|
+
* @param x - Value to compare with this expression.
|
|
1853
|
+
* @returns `true` when the requested ordering is established, otherwise `false`.
|
|
1854
|
+
* @throws {@link core!UnsupportedOperationError}
|
|
1855
|
+
* Thrown when either side is classified as complex.
|
|
1856
|
+
*/
|
|
1857
|
+
lte(x) {
|
|
1858
|
+
x = Expression.create(x);
|
|
1859
|
+
return (0, compare_1.lte)(this, x);
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Subtracts `x` from this Expression.
|
|
1863
|
+
*
|
|
1864
|
+
* @param x - The value to subtract.
|
|
1865
|
+
* @returns A new Expression representing `this − x`.
|
|
1866
|
+
*
|
|
1867
|
+
* @example
|
|
1868
|
+
* ```ts
|
|
1869
|
+
* Expression.create('x').minus(1).text() // "-1+x"
|
|
1870
|
+
* Expression.create(10).minus(3).text() // "7"
|
|
1871
|
+
* ```
|
|
1872
|
+
*/
|
|
1873
|
+
minus(x) {
|
|
1874
|
+
return (0, subtract_1.subtract)(this, Expression.create(x));
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Computes the modulo of this Expression by `x`.
|
|
1878
|
+
*
|
|
1879
|
+
* @param x - The divisor.
|
|
1880
|
+
* @returns A new Expression representing `this mod x`.
|
|
1881
|
+
*
|
|
1882
|
+
* @example
|
|
1883
|
+
* ```ts
|
|
1884
|
+
* Expression.create(10).mod(3).text() // "1"
|
|
1885
|
+
* ```
|
|
1886
|
+
*/
|
|
1887
|
+
mod(x) {
|
|
1888
|
+
return (0, math_1.mod)(this, Expression.create(x));
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Legacy alias for {@link times}.
|
|
1892
|
+
*
|
|
1893
|
+
* @param x - The value to multiply by.
|
|
1894
|
+
* @returns A new Expression representing `this * x`.
|
|
1895
|
+
*/
|
|
1896
|
+
multiply(x) {
|
|
1897
|
+
return this.times(x);
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Negates this Expression by flipping the sign of the multiplier.
|
|
1901
|
+
*
|
|
1902
|
+
* @returns A new Expression equal to `this * −1`.
|
|
1903
|
+
*
|
|
1904
|
+
* @example
|
|
1905
|
+
* ```ts
|
|
1906
|
+
* Expression.create(5).neg().text() // "-5"
|
|
1907
|
+
* Expression.create('x').neg().text() // "-x"
|
|
1908
|
+
* ```
|
|
1909
|
+
*/
|
|
1910
|
+
neg() {
|
|
1911
|
+
const retval = this.copy();
|
|
1912
|
+
retval.multiplier = retval.getMultiplier().neg();
|
|
1913
|
+
return retval;
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Legacy alias for {@link getNumerator}.
|
|
1917
|
+
*
|
|
1918
|
+
* @returns The numerator Expression.
|
|
1919
|
+
*/
|
|
1920
|
+
numerator() {
|
|
1921
|
+
return this.getNumerator();
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Compatibility alias for the copy-oriented base extraction used by
|
|
1925
|
+
* {@link Expression.toLinearAndUnitMultiplier}.
|
|
1926
|
+
*
|
|
1927
|
+
* @returns An independent Expression representing the node's mathematical base.
|
|
1928
|
+
*/
|
|
1929
|
+
parseValue() {
|
|
1930
|
+
const retval = this.toLinearAndUnitMultiplier();
|
|
1931
|
+
return retval;
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Adds `x` to this Expression.
|
|
1935
|
+
*
|
|
1936
|
+
* @param x - The value to add.
|
|
1937
|
+
* @returns A new Expression representing `this + x`.
|
|
1938
|
+
*
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* Expression.create('x').plus(1).text() // "1+x"
|
|
1942
|
+
* Expression.create(2).plus(3).text() // "5"
|
|
1943
|
+
* ```
|
|
1944
|
+
*/
|
|
1945
|
+
plus(x) {
|
|
1946
|
+
return (0, add_1.add)(this, Expression.create(x));
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Raises this expression to a symbolic or numeric exponent.
|
|
1950
|
+
*
|
|
1951
|
+
* @remarks
|
|
1952
|
+
* The operation delegates to Nerdamer's power canonicalization and simplification
|
|
1953
|
+
* rules. Noninteger and complex powers follow principal-branch semantics; identities
|
|
1954
|
+
* such as distributing a fractional power over arbitrary products are therefore
|
|
1955
|
+
* applied only when the implementation can preserve the relevant branch.
|
|
1956
|
+
*
|
|
1957
|
+
* The receiver is not modified.
|
|
1958
|
+
*
|
|
1959
|
+
* @param x - Exponent to apply.
|
|
1960
|
+
* @returns The simplified power expression.
|
|
1961
|
+
* @throws {@link core!UndefinedError}
|
|
1962
|
+
* Thrown for undefined infinity-related powers handled by the power operation.
|
|
1963
|
+
* @throws {@link core!ZeroToZeroPowerError}
|
|
1964
|
+
* Thrown for zero-power cases classified as undefined by the power operation.
|
|
1965
|
+
*/
|
|
1966
|
+
pow(x) {
|
|
1967
|
+
return (0, power_1.power)(this, Expression.create(x));
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Returns Nerdamer's symbolic decomposition of the real component.
|
|
1971
|
+
*
|
|
1972
|
+
* @remarks
|
|
1973
|
+
* Decomposition follows principal-branch handling for powered complex values.
|
|
1974
|
+
* Explicitly complex function calls that cannot be decomposed are preserved through
|
|
1975
|
+
* a symbolic `realpart(...)` wrapper rather than being guessed. The component
|
|
1976
|
+
* functions `realpart(...)` and `imagpart(...)` are themselves treated as real-valued.
|
|
1977
|
+
*
|
|
1978
|
+
* @returns The symbolic real component.
|
|
1979
|
+
*/
|
|
1980
|
+
realPart() {
|
|
1981
|
+
return (0, complex_1.realPart)(this);
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Returns the sign encoded by this node's coefficient representation.
|
|
1985
|
+
*
|
|
1986
|
+
* @remarks
|
|
1987
|
+
* This is not a general symbolic sign analysis. Most node types return the sign of
|
|
1988
|
+
* their outer {@link Rational} multiplier. `EXP` nodes additionally multiply that
|
|
1989
|
+
* result by the representation-level sign of their stored base. Unknown assumptions
|
|
1990
|
+
* are not inferred here.
|
|
1991
|
+
*
|
|
1992
|
+
* @returns `-1`, `0`, or `1` from the represented coefficient/base sign.
|
|
1993
|
+
*/
|
|
1994
|
+
sign() {
|
|
1995
|
+
// REFACTOR:
|
|
1996
|
+
// Deal with numeric EXP
|
|
1997
|
+
if (this.type === EXP) {
|
|
1998
|
+
return this.getBase().sign() * this.getMultiplier().sign();
|
|
1999
|
+
}
|
|
2000
|
+
return this.getMultiplier().sign();
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Removes the sign represented by this expression's current form.
|
|
2004
|
+
*
|
|
2005
|
+
* @remarks
|
|
2006
|
+
* Complex expressions return their modulus `sqrt(re^2 + im^2)`. For other
|
|
2007
|
+
* expressions, the method copies the node and removes a negative stored sign or
|
|
2008
|
+
* multiplier. This is representation-oriented normalization, not a general
|
|
2009
|
+
* assumption-driven implementation of symbolic `abs(...)`.
|
|
2010
|
+
*
|
|
2011
|
+
* @returns A new sign-free expression or complex modulus.
|
|
2012
|
+
*/
|
|
2013
|
+
signFree() {
|
|
2014
|
+
let retval;
|
|
2015
|
+
if (this.isComplex()) {
|
|
2016
|
+
retval = this.abs();
|
|
2017
|
+
}
|
|
2018
|
+
else {
|
|
2019
|
+
retval = this.copy();
|
|
2020
|
+
const m = retval.getMultiplier();
|
|
2021
|
+
// Remove the minus sign from retval value. This is specifically for EXP of numeric values
|
|
2022
|
+
if (retval.value.startsWith('-')) {
|
|
2023
|
+
retval.value = retval.value.substring(1);
|
|
2024
|
+
}
|
|
2025
|
+
if (m.lt('0')) {
|
|
2026
|
+
retval.multiplier = m.neg();
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return retval;
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Formats this expression using SymPy-style `**` exponentiation syntax.
|
|
2033
|
+
*
|
|
2034
|
+
* @remarks
|
|
2035
|
+
* Formatting temporarily switches the class-wide power-operator token while
|
|
2036
|
+
* delegating to the normal expression formatter, then restores `^`. The expression
|
|
2037
|
+
* itself is not modified.
|
|
2038
|
+
*
|
|
2039
|
+
* @param options - Formatting options accepted by the normal text formatter.
|
|
2040
|
+
* @param asId - Request the internal identifier-oriented formatting mode.
|
|
2041
|
+
* @returns SymPy-compatible expression text.
|
|
2042
|
+
*/
|
|
2043
|
+
sptext(options, asId) {
|
|
2044
|
+
// Store the existing power operator
|
|
2045
|
+
const opr = Expression.POW_OPR;
|
|
2046
|
+
// Use the SymPy type
|
|
2047
|
+
Expression.POW_OPR = '**';
|
|
2048
|
+
const txt = (0, format_1.toText)(this, options, asId, Expression.POW_OPR, Expression.sortFunction);
|
|
2049
|
+
// Put back the original
|
|
2050
|
+
Expression.POW_OPR = opr;
|
|
2051
|
+
return txt;
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* Squares this Expression. Shorthand for `this.pow('2')`.
|
|
2055
|
+
*
|
|
2056
|
+
* @returns A new Expression representing `this²`.
|
|
2057
|
+
*
|
|
2058
|
+
* @example
|
|
2059
|
+
* ```ts
|
|
2060
|
+
* Expression.create('x').sq().text() // "x^2"
|
|
2061
|
+
* Expression.create(5).sq().text() // "25"
|
|
2062
|
+
* ```
|
|
2063
|
+
*/
|
|
2064
|
+
sq() {
|
|
2065
|
+
return this.pow('2');
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Tests Nerdamer equality while also requiring the same top-level internal type.
|
|
2069
|
+
*
|
|
2070
|
+
* @remarks
|
|
2071
|
+
* This is stricter than {@link Expression.eq}, but it is still not object identity or
|
|
2072
|
+
* byte-for-byte tree equality. After verifying matching top-level types (or matching
|
|
2073
|
+
* function names for function nodes), it delegates to Nerdamer's symbolic equality
|
|
2074
|
+
* comparison.
|
|
2075
|
+
*
|
|
2076
|
+
* @param x - Value to compare with this expression.
|
|
2077
|
+
* @returns `true` when the type/name requirement and symbolic equality both hold.
|
|
2078
|
+
*/
|
|
2079
|
+
strictEqual(x) {
|
|
2080
|
+
x = Expression.create(x);
|
|
2081
|
+
const matchesExactly = x.isFunction() && this.isFunction() ? x.name === this.name : x.type === this.type;
|
|
2082
|
+
return matchesExactly && (0, compare_1.equal)(this, x);
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Legacy alias for {@link subst}.
|
|
2086
|
+
*
|
|
2087
|
+
* @param value - The sub-expression to find.
|
|
2088
|
+
* @param withValue - The replacement expression.
|
|
2089
|
+
* @returns A new Expression with the substitution applied.
|
|
2090
|
+
*/
|
|
2091
|
+
sub(value, withValue) {
|
|
2092
|
+
return this.subst(value, withValue);
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Replaces occurrences of one symbolic expression with another.
|
|
2096
|
+
*
|
|
2097
|
+
* @remarks
|
|
2098
|
+
* Substitution supports more than direct variable replacement: the underlying
|
|
2099
|
+
* algorithm can match compatible products and sums, recurse into function arguments,
|
|
2100
|
+
* and substitute within powers. Inputs are converted through
|
|
2101
|
+
* {@link Expression.create}. The receiver is not mutated as part of normal execution. Unchanged
|
|
2102
|
+
* branches can preserve the receiver's identity, and an exact match can return the
|
|
2103
|
+
* supplied replacement object directly, so callers that require independent ownership
|
|
2104
|
+
* should copy the result explicitly.
|
|
2105
|
+
*
|
|
2106
|
+
* @param value - Symbolic value or sub-expression to match.
|
|
2107
|
+
* @param withValue - Replacement value.
|
|
2108
|
+
* @returns The expression produced by the substitution algorithm.
|
|
2109
|
+
*/
|
|
2110
|
+
subst(value, withValue) {
|
|
2111
|
+
value = Expression.create(value);
|
|
2112
|
+
withValue = Expression.create(withValue);
|
|
2113
|
+
return (0, subst_1.subst)(this, value, withValue);
|
|
2114
|
+
// Based on the value:
|
|
2115
|
+
// If it's a number then complain. Invalid LH value
|
|
2116
|
+
// If it's a variable then return the multiplier times that value
|
|
2117
|
+
// If it's a function and the value is equal to that values within some multiplier the sub
|
|
2118
|
+
// otherwise call sub on the arguments.
|
|
2119
|
+
// If it's a sum then loop through each element and sub except for numbers
|
|
2120
|
+
// if a number is encountered and the difference is positive then sub it in and return the difference
|
|
2121
|
+
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Legacy alias for {@link minus}.
|
|
2124
|
+
*
|
|
2125
|
+
* @param x - The value to subtract.
|
|
2126
|
+
* @returns A new Expression representing `this − x`.
|
|
2127
|
+
*/
|
|
2128
|
+
subtract(x) {
|
|
2129
|
+
return this.minus(x);
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Formats this expression using Nerdamer's canonical text formatter.
|
|
2133
|
+
*
|
|
2134
|
+
* @remarks
|
|
2135
|
+
* Exact rational text is the default. Use `sort: true` to render terms in Nerdamer's
|
|
2136
|
+
* conventional display order without changing the stored expression. Decimal output,
|
|
2137
|
+
* precision, and power wrapping can also be selected per call. The `asId` mode is used
|
|
2138
|
+
* by internal identity/canonicalization logic and should not be
|
|
2139
|
+
* treated as a stable interchange format.
|
|
2140
|
+
*
|
|
2141
|
+
* @param options - Text-formatting options.
|
|
2142
|
+
* @param asId - Use the internal identifier-oriented formatting mode.
|
|
2143
|
+
* @returns The formatted expression text.
|
|
2144
|
+
*
|
|
2145
|
+
* @example
|
|
2146
|
+
* ```ts
|
|
2147
|
+
* Expression.create('x^2 + 2*x + 1').text(); // "1+2*x+x^2"
|
|
2148
|
+
* Expression.create('x^2 + 2*x + 1').text({ sort: true }); // "x^2+2*x+1"
|
|
2149
|
+
* Expression.create('1/3').text(); // "1/3"
|
|
2150
|
+
* Expression.create('1/3').text({ decimal: true }); // decimal representation
|
|
2151
|
+
* ```
|
|
2152
|
+
*/
|
|
2153
|
+
text(options, asId) {
|
|
2154
|
+
return (0, format_1.toText)(this, options, asId, Expression.POW_OPR, Expression.sortFunction);
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* Multiplies this Expression by `x`.
|
|
2158
|
+
*
|
|
2159
|
+
* @param x - The value to multiply by.
|
|
2160
|
+
* @returns A new Expression representing `this * x`.
|
|
2161
|
+
*
|
|
2162
|
+
* @example
|
|
2163
|
+
* ```ts
|
|
2164
|
+
* Expression.create('x').times(3).text() // "3*x"
|
|
2165
|
+
* Expression.create('x').times('y').text() // "x*y"
|
|
2166
|
+
* ```
|
|
2167
|
+
*/
|
|
2168
|
+
times(x) {
|
|
2169
|
+
return (0, multiply_1.multiply)(this, Expression.create(x));
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Formats this expression using decimal numeric output.
|
|
2173
|
+
*
|
|
2174
|
+
* This is a formatting operation equivalent to calling {@link Expression.text} with
|
|
2175
|
+
* `{ decimal: true }`; it does not convert the expression tree into a floating-point
|
|
2176
|
+
* data structure.
|
|
2177
|
+
*
|
|
2178
|
+
* @param precision - Optional decimal formatting precision.
|
|
2179
|
+
* @returns Decimal-form expression text.
|
|
2180
|
+
*/
|
|
2181
|
+
toDecimal(precision) {
|
|
2182
|
+
const options = { decimal: true };
|
|
2183
|
+
if (precision !== undefined) {
|
|
2184
|
+
options.precision = precision;
|
|
2185
|
+
}
|
|
2186
|
+
return this.text(options);
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Returns an independent base expression with the outer multiplier and power removed.
|
|
2190
|
+
*
|
|
2191
|
+
* @remarks
|
|
2192
|
+
* Base extraction is defined by {@link Expression.getBase}. Because `getBase()`
|
|
2193
|
+
* exposes an `EXP` node's stored base by reference, this method copies
|
|
2194
|
+
* that base before returning it. Non-`EXP` bases are already independent copies.
|
|
2195
|
+
*
|
|
2196
|
+
* @returns An independent expression representing the node without its outer multiplier and power.
|
|
2197
|
+
*/
|
|
2198
|
+
toLinearAndUnitMultiplier() {
|
|
2199
|
+
let retval = this.getBase();
|
|
2200
|
+
if (this.isEXP()) {
|
|
2201
|
+
retval = retval.copy();
|
|
2202
|
+
}
|
|
2203
|
+
return retval;
|
|
2204
|
+
}
|
|
2205
|
+
// /**
|
|
2206
|
+
// * Converts the expression to a predictable (standard) string for easy matching with respect to a variable.
|
|
2207
|
+
// *
|
|
2208
|
+
// * @param variable
|
|
2209
|
+
// * @returns
|
|
2210
|
+
// */
|
|
2211
|
+
// toPattern(variable: string) {
|
|
2212
|
+
// return toPattern(this, variable);
|
|
2213
|
+
// }
|
|
2214
|
+
/**
|
|
2215
|
+
* Returns the string representation. Alias for {@link text}.
|
|
2216
|
+
*
|
|
2217
|
+
* @param options - Formatting options passed to {@link text}.
|
|
2218
|
+
* @returns The text representation.
|
|
2219
|
+
*/
|
|
2220
|
+
toString(options) {
|
|
2221
|
+
return this.text(options);
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Returns the total degree of a monomial by summing the powers of all factors
|
|
2225
|
+
* in a product expression. For non-product types, returns the expression's own power.
|
|
2226
|
+
*
|
|
2227
|
+
* @returns The total power as an Expression.
|
|
2228
|
+
*
|
|
2229
|
+
* @example
|
|
2230
|
+
* ```ts
|
|
2231
|
+
* Expression.create('x^2*y^3').totalPower().text() // "5"
|
|
2232
|
+
* Expression.create('x^4').totalPower().text() // "4"
|
|
2233
|
+
* ```
|
|
2234
|
+
*/
|
|
2235
|
+
totalPower() {
|
|
2236
|
+
if (!this.isProduct()) {
|
|
2237
|
+
return this.getPower();
|
|
2238
|
+
}
|
|
2239
|
+
let retval = Expression.Number('0');
|
|
2240
|
+
const elements = this.getElements();
|
|
2241
|
+
for (const x in elements) {
|
|
2242
|
+
retval = retval.plus(elements[x].getPower());
|
|
2243
|
+
}
|
|
2244
|
+
return retval;
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Returns a copy of this Expression with the multiplier removed (set to `1`).
|
|
2248
|
+
*
|
|
2249
|
+
* For `NUM` types (where the multiplier *is* the value), returns `1`.
|
|
2250
|
+
*
|
|
2251
|
+
* @returns A new multiplier-free Expression.
|
|
2252
|
+
*
|
|
2253
|
+
* @example
|
|
2254
|
+
* ```ts
|
|
2255
|
+
* Expression.create('3*x').toUnitMultiplier().text() // "x"
|
|
2256
|
+
* Expression.create(5).toUnitMultiplier().text() // "1"
|
|
2257
|
+
* ```
|
|
2258
|
+
*/
|
|
2259
|
+
toUnitMultiplier() {
|
|
2260
|
+
let retval;
|
|
2261
|
+
if (this.isNUM()) {
|
|
2262
|
+
// Since type NUM carries its value in the multiplier,
|
|
2263
|
+
// the remainder is just one
|
|
2264
|
+
retval = (0, shortcuts_1.one)();
|
|
2265
|
+
}
|
|
2266
|
+
else {
|
|
2267
|
+
retval = this.copy();
|
|
2268
|
+
delete retval.multiplier;
|
|
2269
|
+
}
|
|
2270
|
+
return retval;
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Regenerates the stored `value` string from mutable structural state where supported.
|
|
2274
|
+
*
|
|
2275
|
+
* @remarks
|
|
2276
|
+
* Function nodes rebuild `value` from their name and arguments. Product and sum-like
|
|
2277
|
+
* nodes rebuild it from their current elements. Other node types are left unchanged.
|
|
2278
|
+
* This method mutates the receiver and returns the same object for chaining.
|
|
2279
|
+
*
|
|
2280
|
+
* @returns This expression after updating its stored value where applicable.
|
|
2281
|
+
*/
|
|
2282
|
+
updateValue() {
|
|
2283
|
+
if (this.isFunction()) {
|
|
2284
|
+
this.value = `${this.name}(${this.getArguments()
|
|
2285
|
+
.map(x => x.text())
|
|
2286
|
+
.join(', ')})`;
|
|
2287
|
+
}
|
|
2288
|
+
else if (this.isProduct() || this.isSum()) {
|
|
2289
|
+
this.value = Expression.getValue(this.elementsArray(), 'text', this.type);
|
|
2290
|
+
}
|
|
2291
|
+
return this;
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Provides the primitive value used by JavaScript coercion.
|
|
2295
|
+
*
|
|
2296
|
+
* Numeric (`NUM`) expressions return the primitive value of their rational
|
|
2297
|
+
* multiplier. Other expressions return decimal-formatted text. This coercion API is
|
|
2298
|
+
* intended for JavaScript interoperability; use explicit symbolic comparison methods
|
|
2299
|
+
* when mathematical equality or ordering is required.
|
|
2300
|
+
*
|
|
2301
|
+
* @returns A number for numeric nodes, otherwise decimal-form expression text.
|
|
2302
|
+
*/
|
|
2303
|
+
valueOf() {
|
|
2304
|
+
let retval;
|
|
2305
|
+
if (this.isNUM()) {
|
|
2306
|
+
retval = this.getMultiplier().valueOf();
|
|
2307
|
+
}
|
|
2308
|
+
else {
|
|
2309
|
+
retval = this.text({ decimal: true });
|
|
2310
|
+
}
|
|
2311
|
+
return retval;
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* Collects distinct free-variable names from this expression tree.
|
|
2315
|
+
*
|
|
2316
|
+
* @remarks
|
|
2317
|
+
* Only `VAR` nodes are collected. The current imaginary-unit symbol and names in
|
|
2318
|
+
* {@link Expression.RESERVED} are excluded. Traversal includes function arguments,
|
|
2319
|
+
* aggregate elements, and `EXP` bases and powers. Encounter order is preserved unless
|
|
2320
|
+
* the caller sorts the returned array separately.
|
|
2321
|
+
*
|
|
2322
|
+
* If `vars` is supplied, new names are appended to that same accumulator without
|
|
2323
|
+
* duplicating names already present.
|
|
2324
|
+
*
|
|
2325
|
+
* @param vars - Optional accumulator of names already collected.
|
|
2326
|
+
* @returns The accumulator containing distinct variable names.
|
|
2327
|
+
*/
|
|
2328
|
+
variables(vars) {
|
|
2329
|
+
return (0, traversal_2.variables)(this, vars, Expression.RESERVED);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
exports.Expression = Expression;
|