{- NEW TRANSLATION STUFF: same thing but interpret source nat as ♭nat-}

open import Preliminaries
open import Source
open import Complexity

module Translation where

  mutual
    ⟨⟨_⟩⟩ : Tp → CTp
    ⟨⟨ unit ⟩⟩ = unit
    ⟨⟨ nat ⟩⟩ = nat
    ⟨⟨ susp A ⟩⟩ = || A ||
    ⟨⟨ A ->s B ⟩⟩ = ⟨⟨ A ⟩⟩ ->c || B ||
    ⟨⟨ A ×s B ⟩⟩ = ⟨⟨ A ⟩⟩ ×c ⟨⟨ B ⟩⟩
    ⟨⟨ list A ⟩⟩ = list ⟨⟨ A ⟩⟩
    ⟨⟨ bool ⟩⟩ = bool

    ||_|| : Tp → CTp
    || τ || = C ×c ⟨⟨ τ ⟩⟩

  ⟨⟨_⟩⟩c : Source.Ctx → Complexity.Ctx
  ⟨⟨ [] ⟩⟩c = []
  ⟨⟨ x :: Γ ⟩⟩c = ⟨⟨ x ⟩⟩ :: ⟨⟨ Γ ⟩⟩c

  interp-Cost : ∀{Γ} → Cost → Γ Complexity.|- C
  interp-Cost 0c = 0C
  interp-Cost 1c = 1C
  interp-Cost (m +c n) = plusC (interp-Cost m) (interp-Cost n)

  lookup : ∀{Γ τ} → τ Source.∈ Γ → ⟨⟨ τ ⟩⟩ Complexity.∈ ⟨⟨ Γ ⟩⟩c
  lookup i0 = i0
  lookup (iS x) = iS (lookup x)

  _+C_ : ∀ {Γ τ} → Γ Complexity.|- C  → Γ Complexity.|- (C ×c τ)→ Γ Complexity.|- (C ×c τ)
  c +C e = prod (plusC c (l-proj e)) (r-proj e)

  -- translation from source expressions to complexity expressions
  ||_||e : ∀{Γ τ} → Γ Source.|- τ → ⟨⟨ Γ ⟩⟩c Complexity.|- || τ ||
  || unit ||e = prod 0C unit
  || var x ||e = prod 0C (var (lookup x))
  || z ||e = prod 0C z
  || suc e ||e = prod (l-proj (|| e ||e)) (s (r-proj (|| e ||e)))
  || rec e e0 e1 ||e = (l-proj (|| e ||e)) +C (rec (r-proj || e ||e) (1C +C || e0 ||e) (1C +C || e1 ||e))
  || lam e ||e = prod 0C (lam || e ||e)
  || app e1 e2 ||e =
    prod (plusC (plusC (plusC 1C (l-proj || e1 ||e)) (l-proj || e2 ||e)) (l-proj (app (r-proj || e1 ||e) (r-proj || e2 ||e))))
    (r-proj (app (r-proj || e1 ||e) (r-proj || e2 ||e)))
  || prod e1 e2 ||e = prod (plusC (l-proj (|| e1 ||e)) (l-proj (|| e2 ||e))) (prod (r-proj (|| e1 ||e)) (r-proj (|| e2 ||e)))
  || delay e ||e = prod 0C (|| e ||e)
  || force e ||e = prod (plusC (l-proj (|| e ||e)) (l-proj (r-proj || e ||e))) (r-proj (r-proj (|| e ||e)))
  || split e0 e1 ||e = prod (plusC (l-proj (|| e0 ||e)) (l-proj E1)) (r-proj E1)
    where E1 = (Complexity.subst || e1 ||e (Complexity.lem4 (l-proj (r-proj || e0 ||e)) (r-proj (r-proj || e0 ||e))))
  || nil ||e = prod 0C nil
  || e ::s e₁ ||e = prod (plusC (l-proj || e ||e) (l-proj || e₁ ||e)) ((r-proj || e ||e) ::c (r-proj || e₁ ||e))
  || listrec e e₁ e₂ ||e = l-proj || e ||e +C listrec (r-proj || e ||e) (1C +C || e₁ ||e) (1C +C || e₂ ||e)
  || true ||e = prod 0C true
  || false ||e = prod 0C false

  -- new translation with let bindings to reduce exponential increase in term size

  throw-s : ∀ {Γ Γ' τ} → Complexity.sctx Γ (τ :: Γ') → Complexity.sctx Γ Γ'
  throw-s Θ x = Θ (iS x)

  _+C'_ : ∀ {Γ τ} → Γ Complexity.|- C  → Γ Complexity.|- (C ×c τ)→ Γ Complexity.|- (C ×c τ)
  c +C' e = letc (prod (plusC (Complexity.wkn c) (l-proj (var i0))) (r-proj (var i0))) e

  ||_||e' : ∀{Γ τ} → Γ Source.|- τ → ⟨⟨ Γ ⟩⟩c Complexity.|- || τ ||
  || unit ||e' = prod 0C unit
  || var x ||e' = prod 0C (var (lookup x))
  || z ||e' = prod 0C z
  || suc e ||e' = (letc (prod (l-proj (var i0)) (s (r-proj (var i0)))) || e ||e')
--  || rec e e0 e1 ||e = (l-proj (|| e ||e)) +C (rec (r-proj || e ||e) (1C +C || e0 ||e) (1C +C || e1 ||e))
  || rec e e0 e1 ||e' =
      letc (l-proj (var i0) +C' rec (r-proj (var i0))
                   (Complexity.wkn (1C +C' || e0 ||e'))
                   (Complexity.subst (1C +C' || e1 ||e') (Complexity.s-extend (Complexity.s-extend (throw-s Complexity.ids)))))
           || e ||e'
  || lam e ||e' = prod 0C (lam || e ||e')
  || app e1 e2 ||e' = letc (letc (prod (plusC (plusC (plusC 1C (l-proj (var (iS i0)))) (l-proj (var i0))) (l-proj (app (r-proj (var (iS i0))) (r-proj (var i0)))))
                            (r-proj (app (r-proj (var (iS i0))) (r-proj (var i0))))) (Complexity.wkn || e2 ||e')) || e1 ||e'
  || prod e1 e2 ||e' = letc (letc (prod (plusC (l-proj (var (iS i0))) (l-proj (var i0))) (prod (r-proj (var (iS i0))) (r-proj (var i0)))) (Complexity.wkn || e2 ||e')) || e1 ||e'
  || delay e ||e' = prod 0C || e ||e'
  || force e ||e' = letc (prod (plusC (l-proj (var i0)) (l-proj (r-proj (var i0)))) (r-proj (r-proj (var i0)))) || e ||e'
  || split e0 e1 ||e' = letc (prod (plusC (Complexity.wkn (l-proj || e0 ||e')) (l-proj (var i0))) (r-proj (var i0))) E1
       where E1 = letc (Complexity.subst || e1 ||e' (Complexity.lem4 (l-proj (r-proj (var i0))) (r-proj (r-proj (var i0))) Complexity.ss Complexity.s-extend (Complexity.s-extend (throw-s Complexity.ids)))) || e0 ||e'
  || nil ||e' = prod 0C nil
  || e ::s e₁ ||e' = letc (letc (prod (plusC (l-proj (var (iS i0))) (l-proj (var i0))) (r-proj (var (iS i0)) ::c r-proj (var i0))) (Complexity.wkn || e₁ ||e')) || e ||e'
  || listrec e e₁ e₂ ||e' =
      letc (l-proj (var i0) +C' listrec (r-proj (var i0))
        (Complexity.wkn (1C +C' || e₁ ||e')) (Complexity.subst (1C +C' || e₂ ||e') (Complexity.s-extend (Complexity.s-extend (Complexity.s-extend (throw-s Complexity.ids)))))) || e ||e'
  || true ||e' = prod 0C true
  || false ||e' = prod 0C false

{- try prove ||.||old <=/=> ||.||new

  trans-eq-l : ∀ {Γ τ} → (e : Γ Source.|- τ) → || e ||e ≤s || e ||e'
  trans-eq-l unit = refl-s
  trans-eq-l (var x) = refl-s
  trans-eq-l z = refl-s
  trans-eq-l (suc e) = (cong-prod (cong-lproj (trans-eq-l e)) (cong-suc (cong-rproj (trans-eq-l e))) trans lam-s) trans letc-app-r
  trans-eq-l (rec e e₁ e₂) = {!!}
  trans-eq-l (lam e) = cong-prod refl-s (cong-lam (trans-eq-l e))
  trans-eq-l (app e e₁) = {!!}
  trans-eq-l (prod e e₁) = (((cong-prod (cong-+ (cong-lproj {!!}) (cong-lproj {!!})) {!!} trans lam-s) trans letc-app-r) trans lam-s) trans letc-app-r
  trans-eq-l (delay e) = {!!}
  trans-eq-l (force e) = {!!}
  trans-eq-l (split e e₁) = {!!}
  trans-eq-l nil = {!!}
  trans-eq-l (e ::s e₁) = {!!}
  trans-eq-l (listrec e e₁ e₂) = {!!}
  trans-eq-l true = refl-s
  trans-eq-l false = refl-s
-}


open import Mockingbird.Forest using (Forest)

-- A Gallery of Sage Birds
module Mockingbird.Problems.Chapter13 {b ℓ} (forest : Forest {b} {ℓ}) where

open import Function using (_$_)
open import Data.Product using (proj₂)

open import Mockingbird.Forest.Birds forest
import Mockingbird.Problems.Chapter09 forest as Chapter₉
import Mockingbird.Problems.Chapter10 forest as Chapter₁₀
import Mockingbird.Problems.Chapter11 forest as Chapter₁₁
import Mockingbird.Problems.Chapter12 forest as Chapter₁₂

open Forest forest

problem₁ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasRobin ⦄ → HasSageBird
problem₁ = record
  { Θ = B ∙ M ∙ (R ∙ M ∙ B)
  ; isSageBird = λ x → begin
      x ∙ (B ∙ M ∙ (R ∙ M ∙ B) ∙ x)  ≈⟨ congˡ lemma ⟩
      x ∙ (M ∙ (B ∙ x ∙ M))          ≈⟨ isFond ⟩
      M ∙ (B ∙ x ∙ M)                ≈˘⟨ lemma ⟩
      B ∙ M ∙ (R ∙ M ∙ B) ∙ x        ∎
  } where
  isFond = λ {x} → proj₂ (Chapter₁₁.problem₂ x)

  lemma : ∀ {x} → B ∙ M ∙ (R ∙ M ∙ B) ∙ x ≈ M ∙ (B ∙ x ∙ M)
  lemma {x} = begin
    B ∙ M ∙ (R ∙ M ∙ B) ∙ x  ≈⟨ isBluebird M (R ∙ M ∙ B) x ⟩
    M ∙ (R ∙ M ∙ B ∙ x)      ≈⟨ congˡ $ isRobin M B x ⟩
    M ∙ (B ∙ x ∙ M)          ∎

problem₂ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasCardinal ⦄ → HasSageBird
problem₂ = record
  { Θ = B ∙ M ∙ (C ∙ B ∙ M)
  ; isSageBird = λ x → begin
      x ∙ (B ∙ M ∙ (C ∙ B ∙ M) ∙ x)  ≈⟨ congˡ lemma ⟩
      x ∙ (M ∙ (B ∙ x ∙ M))          ≈⟨ isFond ⟩
      M ∙ (B ∙ x ∙ M)                ≈˘⟨ lemma ⟩
      B ∙ M ∙ (C ∙ B ∙ M) ∙ x        ∎
  } where
  isFond = λ {x} → proj₂ (Chapter₁₁.problem₂ x)

  lemma : ∀ {x} → B ∙ M ∙ (C ∙ B ∙ M) ∙ x ≈ M ∙ (B ∙ x ∙ M)
  lemma {x} = begin
    B ∙ M ∙ (C ∙ B ∙ M) ∙ x  ≈⟨ isBluebird M (C ∙ B ∙ M) x ⟩
    M ∙ (C ∙ B ∙ M ∙ x)      ≈⟨ congˡ $ isCardinal B M x ⟩
    M ∙ (B ∙ x ∙ M)          ∎

problem₃ : ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasBluebird ⦄ ⦃ _ : HasLark ⦄ → HasSageBird
problem₃ = record
  { Θ = B ∙ M ∙ L
  ; isSageBird = isSageBird ⦃ Chapter₁₀.hasSageBird ⦄
  } where instance hasComposition = Chapter₁₁.problem₁

problem₄ : ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasBluebird ⦄ ⦃ _ : HasWarbler ⦄ → HasSageBird
problem₄ = record
  { Θ = B ∙ M ∙ (B ∙ W ∙ B)
  ; isSageBird = isSageBird ⦃ problem₃ ⦄
  } where instance hasLark = Chapter₁₂.problem₃

problem₆ : ⦃ _ : HasQueerBird ⦄ ⦃ _ : HasLark ⦄ ⦃ _ : HasWarbler ⦄ → HasSageBird
problem₆ = record
  { Θ = W ∙ (Q ∙ L ∙ (Q ∙ L))
  ; isSageBird = λ x → begin
      x ∙ (W ∙ (Q ∙ L ∙ (Q ∙ L)) ∙ x)  ≈⟨ congˡ lemma ⟩
      x ∙ (L ∙ x ∙ (L ∙ x))            ≈⟨ isFond ⟩
      L ∙ x ∙ (L ∙ x)                  ≈˘⟨ lemma ⟩
      W ∙ (Q ∙ L ∙ (Q ∙ L)) ∙ x        ∎
  } where
  isFond = λ {x} → proj₂ (Chapter₉.problem₂₅ x)

  lemma : ∀ {x} → W ∙ (Q ∙ L ∙ (Q ∙ L)) ∙ x ≈ L ∙ x ∙ (L ∙ x)
  lemma {x} = begin
      W ∙ (Q ∙ L ∙ (Q ∙ L)) ∙ x  ≈⟨ isWarbler (Q ∙ L ∙ (Q ∙ L)) x ⟩
      Q ∙ L ∙ (Q ∙ L) ∙ x ∙ x    ≈⟨ congʳ $ isQueerBird L (Q ∙ L) x ⟩
      Q ∙ L ∙ (L ∙ x) ∙ x        ≈⟨ isQueerBird L (L ∙ x) x ⟩
      L ∙ x ∙ (L ∙ x)            ∎

problem₅ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄ → HasSageBird
problem₅ = problem₆
  where
    instance
      hasQueerBird = Chapter₁₁.problem₃₇′
      hasLark = Chapter₁₂.problem₃

problem₇ = problem₅

problem₈ : ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasQueerBird ⦄ → HasSageBird
problem₈ = record
  { Θ = Q ∙ (Q ∙ M) ∙ M
  ; isSageBird = λ x → sym $ begin
      Q ∙ (Q ∙ M) ∙ M ∙ x        ≈⟨ isQueerBird (Q ∙ M) M x ⟩
      M ∙ (Q ∙ M ∙ x)            ≈⟨ isMockingbird (Q ∙ M ∙ x) ⟩
      Q ∙ M ∙ x ∙ (Q ∙ M ∙ x)    ≈⟨ isQueerBird M x (Q ∙ M ∙ x) ⟩
      x ∙ (M ∙ (Q ∙ M ∙ x))      ≈˘⟨ congˡ $ isQueerBird (Q ∙ M) M x ⟩
      x ∙ (Q ∙ (Q ∙ M) ∙ M ∙ x)  ∎
  }

-- TODO: formalise regularity.

problem₉ : ⦃ _ : HasStarling ⦄ ⦃ _ : HasLark ⦄ → HasSageBird
problem₉ = record
  { Θ = S ∙ L ∙ L
  ; isSageBird = λ x → begin
      x ∙ (S ∙ L ∙ L ∙ x)    ≈⟨ congˡ $ isStarling L L x ⟩
      x ∙ (L ∙ x ∙ (L ∙ x))  ≈⟨ isFond ⟩
      L ∙ x ∙ (L ∙ x)        ≈˘⟨ isStarling L L x ⟩
      S ∙ L ∙ L ∙ x          ∎
  } where
  isFond = λ {x} → proj₂ (Chapter₉.problem₂₅ x)

problem₁₀ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasWarbler ⦄ ⦃ _ : HasStarling ⦄ → HasSageBird
problem₁₀ = record
  { Θ = W ∙ S ∙ (B ∙ W ∙ B)
  ; isSageBird = λ x → begin
      x ∙ (W ∙ S ∙ (B ∙ W ∙ B) ∙ x) ≈⟨⟩
      x ∙ (W ∙ S ∙ L ∙ x)      ≈⟨ congˡ $ congʳ $ isWarbler S L ⟩
      x ∙ (S ∙ L ∙ L ∙ x)      ≈⟨ isSageBird ⦃ problem₉ ⦄ x ⟩
      S ∙ L ∙ L ∙ x            ≈˘⟨ congʳ $ isWarbler S L ⟩
      W ∙ S ∙ L ∙ x            ≈⟨⟩
      W ∙ S ∙ (B ∙ W ∙ B) ∙ x  ∎
  } where instance hasLark = Chapter₁₂.problem₃

problem₁₁ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasThrush ⦄ → HasTuringBird
problem₁₁ = record
  { U = B ∙ (B ∙ W ∙ (C ∙ B)) ∙ M
  ; isTuringBird = λ x y → begin
      B ∙ (B ∙ W ∙ (C ∙ B)) ∙ M ∙ x ∙ y  ≈⟨ congʳ $ isBluebird (B ∙ W ∙ (C ∙ B)) M x ⟩
      B ∙ W ∙ (C ∙ B) ∙ (M ∙ x) ∙ y      ≈⟨ congʳ $ isBluebird W (C ∙ B) (M ∙ x) ⟩
      W ∙ (C ∙ B ∙ (M ∙ x)) ∙ y          ≈⟨ isWarbler (C ∙ B ∙ (M ∙ x)) y ⟩
      C ∙ B ∙ (M ∙ x) ∙ y ∙ y            ≈⟨ congʳ $ isCardinal B (M ∙ x) y ⟩
      B ∙ y ∙ (M ∙ x) ∙ y                ≈⟨ isBluebird y (M ∙ x) y ⟩
      y ∙ (M ∙ x ∙ y)                    ≈⟨ congˡ $ congʳ $ isMockingbird x ⟩
      y ∙ (x ∙ x ∙ y)                    ∎
  } where
  instance
    hasCardinal = Chapter₁₁.problem₂₁′
    hasLark = Chapter₁₂.problem₇

problem₁₂ : ⦃ _ : HasTuringBird ⦄ → HasSageBird
problem₁₂ = record
  { Θ = U ∙ U
  ; isSageBird = λ x → sym $ isTuringBird U x
  }

problem₁₃ : ⦃ _ : HasQueerBird ⦄ ⦃ _ : HasWarbler ⦄ → HasOwl
problem₁₃ = record
  { O = Q ∙ Q ∙ W
  ; isOwl = λ x y → begin
      Q ∙ Q ∙ W ∙ x ∙ y  ≈⟨ congʳ $ isQueerBird Q W x ⟩
      W ∙ (Q ∙ x) ∙ y    ≈⟨ isWarbler (Q ∙ x) y ⟩
      Q ∙ x ∙ y ∙ y      ≈⟨ isQueerBird x y y ⟩
      y ∙ (x ∙ y)        ∎
  }

problem₁₄ : ⦃ _ : HasOwl ⦄ ⦃ _ : HasLark ⦄ → HasTuringBird
problem₁₄ = record
  { U = L ∙ O
  ; isTuringBird = λ x y → begin
      L ∙ O ∙ x ∙ y    ≈⟨ congʳ $ isLark O x ⟩
      O ∙ (x ∙ x) ∙ y  ≈⟨ isOwl (x ∙ x) y ⟩
      y ∙ (x ∙ x ∙ y)  ∎
  }

problem₁₅ : ⦃ _ : HasOwl ⦄ ⦃ _ : HasIdentity ⦄ → HasMockingbird
problem₁₅ = record
  { M = O ∙ I
  ; isMockingbird = λ x → begin
      O ∙ I ∙ x    ≈⟨ isOwl I x ⟩
      x ∙ (I ∙ x)  ≈⟨ congˡ $ isIdentity x ⟩
      x ∙ x        ∎
  }

problem₁₆ : ⦃ _ : HasStarling ⦄ ⦃ _ : HasIdentity ⦄ → HasOwl
problem₁₆ = record
  { O = S ∙ I
  ; isOwl = λ x y → begin
      S ∙ I ∙ x ∙ y    ≈⟨ isStarling I x y ⟩
      I ∙ y ∙ (x ∙ y)  ≈⟨ congʳ $ isIdentity y ⟩
      y ∙ (x ∙ y)      ∎
  }

problem₁₇ : ∀ x y → x IsFondOf y → x IsFondOf x ∙ y
problem₁₇ x y xy≈y = begin
  x ∙ (x ∙ y)  ≈⟨ congˡ xy≈y ⟩
  x ∙ y        ∎

problem₁₈ : ⦃ _ : HasOwl ⦄ ⦃ _ : HasSageBird ⦄ → IsSageBird (O ∙ Θ)
problem₁₈ x = begin
  x ∙ (O ∙ Θ ∙ x)    ≈⟨ congˡ $ isOwl Θ x ⟩
  x ∙ (x ∙ (Θ ∙ x))  ≈⟨ isFond ⟩
  x ∙ (Θ ∙ x)        ≈˘⟨ isOwl Θ x ⟩
  O ∙ Θ ∙ x          ∎
  where
    isFond : x IsFondOf (x ∙ (Θ ∙ x))
    isFond = problem₁₇ x (Θ ∙ x) (isSageBird x)

problem₁₉ : ⦃ _ : HasOwl ⦄ ⦃ _ : HasSageBird ⦄ → IsSageBird (Θ ∙ O)
problem₁₉ x = sym $ begin
  Θ ∙ O ∙ x        ≈˘⟨ congʳ $ isSageBird O ⟩
  O ∙ (Θ ∙ O) ∙ x  ≈⟨ isOwl (Θ ∙ O) x ⟩
  x ∙ (Θ ∙ O ∙ x)  ∎

problem₂₀ : ⦃ _ : HasOwl ⦄ → ∀ A → O IsFondOf A → IsSageBird A
problem₂₀ A OA≈A x = begin
  x ∙ (A ∙ x)  ≈˘⟨ isOwl A x ⟩
  O ∙ A ∙ x    ≈⟨ congʳ OA≈A ⟩
  A ∙ x        ∎

problem₂₁ : ⦃ _ : HasSageBird ⦄ → ∀ A → IsChoosy A → IsSageBird (Θ ∙ A)
problem₂₁ A isChoosy = isChoosy (Θ ∙ A) $ isSageBird A

open import Mockingbird.Forest.Extensionality forest

problem₂₂ : ⦃ _ : HasOwl ⦄ ⦃ _ : HasSageBird ⦄ → O ∙ Θ ≋ Θ
problem₂₂ x = begin
  O ∙ Θ ∙ x    ≈⟨ isOwl Θ x ⟩
  x ∙ (Θ ∙ x)  ≈⟨ isSageBird x ⟩
  Θ ∙ x        ∎

problem₂₃ : ⦃ _ : Extensional ⦄ ⦃ _ : HasOwl ⦄ ⦃ _ : HasSageBird ⦄ → O IsFondOf Θ
problem₂₃ = ext problem₂₂


open import Prelude

module Implicits.Resolution.GenericFinite.Algorithm.Completeness where

open import Induction.WellFounded
open import Induction.Nat
open import Data.Fin.Substitution
open import Data.Nat.Base using (_<′_)
open import Data.Maybe as 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 Data.Unit

open import Implicits.Syntax
open import Implicits.Syntax.Type.Unification
open import Implicits.Syntax.Type.Unification.Lemmas as mgu hiding (complete)
open import Implicits.Substitutions
open import Implicits.Substitutions.Lemmas
open import Implicits.Resolution.GenericFinite.Resolution
open import Implicits.Resolution.GenericFinite.Algorithm
open import Implicits.Resolution.GenericFinite.TerminationCondition
open import Implicits.Resolution.Termination

open import Extensions.Bool as Bool

private
  module M = MetaTypeMetaSubst

module ResolutionComplete (cond : TerminationCondition) where

  open TerminationCondition cond
  open ResolutionAlgorithm cond
  open ResolutionRules cond

  open Lemmas

  lemx : ∀ {ν m} {Δ : ICtx ν} {Φ} r c τ (u : M.MetaSub MetaType ν m zero) →
        Δ , Φ ⊢ from-meta (r M./ u M.↑tp) tp[/tp c ] ↓ τ →
        Δ , Φ ⊢ from-meta (open-meta r M./ (to-meta c ∷ u)) ↓ τ
  lemx r c τ u p = {!!}

  mutual
    match'-complete : ∀ {ν m} (Δ : ICtx ν) (Φ : TCtx) τ (r : MetaType m ν) →
                  (Φ↓ : T-Acc Φ) → (m↓ : m<-Acc r) →
                  (∃ λ u → Δ , Φ ⊢ from-meta (r M./ u) ↓ τ) →
                  Is-just (match' Δ Φ τ r Φ↓ m↓)

    match'-complete Δ Φ τ (a ⇒ b) f (acc g) (proj₁ , i-iabs x y b↓τ)
      with match' Δ Φ τ b f (g _ (b-m<-a⇒b a b))
         | match'-complete Δ Φ τ b f (g _ (b-m<-a⇒b a b)) (, b↓τ)
    match'-complete Δ Φ τ (a ⇒ b) f (acc g) (u , i-iabs x y b↓τ) | nothing | ()
    match'-complete Δ Φ τ (a ⇒ b) f (acc g) (u , i-iabs x₁ y b↓τ) | just u' | just px
      with (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ) <? Φ
    match'-complete Δ Φ τ (a ⇒ b) f (acc g) (u , i-iabs x₁ y b↓τ) | just u' | just px | yes p = {!!}
    match'-complete Δ Φ τ (a ⇒ b) f (acc g) (u , i-iabs x₁ y b↓τ) | just u' | just px | no ¬p = {!¬p!}
    -- match'-complete Δ Φ τ (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'-complete Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | yes Φ< | true = just u
    -- match'-complete Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | yes Φ< | false = nothing
    -- match'-complete Δ Φ τ (a ⇒ b) (acc f) (acc g) | just u | no φ> = nothing

    match'-complete Δ Φ τ (∀' r) Φ↓ (acc m↓) (u , i-tabs b open-r↓τ)
      with match'          Δ Φ τ (open-meta r) Φ↓ (m↓ _ (open-meta-a-m<-∀'a r))
         | match'-complete Δ Φ τ (open-meta r) Φ↓ (m↓ _ (open-meta-a-m<-∀'a r))
                           (, lemx r b τ u open-r↓τ)
    match'-complete Δ Φ τ (∀' r) Φ↓ (acc m↓) (u , i-tabs b open-r↓τ) | just x | just px = just tt
    match'-complete Δ Φ τ (∀' r) Φ↓ (acc m↓) (u , i-tabs b open-r↓τ) | nothing | ()
    match'-complete Δ Φ .(tvar x) (simpl (tvar x)) Φ↓ m↓ (u , i-simp .(tvar x)) =
      mgu.complete (simpl (tvar x)) (tvar x) u refl
    match'-complete Δ Φ τ (simpl (mvar x)) Φ↓ m↓ (u , proj₂) =
      let (u' , u'-uni) = mvar-unifiable x τ in mgu.complete (simpl (mvar x)) τ u' u'-uni
    match'-complete Δ Φ ._ (simpl (a →' b)) Φ↓ m↓ (u , i-simp ._) =
      mgu.complete (simpl (a →' b)) _ u refl
    match'-complete Δ Φ .(tc x) (simpl (tc x)) Φ↓ m↓ (u , ResolutionRules.i-simp .(tc x)) =
      mgu.complete (simpl (tc x)) _ u refl

    match-complete : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) → (τ : SimpleType ν) → (r : Type ν) →
                     (Φ↓ : T-Acc Φ) →
                     Δ , Φ ⊢ r ↓ τ →
                     Is-true (match Δ Φ τ r Φ↓)
    match-complete Δ Φ τ r Φ↓ p
      with match'          Δ Φ τ (to-meta {zero} r) Φ↓ (m<-well-founded _)
         | match'-complete Δ Φ τ (to-meta {zero} r) Φ↓ (m<-well-founded _)
                           ([] , subst (λ z → Δ , Φ ⊢ z ↓ τ) (sym $ from-to-meta-/-vanishes) p)
    match-complete Δ Φ τ r Φ↓ p | just x | just px = true tt
    match-complete Δ Φ τ r Φ↓ p | nothing | ()

    match1st-complete : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) (ρs : ICtx ν) → (τ : SimpleType ν) →
                        (Φ↓ : T-Acc Φ) →
                        (∃ λ r → r List.∈ ρs × Δ , Φ ⊢ r ↓ τ) →
                        Is-true (match1st Δ Φ ρs τ Φ↓)
    match1st-complete Δ Φ List.[] τ Φ↓ (_ , () , _)
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (.x , here refl , r↓τ)
      with match            Δ Φ τ x Φ↓
         | match-complete   Δ Φ τ x Φ↓ r↓τ
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (.x , here refl , r↓τ) | true | true _ = true tt
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (.x , here refl , r↓τ) | false | ()
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (proj₁ , there p , r↓τ) with match Δ Φ τ x Φ↓
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (r , there r∈ρs , r↓τ) | true  = true tt
    match1st-complete Δ Φ (x List.∷ ρs) τ Φ↓ (r , there r∈ρs , r↓τ) | false =
      match1st-complete Δ Φ ρs τ Φ↓ (r , r∈ρs , r↓τ)

    complete' : ∀ {ν} (Δ : ICtx ν) Φ {r} → (Φ↓ : T-Acc Φ) → Δ , Φ ⊢ᵣ r → Is-true (resolve' Δ Φ r Φ↓)
    complete' Δ Φ Φ↓ (r-simp x∈Δ x↓τ) = match1st-complete Δ Φ Δ _ Φ↓ (_ , x∈Δ , x↓τ)
    complete' Δ Φ Φ↓ (r-iabs ρ₁ p) = complete' (ρ₁ List.∷ Δ) Φ Φ↓ p
    complete' Δ Φ Φ↓ (r-tabs p) = complete' (ictx-weaken Δ) Φ Φ↓ p

    complete : ∀ {ν} (Δ : ICtx ν) Φ {r} → (Φ↓ : T-Acc Φ) → Δ , Φ ⊢ᵣ r → Is-true (resolve Δ Φ r)
    complete Δ Φ Φ↓ p = complete' Δ Φ (wf-< _) p


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

-- Finished half of the proof!
--  - functions between Bool * A and ∑ A are written
--  - One direction of the inverse proof is done!
--
-- Wrapping up the camera ready version of our
-- PLDI paper took more time than I thought it would and so
-- I got a late start on this. I think by class on Tuesday
-- I could have the other half finished.
--
-- Noet: I collaborated w/ Andre on this some.

module thm8511part2 where

open import Level using (_⊔_)
open import Data.Bool using (Bool; true; false; if_then_else_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Product using (_×_; _,_; proj₁; proj₂; Σ; Σ-syntax)
open import Function renaming (_∘_ to _·_)

import Relation.Binary.Core as C
import Relation.Binary.PropositionalEquality as P
--open P.≡-Reasoning

infixr 8  _∘_     -- path composition
infix  4  _≡_     -- propositional equality
infix  4  _∼_     -- homotopy between two functions
infix  4  _≃_     -- type of equivalences
-- macros from tzaikin for equational rewriting over non-standard ≡
infixr 4 _≡⟨_⟩_
infix 4  _∎

indBool : (C : Bool → Set) →
          (C true) →
          (C false) →
          (b : Bool) →
          (C b)
indBool C ct cf true = ct
indBool C ct cf false = cf

ind× : ∀ {A B : Set}
       {C : A × B → Set} →
       ((a : A) → (b : B) → C (a , b)) →
       (p : A × B) →
       (C p)
ind× {C} f (a , b) = f a b

------------------------------------------------------------------------------
-- Some abbreviations and simple lemmas and paths

_≡_ : ∀ {ℓ} {A : Set ℓ} → (x y : A) → Set ℓ
_≡_ {ℓ} {A} x y = C._≡_ {ℓ} {A} x y

-- Groupoid

refl : ∀ {ℓ} {A} → (x : A) → x ≡ x
refl {ℓ} {A} x = C.refl {ℓ} {A} {x}

! : ∀ {u} → {A : Set u} {x y : A} → (x ≡ y) → (y ≡ x)
! = P.sym

_∘_ : ∀ {ℓ} {A : Set ℓ} {x y z : A} →
      (x ≡ y) → (y ≡ z) → (x ≡ z)
_∘_ = P.trans

unitTransL : {A : Set} {x y : A} → (p : x ≡ y) → (p ≡ refl x ∘ p)
unitTransL C.refl = C.refl

unitTransR : {A : Set} {x y : A} → (p : x ≡ y) → (p ≡ p ∘ refl y)
unitTransR C.refl = C.refl

invComp : {A : Set} {x y z : A} → (p : x ≡ y) → (q : y ≡ z) →
          ! (p ∘ q) ≡ ! q ∘ ! p
invComp C.refl C.refl = C.refl

assocP : {A : Set} {x y z w : A} → (p : x ≡ y) → (q : y ≡ z) → (r : z ≡ w) →
         (p ∘ (q ∘ r) ≡ (p ∘ q) ∘ r)
assocP C.refl C.refl C.refl = C.refl

invTransL : {A : Set} {x y : A} → (p : x ≡ y) → (! p ∘ p ≡ refl y)
invTransL C.refl = C.refl

invId : {A : Set} {x y : A} → (p : x ≡ y) → (! (! p) ≡ p)
invId C.refl = C.refl

-- Handy "macros" (from tzaikin)
_∎ : ∀ {ℓ} → {A : Set ℓ} → (p : A) → p ≡ p
p ∎ = refl p

_≡⟨_⟩_ : ∀ {ℓ} → {A : Set ℓ} → {q r : A} → (p : A) → p ≡ q → q ≡ r → p ≡ r
p ≡⟨ α ⟩ β = α ∘ β

-- Functors

ap : ∀ {ℓ₁ ℓ₂} {A : Set ℓ₁} {B : Set ℓ₂} →
     (f : A → B) → {x y : A} → (x ≡ y) → (f x ≡ f y)
ap = P.cong

apfId : {A : Set} {x y : A} → (p : x ≡ y) → ap id p ≡ p
apfId C.refl = C.refl

apfInv : ∀ {u} → {A B : Set u} {x y : A} → (f : A → B) → (p : x ≡ y) →
         ap f (! p) ≡ ! (ap f p)
apfInv f C.refl = C.refl

apfTrans : ∀ {u} → {A B : Set u} {x y z : A} →
  (f : A → B) → (p : x ≡ y) → (q : y ≡ z) → ap f (p ∘ q) ≡ (ap f p) ∘ (ap f q)
apfTrans f C.refl C.refl = C.refl

apfComp : {A B C : Set} {x y : A} → (f : A → B) → (g : B → C) → (p : x ≡ y) →
          ap g (ap f p) ≡ ap (g · f) p
apfComp f g C.refl = C.refl

apconst : {A B : Set} {x y : A} → (p : x ≡ y) (b : B) →
          ap (λ _ → b) p ≡ refl b
apconst C.refl b = C.refl

-- Transport

transport : ∀ {ℓ₁ ℓ₂} {A : Set ℓ₁} (B : A → Set ℓ₂) →
            {x y : A} → (x ≡ y) → B x → B y
transport = P.subst

transportId : {A B : Set} {y z : A} → (f g : A → B) →
  (p : y ≡ z) → (q : f y ≡ g y) →
  transport (λ x → f x ≡ g x) p q ≡ ! (ap f p) ∘ q ∘ (ap g p)
transportId f g C.refl q =
  (q
    ≡⟨ unitTransR q ⟩
   q ∘ C.refl
    ≡⟨ unitTransL (q ∘ C.refl) ⟩
   ! C.refl ∘ (q ∘ C.refl) ∎)

apd : ∀ {ℓ₁ ℓ₂} → {A : Set ℓ₁} {B : A → Set ℓ₂} →
      (f : (x : A) → B x) → {x y : A} → (p : x ≡ y) →
      transport B p (f x) ≡ f y
apd f C.refl = C.refl

-- Homotopies and equivalences

_∼_ : ∀ {ℓ ℓ'} → {A : Set ℓ} {P : A → Set ℓ'} →
      (f g : (x : A) → P x) → Set (ℓ ⊔ ℓ')
_∼_ {ℓ} {ℓ'} {A} {P} f g = (x : A) → f x ≡ g x

record qinv {ℓ ℓ'} {A : Set ℓ} {B : Set ℓ'} (f : A → B) :
  Set (ℓ ⊔ ℓ') where
  constructor mkqinv
  field
    g : B → A
    α : (f · g) ∼ id
    β : (g · f) ∼ id

record isequiv {ℓ ℓ'} {A : Set ℓ} {B : Set ℓ'} (f : A → B) :
  Set (ℓ ⊔ ℓ') where
  constructor mkisequiv
  field
    g : B → A
    α : (f · g) ∼ id
    h : B → A
    β : (h · f) ∼ id

iso : ∀ {ℓ ℓ'} → {A : Set ℓ} {B : Set ℓ'} {f : A → B} → qinv f → isequiv f
iso (mkqinv qg qα qβ) = mkisequiv qg qα qg qβ

_≃_ : ∀ {ℓ ℓ'} (A : Set ℓ) (B : Set ℓ') → Set (ℓ ⊔ ℓ')
A ≃ B = Σ (A → B) isequiv

postulate
  univalence : {A B : Set} → (A ≡ B) ≃ (A ≃ B)

------------------------------------------------------------------------------
-- Some higher-inductive types

module Circle where

  private
    data S¹* : Set where
      base* : S¹*

  -- define the interface for S¹

  S¹ : Set
  S¹ = S¹*

  base : S¹
  base = base*

  postulate
    loop : base ≡ base

  -- recursion principle

  recS¹ : {C : Set} → (cbase : C) → (cloop : cbase ≡ cbase) → S¹ → C
  recS¹ cbase cloop base* = cbase

  postulate
    βrecS¹ : {C : Set} → (cbase : C) → (cloop : cbase ≡ cbase) →
      ap (recS¹ cbase cloop) loop ≡ cloop

  -- induction principle

  indS¹ : {C : S¹ → Set} →
    (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) →
    (circle : S¹) → C circle
  indS¹ cbase cloop base* = cbase

------------------------------------------------------------------------------
module Suspension where

  private
    data ∑* (A : Set) : Set where
      N* : ∑* A
      S* : ∑* A

  -- define the interface for ∑A

  ∑ : (A : Set) → Set
  ∑ = ∑*

  N : {A : Set} → ∑ A
  N = N*

  S : {A : Set} → ∑ A
  S = S*

  postulate
    merid : {A : Set} → A → (N {A} ≡ S {A})

  -- recursion principle

  rec∑ : {A : Set} → {C : Set} → (cn cs : C) → (cm : A → (cn ≡ cs)) → ∑ A → C
  rec∑ cn cs cm N* = cn
  rec∑ cn cs cm S* = cs

  postulate
    βrec∑ : {A : Set} → {C : Set} → (cn cs : C) → (cm : A → (cn ≡ cs)) →
      (a : A) → ap (rec∑ cn cs cm) (merid a) ≡ (cm a)

  -- induction principle

  ind∑ : {A : Set} → {C : ∑ A → Set} → (cn : C N) → (cs : C S) →
         ((a : A) → transport C (merid a) cn ≡ cs) → (s : ∑ A) → C s
  ind∑ cn cs m N* = cn
  ind∑ cn cs m S* = cs

------------------------------------------------------------------------------
module Join where

  private
    data _**_ (A B : Set) : Set where
      inl* : A → A ** B
      inr* : B → A ** B

  -- define the interface for A*B

  _*_ : (A B : Set) → Set
  A * B = A ** B

  inl : {A B : Set} → A → A * B
  inl = inl*

  inr : {A B : Set} → B → A * B
  inr = inr*

  postulate
    glue : {A B : Set} → (c : A × B) → inl (proj₁ c) ≡ inr (proj₂ c)

  -- recursion principle

  rec* : {A B : Set} {D : Set} →
         (ad : A → D) → (bd : B → D) →
         (gd : (c : A × B) → ad (proj₁ c) ≡ bd (proj₂ c)) →
         A * B → D
  rec* ad bd gd (inl* a) = ad a
  rec* ad bd gd (inr* b) = bd b

  postulate
    βrec* : {A B : Set} {D : Set} →
            (ad : A → D) → (bd : B → D) →
            (gd : (c : A × B) → ad (proj₁ c) ≡ bd (proj₂ c)) →
            (c : A × B) → ap (rec* ad bd gd) (glue c) ≡ gd c

  -- induction principle

  ind* : {A B : Set} {D : A * B → Set} →
         (ad : (a : A) → D (inl a)) → (bd : (b : B) → D (inr b)) →
         (gd : (c : A × B) → transport D (glue c) (ad (proj₁ c)) ≡ bd (proj₂ c))
         (c : A * B) → D c
  ind* ad bd gd (inl* a) = ad a
  ind* ad bd gd (inr* b) = bd b

------------------------------------------------------------------------------
-- Lemma 6.5.1

module ∑Bool≡S¹ where

  open Circle
  open Suspension

  east : N ≡ S
  east = merid false

  west : N ≡ S
  west = merid true

  -- S¹ → ∑ Bool

  fcircle : S¹ → ∑ Bool
  fcircle = recS¹ N (east ∘ ! west)

  floop : ap fcircle loop ≡ east ∘ ! west
  floop = βrecS¹ N (east ∘ ! west)

  -- ∑ Bool → S¹

  gcircle : ∑ Bool → S¹
  gcircle = rec∑ base base (λ b → if b then refl base else loop)

  geast : ap gcircle east ≡ loop
  geast = βrec∑ base base (λ b → if b then refl base else loop) false

  gwest : ap gcircle west ≡ (refl base)
  gwest = βrec∑ base base (λ b → if b then refl base else loop) true

  -- round trip S¹ → S¹

  gf : S¹ → S¹
  gf = gcircle · fcircle

  gfloop : ap gf loop ≡ loop
  gfloop =
          (ap gf loop
             ≡⟨ ! (apfComp fcircle gcircle loop) ⟩
           ap gcircle (ap fcircle loop)
             ≡⟨ ap (ap gcircle) floop ⟩
           ap gcircle (east ∘ ! west)
             ≡⟨ apfTrans gcircle east (! west) ⟩
           ap gcircle east ∘ ap gcircle (! west)
             ≡⟨ ap (λ x → ap gcircle east ∘ x) (apfInv gcircle west) ⟩
           ap gcircle east ∘ ! (ap gcircle west)
             ≡⟨ ap (λ x → ap gcircle east ∘ ! x) gwest ⟩
           ap gcircle east ∘ (refl base)
             ≡⟨ ! (unitTransR (ap gcircle east)) ⟩
           ap gcircle east
             ≡⟨ geast ⟩
           loop ∎)

  αloop : transport (λ x → gf x ≡ x) loop (refl base) ≡ refl base
  αloop =
         (transport (λ x → gf x ≡ x) loop (refl base)
            ≡⟨ transportId gf id loop (refl base) ⟩
          ! (ap gf loop) ∘ refl base ∘ ap id loop
            ≡⟨ ap (λ x → ! (ap gf loop) ∘ refl base ∘ x) (apfId loop) ⟩
          ! (ap gf loop) ∘ refl base ∘ loop
            ≡⟨ ap (λ x → ! (ap gf loop) ∘ x) (! (unitTransL loop)) ⟩
          ! (ap gf loop) ∘ loop
            ≡⟨ ap (λ x → ! x ∘ loop) gfloop ⟩
          ! loop ∘ loop
            ≡⟨ invTransL loop ⟩
          refl base ∎)

  βcircle : gf ∼ id
  βcircle = indS¹ {λ x → gf x ≡ x} (refl base) αloop

  -- round trip ∑ Bool → ∑ Bool

  fg : ∑ Bool → ∑ Bool
  fg = fcircle · gcircle

  fgeast : ap fg east ≡ east ∘ ! west
  fgeast =
         (ap fg east
             ≡⟨ ! (apfComp gcircle fcircle east) ⟩
           ap fcircle (ap gcircle east)
             ≡⟨ ap (ap fcircle) geast ⟩
           ap fcircle loop
             ≡⟨ floop ⟩
           (east ∘ ! west) ∎)

  fgwest : ap fg west ≡ refl N
  fgwest =
         (ap fg west
             ≡⟨ ! (apfComp gcircle fcircle west) ⟩
           ap fcircle (ap gcircle west)
             ≡⟨ ap (ap fcircle) gwest ⟩
           ap fcircle (refl base)
             ≡⟨ C.refl ⟩
           refl N ∎)

  αeast : transport (λ x → fg x ≡ x) east (refl N) ≡ west
  αeast =
         (transport (λ x → fg x ≡ x) east (refl N)
            ≡⟨ transportId fg id east (refl N) ⟩
          ! (ap fg east) ∘ refl N ∘ ap id east
            ≡⟨ ap (λ x → ! (ap fg east) ∘ refl N ∘ x) (apfId east) ⟩
          ! (ap fg east) ∘ refl N ∘ east
             ≡⟨ ap (λ x → ! (ap fg east) ∘ x) (! (unitTransL east)) ⟩
          ! (ap fg east) ∘ east
             ≡⟨ ap (λ x → ! x ∘ east) fgeast ⟩
          ! (east ∘ ! west) ∘ east
            ≡⟨ ap (λ x → x ∘ east) (invComp east (! west)) ⟩
          (! (! west) ∘ ! east) ∘ east
            ≡⟨ ! (assocP (! (! west)) (! east) east) ⟩
          ! (! west) ∘ ! east ∘ east
            ≡⟨ ap (λ x → ! (! west) ∘ x) (invTransL east) ⟩
          ! (! west) ∘ refl S
            ≡⟨ ! (unitTransR (! (! west)))  ⟩
          ! (! west)
            ≡⟨ invId west ⟩
          west ∎)

  αwest : transport (λ x → fg x ≡ x) west (refl N) ≡ west
  αwest =
         (transport (λ x → fg x ≡ x) west (refl N)
            ≡⟨ transportId fg id west (refl N) ⟩
          ! (ap fg west) ∘ refl N ∘ ap id west
            ≡⟨ ap (λ x → ! (ap fg west) ∘ refl N ∘ x) (apfId west) ⟩
          ! (ap fg west) ∘ refl N ∘ west
             ≡⟨ ap (λ x → ! (ap fg west) ∘ x) (! (unitTransL west)) ⟩
          ! (ap fg west) ∘ west
             ≡⟨ ap (λ x → ! x ∘ west) fgwest ⟩
          ! (refl N) ∘ west
            ≡⟨ ! (unitTransL west) ⟩
          west ∎)

  αcircle : fg ∼ id
  αcircle =
    ind∑ (refl N) west (λ { false → αeast; true → αwest })

  -- main lemmas

  equivlemma : ∑ Bool ≃ S¹
  equivlemma = (gcircle , iso (mkqinv fcircle βcircle αcircle))

  lemma : ∑ Bool ≡ S¹
  lemma with univalence
  ... | (_ , eq) = isequiv.g eq equivlemma

------------------------------------------------------------------------------
-- Lemma 8.5.10

module ∑A≡Bool*A {A : Set} where
  open Suspension
  open Join

  f :  ∑ A → Bool * A
  f  = rec∑ (inl true)
            (inl false)
            (λ a → glue (true , a) ∘ ! (glue (false , a)))
  fmerid : (a : A) →
    ap f (merid a) ≡ glue (true , a) ∘ ! (glue (false , a))
  fmerid =
    βrec∑ (inl true)
          (inl false)
          (λ a → glue (true , a) ∘ ! (glue (false , a)))

  g : Bool * A → ∑ A
  g = rec* (λ b → if b then N else S)
           (λ a → S)
           (λ c → indBool (λ b → (if b then N else S) ≡ S)
                          (merid (proj₂ c))
                          (refl S)
                          (proj₁ c))

  fg = f · g
  gf = g · f

  gglue-true : (a : A) → (ap g (glue (true , a))) ≡ merid a
  gglue-true a =
    βrec* (λ b → if b then N else S)
          (λ a → S)
          (λ c → indBool (λ b → (if b then N else S) ≡ S)
                         (merid (proj₂ c))
                         (refl S)
                         (proj₁ c))
          (true , a)

  gglue-false : (a : A) → (ap g (glue (false , a))) ≡ refl S
  gglue-false a =
    βrec* (λ b → if b then N else S)
          (λ a → S)
          (λ c → indBool (λ b → (if b then N else S) ≡ S)
                         (merid (proj₂ c))
                         (refl S)
                         (proj₁ c))
          (false , a)

  α : (f · g) ∼ id
  α  = ind* (indBool (λ b → fg (inl b) ≡ id (inl b))
                     (refl (inl true))
                     (refl (inl false)))
            (λ a → glue (false , a))
            (λ c →
              ind×
              (λ b a →
                indBool
                 (λ b →
                   transport
                    (λ z → fg z ≡ id z)
                       (glue (b , a))
                       (indBool (λ b₁ → fg (inl b₁) ≡ id (inl b₁))
                                (refl (inl true))
                                (refl (inl false))
                                b)
                   ≡ glue (false , a))
                 -- big α equivalence proof 1
                 (transport (λ z → fg z ≡ id z)
                            (glue (true , a))
                            (refl (inl true))
                   ≡⟨ transportId fg id (glue (true , a))
                                        (refl (inl true)) ⟩
                 ((! (ap fg (glue (true , a))))
                  ∘ (refl (inl true))
                  ∘ ap id (glue (true , a)))
                   ≡⟨ ap (λ p → ((! (ap fg (glue (true , a))))
                                 ∘ (refl (inl true))
                                 ∘ p))
                         (apfId (glue (true , a))) ⟩
                 ((! (ap fg (glue (true , a))))
                  ∘ (refl (inl true))
                  ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → ((! (ap fg (glue (true , a)))) ∘ p))
                         (unitTransL (glue (true , a))) ⟩
                 ((! (ap (f · g) (glue (true , a))))
                  ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → ((! p) ∘ (glue (true , a))))
                         (! (apfComp g f (glue (true , a)))) ⟩
                 ((! (ap f (ap g (glue (true , a)))))
                  ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → (! (ap f p)) ∘ (glue (true , a)))
                         (gglue-true a) ⟩
                 ((! (ap f (merid a))) ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → (! p) ∘ (glue (true , a)))
                         (fmerid a) ⟩
                 ((! (glue (true , a) ∘ ! (glue (false , a))))
                  ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → (p ∘ (glue (true , a))))
                         (invComp (glue (true , a))
                                  (! (glue (false , a)))) ⟩
                 ((! (! (glue (false , a)))) ∘ (! (glue (true , a))))
                  ∘ (glue (true , a))
                   ≡⟨ ! (assocP (! (! (glue (false , a))))
                                (! (glue (true , a)))
                                (glue (true , a))) ⟩
                 (! (! (glue (false , a))))
                 ∘ ((! (glue (true , a))) ∘ (glue (true , a)))
                   ≡⟨ ap (λ p → (! (! (glue (false , a)))) ∘ p)
                          (invTransL (glue (true , a))) ⟩
                 (! (! (glue (false , a)))) ∘ (refl (inr a))
                   ≡⟨ ! (unitTransR (! (! (glue (false , a))))) ⟩
                 (! (! (glue (false , a))))
                   ≡⟨ (invId (glue (false , a))) ⟩
                 (glue (false , a)) ∎)
                 -- big α equivalence proof 2
                 ((transport (λ z → fg z ≡ id z)
                                (glue (false , a))
                                (refl (inl false))
                   ≡⟨ transportId fg id (glue (false , a))
                                        (refl (inl false)) ⟩
                  ((! (ap fg (glue (false , a))))
                   ∘ (refl (inl false))
                   ∘ ap id (glue (false , a)))
                   ≡⟨ ap (λ p → (! (ap fg (glue (false , a))))
                                ∘ (refl (inl false))
                                ∘ p)
                         (apfId (glue (false , a))) ⟩
                 ((! (ap fg (glue (false , a))))
                  ∘ ((refl (inl false))
                     ∘ (glue (false , a))))
                   ≡⟨ ap (λ p → (! (ap fg (glue (false , a)))) ∘ p)
                         (unitTransL (glue (false , a))) ⟩
                 (! (ap (f · g) (glue (false , a))))
                  ∘ (glue (false , a))
                   ≡⟨ ! (ap (λ p → (! p) ∘ (glue (false , a)))
                            (apfComp g f (glue (false , a)))) ⟩
                 (! (ap f (ap g (glue (false , a)))))
                  ∘ (glue (false , a))
                   ≡⟨ ap (λ p → (! (ap f p)) ∘ (glue (false , a)))
                         (gglue-false a) ⟩
                 (! (ap f (refl S))) ∘ (glue (false , a))
                   ≡⟨ C.refl ⟩
                 (refl (inl false)) ∘ (glue (false , a))
                   ≡⟨ unitTransL (glue (false , a)) ⟩
                 (glue (false , a)) ∎))
                 b)
               c)

--   fmerid : (a : A) →
--   ap f (merid a) ≡ glue (true , a) ∘ ! (glue (false , a))

  β : (g · f) ∼ id
  β = ind∑
       (refl N)
       (refl S)
       (λ a →
         transport (λ z → gf z ≡ id z)
                   (merid a)
                   (refl N)
            ≡⟨ transportId gf id (merid a) (refl N) ⟩
         ((! (ap gf (merid a))) ∘ (refl N) ∘ ap id (merid a))
           ≡⟨ ap (λ p → ((! (ap gf (merid a))) ∘ (refl N) ∘ p))
                 (apfId (merid a)) ⟩
         ((! (ap gf (merid a))) ∘ (refl N) ∘ (merid a))
            ≡⟨ ap (λ p → (! (ap gf (merid a))) ∘ p)
                  (! (unitTransL (merid a))) ⟩
         ((! (ap (g · f) (merid a))) ∘ (merid a))
            ≡⟨ ap (λ p → (! p) ∘ (merid a))
                  (! (apfComp f g (merid a))) ⟩
         ((! (ap g (ap f (merid a)))) ∘ (merid a))
            ≡⟨ ap (λ p → ((! (ap g p)) ∘ (merid a)))
                  (fmerid a) ⟩
         ((! (ap g (glue (true , a) ∘ ! (glue (false , a))))) ∘ (merid a))
            ≡⟨ ap (λ p → (! p) ∘ (merid a))
                  (apfTrans g (glue (true , a)) (! (glue (false , a)))) ⟩
         ((! ((ap g (glue (true , a))) ∘ (ap g (! (glue (false , a)))))) ∘ (merid a))
            ≡⟨ ap (λ p → ((! (p ∘ (ap g (! (glue (false , a)))))) ∘ (merid a)))
                  (gglue-true a) ⟩
         ((! ((merid a) ∘ (ap g (! (glue (false , a)))))) ∘ (merid a))
            ≡⟨ ap (λ p → ((! ((merid a) ∘ p)) ∘ (merid a)))
                  (apfInv g (glue (false , a))) ⟩
         ((! ((merid a) ∘ (! (ap g (glue (false , a)))))) ∘ (merid a))
            ≡⟨ ap (λ p → ((! ((merid a) ∘ (! p))) ∘ (merid a)))
                  (gglue-false a) ⟩
         ((! ((merid a) ∘ (! (refl S)))) ∘ (merid a))
            ≡⟨ C.refl ⟩
         ((! ((merid a) ∘ (refl S))) ∘ (merid a))
            ≡⟨ ap (λ p → (! p) ∘ (merid a))
                  (! (unitTransR (merid a))) ⟩
         ((! (merid a)) ∘ (merid a))
            ≡⟨ invTransL (merid a) ⟩
         refl S ∎)

  ΣA≃Bool*A : ∑ A ≃ Bool * A
  ΣA≃Bool*A = (f , iso (mkqinv g α β))

  lemma : ∑ A ≡ Bool * A
  lemma with univalence
  ... | (_ , eq) = isequiv.g eq ΣA≃Bool*A

------------------------------------------------------------------------------
-- Lemma 8.5.9

module JoinAssoc where
  open Join

  lemma : {A B C : Set} → (A * B) * C ≡ A * (B * C)
  lemma {A} {B} {C} = {!!}

------------------------------------------------------------------------------
-- Thm 8.5.11

open Circle
open Suspension
open Join

S² S³ : Set
S² = ∑ S¹
S³ = ∑ S²

-- Proof of the main theorem

S¹*S¹≡S³ : S¹ * S¹ ≡ S³
S¹*S¹≡S³ =
      (S¹ * S¹
          ≡⟨ P.cong (λ a → a * S¹) (! ∑Bool≡S¹.lemma) ⟩
         ∑ Bool * S¹
          ≡⟨ P.cong (λ a → a * S¹) ∑A≡Bool*A.lemma ⟩
         (Bool * Bool) * S¹
          ≡⟨ JoinAssoc.lemma ⟩
         Bool * (Bool * S¹)
          ≡⟨ P.cong (λ b → Bool * b) (! ∑A≡Bool*A.lemma) ⟩
         Bool * S²
          ≡⟨ ! ∑A≡Bool*A.lemma ⟩
         S³ ∎)

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


open import Nat
open import Prelude
open import List

open import contexts
open import core

module lemmas-general where
  Coerce-unicity : ∀{r ex1 ex2} →
                     Coerce r := ex1 →
                     Coerce r := ex2 →
                     ex1 == ex2
  Coerce-unicity CoerceUnit CoerceUnit = refl
  Coerce-unicity (CoercePair C1 C2) (CoercePair C3 C4)
    rewrite Coerce-unicity C1 C3 | Coerce-unicity C2 C4
      = refl
  Coerce-unicity (CoerceCtor C1) (CoerceCtor C2)
    rewrite Coerce-unicity C1 C2
      = refl

  Fuel-depletion-unicity : ∀{⛽ ⛽↓1 ⛽↓2} →
                             ⛽ ⛽⇓ ⛽↓1 →
                             ⛽ ⛽⇓ ⛽↓2 →
                             ⛽↓1 == ⛽↓2
  Fuel-depletion-unicity CF∞ CF∞ = refl
  Fuel-depletion-unicity CF⛽ CF⛽ = refl


-- {-# OPTIONS --show-implicit #-} -- in case of emergency
module ldlc-algo-nosingle where

open import Data.Nat renaming (_+_ to _+ᴺ_ ; _≤_ to _≤ᴺ_ ; _≥_ to _≥ᴺ_ ; _<_ to _<ᴺ_ ; _>_ to _>ᴺ_ ; _≟_ to _≟ᴺ_)
open import Data.Nat.Properties renaming (_<?_ to _<ᴺ?_)
open import Data.Integer renaming (_+_ to _+ᶻ_ ; _≤_ to _≤ᶻ_ ; _≥_ to _≥ᶻ_ ; _<_ to _<ᶻ_ ; _>_ to _>ᶻ_)
open import Data.Integer.Properties using (⊖-≥ ; 0≤n⇒+∣n∣≡n ; +-monoˡ-≤)
open import Data.Fin
open import Data.Fin.Subset renaming (∣_∣ to ∣_∣ˢ)
open import Data.Vec hiding (_++_ ; length)
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Relation.Binary.PropositionalEquality renaming (trans to ≡-trans)
open import Data.Product
open import Data.Sum

open import aux

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

module syntx where
  data Exp {n : ℕ} : Set
  data Val {n : ℕ} : Exp {n} → Set
  data Ty {n : ℕ} : Set

  data Exp {n} where
    Var : ℕ → Exp {n}
    UnitE : Exp {n}
    Abs : Exp {n} → Exp {n}
    App : {e : Exp {n}} → Exp {n} → Val e → Exp {n}
    LabI : Fin n → Exp {n}
    CaseE : {s : Subset n} {e : Exp {n}} → Val e → (f : ∀ l → l ∈ s → Exp {n}) → Exp {n}
    Prod : Exp {n} → Exp {n} → Exp {n}
    ProdV : {e : Exp {n}} → Val e → Exp {n} → Exp {n}
    LetP : Exp {n} → Exp {n} → Exp {n}
    LetE : Exp {n} → Exp {n} → Exp {n}

  data Val {n} where
    VUnit : Val UnitE
    VVar : {i : ℕ} → Val (Var i)
    VLab : {x : Fin n} → Val (LabI x)
    VFun : {N : Exp} → Val (Abs N)
    VProd : {e e' : Exp} → (v : Val e) → Val e' → Val (ProdV v e')

  data Ty {n} where
    UnitT : Ty
    Label : Subset n → Ty
    Pi : Ty {n} → Ty {n} → Ty
    Sigma : Ty {n} → Ty {n} → Ty
    CaseT : {s : Subset n} {e : Exp {n}} → Val e → (f : ∀ l → l ∈ s → Ty {n}) → Ty

  -- variable in expression
  data _∈`_ {n : ℕ} : ℕ → Exp {n} → Set where
    in-var : {x : ℕ} → x ∈` (Var x)
    in-abs : {x : ℕ} {e : Exp {n}} → x ∈` e → (ℕ.suc x) ∈` (Abs e)
    in-app : {x : ℕ} {e e' : Exp {n}} {v : Val e'} → x ∈` e ⊎ x ∈` e' → x ∈` App e v
    in-casee : {x : ℕ} {s : Subset n} {f : (∀ l → l ∈ s → Exp {n})} {e : Exp {n}} {v : Val e} → (∃₂ λ l i → x ∈` (f l i)) ⊎ x ∈` e → x ∈` CaseE v f
    in-prod : {x : ℕ} {e e' : Exp {n}} → x ∈` e ⊎ (ℕ.suc x) ∈` e' → x ∈` Prod e e'
    in-prodv : {x : ℕ} {e e' : Exp {n}} {v : Val e} → x ∈` e ⊎ x ∈` e' → x ∈` ProdV v e'  -- (Pair-A-I => e' has 0 substituted away => just x, not suc x)
    in-letp : {x : ℕ} {e e' : Exp {n}} → x ∈` e ⊎ (ℕ.suc (ℕ.suc x)) ∈` e' → x ∈` LetP e e'
    in-lete : {x : ℕ} {e e' : Exp {n}} → x ∈` e ⊎ (ℕ.suc x) ∈` e' → x ∈` LetE e e'

  -- variable in type
  data _∈`ᵀ_ {n : ℕ} : ℕ → Ty {n} → Set where
    in-pi : {x : ℕ} {A B : Ty {n}} → n ∈`ᵀ A ⊎ n ∈`ᵀ B → n ∈`ᵀ Pi A B
    in-pigma : {x : ℕ} {A B : Ty {n}} → n ∈`ᵀ A ⊎ n ∈`ᵀ B → n ∈`ᵀ Sigma A B
    in-case : {x : ℕ} {s : Subset n} {f : ∀ l → l ∈ s → Ty {n}} {e : Exp {n}} {v : Val e} → (∃₂ λ l i → x ∈`ᵀ (f l i)) ⊎ x ∈` e → x ∈`ᵀ CaseT v f

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

module substitution where
  ---- Substitution and Shifting
  open syntx

  ↑ᴺ_,_[_] : ℤ → ℕ → ℕ → ℕ
  ↑ᴺ d , c [ x ]
    with (x <ᴺ? c)
  ...  | yes p = x
  ...  | no ¬p = ∣ ℤ.pos x +ᶻ d ∣

  ↑_,_[_] : ∀ {n} → ℤ → ℕ → Exp {n} → Exp {n}
  shift-val : ∀ {n d c} {e : Exp {n}} → Val e → Val (↑ d , c [ e ])

  ↑ d , c [ UnitE ] = UnitE
  ↑ d , c [ Var x ] = Var (↑ᴺ d , c [ x ])
  ↑ d , c [ Abs t ] = Abs (↑ d , (ℕ.suc c) [ t ])
  ↑ d , c [ App t v ] = App (↑ d , c [ t ]) (shift-val{d = d}{c = c} v)
  ↑ d , c [ LabI x ] = LabI x
  ↑ d , c [ CaseE{e = e} V f ] = CaseE (shift-val{d = d}{c = c} V) (λ l x → ↑ d , c [ f l x ])
  ↑ d , c [ Prod e e' ] = Prod (↑ d , c [ e ]) (↑ d , (ℕ.suc c) [ e' ])
  ↑ d , c [ ProdV e e' ] = ProdV (shift-val{d = d}{c = c} e) (↑ d , (ℕ.suc c) [ e' ])
  ↑ d , c [ LetP e e' ] = LetP (↑ d , c [ e ]) (↑ d , (ℕ.suc (ℕ.suc c)) [ e' ])
  ↑ d , c [ LetE e e' ] = LetE (↑ d , c [ e ]) (↑ d , (ℕ.suc c) [ e' ])

  shift-val {n} {d} {c} {.UnitE} VUnit = VUnit
  shift-val {n} {d} {c} {.(Var _)} VVar = VVar
  shift-val {n} {d} {c} {.(LabI _)} VLab = VLab
  shift-val {n} {d} {c} {.(Abs _)} VFun = VFun
  shift-val {n} {d} {c} {.(ProdV V _)} (VProd V V₁) = VProd (shift-val V) (shift-val V₁)

  -- shorthands
  ↑¹[_] : ∀ {n} → Exp {n} → Exp
  ↑¹[ e ] = ↑ (ℤ.pos 1) , 0 [ e ]

  ↑ⱽ¹[_] : ∀ {n} {e : Exp {n}} → Val e → Val (↑ (ℤ.pos 1) , 0 [ e ])
  ↑ⱽ¹[_] {n} {e} v = shift-val v

  ↑⁻¹[_] : ∀ {n} → Exp {n} → Exp
  ↑⁻¹[ e ] = ↑ (ℤ.negsuc 0) , 0 [ e ]

  -- substitution
  [_↦_]_ : ∀ {n} {e : Exp {n}} → ℕ → Val e → Exp {n} → Exp {n}
  sub-val : ∀ {n k} {e e' : Exp {n}} {v : Val e'} → Val e → Val ([ k ↦ v ] e)
  [_↦_]_ {n} {e} k v (Var x)
    with (_≟ᴺ_ x k)
  ...  | yes p = e
  ...  | no ¬p = Var x
  [ k ↦ v ] UnitE = UnitE
  [ k ↦ v ] Abs e = Abs (([ ℕ.suc k ↦ shift-val{d = ℤ.pos 1}{c = 0} v ] e))
  [_↦_]_ {n} {e'} k v (App{e = e₁} e v') = App ([ k ↦ v ] e) (sub-val{n}{k}{e₁}{e'}{v} v') -- ([ k ↦ v ] e₁)
  [ k ↦ v ] LabI x = LabI x
  [_↦_]_ {n} {e} k v (CaseE v' f) = CaseE (sub-val{n}{k}{e' = e}{v = v} v') (λ l x₁ → [ k ↦ v ] (f l x₁))
  [ k ↦ v ] Prod e e₁ = Prod ([ k ↦ v ] e) ([ ℕ.suc k ↦ shift-val{d = ℤ.pos 1}{c = 0} v ] e₁)
  [_↦_]_ {n} {e} k v (ProdV v' e') = ProdV (sub-val{n}{k}{e' = e}{v = v} v') ([ ℕ.suc k ↦ shift-val{d = ℤ.pos 1}{c = 0} v ] e')
  [ k ↦ v ] LetP e e₁ = LetE ([ k ↦ v ] e) ([ (ℕ.suc (ℕ.suc k)) ↦ shift-val{d = ℤ.pos 2}{c = 0} v ] e₁)
  [ k ↦ v ] LetE e e₁ = LetE ([ k ↦ v ] e) ([ (ℕ.suc k) ↦ shift-val{d = ℤ.pos 1}{c = 0} v ] e₁)

  sub-val {n} {k} {.UnitE} {e'} {v} VUnit = VUnit
  sub-val {n} {k} {(Var i)} {e'} {v} VVar
    with (_≟ᴺ_ i k)
  ...  | yes p = v
  ...  | no ¬p = VVar
  sub-val {n} {k} {.(LabI _)} {e'} {v} VLab = VLab
  sub-val {n} {k} {.(Abs _)} {e'} {v} VFun = VFun
  sub-val {n} {k} {.(ProdV v' _)} {e'} {v} (VProd v' v'') = VProd (sub-val v') (sub-val v'')

  -- type substitution
  [_↦_]ᵀ_ : ∀ {n} {e : Exp {n}} → ℕ → Val e → Ty {n} → Ty {n}
  [ k ↦ s ]ᵀ UnitT = UnitT
  [ k ↦ s ]ᵀ Label x = Label x
  [ k ↦ s ]ᵀ Pi T T₁ = Pi ([ k ↦ s ]ᵀ T) ([ k ↦ s ]ᵀ T₁)
  [ k ↦ s ]ᵀ Sigma T T₁ = Sigma ([ k ↦ s ]ᵀ T) ([ k ↦ s ]ᵀ T₁)
  [_↦_]ᵀ_ {n} {e} k v (CaseT x f) = CaseT (sub-val{n}{k}{e' = e}{v = v} x) λ l x₁ → [ k ↦ v ]ᵀ (f l x₁)

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

module typing where
  open syntx
  open substitution

  -- Type environment
  data TEnv {n : ℕ} : Set where
    [] : TEnv
    ⟨_,_⟩ : (T : Ty {n}) (Γ : TEnv {n}) → TEnv

  _++_ : {n : ℕ} → TEnv {n} → TEnv {n} → TEnv {n}
  [] ++ Γ' = Γ'
  ⟨ T , Γ ⟩ ++ Γ' = ⟨ T , Γ ++ Γ' ⟩

  ++-assoc : {n : ℕ} {Γ Γ' Γ'' : TEnv {n}} → Γ ++ (Γ' ++ Γ'') ≡ (Γ ++ Γ') ++ Γ''
  ++-assoc {n} {[]} {Γ'} {Γ''} = refl
  ++-assoc {n} {⟨ T , Γ ⟩} {Γ'} {Γ''} = cong (λ x → ⟨ T , x ⟩) (++-assoc{n}{Γ}{Γ'}{Γ''})

  length : {n : ℕ} → TEnv {n} → ℕ
  length {n} [] = zero
  length {n} ⟨ T , Γ ⟩ = ℕ.suc (length Γ)

  data _∶_∈_ {n : ℕ} : ℕ → Ty {n} → TEnv {n} → Set where
    here : {T : Ty} {Γ : TEnv} → 0 ∶ T ∈ ⟨ T , Γ ⟩
    there : {n : ℕ} {T₁ T₂ : Ty} {Γ : TEnv} → n ∶ T₁ ∈ Γ → (ℕ.suc n) ∶ T₁ ∈ ⟨ T₂ , Γ ⟩

  ---- Algorithmic Typing
  -- Type environment formation
  data ⊢_ok {n : ℕ} : TEnv {n} → Set
  -- Type formation
  data _⊢_ {n : ℕ} : TEnv {n}→ Ty {n} → Set
  -- Type synthesis
  data _⊢_⇒_ {n : ℕ} : TEnv {n} → Exp {n} → Ty {n} → Set
  -- Type check
  data _⊢_⇐_ {n : ℕ} : TEnv {n} → Exp {n} → Ty {n} → Set
  -- Check subtype (⇐ instead of ⇒?)
  data _⇒_≤_ {n : ℕ} : TEnv {n} → Ty {n} → Ty {n} → Set
  -- Unfolding (e.g. CaseT ... ⇓ T)
  data _⊢_⇓_ {n : ℕ} : TEnv {n} → Ty {n} → Ty {n} → Set
  -- Conversion
  data _⇒_≡_ {n : ℕ} : TEnv {n} → Ty {n} → Ty {n} → Set

  -- Implementations
  data ⊢_ok {n} where
    empty : ⊢ [] ok
    entry : {Γ : TEnv {n}} {A : Ty {n}} → ⊢ Γ ok → Γ ⊢ A → ⊢ ⟨ A , Γ ⟩ ok

  data _⊢_ {n} where
    UnitF : {Γ : TEnv {n}} → ⊢ Γ ok → Γ ⊢ UnitT
    LabF : {Γ : TEnv {n}} {L : Subset n} → ⊢ Γ ok → Γ ⊢ Label L
    PiF : {Γ : TEnv {n}} {A B : Ty {n}} → Γ ⊢ A → ⟨ A , Γ ⟩ ⊢ B → Γ ⊢ Pi A B
    SigmaF : {Γ : TEnv {n}} {A B : Ty {n}} → Γ ⊢ A → ⟨ A , Γ ⟩ ⊢ B → Γ ⊢ Sigma A B
    CaseF : {Γ : TEnv {n}} {L : Subset n} {e : Exp {n}} {V : Val e} {f : ∀ l → l ∈ L → Ty {n}} {f-ok : ∀ l → (i : l ∈ L) → Γ ⊢ (f l i)} → Γ ⊢ e ⇐ Label L → Γ ⊢ CaseT V f

  data _⊢_⇐_ {n} where
    SubTypeA : {Γ : TEnv {n}} {A B : Ty {n}} {M : Exp {n}}
               → Γ ⊢ M ⇒ A
               → Γ ⇒ A ≤ B
               → Γ ⊢ M ⇐ B

  data _⇒_≤_ {n} where
    ASubUnit : {Γ : TEnv {n}} → Γ ⇒ UnitT ≤ UnitT
    ASubLabel : {Γ : TEnv {n}} {L L' : Subset n} → L ⊆ L' → Γ ⇒ Label L ≤ Label L'
    ASubPi : {Γ : TEnv {n}} {A A' B B' : Ty {n}}
             → Γ ⇒ A' ≤ A
             → ⟨ A' , Γ ⟩ ⇒ B ≤ B'
             → Γ ⇒ Pi A B ≤ Pi A' B'
    ASubSigma : {Γ : TEnv {n}} {A A' B B' : Ty {n}}
                → Γ ⇒ A ≤ A'
                → ⟨ A , Γ ⟩ ⇒ B ≤ B'
                → Γ ⇒ Sigma A B ≤ Sigma A' B'
    ASubCaseLL : {Γ : TEnv {n}} {B : Ty {n}} {e : Exp {n}} {V : Val e} {l : Fin n} {L L' : Subset n} {f : ∀ l → l ∈ L → Ty {n}} {ins : l ∈ L}
                 → Γ ⊢ e ⇒ Label ⁅ l ⁆
                 → L' ⊆ L
                 → Γ ⇒ (f l ins) ≤ B
                 → Γ ⇒ CaseT V f ≤ B
    ASubCaseLR : {Γ : TEnv {n}} {A : Ty {n}} {e : Exp {n}} {V : Val e} {l : Fin n} {L L' : Subset n} {f : ∀ l → l ∈ L → Ty {n}} {ins : l ∈ L}
                 → Γ ⊢ e ⇒ Label ⁅ l ⁆
                 → L' ⊆ L
                 → Γ ⇒ A ≤ (f l ins)
                 → Γ ⇒ A ≤ CaseT V f
    ASubCaseXL : {Γ Γ' : TEnv {n}} {B D : Ty {n}} {L : Subset n} {f : ∀ l → l ∈ L → Ty {n}}
               → Γ ⇒ D ≤ Label L
               → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ (f l i) ≤ B)
               → (Γ' ++ ⟨ D , Γ ⟩) ⇒ CaseT (VVar{i = length Γ'}) f ≤ B
    ASubCaseXR : {Γ Γ' : TEnv {n}} {A D : Ty {n}} {L : Subset n} {f : ∀ l → l ∈ L → Ty {n}}
               → Γ ⇒ D ≤ Label L
               → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ A ≤ (f l i))
               → (Γ' ++ ⟨ D , Γ ⟩) ⇒ A ≤ CaseT (VVar{i = length Γ'}) f

  data _⊢_⇒_ {n} where
    VarA : {Γ : TEnv {n}} {A : Ty {n}} {x : ℕ} → ⊢ Γ ok → x ∶ A ∈ Γ → Γ ⊢ Var x ⇒ A
    UnitAI : {Γ : TEnv {n}} → ⊢ Γ ok → Γ ⊢ UnitE ⇒ UnitT
    LabAI : {Γ : TEnv {n}} {l : Fin n} → ⊢ Γ ok → Γ ⊢ LabI l ⇒ Label ⁅ l ⁆
    LabAEl : {Γ : TEnv {n}} {B : Ty {n}} {L L' : Subset n} {l : Fin n} {ins : l ∈ L'} {f : ∀ l → l ∈ L → Exp {n}}
             → Γ ⊢ (LabI l) ⇒ Label L'
             → (s : L' ⊆ L)
             → Γ ⊢ (f l (s ins)) ⇒ B
             → Γ ⊢ CaseE (VLab{x = l}) f ⇒ B
    -- unification has problems with arbitrary functions, hence θ
    -- see https://lists.chalmers.se/pipermail/agda/2020/012293.html
    LabAEx : {Γ Γ' Θ : TEnv {n}} {D : Ty {n}} {L : Subset n} {f : ∀ l → l ∈ L → Exp {n}} {f-t : ∀ l → l ∈ L → Ty {n}} {eq : Θ ≡ (Γ' ++ ⟨ D , Γ ⟩)}
             → Γ ⇒ D ≤ Label L
             → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⊢ (f l i) ⇒ (f-t l i))
             → Θ ⊢ CaseE (VVar{i = length Γ'}) f ⇒ CaseT (VVar{i = length Γ'}) f-t
    PiAI : {Γ : TEnv {n}} {A B : Ty {n}}  {M : Exp {n}} → ⟨ A , Γ ⟩ ⊢ M ⇒ B → Γ ⊢ Abs M ⇒ Pi A B
    PiAE : {Γ : TEnv {n}} {A B D : Ty {n}} {M e : Exp {n}} {V : Val e}
           → Γ ⊢ M ⇒ D
           → Γ ⊢ D ⇓ Pi A B
           → Γ ⊢ e ⇐ A
           → Γ ⊢ ([ 0 ↦ V ]ᵀ B)
           → Γ ⊢ App M V ⇒ ([ 0 ↦ V ]ᵀ B)
    SigmaAI : {Γ : TEnv {n}} {A A' B : Ty {n}} {M N : Exp {n}}
              → Γ ⊢ M ⇐ A
              → Γ ⇒ A' ≤ A
              → ⟨ A' , Γ ⟩ ⊢ N ⇒ B
              → Γ ⊢ Prod M N ⇒ Sigma A B
    PairAI : {Γ : TEnv {n}} {A B : Ty {n}} {e N : Exp {n}} {V : Val e}
             → Γ ⊢ e ⇒ A
             → Γ ⊢ N ⇒ B
             → Γ ⊢ ProdV V N ⇒ Sigma A B
    SigmaAE : {Γ : TEnv {n}} {A B C D : Ty {n}} {M N : Exp {n}}
            → Γ ⊢ M ⇒ D
            → Γ ⊢ D ⇓ Sigma A B
            → ⟨ B , ⟨ A , Γ ⟩ ⟩ ⊢ N ⇒ C
            → (¬ (0 ∈`ᵀ C)) × (¬ (1 ∈`ᵀ C))
            → Γ ⊢ LetP M N ⇒ C
    Let : {Γ : TEnv {n}} {A B : Ty {n}} {M N : Exp {n}}
          → ¬(0 ∈`ᵀ B)
          → Γ ⊢ M ⇒ A
          → ⟨ A , Γ ⟩ ⊢ N ⇒ B
          → Γ ⊢ LetE M N ⇒ B

  data _⊢_⇓_ {n} where
    AURefl-U : {Γ : TEnv {n}} → Γ ⊢ UnitT ⇓ UnitT
    AURefl-L : {Γ : TEnv {n}} {L : Subset n} → Γ ⊢ Label L ⇓ Label L
    AURefl-P : {Γ : TEnv {n}} {A B : Ty {n}} → Γ ⊢ Pi A B ⇓ Pi A B
    AURefl-S : {Γ : TEnv {n}} {A B : Ty {n}} → Γ ⊢ Sigma A B ⇓ Sigma A B
    AUCaseL : {Γ : TEnv {n}} {D : Ty {n}} {l : Fin n} {L L' : Subset n} {ins : l ∈ L} {f : ∀ l → l ∈ L → Ty {n}} {e : Exp {n}} {V : Val e}
              → Γ ⊢ e ⇒ Label ⁅ l ⁆
              → L' ⊆ L
              → Γ ⊢ (f l ins) ⇓ D
              → Γ ⊢ CaseT V f ⇓ D
    AUCaseX-P : {Γ Γ' : TEnv {n}} {A D : Ty {n}} {L : Subset n} {fᴬ fᴮ fᴰ : (∀ l → l ∈ L → Ty {n})}
              → Γ ⇒ D ≤ Label L
              → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⊢ (fᴮ l i) ⇓ Pi (fᴬ l i) (fᴰ l i))
              → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ A ≡ (fᴬ l i))
              → (Γ' ++ ⟨ D , Γ ⟩) ⊢ CaseT (VVar{i = length Γ'}) fᴮ ⇓ Pi A (CaseT (VVar{i = length Γ'}) fᴰ)
    AUCaseX-S : {Γ Γ' : TEnv {n}} {A D : Ty {n}} {L : Subset n} {fᴬ fᴮ fᴰ : (∀ l → l ∈ L → Ty {n})}
              → Γ ⇒ D ≤ Label L
              → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⊢ (fᴮ l i) ⇓ Sigma (fᴬ l i) (fᴰ l i))
              → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ A ≡ (fᴬ l i))
              → (Γ' ++ ⟨ D , Γ ⟩) ⊢ CaseT (VVar{i = length Γ'}) fᴮ ⇓ Sigma A (CaseT (VVar{i = length Γ'}) fᴰ)

  data _⇒_≡_ {n} where
    AConvUnit : {Γ : TEnv {n}} → Γ ⇒ UnitT ≡ UnitT
    AConvLabel : {Γ : TEnv {n}} {L : Subset n} → Γ ⇒ Label L ≡ Label L
    AConvPi : {Γ : TEnv {n}} {A A' B B' : Ty} → Γ ⇒ A ≡ A' → ⟨ A' , Γ ⟩ ⇒ B ≡ B' → Γ ⇒ Pi A B ≡ Pi A' B'
    AConvSigma : {Γ : TEnv {n}} {A A' B B' : Ty} → Γ ⇒ A ≡ A' → ⟨ A , Γ ⟩ ⇒ B ≡ B' → Γ ⇒ Sigma A B ≡ Sigma A' B'
    AConvCaseLL : {Γ : TEnv {n}} {B : Ty {n}} {e : Exp {n}} {V : Val e} {L L' : Subset n} {f : (∀ l → l ∈ L → Ty)} {l : Fin n} {ins : l ∈ L}
                  → Γ ⊢ e ⇒ Label ⁅ l ⁆
                  → L ⊆ L'
                  → Γ ⇒ (f l ins) ≡ B
                  → Γ ⇒ CaseT V f ≡ B
    AConvCaseLR : {Γ : TEnv {n}} {A : Ty {n}} {e : Exp {n}} {V : Val e} {L L' : Subset n} {f : (∀ l → l ∈ L → Ty)} {l : Fin n} {ins : l ∈ L}
                  → Γ ⊢ e ⇒ Label ⁅ l ⁆
                  → L ⊆ L'
                  → Γ ⇒ A ≡ (f l ins)
                  → Γ ⇒ A ≡ CaseT V f
    AConvCaseXL : {Γ Γ' : TEnv {n}} {B D : Ty {n}} {L : Subset n} {f : ∀ l → l ∈ L → Ty {n}}
               → Γ ⇒ D ≤ Label L
               → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ (f l i) ≡ B)
               → (Γ' ++ ⟨ D , Γ ⟩) ⇒ CaseT (VVar{i = length Γ'}) f ≡ B
    AConvCaseXR : {Γ Γ' : TEnv {n}} {A D : Ty {n}} {L : Subset n} {f : ∀ l → l ∈ L → Ty {n}}
               → Γ ⇒ D ≤ Label L
               → (∀ l → (i : l ∈ L) → (Γ' ++ ⟨ Label ⁅ l ⁆ , Γ ⟩) ⇒ A ≡ (f l i))
               → (Γ' ++ ⟨ D , Γ ⟩) ⇒ A ≡ CaseT (VVar{i = length Γ'}) f

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

module semantics where
  open syntx
  open substitution

  data _↠_ {n : ℕ} : Exp {n} → Exp {n} → Set where
    ξ-App : {e₁ e₁' e : Exp {n}} {v : Val e} → e₁ ↠ e₁' → App e₁ v ↠ App e₁' v
    ξ-LetE : {e₁ e₁' e : Exp {n}} → e₁ ↠ e₁' → LetE e₁ e ↠ LetE e₁' e
    ξ-Prod : {e₁ e₁' e : Exp {n}} → e₁ ↠ e₁' → Prod e₁ e ↠ Prod e₁' e
    ξ-ProdV : {e e₂ e₂' : Exp {n}} {v : Val e} → e₂ ↠ e₂' → ProdV v e₂ ↠ ProdV v e₂'
    ξ-LetP : {e₁ e₁' e₂ : Exp {n}} → e₁ ↠ e₁' → LetP e₁ e₂ ↠ LetP e₁' e₂
    β-App : {e e' : Exp {n}} → (v : Val e') → (App (Abs e) v) ↠ (↑⁻¹[ ([ 0 ↦ ↑ⱽ¹[ v ] ] e) ])
    β-Prod : {e e' : Exp {n}} {v : Val e} → Prod e e' ↠ ProdV v (↑⁻¹[ ([ 0 ↦ ↑ⱽ¹[ v ] ] e') ])
    β-LetE : {e e' : Exp {n}} → (v : Val e) → LetE e e' ↠ (↑⁻¹[ ([ 0 ↦ ↑ⱽ¹[ v ] ] e') ])
    β-LetP : {e e' e'' : Exp {n}} → (v : Val e) → (v' : Val e')
                              → LetP (ProdV v e') e''
                                ↠
                                ↑ (ℤ.negsuc 1) , 0 [ ([ 0 ↦ ↑ⱽ¹[ v ] ]
                                                       ([ 0 ↦ shift-val {n} {ℤ.pos 1} {1} v' ] e'')) ]
    β-LabE : {s : Subset n} {f : ∀ l → l ∈ s → Exp {n}} {x : Fin n} → (ins : x ∈ s)
             → CaseE (VLab{x = x}) f ↠ f x ins

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

module progress where
  open syntx
  open substitution
  open typing
  open semantics

  -- To eliminate the possible typing judgement (LabAEx) for case expressions,
  -- we need ([] ≢ Γ' ++ ⟨ D , Γ ⟩. Agda does not know that no possible constructor
  -- for this equality exists, because _++_ is an arbitrary function and therefore
  -- "green slime" (see the link @ (LabAEx) rule).
  --
  -- Workaround: Argue with length of environments
  env-len-++ : {n : ℕ} {Γ Γ' : TEnv {n}} → length (Γ ++ Γ') ≡ length Γ +ᴺ length Γ'
  env-len-++ {n} {[]} {Γ'} = refl
  env-len-++ {n} {⟨ T , Γ ⟩} {Γ'} = cong ℕ.suc (env-len-++ {n} {Γ} {Γ'})

  env-len-> : {n : ℕ} {Γ : TEnv {n}} {T : Ty {n}} → length ⟨ T , Γ ⟩ >ᴺ 0
  env-len-> {n} {Γ} {T} = s≤s z≤n

  env-len->-++ : {n : ℕ} {Γ Γ' : TEnv {n}} → length Γ' >ᴺ 0 → length (Γ ++ Γ') >ᴺ 0
  env-len->-++ {n} {Γ} {⟨ T , Γ' ⟩} gt rewrite (env-len-++ {n} {Γ} {⟨ T , Γ' ⟩})= ≤-trans gt (m≤n+m (length ⟨ T , Γ' ⟩) (length Γ))

  env-len-eq : {n : ℕ} {Γ : TEnv {n}} {Γ' : TEnv {n}} → Γ ≡ Γ' → length Γ ≡ length Γ'
  env-len-eq {n} {Γ} {.Γ} refl = refl

  env-empty-++ : {n : ℕ} {Γ' Γ : TEnv {n}} {D : Ty {n}} → ¬ ([] ≡ Γ' ++ ⟨ D , Γ ⟩)
  env-empty-++ {n} {Γ} {Γ'} {D} eq = contradiction (env-len-eq eq) (<⇒≢ (env-len->-++ (env-len->{T = D})))

  -- Canonical forms
  canonical-forms-pi : {n : ℕ} {e : Exp {n}} {A B D : Ty {n}} → [] ⊢ e ⇒ D → [] ⊢ D ⇓ Pi A B → Val e → (∃[ e' ](e ≡ Abs e'))
  canonical-forms-pi {n} {.(Abs _)} {A} {B} {.(Pi _ _)} (PiAI{M = M} j) u v = M , refl

  canonical-forms-sigma : {n : ℕ} {e : Exp {n}} {A B D : Ty {n}} → [] ⊢ e ⇒ D → [] ⊢ D ⇓ Sigma A B → Val e → (∃{A = Exp {n}} λ e' → ∃{A = Val e'} λ v → ∃ λ e'' → e ≡ ProdV{e = e'} v e'')
  canonical-forms-sigma {n} {.(ProdV _ _)} {A} {B} {.(Sigma _ _)} (PairAI{e = e}{N}{V} j j₁) u v = e , (V , (N , refl))

  -- Main theorem
  data Progress {n : ℕ} (e : Exp {n}) {T : Ty} {j : [] ⊢ e ⇒ T} : Set where
    step : {e' : Exp{n}} → e ↠ e' → Progress e
    value : Val e → Progress e

  progress : {n : ℕ} {e : Exp {n}} {T : Ty} → (j : [] ⊢ e ⇒ T) → Progress e {T} {j}
  progress {n} {Var x} {T} (VarA x₁ x₂) = value VVar
  progress {n} {UnitE} {.UnitT} (UnitAI x) = value VUnit
  progress {n} {Abs e} {.(Pi _ _)} (PiAI j) = value VFun
  progress {n} {App e x} {.([ 0 ↦ x ]ᵀ _)} (PiAE{A = A}{B = B}{D = D} j x₁ x₂ x₃)
    with progress {n} {e} {D} j
  ...  | step x₄ = step (ξ-App x₄)
  ...  | value x₄
       with canonical-forms-pi {n} {e} {A} {B} {D} j x₁ x₄
  ...     | fst , snd rewrite snd = step (β-App x)
  progress {n} {LabI x} {.(Label ⁅ x ⁆)} (LabAI x₁) = value VLab
  progress {n} {Prod e e₁} {.(Sigma A B)} (SigmaAI {A = A} {B = B} (SubTypeA{A = A₁} x x₂) x₁ j)
    with progress {n} {e} {A₁} x
  ...  | step x₃ = step (ξ-Prod x₃)
  ...  | value x₃ = step (β-Prod{v = x₃})
  progress {n} {ProdV x e} {.(Sigma _ _)} (PairAI{A = A} {B = B} j j₁)
    with progress {n} {e} {B} j₁
  ...  | step x₁ = step (ξ-ProdV x₁)
  ...  | value x₁ = value (VProd x x₁)
  progress {n} {LetP e e₁} {T} (SigmaAE{A = A}{B = B}{D = D} j x j₁ x₁)
    with progress {n} {e} {D} j
  ...  | step x₂ = step (ξ-LetP x₂)
  ...  | value x₂
       with canonical-forms-sigma {n} {e} {A} {B} {D} j x x₂
  ...     | fst , fst₁ , fst₂ , snd rewrite snd
          with x₂
  ...        | VProd v v₁ = step (β-LetP v v₁)
  progress {n} {LetE e e₁} {T} (Let{A = A} x j j₁)
    with progress {n} {e} {A} j
  ...  | step x₁ = step (ξ-LetE x₁)
  ...  | value x₁ = step (β-LetE x₁)
  progress {n} {CaseE {e = .(LabI _)} VLab f} {T} (LabAEl {l = _} {ins = ins} (LabAI x₁) x j₁) = step (β-LabE (x ins))
  progress {n} {CaseE .VVar f} {.(CaseT VVar _)} (LabAEx{eq = eq} x x₁) = contradiction eq env-empty-++


------------------------------------------------------------------------
-- Validity of declarative typing and kinding of Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Typing.Validity where

open import Data.Fin using (Fin; suc; zero)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Product as Prod using (∃; _,_; _×_; proj₁; proj₂)
open import Data.Vec as Vec using ([]; _∷_)
import Data.Vec.Relation.Binary.Pointwise.Inductive as Pointwise
open import Function using (_∘_)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (ε; _◅_)
open import Relation.Binary.PropositionalEquality hiding ([_])
open import Relation.Binary.TransReasoning
open import Relation.Nullary using (¬_)
open import Relation.Nullary.Negation using (contradiction)

open import FOmegaInt.Syntax
open import FOmegaInt.Typing
open import FOmegaInt.Kinding.Declarative.Equivalence
open import FOmegaInt.Reduction.Full

import FOmegaInt.Kinding.Declarative
import FOmegaInt.Kinding.Declarative.Validity

-- Extended kinding
module E where
  open FOmegaInt.Kinding.Declarative          public
  open FOmegaInt.Kinding.Declarative.Validity public
  open KindedSubstitution                     public

------------------------------------------------------------------------
-- Validity of declarative kinding, typing, subkinding and subtyping

open Syntax
open Substitution hiding (subst)
open TermCtx
open Typing
open TypedSubstitution
open WfCtxOps using (lookup-tp)

-- Validity of kinding: the kinds of well-kinded types are well-formed.
Tp∈-valid : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ⊢ k kd
Tp∈-valid = sound-kd ∘ E.Tp∈-valid ∘ complete-Tp∈

-- Validity of subkinding: subkinds are well-formed.
<∷-valid : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j <∷ k → Γ ⊢ j kd × Γ ⊢ k kd
<∷-valid = Prod.map sound-kd sound-kd ∘ E.<∷-valid ∘ complete-<∷

-- Validity of subtyping: subtypes that are related in some kind `k'
-- inhabit `k'.
<:-valid : ∀ {n} {Γ : Ctx n} {a b k} →
           Γ ⊢ a <: b ∈ k → Γ ⊢Tp a ∈ k × Γ ⊢Tp b ∈ k
<:-valid = Prod.map sound-Tp∈ sound-Tp∈ ∘ E.<:-valid ∘ complete-<:

-- A corollary.
<:-valid-kd : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a <: b ∈ k → Γ ⊢ k kd
<:-valid-kd a<:b∈k = Tp∈-valid (proj₁ (<:-valid a<:b∈k))

-- Validity of type equality: types that are equal in some kind `k'
-- inhabit `k'.
≃-valid : ∀ {n} {Γ : Ctx n} {a b k} →
          Γ ⊢ a ≃ b ∈ k → Γ ⊢Tp a ∈ k × Γ ⊢Tp b ∈ k
≃-valid = Prod.map sound-Tp∈ sound-Tp∈ ∘ E.≃-valid ∘ complete-≃

-- Validity of kind equality: equal kinds are well-formed.
≅-valid : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ⊢ j kd × Γ ⊢ k kd
≅-valid (<∷-antisym j<∷k k<∷j) = <∷-valid j<∷k

-- Validity of combined kind and type equality: equal ascriptions are
-- well-formed.
≃wf-valid : ∀ {n} {Γ : Ctx n} {a b} →
            Γ ⊢ a ≃ b wf → Γ ⊢ a wf × Γ ⊢ b wf
≃wf-valid (≃wf-≅ j≅k)   = Prod.map wf-kd wf-kd (≅-valid j≅k)
≃wf-valid (≃wf-≃ a≃b∈k) = Prod.map wf-tp wf-tp (≃-valid a≃b∈k)

-- Validity of typing: the types of well-typed terms are well-kinded.
Tm∈-valid : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢Tm a ∈ b → Γ ⊢Tp b ∈ *
Tm∈-valid (∈-var x Γ-ctx Γ[x]≡tp-a) = lookup-tp x Γ-ctx Γ[x]≡tp-a
Tm∈-valid (∈-∀-i k-kd a∈b)    = ∈-∀-f k-kd (Tm∈-valid a∈b)
Tm∈-valid (∈-→-i a∈* b∈c c∈*) = ∈-→-f a∈* c∈*
Tm∈-valid (∈-∀-e a∈∀kc b∈k)   =
  let k-kd , c∈* = Tp∈-∀-inv (Tm∈-valid a∈∀kc)
  in Tp∈-[] c∈* (∈-tp b∈k)
Tm∈-valid (∈-→-e a∈c⇒d b∈c)   =
  let c∈* , d∈* = Tp∈-→-inv (Tm∈-valid a∈c⇒d)
  in d∈*
Tm∈-valid (∈-⇑ a∈b b<:c)      = proj₂ (<:-valid b<:c)

-- Subtypes inhabiting interval kinds are proper types.
<:-⋯-* : ∀ {n} {Γ : Ctx n} {a b c d} → Γ ⊢ a <: b ∈ c ⋯ d → Γ ⊢ a <: b ∈ *
<:-⋯-* a<:b∈c⋯d =
  let a∈c⋯d , b∈c⋯d = <:-valid a<:b∈c⋯d
  in <:-⇑ (<:-⋯-i a<:b∈c⋯d) (<∷-⋯ (<:-⊥ a∈c⋯d) (<:-⊤ b∈c⋯d))

-- An admissible rule for kinding η-expanded type operators.
Tp∈-η : ∀ {n} {Γ : Ctx n} {a j k} →
        Γ ⊢Tp a ∈ Π j k → Γ ⊢Tp Λ j (weaken a · var zero) ∈ Π j k
Tp∈-η a∈Πjk with complete-Tp∈ a∈Πjk
Tp∈-η a∈Πjk | a∈Πjk′ with E.Tp∈-valid a∈Πjk′
... | E.Kinding.kd-Π j-kd k-kd = sound-Tp∈ (E.Tp∈-η a∈Πjk′ j-kd k-kd)

-- Operations on well-formed context equality that require weakening
-- and validity of ascription equality.
module CtxEqOps where
  open Pointwise using (Pointwise; []; _∷_; map⁺)

  -- Convert a context equation to its All₂ representation.
  ≃ctx-toAll : ∀ {m} {Γ Δ : Ctx m} → Γ ≃ Δ ctx →
               Pointwise (Γ ⊢_≃_wf) (toVec Γ) (toVec Δ)
  ≃ctx-toAll ≃-[]          = []
  ≃ctx-toAll (≃-∷ a≃b Γ≃Δ) =
    let a-wf , _ = ≃wf-valid a≃b
    in ≃wf-weaken a-wf a≃b ∷ map⁺ (≃wf-weaken a-wf) (≃ctx-toAll Γ≃Δ)

  -- Ascriptions looked up in equal contexts are equal.

  lookup-≃ : ∀ {m} {Γ Δ : Ctx m} x → Γ ≃ Δ ctx →
             Γ ⊢ lookup Γ x ≃ lookup Δ x wf
  lookup-≃ x Γ≃Δ = Pointwise.lookup (≃ctx-toAll Γ≃Δ) x

  lookup-≃-kd : ∀ {m} {Γ Δ : Ctx m} {j k} x → Γ ≃ Δ ctx →
                lookup Γ x ≡ kd j → lookup Δ x ≡ kd k → Γ ⊢ j ≅ k
  lookup-≃-kd x Γ≃Δ Γ[x]≡kd-j Δ[x]≡kd-k =
    ≃wf-kd-inv (subst₂ (_ ⊢_≃_wf) Γ[x]≡kd-j Δ[x]≡kd-k (lookup-≃ x Γ≃Δ))

----------------------------------------------------------------------
-- Context narrowing (strong version)
--
-- NOTE. We could have proven a *weak* variant of context narrowing
-- for the (original) declarative judgments directly in the Typing
-- module (before establishing validity).  But since we did not need
-- (weak or strong) context narrowing of the original judgments until
-- now, it is easier to instead prove the strengthened version here
-- via context narrowing and equivalence of the extended rules.

module TypedNarrowing where

  -- Narrowing the kind of the first type variable preserves
  -- well-formedness of kinds.
  ⇓-kd : ∀ {n} {Γ : Ctx n} {j₁ j₂ k} →
         Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ k kd → kd j₁ ∷ Γ ⊢ k kd
  ⇓-kd j₁<∷j₂ k-kd =
    let j₁<∷j₂′   = complete-<∷ j₁<∷j₂
        j₁-kd , _ = E.<∷-valid j₁<∷j₂′
    in sound-kd (E.⇓-kd j₁-kd j₁<∷j₂′ (complete-kd k-kd))

  -- Narrowing the kind of the first type variable preserves
  -- well-kindedness.
  ⇓-Tp∈ : ∀ {n} {Γ : Ctx n} {j₁ j₂ a k} →
          Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢Tp a ∈ k → kd j₁ ∷ Γ ⊢Tp a ∈ k
  ⇓-Tp∈ j₁<∷j₂ a∈k =
    let j₁<∷j₂′   = complete-<∷ j₁<∷j₂
        j₁-kd , _ = E.<∷-valid j₁<∷j₂′
    in sound-Tp∈ (E.⇓-Tp∈ j₁-kd j₁<∷j₂′ (complete-Tp∈ a∈k))

  -- Narrowing the kind of the first type variable preserves
  -- subkinding and subtyping.

  ⇓-<∷ : ∀ {n} {Γ : Ctx n} {j₁ j₂ k₁ k₂} →
         Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ k₁ <∷ k₂ → kd j₁ ∷ Γ ⊢ k₁ <∷ k₂
  ⇓-<∷ j₁<∷j₂ k₁<∷k₂ =
    let j₁<∷j₂′   = complete-<∷ j₁<∷j₂
        j₁-kd , _ = E.<∷-valid j₁<∷j₂′
    in sound-<∷ (E.⇓-<∷ j₁-kd j₁<∷j₂′ (complete-<∷ k₁<∷k₂))

  ⇓-<: : ∀ {n} {Γ : Ctx n} {j₁ j₂ a₁ a₂ k} →
         Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ a₁ <: a₂ ∈ k → kd j₁ ∷ Γ ⊢ a₁ <: a₂ ∈ k
  ⇓-<: j₁<∷j₂ a₁<:a₂∈k =
    let j₁<∷j₂′   = complete-<∷ j₁<∷j₂
        j₁-kd , _ = E.<∷-valid j₁<∷j₂′
    in sound-<: (E.⇓-<: j₁-kd j₁<∷j₂′ (complete-<: a₁<:a₂∈k))

open TypedNarrowing

-- Transitivity of subkinding and kind equality.
--
-- NOTE. We could have proven admissibility of these rules directly in
-- the Typing module (before establishing validity) but this would
-- have required a proof of (weak) context narrowing for the
-- (original) declarative rules.  As we do not need these transitivity
-- properties until we prove subject reduction (below), we go with an
-- indirect proof instead.

<∷-trans : ∀ {n} {Γ : Ctx n} {j k l} → Γ ⊢ j <∷ k → Γ ⊢ k <∷ l → Γ ⊢ j <∷ l
<∷-trans k₁<∷k₂ k₂<∷k₃ =
  sound-<∷ (E.<∷-trans (complete-<∷ k₁<∷k₂) (complete-<∷ k₂<∷k₃))

≅-trans : ∀ {n} {Γ : Ctx n} {j k l} → Γ ⊢ j ≅ k → Γ ⊢ k ≅ l → Γ ⊢ j ≅ l
≅-trans k₁≅k₂ k₂≅k₃ =
  sound-≅ (E.≅-trans (complete-≅ k₁≅k₂) (complete-≅ k₂≅k₃))

-- Relational reasoning for kind and type equality.
--
-- NOTE. Even though the equalities are technically preorders, we have
-- to use the weaker reasoning designed for transitive relations here.
-- This is because reflexivity proofs in typed relations are typically
-- also typed, i.e. given some context `Γ' and some raw kind or term
-- `x` we cannot, in general, conclude that `Γ ⊢ x ∼ x` without an
-- additional well-formedness proof `Γ ⊢ x' of some sort.

module ≅-Reasoning where
  ≅-reasoning : TransCtxTermRelReasoning _⊢_≅_
  ≅-reasoning = record { ∼-trans = ≅-trans }

  open TransCtxTermRelReasoning ≅-reasoning public renaming (_∼⟨_⟩_ to _≅⟨_⟩_)

module ≃-Reasoning where
  ≃-reasoning : TypedTransRelReasoning _⊢_≃_∈_
  ≃-reasoning = record { ∼-trans = ≃-trans }

  open TypedTransRelReasoning ≃-reasoning public renaming (_∼⟨_⟩_ to _≃⟨_⟩_)

----------------------------------------------------------------------
-- Generation lemmas for abstractions

-- A generation lemma for kinding of type operator abstractions.
Tp∈-Λ-inv : ∀ {n} {Γ : Ctx n} {l a j k} → Γ ⊢Tp Λ l a ∈ Π j k →
            Γ ⊢ l kd × kd j ∷ Γ ⊢Tp a ∈ k
Tp∈-Λ-inv (∈-Π-i l-kd a∈k) = l-kd , a∈k
Tp∈-Λ-inv (∈-⇑ Λl∈Πj₁k₁ (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd)) =
  let l-kd , a∈k₁ = Tp∈-Λ-inv Λl∈Πj₁k₁
  in l-kd , ∈-⇑ (⇓-Tp∈ j₂<∷j₁ a∈k₁) k₁<∷k₂

-- Term abstractions are not types.
Tp∈-¬λ : ∀ {n} {Γ : Ctx n} {a b k} → ¬ Γ ⊢Tp ƛ a b ∈ k
Tp∈-¬λ (∈-s-i λab∈c⋯d)  = Tp∈-¬λ λab∈c⋯d
Tp∈-¬λ (∈-⇑ λab∈j j<∷k) = Tp∈-¬λ λab∈j

----------------------------------------------------------------------
-- Admissible kind and type equality rules.

-- Functionality of kind formation (strong version).
kd-[≃] : ∀ {n} {Γ : Ctx n} {a b j k} →
         kd j ∷ Γ ⊢ k kd → Γ ⊢ a ≃ b ∈ j → Γ ⊢ k Kind[ a ] ≅ k Kind[ b ]
kd-[≃] k-kd a≃b∈j = sound-≅ (E.kd-[≃] (complete-kd k-kd) (complete-≃ a≃b∈j))

-- Functionality of kinding (strong version).
Tp∈-[≃] : ∀ {n} {Γ : Ctx n} {a b c j k} →
          kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢ b ≃ c ∈ j →
          Γ ⊢ a [ b ] ≃ a [ c ] ∈ k Kind[ b ]
Tp∈-[≃] a∈k b≃c∈j = sound-≃ (E.Tp∈-[≃] (complete-Tp∈ a∈k) (complete-≃ b≃c∈j))

-- An admissible congruence rule for kind equality w.r.t. dependent
-- arrow formation.
≅-Π : ∀ {n} {Γ : Ctx n} {j₁ j₂ k₁ k₂} →
      Γ ⊢ j₁ ≅ j₂ → kd j₁ ∷ Γ ⊢ k₁ ≅ k₂ → Γ ⊢ Π j₁ k₁ ≅ Π j₂ k₂
≅-Π (<∷-antisym j₁<∷j₂ j₂<∷j₁) (<∷-antisym k₁<∷k₂ k₂<∷k₁) =
  let j₁-kd , j₂-kd = <∷-valid j₁<∷j₂
      k₁-kd , k₂-kd = <∷-valid k₁<∷k₂
  in <∷-antisym (<∷-Π j₂<∷j₁ (⇓-<∷ j₂<∷j₁ k₁<∷k₂) (kd-Π j₁-kd k₁-kd))
                (<∷-Π j₁<∷j₂ k₂<∷k₁ (kd-Π j₂-kd (⇓-kd j₂<∷j₁ k₂-kd)))

-- Equal types inhabiting interval kinds are proper types.
≃-⋯-* : ∀ {n} {Γ : Ctx n} {a b c d} → Γ ⊢ a ≃ b ∈ c ⋯ d → Γ ⊢ a ≃ b ∈ *
≃-⋯-* (<:-antisym a<:b∈c⋯d b<:a∈c⋯d) =
  let a<:b∈* = <:-⋯-* a<:b∈c⋯d
      b<:a∈* = <:-⋯-* b<:a∈c⋯d
  in <:-antisym a<:b∈* b<:a∈*

-- An admissible congruence rule for type equality w.r.t. formation of
-- universal types.
≃-∀ : ∀ {n} {Γ : Ctx n} {k₁ k₂ a₁ a₂} →
      Γ ⊢ k₁ ≅ k₂ → kd k₁ ∷ Γ ⊢ a₁ ≃ a₂ ∈ * → Γ ⊢ Π k₁ a₁ ≃ Π k₂ a₂ ∈ *
≃-∀ (<∷-antisym k₁<∷k₂ k₂<∷k₁) (<:-antisym a₁<:a₂∈* a₂<:a₁∈*) =
  let k₁-kd , k₂-kd = <∷-valid k₁<∷k₂
      a₁∈*  , a₂∈*  = <:-valid a₁<:a₂∈*
  in <:-antisym (<:-∀ k₂<∷k₁ (⇓-<: k₂<∷k₁ a₁<:a₂∈*) (∈-∀-f k₁-kd a₁∈*))
                (<:-∀ k₁<∷k₂ a₂<:a₁∈* (∈-∀-f k₂-kd (⇓-Tp∈ k₂<∷k₁ a₂∈*)))

-- Another, weaker congruence lemma for type equality w.r.t. type
-- operator abstraction.
≃-λ′ : ∀ {n} {Γ : Ctx n} {j₁ j₂ a₁ a₂ k} →
       Γ ⊢ j₁ ≅ j₂ → kd j₁ ∷ Γ ⊢ a₁ ≃ a₂ ∈ k → Γ ⊢ Λ j₁ a₁ ≃ Λ j₂ a₂ ∈ Π j₁ k
≃-λ′ (<∷-antisym j₁<∷j₂ j₂<∷j₁) (<:-antisym a₁<:a₂∈k a₂<:a₁∈k) =
  let j₁-kd , j₂-kd = <∷-valid j₁<∷j₂
      a₁∈k  , a₂∈k  = <:-valid a₁<:a₂∈k
      k-kd          = Tp∈-valid a₁∈k
      k-kd′         = ⇓-kd j₂<∷j₁ k-kd
      Πj₂k<∷j₁k     = <∷-Π j₁<∷j₂ (<∷-refl k-kd) (kd-Π j₂-kd k-kd′)
      Λj₁a₁∈Πj₁k    = ∈-Π-i j₁-kd a₁∈k
      Λj₂a₂∈Πj₁k    = ∈-⇑ (∈-Π-i j₂-kd (⇓-Tp∈ j₂<∷j₁ a₂∈k)) Πj₂k<∷j₁k
  in <:-antisym (<:-λ a₁<:a₂∈k Λj₁a₁∈Πj₁k Λj₂a₂∈Πj₁k)
                (<:-λ a₂<:a₁∈k Λj₂a₂∈Πj₁k Λj₁a₁∈Πj₁k)

-- An admissible congruence rule for type equality w.r.t. type
-- application.
≃-· : ∀ {n} {Γ : Ctx n} {a₁ a₂ b₁ b₂ j k} →
      Γ ⊢ a₁ ≃ a₂ ∈ Π j k → Γ ⊢ b₁ ≃ b₂ ∈ j →
      Γ ⊢ a₁ · b₁ ≃ a₂ · b₂ ∈ k Kind[ b₁ ]
≃-· (<:-antisym a₁<:a₂∈Πjk a₂<:a₁∈Πjk) b₁≃b₂∈j with <:-valid-kd a₁<:a₂∈Πjk
... | (kd-Π j-kd k-kd) =
  <:-antisym (<:-· a₁<:a₂∈Πjk b₁≃b₂∈j)
             (<:-⇑ (<:-· a₂<:a₁∈Πjk (≃-sym b₁≃b₂∈j))
                   (≅⇒<∷ (kd-[≃] k-kd (≃-sym b₁≃b₂∈j))))

-- An admissible, more flexible β-rule for type equality.
≃-β′ : ∀ {n} {Γ : Ctx n} {a b j k l} →
       kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ j → Γ ⊢Tp Λ l a ∈ Π j k →
       Γ ⊢ (Λ l a) · b ≃ a [ b ] ∈ k Kind[ b ]
≃-β′ a∈k b∈j Λla∈Πjk =
  let j-kd = Tp∈-valid b∈j
  in ≃-trans (≃-· (≃-λ (≃-refl a∈k) Λla∈Πjk (∈-Π-i j-kd a∈k)) (≃-refl b∈j))
             (≃-β a∈k b∈j)

-- An admissible singleton-introduction rule for type equality.
≃-s-i : ∀ {n} {Γ : Ctx n} {a b c d} → Γ ⊢ a ≃ b ∈ c ⋯ d → Γ ⊢ a ≃ b ∈ a ⋯ a
≃-s-i (<:-antisym a<:b∈c⋯d b<:a∈c⋯d) =
  let a<:b∈*  = <:-⋯-* a<:b∈c⋯d
      b<:a∈*  = <:-⋯-* b<:a∈c⋯d
      a∈* , _ = <:-valid a<:b∈*
      a<:a∈*  = <:-refl a∈*
  in <:-antisym (<:-⇑ (<:-⋯-i a<:b∈c⋯d) (<∷-⋯ a<:a∈* b<:a∈*))
                (<:-⇑ (<:-⋯-i b<:a∈c⋯d) (<∷-⋯ a<:b∈* a<:a∈*))

------------------------------------------------------------------------
-- Subject reduction for kinding.

-- A variant of subject reduction for kinding: untyped β-reduction of
-- kinds and types is included in kind resp. type equality.

mutual

  kd-→β-≅ : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j kd → j Kd→β k → Γ ⊢ j ≅ k
  kd-→β-≅ (kd-⋯ a∈* b∈*)   (a→a′ ⋯₁ b) = ≅-⋯ (Tp∈-→β-≃ a∈* a→a′) (≃-refl b∈*)
  kd-→β-≅ (kd-⋯ a∈* b∈*)   (a ⋯₂ b→b′) = ≅-⋯ (≃-refl a∈*) (Tp∈-→β-≃ b∈* b→b′)
  kd-→β-≅ (kd-Π j-kd k-kd) (Π₁ j→j′ k) =
    ≅-Π (kd-→β-≅ j-kd j→j′) (≅-refl k-kd)
  kd-→β-≅ (kd-Π j-kd k-kd) (Π₂ j k→k′) =
    ≅-Π (≅-refl j-kd) (kd-→β-≅ k-kd k→k′)

  Tp∈-→β-≃ : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢Tp a ∈ k → a →β b → Γ ⊢ a ≃ b ∈ k
  Tp∈-→β-≃ (∈-var x Γ-ctx Γ[x]≡kd-k) ⌈ () ⌉
  Tp∈-→β-≃ (∈-⊥-f Γ-ctx)    ⌈ () ⌉
  Tp∈-→β-≃ (∈-⊤-f Γ-ctx)    ⌈ () ⌉
  Tp∈-→β-≃ (∈-∀-f k-kd a∈*) ⌈ () ⌉
  Tp∈-→β-≃ (∈-∀-f k-kd a∈*) (Π₁ k→k′ a) =
    ≃-∀ (kd-→β-≅ k-kd k→k′) (≃-refl a∈*)
  Tp∈-→β-≃ (∈-∀-f k-kd a∈*) (Π₂ k a→a′) =
    ≃-∀ (≅-refl k-kd) (Tp∈-→β-≃ a∈* a→a′)
  Tp∈-→β-≃ (∈-→-f a∈* b∈*)  ⌈ () ⌉
  Tp∈-→β-≃ (∈-→-f a∈* b∈*)  (a→a′ ⇒₁ b) =
    ≃-→ (Tp∈-→β-≃ a∈* a→a′) (≃-refl b∈*)
  Tp∈-→β-≃ (∈-→-f a∈* b∈*)  (a ⇒₂ b→b′) =
    ≃-→ (≃-refl a∈*) (Tp∈-→β-≃ b∈* b→b′)
  Tp∈-→β-≃ (∈-Π-i j-kd a∈k) ⌈ () ⌉
  Tp∈-→β-≃ (∈-Π-i j-kd a∈k) (Λ₁ j→j′ a) =
    ≃-λ′ (kd-→β-≅ j-kd j→j′) (≃-refl a∈k)
  Tp∈-→β-≃ (∈-Π-i j-kd a∈k) (Λ₂ j a→a′) =
    ≃-λ′ (≅-refl j-kd) (Tp∈-→β-≃ a∈k a→a′)
  Tp∈-→β-≃ (∈-Π-e a∈Πjk b∈j) ⌈ cont-Tp· l a b ⌉ =
    let l-kd , a∈k = Tp∈-Λ-inv a∈Πjk
    in ≃-β′ a∈k b∈j a∈Πjk
  Tp∈-→β-≃ (∈-Π-e a∈Πjk b∈j) ⌈ cont-Tm· a b c ⌉ =
    contradiction a∈Πjk Tp∈-¬λ
  Tp∈-→β-≃ (∈-Π-e a∈Πjk b∈j) (a→a′ ·₁ b) =
    ≃-· (Tp∈-→β-≃ a∈Πjk a→a′) (≃-refl b∈j)
  Tp∈-→β-≃ (∈-Π-e a∈Πjk b∈j) (a ·₂ b→b′) =
    ≃-· (≃-refl a∈Πjk) (Tp∈-→β-≃ b∈j b→b′)
  Tp∈-→β-≃ (∈-s-i a∈k)    a→b = ≃-s-i (Tp∈-→β-≃ a∈k a→b)
  Tp∈-→β-≃ (∈-⇑ a∈j j<∷k) a→b = ≃-⇑ (Tp∈-→β-≃ a∈j a→b) j<∷k

kd-→β*-≅ : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j kd → j Kd→β* k → Γ ⊢ j ≅ k
kd-→β*-≅ j-kd ε            = ≅-refl j-kd
kd-→β*-≅ j-kd (j→k ◅ k→*l) =
  let j≅k  = kd-→β-≅ j-kd j→k
      k-kd = proj₂ (≅-valid j≅k)
  in ≅-trans j≅k (kd-→β*-≅ k-kd k→*l)

Tp∈-→β*-≃ : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢Tp a ∈ k → a →β* b → Γ ⊢ a ≃ b ∈ k
Tp∈-→β*-≃ a∈k ε            = ≃-refl a∈k
Tp∈-→β*-≃ a∈k (a→b ◅ b→*c) =
  let a≃b∈k = Tp∈-→β-≃ a∈k a→b
      b∈k   = proj₂ (≃-valid a≃b∈k)
  in ≃-trans a≃b∈k (Tp∈-→β*-≃ b∈k b→*c)

-- A corollary: subject reduction for kinding, aka preservation of
-- kinding under (full) β-reduction.
pres-Tp∈-→β* : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢Tp a ∈ k → a →β* b → Γ ⊢Tp b ∈ k
pres-Tp∈-→β* a∈k a→b = proj₂ (≃-valid (Tp∈-→β*-≃ a∈k a→b))


{-# OPTIONS --cubical --no-import-sorts --safe --postfix-projections #-}

open import Cubical.Foundations.Everything renaming (uncurry to λ⟨,⟩_)
open import Cubical.Data.Sigma.Properties
open import Cubical.Foundations.CartesianKanOps

module Cubical.Modalities.Lex
  (◯ : ∀ {ℓ} → Type ℓ → Type ℓ)
  (η : ∀ {ℓ} {A : Type ℓ} → A → ◯ A)
  (isModal : ∀ {ℓ} → Type ℓ → Type ℓ)
  (let isModalFam = λ {ℓ ℓ′ : Level} {A : Type ℓ} (B : A → Type ℓ′) → (x : A) → isModal (B x))
  (idemp : ∀ {ℓ} {A : Type ℓ} → isModal (◯ A))
  (≡-modal : ∀ {ℓ} {A : Type ℓ} {x y : A} (A-mod : isModal A) → isModal (x ≡ y))
  (◯-ind : ∀ {ℓ ℓ′} {A : Type ℓ} {B : ◯ A → Type ℓ′} (B-mod : isModalFam B) (f : (x : A) → B (η x)) → ([x] : ◯ A) → B [x])
  (◯-ind-β : ∀ {ℓ ℓ′} {A : Type ℓ} {B : ◯ A → Type ℓ′} (B-mod : isModalFam B) (f : (x : A) → B (η x)) (x : A) → ◯-ind B-mod f (η x) ≡ f x)
  (let Type◯ = λ (ℓ : Level) → Σ (Type ℓ) isModal)
  (◯-lex : ∀ {ℓ} → isModal (Type◯ ℓ))
  where

private
  variable
     ℓ ℓ′ : Level

η-at : (A : Type ℓ) → A → ◯ A
η-at _ = η

module _ where
  private
    variable
      A : Type ℓ
      B : Type ℓ′

  module ◯-rec (B-mod : isModal B) (f : A → B) where
    abstract
      ◯-rec : ◯ A → B
      ◯-rec = ◯-ind (λ _ → B-mod) f

      ◯-rec-β : (x : A) → ◯-rec (η x) ≡ f x
      ◯-rec-β = ◯-ind-β (λ _ → B-mod) f

  open ◯-rec

  module ◯-map (f : A → B) where
    abstract
      ◯-map : ◯ A → ◯ B
      ◯-map = ◯-rec idemp λ x → η (f x)

      ◯-map-β : (x : A) → ◯-map (η x) ≡ η (f x)
      ◯-map-β x = ◯-rec-β idemp _ x

open ◯-rec
open ◯-map

module IsModalToUnitIsEquiv (A : Type ℓ) (A-mod : isModal A) where
  abstract
    inv : ◯ A → A
    inv = ◯-rec A-mod λ x → x

    η-retract : retract η inv
    η-retract = ◯-rec-β _ _

    η-section : section η inv
    η-section = ◯-ind (λ _ → ≡-modal idemp) λ x i → η (η-retract x i)

    η-iso : Iso A (◯ A)
    Iso.fun η-iso = η
    Iso.inv η-iso = inv
    Iso.rightInv η-iso = η-section
    Iso.leftInv η-iso = η-retract

    η-is-equiv : isEquiv (η-at A)
    η-is-equiv = isoToIsEquiv η-iso

abstract
  unit-is-equiv-to-is-modal : {A : Type ℓ} → isEquiv (η-at A) → isModal A
  unit-is-equiv-to-is-modal p = transport (cong isModal (sym (ua (η , p)))) idemp

  retract-is-modal
    : {A : Type ℓ} {B : Type ℓ′}
    → (A-mod : isModal A) (f : A → B) (g : B → A) (r : retract g f)
    → isModal B
  retract-is-modal {A = A} {B = B} A-mod f g r =
    unit-is-equiv-to-is-modal (isoToIsEquiv (iso η η-inv η-section η-retract))
    where
      η-inv : ◯ B → B
      η-inv = f ∘ ◯-rec A-mod g

      η-retract : retract η η-inv
      η-retract b = cong f (◯-rec-β A-mod g b) ∙ r b

      η-section : section η η-inv
      η-section = ◯-ind (λ _ → ≡-modal idemp) (cong η ∘ η-retract)

module LiftFam {A : Type ℓ} (B : A → Type ℓ′) where
  module M = IsModalToUnitIsEquiv (Type◯ ℓ′) ◯-lex

  abstract
    ◯-lift-fam : ◯ A → Type◯ ℓ′
    ◯-lift-fam = M.inv ∘ ◯-map (λ a → ◯ (B a) , idemp)

    ⟨◯⟩ : ◯ A → Type ℓ′
    ⟨◯⟩ [a] = ◯-lift-fam [a] .fst

    ⟨◯⟩-modal : isModalFam ⟨◯⟩
    ⟨◯⟩-modal [a] = ◯-lift-fam [a] .snd

    ⟨◯⟩-compute : (x : A) → ⟨◯⟩ (η x) ≡ ◯ (B x)
    ⟨◯⟩-compute x =
      ⟨◯⟩ (η x)
        ≡⟨ cong (fst ∘ M.inv) (◯-map-β _ _) ⟩
      M.inv (η (◯ (B x) , idemp)) .fst
        ≡⟨ cong fst (M.η-retract _) ⟩
      ◯ (B x) ∎

open LiftFam using (⟨◯⟩; ⟨◯⟩-modal; ⟨◯⟩-compute)

module LiftFamExtra {A : Type ℓ} {B : A → Type ℓ′} where
  ⟨◯⟩←◯ : ∀ {a} → ◯ (B a) → ⟨◯⟩ B (η a)
  ⟨◯⟩←◯ = transport (sym (⟨◯⟩-compute B _))

  ⟨◯⟩→◯ : ∀ {a} → ⟨◯⟩ B (η a) → ◯ (B a)
  ⟨◯⟩→◯ = transport (⟨◯⟩-compute B _)

  ⟨η⟩ : ∀ {a} → B a → ⟨◯⟩ B (η a)
  ⟨η⟩ = ⟨◯⟩←◯ ∘ η

  abstract
    ⟨◯⟩→◯-section : ∀ {a} → section (⟨◯⟩→◯ {a}) ⟨◯⟩←◯
    ⟨◯⟩→◯-section = transport⁻Transport (sym (⟨◯⟩-compute  _ _))

module Combinators where
  private
    variable
      ℓ′′ : Level
      A A′ : Type ℓ
      B : A → Type ℓ′
      C : Σ A B → Type ℓ′′

  λ/coe⟨_⟩_ : (p : A ≡ A′) → ((x : A′) → B (coe1→0 (λ i → p i) x)) → ((x : A) → B x)
  λ/coe⟨_⟩_ {B = B} p f = coe1→0 (λ i → (x : p i) → B (coei→0 (λ j → p j) i x)) f

open Combinators

module _ {A : Type ℓ} {B : A → Type ℓ′} where
  abstract
    Π-modal : isModalFam B → isModal ((x : A) → B x)
    Π-modal B-mod = retract-is-modal idemp η-inv η retr
      where
        η-inv : ◯ ((x : A) → B x) → (x : A) → B x
        η-inv [f] x = ◯-rec (B-mod x) (λ f → f x) [f]

        retr : retract η η-inv
        retr f = funExt λ x → ◯-rec-β (B-mod x) _ f

    Σ-modal : isModal A → isModalFam B → isModal (Σ A B)
    Σ-modal A-mod B-mod = retract-is-modal idemp η-inv η retr
      where
        h : ◯ (Σ A B) → A
        h = ◯-rec A-mod fst

        h-β : (x : Σ A B) → h (η x) ≡ fst x
        h-β = ◯-rec-β A-mod fst

        f : (i : I) (x : Σ A B) → B (h-β x i)
        f i x = coe1→i (λ j → B (h-β x j)) i (snd x)

        η-inv : ◯ (Σ A B) → Σ A B
        η-inv y = h y , ◯-ind (B-mod ∘ h) (f i0) y

        retr : (p : Σ A B) → η-inv (η p) ≡ p
        retr p =
          η-inv (η p)
            ≡⟨ ΣPathP (refl , ◯-ind-β _ _ _) ⟩
          h (η p) , f i0 p
            ≡⟨ ΣPathP (h-β _ , λ i → f i p) ⟩
          p ∎

module Σ-commute {A : Type ℓ} (B : A → Type ℓ′) where
  open LiftFamExtra

  ◯Σ = ◯ (Σ A B)

  module Σ◯ where
    Σ◯ = Σ (◯ A) (⟨◯⟩ B)
    abstract
      Σ◯-modal : isModal Σ◯
      Σ◯-modal = Σ-modal idemp (⟨◯⟩-modal _)

  open Σ◯

  η-Σ◯ : Σ A B → Σ◯
  η-Σ◯ (x , y) = η x , ⟨η⟩ y

  module Push where
    abstract
      fun : ◯Σ → Σ◯
      fun = ◯-rec Σ◯-modal η-Σ◯

      compute : fun ∘ η ≡ η-Σ◯
      compute = funExt (◯-rec-β _ _)

  module Unpush where
    abstract
      fun : Σ◯ → ◯Σ
      fun =
        λ⟨,⟩ ◯-ind (λ _ → Π-modal λ _ → idemp) λ x →
        λ/coe⟨ ⟨◯⟩-compute B x ⟩ ◯-map (x ,_)

      compute : fun ∘ η-Σ◯ ≡ η
      compute =
        funExt λ p →
        fun (η-Σ◯ p)
          ≡⟨ funExt⁻ (◯-ind-β _ _ _) _ ⟩
        transport refl (◯-map _ _)
          ≡⟨ transportRefl _ ⟩
        ◯-map _ (⟨◯⟩→◯ (⟨η⟩ _))
          ≡⟨ cong (◯-map _) (⟨◯⟩→◯-section _) ⟩
        ◯-map _ (η _)
          ≡⟨ ◯-map-β _ _ ⟩
        η p ∎

  push-unpush-compute : Push.fun ∘ Unpush.fun ∘ η-Σ◯ ≡ η-Σ◯
  push-unpush-compute =
    Push.fun ∘ Unpush.fun ∘ η-Σ◯
      ≡⟨ cong (Push.fun ∘_) Unpush.compute ⟩
    Push.fun ∘ η
      ≡⟨ Push.compute ⟩
    η-Σ◯ ∎

  unpush-push-compute : Unpush.fun ∘ Push.fun ∘ η ≡ η
  unpush-push-compute =
    Unpush.fun ∘ Push.fun ∘ η
      ≡⟨ cong (Unpush.fun ∘_) Push.compute ⟩
    Unpush.fun ∘ η-Σ◯
      ≡⟨ Unpush.compute ⟩
    η ∎

  is-section : section Unpush.fun Push.fun
  is-section = ◯-ind (λ x → ≡-modal idemp) λ x i → unpush-push-compute i x

  is-retract : retract Unpush.fun Push.fun
  is-retract =
    λ⟨,⟩ ◯-ind (λ _ → Π-modal λ _ → ≡-modal Σ◯-modal) λ x →
    λ/coe⟨ ⟨◯⟩-compute B x ⟩
     ◯-ind
      (λ _ → ≡-modal Σ◯-modal)
      (λ y i → push-unpush-compute i (x , y))

  push-sg-is-equiv : isEquiv Push.fun
  push-sg-is-equiv = isoToIsEquiv (iso Push.fun Unpush.fun is-retract is-section)

isConnected : Type ℓ → Type ℓ
isConnected A = isContr (◯ A)

module FormalDiskBundle {A : Type ℓ} where
  𝔻 : A → Type ℓ
  𝔻 a = Σ[ x ∈ A ] η a ≡ η x