{-# OPTIONS --without-K --exact-split --two-level #-}

module 2LTT_C.Types.Id_Type where

open import Agda.Builtin.Equality public

open import 2LTT_C.Exotypes.Exo_Equality
open import 2LTT_C.Types.Functions
open import 2LTT_C.Types.Sigma
open import 2LTT_C.Types.Pi
open import 2LTT_C.Primitive

--identity type for (fibrant) types
Id : {i : Level} {A : UU i}  A  A  UU i
Id {i} {A} = _≡_ {i} {A}

--exo-equality for types implies the identity type. The converse is not valid!
=ᵉ-to-Id : {i : Level} {A : UU i} {a b : A}  (c a) =ᵉ (c b)  Id a b
=ᵉ-to-Id reflᵉ = refl

--identity obtained by coerced equality
cic-eq : {i : Level} {A : UU i} {x y : C A}  x =ᵉ y  Id (ic x) (ic y)
cic-eq reflᵉ = refl

--induction principle for the identity type
ind-Id : {i j : Level} {A : UU i} (D : (x y : A)  Id {i} x y  UU j)
          ((x : A)  D x x refl)  ((x y : A)  (p : Id {i} x y)  D x y p )
ind-Id D f x .x refl = f x

--In keeping with Martin-Löf own notation we call this 𝕁
𝕁 =  ind-Id

infix 20 _·_
-- notation for concatenation · is "\ cdot" 
_·_ : {i : Level} {A : UU i} {x y z : A}  Id x y  Id y z  Id x z
refl · q = q

_≡⟨_⟩_ : {i : Level} {A : UU i} (x : A) {y z : A}  Id x y  Id y z  Id x z
x ≡⟨ p  q = p · q

_▮ : {i : Level} {A : UU i} (x : A)  Id x x
x  = refl

infixr 30 _⁻¹
_⁻¹ : {i : Level} {A : UU i} {x y : A}  Id x y  Id y x
refl ⁻¹ = refl

double-inv : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id ((p ⁻¹) ⁻¹) p
double-inv refl = refl

--properties of inverse path
left-inv : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id (p ⁻¹ · p) refl
left-inv refl = refl

right-inv : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id (p · p ⁻¹) refl
right-inv refl = refl

left-unit : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id (refl · p) p
left-unit refl = refl

right-unit : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id (p · refl) p
right-unit refl = refl

idempotency : {i : Level} {A : UU i} {x y : A}  (p : Id x y)  Id ((p ⁻¹) ⁻¹) p
idempotency refl = refl

--associativity
assoc : {i : Level} {A : UU i} {x y z w : A} (p : Id {i} x y) (q : Id {i} y z) (r : Id {i} z w)
         Id ((p · q) · r) (p · (q · r))
assoc refl q r = refl

--Horizontal composition and Whiskering

infixr 30 _◾ᵣ_ _◾ₗ_ _⋆_
--right whiskering
_◾ᵣ_ : {i : Level} {A : UU i} {a b c : A} {p q : Id {i} a b}
       (α : Id  p q) (r : Id b c)  Id (p · r) (q · r)
α ◾ᵣ refl = (right-unit _) · (α · (right-unit _) ⁻¹)

--left whiskering
_◾ₗ_ : {i : Level} {A : UU i} {a b c : A} {r s : Id {i} b c}
       (q : Id a b) (β : Id r s)  Id (q · r) (q · s)
refl ◾ₗ β = (left-unit _) · (β · (left-unit _) ⁻¹)

--horizontal composition
_⋆_ : {i : Level} {A : UU i} {a b c : A} {p q : Id {i} a b} {r s : Id {i} b c}
      (α : Id p q) (β : Id r s)  Id (p · r) (q · s)
α  β = (α ◾ᵣ _) · (_ ◾ₗ β)

--Functions are functors
ap : {i j : Level} {A : UU i} {B : UU j}
     (f : A  B) {x y : A} (p : Id {i} x y)  Id {j} (f x) (f y)
ap f refl = refl

ap-concat : {i j : Level} {A : UU i} {B : UU j}
            (f : A  B) {x y z : A} (p : Id {i} x y) (q : Id {i} y z)
             Id {j} (ap {i} {j} f (p · q)) (ap {i} {j} f p · ap {i} {j} f q)
ap-concat f refl refl = refl

ap-inverse : {i j : Level} {A : UU i} {B : UU j}
            (f : A  B) {x y : A} (p : Id {i} x y)
             Id {j} (ap {i} {j} f (p ⁻¹)) ((ap {i} {j} f p) ⁻¹)
ap-inverse f refl = refl

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

ap-id : {i : Level} {A : UU i} {x y : A} (p : Id {i} x y)
         Id (ap (id) p) p
ap-id refl = refl
{-# INLINE ap-id #-}

--Type Families are fibrations
tr : {i j : Level} {A : UU i} (P : A  UU j) {x y : A} (p : Id x y)  P x  P y
tr P refl b = b

tr-is-retraction : {i j : Level} {A : UU i} (P : A  UU j) {a a' : A} (p : Id {i} a a')  (z : P a')  Id (tr P p (tr P (p ⁻¹) z)) z  
tr-is-retraction P refl z = refl

tr-is-section : {i j : Level} {A : UU i} (P : A  UU j) {a a' : A} (p : Id {i} a a')  (z : P a)  Id (tr P (p ⁻¹) (tr P p z)) z  
tr-is-section P refl z = refl

tr-cong : {i j : Level} {A : UU i} {P : A  UU j} {x y : A} {p q : Id {i} x y} {b : P x}
           Id p q  Id (tr P p b) (tr P q b)
tr-cong refl = refl

--path lifting property
lift : {i j : Level} {A : UU i} {P : A  UU j} {y : A} (u : Σ {i} {j} A P) (p : Id {i} (pr1 u) y)
        Id u (y , tr P p (pr2 u))
lift u refl = refl


apd : {i j : Level} {A : UU i} {P : A  UU j} (f : (x : A)  P x) {x y : A}
      (p : Id {i} x y)  Id {j} (tr {i} {j} P p (f x)) (f y)
apd f refl = refl


tr-concat : {i j : Level} {A : UU i} {P : A  UU j} {x y z : A} {u : P x} (p : Id {i} x y) (q : Id {i} y z)
             Id {j} (tr {i} {j} P q (tr {i} {j} P p u)) (tr {i} {j} P (p · q) u)
tr-concat refl refl = refl

tr-comp-fam : {i j : Level} {A : UU i} {P Q : A  UU j}
              {f : (x : A)  (P x  Q x)} {x y : A} (p : Id {i} x y) (u : P x)
               Id {j} (tr Q p (f x u)) (f y (tr P p u))
tr-comp-fam refl u = refl

tr-elim : {i j : Level} {A : UU i} {P : A  UU j} {x y : A} {p : Id {i} x y} {z z' : P x}
                  (Id z z')  Id (tr P p z) (tr P p z')
tr-elim refl = refl

tr-ap : {i j : Level} {A B : UU i} {f : A  B} {P : B  UU j} {x₁ x₂ : A} (e : Id {i} x₁ x₂) {p : P (f x₁)}
                Id (tr  x  P (f x)) e p)  (tr {i} {j} P (ap f e) p)
tr-ap refl = refl

tr-fam-ap : {i j : Level} {A : UU i} {P : A  UU j} {x y : A} {p : Id {i} x y} {f g : P x  P x}{z : P x}
             (Id f g)  Id (tr P p (f z)) (tr P p (g z))
tr-fam-ap refl = refl

--Homotopies and Equivalences

_~_ :  {i j} {A : UU i} {P : A  UU j} (f g : Π A P)  UU (i  j)
_~_ {i} {j} {A} {B} f g = (x : A)  Id {j} (f x) (g x)


id-htpy : {i j : Level} {A : UU i} {P : A  UU j} (f : Π {i} {j} A P)
           f ~ f
id-htpy f a = refl

sym-htpy : {i j : Level} {A : UU i} {P : A  UU j} (f g : Π {i} {j} A P)
            f ~ g  g ~ f
sym-htpy f g H a = (H a) ⁻¹

trns-htpy : {i j : Level} {A : UU i} {P : A  UU j} (f g h : Π {i} {j} A P)
             f ~ g  g ~ h  f ~ h
trns-htpy f g h H1 H2 a = (H1 a) · (H2 a)

nat-trans-htpy : {i j : Level} {A : UU i} {B : UU j} {f g : A  B} {x y : A} 
                 (H : _~_ {i} {j} f g) (p : Id {i} x y)  Id ((H x) · (ap g p)) ((ap f p) · (H y))
nat-trans-htpy H refl = right-unit (H _)

nat-trans-htpy2 : {i j : Level} {A : UU i} {B : UU j} {f g : A  B} {x y : A} 
                 (H : _~_ {i} {j} f g) (p : Id {i} x y)  Id (((H x) · (ap g p)) · (H y) ⁻¹) (ap f p)
nat-trans-htpy2 {i} {j} {f = f} {x = x} {y = y} H p = ap {j} {j}  s  s · (H y ⁻¹)) (nat-trans-htpy H p)
                                                  · (assoc (ap {i} {j} f p) (H y) (H y ⁻¹)
                                                  · (ap  s  (ap f p) · s) (right-inv (H y))
                                                  · right-unit (ap {i} {j} f p)) )

nat-trans-htpy-to-id : {i : Level} {A : UU i} (f : A  A) (x : A)
                 (H : _~_ {i} {i} f id)  Id (H (f x)) (ap f (H x))
nat-trans-htpy-to-id {i} {A} f x H = ((((right-unit (H (f x)) ⁻¹ ) ·
                                     ap  q  (H (f x)) · q) (right-inv (H x) ⁻¹)) ·
                                     ap {i}  q  H (f x) · q) (ap  q  q · (H x ⁻¹)) (ap-id {i} {A} {f x} {x} (H x))) ⁻¹) ·
                                     assoc (H (f x)) (ap  z  id z) (H x)) (H x ⁻¹) ⁻¹) ·
                                               (nat-trans-htpy {i} {i} H (H x) ◾ᵣ ((H x) ⁻¹) ·
                                     (assoc (ap {i} {i} f (H x)) (H x) (H x ⁻¹) ·
                                     (ap {i} {i}  q  (ap {i} {i} f (H x)) · q) (right-inv (H x)) ·
                                     right-unit (ap f (H x)))))

-----------------------------------------
--IDENTITY TYPES FOR CARTESIAN PRODUCTS
pair-Id : {i j : Level} {A : UU i} {B : UU j} (x y : A × B)
               UU (i  j)
pair-Id x y = Id (pr1 x) (pr1 y) × Id (pr2 x) (pr2 y)

pair⁼ : {i j : Level} {A : UU i} {B : UU j} (x y : A × B)
                     (pair-Id x y)  Id x y 
pair⁼ (a , b) (.(pr1 (a , b)) , .(pr2 (a , b))) (refl , refl) = refl

inv-pair⁼ : {i j : Level} {A : UU i} {B : UU j} (x y : A × B)
                     Id x y  (pair-Id x y)
inv-pair⁼ x .x refl = refl , refl

htpy-inv-pair⁼-pair⁼ : {i j : Level} {A : UU i} {B : UU j} (x y : A × B)
                       (b : pair-Id x y)  Id (inv-pair⁼ x y (pair⁼ x y b)) b
htpy-inv-pair⁼-pair⁼ (a , b) (.(pr1 (a , b)) , .(pr2 (a , b))) (refl , refl) = refl

htpy-pair⁼-inv-pair⁼ : {i j : Level} {A : UU i} {B : UU j} (x y : _×_ {i} {j} A B)
                       (p : Id x y)  Id (pair⁼ x y (inv-pair⁼ x y p)) p
htpy-pair⁼-inv-pair⁼  (a , b) (.(pr1 (a , b)) , .(pr2 (a , b))) refl = refl

--IDENTITY TYPES FOR Σ-TYPES
dep-pair-Id : {i j : Level} {A : UU i} {B : A  UU j} (w w' : Σ A B)
               UU (i  j)
dep-pair-Id {i} {j} {A} {B} w w' = Σ {i} {j} (Id {i} (pr1 w) (pr1 w'))  p  Id {j} {B (pr1 w')} (tr B p (pr2 w)) (pr2 w'))

dep-pair⁼ :  {i j : Level} {A : UU i} {B : A  UU j} (w w' : Σ A B)
              (dep-pair-Id w w')  Id w w'
dep-pair⁼ (a , b) (.a , .b) (refl , refl) = refl


inv-dep-pair⁼ : {i j : Level} {A : UU i} {B : A  UU j} (w w' : Σ A B)
                     Id w w'  (dep-pair-Id w w')
inv-dep-pair⁼ (a , b) (.a , .b) refl = refl , refl

htpy-inv-dep-pair⁼-dep-pair⁼ : {i j : Level} {A : UU i} {B : A  UU j} (x y : Σ A B)
                       (b : dep-pair-Id x y)  Id (inv-dep-pair⁼ x y (dep-pair⁼ x y b)) b
htpy-inv-dep-pair⁼-dep-pair⁼ (a , b) (.a , .b) (refl , refl) = refl

htpy-dep-pair⁼-inv-dep-pair⁼ : {i j : Level} {A : UU i} {B : A  UU j} (x y : Σ {i} {j} A B)
                       (p : Id x y)  Id (dep-pair⁼ x y (inv-dep-pair⁼ x y p)) p
htpy-dep-pair⁼-inv-dep-pair⁼  (a , b) (.a , .b) refl = refl

--Function extensionality
happly : {i j : Level} {A : UU i} {B : A  UU j} {f g : (x : A)  B x}
        Id f g  (f ~ g) 
happly refl x = refl

--We have not defined isEquiv or is-contr. So we are using their expansions because we want that funext is inverse of happly.
postulate
 FUNEXT : {i j : Level} {A : UU i} {B : A  UU j} {f g : (x : A)  B x}
          (Π {i  j} {i  j} (_~_ {i} {j} f g)  H  let T = (Σ {i  j} {i  j} (Id {i  j} f g)
                                                                     p  Id (happly p) H))
                                                         in Σ T  a  ((x : T)   Id {i  j} x a)))) 

funext : {i j : Level} {A : UU i} {B : A  UU j} {f g : (x : A)  B x}
        (f ~ g)  Id f g 
funext H = pr1 (pr1 (FUNEXT H))