l3lab/ntp-mathlib huggingface dataset:
/-- Map `M ∗ N` to `N ∗ M` by applying `Sum.swap` to each element of the underlying list.

See also `MulEquiv.coprodComm` for a `MulEquiv` version. -/
@[to_additive "Map `AddMonoid.Coprod M N` to `AddMonoid.Coprod N M`
  by applying `Sum.swap` to each element of the underlying list.

See also `AddEquiv.coprodComm` for an `AddEquiv` version."]
def swap : M ∗ N →* N ∗ M :=
  clift (mk.comp <| FreeMonoid.map Sum.swap)
    (by simp only [MonoidHom.comp_apply, map_of, Sum.swap_inl, mk_of_inr, map_one])
    (by simp only [MonoidHom.comp_apply, map_of, Sum.swap_inr, mk_of_inl, map_one])
    (fun x y => by simp only [MonoidHom.comp_apply, map_of, Sum.swap_inl, mk_of_inr, map_mul])
    (fun x y => by 

Proof state:
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
P : Type u_5
inst✝⁴ : MulOneClass M
inst✝³ : MulOneClass N
inst✝² : MulOneClass M'
inst✝¹ : MulOneClass N'
inst✝ : MulOneClass P
x y : N
⊢ (MonoidHom.comp mk (FreeMonoid.map Sum.swap)) (of (Sum.inr (x * y))) =
    (MonoidHom.comp mk (FreeMonoid.map Sum.swap)) (of (Sum.inr x) * of (Sum.inr y))

Next tactic:
simp only [MonoidHom.comp_apply, map_of, Sum.swap_inr, mk_of_inl, map_mul]
@[to_additive]
theorem comp_lift {P' : Type*} [Monoid P'] (f : P →* P') (g₁ : M →* P) (g₂ : N →* P) :
    f.comp (lift g₁ g₂) = lift (f.comp g₁) (f.comp g₂) :=
  hom_ext (by 

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : Monoid P
P' : Type u_4
inst✝ : Monoid P'
f : P →* P'
g₁ : M →* P
g₂ : N →* P
⊢ MonoidHom.comp (MonoidHom.comp f (lift g₁ g₂)) inl =
    MonoidHom.comp (lift (MonoidHom.comp f g₁) (MonoidHom.comp f g₂)) inl

Next tactic:
rw [MonoidHom.comp_assoc, lift_comp_inl, lift_comp_inl]
@[to_additive]
theorem comp_lift {P' : Type*} [Monoid P'] (f : P →* P') (g₁ : M →* P) (g₂ : N →* P) :
    f.comp (lift g₁ g₂) = lift (f.comp g₁) (f.comp g₂) :=
  hom_ext (by rw [MonoidHom.comp_assoc, lift_comp_inl, lift_comp_inl]) <| by
    

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : Monoid P
P' : Type u_4
inst✝ : Monoid P'
f : P →* P'
g₁ : M →* P
g₂ : N →* P
⊢ MonoidHom.comp (MonoidHom.comp f (lift g₁ g₂)) inr =
    MonoidHom.comp (lift (MonoidHom.comp f g₁) (MonoidHom.comp f g₂)) inr

Next tactic:
rw [MonoidHom.comp_assoc, lift_comp_inr, lift_comp_inr]
@[to_additive (attr := simp)]
theorem mrange_lift (f : M →* P) (g : N →* P) :
    MonoidHom.mrange (lift f g) = MonoidHom.mrange f ⊔ MonoidHom.mrange g := by
  

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : MulOneClass M
inst✝¹ : MulOneClass N
inst✝ : Monoid P
f : M →* P
g : N →* P
⊢ MonoidHom.mrange (lift f g) = MonoidHom.mrange f ⊔ MonoidHom.mrange g

Next tactic:
simp [mrange_eq]
@[to_additive (attr := simp)]
theorem fst_prod_snd : (fst : M ∗ N →* M).prod snd = toProd := by 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
⊢ MonoidHom.prod fst snd = toProd

Next tactic:
ext1
@[to_additive (attr := simp)]
theorem fst_prod_snd : (fst : M ∗ N →* M).prod snd = toProd := by ext1 <;> 

Proof state:
case h₁
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
⊢ MonoidHom.comp (MonoidHom.prod fst snd) inl = MonoidHom.comp toProd inl

Next tactic:
rfl
@[to_additive (attr := simp)]
theorem fst_prod_snd : (fst : M ∗ N →* M).prod snd = toProd := by ext1 <;> 

Proof state:
case h₂
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
⊢ MonoidHom.comp (MonoidHom.prod fst snd) inr = MonoidHom.comp toProd inr

Next tactic:
rfl
@[to_additive (attr := simp)]
theorem prod_mk_fst_snd (x : M ∗ N) : (fst x, snd x) = toProd x := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M ∗ N
⊢ (fst x, snd x) = toProd x

Next tactic:
rw [← fst_prod_snd, MonoidHom.prod_apply]
@[to_additive (attr := simp)]
theorem fst_comp_toProd : (MonoidHom.fst M N).comp toProd = fst := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
⊢ MonoidHom.comp (MonoidHom.fst M N) toProd = fst

Next tactic:
rw [← fst_prod_snd, MonoidHom.fst_comp_prod]
@[to_additive (attr := simp)]
theorem fst_toProd (x : M ∗ N) : (toProd x).1 = fst x := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M ∗ N
⊢ (toProd x).1 = fst x

Next tactic:
rw [← fst_comp_toProd]
@[to_additive (attr := simp)]
theorem fst_toProd (x : M ∗ N) : (toProd x).1 = fst x := by
  rw [← fst_comp_toProd]; 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M ∗ N
⊢ (toProd x).1 = (MonoidHom.comp (MonoidHom.fst M N) toProd) x

Next tactic:
rfl
@[to_additive (attr := simp)]
theorem snd_comp_toProd : (MonoidHom.snd M N).comp toProd = snd := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
⊢ MonoidHom.comp (MonoidHom.snd M N) toProd = snd

Next tactic:
rw [← fst_prod_snd, MonoidHom.snd_comp_prod]
@[to_additive (attr := simp)]
theorem snd_toProd (x : M ∗ N) : (toProd x).2 = snd x := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M ∗ N
⊢ (toProd x).2 = snd x

Next tactic:
rw [← snd_comp_toProd]
@[to_additive (attr := simp)]
theorem snd_toProd (x : M ∗ N) : (toProd x).2 = snd x := by
  rw [← snd_comp_toProd]; 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M ∗ N
⊢ (toProd x).2 = (MonoidHom.comp (MonoidHom.snd M N) toProd) x

Next tactic:
rfl
@[to_additive]
theorem toProd_surjective : Surjective (toProd : M ∗ N →* M × N) := fun x =>
  ⟨inl x.1 * inr x.2, by 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : Monoid M
inst✝ : Monoid N
x : M × N
⊢ toProd (inl x.1 * inr x.2) = x

Next tactic:
rw [map_mul, toProd_apply_inl, toProd_apply_inr, Prod.fst_mul_snd]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x : FreeMonoid (G ⊕ H)
⊢ (coprodCon G H) (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList x))) * x) 1

Next tactic:
rw [← mk_eq_mk, map_mul, map_one]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x : FreeMonoid (G ⊕ H)
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList x)))) * mk x = 1

Next tactic:
induction x using FreeMonoid.recOn
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  

Proof state:
case h0
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList 1)))) * mk 1 = 1
case ih
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x✝ : G ⊕ H
xs✝ : FreeMonoid (G ⊕ H)
a✝ : mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs✝)))) * mk xs✝ = 1
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList (of x✝ * xs✝))))) * mk (of x✝ * xs✝) = 1

Next tactic:
case h0 => simp [map_one mk]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList 1)))) * mk 1 = 1

Next tactic:
case h0 => simp [map_one mk]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  case h0 => 

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList 1)))) * mk 1 = 1

Next tactic:
simp [map_one mk]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  case h0 => simp [map_one mk] -- TODO: fails without `[map_one mk]`
  

Proof state:
case ih
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x✝ : G ⊕ H
xs✝ : FreeMonoid (G ⊕ H)
a✝ : mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs✝)))) * mk xs✝ = 1
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList (of x✝ * xs✝))))) * mk (of x✝ * xs✝) = 1

Next tactic:
case ih x xs ihx =>
    simp only [toList_of_mul, map_cons, reverse_cons, ofList_append, map_mul, ihx, ofList_singleton]
    rwa [mul_assoc, ← mul_assoc (mk (of _)), mk_of_inv_mul, one_mul]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  case h0 => simp [map_one mk] -- TODO: fails without `[map_one mk]`
  

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x : G ⊕ H
xs : FreeMonoid (G ⊕ H)
ihx : mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs)))) * mk xs = 1
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList (of x * xs))))) * mk (of x * xs) = 1

Next tactic:
case ih x xs ihx =>
    simp only [toList_of_mul, map_cons, reverse_cons, ofList_append, map_mul, ihx, ofList_singleton]
    rwa [mul_assoc, ← mul_assoc (mk (of _)), mk_of_inv_mul, one_mul]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  case h0 => simp [map_one mk] -- TODO: fails without `[map_one mk]`
  case ih x xs ihx =>
    

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x : G ⊕ H
xs : FreeMonoid (G ⊕ H)
ihx : mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs)))) * mk xs = 1
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList (of x * xs))))) * mk (of x * xs) = 1

Next tactic:
simp only [toList_of_mul, map_cons, reverse_cons, ofList_append, map_mul, ihx, ofList_singleton]
@[to_additive]
theorem con_mul_left_inv (x : FreeMonoid (G ⊕ H)) :
    coprodCon G H (ofList (x.toList.map (Sum.map Inv.inv Inv.inv)).reverse * x) 1 := by
  rw [← mk_eq_mk, map_mul, map_one]
  induction x using FreeMonoid.recOn
  case h0 => simp [map_one mk] -- TODO: fails without `[map_one mk]`
  case ih x xs ihx =>
    simp only [toList_of_mul, map_cons, reverse_cons, ofList_append, map_mul, ihx, ofList_singleton]
    

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
x : G ⊕ H
xs : FreeMonoid (G ⊕ H)
ihx : mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs)))) * mk xs = 1
⊢ mk (ofList (reverse (List.map (Sum.map Inv.inv Inv.inv) (toList xs)))) * mk (of (Sum.map Inv.inv Inv.inv x)) *
      (mk (of x) * mk xs) =
    1

Next tactic:
rwa [mul_assoc, ← mul_assoc (mk (of _)), mk_of_inv_mul, one_mul]
@[to_additive (attr := simp)] theorem range_inl_sup_range_inr :
    MonoidHom.range (inl : G →* G ∗ H) ⊔ MonoidHom.range inr = ⊤ := by
  

Proof state:
G : Type u_1
H : Type u_2
inst✝¹ : Group G
inst✝ : Group H
⊢ MonoidHom.range inl ⊔ MonoidHom.range inr = ⊤

Next tactic:
rw [← closure_range_inl_union_inr, Subgroup.closure_union, ← MonoidHom.coe_range,
    ← MonoidHom.coe_range, Subgroup.closure_eq, Subgroup.closure_eq]
@[to_additive] theorem range_eq (f : G ∗ H →* K) :
    MonoidHom.range f = MonoidHom.range (f.comp inl) ⊔ MonoidHom.range (f.comp inr) := by
  

Proof state:
G : Type u_1
H : Type u_2
inst✝² : Group G
inst✝¹ : Group H
K : Type u_3
inst✝ : Group K
f : G ∗ H →* K
⊢ MonoidHom.range f = MonoidHom.range (MonoidHom.comp f inl) ⊔ MonoidHom.range (MonoidHom.comp f inr)

Next tactic:
rw [MonoidHom.range_eq_map, ← range_inl_sup_range_inr, Subgroup.map_sup, MonoidHom.map_range,
    MonoidHom.map_range]
@[to_additive (attr := simp)] theorem range_lift (f : G →* K) (g : H →* K) :
    MonoidHom.range (lift f g) = MonoidHom.range f ⊔ MonoidHom.range g := by
  

Proof state:
G : Type u_1
H : Type u_2
inst✝² : Group G
inst✝¹ : Group H
K : Type u_3
inst✝ : Group K
f : G →* K
g : H →* K
⊢ MonoidHom.range (lift f g) = MonoidHom.range f ⊔ MonoidHom.range g

Next tactic:
simp [range_eq]
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by 

Proof state:
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
⊢ MonoidHom.comp (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e')) (Coprod.map ↑e ↑e') = MonoidHom.id (M ∗ M')

Next tactic:
ext
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by ext <;> 

Proof state:
case h₁.h
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
x✝ : M
⊢ (MonoidHom.comp (MonoidHom.comp (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e')) (Coprod.map ↑e ↑e')) inl) x✝ =
    (MonoidHom.comp (MonoidHom.id (M ∗ M')) inl) x✝

Next tactic:
simp
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by ext <;> 

Proof state:
case h₂.h
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
x✝ : M'
⊢ (MonoidHom.comp (MonoidHom.comp (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e')) (Coprod.map ↑e ↑e')) inr) x✝ =
    (MonoidHom.comp (MonoidHom.id (M ∗ M')) inr) x✝

Next tactic:
simp
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by ext <;> simp) (by 

Proof state:
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
⊢ MonoidHom.comp (Coprod.map ↑e ↑e') (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e')) = MonoidHom.id (N ∗ N')

Next tactic:
ext
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by ext <;> simp) (by ext <;> 

Proof state:
case h₁.h
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
x✝ : N
⊢ (MonoidHom.comp (MonoidHom.comp (Coprod.map ↑e ↑e') (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e'))) inl) x✝ =
    (MonoidHom.comp (MonoidHom.id (N ∗ N')) inl) x✝

Next tactic:
simp
/-- Lift two monoid equivalences `e : M ≃* N` and `e' : M' ≃* N'` to a monoid equivalence
`(M ∗ M') ≃* (N ∗ N')`. -/
@[to_additive (attr := simps! (config := .asFn)) "Lift two additive monoid
equivalences `e : M ≃+ N` and `e' : M' ≃+ N'` to an additive monoid equivalence
`(AddMonoid.Coprod M M') ≃+ (AddMonoid.Coprod N N')`."]
def coprodCongr (e : M ≃* N) (e' : M' ≃* N') : (M ∗ M') ≃* (N ∗ N') :=
  (Coprod.map (e : M →* N) (e' : M' →* N')).toMulEquiv (Coprod.map e.symm e'.symm)
    (by ext <;> simp) (by ext <;> 

Proof state:
case h₂.h
M : Type u_1
N : Type u_2
M' : Type u_3
N' : Type u_4
inst✝³ : MulOneClass M
inst✝² : MulOneClass N
inst✝¹ : MulOneClass M'
inst✝ : MulOneClass N'
e : M ≃* N
e' : M' ≃* N'
x✝ : N'
⊢ (MonoidHom.comp (MonoidHom.comp (Coprod.map ↑e ↑e') (Coprod.map ↑(MulEquiv.symm e) ↑(MulEquiv.symm e'))) inr) x✝ =
    (MonoidHom.comp (MonoidHom.id (N ∗ N')) inr) x✝

Next tactic:
simp
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by 

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
⊢ MonoidHom.comp (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P)))
      (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr)) =
    MonoidHom.id ((M ∗ N) ∗ P)

Next tactic:
ext
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> 

Proof state:
case h₁.h₁.h
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
x✝ : M
⊢ (MonoidHom.comp
        (MonoidHom.comp
          (MonoidHom.comp (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P)))
            (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr)))
          inl)
        inl)
      x✝ =
    (MonoidHom.comp (MonoidHom.comp (MonoidHom.id ((M ∗ N) ∗ P)) inl) inl) x✝

Next tactic:
rfl
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> 

Proof state:
case h₁.h₂.h
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
x✝ : N
⊢ (MonoidHom.comp
        (MonoidHom.comp
          (MonoidHom.comp (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P)))
            (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr)))
          inl)
        inr)
      x✝ =
    (MonoidHom.comp (MonoidHom.comp (MonoidHom.id ((M ∗ N) ∗ P)) inl) inr) x✝

Next tactic:
rfl
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> 

Proof state:
case h₂.h
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
x✝ : P
⊢ (MonoidHom.comp
        (MonoidHom.comp (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P)))
          (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr)))
        inr)
      x✝ =
    (MonoidHom.comp (MonoidHom.id ((M ∗ N) ∗ P)) inr) x✝

Next tactic:
rfl
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> rfl) (by 

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
⊢ MonoidHom.comp (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr))
      (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P))) =
    MonoidHom.id (M ∗ (N ∗ P))

Next tactic:
ext
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> rfl) (by ext <;> 

Proof state:
case h₁.h
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
x✝ : M
⊢ (MonoidHom.comp
        (MonoidHom.comp (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr))
          (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P))))
        inl)
      x✝ =
    (MonoidHom.comp (MonoidHom.id (M ∗ (N ∗ P))) inl) x✝

Next tactic:
rfl
/-- A multiplicative equivalence between `(M ∗ N) ∗ P` and `M ∗ (N ∗ P)`. -/
@[to_additive "An additive equivalence between `AddMonoid.Coprod (AddMonoid.Coprod M N) P` and
`AddMonoid.Coprod M (AddMonoid.Coprod N P)`."]
def coprodAssoc : (M ∗ N) ∗ P ≃* M ∗ (N ∗ P) :=
  MonoidHom.toMulEquiv
    (Coprod.lift (Coprod.map (.id M) inl) (inr.comp inr))
    (Coprod.lift (inl.comp inl) (Coprod.map inr (.id P)))
    (by ext <;> rfl) (by ext <;> 

Proof state:
case h₂.h₁.h
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Monoid M
inst✝¹ : Monoid N
inst✝ : Monoid P
x✝ : N
⊢ (MonoidHom.comp
        (MonoidHom.comp
          (MonoidHom.comp (Coprod.lift (Coprod.map (MonoidHom.id M) inl) (MonoidHom.comp inr inr))
            (Coprod.lift (MonoidHom.comp inl inl) (Coprod.map inr (MonoidHom.id P))))
          inr)
        inl)
      x✝ =
    (MonoidHom.comp (MonoidHom.comp (MonoidHom.id (M ∗ (N ∗ P))) inr) inl) x✝

Next tactic:
rfl