Files
even-perfect-numbers-scanner/AGENTS.md
T
2025-10-19 07:24:00 +02:00

158 lines
11 KiB
Markdown

# Repository Guidelines
- Only solution: `EvenPerfectScanner.sln` (console app scanning numeric ranges).
- Keep changes confined to the relevant solution/project unless instructed otherwise.
- Assume each solution has or will have its own tests and build configuration; avoid cross-project edits unless required.
## Build and Test
- Build: `dotnet build EvenPerfectScanner.sln`.
- Run only tests related to the modified solution.
- Run tests without `--no-build`.
- Write tests with xUnit + FluentAssertions.
- Confirm test runtimes are reasonable; skip suites expected to run excessively long.
- Always apply timeout guards and `Category=Fast` filters when running tests.
## File Editing
- Do not use `apply_patch.cs` until TODOs lift the ban; edit files with shell tools and stage via git.
- Intended usage (once re-enabled): `dotnet script apply_patch.cs -- <patch-file> [target-directory]`.
- Expect the script to run in local/remote Codex environments on Windows and Linux.
- Supported options (when allowed):
- `--check`
- `--ignore-whitespace`
- `--ignore-eol`
- `--ignore-line-endings`
- `--target <root>`
- `--profile`
- `--replace-line <path> <line> <expected> <new>` (use `_` for expected to skip validation)
- Build patches in standard `git diff` format:
- Include `diff --git`, `---`, `+++`, and `@@` headers with numeric ranges.
- Exclude `***` or `*** Begin/End Patch` wrappers.
- Run `--check` before applying patches; apply only after a successful check.
- Keep patches small, precise, and regenerated after each edit; confirm target lines with `Get-Content` or `nl -ba`.
- Record exact start lines for each hunk and double-check with `nl -ba`.
- Prefer `--replace-line` for single-line edits after capturing the target line number.
- Regenerate diffs after successful patch applications and whenever files shift.
- Use `--ignore-whitespace` / `--ignore-line-endings` only when validation noise blocks `--check`.
- Maintain unique patch filenames and delete applied ones.
- Investigate failed hunks immediately and rebuild diffs instead of retrying stale context.
- Prefer single-purpose patches.
- Use `nl -ba` to show line numbers when needed.
- Use four spaces for code block indentation.
- Create patches from the repo root with relative paths.
- Do not apply changes manually or via other scripts unless modifying `apply_patch.cs`.
- If `apply_patch.cs` has issues or lacks features, fix or enhance it and continue using it.
## Test Execution Time Policy
- Estimate runtimes before launching tests or samples.
- Do not run tests expected to exceed 2 minutes unless explicitly authorized.
- Never impose the two-minute guard on benchmarks or profiling runs; let them finish unless obviously stuck.
- Keep timeouts external to the test code.
- Use targeted filters such as `--filter FullyQualifiedName~<ClassOrMethod>` or `--filter "FullyQualifiedName=Namespace.Class.Method"`.
- Keep GPU-heavy tests minimal; skip kernels needing >2 minutes for compile/warmup.
- In local Codex tasks, run only fast, newly added tests and list recommended existing tests instead.
### 120-second Guard Examples
- Windows (PowerShell):
- `$proj = "<path-to>.csproj"`
- `$filter = 'FullyQualifiedName~SomeTests'`
- `$job = Start-Job -ScriptBlock { param($p,$f) dotnet test $p -c Debug --filter $f } -ArgumentList $proj,$filter`
- `if (-not (Wait-Job $job -Timeout 120)) { Stop-Job $job; throw 'Test run exceeded 120s timeout.' }`
- `Receive-Job $job`
- Linux/macOS (bash):
- `timeout 120s dotnet test <path-to>.csproj -c Debug --filter "Category=Fast&FullyQualifiedName~SomeTests"`
- Always specify `timeout 120s` (or shorter) and include `Category=Fast` in the filter.
- Treat exit code 124 as a timeout.
- Codex/agent runs: wrap `dotnet test` commands in shell guards above.
- Filter examples: `--filter "FullyQualifiedName~PrimeTesterGcdTests"`, `--filter "FullyQualifiedName=EvenPerfectBitScanner.Tests.ResultsFileNameTests.BuildResultsFileName_contains_the_range"`.
## Coding Standards
- Use four-space indentation; increase one level per nested block.
- Indent wrapped parameters/expressions by two indentation units.
- Reuse existing variables when prior values are obsolete; call out meaning changes in comments to keep intent clear and register pressure low.
- Avoid try/finally blocks solely for resource release unless omitting finally harms clarity or maintainability; ensure every path releases resources, but consider only successful execution. In case of errors the program must crash.
- Limit the divisor cycle cache to one in-memory block plus the on-disk snapshot; recompute on misses, discard transient results, and avoid background generation or extra caches.
- Write code, comments, commit messages, branch names, and PR descriptions in American English.
- Preserve relevant TODO notes and explanatory comments unless invalid.
- Prefer structs/classes with public fields over auto-properties for hot-path data holders.
- Pass mutable structs as parameters with the `in` modifier whenever the call site and usage allow it (skip when constraints such as lambda captures require a copy).
- Omit the `in` modifier when passing immutable structs as parameters.
- Remove or extend trivial wrapper methods that only call another method.
- Use ThreadStaticPools instead of ArrayPool<T>. Add missing pieces to it, if needed.
## Performance Checklist
- Favor dedicated kernels over flag-controlled/parameterized ones; duplicate code when it improves speed.
- Remove `if` statements inside hot loops when possible.
- Replace runtime flags in hot paths with dedicated method variants.
- Reuse variables instead of allocating new ones; document meaning changes.
- Extract functionality into separate classes and place reusable parts in `*.Core` projects.
- Rent arrays from `ArrayPool<T>` when stack allocation is impractical and return them after use.
- Provide cancellation-aware and cancellation-free variants of hot methods; handle cancellation in outer scopes.
- Prefer `for` loops over `foreach` to avoid enumerator allocations.
- Declare loop control variables outside loops and reuse them.
- Cache arrays, static/read-only arrays, and collection counts in locals before loops.
- Declare variables together outside loops when feasible and reuse them.
- Avoid repeated casting within loops by precomputing or using wider variables.
## GPU Notes
- ILGPU 1.5.3 checks: `%` and integer division supported for 32-bit/64-bit integers; `UInt128` remains unsupported inside kernels. Use the `GpuUInt128` struct for GPU-side emulation and extend it whenever required arithmetic helpers are missing.
- Arithmetic in GPU kernels covers only 32-bit and 64-bit primitives; rely on `GpuUInt128` together with its read-only counterpart `ReadOnlyGpuUInt128` to emulate 128-bit behavior.
- CPU code should default to `UInt128` instead of `GpuUInt128` unless the same calculation executes millions or billions of times, in which case prefer mutable `GpuUInt128` when benchmarks show no material regression; benchmark summaries and recommendations live in `Benchmarks.md`.
- GPU kernels should favor mutable `GpuUInt128` and `ReadOnlyGpuUInt128` when the necessary operations exist on the accelerator and benchmarks do not show a dramatic advantage for `UInt128`.
- Perform conversions from `GpuUInt128` or other structs to `ReadOnlyGpuUInt128` outside hot loops whenever possible; push the conversion toward the top of the call chain to avoid repeated costs.
- For each numeric type, review available helpers in `UIntExtensions`, `ULongExtensions`, `UInt128Extensions`, and `GpuUInt128`; add new operations to the extension class or struct that matches the type.
- Arrays/vectors of `bool` unsupported; use `byte`.
- `UInt128` multiplication, division, bit shifts unsupported in kernels; implement manually.
- GPU context pooling supported and recommended.
- Numeric probe results (ILGPU 1.5.3, CPU accelerator):
- `System.Numerics.BigInteger`: unsupported (`InternalCompilerException`).
- `PeterO.Numbers.EInteger`: unsupported (`InternalCompilerException`).
- `PeterO.Numbers.ERational`: unsupported (`InternalCompilerException`, lacks remainder API).
- Prefer XMath in GPU calculations over Math or BitOperations.
## Patch Preparation Reminders
- Manually review every patch before `--check`; remove unintended whitespace noise.
- Capture context with `nl -ba` before composing each hunk; include ≥3 context lines when available.
- Normalize trailing whitespace early or rely on ignore flags temporarily, then rerun without ignores to confirm the final diff.
- Regenerate patches from scratch after working tree changes; avoid editing old diffs.
- Note that the parser ignores blank metadata lines between headers/hunks; keep headers compact unless tools add blanks.
## apply_patch.cs Profiling Snapshot (2025-10-04)
- `dotnet script apply_patch.cs -- --profile --check /tmp/agents_todo_update.diff`.
- Sample patch: 2 files, 4 hunks, 31 lines, 3468 bytes.
- Timings: read 0.45 ms, parse 2.96 ms, apply 2.96 ms.
- Next focus: stream the patch reader and reuse pooled buffers to reduce parse/apply cost.
## Mersenne Divisor Guidance
- Prime divisor `q` of `2^p - 1` satisfies `q ≡ 1 (mod 2p)` with minimal `q = 2p + 1` for `k = 1`.
- Target for at least `p` in `[138,000,000, 200,000,000]`, minimal `q < 400,000,001` (29 bits).
- Target for at least `p = 600,000,000`, minimal `q ≤ 1,200,000,001` (31 bits).
- Larger `k` values can grow `q` up to nearly `2^p - 1`; plan heuristics/storage for wider integers even though the noted bit widths cover the first admissible divisors.
- Worst-case divisor width: `p - 1` bits (`⌊(2^p - 1) / 2⌋ = 2^{p-1} - 1`).
- `k_max ≈ 2^{p-2} / p` for large `p`.
## Practical Kernel Guidance
- Use native `%` and `/` for 32-bit/64-bit integers when efficient.
- For `UInt128` in kernels, avoid `%`/`/`; prefer Montgomery/Barrett or Mersenne folding.
- Prefer Mersenne folding for `2^p - 1` moduli to remove branches in hot loops.
## Heuristic Multiply-Shift Benchmarks (2025-10-04)
- Run `dotnet run -c Release --project tools/HeuristicMultiplyShiftTiming/HeuristicMultiplyShiftTiming.csproj` to compare `ULongExtensions` helpers without the BenchmarkDotNet harness.
- Average ns per call over 200,000,000 iterations (identical checksums):
- `NearOverflowShift`: `MultiplyShiftRight` ≈ 2.37 ns, shift-first ≈ 4.71 ns, naive ≈ 0.85 ns.
- `HalfRange`: `MultiplyShiftRight` ≈ 2.11 ns, shift-first ≈ 4.15 ns, naive ≈ 0.70 ns.
- `MixedBits`: `MultiplyShiftRight` ≈ 1.43 ns, shift-first ≈ 2.83 ns, naive ≈ 0.60 ns.
- `Sparse`: `MultiplyShiftRight` ≈ 1.57 ns, shift-first ≈ 2.84 ns, naive ≈ 0.59 ns.
- Shift-first doubles CPU cost but stays within 64-bit arithmetic and is the GPU fallback when avoiding `UInt128` overflow.