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

Commit b2d95c0

Browse files
committed
feat(data/nat/modeq): Generalised version of the Chinese remainder theorem (#5683)
That allows the moduli to not be coprime, assuming the necessary condition. Old crt is now in terms of this one
1 parent 8b6f541 commit b2d95c0

File tree

5 files changed

+94
-21
lines changed

5 files changed

+94
-21
lines changed

src/algebra/euclidean_domain.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ def gcd_a (x y : R) : R := (xgcd x y).1
230230
/-- The extended GCD `b` value in the equation `gcd x y = x * a + y * b`. -/
231231
def gcd_b (x y : R) : R := (xgcd x y).2
232232

233+
@[simp] theorem gcd_a_zero_left {s : R} : gcd_a 0 s = 0 :=
234+
by { unfold gcd_a, rw [xgcd, xgcd_zero_left] }
235+
236+
@[simp] theorem gcd_b_zero_left {s : R} : gcd_b 0 s = 1 :=
237+
by { unfold gcd_b, rw [xgcd, xgcd_zero_left] }
238+
233239
@[simp] theorem xgcd_aux_fst (x y : R) : ∀ s t s' t',
234240
(xgcd_aux x s t y s' t').1 = gcd x y :=
235241
gcd.induction x y (by intros; rw [xgcd_zero_left, gcd_zero_left])

src/data/int/basic.lean

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,9 @@ protected theorem mul_div_assoc (a : ℤ) : ∀ {b c : ℤ}, c ∣ b → (a * b)
699699
| ._ c ⟨d, rfl⟩ := if cz : c = 0 then by simp [cz] else
700700
by rw [mul_left_comm, int.mul_div_cancel_left _ cz, int.mul_div_cancel_left _ cz]
701701

702+
protected theorem mul_div_assoc' (b : ℤ) {a c : ℤ} (h : c ∣ a) : a * b / c = a / c * b :=
703+
by rw [mul_comm, int.mul_div_assoc _ h, mul_comm]
704+
702705
theorem div_dvd_div : ∀ {a b c : ℤ} (H1 : a ∣ b) (H2 : b ∣ c), b / a ∣ c / a
703706
| a ._ ._ ⟨b, rfl⟩ ⟨c, rfl⟩ := if az : a = 0 then by simp [az] else
704707
by rw [int.mul_div_cancel_left _ az, mul_assoc, int.mul_div_cancel_left _ az];
@@ -736,6 +739,16 @@ theorem neg_div_of_dvd : ∀ {a b : ℤ} (H : b ∣ a), -a / b = -(a / b)
736739
| ._ b ⟨c, rfl⟩ := if bz : b = 0 then by simp [bz] else
737740
by rw [neg_mul_eq_mul_neg, int.mul_div_cancel_left _ bz, int.mul_div_cancel_left _ bz]
738741

742+
lemma sub_div_of_dvd {a b c : ℤ} (hcb : c ∣ b) : (a - b) / c = a / c - b / c :=
743+
begin
744+
rw [sub_eq_add_neg, sub_eq_add_neg, int.add_div_of_dvd_right ((dvd_neg c b).mpr hcb)],
745+
congr,
746+
exact neg_div_of_dvd hcb,
747+
end
748+
749+
lemma sub_div_of_dvd_sub {a b c : ℤ} (hcab : c ∣ (a - b)) : (a - b) / c = a / c - b / c :=
750+
by rw [eq_sub_iff_add_eq, ← int.add_div_of_dvd_left hcab, sub_add_cancel]
751+
739752
theorem div_sign : ∀ a b, a / sign b = a * sign b
740753
| a (n+1:ℕ) := by unfold sign; simp
741754
| a 0 := by simp [sign]

src/data/int/gcd.lean

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,28 @@ def gcd_a (x y : ℕ) : ℤ := (xgcd x y).1
4646
/-- The extended GCD `b` value in the equation `gcd x y = x * a + y * b`. -/
4747
def gcd_b (x y : ℕ) : ℤ := (xgcd x y).2
4848

49+
@[simp] theorem gcd_a_zero_left {s : ℕ} : gcd_a 0 s = 0 :=
50+
by { unfold gcd_a, rw [xgcd, xgcd_zero_left] }
51+
52+
@[simp] theorem gcd_b_zero_left {s : ℕ} : gcd_b 0 s = 1 :=
53+
by { unfold gcd_b, rw [xgcd, xgcd_zero_left] }
54+
55+
@[simp] theorem gcd_a_zero_right {s : ℕ} (h : s ≠ 0) : gcd_a s 0 = 1 :=
56+
begin
57+
unfold gcd_a xgcd,
58+
induction s,
59+
{ exact absurd rfl h, },
60+
{ simp [xgcd_aux], }
61+
end
62+
63+
@[simp] theorem gcd_b_zero_right {s : ℕ} (h : s ≠ 0) : gcd_b s 0 = 0 :=
64+
begin
65+
unfold gcd_b xgcd,
66+
induction s,
67+
{ exact absurd rfl h, },
68+
{ simp [xgcd_aux], }
69+
end
70+
4971
@[simp] theorem xgcd_aux_fst (x y) : ∀ s t s' t',
5072
(xgcd_aux x s t y s' t').1 = gcd x y :=
5173
gcd.induction x y (by simp) (λ x y h IH s t s' t', by simp [xgcd_aux_rec, h, IH]; rw ← gcd_rec)

src/data/nat/gcd.lean

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,36 @@ by rw [gcd_comm m n, gcd_gcd_self_left_right]
139139
lemma gcd_add_mul_self (m n k : ℕ) : gcd m (n + k * m) = gcd m n :=
140140
by simp [gcd_rec m (n + k * m), gcd_rec m n]
141141

142+
theorem gcd_eq_zero_iff {i j : ℕ} : gcd i j = 0 ↔ i = 0 ∧ j = 0 :=
143+
begin
144+
split,
145+
{ intro h,
146+
exact ⟨eq_zero_of_gcd_eq_zero_left h, eq_zero_of_gcd_eq_zero_right h⟩, },
147+
{ intro h,
148+
rw [h.1, h.2],
149+
exact nat.gcd_zero_right _ }
150+
end
151+
142152
/-! ### `lcm` -/
143153

144154
theorem lcm_comm (m n : ℕ) : lcm m n = lcm n m :=
145155
by delta lcm; rw [mul_comm, gcd_comm]
146156

157+
@[simp]
147158
theorem lcm_zero_left (m : ℕ) : lcm 0 m = 0 :=
148159
by delta lcm; rw [zero_mul, nat.zero_div]
149160

161+
@[simp]
150162
theorem lcm_zero_right (m : ℕ) : lcm m 0 = 0 := lcm_comm 0 m ▸ lcm_zero_left m
151163

164+
@[simp]
152165
theorem lcm_one_left (m : ℕ) : lcm 1 m = m :=
153166
by delta lcm; rw [one_mul, gcd_one_left, nat.div_one]
154167

168+
@[simp]
155169
theorem lcm_one_right (m : ℕ) : lcm m 1 = m := lcm_comm 1 m ▸ lcm_one_left m
156170

171+
@[simp]
157172
theorem lcm_self (m : ℕ) : lcm m m = m :=
158173
or.elim (eq_zero_or_pos m)
159174
(λh, by rw [h, lcm_zero_left])
@@ -184,6 +199,9 @@ dvd_antisymm
184199
(dvd.trans (dvd_lcm_left m n) (dvd_lcm_left (lcm m n) k))
185200
(lcm_dvd (dvd.trans (dvd_lcm_right m n) (dvd_lcm_left (lcm m n) k)) (dvd_lcm_right (lcm m n) k)))
186201

202+
theorem lcm_ne_zero {m n : ℕ} (hm : m ≠ 0) (hn : n ≠ 0) : lcm m n ≠ 0 :=
203+
by { intro h, simpa [h, hm, hn] using gcd_mul_lcm m n, }
204+
187205
/-!
188206
### `coprime`
189207

src/data/nat/modeq.lean

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,45 @@ by rw [modeq_iff_dvd] at *; exact dvd.trans (dvd_mul_left (n : ℤ) (m : ℤ)) h
105105
theorem modeq_of_modeq_mul_right (m : ℕ) : a ≡ b [MOD n * m] → a ≡ b [MOD n] :=
106106
mul_comm m n ▸ modeq_of_modeq_mul_left _
107107

108+
theorem modeq_one : a ≡ b [MOD 1] := modeq_of_dvd $ one_dvd _
109+
108110
local attribute [semireducible] int.nonneg
109111

112+
/-- The natural number less than `lcm n m` congruent to `a` mod `n` and `b` mod `m` -/
113+
def chinese_remainder' (h : a ≡ b [MOD gcd n m]) : {k // k ≡ a [MOD n] ∧ k ≡ b [MOD m]} :=
114+
if hn : n = 0 then ⟨a, begin rw [hn, gcd_zero_left] at h, split, refl, exact h endelse
115+
if hm : m = 0 then ⟨b, begin rw [hm, gcd_zero_right] at h, split, exact h.symm, refl endelse
116+
let (c, d) := xgcd n m in int.to_nat (((n * c * b + m * d * a) / gcd n m) % lcm n m), begin
117+
rw xgcd_val,
118+
dsimp [chinese_remainder'._match_1],
119+
rw [modeq_iff_dvd, modeq_iff_dvd,
120+
int.to_nat_of_nonneg (int.mod_nonneg _ (int.coe_nat_ne_zero.2 (lcm_ne_zero hn hm)))],
121+
have hnonzero : (gcd n m : ℤ) ≠ 0 := begin
122+
norm_cast,
123+
rw [nat.gcd_eq_zero_iff, not_and],
124+
exact λ _, hm,
125+
end,
126+
have hcoedvd : ∀ t, (gcd n m : ℤ) ∣ t * (b - a) := λ t, dvd_mul_of_dvd_right h.dvd_of_modeq _,
127+
have := gcd_eq_gcd_ab n m,
128+
split; rw [int.mod_def, ← sub_add]; refine dvd_add _ (dvd_mul_of_dvd_left _ _); try {norm_cast},
129+
{ rw ← sub_eq_iff_eq_add' at this,
130+
rw [← this, sub_mul, ← add_sub_assoc, add_comm, add_sub_assoc, ← mul_sub,
131+
int.add_div_of_dvd_left, int.mul_div_cancel_left _ hnonzero, int.mul_div_assoc _ h.dvd_of_modeq,
132+
← sub_sub, sub_self, zero_sub, dvd_neg, mul_assoc],
133+
exact dvd_mul_right _ _,
134+
norm_cast, exact dvd_mul_right _ _, },
135+
{ exact dvd_lcm_left n m, },
136+
{ rw ← sub_eq_iff_eq_add at this,
137+
rw [← this, sub_mul, sub_add, ← mul_sub, int.sub_div_of_dvd, int.mul_div_cancel_left _ hnonzero,
138+
int.mul_div_assoc _ h.dvd_of_modeq, ← sub_add, sub_self, zero_add, mul_assoc],
139+
exact dvd_mul_right _ _,
140+
exact hcoedvd _ },
141+
{ exact dvd_lcm_right n m, },
142+
end
143+
110144
/-- The natural number less than `n*m` congruent to `a` mod `n` and `b` mod `m` -/
111145
def chinese_remainder (co : coprime n m) (a b : ℕ) : {k // k ≡ a [MOD n] ∧ k ≡ b [MOD m]} :=
112-
let (c, d) := xgcd n m in int.to_nat ((b * c * n + a * d * m) % (n * m)), begin
113-
rw xgcd_val, dsimp [chinese_remainder._match_1],
114-
rw [modeq_iff_dvd, modeq_iff_dvd],
115-
rw [int.to_nat_of_nonneg], swap,
116-
{ by_cases h₁ : n = 0, {simp [coprime, h₁] at co, substs m n, simp},
117-
by_cases h₂ : m = 0, {simp [coprime, h₂] at co, substs m n, simp},
118-
exact int.mod_nonneg _
119-
(mul_ne_zero (int.coe_nat_ne_zero.2 h₁) (int.coe_nat_ne_zero.2 h₂)) },
120-
have := gcd_eq_gcd_ab n m, simp [co.gcd_eq_one, mul_comm] at this,
121-
rw [int.mod_def, ← sub_add, ← sub_add]; split,
122-
{ refine dvd_add _ (dvd_trans (dvd_mul_right _ _) (dvd_mul_right _ _)),
123-
rw [add_comm, ← sub_sub], refine _root_.dvd_sub _ (dvd_mul_left _ _),
124-
have := congr_arg ((*) ↑a) this,
125-
exact ⟨_, by rwa [mul_add, ← mul_assoc, ← mul_assoc, mul_one, mul_comm,
126-
← sub_eq_iff_eq_add] at this⟩ },
127-
{ refine dvd_add _ (dvd_trans (dvd_mul_left _ _) (dvd_mul_right _ _)),
128-
rw [← sub_sub], refine _root_.dvd_sub _ (dvd_mul_left _ _),
129-
have := congr_arg ((*) ↑b) this,
130-
exact ⟨_, by rwa [mul_add, ← mul_assoc, ← mul_assoc, mul_one, mul_comm _ ↑m,
131-
← sub_eq_iff_eq_add'] at this⟩ }
132-
end
146+
chinese_remainder' (by convert modeq_one)
133147

134148
lemma modeq_and_modeq_iff_modeq_mul {a b m n : ℕ} (hmn : coprime m n) :
135149
a ≡ b [MOD m] ∧ a ≡ b [MOD n] ↔ (a ≡ b [MOD m * n]) :=

0 commit comments

Comments
 (0)