l3lab/ntp-mathlib huggingface dataset:
/-- The `stalkToFiber` map is injective at `x` if any two allowed sections which agree at `x`
agree on some neighborhood of `x`.
-/
theorem stalkToFiber_injective (P : LocalPredicate T) (x : X)
    (w :
      ∀ (U V : OpenNhds x) (fU : ∀ y : U.1, T y) (_ : P.pred fU) (fV : ∀ y : V.1, T y)
        (_ : P.pred fV) (_ : fU ⟨x, U.2⟩ = fV ⟨x, V.2⟩),
        ∃ (W : OpenNhds x) (iU : W ⟶ U) (iV : W ⟶ V), ∀ w : W.1,
          fU (iU w : U.1) = fV (iV w : V.1)) :
    Function.Injective (stalkToFiber P x) := fun tU tV h => by
  -- We promise to provide all the ingredients of the proof later:
  let Q :
    ∃ (W : (OpenNhds x)ᵒᵖ) (s : ∀ w : (unop W).1, T w) (hW : P.pred s),
      tU = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ ∧
        tV = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ :=
    ?_
  · choose W s hW e using Q
    exact e.1.trans e.2.symm
  -- Then use induction to pick particular representatives of `tU tV : stalk x`
  obtain ⟨U, ⟨fU, hU⟩, rfl⟩ := jointly_surjective'.{v, v} tU
  obtain ⟨V, ⟨fV, hV⟩, rfl⟩ := jointly_surjective'.{v, v} tV
  · -- Decompose everything into its constituent parts:
    dsimp
    simp only [stalkToFiber, Types.Colimit.ι_desc_apply'] at h
    specialize w (unop U) (unop V) fU hU fV hV h
    

Proof state:
case refine_1.intro.intro.mk.intro.intro.mk
X : TopCat
T : ↑X → Type v
P : LocalPredicate T
x : ↑X
U : (OpenNhds x)ᵒᵖ
fU : (x_1 : ↥((OpenNhds.inclusion x).op.obj U).unop) → T ↑x_1
hU : PrelocalPredicate.pred P.toPrelocalPredicate fU
V : (OpenNhds x)ᵒᵖ
fV : (x_1 : ↥((OpenNhds.inclusion x).op.obj V).unop) → T ↑x_1
hV : PrelocalPredicate.pred P.toPrelocalPredicate fV
h : fU { val := x, property := (_ : x ∈ U.unop.obj) } = fV { val := x, property := (_ : x ∈ V.unop.obj) }
w :
  ∃ W iU iV,
    ∀ (w : ↥W.obj),
      fU ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑U.unop.obj) }) w) =
        fV ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑V.unop.obj) }) w)
⊢ ∃ W s,
    ∃ (hW : PrelocalPredicate.pred P.toPrelocalPredicate s),
      colimit.ι ((OpenNhds.inclusion x).op ⋙ subpresheafToTypes P.toPrelocalPredicate) U { val := fU, property := hU } =
          Presheaf.germ (subpresheafToTypes P.toPrelocalPredicate) { val := x, property := (_ : x ∈ W.unop.obj) }
            { val := s, property := hW } ∧
        colimit.ι ((OpenNhds.inclusion x).op ⋙ subpresheafToTypes P.toPrelocalPredicate) V
            { val := fV, property := hV } =
          Presheaf.germ (subpresheafToTypes P.toPrelocalPredicate) { val := x, property := (_ : x ∈ W.unop.obj) }
            { val := s, property := hW }

Next tactic:
rcases w with ⟨W, iU, iV, w⟩
/-- The `stalkToFiber` map is injective at `x` if any two allowed sections which agree at `x`
agree on some neighborhood of `x`.
-/
theorem stalkToFiber_injective (P : LocalPredicate T) (x : X)
    (w :
      ∀ (U V : OpenNhds x) (fU : ∀ y : U.1, T y) (_ : P.pred fU) (fV : ∀ y : V.1, T y)
        (_ : P.pred fV) (_ : fU ⟨x, U.2⟩ = fV ⟨x, V.2⟩),
        ∃ (W : OpenNhds x) (iU : W ⟶ U) (iV : W ⟶ V), ∀ w : W.1,
          fU (iU w : U.1) = fV (iV w : V.1)) :
    Function.Injective (stalkToFiber P x) := fun tU tV h => by
  -- We promise to provide all the ingredients of the proof later:
  let Q :
    ∃ (W : (OpenNhds x)ᵒᵖ) (s : ∀ w : (unop W).1, T w) (hW : P.pred s),
      tU = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ ∧
        tV = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ :=
    ?_
  · choose W s hW e using Q
    exact e.1.trans e.2.symm
  -- Then use induction to pick particular representatives of `tU tV : stalk x`
  obtain ⟨U, ⟨fU, hU⟩, rfl⟩ := jointly_surjective'.{v, v} tU
  obtain ⟨V, ⟨fV, hV⟩, rfl⟩ := jointly_surjective'.{v, v} tV
  · -- Decompose everything into its constituent parts:
    dsimp
    simp only [stalkToFiber, Types.Colimit.ι_desc_apply'] at h
    specialize w (unop U) (unop V) fU hU fV hV h
    rcases w with ⟨W, iU, iV, w⟩
    -- and put it back together again in the correct order.
    

Proof state:
case refine_1.intro.intro.mk.intro.intro.mk.intro.intro.intro
X : TopCat
T : ↑X → Type v
P : LocalPredicate T
x : ↑X
U : (OpenNhds x)ᵒᵖ
fU : (x_1 : ↥((OpenNhds.inclusion x).op.obj U).unop) → T ↑x_1
hU : PrelocalPredicate.pred P.toPrelocalPredicate fU
V : (OpenNhds x)ᵒᵖ
fV : (x_1 : ↥((OpenNhds.inclusion x).op.obj V).unop) → T ↑x_1
hV : PrelocalPredicate.pred P.toPrelocalPredicate fV
h : fU { val := x, property := (_ : x ∈ U.unop.obj) } = fV { val := x, property := (_ : x ∈ V.unop.obj) }
W : OpenNhds x
iU : W ⟶ U.unop
iV : W ⟶ V.unop
w :
  ∀ (w : ↥W.obj),
    fU ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑U.unop.obj) }) w) =
      fV ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑V.unop.obj) }) w)
⊢ ∃ W s,
    ∃ (hW : PrelocalPredicate.pred P.toPrelocalPredicate s),
      colimit.ι ((OpenNhds.inclusion x).op ⋙ subpresheafToTypes P.toPrelocalPredicate) U { val := fU, property := hU } =
          Presheaf.germ (subpresheafToTypes P.toPrelocalPredicate) { val := x, property := (_ : x ∈ W.unop.obj) }
            { val := s, property := hW } ∧
        colimit.ι ((OpenNhds.inclusion x).op ⋙ subpresheafToTypes P.toPrelocalPredicate) V
            { val := fV, property := hV } =
          Presheaf.germ (subpresheafToTypes P.toPrelocalPredicate) { val := x, property := (_ : x ∈ W.unop.obj) }
            { val := s, property := hW }

Next tactic:
refine' ⟨op W, fun w => fU (iU w : (unop U).1), P.res _ _ hU, _⟩
/-- The `stalkToFiber` map is injective at `x` if any two allowed sections which agree at `x`
agree on some neighborhood of `x`.
-/
theorem stalkToFiber_injective (P : LocalPredicate T) (x : X)
    (w :
      ∀ (U V : OpenNhds x) (fU : ∀ y : U.1, T y) (_ : P.pred fU) (fV : ∀ y : V.1, T y)
        (_ : P.pred fV) (_ : fU ⟨x, U.2⟩ = fV ⟨x, V.2⟩),
        ∃ (W : OpenNhds x) (iU : W ⟶ U) (iV : W ⟶ V), ∀ w : W.1,
          fU (iU w : U.1) = fV (iV w : V.1)) :
    Function.Injective (stalkToFiber P x) := fun tU tV h => by
  -- We promise to provide all the ingredients of the proof later:
  let Q :
    ∃ (W : (OpenNhds x)ᵒᵖ) (s : ∀ w : (unop W).1, T w) (hW : P.pred s),
      tU = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ ∧
        tV = (subsheafToTypes P).presheaf.germ ⟨x, (unop W).2⟩ ⟨s, hW⟩ :=
    ?_
  · choose W s hW e using Q
    exact e.1.trans e.2.symm
  -- Then use induction to pick particular representatives of `tU tV : stalk x`
  obtain ⟨U, ⟨fU, hU⟩, rfl⟩ := jointly_surjective'.{v, v} tU
  obtain ⟨V, ⟨fV, hV⟩, rfl⟩ := jointly_surjective'.{v, v} tV
  · -- Decompose everything into its constituent parts:
    dsimp
    simp only [stalkToFiber, Types.Colimit.ι_desc_apply'] at h
    specialize w (unop U) (unop V) fU hU fV hV h
    rcases w with ⟨W, iU, iV, w⟩
    -- and put it back together again in the correct order.
    refine' ⟨op W, fun w => fU (iU w : (unop U).1), P.res _ _ hU, _⟩
    rcases W with ⟨W, m⟩
    · 

Proof state:
case refine_1.intro.intro.mk.intro.intro.mk.intro.intro.intro.refine'_1.mk
X : TopCat
T : ↑X → Type v
P : LocalPredicate T
x : ↑X
U : (OpenNhds x)ᵒᵖ
fU : (x_1 : ↥((OpenNhds.inclusion x).op.obj U).unop) → T ↑x_1
hU : PrelocalPredicate.pred P.toPrelocalPredicate fU
V : (OpenNhds x)ᵒᵖ
fV : (x_1 : ↥((OpenNhds.inclusion x).op.obj V).unop) → T ↑x_1
hV : PrelocalPredicate.pred P.toPrelocalPredicate fV
h : fU { val := x, property := (_ : x ∈ U.unop.obj) } = fV { val := x, property := (_ : x ∈ V.unop.obj) }
W : Opens ↑X
m : x ∈ W
iU : { obj := W, property := m } ⟶ U.unop
iV : { obj := W, property := m } ⟶ V.unop
w :
  ∀ (w : ↥{ obj := W, property := m }.obj),
    fU ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑U.unop.obj) }) w) =
      fV ((fun x_1 => { val := ↑x_1, property := (_ : ↑x_1 ∈ ↑V.unop.obj) }) w)
⊢ (op { obj := W, property := m }).unop.obj ⟶ U.unop.obj

Next tactic:
exact iU
/-- Some repackaging:
the presheaf of functions satisfying `continuousPrelocal` is just the same thing as
the presheaf of continuous functions.
-/
def subpresheafContinuousPrelocalIsoPresheafToTop (T : TopCat.{v}) :
    subpresheafToTypes (continuousPrelocal X T) ≅ presheafToTop X T :=
  NatIso.ofComponents fun X =>
    { hom := by 

Proof state:
X✝ : TopCat
T✝ : ↑X✝ → Type v
T : TopCat
X : (Opens ↑X✝)ᵒᵖ
⊢ (subpresheafToTypes (continuousPrelocal X✝ T)).obj X ⟶ (presheafToTop X✝ T).obj X

Next tactic:
rintro ⟨f, c⟩
/-- Some repackaging:
the presheaf of functions satisfying `continuousPrelocal` is just the same thing as
the presheaf of continuous functions.
-/
def subpresheafContinuousPrelocalIsoPresheafToTop (T : TopCat.{v}) :
    subpresheafToTypes (continuousPrelocal X T) ≅ presheafToTop X T :=
  NatIso.ofComponents fun X =>
    { hom := by rintro ⟨f, c⟩; 

Proof state:
case mk
X✝ : TopCat
T✝ : ↑X✝ → Type v
T : TopCat
X : (Opens ↑X✝)ᵒᵖ
f : (x : ↥X.unop) → (fun x => ↑T) ↑x
c : PrelocalPredicate.pred (continuousPrelocal X✝ T) f
⊢ (presheafToTop X✝ T).obj X

Next tactic:
exact ⟨f, c⟩
/-- Some repackaging:
the presheaf of functions satisfying `continuousPrelocal` is just the same thing as
the presheaf of continuous functions.
-/
def subpresheafContinuousPrelocalIsoPresheafToTop (T : TopCat.{v}) :
    subpresheafToTypes (continuousPrelocal X T) ≅ presheafToTop X T :=
  NatIso.ofComponents fun X =>
    { hom := by rintro ⟨f, c⟩; exact ⟨f, c⟩
      inv := by 

Proof state:
X✝ : TopCat
T✝ : ↑X✝ → Type v
T : TopCat
X : (Opens ↑X✝)ᵒᵖ
⊢ (presheafToTop X✝ T).obj X ⟶ (subpresheafToTypes (continuousPrelocal X✝ T)).obj X

Next tactic:
rintro ⟨f, c⟩
/-- Some repackaging:
the presheaf of functions satisfying `continuousPrelocal` is just the same thing as
the presheaf of continuous functions.
-/
def subpresheafContinuousPrelocalIsoPresheafToTop (T : TopCat.{v}) :
    subpresheafToTypes (continuousPrelocal X T) ≅ presheafToTop X T :=
  NatIso.ofComponents fun X =>
    { hom := by rintro ⟨f, c⟩; exact ⟨f, c⟩
      inv := by rintro ⟨f, c⟩; 

Proof state:
case mk
X✝ : TopCat
T✝ : ↑X✝ → Type v
T : TopCat
X : (Opens ↑X✝)ᵒᵖ
f : ↑((Opens.toTopCat X✝).op.obj X).unop → ↑T
c : Continuous f
⊢ (subpresheafToTypes (continuousPrelocal X✝ T)).obj X

Next tactic:
exact ⟨f, c⟩
/-- Diagonal matrices are generated by the `Matrix.diagonal` of their `Matrix.diag`. -/
theorem IsDiag.diagonal_diag [Zero α] [DecidableEq n] {A : Matrix n n α} (h : A.IsDiag) :
    diagonal (diag A) = A :=
  ext fun i j => by
    

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Zero α
inst✝ : DecidableEq n
A : Matrix n n α
h : IsDiag A
i j : n
⊢ diagonal (diag A) i j = A i j

Next tactic:
obtain rfl | hij := Decidable.eq_or_ne i j
/-- Diagonal matrices are generated by the `Matrix.diagonal` of their `Matrix.diag`. -/
theorem IsDiag.diagonal_diag [Zero α] [DecidableEq n] {A : Matrix n n α} (h : A.IsDiag) :
    diagonal (diag A) = A :=
  ext fun i j => by
    obtain rfl | hij := Decidable.eq_or_ne i j
    · 

Proof state:
case inl
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Zero α
inst✝ : DecidableEq n
A : Matrix n n α
h : IsDiag A
i : n
⊢ diagonal (diag A) i i = A i i

Next tactic:
rw [diagonal_apply_eq, diag]
/-- Diagonal matrices are generated by the `Matrix.diagonal` of their `Matrix.diag`. -/
theorem IsDiag.diagonal_diag [Zero α] [DecidableEq n] {A : Matrix n n α} (h : A.IsDiag) :
    diagonal (diag A) = A :=
  ext fun i j => by
    obtain rfl | hij := Decidable.eq_or_ne i j
    · rw [diagonal_apply_eq, diag]
    · 

Proof state:
case inr
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Zero α
inst✝ : DecidableEq n
A : Matrix n n α
h : IsDiag A
i j : n
hij : i ≠ j
⊢ diagonal (diag A) i j = A i j

Next tactic:
rw [diagonal_apply_ne _ hij, h hij]
theorem IsDiag.map [Zero α] [Zero β] {A : Matrix n n α} (ha : A.IsDiag) {f : α → β} (hf : f 0 = 0) :
    (A.map f).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Zero α
inst✝ : Zero β
A : Matrix n n α
ha : IsDiag A
f : α → β
hf : f 0 = 0
⊢ IsDiag (Matrix.map A f)

Next tactic:
intro i j h
theorem IsDiag.map [Zero α] [Zero β] {A : Matrix n n α} (ha : A.IsDiag) {f : α → β} (hf : f 0 = 0) :
    (A.map f).IsDiag := by
  intro i j h
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Zero α
inst✝ : Zero β
A : Matrix n n α
ha : IsDiag A
f : α → β
hf : f 0 = 0
i j : n
h : i ≠ j
⊢ Matrix.map A f i j = 0

Next tactic:
simp [ha h, hf]
theorem IsDiag.neg [AddGroup α] {A : Matrix n n α} (ha : A.IsDiag) : (-A).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddGroup α
A : Matrix n n α
ha : IsDiag A
⊢ IsDiag (-A)

Next tactic:
intro i j h
theorem IsDiag.neg [AddGroup α] {A : Matrix n n α} (ha : A.IsDiag) : (-A).IsDiag := by
  intro i j h
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddGroup α
A : Matrix n n α
ha : IsDiag A
i j : n
h : i ≠ j
⊢ (-A) i j = 0

Next tactic:
simp [ha h]
theorem IsDiag.add [AddZeroClass α] {A B : Matrix n n α} (ha : A.IsDiag) (hb : B.IsDiag) :
    (A + B).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddZeroClass α
A B : Matrix n n α
ha : IsDiag A
hb : IsDiag B
⊢ IsDiag (A + B)

Next tactic:
intro i j h
theorem IsDiag.add [AddZeroClass α] {A B : Matrix n n α} (ha : A.IsDiag) (hb : B.IsDiag) :
    (A + B).IsDiag := by
  intro i j h
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddZeroClass α
A B : Matrix n n α
ha : IsDiag A
hb : IsDiag B
i j : n
h : i ≠ j
⊢ (A + B) i j = 0

Next tactic:
simp [ha h, hb h]
theorem IsDiag.sub [AddGroup α] {A B : Matrix n n α} (ha : A.IsDiag) (hb : B.IsDiag) :
    (A - B).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddGroup α
A B : Matrix n n α
ha : IsDiag A
hb : IsDiag B
⊢ IsDiag (A - B)

Next tactic:
intro i j h
theorem IsDiag.sub [AddGroup α] {A B : Matrix n n α} (ha : A.IsDiag) (hb : B.IsDiag) :
    (A - B).IsDiag := by
  intro i j h
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : AddGroup α
A B : Matrix n n α
ha : IsDiag A
hb : IsDiag B
i j : n
h : i ≠ j
⊢ (A - B) i j = 0

Next tactic:
simp [ha h, hb h]
theorem IsDiag.smul [Monoid R] [AddMonoid α] [DistribMulAction R α] (k : R) {A : Matrix n n α}
    (ha : A.IsDiag) : (k • A).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝² : Monoid R
inst✝¹ : AddMonoid α
inst✝ : DistribMulAction R α
k : R
A : Matrix n n α
ha : IsDiag A
⊢ IsDiag (k • A)

Next tactic:
intro i j h
theorem IsDiag.smul [Monoid R] [AddMonoid α] [DistribMulAction R α] (k : R) {A : Matrix n n α}
    (ha : A.IsDiag) : (k • A).IsDiag := by
  intro i j h
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝² : Monoid R
inst✝¹ : AddMonoid α
inst✝ : DistribMulAction R α
k : R
A : Matrix n n α
ha : IsDiag A
i j : n
h : i ≠ j
⊢ (k • A) i j = 0

Next tactic:
simp [ha h]
@[simp]
theorem isDiag_conjTranspose_iff [Semiring α] [StarRing α] {A : Matrix n n α} :
    Aᴴ.IsDiag ↔ A.IsDiag :=
  ⟨fun ha => by
    

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Semiring α
inst✝ : StarRing α
A : Matrix n n α
ha : IsDiag Aᴴ
⊢ IsDiag A

Next tactic:
convert ha.conjTranspose
@[simp]
theorem isDiag_conjTranspose_iff [Semiring α] [StarRing α] {A : Matrix n n α} :
    Aᴴ.IsDiag ↔ A.IsDiag :=
  ⟨fun ha => by
    convert ha.conjTranspose
    

Proof state:
case h.e'_4
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝¹ : Semiring α
inst✝ : StarRing α
A : Matrix n n α
ha : IsDiag Aᴴ
⊢ A = Aᴴᴴ

Next tactic:
simp
/-- `(A ⊗ B).IsDiag` if both `A` and `B` are diagonal. -/
theorem IsDiag.kronecker [MulZeroClass α] {A : Matrix m m α} {B : Matrix n n α} (hA : A.IsDiag)
    (hB : B.IsDiag) : (A ⊗ₖ B).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : MulZeroClass α
A : Matrix m m α
B : Matrix n n α
hA : IsDiag A
hB : IsDiag B
⊢ IsDiag (kroneckerMap (fun x x_1 => x * x_1) A B)

Next tactic:
rintro ⟨a, b⟩ ⟨c, d⟩ h
/-- `(A ⊗ B).IsDiag` if both `A` and `B` are diagonal. -/
theorem IsDiag.kronecker [MulZeroClass α] {A : Matrix m m α} {B : Matrix n n α} (hA : A.IsDiag)
    (hB : B.IsDiag) : (A ⊗ₖ B).IsDiag := by
  rintro ⟨a, b⟩ ⟨c, d⟩ h
  

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : MulZeroClass α
A : Matrix m m α
B : Matrix n n α
hA : IsDiag A
hB : IsDiag B
a : m
b : n
c : m
d : n
h : (a, b) ≠ (c, d)
⊢ kroneckerMap (fun x x_1 => x * x_1) A B (a, b) (c, d) = 0

Next tactic:
simp only [Prod.mk.inj_iff, Ne.def, not_and_or] at h
/-- `(A ⊗ B).IsDiag` if both `A` and `B` are diagonal. -/
theorem IsDiag.kronecker [MulZeroClass α] {A : Matrix m m α} {B : Matrix n n α} (hA : A.IsDiag)
    (hB : B.IsDiag) : (A ⊗ₖ B).IsDiag := by
  rintro ⟨a, b⟩ ⟨c, d⟩ h
  simp only [Prod.mk.inj_iff, Ne.def, not_and_or] at h
  

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : MulZeroClass α
A : Matrix m m α
B : Matrix n n α
hA : IsDiag A
hB : IsDiag B
a : m
b : n
c : m
d : n
h : ¬a = c ∨ ¬b = d
⊢ kroneckerMap (fun x x_1 => x * x_1) A B (a, b) (c, d) = 0

Next tactic:
cases' h with hac hbd
/-- `(A ⊗ B).IsDiag` if both `A` and `B` are diagonal. -/
theorem IsDiag.kronecker [MulZeroClass α] {A : Matrix m m α} {B : Matrix n n α} (hA : A.IsDiag)
    (hB : B.IsDiag) : (A ⊗ₖ B).IsDiag := by
  rintro ⟨a, b⟩ ⟨c, d⟩ h
  simp only [Prod.mk.inj_iff, Ne.def, not_and_or] at h
  cases' h with hac hbd
  · 

Proof state:
case mk.mk.inl
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : MulZeroClass α
A : Matrix m m α
B : Matrix n n α
hA : IsDiag A
hB : IsDiag B
a : m
b : n
c : m
d : n
hac : ¬a = c
⊢ kroneckerMap (fun x x_1 => x * x_1) A B (a, b) (c, d) = 0

Next tactic:
simp [hA hac]
/-- `(A ⊗ B).IsDiag` if both `A` and `B` are diagonal. -/
theorem IsDiag.kronecker [MulZeroClass α] {A : Matrix m m α} {B : Matrix n n α} (hA : A.IsDiag)
    (hB : B.IsDiag) : (A ⊗ₖ B).IsDiag := by
  rintro ⟨a, b⟩ ⟨c, d⟩ h
  simp only [Prod.mk.inj_iff, Ne.def, not_and_or] at h
  cases' h with hac hbd
  · simp [hA hac]
  · 

Proof state:
case mk.mk.inr
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : MulZeroClass α
A : Matrix m m α
B : Matrix n n α
hA : IsDiag A
hB : IsDiag B
a : m
b : n
c : m
d : n
hbd : ¬b = d
⊢ kroneckerMap (fun x x_1 => x * x_1) A B (a, b) (c, d) = 0

Next tactic:
simp [hB hbd]
theorem IsDiag.isSymm [Zero α] {A : Matrix n n α} (h : A.IsDiag) : A.IsSymm := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix n n α
h : IsDiag A
⊢ IsSymm A

Next tactic:
ext i j
theorem IsDiag.isSymm [Zero α] {A : Matrix n n α} (h : A.IsDiag) : A.IsSymm := by
  ext i j
  

Proof state:
case a.h
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix n n α
h : IsDiag A
i j : n
⊢ Aᵀ i j = A i j

Next tactic:
by_cases g : i = j
theorem IsDiag.isSymm [Zero α] {A : Matrix n n α} (h : A.IsDiag) : A.IsSymm := by
  ext i j
  by_cases g : i = j; · 

Proof state:
case pos
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix n n α
h : IsDiag A
i j : n
g : i = j
⊢ Aᵀ i j = A i j

Next tactic:
rw [g, transpose_apply]
theorem IsDiag.isSymm [Zero α] {A : Matrix n n α} (h : A.IsDiag) : A.IsSymm := by
  ext i j
  by_cases g : i = j; · rw [g, transpose_apply]
  

Proof state:
case neg
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix n n α
h : IsDiag A
i j : n
g : ¬i = j
⊢ Aᵀ i j = A i j

Next tactic:
simp [h g, h (Ne.symm g)]
/-- The block matrix `A.fromBlocks 0 0 D` is diagonal if `A` and `D` are diagonal. -/
theorem IsDiag.fromBlocks [Zero α] {A : Matrix m m α} {D : Matrix n n α} (ha : A.IsDiag)
    (hd : D.IsDiag) : (A.fromBlocks 0 0 D).IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
D : Matrix n n α
ha : IsDiag A
hd : IsDiag D
⊢ IsDiag (Matrix.fromBlocks A 0 0 D)

Next tactic:
rintro (i | i) (j | j) hij
/-- The block matrix `A.fromBlocks 0 0 D` is diagonal if `A` and `D` are diagonal. -/
theorem IsDiag.fromBlocks [Zero α] {A : Matrix m m α} {D : Matrix n n α} (ha : A.IsDiag)
    (hd : D.IsDiag) : (A.fromBlocks 0 0 D).IsDiag := by
  rintro (i | i) (j | j) hij
  · 

Proof state:
case inl.inl
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
D : Matrix n n α
ha : IsDiag A
hd : IsDiag D
i j : m
hij : Sum.inl i ≠ Sum.inl j
⊢ Matrix.fromBlocks A 0 0 D (Sum.inl i) (Sum.inl j) = 0

Next tactic:
exact ha (ne_of_apply_ne _ hij)
/-- The block matrix `A.fromBlocks 0 0 D` is diagonal if `A` and `D` are diagonal. -/
theorem IsDiag.fromBlocks [Zero α] {A : Matrix m m α} {D : Matrix n n α} (ha : A.IsDiag)
    (hd : D.IsDiag) : (A.fromBlocks 0 0 D).IsDiag := by
  rintro (i | i) (j | j) hij
  · exact ha (ne_of_apply_ne _ hij)
  · 

Proof state:
case inl.inr
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
D : Matrix n n α
ha : IsDiag A
hd : IsDiag D
i : m
j : n
hij : Sum.inl i ≠ Sum.inr j
⊢ Matrix.fromBlocks A 0 0 D (Sum.inl i) (Sum.inr j) = 0

Next tactic:
rfl
/-- The block matrix `A.fromBlocks 0 0 D` is diagonal if `A` and `D` are diagonal. -/
theorem IsDiag.fromBlocks [Zero α] {A : Matrix m m α} {D : Matrix n n α} (ha : A.IsDiag)
    (hd : D.IsDiag) : (A.fromBlocks 0 0 D).IsDiag := by
  rintro (i | i) (j | j) hij
  · exact ha (ne_of_apply_ne _ hij)
  · rfl
  · 

Proof state:
case inr.inl
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
D : Matrix n n α
ha : IsDiag A
hd : IsDiag D
i : n
j : m
hij : Sum.inr i ≠ Sum.inl j
⊢ Matrix.fromBlocks A 0 0 D (Sum.inr i) (Sum.inl j) = 0

Next tactic:
rfl
/-- The block matrix `A.fromBlocks 0 0 D` is diagonal if `A` and `D` are diagonal. -/
theorem IsDiag.fromBlocks [Zero α] {A : Matrix m m α} {D : Matrix n n α} (ha : A.IsDiag)
    (hd : D.IsDiag) : (A.fromBlocks 0 0 D).IsDiag := by
  rintro (i | i) (j | j) hij
  · exact ha (ne_of_apply_ne _ hij)
  · rfl
  · rfl
  · 

Proof state:
case inr.inr
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
D : Matrix n n α
ha : IsDiag A
hd : IsDiag D
i j : n
hij : Sum.inr i ≠ Sum.inr j
⊢ Matrix.fromBlocks A 0 0 D (Sum.inr i) (Sum.inr j) = 0

Next tactic:
exact hd (ne_of_apply_ne _ hij)
/-- This is the `iff` version of `Matrix.IsDiag.fromBlocks`. -/
theorem isDiag_fromBlocks_iff [Zero α] {A : Matrix m m α} {B : Matrix m n α} {C : Matrix n m α}
    {D : Matrix n n α} : (A.fromBlocks B C D).IsDiag ↔ A.IsDiag ∧ B = 0 ∧ C = 0 ∧ D.IsDiag := by
  

Proof state:
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
B : Matrix m n α
C : Matrix n m α
D : Matrix n n α
⊢ IsDiag (fromBlocks A B C D) ↔ IsDiag A ∧ B = 0 ∧ C = 0 ∧ IsDiag D

Next tactic:
constructor
/-- This is the `iff` version of `Matrix.IsDiag.fromBlocks`. -/
theorem isDiag_fromBlocks_iff [Zero α] {A : Matrix m m α} {B : Matrix m n α} {C : Matrix n m α}
    {D : Matrix n n α} : (A.fromBlocks B C D).IsDiag ↔ A.IsDiag ∧ B = 0 ∧ C = 0 ∧ D.IsDiag := by
  constructor
  · 

Proof state:
case mp
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
B : Matrix m n α
C : Matrix n m α
D : Matrix n n α
⊢ IsDiag (fromBlocks A B C D) → IsDiag A ∧ B = 0 ∧ C = 0 ∧ IsDiag D

Next tactic:
intro h
/-- This is the `iff` version of `Matrix.IsDiag.fromBlocks`. -/
theorem isDiag_fromBlocks_iff [Zero α] {A : Matrix m m α} {B : Matrix m n α} {C : Matrix n m α}
    {D : Matrix n n α} : (A.fromBlocks B C D).IsDiag ↔ A.IsDiag ∧ B = 0 ∧ C = 0 ∧ D.IsDiag := by
  constructor
  · intro h
    

Proof state:
case mp
α : Type u_1
β : Type u_2
R : Type u_3
n : Type u_4
m : Type u_5
inst✝ : Zero α
A : Matrix m m α
B : Matrix m n α
C : Matrix n m α
D : Matrix n n α
h : IsDiag (fromBlocks A B C D)
⊢ IsDiag A ∧ B = 0 ∧ C = 0 ∧ IsDiag D

Next tactic:
refine' ⟨fun i j hij => _, ext fun i j => _, ext fun i j => _, fun i j hij => _⟩