l3lab/ntp-mathlib huggingface dataset:
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; 

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
ha' : r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₁ * ra /ₒ (s₂ * sa)
⊢ liftExpand (fun r s => f r * ↑(fS s)⁻¹)
      (_ :
        ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
          (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
      (r₁ /ₒ s₁ * (r₂ /ₒ s₂)) =
    liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₁ /ₒ s₁) *
      liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₂ /ₒ s₂)

Next tactic:
rw [ha']
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; 

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
ha' : r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₁ * ra /ₒ (s₂ * sa)
⊢ liftExpand (fun r s => f r * ↑(fS s)⁻¹)
      (_ :
        ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
          (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
      (r₁ * ra /ₒ (s₂ * sa)) =
    liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₁ /ₒ s₁) *
      liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₂ /ₒ s₂)

Next tactic:
clear ha'
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
⊢ liftExpand (fun r s => f r * ↑(fS s)⁻¹)
      (_ :
        ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
          (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
      (r₁ * ra /ₒ (s₂ * sa)) =
    liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₁ /ₒ s₁) *
      liftExpand (fun r s => f r * ↑(fS s)⁻¹)
        (_ :
          ∀ (r t : R) (s : ↥S) (ht : ↑s * t ∈ S),
            (fun r s => f r * ↑(fS s)⁻¹) r s = (fun r s => f r * ↑(fS s)⁻¹) (r * t) { val := ↑s * t, property := ht })
        (r₂ /ₒ s₂)

Next tactic:
rw [liftExpand_of, liftExpand_of, liftExpand_of]
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
⊢ f (r₁ * ra) * ↑(fS (s₂ * sa))⁻¹ = f r₁ * ↑(fS s₁)⁻¹ * (f r₂ * ↑(fS s₂)⁻¹)

Next tactic:
conv_rhs =>
      congr
      · skip
      congr
      rw [← mul_one (f r₂), ← (fS sa).mul_inv, ← mul_assoc, ← hf, ← f.map_mul, ha, f.map_mul]
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      

Proof state:
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₁ * ↑(fS s₁)⁻¹ * (f r₂ * ↑(fS s₂)⁻¹)

Next tactic:
congr
      · skip
      congr
      rw [← mul_one (f r₂), ← (fS sa).mul_inv, ← mul_assoc, ← hf, ← f.map_mul, ha, f.map_mul]
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      

Proof state:
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₁ * ↑(fS s₁)⁻¹ * (f r₂ * ↑(fS s₂)⁻¹)

Next tactic:
congr
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      

Proof state:
case a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₁ * ↑(fS s₁)⁻¹
case a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₂ * ↑(fS s₂)⁻¹

Next tactic:
· skip
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      · 

Proof state:
case a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₁ * ↑(fS s₁)⁻¹

Next tactic:
skip
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      · skip
      

Proof state:
case a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₂ * ↑(fS s₂)⁻¹

Next tactic:
congr
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      · skip
      congr
      

Proof state:
case a.a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| f r₂
case a.a
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
| ↑(fS s₂)⁻¹

Next tactic:
rw [← mul_one (f r₂), ← (fS sa).mul_inv, ← mul_assoc, ← hf, ← f.map_mul, ha, f.map_mul]
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      · skip
      congr
      rw [← mul_one (f r₂), ← (fS sa).mul_inv, ← mul_assoc, ← hf, ← f.map_mul, ha, f.map_mul]
    

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
⊢ f (r₁ * ra) * ↑(fS (s₂ * sa))⁻¹ = f r₁ * ↑(fS s₁)⁻¹ * (f ↑s₁ * f ra * ↑(fS sa)⁻¹ * ↑(fS s₂)⁻¹)

Next tactic:
rw [mul_assoc, mul_assoc, mul_assoc, ← mul_assoc _ (f s₁), hf s₁, (fS s₁).inv_mul, one_mul,
      f.map_mul, mul_assoc, fS.map_mul, ← Units.val_mul]
/-- The universal lift from a morphism `R →* T`, which maps elements of `S` to units of `T`,
to a morphism `R[S⁻¹] →* T`. -/
def universalMulHom : R[S⁻¹] →* T
    where
  -- Porting note: `simp only []` required for beta reductions
  toFun x :=
    x.liftExpand (fun r s => f r * ((fS s)⁻¹ : Units T)) fun r t s ht => by
      simp only []
      have : (fS ⟨s * t, ht⟩ : T) = fS s * f t := by
        simp only [← hf, MonoidHom.map_mul]
      conv_rhs =>
        rw [MonoidHom.map_mul, ← mul_one (f r), ← Units.val_one, ← mul_left_inv (fS s)]
        rw [Units.val_mul, ← mul_assoc, mul_assoc _ (fS s : T), ← this, mul_assoc]
      simp only [mul_one, Units.mul_inv]
  map_one' := by simp only []; rw [OreLocalization.one_def, liftExpand_of]; simp
  map_mul' x y := by
    simp only []
    induction' x using OreLocalization.ind with r₁ s₁
    induction' y using OreLocalization.ind with r₂ s₂
    rcases oreDivMulChar' r₁ r₂ s₁ s₂ with ⟨ra, sa, ha, ha'⟩; rw [ha']; clear ha'
    rw [liftExpand_of, liftExpand_of, liftExpand_of]
    conv_rhs =>
      congr
      · skip
      congr
      rw [← mul_one (f r₂), ← (fS sa).mul_inv, ← mul_assoc, ← hf, ← f.map_mul, ha, f.map_mul]
    rw [mul_assoc, mul_assoc, mul_assoc, ← mul_assoc _ (f s₁), hf s₁, (fS s₁).inv_mul, one_mul,
      f.map_mul, mul_assoc, fS.map_mul, ← Units.val_mul]
    

Proof state:
case c.c.mk.mk.intro
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
ra : R
sa : ↥S
ha : r₂ * ↑sa = ↑s₁ * ra
⊢ f r₁ * (f ra * ↑(fS s₂ * fS sa)⁻¹) = f r₁ * (f ra * ↑((fS sa)⁻¹ * (fS s₂)⁻¹))

Next tactic:
rfl
theorem universalMulHom_commutes {r : R} : universalMulHom f fS hf (numeratorHom r) = f r := by
  

Proof state:
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
r : R
⊢ (universalMulHom f fS hf) (numeratorHom r) = f r

Next tactic:
simp [numeratorHom_apply, universalMulHom_apply]
/-- The universal morphism `universalMulHom` is unique. -/
theorem universalMulHom_unique (φ : R[S⁻¹] →* T) (huniv : ∀ r : R, φ (numeratorHom r) = f r) :
    φ = universalMulHom f fS hf := by
  

Proof state:
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
φ : OreLocalization R S →* T
huniv : ∀ (r : R), φ (numeratorHom r) = f r
⊢ φ = universalMulHom f fS hf

Next tactic:
ext x
/-- The universal morphism `universalMulHom` is unique. -/
theorem universalMulHom_unique (φ : R[S⁻¹] →* T) (huniv : ∀ r : R, φ (numeratorHom r) = f r) :
    φ = universalMulHom f fS hf := by
  ext x; 

Proof state:
case h
R : Type u_1
inst✝² : Monoid R
S : Submonoid R
inst✝¹ : OreSet S
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
φ : OreLocalization R S →* T
huniv : ∀ (r : R), φ (numeratorHom r) = f r
x : OreLocalization R S
⊢ φ x = (universalMulHom f fS hf) x

Next tactic:
induction' x using OreLocalization.ind with r s
/-- The universal morphism `universalMulHom` is unique. -/
theorem universalMulHom_unique (φ : R[S⁻¹] →* T) (huniv : ∀ r : R, φ (numeratorHom r) = f r) :
    φ = universalMulHom f fS hf := 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
T : Type u_2
inst✝ : Monoid T
f : R →* T
fS : ↥S →* Tˣ
hf : ∀ (s : ↥S), f ↑s = ↑(fS s)
φ : OreLocalization R S →* T
huniv : ∀ (r : R), φ (numeratorHom r) = f r
r : R
s : ↥S
⊢ φ (r /ₒ s) = (universalMulHom f fS hf) (r /ₒ s)

Next tactic:
rw [universalMulHom_apply, ← huniv r, numeratorHom_apply, ← mul_one (φ (r /ₒ s)), ←
    Units.val_one, ← mul_right_inv (fS s), Units.val_mul, ← mul_assoc, ← hf, ← huniv, ← φ.map_mul,
    numeratorHom_apply, OreLocalization.mul_cancel]
theorem oreDiv_mul_oreDiv_comm {r₁ r₂ : R} {s₁ s₂ : S} :
    r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₁ * r₂ /ₒ (s₁ * s₂) := by
  

Proof state:
R : Type u_1
inst✝¹ : CommMonoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ : ↥S
⊢ r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₁ * r₂ /ₒ (s₁ * s₂)

Next tactic:
rw [oreDiv_mul_char r₁ r₂ s₁ s₂ r₂ s₁ (by simp [mul_comm]), mul_comm s₂]
theorem oreDiv_mul_oreDiv_comm {r₁ r₂ : R} {s₁ s₂ : S} :
    r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₁ * r₂ /ₒ (s₁ * s₂) := by
  rw [oreDiv_mul_char r₁ r₂ s₁ s₂ r₂ s₁ (by 

Proof state:
R : Type u_1
inst✝¹ : CommMonoid R
S : Submonoid R
inst✝ : OreSet S
r₁ r₂ : R
s₁ s₂ : ↥S
⊢ r₂ * ↑s₁ = ↑s₁ * r₂

Next tactic:
simp [mul_comm]
instance : CommMonoid R[S⁻¹] :=
  { OreLocalization.instMonoidOreLocalization with
    mul_comm := fun x y => by
      

Proof state:
R : Type u_1
inst✝¹ : CommMonoid R
S : Submonoid R
inst✝ : OreSet S
src✝ : Monoid (OreLocalization R S) := instMonoidOreLocalization
x y : OreLocalization R S
⊢ x * y = y * x

Next tactic:
induction' x using OreLocalization.ind with r₁ s₁
instance : CommMonoid R[S⁻¹] :=
  { OreLocalization.instMonoidOreLocalization with
    mul_comm := fun x y => by
      induction' x using OreLocalization.ind with r₁ s₁
      

Proof state:
case c
R : Type u_1
inst✝¹ : CommMonoid R
S : Submonoid R
inst✝ : OreSet S
src✝ : Monoid (OreLocalization R S) := instMonoidOreLocalization
y : OreLocalization R S
r₁ : R
s₁ : ↥S
⊢ r₁ /ₒ s₁ * y = y * (r₁ /ₒ s₁)

Next tactic:
induction' y using OreLocalization.ind with r₂ s₂
instance : CommMonoid R[S⁻¹] :=
  { OreLocalization.instMonoidOreLocalization with
    mul_comm := fun x y => by
      induction' x using OreLocalization.ind with r₁ s₁
      induction' y using OreLocalization.ind with r₂ s₂
      

Proof state:
case c.c
R : Type u_1
inst✝¹ : CommMonoid R
S : Submonoid R
inst✝ : OreSet S
src✝ : Monoid (OreLocalization R S) := instMonoidOreLocalization
r₁ : R
s₁ : ↥S
r₂ : R
s₂ : ↥S
⊢ r₁ /ₒ s₁ * (r₂ /ₒ s₂) = r₂ /ₒ s₂ * (r₁ /ₒ s₁)

Next tactic:
rw [oreDiv_mul_oreDiv_comm, oreDiv_mul_oreDiv_comm, mul_comm r₁, mul_comm s₁]