{-# OPTIONS --type-in-type --no-positivity-check #-}

open import NeilPrelude
open import Properties
open import Extensionality
open import SubSet

module CPO where

------------------------------------------------------------------------

infix  3 _≣_ _⊑_ _⊑⊥_
infixr 4 _$_
infixr 10 _⇒_
infixr 11 _⊕_
infixr 12 _⊗_ _,_

------------------------------------------------------------------------

-- Sets extended with a bottom element
data _-⊥ (A : Set) : Set where
  bot :     A -⊥
  val : A  A -⊥

data _⊑⊥_ {A : Set} : Rel (A -⊥) where
  ⊑⊥-refl   : Reflexive _⊑⊥_
  ⊑⊥-botval : {a : A}  bot ⊑⊥ val a

⊑⊥-bot : {A : Set}  Initial bot (_⊑⊥_ {A})
⊑⊥-bot {_} {bot}   = ⊑⊥-refl
⊑⊥-bot {_} {val _} = ⊑⊥-botval

⊑⊥-trans : {A : Set}  Transitive {A -⊥} _⊑⊥_
⊑⊥-trans ⊑⊥-refl ⊑⊥-refl = ⊑⊥-refl
⊑⊥-trans ⊑⊥-refl ⊑⊥-botval = ⊑⊥-botval
⊑⊥-trans ⊑⊥-botval ⊑⊥-refl = ⊑⊥-botval

⊑⊥-antisym : {A : Set}  Antisymmetric {A -⊥} _⊑⊥_
⊑⊥-antisym ⊑⊥-refl ⊑⊥-refl = refl
⊑⊥-antisym ⊑⊥-botval ()

------------------------------------------------------------------------

-- The CPO abstract syntax
-- These are the primary definitions

data Type : Set where
  Base : Set          Type
  _⊗_  : Type  Type  Type
  _⇒_  : Type  Type  Type
  _⊕_  : Type  Type  Type

mutual

  data CPO : (τ : Type)  Set where
    base : {A : Set}  A -⊥                                   CPO (Base A)
    _,_  : {σ τ : Type}  CPO σ  CPO τ                       CPO (σ  τ)
    fun  : {σ τ : Type}  (f : CPO σ  CPO τ)  Continuous f  CPO (σ  τ)
    inl  : {σ τ : Type}  CPO σ                               CPO (σ  τ)
    inr  : {σ τ : Type}  CPO τ                               CPO (σ  τ)
    bot  : {σ τ : Type}                                       CPO (σ  τ)

------------------------------------------------------------------------

-- Ordering on CPO
-- Note the use of Continuous inside the data type requires a large mutual block where we define continuity

  data _⊑_ : {τ : Type}  Rel (CPO τ) where
    ⊑-base : {A : Set}  {a₁ a₂ : A -⊥}  a₁ ⊑⊥ a₂  base a₁  base a₂
    ⊑-prod : {σ τ : Type}  {x₁ x₂ : CPO σ}  {y₁ y₂ : CPO τ}  x₁  x₂  y₁  y₂  (x₁ , y₁)  (x₂ , y₂)
    ⊑-fun  : {σ τ : Type}  {f g : CPO σ  CPO τ}  {cf : Continuous f}  {cg : Continuous g}  ({x : CPO σ}  f x  g x)  fun f cf  fun g cg
    ⊑-inl  : {σ τ : Type}  {x₁ x₂ : CPO σ}  x₁  x₂  inl {σ} {τ} x₁  inl x₂
    ⊑-inr  : {σ τ : Type}  {y₁ y₂ : CPO τ}  y₁  y₂  inr {σ} {τ} y₁  inr y₂
    ⊑-sum  : {σ τ : Type}  {xy : CPO (σ  τ)}  bot  xy

------------------------------------------------------------------------

  Monotonic : {σ τ : Type}  (CPO σ  CPO τ)  Set
  Monotonic {σ} f = {x y : CPO σ}  x  y  f x  f y

------------------------------------------------------------------------

  -- A Chain is a subset and a limit
  Chain : Type  Set
  Chain τ = SubSet (CPO τ) × CPO τ

  _∈_ : {τ : Type}  CPO τ  Chain τ  Set
  x  (P , _) = P x

   : {τ : Type}  Chain τ  CPO τ
   (_ , ⨆X) = ⨆X

  IsTotalOrder : {τ : Type}  Chain τ  Set
  IsTotalOrder {τ} X = (x₁ x₂ : CPO τ)  x₁  X  x₂  X  (x₁  x₂)  (x₂  x₁)

  -- upper bound
  IsUB : {τ : Type}  Chain τ  Set
  IsUB {τ} X = (x : CPO τ)  x  X  x   X

  -- less than all upper bounds
  IsLUB : {τ : Type}  Chain τ  Set
  IsLUB {τ} X = (y : CPO τ)  ((x : CPO τ)  x  X  x  y)   X  y

  IsChain : {τ : Type}  Chain τ  Set
  IsChain X = IsTotalOrder X × IsUB X × IsLUB X

-----------------------------------------------------------------------

  IsNonEmptyChain : {τ : Type}  Chain τ  Set
  IsNonEmptyChain {τ} (P , ⨆X) = IsChain (P , ⨆X) × Σ (CPO τ) P

-----------------------------------------------------------------------

  chainImage' : {σ τ : Type}  (CPO σ  CPO τ)  Chain σ  Chain τ
  chainImage' f (P , ⨆X) = SubImage f P , f ⨆X

  PreservesLUB : {σ τ : Type}  (CPO σ  CPO τ)  Set
  PreservesLUB {σ} f = ((X : Chain σ)  IsNonEmptyChain X  IsLUB (chainImage' f X))

  Continuous : {σ τ : Type}  (f : CPO σ  CPO τ)  Set
  Continuous {σ} f = Monotonic f × PreservesLUB f

-----------------------------------------------------------------------

_$_ : {σ τ : Type}  CPO (σ  τ)  CPO σ  CPO τ
fun f _ $ x = f x

continuous : {σ τ : Type}  (f : CPO (σ  τ))  Continuous (_$_ f)
continuous (fun f cf) = cf

monotonic : {σ τ : Type}  (f : CPO (σ  τ))  Monotonic (_$_ f)
monotonic (fun f (mf , _)) = mf

preservesLUB : {σ τ : Type}  (f : CPO (σ  τ))  PreservesLUB (_$_ f)
preservesLUB (fun f (_ , plubf)) = plubf

-----------------------------------------------------------------------

chainImage : {σ τ : Type}  (f : CPO (σ  τ))  Chain σ  Chain τ
chainImage f = chainImage' (_$_ f)

monotonic⇒ImageTotalOrder : {σ τ : Type}  (f : CPO σ  CPO τ)  Monotonic f  (X : Chain σ)  IsTotalOrder X  IsTotalOrder (chainImage' f X)
monotonic⇒ImageTotalOrder f monf (P , ⨆X) tot ._ ._ (x₁ , Px₁ , refl) (x₂ , Px₂ , refl) with tot x₁ x₂ Px₁ Px₂
... | inl lt = inl (monf lt)
... | inr gt = inr (monf gt)

monotonic⇒ImageUB : {σ τ : Type}  (f : CPO σ  CPO τ)  Monotonic f  (X : Chain σ)  IsUB X  IsUB (chainImage' f X)
monotonic⇒ImageUB f monf (P , ⨆X) ub ._ (x , Px , refl) = monf (ub x Px)

imageIsTotalOrder : {σ τ : Type}  (f : CPO (σ  τ))  (X : Chain σ)  IsTotalOrder X  IsTotalOrder (chainImage f X)
imageIsTotalOrder (fun f (monf , _)) = monotonic⇒ImageTotalOrder f monf

imageIsUB : {σ τ : Type}  (f : CPO (σ  τ))  (X : Chain σ)  IsUB X  IsUB (chainImage f X)
imageIsUB (fun f (monf , _)) = monotonic⇒ImageUB f monf

imageIsNonEmptyChain : {σ τ : Type}  (f : CPO (σ  τ))  (X : Chain σ)  IsNonEmptyChain X  IsNonEmptyChain (chainImage f X)
imageIsNonEmptyChain (fun f conf) X ((tot , ub , lub) , x , Px) = (imageIsTotalOrder (fun f conf) X tot , imageIsUB (fun f conf) X ub , preservesLUB (fun f conf) X ((tot , ub , lub) , x , Px)) , (f x , (x , (Px , refl)))

-----------------------------------------------------------------------

-- Uniqueness of proofs is useful as it allows the CPO data type contains Continuity proofs.
-- Without it, we wouldn't be able to prove two CPO elements equal.

-- Uniqueness of ⊑⊥ proofs
ultp' : {A : Set}  {a₁ a₂ : A -⊥}  (p q : a₁ ⊑⊥ a₂)  p  q
ultp' ⊑⊥-refl ⊑⊥-refl     = refl
ultp' ⊑⊥-botval ⊑⊥-botval = refl

-- Uniqueness of ⊑ proofs
ultp : {{_ : Extensionality}}  {τ : Type}  {x y : CPO τ}  (p q : x  y)  p  q
ultp (⊑-base p) (⊑-base q) = cong ⊑-base (ultp' p q)
ultp (⊑-prod p₁ q₁) (⊑-prod p₂ q₂) = cong2 ⊑-prod (ultp p₁ p₂) (ultp q₁ q₂)
ultp (⊑-fun p) (⊑-fun q) = cong ⊑-fun (ext-implicit (ultp p q))
ultp (⊑-inl p) (⊑-inl q) = cong ⊑-inl (ultp p q)
ultp (⊑-inr p) (⊑-inr q) = cong ⊑-inr (ultp p q)
ultp ⊑-sum ⊑-sum = refl

-- Uniqueness of monotonicity proofs
ump : {{_ : Extensionality}}  {σ τ : Type}  (f : CPO σ  CPO τ)  (p q : Monotonic f)  _≡_ {_} {Monotonic f} p q
ump {σ} f p q = ext-implicit (ext-implicit (ext  lt  ultp (p lt) (q lt))))

-- Uniqueness of preservation of least upper bound proofs
ulubp : {{_ : Extensionality}}  {σ τ : Type}  (f : CPO σ  CPO τ)  (p q : PreservesLUB f)  _≡_ {_} {PreservesLUB f} p q
ulubp f p q = ext  X  ext  pX  ext  y  ext  ubY  ultp (p X pX y ubY) (q X pX y ubY)))))

-- Uniqueness of continuity proofs
ucp : {{_ : Extensionality}}  {σ τ : Type}  (f : CPO σ  CPO τ)  (p q : Continuous f)  _≡_ {_} {Continuous f} p q
ucp {σ} f (monp , conp) (monq , conq) = ×-cong (ump f monp monq) (ulubp f conp conq)

--                                   this collapses to: {f g : CPO (σ ⇒ τ)} → f ≡ g → fun f cf ≡ fun g cg
injective-$ : {{_ : Extensionality}}  {σ τ : Type}  Injective {CPO (σ  τ)} _$_
injective-$ {_} {_} {fun f cf} {fun .f cg} refl rewrite ucp f cf cg = refl

-----------------------------------------------------------------------

⊑-refl : {τ : Type}  Reflexive (_⊑_ {τ})
⊑-refl {Base _} {base _} = ⊑-base ⊑⊥-refl
⊑-refl {σ  τ} {_ , _}   = ⊑-prod ⊑-refl ⊑-refl
⊑-refl {σ  τ} {fun _ _} = ⊑-fun ⊑-refl
⊑-refl {σ  τ} {inl _}   = ⊑-inl ⊑-refl
⊑-refl {σ  τ} {inr _}   = ⊑-inr ⊑-refl
⊑-refl {σ  τ} {bot}     = ⊑-sum

⊑-eq : {τ : Type}  {x y : CPO τ}  x  y  x  y
⊑-eq refl = ⊑-refl

⊑-trans : {τ : Type}  Transitive {CPO τ} _⊑_
⊑-trans (⊑-base {_} {bot} _) (⊑-base _) = ⊑-base ⊑⊥-bot
⊑-trans (⊑-base {_} {val _} ⊑⊥-refl) q = q
⊑-trans (⊑-prod a₁ a₂) (⊑-prod b₁ b₂) = ⊑-prod (⊑-trans a₁ b₁) (⊑-trans a₂ b₂)
⊑-trans (⊑-fun p) (⊑-fun q) = ⊑-fun (⊑-trans p q)
⊑-trans (⊑-inl p) (⊑-inl q) = ⊑-inl (⊑-trans p q)
⊑-trans (⊑-inr p) (⊑-inr q) = ⊑-inr (⊑-trans p q)
⊑-trans ⊑-sum _ = ⊑-sum

⊑-split : {σ τ : Type}  {x₁ x₂ : CPO σ}  {y₁ y₂ : CPO τ}  (x₁ , y₁)  (x₂ , y₂)  x₁  x₂ × y₁  y₂
⊑-split (⊑-prod lt₁ lt₂) = lt₁ , lt₂

⊑-antisym : {{_ : Extensionality}}  {τ : Type}  Antisymmetric {CPO τ} _⊑_
⊑-antisym (⊑-base p) (⊑-base q) = cong base (⊑⊥-antisym p q)
⊑-antisym (⊑-prod a₁ b₁) (⊑-prod a₂ b₂) = cong2 _,_ (⊑-antisym a₁ a₂) (⊑-antisym b₁ b₂)
⊑-antisym (⊑-fun p) (⊑-fun q) = injective-$ (ext' (⊑-antisym p q))
⊑-antisym (⊑-inl p) (⊑-inl q) = cong inl (⊑-antisym p q)
⊑-antisym (⊑-inr p) (⊑-inr q) = cong inr (⊑-antisym p q)
⊑-antisym ⊑-sum ⊑-sum = refl

-----------------------------------------------------------------------

mutual

   : {τ : Type}  CPO τ
   {Base _} = base bot
   {_  _}  =  , 
   {_  _}  = fun  _  ) continuous-⊥-fun
   {_  _}  = bot

  ⊑-bot : {τ : Type}  Initial ( {τ}) _⊑_
  ⊑-bot {Base _} {base _}   = ⊑-base ⊑⊥-bot
  ⊑-bot {_  _}  {_ , _}    = ⊑-prod ⊑-bot ⊑-bot
  ⊑-bot {σ  τ}  {fun f cf} = ⊑-fun {σ} {τ} {(λ _  )} {f} ⊑-bot
  ⊑-bot {_  _}  { _ }      = ⊑-sum

  abstract

    monotonic-⊥-fun : {σ τ : Type}  Monotonic {σ} {τ}  _  )
    monotonic-⊥-fun _ = ⊑-refl

    continuous-⊥-fun : {σ τ : Type}  Continuous {σ} {τ}  _  )
    continuous-⊥-fun {σ} {τ} = monotonic-⊥-fun ,  _ _ _ _  ⊑-bot)

-----------------------------------------------------------------------

emptyChain : {τ : Type}  Chain τ
emptyChain =  _  False) , 

-----------------------------------------------------------------------

splitChain : {σ τ : Type}  Chain (σ  τ)  Chain σ × Chain τ
splitChain {σ} {τ} (P , ⨆X , ⨆Y) = ((λ x  Σ[ y  CPO τ ] P (x , y)) , ⨆X) ,
                                   ((λ y  Σ[ x  CPO σ ] P (x , y)) , ⨆Y)

fstChain : {σ τ : Type}  Chain (σ  τ)  Chain σ
fstChain = fst  splitChain

sndChain : {σ τ : Type}  Chain (σ  τ)  Chain τ
sndChain = snd  splitChain

uncurry-prop : {σ τ : Type}  {P : CPO (σ  τ)  Set}  ((x : CPO σ)  (y : CPO τ)  P (x , y))  (xy : CPO (σ  τ))  P xy
uncurry-prop f (x , y) = f x y

firstChain : {σ τ : Type}  CPO σ  Chain τ  Chain (σ  τ)
firstChain x (P , ⨆Y) = uncurry-prop  x' y  x  x' × P y) , (x , ⨆Y)

secondChain : {σ τ : Type}  CPO τ  Chain σ  Chain (σ  τ)
secondChain y (P , ⨆X) = uncurry-prop  x y'  y  y' × P x) , (⨆X , y)

-----------------------------------------------------------------------

singletonChain : {τ : Type}  CPO τ  Chain τ
singletonChain x =  x'  x'  x) , x

-----------------------------------------------------------------------

_≣_ : {τ : Type}  Chain τ  Chain τ  Set
(P , ⨆X)  (Q , ⨆Y) = SubSetEq P Q × (⨆X  ⨆Y)

-----------------------------------------------------------------------

ChainComplete : {τ : Type}  (CPO τ  Set)  Set
ChainComplete {τ} P = (X : Chain τ)  IsNonEmptyChain X  ((x : CPO τ)  x  X  P x)  P ( X)

------------------------------------------------------------------------

Strict : {σ τ : Type}  (g : CPO (σ  τ))  Set
Strict g = g $   

------------------------------------------------------------------------

FixedPoint : {τ : Type}  (f : CPO (τ  τ))  CPO τ  Set
FixedPoint f x = f $ x  x

------------------------------------------------------------------------

Functor : Set
Functor = Type  Type

------------------------------------------------------------------------