module Common.Types where

open import Data.Maybe
open import Data.Bool renaming (Bool to 𝔹; _≟_ to _≟ᵇ_)
open import Data.Unit using (; tt)
open import Data.Product using (_×_; ; ∃-syntax; Σ; Σ-syntax) renaming (_,_ to ⟨_,_⟩)
open import Data.List using (List)
open import Function using (case_of_)
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary using (_⇔_)
open import Relation.Binary.PropositionalEquality
  using (_≡_; _≢_; refl; trans; sym; subst; cong; cong₂)

open import Common.Utils
open import Common.SecurityLabels public


{- **** Types **** -}
data Base : Set where
  Bool : Base
  Unit : Base

rep : Base  Set
rep Bool = 𝔹
rep Unit = 

const-eq? :  {ι}  (k₁ k₂ : rep ι)  Dec (k₁  k₂)
const-eq? {Bool} false false  = yes refl
const-eq? {Bool} false true   = no  λ ()
const-eq? {Bool} true false   = no  λ ()
const-eq? {Bool} true true    = yes refl
const-eq? {Unit} tt tt        = yes refl


data RawType : Set
data Type : Set

infix 10 `_
infix 8  ⟦_⟧_⇒_
infix 7  _of_

data RawType where
  `_      : Base  RawType
  Ref_    : Type  RawType
  ⟦_⟧_⇒_ : Label  Type  Type  RawType

data Type where
  _of_ : RawType  Label  Type

-- -- Well-formed types
-- infix 4 ⊢_

-- data ⊢_ : Type → Set where

--   ⊢ι    : ∀ ι g
--       -----------------
--     → ⊢ ` ι of g

--   ⊢ref  : ∀ {T} {ĝ} {g}
--     → ⊢ T of ĝ
--     → g ≾ ĝ
--       -----------------------------
--     → ⊢ Ref (T of ĝ) of g

--   ⊢fun  : ∀ {A} {B} {gᶜ} {g}
--     → ⊢ A
--     → ⊢ B
--     → g ≾ gᶜ
--       -----------------------------
--     → ⊢ ⟦ gᶜ ⟧ A ⇒ B of g

-- ⊢? : ∀ A → Dec (⊢ A)
-- ⊢? (` ι of g) = yes (⊢ι ι g)
-- ⊢? (Ref (T of ĝ) of g) =
--   case ⊢? (T of ĝ) of λ where
--   (yes ⊢Tg) →
--     case g ≾? ĝ of λ where
--     (yes g≾ĝ) → yes (⊢ref ⊢Tg g≾ĝ)
--     (no  g⋨ĝ) → no λ { (⊢ref _ g≾ĝ) → contradiction g≾ĝ g⋨ĝ }
--   (no ¬⊢Tg) → no λ { (⊢ref ⊢Tg _) → contradiction ⊢Tg ¬⊢Tg }
-- ⊢? (⟦ gᶜ ⟧ A ⇒ B of g) =
--   case ⊢? A of λ where
--   (yes ⊢A) →
--     case ⊢? B of λ where
--     (yes ⊢B) →
--       case g ≾? gᶜ of λ where
--       (yes g≾gᶜ) → yes (⊢fun ⊢A ⊢B g≾gᶜ)
--       (no  g⋨gᶜ) → no λ { (⊢fun _ _ g≾gᶜ) → contradiction g≾gᶜ g⋨gᶜ }
--     (no ¬⊢B) → no λ { (⊢fun _ ⊢B _) → contradiction ⊢B ¬⊢B }
--   (no ¬⊢A) → no λ { (⊢fun ⊢A _ _) → contradiction ⊢A ¬⊢A }

infix 4 _≡ᵣ?_
infix 4 _≡?_

_≡ᵣ?_ : (S T : RawType)  Dec (S  T)
_≡?_ : (A B : Type)  Dec (A  B)

(` Bool) ≡ᵣ? (` Bool) = yes refl
(` Unit) ≡ᵣ? (` Unit) = yes refl
(` Bool) ≡ᵣ? (` Unit) = no λ ()
(` Unit) ≡ᵣ? (` Bool) = no λ ()
(` _) ≡ᵣ? (Ref _) = no λ ()
(` _) ≡ᵣ? ( _  _  _) = no λ ()
(Ref A) ≡ᵣ? (Ref B) with A ≡? B
... | yes refl = yes refl
... | no  neq = no  { refl  contradiction refl neq })
(Ref _) ≡ᵣ? (` _) = no λ ()
(Ref _) ≡ᵣ? ( _  x₂  _) = no λ ()
( gc₁  A  B) ≡ᵣ? ( gc₂  C  D)
  with gc₁ ==? gc₂
... | no  neq = no  { refl  contradiction refl neq })
... | yes refl with A ≡? C
... | no  neq = no  { refl  contradiction refl neq })
... | yes refl with B ≡? D
... | no neq  = no  { refl  contradiction refl neq })
... | yes refl = yes refl
( _  _  _) ≡ᵣ? (` _) = no λ ()
( _  _  _) ≡ᵣ? (Ref _) = no λ ()

(S of g₁) ≡? (T of g₂) with S ≡ᵣ? T
... | no  neq = no  { refl  contradiction refl neq })
... | yes refl with g₁ ==? g₂
... | no  neq = no  { refl  contradiction refl neq })
... | yes refl = yes refl


{- **** Subtyping **** -}
infix 5 _<:ᵣ_
infix 5 _<:_

data _<:ᵣ_ : RawType  RawType  Set
data _<:_ :  Type  Type  Set

data _<:ᵣ_ where
  <:-ι :  {ι}  ` ι <:ᵣ ` ι

  <:-ref :  {A B}
     A <: B
     B <: A
      ----------------
     Ref A <:ᵣ Ref B

  <:-fun :  {gc₁ gc₂} {A B C D}
     gc₂ <:ₗ gc₁
     C <: A
     B <: D
      ----------------------------------
      gc₁  A  B <:ᵣ  gc₂  C  D

data _<:_ where
  <:-ty :  {g₁ g₂} {S T}
     g₁ <:ₗ g₂
     S  <:ᵣ T
      ---------------------
     S of g₁ <: T of g₂

<:ᵣ-refl :  {T}  T <:ᵣ T
<:-refl  :  {A}  A <: A

<:ᵣ-refl {` ι}           = <:-ι
<:ᵣ-refl {Ref A}         = <:-ref <:-refl <:-refl
<:ᵣ-refl { gc  A  B} = <:-fun <:ₗ-refl <:-refl <:-refl
<:-refl  {T of g}        = <:-ty <:ₗ-refl <:ᵣ-refl

<:ᵣ-trans :  {S T U}  S <:ᵣ T  T <:ᵣ U  S <:ᵣ U
<:-trans  :  {A B C}  A <: B   B <: C   A <: C

<:ᵣ-trans <:-ι <:-ι = <:-ι
<:ᵣ-trans (<:-ref A<:B B<:A) (<:-ref B<:C C<:B) =
  <:-ref (<:-trans A<:B B<:C) (<:-trans C<:B B<:A)
<:ᵣ-trans (<:-fun gc₂<:gc₁ B₁<:A₁ A₂<:B₂) (<:-fun gc₃<:gc₂ C₁<:B₁ B₂<:C₂) =
  <:-fun (<:ₗ-trans gc₃<:gc₂ gc₂<:gc₁) (<:-trans C₁<:B₁ B₁<:A₁) (<:-trans A₂<:B₂ B₂<:C₂)
<:-trans (<:-ty g₁<:g₂ S<:T) (<:-ty g₂<:g₃ T<:U) =
  <:-ty (<:ₗ-trans g₁<:g₂ g₂<:g₃) (<:ᵣ-trans S<:T T<:U)

<:ᵣ-antisym :  {S T}  S <:ᵣ T  T <:ᵣ S  S  T
<:-antisym  :  {A B}  A <:  B  B <:  A  A  B

<:ᵣ-antisym <:-ι <:-ι = refl
<:ᵣ-antisym (<:-ref A<:B B<:A) (<:-ref _ _) = cong Ref_ (<:-antisym A<:B B<:A)
<:ᵣ-antisym { gc₁  A  B} { gc₂  C  D} (<:-fun gc₂<:gc₁ C<:A B<:D) (<:-fun gc₁<:gc₂ A<:C D<:B) =
  let eq1 :  gc₁  A  B   gc₁  C  D
      eq1 = cong₂  □₁ □₂   gc₁  □₁  □₂) (<:-antisym A<:C C<:A) (<:-antisym B<:D D<:B)
      eq2 :  gc₁  C  D   gc₂  C  D
      eq2 = cong       C  D) (<:ₗ-antisym gc₁<:gc₂ gc₂<:gc₁) in
    trans eq1 eq2
<:-antisym {S of g₁} {T of g₂} (<:-ty g₁<:g₂ S<:T) (<:-ty g₂<:g₁ T<:S) =
  cong₂  □₁ □₂  □₁ of □₂) (<:ᵣ-antisym S<:T T<:S) (<:ₗ-antisym g₁<:g₂ g₂<:g₁)


{- **** Consistency **** -}
infix 5 _~ᵣ_
infix 5 _~_

data _~ᵣ_ : RawType  RawType  Set
data _~_  : Type  Type  Set

data _~ᵣ_ where
  ~-ι :  {ι}  ` ι ~ᵣ ` ι

  ~-ref :  {A B}
     A ~ B
      ---------------
     Ref A ~ᵣ Ref B

  ~-fun :  {gc₁ gc₂} {A B C D}
     gc₁ ~ₗ gc₂
     A ~ C
     B ~ D
      ---------------------------------
      gc₁  A  B ~ᵣ  gc₂  C  D

data _~_ where
  ~-ty :  {g₁ g₂} {S T}
     g₁ ~ₗ g₂
     S  ~ᵣ T
      --------------------
     S of g₁ ~ T of g₂

~ᵣ-refl :  {T}  T ~ᵣ T
~-refl :  {A}  A ~ A

~ᵣ-refl {` ι}           = ~-ι
~ᵣ-refl {Ref A}         = ~-ref ~-refl
~ᵣ-refl { gc  A  B} = ~-fun ~ₗ-refl ~-refl ~-refl
~-refl  {T of g}        = ~-ty ~ₗ-refl ~ᵣ-refl

~ᵣ-sym :  {S T}  S ~ᵣ T  T ~ᵣ S
~-sym  :  {A B}  A ~ B  B ~ A

~ᵣ-sym ~-ι = ~-ι
~ᵣ-sym (~-ref A~B) = ~-ref (~-sym A~B)
~ᵣ-sym (~-fun gc₁~gc₂ A~C B~D) = ~-fun (~ₗ-sym gc₁~gc₂) (~-sym A~C) (~-sym B~D)
~-sym  (~-ty g₁~g₂ S~T) = ~-ty (~ₗ-sym g₁~g₂) (~ᵣ-sym S~T)


{- **** Consistent subtyping **** -}
infix 5 _≲ᵣ_     -- of raw types
infix 5 _≲_      -- of types

data _≲ᵣ_ : RawType  RawType  Set
data _≲_  : Type  Type  Set

data _≲ᵣ_ where
  ≲-ι :  {ι}  ` ι ≲ᵣ ` ι

  ≲-ref :  {A B}
     A  B
     B  A
      -----------------
     Ref A ≲ᵣ Ref B

  ≲-fun :  {gc₁ gc₂} {A B C D}
     gc₂  gc₁
     C  A
     B  D
      -----------------------------------
      gc₁  A  B ≲ᵣ  gc₂  C  D

data _≲_ where
  ≲-ty :  {g₁ g₂} {S T}
     g₁  g₂
     S ≲ᵣ T
      --------------------
     S of g₁  T of g₂

≲ᵣ-refl :  {T}  T ≲ᵣ T
≲-refl  :  {A}  A  A

≲ᵣ-refl {` ι}           = ≲-ι
≲ᵣ-refl {Ref A}         = ≲-ref ≲-refl ≲-refl
≲ᵣ-refl { gc  A  B} = ≲-fun ≾-refl ≲-refl ≲-refl
≲-refl  {T of g}        = ≲-ty ≾-refl ≲ᵣ-refl

≲ᵣ-antisym :  {S T}
   S ≲ᵣ T  T ≲ᵣ S
   S ~ᵣ T
≲-antisym :  {A B}
   A  B  B  A
   A ~ B

≲ᵣ-antisym ≲-ι ≲-ι = ~-ι
≲ᵣ-antisym (≲-ref A≲B B≲A) (≲-ref _ _) =
  ~-ref (≲-antisym A≲B B≲A)
≲ᵣ-antisym (≲-fun gc₂≾gc₁ C≲A B≲D) (≲-fun gc₁≾gc₂ A≲C D≲B) =
  ~-fun (≾-antisym gc₁≾gc₂ gc₂≾gc₁) (≲-antisym A≲C C≲A) (≲-antisym B≲D D≲B)
≲-antisym (≲-ty g₁≾g₂ S≲T) (≲-ty g₂≾g₁ T≲S) =
  ~-ty (≾-antisym g₁≾g₂ g₂≾g₁) (≲ᵣ-antisym S≲T T≲S)

<:→≲ :  {A B}  A <: B  A  B
<:→≲ (<:-ty g₁<:g₂ <:-ι) = ≲-ty (<:ₗ→≾ g₁<:g₂) ≲-ι
<:→≲ (<:-ty g₁<:g₂ (<:-ref A<:B B<:A)) =
  ≲-ty (<:ₗ→≾ g₁<:g₂) (≲-ref (<:→≲ A<:B) (<:→≲ B<:A))
<:→≲ (<:-ty g₁<:g₂ (<:-fun gc₂<:gc₁ C<:A B<:D)) =
  ≲-ty (<:ₗ→≾ g₁<:g₂) (≲-fun (<:ₗ→≾ gc₂<:gc₁) (<:→≲ C<:A) (<:→≲ B<:D))

A≲Tg→A≲T⋆ :  {A T g}  A  T of g  A  T of 
A≲Tg→A≲T⋆ {T₁ of g₁} {T₂} {g₂} (≲-ty g₁≾g₂ T₁≲T₂) =
  ≲-ty ≾-⋆r T₁≲T₂

{- Properties of consistent subtyping (≲):
        B
   ≲  / | <:
     /  |
    A - C
      ~
        B
   ≲  / | ~
     /  |
    A - C
      <:
-}
≲ᵣ-prop :  {S T : RawType}
   S ≲ᵣ T
   ∃[ U ] (S ~ᵣ U) × (U <:ᵣ T)
≲-prop :  {A B : Type}
   A  B
   ∃[ C ] (A ~ C) × (C <: B)

≲ᵣ-prop′ :  {S T : RawType}
   S ≲ᵣ T
   ∃[ U ] (S <:ᵣ U) × (U ~ᵣ T)
≲-prop′ :  {A B : Type}
   A  B
   ∃[ C ] (A <: C) × (C ~ B)

≲ᵣ-prop′ {` ι} {` ι} ≲-ι =  ` ι , <:-ι , ~-ι 
≲ᵣ-prop′ {Ref A} {Ref B} (≲-ref A≲B B≲A) =
   Ref A , <:ᵣ-refl , ~-ref (≲-antisym A≲B B≲A) 
≲ᵣ-prop′ { gc₁  A  B} { gc₂  C  D} (≲-fun gc₂≾gc₁ C≲A B≲D) =
  case ≾-prop gc₂≾gc₁ of λ where
     gc , gc₂~gc , gc<:gc₁  
      case  ≲-prop C≲A , ≲-prop′ B≲D  of λ where
          A′ , C~A′ , A′<:A  ,  B′ , B<:B′ , B′~D   
            gc  A′  B′ , <:-fun gc<:gc₁ A′<:A B<:B′ , ~-fun (~ₗ-sym gc₂~gc) (~-sym C~A′) B′~D 

≲-prop′ {S of g₁} {T of g₂} (≲-ty g₁≾g₂ S≲T) =
  case ≾-prop′ g₁≾g₂ of λ where
     g , g₁<:g , g~g₂  
      case ≲ᵣ-prop′ S≲T of λ where
         U , S<:U , U~T  
           U of g , <:-ty g₁<:g S<:U , ~-ty g~g₂ U~T 

≲ᵣ-prop {` ι} {` ι} ≲-ι =  ` ι , ~-ι , <:-ι 
≲ᵣ-prop {Ref A} {Ref B} (≲-ref A≲B B≲A) =
   Ref B , ~-ref (≲-antisym A≲B B≲A) , <:ᵣ-refl 
≲ᵣ-prop { gc₁  A  B} { gc₂  C  D} (≲-fun gc₂≾gc₁ C≲A B≲D) =
  case ≾-prop′ gc₂≾gc₁ of λ where
     gc , gc₂<:gc , gc~gc₁  
      case  ≲-prop′ C≲A , ≲-prop B≲D  of λ where
          A′ , C<:A′ , A′~A  ,  B′ , B~B′ , B′<:D   
            gc  A′  B′ ,
            ~-fun (~ₗ-sym gc~gc₁) (~-sym A′~A) B~B′ , <:-fun gc₂<:gc C<:A′ B′<:D 

≲-prop {S of g₁} {T of g₂} (≲-ty g₁≾g₂ S≲T) =
  case ≾-prop g₁≾g₂ of λ where
     g , g₁~g , g<:g₂  
      case ≲ᵣ-prop S≲T of λ where
         U , S~U , U<:T  
           U of g , ~-ty g₁~g S~U , <:-ty g<:g₂ U<:T 

-- Consistency implies consistent subtyping (both directions)
~ᵣ→≲ᵣ :  {S T}  S ~ᵣ T  S ≲ᵣ T × T ≲ᵣ S
~→≲ :  {A B}  A ~ B  A  B × B  A

~ᵣ→≲ᵣ ~-ι =  ≲-ι , ≲-ι 
~ᵣ→≲ᵣ (~-ref A~B) =
  case ~→≲ A~B of λ where
     A≲B , B≲A    ≲-ref A≲B B≲A , ≲-ref B≲A A≲B 
~ᵣ→≲ᵣ (~-fun gc₁~gc₂ A~C B~D) =
  case ~ₗ→≾ gc₁~gc₂ of λ where
     gc₁≾gc₂ , gc₂≾gc₁  
      case  ~→≲ A~C , ~→≲ B~D  of λ where
          A≲C , C≲A  ,  B≲D , D≲B   
           ≲-fun gc₂≾gc₁ C≲A B≲D , ≲-fun gc₁≾gc₂ A≲C D≲B 
~→≲ (~-ty g₁~g₂ S~T) =
  case  ~ₗ→≾ g₁~g₂ , ~ᵣ→≲ᵣ S~T  of λ where
      g₁≾g₂ , g₂≾g₁  ,  S≲T , T≲S   
       ≲-ty g₁≾g₂ S≲T , ≲-ty g₂≾g₁ T≲S 


{- **** Gradual meet **** -}
infix 5 _⊓ᵣ_
infix 5 _⊓_

_⊓ᵣ_ : RawType  RawType  Maybe RawType
_⊓_ : Type  Type  Maybe Type

` Unit ⊓ᵣ ` Unit = just (` Unit)
` Bool ⊓ᵣ ` Bool = just (` Bool)
Ref A ⊓ᵣ Ref B =
  do
    A⊓B  A  B
    just (Ref A⊓B)
 gc₁  A  B ⊓ᵣ  gc₂  C  D =
  do
    gc₁⊓gc₂  gc₁ ⊓ₗ gc₂
    A⊓C  A  C
    B⊓D  B  D
    just ( gc₁⊓gc₂  A⊓C  B⊓D)
_ ⊓ᵣ _ = nothing

S of g₁  T of g₂ =
  do
    S⊓T    S ⊓ᵣ T
    g₁⊓g₂  g₁ ⊓ₗ g₂
    just (S⊓T of g₁⊓g₂)

grad-meet-~ᵣ :  {S T U}  S ⊓ᵣ T  just U  S ~ᵣ U × T ~ᵣ U
grad-meet-~  :  {A B C}  A   B  just C  A ~  C × B ~  C

grad-meet-~ᵣ {` Bool} {` Bool} {` Bool} refl =  ~-ι , ~-ι 
grad-meet-~ᵣ {` Unit} {` Unit} {` Unit} refl =  ~-ι , ~-ι 
grad-meet-~ᵣ {Ref A} {Ref B} {U}
  with A  B in A⊓B≡C
... | just C =
  case grad-meet-~ {A} {B} A⊓B≡C of λ where
     A~C , B~C  
      λ { refl   ~-ref A~C , ~-ref B~C  }
grad-meet-~ᵣ { gc₁  A  B} { gc₂  C  D} {U}
  with gc₁ ⊓ₗ gc₂ in gc₁⊓gc₂≡gc | A  C in A⊓C≡A′ | B  D in B⊓D≡B′
... | just gc | just A′ | just B′ =
  case grad-meet-~ₗ gc₁⊓gc₂≡gc of λ where
     gc₁~gc , gc₂~gc  
      case  grad-meet-~ {A} {C} A⊓C≡A′ , grad-meet-~ {B} {D} B⊓D≡B′  of λ where
          A~A′ , C~A′  ,  B~B′ , D~B′   
          λ { refl   ~-fun gc₁~gc A~A′ B~B′ , ~-fun gc₂~gc C~A′ D~B′  }
grad-meet-~ {S of g₁} {T of g₂} {C}
  with S ⊓ᵣ T in S⊓T≡U | g₁ ⊓ₗ g₂ in g₁⊓g₂≡g
... | just U | just g =
  case  grad-meet-~ᵣ {S} {T} S⊓T≡U , grad-meet-~ₗ g₁⊓g₂≡g  of λ where
      S~U , T~U  ,  g₁~g , g₂~g   
      λ { refl   ~-ty g₁~g S~U , ~-ty g₂~g T~U  }


{- **** Consistent join **** -}
infix 5 _∨̃ᵣ_
infix 5 _∨̃_
{- **** Consistent meet **** -}
infix 5 _∧̃ᵣ_
infix 5 _∧̃_

_∨̃ᵣ_ : RawType  RawType  Maybe RawType
_∧̃ᵣ_ : RawType  RawType  Maybe RawType
_∨̃_ :  Type     Type     Maybe Type
_∧̃_ :  Type     Type     Maybe Type

` Unit ∨̃ᵣ ` Unit = just (` Unit)
` Bool ∨̃ᵣ ` Bool = just (` Bool)
Ref A ∨̃ᵣ Ref B =
  do
  A⊓B  A  B
  just (Ref A⊓B)
 gc₁  A  B ∨̃ᵣ  gc₂  C  D =
  do
    A∧̃C  A ∧̃ C
    B∨̃D  B ∨̃ D
    just ( gc₁ ⋏̃ gc₂  A∧̃C  B∨̃D)
_ ∨̃ᵣ _ = nothing

` Unit ∧̃ᵣ ` Unit = just (` Unit)
` Bool ∧̃ᵣ ` Bool = just (` Bool)
Ref A ∧̃ᵣ Ref B =
  do
    A⊓B  A  B
    just (Ref A⊓B)
 gc₁  A  B ∧̃ᵣ  gc₂  C  D =
  do
    A∨̃C  A ∨̃ C
    B∧̃D  B ∧̃ D
    just ( gc₁ ⋎̃ gc₂  A∨̃C  B∧̃D)
_ ∧̃ᵣ _ = nothing

S of g₁ ∨̃ T of g₂ =
  do
    S∨̃T  S ∨̃ᵣ T
    just (S∨̃T of g₁ ⋎̃ g₂)

S of g₁ ∧̃ T of g₂ =
  do
    S∧̃T  S ∧̃ᵣ T
    just (S∧̃T of g₁ ⋏̃ g₂)


consis-join-≲ᵣ-inv :  {S T U}
   S ∨̃ᵣ T  just U  S ≲ᵣ U × T ≲ᵣ U
consis-meet-≲ᵣ-inv :  {S T U}
   S ∧̃ᵣ T  just U  U ≲ᵣ S × U ≲ᵣ T
consis-join-≲-inv :  {A B C}
   A ∨̃ B  just C  A  C × B  C
consis-meet-≲-inv :  {A B C}
   A ∧̃ B  just C  C  A × C  B

consis-join-≲ᵣ-inv {` Bool} {` Bool} {` Bool} refl =  ≲-ι , ≲-ι 
consis-join-≲ᵣ-inv {` Unit} {` Unit} {` Unit} refl =  ≲-ι , ≲-ι 
consis-join-≲ᵣ-inv {` Bool} {Ref _} {_} ()
consis-join-≲ᵣ-inv {` Unit} {Ref _} {_} ()
consis-join-≲ᵣ-inv {` Bool} { _  _  _} {_} ()
consis-join-≲ᵣ-inv {` Unit} { _  _  _} {_} ()
consis-join-≲ᵣ-inv {Ref A} {Ref B} {U}
  with A  B in A⊓B≡C
... | just C =
  case grad-meet-~ {A} {B} A⊓B≡C of λ where
     A~C , B~C  
      case  ~→≲ A~C , ~→≲ B~C  of λ where
          A≲C , C≲A  ,  B≲C , C≲B   
          λ { refl   ≲-ref A≲C C≲A , ≲-ref B≲C C≲B  }
consis-join-≲ᵣ-inv { gc₁  A  B} { gc₂  C  D} {U}
  with A ∧̃ C in A∧̃C≡A′ | B ∨̃ D in B∨̃D≡B′
... | just A′ | just B′ =
  case consis-meet-≾-inv {gc₁} {gc₂} refl of λ where
     gc₁⋏̃gc₂≾gc₁ , gc₁⋏̃gc₂≾gc₂  
      case  consis-meet-≲-inv {A} {C} A∧̃C≡A′ , consis-join-≲-inv {B} {D} B∨̃D≡B′  of λ where
          A′≲A , A′≲C  ,  B≲B′ , D≲B′   
          λ { refl   ≲-fun gc₁⋏̃gc₂≾gc₁ A′≲A B≲B′ , ≲-fun gc₁⋏̃gc₂≾gc₂ A′≲C D≲B′  }
consis-join-≲-inv {S of g₁} {T of g₂} {C}
  with S ∨̃ᵣ T in S∨̃T≡U
... | just U =
  case  consis-join-≲ᵣ-inv {S} {T} S∨̃T≡U , consis-join-≾-inv {g₁} {g₂} refl  of λ where
      S≲U , T≲U  ,  g₁≾g₁⋎̃g₂ , g₂≾g₁⋎̃g₂   
      λ { refl   ≲-ty g₁≾g₁⋎̃g₂ S≲U , ≲-ty g₂≾g₁⋎̃g₂ T≲U  }

consis-meet-≲ᵣ-inv {` Bool} {` Bool} {` Bool} refl =  ≲-ι , ≲-ι 
consis-meet-≲ᵣ-inv {` Unit} {` Unit} {` Unit} refl =  ≲-ι , ≲-ι 
consis-meet-≲ᵣ-inv {` Bool} {Ref _} {_} ()
consis-meet-≲ᵣ-inv {` Unit} {Ref _} {_} ()
consis-meet-≲ᵣ-inv {` Bool} { _  _  _} {_} ()
consis-meet-≲ᵣ-inv {` Unit} { _  _  _} {_} ()
consis-meet-≲ᵣ-inv {Ref A} {Ref B} {U}
  with A  B in A⊓B≡C
... | just C =
  case grad-meet-~ {A} {B} A⊓B≡C of λ where
     A~C , B~C  
      case  ~→≲ A~C , ~→≲ B~C  of λ where
          A≲C , C≲A  ,  B≲C , C≲B   
          λ { refl   ≲-ref C≲A A≲C , ≲-ref C≲B B≲C  }
consis-meet-≲ᵣ-inv { gc₁  A  B} { gc₂  C  D} {U}
  with A ∨̃ C in A∨̃C≡A′ | B ∧̃ D in B∧̃D≡B′
... | just A′ | just B′ =
  case consis-join-≾-inv {gc₁} {gc₂} refl of λ where
     gc₁≾gc₁⋎̃gc₂ , gc₂≾gc₁⋎̃gc₂  
      case  consis-join-≲-inv {A} {C} A∨̃C≡A′ , consis-meet-≲-inv {B} {D} B∧̃D≡B′  of λ where
          A≲A′ , C≲A′  ,  B′≲B , B′≲D   
          λ { refl   ≲-fun gc₁≾gc₁⋎̃gc₂ A≲A′ B′≲B , ≲-fun gc₂≾gc₁⋎̃gc₂ C≲A′ B′≲D  }
consis-meet-≲-inv {S of g₁} {T of g₂} {C}
  with S ∧̃ᵣ T in S∧̃T≡U
... | just U =
  case  consis-meet-≲ᵣ-inv {S} {T} S∧̃T≡U , consis-meet-≾-inv {g₁} {g₂} refl  of λ where
      U≲S , U≲T  ,  g₁⋏̃g₂≾g₁ , g₁⋏̃g₂≾g₂   
      λ { refl   ≲-ty g₁⋏̃g₂≾g₁ U≲S , ≲-ty g₁⋏̃g₂≾g₂ U≲T  }


{- **** Precision **** -}
infix 5 _⊑ᵣ_
infix 5 _⊑_

data _⊑ᵣ_ : RawType  RawType  Set
data _⊑_  : Type  Type  Set

data _⊑ᵣ_ where
  ⊑-ι :  {ι}
      -----------------------------
     ` ι ⊑ᵣ ` ι

  ⊑-ref :  {A B}
     A  B
      ----------------------------------------------
     Ref A ⊑ᵣ Ref B

  ⊑-fun :  {A B C D gc₁ gc₂}
     gc₁ ⊑ₗ gc₂
     A  C
     B  D
      ----------------------------------------------
      gc₁  A  B ⊑ᵣ  gc₂  C  D

data _⊑_ where
  ⊑-ty :  {S T g₁ g₂}
     g₁ ⊑ₗ g₂
     S  ⊑ᵣ T
      --------------------
     S of g₁  T of g₂

⊑-refl :  {A}  A  A
⊑-refl {` ι of g} = ⊑-ty ⊑ₗ-refl ⊑-ι
⊑-refl {Ref A of g} = ⊑-ty ⊑ₗ-refl (⊑-ref ⊑-refl)
⊑-refl { gc  A  B of g} = ⊑-ty ⊑ₗ-refl (⊑-fun ⊑ₗ-refl ⊑-refl ⊑-refl)

infix 4 _⊑?_

_⊑?_ : (A B : Type)  Dec (A  B)
(` ι₁ of g₁) ⊑? (` ι₂ of g₂) =
  case (` ι₁) ≡ᵣ? (` ι₂) of λ where
  (yes refl) 
    case g₁ ⊑ₗ? g₂ of λ where
    (yes g₁⊑g₂)  yes (⊑-ty g₁⊑g₂ ⊑-ι)
    (no  g₁⋤g₂)   no λ { (⊑-ty g₁⊑g₂ ⊑-ι)  contradiction g₁⊑g₂ g₁⋤g₂ }
  (no ι₁≢ι₂)  no λ { (⊑-ty _ ⊑-ι)  contradiction refl ι₁≢ι₂ }
(` ι of g₁) ⊑? (Ref _ of _) = no λ { (⊑-ty _ ()) }
(` ι of g₁) ⊑? ( _  _  _ of _) = no λ { (⊑-ty _ ()) }
(Ref A of g₁) ⊑? (Ref B of g₂) =
  case A ⊑? B of λ where
  (yes A⊑B) 
    case g₁ ⊑ₗ? g₂ of λ where
    (yes g₁⊑g₂)  yes (⊑-ty g₁⊑g₂ (⊑-ref A⊑B))
    (no  g₁⋤g₂)  no λ { (⊑-ty g₁⊑g₂ (⊑-ref _))  contradiction g₁⊑g₂ g₁⋤g₂ }
  (no  A⋤B)  no λ { (⊑-ty _ (⊑-ref A⊑B))  contradiction A⊑B A⋤B }
(Ref A of g₁) ⊑? (` _ of _) = no λ { (⊑-ty _ ()) }
(Ref A of g₁) ⊑? ( _  _  _ of _) = no λ { (⊑-ty _ ()) }
 gᶜ₁  A₁  B₁ of g₁ ⊑?  gᶜ₂  A₂  B₂ of g₂ =
  case gᶜ₁ ⊑ₗ? gᶜ₂ of λ where
  (yes gᶜ₁⊑gᶜ₂) 
    case A₁ ⊑? A₂ of λ where
    (yes A₁⊑A₂) 
      case B₁ ⊑? B₂ of λ where
      (yes B₁⊑B₂) 
        case g₁ ⊑ₗ? g₂ of λ where
          (yes g₁⊑g₂)  yes (⊑-ty g₁⊑g₂ (⊑-fun gᶜ₁⊑gᶜ₂ A₁⊑A₂ B₁⊑B₂))
          (no  g₁⋤g₂)  no λ { (⊑-ty g₁⊑g₂ (⊑-fun _ _ _))  contradiction g₁⊑g₂ g₁⋤g₂ }
      (no  B₁⋤B₂)  no λ { (⊑-ty _ (⊑-fun _ _ B₁⊑B₂))  contradiction B₁⊑B₂ B₁⋤B₂ }
    (no  A₁⋤A₂)  no λ { (⊑-ty _ (⊑-fun _ A₁⊑A₂ _))  contradiction A₁⊑A₂ A₁⋤A₂ }
  (no  gᶜ₁⋤gᶜ₂)  no λ { (⊑-ty _ (⊑-fun gᶜ₁⊑gᶜ₂ _ _))  contradiction gᶜ₁⊑gᶜ₂ gᶜ₁⋤gᶜ₂ }
( _  _  _ of _) ⊑? (` _ of _) = no λ { (⊑-ty _ ()) }
( _  _  _ of _) ⊑? (Ref _ of _) = no λ { (⊑-ty _ ()) }



prec-join-⊑ :  {A A′ B B′ C C′}
   A  A′
   B  B′
   A  B  just C
   A′  B′  just C′
   C  C′
prec-join-⊑ {` Unit of g₁} {` Unit of g₁′} {` Unit of g₂} {` Unit of g₂′}
            (⊑-ty g₁⊑g₁′ ⊑-ι) (⊑-ty g₂⊑g₂′ ⊑-ι) eq eq′
  with g₁ ⊓ₗ g₂ in g₁⊓g₂≡g₃ | g₁′ ⊓ₗ g₂′ in g₁′⊓g₂′≡g₃′ | eq | eq′
... | just g₃ | just g₃′ | refl | refl = ⊑-ty (prec-join-⊑ₗ g₁⊑g₁′ g₂⊑g₂′ g₁⊓g₂≡g₃ g₁′⊓g₂′≡g₃′) ⊑-ι
prec-join-⊑ {` Bool of g₁} {` Bool of g₁′} {` Bool of g₂} {` Bool of g₂′}
            (⊑-ty g₁⊑g₁′ ⊑-ι) (⊑-ty g₂⊑g₂′ ⊑-ι) eq eq′
  with g₁ ⊓ₗ g₂ in g₁⊓g₂≡g₃ | g₁′ ⊓ₗ g₂′ in g₁′⊓g₂′≡g₃′ | eq | eq′
... | just g₃ | just g₃′ | refl | refl = ⊑-ty (prec-join-⊑ₗ g₁⊑g₁′ g₂⊑g₂′ g₁⊓g₂≡g₃ g₁′⊓g₂′≡g₃′) ⊑-ι
prec-join-⊑ {` Unit of _} (⊑-ty _ ⊑-ι) (⊑-ty _ (⊑-ref _)) ()
prec-join-⊑ {` Bool of _} (⊑-ty _ ⊑-ι) (⊑-ty _ (⊑-ref _)) ()
prec-join-⊑ {` Unit of _} (⊑-ty _ ⊑-ι) (⊑-ty _ (⊑-fun _ _ _)) ()
prec-join-⊑ {` Bool of _} (⊑-ty _ ⊑-ι) (⊑-ty _ (⊑-fun _ _ _)) ()
prec-join-⊑ {Ref A of g₁} {Ref A′ of g₁′} {Ref B of g₂} {Ref B′ of g₂′}
            (⊑-ty g₁⊑g₁′ (⊑-ref A⊑A′)) (⊑-ty g₂⊑g₂′ (⊑-ref B⊑B′)) eq eq′
  with A  B in A⊓B≡C | A′  B′ in A′⊓B′≡C′
     | g₁ ⊓ₗ g₂ in g₁⊓g₂≡g₃ | g₁′ ⊓ₗ g₂′ in g₁′⊓g₂′≡g₃′ | eq | eq′
... | just C | just C′ | just g₃ | just g₃′ | refl | refl =
  ⊑-ty (prec-join-⊑ₗ g₁⊑g₁′ g₂⊑g₂′ g₁⊓g₂≡g₃ g₁′⊓g₂′≡g₃′) (⊑-ref (prec-join-⊑ A⊑A′ B⊑B′ A⊓B≡C A′⊓B′≡C′))
prec-join-⊑ { g₃  A  B of g₁} { g₃′  A′  B′ of g₁′} { g₄  C  D of g₂} { g₄′  C′  D′ of g₂′}
  (⊑-ty g₁⊑g₁′ (⊑-fun g₃⊑g₃′ A⊑A′ B⊑B′)) (⊑-ty g₂⊑g₂′ (⊑-fun g₄⊑g₄′ C⊑C′ D⊑D′)) eq eq′
  with g₁ ⊓ₗ g₂ in g₁⊓g₂≡g₅ | g₁′ ⊓ₗ g₂′ in g₁′⊓g₂′≡g₅′ | g₃ ⊓ₗ g₄ in g₃⊓g₄≡g₆ | g₃′ ⊓ₗ g₄′ in g₃′⊓g₄′≡g₆′
     | A  C in A⊓C≡E | A′  C′ in A′⊓C′≡E′ | B  D in B⊓D≡F | B′  D′ in B′⊓D′≡F′ | eq | eq′
... | just g₅ | just g₅′ | just g₆ | just g₆′
    | just E | just E′ | just F | just F′ | refl | refl =
  ⊑-ty (prec-join-⊑ₗ g₁⊑g₁′ g₂⊑g₂′ g₁⊓g₂≡g₅ g₁′⊓g₂′≡g₅′)
       (⊑-fun (prec-join-⊑ₗ g₃⊑g₃′ g₄⊑g₄′ g₃⊓g₄≡g₆ g₃′⊓g₄′≡g₆′)
              (prec-join-⊑ A⊑A′ C⊑C′ A⊓C≡E A′⊓C′≡E′)
              (prec-join-⊑ B⊑B′ D⊑D′ B⊓D≡F B′⊓D′≡F′))


consis-meet-⊑ :  {A A′ B B′ C C′}
   A  A′
   B  B′
   A  ∧̃ B   just C
   A′ ∧̃ B′  just C′
    ----------------------------------------
   C  C′
consis-join-⊑ :  {A A′ B B′ C C′}
   A  A′
   B  B′
   A ∨̃ B  just C
   A′ ∨̃ B′  just C′
    ----------------------------------------
   C  C′
consis-meet-⊑ {A = ` Unit of _} {B = ` Unit of _} (⊑-ty g₁⊑g₂ ⊑-ι) (⊑-ty g₃⊑g₄ ⊑-ι) refl refl =
  ⊑-ty (consis-meet-⊑ₗ g₁⊑g₂ g₃⊑g₄) ⊑-ι
consis-meet-⊑ {A = ` Bool of _} {B = ` Bool of _} (⊑-ty g₁⊑g₂ ⊑-ι) (⊑-ty g₃⊑g₄ ⊑-ι) refl refl =
  ⊑-ty (consis-meet-⊑ₗ g₁⊑g₂ g₃⊑g₄) ⊑-ι
consis-meet-⊑ {A = ` Unit of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-ref x₁)) ()
consis-meet-⊑ {A = ` Bool of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-ref x₁)) ()
consis-meet-⊑ {A = ` Unit of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-fun x₁ x₃ x₄)) ()
consis-meet-⊑ {A = ` Bool of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-fun x₁ x₃ x₄)) ()
consis-meet-⊑ {Ref A of _} {Ref A′ of _} {Ref B of _} {Ref B′ of _}
              (⊑-ty g₁⊑g₂ (⊑-ref A⊑A′)) (⊑-ty g₃⊑g₄ (⊑-ref B⊑B′)) eq eq′
  with A  B in A⊓B≡C | A′  B′ in A′⊓B′≡C | eq | eq′
... | just C | just C′ | refl | refl =
  ⊑-ty (consis-meet-⊑ₗ g₁⊑g₂ g₃⊑g₄) (⊑-ref (prec-join-⊑ A⊑A′ B⊑B′ A⊓B≡C A′⊓B′≡C))
consis-meet-⊑ { g₃  A  B of g₁} { g₃′  A′  B′ of g₁′} { g₄  C  D of g₂} { g₄′  C′  D′ of g₂′}
              (⊑-ty g₁⊑g₁′ (⊑-fun g₃⊑g₃′ A⊑A′ B⊑B′)) (⊑-ty g₂⊑g₂′ (⊑-fun g₄⊑g₄′ C⊑C′ D⊑D′)) eq eq′
  with A ∨̃ C in A∨̃C≡E | B ∧̃ D in B∧̃D≡F | A′ ∨̃ C′ in A′∨̃C′≡E′ | B′ ∧̃ D′ in B′∧̃D′≡F′ | eq | eq′
... | just E | just F | just E′ | just F′ | refl | refl =
  ⊑-ty (consis-meet-⊑ₗ g₁⊑g₁′ g₂⊑g₂′) (⊑-fun (consis-join-⊑ₗ g₃⊑g₃′ g₄⊑g₄′)
       (consis-join-⊑ A⊑A′ C⊑C′ A∨̃C≡E A′∨̃C′≡E′) (consis-meet-⊑ B⊑B′ D⊑D′ B∧̃D≡F B′∧̃D′≡F′))

consis-join-⊑ {A = ` Unit of _} {B = ` Unit of _} (⊑-ty g₁⊑g₂ ⊑-ι) (⊑-ty g₃⊑g₄ ⊑-ι) refl refl =
  ⊑-ty (consis-join-⊑ₗ g₁⊑g₂ g₃⊑g₄) ⊑-ι
consis-join-⊑ {A = ` Bool of _} {B = ` Bool of _} (⊑-ty g₁⊑g₂ ⊑-ι) (⊑-ty g₃⊑g₄ ⊑-ι) refl refl =
  ⊑-ty (consis-join-⊑ₗ g₁⊑g₂ g₃⊑g₄) ⊑-ι
consis-join-⊑ {A = ` Unit of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-ref x₁)) ()
consis-join-⊑ {A = ` Bool of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-ref x₁)) ()
consis-join-⊑ {A = ` Unit of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-fun x₁ x₃ x₄)) ()
consis-join-⊑ {A = ` Bool of _} (⊑-ty x ⊑-ι) (⊑-ty x₂ (⊑-fun x₁ x₃ x₄)) ()
consis-join-⊑ {Ref A of _} {Ref A′ of _} {Ref B of _} {Ref B′ of _} (⊑-ty g₁⊑g₂ (⊑-ref A⊑A′)) (⊑-ty g₃⊑g₄ (⊑-ref B⊑B′)) eq eq′
  with A  B in A⊓B≡C | A′  B′ in A′⊓B′≡C | eq | eq′
... | just C | just C′ | refl | refl =
  ⊑-ty (consis-join-⊑ₗ g₁⊑g₂ g₃⊑g₄) (⊑-ref (prec-join-⊑ A⊑A′ B⊑B′ A⊓B≡C A′⊓B′≡C))
consis-join-⊑ { g₃  A  B of g₁} { g₃′  A′  B′ of g₁′} { g₄  C  D of g₂} { g₄′  C′  D′ of g₂′}
              (⊑-ty g₁⊑g₁′ (⊑-fun g₃⊑g₃′ A⊑A′ B⊑B′)) (⊑-ty g₂⊑g₂′ (⊑-fun g₄⊑g₄′ C⊑C′ D⊑D′)) eq eq′
  with A ∧̃ C in A∧̃C≡E | B ∨̃ D in B∨̃D≡F | A′ ∧̃ C′ in A′∧̃C′≡E′ | B′ ∨̃ D′ in B′∨̃D′≡F′ | eq | eq′
... | just E | just F | just E′ | just F′ | refl | refl =
  ⊑-ty (consis-join-⊑ₗ g₁⊑g₁′ g₂⊑g₂′) (⊑-fun (consis-meet-⊑ₗ g₃⊑g₃′ g₄⊑g₄′)
       (consis-meet-⊑ A⊑A′ C⊑C′ A∧̃C≡E A′∧̃C′≡E′) (consis-join-⊑ B⊑B′ D⊑D′ B∨̃D≡F B′∨̃D′≡F′))


{- **** Precision-subtyping **** -}
infix 5 _⊑:>ᵣ_
infix 5 _⊑:>_
infix 5 _⊑<:ᵣ_
infix 5 _⊑<:_

data _⊑:>ᵣ_ : RawType  RawType  Set
data _⊑:>_  : Type     Type     Set
data _⊑<:ᵣ_ : RawType  RawType  Set
data _⊑<:_  : Type     Type     Set

data _⊑:>ᵣ_ where
  ⊑:>-ι :  {ι}  ` ι ⊑:>ᵣ ` ι

  ⊑:>-ref :  {A B}
    -- → A ⊑:> B → A ⊑<: B
     A  B
      --------------------
     Ref A ⊑:>ᵣ Ref B

  ⊑:>-fun :  {A B C D gc₁ gc₂}
     gc₁ ⊑<:ₗ gc₂
     A ⊑<: C
     B ⊑:> D
      ----------------------------------------------
      gc₁  A  B ⊑:>ᵣ  gc₂  C  D

data _⊑:>_ where
  ⊑:>-ty :  {S T g₁ g₂}
     g₁ ⊑:>ₗ g₂
     S  ⊑:>ᵣ T
      --------------------
     S of g₁ ⊑:> T of g₂

data _⊑<:ᵣ_ where
  ⊑<:-ι :  {ι}  ` ι ⊑<:ᵣ ` ι

  ⊑<:-ref :  {A B}
    -- → A ⊑<: B → A ⊑:> B
     A  B
      --------------------
     Ref A ⊑<:ᵣ Ref B

  ⊑<:-fun :  {A B C D gc₁ gc₂}
     gc₁ ⊑:>ₗ gc₂
     A ⊑:> C
     B ⊑<: D
      ----------------------------------------------
      gc₁  A  B ⊑<:ᵣ  gc₂  C  D

data _⊑<:_ where
  ⊑<:-ty :  {S T g₁ g₂}
     g₁ ⊑<:ₗ g₂
     S  ⊑<:ᵣ T
      --------------------
     S of g₁ ⊑<: T of g₂

⊑:>ᵣ-prop-from :  {T₁ T₂}  T₁ ⊑:>ᵣ T₂  ∃[ S ] (T₁ ⊑ᵣ S) × (T₂ <:ᵣ S)
⊑:>-prop-from  :  {A B}  A ⊑:> B  ∃[ C ] (A  C) × (B <: C)
⊑<:ᵣ-prop-from :  {T₁ T₂}  T₁ ⊑<:ᵣ T₂  ∃[ S ] (T₁ ⊑ᵣ S) × (S <:ᵣ T₂)
⊑<:-prop-from  :  {A B}  A ⊑<: B  ∃[ C ] (A  C) × (C <: B)

⊑:>ᵣ-prop-from {` ι} ⊑:>-ι =  ` ι , ⊑-ι , <:-ι 
⊑:>ᵣ-prop-from {Ref A} {Ref B} (⊑:>-ref A⊑B) =
   Ref B , ⊑-ref A⊑B , <:ᵣ-refl 
⊑:>ᵣ-prop-from { gc₁  A  B} { gc₂  C  D} (⊑:>-fun gc₁⊑<:gc₂ A⊑<:C B⊑:>D) =
  let  gc , gc₁⊑gc , gc<:gc₂  = ⊑<:ₗ-prop-from gc₁⊑<:gc₂
       E  , A⊑E , E<:C  = ⊑<:-prop-from A⊑<:C
       F  , B⊑F , D<:F  = ⊑:>-prop-from B⊑:>D in
    gc  E  F , ⊑-fun gc₁⊑gc A⊑E B⊑F , <:-fun gc<:gc₂ E<:C D<:F 
⊑:>-prop-from {T₁ of g₁} {T₂ of g₂} (⊑:>-ty g₁⊑:>g₂ T₁⊑:>T₂) =
  let  g , g₁⊑g , g₂<:g  = ⊑:>ₗ-prop-from g₁⊑:>g₂
       S , T₁⊑S , T₂<:S  = ⊑:>ᵣ-prop-from T₁⊑:>T₂ in
   S of g , ⊑-ty g₁⊑g T₁⊑S , <:-ty g₂<:g T₂<:S 

⊑<:ᵣ-prop-from {` ι} ⊑<:-ι =  ` ι , ⊑-ι , <:-ι 
⊑<:ᵣ-prop-from {Ref A} {Ref B} (⊑<:-ref A⊑B) =
   Ref B , ⊑-ref A⊑B , <:ᵣ-refl 
⊑<:ᵣ-prop-from { gc₁  A  B} { gc₂  C  D} (⊑<:-fun gc₁⊑:>gc₂ A⊑:>C B⊑<:D) =
  let  gc , gc₁⊑gc , gc₂<:gc  = ⊑:>ₗ-prop-from gc₁⊑:>gc₂
       E  , A⊑E , C<:E  = ⊑:>-prop-from A⊑:>C
       F  , B⊑F , F<:D  = ⊑<:-prop-from B⊑<:D in
    gc  E  F , ⊑-fun gc₁⊑gc A⊑E B⊑F , <:-fun gc₂<:gc C<:E F<:D 
⊑<:-prop-from {T₁ of g₁} {T₂ of g₂} (⊑<:-ty g₁⊑<:g₂ T₁⊑<:T₂) =
  let  g , g₁⊑g , g<:g₂  = ⊑<:ₗ-prop-from g₁⊑<:g₂
       S , T₁⊑S , S<:T₂  = ⊑<:ᵣ-prop-from T₁⊑<:T₂ in
   S of g , ⊑-ty g₁⊑g T₁⊑S , <:-ty g<:g₂ S<:T₂ 


⊑:>ᵣ-prop-to :  {T₁ T₂}  ∃[ S ] (T₁ ⊑ᵣ S) × (T₂ <:ᵣ S)  T₁ ⊑:>ᵣ T₂
⊑:>-prop-to  :  {A B}  ∃[ C ] (A  C) × (B <: C)  A ⊑:> B
⊑<:ᵣ-prop-to :  {T₁ T₂}  ∃[ S ] (T₁ ⊑ᵣ S) × (S <:ᵣ T₂)  T₁ ⊑<:ᵣ T₂
⊑<:-prop-to  :  {A B}  ∃[ C ] (A  C) × (C <: B)  A ⊑<: B

⊑:>ᵣ-prop-to  ` ι , ⊑-ι , <:-ι  = ⊑:>-ι
⊑:>ᵣ-prop-to  Ref C , ⊑-ref A⊑C , <:-ref B<:C C<:B 
  rewrite <:-antisym B<:C C<:B = ⊑:>-ref A⊑C
⊑:>ᵣ-prop-to   gc  E  F , ⊑-fun gc₁⊑gc A⊑E B⊑F , <:-fun gc<:gc₂ E<:C D<:F  =
  ⊑:>-fun (⊑<:ₗ-prop-to  gc , gc₁⊑gc , gc<:gc₂ ) (⊑<:-prop-to  E , A⊑E , E<:C ) (⊑:>-prop-to  F , B⊑F , D<:F )
⊑:>-prop-to  S of g , ⊑-ty g₁⊑g T₁⊑S , <:-ty g₂<:g T₂<:S  =
  ⊑:>-ty (⊑:>ₗ-prop-to  g , g₁⊑g , g₂<:g ) (⊑:>ᵣ-prop-to  S , T₁⊑S , T₂<:S )

⊑<:ᵣ-prop-to  ` ι , ⊑-ι , <:-ι  = ⊑<:-ι
⊑<:ᵣ-prop-to  Ref C , ⊑-ref A⊑C , <:-ref C<:B B<:C 
  rewrite <:-antisym B<:C C<:B = ⊑<:-ref A⊑C
⊑<:ᵣ-prop-to   gc  E  F , ⊑-fun gc₁⊑gc A⊑E B⊑F , <:-fun gc₂<:gc C<:E F<:D  =
  ⊑<:-fun (⊑:>ₗ-prop-to  gc , gc₁⊑gc , gc₂<:gc ) (⊑:>-prop-to  E , A⊑E , C<:E ) (⊑<:-prop-to  F , B⊑F , F<:D )
⊑<:-prop-to  S of g , ⊑-ty g₁⊑g T₁⊑S , <:-ty g<:g₂ S<:T₂  =
  ⊑<:-ty (⊑<:ₗ-prop-to  g , g₁⊑g , g<:g₂ ) (⊑<:ᵣ-prop-to  S , T₁⊑S , S<:T₂ )

{- Properties of precision-subtyping -}
⊑:>-prop : _⊑:>_  λ A B  ∃[ C ] (A  C) × (B <: C)
⊑:>-prop =  ⊑:>-prop-from , ⊑:>-prop-to 

⊑<:-prop : _⊑<:_  λ A B  ∃[ C ] (A  C) × (C <: B)
⊑<:-prop =  ⊑<:-prop-from , ⊑<:-prop-to 

{- Precision-subtyping is decidable -}
_⊑:>ᵣ?_ :  S T  Dec (S ⊑:>ᵣ T)
_⊑:>?_  :  A B  Dec (A ⊑:>  B)
_⊑<:ᵣ?_ :  S T  Dec (S ⊑<:ᵣ T)
_⊑<:?_  :  A B  Dec (A ⊑<:  B)

(` Unit) ⊑:>ᵣ? (` Unit) = yes ⊑:>-ι
(` Bool) ⊑:>ᵣ? (` Unit) = no λ ()
(` Unit) ⊑:>ᵣ? (` Bool) = no λ ()
(` Bool) ⊑:>ᵣ? (` Bool) = yes ⊑:>-ι
(` _) ⊑:>ᵣ? (Ref _) = no λ ()
(` _) ⊑:>ᵣ? ( _  _  _) = no λ ()
(Ref _) ⊑:>ᵣ? (` _) = no λ ()
(Ref A) ⊑:>ᵣ? (Ref B) =
  case A ⊑? B of λ where
  (yes A⊑B)  yes (⊑:>-ref A⊑B)
  (no  A⋤B)  no λ { (⊑:>-ref A⊑B)  contradiction A⊑B A⋤B }
(Ref _) ⊑:>ᵣ? ( _  _  _) = no λ ()
( _  _  _) ⊑:>ᵣ? (` _) = no λ ()
( _  _  _) ⊑:>ᵣ? (Ref _) = no λ ()
( gc₁  A  B) ⊑:>ᵣ? ( gc₂  C  D) =
  case gc₁ ⊑<:ₗ? gc₂ of λ where
  (yes gc₁⊑<:gc₂) 
    case A ⊑<:? C of λ where
    (yes A⊑<:C) 
      case B ⊑:>? D of λ where
      (yes B⊑:>D)  yes (⊑:>-fun gc₁⊑<:gc₂ A⊑<:C B⊑:>D)
      (no ¬B⊑:>D)  no λ { (⊑:>-fun _ _ B⊑:>D)  contradiction B⊑:>D ¬B⊑:>D }
    (no ¬A⊑<:C)  no λ { (⊑:>-fun _ A⊑<:C _)  contradiction A⊑<:C ¬A⊑<:C }
  (no ¬gc₁⊑<:gc₂) 
    no λ { (⊑:>-fun gc₁⊑<:gc₂ _ _)  contradiction gc₁⊑<:gc₂ ¬gc₁⊑<:gc₂ }
(S of g₁) ⊑:>? (T of g₂) =
  case S ⊑:>ᵣ? T of λ where
  (yes S⊑:>T) 
    case g₁ ⊑:>ₗ? g₂ of λ where
    (yes g₁⊑:>g₂)  yes (⊑:>-ty g₁⊑:>g₂ S⊑:>T)
    (no ¬g₁⊑:>g₂)  no λ { (⊑:>-ty g₁⊑:>g₂ _)  contradiction g₁⊑:>g₂ ¬g₁⊑:>g₂ }
  (no ¬S⊑:>T) 
    no  { (⊑:>-ty _ S⊑:>T)  contradiction S⊑:>T ¬S⊑:>T })

(` Unit) ⊑<:ᵣ? (` Unit) = yes ⊑<:-ι
(` Bool) ⊑<:ᵣ? (` Unit) = no λ ()
(` Unit) ⊑<:ᵣ? (` Bool) = no λ ()
(` Bool) ⊑<:ᵣ? (` Bool) = yes ⊑<:-ι
(` _) ⊑<:ᵣ? (Ref _) = no λ ()
(` _) ⊑<:ᵣ? ( _  _  _) = no λ ()
(Ref _) ⊑<:ᵣ? (` _) = no λ ()
(Ref A) ⊑<:ᵣ? (Ref B) =
  case A ⊑? B of λ where
  (yes A⊑B)  yes (⊑<:-ref A⊑B)
  (no  A⋤B)  no λ { (⊑<:-ref A⊑B)  contradiction A⊑B A⋤B }
(Ref _) ⊑<:ᵣ? ( _  _  _) = no λ ()
( _  _  _) ⊑<:ᵣ? (` _) = no λ ()
( _  _  _) ⊑<:ᵣ? (Ref _) = no λ ()
( gc₁  A  B) ⊑<:ᵣ? ( gc₂  C  D) =
  case gc₁ ⊑:>ₗ? gc₂ of λ where
  (yes gc₁⊑:>gc₂) 
    case A ⊑:>? C of λ where
    (yes A⊑:>C) 
      case B ⊑<:? D of λ where
      (yes B⊑<:D)  yes (⊑<:-fun gc₁⊑:>gc₂ A⊑:>C B⊑<:D)
      (no ¬B⊑<:D)  no λ { (⊑<:-fun _ _ B⊑<:D)  contradiction B⊑<:D ¬B⊑<:D }
    (no ¬A⊑:>C)  no λ { (⊑<:-fun _ A⊑:>C _)  contradiction A⊑:>C ¬A⊑:>C }
  (no ¬gc₁⊑:>gc₂) 
    no λ { (⊑<:-fun gc₁⊑:>gc₂ _ _)  contradiction gc₁⊑:>gc₂ ¬gc₁⊑:>gc₂ }
(S of g₁) ⊑<:? (T of g₂) =
  case S ⊑<:ᵣ? T of λ where
  (yes S⊑<:T) 
    case g₁ ⊑<:ₗ? g₂ of λ where
    (yes g₁⊑<:g₂)  yes (⊑<:-ty g₁⊑<:g₂ S⊑<:T)
    (no ¬g₁⊑<:g₂)  no λ { (⊑<:-ty g₁⊑<:g₂ _)  contradiction g₁⊑<:g₂ ¬g₁⊑<:g₂ }
  (no ¬S⊑<:T) 
    no  { (⊑<:-ty _ S⊑<:T)  contradiction S⊑<:T ¬S⊑<:T })


{- **** Type label stamping **** -}
stamp : Type  Label  Type
stamp (T of g₁) g₂ = T of g₁ ⋎̃ g₂

stamp-low :  A  stamp A (l low)  A
stamp-low (T of )      = refl
stamp-low (T of l low)  = refl
stamp-low (T of l high) = refl

stamp-~  :  {A B g₁ g₂}  A ~ B  g₁ ~ₗ g₂  stamp A g₁ ~ stamp B g₂
stamp-~ {S of g₁′} {T of g₂′} (~-ty g₁′~g₂′ S~T) g₁~g₂ = ~-ty (consis-join-~ₗ g₁′~g₂′ g₁~g₂) S~T

stamp-<: :  {A B g₁ g₂}  A <: B  g₁ <:ₗ g₂  stamp A g₁ <: stamp B g₂
stamp-<: (<:-ty g₁′<:g₂′ S<:T) g₁<:g₂ = <:-ty (consis-join-<:ₗ g₁′<:g₂′ g₁<:g₂) S<:T

stamp-⊑ :  {A B g₁ g₂}  A  B  g₁ ⊑ₗ g₂  stamp A g₁  stamp B g₂
stamp-⊑ (⊑-ty g₁′⊑g₂′ S⊑T) g₁⊑g₂ = ⊑-ty (consis-join-⊑ₗ g₁′⊑g₂′ g₁⊑g₂) S⊑T


{- **** Typing contexts **** -}
Context = List Type
-- Ctxt    = List (∃[ A ] (⊢ A))