------------------------------------------------------------------------
-- A labelled transition system for the delay monad
------------------------------------------------------------------------

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

open import Prelude

module Labelled-transition-system.Delay-monad {a} (A : Type a) where

open import Delay-monad hiding (steps)
open import Equality.Propositional
open import Prelude.Size

open import Labelled-transition-system

------------------------------------------------------------------------
-- Transitions

infix 4 _[_]⟶_

data _[_]⟶_ : Delay A ∞ → Maybe A → Delay A ∞ → Type a where
  now   : ∀ {x} → now   x [ just x  ]⟶ now   x
  later : ∀ {x} → later x [ nothing ]⟶ force x

delay-monad : LTS a
delay-monad = record
  { Proc      = Delay A ∞
  ; Label     = Maybe A
  ; _[_]⟶_    = _[_]⟶_
  ; is-silent = if_then true else false
  }

open LTS delay-monad public hiding (_[_]⟶_)

------------------------------------------------------------------------
-- Some simple lemmas

-- If now x can make a sequence of silent transitions to y, then y
-- is equal to now x.

now[nothing]⟶ : ∀ {x y} → now x [ nothing ]⟶ y → y ≡ now x
now[nothing]⟶ ()

now⇒ : ∀ {x y} → now x ⇒ y → y ≡ now x
now⇒ done            = refl
now⇒ (step () now _)

now[nothing]⇒ : ∀ {x y} → now x [ nothing ]⇒ y → y ≡ now x
now[nothing]⇒ (steps nx⇒x′ x′⟶x″ x″⇒y)
  rewrite now⇒ nx⇒x′ | now[nothing]⟶ x′⟶x″ = now⇒ x″⇒y

now[nothing]⇒̂ : ∀ {x y} → now x [ nothing ]⇒̂ y → y ≡ now x
now[nothing]⇒̂ (silent _ tr)     = now⇒ tr
now[nothing]⇒̂ (non-silent ¬s _) = ⊥-elim (¬s _)

now[nothing]⟶̂ : ∀ {x y} → now x [ nothing ]⟶̂ y → y ≡ now x
now[nothing]⟶̂ (done _)  = refl
now[nothing]⟶̂ (step tr) = now[nothing]⟶ tr

-- If now x can make a just y-transition, then x is equal to y.

now[just]⟶ : ∀ {x y z} → now x [ just y ]⟶ z → x ≡ y
now[just]⟶ now = refl

now[just]⇒ : ∀ {x y z} → now x [ just y ]⇒ z → x ≡ y
now[just]⇒ (steps tr₁ tr₂ _) =
  now[just]⟶ (subst (_[ _ ]⟶ _) (now⇒ tr₁) tr₂)

now[just]⇒̂ : ∀ {x y z} → now x [ just y ]⇒̂ z → x ≡ y
now[just]⇒̂ (silent () _)
now[just]⇒̂ (non-silent _ tr) = now[just]⇒ tr

now[just]⟶̂ : ∀ {x y z} → now x [ just y ]⟶̂ z → x ≡ y
now[just]⟶̂ (done ())
now[just]⟶̂ (step tr) = now[just]⟶ tr

-- The computation never can only transition to itself.

never⟶never : ∀ {μ x} → never [ μ ]⟶ x → x ≡ never
never⟶never later = refl

never⇒never : ∀ {x} → never ⇒ x → x ≡ never
never⇒never done               = refl
never⇒never (step _ later ne⇒) = never⇒never ne⇒

never[]⇒never : ∀ {μ x} → never [ μ ]⇒ x → x ≡ never
never[]⇒never (steps ne⇒x x⟶y y⇒z)
  rewrite never⇒never ne⇒x | never⟶never x⟶y = never⇒never y⇒z

never⇒̂never : ∀ {μ x} → never [ μ ]⇒̂ x → x ≡ never
never⇒̂never (silent     _ ne⇒) = never⇒never ne⇒
never⇒̂never (non-silent _ ne⇒) = never[]⇒never ne⇒

never⟶̂never : ∀ {μ x} → never [ μ ]⟶̂ x → x ≡ never
never⟶̂never (done _)   = refl
never⟶̂never (step ne⟶) = never⟶never ne⟶

-- If never can make a μ-transition, then μ is silent.

never⟶→silent : ∀ {μ x} → never [ μ ]⟶ x → Silent μ
never⟶→silent later = _

never[]⇒→silent : ∀ {μ x} → never [ μ ]⇒ x → Silent μ
never[]⇒→silent (steps ne⇒x  x⟶  _) with never⇒never ne⇒x
never[]⇒→silent (steps ne⇒ne ne⟶ _) | refl = never⟶→silent ne⟶

never⇒̂→silent : ∀ {μ x} → never [ μ ]⇒̂ x → Silent μ
never⇒̂→silent (silent s _)       = s
never⇒̂→silent (non-silent _ ne⇒) = never[]⇒→silent ne⇒

never⟶̂→silent : ∀ {μ x} → never [ μ ]⟶̂ x → Silent μ
never⟶̂→silent (done s)   = s
never⟶̂→silent (step ne⟶) = never⟶→silent ne⟶

-- If x can make a non-silent transition, with label just y, to z,
-- then z is equal to now y.

[just]⟶ : ∀ {x y z} → x [ just y ]⟶ z → z ≡ now y
[just]⟶ now = refl

[just]⇒ : ∀ {x y z} → x [ just y ]⇒ z → z ≡ now y
[just]⇒ (steps _ now ny⇒z) = now⇒ ny⇒z

[just]⇒̂ : ∀ {x y z} → x [ just y ]⇒̂ z → z ≡ now y
[just]⇒̂ (silent () _)
[just]⇒̂ (non-silent _ x⇒z) = [just]⇒ x⇒z

[just]⟶̂ : ∀ {x y z} → x [ just y ]⟶̂ z → z ≡ now y
[just]⟶̂ (done ())
[just]⟶̂ (step x⟶z) = [just]⟶ x⟶z

-- In some cases x is also equal to now y.

[just]⟶→≡now : ∀ {x y z} → x [ just y ]⟶ z → x ≡ now y
[just]⟶→≡now now = refl

[just]⟶̂→≡now : ∀ {x y z} → x [ just y ]⟶̂ z → x ≡ now y
[just]⟶̂→≡now (done ())
[just]⟶̂→≡now (step x⟶z) = [just]⟶→≡now x⟶z

-- If force x can make a weak μ-transition (_[_]⇒_ or _[_]⇒̂_) to y,
-- then later x can also make a weak μ-transition (of the same kind)
-- to y.

later[]⇒ : ∀ {μ x y} → force x [ μ ]⇒ y → later x [ μ ]⇒ y
later[]⇒ = ⇒[]⇒-transitive (⟶→⇒ _ later)

later⇒̂ : ∀ {μ x y} → force x [ μ ]⇒̂ y → later x [ μ ]⇒̂ y
later⇒̂ = ⇒⇒̂-transitive (⟶→⇒ _ later)

-- The process x can make a silent transition to drop-later x.

⇒drop-later : ∀ {x} → x ⇒ drop-later x
⇒drop-later {now x}   = done
⇒drop-later {later x} = step _ later done

-- If x can make a transition to y, then drop-later x can in some
-- cases make a transition of the same kind to drop-later y.

drop-later-cong⟶ :
  ∀ {x μ y} →
  ¬ Silent μ → x [ μ ]⟶ y → drop-later x [ μ ]⟶ drop-later y
drop-later-cong⟶ _  now   = now
drop-later-cong⟶ ¬s later = ⊥-elim (¬s _)

drop-later-cong⇒ : ∀ {x y} → x ⇒ y → drop-later x ⇒ drop-later y
drop-later-cong⇒ done                = done
drop-later-cong⇒ (step () now   _)
drop-later-cong⇒ (step _  later x⇒y) =
  ⇒-transitive ⇒drop-later (drop-later-cong⇒ x⇒y)

drop-later-cong[]⇒ :
  ∀ {x μ y} →
  ¬ Silent μ → x [ μ ]⇒ y → drop-later x [ μ ]⇒ drop-later y
drop-later-cong[]⇒ ¬s (steps x⇒y y⟶z z⇒u) =
  steps (drop-later-cong⇒ x⇒y) (drop-later-cong⟶ ¬s y⟶z)
        (drop-later-cong⇒ z⇒u)

drop-later-cong⇒̂ :
  ∀ {x μ y} → x [ μ ]⇒̂ y → drop-later x [ μ ]⇒̂ drop-later y
drop-later-cong⇒̂ (silent s x⇒y)      = silent s (drop-later-cong⇒ x⇒y)
drop-later-cong⇒̂ (non-silent ¬s x⇒y) =
  non-silent ¬s (drop-later-cong[]⇒ ¬s x⇒y)

drop-later-cong⟶̂ :
  ∀ {x μ y} →
  ¬ Silent μ → x [ μ ]⟶̂ y → drop-later x [ μ ]⟶̂ drop-later y
drop-later-cong⟶̂ _  (done s)   = done s
drop-later-cong⟶̂ ¬s (step x⟶y) = step (drop-later-cong⟶ ¬s x⟶y)

-- If later x can make a transition to later y, then force x can
-- make a transition (of the same kind) to force y.

drop-later⟶ :
  ∀ {μ x y} → later x [ μ ]⟶ later y → force x [ μ ]⟶ force y
drop-later⟶ lx⟶ly = helper lx⟶ly refl refl
  where
  helper : ∀ {μ x x′ y y′} →
           later x [ μ ]⟶ y′ →
           y′ ≡ later y →
           x′ ≡ force x →
           x′ [ μ ]⟶ force y
  helper {x = x} {x′} {y} later x≡ly x′≡x =
    subst (_[ _ ]⟶ _) ly≡x′ later
    where
    ly≡x′ =
      later y  ≡⟨ sym x≡ly ⟩
      force x  ≡⟨ sym x′≡x ⟩∎
      x′       ∎

drop-later⇒ : ∀ {x y} → later x ⇒ later y → force x ⇒ force y
drop-later⇒ = drop-later-cong⇒

drop-later[]⇒ :
  ∀ {μ x y} → later x [ μ ]⇒ later y → force x [ μ ]⇒ force y
drop-later[]⇒ (steps lx⇒ly later y⇒lz) =
  ⇒[]⇒-transitive (⇒-transitive (drop-later⇒ lx⇒ly) y⇒lz)
                  (⟶→[]⇒ later)
drop-later[]⇒ (steps _ now ny⇒lz) with now⇒ ny⇒lz
... | ()

drop-later⇒̂ :
  ∀ {μ x y} → later x [ μ ]⇒̂ later y → force x [ μ ]⇒̂ force y
drop-later⇒̂ = drop-later-cong⇒̂

drop-later⟶̂ :
  ∀ {μ x y} → later x [ μ ]⟶̂ later y → force x [ μ ]⟶̂ force y
drop-later⟶̂ (done s)     = done s
drop-later⟶̂ (step lx⟶ly) = step (drop-later⟶ lx⟶ly)

-- If x makes silent transitions to both y and z, then one of y and
-- z makes silent transitions to the other.

⇒×⇒→… : ∀ {x y z} → x ⇒ y → x ⇒ z → y ⇒ z ⊎ z ⇒ y
⇒×⇒→… done               x⇒z                = inj₁ x⇒z
⇒×⇒→… x⇒y                done               = inj₂ x⇒y
⇒×⇒→… (step _ later x⇒y) (step _ later x⇒z) = ⇒×⇒→… x⇒y x⇒z
⇒×⇒→… (step () now _)

-- If x makes silent transitions to y and a non-silent weak
-- μ-transition (of one kind) to z, then y makes a weak μ-transition
-- to z.

⇒×⇒[]→… :
  ∀ {x y z μ} →
  ¬ Silent μ → x ⇒ y → x [ μ ]⇒ z → y [ μ ]⇒ z
⇒×⇒[]→… _ x⇒y (steps x⇒x′ x′⟶x″ x″⇒z) with ⇒×⇒→… x⇒y x⇒x′
⇒×⇒[]→… _ _   (steps _    x′⟶x″ x″⇒z) | inj₁ y⇒x′ = steps y⇒x′ x′⟶x″ x″⇒z
⇒×⇒[]→… _ _   (steps _    now   n⇒z)  | inj₂ done = steps done now   n⇒z

⇒×⇒[]→… _  _  _                 | inj₂ (step () now _)
⇒×⇒[]→… ¬s _  (steps _ later _) | _ = ⊥-elim (¬s _)

-- If x makes silent transitions to y and a weak μ-transition (of
-- one kind) to z, then either y makes a weak μ-transition to z, or
-- μ is silent and z makes silent transitions to y.

⇒×⇒̂→… :
  ∀ {x y z μ} → x ⇒ y → x [ μ ]⇒̂ z → y [ μ ]⇒̂ z ⊎ Silent μ × z ⇒ y
⇒×⇒̂→… x⇒y (silent     s  x⇒z) = ⊎-map (silent s) (s ,_)
                                      (⇒×⇒→… x⇒y x⇒z)
⇒×⇒̂→… x⇒y (non-silent ¬s x⇒z) =
  inj₁ (non-silent ¬s (⇒×⇒[]→… ¬s x⇒y x⇒z))


module _ where

open import Common.Prelude
open import Common.Reflection
open import Common.Equality

record Foo (A : Set) : Set where
  field
    foo : A → Nat

open Foo {{...}}

instance
  FooNat : Foo Nat
  foo {{FooNat}} n = n

  FooBool : Foo Bool
  foo {{FooBool}} true  = 1
  foo {{FooBool}} false = 0

macro
  do-foo : ∀ {A} {{_ : Foo A}} → A → Tactic
  do-foo x = give (lit (nat (foo x)))

test₁ : do-foo 5 ≡ 5
test₁ = refl

test₂ : do-foo false ≡ 0
test₂ = refl

macro
  lastHidden : {n : Nat} → Tactic
  lastHidden {n} = give (lit (nat n))

test₃ : lastHidden {4} ≡ 4
test₃ = refl

macro
  hiddenTerm : {a : Term} → Tactic
  hiddenTerm {a} = give a

test₄ : hiddenTerm {4} ≡ 4
test₄ = refl


module _ where

open import Agda.Builtin.Nat
open import Agda.Builtin.String
open import Common.IO
open import Common.Unit

data ⊥ : Set where

record Pair : Set where
  constructor _,_
  field fst snd : String

open Pair

record ERing : Set where
  constructor ering
  field divRem : (⊥ → ⊥) → Pair

open ERing

eRing : ERing
eRing = ering λ _ → "fst" , "snd"

test : String
test = snd (divRem eRing (λ ()))

main : IO Unit
main = putStrLn test


module STLC2.Kovacs.Completeness where

open import STLC2.Kovacs.Normalisation public
open import STLC2.Kovacs.Convertibility public

-- TODO: Remove this
postulate
  █ : ∀ {ℓ} → {X : Set ℓ} → X

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

-- (_≈_)
mutual
  infix 3 _≫_
  _≫_ : ∀ {A Γ} → Γ ⊢ A → Γ ⊩ A → Set

  _≫_ {⎵}      {Γ} M N = M ∼ embⁿᶠ N

  _≫_ {A ⇒ B} {Γ} M f = ∀ {Γ′} → (η : Γ′ ⊇ Γ)
                                → {N : Γ′ ⊢ A} {∂a : Γ′ ∂⊩ A}
                                → N ∂≫ ∂a
                                → ren η M ∙ N ∂≫ f η ∂a

  _≫_ {A ⩕ B}  {Γ} M s = π₁ M ∂≫ proj₁ s × π₂ M ∂≫ proj₂ s

  _≫_ {⫪}     {Γ} M s = ⊤

  _≫_ {⫫}     {Γ} M s = ⊥

  _≫_ {A ⩖ B}  {Γ} M s = elim⊎ s (λ ∂a → Σ (Γ ⊢ A) (λ M₁ → M₁ ∂≫ ∂a))
                                 (λ ∂b → Σ (Γ ⊢ B) (λ M₂ → M₂ ∂≫ ∂b))

  infix 3 _∂≫_
  _∂≫_ : ∀ {Γ A} → Γ ⊢ A → Γ ∂⊩ A → Set

  _∂≫_ {Γ} {A} M ∂a
    = ∀ {Γ′ C} → (η : Γ′ ⊇ Γ)
               → {N : Γ′ ⊢ C}
               → {Nⁿᶠ : Γ′ ⊢ⁿᶠ C}
               → (f : ∀ {Γ″} → (η′ : Γ″ ⊇ Γ′)
                              → {a : Γ″ ⊩ A}
                              → ren (η ○ η′) M ≫ a
                              → ren η′ N ∼ embⁿᶠ (renⁿᶠ η′ Nⁿᶠ))
               → N ∼ embⁿᶠ Nⁿᶠ

-- (_≈ᶜ_)
infix 3 _∂≫⋆_
data _∂≫⋆_ : ∀ {Γ Ξ} → Γ ⊢⋆ Ξ → Γ ∂⊩⋆ Ξ → Set
  where
    ∅   : ∀ {Γ} → ∅ {Γ} ∂≫⋆ ∅

    _,_ : ∀ {Γ Ξ A} → {σ : Γ ⊢⋆ Ξ} {ρ : Γ ∂⊩⋆ Ξ}
                    → (χ : σ ∂≫⋆ ρ)
                    → {M : Γ ⊢ A} {∂a : Γ ∂⊩ A}
                    → (∂q : M ∂≫ ∂a)
                    → σ , M ∂≫⋆ ρ , ∂a

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

-- (_∼◾≈_)
mutual
  coe≫ : ∀ {A Γ} → {M₁ M₂ : Γ ⊢ A}
                 → M₁ ∼ M₂ → {a : Γ ⊩ A} → M₁ ≫ a
                 → M₂ ≫ a
  coe≫ {⎵}      p {N}      q = p ⁻¹ ⦙ q
  coe≫ {A ⇒ B} p {f}      g = λ η {N} {∂a} ∂q →
                                 ∂coe≫ {∂a = λ η′ f′ → f η ∂a η′ f′}
                                       (ren∼ η p ∙∼ (refl∼ {M = N}))
                                       (g η {N} {∂a} ∂q)
  coe≫ {A ⩕ B}  p {s}      q = ∂coe≫ {∂a = proj₁ s} (π₁∼ p) (proj₁ q) ,
                               ∂coe≫ {∂a = proj₂ s} (π₂∼ p) (proj₂ q)
  coe≫ {⫪}     p {s}      q = tt
  coe≫ {⫫}     p {s}      q = elim⊥ s
  coe≫ {A ⩖ B}  p {inj₁ a} q = q
  coe≫ {A ⩖ B}  p {inj₂ b} q = q

  ∂coe≫ : ∀ {A Γ} → {M₁ M₂ : Γ ⊢ A} {∂a : Γ ∂⊩ A}
                  → M₁ ∼ M₂ → M₁ ∂≫ ∂a
                  → M₂ ∂≫ ∂a

  ∂coe≫ p ∂q
    = λ η {N} {Nⁿᶠ} f →
        ∂q η {N} {Nⁿᶠ} (λ η′ {a} q →
          f η′ (coe≫ (ren∼ (η ○ η′) p)
                     q))

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

-- (≈ₑ)
mutual
  acc≫ : ∀ {A Γ Γ′} → (η : Γ′ ⊇ Γ)
                    → {M : Γ ⊢ A} {a : Γ ⊩ A}
                    → M ≫ a
                    → ren η M ≫ acc η a
  acc≫ {⎵}      η {M} {N}      p         = coe ((λ N′ → ren η M ∼ N′)
                                                & natembⁿᶠ η N ⁻¹)
                                               (ren∼ η p)
  acc≫ {A ⇒ B} η {M} {f}      g         η′ {N} {∂a}
                                         rewrite ren○ η′ η M ⁻¹
                                         = g (η ○ η′) {N} {∂a}
  acc≫ {A ⩕ B}  η {M} {s}      q         = ∂acc≫ η {π₁ M} {proj₁ s} (proj₁ q)
                                         , ∂acc≫ η {π₂ M} {proj₂ s} (proj₂ q)
  acc≫ {⫪}     η {M} {s}      q         = tt
  acc≫ {⫫}     η {M} {s}      q         = elim⊥ s
  acc≫ {A ⩖ B}  η {M} {inj₁ a} (M₁ , ∂a) = ren η M₁ , ∂acc≫ η {M₁} {a} ∂a
  acc≫ {A ⩖ B}  η {M} {inj₂ b} (M₂ , ∂b) = ren η M₂ , ∂acc≫ η {M₂} {b} ∂b

  ∂acc≫ : ∀ {A Γ Γ′} → (η : Γ′ ⊇ Γ)
                     → {M : Γ ⊢ A} {∂a : Γ ∂⊩ A}
                     → M ∂≫ ∂a
                     → ren η M ∂≫ ∂acc η ∂a

  ∂acc≫ η {M} ∂q
    = λ η′ {N} {Nⁿᶠ} f →
        ∂q (η ○ η′) {N} {Nⁿᶠ} (λ η″ q →
          f η″ (coe≫ (coe (_∼_ & ((λ η‴ → ren η‴ M) & assoc○ η″ η′ η ⁻¹)
                               ⊗ ren○ (η′ ○ η″) η M)
                          refl∼)
                     q))

-- (≈ᶜₑ)
-- NOTE: _⬖≫_ = ∂acc≫⋆
_⬖≫_ : ∀ {Γ Γ′ Ξ} → {σ : Γ ⊢⋆ Ξ} {ρ : Γ ∂⊩⋆ Ξ}
                  → (χ : σ ∂≫⋆ ρ) (η : Γ′ ⊇ Γ)
                  → σ ◐ η ∂≫⋆ ρ ⬖ η
∅                 ⬖≫ η = ∅
_,_ χ {M} {∂a} ∂q ⬖≫ η = χ ⬖≫ η , ∂acc≫ η {M} {∂a} ∂q

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

return≫ : ∀ {A Γ} → {M : Γ ⊢ A} {a : Γ ⊩ A}
                  → M ≫ a → M ∂≫ return a

return≫ {M = M} {a} q
  = λ η {N} {Nⁿᶠ} f →
      coe (_∼_ & idren N
               ⊗ embⁿᶠ & idrenⁿᶠ Nⁿᶠ)
          (f idₑ {acc η a}
                 (coe≫ (coe ((λ η′ → ren η M ∼ ren η′ M)
                             & (rid○ η ⁻¹))
                            refl∼)
                       (acc≫ η {M} {a} q)))

bind≫ : ∀ {A C Γ} → {M : Γ ⊢ A} {∂a : Γ ∂⊩ A}
                     {N : Γ ⊢ C} {∂c : Γ ∂⊩ C}
                  → M ∂≫ ∂a → (∀ {Γ′} → (η : Γ′ ⊇ Γ)
                                        → {a : Γ′ ⊩ A}
                                        → ren η M ≫ a
                                        → ren η N ∂≫ ∂acc η ∂c)
                  → N ∂≫ ∂c

bind≫ {M = M} {∂a} {N} {∂c} ∂q f
  = λ η {N′} {Nⁿᶠ′} f′ →
      ∂q η {N′} {Nⁿᶠ′} (λ η′ {a} q →
        f (η ○ η′) {a} q idₑ {ren η′ N′} {renⁿᶠ η′ Nⁿᶠ′} (λ η″ {c} q′ →
          coe (_∼_ & ren○ η″ η′ N′
                   ⊗ embⁿᶠ & renⁿᶠ○ η″ η′ Nⁿᶠ′)
              (f′ (η′ ○ η″) (coe≫ (coe (_∼_ & ren○ (idₑ ○ η″) (η ○ η′) N
                                            ⊗ (λ η‴ → ren η‴ N)
                                              & ( (λ η‴ → (η ○ η′) ○ η‴)
                                                  & lid○ η″
                                                ⦙ assoc○ η″ η′ η
                                                ))
                                       refl∼)
                                  q′))))

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

-- (∈≈)
get≫ : ∀ {Γ Ξ A} → {σ : Γ ⊢⋆ Ξ} {ρ : Γ ∂⊩⋆ Ξ}
                 → σ ∂≫⋆ ρ → (i : Ξ ∋ A)
                 → getₛ σ i ∂≫ getᵥ ρ i
get≫ (χ , p) zero    = p
get≫ (χ , p) (suc i) = get≫ χ i

-- (Tm≈)
eval≫ : ∀ {Γ Ξ A} → {σ : Γ ⊢⋆ Ξ} {ρ : Γ ∂⊩⋆ Ξ}
                 → σ ∂≫⋆ ρ → (M : Ξ ⊢ A)
                 → sub σ M ∂≫ eval ρ M

eval≫ {σ = σ} {ρ} χ (𝓋 i)
  = get≫ χ i

eval≫ {σ = σ} {ρ} χ (ƛ M)
  = return≫ {M = ƛ (sub (liftₛ σ) M)}
            {a = λ {Γ′} η ∂a → eval (ρ ⬖ η , ∂a) M}
            (λ {Γ′} η {N} {∂a} ∂q →
              ∂coe≫ {∂a = eval (ρ ⬖ η , ∂a) M}
                    (coe (((ƛ (ren (liftₑ η) (sub (liftₛ σ) M)) ∙ N) ∼_)
                          & ( sub◑ (idₛ , N) (liftₑ η) (sub (liftₛ σ) M) ⁻¹
                            ⦙ sub● (liftₑ η ◑ (idₛ , N)) (liftₛ σ) M ⁻¹
                            ⦙ (λ σ′ → sub (σ′ , N) M)
                              & ( comp●◑ (η ◑ idₛ , N) (wkₑ idₑ) σ
                                ⦙ (σ ●_) & lid◑ (η ◑ idₛ)
                                ⦙ comp●◑ idₛ η σ ⁻¹
                                ⦙ rid● (σ ◐ η)
                                )
                            ))
                         (red⇒ (ren (liftₑ η) (sub (liftₛ σ) M)) N) ⁻¹)
                    (eval≫ (_,_ (χ ⬖≫ η) {∂a = ∂a} (∂q)) M))

eval≫ {σ = σ} {ρ} χ (M ∙ N)
  = bind≫ {M  = sub σ M}
          {∂a = eval ρ M}
          {N  = sub σ M ∙ sub σ N}
          {∂c = eval ρ M ∂!∙ eval ρ N}
          (eval≫ χ M)
          (λ η {f} g →
            ∂coe≫ {∂a = eval (ρ ⬖ η) M ∂!∙ eval (ρ ⬖ η) N}
                  (coe (_∼_ & ((λ M′ → M′ ∙ ren η (sub σ N))
                               & ( (λ η′ → ren η′ (sub σ M))
                                   & (rid○ η ⁻¹)
                                 ⦙ ren○ idₑ η (sub σ M)
                                 ))
                            ⊗ refl)
                       refl∼)
                  (g idₑ {∂a = ∂acc η (eval ρ N)}
                         (∂coe≫ {∂a = eval (ρ ⬖ η) N}
                                (≡→∼ (sub◐ η σ N))
                                (eval≫ (χ ⬖≫ η) N))))

eval≫ {σ = σ} {ρ} χ (M , N)
  = return≫ {M = sub σ M , sub σ N}
            {a = eval ρ M , eval ρ N}
            ( ∂coe≫ {∂a = eval ρ M}
                    (red⩕₁ (sub σ M) (sub σ N) ⁻¹)
                    (eval≫ χ M)
            , ∂coe≫ {∂a = eval ρ N}
                    (red⩕₂ (sub σ M) (sub σ N) ⁻¹)
                    (eval≫ χ N)
            )

eval≫ {σ = σ} {ρ} χ (π₁ M)
  = bind≫ {M  = sub σ M}
          {∂a = eval ρ M}
          {N  = π₁ (sub σ M)}
          {∂c = ∂!π₁ (eval ρ M)}
          (eval≫ χ M)
          (λ η q → proj₁ q)

eval≫ {σ = σ} {ρ} χ (π₂ M)
  = bind≫ {M  = sub σ M}
          {∂a = eval ρ M}
          {N  = π₂ (sub σ M)}
          {∂c = ∂!π₂ (eval ρ M)}
          (eval≫ χ M)
          (λ η q → proj₂ q)

eval≫ {σ = σ} {ρ} χ τ
  = return≫ {M = τ} tt

eval≫ {σ = σ} {ρ} χ (φ M)
  = bind≫ {M  = sub σ M}
          {∂a = eval ρ M}
          {N  = φ (sub σ M)}
          {∂c = ∂!φ (eval ρ M)}
          (eval≫ χ M)
          (λ η q → elim⊥ q)

eval≫ {σ = σ} {ρ} χ (ι₁ M)
  = return≫ {M = ι₁ (sub σ M)}
            {a = inj₁ (eval ρ M)}
            (sub σ M , eval≫ χ M)

eval≫ {σ = σ} {ρ} χ (ι₂ M)
  = return≫ {M = ι₂ (sub σ M)}
            {a = inj₂ (eval ρ M)}
            (sub σ M , eval≫ χ M)

eval≫ {σ = σ} {ρ} χ (M ⁇ N₁ ∥ N₂)
  = bind≫ {M  = sub σ M}
          {∂a = eval ρ M}
          {N  = sub σ M ⁇ sub (liftₛ σ) N₁
                        ∥ sub (liftₛ σ) N₂}
          {∂c = eval ρ M ∂!⁇ (λ η ∂a → eval (ρ ⬖ η , ∂a) N₁)
                         ∂!∥ (λ η ∂b → eval (ρ ⬖ η , ∂b) N₂)}
          (eval≫ χ M)
          (λ { η {inj₁ ∂a} (M₁ , ∂q) →
                 λ η′ {N} {Nⁿᶠ} f →
                   ∂q η′ {N} {Nⁿᶠ} (λ η″ {a} q →
                     █)
             ; η {inj₂ ∂b} (M₂ , ∂q) →
                 λ η′ {N} {Nⁿᶠ} f →
                   ∂q η′ {N} {Nⁿᶠ} (λ η″ {a} q →
                     █)
             })

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

mutual
  -- (q≈)
  reify≫ : ∀ {A Γ} → {M : Γ ⊢ A} {a : Γ ∂⊩ A}
                   → (∂q : M ∂≫ a)
                   → M ∼ embⁿᶠ (reify a)

  reify≫ {⎵} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {N} p →
        █)

  reify≫ {A ⇒ B} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {f} g →
        █)

  reify≫ {A ⩕ B} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {s} q →
        █)

  reify≫ {⫪} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {s} q →
        coe ((λ M′ → ren η M ∼ M′)
             & natembⁿᶠ η (∂a idₑ (λ η′ s′ → τ)) ⁻¹)
            (ren∼ {M₁ = M}
                  {M₂ = embⁿᶠ (∂a idₑ (λ η′ s′ → τ))}
                  η
                  █))

  reify≫ {⫫} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {s} q →
        █)

  reify≫ {A ⩖ B} {M = M} {∂a} ∂q
    = ∂q idₑ {M} {reify ∂a} (λ η {s} q →
        █)

  -- (u≈)
  reflect≫ : ∀ {A Γ} → (M : Γ ⊢ⁿᵉ A)
                     → embⁿᵉ M ∂≫ reflect M

  reflect≫ {⎵} M
    = return≫ {M = embⁿᵉ M}
              {a = ne M}
              refl∼

  reflect≫ {A ⇒ B} M
    = return≫ {M = embⁿᵉ M}
              {a = λ η ∂a → reflect (renⁿᵉ η M ∙ reify ∂a)}
              (λ η {N} {∂a} ∂q →
                ∂coe≫ {∂a = ∂acc η (reflect M) ∂!∙ ∂a}
                      (≡→∼ (natembⁿᵉ η M ⁻¹) ∙∼ reify≫ ∂q ⁻¹)
                      (reflect≫ (renⁿᵉ η M ∙ reify ∂a)))

  reflect≫ {A ⩕ B} M
    = return≫ {M = embⁿᵉ M}
              {a = reflect (π₁ M) , reflect (π₂ M)}
              (reflect≫ (π₁ M) , reflect≫ (π₂ M))

  reflect≫ {⫪} M
    = return≫ {M = embⁿᵉ M}
              {a = tt}
              tt

  reflect≫ {⫫} M
    = λ η {N} {Nⁿᶠ} f →
        coe (_∼_ & idren N
                 ⊗ embⁿᶠ & idrenⁿᶠ Nⁿᶠ)
            (f idₑ {█} █)

  reflect≫ {A ⩖ B} M
    = λ η {N} {Nⁿᶠ} f →
        coe (_∼_ & idren N
                 ⊗ embⁿᶠ & idrenⁿᶠ Nⁿᶠ)
            (f idₑ {█} █)

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

-- (uᶜ≈)
id≫⋆ : ∀ {Γ} → idₛ {Γ} ∂≫⋆ idᵥ
id≫⋆ {∅}     = ∅
id≫⋆ {Γ , A} = id≫⋆ ⬖≫ wkₑ idₑ , reflect≫ 0

complete : ∀ {Γ A} → (M : Γ ⊢ A)
                   → M ∼ embⁿᶠ (nf M)
complete M = coe ((_∼ embⁿᶠ (reify (eval idᵥ M))) & idsub M)
                 (reify≫ (eval≫ id≫⋆ M))

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


module Theory where

open import Data.List using (List; []; _∷_; _++_)
open import Data.Fin using () renaming (zero to fzero; suc to fsuc)

open import Relation.Binary using (Rel)

open import Level using (suc; _⊔_)

open import Syntax

record Theory ℓ₁ ℓ₂ ℓ₃ : Set (suc (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)) where
  field
    Sg : Signature ℓ₁ ℓ₂

  open Signature Sg
  open Term Sg

  field
    Ax : forall Γ A -> Rel (Γ ⊢ A) ℓ₃

  infix 3 _⊢_≡_
  infix 3 _⊨_≡_

  data _⊢_≡_ : forall Γ {A} -> Γ ⊢ A -> Γ ⊢ A -> Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)
  data _⊨_≡_ : forall Γ {Γ′} -> Γ ⊨ Γ′ -> Γ ⊨ Γ′ -> Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)

  -- Theorems
  data _⊢_≡_ where
    ax : forall {Γ A e₁ e₂} -> Ax Γ A e₁ e₂ -> Γ ⊢ e₁ ≡ e₂

    refl : forall {Γ} {A} {e : Γ ⊢ A} -> Γ ⊢ e ≡ e
    sym : forall {Γ} {A} {e₁ : Γ ⊢ A} {e₂} -> Γ ⊢ e₁ ≡ e₂ -> Γ ⊢ e₂ ≡ e₁
    trans : forall {Γ} {A} {e₁ : Γ ⊢ A} {e₂ e₃} -> Γ ⊢ e₁ ≡ e₂ -> Γ ⊢ e₂ ≡ e₃ -> Γ ⊢ e₁ ≡ e₃

    sub/id : forall {Γ} {A} {e : Γ ⊢ A} -> Γ ⊢ e [ id ] ≡ e
    sub/∙ : forall {Γ} {A} {Γ′ Γ′′} {e : Γ′′ ⊢ A} {γ : Γ′ ⊨ Γ′′} {δ}
      -> Γ ⊢ e [ γ ∙ δ ] ≡ e [ γ ] [ δ ]

    eta/Unit : forall {Γ} (e : Γ ⊢ Unit) -> Γ ⊢ unit ≡ e

    beta/*₁ : forall {Γ} {A B} (e₁ : Γ ⊢ A) (e₂ : Γ ⊢ B) -> Γ ⊢ fst (pair e₁ e₂) ≡ e₁
    beta/*₂ : forall {Γ} {A B} (e₁ : Γ ⊢ A) (e₂ : Γ ⊢ B) -> Γ ⊢ snd (pair e₁ e₂) ≡ e₂
    eta/* : forall {Γ} {A B} (e : Γ ⊢ A * B) -> Γ ⊢ pair (fst e) (snd e) ≡ e

    beta/=> : forall {Γ} {A B} (e : A ∷ Γ ⊢ B) (e′ : Γ ⊢ A) -> Γ ⊢ app (abs e) e′ ≡ e [ ext id e′ ]
    eta/=> : forall {Γ} {A B} (e : Γ ⊢ A => B) -> Γ ⊢ abs (app (e [ weaken ]) var) ≡ e

    -- cong
    cong/func : forall {Γ} {f : Func} {e e′ : Γ ⊢ dom f}
      -> Γ ⊢ e ≡ e′
      -> Γ ⊢ func f e ≡ func f e′
    cong/sub : forall {Γ Γ′} {γ γ′ : Γ ⊨ Γ′} {A} {e e′ : Γ′ ⊢ A}
      -> Γ ⊨ γ ≡ γ′
      -> Γ′ ⊢ e ≡ e′
      -> Γ ⊢ e [ γ ] ≡ e′ [ γ′ ]
    cong/pair : forall {Γ} {A B} {e₁ e₁′ : Γ ⊢ A} {e₂ e₂′ : Γ ⊢ B}
      -> Γ ⊢ e₁ ≡ e₁′
      -> Γ ⊢ e₂ ≡ e₂′
      -> Γ ⊢ pair e₁ e₂ ≡ pair e₁′ e₂′
    cong/fst : forall {Γ} {A B} {e e′ : Γ ⊢ A * B}
      -> Γ ⊢ e ≡ e′
      -> Γ ⊢ fst e ≡ fst e′
    cong/snd : forall {Γ} {A B} {e e′ : Γ ⊢ A * B}
      -> Γ ⊢ e ≡ e′
      -> Γ ⊢ snd e ≡ snd e′
    cong/abs : forall {Γ} {A B} {e e′ : A ∷ Γ ⊢ B}
      -> A ∷ Γ ⊢ e ≡ e′
      -> Γ ⊢ abs e ≡ abs e′
    cong/app : forall {Γ} {A B} {e₁ e₁′ : Γ ⊢ A => B} {e₂ e₂′ : Γ ⊢ A}
      -> Γ ⊢ e₁ ≡ e₁′
      -> Γ ⊢ e₂ ≡ e₂′
      -> Γ ⊢ app e₁ e₂ ≡ app e₁′ e₂′

    -- subst commutes with term formers
    comm/func : forall Γ Γ′ (γ : Γ ⊨ Γ′) f e
      -> Γ ⊢ func f e [ γ ] ≡ func f (e [ γ ])

    comm/unit : forall Γ Γ′ (γ : Γ ⊨ Γ′)
      -> Γ ⊢ unit [ γ ] ≡ unit

    comm/pair : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A B} {e₁ : Γ′ ⊢ A} {e₂ : Γ′ ⊢ B}
      -> Γ ⊢ pair e₁ e₂ [ γ ] ≡ pair (e₁ [ γ ]) (e₂ [ γ ])
    comm/fst : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A B} {e : Γ′ ⊢ A * B}
      -> Γ ⊢ fst e [ γ ] ≡ fst (e [ γ ])
    comm/snd : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A B} {e : Γ′ ⊢ A * B}
      -> Γ ⊢ snd e [ γ ] ≡ snd (e [ γ ])

    comm/abs : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A B} {e : A ∷ Γ′ ⊢ B}
      -> Γ ⊢ (abs e) [ γ ] ≡ abs (e [ ext (γ ∙ weaken) var ])
    comm/app : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A B} {e₁ : Γ′ ⊢ A => B} {e₂}
      -> Γ ⊢ app e₁ e₂ [ γ ] ≡ app (e₁ [ γ ]) (e₂ [ γ ])

    var/ext : forall {Γ Γ′} (γ : Γ ⊨ Γ′) {A} (e : Γ ⊢ A)
      -> Γ ⊢ var [ ext γ e ] ≡ e

  data _⊨_≡_ where
    refl : forall {Γ Γ′} {γ : Γ ⊨ Γ′} -> Γ ⊨ γ ≡ γ
    sym : forall {Γ Γ′} {γ γ′ : Γ ⊨ Γ′} -> Γ ⊨ γ ≡ γ′ -> Γ ⊨ γ′ ≡ γ
    trans : forall {Γ Γ′} {γ₁ γ₂ γ₃ : Γ ⊨ Γ′}
      -> Γ ⊨ γ₁ ≡ γ₂
      -> Γ ⊨ γ₂ ≡ γ₃
      -> Γ ⊨ γ₁ ≡ γ₃

    id∙ˡ : forall {Γ Γ′} {γ : Γ ⊨ Γ′} -> Γ ⊨ id ∙ γ ≡ γ
    id∙ʳ : forall {Γ Γ′} {γ : Γ ⊨ Γ′} -> Γ ⊨ γ ∙ id ≡ γ

    assoc∙ : forall {Γ Γ′ Γ′′ Γ′′′} {γ₁ : Γ′′ ⊨ Γ′′′} {γ₂ : Γ′ ⊨ Γ′′} {γ₃ : Γ ⊨ Γ′}
      -> Γ ⊨ (γ₁ ∙ γ₂) ∙ γ₃ ≡ γ₁ ∙ (γ₂ ∙ γ₃)

    !-unique : forall {Γ} {γ : Γ ⊨ []} -> Γ ⊨ ! ≡ γ
    η-pair : forall {Γ : Context} {A : Type}
      -> A ∷ Γ ⊨ ext weaken var ≡ id
    ext∙ : forall {Γ Γ′ Γ′′} {γ : Γ′ ⊨ Γ′′} {γ′ : Γ ⊨ Γ′} {A} {e : Γ′ ⊢ A}
      -> Γ ⊨ ext γ e ∙ γ′ ≡ ext (γ ∙ γ′) (e [ γ′ ])

    weaken/ext : forall {Γ Γ′} {γ : Γ ⊨ Γ′} {A} {e : Γ ⊢ A}
      -> Γ ⊨ weaken ∙ ext γ e ≡ γ

    cong/ext : forall {Γ Γ′} {γ γ′ : Γ ⊨ Γ′} {A} {e e′ : Γ ⊢ A}
      -> Γ ⊨ γ ≡ γ′
      -> Γ ⊢ e ≡ e′
      -> Γ ⊨ ext γ e ≡ ext γ′ e′
    cong/∙ : forall {Γ Γ′ Γ′′} {γ γ′ : Γ′ ⊨ Γ′′} {δ δ′ : Γ ⊨ Γ′}
      -> Γ′ ⊨ γ ≡ γ′
      -> Γ ⊨ δ ≡ δ′
      -> Γ ⊨ γ ∙ δ ≡ γ′ ∙ δ′

  ×id′-∙ : forall {Γ Γ′ Γ′′} {γ′ : Γ ⊨ Γ′} {γ : Γ′ ⊨ Γ′′} {A}
    -> A ∷ Γ ⊨ γ ×id′ ∙ γ′ ×id′ ≡ (γ ∙ γ′) ×id′
  ×id′-∙ = trans ext∙ (cong/ext (trans assoc∙ (trans (cong/∙ refl weaken/ext) (sym assoc∙))) (var/ext _ _))

  ×id′-ext : forall {Γ Γ′ Γ′′} {γ′ : Γ ⊨ Γ′} {γ : Γ′ ⊨ Γ′′} {A} {e : Γ ⊢ A}
    -> Γ ⊨ γ ×id′ ∙ ext γ′ e ≡ ext (γ ∙ γ′) e
  ×id′-ext = trans ext∙ (cong/ext (trans assoc∙ (cong/∙ refl weaken/ext)) (var/ext _ _))

  open import Relation.Binary.Bundles

  TermSetoid : forall {Γ : Context} {A : Type} -> Setoid (ℓ₁ ⊔ ℓ₂) (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)
  TermSetoid {Γ} {A} = record
             { Carrier = Γ ⊢ A
             ; _≈_ = λ e₁ e₂ → Γ ⊢ e₁ ≡ e₂
             ;isEquivalence = record { refl = refl ; sym = sym ; trans = trans }
             }

  SubstSetoid : forall {Γ : Context} {Γ′ : Context} -> Setoid (ℓ₁ ⊔ ℓ₂) (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)
  SubstSetoid {Γ} {Γ′} = record
             { Carrier = Γ ⊨ Γ′
             ; _≈_ = λ γ₁ γ₂ → Γ ⊨ γ₁ ≡ γ₂
             ;isEquivalence = record { refl = refl ; sym = sym ; trans = trans }
             }

open import Categories.Category
open import Categories.Category.CartesianClosed

module _ {o ℓ e} (𝒞 : Category o ℓ e) (CC : CartesianClosed 𝒞) {ℓ₁ ℓ₂ ℓ₃} (Th : Theory ℓ₁ ℓ₂ ℓ₃) where
  open Category 𝒞
  open import Data.Product using (Σ-syntax)
  open Theory Th
  open import Semantics 𝒞 CC Sg

  Model : Set (o ⊔ ℓ ⊔ e ⊔ ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃)
  Model = Σ[ M ∈ Structure ] forall {Γ A e₁ e₂} -> Ax Γ A e₁ e₂ -> ⟦ e₁ ⟧ M ≈ ⟦ e₂ ⟧ M


open import Common.IO
open import Agda.Builtin.String
open import Agda.Builtin.Unit
open import Agda.Builtin.Nat
open import Agda.Builtin.Sigma
open import Agda.Builtin.Equality
open import Agda.Builtin.Bool
open import Agda.Builtin.Strict

data ∋⋆ : Set where
  Z : ∋⋆

data ⊢⋆ : Set where
  size⋆ : Nat → ⊢⋆

⋆Sub : Set
⋆Sub = ∋⋆ → ⊢⋆

data TermCon : ⊢⋆ → Set where
  integer : (s : Nat) → TermCon (size⋆ s)

data ⊢_ : ⊢⋆ → Set where
  con : ∀ {s} → TermCon (size⋆ s) → ⊢ size⋆ s

data Value : {A : ⊢⋆} → ⊢ A → Set where
  V-con : ∀ {n} → (cn : TermCon (size⋆ n)) → Value (con cn)

notErased : Nat → Bool
notErased n = primForce n λ _ → true

BUILTIN : (σ : ⋆Sub) (vtel : Σ (⊢ σ Z) Value) → Bool
BUILTIN σ vtel with σ Z
BUILTIN σ (_ , V-con (integer s)) | _ = notErased s
-- Either of these work:
-- BUILTIN σ (_ , V-con (integer s)) | size⋆ s = notErased s
-- BUILTIN σ (con (integer s) , V-con (integer s)) | _ = notErased s

con2 : ⊢ size⋆ 8
con2 = con (integer 8)

vcon2 : Value con2
vcon2 = V-con (integer 8)

builtin2plus2 : Bool
builtin2plus2 = BUILTIN (λ _ → size⋆ 8) (con2 , vcon2)

help : Bool → String
help true  = "4"
help false = "something went wrong"

check : "4" ≡ help builtin2plus2
check = refl

main : IO ⊤
main = putStrLn (help builtin2plus2)


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

open import Definition.Typed.EqualityRelation

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

open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Substitution
open import Definition.Untyped using (Con ; Term)

open import Tools.Nat
open import Tools.Product

private
  variable
    n : Nat
    Γ : Con Term n

-- Any level can be embedded into the highest level (validity variant).
maybeEmbᵛ : ∀ {l A}
            ([Γ] : ⊩ᵛ Γ)
          → Γ ⊩ᵛ⟨ l ⟩ A / [Γ]
          → Γ ⊩ᵛ⟨ ¹ ⟩ A / [Γ]
maybeEmbᵛ {l = ⁰} [Γ] [A] ⊢Δ [σ] =
  let [σA]  = proj₁ ([A] ⊢Δ [σ])
      [σA]′ = maybeEmb (proj₁ ([A] ⊢Δ [σ]))
  in  [σA]′
  ,   (λ [σ′] [σ≡σ′] → irrelevanceEq [σA] [σA]′ (proj₂ ([A] ⊢Δ [σ]) [σ′] [σ≡σ′]))
maybeEmbᵛ {l = ¹} [Γ] [A] = [A]

-- The lowest level can be embedded in any level (validity variant).
maybeEmbₛ′ : ∀ {l A}
             ([Γ] : ⊩ᵛ Γ)
           → Γ ⊩ᵛ⟨ ⁰ ⟩ A / [Γ]
           → Γ ⊩ᵛ⟨ l ⟩ A / [Γ]
maybeEmbₛ′ {l = ⁰} [Γ] [A] = [A]
maybeEmbₛ′ {l = ¹} [Γ] [A] ⊢Δ [σ] =
  let [σA]  = proj₁ ([A] ⊢Δ [σ])
      [σA]′ = maybeEmb′ (proj₁ ([A] ⊢Δ [σ]))
  in  [σA]′
  ,   (λ [σ′] [σ≡σ′] → irrelevanceEq [σA] [σA]′ (proj₂ ([A] ⊢Δ [σ]) [σ′] [σ≡σ′]))


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

module Implicits.Syntax.Type.Unification.McBride where

open import Implicits.Syntax
open import Implicits.Syntax.MetaType

open import Data.Vec hiding (_>>=_)
open import Data.Vec.Properties
open import Data.Nat as N using ()
open import Data.Nat.Properties.Simple
open import Data.Product

open import Category.Monad

open import Data.Maybe hiding (module Maybe; map)
open import Data.Maybe as Maybe using (monad; functor)
open import Level using () renaming (zero to level₀)
open RawMonad {level₀} monad using (_>>=_; return)
open import Category.Functor
open RawFunctor {level₀} functor
open import Data.Star hiding (_>>=_)

open import Data.Fin.Properties as FinProp using ()
open import Data.Fin.Substitution
open import Implicits.Substitutions
open import Implicits.Substitutions.Lemmas

private
  module M = MetaTypeMetaSubst
  module T = MetaTypeTypeSubst

thin : ∀ {n} → Fin (suc n) → Fin n → Fin (suc n)
thin zero y = suc y
thin (suc x) zero = zero
thin (suc x) (suc y) = suc (thin x y)

thick : ∀ {n} → (x y : Fin (suc n)) → Maybe (Fin n)
thick zero zero = nothing
thick zero (suc y) = just y
thick {zero} (suc ()) zero
thick {suc n} (suc x) zero = just zero
thick {zero} (suc ()) _
thick {suc n} (suc x) (suc y) = suc <$> (thick x y)

check' : ∀ {ν} → Fin (suc ν) → Type (suc ν) → Maybe (Type ν)
check' n (simpl (tvar m)) = (λ n → simpl (tvar n)) <$> (thick n m)
check' n (simpl (tc x)) = just (simpl (tc x))
check' n (simpl (a →' b)) with check' n a | check' n b
check' n (simpl (a →' b)) | just x | just y = just (simpl (x →' y))
check' n (simpl (a →' b)) | _ | nothing = nothing
check' n (simpl (a →' b)) | nothing | _ = nothing
check' n (a ⇒ b) with check' n a | check' n b
check' n (a ⇒ b) | just x | just y = just (x ⇒ y)
check' n (a ⇒ b) | _ | nothing = nothing
check' n (a ⇒ b) | nothing | _ = nothing
check' n (∀' t) with check' (suc n) t
check' n (∀' t) | just x = just (∀' x)
check' n (∀' t) | nothing = nothing

substitute : {ν m n : ℕ} → (Fin m → MetaType n ν) → MetaType m ν → MetaType n ν
substitute f a = a M./ (tabulate f)

_for_ : ∀ {n ν} → Type ν → Fin (suc n) → Fin (suc n) → MetaType n ν
_for_ t' x y with thick x y
_for_ t' x y | just y' = simpl (mvar y')
_for_ t' x y | nothing = to-meta t'

data ASub (ν : ℕ) : ℕ → ℕ → Set where
  _//_ : ∀ {m} → (t' : Type ν) → Fin (suc m) → ASub ν (suc m) m

AList : ℕ → ℕ → ℕ → Set
AList ν m n = Star (ASub ν) m n

asub-tp-weaken : ∀ {ν m n} → ASub ν m n → ASub (suc ν) m n
asub-tp-weaken (t' // x) = tp-weaken t' // x

asub-weaken : ∀ {ν m n} → ASub ν m n → ASub ν (suc m) (suc n)
asub-weaken (t' // x) = t' // (suc x)

alist-weaken : ∀ {ν m n} → AList ν m n → AList (suc ν) m n
alist-weaken s = gmap Prelude.id (λ x → asub-tp-weaken x) s

_◇_ : ∀ {l m n ν} → (Fin m → MetaType n ν) → (Fin l → MetaType m ν) → (Fin l → MetaType n ν)
f ◇ g = substitute f ∘ g

asub' : ∀ {ν m n} → (σ : AList ν m n) → Fin m → MetaType n ν
asub' ε = λ n → simpl (mvar n)
asub' (t' // x ◅ y) =  asub' y ◇ (t' for x)

asub : ∀ {ν m n} → (σ : AList ν m n) → Sub (flip MetaType ν) m n
asub s = tabulate (asub' s)

mgu : ∀ {m ν} → MetaType m ν → Type ν → Maybe (∃ (AList ν m))
mgu {ν} s t = amgu s t (ν , ε)
  where
    amgu : ∀ {ν m} (s : MetaType m ν) → (t : Type ν)→ ∃ (AList ν m) → Maybe (∃ (AList ν m))
    -- non-matching constructors
    amgu (simpl (tc x)) (_ ⇒ _) acc = nothing
    amgu (simpl (tc _)) (∀' _) x = nothing
    amgu (simpl (tc x)) (simpl (_ →' _)) acc = nothing
    amgu (simpl (tc _)) (simpl (tvar _)) x = nothing
    amgu (simpl (_ →' _)) (∀' _) x = nothing
    amgu (simpl (_ →' _)) (_ ⇒ _) x = nothing
    amgu (simpl (_ →' _)) (simpl (tc _)) acc = nothing
    amgu (simpl (_ →' _)) (simpl (tvar _)) x = nothing
    amgu (_ ⇒ _) (simpl x) acc = nothing
    amgu (_ ⇒ _) (∀' _) x = nothing
    amgu (∀' _) (_ ⇒ _) x = nothing
    amgu (∀' _) (simpl _) x = nothing
    amgu (simpl (tvar _)) (_ ⇒ _) x = nothing
    amgu (simpl (tvar _)) (∀' _) x = nothing
    amgu (simpl (tvar _)) (simpl (tc _)) x = nothing
    amgu (simpl (tvar _)) (simpl (_ →' _)) acc = nothing

    -- matching constructors
    amgu (a ⇒ b) (a' ⇒ b') acc = _>>=_ (amgu b b' acc) (amgu a a')
    amgu (simpl (a →' b)) (simpl (a' →' b')) acc = _>>=_ (amgu b b' acc) (amgu a a')
    amgu (simpl (tc x)) (simpl (tc y)) acc with x N≟ y
    amgu (simpl (tc x)) (simpl (tc y)) acc | yes p = just (, ε)
    amgu (simpl (tc x)) (simpl (tc y)) acc | no ¬p = nothing
    amgu (∀' a) (∀' b) (m , acc) = σ >>= strengthen'
      where
        σ = amgu a b (m , alist-weaken acc)
        strengthen' : ∀ {ν n} → ∃ (AList (suc ν) n) → Maybe (∃ (AList ν n))
        strengthen' (m , ε) = just (m , ε)
        strengthen' (m , t' // x ◅ acc) with check' zero t'
        strengthen' (m , t' // x ◅ acc) | just z =
          (λ { (m , u) → m , z // x ◅ u }) <$> (strengthen' (m , acc))
        strengthen' (m , t' // x ◅ acc) | nothing = nothing

    -- var-var
    amgu ((simpl (tvar x))) ((simpl (tvar y))) (m , ε) with x FinProp.≟ y
    amgu ((simpl (tvar x))) ((simpl (tvar y))) (m , ε) | yes _ = just (, ε)
    amgu ((simpl (tvar x))) ((simpl (tvar y))) (m , ε) | no _ = nothing

    -- var-rigid / rigid-var
    amgu (simpl (mvar ())) t (zero , ε)
    amgu (simpl (mvar x)) t (suc m , ε) = just (m , t // x ◅ ε)

    amgu s t (m , t' // x ◅ us) with amgu (substitute (t' for x) s) t (m , us)
    amgu s t (m , t' // x ◅ us) | just (m' , us') = just (m' , t' // x ◅ us')
    amgu s t (m , t' // x ◅ us) | nothing = nothing


-- Andreas, 2019-06-18, LAIM 2019, issue #3855:
-- Successful tests for the erasure (@0) modality.

module _ where

open import Agda.Builtin.Bool
open import Agda.Builtin.Nat
open import Agda.Builtin.Equality
open import Agda.Builtin.Coinduction

open import Common.IO

module WhereInErasedDeclaration where

  @0 n : Nat
  n = 4711

  @0 m : Nat
  m = n'
    where
    n' = n

module ErasedDeclarationInWhere where

  F : (G : @0 Set → Set) (@0 A : Set) → Set
  F G A = G B
    where
    @0 B : Set
    B = A

module FlatErasure where

  @0 resurrect-λ : (A : Set) (@0 x : A) → A
  resurrect-λ A = λ x → x

  @0 resurrect-λ-where : (A : Set) (@0 x : A) → A
  resurrect-λ-where A = λ where x → x

  @0 resurrect-app : (A : Set) (@0 x : A) → A
  resurrect-app A x = x

module ErasedEquality where

  -- Should maybe not work --without-K
  -- should definitely not work in --cubical

  cast : ∀{A B : Set} → @0 A ≡ B → A → B
  cast refl x = x

  J : ∀{A : Set} {a : A} (P : (x : A) → a ≡ x → Set) {b : A}
      (@0 eq : a ≡ b) → P a refl → P b eq
  J P refl p = p

module ParametersAreErased where

  test : (@0 A : Set) → A ≡ A
  test A = refl {x = _}           -- TODO: A instead of _

module Records where

  record R (A : Set) : Set where
    field
      el : A
    Par : Set
    Par = A    -- record module parameters are NOT erased, so, this should be accepted

  proj : (A : Set) → R A → A  -- TODO: @0 A instead of A
  proj A r = R.el {A = _} r

  MyPar : (A : Set) → R A → Set
  MyPar A = R.Par {A = A}

  record RB (b : Bool) : Set where
    bPar : Bool
    bPar = b

  myBPar : (b : Bool) → RB b → Bool
  myBPar b r = RB.bPar {b = b} r

module CoinductionWithErasure (A : Set) where

  data Stream : Set where
    cons : (x : A) (xs : ∞ Stream) → Stream

  @0 repeat : (@0 a : A) → Stream
  repeat a = cons a (♯ (repeat a))

main = putStrLn "Hello, world!"


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Irrelevance {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView

open import Tools.Embedding
open import Tools.Product
import Tools.PropositionalEquality as PE

-- Irrelevance for propositionally equal types
irrelevance′ : ∀ {A A′ Γ l}
             → A PE.≡ A′
             → Γ ⊩⟨ l ⟩ A
             → Γ ⊩⟨ l ⟩ A′
irrelevance′ PE.refl [A] = [A]

-- Irrelevance for propositionally equal types and contexts
irrelevanceΓ′ : ∀ {l A A′ Γ Γ′}
              → Γ PE.≡ Γ′
              → A PE.≡ A′
              → Γ  ⊩⟨ l ⟩ A
              → Γ′ ⊩⟨ l ⟩ A′
irrelevanceΓ′ PE.refl PE.refl [A] = [A]

mutual
  -- Irrelevance for type equality
  irrelevanceEq : ∀ {Γ A B l l′} (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A)
                → Γ ⊩⟨ l ⟩ A ≡ B / p → Γ ⊩⟨ l′ ⟩ A ≡ B / q
  irrelevanceEq p q A≡B = irrelevanceEqT (goodCasesRefl p q) A≡B

  -- Irrelevance for type equality with propositionally equal first types
  irrelevanceEq′ : ∀ {Γ A A′ B l l′} (eq : A PE.≡ A′)
                   (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                 → Γ ⊩⟨ l ⟩ A ≡ B / p → Γ ⊩⟨ l′ ⟩ A′ ≡ B / q
  irrelevanceEq′ PE.refl p q A≡B = irrelevanceEq p q A≡B

  -- Irrelevance for type equality with propositionally equal types
  irrelevanceEq″ : ∀ {Γ A A′ B B′ l l′} (eqA : A PE.≡ A′) (eqB : B PE.≡ B′)
                    (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                  → Γ ⊩⟨ l ⟩ A ≡ B / p → Γ ⊩⟨ l′ ⟩ A′ ≡ B′ / q
  irrelevanceEq″ PE.refl PE.refl p q A≡B = irrelevanceEq p q A≡B

  -- Irrelevance for type equality with propositionally equal second types
  irrelevanceEqR′ : ∀ {Γ A B B′ l} (eqB : B PE.≡ B′) (p : Γ ⊩⟨ l ⟩ A)
                  → Γ ⊩⟨ l ⟩ A ≡ B / p → Γ ⊩⟨ l ⟩ A ≡ B′ / p
  irrelevanceEqR′ PE.refl p A≡B = A≡B

  -- Irrelevance for type equality with propositionally equal types and
  -- a lifting of propositionally equal types
  irrelevanceEqLift″ : ∀ {Γ A A′ B B′ C C′ l l′}
                        (eqA : A PE.≡ A′) (eqB : B PE.≡ B′) (eqC : C PE.≡ C′)
                        (p : Γ ∙ C ⊩⟨ l ⟩ A) (q : Γ ∙ C′ ⊩⟨ l′ ⟩ A′)
                      → Γ ∙ C ⊩⟨ l ⟩ A ≡ B / p → Γ ∙ C′ ⊩⟨ l′ ⟩ A′ ≡ B′ / q
  irrelevanceEqLift″ PE.refl PE.refl PE.refl p q A≡B = irrelevanceEq p q A≡B

  -- Helper for irrelevance of type equality using shape view
  irrelevanceEqT : ∀ {Γ A B l l′} {p : Γ ⊩⟨ l ⟩ A} {q : Γ ⊩⟨ l′ ⟩ A}
                       → ShapeView Γ l l′ A A p q
                       → Γ ⊩⟨ l ⟩ A ≡ B / p → Γ ⊩⟨ l′ ⟩ A ≡ B / q
  irrelevanceEqT (ℕᵥ ℕA ℕB) (ιx (ℕ₌ x)) = ιx (ℕ₌ x)
  irrelevanceEqT (ne (ne K D neK _) (ne K₁ D₁ neK₁ K≡K₁)) (ιx (ne₌ M D′ neM K≡M))
                 rewrite whrDet* (red D , ne neK) (red D₁ , ne neK₁) =
    ιx (ne₌ M D′ neM K≡M)
  irrelevanceEqT {Γ} (Πᵥ (Πᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
                 (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
    let ΠFG≡ΠF₁G₁   = whrDet* (red D , Πₙ) (red D₁ , Πₙ)
        F≡F₁ , G≡G₁ = Π-PE-injectivity ΠFG≡ΠF₁G₁
    in  Π₌ F′ G′ D′ (PE.subst (λ x → Γ ⊢ x ≅ Π F′ ▹ G′) ΠFG≡ΠF₁G₁ A≡B)
           (λ {ρ} [ρ] ⊢Δ → irrelevanceEq′ (PE.cong (wk ρ) F≡F₁) ([F] [ρ] ⊢Δ)
                                    ([F]₁ [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ))
           (λ {ρ} [ρ] ⊢Δ [a]₁ →
              let [a] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                         ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [a]₁
              in  irrelevanceEq′ (PE.cong (λ y → wk (lift ρ) y [ _ ]) G≡G₁)
                                 ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁) ([G≡G′] [ρ] ⊢Δ [a]))
  irrelevanceEqT (Uᵥ _ _ _ _ _ _) (U₌ B≡U) = U₌ B≡U
  irrelevanceEqT (emb⁰¹ PE.refl x) (ιx A≡B) = irrelevanceEqT x A≡B
  irrelevanceEqT {Γ} {A} {B} {l} {.¹} (emb¹⁰ PE.refl x) A≡B = ιx (irrelevanceEqT x A≡B)

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

  -- Irrelevance for terms
  irrelevanceTerm : ∀ {Γ A t l l′} (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A)
                  → Γ ⊩⟨ l ⟩ t ∷ A / p → Γ ⊩⟨ l′ ⟩ t ∷ A / q
  irrelevanceTerm p q t = irrelevanceTermT (goodCasesRefl p q) t

  -- Irrelevance for terms with propositionally equal types
  irrelevanceTerm′ : ∀ {Γ A A′ t l l′} (eq : A PE.≡ A′)
                     (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                   → Γ ⊩⟨ l ⟩ t ∷ A / p → Γ ⊩⟨ l′ ⟩ t ∷ A′ / q
  irrelevanceTerm′ PE.refl p q t = irrelevanceTerm p q t

  -- Irrelevance for terms with propositionally equal types and terms
  irrelevanceTerm″ : ∀ {Γ A A′ t t′ l l′}
                      (eqA : A PE.≡ A′) (eqt : t PE.≡ t′)
                      (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                    → Γ ⊩⟨ l ⟩ t ∷ A / p → Γ ⊩⟨ l′ ⟩ t′ ∷ A′ / q
  irrelevanceTerm″ PE.refl PE.refl p q t = irrelevanceTerm p q t

  -- Irrelevance for terms with propositionally equal types, terms and contexts
  irrelevanceTermΓ″ : ∀ {l l′ A A′ t t′ Γ Γ′}
                     → Γ PE.≡ Γ′
                     → A PE.≡ A′
                     → t PE.≡ t′
                     → ([A]  : Γ  ⊩⟨ l  ⟩ A)
                       ([A′] : Γ′ ⊩⟨ l′ ⟩ A′)
                     → Γ  ⊩⟨ l  ⟩ t ∷ A  / [A]
                     → Γ′ ⊩⟨ l′ ⟩ t′ ∷ A′ / [A′]
  irrelevanceTermΓ″ PE.refl PE.refl PE.refl [A] [A′] [t] = irrelevanceTerm [A] [A′] [t]

  -- Helper for irrelevance of terms using shape view
  irrelevanceTermT : ∀ {Γ A t l l′} {p : Γ ⊩⟨ l ⟩ A} {q : Γ ⊩⟨ l′ ⟩ A}
                         → ShapeView Γ l l′ A A p q
                         → Γ ⊩⟨ l ⟩ t ∷ A / p → Γ ⊩⟨ l′ ⟩ t ∷ A / q
  irrelevanceTermT (ℕᵥ D D′) (ιx (ℕₜ n d n≡n prop)) = ιx (ℕₜ n d n≡n prop)
  irrelevanceTermT (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ιx (neₜ k d nf))
                   with whrDet* (red D₁ , ne neK₁) (red D , ne neK)
  irrelevanceTermT (ne (ne K D neK K≡K) (ne .K D₁ neK₁ K≡K₁)) (ιx (neₜ k d nf))
    | PE.refl = ιx (neₜ k d nf)
  irrelevanceTermT {Γ} {t = t} (Πᵥ (Πᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                                   (Πᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
                   (Πₜ f d funcF f≡f [f] [f]₁) =
    let ΠFG≡ΠF₁G₁   = whrDet* (red D , Πₙ) (red D₁ , Πₙ)
        F≡F₁ , G≡G₁ = Π-PE-injectivity ΠFG≡ΠF₁G₁
    in  Πₜ f (PE.subst (λ x → Γ ⊢ t :⇒*: f ∷ x) ΠFG≡ΠF₁G₁ d) funcF
           (PE.subst (λ x → Γ ⊢ f ≅ f ∷ x) ΠFG≡ΠF₁G₁ f≡f)
           (λ {ρ} [ρ] ⊢Δ [a]₁ [b]₁ [a≡b]₁ →
              let [a]   = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                           ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [a]₁
                  [b]   = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                           ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [b]₁
                  [a≡b] = irrelevanceEqTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                             ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [a≡b]₁
              in  irrelevanceEqTerm′ (PE.cong (λ G → wk (lift ρ) G [ _ ]) G≡G₁)
                                     ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁)
                                     ([f] [ρ] ⊢Δ [a] [b] [a≡b]))
          (λ {ρ} [ρ] ⊢Δ [a]₁ →
             let [a] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                        ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [a]₁
             in  irrelevanceTerm′ (PE.cong (λ G → wk (lift ρ) G [ _ ]) G≡G₁)
                                  ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁) ([f]₁ [ρ] ⊢Δ [a]))
  irrelevanceTermT (Uᵥ .⁰ 0<1 ⊢Γ .⁰ 0<1 _) t = t
  irrelevanceTermT (emb⁰¹ PE.refl x) (ιx t) = irrelevanceTermT x t
  irrelevanceTermT (emb¹⁰ PE.refl x) t = ιx (irrelevanceTermT x t)

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

  -- Irrelevance for term equality
  irrelevanceEqTerm : ∀ {Γ A t u l l′} (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A)
                    → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / p → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ A / q
  irrelevanceEqTerm p q t≡u = irrelevanceEqTermT (goodCasesRefl p q) t≡u

  -- Irrelevance for term equality with propositionally equal types
  irrelevanceEqTerm′ : ∀ {Γ A A′ t u l l′} (eq : A PE.≡ A′)
                       (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                     → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / p → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ A′ / q
  irrelevanceEqTerm′ PE.refl p q t≡u = irrelevanceEqTerm p q t≡u

  -- Irrelevance for term equality with propositionally equal types and terms
  irrelevanceEqTerm″ : ∀ {Γ A A′ t t′ u u′ l l′}
                        (eqt : t PE.≡ t′) (equ : u PE.≡ u′) (eqA : A PE.≡ A′)
                        (p : Γ ⊩⟨ l ⟩ A) (q : Γ ⊩⟨ l′ ⟩ A′)
                      → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / p → Γ ⊩⟨ l′ ⟩ t′ ≡ u′ ∷ A′ / q
  irrelevanceEqTerm″ PE.refl PE.refl PE.refl p q t≡u = irrelevanceEqTerm p q t≡u

  -- Helper for irrelevance of term equality using shape view
  irrelevanceEqTermT : ∀ {Γ A t u} {l l′} {p : Γ ⊩⟨ l ⟩ A} {q : Γ ⊩⟨ l′ ⟩ A}
                           → ShapeView Γ l l′ A A p q
                           → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / p → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ A / q
  irrelevanceEqTermT (ℕᵥ D D′) (ιx (ℕₜ₌ k k′ d d′ k≡k′ prop)) = ιx (ℕₜ₌ k k′ d d′ k≡k′ prop)
  irrelevanceEqTermT (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ιx (neₜ₌ k m d d′ nf))
                     with whrDet* (red D₁ , ne neK₁) (red D , ne neK)
  irrelevanceEqTermT (ne (ne K D neK K≡K) (ne .K D₁ neK₁ K≡K₁)) (ιx (neₜ₌ k m d d′ nf))
    | PE.refl = ιx (neₜ₌ k m d d′ nf)
  irrelevanceEqTermT {Γ} {t = t} {u = u}
                     (Πᵥ (Πᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
                     (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g]) =
    let ΠFG≡ΠF₁G₁   = whrDet* (red D , Πₙ) (red D₁ , Πₙ)
        F≡F₁ , G≡G₁ = Π-PE-injectivity ΠFG≡ΠF₁G₁
        [A]         = Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext
        [A]₁        = Πᵣ′ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁
    in  Πₜ₌ f g (PE.subst (λ x → Γ ⊢ t :⇒*: f ∷ x) ΠFG≡ΠF₁G₁ d)
            (PE.subst (λ x → Γ ⊢ u :⇒*: g ∷ x) ΠFG≡ΠF₁G₁ d′) funcF funcG
            (PE.subst (λ x → Γ ⊢ f ≅ g ∷ x) ΠFG≡ΠF₁G₁ f≡g)
            (irrelevanceTerm [A] [A]₁ [f]) (irrelevanceTerm [A] [A]₁ [g])
            (λ {ρ} [ρ] ⊢Δ [a]₁ →
               let [a] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F≡F₁))
                                          ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) [a]₁
               in  irrelevanceEqTerm′ (PE.cong (λ G → wk (lift ρ) G [ _ ]) G≡G₁)
                                     ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁) ([f≡g] [ρ] ⊢Δ [a]))
  irrelevanceEqTermT (Uᵥ .⁰ 0<1 ⊢Γ .⁰ 0<1 ⊢Γ₁) t≡u = t≡u
  irrelevanceEqTermT (emb⁰¹ PE.refl x) (ιx t≡u) = irrelevanceEqTermT x t≡u
  irrelevanceEqTermT (emb¹⁰ PE.refl x) t≡u = ιx (irrelevanceEqTermT x t≡u)


--
-- In this module we conclude our proof: we have shown that t, derive-dterm t
-- and produce related values (in the sense of the fundamental property,
-- rfundamental3), and for related values v1, dv and v2 we have v1 ⊕ dv ≡
-- v2 (because ⊕ agrees with validity, rrelV3→⊕).
--
-- We now put these facts together via derive-correct-si and derive-correct.
-- This is immediate, even though I spend so much code on it:
-- Indeed, all theorems are immediate corollaries of what we established (as
-- explained above); only the statements are longer, especially because I bother
-- expanding them.

module Thesis.SIRelBigStep.DeriveCorrect where

open import Data.Product
open import Relation.Binary.PropositionalEquality

open import Thesis.SIRelBigStep.IlcSILR
open import Thesis.SIRelBigStep.FundamentalProperty

-- Theorem statement. This theorem still mentions step-indexes explicitly.
derive-correct-si-type =
  ∀ {τ Γ k} (t : Term Γ τ) ρ1 dρ ρ2 → (ρρ : rrelρ3 Γ ρ1 dρ ρ2 k) →
  rrelT3-skeleton (λ v1 dv v2 _ → v1 ⊕ dv ≡ v2) t (derive-dterm t) t ρ1 dρ ρ2 k

-- A verified expansion of the theorem statement.
derive-correct-si-type-means :
  derive-correct-si-type ≡
  ∀ {τ Γ k} (t : Term Γ τ)
  ρ1 dρ ρ2 (ρρ : rrelρ3 Γ ρ1 dρ ρ2 k) →
  (v1 v2 : Val τ) →
  ∀ j (j<k : j < k) →
  (ρ1⊢t1↓[j]v1 : ρ1 ⊢ t ↓[ i' j ] v1) →
  (ρ2⊢t2↓[n2]v2 : ρ2 ⊢ t ↓[ no ] v2) →
  Σ[ dv ∈ DVal τ ]
  ρ1 D dρ ⊢ derive-dterm t ↓ dv ×
  v1 ⊕ dv ≡ v2
derive-correct-si-type-means = refl

derive-correct-si : derive-correct-si-type
derive-correct-si t ρ1 dρ ρ2 ρρ = rrelT3→⊕ t (derive-dterm t) t ρ1 dρ ρ2 (rfundamental3 _ t ρ1 dρ ρ2 ρρ)

-- Infinitely related environments:
rrelρ3-inf : ∀ Γ (ρ1 : ⟦ Γ ⟧Context) (dρ : ChΔ Γ) (ρ2 : ⟦ Γ ⟧Context) → Set
rrelρ3-inf Γ ρ1 dρ ρ2 = ∀ k → rrelρ3 Γ ρ1 dρ ρ2 k

-- A theorem statement for infinitely-related environments. On paper, if we informally
-- omit the arbitrary derivation step-index as usual, we get a statement without
-- step-indexes.
derive-correct :
  ∀ {τ Γ} (t : Term Γ τ)
  ρ1 dρ ρ2 (ρρ : rrelρ3-inf Γ ρ1 dρ ρ2) →
  (v1 v2 : Val τ) →
  ∀ j →
  (ρ1⊢t1↓[j]v1 : ρ1 ⊢ t ↓[ i' j ] v1) →
  (ρ2⊢t2↓[n2]v2 : ρ2 ⊢ t ↓[ no ] v2) →
  Σ[ dv ∈ DVal τ ]
  ρ1 D dρ ⊢ derive-dterm t ↓ dv ×
  v1 ⊕ dv ≡ v2
derive-correct t ρ1 dρ ρ2 ρρ v1 v2 j = derive-correct-si t ρ1 dρ ρ2 (ρρ (suc j)) v1 v2 j ≤-refl

open import Data.Unit

nilρ : ∀ {Γ n} (ρ : ⟦ Γ ⟧Context) → Σ[ dρ ∈ ChΔ Γ ] rrelρ3 Γ ρ dρ ρ n
nilV : ∀ {τ n} (v : Val τ) → Σ[ dv ∈ DVal τ ] rrelV3 τ v dv v n
nilV (closure t ρ) = let dρ , ρρ = nilρ ρ in dclosure (derive-dterm t) ρ dρ , rfundamental3svv _ (abs t) ρ dρ ρ ρρ
nilV (natV n₁) = dnatV zero , refl
nilV (pairV a b) = let 0a , aa = nilV a; 0b , bb = nilV b in dpairV 0a 0b , aa , bb

nilρ ∅ = ∅ , tt
nilρ (v • ρ) = let dv , vv = nilV v ; dρ , ρρ = nilρ ρ in dv • dρ , vv , ρρ

-- -- Try to prove to define validity-preserving change composition.
-- -- If we don't store proofs that environment changes in closures are valid, we
-- -- can't finish the proof for the closure case using the Fundamental property.
-- -- But it seems that's not the approach we need to use: we might need
-- -- environment validity elsewhere.
-- -- Moreover, the proof is annoying to do unless we build a datatype to invert
-- -- validity proofs (as suggested by Ezyang in
-- -- http://blog.ezyang.com/2013/09/induction-and-logical-relations/).

-- ocomposeρ : ∀ {Γ} n ρ1 dρ1 ρ2 dρ2 ρ3 → rrelρ3 Γ ρ1 dρ1 ρ2 n → rrelρ3 Γ ρ2 dρ2 ρ3 n → Σ[ dρ ∈ ChΔ Γ ] rrelρ3 Γ ρ1 dρ ρ3 n

-- ocomposev : ∀ {τ} n v1 dv1 v2 dv2 v3 → rrelV3 τ v1 dv1 v2 n → rrelV3 τ v2 dv2 v3 n → Σ[ dv ∈ DVal τ ] rrelV3 τ v1 dv v3 n
-- ocomposev n v1 dv1 v2 (bang v3) .v3 vv1 refl = bang v3 , refl
-- ocomposev n v1 (bang x) v2 (dclosure dt ρ dρ) v3 vv1 vv2 = {!!}
-- ocomposev n (closure t ρ) (dclosure .(derive-dterm t) .ρ dρ1) (closure .t .(ρ ⊕ρ dρ1)) (dclosure .(derive-dterm t) .(ρ ⊕ρ dρ1) dρ2) (closure .t .((ρ ⊕ρ dρ1) ⊕ρ dρ2)) ((refl , refl) , refl , refl , refl , refl , vv1) ((refl , refl) , refl , refl , refl , refl , vv2) =
--   let dρ , ρρ = ocomposeρ n ρ dρ1 (ρ ⊕ρ dρ1) dρ2 ((ρ ⊕ρ dρ1) ⊕ρ dρ2) {!!} {!!}
--   in dclosure (derive-dterm t) ρ dρ , (refl , refl) , refl , {!!} , refl , refl ,
--     λ { k (s≤s k≤n) v1 dv v2 vv →
--       let nilv2 , nilv2v = nilV v2
--       -- vv1 k ? v1 dv v2 vv
--       -- vv2 k ? v2 nilv2 v2 nilv2v
--       in {!!}
--       }
--       -- rfundamental3 k t (v1 • ρ) (dv • dρ) (v2 • ((ρ ⊕ρ dρ1) ⊕ρ dρ2)) (vv , rrelρ3-mono k n (≤-step k<n) _ ρ dρ ((ρ ⊕ρ dρ1) ⊕ρ dρ2) ρρ)}
-- -- p1 , p2 , p3 , p4 , p5
-- ocomposev n v1 dv1 v2 (dnatV n₁) v3 vv1 vv2 = {!!}
-- ocomposev n v1 dv1 v2 (dpairV dv2 dv3) v3 vv1 vv2 = {!!}
-- -- ocomposev n v1 (bang v2) .v2 dv2 v3 refl vv2 = bang (v2 ⊕ dv2) , rrelV3→⊕ v2 dv2 v3 vv2
-- -- ocomposev n v1 (dclosure dt ρ dρ) v2 dv2 v3 vv1 vv2 = {!!}
-- -- ocomposev n v1 (dnatV n₁) v2 dv2 v3 vv1 vv2 = {!!}
-- -- ocomposev n v1 (dpairV dv1 dv2) v2 dv3 v3 vv1 vv2 = {!!}
-- -- ocomposeV : ∀ {τ n} (v : Val τ) → Σ[ dv ∈ DVal τ ] rrelV3 τ v dv v n

-- ocomposeρ {∅} n ∅ dρ1 ρ2 dρ2 ∅ ρρ1 ρρ2 = ∅ , tt
-- ocomposeρ {τ • Γ} n (v1 • ρ1) (dv1 • dρ1) (v2 • ρ2) (dv2 • dρ2) (v3 • ρ3) (vv1 , ρρ1) (vv2 , ρρ2) =
--   let dv , vv = ocomposev n v1 dv1 v2 dv2 v3 vv1 vv2
--       dρ , ρρ = ocomposeρ n ρ1 dρ1 ρ2 dρ2 ρ3 ρρ1 ρρ2
--   in  dv • dρ , vv , ρρ

-- -- But we sort of know how to store environments validity proofs in closures, you just need to use well-founded inductions, even if you're using types. Sad, yes, that's insanely tedious. Let's leave that to Coq.
-- -- What is also interesting: if that were fixed, could we prove correct
-- -- composition for change *terms* and rrelT3? And for *open expressions*? The
-- -- last is the main problem Ahmed run into.

-- 1: the proof of transitivity of rrelT by Ahmed is for the case where the second rrelT holds at arbitrary step counts :-)
-- 2: that proof doesn't go through for us. If de1 : e1 -> e2 and de2 : e2 ->
-- e3, we must show that if e1 and e3 evaluate something happens, but to use our
-- hypothesis we need that e2 also terminates, which in our case does not
-- follow. For Ahmed 2006, instead, e1 terminates and e1 ≤ e2 implies that e2
-- terminates.
--
-- Indeed, it seems that we can have a change from e1 to looping e2 and a change
-- from looping e2 to e3, and I don't expect the composition of such changes to
-- satisfy anything.
--
-- Indeed, "Imperative self-adjusting computation" does not mention any
-- transitivity result (even in the technical report).


module AsP where

f
  : {A : Set}
  → A
  → A
  → A
f x@y z@w
  = x

g
  : {A : Set}
  → A
  → A
  → A
g x@y z'@w'
  with y
... | _
  = x


------------------------------------------------------------------------
-- Propierties of abstract typing contexts
------------------------------------------------------------------------

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

module Data.Context.Properties where

open import Data.Fin using (Fin; zero; suc; lift; raise)
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Product using (_×_; _,_)
open import Data.Vec as Vec using (Vec; []; _∷_)
import Data.Vec.Properties as VecProps
open import Function as Fun using (_∘_; flip)
open import Relation.Binary.PropositionalEquality hiding (subst-∘)
open ≡-Reasoning
open import Relation.Unary using (Pred)

open import Data.Context
open import Data.Context.WellFormed

------------------------------------------------------------------------
-- Properties of abstract contexts and context extensions.

-- Properties of the `map' functions.

module _ {ℓ₁ ℓ₂} {T₁ : Pred ℕ ℓ₁} {T₂ : Pred ℕ ℓ₂} where

  -- pointwise equality is a congruence w.r.t. map and mapExt.

  map-cong : ∀ {n} {f g : ∀ {k} → T₁ k → T₂ k} → (∀ {k} → f {k} ≗ g {k}) →
             _≗_ {A = Ctx T₁ n} (map f) (map g)
  map-cong f≗g []        = refl
  map-cong f≗g (_∷_ t Γ) = cong₂ _∷_ (f≗g t) (map-cong f≗g Γ)

  mapExt-cong : ∀ {k m n} {f g : ∀ i → T₁ (i + m) → T₂ (i + n)} →
                (∀ {i} → f i ≗ g i) →
                _≗_ {A = CtxExt T₁ m k} (mapExt {T₂ = T₂} f) (mapExt g)
  mapExt-cong f≗g []            = refl
  mapExt-cong f≗g (_∷_ {l} t Γ) = cong₂ _∷_ (f≗g {l} t) (mapExt-cong f≗g Γ)

module _ {ℓ} {T : Pred ℕ ℓ} where

  -- map and mapExt are functorial.

  map-id : ∀ {n} → _≗_ {A = Ctx T n} (map Fun.id) Fun.id
  map-id []      = refl
  map-id (t ∷ Γ) = cong (t ∷_) (map-id Γ)

  mapExt-id : ∀ {m n} → _≗_ {A = CtxExt T m n} (mapExt λ _ t → t) Fun.id
  mapExt-id []      = refl
  mapExt-id (t ∷ Γ) = cong (t ∷_) (mapExt-id Γ)

module _ {ℓ₁ ℓ₂ ℓ₃} {T₁ : Pred ℕ ℓ₁} {T₂ : Pred ℕ ℓ₂} {T₃ : Pred ℕ ℓ₃} where

  map-∘ : ∀ {n} (f : ∀ {k} → T₂ k → T₃ k) (g : ∀ {k} → T₁ k → T₂ k)
          (Γ : Ctx T₁ n) → map {T₂ = T₃} (f ∘ g) Γ ≡ map {T₁ = T₂} f (map g Γ)
  map-∘ f g []      = refl
  map-∘ f g (t ∷ Γ) = cong (_ ∷_) (map-∘ f g Γ)

  mapExt-∘ : ∀ {k l m n}
             (f : ∀ i → T₂ (i + m) → T₃ (i + n))
             (g : ∀ i → T₁ (i + l) → T₂ (i + m)) →
             (Γ : CtxExt T₁ l k) →
             mapExt {T₂ = T₃} (λ i t → f i (g i t)) Γ ≡
               mapExt {T₁ = T₂} f (mapExt g Γ)
  mapExt-∘ f g []      = refl
  mapExt-∘ f g (t ∷ Γ) = cong (_ ∷_) (mapExt-∘ f g Γ)

-- Lemmas about operations on contexts that require weakening of
-- types.

module WeakenOpsLemmas {ℓ} {T : Pred ℕ ℓ} (extension : Extension T) where

  -- The underlyig operations.
  open WeakenOps extension

  -- Conversion to vector representation commutes with
  -- concatenation.

  toVec-++ : ∀ {m n} (Δ : CtxExt T m n) (Γ : Ctx T m) →
             toVec (Δ ++ Γ) ≡ extToVec Δ (toVec Γ)
  toVec-++ []      Γ = refl
  toVec-++ (t ∷ Δ) Γ =
    cong ((_ ∷_) ∘ Vec.map weaken) (toVec-++ Δ Γ)

  -- Lookup commutes with concatenation.

  lookup-++ : ∀ {m n} (Δ : CtxExt T m n) (Γ : Ctx T m) x →
              lookup (Δ ++ Γ) x ≡ extLookup Δ (toVec Γ) x
  lookup-++ Δ Γ x = cong (flip Vec.lookup x) (toVec-++ Δ Γ)

  -- We can skip the first element when looking up others.

  lookup-suc : ∀ {n} t (Γ : Ctx T n) x →
               lookup (t ∷ Γ) (suc x) ≡ weaken (lookup Γ x)
  lookup-suc t Γ x = VecProps.lookup-map x weaken (toVec Γ)

  extLookup-suc : ∀ {k m n} t (Γ : CtxExt T m n) (ts : Vec (T m) k) x →
                  extLookup (t ∷ Γ) ts (suc x) ≡ weaken (extLookup Γ ts x)
  extLookup-suc t Γ ts x = VecProps.lookup-map x weaken (extToVec Γ ts)

  -- We can skip a spliced-in element when looking up others.

  lookup-lift : ∀ {k m n} (Γ : CtxExt T m n) t (ts : Vec (T m) k) x →
                extLookup Γ ts x ≡ extLookup Γ (t ∷ ts) (lift n suc x)
  lookup-lift             []      t ts x       = refl
  lookup-lift             (u ∷ Δ) t ts zero    = refl
  lookup-lift {n = suc n} (u ∷ Δ) t ts (suc x) = begin
      extLookup (u ∷ Δ) ts (suc x)
    ≡⟨ extLookup-suc u Δ ts x ⟩
      weaken (extLookup Δ ts x)
    ≡⟨ cong weaken (lookup-lift Δ t ts x) ⟩
      weaken (extLookup Δ (t ∷ ts) (lift n suc x))
    ≡⟨ sym (extLookup-suc u Δ (t ∷ ts) (lift n suc x)) ⟩
      extLookup (u ∷ Δ) (t ∷ ts) (suc (lift n suc x))
    ∎

  -- Lookup in the prefix of a concatenation results in weakening.

  lookup-weaken⋆ : ∀ {m} n (Δ : CtxExt T m n) (Γ : Ctx T m) x →
                   lookup (Δ ++ Γ) (raise n x) ≡ weaken⋆ n (lookup Γ x)
  lookup-weaken⋆ zero    []      Γ x = refl
  lookup-weaken⋆ (suc n) (t ∷ Δ) Γ x = begin
      lookup (t ∷ Δ ++ Γ) (suc (raise n x))
    ≡⟨ VecProps.lookup-map (raise n x) weaken (toVec (Δ ++ Γ)) ⟩
      weaken (lookup (Δ ++ Γ) (raise n x))
    ≡⟨ cong weaken (lookup-weaken⋆ n Δ Γ x) ⟩
      weaken (weaken⋆ n (lookup Γ x))
    ∎

-- Lemmas relating conversions of context extensions to vector
-- representation with conversions of the underling entries.

module ConversionLemmas {T₁ T₂ : ℕ → Set}
                        (extension₁ : Extension T₁)
                        (extension₂ : Extension T₂) where
  private
    module W₁ = WeakenOps extension₁
    module W₂ = WeakenOps extension₂

  toVec-map : ∀ {n} (f : ∀ {k} → T₁ k → T₂ k) (Γ : Ctx T₁ n) →
              (∀ {k} (t : T₁ k) → W₂.weaken (f t) ≡ f (W₁.weaken t)) →
              W₂.toVec (map f Γ) ≡ Vec.map f (W₁.toVec Γ)
  toVec-map f []        _   = refl
  toVec-map f (_∷_ t Γ) hyp = cong₂ _∷_ (hyp t) (begin
      Vec.map W₂.weaken (W₂.toVec (map f Γ))
    ≡⟨ cong (Vec.map W₂.weaken) (toVec-map f Γ hyp) ⟩
      (Vec.map W₂.weaken (Vec.map f (W₁.toVec Γ)))
    ≡⟨ sym (VecProps.map-∘ W₂.weaken f (W₁.toVec Γ)) ⟩
      (Vec.map (W₂.weaken ∘ f) (W₁.toVec Γ))
    ≡⟨ VecProps.map-cong hyp (W₁.toVec Γ) ⟩
      (Vec.map (f ∘ W₁.weaken) (W₁.toVec Γ))
    ≡⟨ VecProps.map-∘ f W₁.weaken (W₁.toVec Γ) ⟩
      (Vec.map f (Vec.map W₁.weaken (W₁.toVec Γ)))
    ∎)

  -- Lookup commutes with re-indexing, provided that the reindexing
  -- function commutes with weakening.

  lookup-map : ∀ {n} (f : ∀ {k} → T₁ k → T₂ k) (Γ : Ctx T₁ n) x →
               (∀ {k} (t : T₁ k) → W₂.weaken (f t) ≡ f (W₁.weaken t)) →
               W₂.lookup (map f Γ) x ≡ f (W₁.lookup Γ x)
  lookup-map f Γ x hyp = begin
      W₂.lookup (map f Γ) x
    ≡⟨ cong (flip Vec.lookup x) (toVec-map f Γ hyp) ⟩
      Vec.lookup (Vec.map f (W₁.toVec Γ)) x
    ≡⟨ VecProps.lookup-map x f (W₁.toVec Γ) ⟩
      f (W₁.lookup Γ x)
    ∎

-- Lemmas about well-formed contexts and context extensions.

module ContextFormationLemmas {t ℓ} {T : Pred ℕ t}
                              (_⊢_wf : Wf T T ℓ) where
  open ContextFormation _⊢_wf

  -- Concatenation preserves well-formedness of contexts.

  wf-++-wfExt : ∀ {m n} {Δ : CtxExt T m n} {Γ : Ctx T m} →
                Γ ⊢ Δ wfExt → Γ wf → Δ ++ Γ wf
  wf-++-wfExt []               Γ-wf = Γ-wf
  wf-++-wfExt (t-wf ∷ Δ-wfExt) Γ-wf = t-wf ∷ wf-++-wfExt Δ-wfExt Γ-wf

  -- Splitting of well-formed contexts.
  wf-split : ∀ {m n} {Δ : CtxExt T m n} {Γ : Ctx T m} →
             Δ ++ Γ wf → Γ ⊢ Δ wfExt × Γ wf
  wf-split {Δ = []}    Γ-wf             = [] , Γ-wf
  wf-split {Δ = t ∷ Δ} (t-wf ∷ Δ++Γ-wf) =
    let Δ-wfExt , Γ-wf = wf-split Δ++Γ-wf
    in t-wf ∷ Δ-wfExt , Γ-wf


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

--open import HoTT
open import homotopy.3x3.PushoutPushout
open import homotopy.3x3.Transpose
open import homotopy.3x3.Common

module homotopy.3x3.From {i} (d : Span^2 {i}) where

open Span^2 d
open M d hiding (Pushout^2)
open M (transpose d) using () renaming (A₀∙ to A∙₀; A₂∙ to A∙₂; A₄∙ to A∙₄;
                                        module F₁∙ to F∙₁; f₁∙ to f∙₁;
                                        module F₃∙ to F∙₃; f₃∙ to f∙₃;
                                        v-h-span to h-v-span)
open M using (Pushout^2)

module I∙₀ = PushoutRec {D = Pushout^2 d}
                        (left ∘ left) (right ∘ left) (glue ∘ left)

i∙₀ : A∙₀ → Pushout^2 d
i∙₀ = I∙₀.f

module I∙₄ = PushoutRec {D = Pushout^2 d}
                        (left ∘ right) (right ∘ right) (glue ∘ right)

i∙₄ : A∙₄ → Pushout^2 d
i∙₄ = I∙₄.f

module E∙₂Red (c : A₂₂) where

  lhs-o : _
  lhs-o =
      ap (i∙₄ ∘ f∙₃) (glue c)
         =⟨ ap-∘ i∙₄ f∙₃ (glue c) ⟩
      ap i∙₄ (ap f∙₃ (glue c))
           =⟨ F∙₃.glue-β c |in-ctx (ap i∙₄) ⟩
      ap i∙₄ (ap left (H₁₃ c) ∙ glue (f₂₃ c) ∙ ap right (! (H₃₃ c)))
           =⟨ ap-∙∙!`∘`∘ i∙₄ left right (H₁₃ c) (glue (f₂₃ c)) (H₃₃ c) ⟩
      ap (left ∘ right) (H₁₃ c) ∙ ap i∙₄ (glue (f₂₃ c)) ∙ ! (ap (right ∘ right) (H₃₃ c))
           =⟨ I∙₄.glue-β (f₂₃ c) |in-ctx (λ u → ap (left ∘ right) (H₁₃ c) ∙ u ∙ ! (ap (right ∘ right) (H₃₃ c))) ⟩
      ap (left ∘ right) (H₁₃ c) ∙ glue (right (f₂₃ c)) ∙ ! (ap (right ∘ right) (H₃₃ c)) ∎

  rhs-o : _
  rhs-o =
      ! (ap (left ∘ left) (H₁₁ c)) ∙ glue (left (f₂₁ c)) ∙ ap (right ∘ left) (H₃₁ c)
           =⟨ ! (I∙₀.glue-β (f₂₁ c)) |in-ctx (λ u → (! (ap (left ∘ left) (H₁₁ c)) ∙ u ∙ ap (right ∘ left) (H₃₁ c))) ⟩
      ! (ap (left ∘ left) (H₁₁ c)) ∙ ap i∙₀ (glue (f₂₁ c)) ∙ ap (right ∘ left) (H₃₁ c)
           =⟨ ! (ap-!∙∙`∘`∘ i∙₀ left right (H₁₁ c) (glue (f₂₁ c)) (H₃₁ c)) ⟩
      ap i∙₀ (ap left (! (H₁₁ c)) ∙ glue (f₂₁ c) ∙ ap right (H₃₁ c))
           =⟨ ! (F∙₁.glue-β c) |in-ctx (λ u → ap i∙₀ u) ⟩
      ap i∙₀ (ap f∙₁ (glue c))
           =⟨ ∘-ap i∙₀ f∙₁ (glue c) ⟩
      ap (i∙₀ ∘ f∙₁) (glue c) ∎

  T-lhs : Type _
  T-lhs = right (left (f₃₀ (f₂₁ c))) == right (right (f₃₄ (f₂₃ c))) :> Pushout^2 d

  lhs-i : _ == _ :> T-lhs
  lhs-i =
      ap (right ∘ left) (H₃₁ c) ∙ ap right (glue (f₃₂ c)) ∙ ap (right ∘ right) (H₃₃ c)
        =⟨ ! (ap-∙∙`∘`∘ right left right (H₃₁ c) (glue (f₃₂ c)) (H₃₃ c)) ⟩
      ap right (ap left (H₃₁ c) ∙ glue (f₃₂ c) ∙ ap right (H₃₃ c))
        =⟨ ! (ap (ap right) (F₃∙.glue-β c)) ⟩
      ap right (ap f₃∙ (glue c))
        =⟨ ∘-ap right f₃∙ (glue c) ⟩
      ap (right ∘ f₃∙) (glue c) ∎

  T-rhs : Type _
  T-rhs = left (left (f₁₀ (f₂₁ c))) == left (right (f₁₄ (f₂₃ c))) :> Pushout^2 d

  rhs-i : _ == _ :> T-rhs
  rhs-i =
      ap (left ∘ f₁∙) (glue c)
        =⟨ ap-∘ left f₁∙ (glue c) ⟩
      ap left (ap f₁∙ (glue c))
        =⟨ F₁∙.glue-β c |in-ctx (ap left) ⟩
      ap left (ap left (H₁₁ c) ∙ glue (f₁₂ c) ∙ ap right (H₁₃ c))
        =⟨ ap-∙∙`∘`∘ left left right (H₁₁ c) (glue (f₁₂ c)) (H₁₃ c) ⟩
      (ap (left ∘ left) (H₁₁ c) ∙ ap left (glue (f₁₂ c)) ∙ ap (left ∘ right) (H₁₃ c)) ∎

  coh! : (p : (_ , _ =□ _ , _)) → _
  coh! = pp-coh! {A = Pushout^2 d}
               {u = glue (left (f₂₁ c))}
               {ap (right ∘ left) (H₃₁ c)}
               {ap right (glue (f₃₂ c))}
               {ap (right ∘ right) (H₃₃ c)}
               {ap (left ∘ left) (H₁₁ c)}
               {ap left (glue (f₁₂ c))}
               {ap (left ∘ right) (H₁₃ c)}
               {glue (right (f₂₃ c))}

  coh : (p : (_ , _ =□ _ , _)) → _
  coh = pp-coh {A = Pushout^2 d}
               {p = ap left (glue (f₁₂ c))}
               {ap (left ∘ right) (H₁₃ c)}
               {glue (right (f₂₃ c))}
               {ap (right ∘ right) (H₃₃ c)}
               {ap (left ∘ left) (H₁₁ c)}
               {glue (left (f₂₁ c))}
               {ap (right ∘ left) (H₃₁ c)}
               {ap right (glue (f₃₂ c))}

  module _ (to : Pushout^2 d → Pushout^2 (transpose d)) where

    ap-ap-coh! : (p : _ , _ =□ _ , _) → _
    ap-ap-coh! = pp-coh!  {u = ap to (glue (left (f₂₁ c)))}
                          {ap (to ∘ right ∘ left) (H₃₁ c)}
                          {ap to (ap right (glue (f₃₂ c)))}
                          {ap (to ∘ right ∘ right) (H₃₃ c)}
                          {ap (to ∘ left ∘ left) (H₁₁ c)}
                          {ap to (ap left (glue (f₁₂ c)))}
                          {ap (to ∘ left ∘ right) (H₁₃ c)}
                          {ap to (glue (right (f₂₃ c)))}

    ap-ap-coh : (p : _ , _ =□ _ , _) → _
    ap-ap-coh = pp-coh {p = ap to (ap left (glue (f₁₂ c)))}
                       {ap (to ∘ left ∘ right) (H₁₃ c)}
                       {ap to (glue (right (f₂₃ c)))}
                       {ap (to ∘ right ∘ right) (H₃₃ c)}
                       {ap (to ∘ left ∘ left) (H₁₁ c)}
                       {ap to (glue (left (f₂₁ c)))}
                       {ap (to ∘ right ∘ left) (H₃₁ c)}
                       {ap to (ap right (glue (f₃₂ c)))}

    ap-ap-coh!-lhs-i : _
    ap-ap-coh!-lhs-i = ! (ap-∙∙`∘`∘ to (right ∘ left) (right ∘ right) (H₃₁ c) (ap right (glue (f₃₂ c))) (H₃₃ c))

    ap-ap-coh!-rhs-i : _
    ap-ap-coh!-rhs-i = ap-∙∙`∘`∘ to (left ∘ left) (left ∘ right) (H₁₁ c) (ap left (glue (f₁₂ c))) (H₁₃ c)

    ap-ap-coh!-lhs-o : _
    ap-ap-coh!-lhs-o = ap-∙∙!'`∘`∘ to (left ∘ right) (right ∘ right) (H₁₃ c) (glue (right (f₂₃ c))) (H₃₃ c)

    ap-ap-coh!-rhs-o : _
    ap-ap-coh!-rhs-o = ! (ap-!'∙∙`∘`∘ to (left ∘ left) (right ∘ left) (H₁₁ c) (glue (left (f₂₁ c))) (H₃₁ c))

    ap-ap-coh!-β : (α : _)
            → ap□ to (coh! α) == ap-ap-coh! (ap□ to α
                                             ∙□-i/ ap-ap-coh!-lhs-i
                                                 / ap-ap-coh!-rhs-i /)
                                 ∙□-i/ ap-ap-coh!-lhs-o
                                     / ap-ap-coh!-rhs-o /
    ap-ap-coh!-β α = ap□-coh! {g₁ = left ∘ right} {right ∘ right} {left ∘ left} {right ∘ left} to
                              {u = glue (left (f₂₁ c))} {H₃₁ c} {ap right (glue (f₃₂ c))} {H₃₃ c}
                              {H₁₁ c} {ap left (glue (f₁₂ c))} {H₁₃ c} {glue (right (f₂₃ c))}
                              α

open E∙₂Red

module E∙₂ = PushoutElim {d = span _ _ _ f₁₂ f₃₂} {P = λ c → i∙₀ (f∙₁ c) == i∙₄ (f∙₃ c)}
  (ap left ∘ glue) (ap right ∘ glue)
  (λ c → ↓-='-in (coh! c (↓-='-out (apd (glue {d = v-h-span}) (glue c))
                          ∙□-i/ lhs-i c / rhs-i c /)
                  ∙□-i/ lhs-o c / rhs-o c /))

e∙₂ : (c : A∙₂) → i∙₀ (f∙₁ c) == i∙₄ (f∙₃ c)
e∙₂ = E∙₂.f

module From = PushoutRec {d = h-v-span} {D = Pushout^2 d}
                       i∙₀ i∙₄ e∙₂

from : Pushout^2 (transpose d) → Pushout^2 d
from = From.f

from-glue-glue-β : (c : A₂₂) → ap↓ (ap from) (apd glue (glue c))
                          == (↓-='-in (coh! c (↓-='-out (apd (glue {d = v-h-span}) (glue c))
                                              ∙□-i/ lhs-i c / rhs-i c /)
                                       ∙□-i/ lhs-o c / rhs-o c /))
                              ◃/ From.glue-β (left (f₁₂ c)) / ! (From.glue-β (right (f₃₂ c))) /
from-glue-glue-β c = ∘'-apd (ap from) glue (glue c)
                   ∙ coh1 {p = apd (ap from ∘ glue) (glue c)} {From.glue-β (right (f₃₂ c))}
                          {From.glue-β (left (f₁₂ c))} {apd e∙₂ (glue c)}
                          (↓-=-out (apd From.glue-β (glue c)))
                   ∙ (E∙₂.glue-β c |in-ctx (λ u → u ◃/ From.glue-β (left (f₁₂ c)) / ! (From.glue-β (right (f₃₂ c)))/))


open import Agda.Primitive using (_⊔_ ; lsuc ; Level)

import Categories.Category as Category
import Categories.Category.Cartesian as Cartesian
open import Categories.Object.Terminal using (Terminal)
open import Categories.Object.Product using (Product)

open import MultiSorted.AlgebraicTheory
open import MultiSorted.Substitution
import MultiSorted.Product as Product
import MultiSorted.Interpretation as Interpretation
import MultiSorted.Model as Is-Model
import MultiSorted.InterpretationCategory as InterpretationCategory

module MultiSorted.ModelCategory
         {ℓt o ℓ e}
         {𝓈 ℴ}
         {Σ : Signature {𝓈} {ℴ}}
         (T : Theory ℓt Σ)
         {𝒞 : Category.Category o ℓ e}
         (cartesian-𝒞 : Cartesian.Cartesian 𝒞) where
  open Signature Σ
  open Theory T
  open Category.Category 𝒞
  open Interpretation Σ cartesian-𝒞
  open Is-Model {o = o} {ℓ = ℓ} {e = e} {Σ = Σ} T

  -- Useful shortcuts for levels
  ℓℴ : Level
  ℓℴ = lsuc (o ⊔ ℓ ⊔ e ⊔ 𝓈 ⊔ ℴ ⊔ ℓt)

  ℓ𝒽 : Level
  ℓ𝒽 = ℓ ⊔ o ⊔ e ⊔ 𝓈 ⊔ ℴ

  ℓ𝓇 : Level
  ℓ𝓇 = e ⊔ 𝓈

  -- New definition of models (as a set, not only a property of interpretations)
  record Model : Set ℓℴ where
    field
      interpretation : Interpretation
      is-model : Is-Model.Is-Model T interpretation

  open Model

  -- Homomorphisms of models
  _⇒M_ : ∀ (M N : Model) → Set ℓ𝒽
  _⇒M_ M N = (interpretation M) ⇒I (interpretation N)

  -- Equality of homomorphisms of models (the same as for the interpretations)
  _≈M_ : ∀ {M N : Model} → M ⇒M N → M ⇒M N → Set ℓ𝓇
  _≈M_ {M} {N} ϕ ψ =
                   let open _⇒I_ in
                   ∀ A → (hom-morphism ϕ {A}) ≈ (hom-morphism ψ)

  -- The identity morphism on models
  id-M : (M : Model) → M ⇒M M
  id-M = λ M → id-I {interpretation M}

  -- Composition of morphisms of Models
  _∘M_ : ∀ {M N O : Model} →  N ⇒M O → M ⇒M N → M ⇒M O
  _∘M_ ϕ ψ = ϕ ∘I ψ

  -- The category of Models of Σ in 𝒞
  ℳ : Category.Category ℓℴ ℓ𝒽 ℓ𝓇
  ℳ = record
          { Obj = Model
          ; _⇒_ = _⇒M_
          ; _≈_ = λ {M} {N} ϕ ψ → _≈M_ {M} {N} ϕ ψ
          ; id = λ {M} → id-M M
          ; _∘_ = λ {M} {N} {O} ϕ ψ → _∘M_ {M} {N} {O} ϕ ψ
          ; assoc = λ A → assoc -- λ A → assoc
          ; sym-assoc = λ A → sym-assoc
          ; identityˡ = λ A → identityˡ
          ; identityʳ = λ A → identityʳ
          ; identity² = λ A → identity²
          ; equiv = record { refl = λ A → Equiv.refl
                           ; sym = λ p A → Equiv.sym (p A)
                           ; trans = λ p₁ p₂ A → Equiv.trans (p₁ A) (p₂ A)
                           }
          ; ∘-resp-≈ = λ p₁ p₂ A → ∘-resp-≈ (p₁ A) (p₂ A)
          }

  -- The product of "Model proofs"

  module _ (M N : Model) where
    open Product.Producted
    open HomReasoning
    open InterpretationCategory
    open Cartesian.Cartesian cartesian-𝒞
    open Interpretation.Interpretation
    open import Categories.Object.Product.Morphisms {o} {ℓ} {e} 𝒞
    open Equation

    -- A proof that an axiom holds in a product interpretation amounts to a apir of proofs that the axiom holds in each model
    is-model-pairs : ∀ ε → (interp-term (interpretation M) (Equation.eq-lhs (ax-eq ε)) ⁂  interp-term (interpretation N) (Equation.eq-lhs (ax-eq ε)))
                               ≈ (interp-term (interpretation M) (Equation.eq-rhs (ax-eq ε)) ⁂  interp-term (interpretation N) (Equation.eq-rhs (ax-eq ε))) →
                               Interpretation.interp-term (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N)) (Equation.eq-lhs (ax-eq ε))
                               ≈ Interpretation.interp-term (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N)) (Equation.eq-rhs (ax-eq ε))
    is-model-pairs ε p =
                            begin
                              Interpretation.interp-term
                                (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N))
                                (Equation.eq-lhs (ax-eq ε)) ≈⟨ ⟺
                                                                 (Cartesian.Cartesian.unique cartesian-𝒞
                                                                   (natural-π₁ Σ cartesian-𝒞 {I = interpretation M} {interpretation N} (ax-lhs ε))
                                                                   (natural-π₂ Σ cartesian-𝒞 {I = interpretation M} {interpretation N} (ax-lhs ε))) ⟩
                              product.⟨
                                Interpretation.interp-term (interpretation M) (eq-lhs (ax-eq ε)) ∘
                                π₁
                                ,
                                Interpretation.interp-term (interpretation N) (eq-lhs (ax-eq ε)) ∘
                                π₂
                                ⟩ ≈⟨ ⟨⟩-cong₂ (∘-resp-≈ˡ (Is-Model.model-eq (is-model M) ε)) (∘-resp-≈ˡ (Is-Model.model-eq (is-model N) ε)) ⟩
                              product.⟨
                                Interpretation.interp-term (interpretation M) (eq-rhs (ax-eq ε)) ∘
                                π₁
                                ,
                                Interpretation.interp-term (interpretation N) (eq-rhs (ax-eq ε)) ∘
                                π₂
                                ⟩ ≈⟨ Cartesian.Cartesian.unique cartesian-𝒞
                                     (natural-π₁ Σ cartesian-𝒞 {I = interpretation M} {interpretation N} (ax-rhs ε))
                                     (natural-π₂ Σ cartesian-𝒞 {I = interpretation M} {interpretation N} (ax-rhs ε)) ⟩
                              Interpretation.interp-term
                                (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N))
                                (eq-rhs (ax-eq ε)) ∎

    -- The proof that the product interpetation of two models is a model
    is-model-product : Is-Model (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N))
    Is-Model.model-eq is-model-product ε =
                                           begin
                                             Interpretation.interp-term
                                               (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N))
                                               (Equation.eq-lhs (ax-eq ε)) ≈⟨ is-model-pairs ε (⁂-cong₂ (Is-Model.model-eq (is-model M) ε) (Is-Model.model-eq (is-model N) ε)) ⟩
                                             Interpretation.interp-term
                                               (A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N))
                                               (Equation.eq-rhs (ax-eq ε)) ∎

  -- The product of ℐ𝓃𝓉 carries over the models : the product of two models is a model
  module _ (M N : Model) where
    open Product.Producted
    open HomReasoning
    open InterpretationCategory
    A×B-ℳ : Model
    A×B-ℳ = record
              { interpretation = A×B-ℐ𝓃𝓉 Σ cartesian-𝒞 (interpretation M) (interpretation N)
              ; is-model = is-model-product M N
              }

  -- The cartesian structure of the category of models

  module _ {M N : Model} where
    import Categories.Object.Product.Core
    open Categories.Object.Product.Core.Product
    open InterpretationCategory Σ cartesian-𝒞
    private
      UM UN : Interpretation
      UM = interpretation M
      UN = interpretation N
      UM×UN : Product ℐ𝓃𝓉 UM UN
      UM×UN = product-ℐ𝓃𝓉
    product-ℳ : Product ℳ M N
    -- Structure
    A×B      product-ℳ = A×B-ℳ M N
    π₁       product-ℳ = π₁    UM×UN
    π₂       product-ℳ = π₂    UM×UN
    ⟨_,_⟩    product-ℳ = ⟨_,_⟩ UM×UN
    -- Properties
    project₁ product-ℳ {O} {h} {i}     = project₁ UM×UN {interpretation O} {h} {i}
    project₂ product-ℳ {O} {h} {i}     = project₂ UM×UN {interpretation O} {h} {i}
    unique   product-ℳ {O} {h} {i} {j} = unique   UM×UN {interpretation O} {h} {i} {j}


------------------------------------------------------------------------
-- The reader monad transformer
------------------------------------------------------------------------

-- The interface is based on that used by the "mtl" package on
-- Hackage.

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

open import Equality

module Monad.Reader
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Prelude

open Derived-definitions-and-properties eq
open import Monad eq

-- The reader monad transformer, defined using a wrapper type to make
-- instance resolution easier.

record ReaderT {s} (S : Type s)
               (M : Type s → Type s)
               (A : Type s) : Type s where
  constructor wrap
  field
    run : S → M A

open ReaderT public

instance

  -- ReaderT is a "raw monad" transformer.

  raw-monad-transformer :
    ∀ {s} {S : Type s} →
    Raw-monad-transformer (ReaderT S)
  run (Raw-monad.return (Raw-monad-transformer.transform
                           raw-monad-transformer) x) =
    λ _ → return x
  run (Raw-monad._>>=_ (Raw-monad-transformer.transform
                          raw-monad-transformer) x f) =
    λ s → run x s >>= λ x → run (f x) s

  run (Raw-monad-transformer.liftʳ raw-monad-transformer m) =
    λ _ → m

  transform : ∀ {s} {S : Type s} {M} ⦃ is-raw-monad : Raw-monad M ⦄ →
              Raw-monad (ReaderT S M)
  transform = Raw-monad-transformer.transform raw-monad-transformer

-- ReaderT is a monad transformer (assuming extensionality).

monad-transformer :
  ∀ {s} {S : Type s} →
  Extensionality s s →
  Monad-transformer (ReaderT S)
Monad.raw-monad (Monad-transformer.transform (monad-transformer _)) =
  Raw-monad-transformer.transform raw-monad-transformer

Monad.left-identity (Monad-transformer.transform
                       (monad-transformer ext)) x f =
  cong wrap (apply-ext ext λ s →
    (return x >>= λ x → run (f x) s)  ≡⟨ left-identity _ _ ⟩∎
    run (f x) s                       ∎)

Monad.right-identity (Monad-transformer.transform
                        (monad-transformer ext)) x =
  cong wrap (apply-ext ext λ s →
    run x s >>= return  ≡⟨ right-identity _ ⟩∎
    run x s             ∎)

Monad.associativity (Monad-transformer.transform
                       (monad-transformer ext)) x f g =
  cong wrap (apply-ext ext λ s →
    (run x s >>= λ x → run (f x) s >>= λ x → run (g x) s)    ≡⟨ associativity _ _ _ ⟩∎
    ((run x s >>= λ x → run (f x) s) >>= λ x → run (g x) s)  ∎)

Monad-transformer.liftᵐ (monad-transformer _) = liftʳ

Monad-transformer.lift-return (monad-transformer ext) x =
  cong wrap (apply-ext ext λ _ →
    return x  ∎)

Monad-transformer.lift->>= (monad-transformer ext) x f =
  cong wrap (apply-ext ext λ _ →
    x >>= f  ∎)

-- Returns the "state".

ask : ∀ {s} {S : Type s} {M} ⦃ is-monad : Raw-monad M ⦄ →
      ReaderT S M S
run ask = return

-- Modifies the "state" in a given computation.

local : ∀ {s} {S : Type s} {M A} ⦃ is-monad : Raw-monad M ⦄ →
        (S → S) → ReaderT S M A → ReaderT S M A
run (local f m) = run m ∘ f