{-# OPTIONS --cubical --guardedness --erasure #-}
module BrouwerTree.OrdinalDecidability.Sierpinski where

open import Cubical.Foundations.Prelude hiding (_∨_; _∧_)
open import Cubical.Foundations.Path
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Univalence

open import Cubical.Data.Nat as Nat
open import Cubical.Data.Bool
open import Cubical.Data.Sum
open import Cubical.Data.Sigma hiding (; ∃-syntax; _∨_; _∧_)
open import Cubical.Data.Empty as Empty renaming ( to Empty)
open import Cubical.Data.Unit
open import Cubical.Relation.Nullary
open import Cubical.Foundations.Function

open import Iff
open import PropTrunc

open import BrouwerTree.OrdinalDecidability.Basic

private
  variable
     ℓ' : Level

mutual

  -- The free ω-complete cpo on Unit
  data 𝕊 : Type where
     : 𝕊
     : 𝕊
     : (  𝕊) -> 𝕊
    ∨-comm :  {x y}  x  y  y  x
    ∨-assoc :  {x y z}  x  (y  z)  (x  y)  z
    ∨-idem :  {x}  x  x  x
    ∨-bot :  {x}  x    x
    ∨-absorb :  {s :   𝕊} n  s n  ( s)   s
    ∨-distr :  {s :   𝕊} {x}  x  ( s)    n  x  s n)
    isSet𝕊 : isSet 𝕊

  _∨_ : 𝕊  𝕊  𝕊
  x  y =  (caseNat x y)


module _ (P : 𝕊  Type )(isProp⟨P⟩ : (x : 𝕊)  isProp (P x))
         (p⊤ : P )(p⊥ : P )
         -- we split up the p⋁ case into head and tail to please the
         -- termination checker, due to the `caseNat`s involved
         (p⋁ :  {s}  P (s 0)  ((n : ) -> P (s (suc n)))  P ( s))
       where

  𝕊-ind' : (x : 𝕊) -> P x
  𝕊-ind'  = p⊤
  𝕊-ind'  = p⊥
  𝕊-ind' ( s) = p⋁ (𝕊-ind' (s 0))  n  𝕊-ind' (s (suc n)))
  𝕊-ind' (∨-comm {x} {y} i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-comm i))
                (p⋁ (𝕊-ind' x)  n  𝕊-ind' y))
                (p⋁ (𝕊-ind' y)  n  𝕊-ind' x)) i
  𝕊-ind' (∨-assoc {x} {y} {z} i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-assoc i))
                (p⋁ (𝕊-ind' x)
                     _  p⋁ (𝕊-ind' y)  _  𝕊-ind' z)))
                (p⋁ (p⋁ (𝕊-ind' x)  n  𝕊-ind' y))
                     n  𝕊-ind' z)) i
  𝕊-ind' (∨-idem {x} i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-idem i))
                (p⋁ (𝕊-ind' x)  n  𝕊-ind' x))
                (𝕊-ind' x) i
  𝕊-ind' (∨-bot {x} i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-bot i))
                (p⋁ (𝕊-ind' x)  n  p⊥))
                (𝕊-ind' x) i
  𝕊-ind' (∨-absorb {s} n i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-absorb n i))
                (p⋁ (𝕊-ind' (s n))
                     _  p⋁ (𝕊-ind' (s 0))  k  𝕊-ind' (s (suc k)))))
                    (p⋁ (𝕊-ind' (s 0))  n  𝕊-ind' (s (suc n)))) i
  𝕊-ind' (∨-distr {s} {x} i) =
   isProp→PathP  i  isProp⟨P⟩ (∨-distr i))
                (p⋁ (𝕊-ind' x)
                     _  p⋁ (𝕊-ind' (s 0))  n  𝕊-ind' (s (suc n)))))
                (p⋁ (p⋁ (𝕊-ind' x)
                     n  𝕊-ind' (s 0)))
                     n  p⋁ (𝕊-ind' x)  n₁  𝕊-ind' (s (suc n))))) i
  𝕊-ind' (isSet𝕊 x y p q i j) =
   isProp→SquareP  i j  isProp⟨P⟩ (isSet𝕊 x y p q i j))
                   j  𝕊-ind' x)
                   j  𝕊-ind' y)
                   j  𝕊-ind' (p j))
                   j  𝕊-ind' (q j)) i j

-- Of course now we can give the "proper" type for `p⋁`, post facto

𝕊-ind : (P : 𝕊  Type )
       ((x : 𝕊)  isProp (P x))
       P 
       P 
       ({s :   𝕊}  ((n : ) -> P (s n))  P ( s))
       (x : 𝕊) -> P x
𝕊-ind P isProp⟨P⟩ p⊤ p⊥ p⋁ x =
 𝕊-ind' P isProp⟨P⟩ p⊤ p⊥  {s} p0 ps  p⋁ λ { zero  p0 ; (suc n)  ps n }) x

⊤-is-top : (x : 𝕊)  x    
⊤-is-top =
 𝕊-ind  x  x    )  x  isSet𝕊 (x  ) )
       ∨-idem
       (∨-comm  ∨-bot)
        {s} ih  ( s  )         ≡⟨ ∨-comm 
                   (   s)         ≡⟨ ∨-distr 
                     x    s x) ≡⟨ cong  ([1] ih) 
                   (  )           ≡⟨ ∨-idem 
                                    )
 where
  [1] : {s :   𝕊}  ((n : )  (s n  )  )
        x   (caseNat  (s x)))   v  caseNat   v)
  [1] ih = (funExt  n  ∨-comm  ih n))  funExt  { zero  refl ; (suc n)  refl })

contains-⊤-implies-is-⊤ : (s :   𝕊)
                         ∃[ n   ] s n     s  
contains-⊤-implies-is-⊤ s = ∥-∥rec (isSet𝕊 ( s) )
                                    (n , sn=⊤)   s         ≡⟨ sym (∨-absorb n) 
                                                   (s n   s) ≡⟨ ∨-comm 
                                                   ( s  s n) ≡⟨ cong ( s ∨_) sn=⊤ 
                                                   ( s  )   ≡⟨ ⊤-is-top ( s) 
                                                              )

⋁-idem : {x : 𝕊}    _  x)  x
⋁-idem {x} =   _  x) ≡⟨ [1] 
             (x  x)     ≡⟨ ∨-idem 
             x           
 where
  [1] = cong  (funExt λ { zero  refl ; (suc n)  refl })

-- Same termination checker trick for `is⊥` as for `𝕊-ind`
mutual
  is⊥ : 𝕊 -> Type ℓ-zero
  is⊥  = Empty
  is⊥  = Unit
  is⊥ ( s) = is⊥ (s 0) × (∀ n  is⊥ (s (suc n)))
  is⊥ (∨-comm {x} {y} i) =
   hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } (is⊥-prop y)))
            (isProp× (is⊥-prop y) (isProp→ {A = } (is⊥-prop x)))
             (p , f)  f 1 , λ _  p)
             (p , f)  f 1 , λ _  p) i
  is⊥ (∨-assoc {x} {y} {z} i) =
   hPropExt (isProp× (is⊥-prop x)
                     (isProp→ {A = } (isProp× (is⊥-prop y)
                                      (isProp→ {A = } (is⊥-prop z)))))
            (isProp× (isProp× (is⊥-prop x)
                              (isProp→ {A = } (is⊥-prop y)))
                     (isProp→ {A = } (is⊥-prop z)))
             { (a , bc)  (a , λ _  fst (bc 1)) , λ _  snd (bc 1) 1 })
             ((a , b) , c)  a , λ _  (b 1) , c) i
  is⊥ (∨-idem {x} i) =
   hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } (is⊥-prop x)))
            (is⊥-prop x)
            fst
             p  p , λ _  p)
            i
  is⊥ (∨-bot {x} i) =
   hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } isPropUnit))
            (is⊥-prop x)
            fst
             p  p , λ _  tt) i
  is⊥ (∨-absorb {s = s} n i) =
   hPropExt (isProp× (is⊥-prop (s n))
                     (isProp→ {A = }
                       (isProp× (is⊥-prop (s 0))
                                (isPropΠ {A = }  m  is⊥-prop (s (suc m)))))))
            (isProp× (is⊥-prop (s 0))
                     (isPropΠ {A = }  m  (is⊥-prop (s (suc m))))))
             (sn , f)  (fst (f 1)) ,  m  snd (f 1) m))
             (sz , f)  Nat.elim {A = is⊥  s} sz  m _  f m) n , λ m  sz , f) i
  is⊥ (∨-distr {s} {x} i) =
   hPropExt (isProp× (is⊥-prop x)
                     (isProp→ {A = }
                       (isProp× (is⊥-prop (s 0))
                                (isPropΠ {A = }  m  (is⊥-prop (s (suc m))))))))
            (isProp× (isProp× (is⊥-prop x)
                              (isProp→ {A = } (is⊥-prop (s 0))))
                     (isPropΠ  n  (isProp× (is⊥-prop x)
                                              (isProp→ {A = } (is⊥-prop (s (suc n))))))))
             (p , f)  (p , λ _  fst (f 1)) , λ m  p ,  _  snd (f 1) m))
             ((p , f) , g)  p ,  _  (f 1) ,  m  snd (g m) 1))) i
  is⊥ (isSet𝕊 x y p q i j) =
   fst (isSet→SquareP  _ _  isSetHProp)
                       j  is⊥ (p j) , is⊥-prop (p j))
                       j  is⊥ (q j) , is⊥-prop (q j))
                      refl
                      refl i j)

  is⊥-prop : (x : 𝕊)  isProp (is⊥ x)
  is⊥-prop  = isProp⊥
  is⊥-prop  = isPropUnit
  is⊥-prop ( s) = isProp× (is⊥-prop (s 0)) (isPropΠ  n  is⊥-prop (s (suc n))))
  is⊥-prop (∨-comm {x} {y} i) =
   isProp→PathP
      i  isPropIsProp
              {A = hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } (is⊥-prop y)))
                            (isProp× (is⊥-prop y) (isProp→ {A = } (is⊥-prop x)))
                             (ix , f)  f 1 , λ _  ix)
                             (ix , f)  f 1 , λ _  ix) i})
     (isProp× (is⊥-prop x) (isPropΠ {A = }  n  is⊥-prop y)))
     (isProp× (is⊥-prop y) (isPropΠ {A = }  n  is⊥-prop x))) i
  is⊥-prop (∨-assoc {x} {y} {z} i) =
   isProp→PathP
      i  isPropIsProp
              {A = hPropExt (isProp× (is⊥-prop x)
                                     (isProp→ {A = }
                                       (isProp× (is⊥-prop y)
                                                (isProp→ {A = } (is⊥-prop z)))))
                            (isProp× (isProp× (is⊥-prop x)
                                              (isProp→ {A = } (is⊥-prop y)))
                                     (isProp→ {A = } (is⊥-prop z)))
                             { (a , bc)  (a , λ _  fst (bc 1)) , λ _  snd (bc 1) 1 })
                             ((a , b) , c)  a , λ _  (b 1) , c) i})
     (isProp× (is⊥-prop x)
              (isPropΠ  n  isProp× (is⊥-prop y) (isPropΠ  n₁  is⊥-prop z)))))
     (isProp× (isProp× (is⊥-prop x) (isPropΠ  n  is⊥-prop y)))
              (isPropΠ  n  is⊥-prop z))) i
  is⊥-prop (∨-idem {x} i) =
   isProp→PathP
      i  isPropIsProp
              {A = (hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } (is⊥-prop x)))
                             (is⊥-prop x)
                             fst
                              p  p ,  _  p)) i)})
                (isProp× (is⊥-prop x) (isPropΠ  n  is⊥-prop x)))
                (is⊥-prop x) i
  is⊥-prop (∨-bot {x} i) =
   isProp→PathP
      i  isPropIsProp
              {A = (hPropExt (isProp× (is⊥-prop x) (isProp→ {A = } isPropUnit))
                             (is⊥-prop x)
                             fst
                              p  p ,  _  tt)) i)})
                (isProp× (is⊥-prop x) (isPropΠ  n  isPropUnit)))
                (is⊥-prop x) i
  is⊥-prop (∨-absorb {s = s} n i) =
   isProp→PathP
      i  isPropIsProp
              {A = hPropExt (isProp× (is⊥-prop (s n))
                                     (isProp→ {A = }
                                       (isProp× (is⊥-prop (s 0))
                                                (isPropΠ {A = }
                                                   m  is⊥-prop (s (suc m)))))))
                            (isProp× (is⊥-prop (s 0))
                                     (isPropΠ {A = }  m  (is⊥-prop (s (suc m))))))
                             (sn , f)  (fst (f 1)) ,  m  snd (f 1) m))
                             (sz , f)  Nat.elim {A = λ n  is⊥ (s n)} sz  m _  f m) n , λ m  sz , f) i})
    (isProp× (is⊥-prop (s n))
             (isPropΠ  n₁  isProp× (is⊥-prop (s 0))
                                      (isPropΠ  m  is⊥-prop (s (suc m)))))))
    (isProp× (is⊥-prop (s 0)) (isPropΠ  n₁  is⊥-prop (s (suc n₁))))) i
  is⊥-prop (∨-distr {s} {x} i) =
    isProp→PathP
       i  isPropIsProp
               {A = hPropExt (isProp× (is⊥-prop x)
                                      (isProp→ {A = }
                                        (isProp× (is⊥-prop (s 0))
                                                 (isPropΠ {A = }
                                                    m  (is⊥-prop (s (suc m))))))))
                             (isProp× (isProp× (is⊥-prop x)
                                               (isProp→ {A = } (is⊥-prop (s 0))))
                                      (isPropΠ
                                         n  (isProp× (is⊥-prop x)
                                                        (isProp→ {A = }
                                                          (is⊥-prop (s (suc n))))))))
                              (ix , f)  (ix , λ _  fst (f 1)) , λ m  ix ,  _  snd (f 1) m))
                              ((ix , f) , g)  ix ,  _  (f 1) ,  m  snd (g m) 1))) i})
      (isProp× (is⊥-prop x)
               (isPropΠ  n  isProp× (is⊥-prop (s 0))
                                       (isPropΠ  n₁  is⊥-prop (s (suc n₁)))))))
      (isProp× (isProp× (is⊥-prop x)
                        (isPropΠ  n  is⊥-prop (s 0))))
               (isPropΠ  n  isProp× (is⊥-prop x)
                                       (isPropΠ  n₁  is⊥-prop (s (suc n))))))) i
  is⊥-prop (isSet𝕊 x y p q i j) =
   snd (isSet→SquareP  _ _  isSetHProp)
                       j  is⊥ (p j) , is⊥-prop (p j))
                       j  is⊥ (q j) , is⊥-prop (q j))
                      refl
                      refl i j)

⊥≠⊤ :     Empty
⊥≠⊤ p = subst is⊥ p tt

is⊥-correct :  {x}  is⊥ x  x  
is⊥-correct {x} =
 𝕊-ind  x  is⊥ x  x  )  x  isProp→ (isSet𝕊 x ))
       Empty.rec
        _  refl)
        {s} ih f  let s≡⊥ : s   n  )
                         s≡⊥ = funExt  { zero  ih 0 (fst f)
                                         ; (suc n)  ih (suc n) (snd f n) })
                     in subst  z   z  ) (sym s≡⊥) ⋁-idem)
                 x

is⊥-correct⁻¹ :  {x}  x    is⊥ x
is⊥-correct⁻¹ {x} x≡⊥ = subst is⊥ (sym x≡⊥) tt

mutual
  is⊤ : 𝕊 -> Type ℓ-zero
  is⊤  = Unit
  is⊤  = Empty
  is⊤ ( s) =  (is⊤ (s 0))  (Σ[ n   ] is⊤ (s (suc n))) 
  is⊤ (∨-comm {x} {y} i) = hPropExt isPropPropTrunc
                                    isPropPropTrunc
                                    (∥-∥map (adjust x y))
                                    (∥-∥map (adjust y x))
                                    i
   module comm where
    adjust : (x y : 𝕊)
            (is⊤ x)  (Σ[ n   ] is⊤ y)  (is⊤ y)  (Σ[ n   ] is⊤ x)
    adjust x y (inl p) = inr (0 , p)
    adjust x y (inr (n , p)) = inl p
  is⊤ (∨-assoc {x} {y} {z} i) = hPropExt isPropPropTrunc
                                         isPropPropTrunc
                                         (∥-∥rec isPropPropTrunc adjust₁)
                                         (∥-∥rec isPropPropTrunc adjust₂)
                                         i
   module assoc where
    adjust₁ : (is⊤ x)  (Σ[ n   ]  (is⊤ y)  (Σ[ m   ] is⊤ z) )
               (is⊤ x)  (Σ[ n   ] is⊤ y)   (Σ[ m   ] is⊤ z) 
    adjust₁ (inl p) =  inl  inl p  
    adjust₁ (inr (n , p)) = ∥-∥rec isPropPropTrunc  { (inl p)   inl  inr ((1 , p))  
                                                      ; (inr (m , p))   inr ((1 , p))  }) p
    adjust₂ :  (is⊤ x)  (Σ[ n   ] is⊤ y)   (Σ[ m   ] is⊤ z)
              (is⊤ x)  (Σ[ n   ]  (is⊤ y)  (Σ[ m   ] is⊤ z) ) 
    adjust₂ (inl p) = ∥-∥rec isPropPropTrunc  { (inl p)   inl p 
                                                ; (inr (m , p))   inr (1 ,  inl p )  }) p
    adjust₂ (inr (n , p)) =  inr (1 ,  inr ((1 , p)) ) 
  is⊤ (∨-idem {x} i) = hPropExt isPropPropTrunc
                                (is⊤-prop x)
                                (∥-∥rec (is⊤-prop x) adjust)
                                 p   inl p ) i
   module idem where
    adjust : (is⊤ x)  (Σ[ n   ] is⊤ x)  is⊤ x
    adjust (inl p) = p
    adjust (inr (n , p)) = p
  is⊤ (∨-bot {x} i) = hPropExt isPropPropTrunc
                               (is⊤-prop x)
                               (∥-∥rec (is⊤-prop x) adjust)
                                p   inl p ) i
   module bot where
    adjust : (is⊤ x)  (Σ[ n   ] Empty)  is⊤ x
    adjust (inl p) = p
  is⊤ (∨-absorb {s} n i) = hPropExt isPropPropTrunc
                                    isPropPropTrunc
                                    (∥-∥rec isPropPropTrunc (adjust₁ n))
                                    (∥-∥rec isPropPropTrunc adjust₂) i
   module absorb where
    adjust₁ : (n : )
             (is⊤ (s n))  (Σ[ n   ]  (is⊤ (s 0))  (Σ[ m   ] is⊤ (s (suc m))) )
              (is⊤ (s 0))  (Σ[ n   ] is⊤ (s (suc n))) 
    adjust₁ zero (inl p) =  inl p 
    adjust₁ (suc n) (inl p) =  inr (n , p) 
    adjust₁ n (inr (m , p)) = ∥-∥rec isPropPropTrunc ∣_∣ p
    adjust₂ : (is⊤ (s 0))  (Σ[ n   ] is⊤ (s (suc n)))
              (is⊤ (s n))  (Σ[ n   ]  (is⊤ (s 0))  (Σ[ m   ] is⊤ (s (suc m))) ) 
    adjust₂ (inl p) =  inr (1 ,  inl p ) 
    adjust₂ (inr p) =  inr (1 ,  inr p ) 
  is⊤ (∨-distr {s} {x} i) = hPropExt isPropPropTrunc
                                     isPropPropTrunc
                                     (∥-∥rec isPropPropTrunc adjust₁)
                                     (∥-∥rec isPropPropTrunc adjust₂) i
   module distr where
    adjust₁ : (is⊤ x)  (Σ[ n   ]  (is⊤ (s 0))  (Σ[ m   ] is⊤ (s (suc m))))
               (is⊤ x)  (Σ[ n   ] is⊤ (s 0))   (Σ[ n   ]  (is⊤ x)  (Σ[ m   ] is⊤ (s (suc n))) ) 
    adjust₁ (inl p) =  inl  inl p  
    adjust₁ (inr (n , p)) =
     ∥-∥rec isPropPropTrunc  { (inl p)   inl  inr (1 , p)  
                               ; (inr (m , p))   inr (m ,  inr (1 , p) )  }) p
    adjust₂ :  (is⊤ x)  (Σ[ n   ] is⊤ (s 0))   (Σ[ n   ]  (is⊤ x)  (Σ[ m   ] is⊤ (s (suc n))) )
              (is⊤ x)  (Σ[ n   ]  (is⊤ (s 0))  (Σ[ m   ] is⊤ (s (suc m)))) 
    adjust₂ (inl p) = ∥-∥rec isPropPropTrunc  { (inl p)   inl p 
                                                ; (inr (m , p))   inr (1 ,  inl p )  }) p
    adjust₂ (inr (n , p)) = ∥-∥rec isPropPropTrunc  { (inl p)   inl p 
                                                      ; (inr (m , p))   inr (1 ,  inr (n , p) )  }) p
  is⊤ (isSet𝕊 x y p q i j) =
   fst (isSet→SquareP  _ _  isSetHProp)
                       j  is⊤ (p j) , is⊤-prop (p j))
                       j  is⊤ (q j) , is⊤-prop (q j))
                      refl
                      refl i j)

  is⊤-prop : (x : 𝕊)  isProp (is⊤ x)
  is⊤-prop  = isPropUnit
  is⊤-prop  = isProp⊥
  is⊤-prop ( s) = isPropPropTrunc
  is⊤-prop (∨-comm {x} {y} i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  isPropPropTrunc
                                                  (∥-∥map (comm.adjust {x} {y} i x y))
                                                  (∥-∥map (comm.adjust {x} {y} i y x)) i})
                isPropPropTrunc
                isPropPropTrunc i
  is⊤-prop (∨-assoc {x} {y} {z} i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  isPropPropTrunc
                                                  (∥-∥rec isPropPropTrunc (assoc.adjust₁ {x} {y} {z} i))
                                                  (∥-∥rec isPropPropTrunc (assoc.adjust₂ {x} {y} {z} i)) i})
                isPropPropTrunc
                isPropPropTrunc i

  is⊤-prop (∨-idem {x} i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  (is⊤-prop x)
                                                  (∥-∥rec (is⊤-prop x) (idem.adjust {x} i))
                                                   p   inl p ) i})
                isPropPropTrunc
                (is⊤-prop x) i
  is⊤-prop (∨-bot {x} i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  (is⊤-prop x)
                                                  (∥-∥rec (is⊤-prop x) (bot.adjust {x} i))
                                                   p   inl p ) i})
                isPropPropTrunc
                (is⊤-prop x) i
  is⊤-prop (∨-absorb {s} n i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  isPropPropTrunc
                                                  (∥-∥rec isPropPropTrunc (absorb.adjust₁ {s} n i n))
                                                  (∥-∥rec isPropPropTrunc (absorb.adjust₂ {s} n i)) i})
                isPropPropTrunc
                isPropPropTrunc i
  is⊤-prop (∨-distr {s} {x} i) =
   isProp→PathP  i  isPropIsProp {A = hPropExt isPropPropTrunc
                                                  isPropPropTrunc
                                                  (∥-∥rec isPropPropTrunc (distr.adjust₁ {s} {x} i))
                                                  (∥-∥rec isPropPropTrunc (distr.adjust₂ {s} {x} i)) i})
                isPropPropTrunc
                isPropPropTrunc i
  is⊤-prop (isSet𝕊 x y p q i j) =
   snd (isSet→SquareP  _ _  isSetHProp)
                       j  is⊤ (p j) , is⊤-prop (p j))
                       j  is⊤ (q j) , is⊤-prop (q j))
                      refl
                      refl i j)

is⊤-correct :  {x}  is⊤ x  x  
is⊤-correct {x} = 𝕊-ind  x  is⊤ x  x  )
                                  x  isProp→ (isSet𝕊 x ))
                                 [1]
                                 [2]
                                 [3] x
 where
  [1] : is⊤     
  [1] = λ _  refl
  [2] : is⊤     
  [2] = Empty.rec
  [3] : {s :   𝕊}
       ((n : )  is⊤ (s n)  s n  )
        is⊤ (s 0)  Σ-syntax   n  is⊤ (s (suc n)))    s  
  [3] {s} ih p = ∥-∥rec (isSet𝕊 ( s) ) [4] p
   where
    [4] : (is⊤ (s 0))  (Σ[ n   ] is⊤ (s (suc n)))   s  
    [4] (inl p) = contains-⊤-implies-is-⊤ s  0 , ih 0 p 
    [4] (inr (n , p)) = contains-⊤-implies-is-⊤ s  suc n , ih (suc n) p 

is⊤-correct⁻¹ :  {x}  x    is⊤ x
is⊤-correct⁻¹ {x} x≡⊤ = subst  z  is⊤ z) (sym x≡⊤) tt

is-⊤-implies-contains-⊤ : (s :   𝕊) ->  s    ∃[ n   ] s n  
is-⊤-implies-contains-⊤ s ⋁s≡⊤ = ∥-∥rec isPropPropTrunc [1] (is⊤-correct⁻¹ ⋁s≡⊤)
 where
  [1] : is⊤ (s 0)  Σ-syntax   n  is⊤ (s (suc n)))  ∃[ n   ] s n  
  [1] (inl p) =  0 , is⊤-correct p 
  [1] (inr (n , p)) =  suc n , is⊤-correct p 

is-⊤↔contains-⊤ : (s :   𝕊) ->  s    ∃[ n   ] s n  
is-⊤↔contains-⊤ s = (is-⊤-implies-contains-⊤ s) , (contains-⊤-implies-is-⊤ s)

-- The order corresponding to least upper bounds ∨

_≼_ : 𝕊  𝕊  Type
x  y = (x  y)  y

isProp⟨≼⟩ : (x y : 𝕊)  isProp (x  y)
isProp⟨≼⟩ x y = isSet𝕊 (x  y) y

≼-antisym :  {x y}  x  y  y  x  x  y
≼-antisym {x} {y} x≼y y≼x = x ≡⟨ sym y≼x  y  x ≡⟨ ∨-comm  x  y ≡⟨ x≼y  y 

everything-under-⊤ :  {x}  x  
everything-under-⊤ = ⊤-is-top _

≼-refl :  {x}  x  x
≼-refl = ∨-idem

≼-trans :  {x y z}  x  y  y  z  x  z
≼-trans {x} {y} {z} x≼y y≼z =
  x  z
    ≡⟨ cong  w  x  w) (sym y≼z) 
  (x  (y  z))
    ≡⟨ ∨-assoc 
  ((x  y)  z)
    ≡⟨ cong  w  w  z) x≼y 
  (y  z)
    ≡⟨ y≼z 
  z 


_≼⟨_⟩_ :  {y z}  (x : 𝕊)  (x  y)  (y  z)  (x  z)
x ≼⟨ x≼y  y≼z = ≼-trans x≼y y≼z

_≼∎ : (x : 𝕊)  x  x
_ ≼∎ = ≼-refl

infixr  0 _≼⟨_⟩_
infix   1 _≼∎

≼-cocone-simple : {s :   𝕊}   k  s k   s
≼-cocone-simple = ∨-absorb

≼-cocone : {s :   𝕊} {x : 𝕊} (k : )  x  s k  x   s
≼-cocone {s} {x} k x≤sk = ≼-trans x≤sk (≼-cocone-simple k)

≼-limiting : (s :   𝕊) {x : 𝕊}  ((k : )  s k  x)   s  x
≼-limiting s {x} s≼x =  s  x           ≡⟨ ∨-comm 
                       x   s           ≡⟨ ∨-distr 
                         k  x  s k) ≡⟨ cong  (funExt λ k  ∨-comm) 
                         k  s k  x) ≡⟨ cong  (funExt λ k  s≼x k) 
                         _  x)       ≡⟨ ⋁-idem {x} 
                       x                 

⋁-on-≼ : {s s' :   𝕊}  (∀ k  s k  s' k)   s   s'
⋁-on-≼ {s} {s'} l = ≼-limiting s  k  ≼-cocone {s'} k (l k))

⊥-minimal :  {x}    x
⊥-minimal {x} =
 𝕊-ind  x    x)
        x  isSet𝕊 _ _)
       everything-under-⊤
       ≼-refl
       ((λ {s} ih  ≼-cocone {s} 1 (ih 1)))
       x

-- 𝕊-decidability

𝕊-dec : Type   Type 
𝕊-dec P = Σ[ x  𝕊 ] (P  x  )

⌊_⌋ : {P : Type }  𝕊-dec P  𝕊
 x , p  = x

⌊_⌋-correct : {P : Type }  (s : 𝕊-dec P)  (P   s   )
 x , p ⌋-correct = p

𝕊-dec-stable-under-↔ : {P : Type }{Q : Type ℓ'}
                      (P  Q)
                      𝕊-dec P  𝕊-dec Q
𝕊-dec-stable-under-↔ r =
  (x , ρ)  (x , ↔-trans (↔-sym r) ρ)) ,  (x , ρ)  (x , ↔-trans r ρ))

Dec→𝕊-dec : {P : Type }  Dec P  𝕊-dec P
Dec→𝕊-dec (yes p) =  , ((λ _  refl) , λ _  p)
Dec→𝕊-dec (no ¬p) =  , ((λ p  Empty.rec (¬p p)) , λ ⊥≡⊤  Empty.rec (⊥≠⊤ ⊥≡⊤))

eq𝕊-dec : {P : Type }  isProp P  {u v : 𝕊-dec P}  fst u  fst v  u  v
eq𝕊-dec {P = P} isProp⟨P⟩ {x , p↔x≡⊤} {y , p↔y≡⊤} x≡y =
  Σ≡Prop {B = λ x  (P  x  ) × (x    P)}
          x  isProp× (isPropΠ λ _  isSet𝕊 x ) (isPropΠ λ _  isProp⟨P⟩))
         {u = x , p↔x≡⊤}
         {v = y , p↔y≡⊤}
         x≡y

not-⊤-is-⊥ :  y  (y    Empty)  y  
not-⊤-is-⊥ =
 𝕊-ind  y  (y    Empty)  y  )
        x  isProp→ (isSet𝕊 x ))
        p  Empty.rec (p refl))
        _  refl)
        {s} ih q  is⊥-correct { s}
                      (is⊥-correct⁻¹ (ih 0 ([1] s q 0)) ,
                       λ n  is⊥-correct⁻¹ (ih (suc n) ([1] s q (suc n)))))
  where
  [1] :  (s :   𝕊) -> ( s    Empty)  (n : )  s n    Empty
  [1] s p n sn≡⊤ = p (contains-⊤-implies-is-⊤ s  n , sn≡⊤ )

bisimilar-implies-⋁-equal : {s s' :   𝕊}
                           ((n : )  ∃[ m   ] s' n  s m)
                           ((n : )  ∃[ m   ] s n  s' m)
                            s   s'
bisimilar-implies-⋁-equal {s} {s'} p q =
 ≼-antisym
  (≼-limiting s { s'}  k  ∥-∥rec (isSet𝕊 _ _)
                                      (l , sk≼s'l)  ≼-cocone {s'} {s k} l sk≼s'l) (q k)))
  (≼-limiting s' { s}  k  ∥-∥rec (isSet𝕊 _ _)
                                      (l , s'k≼sl)  ≼-cocone {s} {s' k} l s'k≼sl) (p k)))

⋁-equal-implies-bisimilar-≡⟙ : {s s' :   𝕊}
                               s   s'
                              ((n : )  s n    ∃[ m   ] s' m  )
                             × ((n : )  s' n    ∃[ m   ] s m  )
⋁-equal-implies-bisimilar-≡⟙ {s} {s'} ⋁s≡⋁s' =
 (⋁-equal-implies-simulation ⋁s≡⋁s' , ⋁-equal-implies-simulation (sym ⋁s≡⋁s'))
  where
   ⋁-equal-implies-simulation : {s s' :   𝕊}
                                s   s'
                               (n : )  s n    ∃[ m   ] s' m  
   ⋁-equal-implies-simulation {s} {s'} e n q =
    is-⊤-implies-contains-⊤ s' (sym (subst (_≡  s')
                                           (contains-⊤-implies-is-⊤ s  n , q ) e))

≼-if-preserves-≡⟙ : (x y : 𝕊)  (x    y  )  x  y
≼-if-preserves-≡⟙ = 𝕊-ind  x   y  (x    y  )  x  y)
                           x  isPropΠ  y  isProp→ (isProp⟨≼⟩ x y)))
                          [1]
                          [2]
                          [3]
 where
  [1] : (y : 𝕊)  (    y  )    y
  [1] y p = subst ( ≼_) (sym (p refl)) ≼-refl

  [2] : (y : 𝕊)  (    y  )    y
  [2] y p = ⊥-minimal {y}

  [3] : {s :   𝕊}
       ((n : ) (y : 𝕊)  (s n    y  )  s n  y)
       (y : 𝕊)  ( s    y  )   s  y
  [3] {s} ih y p =
   ≼-limiting s  k  ih k y  sk≡⊤  p (contains-⊤-implies-is-⊤ s  k , sk≡⊤ )))

eq𝕊 : (x y : 𝕊) -> (x    y  )  (y    x  )  x  y
eq𝕊 x y p q = ≼-antisym (≼-if-preserves-≡⟙ x y p) (≼-if-preserves-≡⟙ y x q)

isProp𝕊Dec : {P : Type }  isProp P  isProp (𝕊-dec P)
isProp𝕊Dec {P = P} isProp⟨P⟩ (x , p↔x≡⊤) (y , p↔y≡⊤) =
 eq𝕊-dec isProp⟨P⟩ (eq𝕊 x y x≡⊤ y≡⊤)
  where
   x≡⊤ : x    y  
   x≡⊤ = λ x≡⊤  fst p↔y≡⊤ (snd p↔x≡⊤ x≡⊤)
   y≡⊤ : y    x  
   y≡⊤ = λ y≡⊤  fst p↔x≡⊤ (snd p↔y≡⊤ y≡⊤)

𝕊-decOr : {P : Type }{Q : Type ℓ'}
                  𝕊-dec P
                  𝕊-dec Q
                  𝕊-dec ( P  Q )
𝕊-decOr (sp , lp , rp) (sq , lq , rq) =
 (sp  sq ,
 (∥-∥rec (isSet𝕊 _ _)  { (inl p)  contains-⊤-implies-is-⊤ _  0 , lp p 
                         ; (inr q)  contains-⊤-implies-is-⊤ _  1 , lq q  })) ,
  λ w  ∥-∥rec isPropPropTrunc  { (zero , x)   inl (rp x) 
                                  ; (suc n , x)   inr (rq x)  })
                               (is-⊤-implies-contains-⊤ _ w))

𝟚→𝕊 : Bool  𝕊
𝟚→𝕊 false = 
𝟚→𝕊 true = 

𝟚→𝕊-⊤-from-true : {b : Bool}  𝟚→𝕊 b    b  true
𝟚→𝕊-⊤-from-true {false} e = Empty.rec (⊥≠⊤ e)
𝟚→𝕊-⊤-from-true {true} e = refl

semidec→𝕊-dec : (P : Type )  isProp P  semi-dec P  𝕊-dec P
semidec→𝕊-dec P is-prop-P = ∥-∥rec (isProp𝕊Dec is-prop-P) δ
 where
  δ : semi-dec-str P  𝕊-dec P
  δ (s , l , r) =   n  𝟚→𝕊 (s n)) , l' , r'
   where
    l' : P    n  𝟚→𝕊 (s n))  
    l' p = contains-⊤-implies-is-⊤  n  (𝟚→𝕊 (s n)))
                                   (∥-∥map  (n , e)  n , cong 𝟚→𝕊 e) (l p))

    r' :   n  𝟚→𝕊 (s n))    P
    r' e = r (∥-∥map  (n , e)  n , 𝟚→𝕊-⊤-from-true e)
                     (is-⊤-implies-contains-⊤ _ e))

dec→𝕊-dec : (P : Type )  Dec P  𝕊-dec P
dec→𝕊-dec P (yes p) =  , ((λ _  refl) ,  _  p))
dec→𝕊-dec P (no ¬p) =  , ((λ p  Empty.rec (¬p p)) , λ q  Empty.rec (⊥≠⊤ q))