nerdamer 1.1.13 → 2.0.0-rc.2
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/BREAKING_CHANGES.md +244 -5
- package/LICENSE.md +184 -0
- package/README.md +232 -318
- 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 +2248 -0
- package/index.d.ts +10 -426
- package/output/algebra/adapters.d.ts +3 -0
- package/output/algebra/adapters.js +11 -0
- package/output/algebra/algorithms/arith.d.ts +84 -0
- package/output/algebra/algorithms/arith.js +324 -0
- package/output/algebra/algorithms/groebnerBase.d.ts +161 -0
- package/output/algebra/algorithms/groebnerBase.js +929 -0
- package/output/algebra/dispatch.d.ts +1 -0
- package/output/algebra/dispatch.js +25 -0
- package/output/algebra/factor/factor.d.ts +77 -0
- package/output/algebra/factor/factor.js +245 -0
- package/output/algebra/gcd/gcd.d.ts +30 -0
- package/output/algebra/gcd/gcd.js +95 -0
- package/output/algebra/groebner.d.ts +21 -0
- package/output/algebra/groebner.js +36 -0
- package/output/algebra/partfrac.d.ts +19 -0
- package/output/algebra/partfrac.js +365 -0
- package/output/algebra/polynomial/ModularSparsePolynomial.d.ts +131 -0
- package/output/algebra/polynomial/ModularSparsePolynomial.js +378 -0
- package/output/algebra/polynomial/ModularSparsePolynomialFactor.d.ts +40 -0
- package/output/algebra/polynomial/ModularSparsePolynomialFactor.js +661 -0
- package/output/algebra/polynomial/SparsePolynomialFactor.d.ts +64 -0
- package/output/algebra/polynomial/SparsePolynomialFactor.js +492 -0
- package/output/algebra/polynomial/SparsePolynomialGcd.d.ts +29 -0
- package/output/algebra/polynomial/SparsePolynomialGcd.js +259 -0
- package/output/algebra/polynomial/SparsePolynomialMultivariateFactor.d.ts +82 -0
- package/output/algebra/polynomial/SparsePolynomialMultivariateFactor.js +604 -0
- package/output/algebra/polynomial/modularGcd.d.ts +40 -0
- package/output/algebra/polynomial/modularGcd.js +365 -0
- package/output/algebra/polynomialize.d.ts +20 -0
- package/output/algebra/polynomialize.js +26 -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 +237 -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 +8 -0
- package/output/api/advanced.js +18 -0
- package/output/api/algebra.d.ts +17 -0
- package/output/api/algebra.js +35 -0
- package/output/api/assumptions.d.ts +4 -0
- package/output/api/assumptions.js +11 -0
- package/output/api/calculus.d.ts +10 -0
- package/output/api/calculus.js +22 -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/languages/deu.d.ts +3 -0
- package/output/api/languages/deu.js +266 -0
- package/output/api/languages/fra.d.ts +3 -0
- package/output/api/languages/fra.js +266 -0
- package/output/api/languages/ita.d.ts +3 -0
- package/output/api/languages/ita.js +266 -0
- package/output/api/languages/nld.d.ts +3 -0
- package/output/api/languages/nld.js +266 -0
- package/output/api/languages/por.d.ts +3 -0
- package/output/api/languages/por.js +266 -0
- package/output/api/languages/spa.d.ts +3 -0
- package/output/api/languages/spa.js +266 -0
- package/output/api/parser.d.ts +93 -0
- package/output/api/parser.js +18 -0
- package/output/api/solve.d.ts +10 -0
- package/output/api/solve.js +17 -0
- package/output/api/structures.d.ts +10 -0
- package/output/api/structures.js +23 -0
- package/output/calculus/adapters.d.ts +8 -0
- package/output/calculus/adapters.js +41 -0
- package/output/calculus/derivative/diff.d.ts +30 -0
- package/output/calculus/derivative/diff.js +241 -0
- package/output/calculus/dispatch.d.ts +1 -0
- package/output/calculus/dispatch.js +25 -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/classes/assumption/Assumption.d.ts +218 -0
- package/output/core/classes/assumption/Assumption.js +609 -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/assumption/dispatch.d.ts +1 -0
- package/output/core/classes/assumption/dispatch.js +11 -0
- package/output/core/classes/collection/Collection.d.ts +107 -0
- package/output/core/classes/collection/Collection.js +240 -0
- package/output/core/classes/complex/Complex.d.ts +109 -0
- package/output/core/classes/complex/Complex.js +153 -0
- package/output/core/classes/decimalSet/DecimalSet.d.ts +226 -0
- package/output/core/classes/decimalSet/DecimalSet.js +454 -0
- package/output/core/classes/dictionary/Dictionary.d.ts +102 -0
- package/output/core/classes/dictionary/Dictionary.js +216 -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 +197 -0
- package/output/core/classes/expression/Expression.d.ts +1691 -0
- package/output/core/classes/expression/Expression.js +2381 -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 +54 -0
- package/output/core/classes/expression/format.js +340 -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 +61 -0
- package/output/core/classes/expression/utils.js +235 -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 +901 -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/dispatch.d.ts +1 -0
- package/output/core/classes/matrix/dispatch.js +19 -0
- package/output/core/classes/matrix/functions.d.ts +28 -0
- package/output/core/classes/matrix/functions.js +57 -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 +493 -0
- package/output/core/classes/parser/Parser.js +1863 -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 +162 -0
- package/output/core/classes/parser/constants.js +229 -0
- package/output/core/classes/parser/controlFlowSignals.d.ts +38 -0
- package/output/core/classes/parser/controlFlowSignals.js +60 -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 +236 -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 +474 -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 +438 -0
- package/output/core/classes/parser/operations/multiply.d.ts +11 -0
- package/output/core/classes/parser/operations/multiply.js +328 -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 +36 -0
- package/output/core/classes/parser/preprocess.d.ts +7 -0
- package/output/core/classes/parser/preprocess.js +246 -0
- package/output/core/classes/parser/scripting/controlFlow.d.ts +23 -0
- package/output/core/classes/parser/scripting/controlFlow.js +279 -0
- package/output/core/classes/parser/scripting/deferred.d.ts +4 -0
- package/output/core/classes/parser/scripting/deferred.js +65 -0
- package/output/core/classes/parser/scripting/dispatch.d.ts +1 -0
- package/output/core/classes/parser/scripting/dispatch.js +62 -0
- package/output/core/classes/parser/scripting/evaluate.d.ts +23 -0
- package/output/core/classes/parser/scripting/evaluate.js +128 -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 +8 -0
- package/output/core/classes/parser/scripting/scope.js +132 -0
- package/output/core/classes/parser/types.d.ts +130 -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 +504 -0
- package/output/core/classes/polynomial/Polynomial.js +1195 -0
- package/output/core/classes/polynomial/SparsePolynomial.d.ts +221 -0
- package/output/core/classes/polynomial/SparsePolynomial.js +824 -0
- package/output/core/classes/polynomial/SparsePolynomialAdapter.d.ts +56 -0
- package/output/core/classes/polynomial/SparsePolynomialAdapter.js +241 -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/adapters.d.ts +18 -0
- package/output/core/classes/polynomial/adapters.js +33 -0
- package/output/core/classes/polynomial/dispatch.d.ts +1 -0
- package/output/core/classes/polynomial/dispatch.js +14 -0
- package/output/core/classes/polynomial/functions.d.ts +77 -0
- package/output/core/classes/polynomial/functions.js +189 -0
- package/output/core/classes/polynomial/utils.d.ts +62 -0
- package/output/core/classes/polynomial/utils.js +207 -0
- package/output/core/classes/rational/Rational.d.ts +501 -0
- package/output/core/classes/rational/Rational.js +829 -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 +187 -0
- package/output/core/classes/valuesSet/ValuesSet.js +399 -0
- package/output/core/classes/vector/Vector.d.ts +222 -0
- package/output/core/classes/vector/Vector.js +501 -0
- package/output/core/classes/vector/dispatch.d.ts +1 -0
- package/output/core/classes/vector/dispatch.js +11 -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 +149 -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 +62 -0
- package/output/core/common/common.js +74 -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 +806 -0
- package/output/core/converters/Pattern.d.ts +71 -0
- package/output/core/converters/Pattern.js +302 -0
- package/output/core/dispatch.d.ts +52 -0
- package/output/core/dispatch.js +26 -0
- package/output/core/errors.d.ts +316 -0
- package/output/core/errors.js +349 -0
- package/output/core/fullFunctions.d.ts +7 -0
- package/output/core/fullFunctions.js +31 -0
- package/output/core/functions/bigint/bigint.d.ts +133 -0
- package/output/core/functions/bigint/bigint.js +444 -0
- package/output/core/functions/bigint/primeFactor.d.ts +51 -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 +139 -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.dispatch.d.ts +1 -0
- package/output/core/functions/complex.dispatch.js +16 -0
- package/output/core/functions/complex.js +482 -0
- package/output/core/functions/decimal.d.ts +24 -0
- package/output/core/functions/decimal.js +665 -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 +102 -0
- package/output/core/functions/subst.d.ts +90 -0
- package/output/core/functions/subst.js +506 -0
- package/output/core/functions/utils.d.ts +24 -0
- package/output/core/functions/utils.js +51 -0
- package/output/core/parserFunctions.d.ts +7 -0
- package/output/core/parserFunctions.js +25 -0
- package/output/core/types.d.ts +48 -0
- package/output/core/types.js +2 -0
- package/output/index.d.ts +359 -0
- package/output/index.js +739 -0
- package/output/math/defint/defint.d.ts +18 -0
- package/output/math/defint/defint.js +60 -0
- package/output/math/defint/defintDecimal.d.ts +37 -0
- package/output/math/defint/defintDecimal.js +321 -0
- package/output/math/defint/defintNative.d.ts +57 -0
- package/output/math/defint/defintNative.js +281 -0
- package/output/math/dispatch.d.ts +1 -0
- package/output/math/dispatch.js +101 -0
- package/output/math/geometry.d.ts +12 -0
- package/output/math/geometry.js +55 -0
- package/output/math/math.d.ts +695 -0
- package/output/math/math.js +1955 -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 +66 -0
- package/output/math/utils.js +219 -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 +104 -0
- package/output/solve/classes/PolynomialSolver.js +492 -0
- package/output/solve/classes/SolutionSet.d.ts +172 -0
- package/output/solve/classes/SolutionSet.js +434 -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/dispatch.d.ts +1 -0
- package/output/solve/dispatch.js +13 -0
- package/output/solve/linsolve.d.ts +53 -0
- package/output/solve/linsolve.js +233 -0
- package/output/solve/solve.d.ts +44 -0
- package/output/solve/solve.js +368 -0
- package/output/solve/solveSystem.d.ts +31 -0
- package/output/solve/solveSystem.js +266 -0
- package/output/solve/utils.d.ts +10 -0
- package/output/solve/utils.js +58 -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 +296 -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 +178 -57
- package/.travis.yml +0 -3
- package/Algebra.js +0 -4568
- 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,929 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module groebner
|
|
4
|
+
*
|
|
5
|
+
* Gröbner basis computation, elimination, ideal membership, and solving.
|
|
6
|
+
*
|
|
7
|
+
* This implementation is a Buchberger-style engine with:
|
|
8
|
+
* - configurable monomial order (LEX, GRLEX, GREVLEX)
|
|
9
|
+
* - sugar-strategy pair selection for performance
|
|
10
|
+
* - optional budgets and stats
|
|
11
|
+
* - fraction-free normal form helpers
|
|
12
|
+
* - elimination ideals
|
|
13
|
+
* - ideal membership testing
|
|
14
|
+
* - triangular back-substitution solver
|
|
15
|
+
*
|
|
16
|
+
* External references consulted:
|
|
17
|
+
* - SymPy Gröbner basis implementation:
|
|
18
|
+
* https://github.com/sympy/sympy/blob/120ee85f346f6292b763cc195afda4f907260d4a/sympy/polys/groebnertools.py
|
|
19
|
+
* - Giovini, Mora, Niesi, Robbiano, Traverso, "One Sugar Cube, Please":
|
|
20
|
+
* https://doi.org/10.1145/120694.120701
|
|
21
|
+
* - Bigatti, Caboara, Robbiano, "Computing inhomogeneous Gröbner bases":
|
|
22
|
+
* https://doi.org/10.1016/j.jsc.2010.10.002
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.GroebnerBudgetExceeded = void 0;
|
|
26
|
+
exports.Groebner = Groebner;
|
|
27
|
+
exports.groebnerBasisWithOptions = groebnerBasisWithOptions;
|
|
28
|
+
exports.eliminate = eliminate;
|
|
29
|
+
exports.idealMembership = idealMembership;
|
|
30
|
+
exports.reduceByBasis = reduceByBasis;
|
|
31
|
+
exports.solve = solve;
|
|
32
|
+
const SparsePolynomial_1 = require("../../core/classes/polynomial/SparsePolynomial");
|
|
33
|
+
const errors_1 = require("../../core/errors");
|
|
34
|
+
const bigint_1 = require("../../core/functions/bigint/bigint");
|
|
35
|
+
/**
|
|
36
|
+
* Thrown when a Groebner pair-count or basis-size budget is exhausted.
|
|
37
|
+
*
|
|
38
|
+
* {@link GroebnerBudgetExceeded.stats} is the live counter snapshot from the aborted
|
|
39
|
+
* computation. The error keeps that object by reference and does not freeze it.
|
|
40
|
+
*/
|
|
41
|
+
class GroebnerBudgetExceeded extends Error {
|
|
42
|
+
/** Work counters at the point the budget was exceeded. */
|
|
43
|
+
stats;
|
|
44
|
+
/**
|
|
45
|
+
* @param message - Human-readable budget condition.
|
|
46
|
+
* @param stats - Counter snapshot retained on the error.
|
|
47
|
+
*/
|
|
48
|
+
constructor(message, stats) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'GroebnerBudgetExceeded';
|
|
51
|
+
this.stats = stats;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.GroebnerBudgetExceeded = GroebnerBudgetExceeded;
|
|
55
|
+
function sparseOrder(order) {
|
|
56
|
+
if (order === 'LEX') {
|
|
57
|
+
return 'lex';
|
|
58
|
+
}
|
|
59
|
+
if (order === 'GRLEX') {
|
|
60
|
+
return 'grlex';
|
|
61
|
+
}
|
|
62
|
+
return 'grevlex';
|
|
63
|
+
}
|
|
64
|
+
function ltOrNull(p, order) {
|
|
65
|
+
const leading = p.leadingTerm(sparseOrder(order));
|
|
66
|
+
return leading === null ? null : { exp: leading.exponents, coeff: leading.coefficient };
|
|
67
|
+
}
|
|
68
|
+
function polyKey(p) {
|
|
69
|
+
const parts = p
|
|
70
|
+
.terms()
|
|
71
|
+
.map(term => `${term.exponents.join(',')}:${term.coefficient.toString()}`);
|
|
72
|
+
parts.sort();
|
|
73
|
+
return parts.join('|');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Primitive normalization under a given order:
|
|
77
|
+
* - divide by coefficient content
|
|
78
|
+
* - ensure leading coefficient under `order` is positive
|
|
79
|
+
* - make monic when exact integer division permits it
|
|
80
|
+
*/
|
|
81
|
+
function primitiveNormalizeOrder(p0, order) {
|
|
82
|
+
if (p0.isZero()) {
|
|
83
|
+
return SparsePolynomial_1.SparsePolynomial.zero(p0.variableCount);
|
|
84
|
+
}
|
|
85
|
+
let p = p0.primitivePart().normalizeLeadingSign(sparseOrder(order));
|
|
86
|
+
const lt = ltOrNull(p, order);
|
|
87
|
+
if (lt && lt.coeff !== 0n && lt.coeff !== 1n) {
|
|
88
|
+
let exact = true;
|
|
89
|
+
for (const term of p.terms()) {
|
|
90
|
+
if (term.coefficient % lt.coeff !== 0n) {
|
|
91
|
+
exact = false;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (exact) {
|
|
96
|
+
p = p.divideByScalarExact(lt.coeff);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return p;
|
|
100
|
+
}
|
|
101
|
+
function expLcmDense(a, b) {
|
|
102
|
+
if (a.length !== b.length) {
|
|
103
|
+
throw new RangeError((0, errors_1.message)('multidegreeMismatch'));
|
|
104
|
+
}
|
|
105
|
+
const out = new Array(a.length);
|
|
106
|
+
for (let i = 0; i < a.length; i++) {
|
|
107
|
+
out[i] = a[i] > b[i] ? a[i] : b[i];
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
function monoDividesDense(a, b) {
|
|
112
|
+
if (a.length !== b.length) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < a.length; i++) {
|
|
116
|
+
if (a[i] > b[i]) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
function monoSubDense(b, a) {
|
|
123
|
+
if (a.length !== b.length) {
|
|
124
|
+
throw new RangeError((0, errors_1.message)('multidegreeMismatch'));
|
|
125
|
+
}
|
|
126
|
+
const out = new Array(a.length);
|
|
127
|
+
for (let i = 0; i < a.length; i++) {
|
|
128
|
+
out[i] = b[i] - a[i];
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function monomialTotalDegreeDense(monomial) {
|
|
133
|
+
let degree = 0n;
|
|
134
|
+
for (const exponent of monomial) {
|
|
135
|
+
degree += exponent;
|
|
136
|
+
}
|
|
137
|
+
return degree;
|
|
138
|
+
}
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// S-polynomial (fraction-free)
|
|
141
|
+
// ============================================================================
|
|
142
|
+
function sPolynomialFF(f, g, ltF, ltG) {
|
|
143
|
+
const lcm = expLcmDense(ltF.exp, ltG.exp);
|
|
144
|
+
const fMultiplier = monoSubDense(lcm, ltF.exp);
|
|
145
|
+
const gMultiplier = monoSubDense(lcm, ltG.exp);
|
|
146
|
+
return f.monomialScaleSubtract(ltG.coeff, fMultiplier, g, ltF.coeff, gMultiplier);
|
|
147
|
+
}
|
|
148
|
+
function reducerViews(basis, order, normalize) {
|
|
149
|
+
const reducers = [];
|
|
150
|
+
for (const source of basis) {
|
|
151
|
+
const poly = normalize ? primitiveNormalizeOrder(source, order) : source;
|
|
152
|
+
if (poly.isZero()) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const lt = ltOrNull(poly, order);
|
|
156
|
+
if (lt !== null) {
|
|
157
|
+
reducers.push({ poly, lt });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return reducers;
|
|
161
|
+
}
|
|
162
|
+
/** Head reduction using leading terms only and exact integer arithmetic. */
|
|
163
|
+
function normalFormFractionFree(f0, basis, order, reducers = reducerViews(basis, order, false), sugarState) {
|
|
164
|
+
let f = f0;
|
|
165
|
+
if (f.isZero()) {
|
|
166
|
+
return f;
|
|
167
|
+
}
|
|
168
|
+
while (true) {
|
|
169
|
+
const ltF = ltOrNull(f, order);
|
|
170
|
+
if (!ltF) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
let reduced = false;
|
|
174
|
+
for (let reducerIndex = 0; reducerIndex < reducers.length; reducerIndex++) {
|
|
175
|
+
const { poly: g, lt: ltG } = reducers[reducerIndex];
|
|
176
|
+
if (!monoDividesDense(ltG.exp, ltF.exp)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const multExp = monoSubDense(ltF.exp, ltG.exp);
|
|
180
|
+
if (sugarState) {
|
|
181
|
+
const reductionSugar = sugarState.reducerSugars[reducerIndex] + monomialTotalDegreeDense(multExp);
|
|
182
|
+
if (reductionSugar > sugarState.value) {
|
|
183
|
+
sugarState.value = reductionSugar;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
f = f.scaleSubtractMonomial(ltG.coeff, g, ltF.coeff, multExp);
|
|
187
|
+
reduced = true;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
if (!reduced) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return f;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Fraction-free reduction allowing reduction of any term.
|
|
198
|
+
*
|
|
199
|
+
* For a term t in f and a reducer g whose leading monomial divides t:
|
|
200
|
+
* f := lc(g) * f - coeff(t) * x^(tExp - LM(g)) * g
|
|
201
|
+
*/
|
|
202
|
+
function normalFormFractionFreeAllTerms(f0, basis, order, normalizedBasis = reducerViews(basis, order, true), sugarState) {
|
|
203
|
+
let f = primitiveNormalizeOrder(f0, order);
|
|
204
|
+
if (f.isZero() || normalizedBasis.length === 0) {
|
|
205
|
+
return f;
|
|
206
|
+
}
|
|
207
|
+
const monomialOrder = sparseOrder(order);
|
|
208
|
+
while (true) {
|
|
209
|
+
let selected = null;
|
|
210
|
+
let selectedReducer = null;
|
|
211
|
+
let selectedReducerIndex = -1;
|
|
212
|
+
for (const term of f.terms()) {
|
|
213
|
+
let reducer = null;
|
|
214
|
+
let reducerIndex = -1;
|
|
215
|
+
for (let i = 0; i < normalizedBasis.length; i++) {
|
|
216
|
+
const candidate = normalizedBasis[i];
|
|
217
|
+
if (monoDividesDense(candidate.lt.exp, term.exponents)) {
|
|
218
|
+
reducer = candidate;
|
|
219
|
+
reducerIndex = i;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (reducer !== null &&
|
|
224
|
+
(selected === null ||
|
|
225
|
+
SparsePolynomial_1.SparsePolynomial.compareMonomials(term.exponents, selected.exponents, monomialOrder) > 0)) {
|
|
226
|
+
selected = term;
|
|
227
|
+
selectedReducer = reducer;
|
|
228
|
+
selectedReducerIndex = reducerIndex;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (selected === null || selectedReducer === null) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
const multExp = monoSubDense(selected.exponents, selectedReducer.lt.exp);
|
|
235
|
+
if (sugarState) {
|
|
236
|
+
const reductionSugar = sugarState.reducerSugars[selectedReducerIndex] +
|
|
237
|
+
monomialTotalDegreeDense(multExp);
|
|
238
|
+
if (reductionSugar > sugarState.value) {
|
|
239
|
+
sugarState.value = reductionSugar;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
f = primitiveNormalizeOrder(f.scaleSubtractMonomial(selectedReducer.lt.coeff, selectedReducer.poly, selected.coefficient, multExp), order);
|
|
243
|
+
}
|
|
244
|
+
return f;
|
|
245
|
+
}
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Canonicalization
|
|
248
|
+
// ============================================================================
|
|
249
|
+
function variablePresence(p) {
|
|
250
|
+
const presence = new Array(p.variableCount).fill(false);
|
|
251
|
+
for (const variableIndex of p.variables()) {
|
|
252
|
+
presence[variableIndex] = true;
|
|
253
|
+
}
|
|
254
|
+
return presence;
|
|
255
|
+
}
|
|
256
|
+
function canonicalizeBasis(inputBasis, order) {
|
|
257
|
+
const kept = [];
|
|
258
|
+
const keptHeadReducers = [];
|
|
259
|
+
const keptNormalizedReducers = [];
|
|
260
|
+
const input = inputBasis.filter(p => !p.isZero());
|
|
261
|
+
// 1) Reduce each element against what we've already kept, then normalize.
|
|
262
|
+
for (const p of input) {
|
|
263
|
+
const headReduced = normalFormFractionFree(p, kept, order, keptHeadReducers);
|
|
264
|
+
const normalized = normalFormFractionFreeAllTerms(headReduced, kept, order, keptNormalizedReducers);
|
|
265
|
+
if (!normalized.isZero()) {
|
|
266
|
+
kept.push(normalized);
|
|
267
|
+
keptHeadReducers.push(reducerViews([normalized], order, false)[0]);
|
|
268
|
+
keptNormalizedReducers.push(reducerViews([normalized], order, true)[0]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// 2) Interreduce: each polynomial reduced by all others.
|
|
272
|
+
const out = [];
|
|
273
|
+
for (let i = 0; i < kept.length; i++) {
|
|
274
|
+
const others = kept.filter((_, j) => j !== i);
|
|
275
|
+
const otherHeadReducers = keptHeadReducers.filter((_, j) => j !== i);
|
|
276
|
+
const otherNormalizedReducers = keptNormalizedReducers.filter((_, j) => j !== i);
|
|
277
|
+
const headReduced = normalFormFractionFree(kept[i], others, order, otherHeadReducers);
|
|
278
|
+
const normalized = normalFormFractionFreeAllTerms(headReduced, others, order, otherNormalizedReducers);
|
|
279
|
+
if (!normalized.isZero()) {
|
|
280
|
+
out.push(normalized);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// 3) Remove exact duplicates produced by independent interreduction.
|
|
284
|
+
const byPoly = new Map();
|
|
285
|
+
for (const p of out) {
|
|
286
|
+
byPoly.set(polyKey(p), p);
|
|
287
|
+
}
|
|
288
|
+
const result = Array.from(byPoly.values());
|
|
289
|
+
const monomialOrder = sparseOrder(order);
|
|
290
|
+
const sortInfo = new Map();
|
|
291
|
+
for (const p of result) {
|
|
292
|
+
sortInfo.set(p, {
|
|
293
|
+
presence: variablePresence(p),
|
|
294
|
+
lt: ltOrNull(p, order),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
result.sort((p, q) => {
|
|
298
|
+
const pInfo = sortInfo.get(p);
|
|
299
|
+
const qInfo = sortInfo.get(q);
|
|
300
|
+
for (let i = 0; i < p.variableCount; i++) {
|
|
301
|
+
const pv = pInfo.presence[i] ? 1 : 0;
|
|
302
|
+
const qv = qInfo.presence[i] ? 1 : 0;
|
|
303
|
+
if (pv !== qv) {
|
|
304
|
+
return pv - qv;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (pInfo.lt && qInfo.lt) {
|
|
308
|
+
const comparison = SparsePolynomial_1.SparsePolynomial.compareMonomials(pInfo.lt.exp, qInfo.lt.exp, monomialOrder);
|
|
309
|
+
if (comparison !== 0) {
|
|
310
|
+
return comparison;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return p.termCount - q.termCount;
|
|
314
|
+
});
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Sugar strategy
|
|
319
|
+
// ============================================================================
|
|
320
|
+
/**
|
|
321
|
+
* Sugar degree of an S-polynomial S(f,g).
|
|
322
|
+
*
|
|
323
|
+
* sugar(S(f,g)) = max(sugar(f) + deg(lcm/LM(f)), sugar(g) + deg(lcm/LM(g)))
|
|
324
|
+
*/
|
|
325
|
+
function sPairSugar(sugarF, sugarG, ltFDegree, ltGDegree, lcmDegree) {
|
|
326
|
+
const candidateF = sugarF + lcmDegree - ltFDegree;
|
|
327
|
+
const candidateG = sugarG + lcmDegree - ltGDegree;
|
|
328
|
+
return candidateF > candidateG ? candidateF : candidateG;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Inserts a pair in reverse processing order so the next pair can be removed with pop().
|
|
332
|
+
* Lower sugar and lower lcmDeg are processed first. Exact ties retain the previous
|
|
333
|
+
* last-in-first-out behavior.
|
|
334
|
+
*/
|
|
335
|
+
function insertPairSorted(queue, pair) {
|
|
336
|
+
let lo = 0;
|
|
337
|
+
let hi = queue.length;
|
|
338
|
+
while (lo < hi) {
|
|
339
|
+
const mid = (lo + hi) >>> 1;
|
|
340
|
+
const current = queue[mid];
|
|
341
|
+
if (current.sugar > pair.sugar ||
|
|
342
|
+
(current.sugar === pair.sugar && current.lcmDeg >= pair.lcmDeg)) {
|
|
343
|
+
lo = mid + 1;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
hi = mid;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
queue.splice(lo, 0, pair);
|
|
350
|
+
}
|
|
351
|
+
// ============================================================================
|
|
352
|
+
// Core Buchberger algorithm
|
|
353
|
+
// ============================================================================
|
|
354
|
+
function groebnerBasisSparse(polys, orderOrOpts = 'LEX') {
|
|
355
|
+
const opts = typeof orderOrOpts === 'string' ? { order: orderOrOpts } : (orderOrOpts ?? {});
|
|
356
|
+
const order = opts.order ?? 'LEX';
|
|
357
|
+
const reduced = opts.reduced ?? true;
|
|
358
|
+
const maxPairsPopped = opts.maxPairsPopped;
|
|
359
|
+
const maxBasisSize = opts.maxBasisSize;
|
|
360
|
+
const strategy = opts.strategy ?? 'sugar';
|
|
361
|
+
const stats = {
|
|
362
|
+
pairsPopped: 0,
|
|
363
|
+
pairsSkippedProduct: 0,
|
|
364
|
+
pairsSkippedChain: 0,
|
|
365
|
+
pairsReducedToZero: 0,
|
|
366
|
+
basisAppends: 0,
|
|
367
|
+
};
|
|
368
|
+
const basisInput = polys.filter(p => !p.isZero());
|
|
369
|
+
if (basisInput.length === 0) {
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
const variableCount = basisInput[0].variableCount;
|
|
373
|
+
for (const polynomial of basisInput) {
|
|
374
|
+
if (polynomial.variableCount !== variableCount) {
|
|
375
|
+
throw new RangeError((0, errors_1.message)('multidegreeMismatch'));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const basis = basisInput.slice();
|
|
379
|
+
const headReducers = reducerViews(basis, order, false);
|
|
380
|
+
const normalizedReducers = reducerViews(basis, order, true);
|
|
381
|
+
const lmDegreeCache = headReducers.map(reducer => monomialTotalDegreeDense(reducer.lt.exp));
|
|
382
|
+
const sugarArr = basis.map(polynomial => polynomial.totalDegree() ?? 0n);
|
|
383
|
+
const zeroPairNeighbors = basis.map(() => new Set());
|
|
384
|
+
const markZeroPair = (i, j) => {
|
|
385
|
+
zeroPairNeighbors[i].add(j);
|
|
386
|
+
zeroPairNeighbors[j].add(i);
|
|
387
|
+
};
|
|
388
|
+
const areCoprimeLM = (a, b) => {
|
|
389
|
+
for (let i = 0; i < variableCount; i++) {
|
|
390
|
+
if (a[i] > 0n && b[i] > 0n) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
};
|
|
396
|
+
const chainCriterionApplies = (i, j) => {
|
|
397
|
+
const li = headReducers[i].lt.exp;
|
|
398
|
+
const lj = headReducers[j].lt.exp;
|
|
399
|
+
const lcm = expLcmDense(li, lj);
|
|
400
|
+
const iNeighbors = zeroPairNeighbors[i];
|
|
401
|
+
const jNeighbors = zeroPairNeighbors[j];
|
|
402
|
+
const candidates = iNeighbors.size <= jNeighbors.size ? iNeighbors : jNeighbors;
|
|
403
|
+
const otherNeighbors = candidates === iNeighbors ? jNeighbors : iNeighbors;
|
|
404
|
+
for (const k of candidates) {
|
|
405
|
+
if (k !== i &&
|
|
406
|
+
k !== j &&
|
|
407
|
+
otherNeighbors.has(k) &&
|
|
408
|
+
monoDividesDense(headReducers[k].lt.exp, lcm)) {
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
};
|
|
414
|
+
const appendBasis = (h) => {
|
|
415
|
+
const index = basis.length;
|
|
416
|
+
basis.push(h);
|
|
417
|
+
const headReducer = reducerViews([h], order, false)[0];
|
|
418
|
+
const normalizedReducer = reducerViews([h], order, true)[0];
|
|
419
|
+
headReducers.push(headReducer);
|
|
420
|
+
normalizedReducers.push(normalizedReducer);
|
|
421
|
+
lmDegreeCache.push(monomialTotalDegreeDense(headReducer.lt.exp));
|
|
422
|
+
zeroPairNeighbors.push(new Set());
|
|
423
|
+
stats.basisAppends++;
|
|
424
|
+
if (maxBasisSize !== undefined && basis.length > maxBasisSize) {
|
|
425
|
+
throw new GroebnerBudgetExceeded((0, errors_1.message)('groebnerBasisSizeBudgetExceeded', { max: String(maxBasisSize) }), stats);
|
|
426
|
+
}
|
|
427
|
+
return index;
|
|
428
|
+
};
|
|
429
|
+
const reducePair = (i, j, initialSugar) => {
|
|
430
|
+
const sPolynomial = sPolynomialFF(basis[i], basis[j], headReducers[i].lt, headReducers[j].lt);
|
|
431
|
+
const sugarState = initialSugar === undefined
|
|
432
|
+
? undefined
|
|
433
|
+
: { value: initialSugar, reducerSugars: sugarArr };
|
|
434
|
+
const headReduced = normalFormFractionFree(sPolynomial, basis, order, headReducers, sugarState);
|
|
435
|
+
const polynomial = normalFormFractionFreeAllTerms(headReduced, basis, order, normalizedReducers, sugarState);
|
|
436
|
+
return { polynomial, sugar: sugarState?.value };
|
|
437
|
+
};
|
|
438
|
+
// Build initial pair queue.
|
|
439
|
+
if (strategy === 'sugar') {
|
|
440
|
+
const pairQueue = [];
|
|
441
|
+
const makePair = (i, j) => {
|
|
442
|
+
const li = headReducers[i].lt.exp;
|
|
443
|
+
const lj = headReducers[j].lt.exp;
|
|
444
|
+
const lcm = expLcmDense(li, lj);
|
|
445
|
+
const lcmDegree = monomialTotalDegreeDense(lcm);
|
|
446
|
+
return {
|
|
447
|
+
i,
|
|
448
|
+
j,
|
|
449
|
+
sugar: sPairSugar(sugarArr[i], sugarArr[j], lmDegreeCache[i], lmDegreeCache[j], lcmDegree),
|
|
450
|
+
lcmDeg: lcmDegree,
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
for (let i = 0; i < basis.length; i++) {
|
|
454
|
+
for (let j = i + 1; j < basis.length; j++) {
|
|
455
|
+
insertPairSorted(pairQueue, makePair(i, j));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
while (pairQueue.length > 0) {
|
|
459
|
+
const { i, j, sugar: pairSugar } = pairQueue.pop();
|
|
460
|
+
stats.pairsPopped++;
|
|
461
|
+
if (maxPairsPopped !== undefined && stats.pairsPopped > maxPairsPopped) {
|
|
462
|
+
throw new GroebnerBudgetExceeded((0, errors_1.message)('groebnerPairsBudgetExceeded', { max: String(maxPairsPopped) }), stats);
|
|
463
|
+
}
|
|
464
|
+
const lmi = headReducers[i].lt.exp;
|
|
465
|
+
const lmj = headReducers[j].lt.exp;
|
|
466
|
+
if (areCoprimeLM(lmi, lmj)) {
|
|
467
|
+
stats.pairsSkippedProduct++;
|
|
468
|
+
markZeroPair(i, j);
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (chainCriterionApplies(i, j)) {
|
|
472
|
+
stats.pairsSkippedChain++;
|
|
473
|
+
markZeroPair(i, j);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const reduction = reducePair(i, j, pairSugar);
|
|
477
|
+
const h = reduction.polynomial;
|
|
478
|
+
if (h.isZero()) {
|
|
479
|
+
stats.pairsReducedToZero++;
|
|
480
|
+
markZeroPair(i, j);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (h.isConstant() && h.constantTerm() !== 0n) {
|
|
484
|
+
return [SparsePolynomial_1.SparsePolynomial.constant(variableCount, 1n)];
|
|
485
|
+
}
|
|
486
|
+
const k = appendBasis(h);
|
|
487
|
+
const hTotalDegree = h.totalDegree() ?? 0n;
|
|
488
|
+
const reducedSugar = reduction.sugar ?? pairSugar;
|
|
489
|
+
sugarArr.push(reducedSugar > hTotalDegree ? reducedSugar : hTotalDegree);
|
|
490
|
+
for (let t = 0; t < k; t++) {
|
|
491
|
+
insertPairSorted(pairQueue, makePair(t, k));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
// FIFO strategy.
|
|
497
|
+
const pairs = [];
|
|
498
|
+
for (let i = 0; i < basis.length; i++) {
|
|
499
|
+
for (let j = i + 1; j < basis.length; j++) {
|
|
500
|
+
pairs.push([i, j]);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
let pairIndex = 0;
|
|
504
|
+
while (pairIndex < pairs.length) {
|
|
505
|
+
const [i, j] = pairs[pairIndex++];
|
|
506
|
+
stats.pairsPopped++;
|
|
507
|
+
if (maxPairsPopped !== undefined && stats.pairsPopped > maxPairsPopped) {
|
|
508
|
+
throw new GroebnerBudgetExceeded((0, errors_1.message)('groebnerPairsBudgetExceeded', { max: String(maxPairsPopped) }), stats);
|
|
509
|
+
}
|
|
510
|
+
const lmi = headReducers[i].lt.exp;
|
|
511
|
+
const lmj = headReducers[j].lt.exp;
|
|
512
|
+
if (areCoprimeLM(lmi, lmj)) {
|
|
513
|
+
stats.pairsSkippedProduct++;
|
|
514
|
+
markZeroPair(i, j);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (chainCriterionApplies(i, j)) {
|
|
518
|
+
stats.pairsSkippedChain++;
|
|
519
|
+
markZeroPair(i, j);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const h = reducePair(i, j).polynomial;
|
|
523
|
+
if (h.isZero()) {
|
|
524
|
+
stats.pairsReducedToZero++;
|
|
525
|
+
markZeroPair(i, j);
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (h.isConstant() && h.constantTerm() !== 0n) {
|
|
529
|
+
return [SparsePolynomial_1.SparsePolynomial.constant(variableCount, 1n)];
|
|
530
|
+
}
|
|
531
|
+
const k = appendBasis(h);
|
|
532
|
+
for (let t = 0; t < k; t++) {
|
|
533
|
+
pairs.push([t, k]);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return reduced ? canonicalizeBasis(basis, order) : basis;
|
|
538
|
+
}
|
|
539
|
+
function reduceSparseByBasis(f, basis, order) {
|
|
540
|
+
if (f.isZero() || basis.length === 0) {
|
|
541
|
+
return f;
|
|
542
|
+
}
|
|
543
|
+
const headReducers = reducerViews(basis, order, false);
|
|
544
|
+
const normalizedReducers = reducerViews(basis, order, true);
|
|
545
|
+
const headReduced = normalFormFractionFree(f, basis, order, headReducers);
|
|
546
|
+
return normalFormFractionFreeAllTerms(headReduced, basis, order, normalizedReducers);
|
|
547
|
+
}
|
|
548
|
+
// ============================================================================
|
|
549
|
+
// Public API: Groebner basis
|
|
550
|
+
// ============================================================================
|
|
551
|
+
/**
|
|
552
|
+
* Computes a Groebner basis for exact sparse integer polynomials.
|
|
553
|
+
*
|
|
554
|
+
* The input polynomials must belong to the same ring. With `reduced: true`, the
|
|
555
|
+
* basis is interreduced, coefficient content is removed, leading signs are normalized,
|
|
556
|
+
* and exact monic normalization is applied when possible. Results are sorted
|
|
557
|
+
* deterministically and the supplied polynomials are not modified.
|
|
558
|
+
*
|
|
559
|
+
* @param polys - Ideal generators in one sparse polynomial ring.
|
|
560
|
+
* @param order - Monomial order; defaults to `LEX`.
|
|
561
|
+
* @param reduced - Return the normalized/interreduced basis; defaults to `true`.
|
|
562
|
+
* @returns New sparse basis polynomials in deterministic presentation order.
|
|
563
|
+
*/
|
|
564
|
+
function Groebner(polys, order = 'LEX', reduced = true) {
|
|
565
|
+
if (polys.length === 0) {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
const basis = groebnerBasisSparse(polys, { order, reduced });
|
|
569
|
+
const variableCount = basis[0]?.variableCount ?? polys[0].variableCount;
|
|
570
|
+
const cache = new Map();
|
|
571
|
+
const getInfo = (p) => {
|
|
572
|
+
let info = cache.get(p);
|
|
573
|
+
if (!info) {
|
|
574
|
+
info = { pv: variablePresence(p), lt: ltOrNull(p, order) };
|
|
575
|
+
cache.set(p, info);
|
|
576
|
+
}
|
|
577
|
+
return info;
|
|
578
|
+
};
|
|
579
|
+
return [...basis].sort((p, q) => {
|
|
580
|
+
const pInfo = getInfo(p);
|
|
581
|
+
const qInfo = getInfo(q);
|
|
582
|
+
for (let i = 0; i < variableCount; i++) {
|
|
583
|
+
const pv = pInfo.pv[i] ? 1 : 0;
|
|
584
|
+
const qv = qInfo.pv[i] ? 1 : 0;
|
|
585
|
+
if (pv !== qv) {
|
|
586
|
+
return pv > qv ? -1 : 1;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (pInfo.lt && qInfo.lt) {
|
|
590
|
+
const comparison = SparsePolynomial_1.SparsePolynomial.compareMonomials(pInfo.lt.exp, qInfo.lt.exp, sparseOrder(order));
|
|
591
|
+
if (comparison !== 0) {
|
|
592
|
+
return comparison;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return p.termCount - q.termCount;
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Computes a Groebner basis with ordering, strategy, and budget control.
|
|
600
|
+
*
|
|
601
|
+
* @param polys - Exact sparse integer-coefficient generators in one ring.
|
|
602
|
+
* @param opts - Ordering, normalization, pair-selection, and deterministic budget options.
|
|
603
|
+
* @returns New basis polynomials. Work statistics are exposed only when a budget is
|
|
604
|
+
* exceeded; successful calls return the basis itself.
|
|
605
|
+
* @throws {@link GroebnerBudgetExceeded} Thrown after a configured pair-count or
|
|
606
|
+
* basis-size limit is exceeded.
|
|
607
|
+
*/
|
|
608
|
+
function groebnerBasisWithOptions(polys, opts) {
|
|
609
|
+
return groebnerBasisSparse(polys, opts);
|
|
610
|
+
}
|
|
611
|
+
// ============================================================================
|
|
612
|
+
// Elimination
|
|
613
|
+
// ============================================================================
|
|
614
|
+
/**
|
|
615
|
+
* Computes the part of the ideal that lies in the polynomial ring generated by `keepVars`.
|
|
616
|
+
*
|
|
617
|
+
* @param polys - Exact integer-coefficient generators.
|
|
618
|
+
* @param vars - All variables in exponent-index order. Variables to eliminate must
|
|
619
|
+
* precede retained variables for lexicographic elimination.
|
|
620
|
+
* @param keepVars - Variables to retain, expected to be a suffix of `vars`.
|
|
621
|
+
* @param opts - Reduction, pair strategy, and budgets; order is always `LEX`.
|
|
622
|
+
* @returns New basis polynomials involving only retained variables.
|
|
623
|
+
* @throws {@link GroebnerBudgetExceeded} Thrown when a configured budget is exceeded.
|
|
624
|
+
*/
|
|
625
|
+
function eliminate(polys, vars, keepVars, opts) {
|
|
626
|
+
if (polys.length === 0) {
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
// Determine which variable indices to eliminate.
|
|
630
|
+
const keepSet = new Set(keepVars);
|
|
631
|
+
const eliminateIndices = new Set();
|
|
632
|
+
for (let i = 0; i < vars.length; i++) {
|
|
633
|
+
if (!keepSet.has(vars[i])) {
|
|
634
|
+
eliminateIndices.add(i);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
for (const polynomial of polys) {
|
|
638
|
+
for (const variableIndex of polynomial.variables()) {
|
|
639
|
+
if (variableIndex >= vars.length) {
|
|
640
|
+
throw new RangeError((0, errors_1.message)('multidegreeMismatch'));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
// Compute a LEX basis with variables to eliminate first in the supplied order.
|
|
645
|
+
const basis = groebnerBasisSparse(polys, { ...opts, order: 'LEX', reduced: true });
|
|
646
|
+
const result = basis.filter(poly => {
|
|
647
|
+
const presence = variablePresence(poly);
|
|
648
|
+
for (const variableIndex of eliminateIndices) {
|
|
649
|
+
if (presence[variableIndex]) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return true;
|
|
654
|
+
});
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
// ============================================================================
|
|
658
|
+
// Ideal membership
|
|
659
|
+
// ============================================================================
|
|
660
|
+
/**
|
|
661
|
+
* Tests exact ideal membership by reducing against a computed Groebner basis.
|
|
662
|
+
*
|
|
663
|
+
* @param f - Polynomial to test.
|
|
664
|
+
* @param polys - Exact integer-coefficient ideal generators.
|
|
665
|
+
* @param order - Monomial order; defaults to `LEX`.
|
|
666
|
+
* @param opts - Pair strategy and deterministic budgets.
|
|
667
|
+
* @returns Whether the normal form of `f` is zero.
|
|
668
|
+
* @throws {@link GroebnerBudgetExceeded} Thrown when basis computation exceeds a budget.
|
|
669
|
+
*/
|
|
670
|
+
function idealMembership(f, polys, order = 'LEX', opts) {
|
|
671
|
+
if (f.isZero()) {
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
if (polys.length === 0) {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
const basis = groebnerBasisSparse(polys, { ...opts, order, reduced: true });
|
|
678
|
+
return reduceSparseByBasis(f, basis, order).isZero();
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Reduces a polynomial by a basis or arbitrary reducer set using fraction-free arithmetic.
|
|
682
|
+
*
|
|
683
|
+
* @remarks
|
|
684
|
+
* This does not compute or verify a Groebner basis. The result is a canonical ideal
|
|
685
|
+
* normal form only when `basis` is already a basis for the selected order.
|
|
686
|
+
*
|
|
687
|
+
* @param f - Polynomial to reduce.
|
|
688
|
+
* @param basis - Reducers, normally a Groebner basis.
|
|
689
|
+
* @param order - Monomial order used to select leading terms.
|
|
690
|
+
* @returns The primitive-normalized remainder.
|
|
691
|
+
*/
|
|
692
|
+
function reduceByBasis(f, basis, order = 'LEX') {
|
|
693
|
+
return f.isZero() || basis.length === 0 ? f : reduceSparseByBasis(f, basis, order);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Solves a zero-dimensional polynomial system for rational coordinates.
|
|
697
|
+
*
|
|
698
|
+
* @remarks
|
|
699
|
+
* This is the low-level exact solver used with the Groebner representation, not the
|
|
700
|
+
* ordinary expression solver. It computes a reduced lexicographic basis, walks variables
|
|
701
|
+
* from least to most significant, applies the Rational Root Theorem to univariate
|
|
702
|
+
* constraints, and substitutes each partial assignment into its own basis copy.
|
|
703
|
+
* Irrational and complex roots are outside this routine's result set.
|
|
704
|
+
*
|
|
705
|
+
* @param polys - Exact integer-coefficient system generators.
|
|
706
|
+
* @param vars - Variables in lexicographic elimination order, most significant first.
|
|
707
|
+
* @param opts - Pair strategy and budgets forwarded to basis computation.
|
|
708
|
+
* @returns New solution maps. An empty array can mean inconsistency, no rational roots,
|
|
709
|
+
* or a non-finite/unsupported triangular result; these cases are not distinguished.
|
|
710
|
+
* @throws {@link GroebnerBudgetExceeded} Thrown when basis computation exceeds a budget.
|
|
711
|
+
*/
|
|
712
|
+
function solve(polys, vars, opts) {
|
|
713
|
+
if (polys.length === 0 || vars.length === 0) {
|
|
714
|
+
return [new Map()];
|
|
715
|
+
}
|
|
716
|
+
for (const polynomial of polys) {
|
|
717
|
+
for (const variableIndex of polynomial.variables()) {
|
|
718
|
+
if (variableIndex >= vars.length) {
|
|
719
|
+
throw new RangeError((0, errors_1.message)('multidegreeMismatch'));
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const basis = groebnerBasisSparse(polys, { ...opts, order: 'LEX', reduced: true });
|
|
724
|
+
// Trivial: if the basis is {1}, the system is inconsistent.
|
|
725
|
+
if (basis.length === 1 && basis[0].isConstant() && basis[0].constantTerm() !== 0n) {
|
|
726
|
+
return [];
|
|
727
|
+
}
|
|
728
|
+
if (basis.length === 0) {
|
|
729
|
+
// Ideal is {0}, meaning every point is a solution — infinite solutions.
|
|
730
|
+
return [];
|
|
731
|
+
}
|
|
732
|
+
return backSubstitute(basis, vars);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Triangular back-substitution on a LEX Gröbner basis.
|
|
736
|
+
*
|
|
737
|
+
* Walks variables from last (least significant) to first, finding univariate
|
|
738
|
+
* polynomials, extracting rational roots, and substituting back.
|
|
739
|
+
*
|
|
740
|
+
* Each partial solution carries its own partially-substituted copy of the basis,
|
|
741
|
+
* so when we extend with a new variable binding we only need to substitute that
|
|
742
|
+
* one new value — not redo all prior substitutions.
|
|
743
|
+
*/
|
|
744
|
+
function backSubstitute(basis, vars) {
|
|
745
|
+
let partials = [
|
|
746
|
+
{
|
|
747
|
+
assignment: new Map(),
|
|
748
|
+
basis: [...basis],
|
|
749
|
+
},
|
|
750
|
+
];
|
|
751
|
+
for (let variableIndex = vars.length - 1; variableIndex >= 0; variableIndex--) {
|
|
752
|
+
const nextPartials = [];
|
|
753
|
+
for (const { assignment, basis: partialBasis } of partials) {
|
|
754
|
+
const univariates = partialBasis.filter(poly => {
|
|
755
|
+
if (poly.isZero()) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
const variables = poly.variables();
|
|
759
|
+
if (variables.some(index => index !== variableIndex)) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
const degree = poly.degree(variableIndex);
|
|
763
|
+
return degree !== null && degree > 0n;
|
|
764
|
+
});
|
|
765
|
+
if (univariates.length === 0) {
|
|
766
|
+
// No constraint on this variable — push forward as-is.
|
|
767
|
+
nextPartials.push({
|
|
768
|
+
assignment: new Map(assignment),
|
|
769
|
+
basis: partialBasis,
|
|
770
|
+
});
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
const roots = rationalRootsUnivariate(univariates[0], variableIndex);
|
|
774
|
+
for (const root of roots) {
|
|
775
|
+
let consistent = true;
|
|
776
|
+
for (let polynomialIndex = 1; polynomialIndex < univariates.length; polynomialIndex++) {
|
|
777
|
+
if (!evaluatesToZero(univariates[polynomialIndex], variableIndex, root)) {
|
|
778
|
+
consistent = false;
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (!consistent) {
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
const nextBasis = partialBasis.map(poly => {
|
|
786
|
+
const degree = poly.degree(variableIndex);
|
|
787
|
+
if (degree === null || degree === 0n) {
|
|
788
|
+
return poly;
|
|
789
|
+
}
|
|
790
|
+
const scaled = root.d === 1n ? poly : scaleByDenomPow(poly, variableIndex, root.d);
|
|
791
|
+
return scaled.evaluateVariable(variableIndex, root.n);
|
|
792
|
+
});
|
|
793
|
+
const extended = new Map(assignment);
|
|
794
|
+
extended.set(variableIndex, root);
|
|
795
|
+
nextPartials.push({ assignment: extended, basis: nextBasis });
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
partials = nextPartials;
|
|
799
|
+
}
|
|
800
|
+
return partials.map(({ assignment }) => {
|
|
801
|
+
const named = new Map();
|
|
802
|
+
for (const [variableIndex, value] of assignment) {
|
|
803
|
+
if (variableIndex < vars.length) {
|
|
804
|
+
named.set(vars[variableIndex], value);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return named;
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Scales a polynomial before integer substitution of a rational value n/d.
|
|
812
|
+
*
|
|
813
|
+
* For each term containing x_v^k, the coefficient is multiplied by
|
|
814
|
+
* d^(maxDegree-k), so evaluating x_v at n gives p(n/d) * d^maxDegree.
|
|
815
|
+
*/
|
|
816
|
+
function scaleByDenomPow(poly, variableIndex, denominator) {
|
|
817
|
+
const maxDegree = poly.degree(variableIndex);
|
|
818
|
+
if (maxDegree === null || maxDegree === 0n || denominator === 1n) {
|
|
819
|
+
return poly;
|
|
820
|
+
}
|
|
821
|
+
return new SparsePolynomial_1.SparsePolynomial(poly.variableCount, poly.terms().map(term => ({
|
|
822
|
+
coefficient: term.coefficient *
|
|
823
|
+
denominator ** (maxDegree - term.exponents[variableIndex]),
|
|
824
|
+
exponents: term.exponents,
|
|
825
|
+
})));
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Finds all rational roots p/q of a univariate polynomial.
|
|
829
|
+
*
|
|
830
|
+
* Uses the Rational Root Theorem: p divides the constant term and q divides the
|
|
831
|
+
* leading coefficient. Polynomial degree and term exponents remain bigint values.
|
|
832
|
+
*/
|
|
833
|
+
function rationalRootsUnivariate(poly, variableIndex) {
|
|
834
|
+
const degree = poly.degree(variableIndex);
|
|
835
|
+
if (degree === null || degree === 0n) {
|
|
836
|
+
return [];
|
|
837
|
+
}
|
|
838
|
+
const leadingCoefficient = poly.coefficientIn(variableIndex, degree).constantTerm();
|
|
839
|
+
const constantTerm = poly.constantTerm();
|
|
840
|
+
if (constantTerm === 0n) {
|
|
841
|
+
const roots = [{ n: 0n, d: 1n }];
|
|
842
|
+
let commonPower = degree;
|
|
843
|
+
for (const term of poly.terms()) {
|
|
844
|
+
if (term.exponents[variableIndex] < commonPower) {
|
|
845
|
+
commonPower = term.exponents[variableIndex];
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const shifted = new SparsePolynomial_1.SparsePolynomial(poly.variableCount, poly.terms().map(term => {
|
|
849
|
+
const exponents = [...term.exponents];
|
|
850
|
+
exponents[variableIndex] -= commonPower;
|
|
851
|
+
return { coefficient: term.coefficient, exponents };
|
|
852
|
+
}));
|
|
853
|
+
for (const root of rationalRootsUnivariate(shifted, variableIndex)) {
|
|
854
|
+
if (root.n !== 0n) {
|
|
855
|
+
roots.push(root);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return roots;
|
|
859
|
+
}
|
|
860
|
+
const numeratorDivisors = positiveDivisors((0, bigint_1.abs)(constantTerm));
|
|
861
|
+
const denominatorDivisors = positiveDivisors((0, bigint_1.abs)(leadingCoefficient));
|
|
862
|
+
const roots = [];
|
|
863
|
+
const seen = new Set();
|
|
864
|
+
for (const numeratorFactor of numeratorDivisors) {
|
|
865
|
+
for (const denominatorFactor of denominatorDivisors) {
|
|
866
|
+
for (const sign of [1n, -1n]) {
|
|
867
|
+
const numerator = sign * numeratorFactor;
|
|
868
|
+
const divisor = (0, bigint_1.GCD)((0, bigint_1.abs)(numerator), denominatorFactor);
|
|
869
|
+
const reducedNumerator = numerator / divisor;
|
|
870
|
+
const reducedDenominator = denominatorFactor / divisor;
|
|
871
|
+
const key = `${reducedNumerator}/${reducedDenominator}`;
|
|
872
|
+
if (seen.has(key)) {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
seen.add(key);
|
|
876
|
+
if (evaluatesAtRationalToZero(poly, variableIndex, degree, reducedNumerator, reducedDenominator)) {
|
|
877
|
+
roots.push({ n: reducedNumerator, d: reducedDenominator });
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return roots;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Evaluates a univariate polynomial at n/d after clearing d^degree.
|
|
886
|
+
*/
|
|
887
|
+
function evaluatesAtRationalToZero(poly, variableIndex, degree, numerator, denominator) {
|
|
888
|
+
let result = 0n;
|
|
889
|
+
for (const term of poly.terms()) {
|
|
890
|
+
const exponent = term.exponents[variableIndex];
|
|
891
|
+
result +=
|
|
892
|
+
term.coefficient *
|
|
893
|
+
numerator ** exponent *
|
|
894
|
+
denominator ** (degree - exponent);
|
|
895
|
+
}
|
|
896
|
+
return result === 0n;
|
|
897
|
+
}
|
|
898
|
+
/** Checks whether substituting one rational value makes a univariate polynomial zero. */
|
|
899
|
+
function evaluatesToZero(poly, variableIndex, value) {
|
|
900
|
+
const degree = poly.degree(variableIndex);
|
|
901
|
+
if (degree === null) {
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
if (degree === 0n) {
|
|
905
|
+
return poly.constantTerm() === 0n;
|
|
906
|
+
}
|
|
907
|
+
return evaluatesAtRationalToZero(poly, variableIndex, degree, value.n, value.d);
|
|
908
|
+
}
|
|
909
|
+
/** Positive divisors of a positive bigint. */
|
|
910
|
+
function positiveDivisors(n) {
|
|
911
|
+
if (n === 0n) {
|
|
912
|
+
return [0n];
|
|
913
|
+
}
|
|
914
|
+
if (n < 0n) {
|
|
915
|
+
n = -n;
|
|
916
|
+
}
|
|
917
|
+
const divisors = [];
|
|
918
|
+
let candidate = 1n;
|
|
919
|
+
while (candidate * candidate <= n) {
|
|
920
|
+
if (n % candidate === 0n) {
|
|
921
|
+
divisors.push(candidate);
|
|
922
|
+
if (candidate !== n / candidate) {
|
|
923
|
+
divisors.push(n / candidate);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
candidate++;
|
|
927
|
+
}
|
|
928
|
+
return divisors;
|
|
929
|
+
}
|