public open import Primitives using
  ( Partial
  , coe
  , Sub
  , inS
  , outS
  )
public open import Intervals

variable A B C D : Type
def Path (A : IType) (a : A 0) (b : A 1) : Type ⇒ ⟦ iA i { ~ i := a | i := b }
def Eq (A : Type) (a b : A) : TypePath (\ iA) a b
def infix = {A : Type} ⇒ Eq A looser ~
def idp {a : A} : a = a\ia
def funExt (f g : AB) (p :  af a = g a) : f = g\i ap a i
def pmap (f : AB) {a b : A} (p : a = b) : f a = f b\if (p i)
def sym (p : ⟦ iA) : p 1 = p 0 ⇒ \ip (~ i)

def isContr (A : Type) ⇒ Σ (a : A) ** ( ba = b)

/// Carlo Angiuli's PhD thesis, Section 3.2
module Angiuli {
  def transport {a b : A} (B : AType) (p : a = b) (x : B a) : B bcoe 0 1 (\yB (p y)) x

  def IdSys (a : A) ⇒ ⟦ iA { ~ i := a }

  def uniqueness (a : A) : isContr (IdSys a)
    ⇒ (idp,  \ b i jcoe 0 j (\ka = b k) idp i)

  def J {a : A} (B : IdSys aType)
      (r : B idp) (b : IdSys a) : B btransport B ((uniqueness a).2 b) r
}

public open Angiuli using (transport, J)

// Bye, regularity!
// def Jβ {a : A} (B : ∀ b -> a = b -> Type)
//         (r : B a idp) : J B r idp = r => idp

def funExt2 (f g : ABC) (p :  a bf a b = g a b) : f = g\ i a bp a b i

def funExt3 (f g : ABCD) (p :  a b cf a b c = g a b c) : f = g\ i a b cp a b c i

// The most general path mapping
def pmapd {A : IType} (B :  iA iType)
    (f :  i (a : A i) → B i a)
    (p : ⟦ iA i)
  : ⟦ iB i (p i) { ~ i := f 0 (p 0) | i := f 1 (p 1) } ⇒ \ if i (p i)

def hcomp2d
  {c d : A}
  (p : ⟦ iA)
  (q : p 1 = d)
  (r : p 0 = c) : c = dcoe 0 1 (\ kr k = q k) p

def <=3=> {c d : A} (p : ⟦ iA) (q : p 1 = c) (r : c = d) : p 0 = dhcomp2d q r (sym p)

def infixr <==>
  (p : ⟦ iA)
  (q : ⟦ iA { ~ i := p 1 })
  : p 0 = q 1 ⇒ <=3=> idp p q
  tighter =