-- We define ZigZag-complete relations and prove that quasi equivalence relations
-- give rise to equivalences on the set quotients.
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Relation.ZigZag.Base where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels
open import Cubical.Data.Sigma
open import Cubical.HITs.SetQuotients
open import Cubical.HITs.PropositionalTruncation as Trunc
open import Cubical.Relation.Binary.Base

open BinaryRelation
open isEquivRel

private
 variable
   ℓ' : Level

isZigZagComplete : {A B : Type } (R : A  B  Type ℓ')  Type (ℓ-max  ℓ')
isZigZagComplete R =  {a b a' b'}  R a b  R a' b  R a' b'  R a b'

ZigZagRel : (A B : Type ) (ℓ' : Level)  Type (ℓ-max  (ℓ-suc ℓ'))
ZigZagRel A B ℓ' = Σ[ R  (A  B  Type ℓ') ] (isZigZagComplete R)

record isQuasiEquivRel {A B : Type } (R : A  B  Type ℓ') : Type (ℓ-max  ℓ') where
  field
    zigzag : isZigZagComplete R
    fwd : (a : A)  ∃[ b  B ] R a b
    bwd : (b : B)  ∃[ a  A ] R a b

open isQuasiEquivRel

QuasiEquivRel : (A B : Type ) (ℓ' : Level)  Type (ℓ-max  (ℓ-suc ℓ'))
QuasiEquivRel A B ℓ' =
  Σ[ R  PropRel A B ℓ' ] isQuasiEquivRel (R .fst)

invQER : {A B : Type } {ℓ' : Level}  QuasiEquivRel A B ℓ'  QuasiEquivRel B A ℓ'
invQER (R , qer) .fst = invPropRel R
invQER (R , qer) .snd .zigzag aRb aRb' a'Rb' = qer .zigzag a'Rb' aRb' aRb
invQER (R , qer) .snd .fwd = qer .bwd
invQER (R , qer) .snd .bwd = qer .fwd

QER→EquivRel : {A B : Type }
   QuasiEquivRel A B ℓ'  EquivPropRel A (ℓ-max  ℓ')
QER→EquivRel (R , sim) .fst = compPropRel R (invPropRel R)
QER→EquivRel (R , sim) .snd .reflexive a = Trunc.map  {(b , r)  b , r , r}) (sim .fwd a)
QER→EquivRel (R , sim) .snd .symmetric _ _ = Trunc.map  {(b , r₀ , r₁)  b , r₁ , r₀})
QER→EquivRel (R , sim) .snd .transitive _ _ _ =
  Trunc.map2  {(b , r₀ , r₁) (b' , r₀' , r₁')  b , r₀ , sim .zigzag r₁' r₀' r₁})

-- The following result is due to Carlo Angiuli
module QER→Equiv {A B : Type } (R : QuasiEquivRel A B ℓ') where

  Rᴸ = QER→EquivRel R .fst .fst
  Rᴿ = QER→EquivRel (invQER R) .fst .fst

  private
    sim = R .snd

  private
    f : (a : A)  ∃[ b  B ] R .fst .fst a b  B / Rᴿ
    f a =
      Trunc.rec→Set squash/
        ([_]  fst)
         {(b , r) (b' , r')  eq/ b b'  a , r , r' })

    fPath :
      (a₀ : A) (s₀ : ∃[ b  B ] R .fst .fst a₀ b)
      (a₁ : A) (s₁ : ∃[ b  B ] R .fst .fst a₁ b)
       Rᴸ a₀ a₁
       f a₀ s₀  f a₁ s₁
    fPath a₀ =
      Trunc.elim  _  isPropΠ3 λ _ _ _  squash/ _ _)
         {(b₀ , r₀) a₁ 
          Trunc.elim  _  isPropΠ λ _  squash/ _ _)
             {(b₁ , r₁) 
              Trunc.elim  _  squash/ _ _)
                 {(b' , r₀' , r₁')  eq/ b₀ b₁  a₀ , r₀ , sim .zigzag r₀' r₁' r₁ })})})

    φ : A / Rᴸ  B / Rᴿ
    φ [ a ] = f a (sim .fwd a)
    φ (eq/ a₀ a₁ r i) =  fPath a₀ (sim .fwd a₀) a₁ (sim .fwd a₁) r i
    φ (squash/ _ _ p q j i) = squash/ _ _ (cong φ p) (cong φ q) j i


  relToFwd≡ :  {a b}  R .fst .fst a b  φ [ a ]  [ b ]
  relToFwd≡ {a} {b} r =
    Trunc.elim {P = λ s  f a s  [ b ]}  _  squash/ _ _)
       {(b' , r')  eq/ b' b  a , r' , r })
      (sim .fwd a)

  fwd≡ToRel :  {a b}  φ [ a ]  [ b ]  R .fst .fst a b
  fwd≡ToRel {a} {b} =
    Trunc.elim {P = λ s  f a s  [ b ]  R .fst .fst a b}
       _  isPropΠ λ _  R .fst .snd _ _)
       {(b' , r') p 
        Trunc.rec (R .fst .snd _ _)
           {(a' , s' , s)  R .snd .zigzag r' s' s})
          (effective
            (QER→EquivRel (invQER R) .fst .snd)
            (QER→EquivRel (invQER R) .snd)
            b' b p)})
      (sim .fwd a)

  private
    g : (b : B)  ∃[ a  A ] R .fst .fst a b  A / Rᴸ
    g b =
      Trunc.rec→Set squash/
        ([_]  fst)
         {(a , r) (a' , r')  eq/ a a'  b , r , r' })

    gPath :
      (b₀ : B) (s₀ : ∃[ a  A ] R .fst .fst a b₀)
      (b₁ : B) (s₁ : ∃[ a  A ] R .fst .fst a b₁)
       Rᴿ b₀ b₁
       g b₀ s₀  g b₁ s₁
    gPath b₀ =
      Trunc.elim  _  isPropΠ3 λ _ _ _  squash/ _ _)
         {(a₀ , r₀) b₁ 
          Trunc.elim  _  isPropΠ λ _  squash/ _ _)
             {(a₁ , r₁) 
              Trunc.elim  _  squash/ _ _)
                 {(a' , r₀' , r₁')  eq/ a₀ a₁  b₀ , r₀ , sim .zigzag r₁ r₁' r₀' })})})

    ψ : B / Rᴿ  A / Rᴸ
    ψ [ b ] = g b (sim .bwd b)
    ψ (eq/ b₀ b₁ r i) = gPath b₀ (sim .bwd b₀) b₁ (sim .bwd b₁) r i
    ψ (squash/ _ _ p q j i) = squash/ _ _ (cong ψ p) (cong ψ q) j i

  relToBwd≡ :  {a b}  R .fst .fst a b  ψ [ b ]  [ a ]
  relToBwd≡ {a} {b} r =
    Trunc.elim {P = λ s  g b s  [ a ]}  _  squash/ _ _)
       {(a' , r')  eq/ a' a  b , r' , r })
      (sim .bwd b)

  private
    η :  qb  φ (ψ qb)  qb
    η =
      elimProp  _  squash/ _ _)
         b 
          Trunc.elim {P = λ s  φ (g b s)  [ b ]}  _  squash/ _ _)
             {(a , r)  relToFwd≡ r})
            (sim .bwd b))

    ε :  qa  ψ (φ qa)  qa
    ε =
      elimProp  _  squash/ _ _)
         a 
          Trunc.elim {P = λ s  ψ (f a s)  [ a ]}  _  squash/ _ _)
             {(b , r)  relToBwd≡ r})
            (sim .fwd a))

  bwd≡ToRel :  {a b}  ψ [ b ]  [ a ]  R .fst .fst a b
  bwd≡ToRel {a} {b} p = fwd≡ToRel (cong φ (sym p)  η [ b ])

  Thm : (A / Rᴸ)  (B / Rᴿ)
  Thm = isoToEquiv (iso φ ψ η ε)