Skip to content

Application infrastructure

Numerisect 0.13.0 adds workspace, search, export, scheduling, caching, adapter, and optional catalogue facilities around the native computation core. Nothing in this document performs mathematics: every value shown was produced by PARI/GP, FLINT/Arb, YAFU, Msieve, GMP-ECM, CADO-NFS, primesieve, or primecount and is only stored, filtered, or reformatted here.

Command-line and API parity

The installed numerisect command reaches every HTTP route without starting a browser or opening a socket. numerisect api drives the same FastAPI application in-process through a minimal ASGI caller, attaching the per-launch session token automatically.

numerisect routes --filter /api/primes        # list matching routes
numerisect --json api post /api/primes/check --data '{"expression":"32416190071","mode":"proven"}'
numerisect --json api get /api/jobs --param limit=5 --param status=completed
numerisect api post /api/batch/import --data-file batch.json

--data takes an inline JSON body, --data-file reads one from disk, and --param KEY=VALUE (repeatable) adds query parameters. A non-2xx response exits with status 2 and prints the server's detail.

Exports

Completed work is exportable in six formats. The engine-produced content is never recomputed; only the container changes.

Format Extension Notes
json .json Full record including every factor
jsonl .jsonl One JSON object per line, job record first
csv .csv RFC 4180, job summary followed by a factor table
markdown .md GitHub-flavoured tables, | escaped
latex .tex tabular environments; report bodies use verbatim
pari .gp Assignable record plus a product check you can rerun in GP
curl --cookie jar "http://127.0.0.1:8765/api/exports/jobs/<job-id>?format=pari"
curl --cookie jar "http://127.0.0.1:8765/api/exports/jobs?format=csv&status=completed"
curl --cookie jar "http://127.0.0.1:8765/api/exports/reports/<file>.txt?format=markdown"

The PARI export is deliberately checkable. Loading it defines N, factors, and exponents, then verifies prod(factors[i]^exponents[i]) == N in GP itself.

Batch import

POST /api/batch/import accepts a TXT, CSV, or JSON document containing decimal integers, inclusive ranges (10..14), safe integer expressions, and polynomial families (n^2+n+41 for n=1..100). Ranges and families are expanded by PARI/GP through numerisect/workspace.gp, never in Python.

Limit Default Environment variable
Expanded items per import 500 NUMERISECT_MAX_BATCH_IMPORT_ITEMS
Range span 10000 NUMERISECT_MAX_BATCH_RANGE_SPAN
Document size 2 MB fixed
Family exponent 64 fixed

Set queue: true to enqueue every expanded value as a factorization job at the requested priority.

Workspaces

A workspace records a name, free-text notes, a list of job identifiers, a list of saved report filenames, and arbitrary interface state. Workspaces are local rows in the existing SQLite database; deleting one never deletes the jobs or reports it references.

POST   /api/workspaces
GET    /api/workspaces
GET    /api/workspaces/{id}
POST   /api/workspaces/{id}
DELETE /api/workspaces/{id}

GET /api/jobs accepts q, status, engine, since, until, sort, order, limit, and offset. q matches the expression, the decimal input, and the stored factor list. Every clause is parameterized SQL; quoting characters in q are treated as data.

Facets

GET /api/jobs/facets takes the same filters and returns how many jobs each status, engine and digit band accounts for within that search. The counts and the result list are built from one shared WHERE clause, so a chip reading "failed 2" means two of the jobs this search matched, not two in the database. A band that matches nothing is absent rather than reported as zero. In the browser the chips are the filter controls: clicking one adds it to the search and runs it again.

Saved searches

GET    /api/searches
POST   /api/searches          {"name": "...", "query": {...}}
DELETE /api/searches/{id}

A saved search holds only the fields GET /api/jobs accepts — q, status, engine, since, until, sort, order — and each is validated on the way in, so a stored search cannot ask for a column that is not sortable or an order that is not asc or desc. Blank values are dropped rather than stored as filters. Saving under an existing name replaces that search and keeps its id and creation time. Deleting one touches no job and no report.

GET /api/reports searches an index of saved report files by q and kind. The index is populated automatically whenever a report is written, through a listener registered on numerisect.outputs; an indexing failure can never prevent a report from being saved.

Result caching

Synchronous prime, number-theory, and zeta responses can be cached by (operation, canonical parameters, engine revision). The engine revision combines the pinned manifest revision with the SHA-256 of the executable and of the .gp program actually on disk, so rebuilding an engine or editing a GP file invalidates the cache.

Responses carry cached: true when served from the cache. Send use_cache: false in any request to bypass it. Errors and inconclusive results are never cached: a payload is rejected if it carries a detail key, an inconclusive verdict, or a truncation flag at any depth.

GET    /api/cache          cache size and hit counts
DELETE /api/cache          clear all, or one operation with ?operation=/api/...
Knob Default Environment variable
Enabled on NUMERISECT_RESULT_CACHE
Maximum rows 2000 NUMERISECT_RESULT_CACHE_MAX_ROWS
Maximum response bytes 2000000 NUMERISECT_RESULT_CACHE_MAX_BYTES

Scheduling, limits, and lifecycle

Jobs carry a priority from −10 to 10. The dispatcher orders queued work by priority first and creation time second, still honouring NUMERISECT_MAX_PARALLEL_JOBS.

GET  /api/queue                       queued and running jobs in dispatch order
POST /api/jobs/{id}/priority          {"priority": 5}
POST /api/jobs/reorder                {"job_ids": [...]}
POST /api/jobs/{id}/pause             SIGSTOP the process group
POST /api/jobs/{id}/resume-paused     SIGCONT the process group

Per-job resource limits are applied to the engine process: cpu_seconds becomes RLIMIT_CPU; Linux enforces memory_mb with RLIMIT_AS; and watchdogs enforce wall_seconds and the aggregate process-group resident-set limit on macOS. Darwin needs the RSS watchdog because its loader reserves a large virtual address map, RLIMIT_DATA prevents PARI/GP from starting at practical values, and RLIMIT_RSS is advisory. Exceeding a limit fails the job with an explicit limit_exceeded reason, and all three requested values are recorded in the reproducibility manifest.

Pause uses SIGSTOP, which every supported engine tolerates because it requires no cooperation from the process. A paused job holds its worker slot and its scratch directory. This is independent of CADO-NFS snapshot resume, which restarts a stopped job from its most recent parameter snapshot.

Engine adapters

numerisect/adapters.py describes each engine declaratively: the command, a version probe, an argument-array template, and output patterns for factors and phases. The built-in engines are registered through the same interface they expose to users.

Additional adapters are read from STATE_DIR/adapters/*.toml. They are declarative only: command templates are argument arrays with a fixed set of named placeholders, and output parsing is regular expressions. No adapter file can execute arbitrary code, interpolate a shell string, or introduce a new placeholder. GET /api/adapters lists what loaded and reports any file that was rejected.

Performance history

GET /api/history/performance groups completed jobs by engine and decimal-digit bucket and reports counts and runtimes. These are local measurements on one machine; they depend on hardware, thread count, and the specific input, and they are not benchmarks of the engines in general.

Optional external catalogues

Numerisect is offline by default and makes no outbound request during any calculation. Catalogue lookups are the only exception, and they require two independent approvals:

  1. the process must start with NUMERISECT_ALLOW_NETWORK=1, and
  2. the individual request must carry confirm_network: true.

Missing either one returns HTTP 403 and sends nothing. Only the query itself leaves the machine: an integer sequence for OEIS, or a single decimal integer for a factor catalogue. No credentials, identifiers, results, or telemetry are ever transmitted. All requests use urllib over https with a timeout and a bounded response size.

GET  /api/catalogues            local catalogue files and the permission state
POST /api/catalogues/oeis       {"terms": ["2","3","5","7"], "confirm_network": true}
POST /api/catalogues/factors    {"expression": "2^101-1", "remote": false}

Building a local catalogue instead of importing one

Nothing is bundled, so a fresh installation searches zero catalogue files. The obvious way to fill that gap would be to ship the Cunningham tables, and this project does not vendor third-party data whose licence and provenance would have to be argued. The engines here can produce the same factorizations and say where each one came from:

python scripts/build_factor_catalogue.py                      # 2,3,5,6,7,10 up to n=48
python scripts/build_factor_catalogue.py --bases 2 --max-exponent 128 --seconds 20

PARI/GP factors each \(b^n \pm 1\), proves every factor prime, and verifies the product against the input; the script only orchestrates and writes the file. Each entry records its expression and the engine version that produced it, so a locally computed entry is distinguishable from an imported one. A number the per-number budget cannot finish is listed under skipped and absent from the entries rather than stored half-factored.

The file lands in catalogues/cunningham.json inside the state directory, which the lookup reads with no network access. Catalogue claims stay labelled unverified wherever they are displayed: a catalogue is a source, not an authority, and Numerisect re-checks divisibility with an engine before using one.

Local catalogue files in STATE_DIR/catalogues (.txt as N = p * q lines, or .json) are read with no network access at all, which is the recommended way to use Cunningham-project tables.

Catalogue results are unverified external claims. Every claimed factor is passed to PARI/GP (ws_verify_factors), which decides divisibility and primality, checks whether the claimed factors multiply to the input, and returns the remaining cofactor. The response reports verified per claim plus product_matches and cofactor. No divisibility or primality decision is made in Python.

Knob Default Environment variable
Network permitted off NUMERISECT_ALLOW_NETWORK
Request timeout 15 s NUMERISECT_NETWORK_TIMEOUT
OEIS endpoint https://oeis.org/search NUMERISECT_OEIS_URL
Factor catalogue endpoint unset NUMERISECT_CATALOGUE_URL
GGNFS siever-directory override unset; automatic discovery is used NUMERISECT_GGNFS_DIR

Client examples and notebooks

examples/ contains working clients for Python, Node, curl, C++ (libcurl), and PARI/GP, each showing the session handshake first. numerisect/client.py is a standard-library-only client used by the Python example:

from numerisect.client import NumerisectClient

with NumerisectClient() as client:
    print(client.check_prime("32416190071")["classification"])
    job = client.factor("8051", engine="pari_trial")
    print(client.wait(job["id"])["factors"])

The same class works unchanged in Jupyter and SageMath against a running server. For headless use with no server at all, numerisect.asgi_client.LocalApiClient drives the application in-process, which is what numerisect api uses.