l3lab/ntp-mathlib huggingface dataset:
/-- If all the binary fans involved a limit cones, `BinaryFan.assoc` produces another limit cone.
-/
@[simps]
def IsLimit.assoc {X Y Z : C} {sXY : BinaryFan X Y} (P : IsLimit sXY) {sYZ : BinaryFan Y Z}
    (Q : IsLimit sYZ) {s : BinaryFan sXY.pt Z} (R : IsLimit s) : IsLimit (BinaryFan.assoc Q s) where
  lift t := R.lift (BinaryFan.assocInv P t)
  fac t := by
    rintro ⟨⟨⟩⟩ <;> simp
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
  uniq t m w := by
    have h := R.uniq (BinaryFan.assocInv P t) m
    rw [h]
    rintro ⟨⟨⟩⟩ <;> simp
    apply P.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
    · exact w ⟨WalkingPair.left⟩
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
      rw [← w]
      simp
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ X Y Z : C
sXY : BinaryFan X Y
P : IsLimit sXY
sYZ : BinaryFan Y Z
Q : IsLimit sYZ
s : BinaryFan sXY.pt Z
R : IsLimit s
t : Cone (pair X sYZ.pt)
m : t.pt ⟶ (BinaryFan.assoc Q s).pt
h :
  (∀ (j : Discrete WalkingPair), m ≫ s.π.app j = (BinaryFan.assocInv P t).π.app j) → m = lift R (BinaryFan.assocInv P t)
w : m ≫ (BinaryFan.assoc Q s).π.app { as := WalkingPair.right } = t.π.app { as := WalkingPair.right }
⊢ m ≫ BinaryFan.snd s = BinaryFan.snd t ≫ BinaryFan.snd sYZ

Next tactic:
simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
/-- If all the binary fans involved a limit cones, `BinaryFan.assoc` produces another limit cone.
-/
@[simps]
def IsLimit.assoc {X Y Z : C} {sXY : BinaryFan X Y} (P : IsLimit sXY) {sYZ : BinaryFan Y Z}
    (Q : IsLimit sYZ) {s : BinaryFan sXY.pt Z} (R : IsLimit s) : IsLimit (BinaryFan.assoc Q s) where
  lift t := R.lift (BinaryFan.assocInv P t)
  fac t := by
    rintro ⟨⟨⟩⟩ <;> simp
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
  uniq t m w := by
    have h := R.uniq (BinaryFan.assocInv P t) m
    rw [h]
    rintro ⟨⟨⟩⟩ <;> simp
    apply P.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
    · exact w ⟨WalkingPair.left⟩
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
      rw [← w]
      simp
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
      

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ X Y Z : C
sXY : BinaryFan X Y
P : IsLimit sXY
sYZ : BinaryFan Y Z
Q : IsLimit sYZ
s : BinaryFan sXY.pt Z
R : IsLimit s
t : Cone (pair X sYZ.pt)
m : t.pt ⟶ (BinaryFan.assoc Q s).pt
h :
  (∀ (j : Discrete WalkingPair), m ≫ s.π.app j = (BinaryFan.assocInv P t).π.app j) → m = lift R (BinaryFan.assocInv P t)
w : m ≫ lift Q (BinaryFan.mk (BinaryFan.fst s ≫ BinaryFan.snd sXY) (BinaryFan.snd s)) = BinaryFan.snd t
⊢ m ≫ BinaryFan.snd s = BinaryFan.snd t ≫ BinaryFan.snd sYZ

Next tactic:
rw [← w]
/-- If all the binary fans involved a limit cones, `BinaryFan.assoc` produces another limit cone.
-/
@[simps]
def IsLimit.assoc {X Y Z : C} {sXY : BinaryFan X Y} (P : IsLimit sXY) {sYZ : BinaryFan Y Z}
    (Q : IsLimit sYZ) {s : BinaryFan sXY.pt Z} (R : IsLimit s) : IsLimit (BinaryFan.assoc Q s) where
  lift t := R.lift (BinaryFan.assocInv P t)
  fac t := by
    rintro ⟨⟨⟩⟩ <;> simp
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
  uniq t m w := by
    have h := R.uniq (BinaryFan.assocInv P t) m
    rw [h]
    rintro ⟨⟨⟩⟩ <;> simp
    apply P.hom_ext
    rintro ⟨⟨⟩⟩ <;> simp
    · exact w ⟨WalkingPair.left⟩
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
      rw [← w]
      simp
    · specialize w ⟨WalkingPair.right⟩
      simp? at w says
        simp only [pair_obj_right, BinaryFan.π_app_right, BinaryFan.assoc_snd,
          Functor.const_obj_obj, pair_obj_left] at w
      rw [← w]
      

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y✝ X Y Z : C
sXY : BinaryFan X Y
P : IsLimit sXY
sYZ : BinaryFan Y Z
Q : IsLimit sYZ
s : BinaryFan sXY.pt Z
R : IsLimit s
t : Cone (pair X sYZ.pt)
m : t.pt ⟶ (BinaryFan.assoc Q s).pt
h :
  (∀ (j : Discrete WalkingPair), m ≫ s.π.app j = (BinaryFan.assocInv P t).π.app j) → m = lift R (BinaryFan.assocInv P t)
w : m ≫ lift Q (BinaryFan.mk (BinaryFan.fst s ≫ BinaryFan.snd sXY) (BinaryFan.snd s)) = BinaryFan.snd t
⊢ m ≫ BinaryFan.snd s =
    (m ≫ lift Q (BinaryFan.mk (BinaryFan.fst s ≫ BinaryFan.snd sXY) (BinaryFan.snd s))) ≫ BinaryFan.snd sYZ

Next tactic:
simp
/-- Construct a left unitor from specified limit cones.
-/
@[simps]
def BinaryFan.leftUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan s.pt X} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.snd
  inv :=
    Q.lift
      (BinaryFan.mk
        (P.lift
          { pt := X, π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } })
        (𝟙 X))
  -- Porting note: this should be automatable:
  hom_inv_id := by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan s.pt X
Q : IsLimit t
⊢ snd t ≫
      IsLimit.lift Q
        (mk
          (IsLimit.lift P
            { pt := X,
              π :=
                NatTrans.mk fun x =>
                  Discrete.rec
                    (fun x =>
                      PEmpty.rec
                        (fun x =>
                          (x : PEmpty.{v + 1}) →
                            ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                              (Functor.empty C).obj { as := x })
                        x x)
                    x })
          (𝟙 X)) =
    𝟙 t.pt

Next tactic:
apply Q.hom_ext
/-- Construct a left unitor from specified limit cones.
-/
@[simps]
def BinaryFan.leftUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan s.pt X} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.snd
  inv :=
    Q.lift
      (BinaryFan.mk
        (P.lift
          { pt := X, π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } })
        (𝟙 X))
  -- Porting note: this should be automatable:
  hom_inv_id := by
    apply Q.hom_ext
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan s.pt X
Q : IsLimit t
⊢ ∀ (j : Discrete WalkingPair),
    (snd t ≫
          IsLimit.lift Q
            (mk
              (IsLimit.lift P
                { pt := X,
                  π :=
                    NatTrans.mk fun x =>
                      Discrete.rec
                        (fun x =>
                          PEmpty.rec
                            (fun x =>
                              (x : PEmpty.{v + 1}) →
                                ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                  (Functor.empty C).obj { as := x })
                            x x)
                        x })
              (𝟙 X))) ≫
        t.π.app j =
      𝟙 t.pt ≫ t.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
/-- Construct a left unitor from specified limit cones.
-/
@[simps]
def BinaryFan.leftUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan s.pt X} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.snd
  inv :=
    Q.lift
      (BinaryFan.mk
        (P.lift
          { pt := X, π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } })
        (𝟙 X))
  -- Porting note: this should be automatable:
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · 

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan s.pt X
Q : IsLimit t
⊢ (snd t ≫
        IsLimit.lift Q
          (mk
            (IsLimit.lift P
              { pt := X,
                π :=
                  NatTrans.mk fun x =>
                    Discrete.rec
                      (fun x =>
                        PEmpty.rec
                          (fun x =>
                            (x : PEmpty.{v + 1}) →
                              ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                (Functor.empty C).obj { as := x })
                          x x)
                      x })
            (𝟙 X))) ≫
      t.π.app { as := WalkingPair.left } =
    𝟙 t.pt ≫ t.π.app { as := WalkingPair.left }

Next tactic:
apply P.hom_ext
/-- Construct a left unitor from specified limit cones.
-/
@[simps]
def BinaryFan.leftUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan s.pt X} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.snd
  inv :=
    Q.lift
      (BinaryFan.mk
        (P.lift
          { pt := X, π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } })
        (𝟙 X))
  -- Porting note: this should be automatable:
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · apply P.hom_ext
      

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan s.pt X
Q : IsLimit t
⊢ ∀ (j : Discrete PEmpty.{v + 1}),
    ((snd t ≫
            IsLimit.lift Q
              (mk
                (IsLimit.lift P
                  { pt := X,
                    π :=
                      NatTrans.mk fun x =>
                        Discrete.rec
                          (fun x =>
                            PEmpty.rec
                              (fun x =>
                                (x : PEmpty.{v + 1}) →
                                  ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                    (Functor.empty C).obj { as := x })
                              x x)
                          x })
                (𝟙 X))) ≫
          t.π.app { as := WalkingPair.left }) ≫
        s.π.app j =
      (𝟙 t.pt ≫ t.π.app { as := WalkingPair.left }) ≫ s.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
/-- Construct a left unitor from specified limit cones.
-/
@[simps]
def BinaryFan.leftUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan s.pt X} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.snd
  inv :=
    Q.lift
      (BinaryFan.mk
        (P.lift
          { pt := X, π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } })
        (𝟙 X))
  -- Porting note: this should be automatable:
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · apply P.hom_ext
      rintro ⟨⟨⟩⟩
    · 

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan s.pt X
Q : IsLimit t
⊢ (snd t ≫
        IsLimit.lift Q
          (mk
            (IsLimit.lift P
              { pt := X,
                π :=
                  NatTrans.mk fun x =>
                    Discrete.rec
                      (fun x =>
                        PEmpty.rec
                          (fun x =>
                            (x : PEmpty.{v + 1}) →
                              ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                (Functor.empty C).obj { as := x })
                          x x)
                      x })
            (𝟙 X))) ≫
      t.π.app { as := WalkingPair.right } =
    𝟙 t.pt ≫ t.π.app { as := WalkingPair.right }

Next tactic:
simp
/-- Construct a right unitor from specified limit cones.
-/
@[simps]
def BinaryFan.rightUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan X s.pt} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.fst
  inv :=
    Q.lift
      (BinaryFan.mk (𝟙 X)
        (P.lift
          { pt := X
            π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } }))
  hom_inv_id := by
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan X s.pt
Q : IsLimit t
⊢ fst t ≫
      IsLimit.lift Q
        (mk (𝟙 X)
          (IsLimit.lift P
            { pt := X,
              π :=
                NatTrans.mk fun x =>
                  Discrete.rec
                    (fun x =>
                      PEmpty.rec
                        (fun x =>
                          (x : PEmpty.{v + 1}) →
                            ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                              (Functor.empty C).obj { as := x })
                        x x)
                    x })) =
    𝟙 t.pt

Next tactic:
apply Q.hom_ext
/-- Construct a right unitor from specified limit cones.
-/
@[simps]
def BinaryFan.rightUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan X s.pt} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.fst
  inv :=
    Q.lift
      (BinaryFan.mk (𝟙 X)
        (P.lift
          { pt := X
            π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } }))
  hom_inv_id := by
    apply Q.hom_ext
    

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan X s.pt
Q : IsLimit t
⊢ ∀ (j : Discrete WalkingPair),
    (fst t ≫
          IsLimit.lift Q
            (mk (𝟙 X)
              (IsLimit.lift P
                { pt := X,
                  π :=
                    NatTrans.mk fun x =>
                      Discrete.rec
                        (fun x =>
                          PEmpty.rec
                            (fun x =>
                              (x : PEmpty.{v + 1}) →
                                ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                  (Functor.empty C).obj { as := x })
                            x x)
                        x }))) ≫
        t.π.app j =
      𝟙 t.pt ≫ t.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
/-- Construct a right unitor from specified limit cones.
-/
@[simps]
def BinaryFan.rightUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan X s.pt} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.fst
  inv :=
    Q.lift
      (BinaryFan.mk (𝟙 X)
        (P.lift
          { pt := X
            π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } }))
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · 

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan X s.pt
Q : IsLimit t
⊢ (fst t ≫
        IsLimit.lift Q
          (mk (𝟙 X)
            (IsLimit.lift P
              { pt := X,
                π :=
                  NatTrans.mk fun x =>
                    Discrete.rec
                      (fun x =>
                        PEmpty.rec
                          (fun x =>
                            (x : PEmpty.{v + 1}) →
                              ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                (Functor.empty C).obj { as := x })
                          x x)
                      x }))) ≫
      t.π.app { as := WalkingPair.left } =
    𝟙 t.pt ≫ t.π.app { as := WalkingPair.left }

Next tactic:
simp
/-- Construct a right unitor from specified limit cones.
-/
@[simps]
def BinaryFan.rightUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan X s.pt} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.fst
  inv :=
    Q.lift
      (BinaryFan.mk (𝟙 X)
        (P.lift
          { pt := X
            π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } }))
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · simp
    · 

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan X s.pt
Q : IsLimit t
⊢ (fst t ≫
        IsLimit.lift Q
          (mk (𝟙 X)
            (IsLimit.lift P
              { pt := X,
                π :=
                  NatTrans.mk fun x =>
                    Discrete.rec
                      (fun x =>
                        PEmpty.rec
                          (fun x =>
                            (x : PEmpty.{v + 1}) →
                              ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                (Functor.empty C).obj { as := x })
                          x x)
                      x }))) ≫
      t.π.app { as := WalkingPair.right } =
    𝟙 t.pt ≫ t.π.app { as := WalkingPair.right }

Next tactic:
apply P.hom_ext
/-- Construct a right unitor from specified limit cones.
-/
@[simps]
def BinaryFan.rightUnitor {X : C} {s : Cone (Functor.empty.{v} C)} (P : IsLimit s)
    {t : BinaryFan X s.pt} (Q : IsLimit t) : t.pt ≅ X where
  hom := t.fst
  inv :=
    Q.lift
      (BinaryFan.mk (𝟙 X)
        (P.lift
          { pt := X
            π :=
            -- Porting note: there is something fishy here:
            -- `PEmpty.rec x x` should not even typecheck.
            { app := fun x => Discrete.rec (fun x => PEmpty.rec.{_, v+1} x x) x } }))
  hom_inv_id := by
    apply Q.hom_ext
    rintro ⟨⟨⟩⟩
    · simp
    · apply P.hom_ext
      

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X✝ Y X : C
s : Cone (Functor.empty C)
P : IsLimit s
t : BinaryFan X s.pt
Q : IsLimit t
⊢ ∀ (j : Discrete PEmpty.{v + 1}),
    ((fst t ≫
            IsLimit.lift Q
              (mk (𝟙 X)
                (IsLimit.lift P
                  { pt := X,
                    π :=
                      NatTrans.mk fun x =>
                        Discrete.rec
                          (fun x =>
                            PEmpty.rec
                              (fun x =>
                                (x : PEmpty.{v + 1}) →
                                  ((Functor.const (Discrete PEmpty.{v + 1})).obj X).obj { as := x } ⟶
                                    (Functor.empty C).obj { as := x })
                              x x)
                          x }))) ≫
          t.π.app { as := WalkingPair.right }) ≫
        s.π.app j =
      (𝟙 t.pt ≫ t.π.app { as := WalkingPair.right }) ≫ s.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂)

Next tactic:
apply IsLimit.hom_ext (ℬ _ _).isLimit
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ ∀ (j : Discrete WalkingPair),
    tensorHom ℬ (𝟙 X₁) (𝟙 X₂) ≫ (ℬ X₁ X₂).cone.π.app j = 𝟙 (tensorObj ℬ X₁ X₂) ≫ (ℬ X₁ X₂).cone.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  rintro ⟨⟨⟩⟩ <;>
    · 

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ tensorHom ℬ (𝟙 X₁) (𝟙 X₂) ≫ (ℬ X₁ X₂).cone.π.app { as := WalkingPair.left } =
    𝟙 (tensorObj ℬ X₁ X₂) ≫ (ℬ X₁ X₂).cone.π.app { as := WalkingPair.left }

Next tactic:
dsimp [tensorHom]
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  rintro ⟨⟨⟩⟩ <;>
    · dsimp [tensorHom]
      

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ IsLimit.lift (ℬ X₁ X₂).isLimit
        (BinaryFan.mk (BinaryFan.fst (ℬ X₁ X₂).cone ≫ 𝟙 X₁) (BinaryFan.snd (ℬ X₁ X₂).cone ≫ 𝟙 X₂)) ≫
      BinaryFan.fst (ℬ X₁ X₂).cone =
    𝟙 (tensorObj ℬ X₁ X₂) ≫ BinaryFan.fst (ℬ X₁ X₂).cone

Next tactic:
simp
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  rintro ⟨⟨⟩⟩ <;>
    · 

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ tensorHom ℬ (𝟙 X₁) (𝟙 X₂) ≫ (ℬ X₁ X₂).cone.π.app { as := WalkingPair.right } =
    𝟙 (tensorObj ℬ X₁ X₂) ≫ (ℬ X₁ X₂).cone.π.app { as := WalkingPair.right }

Next tactic:
dsimp [tensorHom]
theorem tensor_id (X₁ X₂ : C) : tensorHom ℬ (𝟙 X₁) (𝟙 X₂) = 𝟙 (tensorObj ℬ X₁ X₂) := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  rintro ⟨⟨⟩⟩ <;>
    · dsimp [tensorHom]
      

Proof state:
case mk.right
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ X₂ : C
⊢ IsLimit.lift (ℬ X₁ X₂).isLimit
        (BinaryFan.mk (BinaryFan.fst (ℬ X₁ X₂).cone ≫ 𝟙 X₁) (BinaryFan.snd (ℬ X₁ X₂).cone ≫ 𝟙 X₂)) ≫
      BinaryFan.snd (ℬ X₁ X₂).cone =
    𝟙 (tensorObj ℬ X₁ X₂) ≫ BinaryFan.snd (ℬ X₁ X₂).cone

Next tactic:
simp
theorem tensor_comp {X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂) (g₁ : Y₁ ⟶ Z₁)
    (g₂ : Y₂ ⟶ Z₂) : tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) = tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂ := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₂
g₁ : Y₁ ⟶ Z₁
g₂ : Y₂ ⟶ Z₂
⊢ tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) = tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂

Next tactic:
apply IsLimit.hom_ext (ℬ _ _).isLimit
theorem tensor_comp {X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂) (g₁ : Y₁ ⟶ Z₁)
    (g₂ : Y₂ ⟶ Z₂) : tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) = tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂ := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₂
g₁ : Y₁ ⟶ Z₁
g₂ : Y₂ ⟶ Z₂
⊢ ∀ (j : Discrete WalkingPair),
    tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) ≫ (ℬ Z₁ Z₂).cone.π.app j =
      (tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂) ≫ (ℬ Z₁ Z₂).cone.π.app j

Next tactic:
rintro ⟨⟨⟩⟩
theorem tensor_comp {X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂) (g₁ : Y₁ ⟶ Z₁)
    (g₂ : Y₂ ⟶ Z₂) : tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) = tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂ := by
  apply IsLimit.hom_ext (ℬ _ _).isLimit;
  rintro ⟨⟨⟩⟩ <;>
    · 

Proof state:
case mk.left
C : Type u
inst✝ : Category.{v, u} C
X Y : C
𝒯 : LimitCone (Functor.empty C)
ℬ : (X Y : C) → LimitCone (pair X Y)
X₁ Y₁ Z₁ X₂ Y₂ Z₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₂
g₁ : Y₁ ⟶ Z₁
g₂ : Y₂ ⟶ Z₂
⊢ tensorHom ℬ (f₁ ≫ g₁) (f₂ ≫ g₂) ≫ (ℬ Z₁ Z₂).cone.π.app { as := WalkingPair.left } =
    (tensorHom ℬ f₁ f₂ ≫ tensorHom ℬ g₁ g₂) ≫ (ℬ Z₁ Z₂).cone.π.app { as := WalkingPair.left }

Next tactic:
dsimp [tensorHom]