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

11 KiB

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. 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.