solve-engine 1.0.0-beta.7 → 1.0.0

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 (205) hide show
  1. package/dist/{BytecodeBuilder-3egMyuDi.d.cts → BytecodeBuilder-B0xskcv5.d.cts} +2 -1
  2. package/dist/{BytecodeBuilder-3egMyuDi.d.ts → BytecodeBuilder-B0xskcv5.d.ts} +2 -1
  3. package/dist/{Configuration-fVpd-HC-.d.cts → Configuration-B-G5gTRn.d.cts} +107 -29
  4. package/dist/{Configuration-fVpd-HC-.d.ts → Configuration-B-G5gTRn.d.ts} +107 -29
  5. package/dist/{EngineError-DvPSnhZi.d.cts → EngineError-LU7W7AgI.d.cts} +96 -16
  6. package/dist/{EngineError-DvPSnhZi.d.ts → EngineError-LU7W7AgI.d.ts} +96 -16
  7. package/dist/{Lexer-W9MBOP0V.d.ts → Lexer-BN5mt30n.d.ts} +16 -0
  8. package/dist/{Lexer-Cfm79Dak.d.cts → Lexer-D78E2yDW.d.cts} +16 -0
  9. package/dist/{PackageRegistry-CjDt-Jy_.d.ts → PackageRegistry-DFeINv2O.d.ts} +54 -13
  10. package/dist/{PackageRegistry-pHtKythi.d.cts → PackageRegistry-u2zdfFaj.d.cts} +54 -13
  11. package/dist/{Parselet-DEdF9I7n.d.ts → Parselet-BaySkMV3.d.ts} +37 -5
  12. package/dist/{Parselet-Cu0bLSis.d.cts → Parselet-DuI1Pjiq.d.cts} +37 -5
  13. package/dist/{ScopeManager-CKfQtl7l.d.cts → ScopeManager-B6GzdhVG.d.cts} +75 -6
  14. package/dist/{ScopeManager-B5k0nk05.d.ts → ScopeManager-udv4Twwq.d.ts} +75 -6
  15. package/dist/{VMCheckpoints-BmDwIXgK.d.ts → VMCheckpoints-BiaIlOOY.d.ts} +4 -4
  16. package/dist/{VMCheckpoints-CqFuLwrT.d.cts → VMCheckpoints-DwLjivM7.d.cts} +4 -4
  17. package/dist/{Value-KtAQNHur.d.cts → Value-CXJqDH9J.d.cts} +5 -2
  18. package/dist/{Value-KtAQNHur.d.ts → Value-CXJqDH9J.d.ts} +5 -2
  19. package/dist/{chunk-CSG5XVNJ.cjs → chunk-3AFRJYP4.cjs} +556 -414
  20. package/dist/chunk-3AFRJYP4.cjs.map +1 -0
  21. package/dist/chunk-3BOK2LDK.js +17 -0
  22. package/dist/chunk-3BOK2LDK.js.map +1 -0
  23. package/dist/{chunk-6RDSQURE.js → chunk-3D7V24DG.js} +57 -6
  24. package/dist/chunk-3D7V24DG.js.map +1 -0
  25. package/dist/{chunk-MEOHSQEH.js → chunk-3LAEG75D.js} +47 -15
  26. package/dist/chunk-3LAEG75D.js.map +1 -0
  27. package/dist/{chunk-P75775WC.js → chunk-3VNGREMU.js} +118 -41
  28. package/dist/chunk-3VNGREMU.js.map +1 -0
  29. package/dist/{chunk-4AVD7NZW.cjs → chunk-4B2CNWQU.cjs} +13 -7
  30. package/dist/chunk-4B2CNWQU.cjs.map +1 -0
  31. package/dist/{chunk-6GCKCWLB.js → chunk-526PMQOA.js} +126 -69
  32. package/dist/chunk-526PMQOA.js.map +1 -0
  33. package/dist/{chunk-2CS6OMZK.js → chunk-5F4C26RU.js} +8 -5
  34. package/dist/chunk-5F4C26RU.js.map +1 -0
  35. package/dist/{chunk-M4N5GA5Z.cjs → chunk-5KMIY374.cjs} +57 -6
  36. package/dist/chunk-5KMIY374.cjs.map +1 -0
  37. package/dist/{chunk-R24DI24X.cjs → chunk-5LI5EPGJ.cjs} +6 -5
  38. package/dist/chunk-5LI5EPGJ.cjs.map +1 -0
  39. package/dist/{chunk-BAMZT3GL.js → chunk-5WVP4YHP.js} +939 -134
  40. package/dist/chunk-5WVP4YHP.js.map +1 -0
  41. package/dist/{chunk-MZOVP3WI.cjs → chunk-6KFYJ6TD.cjs} +3 -2
  42. package/dist/chunk-6KFYJ6TD.cjs.map +1 -0
  43. package/dist/{chunk-BXKJLM5R.cjs → chunk-6WFMPTGB.cjs} +972 -636
  44. package/dist/chunk-6WFMPTGB.cjs.map +1 -0
  45. package/dist/{chunk-2MV4HBKC.cjs → chunk-A2N2GFCG.cjs} +10 -10
  46. package/dist/{chunk-2MV4HBKC.cjs.map → chunk-A2N2GFCG.cjs.map} +1 -1
  47. package/dist/chunk-ADBA3XBE.cjs +20 -0
  48. package/dist/chunk-ADBA3XBE.cjs.map +1 -0
  49. package/dist/{chunk-MBNQVDVC.cjs → chunk-AMOJYLG2.cjs} +16 -10
  50. package/dist/chunk-AMOJYLG2.cjs.map +1 -0
  51. package/dist/{chunk-CLVQBF5C.cjs → chunk-AQNHHJMJ.cjs} +5 -5
  52. package/dist/{chunk-CLVQBF5C.cjs.map → chunk-AQNHHJMJ.cjs.map} +1 -1
  53. package/dist/{chunk-AHCLWAM5.cjs → chunk-BHTNFEGZ.cjs} +13 -13
  54. package/dist/chunk-BHTNFEGZ.cjs.map +1 -0
  55. package/dist/{chunk-35IWNZPJ.cjs → chunk-E4NUZXVK.cjs} +58 -27
  56. package/dist/chunk-E4NUZXVK.cjs.map +1 -0
  57. package/dist/{chunk-P33SBYGG.js → chunk-GLG5ZMX7.js} +54 -23
  58. package/dist/chunk-GLG5ZMX7.js.map +1 -0
  59. package/dist/{chunk-BVAR5J2F.js → chunk-GQCOSXMG.js} +117 -6
  60. package/dist/chunk-GQCOSXMG.js.map +1 -0
  61. package/dist/{chunk-QDMPOAI7.cjs → chunk-HDP7VK3C.cjs} +1118 -313
  62. package/dist/chunk-HDP7VK3C.cjs.map +1 -0
  63. package/dist/{chunk-GVWHCL2N.js → chunk-HMOISHXR.js} +217 -75
  64. package/dist/chunk-HMOISHXR.js.map +1 -0
  65. package/dist/{chunk-MTX2KVU7.cjs → chunk-HVQFNJKE.cjs} +122 -90
  66. package/dist/chunk-HVQFNJKE.cjs.map +1 -0
  67. package/dist/{chunk-OC6LTXYS.cjs → chunk-JMXUNXQS.cjs} +126 -7
  68. package/dist/chunk-JMXUNXQS.cjs.map +1 -0
  69. package/dist/{chunk-WWOLFXTX.cjs → chunk-KBSXGXPM.cjs} +10 -7
  70. package/dist/chunk-KBSXGXPM.cjs.map +1 -0
  71. package/dist/{chunk-YU2CUNFO.js → chunk-KV7UW6T6.js} +11 -5
  72. package/dist/chunk-KV7UW6T6.js.map +1 -0
  73. package/dist/{chunk-CCJPSHGI.cjs → chunk-N7FNVFXW.cjs} +4 -12
  74. package/dist/chunk-N7FNVFXW.cjs.map +1 -0
  75. package/dist/{chunk-CYFK5SY2.js → chunk-O3ANBHSA.js} +169 -39
  76. package/dist/chunk-O3ANBHSA.js.map +1 -0
  77. package/dist/{chunk-NZFKROS7.cjs → chunk-PA4VC73I.cjs} +130 -72
  78. package/dist/chunk-PA4VC73I.cjs.map +1 -0
  79. package/dist/{chunk-NJTXJ5AG.cjs → chunk-PFUESQTW.cjs} +18 -18
  80. package/dist/{chunk-NJTXJ5AG.cjs.map → chunk-PFUESQTW.cjs.map} +1 -1
  81. package/dist/{chunk-SFQWJMKT.js → chunk-QY25VWBF.js} +51 -12
  82. package/dist/chunk-QY25VWBF.js.map +1 -0
  83. package/dist/{chunk-WC5FFSHB.js → chunk-R3PY4G7J.js} +3 -3
  84. package/dist/{chunk-WC5FFSHB.js.map → chunk-R3PY4G7J.js.map} +1 -1
  85. package/dist/{chunk-M5E34VG5.js → chunk-SBUFNJVE.js} +3 -3
  86. package/dist/{chunk-M5E34VG5.js.map → chunk-SBUFNJVE.js.map} +1 -1
  87. package/dist/{chunk-UKPGSAZW.js → chunk-TMA4RCEN.js} +3 -3
  88. package/dist/{chunk-UKPGSAZW.js.map → chunk-TMA4RCEN.js.map} +1 -1
  89. package/dist/{chunk-V6HA6JJZ.cjs → chunk-U5VAY32K.cjs} +215 -138
  90. package/dist/chunk-U5VAY32K.cjs.map +1 -0
  91. package/dist/{chunk-DUEZFTK2.js → chunk-UKQHXPGW.js} +4 -12
  92. package/dist/chunk-UKQHXPGW.js.map +1 -0
  93. package/dist/{chunk-4CVLFLOB.cjs → chunk-UM6BVY2S.cjs} +201 -69
  94. package/dist/chunk-UM6BVY2S.cjs.map +1 -0
  95. package/dist/{chunk-75JP4BZX.js → chunk-UQ3UIZJC.js} +3 -2
  96. package/dist/chunk-UQ3UIZJC.js.map +1 -0
  97. package/dist/{chunk-PCPX42KL.js → chunk-V4XG5QF6.js} +16 -10
  98. package/dist/chunk-V4XG5QF6.js.map +1 -0
  99. package/dist/{chunk-GPPLSM2Z.js → chunk-VB37OC6I.js} +6 -5
  100. package/dist/chunk-VB37OC6I.js.map +1 -0
  101. package/dist/{chunk-MVBEL57Q.js → chunk-Y7FT4IQT.js} +500 -170
  102. package/dist/chunk-Y7FT4IQT.js.map +1 -0
  103. package/dist/{chunk-OFXOTECC.js → chunk-YPPPYLSR.js} +7 -7
  104. package/dist/chunk-YPPPYLSR.js.map +1 -0
  105. package/dist/{chunk-M4F66R4O.cjs → chunk-YWQ6V4ZN.cjs} +121 -82
  106. package/dist/chunk-YWQ6V4ZN.cjs.map +1 -0
  107. package/dist/constants.cjs +5 -5
  108. package/dist/constants.d.cts +1 -1
  109. package/dist/constants.d.ts +1 -1
  110. package/dist/constants.js +2 -2
  111. package/dist/engine.cjs +31 -31
  112. package/dist/engine.d.cts +11 -11
  113. package/dist/engine.d.ts +11 -11
  114. package/dist/engine.js +21 -21
  115. package/dist/errors.cjs +19 -19
  116. package/dist/errors.d.cts +2 -2
  117. package/dist/errors.d.ts +2 -2
  118. package/dist/errors.js +1 -1
  119. package/dist/format.cjs +29 -22
  120. package/dist/format.cjs.map +1 -1
  121. package/dist/format.d.cts +1 -2
  122. package/dist/format.d.ts +1 -2
  123. package/dist/format.js +23 -16
  124. package/dist/format.js.map +1 -1
  125. package/dist/index.cjs +31 -31
  126. package/dist/index.d.cts +10 -10
  127. package/dist/index.d.ts +10 -10
  128. package/dist/index.js +22 -22
  129. package/dist/language.cjs +8 -8
  130. package/dist/language.cjs.map +1 -1
  131. package/dist/language.d.cts +10 -10
  132. package/dist/language.d.ts +10 -10
  133. package/dist/language.js +6 -6
  134. package/dist/language.js.map +1 -1
  135. package/dist/lexer.cjs +18 -18
  136. package/dist/lexer.d.cts +2 -2
  137. package/dist/lexer.d.ts +2 -2
  138. package/dist/lexer.js +6 -6
  139. package/dist/normalizer.cjs +12 -12
  140. package/dist/normalizer.js +6 -6
  141. package/dist/packages.cjs +39 -39
  142. package/dist/packages.d.cts +9 -9
  143. package/dist/packages.d.ts +9 -9
  144. package/dist/packages.js +16 -16
  145. package/dist/parser.cjs +15 -15
  146. package/dist/parser.d.cts +23 -9
  147. package/dist/parser.d.ts +23 -9
  148. package/dist/parser.js +5 -5
  149. package/dist/{pipeline-CrsRPmsv.d.ts → pipeline-B6k5lCB7.d.ts} +1 -1
  150. package/dist/{pipeline-DPUQt7yL.d.cts → pipeline-BEb3hujr.d.cts} +1 -1
  151. package/dist/resolvers.d.cts +2 -2
  152. package/dist/resolvers.d.ts +2 -2
  153. package/dist/uom.cjs +17 -17
  154. package/dist/uom.d.cts +2 -2
  155. package/dist/uom.d.ts +2 -2
  156. package/dist/uom.js +7 -7
  157. package/dist/utilities.cjs +8 -4
  158. package/dist/utilities.d.cts +19 -3
  159. package/dist/utilities.d.ts +19 -3
  160. package/dist/utilities.js +2 -2
  161. package/dist/vm.cjs +43 -31
  162. package/dist/vm.d.cts +105 -9
  163. package/dist/vm.d.ts +105 -9
  164. package/dist/vm.js +9 -9
  165. package/package.json +1 -1
  166. package/dist/chunk-2CS6OMZK.js.map +0 -1
  167. package/dist/chunk-35IWNZPJ.cjs.map +0 -1
  168. package/dist/chunk-4AVD7NZW.cjs.map +0 -1
  169. package/dist/chunk-4CVLFLOB.cjs.map +0 -1
  170. package/dist/chunk-6GCKCWLB.js.map +0 -1
  171. package/dist/chunk-6RDSQURE.js.map +0 -1
  172. package/dist/chunk-75JP4BZX.js.map +0 -1
  173. package/dist/chunk-AHCLWAM5.cjs.map +0 -1
  174. package/dist/chunk-BAMZT3GL.js.map +0 -1
  175. package/dist/chunk-BVAR5J2F.js.map +0 -1
  176. package/dist/chunk-BXKJLM5R.cjs.map +0 -1
  177. package/dist/chunk-CCJPSHGI.cjs.map +0 -1
  178. package/dist/chunk-CSG5XVNJ.cjs.map +0 -1
  179. package/dist/chunk-CYFK5SY2.js.map +0 -1
  180. package/dist/chunk-DUEZFTK2.js.map +0 -1
  181. package/dist/chunk-GPPLSM2Z.js.map +0 -1
  182. package/dist/chunk-GVWHCL2N.js.map +0 -1
  183. package/dist/chunk-M4F66R4O.cjs.map +0 -1
  184. package/dist/chunk-M4N5GA5Z.cjs.map +0 -1
  185. package/dist/chunk-MBNQVDVC.cjs.map +0 -1
  186. package/dist/chunk-MEOHSQEH.js.map +0 -1
  187. package/dist/chunk-MTX2KVU7.cjs.map +0 -1
  188. package/dist/chunk-MVBEL57Q.js.map +0 -1
  189. package/dist/chunk-MZOVP3WI.cjs.map +0 -1
  190. package/dist/chunk-N5YFR4UP.cjs +0 -10
  191. package/dist/chunk-N5YFR4UP.cjs.map +0 -1
  192. package/dist/chunk-NZFKROS7.cjs.map +0 -1
  193. package/dist/chunk-OC6LTXYS.cjs.map +0 -1
  194. package/dist/chunk-OFXOTECC.js.map +0 -1
  195. package/dist/chunk-P33SBYGG.js.map +0 -1
  196. package/dist/chunk-P75775WC.js.map +0 -1
  197. package/dist/chunk-PCPX42KL.js.map +0 -1
  198. package/dist/chunk-PFSDGGYL.js +0 -8
  199. package/dist/chunk-PFSDGGYL.js.map +0 -1
  200. package/dist/chunk-QDMPOAI7.cjs.map +0 -1
  201. package/dist/chunk-R24DI24X.cjs.map +0 -1
  202. package/dist/chunk-SFQWJMKT.js.map +0 -1
  203. package/dist/chunk-V6HA6JJZ.cjs.map +0 -1
  204. package/dist/chunk-WWOLFXTX.cjs.map +0 -1
  205. package/dist/chunk-YU2CUNFO.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { VariableResolver } from './chunk-GCM5JHMX.js';
2
- import { sharedCurrencyExchange } from './chunk-UKPGSAZW.js';
3
- import { powNode, callNode, simplifySymbolic, sameShape, cancelSymbolic, complexNode, complex, RATIONAL_ZERO, rationalFromNumber, freeVariables, symbolicToEntry, rowMajorToColumnMajor, expandSymbolic, matrixMultiply, inverse, determinant, transpose, constNode, rationalToNumber, toPolynomial, polyDegree, polyCoefficients, rationalNeg, substitute, rationalDiv, isRationalZero, varNode, isRationalOne, RATIONAL_ONE, polyUnivariateVar, rationalMul, fromPolynomial, rationalCompare, coefficientGcd, polynomialDivide, isZeroPolynomial, rationalAdd, rationalSub, exactIntegerSqrt, surdNode, compareMonomials, COMPLEX_I, exactRationalSqrt, symbolicKey, monomialFromKey, monomialToKey, signedSurdNode, exactIntegerCbrt } from './chunk-CYFK5SY2.js';
4
- import { errorValue, symbolicValue, numberValue, bigIntValue, uomValue, matrixValue, percentageValue, stringValue, hexValue } from './chunk-BVAR5J2F.js';
5
- import { getMeasure, convertUnit } from './chunk-WC5FFSHB.js';
6
- import { UNIT_TABLE } from './chunk-PCPX42KL.js';
7
- import { ErrorFactory } from './chunk-6RDSQURE.js';
2
+ import { sharedCurrencyExchange } from './chunk-TMA4RCEN.js';
3
+ import { powNode, callNode, simplifySymbolic, sameShape, cancelSymbolic, complexNode, complex, RATIONAL_ZERO, rationalFromNumber, freeVariables, symbolicToEntry, rowMajorToColumnMajor, expandSymbolic, matrixMultiply, inverse, determinant, transpose, matrixPower, constNode, rationalToNumber, toPolynomial, polyDegree, polyCoefficients, rationalNeg, substitute, rationalDiv, isRationalZero, varNode, isRationalOne, RATIONAL_ONE, polyUnivariateVar, rationalMul, fromPolynomial, coefficientGcd, polynomialDivide, isZeroPolynomial, rationalCompare, rationalSub, exactIntegerSqrt, rationalAdd, surdNode, compareMonomials, COMPLEX_I, squareFreePart, exactRationalSqrt, symbolicKey, monomialFromKey, monomialToKey, signedSurdNode, exactIntegerCbrt } from './chunk-O3ANBHSA.js';
4
+ import { faultedOperand, errorValue, symbolicValue, numberValue, bigIntValue, uomValue, matrixValue, percentageValue, stringValue, hexValue } from './chunk-GQCOSXMG.js';
5
+ import { getMeasure, convertUnit } from './chunk-R3PY4G7J.js';
6
+ import { UNIT_TABLE } from './chunk-V4XG5QF6.js';
7
+ import { ErrorFactory } from './chunk-3D7V24DG.js';
8
8
 
9
9
  // src/symbolic/MultivariateFactor.ts
10
10
  function exactRoot(value, degree) {
@@ -92,9 +92,9 @@ function factorBinomial(first, second) {
92
92
  return product([linear, termsToNode([squareOf(cubes.a), middle, squareOf(cubes.b)])]);
93
93
  }
94
94
  function asPowerPair(first, second, degree) {
95
- const magnitude = second.coefficient.n < 0n ? rationalNeg(second.coefficient) : second.coefficient;
95
+ const magnitude2 = second.coefficient.n < 0n ? rationalNeg(second.coefficient) : second.coefficient;
96
96
  const firstRoot = exactRoot(first.coefficient, degree);
97
- const secondRoot = exactRoot(magnitude, degree);
97
+ const secondRoot = exactRoot(magnitude2, degree);
98
98
  if (firstRoot === null || secondRoot === null) return null;
99
99
  const firstKey = monomialRoot(first.key, degree);
100
100
  const secondKey = monomialRoot(second.key, degree);
@@ -119,8 +119,8 @@ function factorPerfectSquare(terms) {
119
119
  if (cross.key !== middle.key) return null;
120
120
  const twice = rationalMul(cross.coefficient, { n: 2n, d: 1n });
121
121
  const negative = middle.coefficient.n < 0n;
122
- const magnitude = negative ? rationalNeg(middle.coefficient) : middle.coefficient;
123
- if (!isRationalOne(rationalDiv(magnitude, twice))) return null;
122
+ const magnitude2 = negative ? rationalNeg(middle.coefficient) : middle.coefficient;
123
+ if (!isRationalOne(rationalDiv(magnitude2, twice))) return null;
124
124
  const base = termsToNode([roots.a, negative ? negated(roots.b) : roots.b]);
125
125
  return { kind: "pow", base, exponent: constNode(2) };
126
126
  }
@@ -174,13 +174,13 @@ function lcm(a, b) {
174
174
  return a / gcd(a, b) * b;
175
175
  }
176
176
  function divisors(value) {
177
- const magnitude = value < 0n ? -value : value;
178
- if (magnitude === 0n) return null;
177
+ const magnitude2 = value < 0n ? -value : value;
178
+ if (magnitude2 === 0n) return null;
179
179
  const found = [];
180
- for (let candidate = 1n; candidate * candidate <= magnitude; candidate++) {
181
- if (magnitude % candidate !== 0n) continue;
180
+ for (let candidate = 1n; candidate * candidate <= magnitude2; candidate++) {
181
+ if (magnitude2 % candidate !== 0n) continue;
182
182
  found.push(candidate);
183
- const paired = magnitude / candidate;
183
+ const paired = magnitude2 / candidate;
184
184
  if (paired !== candidate) found.push(paired);
185
185
  if (found.length > FACTOR_MAX_ROOT_CANDIDATES) return null;
186
186
  }
@@ -246,8 +246,8 @@ function coefficientsToNode(descending, variable) {
246
246
  const terms = /* @__PURE__ */ new Map();
247
247
  descending.forEach((coeff, index) => {
248
248
  if (isRationalZero(coeff)) return;
249
- const power2 = degree - index;
250
- terms.set(power2 === 0 ? "" : power2 === 1 ? variable : `${variable}^${power2}`, coeff);
249
+ const power3 = degree - index;
250
+ terms.set(power3 === 0 ? "" : power3 === 1 ? variable : `${variable}^${power3}`, coeff);
251
251
  });
252
252
  return fromPolynomial({ terms, vars: terms.size > 0 ? [variable] : [] });
253
253
  }
@@ -463,10 +463,10 @@ function trigonometricRoots(p, q, shift) {
463
463
  const pNumber = rationalToNumber(p);
464
464
  const qNumber = rationalToNumber(q);
465
465
  const shiftNumber = rationalToNumber(shift);
466
- const magnitude = 2 * Math.sqrt(-pNumber / 3);
466
+ const magnitude2 = 2 * Math.sqrt(-pNumber / 3);
467
467
  const ratio = 3 * qNumber / (2 * pNumber) * Math.sqrt(-3 / pNumber);
468
468
  const angle = Math.acos(Math.min(1, Math.max(-1, ratio))) / 3;
469
- return [0, 1, 2].map((k) => magnitude * Math.cos(angle - 2 * Math.PI * k / 3) + shiftNumber).sort((left, right) => left - right);
469
+ return [0, 1, 2].map((k) => magnitude2 * Math.cos(angle - 2 * Math.PI * k / 3) + shiftNumber).sort((left, right) => left - right);
470
470
  }
471
471
  function depressQuartic(b, c, d, e) {
472
472
  const shift = rationalNeg(rationalDiv(b, FOUR));
@@ -535,8 +535,8 @@ function nestedSquareRoots(p, r, discriminant) {
535
535
  const roots = [];
536
536
  for (const { node, positive } of solutions) {
537
537
  const radicand = positive ? node : { kind: "neg", operand: node };
538
- const magnitude = { kind: "call", name: "sqrt", args: [radicand] };
539
- const square = positive ? magnitude : { kind: "mul", left: magnitude, right: complexNode(COMPLEX_I) };
538
+ const magnitude2 = { kind: "call", name: "sqrt", args: [radicand] };
539
+ const square = positive ? magnitude2 : { kind: "mul", left: magnitude2, right: complexNode(COMPLEX_I) };
540
540
  roots.push({ kind: "neg", operand: square }, square);
541
541
  }
542
542
  return roots;
@@ -545,13 +545,11 @@ function halved(node) {
545
545
  return { kind: "div", left: node, right: constNode(TWO) };
546
546
  }
547
547
  function complexBiquadraticRoots(p, r) {
548
+ const centre = rationalDiv(rationalNeg(p), TWO);
548
549
  const modulus = exactRationalSqrt(r);
549
- if (modulus === null) return null;
550
- const realPart = rationalDiv(rationalAdd(modulus, rationalDiv(rationalNeg(p), TWO)), TWO);
551
- const imaginaryPart = rationalDiv(rationalSub(modulus, rationalDiv(rationalNeg(p), TWO)), TWO);
552
- if (rationalCompare(realPart, RATIONAL_ZERO) < 0 || rationalCompare(imaginaryPart, RATIONAL_ZERO) < 0) return null;
553
- const re = surdNode(realPart);
554
- const im = { kind: "mul", left: surdNode(imaginaryPart), right: complexNode(COMPLEX_I) };
550
+ const re = modulus === null ? nestedSurd(surdNode(r), centre) : surdNode(rationalDiv(rationalAdd(modulus, centre), TWO));
551
+ const imaginaryMagnitude = modulus === null ? nestedSurd(surdNode(r), rationalNeg(centre)) : surdNode(rationalDiv(rationalSub(modulus, centre), TWO));
552
+ const im = { kind: "mul", left: imaginaryMagnitude, right: complexNode(COMPLEX_I) };
555
553
  const first = { kind: "add", left: re, right: im };
556
554
  const second = { kind: "sub", left: re, right: im };
557
555
  return [
@@ -561,6 +559,10 @@ function complexBiquadraticRoots(p, r) {
561
559
  first
562
560
  ];
563
561
  }
562
+ function nestedSurd(surd, offset) {
563
+ const shifted = offset.n >= 0n ? { kind: "add", left: surd, right: constNode(offset) } : { kind: "sub", left: surd, right: constNode(rationalNeg(offset)) };
564
+ return { kind: "call", name: "sqrt", args: [halved(shifted)] };
565
+ }
564
566
  function splitQuarticRoots(p, q, r, solveQuadratic2) {
565
567
  const resolvent = [
566
568
  { n: 1n, d: 1n },
@@ -585,21 +587,185 @@ function splitQuarticRoots(p, q, r, solveQuadratic2) {
585
587
  return null;
586
588
  }
587
589
 
590
+ // src/symbolic/NumericRoots.ts
591
+ var DURAND_KERNER_MAX_ITERATIONS = 200;
592
+ var DURAND_KERNER_TOLERANCE = 1e-14;
593
+ var ROOT_RESIDUAL_TOLERANCE = 1e-10;
594
+ var ROOT_SEPARATION_TOLERANCE = 1e-10;
595
+ var AXIS_SNAP_TOLERANCE = 1e-9;
596
+ var CONJUGATE_PAIR_TOLERANCE = 1e-8;
597
+ var NEWTON_POLISH_STEPS = 4;
598
+ function subtract(a, b) {
599
+ return { re: a.re - b.re, im: a.im - b.im };
600
+ }
601
+ function multiply(a, b) {
602
+ return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re };
603
+ }
604
+ function divide(a, b) {
605
+ if (Math.abs(b.re) >= Math.abs(b.im)) {
606
+ const ratio2 = b.im / b.re;
607
+ const denominator2 = b.re + b.im * ratio2;
608
+ return { re: (a.re + a.im * ratio2) / denominator2, im: (a.im - a.re * ratio2) / denominator2 };
609
+ }
610
+ const ratio = b.re / b.im;
611
+ const denominator = b.re * ratio + b.im;
612
+ return { re: (a.re * ratio + a.im) / denominator, im: (a.im * ratio - a.re) / denominator };
613
+ }
614
+ function magnitude(z) {
615
+ return Math.hypot(z.re, z.im);
616
+ }
617
+ function evaluateAt2(descending, z) {
618
+ let re = 0;
619
+ let im = 0;
620
+ for (const coefficient of descending) {
621
+ const nextRe = re * z.re - im * z.im + coefficient;
622
+ im = re * z.im + im * z.re;
623
+ re = nextRe;
624
+ }
625
+ return { re, im };
626
+ }
627
+ function evaluationScale(descending, z) {
628
+ const radius = magnitude(z);
629
+ let total = 0;
630
+ for (const coefficient of descending) total = total * radius + Math.abs(coefficient);
631
+ return total;
632
+ }
633
+ function differentiate(descending) {
634
+ const degree = descending.length - 1;
635
+ const derivative = [];
636
+ for (let i = 0; i < degree; i++) derivative.push(descending[i] * (degree - i));
637
+ return derivative;
638
+ }
639
+ function squareFreeDoubles(descending) {
640
+ const ascending = [...descending].reverse();
641
+ const squareFree = [...squareFreePart(ascending)].reverse();
642
+ const doubles = squareFree.map(rationalToNumber);
643
+ if (doubles.some((value) => !Number.isFinite(value))) return null;
644
+ return doubles;
645
+ }
646
+ function initialEstimates(monic) {
647
+ const degree = monic.length - 1;
648
+ let largest = 0;
649
+ for (let i = 1; i < monic.length; i++) largest = Math.max(largest, Math.abs(monic[i]));
650
+ const radius = 1 + largest;
651
+ const centre = -monic[1] / degree;
652
+ const estimates = [];
653
+ for (let k = 0; k < degree; k++) {
654
+ const angle = 2 * Math.PI * k / degree + Math.PI / (2 * degree);
655
+ estimates.push({ re: centre + radius * Math.cos(angle), im: radius * Math.sin(angle) });
656
+ }
657
+ return estimates;
658
+ }
659
+ function durandKerner(monic) {
660
+ const degree = monic.length - 1;
661
+ const estimates = initialEstimates(monic);
662
+ for (let iteration = 0; iteration < DURAND_KERNER_MAX_ITERATIONS; iteration++) {
663
+ let largestStep = 0;
664
+ let largestRoot = 1;
665
+ for (let i = 0; i < degree; i++) {
666
+ let denominator = { re: 1, im: 0 };
667
+ for (let j = 0; j < degree; j++) {
668
+ if (i !== j) denominator = multiply(denominator, subtract(estimates[i], estimates[j]));
669
+ }
670
+ if (denominator.re === 0 && denominator.im === 0) {
671
+ estimates[i] = { re: estimates[i].re + 1e-6 * (i + 1), im: estimates[i].im + 1e-6 };
672
+ continue;
673
+ }
674
+ const step = divide(evaluateAt2(monic, estimates[i]), denominator);
675
+ if (!Number.isFinite(step.re) || !Number.isFinite(step.im)) return null;
676
+ estimates[i] = subtract(estimates[i], step);
677
+ largestStep = Math.max(largestStep, magnitude(step));
678
+ largestRoot = Math.max(largestRoot, magnitude(estimates[i]));
679
+ }
680
+ if (largestStep <= DURAND_KERNER_TOLERANCE * largestRoot) return estimates;
681
+ }
682
+ return null;
683
+ }
684
+ function polish(monic, derivative, start) {
685
+ let candidate = start;
686
+ for (let step = 0; step < NEWTON_POLISH_STEPS; step++) {
687
+ const slope = evaluateAt2(derivative, candidate);
688
+ if (slope.re === 0 && slope.im === 0) break;
689
+ const next = subtract(candidate, divide(evaluateAt2(monic, candidate), slope));
690
+ if (!Number.isFinite(next.re) || !Number.isFinite(next.im)) break;
691
+ candidate = next;
692
+ }
693
+ return candidate;
694
+ }
695
+ function snapToAxes(monic, derivative, z) {
696
+ const scale2 = Math.max(1, magnitude(z));
697
+ if (z.im !== 0 && Math.abs(z.im) <= AXIS_SNAP_TOLERANCE * scale2) {
698
+ const candidate = polish(monic, derivative, { re: z.re, im: 0 });
699
+ if (isRootWithinTolerance(monic, candidate)) return { re: candidate.re, im: 0 };
700
+ }
701
+ if (z.re !== 0 && Math.abs(z.re) <= AXIS_SNAP_TOLERANCE * scale2) {
702
+ const candidate = polish(monic, derivative, { re: 0, im: z.im });
703
+ if (isRootWithinTolerance(monic, candidate)) return { re: 0, im: candidate.im };
704
+ }
705
+ return z;
706
+ }
707
+ function isRootWithinTolerance(monic, z) {
708
+ if (!Number.isFinite(z.re) || !Number.isFinite(z.im)) return false;
709
+ return magnitude(evaluateAt2(monic, z)) <= ROOT_RESIDUAL_TOLERANCE * evaluationScale(monic, z);
710
+ }
711
+ function pairConjugates(roots) {
712
+ const result = [...roots];
713
+ const paired = result.map((root) => root.im === 0);
714
+ for (let i = 0; i < result.length; i++) {
715
+ if (paired[i]) continue;
716
+ const wanted = { re: result[i].re, im: -result[i].im };
717
+ let partner = -1;
718
+ let closest = Number.POSITIVE_INFINITY;
719
+ for (let j = i + 1; j < result.length; j++) {
720
+ if (paired[j]) continue;
721
+ const distance = magnitude(subtract(result[j], wanted));
722
+ if (distance < closest) {
723
+ closest = distance;
724
+ partner = j;
725
+ }
726
+ }
727
+ if (partner === -1 || closest > CONJUGATE_PAIR_TOLERANCE * Math.max(1, magnitude(result[i]))) continue;
728
+ const re = (result[i].re + result[partner].re) / 2;
729
+ const im = (Math.abs(result[i].im) + Math.abs(result[partner].im)) / 2;
730
+ const sign = result[i].im < 0 ? -1 : 1;
731
+ result[i] = { re, im: sign * im };
732
+ result[partner] = { re, im: -sign * im };
733
+ paired[i] = true;
734
+ paired[partner] = true;
735
+ }
736
+ return result;
737
+ }
738
+ function allSeparated(roots) {
739
+ for (let i = 0; i < roots.length; i++) {
740
+ for (let j = i + 1; j < roots.length; j++) {
741
+ const scale2 = Math.max(1, magnitude(roots[i]), magnitude(roots[j]));
742
+ if (magnitude(subtract(roots[i], roots[j])) <= ROOT_SEPARATION_TOLERANCE * scale2) return false;
743
+ }
744
+ }
745
+ return true;
746
+ }
747
+ function approximateRoots(descending) {
748
+ const monic = squareFreeDoubles(descending);
749
+ if (monic === null) return null;
750
+ if (monic.length < 2) return [];
751
+ const converged = durandKerner(monic);
752
+ if (converged === null) return null;
753
+ const derivative = differentiate(monic);
754
+ const roots = pairConjugates(converged).map((root) => snapToAxes(monic, derivative, root));
755
+ for (const root of roots) {
756
+ if (!isRootWithinTolerance(monic, root)) return null;
757
+ }
758
+ if (!allSeparated(roots)) return null;
759
+ return [...roots].sort((a, b) => a.re - b.re || a.im - b.im);
760
+ }
761
+
588
762
  // src/symbolic/Solve.ts
589
763
  var SOLVE_MAX_DEGREE = 8;
590
- var NEWTON_MAX_ITERATIONS = 100;
591
- var NEWTON_TOLERANCE = 1e-12;
592
- var NUMERIC_SCAN_STEPS = 2e3;
593
764
  function evaluateRational(descending, x) {
594
765
  let total = RATIONAL_ZERO;
595
766
  for (const coeff of descending) total = rationalAdd(rationalMul(total, x), coeff);
596
767
  return total;
597
768
  }
598
- function evaluateNumeric(descending, x) {
599
- let total = 0;
600
- for (const coeff of descending) total = total * x + rationalToNumber(coeff);
601
- return total;
602
- }
603
769
  function divideByRoot2(descending, root) {
604
770
  const quotient = [descending[0]];
605
771
  for (let i = 1; i < descending.length - 1; i++) {
@@ -629,9 +795,9 @@ function solveQuadratic(a, b, c) {
629
795
  return ascending ? [lower, upper] : [upper, lower];
630
796
  }
631
797
  function complexQuadraticRoots(negatedB, discriminant, twoA) {
632
- const magnitude = rationalNeg(discriminant);
633
- const rootN = exactIntegerSqrt(magnitude.n);
634
- const rootD = exactIntegerSqrt(magnitude.d);
798
+ const magnitude2 = rationalNeg(discriminant);
799
+ const rootN = exactIntegerSqrt(magnitude2.n);
800
+ const rootD = exactIntegerSqrt(magnitude2.d);
635
801
  if (rootN !== null && rootD !== null) {
636
802
  const imaginary2 = rationalDiv({ n: rootN, d: rootD }, twoA);
637
803
  const real2 = rationalDiv(negatedB, twoA);
@@ -641,7 +807,7 @@ function complexQuadraticRoots(negatedB, discriminant, twoA) {
641
807
  ];
642
808
  }
643
809
  const real = rationalDiv(negatedB, twoA);
644
- const scaledRadicand = rationalDiv(magnitude, rationalMul(twoA, twoA));
810
+ const scaledRadicand = rationalDiv(magnitude2, rationalMul(twoA, twoA));
645
811
  const imaginary = { kind: "mul", left: surdNode(scaledRadicand), right: complexNode(COMPLEX_I) };
646
812
  if (isRationalZero(real)) {
647
813
  return [{ kind: "neg", operand: imaginary }, imaginary];
@@ -657,44 +823,8 @@ function rational2() {
657
823
  function rational4() {
658
824
  return { n: 4n, d: 1n };
659
825
  }
660
- function numericRoots(descending) {
661
- const leading = rationalToNumber(descending[0]);
662
- if (leading === 0) return [];
663
- let bound = 0;
664
- for (let i = 1; i < descending.length; i++) {
665
- const ratio = Math.abs(rationalToNumber(descending[i]) / leading);
666
- if (ratio > bound) bound = ratio;
667
- }
668
- bound += 1;
669
- const roots = [];
670
- const step = 2 * bound / NUMERIC_SCAN_STEPS;
671
- let previousX = -bound;
672
- let previousY = evaluateNumeric(descending, previousX);
673
- for (let i = 1; i <= NUMERIC_SCAN_STEPS; i++) {
674
- const x = -bound + i * step;
675
- const y = evaluateNumeric(descending, x);
676
- if (previousY === 0) roots.push(previousX);
677
- else if (previousY < 0 !== y < 0) roots.push(bisect(descending, previousX, x));
678
- previousX = x;
679
- previousY = y;
680
- }
681
- return roots;
682
- }
683
- function bisect(descending, low, high) {
684
- let left = low;
685
- let right = high;
686
- let leftValue = evaluateNumeric(descending, left);
687
- for (let i = 0; i < NEWTON_MAX_ITERATIONS && right - left > NEWTON_TOLERANCE; i++) {
688
- const middle = (left + right) / 2;
689
- const middleValue = evaluateNumeric(descending, middle);
690
- if (leftValue < 0 === middleValue < 0) {
691
- left = middle;
692
- leftValue = middleValue;
693
- } else {
694
- right = middle;
695
- }
696
- }
697
- return (left + right) / 2;
826
+ function realApproximation(value) {
827
+ return { re: value, im: 0 };
698
828
  }
699
829
  function solveForVariable(lhs, rhs, variable) {
700
830
  const polynomial = toPolynomial({ kind: "sub", left: lhs, right: rhs });
@@ -763,38 +893,76 @@ function closedFormRoots(descending) {
763
893
  }
764
894
  return null;
765
895
  }
766
- function solveUnivariate(descending) {
767
- const exact = [];
896
+ function extractRationalRoots(descending) {
897
+ const roots = [];
768
898
  let remaining = [...descending];
769
- let roots = [...rationalRoots(remaining)].sort(rationalCompare);
770
- while (roots.length > 0 && remaining.length > 1) {
771
- for (const root of roots) {
899
+ let powersOfX = 0;
900
+ while (remaining.length > 1 && isRationalZero(remaining[remaining.length - 1])) {
901
+ remaining = remaining.slice(0, -1);
902
+ powersOfX++;
903
+ }
904
+ if (powersOfX > 0) roots.push(RATIONAL_ZERO);
905
+ while (remaining.length > 1) {
906
+ const found = rationalRoots(remaining);
907
+ if (found.length === 0) break;
908
+ for (const root of found) {
909
+ let multiplicity = 0;
772
910
  while (remaining.length > 1 && isRationalZero(evaluateRational(remaining, root))) {
773
911
  remaining = divideByRoot2(remaining, root);
912
+ multiplicity++;
774
913
  }
775
- exact.push(constNode(root));
914
+ if (multiplicity > 0) roots.push(root);
776
915
  }
777
- roots = remaining.length > 1 ? [...rationalRoots(remaining)].sort(rationalCompare) : [];
778
916
  }
917
+ return { roots: roots.sort(rationalCompare), remaining };
918
+ }
919
+ function solveUnivariate(descending) {
920
+ const { roots, remaining } = extractRationalRoots(descending);
921
+ const exact = roots.map(constNode);
922
+ const approximate = [];
779
923
  const leftoverDegree = remaining.length - 1;
924
+ const solvedDegree = descending.length - remaining.length;
925
+ if (leftoverDegree <= 0) return finish(exact, approximate, solvedDegree, 0, "");
780
926
  if (leftoverDegree === 1) {
781
927
  exact.push(constNode(rationalDiv(rationalNeg(remaining[1]), remaining[0])));
782
- return { kind: "roots", exact, approximate: [] };
928
+ return finish(exact, approximate, solvedDegree + 1, 0, "");
783
929
  }
784
930
  if (leftoverDegree === 2) {
785
- const quadratic = solveQuadratic(remaining[0], remaining[1], remaining[2]);
786
- return { kind: "roots", exact: [...exact, ...quadratic], approximate: [] };
787
- }
788
- if (leftoverDegree <= 0) {
789
- return exact.length > 0 ? { kind: "roots", exact, approximate: [] } : { kind: "no-real-solutions", reason: "there is no value that satisfies this equation" };
931
+ exact.push(...solveQuadratic(remaining[0], remaining[1], remaining[2]));
932
+ return finish(exact, approximate, solvedDegree + 2, 0, "");
790
933
  }
791
934
  const closedForm = closedFormRoots(remaining);
792
935
  if (closedForm !== null) {
793
- return { kind: "roots", exact: [...exact, ...closedForm.exact], approximate: closedForm.approximate };
794
- }
795
- const approximate = numericRoots(remaining);
796
- if (exact.length === 0 && approximate.length === 0) {
797
- return { kind: "no-real-solutions", reason: "no real root was found within the polynomial's root bound" };
936
+ exact.push(...closedForm.exact);
937
+ approximate.push(...closedForm.approximate.map(realApproximation));
938
+ return finish(exact, approximate, solvedDegree + leftoverDegree, 0, "");
939
+ }
940
+ const numeric = approximateRoots(remaining);
941
+ if (numeric !== null) {
942
+ approximate.push(...numeric);
943
+ return finish(exact, approximate, solvedDegree + leftoverDegree, 0, "");
944
+ }
945
+ return finish(
946
+ exact,
947
+ approximate,
948
+ solvedDegree,
949
+ leftoverDegree,
950
+ `a degree-${leftoverDegree} factor of it has no exact solution here and the numerical method did not converge on that factor`
951
+ );
952
+ }
953
+ function finish(exact, approximate, solvedDegree, unsolvedDegree, reason) {
954
+ const found = exact.length + approximate.length;
955
+ const incoherent = solvedDegree >= 1 && (found < 1 || found > solvedDegree);
956
+ if (unsolvedDegree > 0 || incoherent) {
957
+ return {
958
+ kind: "incomplete",
959
+ exact,
960
+ approximate,
961
+ // A distinct root can stand for several equal ones, so the count found
962
+ // is a lower bound on what it covers. The degree left unsolved is not.
963
+ missing: incoherent ? Math.max(1, solvedDegree + unsolvedDegree - found) : unsolvedDegree,
964
+ reason: incoherent ? `${found} root${found === 1 ? "" : "s"} were produced for a degree-${solvedDegree + unsolvedDegree} equation, which does not add up` : reason
965
+ };
798
966
  }
799
967
  return { kind: "roots", exact, approximate };
800
968
  }
@@ -871,7 +1039,8 @@ function symbolicNeg(v) {
871
1039
  function symbolicBuiltin(index, args) {
872
1040
  const nodes = [];
873
1041
  for (const arg of args) {
874
- if (arg.type === 13 /* Error */) return arg;
1042
+ const faulted = faultedOperand(arg);
1043
+ if (faulted) return faulted;
875
1044
  const node = valueToSymbolic(arg);
876
1045
  if (node === null) return unsupported(SYMBOLIC_BUILTIN_NAMES[index] ?? `builtin ${index}`);
877
1046
  nodes.push(node);
@@ -888,19 +1057,28 @@ function rootToValue(root) {
888
1057
  if (value.type === 0 /* Number */ && !Number.isInteger(value.toNumber())) return symbolicValue(root);
889
1058
  return value;
890
1059
  }
1060
+ function approximateRootToValue(root) {
1061
+ if (root.im === 0) return numberValue(root.re);
1062
+ return symbolicValue(complexNode(complex(rationalFromNumber(root.re), rationalFromNumber(root.im))));
1063
+ }
891
1064
  function solveOutcomeToValue(outcome) {
892
1065
  switch (outcome.kind) {
893
1066
  case "identity":
894
1067
  return stringValue("true for every value");
895
1068
  case "contradiction":
896
1069
  return stringValue("no solution");
897
- case "no-real-solutions":
898
- return stringValue(`no real solutions (${outcome.reason})`);
899
1070
  case "unsupported":
900
1071
  return errorValue("SYMBOLIC_SOLVE_UNSUPPORTED", `Cannot solve this equation: ${outcome.reason}.`);
1072
+ case "incomplete": {
1073
+ const found = outcome.exact.length + outcome.approximate.length;
1074
+ return errorValue(
1075
+ "SYMBOLIC_SOLVE_INCOMPLETE",
1076
+ `Only ${found} of this equation's ${found + outcome.missing} roots could be found: ${outcome.reason}.`
1077
+ );
1078
+ }
901
1079
  case "roots": {
902
1080
  const values = outcome.exact.map(rootToValue);
903
- for (const approximate of outcome.approximate) values.push(numberValue(approximate));
1081
+ for (const approximate of outcome.approximate) values.push(approximateRootToValue(approximate));
904
1082
  if (values.length === 0) return stringValue("no solution");
905
1083
  if (values.length === 1) return values[0];
906
1084
  return matrixValue(1, values.length, values.map((v) => v.type === 9 /* Symbolic */ ? v.value : v.toNumber()));
@@ -945,6 +1123,65 @@ function unifyUom(l, r) {
945
1123
  }
946
1124
  return { lv: l.toNumber(), rv: r.toNumber(), unit: void 0, sameMeasure: true };
947
1125
  }
1126
+ var UNIFIED_COMPARISON_TOLERANCE = 1e-12;
1127
+ function largestFiniteMagnitude(a, b, c) {
1128
+ let scale2 = 0;
1129
+ if (Number.isFinite(a) && Math.abs(a) > scale2) scale2 = Math.abs(a);
1130
+ if (Number.isFinite(b) && Math.abs(b) > scale2) scale2 = Math.abs(b);
1131
+ if (Number.isFinite(c) && Math.abs(c) > scale2) scale2 = Math.abs(c);
1132
+ return scale2;
1133
+ }
1134
+ function compareUom(l, r) {
1135
+ const { lv, rv, sameMeasure } = unifyUom(l, r);
1136
+ if (!sameMeasure) return { lv, rv, equal: false, sameMeasure: false };
1137
+ if (lv === rv) return { lv, rv, equal: true, sameMeasure: true };
1138
+ if (!Number.isFinite(lv) || !Number.isFinite(rv)) return { lv, rv, equal: false, sameMeasure: true };
1139
+ const scale2 = largestFiniteMagnitude(lv, rv, r.toNumber());
1140
+ return { lv, rv, equal: Math.abs(lv - rv) <= scale2 * UNIFIED_COMPARISON_TOLERANCE, sameMeasure: true };
1141
+ }
1142
+ function incomparableUnitsError(l, r) {
1143
+ const lUnit = l.type === 6 /* Uom */ ? l.unit : void 0;
1144
+ const rUnit = r.type === 6 /* Uom */ ? r.unit : void 0;
1145
+ return errorValue("INCOMPATIBLE_UNITS", `Cannot compare incompatible units: ${lUnit ?? "?"} and ${rUnit ?? "?"}`);
1146
+ }
1147
+ function toBigIntOperand(v) {
1148
+ if (v.type === 2 /* BigInt */) return v.value;
1149
+ const n = v.toNumber();
1150
+ if (!Number.isInteger(n)) {
1151
+ const shown = Number.isNaN(n) ? "NaN" : String(n);
1152
+ throw ErrorFactory.execution(
1153
+ "BIGINT_INEXACT_OPERAND",
1154
+ `A whole-number (n) value can only be combined with another whole number, and ${shown} is not one`,
1155
+ { operand: shown }
1156
+ );
1157
+ }
1158
+ return BigInt(n);
1159
+ }
1160
+ function bigIntDivisionByZero() {
1161
+ return ErrorFactory.execution(
1162
+ "BIGINT_DIVISION_BY_ZERO",
1163
+ "Division by zero: a whole-number (n) division is exact integer division, which has no answer at zero, unlike 1 / 0 which is Infinity"
1164
+ );
1165
+ }
1166
+ function compareBigIntOperands(l, r) {
1167
+ const lb = exactBigInt(l);
1168
+ const rb = exactBigInt(r);
1169
+ if (lb === null || rb === null) return null;
1170
+ if (lb < rb) return -1;
1171
+ if (lb > rb) return 1;
1172
+ return 0;
1173
+ }
1174
+ function exactBigInt(v) {
1175
+ if (v.type === 2 /* BigInt */) return v.value;
1176
+ const n = v.toNumber();
1177
+ return Number.isInteger(n) ? BigInt(n) : null;
1178
+ }
1179
+ function power(base, exponent) {
1180
+ if (Number.isFinite(exponent)) return Math.pow(base, exponent);
1181
+ if (base === 1) return 1;
1182
+ if (base === -1 && !Number.isNaN(exponent)) return 1;
1183
+ return Math.pow(base, exponent);
1184
+ }
948
1185
  function binaryOp(l, r, op, bigOp, symbolicOp) {
949
1186
  if (l.type === 13 /* Error */) return l;
950
1187
  if (r.type === 13 /* Error */) return r;
@@ -966,14 +1203,13 @@ function binaryOp(l, r, op, bigOp, symbolicOp) {
966
1203
  return numberValue(op(l.value, r.value));
967
1204
  }
968
1205
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
969
- const lb = l.type === 2 /* BigInt */ ? l.value : BigInt(l.toNumber());
970
- const rb = r.type === 2 /* BigInt */ ? r.value : BigInt(r.toNumber());
1206
+ const lb = toBigIntOperand(l);
1207
+ const rb = toBigIntOperand(r);
971
1208
  if (bigOp) return bigIntValue(bigOp(lb, rb));
972
1209
  return bigIntValue(lb + rb);
973
1210
  }
974
1211
  if (l.type === 6 /* Uom */ || r.type === 6 /* Uom */) {
975
1212
  const { lv, rv, unit, sameMeasure } = unifyUom(l, r);
976
- if (isNaN(lv) || isNaN(rv)) return numberValue(0);
977
1213
  if (!sameMeasure) {
978
1214
  const lUnit = l.type === 6 /* Uom */ ? l.unit : void 0;
979
1215
  const rUnit = r.type === 6 /* Uom */ ? r.unit : void 0;
@@ -1003,10 +1239,7 @@ function binaryOp(l, r, op, bigOp, symbolicOp) {
1003
1239
  const result = rm.data.map((v) => op(scalar, v));
1004
1240
  return matrixValue(rm.rows, rm.cols, result);
1005
1241
  }
1006
- const lNum = l.toNumber();
1007
- const rNum = r.toNumber();
1008
- if (isNaN(lNum) || isNaN(rNum)) return numberValue(0);
1009
- return numberValue(op(lNum, rNum));
1242
+ return numberValue(op(l.toNumber(), r.toNumber()));
1010
1243
  }
1011
1244
 
1012
1245
  // src/vm/OpRegistry.ts
@@ -1057,7 +1290,7 @@ var sharedOpRegistry = new OpRegistry();
1057
1290
 
1058
1291
  // src/symbolic/PartialFractions.ts
1059
1292
  var APART_MAX_DEGREE = 12;
1060
- function multiply(a, b) {
1293
+ function multiply2(a, b) {
1061
1294
  if (a.length === 0 || b.length === 0) return [];
1062
1295
  const product2 = new Array(a.length + b.length - 1).fill(RATIONAL_ZERO);
1063
1296
  a.forEach((left, i) => {
@@ -1067,9 +1300,9 @@ function multiply(a, b) {
1067
1300
  });
1068
1301
  return product2;
1069
1302
  }
1070
- function power(base, exponent) {
1303
+ function power2(base, exponent) {
1071
1304
  let result = [RATIONAL_ONE];
1072
- for (let i = 0; i < exponent; i++) result = multiply(result, base);
1305
+ for (let i = 0; i < exponent; i++) result = multiply2(result, base);
1073
1306
  return result;
1074
1307
  }
1075
1308
  function scale(coefficients, factor) {
@@ -1104,12 +1337,12 @@ function enumerateUnknowns(factors) {
1104
1337
  return unknowns;
1105
1338
  }
1106
1339
  function unknownColumn(unknown, factors, denominator) {
1107
- const divisor = power(factors[unknown.factor].base, unknown.power);
1340
+ const divisor = power2(factors[unknown.factor].base, unknown.power);
1108
1341
  const { quotient, remainder } = polynomialDivide(denominator, divisor);
1109
1342
  if (!isZeroPolynomial(remainder)) return null;
1110
1343
  const shift = new Array(unknown.degree).fill(RATIONAL_ZERO);
1111
1344
  shift.push(RATIONAL_ONE);
1112
- return multiply(quotient, shift);
1345
+ return multiply2(quotient, shift);
1113
1346
  }
1114
1347
  function solveLinearSystem(matrix, target) {
1115
1348
  const size = target.length;
@@ -1289,7 +1522,7 @@ function oneMinusSquare(argument) {
1289
1522
  function onePlusSquare(argument) {
1290
1523
  return { kind: "add", left: one(), right: powNode(argument, constNode(2)) };
1291
1524
  }
1292
- function differentiate(node, variable, order = 1) {
1525
+ function differentiate2(node, variable, order = 1) {
1293
1526
  if (order < 0 || !Number.isInteger(order)) {
1294
1527
  throw ErrorFactory.execution("SYMBOLIC_DERIVATIVE_ORDER_LIMIT", "A derivative order must be a whole number, at least zero.");
1295
1528
  }
@@ -1386,9 +1619,9 @@ function integratePolynomial(node, variable) {
1386
1619
  }
1387
1620
  const ascending = polyCoefficients(polynomial, variable);
1388
1621
  let result = constNode(0);
1389
- ascending.forEach((coeff, power2) => {
1622
+ ascending.forEach((coeff, power3) => {
1390
1623
  if (isRationalZero(coeff)) return;
1391
- const nextPower = power2 + 1;
1624
+ const nextPower = power3 + 1;
1392
1625
  const scaled = rationalDiv(coeff, { n: BigInt(nextPower), d: 1n });
1393
1626
  const term = {
1394
1627
  kind: "mul",
@@ -1577,7 +1810,7 @@ function taylorSeries(node, variable, point, degree) {
1577
1810
  let result = constNode(RATIONAL_ZERO);
1578
1811
  for (let order = 0; order <= degree; order++) {
1579
1812
  if (order > 0) factorial *= BigInt(order);
1580
- const derivative = differentiate(node, variable, order);
1813
+ const derivative = differentiate2(node, variable, order);
1581
1814
  const atPoint = simplifySymbolic(substitute(derivative, variable, pointNode));
1582
1815
  if (atPoint.kind !== "const") {
1583
1816
  throw ErrorFactory.execution(
@@ -1589,13 +1822,13 @@ function taylorSeries(node, variable, point, degree) {
1589
1822
  const coefficient = rationalDiv(atPoint.value, { n: factorial, d: 1n });
1590
1823
  if (isRationalZero(coefficient)) continue;
1591
1824
  const shifted = isRationalZero(point) ? varNode(variable) : { kind: "sub", left: varNode(variable), right: pointNode };
1592
- const power2 = order === 0 ? constNode(1) : order === 1 ? shifted : powNode(shifted, constNode(order));
1593
- result = { kind: "add", left: result, right: { kind: "mul", left: constNode(coefficient), right: power2 } };
1825
+ const power3 = order === 0 ? constNode(1) : order === 1 ? shifted : powNode(shifted, constNode(order));
1826
+ result = { kind: "add", left: result, right: { kind: "mul", left: constNode(coefficient), right: power3 } };
1594
1827
  }
1595
1828
  return simplifySymbolic(result);
1596
1829
  }
1597
1830
  function jacobian(functions, variables) {
1598
- return functions.map((fn) => variables.map((variable) => differentiate(fn, variable)));
1831
+ return functions.map((fn) => variables.map((variable) => differentiate2(fn, variable)));
1599
1832
  }
1600
1833
 
1601
1834
  // src/engine/EngineContext.ts
@@ -1709,6 +1942,55 @@ function pluraliseUnit(unit, count) {
1709
1942
  const plural = `${unit}s`;
1710
1943
  return UNIT_TABLE[plural.toLowerCase()] === void 0 ? unit : plural;
1711
1944
  }
1945
+ function extremum(args, wantLargest) {
1946
+ let best;
1947
+ let hasNaN = false;
1948
+ for (const a of args) {
1949
+ if (Number.isNaN(a.toNumber())) {
1950
+ hasNaN = true;
1951
+ continue;
1952
+ }
1953
+ if (best === void 0) {
1954
+ best = a;
1955
+ continue;
1956
+ }
1957
+ const { lv, rv, sameMeasure } = unifyUom(best, a);
1958
+ if (!sameMeasure) {
1959
+ const bestUnit = best.type === 6 /* Uom */ ? best.unit : void 0;
1960
+ const otherUnit = a.type === 6 /* Uom */ ? a.unit : void 0;
1961
+ return errorValue(
1962
+ "INCOMPATIBLE_UNITS",
1963
+ `Cannot compare incompatible units: ${bestUnit ?? "?"} and ${otherUnit ?? "?"}`
1964
+ );
1965
+ }
1966
+ if (wantLargest ? rv > lv : rv < lv) best = a;
1967
+ }
1968
+ if (hasNaN) return numberValue(NaN);
1969
+ if (best === void 0) return numberValue(wantLargest ? -Infinity : Infinity);
1970
+ return best.type === 6 /* Uom */ ? best : numberValue(best.toNumber());
1971
+ }
1972
+ function keepUnit(operand, magnitude2) {
1973
+ if (operand.type === 6 /* Uom */ && operand.unit !== void 0) return uomValue(magnitude2, operand.unit);
1974
+ return numberValue(magnitude2);
1975
+ }
1976
+ function nonFiniteEuclidOperand(a, b, name) {
1977
+ if (Number.isFinite(a) && Number.isFinite(b)) return null;
1978
+ const culprit = Number.isFinite(a) ? b : a;
1979
+ return errorValue(
1980
+ "INVALID_INTEGER_OPERAND",
1981
+ `${name} needs two whole numbers, and ${Number.isNaN(culprit) ? "one of them has no value" : "one of them is infinite"}`
1982
+ );
1983
+ }
1984
+ function euclidGcd(a, b) {
1985
+ let x = a;
1986
+ let y = b;
1987
+ while (y !== 0) {
1988
+ const t = y;
1989
+ y = x % y;
1990
+ x = t;
1991
+ }
1992
+ return x;
1993
+ }
1712
1994
  var builtinFunctions = {
1713
1995
  // ── Populated below ──
1714
1996
  // sqrt: a negative argument has a complex answer now that there is a complex
@@ -1722,38 +2004,20 @@ var builtinFunctions = {
1722
2004
  // abs(a): for a Matrix, "|a|" (Calca's determinant-pipe notation) is a
1723
2005
  // valid alias for det(a) (index 64), reusing the SAME implementation
1724
2006
  // not a separate one. Plain-number abs is unaffected.
1725
- 1: (args) => args[0].type === 7 /* Matrix */ ? determinant(args[0].value) : numberValue(Math.abs(args[0].toNumber())),
2007
+ 1: (args) => args[0].type === 7 /* Matrix */ ? determinant(args[0].value) : keepUnit(args[0], Math.abs(args[0].toNumber())),
1726
2008
  // sin/cos/tan accept an angle with a unit; see angleInRadians().
1727
2009
  2: (args) => numberValue(Math.sin(angleInRadians(args[0]))),
1728
2010
  3: (args) => numberValue(Math.cos(angleInRadians(args[0]))),
1729
2011
  4: (args) => numberValue(Math.tan(angleInRadians(args[0]))),
1730
2012
  5: (args) => numberValue(Math.log(args[0].toNumber())),
1731
- 6: (args) => numberValue(Math.ceil(args[0].toNumber())),
1732
- 7: (args) => numberValue(Math.floor(args[0].toNumber())),
1733
- 8: (args) => numberValue(Math.round(args[0].toNumber())),
1734
- // min/max: a plain loop replicating Math.min/Math.max's exact semantics
1735
- // (NaN poisons the result regardless of position; empty args -> ±Infinity)
1736
- // avoids the intermediate array .map()+spread allocates on every call.
1737
- 9: (args) => {
1738
- let result = Infinity;
1739
- let hasNaN = false;
1740
- for (const a of args) {
1741
- const n = a.toNumber();
1742
- if (Number.isNaN(n)) hasNaN = true;
1743
- else if (n < result) result = n;
1744
- }
1745
- return numberValue(hasNaN ? NaN : result);
1746
- },
1747
- 10: (args) => {
1748
- let result = -Infinity;
1749
- let hasNaN = false;
1750
- for (const a of args) {
1751
- const n = a.toNumber();
1752
- if (Number.isNaN(n)) hasNaN = true;
1753
- else if (n > result) result = n;
1754
- }
1755
- return numberValue(hasNaN ? NaN : result);
1756
- },
2013
+ // round/ceil/floor keep a unit for the same reason abs does; see keepUnit().
2014
+ 6: (args) => keepUnit(args[0], Math.ceil(args[0].toNumber())),
2015
+ 7: (args) => keepUnit(args[0], Math.floor(args[0].toNumber())),
2016
+ 8: (args) => keepUnit(args[0], Math.round(args[0].toNumber())),
2017
+ // min/max: see extremum() for why the winner is carried around as a Value
2018
+ // rather than as a running number.
2019
+ 9: (args) => extremum(args, false),
2020
+ 10: (args) => extremum(args, true),
1757
2021
  11: (args) => numberValue(Math.asin(args[0].toNumber())),
1758
2022
  12: (args) => numberValue(Math.acos(args[0].toNumber())),
1759
2023
  13: (args) => numberValue(Math.atan(args[0].toNumber())),
@@ -1779,7 +2043,22 @@ var builtinFunctions = {
1779
2043
  28: (args) => numberValue(Math.log10(args[0].toNumber())),
1780
2044
  29: (args) => numberValue(Math.log1p(args[0].toNumber())),
1781
2045
  30: (args) => numberValue(Math.log2(args[0].toNumber())),
1782
- 31: (args) => numberValue(Math.pow(args[0].toNumber(), args[1].toNumber())),
2046
+ // pow(): the spelled-out form of `^`, so a Matrix base means the same
2047
+ // repeated matrix multiplication `^` means. Without this branch
2048
+ // `pow([1,2;3,4], 2)` was Math.pow of a value whose toNumber() is 0, and
2049
+ // came back as the number 0. See vm/MatrixOps.ts's matrixPower().
2050
+ 31: (args) => {
2051
+ if (args[0].type === 7 /* Matrix */) {
2052
+ if (args[1].type !== 0 /* Number */) {
2053
+ return errorValue("MATRIX_POWER_UNSUPPORTED", `pow: a matrix's exponent must be a whole number, as in "pow([1,2;3,4], 2)".`);
2054
+ }
2055
+ return matrixPower(args[0].value, args[1].value);
2056
+ }
2057
+ if (args[1].type === 7 /* Matrix */) {
2058
+ return errorValue("MATRIX_POWER_UNSUPPORTED", `pow: a matrix may only be the base, as in "pow([1,2;3,4], 2)".`);
2059
+ }
2060
+ return numberValue(power(args[0].toNumber(), args[1].toNumber()));
2061
+ },
1783
2062
  32: () => numberValue(Math.random()),
1784
2063
  // takes no arguments, unlike its neighbours
1785
2064
  33: (args) => numberValue(Math.sign(args[0].toNumber())),
@@ -1802,32 +2081,37 @@ var builtinFunctions = {
1802
2081
  // gcd(a, b), Euclidean algorithm. Negative inputs are treated by
1803
2082
  // magnitude (gcd is conventionally defined over non-negative integers).
1804
2083
  38: (args) => {
1805
- let a = Math.trunc(Math.abs(args[0].toNumber()));
1806
- let b = Math.trunc(Math.abs(args[1].toNumber()));
1807
- while (b !== 0) {
1808
- const t = b;
1809
- b = a % b;
1810
- a = t;
1811
- }
1812
- return numberValue(a);
2084
+ const a = Math.trunc(Math.abs(args[0].toNumber()));
2085
+ const b = Math.trunc(Math.abs(args[1].toNumber()));
2086
+ const nonFinite = nonFiniteEuclidOperand(a, b, "gcd");
2087
+ if (nonFinite) return nonFinite;
2088
+ return numberValue(euclidGcd(a, b));
1813
2089
  },
1814
2090
  // lcm(a, b) = |a*b| / gcd(a, b), via the same Euclidean gcd inline
1815
2091
  // (a standalone gcd() call site, no shared helper needed for two uses).
1816
2092
  39: (args) => {
1817
2093
  const a = Math.trunc(Math.abs(args[0].toNumber()));
1818
2094
  const b = Math.trunc(Math.abs(args[1].toNumber()));
2095
+ const nonFinite = nonFiniteEuclidOperand(a, b, "lcm");
2096
+ if (nonFinite) return nonFinite;
1819
2097
  if (a === 0 || b === 0) return numberValue(0);
1820
- let x = a, y = b;
1821
- while (y !== 0) {
1822
- const t = y;
1823
- y = x % y;
1824
- x = t;
1825
- }
1826
- return numberValue(a / x * b);
2098
+ return numberValue(a / euclidGcd(a, b) * b);
1827
2099
  },
1828
2100
  // permutation(n, r) = n! / (n-r)!, computed as a running product
1829
2101
  // rather than two full factorials, so it stays exact for n well
1830
2102
  // beyond where n! itself would exceed Number.MAX_SAFE_INTEGER.
2103
+ //
2104
+ // The loop stops the moment the product stops being a representable
2105
+ // double, which is both the answer `fact` (index 62) already gives for
2106
+ // the same question and the thing that bounds the loop at all. `r` comes
2107
+ // straight off the stack: `permutation(1000000000000, 1000000000000)`
2108
+ // used to run a trillion iterations, of which the first ~170 computed
2109
+ // something and the rest multiplied Infinity by a number, and it had not
2110
+ // returned after thirty seconds. Every factor here is at least 1 and they
2111
+ // descend from `n`, so a product that is still finite after 170 steps
2112
+ // means the factors are small, which in turn means `r <= n` is small: the
2113
+ // early exit bounds the trip count without a separate estimate, and
2114
+ // without ever refusing a permutation that does have an answer.
1831
2115
  40: (args) => {
1832
2116
  const n = Math.trunc(args[0].toNumber());
1833
2117
  const r = Math.trunc(args[1].toNumber());
@@ -1835,12 +2119,27 @@ var builtinFunctions = {
1835
2119
  return errorValue("INVALID_RANGE", `permutation: invalid n=${n}, r=${r} (require 0 <= r <= n)`);
1836
2120
  }
1837
2121
  let result = 1;
1838
- for (let i = 0; i < r; i++) result *= n - i;
2122
+ for (let i = 0; i < r; i++) {
2123
+ result *= n - i;
2124
+ if (!Number.isFinite(result)) {
2125
+ return errorValue(
2126
+ "PERMUTATION_OVERFLOW",
2127
+ `permutation: P(${n}, ${r}) exceeds the maximum representable double (it passed it after ${i + 1} of ${r} factors)`
2128
+ );
2129
+ }
2130
+ }
1839
2131
  return numberValue(result);
1840
2132
  },
1841
2133
  // combination(n, r) = n! / (r! * (n-r)!), multiply-then-divide one
1842
2134
  // step at a time (standard technique) keeps every intermediate result
1843
2135
  // an integer, avoiding the overflow a naive factorial-ratio would hit.
2136
+ //
2137
+ // Same early exit, and needed for the same reason: `k = min(r, n-r)`
2138
+ // halves the trip count and bounds nothing, so `combination(1e12, 5e11)`
2139
+ // asked for five hundred billion iterations. The running value is C(n, i),
2140
+ // which grows monotonically to the middle, so once it is past what a
2141
+ // double can hold it never comes back, and the largest run this can now
2142
+ // take is the thousand-odd steps C(n, k) needs to cross that line.
1844
2143
  41: (args) => {
1845
2144
  const n = Math.trunc(args[0].toNumber());
1846
2145
  const r = Math.trunc(args[1].toNumber());
@@ -1849,7 +2148,15 @@ var builtinFunctions = {
1849
2148
  }
1850
2149
  const k = Math.min(r, n - r);
1851
2150
  let result = 1;
1852
- for (let i = 0; i < k; i++) result = result * (n - i) / (i + 1);
2151
+ for (let i = 0; i < k; i++) {
2152
+ result = result * (n - i) / (i + 1);
2153
+ if (!Number.isFinite(result)) {
2154
+ return errorValue(
2155
+ "COMBINATION_OVERFLOW",
2156
+ `combination: C(${n}, ${r}) exceeds the maximum representable double (it passed it after ${i + 1} of ${k} steps)`
2157
+ );
2158
+ }
2159
+ }
1853
2160
  return numberValue(Math.round(result));
1854
2161
  },
1855
2162
  // average(...), arithmetic mean of any number of arguments. Backs the
@@ -2099,7 +2406,7 @@ var builtinFunctions = {
2099
2406
  61: (args) => {
2100
2407
  const n = args[0].toNumber();
2101
2408
  const x = args[1].toNumber();
2102
- return numberValue(Math.pow(x, 1 / n));
2409
+ return numberValue(power(x, 1 / n));
2103
2410
  },
2104
2411
  // fact(n) / factorial(n) -- integer factorial. Deliberately rejects
2105
2412
  // negative or non-integer input (factorial isn't defined for either
@@ -2196,7 +2503,7 @@ var builtinFunctions = {
2196
2503
  if (typeof variable !== "string") return variable;
2197
2504
  const expression = valueToSymbolic(target);
2198
2505
  if (expression === null) return errorValue("SYMBOLIC_NONFINITE_OPERAND", "der needs an expression with an exact value.");
2199
- return symbolicToValue(differentiate(expression, variable, orderValue?.toNumber() ?? 1));
2506
+ return symbolicToValue(differentiate2(expression, variable, orderValue?.toNumber() ?? 1));
2200
2507
  },
2201
2508
  // integral(expr, variable), the indefinite integral without a constant of
2202
2509
  // integration. Reports what it cannot do rather than approximating; see
@@ -2311,8 +2618,11 @@ var builtinFunctions = {
2311
2618
  // so nothing is lost. A pair that is not ordered major-then-minor, or
2312
2619
  // that spans two different measures, reports rather than guessing.
2313
2620
  94: (args) => {
2314
- const major = args[1].value;
2315
- const minor = args[2].value;
2621
+ const major = typeof args[1]?.value === "string" ? args[1].value : void 0;
2622
+ const minor = typeof args[2]?.value === "string" ? args[2].value : void 0;
2623
+ if (major === void 0 || minor === void 0) {
2624
+ return errorValue("UNKNOWN_UNIT", 'both units have to be named, as in "12.5 minutes in minutes and seconds"');
2625
+ }
2316
2626
  const majorEntry = UNIT_TABLE[major.toLowerCase()];
2317
2627
  const minorEntry = UNIT_TABLE[minor.toLowerCase()];
2318
2628
  if (majorEntry === void 0 || minorEntry === void 0) {
@@ -2462,6 +2772,26 @@ var builtinFunctions = {
2462
2772
  return errorValue("INVALID_RATE", "annual return: nothing was returned, so there is no annual rate");
2463
2773
  }
2464
2774
  return percentageValue(Math.pow(returned / invested, 1 / years) - 1);
2775
+ },
2776
+ // roundToPlaces(value, places) -> value rounded to that many decimal
2777
+ // places, backing `<value> to <n> dp` (converters/parselets/
2778
+ // RoundingParselets.ts).
2779
+ //
2780
+ // It exists because the obvious bytecode for it, "multiply by 10^p, round,
2781
+ // divide by 10^p", is only safe for values small enough that the multiply
2782
+ // is exact. `1e21 to 2 dp` came back as 999999999999999900000: 1e21 has no
2783
+ // fractional part to round at all, and the round trip through 1e23 was
2784
+ // pure loss. Asking for fewer decimal places than a number has cannot
2785
+ // change it, so a value that is already whole is returned untouched.
2786
+ 97: (args) => {
2787
+ const source = args[0];
2788
+ const value = source.toNumber();
2789
+ const places = args[1].toNumber();
2790
+ if (!Number.isFinite(value) || Number.isInteger(value)) return keepUnit(source, value);
2791
+ const scale2 = Math.pow(10, places);
2792
+ const scaled = value * scale2;
2793
+ if (!Number.isFinite(scaled) || Math.abs(scaled) > Number.MAX_SAFE_INTEGER) return keepUnit(source, value);
2794
+ return keepUnit(source, Math.round(scaled) / scale2);
2465
2795
  }
2466
2796
  };
2467
2797
  function applyComplexAccessor(name, value) {
@@ -2612,6 +2942,6 @@ function globalDagKey(name) {
2612
2942
  return `global:${name}`;
2613
2943
  }
2614
2944
 
2615
- export { CPI_MAX_YEAR, CPI_MIN_YEAR, OpRegistry, SYMBOLIC_NATIVE_BUILTINS, adjustForInflation, allocatePluginFunctionIndex, asConverterRegistry, binaryOp, builtinFunctions, createEngineContext, defaultEngineContext, globalDagKey, pluginFunctionRegistry, registerAsConverter, sharedGlobalVariableStore, sharedOpRegistry, solveEquationValues, symbolicBuiltin, symbolicNeg, symbolicPow, unifyUom, unregisterAsConverter };
2616
- //# sourceMappingURL=chunk-MVBEL57Q.js.map
2617
- //# sourceMappingURL=chunk-MVBEL57Q.js.map
2945
+ export { CPI_MAX_YEAR, CPI_MIN_YEAR, OpRegistry, SYMBOLIC_NATIVE_BUILTINS, adjustForInflation, allocatePluginFunctionIndex, asConverterRegistry, bigIntDivisionByZero, binaryOp, builtinFunctions, compareBigIntOperands, compareUom, createEngineContext, defaultEngineContext, globalDagKey, incomparableUnitsError, pluginFunctionRegistry, power, registerAsConverter, sharedGlobalVariableStore, sharedOpRegistry, solveEquationValues, symbolicBuiltin, symbolicNeg, symbolicPow, toBigIntOperand, unifyUom, unregisterAsConverter };
2946
+ //# sourceMappingURL=chunk-Y7FT4IQT.js.map
2947
+ //# sourceMappingURL=chunk-Y7FT4IQT.js.map