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

-- In this module, we define F<:⁻, F<:ᵈ (F<: deterministic defined in Pierce92) and
-- show that F<:⁻ subtyping is undecidable.
module FsubMinus where

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

open import Relation.Nullary
open import Relation.Unary using () renaming (Decidable to UDecidable)
open import Relation.Binary using () renaming (Decidable to BDecidable)
open import Relation.Binary.PropositionalEquality as 

open import Data.Nat.Properties as ℕₚ
open import Data.Maybe.Properties as Maybeₚ
open import Utils

-- Definition of F<:⁻
--
-- F<:⁻ is defined by removing function types (_⟶_ in the paper) from F<:.
module FsubMinus where
  infix 6 var_
  infixr 6 Π<:_∙_

  -- Types
  --
  -- * ⊤ is a supertype of all types.
  -- * (var n) represents a type variable.
  -- * (Π<: S ∙ U) represent a universal type (∀ X <: S . U).
  data Ftyp : Set where
           : Ftyp
    var_    :   Ftyp
    Π<:_∙_  : Ftyp  Ftyp  Ftyp

  -- typing context
  Env : Set
  Env = List Ftyp

  -- shifting operation of de Bruijn indices
  infixl 7 _↑_
  _↑_ : Ftyp    Ftyp
    n           = 
  (var x)  n with n ≤? x
  ... | yes p     = var (suc x)
  ... | no ¬p     = var x
  (Π<: S  U)  n = Π<: S  n  U  suc n

  infix 4 _<:_∈_
  data _<:_∈_ :   Ftyp  Env  Set where
    hd :  {T Γ}  0 <: T  0  T  Γ
    tl :  {n T T′ Γ}  n <: T  Γ  suc n <: T  0  T′  Γ

  -- subtyping judgment of F<:⁻
  -- The statement of the rules should be self-explanatory.
  infix 4 _⊢F_<:_  
  data _⊢F_<:_ : Env  Ftyp  Ftyp  Set where
    ftop   :  {Γ T} 
      Γ ⊢F T <: 
    fvrefl :  {Γ n} 
      Γ ⊢F var n <: var n
    fbinds :  {Γ n T U} 
      n <: T  Γ 
      Γ ⊢F T <: U 
      Γ ⊢F var n <: U
    fall   :  {Γ S₁ S₂ U₁ U₂} 
      Γ ⊢F S₂ <: S₁ 
      Γ  S₂ ! ⊢F U₁ <: U₂ 
      Γ ⊢F Π<: S₁  U₁ <: Π<: S₂  U₂

  -- The remaining are some technical setup.
  env-lookup : Env    Maybe Ftyp
  env-lookup [] n = nothing
  env-lookup (T  Γ) zero = just $ T  0
  env-lookup (T  Γ) (suc n) = Maybe.map (_↑ 0) $ env-lookup Γ n

  lookup⇒<:∈ :  {Γ n T}  env-lookup Γ n  just T  n <: T  Γ
  lookup⇒<:∈ {[]} {n} ()
  lookup⇒<:∈ {T  Γ} {zero} refl = hd
  lookup⇒<:∈ {T  Γ} {suc n} eq with env-lookup Γ n | lookup⇒<:∈ {Γ} {n}
  ... | nothing | rec with eq
  ... | ()
  lookup⇒<:∈ {T  Γ} {suc n} refl | just T′ | rec = tl (rec refl) 

  <:∈⇒lookup :  {Γ n T}  n <: T  Γ  env-lookup Γ n  just T
  <:∈⇒lookup hd            = refl
  <:∈⇒lookup (tl <:∈)
    rewrite <:∈⇒lookup <:∈ = refl

  env-lookup′ : Env    Maybe Ftyp
  env-lookup′ Γ n = Maybe.map (repeat (suc n) (_↑ 0)) (lookupOpt Γ n)

  env-lookup-same :  Γ n  env-lookup Γ n  env-lookup′ Γ n
  env-lookup-same [] n          = refl
  env-lookup-same (T  Γ) zero  = refl
  env-lookup-same (T  Γ) (suc n)
    rewrite env-lookup-same Γ n = sym $ Maybeₚ.map-compose (lookupOpt Γ n)

  <:∈-map :  {a} {A : Set a} {n T} {f : A  Ftyp} {_↑′_ : A    A} Γ 
              (∀ a n  f a  n  f (a ↑′ n)) 
              n <: T  List.map f Γ   λ a  T  f a
  <:∈-map {f = f} Γ comm d = aux _ d refl
    where aux :  {n T Γ*} Γ  n <: T  Γ*  Γ*  List.map f Γ   λ a  T  f a
          aux [] hd ()
          aux (T  Γ) hd refl = -, comm _ _
          aux [] (tl <:∈) ()
          aux (T  Γ) (tl <:∈) refl with aux Γ <:∈ refl
          ... | a , refl      = -, comm _ _

  <:∈-func :  {x T T′ Γ}  x <: T  Γ  x <: T′  Γ  T  T′
  <:∈-func hd hd              = refl
  <:∈-func (tl T∈Γ) (tl T′∈Γ) = cong (_↑ 0) (<:∈-func T∈Γ T′∈Γ)

  Ftyp-measure : Ftyp  
  Ftyp-measure            = 1
  Ftyp-measure (var x)     = 1
  Ftyp-measure (Π<: S  U) = Ftyp-measure S + Ftyp-measure U

  Ftyp-measure≥1 :  T  Ftyp-measure T  1
  Ftyp-measure≥1            = s≤s z≤n
  Ftyp-measure≥1 (var _)     = s≤s z≤n
  Ftyp-measure≥1 (Π<: S  U) = ≤-trans (Ftyp-measure≥1 S) (≤-stepsʳ _ ≤-refl)

  ↑-idx :     
  ↑-idx x n with n ≤? x
  ... | yes p = suc x
  ... | no ¬p = x
  
  ↑-var :  x n  (var x)  n  var (↑-idx x n)
  ↑-var x n with n ≤? x
  ... | yes p = refl
  ... | no ¬p = refl
  
  Ftyp-measure-↑ :  T m  Ftyp-measure (T  m)  Ftyp-measure T
  Ftyp-measure-↑  m                 = refl
  Ftyp-measure-↑ (var x) m
    rewrite ↑-var x m                = refl
  Ftyp-measure-↑ (Π<: S  U) m
    rewrite Ftyp-measure-↑ S m
          | Ftyp-measure-↑ U (suc m) = refl

  F-measure :  {Γ S U}  Γ ⊢F S <: U  
  F-measure ftop         = 1
  F-measure fvrefl       = 1
  F-measure (fbinds _ D) = 1 + F-measure D
  F-measure (fall D₁ D₂) = 1 + F-measure D₁ + F-measure D₂

  F-deriv = Σ (Env × Ftyp × Ftyp) λ { (Γ , S , U)  Γ ⊢F S <: U }

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

  typ₁ : F-deriv  Ftyp
  typ₁ ((_ , S , U) , _) = S

  typ₂ : F-deriv  Ftyp
  typ₂ ((_ , S , U) , _) = U

  F-measure-packed : F-deriv  
  F-measure-packed (_ , D) = F-measure D

  ≡⊤-dec : UDecidable (_≡ )
  ≡⊤-dec            = yes refl
  ≡⊤-dec (var _)     = no  ())
  ≡⊤-dec (Π<: _  _) = no  ())
  
  Π<:-injective :  {S U S′ U′}  Π<: S  U  Π<: S′  U′  S  S′ × U  U′
  Π<:-injective refl = refl , refl
  
  Ftyp-≡-dec : BDecidable (_≡_ {A = Ftyp})
  Ftyp-≡-dec                    = yes refl
  Ftyp-≡-dec  (var _)            = no  ())
  Ftyp-≡-dec  (Π<: _  _)        = no  ())
  Ftyp-≡-dec (var _)             = no  ())
  Ftyp-≡-dec (var x) (var y) with x  y
  ... | yes x≡y                   = yes (cong var_ x≡y)
  ... | no x≢y                    = no  { refl  x≢y refl })
  Ftyp-≡-dec (var _) (Π<: _  _)  = no  ())
  Ftyp-≡-dec (Π<: _  _)         = no  ())
  Ftyp-≡-dec (Π<: _  _) (var _)  = no  ())
  Ftyp-≡-dec (Π<: S  U) (Π<: S′  U′)
    with Ftyp-≡-dec S S′ | Ftyp-≡-dec U U′
  ... | yes S≡S′ | yes U≡U′       = yes (cong₂ Π<:_∙_ S≡S′ U≡U′)
  ... | yes S≡S′ | no U≢U′        = no λ Π<:≡  U≢U′ (proj₂ (Π<:-injective Π<:≡))
  ... | no S≢S′  | yes U≡U′       = no  Π<:≡  S≢S′ (proj₁ (Π<:-injective Π<:≡)))
  ... | no S≢S′  | no U≢U′        = no  Π<:≡  S≢S′ (proj₁ (Π<:-injective Π<:≡)))

-- Definition of F<:
--
-- Here we also define full F<:. We do not use it anywhere.
module Full where

  infix 6 var_
  infixr 6 _⇒_ Π<:_∙_
  
  -- Types
  --
  -- * ⊤ is a supertype of all types.
  -- * (var n) represent a type variable.
  -- * (S ⇒ U) represents a function type (S ⟶ U).
  -- * (Π<: S ∙ U) represents a universal type (∀ X <: S . U).
  data Ftyp : Set where
           : Ftyp
    var_    :   Ftyp
    _⇒_     : Ftyp  Ftyp  Ftyp
    Π<:_∙_  : Ftyp  Ftyp  Ftyp
  
  Env : Set
  Env = List Ftyp

  infixl 7 _↑_
  _↑_ : Ftyp    Ftyp
    n           = 
  (var x)  n with n ≤? x
  ... | yes p     = var (suc x)
  ... | no ¬p     = var x
  (S  U)  n     = S  n  U  n
  (Π<: S  U)  n = Π<: S  n  U  suc n

  infix 4 _<:_∈_
  data _<:_∈_ :   Ftyp  Env  Set where
    hd :  {T Γ}  0 <: T  0  T  Γ
    tl :  {n T T′ Γ}  n <: T  Γ  suc n <: T  0  T′  Γ

  infix 4 _⊢F_<:_
  
  data _⊢F_<:_ : Env  Ftyp  Ftyp  Set where
    ftop   :  {Γ T}  Γ ⊢F T <: 
    fvrefl :  {Γ n}  Γ ⊢F var n <: var n
    fbinds :  {Γ n T U} 
               n <: T  Γ 
               Γ ⊢F T <: U 
               Γ ⊢F var n <: U
    ffun   :  {Γ S₁ S₂ U₁ U₂} 
               Γ ⊢F S₂ <: S₁ 
               Γ ⊢F U₁ <: U₂ 
               Γ ⊢F S₁  U₁ <: S₂  U₂
    fall   :  {Γ S₁ S₂ U₁ U₂} 
               Γ ⊢F S₂ <: S₁ 
               S₂  Γ ⊢F U₁ <: U₂ 
               Γ ⊢F Π<: S₁  U₁ <: Π<: S₂  U₂
  
  env-lookup : Env    Maybe Ftyp
  env-lookup [] n = nothing
  env-lookup (T  Γ) zero = just $ T  0
  env-lookup (T  Γ) (suc n) = Maybe.map (_↑ 0) $ env-lookup Γ n

  lookup⇒<:∈ :  {Γ n T}  env-lookup Γ n  just T  n <: T  Γ
  lookup⇒<:∈ {[]} {n} ()
  lookup⇒<:∈ {T  Γ} {zero} refl = hd
  lookup⇒<:∈ {T  Γ} {suc n} eq with env-lookup Γ n | lookup⇒<:∈ {Γ} {n}
  ... | nothing | rec with eq
  ... | ()
  lookup⇒<:∈ {T  Γ} {suc n} refl | just T′ | rec = tl (rec refl) 

  <:∈⇒lookup :  {Γ n T}  n <: T  Γ  env-lookup Γ n  just T
  <:∈⇒lookup hd            = refl
  <:∈⇒lookup (tl <:∈)
    rewrite <:∈⇒lookup <:∈ = refl

  env-lookup′ : Env    Maybe Ftyp
  env-lookup′ Γ n = Maybe.map (repeat (suc n) (_↑ 0)) (lookupOpt Γ n)

  env-lookup-same :  Γ n  env-lookup Γ n  env-lookup′ Γ n
  env-lookup-same [] n          = refl
  env-lookup-same (T  Γ) zero  = refl
  env-lookup-same (T  Γ) (suc n)
    rewrite env-lookup-same Γ n = sym $ Maybeₚ.map-compose (lookupOpt Γ n)

open FsubMinus

-- Undecidability of F<:
--
-- Here we show a reduction proof from F<:⁻ to F<:. Assuming F<:⁻ subtyping being
-- undecidable (which is about to be shown below), we formalize that F<: subtyping is
-- undecidable.
module FsubUndec where

  -- the interpretation function of types from F<:⁻ to F<:
  infixl 5 _*  
  _* : Ftyp  Full.Ftyp
   *           = Full.⊤
  var x *       = Full.var x
  Π<: T₁  T₂ * = Full.Π<: T₁ *  (T₂ *)

  *-↑-comm :  (T : Ftyp) n  (T *) Full.↑ n  (T  n) *
  *-↑-comm  n = refl
  *-↑-comm (var x) n with n ≤? x
  ... | yes p  = refl
  ... | no ¬p  = refl
  *-↑-comm (Π<: S  U) n rewrite *-↑-comm S n | *-↑-comm U (suc n)
               = refl

  repeat*-↑-comm :  T n m  repeat n (Full._↑ m) (T *)  (repeat n (_↑ m) T) *
  repeat*-↑-comm T zero m        = refl
  repeat*-↑-comm T (suc n) m
    rewrite repeat*-↑-comm T n m = *-↑-comm (repeat n (_↑ m) T) m

  *-injective :  {S U}  S *  U *  S  U
  *-injective {} {} refl            = refl
  *-injective {} {var x} ()
  *-injective {} {Π<: _  _} ()
  *-injective {var x} {} ()
  *-injective {var x} {var .x} refl   = refl
  *-injective {var _} {Π<: _  _} ()
  *-injective {Π<: S  S₁} {} ()
  *-injective {Π<: _  _} {var x} ()
  *-injective {Π<: S  S₁} {Π<: U  U₁} eq
    with S * | U * | *-injective {S} {U} | S₁ * | U₁ * | *-injective {S₁} {U₁}
  ... | S* | U* | r | S₁* | U₁* | r₁ with eq
  ... | refl rewrite r refl | r₁ refl = refl

  Full<:∈⇒<:∈ :  {Γ n T}  n Full.<: T *  List.map _* Γ  n <: T  Γ
  Full<:∈⇒<:∈ {Γ} {n} <:∈
    with Full.<:∈⇒lookup <:∈
  ...  | eq rewrite Full.env-lookup-same (List.map _* Γ) n
    with lookupOpt (List.map _* Γ) n | lookupOpt-map-inv {f = _*} Γ {n}
  ...  | just T  | inv
       with inv refl
  ...     | T′ , refl , lk
          with just-injective eq
  ...        | eq′
    rewrite repeat*-↑-comm T′ n 0 | *-↑-comm (repeat n (_↑ 0) T′) 0
    with *-injective eq′
  ...  | refl = lookup⇒<:∈ (trans (env-lookup-same Γ n) aux)
    where aux : env-lookup′ Γ n  just (repeat n (_↑ 0) T′  0)
          aux rewrite lk = refl
  Full<:∈⇒<:∈ {Γ} {n} <:∈ | () | nothing | _ 

  <:∈⇒Full<:∈ :  {Γ n T}  n <: T  Γ  n Full.<: T *  List.map _* Γ
  <:∈⇒Full<:∈ (hd {T})
    rewrite sym $ *-↑-comm T 0 = Full.hd
  <:∈⇒Full<:∈ (tl {T = T} <:∈)
    rewrite sym $ *-↑-comm T 0 = Full.tl (<:∈⇒Full<:∈ <:∈)

  full⇒minus-gen :  {Γ* S* U*}  Γ* Full.⊢F S* <: U* 
                {Γ S U}  Γ*  List.map _* Γ  S*  (S *)  U*  (U *) 
                 Γ ⊢F S <: U
  full⇒minus-gen Full.ftop {Γ} {S} {} eqΓ eqS refl             = ftop
  full⇒minus-gen Full.ftop {Γ} {S} {var x} eqΓ eqS ()
  full⇒minus-gen Full.ftop {Γ} {S} {Π<: U  U₁} eqΓ eqS ()
  full⇒minus-gen Full.fvrefl {Γ} {} {} eqΓ () eqU
  full⇒minus-gen Full.fvrefl {Γ} {var x} {} eqΓ refl ()
  full⇒minus-gen Full.fvrefl {Γ} {Π<: S  S₁} {} eqΓ () eqU
  full⇒minus-gen Full.fvrefl {Γ} {} {var x} eqΓ () eqU
  full⇒minus-gen Full.fvrefl {Γ} {var x} {var .x} eqΓ refl refl = fvrefl
  full⇒minus-gen Full.fvrefl {Γ} {Π<: S  S₁} {var x} eqΓ () eqU
  full⇒minus-gen Full.fvrefl {Γ} {} {Π<: U  U₁} eqΓ () eqU
  full⇒minus-gen Full.fvrefl {Γ} {var x} {Π<: U  U₁} eqΓ refl ()
  full⇒minus-gen Full.fvrefl {Γ} {Π<: S  S₁} {Π<: U  U₁} eqΓ () eqU
  full⇒minus-gen (Full.fbinds T∈Γ* Dfull) {Γ} {} {U} eqΓ () eqU
  full⇒minus-gen (Full.fbinds {n = n} T∈Γ* Dfull)
             {Γ} {var x} {U} eqΓ refl eqU       = aux
    where T′∈Γ :  {Γ* T}  n Full.<: T  Γ*  Γ*  List.map _* Γ  ∃[ T′ ] (T  T′ *)
          T′∈Γ <:∈ refl with Full.<:∈⇒lookup <:∈
          ... | eq rewrite Full.env-lookup-same (List.map _* Γ) n
            with lookupOpt (List.map _* Γ) n | inspect (lookupOpt (List.map _* Γ)) n
          ... | just T | [ lk ]
            with lookupOpt-map-inv Γ lk | just-injective eq
          ... | T′ , refl , lkeq | eq′
            rewrite repeat*-↑-comm T′ n 0
              | *-↑-comm (repeat n (_↑ 0) T′) 0 = -, sym eq′
          T′∈Γ <:∈ refl | () | nothing | _
          aux : Γ ⊢F var n <: U
          aux with T′∈Γ T∈Γ* eqΓ
          ... | T′ , refl rewrite eqΓ           = fbinds (Full<:∈⇒<:∈ T∈Γ*) (full⇒minus-gen Dfull refl refl eqU)
  full⇒minus-gen (Full.fbinds T∈Γ* Dfull) {Γ} {Π<: S  S₁} {U} eqΓ () eqU
  full⇒minus-gen (Full.ffun _ _) {Γ} {} {U} eqΓ () eqU
  full⇒minus-gen (Full.ffun _ _) {Γ} {var x₂} {U} eqΓ () eqU
  full⇒minus-gen (Full.ffun _ _) {Γ} {Π<: S  S₁} {U} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {} {} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {} {var x} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {} {Π<: U  U₁} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {var x} {} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {var x} {var x₁} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {var x} {Π<: U  U₁} eqΓ () eqU
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {Π<: S  S₁} {} eqΓ refl ()
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {Π<: S  S₁} {var x} eqΓ refl ()
  full⇒minus-gen (Full.fall Dp Dbody) {Γ} {Π<: S₁  U₁} {Π<: S₂  U₂}
                 eqΓ refl refl = fall (full⇒minus-gen Dp eqΓ refl refl)
                                      (full⇒minus-gen Dbody (aux eqΓ) refl refl)
    where aux :  {Γ*}  Γ*  List.map _* Γ  (S₂ *)  Γ*  (S₂ *)  List.map _* Γ
          aux refl             = refl

  -- The following two functions accomplish the reduction proof.
  full⇒minus :  {Γ S U}  List.map _* Γ Full.⊢F S * <: U *  Γ ⊢F S <: U
  full⇒minus Dfull = full⇒minus-gen Dfull refl refl refl
  
  minus⇒full :  {Γ S U}  Γ ⊢F S <: U  List.map _* Γ Full.⊢F S * <: U *
  minus⇒full ftop                    = Full.ftop
  minus⇒full fvrefl                  = Full.fvrefl
  minus⇒full {Γ} (fbinds T∈Γ Dminus) = Full.fbinds (<:∈⇒Full<:∈ T∈Γ) (minus⇒full Dminus)
  minus⇒full (fall Dp Db)            = Full.fall (minus⇒full Dp) (minus⇒full Db)

-- Definition of F<:ᵈ
--
-- Definition of F<:ᵈ F<:ᵈ (F<: deterministic) is an intermediate construction in
-- Pierce92 towards the undecidability proof of F<: subtyping. This intermediate
-- construction is also shown undecidable and we directly use that result here.
module DeterministicFsubSized where

  open import Induction.Nat
  import Relation.Binary.EqReasoning as Eq

  infix 6 var⁻_
  infixr 6 Π⁺<:_∙¬_ Π⁻<:⊤∙¬_

  -- Types
  --
  -- Types in F<:ᵈ are divided into two parts and the two parts are mutually defined.
  -- The two parts are positive types [Ftyp⁺] and negative types [Ftyp⁻] and defined
  -- as follows.
  mutual
  -- Positive types
  -- * ⊤⁺ corresponds to ⊤ in F<:⁻.
  -- * (Π⁺<: Ts ∙¬ U) represents a universal type where all types involved are negative types (∀ X₁ <: T₁ ... ∀ Xₙ <: Tₙ . ∀ X <: U . X).
    data Ftyp⁺ (n : ) : Set where
      ⊤⁺       : Ftyp⁺ n
      Π⁺<:_∙¬_ : Vec (Ftyp⁻ n) n  Ftyp⁻ n  Ftyp⁺ n
  
  -- Negative types
  -- * (var⁻ n) corresponds to a type variable in F<:⁻.
  -- * (Π⁻<:⊤∙¬_ T) represents a universal type where all parameter types are ⊤ and T is positive (∀ X₁ <: ⊤ ... ∀ Xₙ <: ⊤ . ∀ X <: T . X).
    data Ftyp⁻ (n : ) : Set where
      var⁻_    :   Ftyp⁻ n
      Π⁻<:⊤∙¬_ : Ftyp⁺ n  Ftyp⁻ n

  -- shifting operation for both positive and negative types
  infixl 7 _↑⁺_ _↑⁺_ ⟦_↑⁻_⟧
  mutual
    _↑⁺_ :  {n}  Ftyp⁺ n    Ftyp⁺ n
    ⊤⁺ ↑⁺ n                  = ⊤⁺
    _↑⁺_ {m} (Π⁺<: S ∙¬ U) n = Π⁺<:  S ↑⁻ n  ∙¬ (U ↑⁻ (m + n))

    _↑⁻_ :  {n}  Ftyp⁻ n    Ftyp⁻ n
    (var⁻ x) ↑⁻ n with n ≤? x
    ... | yes p            = var⁻ (suc x)
    ... | no ¬p            = var⁻ x
    _↑⁻_ {m} (Π⁻<:⊤∙¬ T) n = Π⁻<:⊤∙¬ (T ↑⁺ (m + n))

    ⟦_↑⁻_⟧ :  {n m}  Vec (Ftyp⁻ n) m    Vec (Ftyp⁻ n) m
     nil ↑⁻ n    = nil
     T  S ↑⁻ n  = T ↑⁻ n   S ↑⁻ suc n  

  -- In F<:ᵈ, typing contexts consist only of negative types.
  NEnv :   Set
  NEnv n = List (Ftyp⁻ n)

  nenv-lookup :  {n}  NEnv n    Maybe (Ftyp⁻ n)
  nenv-lookup Γ n = Maybe.map (repeat (suc n) (_↑⁻ 0)) (lookupOpt Γ n)

  -- subtyping judgment of F<:ᵈ
  --
  -- For a fixed n, a subtyping judgment of F<:ᵈ is a ternary predicate, among a
  -- typing context (consisting of negative types), a negative type and a positive
  -- type.  A subtyping judgment in F<:ᵈ asserts that a negative type is a subtype of
  -- a positive type in the given context. Notice that there is no subtyping relation
  -- between two negative types or two positive types.
  infix 4 [_]_⊢FD_<:_  
  data [_]_⊢FD_<:_ (n : ) : NEnv n  Ftyp⁻ n  Ftyp⁺ n  Set where
    fdtop :  {Γ T}  [ n ] Γ ⊢FD T <: ⊤⁺
    fdvar :  {Γ m T S U} 
              nenv-lookup Γ m  just T 
              [ n ] Γ ⊢FD T <: Π⁺<: S ∙¬ U  
              [ n ] Γ ⊢FD var⁻ m <: Π⁺<: S ∙¬ U
    fdall :  {Γ T S U} 
              [ n ] Vec.foldl  _  _) (flip _∷_) Γ S ⊢FD U <: T 
              [ n ] Γ ⊢FD Π⁻<:⊤∙¬ T <: Π⁺<: S ∙¬ U
  
  infix 5 _*⁺ _*⁻
  infix 6 Π⁻[_]∙_ Π⁺_∙_

  -- the interpretation function of types from F<:ᵈ to F<:⁻
  mutual
    _*⁺ :  {n}  Ftyp⁺ n  Ftyp
    ⊤⁺ *⁺         = 
    Π⁺<: S ∙¬ U *⁺ = Π⁻[ S ]∙ U

    Π⁻[_]∙_ :  {n m}  Vec (Ftyp⁻ n) m  Ftyp⁻ n  Ftyp
    Π⁻[ nil ]∙ U    = Π<: U *⁻  var zero
    Π⁻[ S  Ss ]∙ U = Π<: S *⁻  Π⁻[ Ss ]∙ U
  
    _*⁻ :  {n}  Ftyp⁻ n  Ftyp
    var⁻ n *⁻          = var n
    _*⁻ {n} (Π⁻<:⊤∙¬ T) = Π⁺ n  T
    
    Π⁺_∙_ :  {n}    Ftyp⁺ n  Ftyp
    Π⁺ zero  T  = Π<: T *⁺  var zero
    Π⁺ suc n  T = Π<:   Π⁺ n  T

  module _ where
    open Eq (setoid )
    
    mutual
      *-↑-comm⁺ :  {m} (T : Ftyp⁺ m) n  (T *⁺)  n  (T ↑⁺ n) *⁺
      *-↑-comm⁺ ⊤⁺ n            = refl
      *-↑-comm⁺ (Π⁺<: S ∙¬ U) n = Π-↑-comm⁻ S U n
  
      Π-↑-comm⁻ :  {i j} (S : Vec (Ftyp⁻ i) j) (U : Ftyp⁻ i) n 
                    (Π⁻[ S ]∙ U)  n  Π⁻[  S ↑⁻ n  ]∙ (U ↑⁻ (j + n))
      Π-↑-comm⁻ nil U n
        rewrite *-↑-comm⁻ U n = refl
      Π-↑-comm⁻ {j = suc j} (T  S) U n
        rewrite *-↑-comm⁻ T n
              | Π-↑-comm⁻ S U (suc n)
              | +-suc j n     = refl
  
      *-↑-comm⁻ :  {m} (T : Ftyp⁻ m) n  (T *⁻)  n  (T ↑⁻ n) *⁻
      *-↑-comm⁻ (var⁻ x) n with n ≤? x
      ... | yes p             = refl
      ... | no ¬p             = refl
      *-↑-comm⁻ (Π⁻<:⊤∙¬ T) n = Π-↑-comm⁺ _ T n

      Π-↑-comm⁺ :  {i} j (T : Ftyp⁺ i) n 
                    (Π⁺ j  T)  n  Π⁺ j  (T ↑⁺ (j + n))
      Π-↑-comm⁺ zero T n
        rewrite *-↑-comm⁺ T n = refl
      Π-↑-comm⁺ (suc j) T n
        rewrite Π-↑-comm⁺ j T (suc n)
              | +-suc j n     = refl

  repeat*-↑-comm⁻ :  {i} (T : Ftyp⁻ i) n m 
                      repeat n (_↑ m) (T *⁻)  (repeat n (_↑⁻ m) T) *⁻
  repeat*-↑-comm⁻ T zero m        = refl
  repeat*-↑-comm⁻ T (suc n) m 
    rewrite repeat*-↑-comm⁻ T n m = *-↑-comm⁻ (repeat n (_↑⁻ m) T) m

  mutual
    *-injective⁺ :  {n} {S U : Ftyp⁺ n}  S *⁺  U *⁺  S  U
    *-injective⁺ {n} {⊤⁺} {⊤⁺} eq = refl
    *-injective⁺ {.0} {⊤⁺} {Π⁺<: nil ∙¬ _} ()
    *-injective⁺ {.(suc _)} {⊤⁺} {Π⁺<: _  _ ∙¬ _} ()
    *-injective⁺ {n} {Π⁺<: S ∙¬ U} {⊤⁺} eq = ⊥-elim $ aux eq
      where aux :  {n m} {S : Vec (Ftyp⁻ n) m} {U}  ¬ Π⁻[ S ]∙ U  
            aux {S = nil} ()
            aux {S = _  S} ()
    *-injective⁺ {n} {Π⁺<: S₁ ∙¬ U₁} {Π⁺<: S₂ ∙¬ U₂} eq with Π-injective⁻ eq
    ... | refl , refl = refl

    Π-injective⁻ :  {n m} {S₁ S₂ : Vec (Ftyp⁻ n) m} {U₁ U₂ : Ftyp⁻ n} 
                     Π⁻[ S₁ ]∙ U₁  Π⁻[ S₂ ]∙ U₂ 
                     S₁  S₂ × U₁  U₂
    Π-injective⁻ {S₁ = nil} {nil} {U₁} {U₂} eq
      with U₁ *⁻ | U₂ *⁻ | *-injective⁻ {S = U₁} {U₂}
    ...  | U₁* | U₂* | rec with eq
    ...  | refl = refl , rec refl
    Π-injective⁻ {S₁ = S  S₁} {S′  S₂} {U₁} {U₂} eq
      with S *⁻ | S′ *⁻ | Π⁻[ S₁ ]∙ U₁ | Π⁻[ S₂ ]∙ U₂
         | *-injective⁻ {S = S} {S′}
         | Π-injective⁻ {S₁ = S₁} {S₂} {U₁} {U₂}
    ...  | S* | S′* | Π₁ | Π₂ | recS | rec with eq
    ...  | refl with recS refl | rec refl
    ...  | refl | refl , eqU = refl , eqU

    *-injective⁻ :  {n} {S U : Ftyp⁻ n}  S *⁻  U *⁻  S  U
    *-injective⁻ {n} {var⁻ x} {var⁻ .x} refl = refl
    *-injective⁻ {zero} {var⁻ _} {Π⁻<:⊤∙¬ _} ()
    *-injective⁻ {suc n} {var⁻ _} {Π⁻<:⊤∙¬ _} ()
    *-injective⁻ {n} {Π⁻<:⊤∙¬ T} {var⁻ x} eq = ⊥-elim $ aux _ eq
      where aux :  {n x} {T : Ftyp⁺ n} m  ¬ Π⁺ m  T  var x
            aux zero ()
            aux (suc m) ()
    *-injective⁻ {n} {Π⁻<:⊤∙¬ T₁} {Π⁻<:⊤∙¬ T₂} eq with Π-injective⁺ n eq
    ... | refl = refl

    Π-injective⁺ :  {n} {T₁ T₂ : Ftyp⁺ n} m 
                     Π⁺ m  T₁  Π⁺ m  T₂ 
                     T₁  T₂
    Π-injective⁺ {T₁ = T₁} {T₂} zero eq
      with T₁ *⁺ | T₂ *⁺ | *-injective⁺ {S = T₁} {T₂}
    ...  | T₁* | T₂* | rec with eq
    ...  | refl = rec refl
    Π-injective⁺ {T₁ = T₁} {T₂} (suc m) eq
      with Π⁺ m  T₁ | Π⁺ m  T₂ | Π-injective⁺ {T₁ = T₁} {T₂} m
    ...  | Π₁ | Π₂ | rec with eq
    ...  | refl = rec refl

  data Pi (P Q : Ftyp  Set) :   Ftyp  Set where
    pzero :  {U}  Q U  Pi P Q zero (Π<: U  var zero)
    psuc  :  {n S T}  P S  Pi P Q n T -> Pi P Q (suc n) (Π<: S  T)

  -- two predicates that capture the images of the interpretation functions
  mutual
    -- Covar captures the image of the interpretation function from positive types to F<:.
    data Covar (n : ) : Ftyp  Set where
      cvtop : Covar n 
      cvΠ   :  {T}  Pi (Contra n) (Contra n) n T  Covar n T

    -- Contra captures the image of the interpretation function from negative types to F<:.
    data Contra (n : ) : Ftyp  Set where
      cnvar :  m  Contra n (var m)
      cnΠ   :  {T}  Pi ( ≡_) (Covar n) n T  Contra n T
  
  infix 5 ⟦_⟧⁺ ⟦_⟧⁻

  -- the inverse functions of the interpretation functions
  mutual
    ⟦_⟧⁺ :  {n T}  Covar n T  Ftyp⁺ n
     cvtop ⟧⁺ = ⊤⁺
     cvΠ P ⟧⁺ = Π⁺<:  P ∥⁺₁ ∙¬  P ∥⁺₂

    ∥_∥⁺₁ :  {n m T}  Pi (Contra n) (Contra n) m T  Vec (Ftyp⁻ n) m
     pzero U ∥⁺₁  = nil
     psuc S P ∥⁺₁ =  S ⟧⁻   P ∥⁺₁

    ∥_∥⁺₂ :  {n m T}  Pi (Contra n) (Contra n) m T  Ftyp⁻ n
     pzero U ∥⁺₂  =  U ⟧⁻
     psuc S P ∥⁺₂ =  P ∥⁺₂
  
    ⟦_⟧⁻ :  {n T}  Contra n T  Ftyp⁻ n
     cnvar m ⟧⁻ = var⁻ m
     cnΠ P ⟧⁻   = Π⁻<:⊤∙¬  P ∥⁻
    
    ∥_∥⁻ :  {n m T}  Pi ( ≡_) (Covar n) m T  Ftyp⁺ n
     pzero T ∥⁻  =  T ⟧⁺
     psuc S P ∥⁻ =  P ∥⁻

  mutual
    ⟦⟧⁺*⁺⇒id :  {n T} (cT : Covar n T)  ( cT ⟧⁺ *⁺)  T
    ⟦⟧⁺*⁺⇒id cvtop   = refl
    ⟦⟧⁺*⁺⇒id (cvΠ P) = ∥∥⁻Π⇒id P

    ∥∥⁻Π⇒id :  {n m T} (P : Pi (Contra n) (Contra n) m T) 
                Π⁻[  P ∥⁺₁ ]∙  P ∥⁺₂  T
    ∥∥⁻Π⇒id (pzero U)
      rewrite ⟦⟧⁻*⁻⇒id U             = refl
    ∥∥⁻Π⇒id (psuc S P)
      rewrite ⟦⟧⁻*⁻⇒id S | ∥∥⁻Π⇒id P = refl

    ⟦⟧⁻*⁻⇒id :  {n T} (cT : Contra n T)  ( cT ⟧⁻ *⁻)  T
    ⟦⟧⁻*⁻⇒id (cnvar m) = refl
    ⟦⟧⁻*⁻⇒id (cnΠ P)   = ∥∥⁺Π⇒id P

    ∥∥⁺Π⇒id :  {n m T} (P : Pi ( ≡_) (Covar n) m T)  Π⁺ m   P ∥⁻  T
    ∥∥⁺Π⇒id (pzero U) rewrite ⟦⟧⁺*⁺⇒id U    = refl
    ∥∥⁺Π⇒id (psuc refl P) rewrite ∥∥⁺Π⇒id P = refl
  
  infix 5 *⟦_⟧⁺ *⟦_⟧⁻ *∥_,_∥⁺
  
  mutual
  
    *⟦_⟧⁺ :  {n} (T : Ftyp⁺ n)  Covar n (T *⁺)
    *⟦ ⊤⁺ ⟧⁺          = cvtop
    *⟦ Π⁺<: S ∙¬ U ⟧⁺ = cvΠ *∥ S , U ∥⁺

    *∥_,_∥⁺ :  {n m} (S : Vec (Ftyp⁻ n) m) (U : Ftyp⁻ n) 
              Pi (Contra n) (Contra n) m (Π⁻[ S ]∙ U)
    *∥ nil , U ∥⁺    = pzero *⟦ U ⟧⁻
    *∥ S  Ss , U ∥⁺ = psuc *⟦ S ⟧⁻ *∥ Ss , U ∥⁺
  
    *⟦_⟧⁻ :  {n} (T : Ftyp⁻ n)  Contra n (T *⁻)
    *⟦ var⁻ x ⟧⁻    = cnvar x
    *⟦ Π⁻<:⊤∙¬ T ⟧⁻ = cnΠ *∥ _ , T ∥⁻

    *∥_,_∥⁻ :  {n} m (T : Ftyp⁺ n)  Pi ( ≡_) (Covar n) m (Π⁺ m  T)
    *∥ zero , T ∥⁻  = pzero *⟦ T ⟧⁺
    *∥ suc m , T ∥⁻ = psuc refl *∥ m , T ∥⁻
  
  mutual
    *⁺⟦⟧⁺⇒id :  {n} (T : Ftyp⁺ n)   *⟦ T ⟧⁺ ⟧⁺  T
    *⁺⟦⟧⁺⇒id ⊤⁺                           = refl
    *⁺⟦⟧⁺⇒id (Π⁺<: S ∙¬ U)
      rewrite *∥∥⁺₁⇒id S U | *∥∥⁺₂⇒id S U = refl

    *∥∥⁺₁⇒id :  {n m} (S : Vec (Ftyp⁻ n) m) (U : Ftyp⁻ n)   *∥ S , U ∥⁺ ∥⁺₁  S
    *∥∥⁺₁⇒id nil U                       = refl
    *∥∥⁺₁⇒id (S  Ss) U
      rewrite *⁻⟦⟧⁻⇒id S | *∥∥⁺₁⇒id Ss U = refl

    *∥∥⁺₂⇒id :  {n m} (S : Vec (Ftyp⁻ n) m) (U : Ftyp⁻ n)   *∥ S , U ∥⁺ ∥⁺₂  U
    *∥∥⁺₂⇒id nil U      = *⁻⟦⟧⁻⇒id U
    *∥∥⁺₂⇒id (S  Ss) U = *∥∥⁺₂⇒id Ss U

    *⁻⟦⟧⁻⇒id :  {n} (T : Ftyp⁻ n)   *⟦ T ⟧⁻ ⟧⁻  T
    *⁻⟦⟧⁻⇒id (var⁻ x)     = refl
    *⁻⟦⟧⁻⇒id {n} (Π⁻<:⊤∙¬ T)
      rewrite *∥∥⁻⇒id n T = refl

    *∥∥⁻⇒id :  {n} m (T : Ftyp⁺ n)   *∥ m , T ∥⁻ ∥⁻  T
    *∥∥⁻⇒id zero T    = *⁺⟦⟧⁺⇒id T
    *∥∥⁻⇒id (suc m) T = *∥∥⁻⇒id m T

  open Measure <-wellFounded F-measure-packed renaming (wfRec to F-rec)

  minus⇒deterministic-gen :  (D : F-deriv) 
                             n {Γ} (S : Contra n (typ₁ D)) (U : Covar n (typ₂ D)) 
                              env D  List.map _*⁻ Γ  
                              [ n ] Γ ⊢FD  S ⟧⁻ <:  U ⟧⁺
  minus⇒deterministic-gen D = F-rec  D  prop D) pf D
    where prop : F-deriv  Set
          prop D =  n {Γ} (S : Contra n (typ₁ D)) (U : Covar n (typ₂ D)) 
                     env D  List.map _*⁻ Γ  
                     [ n ] Γ ⊢FD  S ⟧⁻ <:  U ⟧⁺
          open ≤-Reasoning

          pf :  (D : F-deriv) 
                 (∀ D′  F-measure-packed D′ < F-measure-packed D  prop D′)  prop D
          pf ((Γ , S* , .) , ftop) rec n S cvtop eq                       = fdtop
          pf ((Γ , S* , .) , ftop) rec n S (cvΠ ()) eq
          pf ((Γ , .(var _) , .(var _)) , fvrefl) rec n S (cvΠ ()) eq
          pf ((Γ , .(var m) , .) , fbinds T∈Γ D) rec n (cnvar m) cvtop eq = fdtop
          
          pf ((Γ , .(var m) , U*) , fbinds T∈Γ D) rec n {Γ′} (cnvar m) (cvΠ P) refl
            with trans (sym $ env-lookup-same (List.map _*⁻ Γ′) m) $ <:∈⇒lookup T∈Γ
          ... | eq with lookupOpt (List.map _*⁻ Γ′) m | inspect (lookupOpt (List.map _*⁻ Γ′)) m
          pf ((Γ , .(var m) , U*) , fbinds T∈Γ D) rec n {Γ′} (cnvar m) (cvΠ P) refl | refl | just T | [ lk ]
            with lookupOpt-map-inv Γ′ lk
          ...  | T′ , refl , lk′
            rewrite repeat*-↑-comm⁻ T′ m 0
               | *-↑-comm⁻ (repeat m (_↑⁻ 0) T′) 0
            with rec (-, D) ≤-refl n *⟦ repeat m (_↑⁻ 0) T′ ↑⁻ 0 ⟧⁻ (cvΠ P) refl
          ...  | Drec rewrite *⁻⟦⟧⁻⇒id (repeat m (_↑⁻ 0) T′ ↑⁻ 0)
            = fdvar (cong (Maybe.map (repeat (suc m) (_↑⁻ 0))) lk′) Drec
          pf ((Γ , .(var m) , U*) , fbinds T∈Γ D) rec n {Γ′} (cnvar m) (cvΠ P) refl | () | nothing | [ lk ]

          pf ((Γ , .(var _) , U*) , fbinds T∈Γ D) rec n (cnΠ ()) U eq
          pf ((Γ , .(Π<: _  _) , .(Π<: _  _)) , Dtop@(fall S₂<:S₁ U₁<:U₂))
             rec n (cnΠ Ps) (cvΠ Pu) refl                                  = fdall (aux _ n (fall S₂<:S₁ U₁<:U₂) Ps Pu ≤-refl)
            where transport :  {Γ T₁ T₂ S*} (S : Contra n S*)
                                (D : S*  Γ ⊢F T₁ <: T₂) 
                                Σ (( S ⟧⁻ *⁻)  Γ ⊢F T₁ <: T₂)  D′  F-measure D′  F-measure D)
                  transport {Γ} {T₁} {T₂} S D
                    = subst  S  Σ (S  Γ ⊢F T₁ <: T₂)  D′  F-measure D′  F-measure D))
                            (sym $ ⟦⟧⁻*⁻⇒id S) (D , refl)

                  aux :  {T₁ T₂} Γ m (D : List.map _*⁻ Γ ⊢F T₁ <: T₂)
                          (Ps : Pi (_≡_ ) (Covar n) m T₁)
                          (Pu : Pi (Contra n) (Contra n) m T₂) 
                          F-measure D  F-measure Dtop 
                          [ n ] Vec.foldl  _  _) (flip _∷_) Γ  Pu ∥⁺₁ ⊢FD  Pu ∥⁺₂ <:  Ps ∥⁻
                  aux Γ zero (fall <:₁ <:₂) (pzero S₁) (pzero S₂) 
                    = rec (-, <:₁) (≤-trans shrinks ) n S₂ S₁ refl
                    where shrinks : (F-measure <:₁) < suc (F-measure <:₁ + F-measure <:₂)
                          shrinks = begin-strict
                            F-measure <:₁                       ≤⟨ ≤-stepsʳ (F-measure <:₂) ≤-refl 
                            F-measure <:₁ + F-measure <:₂       <⟨ ≤-refl 
                            suc (F-measure <:₁ + F-measure <:₂) 
                  aux Γ (suc m) (fall <:₁ <:₂) (psuc refl Ps) (psuc S₂ Pu) 
                    with transport S₂ <:₂
                  ... | <:₂′ , eq = aux ( S₂ ⟧⁻  Γ) m <:₂′ Ps Pu $
                    begin
                      F-measure <:₂′                            ≡⟨ eq 
                      F-measure <:₂                             ≤⟨ ≤-stepsˡ (F-measure <:₁) ≤-refl 
                      F-measure <:₁ + F-measure <:₂             <⟨  
                      suc (F-measure S₂<:S₁ + F-measure U₁<:U₂) 

  -- The following two functions conclude the reduction proof from F<:ᵈ to F<:⁻.
  minus⇒deterministic :  n {Γ : NEnv n} {S U}  List.map _*⁻ Γ ⊢F S *⁻ <: U *⁺  [ n ] Γ ⊢FD S <: U
  minus⇒deterministic n {Γ} {S} {U} D with minus⇒deterministic-gen (-, D) n *⟦ S ⟧⁻ *⟦ U ⟧⁺ refl
  ... | D′ rewrite *⁻⟦⟧⁻⇒id S | *⁺⟦⟧⁺⇒id U = D′

  deterministic⇒minus :  n {Γ : NEnv n} {S U}  [ n ] Γ ⊢FD S <: U  List.map _*⁻ Γ ⊢F S *⁻ <: U *⁺
  deterministic⇒minus n fdtop             = ftop
  deterministic⇒minus n {Γ} (fdvar {m = m} T∈Γ D)
    with lookupOpt Γ m | inspect (lookupOpt Γ) m
  deterministic⇒minus n {Γ} (fdvar {m = m} refl D) | just T | [ eq ]
    = fbinds (lookup⇒<:∈ $ begin
                env-lookup (List.map _*⁻ Γ) m       ≡⟨ env-lookup-same (List.map _*⁻ Γ) m 
                env-lookup′ (List.map _*⁻ Γ) m      ≡⟨ cong (Maybe.map (repeat (suc m) (_↑ 0)))
                                                            (lookupOpt-map Γ _*⁻ eq) 
                just (repeat m (_↑ 0) (T *⁻)  0)   ≡⟨ cong (just  (_↑ 0))
                                                            (repeat*-↑-comm⁻ T m 0) 
                just ((repeat m (_↑⁻ 0) T *⁻)  0)  ≡⟨ cong just $ *-↑-comm⁻ (repeat m (_↑⁻ 0) T) 0 
                just ((repeat m (_↑⁻ 0) T ↑⁻ 0) *⁻) )
             (deterministic⇒minus n D)
    where open Eq (setoid (Maybe Ftyp))
  deterministic⇒minus n {Γ} (fdvar {m = m} () D) | nothing | _
  deterministic⇒minus n {Γ} (fdall D)     = aux n _ _ _ D
    where aux :  {n} m {Γ} (S : Vec (Ftyp⁻ n) m) U T 
                  [ n ] Vec.foldl  _  _) (flip _∷_) Γ S ⊢FD U <: T 
                  List.map _*⁻ Γ ⊢F Π⁺ m  T <: Π⁻[ S ]∙ U
          aux .0 nil U T D                = fall (deterministic⇒minus _ D) fvrefl
          aux .(suc _) (x  S) U T D      = fall ftop (aux _ S U T D)