/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module data.matrix.dmatrix
! leanprover-community/mathlib commit cc70d9141824ea8982d1562ce009952f2c3ece30
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Group.Pi
import Mathbin.Data.Fintype.Basic

/-!
# Matrices

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
-/

universe u u' v w z

#print DMatrix /-
/-- `dmatrix m n` is the type of dependently typed matrices
whose rows are indexed by the fintype `m` and
whose columns are indexed by the fintype `n`. -/
@[nolint unused_arguments]
def DMatrix (m : Type u) (n : Type u') [Fintype m] [Fintype n] (α : m → n → Type v) :
    Type max u u' v :=
  ∀ i j, α i j
#align dmatrix DMatrix
-/

variable {l m n o : Type _} [Fintype l] [Fintype m] [Fintype n] [Fintype o]

variable {α : m → n → Type v}

namespace DMatrix

section Ext

variable {M N : DMatrix m n α}

/- warning: dmatrix.ext_iff -> DMatrix.ext_iff is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} {M : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α} {N : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α}, Iff (forall (i : m) (j : n), Eq.{succ u1} (α i j) (M i j) (N i j)) (Eq.{succ (max u2 u3 u1)} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) M N)
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} {M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α} {N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α}, Iff (forall (i : m) (j : n), Eq.{succ u3} (α i j) (M i j) (N i j)) (Eq.{max (max (succ u3) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) M N)
Case conversion may be inaccurate. Consider using '#align dmatrix.ext_iff DMatrix.ext_iffₓ'. -/
theorem ext_iff : (∀ i j, M i j = N i j) ↔ M = N :=
  ⟨fun h => funext fun i => funext <| h i, fun h => by simp [h]⟩
#align dmatrix.ext_iff DMatrix.ext_iff

/- warning: dmatrix.ext -> DMatrix.ext is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} {M : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α} {N : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α}, (forall (i : m) (j : n), Eq.{succ u1} (α i j) (M i j) (N i j)) -> (Eq.{succ (max u2 u3 u1)} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) M N)
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} {M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α} {N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α}, (forall (i : m) (j : n), Eq.{succ u3} (α i j) (M i j) (N i j)) -> (Eq.{max (max (succ u3) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) M N)
Case conversion may be inaccurate. Consider using '#align dmatrix.ext DMatrix.extₓ'. -/
@[ext]
theorem ext : (∀ i j, M i j = N i j) → M = N :=
  ext_iff.mp
#align dmatrix.ext DMatrix.ext

end Ext

#print DMatrix.map /-
/-- `M.map f` is the dmatrix obtained by applying `f` to each entry of the matrix `M`. -/
def map (M : DMatrix m n α) {β : m → n → Type w} (f : ∀ ⦃i j⦄, α i j → β i j) : DMatrix m n β :=
  fun i j => f (M i j)
#align dmatrix.map DMatrix.map
-/

/- warning: dmatrix.map_apply -> DMatrix.map_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u3}} {n : Type.{u4}} [_inst_2 : Fintype.{u3} m] [_inst_3 : Fintype.{u4} n] {α : m -> n -> Type.{u1}} {M : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α} {β : m -> n -> Type.{u2}} {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)} {i : m} {j : n}, Eq.{succ u2} (β i j) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) f i j) (f i j (M i j))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} {M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α} {β : m -> n -> Type.{u4}} {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)} {i : m} {j : n}, Eq.{succ u4} (β i j) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) f i j) (f i j (M i j))
Case conversion may be inaccurate. Consider using '#align dmatrix.map_apply DMatrix.map_applyₓ'. -/
@[simp]
theorem map_apply {M : DMatrix m n α} {β : m → n → Type w} {f : ∀ ⦃i j⦄, α i j → β i j} {i : m}
    {j : n} : M.map f i j = f (M i j) :=
  rfl
#align dmatrix.map_apply DMatrix.map_apply

/- warning: dmatrix.map_map -> DMatrix.map_map is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u4}} {n : Type.{u5}} [_inst_2 : Fintype.{u4} m] [_inst_3 : Fintype.{u5} n] {α : m -> n -> Type.{u1}} {M : DMatrix.{u4, u5, u1} m n _inst_2 _inst_3 α} {β : m -> n -> Type.{u2}} {γ : m -> n -> Type.{u3}} {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)} {g : forall {{i : m}} {{j : n}}, (β i j) -> (γ i j)}, Eq.{succ (max u4 u5 u3)} (DMatrix.{u4, u5, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => γ i j)) (DMatrix.map.{u2, u3, u4, u5} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (DMatrix.map.{u1, u2, u4, u5} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) f) (fun (i : m) (j : n) => γ i j) g) (DMatrix.map.{u1, u3, u4, u5} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => γ i j) (fun (i : m) (j : n) (x : α i j) => g i j (f i j x)))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} {M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α} {β : m -> n -> Type.{u4}} {γ : m -> n -> Type.{u5}} {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)} {g : forall {{i : m}} {{j : n}}, (β i j) -> (γ i j)}, Eq.{max (max (succ u5) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u5} m n _inst_2 _inst_3 (fun (i : m) (j : n) => γ i j)) (DMatrix.map.{u4, u5, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) f) (fun (i : m) (j : n) => γ i j) g) (DMatrix.map.{u3, u5, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => γ i j) (fun (i : m) (j : n) (x : α i j) => g i j (f i j x)))
Case conversion may be inaccurate. Consider using '#align dmatrix.map_map DMatrix.map_mapₓ'. -/
@[simp]
theorem map_map {M : DMatrix m n α} {β : m → n → Type w} {γ : m → n → Type z}
    {f : ∀ ⦃i j⦄, α i j → β i j} {g : ∀ ⦃i j⦄, β i j → γ i j} :
    (M.map f).map g = M.map fun i j x => g (f x) :=
  by
  ext
  simp
#align dmatrix.map_map DMatrix.map_map

#print DMatrix.transpose /-
/-- The transpose of a dmatrix. -/
def transpose (M : DMatrix m n α) : DMatrix n m fun j i => α i j
  | x, y => M y x
#align dmatrix.transpose DMatrix.transpose
-/

-- mathport name: dmatrix.transpose
scoped postfix:1024 "ᵀ" => DMatrix.transpose

#print DMatrix.col /-
/-- `dmatrix.col u` is the column matrix whose entries are given by `u`. -/
def col {α : m → Type v} (w : ∀ i, α i) : DMatrix m Unit fun i j => α i
  | x, y => w x
#align dmatrix.col DMatrix.col
-/

#print DMatrix.row /-
/-- `dmatrix.row u` is the row matrix whose entries are given by `u`. -/
def row {α : n → Type v} (v : ∀ j, α j) : DMatrix Unit n fun i j => α j
  | x, y => v y
#align dmatrix.row DMatrix.row
-/

instance [∀ i j, Inhabited (α i j)] : Inhabited (DMatrix m n α) :=
  Pi.inhabited _

instance [∀ i j, Add (α i j)] : Add (DMatrix m n α) :=
  Pi.instAdd

instance [∀ i j, AddSemigroup (α i j)] : AddSemigroup (DMatrix m n α) :=
  Pi.addSemigroup

instance [∀ i j, AddCommSemigroup (α i j)] : AddCommSemigroup (DMatrix m n α) :=
  Pi.addCommSemigroup

instance [∀ i j, Zero (α i j)] : Zero (DMatrix m n α) :=
  Pi.instZero

instance [∀ i j, AddMonoid (α i j)] : AddMonoid (DMatrix m n α) :=
  Pi.addMonoid

instance [∀ i j, AddCommMonoid (α i j)] : AddCommMonoid (DMatrix m n α) :=
  Pi.addCommMonoid

instance [∀ i j, Neg (α i j)] : Neg (DMatrix m n α) :=
  Pi.instNeg

instance [∀ i j, Sub (α i j)] : Sub (DMatrix m n α) :=
  Pi.instSub

instance [∀ i j, AddGroup (α i j)] : AddGroup (DMatrix m n α) :=
  Pi.addGroup

instance [∀ i j, AddCommGroup (α i j)] : AddCommGroup (DMatrix m n α) :=
  Pi.addCommGroup

instance [∀ i j, Unique (α i j)] : Unique (DMatrix m n α) :=
  Pi.unique

instance [∀ i j, Subsingleton (α i j)] : Subsingleton (DMatrix m n α) :=
  Pi.subsingleton

/- warning: dmatrix.zero_apply -> DMatrix.zero_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), Zero.{u1} (α i j)] (i : m) (j : n), Eq.{succ u1} (α i j) (Zero.zero.{max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.hasZero.{u1, u2, u3} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j)) i j) (OfNat.ofNat.{u1} (α i j) 0 (OfNat.mk.{u1} (α i j) 0 (Zero.zero.{u1} (α i j) (_inst_5 i j))))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), Zero.{u3} (α i j)] (i : m) (j : n), Eq.{succ u3} (α i j) (OfNat.ofNat.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) 0 (Zero.toOfNat0.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instZeroDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))) i j) (OfNat.ofNat.{u3} (α i j) 0 (Zero.toOfNat0.{u3} (α i j) (_inst_5 i j)))
Case conversion may be inaccurate. Consider using '#align dmatrix.zero_apply DMatrix.zero_applyₓ'. -/
@[simp]
theorem zero_apply [∀ i j, Zero (α i j)] (i j) : (0 : DMatrix m n α) i j = 0 :=
  rfl
#align dmatrix.zero_apply DMatrix.zero_apply

/- warning: dmatrix.neg_apply -> DMatrix.neg_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), Neg.{u1} (α i j)] (M : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u1} (α i j) (Neg.neg.{max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.hasNeg.{u1, u2, u3} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j)) M i j) (Neg.neg.{u1} (α i j) (_inst_5 i j) (M i j))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), Neg.{u3} (α i j)] (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u3} (α i j) (Neg.neg.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instNegDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j)) M i j) (Neg.neg.{u3} (α i j) (_inst_5 i j) (M i j))
Case conversion may be inaccurate. Consider using '#align dmatrix.neg_apply DMatrix.neg_applyₓ'. -/
@[simp]
theorem neg_apply [∀ i j, Neg (α i j)] (M : DMatrix m n α) (i j) : (-M) i j = -M i j :=
  rfl
#align dmatrix.neg_apply DMatrix.neg_apply

/- warning: dmatrix.add_apply -> DMatrix.add_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), Add.{u1} (α i j)] (M : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u1} (α i j) (HAdd.hAdd.{max u2 u3 u1, max u2 u3 u1, max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (instHAdd.{max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.hasAdd.{u1, u2, u3} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))) M N i j) (HAdd.hAdd.{u1, u1, u1} (α i j) (α i j) (α i j) (instHAdd.{u1} (α i j) (_inst_5 i j)) (M i j) (N i j))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), Add.{u3} (α i j)] (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u3} (α i j) (HAdd.hAdd.{max (max u3 u2) u1, max (max u3 u2) u1, max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (instHAdd.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instAddDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))) M N i j) (HAdd.hAdd.{u3, u3, u3} (α i j) (α i j) (α i j) (instHAdd.{u3} (α i j) (_inst_5 i j)) (M i j) (N i j))
Case conversion may be inaccurate. Consider using '#align dmatrix.add_apply DMatrix.add_applyₓ'. -/
@[simp]
theorem add_apply [∀ i j, Add (α i j)] (M N : DMatrix m n α) (i j) : (M + N) i j = M i j + N i j :=
  rfl
#align dmatrix.add_apply DMatrix.add_apply

/- warning: dmatrix.sub_apply -> DMatrix.sub_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u3} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), Sub.{u1} (α i j)] (M : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u1} (α i j) (HSub.hSub.{max u2 u3 u1, max u2 u3 u1, max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (instHSub.{max u2 u3 u1} (DMatrix.{u2, u3, u1} m n _inst_2 _inst_3 α) (DMatrix.hasSub.{u1, u2, u3} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))) M N i j) (HSub.hSub.{u1, u1, u1} (α i j) (α i j) (α i j) (instHSub.{u1} (α i j) (_inst_5 i j)) (M i j) (N i j))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), Sub.{u3} (α i j)] (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (i : m) (j : n), Eq.{succ u3} (α i j) (HSub.hSub.{max (max u3 u2) u1, max (max u3 u2) u1, max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (instHSub.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instSubDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))) M N i j) (HSub.hSub.{u3, u3, u3} (α i j) (α i j) (α i j) (instHSub.{u3} (α i j) (_inst_5 i j)) (M i j) (N i j))
Case conversion may be inaccurate. Consider using '#align dmatrix.sub_apply DMatrix.sub_applyₓ'. -/
@[simp]
theorem sub_apply [∀ i j, Sub (α i j)] (M N : DMatrix m n α) (i j) : (M - N) i j = M i j - N i j :=
  rfl
#align dmatrix.sub_apply DMatrix.sub_apply

/- warning: dmatrix.map_zero -> DMatrix.map_zero is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u3}} {n : Type.{u4}} [_inst_2 : Fintype.{u3} m] [_inst_3 : Fintype.{u4} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), Zero.{u1} (α i j)] {β : m -> n -> Type.{u2}} [_inst_6 : forall (i : m) (j : n), Zero.{u2} (β i j)] {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)}, (forall (i : m) (j : n), Eq.{succ u2} (β i j) (f i j (OfNat.ofNat.{u1} (α i j) 0 (OfNat.mk.{u1} (α i j) 0 (Zero.zero.{u1} (α i j) (_inst_5 i j))))) (OfNat.ofNat.{u2} (β i j) 0 (OfNat.mk.{u2} (β i j) 0 (Zero.zero.{u2} (β i j) (_inst_6 i j))))) -> (Eq.{succ (max u3 u4 u2)} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α (OfNat.ofNat.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) 0 (OfNat.mk.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) 0 (Zero.zero.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.hasZero.{u1, u3, u4} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j))))) (fun (i : m) (j : n) => β i j) f) (OfNat.ofNat.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) 0 (OfNat.mk.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) 0 (Zero.zero.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.hasZero.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j))))))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), Zero.{u3} (α i j)] {β : m -> n -> Type.{u4}} [_inst_6 : forall (i : m) (j : n), Zero.{u4} (β i j)] {f : forall {{i : m}} {{j : n}}, (α i j) -> (β i j)}, (forall (i : m) (j : n), Eq.{succ u4} (β i j) (f i j (OfNat.ofNat.{u3} (α i j) 0 (Zero.toOfNat0.{u3} (α i j) (_inst_5 i j)))) (OfNat.ofNat.{u4} (β i j) 0 (Zero.toOfNat0.{u4} (β i j) (_inst_6 i j)))) -> (Eq.{max (max (succ u4) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α (OfNat.ofNat.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) 0 (Zero.toOfNat0.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instZeroDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => _inst_5 i j)))) (fun (i : m) (j : n) => β i j) f) (OfNat.ofNat.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) 0 (Zero.toOfNat0.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instZeroDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))))
Case conversion may be inaccurate. Consider using '#align dmatrix.map_zero DMatrix.map_zeroₓ'. -/
@[simp]
theorem map_zero [∀ i j, Zero (α i j)] {β : m → n → Type w} [∀ i j, Zero (β i j)]
    {f : ∀ ⦃i j⦄, α i j → β i j} (h : ∀ i j, f (0 : α i j) = 0) : (0 : DMatrix m n α).map f = 0 :=
  by
  ext
  simp [h]
#align dmatrix.map_zero DMatrix.map_zero

/- warning: dmatrix.map_add -> DMatrix.map_add is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u3}} {n : Type.{u4}} [_inst_2 : Fintype.{u3} m] [_inst_3 : Fintype.{u4} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), AddMonoid.{u1} (α i j)] {β : m -> n -> Type.{u2}} [_inst_6 : forall (i : m) (j : n), AddMonoid.{u2} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (M : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (N : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α), Eq.{succ (max u3 u4 u2)} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α (HAdd.hAdd.{max u3 u4 u1, max u3 u4 u1, max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (instHAdd.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.hasAdd.{u1, u3, u4} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => AddZeroClass.toHasAdd.{u1} (α i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j))))) M N) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (f i j))) (HAdd.hAdd.{max u3 u4 u2, max u3 u4 u2, max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (instHAdd.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.hasAdd.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => AddZeroClass.toHasAdd.{u2} (β i j) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))))) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (f i j))) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α N (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (f i j))))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), AddMonoid.{u3} (α i j)] {β : m -> n -> Type.{u4}} [_inst_6 : forall (i : m) (j : n), AddMonoid.{u4} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α), Eq.{max (max (succ u4) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α (HAdd.hAdd.{max (max u3 u2) u1, max (max u3 u2) u1, max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (instHAdd.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instAddDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j))))) M N) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j)) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))))) (f i j))) (HAdd.hAdd.{max (max u4 u2) u1, max (max u4 u2) u1, max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (instHAdd.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))))) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j)) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))))) (f i j))) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α N (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j)) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))))) (f i j))))
Case conversion may be inaccurate. Consider using '#align dmatrix.map_add DMatrix.map_addₓ'. -/
theorem map_add [∀ i j, AddMonoid (α i j)] {β : m → n → Type w} [∀ i j, AddMonoid (β i j)]
    (f : ∀ ⦃i j⦄, α i j →+ β i j) (M N : DMatrix m n α) :
    ((M + N).map fun i j => @f i j) = (M.map fun i j => @f i j) + N.map fun i j => @f i j :=
  by
  ext
  simp
#align dmatrix.map_add DMatrix.map_add

/- warning: dmatrix.map_sub -> DMatrix.map_sub is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u3}} {n : Type.{u4}} [_inst_2 : Fintype.{u3} m] [_inst_3 : Fintype.{u4} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), AddGroup.{u1} (α i j)] {β : m -> n -> Type.{u2}} [_inst_6 : forall (i : m) (j : n), AddGroup.{u2} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (M : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (N : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α), Eq.{succ (max u3 u4 u2)} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α (HSub.hSub.{max u3 u4 u1, max u3 u4 u1, max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (instHSub.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α) (DMatrix.hasSub.{u1, u3, u4} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => SubNegMonoid.toHasSub.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j))))) M N) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (f i j))) (HSub.hSub.{max u3 u4 u2, max u3 u4 u2, max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (instHSub.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.hasSub.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => SubNegMonoid.toHasSub.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (f i j))) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α N (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (SubNegMonoid.toAddMonoid.{u1} (α i j) (AddGroup.toSubNegMonoid.{u1} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u2} (β i j) (SubNegMonoid.toAddMonoid.{u2} (β i j) (AddGroup.toSubNegMonoid.{u2} (β i j) (_inst_6 i j))))) (f i j))))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), AddGroup.{u3} (α i j)] {β : m -> n -> Type.{u4}} [_inst_6 : forall (i : m) (j : n), AddGroup.{u4} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (N : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α), Eq.{max (max (succ u4) (succ u2)) (succ u1)} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α (HSub.hSub.{max (max u3 u2) u1, max (max u3 u2) u1, max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (instHSub.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α) (DMatrix.instSubDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 α (fun (i : m) (j : n) => SubNegMonoid.toSub.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j))))) M N) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j))))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j)))) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))))) (f i j))) (HSub.hSub.{max (max u4 u2) u1, max (max u4 u2) u1, max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (instHSub.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instSubDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => SubNegMonoid.toSub.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j))))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j)))) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))))) (f i j))) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α N (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j))))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j)))) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (SubNegMonoid.toAddMonoid.{u3} (α i j) (AddGroup.toSubNegMonoid.{u3} (α i j) (_inst_5 i j)))) (AddMonoid.toAddZeroClass.{u4} (β i j) (SubNegMonoid.toAddMonoid.{u4} (β i j) (AddGroup.toSubNegMonoid.{u4} (β i j) (_inst_6 i j))))))) (f i j))))
Case conversion may be inaccurate. Consider using '#align dmatrix.map_sub DMatrix.map_subₓ'. -/
theorem map_sub [∀ i j, AddGroup (α i j)] {β : m → n → Type w} [∀ i j, AddGroup (β i j)]
    (f : ∀ ⦃i j⦄, α i j →+ β i j) (M N : DMatrix m n α) :
    ((M - N).map fun i j => @f i j) = (M.map fun i j => @f i j) - N.map fun i j => @f i j :=
  by
  ext
  simp
#align dmatrix.map_sub DMatrix.map_sub

#print DMatrix.subsingleton_of_empty_left /-
instance subsingleton_of_empty_left [IsEmpty m] : Subsingleton (DMatrix m n α) :=
  ⟨fun M N => by
    ext
    exact isEmptyElim i⟩
#align dmatrix.subsingleton_of_empty_left DMatrix.subsingleton_of_empty_left
-/

#print DMatrix.subsingleton_of_empty_right /-
instance subsingleton_of_empty_right [IsEmpty n] : Subsingleton (DMatrix m n α) :=
  ⟨fun M N => by
    ext
    exact isEmptyElim j⟩
#align dmatrix.subsingleton_of_empty_right DMatrix.subsingleton_of_empty_right
-/

end DMatrix

#print AddMonoidHom.mapDMatrix /-
/-- The `add_monoid_hom` between spaces of dependently typed matrices
induced by an `add_monoid_hom` between their coefficients. -/
def AddMonoidHom.mapDMatrix [∀ i j, AddMonoid (α i j)] {β : m → n → Type w}
    [∀ i j, AddMonoid (β i j)] (f : ∀ ⦃i j⦄, α i j →+ β i j) : DMatrix m n α →+ DMatrix m n β
    where
  toFun M := M.map fun i j => @f i j
  map_zero' := by simp
  map_add' := DMatrix.map_add f
#align add_monoid_hom.map_dmatrix AddMonoidHom.mapDMatrix
-/

/- warning: add_monoid_hom.map_dmatrix_apply -> AddMonoidHom.mapDMatrix_apply is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u3}} {n : Type.{u4}} [_inst_2 : Fintype.{u3} m] [_inst_3 : Fintype.{u4} n] {α : m -> n -> Type.{u1}} [_inst_5 : forall (i : m) (j : n), AddMonoid.{u1} (α i j)] {β : m -> n -> Type.{u2}} [_inst_6 : forall (i : m) (j : n), AddMonoid.{u2} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (M : DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 α), Eq.{succ (max u3 u4 u2)} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (coeFn.{max (succ (max u3 u4 u2)) (succ (max u3 u4 u1)), max (succ (max u3 u4 u1)) (succ (max u3 u4 u2))} (AddMonoidHom.{max u3 u4 u1, max u3 u4 u2} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.addMonoid.{u1, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_5 i j) i j))) (AddMonoid.toAddZeroClass.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.addMonoid.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_6 i j) i j)))) (fun (_x : AddMonoidHom.{max u3 u4 u1, max u3 u4 u2} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.addMonoid.{u1, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_5 i j) i j))) (AddMonoid.toAddZeroClass.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.addMonoid.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_6 i j) i j)))) => (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) -> (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j))) (AddMonoidHom.hasCoeToFun.{max u3 u4 u1, max u3 u4 u2} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max u3 u4 u1} (DMatrix.{u3, u4, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.addMonoid.{u1, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_5 i j) i j))) (AddMonoid.toAddZeroClass.{max u3 u4 u2} (DMatrix.{u3, u4, u2} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.addMonoid.{u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => (fun (i : m) (j : n) => _inst_6 i j) i j)))) (AddMonoidHom.mapDMatrix.{u1, u2, u3, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j) f) M) (DMatrix.map.{u1, u2, u3, u4} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (fun (_x : AddMonoidHom.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) => (α i j) -> (β i j)) (AddMonoidHom.hasCoeToFun.{u1, u2} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u1} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u2} (β i j) (_inst_6 i j))) (f i j)))
but is expected to have type
  forall {m : Type.{u2}} {n : Type.{u1}} [_inst_2 : Fintype.{u2} m] [_inst_3 : Fintype.{u1} n] {α : m -> n -> Type.{u3}} [_inst_5 : forall (i : m) (j : n), AddMonoid.{u3} (α i j)] {β : m -> n -> Type.{u4}} [_inst_6 : forall (i : m) (j : n), AddMonoid.{u4} (β i j)] (f : forall {{i : m}} {{j : n}}, AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (M : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 α), Eq.{max (max (succ u4) (succ u2)) (succ u1)} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) => DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) M) (FunLike.coe.{max (max (max (succ u3) (succ u4)) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1), max (max (succ u4) (succ u2)) (succ u1)} (AddMonoidHom.{max (max u3 u1) u2, max (max u4 u1) u2} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j))) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (fun (_x : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) => DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) _x) (AddHomClass.toFunLike.{max (max (max u3 u4) u2) u1, max (max u3 u2) u1, max (max u4 u2) u1} (AddMonoidHom.{max (max u3 u1) u2, max (max u4 u1) u2} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j))) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddZeroClass.toAdd.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j)))) (AddZeroClass.toAdd.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))) (AddMonoidHomClass.toAddHomClass.{max (max (max u3 u4) u2) u1, max (max u3 u2) u1, max (max u4 u2) u1} (AddMonoidHom.{max (max u3 u1) u2, max (max u4 u1) u2} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j))) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))) (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j))) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j))) (AddMonoidHom.addMonoidHomClass.{max (max u3 u2) u1, max (max u4 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (AddMonoid.toAddZeroClass.{max (max u3 u2) u1} (DMatrix.{u2, u1, u3} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j)) (DMatrix.instAddMonoidDMatrix.{u3, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j))) (AddMonoid.toAddZeroClass.{max (max u4 u2) u1} (DMatrix.{u2, u1, u4} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j)) (DMatrix.instAddMonoidDMatrix.{u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j)))))) (AddMonoidHom.mapDMatrix.{u3, u4, u2, u1} m n _inst_2 _inst_3 (fun (i : m) (j : n) => α i j) (fun (i : m) (j : n) => _inst_5 i j) (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => _inst_6 i j) f) M) (DMatrix.map.{u3, u4, u2, u1} m n _inst_2 _inst_3 α M (fun (i : m) (j : n) => β i j) (fun (i : m) (j : n) => FunLike.coe.{max (succ u3) (succ u4), succ u3, succ u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (fun (_x : α i j) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α i j) => β i j) _x) (AddHomClass.toFunLike.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddZeroClass.toAdd.{u3} (α i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j))) (AddZeroClass.toAdd.{u4} (β i j) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (AddMonoidHomClass.toAddHomClass.{max u3 u4, u3, u4} (AddMonoidHom.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))) (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j)) (AddMonoidHom.addMonoidHomClass.{u3, u4} (α i j) (β i j) (AddMonoid.toAddZeroClass.{u3} (α i j) (_inst_5 i j)) (AddMonoid.toAddZeroClass.{u4} (β i j) (_inst_6 i j))))) (f i j)))
Case conversion may be inaccurate. Consider using '#align add_monoid_hom.map_dmatrix_apply AddMonoidHom.mapDMatrix_applyₓ'. -/
@[simp]
theorem AddMonoidHom.mapDMatrix_apply [∀ i j, AddMonoid (α i j)] {β : m → n → Type w}
    [∀ i j, AddMonoid (β i j)] (f : ∀ ⦃i j⦄, α i j →+ β i j) (M : DMatrix m n α) :
    AddMonoidHom.mapDMatrix f M = M.map fun i j => @f i j :=
  rfl
#align add_monoid_hom.map_dmatrix_apply AddMonoidHom.mapDMatrix_apply


import data.nat.basic
import data.pnat.defs
import tactic.squeeze

namespace tactic
namespace interactive
setup_tactic_parser

/-- version of squeeze_simp that tests whether the output matches the expected output -/
meta def squeeze_simp_test
  (key : parse cur_pos)
  (slow_and_accurate : parse (tk "?")?)
  (use_iota_eqn : parse (tk "!")?) (no_dflt : parse only_flag) (hs : parse simp_arg_list)
  (attr_names : parse with_ident_list) (locat : parse location)
  (cfg : parse struct_inst?)
  (_ : parse (tk "=")) (l : parse simp_arg_list) : tactic unit :=
do (cfg',c) ← parse_config cfg,
   squeeze_simp_core slow_and_accurate.is_some no_dflt hs
     (λ l_no_dft l_args, simp use_iota_eqn none l_no_dft l_args attr_names locat cfg')
     (λ a, guard (a.map to_string = l.map to_string) <|> fail!"{a.map to_string} expected.")
end interactive
end tactic

-- Test that squeeze_simp succeeds when it closes the goal.
example : 1 = 1 :=
by { squeeze_simp_test = [eq_self_iff_true] }

-- Test that `squeeze_simp` succeeds when given arguments.
example {a b : ℕ} (h : a + a = b) : b + 0 = 2 * a :=
by { squeeze_simp_test [←h, two_mul] = [←h, two_mul, add_zero] }

-- Test that the order of the given hypotheses do not matter.
example {a b : ℕ} (h : a + a = b) : b + 0 = 2 * a :=
by { squeeze_simp_test [←h, two_mul] = [←h, two_mul, add_zero] }

section namespacing1

@[simp] lemma asda {a : ℕ} : 0 ≤ a := nat.zero_le _

@[simp] lemma pnat.asda {a : ℕ+} : 1 ≤ a := pnat.one_le _

open pnat

-- Test that adding two clashing decls to a namespace doesn't break `squeeze_simp`.
example {a : ℕ} {b : ℕ+} : 0 ≤ a ∧ 1 ≤ b :=
by { squeeze_simp_test = [_root_.asda, pnat.asda, and_self] }

end namespacing1

section namespacing2

open nat

local attribute [simp] nat.mul_succ

-- Test that we strip superflous prefixes from `squeeze_simp` output, if needed.
example (n m : ℕ) : n * m.succ = n*m + n :=
by { squeeze_simp_test = [mul_succ] }

end namespacing2

def a := 0
def b := 0
def c := 0
def f : ℕ → ℕ := default
@[simp] lemma k (x) : f x = b := rfl
@[simp] lemma l : f b = c := rfl

-- Test the fix for #3097
example : f (f a) = c := by { squeeze_simp_test = [k, l] }


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import topology.sheaves.presheaf
import category_theory.limits.punit
import category_theory.limits.shapes.products
import category_theory.limits.shapes.equalizers
import category_theory.full_subcategory

/-!
# The sheaf condition in terms of an equalizer of products

Here we set up the machinery for the "usual" definition of the sheaf condition,
e.g. as in https://stacks.math.columbia.edu/tag/0072
in terms of an equalizer diagram where the two objects are
`∏ F.obj (U i)` and `∏ F.obj (U i) ⊓ (U j)`.

-/

universes v u

noncomputable theory

open category_theory
open category_theory.limits
open topological_space
open opposite
open topological_space.opens

namespace Top

variables {C : Type u} [category.{v} C] [has_products C]
variables {X : Top.{v}} (F : presheaf C X) {ι : Type v} (U : ι → opens X)

namespace presheaf

namespace sheaf_condition_equalizer_products

/-- The product of the sections of a presheaf over a family of open sets. -/
def pi_opens : C := ∏ (λ i : ι, F.obj (op (U i)))
/--
The product of the sections of a presheaf over the pairwise intersections of
a family of open sets.
-/
def pi_inters : C := ∏ (λ p : ι × ι, F.obj (op (U p.1 ⊓ U p.2)))

/--
The morphism `Π F.obj (U i) ⟶ Π F.obj (U i) ⊓ (U j)` whose components
are given by the restriction maps from `U i` to `U i ⊓ U j`.
-/
def left_res : pi_opens F U ⟶ pi_inters F U :=
pi.lift (λ p : ι × ι, pi.π _ p.1 ≫ F.map (inf_le_left (U p.1) (U p.2)).op)

/--
The morphism `Π F.obj (U i) ⟶ Π F.obj (U i) ⊓ (U j)` whose components
are given by the restriction maps from `U j` to `U i ⊓ U j`.
-/
def right_res : pi_opens F U ⟶ pi_inters F U :=
pi.lift (λ p : ι × ι, pi.π _ p.2 ≫ F.map (inf_le_right (U p.1) (U p.2)).op)

/--
The morphism `F.obj U ⟶ Π F.obj (U i)` whose components
are given by the restriction maps from `U j` to `U i ⊓ U j`.
-/
def res : F.obj (op (supr U)) ⟶ pi_opens F U :=
pi.lift (λ i : ι, F.map (topological_space.opens.le_supr U i).op)

@[simp] lemma res_π (i : ι) : res F U ≫ limit.π _ i = F.map (opens.le_supr U i).op :=
by rw [res, limit.lift_π, fan.mk_π_app]

lemma w : res F U ≫ left_res F U = res F U ≫ right_res F U :=
begin
  dsimp [res, left_res, right_res],
  ext,
  simp only [limit.lift_π, limit.lift_π_assoc, fan.mk_π_app, category.assoc],
  rw [←F.map_comp],
  rw [←F.map_comp],
  congr,
end

/--
The equalizer diagram for the sheaf condition.
-/
@[reducible]
def diagram : walking_parallel_pair.{v} ⥤ C :=
parallel_pair (left_res F U) (right_res F U)

/--
The restriction map `F.obj U ⟶ Π F.obj (U i)` gives a cone over the equalizer diagram
for the sheaf condition. The sheaf condition asserts this cone is a limit cone.
-/
def fork : fork.{v} (left_res F U) (right_res F U) := fork.of_ι _ (w F U)

@[simp]
lemma fork_X : (fork F U).X = F.obj (op (supr U)) := rfl

@[simp]
lemma fork_ι : (fork F U).ι = res F U := rfl
@[simp]
lemma fork_π_app_walking_parallel_pair_zero :
  (fork F U).π.app walking_parallel_pair.zero = res F U := rfl
@[simp]
lemma fork_π_app_walking_parallel_pair_one :
  (fork F U).π.app walking_parallel_pair.one = res F U ≫ left_res F U := rfl

variables {F} {G : presheaf C X}

/-- Isomorphic presheaves have isomorphic `pi_opens` for any cover `U`. -/
@[simp]
def pi_opens.iso_of_iso (α : F ≅ G) : pi_opens F U ≅ pi_opens G U :=
pi.map_iso (λ X, α.app _)

/-- Isomorphic presheaves have isomorphic `pi_inters` for any cover `U`. -/
@[simp]
def pi_inters.iso_of_iso (α : F ≅ G) : pi_inters F U ≅ pi_inters G U :=
pi.map_iso (λ X, α.app _)

/-- Isomorphic presheaves have isomorphic sheaf condition diagrams. -/
def diagram.iso_of_iso (α : F ≅ G) : diagram F U ≅ diagram G U :=
nat_iso.of_components
  begin rintro ⟨⟩, exact pi_opens.iso_of_iso U α, exact pi_inters.iso_of_iso U α end
  begin
    rintro ⟨⟩ ⟨⟩ ⟨⟩,
    { simp, },
    { ext, simp [left_res], },
    { ext, simp [right_res], },
    { simp, },
  end.

/--
If `F G : presheaf C X` are isomorphic presheaves,
then the `fork F U`, the canonical cone of the sheaf condition diagram for `F`,
is isomorphic to `fork F G` postcomposed with the corresponding isomorphism between
sheaf condition diagrams.
-/
def fork.iso_of_iso (α : F ≅ G) :
  fork F U ≅ (cones.postcompose (diagram.iso_of_iso U α).inv).obj (fork G U) :=
begin
  fapply fork.ext,
  { apply α.app, },
  { ext,
    dunfold fork.ι, -- Ugh, `simp` can't unfold abbreviations.
    simp [res, diagram.iso_of_iso], }
end

section open_embedding

variables {V : Top.{v}} {j : V ⟶ X} (oe : open_embedding j)
variables (𝒰 : ι → opens V)

/--
Push forward a cover along an open embedding.
-/
@[simp]
def cover.of_open_embedding : ι → opens X := (λ i, oe.is_open_map.functor.obj (𝒰 i))

/--
The isomorphism between `pi_opens` corresponding to an open embedding.
-/
@[simp]
def pi_opens.iso_of_open_embedding :
  pi_opens (oe.is_open_map.functor.op ⋙ F) 𝒰 ≅ pi_opens F (cover.of_open_embedding oe 𝒰) :=
pi.map_iso (λ X, F.map_iso (iso.refl _))

/--
The isomorphism between `pi_inters` corresponding to an open embedding.
-/
@[simp]
def pi_inters.iso_of_open_embedding :
  pi_inters (oe.is_open_map.functor.op ⋙ F) 𝒰 ≅ pi_inters F (cover.of_open_embedding oe 𝒰) :=
pi.map_iso (λ X, F.map_iso
  begin
    dsimp [is_open_map.functor],
    exact iso.op
    { hom := hom_of_le (by
      { simp only [oe.to_embedding.inj, set.image_inter],
        apply le_refl _, }),
      inv := hom_of_le (by
      { simp only [oe.to_embedding.inj, set.image_inter],
        apply le_refl _, }), },
  end)

/-- The isomorphism of sheaf condition diagrams corresponding to an open embedding. -/
def diagram.iso_of_open_embedding :
  diagram (oe.is_open_map.functor.op ⋙ F) 𝒰 ≅ diagram F (cover.of_open_embedding oe 𝒰) :=
nat_iso.of_components
  begin
    rintro ⟨⟩,
    exact pi_opens.iso_of_open_embedding oe 𝒰,
    exact pi_inters.iso_of_open_embedding oe 𝒰
  end
  begin
    rintro ⟨⟩ ⟨⟩ ⟨⟩,
    { simp, },
    { ext,
      dsimp [left_res, is_open_map.functor],
      simp only [limit.lift_π, cones.postcompose_obj_π, iso.op_hom, discrete.nat_iso_hom_app,
        functor.map_iso_refl, functor.map_iso_hom, lim_map_π_assoc, limit.lift_map, fan.mk_π_app,
        nat_trans.comp_app, category.assoc],
      dsimp,
      rw [category.id_comp, ←F.map_comp],
      refl, },
    { ext,
      dsimp [right_res, is_open_map.functor],
      simp only [limit.lift_π, cones.postcompose_obj_π, iso.op_hom, discrete.nat_iso_hom_app,
        functor.map_iso_refl, functor.map_iso_hom, lim_map_π_assoc, limit.lift_map, fan.mk_π_app,
        nat_trans.comp_app, category.assoc],
      dsimp,
      rw [category.id_comp, ←F.map_comp],
      refl, },
    { simp, },
  end.

/--
If `F : presheaf C X` is a presheaf, and `oe : U ⟶ X` is an open embedding,
then the sheaf condition fork for a cover `𝒰` in `U` for the composition of `oe` and `F` is
isomorphic to sheaf condition fork for `oe '' 𝒰`, precomposed with the isomorphism
of indexing diagrams `diagram.iso_of_open_embedding`.

We use this to show that the restriction of sheaf along an open embedding is still a sheaf.
-/
def fork.iso_of_open_embedding :
  fork (oe.is_open_map.functor.op ⋙ F) 𝒰 ≅
    (cones.postcompose (diagram.iso_of_open_embedding oe 𝒰).inv).obj
      (fork F (cover.of_open_embedding oe 𝒰)) :=
begin
  fapply fork.ext,
  { dsimp [is_open_map.functor],
    exact
    F.map_iso (iso.op
    { hom := hom_of_le
      (by simp only [supr_s, supr_mk, le_def, subtype.coe_mk, set.le_eq_subset, set.image_Union]),
      inv := hom_of_le
      (by simp only [supr_s, supr_mk, le_def, subtype.coe_mk, set.le_eq_subset, set.image_Union]),
    }), },
  { ext,
    dunfold fork.ι, -- Ugh, it is unpleasant that we need this.
    simp only [res, diagram.iso_of_open_embedding, discrete.nat_iso_inv_app, functor.map_iso_inv,
      limit.lift_π, cones.postcompose_obj_π, functor.comp_map,
      fork_π_app_walking_parallel_pair_zero, pi_opens.iso_of_open_embedding,
      nat_iso.of_components.inv_app, functor.map_iso_refl, functor.op_map, limit.lift_map,
      fan.mk_π_app, nat_trans.comp_app, quiver.hom.unop_op, category.assoc, lim_map_eq_lim_map],
    dsimp,
    rw [category.comp_id, ←F.map_comp],
    refl, },
end

end open_embedding

end sheaf_condition_equalizer_products

end presheaf

end Top


import M.Marlowe.Language
import M.Marlowe.Semantics

namespace Marlowe.Proof

open Marlowe.Language.Contract
open Marlowe.Language.State
open Marlowe.Semantics (evaluate observe)

variable (e : Environment)
variable (s : State)

variable (a b : Observation)
variable (a' b' : Bool)

variable (ha : observe e s a = a')
variable (hb : observe e s b = b')

variable (c : ChoiceIdT)

variable (x y : Value)
variable (x' y' : Int)

variable (hx : evaluate e s x = x')
variable (hy : evaluate e s y = y')

theorem observe_and : observe e s (AndObs a b) = (a' && b') :=
  by simp [observe, ha, hb]

#check observe_and

theorem observe_or : observe e s (OrObs a b) = (a' || b') :=
  by simp [observe, ha, hb]

#check observe_or

theorem observe_not : observe e s (NotObs a) = !a' :=
 by simp [observe, ha]

#check observe_not

theorem observe_chosen : observe e s (ChoseSomething c) = s.choices.member c :=
  by simp [observe]

#check observe_chosen

theorem observe_ge : observe e s (ValueGE x y) = (x' >= y') :=
  by simp [observe, hx, hy]

#check observe_ge

theorem observe_gt : observe e s (ValueGT x y) = (x' > y') :=
  by simp [observe, hx, hy]

#check observe_gt

theorem observe_lt : observe e s (ValueLT x y) = (x' < y') :=
  by simp [observe, hx, hy]

#check observe_lt

theorem observe_le : observe e s (ValueLE x y) = (x' <= y') :=
  by simp [observe, hx, hy]

#check observe_le

theorem observe_eq : observe e s (ValueEQ x y) = (x' == y') :=
  by simp [observe, hx, hy]

#check observe_eq

theorem observe_true : observe e s TrueObs = true :=
  by rfl

#check observe_true

theorem observe_false : observe e s FalseObs = false :=
  by rfl

#check observe_false

theorem negate_true : observe e s (NotObs TrueObs) = false := by simp [observe]

example : observe e s (NotObs TrueObs) = false := by
  calc
    observe e s (NotObs TrueObs) = ! observe e s TrueObs := by simp [observe]
    _                            = ! true                := by rfl
    _                            = false                 := by rfl

end Marlowe.Proof


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.finite_limits
import Mathlib.category_theory.limits.shapes.binary_products
import Mathlib.category_theory.limits.shapes.terminal
import Mathlib.PostPort

universes v u

namespace Mathlib

namespace category_theory.limits

/--
A category has finite products if there is a chosen limit for every diagram
with shape `discrete J`, where we have `[decidable_eq J]` and `[fintype J]`.
-/
-- We can't simply make this an abbreviation, as we do with other `has_Xs` limits typeclasses,

-- because of https://github.com/leanprover-community/lean/issues/429

def has_finite_products (C : Type u) [category C] :=
  ∀ (J : Type v) [_inst_2 : DecidableEq J] [_inst_3 : fintype J], has_limits_of_shape (discrete J) C

protected instance has_limits_of_shape_discrete (C : Type u) [category C] (J : Type v) [fintype J] [has_finite_products C] : has_limits_of_shape (discrete J) C :=
  _inst_3 J

/-- If `C` has finite limits then it has finite products. -/
theorem has_finite_products_of_has_finite_limits (C : Type u) [category C] [has_finite_limits C] : has_finite_products C :=
  fun (J : Type v) (𝒥₁ : DecidableEq J) (𝒥₂ : fintype J) => limits.has_limits_of_shape_of_has_finite_limits C (discrete J)

/--
If a category has all products then in particular it has finite products.
-/
theorem has_finite_products_of_has_products (C : Type u) [category C] [has_products C] : has_finite_products C :=
  id fun (J : Type v) => _inst_2 J

/--
A category has finite coproducts if there is a chosen colimit for every diagram
with shape `discrete J`, where we have `[decidable_eq J]` and `[fintype J]`.
-/
def has_finite_coproducts (C : Type u) [category C] :=
  ∀ (J : Type v) [_inst_2 : DecidableEq J] [_inst_3 : fintype J], has_colimits_of_shape (discrete J) C

protected instance has_colimits_of_shape_discrete (C : Type u) [category C] (J : Type v) [fintype J] [has_finite_coproducts C] : has_colimits_of_shape (discrete J) C :=
  _inst_3 J

/-- If `C` has finite colimits then it has finite coproducts. -/
theorem has_finite_coproducts_of_has_finite_colimits (C : Type u) [category C] [has_finite_colimits C] : has_finite_coproducts C :=
  fun (J : Type v) (𝒥₁ : DecidableEq J) (𝒥₂ : fintype J) =>
    limits.has_colimits_of_shape_of_has_finite_colimits C (discrete J)

/--
If a category has all coproducts then in particular it has finite coproducts.
-/
theorem has_finite_coproducts_of_has_coproducts (C : Type u) [category C] [has_coproducts C] : has_finite_coproducts C :=
  id fun (J : Type v) => _inst_2 J


/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Floris van Doorn
-/
import geometry.manifold.mfderiv

/-!
### Interactions between differentiability, smoothness and manifold derivatives

We give the relation between `mdifferentiable`, `cont_mdiff`, `mfderiv`, `tangent_map`
and related notions.

## Main statements

* `cont_mdiff_on.cont_mdiff_on_tangent_map_within` states that the bundled derivative
  of a `Cⁿ` function in a domain is `Cᵐ` when `m + 1 ≤ n`.
* `cont_mdiff.cont_mdiff_tangent_map` states that the bundled derivative
  of a `Cⁿ` function is `Cᵐ` when `m + 1 ≤ n`.
-/

open set function filter charted_space smooth_manifold_with_corners bundle
open_locale topology manifold bundle

/-! ### Definition of smooth functions between manifolds -/

variables {𝕜 : Type*} [nontrivially_normed_field 𝕜]
-- declare a smooth manifold `M` over the pair `(E, H)`.
{E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] {I : model_with_corners 𝕜 E H}
{M : Type*} [topological_space M] [charted_space H M] [Is : smooth_manifold_with_corners I M]
-- declare a smooth manifold `M'` over the pair `(E', H')`.
{E' : Type*} [normed_add_comm_group E'] [normed_space 𝕜 E']
{H' : Type*} [topological_space H'] {I' : model_with_corners 𝕜 E' H'}
{M' : Type*} [topological_space M'] [charted_space H' M'] [I's : smooth_manifold_with_corners I' M']
-- declare a smooth manifold `N` over the pair `(F, G)`.
{F : Type*} [normed_add_comm_group F] [normed_space 𝕜 F]
{G : Type*} [topological_space G] {J : model_with_corners 𝕜 F G}
{N : Type*} [topological_space N] [charted_space G N] [Js : smooth_manifold_with_corners J N]
-- declare a smooth manifold `N'` over the pair `(F', G')`.
{F' : Type*} [normed_add_comm_group F'] [normed_space 𝕜 F']
{G' : Type*} [topological_space G'] {J' : model_with_corners 𝕜 F' G'}
{N' : Type*} [topological_space N'] [charted_space G' N'] [J's : smooth_manifold_with_corners J' N']
-- declare some additional normed spaces, used for fibers of vector bundles
{F₁ : Type*} [normed_add_comm_group F₁] [normed_space 𝕜 F₁]
{F₂ : Type*} [normed_add_comm_group F₂] [normed_space 𝕜 F₂]
-- declare functions, sets, points and smoothness indices
{f f₁ : M → M'} {s s₁ t : set M} {x : M} {m n : ℕ∞}

/-! ### Deducing differentiability from smoothness -/

lemma cont_mdiff_within_at.mdifferentiable_within_at
  (hf : cont_mdiff_within_at I I' n f s x) (hn : 1 ≤ n) :
  mdifferentiable_within_at I I' f s x :=
begin
  suffices h : mdifferentiable_within_at I I' f (s ∩ (f ⁻¹' (ext_chart_at I' (f x)).source)) x,
  { rwa mdifferentiable_within_at_inter' at h,
    apply (hf.1).preimage_mem_nhds_within,
    exact ext_chart_at_source_mem_nhds I' (f x) },
  rw mdifferentiable_within_at_iff,
  exact ⟨hf.1.mono (inter_subset_left _ _),
    (hf.2.differentiable_within_at hn).mono (by mfld_set_tac)⟩,
end

lemma cont_mdiff_at.mdifferentiable_at (hf : cont_mdiff_at I I' n f x) (hn : 1 ≤ n) :
  mdifferentiable_at I I' f x :=
mdifferentiable_within_at_univ.1 $ cont_mdiff_within_at.mdifferentiable_within_at hf hn

lemma cont_mdiff_on.mdifferentiable_on (hf : cont_mdiff_on I I' n f s) (hn : 1 ≤ n) :
  mdifferentiable_on I I' f s :=
λ x hx, (hf x hx).mdifferentiable_within_at hn

lemma cont_mdiff.mdifferentiable (hf : cont_mdiff I I' n f) (hn : 1 ≤ n) :
  mdifferentiable I I' f :=
λ x, (hf x).mdifferentiable_at hn

lemma smooth_within_at.mdifferentiable_within_at
  (hf : smooth_within_at I I' f s x) : mdifferentiable_within_at I I' f s x :=
hf.mdifferentiable_within_at le_top

lemma smooth_at.mdifferentiable_at (hf : smooth_at I I' f x) : mdifferentiable_at I I' f x :=
hf.mdifferentiable_at le_top

lemma smooth_on.mdifferentiable_on (hf : smooth_on I I' f s) : mdifferentiable_on I I' f s :=
hf.mdifferentiable_on le_top

lemma smooth.mdifferentiable (hf : smooth I I' f) : mdifferentiable I I' f :=
cont_mdiff.mdifferentiable hf le_top

lemma smooth.mdifferentiable_at (hf : smooth I I' f) : mdifferentiable_at I I' f x :=
hf.mdifferentiable x

lemma smooth.mdifferentiable_within_at (hf : smooth I I' f) :
  mdifferentiable_within_at I I' f s x :=
hf.mdifferentiable_at.mdifferentiable_within_at

/-! ### The tangent map of a smooth function is smooth -/

section tangent_map

/-- If a function is `C^n` with `1 ≤ n` on a domain with unique derivatives, then its bundled
derivative is continuous. In this auxiliary lemma, we prove this fact when the source and target
space are model spaces in models with corners. The general fact is proved in
`cont_mdiff_on.continuous_on_tangent_map_within`-/
lemma cont_mdiff_on.continuous_on_tangent_map_within_aux
  {f : H → H'} {s : set H}
  (hf : cont_mdiff_on I I' n f s) (hn : 1 ≤ n) (hs : unique_mdiff_on I s) :
  continuous_on (tangent_map_within I I' f s) (π (tangent_space I) ⁻¹' s) :=
begin
  suffices h : continuous_on (λ (p : H × E), (f p.fst,
    (fderiv_within 𝕜 (written_in_ext_chart_at I I' p.fst f) (I.symm ⁻¹' s ∩ range I)
      ((ext_chart_at I p.fst) p.fst) : E →L[𝕜] E') p.snd)) (prod.fst ⁻¹' s),
  { have A := (tangent_bundle_model_space_homeomorph H I).continuous,
    rw continuous_iff_continuous_on_univ at A,
    have B := ((tangent_bundle_model_space_homeomorph H' I').symm.continuous.comp_continuous_on h)
      .comp' A,
    have : (univ ∩ ⇑(tangent_bundle_model_space_homeomorph H I) ⁻¹' (prod.fst ⁻¹' s)) =
      π (tangent_space I) ⁻¹' s,
      by { ext ⟨x, v⟩, simp only with mfld_simps },
    rw this at B,
    apply B.congr,
    rintros ⟨x, v⟩ hx,
    dsimp [tangent_map_within],
    ext, { refl },
    simp only with mfld_simps,
    apply congr_fun,
    apply congr_arg,
    rw mdifferentiable_within_at.mfderiv_within (hf.mdifferentiable_on hn x hx),
    refl },
  suffices h : continuous_on (λ (p : H × E), (fderiv_within 𝕜 (I' ∘ f ∘ I.symm)
    (I.symm ⁻¹' s ∩ range I) (I p.fst) : E →L[𝕜] E') p.snd) (prod.fst ⁻¹' s),
  { dsimp [written_in_ext_chart_at, ext_chart_at],
    apply continuous_on.prod
      (continuous_on.comp hf.continuous_on continuous_fst.continuous_on (subset.refl _)),
    apply h.congr,
    assume p hp,
    refl },
  suffices h : continuous_on (fderiv_within 𝕜 (I' ∘ f ∘ I.symm)
                     (I.symm ⁻¹' s ∩ range I)) (I '' s),
  { have C := continuous_on.comp h I.continuous_to_fun.continuous_on (subset.refl _),
    have A : continuous (λq : (E →L[𝕜] E') × E, q.1 q.2) :=
      is_bounded_bilinear_map_apply.continuous,
    have B : continuous_on (λp : H × E,
      (fderiv_within 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I)
                       (I p.1), p.2)) (prod.fst ⁻¹' s),
    { apply continuous_on.prod _ continuous_snd.continuous_on,
      refine (continuous_on.comp C continuous_fst.continuous_on _ : _),
      exact preimage_mono (subset_preimage_image _ _) },
    exact A.comp_continuous_on B },
  rw cont_mdiff_on_iff at hf,
  let x : H := I.symm (0 : E),
  let y : H' := I'.symm (0 : E'),
  have A := hf.2 x y,
  simp only [I.image_eq, inter_comm] with mfld_simps at A ⊢,
  apply A.continuous_on_fderiv_within _ hn,
  convert hs.unique_diff_on_target_inter x using 1,
  simp only [inter_comm] with mfld_simps
end

/-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative is
`C^m` when `m+1 ≤ n`. In this auxiliary lemma, we prove this fact when the source and target space
are model spaces in models with corners. The general fact is proved in
`cont_mdiff_on.cont_mdiff_on_tangent_map_within` -/
lemma cont_mdiff_on.cont_mdiff_on_tangent_map_within_aux
  {f : H → H'} {s : set H}
  (hf : cont_mdiff_on I I' n f s) (hmn : m + 1 ≤ n) (hs : unique_mdiff_on I s) :
  cont_mdiff_on I.tangent I'.tangent m (tangent_map_within I I' f s) (π (tangent_space I) ⁻¹' s) :=
begin
  have m_le_n : m ≤ n,
  { apply le_trans _ hmn,
    have : m + 0 ≤ m + 1 := add_le_add_left (zero_le _) _,
    simpa only [add_zero] using this },
  have one_le_n : 1 ≤ n,
  { apply le_trans _ hmn,
    change 0 + 1 ≤ m + 1,
    exact add_le_add_right (zero_le _) _ },
  have U': unique_diff_on 𝕜 (range I ∩ I.symm ⁻¹' s),
  { assume y hy,
    simpa only [unique_mdiff_on, unique_mdiff_within_at, hy.1, inter_comm] with mfld_simps
      using hs (I.symm y) hy.2 },
  rw cont_mdiff_on_iff,
  refine ⟨hf.continuous_on_tangent_map_within_aux one_le_n hs, λp q, _⟩,
  have A : range I ×ˢ univ ∩
      ((equiv.sigma_equiv_prod H E).symm ∘ λ (p : E × E), ((I.symm) p.fst, p.snd)) ⁻¹'
        (π (tangent_space I) ⁻¹' s)
      = (range I ∩ I.symm ⁻¹' s) ×ˢ univ,
    by { ext ⟨x, v⟩, simp only with mfld_simps },
  suffices h : cont_diff_on 𝕜 m (((λ (p : H' × E'), (I' p.fst, p.snd)) ∘
      (equiv.sigma_equiv_prod H' E')) ∘ tangent_map_within I I' f s ∘
      ((equiv.sigma_equiv_prod H E).symm) ∘ λ (p : E × E), (I.symm p.fst, p.snd))
    ((range ⇑I ∩ ⇑(I.symm) ⁻¹' s) ×ˢ univ),
    by simpa [A] using h,
  change cont_diff_on 𝕜 m (λ (p : E × E),
    ((I' (f (I.symm p.fst)), ((mfderiv_within I I' f s (I.symm p.fst)) : E → E') p.snd) : E' × E'))
    ((range I ∩ I.symm ⁻¹' s) ×ˢ univ),
  -- check that all bits in this formula are `C^n`
  have hf' := cont_mdiff_on_iff.1 hf,
  have A : cont_diff_on 𝕜 m (I' ∘ f ∘ I.symm) (range I ∩ I.symm ⁻¹' s) :=
    by simpa only with mfld_simps using (hf'.2 (I.symm 0) (I'.symm 0)).of_le m_le_n,
  have B : cont_diff_on 𝕜 m ((I' ∘ f ∘ I.symm) ∘ prod.fst)
           ((range I ∩ I.symm ⁻¹' s) ×ˢ univ) :=
    A.comp (cont_diff_fst.cont_diff_on) (prod_subset_preimage_fst _ _),
  suffices C : cont_diff_on 𝕜 m (λ (p : E × E),
    ((fderiv_within 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) p.1 : _) p.2))
    ((range I ∩ I.symm ⁻¹' s) ×ˢ univ),
  { apply cont_diff_on.prod B _,
    apply C.congr (λp hp, _),
    simp only with mfld_simps at hp,
    simp only [mfderiv_within, hf.mdifferentiable_on one_le_n _ hp.2, hp.1, if_pos]
      with mfld_simps },
  have D : cont_diff_on 𝕜 m (λ x,
    (fderiv_within 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) x))
    (range I ∩ I.symm ⁻¹' s),
  { have : cont_diff_on 𝕜 n (I' ∘ f ∘ I.symm) (range I ∩ I.symm ⁻¹' s) :=
      by simpa only with mfld_simps using (hf'.2 (I.symm 0) (I'.symm 0)),
    simpa only [inter_comm] using this.fderiv_within U' hmn },
  have := D.comp (cont_diff_fst.cont_diff_on) (prod_subset_preimage_fst _ _),
  have := cont_diff_on.prod this (cont_diff_snd.cont_diff_on),
  exact is_bounded_bilinear_map_apply.cont_diff.comp_cont_diff_on this,
end

include Is I's

/-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative
is `C^m` when `m+1 ≤ n`. -/
theorem cont_mdiff_on.cont_mdiff_on_tangent_map_within
  (hf : cont_mdiff_on I I' n f s) (hmn : m + 1 ≤ n) (hs : unique_mdiff_on I s) :
  cont_mdiff_on I.tangent I'.tangent m (tangent_map_within I I' f s)
  (π (tangent_space I) ⁻¹' s) :=
begin
  /- The strategy of the proof is to avoid unfolding the definitions, and reduce by functoriality
  to the case of functions on the model spaces, where we have already proved the result.
  Let `l` and `r` be the charts to the left and to the right, so that we have
  ```
     l^{-1}      f       r
  H --------> M ---> M' ---> H'
  ```
  Then the tangent map `T(r ∘ f ∘ l)` is smooth by a previous result. Consider the composition
  ```
      Tl        T(r ∘ f ∘ l^{-1})         Tr^{-1}
  TM -----> TH -------------------> TH' ---------> TM'
  ```
  where `Tr^{-1}` and `Tl` are the tangent maps of `r^{-1}` and `l`. Writing `Tl` and `Tr^{-1}` as
  composition of charts (called `Dl` and `il` for `l` and `Dr` and `ir` in the proof below), it
  follows that they are smooth. The composition of all these maps is `Tf`, and is therefore smooth
  as a composition of smooth maps.
  -/
  have m_le_n : m ≤ n,
  { apply le_trans _ hmn,
    have : m + 0 ≤ m + 1 := add_le_add_left (zero_le _) _,
    simpa only [add_zero] },
  have one_le_n : 1 ≤ n,
  { apply le_trans _ hmn,
    change 0 + 1 ≤ m + 1,
    exact add_le_add_right (zero_le _) _ },
  /- First step: local reduction on the space, to a set `s'` which is contained in chart domains. -/
  refine cont_mdiff_on_of_locally_cont_mdiff_on (λp hp, _),
  have hf' := cont_mdiff_on_iff.1 hf,
  simp only with mfld_simps at hp,
  let l  := chart_at H p.proj,
  set Dl := chart_at (model_prod H E) p with hDl,
  let r  := chart_at H' (f p.proj),
  let Dr := chart_at (model_prod H' E') (tangent_map_within I I' f s p),
  let il := chart_at (model_prod H E) (tangent_map I I l p),
  let ir := chart_at (model_prod H' E') (tangent_map I I' (r ∘ f) p),
  let s' := f ⁻¹' r.source ∩ s ∩ l.source,
  let s'_lift := π (tangent_space I) ⁻¹' s',
  let s'l := l.target ∩ l.symm ⁻¹' s',
  let s'l_lift := π (tangent_space I) ⁻¹' s'l,
  rcases continuous_on_iff'.1 hf'.1 r.source r.open_source with ⟨o, o_open, ho⟩,
  suffices h : cont_mdiff_on I.tangent I'.tangent m (tangent_map_within I I' f s) s'_lift,
  { refine ⟨π (tangent_space I) ⁻¹' (o ∩ l.source), _, _, _⟩,
    show is_open (π (tangent_space I) ⁻¹' (o ∩ l.source)), from
      (is_open.inter o_open l.open_source).preimage (continuous_proj E _) ,
    show p ∈ π (tangent_space I) ⁻¹' (o ∩ l.source),
    { simp,
      have : p.proj ∈ f ⁻¹' r.source ∩ s, by simp [hp],
      rw ho at this,
      exact this.1 },
    { have : π (tangent_space I) ⁻¹' s ∩ π (tangent_space I) ⁻¹' (o ∩ l.source) = s'_lift,
      { dsimp only [s'_lift, s'], rw [ho], mfld_set_tac },
      rw this,
      exact h } },
  /- Second step: check that all functions are smooth, and use the chain rule to write the bundled
  derivative as a composition of a function between model spaces and of charts.
  Convention: statements about the differentiability of `a ∘ b ∘ c` are named `diff_abc`. Statements
  about differentiability in the bundle have a `_lift` suffix. -/
  have U' : unique_mdiff_on I s',
  { apply unique_mdiff_on.inter _ l.open_source,
    rw [ho, inter_comm],
    exact hs.inter o_open },
  have U'l : unique_mdiff_on I s'l :=
    U'.unique_mdiff_on_preimage (mdifferentiable_chart _ _),
  have diff_f : cont_mdiff_on I I' n f s' :=
    hf.mono (by mfld_set_tac),
  have diff_r : cont_mdiff_on I' I' n r r.source :=
    cont_mdiff_on_chart,
  have diff_rf : cont_mdiff_on I I' n (r ∘ f) s',
  { apply cont_mdiff_on.comp diff_r diff_f (λx hx, _),
    simp only [s'] with mfld_simps at hx, simp only [hx] with mfld_simps },
  have diff_l : cont_mdiff_on I I n l.symm s'l,
  { have A : cont_mdiff_on I I n l.symm l.target :=
      cont_mdiff_on_chart_symm,
    exact A.mono (by mfld_set_tac) },
  have diff_rfl : cont_mdiff_on I I' n (r ∘ f ∘ l.symm) s'l,
  { apply cont_mdiff_on.comp diff_rf diff_l,
    mfld_set_tac },
  have diff_rfl_lift : cont_mdiff_on I.tangent I'.tangent m
      (tangent_map_within I I' (r ∘ f ∘ l.symm) s'l) s'l_lift :=
    diff_rfl.cont_mdiff_on_tangent_map_within_aux hmn U'l,
  have diff_irrfl_lift : cont_mdiff_on I.tangent I'.tangent m
      (ir ∘ (tangent_map_within I I' (r ∘ f ∘ l.symm) s'l)) s'l_lift,
  { have A : cont_mdiff_on I'.tangent I'.tangent m ir ir.source := cont_mdiff_on_chart,
    exact cont_mdiff_on.comp A diff_rfl_lift (λp hp, by simp only [ir] with mfld_simps) },
  have diff_Drirrfl_lift : cont_mdiff_on I.tangent I'.tangent m
    (Dr.symm ∘ (ir ∘ (tangent_map_within I I' (r ∘ f ∘ l.symm) s'l))) s'l_lift,
  { have A : cont_mdiff_on I'.tangent I'.tangent m Dr.symm Dr.target :=
      cont_mdiff_on_chart_symm,
    apply cont_mdiff_on.comp A diff_irrfl_lift (λp hp, _),
    simp only [s'l_lift] with mfld_simps at hp,
    simp only [ir, hp] with mfld_simps },
  -- conclusion of this step: the composition of all the maps above is smooth
  have diff_DrirrflilDl : cont_mdiff_on I.tangent I'.tangent m
    (Dr.symm ∘ (ir ∘ (tangent_map_within I I' (r ∘ f ∘ l.symm) s'l)) ∘
      (il.symm ∘ Dl)) s'_lift,
  { have A : cont_mdiff_on I.tangent I.tangent m Dl Dl.source := cont_mdiff_on_chart,
    have A' : cont_mdiff_on I.tangent I.tangent m Dl s'_lift,
    { apply A.mono (λp hp, _),
      simp only [s'_lift] with mfld_simps at hp,
      simp only [Dl, hp] with mfld_simps },
    have B : cont_mdiff_on I.tangent I.tangent m il.symm il.target :=
      cont_mdiff_on_chart_symm,
    have C : cont_mdiff_on I.tangent I.tangent m (il.symm ∘ Dl) s'_lift :=
      cont_mdiff_on.comp B A' (λp hp, by simp only [il] with mfld_simps),
    apply cont_mdiff_on.comp diff_Drirrfl_lift C (λp hp, _),
    simp only [s'_lift] with mfld_simps at hp,
    simp only [il, s'l_lift, hp, total_space.proj] with mfld_simps },
  /- Third step: check that the composition of all the maps indeed coincides with the derivative we
  are looking for -/
  have eq_comp : ∀q ∈ s'_lift, tangent_map_within I I' f s q =
      (Dr.symm ∘ ir ∘ (tangent_map_within I I' (r ∘ f ∘ l.symm) s'l) ∘
      (il.symm ∘ Dl)) q,
  { assume q hq,
    simp only [s'_lift] with mfld_simps at hq,
    have U'q : unique_mdiff_within_at I s' q.1,
      by { apply U', simp only [hq, s'] with mfld_simps },
    have U'lq : unique_mdiff_within_at I s'l (Dl q).1,
      by { apply U'l, simp only [hq, s'l] with mfld_simps },
    have A : tangent_map_within I I' ((r ∘ f) ∘ l.symm) s'l (il.symm (Dl q)) =
      tangent_map_within I I' (r ∘ f) s' (tangent_map_within I I l.symm s'l (il.symm (Dl q))),
    { refine tangent_map_within_comp_at (il.symm (Dl q)) _ _ (λp hp, _) U'lq,
      { apply diff_rf.mdifferentiable_on one_le_n,
        simp only [hq] with mfld_simps },
      { apply diff_l.mdifferentiable_on one_le_n,
        simp only [s'l, hq] with mfld_simps },
      { simp only with mfld_simps at hp, simp only [hp] with mfld_simps } },
    have B : tangent_map_within I I l.symm s'l (il.symm (Dl q)) = q,
    { have : tangent_map_within I I l.symm s'l (il.symm (Dl q))
        = tangent_map I I l.symm (il.symm (Dl q)),
      { refine tangent_map_within_eq_tangent_map U'lq _,
        refine mdifferentiable_at_atlas_symm _ (chart_mem_atlas _ _) _,
        simp only [hq] with mfld_simps },
      rw [this, tangent_map_chart_symm, hDl],
      { simp only [hq] with mfld_simps,
        have : q ∈ (chart_at (model_prod H E) p).source, { simp only [hq] with mfld_simps },
        exact (chart_at (model_prod H E) p).left_inv this },
      { simp only [hq] with mfld_simps } },
    have C : tangent_map_within I I' (r ∘ f) s' q
      = tangent_map_within I' I' r r.source (tangent_map_within I I' f s' q),
    { refine tangent_map_within_comp_at q _ _ (λr hr, _) U'q,
      { apply diff_r.mdifferentiable_on one_le_n,
        simp only [hq] with mfld_simps },
      { apply diff_f.mdifferentiable_on one_le_n,
        simp only [hq] with mfld_simps },
      { simp only [s'] with mfld_simps at hr,
        simp only [hr] with mfld_simps } },
    have D : Dr.symm (ir (tangent_map_within I' I' r r.source (tangent_map_within I I' f s' q)))
      = tangent_map_within I I' f s' q,
    { have A : tangent_map_within I' I' r r.source (tangent_map_within I I' f s' q) =
             tangent_map I' I' r (tangent_map_within I I' f s' q),
      { apply tangent_map_within_eq_tangent_map,
        { apply is_open.unique_mdiff_within_at _ r.open_source, simp [hq] },
        { refine mdifferentiable_at_atlas _ (chart_mem_atlas _ _) _,
          simp only [hq] with mfld_simps } },
      have : f p.proj = (tangent_map_within I I' f s p).1 := rfl,
      rw [A],
      dsimp [r, Dr],
      rw [this, tangent_map_chart],
      { simp only [hq] with mfld_simps,
        have : tangent_map_within I I' f s' q ∈
          (chart_at (model_prod H' E') (tangent_map_within I I' f s p)).source,
            by { simp only [hq] with mfld_simps },
        exact (chart_at (model_prod H' E') (tangent_map_within I I' f s p)).left_inv this },
      { simp only [hq] with mfld_simps } },
    have E : tangent_map_within I I' f s' q = tangent_map_within I I' f s q,
    { refine tangent_map_within_subset (by mfld_set_tac) U'q _,
      apply hf.mdifferentiable_on one_le_n,
      simp only [hq] with mfld_simps },
    simp only [(∘), A, B, C, D, E.symm] },
  exact diff_DrirrflilDl.congr eq_comp,
end

/-- If a function is `C^n` on a domain with unique derivatives, with `1 ≤ n`, then its bundled
derivative is continuous there. -/
theorem cont_mdiff_on.continuous_on_tangent_map_within
  (hf : cont_mdiff_on I I' n f s) (hmn : 1 ≤ n) (hs : unique_mdiff_on I s) :
  continuous_on (tangent_map_within I I' f s) (π (tangent_space I) ⁻¹' s) :=
begin
  have : cont_mdiff_on I.tangent I'.tangent 0 (tangent_map_within I I' f s)
         (π (tangent_space I) ⁻¹' s) :=
    hf.cont_mdiff_on_tangent_map_within hmn hs,
  exact this.continuous_on
end

/-- If a function is `C^n`, then its bundled derivative is `C^m` when `m+1 ≤ n`. -/
theorem cont_mdiff.cont_mdiff_tangent_map
  (hf : cont_mdiff I I' n f) (hmn : m + 1 ≤ n) :
  cont_mdiff I.tangent I'.tangent m (tangent_map I I' f) :=
begin
  rw ← cont_mdiff_on_univ at hf ⊢,
  convert hf.cont_mdiff_on_tangent_map_within hmn unique_mdiff_on_univ,
  rw tangent_map_within_univ
end

/-- If a function is `C^n`, with `1 ≤ n`, then its bundled derivative is continuous. -/
theorem cont_mdiff.continuous_tangent_map
  (hf : cont_mdiff I I' n f) (hmn : 1 ≤ n) :
  continuous (tangent_map I I' f) :=
begin
  rw ← cont_mdiff_on_univ at hf,
  rw continuous_iff_continuous_on_univ,
  convert hf.continuous_on_tangent_map_within hmn unique_mdiff_on_univ,
  rw tangent_map_within_univ
end

end tangent_map

namespace tangent_bundle

include Is
variables (I M)
open bundle

/-- The derivative of the zero section of the tangent bundle maps `⟨x, v⟩` to `⟨⟨x, 0⟩, ⟨v, 0⟩⟩`.

Note that, as currently framed, this is a statement in coordinates, thus reliant on the choice
of the coordinate system we use on the tangent bundle.

However, the result itself is coordinate-dependent only to the extent that the coordinates
determine a splitting of the tangent bundle.  Moreover, there is a canonical splitting at each
point of the zero section (since there is a canonical horizontal space there, the tangent space
to the zero section, in addition to the canonical vertical space which is the kernel of the
derivative of the projection), and this canonical splitting is also the one that comes from the
coordinates on the tangent bundle in our definitions. So this statement is not as crazy as it
may seem.

TODO define splittings of vector bundles; state this result invariantly. -/
lemma tangent_map_tangent_bundle_pure (p : tangent_bundle I M) :
  tangent_map I I.tangent (zero_section (tangent_space I)) p = ⟨⟨p.proj, 0⟩, ⟨p.2, 0⟩⟩ :=
begin
  rcases p with ⟨x, v⟩,
  have N : I.symm ⁻¹' (chart_at H x).target ∈ 𝓝 (I ((chart_at H x) x)),
  { apply is_open.mem_nhds,
    apply (local_homeomorph.open_target _).preimage I.continuous_inv_fun,
    simp only with mfld_simps },
  have A : mdifferentiable_at I I.tangent (λ x, @total_space_mk M (tangent_space I) x 0) x,
  { have : smooth I (I.prod 𝓘(𝕜, E)) (zero_section (tangent_space I : M → Type*)) :=
    bundle.smooth_zero_section 𝕜 (tangent_space I : M → Type*),
    exact this.mdifferentiable_at },
  have B : fderiv_within 𝕜 (λ (x' : E), (x', (0 : E))) (set.range ⇑I) (I ((chart_at H x) x)) v
    = (v, 0),
  { rw [fderiv_within_eq_fderiv, differentiable_at.fderiv_prod],
    { simp },
    { exact differentiable_at_id' },
    { exact differentiable_at_const _ },
    { exact model_with_corners.unique_diff_at_image I },
    { exact differentiable_at_id'.prod (differentiable_at_const _) } },
  simp only [bundle.zero_section, tangent_map, mfderiv, total_space.proj_mk, A,
    if_pos, chart_at, fiber_bundle.charted_space_chart_at, tangent_bundle.trivialization_at_apply,
    tangent_bundle_core, function.comp, continuous_linear_map.map_zero] with mfld_simps,
  rw ← fderiv_within_inter N (I.unique_diff (I ((chart_at H x) x)) (set.mem_range_self _)) at B,
  rw [← fderiv_within_inter N (I.unique_diff (I ((chart_at H x) x)) (set.mem_range_self _)), ← B],
  congr' 2,
  apply fderiv_within_congr _ (λ y hy, _),
  { simp only [prod.mk.inj_iff] with mfld_simps },
  { apply unique_diff_within_at.inter (I.unique_diff _ _) N,
    simp only with mfld_simps },
  { simp only with mfld_simps at hy,
    simp only [hy, prod.mk.inj_iff] with mfld_simps },
end

end tangent_bundle