open import Prelude renaming (_<_ to _N<_)

module Implicits.Resolution.GenericFinite.Algorithm where

open import Induction.WellFounded
open import Induction.Nat
open import Data.Vec
open import Data.List
open import Data.Fin.Substitution
open import Data.Nat.Base using (_<′_)
open import Data.Maybe
open import Data.Nat hiding (_<_)
open import Data.Nat.Properties
open import Relation.Binary using (module DecTotalOrder)
open DecTotalOrder decTotalOrder using () renaming (refl to ≤-refl; trans to ≤-trans)

open import Implicits.Syntax
open import Implicits.Syntax.Type.Unification
open import Implicits.Substitutions
open import Implicits.Substitutions.Lemmas
open import Implicits.Resolution.GenericFinite.Resolution
open import Implicits.Resolution.GenericFinite.TerminationCondition
open import Implicits.Resolution.Termination

private
  module M = MetaTypeMetaSubst

module Lemmas where
  m<-Acc : ∀ {m ν} → MetaType m ν → Set
  m<-Acc {m} {ν} r = Acc _m<_ (m , ν , r)

  ρ<-Acc : ∀ {ν} → Type ν → Set
  ρ<-Acc {ν} r = Acc _ρ<_ (ν , r)

  module Arg<-well-founded  where
    open Lexicographic (_N<_) (const _ρ<_)

    open import Induction.Nat
    open import Data.Nat.Properties
    module image = Subrelation {A = ℕ} {_N<_} {_<′_} ≤⇒≤′

    arg<-well-founded : Well-founded _<_
    arg<-well-founded = well-founded (image.well-founded <-well-founded) ρ<-well-founded

    _arg<_ = _<_

    -- Accessibility of the 'goal' type during resolution.
    -- Either the *head* of the goal shrinks (Oliveira's termination condition)
    -- Or the head size remains equal and the goal shrinks in an absolute sense.
    Arg<-Acc : ∀ {ν} → Type ν → Set
    Arg<-Acc a = Acc _arg<_ (h|| a || , (, a))

  open Arg<-well-founded using (Arg<-Acc; arg<-well-founded) public

open Lemmas

module ResolutionAlgorithm (cond : TerminationCondition) where
  open TerminationCondition cond
  open ResolutionRules cond

  mutual

    match' : ∀ {m ν} (Δ : ICtx ν) (Φ : TCtx) τ → (r : MetaType m ν) →
            T-Acc Φ →
            m<-Acc r →
            Maybe (Sub (flip MetaType ν) m zero)
    match' Δ Φ τ (simpl x) f g = mgu (simpl x) τ

    match' Δ Φ τ (a ⇒ b) f (acc g) with match' Δ Φ τ b f (g _ (b-m<-a⇒b a b))
    match' Δ Φ τ (a ⇒ b) f (acc g) | nothing = nothing
    match' Δ Φ τ (a ⇒ b) f (acc g) | just u
      with (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ) <? Φ
    match' Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | yes Φ<
      with resolve' Δ (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ)
                    (from-meta (a M./ u)) (f _ Φ<)
    match' Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | yes Φ< | true = just u
    match' Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | yes Φ< | false = nothing
    match' Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | no φ> = nothing

    match' Δ Φ τ (∀' a) f (acc g) with match' Δ Φ τ (open-meta a) f (g _ (open-meta-a-m<-∀'a a))
    match' Δ Φ τ (∀' a) f (acc g) | just p = just (tail p)
    match' Δ Φ τ (∀' r) f (acc g) | nothing = nothing

    -- match defers to match', which concerns itself with MetaTypes.
    -- If match' finds a match, we can use the fact that we have zero unification variables open here
    -- to show that we found the right thing.
    match : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) τ r → T-Acc Φ → Bool
    match Δ Φ τ a f = is-just (match' Δ Φ τ (to-meta {zero} a) f (m<-well-founded _))

    match1st : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) (ρs : ICtx ν) → (τ : SimpleType ν) → T-Acc Φ → Bool
    match1st Δ Φ [] τ Φ↓ = false
    match1st Δ Φ (x ∷ xs) τ Φ↓ with match Δ Φ τ x Φ↓
    match1st Δ Φ (x ∷ xs) τ Φ↓ | true = true
    match1st Δ Φ (x ∷ xs) τ Φ↓ | false = match1st Δ Φ xs τ Φ↓

    resolve' : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) r → T-Acc Φ → Bool
    resolve' Δ Φ (simpl x) Φ↓ = match1st Δ Φ Δ x Φ↓
    resolve' Δ Φ (a ⇒ b) Φ↓ = resolve' (a ∷ Δ) Φ b Φ↓
    resolve' Δ Φ (∀' r) Φ↓ = resolve' (ictx-weaken Δ) Φ r Φ↓

    resolve : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) r → Bool
    resolve Δ Φ r = resolve' Δ Φ r (wf-< Φ)


{-# OPTIONS --universe-polymorphism #-}
module Categories.Product where

open import Level
open import Function using () renaming (_∘_ to _∙_)
open import Data.Product using (_×_; Σ; _,_; proj₁; proj₂; zip; map; <_,_>; swap)

open import Categories.Category

private
  map⁎ : ∀ {a b p q} {A : Set a} {B : A → Set b} {P : A → Set p} {Q : {x : A} → P x → B x → Set q} → (f : (x : A) → B x) → (∀ {x} → (y : P x) → Q y (f x)) → (v : Σ A P) → Σ (B (proj₁ v)) (Q (proj₂ v))
  map⁎ f g (x , y) = (f x , g y)

  map⁎′ : ∀ {a b p q} {A : Set a} {B : A → Set b} {P : Set p} {Q : P → Set q} → (f : (x : A) → B x) → ((x : P) → Q x) → (v : A × P) → B (proj₁ v) × Q (proj₂ v)
  map⁎′ f g (x , y) = (f x , g y)

  zipWith : ∀ {a b c p q r s} {A : Set a} {B : Set b} {C : Set c} {P : A → Set p} {Q : B → Set q} {R : C → Set r} {S : (x : C) → R x → Set s} (_∙_ : A → B → C) → (_∘_ : ∀ {x y} → P x → Q y → R (x ∙ y)) → (_*_ : (x : C) → (y : R x) → S x y) → (x : Σ A P) → (y : Σ B Q) → S (proj₁ x ∙ proj₁ y) (proj₂ x ∘ proj₂ y)
  zipWith _∙_ _∘_ _*_ (a , p) (b , q) = (a ∙ b) * (p ∘ q)
  syntax zipWith f g h = f -< h >- g

Product : ∀ {o ℓ e o′ ℓ′ e′} (C : Category o ℓ e) (D : Category o′ ℓ′ e′) → Category (o ⊔ o′) (ℓ ⊔ ℓ′) (e ⊔ e′)
Product C D = record
  { Obj = C.Obj × D.Obj
  ; _⇒_ = C._⇒_ -< _×_ >- D._⇒_
  ; _≡_ = C._≡_ -< _×_ >- D._≡_
  ; _∘_ = zip C._∘_ D._∘_
  ; id = C.id , D.id
  ; assoc = C.assoc , D.assoc
  ; identityˡ = C.identityˡ , D.identityˡ
  ; identityʳ = C.identityʳ , D.identityʳ
  ; equiv = record
    { refl = C.Equiv.refl , D.Equiv.refl
    ; sym = map C.Equiv.sym D.Equiv.sym
    ; trans = zip C.Equiv.trans D.Equiv.trans
    }
  ; ∘-resp-≡ = zip C.∘-resp-≡ D.∘-resp-≡
  }
  where
  module C = Category C
  module D = Category D

open import Categories.Functor using (Functor; module Functor)

infixr 2 _※_
_※_ : ∀ {o ℓ e o′₁ ℓ′₁ e′₁ o′₂ ℓ′₂ e′₂} {C : Category o ℓ e} {D₁ : Category o′₁ ℓ′₁ e′₁} {D₂ : Category o′₂ ℓ′₂ e′₂} → (F : Functor C D₁) → (G : Functor C D₂) → Functor C (Product D₁ D₂)
F ※ G = record
        { F₀ = < F.F₀ , G.F₀ >
        ; F₁ = < F.F₁ , G.F₁ >
        ; identity = F.identity , G.identity
        ; homomorphism = F.homomorphism , G.homomorphism
        ; F-resp-≡ = < F.F-resp-≡ , G.F-resp-≡ >
        }
        where
        module F = Functor F
        module G = Functor G

infixr 2 _⁂_
_⁂_ : ∀ {o₁ ℓ₁ e₁ o′₁ ℓ′₁ e′₁ o₂ ℓ₂ e₂ o′₂ ℓ′₂ e′₂} {C₁ : Category o₁ ℓ₁ e₁} {D₁ : Category o′₁ ℓ′₁ e′₁} → {C₂ : Category o₂ ℓ₂ e₂} {D₂ : Category o′₂ ℓ′₂ e′₂} → (F₁ : Functor C₁ D₁) → (F₂ : Functor C₂ D₂) → Functor (Product C₁ C₂) (Product D₁ D₂)
F ⁂ G = record
        { F₀ = map F.F₀ G.F₀
        ; F₁ = map F.F₁ G.F₁
        ; identity = F.identity , G.identity
        ; homomorphism = F.homomorphism , G.homomorphism
        ; F-resp-≡ = map F.F-resp-≡ G.F-resp-≡
        }
        where
        module F = Functor F
        module G = Functor G

open import Categories.NaturalTransformation using (NaturalTransformation; module NaturalTransformation)

infixr 2 _⁂ⁿ_
_⁂ⁿ_ : ∀ {o₁ ℓ₁ e₁ o′₁ ℓ′₁ e′₁ o₂ ℓ₂ e₂ o′₂ ℓ′₂ e′₂} {C₁ : Category o₁ ℓ₁ e₁} {D₁ : Category o′₁ ℓ′₁ e′₁} → {C₂ : Category o₂ ℓ₂ e₂} {D₂ : Category o′₂ ℓ′₂ e′₂} → {F₁ G₁ : Functor C₁ D₁} {F₂ G₂ : Functor C₂ D₂} → (α : NaturalTransformation F₁ G₁) → (β : NaturalTransformation F₂ G₂) → NaturalTransformation (F₁ ⁂ F₂) (G₁ ⁂ G₂)
α ⁂ⁿ β = record { η = map⁎′ α.η β.η; commute = map⁎′ α.commute β.commute }
         where
         module α = NaturalTransformation α
         module β = NaturalTransformation β

infixr 2 _※ⁿ_
_※ⁿ_ : ∀ {o ℓ e o′₁ ℓ′₁ e′₁} {C : Category o ℓ e} {D₁ : Category o′₁ ℓ′₁ e′₁} {F₁ G₁ : Functor C D₁} (α : NaturalTransformation F₁ G₁) → ∀ {o′₂ ℓ′₂ e′₂} {D₂ : Category o′₂ ℓ′₂ e′₂} {F₂ G₂ : Functor C D₂} (β : NaturalTransformation F₂ G₂) → NaturalTransformation (F₁ ※ F₂) (G₁ ※ G₂)
α ※ⁿ β = record { η = < α.η , β.η >; commute = < α.commute , β.commute > }
         where
         module α = NaturalTransformation α
         module β = NaturalTransformation β

assocˡ : ∀ {o₁ ℓ₁ e₁ o₂ ℓ₂ e₂ o₃ ℓ₃ e₃} → (C₁ : Category o₁ ℓ₁ e₁) (C₂ : Category o₂ ℓ₂ e₂) (C₃ : Category o₃ ℓ₃ e₃) → Functor (Product (Product C₁ C₂) C₃) (Product C₁ (Product C₂ C₃))
assocˡ C₁ C₂ C₃ = record
  { F₀ = < proj₁ ∙ proj₁ , < proj₂ ∙ proj₁ , proj₂ > >
  ; F₁ = < proj₁ ∙ proj₁ , < proj₂ ∙ proj₁ , proj₂ > >
  ; identity = C₁.Equiv.refl , C₂.Equiv.refl , C₃.Equiv.refl
  ; homomorphism = C₁.Equiv.refl , C₂.Equiv.refl , C₃.Equiv.refl
  ; F-resp-≡ = < proj₁ ∙ proj₁ , < proj₂ ∙ proj₁ , proj₂ > >
  }
  where
  module C₁ = Category C₁
  module C₂ = Category C₂
  module C₃ = Category C₃

assocʳ : ∀ {o₁ ℓ₁ e₁ o₂ ℓ₂ e₂ o₃ ℓ₃ e₃} → (C₁ : Category o₁ ℓ₁ e₁) (C₂ : Category o₂ ℓ₂ e₂) (C₃ : Category o₃ ℓ₃ e₃) → Functor (Product C₁ (Product C₂ C₃)) (Product (Product C₁ C₂) C₃)
assocʳ C₁ C₂ C₃ = record
  { F₀ = < < proj₁ , proj₁ ∙ proj₂ > , proj₂ ∙ proj₂ >
  ; F₁ = < < proj₁ , proj₁ ∙ proj₂ > , proj₂ ∙ proj₂ >
  ; identity = (C₁.Equiv.refl , C₂.Equiv.refl) , C₃.Equiv.refl
  ; homomorphism = (C₁.Equiv.refl , C₂.Equiv.refl) , C₃.Equiv.refl
  ; F-resp-≡ = < < proj₁ , proj₁ ∙ proj₂ > , proj₂ ∙ proj₂ >
  }
  where
  module C₁ = Category C₁
  module C₂ = Category C₂
  module C₃ = Category C₃

πˡ : ∀ {o ℓ e o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} → Functor (Product C D) C
πˡ {C = C} = record { F₀ = proj₁; F₁ = proj₁; identity = refl
                    ; homomorphism = refl; F-resp-≡ = proj₁ }
  where open Category.Equiv C using (refl)

πʳ : ∀ {o ℓ e o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} → Functor (Product C D) D
πʳ {D = D} = record { F₀ = proj₂; F₁ = proj₂; identity = refl
                    ; homomorphism = refl; F-resp-≡ = proj₂ }
  where open Category.Equiv D using (refl)

Swap : ∀ {o ℓ e o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} → Functor (Product D C) (Product C D)
Swap {C = C} {D = D} = (record
  { F₀ = swap
  ; F₁ = swap
  ; identity = C.Equiv.refl , D.Equiv.refl
  ; homomorphism = C.Equiv.refl , D.Equiv.refl
  ; F-resp-≡ = swap
  })
  where
  module C = Category C
  module D = Category D


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Conversion {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Substitution

open import Tools.Product

-- Conversion from left to right of valid terms.
convᵛ : ∀ {t A B Γ l}
        ([Γ] : ⊩ᵛ Γ)
        ([A]  : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
        ([B]  : Γ ⊩ᵛ⟨ l ⟩ B / [Γ])
      → Γ ⊩ᵛ⟨ l ⟩ A ≡ B / [Γ] / [A]
      → Γ ⊩ᵛ⟨ l ⟩ t ∷ A / [Γ] / [A]
      → Γ ⊩ᵛ⟨ l ⟩ t ∷ B / [Γ] / [B]
convᵛ [Γ] [A] [B] [A≡B] [t] ⊢Δ [σ] =
  let [σA]     = proj₁ ([A] ⊢Δ [σ])
      [σB]     = proj₁ ([B] ⊢Δ [σ])
      [σA≡σB]  = irrelevanceEq [σA] [σA] ([A≡B] ⊢Δ [σ])
      [σt]     = proj₁ ([t] ⊢Δ [σ])
      [σt≡σ′t] = λ σ′ → proj₂ ([t] ⊢Δ [σ]) {σ′ = σ′}
  in  convTerm₁ [σA] [σB] [σA≡σB] [σt]
  ,   λ [σ′] [σ≡σ′] → convEqTerm₁ [σA] [σB] [σA≡σB] (([σt≡σ′t] _) [σ′] [σ≡σ′])

-- Conversion from right to left of valid terms.
conv₂ᵛ : ∀ {t A B Γ l}
         ([Γ] : ⊩ᵛ Γ)
         ([A]  : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
         ([B]  : Γ ⊩ᵛ⟨ l ⟩ B / [Γ])
       → Γ ⊩ᵛ⟨ l ⟩ A ≡ B / [Γ] / [A]
       → Γ ⊩ᵛ⟨ l ⟩ t ∷ B / [Γ] / [B]
       → Γ ⊩ᵛ⟨ l ⟩ t ∷ A / [Γ] / [A]
conv₂ᵛ [Γ] [A] [B] [A≡B] [t] ⊢Δ [σ] =
  let [σA]     = proj₁ ([A] ⊢Δ [σ])
      [σB]     = proj₁ ([B] ⊢Δ [σ])
      [σA≡σB]  = irrelevanceEq [σA] [σA] ([A≡B] ⊢Δ [σ])
      [σt]     = proj₁ ([t] ⊢Δ [σ])
      [σt≡σ′t] = λ σ′ → (proj₂ ([t] ⊢Δ [σ]) {σ′ = σ′})
  in  convTerm₂ [σA] [σB] [σA≡σB] [σt]
  ,   λ [σ′] [σ≡σ′] → convEqTerm₂ [σA] [σB] [σA≡σB] (([σt≡σ′t] _) [σ′] [σ≡σ′])

-- Conversion from left to right of valid term equality.
convEqᵛ : ∀ {t u A B Γ l}
        ([Γ] : ⊩ᵛ Γ)
        ([A]  : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
        ([B]  : Γ ⊩ᵛ⟨ l ⟩ B / [Γ])
      → Γ ⊩ᵛ⟨ l ⟩ A ≡ B / [Γ] / [A]
      → Γ ⊩ᵛ⟨ l ⟩ t ≡ u ∷ A / [Γ] / [A]
      → Γ ⊩ᵛ⟨ l ⟩ t ≡ u ∷ B / [Γ] / [B]
convEqᵛ [Γ] [A] [B] [A≡B] [t≡u] ⊢Δ [σ] =
  let [σA]     = proj₁ ([A] ⊢Δ [σ])
      [σB]     = proj₁ ([B] ⊢Δ [σ])
      [σA≡σB]  = irrelevanceEq [σA] [σA] ([A≡B] ⊢Δ [σ])
  in  convEqTerm₁ [σA] [σB] [σA≡σB] ([t≡u] ⊢Δ [σ])


open import Coinduction using ( ∞ ; ♭ ; ♯_ )
open import Data.Bool using ( Bool ; true ; false )
open import Data.Nat using ( ℕ ; zero ; suc )
open import Data.Natural using ( Natural ; # ; % ; _+_ )
open import Data.Strict using ( Strict ; ! )
open import System.IO.Transducers.List using ( S⊆S&*T )
open import System.IO.Transducers.Lazy using ( _⇒_ ; inp ; out ; done ; out* ; _[&]_ ; _⟫_ )
open import System.IO.Transducers.Session using ( I ; Σ ; ⟨_⟩ ; _&_ ; ¿ ; * ; _&*_ )
open import System.IO.Transducers.Trace using ( [] ; _∷_ ; _≤_ )

module System.IO.Transducers.Stateful where

-- We apply the usual state transformer construction
-- for a premonoial category: morphisms
-- from T to U with state S are just regular morphisms
-- from T & S to U & S.

-- We are particularly interested in the case where T is I
-- and S is Bytes, since this collapses to Bytes ⇒ U & Bytes,
-- that is, the type for a parser over a byte stream.

-- The type Bytes ⇒ U & Bytes (or more generally * ⟨ B ⟩ ⇒ U & * ⟨ B ⟩)
-- is the type of an iteratee returning U.

-- Lookahead.

-- Lookahead buffers up all input until some output is produced.
-- If the output is (just x), then we discard the buffer, and
-- continue with the process. If the output is nothing, then we
-- return the buffer to the output stream and discard the process.

lookahead¿' : ∀ {T S S'} → (S' ≤ S) → (S' ⇒ ¿ T & S) → (S' ⇒ ¿ T & S)
lookahead¿' {T} as (inp F)       = inp (♯ λ a → lookahead¿' {T} (a ∷ as) (♭ F a))
lookahead¿' {T} as (out true P)  = out true P
lookahead¿' {T} as (out false P) = out false (out* as done)
lookahead¿' {T} as (done)        = inp (♯ λ a → lookahead¿' {T} (a ∷ as) (out a done))

lookahead¿ : ∀ {T S} → (S ⇒ ¿ T & S) → (S ⇒ ¿ T & S)
lookahead¿ {T} = lookahead¿' {T} []

lookahead*' : ∀ {T S S'} → (S' ≤ S) → (S' ⇒ * T & S) → (S' ⇒ * T & S)
lookahead*' {T} as (inp F)       = inp (♯ λ a → lookahead*' {T} (a ∷ as) (♭ F a))
lookahead*' {T} as (out true P)  = out true P
lookahead*' {T} as (out false P) = out false (out* as done)
lookahead*' {T} as (done)        = inp (♯ λ a → lookahead*' {T} (a ∷ as) (out a done))

lookahead* : ∀ {T S} → (S ⇒ * T & S) → (S ⇒ * T & S)
lookahead* {T} = lookahead*' {T} []

-- Iteration structure.

-- Deep breath.

-- This is the trickiest bit of building a stateful transducer
-- library. The idea is to turn a stateful transducer generating
-- an optional U into a stateful transducer generating many Us.
-- We transform the transducer P into one which runs P, then
-- if P returns nothing, then loop P returns nothing and terminates, and
-- if P returns (just x), then loop P finishes running P, then runs loop P again.

-- For example, given a function nat? : ℤ → (Maybe ℕ) which
-- such that nat? n = nothing if n < 0 and just n otherwise,
-- we can define:

-- loop (lookahead (inp (♯ λ n → out (nat? n) done))) :
-- * ⟨ ℤ ⟩ ⇒ * ⟨ ℕ ⟩ & * ⟨ ℤ ⟩

-- This transducer will return the longest non-negative prefix
-- of its input, for example on input just 2 ∷ just 5 ∷ just -3 ∷ ...
-- it will produce output just 2 ∷ just 5 ∷ nothing ∷ just -3 ∷ ...

mutual

  -- This is a remarkably obscure piece of code, given that all its doing is wiring...

  -- The n : ℕ parameter is the induction scheme that gets it all to pass the
  -- termination checker. When loop P is used properly, it is with a contracting
  -- P, that is one which produces stricly fewer S tokens than it consumes.
  -- Without the n parameter, loop P could produce infinite output if P isn't
  -- contracting. For example loop (out (just x) done) would just produce the stream
  -- (just x) ∷ (just x) ∷ ... without ever consuming any input. With the n parameter
  -- we keep track of how many tokens have been consumed. If we ever hit
  -- a loop where n==0, we just run P one last time, then terminate.
  -- For example, loop (out (just x) done) just produces the trace (just x) ∷ [].

  -- For efficiency, we also pass n around as a Natural, not just an ℕ. When we
  -- read input a, we add the weight of a onto n, strictly (in order to
  -- avoid potentially keeping a live), discard the previous ℕ and build a new ℕ.
  -- It would be nice to have an induction scheme for Natural.

  -- It would be a bit nicer to track statically which processes are contractions,
  -- and only allow loop P on contraction maps.

  -- Note that contractions come up in many contexts with treatments of recursion,
  -- for example in Plotkin uniformity, they're called strict maps. They're
  -- closely related to the notion of guarded recursion which is used in checking
  -- productivity of coinductive functions in Agda.

  -- TODO: Find a way to statically enforce contraction and non-expansion maps.
  -- Or alternatively, give in and allow coinductive output,
  -- and hence lose termination for transducers.

  -- TODO: Present this as a trace structure? Show that
  -- it has the expected properties on contracting morphisms.

  -- loop''' 0 P Q R is equivant to P ⟫ Q ⟫ (done ⟨&⟩ loop R)

  loop'''' : ∀ {T T' U S S'} → (Strict Natural) → (U ⇒ S) → (S ⇒ T' & S') → (S' ⇒ ¿ T & S') → (U ⇒ (T' &* T) & S')
  loop'''' {T} {T'} (! n) P Q R = loop''' {T} {T'} (% n) n P Q R

  loop''' : ∀ {T T' U S S'} → ℕ → Natural → (U ⇒ S) → (S ⇒ T' & S') → (S' ⇒ ¿ T & S') → (U ⇒ (T' &* T) & S')
  loop''' {T} {I}             m n P         Q         R = loop' {T} m n (P ⟫ Q) R R
  loop''' {T} {Σ V F} {Σ W G} m n (inp P)   (inp Q)   R = inp (♯ λ a → loop'''' {T} {Σ V F} (! (n + W a)) (♭ P a) (inp Q) R)
  loop''' {T} {Σ V F}         m n (out a P) (inp Q)   R = loop''' {T} {Σ V F} m n P (♭ Q a) R
  loop''' {T} {Σ V F} {Σ W G} m n done      (inp P)   R = inp (♯ λ a → loop'''' {T} {Σ V F} (! (n + W a)) done (♭ P a) R)
  loop''' {T} {Σ V F}         m n P         (out b Q) R = out b (loop''' {T} {♭ F b} m n P Q R)
  loop''' {T} {Σ V F} {Σ W G} m n (inp P)   done      R = inp (♯ λ a → loop'''' {T} {Σ V F} (! (n + W a)) (♭ P a) done R)
  loop''' {T} {Σ V F}         m n (out a P) done      R = out a (loop''' {T} {♭ F a} m n P done R)
  loop''' {T} {Σ V F}         m n done      done      R = inp (♯ λ a → out a (loop'''' {T} {♭ F a} (! (n + (V a))) done done R))

  -- loop' 0 P Q R is equivalent to P ⟫ Q ⟫ loop R ⟨¿⟩ done

  loop'' : ∀ {T U S S'} → (Strict Natural) → (U ⇒ S) → (S ⇒ ¿ T & S') → (S' ⇒ ¿ T & S') → (U ⇒ * T & S')
  loop'' {T} (! n) P Q R = loop' {T} (% n) n P Q R

  loop' : ∀ {T U S S'} → ℕ → Natural → (U ⇒ S) → (S ⇒ ¿ T & S') → (S' ⇒ ¿ T & S') → (U ⇒ * T & S')
  loop' {T} {Σ V F} m       n (inp P)   (inp Q)       R = inp (♯ λ a → loop'' {T} (! (n + V a)) (♭ P a) (inp Q) R)
  loop' {T}         m       n (out a P) (inp Q)       R = loop' {T} m n P (♭ Q a) R
  loop' {T} {Σ V F} m       n done      (inp Q)       R = inp (♯ λ a → loop'' {T} (! (n + V a)) done (♭ Q a) R)
  loop' {T}         zero    n P         (out true Q)  R = out true (P ⟫ Q ⟫ S⊆S&*T {T} [&] done)
  loop' {T}         (suc m) n P         (out true Q)  R = out true (loop''' {T} {T} m n P Q R)
  loop' {T}         m       n P         (out false Q) R = out false (P ⟫ Q)
  loop' {T} {Σ V F} m       n (inp P)   done          R = inp (♯ λ a → loop'' {T} (! (n + V a)) (♭ P a) done R)
  loop' {T}         m       n (out a P) done          R = loop' {T} m n P (out a done) R
  loop' {T}         m       n done      done          R = inp (♯ λ a → loop'' {T} (! (n + # 1)) done (out a done) R)

loop : ∀ {T S} → (S ⇒ ¿ T & S) → (S ⇒ * T & S)
loop {T} P = loop' {T} zero (# 0) done P P


open import Data.Empty using (⊥-elim)
open import Data.Fin using (Fin; _≟_)
open import Data.Nat using (ℕ)
open import Data.Product using (∃-syntax; _,_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Vec using (lookup; _[_]≔_)
open import Data.Vec.Properties using (lookup∘update; lookup∘update′)
open import Relation.Nullary using (yes; no; ¬_)
open import Relation.Binary.PropositionalEquality using (sym; trans; _≡_; refl; cong; _≢_; module ≡-Reasoning)
open import Data.Product using (∃-syntax; _,_; proj₁; proj₂; _×_)
open ≡-Reasoning

open import Common
open import Global
open import Local

private
  variable
    n : ℕ
    l : Label
    p q : Fin n
    g gSub : Global n
    lSub : Local n

project : Global n -> Fin n -> Local n
project endG _
  = endL
project (msgSingle p q p≢q l gSub) r
  with p ≟ r   | q ≟ r
... | yes _    | no _     = sendSingle q l (project gSub r)
... | no _     | yes _    = recvSingle p l (project gSub r)
... | no _     | no _     = project gSub r
... | yes refl | yes refl = ⊥-elim (p≢q refl)

proj-prefix-other :
  (r s t : Fin n)
  -> ∀{ r≢s }
  -> (gSub : Global n)
  -> r ≢ t
  -> s ≢ t
  -> project (msgSingle r s r≢s l gSub) t ≡ project gSub t
proj-prefix-other r s t _ r≢t s≢t
  with  r ≟ t  | s ≟ t
... | yes refl | _        = ⊥-elim (r≢t refl)
... | _        | yes refl = ⊥-elim (s≢t refl)
... | no _     | no _     = refl

proj-prefix-send :
  (r s : Fin n)
  -> (gSub : Global n)
  -> (r≢s : r ≢ s)
  -> project (msgSingle r s r≢s l gSub) r ≡ sendSingle s l (project gSub r)
proj-prefix-send r s _ r≢s
  with  r ≟ r | s ≟ r
... | yes _   | no _     = refl
... | _       | yes refl = ⊥-elim (r≢s refl)
... | no r≢r  | no _     = ⊥-elim (r≢r refl)

proj-prefix-recv :
  (r s : Fin n)
  -> (gSub : Global n)
  -> (r≢s : r ≢ s)
  -> project (msgSingle r s r≢s l gSub) s ≡ recvSingle r l (project gSub s)
proj-prefix-recv r s _ r≢s
  with  r ≟ s  | s ≟ s
... | no _     | yes _   = refl
... | yes refl | _       = ⊥-elim (r≢s refl)
... | _        | no s≢s  = ⊥-elim (s≢s refl)

record _↔_ { n : ℕ } (g : Global n) (c : Configuration n) : Set where
  field
    isProj : ∀(p : Fin n) -> lookup c p ≡ project g p

proj-inv-send :
  project {n} g p ≡ sendSingle q l lSub
  -> ∃[ p≢q ] ∃[ gSub ] g ≡ msgSingle p q p≢q l gSub × project gSub p ≡ lSub
     ⊎
     ∃[ r ] ∃[ s ] ∃[ r≢s ] ∃[ l′ ] ∃[ gSub ]
       g ≡ msgSingle r s r≢s l′ gSub ×
       r ≢ p ×
       s ≢ p ×
       project gSub p ≡ sendSingle q l lSub
proj-inv-send {g = g@endG} projSend = ⊥-elim (endL≢sendSingle projSend)
proj-inv-send {n} {g = g@(msgSingle r s r≢s l′ gSub)} {p} {q} {l} {ltSub} projSend
  with r ≟ p    | s ≟ p
... | yes refl | yes refl = ⊥-elim (r≢s refl)
... | no r≢p   | no s≢p   = inj₂ (r , s , r≢s , l′ , gSub , refl , r≢p , s≢p , projSend)
... | yes refl | no s≢p
      with sendSingle-injective projSend
...     | refl , refl , refl  = inj₁ (r≢s , gSub , refl , refl)

proj-inv-recv :
  project {n} g p ≡ recvSingle q l lSub
  -> ∃[ p≢q ] ∃[ gSub ] g ≡ msgSingle q p p≢q l gSub × project gSub p ≡ lSub
     ⊎
     ∃[ r ] ∃[ s ] ∃[ r≢s ] ∃[ l′ ] ∃[ gSub ]
       g ≡ msgSingle r s r≢s l′ gSub ×
       r ≢ p ×
       s ≢ p ×
       project gSub p ≡ recvSingle q l lSub
proj-inv-recv {g = g@endG} projRecv = ⊥-elim (endL≢recvSingle projRecv)
proj-inv-recv {n} {g = g@(msgSingle r s r≢s l′ gSub)} {p} {q} {l} {lSub} projRecv
  with r ≟ p   | s ≟ p
... | yes refl | yes refl = ⊥-elim (r≢s refl)
... | no r≢p   | no s≢p   = inj₂ (r , s , r≢s , l′ , gSub , refl , r≢p , s≢p , projRecv)
... | no r≢p   | yes refl
      with recvSingle-injective projRecv
...     | refl , refl , refl = inj₁ (r≢s , gSub , refl , refl)

proj-inv-send-recv :
  ∀ { lpSub lqSub }
  -> project {n} g p ≡ sendSingle q l lpSub
  -> project {n} g q ≡ recvSingle p l lqSub
  -> ∃[ p≢q ] ∃[ gSub ] g ≡ msgSingle p q p≢q l gSub × project gSub p ≡ lpSub × project gSub q ≡ lqSub
     ⊎
     ∃[ r ] ∃[ s ] ∃[ r≢s ] ∃[ l′ ] ∃[ gSub ]
       g ≡ msgSingle r s r≢s l′ gSub ×
       r ≢ p ×
       s ≢ p ×
       r ≢ q ×
       s ≢ q ×
       project gSub p ≡ sendSingle q l lpSub ×
       project gSub q ≡ recvSingle p l lqSub
proj-inv-send-recv {n} {g} {p} {q} {l} {lpSub} {lqSub} projSend projRecv
  with proj-inv-send {n} {g} projSend | proj-inv-recv {n} {g} projRecv
... | inj₁ (p≢q₁ , gSub₁ , refl , proj-g₁-p)
    | inj₁ (_ , _ , refl , proj-g₂-q)
        = inj₁ (p≢q₁ , gSub₁ , refl , proj-g₁-p , proj-g₂-q)
proj-inv-send-recv {n} {g} {p} {q} {l} {ltp′} {ltq′} projSend projRecv
    | inj₁ (_ , _ , refl , _)
    | inj₂ (_ , _ , _ , _ , _ , refl , _ , s≢q , _)
        = ⊥-elim (s≢q refl)
proj-inv-send-recv {n} {g} {p} {q} {l} {ltp′} {ltq′} projSend projRecv
    | inj₂ (_ , _ , _ , _ , _ , refl , r≢p , _ , _)
    | inj₁ (_ , _ , refl , _)
        = ⊥-elim (r≢p refl)
proj-inv-send-recv {n} {g} {p} {q} {l} {ltp′} {ltq′} projSend projRecv
    | inj₂ (r , s , r≢s , l′ , gSub₁ , refl , r≢p , s≢p , proj-g₁-p)
    | inj₂ (_ , _ , _   , _  , gSub₂ , refl , r≢q , s≢q , proj-g₂-q)
        = inj₂ (r , s , r≢s , l′ , gSub₁ , refl , r≢p , s≢p , r≢q , s≢q , proj-g₁-p , proj-g₂-q)

config-gt-remove-prefix :
  ∀ { p≢q gSub }
  -> (g : Global n)
  -> (c : Configuration n)
  -> g ↔ c
  -> g ≡ msgSingle p q p≢q l gSub
  -> ∃[ cSub ] cSub ≡ (c [ p ]≔ project gSub p) [ q ]≔ project gSub q × gSub ↔ cSub
config-gt-remove-prefix {n} {p} {q} {_} {p≢q} {gSub} g c assoc refl
  = cSub , refl , (record { isProj = isProj-gSub })
    where
      lpSub = project gSub p
      lqSub = project gSub q
      cSub = (c [ p ]≔ lpSub) [ q ]≔ lqSub
      isProj-gSub : ∀ (r : Fin n) -> lookup cSub r ≡ project gSub r
      isProj-gSub r
        with p ≟ r    | q ≟ r
      ... | yes refl | yes refl = ⊥-elim (p≢q refl)
      ... | yes refl | no  _
          rewrite lookup∘update′ p≢q (c [ p ]≔ lpSub) lqSub
          rewrite lookup∘update p c lpSub
          = refl
      ... | no _    | yes refl
          rewrite lookup∘update q (c [ p ]≔ lpSub) lqSub
          = refl
      ... | no p≢r  | no  q≢r
          rewrite lookup∘update′ (¬≡-flip q≢r) (c [ p ]≔ lpSub) lqSub
          rewrite lookup∘update′ (¬≡-flip p≢r) c lpSub
          rewrite _↔_.isProj assoc r
          = proj-prefix-other p q r gSub p≢r q≢r


------------------------------------------------------------------------
-- The abstract syntax is a set, and the semantics is propositional
------------------------------------------------------------------------

open import Atom

module Propositional (atoms : χ-atoms) where

open import Equality.Propositional.Cubical
open import Logical-equivalence using (_⇔_)
open import Prelude hiding (const)

open import Bijection equality-with-J using (_↔_)
open import Equality.Decidable-UIP equality-with-J
open import Equality.Decision-procedures equality-with-J
open import Function-universe equality-with-J hiding (_∘_)
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J

open import Cancellation  atoms
open import Chi           atoms
open import Deterministic atoms

open χ-atoms atoms

-- Exp has decidable equality.

mutual

  _≟_ : Decidable-equality Exp
  apply e₁₁ e₂₁ ≟ apply e₁₂ e₂₂ with e₁₁ ≟ e₁₂
  ... | no  e₁₁≢e₁₂ = no (e₁₁≢e₁₂ ∘ proj₁ ∘ cancel-apply)
  ... | yes e₁₁≡e₁₂ = ⊎-map (cong₂ apply e₁₁≡e₁₂)
                            (_∘ proj₂ ∘ cancel-apply)
                            (e₂₁ ≟ e₂₂)

  lambda x₁ e₁ ≟ lambda x₂ e₂ with x₁ V.≟ x₂
  ... | no  x₁≢x₂ = no (x₁≢x₂ ∘ proj₁ ∘ cancel-lambda)
  ... | yes x₁≡x₂ = ⊎-map (cong₂ lambda x₁≡x₂)
                          (_∘ proj₂ ∘ cancel-lambda)
                          (e₁ ≟ e₂)

  case e₁ bs₁ ≟ case e₂ bs₂ with e₁ ≟ e₂
  ... | no  e₁≢e₂ = no (e₁≢e₂ ∘ proj₁ ∘ cancel-case)
  ... | yes e₁≡e₂ = ⊎-map (cong₂ case e₁≡e₂)
                          (_∘ proj₂ ∘ cancel-case)
                          (bs₁ ≟-B⋆ bs₂)

  rec x₁ e₁ ≟ rec x₂ e₂ with x₁ V.≟ x₂
  ... | no  x₁≢x₂ = no (x₁≢x₂ ∘ proj₁ ∘ cancel-rec)
  ... | yes x₁≡x₂ = ⊎-map (cong₂ rec x₁≡x₂)
                          (_∘ proj₂ ∘ cancel-rec)
                          (e₁ ≟ e₂)

  var x₁ ≟ var x₂ = ⊎-map (cong var) (_∘ cancel-var) (x₁ V.≟ x₂)

  const c₁ es₁ ≟ const c₂ es₂ with c₁ C.≟ c₂
  ... | no  c₁≢c₂ = no (c₁≢c₂ ∘ proj₁ ∘ cancel-const)
  ... | yes c₁≡c₂ = ⊎-map (cong₂ const c₁≡c₂)
                          (_∘ proj₂ ∘ cancel-const)
                          (es₁ ≟-⋆ es₂)

  apply _ _  ≟ lambda _ _ = no (λ ())
  apply _ _  ≟ case _ _   = no (λ ())
  apply _ _  ≟ rec _ _    = no (λ ())
  apply _ _  ≟ var _      = no (λ ())
  apply _ _  ≟ const _ _  = no (λ ())
  lambda _ _ ≟ apply _ _  = no (λ ())
  lambda _ _ ≟ case _ _   = no (λ ())
  lambda _ _ ≟ rec _ _    = no (λ ())
  lambda _ _ ≟ var _      = no (λ ())
  lambda _ _ ≟ const _ _  = no (λ ())
  case _ _   ≟ apply _ _  = no (λ ())
  case _ _   ≟ lambda _ _ = no (λ ())
  case _ _   ≟ rec _ _    = no (λ ())
  case _ _   ≟ var _      = no (λ ())
  case _ _   ≟ const _ _  = no (λ ())
  rec _ _    ≟ apply _ _  = no (λ ())
  rec _ _    ≟ lambda _ _ = no (λ ())
  rec _ _    ≟ case _ _   = no (λ ())
  rec _ _    ≟ var _      = no (λ ())
  rec _ _    ≟ const _ _  = no (λ ())
  var _      ≟ apply _ _  = no (λ ())
  var _      ≟ lambda _ _ = no (λ ())
  var _      ≟ case _ _   = no (λ ())
  var _      ≟ rec _ _    = no (λ ())
  var _      ≟ const _ _  = no (λ ())
  const _ _  ≟ apply _ _  = no (λ ())
  const _ _  ≟ lambda _ _ = no (λ ())
  const _ _  ≟ case _ _   = no (λ ())
  const _ _  ≟ rec _ _    = no (λ ())
  const _ _  ≟ var _      = no (λ ())

  _≟-B_ : Decidable-equality Br
  branch c₁ xs₁ e₁ ≟-B branch c₂ xs₂ e₂ with c₁ C.≟ c₂
  ... | no  c₁≢c₂ = no (c₁≢c₂ ∘ proj₁ ∘ cancel-branch)
  ... | yes c₁≡c₂ with List.Dec._≟_ V._≟_ xs₁ xs₂
  ...   | no  xs₁≢xs₂ = no (xs₁≢xs₂ ∘ proj₁ ∘ proj₂ ∘ cancel-branch)
  ...   | yes xs₁≡xs₂ = ⊎-map (cong₂ (uncurry branch)
                                     (cong₂ _,_ c₁≡c₂ xs₁≡xs₂))
                              (_∘ proj₂ ∘ proj₂ ∘ cancel-branch)
                              (e₁ ≟ e₂)

  _≟-⋆_ : Decidable-equality (List Exp)
  []         ≟-⋆ []         = yes refl
  (e₁ ∷ es₁) ≟-⋆ (e₂ ∷ es₂) with e₁ ≟ e₂
  ... | no  e₁≢e₂ = no (e₁≢e₂ ∘ List.cancel-∷-head)
  ... | yes e₁≡e₂ = ⊎-map (cong₂ _∷_ e₁≡e₂)
                          (_∘ List.cancel-∷-tail)
                          (es₁ ≟-⋆ es₂)

  []      ≟-⋆ (_ ∷ _) = no (λ ())
  (_ ∷ _) ≟-⋆ []      = no (λ ())

  _≟-B⋆_ : Decidable-equality (List Br)
  []         ≟-B⋆ []         = yes refl
  (b₁ ∷ bs₁) ≟-B⋆ (b₂ ∷ bs₂) with b₁ ≟-B b₂
  ... | no  b₁≢b₂ = no (b₁≢b₂ ∘ List.cancel-∷-head)
  ... | yes b₁≡b₂ = ⊎-map (cong₂ _∷_ b₁≡b₂)
                          (_∘ List.cancel-∷-tail)
                          (bs₁ ≟-B⋆ bs₂)

  []      ≟-B⋆ (_ ∷ _) = no (λ ())
  (_ ∷ _) ≟-B⋆ []      = no (λ ())

-- Exp is a set.

Exp-set : Is-set Exp
Exp-set = decidable⇒set _≟_

-- An alternative definition of the semantics.

data Lookup′ (c : Const) : List Br → List Var → Exp → Type where
  here  : ∀ {c′ xs xs′ e e′ bs} →
          c ≡ c′ → xs ≡ xs′ → e ≡ e′ →
          Lookup′ c (branch c′ xs′ e′ ∷ bs) xs e
  there : ∀ {c′ xs′ e′ bs xs e} →
          c ≢ c′ → Lookup′ c bs xs e →
          Lookup′ c (branch c′ xs′ e′ ∷ bs) xs e

infixr 5 _∷_
infix  4 _[_←_]↦′_

data _[_←_]↦′_ (e : Exp) : List Var → List Exp → Exp → Type where
  []  : ∀ {e′} → e ≡ e′ → e [ [] ← [] ]↦′ e′
  _∷_ : ∀ {x xs e′ es′ e″ e‴} →
        e‴ ≡ e″ [ x ← e′ ] → e [ xs ← es′ ]↦′ e″ →
        e [ x ∷ xs ← e′ ∷ es′ ]↦′ e‴

infix 4 _⇓′_ _⇓⋆′_

mutual

  data _⇓′_ : Exp → Exp → Type where
    apply  : ∀ {e₁ e₂ x e v₂ v} →
             e₁ ⇓′ lambda x e → e₂ ⇓′ v₂ → e [ x ← v₂ ] ⇓′ v →
             apply e₁ e₂ ⇓′ v
    case   : ∀ {e bs c es xs e′ e″ v} →
             e ⇓′ const c es → Lookup′ c bs xs e′ →
             e′ [ xs ← es ]↦′ e″ → e″ ⇓′ v →
             case e bs ⇓′ v
    rec    : ∀ {x e v} → e [ x ← rec x e ] ⇓′ v → rec x e ⇓′ v
    lambda : ∀ {x x′ e e′} →
             x ≡ x′ → e ≡ e′ → lambda x e ⇓′ lambda x′ e′
    const  : ∀ {c c′ es vs} →
             c ≡ c′ → es ⇓⋆′ vs → const c es ⇓′ const c′ vs

  data _⇓⋆′_ : List Exp → List Exp → Type where
    []  : [] ⇓⋆′ []
    _∷_ : ∀ {e es v vs} → e ⇓′ v → es ⇓⋆′ vs → e ∷ es ⇓⋆′ v ∷ vs

-- The alternative definition is isomorphic to the other one.

Lookup↔Lookup′ : ∀ {c bs xs e} → Lookup c bs xs e ↔ Lookup′ c bs xs e
Lookup↔Lookup′ = record
  { surjection = record
    { logical-equivalence = record
      { to   = to
      ; from = from
      }
    ; right-inverse-of = to∘from
    }
  ; left-inverse-of = from∘to
  }
  where
  to : ∀ {c bs xs e} → Lookup c bs xs e → Lookup′ c bs xs e
  to here          = here refl refl refl
  to (there p₁ p₂) = there p₁ (to p₂)

  from : ∀ {c bs xs e} → Lookup′ c bs xs e → Lookup c bs xs e
  from (here refl refl refl) = here
  from (there p₁ p₂)         = there p₁ (from p₂)

  from∘to : ∀ {c bs xs e} (p : Lookup c bs xs e) → from (to p) ≡ p
  from∘to here          = refl
  from∘to (there p₁ p₂) rewrite from∘to p₂ = refl

  to∘from : ∀ {c bs xs e} (p : Lookup′ c bs xs e) → to (from p) ≡ p
  to∘from (here refl refl refl) = refl
  to∘from (there p₁ p₂)         rewrite to∘from p₂ = refl

↦↔↦′ : ∀ {e xs es e′} → e [ xs ← es ]↦ e′ ↔ e [ xs ← es ]↦′ e′
↦↔↦′ = record
  { surjection = record
    { logical-equivalence = record
      { to   = to
      ; from = from
      }
    ; right-inverse-of = to∘from
    }
  ; left-inverse-of = from∘to
  }
  where
  to : ∀ {e xs es e′} → e [ xs ← es ]↦ e′ → e [ xs ← es ]↦′ e′
  to []    = [] refl
  to (∷ p) = refl ∷ to p

  from : ∀ {e xs es e′} → e [ xs ← es ]↦′ e′ → e [ xs ← es ]↦ e′
  from ([] refl)  = []
  from (refl ∷ p) = ∷ from p

  from∘to : ∀ {e xs es e′} (p : e [ xs ← es ]↦ e′) → from (to p) ≡ p
  from∘to []    = refl
  from∘to (∷ p) rewrite from∘to p = refl

  to∘from : ∀ {e xs es e′} (p : e [ xs ← es ]↦′ e′) → to (from p) ≡ p
  to∘from ([] refl)  = refl
  to∘from (refl ∷ p) rewrite to∘from p = refl

⇓↔⇓′ : ∀ {e v} → e ⇓ v ↔ e ⇓′ v
⇓↔⇓′ = record
  { surjection = record
    { logical-equivalence = record
      { to   = to
      ; from = from
      }
    ; right-inverse-of = to∘from
    }
  ; left-inverse-of = from∘to
  }
  where
  mutual

    to : ∀ {e v} → e ⇓ v → e ⇓′ v
    to (apply p q r)  = apply (to p) (to q) (to r)
    to (case p q r s) = case (to p) (_↔_.to Lookup↔Lookup′ q)
                             (_↔_.to ↦↔↦′ r) (to s)
    to (rec p)        = rec (to p)
    to lambda         = lambda refl refl
    to (const ps)     = const refl (to⋆ ps)

    to⋆ : ∀ {e v} → e ⇓⋆ v → e ⇓⋆′ v
    to⋆ []       = []
    to⋆ (p ∷ ps) = to p ∷ to⋆ ps

  mutual

    from : ∀ {e v} → e ⇓′ v → e ⇓ v
    from (apply p q r)      = apply (from p) (from q) (from r)
    from (case p q r s)     = case (from p) (_↔_.from Lookup↔Lookup′ q)
                                   (_↔_.from ↦↔↦′ r) (from s)
    from (rec p)            = rec (from p)
    from (lambda refl refl) = lambda
    from (const refl ps)    = const (from⋆ ps)

    from⋆ : ∀ {e v} → e ⇓⋆′ v → e ⇓⋆ v
    from⋆ []       = []
    from⋆ (p ∷ ps) = from p ∷ from⋆ ps

  mutual

    from∘to : ∀ {e v} (p : e ⇓ v) → from (to p) ≡ p
    from∘to (apply p q r)  rewrite from∘to p | from∘to q | from∘to r
                           = refl
    from∘to (case p q r s) rewrite from∘to p
                                 | _↔_.left-inverse-of Lookup↔Lookup′ q
                                 | _↔_.left-inverse-of ↦↔↦′ r
                                 | from∘to s = refl
    from∘to (rec p)        rewrite from∘to p = refl
    from∘to lambda         = refl
    from∘to (const ps)     rewrite from⋆∘to⋆ ps = refl

    from⋆∘to⋆ : ∀ {e v} (ps : e ⇓⋆ v) → from⋆ (to⋆ ps) ≡ ps
    from⋆∘to⋆ []       = refl
    from⋆∘to⋆ (p ∷ ps) rewrite from∘to p | from⋆∘to⋆ ps = refl

  mutual

    to∘from : ∀ {e v} (p : e ⇓′ v) → to (from p) ≡ p
    to∘from (apply p q r)      rewrite to∘from p | to∘from q | to∘from r
                               = refl
    to∘from (case p q r s)     rewrite to∘from p
                                     | _↔_.right-inverse-of
                                         Lookup↔Lookup′ q
                                     | _↔_.right-inverse-of ↦↔↦′ r
                                     | to∘from s = refl
    to∘from (rec p)            rewrite to∘from p = refl
    to∘from (lambda refl refl) = refl
    to∘from (const refl ps)    rewrite to⋆∘from⋆ ps = refl

    to⋆∘from⋆ : ∀ {e v} (ps : e ⇓⋆′ v) → to⋆ (from⋆ ps) ≡ ps
    to⋆∘from⋆ []       = refl
    to⋆∘from⋆ (p ∷ ps) rewrite to∘from p | to⋆∘from⋆ ps = refl

-- The alternative semantics is deterministic.

Lookup′-deterministic :
  ∀ {c bs xs₁ xs₂ e₁ e₂} →
  Lookup′ c bs xs₁ e₁ → Lookup′ c bs xs₂ e₂ →
  xs₁ ≡ xs₂ × e₁ ≡ e₂
Lookup′-deterministic p q =
  Lookup-deterministic
    (_↔_.from Lookup↔Lookup′ p) (_↔_.from Lookup↔Lookup′ q) refl

↦′-deterministic :
  ∀ {e xs es e₁ e₂} →
  e [ xs ← es ]↦′ e₁ → e [ xs ← es ]↦′ e₂ → e₁ ≡ e₂
↦′-deterministic p q =
  ↦-deterministic (_↔_.from ↦↔↦′ p) (_↔_.from ↦↔↦′ q)

⇓′-deterministic : ∀ {e v₁ v₂} → e ⇓′ v₁ → e ⇓′ v₂ → v₁ ≡ v₂
⇓′-deterministic p q =
  ⇓-deterministic (_↔_.from ⇓↔⇓′ p) (_↔_.from ⇓↔⇓′ q)

-- The alternative semantics is propositional.

Lookup′-propositional :
  ∀ {c bs xs e} → Is-proposition (Lookup′ c bs xs e)
Lookup′-propositional (here p₁ p₂ p₃) (here q₁ q₂ q₃)
    rewrite C.Name-set p₁ q₁
          | decidable⇒set (List.Dec._≟_ V._≟_) p₂ q₂
          | Exp-set p₃ q₃
    = refl
Lookup′-propositional (there p₁ p₂) (there q₁ q₂)
  rewrite ¬-propositional ext p₁ q₁
        | Lookup′-propositional p₂ q₂
  = refl

Lookup′-propositional (here c≡c′ _ _) (there c≢c′ _) =
  ⊥-elim (c≢c′ c≡c′)
Lookup′-propositional (there c≢c′ _) (here c≡c′ _ _) =
  ⊥-elim (c≢c′ c≡c′)

↦′-propositional :
  ∀ {e xs es e′} → Is-proposition (e [ xs ← es ]↦′ e′)
↦′-propositional ([] p)    ([] q)    rewrite Exp-set p q = refl
↦′-propositional (p₁ ∷ p₂) (q₁ ∷ q₂)
  with ↦′-deterministic p₂ q₂
... | refl rewrite Exp-set p₁ q₁
                 | ↦′-propositional p₂ q₂
  = refl

mutual

  ⇓′-propositional : ∀ {e v} → Is-proposition (e ⇓′ v)
  ⇓′-propositional (apply p₁ p₂ p₃) (apply q₁ q₂ q₃)
    with ⇓′-deterministic p₁ q₁
  ... | refl rewrite ⇓′-propositional p₁ q₁
                with ⇓′-deterministic p₂ q₂
  ... | refl rewrite ⇓′-propositional p₂ q₂
                   | ⇓′-propositional p₃ q₃
             = refl
  ⇓′-propositional (case p₁ p₂ p₃ p₄) (case q₁ q₂ q₃ q₄)
    with ⇓′-deterministic p₁ q₁
  ... | refl rewrite ⇓′-propositional p₁ q₁
                with Lookup′-deterministic p₂ q₂
  ... | refl , refl rewrite Lookup′-propositional p₂ q₂
                       with ↦′-deterministic p₃ q₃
  ... | refl rewrite ↦′-propositional p₃ q₃
                   | ⇓′-propositional p₄ q₄
    = refl
  ⇓′-propositional (rec p) (rec q)
    rewrite ⇓′-propositional p q
    = refl
  ⇓′-propositional (lambda p₁ p₂) (lambda q₁ q₂)
    rewrite V.Name-set p₁ q₁
          | Exp-set p₂ q₂
          = refl
  ⇓′-propositional (const p ps) (const q qs)
    rewrite C.Name-set p q
          | ⇓⋆′-propositional ps qs
    = refl

  ⇓⋆′-propositional : ∀ {es vs} → Is-proposition (es ⇓⋆′ vs)
  ⇓⋆′-propositional []       []       = refl
  ⇓⋆′-propositional (p ∷ ps) (q ∷ qs)
    rewrite ⇓′-propositional p q
          | ⇓⋆′-propositional ps qs
    = refl

-- The semantics is propositional.

⇓-propositional : ∀ {e v} → Is-proposition (e ⇓ v)
⇓-propositional {e} {v} =  $⟨ ⇓′-propositional ⟩
  Is-proposition (e ⇓′ v)  ↝⟨ H-level.respects-surjection (_↔_.surjection $ inverse ⇓↔⇓′) 1 ⟩□
  Is-proposition (e ⇓ v)   □


-- Andreas, 2015-07-21 Issue 1612
-- Error "D is not strictly positive" should appear immediately.
-- (There was a performance problem due to the use of Utils.Graph.....allPaths).

{-# NON_TERMINATING #-}

mutual

  data D : Set where
    c0 : A0 → D
    c1 : A1 → D
    c2 : A2 → D
    c3 : A3 → D
    c4 : A4 → D
    c5 : A5 → D
    c6 : A6 → D
    c7 : A7 → D
    c8 : A8 → D
    c9 : A9 → D

  A0 : Set
  A0 = B0

  B0 : Set
  B0 = A0 → D

  A1 : Set
  A1 = B1

  B1 : Set
  B1 = A1 → D

  A2 : Set
  A2 = B2

  B2 : Set
  B2 = A2 → D

  A3 : Set
  A3 = B3

  B3 : Set
  B3 = A3 → D

  A4 : Set
  A4 = B4

  B4 : Set
  B4 = A4 → D

  A5 : Set
  A5 = B5

  B5 : Set
  B5 = A5 → D

  A6 : Set
  A6 = B6

  B6 : Set
  B6 = A6 → D

  A7 : Set
  A7 = B7

  B7 : Set
  B7 = A7 → D

  A8 : Set
  A8 = B8

  B8 : Set
  B8 = A8 → D

  A9 : Set
  A9 = B9

  B9 : Set
  B9 = A9 → D


open import Nat
open import Prelude
open import core
open import disjointness

module elaboration-generality where
  mutual
    elaboration-generality-synth : {Γ : tctx} {e : hexp} {τ : htyp} {d : ihexp} {Δ : hctx} →
                            Γ ⊢ e ⇒ τ ~> d ⊣ Δ →
                            Γ ⊢ e => τ
    elaboration-generality-synth ESConst = SConst
    elaboration-generality-synth (ESVar x₁) = SVar x₁
    elaboration-generality-synth (ESLam apt ex) with elaboration-generality-synth ex
    ... | ih = SLam apt ih
    elaboration-generality-synth (ESAp dis _ a x₁ x₂ x₃) = SAp dis a x₁ (elaboration-generality-ana x₃)
    elaboration-generality-synth ESEHole = SEHole
    elaboration-generality-synth (ESNEHole dis ex) = SNEHole (elab-disjoint-new-synth ex dis) (elaboration-generality-synth ex)
    elaboration-generality-synth (ESAsc x) = SAsc (elaboration-generality-ana x)

    elaboration-generality-ana : {Γ : tctx} {e : hexp} {τ τ' : htyp} {d : ihexp} {Δ : hctx} →
                          Γ ⊢ e ⇐ τ ~> d :: τ' ⊣ Δ →
                          Γ ⊢ e <= τ
    elaboration-generality-ana (EALam apt m ex) = ALam apt m (elaboration-generality-ana ex)
    elaboration-generality-ana (EASubsume x x₁ x₂ x₃) = ASubsume (elaboration-generality-synth x₂) x₃
    elaboration-generality-ana EAEHole = ASubsume SEHole TCHole1
    elaboration-generality-ana (EANEHole dis x) = ASubsume (SNEHole (elab-disjoint-new-synth x dis) (elaboration-generality-synth x)) TCHole1


module UntypingPairs where

open import Relation.Binary.PropositionalEquality

open import Function using (id)
open import Data.Empty
open import Data.Unit
open import Relation.Nullary
open import Data.Nat
open import Data.Fin
open import Data.Vec
open import Data.Sum
open import Data.Product
open import Data.List
open import Data.Maybe

-- Rather tedious --- stuff grows quadratically.  Get this to go
-- through and then see if we can generalize to codes for constructors
-- or something like that.

------------------------------------------------------------
-- Types
------------------------------------------------------------

-- A universe of types for STLC + natural numbers + pairs.
data Type : Set where
  Nat : Type
  _⇒_ : Type → Type → Type
  _⊗_ : Type → Type → Type

infixr 80 _⇒_
infixr 90 _⊗_

-- The function type constructor is injective.
⇒-inj : ∀ {τ₁ τ₂ τ₃ τ₄} → (τ₁ ⇒ τ₂ ≡ τ₃ ⇒ τ₄) → (τ₁ ≡ τ₃) × (τ₂ ≡ τ₄)
⇒-inj refl = refl , refl

⊗-inj : ∀ {τ₁ τ₂ τ₃ τ₄} → (τ₁ ⊗ τ₂ ≡ τ₃ ⊗ τ₄) → (τ₁ ≡ τ₃) × (τ₂ ≡ τ₄)
⊗-inj refl = refl , refl

-- Equality of types is decidable.
_≡?_ : (τ₁ τ₂ : Type) → (τ₁ ≡ τ₂) ⊎ (τ₁ ≢ τ₂)
Nat ≡? Nat = inj₁ refl
Nat ≡? (_ ⇒ _) = inj₂ (λ ())
Nat ≡? (_ ⊗ _) = inj₂ (λ ())
(_ ⇒ _) ≡? Nat = inj₂ (λ ())
(_ ⇒ _) ≡? (_ ⊗ _) = inj₂ (λ ())
(_ ⊗ _) ≡? Nat = inj₂ (λ ())
(_ ⊗ _) ≡? (_ ⇒ _) = inj₂ (λ ())
(τ₁ ⇒ τ₂) ≡? (τ₃ ⇒ τ₄) with τ₁ ≡? τ₃ | τ₂ ≡? τ₄
(τ₁ ⇒ τ₂) ≡? (.τ₁ ⇒ .τ₂) | inj₁ refl | inj₁ refl = inj₁ refl
(τ₁ ⇒ τ₂) ≡? (τ₃ ⇒ τ₄) | inj₂ τ₁≢τ₃ | _ = inj₂ (λ eq → τ₁≢τ₃ (proj₁ (⇒-inj eq)))
(τ₁ ⇒ τ₂) ≡? (τ₃ ⇒ τ₄) | _ | inj₂ τ₂≢τ₄ = inj₂ (λ eq → τ₂≢τ₄ (proj₂ (⇒-inj eq)))
(τ₁ ⊗ τ₂) ≡? (τ₃ ⊗ τ₄) with τ₁ ≡? τ₃ | τ₂ ≡? τ₄
(τ₁ ⊗ τ₂) ≡? (.τ₁ ⊗ .τ₂) | inj₁ refl | inj₁ refl = inj₁ refl
(τ₁ ⊗ τ₂) ≡? (τ₃ ⊗ τ₄) | inj₂ τ₁≢τ₃ | _ = inj₂ (λ x → τ₁≢τ₃ (proj₁ (⊗-inj x)))
(τ₁ ⊗ τ₂) ≡? (τ₃ ⊗ τ₄) | _ | inj₂ τ₂≢τ₄ = inj₂ (λ eq → τ₂≢τ₄ (proj₂ (⊗-inj eq)))

≢-cong-⇒ : ∀ {τ₁ τ₂ τ₃ τ₄} → (τ₁ ⇒ τ₂ ≢ τ₃ ⇒ τ₄) → (τ₁ ≢ τ₃) ⊎ (τ₂ ≢ τ₄)
≢-cong-⇒ {τ₁} {τ₂} {τ₃} {τ₄} neq with τ₁ ≡? τ₃ | τ₂ ≡? τ₄
≢-cong-⇒ neq | inj₂ τ₁≢τ₃ | _ = inj₁ τ₁≢τ₃
≢-cong-⇒ neq | _ | inj₂ τ₂≢τ₄ = inj₂ τ₂≢τ₄
≢-cong-⇒ neq | inj₁ refl | inj₁ refl = ⊥-elim (neq refl)

≢-cong-⊗ : ∀ {τ₁ τ₂ τ₃ τ₄} → (τ₁ ⊗ τ₂ ≢ τ₃ ⊗ τ₄) → (τ₁ ≢ τ₃) ⊎ (τ₂ ≢ τ₄)
≢-cong-⊗ {τ₁} {τ₂} {τ₃} {τ₄} neq with τ₁ ≡? τ₃ | τ₂ ≡? τ₄
≢-cong-⊗ neq | inj₂ τ₁≢τ₃ | _ = inj₁ τ₁≢τ₃
≢-cong-⊗ neq | _ | inj₂ τ₂≢τ₄ = inj₂ τ₂≢τ₄
≢-cong-⊗ neq | inj₁ refl | inj₁ refl = ⊥-elim (neq refl)

-- A type of explicit evidence explaining *why* two types are unequal.
data _≁_ : Type → Type → Set where
  Nat≁⇒ : ∀ {τ₁ τ₂} → Nat ≁ (τ₁ ⇒ τ₂)
  Nat≁⊗ : ∀ {τ₁ τ₂} → Nat ≁ (τ₁ ⊗ τ₂)
  ⇒≁⊗   : ∀ {τ₁ τ₂ τ₃ τ₄} → (τ₁ ⇒ τ₂) ≁ (τ₃ ⊗ τ₄)
  ⇒ˡ-≁  : ∀ {τ₁ τ₂ τ₃ τ₄} → τ₁ ≁ τ₂ → (τ₁ ⇒ τ₃) ≁ (τ₂ ⇒ τ₄)
  ⇒ʳ-≁  : ∀ {τ₁ τ₂ τ₃ τ₄} → τ₃ ≁ τ₄ → (τ₁ ⇒ τ₃) ≁ (τ₂ ⇒ τ₄)
  ⊗ˡ-≁  : ∀ {τ₁ τ₂ τ₃ τ₄} → τ₁ ≁ τ₂ → (τ₁ ⊗ τ₃) ≁ (τ₂ ⊗ τ₄)
  ⊗ʳ-≁  : ∀ {τ₁ τ₂ τ₃ τ₄} → τ₃ ≁ τ₄ → (τ₁ ⊗ τ₃) ≁ (τ₂ ⊗ τ₄)

  ≁-sym   : ∀ {τ₁ τ₂} → τ₁ ≁ τ₂ → τ₂ ≁ τ₁

-- Given such a proof we can show the types are unequal.
≁-≢ : ∀ {τ₁ τ₂} → (τ₁ ≁ τ₂) → (τ₁ ≢ τ₂)
≁-≢ Nat≁⇒ = λ ()
≁-≢ Nat≁⊗ = λ ()
≁-≢ ⇒≁⊗ = λ ()
≁-≢ (⇒ˡ-≁ τ₁≁τ₂)  refl = ≁-≢ τ₁≁τ₂ refl
≁-≢ (⇒ʳ-≁ τ₃≁τ₄)  refl = ≁-≢ τ₃≁τ₄ refl
≁-≢ (⊗ˡ-≁ τ₁≁τ₂)  refl = ≁-≢ τ₁≁τ₂ refl
≁-≢ (⊗ʳ-≁ τ₃≁τ₄)  refl = ≁-≢ τ₃≁τ₄ refl
≁-≢ (≁-sym τ₂≁τ₁) refl = ≁-≢ τ₂≁τ₁ refl

-- Since our universe of types is closed, we can actually go the other
-- way too. That is, ≢ is equivalent to ≁ ; the point is that the
-- latter is more immediately informative (by pattern-matching etc.)
-- which can be used to produce error messages and so on.
--
-- Note, however, that there might be *multiple* terms of type τ₁ ≁
-- τ₂: each corresponds to a different explanation of why the types
-- are not equal.  We might actually care which one we have.
-- Round-tripping through (τ₁ ≢ τ₂) is not the identity.

≢-≁ : ∀ {τ₁ τ₂} → (τ₁ ≢ τ₂) → (τ₁ ≁ τ₂)
≢-≁ {Nat} {Nat} τ₁≢τ₂ with τ₁≢τ₂ refl
... | ()
≢-≁ {Nat} {τ₂ ⇒ τ₃} _ = Nat≁⇒
≢-≁ {Nat} {τ₂ ⊗ τ₃} _ = Nat≁⊗
≢-≁ {τ₁ ⇒ τ₂} {Nat} _ = ≁-sym Nat≁⇒
≢-≁ {τ₁ ⇒ τ₂} {τ₃ ⊗ τ₄} _ = ⇒≁⊗
≢-≁ {τ₁ ⊗ τ₂} {Nat} _ = ≁-sym Nat≁⊗
≢-≁ {τ₁ ⊗ τ₂} {τ₃ ⇒ τ₄} _ = ≁-sym ⇒≁⊗
≢-≁ {τ₁ ⇒ τ₂} {τ₃ ⇒ τ₄} τ₁⇒τ₂≢τ₃⇒τ₄ with ≢-cong-⇒ τ₁⇒τ₂≢τ₃⇒τ₄
≢-≁ {τ₁ ⇒ τ₂} {τ₃ ⇒ τ₄} τ₁⇒τ₂≢τ₃⇒τ₄ | inj₁ τ₁≢τ₃ = ⇒ˡ-≁ (≢-≁ τ₁≢τ₃)
≢-≁ {τ₁ ⇒ τ₂} {τ₃ ⇒ τ₄} τ₁⇒τ₂≢τ₃⇒τ₄ | inj₂ τ₂≢τ₄ = ⇒ʳ-≁ (≢-≁ τ₂≢τ₄)
≢-≁ {τ₁ ⊗ τ₂} {τ₃ ⊗ τ₄} τ₁⊗τ₂≢τ₃⊗τ₄ with ≢-cong-⊗ τ₁⊗τ₂≢τ₃⊗τ₄
≢-≁ {τ₁ ⊗ τ₂} {τ₃ ⊗ τ₄} τ₁⊗τ₂≢τ₃⊗τ₄ | inj₁ τ₁≢τ₃ = ⊗ˡ-≁ (≢-≁ τ₁≢τ₃)
≢-≁ {τ₁ ⊗ τ₂} {τ₃ ⊗ τ₄} τ₁⊗τ₂≢τ₃⊗τ₄ | inj₂ τ₂≢τ₄ = ⊗ʳ-≁ (≢-≁ τ₂≢τ₄)

-- Sometimes it's convenient to decide equality of types using ≁ in place of ≢.
_∼?_ : (τ₁ τ₂ : Type) → (τ₁ ≡ τ₂) ⊎ (τ₁ ≁ τ₂)
τ₁ ∼? τ₂ = Data.Sum.map id ≢-≁ (τ₁ ≡? τ₂)

-- Evidence that a type is not an arrow type.
data not⇒_ : Type → Set where
  not⇒Nat : not⇒ Nat
  not⇒⊗   : ∀ {τ₁ τ₂} → not⇒ (τ₁ ⊗ τ₂)

data not⊗_ : Type → Set where
  not⊗Nat : not⊗ Nat
  not⊗⇒   : ∀ {τ₁ τ₂} → not⊗ (τ₁ ⇒ τ₂)

⇒? : (τ : Type) → (Σ[ τ₁ ∈ Type ] Σ[ τ₂ ∈ Type ] τ ≡ τ₁ ⇒ τ₂) ⊎ (not⇒ τ)
⇒? Nat       = inj₂ not⇒Nat
⇒? (_ ⊗ _) = inj₂ not⇒⊗
⇒? (τ₁ ⇒ τ₂) = inj₁ (τ₁ , τ₂ , refl)

------------------------------------------------------------
-- Expressions
------------------------------------------------------------

-- (Untyped) expressions of STLC + arithmetic + pairs.
data Expr (n : ℕ) : Set where
  lit : ℕ → Expr n
  _⊕_ : Expr n → Expr n → Expr n

  ⟨_,_⟩ : Expr n → Expr n → Expr n
  π₁ : Expr n
  π₂ : Expr n

  var : Fin n → Expr n
  ƛ   : Type → Expr (suc n) → Expr n
  _·_ : Expr n → Expr n → Expr n

Ctx : ℕ → Set
Ctx n = Vec Type n

------------------------------------------------------------
-- Typing
------------------------------------------------------------

-- Typing derivations.
data _⊢_∶_ : ∀ {n} → Ctx n → Expr n → Type → Set where
  lit  : ∀ {n} {Γ : Ctx n} {m}
       → Γ ⊢ lit m ∶ Nat
  _⊕_  : ∀ {n} {Γ : Ctx n} {t₁ t₂}
       → Γ ⊢ t₁        ∶ Nat
       → Γ ⊢ t₂        ∶ Nat
       → Γ ⊢ (t₁ ⊕ t₂) ∶ Nat
  ⟨_,_⟩ : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₁ τ₂}
       → Γ ⊢ t₁        ∶ τ₁
       → Γ ⊢ t₂        ∶ τ₂
       → Γ ⊢ ⟨ t₁ , t₂ ⟩ ∶ τ₁ ⊗ τ₂
  π₁   : ∀ {n} {Γ : Ctx n} {τ₁ τ₂}
       → Γ ⊢ π₁ ∶ (τ₁ ⊗ τ₂) ⇒ τ₁
  π₂   : ∀ {n} {Γ : Ctx n} {τ₁ τ₂}
       → Γ ⊢ π₂ ∶ (τ₁ ⊗ τ₂) ⇒ τ₂
  var  : ∀ {n} {Γ : Ctx n} {i}
       → Γ ⊢ var i ∶ lookup i Γ
  ƛ    : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ₂}
       → (τ₁ ∷ Γ) ⊢ t ∶ τ₂
       → Γ ⊢ ƛ τ₁ t ∶ (τ₁ ⇒ τ₂)
  _·_  : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₁ τ₂}
       → Γ ⊢ t₁ ∶ τ₁ ⇒ τ₂
       → Γ ⊢ t₂ ∶ τ₁
       → Γ ⊢ t₁ · t₂ ∶ τ₂

-- Explicit, constructive evidence for the *untypability* of a term.
data _⊬_∶_ : ∀ {n} → Ctx n → Expr n → Type → Set where

  -- Explicitly build in uniqueness of typing as an axiom.  t is not
  -- typeable at type τ₂ if t is typeable at some different type.
  mismatch : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ₂}
             → Γ ⊢ t ∶ τ₁
             → τ₁ ≁ τ₂
             → Γ ⊬ t ∶ τ₂

  -- There are three ways for a + term to fail to have a given type τ:
  -- either the left or right sides do not have type Nat, or the type
  -- τ itself is not Nat.
  ⊕ˡ       : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ}
             → Γ ⊬ t₁ ∶ Nat
             → Γ ⊬ (t₁ ⊕ t₂) ∶ τ
  ⊕ʳ       : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ}
             → Γ ⊬ t₂ ∶ Nat
             → Γ ⊬ (t₁ ⊕ t₂) ∶ τ
  ⊕≁Nat    : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ}
             → τ ≁ Nat → Γ ⊬ (t₁ ⊕ t₂) ∶ τ

  ⟨⟩-ty    : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ}
             → not⊗ τ → Γ ⊬ ⟨ t₁ , t₂ ⟩ ∶ τ
  ⟨⟩ˡ      : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₁ τ₂}
             → Γ ⊬ t₁ ∶ τ₁
             → Γ ⊬ ⟨ t₁ , t₂ ⟩ ∶ τ₁ ⊗ τ₂
  ⟨⟩ʳ      : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₁ τ₂}
             → Γ ⊬ t₂ ∶ τ₂
             → Γ ⊬ ⟨ t₁ , t₂ ⟩ ∶ τ₁ ⊗ τ₂

  π₁-⇒     : ∀ {n} {Γ : Ctx n} {τ}
             → not⇒ τ
             → Γ ⊬ π₁ ∶ τ
  π₁-⊗     : ∀ {n} {Γ : Ctx n} {τ₁ τ₂}
             → not⊗ τ₁
             → Γ ⊬ π₁ ∶ τ₁ ⇒ τ₂
  π₁-∼     : ∀ {n} {Γ : Ctx n} {τ₁ τ₂ τ₃}
             → τ₁ ≁ τ₃
             → Γ ⊬ π₁ ∶ (τ₁ ⊗ τ₂) ⇒ τ₃

  π₂-⇒     : ∀ {n} {Γ : Ctx n} {τ}
             → not⇒ τ
             → Γ ⊬ π₂ ∶ τ
  π₂-⊗     : ∀ {n} {Γ : Ctx n} {τ₁ τ₂}
             → not⊗ τ₁
             → Γ ⊬ π₂ ∶ τ₁ ⇒ τ₂
  π₂-∼     : ∀ {n} {Γ : Ctx n} {τ₁ τ₂ τ₃}
             → τ₂ ≁ τ₃
             → Γ ⊬ π₂ ∶ (τ₁ ⊗ τ₂) ⇒ τ₃

  -- ƛ-funty holds if τ is not a function type at all, or if it is a
  -- function type whose input type is not τ₁.
  ƛ-funty  : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ}
             → (∀ {τ₂} → τ ≁ τ₁ ⇒ τ₂)
             → Γ ⊬ ƛ τ₁ t ∶ τ

  -- Otherwise, τ is of the form (τ₁ ⇒ τ₂) but the body t does not
  -- have type τ₂.  Note this could be either because t is not typable
  -- at all, or because it has some type other than τ₂.
  ƛ        : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ₂}
             → (τ₁ ∷ Γ) ⊬ t ∶ τ₂
             → Γ ⊬ ƛ τ₁ t ∶ (τ₁ ⇒ τ₂)

  -- Had this ƛ-resty constructor, but it turns out we don't need it:
  -- it is not used in inference or checking, and isn't needed to
  -- prove equivalence of ⊬ and ¬ ⊢ .  It handles *only* the case
  -- where t is typable but has a type different than the output type
  -- of the whole expression; but the ƛ constructor handles this case
  -- as well as the case where t is not typeable at all.

  -- ƛ-resty  : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ₂ τ₃}
  --            → (τ₁ ∷ Γ) ⊢ t ∶ τ₂
  --            → τ₂ ≁ τ₃
  --            → Γ ⊬ ƛ τ₁ t ∶ τ₁ ⇒ τ₃

  -- Finally, there are two cases when an application is not typeable.
  -- Either the function does not have an appropriate function type,
  -- or the argument does not have a type that matches the function's
  -- input type.
  ·-fun    : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₂}
             → (∀ {τ₁} → Γ ⊬ t₁ ∶ τ₁ ⇒ τ₂)
             → Γ ⊬ t₁ · t₂ ∶ τ₂
  ·-arg    : ∀ {n} {Γ : Ctx n} {t₁ t₂} {τ₁ τ₂}
             → Γ ⊢ t₁ ∶ τ₁ ⇒ τ₂
             → Γ ⊬ t₂ ∶ τ₁
             → Γ ⊬ t₁ · t₂ ∶ τ₂

-- ------------------------------------------------------------
-- -- Type inference and checking
-- ------------------------------------------------------------

-- -- Type inference for a term in a given context returns either a type
-- -- and a valid typing derivation, or a constructive proof that the
-- -- term has no type.  Note that in this system, ALL terms can be
-- -- inferred.  In a bidirectional system we would have to restrict this
-- -- to only take inferrable terms as inputs.
-- infer : ∀ {n} → (Γ : Ctx n) → (t : Expr n) → (∃ λ τ → Γ ⊢ t ∶ τ) ⊎ (∀ τ → Γ ⊬ t ∶ τ)
-- infer Γ (lit n)   = inj₁ (Nat , lit)
-- infer Γ (t₁ ⊕ t₂) with infer Γ t₁ | infer Γ t₂
-- infer Γ (t₁ ⊕ t₂) | inj₁ (Nat , Γ⊢t₁∶Nat) | inj₁ (Nat , Γ⊢t₂∶Nat) = inj₁ (Nat , (Γ⊢t₁∶Nat ⊕ Γ⊢t₂∶Nat))
-- infer Γ (t₁ ⊕ t₂) | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁⇒τ₂) | inj₁ _ = inj₂ (λ _ → ⊕ˡ (mismatch Γ⊢t₁∶τ₁⇒τ₂ (≁-sym Nat≁⇒)))
-- infer Γ (t₁ ⊕ t₂) | inj₁ _ | inj₁ (τ₃ ⇒ τ₄ , Γ⊢t₂∶τ₃⇒τ₄) = inj₂ (λ _ → ⊕ʳ (mismatch Γ⊢t₂∶τ₃⇒τ₄ (≁-sym Nat≁⇒)))
-- infer Γ (t₁ ⊕ t₂) | inj₂ Γ⊬t₁∶ | _ = inj₂ (λ _ → ⊕ˡ (Γ⊬t₁∶ Nat))
-- infer Γ (t₁ ⊕ t₂) | _ | inj₂ Γ⊬t₂∶ = inj₂ (λ _ → ⊕ʳ (Γ⊬t₂∶ Nat))
-- infer Γ (var i)   = inj₁ (lookup i Γ , var)
-- infer Γ (ƛ τ₁ t) with infer (τ₁ ∷ Γ) t
-- infer Γ (ƛ τ₁ t) | inj₁ (τ₂ , τ₁∷Γ⊢t∶τ₂) = inj₁ (τ₁ ⇒ τ₂ , ƛ τ₁∷Γ⊢t∶τ₂)
-- infer Γ (ƛ τ₁ t) | inj₂ τ₁∷Γ⊬t∶ = inj₂ lemma
--   where
--     lemma : (τ : Type) → Γ ⊬ ƛ τ₁ t ∶ τ
--     lemma Nat        = ƛ-funty Nat≁⇒
--     lemma (τ₁′ ⇒ τ₂) with τ₁′ ∼? τ₁
--     lemma (.τ₁ ⇒ τ₂) | inj₁ refl = ƛ (τ₁∷Γ⊬t∶ τ₂)
--     lemma (τ₁′ ⇒ τ₂) | inj₂ τ₁′≁τ₁ = ƛ-funty (λ {τ₃} → ⇒ˡ-≁ τ₁′≁τ₁)
-- infer Γ (t₁ · t₂) with infer Γ t₁ | infer Γ t₂
-- infer Γ (t₁ · t₂) | inj₁ (Nat , Γ⊢t₁∶Nat) | _ = inj₂ (λ _ → ·-fun (mismatch Γ⊢t₁∶Nat Nat≁⇒))
-- infer Γ (t₁ · t₂) | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁⇒τ₂) | inj₁ (τ₁′ , Γ⊢t₂∶τ₁′) with τ₁ ∼? τ₁′
-- infer Γ (t₁ · t₂) | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁⇒τ₂) | inj₁ (.τ₁ , Γ⊢t₂∶τ₁ ) | inj₁ refl = inj₁ (τ₂ , (Γ⊢t₁∶τ₁⇒τ₂ · Γ⊢t₂∶τ₁))
-- infer Γ (t₁ · t₂) | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁⇒τ₂) | inj₁ (τ₁′ , Γ⊢t₂∶τ₁′) | inj₂ τ₁≁τ₁′ = inj₂ lemma2
--   where
--     lemma2 : ∀ τ → Γ ⊬ t₁ · t₂ ∶ τ
--     lemma2 τ with τ ∼? τ₂
--     lemma2 τ | inj₁ τ≡τ₂ rewrite τ≡τ₂ = ·-arg Γ⊢t₁∶τ₁⇒τ₂ (mismatch Γ⊢t₂∶τ₁′ (≁-sym τ₁≁τ₁′))
--     lemma2 τ | inj₂ τ≁τ₂ = ·-fun (mismatch Γ⊢t₁∶τ₁⇒τ₂ (⇒ʳ-≁ (≁-sym τ≁τ₂)))
-- infer Γ (t₁ · t₂) | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁⇒τ₂) | inj₂ Γ⊬t₂ = inj₂ lemma3
--   where
--     lemma3 : ∀ τ → Γ ⊬ t₁ · t₂ ∶ τ
--     lemma3 τ with τ ∼? τ₂
--     lemma3 τ | inj₁ τ≡τ₂ rewrite τ≡τ₂ = ·-arg Γ⊢t₁∶τ₁⇒τ₂ (Γ⊬t₂ τ₁)
--     lemma3 τ | inj₂ τ≁τ₂ = ·-fun (mismatch Γ⊢t₁∶τ₁⇒τ₂ (⇒ʳ-≁ (≁-sym τ≁τ₂)))
-- infer Γ (t₁ · t₂) | inj₂ Γ⊬t₁∶ | _ = inj₂ (λ τ₂ → ·-fun (λ {τ₁} → Γ⊬t₁∶ (τ₁ ⇒ τ₂)))

-- -- Check whether a given term has a *given* type.
-- check : ∀ {n} → (Γ : Ctx n) → (t : Expr n) → (τ : Type) → (Γ ⊢ t ∶ τ) ⊎ (Γ ⊬ t ∶ τ)
-- check Γ (lit _)   (τ ⇒ τ₁) = inj₂ (mismatch lit Nat≁⇒)
-- check Γ (lit _)   Nat      = inj₁ lit
-- check Γ (_  ⊕ _ ) (τ ⇒ τ₁) = inj₂ (⊕≁Nat (≁-sym Nat≁⇒))
-- check Γ (t₁ ⊕ t₂) Nat with check Γ t₁ Nat | check Γ t₂ Nat
-- check Γ (t₁ ⊕ t₂) Nat | inj₁ Γ⊢t₁∶Nat | inj₁ Γ⊢t₂∶Nat = inj₁ (Γ⊢t₁∶Nat ⊕ Γ⊢t₂∶Nat)
-- check Γ (t₁ ⊕ t₂) Nat | inj₂ Γ⊬t₁∶Nat | _             = inj₂ (⊕ˡ Γ⊬t₁∶Nat)
-- check Γ (t₁ ⊕ t₂) Nat | _             | inj₂ Γ⊬t₂∶Nat = inj₂ (⊕ʳ Γ⊬t₂∶Nat)
-- check Γ (var i)   τ with τ ∼? lookup i Γ
-- check Γ (var i) τ | inj₁ τ≡iΓ rewrite τ≡iΓ = inj₁ var
-- check Γ (var i) τ | inj₂ τ≁iΓ = inj₂ (mismatch var (≁-sym τ≁iΓ))
-- check Γ (ƛ τ₁ t) Nat = inj₂ (ƛ-funty Nat≁⇒)
-- check Γ (ƛ τ₁ t) (τ ⇒ τ₂) with τ ∼? τ₁ | check (τ₁ ∷ Γ) t τ₂
-- check Γ (ƛ τ₁ t) (τ ⇒ τ₂) | inj₂ τ≁τ₁ | _ = inj₂ (ƛ-funty (⇒ˡ-≁ τ≁τ₁))
-- check Γ (ƛ τ₁ t) (.τ₁ ⇒ τ₂) | inj₁ refl | inj₂ τ₁∷Γ⊬t∶τ₂ = inj₂ (ƛ τ₁∷Γ⊬t∶τ₂)
-- check Γ (ƛ τ₁ t) (.τ₁ ⇒ τ₂) | inj₁ refl | inj₁ τ₁∷Γ⊢t∶τ₂ = inj₁ (ƛ τ₁∷Γ⊢t∶τ₂)
--   --- Note that in order to check an application we have to use type inference on t₁.
-- check Γ (t₁ · t₂) τ with infer Γ t₁
-- check Γ (t₁ · t₂) τ | inj₂ Γ⊬t₁∶ = inj₂ (·-fun (λ {τ₁} → Γ⊬t₁∶ (τ₁ ⇒ τ)))
-- check Γ (t₁ · t₂) τ | inj₁ (Nat , Γ⊢t₁∶τ₁) = inj₂ (·-fun (mismatch Γ⊢t₁∶τ₁ Nat≁⇒))
-- check Γ (t₁ · t₂) τ | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁) with τ ∼? τ₂
-- check Γ (t₁ · t₂) τ | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁) | inj₂ τ≁τ₂ = inj₂ (·-fun (mismatch Γ⊢t₁∶τ₁ (⇒ʳ-≁ (≁-sym τ≁τ₂))))
-- check Γ (t₁ · t₂) τ | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁) | inj₁ τ≡τ₂ rewrite τ≡τ₂ with check Γ t₂ τ₁
-- check Γ (t₁ · t₂) τ | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁) | inj₁ τ≡τ₂ | inj₂ Γ⊬t₂∶τ₁ = inj₂ (·-arg Γ⊢t₁∶τ₁ Γ⊬t₂∶τ₁)
-- check Γ (t₁ · t₂) τ | inj₁ (τ₁ ⇒ τ₂ , Γ⊢t₁∶τ₁) | inj₁ τ≡τ₂ | inj₁ Γ⊢t₂∶τ₁ = inj₁ (Γ⊢t₁∶τ₁ · Γ⊢t₂∶τ₁)

-- ------------------------------------------------------------
-- -- Correctness
-- ------------------------------------------------------------

-- -- The whole idea is that ⊬ is a more explicit/constructive, yet
-- -- equivalent, way to represent the negation of ⊢ .  We can actually
-- -- prove the equivalence.

-- -- First, straightforward induction on typing derivations shows that
-- -- we really do have unique types, as assumed by the 'mismatch'
-- -- constructor.

-- ⊢-unique : ∀ {n} {Γ : Ctx n} {t : Expr n} {τ₁ τ₂ : Type} → (Γ ⊢ t ∶ τ₁) → (Γ ⊢ t ∶ τ₂) → (τ₁ ≡ τ₂)
-- ⊢-unique lit lit = refl
-- ⊢-unique (_ ⊕ _) (_ ⊕ _) = refl
-- ⊢-unique var var = refl
-- ⊢-unique (ƛ Γ⊢t∶τ₁) (ƛ Γ⊢t∶τ₂) rewrite ⊢-unique Γ⊢t∶τ₁ Γ⊢t∶τ₂ = refl
-- ⊢-unique (Γ⊢t∶τ₁ · _) (Γ⊢t∶τ₂ · _) = proj₂ (⇒-inj (⊢-unique Γ⊢t∶τ₁ Γ⊢t∶τ₂))

-- -- Now we can do one direction of the equivalence.  This direction is
-- -- just induction over derivations, making use of uniqueness of
-- -- typing.
-- ⊬-¬⊢ : ∀ {n} {Γ : Ctx n} {t : Expr n} {τ : Type} → (Γ ⊬ t ∶ τ) → (¬ (Γ ⊢ t ∶ τ))

-- ⊬-¬⊢ (mismatch Γ⊢t∶τ₁ τ₁≁τ) Γ⊢t∶τ = ≁-≢ τ₁≁τ (⊢-unique Γ⊢t∶τ₁ Γ⊢t∶τ)

-- ⊬-¬⊢ (⊕ˡ Γ⊬t₁∶N)     (Γ⊢t₁∶N ⊕ _     ) = ⊬-¬⊢ Γ⊬t₁∶N Γ⊢t₁∶N
-- ⊬-¬⊢ (⊕ʳ Γ⊬t₂∶N)     (_      ⊕ Γ⊢t₂∶N) = ⊬-¬⊢ Γ⊬t₂∶N Γ⊢t₂∶N
-- ⊬-¬⊢ (⊕≁Nat τ≁N)     (_      ⊕ _     ) = ≁-≢ τ≁N refl

-- ⊬-¬⊢ (ƛ-funty τ≁τ₁⇒) (ƛ _)      = ≁-≢ τ≁τ₁⇒ refl
-- ⊬-¬⊢ (ƛ Γ⊬t∶τ₂)      (ƛ Γ⊢t∶τ₂) = ⊬-¬⊢ Γ⊬t∶τ₂ Γ⊢t∶τ₂

-- ⊬-¬⊢ (·-fun Γ⊬t₁) (Γ⊢t₁ · _) = ⊬-¬⊢ Γ⊬t₁ Γ⊢t₁
-- ⊬-¬⊢ (·-arg Γ⊢t₁∶τ₁⇒τ Γ⊬t₂∶τ) (Γ⊢t₁∶τ₂⇒τ · Γ⊢t₂)
--   rewrite proj₁ (⇒-inj (⊢-unique Γ⊢t₁∶τ₁⇒τ Γ⊢t₁∶τ₂⇒τ)) = ⊬-¬⊢ Γ⊬t₂∶τ Γ⊢t₂

-- -- The other direction follows straightforwardly from type checking.
-- ¬⊢-⊬ : ∀ {n} {Γ : Ctx n} {t : Expr n} {τ : Type} → (¬ (Γ ⊢ t ∶ τ)) → (Γ ⊬ t ∶ τ)
-- ¬⊢-⊬ {_} {Γ} {t} {τ} ¬Γ⊢t∶τ with check Γ t τ
-- ¬⊢-⊬ ¬Γ⊢t∶τ | inj₁ Γ⊢t∶τ = ⊥-elim (¬Γ⊢t∶τ Γ⊢t∶τ)
-- ¬⊢-⊬ ¬Γ⊢t∶τ | inj₂ Γ⊬t∶τ = Γ⊬t∶τ

-- ------------------------------------------------------------
-- -- Take 2
-- ------------------------------------------------------------

-- -- XXX these need to be dependent somehow?  e.g. consider the ƛ case
-- -- below.  If τ is not an arrow type the whole thing fails; but if it
-- -- does, we need to know what τ₁ and τ₂ are in order to refer to them
-- -- in the rest of the conditions.

-- data many : List (Set × Set) → Set where
--   empty : many []
--   here  : ∀ {Tₗ Tᵣ Ts} → Tₗ → many Ts → many ((Tₗ , Tᵣ) ∷ Ts)
--   there : ∀ {Tₗ Tᵣ Ts} → Tᵣ → many Ts → many ((Tₗ , Tᵣ) ∷ Ts)

-- data some : List (Set × Set) → Set where
--   here  : ∀ {Tₗ Tᵣ Ts} → Tₗ → many Ts → some ((Tₗ , Tᵣ) ∷ Ts)
--   there : ∀ {Tₗ Tᵣ Ts} → Tᵣ → some Ts → some ((Tₗ , Tᵣ) ∷ Ts)

-- -- Unique untypability; gives *every* possible reason that something
-- -- is not typeable.  There is no 'mismatch' constructor.

-- {-# NO_POSITIVITY_CHECK #-}
-- data _⊬₂_∶_ : ∀ {n} → Ctx n → Expr n → Type → Set where

--   lit : ∀ {n} {Γ : Ctx n} {m} {τ} → τ ≁ Nat → Γ ⊬₂ lit m ∶ τ

--   -- For t₁ ⊕ t₂ to be untypeable, at least one of three things must be wrong:
--   ⊕   : ∀ {n} {Γ : Ctx n} {t₁ t₂ τ}
--       → some
--         ( (Γ ⊬₂ t₁ ∶ Nat , Γ ⊢ t₁ ∶ Nat)     -- Either t₁ is well-typed or not
--         ∷ (Γ ⊬₂ t₂ ∶ Nat , Γ ⊢ t₂ ∶ Nat)     -- Either t₂ is well-typed or not
--         ∷ ((τ ≁ Nat) , (τ ≡ Nat))            -- τ is Nat or not
--         ∷ []
--         )
--       → Γ ⊬₂ t₁ ⊕ t₂ ∶ τ

--   var : ∀ {n} {Γ : Ctx n} {i τ}
--       → τ ≁ lookup i Γ
--       → Γ ⊬₂ var i ∶ τ

--   -- ƛ τ₁ t does not have type τ if
--   --   - τ is not an arrow type

--   -- ƛ   : ∀ {n} {Γ : Ctx n} {t} {τ₁ τ₂ τ}
--   --     → some
--   --       ( (τ ≁


------------------------------------------------------------------------------
-- Fair properties
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOTC.Program.ABP.Fair.PropertiesI where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Base.List.PropertiesI
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesI
open import FOTC.Data.Stream.Type
open import FOTC.Program.ABP.Fair.Type
open import FOTC.Program.ABP.Terms

------------------------------------------------------------------------------
-- Because a greatest post-fixed point is a fixed-point, then the Fair
-- predicate is also a pre-fixed point of the functional FairF, i.e.
--
-- FairF Fair ≤ Fair (see FOTC.Program.ABP.Fair).
Fair-in : ∀ {os} → ∃[ ft ] ∃[ os' ] F*T ft ∧ os ≡ ft ++ os' ∧ Fair os' →
          Fair os
Fair-in h = Fair-coind A h' h
  where
  A : D → Set
  A os = ∃[ ft ] ∃[ os' ] F*T ft ∧ os ≡ ft ++ os' ∧ Fair os'

  h' : ∀ {os} → A os → ∃[ ft ] ∃[ os' ] F*T ft ∧ os ≡ ft ++ os' ∧ A os'
  h' (ft , os' , FTft , prf , Fos') = ft , os' , FTft , prf , Fair-out Fos'

head-tail-Fair : ∀ {os} → Fair os → os ≡ T ∷ tail₁ os ∨ os ≡ F ∷ tail₁ os
head-tail-Fair {os} Fos with Fair-out Fos
... | .(T ∷ []) , os' , f*tnil , h , Fos' = inj₁ prf₃
  where
  prf₁ : os ≡ T ∷ [] ++ os'
  prf₁ = os              ≡⟨ h ⟩
         (T ∷ []) ++ os' ≡⟨ ++-∷ T [] os' ⟩
         T ∷ [] ++ os'   ∎

  prf₂ : tail₁ os ≡ [] ++ os'
  prf₂ = tail₁ os              ≡⟨ tailCong prf₁ ⟩
         tail₁ (T ∷ [] ++ os') ≡⟨ tail-∷ T ([] ++ os') ⟩
         [] ++ os'             ∎

  prf₃ : os ≡ T ∷ tail₁ os
  prf₃ = os             ≡⟨ prf₁ ⟩
         T ∷ [] ++ os'  ≡⟨ ∷-rightCong (sym prf₂) ⟩
         T ∷ tail₁ os   ∎

... | .(F ∷ ft) , os' , f*tcons {ft} FTft , h , Fos' =
  inj₂ prf₃
  where
  prf₁ : os ≡ F ∷ ft ++ os'
  prf₁ = os              ≡⟨ h ⟩
         (F ∷ ft) ++ os' ≡⟨ ++-∷ F ft os' ⟩
         F ∷ ft ++ os'   ∎

  prf₂ : tail₁ os ≡ ft ++ os'
  prf₂ = tail₁ os              ≡⟨ tailCong prf₁ ⟩
         tail₁ (F ∷ ft ++ os') ≡⟨ tail-∷ F (ft ++ os') ⟩
         ft ++ os'             ∎

  prf₃ : os ≡ F ∷ tail₁ os
  prf₃ = os             ≡⟨ prf₁ ⟩
         F ∷ ft ++ os'  ≡⟨ ∷-rightCong (sym prf₂) ⟩
         F ∷ tail₁ os   ∎

tail-Fair : ∀ {os} → Fair os → Fair (tail₁ os)
tail-Fair {os} Fos with Fair-out Fos
... | .(T ∷ []) , os' , f*tnil , h , Fos' =
  subst Fair (sym prf₂) Fos'
  where
  prf₁ : os ≡ T ∷ os'
  prf₁ = os              ≡⟨ h ⟩
         (T ∷ []) ++ os' ≡⟨ ++-∷ T [] os' ⟩
         T ∷ [] ++ os'   ≡⟨ ∷-rightCong (++-leftIdentity os') ⟩
         T ∷ os'         ∎

  prf₂ : tail₁ os ≡ os'
  prf₂ = tail₁ os        ≡⟨ tailCong prf₁ ⟩
         tail₁ (T ∷ os') ≡⟨ tail-∷ T os' ⟩
         os'             ∎

... | .(F ∷ ft) , os' , f*tcons {ft} FTft , h , Fos' =
    subst Fair (sym prf₂) (Fair-in (ft , os' , FTft , refl , Fos'))
  where
  prf₁ : os ≡ F ∷ ft ++ os'
  prf₁ = os              ≡⟨ h ⟩
         (F ∷ ft) ++ os' ≡⟨ ++-∷ F ft os' ⟩
         F ∷ ft ++ os'   ∎

  prf₂ : tail₁ os ≡ ft ++ os'
  prf₂ = tail₁ os              ≡⟨ tailCong prf₁ ⟩
         tail₁ (F ∷ ft ++ os') ≡⟨ tail-∷ F (ft ++ os') ⟩
         ft ++ os'             ∎

Fair→Stream : ∀ {os} → Fair os → Stream os
Fair→Stream Fos = Stream-coind A h Fos
  where
  A : D → Set
  A xs = Fair xs

  h : ∀ {os} → A os → ∃[ o' ] ∃[ os' ] os ≡ o' ∷ os' ∧ A os'
  h {os} As with head-tail-Fair As
  ... | inj₁ prf = T , tail₁ os , prf , tail-Fair As
  ... | inj₂ prf = F , tail₁ os , prf , tail-Fair As

F*T→List : ∀ {xs} → F*T xs → List xs
F*T→List f*tnil              = lcons T lnil
F*T→List (f*tcons {ft} FTft) = lcons F (F*T→List FTft)


-- If we try to naively extend the Kripke structure used for NbE of STLC,
-- we find that it is sound, but not complete.
--
-- The definition of semantic objects, which represent terms in normal form,
-- is not big enough to represent neutral terms of the coproduct types.
-- The problem is visible in the definition of `reflect`.

module STLC2.Kovacs.Normalisation.SoundNotComplete where

open import STLC2.Kovacs.NormalForm public

--------------------------------------------------------------------------------

-- (Tyᴺ)
infix 3 _⊩_
_⊩_ : 𝒞 → 𝒯 → Set

Γ ⊩ ⎵      = Γ ⊢ⁿᶠ ⎵

Γ ⊩ A ⇒ B = ∀ {Γ′} → (η : Γ′ ⊇ Γ) (a : Γ′ ⊩ A)
                     → Γ′ ⊩ B

Γ ⊩ A ⩕ B  = Γ ⊩ A × Γ ⊩ B

Γ ⊩ ⫪     = ⊤

Γ ⊩ ⫫     = ⊥

Γ ⊩ A ⩖ B  = Γ ⊩ A ⊎ Γ ⊩ B

-- (Conᴺ ; ∙ ; _,_)
infix 3 _⊩⋆_
data _⊩⋆_ : 𝒞 → 𝒞 → Set
  where
    ∅   : ∀ {Γ} → Γ ⊩⋆ ∅

    _,_ : ∀ {Γ Ξ A} → (ρ : Γ ⊩⋆ Ξ) (a : Γ ⊩ A)
                    → Γ ⊩⋆ Ξ , A

--------------------------------------------------------------------------------

-- (Tyᴺₑ)
acc : ∀ {A Γ Γ′} → Γ′ ⊇ Γ → Γ ⊩ A → Γ′ ⊩ A
acc {⎵}      η M = renⁿᶠ η M
acc {A ⇒ B} η f = λ η′ a → f (η ○ η′) a
acc {A ⩕ B}  η s = acc η (proj₁ s) , acc η (proj₂ s)
acc {⫪}     η s = tt
acc {⫫}     η s = elim⊥ s
acc {A ⩖ B}  η s = elim⊎ s (λ a → inj₁ (acc η a))
                           (λ b → inj₂ (acc η b))

-- (Conᴺₑ)
-- NOTE: _⬖_ = acc⋆
_⬖_ : ∀ {Γ Γ′ Ξ} → Γ ⊩⋆ Ξ → Γ′ ⊇ Γ → Γ′ ⊩⋆ Ξ
∅       ⬖ η = ∅
(ρ , a) ⬖ η = ρ ⬖ η , acc η a

--------------------------------------------------------------------------------

-- (∈ᴺ)
getᵥ : ∀ {Γ Ξ A} → Γ ⊩⋆ Ξ → Ξ ∋ A → Γ ⊩ A
getᵥ (ρ , a) zero    = a
getᵥ (ρ , a) (suc i) = getᵥ ρ i

-- (Tmᴺ)
eval : ∀ {Γ Ξ A} → Γ ⊩⋆ Ξ → Ξ ⊢ A → Γ ⊩ A
eval ρ (𝓋 i)         = getᵥ ρ i
eval ρ (ƛ M)         = λ η a → eval (ρ ⬖ η , a) M
eval ρ (M ∙ N)       = eval ρ M idₑ (eval ρ N)
eval ρ (M , N)       = eval ρ M , eval ρ N
eval ρ (π₁ M)        = proj₁ (eval ρ M)
eval ρ (π₂ M)        = proj₂ (eval ρ M)
eval ρ τ             = tt
eval ρ (φ M)         = elim⊥ (eval ρ M)
eval ρ (ι₁ M)        = inj₁ (eval ρ M)
eval ρ (ι₂ M)        = inj₂ (eval ρ M)
eval ρ (M ⁇ N₁ ∥ N₂) = elim⊎ (eval ρ M) (λ M₁ → eval (ρ , M₁) N₁)
                                        (λ M₂ → eval (ρ , M₂) N₂)

-- mutual
--   -- (qᴺ)
--   reify : ∀ {A Γ} → Γ ⊩ A → Γ ⊢ⁿᶠ A
--   reify {⎵}      M = M
--   reify {A ⇒ B} f = ƛ (reify (f (wkₑ idₑ) (reflect 0)))
--   reify {A ⩕ B}  s = reify (proj₁ s) , reify (proj₂ s)
--   reify {⫪}     s = τ
--   reify {⫫}     s = elim⊥ s
--   reify {A ⩖ B}  s = elim⊎ s (λ a → ι₁ (reify a))
--                              (λ b → ι₂ (reify b))

--   -- (uᴺ)
--   reflect : ∀ {A Γ} → Γ ⊢ⁿᵉ A → Γ ⊩ A
--   reflect {⎵}      M = ne M
--   reflect {A ⇒ B} M = λ η a → reflect (renⁿᵉ η M ∙ reify a)
--   reflect {A ⩕ B}  M = reflect (π₁ M) , reflect (π₂ M)
--   reflect {⫪}     M = tt
--   reflect {⫫}     M = {!!}
--   reflect {A ⩖ B}  M = {!!}

-- -- (uᶜᴺ)
-- idᵥ : ∀ {Γ} → Γ ⊩⋆ Γ
-- idᵥ {∅}     = ∅
-- idᵥ {Γ , A} = idᵥ ⬖ wkₑ idₑ , reflect 0

-- -- (nf)
-- nf : ∀ {Γ A} → Γ ⊢ A → Γ ⊢ⁿᶠ A
-- nf M = reify (eval idᵥ M)

--------------------------------------------------------------------------------


open import Level
open import Relation.Binary.PropositionalEquality
open import Relation.Binary using (Setoid)
import Function.Equality
import Relation.Binary.Reasoning.Setoid as SetoidR

import Categories.Category
import Categories.Functor
import Categories.Category.Instance.Setoids

import Categories.Category.Cocartesian

import SecondOrder.Arity
import SecondOrder.Signature
import SecondOrder.Metavariable
import SecondOrder.Term

module SecondOrder.VRenaming
  {ℓ}
  {𝔸 : SecondOrder.Arity.Arity}
  (Σ : SecondOrder.Signature.Signature ℓ 𝔸)
  where

  open SecondOrder.Signature.Signature Σ
  open SecondOrder.Metavariable Σ
  open SecondOrder.Term Σ

  -- a renaming maps variables between contexts in a type-preserving way
  _⇒ᵛ_ : ∀ (Γ Δ : VContext) → Set ℓ
  Γ ⇒ᵛ Δ = ∀ {A} → A ∈ Γ → A ∈ Δ

  infix 4 _⇒ᵛ_

  -- equality of renamings

  _≡ᵛ_ : ∀ {Γ Δ} (σ τ : Γ ⇒ᵛ Δ) → Set ℓ
  _≡ᵛ_ {Γ} σ τ = ∀ {A} (x : A ∈ Γ) → σ x ≡ τ x

  infixl 3 _≡ᵛ_

  ≡ᵛ-refl : ∀ {Γ Δ} {ρ : Γ ⇒ᵛ Δ} → ρ ≡ᵛ ρ
  ≡ᵛ-refl = λ x → refl

  ≡ᵛ-sym : ∀ {Γ Δ} {ρ ν : Γ ⇒ᵛ Δ}
          → ρ ≡ᵛ ν
          → ν ≡ᵛ ρ
  ≡ᵛ-sym eq x = sym (eq x)

  ≡ᵛ-trans : ∀ {Γ Δ} {ρ ν γ : Γ ⇒ᵛ Δ}
           → ρ ≡ᵛ ν
           → ν ≡ᵛ γ
           → ρ ≡ᵛ γ
  ≡ᵛ-trans eq1 eq2 x = trans (eq1 x) (eq2 x)

  -- renamings form a setoid

  renaming-setoid : ∀ (Γ Δ : VContext) → Setoid ℓ ℓ
  renaming-setoid Γ Δ =
    record
      { Carrier = Γ ⇒ᵛ Δ
      ;  _≈_ = λ ρ ν → ρ ≡ᵛ ν
      ; isEquivalence =
                      record
                        { refl = λ {ρ} x → ≡ᵛ-refl {Γ} {Δ} {ρ} x
                        ; sym = ≡ᵛ-sym
                        ; trans = ≡ᵛ-trans
                        }
      }

  -- renaming preserves equality of variables
  ρ-resp-≡ : ∀ {Γ Δ A} {x y : A ∈ Γ} {ρ : Γ ⇒ᵛ Δ} → x ≡ y → ρ x ≡ ρ y
  ρ-resp-≡ refl = refl

  -- the identity renaming

  idᵛ : ∀ {Γ : VContext} → Γ ⇒ᵛ Γ
  idᵛ x = x

  -- composition of renamings
  _∘ᵛ_ : ∀ {Γ Δ Ξ} → Δ ⇒ᵛ Ξ → Γ ⇒ᵛ Δ → Γ ⇒ᵛ Ξ
  (σ ∘ᵛ ρ) x = σ (ρ x)

  infix 7 _∘ᵛ_

  -- composition respects equality
  ∘ᵛ-resp-≡ᵛ : ∀ {Γ Δ Ξ} {τ₁ τ₂ : Δ ⇒ᵛ Ξ} {σ₁ σ₂ : Γ ⇒ᵛ Δ} →
                 τ₁ ≡ᵛ τ₂ → σ₁ ≡ᵛ σ₂ → τ₁ ∘ᵛ σ₁ ≡ᵛ τ₂ ∘ᵛ σ₂
  ∘ᵛ-resp-≡ᵛ {τ₁ = τ₁} {σ₂ = σ₂} ζ ξ x = trans (cong τ₁ (ξ x)) (ζ (σ₂ x))

  -- the identity is the unit

  identity-leftᵛ : ∀ {Γ Δ} {ρ : Γ ⇒ᵛ Δ} → idᵛ ∘ᵛ ρ ≡ᵛ ρ
  identity-leftᵛ ρ = refl

  identity-rightᵛ : ∀ {Γ Δ} {ρ : Γ ⇒ᵛ Δ} → ρ ∘ᵛ idᵛ ≡ᵛ ρ
  identity-rightᵛ ρ = refl

  -- composition is associative

  assocᵛ : ∀ {Γ Δ Ξ Ψ} {τ : Γ ⇒ᵛ Δ} {ρ : Δ ⇒ᵛ Ξ} {σ : Ξ ⇒ᵛ Ψ} →
             (σ ∘ᵛ ρ) ∘ᵛ τ ≡ᵛ σ ∘ᵛ (ρ ∘ᵛ τ)
  assocᵛ x = refl

  sym-assocᵛ : ∀ {Γ Δ Ξ Ψ} {τ : Γ ⇒ᵛ Δ} {ρ : Δ ⇒ᵛ Ξ} {σ : Ξ ⇒ᵛ Ψ} →
             σ ∘ᵛ (ρ ∘ᵛ τ) ≡ᵛ (σ ∘ᵛ ρ) ∘ᵛ τ
  sym-assocᵛ x = refl

  -- contexts and renamings form a category
  module _ where
    open Categories.Category

    VContexts : Category ℓ ℓ ℓ
    VContexts =
      record
        { Obj = VContext
        ; _⇒_ = _⇒ᵛ_
        ; _≈_ = _≡ᵛ_
        ; id = idᵛ
        ; _∘_ = _∘ᵛ_
        ; assoc = λ {_} {_} {_} {_} {f} {g} {h} {_} → assocᵛ {τ = f} {ρ = g} {σ = h}
        ; sym-assoc = λ {_} {_} {_} {_} {f} {g} {h} {_} → sym-assocᵛ {τ = f} {ρ = g} {σ = h}
        ; identityˡ = λ x → refl
        ; identityʳ = λ x → refl
        ; identity² = λ x → refl
        ; equiv = record { refl = λ {ρ} {_} → ≡ᵛ-refl {ρ = ρ} ; sym = ≡ᵛ-sym ; trans = ≡ᵛ-trans }
        ; ∘-resp-≈ = ∘ᵛ-resp-≡ᵛ
        }

  -- the coproduct structure of the category
  module _ where

    infixl 7 [_,_]ᵛ

    [_,_]ᵛ : ∀ {Γ Δ Ξ} → Γ ⇒ᵛ Ξ → Δ ⇒ᵛ Ξ → Γ ,, Δ ⇒ᵛ Ξ
    [ σ , τ ]ᵛ (var-inl x) = σ x
    [ σ , τ ]ᵛ (var-inr y) = τ y

    [,]ᵛ-resp-≡ᵛ : ∀ {Γ Δ Ξ} {ρ₁ ρ₂ : Γ ⇒ᵛ Ξ} {τ₁ τ₂ : Δ ⇒ᵛ Ξ} → ρ₁ ≡ᵛ ρ₂ → τ₁ ≡ᵛ τ₂ → [ ρ₁ , τ₁ ]ᵛ ≡ᵛ [ ρ₂ , τ₂ ]ᵛ
    [,]ᵛ-resp-≡ᵛ pρ pτ (var-inl x) = pρ x
    [,]ᵛ-resp-≡ᵛ pρ pτ (var-inr x) = pτ x

    inlᵛ : ∀ {Γ Δ} → Γ ⇒ᵛ Γ ,, Δ
    inlᵛ = var-inl

    inrᵛ : ∀ {Γ Δ} → Δ ⇒ᵛ Γ ,, Δ
    inrᵛ = var-inr

    uniqueᵛ : ∀ {Γ Δ Ξ} {τ : Γ ,, Δ ⇒ᵛ Ξ} {ρ : Γ ⇒ᵛ Ξ} {σ : Δ ⇒ᵛ Ξ}
              → τ ∘ᵛ inlᵛ ≡ᵛ ρ
              → τ ∘ᵛ inrᵛ ≡ᵛ σ
              → [ ρ , σ ]ᵛ ≡ᵛ τ
    uniqueᵛ ξ ζ (var-inl x) = sym (ξ x)
    uniqueᵛ ξ ζ (var-inr y) = sym (ζ y)

    uniqueᵛ² : ∀ {Γ Δ Ξ} {τ σ : Γ ,, Δ ⇒ᵛ Ξ}
              → τ ∘ᵛ inlᵛ ≡ᵛ σ ∘ᵛ inlᵛ
              → τ ∘ᵛ inrᵛ ≡ᵛ σ ∘ᵛ inrᵛ
              → τ ≡ᵛ σ
    uniqueᵛ² ξ ζ (var-inl x) = ξ x
    uniqueᵛ² ξ ζ (var-inr y) = ζ y

    Context-+ : Categories.Category.Cocartesian.BinaryCoproducts VContexts
    Context-+ =
      record {
        coproduct =
          λ {Γ Δ} →
          record
            { A+B = Γ ,, Δ
            ; i₁ = inlᵛ
            ; i₂ = inrᵛ
            ; [_,_] = [_,_]ᵛ
            ; inject₁ = λ x → refl
            ; inject₂ = λ x → refl
            ; unique = uniqueᵛ
            }
      }

  open Categories.Category.Cocartesian.BinaryCoproducts Context-+

  -- the renaming from the empty context

  inᵛ : ∀ {Γ} → ctx-empty ⇒ᵛ Γ
  inᵛ ()

  -- extension of a renaming is summing with identity
  ⇑ᵛ : ∀ {Γ Δ Ξ} → Γ ⇒ᵛ Δ → Γ ,, Ξ ⇒ᵛ Δ ,, Ξ
  ⇑ᵛ ρ = ρ +₁ idᵛ

  -- a renaming can also be extended on the right
  ʳ⇑ᵛ : ∀ {Γ Δ} → Γ ⇒ᵛ Δ → ∀ {Ξ} → Ξ ,, Γ ⇒ᵛ Ξ ,, Δ
  ʳ⇑ᵛ ρ = idᵛ +₁ ρ

  -- right extension of renamings commutes with right injection
  ʳ⇑ᵛ-comm-inrᵛ : ∀ {Γ Δ Ξ} (ρ : Γ ⇒ᵛ Δ)
    → (ʳ⇑ᵛ ρ {Ξ = Ξ}) ∘ᵛ (inrᵛ {Δ = Γ}) ≡ᵛ inrᵛ ∘ᵛ ρ
  ʳ⇑ᵛ-comm-inrᵛ ρ var-slot = refl
  ʳ⇑ᵛ-comm-inrᵛ ρ (var-inl x) = refl
  ʳ⇑ᵛ-comm-inrᵛ ρ (var-inr x) = refl

  -- left extension of renamings commutes with left injection
  ⇑ᵛ-comm-inlᵛ : ∀ {Γ Δ Ξ} (ρ : Γ ⇒ᵛ Δ) → (⇑ᵛ {Ξ = Ξ} ρ) ∘ᵛ (inlᵛ {Δ = Ξ}) ≡ᵛ inlᵛ ∘ᵛ ρ
  ⇑ᵛ-comm-inlᵛ ρ var-slot = refl
  ⇑ᵛ-comm-inlᵛ ρ (var-inl x) = refl
  ⇑ᵛ-comm-inlᵛ ρ (var-inr x) = refl

  -- the action of a renaming on terms
  module _ {Θ : MContext} where

    infix 6 [_]ᵛ_

    [_]ᵛ_ : ∀ {Γ Δ A} → Γ ⇒ᵛ Δ → Term Θ Γ A → Term Θ Δ A
    [ ρ ]ᵛ (tm-var x) = tm-var (ρ x)
    [ ρ ]ᵛ (tm-meta M ts) = tm-meta M (λ i → [ ρ ]ᵛ (ts i))
    [ ρ ]ᵛ (tm-oper f es) = tm-oper f (λ i → [ ⇑ᵛ ρ ]ᵛ (es i))

  -- The sum of identities is an identity
  idᵛ+idᵛ : ∀ {Γ Δ} → idᵛ {Γ = Γ} +₁ idᵛ {Γ = Δ} ≡ᵛ idᵛ {Γ = Γ ,, Δ}
  idᵛ+idᵛ (var-inl x) = refl
  idᵛ+idᵛ (var-inr y) = refl

  -- The action of a renaming respects equality of terms
  []ᵛ-resp-≈ : ∀ {Θ Γ Δ A} {s t : Term Θ Γ A} {ρ : Γ ⇒ᵛ Δ} → s ≈ t → [ ρ ]ᵛ s ≈ [ ρ ]ᵛ t
  []ᵛ-resp-≈ (≈-≡ refl) = ≈-≡ refl
  []ᵛ-resp-≈ (≈-meta ξ) = ≈-meta (λ i → []ᵛ-resp-≈ (ξ i))
  []ᵛ-resp-≈ (≈-oper ξ) = ≈-oper (λ i → []ᵛ-resp-≈ (ξ i))

  -- The action of a renaming respects equality of renamings
  []ᵛ-resp-≡ᵛ : ∀ {Θ Γ Δ A} {ρ τ : Γ ⇒ᵛ Δ} {t : Term Θ Γ A} → ρ ≡ᵛ τ → [ ρ ]ᵛ t ≈ [ τ ]ᵛ t
  []ᵛ-resp-≡ᵛ {t = tm-var x} ξ = ≈-≡ (cong tm-var (ξ x))
  []ᵛ-resp-≡ᵛ {t = tm-meta M ts} ξ = ≈-meta (λ i → []ᵛ-resp-≡ᵛ ξ)
  []ᵛ-resp-≡ᵛ {t = tm-oper f es} ξ = ≈-oper (λ i → []ᵛ-resp-≡ᵛ (+₁-cong₂ ξ ≡ᵛ-refl))

  -- The action of the identity is trival
  [idᵛ] : ∀ {Θ Γ A} {t : Term Θ Γ A} → [ idᵛ ]ᵛ t ≈ t
  [idᵛ] {t = tm-var x} = ≈-refl
  [idᵛ] {t = tm-meta M ts} = ≈-meta λ i → [idᵛ]
  [idᵛ] {t = tm-oper f es} = ≈-oper λ i → ≈-trans ([]ᵛ-resp-≡ᵛ idᵛ+idᵛ) [idᵛ]

  -- Extension respects composition
  ⇑ᵛ-resp-∘ᵛ : ∀ {Γ Δ Ξ Ψ} {ρ : Γ ⇒ᵛ Δ} {τ : Δ ⇒ᵛ Ξ} → ⇑ᵛ {Ξ = Ψ} (τ ∘ᵛ ρ) ≡ᵛ (⇑ᵛ τ) ∘ᵛ (⇑ᵛ ρ)
  ⇑ᵛ-resp-∘ᵛ (var-inl x) = refl
  ⇑ᵛ-resp-∘ᵛ (var-inr y) = refl

  -- Right extension respects composition
  ʳ⇑ᵛ-resp-∘ᵛ : ∀ {Γ Δ Ξ Ψ} {ρ : Γ ⇒ᵛ Δ} {τ : Δ ⇒ᵛ Ξ} → ʳ⇑ᵛ (τ ∘ᵛ ρ) {Ξ = Ψ} ≡ᵛ (ʳ⇑ᵛ τ) ∘ᵛ (ʳ⇑ᵛ ρ)
  ʳ⇑ᵛ-resp-∘ᵛ (var-inl x) = refl
  ʳ⇑ᵛ-resp-∘ᵛ (var-inr y) = refl

  -- The action of a renaming is functorial
  [∘ᵛ] : ∀ {Θ Γ Δ Ξ} {ρ : Γ ⇒ᵛ Δ} {τ : Δ ⇒ᵛ Ξ} {A} {t : Term Θ Γ A}
    → [ τ ∘ᵛ ρ ]ᵛ t ≈ [ τ ]ᵛ ([ ρ ]ᵛ t)
  [∘ᵛ] {t = tm-var x} = ≈-refl
  [∘ᵛ] {t = tm-meta M ts} = ≈-meta (λ i → [∘ᵛ])
  [∘ᵛ] {t = tm-oper f es} = ≈-oper (λ i → ≈-trans ([]ᵛ-resp-≡ᵛ ⇑ᵛ-resp-∘ᵛ) [∘ᵛ])

  ∘ᵛ-resp-ʳ⇑ᵛ : ∀ {Γ Δ Ξ Λ} {ρ : Γ ⇒ᵛ Δ} {τ : Δ ⇒ᵛ Ξ}
    → ʳ⇑ᵛ (τ ∘ᵛ ρ) {Ξ = Λ} ≡ᵛ ʳ⇑ᵛ τ ∘ᵛ ʳ⇑ᵛ ρ
  ∘ᵛ-resp-ʳ⇑ᵛ (var-inl x) = refl
  ∘ᵛ-resp-ʳ⇑ᵛ (var-inr y) = refl

  ∘ᵛ-resp-ʳ⇑ᵛ-term : ∀ {Θ Γ Δ Ξ Λ A} {ρ : Γ ⇒ᵛ Δ} {τ : Δ ⇒ᵛ Ξ} {t : Term Θ (Λ ,, Γ) A}
    → [ ʳ⇑ᵛ (τ ∘ᵛ ρ) {Ξ = Λ} ]ᵛ t ≈ [ ʳ⇑ᵛ τ ]ᵛ ([ ʳ⇑ᵛ ρ ]ᵛ t)
  ∘ᵛ-resp-ʳ⇑ᵛ-term {Θ} {Γ} {Δ} {Ξ} {Λ} {A} {ρ} {τ} {t = t} =
    let open SetoidR (Term-setoid Θ (Λ ,, Ξ) A) in
    begin
    [ ʳ⇑ᵛ (τ ∘ᵛ ρ) {Ξ = Λ} ]ᵛ t ≈⟨ []ᵛ-resp-≡ᵛ ∘ᵛ-resp-ʳ⇑ᵛ ⟩
    [ ʳ⇑ᵛ τ ∘ᵛ ʳ⇑ᵛ ρ ]ᵛ t ≈⟨ [∘ᵛ] ⟩
    [ ʳ⇑ᵛ τ ]ᵛ ([ ʳ⇑ᵛ ρ ]ᵛ t)
    ∎

  ʳ⇑ᵛ-comm-inrᵛ-term : ∀ {Θ Γ Δ Ξ A} {ρ : Γ ⇒ᵛ Δ} {t : Term Θ Γ A}
    → ([ ʳ⇑ᵛ ρ {Ξ = Ξ} ]ᵛ ([ inrᵛ {Δ = Γ} ]ᵛ t)) ≈ ([ inrᵛ ]ᵛ ([ ρ ]ᵛ t))
  ʳ⇑ᵛ-comm-inrᵛ-term {Θ} {Γ} {Δ} {Ξ} {A} {ρ} {t = t} =
    let open SetoidR (Term-setoid Θ (Ξ ,, Δ) A) in
    begin
    [ ʳ⇑ᵛ ρ ]ᵛ ([ inrᵛ ]ᵛ t) ≈⟨ ≈-sym [∘ᵛ] ⟩
    [ ʳ⇑ᵛ ρ ∘ᵛ var-inr ]ᵛ t ≈⟨ []ᵛ-resp-≡ᵛ (ʳ⇑ᵛ-comm-inrᵛ ρ) ⟩
    [ inrᵛ ∘ᵛ ρ ]ᵛ t ≈⟨ [∘ᵛ] ⟩
    [ inrᵛ ]ᵛ ([ ρ ]ᵛ t)
    ∎

  -- Forming terms over a given metacontext and sort is functorial in the context
  module _ {Θ : MContext} {A : sort} where
    open Categories.Functor
    open Categories.Category.Instance.Setoids

    Term-Functor : Functor VContexts (Setoids ℓ ℓ)
    Term-Functor =
      record
        { F₀ = λ Γ → Term-setoid Θ Γ A
        ; F₁ = λ ρ → record { _⟨$⟩_ = [ ρ ]ᵛ_ ; cong = []ᵛ-resp-≈ }
        ; identity = ≈-trans [idᵛ]
        ; homomorphism = λ ξ → ≈-trans ([]ᵛ-resp-≈ ξ) [∘ᵛ]
        ; F-resp-≈ = λ ζ ξ → ≈-trans ([]ᵛ-resp-≡ᵛ ζ) ([]ᵛ-resp-≈ ξ)
        }


{- Cubical Agda with K

This file demonstrates the incompatibility of the --cubical
and --with-K flags, relying on the well-known incosistency of K with
univalence.

The --safe flag can be used to prevent accidentally mixing such
incompatible flags.

-}

{-# OPTIONS --with-K #-}

module Cubical.WithK where

open import Cubical.Data.Equality
open import Cubical.Data.Bool
open import Cubical.Data.Empty

private
 variable
  ℓ : Level
  A : Type ℓ
  x y : A

uip : (prf : x ≡ x) → Path _ prf refl
uip refl i = refl

transport-uip : (prf : A ≡ A) → Path _ (transportPath (eqToPath prf) x) x
transport-uip {x = x} prf =
  compPath (congPath (λ p → transportPath (eqToPath p) x) (uip prf)) (transportRefl x)

transport-not : Path _ (transportPath (eqToPath (pathToEq notEq)) true) false
transport-not = congPath (λ a → transportPath a true) (eqToPath-pathToEq notEq)

false-true : Path _ false true
false-true = compPath (symPath transport-not) (transport-uip (pathToEq notEq))

absurd : (X : Type) → X
absurd X = transportPath (congPath sel false-true) true
  where
    sel : Bool → Type
    sel false = Bool
    sel true = X

inconsistency : ⊥
inconsistency = absurd ⊥


open import Data.List using ( List ; [] ; _∷_ )
open import Data.List.Any using ( here ; there )
open import Data.Product using ( _,_ ; proj₁ ; proj₂ )
open import Data.Sum using ( inj₁ ; inj₂ )
open import Relation.Binary.PropositionalEquality using
  ( refl ; sym ; trans ; subst₂ )
open import Relation.Unary using ( _∈_ ; _⊆_ )
open import Web.Semantic.DL.ABox using
  ( ABox ; ε ; _,_ ; _∼_ ; ⟨ABox⟩ ; Assertions
  ; ⟨ABox⟩-resp-id ; ⟨ABox⟩-resp-∘ ; ⟨ABox⟩-resp-∘² )
open import Web.Semantic.DL.ABox.Interp using ( Interp ; _,_ ; ⌊_⌋ ; ind ; _*_ )
open import Web.Semantic.DL.ABox.Interp.Morphism using ( _,_ )
open import Web.Semantic.DL.ABox.Model using
  ( _⊨a_ ; *-resp-⟨ABox⟩ ; ⊨a-resp-⊇ )
open import Web.Semantic.DL.Category.Object using
  ( Object ; _,_ ; IN ; fin ; iface )
open import Web.Semantic.DL.Category.Morphism using
  ( _⇒_ ; _,_ ; BN ; impl )
open import Web.Semantic.DL.Category.Tensor using ( _⊗_ )
open import Web.Semantic.DL.Category.Unit using ( I )
open import Web.Semantic.DL.Integrity using
  ( Unique ; Mediator ; Initial ; _⊕_⊨_ ; _,_ )
open import Web.Semantic.DL.KB using ( _,_ )
open import Web.Semantic.DL.KB.Model using ( _⊨_ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox ; _,_ )
open import Web.Semantic.DL.TBox.Interp using
  ( _⊨_≈_ ; ≈-refl ; ≈-sym ; ≈-trans )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( ≲-refl ; ≲-image )
open import Web.Semantic.DL.TBox.Model using ( _⊨t_ )
open import Web.Semantic.Util using
  ( id ; _∘_ ; False ; tt ; ⊎-swap ; ⊎-assoc ; ⊎-assoc⁻¹ ; ⊎-unit₁ ; ⊎-unit₂
  ; _∈ˡ_ ; _⊕_⊕_ ; inode ; bnode ; enode )

module Web.Semantic.DL.Category.Wiring {Σ : Signature} where

wires : ∀ {X Y} → (Y → X) → List Y → ABox Σ (X ⊕ False ⊕ Y)
wires f [] = ε
wires f (y ∷ ys) = (inode (f y) ∼ enode y , wires f ys)

wires-≈ : ∀ {X Y y ys I} (f : Y → X) → (y ∈ˡ ys) → (I ⊨a wires f ys) →
  (⌊ I ⌋ ⊨ ind I (inode (f y)) ≈ ind I (enode y))
wires-≈ f (here refl)  (x≈y , xs≈ys) = x≈y
wires-≈ f (there y∈ys) (x≈y , xs≈ys) = wires-≈ f y∈ys xs≈ys

wires-≈⁻¹ : ∀ {X Y I} (f : Y → X) →
  (∀ y → ⌊ I ⌋ ⊨ ind I (inode (f y)) ≈ ind I (enode y)) →
    ∀ ys → (I ⊨a wires f ys)
wires-≈⁻¹ f ∀y∙x≈y []       = tt
wires-≈⁻¹ f ∀y∙x≈y (y ∷ ys) = (∀y∙x≈y y , wires-≈⁻¹ f ∀y∙x≈y ys)

wired : ∀ {X Y} → (Y → X) → (X ⊕ False ⊕ Y) → X
wired f (inode x) = x
wired f (bnode ())
wired f (enode y) = f y

wired-init : ∀ {X Y} (f : Y → X) ys → (∀ y → y ∈ˡ ys) →
  (I : Interp Σ X) → (S : TBox Σ) → (⌊ I ⌋ ⊨t S) →
    (wired f * I ∈ Initial I (S , wires f ys))
wired-init f ys ∀y∙y∈ys (I , i) S I⊨S =
  ( (≲-refl I , λ x → ≈-refl I)
  , (I⊨S , wires-≈⁻¹ f (λ x → ≈-refl I) ys)
  , mediator ) where

  mediator : Mediator (I , i) (I , i ∘ wired f)
    (≲-refl I , λ x → ≈-refl I) (S , wires f ys)
  mediator (K , k) (I≲K , i≲k) (K⊨S , K⊨F) =
    ((I≲K , j≲k) , (λ x → ≈-refl K) , uniq ) where

    j≲k : ∀ x → K ⊨ ≲-image I≲K (i (wired f x)) ≈ k x
    j≲k (inode x) = i≲k x
    j≲k (bnode ())
    j≲k (enode y) = ≈-trans K (i≲k (f y)) (wires-≈ f (∀y∙y∈ys y) K⊨F)

    uniq : Unique (I , i) (I , i ∘ wired f) (K , k)
      (≲-refl I , λ x → ≈-refl I) (I≲K , i≲k)
    uniq J≲₁K J≲₂K I≲K≋I≲J≲₁K I≲K≋I≲J≲₂K x =
      ≈-trans K (≈-sym K (I≲K≋I≲J≲₁K x)) (I≲K≋I≲J≲₂K x)

wires✓ : ∀ {X Y ys} (f : Y → X) → (∀ y → y ∈ˡ ys) →
  (S T : TBox Σ) (A : ABox Σ X) (B : ABox Σ Y) →
    (Assertions (⟨ABox⟩ f B) ⊆ Assertions A) →
      (I : Interp Σ X) → (I ⊨ (S , T) , A) → (I ⊕ S , wires f ys ⊨ T , B)
wires✓ {X} {Y} {ys} f ∀y∙y∈ys S T A B B⊆A I I⊨STA =
  (J , J-init , (J⊨T , J⊨B)) where

  J : Interp Σ (X ⊕ False ⊕ Y)
  J = wired f * I

  J⊨T : ⌊ J ⌋ ⊨t T
  J⊨T = proj₂ (proj₁ I⊨STA)

  J⊨B : enode * J ⊨a B
  J⊨B = *-resp-⟨ABox⟩ f I B (⊨a-resp-⊇ I (⟨ABox⟩ f B) A B⊆A (proj₂ I⊨STA))

  J-init : J ∈ Initial I (S , wires f ys)
  J-init = wired-init f ys ∀y∙y∈ys I S (proj₁ (proj₁ I⊨STA))

wiring-impl : ∀ {S T : TBox Σ} → (A B : Object S T) (f : IN B → IN A) →
  ABox Σ (IN A ⊕ False ⊕ IN B)
wiring-impl A B f =
  wires f (proj₁ (fin B))

wiring-impl✓ : ∀ {S T : TBox Σ} → (A B : Object S T) (f : IN B → IN A) →
  (Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)) →
    ∀ I → (I ⊨ (S , T) , iface A) → (I ⊕ (S , wiring-impl A B f) ⊨ (T , iface B))
wiring-impl✓ {S} {T} (X , X∈Fin , A) (Y , Y∈Fin , B) f B⊆A =
  wires✓ f (proj₂ Y∈Fin) S T A B B⊆A

wiring : ∀ {S T : TBox Σ} → (A B : Object S T) (f : IN B → IN A) →
  (Assertions (⟨ABox⟩ f (iface B)) ⊆ Assertions (iface A)) →
    (A ⇒ B)
wiring A B f B⊆A =
  ( False , wiring-impl A B f , wiring-impl✓ A B f B⊆A )

id✓ : ∀ {S T : TBox Σ} → (A : Object S T) →
  (Assertions (⟨ABox⟩ id (iface A)) ⊆ Assertions (iface A))
id✓ A = subst₂ Assertions (⟨ABox⟩-resp-id (iface A)) refl

identity : ∀ {S T : TBox Σ} → (A : Object S T) → (A ⇒ A)
identity A = wiring A A id (id✓ A)

⊎-swap✓ : ∀ {S T : TBox Σ} → (A B : Object S T) →
  Assertions (⟨ABox⟩ ⊎-swap (iface (B ⊗ A))) ⊆ Assertions (iface (A ⊗ B))
⊎-swap✓ A B (inj₁ b∈B) =
  inj₂ (subst₂ Assertions (⟨ABox⟩-resp-∘ ⊎-swap inj₁ (iface B)) refl b∈B)
⊎-swap✓ A B (inj₂ a∈A) =
  inj₁ (subst₂ Assertions (⟨ABox⟩-resp-∘ ⊎-swap inj₂ (iface A)) refl a∈A)

symm : ∀ {S T : TBox Σ} → (A B : Object S T) → ((A ⊗ B) ⇒ (B ⊗ A))
symm A B = wiring (A ⊗ B) (B ⊗ A) ⊎-swap (⊎-swap✓ A B)

⊎-assoc⁻¹✓ : ∀ {S T : TBox Σ} → (A B C : Object S T) →
  (Assertions (⟨ABox⟩ ⊎-assoc⁻¹ (iface (A ⊗ (B ⊗ C))))) ⊆
    (Assertions (iface ((A ⊗ B) ⊗ C)))
⊎-assoc⁻¹✓ A B C (inj₁ a∈A) = inj₁ (inj₁ (subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘ ⊎-assoc⁻¹ inj₁ (iface A))
    (sym (⟨ABox⟩-resp-∘ inj₁ inj₁ (iface A))))
  refl a∈A))
⊎-assoc⁻¹✓ A B C (inj₂ (inj₁ b∈B)) = inj₁ (inj₂ (subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘² ⊎-assoc⁻¹ inj₂ inj₁ (iface B))
    (sym (⟨ABox⟩-resp-∘ inj₁ inj₂ (iface B))))
  refl b∈B))
⊎-assoc⁻¹✓ A B C (inj₂ (inj₂ c∈C)) = inj₂ (subst₂ Assertions
  (⟨ABox⟩-resp-∘² ⊎-assoc⁻¹ inj₂ inj₂ (iface C))
  refl c∈C)

assoc : ∀ {S T : TBox Σ} → (A B C : Object S T) →
  (((A ⊗ B) ⊗ C) ⇒ (A ⊗ (B ⊗ C)))
assoc A B C = wiring ((A ⊗ B) ⊗ C) (A ⊗ (B ⊗ C)) ⊎-assoc⁻¹ (⊎-assoc⁻¹✓ A B C)

⊎-assoc✓ : ∀ {S T : TBox Σ} → (A B C : Object S T) →
  (Assertions (⟨ABox⟩ ⊎-assoc (iface ((A ⊗ B) ⊗ C)))) ⊆
    (Assertions (iface (A ⊗ (B ⊗ C))))
⊎-assoc✓ A B C (inj₁ (inj₁ a∈A)) = inj₁ (subst₂ Assertions
  (⟨ABox⟩-resp-∘² ⊎-assoc inj₁ inj₁ (iface A))
  refl a∈A)
⊎-assoc✓ A B C (inj₁ (inj₂ b∈B)) = inj₂ (inj₁ (subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘² ⊎-assoc inj₁ inj₂ (iface B))
    (sym (⟨ABox⟩-resp-∘ inj₂ inj₁ (iface B))))
  refl b∈B))
⊎-assoc✓ A B C (inj₂ c∈C) = inj₂ (inj₂ (subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘ ⊎-assoc inj₂ (iface C))
    (sym (⟨ABox⟩-resp-∘ inj₂ inj₂ (iface C))))
  refl c∈C))

assoc⁻¹ : ∀ {S T : TBox Σ} → (A B C : Object S T) →
  ((A ⊗ (B ⊗ C)) ⇒ ((A ⊗ B) ⊗ C))
assoc⁻¹ A B C = wiring (A ⊗ (B ⊗ C)) ((A ⊗ B) ⊗ C) ⊎-assoc (⊎-assoc✓ A B C)

inj₂✓ : ∀ {S T : TBox Σ} → (A : Object S T) →
  (Assertions (⟨ABox⟩ inj₂ (iface A))) ⊆
    (Assertions (iface (I ⊗ A)))
inj₂✓ A = inj₂

unit₁ : ∀ {S T : TBox Σ} (A : Object S T) → ((I ⊗ A) ⇒ A)
unit₁ A = wiring (I ⊗ A) A inj₂ (inj₂✓ A)

⊎-unit₁✓ : ∀ {S T : TBox Σ} → (A : Object S T) →
  Assertions (⟨ABox⟩ ⊎-unit₁ (iface (I ⊗ A))) ⊆ Assertions (iface A)
⊎-unit₁✓ A (inj₁ ())
⊎-unit₁✓ A (inj₂ a∈A) = subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘ ⊎-unit₁ inj₂ (iface A)) (⟨ABox⟩-resp-id (iface A)))
  refl a∈A

unit₁⁻¹ : ∀ {S T : TBox Σ} (A : Object S T) → (A ⇒ (I ⊗ A))
unit₁⁻¹ A = wiring A (I ⊗ A) ⊎-unit₁ (⊎-unit₁✓ A)

inj₁✓ : ∀ {S T : TBox Σ} → (A : Object S T) →
  (Assertions (⟨ABox⟩ inj₁ (iface A))) ⊆
    (Assertions (iface (A ⊗ I)))
inj₁✓ A = inj₁

unit₂ : ∀ {S T : TBox Σ} (A : Object S T) → ((A ⊗ I) ⇒ A)
unit₂ A = wiring (A ⊗ I) A inj₁ (inj₁✓ A)

⊎-unit₂✓ : ∀ {S T : TBox Σ} (A : Object S T) →
  Assertions (⟨ABox⟩ ⊎-unit₂ (iface (A ⊗ I))) ⊆ Assertions (iface A)
⊎-unit₂✓ A (inj₁ a∈A) = subst₂ Assertions
  (trans (⟨ABox⟩-resp-∘ ⊎-unit₂ inj₁ (iface A)) (⟨ABox⟩-resp-id (iface A)))
  refl a∈A
⊎-unit₂✓ A (inj₂ ())

unit₂⁻¹ : ∀ {S T : TBox Σ} (A : Object S T) → (A ⇒ (A ⊗ I))
unit₂⁻¹ A = wiring A (A ⊗ I) ⊎-unit₂ (⊎-unit₂✓ A)