{-# OPTIONS --postfix-projections --without-K #-}

open import Agda.Primitive

infixr 50 _,_
infixr 30 _×_

-- Unit type
record  {j} : Set j where
  constructor tt
open  public

-- Σ-types
record Σ {i j} (A : Set i) (B : A  Set j) : Set (i  j) where
  constructor _,_
  field fst : A
        snd : B fst
open Σ public

_×_ :  {i j} (A : Set i) (B : Set j)  Set (i  j)
A × B = Σ A λ _  B

record Lift {i} j (A : Set i) : Set (i  j) where
  constructor lift
  field
    lower : A
open Lift public

-- Id types
data Id {i} {A : Set i} (x : A) : A  Set i where refl : Id x x

--------------------------------------------------------------------------------

ap :  {i j} {A : Set i} {B : Set j} (f : A  B) {x y : A} (p : Id x y)  Id (f x) (f y)
ap f refl = refl

ap-id :  {i} {A : Set i} {x y : A} {p : Id x y}  Id (ap  x  x) p) p
ap-id {p = refl} = refl

ap-∘ :  {i j k} {A : Set i} {B : Set j} {C : Set k} {x y : A} {p : Id x y} {f : B  C} {g : A  B}
      Id (ap f (ap g p)) (ap  x  f (g x)) p)
ap-∘ {p = refl} = refl

ap₂ :  {i j k} {A : Set i} {B : Set j} {C : Set k} (f : A  B  C) {x y : A} {z w : B} (p : Id x y) (q : Id z w)  Id (f x z) (f y w)
ap₂ f refl refl = refl

J :  {i j} {A : Set i} {x : A} (B :  y  Id x y  Set j) {y} (p : Id x y)  B x refl  B y p
J B refl bx = bx

J' :  {i j} {A : Set i} {x : A} (B :  y  Id y x  Set j) {y} (p : Id y x)  B x refl  B y p
J' B refl bx = bx

transp :  {i j} {A : Set i} (B : A  Set j) {x y : A} (p : Id x y)  B x  B y
transp B refl bx = bx

_∙_ :  {i} {A : Set i} {x y z : A}  Id x y  Id y z  Id x z
refl  refl = refl

∙-idl :  {i} {A : Set i} {x y : A} {p : Id x y}  Id (refl  p) p
∙-idl {p = refl} = refl

∙-idr :  {i} {A : Set i} {x y : A} {p : Id x y}  Id (p  refl) p
∙-idr {p = refl} = refl

sym :  {i} {A : Set i} {x y : A}  Id x y  Id y x
sym refl = refl

cancelr :  {i} {A : Set i} {x y z : A} {p q : Id x y} {r : Id y z}  Id (p  r) (q  r)  Id p q
cancelr {r = refl} h = sym ∙-idr  (h  ∙-idr)

∙-transpose-left :  {i} {A : Set i} {x y z : A} {q : Id x z} {p : Id x y} {r : Id y z}  Id q (p  r)  Id (sym p  q) r
∙-transpose-left {p = refl} {r = refl} refl = refl

trans-sym-l :  {i} {A : Set i} {x y : A} {p : Id x y}  Id (sym p  p) refl
trans-sym-l {p = refl} = refl

transp-const :  {i j} {A : Set i} {B : Set j} {x y} {p : Id {A = A} x y} {d}  Id (transp  _  B) p d) d
transp-const {p = refl} = refl

--------------------------------------------------------------------------------

record is-contr {i} (A : Set i) : Set i where
  field
    is-contr-center : A
    is-contr-path   :  y  Id is-contr-center y

  abstract
    is-contr-all-paths :  x y  Id {A = A} x y
    is-contr-all-paths x y = sym (is-contr-path x)  is-contr-path y
    
    is-contr-all-paths-β :  {x}  Id (is-contr-all-paths x x) refl
    is-contr-all-paths-β = trans-sym-l
open is-contr public

is-contr-⊤ :  {i}  is-contr ( {i})
is-contr-⊤ .is-contr-center = tt
is-contr-⊤ .is-contr-path = λ y  refl

is-contr-path-to :  {i} {A : Set i} {x : A}  is-contr (Σ A λ y  Id y x)
is-contr-path-to .is-contr-center = _ , refl
is-contr-path-to .is-contr-path (_ , refl) = refl

is-contr-path-from :  {i} {A : Set i} {x : A}  is-contr (Σ A λ y  Id x y)
is-contr-path-from .is-contr-center = _ , refl
is-contr-path-from .is-contr-path (_ , refl) = refl

is-equiv :  {i j} {A : Set i} {B : Set j} (f : A  B)  Set (i  j)
is-equiv f =  b  is-contr (Σ _ λ a  Id (f a) b)

equiv :  {i j} (A : Set i) (B : Set j)  Set (i  j)
equiv A B = Σ (A  B) is-equiv

abstract
  transp-contr :  {i j} {A : Set i}  is-contr A  (B : A  Set j)   x y  B x  B y
  transp-contr cA B x y bx = transp B (is-contr-all-paths cA x y) bx
  
  transp-contr-β :  {i j} {A : Set i} {cA : is-contr A} {B : A  Set j} {x : A} {bx}  Id (transp-contr cA B x x bx) bx
  transp-contr-β {i} {j} {A} {cA} {B} {x} {bx} = ap  p  transp B p bx) (is-contr-all-paths-β cA)

pair-Id :  {i j} {A : Set i} {B : A  Set j} {a₁ a₂} (p : Id {A = A} a₁ a₂)
            {b₁ : B a₁} {b₂ : B a₂} (q : Id (transp B p b₁) b₂)
           Id {A = Σ A B} (a₁ , b₁) (a₂ , b₂)
pair-Id refl refl = refl

--------------------------------------------------------------------------------

happly :  {i j} {A : Set i} {B : A  Set j} {f g : (a : A)  B a}
        Id f g  (a : A)  Id (f a) (g a)
happly refl a = refl

postulate
  funext :  {i j} {A : Set i} {B : A  Set j} {f : (a : A)  B a}
          is-contr (Σ ((a : A)  B a) λ g  (∀ a  Id (f a) (g a)))
  ua :  {i} (A : Set i)  is-contr (Σ (Set i) λ B  Σ (A  B) is-equiv)

--------------------------------------------------------------------------------

abstract
  lam-Id :  {i j} {A : Set i} {B : A  Set j} {f g : (a : A)  B a}
             (h : (a : A)  Id (f a) (g a))  Id f g
  lam-Id {f = f} h = transp-contr funext  { (g , _)  Id f g }) (_ , λ _  refl) (_ , h) refl

  lam-Id-β :  {i j} {A : Set i} {B : A  Set j} {f : (a : A)  B a}
            Id (lam-Id {g = f}  x  refl)) refl
  lam-Id-β = transp-contr-β

module _ {i j k l} {A : Set i} {B : A  Set j} {C : Set k} {r : C  A} {D :  c  B (r c)  Set l}
         {f g : (a : A)  B a} {h : (a : A)  Id (f a) (g a)}
         {d :  c  D c (f (r c))}
  where
  abstract
    transp-funext : Id (transp {A =  a  B a}  f  (c : C)  D c (f (r c))) (lam-Id h) d)
                        c  transp (D c) (h (r c)) (d c))
    transp-funext
      = transp-contr (funext {f = f})  { (g , h)   d 
                                                    Id (transp {A =  a  B a}  f  (c : C)  D c (f (r c))) (lam-Id h) d)
                                                         c  transp (D c) (h (r c)) (d c)) })
        (f , λ _  refl)
        (g , h)
         d  ap  p  transp {A =  a  B a}  f  (c : C)  D c (f (r c))) p d) lam-Id-β  refl)
        d
--------------------------------------------------------------------------------

module _ {i} {j} {A : Set i} {B : Set j} (r : A  B) (s : B  A) (p :  a  Id (r (s a)) a) where
  abstract
    is-contr-retract : is-contr A  is-contr B
    is-contr-retract cA .is-contr-center = r (cA .is-contr-center)
    is-contr-retract cA .is-contr-path b = ap r (cA .is-contr-path _)  p _

module _ {i} {j} {A : Set i} {B : Set j} (r : A  B) (r-equiv : is-equiv r) where
  abstract
    is-contr-equiv : is-contr A  is-contr B
    is-contr-equiv = is-contr-retract r  b  r-equiv b .is-contr-center .fst)  b  r-equiv b .is-contr-center .snd)

module _ {i j} {A : Set i} {B : Set j}
  (g : B  A) (f : A  B)
  (gf :  a  Id (g (f a)) a)
  (fg :  b  Id (f (g b)) b)
  where

  private
    nat :  {i j} {A : Set i} {B : Set j} {f g : A  B} (h :  a  Id (f a) (g a)) {x y} (p : Id x y)
         Id (ap f p  h y) (h x  ap g p)
    nat h refl = ∙-idl  sym ∙-idr

    lemma :  {i} {A : Set i} (f : A  A) (h :  a  Id (f a) a) a
           Id (h (f a)) (ap f (h a))
    lemma f h a = sym (cancelr (nat h (h a)  ap₂ _∙_ refl ap-id))

    gf' :  a  Id (g (f a)) a
    gf' a = sym (gf (g (f a)))  (ap g (fg (f a))  gf a)

    coherence :  b  Id (gf' (g b)) (ap g (fg b))
    coherence b
        = ∙-transpose-left
          ( ap₂ _∙_ (ap (ap g) (lemma _ fg _)  ap-∘) refl 
            nat  b  gf (g b)) (fg _))

    compute-transp :  {b} {x y} (p : Id x y) q  Id (transp  a  Id (g a) b) p q) (sym (ap g p)  q)
    compute-transp refl refl = refl

  abstract
    iso→equiv : is-equiv g
    iso→equiv a .is-contr-center
        = f a , gf' a
    iso→equiv _ .is-contr-path (y , refl)
        = pair-Id
          (fg y)
          ( compute-transp (fg y) (gf' (g y)) 
            (ap₂ _∙_ refl (coherence _)  ∙-transpose-left (sym ∙-idr)))

--------------------------------------------------------------------------------

abstract
  is-contr-Id :  {i} {A : Set i}  is-contr A   x y  is-contr (Id {A = A} x y)
  is-contr-Id cA x y .is-contr-center
    = sym (is-contr-all-paths cA x x)  is-contr-all-paths cA x y
  is-contr-Id cA x .x .is-contr-path refl
    = ∙-transpose-left (sym ∙-idr)

module _ {i j} {A : Set i} {B : A  Set j} (cA : is-contr A) (cB :  a  is-contr (B a)) where
  abstract
    is-contr-Σ : is-contr (Σ A B)
    is-contr-Σ .is-contr-center = cA .is-contr-center , cB _ .is-contr-center
    is-contr-Σ .is-contr-path (a , b) = pair-Id (cA .is-contr-path _) (is-contr-all-paths (cB _) _ _)

module _ {i j} {A : Set i} {B : A  Set j} (cA : is-contr A) (cAB : is-contr (Σ A B)) where
  abstract
    is-contr-π₂ :  a  is-contr (B a)
    is-contr-π₂ a
      = is-contr-retract {A = Σ A B}
         { (a' , b)  transp-contr cA B a' a b })
         b  (a , b))
         b  transp-contr-β)
        cAB

module _ {ia ib ic id} {A : Set ia} {B : A  Set ib} {C : A  Set ic} {D :  a  B a  C a  Set id} where
  abstract
    is-contr-ΣΣ : is-contr (Σ A B)  (∀ a b  is-contr (Σ (C a) (D a b)))  is-contr (Σ (Σ A C) λ { (a , c)  Σ (B a) λ b  D a b c })
    is-contr-ΣΣ cAB cCD = is-contr-retract {A = Σ (Σ A B) λ { (a , b)  Σ (C a) (D a b) }}
                             { ((a , b) , (c , d))  ((a , c) , (b , d)) })
                             { ((a , c) , (b , d))  ((a , b) , (c , d)) })
                             _  refl)
                            (is-contr-Σ cAB λ { (a , b)  cCD a b })

module _ {i j} {A : Set i} {B : A  Set j} (cB :  a  is-contr (B a)) where
  abstract
    is-contr-Π : is-contr (∀ a  B a)
    is-contr-Π .is-contr-center a = cB a .is-contr-center
    is-contr-Π .is-contr-path f = lam-Id λ a  cB a .is-contr-path (f a)

module _ {i j k l} {A : Set i} {B : A  Set j} {C : Set k} (r : C  A) (p : is-equiv r) {D :  c  B (r c)  Set l}
         (cBD :  c  is-contr (Σ (B (r c)) (D c)))
  where
  abstract
    private
      helper : is-contr (Σ (∀ c  B (r c)) λ b  (∀ c  D c (b _)))
      helper
        = is-contr-retract
           f   c  f c .fst) ,  c  f c .snd))
           { (g , h) c  g c , h c })
           a  refl)
          (is-contr-Π cBD)

      inv : A  C
      inv a = p a .is-contr-center .fst

      β :  a  Id (r (inv a)) a
      β a = p a .is-contr-center .snd

    is-contr-ΣΠ : is-contr (Σ (∀ a  B a) λ b  (∀ c  D c (b _)))
    is-contr-ΣΠ
      = is-contr-retract
         { (f , g)   a  transp B (β a) (f (inv a)))
                     ,  c  transp-contr
                                {A = Σ C λ c'  Id (r c') (r c)} (p (r c))
                                 { (p , e)  D c (transp B e (f p)) })
                                (_ , refl) _
                                (g c)) })
         { (f , g)   c  f (r c)) ,  c  g c) })
         { (f , g)  pair-Id
                       (lam-Id  a  J'  a' q  Id (transp B q (f a')) (f a)) (β a) refl))
                       ( transp-funext {B = B} {D = D}
                        lam-Id λ c 
                         transp-contr (p (r c))
                          { (xx , yy)  Id (transp (D c) (J'  a' q  Id (transp B q (f a')) (f (r c))) yy refl)
                                              (transp  { (p , e)  D c (transp B e (f (r p))) })
                                               (is-contr-all-paths (p (r c)) (c , refl) (xx , yy)) (g c)))
                                             (g c) })
                         (c , refl) (inv (r c) , β (r c))
                         (ap {A = Id {A = Σ C λ c'  Id (r c') (r c)} (c , refl) (c , refl)}  q  transp  { (p , e)  D c (transp B e (f (r p))) }) q _)
                             {x = is-contr-all-paths (p (r c)) (c , refl) (c , refl)} (is-contr-all-paths-β _))
                       )})
        helper

abstract
  fst-equiv :  {i j} {A : Set i} {B : A  Set j}  (∀ a  is-contr (B a))  is-equiv (fst {B = B})
  fst-equiv {i} {j} {A} {B} cB a
    = is-contr-retract {A = B a}
       b  (a , b) , refl)
       { ((_ , b) , refl)  b })
       { ((_ , b) , refl)  refl })
      (cB a)

abstract
  id-equiv :  {i} {A : Set i}  equiv A A
  id-equiv =  x  x) , iso→equiv _ _  _  refl)  _  refl)

module _ {i j k} {A : Set i} {B : Set j} {C : Set k} where
  abstract
    comp-equiv : equiv A B  equiv B C  equiv A C
    comp-equiv (f , f-equiv) (g , g-equiv)
      =  a  g (f a))
      ,  c  is-contr-retract 
                { ((b , refl) , (a , refl))  _ , refl })
                { (a , refl)  (_ , refl) , (_ , refl) })
                { (a , refl)  refl })
               (is-contr-Σ (g-equiv c)  { (b , _)  f-equiv b })))

abstract
  is-equiv-lift :  {i j} {A : Set i}  is-equiv (lift {i} {j} {A})
  is-equiv-lift = iso→equiv _ lower  _  refl)  _  refl)

  is-equiv-lower :  {i j} {A : Set i}  is-equiv (lower {i} {j} {A})
  is-equiv-lower = iso→equiv _ lift  _  refl)  _  refl)

--------------------------------------------------------------------------------

data W {i j} (A : Set i) (B : A  Set j) : Set (i  j) where
  sup : (a : A) (f : B a  W A B)  W A B

module _ {i j k} {A : Set i} {B : A  Set j}
  (P : W A B  Set k) (sup' :  a f  (∀ b  P (f b))  P (sup a f))
  where

  W-elim :  x  P x
  W-elim (sup a f) = sup' a f  b  W-elim (f b))

module _ {i j k} (I : Set i) (A : I  Set j) (B :  {i}  A i  Set k) (child :  {i} {a}  B {i} a  I) where
  
  -- data IW₁ : I → Set where
  --   sup : ∀ i (a : A i) (f : (b : B a) → IW₁ (child b)) → IW₁ i

  IW₀ : Set (i  j  k)
  IW₀ = W (Σ I A) λ p  B (snd p)

  wf : IW₀  I  Set (i  k)
  wf (sup (i , a) f) j = Id i j × (∀ b  wf (f b) (child b))

  IW : I  Set (i  j  k)
  IW i = Σ IW₀ λ x  wf x i

  IW-sup :  i (a : A i) (f : (b : B a)  IW (child b))  IW i
  IW-sup i a f = (sup (i , a)  b  fst (f b)))
               , refl
               , λ b  snd (f b)

  IW-elim
    : (P :  i  IW i  Set)
     (∀ i a f  (∀ b  P _ (f b))  P i (IW-sup i a f))
      i x  P i x
  IW-elim P d i (sup a f , refl , g) = d i (a .snd)  b  f b , g b) λ b  IW-elim P d _ (f b , g b)

  IW-elim-β
    : (P :  i  IW i  Set)
     (d :  i a f  (∀ b  P _ (f b))  P i (IW-sup i a f))
      i a f  Id (IW-elim P d i (IW-sup i a f)) (d i a f λ b  IW-elim P d _ (f b))
  IW-elim-β P d i a f = refl

--------------------------------------------------------------------------------

is-prop :  {i}  Set i  Set i
is-prop A = A  is-contr A

abstract
  is-prop-all-paths :  {i} {A : Set i}  is-prop A  (x y : A)  Id x y
  is-prop-all-paths pA x y = is-contr-all-paths (pA x) x y

abstract
  is-prop-is-contr :  {i} {A : Set i}  is-prop (is-contr A)
  is-prop-is-contr {i} {A} cA .is-contr-center = cA
  is-prop-is-contr {i} {A} cA .is-contr-path cA'
    = ap  { (x , y)  record { is-contr-center = x ; is-contr-path = y } }) inner
    where
      inner : Id {A = Σ A λ a   b  Id a b} (cA .is-contr-center , cA .is-contr-path) (cA' .is-contr-center , cA' .is-contr-path)
      inner = pair-Id
              (is-contr-all-paths cA _ _)
              (lam-Id λ a  is-contr-all-paths (is-contr-Id cA _ _) _ _)

abstract
  is-prop-⊤ :  {i}  is-prop ( {i})
  is-prop-⊤ = λ _  is-contr-⊤

abstract
  is-prop-Σ :  {i j} {A : Set i} {B : A  Set j}  is-prop A  (∀ a  is-prop (B a))  is-prop (Σ A B)
  is-prop-Σ {A = A} {B = B} pA pB (a , b) = is-contr-Σ (pA a)  a  pB a (transp B (is-prop-all-paths pA _ _) b))

abstract
  is-prop-Π :  {i j} {A : Set i} {B : A  Set j}  (∀ a  is-prop (B a))  is-prop (∀ a  B a)
  is-prop-Π {A = A} {B = B} pB f = is-contr-Π  a  pB a (f a))