You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add tests for costFunction setter, enhance Fresnel tests, and introduce special functions tests
- Implement tests for the costFunction setter in ComputeEngine to ensure proper fallback to default when non-function values are assigned.
- Update Fresnel tests to reflect accurate values for large arguments S(50) and C(50).
- Add comprehensive tests for arbitrary-precision kernels for Erf/Erfc/ErfInv and Sinc/FresnelS/FresnelC, ensuring symbolic evaluation and numeric approximation correctness.
- Introduce tests for the intersection of numeric primitives in the type lattice, ensuring soundness and maximality of subtype reductions.
- Add exact values tests for the Riemann zeta function at integer literals, including checks for Bernoulli numbers and symbolic trivial zeros.
Copy file name to clipboardExpand all lines: REVIEW.md
+14-4Lines changed: 14 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -253,7 +253,7 @@ findings there are edge cases. The areas with the most serious problems are:
253
253
| ✅ A2 | HIGH |`boxed-expression/boxed-function.ts:853`|`ln()` of `Root(a,b)` computes the reciprocal: `b.div(a.ln(base))` instead of `a.ln(base).div(b)`. **✓ verified:**`Root(x,3).ln()` → `3/ln(x)`. Fixed: now `(1/3)·ln(x)`. Regression test in `arithmetic.test.ts` → "Ln of Root (REVIEW.md A2)". |
254
254
| ✅ A3 | HIGH |`boxed-expression/abstract-boxed-expression.ts:636-658`|`isLess`/`isGreater`/etc. return definitive `false` when `cmp()` returns the indeterminate `'<='`/`'>='`. **✓ verified:** after `assume(y >= 3)`, `y.isGreater(3)` → `false` (should be `undefined`). These predicates feed sign inference engine-wide. |
255
255
| ✅ A4 | HIGH |`boxed-expression/boxed-number.ts:786-791`|`canonicalNumber` returns **+∞** for a rational with −∞ numerator (inverted sign logic; denominator sign also ignored). **✓ verified:**`ce.number([-Infinity, 5])` → `+oo`. Fixed: result sign is now the product of numerator/denominator signs. Regression test in `numbers.test.ts` → "Rational with an infinite numerator/denominator (REVIEW.md A4)". |
256
-
| A5 | HIGH |`index.ts:1000-1003`|`costFunction` setter is missing an `else`: the guard assignment is always overwritten, so any non-function value is stored and later invoked, crashing `simplify()`. |
256
+
|✅ A5 | HIGH |`index.ts:1000-1003`|`costFunction` setter is missing an `else`: the guard assignment is always overwritten, so any non-function value is stored and later invoked, crashing `simplify()`. |
257
257
| ✅ A6 | MED |`boxed-expression/arithmetic-power.ts:577-605`|`root()` numeric path returns a positive real for even roots of negatives. **✓ verified:**`Root(-16, 4).N()` → `2` (should be NaN/complex). **✓ verified + fixed:** even root of a negative now returns the complex principal root `|a|^(1/n)·(cos(π/n)+i·sin(π/n))` — `Root(-16,4).N()`=√2+√2i, consistent with `Sqrt(-4)`=2i. |
258
258
| ✅ A7 | MED |`boxed-number.ts:391-403`, `boxed-function.ts:866-872`|`ln(base)` silently drops non-integer bases (falls through to natural log). **✓ verified:**`ce.number(8).ln(2.5)` loses the base. `BoxedSymbol.ln` handles it correctly — the three implementations are inconsistent. **✓ verified + fixed:** BoxedNumber/BoxedFunction `ln` now honor any base — `(8).ln(2.5)`=log_2.5(8)≈2.269 (was ln 8). |
259
259
| ✅ A8 | MED |`boxed-expression/boxed-symbol.ts:774-794`| Plain symbols report `isEmptyCollection: true`, `isFiniteCollection: true`, `count: 0` via `?? 0` fallbacks, contradicting the abstract-class contract (`undefined` for non-collections). **✓ verified.****✓ verified + fixed:** removed the `??0`/`count===0`/`isFinite(count)` fallbacks; a plain symbol now returns `undefined` for `count`/`isEmptyCollection`/`isFiniteCollection`. |
@@ -292,7 +292,7 @@ findings there are edge cases. The areas with the most serious problems are:
292
292
| ✅ B20 | MED |`library/trigonometry.ts:63-91`|`Degrees` canonical handler reduces literals mod 360, but the evaluate handler doesn't — the same operator denotes different values depending on whether the arg was a literal at canonicalization. **✓ verified:**`Degrees(390)` → π/6 vs 13π/6. **Fixed (2026-06-10):** removed the mod-360 reduction from the *canonical* handler (rather than adding it to evaluate) so `Degrees` is a faithful `d·π/180` conversion in both paths → both give `13π/6`. This is the correct direction: `serialize-dms.test.ts` shows range normalization is a *serialization* concern (`angleNormalization`) and that faithful negatives (`Degrees(-45.5)`→`-45°30'`) must be preserved; the previously-failing `@fixme``\tan…\degree` test now passes unchanged. Test in `trigonometry.test.ts` → "Degrees is a faithful conversion (B20)". |
293
293
| ✅ B21 | MED |`library/number-theory.ts:200-216`|`IsHappy` throws on negative input (`BigInt('-')`). **✓ verified + fixed (2026-06-10):** non-positive integers (`k < 1`) now return `False` (happy numbers are positive). Test in `number-theory.test.ts` → "IsHappy on non-positive input (B21)". |
294
294
| ✅ B22 | MED |`library/combinatorics.ts:187-247`|`Multinomial`/`BellNumber` use machine floats: `Multinomial(20,20)` → `137846528820.00003`; overflow past n≈170/25. Siblings `Binomial`/`Fibonacci` already use bigint. **✓ verified + fixed (2026-06-10):**`Multinomial` uses an exact bigint factorial (integer division is exact); `BellNumber` uses the bigint Bell triangle (Aitken's array). `Multinomial(20,20)`=137846528820, `BellNumber(25)`=4638590332229999353. The now-dead float `binomial` helper was removed. Tests in `combinatorics.test.ts` → "Exact Multinomial and BellNumber (B22)". |
295
-
|⏸️ B23 | LOW |`library/statistics.ts:38-67`, `trigonometry.ts:269-298`|`Erf`/`Erfc`/`ErfInv`/`Sinc`/`Fresnel*` ignore `numericApproximation` — exact `evaluate()` returns machine floats, and high-precision engines silently get 64-bit accuracy. **⏸️ DEFERRED (2026-06-10):** a proper fix needs arbitrary-precision (BigDecimal) kernels for erf/erfc/sinc/Fresnel — substantial new numeric code, distinct in character from the per-function library fixes here. G1 already made the *machine*`Erf`/`Erfc` full double precision; the bignum/`numericApproximation` path is the remaining LOW work. |
295
+
|✅ B23 | LOW |`library/statistics.ts:38-67`, `trigonometry.ts:269-298`|`Erf`/`Erfc`/`ErfInv`/`Sinc`/`Fresnel*` ignore `numericApproximation` — exact `evaluate()` returns machine floats, and high-precision engines silently get 64-bit accuracy. **⏸️ DEFERRED (2026-06-10):** a proper fix needs arbitrary-precision (BigDecimal) kernels for erf/erfc/sinc/Fresnel — substantial new numeric code, distinct in character from the per-function library fixes here. G1 already made the *machine*`Erf`/`Erfc` full double precision; the bignum/`numericApproximation` path is the remaining LOW work. |
296
296
297
297
### LaTeX syntax & MathJSON
298
298
@@ -554,9 +554,19 @@ verification standard as above (all reproduced at runtime):
554
554
| ✅ G13 | HIGH |`boxed-expression/arithmetic-mul-div.ts:~685` (canonicalDivide) |`ce.box(['Divide', ['Add', 1, 'ImaginaryUnit'], 2])` canonicalizes to `Multiply(1/2, NaN)`. Dividing a Gaussian-integer sum by an integer destroys the value at boxing time. **✓ verified + fixed:** root cause is in `factor()` (not canonicalDivide) — its Add case takes the gcd of term coefficients, but `gcd(1, i)` = NaN (complex coeff), poisoning the result → `factor(1+i)`=NaN → `toNumericValue` returns `[1, NaN]` → `Multiply(1/2, NaN)`. Guarded `factor()` to leave sums with a complex coefficient (`coeff.im !== 0`) unfactored; `Divide((1+i),2)`→`Multiply(1/2, 1+i)` (=0.5+0.5i). Tests in `factor.test.ts` "Gaussian-integer sums (G13)". |
555
555
556
556
**G3 addendum (2026-06-09, fixed in working tree):** the `sets.ts` audit left one stub behind — `setMinus` (the `SetMinus` evaluate handler) unconditionally returned `EmptySet`, so `Element(x, SetMinus(...))` was always False through the evaluate path. Fixed: computes the difference for finite collections, stays symbolic otherwise; trailing set-valued operands now exclude their *members* (consistently in `contains`, `count`, and the iterator). Verified: `Element(3, CC∖{0}) → True`, `Element(0, CC∖{0}) → False`, `SetMinus({1,2,3}, {2,3}) → {1}`.
557
-
| G14 | MED | comparison engine (infinity ordering) |`-∞ > -∞` evaluates to `true` (strict self-comparison of infinities). Found while fixing interval membership; literal interval containment now routes around it via numeric comparison. |
558
-
| G15 | MED |`common/type/reduce.ts` (intersection of numeric primitives) | The type lattice reduces incomparable-but-overlapping numeric primitives to `nothing` — e.g. `integer ∩ finite_real = nothing`, `finite_number ∩ real = nothing` — making type-based membership refutation unsound if used naively. Workaround in sets.ts uses `'number'`-level checks only; the lattice reduction deserves a proper fix. |
557
+
|✅ G14 | MED | comparison engine (infinity ordering) |`-∞ > -∞` evaluates to `true` (strict self-comparison of infinities). Found while fixing interval membership; literal interval containment now routes around it via numeric comparison. |
558
+
|✅ G15 | MED |`common/type/reduce.ts` (intersection of numeric primitives) | The type lattice reduces incomparable-but-overlapping numeric primitives to `nothing` — e.g. `integer ∩ finite_real = nothing`, `finite_number ∩ real = nothing` — making type-based membership refutation unsound if used naively. Workaround in sets.ts uses `'number'`-level checks only; the lattice reduction deserves a proper fix. |
559
559
560
560
**G2 + G10 addendum (2026-06-10, fixed in working tree by the Fungrim thread):**
561
561
-**G2 fixed** — three compounding defects: the `_x` binding mismatch across solve passes (all passes now share one literal-`_x` convention with root back-substitution), `captureWildcard`'s blanket rejection (now rejects only *unbound*-wildcard captures), and two masking bugs (`clearDenominators` mangling `e^x` via the trivial `1^x` denominator; `matchVariations`' Exp case built backwards). Harmonization now chains (depth 4), `Equal` gets injective-wrapper peeling, and two dead built-in harmonization rules had wildcard typos fixed. `e^x=5 → ln 5`, `10^x=100 → 2`, `ln x = ln 3 → 3` exactly; 3 oracle solve snapshots improved ([] → correct root).
562
562
-**G10 fixed** — Set comprehensions are first-class: conservative builder-vs-literal disambiguation (Fungrim `Element` indexing-set form + both LaTeX `Condition` forms), comprehension-aware count/iterator/contains (three-valued, deduped, 1000-value enumeration cap), lazy Set evaluation so the condition isn't pre-evaluated. `Count{k∈Range(1,n): gcd(n,k)=1}` = Totient(n) verified n=2..8; literal sets byte-identical.
-**G14 ✅** NaN/equal-infinity comparisons fixed in all three `cmp()` paths (strict self-comparison of ±∞ now false, non-strict true, NaN → undefined, identical at machine/bignum). Follow-on: explicit NaN absorption added to `evaluateMinMax` (Min/Max with a NaN operand → NaN, now symmetric; 1 snapshot updated).
568
+
-**G15 ✅** principled pairwise meet from the transitive closure of PRIMITIVE_SUBTYPES (`integer ∩ finite_real = finite_integer`, …), + repaired a latent transitivity hole (`imaginary ⊄ finite_number`). 309-test lattice suite.
569
+
-**D6 ✅** BigDecimal transcendental bridge rearchitected: decimal-exponent factoring for exp/ln/sqrt/cbrt, small-argument relative-precision policy for trig/hyperbolics, huge-argument saturation, fpln 0-guard, pow guard digits, honest NaN beyond the stored-π trig-reduction range. `ln(1e-100)` terminates; `exp(−200)` exact to 50 digits.
570
+
-**B23 ✅** bignum kernels for Erf/Erfc/ErfInv/Sinc/FresnelS/C with `numericApproximation` gating (exact mode stays symbolic); full precision at p=40/100, no machine fallback needed. Opportunistic: machine erfInv rewritten (was ~4 digits mid-range), machine Fresnel cutoff fixed (36 → 36974; fresnelS(50) had ~2 correct digits), complex Erf args now stay symbolic (were computing erf(Re z)).
571
+
-**Performance (section c) ✅** LaTeX parsing **~12.6×** faster (dictionary indexing, single-parse symbol triggers, lookAhead cache, sticky-regex tokenizer — ~200× on non-ASCII symbol-heavy input — hoisted constants), byte-identical output across 1,330 snapshots. Type system: parseType memoized (**~200×**), string-operand isSubtype ~29×, widen ~6×; legacy TypeParser deleted (−1,080 net lines).
572
+
-**Feature:** exact `Zeta` at integer literals (ζ(2k) → rational·π^2k via exact Bernoulli; ζ(−n) rationals; ζ(−2k) → 0; cap |s| ≤ 100) + injected symbolic rule `fungrim:zeta-trivial-zeros` (artifact → 1,350 rules); 2 baseline snapshots updated as strictly-better (ascii-math sign-consistency; canonical-form last digit now correctly rounded per D6).
Copy file name to clipboardExpand all lines: scripts/fungrim/curation-overrides.json
+17-1Lines changed: 17 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
{
2
-
"$comment": "Hand-maintained curation overrides for the Fungrim Phase-1 rule compiler (FUNGRIM-PLAN-5-LOADER.md \u00a72.3/\u00a72.6). Merged last by scripts/fungrim/compile-rules.ts. `overrides` entries ({ direction?, purpose?, target?, exclude?, note? }, keyed by corpus entry id) adjust or exclude compiled artifact rules. `transformAllowlist` promotes hand-vetted growth-neutral canonicalizations from 'expand' to 'transform' (the machine policy never emits 'transform'). `solveSeeds` is the \u00a72.6 hand-curated solve-template seed set: it is NOT compiled into the M1 artifact and does not affect default loading \u2014 the M2 runtime loader compiles these entries into UNIVARIATE_ROOTS-style templates behind loadFungrim(ce, { solve: true }) (off by default, Q7).",
2
+
"$comment": "Hand-maintained curation overrides for the Fungrim Phase-1 rule compiler (FUNGRIM-PLAN-5-LOADER.md \u00a72.3/\u00a72.6). Merged last by scripts/fungrim/compile-rules.ts. `overrides` entries ({ direction?, purpose?, target?, exclude?, note? }, keyed by corpus entry id) adjust or exclude compiled artifact rules. `transformAllowlist` promotes hand-vetted growth-neutral canonicalizations from 'expand' to 'transform' (the machine policy never emits 'transform'). `inject` is a list of hand-curated corpus-Entry-shaped synthetic entries appended to the slice and compiled through the FULL pipeline (guards, orientation, self-test) \u2014 for sound identities missing from the upstream corpus. `solveSeeds` is the \u00a72.6 hand-curated solve-template seed set: it is NOT compiled into the M1 artifact and does not affect default loading \u2014 the M2 runtime loader compiles these entries into UNIVARIATE_ROOTS-style templates behind loadFungrim(ce, { solve: true }) (off by default, Q7).",
"references": "Curated injection (not in the upstream corpus slice): the trivial zeros of the Riemann zeta function, Zeta(-2n) = 0 for positive integer n. The Track-3 guard machinery discharges the positive-integer guard for symbolic n (declared integer + assumed n > 0); literal arguments are covered by the exact Zeta evaluate path in library/arithmetic.ts.",
0 commit comments