Repository navigation
Word.Naive: represent words as stdlib bits (Zmod); require rocq-stdlib >= 9.1 - #186
Conversation
|
Downstream adaptations: mit-plv/bedrock2#561 and mit-plv/rupicola#225 (both draft until this merges); fiat-crypto PR to follow. Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
fiat-crypto side: mit-plv/fiat-crypto#2434 (draft until the chain above merges). Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
1a3b595 to
5145ac0
Compare
|
Kami side: mit-plv/kami#51 (Kami words as stdlib bits), with a bedrock2 bridge simplification to follow, stacked on mit-plv/bedrock2#561. Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
| coq_version: ${{ matrix.env.DOCKER_COQ_VERSION }} | ||
| custom_script: etc/ci/github-actions-docker-make.sh -j"$(nproc)" all | ||
| custom_script: | | ||
| if ! [ -d "$(rocq -where)/user-contrib/Stdlib/Zmod" ]; then opam update -y && opam pin add -y rocq-stdlib 9.1.0; fi |
There was a problem hiding this comment.
Does this line update rocq to 9.2 even if matrix lists 9.1?
There was a problem hiding this comment.
I believe it does not (and claude agrees). I am not sure if there is a way to make it error if there are incompatibilities
There was a problem hiding this comment.
I still don't feel great about having the conditional opam install in every CI script, with a patch version of stdlib pinned in it even. I'd rather just drop 9.1 support on opam, I think. It's possible there is a neat alternative, but I can't think of it just now.
There was a problem hiding this comment.
We can ask upstream to maintain docker images for all compatible Rocq*stdlib versions?
There was a problem hiding this comment.
I'd rather not deal with upstream, but if you wish, we can try.
There was a problem hiding this comment.
Done: the pin script is gone and Rocq 9.1 is out of the docker matrix here and in bedrock2/rupicola, so CI tests 9.2 and dev with the build step exactly as on master. The opam bound stays rocq-stdlib >= 9.1~ (rocq-stdlib 9.1.0 does allow Rocq 9.1; it is just not tested).
Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm).
| Definition wrap (z:Z) : rep := | ||
| mk (wrap_value z) (minimize_eq_proof Z.eq_dec (Zdiv.Zmod_mod z _)). | ||
| Definition signed w := swrap_value (unsigned w). | ||
| (* The operations below are named wrappers around the Zmod operations, local |
There was a problem hiding this comment.
Would abbreviations work here?
There was a problem hiding this comment.
No, the purpose is specifically to control performance of cbn. simpl never is attached to constants, whereas abbreviations are only for parsing/printing/naming.
There was a problem hiding this comment.
Yes, but is the control actually necessary? I see some unfoldings added for the wrappers in downstream PRs. Could we fix the runaway simpl/cbn calls instead?
There was a problem hiding this comment.
You can replace them with manual change or some other tactic. You can also do local simplification control in each file, but Claude was claiming that runaway cbn is a foot gun. What's your preference?
There was a problem hiding this comment.
I think we should avoid Definition wrappers. Maaybe some Zmod definitions should be globally simpl never, I think that'd still be preferable. But cbn without arguments is a footgun regardless, so idk, as long as the appropriate cbn calls don't look too bad without adding simpl never i'd still go with that. If we need to use change, that may be a sign that something is wrong.
There was a problem hiding this comment.
Done: the wrappers are gone and nothing is simpl never any more, here or downstream. The record fields are the stdlib operations directly, and the ! flags at the end of the file are back to what master has. Downstream, three sites needed a targeted reduction instead of a bare one: the two NaiveRiscvWordProperties proofs in bedrock2 (cbv with a named list of projections), and in rupicola one hint that ran cbn; lia on every goal (now restricted to arithmetic goal shapes) plus one line reverted to master's text. The local simpl never declarations that bedrock2's KamiWord.v carried are removed too; the Kami cone builds in the same time without them. fiat-crypto needed two one-line adjustments. The three lemmas in this file that restated stdlib ones (unsigned_inj, of_Z_unsigned, signed_of_Z) are dropped as well.
Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm).
| Let adjust_shift_amount n := | ||
| if Z.ltb n width then n else sp.(adjust_too_big_shift_amount) n. | ||
|
|
||
| #[local] Definition slu (x y : rep) : rep := Zmod.slu x (adjust_shift_amount (unsigned y)). |
There was a problem hiding this comment.
After consolidating word type in Kami, we should revisit whether these adjustments are still necessary for the lightbulb end2end proof to go through.
There was a problem hiding this comment.
Claude claims the consolidation is done in mit-plv/kami#51 and mit-plv/bedrock2#562, and the shift division conventions were kept unchanged "on purpose". I have asked Fable what this means
There was a problem hiding this comment.
Apparently it means "I didn't want to change the lightbulb theorem statement", I suspect this happened after it got shifted from Fable to Opus. I have asked it to investigate / fix
There was a problem hiding this comment.
Checked this once the Kami side was on stdlib bits (mit-plv/kami#51, mit-plv/bedrock2#562): the Kami-side word instance and Naive.word agree on all 26 fields at widths 32 and 8 (lemmas, not just spot checks), so bedrock2's processor bridge now simply is Naive.word and its own riscv_ok file is gone (mit-plv/bedrock2#563). The adjustment itself is still needed, though: without it Naive.word violates riscv_ok's slu_ignores_hibits (counterexample y = 1, z = 32), so the compiler theorem could not be instantiated at Naive at all. Before, the lightbulb proof did not depend on it (the Kami-side instance masked shift amounts with mod width itself); after the unification it does, like every other Naive user. What is superfluous is only the generality of the special_cases record, since every user passes the default handler.
Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm).
There was a problem hiding this comment.
Claude is pretending to be me, I need to tell it to stop posting without attaching a Claude footer
5145ac0 to
1223282
Compare
| custom_script: | | ||
| if ! [ -d "$(rocq -where)/user-contrib/Stdlib/Zmod" ]; then opam update -y && opam pin add -y rocq-stdlib 9.1.0; fi |
There was a problem hiding this comment.
Let's consider alternatives that don't need adding ad-hoc scripts to every projects CI config. For example, would it work to test opam only on Rocq 9.2?
1223282 to
f2196bd
Compare
andres-erbsen
left a comment
There was a problem hiding this comment.
These changes look good. Let's wait with merging until other repos are also approved.
When preparing PRs for repos that depend on coqutil, update git submodules such that all PRs test the approved coqutil change and are strictly up to date with the appropriate repository's master branch. When the first set of PRs is approved and passing CI on all repos, I want to be able to fast-forward all master branches to the PR-proposed versions (atomically across all repos, without recomputing submodule hashes).
The record { unsigned ; proof } is replaced by Stdlib.Zmod's bits width.
Arithmetic, bitwise and shift operations are the Zmod ones; division by
zero keeps the previous default semantics because Zmod.udiv/umod/squot/srem
agree with the old default handlers, so the div_by_zero and mod_by_zero
handlers are gone. eq_unsigned and of_Z_unsigned are gone too: they were
Zmod.unsigned_inj and Zmod.of_Z_unsigned restated.
A word of warning for proofs about concrete Naive words: Zmod.add and
friends are written with let-bound intermediate values and Zmod.of_Z
repeats its argument inside the range proof, so reducing through them with
cbn or simpl duplicates subterms at every nesting level and is exponential
in the nesting depth (2.5 s for two nested adds, 17 s for three). Kernel
conversion (vm_compute, reflexivity) shares subterms and is unaffected.
Rewrite with word.unsigned_add and the other interface lemmas instead of
computing the mod form.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm
The rocq/rocq-prover docker images for 9.0 and 9.1 ship rocq-stdlib 9.0, which has no Zmod, so CI tests 9.2 and dev. The opam bound stays rocq-stdlib >= 9.1, which does allow Rocq 9.1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm
f2196bd to
ac2f85a
Compare
|
Re-pinned for the atomic fast-forward. Every branch is on its repository's current master with no merge commits, and each pins the tip listed here for its dependency:
mit-plv/fiat-crypto#2435 (186e533f) is #2434 plus one commit. The Kami set is a second wave on top of these: mit-plv/kami#51 (dbba41f), mit-plv/bedrock2#562 (61831cc2, pins coqutil ac2f85a and kami dbba41f) and #563 (207ec716). Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
Now let's create another stack of PRs to remove the Word interface altogether, hardcoding stdlib |
|
Starting on it. Survey and plan, for the record before the code arrives: Shape. Stack (each on its repo's master, pinning the previous tip, as in the first wave): 1 coqutil (17 files, about -1300/+450); 2 riscv-coq (31 files); 3 bedrock2 as one PR with commits per subproject (171 files; it cannot be split, the compiler instantiates Semantics' types); 4 rupicola (50 files); 5 fiat-crypto (52 files, Types.parameters loses its word fields). Direct route rather than an intermediate "word is a definition" state: the expensive edits are the same either way and the intermediate state breaks every Ltac pattern on Questions where the design forks (proceeding with the first option unless you say otherwise): (1) shift masking spelled out in Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
feel free to use |
|
PR 1 of the word-removal set is #187 (coqutil, stacked on this PR). Numerals: Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
For further follow-ups, consider the following issues and either present analysis or propose a PR (or PR stack) if there is one clear way to resolve the issue:
|
|
One more Zmod-porting task: this lemma and dependencies should be work on top of fiat-crypto, right? andres-erbsen/stdlib@5bd6d0d#diff-9834d25e486dacac5c1bfdd0402f4681e28c2cad3293269c7e80cbb3c38f2201R1364-R1395 I mean the last lemma in ZmodSqrt.v |
|
Notation standardization (first item of the list above). The sextend item (coqutil#4) is answered on that issue: it is resolved by #187. I surveyed every word/Zmod notation in stdlib, coqutil, riscv-coq, bedrock2 (+compiler, What is there: stdlib gives Proposal: one file Full reportFollow-up A: word / Zmod notations across the submodule treeAnswer to Andres Erbsen's question on #186 (2026-09-17): "we seem to have a Status: analysis only, no PR yet. Lane fu-a, 2026-09-17. Clones surveyed (read-only, all already ported to stdlib
Method: 1. Baseline: what stdlib provides
What stdlib does not provide: any notation for Because 2. Inventory per repository2.1 coqutil (8b40a6c)
coqutil has no infix notation for word arithmetic, unsigned, or comparisons. Everything 2.2 riscv-coq (a98896f)
The 2.3 bedrock2 (447b97df)
Duplicates and variants inside bedrock2 itself (all re-
Printing today (t4): with 2.4 rupicola (4764adf)
Only 4 files require it ( 2.5 fiat-crypto (a5ee22098)fiat-crypto never imports bedrock2's or rupicola's
Hazard confirmed (t1 G): with the global 2.6 kami (2777a72)
2.7 bedrock2/processor (uses Kami + riscv-coq + bedrock2)
2.8 quartz (0b5b9e0)No term-level notations on Oddity spotted while reading (not a notation-standardisation issue, but worth telling the 2.9 granite (af1c5c4)
Granite is the only tree whose literal notation is not the coqutil one, and the only one that 3. Conflicts and overlapsSame syntax, different meaning (C = conflict):
Same meaning, different syntax (S = spelling variance):
4. Feasibility checks (all compiled; files in
|
| file | question | result |
|---|---|---|
t1_scopes.v A-B |
can the stdlib scope serve with only Bind Scope? | Zmod.add x 3 and Zmod.mul x 1 parse with nothing open; x + y needs %Zmod/open scope |
t1_scopes.v D-F |
is Open Scope Zmod_scope over Z_scope safe in files that mix Z and words? |
No. Definition m (width : Z) := 2 ^ width fails (parsed as Zmod.pow); let z := x + y in z on Z fails. Positions with an expected Z type (: Z, arguments of Z.lt, Zmod.of_Z, …) are fine via Bind Scope. This is why bedrock2, whose proofs are ~600 \[…] lines of mixed Z/word arithmetic, uses distinct symbols ^+. |
t1_scopes.v G |
fiat-crypto's global coercion | x + y : Z silently, see 2.5 |
t1_scopes.v H, t4_bedrock.v |
printing of literals | see 2.3: 3 prints as 3, 0/-1 as /[0]//[-1] with word_scope open, as Zmod.of_Z (2^w) 0 otherwise |
t2_sections.v |
do the Section-local copies (lightbulb, FlatToRiscvFunctions) leak? | No: notations, Delimit Scope, all die at End. Re-Declare Scope is silent. |
t3_infix.v |
can & | << >> <? <s? be added to Zmod_scope? |
&, <<, >>, <?, <s? work with no clash. | is unusable: once an Infix "|" exists, (a | b) for Z.divide no longer parses even with %Z, and y | x inside a match arm is a syntax error (parenthesised too). Re-binding ^ to xor is accepted with only a notation-overridden warning, i.e. a silent trap. |
t4_bedrock.v |
does /[x] need WordWidth? can word_scope and Zmod_scope coexist? |
x ^+ /[3] resolves by unification with no Bitwidth; a bare /[3] needs the Bitwidth instance (Fail Definition confirms). Opening both scopes: no overlapping symbols, x ^+ y = x + y typechecks (and prints x ^+ y = x ^+ y). |
5. Proposal
5.1 Principles
- One source of truth per layer. stdlib owns
Zmod_scope(0 1 + - * ^, bound to the
type). coqutil owns everything word-flavoured that is shared (literals,\[ ],/[ ], the
^-prefixed operators) in one file,coqutil.Word.WordNotations(new;Bitwidth.vkeeps
the Number Notation because it is the file every port already imports). Downstream repos
import that file and delete their copies. - Never add a symbol to
Zmod_scopethat reads differently inZ_scope(^,|,
<), and never rely onOpen Scope Zmod_scopein files that mixZand words (t1 D/F).
Files that are pure field arithmetic (fiat-cryptoSpec/,Arithmetic/) keep opening it. - Word operators that must coexist with
Zarithmetic get distinct spellings, and the
existing bedrock2 spelling wins on numbers (^+102 lines vs+w6;\[581 lines vs
nothing comparable). - Scope hygiene: exactly one
Declare Scope word_scope(in coqutil), with
Delimit Scope word_scope with word, and noOpen Scopeat import (rupicola
WordNotations.v:22and KamiWord.v:37do this today).
5.2 The shared set (coqutil, word_scope, delimiter %word)
| notation | meaning | decision |
|---|---|---|
3 : bits w |
Zmod.of_Z (2^w) 3 |
already in Bitwidth.v; keep (clear) |
/[ x ] |
bits.of_Z width x (parse via WordWidth hole; print Zmod.of_Z _ x) |
move from bedrock2 verbatim, including the WordWidth class (clear) |
\[ x ] |
Zmod.unsigned x |
move from bedrock2 verbatim (clear) |
^+ ^- ^* |
Zmod.add sub mul |
move from bedrock2 (clear; Kami already agrees on spelling and levels) |
^<< ^>> |
Zmod.slu, Zmod.sru with a Z amount |
move from bedrock2 (clear; 40 lines) |
^>>> |
Zmod.srs |
add for symmetry; taste, low cost |
bitwise and or xor not |
no infix; write Zmod.and x y |
bedrock2 has 0 uses of any infix for these; |/^ are unusable/ambiguous (t3, C1); rupicola's ~31 lines convert to names or keep a rupicola-local scope |
| comparisons | no shared infix; write \[a] < \[b] (unsigned) / Zmod.signed a < Zmod.signed b |
this is what bedrock2 already does everywhere; the only infix users are Kami (Prop) and rupicola (word-valued), which disagree with each other and with riscv's signed < (C2) |
masked shifts Semantics.slu/sru/srs, ltu/lts |
no shared notation | bedrock2-specific semantics; stays in bedrock2 by name |
Taste question T1, literal printing. Options: (a) status quo: x ^+ 3 but x ^+ /[0],
/[-1]; (b) let the Number Notation also print Zmod.of_Z m 0/1 as 0/1 (then Zmod.zero
and of_Z m 0 print identically although they are different terms, which bites rewrite);
(c) drop the printing side of /[ ] and always print Zmod.of_Z. Recommendation: (a); it is
the only option where what you read tells you which term you have.
Taste question T2, $@. Options: (a) keep coqutil's xs $@ a = map.of_list_word_at and
rename the sep-logic one in bedrock2/fiat to xs $@@ a or a plain bytes xs a abbreviation;
(b) the reverse; (c) leave both local. Recommendation: (a) with a bytes-style abbreviation,
because the map meaning is the one that is global today and the predicate meaning is already
spelled bytes in lightbulb.v:92, LAN9250.v:877, ARPResponderProofs.v:15.
5.3 Repo-local decisions
- bedrock2:
WordNotations.vbecomesRequire Export coqutil.Word.WordNotations; delete the
copies inbsearch.v,lightbulb_spec.v,FlatToRiscvFunctions.v,FlatConstMem.v,
SimpleInvariant.v(!n→/[n],#n→Z.of_nat n,[/a]→/[a],[\a]→\[a], ~40 lines);
renameTracePredicate's^+/^*(C8) if that file ever needsword_scope.bedrock_expr
custom-entry operators are untouched (they are AST syntax, not word syntax). - rupicola: replace
+w -w *wby^+ ^- ^*(13 lines),<<w >>w >>>wby
Semantics.slu/sru/srsor a rupicola-local^<<w-style spelling that does not look like
bedrock2's unmasked shift (C9, 26 lines), bitwise/comparison…wby names (~60 lines); remove
Open Scope word.and thewordscope. Rupicola keepsword.b2w. - fiat-crypto: keep
Global Coercionand/ := Zmod.mdiv : Zmod_scope(field files are a
legitimately different dialect; document the t1 G hazard inSpec/ModularArithmetic.v).
ChaCha20.v/Broadcast.v/EdwardsXYZT.vlocal+ * ^:^→Zmod.xor,+→^+or
%Zmod;$ n→literaln;p .+ n→p ^+ n(14 lines) or keep as a local pointer-offset
idiom (taste; I'd keep it, it is readable and local).RidiculousFish.vhas its own word type
and is out of scope. - riscv-coq: nothing to standardise (
alu_scopeis the spec DSL). Delete or repair the dead
Examples/Fib.v(Kami-eraWO~syntax, not built). - Kami: put
^~ ^+ ^* ^-,$ n,# nintoword_scope(Lib/Word.v:101-117) so they stop
being global; Kami's own files are unaffected becauseWord.v:37opens the scope, and
processorthen needsLocal Open Scope word_scope(1-3 files). Kami's Prop-valued< <= > >=
stay Kami-local; do not export them to coqutil. - granite: (a) adopt coqutil's Number Notation semantics (map to
Zmod.of_Z, stop
overriding0/1) and proveunsigned_literalbybits.unsigned_of_Z_smallinstead of
reflexivity, or (b) keepbits_literalbut drop the0/1override and makezeroesa
notation forZmod.zero, or (c) leave as is since granite shares no.vowith the coqutil
world. Recommendation: (b) now (257zeroeslines untouched, thezeroes_zerobridge
disappears), (a) if granite ever depends on coqutil. Delete the unusedlist_bits/Ob~
scope (0 uses). - quartz: nothing (no value-level notations). Report the
-/*mapping oddity
(lang/Syntax.v:382,385) to the owners.
5.4 Migration order
- coqutil: add
src/coqutil/Word/WordNotations.v(scope + delimiter +WordWidth+/[ ] \[ ] ^+ ^- ^* ^<< ^>> ^>>>), tests asGoal … reflexivitylikeBitwidth.v:29-34. Optionally
decide T2 ($@) here. - riscv-coq: bump coqutil only (no notation changes; optional
Fib.vcleanup). - bedrock2: bump;
WordNotations.v→ re-export; delete the five local copies; processor opens
word_scopelocally once Kami is scoped (step 6 can land before or after; the^+spelling
is identical so both orders build). - rupicola: bump; rewrite
Lib/WordNotations.vas above (~100 lines touched in 4 files). - fiat-crypto: bump submodules; ChaCha20/Broadcast/P256 local notations (~30 lines).
- Kami (independent): scope the globals; then bedrock2/processor adds
Local Open Scope word_scope. - granite (independent): T-granite option (b); quartz: nothing.
Steps 1-5 follow the usual submodule chain and each is a small PR; none changes a definition,
only notation files and use sites, so they can be reviewed by reading the diff.
GitHub-ready summary (for #186)
I surveyed every word/Zmod notation in stdlib, coqutil, riscv-coq, bedrock2 (+compiler,
LiveVerif, processor), rupicola, fiat-crypto, Kami, quartz and granite, and tried the obvious
unifications against Rocq 9.2.
What is there: stdlib gives Zmod_scope with 0 1 + - * ^, bound to the type, and nothing
for unsigned, literals, bitwise ops, shifts or comparisons. coqutil adds decimal literals.
bedrock2 adds \[x], /[x], ^+ ^- ^* ^<< ^>> (about 1250 use lines) and re-declares
word_scope four more times with !n, #n, +, [/a]. rupicola has +w, <w, &w, ...
(about 100 lines) and opens its scope at import. Kami has global ^+, $n, #n. fiat-crypto
has local $n, .+, ^ for xor. granite has its own literal notation that overrides 0/1.
Same symbol, different meaning: ^ (pow vs xor), < (unsigned Prop, signed bool, word-valued),
$@ (map vs sep-logic bytes).
Proposal: one file coqutil.Word.WordNotations owning word_scope (%word), containing
bedrock2's /[ ], \[ ], ^+ ^- ^* ^<< ^>> unchanged. No infix for bitwise ops or
comparisons: | breaks Z.divide's (a | b) and match arms (tested), ^ is pow, and
\[a] < \[b] already reads fine. Keep stdlib Zmod_scope for pure field files only, since
opening it over Z_scope mis-parses 2 ^ width (tested). Then bedrock2 re-exports and deletes
its copies, rupicola and fiat-crypto drop their local spellings, Kami scopes its globals, granite
stops overriding 0/1. Full report with counts, file:line evidence and compiled checks in the
follow-up document.
Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm).
|
The ZmodSqrt development builds on fiat-crypto master against released stdlib 9.1: mit-plv/fiat-crypto#2446 (draft: one admit left, Ported andres-erbsen/stdlib@5bd6d0db ("WIP ZmodSqrt") onto fiat-crypto master (a5ee22098) as Changes needed: One real bug in ZmodSqrtDef.v: ZmodSqrt.v was written against an older ZmodSqrtDef ( Suggest opening the fiat-crypto PR with the admit and upstreaming the Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
Yes, port genuinely useful and nontrivial content in a follow-up PR or PRs. |
|
Will do: the closed, nontrivial parts (the Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
First stdlib PR: rocq-prover/stdlib#306 (Factoring.v with the fundamental theorem of arithmetic and the factorization induction principles, plus the coprimality iff lemmas; everything closed, full stdlib build). Next: the Euler criterion / Wilson / quadratic reciprocity part, which is closed and independent of the square-root definitions; the square-root correctness waits for its admit and the k = 1 fix. Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
Second stdlib PR: rocq-prover/stdlib#307 (Euler's criterion, the Wilson direction and quadratic reciprocity, stacked on #306). The square-root definitions and their correctness stay in fiat-crypto#2446 until the admit is closed. Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
Please make these PRs against fiat-crypto instead of stdlib |
|
Understood; closed rocq-prover/stdlib#306/#307. The same cleaned content (Factoring + coprime lemmas; Euler criterion + quadratic reciprocity, no admits, no scratch) will come as fiat-crypto PRs, and fiat-crypto#2446 will shrink to the square-root part that still has the admit. Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
|
As fiat-crypto PRs: mit-plv/fiat-crypto#2450 (Factoring.v with the fundamental theorem of arithmetic, Written by Claude Fable 5.1 via Claude Code (session https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm). |
Represent
Word.Naivewords as stdlibbits width(that is,Zmod (2^width)) instead of the hand-rolled{ unsigned ; proof }record, and use the stdlibZmodoperations for arithmetic, bitwise operations, shifts and division. Division by zero keeps the previous default semantics becauseZmod.udiv/umod/squot/sremalready agree with the old default handlers, so thediv_by_zeroandmod_by_zerohandler fields are gone (special_caseskeeps onlyadjust_too_big_shift_amount). All exported names that downstream code uses (rep,unsigned,signed,wrap,gen_word,gen_ok,word,ok,word8..word512) keep their names and arities;mk,_unsigned_in_rangeandminimize_eq_proofare removed.Naive.rep widthis definitionallybits width, sodestructon a word no longer yields a{ unsigned ; proof }pair.The record fields are the stdlib operations themselves (
Zmod.add,Zmod.udiv, ...), with the shift amount adjustment kept as before; no wrappers and nosimpl neverflags. One caveat for downstream proofs:cbn/simplthroughZmod.add/of_small_Zduplicates subterms at each nesting level, so a barecbnon deeply nested word arithmetic is exponential; proofs about concrete words should rewrite with theword.unsigned_*interface lemmas or use a targeted reduction. Only three downstream sites needed that (two proofs in bedrock2's compiler, one hint in rupicola).Zmodfirst ships in rocq-stdlib 9.1, so the opam bound moves torocq-stdlib >= 9.1. Rocq 9.0 and 9.1 are dropped from CI: theirrocq/rocq-proverdocker images ship rocq-stdlib 9.0.0 (noZmod), so CI tests 9.2 and dev; the opam bound staysrocq-stdlib >= 9.1, which does allow Rocq 9.1. Downstream adaptations: mit-plv/bedrock2 (one proof file in the compiler) and mit-plv/rupicola (two examples); those PRs are linked in a comment below once open.Written by Claude Fable 5.1 from Andres Erbsen's feasibility sketch (https://gist.github.com/andres-erbsen/17b583447d29dc085714bb8e9c7cdc89).
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJbiRBkJtM7x6PvYV6hmEm