l3lab/ntp-mathlib huggingface dataset:
/-- A version of `liftExpand` used to simultaneously lift functions with two arguments
in `R[S⁻¹]`. -/
def lift₂Expand {C : Sort*} (P : R → S → R → S → C)
    (hP :
      ∀ (r₁ t₁ : R) (s₁ : S) (ht₁ : (s₁ : R) * t₁ ∈ S) (r₂ t₂ : R) (s₂ : S)
        (ht₂ : (s₂ : R) * t₂ ∈ S),
        P r₁ s₁ r₂ s₂ = P (r₁ * t₁) ⟨s₁ * t₁, ht₁⟩ (r₂ * t₂) ⟨s₂ * t₂, ht₂⟩) :
    R[S⁻¹] → R[S⁻¹] → C :=
  liftExpand
    (fun r₁ s₁ => liftExpand (P r₁ s₁) fun r₂ t₂ s₂ ht₂ => by
      have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
      simp [this])
    fun r₁ t₁ s₁ ht₁ => by
    ext x; 

Proof state:
case h
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → R → ↥S → C
hP :
  ∀ (r₁ t₁ : R) (s₁ : ↥S) (ht₁ : ↑s₁ * t₁ ∈ S) (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
    P r₁ s₁ r₂ s₂ = P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
r₁ t₁ : R
s₁ : ↥S
ht₁ : ↑s₁ * t₁ ∈ S
x : OreLocalization R S
⊢ (fun r₁ s₁ =>
        liftExpand (P r₁ s₁)
          (_ :
            ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
              P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }))
      r₁ s₁ x =
    (fun r₁ s₁ =>
        liftExpand (P r₁ s₁)
          (_ :
            ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
              P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }))
      (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } x

Next tactic:
induction' x using OreLocalization.ind with r₂ s₂
/-- A version of `liftExpand` used to simultaneously lift functions with two arguments
in `R[S⁻¹]`. -/
def lift₂Expand {C : Sort*} (P : R → S → R → S → C)
    (hP :
      ∀ (r₁ t₁ : R) (s₁ : S) (ht₁ : (s₁ : R) * t₁ ∈ S) (r₂ t₂ : R) (s₂ : S)
        (ht₂ : (s₂ : R) * t₂ ∈ S),
        P r₁ s₁ r₂ s₂ = P (r₁ * t₁) ⟨s₁ * t₁, ht₁⟩ (r₂ * t₂) ⟨s₂ * t₂, ht₂⟩) :
    R[S⁻¹] → R[S⁻¹] → C :=
  liftExpand
    (fun r₁ s₁ => liftExpand (P r₁ s₁) fun r₂ t₂ s₂ ht₂ => by
      have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
      simp [this])
    fun r₁ t₁ s₁ ht₁ => by
    ext x; induction' x using OreLocalization.ind with r₂ s₂
    

Proof state:
case h.c
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → R → ↥S → C
hP :
  ∀ (r₁ t₁ : R) (s₁ : ↥S) (ht₁ : ↑s₁ * t₁ ∈ S) (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
    P r₁ s₁ r₂ s₂ = P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
r₁ t₁ : R
s₁ : ↥S
ht₁ : ↑s₁ * t₁ ∈ S
r₂ : R
s₂ : ↥S
⊢ (fun r₁ s₁ =>
        liftExpand (P r₁ s₁)
          (_ :
            ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
              P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }))
      r₁ s₁ (r₂ /ₒ s₂) =
    (fun r₁ s₁ =>
        liftExpand (P r₁ s₁)
          (_ :
            ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
              P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }))
      (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ /ₒ s₂)

Next tactic:
dsimp only
/-- A version of `liftExpand` used to simultaneously lift functions with two arguments
in `R[S⁻¹]`. -/
def lift₂Expand {C : Sort*} (P : R → S → R → S → C)
    (hP :
      ∀ (r₁ t₁ : R) (s₁ : S) (ht₁ : (s₁ : R) * t₁ ∈ S) (r₂ t₂ : R) (s₂ : S)
        (ht₂ : (s₂ : R) * t₂ ∈ S),
        P r₁ s₁ r₂ s₂ = P (r₁ * t₁) ⟨s₁ * t₁, ht₁⟩ (r₂ * t₂) ⟨s₂ * t₂, ht₂⟩) :
    R[S⁻¹] → R[S⁻¹] → C :=
  liftExpand
    (fun r₁ s₁ => liftExpand (P r₁ s₁) fun r₂ t₂ s₂ ht₂ => by
      have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
      simp [this])
    fun r₁ t₁ s₁ ht₁ => by
    ext x; induction' x using OreLocalization.ind with r₂ s₂
    dsimp only
    

Proof state:
case h.c
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → R → ↥S → C
hP :
  ∀ (r₁ t₁ : R) (s₁ : ↥S) (ht₁ : ↑s₁ * t₁ ∈ S) (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
    P r₁ s₁ r₂ s₂ = P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
r₁ t₁ : R
s₁ : ↥S
ht₁ : ↑s₁ * t₁ ∈ S
r₂ : R
s₂ : ↥S
⊢ liftExpand (P r₁ s₁)
      (_ :
        ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
          P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ })
      (r₂ /ₒ s₂) =
    liftExpand (P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ })
      (_ :
        ∀ (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
          P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } r₂ s₂ =
            P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ })
      (r₂ /ₒ s₂)

Next tactic:
rw [liftExpand_of, liftExpand_of, hP r₁ t₁ s₁ ht₁ r₂ 1 s₂ (by simp)]
/-- A version of `liftExpand` used to simultaneously lift functions with two arguments
in `R[S⁻¹]`. -/
def lift₂Expand {C : Sort*} (P : R → S → R → S → C)
    (hP :
      ∀ (r₁ t₁ : R) (s₁ : S) (ht₁ : (s₁ : R) * t₁ ∈ S) (r₂ t₂ : R) (s₂ : S)
        (ht₂ : (s₂ : R) * t₂ ∈ S),
        P r₁ s₁ r₂ s₂ = P (r₁ * t₁) ⟨s₁ * t₁, ht₁⟩ (r₂ * t₂) ⟨s₂ * t₂, ht₂⟩) :
    R[S⁻¹] → R[S⁻¹] → C :=
  liftExpand
    (fun r₁ s₁ => liftExpand (P r₁ s₁) fun r₂ t₂ s₂ ht₂ => by
      have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
      simp [this])
    fun r₁ t₁ s₁ ht₁ => by
    ext x; induction' x using OreLocalization.ind with r₂ s₂
    dsimp only
    rw [liftExpand_of, liftExpand_of, hP r₁ t₁ s₁ ht₁ r₂ 1 s₂ (by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → R → ↥S → C
hP :
  ∀ (r₁ t₁ : R) (s₁ : ↥S) (ht₁ : ↑s₁ * t₁ ∈ S) (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
    P r₁ s₁ r₂ s₂ = P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
r₁ t₁ : R
s₁ : ↥S
ht₁ : ↑s₁ * t₁ ∈ S
r₂ : R
s₂ : ↥S
⊢ ↑s₂ * 1 ∈ S

Next tactic:
simp
/-- A version of `liftExpand` used to simultaneously lift functions with two arguments
in `R[S⁻¹]`. -/
def lift₂Expand {C : Sort*} (P : R → S → R → S → C)
    (hP :
      ∀ (r₁ t₁ : R) (s₁ : S) (ht₁ : (s₁ : R) * t₁ ∈ S) (r₂ t₂ : R) (s₂ : S)
        (ht₂ : (s₂ : R) * t₂ ∈ S),
        P r₁ s₁ r₂ s₂ = P (r₁ * t₁) ⟨s₁ * t₁, ht₁⟩ (r₂ * t₂) ⟨s₂ * t₂, ht₂⟩) :
    R[S⁻¹] → R[S⁻¹] → C :=
  liftExpand
    (fun r₁ s₁ => liftExpand (P r₁ s₁) fun r₂ t₂ s₂ ht₂ => by
      have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
      simp [this])
    fun r₁ t₁ s₁ ht₁ => by
    ext x; induction' x using OreLocalization.ind with r₂ s₂
    dsimp only
    rw [liftExpand_of, liftExpand_of, hP r₁ t₁ s₁ ht₁ r₂ 1 s₂ (by simp)]; 

Proof state:
case h.c
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → R → ↥S → C
hP :
  ∀ (r₁ t₁ : R) (s₁ : ↥S) (ht₁ : ↑s₁ * t₁ ∈ S) (r₂ t₂ : R) (s₂ : ↥S) (ht₂ : ↑s₂ * t₂ ∈ S),
    P r₁ s₁ r₂ s₂ = P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
r₁ t₁ : R
s₁ : ↥S
ht₁ : ↑s₁ * t₁ ∈ S
r₂ : R
s₂ : ↥S
⊢ P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } (r₂ * 1) { val := ↑s₂ * 1, property := (_ : ↑s₂ * 1 ∈ S) } =
    P (r₁ * t₁) { val := ↑s₁ * t₁, property := ht₁ } r₂ s₂

Next tactic:
simp
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
⊢ OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u)

Next tactic:
simp only [mul']
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
⊢ r₁ * oreNum r₂ s₁ /ₒ (s₂ * oreDenom r₂ s₁) = r₁ * v /ₒ (s₂ * u)

Next tactic:
have h₀ := ore_eq r₂ s₁
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
h₀ : r₂ * ↑(oreDenom r₂ s₁) = ↑s₁ * oreNum r₂ s₁
⊢ r₁ * oreNum r₂ s₁ /ₒ (s₂ * oreDenom r₂ s₁) = r₁ * v /ₒ (s₂ * u)

Next tactic:
set v₀ := oreNum r₂ s₁
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
h₀ : r₂ * ↑(oreDenom r₂ s₁) = ↑s₁ * v₀
⊢ r₁ * v₀ /ₒ (s₂ * oreDenom r₂ s₁) = r₁ * v /ₒ (s₂ * u)

Next tactic:
set u₀ := oreDenom r₂ s₁
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
⊢ r₁ * v₀ /ₒ (s₂ * u₀) = r₁ * v /ₒ (s₂ * u)

Next tactic:
rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  

Proof state:
case mk.mk
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
⊢ r₁ * v₀ /ₒ (s₂ * u₀) = r₁ * v /ₒ (s₂ * u)

Next tactic:
have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
⊢ ↑s₁ * (v * r₃) = r₂ * ↑u * r₃

Next tactic:
rw [← mul_assoc, ← huv]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
⊢ r₂ * ↑u * r₃ = r₂ * ↑u₀ * ↑s₃

Next tactic:
rw [mul_assoc, mul_assoc, h₃]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
⊢ r₂ * ↑u₀ * ↑s₃ = ↑s₁ * (v₀ * ↑s₃)

Next tactic:
rw [← mul_assoc, h₀]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  

Proof state:
case mk.mk
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
⊢ r₁ * v₀ /ₒ (s₂ * u₀) = r₁ * v /ₒ (s₂ * u)

Next tactic:
rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  

Proof state:
case mk.mk.intro
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * v₀ /ₒ (s₂ * u₀) = r₁ * v /ₒ (s₂ * u)

Next tactic:
symm
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; 

Proof state:
case mk.mk.intro
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * v /ₒ (s₂ * u) = r₁ * v₀ /ₒ (s₂ * u₀)

Next tactic:
rw [oreDiv_eq_iff]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  

Proof state:
case mk.mk.intro
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ ∃ u_1 v_1, r₁ * v₀ * ↑u_1 = r₁ * v * v_1 ∧ ↑(s₂ * u₀) * ↑u_1 = ↑(s₂ * u) * v_1

Next tactic:
use s₃ * s₄
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; 

Proof state:
case h
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ ∃ v_1, r₁ * v₀ * ↑(s₃ * s₄) = r₁ * v * v_1 ∧ ↑(s₂ * u₀) * ↑(s₃ * s₄) = ↑(s₂ * u) * v_1

Next tactic:
use r₃ * s₄
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; use r₃ * s₄; 

Proof state:
case h
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * v₀ * ↑(s₃ * s₄) = r₁ * v * (r₃ * ↑s₄) ∧ ↑(s₂ * u₀) * ↑(s₃ * s₄) = ↑(s₂ * u) * (r₃ * ↑s₄)

Next tactic:
simp only [Submonoid.coe_mul]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; use r₃ * s₄; simp only [Submonoid.coe_mul]; 

Proof state:
case h
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * oreNum r₂ s₁ * (↑s₃ * ↑s₄) = r₁ * v * (r₃ * ↑s₄) ∧ ↑s₂ * ↑(oreDenom r₂ s₁) * (↑s₃ * ↑s₄) = ↑s₂ * ↑u * (r₃ * ↑s₄)

Next tactic:
constructor
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; use r₃ * s₄; simp only [Submonoid.coe_mul]; constructor
  · 

Proof state:
case h.left
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * oreNum r₂ s₁ * (↑s₃ * ↑s₄) = r₁ * v * (r₃ * ↑s₄)

Next tactic:
rw [mul_assoc (b := v₀), ← mul_assoc (a := v₀), ← hs₄]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; use r₃ * s₄; simp only [Submonoid.coe_mul]; constructor
  · rw [mul_assoc (b := v₀), ← mul_assoc (a := v₀), ← hs₄]
    

Proof state:
case h.left
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ r₁ * (v * r₃ * ↑s₄) = r₁ * v * (r₃ * ↑s₄)

Next tactic:
simp only [mul_assoc]
private theorem mul'_char (r₁ r₂ : R) (s₁ s₂ : S) (u : S) (v : R) (huv : r₂ * (u : R) = s₁ * v) :
    OreLocalization.mul' r₁ s₁ r₂ s₂ = r₁ * v /ₒ (s₂ * u) := by
  -- Porting note: `assoc_rw` was not ported yet
  simp only [mul']
  have h₀ := ore_eq r₂ s₁; set v₀ := oreNum r₂ s₁; set u₀ := oreDenom r₂ s₁
  rcases oreCondition (u₀ : R) u with ⟨r₃, s₃, h₃⟩
  have :=
    calc
      (s₁ : R) * (v * r₃) = r₂ * u * r₃ := by rw [← mul_assoc, ← huv]
      _ = r₂ * u₀ * s₃ := by rw [mul_assoc, mul_assoc, h₃]
      _ = s₁ * (v₀ * s₃) := by rw [← mul_assoc, h₀]
  rcases ore_left_cancel _ _ _ this with ⟨s₄, hs₄⟩
  symm; rw [oreDiv_eq_iff]
  use s₃ * s₄; use r₃ * s₄; simp only [Submonoid.coe_mul]; constructor
  · rw [mul_assoc (b := v₀), ← mul_assoc (a := v₀), ← hs₄]
    simp only [mul_assoc]
  · 

Proof state:
case h.right
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ u : ↥S
v : R
huv : r₂ * ↑u = ↑s₁ * v
v₀ : R := oreNum r₂ s₁
u₀ : ↥S := oreDenom r₂ s₁
h₀ : r₂ * ↑u₀ = ↑s₁ * v₀
r₃ : R
s₃ : ↥S
h₃ : ↑u₀ * ↑s₃ = ↑u * r₃
this : ↑s₁ * (v * r₃) = ↑s₁ * (v₀ * ↑s₃)
s₄ : ↥S
hs₄ : v * r₃ * ↑s₄ = v₀ * ↑s₃ * ↑s₄
⊢ ↑s₂ * ↑(oreDenom r₂ s₁) * (↑s₃ * ↑s₄) = ↑s₂ * ↑u * (r₃ * ↑s₄)

Next tactic:
rw [mul_assoc (b := (u₀ : R)), ← mul_assoc (a := (u₀ : R)), h₃]