l3lab/ntp-mathlib huggingface dataset:
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
⊢ r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s

Next tactic:
rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  

Proof state:
case mk.mk
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s

Next tactic:
calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * (r' * t) * r₂' /ₒ (s * t') := by rw [h]
    _ = r₁ * r' * (t * r₂') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r' * (r₂ * t') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * r' * r₂ * t' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r' * r₂ /ₒ s := (OreLocalization.expand _ _ _ _).symm
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r * r₂ * ↑t' /ₒ (s * t') = r₁ * r * (r₂ * ↑t') /ₒ (s * t')

Next tactic:
simp [← mul_assoc]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r * (r₂ * ↑t') /ₒ (s * t') = r₁ * r * (↑t * r₂') /ₒ (s * t')

Next tactic:
rw [hr₂]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r * (↑t * r₂') /ₒ (s * t') = r₁ * (r * ↑t) * r₂' /ₒ (s * t')

Next tactic:
simp [← mul_assoc]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * (r' * t) * r₂' /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * (r * ↑t) * r₂' /ₒ (s * t') = r₁ * (r' * ↑t) * r₂' /ₒ (s * t')

Next tactic:
rw [h]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * (r' * t) * r₂' /ₒ (s * t') := by rw [h]
    _ = r₁ * r' * (t * r₂') /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * (r' * ↑t) * r₂' /ₒ (s * t') = r₁ * r' * (↑t * r₂') /ₒ (s * t')

Next tactic:
simp [← mul_assoc]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * (r' * t) * r₂' /ₒ (s * t') := by rw [h]
    _ = r₁ * r' * (t * r₂') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r' * (r₂ * t') /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r' * (↑t * r₂') /ₒ (s * t') = r₁ * r' * (r₂ * ↑t') /ₒ (s * t')

Next tactic:
rw [hr₂]
/-- Fractions which differ by a factor of the numerator can be proven equal if
those factors expand to equal elements of `R`. -/
protected theorem eq_of_num_factor_eq {r r' r₁ r₂ : R} {s t : S} (h : r * t = r' * t) :
    r₁ * r * r₂ /ₒ s = r₁ * r' * r₂ /ₒ s := by
  rcases oreCondition r₂ t with ⟨r₂', t', hr₂⟩
  -- porting note: todo: use `assoc_rw`?
  calc
    r₁ * r * r₂ /ₒ s = r₁ * r * r₂ * t' /ₒ (s * t') := OreLocalization.expand (r₁ * r * r₂) s t' _
    _ = r₁ * r * (r₂ * t') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r * (t * r₂') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * (r * t) * r₂' /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * (r' * t) * r₂' /ₒ (s * t') := by rw [h]
    _ = r₁ * r' * (t * r₂') /ₒ (s * t') := by simp [← mul_assoc]
    _ = r₁ * r' * (r₂ * t') /ₒ (s * t') := by rw [hr₂]
    _ = r₁ * r' * r₂ * t' /ₒ (s * t') := by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
r r' r₁ r₂ : R
s t : ↥S
h : r * ↑t = r' * ↑t
r₂' : R
t' : ↥S
hr₂ : r₂ * ↑t' = ↑t * r₂'
⊢ r₁ * r' * (r₂ * ↑t') /ₒ (s * t') = r₁ * r' * r₂ * ↑t' /ₒ (s * t')

Next tactic:
simp [← mul_assoc]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : (r₂, s₂).1 * ↑u = (r₁, s₁).1 * v
hs₂ : ↑(r₂, s₂).2 * ↑u = ↑(r₁, s₁).2 * v
⊢ (fun p => P p.1 p.2) (r₁, s₁) = (fun p => P p.1 p.2) (r₂, s₂)

Next tactic:
dsimp at *
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    

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

Next tactic:
have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      

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

Next tactic:
rw [← hs₂, ← S.coe_mul]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      

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

Next tactic:
exact SetLike.coe_mem (s₂ * u)
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
hs₂ : ↑s₂ * ↑u = ↑s₁ * v
s₁vS : ↑s₁ * v ∈ S
⊢ P r₁ s₁ = P r₂ s₂

Next tactic:
replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · 

Proof state:
case hs₂
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
hs₂ : ↑s₂ * ↑u = ↑s₁ * v
s₁vS : ↑s₁ * v ∈ S
⊢ s₂ * u = { val := ↑s₁ * v, property := s₁vS }

Next tactic:
ext
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; 

Proof state:
case hs₂.a
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
hs₂ : ↑s₂ * ↑u = ↑s₁ * v
s₁vS : ↑s₁ * v ∈ S
⊢ ↑(s₂ * u) = ↑{ val := ↑s₁ * v, property := s₁vS }

Next tactic:
simp [hs₂]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; simp [hs₂]
    

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
s₁vS : ↑s₁ * v ∈ S
hs₂ : s₂ * u = { val := ↑s₁ * v, property := s₁vS }
⊢ P r₁ s₁ = P r₂ s₂

Next tactic:
rw [hP r₁ v s₁ s₁vS, hP r₂ u s₂ (by norm_cast; rwa [hs₂]), hr₂]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; simp [hs₂]
    rw [hP r₁ v s₁ s₁vS, hP r₂ u s₂ (by 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
s₁vS : ↑s₁ * v ∈ S
hs₂ : s₂ * u = { val := ↑s₁ * v, property := s₁vS }
⊢ ↑s₂ * ↑u ∈ S

Next tactic:
norm_cast
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; simp [hs₂]
    rw [hP r₁ v s₁ s₁vS, hP r₂ u s₂ (by norm_cast; 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
s₁vS : ↑s₁ * v ∈ S
hs₂ : s₂ * u = { val := ↑s₁ * v, property := s₁vS }
⊢ ↑(s₂ * u) ∈ S

Next tactic:
rwa [hs₂]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; simp [hs₂]
    rw [hP r₁ v s₁ s₁vS, hP r₂ u s₂ (by norm_cast; rwa [hs₂]), hr₂]
    

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
s₁vS : ↑s₁ * v ∈ S
hs₂ : s₂ * u = { val := ↑s₁ * v, property := s₁vS }
⊢ P (r₁ * v) { val := ↑s₁ * v, property := s₁vS } = P (r₁ * v) { val := ↑s₂ * ↑u, property := (_ : ↑s₂ * ↑u ∈ S) }

Next tactic:
simp only [← hs₂]
/-- A function or predicate over `R` and `S` can be lifted to `R[S⁻¹]` if it is invariant
under expansion on the right. -/
def liftExpand {C : Sort*} (P : R → S → C)
    (hP : ∀ (r t : R) (s : S) (ht : (s : R) * t ∈ S), P r s = P (r * t) ⟨s * t, ht⟩) : R[S⁻¹] → C :=
  Quotient.lift (fun p : R × S => P p.1 p.2) fun (r₁, s₁) (r₂, s₂) ⟨u, v, hr₂, hs₂⟩ => by
    dsimp at *
    have s₁vS : (s₁ : R) * v ∈ S := by
      rw [← hs₂, ← S.coe_mul]
      exact SetLike.coe_mem (s₂ * u)
    replace hs₂ : s₂ * u = ⟨(s₁ : R) * v, s₁vS⟩
    · ext; simp [hs₂]
    rw [hP r₁ v s₁ s₁vS, hP r₂ u s₂ (by norm_cast; rwa [hs₂]), hr₂]
    simp only [← hs₂]; 

Proof state:
R : Type u_1
inst✝¹ : Monoid R
S : Submonoid R
inst✝ : OreSet S
C : Sort u_2
P : R → ↥S → C
hP : ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S), P r s = P (r * t) { val := ↑s * t, property := ht }
x✝² x✝¹ : R × ↥S
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
x✝ : (r₁, s₁) ≈ (r₂, s₂)
u : ↥S
v : R
hr₂ : r₂ * ↑u = r₁ * v
s₁vS : ↑s₁ * v ∈ S
hs₂ : s₂ * u = { val := ↑s₁ * v, property := s₁vS }
⊢ P (r₁ * v) (s₂ * u) = P (r₁ * v) { val := ↑s₂ * ↑u, property := (_ : ↑s₂ * ↑u ∈ S) }

Next tactic:
rfl
/-- 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
      

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₁ : R
s₁ : ↥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₂ }

Next tactic:
have := hP r₁ 1 s₁ (by simp) r₂ t₂ s₂ ht₂
/-- 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 

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₁ : R
s₁ : ↥S
r₂ t₂ : R
s₂ : ↥S
ht₂ : ↑s₂ * t₂ ∈ 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₂
      

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₁ : R
s₁ : ↥S
r₂ t₂ : R
s₂ : ↥S
ht₂ : ↑s₂ * t₂ ∈ S
this :
  P r₁ s₁ r₂ s₂ =
    P (r₁ * 1) { val := ↑s₁ * 1, property := (_ : ↑s₁ * 1 ∈ S) } (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }
⊢ P r₁ s₁ r₂ s₂ = P r₁ s₁ (r₂ * t₂) { val := ↑s₂ * t₂, property := ht₂ }

Next tactic:
simp [this]
/-- 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
    

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
⊢ (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₁ =
    (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₁ }

Next tactic:
ext x