{-# OPTIONS --without-K --safe #-}

-- In this module, we define the subtyping relation of D<: according to Lemma 2.
module Dsub where

open import Data.List as List
open import Data.List.All as All
open import Data.Nat as 
open import Data.Maybe as Maybe
open import Data.Product as Σ
open import Data.Sum
open import Data.Empty renaming ( to False)
open import Data.Vec as Vec renaming (_∷_ to _‼_ ; [] to nil) hiding (_++_)
open import Function

open import Data.Maybe.Properties as Maybeₚ
open import Data.Nat.Properties as ℕₚ
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality

open import DsubDef
open import Utils

module Dsub where

  infix 4 _⊢_<:_

  data _⊢_<:_ : Env  Typ  Typ  Set where
    dtop   :  {Γ T} 
      Γ  T <: 
    dbot   :  {Γ T} 
      Γ   <: T
    drefl  :  {Γ T} 
      Γ  T <: T
    dtrans :  {Γ S U} T 
      (S<:T : Γ  S <: T) 
      (T<:U : Γ  T <: U) 
      Γ  S <: U
    dbnd   :  {Γ S U S′ U′} 
      (S′<:S : Γ  S′ <: S) 
      (U<:U′ : Γ  U <: U′) 
      Γ  ⟨A: S  U  <: ⟨A: S′  U′ 
    dall   :  {Γ S U S′ U′} 
      (S′<:S : Γ  S′ <: S) 
      (U<:U′ : (S′  Γ)  U <: U′) 
      Γ  Π S  U <: Π S′  U′
    dsel₁  :  {Γ n T S} 
      (T∈Γ : env-lookup Γ n  just T) 
      (D : Γ  T <: ⟨A: S   ) 
      Γ  S <: n ∙A
    dsel₂  :  {Γ n T U} 
      (T∈Γ : env-lookup Γ n  just T) 
      (D : Γ  T <: ⟨A:   U ) 
      Γ  n ∙A <: U

  D-measure :  {Γ S U} (D : Γ  S <: U)  
  D-measure dtop                 = 1
  D-measure dbot                 = 1
  D-measure drefl                = 1
  D-measure (dtrans _ S<:T T<:U) = 1 + D-measure S<:T + D-measure T<:U
  D-measure (dbnd D₁ D₂)         = 1 + D-measure D₁ + D-measure D₂
  D-measure (dall Dp Db)         = 1 + D-measure Dp + D-measure Db
  D-measure (dsel₁ T∈Γ D)        = 1 + D-measure D
  D-measure (dsel₂ T∈Γ D)        = 1 + D-measure D

  D-measure≥1 :  {Γ S U} (D : Γ  S <: U)  D-measure D  1
  D-measure≥1 dtop           = s≤s z≤n
  D-measure≥1 dbot           = s≤s z≤n
  D-measure≥1 drefl          = s≤s z≤n
  D-measure≥1 (dtrans _ _ _) = s≤s z≤n
  D-measure≥1 (dbnd _ _)     = s≤s z≤n
  D-measure≥1 (dall _ _)     = s≤s z≤n
  D-measure≥1 (dsel₁ _ _)    = s≤s z≤n
  D-measure≥1 (dsel₂ _ _)    = s≤s z≤n

  D-deriv : Set
  D-deriv = Σ (Env × Typ × Typ) λ { (Γ , S , U)  Γ  S <: U }

  env : D-deriv  Env
  env ((Γ , _) , _) = Γ

  typ₁ : D-deriv  Typ
  typ₁ ((_ , S , U) , _) = S

  typ₂ : D-deriv  Typ
  typ₂ ((_ , S , U) , _) = U

  drefl-dec :  (D : D-deriv)  Dec (D  ((env D , typ₁ D , _) , drefl))
  drefl-dec (_ , dtop)         = no  ())
  drefl-dec (_ , dbot)         = no  ())
  drefl-dec (_ , drefl)        = yes refl
  drefl-dec (_ , dtrans _ _ _) = no  ())
  drefl-dec (_ , dbnd _ _)     = no  ())
  drefl-dec (_ , dall _ _)     = no  ())
  drefl-dec (_ , dsel₁ _ _)    = no  ())
  drefl-dec (_ , dsel₂ _ _)    = no  ())

  -- weakening
  infixl 7 _⇑
  _⇑ : Env  Env
  []       = []
  (T  Γ)  = T  length Γ  Γ 

  ↦∈-weaken-≤ :  {n T Γ} 
                  n  T  Γ 
                 Γ₁ Γ₂ T′ 
                  Γ  Γ₁  Γ₂ 
                  length Γ₂  n 
                  suc n  T  length Γ₂  Γ₁  T′ !  Γ₂ 
  ↦∈-weaken-≤ hd .(_  _) [] T′ refl z≤n       = tl hd
  ↦∈-weaken-≤ hd Γ₁ (_  Γ₂) T′ eqΓ ()
  ↦∈-weaken-≤ (tl T∈Γ) .(_  _) [] T′ refl z≤n = tl (tl T∈Γ)
  ↦∈-weaken-≤ (tl {_} {T} T∈Γ) Γ₁ (_  Γ₂) T′ refl (s≤s l≤n)
    rewrite ↑-↑-comm T 0 (length Γ₂) z≤n       = tl (↦∈-weaken-≤ T∈Γ Γ₁ Γ₂ T′ refl l≤n)
  
  ↦∈-weaken-> :  {n T Γ} 
                  n  T  Γ 
                 Γ₁ Γ₂ T′ 
                  Γ  Γ₁  Γ₂ 
                  length Γ₂ > n 
                  n  T  length Γ₂  Γ₁  T′ !  Γ₂ 
  ↦∈-weaken-> T∈Γ Γ₁ [] T′ eqΓ ()
  ↦∈-weaken-> (hd {T}) Γ₁ (_  Γ₂) T′ refl (s≤s l>n)
    rewrite ↑-↑-comm T 0 (length Γ₂) l>n = hd
  ↦∈-weaken-> (tl {_} {T} T∈Γ) Γ₁ (_  Γ₂) T′ refl (s≤s l>n)
    rewrite ↑-↑-comm T 0 (length Γ₂) z≤n = tl (↦∈-weaken-> T∈Γ Γ₁ Γ₂ T′ refl l>n)
  
  ↦∈-weaken :  {n T Γ} 
                n  T  Γ 
               Γ₁ Γ₂ T′ 
                Γ  Γ₁  Γ₂ 
                ↑-idx n (length Γ₂)  T  length Γ₂  Γ₁  T′ !  Γ₂ 
  ↦∈-weaken {n} T∈Γ _ Γ₂ _ eqΓ with length Γ₂ ≤? n
  ... | yes l≤n = ↦∈-weaken-≤ T∈Γ _ Γ₂ _ eqΓ l≤n
  ... | no l>n  = ↦∈-weaken-> T∈Γ _ Γ₂ _ eqΓ (≰⇒> l>n)
  
  ↦∈-weaken′ :  {n T Γ} 
                 env-lookup Γ n  just T 
                Γ₁ Γ₂ T′ 
                 Γ  Γ₁  Γ₂ 
                 env-lookup (Γ₁  T′ !  Γ₂ ) (↑-idx n (length Γ₂))  just (T  length Γ₂)
  ↦∈-weaken′ T∈Γ Γ₁ Γ₂ T′ eqΓ = ↦∈⇒lookup (↦∈-weaken (lookup⇒↦∈ T∈Γ) Γ₁ Γ₂ T′ eqΓ)
  
  <:-weakening-gen :  {Γ S U} 
                        Γ  S <: U 
                       Γ₁ Γ₂ T 
                        Γ  Γ₁  Γ₂ 
                        Γ₁  T !  Γ₂   S  length Γ₂ <: U  length Γ₂
  <:-weakening-gen dtop Γ₁ Γ₂ T eqΓ                    = dtop
  <:-weakening-gen dbot Γ₁ Γ₂ T eqΓ                    = dbot
  <:-weakening-gen drefl Γ₁ Γ₂ T eqΓ                   = drefl
  <:-weakening-gen (dtrans T′ S<:T′ T′<:U) Γ₁ Γ₂ T eqΓ = dtrans _
                                                                (<:-weakening-gen S<:T′ Γ₁ Γ₂ T eqΓ)
                                                                (<:-weakening-gen T′<:U Γ₁ Γ₂ T eqΓ)
  <:-weakening-gen (dbnd S′<:S U<:U′) Γ₁ Γ₂ T eqΓ      = dbnd (<:-weakening-gen S′<:S Γ₁ Γ₂ T eqΓ)
                                                              (<:-weakening-gen U<:U′ Γ₁ Γ₂ T eqΓ)
  <:-weakening-gen (dall S′<:S U<:U′) Γ₁ Γ₂ T eqΓ      = dall (<:-weakening-gen S′<:S Γ₁ Γ₂ T eqΓ)
                                                              (<:-weakening-gen U<:U′ Γ₁ (_  Γ₂) T (cong (_ ∷_) eqΓ))
  <:-weakening-gen (dsel₁ {_} {n} T∈Γ T<:B) Γ₁ Γ₂ T eqΓ 
    rewrite ↑-var n (length Γ₂)                        = dsel₁ (↦∈-weaken′ T∈Γ Γ₁ Γ₂ T eqΓ)
                                                               (<:-weakening-gen T<:B Γ₁ Γ₂ T eqΓ) 
  <:-weakening-gen (dsel₂ {_} {n} T∈Γ T<:B) Γ₁ Γ₂ T eqΓ
    rewrite ↑-var n (length Γ₂)                        = dsel₂ (↦∈-weaken′ T∈Γ Γ₁ Γ₂ T eqΓ)
                                                               (<:-weakening-gen T<:B Γ₁ Γ₂ T eqΓ) 
  
  <:-weakening :  {Γ₁ Γ₂ S U} T 
                    Γ₁  Γ₂  S <: U 
                    Γ₁  T !  Γ₂   S  length Γ₂ <: U  length Γ₂
  <:-weakening T S<:U = <:-weakening-gen S<:U _ _ T refl
  
  <:-weakening-hd :  {Γ S U} T 
                       Γ  S <: U 
                       Γ  T !  S  0 <: U  0
  <:-weakening-hd T = <:-weakening {Γ₂ = []} T
  
  -- narrowing
  infix 4 _≺:[_]_
  data _≺:[_]_ : Env    Env  Set where
    ≺[_,_] :  {Γ U} S  Γ  S <: U  Γ  S ! ≺:[ 0 ] Γ  U !
    _∷_    :  {Γ₁ n Γ₂} T  Γ₁ ≺:[ n ] Γ₂  Γ₁  T ! ≺:[ suc n ] Γ₂  T !

  <:∈-find :  {x T Γ Γ′ n} 
               x  T  Γ 
               Γ′ ≺:[ n ] Γ 
               x  n × ( λ T′  n  T′  Γ′ × Γ′  T′ <: T)  x  n × x  T  Γ′
  <:∈-find hd ≺[ T′ , T′<:T ]           = inj₁ (refl , T′  0 , hd , <:-weakening-hd T′ T′<:T)
  <:∈-find hd (T  Γ′≺:Γ)               = inj₂ ((λ ()) , hd)
  <:∈-find (tl T∈Γ) ≺[ T′ , T′<:T ]     = inj₂ ((λ ()) , tl T∈Γ)
  <:∈-find (tl T∈Γ) (S  Γ′≺:Γ) with <:∈-find T∈Γ Γ′≺:Γ
  ... | inj₁ (x≡n , T′ , T′∈Γ′ , T′<:T) = inj₁ (cong suc x≡n , T′  0 , tl T′∈Γ′ , <:-weakening-hd S T′<:T)
  ... | inj₂ (x≢n , T∈Γ′)               = inj₂ (x≢n  suc-injective , tl T∈Γ′)

  <:∈-find′ :  {x T Γ Γ′ n} 
                env-lookup Γ x  just T 
                Γ′ ≺:[ n ] Γ 
                x  n × ( λ T′  env-lookup Γ′ n  just T′ × Γ′  T′ <: T)  x  n × env-lookup Γ′ x  just T
  <:∈-find′ T∈Γ Γ′≺Γ with <:∈-find (lookup⇒↦∈ T∈Γ) Γ′≺Γ
  ... | inj₁ (x≡n , T′ , T′∈Γ′ , T′<:T) = inj₁ (x≡n , T′ , ↦∈⇒lookup T′∈Γ′ , T′<:T)
  ... | inj₂ (x≢n , T∈Γ′)               = inj₂ (x≢n , ↦∈⇒lookup T∈Γ′)

  <:-narrow :  {Γ Γ′ n S U T} 
                Γ  S <: U 
                Γ′ ≺:[ n ] Γ 
                env-lookup Γ n  just T 
                Γ′  S <: U
  <:-narrow dtop Γ′≺Γ T∈Γ                         = dtop
  <:-narrow dbot Γ′≺Γ T∈Γ                         = dbot
  <:-narrow drefl Γ′≺Γ T∈Γ                        = drefl
  <:-narrow (dtrans T S<:T T<:U) Γ′≺Γ T∈Γ         = dtrans T (<:-narrow S<:T Γ′≺Γ T∈Γ) (<:-narrow T<:U Γ′≺Γ T∈Γ)
  <:-narrow (dbnd S′<:S U<:U′) Γ′≺Γ T∈Γ           = dbnd (<:-narrow S′<:S Γ′≺Γ T∈Γ) (<:-narrow U<:U′ Γ′≺Γ T∈Γ)
  <:-narrow  {Γ} {Γ′} {n} (dall {S′ = S′} S′<:S U<:U′) Γ′≺Γ T∈Γ
    = dall (<:-narrow S′<:S Γ′≺Γ T∈Γ)
           (<:-narrow U<:U′ (_  Γ′≺Γ) (↦∈⇒lookup (tl {n} {T′ = S′} {Γ} (lookup⇒↦∈ T∈Γ))))
  <:-narrow (dsel₁ T′∈Γ T′<:B) Γ′≺Γ T∈Γ
    with <:∈-find′ T′∈Γ Γ′≺Γ
  ...  | inj₁ (refl , T″ , T″∈Γ′ , T″<:T)
    rewrite just-injective (trans (sym T′∈Γ) T∈Γ) = dsel₁ T″∈Γ′ (dtrans _ T″<:T (<:-narrow T′<:B Γ′≺Γ T∈Γ))
  ... | inj₂ (x≢n , T′∈Γ′)                        = dsel₁ T′∈Γ′ (<:-narrow T′<:B Γ′≺Γ T∈Γ)
  <:-narrow (dsel₂ T′∈Γ T′<:B) Γ′≺Γ T∈Γ
    with <:∈-find′ T′∈Γ Γ′≺Γ
  ...  | inj₁ (refl , T″ , T″∈Γ′ , T″<:T)
    rewrite just-injective (trans (sym T′∈Γ) T∈Γ) = dsel₂ T″∈Γ′ (dtrans _ T″<:T (<:-narrow T′<:B Γ′≺Γ T∈Γ))
  ... | inj₂ (x≢n , T′∈Γ′)                        = dsel₂ T′∈Γ′ (<:-narrow T′<:B Γ′≺Γ T∈Γ)

open Dsub hiding (<:-weakening-gen; <:-weakening; <:-weakening-hd
                 ; _≺:[_]_; ≺[_,_]; _∷_; <:∈-find; <:∈-find′; <:-narrow) public

-- materialization of inversion properties in invertible contexts
module DsubInvProperties {P : Env  Set}
                         (invertible : InvertibleEnv P) where

  module ContraEnvProperties = InvertibleProperties invertible _⊢_<:_
  open ContraEnvProperties public

  mutual
    <:⇒<:ᵢ :  {Γ S U}  Γ  S <: U  P Γ  Γ ⊢ᵢ S <: U
    <:⇒<:ᵢ dtop                  = ditop
    <:⇒<:ᵢ dbot                  = dibot
    <:⇒<:ᵢ drefl                 = direfl
    <:⇒<:ᵢ (dtrans T S<:T T<:U)  = ditrans T (<:⇒<:ᵢ S<:T ) (<:⇒<:ᵢ T<:U )
    <:⇒<:ᵢ (dbnd S′<:S U<:U′)    = dibnd (<:⇒<:ᵢ S′<:S ) (<:⇒<:ᵢ U<:U′ )
    <:⇒<:ᵢ (dall S′<:S U<:U′)    = diall (<:⇒<:ᵢ S′<:S ) U<:U′
    <:⇒<:ᵢ (dsel₁ T∈Γ T<:B)  with sel-case T∈Γ T<:B 
    ... | refl , _                 = dibot
    <:⇒<:ᵢ (dsel₂ T∈Γ T<:B)  with sel-case T∈Γ T<:B 
    ... | refl , U′ , refl
        with ⟨A:⟩<:⟨A:⟩ (<:⇒<:ᵢ T<:B ) 
    ...    | _ , U′<:U             = ditrans U′ (disel T∈Γ) U′<:U

    sel-case :  {T Γ n S U} 
                 env-lookup Γ n  just T 
                 Γ  T <: ⟨A: S  U  
                 P Γ 
                 S   ×  λ U′  T  ⟨A:   U′ 
    sel-case {} T∈Γ T<:B  = case ⊤<: (<:⇒<:ᵢ T<:B ) of λ ()
    sel-case {} T∈Γ T<:B  = ⊥-elim (no-⊥  T∈Γ)
    sel-case {n ∙A} T∈Γ T<:B 
      with <:⟨A:⟩ (<:⇒<:ᵢ T<:B ) 
    ...  | inj₁ ()
    ...  | inj₂ (_ , _ , ())
    sel-case {Π S  U} T∈Γ T<:B 
      with <:⟨A:⟩ (<:⇒<:ᵢ T<:B ) 
    ...  | inj₁ ()
    ...  | inj₂ (_ , _ , ())
    sel-case {⟨A: S  U } T∈Γ T<:B 
      rewrite ⊥-lb  T∈Γ with ⟨A:⟩<:⟨A:⟩ (<:⇒<:ᵢ T<:B ) 
    ... | S<:⊥ , _ with <:⊥ S<:⊥ 
    ...               | refl = refl , U , refl

  <:ᵢ⇒<: :  {Γ S U}  Γ ⊢ᵢ S <: U  Γ  S <: U
  <:ᵢ⇒<: ditop                 = dtop
  <:ᵢ⇒<: dibot                 = dbot
  <:ᵢ⇒<: direfl                = drefl
  <:ᵢ⇒<: (ditrans T S<:T T<:U) = dtrans T (<:ᵢ⇒<: S<:T) (<:ᵢ⇒<: T<:U)
  <:ᵢ⇒<: (dibnd S′<:S U<:U′)   = dbnd (<:ᵢ⇒<: S′<:S) (<:ᵢ⇒<: U<:U′)
  <:ᵢ⇒<: (diall S′<:S U<:U′)   = dall (<:ᵢ⇒<: S′<:S) U<:U′
  <:ᵢ⇒<: (disel T∈Γ)           = dsel₂ T∈Γ drefl

  ⊤<:′ :  {Γ T}  Γ   <: T  P Γ  T  
  ⊤<:′ ⊤<:T = ⊤<:  <:⇒<:ᵢ ⊤<:T
  
  <:⊥′ :  {Γ T}  Γ  T <:   P Γ  T  
  <:⊥′ T<:⊥  = <:⊥ (<:⇒<:ᵢ T<:⊥ ) 
  
  ⟨A:⟩<:⟨A:⟩′ :  {Γ S′ U U′}  Γ  ⟨A:   U  <: ⟨A: S′  U′   P Γ 
                        S′  
  ⟨A:⟩<:⟨A:⟩′ B<:B′  with ⟨A:⟩<:⟨A:⟩ (<:⇒<:ᵢ B<:B′ ) 
  ... | S<:⊥ , _ = <:⊥ S<:⊥ 
  
  Π<:⟨A:⟩⇒⊥ :  {Γ S U S′ U′}  Γ ⊢ᵢ Π S  U <: ⟨A: S′  U′   P Γ  False
  Π<:⟨A:⟩⇒⊥ Π<:⟨A:⟩  with <:⟨A:⟩ Π<:⟨A:⟩ 
  ... | inj₁ ()
  ... | inj₂ (_ , _ , ())
  
  ∙A<:⟨A:⟩⇒⊥ :  {Γ x S U}  Γ ⊢ᵢ x ∙A <: ⟨A: S  U   P Γ  False
  ∙A<:⟨A:⟩⇒⊥ x∙A<:⟨A:⟩  with <:⟨A:⟩ x∙A<:⟨A:⟩ 
  ... | inj₁ ()
  ... | inj₂ (_ , _ , ()) 
  
  Π<:Π :  {Γ S U S′ U′} 
            Γ  Π S  U <: Π S′  U′ 
            P Γ 
            Γ  S′ <: S × (S′  Γ)  U <: U′
  Π<:Π <:  = Σ.map₁ <:ᵢ⇒<: $ helper (<:⇒<:ᵢ <: ) 
    where helper :  {Γ S U S′ U′} 
                     Γ ⊢ᵢ Π S  U <: Π S′  U′ 
                     P Γ 
                     Γ ⊢ᵢ S′ <: S × (S′  Γ)  U <: U′
          helper direfl                            = direfl , drefl
          helper (ditrans T <: <:′)  with Π<: <:
          ... | inj₁ refl                            = case ⊤<: <:′ of λ ()
          ... | inj₂ (_ , _ , refl)
              with helper <:     | helper <:′ 
          ...    | S″<:S′ , U′<:U″ | S‴<:S″ , U″<:U‴ = ditrans _ S‴<:S″ S″<:S′
                                                     , dtrans _ (Dsub.<:-narrow U′<:U″ Dsub.≺[ _ , <:ᵢ⇒<: S‴<:S″ ] refl) U″<:U‴
          helper (diall <: U<:U′)                  = <: , U<:U′