Skip to content

Formally verified expRayToWad - #593

Open
duncancmt wants to merge 153 commits into
dcmt/formal-nopythonfrom
dcmt/exp
Open

Formally verified expRayToWad#593
duncancmt wants to merge 153 commits into
dcmt/formal-nopythonfrom
dcmt/exp

Conversation

@duncancmt

Copy link
Copy Markdown
Collaborator

No description provided.

duncancmt and others added 30 commits June 28, 2026 11:20
Computes floor(10**18 * exp(x / 10**27)) or one less (never overestimating) for x
up to the octave k = round(x / (10**27 * ln2)) = 63, reverting Panic(0x11) above
that. The result is monotone, equals 10**18 exactly at x == 0, and is 0 for x at or
below floor(10**27 * ln(10**-18)). On the central octave it is tight, so
expRayToWad(lnWadToRay(w)) == w - 1 for w/10**18 in [1/sqrt2, sqrt2) (== w at the
scale point), letting a consumer in that regime recover w by adding one.

The kernel reduces x = k*ln2 + t and evaluates the reciprocal-symmetric rational
exp(t) = (Ev(t^2) + t*Od(t^2)) / (Ev(t^2) - t*Od(t^2)) -- Od degree 4, Ev degree 5
and monic -- on a mixed fixed-point staircase, then floors with the 2**k scaling
folded into the closing shift.

test/0.8.34/Exp.t.sol checks it differentially against a 120-digit mpmath oracle
over FFI, plus the lnWadToRay round trip, monotonicity, the scale point, the
over-range revert, and underflow to zero.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document putting literal operands on the left of commutative ops (using the
operand-flipped opcode when only one variant is commutative), the zero-arm-first
form for boolean Yul switches, and the prohibition on pkill/killall (target a
specific PID instead).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rework the reduction t = x/10^27 - k*ln2: K27*x now takes a single
sar(107, ...) to Q128 and subtracts k*floor(ln2*2^128), replacing the Q135
form (sar(100, ...), subtract k*round(ln2*2^135), then a second sar(7, ...)).
One fewer SAR and a 16-byte ln2 word instead of 17 bytes.

Correct the documented magnitude of E at the revert threshold: ~1.30*10^37,
not 10^19.

Add testFuzzExpRayToWadCentralExact, a direct oracle check over the central
reduced-argument band.

Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mate

The 2^62 margin does not cover the upward bias of the Q128 range
reduction (floor(ln2*2^128) under-subtracts k*ln2), so expRayToWad returned
floor(E)+1 at k=62,63 -- violating the never-overestimate contract. Raise the
one-sided margin to 0x594b01498486da8f, the least integer that covers the
analytic worst-case overshoot S = 0.6976014718030033189 ulp, i.e.
ceil(2^63*S) at the supported edge k=63. Both guarantees then close:
A <= E (never over) and E-A < 1 (at most one ulp low).

Document the exact per-source error budget and a monotonicity argument
in the kernel comment, mirroring Ln.sol. Scrub comment references to
identifiers absent from the code and correct the rational's stated
precision (~135-bit approximation order, ~126-bit integer realization).

Tests: raise the differential and central-exact FFI fuzz to 10000 runs;
add deterministic octave-boundary monotonicity coverage and a high-k
never-overestimate regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Subtract k*ln2 from K27*x at the Q235 product basis using a single 30-byte
ln2 word, then one sar to Q128. This is the same op count (2 MUL, 1 SAR,
1 SUB) and runtime gas as the Q128 reduction -- only the ln2 constant
widens (16 -> 30 bytes). The reduction's upward bias is negligible at this
basis (~2.3e-6 ulp), so the analytic worst-case overshoot is
S = 0.0858862987232991853 ulp and the one-sided margin its analytic
minimum 0xafe527e18748a8a = ceil(2^63*S). Never-over and at-most-one-under
still close; the error-budget comment is updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add ExpWrapper.sol (selector 0x4187462b), an exp kind in formal/yul/YulImporter.lean
and generate_from_forge.sh, and the formal/exp/ExpProof lake package mirroring
formal/ln/LnProof. The generated ExpYul{,Runtime,Proof}.lean (gitignored) compile:
run_exp_ray_to_wad_evm is the EVMYulLean interpretation of the compiled Yul, with
the function chain external_fun_wrap_expRayToWad_99 -> fun_wrap_expRayToWad_99 ->
fun_expRayToWad_70 (overflow guard) -> fun__expRayToWad_80 (kernel), plus fun_panic_8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Direct on the EVMYulLean run_exp_ray_to_wad_evm interpretation (no hand model):
- Seam/RuntimeShared.lean: u256 bounds, wordNat_sar/sdiv bridges, u256 idempotence
  (contract-agnostic; reused by both revert and the kernel arithmetic).
- Seam/Guard.lean: slt(x, 0x8e383a2cdfa1b74a9422d2e1) = 0 for signed x at/above the
  threshold (the overflow-guard comparison; also bounds the in-range domain).
- Seam/Revert.lean: primCall_revert_yul, the zero_value helper direct, and
  call_fun_panic_8_revert_direct (mstore;mstore;revert reduces to .error .Revert).
All build and are axiom-clean ([propext, Classical.choice, Quot.sound]). This validates
per-function interpreter reduction and revert mechanics on the exp contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds Seam/Helpers.lean (the solc ABI/cleanup-plumbing per-function directs,
shared with the value path) and call_fun_expRayToWad_70_revert_direct in
Seam/Revert.lean: for 0x8e383a2cdfa1b74a9422d2e1 <= u256 x < 2^255 the overflow
guard fires and the function reverts via fun_panic_8(ARITHMETIC_OVERFLOW). This
is the core supported-range revert logic. Axioms: [propext, Classical.choice,
Quot.sound].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reformulate call_fun_expRayToWad_70_revert_direct to the (fuel + (extra + N))
slack form so callers can feed it inline, and add
call_fun_wrap_expRayToWad_revert_direct (fun_wrap_expRayToWad_99 forwards to
fun_expRayToWad_70). Completes the function-level supported-range revert chain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds Seam/Dispatcher.lean (branch-agnostic dispatcher/calldata seam for ExpYul:
expSharedAfterFreePtr, the ABI decode chain, shift_right_224, the 0x4187462b
selector switch-case, and the contract-polymorphic runContract_revert_of_exec_revert)
and the revert-specific external/dispatcher reductions in Seam/Revert.lean,
yielding the top-level

  run_exp_ray_to_wad_evm_revert :
    0x8e383a2cdfa1b74a9422d2e1 <= u256 x -> u256 x < 2^255 ->
    run_exp_ray_to_wad_evm x = .error "revert"

proven directly on the EVMYulLean interpretation of the solc/forge Yul, with no
new models. Axioms: [propext, Classical.choice, Quot.sound].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Theorems.lean restates the supported-range revert theorem (run_exp_ray_to_wad_evm_revert) and runs the
axiom gate (#guard_msgs in #print axioms) pinning it to {propext, Classical.choice,
Quot.sound}; the root ExpProof.lean imports it so the default lake build covers the
whole proof and a stray axiom/sorry breaks the build. exp-formal.yml mirrors
ln-formal.yml: regenerate the EVMYulLean artifacts from the compiled ExpWrapper Yul
IR, then build the ExpProof package.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Seam/Value.lean reduces fun__expRayToWad_80 / fun_expRayToWad_70 / fun_wrap_expRayToWad_99
at x=0 to 10^18: at the scale point every mul-by-x vanishes (k=t=v=0), the rational
form is 2^126, and the iszero(0)=1 fix-up lands the result exactly at the wad unit.
The overflow guard iszero(slt(0,threshold))=0 is concrete, so the panic branch is
skipped without needing a general wordNat_slt. These are the function-level core of
zero-input exactness; the ABI .ok dispatcher wrapping is next. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… path

Adds call_allocate_unbounded_direct and the two abi_encode directs (Nat-indexed
value) to Seam/Dispatcher.lean, reusable by the value-path ABI return. The helper
lemmas cover the allocation and ABI return encoding sub-calls used by the
scale-point value-path reduction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes the value path at the scale point x=0 through the ABI .ok dispatcher:

  run_exp_ray_to_wad_evm_zero : run_exp_ray_to_wad_evm 0 = .ok 1000000000000000000

At x=0 the kernel collapses to concrete arithmetic (k=t=v=0, rational form 2^126)
and the iszero(0)=1 fix-up lands exactly 10^18; the guard is skipped (concrete
slt), and the external entrypoint ABI-encodes and returns it. Added to the
Theorems.lean axiom gate. Axioms: [propext, Classical.choice, Quot.sound].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Adds wordNat_slt (wordNat (UInt256.slt a b) = evmSlt (wordNat a) (wordNat b)) and
the evmSlt_u256 absorbers to Seam/RuntimeShared.lean. evmSlt already existed in
FormalYul/Word.lean; this is the missing wordNat-preservation bridge, proven via a
4-way sign case-split on the excess-2^255 offset comparison. Prerequisite for the
symbolic-x kernel reduction used by the runtime floor and monotonicity claims. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
call_fun__expRayToWad_80_direct reduces fun__expRayToWad_80(x) to the inline
let-shared evm* arithmetic tree (transcribed from Exp.sol _expRayToWad, no hand
model) via eq_of_wordNat_eq + the wordNat_*/evm*_u256 bridges (incl. the new
wordNat_slt). The foundation for the runtime floor and monotonicity claims. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
slt(x, threshold) = 1 for signed x < threshold (either negative, or nonnegative
below the threshold) — the complement of slt_thresh_ge. Lets the value-path
fun_expRayToWad_70 reduction skip the panic branch. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ic x)

call_fun_expRayToWad_70_direct (guard skipped via slt_thresh_lt) and
call_fun_wrap_expRayToWad_direct forward the kernel evm* tree for any signed
input below the threshold (u256 x < 0x8e3...2e1 or 2^255 <= u256 x). Completes the
function-level value path (kernel -> 70 -> wrap), all returning the inline tree.
Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
run_exp_ray_to_wad_evm_eq_tree : for signed x below the threshold,
run_exp_ray_to_wad_evm x = .ok <TREE x> (the inline evm* arithmetic tree).
Value-preserving mirror of run_exp_ray_to_wad_evm_zero through the ABI dispatcher,
threading hval into the wrapper. Adds the generic RuntimeShared.toNat_ofNat_evmAdd
helper. In the Theorems.lean axiom gate. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rdMono)

Ports the contract-agnostic int256 floor/division/shift transports the exp
monotonicity argument needs into the ExpYul namespace: a general evmSar floor
sandwich (arbitrary shift), evmSdiv sign-pinned transports, evmShr/evmShl
in-range identities, cross-multiplied division monotonicity, and Int
multiplication-monotonicity helpers. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Mono/RunBridge)

Captures <TREE x> as thin layered defs (kTree/tTree/vTree/evTree/odTree/todTree/
r0Tree/r1Tree/expTree) so the deep Horner accumulator is never forced into whnf
(which overflows the kernel C stack). Proves run_exp_ray_to_wad_evm x = .ok
(expTree x) on the supported domain, and the signed-comparison characterisation
of the evmSlt clamp. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reduces int256 (expTree ·) monotonicity to the meaningful region: below the
clamp boundary C (a negative signed value) expTree x = 0 (clamp off, pin cannot
fire); above it the decode is [x=0] + r1Tree x. Proved via an abstract body word
so the kernel never reduces the Horner accumulator. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundles the analytic obligations as RegionMonotonicityFacts (r1Tree in range, nonnegative,
nondecreasing, and the scale-point pin clearance) and derives expTree_mono over
the whole supported domain by casing on the clamp boundary. Lifts it to the run
level as run_exp_ray_to_wad_evm_mono, modulo the analytic core. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Transports the rounding-shift argument 2^199 + CINV*x to Int (no overflow on the
meaningful region, |int256 x| < 2^96), establishes the k floor sandwich, and
proves k = round(x/(10^27 ln2)) is nondecreasing in the signed input.
Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the octave-index bound (-61 <= k <= 63 on the region) and transports the
reduced-argument shift K27*x - LN2*k to Int (products stay below 2^255).
Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the t floor sandwich and proves t is nondecreasing in the signed input
within a fixed octave (K27 > 0, floor of an increasing affine map). Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wires the Mono layer into the package and adds run_exp_ray_to_wad_evm_mono to the
Theorems signpost with its axiom gate (axioms = the three standard ones). The
theorem holds over the whole supported domain given the meaningful-region core
RegionMonotonicityFacts; the clamp/pin shell, run-level bridge, and octave-index /
reduced-argument transports and monotonicity are unconditional.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port ln's polynomial-nonnegativity certificate checker (Foundation/Poly.lean,
namespace ExpPoly) into ExpProof, and add the Horner-stage Int transports
(Mono/Stages.lean):

- the reduced-argument bound |int256 (tTree x)| < 2^127 (from the coupled
  k-/t-octave sandwiches);
- v = t² in Q128 as a Nat, < 2^126;
- two-sided bounds on the even/odd Horner accumulators (0x4e14… ≤ ev < 2^127,
  0x270a… ≤ od < 2^126) via a chained no-overflow stage bound.

All axiom-clean ([propext, Classical.choice, Quot.sound]).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mono/Quot.lean assembles the reciprocal-symmetric quotient from the Horner
stage bounds, all via abstract opaque-word helpers so the deep Horner tree is
never forced into whnf:

- tod = t*Od in Q87 transported to Int, |tod| < 2^125;
- num = ev+tod and den = ev-tod are strictly positive;
- r0 = sdiv(2^126*num, den) satisfies 1 <= r0 < 2^128.

These supply the range/nonneg obligations of RegionMonotonicityFacts. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Mono/RangeNonneg.lean closes two of the four RegionMonotonicityFacts fields:

- range: r1Tree x < 2^254 (the closing shift s = 126-k in [63,187] floors the
  shift argument WAD*r0-MARGIN < 2^188 down below 2^125 < 2^254);
- nonneg: 0 <= (r1Tree x : Int) (trivial Nat cast), plus the signed
  int256 (r1Tree x) >= 0 used by the floor argument.

Both via abstract opaque-word helpers (closing_shift, shiftArg_bounds_of,
closingSar_facts) so the deep Horner tree is never forced. Adds r0Tree_lt to
Tree.lean. Axiom-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
@duncancmt
duncancmt requested a review from e1Ru1o July 2, 2026 16:52
@duncancmt duncancmt self-assigned this Jul 2, 2026
@duncancmt
duncancmt requested a review from dekz as a code owner July 2, 2026 16:52
@immunefi-magnus

Copy link
Copy Markdown

🛡️ Immunefi PR Reviews

We noticed that your project isn't set up for automatic code reviews. If you'd like this PR reviewed by the Immunefi team, you can request it manually using the link below:

🔗 Send this PR in for review

Once submitted, we'll take care of assigning a reviewer and follow up here.

duncancmt and others added 22 commits July 2, 2026 19:21
forge inspect ... ir trusts whatever artifact the compile cache serves for
the target; an artifact compiled without the ir extra output makes it fail
with "IR output missing from artifact" instead of recompiling. The script's
methodIdentifiers inspection compiles such artifacts, so the second generate
in a job -- the LnWrapper step of exp-formal.yml, whose sources share
Panic.sol with the ExpWrapper step before it -- hit that failure. Setting
FOUNDRY_EXTRA_OUTPUT='["ir"]' on every invocation keeps the cache's
artifact settings uniform across both inspections and across successive
runs, in CI and against a developer's existing cache.

The emitted Yul is unchanged: the generated runtime modules are
byte-identical for the exp and ln wrappers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…roof

The 32 granularity pieces (vlo, vhi, T, DOver, DUnder) live once, in the
tracked leaf module Floor/GranPieces.lean. GenExpVLit.lean instantiates the
per-piece certDOverP/certDUnderP cover certificates from that table (output
byte-identical), and Floor/GranV proves the per-piece facts over the same
entries: granPieces_ok establishes PieceOK for every table entry, the
kernel-decided cover and cap facts (granPieces_cover via piecesCover,
granPieces_caps) replace the literal piece dispatch, and piece_select picks
the runtime point's piece through the cover, keeping its statement unchanged
(GranPair and everything downstream are untouched). The K cap is carried as
evalPoly Kpoly vhi, derived from the tracked Kpoly at the piece's upper edge
rather than restated per piece.

The certificate-generation step builds ExpProof.Floor.GranPieces alongside
ExpProof.Floor.CertDefsV before running the generator (exp-formal.yml and
the README build block).

Full lake build is green from scratch through the Theorems.lean axiom gates;
every public statement is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The trailing-zero trim, greedy checkCoverK cell walk (with its binary
width search), zero-padded index, literal-list emitter, and the cover-cell /
_nonneg-ladder text templates live once, in Common.GenCover; the four
cover-walking generators (GenExpVLit, GenCover, GenErrLit, GenFloorCertLit)
consume them instead of carrying per-file copies. GenErrLit keeps only its
namespace-wrapping litText variant, expressed over the shared emitter, and
GenFloorCertLit's whole-file assembler is renamed fileText to keep the
shared name unambiguous.

Every certificate-generation build line (exp-formal.yml, build-ln-proof,
and the README blocks) builds Common.GenCover before running the
generators. All generated certificate files and all four generators'
stdout are byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
expRayToWad returns the kernel result with a plain return statement, which
shifts solc's AST ids: the compiled wrapper's generated functions are now
fun_expRayToWad_68, fun__expRayToWad_78, and the wrap/external pair 97, and
the kernel body ends in a leave statement rather than a dead let. The Seam
reduction layer follows the regenerated artifacts: every generated-name
reference is renumbered, and the fun_expRayToWad walk simp sets carry
EvmYul.Yul.State.revive and EvmYul.Yul.State.setLeave to reduce the leave,
matching the dispatcher walks. The reduction simp sets are also pruned to
the lemmas the walks use.

The emitted Yul is otherwise identical modulo internal variable ids; no
statement outside the Seam layer changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every fix removes the dead thing itself rather than silencing the linter:
unused simp arguments are pruned from the reduction and cap proofs (exp
Seam sets were pruned with the renumbering; this covers ln RuntimeModel,
Floor/Caps, Error/FactoredCap, Error/LtFactoredCap, Common ExpSum and
Word, and exp GranV); hypotheses nothing consumes are dropped from
smooth_cross_of, DENv_ge_over, tele_step_frac, t_over_2128_le_half_log2,
and exp_t_le_sqrt2, with their call sites updated; four ring calls after
closing converts and an unused ipow255 are deleted; and Stages moves off
the deprecated Int.ofNat_ediv to Int.natCast_ediv.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The general shift/division floor lemmas live once, in Common.Word:
evmShr_lt, evmShl_lt, evmShr_eq_div, evmShl_eq, the signed evmSar floor
sandwich at an arbitrary shift, and the truncated-division monotonicity
chain (nat_div_cross_mono, toNat_mul_of_nonneg, cross_to_div).

exp Mono/WordMono keeps its short names as one-line re-exports (the same
shape it already uses for the FormalYul.Preservation transports); its
ipow256/ipow255 delegate to intPow256/intPow255 and the dead
le_of_mul_le_mul_pos is removed. ln stops re-proving shared facts:
Foundation/Word's twelve int256/evm* transports delegate to
FormalYul.Preservation, Foundation/WordDiv's seven per-shift evmSar
sandwiches and three evmShr floor-division instances derive from the
Common.Word general lemmas (statements unchanged, so no call site moves),
Mono/Step's cross_to_div and toNat_mul_of_nonneg delegate likewise,
Seam/RuntimeModel drops its verbatim copies of the Common.Word
u256/wordNat bridge (u256_lt_word through wordNat_sdiv) in favor of the
shared module, and Floor/Caps takes eval01 from
Common.Foundation.Kronecker.

Both proof packages build green through their axiom gates with the
generated certificates byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The closing bases take the two free bits the final coefficients' byte
widths and the 256-bit t*Od product leave: Ev closes at Q88 (final stage
shr 0x7e) and Od at Q89 (shr 0x80), with the t*Od sar landing at Q88
(0x81) to match Ev; both final coefficients are exact rescalings sharing
the 16-byte literal 0x9c2948bcaca16a0dd2fe98bb4470c3c4, so the exact
rational and its analytic certificates (the Taylor cut, the 32-piece
granularity table, every generated cover) are unchanged.

Halving the closing truncations halves the runtime-bridge budget: the
Horner/div jitter certifies at 0.21706 (ev bracket width
142941343449089*2^480 at 2^527, od 269746241*2^480 at 2^508, alignment
2^637, tod grid 2^129), the never-over budget at
B = 6013505372794194988/10^19, and the under deficit at 31/10 (link-1
5/2). The margin is the budget floor 0x2161b482a02 = floor(5^18*B) + 1 =
2293970250242; the k = 63 deficit envelope is 0.40131 < 1. The supported
range stays pinned at k <= 63.

Every documented property and test witness is unchanged; the
supported-edge result is now certifiably the exact floor and its test
asserts equality. Full lake build is green from the Theorems.lean axiom
gates with certificate regeneration byte-identical; validated against a
60-digit reference on 8000+ adversarial points and the lnWadToRay round
trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The joint low-bit refinement re-runs on the finer closing grids
(sensitivity curves at 90 digits, LP min-max over the quasi-continuous
dimensions, exhaustive search over the two constant-term dimensions,
integer polish): six mid-stage coefficients move and the two constant
terms move identically, keeping Ev(0) = 2*Od(0) exact. The kernel now
binds that shared 16-byte literal once (c0 =
0x9c2948bcaca16a0dd2fe98bb4470c388) and closes both Horner chains from
it. The realized relative envelope tightens to <= 0.0075 ulp (~133 of
the form's ~135 bits, within 0.1% of the constrained continuous
optimum), which carries the Taylor cut at the 2^-132 nudge with 2.1x
slack and halves the Mp budget term to 220970869120796102/10^19.

The never-over budget is B = 5792534503673398887/10^19 (jitter,
granularity, and reduced-argument terms unchanged) and the margin is its
exact grid floor 0x2027afc6c05 = floor(5^18*B) + 1 = 2209676553221, with
strictness slack 1 - 1381252288818359375/10^19. The under side is
unchanged at 31/10; the k = 63 deficit envelope is 0.39891 < 1.

The proof re-derives the coefficient-bearing layers: CertDefsV literals
and stagings (outer scales 2^1192/2^1040), the 2^132 cut scales through
CapsV and the cert-real lemmas, GranV's Kpoly and cross expansion (the
32-piece table and its 2^131-folded budget inequalities are unchanged --
the fold is nudge-independent and the 2^132 amplification it feeds is
strictly smaller), den_ge_194, and the margin word. Certificates
regenerate with every cover reached; the bracket geometry from the Q88
carry is untouched. Full lake build is green from the Theorems.lean
axiom gates; validated against a 60-digit reference on 8000+ adversarial
points and the lnWadToRay round trip; all 11 tests and every witness are
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The revert threshold moves to the k = 65 boundary,
0x907595ccd30708cabec8a9db = ceil((65*2^200 - 2^199)/CINV) ~= 44.71e27
(E up to ~2.61e37): with the refit budget the deficit envelope
((31/10)*10^18 + 2^18*MARGIN)/2^(126-k) is 0.79781 at k = 64 and first
exceeds one ulp at k = 65, so the guard now sits exactly where
floor-or-one-less stops being certifiable. The octave count spans
[-61, 64], the closing shift [44, 168], and the k*ln2 grid-residue and
round-trip deficit chains carry the widened bounds (64/2^235 and
((31/10)*WAD + MARGIN)/2^44 respectively, each with ample slack).

Every documented property is unchanged in form; the certificate layer is
untouched and regeneration is byte-identical. Tests cover the new octave:
never-overestimate witnesses with frac(E) > 0.9999 at k = 64, the
supported-edge bracket (frac(E) ~= 0.52 inside the 0.80 envelope) beside
the exact k = 63 top floor, a k = 64 one-ulp-underestimate witness at the
octave's first input, and the boundary-monotonicity loop through the
k = 65 seam. Full lake build is green from the Theorems.lean axiom gates
(the public revert statement carries the new threshold); validated
against a 60-digit reference across the new octave and its seams.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The clamp comparison spells its constant as sub(0x00,
0x85ebc478242540a11f5f1029) -- the 12-byte magnitude ceil(18*ln10*10^27)
negated in-line -- rather than the 32-byte two's-complement literal. The
representation choice stays with the optimizer: at 2000 runs the constant
folds back into the single push, and at low runs settings the negated form
gives the constant optimizer a 15-byte encoding to work from (5 gas over
the push). The magnitude also states the boundary's derivation directly.

The Seam value walks mirror the compiled form (evmSub 0x00 ... in the
eight quoted trees); the runtime bridge closes by kernel evaluation, so
Cmask and every downstream statement are unchanged. Full lake build green
from the axiom gates; tests unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…^192

The closing sequence multiplies the numerator by 10^18*2^68 = 5^18*2^86
before the single DIV, so the quotient lands directly on the 2^68 output
grid: one truncation at the finest achievable scale, margin 0x03 (the
least grid integer above the never-over image 5^18*B/2^40 < 2.0097), and
a closing shift of 68 - k. This saves 6 gas per call versus scaling after
the division and strictly improves accuracy: the k = 64 deficit envelope
is (33/4 + 3)/2^4 = 45/64 ~= 0.703 ulp, and 26 of 20,524 sampled outputs
move from floor(E)-1 to floor(E), none the other way.

The octave reciprocal is CINV = round(2^192/(10^27*ln2)), 13 bytes; the
computed k is bit-identical for every live input (minimum per-tie safety
factor 6.6x, binding at the j = -54 tie), so the guard threshold and all
witnesses are unchanged.

The proof re-derives the closing layer on the scaleQ68 grid pre-floor:
the under deficit is 33/4 grid units including the one-unit DIV floor
(link-1 6210/1000, Mp 2/25, reduced-argument gap 1267/1000, negative-half
granularity 571/1000), the over side is covered by the margin with 0.99
units of strictness slack, and the k-extraction sandwich sits on the
2^192 basis. Full lake build is green from the Theorems.lean axiom gates.

Tests: a central-octave round-trip fuzz test is added; every existing
witness value is unchanged; 12/12 pass at 10k fuzz runs; forge fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One bit moves from the output grid into the closing polynomial bases:
the numerator closes at Q89 (the even chain's closing constant is exactly
twice the odd chain's, ending the shared literal), t is carried at Q129
in the freed t*Od headroom, the quotient pre-scale is 10^18*2^67 =
5^18*2^85, and the margin is the single grid unit 0x01. The revert
threshold moves to the k = 66 boundary, 0x92b2f16cc66c5a4ae96e80d4 ~=
45.40e27 (E up to ~5.22e37). Runtime gas is identical; every documented
property is unchanged in form.

The rebalance was chosen by exact-rational measurement: the truncation
deficit is owned by the odd-side closing sites, amplified antisymmetrically
through the numerator and denominator, so the widened bases drop the true
envelope 2.5x while the margin requantizes from 3/16 to 1/4 ulp at the
edge octave. formal/README.md documents the procedure (measure the true
envelope, attribute per truncation site, rebalance bases, certify
piecewise) for future transcendental kernels, including the dead ends.

The proof certifies the under deficit at 2993/1000 grid units (link-1
including the DIV floor <= 2378/1000 via a 32-piece kernel-checked
certificate -- global worst-case aggregation provably cannot reach the
target -- plus the reduced-argument gap at 307/1000 / 218/1000 per sign
half, Mp <= 2/25, negative-half granularity <= 143/500), the over side
under the one-unit margin with B-image ~0.99527, and the k = 65 deficit
envelope at 3993/4000 ulp. The certificate domain doubles to H129 and all
cover cells regenerate; the octave fold, seam, and round-trip chains carry
the 2^67 grid. Full lake build is green from the Theorems.lean axiom
gates.

Tests: the supported-edge and underestimate witnesses re-mined for the
new envelope, a k = 65 high-fraction never-over witness added, and the
boundary-monotonicity loop extended through the k = 66 seam; 12/12 at
10k fuzz runs; forge fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The closing constants share one binding: c0 carries the odd chain's
constant term and the even chain's stage doubles it in place with
shl(0x01, c0), making Ev(0) = 2*Od(0) visible in the source instead of
split across two opaque literals. Semantics, tests, witnesses, and every
certified budget are unchanged; runtime gas and deployed bytecode remain
with the optimizer, whose constant folding materializes the doubled
literal as before.

The unoptimized Yul the proof anchors to carries the binding and the
shift verbatim, so the eight quoted value walks mirror the compiled form
(evmShl 0x1 c0 at the even closing, the same treatment the walks give
the k-extraction's shl) and an evaluation lemma discharges the doubling
where the walk trees meet the unchanged hand normal forms. Full lake
build is green from the Theorems.lean axiom gates; 12/12 forge tests at
10k fuzz runs; forge fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The measure/attribute/rebalance section gains its converse: the five
instruments that drive a "leave it alone" answer to an exact floor
(constrained-ideal fitting, the coefficient-count decay law, lattice
search under the floored-and-margined objective, linear certificate-floor
solves, and sweep suprema as lower bounds), with lnWadToRay as the worked
example.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@duncancmt
duncancmt force-pushed the dcmt/exp branch 2 times, most recently from 7bb1f1b to d04c7e9 Compare July 7, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant