{-

Basic operations on and properties of cofibrations.

-}
module cofibration where

open import basic
open import internal-extensional-type-theory
open import axiom.funext
open import axiom.shape

open import axiom.cofibration public

infix 1 _[_↦_]
infixl 3 _▷[_]
infixr 4 _∨ˣ_

private variable
   ℓ' : Level
  Γ Δ : Type 

------------------------------------------------------------------------------------------
-- Abbreviations.
------------------------------------------------------------------------------------------

--↓ Cofibration for the boundary of the interval shape.

 : 𝕀  Cof
 i = 𝕚  i  0  𝕚  i  1

--↓ Context extension by a cofibration.

_▷[_] : (Γ : Type ) (φ : Γ  Cof)  Type 
Γ ▷[ φ ] = Σ γ  Γ , [ φ γ ]

--↓ Restriction along a cofibration.

_↾_ : {A : Γ  Type } (a : Γ ⊢ˣ A) (φ : Γ  Cof)  Γ ▷[ φ ] ⊢ˣ A  𝒑
(a  φ) = a  𝒑

--↓ Operations on cofibrations in context.

[_]ˣ : (Γ  Cof)  (Γ  Type)
[ φ  γ = [ φ γ ]

_∋_≈ˣ_ : (S : Shape)  (Γ   S )  (Γ   S )  (Γ  Cof)
(S  r ≈ˣ s) γ = S  r γ  s γ

_∨ˣ_ : (φ ψ : Γ  Cof)  (Γ  Cof)
(φ ∨ˣ ψ) γ = φ γ  ψ γ

--↓ Version of cofIsStrictProp with implicit arguments.

cofIsStrictProp' : (φ : Cof) {u v : [ φ ]}  u  v
cofIsStrictProp' φ = cofIsStrictProp φ _ _

------------------------------------------------------------------------------------------
-- Partial elements.
------------------------------------------------------------------------------------------

--↓ Type of partial elements in a type, that is elements defined when some cofibration
--↓ holds.

_⁺ : Type   Type 
A  = Σ φ  Cof , ([ φ ]  A)

------------------------------------------------------------------------------------------
-- Restricted types.
------------------------------------------------------------------------------------------

--↓ A [ φ ↦ a ] is the type of elements of A which are equal to a whenever [ φ ] holds.

record _[_↦_] (A : Type ) (φ : Cof) (a : [ φ ]  A) : Type  where
  constructor makeRestrict
  field
    out : A
    out≡ :  u  a u  out

open _[_↦_] public

--↓ Applying a function to a restricted type.

mapRestrict : {A : Type } {B : Type ℓ'} (f : A  B) {φ : Cof} {a : [ φ ]  A}
   A [ φ  a ]  B [ φ  f  a ]
mapRestrict f z .out = f (z .out)
mapRestrict f z .out≡ u = cong f (z .out≡ u)

--↓ Extensionality principle for restricted types.

restrictExt : {A : Type } {φ : Cof} {a : [ φ ]  A}
  {z z' : A [ φ  a ]}
   z .out  z' .out
   z  z'
restrictExt refl = cong (makeRestrict _) (funExt' uip')

--↓ Forget part of a restriction.

narrow : {φ ψ : Cof} {A : Type } {a : [ φ ]  A}
   A [ φ  a ]  (f : [ ψ ]  [ φ ])  A [ ψ  a  f ]
narrow b f .out = b .out
narrow b f .out≡ u = b .out≡ (f u)

------------------------------------------------------------------------------------------
-- Combining compatible partial functions.
------------------------------------------------------------------------------------------

--↓ Derived non-dependent elimination principle for a union of cofibrations.

∨-rec : {φ ψ : Cof} {A : Type }
  (f : [ φ ]  A)
  (g : [ ψ ]  A)
  (p :  u v  f u  g v)
   [ φ  ψ ]  A
∨-rec f g p =
  ∨-elim f g  u v  substConst _ _  p u v)

--↓ Functoriality of union of cofibrations.

_∨`_ : {φ ψ φ' ψ' : Cof}
  (f : [ φ ]  [ φ' ])
  (g : [ ψ ]  [ ψ' ])
   [ φ  ψ ]  [ φ'  ψ' ]
f ∨` g =
  ∨-rec (∨l  f) (∨r  g)  _ _  cofIsStrictProp' (_  _))

--↓ Derived dependent elimination principle for a union of cofibrations into a family of
--↓ equalities.

opaque
  ∨-elimEq : {φ ψ : Cof} {A : [ φ  ψ ]  Type }
    {f g : (uv : [ φ  ψ ])  A uv}
     ((u : [ φ ])  f (∨l u)  g (∨l u))
     ((v : [ ψ ])  f (∨r v)  g (∨r v))
     (w : [ φ  ψ ])  f w  g w
  ∨-elimEq f g =
    ∨-elim f g  _ _  uip')

--↓ Non-dependent and dependent elimination principles for the interval boundary
--↓ cofibration.

∂-rec : (r : 𝕀) {A : Type }
   ([ 𝕚  r  0 ]  A)  ([ 𝕚  r  1 ]  A)  [  r ]  A
∂-rec r f g =
  ∨-rec f g  u v  0≠1 (sym u  v))

∂-elim : (r : 𝕀) {A : [  r ]  Type }
   ((rO : [ 𝕚  r  0 ])  A (∨l rO))
   ((rI : [ 𝕚  r  1 ])  A (∨r rI))
   (r∂ : [  r ])  A r∂
∂-elim r f g =
  ∨-elim f g  u v  0≠1 (sym u  v))

--↓ To check that a map [ φ ∨ φ₀ ] → A and a map [ φ ∨ φ₁ ] → A agree on their
--↓ intersection, it suffices to check that they agree on [ φ ] and on the intersection of
--↓ [ φ₀ ] and [ φ₁ ].

opaque
  takeOutCof : {A : Type } (φ φ₀ φ₁ : Cof)
    {f₀ : [ φ  φ₀ ]  A} {f₁ : [ φ  φ₁ ]  A}
     (∀ u  f₀ (∨l u)  f₁ (∨l u))
     (∀ v₀ v₁  f₀ (∨r v₀)  f₁ (∨r v₁))
     (∀ uv₀ uv₁  f₀ uv₀  f₁ uv₁)
  takeOutCof φ φ₀ φ₁ {f₀} {f₁} p q =
    ∨-elim
       u₀  ∨-elimEq
         u₁  cong f₀ (cofIsStrictProp' (φ  φ₀))  p u₁)
         v₁  p u₀  cong f₁ (cofIsStrictProp' (φ  φ₁))))
       v₀  ∨-elimEq
         u₁  cong f₀ (cofIsStrictProp' (φ  φ₀))  p u₁)
         v₁  q v₀ v₁))
       _ _  funExt' uip')

--↓ Substitution for inhabitants of a cofibration.

substCofEl : (φ : Cof) {P : [ φ ]  Type } {u : [ φ ]}  P u   v  P v
substCofEl φ {P} p v = subst P (cofIsStrictProp' φ) p

--↓ To check a property indexed by two elements of a cofibration, it suffices to check
--↓ the diagonal.

diagonalCofElim : (φ : Cof) {P : [ φ ]  [ φ ]  Type }
   (∀ u  P u u)  (∀ u v  P u v)
diagonalCofElim φ f = substCofEl φ  f