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

! This file was ported from Lean 3 source module category_theory.products.bifunctor
! leanprover-community/mathlib commit 1ead22342e1a078bd44744ace999f85756555d35
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Products.Basic

/-!
# Lemmas about functors out of product categories.

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

open CategoryTheory

namespace CategoryTheory.Bifunctor

universe v₁ v₂ v₃ u₁ u₂ u₃

variable {C : Type u₁} {D : Type u₂} {E : Type u₃}

variable [Category.{v₁} C] [Category.{v₂} D] [Category.{v₃} E]

/- warning: category_theory.bifunctor.map_id -> CategoryTheory.Bifunctor.map_id is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (Y : D), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X) (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y))) (CategoryTheory.CategoryStruct.id.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)))
but is expected to have type
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u5 u4, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (Y : D), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X) (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y))) (CategoryTheory.CategoryStruct.id.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)))
Case conversion may be inaccurate. Consider using '#align category_theory.bifunctor.map_id CategoryTheory.Bifunctor.map_idₓ'. -/
@[simp]
theorem map_id (F : C × D ⥤ E) (X : C) (Y : D) :
    F.map ((𝟙 X, 𝟙 Y) : (X, Y) ⟶ (X, Y)) = 𝟙 (F.obj (X, Y)) :=
  F.map_id (X, Y)
#align category_theory.bifunctor.map_id CategoryTheory.Bifunctor.map_id

/- warning: category_theory.bifunctor.map_id_comp -> CategoryTheory.Bifunctor.map_id_comp is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (W : C) {X : D} {Y : D} {Z : D} (f : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) X Y) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Z), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W X)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W Z))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W X) (Prod.mk.{u4, u5} C D W Z) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) (CategoryTheory.CategoryStruct.comp.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) Y (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z)) f g))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W X)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W Z)) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W X) (Prod.mk.{u4, u5} C D W Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) f)) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D W Y) (Prod.mk.{u4, u5} C D W Z) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) g)))
but is expected to have type
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u5 u4, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (W : C) {X : D} {Y : D} {Z : D} (f : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) X Y) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Z), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W X)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W Z))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W X) (Prod.mk.{u4, u5} C D W Z) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) (CategoryTheory.CategoryStruct.comp.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) Y (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z)) f g))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W X)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W Z)) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W X) (Prod.mk.{u4, u5} C D W Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W X)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) f)) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D W Y) (Prod.mk.{u4, u5} C D W Z) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D W Z))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) W) g)))
Case conversion may be inaccurate. Consider using '#align category_theory.bifunctor.map_id_comp CategoryTheory.Bifunctor.map_id_compₓ'. -/
@[simp]
theorem map_id_comp (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
    F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
      F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) :=
  by rw [← functor.map_comp, prod_comp, category.comp_id]
#align category_theory.bifunctor.map_id_comp CategoryTheory.Bifunctor.map_id_comp

/- warning: category_theory.bifunctor.map_comp_id -> CategoryTheory.Bifunctor.map_comp_id is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (Y : C) (Z : C) (W : D) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) Y Z), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X W)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D Z W))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X W) (Prod.mk.{u4, u5} C D Z W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (CategoryTheory.CategoryStruct.comp.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) Y (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W)) f g) (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X W)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D Y W)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D Z W)) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X W) (Prod.mk.{u4, u5} C D Y W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D Y W) (Prod.mk.{u4, u5} C D Z W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) g (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))))
but is expected to have type
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u5 u4, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (Y : C) (Z : C) (W : D) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) Y Z), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X W)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D Z W))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X W) (Prod.mk.{u4, u5} C D Z W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (CategoryTheory.CategoryStruct.comp.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) Y (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W)) f g) (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X W)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D Y W)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D Z W)) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X W) (Prod.mk.{u4, u5} C D Y W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D Y W) (Prod.mk.{u4, u5} C D Z W) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Y W)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D Z W))) g (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) W))))
Case conversion may be inaccurate. Consider using '#align category_theory.bifunctor.map_comp_id CategoryTheory.Bifunctor.map_comp_idₓ'. -/
@[simp]
theorem map_comp_id (F : C × D ⥤ E) (X Y Z : C) (W : D) (f : X ⟶ Y) (g : Y ⟶ Z) :
    F.map ((f ≫ g, 𝟙 W) : (X, W) ⟶ (Z, W)) =
      F.map ((f, 𝟙 W) : (X, W) ⟶ (Y, W)) ≫ F.map ((g, 𝟙 W) : (Y, W) ⟶ (Z, W)) :=
  by rw [← functor.map_comp, prod_comp, category.comp_id]
#align category_theory.bifunctor.map_comp_id CategoryTheory.Bifunctor.map_comp_id

/- warning: category_theory.bifunctor.diagonal -> CategoryTheory.Bifunctor.diagonal is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (X' : C) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X X') (Y : D) (Y' : D) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Y'), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y')) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y')) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y'))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X) g)) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y') (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y')) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y')) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y')))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f g))
but is expected to have type
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u5 u4, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (X' : C) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X X') (Y : D) (Y' : D) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Y'), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y')) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y')) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y'))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X) g)) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y') (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y')) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y')) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y')))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f g))
Case conversion may be inaccurate. Consider using '#align category_theory.bifunctor.diagonal CategoryTheory.Bifunctor.diagonalₓ'. -/
@[simp]
theorem diagonal (F : C × D ⥤ E) (X X' : C) (f : X ⟶ X') (Y Y' : D) (g : Y ⟶ Y') :
    F.map ((𝟙 X, g) : (X, Y) ⟶ (X, Y')) ≫ F.map ((f, 𝟙 Y') : (X, Y') ⟶ (X', Y')) =
      F.map ((f, g) : (X, Y) ⟶ (X', Y')) :=
  by rw [← functor.map_comp, prod_comp, category.id_comp, category.comp_id]
#align category_theory.bifunctor.diagonal CategoryTheory.Bifunctor.diagonal

/- warning: category_theory.bifunctor.diagonal' -> CategoryTheory.Bifunctor.diagonal' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (X' : C) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X X') (Y : D) (Y' : D) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Y'), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y)) (CategoryTheory.Functor.obj.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y')) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X' Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X') g))) (CategoryTheory.Functor.map.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f g))
but is expected to have type
  forall {C : Type.{u4}} {D : Type.{u5}} {E : Type.{u6}} [_inst_1 : CategoryTheory.Category.{u1, u4} C] [_inst_2 : CategoryTheory.Category.{u2, u5} D] [_inst_3 : CategoryTheory.Category.{u3, u6} E] (F : CategoryTheory.Functor.{max u1 u2, u3, max u5 u4, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3) (X : C) (X' : C) (f : Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) X X') (Y : D) (Y' : D) (g : Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) Y Y'), Eq.{succ u3} (Quiver.Hom.{succ u3, u6} E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.comp.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y)) (Prefunctor.obj.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y')) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y) (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y))) f (CategoryTheory.CategoryStruct.id.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2) Y))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X' Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (CategoryTheory.CategoryStruct.id.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1) X') g))) (Prefunctor.map.{max (succ u1) (succ u2), succ u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.CategoryStruct.toQuiver.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.Category.toCategoryStruct.{max u1 u2, max u4 u5} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2))) E (CategoryTheory.CategoryStruct.toQuiver.{u3, u6} E (CategoryTheory.Category.toCategoryStruct.{u3, u6} E _inst_3)) (CategoryTheory.Functor.toPrefunctor.{max u1 u2, u3, max u4 u5, u6} (Prod.{u4, u5} C D) (CategoryTheory.prod.{u1, u2, u4, u5} C _inst_1 D _inst_2) E _inst_3 F) (Prod.mk.{u4, u5} C D X Y) (Prod.mk.{u4, u5} C D X' Y') (Prod.mk.{u1, u2} (Quiver.Hom.{succ u1, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u4} C (CategoryTheory.Category.toCategoryStruct.{u1, u4} C _inst_1)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.fst.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) (Quiver.Hom.{succ u2, u5} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u5} D (CategoryTheory.Category.toCategoryStruct.{u2, u5} D _inst_2)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X Y)) (Prod.snd.{u4, u5} C D (Prod.mk.{u4, u5} C D X' Y'))) f g))
Case conversion may be inaccurate. Consider using '#align category_theory.bifunctor.diagonal' CategoryTheory.Bifunctor.diagonal'ₓ'. -/
@[simp]
theorem diagonal' (F : C × D ⥤ E) (X X' : C) (f : X ⟶ X') (Y Y' : D) (g : Y ⟶ Y') :
    F.map ((f, 𝟙 Y) : (X, Y) ⟶ (X', Y)) ≫ F.map ((𝟙 X', g) : (X', Y) ⟶ (X', Y')) =
      F.map ((f, g) : (X, Y) ⟶ (X', Y')) :=
  by rw [← functor.map_comp, prod_comp, category.id_comp, category.comp_id]
#align category_theory.bifunctor.diagonal' CategoryTheory.Bifunctor.diagonal'

end CategoryTheory.Bifunctor


/-

Some goals:
  - Define matroid.
  - Define duality.
  - Define minors, deletion, contraction.
  - Prove that disjoint deletions and contractions commute.
  - Prove that dual of a minor is a minor of the dual.

Some things that are needed:
  - Finite sets, size.
  - Union, intersection, complement of finite subsets.

Paying special attention to:
  - When are things (propositionally) equal.

-/

import tactic.ext
import tactic.ring
import tactic.linarith

-- The API for finite boolean algebras (now moved)

import boolalg
import func_heq

/-

  --(inter_subset_right (X Y : subset) : contained (inter X Y) Y)

lemma boolalg.inter_subset_right {A: boolalg} (X Y : A) : (X ∩ Y) ⊆ Y := sorry
lemma boolalg.subset_inter {A: boolalg} (X Y Z : A) : (Z ⊆ X) → (Z ⊆ Y) → ((X ∩ Y) ⊆ Z) := sorry

def finite_set : Type := sorry
def finite_set.subset : finite_set → Type := sorry
def finite_set.subset.size {γ : finite_set} : γ.subset → ℤ := sorry
instance has_subset_finite_set_subset {γ : finite_set} : has_subset γ.subset := sorry
instance has_inter_finite_set_subset {γ : finite_set} : has_inter γ.subset := sorry
instance has_union_finite_set_subset {γ : finite_set} : has_union γ.subset := sorry
instance has_compl_finite_set_subset {γ : finite_set} : has_compl γ.subset := sorry
instance has_top_finite_set_subset {γ : finite_set} : has_top γ.subset := sorry
instance has_bot_finite_set_subset {γ : finite_set} : has_bot γ.subset := sorry

@[trans] lemma finite_set.subset.trans {γ : finite_set} {X Y Z : γ.subset} :
  (X ⊆ Y) → (Y ⊆ Z) → (X ⊆ Z) := sorry

def finite_set.canonical (size : ℤ) :
  (0 ≤ size) → finite_set := sorry

def finite_set.subset.as_finite_set {γ : finite_set} :
  γ.subset → finite_set := sorry

def finite_set.subset.as_finite_set.injective {γ : finite_set} {X Y : γ.subset} :
  (X.as_finite_set = Y.as_finite_set) → (X = Y) := sorry

def finite_set.subset.embed {γ : finite_set} (X : γ.subset) :
  X.as_finite_set.subset → γ.subset := sorry

def finite_set.subset.restrict {γ : finite_set} (X Y : γ.subset) :
  (X ⊆ Y) → Y.as_finite_set.subset := sorry

lemma finite_set.subset.embed_subset {γ : finite_set} (X : γ.subset) (Y : X.as_finite_set.subset) :
  (X.embed Y) ⊆ X := sorry

lemma finite_set.subset.subset_embed {γ : finite_set} (X : γ.subset) (Y Z : X.as_finite_set.subset) :
  (Y ⊆ Z) → (X.embed Y) ⊆ (X.embed Z) := sorry

lemma finite_set.subset.inter_embed {γ : finite_set} (X : γ.subset) (Y Z : X.as_finite_set.subset) :
  (X.embed (Y ∩ Z)) = (X.embed Y) ∩ (X.embed Z) := sorry

lemma finite_set.subset.union_embed {γ : finite_set} (X : γ.subset) (Y Z : X.as_finite_set.subset) :
  (X.embed (Y ∪ Z)) = (X.embed Y) ∪ (X.embed Z) := sorry

lemma finite_set.subset.embed_size {γ : finite_set} (X : γ.subset) (Y : X.as_finite_set.subset) :
  (X.embed Y).size = Y.size := sorry

lemma finite_set.subset.size_empty {γ : finite_set} :
  (⊥ : γ.subset).size = 0 := sorry

lemma finite_set.subset.size_nonneg {γ : finite_set} (X : γ.subset) :
  0 ≤ X.size := sorry

lemma finite_set.subset.size_monotone {γ : finite_set} (X Y : γ.subset) :
  (X ⊆ Y) → X.size ≤ Y.size := sorry

lemma finite_set.subset.size_modular {γ : finite_set} (X Y : γ.subset) :
  (X ∩ Y).size + (X ∪ Y).size = X.size + Y.size := sorry

lemma finite_set.subset.inter_subset_left {γ : finite_set} (X Y : γ.subset) :
  (X ∩ Y) ⊆ X := sorry

lemma finite_set.subset.inter_subset_right {γ : finite_set} (X Y : γ.subset) :
  (X ∩ Y) ⊆ Y := sorry

lemma finite_set.subset.left_subset_union {γ : finite_set} (X Y : γ.subset) :
  X ⊆ (X ∪ Y) := sorry

lemma finite_set.subset.right_subset_union {γ : finite_set} (X Y : γ.subset) :
  Y ⊆ (X ∪ Y) := sorry

lemma finite_set.subset.inter_compl_self {γ : finite_set} (X : γ.subset) :
  (X ∩ Xᶜ) = ⊥ := sorry

lemma finite_set.subset.union_compl_self {γ : finite_set} (X : γ.subset) :
  (X ∪ Xᶜ) = ⊤ := sorry

lemma finite_set.subset.subset_top {γ : finite_set} (X : γ.subset) :
  X ⊆ ⊤ := calc X ⊆ (X ∪ Xᶜ) : X.left_subset_union Xᶜ ... = ⊤ : X.union_compl

lemma finite_set.subset.compl_inter {γ : finite_set} (X Y : γ.subset) :
  (X ∩ Y)ᶜ = Xᶜ ∪ Yᶜ := sorry

lemma finite_set.subset.compl_union {γ : finite_set} (X Y : γ.subset) :
  (X ∪ Y)ᶜ = Xᶜ ∩ Yᶜ := sorry

lemma finite_set.subset.inter_assoc {γ : finite_set} (X Y Z : γ.subset) :
  (X ∩ Y) ∩ Z = X ∩ (Y ∩ Z) := sorry

lemma finite_set.subset.inter_bot {γ : finite_set} (X : γ.subset) :
  (X ∩ ⊥) = ⊥ := sorry

lemma finite_set.subset.inter_top {γ : finite_set} (X : γ.subset) :
  (X ∩ ⊤) = X := sorry

lemma finite_set.subset.union_distrib_inter_left {γ : finite_set} (X Y Z : γ.subset) :
  (X ∩ Y) ∪ Z = (X ∪ Z) ∩ (Y ∪ Z) := sorry

lemma finite_set.subset.union_distrib_union_left {γ : finite_set} (X Y Z : γ.subset) :
  (X ∪ Y) ∪ Z = (X ∪ Z) ∪ (Y ∪ Z) := sorry

lemma finite_set.subset.inter_eq_left {γ : finite_set} (X Y : γ.subset) :
  (X ⊆ Y) → (X ∩ Y) = X := sorry

lemma finite_set.subset.diff_size {γ : finite_set} (X Y : γ.subset) :
  (X ⊆ Y) → (Xᶜ ∩ Y).size = Y.size - X.size := sorry

lemma finite_set.subset.subset_inter_subset_left {γ : finite_set} (X Y Z : γ.subset) :
  (X ⊆ Y) → (X ∩ Z) ⊆ (Y ∩ Z) := sorry

lemma finite_set.subset.subset_union_subset_left {γ : finite_set} (X Y Z : γ.subset) :
  (X ⊆ Y) → (X ∪ Z) ⊆ (Y ∪ Z) := sorry

lemma finite_set.subset.subset_bot {γ : finite_set} (X : γ.subset) :
  (X ⊆ ⊥) → (X = ⊥) := sorry

end API

-/
namespace boolalg
-- The rank-function definition of a matroid, as a packed structure.
@[ext] structure matroid :=
  (A : boolalg)
  (rank : A → ℤ)

  (R0 : forall (X : A),
    0 ≤ rank X)
  (R1 : forall (X : A),
    rank X ≤ size X)
  (R2 : forall {X Y : A},
    X ⊆ Y → rank X ≤ rank Y)
  (R3 : forall (X Y : A),
    rank (X ∩ Y) + rank (X ∪ Y) ≤ rank X + rank Y)

-- An example: uniform matroids, with rank `k` and size `n`.
def uniform_matroid (k n : ℤ) : (0 ≤ k) → (k ≤ n) → matroid :=
  fun (h0k : 0 ≤ k) (hkn : k ≤ n), let
    A : boolalg := boolalg.canonical n (le_trans h0k hkn)
  in {
    A := A,
    rank := (fun (X : A), min k (size X)),

    R0 := (fun X, le_min h0k (size_nonneg X)),
    R1 := (fun X, min_le_right _ _),
    R2 := (fun X Y (h : X ⊆ Y), le_min (min_le_left k _) (le_trans (min_le_right _ (size X)) (size_monotone h))),
    R3 := (fun X Y, or.elim (le_total k (size X))
      (fun (hkX : k ≤ size X), or.elim (le_total k (size Y))
        (fun (hkY : k ≤ (size Y)), let
          term1 : (min k (size (X ∩ Y)) ) ≤ k := min_le_left _ _,
          term2 : (min k (size (X ∪ Y)) ) ≤ k := min_le_left _ _,
          term3 : (min k (size X)) = k := min_eq_left hkX,
          term4 : (min k (size Y)) = k := min_eq_left hkY
          in by linarith)
        (fun (hYk : (size Y) ≤ k), let
          term1 : (min k (size (X ∩ Y))) ≤ (size Y) := le_trans (min_le_right _ _) (size_monotone (inter_subset_right X Y)),
          term2 : (min k (size (X ∪ Y))) ≤ k := min_le_left _ _,
          term3 : (min k (size X)) = k := min_eq_left hkX,
          term4 : (min k (size Y)) = (size Y) := min_eq_right hYk
          in by linarith))
      (fun (hXk : (size X) ≤ k), or.elim (le_total k (size Y))
        (fun (hkY : k ≤ (size Y)), let
          term1 : (min k (size (X ∩ Y))) ≤ (size X) := le_trans (min_le_right _ _) (size_monotone (inter_subset_left X Y)),
          term2 : (min k (size (X ∪ Y))) ≤ k := min_le_left _ _,
          term3 : (min k (size X)) = (size X) := min_eq_right hXk,
          term4 : (min k (size Y)) = k := min_eq_left hkY
          in by linarith)
        (fun (hYk : size Y ≤ k), let
          term1 : (min k (size (X ∩ Y))) ≤ size (X ∩ Y) := min_le_right _ _,
          term2 : (min k (size (X ∪ Y))) ≤ size (X ∪ Y) := min_le_right _ _,
          term3 : (min k (size X)) = size X := min_eq_right hXk,
          term4 : (min k (size Y)) = size Y := min_eq_right hYk,
          term5 : size (X ∪ Y) + size (X ∩ Y) = size X + size Y := size_modular X Y
          in by linarith))),
  }

-- The empty set always has rank zero.
lemma matroid.rank_empty (M : matroid) :
  M.rank ⊥ = 0
    := le_antisymm (calc M.rank ⊥ ≤ size (⊥ : M.A) : M.R1 ⊥ ... = 0 : size_bot M.A) (M.R0 ⊥)

-- The definition of the dual matroid. R2 is the trickier axiom to prove.
def matroid.dual (M : matroid) : matroid :=
{
  A := M.A,
  rank := (fun (X : M.A), M.rank Xᶜ + (size X) - M.rank ⊤),

  R0 := (fun X, calc
    0   ≤ M.rank Xᶜ + M.rank X - M.rank (X ∪ Xᶜ) - M.rank (X ∩ Xᶜ) : by linarith [M.R3 X Xᶜ]
    ... ≤ M.rank Xᶜ + M.rank X - M.rank ⊤        - M.rank ⊥        : by rw [union_compl_self X, inter_compl_self X]
    ... ≤ M.rank Xᶜ + (size X)  - M.rank ⊤                         : by linarith [M.R1 X, M.rank_empty]),
  R1 := (fun X, by linarith [M.R2 (subset_top Xᶜ)]),
  R2 := (fun X Y (hXY : X ⊆ Y), let
    h₁ : (Xᶜ ∩ Y) ∩ Yᶜ = ⊥ := calc
      (Xᶜ ∩ Y) ∩ Yᶜ = Xᶜ ∩ (Y ∩ Yᶜ) : inter_assoc Xᶜ Y Yᶜ
      ...           = Xᶜ ∩ ⊥        : by rw [inter_compl_self Y]
      ...           = ⊥             : inter_bot Xᶜ,
    h₂ : (Xᶜ ∪ Yᶜ) = Xᶜ := calc
      (Xᶜ ∪ Yᶜ) = (X ∩ Y)ᶜ : (compl_inter X Y).symm
      ...       = Xᶜ       : by rw [inter_subset_mp hXY],
    h₃ : (Xᶜ ∩ Y) ∪ Yᶜ = Xᶜ := calc
      (Xᶜ ∩ Y) ∪ Yᶜ = (Xᶜ ∪ Yᶜ) ∩ (Y ∪ Yᶜ) : union_distrib_right Xᶜ Y Yᶜ --Xᶜ.union_distrib_inter_left Y Yᶜ
      ...           = Xᶜ ∩ ⊤               : by rw [h₂, union_compl_self Y]
      ...           = Xᶜ                   : inter_top Xᶜ,
    h₄ : M.rank Xᶜ ≤ size Y - size X + M.rank Yᶜ := calc
      M.rank Xᶜ = M.rank ⊥ + M.rank Xᶜ                            : by linarith [M.rank_empty]
      ...       = M.rank ((Xᶜ ∩ Y) ∩ Yᶜ) + M.rank ((Xᶜ ∩ Y) ∪ Yᶜ) : by rw [h₁, h₃]
      ...       ≤ M.rank (Xᶜ ∩ Y) + M.rank Yᶜ                     : M.R3 _ _
      ...       ≤ size (Xᶜ ∩ Y) + M.rank Yᶜ                       : by linarith [M.R1 (Xᶜ ∩ Y)]
      ...       = size Y - size X + M.rank Yᶜ                     : by rw [compl_inter_size_subset hXY]
    in by linarith),
  R3 := (fun X Y, calc
      (M.rank (X ∩ Y)ᶜ  + size (X ∩ Y) - M.rank ⊤) + (M.rank (X ∪ Y)ᶜ  + size (X ∪ Y) - M.rank ⊤)
    = (M.rank (Xᶜ ∪ Yᶜ) + size (X ∩ Y) - M.rank ⊤) + (M.rank (Xᶜ ∩ Yᶜ) + size (X ∪ Y) - M.rank ⊤) : by rw [compl_inter X Y, compl_union X Y]
... ≤ (M.rank Xᶜ        + size X       - M.rank ⊤) + (M.rank Yᶜ        + size Y       - M.rank ⊤) : by linarith [M.R3 Xᶜ Yᶜ, size_modular X Y]),
}

lemma rank_bot (M : matroid) :
  M.rank ⊥ = 0 :=
  by linarith[M.R0 ⊥, M.R1 ⊥, size_bot M.A]

lemma dual_dual (M : matroid) : M.dual.dual = M :=
begin
    ext, refl, refl,
    intros X X' hXX',
    apply heq_of_eq, rw ←(eq_of_heq hXX'),

    calc M.dual.dual.rank X = M.dual.rank Xᶜ + size X - M.dual.rank ⊤                                                 : rfl
    ...                     = (M.rank Xᶜᶜ + size Xᶜ - M.rank ⊤) + size X - (M.rank ⊤ᶜ + size (⊤ : M.A) - M.rank ⊤)   : rfl
    ...                     = M.rank Xᶜᶜ + (size X + size Xᶜ - size (⊤ : M.A)) - (M.rank ⊤ᶜ)                          : by linarith
    ...                     = M.rank X + (size (⊤ : M.A) - size (⊤ : M.A)) - M.rank ⊥                                 : by rw [compl_compl, size_compl_sum, compl_top]
    ...                     = M.rank X                                                                                : by linarith [rank_bot M]
end

-- The definition of a minor is weird-looking, but should correctly capture the notion of equality of minors.

/- This definition works in the sense that proofs below are sorry-free, but the problem is now that
it doesn't capture equality correctly; it allows multiple 'versions' of the same minor of M to be
propositionally distinct, both before and after as_matroid is applied. Changing the data in the minor
back to just including ground set and rank function solves this problem, but causes ugliness elsewhere. I
want to be able to insist that the embedding is the inclusion map, but I don't see how to make the inclusion
map canonical in type theory -/

@[ext] structure minor (M : matroid) :=
  (m_A : boolalg)
  (m_rank   : m_A → ℤ)
  (kernel : exists (C : M.A) (emb : boolalg.embedding m_A M.A),
    (emb.func (⊥ : m_A) = ⊥) ∧
    (emb.func (⊤ : m_A) ∩ C = ⊥) ∧
    (forall X, m_rank X = M.rank (emb.func X ∪ C) - M.rank C))

-- A matroid minor is a matroid in its own right.
def minor.as_matroid {M : matroid} (m : minor M) : matroid :=
{
  A := m.m_A,
  rank := m.m_rank,

  R0 := by intros X; rcases m.kernel with ⟨C,emb,⟨h0,hC,hr⟩⟩; linarith [M.R2 (subset_union_right (emb.func X) C), hr X],

  R1 :=
  begin
    intros X, rcases m.kernel with ⟨C,emb,⟨h0,hC,hr⟩⟩,
    linarith [bot_to_bot_embedding_size emb h0 X, M.R0 (emb.func X ∩ C), M.R3 (emb.func X) C, M.R1 (emb.func X), hr X],
  end,
  R2 :=
  begin
    intros X Y hXY, rcases m.kernel with ⟨C,emb,⟨h0,hC,hCr⟩⟩,
    linarith [M.R2 (subset_union_subset_left (emb.func X) (emb.func Y) C (embedding_on_subset emb hXY )), hCr X, hCr Y],
  end,
  R3 :=
  begin
    intros X Y, rcases m.kernel with ⟨C,emb,⟨h0,hC,hCr⟩⟩,
    let f := emb.func,
    have hu : (f X ∪ C) ∪ (f Y ∪ C) = f (X ∪ Y) ∪ C := by rw ←union_distrib_union_left; rw ←emb.on_union,
    have hi : (f X ∪ C) ∩ (f Y ∪ C) = f (X ∩ Y) ∪ C := by rw ←union_distrib_left; rw ←emb.on_inter,
    have hR3 := M.R3 (f X ∪ C) (f Y ∪ C),
    rw [hu, hi] at hR3,
    linarith [hCr X, hCr Y, hCr (X ∪ Y), hCr (X ∩ Y), hR3],
  end,
}

-- Is this possible to prove? Mathematically it should be.

-- Yes, it is! I'm not happy with the proof, though...
lemma minor.as_matroid.injective {M : matroid} (m₁ m₂ : minor M) :
  (m₁.as_matroid = m₂.as_matroid) → m₁ = m₂ :=
  begin
    intros hmm,

    have h : m₁.m_A = m₂.m_A :=
      by calc m₁.m_A = m₁.as_matroid.A : rfl
               ...   = m₂.as_matroid.A : by rw hmm
               ...   = m₂.m_A            : rfl,

    injections_and_clear,
    ext, exact h, rw h, intros a a' haa', apply heq_of_eq,
    apply congr_heq, exact h_2, exact haa',
  end

end boolalg

/-def minor.delete {M : matroid} (m : minor M) (D : M.ground.subset) :
  (D ⊆ m.ground) → (minor M) := fun h, {
    ground := (Dᶜ ∩ m.ground),
    rank := sorry,
    kernel := sorry,
  }

def minor.contract {M : matroid} (m : minor M) (C : M.ground.subset) :
  (C ⊆ m.ground) → (minor M) := fun h, {
    ground := (Cᶜ ∩ m.ground),
    rank := sorry,
    kernel := sorry,
  }-/


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/

/-!
# Extra definitions on `option`

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

This file defines more operations involving `option α`. Lemmas about them are located in other
files under `data.option.`.
Other basic operations on `option` are defined in the core library.
-/

namespace option
variables {α : Type*} {β : Type*}

attribute [inline] option.is_some option.is_none

/-- An elimination principle for `option`. It is a nondependent version of `option.rec`. -/
@[simp] protected def elim (b : β) (f : α → β) : option α → β
| (some a) := f a
| none     := b

instance has_mem : has_mem α (option α) := ⟨λ a b, b = some a⟩

@[simp] theorem mem_def {a : α} {b : option α} : a ∈ b ↔ b = some a :=
iff.rfl

lemma mem_iff {a : α} {b : option α} : a ∈ b ↔ b = a := iff.rfl

theorem is_none_iff_eq_none {o : option α} : o.is_none = tt ↔ o = none :=
⟨option.eq_none_of_is_none, λ e, e.symm ▸ rfl⟩

theorem some_inj {a b : α} : some a = some b ↔ a = b := by simp

lemma mem_some_iff {α : Type*} {a b : α} : a ∈ some b ↔ b = a :=
by simp

/--
`o = none` is decidable even if the wrapped type does not have decidable equality.

This is not an instance because it is not definitionally equal to `option.decidable_eq`.
Try to use `o.is_none` or `o.is_some` instead.
-/
@[inline]
def decidable_eq_none {o : option α} : decidable (o = none) :=
decidable_of_decidable_of_iff (bool.decidable_eq _ _) is_none_iff_eq_none

instance decidable_forall_mem {p : α → Prop} [decidable_pred p] :
  ∀ o : option α, decidable (∀ a ∈ o, p a)
| none     := is_true (by simp [false_implies_iff])
| (some a) := if h : p a
  then is_true $ λ o e, some_inj.1 e ▸ h
  else is_false $ mt (λ H, H _ rfl) h

instance decidable_exists_mem {p : α → Prop} [decidable_pred p] :
  ∀ o : option α, decidable (∃ a ∈ o, p a)
| none     := is_false (λ ⟨a, ⟨h, _⟩⟩, by cases h)
| (some a) := if h : p a
  then is_true $ ⟨_, rfl, h⟩
  else is_false $ λ ⟨_, ⟨rfl, hn⟩⟩, h hn

/-- Inhabited `get` function. Returns `a` if the input is `some a`, otherwise returns `default`. -/
@[reducible] def iget [inhabited α] : option α → α
| (some x) := x
| none     := default

@[simp] theorem iget_some [inhabited α] {a : α} : (some a).iget = a := rfl

/-- `guard p a` returns `some a` if `p a` holds, otherwise `none`. -/
def guard (p : α → Prop) [decidable_pred p] (a : α) : option α :=
if p a then some a else none

/-- `filter p o` returns `some a` if `o` is `some a` and `p a` holds, otherwise `none`. -/
def filter (p : α → Prop) [decidable_pred p] (o : option α) : option α :=
o.bind (guard p)

/-- Cast of `option` to `list `. Returns `[a]` if the input is `some a`, and `[]` if it is
`none`. -/
def to_list : option α → list α
| none     := []
| (some a) := [a]

@[simp] theorem mem_to_list {a : α} {o : option α} : a ∈ to_list o ↔ a ∈ o :=
by cases o; simp [to_list, eq_comm]

/-- Two arguments failsafe function. Returns `f a b` if the inputs are `some a` and `some b`, and
"does nothing" otherwise. -/
def lift_or_get (f : α → α → α) : option α → option α → option α
| none     none     := none
| (some a) none     := some a       -- get a
| none     (some b) := some b       -- get b
| (some a) (some b) := some (f a b) -- lift f

instance lift_or_get_comm (f : α → α → α) [h : is_commutative α f] :
  is_commutative (option α) (lift_or_get f) :=
⟨λ a b, by cases a; cases b; simp [lift_or_get, h.comm]⟩

instance lift_or_get_assoc (f : α → α → α) [h : is_associative α f] :
  is_associative (option α) (lift_or_get f) :=
⟨λ a b c, by cases a; cases b; cases c; simp [lift_or_get, h.assoc]⟩

instance lift_or_get_idem (f : α → α → α) [h : is_idempotent α f] :
  is_idempotent (option α) (lift_or_get f) :=
⟨λ a, by cases a; simp [lift_or_get, h.idempotent]⟩

instance lift_or_get_is_left_id (f : α → α → α) :
  is_left_id (option α) (lift_or_get f) none :=
⟨λ a, by cases a; simp [lift_or_get]⟩

instance lift_or_get_is_right_id (f : α → α → α) :
  is_right_id (option α) (lift_or_get f) none :=
⟨λ a, by cases a; simp [lift_or_get]⟩

/-- Lifts a relation `α → β → Prop` to a relation `option α → option β → Prop` by just adding
`none ~ none`. -/
inductive rel (r : α → β → Prop) : option α → option β → Prop
/-- If `a ~ b`, then `some a ~ some b` -/
| some {a b} : r a b → rel (some a) (some b)
/-- `none ~ none` -/
| none       : rel none none

/-- Partial bind. If for some `x : option α`, `f : Π (a : α), a ∈ x → option β` is a
  partial function defined on `a : α` giving an `option β`, where `some a = x`,
  then `pbind x f h` is essentially the same as `bind x f`
  but is defined only when all `x = some a`, using the proof to apply `f`. -/
@[simp] def pbind : Π (x : option α), (Π (a : α), a ∈ x → option β) → option β
| none     _ := none
| (some a) f := f a rfl

/-- Partial map. If `f : Π a, p a → β` is a partial function defined on `a : α` satisfying `p`,
then `pmap f x h` is essentially the same as `map f x` but is defined only when all members of `x`
satisfy `p`, using the proof to apply `f`. -/
@[simp] def pmap {p : α → Prop} (f : Π (a : α), p a → β) :
  Π x : option α, (∀ a ∈ x, p a) → option β
| none     _ := none
| (some a) H := some (f a (H a (mem_def.mpr rfl)))

/-- Flatten an `option` of `option`, a specialization of `mjoin`. -/
@[simp] def join : option (option α) → option α :=
λ x, bind x id

protected def {u v} traverse {F : Type u → Type v} [applicative F] {α β : Type*} (f : α → F β) :
  option α → F (option β)
| none     := pure none
| (some x) := some <$> f x

/- By analogy with `monad.sequence` in `init/category/combinators.lean`. -/

/-- If you maybe have a monadic computation in a `[monad m]` which produces a term of type `α`, then
there is a naturally associated way to always perform a computation in `m` which maybe produces a
result. -/
def {u v} maybe {m : Type u → Type v} [monad m] {α : Type u} : option (m α) → m (option α)
| none      := return none
| (some fn) := some <$> fn

/-- Map a monadic function `f : α → m β` over an `o : option α`, maybe producing a result. -/
def {u v w} mmap {m : Type u → Type v} [monad m] {α : Type w} {β : Type u} (f : α → m β)
  (o : option α) : m (option β) := (o.map f).maybe

/-- A monadic analogue of `option.elim`. -/
def melim {α β : Type*} {m : Type* → Type*} [monad m] (y : m β) (z : α → m β) (x : m (option α)) :
  m β :=
x >>= option.elim y z

/-- A monadic analogue of `option.get_or_else`. -/
def mget_or_else {α : Type*} {m : Type* → Type*} [monad m] (x : m (option α)) (y : m α) : m α :=
melim y pure x

end option


import data.bitvec
import data.dlist
import data.list.basic

import util.logic
import util.control.applicative
import util.control.monad.non_termination
import separation.heap
import separation.program
import separation.specification
import separation.tactic

universes u v w w'

open nat list function nonterm

variables {α β : Type}

namespace separation

namespace examples

def swap_ptr (p q : pointer) : program unit :=
do t ← alloc1 0,
   copy t p,
   copy p q,
   copy q t,
   free1 t

lemma swap_ptr_spec (p q : pointer) (v₀ v₁ : word)
: sat (swap_ptr p q) { pre := p ↦ v₀ :*: q ↦ v₁
                     , post := λ _, p ↦ v₁ :*: q ↦ v₀ } :=
begin
  unfold swap_ptr,
  bind_step with t h,
  bind_step,
  bind_step,
  bind_step,
  last_step',
end

open program

def map_list : pointer → program unit :=
fix (λ map_list p,
if p = 0
then return ()
else do
  modify_nth p 0 2 (+1),
  p' ← read_nth p 1 2,
  map_list p'.to_ptr)

lemma map_list_def (p : pointer)
: map_list p =
  if p = 0 then return ()
  else do
    modify_nth p 0 2 (+1),
    p' ← read_nth p 1 2,
    map_list p'.to_ptr :=
begin
  unfold map_list,
  -- transitivity,
  admit -- rw [program.fix_unroll,dif_eq_if], refl
end

@[ptr_abstraction]
def is_list : pointer → list word → hprop
  | p [] := [| p = 0 |]
  | p (v :: vs) := [| p ≠ 0 |] :*: ∃∃ nx : word, p ↦* [v,nx] :*: is_list nx.to_ptr vs

lemma map_list_spec (p : pointer) (vs : list word)
: sat (map_list p)
    { pre := is_list p vs
    , post := λ _, is_list p (list.map (+1) vs) } :=
begin
  revert p,
  induction vs ; intros p,
  case nil
  { unfold map is_list,
    rw ← embed_s_and_self,
    extract_context h,
    rw embed_s_and_self,
    rw [map_list_def],
    simp [h],
    apply return.spec' },
  case cons : x xs
  { unfold map is_list,
    s_intros nx Hp_nz,
    rw [map_list_def],
    rw if_neg Hp_nz, simp,
    bind_step,
    bind_step with _ h,
    unfold replace nth_le at *,
    last_step' (vs_ih nx.to_ptr), }
end

def list_reverse_aux : ∀ (p r : pointer),  program pointer :=
fix2 (λ list_reverse_aux p r,
if p = 0 then return r
else do
  p' ← read_nth p 1 2,
  write_nth p 1 2 ⟨ r ⟩,
  list_reverse_aux p'.to_ptr p)

lemma list_reverse_aux_def (p r : pointer)
: list_reverse_aux p r =
  if p = 0 then return r
  else do
    p' ← read_nth p 1 2 dec_trivial,
    write_nth p 1 2 ⟨ r ⟩ dec_trivial,
    list_reverse_aux p'.to_ptr p :=
begin
  unfold list_reverse_aux,
  -- transitivity,
  admit -- rw [program.fix2_unroll], refl
end

lemma list_reverse_aux.spec (p r : pointer) (xs ys : list word)
: sat (list_reverse_aux p r)
      { pre := is_list p xs :*: is_list r ys
      , post := λ r', is_list r' (reverse xs ++ ys) } :=
begin
  revert ys p r,
  induction xs ; intros ys p r,
  case nil
  { simp [is_list],
    extract_context h,
    rw [list_reverse_aux_def,if_pos h],
    last_step', },
  case cons : x xs
  { simp [is_list],
    s_intros nx h,
    rw [list_reverse_aux_def,if_neg h],
    bind_step with p' h',
    bind_step, unfold replace const,
    last_step' (xs_ih (x :: ys)), }
end

def list_reverse (p : pointer) : program pointer :=
list_reverse_aux p 0

lemma list_reverse.spec (p : pointer) (xs : list word)
: sat (list_reverse p)
      { pre := is_list p xs
      , post := λ r, is_list r (reverse xs) } :=
begin
  unfold list_reverse,
  apply precondition (is_list p xs :*: is_list 0 []),
  apply postcondition _ (list_reverse_aux.spec p 0 xs []),
  { intros, simp },
  { simp [is_list,embed_eq_emp], }
end

def list_reverse' (p : pointer) : program pointer :=
sorry

lemma list_reverse_spec' (p : pointer) (vs : list word)
: sat (list_reverse' p) { pre := is_list p vs,
                         post := λ q, is_list q (list.reverse vs) } :=
sorry

def list_reverse_dup_aux : pointer → pointer → program pointer :=
fix2 (λ list_reverse_dup_aux p q,
if p = 0 then return q
else do
  x  ← read_nth p 0 2,
  new  ← alloc [x,⟨ q ⟩],
  next ← read_nth p 1 2,
  list_reverse_dup_aux next.to_ptr new)

lemma list_reverse_dup_aux_def (p q : pointer)
: list_reverse_dup_aux p q =
  if p = 0 then return q
  else do
    x  ← read_nth p 0 2,
    new ← alloc [x,⟨ q ⟩],
    next ← read_nth p 1 2,
    list_reverse_dup_aux next.to_ptr new :=
sorry

def segment : pointer → pointer → list word → hprop
 | p q [] := [| p = q |]
 | p q (x :: xs) := [| p ≠ q |] :*: ∃∃ nx, p ↦* [x,nx] :*: segment nx.to_ptr q xs

lemma segment_nil (p : pointer) (xs : list word)
: segment p 0 xs = is_list p xs :=
sorry

@[ptr_abstraction]
lemma segment_append (p r : pointer) (xs ys : list word)
: segment p r (xs++ys) = ∃∃ q, segment p q xs :*: segment q r ys :=
sorry

@[ptr_abstraction]
lemma segment_single (p q : pointer) (x : word)
: segment p q [x] = p ↦* [x,⟨ q ⟩] :=
sorry

@[ptr_abstraction]
lemma word_to_pointer_eq_self (x : word)
: { word . to_ptr := x.to_ptr } = x := sorry

open tactic
lemma list_reverse_dup_aux_spec (p q r : pointer) (xs ys zs : list word)
: sat (list_reverse_dup_aux q r)
      { pre := segment p q xs :*: is_list q ys :*: is_list r zs,
        post := λ q, is_list p (xs ++ ys) :*: is_list q (list.reverse ys ++ zs) } :=
begin
  revert xs zs q r,
  induction ys ; intros xs zs q r,
  case nil
  { unfold is_list,
    extract_context h,
    rw [list_reverse_dup_aux_def],
    subst q, simp [segment_nil],
    apply postcondition _ (return.spec _ _),
    { intros, dsimp [list.reverse_nil], ac_refl } },
  case cons : y ys
  { unfold is_list,
    extract_context h,
    s_exists nx,
    rw [list_reverse_dup_aux_def,if_neg h],
    bind_step with x  h₀,
    bind_step with new h₁,
    bind_step with next h,
    last_step' ys_ih (xs ++ [x]) (x :: zs), }
end

def list_reverse_dup (p : pointer) : program pointer :=
list_reverse_dup_aux p 0

lemma list_reverse_dup_spec (p : pointer) (vs : list word)
: sat (list_reverse_dup p)
      { pre := is_list p vs,
        post := λ q, is_list p vs :*: is_list q (list.reverse vs) } :=
begin
  unfold list_reverse_dup,
  apply adapt_spec (list_reverse_dup_aux_spec p p 0 [] vs []),
  { simp [segment,is_list,embed_eq_emp] },
  { intro r, simp }
end

inductive tree (α : Type u)
  | leaf {} : tree
  | node : tree → α → tree → tree

@[ptr_abstraction]
def is_tree : pointer → tree word → hprop
  | p tree.leaf := [| p = 0 |]
  | p (tree.node l x r) := ∃∃ lp rp : word,
          [| p ≠ 0 |] :*:
          p ↦* [lp,x,rp] :*:
          is_tree lp.to_ptr l :*:
          is_tree rp.to_ptr r

def free_tree : pointer → program unit :=
fix λ free_tree p, do
if p = 0 then return ()
else do
  l ← read_nth p 0 3,
  r ← read_nth p 2 3,
  free p 3,
  free_tree l.to_ptr,
  free_tree r.to_ptr

lemma free_tree_def (p : pointer)
: free_tree p =
  if p = 0 then return ()
  else do
    l ← read_nth p 0 3,
    r ← read_nth p 2 3,
    free p 3,
    free_tree l.to_ptr,
    free_tree r.to_ptr :=
sorry

lemma free_tree_spec (p : pointer) (t : tree word)
: sat (free_tree p) { pre := is_tree p t
                    , post := λ _, emp } :=
begin
  revert p,
  induction t ; intro p,
  case tree.leaf
  { unfold is_tree,
    rw free_tree_def,
    s_intros h,
    simp [h],
    apply return.spec', },
  case tree.node : l x r
  { unfold is_tree,
    s_intros lp rp h,
    rw [free_tree_def,if_neg h],
    bind_step with l hl,
    bind_step with r hr,
    bind_step,
    bind_step t_ih_a,
    last_step' t_ih_a_1 }
end

end examples

end separation


import algebra.big_operators

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

namespace finset
variables {s s₁ s₂ : finset α} {a : α} {f g : α → β}

section comm_monoid
variables [comm_monoid β]

@[to_additive finset.sum_filter']
lemma prod_filter' [decidable_eq β] (s : finset α) (f : α → β) :
  finset.prod (finset.filter (λ (x : α), f x ≠ 1) s) f = finset.prod s f :=
finset.prod_subset (finset.filter_subset s)
    (λ x hx₁ hx₂, not_not.1 (λ h, hx₂ (finset.mem_filter.2 ⟨hx₁, h⟩)))

end comm_monoid
end finset