diff --git a/AGENTS.md b/AGENTS.md
index 9bd1ff7..8e10baa 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -23,7 +23,8 @@ every documented defect.
## Which AngouriMath you built against
The build uses a **sibling AngouriMath checkout at `../AngouriMath` when one exists, and the
-released 2.1.0 NuGet package when it does not**. It prints which; read the line.
+released NuGet package (2.5.0, set once as `AngouriMathPackageVersion`) when it does not**.
+It prints which, and the version; read the line.
This used to be the first trap in the repo — the fallback was 1.4.0, which behaves
differently enough that two assertions in `test/smoke.sh` failed against it legitimately
diff --git a/README.md b/README.md
index c77fc42..542d3eb 100644
--- a/README.md
+++ b/README.md
@@ -264,14 +264,13 @@ Limits worth knowing: there are no eigenvectors, no SVD, and no matrix exponenti
and that is Abel–Ruffini rather than a defect — no general radical solution exists. Ten qubits
would be a 1024×1024 symbolic matrix; this dies long before that.
-**A defect it works around.** `Entity.Matrix.Determinant` calls GenericTensor's
-`DeterminantGaussianSafeDivision`, which divides by pivots and leaves a `provided` guard for
-each. Those guards are artefacts, not mathematics: the raw output claims
-`det([[a,b],[c,d]]) = a*d - b*c provided not a = 0`, and eigenvalues of `[[0,J],[J,0]]` come
-back as `J provided not J = 0` — excluding a perfectly valid case. This server simplifies
-*first* (the guard is what licenses cancelling `a/a`), then strips the guard and reports it
-under `dropped_guards`. The real fix belongs upstream: GenericTensor already ships a
-division-free `DeterminantLaplace`, which emits none of this.
+**Pivot guards.** Before AngouriMath 2.5.0 the determinant divided by pivots and left a
+`provided` guard for each — `det([[a,b],[c,d]]) = a*d - b*c provided not a = 0`, and
+eigenvalues of `[[0,J],[J,0]]` came back as `J provided not J = 0` — artefacts rather than
+mathematics. 2.5.0's determinant leaves none: `a*d - b*c`, and `lambda^2 - J^2` for that
+characteristic polynomial. This server still simplifies *first* and strips any guard that
+remains, reporting it under `dropped_guards`, so a guard from any other pivoting routine is
+handled the same way.
## Step-by-step, and knowing what to distrust
diff --git a/UPSTREAM.md b/UPSTREAM.md
index ce4cb00..4dca690 100644
--- a/UPSTREAM.md
+++ b/UPSTREAM.md
@@ -42,27 +42,32 @@ reports them under `dropped_guards` in the meantime.
## Defects worth reporting upstream
-Re-verified against AngouriMath 2.1.0 — a claim measured on an older build is not worth
+Re-verified against AngouriMath 2.5.0 — a claim measured on an older build is not worth
reporting. `--selftest` re-checks each row on every run; three entries were dropped at the
2.0.0 upgrade because the release fixed them, which is the whole reason that check exists.
-**Nothing was dropped at 2.1.0**, and that is a measurement rather than an assumption: the
-build was made against the *published* 2.1.0 package rather than the sibling checkout, which
-still sat at 2.0.0 while this was written. All eleven identities hold and all five rows below
-still reproduce. 2.1.0 is a correctness release, and none of what it fixed is on this list.
+**Two were dropped at 2.5.0**, measured against the *published* 2.5.0 package rather than the
+sibling checkout: the integral of `x^4*(1-x)^4/(1+x^2)` and the determinant's pivot guards,
+both under "Fixed upstream" below. Nothing was dropped at 2.1.0. All identities hold, the
+integral is now one of them, and the four rows below still reproduce.
| Observed | Note |
|---|---|
| `Simplify(sqrt(x^2))` is left as written, not reduced to `abs(x)` | No longer the soundness bug it was — 2.0.0 stopped answering `x`, which was wrong for every negative. What remains is a gap: writing `abs` needs to know the expression is real, which the codomain of [#719](https://github.com/asc-community/AngouriMath/issues/719) can now say and the simplifier does not yet read. |
| `MathS.Equations(...)` throws on an equality | It wants each equation in `= 0` form; passing an `Equalsf` raises `NotSufficientlySupportedException` rather than normalising `a = b` to `a - b`, which is a rewrite it could do itself. This server does it instead. Re-checked on 2.0.0 by `--selftest`; the exception type changed with the release, the behaviour did not. |
-| `Integrate` declines `x^4*(1-x)^4/(1+x^2)` | It handles the same function once the polynomial division is done by hand, so the gap is dividing a rational function whose numerator outranks its denominator. |
| `Entity.DefiniteIntegral` is a first-order rule | New in 2.0.0, and it is a rectangle rule: the error halves per doubling of the step count, so 4000 steps buy about four digits of `∫[0,1] e^(x^2)` at ~150 ms. Simpson's rule is the same amount of code and would give roughly eight. It also samples both endpoints, so a convergent integral with a singular endpoint — `∫[0,1] sin(x)/x`, `∫[0,1] ln(x)` — returns `NaN` rather than a value. Both are worth raising; this server runs it twice and reports only the agreed digits in the meantime. |
| An unknown identifier still becomes implicit multiplication silently | 2.0.0 closed most of this: `exp`, `log10`, `log2`, `pow`, `floor`, `ceil`, `round`, `min`, `max` and `gcd` became real functions, and eleven names the library does not have are now refused by name. The general case remains — `im(z)` is `im * z` — and cannot be closed without refusing `a(b + c)`, so a warning or a strict default is still the only answer. This server warns. |
## Fixed upstream, kept here as a record
-Each of these was on the list above and reproduced no longer at the 2.0.0 upgrade. Listed
-so that nobody re-reports them, and so the cost of not re-measuring is visible.
+Each of these was on the list above and reproduced no longer at an upgrade. Listed so that
+nobody re-reports them, and so the cost of not re-measuring is visible.
+
+- **`Integrate` declining `x^4*(1-x)^4/(1+x^2)`** (at 2.5.0). It divides the rational
+ function out itself now, and the integral over `[0, 1]` is exactly `22/7 - pi`, which
+ `--selftest` checks as an identity.
+- **The determinant's pivot guards** (at 2.5.0). `det([[a,b],[c,d]])` was
+ `a*d - b*c provided not a = 0`; it is `a*d - b*c`.
- **`Factorize(x^2 - 1)` emitting `sqrt(1)`.** Dropped before 2.0.0: true of 1.4.0, false
of the branch, and it went stale unnoticed. This is why `--selftest` exists.
diff --git a/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj b/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj
index fe13d64..b0f81ae 100644
--- a/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj
+++ b/src/AngouriMath.Mcp/AngouriMath.Mcp.csproj
@@ -31,6 +31,9 @@
$(MSBuildThisFileDirectory)../../../AngouriMath/Sources/AngouriMath/AngouriMath.csproj
true
+
+ 2.5.0
@@ -40,13 +43,13 @@
-
+
+ Text="AngouriMathMCP: building against the released $(AngouriMathPackageVersion) package." />
diff --git a/src/AngouriMath.Mcp/Matrices.cs b/src/AngouriMath.Mcp/Matrices.cs
index 6382167..3e3ce64 100644
--- a/src/AngouriMath.Mcp/Matrices.cs
+++ b/src/AngouriMath.Mcp/Matrices.cs
@@ -134,15 +134,14 @@ public static (Matrix Value, List Guards) Clean(Matrix m)
///
/// Eigenvalues via the characteristic polynomial.
///
- /// The guard-dropping is not cosmetic. Entity.Matrix.Determinant calls GenericTensor's
- /// DeterminantGaussianSafeDivision, which divides by pivots and leaves a `provided` guard
- /// for each one. On a symbolic matrix those guards are artefacts of the algorithm, not
- /// mathematics: the characteristic polynomial of the Pauli X matrix comes back as
- /// `lambda^2 - 1 provided not lambda = 0`, and [[0,J],[J,0]] yields eigenvalues
+ /// The guard-dropping is not cosmetic. Until AngouriMath 2.5.0, Entity.Matrix.Determinant
+ /// called GenericTensor's DeterminantGaussianSafeDivision, which divides by pivots and left
+ /// a `provided` guard for each one. On a symbolic matrix those guards are artefacts of the
+ /// algorithm, not mathematics: the characteristic polynomial of the Pauli X matrix came
+ /// back as `lambda^2 - 1 provided not lambda = 0`, and [[0,J],[J,0]] yielded eigenvalues
/// `{J provided not J = 0, -J provided not J = 0}` — both wrong, since those values are
- /// perfectly valid. GenericTensor also ships the division-free DeterminantLaplace, which
- /// would emit none of this; until AngouriMath uses it for symbolic entries, we strip the
- /// guards here and report what was dropped.
+ /// perfectly valid. 2.5.0's determinant leaves none (`lambda^2 - 1`, `lambda^2 - J^2`); the
+ /// stripping stays, and still reports what it dropped, for any routine that pivots.
///
public static Eigen Compute(Matrix a, Variable lambda)
{
diff --git a/src/AngouriMath.Mcp/Parsing.cs b/src/AngouriMath.Mcp/Parsing.cs
index 270d9e9..3699606 100644
--- a/src/AngouriMath.Mcp/Parsing.cs
+++ b/src/AngouriMath.Mcp/Parsing.cs
@@ -58,6 +58,14 @@ public sealed record Outcome(Entity? Entity, List Warnings, string? Erro
"floor", "ceil", "ceiling", "round", "min", "max", "gcd", "factorial",
// `pow(x, y)` parses to x^y as of 2.0.0.
"pow",
+ // Short and alternative spellings the grammar has accepted at least since 2.1.0, and
+ // which this list missed until the 2.5.0 probe read every name back: `asin(x)` was
+ // being warned about as an unknown function.
+ "asin", "acos", "atan", "asec", "acsc", "acosec", "acot", "acotan",
+ "sh", "ch", "th", "cth", "sch",
+ "acoth", "acsch", "arcoth", "arcsch", "arsch",
+ // New by 2.5.0: a set's cardinality, sums and products, and arg-extrema.
+ "card", "sum", "product", "argmax", "argmin",
"gamma", "phi", "derivative", "integral", "limit",
"limitleft", "limitright", "piecewise", "provided", "apply", "lambda",
"domain", "intersect", "and", "or", "not", "xor",
diff --git a/src/AngouriMath.Mcp/Resources.cs b/src/AngouriMath.Mcp/Resources.cs
index 3a5f4c4..e43c5fc 100644
--- a/src/AngouriMath.Mcp/Resources.cs
+++ b/src/AngouriMath.Mcp/Resources.cs
@@ -131,12 +131,11 @@ exactly what to run — so this doubles as a self-test corpus. If one of these s
## A machine-checked proof that 22/7 > π
The integrand is positive on (0,1), so the integral is positive — which proves the
- inequality. It needs the polynomial division done by hand first, because the
- integrator declines the unexpanded rational form:
+ inequality:
- 1. `am_verify_equal x^4*(1-x)^4/(1+x^2) vs x^6 - 4*x^5 + 5*x^4 - 4*x^2 + 4 - 4/(1+x^2)`
- 2. `am_integrate x^6 - 4*x^5 + 5*x^4 - 4*x^2 + 4 - 4/(1+x^2) dx` (verifies)
- 3. `am_evaluate 1/7 - 4/6 + 1 - 4/3 + 4 - 4*arctan(1)` → **exactly `22/7 - pi`**
+ 1. `am_integrate x^4*(1-x)^4/(1+x^2) dx` (verifies; the integrator divides the
+ rational function out itself as of AngouriMath 2.5.0)
+ 2. `am_evaluate 1/7 - 4/6 + 1 - 4/3 + 4 - 4*arctan(1)` → **exactly `22/7 - pi`**
## Near misses — where floating point would lie to you
diff --git a/src/AngouriMath.Mcp/SelfTest.cs b/src/AngouriMath.Mcp/SelfTest.cs
index 954d79a..b1b0666 100644
--- a/src/AngouriMath.Mcp/SelfTest.cs
+++ b/src/AngouriMath.Mcp/SelfTest.cs
@@ -45,6 +45,17 @@ private static readonly (string Name, Func Holds)[] Identities =
("42 is 101010 in binary", () => MathS.ToBaseN(42, 2) == "101010"),
("42 is the 5th Catalan number", () => Value("10! / (6! * 5!)") == "42"),
("Pythagoras: sin^2 + cos^2 = 1", () => IsZero("sin(x)^2 + cos(x)^2 - 1")),
+ ("22/7 - pi is the integral of x^4*(1-x)^4/(1+x^2) over [0, 1]", () =>
+ {
+ // Documented as declined until 2.5.0 integrated it whole; kept as an identity so
+ // the proof the reliability resource walks through cannot silently stop working.
+ var outcome = Parsing.Parse("x^4*(1-x)^4/(1+x^2)");
+ if (outcome.Entity is null) return false;
+ var x = MathS.Var("x");
+ var antiderivative = outcome.Entity.Integrate(x);
+ var value = antiderivative.Substitute(x, 1) - antiderivative.Substitute(x, 0);
+ return Numeric.IsZero((value - (MathS.FromString("22/7") - MathS.pi)).Simplify());
+ }),
("d/dx integral of x*ln(x) returns the integrand", () =>
{
var outcome = Parsing.Parse("x*ln(x)");
@@ -85,22 +96,6 @@ private static readonly (string Name, Func StillBroken, string Documented)
var ratio = Math.Abs(fine - coarse) / Math.Abs(finer - fine);
return ratio is > 1.7 and < 2.3;
}, "error halves per doubling, so ~4 digits at 4000 steps; Simpson would give more"),
- ("integral of x^4*(1-x)^4/(1+x^2)", () =>
- {
- var outcome = Parsing.Parse("x^4*(1-x)^4/(1+x^2)");
- if (outcome.Entity is null) return false;
- var result = outcome.Entity.Integrate(MathS.Var("x")).Stringize();
- return Guard.IsDeclined(result);
- }, "declined, though it succeeds once divided out by hand"),
- ("determinant leaves a pivot guard", () =>
- {
- var m = MathS.Matrix(new Entity[,]
- {
- { MathS.Var("a"), MathS.Var("b") },
- { MathS.Var("c"), MathS.Var("d") },
- });
- return m.Determinant is { } det && Analysis.Conditions(det.Simplify()).Count > 0;
- }, "det([[a,b],[c,d]]) carries `provided not a = 0`"),
];
public static int Run(TextWriter output)