module CoercionExpr.Stamping where

open import Data.Product using (_×_; ∃-syntax; proj₁; proj₂) renaming (_,_ to ⟨_,_⟩)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; subst)

open import Common.Utils
open import Common.SecurityLabels
open import Common.BlameLabels
open import CoercionExpr.CoercionExpr
open import CoercionExpr.SecurityLevel
open import CoercionExpr.Precision


{-
  Stamping on coercions in normal form:
    1) stamps the target type of the coercion and
    2) promotes the coercion's security by ℓ′
  -}
stampₗ :  { g}  ( : CExpr l   g)  CVal   (ℓ′ : StaticLabel)
   CExpr l   (g ⋎̃ l ℓ′)
stampₗ {g = g}                   v             low  rewrite g⋎̃low≡g {g} = 
stampₗ (id (l low))               id            high = id (l low)  
stampₗ (id (l high))              id            high = id (l high)
stampₗ (id (l low)  low !)       (inj id)      high = id (l low)    high !
stampₗ (id (l high)  high !)     (inj id)      high = id (l high)  high !
stampₗ (id (l low)    high !) (inj (up id)) high = id (l low)    high !
stampₗ (id (l low)  )          (up id)       high = id (l low)  

{-
  Stamping with injection:
    1) returns an injected coercion whose security is promoted by ℓ′
    2) allows different security levels on less precise and more precise sides
  -}
stamp!ₗ :  { g}  ( : CExpr l   g)  CVal   (ℓ′ : StaticLabel)
   CExpr l   
stamp!ₗ {}  {}                  v             low  = 
stamp!ₗ {ℓ₁} {l ℓ₂}               v             low  =   ℓ₂ !
stamp!ₗ (id (l low))               id            high = id (l low)    high !
stamp!ₗ (id (l high))              id            high = id (l high)  high !
stamp!ₗ (id (l low)  low !)       (inj id)      high = id (l low)    high !
stamp!ₗ (id (l high)  high !)     (inj id)      high = id (l high)  high !
stamp!ₗ (id (l low)    high !) (inj (up id)) high = id (l low)    high !
stamp!ₗ (id (l low)  )          (up id)       high = id (l low)    high !


{- Both variants of stamping return coercions in normal form -}
stampₗ-CVal :  { g} ( : CExpr l   g)
   (v : CVal )
   (ℓ′ : StaticLabel)
   CVal (stampₗ  v ℓ′)
stamp!ₗ-CVal :  { g} ( : CExpr l   g)
   (v : CVal )
   (ℓ′ : StaticLabel)
   CVal (stamp!ₗ  v ℓ′)
stampₗ-CVal {g = g}  v low rewrite g⋎̃low≡g {g} = v
stampₗ-CVal (id (l low)) id high = up id
stampₗ-CVal (id (l high)) id high = id
stampₗ-CVal (id (l low)  low !) (inj id) high = inj (up id)
stampₗ-CVal (id (l high)  high !) (inj id) high = inj id
stampₗ-CVal (id (l low)    high !) (inj (up id)) high = inj (up id)
stampₗ-CVal (id (l low)  ) (up id) high = up id
stamp!ₗ-CVal {g = }  v low = v
stamp!ₗ-CVal {g = l _}  v low = inj v
stamp!ₗ-CVal (id (l low)) id high = inj (up id)
stamp!ₗ-CVal (id (l high)) id high = inj id
stamp!ₗ-CVal (id (l low)  low !) (inj id) high = inj (up id)
stamp!ₗ-CVal (id (l high)  high !) (inj id) high = inj id
stamp!ₗ-CVal (id (l low)    high !) (inj (up id)) high = inj (up id)
stamp!ₗ-CVal (id (l low)  ) (up id) high = inj (up id)

stampₗ-low :  { g} { : CExpr l   g}
   (𝓋 : CVal )
   subst    CExpr l   ) g⋎̃low≡g (stampₗ  𝓋 low)  
stampₗ-low (id {l low}) = refl
stampₗ-low (id {l high}) = refl
stampₗ-low (inj id) = refl
stampₗ-low (inj (up id)) = refl
stampₗ-low (up id) = refl

stamp-not-id :  { ℓ′ g} { : CExpr l   g}
   CVal 
   l   g
   l   g ⋎̃ l ℓ′
stamp-not-id {low} {low} id neq = neq
stamp-not-id {low} {high} id neq = λ ()
stamp-not-id {high} id neq = neq
stamp-not-id (inj id) neq = neq
stamp-not-id (inj (up id)) neq = neq
stamp-not-id (up id) neq = neq



{- **** Properties of stamping about security **** -}

{- Both stamping functions promote the coercion's security by ℓ′ -}
stampₗ-security :  { g}
   ( : CExpr l   g)
   (v : CVal )
   (ℓ′ : StaticLabel)
    ---------------------------------------------------------
   (   v)  ℓ′   stampₗ  v ℓ′  (stampₗ-CVal  v ℓ′)
stampₗ-security {g = g}  v low
  rewrite g⋎̃low≡g {g} | ℓ⋎low≡ℓ {   v} = refl
stampₗ-security (id (l low)) id high = refl
stampₗ-security (id (l high)) id high = refl
stampₗ-security (id (l low)  low !) (inj id) high = refl
stampₗ-security (id (l high)  high !) (inj id) high = refl
stampₗ-security (id (l low)    high !) (inj (up id)) high = refl
stampₗ-security (id (l low)  ) (up id) high = refl

stamp!ₗ-security :  { g}
   ( : CExpr l   g)
   (v : CVal )
   (ℓ′ : StaticLabel)
    ---------------------------------------------------------
   (   v)  ℓ′   stamp!ₗ  v ℓ′  (stamp!ₗ-CVal  v ℓ′)
stamp!ₗ-security {g = }  v low rewrite ℓ⋎low≡ℓ {   v} = refl
stamp!ₗ-security {g = l }  id low rewrite ℓ⋎low≡ℓ {} = refl
stamp!ₗ-security {g = l high}  (up id) low = refl
stamp!ₗ-security (id (l low)) id high = refl
stamp!ₗ-security (id (l high)) id high = refl
stamp!ₗ-security (id (l low)  low !) (inj id) high = refl
stamp!ₗ-security (id (l high)  high !) (inj id) high = refl
stamp!ₗ-security (id (l low)    high !) (inj (up id)) high = refl
stamp!ₗ-security (id (l low)  ) (up id) high = refl


{- **** Properties of stamping about precision **** -}

{- Stamping preserves precision when the left side is an injection -}
stamp⋆ₗ-prec :  { ℓ₁ ℓ₂ g} { : CExpr l   } { : CExpr l   g}
   (v : CVal )
   (v′ : CVal )
      
   ℓ₁  ℓ₂
    ------------------------------------
    stampₗ  v ℓ₁  stampₗ  v′ ℓ₂
stamp⋆ₗ-prec {low} (inj id) id prec l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ-prec {high} (inj id) id prec l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = !⊑↑
stamp⋆ₗ-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = ↑!⊑↑
stamp⋆ₗ-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ-prec (inj id) (inj id) prec l≼l = prec-refl _
stamp⋆ₗ-prec {low} (inj id) (inj id) prec l≼h = !⊑↑!
stamp⋆ₗ-prec {high} (inj id) (inj id) prec l≼h = prec-refl _
stamp⋆ₗ-prec (inj (id {l low})) (inj id) prec h≼h = prec-refl _
stamp⋆ₗ-prec (inj (id {l high})) (inj id) prec h≼h = prec
stamp⋆ₗ-prec (inj id) (inj (up id)) prec l≼l = !⊑↑!
stamp⋆ₗ-prec (inj id) (inj (up id)) prec l≼h = !⊑↑!
stamp⋆ₗ-prec (inj id) (inj (up id)) prec h≼h = prec-refl _
-- ⊢ id low ; low ! ⊑ id low ; ↑
stamp⋆ₗ-prec (inj id) (up id) prec l≼l = !⊑↑
stamp⋆ₗ-prec (inj id) (up id) prec l≼h = !⊑↑
stamp⋆ₗ-prec (inj id) (up id) prec h≼h = ↑!⊑↑
stamp⋆ₗ-prec (inj (up id)) id (⊑-castl prec () _) leq
stamp⋆ₗ-prec (inj (up id)) (inj id) (⊑-castr (⊑-castl prec () _) _ _) leq
stamp⋆ₗ-prec (inj (up id)) (inj (up id)) prec l≼l = prec-refl _
stamp⋆ₗ-prec (inj (up id)) (inj (up id)) prec l≼h = prec-refl _
stamp⋆ₗ-prec (inj (up id)) (inj (up id)) prec h≼h = prec-refl _
-- ⊢ id low ; ↑ ; high ! ⊑ id low ; ↑
stamp⋆ₗ-prec (inj (up id)) (up id) prec l≼l = ↑!⊑↑
stamp⋆ₗ-prec (inj (up id)) (up id) prec l≼h = ↑!⊑↑
stamp⋆ₗ-prec (inj (up id)) (up id) prec h≼h = ↑!⊑↑


{- Stamping with the same security label preserves precision -}
stampₗ-prec :  { ℓ₁ g₁ g₂} { : CExpr l ℓ₁  g₁} { : CExpr l ℓ₁  g₂}
   (v : CVal )
   (v′ : CVal )
      
    ------------------------------------
    stampₗ  v   stampₗ  v′ 
stampₗ-prec id id (⊑-id l⊑l) = prec-refl _
stampₗ-prec id (inj id) (⊑-castr _ l⊑l ())
stampₗ-prec id (up id) (⊑-castr _ l⊑l ())
stampₗ-prec (up id) id (⊑-castl _ l⊑l ())
stampₗ-prec (up id) (inj id) (⊑-cast _ _ ())
stampₗ-prec (up id) (inj id) (⊑-castl _ () _)
stampₗ-prec (up id) (inj id) (⊑-castr _ _ ())
stampₗ-prec (up id) (inj (up id)) (⊑-cast _ _ ())
stampₗ-prec (up id) (inj (up id)) (⊑-castl _ () _)
stampₗ-prec (up id) (inj (up id)) (⊑-castr _ _ ())
stampₗ-prec (up id) (up id) c̅⊑d̅ = prec-refl _
stampₗ-prec {} (inj v₁) v₂ prec = stamp⋆ₗ-prec {ℓ₁ = } (inj v₁) v₂ prec ≼-refl


stamp!ₗ-left-prec :  { ℓ₁ ℓ₂ g₁ g₂} { : CExpr l   g₁} { : CExpr l   g₂}
   (v : CVal )
   (v′ : CVal )
      
   ℓ₁  ℓ₂
    ------------------------------------
    stamp!ₗ  v ℓ₁  stampₗ  v′ ℓ₂
stamp!ₗ-left-prec (id {l low}) id (⊑-id l⊑l) l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec (id {l high}) id (⊑-id l⊑l) l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec {low} id id (⊑-id l⊑l) l≼h = !⊑↑
stamp!ₗ-left-prec {high} id id (⊑-id l⊑l) l≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec (id {l low}) id (⊑-id l⊑l) h≼h = ↑!⊑↑
stamp!ₗ-left-prec (id {l high}) id (⊑-id l⊑l) h≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec id (inj id) (⊑-castr _ l⊑l ()) _
stamp!ₗ-left-prec id (up id) (⊑-castr _ l⊑l ()) _
stamp!ₗ-left-prec {low} (inj id) id prec l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec {high} (inj id) id prec l≼l = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = !⊑↑
stamp!ₗ-left-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = ↑!⊑↑
stamp!ₗ-left-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ-left-prec (inj id) (inj id) prec l≼l = prec-refl _
stamp!ₗ-left-prec {low} (inj id) (inj id) prec l≼h = !⊑↑!
stamp!ₗ-left-prec {high} (inj id) (inj id) prec l≼h = prec-refl _
stamp!ₗ-left-prec (inj (id {l low})) (inj id) prec h≼h = prec-refl _
stamp!ₗ-left-prec (inj (id {l high})) (inj id) prec h≼h = prec
stamp!ₗ-left-prec (inj id) (inj (up id)) prec l≼l = !⊑↑!
stamp!ₗ-left-prec (inj id) (inj (up id)) prec l≼h = !⊑↑!
stamp!ₗ-left-prec (inj id) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-left-prec (inj id) (up id) prec l≼l = !⊑↑
stamp!ₗ-left-prec (inj id) (up id) prec l≼h = !⊑↑
stamp!ₗ-left-prec (inj id) (up id) prec h≼h = ↑!⊑↑
stamp!ₗ-left-prec (inj (up id)) id (⊑-castl prec () _) leq
stamp!ₗ-left-prec (inj (up id)) (inj id) (⊑-castr (⊑-castl prec () _) _ _) leq
stamp!ₗ-left-prec (inj (up id)) (inj (up id)) prec l≼l = prec-refl _
stamp!ₗ-left-prec (inj (up id)) (inj (up id)) prec l≼h = prec-refl _
stamp!ₗ-left-prec (inj (up id)) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-left-prec (inj (up id)) (up id) prec l≼l = ↑!⊑↑
stamp!ₗ-left-prec (inj (up id)) (up id) prec l≼h = ↑!⊑↑
stamp!ₗ-left-prec (inj (up id)) (up id) prec h≼h = ↑!⊑↑
stamp!ₗ-left-prec (up id) id (⊑-castl prec _ ()) leq
stamp!ₗ-left-prec (up id) (inj id) (⊑-cast prec _ ()) leq
stamp!ₗ-left-prec (up id) (inj id) (⊑-castl prec _ ()) leq
stamp!ₗ-left-prec (up id) (inj id) (⊑-castr prec () _) leq
stamp!ₗ-left-prec (up id) (inj (up id)) prec l≼l = prec-refl _
stamp!ₗ-left-prec (up id) (inj (up id)) prec l≼h = prec-refl _
stamp!ₗ-left-prec (up id) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-left-prec (up id) (up id) prec l≼l = ↑!⊑↑
stamp!ₗ-left-prec (up id) (up id) prec l≼h = ↑!⊑↑
stamp!ₗ-left-prec (up id) (up id) prec h≼h = ↑!⊑↑


stamp!ₗ-prec :  { ℓ₁ ℓ₂ g₁ g₂} { : CExpr l   g₁} { : CExpr l   g₂}
   (v  : CVal )
   (v′ : CVal )
      
   ℓ₁  ℓ₂
    ---------------------------------------
    stamp!ₗ  v ℓ₁  stamp!ₗ  v′ ℓ₂
stamp!ₗ-prec id id (⊑-id l⊑l) l≼l = prec-refl _
stamp!ₗ-prec {low} id id (⊑-id l⊑l) l≼h = !⊑↑!
stamp!ₗ-prec {high} id id (⊑-id l⊑l) l≼h = prec-refl _
stamp!ₗ-prec id id (⊑-id l⊑l) h≼h = prec-refl _
stamp!ₗ-prec id (inj id) (⊑-castr _ l⊑l ()) _
stamp!ₗ-prec id (up id) (⊑-castr _ l⊑l ()) _
stamp!ₗ-prec {low} (inj id) id prec l≼l = ⊑-castr prec ⋆⊑ ⋆⊑
stamp!ₗ-prec {high} (inj id) id prec l≼l = ⊑-castr prec ⋆⊑ ⋆⊑
stamp!ₗ-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = !⊑↑!
stamp!ₗ-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) l≼h = prec-refl _
stamp!ₗ-prec {low} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = prec-refl _
stamp!ₗ-prec {high} (inj id) id (⊑-castl prec l⊑l x₁) h≼h = prec-refl _
stamp!ₗ-prec (inj id) (inj id) prec l≼l = prec-refl _
stamp!ₗ-prec {low} (inj id) (inj id) prec l≼h = !⊑↑!
stamp!ₗ-prec {high} (inj id) (inj id) prec l≼h = prec-refl _
stamp!ₗ-prec (inj id) (inj id) prec h≼h = prec-refl _
stamp!ₗ-prec (inj id) (inj (up id)) prec l≼l = !⊑↑!
stamp!ₗ-prec (inj id) (inj (up id)) prec l≼h = !⊑↑!
stamp!ₗ-prec (inj id) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-prec (inj id) (up id) prec l≼l = !⊑↑!
stamp!ₗ-prec (inj id) (up id) prec l≼h = !⊑↑!
stamp!ₗ-prec (inj id) (up id) prec h≼h = prec-refl _
stamp!ₗ-prec (inj (up id)) id (⊑-castl prec () _) leq
stamp!ₗ-prec (inj (up id)) (inj id) (⊑-castr (⊑-castl prec () _) _ _) leq
stamp!ₗ-prec (inj (up id)) (inj (up id)) prec l≼l = prec-refl _
stamp!ₗ-prec (inj (up id)) (inj (up id)) prec l≼h = prec-refl _
stamp!ₗ-prec (inj (up id)) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-prec (inj (up id)) (up id) prec l≼l = prec-refl _
stamp!ₗ-prec (inj (up id)) (up id) prec l≼h = prec-refl _
stamp!ₗ-prec (inj (up id)) (up id) prec h≼h = prec-refl _
stamp!ₗ-prec (up id) id (⊑-castl prec _ ()) leq
stamp!ₗ-prec (up id) (inj id) (⊑-cast prec _ ()) leq
stamp!ₗ-prec (up id) (inj id) (⊑-castl prec _ ()) leq
stamp!ₗ-prec (up id) (inj id) (⊑-castr prec () _) leq
stamp!ₗ-prec (up id) (inj (up id)) prec l≼l = prec-refl _
stamp!ₗ-prec (up id) (inj (up id)) prec l≼h = prec-refl _
stamp!ₗ-prec (up id) (inj (up id)) prec h≼h = prec-refl _
stamp!ₗ-prec (up id) (up id) prec l≼l = prec-refl _
stamp!ₗ-prec (up id) (up id) prec l≼h = prec-refl _
stamp!ₗ-prec (up id) (up id) prec h≼h = prec-refl _


stamp⋆ₗ⊑↑ :  {} ( : CExpr l low  )
   (𝓋 : CVal )
    stampₗ  𝓋   id (l low)  
stamp⋆ₗ⊑↑ { = high} (id .(l low)  (_ !)) (inj id) = ↑!⊑↑
stamp⋆ₗ⊑↑ { = low} (id .(l low)  (_ !)) (inj id) = !⊑↑
stamp⋆ₗ⊑↑ { = high} (id .(l low)    (_ !)) (inj (up id)) = ↑!⊑↑
stamp⋆ₗ⊑↑ { = low} (id .(l low)    (_ !)) (inj (up id)) = ↑!⊑↑

stamp!ₗ⊑↑ :  {g } ( : CExpr l low  g)
   (𝓋 : CVal )
    stamp!ₗ  𝓋   id (l low)  
stamp!ₗ⊑↑ { = high} (id .(l low)) id = ↑!⊑↑
stamp!ₗ⊑↑ { = low} (id .(l low)) id = !⊑↑
stamp!ₗ⊑↑ { = high} (id .(l low)  (_ !)) (inj id) = ↑!⊑↑
stamp!ₗ⊑↑ { = low} (id .(l low)  (_ !)) (inj id) = !⊑↑
stamp!ₗ⊑↑ { = high} (id .(l low)    (_ !)) (inj (up id)) = ↑!⊑↑
stamp!ₗ⊑↑ { = low} (id .(l low)    (_ !)) (inj (up id)) = ↑!⊑↑
stamp!ₗ⊑↑ { = high} (id .(l low)  ) (up id) = ↑!⊑↑
stamp!ₗ⊑↑ { = low} (id .(l low)  ) (up id) = ↑!⊑↑

stamp⋆ₗ⊑ℓ :  { ℓ′} ( : CExpr l   )
   ⊢l   l 
   (𝓋 : CVal )
   ℓ′  
   ⊢l stampₗ  𝓋 ℓ′  l 
stamp⋆ₗ⊑ℓ (id (l low)  _ !) c̅⊑ℓ (inj id) l≼l = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ⊑ℓ (id (l high)  _ !) c̅⊑ℓ (inj id) l≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ⊑ℓ (id (l high)  _ !) c̅⊑ℓ (inj id) h≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp⋆ₗ⊑ℓ (id (l low)    _ !) (⊑-cast _ () _) (inj (up id)) l≼l

stamp!ₗ⊑ℓ :  {g  ℓ′} ( : CExpr l   g)
   ⊢l   l 
   (𝓋 : CVal )
   ℓ′  
   ⊢l stamp!ₗ  𝓋 ℓ′  l 
stamp!ₗ⊑ℓ (id (l low)) c̅⊑ℓ id l≼l = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l high)) c̅⊑ℓ id l≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l high)) c̅⊑ℓ id h≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l low)  _ !) c̅⊑ℓ (inj id) l≼l = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l high)  _ !) c̅⊑ℓ (inj id) l≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l high)  _ !) c̅⊑ℓ (inj id) h≼h = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
stamp!ₗ⊑ℓ (id (l low)    _ !) (⊑-cast _ () _) (inj (up id)) l≼l
stamp!ₗ⊑ℓ (id .(l low)  ) (⊑-cast _ _ ()) (up id)