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

Commit fbff76b

Browse files
refactor(number_theory/legendre_symbol/): move Gauss/Eisenstein lemma code to separate file (#13449)
In preparation of further changes to number_theory/legendre_symbol/quadratic_reciprocity, this takes most of the code dealing with the lemmas of Gauss and Eisenstein out of quadratic_reciprocity.lean into a new file gauss_eisenstein_lemmas.lean. Since I am not planning to do much (if anything) to this part of the code and it is rather involved and slows down Lean when I'm editing quadratic_reciprocity.lean, it makes sense to separate this code from the remainder of the file. Co-authored-by: Oliver Nash <github@olivernash.org>
1 parent 0c2d68a commit fbff76b

File tree

2 files changed

+287
-254
lines changed

2 files changed

+287
-254
lines changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/-
2+
Copyright (c) 2018 Chris Hughes. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Chris Hughes
5+
-/
6+
import field_theory.finite.basic
7+
import data.zmod.basic
8+
9+
/-!
10+
# Lemmas of Gauss and Eisenstein
11+
12+
This file contains code for the proof of the Lemmas of Gauss and Eisenstein
13+
on the Legendre symbol. The main results are `gauss_lemma_aux` and
14+
`eisenstein_lemma_aux`; they are used in `quadratic_reciprocity.lean`
15+
to prove `gauss_lemma` and `eisenstein_lemma`, respectively.
16+
-/
17+
18+
open function finset nat finite_field zmod
19+
open_locale big_operators nat
20+
21+
namespace zmod
22+
23+
section wilson
24+
25+
variables (p : ℕ) [fact p.prime]
26+
27+
-- One can probably deduce the following from `finite_field.prod_univ_units_id_eq_neg_one`
28+
/-- **Wilson's Lemma**: the product of `1`, ..., `p-1` is `-1` modulo `p`. -/
29+
@[simp] lemma wilsons_lemma : ((p - 1)! : zmod p) = -1 :=
30+
begin
31+
refine
32+
calc ((p - 1)! : zmod p) = (∏ x in Ico 1 (succ (p - 1)), x) :
33+
by rw [← finset.prod_Ico_id_eq_factorial, prod_nat_cast]
34+
... = (∏ x : (zmod p)ˣ, x) : _
35+
... = -1 : by simp_rw [← units.coe_hom_apply,
36+
← (units.coe_hom (zmod p)).map_prod, prod_univ_units_id_eq_neg_one, units.coe_hom_apply,
37+
units.coe_neg, units.coe_one],
38+
have hp : 0 < p := (fact.out p.prime).pos,
39+
symmetry,
40+
refine prod_bij (λ a _, (a : zmod p).val) _ _ _ _,
41+
{ intros a ha,
42+
rw [mem_Ico, ← nat.succ_sub hp, nat.succ_sub_one],
43+
split,
44+
{ apply nat.pos_of_ne_zero, rw ← @val_zero p,
45+
assume h, apply units.ne_zero a (val_injective p h) },
46+
{ exact val_lt _ } },
47+
{ intros a ha, simp only [cast_id, nat_cast_val], },
48+
{ intros _ _ _ _ h, rw units.ext_iff, exact val_injective p h },
49+
{ intros b hb,
50+
rw [mem_Ico, nat.succ_le_iff, ← succ_sub hp, succ_sub_one, pos_iff_ne_zero] at hb,
51+
refine ⟨units.mk0 b _, finset.mem_univ _, _⟩,
52+
{ assume h, apply hb.1, apply_fun val at h,
53+
simpa only [val_cast_of_lt hb.right, val_zero] using h },
54+
{ simp only [val_cast_of_lt hb.right, units.coe_mk0], } }
55+
end
56+
57+
@[simp] lemma prod_Ico_one_prime : (∏ x in Ico 1 p, (x : zmod p)) = -1 :=
58+
begin
59+
conv in (Ico 1 p) { rw [← succ_sub_one p, succ_sub (fact.out p.prime).pos] },
60+
rw [← prod_nat_cast, finset.prod_Ico_id_eq_factorial, wilsons_lemma]
61+
end
62+
63+
end wilson
64+
65+
end zmod
66+
67+
section gauss_eisenstein
68+
69+
namespace legendre_symbol
70+
71+
/-- The image of the map sending a non zero natural number `x ≤ p / 2` to the absolute value
72+
of the element of interger in the interval `(-p/2, p/2]` congruent to `a * x` mod p is the set
73+
of non zero natural numbers `x` such that `x ≤ p / 2` -/
74+
lemma Ico_map_val_min_abs_nat_abs_eq_Ico_map_id
75+
(p : ℕ) [hp : fact p.prime] (a : zmod p) (hap : a ≠ 0) :
76+
(Ico 1 (p / 2).succ).1.map (λ x, (a * x).val_min_abs.nat_abs) =
77+
(Ico 1 (p / 2).succ).1.map (λ a, a) :=
78+
begin
79+
have he : ∀ {x}, x ∈ Ico 1 (p / 2).succ → x ≠ 0 ∧ x ≤ p / 2,
80+
by simp [nat.lt_succ_iff, nat.succ_le_iff, pos_iff_ne_zero] {contextual := tt},
81+
have hep : ∀ {x}, x ∈ Ico 1 (p / 2).succ → x < p,
82+
from λ x hx, lt_of_le_of_lt (he hx).2 (nat.div_lt_self hp.1.pos dec_trivial),
83+
have hpe : ∀ {x}, x ∈ Ico 1 (p / 2).succ → ¬ p ∣ x,
84+
from λ x hx hpx, not_lt_of_ge (le_of_dvd (nat.pos_of_ne_zero (he hx).1) hpx) (hep hx),
85+
have hmem : ∀ (x : ℕ) (hx : x ∈ Ico 1 (p / 2).succ),
86+
(a * x : zmod p).val_min_abs.nat_abs ∈ Ico 1 (p / 2).succ,
87+
{ assume x hx,
88+
simp [hap, char_p.cast_eq_zero_iff (zmod p) p, hpe hx, lt_succ_iff, succ_le_iff,
89+
pos_iff_ne_zero, nat_abs_val_min_abs_le _], },
90+
have hsurj : ∀ (b : ℕ) (hb : b ∈ Ico 1 (p / 2).succ),
91+
∃ x ∈ Ico 1 (p / 2).succ, b = (a * x : zmod p).val_min_abs.nat_abs,
92+
{ assume b hb,
93+
refine ⟨(b / a : zmod p).val_min_abs.nat_abs, mem_Ico.mpr ⟨_, _⟩, _⟩,
94+
{ apply nat.pos_of_ne_zero,
95+
simp only [div_eq_mul_inv, hap, char_p.cast_eq_zero_iff (zmod p) p, hpe hb, not_false_iff,
96+
val_min_abs_eq_zero, inv_eq_zero, int.nat_abs_eq_zero, ne.def, mul_eq_zero, or_self] },
97+
{ apply lt_succ_of_le, apply nat_abs_val_min_abs_le },
98+
{ rw nat_cast_nat_abs_val_min_abs,
99+
split_ifs,
100+
{ erw [mul_div_cancel' _ hap, val_min_abs_def_pos, val_cast_of_lt (hep hb),
101+
if_pos (le_of_lt_succ (mem_Ico.1 hb).2), int.nat_abs_of_nat], },
102+
{ erw [mul_neg, mul_div_cancel' _ hap, nat_abs_val_min_abs_neg,
103+
val_min_abs_def_pos, val_cast_of_lt (hep hb), if_pos (le_of_lt_succ (mem_Ico.1 hb).2),
104+
int.nat_abs_of_nat] } } },
105+
exact multiset.map_eq_map_of_bij_of_nodup _ _ (finset.nodup _) (finset.nodup _)
106+
(λ x _, (a * x : zmod p).val_min_abs.nat_abs) hmem (λ _ _, rfl)
107+
(inj_on_of_surj_on_of_card_le _ hmem hsurj le_rfl) hsurj
108+
end
109+
110+
private lemma gauss_lemma_aux₁ (p : ℕ) [fact p.prime] [fact (p % 2 = 1)]
111+
{a : ℤ} (hap : (a : zmod p) ≠ 0) :
112+
(a^(p / 2) * (p / 2)! : zmod p) =
113+
(-1)^((Ico 1 (p / 2).succ).filter
114+
(λ x : ℕ, ¬(a * x : zmod p).val ≤ p / 2)).card * (p / 2)! :=
115+
calc (a ^ (p / 2) * (p / 2)! : zmod p) =
116+
(∏ x in Ico 1 (p / 2).succ, a * x) :
117+
by rw [prod_mul_distrib, ← prod_nat_cast, prod_Ico_id_eq_factorial,
118+
prod_const, card_Ico, succ_sub_one]; simp
119+
... = (∏ x in Ico 1 (p / 2).succ, (a * x : zmod p).val) : by simp
120+
... = (∏ x in Ico 1 (p / 2).succ,
121+
(if (a * x : zmod p).val ≤ p / 2 then 1 else -1) *
122+
(a * x : zmod p).val_min_abs.nat_abs) :
123+
prod_congr rfl $ λ _ _, begin
124+
simp only [nat_cast_nat_abs_val_min_abs],
125+
split_ifs; simp
126+
end
127+
... = (-1)^((Ico 1 (p / 2).succ).filter
128+
(λ x : ℕ, ¬(a * x : zmod p).val ≤ p / 2)).card *
129+
(∏ x in Ico 1 (p / 2).succ, (a * x : zmod p).val_min_abs.nat_abs) :
130+
have (∏ x in Ico 1 (p / 2).succ,
131+
if (a * x : zmod p).val ≤ p / 2 then (1 : zmod p) else -1) =
132+
(∏ x in (Ico 1 (p / 2).succ).filter
133+
(λ x : ℕ, ¬(a * x : zmod p).val ≤ p / 2), -1),
134+
from prod_bij_ne_one (λ x _ _, x)
135+
(λ x, by split_ifs; simp * at * {contextual := tt})
136+
(λ _ _ _ _ _ _, id)
137+
(λ b h _, ⟨b, by simp [-not_le, *] at *⟩)
138+
(by intros; split_ifs at *; simp * at *),
139+
by rw [prod_mul_distrib, this]; simp
140+
... = (-1)^((Ico 1 (p / 2).succ).filter
141+
(λ x : ℕ, ¬(a * x : zmod p).val ≤ p / 2)).card * (p / 2)! :
142+
by rw [← prod_nat_cast, finset.prod_eq_multiset_prod,
143+
Ico_map_val_min_abs_nat_abs_eq_Ico_map_id p a hap,
144+
← finset.prod_eq_multiset_prod, prod_Ico_id_eq_factorial]
145+
146+
lemma gauss_lemma_aux (p : ℕ) [hp : fact p.prime] [fact (p % 2 = 1)]
147+
{a : ℤ} (hap : (a : zmod p) ≠ 0) :
148+
(a^(p / 2) : zmod p) = (-1)^((Ico 1 (p / 2).succ).filter
149+
(λ x : ℕ, p / 2 < (a * x : zmod p).val)).card :=
150+
(mul_left_inj'
151+
(show ((p / 2)! : zmod p) ≠ 0,
152+
by rw [ne.def, char_p.cast_eq_zero_iff (zmod p) p, hp.1.dvd_factorial, not_le];
153+
exact nat.div_lt_self hp.1.pos dec_trivial)).1 $
154+
by simpa using gauss_lemma_aux₁ p hap
155+
156+
private lemma eisenstein_lemma_aux₁ (p : ℕ) [fact p.prime] [hp2 : fact (p % 2 = 1)]
157+
{a : ℕ} (hap : (a : zmod p) ≠ 0) :
158+
((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : zmod 2) =
159+
((Ico 1 (p / 2).succ).filter
160+
((λ x : ℕ, p / 2 < (a * x : zmod p).val))).card +
161+
∑ x in Ico 1 (p / 2).succ, x
162+
+ (∑ x in Ico 1 (p / 2).succ, (a * x) / p : ℕ) :=
163+
have hp2 : (p : zmod 2) = (1 : ℕ), from (eq_iff_modeq_nat _).2 hp2.1,
164+
calc ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : zmod 2)
165+
= ((∑ x in Ico 1 (p / 2).succ, ((a * x) % p + p * ((a * x) / p)) : ℕ) : zmod 2) :
166+
by simp only [mod_add_div]
167+
... = (∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : zmod p).val : ℕ) +
168+
(∑ x in Ico 1 (p / 2).succ, (a * x) / p : ℕ) :
169+
by simp only [val_nat_cast];
170+
simp [sum_add_distrib, mul_sum.symm, nat.cast_add, nat.cast_mul, nat.cast_sum, hp2]
171+
... = _ : congr_arg2 (+)
172+
(calc ((∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : zmod p).val : ℕ) : zmod 2)
173+
= ∑ x in Ico 1 (p / 2).succ,
174+
((((a * x : zmod p).val_min_abs +
175+
(if (a * x : zmod p).val ≤ p / 2 then 0 else p)) : ℤ) : zmod 2) :
176+
by simp only [(val_eq_ite_val_min_abs _).symm]; simp [nat.cast_sum]
177+
... = ((Ico 1 (p / 2).succ).filter
178+
(λ x : ℕ, p / 2 < (a * x : zmod p).val)).card +
179+
((∑ x in Ico 1 (p / 2).succ, (a * x : zmod p).val_min_abs.nat_abs) : ℕ) :
180+
by { simp [ite_cast, add_comm, sum_add_distrib, finset.sum_ite, hp2, nat.cast_sum], }
181+
... = _ : by rw [finset.sum_eq_multiset_sum,
182+
Ico_map_val_min_abs_nat_abs_eq_Ico_map_id p a hap,
183+
← finset.sum_eq_multiset_sum];
184+
simp [nat.cast_sum]) rfl
185+
186+
lemma eisenstein_lemma_aux (p : ℕ) [fact p.prime] [fact (p % 2 = 1)]
187+
{a : ℕ} (ha2 : a % 2 = 1) (hap : (a : zmod p) ≠ 0) :
188+
((Ico 1 (p / 2).succ).filter
189+
((λ x : ℕ, p / 2 < (a * x : zmod p).val))).card
190+
≡ ∑ x in Ico 1 (p / 2).succ, (x * a) / p [MOD 2] :=
191+
have ha2 : (a : zmod 2) = (1 : ℕ), from (eq_iff_modeq_nat _).2 ha2,
192+
(eq_iff_modeq_nat 2).1 $ sub_eq_zero.1 $
193+
by simpa [add_left_comm, sub_eq_add_neg, finset.mul_sum.symm, mul_comm, ha2, nat.cast_sum,
194+
add_neg_eq_iff_eq_add.symm, neg_eq_self_mod_two, add_assoc]
195+
using eq.symm (eisenstein_lemma_aux₁ p hap)
196+
197+
lemma div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) : a / b =
198+
((Ico 1 c.succ).filter (λ x, x * b ≤ a)).card :=
199+
calc a / b = (Ico 1 (a / b).succ).card : by simp
200+
... = ((Ico 1 c.succ).filter (λ x, x * b ≤ a)).card :
201+
congr_arg _ $ finset.ext $ λ x,
202+
have x * b ≤ a → x ≤ c,
203+
from λ h, le_trans (by rwa [le_div_iff_mul_le _ _ hb0]) hc,
204+
by simp [lt_succ_iff, le_div_iff_mul_le _ _ hb0]; tauto
205+
206+
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
207+
rectangle `(0, p/2) × (0, q/2)` -/
208+
private lemma sum_Ico_eq_card_lt {p q : ℕ} :
209+
∑ a in Ico 1 (p / 2).succ, (a * q) / p =
210+
(((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
211+
(λ x : ℕ × ℕ, x.2 * p ≤ x.1 * q)).card :=
212+
if hp0 : p = 0 then by simp [hp0, finset.ext_iff]
213+
else
214+
calc ∑ a in Ico 1 (p / 2).succ, (a * q) / p =
215+
∑ a in Ico 1 (p / 2).succ,
216+
((Ico 1 (q / 2).succ).filter (λ x, x * p ≤ a * q)).card :
217+
finset.sum_congr rfl $ λ x hx,
218+
div_eq_filter_card (nat.pos_of_ne_zero hp0)
219+
(calc x * q / p ≤ (p / 2) * q / p :
220+
nat.div_le_div_right (mul_le_mul_of_nonneg_right
221+
(le_of_lt_succ $ (mem_Ico.mp hx).2)
222+
(nat.zero_le _))
223+
... ≤ _ : nat.div_mul_div_le_div _ _ _)
224+
... = _ : by rw [← card_sigma];
225+
exact card_congr (λ a _, ⟨a.1, a.2⟩)
226+
(by simp only [mem_filter, mem_sigma, and_self, forall_true_iff, mem_product]
227+
{contextual := tt})
228+
(λ ⟨_, _⟩ ⟨_, _⟩, by simp only [prod.mk.inj_iff, eq_self_iff_true, and_self, heq_iff_eq,
229+
forall_true_iff] {contextual := tt})
230+
(λ ⟨b₁, b₂⟩ h, ⟨⟨b₁, b₂⟩,
231+
by revert h; simp only [mem_filter, eq_self_iff_true, exists_prop_of_true, mem_sigma,
232+
and_self, forall_true_iff, mem_product] {contextual := tt}⟩)
233+
234+
/-- Each of the sums in this lemma is the cardinality of the set integer points in each of the
235+
two triangles formed by the diagonal of the rectangle `(0, p/2) × (0, q/2)`. Adding them
236+
gives the number of points in the rectangle. -/
237+
lemma sum_mul_div_add_sum_mul_div_eq_mul (p q : ℕ) [hp : fact p.prime]
238+
(hq0 : (q : zmod p) ≠ 0) :
239+
∑ a in Ico 1 (p / 2).succ, (a * q) / p +
240+
∑ a in Ico 1 (q / 2).succ, (a * p) / q =
241+
(p / 2) * (q / 2) :=
242+
begin
243+
have hswap : (((Ico 1 (q / 2).succ).product (Ico 1 (p / 2).succ)).filter
244+
(λ x : ℕ × ℕ, x.2 * q ≤ x.1 * p)).card =
245+
(((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
246+
(λ x : ℕ × ℕ, x.1 * q ≤ x.2 * p)).card :=
247+
card_congr (λ x _, prod.swap x)
248+
(λ ⟨_, _⟩, by simp only [mem_filter, and_self, prod.swap_prod_mk, forall_true_iff, mem_product]
249+
{contextual := tt})
250+
(λ ⟨_, _⟩ ⟨_, _⟩, by simp only [prod.mk.inj_iff, eq_self_iff_true, and_self, prod.swap_prod_mk,
251+
forall_true_iff] {contextual := tt})
252+
(λ ⟨x₁, x₂⟩ h, ⟨⟨x₂, x₁⟩, by revert h; simp only [mem_filter, eq_self_iff_true, and_self,
253+
exists_prop_of_true, prod.swap_prod_mk, forall_true_iff, mem_product] {contextual := tt}⟩),
254+
have hdisj : disjoint
255+
(((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
256+
(λ x : ℕ × ℕ, x.2 * p ≤ x.1 * q))
257+
(((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
258+
(λ x : ℕ × ℕ, x.1 * q ≤ x.2 * p)),
259+
{ apply disjoint_filter.2 (λ x hx hpq hqp, _),
260+
have hxp : x.1 < p, from lt_of_le_of_lt
261+
(show x.1 ≤ p / 2, by simp only [*, lt_succ_iff, mem_Ico, mem_product] at *; tauto)
262+
(nat.div_lt_self hp.1.pos dec_trivial),
263+
have : (x.1 : zmod p) = 0,
264+
{ simpa [hq0] using congr_arg (coe : ℕ → zmod p) (le_antisymm hpq hqp) },
265+
apply_fun zmod.val at this,
266+
rw [val_cast_of_lt hxp, val_zero] at this,
267+
simpa only [this, nonpos_iff_eq_zero, mem_Ico, one_ne_zero, false_and, mem_product] using hx },
268+
have hunion : ((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
269+
(λ x : ℕ × ℕ, x.2 * p ≤ x.1 * q) ∪
270+
((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)).filter
271+
(λ x : ℕ × ℕ, x.1 * q ≤ x.2 * p) =
272+
((Ico 1 (p / 2).succ).product (Ico 1 (q / 2).succ)),
273+
from finset.ext (λ x, by have := le_total (x.2 * p) (x.1 * q);
274+
simp only [mem_union, mem_filter, mem_Ico, mem_product]; tauto),
275+
rw [sum_Ico_eq_card_lt, sum_Ico_eq_card_lt, hswap, ← card_disjoint_union hdisj, hunion,
276+
card_product],
277+
simp only [card_Ico, tsub_zero, succ_sub_succ_eq_sub]
278+
end
279+
280+
end legendre_symbol
281+
282+
end gauss_eisenstein

0 commit comments

Comments
 (0)