l3lab/ntp-mathlib huggingface dataset:
/-- If `F` is cover-preserving and compatible-preserving,
then `F` is a continuous functor.

This result is basically <https://stacks.math.columbia.edu/tag/00WW>.
-/
lemma Functor.isContinuous_of_coverPreserving (hF₁ : CompatiblePreserving.{w} K F)
    (hF₂ : CoverPreserving J K F) : Functor.IsContinuous.{w} F J K where
  op_comp_isSheafOfTypes G X S hS x hx := by
    apply exists_unique_of_exists_of_unique
    · have H := G.2 _ (hF₂.cover_preserve hS)
      exact ⟨H.amalgamate (x.functorPushforward F) (hx.functorPushforward hF₁),
        fun V f hf => (H.isAmalgamation (hx.functorPushforward hF₁) (F.map f) _).trans
          (hF₁.apply_map _ hx hf)⟩
    · intro y₁ y₂ hy₁ hy₂
      apply (Presieve.isSeparated_of_isSheaf _ _ G.cond _ (hF₂.cover_preserve hS)).ext
      rintro Y _ ⟨Z, g, h, hg, rfl⟩
      

Proof state:
case hunique.intro.intro.intro.intro
C : Type u₁
inst✝² : Category.{v₁, u₁} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G✝ : C ⥤ D
hG : CompatiblePreserving K G✝
ℱ : SheafOfTypes K
Z✝ : C
T : Presieve Z✝
x✝ : FamilyOfElements (G✝.op ⋙ ℱ.val) T
h✝ : Compatible x✝
hF₁ : CompatiblePreserving K F
hF₂ : CoverPreserving J K F
G : SheafOfTypes K
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : FamilyOfElements (F.op ⋙ G.val) S.arrows
hx : Compatible x
y₁ y₂ : (F.op ⋙ G.val).obj (op X)
hy₁ : IsAmalgamation x y₁
hy₂ : IsAmalgamation x y₂
Y : D
Z : C
g : Z ⟶ X
h : Y ⟶ F.obj Z
hg : S.arrows g
⊢ G.val.map (h ≫ F.map g).op y₁ = G.val.map (h ≫ F.map g).op y₂

Next tactic:
dsimp
/-- If `F` is cover-preserving and compatible-preserving,
then `F` is a continuous functor.

This result is basically <https://stacks.math.columbia.edu/tag/00WW>.
-/
lemma Functor.isContinuous_of_coverPreserving (hF₁ : CompatiblePreserving.{w} K F)
    (hF₂ : CoverPreserving J K F) : Functor.IsContinuous.{w} F J K where
  op_comp_isSheafOfTypes G X S hS x hx := by
    apply exists_unique_of_exists_of_unique
    · have H := G.2 _ (hF₂.cover_preserve hS)
      exact ⟨H.amalgamate (x.functorPushforward F) (hx.functorPushforward hF₁),
        fun V f hf => (H.isAmalgamation (hx.functorPushforward hF₁) (F.map f) _).trans
          (hF₁.apply_map _ hx hf)⟩
    · intro y₁ y₂ hy₁ hy₂
      apply (Presieve.isSeparated_of_isSheaf _ _ G.cond _ (hF₂.cover_preserve hS)).ext
      rintro Y _ ⟨Z, g, h, hg, rfl⟩
      dsimp
      

Proof state:
case hunique.intro.intro.intro.intro
C : Type u₁
inst✝² : Category.{v₁, u₁} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G✝ : C ⥤ D
hG : CompatiblePreserving K G✝
ℱ : SheafOfTypes K
Z✝ : C
T : Presieve Z✝
x✝ : FamilyOfElements (G✝.op ⋙ ℱ.val) T
h✝ : Compatible x✝
hF₁ : CompatiblePreserving K F
hF₂ : CoverPreserving J K F
G : SheafOfTypes K
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : FamilyOfElements (F.op ⋙ G.val) S.arrows
hx : Compatible x
y₁ y₂ : (F.op ⋙ G.val).obj (op X)
hy₁ : IsAmalgamation x y₁
hy₂ : IsAmalgamation x y₂
Y : D
Z : C
g : Z ⟶ X
h : Y ⟶ F.obj Z
hg : S.arrows g
⊢ G.val.map ((F.map g).op ≫ h.op) y₁ = G.val.map ((F.map g).op ≫ h.op) y₂

Next tactic:
simp only [Functor.map_comp, types_comp_apply]
/-- If `F` is cover-preserving and compatible-preserving,
then `F` is a continuous functor.

This result is basically <https://stacks.math.columbia.edu/tag/00WW>.
-/
lemma Functor.isContinuous_of_coverPreserving (hF₁ : CompatiblePreserving.{w} K F)
    (hF₂ : CoverPreserving J K F) : Functor.IsContinuous.{w} F J K where
  op_comp_isSheafOfTypes G X S hS x hx := by
    apply exists_unique_of_exists_of_unique
    · have H := G.2 _ (hF₂.cover_preserve hS)
      exact ⟨H.amalgamate (x.functorPushforward F) (hx.functorPushforward hF₁),
        fun V f hf => (H.isAmalgamation (hx.functorPushforward hF₁) (F.map f) _).trans
          (hF₁.apply_map _ hx hf)⟩
    · intro y₁ y₂ hy₁ hy₂
      apply (Presieve.isSeparated_of_isSheaf _ _ G.cond _ (hF₂.cover_preserve hS)).ext
      rintro Y _ ⟨Z, g, h, hg, rfl⟩
      dsimp
      simp only [Functor.map_comp, types_comp_apply]
      

Proof state:
case hunique.intro.intro.intro.intro
C : Type u₁
inst✝² : Category.{v₁, u₁} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G✝ : C ⥤ D
hG : CompatiblePreserving K G✝
ℱ : SheafOfTypes K
Z✝ : C
T : Presieve Z✝
x✝ : FamilyOfElements (G✝.op ⋙ ℱ.val) T
h✝ : Compatible x✝
hF₁ : CompatiblePreserving K F
hF₂ : CoverPreserving J K F
G : SheafOfTypes K
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : FamilyOfElements (F.op ⋙ G.val) S.arrows
hx : Compatible x
y₁ y₂ : (F.op ⋙ G.val).obj (op X)
hy₁ : IsAmalgamation x y₁
hy₂ : IsAmalgamation x y₂
Y : D
Z : C
g : Z ⟶ X
h : Y ⟶ F.obj Z
hg : S.arrows g
⊢ G.val.map h.op (G.val.map (F.map g).op y₁) = G.val.map h.op (G.val.map (F.map g).op y₂)

Next tactic:
have H := (hy₁ g hg).trans (hy₂ g hg).symm
/-- If `F` is cover-preserving and compatible-preserving,
then `F` is a continuous functor.

This result is basically <https://stacks.math.columbia.edu/tag/00WW>.
-/
lemma Functor.isContinuous_of_coverPreserving (hF₁ : CompatiblePreserving.{w} K F)
    (hF₂ : CoverPreserving J K F) : Functor.IsContinuous.{w} F J K where
  op_comp_isSheafOfTypes G X S hS x hx := by
    apply exists_unique_of_exists_of_unique
    · have H := G.2 _ (hF₂.cover_preserve hS)
      exact ⟨H.amalgamate (x.functorPushforward F) (hx.functorPushforward hF₁),
        fun V f hf => (H.isAmalgamation (hx.functorPushforward hF₁) (F.map f) _).trans
          (hF₁.apply_map _ hx hf)⟩
    · intro y₁ y₂ hy₁ hy₂
      apply (Presieve.isSeparated_of_isSheaf _ _ G.cond _ (hF₂.cover_preserve hS)).ext
      rintro Y _ ⟨Z, g, h, hg, rfl⟩
      dsimp
      simp only [Functor.map_comp, types_comp_apply]
      have H := (hy₁ g hg).trans (hy₂ g hg).symm
      

Proof state:
case hunique.intro.intro.intro.intro
C : Type u₁
inst✝² : Category.{v₁, u₁} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G✝ : C ⥤ D
hG : CompatiblePreserving K G✝
ℱ : SheafOfTypes K
Z✝ : C
T : Presieve Z✝
x✝ : FamilyOfElements (G✝.op ⋙ ℱ.val) T
h✝ : Compatible x✝
hF₁ : CompatiblePreserving K F
hF₂ : CoverPreserving J K F
G : SheafOfTypes K
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : FamilyOfElements (F.op ⋙ G.val) S.arrows
hx : Compatible x
y₁ y₂ : (F.op ⋙ G.val).obj (op X)
hy₁ : IsAmalgamation x y₁
hy₂ : IsAmalgamation x y₂
Y : D
Z : C
g : Z ⟶ X
h : Y ⟶ F.obj Z
hg : S.arrows g
H : (F.op ⋙ G.val).map g.op y₁ = (F.op ⋙ G.val).map g.op y₂
⊢ G.val.map h.op (G.val.map (F.map g).op y₁) = G.val.map h.op (G.val.map (F.map g).op y₂)

Next tactic:
dsimp at H
/-- If `F` is cover-preserving and compatible-preserving,
then `F` is a continuous functor.

This result is basically <https://stacks.math.columbia.edu/tag/00WW>.
-/
lemma Functor.isContinuous_of_coverPreserving (hF₁ : CompatiblePreserving.{w} K F)
    (hF₂ : CoverPreserving J K F) : Functor.IsContinuous.{w} F J K where
  op_comp_isSheafOfTypes G X S hS x hx := by
    apply exists_unique_of_exists_of_unique
    · have H := G.2 _ (hF₂.cover_preserve hS)
      exact ⟨H.amalgamate (x.functorPushforward F) (hx.functorPushforward hF₁),
        fun V f hf => (H.isAmalgamation (hx.functorPushforward hF₁) (F.map f) _).trans
          (hF₁.apply_map _ hx hf)⟩
    · intro y₁ y₂ hy₁ hy₂
      apply (Presieve.isSeparated_of_isSheaf _ _ G.cond _ (hF₂.cover_preserve hS)).ext
      rintro Y _ ⟨Z, g, h, hg, rfl⟩
      dsimp
      simp only [Functor.map_comp, types_comp_apply]
      have H := (hy₁ g hg).trans (hy₂ g hg).symm
      dsimp at H
      

Proof state:
case hunique.intro.intro.intro.intro
C : Type u₁
inst✝² : Category.{v₁, u₁} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G✝ : C ⥤ D
hG : CompatiblePreserving K G✝
ℱ : SheafOfTypes K
Z✝ : C
T : Presieve Z✝
x✝ : FamilyOfElements (G✝.op ⋙ ℱ.val) T
h✝ : Compatible x✝
hF₁ : CompatiblePreserving K F
hF₂ : CoverPreserving J K F
G : SheafOfTypes K
X : C
S : Sieve X
hS : S ∈ GrothendieckTopology.sieves J X
x : FamilyOfElements (F.op ⋙ G.val) S.arrows
hx : Compatible x
y₁ y₂ : (F.op ⋙ G.val).obj (op X)
hy₁ : IsAmalgamation x y₁
hy₂ : IsAmalgamation x y₂
Y : D
Z : C
g : Z ⟶ X
h : Y ⟶ F.obj Z
hg : S.arrows g
H : G.val.map (F.map g).op y₁ = G.val.map (F.map g).op y₂
⊢ G.val.map h.op (G.val.map (F.map g).op y₁) = G.val.map h.op (G.val.map (F.map g).op y₂)

Next tactic:
rw [H]
/-- The induced functor `Sheaf K A ⥤ Sheaf J A` given by `G.op ⋙ _`
if `G` is a continuous functor.
-/
def Functor.sheafPushforwardContinuous [Functor.IsContinuous.{v₃} F J K] :
    Sheaf K A ⥤ Sheaf J A where
  obj ℱ := ⟨F.op ⋙ ℱ.val, F.op_comp_isSheaf J K ℱ⟩
  map f := ⟨((whiskeringLeft _ _ _).obj F.op).map f.val⟩
  map_id ℱ := by
    

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝¹ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G : C ⥤ D
hG : CompatiblePreserving K G
ℱ✝ : SheafOfTypes K
Z : C
T : Presieve Z
x : FamilyOfElements (G.op ⋙ ℱ✝.val) T
h : Compatible x
inst✝ : IsContinuous F J K
ℱ : Sheaf K A
⊢ { obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
          map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
      (𝟙 ℱ) =
    𝟙
      ({ obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
            map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.obj
        ℱ)

Next tactic:
ext1
/-- The induced functor `Sheaf K A ⥤ Sheaf J A` given by `G.op ⋙ _`
if `G` is a continuous functor.
-/
def Functor.sheafPushforwardContinuous [Functor.IsContinuous.{v₃} F J K] :
    Sheaf K A ⥤ Sheaf J A where
  obj ℱ := ⟨F.op ⋙ ℱ.val, F.op_comp_isSheaf J K ℱ⟩
  map f := ⟨((whiskeringLeft _ _ _).obj F.op).map f.val⟩
  map_id ℱ := by
    ext1
    

Proof state:
case h
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝¹ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G : C ⥤ D
hG : CompatiblePreserving K G
ℱ✝ : SheafOfTypes K
Z : C
T : Presieve Z
x : FamilyOfElements (G.op ⋙ ℱ✝.val) T
h : Compatible x
inst✝ : IsContinuous F J K
ℱ : Sheaf K A
⊢ ({ obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
            map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
        (𝟙 ℱ)).val =
    (𝟙
        ({ obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
              map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.obj
          ℱ)).val

Next tactic:
apply ((whiskeringLeft _ _ _).obj F.op).map_id
/-- The induced functor `Sheaf K A ⥤ Sheaf J A` given by `G.op ⋙ _`
if `G` is a continuous functor.
-/
def Functor.sheafPushforwardContinuous [Functor.IsContinuous.{v₃} F J K] :
    Sheaf K A ⥤ Sheaf J A where
  obj ℱ := ⟨F.op ⋙ ℱ.val, F.op_comp_isSheaf J K ℱ⟩
  map f := ⟨((whiskeringLeft _ _ _).obj F.op).map f.val⟩
  map_id ℱ := by
    ext1
    apply ((whiskeringLeft _ _ _).obj F.op).map_id
  map_comp f g := by
    

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝¹ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G : C ⥤ D
hG : CompatiblePreserving K G
ℱ : SheafOfTypes K
Z : C
T : Presieve Z
x : FamilyOfElements (G.op ⋙ ℱ.val) T
h : Compatible x
inst✝ : IsContinuous F J K
X✝ Y✝ Z✝ : Sheaf K A
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ { obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
          map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
      (f ≫ g) =
    { obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
            map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
        f ≫
      { obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
            map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
        g

Next tactic:
ext1
/-- The induced functor `Sheaf K A ⥤ Sheaf J A` given by `G.op ⋙ _`
if `G` is a continuous functor.
-/
def Functor.sheafPushforwardContinuous [Functor.IsContinuous.{v₃} F J K] :
    Sheaf K A ⥤ Sheaf J A where
  obj ℱ := ⟨F.op ⋙ ℱ.val, F.op_comp_isSheaf J K ℱ⟩
  map f := ⟨((whiskeringLeft _ _ _).obj F.op).map f.val⟩
  map_id ℱ := by
    ext1
    apply ((whiskeringLeft _ _ _).obj F.op).map_id
  map_comp f g := by
    ext1
    

Proof state:
case h
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
F : C ⥤ D
A : Type u₃
inst✝¹ : Category.{v₃, u₃} A
J : GrothendieckTopology C
K : GrothendieckTopology D
L : GrothendieckTopology A
G : C ⥤ D
hG : CompatiblePreserving K G
ℱ : SheafOfTypes K
Z : C
T : Presieve Z
x : FamilyOfElements (G.op ⋙ ℱ.val) T
h : Compatible x
inst✝ : IsContinuous F J K
X✝ Y✝ Z✝ : Sheaf K A
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ ({ obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
            map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
        (f ≫ g)).val =
    ({ obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
              map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
          f ≫
        { obj := fun ℱ => { val := F.op ⋙ ℱ.val, cond := (_ : Presheaf.IsSheaf J (F.op ⋙ ℱ.val)) },
              map := fun {X Y} f => { val := ((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj F.op).map f.val } }.map
          g).val

Next tactic:
apply ((whiskeringLeft _ _ _).obj F.op).map_comp
/-- Matrices over dual numbers and dual numbers over matrices are isomorphic. -/
@[simps]
def Matrix.dualNumberEquiv : Matrix n n (DualNumber R) ≃ₐ[R] DualNumber (Matrix n n R) where
  toFun A := ⟨of fun i j => (A i j).fst, of fun i j => (A i j).snd⟩
  invFun d := of fun i j => (d.fst i j, d.snd i j)
  left_inv A := Matrix.ext fun i j => TrivSqZeroExt.ext rfl rfl
  right_inv d := TrivSqZeroExt.ext (Matrix.ext fun i j => rfl) (Matrix.ext fun i j => rfl)
  map_mul' A B := by
    

Proof state:
R n : Type
inst✝² : CommSemiring R
inst✝¹ : Fintype n
inst✝ : DecidableEq n
A B : Matrix n n (DualNumber R)
⊢ Equiv.toFun
      { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
        invFun := fun d => of fun i j => (fst d i j, snd d i j),
        left_inv :=
          (_ :
            ∀ (A : Matrix n n (DualNumber R)),
              (fun d => of fun i j => (fst d i j, snd d i j))
                  ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                A),
        right_inv :=
          (_ :
            ∀ (d : DualNumber (Matrix n n R)),
              (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                  ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                d) }
      (A * B) =
    Equiv.toFun
        { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
          invFun := fun d => of fun i j => (fst d i j, snd d i j),
          left_inv :=
            (_ :
              ∀ (A : Matrix n n (DualNumber R)),
                (fun d => of fun i j => (fst d i j, snd d i j))
                    ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                  A),
          right_inv :=
            (_ :
              ∀ (d : DualNumber (Matrix n n R)),
                (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                    ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                  d) }
        A *
      Equiv.toFun
        { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
          invFun := fun d => of fun i j => (fst d i j, snd d i j),
          left_inv :=
            (_ :
              ∀ (A : Matrix n n (DualNumber R)),
                (fun d => of fun i j => (fst d i j, snd d i j))
                    ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                  A),
          right_inv :=
            (_ :
              ∀ (d : DualNumber (Matrix n n R)),
                (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                    ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                  d) }
        B

Next tactic:
ext
/-- Matrices over dual numbers and dual numbers over matrices are isomorphic. -/
@[simps]
def Matrix.dualNumberEquiv : Matrix n n (DualNumber R) ≃ₐ[R] DualNumber (Matrix n n R) where
  toFun A := ⟨of fun i j => (A i j).fst, of fun i j => (A i j).snd⟩
  invFun d := of fun i j => (d.fst i j, d.snd i j)
  left_inv A := Matrix.ext fun i j => TrivSqZeroExt.ext rfl rfl
  right_inv d := TrivSqZeroExt.ext (Matrix.ext fun i j => rfl) (Matrix.ext fun i j => rfl)
  map_mul' A B := by
    ext
    · 

Proof state:
case h1.a.h
R n : Type
inst✝² : CommSemiring R
inst✝¹ : Fintype n
inst✝ : DecidableEq n
A B : Matrix n n (DualNumber R)
i✝ x✝ : n
⊢ fst
      (Equiv.toFun
        { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
          invFun := fun d => of fun i j => (fst d i j, snd d i j),
          left_inv :=
            (_ :
              ∀ (A : Matrix n n (DualNumber R)),
                (fun d => of fun i j => (fst d i j, snd d i j))
                    ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                  A),
          right_inv :=
            (_ :
              ∀ (d : DualNumber (Matrix n n R)),
                (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                    ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                  d) }
        (A * B))
      i✝ x✝ =
    fst
      (Equiv.toFun
          { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
            invFun := fun d => of fun i j => (fst d i j, snd d i j),
            left_inv :=
              (_ :
                ∀ (A : Matrix n n (DualNumber R)),
                  (fun d => of fun i j => (fst d i j, snd d i j))
                      ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                    A),
            right_inv :=
              (_ :
                ∀ (d : DualNumber (Matrix n n R)),
                  (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                      ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                    d) }
          A *
        Equiv.toFun
          { toFun := fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)),
            invFun := fun d => of fun i j => (fst d i j, snd d i j),
            left_inv :=
              (_ :
                ∀ (A : Matrix n n (DualNumber R)),
                  (fun d => of fun i j => (fst d i j, snd d i j))
                      ((fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j))) A) =
                    A),
            right_inv :=
              (_ :
                ∀ (d : DualNumber (Matrix n n R)),
                  (fun A => (of fun i j => fst (A i j), of fun i j => snd (A i j)))
                      ((fun d => of fun i j => (fst d i j, snd d i j)) d) =
                    d) }
          B)
      i✝ x✝

Next tactic:
dsimp [mul_apply]
/-- Matrices over dual numbers and dual numbers over matrices are isomorphic. -/
@[simps]
def Matrix.dualNumberEquiv : Matrix n n (DualNumber R) ≃ₐ[R] DualNumber (Matrix n n R) where
  toFun A := ⟨of fun i j => (A i j).fst, of fun i j => (A i j).snd⟩
  invFun d := of fun i j => (d.fst i j, d.snd i j)
  left_inv A := Matrix.ext fun i j => TrivSqZeroExt.ext rfl rfl
  right_inv d := TrivSqZeroExt.ext (Matrix.ext fun i j => rfl) (Matrix.ext fun i j => rfl)
  map_mul' A B := by
    ext
    · dsimp [mul_apply]
      

Proof state:
case h1.a.h
R n : Type
inst✝² : CommSemiring R
inst✝¹ : Fintype n
inst✝ : DecidableEq n
A B : Matrix n n (DualNumber R)
i✝ x✝ : n
⊢ fst (Finset.sum Finset.univ fun j => A i✝ j * B j x✝) = Finset.sum Finset.univ fun j => fst (A i✝ j) * fst (B j x✝)

Next tactic:
simp_rw [fst_sum]
/-- Matrices over dual numbers and dual numbers over matrices are isomorphic. -/
@[simps]
def Matrix.dualNumberEquiv : Matrix n n (DualNumber R) ≃ₐ[R] DualNumber (Matrix n n R) where
  toFun A := ⟨of fun i j => (A i j).fst, of fun i j => (A i j).snd⟩
  invFun d := of fun i j => (d.fst i j, d.snd i j)
  left_inv A := Matrix.ext fun i j => TrivSqZeroExt.ext rfl rfl
  right_inv d := TrivSqZeroExt.ext (Matrix.ext fun i j => rfl) (Matrix.ext fun i j => rfl)
  map_mul' A B := by
    ext
    · dsimp [mul_apply]
      simp_rw [fst_sum]
      

Proof state:
case h1.a.h
R n : Type
inst✝² : CommSemiring R
inst✝¹ : Fintype n
inst✝ : DecidableEq n
A B : Matrix n n (DualNumber R)
i✝ x✝ : n
⊢ (Finset.sum Finset.univ fun i => fst (A i✝ i * B i x✝)) = Finset.sum Finset.univ fun j => fst (A i✝ j) * fst (B j x✝)

Next tactic:
rfl