{-

Fibrancy of path and homotopy fiber types.

-}
module type-former.path where

open import basic
open import internal-extensional-type-theory
open import axiom
open import cofibration
open import fibration.fibration
open import fibration.transport
open import type-former.extension
open import type-former.pi
open import type-former.sigma

private variable
   ℓ' : Level
  Γ Δ : Type 

--↓ A path is a function from the interval shape with specified endpoints.

record _~_ {A : Type } (a₀ a₁ : A) : Type  where
  constructor path
  field
    at : 𝕀  A
    at0 : at 0  a₀
    at1 : at 1  a₁

open _~_ public

eqToPath : {A : Type } {a₀ a₁ : A}  a₀  a₁  a₀ ~ a₁
eqToPath {a₀ = a₀} eq = path (cst a₀) refl eq

--↓ The reflexive path is the constant function.

refl~ : {A : Type } (a : A)  a ~ a
refl~ a = eqToPath refl

congPath : {A : Type } {B : Type ℓ'} (f : A  B) {a₀ a₁ : A}
   a₀ ~ a₁  f a₀ ~ f a₁
congPath f p .at = f  p .at
congPath f p .at0 = cong f (p .at0)
congPath f p .at1 = cong f (p .at1)

--↓ An extensionality principle for paths.

PathExt : {A : Type } {a₀ a₁ : A} {p q : a₀ ~ a₁}
   (∀ i  p .at i  q .at i)  p  q
PathExt t =
  congΣ (uncurry  path) (funExt t) (×ext uip' uip')

--↓ Definition of the family underlying the path type.

Pathˣ : (A : Γ  Type ) (a₀ a₁ : Γ ⊢ˣ A)  Γ  Type 
Pathˣ A a₀ a₁ γ = a₀ γ ~ a₁ γ

congPathˣ : {A : Γ  Type } {B : Γ  Type ℓ'}
  (f : Γ ⊢ˣ A →ˣ B)
  {a₀ a₁ : Γ ⊢ˣ A} (p : Γ ⊢ˣ Pathˣ A a₀ a₁)
   Γ ⊢ˣ Pathˣ B (appˣ f a₀) (appˣ f a₁)
congPathˣ f p γ = congPath (f γ) (p γ)

------------------------------------------------------------------------------------------
-- Fibrancy of path types.
------------------------------------------------------------------------------------------

opaque
  private
    partialEl : {A : Γ  Type } (a₀ a₁ : Γ ⊢ˣ A)
       Γ ▷𝕀 ▷[   𝒒 ] ⊢ˣ (A  𝒑)  (  𝒒)
    partialEl a₀ a₁ =
      uncurry λ (γ , i)  ∂-rec i  _  a₀ γ)  _  a₁ γ)

    retract : {A : Γ  Type } {a₀ a₁ : Γ ⊢ˣ A}
       Γ ⊢ˣ Retractˣ (Pathˣ A a₀ a₁) (Extensionˣ 𝕚 (A  𝒑)  (partialEl a₀ a₁))
    retract γ .sec p i .out = p .at i
    retract γ .sec p i .out≡ =
      ∂-elim i  {refl  sym (p .at0)})  {refl  sym (p .at1)})
    retract γ .ret ex .at i = ex i .out
    retract γ .ret ex .at0 = sym (ex 0 .out≡ (∨l refl))
    retract γ .ret ex .at1 = sym (ex 1 .out≡ (∨r refl))
    retract γ .inv _ = PathExt λ _  refl

  PathFibStr : {A : Γ  Type } (α : FibStr A) (a₀ a₁ : Γ ⊢ˣ A)
     FibStr (Pathˣ A a₀ a₁)
  PathFibStr α a₀ a₁ =
    retractFibStr retract (ExtensionFibStr 𝕚 (α ∘ᶠˢ 𝒑)  _)

  --↓ The fibrancy structure is stable under reindexing.

  reindexPathFibStr : {A : Γ  Type } {α : FibStr A} {a₀ a₁ : Γ ⊢ˣ A}
    (ρ : Δ  Γ)
     PathFibStr α a₀ a₁ ∘ᶠˢ ρ  PathFibStr (α ∘ᶠˢ ρ) (a₀  ρ) (a₁  ρ)
  reindexPathFibStr ρ =
    reindexRetractFibStr retract ρ
    
    cong₂
      retractFibStr
      (funExt' $ retractExt (funExt' $ funExt' $ restrictExt refl) refl)
      (reindexExtensionFibStr ρ)

Pathᶠ : (A : Γ ⊢ᶠType ) (a₀ a₁ : Γ ⊢ᶠ A)  Γ ⊢ᶠType 
Pathᶠ A a₀ a₁ .fst = Pathˣ (A .fst) a₀ a₁
Pathᶠ A a₀ a₁ .snd = PathFibStr (A .snd) a₀ a₁

opaque
  reindexPathᶠ : {A : Γ ⊢ᶠType } {a₀ a₁ : Γ ⊢ᶠ A}
    (ρ : Δ  Γ)  Pathᶠ A a₀ a₁ ∘ᶠ ρ  Pathᶠ (A ∘ᶠ ρ) (a₀  ρ) (a₁  ρ)
  reindexPathᶠ ρ = Σext refl (reindexPathFibStr ρ)

reflᶠ : (A : Γ ⊢ᶠType ) (a : Γ ⊢ᶠ A)  Γ ⊢ᶠ Pathᶠ A a a
reflᶠ A = refl~ ∘_

------------------------------------------------------------------------------------------
-- Homotopy fiber type
------------------------------------------------------------------------------------------

Fiber : {A : Type } {B : Type ℓ'} (f : A  B) (b : B)  Type (  ℓ')
Fiber f b = Σ a  _ , f a ~ b

Fiberˣ : {A : Γ  Type } {B : Γ  Type ℓ'} (f : Γ ⊢ˣ A →ˣ B) (b : Γ ⊢ˣ B)
   (Γ  Type (  ℓ'))
Fiberˣ f b γ = Fiber (f γ) (b γ)

opaque
  FiberFibStr : {A : Γ  Type } (α : FibStr A) {B : Γ  Type ℓ'} (β : FibStr B)
    (f : Γ ⊢ˣ A →ˣ B) (b : Γ ⊢ˣ B)
     FibStr (Fiberˣ f b)
  FiberFibStr α β f b =
    ΣFibStr α (PathFibStr (β ∘ᶠˢ 𝒑) (uncurry f) (b  𝒑))

  reindexFiberFibStr : {A : Γ  Type } {α : FibStr A} {B : Γ  Type ℓ'} {β : FibStr B}
    {f : Γ ⊢ˣ A →ˣ B} {b : Γ ⊢ˣ B}
    (ρ : Δ  Γ)
     FiberFibStr α β f b ∘ᶠˢ ρ  FiberFibStr (α ∘ᶠˢ ρ) (β ∘ᶠˢ ρ) (f  ρ) (b  ρ)
  reindexFiberFibStr ρ =
    reindexΣFibStr _  cong (ΣFibStr _) (reindexPathFibStr _)

Fiberᶠ : (A : Γ ⊢ᶠType ) (B : Γ ⊢ᶠType ℓ') (f : Γ ⊢ᶠ A →ᶠ B) (b : Γ ⊢ᶠ B)
   Γ ⊢ᶠType (  ℓ')
Fiberᶠ A B f b .fst = Fiberˣ f b
Fiberᶠ A B f b .snd = FiberFibStr (A .snd) (B .snd) f b

module _ {A : Type } {B : Type ℓ'} {f : A  B} where

  opaque
    FiberExt : {b : B} {x y : Fiber f b}
       x .fst  y .fst  (∀ i  x .snd .at i  y .snd .at i)  x  y
    FiberExt refl p = Σext refl (PathExt p)

  opaque
    FiberExtDep : {b b' : B} (p : b  b') {x : Fiber f b} {y : Fiber f b'}
       x .fst  y .fst
       (∀ i  x .snd .at i  y .snd .at i)
       subst (Fiber f) p x  y
    FiberExtDep refl = FiberExt

  eqToFiber : {b : B} (a : A)  f a  b  Fiber f b
  eqToFiber a eq .fst = a
  eqToFiber a eq .snd = eqToPath eq

  opaque
    fiberPathEq : {b : B} {x y : Fiber f b}
       x  y   k  x .snd .at k  y .snd .at k
    fiberPathEq refl _ = refl

  opaque
    fiberPathEqDep : {b b' : B} (p : b  b')
      {x : Fiber f b} {y : Fiber f b'}
       subst (Fiber f) p x  y   k  x .snd .at k  y .snd .at k
    fiberPathEqDep refl refl _ = refl

  opaque
    fiberDomEqDep : {b b' : B} (p : b  b') {x : Fiber f b} {y : Fiber f b'}
       subst (Fiber f) p x  y  x .fst  y .fst
    fiberDomEqDep refl refl = refl

------------------------------------------------------------------------------------------
-- Singleton contractibility
------------------------------------------------------------------------------------------

Singl : (A : Type ) (a : A)  Type 
Singl A = Fiber id

Singlᶠ : (A : Γ ⊢ᶠType ) (a : Γ ⊢ᶠ A)  Γ ⊢ᶠType 
Singlᶠ A a = Fiberᶠ A A (λˣ 𝒒) a

singlCenter : {A : Type } (a : A)  Singl A a
singlCenter a = a , refl~ a

singlCenterᶠ : (A : Γ ⊢ᶠType ) (a : Γ ⊢ᶠ A)
   Γ ⊢ᶠ Singlᶠ A a
singlCenterᶠ A a =
  pairᶠ A (Pathᶠ (A ∘ᶠ 𝒑) 𝒒 (a  𝒑)) a (reflᶠ A a)

opaque
  singlContract : (A : 𝟙 ⊢ᶠType ) (a : A $ᶠ tt) (c : Singlᶠ A (cst a) $ᶠ tt)
     singlCenter a ~ c
  singlContract A a c = homotopy
    where
    box : (i : 𝕀)  OpenBox 𝕚 ( A   cst tt) 1
    box i .cof =  i
    box i .tube j = ∂-rec i  {refl  a})  {refl  c .snd .at j})
    box i .cap .out = a
    box i .cap .out≡ = ∂-elim i  {refl  refl})  {refl  c .snd .at1})

    square : (i : 𝕀)  Filler (box i)
    square i = A .snd .lift 𝕚 _ 1 (box i)

    homotopy : (a , refl~ a) ~ c
    homotopy .at i .fst = square i .fill 0 .out
    homotopy .at i .snd = path  j  square i .fill j .out) refl (square i .cap≡)
    homotopy .at0 =
      FiberExt
        (sym (square 0 .fill 0 .out≡ (∨l refl)))
         j  sym (square 0 .fill j .out≡ (∨l refl)))
    homotopy .at1 =
      FiberExt
        (sym (square 1 .fill 0 .out≡ (∨r refl))  c .snd .at0)
         j  sym (square 1 .fill j .out≡ (∨r refl)))

singlContractᶠ : (A : Γ ⊢ᶠType ) (a : Γ ⊢ᶠ A) (c : Γ ⊢ᶠ Singlᶠ A a)
   Γ ⊢ᶠ Pathᶠ (Singlᶠ A a) (singlCenterᶠ A a) c
singlContractᶠ A a c γ = singlContract (A ∘ᶠ cst γ) (a γ) (c γ)

------------------------------------------------------------------------------------------
-- Transport along paths.
------------------------------------------------------------------------------------------

substᶠ : (A : Γ ⊢ᶠType ) (B : Γ ▷ᶠ A ⊢ᶠType ℓ') {a₀ a₁ : Γ ⊢ᶠ A}
  (p : Γ ⊢ᶠ Pathᶠ A a₀ a₁)
   Γ ⊢ᶠ B ∘ᶠ (id ,, a₀)
   Γ ⊢ᶠ B ∘ᶠ (id ,, a₁)
substᶠ A B p b₀ γ =
  subst ( B   (γ ,_)) (p γ .at1)
    (fibTranspStr (B ∘ᶠ (cst γ ,, p γ .at)) .lift 𝕚 id
      0
      (subst ( B   (γ ,_)) (sym (p γ .at0)) (b₀ γ))
      1)

------------------------------------------------------------------------------------------
-- Weak Paulin-Mohring-style J eliminator, stated in a somewhat unorthodox form using
-- singletons for ease of proof.
------------------------------------------------------------------------------------------

Jᶠ : (A : Γ ⊢ᶠType ) (a : Γ ⊢ᶠ A)
  (P : Γ ▷ᶠ Singlᶠ A a ⊢ᶠType ℓ')
  (d : Γ ⊢ᶠ P ∘ᶠ (id ,, singlCenterᶠ A a))
  (c : Γ ⊢ᶠ Singlᶠ A a)
   Γ ⊢ᶠ P ∘ᶠ (id ,, c)
Jᶠ A a P d c =
  substᶠ (Singlᶠ A a) P (singlContractᶠ A a c) d