-- TODO
-- Gentzen-style formalisation of syntax.
-- Simple terms.

module BasicT.Syntax.Gentzen where

open import BasicT.Syntax.Common public

-- Derivations.

infix 3 _⊢_
data _⊢_ (Γ : Cx Ty) : Ty → Set where
  var   : ∀ {A}   → A ∈ Γ → Γ ⊢ A
  lam   : ∀ {A B} → Γ , A ⊢ B → Γ ⊢ A ▻ B
  app   : ∀ {A B} → Γ ⊢ A ▻ B → Γ ⊢ A → Γ ⊢ B
  pair  : ∀ {A B} → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ∧ B
  fst   : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ A
  snd   : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ B
  unit  : Γ ⊢ ⊤
  true  : Γ ⊢ BOOL
  false : Γ ⊢ BOOL
  if    : ∀ {C}   → Γ ⊢ BOOL → Γ ⊢ C → Γ ⊢ C → Γ ⊢ C
  zero  : Γ ⊢ NAT
  suc   : Γ ⊢ NAT → Γ ⊢ NAT
  it    : ∀ {C}   → Γ ⊢ NAT → Γ ⊢ C ▻ C → Γ ⊢ C → Γ ⊢ C
  rec   : ∀ {C}   → Γ ⊢ NAT → Γ ⊢ NAT ▻ C ▻ C → Γ ⊢ C → Γ ⊢ C

infix 3 _⊢⋆_
_⊢⋆_ : Cx Ty → Cx Ty → Set
Γ ⊢⋆ ∅     = 𝟙
Γ ⊢⋆ Ξ , A = Γ ⊢⋆ Ξ × Γ ⊢ A

-- Monotonicity with respect to context inclusion.

mono⊢ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ A → Γ′ ⊢ A
mono⊢ η (var i)     = var (mono∈ η i)
mono⊢ η (lam t)     = lam (mono⊢ (keep η) t)
mono⊢ η (app t u)   = app (mono⊢ η t) (mono⊢ η u)
mono⊢ η (pair t u)  = pair (mono⊢ η t) (mono⊢ η u)
mono⊢ η (fst t)     = fst (mono⊢ η t)
mono⊢ η (snd t)     = snd (mono⊢ η t)
mono⊢ η unit        = unit
mono⊢ η true        = true
mono⊢ η false       = false
mono⊢ η (if t u v)  = if (mono⊢ η t) (mono⊢ η u) (mono⊢ η v)
mono⊢ η zero        = zero
mono⊢ η (suc t)     = suc (mono⊢ η t)
mono⊢ η (it t u v)  = it (mono⊢ η t) (mono⊢ η u) (mono⊢ η v)
mono⊢ η (rec t u v) = rec (mono⊢ η t) (mono⊢ η u) (mono⊢ η v)

mono⊢⋆ : ∀ {Ξ Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢⋆ Ξ → Γ′ ⊢⋆ Ξ
mono⊢⋆ {∅}     η ∙        = ∙
mono⊢⋆ {Ξ , A} η (ts , t) = mono⊢⋆ η ts , mono⊢ η t

-- Shorthand for variables.

v₀ : ∀ {A Γ} → Γ , A ⊢ A
v₀ = var i₀

v₁ : ∀ {A B Γ} → Γ , A , B ⊢ A
v₁ = var i₁

v₂ : ∀ {A B C Γ} → Γ , A , B , C ⊢ A
v₂ = var i₂

-- Reflexivity.

refl⊢⋆ : ∀ {Γ} → Γ ⊢⋆ Γ
refl⊢⋆ {∅}     = ∙
refl⊢⋆ {Γ , A} = mono⊢⋆ weak⊆ refl⊢⋆ , v₀

-- Deduction theorem is built-in.

lam⋆ : ∀ {Ξ Γ A} → Γ ⧺ Ξ ⊢ A → Γ ⊢ Ξ ▻⋯▻ A
lam⋆ {∅}     = I
lam⋆ {Ξ , B} = lam⋆ {Ξ} ∘ lam

lam⋆₀ : ∀ {Γ A} → Γ ⊢ A → ∅ ⊢ Γ ▻⋯▻ A
lam⋆₀ {∅}     = I
lam⋆₀ {Γ , B} = lam⋆₀ ∘ lam

-- Detachment theorem.

det : ∀ {A B Γ} → Γ ⊢ A ▻ B → Γ , A ⊢ B
det t = app (mono⊢ weak⊆ t) v₀

det⋆ : ∀ {Ξ Γ A} → Γ ⊢ Ξ ▻⋯▻ A → Γ ⧺ Ξ ⊢ A
det⋆ {∅}     = I
det⋆ {Ξ , B} = det ∘ det⋆ {Ξ}

det⋆₀ : ∀ {Γ A} → ∅ ⊢ Γ ▻⋯▻ A → Γ ⊢ A
det⋆₀ {∅}     = I
det⋆₀ {Γ , B} = det ∘ det⋆₀

-- Cut and multicut.

cut : ∀ {A B Γ} → Γ ⊢ A → Γ , A ⊢ B → Γ ⊢ B
cut t u = app (lam u) t

multicut : ∀ {Ξ A Γ} → Γ ⊢⋆ Ξ → Ξ ⊢ A → Γ ⊢ A
multicut {∅}     ∙        u = mono⊢ bot⊆ u
multicut {Ξ , B} (ts , t) u = app (multicut ts (lam u)) t

-- Transitivity.

trans⊢⋆ : ∀ {Γ″ Γ′ Γ} → Γ ⊢⋆ Γ′ → Γ′ ⊢⋆ Γ″ → Γ ⊢⋆ Γ″
trans⊢⋆ {∅}      ts ∙        = ∙
trans⊢⋆ {Γ″ , A} ts (us , u) = trans⊢⋆ ts us , multicut ts u

-- Contraction.

ccont : ∀ {A B Γ} → Γ ⊢ (A ▻ A ▻ B) ▻ A ▻ B
ccont = lam (lam (app (app v₁ v₀) v₀))

cont : ∀ {A B Γ} → Γ , A , A ⊢ B → Γ , A ⊢ B
cont t = det (app ccont (lam (lam t)))

-- Exchange, or Schönfinkel’s C combinator.

cexch : ∀ {A B C Γ} → Γ ⊢ (A ▻ B ▻ C) ▻ B ▻ A ▻ C
cexch = lam (lam (lam (app (app v₂ v₀) v₁)))

exch : ∀ {A B C Γ} → Γ , A , B ⊢ C → Γ , B , A ⊢ C
exch t = det (det (app cexch (lam (lam t))))

-- Composition, or Schönfinkel’s B combinator.

ccomp : ∀ {A B C Γ} → Γ ⊢ (B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
ccomp = lam (lam (lam (app v₂ (app v₁ v₀))))

comp : ∀ {A B C Γ} → Γ , B ⊢ C → Γ , A ⊢ B → Γ , A ⊢ C
comp t u = det (app (app ccomp (lam t)) (lam u))

-- Useful theorems in combinatory form.

ci : ∀ {A Γ} → Γ ⊢ A ▻ A
ci = lam v₀

ck : ∀ {A B Γ} → Γ ⊢ A ▻ B ▻ A
ck = lam (lam v₁)

cs : ∀ {A B C Γ} → Γ ⊢ (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
cs = lam (lam (lam (app (app v₂ v₀) (app v₁ v₀))))

cpair : ∀ {A B Γ} → Γ ⊢ A ▻ B ▻ A ∧ B
cpair = lam (lam (pair v₁ v₀))

cfst : ∀ {A B Γ} → Γ ⊢ A ∧ B ▻ A
cfst = lam (fst v₀)

csnd : ∀ {A B Γ} → Γ ⊢ A ∧ B ▻ B
csnd = lam (snd v₀)

-- Closure under context concatenation.

concat : ∀ {A B Γ} Γ′ → Γ , A ⊢ B → Γ′ ⊢ A → Γ ⧺ Γ′ ⊢ B
concat Γ′ t u = app (mono⊢ (weak⊆⧺₁ Γ′) (lam t)) (mono⊢ weak⊆⧺₂ u)

-- Substitution.

[_≔_]_ : ∀ {A B Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢ B → Γ ∖ i ⊢ B
[ i ≔ s ] var j     with i ≟∈ j
[ i ≔ s ] var .i    | same   = s
[ i ≔ s ] var ._    | diff j = var j
[ i ≔ s ] lam t     = lam ([ pop i ≔ mono⊢ weak⊆ s ] t)
[ i ≔ s ] app t u   = app ([ i ≔ s ] t) ([ i ≔ s ] u)
[ i ≔ s ] pair t u  = pair ([ i ≔ s ] t) ([ i ≔ s ] u)
[ i ≔ s ] fst t     = fst ([ i ≔ s ] t)
[ i ≔ s ] snd t     = snd ([ i ≔ s ] t)
[ i ≔ s ] unit      = unit
[ i ≔ s ] true      = true
[ i ≔ s ] false     = false
[ i ≔ s ] if t u v  = if ([ i ≔ s ] t) ([ i ≔ s ] u) ([ i ≔ s ] v)
[ i ≔ s ] zero      = zero
[ i ≔ s ] suc t     = suc ([ i ≔ s ] t)
[ i ≔ s ] it t u v  = it ([ i ≔ s ] t) ([ i ≔ s ] u) ([ i ≔ s ] v)
[ i ≔ s ] rec t u v = rec ([ i ≔ s ] t) ([ i ≔ s ] u) ([ i ≔ s ] v)

[_≔_]⋆_ : ∀ {Ξ A Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢⋆ Ξ → Γ ∖ i ⊢⋆ Ξ
[_≔_]⋆_ {∅}     i s ∙        = ∙
[_≔_]⋆_ {Ξ , B} i s (ts , t) = [ i ≔ s ]⋆ ts , [ i ≔ s ] t

-- Convertibility.

data _⋙_ {Γ : Cx Ty} : ∀ {A} → Γ ⊢ A → Γ ⊢ A → Set where
  refl⋙        : ∀ {A} → {t : Γ ⊢ A}
                        → t ⋙ t

  trans⋙       : ∀ {A} → {t t′ t″ : Γ ⊢ A}
                        → t ⋙ t′ → t′ ⋙ t″
                        → t ⋙ t″

  sym⋙         : ∀ {A} → {t t′ : Γ ⊢ A}
                        → t ⋙ t′
                        → t′ ⋙ t

  conglam⋙     : ∀ {A B} → {t t′ : Γ , A ⊢ B}
                          → t ⋙ t′
                          → lam t ⋙ lam t′

  congapp⋙     : ∀ {A B} → {t t′ : Γ ⊢ A ▻ B} → {u u′ : Γ ⊢ A}
                          → t ⋙ t′ → u ⋙ u′
                          → app t u ⋙ app t′ u′

  congpair⋙    : ∀ {A B} → {t t′ : Γ ⊢ A} → {u u′ : Γ ⊢ B}
                          → t ⋙ t′ → u ⋙ u′
                          → pair t u ⋙ pair t′ u′

  congfst⋙     : ∀ {A B} → {t t′ : Γ ⊢ A ∧ B}
                          → t ⋙ t′
                          → fst t ⋙ fst t′

  congsnd⋙     : ∀ {A B} → {t t′ : Γ ⊢ A ∧ B}
                          → t ⋙ t′
                          → snd t ⋙ snd t′

  congsuc⋙     : ∀ {t t′ : Γ ⊢ NAT} → t ⋙ t′
                                      → suc t ⋙ suc t′

  congif⋙      : ∀ {C} → {t t′ : Γ ⊢ BOOL} → {u u′ : Γ ⊢ C} → {v v′ : Γ ⊢ C}
                        → t ⋙ t′
                        → if t u v ⋙ if t′ u′ v′

  congit⋙      : ∀ {C} → {t t′ : Γ ⊢ NAT} → {u u′ : Γ ⊢ C ▻ C} → {v v′ : Γ ⊢ C}
                        → t ⋙ t′
                        → it t u v ⋙ it t′ u′ v′

  congrec⋙     : ∀ {C} → {t t′ : Γ ⊢ NAT} → {u u′ : Γ ⊢ NAT ▻ C ▻ C} → {v v′ : Γ ⊢ C}
                        → t ⋙ t′
                        → rec t u v ⋙ rec t′ u′ v′

  beta▻⋙       : ∀ {A B} → {t : Γ , A ⊢ B} → {u : Γ ⊢ A}
                          → app (lam t) u ⋙ ([ top ≔ u ] t)

  eta▻⋙        : ∀ {A B} → {t : Γ ⊢ A ▻ B}
                          → t ⋙ lam (app (mono⊢ weak⊆ t) v₀)

  beta∧₁⋙      : ∀ {A B} → {t : Γ ⊢ A} → {u : Γ ⊢ B}
                          → fst (pair t u) ⋙ t

  beta∧₂⋙      : ∀ {A B} → {t : Γ ⊢ A} → {u : Γ ⊢ B}
                          → snd (pair t u) ⋙ u

  eta∧⋙        : ∀ {A B} → {t : Γ ⊢ A ∧ B}
                          → t ⋙ pair (fst t) (snd t)

  eta⊤⋙       : ∀ {t : Γ ⊢ ⊤} → t ⋙ unit

  betaBOOL₁⋙   : ∀ {C} → {u v : Γ ⊢ C}
                        → if true u v ⋙ u

  betaBOOL₂⋙   : ∀ {C} → {u v : Γ ⊢ C}
                        → if false u v ⋙ v

  betaNATit₁⋙  : ∀ {C} → {u : Γ ⊢ C ▻ C} → {v : Γ ⊢ C}
                        → it zero u v ⋙ v

  -- TODO: Verify this.
  betaNATit₂⋙  : ∀ {C} → {t : Γ ⊢ NAT} → {u : Γ ⊢ C ▻ C} → {v : Γ ⊢ C}
                        → it (suc t) u v ⋙ app u (it t u v)

  betaNATrec₁⋙ : ∀ {C} → {u : Γ ⊢ NAT ▻ C ▻ C} → {v : Γ ⊢ C}
                        → rec zero u v ⋙ v

  -- TODO: Verify this.
  betaNATrec₂⋙ : ∀ {C} → {t : Γ ⊢ NAT} → {u : Γ ⊢ NAT ▻ C ▻ C} → {v : Γ ⊢ C}
                        → rec (suc t) u v ⋙ app (app u t) (rec t u v)

  -- TODO: What about eta for BOOL and NAT?


module Esterel.Lang.Properties where

open import Esterel.Lang
open import Relation.Binary.PropositionalEquality
open import Relation.Unary
open import Relation.Nullary using (¬_ ; Dec ; yes ; no)
open import Esterel.Environment
open import Esterel.Variable.Signal as Signal
open import Esterel.Variable.Shared as SharedVar
open import Esterel.Context
  using (EvaluationContext ; EvaluationContext1 ; _⟦_⟧e ; _≐_⟦_⟧e ;
         Context ; Context1 ; _⟦_⟧c ; _≐_⟦_⟧c)
open import Esterel.Context.Properties

open import Data.Empty
  using (⊥ ; ⊥-elim)
open import Data.Nat
  using (ℕ ; zero ; suc) renaming (_⊔_ to _⊔ℕ_)
open import Data.MoreNatProp
  using (⊔-sym)
open import Data.Product
  using (Σ-syntax ; Σ ; _,_ ; _,′_ ; proj₁ ; proj₂ ; _×_)
open import Data.Sum
  using (_⊎_ ; inj₁ ; inj₂)
open import Data.List
open import Data.Maybe
open import Data.List.Any
open import Data.List.All
open import Data.OrderedListMap
open import Function using (_$_)

import Data.OrderedListMap(Signal)(Signal.unwrap)(Signal.Status) as SigOMap
import Data.OrderedListMap(SharedVar)(SharedVar.unwrap)(SharedVar.Status × ℕ) as ShrOMap

open ≡-Reasoning using (begin_ ; _≡⟨_⟩_ ; _∎)

open EvaluationContext1
open _≐_⟦_⟧e
open Context1
open _≐_⟦_⟧c

data halted : Term → Set where
  hnothin :           halted nothin
  hexit   : (n : ℕ) → halted (exit n)

data paused : Term → Set where
  ppause   :                     paused pause
  pseq     : ∀{p q} → paused p → paused (p >> q)
  ploopˢ   : ∀{p q} → paused p → paused (loopˢ p q)
  ppar     : ∀{p q} → paused p → paused q → paused (p ∥ q)
  psuspend : ∀{p S} → paused p → paused (suspend p S)
  ptrap    : ∀{p}   → paused p → paused (trap p)

data done : Term → Set where
  dhalted : ∀{p} → (p/halted : halted p) → done p
  dpaused : ∀{p} → (p/paused : paused p) → done p

data complete-θ : Env → Set where
  θcomplete : ∀{θ} →
                (∀ S → (S∈ : isSig∈ S θ) → ((sig-stats{S} θ S∈ ≡ Signal.absent) ⊎ (sig-stats{S} θ S∈ ≡ Signal.present))) →
                (∀ s → (s∈ : isShr∈ s θ) → (shr-stats{s} θ s∈ ≡ SharedVar.ready)) →
                ---------------
                complete-θ θ

data complete : Term → Set where
  codone : ∀{p} → done p → complete p
  coenv : ∀{θ A p} → complete-θ θ → done p → complete (ρ⟨ θ , A ⟩· p)

-- Halted and paused programs are disjoint
halted-paused-disjoint : ∀{p} → halted p → paused p → ⊥
halted-paused-disjoint hnothin   ()
halted-paused-disjoint (hexit _) ()

↓_ : ∀ {p} → halted p → Term
↓ hnothin = nothin
↓ hexit zero = nothin
↓ hexit (suc n) = exit n

↓-well-behaved : ∀ p -> (haltedp : halted p) -> halted (↓ haltedp)
↓-well-behaved .nothin hnothin = hnothin
↓-well-behaved .(exit 0) (hexit zero) = hnothin
↓-well-behaved .(exit (suc n)) (hexit (suc n)) = hexit n

A-max : Ctrl → Ctrl → Ctrl
A-max GO   GO   = GO
A-max GO   WAIT = GO
A-max WAIT GO   = GO
A-max WAIT WAIT = WAIT

value-max : ∀{p q} → done p → done q → (halted p ⊎ halted q) →  Term
value-max {q = q} (dhalted hnothin)   done-q              h⊎h = q
value-max {p = p} done-p              (dhalted hnothin)   h⊎h = p
value-max         (dhalted (hexit n)) (dhalted (hexit m)) h⊎h = exit (n ⊔ℕ m)
value-max         (dhalted (hexit n)) (dpaused _)         h⊎h = exit n
value-max         (dpaused _)         (dhalted (hexit m)) h⊎h = exit m
value-max         (dpaused paused-p)  (dpaused paused-q)  (inj₁ halted-p) =
  ⊥-elim (halted-paused-disjoint halted-p paused-p)
value-max         (dpaused paused-p)  (dpaused paused-q)  (inj₂ halted-q) =
  ⊥-elim (halted-paused-disjoint halted-q paused-q)

value-max-unique : ∀{p q d1 d2 d3 d4 ij1 ij2} → value-max{p}{q} d1 d2 ij1 ≡ value-max{p}{q} d3 d4 ij2
value-max-unique {d1 = dhalted hnothin}   {d2}                {dhalted hnothin}    {d4}                 {ij1} {ij2} = refl
value-max-unique {d1 = dhalted (hexit n)} {dhalted hnothin}   {dhalted (hexit .n)} {dhalted hnothin}    {ij1} {ij2} = refl
value-max-unique {d1 = dpaused p/paused}  {dhalted hnothin}   {dpaused p/paused'}  {dhalted hnothin}    {ij1} {ij2} = refl
value-max-unique {d1 = dhalted (hexit n)} {dhalted (hexit m)} {dhalted (hexit .n)} {dhalted (hexit .m)} {ij1} {ij2} = refl
value-max-unique {d1 = dhalted (hexit n)} {dpaused q/paused}  {dhalted (hexit .n)} {dpaused q/paused'}  {ij1} {ij2} = refl
value-max-unique {d1 = dpaused p/paused}  {dhalted (hexit n)} {dpaused p/paused'}  {dhalted (hexit .n)} {ij1} {ij2} = refl
value-max-unique {d1 = dpaused p/paused}  {dpaused q/paused}  {dpaused p/paused'}  {dpaused q/paused'} {inj₁ p/halted} {ij2} =
  ⊥-elim (halted-paused-disjoint p/halted p/paused')
value-max-unique {d1 = dpaused p/paused} {dpaused q/paused}   {dpaused p/paused'}  {dpaused q/paused'} {inj₂ q/halted} {ij2} =
  ⊥-elim (halted-paused-disjoint q/halted q/paused')
value-max-unique {d1 = dhalted p/halted} {d2} {dpaused p/paused'} {d4} {ij1} {ij2} =
  ⊥-elim (halted-paused-disjoint p/halted p/paused')
value-max-unique {d1 = dpaused p/paused} {d2} {dhalted p/halted'} {d4} {ij1} {ij2} =
  ⊥-elim (halted-paused-disjoint p/halted' p/paused)
value-max-unique {d1 = d1} {dhalted q/halted} {d3} {dpaused q/paused} {ij1} {ij2} =
  ⊥-elim (halted-paused-disjoint q/halted q/paused)
value-max-unique {d1 = d1} {dpaused q/paused}  {d3} {dhalted q/halted} {ij1} {ij2} =
  ⊥-elim (halted-paused-disjoint q/halted q/paused)

value-max-sym : ∀ {p q} ->
  (donep : done p) ->
  (haltedq : halted q) ->
  value-max donep (dhalted haltedq) (inj₂ haltedq) ≡
  value-max (dhalted haltedq) donep (inj₁ haltedq)
value-max-sym (dhalted hnothin) hnothin = refl
value-max-sym (dhalted hnothin) (hexit n) = refl
value-max-sym (dhalted (hexit n)) hnothin = refl
value-max-sym (dhalted (hexit n)) (hexit m)
  rewrite ⊔-sym n m = refl
value-max-sym (dpaused p/paused) hnothin = refl
value-max-sym (dpaused p/paused) (hexit n) = refl

value-max-done : ∀ {p q} →
  (p/done : done p) →
  (q/done : done q) →
  (p/halted⊎q/halted : halted p ⊎ halted q) →
  done (value-max p/done q/done p/halted⊎q/halted)
value-max-done (dhalted hnothin)   q/done              ⟨p⊎q⟩/halted = q/done
value-max-done (dhalted (hexit n)) (dhalted hnothin)   ⟨p⊎q⟩/halted = dhalted (hexit n)
value-max-done (dhalted (hexit n)) (dhalted (hexit m)) ⟨p⊎q⟩/halted = dhalted (hexit (n ⊔ℕ m))
value-max-done (dhalted (hexit n)) (dpaused q/paused)  ⟨p⊎q⟩/halted = dhalted (hexit n)
value-max-done (dpaused p/paused)  (dhalted hnothin)   ⟨p⊎q⟩/halted = dpaused p/paused
value-max-done (dpaused p/paused)  (dhalted (hexit m)) ⟨p⊎q⟩/halted = dhalted (hexit m)
value-max-done (dpaused p/paused)  (dpaused q/paused)  (inj₁ p/halted) =
  ⊥-elim (halted-paused-disjoint p/halted p/paused)
value-max-done (dpaused p/paused)  (dpaused q/paused)  (inj₂ q/halted) =
  ⊥-elim (halted-paused-disjoint q/halted q/paused)

{-
Properties relating halted, paused, done programs and the contexts. There's no way to
decompose halted programs, so the result must remain halted. The decomposition of paused
programs under the evaluation context must result in paused programs.

    halted-⟦⟧c : ∀{p p' C} → halted p → p ≐ C ⟦ p' ⟧c → halted p'
    paused-⟦⟧e : ∀{p p' E} → paused p → p ≐ E ⟦ p' ⟧e → paused p'
-}
halted-⟦⟧c : ∀{p p' C} → halted p → p ≐ C ⟦ p' ⟧c → halted p'
halted-⟦⟧c hnothin dchole = hnothin
halted-⟦⟧c (hexit n) dchole = hexit n

-- In fact, the only valid case would be E ≡ [] and p ≡ p'
halted-⟦⟧e : ∀{p p' E} → halted p → p ≐ E ⟦ p' ⟧e → halted p'
halted-⟦⟧e p/halted p≐E⟦p'⟧ = halted-⟦⟧c p/halted (⟦⟧e-to-⟦⟧c p≐E⟦p'⟧)

paused-⟦⟧e : ∀{p p' E} → paused p → p ≐ E ⟦ p' ⟧e → paused p'
paused-⟦⟧e ppause dehole = ppause
paused-⟦⟧e (pseq paused) dehole = pseq paused
paused-⟦⟧e (pseq paused) (deseq d) = paused-⟦⟧e paused d
paused-⟦⟧e (ploopˢ paused) dehole = ploopˢ paused
paused-⟦⟧e (ploopˢ paused) (deloopˢ d) = paused-⟦⟧e paused d
paused-⟦⟧e (ppar paused₁ paused₂) dehole = ppar paused₁ paused₂
paused-⟦⟧e (ppar paused₁ paused₂) (depar₁ d) = paused-⟦⟧e paused₁ d
paused-⟦⟧e (ppar paused₁ paused₂) (depar₂ d) = paused-⟦⟧e paused₂ d
paused-⟦⟧e (psuspend paused) dehole = psuspend paused
paused-⟦⟧e (psuspend paused) (desuspend d) = paused-⟦⟧e paused d
paused-⟦⟧e (ptrap paused) dehole = ptrap paused
paused-⟦⟧e (ptrap paused) (detrap d) = paused-⟦⟧e paused d

done-⟦⟧e : ∀ {p p' E} → done p → p ≐ E ⟦ p' ⟧e → done p'
done-⟦⟧e (dhalted p/halted) p≐E⟦p'⟧ = dhalted (halted-⟦⟧e p/halted p≐E⟦p'⟧)
done-⟦⟧e (dpaused p/paused) p≐E⟦p'⟧ = dpaused (paused-⟦⟧e p/paused p≐E⟦p'⟧)

halted-dec : ∀ p -> Dec (halted p)
halted-dec nothin = yes hnothin
halted-dec pause = no (λ ())
halted-dec (signl S p) = no (λ ())
halted-dec (present S ∣⇒ p ∣⇒ p₁) = no (λ ())
halted-dec (emit S) = no (λ ())
halted-dec (p ∥ q) = no (λ ())
halted-dec (loop p) = no (λ ())
halted-dec (loopˢ p q) = no (λ ())
halted-dec (p >> q) = no (λ ())
halted-dec (suspend p S) = no (λ ())
halted-dec (trap p) = no (λ ())
halted-dec (exit x) = yes (hexit x)
halted-dec (shared s ≔ e in: p) = no (λ ())
halted-dec (s ⇐ e) = no (λ ())
halted-dec (var x ≔ e in: p) = no (λ ())
halted-dec (x ≔ e) = no (λ ())
halted-dec (if x ∣⇒ p ∣⇒ q) = no (λ ())
halted-dec (ρ⟨ θ , A ⟩· p) = no (λ ())

paused-dec : ∀ p -> Dec (paused p)
paused-dec nothin = no (λ ())
paused-dec pause = yes ppause
paused-dec (signl S p) = no (λ ())
paused-dec (present S ∣⇒ p ∣⇒ p₁) = no (λ ())
paused-dec (emit S) = no (λ ())
paused-dec (p ∥ q) with paused-dec p | paused-dec q
paused-dec (p ∥ q) | yes pausedp | yes pausedq = yes (ppar pausedp pausedq)
paused-dec (p ∥ q) | _ | no ¬pausedq = no (λ { (ppar _ pausedq) → ¬pausedq pausedq } )
paused-dec (p ∥ q) | no ¬pausedp | _ = no (λ { (ppar pausedp _) → ¬pausedp pausedp } )
paused-dec (loop p) = no (λ ())
paused-dec (loopˢ p q) with paused-dec p
paused-dec (loopˢ p q) | yes pausedp = yes (ploopˢ pausedp)
paused-dec (loopˢ p q) | no ¬pausedp = no (λ { (ploopˢ pausedp) → ¬pausedp pausedp })
paused-dec (p >> q) with paused-dec p
paused-dec (p >> q) | yes pausedp = yes (pseq pausedp)
paused-dec (p >> q) | no ¬pausedp = no (λ { (pseq pausedp) → ¬pausedp pausedp })
paused-dec (suspend p S) with paused-dec p
paused-dec (suspend p S) | yes pausedp = yes (psuspend pausedp)
paused-dec (suspend p S) | no ¬pausedp = no (λ { (psuspend pausedp) → ¬pausedp pausedp })
paused-dec (trap p) with paused-dec p
paused-dec (trap p) | yes pasuedp = yes (ptrap pasuedp)
paused-dec (trap p) | no ¬pausedp = no (λ { (ptrap pausedp) → ¬pausedp pausedp })
paused-dec (exit x) = no (λ ())
paused-dec (shared s ≔ e in: p) = no (λ ())
paused-dec (s ⇐ e) = no (λ ())
paused-dec (var x ≔ e in: p) = no (λ ())
paused-dec (x ≔ e) = no (λ ())
paused-dec (if x ∣⇒ p ∣⇒ q) = no (λ ())
paused-dec (ρ⟨ θ , A ⟩· p) = no (λ ())

done-dec : ∀ p → Dec (done p)
done-dec p with halted-dec p
done-dec p | yes halted-p = yes (dhalted halted-p)
done-dec p | no ¬halted-p with paused-dec p
done-dec p | no _ | yes paused-p = yes (dpaused paused-p)
done-dec p | no ¬halted-p | (no ¬paused-p) = no
  (λ { (dhalted p/halted) → ¬halted-p p/halted ;
       (dpaused p/paused) → ¬paused-p p/paused})

signal-absent-or-present : Signal.Status → Set
signal-absent-or-present S = S ≡ Signal.absent ⊎ S ≡ Signal.present

signal-absent-or-present-dec : Decidable signal-absent-or-present
signal-absent-or-present-dec present = yes (inj₂ refl)
signal-absent-or-present-dec absent = yes (inj₁ refl)
signal-absent-or-present-dec unknown = no (λ { (inj₁ ()) ; (inj₂ ())})

signals-all-absent-or-present-dec :
  (Ss :  SigMap.Map Signal.Status) ->
        Dec (∀ S → (S∈ : (SigMap.∈Dom S Ss)) →
              (signal-absent-or-present (SigMap.lookup{k = S} Ss S∈)))
signals-all-absent-or-present-dec m
   with SigOMap.andmap{_}{signal-absent-or-present} signal-absent-or-present-dec m
signals-all-absent-or-present-dec m | yes p = yes (λ S → p (Signal.unwrap S))
signals-all-absent-or-present-dec m | no ¬p = no (λ z → ¬p (λ n → z (n ₛ)))

var-is-ready : SharedVar.Status × ℕ → Set
var-is-ready p = (proj₁ p) ≡ SharedVar.ready

var-is-ready-dec : Decidable var-is-ready
var-is-ready-dec (ready , _) = yes refl
var-is-ready-dec (new , _) = no (λ ())
var-is-ready-dec (old , _) = no (λ ())

vars-all-ready-dec :
  (ss : ShrMap.Map (SharedVar.Status × ℕ)) ->
   Dec (∀ s → (s∈ : ShrMap.∈Dom s ss) →
        var-is-ready (ShrMap.lookup{k = s} ss s∈))
vars-all-ready-dec ss with ShrOMap.andmap{_}{var-is-ready} var-is-ready-dec ss
vars-all-ready-dec ss | yes p = yes (λ s → p (SharedVar.unwrap s))
vars-all-ready-dec ss | no ¬p = no (λ z → ¬p (λ n → z (n ₛₕ)))

complete-θ-dec : ∀ θ → Dec (complete-θ θ)
complete-θ-dec (Θ sigs shr _) with signals-all-absent-or-present-dec sigs
complete-θ-dec (Θ sigs shrs _) | yes p with vars-all-ready-dec shrs
complete-θ-dec (Θ sigs shrs _) | yes p₁ | yes p₂ = yes (θcomplete p₁ p₂)
complete-θ-dec (Θ sigs shrs _) | yes _ | no ¬p = no (λ { (θcomplete _ x) → ¬p x } )
complete-θ-dec (Θ sigs shrs _) | no ¬p = no (λ { (θcomplete x _) → ¬p x } )

complete-dec : ∀ p → Dec (complete p)
complete-dec p with done-dec p
complete-dec p | yes donep = yes (codone donep)
complete-dec nothin | no ¬donep = no (λ _ → ¬donep (dhalted hnothin))
complete-dec pause | no ¬donep = no (λ _ → ¬donep (dpaused ppause))
complete-dec (signl S p) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (present S ∣⇒ p ∣⇒ p₁) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (emit S) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (p ∥ p₁) | no ¬donep =  no (λ { (codone x) → ¬donep x } )
complete-dec (loop p) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (loopˢ p q) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (p >> p₁) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (suspend p S) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (trap p) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (exit x) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (shared s ≔ e in: p) | no ¬donep = no (λ { (codone x) → ¬donep x } )
complete-dec (s ⇐ e) | no ¬donep =  no (λ { (codone x) → ¬donep x } )
complete-dec (var x ≔ e in: p) | no ¬donep =  no (λ { (codone x) → ¬donep x } )
complete-dec (x ≔ e) | no ¬donep =  no (λ { (codone x) → ¬donep x } )
complete-dec (if x ∣⇒ p ∣⇒ p₁) | no ¬donep =  no (λ { (codone x) → ¬donep x } )
complete-dec (ρ⟨ θ , A ⟩· p) | _ with done-dec p
complete-dec (ρ⟨ θ , A ⟩· p) | _ | yes donep with complete-θ-dec θ
complete-dec (ρ⟨ θ , A ⟩· p) | _ | yes donep | (yes complete-θ) = yes (coenv complete-θ donep)
complete-dec (ρ⟨ θ , A ⟩· p) | _ | yes donep | (no ¬complete-θ) = no
   (λ { (codone (dhalted ())) ;
        (codone (dpaused ())) ;
        (coenv complete-θ _) → ¬complete-θ complete-θ })
complete-dec (ρ⟨ θ , A ⟩· p) | _ | no ¬donep = no
  (λ { (codone (dhalted ())) ;
       (codone (dpaused ())) ;
       (coenv _ donep) → ¬donep donep })

A-max-GO-≡-left : ∀ A → A-max GO A ≡ GO
A-max-GO-≡-left GO =  refl
A-max-GO-≡-left WAIT = refl

A-max-GO-≡-right : ∀ A → A-max A GO ≡ GO
A-max-GO-≡-right GO = refl
A-max-GO-≡-right WAIT = refl

A-max-comm : ∀ A1 A2 → A-max A1 A2 ≡ A-max A2 A1
A-max-comm GO GO = refl
A-max-comm GO WAIT = refl
A-max-comm WAIT GO = refl
A-max-comm WAIT WAIT = refl

A-max-assoc : ∀ A1 A2 A3 → (A-max A1 $ A-max A2 A3) ≡ A-max (A-max A1 A2) A3
A-max-assoc GO GO GO = refl
A-max-assoc GO GO WAIT = refl
A-max-assoc GO WAIT GO = refl
A-max-assoc GO WAIT WAIT = refl
A-max-assoc WAIT GO GO = refl
A-max-assoc WAIT GO WAIT = refl
A-max-assoc WAIT WAIT GO = refl
A-max-assoc WAIT WAIT WAIT = refl

A-max-swap : ∀ A1 A2 A3
             → (A-max (A-max A1 A2) A3) ≡ (A-max (A-max A1 A3) A2)
A-max-swap A1 A2 A3 = begin
                        (A-max (A-max A1 A2) A3) ≡⟨ sym (A-max-assoc A1 A2 A3) ⟩
                        (A-max A1 $ A-max A2 A3) ≡⟨ sym (cong (A-max A1) (A-max-comm A3 A2)) ⟩
                        (A-max A1 $ A-max A3 A2) ≡⟨ A-max-assoc A1 A3 A2 ⟩
                        (A-max (A-max A1 A3) A2) ∎


------------------------------------------------------------------------
-- An up-to technique for CCS
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Prelude hiding (step-→)

module Bisimilarity.Weak.Up-to.CCS {ℓ} {Name : Type ℓ} where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude.Size

open import Function-universe equality-with-J hiding (id; _∘_)

open import Bisimilarity.Weak.CCS
import Bisimilarity.Weak.Equational-reasoning-instances
open import Equational-reasoning
open import Indexed-container hiding (⟨_⟩)
open import Labelled-transition-system.CCS Name
open import Relation

open import Bisimilarity.Weak CCS
open import Bisimilarity.Weak.Up-to CCS
import Labelled-transition-system.Equational-reasoning-instances CCS
  as Dummy

-- Up to (non-degenerate) context for CCS (for polyadic, coinductive
-- contexts).

Up-to-context : Trans₂ ℓ (Proc ∞)
Up-to-context R (p , q) =
  ∃ λ n →
  ∃ λ (C : Context ∞ n) →
  Non-degenerate ∞ C
    ×
  ∃ λ ps →
  ∃ λ qs →
  p ≡ C [ ps ]
    ×
  q ≡ C [ qs ]
    ×
  ∀ x → R (ps x , qs x)

-- Up to context is monotone.

up-to-context-monotone : Monotone Up-to-context
up-to-context-monotone R⊆S =
  Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $
  Σ-map id (R⊆S ∘_)

-- Up to context is size-preserving.

up-to-context-size-preserving : Size-preserving Up-to-context
up-to-context-size-preserving =
  _⇔_.from (monotone→⇔ up-to-context-monotone)
  (λ where
     (_ , C , D , ps , qs , refl , refl , ps∼qs) →

      C [ ps ]  ∼⟨ D [ ps∼qs ]-cong ⟩■
      C [ qs ])

-- Note that up to context is not compatible (assuming that Name is
-- inhabited).
--
-- This counterexample is a minor variant of one due to Pous and
-- Sangiorgi, who state that up to context is contained in a larger
-- function that is compatible for another, "one-sided" step function
-- (see Section 6.5.3 in "Enhancements of the bisimulation proof
-- method").

¬-up-to-context-compatible : Name → ¬ Compatible Up-to-context
¬-up-to-context-compatible x comp = contradiction
  where
  a = x , true

  data R₀ : Rel₂ ℓ (Proc ∞) where
    base : R₀ (τ ∙ (a ∙) , a ∙)

  R : Rel₂ ℓ (Proc ∞)
  R = R₀ ⁼

  !τa[R]!a : Up-to-context R (! τ ∙ (a ∙) , ! a ∙)
  !τa[R]!a =
      1
    , ! hole fzero
    , ! hole
    , (λ _ → τ ∙ (a ∙))
    , (λ _ → a ∙)
    , refl
    , refl
    , (λ _ → inj₁ base)

  !a[τ]⇒̂→≡ : ∀ {P} → ! a ∙ [ τ ]⇒̂ P → P ≡ ! a ∙
  !a[τ]⇒̂→≡ (non-silent ¬s _)                 = ⊥-elim $ ¬s _
  !a[τ]⇒̂→≡ (silent _ done)                   = refl
  !a[τ]⇒̂→≡ (silent _ (step {μ = μ} s !a⟶ _)) = ⊥-elim $ name≢τ (
                                                 name a  ≡⟨ !-only ·-only !a⟶ ⟩
                                                 μ       ≡⟨ silent≡τ s ⟩∎
                                                 τ       ∎)

  drop-[] : ∀ {P Q S} →
            Up-to-context R (P ∣ Q , ! S) → R (P ∣ Q , ! S)
  drop-[] (_ , hole i , _ , _ , _ , P∣Q≡Ps[i] , !S≡Qs[i] , PsRQs) =
    subst R (cong₂ _,_ (sym P∣Q≡Ps[i]) (sym !S≡Qs[i])) (PsRQs i)

  drop-[] (_ , ∅        , _ , _ , _ , () , _)
  drop-[] (_ , _ ∣ _    , _ , _ , _ , _  , () , _)
  drop-[] (_ , _ ⊕ _    , _ , _ , _ , () , _)
  drop-[] (_ , _ · _    , _ , _ , _ , () , _)
  drop-[] (_ , ⟨ν _ ⟩ _ , _ , _ , _ , () , _)
  drop-[] (_ , ! _      , _ , _ , _ , () , _)

  R⊆StepR : R ⊆ ⟦ StepC ⟧ R
  R⊆StepR (inj₁ base) = ⟨ lr , rl ⟩
    where
    lr :
      ∀ {P μ} →
      τ ∙ (a ∙) [ μ ]⟶ P →
      ∃ λ Q → a ∙ [ μ ]⇒̂ Q × R (P , Q)
    lr action =
        _
      , (a ∙  ■)
      , inj₂ refl

    rl :
      ∀ {Q μ} →
      a ∙ [ μ ]⟶ Q →
      ∃ λ P → τ ∙ (a ∙) [ μ ]⇒̂ P × R (P , Q)
    rl action =
        _
      , (τ ∙ (a ∙)  →⟨ ⟶: action ⟩
         a ∙        →⟨ ⟶: action ⟩■
         ∅)
      , inj₂ refl

  R⊆StepR {P , _} (inj₂ refl) =
    ⟨ Σ-map id (Σ-map id inj₂)         ∘ lr
    , Σ-map id (Σ-map id (inj₂ ∘ sym)) ∘ lr
    ⟩
    where
    lr : ∀ {P′ μ} →
         P [ μ ]⟶ P′ →
         ∃ λ Q′ → P [ μ ]⇒̂ Q′ × P′ ≡ Q′
    lr P⟶P′ = _ , ⟶→⇒̂ P⟶P′ , refl

  -- Note the use of compatibility in [R]⊆Step[S].

  [R]⊆Step[R] : Up-to-context R ⊆ ⟦ StepC ⟧ (Up-to-context R)
  [R]⊆Step[R] =
    Up-to-context R              ⊆⟨ up-to-context-monotone (λ {x} → R⊆StepR {x}) ⟩
    Up-to-context (⟦ StepC ⟧ R)  ⊆⟨ comp ⟩∎
    ⟦ StepC ⟧ (Up-to-context R)  ∎

  contradiction : ⊥
  contradiction =                                                       $⟨ !τa[R]!a ⟩
    Up-to-context R (! τ ∙ (a ∙) , ! a ∙)                               ↝⟨ [R]⊆Step[R] ⟩
    ⟦ StepC ⟧ (Up-to-context R) (! τ ∙ (a ∙) , ! a ∙)                   ↝⟨ (λ s → StepC.left-to-right s (replication (par-right action))) ⟩
    (∃ λ P → ! a ∙ [ τ ]⇒̂ P × Up-to-context R (! τ ∙ (a ∙) ∣ a ∙ , P))  ↝⟨ (λ { (_ , !a⟶ , hyp) →
                                                                                subst (Up-to-context R ∘ (_ ,_)) (!a[τ]⇒̂→≡ !a⟶) hyp }) ⟩
    Up-to-context R (! τ ∙ (a ∙) ∣ a ∙ , ! a ∙)                         ↝⟨ drop-[] ⟩
    R (! τ ∙ (a ∙) ∣ a ∙ , ! a ∙)                                       ↝⟨ [ (λ ()) , (λ ()) ] ⟩□
    ⊥                                                                   □


------------------------------------------------------------------------
-- Encodings and properties of higher-order extrema and intervals in
-- Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Typing.Encodings where

open import Data.Fin using (zero)
open import Data.Fin.Substitution using (Sub; Lift; TermSubst)
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Nat using (suc)
open import Data.Product using (_,_; proj₁; proj₂; _×_)
open import Relation.Binary.PropositionalEquality hiding ([_])

open import FOmegaInt.Syntax
open import FOmegaInt.Typing
open import FOmegaInt.Typing.Validity

open Syntax
open TermCtx
open Substitution hiding (subst; _/_; _Kind/_)
open Typing
open TypedSubstitution
open TypedNarrowing

----------------------------------------------------------------------
-- Injection of shapes into kinds.

-- For every shape `k' there is a kind `⌈ k ⌉' that erases/simplifies
-- back to `k', i.e. `⌈_⌉' is a right inverse of kind erasure `⌊_⌋'.
--
-- NOTE. The definition of the injection `⌈_⌉' is rather intuitive,
-- but it is neither the only right inverse of `⌊_⌋', nor even the
-- "most canonical" one.  Below we define two alternatives `⌈_⌉↓' and
-- `⌈_⌉↑' which, in addition to being right-inverse to `⌊_⌋', have
-- some appealing order-theoretic properties.

⌈_⌉ : ∀ {n} → SKind → Kind Term n
⌈ ★     ⌉ = *
⌈ j ⇒ k ⌉ = Π ⌈ j ⌉ ⌈ k ⌉

⌊⌋∘⌈⌉-id : ∀ {n} k → ⌊ ⌈_⌉ {n} k ⌋ ≡ k
⌊⌋∘⌈⌉-id ★       = refl
⌊⌋∘⌈⌉-id (j ⇒ k) = cong₂ _⇒_ (⌊⌋∘⌈⌉-id j) (⌊⌋∘⌈⌉-id k)

-- The kind `⌈ k ⌉' is well-formed (in any well-formed context).

⌈⌉-kd : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉ kd
⌈⌉-kd ★       Γ-ctx = *-kd Γ-ctx
⌈⌉-kd (j ⇒ k) Γ-ctx =
  let ⌈j⌉-kd = ⌈⌉-kd j Γ-ctx
  in kd-Π ⌈j⌉-kd (⌈⌉-kd k (wf-kd ⌈j⌉-kd ∷ Γ-ctx))

----------------------------------------------------------------------
-- Encodings and properties of extremal kinds.

-- The empty interval (note the use of absurd bounds).

∅ : ∀ {n} → Kind Term n
∅ = ⊤ ⋯ ⊥

-- Well-formedness of the empty interval kind.

∅-kd : ∀ {n} {Γ : Ctx n} → Γ ctx → Γ ⊢ ∅ kd
∅-kd Γ-ctx = kd-⋯ (∈-⊤-f Γ-ctx) (∈-⊥-f Γ-ctx)

-- Right-inverses of kind simplification: for every shape `k' there
-- are two canonical kinds `⌈ k ⌉↓' and `⌈ k ⌉↑', such that
--
--  1. `⌈_⌉↓' and `⌈_⌉↑' are right-inverses for `⌊_⌋', i.e. `⌈ k ⌉↓'
--     and `⌈ k ⌉↑' both simplify to `k',
--
--  2. `⌈ k ⌉↓' and `⌈ k ⌉↑' are limits, i.e. they are the least
--     resp. greatest kinds for which 1 holds.
--
-- NOTE. Rather than thinking of `⌈_⌉↓' and `⌈_⌉↑' as right-inverses
-- of kind simplifications, one may instead think of `⌊_⌋' as defining
-- a "kind space" `S(k, Γ) = { j | ⌊ j ⌋ ≡ k, Γ ⊢ j kd }' ordered by
-- subkinding, and interpret `⌈ k ⌉↓' and `⌈ k ⌉↑' as the extrema of
-- the space `S(k, Γ)' associated with `k'.

mutual

  ⌈_⌉↓ : ∀ {n} → SKind → Kind Term n
  ⌈ ★     ⌉↓ = ∅
  ⌈ j ⇒ k ⌉↓ = Π ⌈ j ⌉↑ ⌈ k ⌉↓

  ⌈_⌉↑ : ∀ {n} → SKind → Kind Term n
  ⌈ ★     ⌉↑ = *
  ⌈ j ⇒ k ⌉↑ = Π ⌈ j ⌉↓ ⌈ k ⌉↑

mutual

  -- Proof of point 1 above: `⌈_⌉↓' and `⌈_⌉↑' are right-inverses of
  -- `⌊_⌋'.

  ⌊⌋∘⌈⌉↓-id : ∀ {n} k → ⌊ ⌈_⌉↓ {n} k ⌋ ≡ k
  ⌊⌋∘⌈⌉↓-id ★       = refl
  ⌊⌋∘⌈⌉↓-id (j ⇒ k) = cong₂ _⇒_ (⌊⌋∘⌈⌉↑-id j) (⌊⌋∘⌈⌉↓-id k)

  ⌊⌋∘⌈⌉↑-id : ∀ {n} k → ⌊ ⌈_⌉↑ {n} k ⌋ ≡ k
  ⌊⌋∘⌈⌉↑-id ★       = refl
  ⌊⌋∘⌈⌉↑-id (j ⇒ k) = cong₂ _⇒_ (⌊⌋∘⌈⌉↓-id j) (⌊⌋∘⌈⌉↑-id k)

mutual

  -- `⌈ k ⌉↓' and `⌈ k ⌉↑' are well-formed (in any well-formed
  -- context).

  ⌈⌉↓-kd : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉↓ kd
  ⌈⌉↓-kd ★       Γ-ctx = ∅-kd Γ-ctx
  ⌈⌉↓-kd (j ⇒ k) Γ-ctx =
    let ⌈j⌉↑-kd = ⌈⌉↑-kd j Γ-ctx
    in kd-Π ⌈j⌉↑-kd (⌈⌉↓-kd k (wf-kd ⌈j⌉↑-kd ∷ Γ-ctx))

  ⌈⌉↑-kd : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉↑ kd
  ⌈⌉↑-kd ★       Γ-ctx = *-kd Γ-ctx
  ⌈⌉↑-kd (j ⇒ k) Γ-ctx =
    let ⌈j⌉↓-kd = ⌈⌉↓-kd j Γ-ctx
    in kd-Π ⌈j⌉↓-kd (⌈⌉↑-kd k (wf-kd ⌈j⌉↓-kd ∷ Γ-ctx))

mutual

  -- Proof of point 2 above: `⌈ k ⌉↓' and `⌈ k ⌉↑' are the least
  -- resp. greatest (well-formed) kinds that have shape `k'.

  ⌈⌉↑-maximum : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ k <∷ ⌈ ⌊ k ⌋ ⌉↑
  ⌈⌉↑-maximum (kd-⋯ a∈* b∈*)       = <∷-⋯ (<:-⊥ a∈*) (<:-⊤ b∈*)
  ⌈⌉↑-maximum (kd-Π {j} j-kd k-kd) =
    let ⌈⌊j⌋⌉↓<∷j = ⌈⌉↓-minimum j-kd
    in <∷-Π ⌈⌊j⌋⌉↓<∷j (⌈⌉↑-maximum (⇓-kd ⌈⌊j⌋⌉↓<∷j k-kd))
            (kd-Π j-kd k-kd)

  ⌈⌉↓-minimum : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ ⌈ ⌊ k ⌋ ⌉↓ <∷ k
  ⌈⌉↓-minimum (kd-⋯ a∈* b∈*)   = <∷-⋯ (<:-⊤ a∈*) (<:-⊥ b∈*)
  ⌈⌉↓-minimum (kd-Π {j} {k} j-kd k-kd) =
    <∷-Π (⌈⌉↑-maximum j-kd) (⌈⌉↓-minimum k-kd) (⌈⌉↓-kd ⌊ Π j k ⌋ (kd-ctx j-kd))

-- Some corollaries.

-- Minima are subkinds of maxima.

⌈⌉↓<∷⌈⌉↑ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉↓ <∷ ⌈ k ⌉↑
⌈⌉↓<∷⌈⌉↑ k Γ-ctx =
  subst (_ ⊢ ⌈ k ⌉↓ <∷_) (cong ⌈_⌉↑ (⌊⌋∘⌈⌉↓-id k))
        (⌈⌉↑-maximum (⌈⌉↓-kd k Γ-ctx))

-- Minima are subkinds of the "intuitively" injected shapes.

⌈⌉↓<∷⌈⌉ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉↓ <∷ ⌈ k ⌉
⌈⌉↓<∷⌈⌉ k Γ-ctx =
  subst (_ ⊢_<∷ ⌈ k ⌉) (cong ⌈_⌉↓ (⌊⌋∘⌈⌉-id k))
        (⌈⌉↓-minimum (⌈⌉-kd k Γ-ctx))

-- Maxima are superkinds of the "intuitively" injected shapes.

⌈⌉<∷⌈⌉↑ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢ ⌈ k ⌉ <∷ ⌈ k ⌉↑
⌈⌉<∷⌈⌉↑ k Γ-ctx =
  subst (_ ⊢ ⌈ k ⌉ <∷_) (cong ⌈_⌉↑ (⌊⌋∘⌈⌉-id k))
        (⌈⌉↑-maximum (⌈⌉-kd k Γ-ctx))

-- Every well-kinded type inhabits the maximum kind associated with
-- its shape.

∈-⇑-⌈⌉↑ : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ⊢Tp a ∈ ⌈ ⌊ k ⌋ ⌉↑
∈-⇑-⌈⌉↑ a∈k = ∈-⇑ a∈k (⌈⌉↑-maximum (Tp∈-valid a∈k))

----------------------------------------------------------------------
-- Encodings and properties of higher-order extrema

-- Higher-order extremal types, indexed by shapes.
--
-- NOTE. We will define a variant of the higher-order extrema indexed
-- by possibly dependent kinds below.

⊥⟨_⟩ : ∀ {n} → SKind → Term n
⊥⟨ ★ ⟩     = ⊥
⊥⟨ j ⇒ k ⟩ = Λ ⌈ j ⌉ ⊥⟨ k ⟩

⊤⟨_⟩ : ∀ {n} → SKind → Term n
⊤⟨ ★ ⟩     = ⊤
⊤⟨ j ⇒ k ⟩ = Λ ⌈ j ⌉ ⊤⟨ k ⟩

-- ⊥⟨ k ⟩ and ⊤⟨ k ⟩ inhabit ⌈ k ⌉.

∈-⊥⟨⟩ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢Tp ⊥⟨ k ⟩ ∈ ⌈ k ⌉
∈-⊥⟨⟩ ★       Γ-ctx = ∈-⊥-f Γ-ctx
∈-⊥⟨⟩ (j ⇒ k) Γ-ctx =
  let ⌈j⌉-kd = ⌈⌉-kd j Γ-ctx
      ⌈j⌉∷Γ  = wf-kd ⌈j⌉-kd ∷ Γ-ctx
  in ∈-Π-i ⌈j⌉-kd (∈-⊥⟨⟩ k ⌈j⌉∷Γ)

∈-⊤⟨⟩ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢Tp ⊤⟨ k ⟩ ∈ ⌈ k ⌉
∈-⊤⟨⟩ ★       Γ-ctx = ∈-⊤-f Γ-ctx
∈-⊤⟨⟩ (j ⇒ k) Γ-ctx =
  let ⌈j⌉-kd = ⌈⌉-kd j Γ-ctx
      ⌈j⌉∷Γ  = wf-kd ⌈j⌉-kd ∷ Γ-ctx
  in ∈-Π-i ⌈j⌉-kd (∈-⊤⟨⟩ k ⌈j⌉∷Γ)

-- ⊥⟨ k ⟩ and ⊤⟨ k ⟩ are extremal types in ⌈ k ⌉.

⊥⟨⟩-minimum : ∀ {n} {Γ : Ctx n} {a k} →
              Γ ⊢Tp a ∈ ⌈ k ⌉ → Γ ⊢ ⊥⟨ k ⟩ <: a ∈ ⌈ k ⌉
⊥⟨⟩-minimum {k = ★    } a∈*     = <:-⊥ a∈*
⊥⟨⟩-minimum {k = j ⇒ k} a∈⌈j⇒k⌉ =
  <:-trans (<:-λ (⊥⟨⟩-minimum a·z∈⌈k⌉) (∈-⊥⟨⟩ (j ⇒ k) Γ-ctx) Λa·z∈⌈j⇒k⌉)
           (<:-η₁ a∈⌈j⇒k⌉)
  where
    Γ-ctx  = Tp∈-ctx a∈⌈j⇒k⌉
    Λa·z∈⌈j⇒k⌉ = Tp∈-η a∈⌈j⇒k⌉
    a·z∈⌈k⌉    = proj₂ (Tp∈-Λ-inv Λa·z∈⌈j⇒k⌉)

⊤⟨⟩-maximum : ∀ {n} {Γ : Ctx n} {a k} →
              Γ ⊢Tp a ∈ ⌈ k ⌉ → Γ ⊢ a <: ⊤⟨ k ⟩ ∈ ⌈ k ⌉
⊤⟨⟩-maximum {k = ★    } a∈*     = <:-⊤ a∈*
⊤⟨⟩-maximum {k = j ⇒ k} a∈⌈j⇒k⌉ =
  <:-trans (<:-η₂ a∈⌈j⇒k⌉)
           (<:-λ (⊤⟨⟩-maximum a·z∈⌈k⌉) Λa·z∈⌈j⇒k⌉ (∈-⊤⟨⟩ (j ⇒ k) Γ-ctx))
  where
    Γ-ctx  = Tp∈-ctx a∈⌈j⇒k⌉
    Λa·z∈⌈j⇒k⌉ = Tp∈-η a∈⌈j⇒k⌉
    a·z∈⌈k⌉    = proj₂ (Tp∈-Λ-inv Λa·z∈⌈j⇒k⌉)

-- An alternate pair of higher-order extremal types inhabiting the
-- family of kinds ⌈ k ⌉↑, rather than ⌈ k ⌉.

⊥↑⟨_⟩ : ∀ {n} → SKind → Term n
⊥↑⟨ ★ ⟩     = ⊥
⊥↑⟨ j ⇒ k ⟩ = Λ ⌈ j ⌉↑ ⊥↑⟨ k ⟩

⊤↑⟨_⟩ : ∀ {n} → SKind → Term n
⊤↑⟨ ★ ⟩     = ⊤
⊤↑⟨ j ⇒ k ⟩ = Λ ⌈ j ⌉↑ ⊤↑⟨ k ⟩

-- ⊥↑⟨ k ⟩ and ⊤↑⟨ k ⟩ inhabit ⌈ k ⌉↑.

∈-⊥↑⟨⟩ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢Tp ⊥↑⟨ k ⟩ ∈ ⌈ k ⌉↑
∈-⊥↑⟨⟩ ★       Γ-ctx = ∈-⊥-f Γ-ctx
∈-⊥↑⟨⟩ (j ⇒ k) Γ-ctx =
  let ⌈j⌉↑-kd = ⌈⌉↑-kd j Γ-ctx
      ⌈j⌉↑∷Γ  = wf-kd ⌈j⌉↑-kd ∷ Γ-ctx
      ⌈k⌉↑-kd = ⌈⌉↑-kd k ⌈j⌉↑∷Γ
  in ∈-⇑ (∈-Π-i ⌈j⌉↑-kd (∈-⊥↑⟨⟩ k ⌈j⌉↑∷Γ))
         (<∷-Π (⌈⌉↓<∷⌈⌉↑ j Γ-ctx)
               (<∷-refl (⌈⌉↑-kd k ((wf-kd (⌈⌉↓-kd j Γ-ctx)) ∷ Γ-ctx)))
               (kd-Π ⌈j⌉↑-kd ⌈k⌉↑-kd))

∈-⊤↑⟨⟩ : ∀ {n} {Γ : Ctx n} k → Γ ctx → Γ ⊢Tp ⊤↑⟨ k ⟩ ∈ ⌈ k ⌉↑
∈-⊤↑⟨⟩ ★       Γ-ctx = ∈-⊤-f Γ-ctx
∈-⊤↑⟨⟩ (j ⇒ k) Γ-ctx =
  let ⌈j⌉↑-kd = ⌈⌉↑-kd j Γ-ctx
      ⌈j⌉↑∷Γ  = wf-kd ⌈j⌉↑-kd ∷ Γ-ctx
      ⌈k⌉↑-kd = ⌈⌉↑-kd k ⌈j⌉↑∷Γ
  in ∈-⇑ (∈-Π-i ⌈j⌉↑-kd (∈-⊤↑⟨⟩ k ⌈j⌉↑∷Γ))
         (<∷-Π (⌈⌉↓<∷⌈⌉↑ j Γ-ctx)
               (<∷-refl (⌈⌉↑-kd k ((wf-kd (⌈⌉↓-kd j Γ-ctx)) ∷ Γ-ctx)))
               (kd-Π ⌈j⌉↑-kd ⌈k⌉↑-kd))

-- ⊥↑⟨ k ⟩ and ⊤↑⟨ k ⟩ are extremal types in ⌈ k ⌉↑.

⊥↑⟨⟩-minimum : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Tp a ∈ k → Γ ⊢ ⊥↑⟨ ⌊ k ⌋ ⟩ <: a ∈ ⌈ ⌊ k ⌋ ⌉↑
⊥↑⟨⟩-minimum {k = b ⋯ c} a∈b⋯c = <:-⊥ a∈b⋯c
⊥↑⟨⟩-minimum {k = Π j k} a∈Πjk with Tp∈-valid a∈Πjk
... | kd-Π j-kd k-kd =
  <:-trans (<:-λ (⊥↑⟨⟩-minimum a·z∈k′) (∈-⊥↑⟨⟩ ⌊ Π j k ⌋ Γ-ctx)
                 (∈-⇑ Λa·z∈Πjk Πjk<∷⌈Πjk⌉↑))
           (<:-⇑ (<:-η₁ a∈Πjk) Πjk<∷⌈Πjk⌉↑)
  where
    Πjk<∷⌈Πjk⌉↑ = ⌈⌉↑-maximum (kd-Π j-kd k-kd)
    Γ-ctx       = Tp∈-ctx a∈Πjk
    ⌈⌊j⌋⌉↓<∷j   = ⌈⌉↓-minimum j-kd
    Λa·z∈Πjk    = Tp∈-η a∈Πjk
    a·z∈k       = proj₂ (Tp∈-Λ-inv Λa·z∈Πjk)
    a·z∈k′      = ⇓-Tp∈ ⌈⌊j⌋⌉↓<∷j a·z∈k

⊤↑⟨⟩-maximum : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Tp a ∈ k → Γ ⊢ a <: ⊤↑⟨ ⌊ k ⌋ ⟩ ∈ ⌈ ⌊ k ⌋ ⌉↑
⊤↑⟨⟩-maximum {k = b ⋯ c} a∈b⋯c = <:-⊤ a∈b⋯c
⊤↑⟨⟩-maximum {k = Π j k} a∈Πjk with Tp∈-valid a∈Πjk
... | kd-Π j-kd k-kd =
  <:-trans (<:-⇑ (<:-η₂ a∈Πjk) Πjk<∷⌈Πjk⌉↑)
           (<:-λ (⊤↑⟨⟩-maximum a·z∈k′) (∈-⇑ Λa·z∈Πjk Πjk<∷⌈Πjk⌉↑)
                 (∈-⊤↑⟨⟩ ⌊ Π j k ⌋ Γ-ctx))
  where
    Πjk<∷⌈Πjk⌉↑ = ⌈⌉↑-maximum (kd-Π j-kd k-kd)
    Γ-ctx       = Tp∈-ctx a∈Πjk
    ⌈⌊j⌋⌉↓<∷j   = ⌈⌉↓-minimum j-kd
    Λa·z∈Πjk    = Tp∈-η a∈Πjk
    a·z∈k       = proj₂ (Tp∈-Λ-inv Λa·z∈Πjk)
    a·z∈k′      = ⇓-Tp∈ ⌈⌊j⌋⌉↓<∷j a·z∈k

-- Yet another variant of higher-order extremal types, this time
-- indexed by possibly dependent kinds.

⊥′⟨_⟩ : ∀ {n} → Kind Term n → Term n
⊥′⟨ a ⋯ b ⟩ = ⊥
⊥′⟨ Π j k ⟩ = Λ j ⊥′⟨ k ⟩

⊤′⟨_⟩ : ∀ {n} → Kind Term n → Term n
⊤′⟨ a ⋯ b ⟩ = ⊤
⊤′⟨ Π j k ⟩ = Λ j ⊤′⟨ k ⟩

-- A higher-order variant of *.

*⟨_⟩ : ∀ {n} → Kind Term n → Kind Term n
*⟨ a ⋯ b ⟩ = *
*⟨ Π j k ⟩ = Π j *⟨ k ⟩

-- Substitution commutes with ⊥′⟨_⟩, ⊤′⟨_⟩ and *⟨_⟩.

module EncSubstLemmas {T} (l : Lift T Term) where
  open SubstApp l
  open Lift l hiding (var)

  ⊥′⟨⟩-/ : ∀ {n m} {σ : Sub T m n} k → ⊥′⟨ k ⟩ / σ ≡ ⊥′⟨ k Kind/ σ ⟩
  ⊥′⟨⟩-/ (a ⋯ b) = refl
  ⊥′⟨⟩-/ (Π j k) = cong (Λ _) (⊥′⟨⟩-/ k)

  ⊤′⟨⟩-/ : ∀ {n m} {σ : Sub T m n} k → ⊤′⟨ k ⟩ / σ ≡ ⊤′⟨ k Kind/ σ ⟩
  ⊤′⟨⟩-/ (a ⋯ b) = refl
  ⊤′⟨⟩-/ (Π j k) = cong (Λ _) (⊤′⟨⟩-/ k)

  *⟨⟩-/ : ∀ {n m} {σ : Sub T m n} k → *⟨ k ⟩ Kind/ σ ≡ *⟨ k Kind/ σ ⟩
  *⟨⟩-/ (a ⋯ b) = refl
  *⟨⟩-/ (Π j k) = cong (Π _) (*⟨⟩-/ k)

open EncSubstLemmas (TermSubst.termLift termSubst) public
open EncSubstLemmas (TermSubst.varLift  termSubst) public
  renaming (⊥′⟨⟩-/ to ⊥′⟨⟩-/Var; ⊤′⟨⟩-/ to ⊤′⟨⟩-/Var; *⟨⟩-/ to *⟨⟩-/Var)

-- Well-formedness of higher-order star kinds.

kd-*⟨⟩ : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ *⟨ k ⟩ kd
kd-*⟨⟩ (kd-⋯ a∈* b∈*)   = *-kd (Tp∈-ctx a∈*)
kd-*⟨⟩ (kd-Π j-kd k-kd) = kd-Π j-kd (kd-*⟨⟩ k-kd)

-- ⊥′⟨ k ⟩ and ⊤′⟨ k ⟩ inhabit *⟨ k ⟩.

∈-⊥′⟨⟩ : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢Tp ⊥′⟨ k ⟩ ∈ *⟨ k ⟩
∈-⊥′⟨⟩ (kd-⋯ a∈* b∈*)   = ∈-⊥-f (Tp∈-ctx a∈*)
∈-⊥′⟨⟩ (kd-Π j-kd k-kd) = ∈-Π-i j-kd (∈-⊥′⟨⟩ k-kd)

∈-⊤′⟨⟩ : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢Tp ⊤′⟨ k ⟩ ∈ *⟨ k ⟩
∈-⊤′⟨⟩ (kd-⋯ a∈* b∈*)   = ∈-⊤-f (Tp∈-ctx a∈*)
∈-⊤′⟨⟩ (kd-Π j-kd k-kd) = ∈-Π-i j-kd (∈-⊤′⟨⟩ k-kd)

-- A helper: any well-formed kind k is a subkind of *⟨ k ⟩.

*⟨⟩-maximum : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ k <∷ *⟨ k ⟩
*⟨⟩-maximum (kd-⋯ a∈* b∈*)   = <∷-⋯ (<:-⊥ a∈*) (<:-⊤ b∈*)
*⟨⟩-maximum (kd-Π j-kd k-kd) =
  <∷-Π (<∷-refl j-kd) (*⟨⟩-maximum k-kd) (kd-Π j-kd k-kd)

-- ⊥′⟨ k ⟩ and ⊤′⟨ k ⟩ are extremal types in *⟨ k ⟩.

⊥′⟨⟩-minimum : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Tp a ∈ k → Γ ⊢ ⊥′⟨ k ⟩ <: a ∈ *⟨ k ⟩
⊥′⟨⟩-minimum {k = b ⋯ c} a∈b⋯c = <:-⊥ a∈b⋯c
⊥′⟨⟩-minimum {k = Π j k} a∈Πjk =
  <:-trans (<:-λ (⊥′⟨⟩-minimum a·z∈k) (∈-⊥′⟨⟩ Πjk-kd)
                 (∈-⇑ Λa·z∈Πjk Πjk-kd<∷*⟨Πjk⟩))
           (<:-η₁ (∈-⇑ a∈Πjk Πjk-kd<∷*⟨Πjk⟩))
  where
    Λa·z∈Πjk       = Tp∈-η a∈Πjk
    a·z∈k          = proj₂ (Tp∈-Λ-inv Λa·z∈Πjk)
    Πjk-kd         = Tp∈-valid a∈Πjk
    Πjk-kd<∷*⟨Πjk⟩ = *⟨⟩-maximum Πjk-kd

⊤′⟨⟩-maximum : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Tp a ∈ k → Γ ⊢ a <: ⊤′⟨ k ⟩ ∈ *⟨ k ⟩
⊤′⟨⟩-maximum {k = b ⋯ c} a∈b⋯c = <:-⊤ a∈b⋯c
⊤′⟨⟩-maximum {k = Π j k} a∈Πjk =
  <:-trans (<:-η₂ (∈-⇑ a∈Πjk Πjk-kd<∷*⟨Πjk⟩))
           (<:-λ (⊤′⟨⟩-maximum a·z∈k) (∈-⇑ Λa·z∈Πjk Πjk-kd<∷*⟨Πjk⟩)
                 (∈-⊤′⟨⟩ Πjk-kd))
  where
    Λa·z∈Πjk       = Tp∈-η a∈Πjk
    a·z∈k          = proj₂ (Tp∈-Λ-inv Λa·z∈Πjk)
    Πjk-kd         = Tp∈-valid a∈Πjk
    Πjk-kd<∷*⟨Πjk⟩ = *⟨⟩-maximum Πjk-kd

-- Applications of higher-order extrema result in lower-order extrema.

≃-⊥′⟨⟩-· : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢ Π j k kd → Γ ⊢Tp a ∈ j →
           Γ ⊢ ⊥′⟨ Π j k ⟩ · a ≃ ⊥′⟨ k Kind[ a ] ⟩ ∈ *⟨ k Kind[ a ] ⟩
≃-⊥′⟨⟩-· (kd-Π {j} {k} j-kd k-kd) a∈j =
  subst₂ (_ ⊢ ⊥′⟨ Π j k ⟩ · _ ≃_∈_) (⊥′⟨⟩-/ _) (*⟨⟩-/ _)
         (≃-β′ ⊥⟨k⟩ a∈j ⊥⟨Πjk⟩)
  where
    ⊥⟨Πjk⟩ = ∈-⊥′⟨⟩ (kd-Π j-kd k-kd)
    ⊥⟨k⟩   = ∈-⊥′⟨⟩ k-kd

≃-⊤′⟨⟩-· : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢ Π j k kd → Γ ⊢Tp a ∈ j →
           Γ ⊢ ⊤′⟨ Π j k ⟩ · a ≃ ⊤′⟨ k Kind[ a ] ⟩ ∈ *⟨ k Kind[ a ] ⟩
≃-⊤′⟨⟩-· (kd-Π {j} {k} j-kd k-kd) a∈j =
  subst₂ (_ ⊢ ⊤′⟨ Π j k ⟩ · _ ≃_∈_) (⊤′⟨⟩-/ _) (*⟨⟩-/ _)
         (≃-β′ ⊤⟨k⟩ a∈j ⊤⟨Πjk⟩)
  where
    ⊤⟨Πjk⟩ = ∈-⊤′⟨⟩ (kd-Π j-kd k-kd)
    ⊤⟨k⟩   = ∈-⊤′⟨⟩ k-kd

----------------------------------------------------------------------
-- Encodings and properties of higher-order intervals

infix 6 _⋯⟨_⟩_

-- Higher-order interval kinds.

_⋯⟨_⟩_ : ∀ {n} → Term n → Kind Term n → Term n → Kind Term n
a ⋯⟨ c ⋯ d ⟩ b = a ⋯ b
a ⋯⟨ Π j k ⟩ b = Π j (weaken a · var zero ⋯⟨ k ⟩ weaken b · var zero)

-- Higher order interval kinds have the same shape as their
-- kind-indices.

⌊⌋-⋯⟨⟩ : ∀ {n} {a b : Term n} k → ⌊ a ⋯⟨ k ⟩ b ⌋ ≡ ⌊ k ⌋
⌊⌋-⋯⟨⟩ (a ⋯ b) = refl
⌊⌋-⋯⟨⟩ (Π j k) = cong (⌊ j ⌋ ⇒_) (⌊⌋-⋯⟨⟩ k)

-- "Idempotence" of higher order interval kinds.

⋯⟨⟩-idempotent : ∀ {n} {a b : Term n} k → a ⋯⟨ a ⋯⟨ k ⟩ b ⟩ b ≡ a ⋯⟨ k ⟩ b
⋯⟨⟩-idempotent (a ⋯ b) = refl
⋯⟨⟩-idempotent (Π j k) = cong (Π j) (⋯⟨⟩-idempotent k)

-- Well-formedness of higher-order interval kinds.

kd-⋯⟨⟩ : ∀ {n} {Γ : Ctx n} {a b k} →
         Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → Γ ⊢ a ⋯⟨ k ⟩ b kd
kd-⋯⟨⟩ a∈k   b∈k   with Tp∈-valid a∈k
kd-⋯⟨⟩ a∈c⋯d b∈c⋯d | kd-⋯ _ _       = kd-⋯ (Tp∈-⋯-* a∈c⋯d) (Tp∈-⋯-* b∈c⋯d)
kd-⋯⟨⟩ a∈Πjk b∈Πjk | kd-Π j-kd k-kd =
  kd-Π j-kd (kd-⋯⟨⟩ a·z∈k b·z∈k)
  where
    a·z∈k = proj₂ (Tp∈-Λ-inv (Tp∈-η a∈Πjk))
    b·z∈k = proj₂ (Tp∈-Λ-inv (Tp∈-η b∈Πjk))

-- NOTE.  We would like to show that the inverse of the above also
-- holds, i.e. that given a well-formed higher-order interval kind `a
-- ⋯⟨ k ⟩ b', we have `a ∈ k' and `b ∈ k'.  Unfortunately this is not
-- true because the definition of `a ⋯⟨ k ⟩ b' is a bit too forgetful
-- when the kind-index `k' is itself an interval `k = c ⋯ d', that is,
-- when `a' and `b' are proper types.  E.g. the kind `⊥ ⋯⟨ ∅ ⟩ ⊤' is
-- well-formed, but clearly `⊥ , ⊤ ∉ ø'.
--
-- However, as the following lemma illustrates, we can invert some
-- judgments about higher-order intervals with "sensible" kind
-- indices, such as those resulting from shapes via ⌈_⌉.  Still, a
-- proper inversion lemma would require more work, in particular one
-- has to contract the kinding derivations of the η-expanded bounds
-- `a' and `b' back into their non-expanded form.

-- Higher-order interval kinds indexed by `⌈ k ⌉' for some `k' are
-- subkinds of `⌈ k ⌉'.

⋯⟨⌈⌉⟩-<∷-⌈⌉ : ∀ {n} {Γ : Ctx n} {a b k} →
              Γ ⊢ a ⋯⟨ ⌈ k ⌉ ⟩ b kd → Γ ⊢ a ⋯⟨ ⌈ k ⌉ ⟩ b <∷ ⌈ k ⌉
⋯⟨⌈⌉⟩-<∷-⌈⌉ {k = ★    } (kd-⋯ a∈* b∈*) = <∷-⋯ (<:-⊥ a∈*) (<:-⊤ b∈*)
⋯⟨⌈⌉⟩-<∷-⌈⌉ {k = j ⇒ k} (kd-Π ⌈j⌉-kd a·z⋯⟨⌈k⌉⟩b·z-kd) =
  <∷-Π (<∷-refl ⌈j⌉-kd) (⋯⟨⌈⌉⟩-<∷-⌈⌉ a·z⋯⟨⌈k⌉⟩b·z-kd)
       (kd-Π ⌈j⌉-kd a·z⋯⟨⌈k⌉⟩b·z-kd)

-- Two corollaries.

-- Types inhabiting a type interval indexed by a shape `k' also
-- inhabit `⌈ k ⌉`.

Tp∈-⋯⟨⌈⌉⟩-inv : ∀ {n} {Γ : Ctx n} {a b c k} →
                Γ ⊢Tp a ∈ b ⋯⟨ ⌈ k ⌉ ⟩ c → Γ ⊢Tp a ∈ ⌈ k ⌉
Tp∈-⋯⟨⌈⌉⟩-inv a∈b⋯⟨⌈k⌉⟩c =
  ∈-⇑ a∈b⋯⟨⌈k⌉⟩c (⋯⟨⌈⌉⟩-<∷-⌈⌉ (Tp∈-valid a∈b⋯⟨⌈k⌉⟩c))

-- Types related in a type interval indexed by a shape `k' are also
-- related in `⌈ k ⌉`.

<:-⋯⟨⌈⌉⟩-inv : ∀ {n} {Γ : Ctx n} {a b c d k} →
               Γ ⊢ a <: b ∈ c ⋯⟨ ⌈ k ⌉ ⟩ d → Γ ⊢ a <: b ∈ ⌈ k ⌉
<:-⋯⟨⌈⌉⟩-inv a<:b∈c⋯⟨⌈k⌉⟩d =
  <:-⇑ a<:b∈c⋯⟨⌈k⌉⟩d (⋯⟨⌈⌉⟩-<∷-⌈⌉ (<:-valid-kd a<:b∈c⋯⟨⌈k⌉⟩d))

-- Subkinding of higher-order interval kinds.

<∷-⋯⟨⟩ : ∀ {n} {Γ : Ctx n} {a₁ a₂ b₁ b₂ k} →
         Γ ⊢ a₂ <: a₁ ∈ k → Γ ⊢ b₁ <: b₂ ∈ k → Γ ⊢ a₁ ⋯⟨ k ⟩ b₁ <∷ a₂ ⋯⟨ k ⟩ b₂
<∷-⋯⟨⟩ a₂<:a₁∈k   b₁<:b₂∈k   with <:-valid-kd a₂<:a₁∈k
<∷-⋯⟨⟩ a₂<:a₁∈c⋯d b₁<:b₂∈c⋯d | kd-⋯ _ _ =
  <∷-⋯ (<:-⋯-* a₂<:a₁∈c⋯d) (<:-⋯-* b₁<:b₂∈c⋯d)
<∷-⋯⟨⟩ a₂<:a₁∈Πjk b₁<:b₂∈Πjk | kd-Π j-kd k-kd =
  let a₂∈Πjk , a₁∈Πjk = <:-valid a₂<:a₁∈Πjk
      b₁∈Πjk , b₂∈Πjk = <:-valid b₁<:b₂∈Πjk
      Γ-ctx   = kd-ctx j-kd
      j-wf    = wf-kd j-kd
      j∷Γ-ctx = j-wf ∷ Γ-ctx
      z∈k     = ∈-var zero j∷Γ-ctx refl
      z≃z∈k   = ≃-refl z∈k
      a₂<:a₁∈Πjk′ = <:-weaken j-wf a₂<:a₁∈Πjk
      b₁<:b₂∈Πjk′ = <:-weaken j-wf b₁<:b₂∈Πjk
      k[z]≡k      = Kind-wk↑-sub-zero-vanishes _
      a₂·z<:a₁·z∈k[z] = <:-· a₂<:a₁∈Πjk′ z≃z∈k
      b₁·z<:b₂·z∈k[z] = <:-· b₁<:b₂∈Πjk′ z≃z∈k
      a₂·z<:a₁·z∈k = subst (_ ⊢ _ <: _ ∈_) k[z]≡k a₂·z<:a₁·z∈k[z]
      b₁·z<:b₂·z∈k = subst (_ ⊢ _ <: _ ∈_) k[z]≡k b₁·z<:b₂·z∈k[z]
  in <∷-Π (<∷-refl j-kd)
          (<∷-⋯⟨⟩ a₂·z<:a₁·z∈k b₁·z<:b₂·z∈k)
          (kd-⋯⟨⟩ a₁∈Πjk b₁∈Πjk)

-- A corollary: equality of higher-order interval kinds.

≅-⋯⟨⟩ : ∀ {n} {Γ : Ctx n} {a₁ a₂ b₁ b₂ k} →
        Γ ⊢ a₁ ≃ a₂ ∈ k → Γ ⊢ b₁ ≃ b₂ ∈ k → Γ ⊢ a₁ ⋯⟨ k ⟩ b₁ ≅ a₂ ⋯⟨ k ⟩ b₂
≅-⋯⟨⟩ (<:-antisym a₁<:a₂∈k a₂<:a₁∈k) (<:-antisym b₁<:b₂∈k b₂<:b₁∈k) =
  <∷-antisym (<∷-⋯⟨⟩ a₂<:a₁∈k b₁<:b₂∈k) (<∷-⋯⟨⟩ a₁<:a₂∈k b₂<:b₁∈k)

-- A variant of kind-driven η-expansion that only expands the head.

η-exp : ∀ {n} → Kind Term n → Term n → Term n
η-exp (_ ⋯ _) a = a
η-exp (Π j k) a = Λ j (η-exp k (weaken a · var zero))

-- Soundness of η-expansion.

≃-η-exp : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ⊢ a ≃ η-exp k a ∈ k
≃-η-exp {_} {_} {_} {b ⋯ c} a∈b⋯c = ≃-refl a∈b⋯c
≃-η-exp {_} {_} {a} {Π j k} a∈Πjk with Tp∈-valid a∈Πjk
... | kd-Π j-kd k-kd = begin
  a                                     ≃⟨ ≃-sym (≃-η a∈Πjk) ⟩
  Λ j (weaken a · var zero)             ≃⟨ ≃-λ′ (≅-refl j-kd) (≃-η-exp a·z∈k) ⟩
  Λ j (η-exp k (weaken a · var zero))   ∎
  where
    open ≃-Reasoning
    a·z∈k = proj₂ (Tp∈-Λ-inv (Tp∈-η a∈Πjk))

-- A singleton introduction rule for higher-order interval kinds.

∈-s⟨⟩-i : ∀ {n} {Γ : Ctx n} {a k} →
          Γ ⊢Tp a ∈ k → Γ ⊢Tp η-exp k a ∈ a ⋯⟨ k ⟩ a
∈-s⟨⟩-i a∈k   with Tp∈-valid a∈k
∈-s⟨⟩-i a∈b⋯c | kd-⋯ _ _       = ∈-s-i a∈b⋯c
∈-s⟨⟩-i a∈Πjk | kd-Π j-kd k-kd =
  ∈-Π-i j-kd (∈-s⟨⟩-i a·z∈k)
  where
    a·z∈k = proj₂ (Tp∈-Λ-inv (Tp∈-η a∈Πjk))

-- A corollary: we can kind (the η-expansion of) a type with explicit
-- lower and uper bounds in the interval defined by these bounds.

Tp∈-<:-⋯ : ∀ {n} {Γ : Ctx n} {a b c k} →
           Γ ⊢ b <: a ∈ k → Γ ⊢ a <: c ∈ k → Γ ⊢Tp η-exp k a ∈ b ⋯⟨ k ⟩ c
Tp∈-<:-⋯ b<:a∈k a<:c∈k =
  ∈-⇑ (∈-s⟨⟩-i (proj₁ (<:-valid a<:c∈k))) (<∷-⋯⟨⟩ b<:a∈k a<:c∈k)

-- Bound projection rules for higher-order intervals.
--
-- NOTE.  These lemmas are a bit weaker than one might like.  In
-- particular, the additional premises `Γ ⊢ a , b , c ∈ k' might seem
-- redundant.  But recall that we cannot, in general, invert
-- well-formed higher-order intervals, i.e. `Γ ⊢ b ⋯⟨ k ⟩ c kd' does
-- *not* imply `Γ ⊢ b ∈ k' and `Γ ⊢ c ∈ k'.  Similarly, `Γ ⊢ a ∈ b ⋯⟨
-- k ⟩ c' does *not* imply `Γ ⊢ a ∈ k'.  To see this, consider again
-- the kind `⊥ ⋯⟨ ∅ ⟩ ⊤', which is well-formed and inhabited by both
-- `⊥' and `⊤', yet clearly `⊥ , ⊤ ∉ ø'.

<:-⋯⟨⟩-⟨|⟩ : ∀ {n} {Γ : Ctx n} {a b c k} →
             Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → Γ ⊢Tp c ∈ k →
             Γ ⊢Tp a ∈ b ⋯⟨ k ⟩ c → Γ ⊢ b <: a ∈ k × Γ ⊢ a <: c ∈ k
<:-⋯⟨⟩-⟨|⟩ {k = _ ⋯ _} a∈d⋯e b∈d⋯e c∈d⋯e a∈b⋯c          =
  <:-⇑ (<:-⋯-i (<:-⟨| a∈b⋯c)) (<∷-⋯ (<:-⟨| b∈d⋯e) (<:-|⟩ a∈d⋯e)) ,
  <:-⇑ (<:-⋯-i (<:-|⟩ a∈b⋯c)) (<∷-⋯ (<:-⟨| a∈d⋯e) (<:-|⟩ c∈d⋯e))
<:-⋯⟨⟩-⟨|⟩ {k = Π _ _} a∈Πjk b∈Πjk c∈Πjk a∈Πjb·z⋯⟨k⟩c·z =
  let Λa·z∈Πjk  = Tp∈-η a∈Πjk
      Λb·z∈Πjk  = Tp∈-η b∈Πjk
      Λc·z∈Πjk  = Tp∈-η c∈Πjk
      _ , a·z∈k = Tp∈-Λ-inv Λa·z∈Πjk
      _ , b·z∈k = Tp∈-Λ-inv Λb·z∈Πjk
      _ , c·z∈k = Tp∈-Λ-inv Λc·z∈Πjk
      Λa·z∈Πjb·z⋯⟨k⟩c·z  = Tp∈-η a∈Πjb·z⋯⟨k⟩c·z
      _ , a·z∈b·z⋯⟨k⟩c·z = Tp∈-Λ-inv Λa·z∈Πjb·z⋯⟨k⟩c·z
      b·z<:a·z∈k , a·z<:c·z∈k = <:-⋯⟨⟩-⟨|⟩ a·z∈k b·z∈k c·z∈k a·z∈b·z⋯⟨k⟩c·z
      Λjb·z<:Λja·z∈Πjk = <:-λ b·z<:a·z∈k Λb·z∈Πjk Λa·z∈Πjk
      Λja·z<:Λjc·z∈Πjk = <:-λ a·z<:c·z∈k Λa·z∈Πjk Λc·z∈Πjk
      Λja·z<:a∈kΠjk    = <:-η₁ a∈Πjk
      a<:Λja·z∈kΠjk    = <:-η₂ a∈Πjk
      Λjc·z<:c∈kΠjk    = <:-η₁ c∈Πjk
      b<:Λjb·z∈kΠjk    = <:-η₂ b∈Πjk
  in <:-trans (<:-trans b<:Λjb·z∈kΠjk Λjb·z<:Λja·z∈Πjk) Λja·z<:a∈kΠjk ,
     <:-trans (<:-trans a<:Λja·z∈kΠjk Λja·z<:Λjc·z∈Πjk) Λjc·z<:c∈kΠjk

<:-⋯⟨⟩-⟨| : ∀ {n} {Γ : Ctx n} {a b c k} →
            Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → Γ ⊢Tp c ∈ k →
            Γ ⊢Tp a ∈ b ⋯⟨ k ⟩ c → Γ ⊢ b <: a ∈ k
<:-⋯⟨⟩-⟨| a∈k b∈k c∈k a∈b⋯⟨k⟩c = proj₁ (<:-⋯⟨⟩-⟨|⟩ a∈k b∈k c∈k a∈b⋯⟨k⟩c)

<:-⋯⟨⟩-|⟩ : ∀ {n} {Γ : Ctx n} {a b c k} →
            Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → Γ ⊢Tp c ∈ k →
            Γ ⊢Tp a ∈ b ⋯⟨ k ⟩ c → Γ ⊢ a <: c ∈ k
<:-⋯⟨⟩-|⟩ a∈k b∈k c∈k a∈b⋯⟨k⟩c = proj₂ (<:-⋯⟨⟩-⟨|⟩ a∈k b∈k c∈k a∈b⋯⟨k⟩c)

-- An interval introduction rule for subtypes inhabiting higher-order
-- interval kinds.

<:-⋯⟨⟩-i : ∀ {n} {Γ : Ctx n} {a b k} →
           Γ ⊢ a <: b ∈ k → Γ ⊢ η-exp k a <: η-exp k b ∈ a ⋯⟨ k ⟩ b
<:-⋯⟨⟩-i a<:b∈k   with <:-valid-kd a<:b∈k
<:-⋯⟨⟩-i a<:b∈c⋯d | kd-⋯ _ _    = <:-⋯-i a<:b∈c⋯d
<:-⋯⟨⟩-i a<:b∈Πjk | kd-Π j-kd _ =
  let j-wf          = wf-kd j-kd
      Γ-ctx         = <:-ctx a<:b∈Πjk
      j∷Γ-ctx       = j-wf ∷ Γ-ctx
      a∈Πjk , b∈Πjk = <:-valid a<:b∈Πjk
      a<:b∈Πjk′     = <:-weaken j-wf a<:b∈Πjk
      a·z<:b·z∈k[z] = <:-· a<:b∈Πjk′ (≃-refl (∈-var zero j∷Γ-ctx refl))
      a·z<:b·z∈k    = subst (_ ⊢ _ <: _ ∈_) (Kind-wk↑-sub-zero-vanishes _)
                            a·z<:b·z∈k[z]
      a·z<:b·z∈a·z⋯⟨k⟩b·z = <:-⋯⟨⟩-i a·z<:b·z∈k
  in <:-λ a·z<:b·z∈a·z⋯⟨k⟩b·z
          (∈-⇑ (∈-s⟨⟩-i a∈Πjk) (<∷-⋯⟨⟩ (<:-refl a∈Πjk) a<:b∈Πjk))
          (∈-⇑ (∈-s⟨⟩-i b∈Πjk) (<∷-⋯⟨⟩ a<:b∈Πjk (<:-refl b∈Πjk)))

-- Any interval indexed by *⟨ k ⟩ can be re-indexed by k

*⟨⟩-⋯⟨⟩ : ∀ {n} {a b : Term n} k → a ⋯⟨ *⟨ k ⟩ ⟩ b ≡ a ⋯⟨ k ⟩ b
*⟨⟩-⋯⟨⟩ (a ⋯ b) = refl
*⟨⟩-⋯⟨⟩ (Π j k) = cong (Π j) (*⟨⟩-⋯⟨⟩ k)

-- *⟨ k ⟩ is equal to the HO interval bounded by ⊥′⟨ k ⟩ and ⊤′⟨ k ⟩.

*⟨⟩-⊥⟨⟩⋯⟨⟩⊤⟨⟩ : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd →
                Γ ⊢ *⟨ k ⟩ ≅ ⊥′⟨ k ⟩ ⋯⟨ k ⟩ ⊤′⟨ k ⟩
*⟨⟩-⊥⟨⟩⋯⟨⟩⊤⟨⟩         (kd-⋯ a∈* b∈*)           = ≅-refl (*-kd (Tp∈-ctx a∈*))
*⟨⟩-⊥⟨⟩⋯⟨⟩⊤⟨⟩ {_} {Γ} (kd-Π {j} {k} j-kd k-kd) =
  ≅-Π (≅-refl j-kd)
      (≅-trans (*⟨⟩-⊥⟨⟩⋯⟨⟩⊤⟨⟩ k-kd)
               (subst₂ (_ ⊢_≅_) (*⟨⟩-⋯⟨⟩ k) (*⟨⟩-⋯⟨⟩ k)
                       (≅-⋯⟨⟩ ⊥⟨k⟩≃Λ⊥⟨k⟩·z ⊤⟨k⟩≃Λ⊤⟨k⟩·z)))
  where
    module KL = TermLikeLemmas termLikeLemmasKind

    Γ-ctx   = kd-ctx j-kd
    z∈j     = ∈-var zero (wf-kd j-kd ∷ Γ-ctx) refl
    Πjk-kd′ = kd-weaken (wf-kd j-kd) (kd-Π j-kd k-kd)
    eq₁     = cong (λ k → (Λ (weakenKind j) k) · var zero) (sym (⊥′⟨⟩-/Var k))
    eq₂     = cong (λ k → (Λ (weakenKind j) k) · var zero) (sym (⊤′⟨⟩-/Var k))
    k′[z]   = (k Kind/Var _) Kind[ var zero ]
    ⊥⟨k⟩≃Λ⊥⟨k⟩·z = subst₂ (_ ⊢_≃ weaken (Λ j ⊥′⟨ k ⟩) · var zero ∈_)
                          (cong ⊥′⟨_⟩ (Kind-wk↑-sub-zero-vanishes k))
                          (cong *⟨_⟩ (Kind-wk↑-sub-zero-vanishes k))
                          (subst (kd j ∷ Γ ⊢ ⊥′⟨ k′[z] ⟩ ≃_∈ *⟨ k′[z] ⟩) eq₁
                                 (≃-sym (≃-⊥′⟨⟩-· Πjk-kd′ z∈j)))
    ⊤⟨k⟩≃Λ⊤⟨k⟩·z = subst₂ (_ ⊢_≃ weaken (Λ j ⊤′⟨ k ⟩) · var zero ∈_)
                          (cong ⊤′⟨_⟩ (Kind-wk↑-sub-zero-vanishes k))
                          (cong *⟨_⟩ (Kind-wk↑-sub-zero-vanishes k))
                          (subst (kd j ∷ Γ ⊢ ⊤′⟨ k′[z] ⟩ ≃_∈ *⟨ k′[z] ⟩) eq₂
                                 (≃-sym (≃-⊤′⟨⟩-· Πjk-kd′ z∈j)))

----------------------------------------------------------------------
-- Encodings and admissible kinding rules of bounded quantifiers and
-- operators

-- Bounded operator kind.

Π′ : ∀ {n} → Term n → Term n → Kind Term n → Kind Term (suc n) → Kind Term n
Π′ a b j k = Π (a ⋯⟨ j ⟩ b) k

-- Bounded universal quantifiers.

∀′ : ∀ {n} → Term n → Term n → Kind Term n → Term (suc n) → Term n
∀′ a b k c = Π (a ⋯⟨ k ⟩ b) c

-- Bounded type abstraction.

Λ′ : ∀ {n} → Term n → Term n → Kind Term n → Term (suc n) → Term n
Λ′ a b k c = Λ (a ⋯⟨ k ⟩ b) c

-- A formation rule for bounded operator kinds.

∈-Π′-f : ∀ {n} {Γ : Ctx n} {a b j k} →
         Γ ⊢Tp a ∈ j → Γ ⊢Tp b ∈ j → kd (a ⋯⟨ j ⟩ b) ∷ Γ ⊢ k kd →
         Γ ⊢ Π′ a b j k kd
∈-Π′-f a∈j b∈j k-kd = kd-Π (kd-⋯⟨⟩ a∈j b∈j) k-kd

-- An introduction rule for bounded universal quantifiers.

∈-Π′-i : ∀ {n} {Γ : Ctx n} {a b j c k} →
         Γ ⊢Tp a ∈ j → Γ ⊢Tp b ∈ j → kd (a ⋯⟨ j ⟩ b) ∷ Γ ⊢Tp c ∈ k →
         Γ ⊢Tp Λ′ a b j c ∈ Π′ a b j k
∈-Π′-i a∈j b∈j c∈k = ∈-Π-i (kd-⋯⟨⟩ a∈j b∈j) c∈k

-- An elimination rule for bounded universal quantifiers.

∈-Π′-e : ∀ {n} {Γ : Ctx n} {a b c j k d} →
         Γ ⊢Tp a ∈ Π′ b c j k → Γ ⊢ b <: d ∈ j → Γ ⊢ d <: c ∈ j →
         Γ ⊢Tp a · η-exp j d ∈ k Kind[ η-exp j d ]
∈-Π′-e a∈Πbcjk b<:d∈j d<:c∈j =
  let d∈j , _ = <:-valid d<:c∈j
  in ∈-Π-e a∈Πbcjk (Tp∈-<:-⋯ b<:d∈j d<:c∈j)

-- A formation rule for bounded universal quantifiers.

∈-∀′-f : ∀ {n} {Γ : Ctx n} {a b k c} →
         Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → kd (a ⋯⟨ k ⟩ b) ∷ Γ ⊢Tp c ∈ * →
         Γ ⊢Tp ∀′ a b k c ∈ *
∈-∀′-f a∈k b∈k c∈* = ∈-∀-f (kd-⋯⟨⟩ a∈k b∈k) c∈*

-- An introduction rule for bounded universal quantifiers.

∈-∀′-i : ∀ {n} {Γ : Ctx n} {a b k c d} →
         Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ k → kd (a ⋯⟨ k ⟩ b) ∷ Γ ⊢Tm c ∈ d →
         Γ ⊢Tm Λ′ a b k c ∈ ∀′ a b k d
∈-∀′-i a∈k b∈k c∈d = ∈-∀-i (kd-⋯⟨⟩ a∈k b∈k) c∈d

-- An elimination rule for bounded universal quantifiers.

∈-∀′-e : ∀ {n} {Γ : Ctx n} {a b c k d e} →
         Γ ⊢Tm a ∈ ∀′ b c k d → Γ ⊢ b <: e ∈ k → Γ ⊢ e <: c ∈ k →
         Γ ⊢Tm a ⊡ η-exp k e ∈ d [ η-exp k e ]
∈-∀′-e a∈∀bckd b<:e∈k e<:c∈k =
  let e∈k , _ = <:-valid e<:c∈k
  in ∈-∀-e a∈∀bckd (Tp∈-<:-⋯ b<:e∈k e<:c∈k)

------------------------------------------------------------------------
-- Stone and Harper's singleton (sub)kinding rules.
--
-- See p. 3 (216) of C. A. Stone and R. Harper, Deciding Type
-- Equivalence in a Language with Singleton Kinds, proc. POPL'00, ACM,
-- 2000.

-- An encoding of Stone and Harper's singleton kinds.

S : ∀ {n} → Term n → Kind Term n
S a = a ⋯ a

-- Singleton kind formation.

kd-s : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢Tp a ∈ * → Γ ⊢ S a kd
kd-s a∈* = kd-⋯ a∈* a∈*

-- Singleton introduction for kinding is exactly the `∈-s-i' kinding rule.

-- Singleton introduction for equality.
--
-- NOTE. This is just a weaker version of `≃-s-i'.

≃-s-i′ : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a ≃ b ∈ * → Γ ⊢ a ≃ b ∈ S a
≃-s-i′ a≃b∈* = ≃-s-i a≃b∈*

-- Singleton elimination.
≃-s-e : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢Tp a ∈ S b → Γ ⊢ a ≃ b ∈ *
≃-s-e a∈b⋯b = <:-antisym (<:-|⟩ a∈b⋯b) (<:-⟨| a∈b⋯b)

-- Subkinding of singletons.

<∷-s-* : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢Tp a ∈ * → Γ ⊢ S a <∷ *
<∷-s-* a∈* = <∷-⋯ (<:-⊥ a∈*) (<:-⊤ a∈*)

<∷-s-s : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a ≃ b ∈ * → Γ ⊢ S a <∷ S b
<∷-s-s (<:-antisym a<:b∈* b<:a∈*) = <∷-⋯ b<:a∈* a<:b∈*

-- Equality of singleton kinds.
--
-- NOTE. This is just a weaker version of `≅-⋯'.

≅-s : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a ≃ b ∈ * → Γ ⊢ S a ≅ S b
≅-s a≃b∈* = ≅-⋯ a≃b∈* a≃b∈*

------------------------------------------------------------------------
-- Cardelli and Longo's power (sub)kinding rules.
--
-- See p. 8 (424) of L. Cardelli, G. Longo, A Semantic Basis for
-- Quest, JFP 1(4), Cambridge University Press, 1991.

-- An encoding of Cardelli and Longo's power kinds.

P : ∀ {n} → Term n → Kind Term n
P a = ⊥ ⋯ a

-- Power kind formation.

kd-p : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢Tp a ∈ * → Γ ⊢ P a kd
kd-p a∈* = kd-⋯ (∈-⊥-f (Tp∈-ctx a∈*)) a∈*

-- Power kind introduction.

∈-p-i : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢Tp a ∈ * → Γ ⊢Tp a ∈ P a
∈-p-i a∈* = ∈-⇑ (∈-s-i a∈*) (<∷-⋯ (<:-⊥ a∈*) (<:-refl a∈*))

-- Subkinding of power kinds.

<∷-p : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a <: b ∈ * → Γ ⊢ P a <∷ P b
<∷-p a<:b∈* = <∷-⋯ (<:-⊥ (∈-⊥-f (<:-ctx a<:b∈*))) a<:b∈*

-- Power kinding is equivalent to subtyping of proper types.

∈P⇒<: : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢Tp a ∈ P b → Γ ⊢ a <: b ∈ *
∈P⇒<: a∈Pb = <:-|⟩ a∈Pb

<:⇒∈P : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a <: b ∈ * → Γ ⊢Tp a ∈ P b
<:⇒∈P a<:b∈* = ∈-⇑ (∈-p-i (proj₁ (<:-valid a<:b∈*))) (<∷-p a<:b∈*)


module Coirc.Format where
open import Data.Empty
open import Data.Unit
open import Data.Bool
open import Data.Nat
open import Data.Char
open import Data.String
open import Data.List
open import Data.Sum
open import Data.Product
open import Coirc

infixr 3 _∣_
infixr 1 _>>_ _>>-_ _>>=_

within? : Char → ℕ → ℕ → Bool
within? c start end = toBool lower ∧ toBool higher
  where
  target = toNat c
  lower  = suc target ∸ start
  higher = suc end ∸ target
  toBool : ℕ → Bool
  toBool zero    = false
  toBool (suc _) = true

data DarRange (start end : ℕ) : Bool → Set where
  dar : (c : Char) → DarRange start end (within? c start end)

data Dar : ℕ → Set where
  dar : (c : Char) → Dar (toNat c)

data U : Set where
  CHAR : U
  DAR : ℕ → U
  DAR-RANGE : ℕ → ℕ → U
  `*! `*sp `*crlf : U

El : U → Set
El CHAR = Char
El (DAR n) = Dar n
El (DAR-RANGE n m) = DarRange n m true
El `*! = String
El `*sp = String
El `*crlf = String

mutual
  data Format : Set where
    Fail End : Format
    As : Event → Format
    Base : U → Format
    Skip Or And : Format → Format → Format
    Use : (f : Format) → (⟦ f ⟧ → Format) → Format

  ⟦_⟧ : Format → Set
  ⟦ Fail ⟧ = ⊥
  ⟦ End ⟧ = ⊤
  ⟦ As _ ⟧ = Event
  ⟦ Base u ⟧ = El u
  ⟦ Skip _ f ⟧ = ⟦ f ⟧
  ⟦ Or f₁ f₂ ⟧ = ⟦ f₁ ⟧ ⊎ ⟦ f₂ ⟧
  ⟦ And f₁ f₂ ⟧ = ⟦ f₁ ⟧ × ⟦ f₂ ⟧
  ⟦ Use f₁ f₂ ⟧ = Σ ⟦ f₁ ⟧ λ x → ⟦ f₂ x ⟧

_>>_ : Format → Format → Format
f₁ >> f₂ = Skip f₁ f₂

_>>-_ : Format → Format → Format
x >>- y = And x y

_>>=_ : (f : Format) → (⟦ f ⟧ → Format) → Format
x >>= y = Use x y

_∣_ : Format → Format → Format
x ∣ y = Or x y

char : Char → Format
char c = Base (DAR (toNat c))

str : String → Format
str s = chars (toList s)
  where
  chars : List Char → Format
  chars [] = End
  chars (x ∷ xs) = char x >>- chars xs

DIGIT = Base (DAR-RANGE (toNat '0') (toNat '9'))
cl    = char ':'
sp    = char ' '
cr    = char '\r'
lf    = char '\n'
crlf  = cr >>- lf

prefix = cl >> Base `*sp

Notice : Format
Notice =
  prefix >>
  sp >>
  str "NOTICE" >>
  sp >> char '*' >> sp >>
  Base `*crlf >> -- text
  crlf >>
  As notice

NumericReply : Format
NumericReply =
  prefix >>
  sp >>
  (DIGIT >>- DIGIT >>- DIGIT) >>
  sp >>
  Base `*sp >> -- target
  sp >>
  Base `*crlf >> -- text
  crlf >>
  As numeric

Mode : Format
Mode =
  prefix >>
  sp >>
  str "MODE" >>
  sp >>
  Base `*sp >> -- nickname
  sp >>
  Base `*crlf >> -- modes
  crlf >>
  As mode

Ping : Format
Ping =
  str "PING" >>
  sp >>
  Base `*crlf >> -- server
  crlf >>
  As ping

Privmsg : Format
Privmsg =
  cl >>
  Base `*! >>= λ source →
  Base `*sp >>
  sp >>
  str "PRIVMSG" >>
  sp >>
  Base `*sp >> -- target
  sp >>
  cl >>
  Base `*crlf >>= λ text →
  crlf >>
  As (privmsg source text)


open import Data.Product using ( _×_ ; _,_ ; swap )
open import Data.Sum using ( inj₁ ; inj₂ )
open import Relation.Nullary using ( ¬_ ; yes ; no )
open import Relation.Unary using ( _∈_ )
open import Web.Semantic.DL.ABox using ( ABox ; ε ; _,_ ; _∼_ ; _∈₁_ ; _∈₂_ )
open import Web.Semantic.DL.ABox.Interp using
  ( Interp ; ⌊_⌋ ; ind ; ind⁻¹ ; Surjective ; surj✓ )
open import Web.Semantic.DL.ABox.Interp.Morphism using
  ( _≲_ ; _≃_ ; _,_ ; ≲⌊_⌋ ; ≲-resp-ind )
open import Web.Semantic.DL.ABox.Interp.Meet using
  ( meet ; meet-lb ; meet-glb ; meet-uniq ; meet-surj )
open import Web.Semantic.DL.ABox.Model using ( _⊨a_ )
open import Web.Semantic.DL.Concept using
  ( Concept ; ⟨_⟩ ; ¬⟨_⟩ ; ⊤ ; ⊥ ; _⊔_ ; _⊓_ ; ∀[_]_ ; ∃⟨_⟩_ ; ≤1 ; >1 ; neg )
open import Web.Semantic.DL.Concept.Model using
  ( _⟦_⟧₁ ; neg-sound ; neg-complete )
open import Web.Semantic.DL.Integrity.Closed using
  ( Mediated₀ ; Initial₀ ; sur_⊨₀_ ; _,_ )
open import Web.Semantic.DL.Integrity.Closed.Alternate using
  ( _⊫_∼_ ; _⊫_∈₁_ ; _⊫_∈₂_ ; _⊫t_ ; _⊫a_ ; _⊫k_
  ; eq ; rel ; rev ; +atom ; -atom ; top ; inj₁ ; inj₂
  ; all ; ex ; uniq ; ¬uniq ; cn ; rl ; dis ; ref ; irr ; tra ; ε ; _,_ )
open import Web.Semantic.DL.KB using ( KB ; tbox ; abox )
open import Web.Semantic.DL.KB.Model using ( _⊨_ ; Interps ; ⊨-resp-≃ )
open import Web.Semantic.DL.Role using ( Role ; ⟨_⟩ ; ⟨_⟩⁻¹ )
open import Web.Semantic.DL.Role.Model using ( _⟦_⟧₂ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using
  ( TBox ; ε ; _,_ ; _⊑₁_ ; _⊑₂_ ; Dis ; Ref ; Irr ; Tra )
open import Web.Semantic.DL.TBox.Interp using ( _⊨_≈_ ; ≈-sym ; ≈-trans )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( ≲-resp-≈ ; iso )
open import Web.Semantic.DL.TBox.Model using ( _⊨t_ )
open import Web.Semantic.Util using
  ( ExclMiddle ; ExclMiddle₁ ; smaller-excl-middle ; is! ; is✓ ; tt ; elim )

module Web.Semantic.DL.Integrity.Closed.Properties
  (excl-middle₁ : ExclMiddle₁) {Σ : Signature} {X : Set} where

-- The two definitions of closed-world integrity coincide for surjective interpretations.
-- Note that this requires excluded middle, as the alternate definition assumes a classical logic,
-- for example interpreting C ⊑ D as (¬ C ⊔ D) being a tautology.

min : KB Σ X → Interp Σ X
min K = meet excl-middle₁ (Interps K)

excl-middle : ExclMiddle
excl-middle = smaller-excl-middle excl-middle₁

sound∼ : ∀ K x y → (K ⊫ x ∼ y) → (⌊ min K ⌋ ⊨ x ≈ y)
sound∼ K x y (eq K⊫x∼y) = is! K⊫x∼y

complete∼ : ∀ K x y → (⌊ min K ⌋ ⊨ x ≈ y) → (K ⊫ x ∼ y)
complete∼ K x y x≈y = eq (is✓ x≈y)

sound₂ : ∀ K R xy → (K ⊫ xy ∈₂ R) → (xy ∈ ⌊ min K ⌋ ⟦ R ⟧₂)
sound₂ K ⟨ r ⟩   (x , y) (rel K⊫xy∈r) = is! K⊫xy∈r
sound₂ K ⟨ r ⟩⁻¹ (x , y) (rev K⊫yx∈r) = is! K⊫yx∈r

complete₂ : ∀ K R xy → (xy ∈ ⌊ min K ⌋ ⟦ R ⟧₂) → (K ⊫ xy ∈₂ R)
complete₂ K ⟨ r ⟩   (x , y) xy∈⟦r⟧ = rel (is✓ xy∈⟦r⟧)
complete₂ K ⟨ r ⟩⁻¹ (x , y) yx∈⟦r⟧ = rev (is✓ yx∈⟦r⟧)

sound₁ : ∀ K C x → (K ⊫ x ∈₁ C) → (x ∈ ⌊ min K ⌋ ⟦ C ⟧₁)
sound₁ K ⟨ c ⟩ x (+atom K⊨x∈c) =
  is! K⊨x∈c
sound₁ K ¬⟨ c ⟩ x (-atom K⊭x∈c) =
  λ x∈⟦c⟧ → K⊭x∈c (is✓ x∈⟦c⟧)
sound₁ K ⊤ x top = tt
sound₁ K ⊥ x ()
sound₁ K (C ⊓ D) x (K⊫x∈C , K⊫x∈D) =
  (sound₁ K C x K⊫x∈C , sound₁ K D x K⊫x∈D)
sound₁ K (C ⊔ D) x (inj₁ K⊫x∈C) =
  inj₁ (sound₁ K C x K⊫x∈C)
sound₁ K (C ⊔ D) x (inj₂ K⊫x∈D) =
  inj₂ (sound₁ K D x K⊫x∈D)
sound₁ K (∀[ R ] C) x (all K⊫x∈∀RC) =
  λ y xy∈⟦R⟧ → sound₁ K C y (K⊫x∈∀RC y (complete₂ K R _ xy∈⟦R⟧))
sound₁ K (∃⟨ R ⟩ C) x (ex y K⊫xy∈R K⊫y∈C) =
  (y , sound₂ K R _ K⊫xy∈R , sound₁ K C y K⊫y∈C)
sound₁ K (≤1 R) x (uniq K⊫x∈≤1R) =
  λ y z xy∈⟦R⟧ xz∈⟦R⟧ → sound∼ K y z
    (K⊫x∈≤1R y z (complete₂ K R _ xy∈⟦R⟧) (complete₂ K R _ xz∈⟦R⟧))
sound₁ K (>1 R) x (¬uniq y z K⊫xy∈R K⊫xz∈R K⊯y∼z) =
  ( y , z , sound₂ K R _ K⊫xy∈R , sound₂ K R _ K⊫xz∈R
  , λ y≈z → K⊯y∼z (complete∼ K y z y≈z) )

complete₁ : ∀ K C x → (x ∈ ⌊ min K ⌋ ⟦ C ⟧₁) → (K ⊫ x ∈₁ C)
complete₁ K ⟨ c ⟩ x x∈⟦c⟧ =
  +atom (is✓ x∈⟦c⟧)
complete₁ K ¬⟨ c ⟩ x x∉⟦c⟧ =
  -atom (λ K⊫x∈c → x∉⟦c⟧ (is! K⊫x∈c))
complete₁ K ⊤ x x∈⟦C⟧ =
  top
complete₁ K ⊥ x ()
complete₁ K (C ⊓ D) x (x∈⟦C⟧ , x∈⟦D⟧) =
  (complete₁ K C x x∈⟦C⟧ , complete₁ K D x x∈⟦D⟧)
complete₁ K (C ⊔ D) x (inj₁ x∈⟦C⟧) =
  inj₁ (complete₁ K C x x∈⟦C⟧)
complete₁ K (C ⊔ D) x (inj₂ x∈⟦D⟧) =
  inj₂ (complete₁ K D x x∈⟦D⟧)
complete₁ K (∀[ R ] C) x x∈⟦∀RC⟧ =
  all (λ y K⊫xy∈R → complete₁ K C y (x∈⟦∀RC⟧ y (sound₂ K R _ K⊫xy∈R)))
complete₁ K (∃⟨ R ⟩ C) x (y , xy∈⟦R⟧ , y∈⟦C⟧) =
  ex y (complete₂ K R (x , y) xy∈⟦R⟧) (complete₁ K C y y∈⟦C⟧)
complete₁ K (≤1 R) x x∈⟦≤1R⟧ =
  uniq (λ y z K⊫xy∈R K⊫xz∈R → complete∼ K y z
    (x∈⟦≤1R⟧ y z (sound₂ K R _ K⊫xy∈R) (sound₂ K R _ K⊫xz∈R)))
complete₁ K (>1 R) x (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z) =
  ¬uniq y z (complete₂ K R _ xy∈⟦R⟧) (complete₂ K R _ xz∈⟦R⟧)
    (λ K⊫y∼z → y≉z (sound∼ K y z K⊫y∼z))

⊫-impl-min⊨ : ∀ K L → (K ⊫k L) → (min K ⊨ L)
⊫-impl-min⊨ K L (K⊫T , K⊫A) = ( J⊨T K⊫T , J⊨A K⊫A ) where

  J : Interp Σ X
  J = min K

  J⊨T : ∀ {T} → (K ⊫t T) → (⌊ J ⌋ ⊨t T)
  J⊨T ε = tt
  J⊨T (K⊫T , K⊫U) = (J⊨T K⊫T , J⊨T K⊫U)
  J⊨T (rl Q R K⊫Q⊑R) =
    λ {xy} xy∈⟦Q⟧ → sound₂ K R xy (K⊫Q⊑R xy (complete₂ K Q xy xy∈⟦Q⟧))
  J⊨T (cn C D K⊫C⊑D) = λ {x} → lemma x (K⊫C⊑D x) where

    lemma : ∀ x → (K ⊫ x ∈₁ (neg C ⊔ D)) →
      (x ∈ ⌊ J ⌋ ⟦ C ⟧₁) → (x ∈ ⌊ J ⌋ ⟦ D ⟧₁)
    lemma x (inj₁ K⊫x∈¬C) x∈⟦C⟧ =
      elim (neg-sound ⌊ J ⌋ {x} C (sound₁ K (neg C) x K⊫x∈¬C) x∈⟦C⟧)
    lemma x (inj₂ K⊫x∈D) x∈⟦C⟧ =
      sound₁ K D x K⊫x∈D

  J⊨T (dis Q R K⊫DisQR) = λ {xy} xy∈⟦Q⟧ xy∈⟦R⟧ →
    K⊫DisQR xy (complete₂ K Q xy xy∈⟦Q⟧) (complete₂ K R xy xy∈⟦R⟧)
  J⊨T (ref R K⊫RefR) = λ x → sound₂ K R (x , x) (K⊫RefR x)
  J⊨T (irr R K⊫IrrR) = λ x xx∈⟦R⟧ → K⊫IrrR x (complete₂ K R (x , x) xx∈⟦R⟧)
  J⊨T (tra R K⊫TraR) = λ {x} {y} {z} xy∈⟦R⟧ yz∈⟦R⟧ →
    sound₂ K R (x , z) (K⊫TraR x y z
      (complete₂ K R (x , y) xy∈⟦R⟧) (complete₂ K R (y , z) yz∈⟦R⟧))

  J⊨A : ∀ {A} → (K ⊫a A) → (J ⊨a A)
  J⊨A ε = tt
  J⊨A (K⊫A , K⊫B) = (J⊨A K⊫A , J⊨A K⊫B)
  J⊨A (eq x y K⊫x∼y) = sound∼ K x y K⊫x∼y
  J⊨A (rl (x , y) r K⊫xy∈r) = sound₂ K ⟨ r ⟩ (x , y) K⊫xy∈r
  J⊨A (cn x c K⊫x∈c) = sound₁ K ⟨ c ⟩ x K⊫x∈c

min⊨-impl-⊫ : ∀ K L → (min K ⊨ L) → (K ⊫k L)
min⊨-impl-⊫ K L (J⊨T , J⊨A) =
  ( K⊫T (tbox L) J⊨T ,  K⊫A (abox L) J⊨A ) where

  J : Interp Σ X
  J = min K

  K⊫T : ∀ T → (⌊ J ⌋ ⊨t T) → (K ⊫t T)
  K⊫T ε J⊨ε = ε
  K⊫T (T , U) (J⊨T , J⊨U) = (K⊫T T J⊨T , K⊫T U J⊨U)
  K⊫T (Q ⊑₂ R) J⊨Q⊑R =
    rl Q R (λ xy K⊫xy∈Q → complete₂ K R xy (J⊨Q⊑R (sound₂ K Q xy K⊫xy∈Q)))
  K⊫T (C ⊑₁ D) J⊨C⊑D = cn C D lemma where

    lemma : ∀ x → (K ⊫ x ∈₁ neg C ⊔ D)
    lemma x with excl-middle (x ∈ ⌊ J ⌋ ⟦ C ⟧₁)
    lemma x | yes x∈⟦C⟧ =
      inj₂ (complete₁ K D x (J⊨C⊑D x∈⟦C⟧))
    lemma x | no  x∉⟦C⟧ =
      inj₁ (complete₁ K (neg C) x (neg-complete excl-middle ⌊ J ⌋ C x∉⟦C⟧))

  K⊫T (Dis Q R) J⊨DisQR =
    dis Q R (λ xy K⊫xy∈Q K⊫xy∈R →
      J⊨DisQR (sound₂ K Q xy K⊫xy∈Q) (sound₂ K R xy K⊫xy∈R))
  K⊫T (Ref R) J⊨RefR =
    ref R (λ x → complete₂ K R (x , x) (J⊨RefR x))
  K⊫T (Irr R) J⊨IrrR =
    irr R (λ x K⊫xx∈R → J⊨IrrR x (sound₂ K R (x , x) K⊫xx∈R))
  K⊫T (Tra R) J⊨TrR =
    tra R (λ x y z K⊫xy∈R K⊫yz∈R →
      complete₂ K R (x , z) (J⊨TrR
        (sound₂ K R (x , y) K⊫xy∈R) (sound₂ K R (y , z) K⊫yz∈R)))

  K⊫A : ∀ A → (J ⊨a A) → (K ⊫a A)
  K⊫A ε J⊨ε = ε
  K⊫A (A , B) (J⊨A , J⊨B) = (K⊫A A J⊨A , K⊫A B J⊨B)
  K⊫A (x ∼ y) x≈y = eq x y (complete∼ K x y x≈y)
  K⊫A (x ∈₁ c) x∈⟦c⟧ = cn x c (complete₁ K ⟨ c ⟩ x x∈⟦c⟧)
  K⊫A ((x , y) ∈₂ r) xy∈⟦r⟧ = rl (x , y) r (complete₂ K ⟨ r ⟩ (x , y) xy∈⟦r⟧)

min-med : ∀ (K : KB Σ X) J → (J ⊨ K) → Mediated₀ (min K) J
min-med K J J⊨K = (meet-lb excl-middle₁ (Interps K) J J⊨K , meet-uniq excl-middle₁ (Interps K) J J⊨K)

min-init : ∀ (K : KB Σ X) → (K ⊫k K) → (min K ∈ Initial₀ K)
min-init K K⊫K = ( ⊫-impl-min⊨ K K K⊫K , min-med K)

min-uniq : ∀ (I : Interp Σ X) (K : KB Σ X) → (I ∈ Surjective) → (I ∈ Initial₀ K) → (I ≃ min K)
min-uniq I K I∈Surj (I⊨K , I-med) =
  ( iso
      ≲⌊ meet-glb excl-middle₁ (Interps K) I I∈Surj lemma₁ ⌋
      ≲⌊ meet-lb excl-middle₁ (Interps K) I I⊨K ⌋
      (λ x → ≈-sym ⌊ I ⌋ (surj✓ I∈Surj x))
      (λ x → is! (lemma₂ x))
  , λ x → is! (lemma₂ x)) where

  lemma₁ : ∀ J J⊨K → I ≲ J
  lemma₁ J J⊨K with I-med J J⊨K
  lemma₁ J J⊨K | (I≲J , I≲J-uniq) = I≲J

  lemma₂ : ∀ x J J⊨K → ⌊ J ⌋ ⊨ ind J (ind⁻¹ I∈Surj (ind I x)) ≈ ind J x
  lemma₂ x J J⊨K =
    ≈-trans ⌊ J ⌋ (≈-sym ⌊ J ⌋ (≲-resp-ind (lemma₁ J J⊨K) (ind⁻¹ I∈Surj (ind I x))))
      (≈-trans ⌊ J ⌋ (≲-resp-≈ ≲⌊ lemma₁ J J⊨K ⌋ (≈-sym ⌊ I ⌋ (surj✓ I∈Surj (ind I x))))
        (≲-resp-ind (lemma₁ J J⊨K) x))

⊫-impl-⊨₀ : ∀ (KB₁ KB₂ : KB Σ X) → (KB₁ ⊫k KB₁) → (KB₁ ⊫k KB₂) → (sur KB₁ ⊨₀ KB₂)
⊫-impl-⊨₀ KB₁ KB₂ KB₁⊫KB₁ KB₁⊫KB₂ =
  ( min KB₁
  , meet-surj excl-middle₁ (Interps KB₁)
  , min-init KB₁ KB₁⊫KB₁
  , ⊫-impl-min⊨ KB₁ KB₂ KB₁⊫KB₂ )

⊨₀-impl-⊫₁ : ∀ (KB₁ KB₂ : KB Σ X) → (sur KB₁ ⊨₀ KB₂) → (KB₁ ⊫k KB₁)
⊨₀-impl-⊫₁ KB₁ KB₂ (I , I∈Surj , (I⊨KB₁ , I-med) , I⊨KB₂) =
  min⊨-impl-⊫ KB₁ KB₁ (⊨-resp-≃ (min-uniq I KB₁ I∈Surj (I⊨KB₁ , I-med)) KB₁ I⊨KB₁)

⊨₀-impl-⊫₂ : ∀ (KB₁ KB₂ : KB Σ X) → (sur KB₁ ⊨₀ KB₂) → (KB₁ ⊫k KB₂)
⊨₀-impl-⊫₂ KB₁ KB₂ (I , I∈Surj , I-init , I⊨KB₂) =
  min⊨-impl-⊫ KB₁ KB₂ (⊨-resp-≃ (min-uniq I KB₁ I∈Surj I-init) KB₂ I⊨KB₂)


{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.NType2
open import lib.types.Empty
open import lib.types.Group
open import lib.types.Int
open import lib.types.List
open import lib.types.Nat
open import lib.types.Pi
open import lib.types.SetQuotient
open import lib.types.Sigma
open import lib.types.Word
open import lib.groups.GroupProduct
open import lib.groups.Homomorphism
open import lib.groups.Isomorphism

module lib.groups.FreeAbelianGroup {i} where

-- the relation for making the quotient.

-- [fsr-sym] is not needed, but it seems more principled to
-- make [FormalSumRel] an equivalence relation.
data FormalSumRel {A : Type i} : Word A → Word A → Type i where
  fsr-refl : ∀ {l₁ l₂} → l₁ == l₂ → FormalSumRel l₁ l₂
  fsr-trans : ∀ {l₁ l₂ l₃} → FormalSumRel l₁ l₂ → FormalSumRel l₂ l₃ → FormalSumRel l₁ l₃
  fsr-sym : ∀ {l₁ l₂} → FormalSumRel l₁ l₂ → FormalSumRel l₂ l₁
  fsr-cons : ∀ x {l₁ l₂} → FormalSumRel l₁ l₂ → FormalSumRel (x :: l₁) (x :: l₂)
  fsr-swap : ∀ x₁ x₂ l → FormalSumRel (x₁ :: x₂ :: l) (x₂ :: x₁ :: l)
  fsr-flip : ∀ x₁ l → FormalSumRel (x₁ :: flip x₁ :: l) l

-- The quotient
FormalSum : Type i → Type i
FormalSum A = SetQuot (FormalSumRel {A})

module _ {A : Type i} where

  fs[_] : Word A → FormalSum A
  fs[_] = q[_]

  module FormalSumElim {k} {P : FormalSum A → Type k}
    {{p : {x : FormalSum A} → is-set (P x)}} (incl* : (a : Word A) → P fs[ a ])
    (rel* : ∀ {a₁ a₂} (r : FormalSumRel a₁ a₂) → incl* a₁ == incl* a₂ [ P ↓ quot-rel r ])
    = SetQuotElim incl* rel*
  open FormalSumElim public using () renaming (f to FormalSum-elim)

  module FormalSumRec {k} {B : Type k} {{p : is-set B}} (incl* : Word A → B)
    (rel* : ∀ {a₁ a₂} (r : FormalSumRel a₁ a₂) → incl* a₁ == incl* a₂)
    = SetQuotRec incl* rel*
  open FormalSumRec public using () renaming (f to FormalSum-rec)

module _ {A : Type i} where
  -- useful properties that remain public
  abstract
    FormalSumRel-cong-++-l :
      ∀ {l₁ l₁'} → FormalSumRel {A} l₁ l₁'
      → (l₂ : Word A)
      → FormalSumRel (l₁ ++ l₂) (l₁' ++ l₂)
    FormalSumRel-cong-++-l (fsr-refl idp) l₂ = fsr-refl idp
    FormalSumRel-cong-++-l (fsr-trans fsr₁ fsr₂) l₂ = fsr-trans (FormalSumRel-cong-++-l fsr₁ l₂) (FormalSumRel-cong-++-l fsr₂ l₂)
    FormalSumRel-cong-++-l (fsr-sym fsr) l₂ = fsr-sym (FormalSumRel-cong-++-l fsr l₂)
    FormalSumRel-cong-++-l (fsr-cons x fsr₁) l₂ = fsr-cons x (FormalSumRel-cong-++-l fsr₁ l₂)
    FormalSumRel-cong-++-l (fsr-swap x₁ x₂ l₁) l₂ = fsr-swap x₁ x₂ (l₁ ++ l₂)
    FormalSumRel-cong-++-l (fsr-flip x₁ l₁) l₂ = fsr-flip x₁ (l₁ ++ l₂)

    FormalSumRel-cong-++-r :
      ∀ (l₁ : Word A)
      → ∀ {l₂ l₂'} → FormalSumRel l₂ l₂'
      → FormalSumRel (l₁ ++ l₂) (l₁ ++ l₂')
    FormalSumRel-cong-++-r nil fsr₂ = fsr₂
    FormalSumRel-cong-++-r (x :: l₁) fsr₂ = fsr-cons x (FormalSumRel-cong-++-r l₁ fsr₂)

    FormalSumRel-swap1 : ∀ x l₁ l₂ → FormalSumRel {A} (l₁ ++ (x :: l₂)) (x :: l₁ ++ l₂)
    FormalSumRel-swap1 x nil l₂ = fsr-refl idp
    FormalSumRel-swap1 x (x₁ :: l₁) l₂ = fsr-trans (fsr-cons x₁ (FormalSumRel-swap1 x l₁ l₂)) (fsr-swap x₁ x (l₁ ++ l₂))

module _ (A : Type i) where
  private
    infixl 80 _⊞_
    _⊞_ : FormalSum A → FormalSum A → FormalSum A
    _⊞_ = FormalSum-rec
      (λ l₁ → FormalSum-rec (λ l₂ → fs[ l₁ ++ l₂ ])
        (λ r → quot-rel $ FormalSumRel-cong-++-r l₁ r))
      (λ {l₁} {l₁'} r → λ= $ FormalSum-elim
        (λ l₂ → quot-rel $ FormalSumRel-cong-++-l r l₂)
        (λ _ → prop-has-all-paths-↓))

    abstract
      FormalSumRel-cong-flip : ∀ {l₁ l₂}
        → FormalSumRel {A} l₁ l₂ → FormalSumRel (Word-flip l₁) (Word-flip l₂)
      FormalSumRel-cong-flip (fsr-refl idp) = fsr-refl idp
      FormalSumRel-cong-flip (fsr-trans fsr₁ fsr₂) = fsr-trans (FormalSumRel-cong-flip fsr₁) (FormalSumRel-cong-flip fsr₂)
      FormalSumRel-cong-flip (fsr-sym fsr) = fsr-sym (FormalSumRel-cong-flip fsr)
      FormalSumRel-cong-flip (fsr-cons x fsr₁) = fsr-cons (flip x) (FormalSumRel-cong-flip fsr₁)
      FormalSumRel-cong-flip (fsr-swap x₁ x₂ l) = fsr-swap (flip x₁) (flip x₂) (Word-flip l)
      FormalSumRel-cong-flip (fsr-flip (inl x) l) = fsr-flip (inr x) (Word-flip l)
      FormalSumRel-cong-flip (fsr-flip (inr x) l) = fsr-flip (inl x) (Word-flip l)

    ⊟ : FormalSum A → FormalSum A
    ⊟ = FormalSum-rec (fs[_] ∘ Word-flip)
      (λ r → quot-rel $ FormalSumRel-cong-flip r)

    ⊞-unit : FormalSum A
    ⊞-unit = fs[ nil ]

    abstract
      ⊞-unit-l : ∀ g → ⊞-unit ⊞ g == g
      ⊞-unit-l = FormalSum-elim
        (λ _ → idp)
        (λ _ → prop-has-all-paths-↓)

      ⊞-assoc : ∀ g₁ g₂ g₃ → (g₁ ⊞ g₂) ⊞ g₃ == g₁ ⊞ (g₂ ⊞ g₃)
      ⊞-assoc = FormalSum-elim
        (λ l₁ → FormalSum-elim
          (λ l₂ → FormalSum-elim
            (λ l₃ → ap fs[_] $ ++-assoc l₁ l₂ l₃)
            (λ _ → prop-has-all-paths-↓))
          (λ _ → prop-has-all-paths-↓))
        (λ _ → prop-has-all-paths-↓)

      Word-inv-l : ∀ l → FormalSumRel {A} (Word-flip l ++ l) nil
      Word-inv-l nil = fsr-refl idp
      Word-inv-l (x :: l) =
        fsr-trans (FormalSumRel-swap1 x (flip x :: Word-flip l) l) $
        fsr-trans (fsr-flip x (Word-flip l ++ l)) (Word-inv-l l)

      ⊟-inv-l : ∀ g → (⊟ g) ⊞ g == ⊞-unit
      ⊟-inv-l = FormalSum-elim
        (λ l → quot-rel (Word-inv-l l))
        (λ _ → prop-has-all-paths-↓)

      FormalSumRel-swap : ∀ l₁ l₂ → FormalSumRel {A} (l₁ ++ l₂) (l₂ ++ l₁)
      FormalSumRel-swap l₁ nil = fsr-refl (++-unit-r l₁)
      FormalSumRel-swap l₁ (x :: l₂) = fsr-trans (FormalSumRel-swap1 x l₁ l₂) (fsr-cons x (FormalSumRel-swap l₁ l₂))

      ⊞-comm : ∀ g₁ g₂ → g₁ ⊞ g₂ == g₂ ⊞ g₁
      ⊞-comm = FormalSum-elim
        (λ l₁ → FormalSum-elim
          (λ l₂ → quot-rel $ FormalSumRel-swap l₁ l₂)
          (λ _ → prop-has-all-paths-↓))
        (λ _ → prop-has-all-paths-↓)

  FormalSum-group-structure : GroupStructure (FormalSum A)
  FormalSum-group-structure = record
    { ident = ⊞-unit
    ; inv = ⊟
    ; comp = _⊞_
    ; unit-l = ⊞-unit-l
    ; assoc = ⊞-assoc
    ; inv-l = ⊟-inv-l
    }

  FreeAbGroup : AbGroup i
  FreeAbGroup = group _ FormalSum-group-structure , ⊞-comm

  module FreeAbGroup = AbGroup FreeAbGroup

{- Freeness (universal properties) -}

module _ {A : Type i} {j} (G : AbGroup j) where

  private
    module G = AbGroup G

    abstract
      Word-extendᴳ-emap : ∀ f {l₁ l₂}
        → FormalSumRel {A} l₁ l₂
        → Word-extendᴳ G.grp f l₁ == Word-extendᴳ G.grp f l₂
      Word-extendᴳ-emap f (fsr-refl idp) = idp
      Word-extendᴳ-emap f (fsr-trans fsr fsr₁) = (Word-extendᴳ-emap f fsr) ∙ (Word-extendᴳ-emap f fsr₁)
      Word-extendᴳ-emap f (fsr-sym fsr) = ! (Word-extendᴳ-emap f fsr)
      Word-extendᴳ-emap f (fsr-cons x fsr) = ap (G.comp (PlusMinus-extendᴳ G.grp f x)) (Word-extendᴳ-emap f fsr)
      Word-extendᴳ-emap f (fsr-swap x₁ x₂ l) =
          ! (G.assoc (PlusMinus-extendᴳ G.grp f x₁) (PlusMinus-extendᴳ G.grp f x₂) (Word-extendᴳ G.grp f l))
        ∙ ap (λ g → G.comp g (Word-extendᴳ G.grp f l)) (G.comm (PlusMinus-extendᴳ G.grp f x₁) (PlusMinus-extendᴳ G.grp f x₂))
        ∙ G.assoc (PlusMinus-extendᴳ G.grp f x₂) (PlusMinus-extendᴳ G.grp f x₁) (Word-extendᴳ G.grp f l)
      Word-extendᴳ-emap f (fsr-flip (inl x) l) =
          ! (G.assoc (f x) (G.inv (f x)) (Word-extendᴳ G.grp f l))
        ∙ ap (λ g → G.comp g (Word-extendᴳ G.grp f l)) (G.inv-r (f x)) ∙ G.unit-l _
      Word-extendᴳ-emap f (fsr-flip (inr x) l) =
          ! (G.assoc (G.inv (f x)) (f x) (Word-extendᴳ G.grp f l))
        ∙ ap (λ g → G.comp g (Word-extendᴳ G.grp f l)) (G.inv-l (f x)) ∙ G.unit-l _

  FormalSum-extend : (A → G.El) → FormalSum A → G.El
  FormalSum-extend f = FormalSum-rec (Word-extendᴳ G.grp f)
    (λ r → Word-extendᴳ-emap f r)

  FreeAbGroup-extend : (A → G.El) → (FreeAbGroup.grp A →ᴳ G.grp)
  FreeAbGroup-extend f' = record {M} where
    module M where
      f : FreeAbGroup.El A → G.El
      f = FormalSum-extend f'
      abstract
        pres-comp : preserves-comp (FreeAbGroup.comp A) G.comp f
        pres-comp =
          FormalSum-elim
            (λ l₁ → FormalSum-elim
              (λ l₂ → Word-extendᴳ-++ G.grp f' l₁ l₂)
              (λ _ → prop-has-all-paths-↓))
            (λ _ → prop-has-all-paths-↓)

  private
    module Lemma (hom : FreeAbGroup.grp A →ᴳ G.grp) where
      open GroupHom hom
      f* : A → G.El
      f* a = f fs[ inl a :: nil ]

      abstract
        PlusMinus-extend-hom : ∀ x → PlusMinus-extendᴳ G.grp f* x == f fs[ x :: nil ]
        PlusMinus-extend-hom (inl x) = idp
        PlusMinus-extend-hom (inr x) = ! $ pres-inv fs[ inl x :: nil ]

        Word-extend-hom : ∀ l → Word-extendᴳ G.grp f* l == f fs[ l ]
        Word-extend-hom nil = ! pres-ident
        Word-extend-hom (x :: l) = ap2 G.comp (PlusMinus-extend-hom x) (Word-extend-hom l) ∙ ! (pres-comp _ _)
    open Lemma

  FreeAbGroup-extend-is-equiv : is-equiv FreeAbGroup-extend
  FreeAbGroup-extend-is-equiv = is-eq _ from to-from from-to where
    to = FreeAbGroup-extend
    from = f*

    abstract
      to-from : ∀ h → to (from h) == h
      to-from h = group-hom= $ λ= $ FormalSum-elim
        (λ l → Word-extend-hom h l)
        (λ _ → prop-has-all-paths-↓)

      from-to : ∀ f → from (to f) == f
      from-to f = λ= λ a → G.unit-r (f a)

  FreeAbGroup-extend-hom : Πᴳ A (λ _ → G.grp) →ᴳ hom-group (FreeAbGroup.grp A) G
  FreeAbGroup-extend-hom = record {M} where
    module M where
      f : (A → G.El) → (FreeAbGroup.grp A →ᴳ G.grp)
      f = FreeAbGroup-extend
      abstract
        pres-comp : preserves-comp (Group.comp (Πᴳ A (λ _ → G.grp))) (Group.comp (hom-group (FreeAbGroup.grp A) G)) f
        pres-comp = λ f₁ f₂ → group-hom= $ λ= $
          FormalSum-elim
            (Word-extendᴳ-comp G f₁ f₂)
            (λ _ → prop-has-all-paths-↓)

  FreeAbGroup-extend-iso : Πᴳ A (λ _ → G.grp) ≃ᴳ hom-group (FreeAbGroup.grp A) G
  FreeAbGroup-extend-iso = FreeAbGroup-extend-hom , FreeAbGroup-extend-is-equiv

{- relation with [Word-exp] -}

module _ {A : Type i} where

  abstract
    FormalSumRel-pres-exp : ∀ (a : A) z →
      fs[ Word-exp a z ] == GroupStructure.exp (FormalSum-group-structure A) fs[ inl a :: nil ] z
    FormalSumRel-pres-exp a (pos O) = idp
    FormalSumRel-pres-exp a (pos (S O)) = idp
    FormalSumRel-pres-exp a (pos (S (S n))) =
      ap (Group.comp (FreeAbGroup.grp A) fs[ inl a :: nil ]) (FormalSumRel-pres-exp a (pos (S n)))
    FormalSumRel-pres-exp a (negsucc O) = idp
    FormalSumRel-pres-exp a (negsucc (S n)) =
      ap (Group.comp (FreeAbGroup.grp A) fs[ inr a :: nil ]) (FormalSumRel-pres-exp a (negsucc n))


open import Agda.Builtin.Reflection
open import Agda.Builtin.Equality
open import Agda.Builtin.Int
open import Agda.Builtin.String
open import Common.Prelude

getFixity = primQNameFixity

infixl -1729 _to_
_to_ : Set → Set → Set
A to B = A → B

data Check : Set₁ where
  equal : {A : Set} (x y : A) → x ≡ y → Check

pattern _==_ x y = equal x y refl

check-cons = getFixity (quote _∷_)  == fixity right-assoc (related (pos 5))
check-to   = getFixity (quote _to_) == fixity left-assoc  (related (negsuc 1728))
check-eq   = getFixity (quote _≡_)  == fixity non-assoc   (related (pos 4))
check-list = getFixity (quote List) == fixity non-assoc   unrelated

showAssoc : Associativity → String
showAssoc left-assoc  = "infixl"
showAssoc right-assoc = "infixr"
showAssoc non-assoc   = "infix"

showFixity : Fixity → String
showFixity (fixity a unrelated)   = "(no fixity)"
showFixity (fixity a (related p)) = showAssoc a +S+ " " +S+ intToString p

showFix : Name → String
showFix x = showFixity (getFixity x)

main : IO Unit
main = putStrLn (showFix (quote _∷_))  >>= λ _ →
       putStrLn (showFix (quote _to_)) >>= λ _ →
       putStrLn (showFix (quote _≡_))  >>= λ _ →
       putStrLn (showFix (quote List)) >>= λ _ →
       return unit


module bstd.belt where

open import Agda.Builtin.Nat using () renaming (Nat to ℕ)
-- open import Agda.Builtin.Word using (Word64)

record traits : Set₁ where
  infixl 5 _⊕_
  infixl 6 _⊞_ _⊟_
  field
    w : Set
    _⊟_ : w → w → w
    _⊞_ : w → w → w
    _⊕_ : w → w → w
    G_ : ℕ → w → w
    _⋘_ : w → ℕ → w

module prim (t : traits) where

  open traits t
  record block : Set where
    constructor _∶_∶_∶_
    field
      a b c d : w

  record key : Set where

  G₅ = G 5
  G₁₃ = G 13
  G₂₁ = G 21

  encᵢ : w → (ℕ → w) → block → block
  encᵢ i k (a ∶ b ∶ c ∶ d) = b‴ ∶ d′ ∶ a′ ∶ c‴ where
    b′ = b ⊕ G₅ (a ⊞ k 0)
    c′ = c ⊕ G₂₁ (d ⊞ k 1)
    a′ = a ⊟ G₁₃ (b ⊞ k 2)
    e = G₂₁ (b ⊞ c ⊞ k 3) ⊕ i
    b″ = b′ ⊞ e
    c″ = c′ ⊟ e
    d′ = d ⊞ G₁₃ (c ⊞ k 4)
    b‴ = b″ ⊕ G₂₁ (a ⊞ k 5)
    c‴ = c″ ⊕ G₅ (d ⊞ k 6)

  𝔼 : key → block → block
  𝔼 k b = {!encᵢ 2 k (encᵢ 1 k x))!}

{-
0 B1 94 BA C8 0A 08 F5 3B 36 6D 00 8E 58 4A 5D E4
1 85 04 FA 9D 1B B6 C7 AC 25 2E 72 C2 02 FD CE 0D
2 5B E3 D6 12 17 B9 61 81 FE 67 86 AD 71 6B 89 0B
3 5C B0 C0 FF 33 C3 56 B8 35 C4 05 AE D8 E0 7F 99
4 E1 2B DC 1A E2 82 57 EC 70 3F CC F0 95 EE 8D F1
5 C1 AB 76 38 9F E6 78 CA F7 C6 F8 60 D5 BB 9C 4F
6 F3 3C 65 7B 63 7C 30 6A DD 4E A7 79 9E B2 3D 31
7 3E 98 B5 6E 27 D3 BC CF 59 1E 18 1F 4C 5A B7 93
8 E9 DE E7 2C 8F 0C 0F A6 2D DB 49 F4 6F 73 96 47
9 06 07 53 16 ED 24 7A 37 39 CB A3 83 03 A9 8B F6
A 92 BD 9B 1C E5 D1 41 01 54 45 FB C9 5E 4D 0E F2
B 68 20 80 AA 22 7D 64 2F 26 87 F9 34 90 40 55 11
C BE 32 97 13 43 FC 9A 48 A0 2A 88 5F 19 4B 09 A1
D 7E CD A4 D0 15 44 AF 8C A5 84 50 BF 66 D2 E8 8A
E A2 D7 46 52 42 A8 DF B3 69 74 C5 51 EB 23 29 21
F D4 EF D9 B4 3A 62 28 75 91 14 10 EA 77 6C DA 1D
-}


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

module Definition.Conversion.Consequences.Completeness where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Conversion

open import Definition.Conversion.EqRelInstance
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Escape
open import Definition.LogicalRelation.Fundamental

open import Tools.Product

-- Algorithmic equality is derivable from judgemental equality of types.
completeEq : ∀ {A B Γ} → Γ ⊢ A ≡ B → Γ ⊢ A [conv↑] B
completeEq A≡B =
  let [Γ] , [A] , [B] , [A≡B] = fundamentalEq A≡B
  in  escapeEqᵛ [Γ] [A] [A≡B]

-- Algorithmic equality is derivable from judgemental equality of terms.
completeEqTerm : ∀ {t u A Γ} → Γ ⊢ t ≡ u ∷ A → Γ ⊢ t [conv↑] u ∷ A
completeEqTerm t≡u =
  let [Γ] , modelsTermEq [A] [t] [u] [t≡u] = fundamentalTermEq t≡u
  in  escapeEqTermᵛ [Γ] [A] [t≡u]


open import Prelude hiding (_<_)

module Implicits.Resolution.GenericFinite.Resolution where

open import Coinduction
open import Data.List
open import Data.List.Any
open Membership-≡
open import Data.Fin.Substitution
open import Relation.Binary using (Rel)

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

private
  module M = MetaTypeMetaSubst

module ResolutionRules (cond : TerminationCondition) where
  open TerminationCondition cond

  mutual
    data _,_⊢_↓_ {ν} (Δ : ICtx ν) : (Φ : TCtx) → Type ν → SimpleType ν → Set where
      i-simp : ∀ {Φ} a → Δ , Φ ⊢ simpl a ↓ a
      i-iabs : ∀ {Φ ρ₁ ρ₂ a} → let Φ' = (step Φ Δ ρ₁ ρ₂ a) in
               Φ' < Φ → Δ , Φ' ⊢ᵣ ρ₁ → Δ , Φ ⊢ ρ₂ ↓ a → Δ , Φ ⊢ ρ₁ ⇒ ρ₂ ↓ a
      i-tabs : ∀ {Φ ρ a} b → Δ , Φ ⊢ ρ tp[/tp b ] ↓ a → Δ , Φ ⊢ ∀' ρ ↓ a

    data _,_⊢ᵣ_ {ν} (Δ : ICtx ν) : TCtx → Type ν → Set where
      r-simp : ∀ {Φ r τ} → r ∈ Δ → Δ , Φ ⊢ r ↓ τ → Δ , Φ ⊢ᵣ simpl τ
      r-iabs : ∀ {Φ} ρ₁ {ρ₂} → ((ρ₁ ∷ Δ) , Φ ⊢ᵣ ρ₂) → Δ , Φ ⊢ᵣ (ρ₁ ⇒ ρ₂)
      r-tabs : ∀ {Φ ρ} → ictx-weaken Δ , Φ ⊢ᵣ ρ → Δ , Φ ⊢ᵣ ∀' ρ


module Contexts where

open import Prelude
open import T
open import SubstTheory

module Contexts where

  infix 60 _e$_ _$e_
  -- Has a hole of type (Γ ⊢ A), produces a term of type (Γ' ⊢ A')
  data TCtx (Γ : Ctx) (A : TTp) : (Γ' : Ctx) → (A' : TTp) → Set where
    ∘ : TCtx Γ A Γ A
    _e$_ : ∀{Γ' A' B} (e₁ : TExp Γ' (A' ⇒ B)) (C₂ : TCtx Γ A Γ' A') → TCtx Γ A Γ' B
    _$e_ : ∀{Γ' A' B} (C₁ : TCtx Γ A Γ' (A' ⇒ B)) (e₂ : TExp Γ' A') → TCtx Γ A Γ' B

    Λ : ∀{A₁ A₂ Γ'} (C : TCtx Γ A (A₁ :: Γ') A₂) → TCtx Γ A Γ' (A₁ ⇒ A₂)
    suc : ∀{Γ'} (C : TCtx Γ A Γ' nat) → TCtx Γ A Γ' nat

    rec1 : ∀{Γ' B} → (C : TCtx Γ A Γ' nat) → (e₀ : TExp Γ' B) → (es : TExp (B :: Γ') B) →
                     TCtx Γ A Γ' B
    rec2 : ∀{Γ' B} → (e : TExp Γ' nat) → (C₀ : TCtx Γ A Γ' B) → (es : TExp (B :: Γ') B) →
                     TCtx Γ A Γ' B
    rec3 : ∀{Γ' B} → (e : TExp Γ' nat) → (e₀ : TExp Γ' B) → (Cs : TCtx Γ A (B :: Γ') B) →
                     TCtx Γ A Γ' B

  _<_> : ∀{Γ A Γ' A'} → TCtx Γ A Γ' A' → TExp Γ A → TExp Γ' A'
  ∘ < e' > = e'
  (e₁ e$ C₂) < e' > = e₁ $ C₂ < e' >
  (C₁ $e e₂) < e' > = C₁ < e' > $ e₂
  Λ C < e' > = Λ (C < e' >)
  suc C < e' > = suc (C < e' >)
  rec1 C e₀ es < e' > = rec (C < e' >) e₀ es
  rec2 e C₀ es < e' > = rec e (C₀ < e' >) es
  rec3 e e₀ Cs < e' > = rec e e₀ (Cs < e' >)

  infix 70 _<<_>> _<_>
  _<<_>> : ∀{Γ A Γ' A' Γ'' A''} → TCtx Γ' A' Γ'' A'' → TCtx Γ A Γ' A' →
            TCtx Γ A Γ'' A''
  ∘ << C' >> = C'
  (e₁ e$ C₂) << C' >> = e₁ e$ C₂ << C' >>
  (C₁ $e e₂) << C' >> = C₁ << C' >> $e e₂
  Λ C << C' >> = Λ (C << C' >>)
  suc C << C' >> = suc (C << C' >>)
  rec1 C e₀ es << C' >> = rec1 (C << C' >>) e₀ es
  rec2 e C₀ es << C' >> = rec2 e (C₀ << C' >>) es
  rec3 e e₀ Cs << C' >> = rec3 e e₀ (Cs << C' >>)

  -- I hate having to prove this sort of theorem.
  composing-commutes : ∀{Γ A Γ' A' Γ'' A''} →
                       (C : TCtx Γ' A' Γ'' A'') →
                       (C' : TCtx Γ A Γ' A') →
                       (e : TExp Γ A) →
                       ((C << C' >>) < e >) ≡ C < C' < e > >
  composing-commutes ∘ C' e = Refl
  composing-commutes (e₁ e$ C) C' e = resp (_$_ e₁) (composing-commutes C C' e)
  composing-commutes (C $e e₂) C' e = resp (λ x → x $ e₂) (composing-commutes C C' e)
  composing-commutes (Λ C) C' e = resp Λ (composing-commutes C C' e)
  composing-commutes (suc C) C' e = resp suc (composing-commutes C C' e)
  composing-commutes (rec1 C e₀ es) C' e = resp (λ x → rec x e₀ es) (composing-commutes C C' e)
  composing-commutes (rec2 e' C es) C' e = resp (λ x → rec e' x es) (composing-commutes C C' e)
  composing-commutes (rec3 e' e₀ C) C' e = resp (λ x → rec e' e₀ x) (composing-commutes C C' e)

  -- If the hole is closed, then the rest needs to be too.
  tctx-empty-thing : ∀{F : Set} {Γ A A' B} → TCtx [] A (B :: Γ) A' → F
  tctx-empty-thing (e₁ e$ C) = tctx-empty-thing C
  tctx-empty-thing (C $e e₂) = tctx-empty-thing C
  tctx-empty-thing (Λ C) = tctx-empty-thing C
  tctx-empty-thing (suc C) = tctx-empty-thing C
  tctx-empty-thing (rec1 C e₀ es) = tctx-empty-thing C
  tctx-empty-thing (rec2 e C es) = tctx-empty-thing C
  tctx-empty-thing (rec3 e e₀ C) = tctx-empty-thing C

  -- Very restricted function for weakening a program context where
  -- the hole also occurs under no free variables.
  weaken-closed-tctx : ∀{Γ A A'} → TCtx [] A [] A' → TCtx Γ A Γ A'
  weaken-closed-tctx ∘ = ∘
  weaken-closed-tctx (e₁ e$ C) = weaken-closed e₁ e$ weaken-closed-tctx C
  weaken-closed-tctx (C $e e₂) = weaken-closed-tctx C $e weaken-closed e₂
  weaken-closed-tctx (Λ C) = tctx-empty-thing C
  weaken-closed-tctx (suc C) = suc (weaken-closed-tctx C)
  weaken-closed-tctx (rec1 C e₀ es) =
    rec1 (weaken-closed-tctx C) (weaken-closed e₀) (ren (wk closed-wkγ) es)
  weaken-closed-tctx (rec2 e C es) =
    rec2 (weaken-closed e) (weaken-closed-tctx C) (ren (wk closed-wkγ) es)
  weaken-closed-tctx (rec3 e e₀ C) = tctx-empty-thing C

  -- Substitution commutes with a closed context
  subst-commutes-w-closed-tctx : ∀{Γ A A'} → (γ : TSubst Γ []) → (C : TCtx [] A [] A') → (e : TExp Γ A) →
                                 C < ssubst γ e > ≡ ssubst γ ((weaken-closed-tctx C) < e >)
  subst-commutes-w-closed-tctx γ ∘ e = Refl
  subst-commutes-w-closed-tctx γ (e₁ e$ C) e =
    resp2 _$_
    (symm (closed-subst _ e₁) ≡≡ symm (subren γ closed-wkγ e₁))
    (subst-commutes-w-closed-tctx γ C e)
  subst-commutes-w-closed-tctx γ (C $e e₂) e =
    resp2 _$_
    (subst-commutes-w-closed-tctx γ C e)
    (symm (closed-subst _ e₂) ≡≡ symm (subren γ closed-wkγ e₂))
  subst-commutes-w-closed-tctx γ (Λ C) e = tctx-empty-thing C
  subst-commutes-w-closed-tctx γ (suc C) e =
    resp suc (subst-commutes-w-closed-tctx γ C e)
  subst-commutes-w-closed-tctx γ (rec1 C e₀ es) e with subren (liftγ γ) (wk closed-wkγ) es
  ... | lol1 with subeq (liftwk γ closed-wkγ) es
  ... | lol2 with lift-closed-subst (γ o closed-wkγ) es
  ... | lol3 with symm lol3 ≡≡ symm lol2 ≡≡ symm lol1
  ... | lol = resp3 rec
              (subst-commutes-w-closed-tctx γ C e)
              (symm (closed-subst _ e₀) ≡≡ symm (subren γ closed-wkγ e₀))
              lol
  subst-commutes-w-closed-tctx γ (rec2 en C es) e with subren (liftγ γ) (wk closed-wkγ) es
  ... | lol1 with subeq (liftwk γ closed-wkγ) es
  ... | lol2 with lift-closed-subst (γ o closed-wkγ) es
  ... | lol3 with symm lol3 ≡≡ symm lol2 ≡≡ symm lol1
  ... | lol = resp3 rec
              (symm (closed-subst _ en) ≡≡ symm (subren γ closed-wkγ en))
              (subst-commutes-w-closed-tctx γ C e)
              lol
  subst-commutes-w-closed-tctx γ (rec3 e e₀ C) e₁ = tctx-empty-thing C

open Contexts public