\import Category
\import Category.Comma
\import Category.Functor
\import Category.Limit
\import Function.Meta
\import Meta
\import Paths
\import Paths.Meta

\func LeftKanExt.{s,o} {C : Precat.{s,s}} {C' : Precat.{o,s}} {D : CocompletePrecat.{s}} (p : Functor C C') (F : Functor C D) : Functor C' D
  => Functor.op {RightKanExt D.op p.op F.op}

\func RightKanExt.{s,o} {C : Precat.{s,s}} {C' : Precat.{o,s}} (D : CompletePrecat.{s}) (p : Functor C C') (F : Functor C D) : Functor C' D \cowith
   | F c' => lim _ F c'
   | Func {x} {y} f => limMap {lim p F y} (Cone.premap (commaFunctor f) (lim p F x))
   | Func-id {x} => limUniqueBeta {lim p F x} {lim p F x} \lam j => path (\lam i => coneMap {lim p F x} (j.1, j.2, (id-right *> id-left) @ i)) *> inv id-right
   | Func-o {x} {y} {z} {g} {f} => limUniqueBeta {lim p F z} {lim p F x} \lam j => inv (pmap ( _) (limBeta {lim p F z} (Cone.premap (commaFunctor g) (lim p F y)) j) *>
       limBeta {lim p F y} (Cone.premap (commaFunctor f) (lim p F x)) (j.1, j.2, id  j.3  g) *> path (\lam i => coneMap {lim p F x} (j.1, j.2, (pmap ( _) id-left *> o-assoc) @ i))) *> o-assoc
  \where {
    \open Limit

    \func lim.{s,o} {C : Precat.{s,s}} {C' : Precat.{o,s}} {D : CompletePrecat.{s}} (p : Functor C C') (F : Functor C D) (b : C')
      => D.limit (Comp F (commaPrecat.rightForget (Const {TrivialCat} b) p))

    \func commaFunctor {C C' : Precat} {p : Functor C C'} {x y : C'} (f : Hom y x)
      => commaPrecat.functor (Const {TrivialCat} x) (Const y) p p (Const.natTrans f) id

    -- | A limit of the form `lim_{j : J} (LeftKanExt.op D p F (G j))` is a double limit and can be expressed as a single limit of {F} over the comma category `G|p`.
    \class DoubleLimit.{s,o} \noclassifying {J C : Precat.{s,s}} {C' : Precat.{o,s}} {D : CompletePrecat.{s}} (p : Functor C C') (F : Functor C D) (G : Functor J C') {
      \lemma map_iso (c : Cone G) (e : Iso (lim'.limMap (map_cone c))) : Iso (limMap {D.limit (Comp (RightKanExt D p F) G)} (Cone.map (RightKanExt D p F) c))
        => Iso.rightFactor _ iso $ transport (Iso __) (lim'.limUniqueBeta {_} {map_cone c} \lam k => inv (pmap ( _) (lim'.limBeta cone k) *> o-assoc *> pmap (_ ) (limBeta (Cone.map (RightKanExt D p F) c) _) *>
              limBeta {limit (Comp F (commaPrecat.rightForget (Const {TrivialCat} (G k.1)) p))} (Cone.premap (commaFunctor (c.coneMap k.1)) (lim p F c)) ((), k.2, k.3) *> repeat {2} unfold (rewrite id-left idp)) *> o-assoc) e

      \func map_cone (c : Cone G) : Cone (Comp F (commaPrecat.rightForget G p)) \cowith
        | apex => RightKanExt D p F c
        | coneMap (j,z,f) => coneMap $ later ((), z, f  c.coneMap j)
        | coneCoh {w} {w'} (h1,h2,h3) =>
          coneCoh {D.limit (Comp F (commaPrecat.rightForget (Const {TrivialCat} c) p))} {(), w.2, w.3  c.coneMap w.1} {(), w'.2, w'.3  c.coneMap w'.1}
            (Graph.empty idp, h2, id-right *> pmap (w'.3 ) (inv (c.coneCoh h1)) *> inv o-assoc *> pmap ( _) h3 *> o-assoc)

      \func iso : Iso {D} {D.limit (Comp (RightKanExt D p F) G)} {lim'} \cowith
        | f => lim'.limMap cone
        | hinv => limMap cone'
        | hinv_f => limUnique \lam j => inv o-assoc *> pmap ( _) (limBeta cone' j) *> limUnique (\lam w => inv o-assoc *> pmap ( _) (limBeta {lim p F (G j)} (cone'' j) w) *> lim'.limBeta cone (j, w.2, w.3)) *> inv id-right
        | f_hinv => limUnique \lam w => inv o-assoc *> pmap ( _) (lim'.limBeta cone w) *> o-assoc *> pmap (_ ) (limBeta {D.limit (Comp (RightKanExt D p F) G)} cone' w.1) *> limBeta {lim p F (G w.1)} (cone'' w.1) ((), w.2, w.3) *> inv id-right

      \func lim' : Limit => D.limit (Comp F (commaPrecat.rightForget G p))

      \func cone : Cone (Comp F (commaPrecat.rightForget G p)) (D.limit (Comp (RightKanExt D p F) G)) \cowith
        | coneMap w => later (coneMap $ later ((), w.2, w.3))  coneMap (commaPrecat.leftForget G p w)
        | coneCoh {w} {w'} h => inv o-assoc *> pmap ( _) (coneCoh {lim p F (G w.1)} {(), w.2, w.3} {(), w'.2, id  w'.3  G.Func h.1} (TrivialCat.id, h.2, id-right *> o-assoc *> id-left *> h.3) *>
                                                            inv (limBeta {lim p F (G w'.1)} (Cone.premap (commaFunctor (G.Func h.1)) (lim p F (G w.1))) ((), w'.2, w'.3) )) *>
                                    o-assoc *> pmap (_ ) (coneCoh {D.limit (Comp (RightKanExt D p F) G)} h.1)

      \func cone'' (j : J) : Cone (Comp F (commaPrecat.rightForget (Const {TrivialCat} (G j)) p)) lim' \cowith
        | coneMap y => coneMap $ later (j, y.2, y.3)
        | coneCoh {y} {y'} h => lim'.coneCoh {j, y.2, y.3} {j, y'.2, y'.3} (id, h.2, pmap (y'.3 ) (later Func-id) *> h.3)

      \func cone' : Cone (Comp (RightKanExt D p F) G) lim' \cowith
        | coneMap j => limMap {lim p F (G j)} (cone'' j)
        | coneCoh {j} {j'} h => limUnique \lam w => inv o-assoc *> pmap ( _) (limBeta {lim p F (G j')} (Cone.premap (commaFunctor (G.Func h)) (lim p F (G j))) w) *>
                                                      limBeta {lim p F (G j)} (cone'' j) (w.1, w.2, id  w.3  G.Func h) *> inv (pmap ( _) Func-id *> id-left) *>
                                                      lim'.coneCoh {j, w.2, id  w.3  G.Func h} {j', w.2, w.3} (h, id, inv $ pmap ( _) Func-id *> id-left *> o-assoc *> id-left) *>
                                                      inv (limBeta {lim p F (G j')} (cone'' j') w)
    }
  }