Python solution conventions
Implement solve() in the canonical Python solution template; for reference, see
new.py. For the in-source docstrings and comments themselves, follow the
Source documentation conventions.
- Match the schema, not an assumption.
solve(*args: str)receives thetest_cases.jsoninputvalues as positional string arguments, in the order of theinputkeys — parse each with therunnerhelpers as its type needs (runner.parse_intfor ints/power notation,runner.parse_listfor a[1, 2, 3]literal,runner.get_text_filefor thefile_urlinput — pass its projecteuler.net URL and it reads the locally cached copy inresources/). By convention a statement-linked file is always thefile_urlinput; an emptyfile_url("") means the data is embedded in the module as a variable, so branch on it rather than reading. The return annotation is always-> str; return the answer's string form (the harness coerces it back to the recordedanswer's type to compare, so alistanswer muststr()to a literalast.literal_evalround-trips). - Keep the harness scaffolding byte-compatible. The
#!/usr/bin/env python3.14shebang,# -*- coding: utf-8 -*-,from __future__ import annotations, thefrom solver.runners import runnerimport, the@runner.maindecorator onsolve(), and theif __name__ == "__main__": raise SystemExit(solve())block are the project's test harness — leave them exactly as the template provides. Implementsolve()'s body and the module-level helpers above it. - Output contract.
@runner.main(inpackages/solver/solver/runners/runner.py) benchmarkssolve()and prints the final stdout line the harness reads —<runs> <avg_seconds> <result>.solve()only returns the answer string; it must never print that line itself. Gate any diagnostic output behindrunner.show(the global set by--show), and never print aftersolve()returns. - Full type annotations on every signature, parameter, and notable local — mypy is strict.
- Libraries: the Python standard library only, plus
numpyfor numerical work andprimesievefor sieve-based prime generation where warranted. Nothing else. - Build all computation inside
solve()— for a truthful benchmark. Sieves, precomputed tables, and any caching (@functools.cache/@lru_cache, memo dicts) must be built or reset insidesolve()(or in helpers it calls fresh on each call), not at module level. With--runs=Nthe harness times the repeatedsolve()calls; anything built once at import (or a cache that stays warm between runs) is excluded from the first run and free thereafter, so the reported average understates the real cost. Size any sieve bound to the inputs rather than to a fixed, oversizedM. (This mirrors the build-inside-solve()rule inCLAUDE.md; the benchmarking rationale is why both insist on it.) - A different algorithm per index. If a
pNNNN_s*.pyalready exists, the new index must use a genuinely different approach, not a rephrasing of the existing one. - Verify before trusting the result: the
devcase must match first; brute-force cross-check a small input where the problem admits direct simulation (this catches off-by-one, parity, and boundary slips); and benchmark scaling on smaller sizes, then extrapolate before the main run — if the projected runtime is unreasonable (more than a few minutes), revisit the algorithm. If there is nodevcase (only amaincase withanswer: null), these checks carry the full verification weight — do not treat the absence of adevcase as licence to skip them. - Record the answer. Write the computed value into every
nullanswerintest_cases.json, preserving all other fields, and confirm withsolver "eval main".