l3lab/ntp-mathlib huggingface dataset:
/-- Chooses an element of each associate class, by multiplying by `normUnit` -/
def normalize : α →*₀ α where
  toFun x := x * normUnit x
  map_zero' := by
    simp only [normUnit_zero]
    exact mul_one (0:α)
  map_one' := by dsimp only; rw [normUnit_one, one_mul]; rfl
  map_mul' x y :=
    (by_cases fun hx : x = 0 => by dsimp only; rw [hx, zero_mul, zero_mul, zero_mul]) fun hx =>
      (by_cases fun hy : y = 0 => by dsimp only; rw [hy, mul_zero, zero_mul, mul_zero]) fun hy => by
        simp only [normUnit_mul hx hy, Units.val_mul]; 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
x y : α
hx : ¬x = 0
hy : ¬y = 0
⊢ x * y * (↑(normUnit x) * ↑(normUnit y)) = x * ↑(normUnit x) * (y * ↑(normUnit y))

Next tactic:
simp only [mul_assoc, mul_left_comm y]
theorem normalize_coe_units (u : αˣ) : normalize (u : α) = 1 := by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
u : αˣ
⊢ normalize ↑u = 1

Next tactic:
simp
theorem normalize_eq_zero {x : α} : normalize x = 0 ↔ x = 0 :=
  ⟨fun hx => (associated_zero_iff_eq_zero x).1 <| hx ▸ associated_normalize _, by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
x : α
⊢ x = 0 → normalize x = 0

Next tactic:
rintro rfl
theorem normalize_eq_zero {x : α} : normalize x = 0 ↔ x = 0 :=
  ⟨fun hx => (associated_zero_iff_eq_zero x).1 <| hx ▸ associated_normalize _, by
    rintro rfl; 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
⊢ normalize 0 = 0

Next tactic:
exact normalize_zero
@[simp]
theorem normUnit_mul_normUnit (a : α) : normUnit (a * normUnit a) = 1 := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
⊢ normUnit (a * ↑(normUnit a)) = 1

Next tactic:
nontriviality α using Subsingleton.elim a 0
@[simp]
theorem normUnit_mul_normUnit (a : α) : normUnit (a * normUnit a) = 1 := by
  nontriviality α using Subsingleton.elim a 0
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
⊢ normUnit (a * ↑(normUnit a)) = 1

Next tactic:
obtain rfl | h := eq_or_ne a 0
@[simp]
theorem normUnit_mul_normUnit (a : α) : normUnit (a * normUnit a) = 1 := by
  nontriviality α using Subsingleton.elim a 0
  obtain rfl | h := eq_or_ne a 0
  · 

Proof state:
case inl
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
✝ : Nontrivial α
⊢ normUnit (0 * ↑(normUnit 0)) = 1

Next tactic:
rw [normUnit_zero, zero_mul, normUnit_zero]
@[simp]
theorem normUnit_mul_normUnit (a : α) : normUnit (a * normUnit a) = 1 := by
  nontriviality α using Subsingleton.elim a 0
  obtain rfl | h := eq_or_ne a 0
  · rw [normUnit_zero, zero_mul, normUnit_zero]
  · 

Proof state:
case inr
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
h : a ≠ 0
⊢ normUnit (a * ↑(normUnit a)) = 1

Next tactic:
rw [normUnit_mul h (Units.ne_zero _), normUnit_coe_units, mul_inv_eq_one]
theorem normalize_idem (x : α) : normalize (normalize x) = normalize x := by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
x : α
⊢ normalize (normalize x) = normalize x

Next tactic:
simp
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a b : α
hab : a ∣ b
hba : b ∣ a
⊢ normalize a = normalize b

Next tactic:
nontriviality α
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a b : α
hab : a ∣ b
hba : b ∣ a
✝ : Nontrivial α
⊢ normalize a = normalize b

Next tactic:
rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  

Proof state:
case intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
⊢ normalize a = normalize (a * ↑u)

Next tactic:
refine' by_cases (by rintro rfl; simp only [zero_mul]) fun ha : a ≠ 0 => _
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
⊢ a = 0 → normalize a = normalize (a * ↑u)

Next tactic:
rintro rfl
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by rintro rfl; 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
✝ : Nontrivial α
u : αˣ
hab : 0 ∣ 0 * ↑u
hba : 0 * ↑u ∣ 0
⊢ normalize 0 = normalize (0 * ↑u)

Next tactic:
simp only [zero_mul]
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by rintro rfl; simp only [zero_mul]) fun ha : a ≠ 0 => _
  

Proof state:
case intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
ha : a ≠ 0
⊢ normalize a = normalize (a * ↑u)

Next tactic:
suffices a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹ by
    simpa only [normalize_apply, mul_assoc, normUnit_mul ha u.ne_zero, normUnit_coe_units]
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by rintro rfl; simp only [zero_mul]) fun ha : a ≠ 0 => _
  suffices a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹ by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
ha : a ≠ 0
this : a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹
⊢ normalize a = normalize (a * ↑u)

Next tactic:
simpa only [normalize_apply, mul_assoc, normUnit_mul ha u.ne_zero, normUnit_coe_units]
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by rintro rfl; simp only [zero_mul]) fun ha : a ≠ 0 => _
  suffices a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹ by
    simpa only [normalize_apply, mul_assoc, normUnit_mul ha u.ne_zero, normUnit_coe_units]
  

Proof state:
case intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
ha : a ≠ 0
⊢ a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹

Next tactic:
calc
    a * ↑(normUnit a) = a * ↑(normUnit a) * ↑u * ↑u⁻¹ := (Units.mul_inv_cancel_right _ _).symm
    _ = a * ↑u * ↑(normUnit a) * ↑u⁻¹ := by rw [mul_right_comm a]
theorem normalize_eq_normalize {a b : α} (hab : a ∣ b) (hba : b ∣ a) :
    normalize a = normalize b := by
  nontriviality α
  rcases associated_of_dvd_dvd hab hba with ⟨u, rfl⟩
  refine' by_cases (by rintro rfl; simp only [zero_mul]) fun ha : a ≠ 0 => _
  suffices a * ↑(normUnit a) = a * ↑u * ↑(normUnit a) * ↑u⁻¹ by
    simpa only [normalize_apply, mul_assoc, normUnit_mul ha u.ne_zero, normUnit_coe_units]
  calc
    a * ↑(normUnit a) = a * ↑(normUnit a) * ↑u * ↑u⁻¹ := (Units.mul_inv_cancel_right _ _).symm
    _ = a * ↑u * ↑(normUnit a) * ↑u⁻¹ := by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
✝ : Nontrivial α
u : αˣ
hab : a ∣ a * ↑u
hba : a * ↑u ∣ a
ha : a ≠ 0
⊢ a * ↑(normUnit a) * ↑u * ↑u⁻¹ = a * ↑u * ↑(normUnit a) * ↑u⁻¹

Next tactic:
rw [mul_right_comm a]
theorem out_mul (a b : Associates α) : (a * b).out = a.out * b.out :=
  Quotient.inductionOn₂ a b fun _ _ => by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a b : Associates α
x✝¹ x✝ : α
⊢ Associates.out (⟦x✝¹⟧ * ⟦x✝⟧) = Associates.out ⟦x✝¹⟧ * Associates.out ⟦x✝⟧

Next tactic:
simp only [Associates.quotient_mk_eq_mk, out_mk, mk_mul_mk, normalize.map_mul]
theorem dvd_out_iff (a : α) (b : Associates α) : a ∣ b.out ↔ Associates.mk a ≤ b :=
  Quotient.inductionOn b <| by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
b : Associates α
⊢ ∀ (a_1 : α), a ∣ Associates.out ⟦a_1⟧ ↔ Associates.mk a ≤ ⟦a_1⟧

Next tactic:
simp [Associates.out_mk, Associates.quotient_mk_eq_mk, mk_le_mk_iff_dvd_iff]
theorem out_dvd_iff (a : α) (b : Associates α) : b.out ∣ a ↔ b ≤ Associates.mk a :=
  Quotient.inductionOn b <| by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizationMonoid α
a : α
b : Associates α
⊢ ∀ (a_1 : α), Associates.out ⟦a_1⟧ ∣ a ↔ ⟦a_1⟧ ≤ Associates.mk a

Next tactic:
simp [Associates.out_mk, Associates.quotient_mk_eq_mk, mk_le_mk_iff_dvd_iff]
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
h : gcd a b = 0
⊢ a = 0 ∧ b = 0

Next tactic:
let ⟨ca, ha⟩ := gcd_dvd_left a b
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
h : gcd a b = 0
ca : α
ha : a = gcd a b * ca
⊢ a = 0 ∧ b = 0

Next tactic:
let ⟨cb, hb⟩ := gcd_dvd_right a b
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      let ⟨cb, hb⟩ := gcd_dvd_right a b
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
h : gcd a b = 0
ca : α
ha : a = gcd a b * ca
cb : α
hb : b = gcd a b * cb
⊢ a = 0 ∧ b = 0

Next tactic:
rw [h, zero_mul] at ha hb
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      let ⟨cb, hb⟩ := gcd_dvd_right a b
      rw [h, zero_mul] at ha hb
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
h : gcd a b = 0
ca : α
ha : a = 0
cb : α
hb : b = 0
⊢ a = 0 ∧ b = 0

Next tactic:
exact ⟨ha, hb⟩
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      let ⟨cb, hb⟩ := gcd_dvd_right a b
      rw [h, zero_mul] at ha hb
      exact ⟨ha, hb⟩)
    fun ⟨ha, hb⟩ => by
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
x✝ : a = 0 ∧ b = 0
ha : a = 0
hb : b = 0
⊢ gcd a b = 0

Next tactic:
rw [ha, hb, ← zero_dvd_iff]
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      let ⟨cb, hb⟩ := gcd_dvd_right a b
      rw [h, zero_mul] at ha hb
      exact ⟨ha, hb⟩)
    fun ⟨ha, hb⟩ => by
    rw [ha, hb, ← zero_dvd_iff]
    

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
x✝ : a = 0 ∧ b = 0
ha : a = 0
hb : b = 0
⊢ 0 ∣ gcd 0 0

Next tactic:
apply dvd_gcd
@[simp]
theorem gcd_eq_zero_iff [GCDMonoid α] (a b : α) : gcd a b = 0 ↔ a = 0 ∧ b = 0 :=
  Iff.intro
    (fun h => by
      let ⟨ca, ha⟩ := gcd_dvd_left a b
      let ⟨cb, hb⟩ := gcd_dvd_right a b
      rw [h, zero_mul] at ha hb
      exact ⟨ha, hb⟩)
    fun ⟨ha, hb⟩ => by
    rw [ha, hb, ← zero_dvd_iff]
    apply dvd_gcd <;> 

Proof state:
case a
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b : α
x✝ : a = 0 ∧ b = 0
ha : a = 0
hb : b = 0
⊢ 0 ∣ 0

Next tactic:
rfl
@[simp]
theorem gcd_mul_left [NormalizedGCDMonoid α] (a b c : α) :
    gcd (a * b) (a * c) = normalize a * gcd b c :=
  (by_cases (by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizedGCDMonoid α
a b c : α
⊢ a = 0 → gcd (a * b) (a * c) = normalize a * gcd b c

Next tactic:
rintro rfl
@[simp]
theorem gcd_mul_left [NormalizedGCDMonoid α] (a b c : α) :
    gcd (a * b) (a * c) = normalize a * gcd b c :=
  (by_cases (by rintro rfl; 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizedGCDMonoid α
b c : α
⊢ gcd (0 * b) (0 * c) = normalize 0 * gcd b c

Next tactic:
simp only [zero_mul, gcd_zero_left, normalize_zero]
@[simp]
theorem gcd_mul_left [NormalizedGCDMonoid α] (a b c : α) :
    gcd (a * b) (a * c) = normalize a * gcd b c :=
  (by_cases (by rintro rfl; simp only [zero_mul, gcd_zero_left, normalize_zero]))
    fun ha : a ≠ 0 =>
    suffices gcd (a * b) (a * c) = normalize (a * gcd b c) by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizedGCDMonoid α
a b c : α
ha : a ≠ 0
this : gcd (a * b) (a * c) = normalize (a * gcd b c)
⊢ gcd (a * b) (a * c) = normalize a * gcd b c

Next tactic:
simpa
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
⊢ Associated (gcd (a * b) (a * c)) (a * gcd b c)

Next tactic:
obtain rfl | ha := eq_or_ne a 0
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · 

Proof state:
case inl
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
b c : α
⊢ Associated (gcd (0 * b) (0 * c)) (0 * gcd b c)

Next tactic:
simp only [zero_mul, gcd_zero_left']
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  

Proof state:
case inr
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
⊢ Associated (gcd (a * b) (a * c)) (a * gcd b c)

Next tactic:
obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
  

Proof state:
case inr.intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
d : α
eq : gcd (a * b) (a * c) = a * d
⊢ Associated (gcd (a * b) (a * c)) (a * gcd b c)

Next tactic:
apply associated_of_dvd_dvd
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
  apply associated_of_dvd_dvd
  · 

Proof state:
case inr.intro.hab
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
d : α
eq : gcd (a * b) (a * c) = a * d
⊢ gcd (a * b) (a * c) ∣ a * gcd b c

Next tactic:
rw [eq]
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
  apply associated_of_dvd_dvd
  · rw [eq]
    

Proof state:
case inr.intro.hab
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
d : α
eq : gcd (a * b) (a * c) = a * d
⊢ a * d ∣ a * gcd b c

Next tactic:
apply mul_dvd_mul_left
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
  apply associated_of_dvd_dvd
  · rw [eq]
    apply mul_dvd_mul_left
    

Proof state:
case inr.intro.hab.h
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
d : α
eq : gcd (a * b) (a * c) = a * d
⊢ d ∣ gcd b c

Next tactic:
exact
      dvd_gcd ((mul_dvd_mul_iff_left ha).1 <| eq ▸ gcd_dvd_left _ _)
        ((mul_dvd_mul_iff_left ha).1 <| eq ▸ gcd_dvd_right _ _)
theorem gcd_mul_left' [GCDMonoid α] (a b c : α) :
    Associated (gcd (a * b) (a * c)) (a * gcd b c) := by
  obtain rfl | ha := eq_or_ne a 0
  · simp only [zero_mul, gcd_zero_left']
  obtain ⟨d, eq⟩ := dvd_gcd (dvd_mul_right a b) (dvd_mul_right a c)
  apply associated_of_dvd_dvd
  · rw [eq]
    apply mul_dvd_mul_left
    exact
      dvd_gcd ((mul_dvd_mul_iff_left ha).1 <| eq ▸ gcd_dvd_left _ _)
        ((mul_dvd_mul_iff_left ha).1 <| eq ▸ gcd_dvd_right _ _)
  · 

Proof state:
case inr.intro.hba
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
ha : a ≠ 0
d : α
eq : gcd (a * b) (a * c) = a * d
⊢ a * gcd b c ∣ gcd (a * b) (a * c)

Next tactic:
exact dvd_gcd (mul_dvd_mul_left a <| gcd_dvd_left _ _) (mul_dvd_mul_left a <| gcd_dvd_right _ _)
@[simp]
theorem gcd_mul_right [NormalizedGCDMonoid α] (a b c : α) :
    gcd (b * a) (c * a) = gcd b c * normalize a := by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizedGCDMonoid α
a b c : α
⊢ gcd (b * a) (c * a) = gcd b c * normalize a

Next tactic:
simp only [mul_comm, gcd_mul_left]
@[simp]
theorem gcd_mul_right' [GCDMonoid α] (a b c : α) : Associated (gcd (b * a) (c * a)) (gcd b c * a) :=
  by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
a b c : α
⊢ Associated (gcd (b * a) (c * a)) (gcd b c * a)

Next tactic:
simp only [mul_comm, gcd_mul_left']
theorem gcd_eq_right_iff [NormalizedGCDMonoid α] (a b : α) (h : normalize b = b) :
    gcd a b = b ↔ b ∣ a := by 

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : NormalizedGCDMonoid α
a b : α
h : normalize b = b
⊢ gcd a b = b ↔ b ∣ a

Next tactic:
simpa only [gcd_comm a b] using gcd_eq_left_iff b a h
theorem dvd_mul_gcd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) : k ∣ m * gcd k n := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ m * n
⊢ k ∣ m * gcd k n

Next tactic:
rw [mul_comm] at H ⊢
theorem dvd_mul_gcd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) : k ∣ m * gcd k n := by
  rw [mul_comm] at H ⊢
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ n * m
⊢ k ∣ gcd k n * m

Next tactic:
exact dvd_gcd_mul_of_dvd_mul H
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ m * n
⊢ ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂

Next tactic:
by_cases h0 : gcd k m = 0
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  by_cases h0 : gcd k m = 0
  · 

Proof state:
case pos
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ m * n
h0 : gcd k m = 0
⊢ ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂

Next tactic:
rw [gcd_eq_zero_iff] at h0
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  by_cases h0 : gcd k m = 0
  · rw [gcd_eq_zero_iff] at h0
    

Proof state:
case pos
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ m * n
h0 : k = 0 ∧ m = 0
⊢ ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂

Next tactic:
rcases h0 with ⟨rfl, rfl⟩
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  by_cases h0 : gcd k m = 0
  · rw [gcd_eq_zero_iff] at h0
    rcases h0 with ⟨rfl, rfl⟩
    

Proof state:
case pos.intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
n : α
H : 0 ∣ 0 * n
⊢ ∃ d₁ d₂, d₁ ∣ 0 ∧ d₂ ∣ n ∧ 0 = d₁ * d₂

Next tactic:
refine' ⟨0, n, dvd_refl 0, dvd_refl n, _⟩
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  by_cases h0 : gcd k m = 0
  · rw [gcd_eq_zero_iff] at h0
    rcases h0 with ⟨rfl, rfl⟩
    refine' ⟨0, n, dvd_refl 0, dvd_refl n, _⟩
    

Proof state:
case pos.intro
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
n : α
H : 0 ∣ 0 * n
⊢ 0 = 0 * n

Next tactic:
simp
/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

In other words, the nonzero elements of a `GCDMonoid` form a decomposition monoid
(more widely known as a pre-Schreier domain in the context of rings).

Note: In general, this representation is highly non-unique.

See `Nat.prodDvdAndDvdOfDvdProd` for a constructive version on `ℕ`.  -/
theorem exists_dvd_and_dvd_of_dvd_mul [GCDMonoid α] {m n k : α} (H : k ∣ m * n) :
    ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂ := by
  by_cases h0 : gcd k m = 0
  · rw [gcd_eq_zero_iff] at h0
    rcases h0 with ⟨rfl, rfl⟩
    refine' ⟨0, n, dvd_refl 0, dvd_refl n, _⟩
    simp
  · 

Proof state:
case neg
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : GCDMonoid α
m n k : α
H : k ∣ m * n
h0 : ¬gcd k m = 0
⊢ ∃ d₁ d₂, d₁ ∣ m ∧ d₂ ∣ n ∧ k = d₁ * d₂

Next tactic:
obtain ⟨a, ha⟩ := gcd_dvd_left k m