{-

Basic theory about h-levels/n-types:

- Basic properties of isContr, isProp and isSet (definitions are in Prelude)

- Hedberg's theorem can be found in Cubical/Relation/Nullary/DecidableEq

-}
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Foundations.HLevels where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Structure
open import Cubical.Functions.FunExtEquiv
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Path
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Univalence using (ua ; univalence)

open import Cubical.Data.Sigma
open import Cubical.Data.Nat   using (; zero; suc; _+_; +-zero; +-comm)

HLevel : Type₀
HLevel = 

private
  variable
     ℓ' ℓ'' ℓ''' : Level
    A : Type 
    B : A  Type 
    C : (x : A)  B x  Type 
    D : (x : A) (y : B x)  C x y  Type 
    E : (x : A) (y : B x)  (z : C x y)  D x y z  Type 
    x y : A
    n : HLevel

isOfHLevel : HLevel  Type   Type 
isOfHLevel 0 A = isContr A
isOfHLevel 1 A = isProp A
isOfHLevel (suc (suc n)) A = (x y : A)  isOfHLevel (suc n) (x  y)

isOfHLevelFun : (n : HLevel) {A : Type } {B : Type ℓ'} (f : A  B)  Type (ℓ-max  ℓ')
isOfHLevelFun n f =  b  isOfHLevel n (fiber f b)

TypeOfHLevel :    HLevel  Type (ℓ-suc )
TypeOfHLevel  n = TypeWithStr  (isOfHLevel n)

hProp hSet hGroupoid h2Groupoid :    Type (ℓ-suc )
hProp       = TypeOfHLevel  1
hSet        = TypeOfHLevel  2
hGroupoid   = TypeOfHLevel  3
h2Groupoid  = TypeOfHLevel  4

-- lower h-levels imply higher h-levels

isOfHLevelSuc : (n : HLevel)  isOfHLevel n A  isOfHLevel (suc n) A
isOfHLevelSuc 0 = isContr→isProp
isOfHLevelSuc 1 = isProp→isSet
isOfHLevelSuc (suc (suc n)) h a b = isOfHLevelSuc (suc n) (h a b)

isSet→isGroupoid : isSet A  isGroupoid A
isSet→isGroupoid = isOfHLevelSuc 2

isGroupoid→is2Groupoid : isGroupoid A  is2Groupoid A
isGroupoid→is2Groupoid = isOfHLevelSuc 3

isOfHLevelPlus : (m : HLevel)  isOfHLevel n A  isOfHLevel (m + n) A
isOfHLevelPlus zero hA = hA
isOfHLevelPlus (suc m) hA = isOfHLevelSuc _ (isOfHLevelPlus m hA)

isContr→isOfHLevel : (n : HLevel)  isContr A  isOfHLevel n A
isContr→isOfHLevel {A = A} n cA = subst  m  isOfHLevel m A) (+-zero n) (isOfHLevelPlus n cA)

isProp→isOfHLevelSuc : (n : HLevel)  isProp A  isOfHLevel (suc n) A
isProp→isOfHLevelSuc {A = A} n pA = subst  m  isOfHLevel m A) (+-comm n 1) (isOfHLevelPlus n pA)

-- hlevel of path and dependent path types

isProp→isContrPath : isProp A  (x y : A)  isContr (x  y)
isProp→isContrPath h x y = h x y , isProp→isSet h x y _

isContr→isContrPath : isContr A  (x y : A)  isContr (x  y)
isContr→isContrPath cA = isProp→isContrPath (isContr→isProp cA)

isOfHLevelPath' : (n : HLevel)  isOfHLevel (suc n) A  (x y : A)  isOfHLevel n (x  y)
isOfHLevelPath' 0 = isProp→isContrPath
isOfHLevelPath' (suc n) h x y = h x y

isOfHLevelPath'⁻ : (n : HLevel)  ((x y : A)  isOfHLevel n (x  y))  isOfHLevel (suc n) A
isOfHLevelPath'⁻ zero h x y = h x y .fst
isOfHLevelPath'⁻ (suc n) h = h

isOfHLevelPath : (n : HLevel)  isOfHLevel n A  (x y : A)  isOfHLevel n (x  y)
isOfHLevelPath 0 h x y = isContr→isContrPath h x y
isOfHLevelPath (suc n) h x y = isOfHLevelSuc n (isOfHLevelPath' n h x y)

isOfHLevelPathP' : {A : I  Type } (n : HLevel)
                    isOfHLevel (suc n) (A i1)
                    (x : A i0) (y : A i1)  isOfHLevel n (PathP A x y)
isOfHLevelPathP' {A = A} n h x y = transport⁻  i  isOfHLevel n (PathP≡Path A x y i))
                                              (isOfHLevelPath' n h _ _)

isOfHLevelPathP : {A : I  Type } (n : HLevel)
                   isOfHLevel n (A i1)
                   (x : A i0) (y : A i1)  isOfHLevel n (PathP A x y)
isOfHLevelPathP {A = A} n h x y = transport⁻  i  isOfHLevel n (PathP≡Path A x y i))
                                             (isOfHLevelPath n h _ _)

isProp→isContrPathP : {A : I  Type }  (∀ i  isProp (A i))
                                        (x : A i0) (y : A i1)  isContr (PathP A x y)
isProp→isContrPathP h x y = isProp→PathP h x y , isOfHLevelPathP 1 (h i1) x y _

-- h-level of isOfHLevel

isPropIsOfHLevel : (n : HLevel)  isProp (isOfHLevel n A)
isPropIsOfHLevel 0 = isPropIsContr
isPropIsOfHLevel 1 = isPropIsProp
isPropIsOfHLevel (suc (suc n)) f g i a b =
  isPropIsOfHLevel (suc n) (f a b) (g a b) i

isPropIsSet : isProp (isSet A)
isPropIsSet = isPropIsOfHLevel 2

isPropIsGroupoid : isProp (isGroupoid A)
isPropIsGroupoid = isPropIsOfHLevel 3

isPropIs2Groupoid : isProp (is2Groupoid A)
isPropIs2Groupoid = isPropIsOfHLevel 4

-- Fillers for cubes from h-level

isSet→isSet' : isSet A  isSet' A
isSet→isSet' {A = A} Aset a₀₋ a₁₋ a₋₀ a₋₁ =
  transport⁻ (PathP≡Path  i  a₋₀ i  a₋₁ i) a₀₋ a₁₋) (Aset _ _ _ _)

isSet'→isSet : isSet' A  isSet A
isSet'→isSet {A = A} Aset' x y p q = Aset' p q refl refl

isGroupoid→isGroupoid' : isGroupoid A  isGroupoid' A
isGroupoid→isGroupoid' {A = A} Agpd a₀₋₋ a₁₋₋ a₋₀₋ a₋₁₋ a₋₋₀ a₋₋₁ =
  transport⁻ (PathP≡Path  i  Square (a₋₀₋ i) (a₋₁₋ i) (a₋₋₀ i) (a₋₋₁ i)) a₀₋₋ a₁₋₋)
    (isGroupoid→isPropSquare _ _ _ _ _ _)
  where
  isGroupoid→isPropSquare :
    {a₀₀ a₀₁ : A} (a₀₋ : a₀₀  a₀₁)
    {a₁₀ a₁₁ : A} (a₁₋ : a₁₀  a₁₁)
    (a₋₀ : a₀₀  a₁₀) (a₋₁ : a₀₁  a₁₁)
     isProp (Square a₀₋ a₁₋ a₋₀ a₋₁)
  isGroupoid→isPropSquare a₀₋ a₁₋ a₋₀ a₋₁ =
    transport⁻
      (cong isProp (PathP≡Path  i  a₋₀ i  a₋₁ i) a₀₋ a₁₋))
      (Agpd _ _ _ _)

isGroupoid'→isGroupoid : isGroupoid' A  isGroupoid A
isGroupoid'→isGroupoid Agpd' x y p q r s = Agpd' r s refl refl refl refl

-- hlevels are preserved by retracts (and consequently equivalences)

isContrRetract
  :  {B : Type }
   (f : A  B) (g : B  A)
   (h : retract f g)
   (v : isContr B)  isContr A
isContrRetract f g h (b , p) = (g b , λ x  (cong g (p (f x)))  (h x))

isPropRetract
  : {B : Type }
  (f : A  B) (g : B  A)
  (h : (x : A)  g (f x)  x)
   isProp B  isProp A
isPropRetract f g h p x y i =
  hcomp
     j  λ
      { (i = i0)  h x j
      ; (i = i1)  h y j})
    (g (p (f x) (f y) i))

isOfHLevelRetract
  : (n : HLevel) {B : Type }
  (f : A  B) (g : B  A)
  (h : (x : A)  g (f x)  x)
   isOfHLevel n B  isOfHLevel n A
isOfHLevelRetract 0 = isContrRetract
isOfHLevelRetract 1 = isPropRetract
isOfHLevelRetract (suc (suc n)) f g h ofLevel x y =
  isOfHLevelRetract (suc n)
    (cong f)
     q i 
      hcomp
         j  λ
          { (i = i0)  h x j
          ; (i = i1)  h y j})
        (g (q i)))
     p k i 
      hcomp
         j  λ
          { (i = i0)  h x (j  k)
          ; (i = i1)  h y (j  k)
          ; (k = i1)  p i})
        (h (p i) k))
    (ofLevel (f x) (f y))

isOfHLevelRespectEquiv : {A : Type } {B : Type ℓ'}  (n : HLevel)  A  B  isOfHLevel n A  isOfHLevel n B
isOfHLevelRespectEquiv n eq = isOfHLevelRetract n (invEq eq) (eq .fst) (retEq eq)

-- h-level of Σ-types

isContrΣ : isContr A  ((x : A)  isContr (B x))  isContr (Σ A B)
isContrΣ {A = A} {B = B} (a , p) q =
  let h : (x : A) (y : B x)  (q x) .fst  y
      h x y = (q x) .snd y
  in (( a , q a .fst)
     , ( λ x i  p (x .fst) i
       , h (p (x .fst) i) (transp  j  B (p (x .fst) (i  ~ j))) i (x .snd)) i))

isContrΣ′ : (ca : isContr A)  isContr (B (fst ca))  isContr (Σ A B)
isContrΣ′ ca cb = isContrΣ ca  x  subst _ (snd ca x) cb)

Σ≡Prop-equiv
  : (pB : (x : A)  isProp (B x)) {u v : Σ[ a  A ] B a}
   isEquiv (Σ≡Prop pB {u} {v})
Σ≡Prop-equiv {A = A} pB {u} {v} = isoToIsEquiv (iso (Σ≡Prop pB) (cong fst) sq  _  refl))
  where sq : (p : u  v)  Σ≡Prop pB (cong fst p)  p
        sq p j i = (p i .fst) , isProp→PathP  i  isOfHLevelPath 1 (pB (fst (p i)))
                                                       (Σ≡Prop pB {u} {v} (cong fst p) i .snd)
                                                       (p i .snd) )
                                              refl refl i j

isPropΣ : isProp A  ((x : A)  isProp (B x))  isProp (Σ A B)
isPropΣ pA pB t u = Σ≡Prop pB (pA (t .fst) (u .fst))

isOfHLevelΣ :  n  isOfHLevel n A  ((x : A)  isOfHLevel n (B x))
                   isOfHLevel n (Σ A B)
isOfHLevelΣ 0 = isContrΣ
isOfHLevelΣ 1 = isPropΣ
isOfHLevelΣ {B = B} (suc (suc n)) h1 h2 x y =
  let h3 : isOfHLevel (suc n) (ΣPathTransport x y)
      h3 = isOfHLevelΣ (suc n) (h1 (fst x) (fst y)) λ p  h2 (p i1)
                       (subst B p (snd x)) (snd y)
  in transport  i  isOfHLevel (suc n) (ΣPathTransport≡PathΣ x y i)) h3

isSetΣ : isSet A  ((x : A)  isSet (B x))  isSet (Σ A B)
isSetΣ = isOfHLevelΣ 2

isGroupoidΣ : isGroupoid A  ((x : A)  isGroupoid (B x))  isGroupoid (Σ A B)
isGroupoidΣ = isOfHLevelΣ 3

is2GroupoidΣ : is2Groupoid A  ((x : A)  is2Groupoid (B x))  is2Groupoid (Σ A B)
is2GroupoidΣ = isOfHLevelΣ 4

-- h-level of ×

isProp× : {A : Type } {B : Type ℓ'}  isProp A  isProp B  isProp (A × B)
isProp× pA pB = isPropΣ pA  _  pB)

isProp×2 : {A : Type } {B : Type ℓ'} {C : Type ℓ''}
          isProp A  isProp B  isProp C  isProp (A × B × C)
isProp×2 pA pB pC = isProp× pA (isProp× pB pC)

isProp×3 : {A : Type } {B : Type ℓ'} {C : Type ℓ''} {D : Type ℓ'''}
          isProp A  isProp B  isProp C  isProp D  isProp (A × B × C × D)
isProp×3 pA pB pC pD = isProp×2 pA pB (isProp× pC pD)

isOfHLevel× :  {A : Type } {B : Type ℓ'} n  isOfHLevel n A  isOfHLevel n B
                                              isOfHLevel n (A × B)
isOfHLevel× n hA hB = isOfHLevelΣ n hA  _  hB)

isSet× :  {A : Type } {B : Type ℓ'}  isSet A  isSet B  isSet (A × B)
isSet× = isOfHLevel× 2

isGroupoid× :  {A : Type } {B : Type ℓ'}  isGroupoid A  isGroupoid B
                                            isGroupoid (A × B)
isGroupoid× = isOfHLevel× 3

is2Groupoid× :  {A : Type } {B : Type ℓ'}  is2Groupoid A  is2Groupoid B
                                             is2Groupoid (A × B)
is2Groupoid× = isOfHLevel× 4

-- h-level of Π-types

isOfHLevelΠ :  n  ((x : A)  isOfHLevel n (B x))
                   isOfHLevel n ((x : A)  B x)
isOfHLevelΠ 0 h =  x  fst (h x)) , λ f i y  snd (h y) (f y) i
isOfHLevelΠ 1 h f g i x = (h x) (f x) (g x) i
isOfHLevelΠ (suc (suc n)) h f g =
  subst (isOfHLevel (suc n)) funExtPath (isOfHLevelΠ (suc n) λ x  h x (f x) (g x))

isPropΠ : (h : (x : A)  isProp (B x))  isProp ((x : A)  B x)
isPropΠ = isOfHLevelΠ 1

isPropΠ2 : (h : (x : A) (y : B x)  isProp (C x y))
          isProp ((x : A) (y : B x)  C x y)
isPropΠ2 h = isPropΠ λ x  isPropΠ λ y  h x y

isPropΠ3 : (h : (x : A) (y : B x) (z : C x y)  isProp (D x y z))
          isProp ((x : A) (y : B x) (z : C x y)  D x y z)
isPropΠ3 h = isPropΠ λ x  isPropΠ λ y  isPropΠ λ z  h x y z

isPropΠ4 : (h : (x : A) (y : B x) (z : C x y) (w : D x y z)  isProp (E x y z w))
             isProp ((x : A) (y : B x) (z : C x y) (w : D x y z)  E x y z w)
isPropΠ4 h = isPropΠ λ _  isPropΠ3 λ _  h _ _

isPropImplicitΠ : (h : (x : A)  isProp (B x))  isProp ({x : A}  B x)
isPropImplicitΠ h f g i {x} = h x (f {x}) (g {x}) i

isProp→ : {A : Type } {B : Type ℓ'}  isProp B  isProp (A  B)
isProp→ pB = isPropΠ λ _  pB

isSetΠ : ((x : A)  isSet (B x))  isSet ((x : A)  B x)
isSetΠ = isOfHLevelΠ 2

isSetΠ2 : (h : (x : A) (y : B x)  isSet (C x y))
         isSet ((x : A) (y : B x)  C x y)
isSetΠ2 h = isSetΠ λ x  isSetΠ λ y  h x y

isGroupoidΠ : ((x : A)  isGroupoid (B x))  isGroupoid ((x : A)  B x)
isGroupoidΠ = isOfHLevelΠ 3

isGroupoidΠ2 : (h : (x : A) (y : B x)  isGroupoid (C x y))  isGroupoid ((x : A) (y : B x)  C x y)
isGroupoidΠ2 h = isGroupoidΠ λ _  isGroupoidΠ λ _  h _ _

isGroupoidΠ3 : (h : (x : A) (y : B x) (z : C x y)  isGroupoid (D x y z))
             isGroupoid ((x : A) (y : B x) (z : C x y)  D x y z)
isGroupoidΠ3 h = isGroupoidΠ λ _  isGroupoidΠ2 λ _  h _ _

isGroupoidΠ4 : (h : (x : A) (y : B x) (z : C x y) (w : D x y z)  isGroupoid (E x y z w))
             isGroupoid ((x : A) (y : B x) (z : C x y) (w : D x y z)  E x y z w)
isGroupoidΠ4 h = isGroupoidΠ λ _  isGroupoidΠ3 λ _  h _ _

is2GroupoidΠ : ((x : A)  is2Groupoid (B x))  is2Groupoid ((x : A)  B x)
is2GroupoidΠ = isOfHLevelΠ 4

isOfHLevelΠ⁻ :  {A : Type } {B : Type ℓ'} n
              isOfHLevel n (A  B)  (A  isOfHLevel n B)
isOfHLevelΠ⁻ 0 h x = fst h x , λ y  funExt⁻ (snd h (const y)) x
isOfHLevelΠ⁻ 1 h x y z = funExt⁻ (h (const y) (const z)) x
isOfHLevelΠ⁻ (suc (suc n)) h x y z =
  isOfHLevelΠ⁻ (suc n) (subst (isOfHLevel (suc n)) (sym funExtPath) (h (const y) (const z))) x

-- h-level of A ≃ B and A ≡ B

isOfHLevel≃ :  n  {A B : Type } (hA : isOfHLevel n A) (hB : isOfHLevel n B)  isOfHLevel n (A  B)
isOfHLevel≃ zero {A = A} {B = B} hA hB = A≃B , contr
  where
  A≃B : A  B
  A≃B = isoToEquiv (iso  _  fst hB)  _  fst hA) (snd hB ) (snd hA))

  contr : (y : A  B)  A≃B  y
  contr y = Σ≡Prop isPropIsEquiv (funExt  a  snd hB (fst y a)))

isOfHLevel≃ (suc n) hA hB =
  isOfHLevelΣ (suc n) (isOfHLevelΠ (suc n)  _  hB))
               a  subst  n  isOfHLevel n (isEquiv a)) (+-comm n 1) (isOfHLevelPlus n (isPropIsEquiv a)))

isOfHLevel≡ :  n  {A B : Type } (hA : isOfHLevel n A) (hB : isOfHLevel n B) 
  isOfHLevel n (A  B)
isOfHLevel≡ n hA hB = isOfHLevelRespectEquiv n (invEquiv univalence) (isOfHLevel≃ n hA hB)

-- h-level of TypeOfHLevel

isPropHContr : isProp (TypeOfHLevel  0)
isPropHContr x y = Σ≡Prop  _  isPropIsContr) (isOfHLevel≡ 0 (x .snd) (y .snd) .fst)

isOfHLevelTypeOfHLevel :  n  isOfHLevel (suc n) (TypeOfHLevel  n)
isOfHLevelTypeOfHLevel 0 = isPropHContr
isOfHLevelTypeOfHLevel (suc n) x y = subst (isOfHLevel (suc n)) eq (isOfHLevel≡ (suc n) (snd x) (snd y))
  where eq :  {A B : Type } {hA : isOfHLevel (suc n) A} {hB : isOfHLevel (suc n) B}
              (A  B)  ((A , hA)  (B , hB))
        eq = ua (_ , Σ≡Prop-equiv  _  isPropIsOfHLevel (suc n)))

isSetHProp : isSet (hProp )
isSetHProp = isOfHLevelTypeOfHLevel 1

-- h-level of lifted type

isOfHLevelLift :  { ℓ'} (n : HLevel) {A : Type }  isOfHLevel n A  isOfHLevel n (Lift {j = ℓ'} A)
isOfHLevelLift n = isOfHLevelRetract n lower lift λ _  refl

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

-- More consequences of isProp and isContr

inhProp→isContr : A  isProp A  isContr A
inhProp→isContr x h = x , h x

extend : isContr A  (∀ φ  (u : Partial φ A)  Sub A φ u)
extend (x , p) φ u = inS (hcomp  { j (φ = i1)  p (u 1=1) j }) x)

isContrPartial→isContr :  {} {A : Type }
                        (extend :  φ  Partial φ A  A)
                        (∀ u  u  (extend i1 λ { _  u}))
                        isContr A
isContrPartial→isContr {A = A} extend law
  = ex , λ y  law ex   i  Aux.v y i)  sym (law y)
    where ex = extend i0 empty
          module Aux (y : A) (i : I) where
            φ = ~ i  i
            u : Partial φ A
            u = λ { (i = i0)  ex ; (i = i1)  y }
            v = extend φ u

-- Dependent h-level over a type

isOfHLevelDep : HLevel  {A : Type } (B : A  Type ℓ')  Type (ℓ-max  ℓ')
isOfHLevelDep 0 {A = A} B = {a : A}  Σ[ b  B a ] ({a' : A} (b' : B a') (p : a  a')  PathP  i  B (p i)) b b')
isOfHLevelDep 1 {A = A} B = {a0 a1 : A} (b0 : B a0) (b1 : B a1) (p : a0  a1)   PathP  i  B (p i)) b0 b1
isOfHLevelDep (suc (suc  n)) {A = A} B = {a0 a1 : A} (b0 : B a0) (b1 : B a1)  isOfHLevelDep (suc n) {A = a0  a1}  p  PathP  i  B (p i)) b0 b1)

isOfHLevel→isOfHLevelDep : (n : HLevel)
   {A : Type } {B : A  Type ℓ'} (h : (a : A)  isOfHLevel n (B a))  isOfHLevelDep n {A = A} B
isOfHLevel→isOfHLevelDep 0 h {a} =
  (h a .fst , λ b' p  isProp→PathP  i  isContr→isProp (h (p i))) (h a .fst) b')
isOfHLevel→isOfHLevelDep 1 h = λ b0 b1 p  isProp→PathP  i  h (p i)) b0 b1
isOfHLevel→isOfHLevelDep (suc (suc n)) {A = A} {B} h {a0} {a1} b0 b1 =
  isOfHLevel→isOfHLevelDep (suc n)  p  helper a1 p b1)
  where
  helper : (a1 : A) (p : a0  a1) (b1 : B a1) 
    isOfHLevel (suc n) (PathP  i  B (p i)) b0 b1)
  helper a1 p b1 = J
                      a1 p   b1  isOfHLevel (suc n) (PathP  i  B (p i)) b0 b1))
                      _  h _ _ _) p b1