Check a C code change with Lean
A shorter program can still have a bug. In this example, you can change a pricing function and check that both compiled versions return the required price for every input.
Start with the example below. It replaces a loop with multiplication and simplifies the price calculation. Compile both versions, inspect their instructions, then ask AI to write a proof. Lean checks the proof.
The price rule 9¢ per item after discount + 2¢ fee. Add 1¢ for odd orders. See the calculation ↓
Start at 12¢ per item. Apply a 25% discount. Add 2¢ for an even item count, 3¢ for an odd count. Return 0 for orders over 1,000 items. Arithmetic uses unsigned 32-bit values.
price(n) = n ≤ 1000 ? 9 × n + 2 + (n & 1) : 03 items → 30¢. 4 items → 38¢. The proof must cover every input, including the bounds.
No access code is needed. The server accepts one job at a time. If it is busy, try again later. You can still explore the explanations.
1. Change the pricing function
Start here The rule both programs must meet
The code can change. The price rule stays fixed.
For each item count, both programs must finish and return the price below. This is our .
3 is odd. Add the 3¢ fee.
This picture explains the fixed rule. “Run one input” above runs your actual compiled programs.
Different instructions can give the same result
Adding 12 three times and multiplying 12 by 3 both give 36. A faster version can use different instructions.
Here, means both functions finish with the same required result for every 32-bit input. It does not mean identical files or equal speed.
Inputs over 1,000 must return 0. The proof must cover those inputs too.
The original adds 12 for each item in a loop. The edited version uses multiplication. You can change either program.
Start with “Compile & inspect” to explore without calling AI. “Generate and check proof” runs the whole process.
The idea Translate C into Wasm instructions
A compiler translates your code into small instructions.
Clang reads C. LLVM simplifies it. The linker joins the pieces into a Wasm file.
is a small instruction format. The browser can run it. Lean can reason about its rules.
Read the four compiler views
- Wasm instructions
- A readable view of the binary file.
local.getreads a variable.i32.addadds two values. - Binary bytes
- The actual file, shown as numbers. The proof starts with these exact bytes.
- LLVM IR / before
- The compiler’s working code, before this demo’s simplification passes.
- LLVM IR / after
- The working code after those passes. The compiler then turns it into Wasm.
The identifies each file. It does not prove that the file behaves correctly.
Linking creates the file. The later Lean step checks its behavior. This demo does not prove the C compiler correct.
32-bit values and arithmetic wraparound
A 32-bit value has 32 binary digits. Here it represents a whole number from 0 to 4,294,967,295.
Wasm addition wraps at that limit. Use this small 4-bit example to see the same idea.
1110+ 1 →11111514 + 1 = 15. The result still fits.
An illustration with 4 bits. Real proof arithmetic uses 32 bits and must account for wraparound.
Your compiled files will appear here.
Edit the C, then build it. You can inspect the instructions, binary bytes and LLVM intermediate code.
Linking builds the file. It does not prove equivalence.
A / Original
B / Your edit
The idea Describe the instructions with Lean data
Describe the instructions with data that Lean can read.
is a language for writing precise claims and checking proofs. Jad gives each Wasm instruction an execution rule in Lean.
41 0ci32.const 12.i (.intConst .i32 (12 : BitVec 32))All three describe the instruction “put the number 12 on the value stack.”
Check that the data matches the bytes
Python prepares the bytes, variable types and instruction list. Treat that list as a proposal.
Jad’s reads the exact bytes inside Lean. Lean checks that the result agrees with the proposed list.
The decoder check establishes what the instructions are. The program proof must still establish what they do.
“Prepared” means the data is ready. It stays unchecked until the full Lean job passes.
Read the Lean definitions
- Bytes
- The exact Wasm file, as a list of byte values.
- Locals
- The types of the function’s working variables.
- Body
- The instructions, written with Jad’s Lean definitions.
- Ann
- A loop note: a fact that stays true and a number that must decrease. AI writes these next.
The proof starts at the Wasm bytes.
Build the C to see how those bytes become input for Jad’s Lean execution rules.
Unchecked preview. Lean will check the exact bytes and the completed proof together.
A / Lean input
B / Lean input
Try it Step through the loop and find a fact that stays true
Find one fact that holds before and after each repeat.
In the original C loop, the total is always 12 × items counted. This fact is an .
0 = 12 × 0
Start: no items counted. The total is 0. The fact holds.
Each repeat adds 1 to the count and 12 to the total. The fact still holds.
Items left decreases to zero. That is the that proves the loop stops.
This illustrates the original C loop. The compiler can change its shape. The live proof below must follow the actual Wasm.
Extend the argument to every input
- Start. Show the fact holds before the first repeat.
- Repeat. Assume it holds now. Show one repeat preserves it.
- Finish. Use the fact to show the final price is right.
- Stop. Show a nonnegative whole number decreases on each repeat.
These steps apply to any allowed count. They do not list each possible run.
The parts written by AI and the parts supplied by the demo
AI writes new loop facts, decreasing numbers and for your compiled programs.
A fixed template adds the price rule, exact bytes and links to Jad’s execution rules. The AI cannot change the claim.
generate.py puts these parts in one Lean file. It does not check whether the argument works.
Below, text appears as the model sends it. A failed attempt goes back to AI with Lean’s feedback. A new attempt can still fail.
Read the Lean proof tools
- wasm_vc
- Turns the instructions and loop notes into claims the proof must establish.
- ring
- Builds algebra proofs. For example,
12 × i + 12 = 12 × (i + 1). - omega
- Builds proofs about whole-number bounds, sums, constant multiples, division and remainders.
- bv_omega
- Connects fixed-width values to whole-number facts.
These tools construct proof steps. Lean checks the steps. They do not settle the claim by trying sample inputs.
The AI writes a proof attempt.
AI must explain each loop and prove the price rule for both compiled programs. Click “Generate and check proof” to start.
This is the model’s actual output as it arrives. It is a proof attempt until Lean accepts it. Private model reasoning is not shown.
The idea Understand what the proof checks
A test checks one run. This proof must cover every allowed input.
One observed result. Another input could still fail.
Both functions finish and return the fixed price under Jad’s Wasm rules.
Lean checks each logical step against those rules. A convincing explanation alone cannot pass.
Read the three check results
- Exact files
- The bytes match the proposed instructions. The function has the required input, result and export.
- Every input
- The loop facts, branches, arithmetic and stopping argument establish the price rule.
- Proof dependencies
- The proof has no missing steps or extra assumptions. Only the three standard Lean axioms are allowed.
An is an accepted starting rule. The full list is in the checker output.
All three checks belong to one Lean job. They turn green together when that job and its dependency audit pass.
Failure, time limits and progress
A failed attempt means “not established.” The code could be wrong, or the attempted proof could be incomplete.
A single mismatched run shows a bug. It is separate from a Lean proof that the program breaks a rule.
Lean reads the library and checks the new argument on the server. The work depends on the program and proof.
The timer shows elapsed time. We do not know a reliable percentage while Lean is running.
Lean checks the complete argument.
Once its argument is complete, the server adds the exact files and fixed claim. Lean checks the whole file.
These checks run in one Lean job. They turn green together only after the job and its axiom audit pass.
The decoder and metadata checks must match both binary files.
WaitingBoth functions must finish and return the fixed price.
WaitingNo sorry or unapproved axioms. Only the three standard axioms.
WaitingThe complete Lean file being checked
The AI writes the annotations and program-specific proof steps. The template fixes the claim, embeds the exact bytes and connects the argument to Jad’s execution rules.
Actual checker output
The idea Recheck the completed proof object
Recheck the proof object, after the proof-building tools have finished.
The checks the small logical steps. Replay uses the separate leanchecker tool.
Replay checks proof steps
Replay checks the compiled proof object. It does not try more inputs or ask AI to grade its answer.
Both Lean checking and replay must pass before this demo accepts equivalence.
Read the scope of an accepted result
Both exact Wasm functions finish and return the specified price for every 32-bit input in the model.
The proof uses Jad’s Wasm execution rules and the stated function setup. Those rules must describe the behavior we intend.
This does not prove the C compiler correct, full module loading, equal speed or equal memory use.
Download the proof, binaries and record. Independent checking of this editable example needs access to the pinned private library.
Check the proof object again.
After Lean accepts the source, a separate kernel replay checks its compiled proof object. Both steps must pass.
The demo accepts equivalence only after the byte checks, proof, axiom audit and replay pass. You can download the proof and exact binaries below.
What the AI argued
These explanations describe the attempt. The result above says whether Lean accepted it.
AI-written invariants and Lean proof steps
The AI writes a fact for each loop, a quantity that must decrease, and Lean steps that prove the required obligations.
What this example covers
You can try a new rewrite. It does not have to match a preset. Both functions must still meet the fixed price rule.
Supported code
The proof path supports one 32-bit input and result, local variables, integer arithmetic, branches and up to two loops. Calls, memory access, floating point and many other instructions are outside this path. Unsupported code gets an error.
An equivalent edit can still be too hard for the current agent or time limit. “Not established” means no accepted proof was found.
Where the work runs and how to check it yourself
The browser runs the Wasm examples. Compilation, AI requests and Lean checking run through the server.
This uses Jad’s Wasm decoder, loop rules and proof tools. His compiled library is reused; AI writes a new argument for each submitted pair.
You can download the proof source, helper, exact Wasm files, logs and all attempts. Independent checking needs access to the pinned private library and its small lint-only patch.
Exact formal claim and trust boundaries
LiveDemoProof.binaries_same_result checks that Jad’s decoder reads the exact two binaries as the annotated function bodies. It proves actual Core.Steps executions of both function calls to LiveDemo.price n for every i32 input, under any numerical profile and host, in the one-function instances built by the helper. Both return and preserve their stores. return_values_unique uses Jad’s determinism bridge to show that every returning execution agrees.
Separate checked metadata establishes the i32 → i32 type, absence of imports and the compute export. This is a function-call theorem. It does not prove full module instantiation, full ExportEquiv, C compiler correctness or equal resource use. Browser execution is a separate test.
Private Silmaril revision fc6be8f5e6f6de4c7795776842608dc9795c728f, with lint-only fixes; Lean 4.35.0-rc4. Only standard propext, Classical.choice, Quot.sound axioms are accepted. No sorry, native_decide or unproved SMT facts. The library model, toolchain and I/O are trust boundaries.
Native jobs use containers with no network, read-only libraries, and time and resource limits. A failed, busy or unavailable checker establishes no equivalence claim.
Explore existing public proofs: SHA-256 executable · Proved bugs. These reference examples do not generate new AI proofs.
Design references and further reading
The page follows the approach used in Red Blob Games’ A* guide: start with one concrete example, inspect the data, and introduce each step beside an interactive view. These guides also informed the explanations:
- Chris Olah — Understanding LSTM Networks: keep a consistent visual language and explain one part at a time.
- Olah & Carter / Distill — Attention and Augmented RNNs: put diagrams beside the explanation.
- Red Blob Games — Introduction to A*: step through a small example and see the state change.
- Bartosz Ciechanowski — Mechanical Watch: let readers move controls to see inside a system.
- Nicky Case — Neurotic Neurons: introduce an idea through interaction.
The illustrations here explain concepts. The live compiler and Lean stages produce the actual artifacts and results.