feat(NumberTheory/Wilson): explicit square root of -1 modulo a prime - #42326
feat(NumberTheory/Wilson): explicit square root of -1 modulo a prime#42326AlyciaBHZ wants to merge 2 commits into
Conversation
Add `ZMod.factorial_eq_neg_one_pow_mul_half_factorial_sq`, the half-factorial pairing identity for odd moduli, and `ZMod.half_factorial_sq_eq_neg_one`, which exhibits `((p - 1) / 2)!` as a square root of `-1` when `p % 4 ≠ 3`. Mathlib has `ZMod.exists_sq_eq_neg_one_iff` for existence and `ZMod.mod_four_ne_three_of_sq_eq_neg_one` for the converse, but no way to pass from the congruence condition to a specific root.
Welcome new contributor!Thank you for contributing to Mathlib! If you haven't done so already, please review our contribution guidelines, as well as the style guide and naming conventions. In particular, we kindly remind contributors that we have guidelines regarding the use of AI when making pull requests. We use a review queue to manage reviews. If your PR does not appear there, it is probably because it is not successfully building (i.e., it doesn't have a green checkmark), has the If you haven't already done so, please come to https://leanprover.zulipchat.com/, introduce yourself, and mention your new PR. Thank you again for joining our community. |
PR summary 76dfe476e0
|
| File | Base Count | Head Count | Change |
|---|---|---|---|
| Mathlib.NumberTheory.Wilson | 1768 | 1770 | +2 (+0.11%) |
Import changes for all files
| Files | Import difference |
|---|---|
Mathlib.NumberTheory.Wilson |
2 |
Declarations diff (regex)
+ factorial_eq_neg_one_pow_mul_half_factorial_sq
+ half_factorial_sq_eq_neg_one
You can run this locally as follows
## from your `mathlib4` directory:
git clone https://github.com/leanprover-community/mathlib-ci.git ../mathlib-ci
## summary with just the declaration names:
../mathlib-ci/scripts/pr_summary/declarations_diff.sh <optional_commit>
## more verbose report:
../mathlib-ci/scripts/pr_summary/declarations_diff.sh long <optional_commit>The doc-module for scripts/pr_summary/declarations_diff.sh in the mathlib-ci repository contains some details about this script.
Declarations diff (Lean)
✅ Lean-aware diff — post-build, computed from the Lean environment (commit
76dfe47).
- +2 new declarations
- −0 removed declarations
+ZMod.factorial_eq_neg_one_pow_mul_half_factorial_sq
+ZMod.half_factorial_sq_eq_neg_oneNo changes to strong technical debt.
No changes to weak technical debt.
Current commit 76dfe476e0
Reference commit 62244e5ddb
This script lives in the mathlib-ci repository. To run it locally, from your mathlib4 directory:
git clone https://github.com/leanprover-community/mathlib-ci.git ../mathlib-ci
../mathlib-ci/scripts/reporting/technical-debt-metrics.sh pr_summary
- The
relativevalue is the weighted sum of the differences with weight given by the inverse of the current value of the statistic. - The
absolutevalue is therelativevalue divided by the total sum of the inverses of the current values (i.e. the weighted average of the differences).
|
|
||
| This gives an explicit witness, namely the half-factorial itself, rather than merely asserting | ||
| that some square root exists. -/ | ||
| theorem half_factorial_sq_eq_neg_one (p : ℕ) [Fact p.Prime] (hp : p % 4 ≠ 3) : |
There was a problem hiding this comment.
Could you reuse the declared variable?
| theorem half_factorial_sq_eq_neg_one (p : ℕ) [Fact p.Prime] (hp : p % 4 ≠ 3) : | |
| theorem half_factorial_sq_eq_neg_one (hp : p % 4 ≠ 3) : |
| ## Main results | ||
|
|
||
| * `ZMod.factorial_eq_neg_one_pow_mul_half_factorial_sq` expresses `(p - 1)!` using the square of | ||
| the half-factorial when `p` is odd. | ||
| * `ZMod.half_factorial_sq_eq_neg_one` gives the half-factorial as an explicit square root of `-1` | ||
| modulo a prime not congruent to three modulo four. | ||
|
|
There was a problem hiding this comment.
Should also add the previous main results here prime_iff_fac_equiv_neg_one
| /-- If `p` is odd, pairing each factor above `(p - 1) / 2` with its negative gives | ||
| `(p - 1)! = (-1) ^ ((p - 1) / 2) * (((p - 1) / 2)!) ^ 2` in `ZMod p`. | ||
|
|
||
| The identity only needs the modulus to be odd; primality is not required. -/ | ||
| theorem factorial_eq_neg_one_pow_mul_half_factorial_sq (p : ℕ) (hp : Odd p) : |
There was a problem hiding this comment.
| /-- If `p` is odd, pairing each factor above `(p - 1) / 2` with its negative gives | |
| `(p - 1)! = (-1) ^ ((p - 1) / 2) * (((p - 1) / 2)!) ^ 2` in `ZMod p`. | |
| The identity only needs the modulus to be odd; primality is not required. -/ | |
| theorem factorial_eq_neg_one_pow_mul_half_factorial_sq (p : ℕ) (hp : Odd p) : | |
| /-- If `p` is odd, pairing each factor above `(p - 1) / 2` with its negative gives | |
| `(p - 1)! = (-1) ^ ((p - 1) / 2) * (((p - 1) / 2)!) ^ 2` in `ZMod p`. -/ | |
| theorem factorial_eq_neg_one_pow_mul_half_factorial_sq (p : ℕ) (hp : Odd p) : |
| /-- Let `p` be prime with `p % 4 ≠ 3`. Then `((p - 1) / 2)!` is a specified square root of `-1` | ||
| in `ZMod p`. | ||
|
|
||
| This gives an explicit witness, namely the half-factorial itself, rather than merely asserting | ||
| that some square root exists. -/ |
There was a problem hiding this comment.
The second part reads like straw man argument against something that doesn't exist
| /-- Let `p` be prime with `p % 4 ≠ 3`. Then `((p - 1) / 2)!` is a specified square root of `-1` | |
| in `ZMod p`. | |
| This gives an explicit witness, namely the half-factorial itself, rather than merely asserting | |
| that some square root exists. -/ | |
| /-- Let `p` be prime with `p % 4 ≠ 3`. Then `((p - 1) / 2)!` is a specified square root of `-1` | |
| in `ZMod p`. -/ |
|
Could you shorten the PR description? (And if it was written by AI, please write it yourself as per the guideline |
|
Thanks — all four applied. The You are right about the second docstring paragraph. The comparison I had in mind was with Added |
Adds an explicit square root of
-1inZMod p, together with the half-factorial pairingidentity it rests on.
ZMod.factorial_eq_neg_one_pow_mul_half_factorial_sq: for oddp, pairing each factorabove
(p - 1) / 2with its negative gives(p - 1)! = (-1)^m * (m !)^2inZMod p,where
m = (p - 1) / 2. This assumes onlyOdd p, not primality.ZMod.half_factorial_sq_eq_neg_one: for primepwithp % 4 ≠ 3,((p - 1) / 2)!is asquare root of
-1.Why
Mathlib already has the two neighbouring statements, but not this one:
ZMod.exists_sq_eq_neg_one_iffgivesIsSquare (-1 : ZMod p) ↔ p % 4 ≠ 3— existence only.ZMod.mod_four_ne_three_of_sq_eq_neg_onegoes the other way: given someywithy ^ 2 = -1,conclude
p % 4 ≠ 3.So the library can tell you a square root exists, and can use one if you already have it, but
there is no way to get from the congruence condition to a specific root. I checked all seventeen
uses of
IsSquare (-1 : ZMod _)currently in Mathlib and none of them exhibits a witness.There is at least one downstream development that reproved the pairing identity because of this:
SumOfTwoSquares/Wilson.leaninseewoo5/DifferentProofsstates it verbatim in a docstring,though only for
p ≡ 1 mod 4and with primality assumed. That seems like the usual sign that alemma belongs upstream. I do not have a consumer for it in my own work — I ran into the gap while
formalizing something adjacent and it seemed worth contributing rather than keeping local.
The pairing identity is also worth having separately: it is the core step of the standard
Wilson-theorem argument, and it holds for any odd
p, so it is not really a statement aboutprimes at all.
Placement
half_factorial_sq_eq_neg_onegoes naturally inMathlib/NumberTheory/Wilson.lean, next toZMod.wilsons_lemmawhich it uses. The pairing identity would fitMathlib/Data/ZMod/Factorial.lean,but that file carries
assert_not_exists TwoSidedIdealandwilsons_lemmapulls inFieldTheory.Finite.Basic, so the two may have to live in separate files. I have put both inWilson.leanfor now — happy to split them if that is preferred.AI disclosure
Per the AI policy: the Lean proofs were drafted with an OpenAI Codex agent, orchestrated by Claude
Code, and I reviewed the result. Before opening this I re-ran the duplicate search against current
master, against CSLib, Batteries and Lean core, acrosslanguage:leanon GitHub, and by typesignature on Loogle — the type-level query
(Nat.factorial ?n : ZMod ?p) ^ 2 = -1returns onlymod_four_ne_three_of_sq_eq_neg_one, which is the converse direction.lake build,lake exe runLinterandlake exe lint-stylewere run by hand. Please add theLLM-generatedlabel — I do not have permission to set it myself.