module STLCRef.Semantics where

-- This file contains the definitional interpreter for STLC+Ref
-- described in Section 3 of the paper.

open import Agda.Primitive
open import Data.Unit
open import Data.Nat hiding (_⊔_ ; _^_)
open import Data.Integer hiding (_⊔_)
open import Data.List
open import Data.List.Properties.Extra
open import Data.List.All.Properties.Extra
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.Any
open import Data.List.Relation.Unary.All as All
open import Data.List.Prefix
open import Data.Product
open import Data.Maybe hiding (_>>=_)
open import Function
open import Common.Weakening

------------
-- SYNTAX --
------------

-- These definitions correspond to Section 3.1, except we have
-- included numbers (integers) and integer operations in the language.

data Ty : Set

Ctx = List Ty

data Ty where
  _⇒_  : (a b : Ty) → Ty
  unit : Ty
  int  : Ty
  ref  : Ty -> Ty

data Expr (Γ : List Ty) : Ty → Set where
  unit  : Expr Γ unit
  var   : ∀ {t} → t ∈ Γ → Expr Γ t
  ƛ     : ∀ {a b} → Expr (a ∷ Γ) b → Expr Γ (a ⇒ b)
  _·_   : ∀ {a b} → Expr Γ (a ⇒ b) → Expr Γ a → Expr Γ b
  num   : ℤ → Expr Γ int
  iop   : (ℤ → ℤ → ℤ) → (l r : Expr Γ int) → Expr Γ int
  ifz   : ∀ {t} → Expr Γ int → Expr Γ t → Expr Γ t → Expr Γ t
  ref   : ∀ {t} → Expr Γ t → Expr Γ (ref t)
  !_    : ∀ {t} → Expr Γ (ref t) → Expr Γ t
  _≔_   : ∀ {t} → Expr Γ (ref t) → Expr Γ t → Expr Γ unit

-----------------------
-- STORES AND VALUES --
-----------------------

StoreTy = List Ty

mutual
  data Val : Ty → (Σ : StoreTy) → Set where
    loc    : ∀ {Σ t} → t ∈ Σ → Val (ref t) Σ
    unit   : ∀ {Σ} → Val unit Σ
    ⟨_,_⟩  : ∀ {Σ Γ a b} → Expr (a ∷ Γ) b → Env Γ Σ → Val (a ⇒ b) Σ
    num   : ∀ {Σ} → ℤ → Val int Σ

  Env : (Γ : Ctx)(Σ : StoreTy) → Set
  Env Γ Σ = All (λ t → Val t Σ) Γ

Store : (Σ : StoreTy) → Set
Store Σ = All (λ t → Val t Σ) Σ

-- The `lookup-store` function is defined in terms of the `lookup`
-- function from `Data.List.All` in the Agda Standard Library.
lookup-store : ∀ {Σ t} → t ∈ Σ → Store Σ → Val t Σ
lookup-store x μ = All.lookup μ x

-- The `update-store` function is defined in terms of the update
-- function for the `All` type: `_All[_]≔'_` from the Standard Library
-- extension (contained in the `lib/*` folder of this artifact).
update-store : ∀ {Σ t} → t ∈ Σ → Val t Σ → Store Σ → Store Σ
update-store ptr v μ = μ All.[ ptr ]≔ v

-----------
-- MONAD --
-----------

-- These definitions correspond to Section 3.3.

M : ∀ {i}(Γ : Ctx) → (p : StoreTy → Set i) → (Σ : StoreTy) → Set i
M Γ p Σ = Env Γ Σ → Store Σ → Maybe (∃ λ Σ' → Store Σ' × p Σ' × Σ ⊑ Σ')

mutual
  weaken-val  : ∀ {a}{Σ Σ' : StoreTy} → Σ ⊑ Σ' → Val a Σ → Val a Σ'
  weaken-val ext unit      = unit
  weaken-val ext (loc l)   = loc (∈-⊒ l ext)
  weaken-val ext ⟨ e , E ⟩ = ⟨ e , weaken-env ext E ⟩
  weaken-val ext (num z)   = num z

  weaken-env  : ∀ {Γ}{Σ Σ' : StoreTy} → Σ ⊑ Σ' → Env Γ Σ → Env Γ Σ'
  weaken-env ext (v ∷ vs)  = weaken-val ext v ∷ weaken-env ext vs
  weaken-env ext []        = []

-- The definition below asserts that values can be weakened, by adding
-- an instance argument of `Weakenable (Val t)`.
--
-- Here, `Weakenable` is defined as in the paper Section 3.4; see
-- `Common.Weakening` in this artifact.

instance
  weaken-val' : ∀ {t} → Weakenable (Val t)
  weaken-val' = record { wk = weaken-val }

return   :    ∀ {Σ Γ}{p : List Ty → Set} → p Σ → M Γ p Σ
return x E μ = just (_ , μ , x , ⊑-refl)

_>>=_ : ∀ {Σ Γ}{p q : StoreTy → Set} →
        (f : M Γ p Σ) → (g : ∀ {Σ'} → p Σ' → M Γ q Σ') → M Γ q Σ
(f >>= c) E μ = case (f E μ) of λ{
  nothing → nothing ;
  (just (_ , μ' , x , ext)) → case (c x (weaken-env ext E) μ') of λ{
    nothing → nothing ;
    (just (_ , μ'' , y , ext')) → just (_ , μ'' , y , ext ⊚ ext')
  }}

getEnv   :    ∀ {Σ Γ} → M Γ (Env Γ) Σ
getEnv E = return E E

usingEnv   :    ∀ {Σ Γ Γ'}{p : List Ty → Set} → Env Γ Σ → M Γ p Σ → M Γ' p Σ
usingEnv E f  _ = f E

timeout  :    ∀ {Σ Γ}{p : List Ty → Set} → M Γ p Σ
timeout _ _ = nothing

store    :    ∀ {Σ t Γ} → Val t Σ → M Γ (Val (ref t)) Σ
store {Σ} {t} v _ μ
  = let ext = ∷ʳ-⊒ t Σ
        v'  = loc (∈-∷ʳ Σ t)
        μ'  = (All.map (weaken-val ext) μ) all-∷ʳ (weaken-val ext v)
    in just (_ , μ' , v' , ext)

deref    :    ∀ {Σ Γ t} → t ∈ Σ → M Γ (Val t) Σ
deref x E μ = return (All.lookup μ x) E μ

update   :    ∀ {Σ Γ t} → t ∈ Σ → Val t Σ → M Γ (λ _ → ⊤) Σ
update x v E μ = return tt E (update-store x v μ)

weaken : ∀ {i}{p : List Ty → Set i}⦃ w : Weakenable p ⦄ → ∀ {Σ Σ'} → Σ ⊑ Σ' → p Σ → p Σ'
weaken ⦃ w ⦄ ext v = Weakenable.wk w ext v

--------------------------------
-- STRONG MONADIC INTERPRETER --
--------------------------------

-- These definitions correspond to Section 3.4.
--
-- The definition of `_^_` below is defined in terms of the `_⊗_`
-- type, which is defined in `Common.Weakening` in this artifact.

_^_  : ∀ {Σ Γ}{p q : StoreTy → Set} → ⦃ w : Weakenable q ⦄ → M Γ p Σ → q Σ → M Γ (p ⊗ q) Σ
(f ^ x) E μ = case (f E μ) of λ {
  nothing → nothing ;
  (just (Σ , μ' , y , ext)) → just (Σ , μ' , (y , weaken ext x) , ext) }

eval : ℕ → ∀ {Σ Γ t} → Expr Γ t → M Γ (Val t) Σ
eval zero     _           =
  timeout
eval (suc k)  unit        =
  return unit
eval (suc k)  (var x)     =
  getEnv >>= λ E →
  return (All.lookup E x)
eval (suc k)  (ƛ e)       =
  getEnv >>= λ E →
  return ⟨ e , E ⟩
eval (suc k)  (l · r)     =
  eval k l >>= λ{ ⟨ e , E ⟩ →
  (eval k r ^ E) >>= λ{ (v , E) →
  usingEnv (v ∷ E) (eval k e) }}
eval (suc k)  (num x)     =
  return (num x)
eval (suc k)  (iop f l r) =
  eval k l >>= λ{ (num vₗ) →
  eval k r >>= λ{ (num vᵣ) →
  return (num (f vₗ vᵣ)) }}
eval (suc k) (ifz c t e) =
  eval k c >>= λ{ (num z) →
  case z of λ{ (+ zero) →
    eval k t
  ; _ →
    eval k e }}
eval (suc k)  (ref e)     =
  eval k e >>= λ v →
  store v
eval (suc k)  (! e)       =
  eval k e >>= λ{ (loc l) →
  deref l }
eval (suc k)  (r ≔ e)     =
  eval k r >>= λ{ (loc l) →
  (eval k e ^ l) >>= λ{ (v , l) →
  update l v >>= λ _ →
  return unit }}


------------------------------------------------------------------------
-- The Agda standard library
--
-- The lifting of a strict order to incorporate a new supremum
------------------------------------------------------------------------

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

-- This module is designed to be used with
-- Relation.Nullary.Construct.Add.Supremum

open import Relation.Binary

module Relation.Binary.Construct.Add.Supremum.Strict
  {a r} {A : Set a} (_<_ : Rel A r) where

open import Level using (_⊔_)
open import Data.Product
open import Function
open import Relation.Nullary hiding (Irrelevant)
import Relation.Nullary.Decidable as Dec
open import Relation.Binary.PropositionalEquality as P
  using (_≡_; refl)
open import Relation.Nullary.Construct.Add.Supremum
import Relation.Binary.Construct.Add.Supremum.Equality as Equality
import Relation.Binary.Construct.Add.Supremum.NonStrict as NonStrict

------------------------------------------------------------------------
-- Definition

infix 4 _<⁺_

data _<⁺_ : Rel (A ⁺) (a ⊔ r) where
  [_]    : {k l : A} → k < l → [ k ] <⁺ [ l ]
  [_]<⊤⁺ : (k : A)           → [ k ] <⁺ ⊤⁺

------------------------------------------------------------------------
-- Relational properties

[<]-injective : ∀ {k l} → [ k ] <⁺ [ l ] → k < l
[<]-injective [ p ] = p

<⁺-asym : Asymmetric _<_ → Asymmetric _<⁺_
<⁺-asym <-asym [ p ]    [ q ] = <-asym p q

<⁺-trans : Transitive _<_ → Transitive _<⁺_
<⁺-trans <-trans [ p ] [ q ]    = [ <-trans p q ]
<⁺-trans <-trans [ p ] [ k ]<⊤⁺ = [ _ ]<⊤⁺

<⁺-dec : Decidable _<_ → Decidable _<⁺_
<⁺-dec _<?_ [ k ] [ l ] = Dec.map′ [_] [<]-injective (k <? l)
<⁺-dec _<?_ [ k ] ⊤⁺    = yes [ k ]<⊤⁺
<⁺-dec _<?_ ⊤⁺    [ l ] = no (λ ())
<⁺-dec _<?_ ⊤⁺    ⊤⁺    = no (λ ())

<⁺-irrelevant : Irrelevant _<_ → Irrelevant _<⁺_
<⁺-irrelevant <-irr [ p ]    [ q ]    = P.cong _ (<-irr p q)
<⁺-irrelevant <-irr [ k ]<⊤⁺ [ k ]<⊤⁺ = P.refl

module _ {r} {_≤_ : Rel A r} where

  open NonStrict _≤_

  <⁺-transʳ : Trans _≤_ _<_ _<_ → Trans _≤⁺_ _<⁺_ _<⁺_
  <⁺-transʳ <-transʳ [ p ] [ q ]    = [ <-transʳ p q ]
  <⁺-transʳ <-transʳ [ p ] [ k ]<⊤⁺ = [ _ ]<⊤⁺

  <⁺-transˡ : Trans _<_ _≤_ _<_ → Trans _<⁺_ _≤⁺_ _<⁺_
  <⁺-transˡ <-transˡ [ p ]    [ q ]       = [ <-transˡ p q ]
  <⁺-transˡ <-transˡ [ p ]    ([ _ ] ≤⊤⁺) = [ _ ]<⊤⁺
  <⁺-transˡ <-transˡ [ k ]<⊤⁺ (⊤⁺ ≤⊤⁺)    = [ k ]<⊤⁺

------------------------------------------------------------------------
-- Relational properties + propositional equality

<⁺-cmp-≡ : Trichotomous _≡_ _<_ → Trichotomous _≡_ _<⁺_
<⁺-cmp-≡ <-cmp ⊤⁺    ⊤⁺    = tri≈ (λ ()) refl (λ ())
<⁺-cmp-≡ <-cmp ⊤⁺    [ l ] = tri> (λ ()) (λ ()) [ l ]<⊤⁺
<⁺-cmp-≡ <-cmp [ k ] ⊤⁺    = tri< [ k ]<⊤⁺ (λ ()) (λ ())
<⁺-cmp-≡ <-cmp [ k ] [ l ] with <-cmp k l
... | tri< a ¬b    ¬c = tri< [ a ] (¬b ∘ []-injective) (¬c ∘ [<]-injective)
... | tri≈ ¬a refl ¬c = tri≈ (¬a ∘ [<]-injective) refl (¬c ∘ [<]-injective)
... | tri> ¬a ¬b    c = tri> (¬a ∘ [<]-injective) (¬b ∘ []-injective) [ c ]

<⁺-irrefl-≡ : Irreflexive _≡_ _<_ → Irreflexive _≡_ _<⁺_
<⁺-irrefl-≡ <-irrefl refl [ x ] = <-irrefl refl x

<⁺-respˡ-≡ : _<⁺_ Respectsˡ _≡_
<⁺-respˡ-≡ = P.subst (_<⁺ _)

<⁺-respʳ-≡ : _<⁺_ Respectsʳ _≡_
<⁺-respʳ-≡ = P.subst (_ <⁺_)

<⁺-resp-≡ : _<⁺_ Respects₂ _≡_
<⁺-resp-≡ = <⁺-respʳ-≡ , <⁺-respˡ-≡

------------------------------------------------------------------------
-- Relational properties + setoid equality

module _ {e} {_≈_ : Rel A e} where

  open Equality _≈_

  <⁺-cmp : Trichotomous _≈_ _<_ → Trichotomous _≈⁺_ _<⁺_
  <⁺-cmp <-cmp ⊤⁺    ⊤⁺    = tri≈ (λ ()) ⊤⁺≈⊤⁺ (λ ())
  <⁺-cmp <-cmp ⊤⁺    [ l ] = tri> (λ ()) (λ ()) [ l ]<⊤⁺
  <⁺-cmp <-cmp [ k ] ⊤⁺    = tri< [ k ]<⊤⁺ (λ ()) (λ ())
  <⁺-cmp <-cmp [ k ] [ l ] with <-cmp k l
  ... | tri< a ¬b ¬c = tri< [ a ] (¬b ∘ [≈]-injective) (¬c ∘ [<]-injective)
  ... | tri≈ ¬a b ¬c = tri≈ (¬a ∘ [<]-injective) [ b ] (¬c ∘ [<]-injective)
  ... | tri> ¬a ¬b c = tri> (¬a ∘ [<]-injective) (¬b ∘ [≈]-injective) [ c ]

  <⁺-irrefl : Irreflexive _≈_ _<_ → Irreflexive _≈⁺_ _<⁺_
  <⁺-irrefl <-irrefl [ p ] [ q ] = <-irrefl p q

  <⁺-respˡ-≈⁺ : _<_ Respectsˡ _≈_ → _<⁺_ Respectsˡ _≈⁺_
  <⁺-respˡ-≈⁺ <-respˡ-≈ [ p ] [ q ]      = [ <-respˡ-≈ p q ]
  <⁺-respˡ-≈⁺ <-respˡ-≈ [ p ] ([ l ]<⊤⁺) = [ _ ]<⊤⁺
  <⁺-respˡ-≈⁺ <-respˡ-≈ ⊤⁺≈⊤⁺ q          = q

  <⁺-respʳ-≈⁺ : _<_ Respectsʳ _≈_ → _<⁺_ Respectsʳ _≈⁺_
  <⁺-respʳ-≈⁺ <-respʳ-≈ [ p ] [ q ] = [ <-respʳ-≈ p q ]
  <⁺-respʳ-≈⁺ <-respʳ-≈ ⊤⁺≈⊤⁺ q     = q

  <⁺-resp-≈⁺ : _<_ Respects₂ _≈_ → _<⁺_ Respects₂ _≈⁺_
  <⁺-resp-≈⁺ = map <⁺-respʳ-≈⁺ <⁺-respˡ-≈⁺

------------------------------------------------------------------------
-- Structures + propositional equality

<⁺-isStrictPartialOrder-≡ : IsStrictPartialOrder _≡_ _<_ →
                            IsStrictPartialOrder _≡_ _<⁺_
<⁺-isStrictPartialOrder-≡ strict = record
  { isEquivalence = P.isEquivalence
  ; irrefl        = <⁺-irrefl-≡ irrefl
  ; trans         = <⁺-trans trans
  ; <-resp-≈      = <⁺-resp-≡
  } where open IsStrictPartialOrder strict

<⁺-isDecStrictPartialOrder-≡ : IsDecStrictPartialOrder _≡_ _<_ →
                               IsDecStrictPartialOrder _≡_ _<⁺_
<⁺-isDecStrictPartialOrder-≡ dectot = record
  { isStrictPartialOrder = <⁺-isStrictPartialOrder-≡ isStrictPartialOrder
  ; _≟_                  = ≡-dec _≟_
  ; _<?_                 = <⁺-dec _<?_
  } where open IsDecStrictPartialOrder dectot

<⁺-isStrictTotalOrder-≡ : IsStrictTotalOrder _≡_ _<_ →
                          IsStrictTotalOrder _≡_ _<⁺_
<⁺-isStrictTotalOrder-≡ strictot = record
  { isEquivalence = P.isEquivalence
  ; trans         = <⁺-trans trans
  ; compare       = <⁺-cmp-≡ compare
  } where open IsStrictTotalOrder strictot

------------------------------------------------------------------------
-- Structures + setoid equality

module _ {e} {_≈_ : Rel A e} where

  open Equality _≈_

  <⁺-isStrictPartialOrder : IsStrictPartialOrder _≈_ _<_ →
                            IsStrictPartialOrder _≈⁺_ _<⁺_
  <⁺-isStrictPartialOrder strict = record
    { isEquivalence = ≈⁺-isEquivalence isEquivalence
    ; irrefl        = <⁺-irrefl irrefl
    ; trans         = <⁺-trans trans
    ; <-resp-≈      = <⁺-resp-≈⁺ <-resp-≈
    } where open IsStrictPartialOrder strict

  <⁺-isDecStrictPartialOrder : IsDecStrictPartialOrder _≈_ _<_ →
                               IsDecStrictPartialOrder _≈⁺_ _<⁺_
  <⁺-isDecStrictPartialOrder dectot = record
    { isStrictPartialOrder = <⁺-isStrictPartialOrder isStrictPartialOrder
    ; _≟_                  = ≈⁺-dec _≟_
    ; _<?_                 = <⁺-dec _<?_
    } where open IsDecStrictPartialOrder dectot

  <⁺-isStrictTotalOrder : IsStrictTotalOrder _≈_ _<_ →
                          IsStrictTotalOrder _≈⁺_ _<⁺_
  <⁺-isStrictTotalOrder strictot = record
    { isEquivalence = ≈⁺-isEquivalence isEquivalence
    ; trans         = <⁺-trans trans
    ; compare       = <⁺-cmp compare
    } where open IsStrictTotalOrder strictot


------------------------------------------------------------------------
-- A quotient inductive-inductive definition of the lifting
-- construction on ω-cpos
------------------------------------------------------------------------

-- The code in this module is based on a suggestion from Paolo
-- Capriotti.

{-# OPTIONS --erased-cubical --safe #-}

open import Omega-cpo

module Lifting {ℓ} (cpo : ω-cpo ℓ ℓ) where

import Equality.Path as P
open import Equality.Propositional.Cubical
open import Logical-equivalence using (_⇔_)
open import Prelude hiding (⊥)

open import Bijection equality-with-J using (_↔_)
open import Equivalence equality-with-J as Eq using (_≃_)
open import Function-universe equality-with-J hiding (_∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J

private
  module CPO = ω-cpo cpo

------------------------------------------------------------------------
-- An ω-cppo

-- The carrier type (Carrier) and the information ordering (_⊑_) are
-- defined simultaneously.

infix 4 _⊑_ _⊒_

data Carrier : Type ℓ
data _⊑_     : Carrier → Carrier → Type ℓ

_⊒_ : Carrier → Carrier → Type ℓ
x ⊒ y = y ⊑ x

private
  variable
    x y : Carrier

-- Increasing sequences.

Increasing-sequence : Type ℓ
Increasing-sequence = ∃ λ (f : ℕ → Carrier) → ∀ n → f n ⊑ f (suc n)

-- Projection functions for Increasing-sequence.

infix 30 _[_]

_[_] : Increasing-sequence → ℕ → Carrier
_[_] = proj₁

increasing : (s : Increasing-sequence) →
             ∀ n → s [ n ] ⊑ s [ suc n ]
increasing = proj₂

-- Upper bounds.

Is-upper-bound : Increasing-sequence → Carrier → Type ℓ
Is-upper-bound s x = ∀ n → s [ n ] ⊑ x

-- Originally these types were postulated. After the release of
-- Cubical Agda they were turned into a QIIT, but in order to not get
-- any extra computation rules they were made abstract.

abstract

  -- _⊥ "constructors".

  data Carrier where
    never′        : Carrier
    now′          : CPO.Carrier → Carrier
    ⨆′            : Increasing-sequence → Carrier
    antisymmetry′ : x ⊑ y → x ⊒ y → x P.≡ y

    -- We have chosen to explicitly make the type set-truncated.
    -- However, this constructor is only used in the definition of the
    -- eliminator below.
    ≡-propositional′ : P.Is-set Carrier

  -- _⊑_ "constructors".

  data _⊑_ where
    ⊑-refl′            : ∀ x → x ⊑ x
    ⊑-trans′           : ∀ x y z → x ⊑ y → y ⊑ z → x ⊑ z
    never⊑′            : ∀ x → never′ ⊑ x
    upper-bound′       : ∀ s → Is-upper-bound s (⨆′ s)
    least-upper-bound′ : ∀ s ub → Is-upper-bound s ub → ⨆′ s ⊑ ub
    now-mono′          : ∀ {x y} → x CPO.⊑ y → now′ x ⊑ now′ y
    now-⨆″             : ∀ s →
                         now′ (CPO.⨆ s) ⊑
                         ⨆′ (Σ-map (now′ ∘_) (now-mono′ ∘_) s)
    ⊑-propositional′   : ∀ {x y} → P.Is-proposition (x ⊑ y)

  never : Carrier
  never = never′

  now : CPO.Carrier → Carrier
  now = now′

  ⨆ : Increasing-sequence → Carrier
  ⨆ = ⨆′

  antisymmetry : x ⊑ y → y ⊑ x → x ≡ y
  antisymmetry = λ p q → _↔_.from ≡↔≡ (antisymmetry′ p q)

  ⊑-refl : ∀ x → x ⊑ x
  ⊑-refl = ⊑-refl′

  ⊑-trans : ∀ x y z → x ⊑ y → y ⊑ z → x ⊑ z
  ⊑-trans = ⊑-trans′

  never⊑ : ∀ x → never ⊑ x
  never⊑ = never⊑′

  upper-bound : ∀ s → Is-upper-bound s (⨆ s)
  upper-bound = upper-bound′

  least-upper-bound : ∀ s ub → Is-upper-bound s ub → ⨆ s ⊑ ub
  least-upper-bound = least-upper-bound′

  now-mono : ∀ {x y} → x CPO.⊑ y → now x ⊑ now y
  now-mono = now-mono′

  now-⨆′ : ∀ s → now (CPO.⨆ s) ⊑ ⨆ (Σ-map (now ∘_) (now-mono ∘_) s)
  now-⨆′ = now-⨆″

  ⊑-propositional : ∀ {x y} → Is-proposition (x ⊑ y)
  ⊑-propositional {x = x} {y = y} =
                              $⟨ ⊑-propositional′ ⟩
    P.Is-proposition (x ⊑ y)  ↝⟨ _↔_.from (H-level↔H-level 1) ⟩□
    Is-proposition (x ⊑ y)    □

-- The construction above is an ω-cppo.

cppo : ω-cppo ℓ ℓ
cppo = record
  { cpo = record
    { Carrier           = Carrier
    ; _⊑_               = _⊑_
    ; reflexivity       = ⊑-refl _
    ; antisymmetry      = antisymmetry
    ; transitivity      = ⊑-trans _ _ _
    ; ⊑-propositional   = ⊑-propositional
    ; ⨆                 = ⨆
    ; upper-bound       = upper-bound
    ; least-upper-bound = least-upper-bound _ _
    }
  ; least  = never
  ; least⊑ = never⊑ _
  }

------------------------------------------------------------------------
-- Dependent eliminators

-- Predicate transformer related to increasing sequences.

Inc : ∀ {p q}
      (P : Carrier → Type p)
      (Q : ∀ {x y} → P x → P y → x ⊑ y → Type q) →
      Increasing-sequence → Type (p ⊔ q)
Inc P Q s =
  ∃ λ (p : ∀ n → P (s [ n ])) →
    ∀ n → Q (p n) (p (suc n)) (increasing s n)

-- Record wrapping up the eliminators' arguments.

record Rec-args
         {p q}
         (P : Carrier → Type p)
         (Q : ∀ {x y} → P x → P y → x ⊑ y → Type q) :
         Type (ℓ ⊔ p ⊔ q) where
  field
    pe : P never
    po : ∀ x → P (now x)
    pl : ∀ s (pq : Inc P Q s) → P (⨆ s)
    pa : ∀ {x y} (x⊑y : x ⊑ y) (x⊒y : x ⊒ y)
         (p-x : P x) (p-y : P y)
         (q-x⊑y : Q p-x p-y x⊑y) (q-x⊒y : Q p-y p-x x⊒y) →
         subst P (antisymmetry x⊑y x⊒y) p-x ≡ p-y
    pp : ∀ {x} {p₁ p₂ : P x} → Is-proposition (p₁ ≡ p₂)
    qr : ∀ x (p : P x) → Q p p (⊑-refl x)
    qt : ∀ {x y z}
         (x⊑y : x ⊑ y) (y⊑z : y ⊑ z)
         (p-x : P x) (p-y : P y) (p-z : P z) →
         Q p-x p-y x⊑y → Q p-y p-z y⊑z →
         Q p-x p-z (⊑-trans x y z x⊑y y⊑z)
    qe : ∀ x (p : P x) → Q pe p (never⊑ x)
    qu : ∀ s (pq : Inc P Q s) n →
         Q (proj₁ pq n) (pl s pq) (upper-bound s n)
    ql : ∀ s ub is-ub (pq : Inc P Q s) (pu : P ub)
         (qu : ∀ n → Q (proj₁ pq n) pu (is-ub n)) →
         Q (pl s pq) pu (least-upper-bound s ub is-ub)
    qm : ∀ {x y} (le : x CPO.⊑ y) →
         Q (po x) (po y) (now-mono le)
    q⨆ : ∀ s →
         Q (po (CPO.⨆ s))
           (pl (Σ-map (now ∘_) (now-mono ∘_) s)
               ( (λ n → po (s CPO.[ n ]))
               , (λ n → qm (CPO.increasing s n))
               ))
           (now-⨆′ s)
    qp : ∀ {x y} (p-x : P x) (p-y : P y) (x⊑y : x ⊑ y) →
         Is-proposition (Q p-x p-y x⊑y)

-- The eliminators.

module _ {p q}
         {P : Carrier → Type p}
         {Q : ∀ {x y} → P x → P y → x ⊑ y → Type q}
         (args : Rec-args P Q) where

  open Rec-args args

  mutual

    inc-rec : (s : Increasing-sequence) → Inc P Q s
    inc-rec (s , inc) = (λ n → ⊥-rec (s   n))
                      , (λ n → ⊑-rec (inc n))

    abstract

      ⊥-rec : (x : Carrier) → P x
      ⊥-rec never′ = pe

      ⊥-rec (now′ x) = po x

      ⊥-rec (⨆′ s) = pl s (inc-rec s)

      ⊥-rec (antisymmetry′ {x = x} {y = y} p q i) =
        subst≡→[]≡
          {B = P}
          {eq = antisymmetry′ p q}
          (pa p q (⊥-rec x) (⊥-rec y) (⊑-rec p) (⊑-rec q)) i

      ⊥-rec (≡-propositional′ {x = x} {y = y} p q i j) = lemma i j
        where
        ≡-propositional : P.Is-set Carrier
        ≡-propositional = ≡-propositional′

        lemma :
          P.[ (λ i → P.[ (λ j → P (≡-propositional p q i j)) ]
                       ⊥-rec x ≡ ⊥-rec y) ]
            (λ i → ⊥-rec (p i)) ≡ (λ i → ⊥-rec (q i))
        lemma = P.heterogeneous-UIP
                  (λ x → _↔_.to (H-level↔H-level 2) (pp {x = x}))
                  (≡-propositional′ p q)
                  _ _

      ⊑-rec : (x⊑y : x ⊑ y) → Q (⊥-rec x) (⊥-rec y) x⊑y
      ⊑-rec (⊑-refl′ x) = qr x (⊥-rec x)

      ⊑-rec (⊑-trans′ x y z p q) =
        qt p q (⊥-rec x) (⊥-rec y) (⊥-rec z) (⊑-rec p) (⊑-rec q)

      ⊑-rec (never⊑′ x) = qe x (⊥-rec x)

      ⊑-rec (upper-bound′ s n) = qu s (inc-rec s) n

      ⊑-rec (least-upper-bound′ s ub is-ub) =
        ql s ub is-ub (inc-rec s) (⊥-rec ub)
          (λ n → ⊑-rec {x = proj₁ s n} (is-ub n))

      ⊑-rec (now-mono′ x) = qm x

      ⊑-rec (now-⨆″ s) = q⨆ s

      ⊑-rec (⊑-propositional′ {x = x} {y = y} p q i) = lemma i
        where
        ⊑-propositional″ : ∀ {x y} → P.Is-proposition (x ⊑ y)
        ⊑-propositional″ = ⊑-propositional′

        lemma : P.[ (λ i → Q (⊥-rec x) (⊥-rec y)
                             (⊑-propositional″ p q i)) ]
                  ⊑-rec p ≡ ⊑-rec q
        lemma =
          P.heterogeneous-irrelevance
            (λ p → _↔_.to (H-level↔H-level 1)
                     (qp (⊥-rec x) (⊥-rec y) p))

      -- Some computation rules.

      ⊥-rec-never : ⊥-rec never ≡ pe
      ⊥-rec-never = refl

      ⊥-rec-now : ∀ x → ⊥-rec (now x) ≡ po x
      ⊥-rec-now _ = refl

      ⊥-rec-⨆ : ∀ s → ⊥-rec (⨆ s) ≡ pl s (inc-rec s)
      ⊥-rec-⨆ _ = refl

------------------------------------------------------------------------
-- Some lemmas

-- The inequality now-⨆′ can be strengthened to an equality.

now-⨆ : (s : CPO.Increasing-sequence) →
        now (CPO.⨆ s) ≡ ⨆ (Σ-map (now ∘_) (now-mono ∘_) s)
now-⨆ s =
  antisymmetry
    (now-⨆′ s)
    (least-upper-bound _ _ λ n → now-mono (CPO.upper-bound s n))

private

  -- A lemma.

  Carrier-is-set-and-equality-characterisation : Is-set Carrier × _
  Carrier-is-set-and-equality-characterisation =
    Eq.propositional-identity≃≡
      (λ x y → x ⊑ y × x ⊒ y)
      (λ _ _ → ×-closure 1 ⊑-propositional ⊑-propositional)
      (λ x → ⊑-refl x , ⊑-refl x)
      (λ x y → uncurry {B = λ _ → x ⊒ y} antisymmetry)

-- Carrier is a set. (This lemma is analogous to Theorem 11.3.9 in
-- "Homotopy Type Theory: Univalent Foundations of Mathematics" (first
-- edition).)

Carrier-is-set : Is-set Carrier
Carrier-is-set = proj₁ Carrier-is-set-and-equality-characterisation

-- Equality characterisation lemma for Carrier.

equality-characterisation-Carrier :
  ∀ {x y} → (x ⊑ y × x ⊒ y) ≃ (x ≡ y)
equality-characterisation-Carrier =
  proj₂ Carrier-is-set-and-equality-characterisation ext

-- Equality characterisation lemma for increasing sequences.

equality-characterisation-increasing :
  {s₁ s₂ : Increasing-sequence} →
  (∀ n → s₁ [ n ] ≡ s₂ [ n ]) ↔ s₁ ≡ s₂
equality-characterisation-increasing {s₁} {s₂} =
  (∀ n → s₁ [ n ] ≡ s₂ [ n ])  ↔⟨ Eq.extensionality-isomorphism ext ⟩
  proj₁ s₁ ≡ proj₁ s₂          ↝⟨ ignore-propositional-component
                                    (Π-closure ext 1 λ _ →
                                     ⊑-propositional) ⟩□
  s₁ ≡ s₂                      □


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

open import Data.Product
open import Data.Sum

open import Common

module Progress {ℙ : Set} (message : Message ℙ)
  where

open import SessionType message
open import Transitions message
open import Session message

data Progress : Session -> Set where
  win#def : ∀{T S} (w : Win T) (def : Defined S) -> Progress (T # S)
  inp#out : ∀{f g} (W : Witness g) -> Progress (inp f # out g)
  out#inp : ∀{f g} (W : Witness f) -> Progress (out f # inp g)

progress-sound : ∀{S} -> Progress S -> ProgressS S
progress-sound (win#def e def) = inj₁ (win#def e def)
progress-sound (inp#out (_ , !x)) = inj₂ (_ , sync inp (out !x))
progress-sound (out#inp (_ , !x)) = inj₂ (_ , sync (out !x) inp)

progress-complete : ∀{S} -> ProgressS S -> Progress S
progress-complete (inj₁ (win#def e def)) = win#def e def
progress-complete (inj₂ (_ , sync inp (out !x))) = inp#out (_ , !x)
progress-complete (inj₂ (_ , sync (out !x) inp)) = out#inp (_ , !x)


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

import Level

open import Data.Empty
open import Data.Product
open import Data.List using (_∷_; []; _++_)
open import Data.List.Properties using (∷-injectiveʳ)

open import Relation.Nullary
open import Relation.Unary
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; subst)

open import Common

module Semantics {ℙ : Set} (message : Message ℙ)
  where

open import Trace message
open import HasTrace message
open import TraceSet message
open import SessionType message
open import Transitions message

record Semantics (X : TraceSet) : Set where
  field
    closed   : PrefixClosed X
    coherent : Coherent X
open Semantics public

data Classification : TraceSet -> Set where
  emp : ∀{X}   -> X ⊆ ∅        -> Classification X
  eps : ∀{X}   -> X ⊆⊇ ｛ [] ｝ -> Classification X
  inp : ∀{X x} -> I x ∷ [] ∈ X -> Classification X
  out : ∀{X x} -> O x ∷ [] ∈ X -> Classification X

is-emp : ∀{X} -> Semantics X -> [] ∉ X -> X ⊆ ∅
is-emp sem nxε xφ with closed sem none xφ
... | xε = ⊥-elim (nxε xε)

is-eps :
  ∀{X} ->
  Semantics X ->
  (¬ (∃[ x ] (I x ∷ [] ∈ X))) ->
  (¬ (∃[ x ] (O x ∷ [] ∈ X))) ->
  X ⊆ ｛ [] ｝
is-eps sem nix nox {[]} xφ = refl
is-eps sem nix nox {I x ∷ φ} xφ with closed sem (some none) xφ
... | ix = ⊥-elim (nix (x , ix))
is-eps sem nix nox {O x ∷ φ} xφ with closed sem (some none) xφ
... | ox = ⊥-elim (nox (x , ox))

kind? : ∀{X} -> Semantics X -> Classification X
kind? {X} sem with excluded-middle {∃[ x ] (I x ∷ [] ∈ X)}
... | yes (_ , ix) = inp ix
... | no nix with excluded-middle {∃[ x ] (O x ∷ [] ∈ X)}
... | yes (_ , ox) = out ox
... | no nox with excluded-middle {[] ∈ X}
... | yes xε = eps (is-eps sem nix nox , λ { refl -> xε })
... | no nxε = emp (is-emp sem nxε)

sem-sound : (T : SessionType) -> Semantics ⟦ T ⟧
closed (sem-sound T) = ⊑-has-trace
coherent (sem-sound T) = trace-coherence

derive-semantics : ∀{X} -> Semantics X -> (α : Action) -> Semantics (X ∂ α)
closed (derive-semantics sem _) pre hasφ = closed sem (some pre) hasφ
coherent (derive-semantics {X} sem γ) {φ} {α} {β} hasφα hasφβ = coherent {X} sem {γ ∷ φ} hasφα hasφβ

co-semantics : ∀{X} -> Semantics X -> Semantics (CoSet X)
closed (co-semantics sem) le coφ = closed sem (⊑-co-trace le) coφ
coherent (co-semantics sem) {φ} {ψ₁} {ψ₂} {x} {y} iφx oφx
  rewrite co-trace-++ φ (I x ∷ ψ₁) | co-trace-++ φ (O y ∷ ψ₂) =
  coherent sem {co-trace φ} {co-trace ψ₂} {co-trace ψ₁} {y} {x} oφx iφx

non-empty-semantics-contains-[] : ∀{X : TraceSet}{φ} -> Semantics X -> φ ∈ X -> [] ∈ X
non-empty-semantics-contains-[] sem w = closed sem none w

decode : ∀{X} -> Semantics X -> ∞SessionType
force (decode sem) with kind? sem
... | emp _ = nil
... | eps _ = win
... | inp _ = inp λ x -> decode (derive-semantics sem (I x))
... | out _ = out λ x -> decode (derive-semantics sem (O x))

decode-sub : ∀{X} (sem : Semantics X) -> ⟦ decode sem .force ⟧ ⊆ X
decode-sub sem tφ with kind? sem
decode-sub _ (_ , () , refl) | emp _
decode-sub _ (_ , _ , step () _) | emp _
decode-sub _ (_ , _ , refl) | eps (_ , sup) = sup refl
decode-sub _ (_ , _ , step (out ()) _) | eps _
decode-sub sem (_ , _ , refl) | inp w = non-empty-semantics-contains-[] sem w
decode-sub sem (_ , def , step inp tr) | inp w =
  decode-sub (derive-semantics sem (I _)) (_ , def , tr)
decode-sub sem (_ , _ , refl) | out w = non-empty-semantics-contains-[] sem w
decode-sub sem (_ , def , step (out !x) tr) | out w =
  decode-sub (derive-semantics sem (O _)) (_ , def , tr)

decode-sup : ∀{X} (sem : Semantics X) -> X ⊆ ⟦ decode sem .force ⟧
decode-sup sem {φ} hasφ with kind? sem
... | emp sub = ⊥-elim (sub hasφ)
... | eps (sub , _) rewrite sym (sub hasφ) = _ , out , refl
decode-sup sem {[]} hasφ | inp _ = _ , inp , refl
decode-sup sem {I _ ∷ _} hasφ | inp _ =
  let _ , def , tr = decode-sup (derive-semantics sem (I _)) hasφ in
  _ , def , step inp tr
decode-sup sem {O _ ∷ _} hasφ | inp w = ⊥-elim (coherent sem {[]} w hasφ)
decode-sup sem {[]} hasφ | out _ = _ , out , refl
decode-sup sem {I _ ∷ _} hasφ | out w = ⊥-elim (coherent sem {[]} hasφ w)
decode-sup sem {O _ ∷ _} hasφ | out _ =
  let _ , def , tr = decode-sup (derive-semantics sem (O _)) hasφ in
  _ , def , step (out (transitions+defined->defined tr def)) tr

decode+maximal->win :
  ∀{X φ}
  (sem : Semantics X)
  (rφ : decode sem .force HasTrace φ) ->
  φ ∈ Maximal X ->
  Win (after rφ)
decode+maximal->win sem rφ comp with kind? sem
decode+maximal->win sem (_ , () , refl) comp | emp sub
decode+maximal->win sem (_ , _ , step () _) comp | emp sub
decode+maximal->win sem (_ , _ , refl) comp | eps _ = Win-win
decode+maximal->win sem (_ , _ , step (out ()) _) comp | eps _
decode+maximal->win sem (_ , def , refl) (maximal _ F) | inp w with F none w
... | ()
decode+maximal->win sem (_ , def , step inp tr) comp | inp _ =
  decode+maximal->win (derive-semantics sem (I _)) (_ , def , tr) (derive-maximal comp)
decode+maximal->win sem (_ , def , refl) (maximal _ F) | out w with F none w
... | ()
decode+maximal->win sem (_ , def , step (out !x) tr) comp | out w =
  decode+maximal->win (derive-semantics sem (O _)) (_ , def , tr) (derive-maximal comp)

win->maximal :
  ∀{T φ ψ} ->
  φ ⊑ ψ ->
  (tφ : T HasTrace φ) ->
  T HasTrace ψ ->
  Win (after tφ) ->
  φ ≡ ψ
win->maximal le (_ , tdef , refl) (_ , sdef , refl) w = refl
win->maximal le (_ , tdef , refl) (_ , sdef , step (out !x) sr) (out U) = ⊥-elim (U _ !x)
win->maximal (some le) (_ , tdef , step inp tr) (_ , sdef , step inp sr) w
  rewrite win->maximal le (_ , tdef , tr) (_ , sdef , sr) w = refl
win->maximal (some le) (_ , tdef , step (out _) tr) (_ , sdef , step (out _) sr) w
  rewrite win->maximal le (_ , tdef , tr) (_ , sdef , sr) w = refl

decode+win->maximal :
  ∀{X φ}
  (sem : Semantics X)
  (rφ : decode sem .force HasTrace φ) ->
  Win (after rφ) ->
  φ ∈ Maximal X
decode+win->maximal {X} {φ} sem rφ w =
  maximal (decode-sub sem rφ) λ le xψ -> let rψ = decode-sup sem xψ in
                                          sym (win->maximal le rφ rψ w)


module Pi-.Interp where
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Pi-.Syntax
open import Pi-.Opsem
open import Pi-.Eval

{-# TERMINATING #-}
mutual
  interp : {A B : 𝕌} → (A ↔ B) → Val A B → Val B A
  -- Forward
  interp unite₊l (inj₂ y ⃗)        = y ⃗
  interp uniti₊l (v ⃗)             = inj₂ v ⃗
  interp swap₊   (inj₁ x ⃗)        = inj₂ x ⃗
  interp swap₊   (inj₂ y ⃗)        = inj₁ y ⃗
  interp assocl₊ (inj₁ x ⃗)        = inj₁ (inj₁ x) ⃗
  interp assocl₊ (inj₂ (inj₁ y) ⃗) = inj₁ (inj₂ y) ⃗
  interp assocl₊ (inj₂ (inj₂ z) ⃗) = inj₂ z ⃗
  interp assocr₊ (inj₁ (inj₁ x) ⃗) = inj₁ x ⃗
  interp assocr₊ (inj₁ (inj₂ y) ⃗) = inj₂ (inj₁ y) ⃗
  interp assocr₊ (inj₂ z ⃗)        = inj₂ (inj₂ z) ⃗
  interp unite⋆l ((tt , v) ⃗)      = v ⃗
  interp uniti⋆l (v ⃗)             = (tt , v) ⃗
  interp swap⋆   ((x , y) ⃗)       = (y , x) ⃗
  interp assocl⋆ ((x , (y , z)) ⃗) = ((x , y) , z) ⃗
  interp assocr⋆ (((x , y) , z) ⃗) = (x , y , z) ⃗
  interp dist    ((inj₁ x , z) ⃗)  = inj₁ (x , z) ⃗
  interp dist    ((inj₂ y , z) ⃗)  = inj₂ (y , z) ⃗
  interp factor  (inj₁ (x , z) ⃗)  = (inj₁ x , z) ⃗
  interp factor  (inj₂ (y , z) ⃗)  = (inj₂ y , z) ⃗
  interp id↔     (v ⃗)             = v ⃗
  interp (c₁ ⨾ c₂) (v ⃗) with interp c₁ (v ⃗)
  interp (c₁ ⨾ c₂) (v ⃗) | (v' ⃖) = v' ⃖
  interp (c₁ ⨾ c₂) (v ⃗) | (v' ⃗) = c₁ ⨾[ v' ⃗]⨾ c₂
  interp (c₁ ⊕ c₂) (inj₁ x ⃗) with interp c₁ (x ⃗)
  interp (c₁ ⊕ c₂) (inj₁ x ⃗) | (x' ⃗) = inj₁ x' ⃗
  interp (c₁ ⊕ c₂) (inj₁ x ⃗) | (x' ⃖) = inj₁ x' ⃖
  interp (c₁ ⊕ c₂) (inj₂ y ⃗) with interp c₂ (y ⃗)
  interp (c₁ ⊕ c₂) (inj₂ y ⃗) | (y' ⃗) = inj₂ y' ⃗
  interp (c₁ ⊕ c₂) (inj₂ y ⃗) | (y' ⃖) = inj₂ y' ⃖
  interp (c₁ ⊗ c₂) ((x , y) ⃗) with interp c₁ (x ⃗)
  interp (c₁ ⊗ c₂) ((x , y) ⃗) | (x' ⃖) = (x' , y) ⃖
  interp (c₁ ⊗ c₂) ((x , y) ⃗) | (x' ⃗) with interp c₂ (y ⃗)
  interp (c₁ ⊗ c₂) ((x , y) ⃗) | (x' ⃗) | (y' ⃗) = (x' , y') ⃗
  interp (c₁ ⊗ c₂) ((x , y) ⃗) | (x' ⃗) | (y' ⃖) = (x , y') ⃖
  interp ε₊ (inj₁ v ⃗)     = inj₂ (- v) ⃖
  interp ε₊ (inj₂ (- v) ⃗) = inj₁ v ⃖
  -- Backward
  interp unite₊l (v ⃖)             = inj₂ v ⃖
  interp uniti₊l (inj₂ v ⃖)        = v ⃖
  interp swap₊   (inj₁ x ⃖)        = inj₂ x ⃖
  interp swap₊   (inj₂ y ⃖)        = inj₁ y ⃖
  interp assocl₊ (inj₁ (inj₁ x) ⃖) = inj₁ x ⃖
  interp assocl₊ (inj₁ (inj₂ y) ⃖) = inj₂ (inj₁ y) ⃖
  interp assocl₊ (inj₂ z ⃖)        = inj₂ (inj₂ z) ⃖
  interp assocr₊ (inj₁ x ⃖)        = inj₁ (inj₁ x) ⃖
  interp assocr₊ (inj₂ (inj₁ y) ⃖) = inj₁ (inj₂ y) ⃖
  interp assocr₊ (inj₂ (inj₂ z) ⃖) = inj₂ z ⃖
  interp unite⋆l (v ⃖)             = (tt , v) ⃖
  interp uniti⋆l ((tt , v) ⃖)      = v ⃖
  interp swap⋆   ((x , y) ⃖)       = (y , x) ⃖
  interp assocl⋆ (((x , y) , z) ⃖) = (x , (y , z)) ⃖
  interp assocr⋆ ((x , (y , z)) ⃖) = ((x , y) , z) ⃖
  interp dist    (inj₁ (x , z) ⃖)  = (inj₁ x , z) ⃖
  interp dist    (inj₂ (y , z) ⃖)  = (inj₂ y , z) ⃖
  interp factor  ((inj₁ x , z) ⃖)  = inj₁ (x , z) ⃖
  interp factor  ((inj₂ y , z) ⃖)  = inj₂ (y , z) ⃖
  interp id↔ (v ⃖) = v ⃖
  interp (c₁ ⨾ c₂) (v ⃖) with interp c₂ (v ⃖)
  interp (c₁ ⨾ c₂) (v ⃖) | v' ⃗ = v' ⃗
  interp (c₁ ⨾ c₂) (v ⃖) | v' ⃖ = c₁ ⨾[ v' ⃖]⨾ c₂
  interp (c₁ ⊕ c₂) (inj₁ x ⃖) with interp c₁ (x ⃖)
  interp (c₁ ⊕ c₂) (inj₁ x ⃖) | (x' ⃖) = inj₁ x' ⃖
  interp (c₁ ⊕ c₂) (inj₁ x ⃖) | (x' ⃗) = inj₁ x' ⃗
  interp (c₁ ⊕ c₂) (inj₂ y ⃖) with interp c₂ (y ⃖)
  interp (c₁ ⊕ c₂) (inj₂ y ⃖) | (y' ⃖) = inj₂ y' ⃖
  interp (c₁ ⊕ c₂) (inj₂ y ⃖) | (y' ⃗) = inj₂ y' ⃗
  interp (c₁ ⊗ c₂) ((x , y) ⃖) with interp c₂ (y ⃖)
  interp (c₁ ⊗ c₂) ((x , y) ⃖) | (y' ⃗) = (x , y') ⃗
  interp (c₁ ⊗ c₂) ((x , y) ⃖) | (y' ⃖) with interp c₁ (x ⃖)
  interp (c₁ ⊗ c₂) ((x , y) ⃖) | (y' ⃖) | (x' ⃖) = (x' , y') ⃖
  interp (c₁ ⊗ c₂) ((x , y) ⃖) | (y' ⃖) | (x' ⃗) = (x' , y) ⃗
  interp η₊ (inj₁ v ⃖)     = inj₂ (- v) ⃗
  interp η₊ (inj₂ (- v) ⃖) = inj₁ v ⃗

  _⨾[_⃗]⨾_ : ∀ {A B C} → (A ↔ B) → ⟦ B ⟧ → (B ↔ C) → Val C A
  c₁ ⨾[ b ⃗]⨾ c₂ with interp c₂ (b ⃗)
  c₁ ⨾[ b ⃗]⨾ c₂ | c ⃗ = c ⃗
  c₁ ⨾[ b ⃗]⨾ c₂ | b' ⃖ = c₁ ⨾[ b' ⃖]⨾ c₂

  _⨾[_⃖]⨾_ : ∀ {A B C} → (A ↔ B) → ⟦ B ⟧ → (B ↔ C) → Val C A
  c₁ ⨾[ b ⃖]⨾ c₂ with interp c₁ (b ⃖)
  c₁ ⨾[ b ⃖]⨾ c₂ | a ⃖ = a ⃖
  c₁ ⨾[ b ⃖]⨾ c₂ | b' ⃗ = c₁ ⨾[ b' ⃗]⨾ c₂


------------------------------------------------------------------------
-- The semantics in Lambda.Closure.Relational and
-- Lambda.Closure.Functional are equivalent
------------------------------------------------------------------------

module Lambda.Closure.Equivalence where

open import Axiom.ExcludedMiddle
open import Category.Monad.Partiality as Partiality
  using (_⊥; never; steps; module Steps)
open import Codata.Musical.Notation
open import Data.Empty using (⊥-elim)
open import Data.Maybe hiding (_>>=_)
open import Data.Nat
open import Data.Nat.Induction
import Data.Nat.Properties as ℕ
import Data.Nat.Solver as ℕ
open import Data.Product
open import Data.Sum
open import Data.Vec using (Vec; []; _∷_)
open import Function
import Level
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Nullary.Negation

open ℕ.+-*-Solver using (solve; _:=_; _:+_; con)
open Partiality._⊥
private
  open module PE {A : Set} = Partiality.Equality (_≡_ {A = A})
  open module PR {A : Set} =
    Partiality.Reasoning (P.isEquivalence {A = A})
    hiding (_≡⟨_⟩_) renaming (_∎ to _□)

open import Lambda.Syntax
open Closure Tm
open import Lambda.Closure.Relational as R
open import Lambda.Closure.Functional as F hiding (module Workaround)
open PF

------------------------------------------------------------------------
-- Successful, terminating computations

-- The functional semantics is complete with respect to the relational
-- one.

complete⇓ : ∀ {n} {t : Tm n} {ρ v} →
            ρ ⊢ t ⇓ v → ⟦ t ⟧ ρ ≈ return v
complete⇓ var = _ □
complete⇓ con = _ □
complete⇓ ƛ   = _ □
complete⇓ {ρ = ρ} {v} (app {t₁} {t₂} {t = t} {ρ′} {v′ = v′} t₁⇓ t₂⇓ t₁t₂⇓) =
  ⟦ t₁ · t₂ ⟧ ρ                  ≅⟨ ·-comp t₁ t₂ ⟩
  ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ          ≈⟨ complete⇓ t₁⇓ ⟦·⟧-cong complete⇓ t₂⇓ ⟩
  return (ƛ t ρ′) ⟦·⟧ return v′  ≳⟨ laterˡ (_ □) ⟩
  ⟦ t ⟧ (v′ ∷ ρ′)                ≈⟨ complete⇓ t₁t₂⇓ ⟩
  return v                       □

-- The functional semantics is sound with respect to the relational
-- one.

sound⇓ : ∀ {n} (t : Tm n) {ρ : Env n} {v} →
         ⟦ t ⟧ ρ ≈ return v → ρ ⊢ t ⇓ v
sound⇓ t t⇓ = <′-rec P sound⇓′ (steps t⇓) t t⇓ P.refl
  where
  P : ℕ → Set
  P s = ∀ {n} (t : Tm n) {ρ : Env n} {v}
          (t⇓ : ⟦ t ⟧ ρ ≈ return v) → steps t⇓ ≡ s → ρ ⊢ t ⇓ v

  sound⇓′ : ∀ s → <′-Rec P s → P s
  sound⇓′ s rec (con i)           (now P.refl) _  = con
  sound⇓′ s rec (var x)           (now P.refl) _  = var
  sound⇓′ s rec (ƛ t)             (now P.refl) _  = ƛ
  sound⇓′ s rec (t₁ · t₂) {ρ} {v} t₁t₂⇓        eq
    with >>=-inversion-⇓ (⟦ t₁ ⟧ ρ) (
           ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ  ≅⟨ sym $ ·-comp t₁ t₂ ⟩
           ⟦ t₁ · t₂ ⟧ ρ          ≈⟨ t₁t₂⇓ ⟩
           return v               □)
  sound⇓′ s rec (t₁ · t₂) {ρ}     t₁t₂⇓ eq | (v₁    , t₁⇓ , t₂∙⇓ , eq₁) with >>=-inversion-⇓ (⟦ t₂ ⟧ ρ) t₂∙⇓
  sound⇓′ s rec (t₁ · t₂)         t₁t₂⇓ eq | (con i , t₁⇓ , t₂∙⇓ , eq₁) | (v₂ , t₂⇓ , now ()    , _  )
  sound⇓′ s rec (t₁ · t₂) {ρ} {v} t₁t₂⇓ eq | (ƛ t _ , t₁⇓ , t₂∙⇓ , eq₁) | (v₂ , t₂⇓ , laterˡ ∙⇓ , eq₂) =
    app (rec (steps t₁⇓) ₁< t₁ t₁⇓ P.refl)
        (rec (steps t₂⇓) ₂< t₂ t₂⇓ P.refl)
        (rec (steps  ∙⇓) ∙< t   ∙⇓ P.refl)
    where
    open ℕ.≤-Reasoning

    ₁₂∙< = begin
      1 + steps t₁⇓ + (steps t₂⇓ + steps ∙⇓)                 ≡⟨ solve 3 (λ x y z → con 1 :+ x :+ (y :+ z) :=
                                                                                x :+ (y :+ (con 1 :+ z)))
                                                                        P.refl (steps t₁⇓) (steps t₂⇓) _ ⟩
      steps t₁⇓ + (steps t₂⇓ + (1 + steps ∙⇓))               ≡⟨ P.cong (_+_ (steps t₁⇓)) eq₂ ⟩
      steps t₁⇓ + steps t₂∙⇓                                 ≡⟨ eq₁ ⟩
      steps (⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ  ≅⟨ sym $ ·-comp t₁ t₂ ⟩
             ⟦ t₁ · t₂ ⟧ ρ          ≈⟨ t₁t₂⇓ ⟩
             return v               □)                       ≡⟨ Steps.left-identity (sym $ ·-comp t₁ t₂) _ ⟩
      steps (⟦ t₁ · t₂ ⟧ ρ          ≈⟨ t₁t₂⇓ ⟩
             return v               □)                       ≡⟨ Steps.right-identity t₁t₂⇓ (return v □) ⟩
      steps t₁t₂⇓                                            ≡⟨ eq ⟩
      s                                                      ∎

    ₁< = ℕ.≤⇒≤′ (begin
      1 + steps t₁⇓                           ≤⟨ ℕ.m≤m+n (1 + steps t₁⇓) _ ⟩
      1 + steps t₁⇓ + (steps t₂⇓ + steps ∙⇓)  ≤⟨ ₁₂∙< ⟩
      s                                       ∎)

    ₂∙< = begin
      1 + steps t₂⇓ + steps ∙⇓                ≤⟨ s≤s (ℕ.m≤n+m _ (steps t₁⇓)) ⟩
      1 + steps t₁⇓ + (steps t₂⇓ + steps ∙⇓)  ≤⟨ ₁₂∙< ⟩
      s                                       ∎

    ₂< = ℕ.≤⇒≤′ (begin
      1 + steps t₂⇓             ≤⟨ ℕ.m≤m+n (1 + steps t₂⇓) _ ⟩
      1 + steps t₂⇓ + steps ∙⇓  ≤⟨ ₂∙< ⟩
      s                         ∎)

    ∙< = ℕ.≤⇒≤′ (begin
      1 + steps ∙⇓              ≤⟨ s≤s (ℕ.m≤n+m _ (steps t₂⇓)) ⟩
      1 + steps t₂⇓ + steps ∙⇓  ≤⟨ ₂∙< ⟩
      s                         ∎)

------------------------------------------------------------------------
-- Non-terminating computations

-- The functional semantics is complete for non-terminating computations.

module Complete⇑ where

  infix  4 _≈P_ _≈W_
  infixr 2 _≅⟪_⟫_ _≳⟪_⟫_ _≈⟪_⟫_

  data _≈P_ : Maybe Value ⊥ → Maybe Value ⊥ → Set₁ where
    ⌈_⌉ : ∀ {x y} (x≳y : x ≳ y) → x ≈P y

    later : ∀ {x y} (x≈y : ∞ (♭ x ≈P ♭ y)) → later x ≈P later y

    _>>=P_ : ∀ {x₁ x₂ f₁ f₂} →
             (x₁≈x₂ : x₁ ≈P x₂) (f₁≈f₂ : ∀ x → f₁ x ≈P f₂ x) →
             (x₁ >>= f₁) ≈P (x₂ >>= f₂)
    _⟦·⟧P_ : ∀ {v₁₁ v₁₂ v₂₁ v₂₂} →
             (v₁₁≈v₂₁ : v₁₁ ≈P v₂₁) (v₁₂≈v₂₂ : v₁₂ ≈P v₂₂) →
             v₁₁ ⟦·⟧ v₁₂ ≈P v₂₁ ⟦·⟧ v₂₂

    _≅⟪_⟫_ : ∀ x {y z} (x≅y : x ≅  y) (y≈z : y ≈P z) → x ≈P z
    _≳⟪_⟫_ : ∀ x {y z} (x≳y : x ≳  y) (y≈z : y ≈P z) → x ≈P z
    _≈⟪_⟫_ : ∀ x {y z} (x≈y : x ≈P y) (y≅z : y ≅  z) → x ≈P z

  private

    data _≈W_ : Maybe Value ⊥ → Maybe Value ⊥ → Set₁ where
      now    : ∀ {v}                      → now   v ≈W now   v
      later  : ∀ {x y} (x≈y : ♭ x ≈P ♭ y) → later x ≈W later y
      laterˡ : ∀ {x y} (x≈y : ♭ x ≈W   y) → later x ≈W       y

    ⌈_⌉W : ∀ {x y} → x ≳ y → x ≈W y
    ⌈ now P.refl ⌉W = now
    ⌈ later  x≳y ⌉W = later ⌈ ♭ x≳y ⌉
    ⌈ laterˡ x≳y ⌉W = laterˡ ⌈ x≳y ⌉W

    program : ∀ {x y} → x ≈W y → x ≈P y
    program now          = ⌈ now P.refl ⌉
    program (later  x≈y) = later (♯ x≈y)
    program (laterˡ x≈y) = _ ≳⟪ laterˡ (_ □) ⟫ program x≈y

    _>>=W_ : ∀ {x₁ x₂ f₁ f₂} →
             x₁ ≈W x₂ → (∀ x → f₁ x ≈W f₂ x) →
             (x₁ >>= f₁) ≈W (x₂ >>= f₂)
    now {v = nothing} >>=W f₁≈f₂ = now
    now {v = just v } >>=W f₁≈f₂ = f₁≈f₂ v
    later  x≈y        >>=W f₁≈f₂ = later (x≈y >>=P (program ∘ f₁≈f₂))
    laterˡ x≈y        >>=W f₁≈f₂ = laterˡ (x≈y >>=W f₁≈f₂)

    _⟦·⟧W_ : ∀ {v₁₁ v₁₂ v₂₁ v₂₂} →
             v₁₁ ≈W v₂₁ → v₁₂ ≈W v₂₂ → v₁₁ ⟦·⟧ v₁₂ ≈W v₂₁ ⟦·⟧ v₂₂
    v₁₁≈v₂₁ ⟦·⟧W v₁₂≈v₂₂ =
      v₁₁≈v₂₁ >>=W λ v₁ →
      v₁₂≈v₂₂ >>=W λ v₂ →
      ⌈ ⟪ v₁ ∙ v₂ ⟫P □ ⌉W
      where open F.Workaround

    trans≅≈W : ∀ {x y z} → x ≅ y → y ≈W z → x ≈W z
    trans≅≈W (now P.refl) now           = now
    trans≅≈W (later  x≅y) (later  y≈z)  = later (_ ≅⟪ ♭ x≅y ⟫ y≈z)
    trans≅≈W (later  x≅y) (laterˡ y≈z)  = laterˡ (trans≅≈W (♭ x≅y) y≈z)

    trans≳≈W : ∀ {x y z} → x ≳ y → y ≈W z → x ≈W z
    trans≳≈W (now P.refl) now           = now
    trans≳≈W (later  x≳y) (later  y≈z)  = later (_ ≳⟪ ♭ x≳y ⟫ y≈z)
    trans≳≈W (later  x≳y) (laterˡ y≈z)  = laterˡ (trans≳≈W (♭ x≳y) y≈z)
    trans≳≈W (laterˡ x≳y)         y≈z   = laterˡ (trans≳≈W x≳y y≈z)

    trans≈W≅ : ∀ {x y z} → x ≈W y → y ≅ z → x ≈W z
    trans≈W≅ now          (now P.refl) = now
    trans≈W≅ (later  x≈y) (later y≅z)  = later (_ ≈⟪ x≈y ⟫ ♭ y≅z)
    trans≈W≅ (laterˡ x≈y)        y≅z   = laterˡ (trans≈W≅ x≈y y≅z)

    whnf : ∀ {x y} → x ≈P y → x ≈W y
    whnf ⌈ x≳y ⌉                = ⌈ x≳y ⌉W
    whnf (later x≈y)            = later (♭ x≈y)
    whnf (x₁≈x₂ >>=P f₁≈f₂)     = whnf x₁≈x₂ >>=W λ v → whnf (f₁≈f₂ v)
    whnf (v₁₁≈v₂₁ ⟦·⟧P v₁₂≈v₂₂) = whnf v₁₁≈v₂₁ ⟦·⟧W whnf v₁₂≈v₂₂
    whnf (x ≅⟪ x≅y ⟫ y≈z)       = trans≅≈W x≅y (whnf y≈z)
    whnf (x ≳⟪ x≳y ⟫ y≈z)       = trans≳≈W x≳y (whnf y≈z)
    whnf (x ≈⟪ x≈y ⟫ y≅z)       = trans≈W≅ (whnf x≈y) y≅z

  mutual

    -- Note that the types of soundW and soundP could be strengthened:
    -- laterʳ is not used.

    private

      soundW : ∀ {x y} → x ≈W y → x ≈ y
      soundW now          = now P.refl
      soundW (later  x≈y) = later (♯ soundP x≈y)
      soundW (laterˡ x≈y) = laterˡ (soundW x≈y)

    soundP : ∀ {x y} → x ≈P y → x ≈ y
    soundP x≈y = soundW (whnf x≈y)

  -- The language _≈P_ does not include transitivity for weak
  -- equality, as this would be unsound. Instead some limited notions
  -- of transitivity are used:
  --
  -- • Weak equality can be combined with strong equality on both
  --   sides.
  --
  -- • The _≳_ order can be "prepended" to a weak equality. Note that
  --   results of the form "x ≈ return v" can be turned into
  --   "x ≳ return v", as in the first case below.

  complete⇑ : ∀ {n} {t : Tm n} {ρ : Env n} →
              ρ ⊢ t ⇑ → ⟦ t ⟧ ρ ≈P never
  complete⇑ {ρ = ρ} (app {t₁} {t₂} {t = t} {ρ′} {v} t₁⇓ t₂⇓ t₁t₂⇑) =
    ⟦ t₁ · t₂ ⟧ ρ                 ≅⟪ ·-comp t₁ t₂ ⟫
    ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ         ≳⟪ Partiality.now⇒now (complete⇓ t₁⇓) ⟦·⟧-cong
                                     Partiality.now⇒now (complete⇓ t₂⇓) ⟫
    return (ƛ t ρ′) ⟦·⟧ return v  ≅⟪ later (♯ (_ □)) ⟫
    later (♯ ⟦ t ⟧ (v ∷ ρ′))      ≈⟪ later (♯ complete⇑ (♭ t₁t₂⇑)) ⟫
    never                         □
  complete⇑ {ρ = ρ} (·ˡ {t₁} {t₂} t₁⇑) =
    ⟦ t₁ · t₂ ⟧ ρ          ≅⟪ ·-comp t₁ t₂ ⟫
    ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ  ≈⟪ complete⇑ (♭ t₁⇑) ⟦·⟧P ⌈ ⟦ t₂ ⟧ ρ □ ⌉ ⟫
    never ⟦·⟧ ⟦ t₂ ⟧ ρ     ≅⟨ Partiality.left-zero _ ⟩
    never                  □
  complete⇑ {ρ = ρ} (·ʳ {t₁} {t₂} {v} t₁⇓ t₂⇑) =
    ⟦ t₁ · t₂ ⟧ ρ          ≅⟪ ·-comp t₁ t₂ ⟫
    ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ  ≈⟪ ⌈ Partiality.now⇒now (complete⇓ t₁⇓) ⌉ ⟦·⟧P complete⇑ (♭ t₂⇑) ⟫
    return v ⟦·⟧ never     ≅⟨ Partiality.left-zero _ ⟩
    never                  □

complete⇑ : ∀ {n} {t : Tm n} {ρ : Env n} →
            ρ ⊢ t ⇑ → ⟦ t ⟧ ρ ≈ never
complete⇑ = Complete⇑.soundP ∘ Complete⇑.complete⇑

-- The functional semantics is sound for non-terminating computations.
-- I assume excluded middle here because double-negation elimination
-- is used "infinitely often".

sound⇑ : ExcludedMiddle Level.zero →
         ∀ {n} (t : Tm n) {ρ : Env n} →
         ⟦ t ⟧ ρ ≈ never → ρ ⊢ t ⇑
sound⇑ em (con i)       i⇑    = ⊥-elim (Partiality.now≉never i⇑)
sound⇑ em (var x)       x⇑    = ⊥-elim (Partiality.now≉never x⇑)
sound⇑ em (ƛ t)         ƛ⇑    = ⊥-elim (Partiality.now≉never ƛ⇑)
sound⇑ em (t₁ · t₂) {ρ} t₁t₂⇑
  with decidable-stable em $ >>=-inversion-⇑ (⟦ t₁ ⟧ ρ) (
         ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ  ≅⟨ sym $ ·-comp t₁ t₂ ⟩
         ⟦ t₁ · t₂ ⟧ ρ          ≈⟨ t₁t₂⇑ ⟩
         never                  □)
sound⇑ em (t₁ · t₂)     ⇑ | inj₁ t₁⇑               = ·ˡ (♯ sound⇑ em t₁ t₁⇑)
sound⇑ em (t₁ · t₂) {ρ} ⇑ | inj₂ (v₁ , t₁⇓ , t₂∙⇑)
  with decidable-stable em $ >>=-inversion-⇑ (⟦ t₂ ⟧ ρ) t₂∙⇑
sound⇑ em (t₁ · t₂) ⇑ | inj₂ (v₁    , t₁⇓ , t₂∙⇑) | inj₁ t₂⇑             = ·ʳ (sound⇓ t₁ t₁⇓) (♯ sound⇑ em t₂ t₂⇑)
sound⇑ em (t₁ · t₂) ⇑ | inj₂ (con i , t₁⇓ , t₂∙⇑) | inj₂ (v₂ , t₂⇓ , ∙⇑) = ⊥-elim (Partiality.now≉never ∙⇑)
sound⇑ em (t₁ · t₂) ⇑ | inj₂ (ƛ t _ , t₁⇓ , t₂∙⇑) | inj₂ (v₂ , t₂⇓ , ∙⇑) =
  app (sound⇓ t₁ t₁⇓) (sound⇓ t₂ t₂⇓) (♯ sound⇑ em t (Partiality.later⁻¹ ∙⇑))

------------------------------------------------------------------------
-- Crashing computations

-- The functional semantics is complete for crashing computations.

complete↯ : ExcludedMiddle Level.zero →
            ∀ {n} (t : Tm n) (ρ : Env n) →
            ρ ⊢ t ↯ → ⟦ t ⟧ ρ ≈ fail
complete↯ em t ρ ¬⇒
  with decidable-stable em $
         Partiality.now-or-never {_∼_ = _≡_} P.refl
                                 {k = Partiality.weak} (⟦ t ⟧ ρ)
... | inj₂ t⇑             = ⊥-elim (proj₂ ¬⇒ (sound⇑ em t t⇑))
... | inj₁ (nothing , t↯) = t↯
... | inj₁ (just v  , t⇓) = ⊥-elim (proj₁ ¬⇒ (-, sound⇓ t t⇓))

-- The functional semantics is sound for crashing computations.

sound↯ : ∀ {n} {t : Tm n} {ρ : Env n} →
         ⟦ t ⟧ ρ ≈ fail → ρ ⊢ t ↯
sound↯ {t = t} {ρ} t↯ = (¬⇓ , ¬⇑)
  where
  ¬⇓ : ¬ (∃ λ v → ρ ⊢ t ⇓ v)
  ¬⇓ (v , t⇓) with
    fail      ≈⟨ sym t↯ ⟩
    ⟦ t ⟧ ρ   ≈⟨ complete⇓ t⇓ ⟩
    return v  □
  ... | now ()

  ¬⇑ : ¬ (ρ ⊢ t ⇑)
  ¬⇑ t⇑ = Partiality.now≉never (
    fail     ≈⟨ sym t↯ ⟩
    ⟦ t ⟧ ρ  ≈⟨ complete⇑ t⇑ ⟩
    never    □)


{-# OPTIONS --without-K #-}
module Model.Term where

open import Cats.Category

open import Model.Size as MS using (_<_ ; ⟦_⟧Δ ; ⟦_⟧n ; ⟦_⟧σ)
open import Model.Type as MT
open import Util.HoTT.Equiv
open import Util.Prelude hiding (id ; _∘_ ; _×_)
open import Source.Size as SS using (v0 ; v1 ; ⋆)
open import Source.Size.Substitution.Theory
open import Source.Size.Substitution.Universe as SU using (Sub⊢ᵤ)
open import Source.Term

import Model.RGraph as RG
import Source.Type as ST

open Category._≅_
open MS.Size
open MS._<_
open MS._≤_
open RG._⇒_
open SS.Size
open SS.Ctx
open ST.Ctx

⟦_⟧x : ∀ {Δ Γ x T}
  → Δ , Γ ⊢ₓ x ∶ T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T
⟦_⟧x {Γ = Γ ∙ T} zero = π₂ ⟦ Γ ⟧Γ
⟦ suc {U = U} x ⟧x = ⟦ x ⟧x ∘ π₁ ⟦ U ⟧T

⟦abs⟧ : ∀ Δ (Γ : ST.Ctx Δ) T U
  → ⟦ Γ ∙ T ⟧Γ ⇒ ⟦ U ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T ↝ ⟦ U ⟧T
⟦abs⟧ Δ Γ T U t = curry ⟦ Γ ⟧Γ ⟦ T ⟧T ⟦ U ⟧T t

⟦app⟧ : ∀ Δ (Γ : ST.Ctx Δ) T U
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T ↝ ⟦ U ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ U ⟧T
⟦app⟧ Δ Γ T U t u = eval ⟦ T ⟧T ⟦ U ⟧T ∘ ⟨ t , u ⟩

⟦absₛ⟧ : ∀ Δ n (Γ : ST.Ctx Δ) (T : ST.Type (Δ ∙ n))
  → ⟦ Γ [ SU.Wk ]ᵤ ⟧Γ ⇒ ⟦ T ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Π n , T ⟧T
⟦absₛ⟧ Δ n Γ T t = MT.absₛ (t ∘ ⟦subΓ⟧ SU.Wk Γ .back)

⟦appₛ⟧ : ∀ Δ m n (Γ : ST.Ctx Δ) T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Π n , T ⟧T
  → m SS.< n
  → ⟦ Γ ⟧Γ ⇒ ⟦ T [ SU.Sing m ]ᵤ ⟧T
⟦appₛ⟧ Δ m n Γ T t m<n = ⟦subT⟧ (SU.Sing m<n) T .back ∘ MT.appₛ m<n t

⟦zero⟧ : ∀ Δ (Γ : ST.Ctx Δ) n
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat n ⟧T
⟦zero⟧ Δ Γ n = record
  { fobj = λ {δ} γ → zero≤ (⟦ n ⟧n .fobj δ)
  ; feq = λ δ≈δ′ x≈y → refl
  }

⟦suc⟧ : ∀ Δ (Γ : ST.Ctx Δ) {m n}
  → m SS.< n
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat m ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat n ⟧T
⟦suc⟧ Δ Γ m<n i = record
  { fobj = λ γ → suc≤ _ _ (MS.⟦<⟧ m<n) (i .fobj γ)
  ; feq = λ δ≈δ′ x≈y → cong suc (i .feq _ x≈y)
  }

⟦caseNat⟧ : ∀ Δ (Γ : ST.Ctx Δ) n T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat n ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Π n , ST.Nat v0 ST.⇒ T [ SU.Wk ]ᵤ ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T
⟦caseNat⟧ Δ Γ n T i z s = record
  { fobj = λ γ → caseℕ≤ (i .fobj γ) (z .fobj γ)
      λ m m<n i
        → ⟦subT⟧ SU.Wk T .forth .fobj
            (s .fobj γ .arr m m<n .fobj i)
  ; feq = λ {δ δ′} δ≈δ′ {γ γ′} γ≈γ′
      → caseℕ≤-pres (⟦ T ⟧T .eq δ≈δ′) (i .fobj γ) (i .fobj γ′)
          (z .fobj γ) (z .fobj γ′) _ _ (i .feq _ γ≈γ′)
          (z .feq _ γ≈γ′)
          λ m m<n m′ m′<n′ j j′ j≡j′
            → ⟦subT⟧ SU.Wk T .forth .feq _
                (s .feq _ γ≈γ′ m m<n m′ m′<n′ j≡j′)
  }

⟦cons⟧ : ∀ Δ (Γ : ST.Ctx Δ) n
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat ∞ ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Π n , ST.Stream v0 ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Stream n ⟧T
⟦cons⟧ Δ Γ n i is = record
  { fobj = λ γ → MT.cons (i .fobj γ .proj₁) (is .fobj γ .arr)
  ; feq = λ δ≈δ′ γ≈γ′ k k≤nδ k≤nδ′
      → cons-≡⁺ (i .feq _ γ≈γ′)
          (λ m m<n m<n′ k k≤m → is .feq _ γ≈γ′ m m<n m m<n′ k k≤m k≤m)
          k k≤nδ k≤nδ′
  }

⟦head⟧ : ∀ Δ (Γ : ST.Ctx Δ) n
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Stream n ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Nat ∞ ⟧T
⟦head⟧ Δ Γ n is = record
  { fobj = λ γ → MT.head (is .fobj γ) , MS.<→≤ MS.zero<∞
  ; feq = λ δ≈δ′ γ≈γ′ → head-≡⁺ (is .feq _ γ≈γ′)
  }

⟦tail⟧ : ∀ Δ (Γ : ST.Ctx Δ) {m n}
  → m SS.< n
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Stream n ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Stream m ⟧T
⟦tail⟧ Δ Γ m<n is = record
  { fobj = λ γ → MT.tail (is .fobj γ) _ (MS.⟦<⟧ m<n)
  ; feq = λ δ≈δ′ γ≈γ′ i i≤mδ i≤mδ′
      → tail-≡⁺ (is .feq _ γ≈γ′) _ _ (MS.⟦<⟧ m<n) (MS.⟦<⟧ m<n) i i≤mδ i≤mδ′
  }

⟦fix⟧ : ∀ Δ (Γ : ST.Ctx Δ) n T
  → n SS.< ⋆
  → ⟦ Γ ⟧Γ ⇒ ⟦ ST.Π ⋆ , (ST.Π v0 , T [ SU.Skip ]ᵤ) ST.⇒ T ⟧T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T [ SU.Sing n ]ᵤ ⟧T
⟦fix⟧ Δ Γ n T n<⋆ t = ⟦subT⟧ (SU.Sing n<⋆) T .back ∘ term⇒
  module ⟦fix⟧ where
    go
      : Σ[ f ∈ (∀ n n<⋆ δ → ⟦ Γ ⟧Γ .Obj δ → ⟦ T ⟧T .Obj (δ , n , n<⋆)) ]
        Σ[ f-param ∈ (∀ n n′ n<⋆ n′<⋆ δ δ′ (γ : ⟦ Γ ⟧Γ .Obj δ) (γ′ : ⟦ Γ ⟧Γ .Obj δ′)
          → ⟦ Γ ⟧Γ .eq _ γ γ′ → ⟦ T ⟧T .eq _ (f n n<⋆ δ γ) (f n′ n′<⋆ δ′ γ′)) ]
          (∀ {n} → f n ≡ _)
    go = MS.<-indΣ′
      (λ n → ∀ n<⋆ δ (γ : ⟦ Γ ⟧Γ .Obj δ) → ⟦ T ⟧T .Obj (δ , n , n<⋆))
      (λ n m f g → ∀ n<⋆ m<⋆ δ δ′ γ γ′ (γ≈γ′ : ⟦ Γ ⟧Γ .eq _ γ γ′)
        → ⟦ T ⟧T .eq _ (f n<⋆ δ γ) (g m<⋆ δ′ γ′))
      (λ n rec rec-resp n<⋆ δ γ
        → t .fobj γ .arr n n<⋆ .fobj
            (⟦∀⟧′-resp-≈⟦Type⟧ (⟦subT⟧ SU.Skip T) .back record
              { arr = λ m m<n → rec m m<n (MS.<-trans m<n n<⋆) δ γ
              ; param = λ m m<n m′ m′<n
                  → rec-resp m m<n m′ m′<n _ _ δ δ γ γ (⟦ Γ ⟧Γ .eq-refl γ)
              }))
      λ n g g-resp m h h-resp g≈h n<⋆ m<⋆ δ δ′ γ γ′ γ≈γ′
        → t .feq _ γ≈γ′ n n<⋆ m m<⋆ λ k k<n k′ k′<m
            → ⟦subT⟧ SU.Skip T .back .feq _
                (g≈h k k<n k′ k′<m _ _ δ δ′ γ γ′ γ≈γ′)

    term : ∀ n n<⋆ δ → ⟦ Γ ⟧Γ .Obj δ → ⟦ T ⟧T .Obj (δ , n , n<⋆)
    term = go .proj₁

    term-param : ∀ n n′ n<⋆ n′<⋆ δ δ′ (γ : ⟦ Γ ⟧Γ .Obj δ) (γ′ : ⟦ Γ ⟧Γ .Obj δ′)
      → ⟦ Γ ⟧Γ .eq _ γ γ′
      → ⟦ T ⟧T .eq _ (term n n<⋆ δ γ) (term n′ n′<⋆ δ′ γ′)
    term-param = go .proj₂ .proj₁

    term⇒ : ⟦ Γ ⟧Γ ⇒ subT ⟦ SU.Sing n<⋆ ⟧σ ⟦ T ⟧T
    term⇒ = record
      { fobj = term _ _ _
      ; feq = λ δ≈δ′ → term-param _ _ _ _ _ _ _ _
      }

    term-unfold₀ : ∀ {n}
      → term n
      ≡ λ n<⋆ δ γ → t .fobj γ .arr n n<⋆ .fobj
            (⟦∀⟧′-resp-≈⟦Type⟧ (⟦subT⟧ SU.Skip T) .back record
              { arr = λ m m<n → term m (MS.<-trans m<n n<⋆) δ γ
              ; param = λ m m<n m′ m′<n
                  → term-param _ _ _ _ _ _ _ _ (⟦ Γ ⟧Γ .eq-refl γ)
              })
    term-unfold₀ = go .proj₂ .proj₂

    term-unfold : ∀ {n n<⋆ δ γ}
      → term n n<⋆ δ γ
      ≡ t .fobj γ .arr n n<⋆ .fobj
          (⟦∀⟧′-resp-≈⟦Type⟧ (⟦subT⟧ SU.Skip T) .back record
            { arr = λ m m<n → term m (MS.<-trans m<n n<⋆) δ γ
            ; param = λ m m<n m′ m′<n
                → term-param _ _ _ _ _ _ _ _ (⟦ Γ ⟧Γ .eq-refl γ)
            })
    term-unfold {n} {n<⋆} {δ} {γ} = cong (λ f → f n<⋆ δ γ) term-unfold₀

⟦_⟧t : ∀ {Δ Γ t T}
  → Δ , Γ ⊢ t ∶ T
  → ⟦ Γ ⟧Γ ⇒ ⟦ T ⟧T
⟦ var ⊢x ⟧t = ⟦ ⊢x ⟧x
⟦ abs {Δ = Δ} {Γ} {T} {t} {U} ⊢t ⟧t = ⟦abs⟧ Δ Γ T U ⟦ ⊢t ⟧t
⟦ app {Δ} {Γ} {T = T} {U = U} ⊢t ⊢u ⟧t = ⟦app⟧ Δ Γ T U ⟦ ⊢t ⟧t ⟦ ⊢u ⟧t
⟦ absₛ {Δ} {n} {T = T} ⊢t refl ⟧t = ⟦absₛ⟧ Δ n _ T ⟦ ⊢t ⟧t
⟦ appₛ {Δ} {m} {n} {Γ} {T = T} m<n ⊢t refl ⟧t = ⟦appₛ⟧ Δ m n Γ T ⟦ ⊢t ⟧t m<n
⟦ zero {Δ} {n} {Γ} n<⋆ ⟧t = ⟦zero⟧ Δ Γ n
⟦ suc {Δ} {Γ = Γ} n<⋆ m<n ⊢i ⟧t = ⟦suc⟧ Δ Γ m<n ⟦ ⊢i ⟧t
⟦ cons {Δ} {n} {Γ} n<⋆ ⊢i ⊢is ⟧t = ⟦cons⟧ Δ Γ n ⟦ ⊢i ⟧t ⟦ ⊢is ⟧t
⟦ head {Δ} {n} {Γ} n<⋆ ⊢is ⟧t = ⟦head⟧ Δ Γ n ⟦ ⊢is ⟧t
⟦ tail {Δ} {n} {m} {Γ} n<⋆ m<n ⊢is ⟧t = ⟦tail⟧ Δ Γ m<n ⟦ ⊢is ⟧t
⟦ caseNat {Δ} {n} {Γ} {T = T} n<⋆ ⊢i ⊢z ⊢s refl ⟧t
  = ⟦caseNat⟧ Δ Γ n T ⟦ ⊢i ⟧t ⟦ ⊢z ⟧t ⟦ ⊢s ⟧t
⟦ fix {Δ} {n} {Γ} {T = T} n<⋆ ⊢t refl refl ⟧t
  = ⟦fix⟧ Δ Γ n T n<⋆ ⟦ ⊢t ⟧t

⟦_⟧ν : ∀ {Δ} {Γ Ψ : ST.Ctx Δ} {ν} → ν ∶ Γ ⇛ Ψ → ⟦ Γ ⟧Γ ⇒ ⟦ Ψ ⟧Γ
⟦ [] ⟧ν = ! _
⟦ Snoc ⊢ν ⊢t ⟧ν = ⟨ ⟦ ⊢ν ⟧ν , ⟦ ⊢t ⟧t ⟩


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Logical relation for erasure with the Nehemiah plugin.
------------------------------------------------------------------------

module Nehemiah.Change.Implementation where

open import Nehemiah.Syntax.Type
open import Nehemiah.Syntax.Term
open import Nehemiah.Denotation.Value
open import Nehemiah.Denotation.Evaluation
open import Nehemiah.Change.Validity
open import Nehemiah.Change.Type
open import Nehemiah.Change.Value
open import Nehemiah.Change.Derive

open import Relation.Binary.PropositionalEquality
open import Data.Unit
open import Data.Product
open import Data.Integer
open import Structure.Bag.Nehemiah

import Parametric.Change.Implementation
    Const ⟦_⟧Base ⟦_⟧Const ΔBase
    ⟦apply-base⟧ ⟦diff-base⟧ ⟦nil-base⟧ derive-const as Implementation

private
  implements-base : ∀ ι {v : ⟦ ι ⟧Base} → Δ₍ ι ₎ v → ⟦ ΔBase ι ⟧Type → Set
  implements-base base-int {v} Δv Δv′ = Δv ≡ Δv′
  implements-base base-bag {v} Δv Δv′ = Δv ≡ Δv′

  u⊟v≈u⊝v-base : ∀ ι → {u v : ⟦ ι ⟧Base} →
      implements-base ι {v} (u ⊟₍ ι ₎ v) (⟦diff-base⟧ ι u v)
  u⊟v≈u⊝v-base base-int = refl
  u⊟v≈u⊝v-base base-bag = refl

  nil-v≈⟦nil⟧-v-base : ∀ ι {v : ⟦ ι ⟧Base} →
    implements-base ι (nil₍ ι ₎ v) (⟦nil-base⟧ ι v)
  nil-v≈⟦nil⟧-v-base base-int = refl
  nil-v≈⟦nil⟧-v-base base-bag = refl

  carry-over-base : ∀ {ι}
    {v : ⟦ ι ⟧Base}
    (Δv : Δ₍ ι ₎ v)
    {Δv′ : ⟦ ΔBase ι ⟧Type} (Δv≈Δv′ : implements-base ι {v} Δv Δv′) →
      v ⊞₍ base ι ₎ Δv ≡ v ⟦⊕₍ base ι ₎⟧ Δv′
  carry-over-base {base-int} {v} Δv Δv≈Δv′ = cong (_+_ v) Δv≈Δv′
  carry-over-base {base-bag} Δv Δv≈Δv′ = cong (_++_ (before₍ bag ₎ Δv)) Δv≈Δv′

implementation-structure : Implementation.Structure
implementation-structure = record
    { implements-base = implements-base
    ; u⊟v≈u⊝v-base = u⊟v≈u⊝v-base
    ; nil-v≈⟦nil⟧-v-base = nil-v≈⟦nil⟧-v-base
    ; carry-over-base = carry-over-base
    }

open Implementation.Structure implementation-structure public


{-# OPTIONS --cubical #-}

module Issue4606 where

open import Agda.Builtin.Cubical.Path
open import Agda.Primitive.Cubical

-- Test case from Andrew Pitts.
----------------------------------------------------------------------
-- Well-Founded Recursion
----------------------------------------------------------------------
data Acc {A : Set}(R : A → A → Set)(x : A) : Set where
  acc : (∀ y → R y x → Acc R y) → Acc R x

iswf : {A : Set}(R : A → A → Set) → Set
iswf R = ∀ x → Acc R x

----------------------------------------------------------------------
-- A logical contradiction
----------------------------------------------------------------------
data A : Set where
  O : A
  S : A → A
  E : ∀ x → S x ≡ x

data _<_ (x : A) : A → Set where
  <S : x < S x

O<O : O < O
O<O = primTransp (\ i →  O < E O i) i0 <S

-- Given that O < O holds, we should not be able to prove that < is
-- well-founded, but we can!
iswf< : iswf _<_
iswf< i = acc (α i)
  where
  α : ∀ x y → y < x → Acc _<_ y
 -- I don't think the following clause should pass
 -- the termination checker
  α .(S y) y <S = acc (α y)

data ⊥ : Set where

AccO→⊥ : Acc _<_ O → ⊥
AccO→⊥ (acc f) = AccO→⊥ (f O O<O)

contradiction : ⊥
contradiction = AccO→⊥ (iswf< O)


-- Andreas, 2015-09-18 Andreas, reported by Andrea
-- {-# OPTIONS -v impossible:10 #-}
-- {-# OPTIONS -v tc.cover.splittree:10 #-}
-- {-# OPTIONS -v tc.cc:30 #-}

open import Common.Bool
open import Common.Product
open import Common.Equality

postulate
 A : Set
 a : A

Works : A × A → Bool → A
Works (x , y) true = y
Works d false = a

works : ∀{x y} → Works (x , y) false ≡ a
works = refl

Test : A × (A × A) → Bool → A
Test (x , (y , z)) true = z
Test d false = a

test : ∀{x y z} → Test (x , (y , z)) false ≡ a
test = refl

-- ERROR WAS:
-- An internal error has occurred. Please report this as a bug.
-- Location of the error: src/full/Agda/TypeChecking/CompiledClause/Match.hs:189

-- Behavior of clause compiler WAS:
-- splitting on 0 after expandCatchAlls True:
--   (x,(y,z)) true -> z
--   (_,_)     false -> a
--   _         false -> a
-- splitting on 1 after expandCatchAlls False:
--   d false -> a
-- splitting on 1 after expandCatchAlls False:  <== NOW: True
--   x (y,z) true  -> z
--   _ (_,_) false -> a  <== WAS MISSING
--   _ _     false -> a
-- splitting on 2 after expandCatchAlls False:
--   _ _ false -> a
-- splitting on 3 after expandCatchAlls False:
--   x y z true  -> z
--   x y z false -> a  <== WAS MISSING


------------------------------------------------------------------------
-- Representation-independent results for non-dependent lenses
------------------------------------------------------------------------

import Equality.Path as P

module Lens.Non-dependent
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq

open import Logical-equivalence using (_⇔_)
open import Prelude

open import Bijection equality-with-J as Bij using (module _↔_)
open import Equality.Decision-procedures equality-with-J
open import Equivalence equality-with-J using (_≃_)
open import Erased.Cubical eq
open import Function-universe equality-with-J as F hiding (_∘_)
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq as T using (∥_∥)
open import Surjection equality-with-J using (_↠_)

private
  variable
    a b c c₁ c₂ c₃ l  : Level
    A B               : Type a
    Lens₁ Lens₂ Lens₃ : Type a → Type b → Type c

------------------------------------------------------------------------
-- Some existence results

-- There is, in general, no lens for the first projection from a
-- Σ-type, assuming that lenses with contractible domains have
-- contractible codomains.

no-first-projection-lens :
  (Lens : Type → Type → Type a) →
  @0 (∀ {A B} → Lens A B → Contractible A → Contractible B) →
  ¬ Lens (∃ λ (b : Bool) → b ≡ true) Bool
no-first-projection-lens _ contractible-to-contractible l =
  _↔_.to Erased-⊥↔⊥
    [                                $⟨ singleton-contractible _ ⟩
      Contractible (Singleton true)  ↝⟨ contractible-to-contractible l ⟩
      Contractible Bool              ↝⟨ mono₁ 0 ⟩
      Is-proposition Bool            ↝⟨ ¬-Bool-propositional ⟩□
      ⊥                              □
    ]

-- A variant of the previous result: If A is merely inhabited, and one
-- can "project" out a boolean from a value of type A, but this
-- boolean is necessarily true, then there is no lens corresponding to
-- this projection (if the get-set law holds).

no-singleton-projection-lens :
  {Lens : Type l}
  (get : Lens → A → Bool)
  (set : Lens → A → Bool → A)
  (get-set : ∀ l a b → get l (set l a b) ≡ b) →
  ∥ A ∥ →
  (bool : A → Bool) →
  (∀ x → bool x ≡ true) →
  ¬ ∃ λ (l : Lens) →
      ∀ x → get l x ≡ bool x
no-singleton-projection-lens
  get set get-set x bool is-true (l , get≡bool) =
  _↔_.to Erased-⊥↔⊥
    [ (flip (T.rec ⊥-propositional) x λ x →
       Bool.true≢false
         (true                   ≡⟨ sym $ is-true _ ⟩
          bool (set l x false)   ≡⟨ sym $ get≡bool _ ⟩
          get l (set l x false)  ≡⟨ get-set _ _ _ ⟩∎
          false                  ∎))
    ]

------------------------------------------------------------------------
-- Statements of preservation results, and some related lemmas

-- Lens-like things with getters and setters.

record Has-getter-and-setter
         (Lens : Type a → Type b → Type c) :
         Type (lsuc (a ⊔ b ⊔ c)) where
  field
    -- Getter.
    get : {A : Type a} {B : Type b} → Lens A B → A → B

    -- Typeter.
    set : {A : Type a} {B : Type b} → Lens A B → A → B → A

-- A statement of what it means for two lenses to have the same getter
-- and setter.

Same-getter-and-setter :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  {A : Type a} {B : Type b} →
  Lens₁ A B → Lens₂ A B → Type (a ⊔ b)
Same-getter-and-setter ⦃ L₁ = L₁ ⦄ ⦃ L₂ = L₂ ⦄ l₁ l₂ =
  get L₁ l₁ ≡ get L₂ l₂ ×
  set L₁ l₁ ≡ set L₂ l₂
  where
  open Has-getter-and-setter

-- A statement of what it means for a function to preserve getters and
-- setters for all inputs.

Preserves-getters-and-setters-→ :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  (A : Type a) (B : Type b) →
  (Lens₁ A B → Lens₂ A B) →
  Type (a ⊔ b ⊔ c₁)
Preserves-getters-and-setters-→ {Lens₁ = Lens₁} A B f =
  (l : Lens₁ A B) → Same-getter-and-setter (f l) l

-- A statement of what it means for a logical equivalence to preserve
-- getters and setters.

Preserves-getters-and-setters-⇔ :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  (A : Type a) (B : Type b) →
  (Lens₁ A B ⇔ Lens₂ A B) →
  Type (a ⊔ b ⊔ c₁ ⊔ c₂)
Preserves-getters-and-setters-⇔ A B eq =
  Preserves-getters-and-setters-→ A B (_⇔_.to eq) ×
  Preserves-getters-and-setters-→ A B (_⇔_.from eq)

-- Composition preserves Preserves-getters-and-setters-→.

Preserves-getters-and-setters-→-∘ :
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  ⦃ L₃ : Has-getter-and-setter Lens₃ ⦄
  {f : Lens₂ A B → Lens₃ A B}
  {g : Lens₁ A B → Lens₂ A B} →
  Preserves-getters-and-setters-→ A B f →
  Preserves-getters-and-setters-→ A B g →
  Preserves-getters-and-setters-→ A B (f ∘ g)
Preserves-getters-and-setters-→-∘ p-f p-g _ =
    trans (proj₁ (p-f _)) (proj₁ (p-g _))
  , trans (proj₂ (p-f _)) (proj₂ (p-g _))

-- Composition preserves Preserves-getters-and-setters-⇔.

Preserves-getters-and-setters-⇔-∘ :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  {Lens₃ : Type a → Type b → Type c₃}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  ⦃ L₃ : Has-getter-and-setter Lens₃ ⦄
  {f : Lens₂ A B ⇔ Lens₃ A B}
  {g : Lens₁ A B ⇔ Lens₂ A B} →
  Preserves-getters-and-setters-⇔ A B f →
  Preserves-getters-and-setters-⇔ A B g →
  Preserves-getters-and-setters-⇔ A B (f F.∘ g)
Preserves-getters-and-setters-⇔-∘ p-f p-g =
    Preserves-getters-and-setters-→-∘ (proj₁ p-f) (proj₁ p-g)
  , Preserves-getters-and-setters-→-∘ (proj₂ p-g) (proj₂ p-f)

-- The function inverse preserves Preserves-getters-and-setters-⇔.

Preserves-getters-and-setters-⇔-inverse :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  {f : Lens₁ A B ⇔ Lens₂ A B} →
  Preserves-getters-and-setters-⇔ A B f →
  Preserves-getters-and-setters-⇔ A B (inverse f)
Preserves-getters-and-setters-⇔-inverse = swap

-- If the forward direction of a split surjection preserves getters
-- and setters, then both directions do.

Preserves-getters-and-setters-→-↠-⇔ :
  {Lens₁ : Type a → Type b → Type c₁}
  {Lens₂ : Type a → Type b → Type c₂}
  ⦃ L₁ : Has-getter-and-setter Lens₁ ⦄
  ⦃ L₂ : Has-getter-and-setter Lens₂ ⦄
  (f : Lens₁ A B ↠ Lens₂ A B) →
  Preserves-getters-and-setters-→ A B (_↠_.to f) →
  Preserves-getters-and-setters-⇔ A B (_↠_.logical-equivalence f)
Preserves-getters-and-setters-→-↠-⇔ ⦃ L₁ = L₁ ⦄ ⦃ L₂ = L₂ ⦄ f p =
    p
  , λ l →
        (get L₁ (_↠_.from f l)             ≡⟨ sym $ proj₁ $ p (_↠_.from f l) ⟩
         get L₂ (_↠_.to f (_↠_.from f l))  ≡⟨ cong (get L₂) $ _↠_.right-inverse-of f _ ⟩∎
         get L₂ l                          ∎)
      , (set L₁ (_↠_.from f l)             ≡⟨ sym $ proj₂ $ p (_↠_.from f l) ⟩
         set L₂ (_↠_.to f (_↠_.from f l))  ≡⟨ cong (set L₂) $ _↠_.right-inverse-of f _ ⟩∎
         set L₂ l                          ∎)
  where
  open Has-getter-and-setter


-- Andreas, 2017-01-05, issue #2376
-- Ensure correct printing for termination error

-- {-# OPTIONS -v term.clause:30 #-}

postulate
  id : ∀{a}{A : Set a} → A → A
  A : Set
  P : A → Set

f : {a : A} → P a → {b : A} → P b → Set
f = λ p → f (id p)  -- id to prevent eta-contration before termination checking

-- Expected termination error:
-- ...
-- Problematic calls:
--   f (id p)


------------------------------------------------------------------------
-- Semantics of the simplified parsers
------------------------------------------------------------------------

module StructurallyRecursiveDescentParsing.Simplified.Semantics where

open import Algebra
open import Codata.Musical.Notation
open import Data.Bool
open import Data.List
import Data.List.Properties
private
  module LM {Tok : Set} = Monoid (Data.List.Properties.++-monoid Tok)
open import Data.Product as Prod
open import Function
open import Data.Empty
open import Relation.Binary.PropositionalEquality as P using (_≡_)

open import StructurallyRecursiveDescentParsing.Simplified
open import TotalParserCombinators.Parser using (○; ◌)
open import TotalParserCombinators.Semantics as Semantics
  using ([_-_]_>>=_) renaming (_∈_·_ to _∈′_·_)
open Semantics._∈_·_

------------------------------------------------------------------------
-- Semantics

-- The semantics of the parsers. x ∈ p · s means that x can be the
-- result of applying the parser p to the string s. Note that the
-- semantics is defined inductively.

infixl 10 _?>>=_ _!>>=_
infix   4 _∈_·_

data _∈_·_ {Tok} : ∀ {R e} → R → Parser Tok e R → List Tok → Set1 where
  return : ∀ {R} {x : R} → x ∈ return x · []
  token  : ∀ {x} → x ∈ token · [ x ]
  ∣ˡ     : ∀ {R x e₁ e₂ s} {p₁ : Parser Tok e₁ R} {p₂ : Parser Tok e₂ R}
           (x∈p₁ : x ∈ p₁ · s) → x ∈ p₁ ∣ p₂ · s
  ∣ʳ     : ∀ {R x e₂ s} e₁ {p₁ : Parser Tok e₁ R} {p₂ : Parser Tok e₂ R}
           (x∈p₂ : x ∈ p₂ · s) → x ∈ p₁ ∣ p₂ · s
  _?>>=_ : ∀ {R₁ R₂ x y e₂ s₁ s₂}
             {p₁ : Parser Tok true R₁} {p₂ : R₁ → Parser Tok e₂ R₂}
           (x∈p₁ : x ∈ p₁ · s₁) (y∈p₂x : y ∈ p₂ x · s₂) →
           y ∈ p₁ ?>>= p₂ · s₁ ++ s₂
  _!>>=_ : ∀ {R₁ R₂ x y} {e₂ : R₁ → Bool} {s₁ s₂}
             {p₁ : Parser Tok false R₁}
             {p₂ : (x : R₁) → ∞ (Parser Tok (e₂ x) R₂)}
           (x∈p₁ : x ∈ p₁ · s₁) (y∈p₂x : y ∈ ♭ (p₂ x) · s₂) →
           y ∈ p₁ !>>= p₂ · s₁ ++ s₂

------------------------------------------------------------------------
-- _∈_·_ is correct with respect to _∈′_·_

sound : ∀ {Tok e R} {p : Parser Tok e R} {x s} →
        x ∈ p · s → x ∈′ ⟦ p ⟧ · s
sound return              = return
sound token               = token
sound (∣ˡ x∈p₁)           = cast (∣-left               (sound x∈p₁))
sound (∣ʳ _ {p₁} x∈p₂)    = cast (∣-right (initial p₁) (sound x∈p₂))
sound (_?>>=_ {p₂ = p₂}
              x∈p₁ y∈p₂x) = cast ([_-_]_>>=_ ○ ○ {p₂ = λ x → ⟦ p₂ x ⟧}
                                             (sound x∈p₁) (sound y∈p₂x))
sound (x∈p₁ !>>= y∈p₂x)   = [ ○ - ◌ ] sound x∈p₁ >>= sound y∈p₂x

complete : ∀ {Tok e R} (p : Parser Tok e R) {x s} →
           x ∈′ ⟦ p ⟧ · s → x ∈ p · s
complete (return x)       return                   = return
complete fail             ()
complete token            token                    = token
complete (p₁ ∣ p₂)        (cast (∣-left     x∈p₁)) = ∣ˡ    (complete p₁ x∈p₁)
complete (_∣_ {e₁} p₁ p₂) (cast (∣-right ._ x∈p₂)) = ∣ʳ e₁ (complete p₂ x∈p₂)
complete (p₁ ?>>= p₂)     (cast (x∈p₁ >>= y∈p₂x))  = complete p₁ x∈p₁ ?>>= complete    (p₂ _)  y∈p₂x
complete (p₁ !>>= p₂)           (x∈p₁ >>= y∈p₂x)   = complete p₁ x∈p₁ !>>= complete (♭ (p₂ _)) y∈p₂x

------------------------------------------------------------------------
-- A lemma

-- A simple cast lemma.

cast∈ : ∀ {Tok e R} {p p′ : Parser Tok e R} {x x′ s s′} →
        x ≡ x′ → p ≡ p′ → s ≡ s′ → x ∈ p · s → x′ ∈ p′ · s′
cast∈ P.refl P.refl P.refl x∈ = x∈

------------------------------------------------------------------------
-- A variant of the semantics

-- The statement x ⊕ s₂ ∈ p · s means that there is some s₁ such that
-- s ≡ s₁ ++ s₂ and x ∈ p · s₁. This variant of the semantics is
-- perhaps harder to understand, but sometimes easier to work with
-- (and it is proved equivalent to the semantics above).

infix 4 _⊕_∈_·_

data _⊕_∈_·_ {Tok} : ∀ {R e} → R → List Tok →
                     Parser Tok e R → List Tok → Set1 where
  return : ∀ {R} {x : R} {s} → x ⊕ s ∈ return x · s
  token  : ∀ {x s} → x ⊕ s ∈ token · x ∷ s
  ∣ˡ     : ∀ {R x e₁ e₂ s s₁}
             {p₁ : Parser Tok e₁ R} {p₂ : Parser Tok e₂ R}
           (x∈p₁ : x ⊕ s₁ ∈ p₁ · s) → x ⊕ s₁ ∈ p₁ ∣ p₂ · s
  ∣ʳ     : ∀ {R x e₂ s s₁} e₁
             {p₁ : Parser Tok e₁ R} {p₂ : Parser Tok e₂ R}
           (x∈p₂ : x ⊕ s₁ ∈ p₂ · s) → x ⊕ s₁ ∈ p₁ ∣ p₂ · s
  _?>>=_ : ∀ {R₁ R₂ x y e₂ s s₁ s₂}
             {p₁ : Parser Tok true R₁} {p₂ : R₁ → Parser Tok e₂ R₂}
           (x∈p₁ : x ⊕ s₁ ∈ p₁ · s) (y∈p₂x : y ⊕ s₂ ∈ p₂ x · s₁) →
           y ⊕ s₂ ∈ p₁ ?>>= p₂ · s
  _!>>=_ : ∀ {R₁ R₂ x y} {e₂ : R₁ → Bool} {s s₁ s₂}
             {p₁ : Parser Tok false R₁}
             {p₂ : (x : R₁) → ∞ (Parser Tok (e₂ x) R₂)}
           (x∈p₁ : x ⊕ s₁ ∈ p₁ · s) (y∈p₂x : y ⊕ s₂ ∈ ♭ (p₂ x) · s₁) →
           y ⊕ s₂ ∈ p₁ !>>= p₂ · s

-- The definition is sound and complete with respect to the one above.

⊕-sound′ : ∀ {Tok R e x s₂ s} {p : Parser Tok e R} →
           x ⊕ s₂ ∈ p · s → ∃ λ s₁ → (s ≡ s₁ ++ s₂) × (x ∈ p · s₁)
⊕-sound′ return            = ([]    , P.refl , return)
⊕-sound′ {x = x} token     = ([ x ] , P.refl , token)
⊕-sound′ (∣ˡ x∈p₁)         with ⊕-sound′ x∈p₁
⊕-sound′ (∣ˡ x∈p₁)         | (s₁ , P.refl , x∈p₁′) = (s₁ , P.refl , ∣ˡ    x∈p₁′)
⊕-sound′ (∣ʳ e₁ x∈p₁)      with ⊕-sound′ x∈p₁
⊕-sound′ (∣ʳ e₁ x∈p₁)      | (s₁ , P.refl , x∈p₁′) = (s₁ , P.refl , ∣ʳ e₁ x∈p₁′)
⊕-sound′ (x∈p₁ ?>>= y∈p₂x) with ⊕-sound′ x∈p₁ | ⊕-sound′ y∈p₂x
⊕-sound′ (x∈p₁ ?>>= y∈p₂x) | (s₁ , P.refl , x∈p₁′) | (s₂ , P.refl , y∈p₂x′) =
                             (s₁ ++ s₂ , P.sym (LM.assoc s₁ s₂ _)
                                       , x∈p₁′ ?>>= y∈p₂x′)
⊕-sound′ (x∈p₁ !>>= y∈p₂x) with ⊕-sound′ x∈p₁ | ⊕-sound′ y∈p₂x
⊕-sound′ (x∈p₁ !>>= y∈p₂x) | (s₁ , P.refl , x∈p₁′) | (s₂ , P.refl , y∈p₂x′) =
                             (s₁ ++ s₂ , P.sym (LM.assoc s₁ s₂ _)
                                       , x∈p₁′ !>>= y∈p₂x′)

⊕-sound : ∀ {Tok R e x s} {p : Parser Tok e R} →
          x ⊕ [] ∈ p · s → x ∈ p · s
⊕-sound x∈p with ⊕-sound′ x∈p
⊕-sound x∈p | (s , P.refl , x∈p′) with s ++ [] | proj₂ LM.identity s
⊕-sound x∈p | (s , P.refl , x∈p′) | .s | P.refl = x∈p′

extend : ∀ {Tok R e x s s′ s″} {p : Parser Tok e R} →
         x ⊕ s′ ∈ p · s → x ⊕ s′ ++ s″ ∈ p · s ++ s″
extend return            = return
extend token             = token
extend (∣ˡ x∈p₁)         = ∣ˡ    (extend x∈p₁)
extend (∣ʳ e₁ x∈p₂)      = ∣ʳ e₁ (extend x∈p₂)
extend (x∈p₁ ?>>= y∈p₂x) = extend x∈p₁ ?>>= extend y∈p₂x
extend (x∈p₁ !>>= y∈p₂x) = extend x∈p₁ !>>= extend y∈p₂x

⊕-complete : ∀ {Tok R e x s} {p : Parser Tok e R} →
             x ∈ p · s → x ⊕ [] ∈ p · s
⊕-complete return            = return
⊕-complete token             = token
⊕-complete (∣ˡ x∈p₁)         = ∣ˡ    (⊕-complete x∈p₁)
⊕-complete (∣ʳ e₁ x∈p₂)      = ∣ʳ e₁ (⊕-complete x∈p₂)
⊕-complete (x∈p₁ ?>>= y∈p₂x) = extend (⊕-complete x∈p₁) ?>>=
                                       ⊕-complete y∈p₂x
⊕-complete (x∈p₁ !>>= y∈p₂x) = extend (⊕-complete x∈p₁) !>>=
                                       ⊕-complete y∈p₂x

⊕-complete′ : ∀ {Tok R e x s₂ s} {p : Parser Tok e R} →
              (∃ λ s₁ → s ≡ s₁ ++ s₂ × x ∈ p · s₁) →
              x ⊕ s₂ ∈ p · s
⊕-complete′ (s₁ , P.refl , x∈p) = extend (⊕-complete x∈p)


open import Oscar.Prelude
open import Oscar.Class.Transitivity

module Oscar.Class.Congruity where

module _ where

  module _
    {ℓ} (_∼_ : ∀ {𝔬} {𝔒 : Ø 𝔬} → 𝔒 → 𝔒 → Ø ℓ)
    𝔵 𝔶
    where
    𝓬ongruity = ∀ {𝔛 : Ø 𝔵} {𝔜 : Ø 𝔶} {x₁ x₂} (f : 𝔛 → 𝔜) → x₁ ∼ x₂ → f x₁ ∼ f x₂
    record 𝓒ongruity : Ø ℓ ∙̂ ↑̂ (𝔵 ∙̂ 𝔶) where
      field congruity : 𝓬ongruity

  open 𝓒ongruity ⦃ … ⦄ public

  module _
    {ℓ} (_∼_ : ∀ {𝔬} {𝔒 : Ø 𝔬} → 𝔒 → 𝔒 → Ø ℓ)
    {𝔵 𝔶} (𝔛 : Ø 𝔵) (𝔜 : Ø 𝔶)
    where
    𝓬ongruity' = ∀ {x₁ x₂} (f : 𝔛 → 𝔜) → x₁ ∼ x₂ → f x₁ ∼ f x₂
    record 𝓒ongruity' : Ø ℓ ∙̂ ↑̂ (𝔵 ∙̂ 𝔶) where
      field congruity' : 𝓬ongruity'

  open 𝓒ongruity' ⦃ … ⦄ public

  module _
    {ℓ} {𝔬} (_∼_ : ∀ {𝔒 : Ø 𝔬} → 𝔒 → 𝔒 → Ø ℓ)
    (𝔛 𝔜 : Ø 𝔬)
    where
    𝓬ongruity'' = ∀ {x₁ x₂} (f : 𝔛 → 𝔜) → x₁ ∼ x₂ → f x₁ ∼ f x₂
    record 𝓒ongruity'' : Ø ℓ ∙̂ 𝔬 where
      field congruity'' : 𝓬ongruity''

  open 𝓒ongruity'' ⦃ … ⦄ public

-- a functional replacement of 𝓒ongruity₂ (but note the additional requirement of 𝓣ransitivity)
congruity2 : ∀ {ℓ} {_∼_ : ∀ {x} {X : Ø x} → X → X → Ø ℓ}
    {𝔵 𝔶 𝔷}
    ⦃ _ : 𝓒ongruity _∼_ 𝔵 (𝔶 ∙̂ 𝔷) ⦄
    ⦃ _ : 𝓒ongruity _∼_ (𝔶 ∙̂ 𝔷) 𝔷 ⦄
    ⦃ _ : 𝓒ongruity _∼_ 𝔶 (𝔵 ∙̂ 𝔷) ⦄
    ⦃ _ : 𝓒ongruity _∼_ (𝔵 ∙̂ 𝔷) 𝔷 ⦄
    ⦃ _ : ∀ {x} {X : Ø x} → Transitivity.class (_∼_ {X = X}) ⦄
    → ∀ {𝔛 : Ø 𝔵} {𝔜 : Ø 𝔶} {ℨ : Ø 𝔷} {x₁ x₂} {y₁ y₂} (f : 𝔛 → 𝔜 → ℨ) → x₁ ∼ x₂ → y₁ ∼ y₂ → f x₁ y₁ ∼ f x₂ y₂
congruity2 {𝔛 = 𝔛}{𝔜}{ℨ}{x₁}{x₂}{y₁}{y₂} f x₁∼x₂ y₁∼y₂ =
  let fx1=fx2 = congruity f x₁∼x₂ in
  let g2 = λ (fx : 𝔜 → ℨ) → fx y₂ in
  let fx1y2=fx2y2 = congruity g2 fx1=fx2 in
  let e = flip f in
  let ey1=ey2 = congruity e y₁∼y₂ in
  let h1 = λ (ex : 𝔛 → ℨ) → ex x₁ in
  let fx1y1=fx1y2 = congruity h1 ey1=ey2 in
  transitivity fx1y1=fx1y2 fx1y2=fx2y2

module _ where

  record 𝓒ongruity₂
    {ℓ} (_∼_ : ∀ {x} {X : Ø x} → X → X → Ø ℓ)
    𝔵 𝔶 𝔷
    : Ø ℓ ∙̂ ↑̂ (𝔵 ∙̂ 𝔶 ∙̂ 𝔷) where
    field congruity₂ : ∀ {𝔛 : Ø 𝔵} {𝔜 : Ø 𝔶} {ℨ : Ø 𝔷} {x₁ x₂} {y₁ y₂} (f : 𝔛 → 𝔜 → ℨ) → x₁ ∼ x₂ → y₁ ∼ y₂ → f x₁ y₁ ∼ f x₂ y₂

  open 𝓒ongruity₂ ⦃ … ⦄ public

module _ where

  module _
    𝔬 𝔭
    {ℓ} (_∼̇_ : ∀ {⋆ : Ø 𝔬} {⋆̇ : ⋆ → Ø 𝔭} → ((𝓞 : ⋆) → ⋆̇ 𝓞) → ((𝓞 : ⋆) → ⋆̇ 𝓞) → Ø ℓ)
    (let infix 4 _∼̇_ ; _∼̇_ = _∼̇_)
    where
    record 𝓒̇ongruity : Ø ↑̂ (𝔬 ∙̂ 𝔭) ∙̂ ℓ where
      field ċongruity : ∀ {⋆ : Ø 𝔬} {⋆̇ : ⋆ → Ø 𝔭} {f₁ f₂ : (𝓞 : ⋆) → ⋆̇ 𝓞} (G : ∀ {𝓞 : ⋆} → ⋆̇ 𝓞 → ⋆̇ 𝓞) → f₁ ∼̇ f₂ → G ∘ f₁ ∼̇ G ∘ f₂

  open 𝓒̇ongruity ⦃ … ⦄ public


------------------------------------------------------------------------
-- Well-typed substitutions
------------------------------------------------------------------------

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

module Data.Fin.Substitution.Typed where

open import Data.Context as Ctx hiding (map)
open import Data.Context.WellFormed
open import Data.Context.Properties using (module ContextFormationLemmas)
open import Data.Fin using (Fin; zero; suc)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Nat using (ℕ; suc)
open import Data.Vec as Vec using (Vec; []; _∷_; map)
open import Data.Vec.Properties using (map-cong; map-∘; lookup-map)
open import Data.Vec.Relation.Binary.Pointwise.Inductive as PW
  using (Pointwise; []; _∷_)
open import Function as Fun using (_∘_)
open import Level using (_⊔_) renaming (zero to lzero; suc to lsuc)
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning
open import Relation.Unary using (Pred)

------------------------------------------------------------------------
-- Abstract typing
--
-- Well-typed substitutions are defined over an abstract "type
-- system", i.e. a pair of formation and typing judgments and
-- substitution lemmas for raw terms and types.

-- An abtract typing judgment _⊢_∈_ : Typing Bnd Term Type is a
-- ternary relation which, in a given Bnd-context, relates Term-terms
-- to their Type-types.

Typing : ∀ {t₁ t₂ t₃} →
         Pred ℕ t₁ → Pred ℕ t₂ → Pred ℕ t₃ → ∀ ℓ →
         Set (t₁ ⊔ t₂ ⊔ t₃ ⊔ lsuc ℓ)
Typing Bnd Term Type ℓ = ∀ {n} → Ctx Bnd n → Term n → Type n → Set ℓ

------------------------------------------------------------------------
-- Abstract well-typed substitutions (aka context morphisms)

record TypedSub {t}
                (Type : Pred ℕ t)      -- Type syntax
                (Term : Pred ℕ lzero)  -- Term syntax
                ℓ
                : Set (lsuc (t ⊔ ℓ)) where

  -- The underlying type assignment system

  infix 4 _⊢_∈_ _⊢_wf

  field
    _⊢_wf : Wf Type Type ℓ            -- Type formation judgment
    _⊢_∈_ : Typing Type Term Type ℓ   -- Typing judgment

  -- Weakening and term substitutions in types.

  field
    typeExtension       : Extension Type          -- weakening of types
    typeTermApplication : Application Type Term   -- app. of term subst.
    termSimple          : Simple Term             -- simple term subst.

  open ContextFormation _⊢_wf          using (_wf)
  open Application typeTermApplication using (_/_)

  infix  4 _⊢/_∈_
  infixr 5 _∷_

  -- Typed substitutions.
  --
  -- A typed substitution Δ ⊢/ σ ∈ Γ is a substitution σ which, when
  -- applied to a well-typed term Γ ⊢ t ∈ a in a source context Γ,
  -- yields a well-typed term Δ ⊢ t / σ ∈ a / σ in a well-formed
  -- target context Δ.

  data _⊢/_∈_ {n} (Δ : Ctx Type n) :
              ∀ {m} → Sub Term m n → Ctx Type m → Set (t ⊔ ℓ) where
    []  : Δ wf → Δ ⊢/ [] ∈ []
    _∷_ : ∀ {m t} {a : Type m} {σ : Sub Term m n} {Γ} →
          Δ ⊢ t ∈ a / σ → Δ ⊢/ σ ∈ Γ → Δ ⊢/ t ∷ σ ∈ a ∷ Γ

  -- The domains of well-typed substitutions are well-formed.

  /∈-wf : ∀ {m n Δ} {σ : Sub Term m n} {Γ} → Δ ⊢/ σ ∈ Γ → Δ wf
  /∈-wf ([] Δ-wf) = Δ-wf
  /∈-wf (_ ∷ σ∈Γ) = /∈-wf σ∈Γ

------------------------------------------------------------------------
-- Operations on abstract well-typed substitutions

-- Operations on abstract typed substitutions that require weakening
-- of well-typed terms

record TypedWeakenOps {t ℓ} {Type : Pred ℕ t} {Term : Pred ℕ lzero}
                      (typedSub : TypedSub Type Term ℓ)
                      : Set (lsuc (t ⊔ ℓ)) where

  open TypedSub typedSub

  -- Operations on contexts and raw terms that require weakening

  private
    termExtension : Extension Term
    termExtension = record { weaken = Simple.weaken termSimple }

    module E = Extension termExtension
    module C = WeakenOps typeExtension

  open Simple           termSimple          using (wk)
  open Application      typeTermApplication using (_/_; _⊙_)
  open ContextFormation _⊢_wf               using (_wf; _∷_; wf-∷₁)

  field

    -- Weakening preserves well-typed terms.

    ∈-weaken : ∀ {n} {Δ : Ctx Type n} {t a b} → Δ ⊢ a wf → Δ ⊢ t ∈ b →
               (a ∷ Δ) ⊢ E.weaken t ∈ C.weaken b

    -- Well-typedness implies well-formedness of contexts.

    ∈-wf : ∀ {n} {Δ : Ctx Type n} {t a} → Δ ⊢ t ∈ a → Δ wf

    -- Lemmas relating type weakening to term substitutions

    /-wk : ∀ {n} {a : Type n} → a / wk ≡ C.weaken a

    /-weaken : ∀ {m n} {σ : Sub Term m n} a →
               a / map E.weaken σ ≡ a / σ / wk

    weaken-/-∷ : ∀ {n m} {t} {σ : Sub Term m n} (a : Type m) →
                 C.weaken a / (t ∷ σ) ≡ a / σ

  -- Some helper lemmas

  /-map-weaken : ∀ {m n} a (ρ : Sub Term m n) →
                 C.weaken (a / ρ) ≡ a / map E.weaken ρ
  /-map-weaken a ρ = begin
    C.weaken (a / ρ)    ≡⟨ sym /-wk ⟩
    a / ρ / wk          ≡⟨ sym (/-weaken a) ⟩
    a / map E.weaken ρ  ∎

  map-weaken-⊙-∷ : ∀ {m n} (σ : Sub Type m m) t (ρ : Sub Term m n) →
                   σ ⊙ ρ ≡ map C.weaken σ ⊙ (t ∷ ρ)
  map-weaken-⊙-∷ σ t ρ = sym (begin
    map C.weaken σ ⊙ (t ∷ ρ)          ≡⟨ sym (map-∘ _ C.weaken σ) ⟩
    map ((_/ (t ∷ ρ)) ∘ C.weaken) σ   ≡⟨ map-cong weaken-/-∷ σ ⟩
    map (_/ ρ) σ                      ≡⟨⟩
    σ ⊙ ρ                             ∎)

  -- Weakening preserves well-typed substitutions.

  /∈-weaken : ∀ {m n Δ a} {σ : Sub Term m n} {Γ} →
              Δ ⊢ a wf → Δ ⊢/ σ ∈ Γ →
              (a ∷ Δ) ⊢/ map E.weaken σ ∈ Γ
  /∈-weaken                         a-wf ([] Δ-wf)     = [] (a-wf ∷ Δ-wf)
  /∈-weaken {_} {_} {Δ} {a} {t ∷ σ} a-wf (t∈b/σ ∷ σ∈Γ) =
    subst (a ∷ Δ ⊢ _ ∈_) (/-map-weaken _ σ) (∈-weaken a-wf t∈b/σ) ∷
    (/∈-weaken a-wf σ∈Γ)

  -- A typed substitution consists of pointwise-typed terms.

  toPointwise : ∀ {m n Δ} {σ : Sub Term m n} {Γ} →
                Δ ⊢/ σ ∈ Γ → Pointwise (Δ ⊢_∈_) σ (C.toVec Γ ⊙ σ)
  toPointwise                     ([] Δ-wf)     = []
  toPointwise {_} {_} {Δ} {t ∷ σ} (t∈a/σ ∷ σ∈Γ) =
    subst (Δ ⊢ t ∈_) (sym (weaken-/-∷ _)) t∈a/σ ∷
    subst (Pointwise (_⊢_∈_ Δ) σ) (map-weaken-⊙-∷ _ t σ) (toPointwise σ∈Γ)

  -- Look up an entry in a typed substitution.

  lookup : ∀ {m n Δ} {σ : Sub Term m n} {Γ} →
           Δ ⊢/ σ ∈ Γ → (x : Fin m) → Δ ⊢ Vec.lookup σ x ∈ C.lookup Γ x / σ
  lookup {_} {_} {Δ} {σ} {Γ} σ∈Γ x =
    subst (Δ ⊢ _ ∈_) (lookup-map x _ (C.toVec Γ))
          (PW.lookup (toPointwise σ∈Γ) x)

  -- Extension by a typed term.

  ∈-/∷ : ∀ {m n Δ} {σ : Sub Term m n} {Γ t a b} →
         (b ∷ Δ) ⊢ t ∈ C.weaken (a / σ) → Δ ⊢/ σ ∈ Γ →
         b ∷ Δ ⊢/ t E./∷ σ ∈ a ∷ Γ
  ∈-/∷ t∈a/σ σ∈Γ =
    subst (_⊢_∈_ _ _) (/-map-weaken _ _) t∈a/σ ∷ /∈-weaken b-wf σ∈Γ

    where
    b∷Δ-wf = ∈-wf t∈a/σ
    b-wf   = wf-∷₁ b∷Δ-wf

-- Operations on abstract typed substitutions that require simple term
-- substitutions

record TypedSimple {t ℓ} {Type : Pred ℕ t} {Term : Pred ℕ lzero}
                   (typedSub : TypedSub Type Term ℓ)
                   : Set (lsuc (t ⊔ ℓ)) where

  -- Operations on abstract typed substitutions that require weakening
  -- of terms

  field typedWeakenOps : TypedWeakenOps typedSub

  open TypedWeakenOps   typedWeakenOps public
  open TypedSub         typedSub
  open ContextFormation _⊢_wf using (_wf; _⊢_wfExt; []; _∷_; wf-∷₂)
  open Application      typeTermApplication using (_/_)

  private
    module S = Simple    termSimple
    module C = WeakenOps typeExtension
  open S using (_↑; _↑⋆_; id; wk; sub)

  -- Context operations that require term substitutions in types.

  open SubstOps typeTermApplication termSimple using (_E/_)

  field

    -- Takes variables to well-typed Tms.

    ∈-var : ∀ {n} {Γ : Ctx Type n} →
            ∀ x → Γ wf → Γ ⊢ S.var x ∈ C.lookup Γ x

    -- Well-formedness of types implies well-formedness of contexts.

    wf-wf : ∀ {n} {Γ : Ctx Type n} {a} → Γ ⊢ a wf → Γ wf

    -- The identity substitution is an identity

    id-vanishes : ∀ {n} (a : Type n) → a / id ≡ a

  -- Lifting.

  ∈-↑ : ∀ {m n Δ} {σ : Sub Term m n} {a Γ} →
        Δ ⊢ a / σ wf → Δ ⊢/ σ ∈ Γ → a / σ ∷ Δ ⊢/ σ ↑ ∈ a ∷ Γ
  ∈-↑ a/σ-wf σ∈Γ = ∈-/∷ (∈-var zero (a/σ-wf ∷ (wf-wf a/σ-wf))) σ∈Γ

  ∈-↑⋆ : ∀ {k m n Δ} {E : CtxExt Type m k} {σ : Sub Term m n} {Γ} →
         Δ ⊢ (E E/ σ) wfExt → Δ ⊢/ σ ∈ Γ → (E E/ σ) ++ Δ ⊢/ σ ↑⋆ k ∈ (E ++ Γ)
  ∈-↑⋆ {E = []}            []                        σ∈Γ = σ∈Γ
  ∈-↑⋆ {E = a ∷ E} {Δ} {Γ} (a/σ↑⋆1+k-wf ∷ E/σ-wfExt) σ∈Γ =
    ∈-↑ {Γ = E ++ Γ} a/σ↑⋆1+k-wf (∈-↑⋆ {E = E} E/σ-wfExt σ∈Γ)

  -- The identity substitution.

  ∈-id : ∀ {n} {Γ : Ctx Type n} → Γ wf → Γ ⊢/ id ∈ Γ
  ∈-id []            = [] []
  ∈-id (a-wf ∷ Γ-wf) =
    ∈-/∷ (subst (_ ⊢ S.var zero ∈_) (sym (cong C.weaken (id-vanishes _)))
                (∈-var zero (a-wf ∷ Γ-wf)))
         (∈-id Γ-wf)

  -- Weakening.

  ∈-wk : ∀ {n} {Γ : Ctx Type n} {a} → Γ ⊢ a wf → a ∷ Γ ⊢/ wk ∈ Γ
  ∈-wk a-wf = /∈-weaken a-wf (∈-id (wf-wf a-wf))

  -- A substitution which only replaces the first variable.

  ∈-sub : ∀ {n} {Γ : Ctx Type n} {t a} → Γ ⊢ t ∈ a → Γ ⊢/ sub t ∈ a ∷ Γ
  ∈-sub t∈a =
    (subst (_ ⊢ _ ∈_) (sym (id-vanishes _)) t∈a) ∷ (∈-id (∈-wf t∈a))

  -- A substitution which only changes the type of the first variable.

  ∈-tsub : ∀ {n} {Γ : Ctx Type n} {a b} → b ∷ Γ ⊢ S.var zero ∈ C.weaken a →
           b ∷ Γ ⊢/ id ∈ a ∷ Γ
  ∈-tsub z∈a =
    ∈-/∷ (subst (_ ⊢ _ ∈_) (cong C.weaken (sym (id-vanishes _))) z∈a)
         (∈-id (wf-∷₂ (∈-wf z∈a)))

-- Application of typed substitutions to typed terms.

record TypedApplication {t ℓ} {Type : Pred ℕ t} {Term : Pred ℕ lzero}
                        (typedSub : TypedSub Type Term ℓ)
                        : Set (lsuc (t ⊔ ℓ)) where

  open TypedSub    typedSub
  open Application typeTermApplication using (_/_)

  -- Applications of term substitutions to terms

  field termTermApplication : Application Term Term

  private
    module C = WeakenOps typeExtension
    module A = Application termTermApplication
  open A using (_⊙_)

  field

    -- Post-application of substitutions to things.

    ∈-/ : ∀ {m n Δ} {σ : Sub Term m n} {Γ t a} →
          Γ ⊢ t ∈ a → Δ ⊢/ σ ∈ Γ → Δ ⊢ t A./ σ ∈ a / σ

    -- Application of composed substitutions is repeated application.

    /-⊙ : ∀ {m n k} {σ : Sub Term m n} {ρ : Sub Term n k} a →
          a / σ ⊙ ρ ≡ a / σ / ρ

  -- Reverse composition. (Fits well with post-application.)

  ∈-⊙ : ∀ {m n k Δ Γ E} {σ : Sub Term m n} {ρ : Sub Term n k} →
        Δ ⊢/ σ ∈ Γ → E ⊢/ ρ ∈ Δ → E ⊢/ σ ⊙ ρ ∈ Γ
  ∈-⊙ ([] Δ-wf)     ρ∈Δ = [] (/∈-wf ρ∈Δ)
  ∈-⊙ (t∈a/σ ∷ σ∈Γ) ρ∈Δ =
    (subst (_ ⊢ _ ∈_) (sym (/-⊙ _)) (∈-/ t∈a/σ ρ∈Δ)) ∷ (∈-⊙ σ∈Γ ρ∈Δ)

------------------------------------------------------------------------
-- Instantiations and code for facilitating instantiations

-- Abstract typed liftings from Term₁ to Term₂ terms

record LiftTyped {t ℓ₁ ℓ₂} {Type : Pred ℕ t} {Term₁ Term₂ : Pred ℕ lzero}
                 (typeTermSubst : TermLikeSubst Type Term₂)
                 (_⊢_wf  : Wf Type Type ℓ₁)
                 (_⊢₁_∈_ : Typing Type Term₁ Type ℓ₁)
                 (_⊢₂_∈_ : Typing Type Term₂ Type ℓ₂)
                 : Set (lsuc (t ⊔ ℓ₁ ⊔ ℓ₂)) where

  field rawLift : Lift Term₁ Term₂   -- Lifting between raw terms

  open TermLikeSubst typeTermSubst using (app; weaken; termSubst)
  private module S₂ = TermSubst termSubst

  typedSub : TypedSub Type Term₁ ℓ₁
  typedSub = record
    { _⊢_wf               = _⊢_wf
    ; _⊢_∈_               = _⊢₁_∈_
    ; typeExtension       = record { weaken = weaken }
    ; typeTermApplication = record { _/_    = app rawLift }
    ; termSimple          = Lift.simple rawLift
    }
  open TypedSub typedSub public hiding (_⊢_wf; _⊢_∈_; typeExtension)

  -- Simple typed Term₁ substitutions.

  field typedSimple : TypedSimple typedSub

  open Lift        rawLift             public
  open Application typeTermApplication public
  open TypedSimple typedSimple         public hiding (_⊢_∈_)

  field

    -- Lifts well-typed Term₁-terms to well-typed Term₂-terms.

    ∈-lift : ∀ {n} {Γ : Ctx Type n} {t a} → Γ ⊢₁ t ∈ a → Γ ⊢₂ (lift t) ∈ a

    -- A usefule lemma: lifting preserves variables.

    lift-var : ∀ {n} (x : Fin n) → lift (var x) ≡ S₂.var x

-- Abstract variable typings.

module VarTyping {t ℓ} {Type : Pred ℕ t}
                 (_⊢_wf : Wf Type Type (t ⊔ ℓ))
                 (typeExtension : Extension Type) where

  open ContextFormation _⊢_wf
  open WeakenOps        typeExtension

  infix 4 _⊢Var_∈_

  -- Abstract reflexive variable typings.

  data _⊢Var_∈_ {n} (Γ : Ctx Type n) : Fin n → Type n → Set (t ⊔ ℓ) where
    ∈-var : ∀ x → Γ wf → Γ ⊢Var x ∈ lookup Γ x

  -- Variable typing together with type formation forms
  -- a type assignment system.

-- Abstract typed variable substitutions (renamings).

record TypedVarSubst {t} (Type : Pred ℕ t) ℓ : Set (lsuc (t ⊔ ℓ)) where

  infix 4 _⊢_wf

  field
    _⊢_wf : Wf Type Type (t ⊔ ℓ)     -- Type formation judgment

    -- Generic application of renamings to raw types

    typeExtension      : Extension Type
    typeVarApplication : Application Type Fin

  open VarTyping {t} {t ⊔ ℓ} _⊢_wf typeExtension public

  open WeakenOps        typeExtension      using (weaken; toVec)
  open Application      typeVarApplication using (_/_)
  open ContextFormation _⊢_wf              using (_wf; _∷_)
  open VarSubst                            using (id; wk; _⊙_)

  field

    -- Context validity of type formation.

    wf-wf : ∀ {n} {Γ : Ctx Type n} {a}   → Γ ⊢ a wf  → Γ wf

    -- Lemmas about renamings in types

    /-wk        : ∀ {n} {a : Type n} → a / wk ≡ weaken a
    id-vanishes : ∀ {n} (a : Type n) → a / id ≡ a
    /-⊙         : ∀ {m n k} {ρ₁ : Sub Fin m n} {ρ₂ : Sub Fin n k} a →
                  a / ρ₁ ⊙ ρ₂ ≡ a / ρ₁ / ρ₂

  -- Typed renamings and associated lemmas.

  typedRenaming : TypedSub Type Fin (t ⊔ ℓ)
  typedRenaming = record
    { _⊢_wf               = _⊢_wf
    ; _⊢_∈_               = _⊢Var_∈_
    ; typeExtension       = typeExtension
    ; typeTermApplication = typeVarApplication
    ; termSimple          = VarSubst.simple
    }

  appLemmas : AppLemmas Type Fin
  appLemmas = record
    { application = typeVarApplication
    ; lemmas₄     = VarLemmas.lemmas₄
    ; id-vanishes = id-vanishes
    ; /-⊙         = /-⊙
    }

  weakenLemmas : WeakenLemmas Type Fin
  weakenLemmas = record
    { appLemmas = appLemmas
    ; /-wk      = /-wk
    }

  open AppLemmas    appLemmas    using (/-weaken)
  open WeakenLemmas weakenLemmas using (weaken-/-∷)

  -- Simple typed renamings.

  typedSimple : TypedSimple typedRenaming
  typedSimple = record
    { typedWeakenOps = record
      { ∈-weaken     = ∈-weaken
      ; ∈-wf         = ∈-wf
      ; /-wk         = /-wk
      ; /-weaken     = /-weaken
      ; weaken-/-∷   = weaken-/-∷
      }
    ; ∈-var          = ∈-var
    ; wf-wf          = wf-wf
    ; id-vanishes    = id-vanishes
    }
    where

    ∈-weaken : ∀ {n} {Γ : Ctx Type n} {x a b} →
               Γ ⊢ a wf → Γ ⊢Var x ∈ b →
               a ∷ Γ ⊢Var suc x ∈ weaken b
    ∈-weaken {_} {Γ} a-wf (∈-var x Γ-wf) =
      subst (_ ∷ Γ ⊢Var _ ∈_) (lookup-map x weaken (toVec Γ))
            (∈-var (suc x) (a-wf ∷ Γ-wf))

    ∈-wf : ∀ {n} {Γ : Ctx Type n} {x a} → Γ ⊢Var x ∈ a → Γ wf
    ∈-wf (∈-var x Γ-wf) = Γ-wf

  open TypedSub typedRenaming public
    renaming (_⊢/_∈_ to _⊢/Var_∈_) hiding (_⊢_wf; _/_)
  open TypedSimple typedSimple public
    hiding (typeExtension; _/_; _⊢_∈_; ∈-var; _⊢_wf; wf-wf; /-wk; id-vanishes)

  -- Applications of typed renamings to typed variables

  typedApplication : TypedApplication typedRenaming
  typedApplication = record
    { termTermApplication = VarSubst.application
    ; ∈-/ = ∈-/
    ; /-⊙ = /-⊙
    }
    where

    ∈-/ : ∀ {m n Δ} {ρ : Sub Fin m n} {Γ x a} →
          Γ ⊢Var x ∈ a → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Var Vec.lookup ρ x ∈ a / ρ
    ∈-/ (∈-var x Γ-wf) ρ∈Γ = lookup ρ∈Γ x

  open TypedApplication typedApplication public
    using (_/_; ∈-/; ∈-⊙)

-- Abstract typed term substitutions.

record TypedTermSubst {t h} (Type : Pred ℕ t) (Term : Pred ℕ lzero) ℓ
                      (Helpers : ∀ {T} → Lift T Term → Set h)
                      : Set (lsuc (t ⊔ ℓ) ⊔ h) where

  infix 4 _⊢_∈_ _⊢_wf

  field
    _⊢_wf : Wf Type Type (t ⊔ ℓ)            -- Type formation judgment
    _⊢_∈_ : Typing Type Term Type (t ⊔ ℓ)   -- Typing judgment

    -- Lemmas about term substitutions in Types

    termLikeLemmas : TermLikeLemmas Type Term

  private
    module Tp = TermLikeLemmas termLikeLemmas
    module Tm = TermLemmas Tp.termLemmas
    module S  = TermSubst Tm.termSubst

  typeExtension : Extension Type
  typeExtension = record { weaken = Tp.weaken }

  private module C = WeakenOps typeExtension

  typedSub : TypedSub Type Term (t ⊔ ℓ)
  typedSub = record
    { _⊢_wf               = _⊢_wf
    ; _⊢_∈_               = _⊢_∈_
    ; typeExtension       = typeExtension
    ; termSimple          = S.simple
    ; typeTermApplication = Tp.termApplication
    }

  open ContextFormation _⊢_wf using (_wf)

  field

    -- Custom helper lemmas used to implement application of typed
    -- substitutions

    varHelpers  : Helpers S.varLift   -- lemmas about renamings
    termHelpers : Helpers S.termLift  -- lemmas about substitutions

    -- Context validity of type formation and typing.

    wf-wf : ∀ {n} {Γ : Ctx Type n} {a}   → Γ ⊢ a wf  → Γ wf
    ∈-wf  : ∀ {n} {Γ : Ctx Type n} {t a} → Γ ⊢ t ∈ a → Γ wf

    -- Takes variables to well-typed Tms.

    ∈-var : ∀ {n} {Γ : Ctx Type n} →
            ∀ x → Γ wf → Γ ⊢ S.var x ∈ C.lookup Γ x

    -- Generic application of typed Term′ substitutions to typed Terms.

    typedApp : ∀ {Term′ : Pred ℕ lzero}
               (_⊢′_∈_ : Typing Type Term′ Type (t ⊔ ℓ))
               (liftTyped : LiftTyped Tp.termLikeSubst _⊢_wf _⊢′_∈_ _⊢_∈_) →
               let open LiftTyped liftTyped using (rawLift; _⊢/_∈_; _/_)
               in (helpers : Helpers rawLift) →
                  ∀ {m n Γ Δ t a} {σ : Sub Term′ m n} →
                  Γ ⊢ t ∈ a → Δ ⊢/ σ ∈ Γ → Δ ⊢ S.app rawLift t σ ∈ a / σ

  typedVarSubst : TypedVarSubst Type (t ⊔ ℓ)
  typedVarSubst = record
    { _⊢_wf              = _⊢_wf
    ; typeVarApplication = Tp.varApplication
    ; typeExtension      = typeExtension
    ; wf-wf              = wf-wf
    ; /-wk               = refl
    ; id-vanishes        = id-vanishes
    ; /-⊙                = /-⊙
    }
    where
    open LiftSubLemmas Tp.varLiftSubLemmas using (liftAppLemmas)
    open LiftAppLemmas liftAppLemmas       using (id-vanishes; /-⊙)

  open TypedVarSubst typedVarSubst public
    using (_⊢Var_∈_; _⊢/Var_∈_; typedRenaming)
    renaming
    ( typedSimple    to varTypedSimple
    ; ∈-var          to Var∈-var
    ; ∈-↑            to Var∈-↑
    ; ∈-id           to Var∈-id
    ; ∈-wk           to Var∈-wk
    ; ∈-sub          to Var∈-sub
    ; ∈-tsub         to Var∈-tsub
    )

  varLiftTyped : LiftTyped Tp.termLikeSubst _⊢_wf _⊢Var_∈_ _⊢_∈_
  varLiftTyped = record
    { rawLift     = S.varLift
    ; typedSimple = varTypedSimple
    ; ∈-lift      = ∈-lift
    ; lift-var    = λ _ → refl
    }
    where

    ∈-lift : ∀ {n Γ x} {a : Type n} → Γ ⊢Var x ∈ a → Γ ⊢ S.var x ∈ a
    ∈-lift (Var∈-var x Γ-wf) = ∈-var x Γ-wf

  ∈-/Var : ∀ {m n} {Γ t a} {ρ : Sub Fin m n} {Δ} →
           Δ ⊢ t ∈ a → Γ ⊢/Var ρ ∈ Δ → Γ ⊢ t S./Var ρ ∈ a Tp./Var ρ
  ∈-/Var = typedApp _⊢Var_∈_ varLiftTyped varHelpers

  -- Operations on well-formed contexts that require weakening of
  -- well-formedness judgments.

  -- Simple typed substitutions.

  typedSimple : TypedSimple typedSub
  typedSimple = record
    { typedWeakenOps = record
      { ∈-weaken     = λ a-wf t∈b → ∈-/Var t∈b (Var∈-wk a-wf)
      ; ∈-wf         = ∈-wf
      ; /-wk         = Tp./-wk
      ; /-weaken     = Tp./-weaken
      ; weaken-/-∷   = Tp.weaken-/-∷
      }
    ; ∈-var          = ∈-var
    ; wf-wf          = wf-wf
    ; id-vanishes    = Tp.id-vanishes
    }

  termLiftTyped : LiftTyped Tp.termLikeSubst _⊢_wf _⊢_∈_ _⊢_∈_
  termLiftTyped = record
    { rawLift     = S.termLift
    ; typedSimple = typedSimple
    ; ∈-lift      = Fun.id
    ; lift-var    = λ _ → refl
    }

  -- Applications of typed term substitutions to typed terms

  typedApplication : TypedApplication typedSub
  typedApplication = record
    { termTermApplication = S.application
    ; ∈-/ = typedApp _⊢_∈_ termLiftTyped termHelpers
    ; /-⊙ = Tp./-⊙
    }

  open TypedSub typedSub public hiding (typeExtension)
  open TypedSimple typedSimple public hiding
    ( typeExtension; ∈-weaken; ∈-wf; ∈-var; wf-wf
    ; /-wk; /-weaken; weaken-/-∷; id-vanishes
    )
  open TypedApplication typedApplication public hiding (/-⊙)