{-

Set quotients:

-}

{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.HITs.SetQuotients.Properties where

open import Cubical.HITs.SetQuotients.Base

open import Cubical.Core.Everything

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.Univalence

open import Cubical.Data.Sigma

open import Cubical.Relation.Nullary
open import Cubical.Relation.Binary.Base

open import Cubical.HITs.PropositionalTruncation as PropTrunc using (∥_∥; ∣_∣; squash)
open import Cubical.HITs.SetTruncation as SetTrunc using (∥_∥₂; ∣_∣₂; squash₂)

-- Type quotients

private
  variable
     : Level
    A : Type 
    R : A  A  Type 
    B : A / R  Type 
    C : A / R  A / R  Type 
    D : A / R  A / R  A / R  Type 

elimEq/ : (Bprop : (x : A / R )  isProp (B x))
          {x y : A / R}
          (eq : x  y)
          (bx : B x)
          (by : B y) 
          PathP  i  B (eq i)) bx by
elimEq/ {B = B} Bprop {x = x} =
  J  y eq   bx by  PathP  i  B (eq i)) bx by)  bx by  Bprop x bx by)

elimProp : ((x : A / R )  isProp (B x))
          ((a : A)  B ( [ a ]))
          (x : A / R)
          B x
elimProp Bprop f [ x ] = f x
elimProp Bprop f (squash/ x y p q i j) =
  isOfHLevel→isOfHLevelDep 2  x  isProp→isSet (Bprop x))
    (g x) (g y) (cong g p) (cong g q) (squash/ x y p q) i j
    where
    g = elimProp Bprop f
elimProp Bprop f (eq/ a b r i) = elimEq/ Bprop (eq/ a b r) (f a) (f b) i

elimProp2 : ((x y : A / R )  isProp (C x y))
           ((a b : A)  C [ a ] [ b ])
           (x y : A / R)
           C x y
elimProp2 Cprop f = elimProp  x  isPropΠ  y  Cprop x y))
                              x  elimProp  y  Cprop [ x ] y) (f x))

elimProp3 : ((x y z : A / R )  isProp (D x y z))
           ((a b c : A)  D [ a ] [ b ] [ c ])
           (x y z : A / R)
           D x y z
elimProp3 Dprop f = elimProp  x  isPropΠ2  y z  Dprop x y z))
                              x  elimProp2  y z  Dprop [ x ] y z) (f x))

-- lemma 6.10.2 in hott book
[]surjective : (x : A / R)  ∃[ a  A ] [ a ]  x
[]surjective = elimProp  x  squash)  a   a , refl )

elim : {B : A / R  Type }
      ((x : A / R)  isSet (B x))
      (f : (a : A)  (B [ a ]))
      ((a b : A) (r : R a b)  PathP  i  B (eq/ a b r i)) (f a) (f b))
      (x : A / R)
      B x
elim Bset f feq [ a ] = f a
elim Bset f feq (eq/ a b r i) = feq a b r i
elim Bset f feq (squash/ x y p q i j) =
  isOfHLevel→isOfHLevelDep 2 Bset
              (g x) (g y) (cong g p) (cong g q) (squash/ x y p q) i j
    where
      g = elim Bset f feq

rec : {B : Type }
      (Bset : isSet B)
      (f : A  B)
      (feq : (a b : A) (r : R a b)  f a  f b)
     A / R  B
rec Bset f feq [ a ] = f a
rec Bset f feq (eq/ a b r i) = feq a b r i
rec Bset f feq (squash/ x y p q i j) = Bset (g x) (g y) (cong g p) (cong g q) i j
  where
  g = rec Bset f feq

rec2 : {B : Type } (Bset : isSet B)
       (f : A  A  B) (feql : (a b c : A) (r : R a b)  f a c  f b c)
                       (feqr : (a b c : A) (r : R b c)  f a b  f a c)
     A / R  A / R  B
rec2 Bset f feql feqr = rec (isSetΠ  _  Bset))
                             a  rec Bset (f a) (feqr a))
                             a b r  funExt (elimProp  _  Bset _ _)
                                               c  feql a b c r)))

setQuotUniversal : {B : Type } (Bset : isSet B) 
                   (A / R  B)  (Σ[ f  (A  B) ] ((a b : A)  R a b  f a  f b))
setQuotUniversal Bset = isoToEquiv (iso intro out outRightInv outLeftInv)
  where
  intro = λ g    a  g [ a ]) , λ a b r i  g (eq/ a b r i)
  out = λ h  elim  x  Bset) (fst h) (snd h)

  outRightInv :  h  intro (out h)  h
  outRightInv h = refl

  outLeftInv :  g  out (intro g)  g
  outLeftInv = λ g  funExt  x  PropTrunc.elim {P = λ sur  out (intro g) x  g x}
     sur  Bset (out (intro g) x) (g x))
     sur  cong (out (intro g)) (sym (snd sur))  (cong g (snd sur))) ([]surjective x)
    )

open BinaryRelation

effective : (Rprop : isPropValued R) (Requiv : isEquivRel R) (a b : A)  [ a ]  [ b ]  R a b
effective {A = A} {R = R} Rprop (equivRel R/refl R/sym R/trans) a b p = transport aa≡ab (R/refl _)
  where
    helper : A / R  hProp _
    helper =
      rec isSetHProp  c  (R a c , Rprop a c))
                      c d cd  Σ≡Prop  _  isPropIsProp)
                                        (hPropExt (Rprop a c) (Rprop a d)
                                                   ac  R/trans _ _ _ ac cd)  ad  R/trans _ _ _ ad (R/sym _ _ cd))))

    aa≡ab : R a a  R a b
    aa≡ab i = helper (p i) .fst

isEquivRel→isEffective : isPropValued R  isEquivRel R  isEffective R
isEquivRel→isEffective {R = R} Rprop Req a b = isoToIsEquiv (iso out intro out-intro intro-out)
  where
    intro : [ a ]  [ b ]  R a b
    intro = effective Rprop Req a b

    out : R a b  [ a ]  [ b ]
    out = eq/ a b

    intro-out :  x  intro (out x)  x
    intro-out ab = Rprop a b _ _

    out-intro :  x  out (intro x)  x
    out-intro eq = squash/ _ _ _ _

discreteSetQuotients : Discrete A  isPropValued R  isEquivRel R  (∀ a₀ a₁  Dec (R a₀ a₁))  Discrete (A / R)
discreteSetQuotients {A = A} {R = R} Adis Rprop Req Rdec =
  elim  a₀  isSetΠ  a₁  isProp→isSet (isPropDec (squash/ a₀ a₁))))
    discreteSetQuotients' discreteSetQuotients'-eq
  where
    discreteSetQuotients' : (a : A) (y : A / R)  Dec ([ a ]  y)
    discreteSetQuotients' a₀ = elim  a₁  isProp→isSet (isPropDec (squash/ [ a₀ ] a₁))) dis dis-eq
      where
        dis : (a₁ : A)  Dec ([ a₀ ]  [ a₁ ])
        dis a₁ with Rdec a₀ a₁
        ... | (yes p) = yes (eq/ a₀ a₁ p)
        ... | (no ¬p) = no λ eq  ¬p (effective Rprop Req a₀ a₁ eq )

        dis-eq : (a b : A) (r : R a b) 
          PathP  i  Dec ([ a₀ ]  eq/ a b r i)) (dis a) (dis b)
        dis-eq a b ab = J  b ab   k  PathP  i  Dec ([ a₀ ]  ab i)) (dis a) k)
                           k  isPropDec (squash/ _ _) _  _) (eq/ a b ab) (dis b)

    discreteSetQuotients'-eq : (a b : A) (r : R a b) 
      PathP  i  (y : A / R)  Dec (eq/ a b r i  y))
            (discreteSetQuotients' a) (discreteSetQuotients' b)
    discreteSetQuotients'-eq a b ab =
      J  b ab   k  PathP  i  (y : A / R)  Dec (ab i  y))
                              (discreteSetQuotients' a) k)
         k  funExt  x  isPropDec (squash/ _ _) _ _)) (eq/ a b ab) (discreteSetQuotients' b)