------------------------------------------------------------------------
-- Functional semantics for a non-deterministic untyped λ-calculus
-- with constants
------------------------------------------------------------------------

module Lambda.Closure.Functional.Non-deterministic where

open import Category.Monad.Partiality as Pa using (_⊥; now; later)
open import Codata.Musical.Notation
open import Data.Fin using (Fin; zero; suc; #_)
open import Data.List hiding (lookup)
open import Data.Maybe hiding (_>>=_)
open import Data.Nat
open import Data.Vec using ([]; _∷_; lookup)
open import Function
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Nullary.Decidable

open import Lambda.Syntax using (Ty; Ctxt)
open Lambda.Syntax.Closure using (con; ƛ)
open Lambda.Syntax.Ty
open import Lambda.VirtualMachine
  hiding (comp; comp-val; comp-env; lookup-hom)
open Functional
private
  module VM = Lambda.Syntax.Closure Code

------------------------------------------------------------------------
-- A monad with partiality, failure and non-determinism

module D where

  -- This is basically the maybe monad transformer applied to the
  -- partiality monad transformer applied to the non-determinism monad
  -- N A = μX. A ⊎ X × X. Unfortunately it is somewhat awkward to
  -- implement the partiality monad transformer in Agda: the
  -- definition
  --
  --   data Partiality′ (M : Set → Set) (A : Set) : Set where
  --     now-or-later : M (A ⊎ Partiality′ M A) → Partiality′ M A
  --
  -- is rejected, because M might use its argument negatively.

  infixr 6 _∣_

  data D (A : Set) : Set where
    fail   : D A
    return : (x : A) → D A
    _∣_    : (x y : D A) → D A
    later  : (x : ∞ (D A)) → D A

  -- The function force n removes (up to) n layers of later
  -- constructors.

  force : {A : Set} → ℕ → D A → D A
  force (suc n) (later x) = force n (♭ x)
  force n       (x₁ ∣ x₂) = force n x₁ ∣ force n x₂
  force _       x         = x

  -- Bind.

  infixl 5 _>>=_

  _>>=_ : {A B : Set} → D A → (A → D B) → D B
  fail      >>= f = fail
  return x  >>= f = f x
  (x₁ ∣ x₂) >>= f = (x₁ >>= f) ∣ (x₂ >>= f)
  later x   >>= f = later (♯ (♭ x >>= f))

  -- A deterministic non-terminating computation.

  never : {A : Set} → D A
  never = later (♯ never)

  -- Strong bisimilarity.

  infix 4 _≅_

  data _≅_ {A : Set} : D A → D A → Set where
    fail   : fail ≅ fail
    return : ∀ {x} → return x ≅ return x
    _∣_    : ∀ {x₁ x₂ y₁ y₂}
             (x₁≅y₁ : x₁ ≅ y₁) (x₂≅y₂ : x₂ ≅ y₂) → x₁ ∣ x₂ ≅ y₁ ∣ y₂
    later  : ∀ {x y} (x≅y : ∞ (♭ x ≅ ♭ y)) → later x ≅ later y

  -- Strong bisimilarity is reflexive.

  infixr 3 _∎

  _∎ : {A : Set} (x : D A) → x ≅ x
  fail     ∎ = fail
  return x ∎ = return
  x₁ ∣ x₂  ∎ = (x₁ ∎) ∣ (x₂ ∎)
  later x  ∎ = later (♯ (♭ x ∎))

  -- Strong bisimilarity is symmetric.

  sym : {A : Set} {x y : D A} → x ≅ y → y ≅ x
  sym fail            = fail
  sym return          = return
  sym (x₁≅y₁ ∣ x₂≅y₂) = sym x₁≅y₁ ∣ sym x₂≅y₂
  sym (later x≅y)     = later (♯ sym (♭ x≅y))

  -- Strong bisimilarity is transitive.

  infixr 2 _≅⟨_⟩_

  _≅⟨_⟩_ : ∀ {A : Set} (x : D A) {y z} → x ≅ y → y ≅ z → x ≅ z
  ._ ≅⟨ fail          ⟩ fail          = fail
  ._ ≅⟨ return        ⟩ return        = return
  ._ ≅⟨ x₁≅y₁ ∣ x₂≅y₂ ⟩ y₁≅z₁ ∣ y₂≅z₂ = (_ ≅⟨ x₁≅y₁ ⟩ y₁≅z₁) ∣ (_ ≅⟨ x₂≅y₂ ⟩ y₂≅z₂)
  ._ ≅⟨ later x≅y     ⟩ later y≅z     = later (♯ (_ ≅⟨ ♭ x≅y ⟩ ♭ y≅z))

  -- The monad laws hold up to strong bisimilarity.

  left-identity : {A B : Set} {x : A} {f : A → D B} →
                  return x >>= f ≅ f x
  left-identity {x = x} {f} = f x ∎

  right-identity : {A : Set} (x : D A) → x >>= return ≅ x
  right-identity fail       = fail
  right-identity (return x) = return
  right-identity (x₁ ∣ x₂)  = right-identity x₁ ∣ right-identity x₂
  right-identity (later x)  = later (♯ right-identity (♭ x))

  associative : {A B C : Set} (x : D A) {f : A → D B} {g : B → D C} →
                x >>= f >>= g ≅ x >>= λ y → f y >>= g
  associative fail               = fail
  associative (return x) {f} {g} = f x >>= g ∎
  associative (x₁ ∣ x₂)          = associative x₁ ∣ associative x₂
  associative (later x)          = later (♯ associative (♭ x))

  -- Bind respects strong bisimilarity.

  infixl 5 _>>=-cong_

  _>>=-cong_ : {A B : Set} {x₁ x₂ : D A} {f₁ f₂ : A → D B} →
               x₁ ≅ x₂ → (∀ y → f₁ y ≅ f₂ y) → x₁ >>= f₁ ≅ x₂ >>= f₂
  fail          >>=-cong f₁≅f₂ = fail
  return        >>=-cong f₁≅f₂ = f₁≅f₂ _
  later x≅y     >>=-cong f₁≅f₂ = later (♯ (♭ x≅y >>=-cong f₁≅f₂))
  x₁≅x₂ ∣ y₁≅y₂ >>=-cong f₁≅f₂ =
    (x₁≅x₂ >>=-cong f₁≅f₂) ∣ (y₁≅y₂ >>=-cong f₁≅f₂)

  -- More laws.

  never-left-zero : {A B : Set} {f : A → D B} → never >>= f ≅ never
  never-left-zero = later (♯ never-left-zero)

  fail-left-zero : {A B : Set} {f : A → D B} → fail >>= f ≅ fail
  fail-left-zero = fail ∎

open D using (D; _≅_; _≅⟨_⟩_; _∎; _>>=-cong_) hiding (module D)
open D.D
open D._≅_

------------------------------------------------------------------------
-- Syntax

infixl 9 _·_
infixr 6 _∣_

data Tm (n : ℕ) : Set where
  con : (i : ℕ) → Tm n
  var : (x : Fin n) → Tm n
  ƛ   : (t : Tm (suc n)) → Tm n
  _·_ : (t₁ t₂ : Tm n) → Tm n
  _∣_ : (t₁ t₂ : Tm n) → Tm n

-- Convenient helper.

vr : ∀ m {n} {m<n : True (suc m ≤? n)} → Tm n
vr _ {m<n = m<n} = var (#_ _ {m<n = m<n})

open Lambda.Syntax.Closure Tm hiding (con; ƛ)

------------------------------------------------------------------------
-- A workaround for the limitations of guardedness

module Workaround where

  infixr 6 _∣_
  infixl 5 _>>=_

  data DP : Set → Set₁ where
    fail   : ∀ {A} → DP A
    return : ∀ {A} (x : A) → DP A
    _∣_    : ∀ {A} (x y : DP A) → DP A
    later  : ∀ {A} (x : ∞ (DP A)) → DP A
    _>>=_  : ∀ {A B} (x : DP A) (f : A → DP B) → DP B

  private

    data DW : Set → Set₁ where
      fail   : ∀ {A} → DW A
      return : ∀ {A} (x : A) → DW A
      _∣_    : ∀ {A} (x y : DW A) → DW A
      later  : ∀ {A} (x : DP A) → DW A

    mutual

      _>>=W_ : ∀ {A B} → DW A → (A → DP B) → DW B
      fail     >>=W f = fail
      return x >>=W f = whnf (f x)
      (x ∣ y)  >>=W f = (x >>=W f) ∣ (y >>=W f)
      later x  >>=W f = later (x >>= f)

      whnf : ∀ {A} → DP A → DW A
      whnf fail       = fail
      whnf (return x) = return x
      whnf (later x)  = later (♭ x)
      whnf (x ∣ y)    = whnf x ∣ whnf y
      whnf (x >>= f)  = whnf x >>=W f

  mutual

    private

      ⟪_⟫W : ∀ {A} → DW A → D A
      ⟪ fail     ⟫W = fail
      ⟪ return x ⟫W = return x
      ⟪ x ∣ y    ⟫W = ⟪ x ⟫W ∣ ⟪ y ⟫W
      ⟪ later x  ⟫W = later (♯ ⟪ x ⟫P)

    ⟪_⟫P : ∀ {A} → DP A → D A
    ⟪ p ⟫P = ⟪ whnf p ⟫W

  -- The definitions above make sense. ⟪_⟫P is homomorphic with respect
  -- to fail, return, _∣_, later and _>>=_.

  fail-hom : ∀ {A} → ⟪ fail {A = A} ⟫P ≅ fail
  fail-hom = fail ∎

  return-hom : ∀ {A} (x : A) → ⟪ return x ⟫P ≅ return x
  return-hom x = return x ∎

  ∣-hom : ∀ {A} (x y : DP A) → ⟪ x ∣ y ⟫P ≅ ⟪ x ⟫P ∣ ⟪ y ⟫P
  ∣-hom x y = ⟪ x ⟫P ∣ ⟪ y ⟫P ∎

  later-hom : ∀ {A} (x : ∞ (DP A)) → ⟪ later x ⟫P ≅ later (♯ ⟪ ♭ x ⟫P)
  later-hom x = later (♯ (⟪ ♭ x ⟫P ∎))

  mutual

    private

      >>=-homW : ∀ {A B} (x : DW A) (f : A → DP B) →
                 ⟪ x >>=W f ⟫W ≅ D._>>=_ ⟪ x ⟫W (λ y → ⟪ f y ⟫P)
      >>=-homW fail       f = fail ∎
      >>=-homW (return x) f = ⟪ f x ⟫P ∎
      >>=-homW (x ∣ y)    f = >>=-homW x f ∣ >>=-homW y f
      >>=-homW (later x)  f = later (♯ >>=-hom x f)

    >>=-hom : ∀ {A B} (x : DP A) (f : A → DP B) →
              ⟪ x >>= f ⟫P ≅ D._>>=_ ⟪ x ⟫P (λ y → ⟪ f y ⟫P)
    >>=-hom x f = >>=-homW (whnf x) f

open Workaround hiding (_>>=_)

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

infix 9 _∙_

mutual

  ⟦_⟧′ : ∀ {n} → Tm n → Env n → DP Value
  ⟦ con i ⟧′   ρ = return (con i)
  ⟦ var x ⟧′   ρ = return (lookup ρ x)
  ⟦ ƛ t ⟧′     ρ = return (ƛ t ρ)
  ⟦ t₁ · t₂ ⟧′ ρ = ⟦ t₁ ⟧′ ρ >>= λ v₁ →
                   ⟦ t₂ ⟧′ ρ >>= λ v₂ →
                   v₁ ∙ v₂
                   where open Workaround
  ⟦ t₁ ∣ t₂ ⟧′ ρ = ⟦ t₁ ⟧′ ρ ∣ ⟦ t₂ ⟧′ ρ

  _∙_ : Value → Value → DP Value
  con i  ∙ v₂ = fail
  ƛ t₁ ρ ∙ v₂ = later (♯ (⟦ t₁ ⟧′ (v₂ ∷ ρ)))

⟦_⟧ : ∀ {n} → Tm n → Env n → D Value
⟦ t ⟧ ρ = ⟪ ⟦ t ⟧′ ρ ⟫P

-- An abbreviation.

infix 9 _⟦·⟧_

_⟦·⟧_ : D Value → D Value → D Value
v₁ ⟦·⟧ v₂ = v₁ >>= λ v₁ → v₂ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P
  where open D

-- The semantics of application is compositional (with respect to the
-- syntactic equality which is used).

·-comp : ∀ {n} (t₁ t₂ : Tm n) {ρ} →
         ⟦ t₁ · t₂ ⟧ ρ ≅ ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ
·-comp t₁ t₂ {ρ} =
  ⟦ t₁ · t₂ ⟧ ρ                                   ≅⟨ >>=-hom (⟦ t₁ ⟧′ ρ) _ ⟩

  D._>>=_ (⟦ t₁ ⟧ ρ) (λ v₁ →
          ⟪ Workaround._>>=_ (⟦ t₂ ⟧′ ρ) (λ v₂ →
            v₁ ∙ v₂) ⟫P)                          ≅⟨ ((⟦ t₁ ⟧ ρ ∎) >>=-cong λ _ →
                                                      >>=-hom (⟦ t₂ ⟧′ ρ) _) ⟩
  ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ                           ∎

open D

------------------------------------------------------------------------
-- Compiler

-- The compiler takes a code continuation.
--
-- Non-determinism is resolved by always picking the left choice.

comp : ∀ {n} → Tm n → Code n → Code n
comp (con i)   c = con i ∷ c
comp (var x)   c = var x ∷ c
comp (ƛ t)     c = clo (comp t [ ret ]) ∷ c
comp (t₁ · t₂) c = comp t₁ (comp t₂ (app ∷ c))
comp (t₁ ∣ t₂) c = comp t₁ c

-- Environments and values can also be compiled.

mutual

  comp-env : ∀ {n} → Env n → VM.Env n
  comp-env []      = []
  comp-env (v ∷ ρ) = comp-val v ∷ comp-env ρ

  comp-val : Value → VM.Value
  comp-val (con i) = con i
  comp-val (ƛ t ρ) = ƛ (comp t [ ret ]) (comp-env ρ)

-- lookup x is homomorphic with respect to comp-env/comp-val.

lookup-hom : ∀ {n} (x : Fin n) ρ →
             lookup (comp-env ρ) x ≡ comp-val (lookup ρ x)
lookup-hom zero    (v ∷ ρ) = P.refl
lookup-hom (suc x) (v ∷ ρ) = lookup-hom x ρ

------------------------------------------------------------------------
-- Examples

-- A non-terminating term.

Ω : Tm 0
Ω = ω · ω
  where ω = ƛ (vr 0 · vr 0)

Ω-loops : ⟦ Ω ⟧ [] ≅ never
Ω-loops = later (♯ Ω-loops)

-- A call-by-value fix-point combinator.

Z : {n : ℕ} → Tm n
Z = ƛ (t · t)
  where t = ƛ (vr 1 · ƛ (vr 1 · vr 1 · vr 0))

-- A non-deterministically non-terminating term.

! : Tm 0
! = Z · ƛ (ƛ (vr 1 · vr 0 ∣ vr 1 · vr 0)) · con 0

-- Its semantics.

!-sem : D Value
!-sem = later (♯ later (♯ later (♯ later (♯ (!-sem ∣ !-sem)))))

⟦!⟧≅!-sem : ⟦ ! ⟧ [] ≅ !-sem
⟦!⟧≅!-sem = later (♯ lem)
  where
  lem : force 1 (⟦ ! ⟧ []) ≅ force 1 !-sem
  lem = later (♯ later (♯ later (♯ (later (♯ lem) ∣ later (♯ lem)))))

-- How did I come up with this proof term? Through a manual
-- calculation...
--
-- Let us first define some abbreviations:
--
--   t₀ = vr 1 · vr 1 · vr 0
--   t₁ = ƛ t₀
--   t₂ = vr 1 · t₁
--   t₃ = ƛ t₂
--
--   u₀ = vr 1 · vr 0
--   u₁ = u₀ ∣ u₀
--   u₂ = ƛ u₁
--   u₃ = ƛ u₂
--
--   c₀ = ƛ u₂ []
--   c₁ = ƛ t₂ (c₀ ∷ [])
--   c₂ = ƛ t₀ (c₁ ∷ c₀ ∷ [])
--
-- Now we can calculate as follows (ignoring ♯):
--
--   ⟦ Z · u₃ · con 0 ⟧ []
-- = ⟦ Z · u₃ ⟧ [] ⟦·⟧ return (con 0)
-- = ƛ (t₃ · t₃) [] ∙ c₀ ⟦·⟧ return (con 0)
-- = later (⟦ t₃ · t₃ ⟧ (c₀ ∷ []) ⟦·⟧ return (con 0))
-- = later (c₁ ∙ c₁ ⟦·⟧ return (con 0))
--
-- = c₁ ∙ c₁ ⟦·⟧ return (con 0)
-- = later (⟦ t₂ ⟧ (c₁ ∷ c₀ ∷ []) ⟦·⟧ return (con 0))
-- = later (c₀ ∙ c₂ ⟦·⟧ return (con 0))
-- = later (later (⟦ u₂ ⟧ (c₂ ∷ []) ⟦·⟧ return (con 0)))
-- = later (later (ƛ u₁ (c₂ ∷ []) ∙ con 0))
-- = later (later (later (⟦ u₁ ⟧ (con 0 ∷ c₂ ∷ []))))
-- = later (later (later (⟦ u₀ ⟧ (con 0 ∷ c₂ ∷ []) ∣
--                        ⟦ u₀ ⟧ (con 0 ∷ c₂ ∷ []))))
-- = later (later (later (c₂ ∙ con 0 ∣ c₂ ∙ con 0)))
-- = later (later (later (⟦ t₀ ⟧ (con 0 ∷ c₁ ∷ c₀ ∷ []) ∣
--                        ⟦ t₀ ⟧ (con 0 ∷ c₁ ∷ c₀ ∷ []))))
-- = later (later (later (later (c₁ ∙ c₁ ⟦·⟧ return (con 0)) ∣
--                        later (c₁ ∙ c₁ ⟦·⟧ return (con 0)))))

------------------------------------------------------------------------
-- A relation relating deterministic and non-deterministic
-- computations

-- x ≈∈ y means that x implements /one/ possible semantics of y (up to
-- weak bisimilarity).

infix 4 _≈∈_

data _≈∈_ {A : Set} : Maybe A ⊥ → D A → Set where
  fail   : now nothing ≈∈ fail

  return : ∀ {x} → now (just x) ≈∈ return x

  ∣ˡ     : ∀ {x y₁ y₂} (x≈∈y₁ : x ≈∈ y₁) → x ≈∈ y₁ ∣ y₂
  ∣ʳ     : ∀ {x y₁ y₂} (x≈∈y₂ : x ≈∈ y₂) → x ≈∈ y₁ ∣ y₂

  later  : ∀ {x y} (x≈∈y : ∞ (♭ x ≈∈ ♭ y)) → later x ≈∈ later y
  laterˡ : ∀ {x y} (x≈∈y :    ♭ x ≈∈   y ) → later x ≈∈       y
  laterʳ : ∀ {x y} (x≈∈y :      x ≈∈ ♭ y ) →       x ≈∈ later y

-- A transitivity-like result for _≡_ and _≈∈_.

infixr 2 _≡⟨_⟩_

_≡⟨_⟩_ : ∀ {A : Set} (x : Maybe A ⊥) {y z} → x ≡ y → y ≈∈ z → x ≈∈ z
_ ≡⟨ P.refl ⟩ y≈z = y≈z

-- A transitivity-like result for _≈∈_ and _≅_.

infixr 2 _≈∈⟨_⟩_

_≈∈⟨_⟩_ : ∀ {A : Set} (x : Maybe A ⊥) {y z} → x ≈∈ y → y ≅ z → x ≈∈ z
._ ≈∈⟨ fail        ⟩ fail          = fail
._ ≈∈⟨ return      ⟩ return        = return
_  ≈∈⟨ ∣ˡ x₁≈∈y₁   ⟩ y₁≅z₁ ∣ y₂≅z₂ = ∣ˡ (_ ≈∈⟨ x₁≈∈y₁ ⟩ y₁≅z₁)
_  ≈∈⟨ ∣ʳ x₂≈∈y₂   ⟩ y₁≅z₁ ∣ y₂≅z₂ = ∣ʳ (_ ≈∈⟨ x₂≈∈y₂ ⟩ y₂≅z₂)
._ ≈∈⟨ later  x≈∈y ⟩ later y≅z     = later (♯ (_ ≈∈⟨ ♭ x≈∈y ⟩ ♭ y≅z))
._ ≈∈⟨ laterˡ x≈∈y ⟩       y≅z     = laterˡ   (_ ≈∈⟨   x≈∈y ⟩   y≅z)
_  ≈∈⟨ laterʳ x≈∈y ⟩ later y≅z     = laterʳ   (_ ≈∈⟨   x≈∈y ⟩ ♭ y≅z)

-- An example.

lemma : Pa.never ≈∈ !-sem
lemma = later (♯ later (♯ later (♯ later (♯ ∣ˡ lemma))))

------------------------------------------------------------------------
-- Compiler correctness

module Correctness where

  infix  4 _≈∈P_ _≈∈W_
  infixr 2 _≡⟨_⟩W_ _≈∈⟨_⟩P_ _≈∈⟨_⟩W_

  mutual

    data _≈∈P_ : Maybe VM.Value ⊥ → D VM.Value → Set where
      _≈∈⟨_⟩P_ : ∀ x {y z} (x≈y : x ≈∈P y) (y≅z : y ≅ z) → x ≈∈P z
      correct :
        ∀ {n} t {ρ : Env n} {c s} {k : Value → D VM.Value} →
        (hyp : ∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩
                       ≈∈W k v) →
        exec ⟨ comp t c , s , comp-env ρ ⟩ ≈∈P (⟦ t ⟧ ρ >>= k)

    data _≈∈W_ : Maybe VM.Value ⊥ → D VM.Value → Set where
      ⌈_⌉    : ∀ {x y} (x≈y : x ≈∈ y) → x ≈∈W y
      ∣ˡ     : ∀ {x y₁ y₂} (x≈y₁ : x ≈∈W y₁) → x ≈∈W y₁ ∣ y₂
      later  : ∀ {x y} (x≈y : ♭ x ≈∈P ♭ y) → later x ≈∈W later y
      laterˡ : ∀ {x y} (x≈y : ♭ x ≈∈W   y) → later x ≈∈W       y

  _≡⟨_⟩W_ : ∀ x {y z} → x ≡ y → y ≈∈W z → x ≈∈W z
  _ ≡⟨ P.refl ⟩W y≈z = y≈z

  _≈∈⟨_⟩W_ : ∀ x {y z} → x ≈∈W y → y ≅ z → x ≈∈W z
  _  ≈∈⟨ ⌈ x≈y ⌉    ⟩W       y≅z     = ⌈ _ ≈∈⟨ x≈y ⟩ y≅z ⌉
  _  ≈∈⟨ ∣ˡ x≈y₁    ⟩W y₁≅z₁ ∣ y₂≅z₂ = ∣ˡ (_ ≈∈⟨ x≈y₁ ⟩W y₁≅z₁)
  ._ ≈∈⟨ later  x≈y ⟩W later y≅z     = later  (_ ≈∈⟨ x≈y ⟩P ♭ y≅z)
  ._ ≈∈⟨ laterˡ x≈y ⟩W       y≅z     = laterˡ (_ ≈∈⟨ x≈y ⟩W   y≅z)

  mutual

    correctW :
      ∀ {n} t {ρ : Env n} {c s} {k : Value → D VM.Value} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈∈W k v) →
      exec ⟨ comp t c , s , comp-env ρ ⟩ ≈∈W (⟦ t ⟧ ρ >>= k)
    correctW (con i) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (con i) ∷ s , comp-env ρ ⟩  ≈∈⟨ hyp (con i) ⟩W
      k (con i)                                  ∎)
    correctW (var x) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (lookup (comp-env ρ) x) ∷ s , comp-env ρ ⟩  ≡⟨ P.cong (λ v → exec ⟨ c , val v ∷ s , comp-env ρ ⟩) (lookup-hom x ρ) ⟩W
      exec ⟨ c , val (comp-val (lookup ρ x)) ∷ s , comp-env ρ ⟩  ≈∈⟨ hyp (lookup ρ x) ⟩W
      k (lookup ρ x)                                             ∎)
    correctW (ƛ t) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (comp-val (ƛ t ρ)) ∷ s , comp-env ρ ⟩  ≈∈⟨ hyp (ƛ t ρ) ⟩W
      k (ƛ t ρ)                                             ∎)
    correctW (t₁ · t₂) {ρ} {c} {s} {k} hyp =
      exec ⟨ comp t₁ (comp t₂ (app ∷ c)) , s , comp-env ρ ⟩           ≈∈⟨ correctW t₁ (λ v₁ → correctW t₂ (λ v₂ →
                                                                            ∙-correctW v₁ v₂ hyp)) ⟩W
      (⟦ t₁ ⟧ ρ >>= λ v₁ →  ⟦ t₂ ⟧ ρ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P  >>= k)  ≅⟨ ((⟦ t₁ ⟧ ρ ∎) >>=-cong λ _ → sym $ associative (⟦ t₂ ⟧ ρ)) ⟩
      (⟦ t₁ ⟧ ρ >>= λ v₁ → (⟦ t₂ ⟧ ρ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P) >>= k)  ≅⟨ sym $ associative (⟦ t₁ ⟧ ρ) ⟩
      (⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ >>= k)                                   ≅⟨ sym (·-comp t₁ t₂ >>=-cong λ v → k v ∎) ⟩
      (⟦ t₁ · t₂ ⟧ ρ >>= k)                                           ∎
    correctW (t₁ ∣ t₂) {ρ} {c} {s} {k} hyp =
      exec ⟨ comp t₁ c , s , comp-env ρ ⟩  ≈∈⟨ ∣ˡ (correctW t₁ hyp) ⟩W
      (⟦ t₁ ⟧ ρ >>= k) ∣ (⟦ t₂ ⟧ ρ >>= k)  ∎

    ∙-correctW :
      ∀ {n} v₁ v₂ {ρ : Env n} {c s} {k : Value → D VM.Value} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈∈W k v) →
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val v₁) ∷ s , comp-env ρ ⟩ ≈∈W
      ⟪ v₁ ∙ v₂ ⟫P >>= k
    ∙-correctW (con i)   v₂                 _   = ⌈ fail ⌉
    ∙-correctW (ƛ t₁ ρ₁) v₂ {ρ} {c} {s} {k} hyp =
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val (ƛ t₁ ρ₁)) ∷ s , comp-env ρ ⟩  ≈∈⟨ later (

        exec ⟨ comp t₁ [ ret ] , ret c (comp-env ρ) ∷ s , comp-env (v₂ ∷ ρ₁) ⟩                ≈∈⟨ correct t₁ (λ v → laterˡ (hyp v)) ⟩P
        (⟦ t₁ ⟧ (v₂ ∷ ρ₁) >>= k)                                                              ∎) ⟩W

      (⟪ ƛ t₁ ρ₁ ∙ v₂ ⟫P >>= k)                                                         ∎

  whnf : ∀ {x y} → x ≈∈P y → x ≈∈W y
  whnf (x ≈∈⟨ x≈y ⟩P y≅z) = x ≈∈⟨ whnf x≈y ⟩W y≅z
  whnf (correct t hyp)    = correctW t hyp

  mutual

    soundW : ∀ {x y} → x ≈∈W y → x ≈∈ y
    soundW ⌈ x≈y ⌉      = x≈y
    soundW (∣ˡ x≈y₁)    = ∣ˡ (soundW x≈y₁)
    soundW (later  x≈y) = later (♯ soundP x≈y)
    soundW (laterˡ x≈y) = laterˡ (soundW x≈y)

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

correct : ∀ t →
          exec ⟨ comp t [] , [] , [] ⟩ ≈∈
          ⟦ t ⟧ [] >>= λ v → return (comp-val v)
correct t =
  soundP $ Correctness.correct t (λ _ → ⌈ return ⌉)
  where open Correctness

------------------------------------------------------------------------
-- Type system (following Leroy and Grall)

infix 4 _⊢_∈_

data _⊢_∈_ {n} (Γ : Ctxt n) : Tm n → Ty → Set where
  con : ∀ {i} → Γ ⊢ con i ∈ nat
  var : ∀ {x} → Γ ⊢ var x ∈ lookup Γ x
  ƛ   : ∀ {t σ τ} (t∈ : ♭ σ ∷ Γ ⊢ t ∈ ♭ τ) → Γ ⊢ ƛ t ∈ σ ⇾ τ
  _·_ : ∀ {t₁ t₂ σ τ} (t₁∈ : Γ ⊢ t₁ ∈ σ ⇾ τ) (t₂∈ : Γ ⊢ t₂ ∈ ♭ σ) →
        Γ ⊢ t₁ · t₂ ∈ ♭ τ
  _∣_ : ∀ {t₁ t₂ σ} (t₁∈ : Γ ⊢ t₁ ∈ σ) (t₂∈ : Γ ⊢ t₂ ∈ σ) →
        Γ ⊢ t₁ ∣ t₂ ∈ σ

-- Ω is well-typed.

Ω-well-typed : (τ : Ty) → [] ⊢ Ω ∈ τ
Ω-well-typed τ =
  _·_ {σ = ♯ σ} {τ = ♯ τ} (ƛ (var · var)) (ƛ (var · var))
  where σ = ♯ σ ⇾ ♯ τ

-- The call-by-value fix-point combinator is also well-typed.

fix-well-typed :
  ∀ {σ τ} → [] ⊢ Z ∈ ♯ (♯ (σ ⇾ τ) ⇾ ♯ (σ ⇾ τ)) ⇾ ♯ (σ ⇾ τ)
fix-well-typed =
  ƛ (_·_ {σ = υ} {τ = ♯ _}
         (ƛ (var · ƛ (var · var · var)))
         (ƛ (var · ƛ (var · var · var))))
  where
  υ : ∞ Ty
  υ = ♯ (υ ⇾ ♯ _)

------------------------------------------------------------------------
-- Type soundness

-- WF-Value, WF-Env and WF-DV specify when a
-- value/environment/computation is well-formed with respect to a
-- given context (and type).

mutual

  data WF-Value : Ty → Value → Set where
    con : ∀ {i} → WF-Value nat (con i)
    ƛ   : ∀ {n Γ σ τ} {t : Tm (1 + n)} {ρ}
          (t∈ : ♭ σ ∷ Γ ⊢ t ∈ ♭ τ) (ρ-wf : WF-Env Γ ρ) →
          WF-Value (σ ⇾ τ) (ƛ t ρ)

  infixr 5 _∷_

  data WF-Env : ∀ {n} → Ctxt n → Env n → Set where
    []  : WF-Env [] []
    _∷_ : ∀ {n} {Γ : Ctxt n} {ρ σ v}
          (v-wf : WF-Value σ v) (ρ-wf : WF-Env Γ ρ) →
          WF-Env (σ ∷ Γ) (v ∷ ρ)

data WF-DV (σ : Ty) : D Value → Set where
  return : ∀ {v} (v-wf : WF-Value σ v) → WF-DV σ (return v)
  _∣_    : ∀ {x y}
           (x-wf : WF-DV σ x) (y-wf : WF-DV σ y) → WF-DV σ (x ∣ y)
  later  : ∀ {x} (x-wf : ∞ (WF-DV σ (♭ x))) → WF-DV σ (later x)

-- Variables pointing into a well-formed environment refer to
-- well-formed values.

lookup-wf : ∀ {n Γ ρ} (x : Fin n) → WF-Env Γ ρ →
            WF-Value (lookup Γ x) (lookup ρ x)
lookup-wf zero    (v-wf ∷ ρ-wf) = v-wf
lookup-wf (suc x) (v-wf ∷ ρ-wf) = lookup-wf x ρ-wf

-- If we can prove WF-DV σ x, then x does not "go wrong".

does-not-go-wrong : ∀ {σ x} → WF-DV σ x → ¬ now nothing ≈∈ x
does-not-go-wrong (return v-wf) ()
does-not-go-wrong (x-wf ∣ y-wf) (∣ˡ x↯)     = does-not-go-wrong x-wf x↯
does-not-go-wrong (x-wf ∣ y-wf) (∣ʳ y↯)     = does-not-go-wrong y-wf y↯
does-not-go-wrong (later x-wf)  (laterʳ x↯) =
  does-not-go-wrong (♭ x-wf) x↯

-- A variant of WF-DV, used to please the productivity checker.

infix  3 _⟨_⟩P
infixr 2 _≅⟨_⟩P_

data WF-DVP (σ : Ty) : D Value → Set where
  return      : ∀ {v} (v-wf : WF-Value σ v) → WF-DVP σ (return v)
  _∣_         : ∀ {x y} (x-wf : WF-DVP σ x) (y-wf : WF-DVP σ y) →
                WF-DVP σ (x ∣ y)
  later       : ∀ {x} (x-wf : ∞ (WF-DVP σ (♭ x))) → WF-DVP σ (later x)
  _>>=-congP_ : ∀ {τ x f}
                (x-wf : WF-DVP τ x)
                (f-wf : ∀ {v} → WF-Value τ v → WF-DVP σ (f v)) →
                WF-DVP σ (x >>= f)
  _≅⟨_⟩P_     : ∀ x {y} (x≅y : x ≅ y) (y-wf : WF-DVP σ y) → WF-DVP σ x
  _⟨_⟩P       : ∀ x (x-wf : WF-DVP σ x) → WF-DVP σ x

-- WF-DVP is sound with respect to WF-DV.

private

  -- WHNFs.

  data WF-DVW (σ : Ty) : D Value → Set where
    return : ∀ {v} (v-wf : WF-Value σ v) → WF-DVW σ (return v)
    _∣_    : ∀ {x y} (x-wf : WF-DVW σ x) (y-wf : WF-DVW σ y) →
             WF-DVW σ (x ∣ y)
    later  : ∀ {x} (x-wf : WF-DVP σ (♭ x)) → WF-DVW σ (later x)

  -- Functions which turn programs into WHNFs.

  trans-≅ : ∀ {σ x y} → x ≅ y → WF-DVW σ y → WF-DVW σ x
  trans-≅ fail            ()
  trans-≅ return          (return v-wf)   = return v-wf
  trans-≅ (x₁≅y₁ ∣ x₂≅y₂) (y₁-wf ∣ y₂-wf) = trans-≅ x₁≅y₁ y₁-wf
                                          ∣ trans-≅ x₂≅y₂ y₂-wf
  trans-≅ (later x≅y)     (later y-wf)    = later (_ ≅⟨ ♭ x≅y ⟩P y-wf)

  mutual

    _>>=-congW_ : ∀ {σ τ x f} →
                  WF-DVW σ x →
                  (∀ {v} → WF-Value σ v → WF-DVP τ (f v)) →
                  WF-DVW τ (x >>= f)
    return v-wf   >>=-congW f-wf = whnf (f-wf v-wf)
    (x-wf ∣ y-wf) >>=-congW f-wf = (x-wf >>=-congW f-wf)
                                 ∣ (y-wf >>=-congW f-wf)
    later x-wf    >>=-congW f-wf = later (x-wf >>=-congP f-wf)

    whnf : ∀ {σ x} → WF-DVP σ x → WF-DVW σ x
    whnf (return v-wf)         = return v-wf
    whnf (x-wf ∣ y-wf)         = whnf x-wf ∣ whnf y-wf
    whnf (later x-wf)          = later (♭ x-wf)
    whnf (x-wf >>=-congP f-wf) = whnf x-wf >>=-congW f-wf
    whnf (_ ≅⟨ x≅y ⟩P y-wf)    = trans-≅ x≅y (whnf y-wf)
    whnf (_ ⟨ x-wf ⟩P)         = whnf x-wf

sound : ∀ {σ x} → WF-DVP σ x → WF-DV σ x
sound = λ p → soundW (whnf p)
  where
  soundW : ∀ {σ x} → WF-DVW σ x → WF-DV σ x
  soundW (return v-wf) = return v-wf
  soundW (x-wf ∣ y-wf) = soundW x-wf ∣ soundW y-wf
  soundW (later x-wf)  = later (♯ sound x-wf)

-- Well-typed programs do not "go wrong".

mutual

  ⟦⟧-wf : ∀ {n Γ} (t : Tm n) {σ} → Γ ⊢ t ∈ σ →
          ∀ {ρ} → WF-Env Γ ρ → WF-DVP σ (⟦ t ⟧ ρ)
  ⟦⟧-wf (con i)   con             ρ-wf = return con
  ⟦⟧-wf (var x)   var             ρ-wf = return (lookup-wf x ρ-wf)
  ⟦⟧-wf (ƛ t)     (ƛ t∈)          ρ-wf = return (ƛ t∈ ρ-wf)
  ⟦⟧-wf (t₁ ∣ t₂) (t₁∈ ∣ t₂∈)     ρ-wf = ⟦⟧-wf t₁ t₁∈ ρ-wf
                                       ∣ ⟦⟧-wf t₂ t₂∈ ρ-wf
  ⟦⟧-wf (t₁ · t₂) (t₁∈ · t₂∈) {ρ} ρ-wf =
    ⟦ t₁ · t₂ ⟧ ρ          ≅⟨ ·-comp t₁ t₂ ⟩P
    ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ   ⟨ (⟦⟧-wf t₁ t₁∈ ρ-wf >>=-congP λ f-wf →
                               ⟦⟧-wf t₂ t₂∈ ρ-wf >>=-congP λ v-wf →
                               ∙-wf f-wf v-wf) ⟩P

  ∙-wf : ∀ {σ τ f v} →
         WF-Value (σ ⇾ τ) f → WF-Value (♭ σ) v →
         WF-DVP (♭ τ) ⟪ f ∙ v ⟫P
  ∙-wf (ƛ t₁∈ ρ₁-wf) v₂-wf = later (♯ ⟦⟧-wf _ t₁∈ (v₂-wf ∷ ρ₁-wf))

type-soundness : ∀ {t : Tm 0} {σ} →
                 [] ⊢ t ∈ σ → ¬ now nothing ≈∈ ⟦ t ⟧ []
type-soundness t∈ = does-not-go-wrong (sound (⟦⟧-wf _ t∈ []))


module WellScopedTermsModel where

open import Library
open import WellScopedTerms
open import RMonads
open import RMonads.REM
open import Categories.Sets

_<<_ : ∀{n}{X : Set} → (Fin n → X) → X → Fin (suc n) → X
(f << x) zero     = x
(f << x) (suc i) = f i

record LambdaModel : Set₁ where
  field S      : Set
  Env = λ n → Fin n → S
  field eval   : ∀{n} → Env n → Tm n → S
        ap     : S → S → S
        lawvar : ∀{n}{i : Fin n}{γ : Env n} →
                 eval γ (var i) ≅ γ i
        lawapp : ∀{n}{t u : Tm n}{γ : Env n} →
                 eval γ (app t u) ≅ ap (eval γ t) (eval γ u)
        lawlam : ∀{n}{t : Tm (suc n)}{γ : Env n}{s : S} →
                 ap (eval γ (lam t)) s ≅ eval (γ << s) t
        lawext : ∀{f g : S} → ((a : S) → ap f a ≅ ap g a) → f ≅ g

module Model (l : LambdaModel) where
  open LambdaModel l

  wk<< : ∀{m n}(α  : Ren m n)(β : Env n)
            (v : S) → (y : Fin (suc m)) →
            ((β ∘ α) << v) y ≅ (β << v) (wk α y)
  wk<< α β v zero    = refl
  wk<< α β v (suc i) = refl

  reneval : ∀{m n}(α : Ren m n)(β : Env n)
            (t : Tm m) → eval (β ∘ α) t ≅ (eval β ∘ ren α) t
  reneval α β (var i) =
    proof
    eval (β ∘ α) (var i)
    ≅⟨ lawvar ⟩
    β (α i)
    ≅⟨ sym lawvar ⟩
    eval β (var (α i)) ∎
  reneval α β (lam t) = lawext λ a →
    proof
    ap (eval (β ∘ α) (lam t)) a
    ≅⟨ lawlam ⟩
    eval ((β ∘ α) << a) t
    ≅⟨ cong (λ (f : Env _) → eval f t) (ext (wk<< α β a)) ⟩
    eval ((β << a) ∘ wk α) t
    ≅⟨ reneval (wk α) (β << a) t ⟩
    eval (β << a) (ren (wk α) t)
    ≅⟨ sym lawlam ⟩
    ap (eval β (lam (ren (wk α) t))) a
    ∎
  reneval α β (app t u) =
    proof
    eval (β ∘ α) (app t u)
    ≅⟨ lawapp ⟩
    ap (eval (β ∘ α) t) (eval (β ∘ α) u)
    ≅⟨ cong₂ ap (reneval α β t) (reneval α β u) ⟩
    ap (eval β (ren α t)) (eval β (ren α u))
    ≅⟨ sym lawapp ⟩
    eval β (app (ren α t) (ren α u))
    ∎

  lift<< : ∀{m n}(γ  : Sub m n)(α : Env n)
           (a  : S)(i : Fin (suc m)) →
           ((eval α ∘ γ ) << a) i ≅ (eval (α << a) ∘ lift γ) i
  lift<< γ α a zero =
    proof
    a
    ≅⟨ sym lawvar ⟩
    eval (α << a) (var zero)
    ∎
  lift<< γ α a (suc i) =
    proof
    eval α (γ i)
    ≡⟨⟩
    eval ((α << a) ∘ suc) (γ i)
    ≅⟨ reneval suc (α << a) (γ i) ⟩
    eval (α << a) (ren suc (γ i))
    ∎

  subeval : ∀{m n}(t : Tm m)(γ : Sub m n)(α : Env n) →
            eval (eval α ∘ γ) t  ≅ (eval α ∘ sub γ) t
  subeval (var i)   γ α =
    proof
    eval (eval α ∘ γ) (var i)
    ≅⟨ lawvar ⟩
    eval α (γ i)
    ∎
  subeval (lam t)   γ α = lawext λ a →
    proof
    ap (eval (eval α ∘ γ) (lam t)) a
    ≅⟨ lawlam ⟩
    eval ((eval α ∘ γ) << a) t
    ≅⟨ cong (λ (f : Env _) → eval f t) (ext (lift<< γ α a)) ⟩
    eval (eval (α << a) ∘ lift γ) t
    ≅⟨ subeval t (lift γ) (α << a) ⟩
    eval (α << a) (sub (lift γ) t)
    ≅⟨ sym lawlam ⟩
    ap (eval α (lam (sub (lift γ) t))) a
    ∎
  subeval (app t u) γ α =
    proof
    eval (eval α ∘ γ) (app t u)
    ≅⟨ lawapp ⟩
    ap (eval (eval α ∘ γ) t) (eval (eval α ∘ γ) u)
    ≅⟨ cong₂ ap (subeval t γ α) (subeval u γ α) ⟩
    ap (eval α (sub γ t)) (eval α (sub γ u))
    ≅⟨ sym lawapp ⟩
    eval α (app (sub γ t) (sub γ u))
    ∎

  TmRAlg : RAlg TmRMonad
  TmRAlg = record{
    acar  = S;
    astr  = eval;
    alaw1 = ext λ _ → sym lawvar;
    alaw2 =  ext λ t → subeval t _ _}

module VEnv where
  open import Delay
  open import Size

  mutual
    Env : ℕ → Set
    Env n = Fin n → Val

    data Val : Set where
      clo : ∀{n} → Env n → Tm (suc n) → Val

  -- the RAlg is expecting a env containing 'values' here the values
  -- evaluator takes an env of undelayed values and makes a delayed
  -- values.
  mutual
    ev : ∀{i n} → Env n → Tm n → Delay Val i
    ev γ (var x) = now (γ x)
    ev γ (lam t) = now (clo γ t)
    ev γ (app t u) = ev γ t >>= λ f → ev γ u >>= λ v → f $$ v

    _∞$$_ : ∀{i} → Val → Val → ∞Delay Val i
    force (clo γ t ∞$$ v) = ev (γ << v) t

    _$$_ : ∀{i} → Val → Val → Delay Val i
    f $$ v = later (f ∞$$ v)

module FusedVals where
  open import Size
  mutual
    data Env (i : Size) : (n : ℕ) → Set where
      ε   : Env i zero
      _,_ : ∀ {n} (ρ : Env i n) (v : Val i) → Env i (suc n)

    data Val (i : Size) : Set where
      lam   : forall {n} (t : Tm (suc n)) (ρ : Env i n)  → Val i
      later : (v∞ : ∞Val i)                   → Val i

    record ∞Val (i : Size) : Set where
      coinductive
      constructor ∞val
      field
        vforce : {j : Size< i} → Val j

  open ∞Val

  lookup : ∀{i n} -> Env i n -> Fin n -> Val i
  lookup (ρ , v) zero    = v
  lookup (ρ , v) (suc i) = lookup ρ i

  tabulate : ∀{i n} -> (Fin n -> Val i) -> Env i n
  tabulate {n = zero}  f = ε
  tabulate {n = suc n} f = (tabulate {n = n} (f ∘ suc)) , f zero

  mutual
    ev : ∀{i n} -> Env i n -> Tm n -> Val i
    ev ρ (var x)   = lookup ρ x
    ev ρ (lam t)   = lam t ρ
    ev ρ (app t u) = ev ρ t $$ ev ρ u

    _$$_ : ∀{i} → Val i → Val i → Val i
    f $$ v = later (f ∞$$ v)

    _∞$$_ : ∀{i} → Val i → Val i → ∞Val i
    vforce (lam t ρ ∞$$ v) = ev (ρ , v) t
    vforce (later p ∞$$ v) = later (vforce p ∞$$ v)

{-
    FRAlg : ∀ {i} -> RAlg TmRMonad
    FRAlg {i} = ralg
      (Val i)
      (ev ∘ tabulate)
      {!!}
      {!!}
-}


open import Relation.Binary using (Decidable; DecSetoid)
open import Level

module CP.Session2 {a} (ChanSetoid : DecSetoid zero a) (Type : Set) where

Chan : Set
Chan = DecSetoid.Carrier ChanSetoid

_≟Chan_ = DecSetoid._≟_ ChanSetoid
_≈Chan_ = DecSetoid._≈_ ChanSetoid
_≉Chan_ = DecSetoid._≉_ ChanSetoid

infixl 5 _,_∶_
data Session : Set a where
    _,_∶_ : Session → Chan → Type → Session
    _++_ : Session → Session → Session
    ∅ : Session

-- open import Data.Bool hiding (_≟_)

-- open import Data.Empty
-- open import Data.Unit
open import Relation.Nullary
open import Relation.Nullary.Decidable

-- open import Relation.Binary.PropositionalEquality

infix 4 _∋_
data _∋_ : (Γ : Session) → (x : Chan) → Set a where
  here  : ∀ {Γ x y A} → x ≈Chan y         → Γ , y ∶ A ∋ x
  there : ∀ {Γ x y A} →             Γ ∋ x → Γ , y ∶ A ∋ x
  left  : ∀ {Γ Δ x}               → Γ ∋ x → Γ ++ Δ    ∋ x
  right : ∀ {Γ Δ x}               → Δ ∋ x → Γ ++ Δ    ∋ x

infix 4 _∋?_
_∋?_ : (Γ : Session) → (x : Chan) → Dec (Γ ∋ x)
Γ , y ∶ A ∋? x with x ≟Chan y
... | yes x≈y = yes (here x≈y)
... | no ¬x≈y with Γ ∋? x
... | yes Γ∋x = yes (there Γ∋x)
... | no ¬Γ∋x = no (λ where (here x≈y) → ¬x≈y x≈y
                            (there Γ∋x) → ¬Γ∋x Γ∋x)
(Γ ++ Δ) ∋? x with Γ ∋? x
... | yes Γ∋x = yes (left Γ∋x)
... | no ¬Γ∋x with Δ ∋? x
... | yes Δ∋x = yes (right Δ∋x)
... | no ¬Δ∋x = no (λ where (left Γ∋x) → ¬Γ∋x Γ∋x
                            (right Δ∋x) → ¬Δ∋x Δ∋x)
∅ ∋? x = no (λ ())

open import Data.Product
open import Data.Empty using (⊥-elim)

infix 4 _≈_
_≈_ : Session → Session → Set a
Γ ≈ Δ = (∀ x → Γ ∋ x → Δ ∋ x) × (∀ x → Δ ∋ x → Γ ∋ x)

∅∌x : ∀ {x} → ¬ ∅ ∋ x
∅∌x ()

open DecSetoid ChanSetoid hiding (_≈_)

∅-empty : ∀ {Δ x A} → ¬ ∅ ≈ Δ , x ∶ A
∅-empty {Δ} {x} (P , Q) = ∅∌x (Q x (here refl))

<<<<<<< HEAD
swap : ∀ {Γ x y A B} → Γ , x ∶ A , y ∶ B ≈ Γ , y ∶ B , x ∶ A
swap {Γ} {x} {y} {A} {B} = to , from
  where
    to : ∀ v → Γ , x ∶ A , y ∶ B ∋ v → Γ , y ∶ B , x ∶ A ∋ v
    to v (here P) = there (here P)
    to v (there (here P)) = here P
    to v (there (there P)) = there (there P)

    from : ∀ v → Γ , y ∶ B , x ∶ A ∋ v → Γ , x ∶ A , y ∶ B ∋ v
    from v (here P) = there (here P)
    from v (there (here P)) = here P
    from v (there (there P)) = there (there P)

-- contract : ∀ {Γ x y A B} →  → Γ , x ∶ A ≈ Γ
-- contract
-- strengthen : ∀ {Γ x A v} → Γ , x ∶ A ∋ v → x ≉ v → Γ ∋ v
-- strengthen (here x≈v) x≉v = ⊥-elim (x≉v (sym x≈v))
-- strengthen (there P) x≉v = P
=======
>>>>>>> 40f82fd098a0c30c87ea0056baa48152aefae1f6

lookup : (Γ : Session) (x : Chan) → Dec (∃[ Δ ] ∃[ A ] (Γ ≈ (Δ , x ∶ A)))
lookup (Γ , y ∶ A) x with x ≟Chan y
... | yes x≈y = yes (Γ , A  , (λ where v (here v≈y) → here (trans v≈y (sym x≈y))
                                       v (there Γ∷y∋v) → there Γ∷y∋v)
                             , λ where v (here v≈x) → here (trans v≈x x≈y)
                                       v (there Γ∷x∋v) → there Γ∷x∋v)
... | no ¬x≈y with lookup Γ x
... | yes (Δ , B , Γ≈Δ,x∶B) = yes (Δ , y ∶ A , B  , (λ where v (here v≈y) → there (here v≈y)
<<<<<<< HEAD
                                                             v (there Γ∋v) → proj₁ swap v (there (proj₁ Γ≈Δ,x∶B v Γ∋v)))
                                                  , λ where v (here v≈x) → there (proj₂ Γ≈Δ,x∶B v (here v≈x))
                                                            v (there (here v≈y)) → here v≈y
                                                            v (there (there Δ∋v)) → there (proj₂ Γ≈Δ,x∶B v (there Δ∋v)))
... | no ¬P = no (λ (Δ , B , Q) → ¬P (Δ , B , {!   !}))
  -- (λ where v P → proj₁ Q v (there P))
  --                                           , (λ where v P → {! proj₂ Q v P   !})))
=======
                                                             v (there Γ∋v) → {!   !}) , {!   !})
... | no P = {!   !}
>>>>>>> 40f82fd098a0c30c87ea0056baa48152aefae1f6
lookup (Γ ++ Δ) x = {!   !}
lookup ∅ x = no (λ where (Δ , A , P) → ∅-empty P)

-- ... | yes x≈y = yes (Γ , A , λ v → (λ where (here v≈y) → here (trans v≈y (sym x≈y))
--                                             (there Γ∋v) → there Γ∋v) -- (λ v≈x → v≉y (trans v≈x x≈y))
--                                   , λ where (here v≈x) → here (trans v≈x x≈y)
--                                             (there Γ∋v) → there Γ∋v) -- (λ v≈y → v≉x (trans v≈y (sym x≈y)))
-- ... | no ¬x≈y with lookup Γ x
-- ... | yes (Δ , B , Γ≈Δ,x∶B) = yes (Δ , y ∶ A , B , λ v → (λ where (here v≈y) → there (here v≈y) -- (λ v≈x → ¬x≈y (trans (sym v≈x) v≈y))
--                                                                   (there Γ∋v) → lemma-1 (proj₁ (Γ≈Δ,x∶B v) Γ∋v))
--                                                         , λ where (here v≈x) → there (proj₂ (Γ≈Δ,x∶B v) (here v≈x)) -- (λ v≈y → ¬x≈y (trans (sym v≈x) v≈y))
--                                                                   (there Δ,y∶A∋y) → ,-weakening y A (proj₂ (Γ≈Δ,x∶B v)) (lemma-1 Δ,y∶A∋y))
--     where
--       lemma-1 : ∀ {Γ x y z A B} → Γ , x ∶ A ∋ z → Γ , y ∶ B , x ∶ A ∋ z
--       lemma-1 (here z≈x) = here z≈x
--       lemma-1 (there Γ∋z) = there (there Γ∋z)

--       ,-weakening : ∀ {Γ Δ v} x A → (Γ ∋ v → Δ ∋ v) → (Γ , x ∶ A ∋ v → Δ , x ∶ A ∋ v)
--       ,-weakening x A f (here v≈x) = here v≈x
--       ,-weakening x A f (there Γ∋v) = there (f Γ∋v)

-- ... | no ¬P = no λ Q → {!   !}

-- -- GOAL : Γ , y ∶ A ≉ CTX , x ∶ TYPE
-- -- ¬P     Γ         ≉ CTX , x : TYPE

--   -- no (λ (Δ , B , Q) → ¬P (Δ , B , {!  Q !}))
--   -- λ v → (λ Γ∋v → proj₁ (Q v) (there Γ∋v))
--   --                                                 , (λ Δ,x∶B∋v → {! proj₂ (Q v) Δ,x∶B∋v  !})))
--   --                       → (λ Γ∋v → proj₁ (Q v) (there Γ∋v))
--   --                       , λ Δ,x∶B∋v → {! (proj₂ (Q v) Δ,x∶B∋v)  !}))
--                           -- where
--                           --   (here P)  → strengthen (proj₂ (Q v) (here P)) (λ y≈v → ¬x≈y (sym (trans y≈v P)))
--                           --   (there P) → {!   !}))

--     where

--       -- GOAL : Δ , x ∶ B           →       Γ

--       -- Q v :  Δ , x ∶ B          <≈>      Γ , y ∶ A

--       -- -- ¬P Δ A =              Γ   <=>  Δ , x ∶ A
--       --        (x₁ : Carrier) →
--       --        Σ (Γ ∋ x₁ → Δ₁ , x ∶ A₁ ∋ x₁) (λ x₂ → Δ₁ , x ∶ A₁ ∋ x₁ → Γ ∋ x₁)))

--       strengthen : ∀ {Γ x A v} → Γ , x ∶ A ∋ v → x ≉ v → Γ ∋ v
--       strengthen (here x≈v) x≉v = ⊥-elim (x≉v (sym x≈v))
--       strengthen (there P) x≉v = P

--       temp : ∀ Δ B v → (Δ ∋ v) → (f : Δ , x ∶ B ∋ v → Γ , y ∶ A ∋ v) → Γ ∋ v
--       temp Δ B v Δ∋v f with v ≟Chan x
--       ... | yes v≈x = strengthen (f (here v≈x)) (λ y≈v → ¬x≈y (sym (trans y≈v v≈x)))
--       ... | no ¬v≈x with y ≟Chan v
--       ... | yes y≈v = {!   !}
--       ... | no ¬y≈v = strengthen (f (there Δ∋v)) ¬y≈v
--         -- strengthen (f (there Δ∈v)) λ y≈v → {!   !}

--       -- lemma : G

-- lookup (Γ ++ Δ) x = {!   !}
-- lookup ∅ x = no (λ where (Δ , A , P) → ∅-empty P)

-- -- _≈?_ : (Γ Δ : Session) → Dec (Γ ≈ Δ)
-- -- Γ ≈? Δ = {!   !}
-- -- empty : ∀ {Γ x A} → ¬ (∅ ≈ (Γ , x ∶ A))
-- -- empty {Γ} {x} {A} P with x ≟ x
-- -- ... | no ¬p = {!   !}
-- -- ... | yes p = {!   !}

-- -- lookup : (Γ : Session) (x : Chan) → Dec (∃[ Δ ] ∃[ A ] (Γ ≈ (Δ , x ∶ A)))
-- -- lookup (Γ , y ∶ A) x = {!   !}
-- -- lookup (Γ ++ Δ) x with lookup Γ x
-- -- ... | yes p = {!   !}
-- -- ... | no ¬p = {!   !}
-- -- lookup ∅ x = no (λ where (Γ , A , P) → {! P x  !})


module Issue553a where

data D : Set where
  d₁ d₂ : D

data E : Set where

module M (A : Set) where

  data B : Set where
    b : D → B

  T : B → Set
  T (b d₁) = E
  T (b d₂) = E

open M E

g : (d : D) → T (b d) → D
g d t with d₁
g d t | d′ = d′  -- Unsolved meta-variable, no constraints.


{-# OPTIONS --cubical --safe --guardedness #-}

module Data.PolyP.RecursionSchemes where

open import Function hiding (_⟨_⟩_)
open import Data.Sum
open import Data.Sigma
open import Level
open import Data.Unit
open import Data.Nat
open import Data.Vec.Iterated
open import Data.Empty
open import WellFounded
open import Literals.Number
open import Data.Fin.Indexed.Literals
open import Data.Fin.Indexed.Properties
open import Data.Fin.Indexed
open import Data.Nat.Literals
open import Data.Maybe
open import Path

open import Data.PolyP.Universe

---------------------------------------------------------------------------------
--
-- Helpers for Termination
--
---------------------------------------------------------------------------------

-- This is just the identity type. We need to use it because, if --without-K is
-- turned on, Agda will only use an argument to a function to prove structural
-- descent if that argument is a concrete data type.
--
--   wont-pass : (x : Bool) → (if x then ℕ else ℕ) → ℕ
--   wont-pass false zero    = zero
--   wont-pass false (suc n) = wont-pass true n
--   wont-pass true  zero    = zero
--   wont-pass true  (suc n) = wont-pass false n
--
-- Even though we're clearly structurally descending on the second argument
-- there, Agda won't use it unless we make it concrete, like so:
--
--   will-pass : (x : Bool) → <! (if x then ℕ else ℕ) !> → ℕ
--   will-pass false [! zero  !] = zero
--   will-pass false [! suc n !] = will-pass true  [! n !]
--   will-pass true  [! zero  !] = zero
--   will-pass true  [! suc n !] = will-pass false [! n !]
record <!_!> (A : Type) : Type  where
  eta-equality
  constructor [!_!]
  field !! : A
open <!_!>

-- For the map and cata functions to be structurally
-- terminating, we can't do things like:
--
--   cata f = f ∘ fmap (cata f) ∘ unwrap
--
-- So instead we need to carry a stack of all of the functors
-- we're under at any given point, and pattern match on that to
-- tell whether we should do f or fmap f.

data Layers (n : ℕ) : ℕ → Type₁ where
  []   : Layers n n
  _∷_  :  Functor (1 + m) →
          Layers n m →
          Layers n (1 + m)

_++∙_ : Layers n m → Params n → Params m
[]       ++∙ ys = ys
(x ∷ xs) ++∙ ys = let zs = xs ++∙ ys in μ x zs ∷ zs
infixr 5 _∷_ _++∙_

---------------------------------------------------------------------------------
--
-- Mapping
--
---------------------------------------------------------------------------------

module Mapping {m} {As Bs : Params m} (f : (i : Fin m) → As [ i ] → Bs [ i ]) where
  mapRec : ∀ (F : Functor n) (Fs : Layers m n) →
          <! ⟦ F ⟧ (Fs ++∙ As) !> → ⟦ F ⟧ (Fs ++∙ Bs)
  mapRec (F ⊕ G)    Fs       [! inl x  !] = inl (mapRec F Fs [! x !])
  mapRec (F ⊕ G)    Fs       [! inr x  !] = inr (mapRec G Fs [! x !])
  mapRec (F ⊗ G)    Fs       [! x , y  !] = mapRec F Fs [! x !] , mapRec G Fs [! y !]
  mapRec μ⟨ F ⟩     Fs       [! ⟨ xs ⟩ !] = ⟨ mapRec F (F ∷ Fs) [! xs !] ⟩
  mapRec (! i     ) []       [! xs     !] = f i xs
  mapRec (! f0    ) (F ∷ Fs) [! ⟨ xs ⟩ !] = ⟨ mapRec F (F ∷ Fs) [! xs !] ⟩
  mapRec (! (fs i)) (F ∷ Fs) [! xs     !] = mapRec (! i) Fs [! xs !]
  mapRec ①          Fs       _            = tt

mapParamAt : (i : Fin n) → (As [ i ] → A) → (j : Fin n) → As [ j ] → As [ i ]≔ A [ j ]
mapParamAt f0     f f0     x = f x
mapParamAt f0     f (fs _) x = x
mapParamAt (fs _) f f0     x = x
mapParamAt (fs i) f (fs j) x = mapParamAt i f j x

map : ∀ F → (i : Fin n) → (As [ i ] → A) → ⟦ F ⟧ As → ⟦ F ⟧ (As [ i ]≔ A)
map F i f xs = Mapping.mapRec (mapParamAt i f) F [] [! xs !]

module MapComp
    {m} {As Bs Cs : Params m}
    (f : (i : Fin m) → Bs [ i ] → Cs [ i ])
    (g : (i : Fin m) → As [ i ] → Bs [ i ])
  where
  open Mapping

  mapComp : ∀ (F : Functor n) (Fs : Layers m n) (xs : <! ⟦ F ⟧ (Fs ++∙ As) !>) →
            mapRec f F Fs [! mapRec g F Fs xs !] ≡ mapRec (λ i → f i ∘ g i) F Fs xs
  mapComp (F ⊕ G) Fs [! inl x !] = cong inl (mapComp F Fs [! x !])
  mapComp (F ⊕ G) Fs [! inr x !] = cong inr (mapComp G Fs [! x !])
  mapComp (F ⊗ G) Fs [! x , y !] = cong₂ _,_ (mapComp F Fs [! x !]) (mapComp G Fs [! y !])
  mapComp μ⟨ F ⟩  Fs [! ⟨ x ⟩ !] = cong ⟨_⟩ (mapComp F (F ∷ Fs) [! x !])
  mapComp (! i)      []       [! xs    !] = refl
  mapComp (! f0)     (F ∷ Fs) [! ⟨ x ⟩ !] = cong ⟨_⟩ (mapComp F (F ∷ Fs) [! x !])
  mapComp (! (fs i)) (F ∷ Fs) [! xs    !] = mapComp (! i) Fs [! xs !]
  mapComp ①      Fs xs = refl

map-comp : ∀ (F : Functor (suc n)) → (f : B → C) → (g : A → B) → (x : ⟦ F ⟧ (A ∷ As)) →
           map F f0 f (map F f0 g x) ≡ map F f0 (f ∘ g) x
map-comp F f g x =
  MapComp.mapComp (mapParamAt f0 f) (mapParamAt f0 g) F [] [! x !] ;
  cong (λ c → Mapping.mapRec c F [] [! x !])
    (funExt λ { f0 → refl ; (fs x) → refl } )

module MapId {m} {As : Params m}
  where
  open Mapping {m = m} {As = As} {Bs = As}

  mapId : ∀ (F : Functor n) (Fs : Layers m n) (xs : <! ⟦ F ⟧ (Fs ++∙ As) !>) →
            mapRec (λ _ x → x) F Fs xs ≡ xs .!!
  mapId (F ⊕ G) Fs [! inl x !] = cong inl (mapId F Fs [! x !])
  mapId (F ⊕ G) Fs [! inr x !] = cong inr (mapId G Fs [! x !])
  mapId (F ⊗ G) Fs [! x , y !] = cong₂ _,_ (mapId F Fs [! x !]) (mapId G Fs [! y !])
  mapId μ⟨ F ⟩  Fs [! ⟨ x ⟩ !] = cong ⟨_⟩ (mapId F (F ∷ Fs) [! x !])
  mapId (! i)      []       [! xs    !] = refl
  mapId (! f0)     (F ∷ Fs) [! ⟨ x ⟩ !] = cong ⟨_⟩ (mapId F (F ∷ Fs) [! x !])
  mapId (! (fs i)) (F ∷ Fs) [! xs    !] = mapId (! i) Fs [! xs !]
  mapId ①      Fs xs = refl

map-id : (F : Functor (suc n)) → (x : ⟦ F ⟧ (A ∷ As)) → map F f0 id x ≡ x
map-id F x =
  cong
    (λ c → Mapping.mapRec c F [] [! x !])
    (funExt (λ { f0 → refl ; (fs i) → refl})) ;
  MapId.mapId F [] [! x !]

---------------------------------------------------------------------------------
--
-- Categorical
--
---------------------------------------------------------------------------------
module Categorical (F : Functor (suc n)) (As : Params n) where
  infix 4 _≗_
  _≗_ : (A → B) → (A → B) → Set _
  f ≗ g = ∀ x → f x ≡ g x
  {-# INLINE _≗_ #-}

  Alg : Type₁
  Alg = Σ[ A ⦂ Type ] × (⟦ F ⟧ (A ∷ As) → A)

  -- Hom
  _⟶_ : Alg → Alg → Type
  (A , a) ⟶ (B , b) = Σ[ h ⦂ (A → B) ] × (h ∘ a ≗ b ∘ map F f0 h)

  variable
    X Y Z : Alg

  _∙_ : (Y ⟶ Z) → (X ⟶ Y) → (X ⟶ Z)
  (f ∙ g) .fst = f .fst ∘ g .fst
  _∙_ {Y = Y}  {Z = Z} {X = X} f g .snd x =
    cong (f .fst) (g .snd x) ;
    f .snd (map F f0 (g .fst) x) ;
    cong (Z .snd) (map-comp F (f .fst) (g .fst) x)

  id′ : X ⟶ X
  id′ .fst = id
  id′ {X = X} .snd x = cong (X .snd) (sym (map-id F x))

---------------------------------------------------------------------------------
--
-- Catamorphisms
--
---------------------------------------------------------------------------------

module Cata {k} {F : Functor (suc k)} {As : Params k} (alg : ⟦ F ⟧ (A ∷ As) → A) where
  cataRec : (G : Functor n) (Gs : Layers (suc m) n) →
            <! ⟦ G ⟧ (Gs ++∙ μ F As ∷ Bs) !> → ⟦ G ⟧ (Gs ++∙ A ∷ Bs)
  cataRec (G₁ ⊕ G₂)  Gs       [! inl x !] = inl (cataRec G₁ Gs [! x !])
  cataRec (G₁ ⊕ G₂)  Gs       [! inr x !] = inr (cataRec G₂ Gs [! x !])
  cataRec (G₁ ⊗ G₂)  Gs       [! x , y !] = cataRec G₁ Gs [! x !] , cataRec G₂ Gs [! y !]
  cataRec μ⟨ G ⟩     Gs       [! ⟨ x ⟩ !] = ⟨ cataRec G (G ∷ Gs) [! x !] ⟩
  cataRec (! f0    ) []       [! ⟨ x ⟩ !] = alg (cataRec F [] [! x !])
  cataRec (! (fs i)) []       [! x     !] = x
  cataRec (! (fs i)) (G ∷ Gs) [! x     !] = cataRec (! i) Gs [! x !]
  cataRec (! f0    ) (G ∷ Gs) [! ⟨ x ⟩ !] = ⟨ cataRec G (G ∷ Gs) [! x !] ⟩
  cataRec ①          Gs       [! _     !] = tt

module _ {F : Functor (suc n)} {As : Params n} where
  cata : (⟦ F ⟧ (A ∷ As) → A) → μ F As → A
  cata alg xs = Cata.cataRec alg {Bs = As} (! f0) [] [! xs !]

module CataId {k} {F : Functor (suc k)} {As : Params k} where
  cataRecId : (G : Functor n) (Gs : Layers (suc m) n) →
              (x : <! ⟦ G ⟧ (Gs ++∙ μ F As ∷ Bs) !>) → Cata.cataRec ⟨_⟩ G Gs x ≡ !! x
  cataRecId (G₁ ⊕ G₂)  Gs       [! inl x !] = cong inl (cataRecId G₁ Gs [! x !])
  cataRecId (G₁ ⊕ G₂)  Gs       [! inr x !] = cong inr (cataRecId G₂ Gs [! x !])
  cataRecId (G₁ ⊗ G₂)  Gs       [! x , y !] = cong₂ _,_ (cataRecId G₁ Gs [! x !]) (cataRecId G₂ Gs [! y !])
  cataRecId μ⟨ G ⟩     Gs       [! ⟨ x ⟩ !] = cong ⟨_⟩ (cataRecId G (G ∷ Gs) [! x !])
  cataRecId (! f0    ) []       [! ⟨ x ⟩ !] = cong ⟨_⟩ (cataRecId F [] [! x !])
  cataRecId (! (fs i)) []       [! x     !] = refl
  cataRecId (! (fs i)) (G ∷ Gs) [! x     !] = cataRecId (! i) Gs [! x !]
  cataRecId (! f0    ) (G ∷ Gs) [! ⟨ x ⟩ !] = cong ⟨_⟩ ( cataRecId G (G ∷ Gs) [! x !] )
  cataRecId ①          Gs       [! _     !] = refl

module _ {F : Functor (suc n)} {As : Params n} where
  cataId : (x : μ F As) → cata ⟨_⟩ x ≡ x
  cataId x = CataId.cataRecId (! f0) [] [! x !]
---------------------------------------------------------------------------------
--
-- Eliminators
--
---------------------------------------------------------------------------------

module Eliminator {As : Params k}
         {F : Functor (suc k)}
         (P : μ F As → Type)
         (f : (x : ⟦ F ⟧ ((∃ x × P x) ∷ As)) → P ⟨ map F f0 fst x ⟩)
         where
  open import Path
  open Mapping
  open Cata

  alg : ⟦ F ⟧ ((∃ x × P x) ∷ As) → ∃ x × P x
  alg x = ⟨ map F f0 fst x ⟩ , f x

  mutual
    elimRec : (G : Functor n) (Gs : Layers (suc m) n) →
              (x : <! ⟦ G ⟧ (Gs ++∙ μ F As ∷ Bs) !>) →
              mapRec (mapParamAt f0 fst) G Gs [! cataRec alg G Gs x !] ≡ !! x
    elimRec (G₁ ⊕ G₂)   Gs       [! inl x !] = cong inl (elimRec G₁ Gs [! x !])
    elimRec (G₁ ⊕ G₂)   Gs       [! inr x !] = cong inr (elimRec G₂ Gs [! x !])
    elimRec (G₁ ⊗ G₂)   Gs       [! x , y !] = cong₂ _,_ (elimRec G₁ Gs [! x !]) (elimRec G₂ Gs [! y !])
    elimRec μ⟨ G ⟩      Gs       [! ⟨ x ⟩ !] = cong ⟨_⟩  (elimRec G (G ∷ Gs) [! x !])
    elimRec (! f0    )  []       [! ⟨ x ⟩ !] = cong ⟨_⟩ (elimRec F [] [! x !])
    elimRec (! (fs i))  []       [! x     !] = refl
    elimRec (! (fs i))  (G ∷ Gs) [! x     !] = elimRec (! i) Gs [! x !]
    elimRec (! f0    )  (G ∷ Gs) [! ⟨ x ⟩ !] = cong ⟨_⟩ (elimRec G (G ∷ Gs) [! x !])
    elimRec ①          Gs        [! _     !] = refl

  elim : ∀ x → P x
  elim x = subst P (elimRec {Bs = As} (! f0) [] [! x !]) (snd (cata alg x))

module _ {F : Functor (suc n)} where
  elim : (P : μ F As → Type) →
         ((x : ⟦ F ⟧ ((∃ x × P x) ∷ As)) → P ⟨ map F f0 fst x ⟩) →
         (x : μ F As) → P x
  elim = Eliminator.elim

module AlgIsomorphism {F : Functor (suc n)} {As : Params n} where
  Alg : Type → Type
  Alg A = ⟦ F ⟧ (A ∷ As) → A

  AsAlg : Type₁
  AsAlg = ∀ A → Alg A → A

  open import Function.Isomorphism

  toAlg : μ F As → AsAlg
  toAlg xs A alg = cata alg xs
  {-# INLINE toAlg #-}

  fromAlg : AsAlg → μ F As
  fromAlg f = f _ ⟨_⟩
  {-# INLINE fromAlg #-}

  rinv : (x : μ F As) → fromAlg (toAlg x) ≡ x
  rinv = cataId

  -- think you need parametricity for this
  -- linv : (x : AsAlg) (A : Type) (alg : Alg A) → toAlg (fromAlg x) A alg ≡ x A alg
  -- linv x A alg = {!!}

  -- isom : AsAlg ⇔ μ F As
  -- isom .fun = fromAlg
  -- isom .inv = toAlg
  -- isom .rightInv = rinv
  -- isom .leftInv x = funExt λ A → funExt λ alg → linv x A alg

---------------------------------------------------------------------------------
--
-- Anamorphisms
--
---------------------------------------------------------------------------------

-- Coinductive fixpoint
record ν (F : Functor (suc n)) (As : Params n) : Type where
  coinductive; constructor ⟪_⟫
  field unfold : ⟦ F ⟧ (ν F As ∷ As)
open ν public

-- The "proper" anamorphism, which is coinductive.
module AnaInf {k} {F : Functor (suc k)} {As : Params k} (coalg : A → ⟦ F ⟧ (A  ∷ As)) where
  mutual
    anaRec : (G : Functor n) (Gs : Layers (suc m) n) →
             <! ⟦ G ⟧ (Gs ++∙ A ∷ Bs) !> → <! ⟦ G ⟧ (Gs ++∙ ν F As ∷ Bs) !>
    anaRec (G₁ ⊕ G₂)  Gs       [! inl x !] .!! = inl (anaRec G₁ Gs [! x !] .!!)
    anaRec (G₁ ⊕ G₂)  Gs       [! inr x !] .!! = inr (anaRec G₂ Gs [! x !] .!!)
    anaRec (G₁ ⊗ G₂)  Gs       [! x , y !] .!! .fst = anaRec G₁ Gs [! x !] .!!
    anaRec (G₁ ⊗ G₂)  Gs       [! x , y !] .!! .snd = anaRec G₂ Gs [! y !] .!!
    anaRec μ⟨ G ⟩     Gs       [! ⟨ x ⟩ !] .!! = ⟨ anaRec G (G ∷ Gs) [! x !] .!! ⟩
    anaRec (! f0    ) []       [! x     !] .!! = ana x
    anaRec (! (fs i)) []       [! x     !] .!! = x
    anaRec (! (fs i)) (G ∷ Gs) [! x     !] .!! = anaRec (! i) Gs [! x !] .!!
    anaRec (! f0    ) (G ∷ Gs) [! ⟨ x ⟩ !] .!! = ⟨ anaRec G (G ∷ Gs) [! x !] .!! ⟩
    anaRec ①          Gs       [! _     !] .!! = tt

    ana : A → ν F As
    ana x .unfold = anaRec F [] [! coalg x !] .!!

module AnaInfDisplay {F : Functor (suc n)} {As : Params n} where
  ana : (A → ⟦ F ⟧ (A ∷ As)) → A → ν F As
  ana = AnaInf.ana
open AnaInfDisplay public

-- The terminating anamorphism: uses well-founded recursion to ensure we're
-- building a finite type.
module AnaTerm {B : Type} {_<_ : B → B → Type} (<-wellFounded : WellFounded _<_)
         {k} {F : Functor (suc k)}
         {As : Params k}
         (coalg : (x : B) → ⟦ F ⟧ ((∃ y × (y < x))  ∷ As)) where

  pr-anaAcc : (x : B) → Acc _<_ x → μ F As
  pr-anaAcc x (acc wf) = ⟨ map F f0 (λ { (x , p) → pr-anaAcc x (wf x p) }) (coalg x)  ⟩

  pr-ana : B → μ F As
  pr-ana x = pr-anaAcc x (<-wellFounded x)

module AnaTermDisplay
  {A : Type}
  {_<_ : A → A → Type}
  {F : Functor (suc n)}
  {As : Params n}
  where
  pr-ana :  WellFounded _<_ →
            ((x : A) → ⟦ F ⟧ ((∃ y × (y < x)) ∷ As)) → A → μ F As
  pr-ana wf = AnaTerm.pr-ana wf

module Truncate {B : Type} {_<_ : B → B → Type} (<-wellFounded : WellFounded _<_)
                {k} {F : Functor (suc k)}
                {As : Params k} (step : (x : B) -> ⟦ F ⟧ (ν F As ∷ As) → ⟦ F ⟧ ((ν F As × ∃ y × (y < x)) ∷ As)) where

  truncAcc : (x : B) → Acc _<_ x → ν F As → μ F As
  truncAcc x (acc wf) xs = ⟨ map F f0 (λ { (ys , z , z<x) → truncAcc z (wf z z<x) ys}) (step x (xs .unfold)) ⟩

  trunc : B → ν F As → μ F As
  trunc x = truncAcc x (<-wellFounded x)

module TruncDisplay
  {A : Type}
  {_<_ : A → A → Type}
  {F : Functor (suc n)}
  {As : Params n} where
  trunc :  WellFounded _<_ →
           ((x : A) -> ⟦ F ⟧ (ν F As ∷ As) → ⟦ F ⟧ ((ν F As × ∃ y × (y < x)) ∷ As)) →
           A → ν F As → μ F As
  trunc wf step = Truncate.trunc wf step


------------------------------------------------------------------------
-- Total parser combinators (very short version)
-- Nils Anders Danielsson
------------------------------------------------------------------------

module Short where

open import Category.Monad
open import Coinduction
open import Data.Bool
import Data.BoundedVec.Inefficient as BoundedVec
open import Data.Char
import Data.Colist as Colist
open import Data.List as List
open import Data.Maybe
open import Data.Nat
open import Data.Nat.Show
open import Data.String using (Costring)
open import Function
open import IO hiding (return) renaming (_>>=_ to _>>=IO_)
open import Relation.Nullary.Decidable

open RawMonadPlus List.monadPlus using () renaming (_>>=_ to _>>=′_)

infix  7 _⋆ _+
infixl 6 _>>=_ _>>=app_
infixl 5 _∣_

------------------------------------------------------------------------
-- Helper functions

flatten : {A : Set} → Maybe (List A) → List A
flatten nothing   = []
flatten (just xs) = xs

app : {A B : Set} → Maybe (A → List B) → A → List B
app nothing  x = []
app (just f) x = f x

_>>=app_ : {A B : Set} → List A → Maybe (A → List B) → List B
xs >>=app nothing = []
xs >>=app just f  = xs >>=′ f

------------------------------------------------------------------------
-- Parsers

mutual

  data Parser : (R : Set) → List R → Set₁ where
    return : ∀ {R} (x : R) → Parser R [ x ]
    fail   : ∀ {R} → Parser R []
    token  : Parser Char []
    _∣_    : ∀ {R xs₁ xs₂}
             (p₁ : Parser R  xs₁       )
             (p₂ : Parser R         xs₂) →
                   Parser R (xs₁ ++ xs₂)
    _>>=_  : ∀ {R₁ R₂ xs} {f : Maybe (R₁ → List R₂)}
             (p₁ :            ∞⟨ f  ⟩Parser R₁ (flatten xs)           )
             (p₂ : (x : R₁) → ∞⟨ xs ⟩Parser R₂               (app f x)) →
                                     Parser R₂ (flatten xs >>=app f)

  ∞⟨_⟩Parser : {R₂ : Set} → Maybe R₂ → (R₁ : Set) → List R₁ → Set₁
  ∞⟨ nothing ⟩Parser R₁ xs = ∞ (Parser R₁ xs)
  ∞⟨ just _  ⟩Parser R₁ xs =    Parser R₁ xs

------------------------------------------------------------------------
-- Derived combinators

sat : ∀ {R} → (Char → Maybe R) → Parser R []
sat {R} p = token >>= (ok ∘ p)
  where
  ok-index : Maybe R → List R
  ok-index nothing  = []
  ok-index (just x) = [ x ]

  ok : (x : Maybe R) → Parser R (ok-index x)
  ok nothing  = fail
  ok (just x) = return x

tok : Char → Parser Char []
tok t = sat (λ t′ → if t == t′ then just t′ else nothing)

return⋆ : ∀ {R} (xs : List R) → Parser R xs
return⋆ []       = fail
return⋆ (x ∷ xs) = return x ∣ return⋆ xs

mutual

  _⋆ : ∀ {R} → Parser R [] → Parser (List R) _
  p ⋆ = return [] ∣ p +

  _+ : ∀ {R} → Parser R [] → Parser (List R) _
  p + = p               >>= λ x  → ♯ (
        p ⋆             >>= λ xs →
        return (x ∷ xs)              )

digit = sat (λ t → if in-range t then just (to-number t) else nothing)
  where
  in-range : Char → Bool
  in-range t = ⌊ toNat '0' ≤? toNat  t  ⌋ ∧
               ⌊ toNat  t  ≤? toNat '9' ⌋

  to-number : Char → ℕ
  to-number t = toNat t ∸ toNat '0'

number = digit + >>= return ∘ foldl (λ n d → 10 * n + d) 0

------------------------------------------------------------------------
-- Parser interpreter

delayed? : ∀ {R R′ xs} {m : Maybe R′} → ∞⟨ m ⟩Parser R xs → Maybe R′
delayed? {m = m} _ = m

delayed?′ : ∀ {R₁ R₂ R′ : Set} {m : Maybe R′} {f : R₁ → List R₂} →
            ((x : R₁) → ∞⟨ m ⟩Parser R₂ (f x)) → Maybe R′
delayed?′ {m = m} _ = m

∂-initial : ∀ {R xs} → Parser R xs → Char → List R
∂-initial (return x)  t = []
∂-initial fail        t = []
∂-initial token       t = [ t ]
∂-initial (p₁ ∣ p₂)   t = ∂-initial p₁ t ++ ∂-initial p₂ t
∂-initial (p₁ >>= p₂) t with delayed? p₁ | delayed?′ p₂
... | just f  | nothing =  ∂-initial p₁ t >>=′ f
... | just f  | just xs = (∂-initial p₁ t >>=′ f) ++
                          (xs >>=′ λ x → ∂-initial (p₂ x) t)
... | nothing | nothing = []
... | nothing | just xs =  xs >>=′ λ x → ∂-initial (p₂ x) t

∂ : ∀ {R xs} (p : Parser R xs) (t : Char) → Parser R (∂-initial p t)
∂ (return x)  t = fail
∂ fail        t = fail
∂ token       t = return t
∂ (p₁ ∣ p₂)   t = ∂ p₁ t ∣ ∂ p₂ t
∂ (p₁ >>= p₂) t with delayed? p₁ | delayed?′ p₂
... | just f  | nothing = ∂ p₁ t >>= (λ x → ♭ (p₂ x))
... | just f  | just xs = ∂ p₁ t >>= (λ x →    p₂ x)
                        ∣ return⋆ xs >>= λ x → ∂ (p₂ x) t
... | nothing | nothing = ♯ ∂ (♭ p₁) t >>= (λ x → ♭ (p₂ x))
... | nothing | just xs = ♯ ∂ (♭ p₁) t >>= (λ x →    p₂ x)
                        ∣ return⋆ xs >>= λ x → ∂ (p₂ x) t

parse : ∀ {R xs} → Parser R xs → List Char → List R
parse {xs = xs} p []      = xs
parse           p (t ∷ s) = parse (∂ p t) s

------------------------------------------------------------------------
-- Example

data Expr : Set where
  num   : (n : ℕ)        → Expr
  plus  : (e₁ e₂ : Expr) → Expr
  times : (e₁ e₂ : Expr) → Expr

⟦_⟧ : Expr → ℕ
⟦ num n       ⟧ = n
⟦ plus  e₁ e₂ ⟧ = ⟦ e₁ ⟧ + ⟦ e₂ ⟧
⟦ times e₁ e₂ ⟧ = ⟦ e₁ ⟧ * ⟦ e₂ ⟧

mutual

  term   = factor
         ∣ ♯ term               >>= λ e₁ →
           tok '+'              >>= λ _  →
           factor               >>= λ e₂ →
           return (plus e₁ e₂)

  factor = atom
         ∣ ♯ factor             >>= λ e₁ →
           tok '*'              >>= λ _  →
           atom                 >>= λ e₂ →
           return (times e₁ e₂)

  atom   = (number >>= return ∘ num)
         ∣ tok '('              >>= λ _  →
           ♯ term               >>= λ e  →
           tok ')'              >>= λ _  →
           return e

------------------------------------------------------------------------
-- IO

toList : Costring → List Char
toList = BoundedVec.toList ∘ Colist.take 100000

main = run $
  ♯ getContents >>=IO λ s → ♯
  let s′ = takeWhile (not ∘ _==_ '\n') $ toList s
      es = parse term s′ in
  mapM′ (putStrLn ∘ show ∘ ⟦_⟧) (Colist.fromList es)


module STLC.Lib.MaybeElim where

open import Level
open import Function
open import Relation.Binary.PropositionalEquality
open import Data.Unit.Base
open import Data.Bool.Base
open import Data.Maybe.Base

infixl 1  _>>=ᵀ_ _>>=ᵗ_ _>>=⊤_
infixl 4  _<$>ᵗ_
infixr 1  _>=>ᵗ_
infixr 10 _<∘>ᵗ_

data _>>=ᵀ_ {α β} {A : Set α} : (mx : Maybe A) -> (∀ x -> mx ≡ just x -> Set β) -> Set β where
  nothingᵗ : ∀ {B}   ->             nothing >>=ᵀ B
  justᵗ    : ∀ {x B} -> B x refl -> just x  >>=ᵀ B

-- We could write (Set (maybe (const β) zero mx)),
-- but I don't want to introduce dependency at the type level.
FromJustᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : ∀ x -> mx ≡ just x -> Set β}
          -> mx >>=ᵀ B -> Set β
FromJustᵗ  nothingᵗ         = Lift ⊤
FromJustᵗ (justᵗ {x} {B} y) = B x refl

fromJustᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : ∀ x -> mx ≡ just x -> Set β}
          -> (yᵗ : mx >>=ᵀ B) -> FromJustᵗ yᵗ
fromJustᵗ  nothingᵗ = _
fromJustᵗ (justᵗ y) = y

_>>=ᵗ_ : ∀ {α β} {A : Set α} {B : A -> Set β}
       -> (mx : Maybe A) -> (∀ x -> B x) -> mx >>=ᵀ λ x _ -> B x
nothing >>=ᵗ f = nothingᵗ
just  x >>=ᵗ f = justᵗ (f x)

_>>=⊤_ : ∀ {α β} {A : Set α} {B : A -> Set β}
       -> (mx : Maybe A) -> (g : ∀ x -> B x) -> FromJustᵗ (mx >>=ᵗ g)
mx >>=⊤ g = fromJustᵗ $ mx >>=ᵗ g

runᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : ∀ x -> mx ≡ just x -> Set β} {x}
     -> mx >>=ᵀ B -> (p : mx ≡ just x) -> B x p
runᵗ (justᵗ y) refl = y

_<$>ᵗ_ : ∀ {α β γ} {A : Set α} {mx : Maybe A}
           {B : ∀ x -> mx ≡ just x -> Set β}
           {C : ∀ {x} {p : mx ≡ just x} -> B x p -> Set γ}
       -> (∀ {x} {p : mx ≡ just x} -> (y : B x p) -> C y)
       -> (yᵗ : mx >>=ᵀ B)
       -> mx >>=ᵀ λ _ -> C ∘ runᵗ yᵗ
g <$>ᵗ nothingᵗ = nothingᵗ
g <$>ᵗ justᵗ y  = justᵗ (g y)

_>=>ᵗ_ : ∀ {α β γ} {A : Set α} {B : A -> Set β} {C : ∀ {x} -> B x -> Set γ}
       -> (f : ∀ x -> Maybe (B x))
       -> (∀ {x} -> (y : B x) -> C y)
       -> ∀ x -> f x >>=ᵀ λ y _ -> C y
(f >=>ᵗ g) x = f x >>=ᵗ g

_<∘>ᵗ_ : ∀ {α β γ δ} {A : Set α} {B : A -> Set β} {f : ∀ x -> Maybe (B x)}
           {C : ∀ x y -> f x ≡ just y -> Set γ}
           {D : ∀ {x y} {p : f x ≡ just y} -> C x y p -> Set δ}
       -> (∀ {x y} {p : f x ≡ just y} -> (z : C x y p) -> D z)
       -> (g : ∀ x -> f x >>=ᵀ C x)
       -> ∀ x -> f x >>=ᵀ λ _ -> D ∘ runᵗ (g x)
(h <∘>ᵗ g) x = h <$>ᵗ g x


-- Algorithmic equality.

{-# OPTIONS --safe #-}

module Definition.Conversion where

open import Definition.Untyped
open import Definition.Typed

open import Tools.Nat
import Tools.PropositionalEquality as PE

infix 10 _⊢_~_↑_^_
infix 10 _⊢_[conv↑]_^_
infix 10 _⊢_[conv↓]_^_
infix 10 _⊢_[conv↑]_∷_^_
infix 10 _⊢_[conv↓]_∷_^_
infix 10 _⊢_[genconv↑]_∷_^_

mutual
  -- Neutral equality.
  data _⊢_~_↑!_^_ (Γ : Con Term) : (k l A : Term) → TypeLevel → Set where
    var-refl    : ∀ {x y A l}
                → Γ ⊢ var x ∷ A ^ [ ! , l ]
                → x PE.≡ y
                → Γ ⊢ var x ~ var y ↑! A ^ l
    app-cong    : ∀ {k l t v F rF lF lG G lΠ}
                → Γ ⊢ k ~ l ↓! Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ ι lΠ
                → Γ ⊢ t [genconv↑] v ∷ F ^ [ rF , ι lF ]
                → Γ ⊢ k ∘ t ^ lΠ ~ l ∘ v ^ lΠ ↑! G [ t ] ^ ι lG
    natrec-cong : ∀ {k l h g a₀ b₀ F G lF}
                → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F [conv↑] G ^ [ ! , ι lF ]
                → Γ ⊢ a₀ [conv↑] b₀ ∷ F [ zero ] ^ ι lF
                → Γ ⊢ h [conv↑] g ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° lF ▹▹ F [ suc (var 0) ]↑ ° lF ° lF) ° lF ° lF ^ ι lF
                → Γ ⊢ k ~ l ↓! ℕ ^ ι ⁰
                → Γ ⊢ natrec lF F a₀ h k ~ natrec lF G b₀ g l ↑! F [ k ] ^ ι lF
    Emptyrec-cong : ∀ {k l F G ll lEmpty}
                  → Γ ⊢ F [conv↑] G ^ [ ! , ι ll ]
                  → Γ ⊢ k ~ l ↑% Empty lEmpty ^ ι lEmpty
                  → Γ ⊢ Emptyrec ll lEmpty F k ~ Emptyrec ll lEmpty G l ↑! F ^ ι ll
    Id-cong : ∀ {l A A' t t' u u'}
              → Γ ⊢ A ~ A' ↓! U l ^ next l
              → Γ ⊢ t [conv↑] t' ∷ A ^ ι l
              → Γ ⊢ u [conv↑] u' ∷ A ^ ι l
              → Γ ⊢ Id A t u ~ Id A' t' u' ↑! SProp l ^ next l
    Id-ℕ : ∀ {t t' u u'}
              → Γ ⊢ t ~ t' ↓! ℕ ^ ι ⁰
              → Γ ⊢ u [conv↑] u' ∷ ℕ ^ ι ⁰
              → Γ ⊢ Id ℕ t u ~ Id ℕ t' u' ↑! SProp ⁰ ^ next ⁰
    Id-ℕ0 : ∀ {t t'}
              → Γ ⊢ t ~ t' ↓! ℕ ^ ι ⁰
              → Γ ⊢ Id ℕ zero t ~ Id ℕ zero t' ↑! SProp ⁰ ^ next ⁰
    Id-ℕS : ∀ {t t' u u'}
              → Γ ⊢ t [conv↑] t' ∷ ℕ ^ ι ⁰
              → Γ ⊢ u ~ u' ↓! ℕ ^ ι ⁰
              → Γ ⊢ Id ℕ (suc t) u ~ Id ℕ (suc t') u' ↑! SProp ⁰ ^ next ⁰
    Id-U : ∀ {t t' u u'}
              → Γ ⊢ t ~ t' ↓! U ⁰ ^ ι ¹
              → Γ ⊢ u [conv↑] u' ∷ U ⁰ ^ ι ¹
              → Γ ⊢ Id (U ⁰) t u ~ Id (U ⁰) t' u' ↑! SProp ¹ ^ next ¹
    Id-Uℕ : ∀ {t t'}
              → Γ ⊢ t ~ t' ↓! U ⁰ ^ ι ¹
              → Γ ⊢ Id (U ⁰) ℕ t ~ Id (U ⁰) ℕ t' ↑! SProp ¹ ^ next ¹
    Id-UΠ : ∀ {A rA B A' B' t t'}
              → Γ ⊢ Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰  [conv↑] Π A' ^ rA ° ⁰ ▹ B' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t ~ t' ↓! U ⁰ ^ ι ¹
              → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰  ) t ~ Id (U ⁰) (Π A' ^ rA ° ⁰ ▹ B' ° ⁰ ° ⁰  ) t' ↑! SProp ¹ ^ next ¹
    cast-cong : ∀ {A A' B B' t t' e e'}
              → Γ ⊢ A ~ A' ↓! U ⁰ ^ next ⁰
              → Γ ⊢ B [conv↑] B' ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t [conv↑] t' ∷ A ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) A B) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) A' B') ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ A B e t ~ cast ⁰ A' B' e' t' ↑! B ^ ι ⁰
    cast-ℕ : ∀ {A A' t t' e e'}
              → Γ ⊢ A ~ A' ↓! U ⁰ ^ next ⁰
              → Γ ⊢ t [conv↑] t' ∷ ℕ ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) ℕ A) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) ℕ A') ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ ℕ A e t ~ cast ⁰ ℕ A' e' t' ↑! A ^ ι ⁰
    cast-ℕℕ : ∀ {t t' e e'}
              → Γ ⊢ t ~ t' ↓! ℕ ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) ℕ ℕ) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) ℕ ℕ) ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ ℕ ℕ e t ~ cast ⁰ ℕ ℕ e' t' ↑! ℕ ^ ι ⁰
    cast-Π : ∀ {A rA P A' P' B B' t t' e e'}
              → Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰  [conv↑] Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰  ∷ U ⁰ ^ next ⁰
              → Γ ⊢ B ~ B' ↓! U ⁰ ^ next ⁰
              → Γ ⊢ t [conv↑] t' ∷ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰  ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) B) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ) B') ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) B e t ~ cast ⁰ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ) B' e' t' ↑! B ^ ι ⁰
    cast-Πℕ : ∀ {A rA P A' P' t t' e e'}
              → Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰  [conv↑] Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t [conv↑] t' ∷ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰  ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) ℕ) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ) ℕ) ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) ℕ e t ~ cast ⁰ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ) ℕ e' t' ↑! ℕ ^ ι ⁰
    cast-ℕΠ : ∀ {A rA P A' P' t t' e e'}
              → Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰  [conv↑] Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t [conv↑] t' ∷ ℕ ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) ℕ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) ℕ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ ℕ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) e t ~ cast ⁰ ℕ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ) e' t' ↑! (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ) ^ ι ⁰
    cast-ΠΠ%! : ∀ {A P A' P' B Q B' Q' t t' e e'}
              → Γ ⊢ Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰  [conv↑] Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰  [conv↑] Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t [conv↑] t' ∷ Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰  ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰ ) (Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰ ) (Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ (Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰ ) (Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰ ) e t ~
                    cast ⁰ (Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰ ) (Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰ ) e' t' ↑! (Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰ ) ^ ι ⁰
    cast-ΠΠ!% : ∀ {A P A' P' B Q B' Q' t t' e e'}
              → Γ ⊢ Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰  [conv↑] Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰  [conv↑] Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰  ∷ U ⁰ ^ ι ¹
              → Γ ⊢ t [conv↑] t' ∷ Π A ^ ! ° ⁰ ▹ P ° ⁰  ° ⁰ ^ ι ⁰
              → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰ ) (Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰ ) (Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰ )) ^ [ % , next ⁰ ]
              → Γ ⊢ cast ⁰ (Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰ ) (Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰ ) e t ~
                    cast ⁰ (Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰ ) (Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰ ) e' t' ↑! (Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰ ) ^ ι ⁰

  record _⊢_~_↑%_^_ (Γ : Con Term) (k l A : Term) (ll : TypeLevel) : Set where
    inductive
    constructor %~↑
    field
      ⊢k : Γ ⊢ k ∷ A ^ [ % , ll ]
      ⊢l : Γ ⊢ l ∷ A ^ [ % , ll ]

  data _⊢_~_↑_^_ (Γ : Con Term) : (k l A : Term) → TypeInfo → Set where
    ~↑! : ∀ {k l A ll} → Γ ⊢ k ~ l ↑! A ^ ll → Γ ⊢ k ~ l ↑ A ^ [ ! , ll ]
    ~↑% : ∀ {k l A ll} → Γ ⊢ k ~ l ↑% A ^ ll → Γ ⊢ k ~ l ↑ A ^ [ % , ll ]

  -- Neutral equality with types in WHNF.
  record _⊢_~_↓!_^_ (Γ : Con Term) (k l B : Term) (ll : TypeLevel) : Set where
    inductive
    constructor [~]
    field
      A     : Term
      D     : Γ ⊢ A ⇒* B ^ [ ! , ll ]
      whnfB : Whnf B
      k~l   : Γ ⊢ k ~ l ↑! A ^ ll

  -- Type equality.
  record _⊢_[conv↑]_^_ (Γ : Con Term) (A B : Term) (rA : TypeInfo) : Set where
    inductive
    constructor [↑]
    field
      A′ B′  : Term
      D      : Γ ⊢ A ⇒* A′ ^ rA
      D′     : Γ ⊢ B ⇒* B′ ^ rA
      whnfA′ : Whnf A′
      whnfB′ : Whnf B′
      A′<>B′ : Γ ⊢ A′ [conv↓] B′ ^ rA

  -- Type equality with types in WHNF.
  data _⊢_[conv↓]_^_ (Γ : Con Term) : (A B : Term) → TypeInfo → Set where
    U-refl    : ∀ {r r' }
              → r PE.≡ r' -- needed for K issues
              → ⊢ Γ → Γ ⊢ Univ r ¹ [conv↓] Univ r' ¹ ^ [ ! , next ¹ ]
    univ      : ∀ {A B r l}
              → Γ ⊢ A [conv↓] B ∷ Univ r l ^ next l
              → Γ ⊢ A [conv↓] B ^ [ r , ι l ]

  -- Term equality.
  record _⊢_[conv↑]_∷_^_ (Γ : Con Term) (t u A : Term) (l : TypeLevel) : Set where
    inductive
    constructor [↑]ₜ
    field
      B t′ u′ : Term
      D       : Γ ⊢ A ⇒* B ^ [ ! , l ]
      d       : Γ ⊢ t ⇒* t′ ∷ B ^ l
      d′      : Γ ⊢ u ⇒* u′ ∷ B ^ l
      whnfB   : Whnf B
      whnft′  : Whnf t′
      whnfu′  : Whnf u′
      t<>u    : Γ ⊢ t′ [conv↓] u′ ∷ B ^ l

  -- Term equality with types and terms in WHNF.
  data _⊢_[conv↓]_∷_^_ (Γ : Con Term) : (t u A : Term) (l : TypeLevel) → Set where
    U-refl    : ∀ {r r' }
              → r PE.≡ r' -- needed for K issues
              → ⊢ Γ → Γ ⊢ Univ r ⁰ [conv↓] Univ r' ⁰ ∷ U ¹ ^ next ¹
    ne        : ∀ {r K L lU l}
                → Γ ⊢ K ~ L ↓! Univ r lU ^ l
                → Γ ⊢ K [conv↓] L ∷ Univ r lU ^ l
    ℕ-refl    : ⊢ Γ → Γ ⊢ ℕ [conv↓] ℕ ∷ U ⁰ ^ next ⁰
    Empty-refl : ∀ {l ll} → ll PE.≡ next l → ⊢ Γ → Γ ⊢ Empty l [conv↓] Empty l ∷ SProp l ^ ll
    Π-cong    : ∀ {F G H E rF rH rΠ lF lH lG lE lΠ ll}
              → ll PE.≡ next lΠ
              → rF PE.≡ rH -- needed for K issues
              → lF PE.≡ lH -- needed for K issues
              → lG PE.≡ lE -- needed for K issues
              → lF ≤ lΠ
              → lG ≤ lΠ
              → Γ ⊢ F ^ [ rF , ι lF ]
              → Γ ⊢ F [conv↑] H ∷ Univ rF lF ^ next lF
              → Γ ∙ F ^ [ rF , ι lF ] ⊢ G [conv↑] E  ∷ Univ rΠ lG ^ next lG
              → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° lΠ [conv↓] Π H ^ rH ° lH ▹ E ° lE ° lΠ ∷ Univ rΠ lΠ ^ ll
    ∃-cong    : ∀ {F G H E l ll}
              → ll PE.≡ next l
              → Γ ⊢ F ^ [ % , ι l ]
              → Γ ⊢ F [conv↑] H ∷ SProp l ^ next l
              → Γ ∙ F ^ [ % , ι l ] ⊢ G [conv↑] E  ∷ SProp l ^ next l
              → Γ ⊢ ∃ F ▹ G [conv↓] ∃ H ▹ E ∷ SProp l ^ ll
    ℕ-ins     : ∀ {k l}
              → Γ ⊢ k ~ l ↓! ℕ ^ ι ⁰
              → Γ ⊢ k [conv↓] l ∷ ℕ ^ ι ⁰
    ne-ins    : ∀ {k l M N ll}
              → Γ ⊢ k ∷ N ^ [ ! , ι ll ]
              → Γ ⊢ l ∷ N ^ [ ! , ι ll ]
              → Neutral N
              → Γ ⊢ k ~ l ↓! M ^ ι ll
              → Γ ⊢ k [conv↓] l ∷ N ^ ι ll
    zero-refl : ⊢ Γ → Γ ⊢ zero [conv↓] zero ∷ ℕ ^ ι ⁰
    suc-cong  : ∀ {m n}
              → Γ ⊢ m [conv↑] n ∷ ℕ ^ ι ⁰
              → Γ ⊢ suc m [conv↓] suc n ∷ ℕ ^ ι ⁰
    η-eq      : ∀ {f g F G rF lF lG l}
              → lF ≤ l
              → lG ≤ l
              → Γ ⊢ F ^ [ rF , ι lF ]
              → Γ ⊢ f ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
              → Γ ⊢ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
              → Function f
              → Function g
              → Γ ∙ F ^ [ rF , ι lF ] ⊢ wk1 f ∘ var 0 ^ l [conv↑] wk1 g ∘ var 0 ^ l ∷ G ^ ι lG
                → Γ ⊢ f [conv↓] g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ ι l

  _⊢_[genconv↑]_∷_^_ : (Γ : Con Term) (t u A : Term) (r : TypeInfo) → Set
  _⊢_[genconv↑]_∷_^_ Γ k l A [ ! , ll ] =  Γ ⊢ k [conv↑] l ∷ A ^ ll
  _⊢_[genconv↑]_∷_^_ Γ k l A [ % , ll ] =  Γ ⊢ k ~ l ↑% A ^  ll

var-refl′ : ∀ {Γ x A rA ll}
          → Γ ⊢ var x ∷ A ^ [ rA , ll ]
          → Γ ⊢ var x ~ var x ↑ A ^ [ rA , ll ]
var-refl′ {rA = !} ⊢x = ~↑! (var-refl ⊢x PE.refl)
var-refl′ {rA = %} ⊢x = ~↑% (%~↑ ⊢x ⊢x)


{-# OPTIONS --cubical --safe --postfix-projections #-}

open import Prelude

module Cardinality.Finite.Cardinal.Category {ℓ : Level} where

open import Cardinality.Finite.Cardinal
open import HITs.PropositionalTruncation
open import Data.Sigma.Properties
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Categories
open import Cubical.Foundations.Univalence
open import Categories.Product
open import Categories.Exponential
open import Data.Fin
open import Cardinality.Finite.ManifestBishop

𝒞⇒isSet : 𝒞 A → isSet A
𝒞⇒isSet = Discrete→isSet ∘ 𝒞⇒Discrete

finSetPreCategory : PreCategory (ℓsuc ℓ) ℓ
finSetPreCategory .PreCategory.Ob = Σ (Type ℓ) 𝒞
finSetPreCategory .PreCategory.Hom (X , _) (Y , _) = X → Y
finSetPreCategory .PreCategory.Id = id
finSetPreCategory .PreCategory.Comp f g = f ∘ g
finSetPreCategory .PreCategory.assoc-Comp _ _ _ = refl
finSetPreCategory .PreCategory.Comp-Id _ = refl
finSetPreCategory .PreCategory.Id-Comp _ = refl
finSetPreCategory .PreCategory.Hom-Set {X} {Y} = hLevelPi 2 λ _ → Discrete→isSet (𝒞⇒Discrete (Y .snd))

open PreCategory finSetPreCategory

iso-iso : (X ≅ Y) ⇔ (fst X ⇔ fst Y)
iso-iso .fun (f , g , f∘g , g∘f) = iso f g (λ x i → g∘f i x) (λ x i → f∘g i x)
iso-iso .inv (iso f g g∘f f∘g) = f , g  , (λ i x → f∘g x i) , (λ i x → g∘f x i)
iso-iso .rightInv _ = refl
iso-iso .leftInv  _ = refl

finSetCategory : Category (ℓsuc ℓ) ℓ
finSetCategory .Category.preCategory = finSetPreCategory
finSetCategory .Category.univalent {X} {Y} =
  ≃ΣProp≡ (λ _ → squash) ⟨ trans-≃ ⟩
  univalence ⟨ trans-≃ ⟩
  isoToEquiv (
  sym-⇔ (iso⇔equiv (Discrete→isSet (𝒞⇒Discrete (X .snd)))) ⟨ trans-⇔ ⟩
  sym-⇔ (iso-iso {X} {Y}))

finSetHasProducts : HasProducts finSetCategory
finSetHasProducts .HasProducts.product X Y .Product.obj = X .fst × Y .fst , X .snd ∥×∥ Y .snd
finSetHasProducts .HasProducts.product X Y .Product.proj₁ = fst
finSetHasProducts .HasProducts.product X Y .Product.proj₂ = snd
finSetHasProducts .HasProducts.product X Y .Product.ump f g .fst z = f z , g z
finSetHasProducts .HasProducts.product X Y .Product.ump f g .snd .fst .fst = refl
finSetHasProducts .HasProducts.product X Y .Product.ump f g .snd .fst .snd = refl
finSetHasProducts .HasProducts.product X Y .Product.ump f g .snd .snd (f≡ , g≡) i x = f≡ (~ i) x , g≡ (~ i) x

finSetHasExp : HasExponentials finSetCategory finSetHasProducts
finSetHasExp X Y .Exponential.obj = (X .fst → Y .fst) , (X .snd ∥→∥ Y .snd)
finSetHasExp X Y .Exponential.eval (f , x) = f x
finSetHasExp X Y .Exponential.uniq X₁ f .fst = curry f
finSetHasExp X Y .Exponential.uniq X₁ f .snd .fst = refl
finSetHasExp X Y .Exponential.uniq X₁ f .snd .snd x = cong curry (sym x)

open import Categories.Pullback
open import Cardinality.Finite.SplitEnumerable using (ℰ!⟨Lift⟩; ℰ!⟨⊤⟩; ℰ!⟨⊥⟩)

finSetHasPullbacks : HasPullbacks finSetCategory
finSetHasPullbacks {X = X} {Y = Y} {Z = Z} f g .Pullback.P = ∃[ ab ] (f (fst ab) ≡ g (snd ab)) , ((X .snd ∥×∥ Y .snd) ∥Σ∥ λ _ → 𝒞⟨≡⟩ _ _ (Z .snd))
finSetHasPullbacks f g .Pullback.p₁ ((x , _) , _) = x
finSetHasPullbacks f g .Pullback.p₂ ((_ , y) , _) = y
finSetHasPullbacks f g .Pullback.commute = funExt snd
finSetHasPullbacks f g .Pullback.ump {A = A} h₁ h₂ p .fst x = (h₁ x , h₂ x) , λ i → p i x
finSetHasPullbacks f g .Pullback.ump {A = A} h₁ h₂ p .snd .fst .fst = refl
finSetHasPullbacks f g .Pullback.ump {A = A} h₁ h₂ p .snd .fst .snd = refl
finSetHasPullbacks {Z = Z} f g .Pullback.ump {A = A} h₁ h₂ p .snd .snd {i} (p₁e , p₂e) = funExt (λ x → ΣProp≡ (λ _ →  𝒞⇒isSet (Z .snd) _ _) λ j → p₁e (~ j) x , p₂e (~ j) x)

finSetTerminal : Terminal
finSetTerminal .fst = Lift _ ⊤ , ∣ ℰ!⇒ℬ (ℰ!⟨Lift⟩ ℰ!⟨⊤⟩) ∣
finSetTerminal .snd .fst x .lower = tt
finSetTerminal .snd .snd y = funExt λ _ → refl

finSetInitial : Initial
finSetInitial .fst = Lift _ ⊥ , ∣ ℰ!⇒ℬ (ℰ!⟨Lift⟩ ℰ!⟨⊥⟩) ∣
finSetInitial .snd .fst ()
finSetInitial .snd .snd y i ()

open import HITs.PropositionalTruncation
open import Categories.Coequalizer

∃!′ : (A : Type a) → (A → Type b) → Type (a ℓ⊔ b)
∃!′ A P = ∥ Σ A P ∥ Prelude.× AtMostOne P

lemma23 : ∀ {p} {P : A → hProp p} → ∃!′ A (fst ∘ P) → Σ A (fst ∘ P)
lemma23 {P = P} (x , y) = rec (λ xs ys → ΣProp≡ (snd ∘ P) (y (xs .fst) (ys .fst) (xs .snd) (ys .snd))) id x

module _ {A : Type a} {P : A → Type b} (R : ∀ x → P x → hProp c) where
  uniqueChoice : (Π[ x ⦂ A ] (∃!′ (P x) (λ u → R x u .fst))) →
                 Σ[ f ⦂ Π[ x ⦂ A ] P x ] Π[ x ⦂ A ] (R x (f x) .fst)
  uniqueChoice H = fst ∘ mid , snd ∘ mid
    where
    mid : Π[ x ⦂ A ] Σ[ u ⦂ P x ] (R x u .fst)
    mid x = lemma23 {P = R x} (H x)

open import HITs.PropositionalTruncation.Sugar
open import Data.List using (_∷_; [])
open import Cardinality.Finite.SplitEnumerable.Search using (ℰ!⟨fib⟩)

module CoeqProofs {X Y : Ob} (f : X ⟶ Y) where
  KernelPair : Pullback finSetCategory {X = X} {Z = Y} {Y = X} f f
  KernelPair = finSetHasPullbacks f f

  Im : Ob
  Im = ∃[ b ] ∥ fiber f b ∥ , (Y .snd ∥Σ∥ λ x → do cx ← X .snd ; cy ← Y .snd ; ∣ ℰ!⇒ℬ (ℰ!⟨fib⟩ f _ (ℬ⇒ℰ! cx) (ℬ⇒ℰ! cy)) ∣)

  im : X ⟶ Im
  im x = f x , ∣ x , refl ∣

  open Pullback KernelPair

  lem : ∀ {H : Ob} (h : X ⟶ H) → h ∘ p₁ ≡ h ∘ p₂ → Σ[ f ⦂ (Im ⟶ H) ] Π[ x ⦂ Im .fst ] (∀ y → im y ≡ x → h y ≡ f x)
  lem {H = H} h eq = uniqueChoice R prf
    where
    R : Im .fst → H .fst → hProp _
    R w x .fst = ∀ y → im y ≡ w → h y ≡ x
    R w x .snd = hLevelPi 1 λ _ → hLevelPi 1 λ _ → 𝒞⇒isSet (H .snd) _ _

    prf : Π[ x ⦂ Im .fst ] ∃!′ (H .fst) (λ u → ∀ y → im y ≡ x → h y ≡ u)
    prf (xy , p) .fst = (λ { (z , r) → h z , λ y imy≡xyp → cong (_$ ((y , z) , cong fst imy≡xyp ; sym r)) eq }) ∥$∥ p
    prf (xy , p) .snd x₁ x₂ Px₁ Px₂ = rec (𝒞⇒isSet (H .snd) x₁ x₂) (λ { (z , zs) → sym (Px₁ z (ΣProp≡ (λ _ → squash) zs)) ; Px₂ z (ΣProp≡ (λ _ → squash) zs) } ) p

  lem₂ : ∀ (H : Ob) (h : X ⟶ H) (i : Im ⟶ H) (x : Im .fst) (hi : h ≡ i ∘ im) (eq : h ∘ p₁ ≡ h ∘ p₂) → i x ≡ lem {H = H} h eq .fst x
  lem₂ H h i x hi eq = rec (𝒞⇒isSet (H .snd) _ _) (λ { (y , ys) → (cong i (ΣProp≡ (λ _ → squash) (sym ys)) ; sym (cong (_$ y) hi)) ; lem {H = H} h eq .snd x y (ΣProp≡ (λ _ → squash) ys) }) (x .snd)

  finSetCoeq : Coequalizer finSetCategory {X = P} {Y = X} p₁ p₂
  finSetCoeq .Coequalizer.obj = Im
  finSetCoeq .Coequalizer.arr = im
  finSetCoeq .Coequalizer.equality = funExt λ x → ΣProp≡ (λ _ → squash) λ i → commute i x
  finSetCoeq .Coequalizer.coequalize {H = H} {h = h} eq = lem {H = H} h eq .fst
  finSetCoeq .Coequalizer.universal {H = H} {h = h} {eq = eq} = funExt λ x → lem {H = H} h eq .snd (im x) x refl
  finSetCoeq .Coequalizer.unique {H = H} {h = h} {i = i} {eq = eq} prf = funExt λ x → lem₂ H h i x prf eq
open CoeqProofs public using (finSetCoeq)

module PullbackSurjProofs {X Y : Ob} (f : X ⟶ Y) (fSurj : Surjective f) where
  KernelPair : Pullback finSetCategory {X = X} {Z = Y} {Y = X} f f
  KernelPair = finSetHasPullbacks f f

  open Pullback KernelPair

  p₁surj : Surjective p₁
  p₁surj y = ∣ ((y , y) , refl) , refl ∣

  p₂surj : Surjective p₂
  p₂surj y = ∣ ((y , y) , refl) , refl ∣


-- Copyright:  (c) 2016 Ertugrul Söylemez
-- License:    BSD3
-- Maintainer: Ertugrul Söylemez <esz@posteo.de>
--
-- This module contains definitions that are fundamental and/or used
-- everywhere.

module Core where

open import Agda.Builtin.Equality public
  renaming (refl to ≡-refl)
  using (_≡_)
open import Agda.Builtin.FromNat public
open import Agda.Builtin.FromNeg public
open import Agda.Builtin.FromString public
open import Agda.Builtin.Unit using (⊤; tt) public
open import Agda.Primitive using (Level; lsuc; lzero; _⊔_) public

-- An empty type (or a false hypothesis).

data ⊥ : Set where

-- Dependent sums (or existential quantification).

record Σ {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    fst : A
    snd : B fst

open Σ public

infixr 4 _,_
infixr 0 _because_ _because:_

∃ : ∀ {a b} {A : Set a} (B : A → Set b) → Set (a ⊔ b)
∃ = Σ _

_because_ : ∀ {a b} {A : Set a} {B : A → Set b} → (x : A) → B x → Σ A B
_because_ = _,_

_because:_ : ∀ {a b} {A : Set a} {B : A → Set b} → (x : A) → B x → Σ A B
_because:_ = _,_

_×_ : ∀ {a b} (A : Set a) (B : Set b) → Set (a ⊔ b)
A × B = Σ A (λ _ → B)

infixr 7 _×_

-- Tagged unions.

data Either {a} {b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  Left  : A → Either A B
  Right : B → Either A B

-- Equivalence relations.

record Equiv {a r} (A : Set a) : Set (a ⊔ lsuc r) where
  field
    _≈_   : A → A → Set r
    refl  : ∀ {x} → x ≈ x
    sym   : ∀ {x y} → x ≈ y → y ≈ x
    trans : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  infix 4 _≈_

  -- Helper functions for equational reasoning.
  begin_ : ∀ {x y} → x ≈ y → x ≈ y
  begin_ p = p

  _≈[_]_ : ∀ x {y z} → x ≈ y → y ≈ z → x ≈ z
  _ ≈[ x≈y ] y≈z = trans x≈y y≈z

  _≈[]_ : ∀ x {y} → x ≈ y → x ≈ y
  _ ≈[] p = p

  _qed : ∀ (x : A) → x ≈ x
  _qed _ = refl

  infix  1 begin_
  infixr 2 _≈[_]_ _≈[]_
  infix  3 _qed

PropEq : ∀ {a} → (A : Set a) → Equiv A
PropEq A =
  record {
    _≈_ = _≡_;
    refl = ≡-refl;
    sym = sym';
    trans = trans'
  }

  where
  sym' : ∀ {x y} → x ≡ y → y ≡ x
  sym' ≡-refl = ≡-refl

  trans' : ∀ {x y z} → x ≡ y → y ≡ z → x ≡ z
  trans' ≡-refl q = q

module PropEq {a} {A : Set a} = Equiv (PropEq A)

FuncEq : ∀ {a b} (A : Set a) (B : Set b) → Equiv (A → B)
FuncEq A B =
  record {
    _≈_ = λ f g → ∀ x → f x ≡ g x;
    refl = λ _ → ≡-refl;
    sym = λ p x → PropEq.sym (p x);
    trans = λ p q x → PropEq.trans (p x) (q x)
  }

module FuncEq {a b} {A : Set a} {B : Set b} = Equiv (FuncEq A B)

cong : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) {x y} → x ≡ y → f x ≡ f y
cong _ ≡-refl = ≡-refl

cong2 :
  ∀ {a b c} {A : Set a} {B : Set b} {C : Set c}
    (f : A → B → C)
  → ∀ {x1 x2 : A} {y1 y2 : B}
    → x1 ≡ x2
    → y1 ≡ y2
    → f x1 y1 ≡ f x2 y2
cong2 _ ≡-refl ≡-refl = ≡-refl

-- Partial orders.

record PartialOrder {a re rl} (A : Set a) : Set (a ⊔ lsuc (re ⊔ rl)) where
  field Eq : Equiv {r = re} A
  module ≈ = Equiv Eq
  open ≈ public using (_≈_)

  field
    _≤_     : (x : A) → (y : A) → Set rl
    antisym : ∀ {x y} → x ≤ y → y ≤ x → x ≈ y
    refl'   : ∀ {x y} → x ≈ y → x ≤ y
    trans   : ∀ {x y z} → x ≤ y → y ≤ z → x ≤ z

  infix 4 _≤_

  refl : ∀ {x} → x ≤ x
  refl = refl' ≈.refl

  -- Helper functions for transitivity reasoning.
  begin_ : ∀ {x y} → x ≤ y → x ≤ y
  begin_ p = p

  _≤[_]_ : ∀ x {y z} → x ≤ y → y ≤ z → x ≤ z
  _ ≤[ x≤y ] y≤z = trans x≤y y≤z

  _≤[]_ : ∀ x {y} → x ≤ y → x ≤ y
  _ ≤[] p = p

  _qed : ∀ (x : A) → x ≤ x
  _qed _ = refl

  infix  1 begin_
  infixr 2 _≤[_]_ _≤[]_
  infix  3 _qed

-- Total orders.

record TotalOrder {a re rl} (A : Set a) : Set (a ⊔ lsuc (re ⊔ rl)) where
  field partialOrder : PartialOrder {a} {re} {rl} A
  open PartialOrder partialOrder public

  field
    total : ∀ x y → Either (x ≤ y) (y ≤ x)

-- Low-priority function application.

_$_ : ∀ {a} {A : Set a} → A → A
_$_ f = f

infixr 0 _$_

-- Given two predicates, this is the predicate that requires both.

_and_ : ∀ {a r1 r2} {A : Set a} → (A → Set r1) → (A → Set r2) → A → Set (r1 ⊔ r2)
(P and Q) x = P x × Q x

infixr 6 _and_

-- Use instance resolution to find a value of the target type.

it : ∀ {a} {A : Set a} {{_ : A}} → A
it {{x}} = x

-- Not.

not : ∀ {a} → Set a → Set a
not A = A → ⊥

-- Given two predicates, this is the predicate that requires at least
-- one of them.

_or_ : ∀ {a r1 r2} {A : Set a} → (A → Set r1) → (A → Set r2) → A → Set (r1 ⊔ r2)
(P or Q) x = Either (P x) (Q x)

-- Values with inline type signatures.

the : ∀ {a} (A : Set a) → A → A
the _ x = x

-- Decidable properties.

data Decision {a} (P : Set a) : Set a where
  yes : (p : P) → Decision P
  no  : (np : not P) → Decision P