{-# OPTIONS --safe #-}
{-
Constructions of "sets" in the cumulative hierarchy. Including:
- the empty set
- unions
- ω
- replacement
- separation
-}

module Cubical.HITs.CumulativeHierarchy.Constructions where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Function
open import Cubical.Foundations.Structure
open import Cubical.Functions.Embedding
open import Cubical.Functions.Logic as L
open import Cubical.Data.Sum hiding (elim)
open import Cubical.Data.Sigma
open import Cubical.Data.Nat hiding (elim)
open import Cubical.Data.Sum as  using (_⊎_)
open import Cubical.Data.Bool
open import Cubical.Data.Unit
open import Cubical.Data.Empty as E hiding (elim)
open import Cubical.HITs.PropositionalTruncation as P hiding (elim; elim2)

open import Cubical.HITs.CumulativeHierarchy.Base
open import Cubical.HITs.CumulativeHierarchy.Properties
import Cubical.HITs.PropositionalTruncation.Monad as PropMonad

private
  variable
     ℓ' : Level

------------
-- Structures for building specific sets by giving encodings and decodings for their membership
-----------

record SetStructure  : Type (ℓ-suc ) where
  field
    X : Type 
    ix : X  V 
  resSet : V 
  resSet = sett X ix

record SetPackage  ℓ' : Type (ℓ-max (ℓ-suc ) (ℓ-suc ℓ')) where
  field
    structure : SetStructure 
  open SetStructure structure hiding (resSet)
  open SetStructure structure using (resSet) public
  field
    ∈-rep : V   hProp ℓ'
    unpack : (x : X)   ∈-rep (ix x) 
    repack : {y : V }   ∈-rep y    fiber ix y ∥₁

  open PropMonad
  classification :  ∀[ y ] (y ∈ₛ resSet  ∈-rep y) 
  classification y = intoClassifier , fromClassifier where
    intoClassifier :  y ∈ₛ resSet    ∈-rep y 
    intoClassifier (yi , yr) = proof (∈-rep y) by do
      (x , ix)  ∈∈ₛ {b = resSet} .snd (yi , yr)
      return (subst (fst  ∈-rep) ix (unpack x))
    fromClassifier :  ∈-rep y    y ∈ₛ resSet 
    fromClassifier yr = ∈∈ₛ {b = resSet} .fst (repack {y = y} yr)

------------
-- Specific constructions
-----------
open SetPackage using (structure; ∈-rep; unpack; repack)

module EmptySet where
  EmptyStructure : SetStructure 
  SetStructure.X EmptyStructure = Lift E.⊥
  SetStructure.ix EmptyStructure ()

  EmptyPackage : SetPackage  ℓ-zero
  structure EmptyPackage = EmptyStructure
  ∈-rep EmptyPackage _ = L.⊥
  unpack EmptyPackage ()
  repack EmptyPackage ()

   : V 
   = SetStructure.resSet EmptyStructure

  ∅-empty :  ∀[ b  V  ] ¬ (b ∈ₛ ) 
  ∅-empty b = SetPackage.classification EmptyPackage b .fst
open EmptySet using (; ∅-empty) public

module UnionSet (S : V ) where
  UnionStructure : SetStructure 
  SetStructure.X UnionStructure = Σ[ r   S  ]   S ⟫↪ r 
  SetStructure.ix UnionStructure (r , i) =   S ⟫↪ r ⟫↪ i

  UNION : V 
  UNION = SetStructure.resSet UnionStructure

  UnionPackage : SetPackage _ (ℓ-suc )
  structure UnionPackage = UnionStructure
  ∈-rep UnionPackage y = ∃[ v ] (v ∈ₛ S)  (y ∈ₛ v)
  unpack UnionPackage (vi , yi) =   S ⟫↪ vi , ∈ₛ⟪ S ⟫↪ vi , ∈ₛ⟪  S ⟫↪ vi ⟫↪ yi ∣₁
  repack UnionPackage {y = y} = P.rec squash₁ go where
    go : Σ[ v  V _ ]  v ∈ₛ S  ⊓′  y ∈ₛ v    fiber _ y ∥₁
    go (v , (vi , vS) , xv) =  repFiber≃fiber _ _ .fst ((vi , key .fst) , key .snd) ∣₁ where
      path : v   S ⟫↪ vi
      path = sym (equivFun identityPrinciple vS)
      key : Σ[ i    S ⟫↪ vi  ]   S ⟫↪ vi ⟫↪ i  y
      key = subst  v  Σ[ ix   v  ]  v ⟫↪ ix  y) path xv

  union-ax :  ∀[ u ] (u ∈ₛ UNION  (∃[ v ] (v ∈ₛ S)  (u ∈ₛ v))) 
  union-ax = classification where
    open SetPackage UnionPackage using (classification)
open UnionSet renaming (UNION to infixr 9 ⋃_) using (union-ax) public

module PairingSet (a b : V ) where
  PairingStructure : SetStructure 
  SetStructure.X PairingStructure = Lift Bool
  SetStructure.ix PairingStructure (lift false) = a
  SetStructure.ix PairingStructure (lift true) = b

  open PropMonad
  PairingPackage : SetPackage _ (ℓ-suc )
  structure PairingPackage = PairingStructure
  ∈-rep PairingPackage d = (d ≡ₕ a)  (d ≡ₕ b)
  unpack PairingPackage (lift false) = L.inl refl
  unpack PairingPackage (lift true) = L.inr refl
  repack PairingPackage {y = y} = _>>= λ where
    (_⊎_.inl ya)   lift false , sym ya ∣₁
    (_⊎_.inr yb)   lift true , sym yb ∣₁

  PAIR : V 
  PAIR = SetStructure.resSet PairingStructure

  pairing-ax :  ∀[ d ] (d ∈ₛ PAIR  (d ≡ₕ a)  (d ≡ₕ b)) 
  pairing-ax = classification where
    open SetPackage PairingPackage using (classification)
-- pairing TODO: notation?
open PairingSet renaming (PAIR to infix 12 ⁅_,_⁆) using (pairing-ax) public

module SingletonSet (a : V ) where
  SingletonStructure : SetStructure 
  SetStructure.X SingletonStructure = Lift Unit
  SetStructure.ix SingletonStructure (lift tt) = a

  SingletonPackage : SetPackage _ (ℓ-suc )
  structure SingletonPackage = SingletonStructure
  ∈-rep SingletonPackage d = d ≡ₕ a
  unpack SingletonPackage _ = refl
  repack SingletonPackage ya =  lift tt , sym ya ∣₁

  SINGL : V 
  SINGL = SetStructure.resSet SingletonStructure
open SingletonSet renaming (SINGL to infix 10 ⁅_⁆s) public

-- small unions
_∪_ : (a b : V )  V 
a  b =   a , b 

module InfinitySet {} where
  sucV : V   V 
  sucV N = N   N ⁆s

  #_ :   V 
  # zero = 
  # suc n = sucV (# n)

  ωStructure : SetStructure 
  SetStructure.X ωStructure = Lift 
  SetStructure.ix ωStructure = #_  lower

  ω : V 
  ω = SetStructure.resSet ωStructure

  open PropMonad
  ωPackage : SetPackage _ (ℓ-suc )
  structure ωPackage = ωStructure
  ∈-rep ωPackage d = (d ≡ₕ )  (∃[ v ] (d ≡ₕ sucV v)  (v ∈ₛ ω))
  unpack ωPackage (lift zero) = L.inl refl
  unpack ωPackage (lift (suc n)) = L.inr  # n , refl , ∈∈ₛ {b = ω} .fst  lift n , refl ∣₁ ∣₁
  repack ωPackage {y = y} = ⊔-elim (y ≡ₕ )  _ ∥ₚ  _   fiber _ y ∥ₚ)
     e   lift zero , sym e ∣₁)
     m  do (v , yv , vr)  m
              (lift n , eq)  ∈∈ₛ {b = ω} .snd vr
               lift (suc n) , sym (subst  v  y  (v   v ⁆s)) (sym eq) yv) ∣₁
    )

  infinity-ax :  ∀[ y ] (y ∈ₛ ω  (y ≡ₕ )  (∃[ v ] (y ≡ₕ sucV v)  (v ∈ₛ ω))) 
  infinity-ax = classification where
    open SetPackage ωPackage using (classification)

  ω-empty :   ∈ₛ ω 
  ω-empty = infinity-ax  .snd (L.inl refl)

  ω-next :  ∀[ x  V  ] x ∈ₛ ω  sucV x ∈ₛ ω 
  ω-next x x∈ω = infinity-ax (sucV x) .snd (L.inr  x , refl , x∈ω ∣₁)

  #-in-ω :  n   # n ∈ₛ ω 
  #-in-ω zero = ω-empty
  #-in-ω (suc n) = ω-next (# n) (#-in-ω n)

open InfinitySet using (ω; ω-empty; ω-next; infinity-ax) public

module ReplacementSet (r : V   V ) (a : V ) where
  ReplacementStructure : SetStructure 
  SetStructure.X ReplacementStructure =  a 
  SetStructure.ix ReplacementStructure = r   a ⟫↪

  REPLACED : V 
  REPLACED = SetStructure.resSet ReplacementStructure

  open PropMonad
  ReplacementPackage : SetPackage _ (ℓ-suc )
  structure ReplacementPackage = ReplacementStructure
  ∈-rep ReplacementPackage y = ∃[ z ] (z ∈ₛ a)  (y ≡ₕ r z)
  unpack ReplacementPackage ⟪a⟫ =   a ⟫↪ ⟪a⟫ , (∈ₛ⟪ a ⟫↪ ⟪a⟫) , refl ∣₁
  repack ReplacementPackage {y = y} m = do
    (z , (a , za) , yr)  m
     a , cong r (equivFun identityPrinciple za)  sym yr ∣₁

  replacement-ax :  ∀[ y ] (y ∈ₛ REPLACED  (∃[ z ] (z ∈ₛ a)  (y ≡ₕ r z))) 
  replacement-ax y = classification y where
    open SetPackage ReplacementPackage using (classification)
open ReplacementSet renaming (REPLACED to infix 12 ⁅_∣_⁆) using (replacement-ax) public

module SeparationSet (a : V ) (ϕ : V   hProp ) where
  SeparationStructure : SetStructure 
  SetStructure.X SeparationStructure = Σ[ x   a  ]  ϕ ( a ⟫↪ x) 
  SetStructure.ix SeparationStructure =  a ⟫↪  fst

  SeparationPackage : SetPackage _ 
  structure SeparationPackage = SeparationStructure
  ∈-rep SeparationPackage y = (y ∈ₛ a)  ϕ y
  unpack SeparationPackage (⟪a⟫ , phi) = (∈ₛ⟪ a ⟫↪ ⟪a⟫) , phi
  repack SeparationPackage ((⟪a⟫ , ya) , phi) =
     (⟪a⟫ , subst (fst  ϕ) (sym (equivFun identityPrinciple ya)) phi)
      , equivFun identityPrinciple ya ∣₁

  SEPAREE : V 
  SEPAREE = SetStructure.resSet SeparationStructure

  separation-ax :  ∀[ y ] (y ∈ₛ SEPAREE  (y ∈ₛ a)  ϕ y) 
  separation-ax y = classification y .fst , classification y .snd where
    open SetPackage SeparationPackage using (classification)
open SeparationSet renaming (SEPAREE to infix 12 ⁅_∶_⁆) using (separation-ax) public

module Examples where
  open InfinitySet

  the1 : V ℓ-zero
  the1 = # 1

  1-ok? :  Bool ∥₁
  1-ok? = do
    prf  infinity-ax the1 .fst (#-in-ω 1)
    case prf of λ { (⊎.inl _)   false ∣₁ ; (⊎.inr _)   true ∣₁ }
    where
    open PropMonad
  test : 1-ok?   true ∣₁
  test = refl