{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.types.Coproduct
open import lib.types.Paths
open import lib.types.Pointed
open import lib.types.Pushout
open import lib.types.PushoutFlattening
open import lib.types.PushoutFmap
open import lib.types.Sigma
open import lib.types.Span
open import lib.types.Unit

-- Wedge of two pointed types is defined as a particular case of pushout

module lib.types.Wedge where

module _ {i j} (X : Ptd i) (Y : Ptd j) where

  ⊙∨-span : ⊙Span
  ⊙∨-span = ⊙span X Y ⊙Unit ⊙cst ⊙cst
  ⊙wedge-span = ⊙∨-span

  ∨-span : Span
  ∨-span = ⊙Span-to-Span ⊙∨-span
  wedge-span = ∨-span

  Wedge : Type (lmax i j)
  Wedge = Pushout wedge-span

  infix 80 _∨_
  _∨_ = Wedge

  ⊙Wedge : Ptd (lmax i j)
  ⊙Wedge = ⊙Pushout ⊙wedge-span

  infix 80 _⊙∨_
  _⊙∨_ = ⊙Wedge

module _ {i j} {X : Ptd i} {Y : Ptd j} where

  winl : de⊙ X → X ∨ Y
  winl x = left x

  winr : de⊙ Y → X ∨ Y
  winr y = right y

  wglue : winl (pt X) == winr (pt Y)
  wglue = glue tt

  ⊙winl : X ⊙→ X ⊙∨ Y
  ⊙winl = (winl , idp)

  ⊙winr : Y ⊙→ X ⊙∨ Y
  ⊙winr = (winr , ! wglue)

  module WedgeElim {k} {P : X ∨ Y → Type k}
    (winl* : (x : de⊙ X) → P (winl x)) (winr* : (y : de⊙ Y) → P (winr y))
    (glue* : winl* (pt X) == winr* (pt Y) [ P ↓ wglue ]) where

    private
      module M = PushoutElim winl* winr* (λ _ → glue*)

    f = M.f
    glue-β = M.glue-β unit

  ∨-elim = WedgeElim.f
  Wedge-elim = WedgeElim.f

  module WedgeRec {k} {C : Type k} (winl* : de⊙ X → C) (winr* : de⊙ Y → C)
    (glue* : winl* (pt X) == winr* (pt Y)) where

    private
      module M = PushoutRec {d = wedge-span X Y} winl* winr* (λ _ → glue*)

    f = M.f
    glue-β = M.glue-β unit

  ∨-rec = WedgeRec.f
  Wedge-rec = WedgeRec.f

  module ⊙WedgeRec {k} {Z : Ptd k} (g : X ⊙→ Z) (h : Y ⊙→ Z) where

    open WedgeRec (fst g) (fst h) (snd g ∙ ! (snd h)) public

    ⊙f : X ⊙∨ Y ⊙→ Z
    ⊙f = (f , snd g)

    ⊙winl-β : ⊙f ⊙∘ ⊙winl == g
    ⊙winl-β = idp

    ⊙winr-β : ⊙f ⊙∘ ⊙winr == h
    ⊙winr-β = ⊙λ=' (λ _ → idp) lemma where
      abstract
        lemma : snd (⊙f ⊙∘ ⊙winr) == snd h
        lemma =
          ap (_∙ snd g)
             (ap-! f wglue ∙ ap ! glue-β ∙ !-∙ (snd g) (! (snd h)))
          ∙ ∙-assoc (! (! (snd h))) (! (snd g)) (snd g)
          ∙ ap (! (! (snd h)) ∙_) (!-inv-l (snd g))
          ∙ ∙-unit-r (! (! (snd h)))
          ∙ !-! (snd h)

  ⊙∨-rec = ⊙WedgeRec.⊙f
  ⊙Wedge-rec = ⊙WedgeRec.⊙f

  ⊙Wedge-rec-post∘ : ∀ {k l} {Z : Ptd k} {W : Ptd l}
    (k : Z ⊙→ W) (g : X ⊙→ Z) (h : Y ⊙→ Z)
    → k ⊙∘ ⊙Wedge-rec g h ⊙∼ ⊙Wedge-rec (k ⊙∘ g) (k ⊙∘ h)
  ⊙Wedge-rec-post∘ k g h =
    (Wedge-elim (λ _ → idp) (λ _ → idp)
      (↓-='-in' $ ⊙WedgeRec.glue-β (k ⊙∘ g) (k ⊙∘ h)
                 ∙ lemma (fst k) (snd g) (snd h) (snd k)
                 ∙ ! (ap (ap (fst k)) (⊙WedgeRec.glue-β g h))
                 ∙ ∘-ap (fst k) (fst (⊙Wedge-rec g h)) wglue)) ,
    idp
    where
    lemma : ∀ {i j} {A : Type i} {B : Type j} (f : A → B) {x y z : A} {w : B}
      (p : x == z) (q : y == z) (r : f z == w)
      → (ap f p ∙ r) ∙ ! (ap f q ∙ r) == ap f (p ∙ ! q)
    lemma f idp idp idp = idp
  ⊙∨-rec-post∘ = ⊙Wedge-rec-post∘

  ⊙Wedge-rec-η : ⊙Wedge-rec ⊙winl ⊙winr == ⊙idf (X ⊙∨ Y)
  ⊙Wedge-rec-η = ⊙λ='
    (Wedge-elim (λ _ → idp) (λ _ → idp)
      (↓-='-in' $ ap-idf wglue
                 ∙ ! (!-! wglue)
                 ∙ ! (⊙WedgeRec.glue-β ⊙winl ⊙winr)))
    idp
  ⊙∨-rec-η = ⊙Wedge-rec-η

  add-wglue : de⊙ (X ⊙⊔ Y) → X ∨ Y
  add-wglue (inl x) = winl x
  add-wglue (inr y) = winr y

  ⊙add-wglue : X ⊙⊔ Y ⊙→ X ⊙∨ Y
  ⊙add-wglue = add-wglue , idp

  module Projl = ⊙WedgeRec (⊙idf X) (⊙cst {X = Y})
  module Projr = ⊙WedgeRec (⊙cst {X = X}) (⊙idf Y)

  projl = Projl.f
  projr = Projr.f
  ⊙projl = Projl.⊙f
  ⊙projr = Projr.⊙f

  module WedgeToProduct = ⊙WedgeRec ((_, pt Y) , idp) ((pt X ,_), idp)

  ∨-⊙to-× : X ⊙∨ Y ⊙→ X ⊙× Y
  ∨-⊙to-× = WedgeToProduct.⊙f

  ∨-to-× : X ∨ Y → de⊙ (X ⊙× Y)
  ∨-to-× = WedgeToProduct.f

  ∨-to-×-glue-β : ap ∨-to-× wglue == idp
  ∨-to-×-glue-β = WedgeToProduct.glue-β

  abstract
    ↓-∨to×=cst-in : ∀ {x y} {p p' : (pt X , pt Y) == (x , y)}
      → p == p'
      → p == p' [ (λ w → ∨-to-× w == (x , y)) ↓ wglue ]
    ↓-∨to×=cst-in {p' = idp} q =
      ↓-app=cst-in' (q ∙ ! WedgeToProduct.glue-β)

    ↓-∨to×=cst-out : ∀ {x y} {p p' : (pt X , pt Y) == (x , y)}
      → p == p' [ (λ w → ∨-to-× w == (x , y)) ↓ wglue ]
      → p == p'
    ↓-∨to×=cst-out {p' = idp} q =
      ↓-app=cst-out' q ∙ WedgeToProduct.glue-β

    ↓-∨to×=cst-β : ∀ {x y} {p p' : (pt X , pt Y) == (x , y)}
      (q : p == p')
      → ↓-∨to×=cst-out (↓-∨to×=cst-in q) == q
    ↓-∨to×=cst-β {p' = idp} idp =
        ap (_∙ WedgeToProduct.glue-β) (↓-app=cst-β' {p = wglue} (! WedgeToProduct.glue-β))
      ∙ !-inv-l WedgeToProduct.glue-β

    ↓-∨to×=cst-η : ∀ {x y} {p p' : (pt X , pt Y) == (x , y)}
      (q : p == p' [ (λ w → ∨-to-× w == (x , y)) ↓ wglue ])
      → ↓-∨to×=cst-in (↓-∨to×=cst-out q) == q
    ↓-∨to×=cst-η {p = p} {p' = idp} q =
        ap ↓-app=cst-in'
          ( ∙-assoc (↓-app=cst-out' q) WedgeToProduct.glue-β (! WedgeToProduct.glue-β)
          ∙ ap (↓-app=cst-out' q ∙_) (!-inv-r WedgeToProduct.glue-β)
          ∙ ∙-unit-r (↓-app=cst-out' q))
      ∙ ↓-app=cst-η' q

module Fold {i} {X : Ptd i} = ⊙WedgeRec (⊙idf X) (⊙idf X)
fold = Fold.f
⊙fold = Fold.⊙f

module _ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'}
  (fX : X ⊙→ X') (fY : Y ⊙→ Y') where

  wedge-span-map : SpanMap (wedge-span X Y) (wedge-span X' Y')
  wedge-span-map = span-map (fst fX) (fst fY) (idf _)
                      (comm-sqr λ _ → snd fX)
                      (comm-sqr λ _ → snd fY)

  module WedgeFmap where
    private
      module M = PushoutFmap wedge-span-map
    f = M.f
    glue-β = M.glue-β unit

  ∨-fmap : X ∨ Y → X' ∨ Y'
  ∨-fmap = WedgeFmap.f

  ⊙∨-fmap : X ⊙∨ Y ⊙→ X' ⊙∨ Y'
  ⊙∨-fmap = ∨-fmap , ap winl (snd fX)

  Wedge-fmap = ∨-fmap
  ⊙Wedge-fmap = ⊙∨-fmap

module _ {i₀ i₁ i₂ j₀ j₁ j₂} {X₀ : Ptd i₀} {X₁ : Ptd i₁} {X₂ : Ptd i₂}
  {Y₀ : Ptd j₀} {Y₁ : Ptd j₁} {Y₂ : Ptd j₂}
  where

  ∨-fmap-∘ :
    (gX : X₁ ⊙→ X₂) (fX : X₀ ⊙→ X₁)
    (gY : Y₁ ⊙→ Y₂) (fY : Y₀ ⊙→ Y₁)
    → ∨-fmap (gX ⊙∘ fX) (gY ⊙∘ fY) ∼ ∨-fmap gX gY ∘ ∨-fmap fX fY
  ∨-fmap-∘ (gX , idp) (fX , idp) (gY , idp) (fY , idp) =
    Pushout-fmap-∘ (wedge-span-map (gX , idp) (gY , idp)) (wedge-span-map (fX , idp) (fY , idp))

  ⊙∨-fmap-∘ :
    (gX : X₁ ⊙→ X₂) (fX : X₀ ⊙→ X₁)
    (gY : Y₁ ⊙→ Y₂) (fY : Y₀ ⊙→ Y₁)
    → ⊙∨-fmap (gX ⊙∘ fX) (gY ⊙∘ fY) ⊙∼ ⊙∨-fmap gX gY ⊙∘ ⊙∨-fmap fX fY
  ⊙∨-fmap-∘ (gX , idp) (fX , idp) (gY , idp) (fY , idp) =
    ∨-fmap-∘ (gX , idp) (fX , idp) (gY , idp) (fY , idp) , idp

  Wedge-fmap-∘ = ∨-fmap-∘
  ⊙Wedge-fmap-∘ = ⊙∨-fmap-∘

module _ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'}
  (eqX : X ⊙≃ X') (eqY : Y ⊙≃ Y') where

  wedge-span-emap : SpanEquiv (wedge-span X Y) (wedge-span X' Y')
  wedge-span-emap = wedge-span-map (⊙–> eqX) (⊙–> eqY)
                  , snd eqX , snd eqY , idf-is-equiv _

  ∨-emap : X ∨ Y ≃ X' ∨ Y'
  ∨-emap = Pushout-emap wedge-span-emap

  ⊙∨-emap : X ⊙∨ Y ⊙≃ X' ⊙∨ Y'
  ⊙∨-emap = ≃-to-⊙≃ ∨-emap (ap winl (⊙–>-pt eqX))

  Wedge-emap = ∨-emap
  ⊙Wedge-emap = ⊙∨-emap

module _ {i i' j j' k} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j}
  {Y' : Ptd j'} {Z : Ptd k} (winl* : X' ⊙→ Z) (winr* : Y' ⊙→ Z)
  (f : X ⊙→ X') (g : Y ⊙→ Y') where

  ⊙Wedge-rec-fmap :
       ⊙Wedge-rec winl* winr* ⊙∘ ⊙∨-fmap f g
    ⊙∼ ⊙Wedge-rec (winl* ⊙∘ f) (winr* ⊙∘ g)
  ⊙Wedge-rec-fmap =
    Wedge-elim (λ _ → idp) (λ _ → idp) (↓-='-in' $ ! $ lemma₀ winl* winr* f g) ,
    lemma₁ winl* winr* f
    where
      abstract
        lemma₀ : ∀ {X' Y' Z} (winl* : X' ⊙→ Z) (winr* : Y' ⊙→ Z)
          (f : X ⊙→ X') (g : Y ⊙→ Y')
          →  ap (⊙WedgeRec.f winl* winr* ∘ ∨-fmap f g) wglue
          == ap (⊙WedgeRec.f (winl* ⊙∘ f) (winr* ⊙∘ g)) wglue
        lemma₀ (winl* , idp) (winr* , winr*-pt) (f , idp) (g , idp) =
          ap (Wedge-rec winl* winr* (! winr*-pt) ∘ ∨-fmap (f , idp) (g , idp)) wglue
            =⟨ ap-∘ (Wedge-rec winl* winr* (! winr*-pt)) (∨-fmap (f , idp) (g , idp)) wglue ⟩
          ap (Wedge-rec winl* winr* (! winr*-pt)) (ap (∨-fmap (f , idp) (g , idp)) wglue)
            =⟨ ap (ap (Wedge-rec winl* winr* (! winr*-pt))) $ WedgeFmap.glue-β (f , idp) (g , idp) ⟩
          ap (Wedge-rec winl* winr* (! winr*-pt)) wglue
            =⟨ WedgeRec.glue-β winl* winr* (! winr*-pt) ⟩
          ! winr*-pt
            =⟨ ! $ WedgeRec.glue-β (winl* ∘ f) (winr* ∘ g) (! winr*-pt) ⟩
          ap (Wedge-rec (winl* ∘ f) (winr* ∘ g) (! winr*-pt)) wglue
            =∎

        lemma₁ : ∀ {X' Z} (winl* : X' ⊙→ Z) (winr* : Y' ⊙→ Z) (f : X ⊙→ X')
          →  snd (⊙Wedge-rec winl* winr* ⊙∘ ⊙∨-fmap f g)
          == snd (⊙Wedge-rec (winl* ⊙∘ f) (winr* ⊙∘ g))
        lemma₁ (f , idp) _ (winl* , idp) = idp
  ⊙∨-rec-fmap = ⊙Wedge-rec-fmap

module _ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j}
  {Y' : Ptd j'} (f : X ⊙→ X') (g : Y ⊙→ Y') where

  ⊙projl-fmap : ⊙projl ⊙∘ ⊙∨-fmap f g ⊙∼ ⊙Wedge-rec f ⊙cst
  ⊙projl-fmap =
    Wedge-elim (λ _ → idp) (λ _ → idp) (↓-='-in' $ ! $ lemma₀ f g) , lemma₁ f g
    where
      abstract
        lemma₀ : ∀ {X' Y'} (f : X ⊙→ X') (g : Y ⊙→ Y')
          →  ap (projl ∘ ∨-fmap f g) wglue
          == ap (⊙WedgeRec.f f ⊙cst) wglue
        lemma₀ (f , idp) (g , idp) =
          ap (projl ∘ ∨-fmap (f , idp) (g , idp)) wglue
            =⟨ ap-∘ projl (∨-fmap (f , idp) (g , idp)) wglue ⟩
          ap projl (ap (∨-fmap (f , idp) (g , idp)) wglue)
            =⟨ ap (ap projl) (WedgeFmap.glue-β (f , idp) (g , idp)) ⟩
          ap projl wglue
            =⟨ Projl.glue-β ⟩
          idp
            =⟨ ! $ ⊙WedgeRec.glue-β (f , idp) ⊙cst ⟩
          ap (⊙WedgeRec.f (f , idp) ⊙cst) wglue
            =∎

        lemma₁ : ∀ {X' Y'} (f : X ⊙→ X') (g : Y ⊙→ Y')
          →  snd (⊙projl ⊙∘ ⊙∨-fmap f g)
          == snd (⊙Wedge-rec {Y = Y} f ⊙cst)
        lemma₁ (f , idp) (g , idp) = idp

  ⊙projr-fmap : ⊙projr ⊙∘ ⊙∨-fmap f g ⊙∼ ⊙Wedge-rec ⊙cst g
  ⊙projr-fmap =
    Wedge-elim (λ _ → idp) (λ _ → idp) (↓-='-in' $ ! $ lemma₀ f g) , lemma₁ f g
    where
      abstract
        lemma₀ : ∀ {X' Y'} (f : X ⊙→ X') (g : Y ⊙→ Y')
          →  ap (projr ∘ ∨-fmap f g) wglue
          == ap (⊙WedgeRec.f ⊙cst g) wglue
        lemma₀ (f , idp) (g , idp) =
          ap (projr ∘ ∨-fmap (f , idp) (g , idp)) wglue
            =⟨ ap-∘ projr (∨-fmap (f , idp) (g , idp)) wglue ⟩
          ap projr (ap (∨-fmap (f , idp) (g , idp)) wglue)
            =⟨ ap (ap projr) (WedgeFmap.glue-β (f , idp) (g , idp)) ⟩
          ap projr wglue
            =⟨ Projr.glue-β ⟩
          idp
            =⟨ ! $ ⊙WedgeRec.glue-β ⊙cst (g , idp) ⟩
          ap (⊙WedgeRec.f ⊙cst (g , idp)) wglue
            =∎

        lemma₁ : ∀ {X' Y'} (f : X ⊙→ X') (g : Y ⊙→ Y')
          →  snd (⊙projr ⊙∘ ⊙∨-fmap f g)
          == snd (⊙Wedge-rec {X = X} ⊙cst g)
        lemma₁ (f , idp) (g , idp) = idp

module _ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (f : X ⊙→ Z) (g : Y ⊙→ Z) where

  {- favonia: This is a special case, but still proved separately to make sure
     it has good computational content. (Maybe this is overkilling.) -}
  ⊙fold-fmap : ⊙fold ⊙∘ ⊙∨-fmap f g ⊙∼ ⊙Wedge-rec f g
  ⊙fold-fmap =
    Wedge-elim (λ _ → idp) (λ _ → idp) (↓-='-in' $ ! $ lemma₀ f g) , lemma₁ f g
    where
      abstract
        lemma₀ : ∀ {Z} (f : X ⊙→ Z) (g : Y ⊙→ Z)
          →  ap (⊙WedgeRec.f (⊙idf _) (⊙idf _) ∘ ∨-fmap f g) wglue
          == ap (⊙WedgeRec.f f g) wglue
        lemma₀ (f , idp) (g , g-pt) =
          ap (⊙WedgeRec.f (⊙idf _) (⊙idf _) ∘ ∨-fmap (f , idp) (g , g-pt)) wglue
            =⟨ ap-∘
                (⊙WedgeRec.f (⊙idf _) (⊙idf _))
                (∨-fmap (f , idp) (g , g-pt))
                wglue ⟩
          ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)) (ap (∨-fmap (f , idp) (g , g-pt)) wglue)
            =⟨ ap (ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)))
                  (WedgeFmap.glue-β (f , idp) (g , g-pt)) ⟩
          ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)) (wglue ∙' ap winr (! g-pt))
            =⟨ ap-∙' (⊙WedgeRec.f (⊙idf _) (⊙idf _)) wglue (ap winr (! g-pt)) ⟩
          ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)) wglue
            ∙' ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)) (ap winr (! g-pt))
            =⟨ ap2 _∙'_
                (⊙WedgeRec.glue-β (⊙idf _) (⊙idf _))
                ( ∘-ap (⊙WedgeRec.f (⊙idf _) (⊙idf _)) winr (! g-pt)
                ∙ ap-idf (! g-pt)) ⟩
          idp ∙' ! g-pt
            =⟨ ∙'-unit-l (! g-pt) ⟩
          ! g-pt
            =⟨ ! $ ⊙WedgeRec.glue-β (f , idp) (g , g-pt) ⟩
          ap (⊙WedgeRec.f (f , idp) (g , g-pt) ) wglue
            =∎

        lemma₁ : ∀ {Z} (f : X ⊙→ Z) (g : Y ⊙→ Z)
          →  snd (⊙Wedge-rec (⊙idf _) (⊙idf _) ⊙∘ ⊙∨-fmap f g)
          == snd (⊙Wedge-rec f g)
        lemma₁ (f , idp) (g , g-pt) = idp

module _ {i j k} (X : Ptd i) (Y : Ptd j) (Z : Ptd k) where

  module WedgeAssocInl = WedgeRec {C = X ∨ (Y ⊙∨ Z)} winl (winr ∘ winl) wglue
  module WedgeAssoc = WedgeRec {X = X ⊙∨ Y} WedgeAssocInl.f (winr ∘ winr) (wglue ∙ ap winr wglue)

  ∨-assoc : (X ⊙∨ Y) ∨ Z ≃ X ∨ (Y ⊙∨ Z)
  ∨-assoc = equiv to from to-from from-to where

    to : (X ⊙∨ Y) ∨ Z → X ∨ (Y ⊙∨ Z)
    to = WedgeAssoc.f

    module FromInr = WedgeRec {C = (X ⊙∨ Y) ∨ Z} (winl ∘ winr) winr (! (ap winl wglue) ∙ wglue)
    module From = WedgeRec {Y = Y ⊙∨ Z} (winl ∘ winl) FromInr.f (ap winl wglue)

    from : X ∨ (Y ⊙∨ Z) → (X ⊙∨ Y) ∨ Z
    from = From.f

    abstract
      to-from : ∀ x → to (from x) == x
      to-from = Wedge-elim
        (λ x → idp)
        (Wedge-elim (λ y → idp) (λ z → idp) $ ↓-='-in' $ ! $
          ap (to ∘ FromInr.f) wglue
            =⟨ ap-∘ to FromInr.f wglue ⟩
          ap to (ap FromInr.f wglue)
            =⟨ ap (ap to) FromInr.glue-β ⟩
          ap to (! (ap winl wglue) ∙ wglue)
            =⟨ ap-∙ to (! (ap winl wglue)) wglue ⟩
          ap to (! (ap winl wglue)) ∙ ap to wglue
            =⟨ _∙2_ (ap-! to (ap winl wglue) ∙ ap ! (∘-ap to winl wglue ∙ WedgeAssocInl.glue-β)) WedgeAssoc.glue-β ⟩
          ! wglue ∙ wglue ∙ ap winr wglue
            =⟨ ! $ ∙-assoc (! wglue) wglue (ap winr wglue) ⟩
          (! wglue ∙ wglue) ∙ ap winr wglue
            =⟨ ap (_∙ ap winr wglue) (!-inv-l wglue) ⟩
          ap winr wglue
            =∎)
        (↓-∘=idf-in' to from (ap (ap to) From.glue-β ∙ ∘-ap to winl wglue ∙ WedgeAssocInl.glue-β))

      from-to : ∀ x → from (to x) == x
      from-to = Wedge-elim
        (Wedge-elim (λ x → idp) (λ y → idp) $ ↓-='-in' $ ! $
          ap-∘ from WedgeAssocInl.f wglue ∙ ap (ap from) WedgeAssocInl.glue-β ∙ From.glue-β)
        (λ z → idp)
        (↓-∘=idf-in' from to $
          ap from (ap to wglue)
            =⟨ ap (ap from) WedgeAssoc.glue-β ⟩
          ap from (wglue ∙ ap winr wglue)
            =⟨ ap-∙ from wglue (ap winr wglue) ⟩
          ap from wglue ∙ ap from (ap winr wglue)
            =⟨ From.glue-β ∙2 (∘-ap from winr wglue ∙ FromInr.glue-β) ⟩
          ap winl wglue ∙ ! (ap winl wglue) ∙ wglue
            =⟨ ! $ ∙-assoc (ap winl wglue) (! (ap winl wglue)) wglue ⟩
          (ap winl wglue ∙ ! (ap winl wglue)) ∙ wglue
            =⟨ ap (_∙ wglue) (!-inv-r (ap winl wglue)) ⟩
          wglue
            =∎)

  ⊙∨-assoc : (X ⊙∨ Y) ⊙∨ Z ⊙≃ X ⊙∨ (Y ⊙∨ Z)
  ⊙∨-assoc = ≃-to-⊙≃ ∨-assoc idp

{-
module _ {i₀ i₁ j₀ j₁ k₀ k₁}
  {X₀ : Ptd i₀} {Y₀ : Ptd j₀} {Z₀ : Ptd k₀}
  {X₁ : Ptd i₁} {Y₁ : Ptd j₁} {Z₁ : Ptd k₁}
  where

  ⊙∨-assoc-nat : ∀ (f : X₀ ⊙→ X₁) (g : Y₀ ⊙→ Y₁) (h : Z₀ ⊙→ Z₁)
    →  ⊙–> (⊙∨-assoc X₁ Y₁ Z₁) ⊙∘ ⊙∨-fmap (⊙∨-fmap f g) h
    ⊙∼ ⊙∨-fmap f (⊙∨-fmap g h) ⊙∘ ⊙–> (⊙∨-assoc X₀ Y₀ Z₀)
  ⊙∨-assoc-nat (f , idp) (g , idp) (h , idp) =
    (Wedge-elim
      -- {P = –> (∨-assoc X₁ Y₁ Z₁) ∘ ∨-fmap (⊙∨-fmap f g) h ∼ ∨-fmap f (⊙∨-fmap g h) ∘ –> (∨-assoc X₀ Y₀ Z₀)}
      (Wedge-elim (λ _ → idp) (λ _ → idp)
        (↓-='-in' $
          ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp)) ∘ WedgeAssocInl.f X₀ Y₀ Z₀) wglue
            =⟨ ap-∘ (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (WedgeAssocInl.f X₀ Y₀ Z₀) wglue ⟩
          ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (ap (WedgeAssocInl.f X₀ Y₀ Z₀) wglue)
            =⟨ ap (ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp)))) (WedgeAssocInl.glue-β X₀ Y₀ Z₀) ⟩
          ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) wglue
            =⟨ WedgeFmap.glue-β (f , idp) (⊙∨-fmap (g , idp) (h , idp)) ⟩
          wglue
            =⟨ ! $ WedgeAssocInl.glue-β X₁ Y₁ Z₁ ⟩
          ap (WedgeAssocInl.f X₁ Y₁ Z₁) wglue
            =⟨ ! $ ap (ap (WedgeAssocInl.f X₁ Y₁ Z₁)) $ WedgeFmap.glue-β (f , idp) (g , idp) ⟩
          ap (WedgeAssocInl.f X₁ Y₁ Z₁) (ap (∨-fmap (f , idp) (g , idp)) wglue)
            =⟨ ∘-ap (WedgeAssocInl.f X₁ Y₁ Z₁) (∨-fmap (f , idp) (g , idp)) wglue ⟩
          ap (WedgeAssocInl.f X₁ Y₁ Z₁ ∘ ∨-fmap (f , idp) (g , idp)) wglue
            =∎))
      (λ _ → idp)
      (↓-='-in' $
        ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp)) ∘ WedgeAssoc.f X₀ Y₀ Z₀) wglue
          =⟨ ap-∘ (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (WedgeAssoc.f X₀ Y₀ Z₀) wglue ⟩
        ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (ap (WedgeAssoc.f X₀ Y₀ Z₀) wglue)
          =⟨ ap (ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp)))) (WedgeAssoc.glue-β X₀ Y₀ Z₀) ⟩
        ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (wglue ∙ ap winr wglue)
          =⟨ ap-∙ (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) wglue (ap winr wglue) ⟩
        ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) wglue
        ∙ ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) (ap winr wglue)
          =⟨ ap2 _∙_
               (WedgeFmap.glue-β (f , idp) (⊙∨-fmap (g , idp) (h , idp)))
               ( ∘-ap (∨-fmap (f , idp) (⊙∨-fmap (g , idp) (h , idp))) winr wglue
               ∙ ap-∘ winr (∨-fmap (g , idp) (h , idp)) wglue
               ∙ ap (ap winr) (WedgeFmap.glue-β (g , idp) (h , idp))) ⟩
        wglue ∙ ap winr wglue
          =⟨ ! $ WedgeAssoc.glue-β X₁ Y₁ Z₁ ⟩
        ap (WedgeAssoc.f X₁ Y₁ Z₁) wglue
          =⟨ ! $ ap (ap (WedgeAssoc.f X₁ Y₁ Z₁)) $ WedgeFmap.glue-β (⊙∨-fmap (f , idp) (g , idp)) (h , idp) ⟩
        ap (WedgeAssoc.f X₁ Y₁ Z₁) (ap (∨-fmap (⊙∨-fmap (f , idp) (g , idp)) (h , idp)) wglue)
          =⟨ ∘-ap (WedgeAssoc.f X₁ Y₁ Z₁) (∨-fmap (⊙∨-fmap (f , idp) (g , idp)) (h , idp)) wglue ⟩
        ap (WedgeAssoc.f X₁ Y₁ Z₁ ∘ ∨-fmap (⊙∨-fmap (f , idp) (g , idp)) (h , idp)) wglue
          =∎))
    ,
    idp
-}

module _ {i j k l} {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
  (f : X ⊙→ W) (g : Y ⊙→ W) (h : Z ⊙→ W) where

  ⊙Wedge-rec-assoc : ⊙Wedge-rec (⊙Wedge-rec f g) h
    ⊙∼ ⊙Wedge-rec f (⊙Wedge-rec g h) ⊙∘ ⊙–> (⊙∨-assoc X Y Z)
  ⊙Wedge-rec-assoc =
    (Wedge-elim
      (Wedge-elim (λ x → idp) (λ y → idp)
        (↓-='-in' $
            ap-∘ (⊙WedgeRec.f f (⊙Wedge-rec g h)) (WedgeAssocInl.f X Y Z) wglue
          ∙ ap (ap (⊙WedgeRec.f f (⊙Wedge-rec g h))) (WedgeAssocInl.glue-β X Y Z)
          ∙ ⊙WedgeRec.glue-β f (⊙Wedge-rec g h)
          ∙ ! (⊙WedgeRec.glue-β f g)))
      (λ z → idp)
      (↓-='-in' $
          ap-∘ (⊙WedgeRec.f f (⊙Wedge-rec g h)) (WedgeAssoc.f X Y Z) wglue
        ∙ ap (ap (⊙WedgeRec.f f (⊙Wedge-rec g h))) (WedgeAssoc.glue-β X Y Z)
        ∙ ap-∙ (⊙WedgeRec.f f (⊙Wedge-rec g h)) wglue (ap winr wglue)
        ∙ _∙2_ (⊙WedgeRec.glue-β f (⊙Wedge-rec g h))
               ( ∘-ap (⊙WedgeRec.f f (⊙Wedge-rec g h)) winr wglue
               ∙ ⊙WedgeRec.glue-β g h)
        ∙ ∙-assoc (snd f) (! (snd g)) (snd g ∙ ! (snd h))
        ∙ ap (snd f ∙_) (! $ ∙-assoc (! (snd g)) (snd g) (! (snd h)))
        ∙ ap (λ p → snd f ∙ p ∙ ! (snd h)) (!-inv-l (snd g))
        ∙ ! (⊙WedgeRec.glue-β (⊙Wedge-rec f g) h)))
    ,
    idp
  ⊙∨-rec-assoc = ⊙Wedge-rec-assoc


open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
module DTGP
  {Domain Word : Set}
  (pre post : Word → Domain → Domain)
  (_≟_ : (x y : Domain) → Dec (x ≡ y))
where
open import Function
open import Relation.Binary
open import Data.Bool hiding (_≟_)
open import Data.Nat hiding (_≥_; _≟_)
open import Data.Fin hiding (_+_; raise)
open import Data.Maybe
open import Data.Product hiding (map; swap)
open import Data.List hiding (length) renaming (_++_ to _l++_)
open import Data.Vec hiding (_++_; _>>=_; concat; map; init)
open import DTGP.Rand

infixr 5 _∷_ _++_ _++'_

data Term (inp : Domain) : Domain → Set where
  []  : Term inp inp

  _∷_ : ∀ {d} (w : Word) →
    Term inp (pre w d) → Term inp (post w d)

_++_ : ∀ {inp mid out} →
  Term mid out →
  Term inp mid →
  Term inp out
[] ++ ys = ys
(x ∷ xs) ++ ys = x ∷ (xs ++ ys)

data Split {inp out} mid : Term inp out → Set where
  _++'_ :
    (xs : Term mid out)
    (ys : Term inp mid) →
    Split mid (xs ++ ys)

swap₁ : ∀ {inp mid out} {xs ys : Term inp out} →
  Split mid xs → Split mid ys → Term inp out
swap₁ (xs ++' ys) (as ++' bs) = xs ++ bs

swap₂ : ∀ {inp mid out} {xs ys : Term inp out} →
  Split mid xs → Split mid ys → Term inp out
swap₂ (xs ++' ys) (as ++' bs) = as ++ ys

swaps : ∀ {inp mid out} {xs ys : Term inp out} →
  Split mid xs → Split mid ys →
  Term inp out × Term inp out
swaps xs ys = swap₁ xs ys , swap₂ xs ys

split : ∀ {inp out} (n : ℕ) (xs : Term inp out) → ∃ λ mid → Split mid xs
split zero xs = _ , [] ++' xs
split (suc n) [] = _ , [] ++' []
split (suc n) (x ∷ xs) with split n xs
split (suc n) (x ∷ ._) | _ , xs ++' ys = _ , (x ∷ xs) ++' ys

splits : ∀ {inp out} (n : ℕ) mid → (xs : Term inp out) → ∃ (Vec (Split mid xs))
splits zero mid xs with split zero xs
... | mid' , ys with mid ≟ mid'
... | yes p rewrite p = _ , ys ∷ []
... | no p = _ , []
splits (suc n) mid xs with split (suc n) xs
... | mid' , ys with mid ≟ mid' | splits n mid xs
... | yes p | _ , yss rewrite p = _ , ys ∷ yss
... | no p | _ , yss = _ , yss

length : ∀ {inp out} → Term inp out → ℕ
length [] = 0
length (x ∷ xs) = suc (length xs)

split♀ : ∀ {inp out} → (xs : Term inp out) → Rand (∃ λ mid → Split mid xs)
split♀ xs =
  rand >>= λ r →
  let i = r mod (suc (length xs))
  in return (split (toℕ i) xs)

split♂ : ∀ {inp out} (xs : Term inp out) mid →
  Maybe (Rand (Split mid xs))
split♂ xs B
  with splits (length xs) B xs
... | zero , [] = nothing
... | suc n , xss = just (
  rand >>= λ r →
  return (lookup (r mod suc n) xss)
 )

crossover : ∀ {inp out} (♀ ♂ : Term inp out) →
  Rand (Term inp out × Term inp out)
crossover ♀ ♂ =
  split♀ ♀ >>= λ b,xs →
  maybe′
    (_=<<_ (return ∘ (swaps (proj₂ b,xs))))
    (return (♀ , ♂))
    (split♂ ♂ (proj₁ b,xs))

Population : ∀ inp out n → Set
Population inp out n = Vec (Term inp out) (2 + n)

module Initialization
  (match : ∀ w out → Dec (∃ λ d → out ≡ pre w d))
  where

  toMaybe : ∀ {w inp out} →
    Term inp out →
    Dec (∃ λ d → out ≡ pre w d) →
    Maybe (∃ λ d → Term inp d)
  toMaybe {w = w} ws (no _) = nothing
  toMaybe {w = w} ws (yes (_ , p))
    rewrite p = just (_ , w ∷ ws)

  enum-inp : ∀ (n : ℕ) inp → List Word → List (∃ λ out → Term inp out)
  enum-inp zero inp ws = gfilter (λ w → toMaybe [] (match w inp)) ws
  enum-inp (suc n) A ws
    with enum-inp n A ws
  ... | ih = concat (map (λ out,t → gfilter (λ w →
    toMaybe (proj₂ out,t) (match w (proj₁ out,t))
      ) ws) ih) l++ ih

  filter-out : ∀ {inp} out → List (∃ (Term inp)) → List (Term inp out)
  filter-out out [] = []
  filter-out out ((out' , x) ∷ xs)
    with out' ≟ out
  ... | no p = filter-out out xs
  ... | yes p rewrite p = x ∷ filter-out out xs

  init : ∀ (n : ℕ) inp out → List Word → List (Term inp out)
  init n inp out ws = filter-out out (enum-inp n inp ws)

module Evolution {inp out} (score : Term inp out → ℕ) where

  _≥_ : ℕ → ℕ → Bool
  zero ≥ zero = true
  zero ≥ (suc n) = false
  (suc m) ≥ zero = true
  (suc m) ≥ (suc n) = m ≥ n

  select : ∀ {n} →
    Population inp out n → Rand (Term inp out)
  select {n = n} xss =
    rand >>= λ ii →
    rand >>= λ jj →
    let ♀ = lookup (ii mod (2 + n)) xss
        ♂ = lookup (jj mod (2 + n)) xss
    in return $
      if score ♀ ≥ score ♂
      then ♀ else ♂

  breed2 : ∀ {n} →
    Population inp out n →
    Rand (Term inp out × Term inp out)
  breed2 xss =
    select xss >>= λ ♀ →
    select xss >>= λ ♂ →
    crossover ♀ ♂

  breedN : ∀ {m} → (n : ℕ) →
    Population inp out m →
    Rand (Vec (Term inp out) n)
  breedN zero xss = return []
  breedN (suc n) xss =
    breed2 xss >>= λ offspring →
    breedN n xss >>= λ ih →
    return (proj₁ offspring ∷ ih)

  evolve1 : ∀ {n} →
    Population inp out n → Rand (Population inp out n)
  evolve1 xss = breedN _ xss

  evolveN : ∀ {n} → (gens : ℕ) →
    Population inp out n → Rand (Population inp out n)
  evolveN zero xss = return xss
  evolveN (suc gens) xss =
    evolveN gens xss >>= evolve1

  evolve : ∀ {n} → (seed gens : ℕ) →
    Population inp out n → Population inp out n
  evolve seed gens xss =
    runRand (evolveN gens xss) seed


{-# OPTIONS --without-K #-}

open import lib.Basics
open import lib.NConnected
open import lib.types.TLevel
open import lib.NType2
open import lib.types.Pi
open import lib.types.Sigma
open import lib.types.Pointed
open import lib.types.LoopSpace
open import lib.types.Truncation
open import lib.types.PathSet

module lib.types.Cover where

{-
  The definition of covering spaces.
-}
record Cover {i} (A : Type i) j : Type (lmax i (lsucc j)) where
  constructor cover
  field
    Fiber : A → Type j
    Fiber-level : ∀ a → is-set (Fiber a)
  Fiber-is-set = Fiber-level
  TotalSpace = Σ A Fiber

-- Basic lemmas
module _ {i} {A : Type i} {j} where

  open Cover

  -- Equality between covers.
  private
    cover=′ : {c₁ c₂ : Cover A j} → Fiber c₁ == Fiber c₂ → c₁ == c₂
    cover=′ {cover f _} {cover .f _} idp = ap (cover f) $
      prop-has-all-paths (Π-is-prop λ _ → is-set-is-prop) _ _

  cover= : {c₁ c₂ : Cover A j} → (∀ a → Fiber c₁ a ≃ Fiber c₂ a)
    → c₁ == c₂
  cover= F≃ = cover=′ (λ= λ a → ua $ F≃ a)

  -- The definition of universality in terms of connectedness.
  is-universal : Cover A j → Type (lmax i j)
  is-universal cov = is-connected 1 $ TotalSpace cov

  -- In terms of connectedness
  UniversalCover : Type (lmax i (lsucc j))
  UniversalCover = Σ (Cover A j) is-universal

-- Theorem: A covering space keeps higher homotopy groups.
module _ {i} (A∙ : Ptd i)
  {j} (c : Cover (fst A∙) j)
  (a↑ : Cover.Fiber c (snd A∙)) where

  open Cover c
  private
    F = Cover.Fiber c
    F-level = Cover.Fiber-level c
    A = fst A∙
    a = snd A∙

  private
    -- The projection map with one end free (in order to apply J).
    to′ : ∀ {p⇑ : _==_ {A = Σ A F} (a , a↑) (a , a↑)}
        → idp == p⇑ → idp == fst= p⇑
    to′ idp=p⇑ = ap fst= idp=p⇑

    -- The projection map from Ω²(Σ A F) to Ω²(A).
    to : Ω^ 2 ⊙[ Σ A F , (a , a↑) ] → Ω^ 2 A∙
    to p²⇑ = to′ {p⇑ = idp} p²⇑

    -- Auxiliary synthesized path for injection.
    idp=p↑ : ∀ {p : a == a} {p↑ : a↑ == a↑ [ F ↓ p ]}
             → (idp=p : idp == p)
             → idp == p↑
               [ (λ p → a↑ == a↑ [ F ↓ p ]) ↓ idp=p ]
    idp=p↑ idp = prop-has-all-paths (F-level a _ _) _ _

    -- The injection map with some ends free (in order to apply J).
    from′ : ∀ {p : a == a} {p↑ : a↑ == a↑ [ F ↓ p ]}
            → (idp=p : idp == p)
            → idp == p↑ -- aux
              [ (λ p → a↑ == a↑ [ F ↓ p ]) ↓ idp=p ]
            → idp == pair= p p↑
    from′ idp=p idp=p↑ = pair== idp=p idp=p↑

    -- The injection map from Ω²(A) to Ω²(Σ A F).
    from : Ω^ 2 A∙ → Ω^ 2 ⊙[ Σ A F , (a , a↑) ]
    from p² = from′ {p = idp} {p↑ = idp} p² (idp=p↑ p²)

    -- Injection is left-inverse to projection (with some ends free).
    from′-to′ : ∀ {p⇑ : _==_ {A = Σ A F} (a , a↑) (a , a↑)}
                → (idp=p⇑ : idp == p⇑)
                → (idp=snd=p⇑ : idp == snd= p⇑ -- aux
                  [ (λ p → a↑ == a↑ [ F ↓ p ]) ↓ to′ idp=p⇑ ])
                → from′ (to′ idp=p⇑) idp=snd=p⇑ == idp=p⇑
                  [ (λ p⇑ → idp == p⇑) ↓ ! (pair=-η p⇑) ]
    from′-to′ idp idp=snd=p⇑ = ap (from′ idp)
      $ contr-has-all-paths (F-level a _ _ _ _) _ _

    -- Injection is left-inverse to projection.
    from-to : ∀ p²⇑ → from (to p²⇑) == p²⇑
    from-to p²⇑ = from′-to′ {p⇑ = idp} p²⇑ (idp=p↑ (to′ p²⇑))

    -- Injection is right-inverse to projection (with some ends free).
    to′-from′ : ∀ {p : a == a} {p↑ : a↑ == a↑ [ F ↓ p ]}
                → (idp=p : idp == p)
                → (idp=p↑ : idp == p↑ -- aux
                  [ (λ p → a↑ == a↑ [ F ↓ p ]) ↓ idp=p ])
                → to′ (from′ idp=p idp=p↑) == idp=p
                  [ (λ p → idp == p) ↓ fst=-β p p↑ ]
    to′-from′ idp idp = idp

    -- Injection is right-inverse to projection.
    to-from : ∀ p² → to (from p²) == p²
    to-from p² = to′-from′ p² (idp=p↑ p²)

  -- The theorem.
  Ω²ΣAFiber≃Ω²A : Ω^ 2 (Σ A F , (a , a↑)) ≃ Ω^ 2 A∙
  Ω²ΣAFiber≃Ω²A = to , is-eq to from to-from from-to

-- A natural way to construct a G-set from covering spaces.
module _ {i} {A : Type i} where
  open Cover

  cover-trace : ∀ {j} (cov : Cover A j) {a₁ a₂}
    → Fiber cov a₁ → a₁ =₀ a₂
    → Fiber cov a₂
  cover-trace cov a↑ p =
    transport₀ (Fiber cov) (Fiber-is-set cov _) p a↑

  abstract
    cover-trace-idp₀ : ∀ {j} (cov : Cover A j) {a₁}
      → (a↑ : Fiber cov a₁)
      → cover-trace cov a↑ idp₀ == a↑
    cover-trace-idp₀ cov a↑ = idp

    cover-paste : ∀ {j} (cov : Cover A j) {a₁ a₂}
      → (a↑ : Fiber cov a₁)
      → (loop : a₁ =₀ a₁)
      → (p : a₁ =₀ a₂)
      → cover-trace cov (cover-trace cov a↑ loop) p
      == cover-trace cov a↑ (loop ∙₀ p)
    cover-paste cov a↑ loop p = ! $ trans₀-∙₀ (Fiber-is-set cov) loop p a↑

-- Path sets form a covering space
module _ {i} (A∙ : Ptd i) where
  path-set-cover : Cover (fst A∙) i
  path-set-cover = record
    { Fiber = λ a → snd A∙ =₀ a
    ; Fiber-level = λ a → Trunc-level
    }

-- Cover morphisms
CoverHom : ∀ {i} {A : Type i} {j₁ j₂}
  → (cov1 : Cover A j₁)
  → (cov2 : Cover A j₂)
  → Type (lmax i (lmax j₁ j₂))
CoverHom (cover F₁ _) (cover F₂ _) = ∀ a → F₁ a → F₂ a


{-# OPTIONS --without-K #-}

open import HoTT

{- The pseudo-adjoint functors F,G : Ptd → Ptd
 - It stops at composition and ignores
 - all the higher associahedrons.
 -}

module homotopy.PtdAdjoint where

record PtdFunctor i j : Type (lsucc (lmax i j)) where
  field
    obj : Ptd i → Ptd j
    arr : {X Y : Ptd i} → fst (X ⊙→ Y) → fst (obj X ⊙→ obj Y)
    id : (X : Ptd i) → arr (⊙idf X) == ⊙idf (obj X)
    comp : {X Y Z : Ptd i} (g : fst (Y ⊙→ Z)) (f : fst (X ⊙→ Y))
      → arr (g ⊙∘ f) == arr g ⊙∘ arr f

{- counit-unit description of F ⊣ G -}
record CounitUnitAdjoint {i j} (F : PtdFunctor i j) (G : PtdFunctor j i)
  : Type (lsucc (lmax i j)) where

  private
    module F = PtdFunctor F
    module G = PtdFunctor G

  field
    η : (X : Ptd i) → fst (X ⊙→ G.obj (F.obj X))
    ε : (U : Ptd j) → fst (F.obj (G.obj U) ⊙→ U)

    η-natural : {X Y : Ptd i} (h : fst (X ⊙→ Y))
      → η Y ⊙∘ h == G.arr (F.arr h) ⊙∘ η X
    ε-natural : {U V : Ptd j} (k : fst (U ⊙→ V))
      → ε V ⊙∘ F.arr (G.arr k) == k ⊙∘ ε U

    εF-Fη : (X : Ptd i) → ε (F.obj X) ⊙∘ F.arr (η X) == ⊙idf (F.obj X)
    Gε-ηG : (U : Ptd j) → G.arr (ε U) ⊙∘ η (G.obj U) == ⊙idf (G.obj U)

{- hom-set isomorphism description of F ⊣ G -}
record HomAdjoint {i j} (F : PtdFunctor i j) (G : PtdFunctor j i)
  : Type (lsucc (lmax i j)) where

  private
    module F = PtdFunctor F
    module G = PtdFunctor G

  field
    eq : (X : Ptd i) (U : Ptd j) → fst (F.obj X ⊙→ U) ≃ fst (X ⊙→ G.obj U)

    nat-dom : {X Y : Ptd i} (h : fst (X ⊙→ Y)) (U : Ptd j)
      (r : fst (F.obj Y ⊙→ U))
      → –> (eq Y U) r ⊙∘ h == –> (eq X U) (r ⊙∘ F.arr h)

    nat-cod : (X : Ptd i) {U V : Ptd j} (k : fst (U ⊙→ V))
      (r : fst (F.obj X ⊙→ U))
      → G.arr k ⊙∘ –> (eq X U) r == –> (eq X V) (k ⊙∘ r)

  nat!-dom : {X Y : Ptd i} (h : fst (X ⊙→ Y)) (U : Ptd j)
    (s : fst (Y ⊙→ G.obj U))
    → <– (eq Y U) s ⊙∘ F.arr h == <– (eq X U) (s ⊙∘ h)
  nat!-dom {X} {Y} h U s =
    ! (<–-inv-l (eq X U) (<– (eq Y U) s ⊙∘ F.arr h))
    ∙ ap (<– (eq X U)) (! (nat-dom h U (<– (eq Y U) s)))
    ∙ ap (λ w → <– (eq X U) (w ⊙∘ h)) (<–-inv-r (eq Y U) s)

  nat!-cod : (X : Ptd i) {U V : Ptd j} (k : fst (U ⊙→ V))
    (s : fst (X ⊙→ G.obj U))
    → k ⊙∘ <– (eq X U) s == <– (eq X V) (G.arr k ⊙∘ s)
  nat!-cod X {U} {V} k s =
    ! (<–-inv-l (eq X V) (k ⊙∘ <– (eq X U) s))
    ∙ ap (<– (eq X V)) (! (nat-cod X k (<– (eq X U) s)))
    ∙ ap (λ w → <– (eq X V) (G.arr k ⊙∘ w)) (<–-inv-r (eq X U) s)

counit-unit-to-hom : ∀ {i j} {F : PtdFunctor i j} {G : PtdFunctor j i}
  → CounitUnitAdjoint F G → HomAdjoint F G
counit-unit-to-hom {i} {j} {F} {G} adj = record {
  eq = eq;
  nat-dom = nat-dom;
  nat-cod = nat-cod}
  where
  module F = PtdFunctor F
  module G = PtdFunctor G
  open CounitUnitAdjoint adj

  module _ (X : Ptd i) (U : Ptd j) where

    into : fst (F.obj X ⊙→ U) → fst (X ⊙→ G.obj U)
    into r = G.arr r ⊙∘ η X

    out : fst (X ⊙→ G.obj U) → fst (F.obj X ⊙→ U)
    out s = ε U ⊙∘ F.arr s

    into-out : (s : fst (X ⊙→ G.obj U)) → into (out s) == s
    into-out s =
      G.arr (ε U ⊙∘ F.arr s) ⊙∘ η X
        =⟨ G.comp (ε U) (F.arr s) |in-ctx (λ w → w ⊙∘ η X) ⟩
      (G.arr (ε U) ⊙∘ G.arr (F.arr s)) ⊙∘ η X
        =⟨ ⊙∘-assoc (G.arr (ε U)) (G.arr (F.arr s)) (η X) ⟩
      G.arr (ε U) ⊙∘ G.arr (F.arr s) ⊙∘ η X
        =⟨ ! (η-natural s) |in-ctx (λ w → G.arr (ε U) ⊙∘ w) ⟩
      G.arr (ε U) ⊙∘ η (G.obj U) ⊙∘ s
        =⟨ ! (⊙∘-assoc (G.arr (ε U)) (η (G.obj U)) s) ⟩
      (G.arr (ε U) ⊙∘ η (G.obj U)) ⊙∘ s
        =⟨ Gε-ηG U |in-ctx (λ w → w ⊙∘ s) ⟩
      ⊙idf (G.obj U) ⊙∘ s
        =⟨ ⊙∘-unit-l s ⟩
      s ∎

    out-into : (r : fst (F.obj X ⊙→ U)) → out (into r) == r
    out-into r =
      ε U ⊙∘ F.arr (G.arr r ⊙∘ η X)
        =⟨ F.comp (G.arr r) (η X) |in-ctx (λ w → ε U ⊙∘ w) ⟩
      ε U ⊙∘ F.arr (G.arr r) ⊙∘ F.arr (η X)
        =⟨ ! (⊙∘-assoc (ε U) (F.arr (G.arr r)) (F.arr (η X))) ⟩
      (ε U ⊙∘ F.arr (G.arr r)) ⊙∘ F.arr (η X)
        =⟨ ε-natural r |in-ctx (λ w → w ⊙∘ F.arr (η X)) ⟩
      (r ⊙∘ ε (F.obj X)) ⊙∘ F.arr (η X)
        =⟨ ⊙∘-assoc r (ε (F.obj X)) (F.arr (η X)) ⟩
      r ⊙∘ ε (F.obj X) ⊙∘ F.arr (η X)
        =⟨ εF-Fη X |in-ctx (λ w → r ⊙∘ w) ⟩
      r ∎

    eq : fst (F.obj X ⊙→ U) ≃ fst (X ⊙→ G.obj U)
    eq = equiv into out into-out out-into

  nat-dom : {X Y : Ptd i} (h : fst (X ⊙→ Y)) (U : Ptd j)
    (r : fst (F.obj Y ⊙→ U))
    → –> (eq Y U) r ⊙∘ h == –> (eq X U) (r ⊙∘ F.arr h)
  nat-dom {X} {Y} h U r =
    (G.arr r ⊙∘ η Y) ⊙∘ h
      =⟨ ⊙∘-assoc (G.arr r) (η Y) h ⟩
    G.arr r ⊙∘ η Y ⊙∘ h
      =⟨ η-natural h |in-ctx (λ w → G.arr r ⊙∘ w) ⟩
    G.arr r ⊙∘ G.arr (F.arr h) ⊙∘ η X
      =⟨ ! (⊙∘-assoc (G.arr r) (G.arr (F.arr h)) (η X)) ⟩
    (G.arr r ⊙∘ G.arr (F.arr h)) ⊙∘ η X
      =⟨ ! (G.comp r (F.arr h)) |in-ctx (λ w → w ⊙∘ η X) ⟩
    G.arr (r ⊙∘ F.arr h) ⊙∘ η X ∎

  nat-cod : (X : Ptd i) {U V : Ptd j} (k : fst (U ⊙→ V))
    (r : fst (F.obj X ⊙→ U))
    → G.arr k ⊙∘ –> (eq X U) r == –> (eq X V) (k ⊙∘ r)
  nat-cod X k r =
    G.arr k ⊙∘ (G.arr r ⊙∘ η X)
      =⟨ ! (⊙∘-assoc (G.arr k) (G.arr r) (η X)) ⟩
    (G.arr k ⊙∘ G.arr r) ⊙∘ η X
      =⟨ ! (G.comp k r) |in-ctx (λ w → w ⊙∘ η X) ⟩
    G.arr (k ⊙∘ r) ⊙∘ η X ∎

{- a right adjoint preserves products -}
module RightAdjoint× {i j} {F : PtdFunctor i j} {G : PtdFunctor j i}
  (adj : HomAdjoint F G) (U V : Ptd j) where

  private
    module F = PtdFunctor F
    module G = PtdFunctor G
    module A = HomAdjoint adj

  ⊙into : fst (G.obj (U ⊙× V) ⊙→ G.obj U ⊙× G.obj V)
  ⊙into = ⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)

  ⊙out : fst (G.obj U ⊙× G.obj V ⊙→ G.obj (U ⊙× V))
  ⊙out = –> (A.eq _ _) (⊙×-in (<– (A.eq _ _) ⊙fst)
                              (<– (A.eq _ _) ⊙snd))

  ⊙into-out : ⊙into ⊙∘ ⊙out == ⊙idf _
  ⊙into-out =
    ⊙×-in (G.arr ⊙fst) (G.arr ⊙snd) ⊙∘ ⊙out
      =⟨ ⊙×-in-pre∘ (G.arr ⊙fst) (G.arr ⊙snd) ⊙out ⟩
    ⊙×-in (G.arr ⊙fst ⊙∘ ⊙out) (G.arr ⊙snd ⊙∘ ⊙out)
      =⟨ ap2 ⊙×-in
           (A.nat-cod _ ⊙fst (⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd))
            ∙ ap (–> (A.eq _ _))
                 (⊙fst-⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd))
            ∙ <–-inv-r (A.eq _ _) ⊙fst)
           (A.nat-cod _ ⊙snd (⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd))
            ∙ ap (–> (A.eq _ _))
                 (⊙snd-⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd))
            ∙ <–-inv-r (A.eq _ _) ⊙snd) ⟩
    ⊙×-in ⊙fst ⊙snd ∎

  ⊙out-into : ⊙out ⊙∘ ⊙into == ⊙idf _
  ⊙out-into =
    –> (A.eq _ _) (⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd))
    ⊙∘ ⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)
      =⟨ A.nat-dom (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)) _
           (⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd)) ⟩
    –> (A.eq _ _) (⊙×-in (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd)
    ⊙∘ F.arr (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)))
      =⟨ ⊙×-in-pre∘ (<– (A.eq _ _) ⊙fst) (<– (A.eq _ _) ⊙snd)
           (F.arr (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)))
         |in-ctx –> (A.eq _ _) ⟩
    –> (A.eq _ _) (⊙×-in
            (<– (A.eq _ _) ⊙fst ⊙∘ F.arr (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)))
            (<– (A.eq _ _) ⊙snd ⊙∘ F.arr (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd))))
      =⟨ ap2 (λ f g → –> (A.eq _ _) (⊙×-in f g))
           (A.nat!-dom (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)) _ ⊙fst
            ∙ ap (<– (A.eq _ _)) (⊙fst-⊙×-in (G.arr ⊙fst) (G.arr ⊙snd))
            ∙ ! (A.nat!-cod _ ⊙fst (⊙idf _)))
           (A.nat!-dom (⊙×-in (G.arr ⊙fst) (G.arr ⊙snd)) _ ⊙snd
            ∙ ap (<– (A.eq _ _)) (⊙snd-⊙×-in (G.arr ⊙fst) (G.arr ⊙snd))
            ∙ ! (A.nat!-cod _ ⊙snd (⊙idf _))) ⟩
    –> (A.eq _ _) (⊙×-in (⊙fst ⊙∘ <– (A.eq _ _) (⊙idf _))
                         (⊙snd ⊙∘ <– (A.eq _ _) (⊙idf _)))
      =⟨ ap (–> (A.eq _ _)) (! (⊙×-in-pre∘ ⊙fst ⊙snd (<– (A.eq _ _) (⊙idf _)))) ⟩
    –> (A.eq _ _) (⊙×-in ⊙fst ⊙snd ⊙∘ <– (A.eq _ _) (⊙idf _))
      =⟨ ⊙∘-unit-l _ |in-ctx –> (A.eq _ _) ⟩
    –> (A.eq _ _) (<– (A.eq _ _) (⊙idf _))
      =⟨ <–-inv-r (A.eq _ _) (⊙idf _) ⟩
    ⊙idf _ ∎

  ⊙eq : G.obj (U ⊙× V) ⊙≃ G.obj U ⊙× G.obj V
  ⊙eq = ⊙≃-in (equiv (fst ⊙into) (fst ⊙out)
                  (app= (ap fst ⊙into-out)) (app= (ap fst ⊙out-into)))
                (snd ⊙into)

  ⊙path = ⊙ua ⊙eq

{- Using the equivalence in RightAdjoint× we get a binary
 - "G.arr2" : (X × Y → Z) → (G X × G Y → G Z)
 - and there is some kind of naturality wrt the (FX→Y)≃(X→GY) equivalence
 - (use case: from ⊙ap we get ⊙ap2) -}
module RightAdjointBinary {i j} {F : PtdFunctor i j} {G : PtdFunctor j i}
  (adj : HomAdjoint F G)
  where

  private
    module F = PtdFunctor F
    module G = PtdFunctor G
    module A = HomAdjoint adj
    module A× = RightAdjoint× adj

  arr2 : {X Y Z : Ptd j}
    → fst (X ⊙× Y ⊙→ Z) → fst (G.obj X ⊙× G.obj Y ⊙→ G.obj Z)
  arr2 {X} {Y} {Z} f = G.arr f ⊙∘ A×.⊙out X Y

  nat-cod : {X : Ptd i} {Y Z W : Ptd j}
    (r₁ : fst (F.obj X ⊙→ Y)) (r₂ : fst (F.obj X ⊙→ Z))
    (o : fst (Y ⊙× Z ⊙→ W))
    → –> (A.eq X W) (o ⊙∘ ⊙×-in r₁ r₂)
      == arr2 o ⊙∘ ⊙×-in (–> (A.eq X Y) r₁) (–> (A.eq X Z) r₂)
  nat-cod {X} {Y} {Z} {W} r₁ r₂ o =
    –> (A.eq X W) (o ⊙∘ ⊙×-in r₁ r₂)
      =⟨ ! (A.nat-cod X o (⊙×-in r₁ r₂)) ⟩
    G.arr o ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂)
      =⟨ ! (A×.⊙out-into Y Z)
         |in-ctx (λ w → (G.arr o ⊙∘ w) ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂)) ⟩
    (G.arr o ⊙∘ (A×.⊙out Y Z ⊙∘ A×.⊙into Y Z))
    ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂)
      =⟨ ⊙∘-assoc-lemma (G.arr o) (A×.⊙out Y Z) (A×.⊙into Y Z)
           (–> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂)) ⟩
    arr2 o ⊙∘ A×.⊙into Y Z ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂)
      =⟨ ⊙×-in-pre∘ (G.arr ⊙fst) (G.arr ⊙snd) (–> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂))
         |in-ctx (λ w → arr2 o ⊙∘ w) ⟩
    arr2 o ⊙∘ ⊙×-in (G.arr ⊙fst ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂))
                    (G.arr ⊙snd ⊙∘ –> (A.eq X (Y ⊙× Z)) (⊙×-in r₁ r₂))
      =⟨ ap2 (λ w₁ w₂ → arr2 o ⊙∘ ⊙×-in w₁ w₂)
           (A.nat-cod X ⊙fst (⊙×-in r₁ r₂)
            ∙ ap (–> (A.eq X Y)) (⊙fst-⊙×-in r₁ r₂))
           (A.nat-cod X ⊙snd (⊙×-in r₁ r₂)
            ∙ ap (–> (A.eq X Z)) (⊙snd-⊙×-in r₁ r₂)) ⟩
    arr2 o ⊙∘ ⊙×-in (–> (A.eq X Y) r₁) (–> (A.eq X Z) r₂) ∎
    where
    ⊙∘-assoc-lemma : ∀ {i j k l m}
      {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {U : Ptd l} {V : Ptd m}
      (k : fst (U ⊙→ V)) (h : fst (Z ⊙→ U))
      (g : fst (Y ⊙→ Z)) (f : fst (X ⊙→ Y))
      → (k ⊙∘ (h ⊙∘ g)) ⊙∘ f == (k ⊙∘ h) ⊙∘ g ⊙∘ f
    ⊙∘-assoc-lemma (k , idp) (h , idp) (g , idp) (f , idp) = idp

{- a left adjoint preserves wedges -}
module LeftAdjoint∨ {i j} {F : PtdFunctor i j} {G : PtdFunctor j i}
  (adj : HomAdjoint F G) (U V : Ptd i) where

  private
    module F = PtdFunctor F
    module G = PtdFunctor G
    module A = HomAdjoint adj

  module Into = ⊙WedgeRec (F.arr ⊙winl) (F.arr ⊙winr)

  ⊙into : fst (F.obj U ⊙∨ F.obj V ⊙→ F.obj (U ⊙∨ V))
  ⊙into = Into.⊙f

  module OutHelp = ⊙WedgeRec (–> (A.eq _ _) ⊙winl) (–> (A.eq _ _) ⊙winr)

  ⊙out : fst (F.obj (U ⊙∨ V) ⊙→ F.obj U ⊙∨ F.obj V)
  ⊙out = <– (A.eq _ _) OutHelp.⊙f

  ⊙into-out : ⊙into ⊙∘ ⊙out == ⊙idf _
  ⊙into-out =
    ⊙into ⊙∘ ⊙out
      =⟨ A.nat!-cod _ ⊙into (⊙wedge-rec (–> (A.eq _ _) ⊙winl)
                                        (–> (A.eq _ _) ⊙winr)) ⟩
    <– (A.eq _ _) (G.arr ⊙into ⊙∘ ⊙wedge-rec (–> (A.eq _ _) ⊙winl)
                                             (–> (A.eq _ _) ⊙winr))
      =⟨ ap (<– (A.eq _ _)) (⊙wedge-rec-post∘ (G.arr ⊙into)
                              (–> (A.eq _ _) ⊙winl) (–> (A.eq _ _) ⊙winr)) ⟩
    <– (A.eq _ _) (⊙wedge-rec (G.arr ⊙into ⊙∘ –> (A.eq _ _) ⊙winl)
                              (G.arr ⊙into ⊙∘ –> (A.eq _ _) ⊙winr))
      =⟨ ap2 (λ w₁ w₂ → <– (A.eq _ _) (⊙wedge-rec w₁ w₂))
             (A.nat-cod _ ⊙into ⊙winl
              ∙ ap (–> (A.eq _ _)) (Into.⊙winl-β ∙ ! (⊙∘-unit-l _))
              ∙ ! (A.nat-dom ⊙winl _ (⊙idf _)))
             (A.nat-cod _ ⊙into ⊙winr
              ∙ ap (–> (A.eq _ _)) (Into.⊙winr-β ∙ ! (⊙∘-unit-l _))
              ∙ ! (A.nat-dom ⊙winr _ (⊙idf _))) ⟩
    <– (A.eq _ _) (⊙wedge-rec (–> (A.eq _ _) (⊙idf _) ⊙∘ ⊙winl)
                              (–> (A.eq _ _) (⊙idf _) ⊙∘ ⊙winr))
      =⟨ ap (<– (A.eq _ _))
            (! (⊙wedge-rec-post∘ (–> (A.eq _ _) (⊙idf _)) ⊙winl ⊙winr)) ⟩
    <– (A.eq _ _) (–> (A.eq _ _) (⊙idf _) ⊙∘ ⊙wedge-rec ⊙winl ⊙winr)
      =⟨ ap (λ w → <– (A.eq _ _) (–> (A.eq _ _) (⊙idf _) ⊙∘ w))
            ⊙wedge-rec-η ⟩
    <– (A.eq _ _) (–> (A.eq _ _) (⊙idf _))
      =⟨ <–-inv-l (A.eq _ _) (⊙idf _) ⟩
    ⊙idf _ ∎

  ⊙out-into : ⊙out ⊙∘ ⊙into == ⊙idf _
  ⊙out-into =
    ⊙out ⊙∘ ⊙wedge-rec (F.arr ⊙winl) (F.arr ⊙winr)
      =⟨ ⊙wedge-rec-post∘ ⊙out (F.arr ⊙winl) (F.arr ⊙winr) ⟩
    ⊙wedge-rec (⊙out ⊙∘ F.arr ⊙winl) (⊙out ⊙∘ F.arr ⊙winr)
      =⟨ ap2 ⊙wedge-rec
           (A.nat!-dom ⊙winl _ (⊙wedge-rec _ _)
            ∙ ap (<– (A.eq _ _)) OutHelp.⊙winl-β
            ∙ <–-inv-l (A.eq _ _) ⊙winl)
           (A.nat!-dom ⊙winr _ (⊙wedge-rec _ _)
            ∙ ap (<– (A.eq _ _)) OutHelp.⊙winr-β
            ∙ <–-inv-l (A.eq _ _) ⊙winr) ⟩
    ⊙wedge-rec ⊙winl ⊙winr
      =⟨ ⊙wedge-rec-η ⟩
    ⊙idf _ ∎

  ⊙eq : F.obj U ⊙∨ F.obj V ⊙≃ F.obj (U ⊙∨ V)
  ⊙eq = ⊙≃-in (equiv (fst ⊙into) (fst ⊙out)
                  (app= (ap fst ⊙into-out)) (app= (ap fst ⊙out-into)))
                (snd ⊙into)

  ⊙path = ⊙ua ⊙eq


{-# OPTIONS --without-K #-}
module well-typed-syntax-interpreter where
open import common public
open import well-typed-syntax
import well-typed-syntax-pre-interpreter
open import well-typed-syntax-eq-dec

max-level : Level
max-level = well-typed-syntax-pre-interpreter.max-level

Context⇓ : (Γ : Context) → Set (lsuc max-level)
Context⇓ = well-typed-syntax-pre-interpreter.inner.Context⇓
           (λ ℓ P Γ' dummy val → context-pick-if {P = P} dummy val)
           (λ ℓ P dummy val → context-pick-if-refl {P = P} {dummy})

Typ⇓ : {Γ : Context} → Typ Γ → Context⇓ Γ → Set max-level
Typ⇓ = well-typed-syntax-pre-interpreter.inner.Typ⇓
       (λ ℓ P Γ' dummy val → context-pick-if {P = P} dummy val)
       (λ ℓ P dummy val → context-pick-if-refl {P = P} {dummy})

Term⇓ : ∀ {Γ : Context} {T : Typ Γ} → Term T → (Γ⇓ : Context⇓ Γ) → Typ⇓ T Γ⇓
Term⇓ = well-typed-syntax-pre-interpreter.inner.Term⇓
        (λ ℓ P Γ' dummy val → context-pick-if {P = P} dummy val)
        (λ ℓ P dummy val → context-pick-if-refl {P = P} {dummy})


-- 2014-08-25 reported by Wolfram Kahl, test case shrunk by Ulf, fixed by Andreas and Ulf
-- {-# OPTIONS --show-implicit -v tc.cover.splittree:10 -v tc.with:40 -v tc.cc:12 #-}
postulate K : Set

data SUList : K → Set where
  cons : ∀ {k} (es : SUList k) → SUList k

data Tri : Set where
  tri< tri> : Tri

postulate
  k : K
  compareK : Tri
  dummy : Set
  es : SUList k

diff : SUList k → SUList k → Tri → Set
diff (cons _) _     tri< = K
diff es₁ (cons es₂) tri> = diff es₁ es₂ compareK

test = diff (cons es) (cons es) tri>
-- normalizing  test
-- gives WRONG  diff (cons k es) es compareK
-- should be    diff (cons {k} es) es compareK
-- Problem was in the clause compiler

-- Trigger the bug:
from : (es : SUList k) (c : Tri) → diff (cons es) (cons es) c → Set
from es tri< d = K
from es tri> d with dummy
... | _ = K

-- WAS:
-- Expected a hidden argument, but found a visible argument
-- when checking that the type
-- Set → (es₁ : SUList k) (d : diff (cons k es₁) es₁ compareK) → Set
-- of the generated with function is well-formed


{-# OPTIONS --cubical --safe --postfix-projections #-}

open import Cubical.Core.Everything

open import Cubical.Foundations.Embedding
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Univalence

-- A helper module for deriving univalence for a higher inductive-recursive
-- universe.
--
--  U is the type of codes
--  El is the decoding
--  uaf is a higher constructor that requires paths between codes to exist
--    for equivalences of decodings
--  comp is intended to be the computational behavior of El on uaf, although
--    it seems that being a path is sufficient.
--  ret is a higher constructor that fills out the equivalence structure
--    for uaf and the computational behavior of El.
--
-- Given a universe defined as above, it's possible to show that the path
-- space of the code type is equivalent to the path space of the actual
-- decodings, which are themselves determined by equivalences.
--
-- The levels are left independent, but of course it will generally be
-- impossible to define this sort of universe unless ℓ' < ℓ, because El will
-- be too big to go in a constructor of U. The exception would be if U could
-- be defined independently of El, though it might be tricky to get the right
-- higher structure in such a case.
module Cubical.Foundations.Univalence.Universe {ℓ ℓ'}
    (U : Type ℓ)
    (El : U → Type ℓ')
    (uaf : ∀{s t} → El s ≃ El t → s ≡ t)
    (comp : ∀{s t} (e : El s ≃ El t) → cong El (uaf e) ≡ ua e)
    (ret : ∀{s t : U} → (p : s ≡ t) → uaf (lineToEquiv (λ i → El (p i))) ≡ p)
  where

minivalence : ∀{s t} → (s ≡ t) ≃ (El s ≡ El t)
minivalence {s} {t} = isoToEquiv mini
  where
  open Iso
  mini : Iso (s ≡ t) (El s ≡ El t)
  mini .fun = cong El
  mini .inv = uaf ∘ pathToEquiv
  mini .rightInv p = comp (pathToEquiv p) ∙ uaη p
  mini .leftInv = ret

isEmbeddingEl : isEmbedding El
isEmbeddingEl s t = snd minivalence


{-# OPTIONS --without-K --safe #-}

open import Categories.Adjoint
open import Categories.Category
open import Categories.Functor renaming (id to idF)

module Categories.Adjoint.Monadic.Properties {o ℓ e o′ ℓ′ e′} {𝒞 : Category o ℓ e} {𝒟 : Category o′ ℓ′ e′}
                                             {L : Functor 𝒞 𝒟} {R : Functor 𝒟 𝒞} (adjoint : L ⊣ R) where

open import Level
open import Function using (_$_)

open import Categories.Adjoint.Properties
open import Categories.Adjoint.Monadic
open import Categories.NaturalTransformation.NaturalIsomorphism
open import Categories.NaturalTransformation
open import Categories.Monad

open import Categories.Diagram.Coequalizer

open import Categories.Category.Construction.EilenbergMoore
open import Categories.Category.Construction.Properties.EilenbergMoore

open import Categories.Morphism
import Categories.Morphism.Reasoning as MR

private
  module L = Functor L
  module R = Functor R

  module 𝒞 = Category 𝒞
  module 𝒟 = Category 𝒟

  module adjoint = Adjoint adjoint

  T : Monad 𝒞
  T = adjoint⇒monad adjoint

  𝒞ᵀ : Category _ _ _
  𝒞ᵀ = EilenbergMoore T

  Comparison : Functor 𝒟 𝒞ᵀ
  Comparison = ComparisonF adjoint

  module Comparison = Functor Comparison

-- If we have a coequalizer of the following diagram for every T-algabra, then the comparison functor has a left adjoint.
module _ (has-coequalizer : (M : Module T) → Coequalizer 𝒟 (L.F₁ (Module.action M)) (adjoint.counit.η (L.₀ (Module.A M)))) where

  open Coequalizer

  Comparison⁻¹ : Functor 𝒞ᵀ 𝒟
  Comparison⁻¹ = record
    { F₀ = λ M → obj (has-coequalizer M)
    ; F₁ = λ {M} {N} α → coequalize (has-coequalizer M) $ begin
      𝒟 [ 𝒟 [ arr (has-coequalizer N) ∘ L.F₁ (Module⇒.arr α) ] ∘ L.F₁ (Module.action M) ]                             ≈⟨ pullʳ (⟺ L.homomorphism) ⟩
      𝒟 [ arr (has-coequalizer N) ∘ L.F₁ (𝒞 [ Module⇒.arr α ∘ Module.action M ]) ]                                    ≈⟨ refl⟩∘⟨ L.F-resp-≈ (Module⇒.commute α) ⟩
      𝒟 [ arr (has-coequalizer N) ∘ L.F₁ (𝒞 [ Module.action N ∘ R.F₁ (L.F₁ (Module⇒.arr α)) ]) ]                      ≈⟨ refl⟩∘⟨ L.homomorphism ⟩
      𝒟 [ arr (has-coequalizer N) ∘ 𝒟 [ L.F₁ (Module.action N) ∘ L.F₁ (R.F₁ (L.F₁ (Module⇒.arr α))) ] ]               ≈⟨ pullˡ (equality (has-coequalizer N)) ⟩
      𝒟 [ 𝒟 [ arr (has-coequalizer N) ∘ adjoint.counit.η (L.F₀ (Module.A N)) ] ∘ L.F₁ (R.F₁ (L.F₁ (Module⇒.arr α))) ] ≈⟨ extendˡ (adjoint.counit.commute (L.F₁ (Module⇒.arr α))) ⟩
      𝒟 [ 𝒟 [ arr (has-coequalizer N) ∘ L.F₁ (Module⇒.arr α) ] ∘ adjoint.counit.η (L.₀ (Module.A M)) ]                ∎
    ; identity = λ {A} → ⟺ $ unique (has-coequalizer A) $ begin
      𝒟 [ arr (has-coequalizer A) ∘ L.F₁ 𝒞.id ] ≈⟨ refl⟩∘⟨ L.identity ⟩
      𝒟 [ arr (has-coequalizer A) ∘ 𝒟.id ]      ≈⟨ id-comm ⟩
      𝒟 [ 𝒟.id ∘ arr (has-coequalizer A) ]      ∎
    ; homomorphism = λ {X} {Y} {Z} {f} {g} → ⟺ $ unique (has-coequalizer X) $ begin
      𝒟 [ arr (has-coequalizer Z) ∘ L.F₁ (𝒞 [ Module⇒.arr g ∘ Module⇒.arr f ]) ]                              ≈⟨ 𝒟.∘-resp-≈ʳ L.homomorphism ○ 𝒟.sym-assoc ⟩
      𝒟 [ 𝒟 [ arr (has-coequalizer Z) ∘ L.F₁ (Module⇒.arr g) ] ∘ L.F₁ (Module⇒.arr f) ]                       ≈⟨ universal (has-coequalizer Y) ⟩∘⟨refl ⟩
      𝒟 [ 𝒟 [ coequalize (has-coequalizer Y) _ ∘ arr (has-coequalizer Y) ] ∘ L.F₁ (Module⇒.arr f) ]            ≈⟨ extendˡ (universal (has-coequalizer X)) ⟩
      𝒟 [ 𝒟 [ coequalize (has-coequalizer Y) _ ∘ coequalize (has-coequalizer X) _ ] ∘ arr (has-coequalizer X) ] ∎
    ; F-resp-≈ = λ {A} {B} {f} {g} eq → unique (has-coequalizer A) $ begin
      𝒟 [ arr (has-coequalizer B) ∘ L.F₁ (Module⇒.arr g) ]            ≈⟨ refl⟩∘⟨ L.F-resp-≈ (𝒞.Equiv.sym eq) ⟩
      𝒟 [ arr (has-coequalizer B) ∘ L.F₁ (Module⇒.arr f) ]            ≈⟨ universal (has-coequalizer A) ⟩
      𝒟 [ coequalize (has-coequalizer A) _ ∘ arr (has-coequalizer A) ] ∎
    }
    where
      open 𝒟.HomReasoning
      open MR 𝒟

  private
    module Comparison⁻¹ = Functor Comparison⁻¹

  Comparison⁻¹⊣Comparison : Comparison⁻¹ ⊣ Comparison
  Adjoint.unit Comparison⁻¹⊣Comparison = ntHelper record
    { η = λ M → record
      { arr = 𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ adjoint.unit.η (Module.A M) ]
      ; commute = begin
        𝒞 [ 𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ adjoint.unit.η (Module.A M) ] ∘ Module.action M ]                                                      ≈⟨ pullʳ (adjoint.unit.commute (Module.action M)) ⟩
        -- It would be nice to have a reasoning combinator doing this "⟺ homomorphism ... homomorphism" pattern
        𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ 𝒞 [ R.F₁ (L.F₁ (Module.action M)) ∘ adjoint.unit.η (R.F₀ (L.F₀ (Module.A M))) ] ]                          ≈⟨ pullˡ (⟺ R.homomorphism) ⟩
        𝒞 [ R.F₁ (𝒟 [ arr (has-coequalizer M) ∘ L.F₁ (Module.action M) ]) ∘ adjoint.unit.η (R.F₀ (L.F₀ (Module.A M))) ]                                 ≈⟨ (R.F-resp-≈ (equality (has-coequalizer M)) ⟩∘⟨refl) ⟩
        𝒞 [ R.F₁ (𝒟 [ arr (has-coequalizer M) ∘ adjoint.counit.η (L.F₀ (Module.A M)) ]) ∘ adjoint.unit.η (R.F₀ (L.F₀ (Module.A M))) ]                   ≈⟨ (R.homomorphism ⟩∘⟨refl) ⟩
        𝒞 [ 𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ R.F₁ (adjoint.counit.η (L.F₀ (Module.A M))) ] ∘ adjoint.unit.η (R.F₀ (L.F₀ (Module.A M))) ]            ≈⟨ cancelʳ adjoint.zag ⟩
        -- FIXME Use something like cancel here
        R.F₁ (arr (has-coequalizer M))                                                                                ≈˘⟨ R.F-resp-≈ (𝒟.identityʳ) ⟩
        R.F₁ (𝒟 [ arr (has-coequalizer M) ∘ 𝒟.id ])                                                                                ≈˘⟨ R.F-resp-≈ (𝒟.∘-resp-≈ʳ adjoint.zig) ⟩
        R.F₁ (𝒟 [ arr (has-coequalizer M) ∘ 𝒟 [ adjoint.counit.η (L.F₀ (Module.A M)) ∘ L.F₁ (adjoint.unit.η (Module.A M)) ] ])                          ≈⟨ R.F-resp-≈ (MR.extendʳ 𝒟 (adjoint.counit.sym-commute (arr (has-coequalizer M)))) ⟩
        R.F₁ (𝒟 [ adjoint.counit.η (obj (has-coequalizer M)) ∘ 𝒟 [ L.F₁ (R.F₁ (arr (has-coequalizer M))) ∘ L.F₁ (adjoint.unit.η (Module.A M)) ] ])      ≈˘⟨ R.F-resp-≈ (𝒟.∘-resp-≈ʳ L.homomorphism) ⟩
        R.F₁ (𝒟 [ adjoint.counit.η (obj (has-coequalizer M)) ∘ L.F₁ (𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ adjoint.unit.η (Module.A M)])])               ≈⟨ R.homomorphism ⟩
        𝒞 [ R.F₁ (adjoint.counit.η (obj (has-coequalizer M))) ∘ R.F₁ (L.F₁ (𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ adjoint.unit.η (Module.A M)])) ]       ∎
      }
    ; commute = λ {M} {N} f → begin
      𝒞 [ 𝒞 [ R.F₁ (arr (has-coequalizer N)) ∘ adjoint.unit.η (Module.A N) ] ∘ Module⇒.arr f ]                           ≈⟨ extendˡ (adjoint.unit.commute (Module⇒.arr f)) ⟩
      𝒞 [ 𝒞 [ R.F₁ (arr (has-coequalizer N)) ∘ R.F₁ (L.F₁ (Module⇒.arr f)) ] ∘ adjoint.unit.η (Module.A M) ]             ≈˘⟨ R.homomorphism ⟩∘⟨refl ⟩
      𝒞 [ R.F₁ (𝒟 [ arr (has-coequalizer N) ∘ L.F₁ (Module⇒.arr f) ]) ∘ adjoint.unit.η (Module.A M) ]                    ≈⟨ R.F-resp-≈ (universal (has-coequalizer M)) ⟩∘⟨refl ⟩
      𝒞 [ R.F₁ (𝒟 [ (coequalize (has-coequalizer M) _) ∘ (arr (has-coequalizer M)) ]) ∘ adjoint.unit.η (Module.A M) ]    ≈⟨ pushˡ R.homomorphism ⟩
      𝒞 [ R.F₁ (coequalize (has-coequalizer M) _) ∘ 𝒞 [ R.F₁ (arr (has-coequalizer M)) ∘ adjoint.unit.η (Module.A M) ] ] ∎
    }
    where
      open 𝒞.HomReasoning
      open MR 𝒞
  Adjoint.counit Comparison⁻¹⊣Comparison = ntHelper record
    { η = λ X → coequalize (has-coequalizer (Comparison.F₀ X)) (adjoint.counit.commute (adjoint.counit.η X))
    ; commute = λ {X} {Y} f → begin
      𝒟 [ coequalize (has-coequalizer (Comparison.F₀ Y)) _ ∘ coequalize (has-coequalizer (Comparison.F₀ X)) _ ]  ≈⟨ unique (has-coequalizer (Comparison.F₀ X)) (adjoint.counit.sym-commute f ○ pushˡ (universal (has-coequalizer (Comparison.F₀ Y))) ○ pushʳ (universal (has-coequalizer (Comparison.F₀ X)))) ⟩
      coequalize (has-coequalizer (Comparison.F₀ X)) (extendˡ (adjoint.counit.commute (adjoint.counit.η X)))     ≈˘⟨ unique (has-coequalizer (Comparison.F₀ X)) (pushʳ (universal (has-coequalizer (Comparison.F₀ X)))) ⟩
      𝒟 [ f ∘ coequalize (has-coequalizer (Comparison.F₀ X)) _ ]                                                 ∎
    }
    where
      open 𝒟.HomReasoning
      open MR 𝒟
  Adjoint.zig Comparison⁻¹⊣Comparison {X} = begin
    𝒟 [ coequalize (has-coequalizer (Comparison.F₀ (Comparison⁻¹.F₀ X))) _ ∘ coequalize (has-coequalizer X) _ ] ≈⟨ unique (has-coequalizer X) (⟺ adjoint.RLadjunct≈id ○ pushˡ (universal (has-coequalizer (Comparison.F₀ (Comparison⁻¹.F₀ X)))) ○ pushʳ (universal (has-coequalizer X))) ⟩
    coequalize (has-coequalizer X) {h = arr (has-coequalizer X)} (equality (has-coequalizer X))                 ≈˘⟨ unique (has-coequalizer X) (⟺ 𝒟.identityˡ) ⟩
    𝒟.id                                                                                ∎
    where
      open 𝒟.HomReasoning
      open MR 𝒟
  Adjoint.zag Comparison⁻¹⊣Comparison {A} = begin
    𝒞 [ R.F₁ (coequalize (has-coequalizer (Comparison.F₀ A)) _) ∘  𝒞 [ R.F₁ (arr (has-coequalizer (Comparison.F₀ A))) ∘ adjoint.unit.η (Module.A (Comparison.F₀ A)) ] ] ≈⟨ pullˡ (⟺ R.homomorphism) ⟩
    𝒞 [ R.F₁ (𝒟 [ coequalize (has-coequalizer (Comparison.F₀ A)) _ ∘ arr (has-coequalizer (Comparison.F₀ A)) ]) ∘ adjoint.unit.η (Module.A (Comparison.F₀ A)) ]         ≈˘⟨ R.F-resp-≈ (universal (has-coequalizer (Comparison.F₀ A))) ⟩∘⟨refl ⟩
    𝒞 [ R.F₁ (adjoint.counit.η A) ∘ adjoint.unit.η (R.F₀ A) ]                                                                                ≈⟨ adjoint.zag ⟩
    𝒞.id                                                                                ∎
    where
      open 𝒞.HomReasoning
      open MR 𝒞


{-# OPTIONS --allow-unsolved-metas #-}
-- {-# OPTIONS -v tc.conv.elim:100 #-}
module Issue814 where

record IsMonoid (M : Set) : Set where
  field
    unit : M
    _*_ : M → M → M

record Monoid : Set₁ where
  field
    carrier : Set
    is-mon : IsMonoid carrier

record Structure (Struct : Set₁)
                 (HasStruct : Set → Set)
                 (carrier : Struct → Set) : Set₁ where
  field
    has-struct : (X : Struct) → HasStruct (carrier X)

mon-mon-struct : Structure Monoid IsMonoid Monoid.carrier
mon-mon-struct = record
  { has-struct = Monoid.is-mon }

mon-mon-struct' : Structure Monoid IsMonoid Monoid.carrier
mon-mon-struct' = record
  { has-struct = Monoid.is-mon }

unit : {Struct : Set₁}{C : Struct → Set}
     → ⦃ X : Struct ⦄
     → ⦃ struct : Structure Struct IsMonoid C ⦄
     → C X
unit {Struct}{C} ⦃ X ⦄ ⦃ struct ⦄ = IsMonoid.unit (Structure.has-struct struct X)

f : (M : Monoid) → Monoid.carrier M
f M = unit

-- An internal error has occurred. Please report this as a bug.
-- Location of the error: src/full/Agda/TypeChecking/Eliminators.hs:45

-- This used to crash, but should only produce unsolved metas.


{-# OPTIONS --without-K #-}

open import lib.Basics
open import lib.NType2
open import lib.PathFunctor
open import lib.PathGroupoid

open import lib.types.Bool
open import lib.types.IteratedSuspension
open import lib.types.LoopSpace
open import lib.types.Nat
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.types.Suspension
open import lib.types.TLevel
open import lib.types.Unit

open import nicolai.pseudotruncations.Preliminary-definitions
open import nicolai.pseudotruncations.Liblemmas
open import nicolai.pseudotruncations.SeqColim
open import nicolai.pseudotruncations.wconstSequence

module nicolai.pseudotruncations.PseudoTruncs-wconst-seq where

open import nicolai.pseudotruncations.pointed-O-Sphere
open import nicolai.pseudotruncations.LoopsAndSpheres
open import nicolai.pseudotruncations.PseudoTruncs

{- The special sequence that we consider -}
module PtruncsSeq {i} (X : Type i) where

  A : ℕ → Type i
  A O = X
  A (S n) = Pseudo n -1-trunc (A n)

  f : (n : ℕ) → A n → A (S n)
  f n x = point n -1 x

  C : Sequence {i}
  C = (A , f)

{- A main result: If we have an inhabitant of X, then the sequence is weakly constant.
   This is Lemma 6.2 ! -}
module PtruncSeqWC {i} (X : Type i) (x₀ : X) where

  open PtruncsSeq {i} X

  fs : (n : ℕ) → A n
  fs O = x₀
  fs (S n) = f n (fs n)

  P : (n : ℕ) → (x : A n) → Type i
  P n x = f n x == fs (S n)

  {- This is the easy 'Case j ≡ -2' -}
  f₀-x₀ : (y : X) → P O y
  f₀-x₀ y = (spoke O -1 r (lift false)) ∙ ! (spoke O -1 r (lift true))  where

    r : Sphere {i} O → X
    r (lift true) = x₀
    r (lift false) = y

  {- Now, the general case is done by induction on n
     (note that this variable is called 'j' in the paper).
     Hence, what is 'j' in the paper is now 'S n'.

     Unfortunately, we have to do everything in one "big"
     step with many "where" clauses due to the mutual dependencies. -}

  fₙ-x₀ : (n : ℕ) → (y : A n) → P n y
  fₙ-x₀ O y = f₀-x₀ y
  fₙ-x₀ (S n) = Pseudotrunc-ind n Point Hub Spoke where

    -- just for convenience - saves brackets
    norₙ' : Sphere' {i} n
    norₙ' = nor' n

    fⁿx₀ = fs n

    Point : (w : A n) → P (S n) (point n -1 w)
    Point w = ap (point (S n) -1) (fₙ-x₀ n w)

    Hub : (r : Sphere' n → A n) → point S n -1 _ == _
    Hub r = ap (point (S n) -1) (! (spoke n -1 r norₙ') ∙ fₙ-x₀ n (r norₙ'))

    {- The definition of [Spoke] is the hard part.
       First, we do the easy things that we have to do... -}
    Spoke : (r : _) → (x : Sphere' n) → _
    Spoke r = λ x → from-transp (P (S n))
                                (spoke n -1 r x)
                                (
      transport (P (S n)) (spoke n -1 r x) (Point (r x))
        =⟨ trans-ap₁ (f (S n)) (fs (S (S n))) (spoke n -1 r x) (Point (r x)) ⟩
      ! (ap (point (S n) -1) (spoke n -1 r x)) ∙ Point (r x)
        =⟨ ! (ap (point (S n) -1) (spoke n -1 r x)) ∙ₗ ! (∙-unit-r (Point (r x))) ⟩
      ! (ap (point (S n) -1) (spoke n -1 r x)) ∙ Point (r x) ∙ idp
        {- Now comes the hard step which requires A LOT of work in the where clause
           below: we can compose with something which, for a complicated reason, is idp! -}
        =⟨ ! (ap (point (S n) -1) (spoke n -1 r x)) ∙ₗ (Point (r x) ∙ₗ ! (k-const x)) ⟩
      ! (ap (point (S n) -1) (spoke n -1 r x)) ∙ Point (r x) ∙ k x
        {- From here, it's easy; we just have to re-associate paths and cancel inverses.
           This could be done with standard library lemmas, but it's easier to just use
           an 'ad-hoc' lemma. -}
        =⟨ multi-cancelling (ap (point S n -1) (spoke n -1 r x)) (Point (r x)) (Hub r) ⟩
      Hub r
        ∎
      )

        where

          {- Now, the actual work follows! -}

          {- First, we define the interesting loop.
             In the paper, it is called [kₓ].
             Here, it is just [k x].  -}
          k : (x : Sphere' {i} n)
              → Ω (Pseudo S n -1-trunc (A (S n)) ,
                   f (S n) (f n fⁿx₀))
          k x = ! (Point (r x)) ∙ ap (point (S n) -1) (spoke n -1 r x) ∙ (Hub r)

          {- We want to show that [k] factors as [ap pₙ ∘ h].
             First, we define h. -}
          h : Sphere' {i} n
              → Ω (Pseudo n -1-trunc (A n) ,
                   f n fⁿx₀)
          h x =   ! (fₙ-x₀ n (r x))
                ∙ (spoke n -1 r x)
                ∙ (! (spoke n -1 r norₙ') ∙ fₙ-x₀ n (r norₙ'))

          {- The statement that k == ap pₙ ∘ h: -}
          k-p-h : k == ap (point S n -1) ∘ h
          k-p-h = λ= (λ (x : Sphere' {i} n)
                     → k x
                         =⟨ idp ⟩
                         ! (Point (r x))
                       ∙ (ap (point (S n) -1) (spoke n -1 r x) ∙ (Hub r))
                         =⟨ !-ap (point S n -1) (fₙ-x₀ n (r x))
                                ∙ᵣ (  ap (point S n -1) (spoke n -1 r x)
                                    ∙ Hub r) ⟩
                         ap (point (S n) -1) (! (fₙ-x₀ n (r x)))
                       ∙ ap (point (S n) -1) (spoke n -1 r x)
                       ∙ (Hub r)
                         =⟨ ! (ap (point (S n) -1) (! (fₙ-x₀ n (r x)))
                                ∙ₗ ap-∙ point S n -1
                                        (spoke n -1 r x)
                                        _ ) ⟩
                         ap (point (S n) -1) (! (fₙ-x₀ n (r x)))
                       ∙ ap (point (S n) -1)
                            (  spoke n -1 r x
                             ∙ (! (spoke n -1 r norₙ')
                             ∙ fₙ-x₀ n (r norₙ')))
                         =⟨ ! (ap-∙ point S n -1 (! (fₙ-x₀ n (r x))) _) ⟩
                       ap (point S n -1) (h x)
                         ∎)

          {- [h] can be made into a a pointed map, written [ĥ] -}
          ĥ : (⊙Sphere' {i} n)
                 →̇ ⊙Ω (A (S n) ,
                      f n fⁿx₀)
          ĥ = h ,
                  (! (fₙ-x₀ n (r _))
                ∙ (spoke n -1 r _)
                ∙ ! (spoke n -1 r norₙ')
                ∙ fₙ-x₀ n (r norₙ')

                  =⟨ (! (fₙ-x₀ n (r _)))
                      ∙ₗ (! (∙-assoc (spoke n -1 r _)
                                     (! (spoke n -1 r norₙ'))
                                     (fₙ-x₀ n (r norₙ')))) ⟩

                ! (fₙ-x₀ n (r _))
                ∙ ((spoke n -1 r _) ∙ (! (spoke n -1 r norₙ')))
                ∙ fₙ-x₀ n (r norₙ')

                  =⟨ ! (fₙ-x₀ n (r _))
                     ∙ₗ !-inv-r (spoke n -1 r _)
                     ∙ᵣ fₙ-x₀ n (r norₙ') ⟩

                ! (fₙ-x₀ n (r _))
                ∙ idp
                ∙ fₙ-x₀ n (r norₙ')

                  =⟨ !-inv-l (fₙ-x₀ n (r _)) ⟩

                idp

                  ∎ )

          {- A pointed version of the first constructor. -}
          pointsₙ : (A (S n) , f n fⁿx₀) →̇ A (S (S n)) , f (S n) (f n fⁿx₀)
          pointsₙ = point S n -1 , idp

          open null
          open hom-adjoint

          points-Φ⁻¹-null : isNull∙ (pointsₙ ⊙∘ Φ⁻¹ _ _ ĥ)
          points-Φ⁻¹-null = <– (isNull-equiv (pointsₙ ⊙∘ Φ⁻¹ _ _ ĥ))
                            -- translate from isNull∙'
                                 (null-lequiv (pointsₙ ⊙∘ Φ⁻¹ _ _ ĥ)
                                 -- translate from isNulld; this,
                                 -- we have done already!
                                    (cmp-nll'.from-sphere-null'∙ n (Φ⁻¹ _ _ ĥ)))

          ap-points-ĥ-null : isNull∙ (⊙ap (point S n -1 , idp) ⊙∘ ĥ)
          ap-points-ĥ-null = –> (combine-isnull-nat' ĥ (point S n -1 , idp)) points-Φ⁻¹-null

          {- ... consequently, h is always refl [in the library "idp"]: -}
          points-h-const : (x : Sphere' n) → ap (point S n -1) (h x) == idp
          points-h-const x = null-lequiv-easy _ ap-points-ĥ-null x

          {- ... and so is k: -}
          k-const : (x : Sphere' n) → k x == idp
          k-const x = app= k-p-h x  ∙ points-h-const x

  {- Main result: each function in the sequence is propositional! -}
  wconst-f : wconst-chain C
  wconst-f n w₁ w₂ = fₙ-x₀ n w₁ ∙ ! (fₙ-x₀ n w₂)

{- Another important result:
   if we want to show a proposition, we can assume A₀ instead of Aω
   But this should follow from the general induction principle, so... TODO
-}
module PtruncSeqResult' {i} (X : Type i) where

  open PtruncsSeq {i} X -- this defines the chain C of pseudo-truncations

  SC = SeqCo C

  reduction-lemma : (P : Type i) → (is-prop P) → (A O → P) → (SC → P)
  reduction-lemma P ip ff = SeqCo-rec {C = C} {B = P} Ins Glue where

    Ins : (n : ℕ) → A n → P
    Ins O = ff
    Ins (S n) = Pseudotrunc-rec {P = P} n Point-1 Hub-1 Spoke-1 where
      Point-1 : _ → P
      Point-1 x = Ins n x
      Hub-1 : (Sphere' n → A n) → P
      Hub-1 r = Ins n (r (nor' n))
      Spoke-1 : (r : Sphere' n → A n) (x : Sphere' n) → Point-1 (r x) == Hub-1 r
      Spoke-1 r x = prop-has-all-paths {A = P} ip _ _

    Glue : (n : ℕ) (a : A n) → Ins n a == Ins (S n) (f n a)
    Glue n a = prop-has-all-paths ip _ _

{- Corollary of the main result: The colimit of the considered sequence is propositional! -}
module PtruncSeqResult {i} (X : Type i) where

  open PtruncsSeq {i} X -- this defines the chain C of pseudo-truncations

  colim-is-prp : is-prop (SeqCo C)
  colim-is-prp =
    inhab-to-contr-is-prop
      (PtruncSeqResult'.reduction-lemma X (is-contr (SeqCo C)) has-level-is-prop
        (λ x₀ → ins O x₀ , prop-has-all-paths (wconst-prop C (PtruncSeqWC.wconst-f X x₀))
          (ins O x₀)))

  open PtruncSeqResult' X

  {- If we have the propositional truncation in the theory: -}
  open import lib.types.Truncation

  colim-is-trunc : (Trunc ⟨-1⟩ X) ≃ SeqCo C
  colim-is-trunc = equiv (Trunc-rec (colim-is-prp) (ins 0))
                         (reduction-lemma (Trunc ⟨-1⟩ X) Trunc-level [_])
                         (λ _ → prop-has-all-paths colim-is-prp _ _)
                         (λ _ → prop-has-all-paths Trunc-level _ _)


{-# OPTIONS --without-K --rewriting #-}

open import HoTT

module stash.modalities.gbm.GbmUtil where

  BM-Relation : ∀ {ℓ} (M : Modality ℓ) {A : Type ℓ} {B : Type ℓ} (Q : A → B → Type ℓ) → Type ℓ
  BM-Relation M {A} {B} Q =
    {a₀ : A} {b₀ : B} (q₀ : Q a₀ b₀)
    {a₁ : A} (q₁ : Q a₁ b₀)
    {b₁ : B} (q₂ : Q a₀ b₁) →
    Modality.is-◯-connected M (((a₀ , q₀) == (a₁ , q₁)) * ((b₀ , q₀) == (b₁ , q₂)))

  prop-lemma : ∀ {ℓ} {A : Type ℓ} {a₀ a₁ : A} (P : A → hProp ℓ)
               (p : a₀ == a₁) (x₀ : (fst ∘ P) a₀) (x₁ : (fst ∘ P) a₁) →
               x₀ == x₁ [ (fst ∘ P) ↓ p ]
  prop-lemma P idp x₀ x₁ = prop-has-all-paths (snd (P _)) x₀ x₁

  pths-ovr-is-prop : ∀ {ℓ} {A : Type ℓ} {a₀ a₁ : A} (P : A → hProp ℓ)
                     (p : a₀ == a₁) (x₀ : (fst ∘ P) a₀) (x₁ : (fst ∘ P) a₁) →
                     is-prop (x₀ == x₁ [ (fst ∘ P) ↓ p ])
  pths-ovr-is-prop P idp x₀ x₁ = =-preserves-level (snd (P _))

  pths-ovr-contr : ∀ {ℓ} {A : Type ℓ} {a₀ a₁ : A} (P : A → hProp ℓ)
                     (p : a₀ == a₁) (x₀ : (fst ∘ P) a₀) (x₁ : (fst ∘ P) a₁) →
                     is-contr (x₀ == x₁ [ (fst ∘ P) ↓ p ])
  pths-ovr-contr P idp x₀ x₁ = =-preserves-level (inhab-prop-is-contr x₀ (snd (P _)))

  eqv-square : ∀ {i j} {A : Type i} {B : Type j} (e : A ≃ B)
    {b b' : B} (p : b == b')
    → ap (–> e ∘ <– e) p == (<–-inv-r e b) ∙ p ∙ (! (<–-inv-r e b'))
  eqv-square e idp = ! (!-inv-r (<–-inv-r e _))

  eqv-square' : ∀ {i j} {A : Type i} {B : Type j} (e : A ≃ B)
    {a a' : A} (p : a == a')
    → ap (<– e ∘ –> e) p == <–-inv-l e a ∙ p ∙ ! (<–-inv-l e a')
  eqv-square' e idp = ! (!-inv-r (<–-inv-l e _))

  ↓-==-in : ∀ {i j} {A : Type i} {B : Type j} {f g : A → B}
    {x y : A} {p : x == y} {u : f x == g x} {v : f y == g y}
    → (u ∙ ap g p) == (ap f p ∙ v)
    → (u == v [ (λ x → f x == g x) ↓ p ])
  ↓-==-in {p = idp} q = ! (∙-unit-r _) ∙ q

  ↓-==-out : ∀ {i j} {A : Type i} {B : Type j} {f g : A → B}
    {x y : A} {p : x == y} {u : f x == g x} {v : f y == g y}
    → (u == v [ (λ x → f x == g x) ↓ p ])
    → (u ∙ ap g p) == (ap f p ∙ v)
  ↓-==-out {p = idp} q = (∙-unit-r _) ∙ q

  module HFiberSrcEquiv {i₀ i₁ i₂} {A : Type i₀} {B : Type i₁} {C : Type i₂}
    (f : A → B) (h : B → C) (k : A → C)
    (w : (a : A) → h (f a) == k a)
    (ise-f : is-equiv f)
    where

     private
       g = is-equiv.g ise-f
       f-g = is-equiv.f-g ise-f
       g-f = is-equiv.g-f ise-f
       adj = is-equiv.adj ise-f
       adj' = is-equiv.adj' ise-f

       to : (c : C) → hfiber k c → hfiber h c
       to .(k a) (a , idp) = f a , w a

       to-lem₀ : (c : C) (x : hfiber k c) → fst (to c x) == f (fst x)
       to-lem₀ .(k a) (a , idp) = idp

       to-lem₁ : (c : C) (x : hfiber k c) → snd (to c x) == ap h (to-lem₀ c x) ∙ w (fst x) ∙ snd x
       to-lem₁ .(k a) (a , idp) = ! (∙-unit-r (w a))

       from : (c : C) → hfiber h c → hfiber k c
       from .(h b) (b , idp) = g b , ! (w (g b)) ∙ ap h (f-g b)

       from-lem₀ : (c : C) (x : hfiber h c) → fst (from c x) == g (fst x)
       from-lem₀ .(h b) (b , idp) = idp

       from-lem₁ : (c : C) (x : hfiber h c) → snd (from c x) == ap k (from-lem₀ c x) ∙ (! (w (g (fst x)))) ∙ ap h (f-g (fst x)) ∙ snd x
       from-lem₁ .(h b) (b , idp) = ap (λ p → ! (w (g b)) ∙ p) (! (∙-unit-r (ap h (f-g b))))

       to-from : (c : C) (x : hfiber h c) → to c (from c x) == x
       to-from .(h b) (b , idp) = pair= (q ∙ f-g b) (↓-app=cst-in (r ∙ eq))

         where c = h b
               p = ! (w (g b)) ∙ ap h (f-g b)
               q = to-lem₀ c (g b , p)
               r = to-lem₁ c (g b , p)

               eq = ap h q ∙ w (g b) ∙ ! (w (g b)) ∙ ap h (f-g b)
                      =⟨ ! (∙-assoc (w (g b)) (! (w (g b))) (ap h (f-g b))) |in-ctx (λ x → ap h q ∙ x) ⟩
                    ap h q ∙ (w (g b) ∙ (! (w (g b)))) ∙ ap h (f-g b)
                      =⟨ !-inv-r (w (g b)) |in-ctx (λ x → ap h q ∙ x ∙ ap h (f-g b)) ⟩
                    ap h q ∙ ap h (f-g b)
                      =⟨ ∙-ap h q (f-g b) ⟩
                    ap h (q ∙ f-g b)
                      =⟨ ! (∙-unit-r (ap h (q ∙ f-g b))) ⟩
                    ap h (q ∙ f-g b) ∙ idp ∎

       from-to : (c : C) (x : hfiber k c) → from c (to c x) == x
       from-to .(k a) (a , idp) = pair= (p ∙ g-f a) (↓-app=cst-in (q ∙ eq))

         where c = k a
               p = from-lem₀ c (f a , w a)
               q = from-lem₁ c (f a , w a)

               eq = ap k p ∙ ! (w (g (f a))) ∙ ap h (f-g (f a)) ∙ w a
                       =⟨ ! (adj a) |in-ctx (λ x → ap k p ∙ ! (w (g (f a))) ∙ ap h x ∙ w a) ⟩
                    ap k p ∙ ! (w (g (f a))) ∙ ap h (ap f (g-f a)) ∙ w a
                       =⟨ ∘-ap h f (g-f a) |in-ctx (λ x → ap k p ∙ ! (w (g (f a))) ∙ x ∙ w a) ⟩
                    ap k p ∙ ! (w (g (f a))) ∙ ap (h ∘ f) (g-f a) ∙ w a
                       =⟨ ! (↓-='-out (apd w (g-f a))) |in-ctx (λ x → ap k p ∙ ! (w (g (f a))) ∙ x) ⟩
                    ap k p ∙ ! (w (g (f a))) ∙ w (g (f a)) ∙' ap k (g-f a)
                       =⟨ ∙'=∙ (w (g (f a))) (ap k (g-f a)) |in-ctx (λ x → ap k p ∙ ! (w (g (f a))) ∙ x) ⟩
                    ap k p ∙ ! (w (g (f a))) ∙ w (g (f a)) ∙ ap k (g-f a)
                       =⟨ ! (∙-assoc (! (w (g (f a)))) (w (g (f a))) (ap k (g-f a))) |in-ctx (λ x → ap k p ∙ x) ⟩
                    ap k p ∙ (! (w (g (f a))) ∙ w (g (f a))) ∙ ap k (g-f a)
                       =⟨ !-inv-l (w (g (f a))) |in-ctx (λ x → ap k p ∙ x ∙ ap k (g-f a)) ⟩
                    ap k p ∙ ap k (g-f a)
                       =⟨ ∙-ap k p (g-f a) ⟩
                    ap k (p ∙ g-f a)
                      =⟨ ! (∙-unit-r (ap k (p ∙ g-f a))) ⟩
                    ap k (p ∙ g-f a) ∙ idp ∎

     eqv : (c : C) → hfiber k c ≃ hfiber h c
     eqv c = equiv (to c) (from c) (to-from c) (from-to c)

  module HFiberTgtEquiv {i₀ i₁ i₂} {A : Type i₀} {B : Type i₁} {C : Type i₂}
    (h : A → B) (k : A → C) (f : B → C)
    (w : (a : A) → k a == f (h a))
    (ise-f : is-equiv f)
    where

     private
       g = is-equiv.g ise-f
       f-g = is-equiv.f-g ise-f
       g-f = is-equiv.g-f ise-f
       adj = is-equiv.adj ise-f
       adj' = is-equiv.adj' ise-f

       to : (b : B) → hfiber h b → hfiber k (f b)
       to .(h a) (a , idp) = a , w a

       to-lem₀ : (b : B) (x : hfiber h b) → fst (to b x) == fst x
       to-lem₀ .(h a) (a , idp) = idp

       to-lem₁ : (b : B) (x : hfiber h b) → snd (to b x) == ap k (to-lem₀ b x) ∙ w (fst x) ∙ ap f (snd x)
       to-lem₁ .(h a) (a , idp) = ! (∙-unit-r (w a))

       from : (b : B) → hfiber k (f b) → hfiber h b
       from b (a , p) = a , (! (g-f (h a)) ∙ ap g (! (w a) ∙ p) ∙ g-f b)

       to-from : (b : B) (x : hfiber k (f b)) → to b (from b x) == x
       to-from b (a , p) = pair= r (↓-app=cst-in (s ∙ (ap (λ x → ap k r ∙ x) eq)))

         where q = ! (g-f (h a)) ∙ ap g (! (w a) ∙ p) ∙ g-f b
               r = to-lem₀ b (a , q)
               s = to-lem₁ b (a , q)

               eq = w a ∙ ap f q
                      =⟨ ! (∙-ap f (! (g-f (h a))) (ap g (! (w a) ∙ p) ∙ g-f b)) |in-ctx (λ x → w a ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ ap f (ap g (! (w a) ∙ p) ∙ g-f b)
                      =⟨ ! (∙-ap f (ap g (! (w a) ∙ p)) (g-f b)) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ ap f (ap g (! (w a) ∙ p)) ∙ ap f (g-f b)
                      =⟨ ∘-ap f g (! (w a) ∙ p) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ x ∙ ap f (g-f b)) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ ap (f ∘ g) (! (w a) ∙ p) ∙ ap f (g-f b)
                      =⟨ ap (λ x → w a ∙ ap f (! (g-f (h a))) ∙ x ∙ ap f (g-f b)) (eqv-square (f , ise-f) (! (w a) ∙ p)) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ (f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ ! (f-g (f b))) ∙ ap f (g-f b)
                      =⟨ adj b |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ (f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ ! (f-g (f b))) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ (f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ ! (f-g (f b))) ∙ f-g (f b)
                      =⟨ ∙-assoc (f-g (f (h a))) ((! (w a) ∙ p) ∙ ! (f-g (f b))) (f-g (f b)) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ ((! (w a) ∙ p) ∙ ! (f-g (f b))) ∙ f-g (f b)
                      =⟨ ∙-assoc (! (w a) ∙ p) (! (f-g (f b))) (f-g (f b)) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ ! (f-g (f b)) ∙ f-g (f b)
                      =⟨ !-inv-l (f-g (f b)) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ (! (w a) ∙ p) ∙ idp
                      =⟨ ∙-unit-r (! (w a) ∙ p) |in-ctx (λ x → w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ x) ⟩
                    w a ∙ ap f (! (g-f (h a))) ∙ f-g (f (h a)) ∙ ! (w a) ∙ p
                      =⟨ ap-! f (g-f (h a)) |in-ctx (λ x → w a ∙ x ∙ f-g (f (h a)) ∙ ! (w a) ∙ p) ⟩
                    w a ∙ ! (ap f (g-f (h a))) ∙ f-g (f (h a)) ∙ ! (w a) ∙ p
                      =⟨ ! (adj (h a)) |in-ctx (λ x → w a ∙ ! (ap f (g-f (h a))) ∙ x ∙ ! (w a) ∙ p) ⟩
                    w a ∙ ! (ap f (g-f (h a))) ∙ ap f (g-f (h a)) ∙ ! (w a) ∙ p
                      =⟨ ! (∙-assoc (! (ap f (g-f (h a)))) (ap f (g-f (h a))) (! (w a) ∙ p)) |in-ctx (λ x → w a ∙ x)⟩
                    w a ∙ (! (ap f (g-f (h a))) ∙ ap f (g-f (h a))) ∙ ! (w a) ∙ p
                      =⟨ !-inv-l (ap f (g-f (h a))) |in-ctx (λ x → w a ∙ x ∙ ! (w a) ∙ p)⟩
                    w a ∙ ! (w a) ∙ p
                      =⟨ ! (∙-assoc (w a) (! (w a)) p) ⟩
                    (w a ∙ ! (w a)) ∙ p
                      =⟨ !-inv-r (w a) |in-ctx (λ x → x ∙ p) ⟩
                    p ∎

       from-to : (b : B) (x : hfiber h b) → from b (to b x) == x
       from-to .(h a) (a , idp) = pair= idp eq

         where eq = ! (g-f (h a)) ∙ ap g (! (w a) ∙ w a) ∙ g-f (h a)
                      =⟨ !-inv-l (w a)|in-ctx (λ x → ! (g-f (h a)) ∙ ap g x ∙ g-f (h a)) ⟩
                    ! (g-f (h a)) ∙ g-f (h a)
                      =⟨ !-inv-l (g-f (h a)) ⟩
                    idp ∎

     eqv : (b : B) → hfiber h b ≃ hfiber k (f b)
     eqv b = equiv (to b) (from b) (to-from b) (from-to b)

  module HFiberSqEquiv {i₀ i₁ j₀ j₁}
    {A₀ : Type i₀} {A₁ : Type i₁} {B₀ : Type j₀} {B₁ : Type j₁}
    (f₀ : A₀ → B₀) (f₁ : A₁ → B₁) (hA : A₀ → A₁) (hB : B₀ → B₁)
    (sq : CommSquare f₀ f₁ hA hB) (ise-hA : is-equiv hA) (ise-hB : is-equiv hB) where

    open CommSquare

    private
      gB = is-equiv.g ise-hB

    module Src = HFiberSrcEquiv hA f₁ (hB ∘ f₀) (λ a₀ → ! (commutes sq a₀)) ise-hA
    module Tgt = HFiberTgtEquiv f₀ (f₁ ∘ hA) hB (λ a₀ → ! (commutes sq a₀)) ise-hB

    eqv : (b₀ : B₀) → hfiber f₀ b₀ ≃ hfiber f₁ (hB b₀)
    eqv b₀ = Src.eqv (hB b₀)
      ∘e coe-equiv (ap (λ s → hfiber s (hB b₀)) (λ= (λ a₀ → ! (commutes sq a₀))))
      ∘e Tgt.eqv b₀

  hfiber-sq-eqv : ∀ {i₀ i₁ j₀ j₁}
    {A₀ : Type i₀} {A₁ : Type i₁} {B₀ : Type j₀} {B₁ : Type j₁}
    (f₀ : A₀ → B₀) (f₁ : A₁ → B₁) (hA : A₀ → A₁) (hB : B₀ → B₁)
    (sq : CommSquare f₀ f₁ hA hB) (ise-hA : is-equiv hA) (ise-hB : is-equiv hB) →
    (b₀ : B₀) → hfiber f₀ b₀ ≃ hfiber f₁ (hB b₀)
  hfiber-sq-eqv f₀ f₁ hA hB sq ise-hA ise-hB b₀ = M.eqv b₀

    where module M = HFiberSqEquiv f₀ f₁ hA hB sq ise-hA ise-hB


------------------------------------------------------------------------
-- A definition of the propositional truncation operator that does not
-- use recursive higher inductive types
------------------------------------------------------------------------

-- The definition does use natural numbers. The code is based on van
-- Doorn's "Constructing the Propositional Truncation using
-- Non-recursive HITs".

{-# OPTIONS --erased-cubical --safe #-}

import Equality.Path as P

module H-level.Truncation.Propositional.Non-recursive
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

private
  open module PD = P.Derived-definitions-and-properties eq
    hiding (elim)

open import Prelude

open import Colimit.Sequential eq as C using (Colimit)
open import Equality.Decidable-UIP equality-with-J
open import Equality.Path.Isomorphisms eq
open import Equivalence equality-with-J as Eq using (_≃_)
open import Function-universe equality-with-J as F
open import H-level equality-with-J
open import H-level.Closure equality-with-J
import H-level.Truncation.Propositional eq as T
open import H-level.Truncation.Propositional.Non-recursive.Erased eq
  as N using (∥_∥ᴱ)
open import H-level.Truncation.Propositional.One-step eq as O
  using (∥_∥¹; ∥_∥¹-out-^; ∣_∣-out-^)

private
  variable
    a p : Level
    A B : Type a
    P   : A → Type p
    e x : A

-- The propositional truncation operator.

∥_∥ : Type a → Type a
∥ A ∥ = Colimit ∥ A ∥¹-out-^ O.∣_∣

-- The point constructor.

∣_∣ : A → ∥ A ∥
∣_∣ = C.∣_∣

-- The eliminator.

record Elim {A : Type a} (P : ∥ A ∥ → Type p) : Type (a ⊔ p) where
  no-eta-equality
  field
    ∣∣ʳ             : ∀ x → P ∣ x ∣
    is-propositionʳ : ∀ x → Is-proposition (P x)

open Elim public

elim : Elim P → (x : ∥ A ∥) → P x
elim {A = A} {P = P} e = C.elim λ where
    .C.Elim.∣∣ʳ {n = n} → helper n
    .C.Elim.∣∣≡∣∣ʳ _    → E.is-propositionʳ _ _ _
  where
  module E = Elim e

  helper : ∀ n (x : ∥ A ∥¹-out-^ n) → P C.∣ x ∣
  helper zero    = E.∣∣ʳ
  helper (suc n) = O.elim λ where
    .O.Elim.∣∣ʳ x            → subst P (sym (C.∣∣≡∣∣ x)) (helper n x)
    .O.Elim.∣∣-constantʳ _ _ → E.is-propositionʳ _ _ _

_ : elim e ∣ x ∣ ≡ e .∣∣ʳ x
_ = refl _

-- The propositional truncation operator returns propositions.

∥∥-proposition : Is-proposition ∥ A ∥
∥∥-proposition {A = A} = elim λ where
    .is-propositionʳ → Π≡-proposition ext
    .∣∣ʳ x → C.elim λ where
      .C.Elim.∣∣ʳ              → lemma₁ _ x
      .C.Elim.∣∣≡∣∣ʳ {n = n} y →
        subst (∣ x ∣ ≡_) (C.∣∣≡∣∣ y) (lemma₁ (suc n) x O.∣ y ∣)   ≡⟨ sym trans-subst ⟩

        trans (lemma₁ (1 + n) x O.∣ y ∣) (C.∣∣≡∣∣ y)              ≡⟨⟩

        trans (trans (sym (trans (C.∣∣≡∣∣ (∣ x ∣-out-^ (1 + n)))
                             (lemma₀ (1 + n) x)))
                 (trans (cong C.∣_∣
                           (O.∣∣-constant
                              (∣ x ∣-out-^ (1 + n)) O.∣ y ∣))
                    (C.∣∣≡∣∣ O.∣ y ∣)))
          (C.∣∣≡∣∣ y)                                             ≡⟨ trans (cong (λ eq → trans (trans eq
                                                                                (trans (cong C.∣_∣ (O.∣∣-constant _ _))
                                                                                (C.∣∣≡∣∣ _)))
                                                                                (C.∣∣≡∣∣ _)) $
                                                                            sym-trans _ _) $
                                                                     trans (trans-assoc _ _ _) $
                                                                     trans (trans-assoc _ _ _) $
                                                                     cong (trans (sym (lemma₀ (1 + n) _))) $
                                                                     sym $ trans-assoc _ _ _ ⟩
        trans (sym (lemma₀ (1 + n) x))
          (trans (trans (sym (C.∣∣≡∣∣ (∣ x ∣-out-^ (1 + n))))
                    (trans (cong C.∣_∣
                              (O.∣∣-constant
                                 (∣ x ∣-out-^ (1 + n)) O.∣ y ∣))
                       (C.∣∣≡∣∣ O.∣ y ∣)))
             (C.∣∣≡∣∣ y))                                         ≡⟨ cong (λ eq → trans (sym (lemma₀ (1 + n) _))
                                                                                (trans (trans (sym (C.∣∣≡∣∣ _)) eq) (C.∣∣≡∣∣ _))) $ sym $
                                                                     lemma₂ _ _ _ _ _ ⟩
        trans (sym (lemma₀ (1 + n) x))
          (trans (trans (sym (C.∣∣≡∣∣ (∣ x ∣-out-^ (1 + n))))
                    (trans (C.∣∣≡∣∣ (∣ x ∣-out-^ (1 + n)))
                       (cong C.∣_∣ (O.∣∣-constant _ _))))
             (C.∣∣≡∣∣ y))                                         ≡⟨ cong (λ eq → trans (sym (lemma₀ (1 + n) _)) (trans eq (C.∣∣≡∣∣ _))) $
                                                                     trans-sym-[trans] _ _ ⟩
        trans (sym (lemma₀ (1 + n) x))
          (trans (cong C.∣_∣ (O.∣∣-constant _ _)) (C.∣∣≡∣∣ y))    ≡⟨⟩

        lemma₁ n x y                                              ∎
  where
  lemma₀ : ∀ n (x : A) → C.∣ ∣ x ∣-out-^ n ∣ ≡ ∣ x ∣
  lemma₀ zero    x = ∣ x ∣  ∎
  lemma₀ (suc n) x =
    C.∣ O.∣ ∣ x ∣-out-^ n ∣ ∣  ≡⟨ C.∣∣≡∣∣ (∣ x ∣-out-^ n) ⟩
    C.∣ ∣ x ∣-out-^ n ∣        ≡⟨ lemma₀ n x ⟩∎
    ∣ x ∣                      ∎

  lemma₁ : ∀ n (x : A) (y : ∥ A ∥¹-out-^ n) → ∣ x ∣ ≡ C.∣ y ∣
  lemma₁ n x y =
    ∣ x ∣                      ≡⟨ sym (lemma₀ (suc n) x) ⟩
    C.∣ ∣ x ∣-out-^ (suc n) ∣  ≡⟨⟩
    C.∣ O.∣ ∣ x ∣-out-^ n ∣ ∣  ≡⟨ cong C.∣_∣ (O.∣∣-constant _ _) ⟩
    C.∣ O.∣ y ∣ ∣              ≡⟨ C.∣∣≡∣∣ y ⟩∎
    C.∣ y ∣                    ∎

  lemma₂ :
    ∀ n (x y : ∥ A ∥¹-out-^ n) (p : x ≡ y) (q : O.∣ x ∣ ≡ O.∣ y ∣) →
    trans (C.∣∣≡∣∣ {P = ∥ A ∥¹-out-^} x) (cong C.∣_∣ p) ≡
    trans (cong C.∣_∣ q) (C.∣∣≡∣∣ y)
  lemma₂ n x y p q =
    trans (C.∣∣≡∣∣ x) (cong C.∣_∣ p)                        ≡⟨ PD.elim
                                                                 (λ {x y} p → trans (C.∣∣≡∣∣ x) (cong C.∣_∣ p) ≡
                                                                              trans (cong C.∣_∣ (cong O.∣_∣ p)) (C.∣∣≡∣∣ y))
                                                                 (λ x →
      trans (C.∣∣≡∣∣ x) (cong C.∣_∣ (refl _))                       ≡⟨ cong (trans _) $ cong-refl _ ⟩
      trans (C.∣∣≡∣∣ x) (refl _)                                    ≡⟨ trans-reflʳ _ ⟩
      C.∣∣≡∣∣ x                                                     ≡⟨ sym $ trans-reflˡ _ ⟩
      trans (refl _) (C.∣∣≡∣∣ x)                                    ≡⟨ cong (flip trans _) $ sym $
                                                                       trans (cong (cong C.∣_∣) $ cong-refl _) $
                                                                       cong-refl _ ⟩∎
      trans (cong C.∣_∣ (cong O.∣_∣ (refl _))) (C.∣∣≡∣∣ x)          ∎)
                                                                 p ⟩

    trans (cong C.∣_∣ (cong O.∣_∣ p)) (C.∣∣≡∣∣ y)           ≡⟨ cong (flip trans _) $
                                                               cong-preserves-Constant
                                                                 (λ u v →
      C.∣ u ∣                                                       ≡⟨ sym (C.∣∣≡∣∣ u) ⟩
      C.∣ O.∣ u ∣ ∣                                                 ≡⟨ cong C.∣_∣ (O.∣∣-constant _ _) ⟩
      C.∣ O.∣ v ∣ ∣                                                 ≡⟨ C.∣∣≡∣∣ v ⟩∎
      C.∣ v ∣                                                       ∎)
                                                                 _ _ ⟩∎
    trans (cong C.∣_∣ q) (C.∣∣≡∣∣ y)                        ∎

-- ∥_∥ is pointwise equivalent to T.∥_∥.

∥∥≃∥∥ : ∥ A ∥ ≃ T.∥ A ∥
∥∥≃∥∥ = Eq.⇔→≃
  ∥∥-proposition
  T.truncation-is-proposition
  (elim λ where
     .∣∣ʳ               → T.∣_∣
     .is-propositionʳ _ → T.truncation-is-proposition)
  (T.rec ∥∥-proposition ∣_∣)

-- Functions from T.∥ A ∥ can be expressed as families of functions
-- from ∥ A ∥¹-out-^ that satisfy a certain property.

∥∥→≃ :
  (T.∥ A ∥ → B)
    ≃
  (∃ λ (f : ∀ n → ∥ A ∥¹-out-^ n → B) →
     ∀ n x → f (suc n) O.∣ x ∣ ≡ f n x)
∥∥→≃ {A = A} {B = B} =
  (T.∥ A ∥ → B)                          ↝⟨ →-cong ext (inverse ∥∥≃∥∥) F.id ⟩

  (∥ A ∥ → B)                            ↝⟨ C.universal-property ⟩□

  (∃ λ (f : ∀ n → ∥ A ∥¹-out-^ n → B) →
     ∀ n x → f (suc n) O.∣ x ∣ ≡ f n x)  □

------------------------------------------------------------------------
-- Some conversion functions

-- ∥ A ∥ᴱ implies ∥ A ∥.

∥∥ᴱ→∥∥ : ∥ A ∥ᴱ → ∥ A ∥
∥∥ᴱ→∥∥ = N.elim λ where
  .N.∣∣ʳ               → ∣_∣
  .N.is-propositionʳ _ → ∥∥-proposition

-- In erased contexts ∥ A ∥ᴱ and ∥ A ∥ are equivalent.

@0 ∥∥ᴱ≃∥∥ : ∥ A ∥ᴱ ≃ ∥ A ∥
∥∥ᴱ≃∥∥ = Eq.↔→≃
  ∥∥ᴱ→∥∥
  (elim λ @0 where
     .∣∣ʳ               → N.∣_∣
     .is-propositionʳ _ → N.∥∥ᴱ-proposition)
  (elim λ @0 where
     .∣∣ʳ _             → refl _
     .is-propositionʳ _ → mono₁ 1 ∥∥-proposition)
  (N.elim λ where
     .N.∣∣ʳ _             → refl _
     .N.is-propositionʳ _ → mono₁ 1 N.∥∥ᴱ-proposition)


{-# OPTIONS --without-K #-}

open import Base
open import Homotopy.Connected

{-
    Wedge is a pushout.
-}

module Homotopy.Wedge
  where

  import Homotopy.Pushout as P

  record wedge-diag i : Set (suc i) where
    constructor diag_,_,_,_
    field
      A : Set i
      B : Set i
      a : A
      b : B

    f : unit {i} → A
    f _ = a
    g : unit {i} → B
    g _ = b

  wedge-diag-to-pushout-diag : ∀ {i} → wedge-diag i → P.pushout-diag i
  wedge-diag-to-pushout-diag {i} (diag A , B , a , b) = P.diag A , B , unit {i} , (λ _ → a) , (λ _ → b)

  wedge : ∀ {i} → wedge-diag i → Set i
  wedge d = P.pushout (wedge-diag-to-pushout-diag d)

  left : ∀ {i} {d : wedge-diag i} → wedge-diag.A d → wedge d
  left = P.left

  right : ∀ {i} {d : wedge-diag i} → wedge-diag.B d → wedge d
  right = P.right

  glue : ∀ {i} {d : wedge-diag i} → left (wedge-diag.a d) ≡ right (wedge-diag.b d)
  glue = P.glue tt

  wedge-rec : ∀ {i} {d : wedge-diag i}
    → let open wedge-diag d in
    ∀ {j} (P : wedge d → Set j)
    (left* : ∀ a → P (left a))
    (right* : ∀ b → P (right b))
    (glue* : transport P glue (left* a) ≡ right* b)
    → (∀ x → P x)
  wedge-rec P left* right* glue* = P.pushout-rec P left* right* (λ _ → glue*)

  wedge-rec-nondep : ∀ {i} {d : wedge-diag i}
    → let open wedge-diag d in
    ∀ {j} (P : Set j)
    (left* : ∀ a → P)
    (right* : ∀ b → P)
    (glue* : left* a ≡ right* b)
    → (wedge d → P)
  wedge-rec-nondep P left* right* glue* = P.pushout-rec-nondep P left* right* (λ _ → glue*)

{-
  module _ (f : X → Y) where
  nA (A-is-conn : is-connected n A)
  nB (B-is-conn : is-connected n B)
  (P : A → B → Set k)
  ⦃ P-is-trunc : ∀ a b → is-connected (n +2+ m) (P a b) ⦄
    extension :
-}


open import Oscar.Prelude
open import Oscar.Class.Smap
open import Oscar.Data.Proposequality
open import Oscar.Class.Surjection

module Oscar.Data.Surjcollation where

𝓼urjcollation : ∀ {𝔵₁ 𝔵₂ 𝔞 𝔟} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂}
  (surjection : Surjection.type 𝔛₁ 𝔛₂)
  𝔟̇
  (𝔅 : 𝔛₂ → Ø 𝔟)
  (𝔄 : π̂² 𝔞 𝔛₁)
  → Ø 𝔵₁ ∙̂ 𝔞 ∙̂ 𝔟 ∙̂ ↑̂ 𝔟̇
𝓼urjcollation surjection 𝔟̇ 𝔅 𝔄 = ∀ {m} → 𝔅 (surjection m) → 𝔅 (surjection m) → LeftṖroperty 𝔟̇ 𝔄 m

module _ {𝔵₁ 𝔵₂ 𝔞 𝔟 𝔟̇} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂}
  (𝔅 : 𝔛₂ → Ø 𝔟)
  (𝔄 : π̂² 𝔞 𝔛₁)
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
  (𝔅̇ : ∀̇ π̂² 𝔟̇ 𝔅)
  (let infix 4 _⟨𝔅̇⟩_
       _⟨𝔅̇⟩_ : ∀ {x} → 𝔅 x → 𝔅 x → Ø 𝔟̇
       _⟨𝔅̇⟩_ p q = 𝔅̇ p q)
  where
  surjcollation[_]⟦_/_⟧ : 𝓼urjcollation surjection 𝔟̇ 𝔅 𝔄
  surjcollation[_]⟦_/_⟧ p q .π₀ x = x ◃ p ⟨𝔅̇⟩ x ◃ q

module _ {𝔵₁ 𝔵₂ 𝔞 𝔟 𝔟̇} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂} {𝔅 : 𝔛₂ → Ø 𝔟}
  (𝔄 : π̂² 𝔞 𝔛₁)
  ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
  (𝔅̇ : ∀̇ π̂² 𝔟̇ 𝔅)
  where
  surjcollation⟦_/_⟧ = surjcollation[ 𝔅 ]⟦ 𝔄 / 𝔅̇ ⟧

module _ {𝔵₁ 𝔵₂ 𝔞} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂}
  (𝔄 : 𝔛₁ → 𝔛₁ → Ø 𝔞)
    {𝔟} {𝔅 : 𝔛₂ → Ø 𝔟}
    ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
    ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
    {𝔟̇} {𝔅̇ : ∀ {x} → 𝔅 x → 𝔅 x → Ø 𝔟̇}
  where
  surjcollation⟦_⟧ = surjcollation[ 𝔅 ]⟦ 𝔄 / 𝔅̇ ⟧

module _ {𝔵₁ 𝔵₂ 𝔞 𝔟} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂}
  (𝔅 : 𝔛₂ → Ø 𝔟)
  (𝔄 : π̂² 𝔞 𝔛₁)
    ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
    ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
  where
  ≡-surjcollation[_]⟦_⟧ = surjcollation[ 𝔅 ]⟦ 𝔄 / _≡_ ⟧

module _ {𝔵₁ 𝔞} {𝔛₁ : Ø 𝔵₁}
  (𝔄 : π̂² 𝔞 𝔛₁)
    {𝔵₂} {𝔛₂ : Ø 𝔵₂} ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
    {𝔟} {𝔅 : 𝔛₂ → Ø 𝔟} ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
  where
  ≡-surjcollation⟦_⟧ = ≡-surjcollation[ 𝔅 ]⟦ 𝔄 ⟧

module _ {𝔵₁ 𝔞} {𝔛₁ : Ø 𝔵₁} {𝔄 : π̂² 𝔞 𝔛₁}
  where
  ≡-surjcollation = ≡-surjcollation⟦ 𝔄 ⟧

module Surjcollation {𝔵₁ 𝔵₂ 𝔞 𝔟̇} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂} ⦃ _ : Surjection.class 𝔛₁ 𝔛₂ ⦄
  (𝔄 : 𝔛₁ → 𝔛₁ → Ø 𝔞)
  (𝔅̇ : ∀ {𝔟} {𝔅 : 𝔛₂ → Ø 𝔟} → (∀ {x} → 𝔅 x → 𝔅 x → Ø 𝔟̇))
    {𝔟} {𝔅 : 𝔛₂ → Ø 𝔟}
    ⦃ _ : Smaphomarrow!.class 𝔄 𝔅 ⦄
  where
  method = surjcollation[ 𝔅 ]⟦ 𝔄 / 𝔅̇ {𝔅 = 𝔅} ⟧

  infix 18 _⟹_
  _⟹_ = method