Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 325dbc8

Browse files
refactor(number_theory/legendre_symbol/quadratic_reciprocity.lean): change definition of legendre_sym, simplify proofs, add lemmas (#13667)
This changes the definition of `legendre_sym` to use `quadratic_char`. The proof of some of the statements can then be simplified by using the corresponding statements for quadratic characters. Some new API lemmas are added, including the fact that the Legendre symbol is multiplicative, Also, a few `simps` are squeezed in `.../quadratic_char.lean`.
1 parent 8b14d48 commit 325dbc8

File tree

3 files changed

+123
-81
lines changed

3 files changed

+123
-81
lines changed

src/data/zmod/basic.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ instance (n : ℕ) : char_p (zmod n) n :=
151151
rw [val_nat_cast, val_zero, nat.dvd_iff_mod_eq_zero],
152152
end }
153153

154+
/-- We have that `ring_char (zmod n) = n`. -/
155+
lemma ring_char_zmod_n (n : ℕ) : ring_char (zmod n) = n :=
156+
by { rw ring_char.eq_iff, exact zmod.char_p n, }
157+
154158
@[simp] lemma nat_cast_self (n : ℕ) : (n : zmod n) = 0 :=
155159
char_p.cast_eq_zero (zmod n) n
156160

src/number_theory/legendre_symbol/quadratic_char.lean

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ end
7272

7373
/-- A unit `a` of a finite field `F` of odd characteristic is a square
7474
if and only if `a ^ (#F / 2) = 1`. -/
75-
lemma unit_is_sqare_iff (hF : ring_char F ≠ 2) (a : Fˣ) :
75+
lemma unit_is_square_iff (hF : ring_char F ≠ 2) (a : Fˣ) :
7676
is_square a ↔ a ^ (fintype.card F / 2) = 1 :=
7777
begin
7878
classical,
@@ -102,7 +102,7 @@ lemma is_square_iff (hF : ring_char F ≠ 2) {a : F} (ha : a ≠ 0) :
102102
is_square a ↔ a ^ (fintype.card F / 2) = 1 :=
103103
begin
104104
apply (iff_congr _ (by simp [units.ext_iff])).mp
105-
(finite_field.unit_is_sqare_iff hF (units.mk0 a ha)),
105+
(finite_field.unit_is_square_iff hF (units.mk0 a ha)),
106106
simp only [is_square, units.ext_iff, units.coe_mk0, units.coe_mul],
107107
split, { rintro ⟨y, hy⟩, exact ⟨y, hy⟩ },
108108
{ rintro ⟨y, rfl⟩,
@@ -176,7 +176,7 @@ begin
176176
simp only [quadratic_char],
177177
by_cases ha : a = 0,
178178
{ simp only [ha, eq_self_iff_true, if_true], },
179-
{ simp [ha],
179+
{ simp only [ha, if_false, iff_false],
180180
split_ifs; simp only [neg_eq_zero, one_ne_zero, not_false_iff], },
181181
end
182182

@@ -191,7 +191,8 @@ by simp only [quadratic_char, one_ne_zero, is_square_one, if_true, if_false, id.
191191
/-- For nonzero `a : F`, `quadratic_char F a = 1 ↔ is_square a`. -/
192192
lemma quadratic_char_one_iff_is_square {a : F} (ha : a ≠ 0) :
193193
quadratic_char F a = 1 ↔ is_square a :=
194-
by { simp [quadratic_char, ha, (dec_trivial : (-1 : ℤ) ≠ 1)], tauto }
194+
by { simp only [quadratic_char, ha, (dec_trivial : (-1 : ℤ) ≠ 1), if_false, ite_eq_left_iff],
195+
tauto, }
195196

196197
/-- The quadratic character takes the value `1` on nonzero squares. -/
197198
lemma quadratic_char_sq_one' {a : F} (ha : a ≠ 0) : quadratic_char F (a ^ 2) = 1 :=
@@ -254,7 +255,7 @@ end
254255
map_mul' := quadratic_char_mul }
255256

256257
/-- The square of the quadratic character on nonzero arguments is `1`. -/
257-
lemma quadratic_char_sq_one {a : F} (ha : a ≠ 0) : (quadratic_char F a)^2 = 1 :=
258+
lemma quadratic_char_sq_one {a : F} (ha : a ≠ 0) : (quadratic_char F a) ^ 2 = 1 :=
258259
by rwa [pow_two, ← quadratic_char_mul, ← pow_two, quadratic_char_sq_one']
259260

260261
/-- The quadratic character is `1` or `-1` on nonzero arguments. -/

src/number_theory/legendre_symbol/quadratic_reciprocity.lean

Lines changed: 113 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
/-
22
Copyright (c) 2018 Chris Hughes. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
4-
Authors: Chris Hughes
4+
Authors: Chris Hughes, Michael Stoll
55
-/
6-
7-
import field_theory.finite.basic
8-
import data.zmod.basic
9-
import data.nat.parity
106
import number_theory.legendre_symbol.gauss_eisenstein_lemmas
7+
import number_theory.legendre_symbol.quadratic_char
118

129
/-!
13-
# Quadratic reciprocity.
10+
# Legendre symbol and quadratic reciprocity.
1411
1512
This file contains results about quadratic residues modulo a prime number.
1613
@@ -32,8 +29,7 @@ Also proven are conditions for `-1` and `2` to be a square modulo a prime,
3229
The proof of quadratic reciprocity implemented uses Gauss' lemma and Eisenstein's lemma
3330
-/
3431

35-
open function finset nat finite_field zmod
36-
open_locale big_operators nat
32+
open finset nat char
3733

3834
namespace zmod
3935

@@ -43,21 +39,15 @@ variables (p q : ℕ) [fact p.prime] [fact q.prime]
4339
lemma euler_criterion_units (x : (zmod p)ˣ) :
4440
(∃ y : (zmod p)ˣ, y ^ 2 = x) ↔ x ^ (p / 2) = 1 :=
4541
begin
46-
cases nat.prime.eq_two_or_odd (fact.out p.prime) with hp2 hp_odd,
47-
{ substI p, refine iff_of_true ⟨1, _⟩ _; apply subsingleton.elim },
48-
obtain ⟨g, hg⟩ := is_cyclic.exists_generator (zmod p)ˣ,
49-
obtain ⟨n, hn⟩ : x ∈ submonoid.powers g, { rw mem_powers_iff_mem_zpowers, apply hg },
50-
split,
51-
{ rintro ⟨y, rfl⟩, rw [← pow_mul, two_mul_odd_div_two hp_odd, units_pow_card_sub_one_eq_one], },
52-
{ subst x, assume h,
53-
have key : 2 * (p / 2) ∣ n * (p / 2),
54-
{ rw [← pow_mul] at h,
55-
rw [two_mul_odd_div_two hp_odd, ← card_units, ← order_of_eq_card_of_forall_mem_zpowers hg],
56-
apply order_of_dvd_of_pow_eq_one h },
57-
have : 0 < p / 2 := nat.div_pos (fact.out (1 < p)) dec_trivial,
58-
obtain ⟨m, rfl⟩ := dvd_of_mul_dvd_mul_right this key,
59-
refine ⟨g ^ m, _⟩,
60-
rw [mul_comm, pow_mul], },
42+
by_cases hc : p = 2,
43+
{ substI hc,
44+
simp only [eq_iff_true_of_subsingleton, exists_const], },
45+
{ have h₀ := finite_field.unit_is_square_iff (by rwa ring_char_zmod_n) x,
46+
have hs : (∃ y : (zmod p)ˣ, y ^ 2 = x) ↔ is_square(x) :=
47+
by { rw is_square_iff_exists_sq x,
48+
simp_rw eq_comm, },
49+
rw hs,
50+
rwa card p at h₀, },
6151
end
6252

6353
/-- Euler's Criterion: a nonzero `a : zmod p` is a square if and only if `x ^ (p / 2) = 1`. -/
@@ -116,62 +106,101 @@ lemma pow_div_two_eq_neg_one_or_one {a : zmod p} (ha : a ≠ 0) :
116106
a ^ (p / 2) = 1 ∨ a ^ (p / 2) = -1 :=
117107
begin
118108
cases nat.prime.eq_two_or_odd (fact.out p.prime) with hp2 hp_odd,
119-
{ substI p, revert a ha, exact dec_trivial },
109+
{ substI p, revert a ha, dec_trivial },
120110
rw [← mul_self_eq_one_iff, ← pow_add, ← two_mul, two_mul_odd_div_two hp_odd],
121111
exact pow_card_sub_one_eq_one ha
122112
end
123113

124-
/-- The Legendre symbol of `a` and `p`, `legendre_sym p a`, is an integer defined as
114+
/-- The Legendre symbol of `a : ℤ` and a prime `p`, `legendre_sym p a`,
115+
is an integer defined as
125116
126117
* `0` if `a` is `0` modulo `p`;
127-
* `1` if `a ^ (p / 2)` is `1` modulo `p`
128-
(by `euler_criterion` this is equivalent to “`a` is a square modulo `p`”);
118+
* `1` if `a` is a square modulo `p`
129119
* `-1` otherwise.
130120
131121
Note the order of the arguments! The advantage of the order chosen here is
132122
that `legendre_sym p` is a multiplicative function `ℤ → ℤ`.
133123
-/
134-
def legendre_sym (p : ℕ) (a : ℤ) : ℤ :=
135-
if (a : zmod p) = 0 then 0
136-
else if (a : zmod p) ^ (p / 2) = 1 then 1
137-
else -1
124+
def legendre_sym (p : ℕ) [fact p.prime] (a : ℤ) : ℤ := quadratic_char (zmod p) a
138125

126+
/-- We have the congruence `legendre_sym p a ≡ a ^ (p / 2) mod p`. -/
139127
lemma legendre_sym_eq_pow (p : ℕ) (a : ℤ) [hp : fact p.prime] :
140128
(legendre_sym p a : zmod p) = (a ^ (p / 2)) :=
141129
begin
142130
rw legendre_sym,
143131
by_cases ha : (a : zmod p) = 0,
144-
{ simp only [int.cast_coe_nat, if_pos, ha,
145-
zero_pow (nat.div_pos (hp.1.two_le) (succ_pos 1)), int.cast_zero] },
146-
cases hp.1.eq_two_or_odd with hp2 hp_odd,
132+
{ simp only [ha, zero_pow (nat.div_pos (hp.1.two_le) (succ_pos 1)), quadratic_char_zero,
133+
int.cast_zero], },
134+
by_cases hp₁ : p = 2,
147135
{ substI p,
148136
generalize : (a : (zmod 2)) = b, revert b, dec_trivial, },
149-
{ haveI := fact.mk hp_odd,
150-
rw [if_neg ha],
151-
have : (-1 : zmod p) ≠ 1, from (ne_neg_self p one_ne_zero).symm,
137+
{ have h₁ := quadratic_char_eq_pow_of_char_ne_two (by rwa ring_char_zmod_n p) ha,
138+
rw card p at h₁,
139+
rw h₁,
140+
have h₂ := finite_field.neg_one_ne_one_of_char_ne_two (by rwa ring_char_zmod_n p),
152141
cases pow_div_two_eq_neg_one_or_one p ha with h h,
153142
{ rw [if_pos h, h, int.cast_one], },
154-
{ rw [h, if_neg this, int.cast_neg, int.cast_one], } }
143+
{ rw [h, if_neg h₂, int.cast_neg, int.cast_one], } }
155144
end
156145

157-
lemma legendre_sym_eq_one_or_neg_one (p : ℕ) (a : ℤ) (ha : (a : zmod p) ≠ 0) :
158-
legendre_sym p a = -1 ∨ legendre_sym p a = 1 :=
146+
/-- If `p ∤ a`, then `legendre_sym p a` is `1` or `-1`. -/
147+
lemma legendre_sym_eq_one_or_neg_one (p : ℕ) [fact p.prime] (a : ℤ) (ha : (a : zmod p) ≠ 0) :
148+
legendre_sym p a = 1 ∨ legendre_sym p a = -1 :=
149+
quadratic_char_dichotomy ha
150+
151+
/-- The Legendre symbol of `p` and `a` is zero iff `p ∣ a`. -/
152+
lemma legendre_sym_eq_zero_iff (p : ℕ) [fact p.prime] (a : ℤ) :
153+
legendre_sym p a = 0 ↔ (a : zmod p) = 0 :=
154+
quadratic_char_eq_zero_iff a
155+
156+
@[simp] lemma legendre_sym_zero (p : ℕ) [fact p.prime] : legendre_sym p 0 = 0 :=
159157
begin
160-
unfold legendre_sym,
161-
split_ifs;
162-
simp only [*, eq_self_iff_true, or_true, true_or] at *,
158+
rw legendre_sym,
159+
exact quadratic_char_zero,
163160
end
164161

165-
lemma legendre_sym_eq_zero_iff (p : ℕ) (a : ℤ) :
166-
legendre_sym p a = 0 ↔ (a : zmod p) = 0 :=
162+
@[simp] lemma legendre_sym_one (p : ℕ) [fact p.prime] : legendre_sym p 1 = 1 :=
163+
begin
164+
rw [legendre_sym, (by norm_cast : ((1 : ℤ) : zmod p) = 1)],
165+
exact quadratic_char_one,
166+
end
167+
168+
/-- The Legendre symbol is multiplicative in `a` for `p` fixed. -/
169+
lemma legendre_sym_mul (p : ℕ) [fact p.prime] (a b : ℤ) :
170+
legendre_sym p (a * b) = legendre_sym p a * legendre_sym p b :=
167171
begin
168-
split,
169-
{ classical, contrapose,
170-
assume ha, cases legendre_sym_eq_one_or_neg_one p a ha with h h,
171-
all_goals { rw h, norm_num } },
172-
{ assume ha, rw [legendre_sym, if_pos ha] }
172+
rw [legendre_sym, legendre_sym, legendre_sym],
173+
push_cast,
174+
exact quadratic_char_mul (a : zmod p) b,
173175
end
174176

177+
/-- The Legendre symbol is a homomorphism of monoids with zero. -/
178+
@[simps] def legendre_sym_hom (p : ℕ) [fact p.prime] : ℤ →*₀ ℤ :=
179+
{ to_fun := legendre_sym p,
180+
map_zero' := legendre_sym_zero p,
181+
map_one' := legendre_sym_one p,
182+
map_mul' := legendre_sym_mul p }
183+
184+
/-- The square of the symbol is 1 if `p ∤ a`. -/
185+
theorem legendre_sym_sq_one (p : ℕ) [fact p.prime] (a : ℤ) (ha : (a : zmod p) ≠ 0) :
186+
(legendre_sym p a)^2 = 1 :=
187+
quadratic_char_sq_one ha
188+
189+
/-- The Legendre symbol of `a^2` at `p` is 1 if `p ∤ a`. -/
190+
theorem legendre_sym_sq_one' (p : ℕ) [fact p.prime] (a : ℤ) (ha : (a : zmod p) ≠ 0) :
191+
legendre_sym p (a ^ 2) = 1 :=
192+
begin
193+
rw [legendre_sym],
194+
push_cast,
195+
exact quadratic_char_sq_one' ha,
196+
end
197+
198+
/-- The Legendre symbol depends only on `a` mod `p`. -/
199+
theorem legendre_sym_mod (p : ℕ) [fact p.prime] (a : ℤ) :
200+
legendre_sym p a = legendre_sym p (a % p) :=
201+
by simp only [legendre_sym, int_cast_mod]
202+
203+
175204
/-- Gauss' lemma. The legendre symbol can be computed by considering the number of naturals less
176205
than `p/2` such that `(a * x) % p > p / 2` -/
177206
lemma gauss_lemma {a : ℤ} (hp : p ≠ 2) (ha0 : (a : zmod p) ≠ 0) :
@@ -188,14 +217,12 @@ begin
188217
simp [*, ne_neg_self p one_ne_zero, (ne_neg_self p one_ne_zero).symm] at *
189218
end
190219

220+
/-- When `p ∤ a`, then `legendre_sym p a = 1` iff `a` is a square mod `p`. -/
191221
lemma legendre_sym_eq_one_iff {a : ℤ} (ha0 : (a : zmod p) ≠ 0) :
192-
legendre_sym p a = 1 ↔ (∃ b : zmod p, b ^ 2 = a) :=
193-
begin
194-
rw [euler_criterion p ha0, legendre_sym, if_neg ha0],
195-
split_ifs,
196-
{ simp only [h, eq_self_iff_true] },
197-
{ simp only [h, iff_false], tauto }
198-
end
222+
legendre_sym p a = 1 ↔ is_square (a : zmod p) :=
223+
quadratic_char_one_iff_is_square ha0
224+
225+
open_locale big_operators
199226

200227
lemma eisenstein_lemma (hp : p ≠ 2) {a : ℕ} (ha1 : a % 2 = 1) (ha0 : (a : zmod p) ≠ 0) :
201228
legendre_sym p a = (-1)^∑ x in Ico 1 (p / 2).succ, (x * a) / p :=
@@ -252,12 +279,12 @@ begin
252279
end
253280

254281
lemma exists_sq_eq_two_iff (hp1 : p ≠ 2) :
255-
(∃ a : zmod p, a ^ 2 = 2) ↔ p % 8 = 1 ∨ p % 8 = 7 :=
256-
have hp2 : ((2 : ℤ) : zmod p) ≠ 0,
257-
from prime_ne_zero p 2 (λ h, by simpa [h] using hp1),
258-
have hpm4 : p % 4 = p % 8 % 4, from (nat.mod_mul_left_mod p 2 4).symm,
259-
have hpm2 : p % 2 = p % 8 % 2, from (nat.mod_mul_left_mod p 4 2).symm,
282+
is_square (2 : zmod p) ↔ p % 8 = 1 ∨ p % 8 = 7 :=
260283
begin
284+
have hp2 : ((2 : ℤ) : zmod p) ≠ 0,
285+
from prime_ne_zero p 2 (λ h, by simpa [h] using hp1),
286+
have hpm4 : p % 4 = p % 8 % 4, from (nat.mod_mul_left_mod p 2 4).symm,
287+
have hpm2 : p % 2 = p % 8 % 2, from (nat.mod_mul_left_mod p 4 2).symm,
261288
rw [show (2 : zmod p) = (2 : ℤ), by simp, ← legendre_sym_eq_one_iff p hp2],
262289
erw [legendre_sym_two p hp1, neg_one_pow_eq_one_iff_even (show (-1 : ℤ) ≠ 1, from dec_trivial),
263290
even_add, even_div, even_div],
@@ -270,39 +297,49 @@ begin
270297
end
271298

272299
lemma exists_sq_eq_prime_iff_of_mod_four_eq_one (hp1 : p % 4 = 1) (hq1 : q ≠ 2) :
273-
(∃ a : zmod p, a ^ 2 = q) ↔ ∃ b : zmod q, b ^ 2 = p :=
300+
is_square (q : zmod p) ↔ is_square (p : zmod q) :=
274301
if hpq : p = q then by substI hpq else
275302
have h1 : ((p / 2) * (q / 2)) % 2 = 0,
276303
from (dvd_iff_mod_eq_zero _ _).1
277304
(dvd_mul_of_dvd_left ((dvd_iff_mod_eq_zero _ _).2 $
278305
by rw [← mod_mul_right_div_self, show 2 * 2 = 4, from rfl, hp1]; refl) _),
279306
begin
280307
have hp_odd : p ≠ 2 := by { by_contra, simp [h] at hp1, norm_num at hp1, },
281-
have hpq0 : (p : zmod q) ≠ 0 := prime_ne_zero q p (ne.symm hpq),
282-
have hqp0 : (q : zmod p) ≠ 0 := prime_ne_zero p q hpq,
308+
have hpq0 : ((p : ℤ) : zmod q) ≠ 0 := prime_ne_zero q p (ne.symm hpq),
309+
have hqp0 : ((q : ℤ) : zmod p) ≠ 0 := prime_ne_zero p q hpq,
283310
have := quadratic_reciprocity p q hp_odd hq1 hpq,
284-
rw [neg_one_pow_eq_pow_mod_two, h1, legendre_sym, legendre_sym, int.cast_coe_nat,
285-
int.cast_coe_nat, if_neg hqp0, if_neg hpq0] at this,
286-
rw [euler_criterion q hpq0, euler_criterion p hqp0],
287-
split_ifs at this; simp *; contradiction,
311+
rw [neg_one_pow_eq_pow_mod_two, h1, pow_zero] at this,
312+
rw [(by norm_cast : (p : zmod q) = (p : ℤ)), (by norm_cast : (q : zmod p) = (q : ℤ)),
313+
← legendre_sym_eq_one_iff _ hpq0, ← legendre_sym_eq_one_iff _ hqp0],
314+
cases (legendre_sym_eq_one_or_neg_one p q hqp0) with h h,
315+
{ simp only [h, eq_self_iff_true, true_iff, mul_one] at this ⊢,
316+
exact this, },
317+
{ simp only [h, mul_neg, mul_one] at this ⊢,
318+
rw eq_neg_of_eq_neg this.symm, },
288319
end
289320

290321
lemma exists_sq_eq_prime_iff_of_mod_four_eq_three (hp3 : p % 4 = 3)
291-
(hq3 : q % 4 = 3) (hpq : p ≠ q) : (∃ a : zmod p, a ^ 2 = q) ↔ ¬∃ b : zmod q, b ^ 2 = p :=
322+
(hq3 : q % 4 = 3) (hpq : p ≠ q) : is_square (q : zmod p) ↔ ¬ is_square (p : zmod q) :=
292323
have h1 : ((p / 2) * (q / 2)) % 2 = 1,
293324
from nat.odd_mul_odd
294325
(by rw [← mod_mul_right_div_self, show 2 * 2 = 4, from rfl, hp3]; refl)
295326
(by rw [← mod_mul_right_div_self, show 2 * 2 = 4, from rfl, hq3]; refl),
296327
begin
297328
have hp_odd : p ≠ 2 := by { by_contra, simp [h] at hp3, norm_num at hp3, },
298329
have hq_odd : q ≠ 2 := by { by_contra, simp [h] at hq3, norm_num at hq3, },
299-
have hpq0 : (p : zmod q) ≠ 0 := prime_ne_zero q p (ne.symm hpq),
300-
have hqp0 : (q : zmod p) ≠ 0 := prime_ne_zero p q hpq,
330+
have hpq0 : ((p : ℤ) : zmod q) ≠ 0 := prime_ne_zero q p (ne.symm hpq),
331+
have hqp0 : ((q : ℤ) : zmod p) ≠ 0 := prime_ne_zero p q hpq,
301332
have := quadratic_reciprocity p q hp_odd hq_odd hpq,
302-
rw [neg_one_pow_eq_pow_mod_two, h1, legendre_sym, legendre_sym, int.cast_coe_nat,
303-
int.cast_coe_nat, if_neg hpq0, if_neg hqp0] at this,
304-
rw [euler_criterion q hpq0, euler_criterion p hqp0],
305-
split_ifs at this; simp *; contradiction
333+
rw [neg_one_pow_eq_pow_mod_two, h1, pow_one] at this,
334+
rw [(by norm_cast : (p : zmod q) = (p : ℤ)), (by norm_cast : (q : zmod p) = (q : ℤ)),
335+
← legendre_sym_eq_one_iff _ hpq0, ← legendre_sym_eq_one_iff _ hqp0],
336+
cases (legendre_sym_eq_one_or_neg_one q p hpq0) with h h,
337+
{ simp only [h, eq_self_iff_true, not_true, iff_false, one_mul] at this ⊢,
338+
simp only [this],
339+
norm_num, },
340+
{ simp only [h, neg_mul, one_mul, neg_inj] at this ⊢,
341+
simp only [this, eq_self_iff_true, true_iff],
342+
norm_num, },
306343
end
307344

308345
end zmod

0 commit comments

Comments
 (0)