| # | Solver | Score (huber) |
|---|---|---|
| 1 | steventhornton | 4,412,972,275 |
Click a row to view the submitted code.
Implement the most accurate standard normal cumulative distribution function
you can, in Solidity, over the domain with
, using 18-decimal fixed-point
(PRB-Math SD59x18).
What you submit
A Solidity library that defines the implementation of the _cdf function:
library CDFImpl {
function _cdf(SD59x18 x) internal pure returns (SD59x18) {
// your implementation, for x in [0, b]
}
}
The verifier compiles your file together with a fixed, canonical NormalCDF
contract (which you cannot modify). That contract exposes the public entry point
cdf(SD59x18) and calls your _cdf on the contest domain. Outside the
canonical wrapper supplies the exact tail values and the reflection identity
; those regions are fixed and are not scored.
Inside _cdf you may do anything allowed within the limits (see the
Constraints below): you need not use PRB-Math's math functions
and may drop to raw int256 arithmetic. Only the opcode allowlist, size, and gas
limits constrain you.
Domain and encoding
A ULP (unit in the last place) is the smallest representable step at 18 decimals:
, the integer 1.
- 18-decimal fixed point: a real is the integer .
- Domain: with (integer
8835109788175395786). This is one ULP below , the smallest input whose rounds to at 18 decimals. -
500000000000000000. -
999999999999999999(that is ).
Score: accuracy first (Huber), then gas
The primary metric is accuracy, measured by the Huber loss of the output-ULP error against the correctly rounded reference, over a large random sample of the domain. Per point, the error and its Huber term are:
with knee ULP. The reported score is (lower is better), rounded to the nearest integer. Scores are on the order of to , so the score is never reported or ranked at a finer resolution than 1 ULP: a sub-1-ULP difference is an exact tie (and then gas decides). Huber, rather than plain RMSE, keeps the score robust: errors up to count quadratically (normal least-squares), but the rare deep-tail spikes count only linearly, so a handful of large errors cannot dominate the score.
The sample is drawn in 10 equal-weight bands across the output range: the output is split into 10 equal-width bands, and each band gets the same number of points, drawn uniformly in within the band. Equal weight per band means the upper decile (the deep tail, out to ) counts as much as the lower decile, so the tail is exercised rather than starved. The draw uses a fixed public seed (42), the same for every submission, so your score is deterministic and reproducible.
Submissions are ranked lexicographically on two metrics:
- huber (lower is better): accuracy, and it always wins first.
- gas (lower is better): the worst-case exec gas over the sample, used only to break an exact accuracy tie.
Any accuracy gain, however small, outranks any gas gain. Gas only decides submissions whose accuracy score is exactly equal (for example, the same approximation with and without an accuracy-neutral dead branch: the leaner one ranks higher). A new record must be strictly better on the first metric where it differs; a submission that ties on both metrics does not displace the incumbent, so exact ties go to the earliest submission.
Domain
The contest domain is x in [0, b] (18-decimal fixed point), with
b = 8.835109788175395786(integer8835109788175395786), one ULP below the smallest input whoseΦrounds to1at 18 decimals.Φ(0) = 0.5->500000000000000000, andΦ(b) = 1 - 1e-18->999999999999999999.
Submission format
- A single Solidity source file (not bytecode) defining exactly:
// SPDX-License-Identifier: MIT pragma solidity >=0.8.19; import { SD59x18 } from "@prb/math/src/SD59x18.sol"; library CDFImpl { function _cdf(SD59x18 x) internal pure returns (SD59x18) { // ... } } - You MUST import PRB-Math's
SD59x18(import { SD59x18 } from "@prb/math/src/SD59x18.sol"). You may define your own internal helpers/constants in the same file. No other external imports. - The verifier supplies and deploys the canonical
NormalCDFcontract; you cannot change it. It callsCDFImpl._cdf(x)forxin[0, b].
Compilation (fixed)
Compiled by the verifier with, exactly:
- solc 0.8.33 (solc-js build
0.8.33+commit.64118f21.Emscripten.clang), with--optimize --optimize-runs 200,--evm-version osaka,--via-ir, and--no-cbor-metadata. solc-js is a pure-JS build of that exact compiler, so no external solc binary is needed and the bytecode is reproducible from the pinned npm version. - PRB-Math v4.1.2, installed from npm (
@prb/math@4.1.2).
Validity rules (any failure means invalid)
- Compiles under the pinned toolchain and produces a deployable
NormalCDFwithcdf(int256) pure. - Opcode allowlist: the whole deployed runtime must use only the allowed
opcodes (see the list below). It must be a pure computation: no storage,
block/tx context, external code, gas introspection, logs, calls, contract
creation, or
KECCAK256. Enforced both statically (a sweep of the whole runtime) and dynamically (a small, diverse subset of calls is executed under an opcode-recording tracer, so any banned opcode that executes on those paths is caught even if it was hidden from the static sweep). - Bytecode size at most
512bytes, on the whole deployed runtime. - Endpoints (exact):
cdf(0) == 500000000000000000andcdf(b) == 999999999999999999. - Monotonic: the sampled outputs, sorted by
x, are non-decreasing. True monotonicity over the whole domain is not tested (it is infeasible); the check is on theNsample points sorted byx. - In range: every sampled output lies in
[500000000000000000, 999999999999999999]. - Total:
cdfnever reverts or runs out of gas on a domain input. - Gas: max execution gas per call over the sample at most
1024(gas_used - 21000 - calldata_cost). - No Yul
verbatim: the submission source must not use the Yulverbatimbuiltin (it injects raw bytecode that would bypass the opcode allowlist). Ordinary inlineassembly { }is allowed. - Source size at most
65536bytes.
Opcode allowlist
The canonical list is verifier/opcodes.json. It is
closed-world: anything not listed, including opcodes added by future hardforks, is
rejected. CALLVALUE is allowed because solc emits it (with ISZERO + revert) as
the non-payable guard on every non-payable function.
| Group | Opcodes |
|---|---|
| Halt / return | STOP RETURN REVERT INVALID |
| Arithmetic | ADD MUL SUB DIV SDIV MOD SMOD ADDMOD MULMOD EXP SIGNEXTEND |
| Comparison / bitwise | LT GT SLT SGT EQ ISZERO AND OR XOR NOT BYTE SHL SHR SAR |
| Read calldata | CALLDATALOAD CALLDATASIZE CALLDATACOPY |
| Non-payable guard | CALLVALUE |
| Stack / memory / local flow | POP MLOAD MSTORE MSTORE8 MSIZE MCOPY JUMP JUMPI JUMPDEST PC PUSH0 |
| Push | PUSH1 .. PUSH32 |
| Dup | DUP1 .. DUP16 |
| Swap | SWAP1 .. SWAP16 |
Scoring
- Two ranked metrics, in priority order:
- huber (lower wins) - the Huber loss of the output-ULP error vs the
banker's-rounded reference, reported as
sqrt(mean Huber loss)rounded to the nearest integer (resolution 1 ULP; scores are 1e7 to 1e9, so a sub-1-ULP difference is a tie). Per point,err = |cdf(x) - round_half_even(Phi(x)*1e18)|, and the Huber term iserr^2iferr <= 1e9(the knee), else1e9*(2*err - 1e9). Measured over the seeded, output-banded / uniform-in-x sample ofN = 1,000,000points. Huber keeps the score robust: the rare deep-tail spikes count only linearly, so they cannot dominate. - gas (lower wins) - worst-case exec gas over the sample; a tie-breaker only.
- huber (lower wins) - the Huber loss of the output-ULP error vs the
banker's-rounded reference, reported as
- Ranking is strictly lexicographic: a new record must be strictly better on the first metric where it differs from the current best. Accuracy always decides first; gas only separates submissions with exactly equal accuracy. A submission that ties on both metrics does not displace the incumbent (earliest submission wins). There is no minimum-improvement margin.
- Sampling. The output range
Phi in [0.5, 1]is split into 10 equal-width bands; each band gets an equal share of theNpoints, drawn uniformly inxwithin the band (the band edges aregrid(probit)of the band boundaries, ends pinned to0andb). Equal weight per band means the deep tail (top band, out tox = b) is exercised as much as the head, so no region is starved. - These parameters (
N, the sample seed, the band edges, the Huber knee, the size cap, the gas cap, the source-size cap, the domain, the toolchain pin) are the verifier's frozen spec and live inverifier/config.ts. The verifier is canonical: it IS the spec.
Determinism, the sample seed, and the bytecode hash
- Sample seed (fixed and public). The sample is drawn by a
SHA-256counter-mode PRNG from a fixed public seed, the number42(as a 32-byte value; seeverifier/config.ts,SAMPLE_SEED_HEX), the same for every submission. Scoring is therefore fully deterministic and reproducible, and the fixed seed is what lets gas break an exact accuracy tie (two submissions with the same accuracy logic draw the identical sample, so their accuracy score is exactly equal). - Duplicate rejection (runtime bytecode hash). Independently of the seed, the
dedup key is
SHA-256of your deployed runtime bytecode with the trailing CBOR metadata stripped (the pin includes--no-cbor-metadata, and any trailer is stripped as a fallback). A submission whose runtime bytecode is byte-identical to an earlier one is rejected (it would score identically and lose the earliest-wins tie anyway). Because only real logic changes move the runtime bytecode (comments, whitespace, and formatting do not), cosmetic edits do not dodge dedup.
Compiles the submitted CDFImpl library with a fixed public wrapper under a pinned solc 0.8.33 + PRB-Math toolchain (via-ir), enforces the opcode allowlist (static plus a small-subset execution trace), a bytecode-size cap, a source-size cap, a ban on Yul verbatim, a per-call gas cap, exact endpoints, and monotonicity, then scores the Huber loss of the output-ULP error against a banker's-rounded reference over a seeded, output-banded / uniform-in-x sample.
The verifier is the canonical spec. View verifier source →
steventhornton