Infinitude of Primes - Folklore proof
Acknowledgements. Yves Etienne Jäckle previously implemented a version of this proof in Lean 3 as part of his Master’s thesis. Yaël Dillies ported that code to Lean 4. Moritz Firsching also independently implemented a version. The code presented here was implemented from scratch to more closely follow the natural language version of the proof as presented by Martin Aigner and Günter Ziegler in their book ‘Proofs from THE BOOK’. Thanks to Jireh Loreaux and Eric Rodriguez for casting some lights in the intricacies of Nat.cast
on Zulip.
The next proof takes an algebraic turn, deriving a contradiction from the assumption that there exists a largest prime through Lagrange’s theorem.
The sentence “consider the so-called Mersenne number \(2^p - 1\) and show that any prime factor \(q\) of \(2^p - 1\) is bigger than \(p\)” indicates that we first establish the following lemma, from which the actual infinitude of primes will later be derived.
1 |
lemma mersenne_prime_dvd_gt (p q : ℕ) [p_prime: Fact (Nat.Prime p)] [q_prime : Fact (Nat.Prime q)] (h : q ∣ 2^p - 1) : p < q := by |
Note that Fact
is simply a neat way of letting Lean infer an assumption when applying a statement, so that you can just call some_theorem p
instead of some_theorem p p_prime
. We will use several statements that will automatically infer the info that p
and q
are prime, but we will also encounter statements that want to be given this info explicitly, in which case we simply turn the Fact
back into its underlying assumption through p_prime.out
or, more explicitly, Fact.out p_prime
. In order to create a fact from an assumption, one can use Fact.mk
.
The assumptions [q_prime: Fact (Nat.Prime q)]
and (h : q ∣ 2^p - 1)
express “(l)et \(q\) be a prime dividing \(2^p - 1\)” and the consequence “we have \(2^p \equiv 1 \: (\operatorname{mod} \, q)\)” follows from Nat.modEq_iff_dvd'
. The Nat
indicates this lemma applies to natural numbers (rather than integers) and indeed the expression 2^p - 1
also referred to subtraction in the natural numbers that saturates at zero. Thankfully this is not a problem, since \(2^p \geq 1\) due to Nat.one_le_two_pow
.
1 |
have two_pow_p_eq_one_mod_q : 2^p ≡ 1 [MOD q] := ((λ x ↦ (Nat.modEq_iff_dvd' x).mpr) (Nat.one_le_two_pow) h).symm |
Next up, the proof states that “(s)ince \(p\) is prime, this means that the element \(2\) has order \(p\) in the multiplicative group \(\mathbb{Z}_q \setminus \{0\}\) of the field \(\mathbb{Z}_q\).” Fortunately, the orderOf_eq_prime
lemma in mathlib directly supports this argument.
1 2 3 |
have two_pow_p_eq_one := ((ZMod.eq_iff_modEq_nat q).mpr two_pow_p_eq_one_mod_q) rw [Nat.cast_pow, Nat.cast_one] at two_pow_p_eq_one have order_two_eq_p := orderOf_eq_prime two_pow_p_eq_one (by apply sub_eq_zero.not.mp; norm_num) |
There are some nuances here: the first is that orderOf_eq_prime
also requires a proof that \(2 \ne 1\) in \(\mathbb{Z}_q\), which we take care of through (by apply sub_eq_zero.not.mp; norm_num)
. The second is that two_pow_p_eq_one_mod_q
was a statement about natural numbers 2
and 1
, but we need a statement that \(2^p = 1\) in \(\mathbb{Z}_q\). ZMod.eq_iff_modEq_nat
takes us most of the way there, but it takes 2^p
as an integer and casts it in its entirety to \(\mathbb{Z}_q\). This is expressed in the Infoview through ↑(2 ^ p)
. However, what we actually want is the p
-th exponent of the element 2
in \(\mathbb{Z}_q\). Rewriting with Nat.cast_pow
gives us exactly that and turns the ↑(2 ^ p)
into an ↑2 ^ p
. However, one tiny issue remains: mathlib right now does not understand that ↑1
is also the multiplicative unit of \(\mathbb{Z}_q\). This important for orderOf_eq_prime
, so we rewrite ↑1
to 1
using Nat.cast_one
.
The proof continues by stating that “(t)his group has \(q-1\) elements.” and “(b)y Lagrange’s theorem […] we know that the order of every element divides the size of the group, that is, we have \(p \mid q-1\)”. Thankfully we do not have to go all the way to Lagrange’s theorem, since there is a lemma in mathlib in the form of ZMod.orderOf_dvd_card_sub_one
stating this very fact.1 We just again need to supply it with a small, easily overlooked detail: we must have \(2 \ne 0\) in \(\mathbb{Z}_q\) (two_ne_zero
). This is of course the case as long as \(q \ne 2\), which somewhat cumbersomely derive from h : q ∣ 2^p - 1
.
1 2 3 4 5 6 7 |
have two_ne_zero : (2 : ZMod q) ≠ 0 := by by_contra contra have q_lq_two : q ≤ 2 := (Nat.Prime.dvd_factorial q_prime.out).mp ((ZMod.nat_cast_zmod_eq_zero_iff_dvd 2 q).mp contra) have q_gt_two : q > 2 := Nat.lt_of_le_of_ne (Nat.Prime.two_le q_prime.out) (Odd.ne_two_of_dvd_nat (Nat.Even.sub_odd (Nat.one_le_two_pow) ( even_iff_two_dvd.mpr (dvd_pow_self 2 (Nat.Prime.ne_zero p_prime.out))) (Exists.intro Nat.zero rfl)) h).symm exact (Nat.not_lt.mpr q_lq_two) q_gt_two have p_dvd_q_minus_1 : p ∣ (q - 1) := order_two_eq_p ▸ (ZMod.orderOf_dvd_card_sub_one two_ne_zero) |
Finally, we conclude the proof of our main lemma by deriving that “\(p < q\)”.
1 |
exact Nat.lt_of_le_pred (Nat.Prime.pos q_prime.out) (Nat.le_of_dvd (Nat.sub_pos_of_lt (Nat.Prime.two_le q_prime.out)) p_dvd_q_minus_1) |
This however does not conclude the actual proof of the infinitude of primes. The proof simply suggests we “(s)uppose \(\mathbb{P}\) is finite and \(p\) is the largest prime” and use the previous lemma to derive a contradiction. Let us do just that! We start by defining P
as the set of primes and supposing that it is finite using by_contra
, which allows us to define p
as its maximum
1 2 3 4 5 6 7 8 |
theorem infinitude_of_primes : { p : ℕ | Nat.Prime p}.Infinite := by by_contra contra have : Fintype { p : ℕ | Nat.Prime p} := Set.Finite.fintype (Set.not_infinite.mp contra) let P := { p : ℕ | Nat.Prime p}.toFinset let P_nonenmpty : P.Nonempty := (Set.nonempty_of_mem (Set.mem_toFinset.mpr Nat.prime_two)) let p := Finset.max' P P_nonenmpty have p_in_P : p ∈ P := Finset.max'_mem P P_nonenmpty have p_prime : Nat.Prime p := by simp [P] at p_in_P; exact p_in_P |
Using this, we let \(q\) be an arbitrary prime divisor of \(2^p-1\), after establishing that \(2^p - 1 \neq 1\) (two_pow_p_minus_one_ne_one
), and apply mersenne_prime_dvd_gt
to obtain the fact that \(q > p\).
1 2 3 |
have two_pow_p_minus_one_ne_one : 2^p - 1 ≠ 1 := Nat.ne_of_gt (Nat.lt_sub_of_add_lt ((@Nat.size_le 2 p).mp (Nat.Prime.two_le p_prime))) let ⟨q, q_prime, q_dvd⟩ := Nat.exists_prime_and_dvd two_pow_p_minus_one_ne_one have q_gt_p : q > p := @mersenne_prime_dvd_gt p q (Fact.mk p_prime) (Fact.mk q_prime) q_dvd |
But this is of course a contradiction, since \(p\) was the largest prime and \(q\) is prime, so we must also have \(q \leq p\).
1 2 |
have q_lt_p : q ≤ p := Finset.le_max' P q (Set.mem_toFinset.mpr q_prime) exact Nat.lt_irrefl p (Nat.lt_of_lt_of_le q_gt_p q_lt_p) |
This concludes the proof. 🎉
-
If this feels like cheating, check out Moritz Firsching’s version of this proof which consciously uses mathlib’s version of Lagrange’s theorem. ↩