l3lab/ntp-mathlib huggingface dataset:
theorem Sorted.merge : ∀ {l l' : List α}, Sorted r l → Sorted r l' → Sorted r (merge r l l')
  | [], [], _, _ => by simp [List.merge]
  | [], b :: l', _, h₂ => by simpa [List.merge] using h₂
  | a :: l, [], h₁, _ => by simpa [List.merge] using h₁
  | a :: l, b :: l', h₁, h₂ => by
    by_cases h : a ≼ b
    · suffices ∀ b' ∈ List.merge r l (b :: l'), r a b' by
        simpa [List.merge, h, h₁.of_cons.merge h₂]
      intro b' bm
      rcases show b' = b ∨ b' ∈ l ∨ b' ∈ l' by
          simpa [or_left_comm] using (perm_merge _ _ _).subset bm with
        (be | bl | bl')
      · subst b'
        assumption
      · exact rel_of_sorted_cons h₁ _ bl
      · exact _root_.trans h (rel_of_sorted_cons h₂ _ bl')
    · suffices ∀ b' ∈ List.merge r (a :: l) l', r b b' by
        simpa [List.merge, h, h₁.merge h₂.of_cons]
      intro b' bm
      have ba : b ≼ a := (total_of r _ _).resolve_left h
      have : b' = a ∨ b' ∈ l ∨ b' ∈ l' := by simpa using (perm_merge _ _ _).subset bm
      rcases this with (be | bl | bl')
      · subst b'
        assumption
      · exact _root_.trans ba (rel_of_sorted_cons h₁ _ bl)
      · 

Proof state:
case neg.inr.inr
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a : α
l : List α
b : α
l' : List α
h₁ : Sorted r (a :: l)
h₂ : Sorted r (b :: l')
h : ¬r a b
b' : α
bm : b' ∈ List.merge r (a :: l) l'
ba : r b a
bl' : b' ∈ l'
⊢ r b b'

Next tactic:
exact rel_of_sorted_cons h₂ _ bl'
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by 

Proof state:
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
⊢ Sorted r (mergeSort r [])

Next tactic:
simp [mergeSort]
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by simp [mergeSort]
  | [a] => by 

Proof state:
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a : α
⊢ Sorted r (mergeSort r [a])

Next tactic:
simp [mergeSort]
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by simp [mergeSort]
  | [a] => by simp [mergeSort]
  | a :: b :: l => by
    

Proof state:
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a b : α
l : List α
⊢ Sorted r (mergeSort r (a :: b :: l))

Next tactic:
cases' e : split (a :: b :: l) with l₁ l₂
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by simp [mergeSort]
  | [a] => by simp [mergeSort]
  | a :: b :: l => by
    cases' e : split (a :: b :: l) with l₁ l₂
    

Proof state:
case mk
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a b : α
l l₁ l₂ : List α
e : split (a :: b :: l) = (l₁, l₂)
⊢ Sorted r (mergeSort r (a :: b :: l))

Next tactic:
cases' length_split_lt e with h₁ h₂
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by simp [mergeSort]
  | [a] => by simp [mergeSort]
  | a :: b :: l => by
    cases' e : split (a :: b :: l) with l₁ l₂
    cases' length_split_lt e with h₁ h₂
    

Proof state:
case mk.intro
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a b : α
l l₁ l₂ : List α
e : split (a :: b :: l) = (l₁, l₂)
h₁ : length l₁ < length (a :: b :: l)
h₂ : length l₂ < length (a :: b :: l)
⊢ Sorted r (mergeSort r (a :: b :: l))

Next tactic:
rw [mergeSort_cons_cons r e]
theorem sorted_mergeSort : ∀ l : List α, Sorted r (mergeSort r l)
  | [] => by simp [mergeSort]
  | [a] => by simp [mergeSort]
  | a :: b :: l => by
    cases' e : split (a :: b :: l) with l₁ l₂
    cases' length_split_lt e with h₁ h₂
    rw [mergeSort_cons_cons r e]
    

Proof state:
case mk.intro
α : Type uu
r : α → α → Prop
inst✝² : DecidableRel r
inst✝¹ : IsTotal α r
inst✝ : IsTrans α r
a b : α
l l₁ l₂ : List α
e : split (a :: b :: l) = (l₁, l₂)
h₁ : length l₁ < length (a :: b :: l)
h₂ : length l₂ < length (a :: b :: l)
⊢ Sorted r (merge r (mergeSort r l₁) (mergeSort r l₂))

Next tactic:
exact (sorted_mergeSort l₁).merge (sorted_mergeSort l₂)
@[simp]
theorem mergeSort_nil : [].mergeSort r = [] := by 

Proof state:
α : Type uu
r : α → α → Prop
inst✝ : DecidableRel r
⊢ mergeSort r [] = []

Next tactic:
rw [List.mergeSort]
@[simp]
theorem mergeSort_singleton (a : α) : [a].mergeSort r = [a] := by 

Proof state:
α : Type uu
r : α → α → Prop
inst✝ : DecidableRel r
a : α
⊢ mergeSort r [a] = [a]

Next tactic:
rw [List.mergeSort]
instance (priority := 100) IsNoetherianRing.wfDvdMonoid [CommRing α] [IsDomain α]
    [IsNoetherianRing α] : WfDvdMonoid α :=
  ⟨by
    

Proof state:
α : Type u_1
inst✝² : CommRing α
inst✝¹ : IsDomain α
inst✝ : IsNoetherianRing α
⊢ WellFounded DvdNotUnit

Next tactic:
convert InvImage.wf (fun a => Ideal.span ({a} : Set α)) (wellFounded_submodule_gt _ _)
instance (priority := 100) IsNoetherianRing.wfDvdMonoid [CommRing α] [IsDomain α]
    [IsNoetherianRing α] : WfDvdMonoid α :=
  ⟨by
    convert InvImage.wf (fun a => Ideal.span ({a} : Set α)) (wellFounded_submodule_gt _ _)
    

Proof state:
case h.e'_2
α : Type u_1
inst✝² : CommRing α
inst✝¹ : IsDomain α
inst✝ : IsNoetherianRing α
⊢ DvdNotUnit = InvImage (fun x x_1 => x > x_1) fun a => Ideal.span {a}

Next tactic:
ext
instance (priority := 100) IsNoetherianRing.wfDvdMonoid [CommRing α] [IsDomain α]
    [IsNoetherianRing α] : WfDvdMonoid α :=
  ⟨by
    convert InvImage.wf (fun a => Ideal.span ({a} : Set α)) (wellFounded_submodule_gt _ _)
    ext
    

Proof state:
case h.e'_2.h.h.a
α : Type u_1
inst✝² : CommRing α
inst✝¹ : IsDomain α
inst✝ : IsNoetherianRing α
x✝¹ x✝ : α
⊢ DvdNotUnit x✝¹ x✝ ↔ InvImage (fun x x_1 => x > x_1) (fun a => Ideal.span {a}) x✝¹ x✝

Next tactic:
exact Ideal.span_singleton_lt_span_singleton.symm
theorem exists_factors (a : α) :
    a ≠ 0 → ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ Associated f.prod a :=
  induction_on_irreducible a (fun h => (h rfl).elim)
    (fun u hu _ => ⟨0, fun _ h => False.elim (Multiset.not_mem_zero _ h), hu.unit, one_mul _⟩)
    fun a i ha0 hi ih _ =>
    let ⟨s, hs⟩ := ih ha0
    ⟨i ::ₘ s, fun b H => (Multiset.mem_cons.1 H).elim (fun h => h.symm ▸ hi) (hs.1 b), by
      

Proof state:
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
a✝ a i : α
ha0 : a ≠ 0
hi : Irreducible i
ih : a ≠ 0 → ∃ f, (∀ b ∈ f, Irreducible b) ∧ Multiset.prod f ~ᵤ a
x✝ : i * a ≠ 0
s : Multiset α
hs : (∀ b ∈ s, Irreducible b) ∧ Multiset.prod s ~ᵤ a
⊢ Multiset.prod (i ::ₘ s) ~ᵤ i * a

Next tactic:
rw [s.prod_cons i]
theorem exists_factors (a : α) :
    a ≠ 0 → ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ Associated f.prod a :=
  induction_on_irreducible a (fun h => (h rfl).elim)
    (fun u hu _ => ⟨0, fun _ h => False.elim (Multiset.not_mem_zero _ h), hu.unit, one_mul _⟩)
    fun a i ha0 hi ih _ =>
    let ⟨s, hs⟩ := ih ha0
    ⟨i ::ₘ s, fun b H => (Multiset.mem_cons.1 H).elim (fun h => h.symm ▸ hi) (hs.1 b), by
      rw [s.prod_cons i]
      

Proof state:
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
a✝ a i : α
ha0 : a ≠ 0
hi : Irreducible i
ih : a ≠ 0 → ∃ f, (∀ b ∈ f, Irreducible b) ∧ Multiset.prod f ~ᵤ a
x✝ : i * a ≠ 0
s : Multiset α
hs : (∀ b ∈ s, Irreducible b) ∧ Multiset.prod s ~ᵤ a
⊢ i * Multiset.prod s ~ᵤ i * a

Next tactic:
exact hs.2.mul_left i
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    

Proof state:
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
a : α
hn0 : a ≠ 0
hnu : ¬IsUnit a
⊢ ∃ f, (∀ b ∈ f, Irreducible b) ∧ Multiset.prod f = a ∧ f ≠ ∅

Next tactic:
obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    

Proof state:
case intro.intro.intro
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
⊢ ∃ f_1, (∀ b ∈ f_1, Irreducible b) ∧ Multiset.prod f_1 = Multiset.prod f * ↑u ∧ f_1 ≠ ∅

Next tactic:
obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by 

Proof state:
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
h : f = 0
⊢ IsUnit (Multiset.prod f * ↑u)

Next tactic:
simp [h]
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
    

Proof state:
case intro.intro.intro.intro
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
⊢ ∃ f_1, (∀ b ∈ f_1, Irreducible b) ∧ Multiset.prod f_1 = Multiset.prod f * ↑u ∧ f_1 ≠ ∅

Next tactic:
classical
      refine' ⟨(f.erase b).cons (b * u), fun a ha => _, _, Multiset.cons_ne_zero⟩
      · obtain rfl | ha := Multiset.mem_cons.1 ha
        exacts [Associated.irreducible ⟨u, rfl⟩ (hi b h), hi a (Multiset.mem_of_mem_erase ha)]
      · rw [Multiset.prod_cons, mul_comm b, mul_assoc, Multiset.prod_erase h, mul_comm]
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
    classical
      

Proof state:
case intro.intro.intro.intro
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
⊢ ∃ f_1, (∀ b ∈ f_1, Irreducible b) ∧ Multiset.prod f_1 = Multiset.prod f * ↑u ∧ f_1 ≠ ∅

Next tactic:
refine' ⟨(f.erase b).cons (b * u), fun a ha => _, _, Multiset.cons_ne_zero⟩
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
    classical
      refine' ⟨(f.erase b).cons (b * u), fun a ha => _, _, Multiset.cons_ne_zero⟩
      · 

Proof state:
case intro.intro.intro.intro.refine'_1
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
a : α
ha : a ∈ b * ↑u ::ₘ Multiset.erase f b
⊢ Irreducible a

Next tactic:
obtain rfl | ha := Multiset.mem_cons.1 ha
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
    classical
      refine' ⟨(f.erase b).cons (b * u), fun a ha => _, _, Multiset.cons_ne_zero⟩
      · obtain rfl | ha := Multiset.mem_cons.1 ha
        

Proof state:
case intro.intro.intro.intro.refine'_1.inl
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
ha : b * ↑u ∈ b * ↑u ::ₘ Multiset.erase f b
⊢ Irreducible (b * ↑u)
case intro.intro.intro.intro.refine'_1.inr
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
a : α
ha✝ : a ∈ b * ↑u ::ₘ Multiset.erase f b
ha : a ∈ Multiset.erase f b
⊢ Irreducible a

Next tactic:
exacts [Associated.irreducible ⟨u, rfl⟩ (hi b h), hi a (Multiset.mem_of_mem_erase ha)]
theorem not_unit_iff_exists_factors_eq (a : α) (hn0 : a ≠ 0) :
    ¬IsUnit a ↔ ∃ f : Multiset α, (∀ b ∈ f, Irreducible b) ∧ f.prod = a ∧ f ≠ ∅ :=
  ⟨fun hnu => by
    obtain ⟨f, hi, u, rfl⟩ := exists_factors a hn0
    obtain ⟨b, h⟩ := Multiset.exists_mem_of_ne_zero fun h : f = 0 => hnu <| by simp [h]
    classical
      refine' ⟨(f.erase b).cons (b * u), fun a ha => _, _, Multiset.cons_ne_zero⟩
      · obtain rfl | ha := Multiset.mem_cons.1 ha
        exacts [Associated.irreducible ⟨u, rfl⟩ (hi b h), hi a (Multiset.mem_of_mem_erase ha)]
      · 

Proof state:
case intro.intro.intro.intro.refine'_2
α : Type u_1
inst✝¹ : CommMonoidWithZero α
inst✝ : WfDvdMonoid α
f : Multiset α
hi : ∀ b ∈ f, Irreducible b
u : αˣ
hn0 : Multiset.prod f * ↑u ≠ 0
hnu : ¬IsUnit (Multiset.prod f * ↑u)
b : α
h : b ∈ f
⊢ Multiset.prod (b * ↑u ::ₘ Multiset.erase f b) = Multiset.prod f * ↑u

Next tactic:
rw [Multiset.prod_cons, mul_comm b, mul_assoc, Multiset.prod_erase h, mul_comm]
theorem WfDvdMonoid.of_wellFounded_associates [CancelCommMonoidWithZero α]
    (h : WellFounded ((· < ·) : Associates α → Associates α → Prop)) : WfDvdMonoid α :=
  WfDvdMonoid.of_wfDvdMonoid_associates
    ⟨by
      

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
h : WellFounded fun x x_1 => x < x_1
⊢ WellFounded DvdNotUnit

Next tactic:
convert h
theorem WfDvdMonoid.of_wellFounded_associates [CancelCommMonoidWithZero α]
    (h : WellFounded ((· < ·) : Associates α → Associates α → Prop)) : WfDvdMonoid α :=
  WfDvdMonoid.of_wfDvdMonoid_associates
    ⟨by
      convert h
      

Proof state:
case h.e'_2.h.h.h.e
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
h : WellFounded fun x x_1 => x < x_1
x✝¹ x✝ : Associates α
⊢ DvdNotUnit = LT.lt

Next tactic:
ext
theorem WfDvdMonoid.of_wellFounded_associates [CancelCommMonoidWithZero α]
    (h : WellFounded ((· < ·) : Associates α → Associates α → Prop)) : WfDvdMonoid α :=
  WfDvdMonoid.of_wfDvdMonoid_associates
    ⟨by
      convert h
      ext
      

Proof state:
case h.e'_2.h.h.h.e.h.h.a
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
h : WellFounded fun x x_1 => x < x_1
x✝³ x✝² x✝¹ x✝ : Associates α
⊢ DvdNotUnit x✝¹ x✝ ↔ x✝¹ < x✝

Next tactic:
exact Associates.dvdNotUnit_iff_lt
theorem WfDvdMonoid.iff_wellFounded_associates [CancelCommMonoidWithZero α] :
    WfDvdMonoid α ↔ WellFounded ((· < ·) : Associates α → Associates α → Prop) :=
  ⟨by 

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
⊢ WfDvdMonoid α → WellFounded fun x x_1 => x < x_1

Next tactic:
apply WfDvdMonoid.wellFounded_associates
instance Associates.ufm [CancelCommMonoidWithZero α] [UniqueFactorizationMonoid α] :
    UniqueFactorizationMonoid (Associates α) :=
  { (WfDvdMonoid.wfDvdMonoid_associates : WfDvdMonoid (Associates α)) with
    irreducible_iff_prime := by
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
src✝ : WfDvdMonoid (Associates α) := WfDvdMonoid.wfDvdMonoid_associates
⊢ ∀ {a : Associates α}, Irreducible a ↔ Prime a

Next tactic:
rw [← Associates.irreducible_iff_prime_iff]
instance Associates.ufm [CancelCommMonoidWithZero α] [UniqueFactorizationMonoid α] :
    UniqueFactorizationMonoid (Associates α) :=
  { (WfDvdMonoid.wfDvdMonoid_associates : WfDvdMonoid (Associates α)) with
    irreducible_iff_prime := by
      rw [← Associates.irreducible_iff_prime_iff]
      

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
src✝ : WfDvdMonoid (Associates α) := WfDvdMonoid.wfDvdMonoid_associates
⊢ ∀ (a : α), Irreducible a ↔ Prime a

Next tactic:
apply UniqueFactorizationMonoid.irreducible_iff_prime
theorem exists_prime_factors (a : α) :
    a ≠ 0 → ∃ f : Multiset α, (∀ b ∈ f, Prime b) ∧ f.prod ~ᵤ a := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
a : α
⊢ a ≠ 0 → ∃ f, (∀ b ∈ f, Prime b) ∧ Multiset.prod f ~ᵤ a

Next tactic:
simp_rw [← UniqueFactorizationMonoid.irreducible_iff_prime]
theorem exists_prime_factors (a : α) :
    a ≠ 0 → ∃ f : Multiset α, (∀ b ∈ f, Prime b) ∧ f.prod ~ᵤ a := by
  simp_rw [← UniqueFactorizationMonoid.irreducible_iff_prime]
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
a : α
⊢ a ≠ 0 → ∃ f, (∀ b ∈ f, Irreducible b) ∧ Multiset.prod f ~ᵤ a

Next tactic:
apply WfDvdMonoid.exists_factors a
@[elab_as_elim]
theorem induction_on_prime {P : α → Prop} (a : α) (h₁ : P 0) (h₂ : ∀ x : α, IsUnit x → P x)
    (h₃ : ∀ a p : α, a ≠ 0 → Prime p → P a → P (p * a)) : P a := by
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
P : α → Prop
a : α
h₁ : P 0
h₂ : ∀ (x : α), IsUnit x → P x
h₃ : ∀ (a p : α), a ≠ 0 → Prime p → P a → P (p * a)
⊢ P a

Next tactic:
simp_rw [← UniqueFactorizationMonoid.irreducible_iff_prime] at h₃
@[elab_as_elim]
theorem induction_on_prime {P : α → Prop} (a : α) (h₁ : P 0) (h₂ : ∀ x : α, IsUnit x → P x)
    (h₃ : ∀ a p : α, a ≠ 0 → Prime p → P a → P (p * a)) : P a := by
  simp_rw [← UniqueFactorizationMonoid.irreducible_iff_prime] at h₃
  

Proof state:
α : Type u_1
inst✝¹ : CancelCommMonoidWithZero α
inst✝ : UniqueFactorizationMonoid α
P : α → Prop
a : α
h₁ : P 0
h₂ : ∀ (x : α), IsUnit x → P x
h₃ : ∀ (a p : α), a ≠ 0 → Irreducible p → P a → P (p * a)
⊢ P a

Next tactic:
exact WfDvdMonoid.induction_on_irreducible a h₁ h₂ h₃
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
⊢ ∀ {f g : Multiset α},
    (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod f ~ᵤ Multiset.prod g → Multiset.Rel Associated f g

Next tactic:
classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · intros g _ hg h
    exact Multiset.rel_zero_left.2 <|
      Multiset.eq_zero_of_forall_not_mem fun x hx =>
        have : IsUnit g.prod := by simpa [associated_one_iff_isUnit] using h.symm
        (hg x hx).not_unit <|
          isUnit_iff_dvd_one.2 <| (Multiset.dvd_prod hx).trans (isUnit_iff_dvd_one.1 this)
  · intros g hf hg hfg
    let ⟨b, hbg, hb⟩ :=
      (exists_associated_mem_of_dvd_prod (hf p (by simp)) fun q hq => hg _ hq) <|
        hfg.dvd_iff_dvd_right.1 (show p ∣ (p ::ₘ f).prod by simp)
    haveI := Classical.decEq α
    rw [← Multiset.cons_erase hbg]
    exact
      Multiset.Rel.cons hb
        (ih (fun q hq => hf _ (by simp [hq]))
          (fun {q} (hq : q ∈ g.erase b) => hg q (Multiset.mem_of_mem_erase hq))
          (Associated.of_mul_left
            (by rwa [← Multiset.prod_cons, ← Multiset.prod_cons, Multiset.cons_erase hbg]) hb
            (hf p (by simp)).ne_zero))
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
⊢ ∀ {f g : Multiset α},
    (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod f ~ᵤ Multiset.prod g → Multiset.Rel Associated f g

Next tactic:
intro f
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
f : Multiset α
⊢ ∀ {g : Multiset α},
    (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod f ~ᵤ Multiset.prod g → Multiset.Rel Associated f g

Next tactic:
induction' f using Multiset.induction_on with p f ih
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · 

Proof state:
case empty
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
⊢ ∀ {g : Multiset α},
    (∀ x ∈ 0, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod 0 ~ᵤ Multiset.prod g → Multiset.Rel Associated 0 g

Next tactic:
intros g _ hg h
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · intros g _ hg h
    

Proof state:
case empty
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
g : Multiset α
a✝ : ∀ x ∈ 0, Prime x
hg : ∀ x ∈ g, Prime x
h : Multiset.prod 0 ~ᵤ Multiset.prod g
⊢ Multiset.Rel Associated 0 g

Next tactic:
exact Multiset.rel_zero_left.2 <|
      Multiset.eq_zero_of_forall_not_mem fun x hx =>
        have : IsUnit g.prod := by simpa [associated_one_iff_isUnit] using h.symm
        (hg x hx).not_unit <|
          isUnit_iff_dvd_one.2 <| (Multiset.dvd_prod hx).trans (isUnit_iff_dvd_one.1 this)
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · intros g _ hg h
    exact Multiset.rel_zero_left.2 <|
      Multiset.eq_zero_of_forall_not_mem fun x hx =>
        have : IsUnit g.prod := by 

Proof state:
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
g : Multiset α
a✝ : ∀ x ∈ 0, Prime x
hg : ∀ x ∈ g, Prime x
h : Multiset.prod 0 ~ᵤ Multiset.prod g
x : α
hx : x ∈ g
⊢ IsUnit (Multiset.prod g)

Next tactic:
simpa [associated_one_iff_isUnit] using h.symm
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · intros g _ hg h
    exact Multiset.rel_zero_left.2 <|
      Multiset.eq_zero_of_forall_not_mem fun x hx =>
        have : IsUnit g.prod := by simpa [associated_one_iff_isUnit] using h.symm
        (hg x hx).not_unit <|
          isUnit_iff_dvd_one.2 <| (Multiset.dvd_prod hx).trans (isUnit_iff_dvd_one.1 this)
  · 

Proof state:
case cons
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
p : α
f : Multiset α
ih :
  ∀ {g : Multiset α},
    (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod f ~ᵤ Multiset.prod g → Multiset.Rel Associated f g
⊢ ∀ {g : Multiset α},
    (∀ x ∈ p ::ₘ f, Prime x) →
      (∀ x ∈ g, Prime x) → Multiset.prod (p ::ₘ f) ~ᵤ Multiset.prod g → Multiset.Rel Associated (p ::ₘ f) g

Next tactic:
intros g hf hg hfg
theorem prime_factors_unique [CancelCommMonoidWithZero α] :
    ∀ {f g : Multiset α},
      (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → f.prod ~ᵤ g.prod → Multiset.Rel Associated f g := by
  classical
  intro f
  induction' f using Multiset.induction_on with p f ih
  · intros g _ hg h
    exact Multiset.rel_zero_left.2 <|
      Multiset.eq_zero_of_forall_not_mem fun x hx =>
        have : IsUnit g.prod := by simpa [associated_one_iff_isUnit] using h.symm
        (hg x hx).not_unit <|
          isUnit_iff_dvd_one.2 <| (Multiset.dvd_prod hx).trans (isUnit_iff_dvd_one.1 this)
  · intros g hf hg hfg
    

Proof state:
case cons
α : Type u_1
inst✝ : CancelCommMonoidWithZero α
p : α
f : Multiset α
ih :
  ∀ {g : Multiset α},
    (∀ x ∈ f, Prime x) → (∀ x ∈ g, Prime x) → Multiset.prod f ~ᵤ Multiset.prod g → Multiset.Rel Associated f g
g : Multiset α
hf : ∀ x ∈ p ::ₘ f, Prime x
hg : ∀ x ∈ g, Prime x
hfg : Multiset.prod (p ::ₘ f) ~ᵤ Multiset.prod g
⊢ Multiset.Rel Associated (p ::ₘ f) g

Next tactic:
let ⟨b, hbg, hb⟩ :=
      (exists_associated_mem_of_dvd_prod (hf p (by simp)) fun q hq => hg _ hq) <|
        hfg.dvd_iff_dvd_right.1 (show p ∣ (p ::ₘ f).prod by simp)