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.
Files changed (341) hide show
  1. package/LICENSE.md +184 -0
  2. package/README.md +293 -312
  3. package/dist/bundle.js +2 -0
  4. package/dist/bundle.js.LICENSE.txt +7 -0
  5. package/dist/parser.js +2 -0
  6. package/dist/parser.js.LICENSE.txt +7 -0
  7. package/docs-data/parser-functions.json +1960 -0
  8. package/index.d.ts +10 -426
  9. package/output/algebra/adapters.d.ts +33 -0
  10. package/output/algebra/adapters.js +109 -0
  11. package/output/algebra/algorithms/arith.d.ts +84 -0
  12. package/output/algebra/algorithms/arith.js +323 -0
  13. package/output/algebra/algorithms/factor.d.ts +3 -0
  14. package/output/algebra/algorithms/factor.js +5 -0
  15. package/output/algebra/algorithms/factorMultivariate.d.ts +23 -0
  16. package/output/algebra/algorithms/factorMultivariate.js +2393 -0
  17. package/output/algebra/algorithms/factorUnivariate.d.ts +72 -0
  18. package/output/algebra/algorithms/factorUnivariate.js +1072 -0
  19. package/output/algebra/algorithms/gcd.d.ts +22 -0
  20. package/output/algebra/algorithms/gcd.js +690 -0
  21. package/output/algebra/algorithms/groebnerBase.d.ts +157 -0
  22. package/output/algebra/algorithms/groebnerBase.js +1166 -0
  23. package/output/algebra/algorithms/multiPoly/MultiPoly.d.ts +137 -0
  24. package/output/algebra/algorithms/multiPoly/MultiPoly.js +346 -0
  25. package/output/algebra/algorithms/poly.d.ts +228 -0
  26. package/output/algebra/algorithms/poly.js +1299 -0
  27. package/output/algebra/algorithms/rational.d.ts +17 -0
  28. package/output/algebra/algorithms/rational.js +115 -0
  29. package/output/algebra/factor/factor.d.ts +62 -0
  30. package/output/algebra/factor/factor.js +158 -0
  31. package/output/algebra/gcd/gcd.d.ts +29 -0
  32. package/output/algebra/gcd/gcd.js +141 -0
  33. package/output/algebra/groebner.d.ts +21 -0
  34. package/output/algebra/groebner.js +42 -0
  35. package/output/algebra/partfrac.d.ts +19 -0
  36. package/output/algebra/partfrac.js +398 -0
  37. package/output/algebra/polynomialize.d.ts +20 -0
  38. package/output/algebra/polynomialize.js +24 -0
  39. package/output/algebra/simplify/complexsimp.d.ts +7 -0
  40. package/output/algebra/simplify/complexsimp.js +24 -0
  41. package/output/algebra/simplify/factorCommon.d.ts +13 -0
  42. package/output/algebra/simplify/factorCommon.js +136 -0
  43. package/output/algebra/simplify/funcsimp.d.ts +26 -0
  44. package/output/algebra/simplify/funcsimp.js +633 -0
  45. package/output/algebra/simplify/invtrigrewrite.d.ts +10 -0
  46. package/output/algebra/simplify/invtrigrewrite.js +96 -0
  47. package/output/algebra/simplify/ratsimp.d.ts +21 -0
  48. package/output/algebra/simplify/ratsimp.js +140 -0
  49. package/output/algebra/simplify/simplify.d.ts +25 -0
  50. package/output/algebra/simplify/simplify.js +228 -0
  51. package/output/algebra/simplify/trigreduce.d.ts +38 -0
  52. package/output/algebra/simplify/trigreduce.js +424 -0
  53. package/output/algebra/simplify/trigrewrite.d.ts +17 -0
  54. package/output/algebra/simplify/trigrewrite.js +156 -0
  55. package/output/algebra/simplify/trigsimp.d.ts +11 -0
  56. package/output/algebra/simplify/trigsimp.js +369 -0
  57. package/output/algebra/simplify/utils.d.ts +32 -0
  58. package/output/algebra/simplify/utils.js +56 -0
  59. package/output/algebra/utils.d.ts +32 -0
  60. package/output/algebra/utils.js +48 -0
  61. package/output/api/advanced.d.ts +7 -0
  62. package/output/api/advanced.js +18 -0
  63. package/output/api/algebra.d.ts +17 -0
  64. package/output/api/algebra.js +32 -0
  65. package/output/api/assumptions.d.ts +4 -0
  66. package/output/api/assumptions.js +11 -0
  67. package/output/api/calculus.d.ts +9 -0
  68. package/output/api/calculus.js +21 -0
  69. package/output/api/core.d.ts +17 -0
  70. package/output/api/core.js +41 -0
  71. package/output/api/debug.d.ts +16 -0
  72. package/output/api/debug.js +23 -0
  73. package/output/api/parser.d.ts +85 -0
  74. package/output/api/parser.js +16 -0
  75. package/output/api/solve.d.ts +10 -0
  76. package/output/api/solve.js +16 -0
  77. package/output/api/structures.d.ts +10 -0
  78. package/output/api/structures.js +22 -0
  79. package/output/calculus/derivative/diff.d.ts +30 -0
  80. package/output/calculus/derivative/diff.js +238 -0
  81. package/output/calculus/fresnel.d.ts +16 -0
  82. package/output/calculus/fresnel.js +39 -0
  83. package/output/calculus/integrate/byParts.d.ts +18 -0
  84. package/output/calculus/integrate/byParts.js +339 -0
  85. package/output/calculus/integrate/bySubstitution.d.ts +135 -0
  86. package/output/calculus/integrate/bySubstitution.js +411 -0
  87. package/output/calculus/integrate/integrate.d.ts +38 -0
  88. package/output/calculus/integrate/integrate.js +652 -0
  89. package/output/calculus/integrate/integrationTable.d.ts +2 -0
  90. package/output/calculus/integrate/integrationTable.js +914 -0
  91. package/output/calculus/integrate/utils.d.ts +28 -0
  92. package/output/calculus/integrate/utils.js +96 -0
  93. package/output/calculus/laplace/ilaplace.d.ts +31 -0
  94. package/output/calculus/laplace/ilaplace.js +192 -0
  95. package/output/calculus/laplace/ilaplaceTable.d.ts +2 -0
  96. package/output/calculus/laplace/ilaplaceTable.js +200 -0
  97. package/output/calculus/laplace/laplace.d.ts +22 -0
  98. package/output/calculus/laplace/laplace.js +109 -0
  99. package/output/calculus/laplace/laplaceTable.d.ts +2 -0
  100. package/output/calculus/laplace/laplaceTable.js +187 -0
  101. package/output/calculus/limit/limit.d.ts +35 -0
  102. package/output/calculus/limit/limit.js +1183 -0
  103. package/output/calculus/limit/limitsTable.d.ts +2 -0
  104. package/output/calculus/limit/limitsTable.js +7 -0
  105. package/output/core/Settings.d.ts +32 -0
  106. package/output/core/Settings.js +68 -0
  107. package/output/core/adapters.d.ts +18 -0
  108. package/output/core/adapters.js +40 -0
  109. package/output/core/classes/assumption/Assumption.d.ts +218 -0
  110. package/output/core/classes/assumption/Assumption.js +605 -0
  111. package/output/core/classes/assumption/assertiveFunctions.d.ts +6 -0
  112. package/output/core/classes/assumption/assertiveFunctions.js +64 -0
  113. package/output/core/classes/assumption/assume.d.ts +90 -0
  114. package/output/core/classes/assumption/assume.js +145 -0
  115. package/output/core/classes/collection/Collection.d.ts +107 -0
  116. package/output/core/classes/collection/Collection.js +231 -0
  117. package/output/core/classes/complex/Complex.d.ts +99 -0
  118. package/output/core/classes/complex/Complex.js +135 -0
  119. package/output/core/classes/decimalSet/DecimalSet.d.ts +226 -0
  120. package/output/core/classes/decimalSet/DecimalSet.js +453 -0
  121. package/output/core/classes/dictionary/Dictionary.d.ts +102 -0
  122. package/output/core/classes/dictionary/Dictionary.js +213 -0
  123. package/output/core/classes/equation/Equation.d.ts +256 -0
  124. package/output/core/classes/equation/Equation.js +350 -0
  125. package/output/core/classes/expression/CoeffObject.d.ts +100 -0
  126. package/output/core/classes/expression/CoeffObject.js +198 -0
  127. package/output/core/classes/expression/Expression.d.ts +1674 -0
  128. package/output/core/classes/expression/Expression.js +2332 -0
  129. package/output/core/classes/expression/analysis.d.ts +25 -0
  130. package/output/core/classes/expression/analysis.js +218 -0
  131. package/output/core/classes/expression/collect.d.ts +9 -0
  132. package/output/core/classes/expression/collect.js +49 -0
  133. package/output/core/classes/expression/format.d.ts +45 -0
  134. package/output/core/classes/expression/format.js +217 -0
  135. package/output/core/classes/expression/products.d.ts +23 -0
  136. package/output/core/classes/expression/products.js +115 -0
  137. package/output/core/classes/expression/shortcuts.d.ts +35 -0
  138. package/output/core/classes/expression/shortcuts.js +129 -0
  139. package/output/core/classes/expression/traversal.d.ts +31 -0
  140. package/output/core/classes/expression/traversal.js +216 -0
  141. package/output/core/classes/expression/trig.d.ts +25 -0
  142. package/output/core/classes/expression/trig.js +40 -0
  143. package/output/core/classes/expression/utils.d.ts +52 -0
  144. package/output/core/classes/expression/utils.js +189 -0
  145. package/output/core/classes/lookupTable/LookupTable.d.ts +12 -0
  146. package/output/core/classes/lookupTable/LookupTable.js +55 -0
  147. package/output/core/classes/matrix/Matrix.d.ts +227 -0
  148. package/output/core/classes/matrix/Matrix.js +889 -0
  149. package/output/core/classes/matrix/Sylvester.d.ts +11 -0
  150. package/output/core/classes/matrix/Sylvester.js +54 -0
  151. package/output/core/classes/matrix/functions.d.ts +26 -0
  152. package/output/core/classes/matrix/functions.js +52 -0
  153. package/output/core/classes/matrix/utils.d.ts +9 -0
  154. package/output/core/classes/matrix/utils.js +22 -0
  155. package/output/core/classes/parser/Parser.d.ts +480 -0
  156. package/output/core/classes/parser/Parser.js +1812 -0
  157. package/output/core/classes/parser/Token.d.ts +58 -0
  158. package/output/core/classes/parser/Token.js +137 -0
  159. package/output/core/classes/parser/constants.d.ts +160 -0
  160. package/output/core/classes/parser/constants.js +227 -0
  161. package/output/core/classes/parser/helpers.d.ts +21 -0
  162. package/output/core/classes/parser/helpers.js +42 -0
  163. package/output/core/classes/parser/operations/add.d.ts +15 -0
  164. package/output/core/classes/parser/operations/add.js +238 -0
  165. package/output/core/classes/parser/operations/comma.d.ts +7 -0
  166. package/output/core/classes/parser/operations/comma.js +11 -0
  167. package/output/core/classes/parser/operations/compare.d.ts +21 -0
  168. package/output/core/classes/parser/operations/compare.js +461 -0
  169. package/output/core/classes/parser/operations/divide.d.ts +2 -0
  170. package/output/core/classes/parser/operations/divide.js +72 -0
  171. package/output/core/classes/parser/operations/functions.d.ts +46 -0
  172. package/output/core/classes/parser/operations/functions.js +430 -0
  173. package/output/core/classes/parser/operations/multiply.d.ts +11 -0
  174. package/output/core/classes/parser/operations/multiply.js +321 -0
  175. package/output/core/classes/parser/operations/power.d.ts +48 -0
  176. package/output/core/classes/parser/operations/power.js +673 -0
  177. package/output/core/classes/parser/operations/subtract.d.ts +3 -0
  178. package/output/core/classes/parser/operations/subtract.js +35 -0
  179. package/output/core/classes/parser/preprocess.d.ts +14 -0
  180. package/output/core/classes/parser/preprocess.js +259 -0
  181. package/output/core/classes/parser/scripting/controlFlow.d.ts +121 -0
  182. package/output/core/classes/parser/scripting/controlFlow.js +355 -0
  183. package/output/core/classes/parser/scripting/evaluate.d.ts +10 -0
  184. package/output/core/classes/parser/scripting/evaluate.js +112 -0
  185. package/output/core/classes/parser/scripting/functions.d.ts +14 -0
  186. package/output/core/classes/parser/scripting/functions.js +59 -0
  187. package/output/core/classes/parser/scripting/scope.d.ts +35 -0
  188. package/output/core/classes/parser/scripting/scope.js +155 -0
  189. package/output/core/classes/parser/types.d.ts +125 -0
  190. package/output/core/classes/parser/types.js +2 -0
  191. package/output/core/classes/parser/wrappers/IndexedReference.d.ts +31 -0
  192. package/output/core/classes/parser/wrappers/IndexedReference.js +68 -0
  193. package/output/core/classes/parser/wrappers/KeyValuePair.d.ts +24 -0
  194. package/output/core/classes/parser/wrappers/KeyValuePair.js +51 -0
  195. package/output/core/classes/polynomial/Polynomial.d.ts +500 -0
  196. package/output/core/classes/polynomial/Polynomial.js +1149 -0
  197. package/output/core/classes/polynomial/Term.d.ts +222 -0
  198. package/output/core/classes/polynomial/Term.js +430 -0
  199. package/output/core/classes/polynomial/functions.d.ts +69 -0
  200. package/output/core/classes/polynomial/functions.js +126 -0
  201. package/output/core/classes/polynomial/utils.d.ts +62 -0
  202. package/output/core/classes/polynomial/utils.js +206 -0
  203. package/output/core/classes/rational/Rational.d.ts +504 -0
  204. package/output/core/classes/rational/Rational.js +779 -0
  205. package/output/core/classes/seq/SEQ.d.ts +13 -0
  206. package/output/core/classes/seq/SEQ.js +137 -0
  207. package/output/core/classes/valuesSet/ValuesSet.d.ts +179 -0
  208. package/output/core/classes/valuesSet/ValuesSet.js +381 -0
  209. package/output/core/classes/vector/Vector.d.ts +222 -0
  210. package/output/core/classes/vector/Vector.js +497 -0
  211. package/output/core/classes/vector/functions.d.ts +28 -0
  212. package/output/core/classes/vector/functions.js +47 -0
  213. package/output/core/common/classes/MathematicalAggregate.d.ts +53 -0
  214. package/output/core/common/classes/MathematicalAggregate.js +140 -0
  215. package/output/core/common/classes/Scope.d.ts +62 -0
  216. package/output/core/common/classes/Scope.js +122 -0
  217. package/output/core/common/classes/StructuredEntity.d.ts +55 -0
  218. package/output/core/common/classes/StructuredEntity.js +151 -0
  219. package/output/core/common/common.d.ts +63 -0
  220. package/output/core/common/common.js +279 -0
  221. package/output/core/common/functions/functions.d.ts +14 -0
  222. package/output/core/common/functions/functions.js +33 -0
  223. package/output/core/common/functions/structuredEntityUtils.d.ts +27 -0
  224. package/output/core/common/functions/structuredEntityUtils.js +40 -0
  225. package/output/core/converters/BaseConverter.d.ts +97 -0
  226. package/output/core/converters/BaseConverter.js +405 -0
  227. package/output/core/converters/Converter.d.ts +111 -0
  228. package/output/core/converters/Converter.js +802 -0
  229. package/output/core/converters/Pattern.d.ts +71 -0
  230. package/output/core/converters/Pattern.js +302 -0
  231. package/output/core/dispatch.d.ts +34 -0
  232. package/output/core/dispatch.js +304 -0
  233. package/output/core/errors.d.ts +531 -0
  234. package/output/core/errors.js +621 -0
  235. package/output/core/functions/bigint/bigint.d.ts +121 -0
  236. package/output/core/functions/bigint/bigint.js +392 -0
  237. package/output/core/functions/bigint/primeFactor.d.ts +49 -0
  238. package/output/core/functions/bigint/primeFactor.js +267 -0
  239. package/output/core/functions/bigint/primes.d.ts +1 -0
  240. package/output/core/functions/bigint/primes.js +11 -0
  241. package/output/core/functions/build/definitions.d.ts +12 -0
  242. package/output/core/functions/build/definitions.js +133 -0
  243. package/output/core/functions/build/index.d.ts +32 -0
  244. package/output/core/functions/build/index.js +136 -0
  245. package/output/core/functions/complex.d.ts +90 -0
  246. package/output/core/functions/complex.js +482 -0
  247. package/output/core/functions/decimal.d.ts +24 -0
  248. package/output/core/functions/decimal.js +664 -0
  249. package/output/core/functions/expand/expand.d.ts +42 -0
  250. package/output/core/functions/expand/expand.js +376 -0
  251. package/output/core/functions/fresnelNumeric.d.ts +12 -0
  252. package/output/core/functions/fresnelNumeric.js +123 -0
  253. package/output/core/functions/numeric.d.ts +140 -0
  254. package/output/core/functions/numeric.js +790 -0
  255. package/output/core/functions/rationalNormalization.d.ts +11 -0
  256. package/output/core/functions/rationalNormalization.js +117 -0
  257. package/output/core/functions/setFunction.d.ts +19 -0
  258. package/output/core/functions/setFunction.js +75 -0
  259. package/output/core/functions/string.d.ts +26 -0
  260. package/output/core/functions/string.js +101 -0
  261. package/output/core/functions/subst.d.ts +90 -0
  262. package/output/core/functions/subst.js +498 -0
  263. package/output/core/functions/utils.d.ts +24 -0
  264. package/output/core/functions/utils.js +51 -0
  265. package/output/core/types.d.ts +48 -0
  266. package/output/core/types.js +2 -0
  267. package/output/index.d.ts +339 -0
  268. package/output/index.js +715 -0
  269. package/output/math/defint/defintDecimal.d.ts +37 -0
  270. package/output/math/defint/defintDecimal.js +320 -0
  271. package/output/math/defint/defintNative.d.ts +57 -0
  272. package/output/math/defint/defintNative.js +292 -0
  273. package/output/math/geometry.d.ts +8 -0
  274. package/output/math/geometry.js +32 -0
  275. package/output/math/math.d.ts +655 -0
  276. package/output/math/math.js +1811 -0
  277. package/output/math/trig.d.ts +518 -0
  278. package/output/math/trig.js +1444 -0
  279. package/output/math/trunc.d.ts +19 -0
  280. package/output/math/trunc.js +36 -0
  281. package/output/math/utils.d.ts +60 -0
  282. package/output/math/utils.js +180 -0
  283. package/output/solve/classes/DecimalMatrix.d.ts +17 -0
  284. package/output/solve/classes/DecimalMatrix.js +86 -0
  285. package/output/solve/classes/FunctionSolver.d.ts +135 -0
  286. package/output/solve/classes/FunctionSolver.js +433 -0
  287. package/output/solve/classes/MultivariateSolver.d.ts +84 -0
  288. package/output/solve/classes/MultivariateSolver.js +240 -0
  289. package/output/solve/classes/PolynomialSolver.d.ts +85 -0
  290. package/output/solve/classes/PolynomialSolver.js +292 -0
  291. package/output/solve/classes/SolutionSet.d.ts +153 -0
  292. package/output/solve/classes/SolutionSet.js +357 -0
  293. package/output/solve/classes/Solver.d.ts +17 -0
  294. package/output/solve/classes/Solver.js +132 -0
  295. package/output/solve/classes/SymbolicSolver.d.ts +74 -0
  296. package/output/solve/classes/SymbolicSolver.js +601 -0
  297. package/output/solve/linsolve.d.ts +51 -0
  298. package/output/solve/linsolve.js +225 -0
  299. package/output/solve/solve.d.ts +34 -0
  300. package/output/solve/solve.js +260 -0
  301. package/output/solve/solveSystem.d.ts +31 -0
  302. package/output/solve/solveSystem.js +290 -0
  303. package/output/solve/utils.d.ts +9 -0
  304. package/output/solve/utils.js +62 -0
  305. package/output/utils/array.d.ts +42 -0
  306. package/output/utils/array.js +98 -0
  307. package/output/utils/debug.d.ts +131 -0
  308. package/output/utils/debug.js +295 -0
  309. package/output/utils/decimal.d.ts +3 -0
  310. package/output/utils/decimal.js +16 -0
  311. package/output/utils/numeric.d.ts +14 -0
  312. package/output/utils/numeric.js +51 -0
  313. package/output/utils/object.d.ts +26 -0
  314. package/output/utils/object.js +56 -0
  315. package/package.json +213 -57
  316. package/.travis.yml +0 -3
  317. package/Algebra.js +0 -4568
  318. package/BREAKING_CHANGES.md +0 -5
  319. package/CODE_OF_CONDUCT.md +0 -46
  320. package/CONTRIBUTING.md +0 -14
  321. package/Calculus.js +0 -2675
  322. package/Extra.js +0 -622
  323. package/Solve.js +0 -1783
  324. package/all.js +0 -16
  325. package/all.min.js +0 -1
  326. package/gulpfile.js +0 -16
  327. package/index.html +0 -158
  328. package/license.txt +0 -19
  329. package/nerdamer.core.js +0 -12510
  330. package/spec/LaTeX.spec.js +0 -302
  331. package/spec/TeXConvert.spec.js +0 -8
  332. package/spec/algebra.spec.js +0 -287
  333. package/spec/basic_parser.spec.js +0 -134
  334. package/spec/build.spec.js +0 -131
  335. package/spec/calculus.spec.js +0 -183
  336. package/spec/core.spec.js +0 -2970
  337. package/spec/extra.spec.js +0 -54
  338. package/spec/solve.spec.js +0 -126
  339. package/spec/support/jasmine.json +0 -11
  340. package/spec/support/utils.js +0 -42
  341. package/spec/text.spec.js +0 -132
@@ -0,0 +1,1166 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.GroebnerBudgetExceeded = void 0;
18
+ exports.Groebner = Groebner;
19
+ exports.groebnerBasisWithOptions = groebnerBasisWithOptions;
20
+ exports.eliminate = eliminate;
21
+ exports.idealMembership = idealMembership;
22
+ exports.reduceByBasis = reduceByBasis;
23
+ exports.solve = solve;
24
+ const bigint_1 = require("../../core/functions/bigint/bigint");
25
+ const MultiPoly_1 = require("./multiPoly/MultiPoly");
26
+ const poly_1 = require("./poly");
27
+ /**
28
+ * Thrown when a Groebner pair-count or basis-size budget is exhausted.
29
+ *
30
+ * {@link GroebnerBudgetExceeded.stats} is the live counter snapshot from the aborted
31
+ * computation. The error keeps that object by reference and does not freeze it.
32
+ */
33
+ class GroebnerBudgetExceeded extends Error {
34
+ /** Work counters at the point the budget was exceeded. */
35
+ stats;
36
+ /**
37
+ * @param message - Human-readable budget condition.
38
+ * @param stats - Counter snapshot retained on the error.
39
+ */
40
+ constructor(message, stats) {
41
+ super(message);
42
+ this.name = 'GroebnerBudgetExceeded';
43
+ this.stats = stats;
44
+ }
45
+ }
46
+ exports.GroebnerBudgetExceeded = GroebnerBudgetExceeded;
47
+ function ltOrNull(p, order, nVars) {
48
+ return mvLeadTermDense(p, order, nVars);
49
+ }
50
+ function polyIsZero(p) {
51
+ return p.terms.size === 0;
52
+ }
53
+ function numVarsFromPolys(...polys) {
54
+ let maxIndex = -1;
55
+ for (const p of polys) {
56
+ for (const key of p.terms.keys()) {
57
+ const exp = (0, MultiPoly_1.keyToExp)(key);
58
+ for (const [i, e] of exp.entries()) {
59
+ if (e > 0 && i > maxIndex) {
60
+ maxIndex = i;
61
+ }
62
+ }
63
+ }
64
+ }
65
+ return maxIndex + 1;
66
+ }
67
+ function polyKey(p) {
68
+ const parts = [];
69
+ for (const [k, c] of p.terms.entries()) {
70
+ if (c !== 0n) {
71
+ parts.push(`${k}:${c.toString()}`);
72
+ }
73
+ }
74
+ parts.sort();
75
+ return parts.join('|');
76
+ }
77
+ function divScalarExact(p, d) {
78
+ if (d === 0n) {
79
+ throw new Error('divScalarExact: divide by 0');
80
+ }
81
+ if (d === 1n) {
82
+ return p;
83
+ }
84
+ const out = new MultiPoly_1.MultiPoly();
85
+ for (const [k, c] of p.terms.entries()) {
86
+ if (c % d !== 0n) {
87
+ throw new Error('divScalarExact: non-exact');
88
+ }
89
+ const q = c / d;
90
+ if (q !== 0n) {
91
+ out.terms.set(k, q);
92
+ }
93
+ }
94
+ out.trim();
95
+ return out;
96
+ }
97
+ /**
98
+ * Primitive normalization under a given order:
99
+ * - normalize (combine like terms)
100
+ * - divide by coefficient content
101
+ * - ensure leading coefficient (under `order`) is positive
102
+ * - optionally make monic if exact
103
+ */
104
+ function primitiveNormalizeOrder(p0, order, nVars) {
105
+ if (polyIsZero(p0)) {
106
+ return MultiPoly_1.MultiPoly.zero();
107
+ }
108
+ let p = (0, poly_1.mvNormalize)(p0);
109
+ const { primitive } = (0, poly_1.mvDivideByCoeffContent)(p);
110
+ p = (0, poly_1.mvNormalize)(primitive);
111
+ // Fix sign using the requested order
112
+ const lt = ltOrNull(p, order, nVars);
113
+ if (lt && lt.coeff < 0n) {
114
+ p = (0, poly_1.negPoly)(p);
115
+ }
116
+ // Attempt monic (exact) if possible
117
+ const lt2 = ltOrNull(p, order, nVars);
118
+ if (lt2 && lt2.coeff !== 0n && lt2.coeff !== 1n) {
119
+ const lc = lt2.coeff;
120
+ let ok = true;
121
+ for (const c of p.terms.values()) {
122
+ if (c % lc !== 0n) {
123
+ ok = false;
124
+ break;
125
+ }
126
+ }
127
+ if (ok) {
128
+ p = (0, poly_1.mvNormalize)(divScalarExact(p, lc));
129
+ }
130
+ }
131
+ return p;
132
+ }
133
+ // ============================================================================
134
+ // S-polynomial (fraction-free)
135
+ // ============================================================================
136
+ function sPolynomialFF(f0, g0, order, nVars) {
137
+ const f = (0, poly_1.mvNormalize)(f0);
138
+ const g = (0, poly_1.mvNormalize)(g0);
139
+ const ltF = ltOrNull(f, order, nVars);
140
+ const ltG = ltOrNull(g, order, nVars);
141
+ if (!ltF || !ltG) {
142
+ return MultiPoly_1.MultiPoly.zero();
143
+ }
144
+ const l = expLcmDense(ltF.exp, ltG.exp);
145
+ const a = (0, poly_1.monoSubDense)(l, ltF.exp);
146
+ const b = (0, poly_1.monoSubDense)(l, ltG.exp);
147
+ // fraction-free S: lc(g)*x^a*f - lc(f)*x^b*g
148
+ const m1 = MultiPoly_1.MultiPoly.monomial(ltG.coeff, a);
149
+ const m2 = MultiPoly_1.MultiPoly.monomial(ltF.coeff, b);
150
+ const term1 = (0, poly_1.mulPoly)(f, m1);
151
+ const term2 = (0, poly_1.mulPoly)(g, m2);
152
+ return (0, poly_1.mvNormalize)((0, poly_1.subPoly)(term1, term2));
153
+ }
154
+ // ============================================================================
155
+ // Normal form (fraction-free)
156
+ // ============================================================================
157
+ /** Head reduction (fraction-free) using leading terms only. */
158
+ function normalFormFractionFree(f0, G, order, nVars) {
159
+ let f = (0, poly_1.mvNormalize)(f0);
160
+ if (polyIsZero(f)) {
161
+ return f;
162
+ }
163
+ while (true) {
164
+ const ltF = ltOrNull(f, order, nVars);
165
+ if (!ltF) {
166
+ break;
167
+ }
168
+ let reduced = false;
169
+ for (const g0 of G) {
170
+ const g = (0, poly_1.mvNormalize)(g0);
171
+ if (polyIsZero(g)) {
172
+ continue;
173
+ }
174
+ const ltG = ltOrNull(g, order, nVars);
175
+ if (!ltG) {
176
+ continue;
177
+ }
178
+ if (!(0, poly_1.monoDividesDense)(ltG.exp, ltF.exp)) {
179
+ continue;
180
+ }
181
+ const multExp = (0, poly_1.monoSubDense)(ltF.exp, ltG.exp);
182
+ // fraction-free cancel the head term:
183
+ // f := lc(g)*f - lc(f)*x^(multExp)*g
184
+ const left = (0, poly_1.scalePoly)(f, ltG.coeff);
185
+ const right = (0, poly_1.mulPoly)(g, MultiPoly_1.MultiPoly.monomial(ltF.coeff, multExp));
186
+ f = (0, poly_1.mvNormalize)((0, poly_1.subPoly)(left, right));
187
+ reduced = true;
188
+ break;
189
+ }
190
+ if (!reduced) {
191
+ break;
192
+ }
193
+ }
194
+ return f;
195
+ }
196
+ function collectDenseTermsDesc(p, order, nVars) {
197
+ const terms = [];
198
+ for (const [key, coeff] of p.terms.entries()) {
199
+ if (coeff === 0n) {
200
+ continue;
201
+ }
202
+ const expMap = (0, MultiPoly_1.keyToExp)(key);
203
+ const dense = new Array(nVars).fill(0);
204
+ for (const [i, e] of expMap.entries()) {
205
+ if (i >= 0 && i < nVars) {
206
+ dense[i] = e;
207
+ }
208
+ }
209
+ terms.push({ exp: dense, coeff });
210
+ }
211
+ terms.sort((a, b) => -compareMonomialDense(a.exp, b.exp, order));
212
+ return terms;
213
+ }
214
+ /**
215
+ * Fraction-free reduction allowing reduction of ANY term (tail reduction),
216
+ * without requiring coefficient division.
217
+ *
218
+ * For a term t in f and a reducer g whose LM divides t, we cancel t via:
219
+ * f := lc(g) * f - coeff(t) * x^(tExp - LM(g)) * g
220
+ * and then normalize/primitive-reduce to control coefficient growth.
221
+ */
222
+ function normalFormFractionFreeAllTerms(f0, G, order, nVars) {
223
+ let f = primitiveNormalizeOrder((0, poly_1.mvNormalize)(f0), order, nVars);
224
+ if (polyIsZero(f) || G.length === 0) {
225
+ return f;
226
+ }
227
+ // Pre-normalize all reducers and cache their leading terms once.
228
+ // The reducers don't change during the loop, so this avoids O(|G| * iterations)
229
+ // redundant normalizations.
230
+ const normalizedG = [];
231
+ for (const g0 of G) {
232
+ const g = primitiveNormalizeOrder((0, poly_1.mvNormalize)(g0), order, nVars);
233
+ if (polyIsZero(g)) {
234
+ continue;
235
+ }
236
+ const lt = ltOrNull(g, order, nVars);
237
+ if (!lt || lt.coeff === 0n) {
238
+ continue;
239
+ }
240
+ normalizedG.push({ poly: g, lt });
241
+ }
242
+ if (normalizedG.length === 0) {
243
+ return f;
244
+ }
245
+ while (true) {
246
+ let changed = false;
247
+ const fTerms = collectDenseTermsDesc(f, order, nVars);
248
+ if (fTerms.length === 0) {
249
+ break;
250
+ }
251
+ outer: for (const t of fTerms) {
252
+ for (const { poly: g, lt: ltG } of normalizedG) {
253
+ if (!(0, poly_1.monoDividesDense)(ltG.exp, t.exp)) {
254
+ continue;
255
+ }
256
+ const multExp = (0, poly_1.monoSubDense)(t.exp, ltG.exp);
257
+ const left = (0, poly_1.scalePoly)(f, ltG.coeff);
258
+ const right = (0, poly_1.mulPoly)(g, MultiPoly_1.MultiPoly.monomial(t.coeff, multExp));
259
+ f = primitiveNormalizeOrder((0, poly_1.mvNormalize)((0, poly_1.subPoly)(left, right)), order, nVars);
260
+ changed = true;
261
+ break outer;
262
+ }
263
+ }
264
+ if (!changed) {
265
+ break;
266
+ }
267
+ }
268
+ return f;
269
+ }
270
+ // ============================================================================
271
+ // Canonicalization
272
+ // ============================================================================
273
+ /** For each variable index 0..nVars-1, returns true if that variable appears in p. */
274
+ function variablePresence(p, nVars) {
275
+ const a = new Array(nVars).fill(false);
276
+ for (const k of p.terms.keys()) {
277
+ const exp = (0, MultiPoly_1.keyToExp)(k);
278
+ for (const [i, e] of exp.entries()) {
279
+ if (e > 0) {
280
+ a[i] = true;
281
+ }
282
+ }
283
+ }
284
+ return a;
285
+ }
286
+ function canonicalizeBasis(G0, order, nVars) {
287
+ const kept = [];
288
+ const input = G0.filter(p => !polyIsZero(p)).map(p => (0, poly_1.mvNormalize)(p));
289
+ // 1) Reduce each element against what we've already kept, then normalize.
290
+ for (const p of input) {
291
+ const r0 = normalFormFractionFree(p, kept, order, nVars);
292
+ const r1 = normalFormFractionFreeAllTerms(r0, kept, order, nVars);
293
+ const rn = primitiveNormalizeOrder(r1, order, nVars);
294
+ if (!polyIsZero(rn)) {
295
+ kept.push(rn);
296
+ }
297
+ }
298
+ // 2) Interreduce: each polynomial reduced by all others.
299
+ const out = [];
300
+ for (let i = 0; i < kept.length; i++) {
301
+ const others = kept.filter((_, j) => j !== i);
302
+ const ri0 = normalFormFractionFree(kept[i], others, order, nVars);
303
+ const ri1 = normalFormFractionFreeAllTerms(ri0, others, order, nVars);
304
+ const rn = primitiveNormalizeOrder(ri1, order, nVars);
305
+ if (!polyIsZero(rn)) {
306
+ out.push(rn);
307
+ }
308
+ }
309
+ // 3) Dedupe by leading monomial.
310
+ const betterSameLM = (p, q) => {
311
+ const pp = variablePresence(p, nVars);
312
+ const qp = variablePresence(q, nVars);
313
+ let cp = 0;
314
+ let cq = 0;
315
+ for (let i = 0; i < nVars; i++) {
316
+ if (pp[i]) {
317
+ cp++;
318
+ }
319
+ if (qp[i]) {
320
+ cq++;
321
+ }
322
+ }
323
+ if (cp !== cq) {
324
+ return cp < cq ? p : q;
325
+ }
326
+ if (p.terms.size !== q.terms.size) {
327
+ return p.terms.size < q.terms.size ? p : q;
328
+ }
329
+ const ltp = ltOrNull(p, order, nVars);
330
+ const ltq = ltOrNull(q, order, nVars);
331
+ if (ltp && ltq) {
332
+ const ap = (0, bigint_1.abs)(ltp.coeff);
333
+ const aq = (0, bigint_1.abs)(ltq.coeff);
334
+ if (ap !== aq) {
335
+ return ap < aq ? p : q;
336
+ }
337
+ }
338
+ return p;
339
+ };
340
+ const byPoly = new Map();
341
+ for (const p of out) {
342
+ const key = polyKey(p);
343
+ const prev = byPoly.get(key);
344
+ if (!prev) {
345
+ byPoly.set(key, p);
346
+ continue;
347
+ }
348
+ byPoly.set(key, betterSameLM(prev, p));
349
+ }
350
+ const arr = Array.from(byPoly.values());
351
+ arr.sort((p, q) => {
352
+ const pp = variablePresence(p, nVars);
353
+ const qp = variablePresence(q, nVars);
354
+ for (let i = 0; i < nVars; i++) {
355
+ const ap = pp[i] ? 1 : 0;
356
+ const aq = qp[i] ? 1 : 0;
357
+ if (ap !== aq) {
358
+ return ap - aq;
359
+ }
360
+ }
361
+ const ltp = ltOrNull(p, order, nVars);
362
+ const ltq = ltOrNull(q, order, nVars);
363
+ if (ltp && ltq) {
364
+ const c = compareMonomialDense(ltp.exp, ltq.exp, order);
365
+ if (c !== 0) {
366
+ return c;
367
+ }
368
+ }
369
+ return p.terms.size - q.terms.size;
370
+ });
371
+ return arr;
372
+ }
373
+ // ============================================================================
374
+ // Sugar strategy
375
+ // ============================================================================
376
+ /**
377
+ * Sugar degree of a polynomial: the total degree of its leading monomial.
378
+ * This is the initial sugar value assigned when a polynomial enters the basis.
379
+ */
380
+ function sugarDegree(p, order, nVars) {
381
+ const lt = ltOrNull(p, order, nVars);
382
+ if (!lt) {
383
+ return 0;
384
+ }
385
+ return monomialTotalDegreeDense(lt.exp);
386
+ }
387
+ /**
388
+ * Sugar degree of an S-polynomial S(f,g).
389
+ *
390
+ * sugar(S(f,g)) = max(sugar(f) + deg(lcm/LM(f)), sugar(g) + deg(lcm/LM(g)))
391
+ *
392
+ * where deg() is total degree difference between the lcm and respective LMs.
393
+ */
394
+ function sPairSugar(sugarF, sugarG, ltF, ltG) {
395
+ const lcm = expLcmDense(ltF, ltG);
396
+ const lcmDeg = monomialTotalDegreeDense(lcm);
397
+ const degF = monomialTotalDegreeDense(ltF);
398
+ const degG = monomialTotalDegreeDense(ltG);
399
+ return Math.max(sugarF + (lcmDeg - degF), sugarG + (lcmDeg - degG));
400
+ }
401
+ /**
402
+ * Insert a pair into a sugar-sorted queue.
403
+ * Primary sort: ascending sugar. Secondary: ascending lcmDeg.
404
+ * Uses binary insertion to keep the queue sorted.
405
+ */
406
+ function insertPairSorted(queue, pair) {
407
+ let lo = 0;
408
+ let hi = queue.length;
409
+ while (lo < hi) {
410
+ const mid = (lo + hi) >>> 1;
411
+ const m = queue[mid];
412
+ if (m.sugar < pair.sugar || (m.sugar === pair.sugar && m.lcmDeg < pair.lcmDeg)) {
413
+ lo = mid + 1;
414
+ }
415
+ else {
416
+ hi = mid;
417
+ }
418
+ }
419
+ queue.splice(lo, 0, pair);
420
+ }
421
+ function groebnerBasis(polys, orderOrOpts = 'LEX') {
422
+ const opts = typeof orderOrOpts === 'string' ? { order: orderOrOpts } : (orderOrOpts ?? {});
423
+ const order = opts.order ?? 'LEX';
424
+ const reduced = opts.reduced ?? true;
425
+ const maxPairsPopped = opts.maxPairsPopped;
426
+ const maxBasisSize = opts.maxBasisSize;
427
+ const strategy = opts.strategy ?? 'sugar';
428
+ const stats = {
429
+ pairsPopped: 0,
430
+ pairsSkippedProduct: 0,
431
+ pairsSkippedChain: 0,
432
+ pairsReducedToZero: 0,
433
+ basisAppends: 0,
434
+ };
435
+ const F = polys.filter(p => !polyIsZero(p)).map(p => (0, poly_1.mvNormalize)(p));
436
+ if (F.length === 0) {
437
+ return [];
438
+ }
439
+ const nVars = numVarsFromPolys(...F);
440
+ const G = F.slice();
441
+ // Sugar degrees tracked per basis element
442
+ const sugarArr = G.map(p => sugarDegree(p, order, nVars));
443
+ // Leading-monomial cache: index → dense exponent vector.
444
+ // Basis elements are append-only and immutable once added, so cache entries never stale.
445
+ const lmCache = G.map(p => {
446
+ const lt = ltOrNull(p, order, nVars);
447
+ return lt ? lt.exp : null;
448
+ });
449
+ // Buchberger criteria bookkeeping.
450
+ const zeroPairs = new Set();
451
+ const pairKey = (i, j) => (i < j ? `${i},${j}` : `${j},${i}`);
452
+ const lmExpCached = (idx) => {
453
+ if (idx < lmCache.length) {
454
+ return lmCache[idx];
455
+ }
456
+ const lt = ltOrNull(G[idx], order, nVars);
457
+ const exp = lt ? lt.exp : null;
458
+ lmCache[idx] = exp;
459
+ return exp;
460
+ };
461
+ const areCoprimeLM = (a, b) => {
462
+ for (let i = 0; i < nVars; i++) {
463
+ if (a[i] > 0 && b[i] > 0) {
464
+ return false;
465
+ }
466
+ }
467
+ return true;
468
+ };
469
+ const chainCriterionApplies = (i, j) => {
470
+ const li = lmExpCached(i);
471
+ const lj = lmExpCached(j);
472
+ if (!li || !lj) {
473
+ return false;
474
+ }
475
+ const lcm = expLcmDense(li, lj);
476
+ for (let k = 0; k < G.length; k++) {
477
+ if (k === i || k === j) {
478
+ continue;
479
+ }
480
+ const lk = lmExpCached(k);
481
+ if (!lk) {
482
+ continue;
483
+ }
484
+ if (!(0, poly_1.monoDividesDense)(lk, lcm)) {
485
+ continue;
486
+ }
487
+ if (zeroPairs.has(pairKey(i, k)) && zeroPairs.has(pairKey(k, j))) {
488
+ return true;
489
+ }
490
+ }
491
+ return false;
492
+ };
493
+ // Build initial pair queue
494
+ if (strategy === 'sugar') {
495
+ const pairQueue = [];
496
+ const makePair = (i, j) => {
497
+ const li = lmExpCached(i);
498
+ const lj = lmExpCached(j);
499
+ if (!li || !lj) {
500
+ return null;
501
+ }
502
+ const lcm = expLcmDense(li, lj);
503
+ return {
504
+ i,
505
+ j,
506
+ sugar: sPairSugar(sugarArr[i], sugarArr[j], li, lj),
507
+ lcmDeg: monomialTotalDegreeDense(lcm),
508
+ };
509
+ };
510
+ for (let i = 0; i < G.length; i++) {
511
+ for (let j = i + 1; j < G.length; j++) {
512
+ const cp = makePair(i, j);
513
+ if (cp) {
514
+ insertPairSorted(pairQueue, cp);
515
+ }
516
+ }
517
+ }
518
+ while (pairQueue.length > 0) {
519
+ const { i, j, sugar: pairSugar } = pairQueue[0];
520
+ pairQueue.shift();
521
+ stats.pairsPopped++;
522
+ if (maxPairsPopped !== undefined && stats.pairsPopped > maxPairsPopped) {
523
+ throw new GroebnerBudgetExceeded(`Groebner budget exceeded: pairsPopped > ${maxPairsPopped}`, stats);
524
+ }
525
+ // Buchberger criteria
526
+ const lmi = lmExpCached(i);
527
+ const lmj = lmExpCached(j);
528
+ if (lmi && lmj && areCoprimeLM(lmi, lmj)) {
529
+ stats.pairsSkippedProduct++;
530
+ zeroPairs.add(pairKey(i, j));
531
+ continue;
532
+ }
533
+ if (chainCriterionApplies(i, j)) {
534
+ stats.pairsSkippedChain++;
535
+ zeroPairs.add(pairKey(i, j));
536
+ continue;
537
+ }
538
+ const S = sPolynomialFF(G[i], G[j], order, nVars);
539
+ const h0 = normalFormFractionFree(S, G, order, nVars);
540
+ const h1 = normalFormFractionFreeAllTerms(h0, G, order, nVars);
541
+ const h = primitiveNormalizeOrder(h1, order, nVars);
542
+ if (polyIsZero(h)) {
543
+ stats.pairsReducedToZero++;
544
+ zeroPairs.add(pairKey(i, j));
545
+ }
546
+ else if (h.isConstant() && h.constantTerm() !== 0n) {
547
+ return [MultiPoly_1.MultiPoly.constant(1n)];
548
+ }
549
+ else {
550
+ const k = G.length;
551
+ G.push(h);
552
+ // Cache the LM of the new element
553
+ const ltH = ltOrNull(h, order, nVars);
554
+ lmCache.push(ltH ? ltH.exp : null);
555
+ // Sugar of the new element: max of the pair sugar and the actual degree
556
+ const hDeg = sugarDegree(h, order, nVars);
557
+ sugarArr.push(Math.max(pairSugar, hDeg));
558
+ stats.basisAppends++;
559
+ if (maxBasisSize !== undefined && G.length > maxBasisSize) {
560
+ throw new GroebnerBudgetExceeded(`Groebner budget exceeded: basis size > ${maxBasisSize}`, stats);
561
+ }
562
+ for (let t = 0; t < k; t++) {
563
+ const cp = makePair(t, k);
564
+ if (cp) {
565
+ insertPairSorted(pairQueue, cp);
566
+ }
567
+ }
568
+ }
569
+ }
570
+ }
571
+ else {
572
+ // FIFO strategy (original behavior)
573
+ const pairs = [];
574
+ for (let i = 0; i < G.length; i++) {
575
+ for (let j = i + 1; j < G.length; j++) {
576
+ pairs.push([i, j]);
577
+ }
578
+ }
579
+ while (pairs.length > 0) {
580
+ const [i, j] = pairs[0];
581
+ pairs.shift();
582
+ stats.pairsPopped++;
583
+ if (maxPairsPopped !== undefined && stats.pairsPopped > maxPairsPopped) {
584
+ throw new GroebnerBudgetExceeded(`Groebner budget exceeded: pairsPopped > ${maxPairsPopped}`, stats);
585
+ }
586
+ const lmi = lmExpCached(i);
587
+ const lmj = lmExpCached(j);
588
+ if (lmi && lmj && areCoprimeLM(lmi, lmj)) {
589
+ stats.pairsSkippedProduct++;
590
+ zeroPairs.add(pairKey(i, j));
591
+ continue;
592
+ }
593
+ if (chainCriterionApplies(i, j)) {
594
+ stats.pairsSkippedChain++;
595
+ zeroPairs.add(pairKey(i, j));
596
+ continue;
597
+ }
598
+ const S = sPolynomialFF(G[i], G[j], order, nVars);
599
+ const h0 = normalFormFractionFree(S, G, order, nVars);
600
+ const h1 = normalFormFractionFreeAllTerms(h0, G, order, nVars);
601
+ const h = primitiveNormalizeOrder(h1, order, nVars);
602
+ if (polyIsZero(h)) {
603
+ stats.pairsReducedToZero++;
604
+ zeroPairs.add(pairKey(i, j));
605
+ }
606
+ else if (h.isConstant() && h.constantTerm() !== 0n) {
607
+ return [MultiPoly_1.MultiPoly.constant(1n)];
608
+ }
609
+ else {
610
+ const k = G.length;
611
+ G.push(h);
612
+ // Cache the LM of the new element
613
+ const ltH = ltOrNull(h, order, nVars);
614
+ lmCache.push(ltH ? ltH.exp : null);
615
+ stats.basisAppends++;
616
+ if (maxBasisSize !== undefined && G.length > maxBasisSize) {
617
+ throw new GroebnerBudgetExceeded(`Groebner budget exceeded: basis size > ${maxBasisSize}`, stats);
618
+ }
619
+ for (let t = 0; t < k; t++) {
620
+ pairs.push([t, k]);
621
+ }
622
+ }
623
+ }
624
+ }
625
+ return reduced ? canonicalizeBasis(G, order, nVars) : G;
626
+ }
627
+ // ============================================================================
628
+ // Public API: Groebner basis
629
+ // ============================================================================
630
+ /**
631
+ * Computes a Groebner basis for an exact integer-coefficient ideal.
632
+ *
633
+ * @remarks
634
+ * Variable indices in every {@link MultiPoly} must agree with `vars`. The Buchberger
635
+ * engine uses fraction-free reduction. With `reduced: true`, it interreduces, removes
636
+ * coefficient content, normalizes leading signs, and makes a polynomial monic only
637
+ * when exact integer division permits it. The returned basis is sorted deterministically,
638
+ * and the routine works from normalized copies rather than editing the supplied generators.
639
+ *
640
+ * @param polys - Ideal generators over integer coefficients.
641
+ * @param vars - Variable names aligned with exponent indices.
642
+ * @param order - Monomial order; defaults to `LEX`.
643
+ * @param reduced - Return the normalized/interreduced basis; defaults to `true`.
644
+ * @returns New basis polynomials in deterministic presentation order.
645
+ */
646
+ function Groebner(polys, vars, order = 'LEX', reduced = true) {
647
+ if (polys.length === 0) {
648
+ return [];
649
+ }
650
+ const nVars = vars.length;
651
+ const G = groebnerBasis(polys, { order, reduced });
652
+ // Present basis deterministically in a SymPy-like order:
653
+ // 1) Prefer polynomials that involve earlier variables (presence vector, lex-desc)
654
+ // 2) Then by leading monomial under the chosen order (ascending)
655
+ // 3) Then by term count (ascending)
656
+ const cache = new Map();
657
+ const getInfo = (p) => {
658
+ let info = cache.get(p);
659
+ if (!info) {
660
+ info = { pv: variablePresence(p, nVars), lt: ltOrNull(p, order, nVars) };
661
+ cache.set(p, info);
662
+ }
663
+ return info;
664
+ };
665
+ const cmp = (p, q) => {
666
+ const ip = getInfo(p);
667
+ const iq = getInfo(q);
668
+ for (let i = 0; i < nVars; i++) {
669
+ const ap = ip.pv[i] ? 1 : 0;
670
+ const aq = iq.pv[i] ? 1 : 0;
671
+ if (ap !== aq) {
672
+ return ap > aq ? -1 : 1;
673
+ }
674
+ }
675
+ if (ip.lt && iq.lt) {
676
+ const c = compareMonomialDense(ip.lt.exp, iq.lt.exp, order);
677
+ if (c !== 0) {
678
+ return c;
679
+ }
680
+ }
681
+ const tp = p.terms.size;
682
+ const tq = q.terms.size;
683
+ if (tp !== tq) {
684
+ return tp < tq ? -1 : 1;
685
+ }
686
+ return 0;
687
+ };
688
+ return [...G].sort(cmp);
689
+ }
690
+ /**
691
+ * Computes a Groebner basis with ordering, strategy, and budget control.
692
+ *
693
+ * @param polys - Exact integer-coefficient generators with consistent variable indices.
694
+ * @param opts - Ordering, normalization, pair-selection, and deterministic budget options.
695
+ * @returns New basis polynomials. Work statistics are exposed only when a budget is
696
+ * exceeded; successful calls return the basis itself.
697
+ * @throws {@link GroebnerBudgetExceeded} Thrown after a configured pair-count or
698
+ * basis-size limit is exceeded.
699
+ */
700
+ function groebnerBasisWithOptions(polys, opts) {
701
+ return groebnerBasis(polys, opts);
702
+ }
703
+ // ============================================================================
704
+ // Elimination
705
+ // ============================================================================
706
+ /**
707
+ * Computes the part of the ideal that lies in the polynomial ring generated by `keepVars`.
708
+ *
709
+ * @param polys - Exact integer-coefficient generators.
710
+ * @param vars - All variables in exponent-index order. Variables to eliminate must
711
+ * precede retained variables for lexicographic elimination.
712
+ * @param keepVars - Variables to retain, expected to be a suffix of `vars`.
713
+ * @param opts - Reduction, pair strategy, and budgets; order is always `LEX`.
714
+ * @returns New basis polynomials involving only retained variables.
715
+ * @throws {@link GroebnerBudgetExceeded} Thrown when a configured budget is exceeded.
716
+ */
717
+ function eliminate(polys, vars, keepVars, opts) {
718
+ if (polys.length === 0) {
719
+ return [];
720
+ }
721
+ // Determine which variable indices to eliminate
722
+ const keepSet = new Set(keepVars);
723
+ const eliminateIndices = new Set();
724
+ for (let i = 0; i < vars.length; i++) {
725
+ if (!keepSet.has(vars[i])) {
726
+ eliminateIndices.add(i);
727
+ }
728
+ }
729
+ // Compute LEX basis (elimination order: variables to eliminate are first = most significant)
730
+ const G = groebnerBasis(polys, { ...opts, order: 'LEX', reduced: true });
731
+ // Extract polynomials that don't involve any eliminated variable
732
+ const nVars = vars.length;
733
+ const result = [];
734
+ for (const g of G) {
735
+ const pv = variablePresence(g, nVars);
736
+ let ok = true;
737
+ for (const ei of eliminateIndices) {
738
+ if (pv[ei]) {
739
+ ok = false;
740
+ break;
741
+ }
742
+ }
743
+ if (ok) {
744
+ result.push(g);
745
+ }
746
+ }
747
+ return result;
748
+ }
749
+ // ============================================================================
750
+ // Ideal membership
751
+ // ============================================================================
752
+ /**
753
+ * Tests exact ideal membership by reducing against a computed Groebner basis.
754
+ *
755
+ * @param f - Polynomial to test.
756
+ * @param polys - Exact integer-coefficient ideal generators.
757
+ * @param order - Monomial order; defaults to `LEX`.
758
+ * @param opts - Pair strategy and deterministic budgets.
759
+ * @returns Whether the normal form of `f` is zero.
760
+ * @throws {@link GroebnerBudgetExceeded} Thrown when basis computation exceeds a budget.
761
+ */
762
+ function idealMembership(f, polys, order = 'LEX', opts) {
763
+ if (polyIsZero(f)) {
764
+ return true;
765
+ }
766
+ if (polys.length === 0) {
767
+ return false;
768
+ }
769
+ const G = groebnerBasis(polys, { ...opts, order, reduced: true });
770
+ return polyIsZero(reduceByBasis(f, G, order));
771
+ }
772
+ /**
773
+ * Reduces a polynomial by a basis or arbitrary reducer set using fraction-free arithmetic.
774
+ *
775
+ * @remarks
776
+ * This does not compute or verify a Groebner basis. The result is a canonical ideal
777
+ * normal form only when `basis` is already a basis for the selected order. Reduction
778
+ * works from normalized polynomial copies, leaving the supplied objects alone.
779
+ *
780
+ * @param f - Polynomial to reduce.
781
+ * @param basis - Reducers, normally a Groebner basis.
782
+ * @param order - Monomial order used to select leading terms.
783
+ * @returns The primitive-normalized remainder.
784
+ */
785
+ function reduceByBasis(f, basis, order = 'LEX') {
786
+ if (polyIsZero(f) || basis.length === 0) {
787
+ return (0, poly_1.mvNormalize)(f);
788
+ }
789
+ const nVars = numVarsFromPolys(f, ...basis);
790
+ const h0 = normalFormFractionFree(f, basis, order, nVars);
791
+ const h1 = normalFormFractionFreeAllTerms(h0, basis, order, nVars);
792
+ return primitiveNormalizeOrder(h1, order, nVars);
793
+ }
794
+ /**
795
+ * Solves a zero-dimensional polynomial system for rational coordinates.
796
+ *
797
+ * @remarks
798
+ * This is the low-level exact solver used with the Groebner representation, not the
799
+ * ordinary expression solver. It computes a reduced lexicographic basis, walks variables
800
+ * from least to most significant, applies the Rational Root Theorem to univariate
801
+ * constraints, and substitutes each partial assignment into its own basis copy.
802
+ * Irrational and complex roots are outside this routine's result set.
803
+ *
804
+ * @param polys - Exact integer-coefficient system generators.
805
+ * @param vars - Variables in lexicographic elimination order, most significant first.
806
+ * @param opts - Pair strategy and budgets forwarded to basis computation.
807
+ * @returns New solution maps. An empty array can mean inconsistency, no rational roots,
808
+ * or a non-finite/unsupported triangular result; these cases are not distinguished.
809
+ * @throws {@link GroebnerBudgetExceeded} Thrown when basis computation exceeds a budget.
810
+ */
811
+ function solve(polys, vars, opts) {
812
+ if (polys.length === 0 || vars.length === 0) {
813
+ return [new Map()];
814
+ }
815
+ const G = groebnerBasis(polys, { ...opts, order: 'LEX', reduced: true });
816
+ // Trivial: if the basis is {1}, the system is inconsistent.
817
+ if (G.length === 1 && G[0].isConstant() && G[0].constantTerm() !== 0n) {
818
+ return [];
819
+ }
820
+ if (G.length === 0) {
821
+ // Ideal is {0}, meaning every point is a solution — infinite solutions.
822
+ return [];
823
+ }
824
+ const nVars = vars.length;
825
+ return backSubstitute(G, vars, nVars);
826
+ }
827
+ /**
828
+ * Triangular back-substitution on a LEX Gröbner basis.
829
+ *
830
+ * Walks variables from last (least significant) to first, finding univariate
831
+ * polynomials, extracting rational roots, and substituting back.
832
+ *
833
+ * Each partial solution carries its own partially-substituted copy of the basis,
834
+ * so when we extend with a new variable binding we only need to substitute that
835
+ * one new value — not redo all prior substitutions.
836
+ */
837
+ function backSubstitute(G, vars, nVars) {
838
+ // Start with a single empty partial assignment, basis = original
839
+ let partials = [
840
+ {
841
+ assignment: new Map(),
842
+ basis: G.map(g => (0, poly_1.mvNormalize)(g)),
843
+ },
844
+ ];
845
+ // Solve from last variable to first
846
+ for (let v = nVars - 1; v >= 0; v--) {
847
+ const nextPartials = [];
848
+ for (const { assignment, basis } of partials) {
849
+ // Find polynomials that are now univariate in variable v
850
+ const univariates = basis.filter(p => {
851
+ if (polyIsZero(p)) {
852
+ return false;
853
+ }
854
+ for (const key of p.terms.keys()) {
855
+ const exp = (0, MultiPoly_1.keyToExp)(key);
856
+ for (const [i, e] of exp.entries()) {
857
+ if (e > 0 && i !== v) {
858
+ return false;
859
+ }
860
+ }
861
+ }
862
+ return p.degree(v) > 0;
863
+ });
864
+ if (univariates.length === 0) {
865
+ // No constraint on this variable — push forward as-is.
866
+ nextPartials.push({ assignment: new Map(assignment), basis });
867
+ continue;
868
+ }
869
+ // Extract rational roots of the first univariate polynomial.
870
+ const poly = univariates[0];
871
+ const roots = rationalRootsUnivariate(poly, v);
872
+ // Filter roots by consistency with other univariates
873
+ for (const root of roots) {
874
+ let consistent = true;
875
+ for (let k = 1; k < univariates.length; k++) {
876
+ if (!evaluatesToZero(univariates[k], v, root)) {
877
+ consistent = false;
878
+ break;
879
+ }
880
+ }
881
+ if (!consistent) {
882
+ continue;
883
+ }
884
+ // Incrementally substitute only variable v into the basis
885
+ const newBasis = basis.map(p => {
886
+ const deg = p.degree(v);
887
+ if (deg <= 0) {
888
+ return p;
889
+ }
890
+ let q = p;
891
+ if (root.d !== 1n) {
892
+ q = scaleByDenomPow(q, v, root.d);
893
+ }
894
+ return (0, poly_1.mvNormalize)((0, poly_1.substituteVarIndex)(q, v, root.n));
895
+ });
896
+ const ext = new Map(assignment);
897
+ ext.set(v, root);
898
+ nextPartials.push({ assignment: ext, basis: newBasis });
899
+ }
900
+ }
901
+ partials = nextPartials;
902
+ }
903
+ // Convert variable indices back to names
904
+ return partials.map(({ assignment }) => {
905
+ const named = new Map();
906
+ for (const [vi, val] of assignment) {
907
+ if (vi < vars.length) {
908
+ named.set(vars[vi], val);
909
+ }
910
+ }
911
+ return named;
912
+ });
913
+ }
914
+ /**
915
+ * Scale polynomial so that substituting x_v = n (integer) correctly accounts
916
+ * for a rational value n/d. For each term with x_v^k, multiply coefficient by d^(maxDeg - k).
917
+ * This gives: p(n/d) * d^maxDeg = result evaluated at x_v = n.
918
+ */
919
+ function scaleByDenomPow(p, varIndex, d) {
920
+ const maxDeg = p.degree(varIndex);
921
+ if (maxDeg <= 0 || d === 1n) {
922
+ return p;
923
+ }
924
+ // Precompute powers of d up to maxDeg
925
+ const dPowers = new Array(maxDeg + 1);
926
+ dPowers[0] = 1n;
927
+ for (let i = 1; i <= maxDeg; i++) {
928
+ dPowers[i] = dPowers[i - 1] * d;
929
+ }
930
+ const out = new MultiPoly_1.MultiPoly();
931
+ for (const [key, coeff] of p.terms) {
932
+ const exp = (0, MultiPoly_1.keyToExp)(key);
933
+ const k = exp.get(varIndex) ?? 0;
934
+ const newCoeff = coeff * dPowers[maxDeg - k];
935
+ if (newCoeff !== 0n) {
936
+ out.terms.set(key, newCoeff);
937
+ }
938
+ }
939
+ out.trim();
940
+ return out;
941
+ }
942
+ /**
943
+ * Find all rational roots p/q of a univariate polynomial in variable `varIndex`.
944
+ * Uses the Rational Root Theorem: p divides the constant term, q divides the leading coefficient.
945
+ */
946
+ function rationalRootsUnivariate(poly, varIndex) {
947
+ const deg = poly.degree(varIndex);
948
+ if (deg <= 0) {
949
+ return [];
950
+ }
951
+ // Extract coefficients as bigint array (index = power of varIndex)
952
+ const coeffs = new Array(deg + 1).fill(0n);
953
+ for (const [key, coeff] of poly.terms) {
954
+ const exp = (0, MultiPoly_1.keyToExp)(key);
955
+ const d = exp.get(varIndex) ?? 0;
956
+ if (d <= deg) {
957
+ coeffs[d] += coeff;
958
+ }
959
+ }
960
+ // Trim leading zeros
961
+ let actualDeg = deg;
962
+ while (actualDeg > 0 && coeffs[actualDeg] === 0n) {
963
+ actualDeg--;
964
+ }
965
+ if (actualDeg === 0) {
966
+ return [];
967
+ }
968
+ const lc = coeffs[actualDeg];
969
+ const ct = coeffs[0]; // constant term
970
+ if (ct === 0n) {
971
+ // x = 0 is a root; factor it out and recurse
972
+ const roots = [{ n: 0n, d: 1n }];
973
+ // Shift down: divide by x
974
+ const shiftedPoly = new MultiPoly_1.MultiPoly();
975
+ for (let i = 1; i <= actualDeg; i++) {
976
+ if (coeffs[i] !== 0n) {
977
+ const exp = new Map();
978
+ if (i > 1) {
979
+ exp.set(varIndex, i - 1);
980
+ }
981
+ shiftedPoly.terms.set((0, MultiPoly_1.expToKey)(exp), coeffs[i]);
982
+ }
983
+ }
984
+ const moreRoots = rationalRootsUnivariate(shiftedPoly, varIndex);
985
+ for (const r of moreRoots) {
986
+ if (r.n !== 0n) {
987
+ roots.push(r);
988
+ }
989
+ }
990
+ return roots;
991
+ }
992
+ // Divisors of |ct| and |lc|
993
+ const pDivisors = positiveDivisors((0, bigint_1.abs)(ct));
994
+ const qDivisors = positiveDivisors((0, bigint_1.abs)(lc));
995
+ const roots = [];
996
+ const seen = new Set();
997
+ for (const p of pDivisors) {
998
+ for (const q of qDivisors) {
999
+ // Try ±p/q
1000
+ for (const sign of [1n, -1n]) {
1001
+ const num = sign * p;
1002
+ // Reduce to lowest terms
1003
+ const g = (0, bigint_1.GCD)((0, bigint_1.abs)(num), q);
1004
+ const rn = num / g;
1005
+ const rd = q / g;
1006
+ const key = `${rn}/${rd}`;
1007
+ if (seen.has(key)) {
1008
+ continue;
1009
+ }
1010
+ seen.add(key);
1011
+ if (evaluatesCoeffsToZero(coeffs, actualDeg, rn, rd)) {
1012
+ roots.push({ n: rn, d: rd });
1013
+ }
1014
+ }
1015
+ }
1016
+ }
1017
+ return roots;
1018
+ }
1019
+ /** Evaluate polynomial coefficients at n/d: check if sum(c_i * n^i * d^(deg-i)) == 0 */
1020
+ function evaluatesCoeffsToZero(coeffs, deg, n, d) {
1021
+ let result = 0n;
1022
+ let nPow = 1n;
1023
+ let dPow = 1n;
1024
+ // Precompute d^deg
1025
+ for (let i = 0; i < deg; i++) {
1026
+ dPow *= d;
1027
+ }
1028
+ for (let i = 0; i <= deg; i++) {
1029
+ result += coeffs[i] * nPow * dPow;
1030
+ nPow *= n;
1031
+ if (i < deg && d !== 0n) {
1032
+ dPow /= d;
1033
+ }
1034
+ }
1035
+ return result === 0n;
1036
+ }
1037
+ /** Check if substituting varIndex = n/d into poly gives zero. */
1038
+ function evaluatesToZero(poly, varIndex, val) {
1039
+ const deg = poly.degree(varIndex);
1040
+ if (deg <= 0) {
1041
+ return polyIsZero(poly) || poly.constantTerm() === 0n;
1042
+ }
1043
+ let p = poly;
1044
+ if (val.d !== 1n) {
1045
+ p = scaleByDenomPow(p, varIndex, val.d);
1046
+ }
1047
+ const result = (0, poly_1.substituteVarIndex)(p, varIndex, val.n);
1048
+ return polyIsZero((0, poly_1.mvNormalize)(result));
1049
+ }
1050
+ /** Positive divisors of a positive bigint. */
1051
+ function positiveDivisors(n) {
1052
+ if (n === 0n) {
1053
+ return [0n];
1054
+ }
1055
+ if (n < 0n) {
1056
+ n = -n;
1057
+ }
1058
+ const divs = [];
1059
+ let i = 1n;
1060
+ while (i * i <= n) {
1061
+ if (n % i === 0n) {
1062
+ divs.push(i);
1063
+ if (i !== n / i) {
1064
+ divs.push(n / i);
1065
+ }
1066
+ }
1067
+ i++;
1068
+ }
1069
+ return divs;
1070
+ }
1071
+ // ============================================================================
1072
+ // Monomial order comparisons and utilities
1073
+ // ============================================================================
1074
+ /**
1075
+ * Leading term under a specified monomial order using dense exponent vectors.
1076
+ * Returns null for the zero polynomial.
1077
+ */
1078
+ function mvLeadTermDense(p, order, nVars) {
1079
+ if (p.terms.size === 0) {
1080
+ return null;
1081
+ }
1082
+ let bestExp = null;
1083
+ let bestCoeff = 0n;
1084
+ for (const [key, coeff] of p.terms.entries()) {
1085
+ if (coeff === 0n) {
1086
+ continue;
1087
+ }
1088
+ const expMap = (0, MultiPoly_1.keyToExp)(key);
1089
+ const dense = new Array(nVars).fill(0);
1090
+ for (const [i, e] of expMap.entries()) {
1091
+ if (i >= 0 && i < nVars) {
1092
+ dense[i] = e;
1093
+ }
1094
+ }
1095
+ if (!bestExp || compareMonomialDense(dense, bestExp, order) > 0) {
1096
+ bestExp = dense;
1097
+ bestCoeff = coeff;
1098
+ }
1099
+ }
1100
+ return bestExp ? { exp: bestExp, coeff: bestCoeff } : null;
1101
+ }
1102
+ /** Componentwise lcm (max). */
1103
+ function expLcmDense(a, b) {
1104
+ const n = Math.max(a.length, b.length);
1105
+ const out = new Array(n).fill(0);
1106
+ for (let i = 0; i < n; i++) {
1107
+ out[i] = Math.max(a[i] ?? 0, b[i] ?? 0);
1108
+ }
1109
+ return out;
1110
+ }
1111
+ /**
1112
+ * Compare dense exponent vectors under a named monomial order.
1113
+ *
1114
+ * Returns:
1115
+ * - 1 if a > b
1116
+ * - -1 if a < b
1117
+ * - 0 if equal
1118
+ *
1119
+ * Convention: variable priority is index order: x0 > x1 > x2 > ...
1120
+ */
1121
+ function compareMonomialDense(a, b, order) {
1122
+ const n = Math.max(a.length, b.length);
1123
+ if (order === 'LEX') {
1124
+ for (let i = 0; i < n; i++) {
1125
+ const ai = a[i] ?? 0;
1126
+ const bi = b[i] ?? 0;
1127
+ if (ai !== bi) {
1128
+ return ai > bi ? 1 : -1;
1129
+ }
1130
+ }
1131
+ return 0;
1132
+ }
1133
+ const da = monomialTotalDegreeDense(a);
1134
+ const db = monomialTotalDegreeDense(b);
1135
+ if (da !== db) {
1136
+ return da > db ? 1 : -1;
1137
+ }
1138
+ if (order === 'GRLEX') {
1139
+ for (let i = 0; i < n; i++) {
1140
+ const ai = a[i] ?? 0;
1141
+ const bi = b[i] ?? 0;
1142
+ if (ai !== bi) {
1143
+ return ai > bi ? 1 : -1;
1144
+ }
1145
+ }
1146
+ return 0;
1147
+ }
1148
+ // GREVLEX: same total degree; compare from last variable down.
1149
+ // At the last index where they differ, the monomial with the *smaller*
1150
+ // exponent is considered larger.
1151
+ for (let i = n - 1; i >= 0; i--) {
1152
+ const ai = a[i] ?? 0;
1153
+ const bi = b[i] ?? 0;
1154
+ if (ai !== bi) {
1155
+ return ai < bi ? 1 : -1;
1156
+ }
1157
+ }
1158
+ return 0;
1159
+ }
1160
+ function monomialTotalDegreeDense(m) {
1161
+ let s = 0;
1162
+ for (let i = 0; i < m.length; i++) {
1163
+ s += m[i] ?? 0;
1164
+ }
1165
+ return s;
1166
+ }