From f9a5fbb0ea767db352f6f23995b2e91de65bf6c0 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 4 Aug 2026 19:55:31 +0000 Subject: [PATCH 1/2] Drop MyGet The per-push master feed goes; releases still publish to NuGet, which is the only thing the workflow now does. The feed source comes out of NuGet.Config and the badge table out of the README, so nothing points at a feed that will stop being fed. --- .github/workflows/Nuget.yml | 77 +++++++++++++------------------------ README.md | 12 ------ Sources/NuGet.Config | 1 - 3 files changed, 27 insertions(+), 63 deletions(-) diff --git a/.github/workflows/Nuget.yml b/.github/workflows/Nuget.yml index 95ec524fa..57962370e 100644 --- a/.github/workflows/Nuget.yml +++ b/.github/workflows/Nuget.yml @@ -1,11 +1,6 @@ -name: 'Upload to MyGet and NuGet' - -run-name: ${{ github.event_name == 'push' && 'Upload to MyGet' || 'Upload to NuGet' }} +name: 'Upload to NuGet' on: - push: - branches: - - master release: types: [published] @@ -23,49 +18,31 @@ jobs: - uses: actions/checkout@v6 - - name: 'Determine version and target' + - name: 'Determine version' id: version run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - # MyGet versioning - commithash=$(git rev-parse --short HEAD) - currtime=$(date +%s) - echo "commit hash is $commithash" - echo "time is $currtime" - version=10.0.0-master-$currtime-$commithash - echo "version is $version" - echo "version=$version" >> $GITHUB_OUTPUT - echo "apikey=${{ secrets.MYGET_KEY }}" >> $GITHUB_OUTPUT - echo "source=myget" >> $GITHUB_OUTPUT - echo "target=MyGet" >> $GITHUB_OUTPUT - else - # NuGet versioning from release tag - tagname="${{ github.event.release.tag_name }}" - - # Validate tag name exists - if [[ -z "$tagname" ]]; then - echo "Error: Release tag name is empty" - exit 1 - fi - - # Remove 'v' prefix if present (e.g., v1.0.0 -> 1.0.0) - version="${tagname#v}" - echo "Release tag is $tagname" - echo "Package version is $version" - - # Validate version format (SemVer 2.0.0: X.Y.Z[-prerelease][+build]) - if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then - echo "Error: Invalid version format '$version'." - echo "Expected semantic version (e.g., 1.0.0, 1.0.0-beta.1, 1.0.0+build.123)" - exit 1 - fi - - echo "version=$version" >> $GITHUB_OUTPUT - echo "apikey=${{ secrets.NUGET_KEY }}" >> $GITHUB_OUTPUT - echo "source=nuget.org" >> $GITHUB_OUTPUT - echo "target=NuGet" >> $GITHUB_OUTPUT + tagname="${{ github.event.release.tag_name }}" + + # Validate tag name exists + if [[ -z "$tagname" ]]; then + echo "Error: Release tag name is empty" + exit 1 fi - + + # Remove 'v' prefix if present (e.g., v1.0.0 -> 1.0.0) + version="${tagname#v}" + echo "Release tag is $tagname" + echo "Package version is $version" + + # Validate version format (SemVer 2.0.0: X.Y.Z[-prerelease][+build]) + if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then + echo "Error: Invalid version format '$version'." + echo "Expected semantic version (e.g., 1.0.0, 1.0.0-beta.1, 1.0.0+build.123)" + exit 1 + fi + + echo "version=$version" >> $GITHUB_OUTPUT + - name: 'Pack and publish AngouriMath' run: | cd Sources/AngouriMath @@ -73,7 +50,7 @@ jobs: dotnet build AngouriMath.csproj -c release dotnet pack AngouriMath.csproj -c release -p:PackageVersion=${{ steps.version.outputs.version }} cd bin/release - dotnet nuget push AngouriMath.${{ steps.version.outputs.version }}.nupkg --api-key ${{ steps.version.outputs.apikey }} --source "${{ steps.version.outputs.source }}" + dotnet nuget push AngouriMath.${{ steps.version.outputs.version }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source nuget.org - name: 'Pack and publish AngouriMath.FSharp' run: | @@ -82,7 +59,7 @@ jobs: dotnet build -c Release dotnet pack -c Release -p:PackageVersion=${{ steps.version.outputs.version }} cd bin/Release - dotnet nuget push AngouriMath.FSharp.${{ steps.version.outputs.version }}.nupkg --api-key ${{ steps.version.outputs.apikey }} --source "${{ steps.version.outputs.source }}" + dotnet nuget push AngouriMath.FSharp.${{ steps.version.outputs.version }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source nuget.org - name: 'Pack and publish AngouriMath.Interactive' run: | @@ -91,7 +68,7 @@ jobs: dotnet build -c Release dotnet pack -c Release -p:PackageVersion=${{ steps.version.outputs.version }} cd bin/Release - dotnet nuget push AngouriMath.Interactive.${{ steps.version.outputs.version }}.nupkg --api-key ${{ steps.version.outputs.apikey }} --source "${{ steps.version.outputs.source }}" + dotnet nuget push AngouriMath.Interactive.${{ steps.version.outputs.version }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source nuget.org - name: 'Pack and publish AngouriMath.Terminal' run: | @@ -100,4 +77,4 @@ jobs: dotnet build -c Release dotnet pack -c Release -p:PackageVersion=${{ steps.version.outputs.version }} cd bin/Release - dotnet nuget push AngouriMath.Terminal.${{ steps.version.outputs.version }}.nupkg --api-key ${{ steps.version.outputs.apikey }} --source "${{ steps.version.outputs.source }}" + dotnet nuget push AngouriMath.Terminal.${{ steps.version.outputs.version }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source nuget.org diff --git a/README.md b/README.md index d4199141e..8efeec145 100644 --- a/README.md +++ b/README.md @@ -58,18 +58,6 @@ Note, that all tests and builds are tested for the following three operating sys | Terminal | Nuget (with prereleases) | Nuget | Nuget | | C++ | GitHub release (latest SemVer including pre-releases) | WIP | WIP | -There are also latest-master versions (updated on every push to master) on [MyGet](https://www.myget.org/feed/Packages/angourimath): -| MyGet | Downloads | -|-------|-----------| -| [![MyGet (with prereleases)](https://img.shields.io/myget/angourimath/vpre/AngouriMath?label=AngouriMath)](https://www.myget.org/feed/angourimath/package/nuget/AngouriMath) | ![MyGet](https://img.shields.io/myget/angourimath/dt/AngouriMath?label=Downloads) | -| [![MyGet (with prereleases)](https://img.shields.io/myget/angourimath/vpre/AngouriMath.FSharp?label=AngouriMath.FSharp)](https://www.myget.org/feed/angourimath/package/nuget/AngouriMath.FSharp) | ![MyGet](https://img.shields.io/myget/angourimath/dt/AngouriMath.FSharp?label=Downloads) | -| [![MyGet (with prereleases)](https://img.shields.io/myget/angourimath/vpre/AngouriMath.Interactive?label=AngouriMath.Interactive)](https://www.myget.org/feed/angourimath/package/nuget/AngouriMath.Interactive) | ![MyGet](https://img.shields.io/myget/angourimath/dt/AngouriMath.Interactive?label=Downloads) | -| [![MyGet (with prereleases)](https://img.shields.io/myget/angourimath/vpre/AngouriMath.Terminal?label=AngouriMath.Terminal)](https://www.myget.org/feed/angourimath/package/nuget/AngouriMath.Terminal) | ![MyGet](https://img.shields.io/myget/angourimath/dt/AngouriMath.Terminal?label=Downloads) | - -Source to install from MyGet: -``` -https://www.myget.org/F/angourimath/api/v3/index.json -``` #### Other info | Website | Stars | License | diff --git a/Sources/NuGet.Config b/Sources/NuGet.Config index 55b308200..f115653fa 100644 --- a/Sources/NuGet.Config +++ b/Sources/NuGet.Config @@ -6,6 +6,5 @@ - \ No newline at end of file From 8d0cad881404eb09a17cb2aad60ee87981b1cc3b Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 4 Aug 2026 19:56:39 +0000 Subject: [PATCH 2/2] Add AGENTS.md What an agent working here needs that CONTRIBUTING.md does not say: that mathematical correctness outranks backward compatibility, that consistency is the defect #497 names, that an unevaluated answer and a NaN are different claims and only one of them is honest about failure, and that the mathematics rather than the printed string is what a test should assert. Also where to look things up, since these algorithms are decades of literature deep and the good ones have names, and where the work is. --- AGENTS.md | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..d085ada98 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,158 @@ +# AGENTS.md + +For AI agents working on AngouriMath. Humans: [CONTRIBUTING.md](CONTRIBUTING.md) is yours, and +everything below applies to you too. + +AngouriMath is a computer algebra system. The thing being built is *mathematics*, and the code is +how it is expressed. Read this as instructions for doing mathematics well, using C# and F#. + +## The one rule everything else follows from + +**Be a mathematician first.** When mathematical correctness and backward compatibility disagree, +correctness wins. A published API that returns the wrong answer is not an asset to preserve; it is +a bug with users. Say so in the changelog, and change it. + +The same goes for convention. If mathematicians write it one way and the library writes it another, +the library is wrong — even where the library's way is defensible in isolation. `arcsinh` is not a +thing; the inverse of `sinh` is an *area*, not an arc, so it is `arsinh` (#687). Follow the notation +of the people who use the subject, not the notation that was easiest to parse. + +**Consistency is the point.** [#497](https://github.com/asc-community/AngouriMath/issues/497), the +2.0 paper, names inconsistency as the central defect: *"one may find it inconsistent in a lot of +places in API, behaviour, and internal structure of code."* A rule that fires for `sin` but not +`tan`, a limit that works from both sides but not from one, an evaluation that holds precision for +large numbers and drops it for small — each is a bug even when every individual case is defensible. +When you fix something, ask what else is the same shape, and fix that too, or write down why not. + +## Not answering is a legitimate answer. Answering wrongly is not. + +The most important distinction in this codebase: + +| result | means | +|---|---| +| unevaluated (`Limitf`, the original expression back) | "I could not settle this" | +| `NaN` | "**this does not exist**" | +| a value | "this is the value" | + +These are three different claims and they are not interchangeable. Returning `NaN` for a limit you +merely failed to compute is a *wrong answer*, not a graceful failure — it tells the caller the limit +does not exist. Returning an unevaluated node is honest. + +So, in order of preference: right answer > no answer > slow answer > wrong answer. A wrong answer is +worse than a hang, because a hang is visible. + +## Verify the mathematics, not the string + +Never assert on printed form unless the printed form *is* the bug. Assert the property: + +- an integral: differentiate it back and compare to the integrand +- a root: substitute it into the equation +- an identity: subtract the two sides and simplify to zero +- a limit: check it against the value at nearby points, or a series + +A string comparison passes for `2*x` and fails for `x*2`, which tells you nothing about whether the +answer is right. `Sources/Tests/UnitTests/Common/IssueRegressionTest.cs` is where issue regressions +go, named for the issue. + +**Reproduce on a stock `master` build before claiming anything is broken.** More than one confident +report here has turned out to describe behaviour that was never broken. + +## Aim high + +The ambition is to answer the hard problems: olympiad and competition algebra, calculus and number +theory; the integrals and limits that need real machinery rather than pattern tables; the questions +where the honest current answer is "unevaluated". Concretely, that means things like Risch for +integration, a proper polynomial layer (multivariate GCD, resultants, factorisation) which most of +the open simplification issues sit behind, and quantifier elimination for inequalities. + +Measure against a corpus of problems with known answers, and report **wrong / error / timeout** +counts alongside the solved count. A change that solves one more problem and introduces one wrong +answer is a regression. Compare against SymPy, Mathematica, or a textbook — being different from +SymPy is not automatically being wrong, but it is always worth explaining. + +## Keep up with the mathematics + +Algorithms here are decades of literature deep, and the good ones are written down. Before inventing +a procedure, find out whether it has a name: + +- **SymPy** (`sympy/`) — readable reference implementations of Risch, Gruntz, Gröbner, and much else, + with the papers cited in the docstrings. The single most useful cross-check. +- **Mathematica / Wolfram Alpha, Maxima, SageMath** — for deciding what the right answer *is*. +- **DLMF** (dlmf.nist.gov) — the authority on special-function identities and branch cuts. +- **OEIS** — for recognising an integer sequence. +- **arXiv math.AC / cs.SC**, the *Journal of Symbolic Computation*, and the ISSAC proceedings — where + new symbolic-computation algorithms appear. +- **Gruntz's thesis** for limits, **Bronstein's _Symbolic Integration I_** for integration, + **_Modern Computer Algebra_** (von zur Gathen & Gerhard) for the polynomial layer. + +Branch cuts deserve a specific warning: `arcsin`, `log`, and fractional powers disagree between +conventions, and C99, .NET, Python and Mathematica do not all agree. Decide deliberately, cite the +convention, and test the disagreeing points. + +## Working practice + +**Branch first, always.** Never commit a feature or a fix straight to `master`, even with write +access. One branch per change, each independently mergeable, cut from `master`. + +``` +fix/ fix/vanishing-denominator +feat/ feat/gruntz +perf/ perf/limit-memoisation +docs/ docs/agents-md +chore/ chore/drop-myget +``` + +Then: + +1. Write the failing test first, and check it fails for the reason you think. +2. Fix it. Prefer the smaller change with the smaller blast radius. +3. Run everything: `dotnet test Sources/Tests/UnitTests`, + `dotnet test Sources/Tests/FSharpWrapperUnitTests`. +4. If an existing test now fails, decide honestly which it is — the answer got better, the test was + pinning a fudge, or you broke something — and say which in the commit message. Never loosen an + assertion without writing down why. +5. Open a PR. State what was wrong, why the fix is right, and what you measured. + +`TreatWarningsAsErrors` is on and there are custom analyzers; a static field needs +`[ConstantField]`, `[ThreadStatic]` or `[ConcurrentField]`. + +## Write for the reader, briefly + +Comments explain **why**, not what — the code says what. The reader is a mathematician six months +from now wondering whether a line can be deleted. Tell them what breaks if it is. + +```csharp +// (x - a)^k is positive on the right whatever k is, and on the left takes the sign of +// (-1)^k, so approaching from the left at an odd order turns the sign around. +``` + +Be concise. Do not narrate your own process, and **do not record your mistakes, wrong turns or +retracted diagnoses in code, commit messages or documentation.** Those belong in the issue tracker, +where they are searchable and where someone hitting the same wall will find them. What belongs in +the code is the conclusion and the reason it holds. A measurement that justifies a constant is a +reason; a story about how you arrived at it is not. + +Cite issues by full URL in code comments (`https://github.com/asc-community/AngouriMath/issues/557`), +since a bare `#557` means nothing outside GitHub. `#557` is fine in PR titles and bodies. + +## Where the work is + +Good entry points, roughly by depth: + +- **Simplification consistency** — [#531](https://github.com/asc-community/AngouriMath/issues/531), + [#557](https://github.com/asc-community/AngouriMath/issues/557), + [#415](https://github.com/asc-community/AngouriMath/issues/415). Rules that fire in one arrangement + and not another. +- **Limits** — [#596](https://github.com/asc-community/AngouriMath/issues/596), + [#536](https://github.com/asc-community/AngouriMath/issues/536). `0^0` and `oo^0` have no rule + where `1^oo` has one; piecewise has none at all. +- **Numerics** — [#602](https://github.com/asc-community/AngouriMath/issues/602). Precision that + holds at one scale and not another. +- **Solving** — [#629](https://github.com/asc-community/AngouriMath/issues/629), + [#475](https://github.com/asc-community/AngouriMath/issues/475), + [#381](https://github.com/asc-community/AngouriMath/issues/381). Diophantine equations and + characteristic polynomials both want the polynomial layer. +- **The polynomial layer itself** — multivariate GCD, resultants, factorisation. Large, and most of + the above sits behind it. +- **[#497](https://github.com/asc-community/AngouriMath/issues/497), AngouriMath 2.0** — the design + paper. If you are proposing something structural, propose it there.