l3lab/ntp-mathlib huggingface dataset:
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    

Proof state:
case h
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
j : K
⊢ (E.ι.app j ≫ GrothendieckTopology.toSheafify J E.pt) ≫
      GrothendieckTopology.sheafifyLift J (IsColimit.desc hE ((sheafToPresheaf J D).mapCocone S))
        (_ : Presheaf.IsSheaf J S.pt.val) =
    (S.ι.app j).val

Next tactic:
erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    

Proof state:
case h
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
j : K
⊢ ((sheafToPresheaf J D).mapCocone S).ι.app j = (S.ι.app j).val

Next tactic:
rfl
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    

Proof state:
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
⊢ ∀ (s : Cocone F) (m : (sheafifyCocone E).pt ⟶ s.pt),
    (∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = s.ι.app j) →
      m =
        (fun S =>
            {
              val :=
                GrothendieckTopology.sheafifyLift J (IsColimit.desc hE ((sheafToPresheaf J D).mapCocone S))
                  (_ : Presheaf.IsSheaf J S.pt.val) })
          s

Next tactic:
intro S m hm
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    

Proof state:
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
⊢ m =
    (fun S =>
        {
          val :=
            GrothendieckTopology.sheafifyLift J (IsColimit.desc hE ((sheafToPresheaf J D).mapCocone S))
              (_ : Presheaf.IsSheaf J S.pt.val) })
      S

Next tactic:
ext1
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    

Proof state:
case h
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
⊢ m.val =
    ((fun S =>
          {
            val :=
              GrothendieckTopology.sheafifyLift J (IsColimit.desc hE ((sheafToPresheaf J D).mapCocone S))
                (_ : Presheaf.IsSheaf J S.pt.val) })
        S).val

Next tactic:
apply J.sheafifyLift_unique
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    apply J.sheafifyLift_unique
    

Proof state:
case h.a
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
⊢ GrothendieckTopology.toSheafify J E.pt ≫ m.val = IsColimit.desc hE ((sheafToPresheaf J D).mapCocone S)

Next tactic:
apply hE.uniq ((sheafToPresheaf J D).mapCocone S)
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    apply J.sheafifyLift_unique
    apply hE.uniq ((sheafToPresheaf J D).mapCocone S)
    

Proof state:
case h.a.x
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
⊢ ∀ (j : K), E.ι.app j ≫ GrothendieckTopology.toSheafify J E.pt ≫ m.val = ((sheafToPresheaf J D).mapCocone S).ι.app j

Next tactic:
intro j
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    apply J.sheafifyLift_unique
    apply hE.uniq ((sheafToPresheaf J D).mapCocone S)
    intro j
    

Proof state:
case h.a.x
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
j : K
⊢ E.ι.app j ≫ GrothendieckTopology.toSheafify J E.pt ≫ m.val = ((sheafToPresheaf J D).mapCocone S).ι.app j

Next tactic:
dsimp
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    apply J.sheafifyLift_unique
    apply hE.uniq ((sheafToPresheaf J D).mapCocone S)
    intro j
    dsimp
    

Proof state:
case h.a.x
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
j : K
⊢ E.ι.app j ≫ GrothendieckTopology.toSheafify J E.pt ≫ m.val = (S.ι.app j).val

Next tactic:
simp only [← Category.assoc, ← hm]
/-- If `E` is a colimit cocone of presheaves, over a diagram factoring through sheaves,
then `sheafifyCocone E` is a colimit cocone. -/
@[simps]
noncomputable def isColimitSheafifyCocone {F : K ⥤ Sheaf J D}
    (E : Cocone (F ⋙ sheafToPresheaf J D)) (hE : IsColimit E) : IsColimit (sheafifyCocone E) where
  desc S := ⟨J.sheafifyLift (hE.desc ((sheafToPresheaf J D).mapCocone S)) S.pt.2⟩
  fac := by
    intro S j
    ext1
    dsimp [sheafifyCocone]
    erw [Category.assoc, J.toSheafify_sheafifyLift, hE.fac]
    rfl
  uniq := by
    intro S m hm
    ext1
    apply J.sheafifyLift_unique
    apply hE.uniq ((sheafToPresheaf J D).mapCocone S)
    intro j
    dsimp
    simp only [← Category.assoc, ← hm] -- Porting note: was `simpa only [...]`
    

Proof state:
case h.a.x
C : Type u
inst✝⁸ : Category.{v, u} C
J : GrothendieckTopology C
D : Type w
inst✝⁷ : Category.{max v u, w} D
K : Type z
inst✝⁶ : Category.{z', z} K
inst✝⁵ : ConcreteCategory D
inst✝⁴ :
  ∀ (P : Cᵒᵖ ⥤ D) (X : C) (S : GrothendieckTopology.Cover J X), HasMultiequalizer (GrothendieckTopology.Cover.index S P)
inst✝³ : PreservesLimits (forget D)
inst✝² : ∀ (X : C), HasColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ D
inst✝¹ : (X : C) → PreservesColimitsOfShape (GrothendieckTopology.Cover J X)ᵒᵖ (forget D)
inst✝ : ReflectsIsomorphisms (forget D)
F : K ⥤ Sheaf J D
E : Cocone (F ⋙ sheafToPresheaf J D)
hE : IsColimit E
S : Cocone F
m : (sheafifyCocone E).pt ⟶ S.pt
hm : ∀ (j : K), (sheafifyCocone E).ι.app j ≫ m = S.ι.app j
j : K
⊢ (E.ι.app j ≫ GrothendieckTopology.toSheafify J E.pt) ≫ m.val = ((sheafifyCocone E).ι.app j ≫ m).val

Next tactic:
rfl
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  

Proof state:
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ Group (MulAut M)

Next tactic:
refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. }
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_1
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a b c : MulAut M), a * b * c = a * (b * c)

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_2
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a : MulAut M), 1 * a = a

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_3
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a : MulAut M), a * 1 = a

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_4
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (x : MulAut M), npowRec 0 x = 1

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_5
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (n : ℕ) (x : MulAut M), npowRec (n + 1) x = x * npowRec n x

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_6
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a b : MulAut M), a / b = a * b⁻¹

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_7
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a : MulAut M), zpowRec 0 a = 1

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_8
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (n : ℕ) (a : MulAut M), zpowRec (Int.ofNat (Nat.succ n)) a = a * zpowRec (Int.ofNat n) a

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_9
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (n : ℕ) (a : MulAut M), zpowRec (Int.negSucc n) a = (zpowRec (↑(Nat.succ n)) a)⁻¹

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  

Proof state:
case refine'_10
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
⊢ ∀ (a : MulAut M), a⁻¹ * a = 1

Next tactic:
intros
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  intros <;>
  

Proof state:
case refine'_1
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
a✝ b✝ c✝ : MulAut M
⊢ a✝ * b✝ * c✝ = a✝ * (b✝ * c✝)

Next tactic:
ext
/-- The group operation on multiplicative automorphisms is defined by `g h => MulEquiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : Group (MulAut M) := by
  refine'
  { mul := fun g h => MulEquiv.trans h g
    one := MulEquiv.refl M
    inv := MulEquiv.symm
    div := fun g h => MulEquiv.trans h.symm g
    npow := @npowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩
    zpow := @zpowRec _ ⟨MulEquiv.refl M⟩ ⟨fun g h => MulEquiv.trans h g⟩ ⟨MulEquiv.symm⟩
    .. } <;>
  intros <;>
  

Proof state:
case refine'_2
A : Type u_1
M : Type u_2
G : Type u_3
inst✝ : Mul M
a✝ : MulAut M
⊢ 1 * a✝ = a✝

Next tactic:
ext