Theory Category3.Limit

(*  Title:       Limit
    Author:      Eugene W. Stark <stark@cs.stonybrook.edu>, 2016
    Maintainer:  Eugene W. Stark <stark@cs.stonybrook.edu>
*)

chapter Limit

theory Limit
imports FreeCategory DiscreteCategory Adjunction
begin

  text‹
    This theory defines the notion of limit in terms of diagrams and cones and relates
    it to the concept of a representation of a functor.  The diagonal functor associated
    with a diagram shape @{term J} is defined and it is shown that a right adjoint to
    the diagonal functor gives limits of shape @{term J} and that a category has limits
    of shape @{term J} if and only if the diagonal functor is a left adjoint functor.
    Products and equalizers are defined as special cases of limits, and it is shown
    that a category with equalizers has limits of shape @{term J} if it has products
    indexed by the sets of objects and arrows of @{term J}.
    The existence of limits in a set category is investigated, and it is shown that
    every set category has equalizers and that a set category @{term S} has @{term I}-indexed
    products if and only if the universe of @{term S} ``admits @{term I}-indexed tupling.''
    The existence of limits in functor categories is also developed, showing that
    limits in functor categories are ``determined pointwise'' and that a functor category
    @{term "[A, B]"} has limits of shape @{term J} if @{term B} does.
    Finally, it is shown that the Yoneda functor preserves limits.

    This theory concerns itself only with limits; I have made no attempt to consider colimits.
    Although it would be possible to rework the entire development in dual form,
    it is possible that there is a more efficient way to dualize at least parts of it without
    repeating all the work.  This is something that deserves further thought.
›

  section "Representations of Functors"

  text‹
    A representation of a contravariant functor F: Cop → S›, where @{term S}
    is a set category that is the target of a hom-functor for @{term C}, consists of
    an object @{term a} of @{term C} and a natural isomorphism @{term "Φ: Y a  F"},
    where Y: C → [Cop, S]› is the Yoneda functor.
›

  locale representation_of_functor =
    C: category C +
    Cop: dual_category C +
    S: set_category S setp +
    F: "functor" Cop.comp S F +
    Hom: hom_functor C S setp φ +
    Ya: yoneda_functor_fixed_object C S setp φ a +
    natural_isomorphism Cop.comp S Ya.Y a F Φ
  for C :: "'c comp"      (infixr  55)
  and S :: "'s comp"      (infixr S 55)
  and setp :: "'s set  bool"
  and φ :: "'c * 'c  'c  's"
  and F :: "'c  's"
  and a :: 'c
  and Φ :: "'c  's"
  begin

     abbreviation Y where "Y  Ya.Y"
     abbreviation ψ where "ψ  Hom.ψ"

  end

  text‹
    Two representations of the same functor are uniquely isomorphic.
›

  locale two_representations_one_functor =
    C: category C +
    Cop: dual_category C +
    S: set_category S setp +
    F: set_valued_functor Cop.comp S setp F +
    yoneda_functor C S setp φ +
    Ya: yoneda_functor_fixed_object C S setp φ a +
    Ya': yoneda_functor_fixed_object C S setp φ a' +
    Φ: representation_of_functor C S setp φ F a Φ +
    Φ': representation_of_functor C S setp φ F a' Φ'
  for C :: "'c comp"      (infixr  55)
  and S :: "'s comp"      (infixr S 55)
  and setp :: "'s set  bool"
  and F :: "'c  's"
  and φ :: "'c * 'c  'c  's"
  and a :: 'c
  and Φ :: "'c  's"
  and a' :: 'c
  and Φ' :: "'c  's"
  begin

    interpretation Ψ: inverse_transformation Cop.comp S Y a F Φ ..
    interpretation Ψ': inverse_transformation Cop.comp S Y a' F Φ' ..
    interpretation ΦΨ': vertical_composite Cop.comp S Y a F Y a' Φ Ψ'.map ..
    interpretation Φ'Ψ: vertical_composite Cop.comp S Y a' F Y a Φ' Ψ.map ..

    lemma are_uniquely_isomorphic:
      shows "∃!φ. «φ : a  a'»  C.iso φ  map φ = Cop_S.MkArr (Y a) (Y a') ΦΨ'.map"
    proof -
      interpret ΦΨ': natural_isomorphism Cop.comp S Y a Y a' ΦΨ'.map
        using Φ.natural_isomorphism_axioms Ψ'.natural_isomorphism_axioms
              natural_isomorphisms_compose
        by blast
      interpret Φ'Ψ: natural_isomorphism Cop.comp S Y a' Y a Φ'Ψ.map
        using Φ'.natural_isomorphism_axioms Ψ.natural_isomorphism_axioms
              natural_isomorphisms_compose
        by blast
      interpret ΦΨ'_Φ'Ψ: inverse_transformations Cop.comp S Y a Y a' ΦΨ'.map Φ'Ψ.map
      proof
        fix x
        assume X: "Cop.ide x"
        show "S.inverse_arrows (ΦΨ'.map x) (Φ'Ψ.map x)"
          using S.inverse_arrows_compose S.inverse_arrows_sym X Φ'Ψ.map_simp_ide
                ΦΨ'.map_simp_ide Ψ'.inverts_components Ψ.inverts_components
          by force
      qed
      have "Cop_S.inverse_arrows (Cop_S.MkArr (Y a) (Y a') ΦΨ'.map)
                                 (Cop_S.MkArr (Y a') (Y a) Φ'Ψ.map)"
      proof -
        have Ya: "functor Cop.comp S (Y a)" ..
        have Ya': "functor Cop.comp S (Y a')" ..
        have ΦΨ': "natural_transformation Cop.comp S (Y a) (Y a') ΦΨ'.map" ..
        have Φ'Ψ: "natural_transformation Cop.comp S (Y a') (Y a) Φ'Ψ.map" ..
        show ?thesis
          by (metis (no_types, lifting) Cop_S.arr_MkArr Cop_S.comp_MkArr Cop_S.ide_MkIde
              Cop_S.inverse_arrows_def Ya'.functor_axioms Ya.functor_axioms
              Φ'Ψ.natural_transformation_axioms ΦΨ'.natural_transformation_axioms
              ΦΨ'_Φ'Ψ.inverse_transformations_axioms inverse_transformations_inverse(1-2)
              mem_Collect_eq)
      qed
      hence 3: "Cop_S.iso (Cop_S.MkArr (Y a) (Y a') ΦΨ'.map)" using Cop_S.isoI by blast
      hence "f. «f : a  a'»  map f = Cop_S.MkArr (Y a) (Y a') ΦΨ'.map"
        using Ya.ide_a Ya'.ide_a is_full Y_def Cop_S.iso_is_arr full_functor.is_full
              Cop_S.MkArr_in_hom ΦΨ'.natural_transformation_axioms preserves_ide
        by force
      from this obtain φ
        where φ: "«φ : a  a'»  map φ = Cop_S.MkArr (Y a) (Y a') ΦΨ'.map"
        by blast
      show ?thesis
        by (metis 3 C.in_homE φ is_faithful reflects_iso)
    qed

  end

  section "Diagrams and Cones"

  text‹
    A \emph{diagram} in a category @{term C} is a functor D: J → C›.
    We refer to the category @{term J} as the diagram \emph{shape}.
    Note that in the usual expositions of category theory that use set theory
    as their foundations, the shape @{term J} of a diagram is required to be
    a ``small'' category, where smallness means that the collection of objects
    of @{term J}, as well as each of the ``homs,'' is a set.
    However, in HOL there is no class of all sets, so it is not meaningful
    to speak of @{term J} as ``small'' in any kind of absolute sense.
    There is likely a meaningful notion of smallness of @{term J}
    \emph{relative to} @{term C} (the result below that states that a set
    category has @{term I}-indexed products if and only if its universe
    ``admits @{term I}-indexed tuples'' is suggestive of how this might
    be defined), but I haven't fully explored this idea at present.
›

  locale diagram =
    C: category C +
    J: category J +
    "functor" J C D
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  begin

    notation J.in_hom («_ : _ J _»)

  end
 
  lemma comp_diagram_functor:
  assumes "diagram J C D" and "functor J' J F"
  shows "diagram J' C (D o F)"
    by (meson assms(1) assms(2) diagram_def functor.axioms(1) functor_comp)
    
  text‹
    A \emph{cone} over a diagram D: J → C› is a natural transformation
    from a constant functor to @{term D}.  The value of the constant functor is
    the \emph{apex} of the cone.
›

  locale cone =
    C: category C +
    J: category J +
    D: diagram J C D +
    A: constant_functor J C a +
    natural_transformation J C A.map D χ
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and a :: 'c
  and χ :: "'j  'c"
  begin

    lemma ide_apex:
    shows "C.ide a"
      using A.value_is_ide by auto

    lemma component_in_hom:
    assumes "J.arr j"
    shows "«χ j : a  D (J.cod j)»"
      using assms by auto

    (* Suggested by Charles Staats, 12/13/2022 *)
    lemma cod_determines_component:
    assumes "J.arr j"
    shows "χ j = χ (J.cod j)"
      using assms is_natural_2 A.map_simp C.comp_arr_ide ide_apex preserves_reflects_arr
      by metis

  end

  text‹
    A cone over diagram @{term D} is transformed into a cone over diagram @{term "D o F"}
    by pre-composing with @{term F}.
›

  lemma comp_cone_functor:
  assumes "cone J C D a χ" and "functor J' J F"
  shows "cone J' C (D o F) a (χ o F)"
  proof -
    interpret χ: cone J C D a χ using assms(1) by auto
    interpret F: "functor" J' J F using assms(2) by auto
    interpret A': constant_functor J' C a
      using χ.A.value_is_ide by unfold_locales auto
    have 1: "χ.A.map o F = A'.map"
      using χ.A.map_def A'.map_def χ.J.not_arr_null by auto
    interpret χ': natural_transformation J' C A'.map D o F χ o F
      using 1 horizontal_composite F.as_nat_trans.natural_transformation_axioms
            χ.natural_transformation_axioms
      by fastforce
    show "cone J' C (D o F) a (χ o F)" ..
  qed

  text‹
    A cone over diagram @{term D} can be transformed into a cone over a diagram @{term D'}
    by post-composing with a natural transformation from @{term D} to @{term D'}.
›

  lemma vcomp_transformation_cone:
  assumes "cone J C D a χ"
  and "natural_transformation J C D D' τ"
  shows "cone J C D' a (vertical_composite.map J C χ τ)"
    by (meson assms cone.axioms(4-5) cone.intro diagram.intro natural_transformation.axioms(1-4)
              vertical_composite.intro vertical_composite.is_natural_transformation)

  context "functor"
  begin

    lemma preserves_diagrams:
    fixes J :: "'j comp"
    assumes "diagram J A D"
    shows "diagram J B (F o D)"
      by (meson assms diagram_def functor_axioms functor_comp functor_def)

    lemma preserves_cones:
    fixes J :: "'j comp"
    assumes "cone J A D a χ"
    shows "cone J B (F o D) (F a) (F o χ)"
    proof -
      interpret χ: cone J A D a χ using assms by auto
      interpret Fa: constant_functor J B F a
        using χ.ide_apex by unfold_locales auto
      have 1: "F o χ.A.map = Fa.map"
      proof
        fix f
        show "(F  χ.A.map) f = Fa.map f"
          using is_extensional Fa.is_extensional χ.A.is_extensional
          by (cases "χ.J.arr f", simp_all)
      qed
      interpret χ': natural_transformation J B Fa.map F o D F o χ
        using 1 horizontal_composite χ.natural_transformation_axioms
              as_nat_trans.natural_transformation_axioms
        by fastforce
      show "cone J B (F o D) (F a) (F o χ)" ..
    qed

  end

  context diagram
  begin

    abbreviation cone
    where "cone a χ  Limit.cone J C D a χ"

    abbreviation cones :: "'c  ('j  'c) set"
    where "cones a  { χ. cone a χ }"

    text‹
      An arrow @{term "f  C.hom a' a"} induces by composition a transformation from
      cones with apex @{term a} to cones with apex @{term a'}.  This transformation
      is functorial in @{term f}.
›

    abbreviation cones_map :: "'c  ('j  'c)  ('j  'c)"
    where "cones_map f  (λχ  cones (C.cod f). λj. if J.arr j then χ j  f else C.null)"

    lemma cones_map_mapsto:
    assumes "C.arr f"
    shows "cones_map f 
             extensional (cones (C.cod f))  (cones (C.cod f)  cones (C.dom f))"
    proof
      show "cones_map f  extensional (cones (C.cod f))" by blast
      show "cones_map f  cones (C.cod f)  cones (C.dom f)"
      proof
        fix χ
        assume "χ  cones (C.cod f)"
        hence χ: "cone (C.cod f) χ" by auto
        interpret χ: cone J C D C.cod f χ using χ by auto
        interpret B: constant_functor J C C.dom f
          using assms by unfold_locales auto
        have "cone (C.dom f) (λj. if J.arr j then χ j  f else C.null)"
          using assms B.value_is_ide χ.is_natural_1 χ.is_natural_2
          apply (unfold_locales, auto)
          using χ.is_natural_1
           apply (metis C.comp_assoc)
          using χ.is_natural_2 C.comp_arr_dom
          by (metis J.arr_cod_iff_arr J.cod_cod C.comp_assoc)
        thus "(λj. if J.arr j then χ j  f else C.null)  cones (C.dom f)" by auto
      qed
    qed

    lemma cones_map_ide:
    assumes "χ  cones a"
    shows "cones_map a χ = χ"
    proof -
      interpret χ: cone J C D a χ using assms by auto
      show ?thesis
      proof
        fix j
        show "cones_map a χ j = χ j"
          using assms χ.A.value_is_ide χ.preserves_hom C.comp_arr_dom χ.is_extensional
          by (cases "J.arr j", auto)
      qed
    qed

    lemma cones_map_comp:
    assumes "C.seq f g"
    shows "cones_map (f  g) = restrict (cones_map g o cones_map f) (cones (C.cod f))"
    proof (intro restr_eqI)
      show "cones (C.cod (f  g)) = cones (C.cod f)" using assms by simp
      show "χ. χ  cones (C.cod (f  g)) 
                  (λj. if J.arr j then χ j  f  g else C.null) = (cones_map g o cones_map f) χ"
      proof -
        fix χ
        assume χ: "χ  cones (C.cod (f  g))"
        show "(λj. if J.arr j then χ j  f  g else C.null) = (cones_map g o cones_map f) χ"
        proof -
          have "((cones_map g) o (cones_map f)) χ = cones_map g (cones_map f χ)"
            by force
          also have "... = (λj. if J.arr j then
                              (λj. if J.arr j then χ j  f else C.null) j  g else C.null)"
          proof
            fix j
            have "cone (C.dom f) (cones_map f χ)"
              using assms χ cones_map_mapsto by (elim C.seqE, force)
            thus "cones_map g (cones_map f χ) j =
                  (if J.arr j then C (if J.arr j then χ j  f else C.null) g else C.null)"
              using χ assms by auto
          qed
          also have "... = (λj. if J.arr j then χ j  f  g else C.null)"
            using C.comp_assoc by fastforce
          finally show ?thesis by auto
        qed
      qed
    qed

  end

  text‹
    Changing the apex of a cone by pre-composing with an arrow @{term f} commutes
    with changing the diagram of a cone by post-composing with a natural transformation.
›

  lemma cones_map_vcomp:
  assumes "diagram J C D" and "diagram J C D'"
  and "natural_transformation J C D D' τ"
  and "cone J C D a χ"
  and f: "partial_composition.in_hom C f a' a"
  shows "diagram.cones_map J C D' f (vertical_composite.map J C χ τ)
           = vertical_composite.map J C (diagram.cones_map J C D f χ) τ"
  proof -
    interpret D: diagram J C D using assms(1) by auto
    interpret D': diagram J C D' using assms(2) by auto
    interpret τ: natural_transformation J C D D' τ using assms(3) by auto
    interpret χ: cone J C D a χ using assms(4) by auto
    interpret τoχ: vertical_composite J C χ.A.map D D' χ τ ..
    interpret τoχ: cone J C D' a τoχ.map ..
    interpret χf: cone J C D a' D.cones_map f χ
      using f χ.cone_axioms D.cones_map_mapsto by blast
    interpret τoχf: vertical_composite J C χf.A.map D D' D.cones_map f χ τ ..
    interpret τoχ_f: cone J C D' a' D'.cones_map f τoχ.map
      using f τoχ.cone_axioms D'.cones_map_mapsto [of f] by blast
    write C (infixr  55)
    show "D'.cones_map f τoχ.map = τoχf.map"
    proof (intro NaturalTransformation.eqI)
      show "natural_transformation J C χf.A.map D' (D'.cones_map f τoχ.map)" ..
      show "natural_transformation J C χf.A.map D' τoχf.map" ..
      show "j. D.J.ide j  D'.cones_map f τoχ.map j = τoχf.map j"
      proof -
        fix j
        assume j: "D.J.ide j"
        have "D'.cones_map f τoχ.map j = τoχ.map j  f"
          using f τoχ.cone_axioms τoχ.map_simp_2 τoχ.is_extensional by auto
        also have "... = (τ j  χ (D.J.dom j))  f"
          using j τoχ.map_simp_2 by simp
        also have "... = τ j  χ (D.J.dom j)  f"
          using D.C.comp_assoc by simp
        also have "... = τoχf.map j"
          using j f χ.cone_axioms τoχf.map_simp_2 by auto
        finally show "D'.cones_map f τoχ.map j = τoχf.map j" by auto
      qed
    qed
  qed

  text‹
    Given a diagram @{term D}, we can construct a contravariant set-valued functor,
    which takes each object @{term a} of @{term C} to the set of cones over @{term D}
    with apex @{term a}, and takes each arrow @{term f} of @{term C} to the function
    on cones over @{term D} induced by pre-composition with @{term f}.
    For this, we need to introduce a set category @{term S} whose universe is large
    enough to contain all the cones over @{term D}, and we need to have an explicit
    correspondence between cones and elements of the universe of @{term S}.
    A replete set category @{term S} equipped with an injective mapping
    @{term_type "ι :: ('j => 'c) => 's"} serves this purpose.
›
  locale cones_functor =
    C: category C +
    Cop: dual_category C +
    J: category J +
    D: diagram J C D +
    S: replete_concrete_set_category S UNIV ι
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and S :: "'s comp"      (infixr S 55)
  and ι :: "('j  'c)  's"
  begin

    notation S.in_hom     («_ : _ S _»)

    abbreviation 𝗈 where "𝗈  S.DN"

    definition map :: "'c  's"
    where "map = (λf. if C.arr f then
                        S.mkArr (ι ` D.cones (C.cod f)) (ι ` D.cones (C.dom f))
                                (ι o D.cones_map f o 𝗈)
                      else S.null)"

    lemma map_simp [simp]:
    assumes "C.arr f"
    shows "map f = S.mkArr (ι ` D.cones (C.cod f)) (ι ` D.cones (C.dom f))
                           (ι o D.cones_map f o 𝗈)"
      using assms map_def by auto

    lemma arr_map:
    assumes "C.arr f"
    shows "S.arr (map f)"
    proof -
      have "ι o D.cones_map f o 𝗈  ι ` D.cones (C.cod f)  ι ` D.cones (C.dom f)"
        using assms D.cones_map_mapsto by force
      thus ?thesis using assms S.UP_mapsto by auto
    qed

    lemma map_ide:
    assumes "C.ide a"
    shows "map a = S.mkIde (ι ` D.cones a)"
    proof -
      have "map a = S.mkArr (ι ` D.cones a) (ι ` D.cones a) (ι o D.cones_map a o 𝗈)"
        using assms map_simp by force
      also have "... = S.mkArr (ι ` D.cones a) (ι ` D.cones a) (λx. x)"
        using S.UP_mapsto D.cones_map_ide by force
      also have "... = S.mkIde (ι ` D.cones a)"
        using assms S.mkIde_as_mkArr S.UP_mapsto by blast
      finally show ?thesis by auto
    qed

    lemma map_preserves_dom:
    assumes "Cop.arr f"
    shows "map (Cop.dom f) = S.dom (map f)"
      using assms arr_map map_ide by auto

    lemma map_preserves_cod:
    assumes "Cop.arr f"
    shows "map (Cop.cod f) = S.cod (map f)"
      using assms arr_map map_ide by auto

    lemma map_preserves_comp:
    assumes "Cop.seq g f"
    shows "map (g op f) = map g S map f"
    proof -
      have "map (g op f) = S.mkArr (ι ` D.cones (C.cod f)) (ι ` D.cones (C.dom g))
                                   ((ι o D.cones_map g o 𝗈) o (ι o D.cones_map f o 𝗈))"
      proof -
        have 1: "S.arr (map (g op f))"
          using assms arr_map [of "C f g"] by simp
        have "map (g op f) = S.mkArr (ι ` D.cones (C.cod f)) (ι ` D.cones (C.dom g))
                                     (ι o D.cones_map (C f g) o 𝗈)"
          using assms map_simp [of "C f g"] by simp
        also have "... = S.mkArr (ι ` D.cones (C.cod f)) (ι ` D.cones (C.dom g))
                                 ((ι o D.cones_map g o 𝗈) o (ι o D.cones_map f o 𝗈))"
          using assms 1 calculation D.cones_map_mapsto D.cones_map_comp by auto
        finally show ?thesis by blast
      qed
      also have "... = map g S map f"
        using assms arr_map [of f] arr_map [of g] map_simp S.comp_mkArr by auto
      finally show ?thesis by auto
    qed

    lemma is_functor:
    shows "functor Cop.comp S map"
      apply (unfold_locales)
      using map_def arr_map map_preserves_dom map_preserves_cod map_preserves_comp
      by auto
    
  end

  sublocale cones_functor  "functor" Cop.comp S map using is_functor by auto
  sublocale cones_functor  set_valued_functor Cop.comp S λA. A  S.Univ map ..

  section Limits

  subsection "Limit Cones"

  text‹
    A \emph{limit cone} for a diagram @{term D} is a cone @{term χ} over @{term D}
    with the universal property that any other cone @{term χ'} over the diagram @{term D}
    factors uniquely through @{term χ}.
›

  locale limit_cone =
    C: category C +
    J: category J +
    D: diagram J C D +
    cone J C D a χ
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and a :: 'c
  and χ :: "'j  'c" +
  assumes is_universal: "cone J C D a' χ'  ∃!f. «f : a'  a»  D.cones_map f χ = χ'"
  begin

    definition induced_arrow :: "'c  ('j  'c)  'c"
    where "induced_arrow a' χ' = (THE f. «f : a'  a»  D.cones_map f χ = χ')"

    lemma induced_arrowI:
    assumes χ': "χ'  D.cones a'"
    shows "«induced_arrow a' χ' : a'  a»"
    and "D.cones_map (induced_arrow a' χ') χ = χ'"
    proof -
      have "∃!f. «f : a'  a»  D.cones_map f χ = χ'"
        using assms χ' is_universal by simp
      hence 1: "«induced_arrow a' χ' : a'  a»  D.cones_map (induced_arrow a' χ') χ = χ'"
        using theI' [of "λf. «f : a'  a»  D.cones_map f χ = χ'"] induced_arrow_def
        by presburger
      show "«induced_arrow a' χ' : a'  a»" using 1 by simp
      show "D.cones_map (induced_arrow a' χ') χ = χ'" using 1 by simp
    qed

    lemma cones_map_induced_arrow:
    shows "induced_arrow a'  D.cones a'  C.hom a' a"
    and "χ'. χ'  D.cones a'  D.cones_map (induced_arrow a' χ') χ = χ'"
      using induced_arrowI by auto

    lemma induced_arrow_cones_map:
    assumes "C.ide a'"
    shows "(λf. D.cones_map f χ)  C.hom a' a  D.cones a'"
    and "f. «f : a'  a»  induced_arrow a' (D.cones_map f χ) = f"
    proof -
      have a': "C.ide a'" using assms by (simp add: cone.ide_apex)
      have cone_χ: "cone J C D a χ" ..
      show "(λf. D.cones_map f χ)  C.hom a' a  D.cones a'"
        using cone_χ D.cones_map_mapsto by blast
      fix f
      assume f: "«f : a'  a»"
      show "induced_arrow a' (D.cones_map f χ) = f"
      proof -
        have "D.cones_map f χ  D.cones a'"
          using f cone_χ D.cones_map_mapsto by blast
        hence "∃!f'. «f' : a'  a»  D.cones_map f' χ = D.cones_map f χ"
          using assms is_universal by auto
        thus ?thesis
          using f induced_arrow_def
                the1_equality [of "λf'. «f' : a'  a»  D.cones_map f' χ = D.cones_map f χ"]
          by presburger
      qed
    qed

    text‹
      For a limit cone @{term χ} with apex @{term a}, for each object @{term a'} the
      hom-set @{term "C.hom a' a"} is in bijective correspondence with the set of cones
      with apex @{term a'}.
›

    lemma bij_betw_hom_and_cones:
    assumes "C.ide a'"
    shows "bij_betw (λf. D.cones_map f χ) (C.hom a' a) (D.cones a')"
    proof (intro bij_betwI)
      show "(λf. D.cones_map f χ)  C.hom a' a  D.cones a'"
        using assms induced_arrow_cones_map by blast
      show "induced_arrow a'  D.cones a'  C.hom a' a"
        using assms cones_map_induced_arrow by blast
      show "f. f  C.hom a' a  induced_arrow a' (D.cones_map f χ) = f"
        using assms induced_arrow_cones_map by blast
      show "χ'. χ'  D.cones a'  D.cones_map (induced_arrow a' χ') χ = χ'"
        using assms cones_map_induced_arrow by blast
    qed

    lemma induced_arrow_eqI:
    assumes "D.cone a' χ'" and "«f : a'  a»" and "D.cones_map f χ = χ'"
    shows "induced_arrow a' χ' = f"
      using assms is_universal induced_arrow_def
            the1_equality [of "λf. f  C.hom a' a  D.cones_map f χ = χ'" f]
      by simp

    lemma induced_arrow_self:
    shows "induced_arrow a χ = a"
    proof -
      have "«a : a  a»  D.cones_map a χ = χ"
        using ide_apex cone_axioms D.cones_map_ide by force
      thus ?thesis using induced_arrow_eqI cone_axioms by auto
    qed

  end

  context diagram
  begin

    abbreviation limit_cone
    where "limit_cone a χ  Limit.limit_cone J C D a χ"

    text‹
      A diagram @{term D} has object @{term a} as a limit if @{term a} is the apex
      of some limit cone over @{term D}.
›

    abbreviation has_as_limit :: "'c  bool"
    where "has_as_limit a  (χ. limit_cone a χ)"

    abbreviation has_limit
    where "has_limit  (a χ. limit_cone a χ)"

    definition some_limit :: 'c
    where "some_limit = (SOME a. χ. limit_cone a χ)"

    definition some_limit_cone :: "'j  'c"
    where "some_limit_cone = (SOME χ. limit_cone some_limit χ)"

    lemma limit_cone_some_limit_cone:
    assumes has_limit
    shows "limit_cone some_limit some_limit_cone"
    proof -
      have "a. has_as_limit a" using assms by simp
      hence "has_as_limit some_limit"
        using some_limit_def someI_ex [of "λa. χ. limit_cone a χ"] by simp
      thus "limit_cone some_limit some_limit_cone"
        using assms some_limit_cone_def someI_ex [of "λχ. limit_cone some_limit χ"]
        by simp
    qed

    lemma ex_limitE:
    assumes "a. has_as_limit a"
    obtains a χ where "limit_cone a χ"
      using assms someI_ex by blast

  end

  subsection "Limits by Representation"

  text‹
    A limit for a diagram D can also be given by a representation (a, Φ)›
    of the cones functor.
›

  locale representation_of_cones_functor =
    C: category C +
    Cop: dual_category C +
    J: category J +
    D: diagram J C D +
    S: replete_concrete_set_category S UNIV ι +
    Cones: cones_functor J C D S ι +
    Hom: hom_functor C S λA. A  S.Univ φ +
    representation_of_functor C S S.setp φ Cones.map a Φ
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and S :: "'s comp"      (infixr S 55)
  and φ :: "'c * 'c  'c  's"
  and ι :: "('j  'c)  's"
  and a :: 'c
  and Φ :: "'c  's"

  subsection "Putting it all Together"

  text‹
    A ``limit situation'' combines and connects the ways of presenting a limit.
›

  locale limit_situation =
    C: category C +
    Cop: dual_category C +
    J: category J +
    D: diagram J C D +
    S: replete_concrete_set_category S UNIV ι +
    Cones: cones_functor J C D S ι +
    Hom: hom_functor C S S.setp φ +
    Φ: representation_of_functor C S S.setp φ Cones.map a Φ +
    χ: limit_cone J C D a χ
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and S :: "'s comp"      (infixr S 55)
  and φ :: "'c * 'c  'c  's"
  and ι :: "('j  'c)  's"
  and a :: 'c
  and Φ :: "'c  's"
  and χ :: "'j  'c" +
  assumes χ_in_terms_of_Φ: "χ = S.DN (S.Fun (Φ a) (φ (a, a) a))"
  and Φ_in_terms_of_χ:
     "Cop.ide a'  Φ a' = S.mkArr (Hom.set (a', a)) (ι ` D.cones a')
                                    (λx. ι (D.cones_map (Hom.ψ (a', a) x) χ))"

  text (in limit_situation) ‹
    The assumption @{prop χ_in_terms_of_Φ} states that the universal cone @{term χ} is obtained
    by applying the function @{term "S.Fun (Φ a)"} to the identity @{term a} of
    @{term[source=true] C} (after taking into account the necessary coercions).
›

  text (in limit_situation) ‹
    The assumption @{prop Φ_in_terms_of_χ} states that the component of @{term Φ} at @{term a'}
    is the arrow of @{term[source=true] S} corresponding to the function that takes an arrow
    @{term "f  C.hom a' a"} and produces the cone with vertex @{term a'} obtained
    by transforming the universal cone @{term χ} by @{term f}.
›

  subsection "Limit Cones Induce Limit Situations"

  text‹
    To obtain a limit situation from a limit cone, we need to introduce a set category
    that is large enough to contain the hom-sets of @{term C} as well as the cones
    over @{term D}.  We use the category of all @{typ "('c + ('j  'c))"}-sets for this.
›

  context limit_cone
  begin

    interpretation Cop: dual_category C ..
    interpretation CopxC: product_category Cop.comp C ..
    interpretation S: replete_setcat TYPE('c + ('j  'c)) .

    notation S.comp      (infixr S 55)

    interpretation Sr: replete_concrete_set_category S.comp UNIV S.UP o Inr
      apply unfold_locales
      using S.UP_mapsto
       apply auto[1]
      using S.inj_UP inj_Inr inj_compose
      by metis

    interpretation Cones: cones_functor J C D S.comp S.UP o Inr ..

    interpretation Hom: hom_functor C S.comp S.setp λ_. S.UP o Inl
      apply (unfold_locales)
      using S.UP_mapsto
        apply auto[1]
      using S.inj_UP injD inj_onI inj_Inl inj_compose
       apply (metis (no_types, lifting))
      using S.UP_mapsto
      by auto

    interpretation Y: yoneda_functor C S.comp S.setp λ_. S.UP o Inl ..
    interpretation Ya: yoneda_functor_fixed_object C S.comp S.setp λ_. S.UP o Inl a
      apply (unfold_locales) using ide_apex by auto

    abbreviation inl :: "'c  'c + ('j  'c)" where "inl  Inl"
    abbreviation inr :: "('j  'c)  'c + ('j  'c)" where "inr  Inr"
    abbreviation ι where "ι  S.UP o inr"
    abbreviation 𝗈 where "𝗈  Cones.𝗈"
    abbreviation φ where "φ  λ_. S.UP o inl"
    abbreviation ψ where "ψ  Hom.ψ"
    abbreviation Y where "Y  Y.Y"

    lemma Ya_ide:
    assumes a': "C.ide a'"
    shows "Y a a' = S.mkIde (Hom.set (a', a))"
      using assms ide_apex Y.Y_simp Hom.map_ide by simp

    lemma Ya_arr:
    assumes g: "C.arr g"
    shows "Y a g = S.mkArr (Hom.set (C.cod g, a)) (Hom.set (C.dom g, a))
                           (φ (C.dom g, a) o Cop.comp g o ψ (C.cod g, a))"
      using ide_apex g Y.Y_ide_arr [of a g "C.dom g" "C.cod g"] by auto

    lemma is_cone [simp]:
    shows "χ  D.cones a"
      using cone_axioms by simp
    
    text‹
      For each object @{term a'} of @{term[source=true] C} we have a function mapping
      @{term "C.hom a' a"} to the set of cones over @{term D} with apex @{term a'},
      which takes @{term "f  C.hom a' a"} to χf›, where χf› is the cone obtained by
      composing @{term χ} with @{term f} (after accounting for coercions to and from the
      universe of @{term S}).  The corresponding arrows of @{term S} are the
      components of a natural isomorphism from @{term "Y a"} to Cones›.
›

    definition Φo :: "'c  ('c + ('j  'c)) setcat.arr"
    where
      "Φo a' = S.mkArr (Hom.set (a', a)) (ι ` D.cones a') (λx. ι (D.cones_map (ψ (a', a) x) χ))"

    lemma Φo_in_hom:
    assumes a': "C.ide a'"
    shows "«Φo a' : S.mkIde (Hom.set (a', a)) S S.mkIde (ι ` D.cones a')»"
    proof -
      have " «S.mkArr (Hom.set (a', a)) (ι ` D.cones a') (λx. ι (D.cones_map (ψ (a', a) x) χ)) :
                 S.mkIde (Hom.set (a', a)) S S.mkIde (ι ` D.cones a')»"
      proof -
        have "(λx. ι (D.cones_map (ψ (a', a) x) χ))  Hom.set (a', a)  ι ` D.cones a'"
        proof
          fix x
          assume x: "x  Hom.set (a', a)"
          hence "«ψ (a', a) x : a'  a»"
            using ide_apex a' Hom.ψ_mapsto by auto
          hence "D.cones_map (ψ (a', a) x) χ  D.cones a'"
            using ide_apex a' x D.cones_map_mapsto is_cone by force
          thus "ι (D.cones_map (ψ (a', a) x) χ)  ι ` D.cones a'" by simp
        qed
        moreover have "Hom.set (a', a)  S.Univ"
          using ide_apex a' Hom.set_subset_Univ by auto
        moreover have "ι ` D.cones a'  S.Univ"
          using S.UP_mapsto by auto
        ultimately show ?thesis using S.mkArr_in_hom by simp
      qed
      thus ?thesis using Φo_def [of a'] by auto
    qed

    interpretation Φ: transformation_by_components Cop.comp S.comp Y a Cones.map Φo
    proof
      fix a'
      assume A': "Cop.ide a'"
      show "«Φo a' : Y a a' S Cones.map a'»"
        using A' Ya_ide Φo_in_hom Cones.map_ide by auto
      next
      fix g
      assume g: "Cop.arr g"
      show "Φo (Cop.cod g) S Y a g = Cones.map g S Φo (Cop.dom g)"
      proof -
        let ?A = "Hom.set (C.cod g, a)"
        let ?B = "Hom.set (C.dom g, a)"
        let ?B' = "ι ` D.cones (C.cod g)"
        let ?C = "ι ` D.cones (C.dom g)"
        let ?F = "φ (C.dom g, a) o Cop.comp g o ψ (C.cod g, a)"
        let ?F' = "ι o D.cones_map g o 𝗈"
        let ?G = "λx. ι (D.cones_map (ψ (C.dom g, a) x) χ)"
        let ?G' = "λx. ι (D.cones_map (ψ (C.cod g, a) x) χ)"
        have "S.arr (Y a g)  Y a g = S.mkArr ?A ?B ?F"
          using ide_apex g Ya.preserves_arr Ya_arr by fastforce
        moreover have "S.arr (Φo (Cop.cod g))"
          using g Φo_in_hom [of "Cop.cod g"] by auto
        moreover have "Φo (Cop.cod g) = S.mkArr ?B ?C ?G"
          using g Φo_def [of "C.dom g"] by auto
        moreover have "S.seq (Φo (Cop.cod g)) (Y a g)"
          using ide_apex g Φo_in_hom [of "Cop.cod g"] by auto
        ultimately have 1: "S.seq (Φo (Cop.cod g)) (Y a g) 
                            Φo (Cop.cod g) S Y a g = S.mkArr ?A ?C (?G o ?F)"
          using S.comp_mkArr [of ?A ?B ?F ?C ?G] by argo

        have "Cones.map g = S.mkArr (ι ` D.cones (C.cod g)) (ι ` D.cones (C.dom g)) ?F'"
          using g Cones.map_simp by fastforce
        moreover have "Φo (Cop.dom g) = S.mkArr ?A ?B' ?G'"
          using g Φo_def by fastforce
        moreover have "S.seq (Cones.map g) (Φo (Cop.dom g))"
          using g Cones.preserves_hom [of g "C.cod g" "C.dom g"] Φo_in_hom [of "Cop.dom g"]
          by force
        ultimately have
          2: "S.seq (Cones.map g) (Φo (Cop.dom g)) 
              Cones.map g S Φo (Cop.dom g) = S.mkArr ?A ?C (?F' o ?G')"
          using S.seqI' [of "Φo (Cop.dom g)" "Cones.map g"] S.comp_mkArr by auto

        have "Φo (Cop.cod g) S Y a g = S.mkArr ?A ?C (?G o ?F)"
          using 1 by auto
        also have "... = S.mkArr ?A ?C (?F' o ?G')"
        proof (intro S.mkArr_eqI')
          show "S.arr (S.mkArr ?A ?C (?G o ?F))" using 1 by force
          show "x. x  ?A  (?G o ?F) x = (?F' o ?G') x"
          proof -
            fix x
            assume x: "x  ?A"
            hence 1: "«ψ (C.cod g, a) x : C.cod g  a»"
              using ide_apex g Hom.ψ_mapsto [of "C.cod g" a] by auto
            have "(?G o ?F) x = ι (D.cones_map (ψ (C.dom g, a)
                                  (φ (C.dom g, a) (ψ (C.cod g, a) x  g))) χ)"
            proof - (* Why is it so balky with this proof? *)
              have "(?G o ?F) x = ?G (?F x)" by simp
              also have "... = ι (D.cones_map (ψ (C.dom g, a)
                                     (φ (C.dom g, a) (ψ (C.cod g, a) x  g))) χ)"
                by (metis Cop.comp_def comp_apply)
              finally show ?thesis by auto
            qed
            also have "... = ι (D.cones_map (ψ (C.cod g, a) x  g) χ)"
            proof -
              have "«ψ (C.cod g, a) x  g : C.dom g  a»" using g 1 by auto
              thus ?thesis using Hom.ψ_φ by presburger
            qed
            also have "... = ι (D.cones_map g (D.cones_map (ψ (C.cod g, a) x) χ))"
              using g x 1 is_cone D.cones_map_comp [of "ψ (C.cod g, a) x" g] by fastforce
            also have "... = ι (D.cones_map g (𝗈 (ι (D.cones_map (ψ (C.cod g, a) x) χ))))"
              using 1 is_cone D.cones_map_mapsto Sr.DN_UP by auto
            also have "... = (?F' o ?G') x" by simp
            finally show "(?G o ?F) x = (?F' o ?G') x" by auto
          qed
        qed
        also have "... = Cones.map g S Φo (Cop.dom g)"
          using 2 by auto
        finally show ?thesis by auto
      qed
    qed

    interpretation Φ: set_valued_transformation Cop.comp S.comp S.setp
                        Y a Cones.map Φ.map ..
                                            
    interpretation Φ: natural_isomorphism Cop.comp S.comp Y a Cones.map Φ.map
    proof
      fix a'
      assume a': "Cop.ide a'"
      show "S.iso (Φ.map a')"
      proof -
        let ?F = "λx. ι (D.cones_map (ψ (a', a) x) χ)"
        have bij: "bij_betw ?F (Hom.set (a', a)) (ι ` D.cones a')"
        proof -
          have "x x'.  x  Hom.set (a', a); x'  Hom.set (a', a);
                         ι (D.cones_map (ψ (a', a) x) χ) = ι (D.cones_map (ψ (a', a) x') χ) 
                             x = x'"
          proof -
            fix x x'
            assume x: "x  Hom.set (a', a)" and x': "x'  Hom.set (a', a)"
            and xx': "ι (D.cones_map (ψ (a', a) x) χ) = ι (D.cones_map (ψ (a', a) x') χ)"
            have ψx: "«ψ (a', a) x : a'  a»" using x ide_apex a' Hom.ψ_mapsto by auto
            have ψx': "«ψ (a', a) x' : a'  a»" using x' ide_apex a' Hom.ψ_mapsto by auto
            have 1: "∃!f. «f : a'  a»  ι (D.cones_map f χ) = ι (D.cones_map (ψ (a', a) x) χ)"
            proof -
              have "D.cones_map (ψ (a', a) x) χ  D.cones a'"
                using ψx a' is_cone D.cones_map_mapsto by force
              hence 2: "∃!f. «f : a'  a»  D.cones_map f χ = D.cones_map (ψ (a', a) x) χ"
                using a' is_universal by simp
              show "∃!f. «f : a'  a»  ι (D.cones_map f χ) = ι (D.cones_map (ψ (a', a) x) χ)"
              proof -
                have "f. ι (D.cones_map f χ) = ι (D.cones_map (ψ (a', a) x) χ)
                              D.cones_map f χ = D.cones_map (ψ (a', a) x) χ"
                proof -
                  fix f :: 'c
                  have "D.cones_map f χ = D.cones_map (ψ (a', a) x) χ
                            ι (D.cones_map f χ) = ι (D.cones_map (ψ (a', a) x) χ)"
                    by simp
                  thus "(ι (D.cones_map f χ) = ι (D.cones_map (ψ (a', a) x) χ))
                            = (D.cones_map f χ = D.cones_map (ψ (a', a) x) χ)"
                    by (meson Sr.inj_UP injD)
                qed
                thus ?thesis using 2 by auto
              qed
            qed
            have 2: "∃!x''. x''  Hom.set (a', a) 
                            ι (D.cones_map (ψ (a', a) x'') χ) = ι (D.cones_map (ψ (a', a) x) χ)"
            proof -
              from 1 obtain f'' where
                  f'': "«f'' : a'  a»  ι (D.cones_map f'' χ) = ι (D.cones_map (ψ (a', a) x) χ)"
                by blast
              have "φ (a', a) f''  Hom.set (a', a) 
                    ι (D.cones_map (ψ (a', a) (φ (a', a) f'')) χ) = ι (D.cones_map (ψ (a', a) x) χ)"
              proof
                show "φ (a', a) f''  Hom.set (a', a)" using f'' Hom.set_def by auto
                show "ι (D.cones_map (ψ (a', a) (φ (a', a) f'')) χ) =
                         ι (D.cones_map (ψ (a', a) x) χ)"
                  using f'' Hom.ψ_φ by presburger
              qed
              moreover have
                 "x''. x''  Hom.set (a', a) 
                         ι (D.cones_map (ψ (a', a) x'') χ) = ι (D.cones_map (ψ (a', a) x) χ)
                              x'' = φ (a', a) f''"
              proof -
                fix x''
                assume x'': "x''  Hom.set (a', a) 
                             ι (D.cones_map (ψ (a', a) x'') χ) = ι (D.cones_map (ψ (a', a) x) χ)"
                hence "«ψ (a', a) x'' : a'  a» 
                       ι (D.cones_map (ψ (a', a) x'') χ) = ι (D.cones_map (ψ (a', a) x) χ)"
                  using ide_apex a' Hom.set_def Hom.ψ_mapsto [of a' a] by auto
                hence "φ (a', a) (ψ (a', a) x'') = φ (a', a) f''"
                  using 1 f'' by auto
                thus "x'' = φ (a', a) f''"
                  using ide_apex a' x'' Hom.φ_ψ by simp
              qed
              ultimately show ?thesis
                using ex1I [of "λx'. x'  Hom.set (a', a) 
                                     ι (D.cones_map (ψ (a', a) x') χ) =
                                        ι (D.cones_map (ψ (a', a) x) χ)"
                               "φ (a', a) f''"]
                by simp
            qed
            thus "x = x'" using x x' xx' by auto
          qed
          hence "inj_on ?F (Hom.set (a', a))"
            using inj_onI [of "Hom.set (a', a)" ?F] by auto 
          moreover have "?F ` Hom.set (a', a) = ι ` D.cones a'"
          proof
            show "?F ` Hom.set (a', a)  ι ` D.cones a'"
            proof
              fix X'
              assume X': "X'  ?F ` Hom.set (a', a)"
              from this obtain x' where x': "x'  Hom.set (a', a)  ?F x' = X'" by blast
              show "X'  ι ` D.cones a'"
              proof -
                have "X' = ι (D.cones_map (ψ (a', a) x') χ)" using x' by blast
                hence "X' = ι (D.cones_map (ψ (a', a) x') χ)" using x' by force
                moreover have "«ψ (a', a) x' : a'  a»"
                  using ide_apex a' x' Hom.set_def Hom.ψ_φ by auto
                ultimately show ?thesis
                  using x' is_cone D.cones_map_mapsto by force
              qed
            qed
            show "ι ` D.cones a'  ?F ` Hom.set (a', a)"
            proof
              fix X'
              assume X': "X'  ι ` D.cones a'"
              hence "𝗈 X'  𝗈 ` ι ` D.cones a'" by simp
              with Sr.DN_UP have "𝗈 X'  D.cones a'"
                by auto
              hence "∃!f. «f : a'  a»  D.cones_map f χ = 𝗈 X'"
                using a' is_universal by simp
              from this obtain f where "«f : a'  a»  D.cones_map f χ = 𝗈 X'"
                by auto
              hence f: "«f : a'  a»  ι (D.cones_map f χ) = X'"
                using X' Sr.UP_DN by auto
              have "X' = ?F (φ (a', a) f)"
                using f Hom.ψ_φ by presburger
              thus "X'  ?F ` Hom.set (a', a)"
                using f Hom.set_def by force
            qed
          qed
          ultimately show ?thesis
            using bij_betw_def [of ?F "Hom.set (a', a)" "ι ` D.cones a'"] inj_on_def by auto
        qed
        let ?f = "S.mkArr (Hom.set (a', a)) (ι ` D.cones a') ?F"
        have iso: "S.iso ?f"
        proof -
          have "?F  Hom.set (a', a)  ι ` D.cones a'"
            using bij bij_betw_imp_funcset by fast
          hence 1: "S.arr ?f"
            using ide_apex a' Hom.set_subset_Univ S.UP_mapsto by auto
          thus ?thesis using bij S.iso_char S.set_mkIde by fastforce
        qed
        moreover have "?f = Φ.map a'"
          using a' Φo_def by force
        finally show ?thesis by auto
      qed
    qed

    interpretation R: representation_of_functor C S.comp S.setp φ Cones.map a Φ.map ..

    lemma χ_in_terms_of_Φ:
    shows "χ = 𝗈 (Φ.FUN a (φ (a, a) a))"
    proof -
      have "Φ.FUN a (φ (a, a) a) = 
              (λx  Hom.set (a, a). ι (D.cones_map (ψ (a, a) x) χ)) (φ (a, a) a)"
        using ide_apex S.Fun_mkArr Φ.map_simp_ide Φo_def Φ.preserves_reflects_arr [of a]
        by simp
      also have "... = ι (D.cones_map a χ)"
      proof -
        have "(λx  Hom.set (a, a). ι (D.cones_map (ψ (a, a) x) χ)) (φ (a, a) a)
                 = ι (D.cones_map (ψ (a, a) (φ (a, a) a)) χ)"
        proof -
          have "φ (a, a) a  Hom.set (a, a)"
            using ide_apex Hom.φ_mapsto by fastforce
          thus ?thesis
            using restrict_apply' [of "φ (a, a) a" "Hom.set (a, a)"] by blast
        qed
        also have "... = ι (D.cones_map a χ)"
        proof -
          have "ψ (a, a) (φ (a, a) a) = a"
            using ide_apex Hom.ψ_φ [of a a a] by fastforce
          thus ?thesis by metis
        qed
        finally show ?thesis by auto
      qed
      finally have "Φ.FUN a (φ (a, a) a) = ι (D.cones_map a χ)" by auto
      also have "... = ι χ"
        using ide_apex D.cones_map_ide [of χ a] is_cone by simp
      finally have "Φ.FUN a (φ (a, a) a) = ι χ" by blast
      hence "𝗈 (Φ.FUN a (φ (a, a) a)) = 𝗈 (ι χ)" by simp
      thus ?thesis using is_cone Sr.DN_UP by simp
    qed

    abbreviation Hom
    where "Hom  Hom.map"

    abbreviation Φ
    where "Φ  Φ.map"

    lemma induces_limit_situation:
    shows "limit_situation J C D S.comp φ ι a Φ χ"
      using χ_in_terms_of_Φ Φo_def by unfold_locales auto

    no_notation S.comp      (infixr S 55)

  end

  sublocale limit_cone  limit_situation J C D replete_setcat.comp φ ι a Φ χ
    using induces_limit_situation by auto

  subsection "Representations of the Cones Functor Induce Limit Situations"

  context representation_of_cones_functor
  begin

    interpretation Φ: set_valued_transformation Cop.comp S S.setp Y a Cones.map Φ ..
    interpretation Ψ: inverse_transformation Cop.comp S Y a Cones.map Φ ..
    interpretation Ψ: set_valued_transformation Cop.comp S S.setp
                        Cones.map Y a Ψ.map ..

    abbreviation 𝗈
    where "𝗈  Cones.𝗈"

    abbreviation χ
    where "χ  𝗈 (S.Fun (Φ a) (φ (a, a) a))"

    lemma Cones_SET_eq_ι_img_cones:
    assumes "C.ide a'"
    shows "Cones.SET a' = ι ` D.cones a'"
    proof -
      have "ι ` D.cones a'  S.Univ" using S.UP_mapsto by auto
      thus ?thesis using assms Cones.map_ide S.set_mkIde by auto
    qed

    lemma ιχ:
    shows "ι χ = S.Fun (Φ a) (φ (a, a) a)"
    proof -
      have "S.Fun (Φ a) (φ (a, a) a)  Cones.SET a"
        using Ya.ide_a Hom.φ_mapsto S.Fun_mapsto [of "Φ a"] Hom.set_map by fastforce
      thus ?thesis
        using Ya.ide_a Cones_SET_eq_ι_img_cones by auto
    qed

    interpretation χ: cone J C D a χ
    proof -
      have "ι χ  ι ` D.cones a"
        using Ya.ide_a ιχ S.Fun_mapsto [of "Φ a"] Hom.φ_mapsto Hom.set_map
              Cones_SET_eq_ι_img_cones by fastforce
      thus "D.cone a χ"
        by (metis (no_types, lifting) S.DN_UP UNIV_I f_inv_into_f inv_into_into mem_Collect_eq)
    qed

    lemma cone_χ:
    shows "D.cone a χ" ..

    lemma Φ_FUN_simp:
    assumes a': "C.ide a'" and x: "x  Hom.set (a', a)"
    shows "Φ.FUN a' x = Cones.FUN (ψ (a', a) x) (ι χ)"
    proof -
      have ψx: "«ψ (a', a) x : a'  a»"
        using Ya.ide_a a' x Hom.ψ_mapsto by blast
      have φa: "φ (a, a) a  Hom.set (a, a)" using Ya.ide_a Hom.φ_mapsto by fastforce
      have "Φ.FUN a' x = (Φ.FUN a' o Ya.FUN (ψ (a', a) x)) (φ (a, a) a)"
      proof -
        have "φ (a', a) (a  ψ (a', a) x) = x"
          using Ya.ide_a a' x ψx Hom.φ_ψ C.comp_cod_arr by fastforce
        moreover have "S.arr (S.mkArr (Hom.set (a, a)) (Hom.set (a', a))
                             (φ (a', a)  Cop.comp (ψ (a', a) x)  ψ (a, a)))"
          by (metis C.arrI Cop.arr_char Ya.Y_ide_arr(2) Ya.preserves_arr χ.ide_apex ψx)
        ultimately show ?thesis
          using Ya.ide_a a' x Ya.Y_ide_arr ψx φa C.ide_in_hom by auto
      qed
      also have "... = (Cones.FUN (ψ (a', a) x) o Φ.FUN a) (φ (a, a) a)"
      proof -
        have "(Φ.FUN a' o Ya.FUN (ψ (a', a) x)) (φ (a, a) a)
                = S.Fun (Φ a' S Y a (ψ (a', a) x)) (φ (a, a) a)"
          using ψx a' φa Ya.ide_a Ya.map_simp Hom.set_map by (elim C.in_homE, auto)
        also have "... = S.Fun (S (Cones.map (ψ (a', a) x)) (Φ a)) (φ (a, a) a)"
          using ψx is_natural_1 [of "ψ (a', a) x"] is_natural_2 [of "ψ (a', a) x"] by auto
        also have "... = (Cones.FUN (ψ (a', a) x) o Φ.FUN a) (φ (a, a) a)"
        proof -
          have "S.seq (Cones.map (ψ (a', a) x)) (Φ a)"
            using Ya.ide_a ψx Cones.map_preserves_dom [of "ψ (a', a) x"]
            apply (intro S.seqI)
              apply auto[2]
            by fastforce
          thus ?thesis
            using Ya.ide_a φa Hom.set_map by auto
        qed
        finally show ?thesis by simp
      qed
      also have "... = Cones.FUN (ψ (a', a) x) (ι χ)" using ιχ by simp
      finally show ?thesis by auto
    qed

    lemma χ_is_universal:
    assumes "D.cone a' χ'"
    shows "«ψ (a', a) (Ψ.FUN a' (ι χ')) : a'  a»"
    and "D.cones_map (ψ (a', a) (Ψ.FUN a' (ι χ'))) χ = χ'"
    and " «f' : a'  a»; D.cones_map f' χ = χ'   f' = ψ (a', a) (Ψ.FUN a' (ι χ'))"
    proof -
      interpret χ': cone J C D a' χ' using assms by auto
      have a': "C.ide a'" using χ'.ide_apex by simp
      have ιχ': "ι χ'  Cones.SET a'" using assms a' Cones_SET_eq_ι_img_cones by auto
      let ?f = "ψ (a', a) (Ψ.FUN a' (ι χ'))"
      have A: "Ψ.FUN a' (ι χ')  Hom.set (a', a)"
      proof -
        have "Ψ.FUN a'  Cones.SET a'  Ya.SET a'"
          using a' Ψ.preserves_hom [of a' a' a'] S.Fun_mapsto [of "Ψ.map a'"] by fastforce
        thus ?thesis using a' ιχ' Ya.ide_a Hom.set_map by auto
      qed
      show f: "«?f : a'  a»" using A a' Ya.ide_a Hom.ψ_mapsto [of a' a] by auto
      have E: "f. «f : a'  a»  Cones.FUN f (ι χ) = Φ.FUN a' (φ (a', a) f)"
      proof -
        fix f
        assume f: "«f : a'  a»"
        have "φ (a', a) f  Hom.set (a', a)"
          using a' Ya.ide_a f Hom.φ_mapsto by auto
        thus "Cones.FUN f (ι χ) = Φ.FUN a' (φ (a', a) f)"
          using a' f Φ_FUN_simp by simp
      qed
      have I: "Φ.FUN a' (Ψ.FUN a' (ι χ')) = ι χ'"
      proof -
        have "Φ.FUN a' (Ψ.FUN a' (ι χ')) =
              compose (Ψ.DOM a') (Φ.FUN a') (Ψ.FUN a') (ι χ')"
          using a' ιχ' Cones.map_ide Ψ.preserves_hom [of a' a' a'] by force
        also have "... = (λx  Ψ.DOM a'. x) (ι χ')"
          using a' Ψ.inverts_components S.inverse_arrows_char by force
        also have "... = ι χ'"
          using a' ιχ' Cones.map_ide Ψ.preserves_hom [of a' a' a'] by force
        finally show ?thesis by auto
      qed
      show : "D.cones_map ?f χ = χ'"
      proof -
        have "D.cones_map ?f χ = (𝗈 o Cones.FUN ?f o ι) χ"
          using f Cones.preserves_arr [of ?f] cone_χ
          by (cases "D.cone a χ", auto)
        also have "... = χ'"
           using f Ya.ide_a a' A E I by auto
        finally show ?thesis by auto
      qed
      show " «f' : a'  a»; D.cones_map f' χ = χ'   f' = ?f"
      proof -
        assume f': "«f' : a'  a»" and f'χ: "D.cones_map f' χ = χ'"
        show "f' = ?f"
        proof -
          have 1: "φ (a', a) f'  Hom.set (a', a)  φ (a', a) ?f  Hom.set (a', a)"
            using Ya.ide_a a' f f' Hom.φ_mapsto by auto
          have "S.iso (Φ a')" using χ'.ide_apex components_are_iso by auto
          hence 2: "S.arr (Φ a')  bij_betw (Φ.FUN a') (Hom.set (a', a)) (Cones.SET a')"
            using Ya.ide_a a' S.iso_char Hom.set_map by auto
          have "Φ.FUN a' (φ (a', a) f') = Φ.FUN a' (φ (a', a) ?f)"
          proof -
            have "Φ.FUN a' (φ (a', a) ?f) = ι χ'"
              using A I Hom.φ_ψ Ya.ide_a a' by simp
            also have "... = Cones.FUN f' (ι χ)"
              using f f' A E cone_χ Cones.preserves_arr  f'χ by (elim C.in_homE, auto)
            also have "... = Φ.FUN a' (φ (a', a) f')"
              using f' E by simp
            finally show ?thesis by argo
          qed
          moreover have "inj_on (Φ.FUN a') (Hom.set (a', a))"
            using 2 bij_betw_imp_inj_on by blast
          ultimately have 3: "φ (a', a) f' = φ (a', a) ?f"
            using 1 inj_on_def [of "Φ.FUN a'" "Hom.set (a', a)"] by blast
          show ?thesis
          proof -
            have "f' = ψ (a', a) (φ (a', a) f')"
              using Ya.ide_a a' f' Hom.ψ_φ by simp
            also have "... = ψ (a', a) (Ψ.FUN a' (ι χ'))"
              using Ya.ide_a a' Hom.ψ_φ A 3 by simp
            finally show ?thesis by blast
          qed
        qed
      qed
    qed

    interpretation χ: limit_cone J C D a χ
    proof
      show "a' χ'. D.cone a' χ'  ∃!f. «f : a'  a»  D.cones_map f χ = χ'"
      proof -
        fix a' χ'
        assume 1: "D.cone a' χ'"
        show "∃!f. «f : a'  a»  D.cones_map f χ = χ'"
        proof
          show "«ψ (a', a) (Ψ.FUN a' (ι χ')) : a'  a» 
                D.cones_map (ψ (a', a) (Ψ.FUN a' (ι χ'))) χ = χ'"
            using 1 χ_is_universal by blast
          show "f. «f : a'  a»  D.cones_map f χ = χ'  f = ψ (a', a) (Ψ.FUN a' (ι χ'))"
            using 1 χ_is_universal by blast
        qed
      qed
    qed

    lemma χ_is_limit_cone:
    shows "D.limit_cone a χ" ..

    lemma induces_limit_situation:
    shows "limit_situation J C D S φ ι a Φ χ"
    proof
      show "χ = χ" by simp
      fix a'
      assume a': "Cop.ide a'"
      let ?F = "λx. ι (D.cones_map (ψ (a', a) x) χ)"
      show "Φ a' = S.mkArr (Hom.set (a', a)) (ι ` D.cones a') ?F"
      proof -
        have 1: "«Φ a' : S.mkIde (Hom.set (a', a)) S S.mkIde (ι ` D.cones a')»"
          using a' Cones.map_ide Ya.ide_a by auto
        moreover have "Φ.DOM a' = Hom.set (a', a)"
          using 1 Hom.set_subset_Univ a' Ya.ide_a Hom.set_map by simp
        moreover have "Φ.COD a' = ι ` D.cones a'"
          using a' Cones_SET_eq_ι_img_cones by fastforce
        ultimately have 2: "Φ a' = S.mkArr (Hom.set (a', a)) (ι ` D.cones a') (Φ.FUN a')"
          using S.mkArr_Fun [of "Φ a'"] by fastforce
        also have "... = S.mkArr (Hom.set (a', a)) (ι ` D.cones a') ?F"
        proof
          show "S.arr (S.mkArr (Hom.set (a', a)) (ι ` D.cones a') (Φ.FUN a'))"
            using 1 2 by auto
          show "x. x  Hom.set (a', a)  Φ.FUN a' x = ?F x"
          proof -
            fix x
            assume x: "x  Hom.set (a', a)"
            hence ψx: "«ψ (a', a) x : a'  a»"
              using a' Ya.ide_a Hom.ψ_mapsto by auto
            show "Φ.FUN a' x = ?F x"
            proof -
              have "Φ.FUN a' x = Cones.FUN (ψ (a', a) x) (ι χ)"
                using a' x Φ_FUN_simp by simp
              also have "... = restrict (ι o D.cones_map (ψ (a', a) x) o 𝗈) (ι ` D.cones a) (ι χ)"
                using ψx Cones.map_simp Cones.preserves_arr [of "ψ (a', a) x"] S.Fun_mkArr
                by (elim C.in_homE, auto)
              also have "... = ?F x" using cone_χ by simp
              ultimately show ?thesis by simp
            qed
          qed
        qed
        finally show "Φ a' = S.mkArr (Hom.set (a', a)) (ι ` D.cones a') ?F" by auto
      qed
    qed

  end

  sublocale representation_of_cones_functor  limit_situation J C D S φ ι a Φ χ
    using induces_limit_situation by auto

  section "Categories with Limits"

  context category
  begin

    text‹
      A category @{term[source=true] C} has limits of shape @{term J} if every diagram of shape
      @{term J} admits a limit cone.
›

    definition has_limits_of_shape
    where "has_limits_of_shape J  D. diagram J C D  (a χ. limit_cone J C D a χ)"

    text‹
      A category has limits at a type @{typ 'j} if it has limits of shape @{term J}
      for every category @{term J} whose arrows are of type @{typ 'j}.
›

    definition has_limits
    where "has_limits (_ :: 'j)  J :: 'j comp. category J  has_limits_of_shape J"

    text‹
      Whether a category has limits of shape J› truly depends only on the ``shape''
      (\emph{i.e.}~isomorphism class) of J› and not on details of its construction.
    ›

    lemma has_limits_preserved_by_isomorphism:
    assumes "has_limits_of_shape J" and "isomorphic_categories J J'"
    shows "has_limits_of_shape J'"
    proof -
      interpret J: category J
        using assms(2) isomorphic_categories_def isomorphic_categories_axioms_def by auto
      interpret J': category J'
        using assms(2) isomorphic_categories_def isomorphic_categories_axioms_def by auto
      from assms(2) obtain φ ψ where IF: "inverse_functors J' J φ ψ"
        using isomorphic_categories_def isomorphic_categories_axioms_def
              inverse_functors_sym
        by blast
      interpret IF: inverse_functors J' J φ ψ using IF by auto
      have ψφ: "ψ o φ = J.map" using IF.inv by metis
      have φψ: "φ o ψ = J'.map" using IF.inv' by metis
      have "D'. diagram J' C D'  a χ. limit_cone J' C D' a χ"
      proof -
        fix D'
        assume D': "diagram J' C D'"
        interpret D': diagram J' C D' using D' by auto
        interpret D: composite_functor J J' C φ D' ..
        interpret D: diagram J C D' o φ ..
        have D: "diagram J C (D' o φ)" ..
        from assms(1) obtain a χ where χ: "D.limit_cone a χ"
          using D has_limits_of_shape_def by blast
        interpret χ: limit_cone J C D' o φ a χ using χ by auto
        interpret A': constant_functor J' C a
          using χ.ide_apex by (unfold_locales, auto)
        have χoψ: "cone J' C (D' o φ o ψ) a (χ o ψ)"
          using comp_cone_functor IF.G.functor_axioms χ.cone_axioms by fastforce
        hence χoψ: "cone J' C D' a (χ o ψ)"
          using φψ by (metis D'.functor_axioms Fun.comp_assoc comp_functor_identity)
        interpret χoψ: cone J' C D' a χ o ψ using χoψ by auto
        interpret χoψ: limit_cone J' C D' a χ o ψ
        proof
          fix a' χ'
          assume χ': "D'.cone a' χ'"
          interpret χ': cone J' C D' a' χ' using χ' by auto
          have χ'oφ: "cone J C (D' o φ) a' (χ' o φ)"
            using χ' comp_cone_functor IF.F.functor_axioms by fastforce
          interpret χ'oφ: cone J C D' o φ a' χ' o φ using χ'oφ by auto
          have "cone J C (D' o φ) a' (χ' o φ)" ..
          hence 1: "∃!f. «f : a'  a»  D.cones_map f χ = χ' o φ"
            using χ.is_universal by simp
          show "∃!f. «f : a'  a»  D'.cones_map f (χ o ψ) = χ'"
          proof
            let ?f = "THE f. «f : a'  a»  D.cones_map f χ = χ' o φ"
            have f: "«?f : a'  a»  D.cones_map ?f χ = χ' o φ"
              using 1 theI' [of "λf. «f : a'  a»  D.cones_map f χ = χ' o φ"] by blast
            have f_in_hom: "«?f : a'  a»" using f by blast
            have "D'.cones_map ?f (χ o ψ) = χ'"
            proof
              fix j'
              have "¬J'.arr j'  D'.cones_map ?f (χ o ψ) j' = χ' j'"
              proof -
                assume j': "¬J'.arr j'"
                have "D'.cones_map ?f (χ o ψ) j' = null"
                  using j' f_in_hom χoψ by fastforce
                thus ?thesis
                  using j' χ'.is_extensional by simp
              qed
              moreover have "J'.arr j'  D'.cones_map ?f (χ o ψ) j' = χ' j'"
              proof -
                assume j': "J'.arr j'"
                have "D'.cones_map ?f (χ o ψ) j' = χ (ψ j')  ?f"
                  using j' f χoψ by fastforce
                also have "... = D.cones_map ?f χ (ψ j')"
                  using j' f_in_hom χ χ.is_cone by fastforce
                also have "... = χ' j'"
                  using j' f χ φψ Fun.comp_def J'.map_simp by metis
                finally show "D'.cones_map ?f (χ o ψ) j' = χ' j'" by auto
              qed
              ultimately show "D'.cones_map ?f (χ o ψ) j' = χ' j'" by blast
            qed
            thus "«?f : a'  a»  D'.cones_map ?f (χ o ψ) = χ'" using f by auto
            fix f'
            assume f': "«f' : a'  a»  D'.cones_map f' (χ o ψ) = χ'"
            have "D.cones_map f' χ = χ' o φ"
            proof
              fix j
              have "¬J.arr j  D.cones_map f' χ j = (χ' o φ) j"
                using f' χ χ'oφ.is_extensional χ.is_cone mem_Collect_eq restrict_apply by auto
              moreover have "J.arr j  D.cones_map f' χ j = (χ' o φ) j"
              proof -
                assume j: "J.arr j"
                have "D.cones_map f' χ j = C (χ j) f'"
                  using j f' χ.is_cone by auto
                also have "... = C ((χ o ψ) (φ j)) f'"
                  using j f' ψφ by (metis comp_apply J.map_simp)
                also have "... = D'.cones_map f' (χ o ψ) (φ j)"
                  using j f' χoψ by fastforce
                also have "... = (χ' o φ) j"
                  using j f' by auto
                finally show "D.cones_map f' χ j = (χ' o φ) j" by auto
              qed
              ultimately show "D.cones_map f' χ j = (χ' o φ) j" by blast
            qed
            hence "«f' : a'  a»  D.cones_map f' χ = χ' o φ"
              using f' by auto
            moreover have "P x x'. (∃!x. P x)  P x  P x'  x = x'"
              by auto
            ultimately show "f' = ?f" using 1 f by blast
          qed
        qed
        have "limit_cone J' C D' a (χ o ψ)" ..
        thus "a χ. limit_cone J' C D' a χ" by blast
      qed
      thus ?thesis using has_limits_of_shape_def by auto
    qed

  end

  subsection "Diagonal Functors"

  text‹
    The existence of limits can also be expressed in terms of adjunctions: a category @{term C}
    has limits of shape @{term J} if the diagonal functor taking each object @{term a}
    in @{term C} to the constant-@{term a} diagram and each arrow f ∈ C.hom a a'›
    to the constant-@{term f} natural transformation between diagrams is a left adjoint functor.
›

  locale diagonal_functor =
    C: category C +
    J: category J +
    J_C: functor_category J C
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  begin

    notation J.in_hom     («_ : _ J _»)
    notation J_C.comp     (infixr [J,C] 55)
    notation J_C.in_hom   («_ : _ [J,C] _»)

    definition map :: "'c  ('j, 'c) J_C.arr"
    where "map f = (if C.arr f then J_C.MkArr (constant_functor.map J C (C.dom f))
                                              (constant_functor.map J C (C.cod f))
                                              (constant_transformation.map J C f)
                               else J_C.null)"

    lemma is_functor:
    shows "functor C J_C.comp map"
    proof
      fix f
      show "¬ C.arr f  local.map f = J_C.null"
        using map_def by simp
      assume f: "C.arr f"
      interpret Dom_f: constant_functor J C C.dom f
        using f by (unfold_locales, auto)
      interpret Cod_f: constant_functor J C C.cod f
        using f by (unfold_locales, auto)
      interpret Fun_f: constant_transformation J C f
        using f by (unfold_locales, auto)
      show 1: "J_C.arr (map f)"
        using f map_def by (simp add: Fun_f.natural_transformation_axioms)
      show "J_C.dom (map f) = map (C.dom f)"
      proof -
        have "constant_transformation J C (C.dom f)"
          using f by unfold_locales auto
        hence "constant_transformation.map J C (C.dom f) = Dom_f.map"
          using Dom_f.map_def constant_transformation.map_def [of J C "C.dom f"] by auto
        thus ?thesis using f 1 by (simp add: map_def J_C.dom_char)
      qed
      show "J_C.cod (map f) = map (C.cod f)"
      proof -
        have "constant_transformation J C (C.cod f)"
          using f by unfold_locales auto
        hence "constant_transformation.map J C (C.cod f) = Cod_f.map"
          using Cod_f.map_def constant_transformation.map_def [of J C "C.cod f"] by auto
        thus ?thesis using f 1 by (simp add: map_def J_C.cod_char)
      qed
      next
      fix f g
      assume g: "C.seq g f"
      have f: "C.arr f" using g by auto
      interpret Dom_f: constant_functor J C C.dom f
        using f by unfold_locales auto
      interpret Cod_f: constant_functor J C C.cod f
        using f by unfold_locales auto
      interpret Fun_f: constant_transformation J C f
        using f by unfold_locales auto
      interpret Cod_g: constant_functor J C C.cod g
        using g by unfold_locales auto
      interpret Fun_g: constant_transformation J C g
        using g by unfold_locales auto
      interpret Fun_g: natural_transformation J C Cod_f.map Cod_g.map Fun_g.map
        apply unfold_locales
        using f g C.seqE [of g f] C.comp_arr_dom C.comp_cod_arr Fun_g.is_extensional by auto
      interpret Fun_fg: vertical_composite
                          J C Dom_f.map Cod_f.map Cod_g.map Fun_f.map Fun_g.map ..
      have 1: "J_C.arr (map f)"
        using f map_def by (simp add: Fun_f.natural_transformation_axioms)
      show "map (g  f) = map g [J,C] map f"
      proof -
        have "map (C g f) = J_C.MkArr Dom_f.map Cod_g.map
                                      (constant_transformation.map J C (C g f))"
          using f g map_def by simp
        also have "... = J_C.MkArr Dom_f.map Cod_g.map (λj. if J.arr j then C g f else C.null)"
        proof -
          have "constant_transformation J C (g  f)"
            using g by unfold_locales auto
          thus ?thesis using constant_transformation.map_def by metis
        qed
        also have "... = J_C.comp (J_C.MkArr Cod_f.map Cod_g.map Fun_g.map)
                                  (J_C.MkArr Dom_f.map Cod_f.map Fun_f.map)"
        proof -
          have "J_C.MkArr Cod_f.map Cod_g.map Fun_g.map [J,C]
                J_C.MkArr Dom_f.map Cod_f.map Fun_f.map
                  = J_C.MkArr Dom_f.map Cod_g.map Fun_fg.map"
            using J_C.comp_char J_C.comp_MkArr Fun_f.natural_transformation_axioms
                  Fun_g.natural_transformation_axioms
            by blast
          also have "... = J_C.MkArr Dom_f.map Cod_g.map
                                     (λj. if J.arr j then g  f else C.null)"
            using Fun_fg.is_extensional Fun_fg.map_simp_2 by auto
          finally show ?thesis by auto
        qed
        also have "... = map g [J,C] map f"
          using f g map_def by fastforce
        finally show ?thesis by auto
      qed
    qed

    sublocale "functor" C J_C.comp map
      using is_functor by auto

    text‹
      The objects of [J, C]› correspond bijectively to diagrams of shape @{term J}
      in @{term C}.
›

    lemma ide_determines_diagram:
    assumes "J_C.ide d"
    shows "diagram J C (J_C.Map d)" and "J_C.MkIde (J_C.Map d) = d"
    proof -
      interpret δ: natural_transformation J C J_C.Map d J_C.Map d J_C.Map d
        using assms J_C.ide_char J_C.arr_MkArr by fastforce
      interpret D: "functor" J C J_C.Map d ..
      show "diagram J C (J_C.Map d)" ..
      show "J_C.MkIde (J_C.Map d) = d"
        using assms J_C.ide_char by (metis J_C.ideD(1) J_C.MkArr_Map)
    qed

    lemma diagram_determines_ide:
    assumes "diagram J C D"
    shows "J_C.ide (J_C.MkIde D)" and "J_C.Map (J_C.MkIde D) = D"
    proof -
      interpret D: diagram J C D using assms by auto
      show "J_C.ide (J_C.MkIde D)" using J_C.ide_char
        using D.functor_axioms J_C.ide_MkIde by auto
      thus "J_C.Map (J_C.MkIde D) = D"
        using J_C.in_homE by simp
    qed

    lemma bij_betw_ide_diagram:
    shows "bij_betw J_C.Map (Collect J_C.ide) (Collect (diagram J C))"
    proof (intro bij_betwI)
      show "J_C.Map  Collect J_C.ide  Collect (diagram J C)"
        using ide_determines_diagram by blast
      show "J_C.MkIde  Collect (diagram J C)  Collect J_C.ide"
        using diagram_determines_ide by blast
      show "d. d  Collect J_C.ide  J_C.MkIde (J_C.Map d) = d"
        using ide_determines_diagram by blast
      show "D. D  Collect (diagram J C)  J_C.Map (J_C.MkIde D) = D"
        using diagram_determines_ide by blast
    qed

    text‹
      Arrows from from the diagonal functor correspond bijectively to cones.
›

    lemma arrow_determines_cone:
    assumes "J_C.ide d" and "arrow_from_functor C J_C.comp map a d x"
    shows "cone J C (J_C.Map d) a (J_C.Map x)"
    and "J_C.MkArr (constant_functor.map J C a) (J_C.Map d) (J_C.Map x) = x"
    proof -
      interpret D: diagram J C J_C.Map d
        using assms ide_determines_diagram by auto
      interpret x: arrow_from_functor C J_C.comp map a d x
        using assms by auto
      interpret A: constant_functor J C a
        using x.arrow by (unfold_locales, auto)
      interpret α: constant_transformation J C a
        using x.arrow by (unfold_locales, auto)
      have Dom_x: "J_C.Dom x = A.map"
        using J_C.in_hom_char map_def x.arrow by force
      have Cod_x: "J_C.Cod x = J_C.Map d"
        using x.arrow by auto
      interpret χ: natural_transformation J C A.map J_C.Map d J_C.Map x
        using x.arrow J_C.arr_char [of x] Dom_x Cod_x by force
      show "D.cone a (J_C.Map x)" ..
      show "J_C.MkArr A.map (J_C.Map d) (J_C.Map x) = x"
        using x.arrow Dom_x Cod_x χ.natural_transformation_axioms
        by (intro J_C.arr_eqI, auto)
    qed

    lemma cone_determines_arrow:
    assumes "J_C.ide d" and "cone J C (J_C.Map d) a χ"
    shows "arrow_from_functor C J_C.comp map a d
             (J_C.MkArr (constant_functor.map J C a) (J_C.Map d) χ)"
    and "J_C.Map (J_C.MkArr (constant_functor.map J C a) (J_C.Map d) χ) = χ"
    proof -
       interpret χ: cone J C J_C.Map d a χ using assms(2) by auto
       let ?x = "J_C.MkArr χ.A.map (J_C.Map d) χ"
       interpret x: arrow_from_functor C J_C.comp map a d ?x
       proof
         have "«J_C.MkArr χ.A.map (J_C.Map d) χ :
                  J_C.MkIde χ.A.map [J,C] J_C.MkIde (J_C.Map d)»"
           using χ.natural_transformation_axioms by auto
         moreover have "J_C.MkIde χ.A.map = map a"
           using χ.A.value_is_ide map_def χ.A.map_def C.ide_char
           by (metis (no_types, lifting) J_C.dom_MkArr preserves_arr preserves_dom)
         moreover have "J_C.MkIde (J_C.Map d) = d"
           using assms ide_determines_diagram(2) by simp
         ultimately show "C.ide a  «J_C.MkArr χ.A.map (J_C.Map d) χ : map a [J,C] d»"
           using χ.A.value_is_ide by simp
       qed
       show "arrow_from_functor C J_C.comp map a d ?x" ..
       show "J_C.Map (J_C.MkArr (constant_functor.map J C a) (J_C.Map d) χ) = χ"
         by (simp add: χ.natural_transformation_axioms)
    qed

    text‹
      Transforming a cone by composing at the apex with an arrow @{term g} corresponds,
      via the preceding bijections, to composition in [J, C]› with the image of @{term g}
      under the diagonal functor.
›

    lemma cones_map_is_composition:
    assumes "«g : a'  a»" and "cone J C D a χ"
    shows "J_C.MkArr (constant_functor.map J C a') D (diagram.cones_map J C D g χ)
             = J_C.MkArr (constant_functor.map J C a) D χ [J,C] map g"
    proof -
      interpret A: constant_transformation J C a
        using assms(1) by (unfold_locales, auto)
      interpret χ: cone J C D a χ using assms(2) by auto
      have cone_χ: "cone J C D a χ" ..
      interpret A': constant_transformation J C a'
        using assms(1) by (unfold_locales, auto)
      let ?χ' = "χ.D.cones_map g χ"
      interpret χ': cone J C D a' ?χ'
        using assms(1) cone_χ χ.D.cones_map_mapsto by blast
      let ?x = "J_C.MkArr χ.A.map D χ"
      let ?x' = "J_C.MkArr χ'.A.map D ?χ'"
      show "?x' = J_C.comp ?x (map g)"
      proof (intro J_C.arr_eqI)
        have x: "J_C.arr ?x"
          using χ.natural_transformation_axioms J_C.arr_char [of ?x] by simp
        show x': "J_C.arr ?x'"
          using χ'.natural_transformation_axioms J_C.arr_char [of ?x'] by simp
        have 3: "«?x : map a [J,C] J_C.MkIde D»"
          using χ.D.diagram_axioms arrow_from_functor.arrow cone_χ cone_determines_arrow(1)
                diagram_determines_ide(1)
          by fastforce
        have 4: "«?x' : map a' [J,C] J_C.MkIde D»"
          by (metis (no_types, lifting) J_C.Dom.simps(1) J_C.Dom_cod J_C.Map_cod
              J_C.cod_MkArr χ'.cone_axioms arrow_from_functor.arrow category.ide_cod
              cone_determines_arrow(1) functor_def is_functor x)
        have seq_xg: "J_C.seq ?x (map g)"
          using assms(1) 3 preserves_hom [of g] by (intro J_C.seqI', auto)
        show 2: "J_C.seq ?x (map g)"
          using seq_xg J_C.seqI' by blast
        show "J_C.Dom ?x' = J_C.Dom (?x [J,C] map g)"
        proof -
          have "J_C.Dom ?x' = J_C.Dom (J_C.dom ?x')"
            using x' J_C.Dom_dom by simp
          also have "... = J_C.Dom (map a')"
            using 4 by force
          also have "... = J_C.Dom (J_C.dom (?x [J,C] map g))"
            using assms(1) 2 by auto
          also have "... = J_C.Dom (?x [J,C] map g)"
            using seq_xg J_C.Dom_dom J_C.seqI' by blast
          finally show ?thesis by auto
        qed
        show "J_C.Cod ?x' = J_C.Cod (?x [J,C] map g)"
        proof -
          have "J_C.Cod ?x' = J_C.Cod (J_C.cod ?x')"
            using x' J_C.Cod_cod by simp
          also have "... = J_C.Cod (J_C.MkIde D)"
            using 4 by force
          also have "... = J_C.Cod (J_C.cod (?x [J,C] map g))"
            using 2 3 J_C.cod_comp J_C.in_homE by metis
          also have "... = J_C.Cod (?x [J,C] map g)"
            using seq_xg J_C.Cod_cod J_C.seqI' by blast
          finally show ?thesis by auto
        qed
        show "J_C.Map ?x' = J_C.Map (?x [J,C] map g)"
        proof -
          interpret g: constant_transformation J C g
            apply unfold_locales using assms(1) by auto
          interpret χog: vertical_composite J C A'.map χ.A.map D g.map χ
            using assms(1) C.comp_arr_dom C.comp_cod_arr A'.is_extensional g.is_extensional
            apply (unfold_locales, auto)
            by (elim J.seqE, auto)
          have "J_C.Map (?x [J,C] map g) = χog.map"
            using assms(1) 2 J_C.comp_char map_def by auto
          also have "... = J_C.Map ?x'"
            using x' χog.map_def J_C.arr_char [of ?x'] natural_transformation.is_extensional
                  assms(1) cone_χ χog.map_simp_2
            by fastforce
          finally show ?thesis by auto
        qed
      qed
    qed

    text‹
      Coextension along an arrow from a functor is equivalent to a transformation of cones.
›

    lemma coextension_iff_cones_map:
    assumes x: "arrow_from_functor C J_C.comp map a d x"
    and g: "«g : a'  a»"
    and x': "«x' : map a' [J,C] d»"
    shows "arrow_from_functor.is_coext C J_C.comp map a x a' x' g
               J_C.Map x' = diagram.cones_map J C (J_C.Map d) g (J_C.Map x)"
    proof -
      interpret x: arrow_from_functor C J_C.comp map a d x
        using assms by auto
      interpret A': constant_functor J C a'
        using assms(2) by (unfold_locales, auto)
      have x': "arrow_from_functor C J_C.comp map a' d x'"
        using A'.value_is_ide assms(3) by (unfold_locales, blast)
      have d: "J_C.ide d" using J_C.ide_cod x.arrow by blast
      let ?D = "J_C.Map d"
      let  = "J_C.Map x"
      let ?χ' = "J_C.Map x'"
      interpret D: diagram J C ?D
        using ide_determines_diagram J_C.ide_cod x.arrow by blast
      interpret χ: cone J C ?D a 
        using assms(1) d arrow_determines_cone by simp
      interpret γ: constant_transformation J C g
        using g χ.ide_apex by (unfold_locales, auto)
      interpret χog: vertical_composite J C A'.map χ.A.map ?D γ.map 
        using g C.comp_arr_dom C.comp_cod_arr γ.is_extensional by (unfold_locales, auto)
      show ?thesis
      proof
        assume 0: "x.is_coext a' x' g"
        show "?χ' = D.cones_map g "
        proof -
          have 1: "x' = x [J,C] map g"
            using 0 x.is_coext_def by blast
          hence "?χ' = J_C.Map x'"
            using 0 x.is_coext_def by fast
          moreover have "... = D.cones_map g "
          proof -
            have "J_C.MkArr A'.map (J_C.Map d) (D.cones_map g (J_C.Map x)) = x'"
            proof -
              have "J_C.MkArr A'.map (J_C.Map d) (D.cones_map g (J_C.Map x)) =
                    x [J,C] map g"
                using d g cones_map_is_composition arrow_determines_cone(2) χ.cone_axioms
                      x.arrow_from_functor_axioms
                by auto
              thus ?thesis by (metis 1)
            qed
            moreover have "J_C.arr (J_C.MkArr A'.map (J_C.Map d) (D.cones_map g (J_C.Map x)))"
              using 1 d g cones_map_is_composition preserves_arr arrow_determines_cone(2)
                    χ.cone_axioms x.arrow_from_functor_axioms assms(3)
              by auto
            ultimately show ?thesis by auto
          qed
          ultimately show ?thesis by blast
        qed
        next
        assume X': "?χ' = D.cones_map g "
        show "x.is_coext a' x' g"
        proof -
          have 4: "J_C.seq x (map g)"
            using g x.arrow mem_Collect_eq preserves_arr preserves_cod
            by (elim C.in_homE, auto)
          hence 1: "x [J,C] map g =
                   J_C.MkArr (J_C.Dom (map g)) (J_C.Cod x)
                             (vertical_composite.map J C (J_C.Map (map g)) )"
            using J_C.comp_char [of x "map g"] by simp
          have 2: "vertical_composite.map J C (J_C.Map (map g))  = χog.map"
            by (simp add: map_def γ.value_is_arr γ.natural_transformation_axioms)
          have 3: "... = D.cones_map g "
            using g χog.map_simp_2 χ.cone_axioms χog.is_extensional by auto
          have "J_C.MkArr A'.map ?D ?χ' = J_C.comp x (map g)"
          proof -
            have f1: "A'.map = J_C.Dom (map g)"
              using γ.natural_transformation_axioms map_def g by auto
            have "J_C.Map d = J_C.Cod x"
              using x.arrow by auto
            thus ?thesis using f1 X' 1 2 3 by argo
          qed
          moreover have "J_C.MkArr A'.map ?D ?χ' = x'"
            using d x' arrow_determines_cone by blast
          ultimately show ?thesis
            using g x.is_coext_def by simp
        qed
      qed
    qed

  end

  locale right_adjoint_to_diagonal_functor =
    C: category C +
    J: category J +
    J_C: functor_category J C +
    Δ: diagonal_functor J C +
    "functor" J_C.comp C G +
    Adj: meta_adjunction J_C.comp C Δ.map G φ ψ
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and G :: "('j, 'c) functor_category.arr  'c"
  and φ :: "'c  ('j, 'c) functor_category.arr  'c"
  and ψ :: "('j, 'c) functor_category.arr  'c  ('j, 'c) functor_category.arr" +
  assumes adjoint: "adjoint_functors J_C.comp C Δ.map G"
  begin

    interpretation S: replete_setcat .
    interpretation Adj: adjunction J_C.comp C S.comp S.setp Adj.φC Adj.φD Δ.map G
                          φ ψ Adj.η Adj.ε Adj.Φ Adj.Ψ
      using Adj.induces_adjunction by simp

    text‹
      A right adjoint @{term G} to a diagonal functor maps each object @{term d} of
      [J, C]› (corresponding to a diagram @{term D} of shape @{term J} in @{term C}
      to an object of @{term C}.  This object is the limit object, and the component at @{term d}
      of the counit of the adjunction determines the limit cone.
›

    lemma gives_limit_cones:
    assumes "diagram J C D"
    shows "limit_cone J C D (G (J_C.MkIde D)) (J_C.Map (Adj.ε (J_C.MkIde D)))"
    proof -
      interpret D: diagram J C D using assms by auto
      let ?d = "J_C.MkIde D"
      let ?a = "G ?d"
      let ?x = "Adj.ε ?d"
      let  = "J_C.Map ?x"
      have "diagram J C D" ..
      hence 1: "J_C.ide ?d" using Δ.diagram_determines_ide by auto
      hence 2: "J_C.Map (J_C.MkIde D) = D"
        using assms 1 J_C.in_homE Δ.diagram_determines_ide(2) by simp
      interpret x: terminal_arrow_from_functor C J_C.comp Δ.map ?a ?d ?x
        apply unfold_locales
         apply (metis (no_types, lifting) "1" preserves_ide Adj.ε_in_terms_of_ψ
                Adj.εo_def Adj.εo_in_hom)
        by (metis 1 Adj.has_terminal_arrows_from_functor(1)
                  terminal_arrow_from_functor.is_terminal)
      have 3: "arrow_from_functor C J_C.comp Δ.map ?a ?d ?x" ..
      interpret χ: cone J C D ?a 
        using 1 2 3 Δ.arrow_determines_cone [of ?d] by auto
      have cone_χ: "D.cone ?a " ..
      interpret χ: limit_cone J C D ?a 
      proof
        fix a' χ'
        assume cone_χ': "D.cone a' χ'"
        interpret χ': cone J C D a' χ' using cone_χ' by auto
        let ?x' = "J_C.MkArr χ'.A.map D χ'"
        interpret x': arrow_from_functor C J_C.comp Δ.map a' ?d ?x'
          using 1 2 by (metis Δ.cone_determines_arrow(1) cone_χ')
        have "arrow_from_functor C J_C.comp Δ.map a' ?d ?x'" ..
        hence 4: "∃!g. x.is_coext a' ?x' g"
          using x.is_terminal by simp
        have 5: "g. «g : a' C ?a»  x.is_coext a' ?x' g  D.cones_map g  = χ'"
          using Δ.coextension_iff_cones_map x'.arrow x.arrow_from_functor_axioms by auto
        have 6: "g. x.is_coext a' ?x' g  «g : a' C ?a»"
          using x.is_coext_def by simp
        show "∃!g. «g : a' C ?a»  D.cones_map g  = χ'"
        proof -
          have "g. «g : a' C ?a»  D.cones_map g  = χ'"
            using 4 5 6 by meson
          thus ?thesis
            using 4 5 6 by blast
        qed
      qed
      show "D.limit_cone ?a " ..
    qed

    corollary gives_limits:
    assumes "diagram J C D"
    shows "diagram.has_as_limit J C D (G (J_C.MkIde D))"
      using assms gives_limit_cones by fastforce

  end

  lemma (in category) limits_are_isomorphic:
  fixes J :: "'j comp"
  assumes "limit_cone J C D a χ" and "limit_cone J C D a' χ'"
  shows "isomorphic a a'" and "iso (limit_cone.induced_arrow J C D a χ a' χ')"
  proof -
    interpret J: category J
      using assms(1) limit_cone.axioms(2) by metis
    interpret C: category C
      using assms(1) limit_cone.axioms(1) by metis
    interpret D: diagram J C D
      using assms(1) limit_cone.axioms(3) by metis
    interpret χ: limit_cone J C D a χ
      using assms(1) by blast
    interpret χ': limit_cone J C D a' χ'
      using assms(2) by blast
    have 1: "∃!f. «f : a  a'»  D.cones_map f χ' = χ"
      using χ'.is_universal [of a χ] χ.cone_axioms by simp
    have 2: "∃!g. «g : a'  a»  D.cones_map g χ = χ'"
      using χ.is_universal [of a' χ'] χ'.cone_axioms by simp
    define f where "f = χ'.induced_arrow a χ"
    define g where "g = χ.induced_arrow a' χ'"
    have f: "«f : a  a'»  D.cones_map f χ' = χ"
      using f_def χ'.induced_arrowI(1-2) χ.is_cone by blast
    have g: "«g : a'  a»  D.cones_map g χ = χ'"
      using g_def χ.induced_arrowI(1-2) χ'.is_cone by blast
    have *: "inverse_arrows f g"
    proof
      show "ide (g  f)"
      proof -
        have "g  f = a"
        proof -
          have "∃!h. «h : a  a»  D.cones_map h χ = χ"
            using χ.is_universal [of a χ] χ.cone_axioms by blast
          moreover have "«g  f : a  a»"
            using f g by blast
          moreover have "D.cones_map (g  f) χ = χ"
          proof
            fix j :: 'j
            show "D.cones_map (g  f) χ j = χ j"
            proof (cases "J.arr j")
              show "¬ J.arr j  ?thesis"
                using f g χ.cone_axioms χ.is_extensional by fastforce
              assume j: "J.arr j"
              have "D.cone (dom g) (D.cones_map g χ)"
                using g D.cones_map_mapsto χ.cone_axioms by blast
              thus ?thesis
                using f g χ.cone_axioms D.cones_map_comp [of g f] by fastforce
            qed
          qed
          moreover have "«a : a  a»"
            using χ.ide_apex by auto
          moreover have "D.cones_map a χ = χ"
            using f χ.cone_axioms D.cones_map_ide by blast
          ultimately show ?thesis by blast
        qed
        thus ?thesis
          using χ.ide_apex by blast
      qed
      show "ide (f  g)"
      proof -
        have "f  g = a'"
        proof -
          have "∃!h. «h : a'  a'»  D.cones_map h χ' = χ'"
            using χ'.is_universal [of a' χ'] χ'.cone_axioms by blast
          moreover have "«f  g : a'  a'»"
            using f g by blast
          moreover have "D.cones_map (f  g) χ' = χ'"
          proof
            fix j :: 'j
            show "D.cones_map (f  g) χ' j = χ' j"
            proof (cases "J.arr j")
              show "¬ J.arr j  ?thesis"
                using f g χ'.cone_axioms χ'.is_extensional by fastforce
              assume j: "J.arr j"
              have "D.cone (dom f) (D.cones_map f χ')"
                using f D.cones_map_mapsto χ'.cone_axioms by blast
              thus ?thesis
                using f g χ'.cone_axioms D.cones_map_comp [of f g] by fastforce
            qed
          qed
          moreover have "«a' : a'  a'»"
            using χ'.ide_apex by auto
          moreover have "D.cones_map a' χ' = χ'"
            using g χ'.cone_axioms D.cones_map_ide by blast
          ultimately show ?thesis by blast
        qed
        thus ?thesis
          using χ'.ide_apex by blast
      qed
    qed
    show "isomorphic a a'"
      using * f g by blast
    show "iso (χ.induced_arrow a' χ')"
      using * g_def by blast
  qed

  lemma (in category) has_limits_iff_left_adjoint_diagonal:
  assumes "category J"
  shows "has_limits_of_shape J 
           left_adjoint_functor C (functor_category.comp J C) (diagonal_functor.map J C)"
  proof -
    interpret J: category J using assms by auto
    interpret J_C: functor_category J C ..
    interpret Δ: diagonal_functor J C ..
    show ?thesis
    proof
      assume A: "left_adjoint_functor C J_C.comp Δ.map"
      interpret Δ: left_adjoint_functor C J_C.comp Δ.map using A by auto
      interpret Adj: meta_adjunction J_C.comp C Δ.map Δ.G Δ.φ Δ.ψ
        using Δ.induces_meta_adjunction by auto
      have 1: "adjoint_functors J_C.comp C Δ.map Δ.G"
        using adjoint_functors_def Δ.induces_meta_adjunction by blast
      interpret G: right_adjoint_to_diagonal_functor J C Δ.G Δ.φ Δ.ψ
        using 1 by unfold_locales auto
      show "has_limits_of_shape J"
        using A G.gives_limits has_limits_of_shape_def by blast
      next
      text‹
        If @{term "has_limits J"}, then every diagram @{term D} from @{term J} to
        @{term[source=true] C} has a limit cone.
        This means that, for every object @{term d} of the functor category
        [J, C]›, there exists an object @{term a} of @{term C} and a terminal arrow from
        Δ a› to @{term d} in [J, C]›.  The terminal arrow is given by the
        limit cone.
›
      assume A: "has_limits_of_shape J"
      show "left_adjoint_functor C J_C.comp Δ.map"
      proof
        fix d
        assume D: "J_C.ide d"
        interpret D: diagram J C J_C.Map d
          using D Δ.ide_determines_diagram by auto
        let ?D = "J_C.Map d"
        have "diagram J C (J_C.Map d)" ..
        from this obtain a χ where limit: "limit_cone J C ?D a χ"
          using A has_limits_of_shape_def by blast
        interpret A: constant_functor J C a
          using limit by (simp add: Limit.cone_def limit_cone_def)
        interpret χ: limit_cone J C ?D a χ using limit by simp
        have cone_χ: "cone J C ?D a χ" ..
        let ?x = "J_C.MkArr A.map ?D χ"
        interpret x: arrow_from_functor C J_C.comp Δ.map a d ?x
          using D cone_χ Δ.cone_determines_arrow by auto
        have "terminal_arrow_from_functor C J_C.comp Δ.map a d ?x"
        proof
          show "a' x'. arrow_from_functor C J_C.comp Δ.map a' d x'  ∃!g. x.is_coext a' x' g"
          proof -
            fix a' x'
            assume x': "arrow_from_functor C J_C.comp Δ.map a' d x'"
            interpret x': arrow_from_functor C J_C.comp Δ.map a' d x' using x' by auto
            interpret A': constant_functor J C a'
              by (unfold_locales, simp add: x'.arrow)
            let ?χ' = "J_C.Map x'"
            interpret χ': cone J C ?D a' ?χ'
              using D x' Δ.arrow_determines_cone by auto
            have cone_χ': "cone J C ?D a' ?χ'" ..
            let ?g = "χ.induced_arrow a' ?χ'"
            show "∃!g. x.is_coext a' x' g"
            proof
              show "x.is_coext a' x' ?g"
              proof (unfold x.is_coext_def)
                have 1: "«?g : a'  a»  D.cones_map ?g χ = ?χ'"
                  using χ.induced_arrow_def χ.is_universal cone_χ'
                        theI' [of "λf. «f : a'  a»  D.cones_map f χ = ?χ'"]
                  by presburger
                hence 2: "x' = ?x [J,C] Δ.map ?g"
                proof -
                  have "x' = J_C.MkArr A'.map ?D ?χ'"
                    using D Δ.arrow_determines_cone(2) x'.arrow_from_functor_axioms by auto
                  thus ?thesis
                    using 1 cone_χ Δ.cones_map_is_composition [of ?g a' a ?D χ] by simp
                qed
                show "«?g : a'  a»  x' = ?x [J,C] Δ.map ?g"
                  using 1 2 by auto
              qed
              next
              fix g
              assume X: "x.is_coext a' x' g"
              show "g = ?g"
              proof -
                have "«g : a'  a»  D.cones_map g χ = ?χ'"
                proof
                  show G: "«g : a'  a»" using X x.is_coext_def by blast
                  show "D.cones_map g χ = ?χ'"
                  proof -
                    have "?χ' = J_C.Map (?x [J,C] Δ.map g)"
                      using X x.is_coext_def [of a' x' g] by fast
                    also have "... = D.cones_map g χ"
                    proof -
                      interpret map_g: constant_transformation J C g
                        using G by (unfold_locales, auto)
                      interpret χ': vertical_composite J C
                                      map_g.F.map A.map χ.Φ.Ya.Cop_S.Map d
                                      map_g.map χ
                      proof (intro_locales)
                        have "map_g.G.map = A.map"
                          using G by blast
                        thus "natural_transformation_axioms J (⋅) map_g.F.map A.map map_g.map"
                          using map_g.natural_transformation_axioms
                          by (simp add: natural_transformation_def)
                      qed
                      have "J_C.Map (?x [J,C] Δ.map g) = vertical_composite.map J C map_g.map χ"
                      proof -
                        have "J_C.seq ?x (Δ.map g)"
                          using G x.arrow by auto
                        thus ?thesis
                          using G Δ.map_def J_C.Map_comp' [of ?x "Δ.map g"] by auto
                      qed
                      also have "... = D.cones_map g χ"
                        using G cone_χ χ'.map_def map_g.map_def χ.is_natural_2 χ'.map_simp_2
                        by auto
                      finally show ?thesis by blast
                    qed
                    finally show ?thesis by auto
                  qed
                qed
                thus ?thesis
                  using cone_χ' χ.is_universal χ.induced_arrow_def
                        theI_unique [of "λg. «g : a'  a»  D.cones_map g χ = ?χ'" g]
                  by presburger
              qed
            qed
          qed
        qed
        thus "a x. terminal_arrow_from_functor C J_C.comp Δ.map a d x" by auto
      qed
    qed
  qed

  section "Right Adjoint Functors Preserve Limits"

  context right_adjoint_functor
  begin

    lemma preserves_limits:
    fixes J :: "'j comp"
    assumes "diagram J C E" and "diagram.has_as_limit J C E a"
    shows "diagram.has_as_limit J D (G o E) (G a)"
    proof -
      text‹
        From the assumption that @{term E} has a limit, obtain a limit cone @{term χ}.
›
      interpret J: category J using assms(1) diagram_def by auto
      interpret E: diagram J C E using assms(1) by auto
      from assms(2) obtain χ where χ: "limit_cone J C E a χ" by auto
      interpret χ: limit_cone J C E a χ using χ by auto
      have a: "C.ide a" using χ.ide_apex by auto
      text‹
        Form the @{term E}-image GE› of the diagram @{term E}.
›
      interpret GE: composite_functor J C D E G ..
      interpret GE: diagram J D GE.map ..
      text‹Let Gχ› be the @{term G}-image of the cone @{term χ},
             and note that it is a cone over GE›.›
      let ?Gχ = "G o χ"
      interpret: cone J D GE.map G a ?Gχ
        using χ.cone_axioms preserves_cones by blast
      text‹
        Claim that Gχ› is a limit cone for diagram GE›.
›
      interpret: limit_cone J D GE.map G a ?Gχ
      proof
        text ‹
          Let @{term κ} be an arbitrary cone over GE›.
›
        fix b κ
        assume κ: "GE.cone b κ"
        interpret κ: cone J D GE.map b κ using κ by auto
        interpret Fb: constant_functor J C F b
          apply unfold_locales
          by (meson F_is_functor κ.ide_apex functor.preserves_ide)
        interpret Adj: meta_adjunction C D F G φ ψ
          using induces_meta_adjunction by auto
        interpret S: replete_setcat .
        interpret Adj: adjunction C D S.comp S.setp
                         Adj.φC Adj.φD F G φ ψ Adj.η Adj.ε Adj.Φ Adj.Ψ
          using Adj.induces_adjunction by simp
        text‹
          For each arrow @{term j} of @{term J}, let @{term "χ' j"} be defined to be
          the adjunct of @{term "χ j"}.  We claim that @{term χ'} is a cone over @{term E}.
›
        let ?χ' = "λj. if J.arr j then Adj.ε (C.cod (E j)) C F (κ j) else C.null"
        have cone_χ': "E.cone (F b) ?χ'"
        proof
          show "j. ¬J.arr j  ?χ' j = C.null" by simp
          fix j
          assume j: "J.arr j"
          show "C.dom (?χ' j) = Fb.map (J.dom j)" using j ψ_in_hom by simp
          show "C.cod (?χ' j) = E (J.cod j)" using j ψ_in_hom by simp
          show "E j C ?χ' (J.dom j) = ?χ' j"
          proof -
            have "E j C ?χ' (J.dom j) = (E j C Adj.ε (E (J.dom j))) C F (κ (J.dom j))"
              using j C.comp_assoc by simp
            also have "... = Adj.ε (E (J.cod j)) C F (κ j)"
            proof -
              have "(E j C Adj.ε (E (J.dom j))) C F (κ (J.dom j))
                       = (Adj.ε (C.cod (E j)) C Adj.FG.map (E j)) C F (κ (J.dom j))"
                using j Adj.ε.naturality [of "E j"] by fastforce
              also have "... = Adj.ε (C.cod (E j)) C Adj.FG.map (E j) C F (κ (J.dom j))"
                using C.comp_assoc by simp
              also have "... = Adj.ε (E (J.cod j)) C F (κ j)"
              proof -
                have "Adj.FG.map (E j) C F (κ (J.dom j)) = F (GE.map j D κ (J.dom j))"
                  using j by simp
                hence "Adj.FG.map (E j) C F (κ (J.dom j)) = F (κ j)"
                  using j κ.is_natural_1 by metis
                thus ?thesis using j by simp
              qed
              finally show ?thesis by auto
            qed
            also have "... = ?χ' j"
              using j by simp
            finally show ?thesis by auto
          qed
          show "?χ' (J.cod j) C Fb.map j = ?χ' j"
          proof -
            have "?χ' (J.cod j) C Fb.map j = Adj.ε (E (J.cod j)) C F (κ (J.cod j))"
              using j Fb.value_is_ide Adj.ε.preserves_hom C.comp_arr_dom [of "F (κ (J.cod j))"]
                    C.comp_assoc
              by simp
            also have "... = Adj.ε (E (J.cod j)) C F (κ j)"
              using j κ.is_natural_1 κ.is_natural_2 Adj.ε.naturality J.arr_cod_iff_arr
              by (metis J.cod_cod κ.A.map_simp)
            also have "... = ?χ' j" using j by simp
            finally show ?thesis by auto
          qed
        qed
        text‹
          Using the universal property of the limit cone @{term χ}, obtain the unique arrow
          @{term f} that transforms @{term χ} into @{term χ'}.
›
        from this χ.is_universal [of "F b" ?χ'] obtain f
          where f: "«f : F b C a»  E.cones_map f χ = ?χ'"
          by auto
        text‹
          Let @{term g} be the adjunct of @{term f}, and show that @{term g} transforms
          @{term } into @{term κ}.
›
        let ?g = "G f D Adj.η b"
        have 1: "«?g : b D G a»" using f κ.ide_apex by fastforce
        moreover have "GE.cones_map ?g ?Gχ = κ"
        proof
          fix j
          have "¬J.arr j  GE.cones_map ?g ?Gχ j = κ j"
            using 1 Gχ.cone_axioms κ.is_extensional by auto
          moreover have "J.arr j  GE.cones_map ?g ?Gχ j = κ j"
          proof -
            fix j
            assume j: "J.arr j"
            have "GE.cones_map ?g ?Gχ j = G (χ j) D ?g"
              using j 1 Gχ.cone_axioms mem_Collect_eq restrict_apply by auto
            also have "... = G (χ j C f) D Adj.η b"
              using j f χ.preserves_hom [of j "J.dom j" "J.cod j"] D.comp_assoc by fastforce
            also have "... = G (E.cones_map f χ j) D Adj.η b"
            proof -
              have "χ j C f = Adj.ε (C.cod (E j)) C F (κ j)"
              proof -
                have "χ j C f = E.cones_map f χ j"
                proof -
                  have "E.cone (C.cod f) χ"
                    using f χ.cone_axioms by blast
                  thus ?thesis
                    using χ.is_extensional by simp
                qed
                also have "... = Adj.ε (C.cod (E j)) C F (κ j)"
                  using j f by simp
                finally show ?thesis by blast
              qed
              thus ?thesis
                using f mem_Collect_eq restrict_apply Adj.F.is_extensional by simp
            qed
            also have "... = (G (Adj.ε (C.cod (E j))) D Adj.η (D.cod (GE.map j))) D κ j"
              using j f Adj.η.naturality [of "κ j"] D.comp_assoc by auto
            also have "... = D.cod (κ j) D κ j"
              using j Adj.ηε.triangle_G Adj.ε_in_terms_of_ψ Adj.εo_def
                      Adj.η_in_terms_of_φ Adj.ηo_def Adj.unit_counit_G
              by fastforce
            also have "... = κ j"
              using j D.comp_cod_arr by simp
            finally show "GE.cones_map ?g ?Gχ j = κ j" by metis
          qed
          ultimately show "GE.cones_map ?g ?Gχ j = κ j" by auto
        qed
        ultimately have "«?g : b D G a»  GE.cones_map ?g ?Gχ = κ" by auto
        text‹
          It remains to be shown that @{term g} is the unique such arrow.
          Given any @{term g'} that transforms @{term } into @{term κ},
          its adjunct transforms @{term χ} into @{term χ'}.
          The adjunct of @{term g'} is therefore equal to @{term f},
          which implies @{term g'} = @{term g}.
›
        moreover have "g'. «g' : b D G a»  GE.cones_map g' ?Gχ = κ  g' = ?g"
        proof -
          fix g'
          assume g': "«g' : b D G a»  GE.cones_map g' ?Gχ = κ"
          have 1: "«ψ a g' : F b C a»"
            using g' a ψ_in_hom by simp
          have 2: "E.cones_map (ψ a g') χ = ?χ'"
          proof
            fix j
            have "¬J.arr j  E.cones_map (ψ a g') χ j = ?χ' j"
              using 1 χ.cone_axioms by auto
            moreover have "J.arr j  E.cones_map (ψ a g') χ j = ?χ' j"
            proof -
              fix j
              assume j: "J.arr j"
              have "E.cones_map (ψ a g') χ j = χ j C ψ a g'"
                using 1 χ.cone_axioms χ.is_extensional by auto
              also have "... = (χ j C Adj.ε a) C F g'"
                using j a g' Adj.ψ_in_terms_of_ε C.comp_assoc Adj.ε_def by auto
              also have "... = (Adj.ε (C.cod (E j)) C F (G (χ j))) C F g'"
                using j a g' Adj.ε.naturality [of "χ j"] by simp
              also have "... = Adj.ε (C.cod (E j)) C F (κ j)"
                using j a g' Gχ.cone_axioms C.comp_assoc by auto
              finally show "E.cones_map (ψ a g') χ j = ?χ' j" by (simp add: j)
            qed
            ultimately show "E.cones_map (ψ a g') χ j = ?χ' j" by auto
          qed
          have "ψ a g' = f"
          proof -
            have "∃!f. «f : F b C a»  E.cones_map f χ = ?χ'"
              using cone_χ' χ.is_universal by simp
            moreover have "«ψ a g' : F b C a»  E.cones_map (ψ a g') χ = ?χ'"
              using 1 2 by simp
            ultimately show ?thesis
              using ex1E [of "λf. «f : F b C a»  E.cones_map f χ = ?χ'" "ψ a g' = f"]
              using 1 2 Adj.ε.is_extensional C.null_is_zero(2) C.ex_un_null χ.cone_axioms f
                    mem_Collect_eq restrict_apply
              by blast
          qed
          hence "φ b (ψ a g') = φ b f" by auto
          hence "g' = φ b f" using χ.ide_apex g' by (simp add: φ_ψ)
          moreover have "?g = φ b f" using f Adj.φ_in_terms_of_η κ.ide_apex Adj.η_def by auto
          ultimately show "g' = ?g" by argo
        qed
        ultimately show "∃!g. «g : b D G a»  GE.cones_map g ?Gχ = κ" by blast
      qed
      have "GE.limit_cone (G a) ?Gχ" ..
      thus ?thesis by auto
    qed

  end

  section "Special Kinds of Limits"

  subsection "Terminal Objects"

  text‹
   An object of a category @{term C} is a terminal object if and only if it is a limit of the
   empty diagram in @{term C}.
›

  locale empty_diagram =
    diagram J C D
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c" +
  assumes is_empty: "¬J.arr j"
  begin

    lemma has_as_limit_iff_terminal:
    shows "has_as_limit a  C.terminal a"
    proof
      assume a: "has_as_limit a"
      show "C.terminal a"
      proof
        have "χ. limit_cone a χ" using a by auto
        from this obtain χ where χ: "limit_cone a χ" by blast
        interpret χ: limit_cone J C D a χ using χ by auto
        have cone_χ: "cone a χ" ..
        show "C.ide a" using χ.ide_apex by auto
        have 1: "χ = (λj. C.null)" using is_empty χ.is_extensional by auto
        show "a'. C.ide a'  ∃!f. «f : a'  a»"
        proof -
          fix a'
          assume a': "C.ide a'"
          interpret A': constant_functor J C a'
            apply unfold_locales using a' by auto
          let ?χ' = "λj. C.null"
          have cone_χ': "cone a' ?χ'"
            using a' is_empty apply unfold_locales by auto
          hence "∃!f. «f : a'  a»  cones_map f χ = ?χ'"
            using χ.is_universal by force
          moreover have "f. «f : a'  a»  cones_map f χ = ?χ'"
            using 1 cone_χ by auto
          ultimately show "∃!f. «f : a'  a»" by blast
        qed
      qed
      next
      assume a: "C.terminal a"
      show "has_as_limit a"
      proof -
        let  = "λj. C.null"
        have "C.ide a" using a C.terminal_def by simp
        interpret A: constant_functor J C a
          apply unfold_locales using C.ide a by simp
        interpret χ: cone J C D a 
          using C.ide a is_empty by (unfold_locales, auto)
        have cone_χ: "cone a " .. 
        have 1: "a' χ'. cone a' χ'  χ' = (λj. C.null)"
        proof -
          fix a' χ'
          assume χ': "cone a' χ'"
          interpret χ': cone J C D a' χ' using χ' by auto
          show "χ' = (λj. C.null)"
            using is_empty χ'.is_extensional by metis
        qed
        have "limit_cone a "
        proof
          fix a' χ'
          assume χ': "cone a' χ'"
          have 2: "χ' = (λj. C.null)" using 1 χ' by simp
          interpret χ': cone J C D a' χ' using χ' by auto
          have "∃!f. «f : a'  a»" using a C.terminal_def χ'.ide_apex by simp
          moreover have "f. «f : a'  a»  cones_map f  = χ'"
           using 1 2 cones_map_mapsto cone_χ χ'.cone_axioms mem_Collect_eq by blast
          ultimately show "∃!f. «f : a'  a»  cones_map f (λj. C.null) = χ'"
            by blast
        qed
        thus ?thesis by auto
      qed
    qed

  end

  subsection "Products"

  text‹
    A \emph{product} in a category @{term C} is a limit of a discrete diagram in @{term C}.
›

  locale discrete_diagram =
    J: category J +
    diagram J C D
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c" +
  assumes is_discrete: "J.arr = J.ide"
  begin

    abbreviation mkCone
    where "mkCone F  (λj. if J.arr j then F j else C.null)"

    lemma cone_mkCone:
    assumes "C.ide a" and "j. J.arr j  «F j : a  D j»"
    shows "cone a (mkCone F)"
    proof -
      interpret A: constant_functor J C a
         using assms(1) by unfold_locales auto
      show "cone a (mkCone F)"
        using assms(2) is_discrete
        apply unfold_locales
            apply auto
         apply (metis C.in_homE C.comp_cod_arr)
        using C.comp_arr_ide by fastforce
    qed

    lemma mkCone_cone:
    assumes "cone a π"
    shows "mkCone π = π"
    proof -
      interpret π: cone J C D a π
        using assms by auto
      show "mkCone π = π" using π.is_extensional by auto
    qed

  end

  text‹
    The following locale defines a discrete diagram in a category @{term C},
    given an index set @{term I} and a function @{term D} mapping @{term I}
    to objects of @{term C}.  Here we obtain the diagram shape @{term J}
    using a discrete category construction that allows us to directly identify
    the objects of @{term J} with the elements of @{term I}, however this construction
    can only be applied in case the set @{term I} is not the universe of its
    element type.
›

  locale discrete_diagram_from_map =
    J: discrete_category I null +
    C: category C
  for I :: "'i set"
  and C :: "'c comp"      (infixr  55)
  and D :: "'i  'c"
  and null :: 'i +
  assumes maps_to_ide: "i  I  C.ide (D i)"
  begin

    definition map
    where "map j  if J.arr j then D j else C.null"

  end

  sublocale discrete_diagram_from_map  discrete_diagram J.comp C map
    using map_def maps_to_ide J.arr_char J.Null_not_in_Obj J.null_char
    by unfold_locales auto

  locale product_cone =
    J: category J +
    C: category C +
    D: discrete_diagram J C D +
    limit_cone J C D a π
  for J :: "'j comp"      (infixr J 55)
  and C :: "'c comp"      (infixr  55)
  and D :: "'j  'c"
  and a :: 'c
  and π :: "'j  'c"
  begin

    lemma is_cone:
    shows "D.cone a π" ..

    text‹
      The following versions of @{prop is_universal} and @{prop induced_arrowI}
      from the limit_cone› locale are specialized to the case in which the
      underlying diagram is a product diagram.
›

    lemma is_universal':
    assumes "C.ide b" and "j. J.arr j  «F j: b  D j»"
    shows "∃!f. «f : b  a»  (j. J.arr j  π j  f = F j)"
    proof -
      let  = "D.mkCone F"
      interpret B: constant_functor J C b
        using assms(1) by unfold_locales auto
      have cone_χ: "D.cone b "
        using assms D.is_discrete D.cone_mkCone by blast
      interpret χ: cone J C D b  using cone_χ by auto
      have "∃!f. «f : b  a»  D.cones_map f π = "
        using cone_χ is_universal by force
      moreover have
           "f. «f : b  a»  D.cones_map f π =   (j. J.arr j  π j  f = F j)"
      proof -
        fix f
        assume f: "«f : b  a»"
        show "D.cones_map f π =   (j. J.arr j  π j  f = F j)"
        proof
          assume 1: "D.cones_map f π = "
          show "j. J.arr j  π j  f = F j"
          proof -
            have "j. J.arr j  π j  f = F j"
            proof -
              fix j
              assume j: "J.arr j"
              have "π j  f = D.cones_map f π j"
                using j f cone_axioms by force
              also have "... = F j" using j 1 by simp
              finally show "π j  f = F j" by auto
            qed
            thus ?thesis by auto
          qed
          next
          assume 1: "j. J.arr j  π j  f = F j"
          show "D.cones_map f π = "
            using 1 f is_cone χ.is_extensional D.is_discrete is_cone cone_χ by auto
        qed
      qed
      ultimately show ?thesis by blast
    qed

    abbreviation induced_arrow' :: "'c  ('j  'c)  'c"
    where "induced_arrow' b F  induced_arrow b (D.mkCone F)"

    lemma induced_arrowI':
    assumes "C.ide b" and "j. J.arr j  «F j : b  D j»"
    shows "j. J.arr j  π j  induced_arrow' b F = F j"
    proof -
      interpret B: constant_functor J C b
        using assms(1) by unfold_locales auto
      interpret χ: cone J C D b D.mkCone F
        using assms D.cone_mkCone by blast
      have cone_χ: "D.cone b (D.mkCone F)" ..
      hence 1: "D.cones_map (induced_arrow' b F) π = D.mkCone F"
        using induced_arrowI by blast
      fix j
      assume j: "J.arr j"
      have "π j  induced_arrow' b F = D.cones_map (induced_arrow' b F) π j"
        using induced_arrowI(1) cone_χ is_cone is_extensional by force
      also have "... = F j"
        using j 1 by auto
      finally show "π j  induced_arrow' b F = F j"
        by auto
    qed

  end

  context discrete_diagram
  begin

    lemma product_coneI:
    assumes "limit_cone a π" 
    shows "product_cone J C D a π"
      by (meson assms discrete_diagram_axioms functor_axioms functor_def product_cone.intro)

  end

  context category
  begin

    definition has_as_product
    where "has_as_product J D a  (π. product_cone J C D a π)"

    lemma product_is_ide:
    assumes "has_as_product J D a"
    shows "ide a"
    proof -
      obtain π where π: "product_cone J C D a π"
        using assms has_as_product_def by blast
      interpret π: product_cone J C D a π
        using π by auto
      show ?thesis using π.ide_apex by auto
    qed

    text‹
      A category has @{term I}-indexed products for an @{typ 'i}-set @{term I}
      if every @{term I}-indexed discrete diagram has a product.
      In order to reap the benefits of being able to directly identify the elements
      of a set I with the objects of discrete category it generates (thereby avoiding
      the use of coercion maps), it is necessary to assume that @{term "I  UNIV"}.
      If we want to assert that a category has products indexed by the universe of
      some type @{typ 'i}, we have to pass to a larger type, such as @{typ "'i option"}.
›

    definition has_products
    where "has_products (I :: 'i set) 
             I  UNIV 
             (J D. discrete_diagram J C D  Collect (partial_composition.arr J) = I
                       (a. has_as_product J D a))"

    lemma ex_productE:
    assumes "a. has_as_product J D a"
    obtains a π where "product_cone J C D a π"
      using assms has_as_product_def someI_ex [of "λa. has_as_product J D a"] by metis

    lemma has_products_if_has_limits:
    assumes "has_limits (undefined :: 'j)" and "I  (UNIV :: 'j set)"
    shows "has_products I"
    proof (unfold has_products_def, intro conjI allI impI)
      show "I  UNIV" by fact
      fix J D
      assume D: "discrete_diagram J C D  Collect (partial_composition.arr J) = I"
      interpret D: discrete_diagram J C D
        using D by simp
      have 1: "a. D.has_as_limit a"
        using assms D D.diagram_axioms D.J.category_axioms
        by (simp add: has_limits_of_shape_def has_limits_def)
      show "a. has_as_product J D a"
        using 1 has_as_product_def D.product_coneI by blast
    qed

    lemma has_finite_products_if_has_finite_limits:
    assumes "J :: 'j comp. (finite (Collect (partial_composition.arr J)))  has_limits_of_shape J"
    and "finite (I :: 'j set)" and "I  UNIV"
    shows "has_products I"
    proof (unfold has_products_def, intro conjI allI impI)
      show "I  UNIV" by fact
      fix J D
      assume D: "discrete_diagram J C D  Collect (partial_composition.arr J) = I"
      interpret D: discrete_diagram J C D
        using D by simp
      have 1: "a. D.has_as_limit a"
        using assms D has_limits_of_shape_def D.diagram_axioms by auto
      show "a. has_as_product J D a"
        using 1 has_as_product_def D.product_coneI by blast
    qed

    lemma has_products_preserved_by_bijection:
    assumes "has_products I" and "bij_betw φ I I'" and "I'  UNIV"
    shows "has_products I'"
    proof (unfold has_products_def, intro conjI allI impI)
      show "I'  UNIV" by fact
      show "J' D'. discrete_diagram J' C D'  Collect (partial_composition.arr J') = I'
                      a. has_as_product J' D' a"
      proof -
        fix J' D'
        assume 1: "discrete_diagram J' C D'  Collect (partial_composition.arr J') = I'"
        interpret J': category J'
          using 1 by (simp add: discrete_diagram_def)
        interpret D': discrete_diagram J' C D'
          using 1 by simp
        interpret J: discrete_category I SOME x. x  I
          using assms has_products_def [of I] someI_ex [of "λx. x  I"]
          by unfold_locales auto
        have 2: "Collect J.arr = I  Collect J'.arr = I'"
          using 1 by auto
        have φ: "bij_betw φ (Collect J.arr) (Collect J'.arr)"
          using 2 assms(2) by simp
        let  = "λj. if J.arr j then φ j else J'.null"
        let ?φ' = "λj'. if J'.arr j' then the_inv_into I φ j' else J.null"
        interpret φ: "functor" J.comp J' 
        proof -
          have "φ ` I = I'"
            using φ 2 bij_betw_def [of φ I I'] by simp
          hence "j. J.arr j  J'.arr ( j)"
            using 1 D'.is_discrete by auto
          thus "functor J.comp J' "
            using D'.is_discrete J.is_discrete J.seqE
            by unfold_locales auto
        qed
        interpret φ': "functor" J' J.comp ?φ'
        proof -
          have "the_inv_into I φ ` I' = I"
            using assms(2) φ bij_betw_the_inv_into bij_betw_imp_surj_on by metis
          hence "j'. J'.arr j'  J.arr (?φ' j')"
            using 2 D'.is_discrete J.is_discrete by auto
          thus "functor J' J.comp ?φ'"
            using D'.is_discrete J.is_discrete J'.seqE
            by unfold_locales auto
        qed
        let ?D = "λi. D' (φ i)"
        interpret D: discrete_diagram_from_map I C ?D SOME j. j  I
          using assms 1 D'.is_discrete bij_betw_imp_surj_on φ.preserves_ide
          by unfold_locales auto
        obtain a where a: "has_as_product J.comp D.map a"
          using assms D.discrete_diagram_axioms has_products_def [of I] by auto
        obtain π where π: "product_cone J.comp C D.map a π"
          using a has_as_product_def by blast
        interpret π: product_cone J.comp C D.map a π
          using π by simp
        let ?π' = "π o ?φ'"
        interpret A: constant_functor J' C a
          using π.ide_apex by unfold_locales simp
        interpret π': natural_transformation J' C A.map D' ?π'
        proof -
          have "π.A.map  ?φ' = A.map"
            using φ A.map_def φ'.preserves_arr π.A.is_extensional J.not_arr_null by auto
          moreover have "D.map  ?φ' = D'"
          proof
            fix j'
            have "J'.arr j'  (D.map  ?φ') j' = D' j'"
            proof -
              assume 2: "J'.arr j'"
              have 3: "inj_on φ I"
                using assms(2) bij_betw_imp_inj_on by auto
              have "φ ` I = I'"
                by (metis (no_types) assms(2) bij_betw_imp_surj_on)
              hence "φ ` I = Collect J'.arr"
                using 1 by force
              thus ?thesis
                using 2 3 D.map_def φ'.preserves_arr f_the_inv_into_f by fastforce
            qed
            moreover have "¬ J'.arr j'  (D.map  ?φ') j' = D' j'"
              using D.is_extensional D'.is_extensional
              by (simp add: J.Null_not_in_Obj J.null_char)
            ultimately show "(D.map  ?φ') j' = D' j'" by blast
          qed
          ultimately show "natural_transformation J' C A.map D' ?π'"
            using π.natural_transformation_axioms φ'.as_nat_trans.natural_transformation_axioms
                  horizontal_composite [of J' J.comp ?φ' ?φ' ?φ' C π.A.map D.map π]
            by simp
        qed
        interpret π': cone J' C D' a ?π' ..
        interpret π': product_cone J' C D' a ?π'
        proof
          fix a' χ'
          assume χ': "D'.cone a' χ'"
          interpret χ': cone J' C D' a' χ'
            using χ' by simp
          show "∃!f. «f : a'  a»  D'.cones_map f (π  ?φ') = χ'"
          proof -
            let  = "χ' o "
            interpret A': constant_functor J.comp C a'
              using χ'.ide_apex by unfold_locales simp
            interpret χ: natural_transformation J.comp C A'.map D.map 
            proof -
              have "χ'.A.map   = A'.map"
                using φ φ.preserves_arr A'.map_def χ'.A.is_extensional by auto
              moreover have "D'   = D.map"
                using φ D.map_def D'.is_extensional by auto
              ultimately show "natural_transformation J.comp C A'.map D.map "
                using χ'.natural_transformation_axioms
                      φ.as_nat_trans.natural_transformation_axioms
                      horizontal_composite [of J.comp J'    C χ'.A.map D' χ']
                by simp
            qed
            interpret χ: cone J.comp C D.map a'  ..
            have *: "∃!f. «f : a'  a»  D.cones_map f π = "
              using π.is_universal χ.cone_axioms by simp
            show "∃!f. «f : a'  a»  D'.cones_map f ?π' = χ'"
            proof -
              have "f. «f : a'  a»  D'.cones_map f ?π' = χ'"
              proof -
                obtain f where f: "«f : a'  a»  D.cones_map f π = "
                  using * by blast
                have "D'.cones_map f ?π' = χ'"
                proof
                  fix j'
                  show "D'.cones_map f ?π' j' = χ' j'"
                  proof (cases "J'.arr j'")
                    assume j': "¬ J'.arr j'"
                    show "D'.cones_map f ?π' j' = χ' j'"
                      using f j' χ'.is_extensional π'.cone_axioms by auto
                    next
                    assume j': "J'.arr j'"
                    show "D'.cones_map f ?π' j' = χ' j'"
                    proof -
                      have "D'.cones_map f ?π' j' = π (the_inv_into I φ j')  f"
                        using f j' π'.cone_axioms by auto
                      also have "... = D.cones_map f π (the_inv_into I φ j')"
                      proof -
                        have "arr f  dom f = a'  cod f = a"
                          using f by blast
                        thus ?thesis
                          using φ'.preserves_arr π.is_cone j' by auto
                      qed
                      also have "... = (χ'  ) (the_inv_into I φ j')"
                        using f by simp
                      also have "... = χ' j'"
                        using assms(2) j' 2 bij_betw_def [of φ I I'] bij_betw_imp_inj_on
                              φ'.preserves_arr f_the_inv_into_f
                        by fastforce
                      finally show ?thesis by simp
                    qed
                  qed
                qed
                thus ?thesis using f by blast
              qed
              moreover have "f f'.  «f : a'  a»; D'.cones_map f ?π' = χ';
                                      «f' : a'  a»; D'.cones_map f' ?π' = χ' 
                                          f = f'"
              proof -
                fix f f'
                assume f: "«f : a'  a»" and f': "«f' : a'  a»"
                and fχ': "D'.cones_map f ?π' = χ'" and f'χ': "D'.cones_map f' ?π' = χ'"
                have "D.cones_map f π = χ'    D.cones_map f' π = χ' o "
                proof (intro conjI)
                  show "D.cones_map f π = χ'  "
                  proof
                    fix j
                    have "¬ J.arr j  D.cones_map f π j = (χ'  ) j"
                      using f fχ' π.cone_axioms χ.is_extensional by auto
                    moreover have "J.arr j  D.cones_map f π j = (χ'  ) j"
                    proof -
                      assume j: "J.arr j"
                      have 1: "j = the_inv_into I φ (φ j)"
                        using assms(2) j φ the_inv_into_f_f bij_betw_imp_inj_on J.arr_char
                        by metis
                      have "D.cones_map f π j = D.cones_map f π (the_inv_into I φ (φ j))"
                        using 1 by simp
                      also have "... = (χ'  ) j"
                        using f j fχ' 1 π.cone_axioms π'.cone_axioms φ.preserves_arr by auto
                      finally show "D.cones_map f π j = (χ'  ) j" by blast
                    qed
                    ultimately show "D.cones_map f π j = (χ'  ) j" by blast
                  qed
                  show "D.cones_map f' π = χ'  "
                  proof
                    fix j
                    have "¬ J.arr j  D.cones_map f' π j = (χ'  ) j"
                      using f' fχ' π.cone_axioms χ.is_extensional by auto
                    moreover have "J.arr j  D.cones_map f' π j = (χ'  ) j"
                    proof -
                      assume j: "J.arr j"
                      have 1: "j = the_inv_into I φ (φ j)"
                        using assms(2) j φ the_inv_into_f_f bij_betw_imp_inj_on J.arr_char
                        by metis
                      have "D.cones_map f' π j = D.cones_map f' π (the_inv_into I φ (φ j))"
                        using 1 by simp
                      also have "... = (χ'  ) j"
                        using f' j f'χ' 1 π.cone_axioms π'.cone_axioms φ.preserves_arr by auto
                      finally show "D.cones_map f' π j = (χ'  ) j" by blast
                    qed
                    ultimately show "D.cones_map f' π j = (χ'  ) j" by blast
                  qed
                qed
                thus "f = f'"
                  using f f' * by auto
              qed
              ultimately show ?thesis by blast
            qed
          qed
        qed
        have "has_as_product J' D' a"
          using has_as_product_def π'.product_cone_axioms by auto
        thus "a. has_as_product J' D' a" by blast
      qed
    qed

    lemma ide_is_unary_product:
    assumes "ide a"
    shows "m n :: nat. m  n  has_as_product (discrete_category.comp {m :: nat} (n :: nat))
                                                  (λi. if i = m then a else null) a"
    proof -
      fix m n :: nat
      assume neq: "m  n"
      have "{m :: nat}  UNIV"
      proof -
        have "finite {m :: nat}" by simp
        moreover have "¬finite (UNIV :: nat set)" by simp
        ultimately show ?thesis by fastforce
      qed
      interpret J: discrete_category "{m :: nat}" n :: nat
         using neq {m :: nat}  UNIV by unfold_locales auto
      let ?D = "λi. if i = m then a else null"
      interpret D: discrete_diagram J.comp C ?D
        apply unfold_locales
        using assms J.null_char neq
             apply auto
        by metis
      interpret A: constant_functor J.comp C a
        using assms by unfold_locales auto
      show "has_as_product J.comp ?D a"
      proof (unfold has_as_product_def)
        let  = "λi :: nat. if i = m then a else null"
        interpret π: natural_transformation J.comp C A.map ?D 
          using assms J.arr_char J.dom_char J.cod_char
          by unfold_locales auto
        interpret π: cone J.comp C ?D a  ..
        interpret π: product_cone J.comp C ?D a 
        proof
          fix a' χ'
          assume χ': "D.cone a' χ'"
          interpret χ': cone J.comp C ?D a' χ' using χ' by auto
          show "∃!f. «f : a'  a»  D.cones_map f  = χ'"
          proof
            show "«χ' m : a'  a»  D.cones_map (χ' m)  = χ'"
            proof
              show 1: "«χ' m : a'  a»"
                using χ'.preserves_hom χ'.A.map_def J.arr_char J.dom_char J.cod_char
                by auto
              show "D.cones_map (χ' m)  = χ'"
              proof
                fix j
                show "D.cones_map (χ' m) (λi. if i = m then a else null) j = χ' j"
                  using J.arr_char J.dom_char J.cod_char J.ide_char π.cone_axioms comp_cod_arr
                  apply (cases "j = m")
                   apply simp
                  using χ'.is_extensional by simp
              qed
            qed
            show "f. «f : a'  a»  D.cones_map f  = χ'  f = χ' m"
            proof -
              fix f
              assume f: "«f : a'  a»  D.cones_map f  = χ'"
              show "f = χ' m"
                using assms f χ'.preserves_hom J.arr_char J.dom_char J.cod_char π.cone_axioms
                      comp_cod_arr
                by auto
            qed
          qed
        qed
        show "π. product_cone J.comp C (λi. if i = m then a else null) a π"
          using π.product_cone_axioms by blast
      qed
    qed

    lemma has_unary_products:
    assumes "card I = 1" and "I  UNIV"
    shows "has_products I"
    proof -
      obtain i where i: "I = {i}"
        using assms card_1_singletonE by blast
      obtain n where n: "n  I"
        using assms by auto
      have "has_products {1 :: nat}"
      proof (unfold has_products_def, intro conjI)
        show "{1 :: nat}  UNIV" by auto
        show "(J :: nat comp) D.
                discrete_diagram J (⋅) D  Collect (partial_composition.arr J) = {1}
                    (a. has_as_product J D a)"
        proof
          fix J :: "nat comp"
          show "D. discrete_diagram J (⋅) D  Collect (partial_composition.arr J) = {1}
                        (a. has_as_product J D a)"
          proof (intro allI impI)
            fix D
            assume D: "discrete_diagram J (⋅) D  Collect (partial_composition.arr J) = {1}"
            interpret D: discrete_diagram J C D
              using D by auto
            interpret J: discrete_category {1 :: nat} D.J.null
              by (metis D D.J.not_arr_null discrete_category.intro mem_Collect_eq)
            have 1: "has_as_product J.comp D (D 1)"
            proof -
              have "has_as_product J.comp (λi. if i = 1 then D 1 else null) (D 1)"
                using ide_is_unary_product D.preserves_ide D.is_discrete D J.null_char
                by (metis J.Null_not_in_Obj insertCI mem_Collect_eq)
              moreover have "D = (λi. if i = 1 then D 1 else null)"
              proof
                fix j
                show "D j = (if j = 1 then D 1 else null)"
                  using D D.is_extensional by auto
              qed
              ultimately show ?thesis by simp
            qed
            moreover have "J = J.comp"
            proof -
              have "j j'. J j j' = J.comp j j'"
              proof -
                fix j j'
                show "J j j' = J.comp j j'"
                  using J.comp_char D.is_discrete D
                  by (metis D.J.category_axioms D.J.ext D.J.ide_def J.null_char
                            category.seqE mem_Collect_eq)
              qed
              thus ?thesis by auto
            qed
            ultimately show "a. has_as_product J D a" by auto
          qed
        qed
      qed
      moreover have "bij_betw (λk. if k = 1 then i else n) {1 :: nat} I"
        using i by (intro bij_betwI') auto
      ultimately show "has_products I"
        using assms has_products_preserved_by_bijection by blast
    qed

  end

  subsection "Equalizers"

  text‹
    An \emph{equalizer} in a category @{term C} is a limit of a parallel pair
    of arrows in @{term C}.
›

  locale parallel_pair_diagram =
    J: parallel_pair +
    C: category C
  for C :: "'c comp"      (infixr  55)
  and f0 :: 'c
  and f1 :: 'c +
  assumes is_parallel: "C.par f0 f1"
  begin

    no_notation J.comp   (infixr  55)
    notation J.comp      (infixr J 55)

    definition map
    where "map  (λj. if j = J.Zero then C.dom f0
                       else if j = J.One then C.cod f0
                       else if j = J.j0 then f0
                       else if j = J.j1 then f1
                       else C.null)"

    lemma map_simp:
    shows "map J.Zero = C.dom f0"
    and "map J.One = C.cod f0"
    and "map J.j0 = f0"
    and "map J.j1 = f1"
    proof -
      show "map J.Zero = C.dom f0"
        using map_def by metis
      show "map J.One = C.cod f0"
        using map_def J.Zero_not_eq_One by metis
      show "map J.j0 = f0"
        using map_def J.Zero_not_eq_j0 J.One_not_eq_j0 by metis
      show "map J.j1 = f1"
        using map_def J.Zero_not_eq_j1 J.One_not_eq_j1 J.j0_not_eq_j1 by metis
    qed

  end

  sublocale parallel_pair_diagram  diagram J.comp C map
    apply unfold_locales
        apply (simp add: J.arr_char map_def)
    using map_def is_parallel J.arr_char J.cod_simp J.dom_simp
       apply auto[2]
  proof -
    show 1: "j. J.arr j  C.cod (map j) = map (J.cod j)"
      using is_parallel map_simp(1-4) J.arr_char by auto
    next
    fix j j'
    assume jj': "J.seq j' j"
    show "map (j' J j) = map j'  map j"
    proof -
      have 1: "(j = J.Zero  j'  J.One)  (j  J.Zero  j' = J.One)"
        using jj' J.seq_char by blast
      moreover have "j = J.Zero  j'  J.One  ?thesis"
        by (metis (no_types, lifting) C.arr_dom_iff_arr C.comp_arr_dom C.dom_dom
            J.comp_simp(1) jj' map_simp(1,3-4) J.arr_char is_parallel)
      moreover have "j  J.Zero  j' = J.One  ?thesis"
        by (metis (no_types, lifting) C.comp_arr_dom C.comp_cod_arr C.seqE J.comp_char jj'
            map_simp(2-4) J.arr_char is_parallel)
      ultimately show ?thesis by blast
    qed
  qed

  context parallel_pair_diagram
  begin

    definition mkCone
    where "mkCone e  λj. if J.arr j then if j = J.Zero then e else f0  e else C.null"

    abbreviation is_equalized_by
    where "is_equalized_by e  C.seq f0 e  f0  e = f1  e"

    abbreviation has_as_equalizer
    where "has_as_equalizer e  limit_cone (C.dom e) (mkCone e)"

    lemma cone_mkCone:
    assumes "is_equalized_by e"
    shows "cone (C.dom e) (mkCone e)"
    proof -
      interpret E: constant_functor J.comp C C.dom e
        apply unfold_locales using assms by auto
      show "cone (C.dom e) (mkCone e)"
        using assms mkCone_def apply unfold_locales
            apply auto[2]
        using C.dom_comp C.seqE C.cod_comp J.Zero_not_eq_One J.arr_char' J.cod_char map_def
          apply (metis (no_types, lifting) C.not_arr_null parallel_pair.cod_simp(1) preserves_arr)
      proof -
        show "j. J.arr j  map j  mkCone e (J.dom j) = mkCone e j"
        proof -
          fix j
          assume j: "J.arr j"
          have 1: "j = J.Zero  map j  mkCone e (J.dom j) = mkCone e j"
            using assms j mkCone_def C.cod_comp
            by (metis (no_types, lifting) C.comp_cod_arr J.arr_char J.dom_char map_def
                J.dom_simp(2))
          show "map j  mkCone e (J.dom j) = mkCone e j"
            by (metis (no_types, lifting) 1 C.comp_cod_arr C.seqE assms j map_simp(1)
                  mkCone_def J.dom_simp(1))
        qed
        next
        show "j. J.arr j  mkCone e (J.cod j)  E.map j = mkCone e j"
          by (metis (no_types, lifting) C.comp_arr_dom C.comp_assoc C.seqE
              J.arr_cod_iff_arr J.seqI J.seq_char assms E.map_simp mkCone_def
              J.cod_simp(1) J.dom_simp(1))
      qed
    qed

    lemma is_equalized_by_cone:
    assumes "cone a χ"
    shows "is_equalized_by (χ (J.Zero))"
    proof -
      interpret χ: cone J.comp C map a χ
        using assms by auto
      show ?thesis
        by (metis (no_types, lifting) J.arr_char J.cod_char cone_def
            χ.component_in_hom χ.is_natural_1 χ.naturality assms C.in_homE
            constant_functor.map_simp J.dom_simp(3-4) map_simp(3-4))
    qed

    lemma mkCone_cone:
    assumes "cone a χ"
    shows "mkCone (χ J.Zero) = χ"
    proof -
      interpret χ: cone J.comp C map a χ
        using assms by auto
      have 1: "is_equalized_by (χ J.Zero)"
        using assms is_equalized_by_cone by blast
      show ?thesis
      proof
        fix j
        have "j = J.Zero  mkCone (χ J.Zero) j = χ j"
          using mkCone_def χ.is_extensional by simp
        moreover have "j = J.One  j = J.j0  j = J.j1  mkCone (χ J.Zero) j = χ j"
          using J.arr_char J.cod_char J.dom_char J.seq_char mkCone_def
                χ.is_natural_1 χ.is_natural_2 χ.A.map_simp map_def
          by (metis (no_types, lifting) J.Zero_not_eq_j0 J.dom_simp(2))
        ultimately have "J.arr j  mkCone (χ J.Zero) j = χ j"
          using J.arr_char by auto
        thus "mkCone (χ J.Zero) j = χ j"
          using mkCone_def χ.is_extensional by fastforce
      qed
    qed

  end

  locale equalizer_cone =
    J: parallel_pair +
    C: category C +
    D: parallel_pair_diagram C f0 f1 +
    limit_cone J.comp C D.map "C.dom e" "D.mkCone e"
  for C :: "'c comp"      (infixr  55)
  and f0 :: 'c
  and f1 :: 'c
  and e :: 'c
  begin

    lemma equalizes:
    shows "D.is_equalized_by e"
    proof
      show "C.seq f0 e"
      proof (intro C.seqI)
        show "C.arr e" using ide_apex C.arr_dom_iff_arr by fastforce
        show "C.arr f0"
          using D.map_simp D.preserves_arr J.arr_char by metis
        show "C.dom f0 = C.cod e"
          using J.arr_char J.ide_char D.mkCone_def D.map_simp preserves_cod [of J.Zero]
          by auto
      qed
      show "f0  e = f1  e"
        using D.map_simp D.mkCone_def J.arr_char naturality [of J.j0] naturality [of J.j1]
        by force
    qed

    lemma is_universal':
    assumes "D.is_equalized_by e'"
    shows "∃!h. «h : C.dom e'  C.dom e»  e  h = e'"
    proof -
      have "D.cone (C.dom e') (D.mkCone e')"
        using assms D.cone_mkCone by blast
      moreover have 0: "D.cone (C.dom e) (D.mkCone e)" ..
      ultimately have 1: "∃!h. «h : C.dom e'  C.dom e» 
                               D.cones_map h (D.mkCone e) = D.mkCone e'"
        using is_universal [of "C.dom e'" "D.mkCone e'"] by auto
      have 2: "h. «h : C.dom e'  C.dom e» 
                    D.cones_map h (D.mkCone e) = D.mkCone e'  e  h = e'"
      proof -
        fix h
        assume h: "«h : C.dom e'  C.dom e»"
        show "D.cones_map h (D.mkCone e) = D.mkCone e'  e  h = e'"
        proof
          assume 3: "D.cones_map h (D.mkCone e) = D.mkCone e'"
          show "e  h = e'"
          proof -
            have "e' = D.mkCone e' J.Zero"
              using D.mkCone_def J.arr_char by simp
            also have "... = D.cones_map h (D.mkCone e) J.Zero"
              using 3 by simp
            also have "... = e  h"
              using 0 h D.mkCone_def J.arr_char by auto
            finally show ?thesis by auto
          qed
          next
          assume e': "e  h = e'"
          show "D.cones_map h (D.mkCone e) = D.mkCone e'"
          proof
            fix j
            have "¬J.arr j  D.cones_map h (D.mkCone e) j = D.mkCone e' j"
              using h cone_axioms D.mkCone_def by auto
            moreover have "j = J.Zero  D.cones_map h (D.mkCone e) j = D.mkCone e' j"
              using h e' is_cone D.mkCone_def J.arr_char [of J.Zero] by force
            moreover have
                "J.arr j  j  J.Zero  D.cones_map h (D.mkCone e) j = D.mkCone e' j"
              using C.comp_assoc D.mkCone_def is_cone e' h by auto
            ultimately show "D.cones_map h (D.mkCone e) j = D.mkCone e' j" by blast
          qed
        qed
      qed
      thus ?thesis using 1 by blast
    qed

    lemma induced_arrowI':
    assumes "D.is_equalized_by e'"
    shows "«induced_arrow (C.dom e') (D.mkCone e') : C.dom e'  C.dom e»"
    and "e  induced_arrow (C.dom e') (D.mkCone e') = e'"
    proof -
      interpret A': constant_functor J.comp C C.dom e'
        using assms by (unfold_locales, auto)
      have cone: "D.cone (C.dom e') (D.mkCone e')"
        using assms D.cone_mkCone [of e'] by blast
      have "e  induced_arrow (C.dom e') (D.mkCone e') =
              D.cones_map (induced_arrow (C.dom e') (D.mkCone e')) (D.mkCone e) J.Zero"
        using cone induced_arrowI(1) D.mkCone_def J.arr_char is_cone by force
      also have "... = e'"
      proof -
        have "D.cones_map (induced_arrow (C.dom e') (D.mkCone e')) (D.mkCone e) =
              D.mkCone e'"
          using cone induced_arrowI by blast
        thus ?thesis
          using J.arr_char D.mkCone_def by simp
      qed
      finally have 1: "e  induced_arrow (C.dom e') (D.mkCone e') = e'"
        by auto
      show "«induced_arrow (C.dom e') (D.mkCone e') : C.dom e'  C.dom e»"
        using 1 cone induced_arrowI by simp
      show "e  induced_arrow (C.dom e') (D.mkCone e') = e'"
        using 1 cone induced_arrowI by simp
    qed

  end

  context category
  begin

    definition has_as_equalizer
    where "has_as_equalizer f0 f1 e  par f0 f1  parallel_pair_diagram.has_as_equalizer C f0 f1 e"

    definition has_equalizers
    where "has_equalizers = (f0 f1. par f0 f1  (e. has_as_equalizer f0 f1 e))"

    lemma has_as_equalizerI [intro]:
    assumes "par f g" and "seq f e" and "f  e = g  e"
    and "e'. seq f e'; f  e' = g  e'  ∃!h. e  h = e'"
    shows "has_as_equalizer f g e"
    proof (unfold has_as_equalizer_def, intro conjI)
      show "arr f" and "arr g" and "dom f = dom g" and "cod f = cod g"
        using assms(1) by auto
      interpret J: parallel_pair .
      interpret D: parallel_pair_diagram C f g
        using assms(1) by unfold_locales
      show "D.has_as_equalizer e"
      proof -
        let  = "D.mkCone e"
        let ?a = "dom e"
        interpret χ: cone J.comp C D.map ?a 
           using assms(2-3) D.cone_mkCone [of e] by simp
        interpret χ: limit_cone J.comp C D.map ?a 
        proof
          fix a' χ'
          assume χ': "D.cone a' χ'"
          interpret χ': cone J.comp C D.map a' χ'
            using χ' by blast
          have "seq f (χ' J.Zero)"
            using J.ide_char J.arr_char χ'.preserves_hom
            by (metis (no_types, lifting) D.map_simp(3) χ'.is_natural_1
              χ'.natural_transformation_axioms natural_transformation.preserves_reflects_arr
              parallel_pair.dom_simp(3))
          moreover have "f  (χ' J.Zero) = g  (χ' J.Zero)"
            using χ' D.is_equalized_by_cone by blast
          ultimately have 1: "∃!h. e  h = χ' J.Zero"
            using assms by blast
          obtain h where h: "e  h = χ' J.Zero"
            using 1 by blast
          have 2: "D.is_equalized_by e"
          using assms(2-3) by blast
          have "«h : a'  dom e»  D.cones_map h (D.mkCone e) = χ'"
          proof
            show 3: "«h : a'  dom e»"
              using h χ'.preserves_dom
              by (metis (no_types, lifting) χ'.component_in_hom seq f (χ' J.Zero)
                category.has_codomain_iff_arr category.seqE category_axioms cod_in_codomains
                domains_comp in_hom_def parallel_pair.arr_char)
            show "D.cones_map h (D.mkCone e) = χ'"
            proof
              fix j
              have "D.cone (cod h) (D.mkCone e)"
                using 2 3 D.cone_mkCone by auto
              thus "D.cones_map h (D.mkCone e) j = χ' j"
                using h 2 3 D.cone_mkCone [of e] J.arr_char D.mkCone_def comp_assoc
                apply (cases "J.arr j")
                 apply simp_all
                 apply (metis (no_types, lifting) D.mkCone_cone χ')
                using χ'.is_extensional
                by presburger
            qed
          qed
          hence "h. «h : a'  dom e»  D.cones_map h (D.mkCone e) = χ'"
            by blast
          moreover have "h'. «h' : a'  dom e»  D.cones_map h' (D.mkCone e) = χ'  h' = h"
          proof (elim conjE)
            fix h'
            assume h': "«h' : a'  dom e»"
            assume eq: "D.cones_map h' (D.mkCone e) = χ'"
            have "e  h' = χ' J.Zero"
              using eq D.cone_mkCone D.mkCone_def χ'.preserves_reflects_arr χ.cone_axioms
                    seq f (χ' J.Zero) eq h' in_homE mem_Collect_eq restrict_apply seqE
              apply simp
              by fastforce
            moreover have "∃!h. e  h = χ' J.Zero"
              using assms(2,4) 1 category.seqI by blast
            ultimately show "h' = h"
              using h by auto
          qed
          ultimately show "∃!h. «h : a'  dom e»  D.cones_map h (D.mkCone e) = χ'"
            by blast
        qed
        show "D.has_as_equalizer e"
          using assms χ.limit_cone_axioms by blast
      qed
    qed

  end

  section "Limits by Products and Equalizers"
 
  text‹
    A category with equalizers has limits of shape @{term J} if it has products
    indexed by the set of arrows of @{term J} and the set of objects of @{term J}.
    The proof is patterned after cite"MacLane", Theorem 2, page 109:
    \begin{quotation}
       \noindent
       ``The limit of F: J → C› is the equalizer e›
       of f, g: Πi Fi → Πu Fcod u (u ∈ arr J, i ∈ J)›
       where pu f = pcod u, pu g = Fu o pdom u;
       the limiting cone μ› is μj = pj e›, for j ∈ J›.''
    \end{quotation}
›

  locale category_with_equalizers =
    category C
  for C :: "'c comp"      (infixr  55) +
  assumes has_equalizers: "has_equalizers"
  begin

    lemma has_limits_if_has_products:
    fixes J :: "'j comp"  (infixr J 55)
    assumes "category J" and "has_products (Collect (partial_composition.ide J))"
    and "has_products (Collect (partial_composition.arr J))"
    shows "has_limits_of_shape J"
    proof (unfold has_limits_of_shape_def)
      interpret J: category J using assms(1) by auto
      have "D. diagram J C D  (a χ. limit_cone J C D a χ)"
      proof -
        fix D
        assume D: "diagram J C D"
        interpret D: diagram J C D using D by auto

        text‹
          First, construct the two required products and their cones.
›
        interpret Obj: discrete_category Collect J.ide J.null
          using J.not_arr_null J.ideD(1) mem_Collect_eq by (unfold_locales, blast)
        interpret Δo: discrete_diagram_from_map Collect J.ide C D J.null
          using D.preserves_ide by (unfold_locales, auto)
        have "p. has_as_product Obj.comp Δo.map p"
          using assms(2) Δo.diagram_axioms has_products_def Obj.arr_char
          by (metis (no_types, lifting) Collect_cong Δo.discrete_diagram_axioms mem_Collect_eq)
        from this obtain Πo πo where πo: "product_cone Obj.comp C Δo.map Πo πo"
           using ex_productE [of Obj.comp Δo.map] by auto
        interpret πo: product_cone Obj.comp C Δo.map Πo πo using πo by auto
        have πo_in_hom: "j. Obj.arr j  «πo j : Πo  D j»"
          using πo.preserves_dom πo.preserves_cod Δo.map_def by auto

        interpret Arr: discrete_category Collect J.arr J.null
          using J.not_arr_null by (unfold_locales, blast)
        interpret Δa: discrete_diagram_from_map Collect J.arr C D o J.cod J.null
          by (unfold_locales, auto)
        have "p. has_as_product Arr.comp Δa.map p"
          using assms(3) has_products_def [of "Collect J.arr"] Δa.discrete_diagram_axioms
          by blast
        from this obtain Πa πa where πa: "product_cone Arr.comp C Δa.map Πa πa"
          using ex_productE [of Arr.comp Δa.map] by auto
        interpret πa: product_cone Arr.comp C Δa.map Πa πa using πa by auto
        have πa_in_hom: "j. Arr.arr j  «πa j : Πa  D (J.cod j)»"
          using πa.preserves_cod πa.preserves_dom Δa.map_def by auto

        text‹
           Next, construct a parallel pair of arrows f, g: Πo → Πa›
           that expresses the commutativity constraints imposed by the diagram.
›
        interpret Πo: constant_functor Arr.comp C Πo
          using πo.ide_apex by (unfold_locales, auto)
        let  = "λj. if Arr.arr j then πo (J.cod j) else null"
        interpret χ: cone Arr.comp C Δa.map Πo 
          using πo.ide_apex πo_in_hom Δa.map_def Δo.map_def Δo.is_discrete πo.is_natural_2
                comp_cod_arr
          by (unfold_locales, auto)

        let ?f = "πa.induced_arrow Πo "
        have f_in_hom: "«?f : Πo  Πa»"
          using χ.cone_axioms πa.induced_arrowI by blast
        have f_map: "Δa.cones_map ?f πa = "
          using χ.cone_axioms πa.induced_arrowI by blast
        have ff: "j. J.arr j  πa j  ?f = πo (J.cod j)"
          using χ.component_in_hom πa.induced_arrowI' πo.ide_apex by auto

        let ?χ' = "λj. if Arr.arr j then D j  πo (J.dom j) else null"
        interpret χ': cone Arr.comp C Δa.map Πo ?χ'
          using πo.ide_apex πo_in_hom Δo.map_def Δa.map_def comp_arr_dom comp_cod_arr
          by (unfold_locales, auto)
        let ?g = "πa.induced_arrow Πo ?χ'"
        have g_in_hom: "«?g : Πo  Πa»"
          using χ'.cone_axioms πa.induced_arrowI by blast
        have g_map: "Δa.cones_map ?g πa = ?χ'"
          using χ'.cone_axioms πa.induced_arrowI by blast
        have gg: "j. J.arr j  πa j  ?g = D j  πo (J.dom j)"
          using χ'.component_in_hom πa.induced_arrowI' πo.ide_apex by force

        interpret PP: parallel_pair_diagram C ?f ?g
          using f_in_hom g_in_hom
          by (elim in_homE, unfold_locales, auto)
        from PP.is_parallel obtain e where equ: "PP.has_as_equalizer e"
          using has_equalizers has_equalizers_def has_as_equalizer_def by blast
        interpret EQU: limit_cone PP.J.comp C PP.map dom e PP.mkCone e
          using equ by auto
        interpret EQU: equalizer_cone C ?f ?g e ..

        text‹
          An arrow @{term h} with @{term "cod h = Πo"} equalizes @{term f} and @{term g}
          if and only if it satisfies the commutativity condition required for a cone over
          @{term D}.
›
        have E: "h. «h : dom h  Πo» 
                   ?f  h = ?g  h  (j. J.arr j   j  h = ?χ' j  h)"
        proof
          fix h
          assume h: "«h : dom h  Πo»"
          show "?f  h = ?g  h  j. J.arr j   j  h = ?χ' j  h"
          proof -
            assume E: "?f  h = ?g  h"
            have "j. J.arr j   j  h = ?χ' j  h"
            proof -
              fix j
              assume j: "J.arr j"
              have " j  h = Δa.cones_map ?f πa j  h"
                using j f_map by fastforce
              also have "... = πa j  ?f  h"
                using j f_in_hom Δa.map_def πa.is_cone comp_assoc by auto
              also have "... = πa j  ?g  h"
                using j E by simp
              also have "... = Δa.cones_map ?g πa j  h"
                using j g_in_hom Δa.map_def πa.is_cone comp_assoc by auto
              also have "... = ?χ' j  h"
                using j g_map by force
              finally show " j  h = ?χ' j  h" by auto
            qed
            thus "j. J.arr j   j  h = ?χ' j  h" by blast
          qed
          show "j. J.arr j   j  h = ?χ' j  h  ?f  h = ?g  h"
          proof -
            assume 1: "j. J.arr j   j  h = ?χ' j  h"
            have 2: "j. j  Collect J.arr  πa j  ?f  h = πa j  ?g  h"
            proof -
              fix j
              assume j: "j  Collect J.arr"
              have "πa j  ?f  h = (πa j  ?f)  h"
                using comp_assoc by simp
              also have "... =  j  h"
                using ff j by force
              also have "... = ?χ' j  h"
                using 1 j by auto
              also have "... = (πa j  ?g)  h"
                using gg j by force
              also have "... = πa j  ?g  h"
                using comp_assoc by simp
              finally show "πa j  ?f  h = πa j  ?g  h"
                by auto
            qed
            show "C ?f h = C ?g h"
            proof -
              have "j. Arr.arr j  «πa j  ?f  h : dom h  Δa.map j»"
                using f_in_hom h πa_in_hom by (elim in_homE, auto)
              hence 3: "∃!k. «k : dom h  Πa»  (j. Arr.arr j  πa j  k = πa j  ?f  h)"
                using h πa πa.is_universal' [of "dom h" "λj. πa j  ?f  h"] Δa.map_def
                      ide_dom [of h]
                by blast
              have 4: "P x x'. ∃!k. P k x  P x x  P x' x  x' = x" by auto
              let ?P = "λ k x. «k : dom h  Πa» 
                               (j. j  Collect J.arr  πa j  k = πa j  x)"
              have "?P (?g  h) (?g  h)"
                using g_in_hom h by force
              moreover have "?P (?f  h) (?g  h)"
                using 2 f_in_hom g_in_hom h by force
              ultimately show ?thesis
                using 3 4 [of ?P "?f  h" "?g  h"] by auto
            qed
          qed
        qed
        have E': "e. «e : dom e  Πo» 
                   ?f  e = ?g  e 
                   (j. J.arr j 
                           (D (J.cod j)  πo (J.cod j)  e)  dom e = D j  πo (J.dom j)  e)"
        proof -
          have 1: "e j. «e : dom e  Πo»  J.arr j 
                           j  e = (D (J.cod j)  πo (J.cod j)  e)  dom e"
          proof -
            fix e j
            assume e: "«e : dom e  Πo»"
            assume j: "J.arr j"
            have "«πo (J.cod j)  e : dom e  D (J.cod j)»"
              using e j πo_in_hom by auto
            thus " j  e = (D (J.cod j)  πo (J.cod j)  e)  dom e"
              using j comp_arr_dom comp_cod_arr by (elim in_homE, auto)
          qed
          have 2: "e j. «e : dom e  Πo»  J.arr j  ?χ' j  e = D j  πo (J.dom j)  e"
            by (simp add: comp_assoc)
          show "e. «e : dom e  Πo» 
                   ?f  e = ?g  e 
                     (j. J.arr j 
                           (D (J.cod j)  πo (J.cod j)  e)  dom e = D j  πo (J.dom j)  e)"
            using 1 2 E by presburger
        qed
        text‹
          The composites of @{term e} with the projections from the product @{term Πo}
          determine a limit cone @{term μ} for @{term D}.  The component of @{term μ}
          at an object @{term j} of @{term[source=true] J} is the composite @{term "C (πo j) e"}.
          However, we need to extend @{term μ} to all arrows @{term j} of @{term[source=true] J},
          so the correct definition is @{term "μ j = C (D j) (C (πo (J.dom j)) e)"}.
›
        have e_in_hom: "«e : dom e  Πo»"
          using EQU.equalizes f_in_hom in_homI
          by (metis (no_types, lifting) seqE in_homE)
        have e_map: "C ?f e = C ?g e"
          using EQU.equalizes f_in_hom in_homI by fastforce
        interpret domE: constant_functor J C dom e
          using e_in_hom by (unfold_locales, auto)
        let  = "λj. if J.arr j then D j  πo (J.dom j)  e else null"
        have μ: "j. J.arr j  « j : dom e  D (J.cod j)»"
        proof -
          fix j
          assume j: "J.arr j"
          show "« j : dom e  D (J.cod j)»"
            using j e_in_hom πo_in_hom [of "J.dom j"] by auto
        qed
        interpret μ: cone J C D dom e 
          using μ comp_cod_arr e_in_hom e_map E'
            apply unfold_locales
               apply auto
          by (metis D.as_nat_trans.is_natural_1 comp_assoc)
        text‹
          If @{term τ} is any cone over @{term D} then @{term τ} restricts to a cone over
          @{term Δo} for which the induced arrow to @{term Πo} equalizes @{term f} and @{term g}.
›
        have R: "a τ. cone J C D a τ 
                        cone Obj.comp C Δo.map a (Δo.mkCone τ) 
                        ?f  πo.induced_arrow a (Δo.mkCone τ)
                           = ?g  πo.induced_arrow a (Δo.mkCone τ)"
        proof -
          fix a τ
          assume cone_τ: "cone J C D a τ"
          interpret τ: cone J C D a τ using cone_τ by auto
          interpret A: constant_functor Obj.comp C a
            using τ.ide_apex by (unfold_locales, auto)
          interpret τo: cone Obj.comp C Δo.map a Δo.mkCone τ
            using A.value_is_ide Δo.map_def comp_cod_arr comp_arr_dom
            by (unfold_locales, auto)
          let ?e = "πo.induced_arrow a (Δo.mkCone τ)"
          have mkCone_τ: "Δo.mkCone τ  Δo.cones a"
            using τo.cone_axioms by auto
          have e: "«?e : a  Πo»"
            using mkCone_τ πo.induced_arrowI by simp
          have ee: "j. J.ide j  πo j  ?e = τ j"
          proof -
            fix j
            assume j: "J.ide j"
            have "πo j  ?e = Δo.cones_map ?e πo j"
              using j e πo.cone_axioms by force
            also have "... = Δo.mkCone τ j"
              using j mkCone_τ πo.induced_arrowI [of "Δo.mkCone τ" a] by fastforce
            also have "... = τ j"
              using j by simp
            finally show "πo j  ?e = τ j" by auto
          qed
          have "j. J.arr j 
                      (D (J.cod j)  πo (J.cod j)  ?e)  dom ?e = D j  πo (J.dom j)  ?e"
          proof -
            fix j
            assume j: "J.arr j"
            have 1: "«πo (J.cod j) : Πo  D (J.cod j)»" using j πo_in_hom by simp
            have 2: "(D (J.cod j)  πo (J.cod j)  ?e)  dom ?e
                        = D (J.cod j)  πo (J.cod j)  ?e"
            proof -
              have "seq (D (J.cod j)) (πo (J.cod j))"
                using j 1 by auto
              moreover have "seq (πo (J.cod j)) ?e"
                using j e by fastforce
              ultimately show ?thesis using comp_arr_dom by auto
            qed
            also have 3: "... = πo (J.cod j)  ?e"
              using j e 1 comp_cod_arr by (elim in_homE, auto)
            also have "... = D j  πo (J.dom j)  ?e"
              using j e ee 2 3 τ.naturality τ.A.map_simp τ.ide_apex comp_cod_arr by auto
            finally show "(D (J.cod j)  πo (J.cod j)  ?e)  dom ?e = D j  πo (J.dom j)  ?e"
              by auto
          qed
          hence "C ?f ?e = C ?g ?e"
            using E' πo.induced_arrowI τo.cone_axioms mem_Collect_eq by blast
          thus "cone Obj.comp C Δo.map a (Δo.mkCone τ)  C ?f ?e = C ?g ?e"
            using τo.cone_axioms by auto
        qed
        text‹
          Finally, show that @{term μ} is a limit cone.
›
        interpret μ: limit_cone J C D dom e 
        proof
          fix a τ
          assume cone_τ: "cone J C D a τ"
          interpret τ: cone J C D a τ using cone_τ by auto
          interpret A: constant_functor Obj.comp C a
            using τ.ide_apex by unfold_locales auto
          have cone_τo: "cone Obj.comp C Δo.map a (Δo.mkCone τ)"
            using A.value_is_ide Δo.map_def D.preserves_ide comp_cod_arr comp_arr_dom
                  τ.preserves_hom
            by unfold_locales auto
          show "∃!h. «h : a  dom e»  D.cones_map h  = τ"
          proof
            let ?e' = "πo.induced_arrow a (Δo.mkCone τ)"
            have e'_in_hom: "«?e' : a  Πo»"
              using cone_τ R πo.induced_arrowI by auto
            have e'_map: "?f  ?e' = ?g  ?e'  Δo.cones_map ?e' πo = Δo.mkCone τ"
              using cone_τ R πo.induced_arrowI [of "Δo.mkCone τ" a] by auto
            have equ: "PP.is_equalized_by ?e'"
              using e'_map e'_in_hom f_in_hom seqI' by blast
            let ?h = "EQU.induced_arrow a (PP.mkCone ?e')"
            have h_in_hom: "«?h : a  dom e»"
              using EQU.induced_arrowI PP.cone_mkCone [of ?e'] e'_in_hom equ by fastforce
            have h_map: "PP.cones_map ?h (PP.mkCone e) = PP.mkCone ?e'"
              using EQU.induced_arrowI [of "PP.mkCone ?e'" a] PP.cone_mkCone [of ?e']
                    e'_in_hom equ
              by fastforce
            have 3: "D.cones_map ?h  = τ"
            proof
              fix j
              have "¬J.arr j  D.cones_map ?h  j = τ j"
                using h_in_hom μ.cone_axioms cone_τ τ.is_extensional by force
              moreover have "J.arr j  D.cones_map ?h  j = τ j"
              proof -
                fix j
                assume j: "J.arr j"
                have 1: "«πo (J.dom j)  e : dom e  D (J.dom j)»"
                  using j e_in_hom πo_in_hom [of "J.dom j"] by auto
                have "D.cones_map ?h  j =  j  ?h"
                  using h_in_hom j μ.cone_axioms by auto
                also have "... = D j  (πo (J.dom j)  e)  ?h"
                  using j comp_assoc by simp
                also have "... = D j  τ (J.dom j)"
                proof -
                  have "(πo (J.dom j)  e)  ?h = τ (J.dom j)"
                  proof -
                    have "(πo (J.dom j)  e)  ?h = πo (J.dom j)  e  ?h"
                      using j 1 e_in_hom h_in_hom πo arrI comp_assoc by auto
                    also have "... = πo (J.dom j)  ?e'"
                      using equ e'_in_hom EQU.induced_arrowI' [of ?e'] by auto
                    also have "... = Δo.cones_map ?e' πo (J.dom j)"
                      using j e'_in_hom πo.cone_axioms by auto
                    also have "... = τ (J.dom j)"
                      using j e'_map by simp
                    finally show ?thesis by auto
                  qed
                  thus ?thesis by simp
                qed
                also have "... = τ j"
                  using j τ.is_natural_1 by simp
                finally show "D.cones_map ?h  j = τ j" by auto
              qed
              ultimately show "D.cones_map ?h  j = τ j" by auto
            qed
            show "«?h : a  dom e»  D.cones_map ?h  = τ"
              using h_in_hom 3 by simp
            show "h'. «h' : a  dom e»  D.cones_map h'  = τ  h' = ?h"
            proof -
              fix h'
              assume h': "«h' : a  dom e»  D.cones_map h'  = τ"
              have h'_in_hom: "«h' : a  dom e»" using h' by simp
              have h'_map: "D.cones_map h'  = τ" using h' by simp
              show "h' = ?h"
              proof -
                have 1: "«e  h' : a  Πo»  ?f  e  h' = ?g  e  h' 
                         Δo.cones_map (C e h') πo = Δo.mkCone τ"
                proof -
                  have 2: "«e  h' : a  Πo»" using h'_in_hom e_in_hom by auto
                  moreover have "?f  e  h' = ?g  e  h'"
                    by (metis (no_types, lifting) EQU.equalizes comp_assoc)
                  moreover have "Δo.cones_map (e  h') πo = Δo.mkCone τ"
                  proof
                    have "Δo.cones_map (e  h') πo = Δo.cones_map h' (Δo.cones_map e πo)"
                      using πo.cone_axioms e_in_hom h'_in_hom Δo.cones_map_comp [of e h']
                      by fastforce
                    fix j
                    have "¬Obj.arr j  Δo.cones_map (e  h') πo j = Δo.mkCone τ j"
                      using 2 e_in_hom h'_in_hom πo.cone_axioms by auto
                    moreover have "Obj.arr j  Δo.cones_map (e  h') πo j = Δo.mkCone τ j"
                    proof -
                      assume j: "Obj.arr j"
                      have "Δo.cones_map (e  h') πo j = πo j  e  h'"
                        using 2 j πo.cone_axioms by auto
                      also have "... = (πo j  e)  h'"
                        using comp_assoc by auto
                      also have "... = Δo.mkCone  j  h'"
                        using j e_in_hom πo_in_hom comp_ide_arr [of "D j" "πo j  e"]
                        by fastforce
                      also have "... = Δo.mkCone τ j"
                        using j h' μ.cone_axioms mem_Collect_eq by auto
                      finally show "Δo.cones_map (e  h') πo j = Δo.mkCone τ j" by auto
                    qed
                    ultimately show "Δo.cones_map (e  h') πo j = Δo.mkCone τ j" by auto
                  qed
                  ultimately show ?thesis by auto
                qed
                have "«e  h' : a  Πo»" using 1 by simp
                moreover have "e  h' = ?e'"
                  using 1 cone_τo e'_in_hom e'_map πo.is_universal πo by blast
                ultimately show "h' = ?h"
                  using 1 h'_in_hom h'_map EQU.is_universal' [of "e  h'"]
                        EQU.induced_arrowI' [of ?e'] equ
                  by (elim in_homE) auto
              qed
            qed
          qed
        qed
        have "limit_cone J C D (dom e) " ..
        thus "a μ. limit_cone J C D a μ" by auto
      qed
      thus "D. diagram J C D  (a μ. limit_cone J C D a μ)" by blast
    qed

  end

  section "Limits in a Set Category"

  text‹
    In this section, we consider the special case of limits in a set category.
›

  locale diagram_in_set_category =
    J: category J +
    S: set_category S is_set +
    diagram J S D
  for J :: "'j comp"      (infixr J 55)
  and S :: "'s comp"      (infixr  55)
  and is_set :: "'s set  bool"
  and D :: "'j  's"
  begin

    notation S.in_hom («_ : _  _»)

    text‹
      An object @{term a} of a set category @{term[source=true] S} is a limit of a diagram in
      @{term[source=true] S} if and only if there is a bijection between the set
      @{term "S.hom S.unity a"} of points of @{term a} and the set of cones over the diagram
      that have apex @{term S.unity}.
›

    lemma limits_are_sets_of_cones:
    shows "has_as_limit a  S.ide a  (φ. bij_betw φ (S.hom S.unity a) (cones S.unity))"
    proof
      text‹
        If has_limit a›, then by the universal property of the limit cone,
        composition in @{term[source=true] S} yields a bijection between @{term "S.hom S.unity a"}
        and @{term "cones S.unity"}.
›
      assume a: "has_as_limit a"
      hence "S.ide a"
        using limit_cone_def cone.ide_apex by metis
      from a obtain χ where χ: "limit_cone a χ" by auto
      interpret χ: limit_cone J S D a χ using χ by auto
      have "bij_betw (λf. cones_map f χ) (S.hom S.unity a) (cones S.unity)"
        using χ.bij_betw_hom_and_cones S.ide_unity by simp
      thus "S.ide a  (φ. bij_betw φ (S.hom S.unity a) (cones S.unity))"
        using S.ide a by blast
      next
      text‹
        Conversely, an arbitrary bijection @{term φ} between @{term "S.hom S.unity a"}
        and cones unity extends pointwise to a natural bijection @{term "Φ a'"} between
        @{term "S.hom a' a"} and @{term "cones a'"}, showing that @{term a} is a limit.

        In more detail, the hypotheses give us a correspondence between points of @{term a}
        and cones with apex @{term "S.unity"}.  We extend this to a correspondence between
        functions to @{term a} and general cones, with each arrow from @{term a'} to @{term a}
        determining a cone with apex @{term a'}.  If @{term "f  hom a' a"} then composition
        with @{term f} takes each point @{term y} of @{term a'} to the point @{term "S f y"}
        of @{term a}.  To this we may apply the given bijection @{term φ} to obtain
        @{term "φ (S f y)  cones S.unity"}.  The component @{term "φ (S f y) j"} at @{term j}
        of this cone is a point of @{term "S.cod (D j)"}.  Thus, @{term "f  hom a' a"} determines
        a cone @{term χf} with apex @{term a'} whose component at @{term j} is the
        unique arrow @{term "χf j"} of @{term[source=true] S} such that
        @{term "χf j  hom a' (cod (D j))"} and @{term "S (χf j) y = φ (S f y) j"}
        for all points @{term y} of @{term a'}.
        The cone @{term χa} corresponding to @{term "a  S.hom a a"} is then a limit cone.
›
      assume a: "S.ide a  (φ. bij_betw φ (S.hom S.unity a) (cones S.unity))"
      hence ide_a: "S.ide a" by auto
      show "has_as_limit a"
      proof -
        from a obtain φ where φ: "bij_betw φ (S.hom S.unity a) (cones S.unity)" by blast
        have X: "f j y.  «f : S.dom f  a»; J.arr j; «y : S.unity  S.dom f» 
                                 «φ (f  y) j : S.unity  S.cod (D j)»"
        proof -
          fix f j y
          assume f: "«f : S.dom f  a»" and j: "J.arr j" and y: "«y : S.unity  S.dom f»"
          interpret χ: cone J S D S.unity φ (S f y)
            using f y φ bij_betw_imp_funcset funcset_mem by blast
          show "«φ (f  y) j : S.unity  S.cod (D j)»" using j by auto
        qed
        text‹
          We want to define the component @{term "χj  S.hom (S.dom f) (S.cod (D j))"}
          at @{term j} of a cone by specifying how it acts by composition on points
          @{term "y  S.hom S.unity (S.dom f)"}.  We can do this because @{term[source=true] S}
          is a set category.
›
        let ?P = "λf j χj. «χj : S.dom f  S.cod (D j)» 
                           (y. «y : S.unity  S.dom f»  χj  y = φ (f  y) j)"
        let  = "λf j. if J.arr j then (THE χj. ?P f j χj) else S.null"
        have χ: "f j.  «f : S.dom f  a»; J.arr j   ?P f j ( f j)"
        proof -
          fix b f j
          assume f: "«f : S.dom f  a»" and j: "J.arr j"
          interpret B: constant_functor J S S.dom f
            using f by (unfold_locales) auto
          have "(λy. φ (f  y) j)  S.hom S.unity (S.dom f)  S.hom S.unity (S.cod (D j))"
            using f j X Pi_I' by simp
          hence "∃!χj. ?P f j χj"
            using f j S.fun_complete' by (elim S.in_homE) auto
          thus "?P f j ( f j)" using j theI' [of "?P f j"] by simp
        qed
        text‹
          The arrows @{term "χ f j"} are in fact the components of a cone with apex
          @{term "S.dom f"}.
›
        have cone: "f. «f : S.dom f  a»  cone (S.dom f) ( f)"
        proof -
          fix f
          assume f: "«f : S.dom f  a»"
          interpret B: constant_functor J S S.dom f
            using f by unfold_locales auto
          show "cone (S.dom f) ( f)"
          proof
            show "j. ¬J.arr j   f j = S.null" by simp
            fix j
            assume j: "J.arr j"
            have 0: "« f j : S.dom f  S.cod (D j)»" using f j χ by simp
            show "S.dom ( f j) = B.map (J.dom j)" using f j χ by auto
            show "S.cod ( f j) = D (J.cod j)" using f j χ by auto
            have par2: "S.par ( f (J.cod j)  B.map j) ( f j)"
              using f j 0 χ [of f "J.cod j"] by (elim S.in_homE, auto)
            have nat: "y. «y : S.unity  S.dom f» 
                              (D j   f (J.dom j))  y =  f j  y 
                              ( f (J.cod j)  B.map j)  y =  f j  y"
            proof -
              fix y
              assume y: "«y : S.unity  S.dom f»"
              show "(D j   f (J.dom j))  y =  f j  y 
                    ( f (J.cod j)  B.map j)  y =  f j  y"
              proof
                have 1: "φ (f  y)  cones S.unity"
                  using f y φ bij_betw_imp_funcset PiE
                        S.seqI S.cod_comp S.dom_comp mem_Collect_eq
                  by fastforce
                interpret χ: cone J S D S.unity φ (f  y)
                  using 1 by simp
                show "(D j   f (J.dom j))  y =  f j  y"
                  using J.arr_dom S.comp_assoc χ χ.is_natural_1 f j y by presburger
                have "( f (J.cod j)  B.map j)  y =  f (J.cod j)  y"
                  using j B.map_simp par2 B.value_is_ide S.comp_arr_ide
                  by (metis (no_types, lifting))
                also have "... = φ (f  y) (J.cod j)"
                  using f y χ χ.is_extensional by simp
                also have "... = φ (f  y) j"
                  using j χ.is_natural_2
                  by (metis J.arr_cod χ.A.map_simp J.cod_cod)
                also have "... =  f j  y"
                  using f y χ χ.is_extensional by simp
                finally show "( f (J.cod j)  B.map j)  y =  f j  y" by auto
              qed
            qed
            show "D j   f (J.dom j) =  f j"
            proof -
              have "S.par (D j   f (J.dom j)) ( f j)"
                using f j 0 χ [of f "J.dom j"] by (elim S.in_homE, auto)
              thus ?thesis
                using nat 0
                apply (intro S.arr_eqI'SC [of "D j   f (J.dom j)" " f j"])
                 apply force
                by auto
            qed
            show " f (J.cod j)  B.map j =  f j"
              using par2 nat 0 f j χ
              apply (intro S.arr_eqI'SC [of " f (J.cod j)  B.map j" " f j"])
               apply force
              by (metis (no_types, lifting) S.in_homE)
          qed
        qed
        interpret χa: cone J S D a  a using a cone [of a] by fastforce
        text‹
          Finally, show that χ a› is a limit cone.
›
        interpret χa: limit_cone J S D a  a
        proof
          fix a' χ'
          assume cone_χ': "cone a' χ'"
          interpret χ': cone J S D a' χ' using cone_χ' by auto
          show "∃!f. «f : a'  a»  cones_map f ( a) = χ'"
          proof
            let  = "inv_into (S.hom S.unity a) φ"
            have ψ: "  cones S.unity  S.hom S.unity a"
              using φ bij_betw_inv_into bij_betwE by blast
            let ?P = "λf. «f : a'  a» 
                          (y. y  S.hom S.unity a'  f  y =  (cones_map y χ'))"
            have 1: "∃!f. ?P f"
            proof -
              have "(λy.  (cones_map y χ'))  S.hom S.unity a'  S.hom S.unity a"
              proof
                fix x
                assume "x  S.hom S.unity a'"
                hence "«x : S.unity  a'»" by simp
                hence "cones_map x  cones a'  cones S.unity"
                  using cones_map_mapsto [of x] by (elim S.in_homE) auto
                hence "cones_map x χ'  cones S.unity"
                  using cone_χ' by blast
                thus " (cones_map x χ')  S.hom S.unity a"
                  using ψ by auto
              qed
              thus ?thesis
                using S.fun_complete' a χ'.ide_apex by simp
            qed
            let ?f = "THE f. ?P f"
            have f: "?P ?f" using 1 theI' [of ?P] by simp
            have f_in_hom: "«?f : a'  a»" using f by simp
            have f_map: "cones_map ?f ( a) = χ'"
            proof -
              have 1: "cone a' (cones_map ?f ( a))"
              proof -
                have "cones_map ?f  cones a  cones a'"
                  using f_in_hom cones_map_mapsto [of ?f] by (elim S.in_homE) auto
                hence "cones_map ?f ( a)  cones a'"
                  using χa.cone_axioms by blast
                thus ?thesis by simp
              qed
              interpret fχa: cone J S D a' cones_map ?f ( a)
                using 1 by simp
              show ?thesis
              proof
                fix j
                have "¬J.arr j  cones_map ?f ( a) j = χ' j"
                  using 1 χ'.is_extensional fχa.is_extensional by presburger
                moreover have "J.arr j  cones_map ?f ( a) j = χ' j"
                proof -
                  assume j: "J.arr j"
                  show "cones_map ?f ( a) j = χ' j"
                  proof (intro S.arr_eqI'SC [of "cones_map ?f ( a) j" "χ' j"])
                    show par: "S.par (cones_map ?f ( a) j) (χ' j)"
                      using j χ'.preserves_cod χ'.preserves_dom χ'.preserves_reflects_arr
                            fχa.preserves_cod fχa.preserves_dom fχa.preserves_reflects_arr
                      by presburger
                    fix y
                    assume "«y : S.unity  S.dom (cones_map ?f ( a) j)»"
                    hence y: "«y : S.unity  a'»"
                      using j fχa.preserves_dom by simp
                    have 1: "« a j : a  D (J.cod j)»"
                      using j χa.preserves_hom by force
                    have 2: "«?f  y : S.unity  a»"
                      using f_in_hom y by blast
                    have "cones_map ?f ( a) j  y = ( a j  ?f)  y"
                    proof -
                      have "S.cod ?f = a" using f_in_hom by blast
                      thus ?thesis using j χa.cone_axioms by simp
                    qed
                    also have "... =  a j  ?f  y"
                      using 1 j y f_in_hom S.comp_assoc S.seqI' by blast
                    also have "... = φ (a  ?f  y) j"
                      using 1 2 ide_a f j y χ [of a] by (simp add: S.ide_in_hom)
                    also have "... = φ (?f  y) j"
                      using a 2 y S.comp_cod_arr by (elim S.in_homE, auto)
                    also have "... = φ ( (cones_map y χ')) j"
                      using j y f by simp
                    also have "... = cones_map y χ' j"
                    proof -
                      have "cones_map y χ'  cones S.unity"
                        using cone_χ' y cones_map_mapsto by force
                      hence "φ ( (cones_map y χ')) = cones_map y χ'"
                        using φ bij_betw_inv_into_right [of φ] by simp
                      thus ?thesis by auto
                    qed
                    also have "... = χ' j  y"
                      using cone_χ' j y by auto
                    finally show "cones_map ?f ( a) j  y = χ' j  y"
                      by auto
                  qed
                qed
                ultimately show "cones_map ?f ( a) j = χ' j" by blast
              qed
            qed
            show "«?f : a'  a»  cones_map ?f ( a) = χ'"
              using f_in_hom f_map by simp
            show "f'. «f' : a'  a»  cones_map f' ( a) = χ'  f' = ?f"
            proof -
              fix f'
              assume f': "«f' : a'  a»  cones_map f' ( a) = χ'"
              have f'_in_hom: "«f' : a'  a»" using f' by simp
              have f'_map: "cones_map f' ( a) = χ'" using f' by simp
              show "f' = ?f"
              proof (intro S.arr_eqI'SC [of f' ?f])
                show "S.par f' ?f"
                  using f_in_hom f'_in_hom by (elim S.in_homE, auto)
                show "y'. «y' : S.unity  S.dom f'»  f'  y' = ?f  y'"
                proof -
                  fix y'
                  assume y': "«y' : S.unity  S.dom f'»"
                  have 0: "φ (f'  y') = cones_map y' χ'"
                  proof
                    fix j
                    have 1: "«f'  y' : S.unity  a»" using f'_in_hom y' by auto
                    hence 2: "φ (f'  y')  cones S.unity"
                      using φ bij_betw_imp_funcset [of φ "S.hom S.unity a" "cones S.unity"]
                      by auto
                    interpret χ'': cone J S D S.unity φ (f'  y') using 2 by auto
                    have "¬J.arr j  φ (f'  y') j = cones_map y' χ' j"
                      using f' y' cone_χ' χ''.is_extensional mem_Collect_eq restrict_apply
                      by (elim S.in_homE, auto)
                    moreover have "J.arr j  φ (f'  y') j = cones_map y' χ' j"
                    proof -
                      assume j: "J.arr j"
                      have 3: "« a j : a  D (J.cod j)»"
                        using j χa.preserves_hom by force
                      have "φ (f'  y') j = φ (a  f'  y') j"
                        using a f' y' j S.comp_cod_arr by (elim S.in_homE, auto)
                      also have "... =  a j  f'  y'"
                        using 1 3 χ [of a] a f' y' j by fastforce
                      also have "... = ( a j  f')  y'"
                        using S.comp_assoc by simp
                      also have "... = cones_map f' ( a) j  y'"
                        using f' y' j χa.cone_axioms by auto
                      also have "... = χ' j  y'"
                        using f' by blast
                      also have "... = cones_map y' χ' j"
                        using y' j cone_χ' f' mem_Collect_eq restrict_apply by force
                      finally show "φ (f'  y') j = cones_map y' χ' j" by auto
                    qed
                    ultimately show "φ (f'  y') j = cones_map y' χ' j" by auto
                  qed
                  hence "f'  y' =  (cones_map y' χ')"
                    using φ f'_in_hom y' S.comp_in_homI
                          bij_betw_inv_into_left [of φ "S.hom S.unity a" "cones S.unity" "f'  y'"]
                    by (elim S.in_homE, auto)
                  moreover have "?f  y' =  (cones_map y' χ')"
                    using φ 0 1 f f_in_hom f'_in_hom y' S.comp_in_homI
                          bij_betw_inv_into_left [of φ "S.hom S.unity a" "cones S.unity" "?f  y'"]
                    by (elim S.in_homE, auto)
                  ultimately show "f'  y' = ?f  y'" by auto
                qed
              qed
            qed
          qed
        qed
        have "limit_cone a ( a)" ..
        thus ?thesis by auto
      qed
    qed

  end

  locale diagram_in_replete_set_category =
    J: category J +
    S: replete_set_category S +
    diagram J S D
  for J :: "'j comp"      (infixr J 55)
  and S :: "'s comp"      (infixr  55)
  and D :: "'j  's"
  begin

    sublocale diagram_in_set_category J S S.setp D
      ..

  end

  context set_category
  begin

    text‹
      A set category has an equalizer for any parallel pair of arrows.
›

    lemma has_equalizersSC:
    shows "has_equalizers"
    proof (unfold has_equalizers_def)
      have "f0 f1. par f0 f1  e. has_as_equalizer f0 f1 e"
      proof -
        fix f0 f1
        assume par: "par f0 f1"
        interpret J: parallel_pair .
        interpret PP: parallel_pair_diagram S f0 f1
          using par by unfold_locales auto
        interpret PP: diagram_in_set_category J.comp S setp PP.map ..
        text‹
          Let @{term a} be the object corresponding to the set of all images of equalizing points
          of @{term "dom f0"}, and let @{term e} be the inclusion of @{term a} in @{term "dom f0"}.
›
        let ?a = "mkIde (img ` {e. e  hom unity (dom f0)  f0  e = f1  e})"
        have 0: "{e. e  hom unity (dom f0)  f0  e = f1  e}  hom unity (dom f0)"
          by auto
        hence 1: "img ` {e. e  hom unity (dom f0)  f0  e = f1  e}  Univ"
          using img_point_in_Univ by auto
        have 2: "setp (img ` {e. e  hom unity (dom f0)  f0  e = f1  e})"
        proof -
          have "setp (img ` hom unity (dom f0))"
            using ide_dom par setp_img_points by blast
          moreover have "img ` {e. e  hom unity (dom f0)  f0  e = f1  e} 
                         img ` hom unity (dom f0)"
            by blast
          ultimately show ?thesis
            by (meson setp_respects_subset)
        qed
        have ide_a: "ide ?a" using 1 2 ide_mkIde by auto
        have set_a: "set ?a = img ` {e. e  hom unity (dom f0)  f0  e = f1  e}"
          using 1 2 set_mkIde by simp
        have incl_in_a: "incl_in ?a (dom f0)"
        proof -
          have "ide (dom f0)"
            using PP.is_parallel by simp
          moreover have "set ?a  set (dom f0)"
            using img_point_elem_set set_a by fastforce
          ultimately show ?thesis
            using incl_in_def ide ?a by simp
        qed
        text‹
          Then @{term "set a"} is in bijective correspondence with @{term "PP.cones unity"}.
›
        let  = "λt. PP.mkCone (mkPoint (dom f0) t)"
        let  = "λχ. img (χ (J.Zero))"
        have bij: "bij_betw  (set ?a) (PP.cones unity)"
        proof (intro bij_betwI)
          show "  set ?a  PP.cones unity"
          proof
            fix t
            assume t: "t  set ?a"
            hence 1: "t  img ` {e. e  hom unity (dom f0)  f0  e = f1  e}"
              using set_a by blast
            then have 2: "mkPoint (dom f0) t  hom unity (dom f0)"
              using mkPoint_in_hom imageE mem_Collect_eq mkPoint_img(2) by auto
            with 1 have 3: "mkPoint (dom f0) t  {e. e  hom unity (dom f0)  f0  e = f1  e}"
              using mkPoint_img(2) by auto
            then have "PP.is_equalized_by (mkPoint (dom f0) t)"
              using CollectD par by fastforce
            thus "PP.mkCone (mkPoint (dom f0) t)  PP.cones unity"
              using 2 PP.cone_mkCone [of "mkPoint (dom f0) t"] by auto
          qed
          show "  PP.cones unity  set ?a"
          proof
            fix χ
            assume χ: "χ  PP.cones unity"
            interpret χ: cone J.comp S PP.map unity χ using χ by auto
            have "χ (J.Zero)  hom unity (dom f0)  f0  χ (J.Zero) = f1  χ (J.Zero)"
              using χ PP.map_def PP.is_equalized_by_cone J.arr_char by auto
            hence "img (χ (J.Zero))  set ?a"
              using set_a by simp
            thus " χ  set ?a" by blast
          qed
          show "t. t  set ?a   ( t) = t"
            using set_a J.arr_char PP.mkCone_def imageE mem_Collect_eq mkPoint_img(2)
            by auto
          show "χ. χ  PP.cones unity   ( χ) = χ"
          proof -
            fix χ
            assume χ: "χ  PP.cones unity"
            interpret χ: cone J.comp S PP.map unity χ using χ by auto
            have 1: "χ (J.Zero)  hom unity (dom f0)  f0  χ (J.Zero) = f1  χ (J.Zero)"
              using χ PP.map_def PP.is_equalized_by_cone J.arr_char by auto
            hence "img (χ (J.Zero))  set ?a"
              using set_a by simp
            hence "img (χ (J.Zero))  set (dom f0)"
              using incl_in_a incl_in_def by auto
            hence "mkPoint (dom f0) (img (χ J.Zero)) = χ J.Zero"
              using 1 mkPoint_img(2) by blast
            hence " ( χ) = PP.mkCone (χ J.Zero)" by simp
            also have "... = χ"
              using χ PP.mkCone_cone by simp
            finally show " ( χ) = χ" by auto
          qed
        qed
        text‹
          It follows that @{term a} is a limit of PP›, and that the limit cone gives an
          equalizer of @{term f0} and @{term f1}.
›
        have "PP.has_as_limit ?a"
        proof -
          have "μ. bij_betw μ (hom unity ?a) (set ?a)"
            using bij_betw_points_and_set ide_a by auto
          from this obtain μ where μ: "bij_betw μ (hom unity ?a) (set ?a)" by blast
          have "bij_betw ( o μ) (hom unity ?a) (PP.cones unity)"
            using bij μ bij_betw_comp_iff by blast
          hence "φ. bij_betw φ (hom unity ?a) (PP.cones unity)" by auto
          thus ?thesis
            using ide_a PP.limits_are_sets_of_cones by simp
        qed
        from this obtain ε where ε: "limit_cone J.comp S PP.map ?a ε" by auto
        have "PP.has_as_equalizer (ε J.Zero)"
        proof -
          interpret ε: limit_cone J.comp S PP.map ?a ε using ε by auto
          have "PP.mkCone (ε (J.Zero)) = ε"
            using ε PP.mkCone_cone ε.cone_axioms by simp
          moreover have "dom (ε (J.Zero)) = ?a"
            using J.ide_char ε.preserves_hom ε.A.map_def by simp
          ultimately show ?thesis
            using ε by simp
        qed
        thus "e. has_as_equalizer f0 f1 e"
          using par has_as_equalizer_def by auto   
      qed
      thus "f0 f1. par f0 f1  (e. has_as_equalizer f0 f1 e)" by auto
    qed

  end

  sublocale set_category  category_with_equalizers S
    apply unfold_locales using has_equalizersSC by auto

  context set_category
  begin

    text‹
      The aim of the next results is to characterize the conditions under which a set
      category has products.  In a traditional development of category theory,
      one shows that the category \textbf{Set} of \emph{all} sets has all small
      (\emph{i.e.}~set-indexed) products.  In the present context we do not have a
      category of \emph{all} sets, but rather only a category of all sets with
      elements at a particular type.  Clearly, we cannot expect such a category
      to have products indexed by arbitrarily large sets.  The existence of
      @{term I}-indexed products in a set category @{term[source=true] S} implies that the universe
      S.Univ› of @{term[source=true] S} must be large enough to admit the formation of
      @{term I}-tuples of its elements.  Conversely, for a set category @{term[source=true] S}
      the ability to form @{term I}-tuples in @{term Univ} implies that
      @{term[source=true] S} has @{term I}-indexed products.  Below we make this precise by
      defining the notion of when a set category @{term[source=true] S}
      ``admits @{term I}-indexed tupling'' and we show that @{term[source=true] S}
      has @{term I}-indexed products if and only if it admits @{term I}-indexed tupling.

      The definition of ``@{term[source=true] S} admits @{term I}-indexed tupling'' says that
      there is an injective map, from the space of extensional functions from
      @{term I} to @{term Univ}, to @{term Univ}.  However for a convenient
      statement and proof of the desired result, the definition of extensional
      function from theory @{theory "HOL-Library.FuncSet"} needs to be modified.
      The theory @{theory "HOL-Library.FuncSet"} uses the definite, but arbitrarily chosen value
      @{term undefined} as the value to be assumed by an extensional function outside
      of its domain.  In the context of the set_category›, though, it is
      more natural to use S.unity›, which is guaranteed to be an element of the
      universe of @{term[source=true] S}, for this purpose.  Doing things that way makes it
      simpler to establish a bijective correspondence between cones over @{term D} with apex
      @{term unity} and the set of extensional functions @{term d} that map
      each arrow @{term j} of @{term J} to an element @{term "d j"} of @{term "set (D j)"}.
      Possibly it makes sense to go back and make this change in set_category›,
      but that would mean completely abandoning @{theory "HOL-Library.FuncSet"} and essentially
      introducing a duplicate version for use with set_category›.
      As a compromise, what I have done here is to locally redefine the few notions from
      @{theory "HOL-Library.FuncSet"} that I need in order to prove the next set of results.
      The redefined notions are primed to avoid confusion with the original versions.
›

    definition extensional'
    where "extensional' A  {f. x. x  A  f x = unity}"

    abbreviation PiE'
    where "PiE' A B  Pi A B  extensional' A"

    abbreviation restrict'
    where "restrict' f A  λx. if x  A then f x else unity"

    lemma extensional'I [intro]:
    assumes "x. x  A  f x = unity"
    shows "f  extensional' A"
      using assms extensional'_def by auto

    lemma extensional'_arb:
    assumes "f  extensional' A" and "x  A"
    shows "f x = unity"
      using assms extensional'_def by fast

    lemma extensional'_monotone:
    assumes "A  B"
    shows "extensional' A  extensional' B"
      using assms extensional'_arb by fastforce

    lemma PiE'_mono: "(x. x  A  B x  C x)  PiE' A B  PiE' A C"
      by auto

  end

  locale discrete_diagram_in_set_category =
    S: set_category S 𝔖 +
    discrete_diagram J S D +
    diagram_in_set_category J S 𝔖 D
  for J :: "'j comp"      (infixr J 55)
  and S :: "'s comp"      (infixr  55)
  and 𝔖 :: "'s set  bool"
  and D :: "'j  's"
  begin

    text‹
      For @{term D} a discrete diagram in a set category, there is a bijective correspondence
      between cones over @{term D} with apex unity and the set of extensional functions @{term d}
      that map each arrow @{term j} of @{term[source=true] J} to an element of
      @{term "S.set (D j)"}.
›

    abbreviation I
    where "I  Collect J.arr"

    definition funToCone
    where "funToCone F  λj. if J.arr j then S.mkPoint (D j) (F j) else S.null"

    definition coneToFun
    where "coneToFun χ  λj. if J.arr j then S.img (χ j) else S.unity"

    lemma funToCone_mapsto:
    shows "funToCone  S.PiE' I (S.set o D)  cones S.unity"
    proof
      fix F
      assume F: "F  S.PiE' I (S.set o D)"
      interpret U: constant_functor J S S.unity
        apply unfold_locales using S.ide_unity by auto
      have "cone S.unity (funToCone F)"
      proof
        show "j. ¬J.arr j  funToCone F j = S.null"
          using funToCone_def by simp
        fix j
        assume j: "J.arr j"
        have "funToCone F j = S.mkPoint (D j) (F j)"
          using j funToCone_def by simp
        moreover have "...  S.hom S.unity (D j)"
          using F j is_discrete S.img_mkPoint(1) [of "D j"] by force
        ultimately have 2: "funToCone F j  S.hom S.unity (D j)" by auto
        show 3: "S.dom (funToCone F j) = U.map (J.dom j)"
          using 2 j U.map_simp by auto
        show 4: "S.cod (funToCone F j) = D (J.cod j)"
          using 2 j is_discrete by auto
        show "D j  funToCone F (J.dom j) = funToCone F j"
          using 2 j is_discrete S.comp_cod_arr by auto
        show "funToCone F (J.cod j)  (U.map j) = funToCone F j"
          using 3 j is_discrete U.map_simp S.arr_dom_iff_arr S.comp_arr_dom U.preserves_arr
          by (metis J.ide_char)
      qed
      thus "funToCone F  cones S.unity" by auto
    qed

    lemma coneToFun_mapsto:
    shows "coneToFun  cones S.unity  S.PiE' I (S.set o D)"
    proof
      fix χ
      assume χ: "χ  cones S.unity"
      interpret χ: cone J S D S.unity χ using χ by auto
      show "coneToFun χ  S.PiE' I (S.set o D)"
      proof
        show "coneToFun χ  Pi I (S.set o D)"
          using S.mkPoint_img(1) coneToFun_def is_discrete χ.component_in_hom
          by (simp add: S.img_point_elem_set restrict_apply')
        show "coneToFun χ  S.extensional' I"
          by (metis S.extensional'I coneToFun_def mem_Collect_eq)
      qed
    qed

    lemma funToCone_coneToFun:
    assumes "χ  cones S.unity"
    shows "funToCone (coneToFun χ) = χ"
    proof
      interpret χ: cone J S D S.unity χ using assms by auto
      fix j
      have "¬J.arr j  funToCone (coneToFun χ) j = χ j"
        using funToCone_def χ.is_extensional by simp
      moreover have "J.arr j  funToCone (coneToFun χ) j = χ j"
        using funToCone_def coneToFun_def S.mkPoint_img(2) is_discrete χ.component_in_hom
        by auto
      ultimately show "funToCone (coneToFun χ) j = χ j" by blast
    qed

    lemma coneToFun_funToCone:
    assumes "F  S.PiE' I (S.set o D)"
    shows "coneToFun (funToCone F) = F"
    proof
      fix i
      have "i  I  coneToFun (funToCone F) i = F i"
        using assms coneToFun_def S.extensional'_arb [of F I i] by auto
      moreover have "i  I  coneToFun (funToCone F) i = F i"
      proof -
        assume i: "i  I"
        have "coneToFun (funToCone F) i = S.img (funToCone F i)"
          using i coneToFun_def by simp
        also have "... = S.img (S.mkPoint (D i) (F i))"
          using i funToCone_def by auto
        also have "... = F i"
          using assms i is_discrete S.img_mkPoint(2) by force
        finally show "coneToFun (funToCone F) i = F i" by auto
      qed
      ultimately show "coneToFun (funToCone F) i = F i" by auto
    qed

    lemma bij_coneToFun:
    shows "bij_betw coneToFun (cones S.unity) (S.PiE' I (S.set o D))"
      using coneToFun_mapsto funToCone_mapsto funToCone_coneToFun coneToFun_funToCone
            bij_betwI
      by blast

    lemma bij_funToCone:
    shows "bij_betw funToCone (S.PiE' I (S.set o D)) (cones S.unity)"
      using coneToFun_mapsto funToCone_mapsto funToCone_coneToFun coneToFun_funToCone
            bij_betwI
      by blast
 
  end

  context set_category
  begin

    text‹
      A set category admits @{term I}-indexed tupling if there is an injective map that takes
      each extensional function from @{term I} to @{term Univ} to an element of @{term Univ}.
›

    definition admits_tupling
    where "admits_tupling I  π. π  PiE' I (λ_. Univ)  Univ  inj_on π (PiE' I (λ_. Univ))"

    lemma admits_tupling_monotone:
    assumes "admits_tupling I" and "I'  I"
    shows "admits_tupling I'"
    proof -
      from assms(1) obtain π
      where π: "π  PiE' I (λ_. Univ)  Univ  inj_on π (PiE' I (λ_. Univ))"
        using admits_tupling_def by metis
      have "π  PiE' I' (λ_. Univ)  Univ"
      proof
        fix f
        assume f: "f  PiE' I' (λ_. Univ)"
        have "f  PiE' I (λ_. Univ)"
          using assms(2) f extensional'_def [of I'] terminal_unitySC extensional'_monotone by auto
        thus "π f  Univ" using π by auto
      qed
      moreover have "inj_on π (PiE' I' (λ_. Univ))"
      proof -
        have 1: "F A A'. inj_on F A  A'  A  inj_on F A'"
          using subset_inj_on by blast
        moreover have "PiE' I' (λ_. Univ)  PiE' I (λ_. Univ)"
          using assms(2) extensional'_def [of I'] terminal_unitySC by auto
        ultimately show ?thesis using π assms(2) by blast
      qed
      ultimately show ?thesis using admits_tupling_def by metis
    qed

    lemma admits_tupling_respects_bij:
    assumes "admits_tupling I" and "bij_betw φ I I'"
    shows "admits_tupling I'"
    proof -
      obtain π where π: "π  (I  Univ)  extensional' I  Univ 
                         inj_on π ((I  Univ)  extensional' I)"
        using assms(1) admits_tupling_def by metis
      have inv: "bij_betw (inv_into I φ) I' I"
        using assms(2) bij_betw_inv_into by blast
      let ?C = "λf x. if x  I then f (φ x) else unity"
      let ?π' = "λf. π (?C f)"
      have 1: "f. f  (I'  Univ)  extensional' I'  ?C f  (I  Univ)  extensional' I"
        using assms bij_betw_apply by fastforce
      have "?π'  (I'  Univ)  extensional' I'  Univ 
            inj_on ?π' ((I'  Univ)  extensional' I')"
      proof
        show "(λf. π (?C f))  (I'  Univ)  extensional' I'  Univ"
          using 1 π by blast
        show "inj_on ?π' ((I'  Univ)  extensional' I')"
        proof
          fix f g
          assume f: "f  (I'  Univ)  extensional' I'"
          assume g: "g  (I'  Univ)  extensional' I'"
          assume eq: "?π' f = ?π' g"
          have f': "?C f  (I  Univ)  extensional' I"
            using f 1 by simp
          have g': "?C g  (I  Univ)  extensional' I"
            using g 1 by simp
          have 2: "?C f = ?C g"
            using f' g' π eq by (simp add: inj_on_def)
          show "f = g"
          proof
            fix x
            show "f x = g x"
            proof (cases "x  I'")
              show "x  I'  ?thesis"
                using f g
                by (metis (no_types, opaque_lifting) "2" assms(2) bij_betw_apply
                    bij_betw_inv_into_right inv)
              show "x  I'  ?thesis"
                using f g by (metis IntD2 extensional'_arb)
            qed
          qed
        qed
      qed
      thus ?thesis
        using admits_tupling_def by blast
    qed

  end

  context replete_set_category
  begin

    lemma has_products_iff_admits_tupling:
    fixes I :: "'i set"
    shows "has_products I  I  UNIV  admits_tupling I"
    proof
      text‹
        If @{term[source=true] S} has @{term I}-indexed products, then for every @{term I}-indexed
        discrete diagram @{term D} in @{term[source=true] S} there is an object @{term ΠD}
        of @{term[source=true] S} whose points are in bijective correspondence with the set of
        cones over @{term D} with apex @{term unity}.  In particular this is true for
        the diagram @{term D} that assigns to each element of @{term I} the
        ``universal object'' @{term "mkIde Univ"}.
›
      assume has_products: "has_products I"
      have I: "I  UNIV" using has_products has_products_def by auto
      interpret J: discrete_category I SOME x. x  I
        using I someI_ex [of "λx. x  I"] by (unfold_locales, auto)
      let ?D = "λi. mkIde Univ"
      interpret D: discrete_diagram_from_map I S ?D SOME j. j  I
        using J.not_arr_null J.arr_char ide_mkIde
        by (unfold_locales, auto)
      interpret D: discrete_diagram_in_set_category J.comp S λA. A  Univ D.map ..
      have "discrete_diagram J.comp S D.map" ..
      from this obtain ΠD χ where χ: "product_cone J.comp S D.map ΠD χ"
        using has_products has_products_def [of I] ex_productE [of "J.comp" D.map]
              D.diagram_axioms
        by blast
      interpret χ: product_cone J.comp S D.map ΠD χ
        using χ by auto
      have "D.has_as_limit ΠD"
        using χ.limit_cone_axioms by auto
      hence ΠD: "ide ΠD  (φ. bij_betw φ (hom unity ΠD) (D.cones unity))"
        using D.limits_are_sets_of_cones by simp
      from this obtain φ where φ: "bij_betw φ (hom unity ΠD) (D.cones unity)"
        by blast
      have φ': "inv_into (hom unity ΠD) φ  D.cones unity  hom unity ΠD 
                inj_on (inv_into (hom unity ΠD) φ) (D.cones unity)"
        using φ bij_betw_inv_into bij_betw_imp_inj_on bij_betw_imp_funcset by metis 
      let  = "img o (inv_into (hom unity ΠD) φ) o D.funToCone"
      have 1: "D.funToCone  PiE' I (set o D.map)  D.cones unity"
        using D.funToCone_mapsto extensional'_def [of I] by auto
      have 2: "inv_into (hom unity ΠD) φ  D.cones unity  hom unity ΠD"
        using φ' by auto
      have 3: "img  hom unity ΠD  Univ"
        using img_point_in_Univ by blast
      have 4: "inj_on D.funToCone (PiE' I (set o D.map))"
      proof -
        have "D.I = I" by auto
        thus ?thesis
          using D.bij_funToCone bij_betw_imp_inj_on by auto
      qed
      have 5: "inj_on (inv_into (hom unity ΠD) φ) (D.cones unity)"
        using φ' by auto
      have 6: "inj_on img (hom unity ΠD)"
        using ΠD bij_betw_points_and_set bij_betw_imp_inj_on [of img "hom unity ΠD" "set ΠD"]
        by simp
      have "  PiE' I (set o D.map)  Univ"
        using 1 2 3 by force
      moreover have "inj_on  (PiE' I (set o D.map))"
      proof -
        have 7: "A B C D F G H. F  A  B  G  B  C  H  C  D
                       inj_on F A  inj_on G B  inj_on H C
                     inj_on (H o G o F) A"
          by (simp add: Pi_iff inj_on_def)
        show ?thesis
          using 1 2 3 4 5 6 7 [of D.funToCone "PiE' I (set o D.map)" "D.cones unity"
                                  "inv_into (hom unity ΠD) φ" "hom unity ΠD"
                                  img Univ]
          by fastforce
      qed
      moreover have "PiE' I (set o D.map) = PiE' I (λx. Univ)"
      proof -
        have "i. i  I  (set o D.map) i = Univ"
          using J.arr_char D.map_def by simp
        thus ?thesis by blast
      qed
      ultimately have "  (PiE' I (λx. Univ))  Univ  inj_on  (PiE' I (λx. Univ))"
        by auto
      thus "I  UNIV  admits_tupling I"
        using I admits_tupling_def by auto
      next
      assume ex_π: "I  UNIV  admits_tupling I"
      show "has_products I"
      proof (unfold has_products_def)
        from ex_π obtain π
        where π: "π  (PiE' I (λx. Univ))  Univ  inj_on π (PiE' I (λx. Univ))"
          using admits_tupling_def by metis
        text‹
          Given an @{term I}-indexed discrete diagram @{term D}, obtain the object @{term ΠD}
          of @{term[source=true] S} corresponding to the set @{term "π ` PiE I D"} of all
          @{term "π d"} where d ∈ d ∈ J →E Univ› and @{term "d i  D i"}
          for all @{term "i  I"}.
          The elements of @{term ΠD} are in bijective correspondence with the set of cones
          over @{term D}, hence @{term ΠD} is a limit of @{term D}.
›
        have "J D. discrete_diagram J S D  Collect (partial_composition.arr J) = I
                  ΠD. has_as_product J D ΠD"
        proof
          fix J :: "'i comp" and D
          assume D: "discrete_diagram J S D  Collect (partial_composition.arr J) = I"
          interpret J: category J
            using D discrete_diagram.axioms(1) by blast
          interpret D: discrete_diagram J S D
            using D by simp
          interpret D: discrete_diagram_in_set_category J S λA. A  Univ D ..
          let ?ΠD = "mkIde (π ` PiE' I (set o D))"
          have 0: "ide ?ΠD"
          proof -
            have "set o D  I  Pow Univ"
              using Pow_iff incl_in_def o_apply elem_set_implies_incl_in subsetI Pi_I'
                    setp_set_ide
              by (metis (mono_tags, lifting))
            hence "π ` PiE' I (set o D)  Univ"
              using π by blast
            thus ?thesis using π ide_mkIde by simp
          qed
          hence set_ΠD: "π ` PiE' I (set o D) = set ?ΠD"
            using 0 ide_in_hom arr_mkIde set_mkIde by auto
          text‹
            The elements of @{term ΠD} are all values of the form @{term "π d"},
            where @{term d} satisfies @{term "d i  set (D i)"} for all @{term "i  I"}.
            Such @{term d} correspond bijectively to cones.
            Since @{term π} is injective, the values @{term "π d"} correspond bijectively to cones.
›
          let  = "mkPoint ?ΠD o π o D.coneToFun"
          let ?φ' = "D.funToCone o inv_into (PiE' I (set o D)) π o img"
          have 1: "π  PiE' I (set o D)  set ?ΠD  inj_on π (PiE' I (set o D))"
          proof -
            have "PiE' I (set o D)  PiE' I (λx. Univ)"
              using setp_set_ide elem_set_implies_incl_in elem_set_implies_set_eq_singleton
                    incl_in_def PiE'_mono comp_apply subsetI
              by (metis (no_types, lifting))
            thus ?thesis using π subset_inj_on set_ΠD Pi_I' imageI by fastforce
          qed
          have 2: "inv_into (PiE' I (set o D)) π  set ?ΠD  PiE' I (set o D)"
          proof
            fix y
            assume y: "y  set ?ΠD"
            have "y  π ` (PiE' I (set o D))" using y set_ΠD by auto
            thus "inv_into (PiE' I (set o D)) π y  PiE' I (set o D)"
              using inv_into_into [of y π "PiE' I (set o D)"] by simp
          qed
          have 3: "x. x  set ?ΠD  π (inv_into (PiE' I (set o D)) π x) = x"
            using set_ΠD by (simp add: f_inv_into_f)
          have 4: "d. d  PiE' I (set o D)  inv_into (PiE' I (set o D)) π (π d) = d"
            using 1 by auto
          have 5: "D.I = I"
            using D by auto
          have "bij_betw  (D.cones unity) (hom unity ?ΠD)"
          proof (intro bij_betwI)
            show "  D.cones unity  hom unity ?ΠD"
            proof
              fix χ
              assume χ: "χ  D.cones unity"
              show " χ  hom unity ?ΠD"
                using χ 0 1 5 D.coneToFun_mapsto mkPoint_in_hom [of ?ΠD]
                by (simp, blast)
            qed
            show "?φ'  hom unity ?ΠD  D.cones unity"
            proof
              fix x
              assume x: "x  hom unity ?ΠD"
              hence "img x  set ?ΠD"
                using img_point_elem_set by blast
              hence "inv_into (PiE' I (set o D)) π (img x)  Pi I (set  D)  extensional' I"
                using 2 by blast
              thus "?φ' x  D.cones unity"
                using 5 D.funToCone_mapsto by auto
            qed
            show "x. x  hom unity ?ΠD   (?φ' x) = x"
            proof -
              fix x
              assume x: "x  hom unity ?ΠD"
              show " (?φ' x) = x"
              proof -
                have "D.coneToFun (D.funToCone (inv_into (PiE' I (set o D)) π (img x)))
                          = inv_into (PiE' I (set o D)) π (img x)"
                  using x 1 5 img_point_elem_set set_ΠD D.coneToFun_funToCone by force
                hence "π (D.coneToFun (D.funToCone (inv_into (PiE' I (set o D)) π (img x))))
                          = img x"
                  using x 3 img_point_elem_set set_ΠD by force
                thus ?thesis using x 0 mkPoint_img by auto
              qed
            qed
            show "χ. χ  D.cones unity  ?φ' ( χ) = χ"
            proof -
              fix χ
              assume χ: "χ  D.cones unity"
              show "?φ' ( χ) = χ"
              proof -
                have "img (mkPoint ?ΠD (π (D.coneToFun χ))) = π (D.coneToFun χ)"
                  using χ 0 1 5 D.coneToFun_mapsto img_mkPoint(2) by blast
                hence "inv_into (PiE' I (set o D)) π (img (mkPoint ?ΠD (π (D.coneToFun χ))))
                         = D.coneToFun χ"
                  using χ D.coneToFun_mapsto 4 5
                  by (metis (no_types, lifting) PiE)
                hence "D.funToCone (inv_into (PiE' I (set o D)) π
                                             (img (mkPoint ?ΠD (π (D.coneToFun χ)))))
                         = χ"
                  using χ D.funToCone_coneToFun by auto
                thus ?thesis by auto
              qed
            qed
          qed
          hence "bij_betw (inv_into (D.cones unity) ) (hom unity ?ΠD) (D.cones unity)"
            using bij_betw_inv_into by blast
          hence "φ. bij_betw φ (hom unity ?ΠD) (D.cones unity)" by blast
          hence "D.has_as_limit ?ΠD"
            using ide ?ΠD D.limits_are_sets_of_cones by simp
          from this obtain χ where χ: "limit_cone J S D ?ΠD χ" by blast
          interpret χ: limit_cone J S D ?ΠD χ using χ by auto
          interpret P: product_cone J S D ?ΠD χ
            using χ D.product_coneI by blast
          have "product_cone J S D ?ΠD χ" ..
          thus "has_as_product J D ?ΠD"
            using has_as_product_def by auto
        qed
        thus "I  UNIV 
              (J D. discrete_diagram J S D  Collect (partial_composition.arr J) = I
                   (ΠD. has_as_product J D ΠD))"
          using ex_π by blast
      qed
    qed

  end

  context replete_set_category
  begin

    text‹
      Characterization of the completeness properties enjoyed by a set category:
      A set category @{term[source=true] S} has all limits at a type @{typ 'j},
      if and only if @{term[source=true] S} admits @{term I}-indexed tupling
      for all @{typ 'j}-sets @{term I} such that @{term "I  UNIV"}.
›

    theorem has_limits_iff_admits_tupling:
    shows "has_limits (undefined :: 'j)  (I :: 'j set. I  UNIV  admits_tupling I)"
    proof
      assume has_limits: "has_limits (undefined :: 'j)"
      show "I :: 'j set. I  UNIV  admits_tupling I"
        using has_limits has_products_if_has_limits has_products_iff_admits_tupling by blast
      next
      assume admits_tupling: "I :: 'j set. I  UNIV  admits_tupling I"
      show "has_limits (undefined :: 'j)"
        using has_limits_def admits_tupling has_products_iff_admits_tupling
        by (metis category.axioms(1) category.ideD(1) has_limits_if_has_products
            iso_tuple_UNIV_I mem_Collect_eq partial_composition.not_arr_null)
    qed

  end

  section "Limits in Functor Categories"

  text‹
    In this section, we consider the special case of limits in functor categories,
    with the objective of showing that limits in a functor category [A, B]›
    are given pointwise, and that [A, B]› has all limits that @{term B} has.
›

  locale parametrized_diagram =
    J: category J +
    A: category A +
    B: category B +
    JxA: product_category J A +
    binary_functor J A B D
  for J :: "'j comp"      (infixr J 55)
  and A :: "'a comp"      (infixr A 55)
  and B :: "'b comp"      (infixr B 55)
  and D :: "'j * 'a  'b"
  begin

    (* Notation for A.in_hom and B.in_hom is being inherited, but from where? *)
    notation J.in_hom     («_ : _ J _»)
    notation JxA.comp     (infixr JxA 55)
    notation JxA.in_hom   («_ : _ JxA _»)

    text‹
      A choice of limit cone for each diagram D (-, a)›, where @{term a}
      is an object of @{term[source=true] A}, extends to a functor L: A → B›,
      where the action of @{term L} on arrows of @{term[source=true] A} is determined by
      universality.
›

    abbreviation L
    where "L  λl χ. λa. if A.arr a then
                            limit_cone.induced_arrow J B (λj. D (j, A.cod a))
                              (l (A.cod a)) (χ (A.cod a))
                              (l (A.dom a)) (vertical_composite.map J B
                                               (χ (A.dom a)) (λj. D (j, a)))
                          else B.null"

    abbreviation P
    where "P  λl χ. λa f. «f : l (A.dom a) B l (A.cod a)» 
                           diagram.cones_map J B (λj. D (j, A.cod a)) f (χ (A.cod a)) =
                           vertical_composite.map J B (χ (A.dom a)) (λj. D (j, a))"

    lemma L_arr:
    assumes "a. A.ide a  limit_cone J B (λj. D (j, a)) (l a) (χ a)"
    shows "a. A.arr a  (∃!f. P l χ a f)  P l χ a (L l χ a)"
    proof
      fix a
      assume a: "A.arr a"
      interpret χ_dom_a: limit_cone J B λj. D (j, A.dom a) l (A.dom a) χ (A.dom a)
        using a assms by auto
      interpret χ_cod_a: limit_cone J B λj. D (j, A.cod a) l (A.cod a) χ (A.cod a)
        using a assms by auto
      interpret Da: natural_transformation J B λj. D (j, A.dom a) λj. D (j, A.cod a)
                                               λj. D (j, a)
        using a fixing_arr_gives_natural_transformation_2 by simp
      interpret Daoχ_dom_a: vertical_composite J B
                              χ_dom_a.A.map λj. D (j, A.dom a) λj. D (j, A.cod a)
                              χ (A.dom a) λj. D (j, a) ..
      interpret Daoχ_dom_a: cone J B λj. D (j, A.cod a) l (A.dom a) Daoχ_dom_a.map ..
      show "P l χ a (L l χ a)"
        using a Daoχ_dom_a.cone_axioms χ_cod_a.induced_arrowI [of Daoχ_dom_a.map "l (A.dom a)"]
        by auto
      show "∃!f. P l χ a f"
        using χ_cod_a.is_universal Daoχ_dom_a.cone_axioms by blast
    qed

    lemma L_ide:
    assumes "a. A.ide a  limit_cone J B (λj. D (j, a)) (l a) (χ a)"
    shows "a. A.ide a  L l χ a = l a"
    proof -
      let ?L = "L l χ"
      let ?P = "P l χ"
      fix a
      assume a: "A.ide a"
      interpret χa: limit_cone J B λj. D (j, a) l a χ a using a assms by auto
      have Pa: "?P a = (λf. f  B.hom (l a) (l a) 
                            diagram.cones_map J B (λj. D (j, a)) f (χ a) = χ a)"
        using a vcomp_ide_dom χa.natural_transformation_axioms by simp
      have "?P a (?L a)" using assms a L_arr [of l χ a] by fastforce
      moreover have "?P a (l a)"
      proof -
        have "?P a (l a)  l a  B.hom (l a) (l a)  χa.D.cones_map (l a) (χ a) = χ a"
          using Pa by meson
        thus ?thesis
          using a χa.ide_apex χa.cone_axioms χa.D.cones_map_ide [of "χ a" "l a"] by force
      qed
      moreover have "∃!f. ?P a f"
        using a Pa χa.is_universal χa.cone_axioms by force
      ultimately show "?L a = l a" by blast
    qed

    lemma chosen_limits_induce_functor:
    assumes "a. A.ide a  limit_cone J B (λj. D (j, a)) (l a) (χ a)"
    shows "functor A B (L l χ)"
    proof -
      let ?L = "L l χ"
      let ?P = "λa. λf. «f : l (A.dom a) B l (A.cod a)» 
                        diagram.cones_map J B (λj. D (j, A.cod a)) f (χ (A.cod a))
                             = vertical_composite.map J B (χ (A.dom a)) (λj. D (j, a))"
      interpret L: "functor" A B ?L
        apply unfold_locales
        using assms L_arr [of l] L_ide
            apply auto[4]
      proof -
        fix a' a
        assume 1: "A.arr (A a' a)"
        have a: "A.arr a" using 1 by auto
        have a': "«a' : A.cod a A A.cod a'»" using 1 by auto
        have a'a: "A.seq a' a" using 1 by auto
        interpret χ_dom_a: limit_cone J B λj. D (j, A.dom a) l (A.dom a) χ (A.dom a)
          using a assms by auto
        interpret χ_cod_a: limit_cone J B λj. D (j, A.cod a) l (A.cod a) χ (A.cod a)
          using a'a assms by auto
        interpret χ_dom_a'a: limit_cone J B λj. D (j, A.dom (a' A a)) l (A.dom (a' A a))
                                            χ (A.dom (a' A a))
          using a'a assms by auto
        interpret χ_cod_a'a: limit_cone J B λj. D (j, A.cod (a' A a)) l (A.cod (a' A a))
                                            χ (A.cod (a' A a))
          using a'a assms by auto
        interpret Da: natural_transformation J B
                         λj. D (j, A.dom a) λj. D (j, A.cod a) λj. D (j, a)
          using a fixing_arr_gives_natural_transformation_2 by simp
        interpret Da': natural_transformation J B
                         λj. D (j, A.cod a) λj. D (j, A.cod (a' A a)) λj. D (j, a')
          using a a'a fixing_arr_gives_natural_transformation_2 by fastforce
        interpret Da'oχ_cod_a: vertical_composite J B
                                 χ_cod_a.A.map λj. D (j, A.cod a) λj. D (j, A.cod (a' A a))
                                 χ (A.cod a) λj. D (j, a')..
        interpret Da'oχ_cod_a: cone J B λj. D (j, A.cod (a' A a)) l (A.cod a) Da'oχ_cod_a.map
          ..
        interpret Da'a: natural_transformation J B
                          λj. D (j, A.dom (a' A a)) λj. D (j, A.cod (a' A a))
                          λj. D (j, a' A a)
          using a'a fixing_arr_gives_natural_transformation_2 [of "a' A a"] by auto
        interpret Da'aoχ_dom_a'a:
            vertical_composite J B χ_dom_a'a.A.map λj. D (j, A.dom (a' A a))
                                   λj. D (j, A.cod (a' A a)) χ (A.dom (a' A a))
                                   λj. D (j, a' A a) ..
        interpret Da'aoχ_dom_a'a: cone J B λj. D (j, A.cod (a' A a))
                                       l (A.dom (a' A a)) Da'aoχ_dom_a'a.map ..
        show "?L (a' A a) = ?L a' B ?L a"
        proof -
          have "?P (a' A a) (?L (a' A a))" using assms a'a L_arr [of l χ "a' A a"] by fastforce
          moreover have "?P (a' A a) (?L a' B ?L a)"
          proof
            have La: "«?L a : l (A.dom a) B l (A.cod a)»"
              using assms a L_arr by fast
            moreover have La': "«?L a' : l (A.cod a) B l (A.cod a')»"
              using assms a a' L_arr [of l χ a'] by auto
            ultimately have seq: "B.seq (?L a') (?L a)" by (elim B.in_homE, auto)
            thus La'_La: "«?L a' B ?L a : l (A.dom (a' A a)) B l (A.cod (a' A a))»"
              using a a' 1 La La' by (intro B.comp_in_homI, auto)
            show "χ_cod_a'a.D.cones_map (?L a' B ?L a) (χ (A.cod (a' A a)))
                    = Da'aoχ_dom_a'a.map"
            proof -
              have "χ_cod_a'a.D.cones_map (?L a' B ?L a) (χ (A.cod (a' A a)))
                       = (χ_cod_a'a.D.cones_map (?L a) o χ_cod_a'a.D.cones_map (?L a'))
                           (χ (A.cod a'))"
              proof -
                have "χ_cod_a'a.D.cones_map (?L a' B ?L a) (χ (A.cod (a' A a))) =
                      restrict (χ_cod_a'a.D.cones_map (?L a)  χ_cod_a'a.D.cones_map (?L a'))
                               (χ_cod_a'a.D.cones (B.cod (?L a')))
                               (χ (A.cod (a' A a)))"
                  using seq χ_cod_a'a.cone_axioms χ_cod_a'a.D.cones_map_comp [of "?L a'" "?L a"]
                  by argo
                also have "... = (χ_cod_a'a.D.cones_map (?L a) o χ_cod_a'a.D.cones_map (?L a'))
                                 (χ (A.cod a'))"
                proof -
                  have "χ (A.cod a')  χ_cod_a'a.D.cones (l (A.cod a'))"
                    using χ_cod_a'a.cone_axioms a'a by simp
                  moreover have "B.cod (?L a') = l (A.cod a')"
                    using assms a' L_arr [of l] by auto
                  ultimately show ?thesis
                    using a' a'a by simp
                qed
                finally show ?thesis by blast
              qed
              also have "... = χ_cod_a'a.D.cones_map (?L a)
                                   (χ_cod_a'a.D.cones_map (?L a') (χ (A.cod a')))"
                  by simp
              also have "... = χ_cod_a'a.D.cones_map (?L a) Da'oχ_cod_a.map"
              proof -
                have "?P a' (?L a')" using assms a' L_arr [of l χ a'] by fast
                moreover have
                    "?P a' = (λf. f  B.hom (l (A.cod a)) (l (A.cod a')) 
                                  χ_cod_a'a.D.cones_map f (χ (A.cod a')) = Da'oχ_cod_a.map)"
                  using a'a by force
                ultimately show ?thesis using a'a by force
              qed
              also have "... = vertical_composite.map J B
                                 (χ_cod_a.D.cones_map (?L a) (χ (A.cod a)))
                                 (λj. D (j, a'))"
                using assms χ_cod_a.D.diagram_axioms χ_cod_a'a.D.diagram_axioms
                      Da'.natural_transformation_axioms χ_cod_a.cone_axioms La
                      cones_map_vcomp [of J B "λj. D (j, A.cod a)" "λj. D (j, A.cod (a' A a))"
                                          "λj. D (j, a')" "l (A.cod a)" "χ (A.cod a)"
                                          "?L a" "l (A.dom a)"]
                by blast
              also have "... = vertical_composite.map J B
                                 (vertical_composite.map J B (χ (A.dom a)) (λj. D (j, a)))
                                 (λj. D (j, a'))"
                using assms a L_arr by presburger
              also have "... = vertical_composite.map J B (χ (A.dom a))
                                 (vertical_composite.map J B (λj. D (j, a)) (λj. D (j, a')))"
                using a'a Da.natural_transformation_axioms Da'.natural_transformation_axioms
                      χ_dom_a.natural_transformation_axioms vcomp_assoc
                by auto
              also have
                  "... = vertical_composite.map J B (χ (A.dom (a' A a))) (λj. D (j, a' A a))"
                using a'a preserves_comp_2 by simp
              finally show ?thesis by auto
            qed
          qed
          moreover have "∃!f. ?P (a' A a) f"
            using χ_cod_a'a.is_universal
                    [of "l (A.dom (a' A a))"
                        "vertical_composite.map J B (χ (A.dom (a' A a))) (λj. D (j, a' A a))"]
                  Da'aoχ_dom_a'a.cone_axioms
            by fast
          ultimately show ?thesis by blast
        qed
      qed
      show ?thesis ..
    qed

  end

  locale diagram_in_functor_category =
    A: category A +
    B: category B +
    A_B: functor_category A B +
    diagram J A_B.comp D
  for A :: "'a comp"      (infixr A 55)
  and B :: "'b comp"      (infixr B 55)
  and J :: "'j comp"      (infixr J 55)
  and D :: "'j  ('a, 'b) functor_category.arr"
  begin

    interpretation JxA: product_category J A ..
    interpretation A_BxA: product_category A_B.comp A ..
    interpretation E: evaluation_functor A B ..
    interpretation Curry: currying J A B ..

    notation JxA.comp     (infixr JxA 55)
    notation JxA.in_hom   («_ : _ JxA _»)

    text‹
      Evaluation of a functor or natural transformation from @{term[source=true] J}
      to [A, B]› at an arrow @{term a} of @{term[source=true] A}.
›

    abbreviation at
    where "at a τ  λj. Curry.uncurry τ (j, a)"

    lemma at_simp:
    assumes "A.arr a" and "J.arr j" and "A_B.arr (τ j)"
    shows "at a τ j = A_B.Map (τ j) a"
      using assms Curry.uncurry_def E.map_simp by simp

    lemma functor_at_ide_is_functor:
    assumes "functor J A_B.comp F" and "A.ide a"
    shows "functor J B (at a F)"
    proof -
      interpret uncurry_F: "functor" JxA.comp B Curry.uncurry F
        using assms(1) Curry.uncurry_preserves_functors by simp
      interpret uncurry_F: binary_functor J A B Curry.uncurry F ..
      show ?thesis using assms(2) uncurry_F.fixing_ide_gives_functor_2 by simp
    qed

    lemma functor_at_arr_is_transformation:
    assumes "functor J A_B.comp F" and "A.arr a"
    shows "natural_transformation J B (at (A.dom a) F) (at (A.cod a) F) (at a F)"
    proof -
      interpret uncurry_F: "functor" JxA.comp B Curry.uncurry F
        using assms(1) Curry.uncurry_preserves_functors by simp
      interpret uncurry_F: binary_functor J A B Curry.uncurry F ..
      show ?thesis
        using assms(2) uncurry_F.fixing_arr_gives_natural_transformation_2 by simp
    qed

    lemma transformation_at_ide_is_transformation:
    assumes "natural_transformation J A_B.comp F G τ" and "A.ide a"
    shows "natural_transformation J B (at a F) (at a G) (at a τ)"
    proof -
      interpret τ: natural_transformation J A_B.comp F G τ using assms(1) by auto
      interpret uncurry_F: "functor" JxA.comp B Curry.uncurry F
        using Curry.uncurry_preserves_functors τ.F.functor_axioms by simp
      interpret uncurry_f: binary_functor J A B Curry.uncurry F ..
      interpret uncurry_G: "functor" JxA.comp B Curry.uncurry G
        using Curry.uncurry_preserves_functors τ.G.functor_axioms by simp
      interpret uncurry_G: binary_functor J A B Curry.uncurry G ..
      interpret uncurry_τ: natural_transformation
                             JxA.comp B Curry.uncurry F Curry.uncurry G Curry.uncurry τ
        using Curry.uncurry_preserves_transformations τ.natural_transformation_axioms
        by simp
      interpret uncurry_τ: binary_functor_transformation J A B
                            Curry.uncurry F Curry.uncurry G Curry.uncurry τ ..
      show ?thesis
        using assms(2) uncurry_τ.fixing_ide_gives_natural_transformation_2 by simp
    qed

    lemma constant_at_ide_is_constant:
    assumes "cone x χ" and a: "A.ide a"
    shows "at a (constant_functor.map J A_B.comp x) =
           constant_functor.map J B (A_B.Map x a)"
    proof -
      interpret χ: cone J A_B.comp D x χ using assms(1) by auto
      have x: "A_B.ide x" using χ.ide_apex by auto
      interpret Fun_x: "functor" A B A_B.Map x
        using x A_B.ide_char by simp
      interpret Da: "functor" J B at a D
        using a functor_at_ide_is_functor functor_axioms by blast
      interpret Da: diagram J B at a D ..
      interpret Xa: constant_functor J B A_B.Map x a
        using a Fun_x.preserves_ide by unfold_locales simp
      show "at a χ.A.map = Xa.map"
        using a x Curry.uncurry_def E.map_def Xa.is_extensional by auto
    qed

    lemma at_ide_is_diagram:
    assumes a: "A.ide a"
    shows "diagram J B (at a D)"
    proof -
      interpret Da: "functor" J B "at a D"
        using a functor_at_ide_is_functor functor_axioms by simp
      show ?thesis ..
    qed

    lemma cone_at_ide_is_cone:
    assumes "cone x χ" and a: "A.ide a"
    shows "diagram.cone J B (at a D) (A_B.Map x a) (at a χ)"
    proof -
      interpret χ: cone J A_B.comp D x χ using assms(1) by auto
      have x: "A_B.ide x" using χ.ide_apex by auto
      interpret Fun_x: "functor" A B A_B.Map x
        using x A_B.ide_char by simp
      interpret Da: diagram J B at a D using a at_ide_is_diagram by auto
      interpret Xa: constant_functor J B A_B.Map x a
        using a by (unfold_locales, simp)
      interpret χa: natural_transformation J B Xa.map at a D at a χ
        using assms(1) x a transformation_at_ide_is_transformation χ.natural_transformation_axioms
              constant_at_ide_is_constant
        by fastforce
      interpret χa: cone J B at a D A_B.Map x a at a χ ..
      show cone_χa: "Da.cone (A_B.Map x a) (at a χ)" ..
    qed

    lemma at_preserves_comp:
    assumes "A.seq a' a"
    shows "at (A a' a) D = vertical_composite.map J B (at a D) (at a' D)"
    proof -
      interpret Da: natural_transformation J B at (A.dom a) D at (A.cod a) D at a D
        using assms functor_at_arr_is_transformation functor_axioms by blast
      interpret Da': natural_transformation J B at (A.cod a) D at (A.cod a') D at a' D
        using assms functor_at_arr_is_transformation [of D a'] functor_axioms by fastforce
      interpret Da'oDa: vertical_composite J B
                          at (A.dom a) D at (A.cod a) D at (A.cod a') D
                          at a D at a' D ..
      interpret Da'a: natural_transformation J B at (A.dom a) D at (A.cod a') D
                          at (a' A a) D
        using assms functor_at_arr_is_transformation [of D "a' A a"] functor_axioms by simp
      show "at (a' A a) D = Da'oDa.map"
      proof (intro NaturalTransformation.eqI)
        show "natural_transformation J B (at (A.dom a) D) (at (A.cod a') D) Da'oDa.map" ..
        show "natural_transformation J B (at (A.dom a) D) (at (A.cod a') D) (at (a' A a) D)" ..
        show "j. J.ide j  at (a' A a) D j = Da'oDa.map j"
        proof -
          fix j
          assume j: "J.ide j"
          interpret Dj: "functor" A B A_B.Map (D j)
            using j preserves_ide A_B.ide_char by simp
          show "at (a' A a) D j = Da'oDa.map j"
            using assms j Dj.preserves_comp at_simp Da'oDa.map_simp_ide by auto
        qed
      qed
    qed

    lemma cones_map_pointwise:
    assumes "cone x χ" and "cone x' χ'"
    and f: "f  A_B.hom x' x"
    shows "cones_map f χ = χ' 
             (a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ')"
    proof
      interpret χ: cone J A_B.comp D x χ using assms(1) by auto
      interpret χ': cone J A_B.comp D x' χ' using assms(2) by auto
      have x: "A_B.ide x" using χ.ide_apex by auto
      have x': "A_B.ide x'" using χ'.ide_apex by auto
      interpret χf: cone J A_B.comp D x' cones_map f χ
        using x' f assms(1) cones_map_mapsto by blast
      interpret Fun_x: "functor" A B A_B.Map x using x A_B.ide_char by simp
      interpret Fun_x': "functor" A B A_B.Map x' using x' A_B.ide_char by simp
      show "cones_map f χ = χ' 
              (a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ')"
      proof -
        assume χ': "cones_map f χ = χ'"
        have "a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ'"
        proof -
          fix a
          assume a: "A.ide a"
          interpret Da: diagram J B at a D using a at_ide_is_diagram by auto
          interpret χa: cone J B at a D A_B.Map x a at a χ
            using a assms(1) cone_at_ide_is_cone by simp
          interpret χ'a: cone J B at a D A_B.Map x' a at a χ'
            using a assms(2) cone_at_ide_is_cone by simp
          have 1: "«A_B.Map f a : A_B.Map x' a B A_B.Map x a»"
            using f a A_B.arr_char A_B.Map_cod A_B.Map_dom mem_Collect_eq
                  natural_transformation.preserves_hom A.ide_in_hom
            by (metis (no_types, lifting) A_B.in_homE)
          interpret χfa: cone J B at a D A_B.Map x' a
                           Da.cones_map (A_B.Map f a) (at a χ)
            using 1 χa.cone_axioms Da.cones_map_mapsto by force
          show "Da.cones_map (A_B.Map f a) (at a χ) = at a χ'"
          proof
            fix j
            have "¬J.arr j  Da.cones_map (A_B.Map f a) (at a χ) j = at a χ' j"
              using χ'a.is_extensional χfa.is_extensional [of j] by simp
            moreover have "J.arr j  Da.cones_map (A_B.Map f a) (at a χ) j = at a χ' j"
              using a f 1 χ.cone_axioms χa.cone_axioms at_simp
              apply simp
              apply (elim A_B.in_homE B.in_homE, auto)
              using χ' χ.A.map_simp A_B.Map_comp [of "χ j" f a a] by auto
            ultimately show "Da.cones_map (A_B.Map f a) (at a χ) j = at a χ' j" by blast
          qed
        qed
        thus "a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ'"
          by simp
      qed
      show "a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ'
               cones_map f χ = χ'"
      proof -
        assume A:
            "a. A.ide a  diagram.cones_map J B (at a D) (A_B.Map f a) (at a χ) = at a χ'"
        show "cones_map f χ = χ'"
        proof (intro NaturalTransformation.eqI)
          show "natural_transformation J A_B.comp χ'.A.map D (cones_map f χ)" ..
          show "natural_transformation J A_B.comp χ'.A.map D χ'" ..
          show "j. J.ide j  cones_map f χ j = χ' j"
          proof (intro A_B.arr_eqI)
            fix j
            assume j: "J.ide j"
            show 1: "A_B.arr (cones_map f χ j)"
              using j χf.preserves_reflects_arr by simp
            show "A_B.arr (χ' j)" using j by auto
            have Dom_χf_j: "A_B.Dom (cones_map f χ j) = A_B.Map x'"
              using x' j 1 A_B.Map_dom χ'.A.map_simp χf.preserves_dom J.ide_in_hom
              by (metis (no_types, lifting) J.ideD(2) χf.preserves_reflects_arr)
            also have Dom_χ'_j: "... = A_B.Dom (χ' j)"
              using x' j A_B.Map_dom [of "χ' j"] χ'.preserves_hom χ'.A.map_simp by simp
            finally show "A_B.Dom (cones_map f χ j) = A_B.Dom (χ' j)" by auto
            have Cod_χf_j: "A_B.Cod (cones_map f χ j) = A_B.Map (D (J.cod j))"
              using j A_B.Map_cod A_B.cod_char J.ide_in_hom χf.preserves_hom
              by (metis (no_types, lifting) "1" J.ideD(1) χf.preserves_cod)
            also have Cod_χ'_j: "... = A_B.Cod (χ' j)"
              using j A_B.Map_cod [of "χ' j"] χ'.preserves_hom by simp
            finally show "A_B.Cod (cones_map f χ j) = A_B.Cod (χ' j)" by auto
            show "A_B.Map (cones_map f χ j) = A_B.Map (χ' j)"
            proof (intro NaturalTransformation.eqI)
              interpret χfj: natural_transformation A B A_B.Map x' A_B.Map (D (J.cod j))
                                                    A_B.Map (cones_map f χ j)
                using j χf.preserves_reflects_arr A_B.arr_char [of "cones_map f χ j"]
                      Dom_χf_j Cod_χf_j
                by simp
              show "natural_transformation A B (A_B.Map x') (A_B.Map (D (J.cod j)))
                                           (A_B.Map (cones_map f χ j))" ..
              interpret χ'j: natural_transformation A B A_B.Map x' A_B.Map (D (J.cod j))
                                                   A_B.Map (χ' j)
                using j A_B.arr_char [of "χ' j"] Dom_χ'_j Cod_χ'_j by simp
              show "natural_transformation A B (A_B.Map x') (A_B.Map (D (J.cod j)))
                                           (A_B.Map (χ' j))" ..
              show "a. A.ide a  A_B.Map (cones_map f χ j) a = A_B.Map (χ' j) a"
              proof -
                fix a
                assume a: "A.ide a"
                interpret Da: diagram J B at a D using a at_ide_is_diagram by auto
                have cone_χa: "Da.cone (A_B.Map x a) (at a χ)"
                  using a assms(1) cone_at_ide_is_cone by simp
                interpret χa: cone J B at a D A_B.Map x a at a χ
                  using cone_χa by auto
                interpret Fun_f: natural_transformation A B A_B.Dom f A_B.Cod f
                                   A_B.Map f
                  using f A_B.arr_char by fast
                have fa: "A_B.Map f a  B.hom (A_B.Map x' a) (A_B.Map x a)"
                  using a f Fun_f.preserves_hom A.ide_in_hom by auto
                have "A_B.Map (cones_map f χ j) a = Da.cones_map (A_B.Map f a) (at a χ) j"
                proof -
                  have "A_B.Map (cones_map f χ j) a = A_B.Map (A_B.comp (χ j) f) a"
                    using assms(1) f χ.is_extensional by auto
                  also have "... = B (A_B.Map (χ j) a) (A_B.Map f a)"
                    using f j a χ.preserves_hom A.ide_in_hom J.ide_in_hom A_B.Map_comp
                          χ.A.map_simp
                    by (metis (no_types, lifting) A.comp_ide_self A.ideD(1) A_B.seqI'
                        J.ideD(1) mem_Collect_eq)
                  also have "... = Da.cones_map (A_B.Map f a) (at a χ) j"
                    using j a cone_χa fa Curry.uncurry_def E.map_simp by auto
                  finally show ?thesis by auto
                qed
                also have "... = at a χ' j" using j a A by simp
                also have "... = A_B.Map (χ' j) a"
                  using j Curry.uncurry_def E.map_simp χ'j.is_extensional by simp
                finally show "A_B.Map (cones_map f χ j) a = A_B.Map (χ' j) a" by auto
              qed
            qed
          qed
        qed
      qed
    qed
       
    text‹
      If @{term χ} is a cone with apex @{term a} over @{term D}, then @{term χ}
      is a limit cone if, for each object @{term x} of @{term X}, the cone obtained
      by evaluating @{term χ} at @{term x} is a limit cone with apex @{term "A_B.Map a x"}
      for the diagram in @{term C} obtained by evaluating @{term D} at @{term x}.
›

    lemma cone_is_limit_if_pointwise_limit:
    assumes cone_χ: "cone x χ"
    and "a. A.ide a  diagram.limit_cone J B (at a D) (A_B.Map x a) (at a χ)"
    shows "limit_cone x χ"
    proof -
      interpret χ: cone J A_B.comp D x χ using assms by auto
      have x: "A_B.ide x" using χ.ide_apex by auto
      show "limit_cone x χ"
      proof
        fix x' χ'
        assume cone_χ': "cone x' χ'"
        interpret χ': cone J A_B.comp D x' χ' using cone_χ' by auto
        have x': "A_B.ide x'" using χ'.ide_apex by auto
        text‹
          The universality of the limit cone at a χ› yields, for each object
          a› of A›, a unique arrow fa› that transforms
          at a χ› to at a χ'›.
›
        have EU: "a. A.ide a 
                        ∃!fa. fa  B.hom (A_B.Map x' a) (A_B.Map x a) 
                                   diagram.cones_map J B (at a D) fa (at a χ) = at a χ'"
        proof -
          fix a
          assume a: "A.ide a"
          interpret Da: diagram J B at a D using a at_ide_is_diagram by auto
          interpret χa: limit_cone J B at a D A_B.Map x a at a χ
            using assms(2) a by auto
          interpret χ'a: cone J B at a D A_B.Map x' a at a χ'
            using a cone_χ' cone_at_ide_is_cone by auto
          have "Da.cone (A_B.Map x' a) (at a χ')" ..
          thus "∃!fa. fa  B.hom (A_B.Map x' a) (A_B.Map x a) 
                      Da.cones_map fa (at a χ) = at a χ'"
            using χa.is_universal by simp
        qed
        text‹
          Our objective is to show the existence of a unique arrow f› that transforms
          χ› into χ'›.  We obtain f› by bundling the arrows fa›
          of C› and proving that this yields a natural transformation from X›
          to C›, hence an arrow of [X, C]›.
›
        show "∃!f. «f : x' [A,B] x»  cones_map f χ = χ'"
        proof
          let ?P = "λa fa. «fa : A_B.Map x' a B A_B.Map x a» 
                           diagram.cones_map J B (at a D) fa (at a χ) = at a χ'"
          have AaPa: "a. A.ide a  ?P a (THE fa. ?P a fa)"
          proof -
            fix a
            assume a: "A.ide a"
            have "∃!fa. ?P a fa" using a EU by simp
            thus "?P a (THE fa. ?P a fa)" using a theI' [of "?P a"] by fastforce
          qed
          have AaPa_in_hom:
              "a. A.ide a  «THE fa. ?P a fa : A_B.Map x' a B A_B.Map x a»"
            using AaPa by blast
          have AaPa_map:
                  "a. A.ide a 
                       diagram.cones_map J B (at a D) (THE fa. ?P a fa) (at a χ) = at a χ'"
            using AaPa by blast
          let ?Fun_f = "λa. if A.ide a then (THE fa. ?P a fa) else B.null"
          interpret Fun_x: "functor" A B λa. A_B.Map x a
            using x A_B.ide_char by simp
          interpret Fun_x': "functor" A B λa. A_B.Map x' a
            using x' A_B.ide_char by simp
          text‹
            The arrows Fun_f a› are the components of a natural transformation.
            It is more work to verify the naturality than it seems like it ought to be.
›
          interpret φ: transformation_by_components A B
                         λa. A_B.Map x' a λa. A_B.Map x a ?Fun_f
          proof
            fix a
            assume a: "A.ide a"
            show "«?Fun_f a : A_B.Map x' a B A_B.Map x a»" using a AaPa by simp
            next
            fix a
            assume a: "A.arr a"
            text‹
\newcommand\xdom{\mathop{\rm dom}}
\newcommand\xcod{\mathop{\rm cod}}
$$\xymatrix{
  {x_{\xdom a}} \drtwocell\omit{\omit(A)} \ar[d]_{\chi_{\xdom a}} \ar[r]^{x_a} & {x_{\xcod a}}
     \ar[d]^{\chi_{\xcod a}} \\
  {D_{\xdom a}} \ar[r]^{D_a} & {D_{\xcod a}} \\
  {x'_{\xdom a}} \urtwocell\omit{\omit(B)} \ar@/^5em/[uu]^{f_{\xdom a}}_{\hspace{1em}(C)} \ar[u]^{\chi'_{\xdom a}}
     \ar[r]_{x'_a} & {x'_{\xcod a}} \ar[u]_{x'_{\xcod a}} \ar@/_5em/[uu]_{f_{\xcod a}}
}$$
›
            let ?x_dom_a = "A_B.Map x (A.dom a)"
            let ?x_cod_a = "A_B.Map x (A.cod a)"
            let ?x_a = "A_B.Map x a"
            have x_a: "«?x_a : ?x_dom_a B ?x_cod_a»"
              using a x A_B.ide_char by auto
            let ?x'_dom_a = "A_B.Map x' (A.dom a)"
            let ?x'_cod_a = "A_B.Map x' (A.cod a)"
            let ?x'_a = "A_B.Map x' a"
            have x'_a: "«?x'_a : ?x'_dom_a B ?x'_cod_a»"
              using a x' A_B.ide_char by auto
            let ?f_dom_a = "?Fun_f (A.dom a)"
            let ?f_cod_a = "?Fun_f (A.cod a)"
            have f_dom_a: "«?f_dom_a : ?x'_dom_a B ?x_dom_a»" using a AaPa by simp
            have f_cod_a: "«?f_cod_a : ?x'_cod_a B ?x_cod_a»" using a AaPa by simp
            interpret D_dom_a: diagram J B at (A.dom a) D using a at_ide_is_diagram by simp
            interpret D_cod_a: diagram J B at (A.cod a) D using a at_ide_is_diagram by simp
            interpret Da: natural_transformation J B at (A.dom a) D at (A.cod a) D at a D
              using a functor_axioms functor_at_arr_is_transformation by simp
            interpret χ_dom_a: limit_cone J B at (A.dom a) D A_B.Map x (A.dom a)
                                              at (A.dom a) χ
              using assms(2) a by auto
            interpret χ_cod_a: limit_cone J B at (A.cod a) D A_B.Map x (A.cod a)
                                              at (A.cod a) χ
              using assms(2) a by auto
            interpret χ'_dom_a: cone J B at (A.dom a) D A_B.Map x' (A.dom a)
                                         at (A.dom a) χ'
              using a cone_χ' cone_at_ide_is_cone by auto
            interpret χ'_cod_a: cone J B at (A.cod a) D A_B.Map x' (A.cod a)
                                         at (A.cod a) χ'
              using a cone_χ' cone_at_ide_is_cone by auto
            text‹
              Now construct cones with apexes x_dom_a› and x'_dom_a›
              over @{term "at (A.cod a) D"} by forming the vertical composites of
              @{term "at (A.dom a) χ"} and @{term "at (A.cod a) χ'"} with the natural
              transformation @{term "at a D"}.
›
            interpret Daoχ_dom_a: vertical_composite J B
                                    χ_dom_a.A.map at (A.dom a) D at (A.cod a) D
                                    at (A.dom a) χ at a D ..
            interpret Daoχ_dom_a: cone J B at (A.cod a) D ?x_dom_a Daoχ_dom_a.map
              using χ_dom_a.cone_axioms Da.natural_transformation_axioms vcomp_transformation_cone
              by metis
            interpret Daoχ'_dom_a: vertical_composite J B
                                     χ'_dom_a.A.map at (A.dom a) D at (A.cod a) D
                                     at (A.dom a) χ' at a D ..
            interpret Daoχ'_dom_a: cone J B at (A.cod a) D ?x'_dom_a Daoχ'_dom_a.map
              using χ'_dom_a.cone_axioms Da.natural_transformation_axioms vcomp_transformation_cone
              by metis
            have Daoχ_dom_a: "D_cod_a.cone ?x_dom_a Daoχ_dom_a.map" ..
            have Daoχ'_dom_a: "D_cod_a.cone ?x'_dom_a Daoχ'_dom_a.map" ..
            text‹
              These cones are also obtained by transforming the cones @{term "at (A.cod a) χ"}
              and @{term "at (A.cod a) χ'"} by x_a› and x'_a›, respectively.
›
            have A: "Daoχ_dom_a.map = D_cod_a.cones_map ?x_a (at (A.cod a) χ)"
            proof
              fix j
              have "¬J.arr j  Daoχ_dom_a.map j = D_cod_a.cones_map ?x_a (at (A.cod a) χ) j"
                using Daoχ_dom_a.is_extensional χ_cod_a.cone_axioms x_a by force
              moreover have
                   "J.arr j  Daoχ_dom_a.map j = D_cod_a.cones_map ?x_a (at (A.cod a) χ) j"
              proof -
                assume j: "J.arr j"
                have "Daoχ_dom_a.map j = at a D j B at (A.dom a) χ (J.dom j)"
                  using j Daoχ_dom_a.map_simp_2 by simp
                also have "... = A_B.Map (D j) a B A_B.Map (χ (J.dom j)) (A.dom a)"
                  using a j at_simp by simp
                also have "... = A_B.Map (A_B.comp (D j) (χ (J.dom j))) a"
                  using a j A_B.Map_comp
                  by (metis (no_types, lifting) A.comp_arr_dom χ.is_natural_1
                      χ.preserves_reflects_arr)
                also have "... = A_B.Map (A_B.comp (χ (J.cod j)) (χ.A.map j)) a"
                  using a j χ.naturality by simp
                also have "... = A_B.Map (χ (J.cod j)) (A.cod a) B A_B.Map x a"
                  using a j x A_B.Map_comp
                  by (metis (no_types, lifting) A.comp_cod_arr χ.A.map_simp χ.is_natural_2
                            χ.preserves_reflects_arr)
                also have "... = at (A.cod a) χ (J.cod j) B A_B.Map x a"
                  using a j at_simp by simp
                also have "... = at (A.cod a) χ j B A_B.Map x a"
                  using a j χ_cod_a.is_natural_2 χ_cod_a.A.map_simp
                  by (metis J.arr_cod_iff_arr J.cod_cod)
                also have "... = D_cod_a.cones_map ?x_a (at (A.cod a) χ) j"
                  using a j x χ_cod_a.cone_axioms preserves_cod by simp
                finally show ?thesis by blast
              qed
              ultimately show "Daoχ_dom_a.map j = D_cod_a.cones_map ?x_a (at (A.cod a) χ) j"
                by blast
            qed
            have B: "Daoχ'_dom_a.map = D_cod_a.cones_map ?x'_a (at (A.cod a) χ')"
            proof
              fix j
              have "¬J.arr j 
                      Daoχ'_dom_a.map j = D_cod_a.cones_map ?x'_a (at (A.cod a) χ') j"
                using Daoχ'_dom_a.is_extensional χ'_cod_a.cone_axioms x'_a by force
              moreover have
                  "J.arr j  Daoχ'_dom_a.map j = D_cod_a.cones_map ?x'_a (at (A.cod a) χ') j"
              proof -
                assume j: "J.arr j"
                have "Daoχ'_dom_a.map j = at a D j B at (A.dom a) χ' (J.dom j)"
                  using j Daoχ'_dom_a.map_simp_2 by simp
                also have "... = A_B.Map (D j) a B A_B.Map (χ' (J.dom j)) (A.dom a)"
                  using a j at_simp by simp
                also have "... = A_B.Map (A_B.comp (D j) (χ' (J.dom j))) a"
                  using a j A_B.Map_comp
                  by (metis (no_types, lifting) A.comp_arr_dom χ'.is_natural_1
                      χ'.preserves_reflects_arr)
                also have "... = A_B.Map (A_B.comp (χ' (J.cod j)) (χ'.A.map j)) a"
                  using a j χ'.naturality by simp
                also have "... = A_B.Map (χ' (J.cod j)) (A.cod a) B A_B.Map x' a"
                  using a j x' A_B.Map_comp
                  by (metis (no_types, lifting) A.comp_cod_arr χ'.A.map_simp χ'.is_natural_2
                            χ'.preserves_reflects_arr)
                also have "... = at (A.cod a) χ' (J.cod j) B A_B.Map x' a"
                  using a j at_simp by simp
                also have "... = at (A.cod a) χ' j B A_B.Map x' a"
                  using a j χ'_cod_a.is_natural_2 χ'_cod_a.A.map_simp
                  by (metis J.arr_cod_iff_arr J.cod_cod)
                also have "... = D_cod_a.cones_map ?x'_a (at (A.cod a) χ') j"
                  using a j x' χ'_cod_a.cone_axioms preserves_cod by simp
                finally show ?thesis by blast
              qed
              ultimately show
                  "Daoχ'_dom_a.map j = D_cod_a.cones_map ?x'_a (at (A.cod a) χ') j"
                by blast
            qed
            text‹
              Next, we show that f_dom_a›, which is the unique arrow that transforms
              χ_dom_a› into χ'_dom_a›, is also the unique arrow that transforms
              Daoχ_dom_a› into Daoχ'_dom_a›.
›
            have C: "D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map = Daoχ'_dom_a.map"
            proof (intro NaturalTransformation.eqI)
              show "natural_transformation
                      J B χ'_dom_a.A.map (at (A.cod a) D) Daoχ'_dom_a.map" ..
              show "natural_transformation J B χ'_dom_a.A.map (at (A.cod a) D)
                      (D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map)"
              proof -
                interpret κ: cone J B at (A.cod a) D ?x'_dom_a
                                  D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map
                proof -
                  have "b b' f.  f  B.hom b' b; D_cod_a.cone b Daoχ_dom_a.map 
                                      D_cod_a.cone b' (D_cod_a.cones_map f Daoχ_dom_a.map)"
                    using D_cod_a.cones_map_mapsto by blast
                  moreover have "D_cod_a.cone ?x_dom_a Daoχ_dom_a.map" ..
                  ultimately show "D_cod_a.cone ?x'_dom_a
                                     (D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map)"
                    using f_dom_a by simp
                qed
                show ?thesis ..
              qed
              show "j. J.ide j 
                          D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map j = Daoχ'_dom_a.map j"
              proof -
                fix j
                assume j: "J.ide j"
                have "D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map j =
                      Daoχ_dom_a.map j B ?f_dom_a"
                  using j f_dom_a Daoχ_dom_a.cone_axioms
                  by (elim B.in_homE, auto)
                also have "... = (at a D j B at (A.dom a) χ j) B ?f_dom_a"
                  using j Daoχ_dom_a.map_simp_ide by simp
                also have "... = at a D j B at (A.dom a) χ j B ?f_dom_a"
                  using B.comp_assoc by simp
                also have "... = at a D j B D_dom_a.cones_map ?f_dom_a (at (A.dom a) χ) j"
                  using j χ_dom_a.cone_axioms f_dom_a
                  by (elim B.in_homE, auto)
                also have "... = at a D j B at (A.dom a) χ' j"
                  using a AaPa A.ide_dom by presburger
                also have "... = Daoχ'_dom_a.map j"
                  using j Daoχ'_dom_a.map_simp_ide by simp
                finally show
                    "D_cod_a.cones_map ?f_dom_a Daoχ_dom_a.map j = Daoχ'_dom_a.map j"
                  by auto
              qed
            qed
            text‹
              Naturality amounts to showing that C f_cod_a x'_a = C x_a f_dom_a›.
              To do this, we show that both arrows transform @{term "at (A.cod a) χ"}
              into Daoχ'_cod_a›, thus they are equal by the universality of
              @{term "at (A.cod a) χ"}.
›
            have "∃!fa. «fa : ?x'_dom_a B ?x_cod_a» 
                        D_cod_a.cones_map fa (at (A.cod a) χ) = Daoχ'_dom_a.map"
              using Daoχ'_dom_a.cone_axioms a χ_cod_a.is_universal [of ?x'_dom_a Daoχ'_dom_a.map]
              by fast
            moreover have
                 "?f_cod_a B ?x'_a  B.hom ?x'_dom_a ?x_cod_a 
                  D_cod_a.cones_map (?f_cod_a B ?x'_a) (at (A.cod a) χ) = Daoχ'_dom_a.map"
            proof
              show "?f_cod_a B ?x'_a  B.hom ?x'_dom_a ?x_cod_a"
                using f_cod_a x'_a by blast
              show "D_cod_a.cones_map (?f_cod_a B ?x'_a) (at (A.cod a) χ) = Daoχ'_dom_a.map"
              proof -
                have "D_cod_a.cones_map (?f_cod_a B ?x'_a) (at (A.cod a) χ)
                         = restrict (D_cod_a.cones_map ?x'_a o D_cod_a.cones_map ?f_cod_a)
                                    (D_cod_a.cones (?x_cod_a))
                                    (at (A.cod a) χ)"
                  using x'_a D_cod_a.cones_map_comp [of ?f_cod_a ?x'_a] f_cod_a
                  by (elim B.in_homE, auto)
                also have "... = D_cod_a.cones_map ?x'_a
                                   (D_cod_a.cones_map ?f_cod_a (at (A.cod a) χ))"
                  using χ_cod_a.cone_axioms by simp
                also have "... = Daoχ'_dom_a.map"
                  using a B AaPa_map A.ide_cod by presburger
                finally show ?thesis by auto
              qed
            qed
            moreover have
                 "?x_a B ?f_dom_a  B.hom ?x'_dom_a ?x_cod_a 
                  D_cod_a.cones_map (?x_a B ?f_dom_a) (at (A.cod a) χ) = Daoχ'_dom_a.map"
            proof
              show "?x_a B ?f_dom_a  B.hom ?x'_dom_a ?x_cod_a"
                using f_dom_a x_a by blast
              show "D_cod_a.cones_map (?x_a B ?f_dom_a) (at (A.cod a) χ) = Daoχ'_dom_a.map"
              proof -
                have
                    "D_cod_a.cones (B.cod (A_B.Map x a)) = D_cod_a.cones (A_B.Map x (A.cod a))"
                  using a x by simp
                moreover have "B.seq ?x_a ?f_dom_a"
                  using f_dom_a x_a by (elim B.in_homE, auto)
                ultimately have
                     "D_cod_a.cones_map (?x_a B ?f_dom_a) (at (A.cod a) χ)
                         = restrict (D_cod_a.cones_map ?f_dom_a o D_cod_a.cones_map ?x_a)
                                    (D_cod_a.cones (?x_cod_a))
                                    (at (A.cod a) χ)"
                  using D_cod_a.cones_map_comp [of ?x_a ?f_dom_a] x_a by argo
                also have "... = D_cod_a.cones_map ?f_dom_a
                                   (D_cod_a.cones_map ?x_a (at (A.cod a) χ))"
                  using χ_cod_a.cone_axioms by simp
                also have "... = Daoχ'_dom_a.map"
                  using A C a AaPa by argo
                finally show ?thesis by blast
              qed
            qed
            ultimately show "?f_cod_a B ?x'_a = ?x_a B ?f_dom_a"
              using a χ_cod_a.is_universal by blast
          qed
          text‹
            The arrow from @{term x'} to @{term x} in [A, B]› determined by
            the natural transformation φ› transforms @{term χ} into @{term χ'}.
            Moreover, it is the unique such arrow, since the components of φ›
            are each determined by universality.
›
          let ?f = "A_B.MkArr (λa. A_B.Map x' a) (λa. A_B.Map x a) φ.map"
          have f_in_hom: "?f  A_B.hom x' x"
          proof -
            have arr_f: "A_B.arr ?f"
              using x' x A_B.arr_MkArr φ.natural_transformation_axioms by simp
            moreover have "A_B.MkIde (λa. A_B.Map x a) = x"
              using x A_B.ide_char A_B.MkArr_Map A_B.in_homE A_B.ide_in_hom by metis
            moreover have "A_B.MkIde (λa. A_B.Map x' a) = x'"
              using x' A_B.ide_char A_B.MkArr_Map A_B.in_homE A_B.ide_in_hom by metis
            ultimately show ?thesis
              using A_B.dom_char A_B.cod_char by auto
          qed
          have Fun_f: "a. A.ide a  A_B.Map ?f a = (THE fa. ?P a fa)"
            using f_in_hom φ.map_simp_ide by fastforce
          have cones_map_f: "cones_map ?f χ = χ'"
            using AaPa Fun_f at_ide_is_diagram assms(2) x x' cone_χ cone_χ' f_in_hom Fun_f
                  cones_map_pointwise
            by presburger
          show "«?f : x' [A,B] x»  cones_map ?f χ = χ'" using f_in_hom cones_map_f by auto
          show "f'. «f' : x' [A,B] x»  cones_map f' χ = χ'  f' = ?f"
          proof -
            fix f'
            assume f': "«f' : x' [A,B] x»  cones_map f' χ = χ'"
            have 0: "a. A.ide a 
                           diagram.cones_map J B (at a D) (A_B.Map f' a) (at a χ) = at a χ'"
              using f' cone_χ cone_χ' cones_map_pointwise by blast
            have "f' = A_B.MkArr (A_B.Dom f') (A_B.Cod f') (A_B.Map f')"
              using f' A_B.MkArr_Map by auto
            also have "... = ?f"
            proof (intro A_B.MkArr_eqI)
              show 1: "A_B.Dom f' = A_B.Map x'" using f' A_B.Map_dom by auto
              show 2: "A_B.Cod f' = A_B.Map x" using f' A_B.Map_cod by auto
              show "A_B.Map f' = φ.map"
              proof (intro NaturalTransformation.eqI)
                show "natural_transformation A B (A_B.Map x') (A_B.Map x) φ.map" ..
                show "natural_transformation A B (A_B.Map x') (A_B.Map x) (A_B.Map f')"
                  using f' 1 2 A_B.arr_char [of f'] by auto
                show "a. A.ide a  A_B.Map f' a = φ.map a"
                proof -
                  fix a
                  assume a: "A.ide a"
                  interpret Da: diagram J B at a D using a at_ide_is_diagram by auto
                  interpret Fun_f': natural_transformation A B A_B.Dom f' A_B.Cod f'
                                                           A_B.Map f'
                    using f' A_B.arr_char by fast
                  have "A_B.Map f' a  B.hom (A_B.Map x' a) (A_B.Map x a)"
                    using a f' Fun_f'.preserves_hom A.ide_in_hom by auto
                  hence "?P a (A_B.Map f' a)" using a 0 [of a] by simp
                  moreover have "?P a (φ.map a)"
                    using a φ.map_simp_ide Fun_f AaPa by presburger
                  ultimately show "A_B.Map f' a = φ.map a" using a EU by blast
                qed
              qed
            qed
            finally show "f' = ?f" by auto
          qed
        qed
      qed
    qed

  end

  context functor_category
  begin

    text‹
      A functor category [A, B]› has limits of shape @{term[source=true] J}
      whenever @{term B} has limits of shape @{term[source=true] J}.
›

    lemma has_limits_of_shape_if_target_does:
    assumes "category (J :: 'j comp)"
    and "B.has_limits_of_shape J"
    shows "has_limits_of_shape J"
    proof (unfold has_limits_of_shape_def)
      have "D. diagram J comp D  (x χ. limit_cone J comp D x χ)"
      proof -
        fix D
        assume D: "diagram J comp D"
        interpret J: category J using assms(1) by auto
        interpret JxA: product_category J A ..
        interpret D: diagram J comp D using D by auto
        interpret D: diagram_in_functor_category A B J D ..
        interpret Curry: currying J A B ..
        text‹
          Given diagram @{term D} in [A, B]›, choose for each object a›
          of A› a limit cone (la, χa)› for at a D› in B›.
›
        let ?l = "λa. diagram.some_limit J B (D.at a D)"
        let  = "λa. diagram.some_limit_cone J B (D.at a D)"
        have : "a. A.ide a  diagram.limit_cone J B (D.at a D) (?l a) ( a)"
          using B.has_limits_of_shape_def D.at_ide_is_diagram assms(2)
                diagram.limit_cone_some_limit_cone
          by blast
        text‹
          The choice of limit cones induces a limit functor from A› to B›.
›
        interpret uncurry_D: diagram JxA.comp B "Curry.uncurry D"
        proof -
          interpret "functor" JxA.comp B Curry.uncurry D
            using D.functor_axioms Curry.uncurry_preserves_functors by simp
          interpret binary_functor J A B Curry.uncurry D ..
          show "diagram JxA.comp B (Curry.uncurry D)" ..
        qed
        interpret uncurry_D: parametrized_diagram J A B Curry.uncurry D ..
        let ?L = "uncurry_D.L ?l "
        let ?P = "uncurry_D.P ?l "
        interpret L: "functor" A B ?L
          using  uncurry_D.chosen_limits_induce_functor [of ?l ] by simp
        have L_ide: "a. A.ide a  ?L a = ?l a"
          using uncurry_D.L_ide [of ?l ]  by blast
        have L_arr: "a. A.arr a  (∃!f. ?P a f)  ?P a (?L a)"
          using uncurry_D.L_arr [of ?l ]  by blast
        text‹
          The functor L› extends to a functor L'› from JxA›
          to B› that is constant on J›.
›
        let ?L' = "λja. if JxA.arr ja then ?L (snd ja) else B.null"
        let ?P' = "λja. ?P (snd ja)"
        interpret L': "functor" JxA.comp B ?L'
          apply unfold_locales
          using L.preserves_arr L.preserves_dom L.preserves_cod
              apply auto[4]
          using L.preserves_comp JxA.comp_char by (elim JxA.seqE, auto)
        have "ja. JxA.arr ja  (∃!f. ?P' ja f)  ?P' ja (?L' ja)"
        proof -
          fix ja
          assume ja: "JxA.arr ja"
          have "A.arr (snd ja)" using ja by blast
          thus "(∃!f. ?P' ja f)  ?P' ja (?L' ja)"
            using ja L_arr by presburger
        qed
        hence L'_arr: "ja. JxA.arr ja  ?P' ja (?L' ja)" by blast
        have L'_ide: "ja.  J.arr (fst ja); A.ide (snd ja)   ?L' ja = ?l (snd ja)"
          using L_ide  by force
        have L'_arr_map:
             "ja. JxA.arr ja  uncurry_D.P ?l  (snd ja) (uncurry_D.L ?l  (snd ja))"
           using L'_arr by presburger
        text‹
          The map that takes an object (j, a)› of JxA› to the component
          χ a j› of the limit cone χ a› is a natural transformation
          from L› to uncurry D›.
›
        let ?χ' = "λja.  (snd ja) (fst ja)"
        interpret χ': transformation_by_components JxA.comp B ?L' Curry.uncurry D ?χ'
        proof
          fix ja
          assume ja: "JxA.ide ja"
          let ?j = "fst ja"
          let ?a = "snd ja"
          interpret χa: limit_cone J B D.at ?a D ?l ?a  ?a
            using ja  by blast
          show "«?χ' ja : ?L' ja B Curry.uncurry D ja»"
            using ja L'_ide [of ja] by force
          next
          fix ja
          assume ja: "JxA.arr ja"
          let ?j = "fst ja"
          let ?a = "snd ja"
          have j: "J.arr ?j" using ja by simp
          have a: "A.arr ?a" using ja by simp
          interpret D_dom_a: diagram J B D.at (A.dom ?a) D
            using a D.at_ide_is_diagram by auto
          interpret D_cod_a: diagram J B D.at (A.cod ?a) D
            using a D.at_ide_is_diagram by auto
          interpret Da: natural_transformation J B
                          D.at (A.dom ?a) D D.at (A.cod ?a) D D.at ?a D
            using a D.functor_axioms D.functor_at_arr_is_transformation by simp
          interpret χ_dom_a: limit_cone J B D.at (A.dom ?a) D ?l (A.dom ?a)
                                (A.dom ?a)
            using a  by simp
          interpret χ_cod_a: limit_cone J B D.at (A.cod ?a) D ?l (A.cod ?a)
                                (A.cod ?a)
            using a  by simp
          interpret Daoχ_dom_a: vertical_composite J B
                                  χ_dom_a.A.map D.at (A.dom ?a) D D.at (A.cod ?a) D
                                   (A.dom ?a) D.at ?a D
            ..
          interpret Daoχ_dom_a: cone J B D.at (A.cod ?a) D ?l (A.dom ?a) Daoχ_dom_a.map ..
          show "?χ' (JxA.cod ja) B ?L' ja = B (Curry.uncurry D ja) (?χ' (JxA.dom ja))"
          proof -
            have "?χ' (JxA.cod ja) B ?L' ja =  (A.cod ?a) (J.cod ?j) B ?L' ja"
              using ja by fastforce
            also have "... = D_cod_a.cones_map (?L' ja) ( (A.cod ?a)) (J.cod ?j)"
              using ja L'_arr_map [of ja] χ_cod_a.cone_axioms by auto
            also have "... = Daoχ_dom_a.map (J.cod ?j)"
              using ja χ_cod_a.induced_arrowI Daoχ_dom_a.cone_axioms L'_arr by presburger
            also have "... = D.at ?a D (J.cod ?j) B D_dom_a.some_limit_cone (J.cod ?j)"
              using ja Daoχ_dom_a.map_simp_ide by fastforce
            also have "... = D.at ?a D (J.cod ?j) B D.at (A.dom ?a) D ?j B ?χ' (JxA.dom ja)"
              using ja χ_dom_a.naturality χ_dom_a.ide_apex apply simp
              by (metis B.comp_arr_ide χ_dom_a.preserves_reflects_arr)
            also have "... = (D.at ?a D (J.cod ?j) B D.at (A.dom ?a) D ?j) B ?χ' (JxA.dom ja)"
              using j ja B.comp_assoc by presburger
            also have "... = B (D.at ?a D ?j) (?χ' (JxA.dom ja))"
              using a j ja Map_comp A.comp_arr_dom D.as_nat_trans.is_natural_2 by simp
           also have "... = Curry.uncurry D ja B ?χ' (JxA.dom ja)"
             using Curry.uncurry_def by simp
           finally show ?thesis by auto
         qed
       qed
       text‹
         Since χ'› is constant on J›, curry χ'› is a cone over D›.
›
       interpret constL: constant_functor J comp MkIde ?L
         using L.as_nat_trans.natural_transformation_axioms MkArr_in_hom ide_in_hom
              L.functor_axioms
         by unfold_locales blast
       (* TODO: This seems a little too involved. *)
       have curry_L': "constL.map = Curry.curry ?L' ?L' ?L'"
       proof
         fix j
         have "¬J.arr j  constL.map j = Curry.curry ?L' ?L' ?L' j"
           using Curry.curry_def constL.is_extensional by simp
         moreover have "J.arr j  constL.map j = Curry.curry ?L' ?L' ?L' j"
           using Curry.curry_def constL.value_is_ide in_homE ide_in_hom by auto
         ultimately show "constL.map j = Curry.curry ?L' ?L' ?L' j" by blast
       qed
       hence uncurry_constL: "Curry.uncurry constL.map = ?L'"
         using L'.as_nat_trans.natural_transformation_axioms Curry.uncurry_curry by simp
       interpret curry_χ': natural_transformation J comp constL.map D
                             Curry.curry ?L' (Curry.uncurry D) χ'.map
       proof -
         have "Curry.curry (Curry.uncurry D) (Curry.uncurry D) (Curry.uncurry D) = D"
           using Curry.curry_uncurry D.functor_axioms D.as_nat_trans.natural_transformation_axioms
           by blast
         thus "natural_transformation J comp constL.map D
                 (Curry.curry ?L' (Curry.uncurry D) χ'.map)"
           using Curry.curry_preserves_transformations curry_L' χ'.natural_transformation_axioms
           by force
       qed
       interpret curry_χ': cone J comp D MkIde ?L Curry.curry ?L' (Curry.uncurry D) χ'.map
         ..
       text‹
         The value of curry_χ'› at each object a› of A› is the
         limit cone χ a›, hence curry_χ'› is a limit cone.
›
       have 1: "a. A.ide a  D.at a (Curry.curry ?L' (Curry.uncurry D) χ'.map) =  a"
       proof -
         fix a
         assume a: "A.ide a"
         have "D.at a (Curry.curry ?L' (Curry.uncurry D) χ'.map) =
                 (λj. Curry.uncurry (Curry.curry ?L' (Curry.uncurry D) χ'.map) (j, a))"
           using a by simp
         moreover have "... = (λj. χ'.map (j, a))"
           using a Curry.uncurry_curry χ'.natural_transformation_axioms by simp
         moreover have "... =  a"
         proof (intro NaturalTransformation.eqI)
           interpret χa: limit_cone J B D.at a D ?l a  a using a  by simp
           interpret χ': binary_functor_transformation J A B ?L' Curry.uncurry D χ'.map ..
           show "natural_transformation J B χa.A.map (D.at a D) ( a)" ..
           show "natural_transformation J B χa.A.map (D.at a D) (λj. χ'.map (j, a))"
           proof -
             have "χa.A.map = (λj. ?L' (j, a))"
               using a χa.A.map_def L'_ide by auto
             thus ?thesis
               using a χ'.fixing_ide_gives_natural_transformation_2 by simp
           qed
           fix j
           assume j: "J.ide j"
           show "χ'.map (j, a) =  a j"
             using a j χ'.map_simp_ide by simp
         qed
         ultimately show "D.at a (Curry.curry ?L' (Curry.uncurry D) χ'.map) =  a" by simp
       qed
       hence 2: "a. A.ide a  diagram.limit_cone J B (D.at a D) (?l a)
                                (D.at a (Curry.curry ?L' (Curry.uncurry D) χ'.map))"
         using  by simp
       hence "limit_cone J comp D (MkIde ?L) (Curry.curry ?L' (Curry.uncurry D) χ'.map)"
         using 1 2 L.functor_axioms L_ide curry_χ'.cone_axioms curry_L'
               D.cone_is_limit_if_pointwise_limit
         by simp
       thus "x χ. limit_cone J comp D x χ" by blast
     qed
     thus "D. diagram J comp D  (x χ. limit_cone J comp D x χ)" by blast
    qed

    lemma has_limits_if_target_does:
    assumes "B.has_limits (undefined :: 'j)"
    shows "has_limits (undefined :: 'j)"
      using assms B.has_limits_def has_limits_def has_limits_of_shape_if_target_does by fast

  end

  section "The Yoneda Functor Preserves Limits"

  text‹
    In this section, we show that the Yoneda functor from C› to [Cop, S]›
    preserves limits.
›

  context yoneda_functor
  begin

    lemma preserves_limits:
    fixes J :: "'j comp"
    assumes "diagram J C D" and "diagram.has_as_limit J C D a"
    shows "diagram.has_as_limit J Cop_S.comp (map o D) (map a)"
    proof -
      text‹
        The basic idea of the proof is as follows:
        If χ› is a limit cone in C›, then for every object a'›
        of Cop› the evaluation of Y o χ› at a'› is a limit cone
        in S›.  By the results on limits in functor categories,
        this implies that Y o χ› is a limit cone in [Cop, S]›.
›
      interpret J: category J using assms(1) diagram_def by auto
      interpret D: diagram J C D using assms(1) by auto
      from assms(2) obtain χ where χ: "D.limit_cone a χ" by blast
      interpret χ: limit_cone J C D a χ using χ by auto
      have a: "C.ide a" using χ.ide_apex by auto
      interpret YoD: diagram J Cop_S.comp map o D
        using D.diagram_axioms functor_axioms preserves_diagrams [of J D] by simp
      interpret YoD: diagram_in_functor_category Cop.comp S J map o D ..
      interpret Yoχ: cone J Cop_S.comp map o D map a map o χ
        using χ.cone_axioms preserves_cones by blast
      have "a'. C.ide a' 
                   limit_cone J S (YoD.at a' (map o D))
                                  (Cop_S.Map (map a) a') (YoD.at a' (map o χ))"
      proof -
        fix a'
        assume a': "C.ide a'"
        interpret A': constant_functor J C a'
          using a' by (unfold_locales, auto)
        interpret YoD_a': diagram J S YoD.at a' (map o D)
          using a' YoD.at_ide_is_diagram by simp
        interpret Yoχ_a': cone J S YoD.at a' (map o D)
                                   Cop_S.Map (map a) a' YoD.at a' (map o χ)
          using a' YoD.cone_at_ide_is_cone Yoχ.cone_axioms by fastforce
        have eval_at_ide: "j. J.ide j  YoD.at a' (map  D) j = Hom.map (a', D j)"
        proof -
          fix j
          assume j: "J.ide j"
          have "YoD.at a' (map  D) j = Cop_S.Map (map (D j)) a'"
            using a' j YoD.at_simp YoD.preserves_arr [of j] by auto
          also have "... = Y (D j) a'" using Y_def by simp
          also have "... = Hom.map (a', D j)" using a' j D.preserves_arr by simp
          finally show "YoD.at a' (map  D) j = Hom.map (a', D j)" by auto
        qed
        have eval_at_arr: "j. J.arr j  YoD.at a' (map  χ) j = Hom.map (a', χ j)"
        proof -
          fix j
          assume j: "J.arr j"
          have "YoD.at a' (map  χ) j = Cop_S.Map ((map o χ) j) a'"
            using a' j YoD.at_simp [of a' j "map o χ"] preserves_arr by fastforce
          also have "... = Y (χ j) a'" using Y_def by simp
            also have "... = Hom.map (a', χ j)" using a' j by simp
          finally show "YoD.at a' (map  χ) j = Hom.map (a', χ j)" by auto
        qed
        have Fun_map_a_a': "Cop_S.Map (map a) a' = Hom.map (a', a)"
          using a a' map_simp preserves_arr [of a] by simp
        show "limit_cone J S (YoD.at a' (map o D))
                             (Cop_S.Map (map a) a') (YoD.at a' (map o χ))"
        proof
          fix x σ
          assume σ: "YoD_a'.cone x σ"
          interpret σ: cone J S YoD.at a' (map o D) x σ using σ by auto
          have x: "S.ide x" using σ.ide_apex by simp
          text‹
            For each object j› of J›, the component σ j›
            is an arrow in S.hom x (Hom.map (a', D j))›.
            Each element e ∈ S.set x› therefore determines an arrow
            ψ (a', D j) (S.Fun (σ j) e) ∈ C.hom a' (D j)›.
            These arrows are the components of a cone κ e› over @{term D}
            with apex @{term a'}.
›
          have σj: "j. J.ide j  «σ j : x S Hom.map (a', D j)»"
            using eval_at_ide σ.preserves_hom J.ide_in_hom by force
          have κ: "e. e  S.set x 
                        transformation_by_components
                          J C A'.map D (λj. ψ (a', D j) (S.Fun (σ j) e))"
          proof -
            fix e
            assume e: "e  S.set x"
            show "transformation_by_components J C A'.map D (λj. ψ (a', D j) (S.Fun (σ j) e))"
            proof
              fix j
              assume j: "J.ide j"
              show "«ψ (a', D j) (S.Fun (σ j) e) : A'.map j  D j»"
                using e j S.Fun_mapsto [of "σ j"] A'.preserves_ide Hom.set_map eval_at_ide
                      Hom.ψ_mapsto [of "A'.map j" "D j"]
                by force
              next
              fix j
              assume j: "J.arr j"
              show "ψ (a', D (J.cod j)) (S.Fun (σ (J.cod j)) e)  A'.map j =
                    D j  ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)"
              proof -
                have "ψ (a', D (J.cod j)) (S.Fun (σ (J.cod j)) e)  A'.map j =
                      ψ (a', D (J.cod j)) (S.Fun (σ (J.cod j)) e)  a'"
                  using A'.map_simp j by simp
                also have "... = ψ (a', D (J.cod j)) (S.Fun (σ (J.cod j)) e)"
                proof -
                  have "ψ (a', D (J.cod j)) (S.Fun (σ (J.cod j)) e)  C.hom a' (D (J.cod j))"
                    using a' e j Hom.ψ_mapsto [of "A'.map j" "D (J.cod j)"] A'.map_simp
                          S.Fun_mapsto [of "σ (J.cod j)"] Hom.set_map eval_at_ide
                    by auto
                  thus ?thesis
                    using C.comp_arr_dom by fastforce
                qed
                also have "... = ψ (a', D (J.cod j)) (S.Fun (Y (D j) a') (S.Fun (σ (J.dom j)) e))"
                proof -
                  have "S.Fun (Y (D j) a') (S.Fun (σ (J.dom j)) e) =
                        (S.Fun (Y (D j) a') o S.Fun (σ (J.dom j))) e"
                    by simp
                  also have "... = S.Fun (Y (D j) a' S σ (J.dom j)) e"
                    using a' e j Y_arr_ide(1) S.in_homE σj eval_at_ide S.Fun_comp by force
                  also have "... = S.Fun (σ (J.cod j)) e"
                    using a' j x σ.is_natural_2 σ.A.map_simp S.comp_arr_dom J.arr_cod_iff_arr
                          J.cod_cod YoD.preserves_arr σ.is_natural_1 YoD.at_simp
                    by auto
                  finally have
                      "S.Fun (Y (D j) a') (S.Fun (σ (J.dom j)) e) = S.Fun (σ (J.cod j)) e"
                    by auto
                  thus ?thesis by simp
                qed
                also have "... = D j  ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)"
                proof -
                  have "S.Fun (Y (D j) a') (S.Fun (σ (J.dom j)) e) =
                         φ (a', D (J.cod j)) (D j  ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e))"
                  proof -
                    have "S.Fun (σ (J.dom j)) e  Hom.set (a', D (J.dom j))"
                      using a' e j σj S.Fun_mapsto [of "σ (J.dom j)"] Hom.set_map
                            YoD.at_simp eval_at_ide
                      by auto
                    moreover have "C.arr (ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)) 
                                   C.dom (ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)) = a'"
                      using a' e j σj S.Fun_mapsto [of "σ (J.dom j)"] Hom.set_map eval_at_ide
                            Hom.ψ_mapsto [of a' "D (J.dom j)"]
                      by auto
                    ultimately show ?thesis
                      using a' e j Hom.Fun_map C.comp_arr_dom by force
                  qed
                  moreover have "D j  ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)
                                    C.hom a' (D (J.cod j))"
                  proof -
                    have "ψ (a', D (J.dom j)) (S.Fun (σ (J.dom j)) e)  C.hom a' (D (J.dom j))"
                      using a' e j Hom.ψ_mapsto [of a' "D (J.dom j)"] eval_at_ide
                            S.Fun_mapsto [of "σ (J.dom j)"] Hom.set_map
                      by auto
                    thus ?thesis using j D.preserves_hom by blast
                  qed
                  ultimately show ?thesis using a' j Hom.ψ_φ by simp
                qed
                finally show ?thesis by auto
              qed
            qed
          qed
          let  = "λe. transformation_by_components.map J C A'.map
                          (λj. ψ (a', D j) (S.Fun (σ j) e))"
          have cone_κe: "e. e  S.set x  D.cone a' ( e)"
          proof -
            fix e
            assume e: "e  S.set x"
            interpret κe: transformation_by_components J C A'.map D
                            λj. ψ (a', D j) (S.Fun (σ j) e)
              using e κ by blast
            show "D.cone a' ( e)" ..
          qed
          text‹
            Since κ e› is a cone for each element e› of S.set x›,
            by the universal property of the limit cone χ› there is a unique arrow
            fe ∈ C.hom a' a› that transforms χ› to κ e›.
›
          have ex_fe: "e. e  S.set x  ∃!fe. «fe : a'  a»  D.cones_map fe χ =  e"
            using cone_κe χ.is_universal by simp
          text‹
            The map taking e ∈ S.set x› to fe ∈ C.hom a' a›
            determines an arrow f ∈ S.hom x (Hom (a', a))› that
            transforms the cone obtained by evaluating Y o χ› at a'›
            to the cone σ›.
›
          let ?f = "S.mkArr (S.set x) (Hom.set (a', a))
                            (λe. φ (a', a) (χ.induced_arrow a' ( e)))"
          have 0: "(λe. φ (a', a) (χ.induced_arrow a' ( e)))  S.set x  Hom.set (a', a)"
          proof
            fix e
            assume e: "e  S.set x"
            interpret κe: cone J C D a'  e using e cone_κe by simp
            have "χ.induced_arrow a' ( e)  C.hom a' a"
              using a a' e ex_fe χ.induced_arrowI κe.cone_axioms by simp
            thus "φ (a', a) (χ.induced_arrow a' ( e))  Hom.set (a', a)"
              using a a' Hom.φ_mapsto by auto
          qed
          have f: "«?f : x S Hom.map (a', a)»"
          proof -
            have "(λe. φ (a', a) (χ.induced_arrow a' ( e)))  S.set x  Hom.set (a', a)"
            proof
              fix e
              assume e: "e  S.set x"
              interpret κe: cone J C D a'  e using e cone_κe by simp
              have "χ.induced_arrow a' ( e)  C.hom a' a"
                using a a' e ex_fe χ.induced_arrowI κe.cone_axioms by simp
              thus "φ (a', a) (χ.induced_arrow a' ( e))  Hom.set (a', a)"
                using a a' Hom.φ_mapsto by auto
            qed
            moreover have "setp (Hom.set (a', a))"
              using a a' Hom.small_homs
              by (metis Fun_map_a_a' Hom.map_ide S.arr_mkIde S.ideD(1) Yoχ_a'.ide_apex)
            ultimately show ?thesis
              using a a' x σ.ide_apex S.mkArr_in_hom [of "S.set x" "Hom.set (a', a)"]
                    Hom.set_subset_Univ S.mkIde_set
              by simp
          qed
          have "YoD_a'.cones_map ?f (YoD.at a' (map o χ)) = σ"
          proof (intro NaturalTransformation.eqI)
            show "natural_transformation J S σ.A.map (YoD.at a' (map o D)) σ"
              using σ.natural_transformation_axioms by auto
            have 1: "S.cod ?f = Cop_S.Map (map a) a'"
              using f Fun_map_a_a' by force
            interpret YoD_a'of: cone J S YoD.at a' (map o D) x
                                     YoD_a'.cones_map ?f (YoD.at a' (map o χ))
            proof -
              have "YoD_a'.cone (S.cod ?f) (YoD.at a' (map o χ))"
                using a a' f Yoχ_a'.cone_axioms preserves_arr [of a] by auto
              hence "YoD_a'.cone (S.dom ?f) (YoD_a'.cones_map ?f (YoD.at a' (map o χ)))"
                using f YoD_a'.cones_map_mapsto S.arrI by blast
              thus "cone J S (YoD.at a' (map o D)) x
                                        (YoD_a'.cones_map ?f (YoD.at a' (map o χ)))"
                using f by auto
            qed
            show "natural_transformation J S σ.A.map (YoD.at a' (map o D))
                                         (YoD_a'.cones_map ?f (YoD.at a' (map o χ)))" ..
            fix j
            assume j: "J.ide j"
            have "YoD_a'.cones_map ?f (YoD.at a' (map o χ)) j = YoD.at a' (map o χ) j S ?f"
              using f j Fun_map_a_a' Yoχ_a'.cone_axioms by fastforce
            also have "... = σ j"
            proof (intro S.arr_eqISC)
              show "S.par (YoD.at a' (map o χ) j S ?f) (σ j)"
                using 1 f j x YoD_a'.preserves_hom by fastforce
              show "S.Fun (YoD.at a' (map o χ) j S ?f) = S.Fun (σ j)"
              proof
                fix e
                have "e  S.set x  S.Fun (YoD.at a' (map o χ) j S ?f) e = S.Fun (σ j) e"
                  using 1 f j x S.Fun_mapsto [of "σ j"] σ.A.map_simp
                        extensional_arb [of "S.Fun (σ j)"]
                  by auto
                moreover have "e  S.set x 
                                  S.Fun (YoD.at a' (map o χ) j S ?f) e = S.Fun (σ j) e"
                proof -
                  assume e: "e  S.set x"
                  interpret κe: transformation_by_components J C A'.map D
                                  λj. ψ (a', D j) (S.Fun (σ j) e)
                    using e κ by blast
                  interpret κe: cone J C D a'  e using e cone_κe by simp
                  have induced_arrow: "χ.induced_arrow a' ( e)  C.hom a' a"
                    using a a' e ex_fe χ.induced_arrowI κe.cone_axioms by simp
                  have "S.Fun (YoD.at a' (map o χ) j S ?f) e =
                          restrict (S.Fun (YoD.at a' (map o χ) j) o S.Fun ?f) (S.set x) e"
                    using 1 e f j S.Fun_comp YoD_a'.preserves_hom by force
                  also have "... = (φ (a', D j) o C (χ j) o ψ (a', a)) (S.Fun ?f e)"
                    using j a' f e Hom.map_simp_2 S.Fun_mkArr Hom.preserves_arr [of "(a', χ j)"]
                          eval_at_arr
                    by (elim S.in_homE, auto)
                  also have "... = (φ (a', D j) o C (χ j) o ψ (a', a))
                                     (φ (a', a) (χ.induced_arrow a' ( e)))"
                    using e f S.Fun_mkArr by fastforce
                  also have "... = φ (a', D j) (D.cones_map (χ.induced_arrow a' ( e)) χ j)"
                      using a a' e j 0 Hom.ψ_φ induced_arrow χ.cone_axioms by auto
                  also have "... = φ (a', D j) ( e j)"
                    using χ.induced_arrowI κe.cone_axioms by fastforce
                  also have "... = φ (a', D j) (ψ (a', D j) (S.Fun (σ j) e))"
                    using j κe.map_def [of j] by simp
                  also have "... = S.Fun (σ j) e"
                  proof -
                    have "S.Fun (σ j) e  Hom.set (a', D j)"
                      using a' e j S.Fun_mapsto [of "σ j"] eval_at_ide Hom.set_map by auto
                    thus ?thesis
                      using a' j Hom.φ_ψ C.ide_in_hom J.ide_in_hom by blast
                  qed
                  finally show "S.Fun (YoD.at a' (map o χ) j S ?f) e = S.Fun (σ j) e"
                    by auto
                qed
                ultimately show "S.Fun (YoD.at a' (map o χ) j S ?f) e = S.Fun (σ j) e"
                  by auto
              qed
            qed
            finally show "YoD_a'.cones_map ?f (YoD.at a' (map o χ)) j = σ j" by auto
          qed
          hence ff: "?f  S.hom x (Hom.map (a', a)) 
                     YoD_a'.cones_map ?f (YoD.at a' (map o χ)) = σ"
            using f by auto
          text‹
            Any other arrow f' ∈ S.hom x (Hom.map (a', a))› that
            transforms the cone obtained by evaluating Y o χ› at @{term a'}
            to the cone @{term σ}, must equal f›, showing that f›
            is unique.
›
          moreover have "f'. «f' : x S Hom.map (a', a)» 
                              YoD_a'.cones_map f' (YoD.at a' (map o χ)) = σ
                                 f' = ?f"
          proof -
            fix f'
            assume f': "«f' : x S Hom.map (a', a)» 
                        YoD_a'.cones_map f' (YoD.at a' (map o χ)) = σ"
            show "f' = ?f"
            proof (intro S.arr_eqISC)
              show par: "S.par f' ?f" using f f' by (elim S.in_homE, auto)
              show "S.Fun f' = S.Fun ?f"
              proof
                fix e
                have "e  S.set x  S.Fun f' e = S.Fun ?f e"
                  using f f' S.Fun_in_terms_of_comp by fastforce
                moreover have "e  S.set x  S.Fun f' e = S.Fun ?f e"
                proof -
                  assume e: "e  S.set x"
                  have fe: "S.Fun ?f e  Hom.set (a', a)"
                    using e f par by auto
                  have f'e: "S.Fun f' e  Hom.set (a', a)"
                    using a a' e f' S.Fun_mapsto Hom.set_map by fastforce
                  have 1: "«ψ (a', a) (S.Fun f' e) : a'  a»"
                    using a a' e f' f'e S.Fun_mapsto Hom.ψ_mapsto Hom.set_map by blast
                  have 2: "«ψ (a', a) (S.Fun ?f e) : a'  a»"
                    using a a' e f' fe S.Fun_mapsto Hom.ψ_mapsto Hom.set_map by blast
                  interpret χofe: cone J C D a' D.cones_map (ψ (a', a) (S.Fun ?f e)) χ
                  proof -
                    have "D.cones_map (ψ (a', a) (S.Fun ?f e))  D.cones a  D.cones a'"
                      using 2 D.cones_map_mapsto [of "ψ (a', a) (S.Fun ?f e)"]
                      by (elim C.in_homE, auto)
                    thus "cone J C D a' (D.cones_map (ψ (a', a) (S.Fun ?f e)) χ)"
                      using χ.cone_axioms by blast
                  qed
                  have A: "h j. h  C.hom a' a  J.arr j 
                                   S.Fun (YoD.at a' (map o χ) j) (φ (a', a) h)
                                     = φ (a', D (J.cod j)) (χ j  h)"
                  proof -
                    fix h j
                    assume j: "J.arr j"
                    assume h: "h  C.hom a' a"
                    have "S.Fun (YoD.at a' (map o χ) j) (φ (a', a) h)
                              = (φ (a', D (J.cod j))  C (χ j)  ψ (a', a)) (φ (a', a) h)"
                    proof -
                      have "S.Fun (YoD.at a' (map o χ) j)
                              = restrict (φ (a', D (J.cod j))  C (χ j)  ψ (a', a))
                                         (Hom.set (a', a))"
                      proof -
                        have "S.Fun (YoD.at a' (map o χ) j) = S.Fun (Y (χ j) a')"
                          using a' j YoD.at_simp Y_def Yoχ.preserves_reflects_arr [of j]
                          by simp
                        also have "... = restrict (φ (a', D (J.cod j))  C (χ j)  ψ (a', a))
                                                  (Hom.set (a', a))"
                          using a' j χ.preserves_hom [of j "J.dom j" "J.cod j"]
                                Y_arr_ide [of a' "χ j" a "D (J.cod j)"] χ.A.map_simp S.Fun_mkArr
                          by fastforce
                        finally show ?thesis by blast
                      qed
                      thus ?thesis
                        using a a' h Hom.φ_mapsto by auto
                    qed
                    also have "... = φ (a', D (J.cod j)) (χ j  h)"
                      using a a' h Hom.ψ_φ by simp
                    finally show "S.Fun (YoD.at a' (map o χ) j) (φ (a', a) h)
                                    = φ (a', D (J.cod j)) (χ j  h)"
                      by auto
                  qed
                  have "D.cones_map (ψ (a', a) (S.Fun f' e)) χ =
                        D.cones_map (ψ (a', a) (S.Fun ?f e)) χ"
                  proof
                    fix j
                    have "¬J.arr j  D.cones_map (ψ (a', a) (S.Fun f' e)) χ j =
                                       D.cones_map (ψ (a', a) (S.Fun ?f e)) χ j"
                      using 1 2 χ.cone_axioms by (elim C.in_homE, auto)
                    moreover have "J.arr j  D.cones_map (ψ (a', a) (S.Fun f' e)) χ j =
                                               D.cones_map (ψ (a', a) (S.Fun ?f e)) χ j"
                    proof -
                      assume j: "J.arr j"
                      have "D.cones_map (ψ (a', a) (S.Fun f' e)) χ j =
                              χ j  ψ (a', a) (S.Fun f' e)"
                        using j 1 χ.cone_axioms by auto
                      also have "... = ψ (a', D (J.cod j)) (S.Fun (σ j) e)"
                      proof -
                        have "ψ (a', D (J.cod j)) (S.Fun (YoD.at a' (map o χ) j) (S.Fun f' e)) =
                                ψ (a', D (J.cod j))
                                  (φ (a', D (J.cod j)) (χ j  ψ (a', a) (S.Fun f' e)))"
                          using j a a' f'e A Hom.φ_ψ Hom.ψ_mapsto by force
                        moreover have "χ j  ψ (a', a) (S.Fun f' e)  C.hom a' (D (J.cod j))"
                          using a a' j f'e Hom.ψ_mapsto χ.preserves_hom [of j "J.dom j" "J.cod j"]
                                χ.A.map_simp
                          by auto
                        moreover have "S.Fun (YoD.at a' (map o χ) j) (S.Fun f' e) =
                                       S.Fun (σ j) e"
                          using Fun_map_a_a' a a' j f' e x Yoχ_a'.A.map_simp eval_at_ide
                                Yoχ_a'.cone_axioms
                          by auto
                        ultimately show ?thesis
                          using a a' Hom.ψ_φ by auto
                      qed
                      also have "... = χ j  ψ (a', a) (S.Fun ?f e)"
                      proof -
                        have "S.Fun (YoD.at a' (map o χ) j) (S.Fun ?f e) =
                                φ (a', D (J.cod j)) (χ j  ψ (a', a) (S.Fun ?f e))"
                          using j a a' fe A [of "ψ (a', a) (S.Fun ?f e)" j] Hom.φ_ψ Hom.ψ_mapsto
                          by auto
                        hence "ψ (a', D (J.cod j)) (S.Fun (YoD.at a' (map o χ) j) (S.Fun ?f e)) =
                                ψ (a', D (J.cod j))
                                  (φ (a', D (J.cod j)) (χ j  ψ (a', a) (S.Fun ?f e)))"
                          by simp
                        moreover have "χ j  ψ (a', a) (S.Fun ?f e)  C.hom a' (D (J.cod j))"
                          using a a' j fe Hom.ψ_mapsto χ.preserves_hom [of j "J.dom j" "J.cod j"]
                                χ.A.map_simp
                          by auto
                        moreover have "S.Fun (YoD.at a' (map o χ) j) (S.Fun ?f e) =
                                       S.Fun (σ j) e"
                        proof -
                          have "S.Fun (YoD.at a' (map o χ) j) (S.Fun ?f e)
                                  = (S.Fun (YoD.at a' (map o χ) j) o S.Fun ?f) e"
                            by simp
                          also have "... = S.Fun (YoD.at a' (map o χ) j S ?f) e"
                            using Fun_map_a_a' a a' j f e x Yoχ_a'.A.map_simp eval_at_ide
                            by auto
                          also have "... = S.Fun (σ j) e"
                          proof -
                            have "YoD.at a' (map o χ) j S ?f =
                                  YoD_a'.cones_map ?f (YoD.at a' (map o χ)) j"
                              using j f Yoχ_a'.cone_axioms Fun_map_a_a' by auto
                            thus ?thesis using j ff by argo
                          qed
                          finally show ?thesis by auto
                        qed
                        ultimately show ?thesis
                          using a a' Hom.ψ_φ by auto
                      qed
                      also have "... = D.cones_map (ψ (a', a) (S.Fun ?f e)) χ j"
                        using j 2 χ.cone_axioms by force
                      finally show "D.cones_map (ψ (a', a) (S.Fun f' e)) χ j =
                                    D.cones_map (ψ (a', a) (S.Fun ?f e)) χ j"
                        by auto
                    qed
                    ultimately show "D.cones_map (ψ (a', a) (S.Fun f' e)) χ j =
                                     D.cones_map (ψ (a', a) (S.Fun ?f e)) χ j"
                      by auto
                  qed
                  hence "ψ (a', a) (S.Fun f' e) = ψ (a', a) (S.Fun ?f e)"
                    using 1 2 χofe.cone_axioms χ.cone_axioms χ.is_universal by blast
                  hence "φ (a', a) (ψ (a', a) (S.Fun f' e)) = φ (a', a) (ψ (a', a) (S.Fun ?f e))"
                    by simp
                  thus "S.Fun f' e = S.Fun ?f e"
                    using a a' fe f'e Hom.φ_ψ by force
                qed
                ultimately show "S.Fun f' e = S.Fun ?f e" by auto
              qed
            qed
          qed
          ultimately have "∃!f. «f : x S Hom.map (a', a)» 
                                YoD_a'.cones_map f (YoD.at a' (map o χ)) = σ"
            using ex1I [of "λf. S.in_hom x (Hom.map (a', a)) f 
                                YoD_a'.cones_map f (YoD.at a' (map o χ)) = σ"]
            by blast
          thus "∃!f. «f : x S Cop_S.Map (map a) a'» 
                     YoD_a'.cones_map f (YoD.at a' (map o χ)) = σ"
            using a a' Y_def by simp
        qed
      qed
      thus "YoD.has_as_limit (map a)"
        using YoD.cone_is_limit_if_pointwise_limit Yoχ.cone_axioms by auto
    qed

  end

end