\begin{code}
{-# OPTIONS --safe #-}

module Truth where

open import Prelude
  hiding (⊤; tt)
  renaming (Poly-⊤ to ⊤; Poly-tt to tt)

private module DisplayOmega where
\end{code}
%<*omega>
\begin{code}[number=truth-def]
  record Ω : Type₁ where
    field  ProofOf  : Type
           IsProp   : isProp ProofOf 
\end{code}
%</omega>
\begin{code}
  open Ω

\end{code}
%<*true>
\begin{code}
  True : Ω
  ProofOf  True = ⊤
  IsProp   True _ _ = refl
\end{code}
%</true>
%<*false>
\begin{code}
  False : Ω
  ProofOf  False = ⊥
  IsProp   False ()
\end{code}
%</false>
\begin{code}
  open import Data.Nat.Properties using (isSetNat; _mod_)
\end{code}
%<*true-false>
\begin{code}[number=three-is-even]
  3-is-even : Ω
  ProofOf  3-is-even = 3 mod 2 ≡ 0
  IsProp   3-is-even = isSetNat _ _
\end{code}
%</true-false>
\begin{code}

  module _ {A : Type} where
\end{code}
%<*equality>
\begin{code}
    _|≡|_ : A → A → Ω
    ProofOf  (x |≡| y) = ∥ x ≡ y ∥
    IsProp   (x |≡| y) = squash
\end{code}
%</equality>
\begin{code}




infix 3 _∥_
record Ω p : Type (ℓsuc p) where
  constructor _∥_; no-eta-equality
  field  ProofOf  : Type p
         IsProp   : isProp ProofOf
open Ω public
True : Ω a
ProofOf  True = ⊤
IsProp   True _ _ = refl

open import Cubical.Foundations.HLevels using (isPropIsOfHLevel)

cong-∥ : (x y : Ω a) → (lhs : ProofOf x ≡ ProofOf y) → PathP (λ i → isProp (lhs i)) (IsProp x) (IsProp y) → x ≡ y
cong-∥ x y lhs rhs i .ProofOf = lhs i
cong-∥ x y lhs rhs i .IsProp = rhs i

Ω≡ : (x y : Ω a) → ProofOf x ≡ ProofOf y → x ≡ y
Ω≡ x y p = 
  cong-∥ x y p
    (J (λ y idxy → ∀ lhs rhs → PathP (λ i → isProp (idxy i)) lhs rhs) (isPropIsOfHLevel 1) p (IsProp x) (IsProp y))

private
  variable X Y Z : Ω a

\end{code}
%<*iff>
\begin{code}
_iff_ : (ProofOf X → ProofOf Y) → (ProofOf Y → ProofOf X) → X ≡ Y
\end{code}
%</iff>
\begin{code}
_iff_ {X = x} {Y = y} lhs rhs =
  Ω≡ x y (isoToPath (iso lhs rhs (λ _ → y .IsProp _ _) λ _ → x .IsProp _ _))

proves-lhs : (x : Ω a) → ProofOf x → ProofOf x ≡ ⊤
proves-lhs x p = isoToPath (iso (const tt) (const p) (λ _ → refl) λ _ → IsProp x _ _)

proves : ProofOf X → X ≡ True
proves p = const tt iff const p

module LevelZero where
  False : Ω _
  ProofOf  False = ⊥
  IsProp   False ()

  disproves : (ProofOf X → ⊥) → X ≡ False
  disproves ¬p = ¬p iff absurd

  open import Data.Nat.Properties
    using (isSetNat; _mod_; suc≢zero)

\end{code}
%<*one-neq-zero>
\begin{code}[inline]
  1≢0 : 1 ≡ 0 → ⊥
\end{code}
%</one-neq-zero>
\begin{code}
  1≢0 = suc≢zero

  3-is-even : Ω 0
  ProofOf  3-is-even = 3 mod 2 ≡ 0
  IsProp   3-is-even = isSetNat _ _

\end{code}
%<*3-is-not-even>
\begin{code}
  3-is-not-even : 3-is-even ≡ False
  3-is-not-even = 1≢0 iff absurd
\end{code}
%</3-is-not-even>
\begin{code}
module _ {a : Level} where
  False : Ω a
  False .ProofOf = Poly-⊥ 
  False .IsProp ()

  disproves :  (ProofOf X → ⊥)
          →   X ≡ False
  disproves p = (absurd ∘ p) iff λ ()

  at-most-two : ∄ p × p ≢ True × p ≢ False
  at-most-two (p , p≢True , p≢False) = p≢False (disproves (p≢True ∘ proves))

Decided : ∀ ℓ → Type (ℓsuc ℓ)
Decided ℓ = Σ[ P ⦂ Ω ℓ ] × Dec (P .ProofOf)

module IsoDec {a : Level} where
  inv-fnc : Bool → Decided a
  inv-fnc = bool (False , no λ ()) (True , yes _)

  left-inv : (x : Decided a) → inv-fnc (does (x .snd)) .fst ≡ x .fst
  left-inv (P , yes p) = sym (proves p)
  left-inv (P , no ¬p) = sym (disproves ¬p)

  DecΩ⇔Bool : Decided a ⇔ Bool
  DecΩ⇔Bool .fun (_ , x) = does x
  DecΩ⇔Bool .inv = inv-fnc
  DecΩ⇔Bool .rightInv false = refl
  DecΩ⇔Bool .rightInv true  = refl
  DecΩ⇔Bool .leftInv  x = Σ≡Prop (λ p → isPropDec (p .IsProp)) (left-inv x)

  DecΩ≡Bool : Decided a ≡ Lift Bool
  DecΩ≡Bool = isoToPath (DecΩ⇔Bool ⟨ trans-⇔ ⟩ lift⇔)
open IsoDec public using (DecΩ⇔Bool; DecΩ≡Bool)

extract : {x : Ω a} → x ≡ True → ProofOf x
extract p = subst ProofOf (sym p) tt

open import Cubical.Foundations.HLevels

Truth⇔hProp : Ω a ⇔ hProp a
Truth⇔hProp .fun x .fst = x .ProofOf
Truth⇔hProp .fun x .snd = x .IsProp
Truth⇔hProp .inv x .ProofOf = x .fst
Truth⇔hProp .inv x .IsProp = x .snd
Truth⇔hProp .rightInv b = refl
Truth⇔hProp .leftInv x i .ProofOf = x .ProofOf
Truth⇔hProp .leftInv x i .IsProp = x .IsProp

abstract
  isSetΩ : isSet (Ω a)
  isSetΩ = subst isSet (sym (isoToPath Truth⇔hProp)) isSetHProp

open import Cubical.HITs.PropositionalTruncation

interleaved mutual
  infixl 7 _|∧|_
  _|∧|_ : Ω a → Ω a → Ω _
  infixl 6 _|∨|_
  _|∨|_ : Ω a → Ω b → Ω _
  infixr 5 _|→|_
  _|→|_ : Ω a → Ω b → Ω _

\end{code}
%<*logic>
\begin{code}
  ProofOf (X |∧| Y) = ProofOf X × ProofOf Y
  ProofOf (X |→| Y) = ProofOf X → ProofOf Y
  ProofOf (X |∨| Y) = ∥ ProofOf X ⊎ ProofOf Y ∥
\end{code}
%</logic>
\begin{code}
  IsProp (_  |→| Y) = isProp→ (IsProp Y)
  IsProp (x |∧| y) = isProp× (x .IsProp) (y .IsProp)
  IsProp (x |∨| y) = squash

|∀| : (A → Ω b) → Ω _
ProofOf (|∀| f) = ∀ x → ProofOf (f x)
IsProp   (|∀| f) = isPropΠ λ x → IsProp (f x)

|∃| : (A → Ω b) → Ω _
ProofOf (|∃| f) = ∥ ∃ x × ProofOf (f x) ∥
IsProp  (|∃| f) = squash

_|↔|_ : Ω a → Ω b → Ω _
X |↔| Y = (X |→| Y) |∧| (Y |→| X)

|→|-id : (x : Ω a) → x |→| x ≡ True
|→|-id x = proves id

Not : Ω a → Ω a
Not x .ProofOf = ¬ x .ProofOf
Not x .IsProp = isProp→ λ ()

|→|-idˡ : (x : Ω a) → True |→| x ≡ x
|→|-idˡ {a = a} x = (_$ (tt {ℓ = a})) iff const

|→|-annʳ : (x : Ω a) → x |→| True {a = b} ≡ True
|→|-annʳ x = const tt iff (λ _ _ → tt)

|→|-annˡ : (x : Ω a) → False {a = b} |→| x ≡ True
|→|-annˡ x = (λ _ → tt) iff λ _ ()

Ω∣_∣ : Type a → Ω a
ProofOf  Ω∣ P ∣ = ∥ P ∥
IsProp   Ω∣ P ∣ = squash

infix 5.5 _|≡|_
_|≡|_ : A → A → Ω _
x |≡| y = Ω∣ x ≡ y ∣

|T| : Bool → Ω a
|T| = bool′ False True

|→|-trans : (x y z : Ω a) → ((x |→| y) |∧| (y |→| z)) |→| (x |→| z) ≡ True
|→|-trans x y z = const tt iff const (uncurry (flip _∘_))

|→|-curry : (X Y Z : Ω a) → (X |∧| Y |→| Z) ≡ (X |→| Y |→| Z)
|→|-curry _ _ _ = curry iff uncurry

|→|-|∧| : (x y z : Ω a) → (x |→| (y |∧| z)) ≡ (x |→| y) |∧| (x |→| z)
|→|-|∧| _ _ _ = (λ f → fst ∘ f , snd ∘ f) iff λ { (f , g) x → f x , g x }

|∨|-|→| : (x y z : Ω a) → ((x |∨| y) |→| z) ≡ (x |→| z) |∧| (y |→| z)
|∨|-|→| _ _ z =
    (λ f → f ∘ ∣_∣ ∘ inl , f ∘ ∣_∣ ∘ inr) iff
    λ { (f , g) → rec (z .IsProp) (either f g) }

∧-com : (x y : Ω a) → x |∧| y ≡ y |∧| x
∧-com _ _ = (λ { (x , y) → (y , x) }) iff λ { (x , y) → y , x }

∧-idem : (x : Ω a) → x |∧| x ≡ x
∧-idem _ = fst iff (λ x → x , x)

∨-idem : (x : Ω a) → x |∨| x ≡ x
∨-idem x = (rec (x .IsProp) (either id id)) iff (∣_∣ ∘ inl)

∨-com : (x y : Ω a) → x |∨| y ≡ y |∨| x
∨-com x y = (rec squash (∣_∣ ∘ either inr inl)) iff (rec squash (∣_∣ ∘ either inr inl))

∧-ann : (x : Ω a) → x |∧| (False {a}) ≡ False
∧-ann x = snd iff λ ()

refute : (x : Ω a) → ¬ ProofOf x → x ≡ False
refute x ¬p = (absurd ∘ ¬p) iff λ ()

∨-assoc : (x y z : Ω a) → (x |∨| y) |∨| z ≡ x |∨| (y |∨| z)
∨-assoc x y z =
    (rec squash (either (rec squash  (∣_∣ ∘ mapʳ (∣_∣ ∘ inl))) (∣_∣ ∘ inr ∘ ∣_∣ ∘ inr ))) iff
    (rec squash (either (∣_∣ ∘ inl ∘ ∣_∣ ∘ inl) (rec squash (∣_∣ ∘ mapˡ (∣_∣ ∘ inr) ))))

∧-assoc : (x y z : Ω a) → (x |∧| y) |∧| z ≡ x |∧| (y |∧| z)
∧-assoc x y z =
    (λ { ((x , y) , z) → x , y , z }) iff
    λ { (x , y , z) → (x , y)  , z }

∧-id : (x : Ω a) → (True {a}) |∧| x ≡ x
∧-id x = snd iff (tt ,_)

∨-ann : (x : Ω a) → x |∨| (True {a}) ≡ True
∨-ann x = (const tt) iff (∣_∣ ∘ inr)

∨-id : (x : Ω a) → x |∨| (False {a}) ≡ x
∨-id x = (rec (x .IsProp) (either id (λ ()))) iff (∣_∣ ∘ inl)

∨-idˡ : (x : Ω a) → (False {a}) |∨| x ≡ x
∨-idˡ x = (rec (x .IsProp) (either (λ ()) id)) iff (∣_∣ ∘ inr)

∧-distrib-∨ : (x y z : Ω a) → x |∧| (y |∨| z) ≡ (x |∧| y) |∨| (x |∧| z)
∧-distrib-∨ x y z =
    (uncurry (λ xp → rec squash (∣_∣ ∘ map-⊎ (xp ,_) (xp ,_)) )) iff
    (rec (isProp× (x .IsProp) squash) (either (map₂ (∣_∣ ∘ inl)) (map₂ (∣_∣ ∘ inr))))


∨-distrib-∧ : (x y z : Ω a) → x |∨| (y |∧| z) ≡ (x |∨| y) |∧| (x |∨| z)
∨-distrib-∧ x y z =
    (rec (isProp× squash squash) (either (λ x → ∣ inl x ∣ , ∣ inl x ∣) λ { (yp , zp) → ∣ inr yp ∣ , ∣ inr zp ∣ })) iff
    (uncurry (rec2 squash  λ { (inl x) _ → ∣ inl x ∣ ; _ (inl x) → ∣ inl x ∣ ; (inr y) (inr z) → ∣ inr (y , z) ∣  }))
\end{code}