module Common.CoercePrecision where

open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; subst)
open import Function using (case_of_; case_return_of_)

open import Common.Utils
open import Common.Types
open import Common.BlameLabels
open import CoercionExpr.CoercionExpr
  hiding   (Progress; progress)
  renaming (_—→⟨_⟩_ to _—→ₗ⟨_⟩_; _∎ to _∎ₗ ;
            _—→_ to _—→ₗ_; _—↠_ to _—↠ₗ_;
            plug-cong to plug-congₗ)
  public
open import CoercionExpr.SecurityLevel renaming (∥_∥ to ∥_∥ₗ) public
open import CoercionExpr.Stamping
open import CoercionExpr.SyntacComp renaming (_⨟_ to _⊹⊹_)
open import CoercionExpr.Precision
open import Common.Coercions
open import Common.CoercionPrecision


private
  id⋆⊑coerce-to⋆ :  {g}   id   coerce g ⇒⋆
  id⋆⊑coerce-to⋆ {} = ⊑-id ⋆⊑
  id⋆⊑coerce-to⋆ {l x} = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑


coerceₗ-prec :  {g₁ g₂ g₃ g₄} {p q}
   g₁ ⊑ₗ g₂
   g₃ ⊑ₗ g₄
   (g₁≾g₃ : g₁  g₃)
   (g₂≾g₄ : g₂  g₄)
    ----------------------------------------
    coerceₗ g₁≾g₃ p  coerceₗ g₂≾g₄ q
coerceₗ-prec {g₂ = l } ⋆⊑ ⋆⊑ ≾-⋆r ≾-⋆r = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec {g₂ = } ⋆⊑ ⋆⊑ ≾-⋆r ≾-⋆r = ⊑-id ⋆⊑
coerceₗ-prec l⊑l g₃⊑g₄ ≾-⋆r ≾-⋆r = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
coerceₗ-prec {g₄ = l } ⋆⊑ ⋆⊑ ≾-⋆r ≾-⋆l = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec {g₄ = } ⋆⊑ ⋆⊑ ≾-⋆r ≾-⋆l = ⊑-id ⋆⊑
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆r (≾-l l≼l) = ⊑-id ⋆⊑
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆r (≾-l l≼h) = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆r (≾-l h≼h) = ⊑-id ⋆⊑
coerceₗ-prec l⊑l ⋆⊑ ≾-⋆r (≾-l l≼l) = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
coerceₗ-prec l⊑l ⋆⊑ ≾-⋆r (≾-l l≼h) = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
coerceₗ-prec l⊑l ⋆⊑ ≾-⋆r (≾-l h≼h) = ⊑-castl (⊑-id l⊑l) l⊑l ⋆⊑
coerceₗ-prec {g₂ = l } ⋆⊑ ⋆⊑ ≾-⋆l ≾-⋆r = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec {g₂ = } ⋆⊑ ⋆⊑ ≾-⋆l ≾-⋆r = ⊑-id ⋆⊑
coerceₗ-prec {g₄ = l } ⋆⊑ ⋆⊑ ≾-⋆l ≾-⋆l = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec {g₄ = } ⋆⊑ ⋆⊑ ≾-⋆l ≾-⋆l = ⊑-id ⋆⊑
coerceₗ-prec ⋆⊑ l⊑l ≾-⋆l ≾-⋆l = ⊑-cast (⊑-id ⋆⊑) ⋆⊑ l⊑l
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆l (≾-l l≼l) = ⊑-id ⋆⊑
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆l (≾-l l≼h) = ⊑-castr (⊑-id ⋆⊑) ⋆⊑ ⋆⊑
coerceₗ-prec ⋆⊑ ⋆⊑ ≾-⋆l (≾-l h≼h) = ⊑-id ⋆⊑
coerceₗ-prec ⋆⊑ l⊑l ≾-⋆l (≾-l l≼l) = ⊑-castl (⊑-id ⋆⊑) ⋆⊑ l⊑l
coerceₗ-prec ⋆⊑ l⊑l ≾-⋆l (≾-l l≼h) = ⊑-cast (⊑-id ⋆⊑) ⋆⊑ l⊑l
coerceₗ-prec ⋆⊑ l⊑l ≾-⋆l (≾-l h≼h) = ⊑-castl (⊑-id ⋆⊑) ⋆⊑ l⊑l
coerceₗ-prec l⊑l l⊑l (≾-l l≼l) (≾-l l≼l) = ⊑-id l⊑l
coerceₗ-prec l⊑l l⊑l (≾-l l≼h) (≾-l l≼h) = ⊑-cast (⊑-id l⊑l) l⊑l l⊑l
coerceₗ-prec l⊑l l⊑l (≾-l h≼h) (≾-l h≼h) = ⊑-id l⊑l

coerceₗ-prec-left :  {g₁ g₂ g} {p}
   g₁ ⊑ₗ g
   g₂ ⊑ₗ g
   (g₁≾g₂ : g₁  g₂)
    ----------------------------------------
   ⊢l coerceₗ g₁≾g₂ p  g
coerceₗ-prec-left ⋆⊑ ⋆⊑ ≾-⋆r = ⊑-id ⋆⊑
coerceₗ-prec-left ⋆⊑ ⋆⊑ ≾-⋆l = ⊑-id ⋆⊑
coerceₗ-prec-left ⋆⊑ l⊑l ≾-⋆l = ⊑-cast (⊑-id ⋆⊑) ⋆⊑ l⊑l
coerceₗ-prec-left l⊑l ⋆⊑ ≾-⋆r = ⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑
coerceₗ-prec-left l⊑l l⊑l (≾-l l≼l) = ⊑-id l⊑l
coerceₗ-prec-left l⊑l l⊑l (≾-l h≼h) = ⊑-id l⊑l

coerce-prec :  {A A′ B B′} {p q}
   A   A′
   B   B′
   (A≲B   : A   B )
   (A′≲B′ : A′  B′)
    ----------------------------------------
    coerce A≲B p ⟩⊑⟨ coerce A′≲B′ q 
coerce-prec (⊑-ty g₁⊑g₂ ⊑-ι) (⊑-ty g₃⊑g₄ ⊑-ι) (≲-ty g₁≲g₃ ≲-ι) (≲-ty g₂≲g₄ ≲-ι) =
  ⊑-base (coerceₗ-prec g₁⊑g₂ g₃⊑g₄ g₁≲g₃ g₂≲g₄)
coerce-prec (⊑-ty g₁⊑g₃ (⊑-ref A⊑C)) (⊑-ty g₂⊑g₄ (⊑-ref B⊑D))
            (≲-ty g₁≲g₂ (≲-ref A≲B B≲A)) (≲-ty g₃≲g₄ (≲-ref C≲D D≲C)) =
  ⊑-ref (coerce-prec B⊑D A⊑C B≲A D≲C) (coerce-prec A⊑C B⊑D A≲B C≲D) (coerceₗ-prec g₁⊑g₃ g₂⊑g₄ g₁≲g₂ g₃≲g₄)
coerce-prec (⊑-ty g₁⊑g₂ (⊑-fun gc₁⊑gc₂ A⊑C B⊑D)) (⊑-ty g₃⊑g₄ (⊑-fun gc₃⊑gc₄ A′⊑C′ B′⊑D′))
            (≲-ty g₁≲g₃ (≲-fun gc₃≲gc₁ A′≲A B≲B′)) (≲-ty g₂≲g₄ (≲-fun gc₄≲gc₂ C′≲C D≲D′)) =
  ⊑-fun (coerceₗ-prec gc₃⊑gc₄ gc₁⊑gc₂ gc₃≲gc₁ gc₄≲gc₂)
        (coerce-prec A′⊑C′ A⊑C A′≲A C′≲C)
        (coerce-prec B⊑D B′⊑D′ B≲B′ D≲D′)
        (coerceₗ-prec g₁⊑g₂ g₃⊑g₄ g₁≲g₃ g₂≲g₄)


coerce-prec-left :  {A B C} {p}
   A  C
   B  C
   (A≲B : A  B)
    ----------------------------------------
    coerce A≲B p ⟩⊑ C
coerce-prec-left (⊑-ty g₁⊑g ⊑-ι) (⊑-ty g₂⊑g ⊑-ι) (≲-ty g₁≲g₂ ≲-ι) =
  ⊑-base (coerceₗ-prec-left g₁⊑g g₂⊑g g₁≲g₂)
coerce-prec-left (⊑-ty g₁⊑g (⊑-ref A⊑C)) (⊑-ty g₂⊑g (⊑-ref B⊑C))
                 (≲-ty g₁≲g₂ (≲-ref A≲B B≲A)) =
  ⊑-ref (coerce-prec-left B⊑C A⊑C B≲A) (coerce-prec-left A⊑C B⊑C A≲B) (coerceₗ-prec-left g₁⊑g g₂⊑g g₁≲g₂)
coerce-prec-left (⊑-ty g₁⊑g (⊑-fun gc₁⊑gc A⊑E B⊑F)) (⊑-ty g₂⊑g (⊑-fun gc₂⊑gc C⊑E D⊑F))
            (≲-ty g₁≲g₂ (≲-fun gc₂≲gc₁ C≲A B≲D)) =
  ⊑-fun (coerceₗ-prec-left gc₂⊑gc gc₁⊑gc gc₂≲gc₁)
        (coerce-prec-left C⊑E A⊑E C≲A)
        (coerce-prec-left B⊑F D⊑F B≲D)
        (coerceₗ-prec-left g₁⊑g g₂⊑g g₁≲g₂)

coerce-id-prec-left :  {A B}
   A  B
    ------------------------
    coerce-id A ⟩⊑ B
coerce-id-prec-left (⊑-ty x ⊑-ι) = ⊑-base (⊑-id x)
coerce-id-prec-left (⊑-ty x (⊑-ref x₁)) =
  ⊑-ref (coerce-id-prec-left x₁) (coerce-id-prec-left x₁) (⊑-id x)
coerce-id-prec-left (⊑-ty x (⊑-fun x₁ x₂ x₃)) =
  ⊑-fun (⊑-id x₁) (coerce-id-prec-left x₂) (coerce-id-prec-left x₃) (⊑-id x)

coerce-id-prec :  {A B}
   A  B
    ------------------------
    coerce-id A ⟩⊑⟨ coerce-id B 
coerce-id-prec (⊑-ty x ⊑-ι) = ⊑-base (⊑-id x)
coerce-id-prec (⊑-ty x (⊑-ref x₁)) =
  ⊑-ref (coerce-id-prec x₁) (coerce-id-prec x₁) (⊑-id x)
coerce-id-prec (⊑-ty x (⊑-fun x₁ x₂ x₃)) =
  ⊑-fun (⊑-id x₁) (coerce-id-prec x₂) (coerce-id-prec x₃) (⊑-id x)

inject-prec-left :  {T g A}
   T of g  A
    ----------------------------
    inject T g ⟩⊑ A
inject-prec-left (⊑-ty ⋆⊑ ⊑-ι) = ⊑-base (⊑-id ⋆⊑)
inject-prec-left (⊑-ty l⊑l ⊑-ι ) = ⊑-base (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)
inject-prec-left (⊑-ty ⋆⊑ (⊑-ref x)) =
  ⊑-ref (coerce-id-prec-left x) (coerce-id-prec-left x) (⊑-id ⋆⊑)
inject-prec-left (⊑-ty l⊑l (⊑-ref x)) =
  ⊑-ref (coerce-id-prec-left x) (coerce-id-prec-left x) (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)
inject-prec-left (⊑-ty ⋆⊑ (⊑-fun x x₁ x₂)) =
  ⊑-fun (⊑-id x) (coerce-id-prec-left x₁) (coerce-id-prec-left x₂) (⊑-id ⋆⊑)
inject-prec-left (⊑-ty l⊑l (⊑-fun x x₁ x₂)) =
  ⊑-fun (⊑-id x) (coerce-id-prec-left x₁) (coerce-id-prec-left x₂) (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)


inject-prec :  {S T g g′}
   S of g  T of g′
    ----------------------------
    inject S g ⟩⊑⟨ inject T g′ 
inject-prec (⊑-ty ⋆⊑ ⊑-ι) = ⊑-base id⋆⊑coerce-to⋆
inject-prec (⊑-ty l⊑l ⊑-ι ) = ⊑-base (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)
inject-prec (⊑-ty ⋆⊑ (⊑-ref x)) =
  ⊑-ref (coerce-id-prec x) (coerce-id-prec x) id⋆⊑coerce-to⋆
inject-prec (⊑-ty l⊑l (⊑-ref x)) =
  ⊑-ref (coerce-id-prec x) (coerce-id-prec x) (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)
inject-prec (⊑-ty ⋆⊑ (⊑-fun x x₁ x₂)) =
  ⊑-fun (⊑-id x) (coerce-id-prec x₁) (coerce-id-prec x₂) id⋆⊑coerce-to⋆
inject-prec (⊑-ty l⊑l (⊑-fun x x₁ x₂)) =
  ⊑-fun (⊑-id x) (coerce-id-prec x₁) (coerce-id-prec x₂) (⊑-cast (⊑-id l⊑l) l⊑l ⋆⊑)