{-# OPTIONS --cubical --guardedness --safe #-}

{-
   Shared cubical foundation for the coinductive-repair development.

   This is the dependency-free base that everything else sits on:
   Σ, the path vocabulary, the bi-invertible equivalence `_≃_` and
   logical biimplication `_↔_`, h-levels, a little path algebra, and the
   `≃`-combinators.  `coinductive-repair.mtype` re-exports this module
   publicly, so importing `mtype` (directly or via `coalg`) brings all of
   it into scope.

   Only Agda primitives are used here; there is no external library.
-}

module coinductive-repair.cubical-common where

open import Agda.Primitive
  using (Level; _⊔_; lsuc; lzero)
  renaming (Set to Type)
open import Agda.Primitive.Cubical
  using (I; i0; i1; primHComp; primComp)
  renaming (primIMin to _∧_; primIMax to _∨_; primINeg to ~_;
            primTransp to transp)
open import Agda.Builtin.Cubical.Path public

private variable
   ℓ' ℓ'' : Level

{- Dependent pairs. -}

infixr 4 _,_
record Σ (X : Type ) (P : X  Type ℓ') : Type (  ℓ') where
  constructor _,_
  field
    fst : X
    snd : P fst
open Σ public

{- Path vocabulary: reflexivity, inversion, congruence, transport, and
   `subst`-based path concatenation. -}

refl : {A : Type } {x : A}  x  x
refl {x = x} = λ _  x

sym : {A : Type } {x y : A}  x  y  y  x
sym p i = p (~ i)

cong : {A : Type } {B : A  Type ℓ'} (f : (x : A)  B x)
       {x y : A} (p : x  y)  PathP  i  B (p i)) (f x) (f y)
cong f p i = f (p i)

transport : {A B : Type }  A  B  A  B
transport p a = transp  i  p i) i0 a

subst : {A : Type } (B : A  Type ℓ')
        {x y : A}  x  y  B x  B y
subst B p bx = transp  i  B (p i)) i0 bx

-- `subst` as a dependent path: `bx` and `subst B p bx` are connected by a
-- `PathP` over the family `B ∘ p`.
subst-filler : {A : Type } (B : A  Type ℓ')
               {x y : A} (p : x  y) (bx : B x) 
               PathP  i  B (p i)) bx (subst B p bx)
subst-filler B p bx i = transp  j  B (p (i  j))) (~ i) bx

-- `PathP A x y` is, as a *type*, the path `transport A x ≡ y`. Presenting
-- it as an actual path of types makes `fromPathP`/`toPathP` literal
-- transports, so their round-trip is `transport⁻Transport`.
PathP≡Path : (A : I  Type ) (x : A i0) (y : A i1)
            PathP A x y  (transp  i  A i) i0 x  y)
PathP≡Path A x y i =
  PathP  j  A (i  j)) (transp  j  A (i  j)) (~ i) x) y

-- Collapse a `PathP` to a plain path on the transported endpoint.
fromPathP : {A : I  Type } {x : A i0} {y : A i1}
           PathP A x y  transp  i  A i) i0 x  y
fromPathP {A = A} {x = x} {y = y} = transport (PathP≡Path A x y)

-- Build a `PathP` from such a plain path (inverse of `fromPathP`).
toPathP : {A : I  Type } {x : A i0} {y : A i1}
         transp  i  A i) i0 x  y  PathP A x y
toPathP {A = A} {x = x} {y = y} = transport (sym (PathP≡Path A x y))

-- `subst` along `refl` is the identity (up to a path).
substRefl : {A : Type } (B : A  Type ℓ') {x : A} (bx : B x)
           subst B refl bx  bx
substRefl B {x} bx i = transp  _  B x) i bx

-- Extend a `PathP` by a plain path on its right (`i1`) endpoint.
_▷_ : {A : I  Type } {x : A i0} {y z : A i1}
     PathP A x y  y  z  PathP A x z
_▷_ {x = x} p q i =
  primHComp
     j  λ where
      (i = i0)  x
      (i = i1)  q j)
    (p i)
infixl 30 _▷_

-- Composition of base paths (hcomp form) and its filler.
compBase : {A : Type } {a0 a1 a2 : A}  a0  a1  a1  a2  a0  a2
compBase {a0 = a0} bp bq i =
  primHComp  j  λ where (i = i0)  a0 ; (i = i1)  bq j) (bp i)

compBase-fill : {A : Type } {a0 a1 a2 : A}
                (bp : a0  a1) (bq : a1  a2) (i j : I)  A
compBase-fill {a0 = a0} bp bq i j =
  primHComp  l  λ where (i = i0)  a0
                           (i = i1)  bq (j  l)
                           (j = i0)  bp i)
            (bp i)

-- Composition of dependent paths over a composite base, `compBase bp bq`.
compPathP : {A : Type } {B : A  Type ℓ'} {a0 a1 a2 : A}
            {bp : a0  a1} {bq : a1  a2}
            {b0 : B a0} {b1 : B a1} {b2 : B a2}
           PathP  i  B (bp i)) b0 b1
           PathP  i  B (bq i)) b1 b2
           PathP  i  B (compBase bp bq i)) b0 b2
compPathP {B = B} {bp = bp} {bq = bq} {b0 = b0} P Q i =
  primComp  j  B (compBase-fill bp bq i j))
            j  λ where (i = i0)  b0 ; (i = i1)  Q j)
           (P i)

-- Appending `refl` on the right of a `PathP` is the identity.
▷-refl : {A : I  Type } {a : A i0} {b : A i1} (P : PathP A a b)
        (P   _  b))  P
▷-refl {a = a} {b = b} P l i =
  primHComp  j  λ where (i = i0)  a ; (i = i1)  b ; (l = i1)  P i) (P i)

-- Appending `refl` on the left of a base composition collapses it.
compBase-refl : {A : Type } {a0 a1 : A} (bp : a0  a1)
               compBase bp  _  a1)  bp
compBase-refl {a0 = a0} {a1 = a1} bp l i =
  primHComp  j  λ where (i = i0)  a0 ; (i = i1)  a1 ; (l = i1)  bp i)
            (bp i)

-- The 3-D filler linking `compBase-fill bp refl` (l=i0) to `bp` (l=i1),
-- with top face `compBase-refl bp` (j=i1).
compBase-refl-fill : {A : Type } {a0 a1 : A} (bp : a0  a1) (l i j : I)  A
compBase-refl-fill {a0 = a0} {a1 = a1} bp l i j =
  primHComp  m  λ where (i = i0)  a0
                           (i = i1)  a1
                           (j = i0)  bp i
                           (l = i1)  bp i)
            (bp i)

-- `compPathP P refl ≡ P`, as a dependent path over `compBase-refl bp`.
compPathP-refl : {A : Type } {B : A  Type ℓ'} {a0 a1 : A} {bp : a0  a1}
                 {b0 : B a0} {b1 : B a1}
                 (P : PathP  i  B (bp i)) b0 b1)
                PathP  l  PathP  i  B (compBase-refl bp l i)) b0 b1)
                       (compPathP {A = A} {B = B} {bp = bp} {bq = λ _  a1}
                                  P  _  b1)) P
compPathP-refl {B = B} {bp = bp} {b0 = b0} {b1 = b1} P l i =
  primComp  j  B (compBase-refl-fill bp l i j))
            j  λ where (i = i0)  b0 ; (i = i1)  b1 ; (l = i1)  P i)
           (P i)

funExt : {A : Type } {B : A  Type ℓ'} {f g : (x : A)  B x}
        (∀ x  f x  g x)  f  g
funExt h i x = h x i

_∙_ : {A : Type } {x y z : A}  x  y  y  z  x  z
_∙_ {x = x} p q = subst  a  x  a) q p
infixr 30 _∙_

{-
   Bi-invertible type equivalence: a forward map with separately stated
   left and right inverses.  (They coincide as functions in our uses, but
   keeping the two round-trip witnesses independent is convenient.)
-}

record _≃_ (X Y : Type ) : Type  where
  field
    fwd      : X  Y
    bwd-L    : Y  X
    leftInv  : (x : X)  bwd-L (fwd x)  x
    bwd-R    : Y  X
    rightInv : (y : Y)  fwd (bwd-R y)  y
open _≃_ public

-- Alias used by the carrier-iso corollary in `coalg`.
Iso : (X Y : Type )  Type 
Iso = _≃_

{-
   Logical biimplication: forward and backward maps with no round-trip
   witnesses.  Weaker than `_≃_`, used when one side is not (or is not
   asserted to be) a proposition.
-}

record _↔_ (X Y : Type ) : Type  where
  field
    fwd : X  Y
    bwd : Y  X
open _↔_ public

{- Propositions, contractibility, and the basic h-level lemmas. -}

isProp : Type   Type 
isProp A = (x y : A)  x  y

isSet : Type   Type 
isSet A = (x y : A) (p q : x  y)  p  q

isContr : Type   Type 
isContr A = Σ A  a  (x : A)  a  x)

isPropΠ : {A : Type } {B : A  Type ℓ'}
         ((x : A)  isProp (B x))  isProp ((x : A)  B x)
isPropΠ h f g = funExt  x  h x (f x) (g x))

-- `isContr A` is itself a proposition (i = outer isProp index, j = the
-- inner snd-PathP index).
isPropIsContr : {A : Type }  isProp (isContr A)
isPropIsContr (c₀ , p₀) (c₁ , p₁) i =
  ( p₀ c₁ i
  , λ x j  primHComp
       k  λ where
        (j = i0)  p₀ c₁ i
        (j = i1)  p₀ x (i  k)
        (i = i0)  p₀ x (j  k)
        (i = i1)  p₁ x j)
      (p₀ (p₁ x j) i))

{-
   Path induction, and path algebra for the `subst`-based `_∙_`.  The
   `J` base cases collapse because `transp A i1 a ≡ a` always holds
   definitionally (whereas `transp (λ _ → A) i0` does not reduce for
   abstract `A`, so `p ∙ refl ≡ p` is `∙-idr`, not `refl`).
-}

J : {A : Type } {x : A}
    (P : (y : A)  x  y  Type ℓ')
   P x refl  {y : A} (p : x  y)  P y p
J {x = x} P d p = transp  i  P (p i)  j  p (i  j))) i0 d

-- `transport` along `refl` is the identity.
transportRefl : {A : Type } (a : A)  transport refl a  a
transportRefl a = substRefl  X  X) a

-- Transporting backward then forward is the identity (the round-trip of a
-- type equivalence presented by a path).
transport⁻Transport : {A B : Type } (p : A  B) (b : B)
                     transport p (transport (sym p) b)  b
transport⁻Transport {A = A} p b =
  J  B' p'  (b' : B')  transport p' (transport (sym p') b')  b')
     b'  transportRefl (transport refl b')  transportRefl b')
    p b

-- The `fromPathP` / `toPathP` round-trip — now a literal `transport⁻Transport`.
fromPathP-toPathP : {A : I  Type } {x : A i0} {y : A i1}
                    (p : transp  i  A i) i0 x  y)
                   fromPathP {A = A} (toPathP {A = A} p)  p
fromPathP-toPathP {A = A} {x = x} {y = y} p =
  transport⁻Transport (PathP≡Path A x y) p

toPathP-fromPathP : {A : I  Type } {x : A i0} {y : A i1}
                    (P : PathP A x y)
                   toPathP {A = A} (fromPathP {A = A} P)  P
toPathP-fromPathP {A = A} {x = x} {y = y} P =
  transport⁻Transport (sym (PathP≡Path A x y)) P

-- `V ▷ sym (fromPathP V)` is the canonical `toPathP refl` (the transp-filler),
-- *independently of `V`*.  This is the key fact behind the p/q corner
-- coincidence in `deriveCoind2D`.
▷-sym-fromPathP :
    {A : I  Type } {x : A i0} {y : A i1} (V : PathP A x y)
   (V   i  fromPathP V (~ i)))
       toPathP {A = A}  _  transp  i  A i) i0 x)
▷-sym-fromPathP {A = A} {x = x} {y = y} V =
  subst  V'  (V'   i  fromPathP V (~ i)))
                   toPathP {A = A}  _  tx))
        (toPathP-fromPathP V)
        (base (fromPathP V))
  where
    tx : A i1
    tx = transp  i  A i) i0 x
    base : (w : tx  y)
          (toPathP {A = A} w   i  w (~ i)))  toPathP {A = A}  _  tx)
    base w =
      J  y' w'  (toPathP {A = A} {x = x} {y = y'} w'   i  w' (~ i)))
                      toPathP {A = A}  _  tx))
        (▷-refl (toPathP {A = A}  _  tx)))
        w

∙-idr : {A : Type } {x y : A} (p : x  y)  p  refl  p
∙-idr {x = x} {y = y} p i = transp  _  x  y) i p

∙assoc : {A : Type } {w x y z : A}
         (p : w  x) (q : x  y) (r : y  z)
        (p  q)  r  p  (q  r)
∙assoc p q r =
  J  _ r'  (p  q)  r'  p  (q  r'))
    (∙-idr (p  q)  sym (cong (p ∙_) (∙-idr q)))
    r

cong₂ : {A : Type } {B : Type ℓ'} {C : Type ℓ''} (f : A  B  C)
        {x y : A} {u v : B}  x  y  u  v  f x u  f y v
cong₂ f p q i = f (p i) (q i)

cong-∙ : {A : Type } {X : Type ℓ'}
         (F : A  X) {x y z : A} (p : x  y) (q : y  z)
        cong F (p  q)  cong F p  cong F q
cong-∙ F p q =
  J  _ q'  cong F (p  q')  cong F p  cong F q')
    (cong (cong F) (∙-idr p)  sym (∙-idr (cong F p)))
    q

∙-idl : {A : Type } {x y : A} (p : x  y)  refl  p  p
∙-idl p = J  _ p'  refl  p'  p') (∙-idr refl) p

{- Sliding the left endpoint of `q` along `p` (`subst (_≡ y) p`)
   is the same as prepending `sym p`. -}
subst-slide : {A : Type } {a b y : A} (p : a  b) (q : a  y)
             subst  z  z  y) p q  sym p  q
subst-slide {y = y} =
  J  _ p'  (q : _  y)  subst  z  z  y) p' q  sym p'  q)
     q  substRefl  z  z  y) q  sym (∙-idl q))

-- `subst` on the right endpoint of `q : x ≡ a` along `p : a ≡ b`
-- is the same as appending `p`.
subst-slide-right : {A : Type } {x a b : A} (p : a  b) (q : x  a)
                   subst  z  x  z) p q  q  p
subst-slide-right {x = x} =
  J  _ p'  (q : x  _)  subst  z  x  z) p' q  q  p')
     q  substRefl  z  x  z) q  sym (∙-idr q))

-- Transporting a path along its endpoints (`sym a ∙ p ∙ b`).
transp-path : {A : Type } {a0 a1 b0 b1 : A}
              (a : a0  a1) (b : b0  b1) (p : a0  b0)
             transp  j  a j  b j) i0 p  sym a  (p  b)
transp-path {a0 = a0} a b p =
  J  _ a'  {b0 b1 : _} (b' : b0  b1) (p' : a0  b0)
                 transp  j  a' j  b' j) i0 p'  sym a'  (p'  b'))
     {b0} {b1} b' p' 
       J  _ b''  (p'' : a0  b0)
                       transp  j  a0  b'' j) i0 p''  sym refl  (p''  b''))
          p''  transportRefl p''  sym (∙-idl (p''  refl)  ∙-idr p''))
         b' p')
    a b p

-- The diagonal of a `transp`-over-constant square is its
-- `transportRefl` edge followed by the path.
transp-diag : {C : Type } {a b : C} (P : a  b)
              k  transp  _  C) k (P k))  transportRefl (P i0)  P
transp-diag {C = C} {a = a} P =
  J  b' P'   k  transp  _  C) k (P' k))  transportRefl (P' i0)  P')
    (sym (∙-idr (transportRefl a)))
    P

-- Right inverse: `p ∙ sym p` contracts to `refl`.
rCancel : {A : Type } {x y : A} (p : x  y)  p  sym p  refl
rCancel {x = x} p = J  _ p'  p'  sym p'  refl) (∙-idr refl) p

-- Left inverse: `sym p ∙ p` contracts to `refl`.
lCancel : {A : Type } {x y : A} (p : x  y)  sym p  p  refl
lCancel p = J  _ p'  sym p'  p'  refl) (∙-idr refl) p

-- `sym` is a contravariant functor on `_∙_`.
symDist : {A : Type } {x y z : A} (p : x  y) (q : y  z)
         sym (p  q)  sym q  sym p
symDist p q =
  J  _ q'  sym (p  q')  sym q'  sym p)
    (cong sym (∙-idr p)  sym (∙-idl (sym p)))
    q

{- A square (a `PathP` between two paths) gives the equality of its two
   ways around, for the `subst`-based `_∙_`.  Edges:
       top : a ≡ b,  bot : c ≡ d,  left : a ≡ c,  right : b ≡ d.
   Proof: `J` on `top` (abstracting `right`, whose type tracks `top`'s
   endpoint) reduces to the base case `top = refl`, where `left ∙ bot`
   is *definitionally* `transp (λ i → a ≡ bot i) i0 left`, so
   `fromPathP sq` is exactly `left ∙ bot ≡ right`. -}
Square→∙ : {A : Type } {a b c d : A}
           {top : a  b} {bot : c  d} {left : a  c} {right : b  d}
          PathP  i  top i  bot i) left right
          top  right  left  bot
Square→∙ {d = d} {top = top} {bot = bot} {left = left} {right = right} sq =
  J  _ top'  (right' : _  d)
               PathP  i  top' i  bot i) left right'
               top'  right'  left  bot)
     right' sq'  ∙-idl right'  sym (fromPathP sq'))
    top right sq

{- More h-levels: a prop is a set, contractibility implies set-ness,
   `PathP` between propositions, and Σ/retract closure. -}

isContr→isProp : {A : Type }  isContr A  isProp A
isContr→isProp (c , p) x y = sym (p x)  p y

isProp→isSet : {A : Type }  isProp A
              (x y : A) (p q : x  y)  p  q
isProp→isSet {A = A} h x y p q j i =
  primHComp
     k  λ where
      (i = i0)  h x x k
      (i = i1)  h x y k
      (j = i0)  h x (p i) k
      (j = i1)  h x (q i) k)
    x

isContr→isSet : {A : Type }  isContr A
               (x y : A) (p q : x  y)  p  q
isContr→isSet c = isProp→isSet (isContr→isProp c)

isProp→PathP : {A : I  Type }  ((i : I)  isProp (A i))
              (a₀ : A i0) (a₁ : A i1)  PathP A a₀ a₁
isProp→PathP {A = A} hA a₀ a₁ i =
  hA i (transp  j  A (i  j)) (~ i) a₀)
       (transp  j  A (i  ~ j)) i a₁) i

Σ-isProp : {A : Type } {B : A  Type ℓ'}
          isProp A  ((a : A)  isProp (B a))  isProp (Σ A B)
Σ-isProp pa pb (x₀ , y₀) (x₁ , y₁) i =
    pa x₀ x₁ i
  , isProp→PathP  j  pb (pa x₀ x₁ j)) y₀ y₁ i

retract-isContr : {A : Type } {B : Type ℓ'}
                  (r : A  B) (s : B  A)
                 ((b : B)  r (s b)  b)
                 isContr A  isContr B
retract-isContr r s rs (c , p) =
  r c ,  b  cong r (p (s b))  rs b)

isSetΠ : {A : Type } {B : A  Type ℓ'}
        ((a : A)  isSet (B a))  isSet ((a : A)  B a)
isSetΠ h f g p q i j a =
  h a (f a) (g a)  k  p k a)  k  q k a) i j

-- Σ over a constant family (all `P` needs); Σ-eta makes the
-- rebuilt square's boundary match on the nose.
isSetΣconst : {A : Type } {B : Type ℓ'}
             isSet A  isSet B  isSet (Σ A  _  B))
isSetΣconst sa sb u v p q i j =
    sa (fst u) (fst v)  k  fst (p k))  k  fst (q k)) i j
  , sb (snd u) (snd v)  k  snd (p k))  k  snd (q k)) i j

isSetRetract : {A : Type } {B : Type ℓ'}
               (s : B  A) (r : A  B)
              ((b : B)  r (s b)  b)
              isSet A  isSet B
isSetRetract s r rs sa x y p q i j =
  primHComp
     k  λ where
      (i = i0)  rs (p j) k
      (i = i1)  rs (q j) k
      (j = i0)  rs x k
      (j = i1)  rs y k)
    (r (sa (s x) (s y) (cong s p) (cong s q) i j))

{- Non-dependent products and the `≃`-combinators. -}

infixr 2 _×_
_×_ : Type   Type ℓ'  Type (  ℓ')
A × B = Σ A  _  B)

≃-trans : {A B C : Type }  A  B  B  C  A  C
≃-trans e f = record
  { fwd     = λ x  fwd f (fwd e x)
  ; bwd-L   = λ z  bwd-L e (bwd-L f z)
  ; leftInv = λ x  cong (bwd-L e) (leftInv f (fwd e x))  leftInv e x
  ; bwd-R   = λ z  bwd-R e (bwd-R f z)
  ; rightInv = λ z  cong (fwd f) (rightInv e (bwd-R f z))  rightInv f z }

-- Componentwise congruence of `≃` under binary product.
≃-× : {A A' B B' : Type }  A  A'  B  B'  (A × B)  (A' × B')
≃-× e f = record
  { fwd      = λ p  fwd e (fst p) , fwd f (snd p)
  ; bwd-L    = λ p  bwd-L e (fst p) , bwd-L f (snd p)
  ; leftInv  = λ p i  leftInv e (fst p) i , leftInv f (snd p) i
  ; bwd-R    = λ p  bwd-R e (fst p) , bwd-R f (snd p)
  ; rightInv = λ p i  rightInv e (fst p) i , rightInv f (snd p) i }