module Category.Functor.Either where
open import Agda.Primitive using (_⊔_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Category.Functor     using (RawFunctor    ; module RawFunctor    )
open import Category.Applicative using (RawApplicative; module RawApplicative)
open import Function using (_∘_)

Either : ∀ {l₁ l₂} (A : Set l₁) (B : Set l₂) → Set (l₁ ⊔ l₂)
Either = _⊎_

eitherFunctor : ∀ {l₁ l₂} {A : Set l₁} → RawFunctor (Either {l₁} {l₂} A)
eitherFunctor = record
  { _<$>_ = λ f → λ { (inj₁ z) → inj₁ z ; (inj₂ a) → inj₂ (f a) }
  }

eitherApplicative : ∀ {l₁} {A : Set l₁} → RawApplicative (Either {l₁} {l₁} A)
eitherApplicative = record
  { pure = inj₂
  ; _⊛_ = λ { (inj₁ a) → λ _ → inj₁ a ; (inj₂ f) → λ { (inj₁ a) → inj₁ a ; (inj₂ b) → inj₂ (f b) } }
  }


------------------------------------------------------------------------
-- A coinductive definition of (strong) bisimilarity
------------------------------------------------------------------------

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

open import Labelled-transition-system

module Bisimilarity {ℓ} (lts : LTS ℓ) where

open import Equality.Propositional
open import Prelude
open import Prelude.Size

import Function-universe equality-with-J as F

import Bisimilarity.General
open import Indexed-container using (Container; ν; ν′)
open import Relation
open import Up-to

open LTS lts

private
  module General = Bisimilarity.General lts _[_]⟶_ _[_]⟶_ id id

open General public
  using (module StepC; ⟨_,_⟩; left-to-right; right-to-left; force;
         reflexive-∼; reflexive-∼′; ≡⇒∼; ∼:_; ∼′:_;
         [_]_≡_; [_]_≡′_; []≡↔; module Bisimilarity-of-∼;
         Extensionality; extensionality)

-- StepC is given in the following way, rather than via open public,
-- to make hyperlinks to it more informative.

StepC : Container (Proc × Proc) (Proc × Proc)
StepC = General.StepC

-- The following definitions are given explicitly, to make the code
-- easier to follow for readers of the paper.

Bisimilarity : Size → Rel₂ ℓ Proc
Bisimilarity = ν StepC

Bisimilarity′ : Size → Rel₂ ℓ Proc
Bisimilarity′ = ν′ StepC

infix 4 [_]_∼_ [_]_∼′_ _∼_ _∼′_

[_]_∼_ : Size → Proc → Proc → Type ℓ
[ i ] p ∼ q = ν StepC i (p , q)

[_]_∼′_ : Size → Proc → Proc → Type ℓ
[ i ] p ∼′ q = ν′ StepC i (p , q)

_∼_ : Proc → Proc → Type ℓ
_∼_ = [ ∞ ]_∼_

_∼′_ : Proc → Proc → Type ℓ
_∼′_ = [ ∞ ]_∼′_

private

  -- However, these definitions are definitionally equivalent to
  -- corresponding definitions in General.

  indirect-Bisimilarity : Bisimilarity ≡ General.Bisimilarity
  indirect-Bisimilarity = refl

  indirect-Bisimilarity′ : Bisimilarity′ ≡ General.Bisimilarity′
  indirect-Bisimilarity′ = refl

  indirect-[]∼ : [_]_∼_ ≡ General.[_]_∼_
  indirect-[]∼ = refl

  indirect-[]∼′ : [_]_∼′_ ≡ General.[_]_∼′_
  indirect-[]∼′ = refl

  indirect-∼ : _∼_ ≡ General._∼_
  indirect-∼ = refl

  indirect-∼′ : _∼′_ ≡ General._∼′_
  indirect-∼′ = refl

-- Combinators that can perhaps make the code a bit nicer to read.

infix -3 _⟶⟨_⟩ʳˡ_ _[_]⟶⟨_⟩ʳˡ_
         lr-result-with-action lr-result-without-action

_⟶⟨_⟩ʳˡ_ : ∀ {i p′ q′ μ} p → p [ μ ]⟶ p′ → [ i ] p′ ∼′ q′ →
           ∃ λ p′ → p [ μ ]⟶ p′ × [ i ] p′ ∼′ q′
_ ⟶⟨ p⟶p′ ⟩ʳˡ p′∼′q′ = _ , p⟶p′ , p′∼′q′

_[_]⟶⟨_⟩ʳˡ_ : ∀ {i p′ q′} p μ → p [ μ ]⟶ p′ → [ i ] p′ ∼′ q′ →
              ∃ λ p′ → p [ μ ]⟶ p′ × [ i ] p′ ∼′ q′
_ [ _ ]⟶⟨ p⟶p′ ⟩ʳˡ p′∼′q′ = _ ⟶⟨ p⟶p′ ⟩ʳˡ p′∼′q′

lr-result-without-action :
  ∀ {i p′ q′ μ} → [ i ] p′ ∼′ q′ → ∀ q → q [ μ ]⟶ q′ →
  ∃ λ q′ → q [ μ ]⟶ q′ × [ i ] p′ ∼′ q′
lr-result-without-action p′∼′q′ _ q⟶q′ = _ , q⟶q′ , p′∼′q′

lr-result-with-action :
  ∀ {i p′ q′} → [ i ] p′ ∼′ q′ → ∀ μ q → q [ μ ]⟶ q′ →
  ∃ λ q′ → q [ μ ]⟶ q′ × [ i ] p′ ∼′ q′
lr-result-with-action p′∼′q′ _ _ q⟶q′ =
  lr-result-without-action  p′∼′q′ _ q⟶q′

syntax lr-result-without-action p′∼q′   q q⟶q′ = p′∼q′      ⟵⟨ q⟶q′ ⟩ q
syntax lr-result-with-action    p′∼q′ μ q q⟶q′ = p′∼q′ [ μ ]⟵⟨ q⟶q′ ⟩ q

-- Strong bisimilarity is a weak simulation (of a certain kind).

strong-is-weak⇒̂ :
  ∀ {p p′ q μ} →
  p ∼ q → p [ μ ]⇒̂ p′ →
  ∃ λ q′ → q [ μ ]⇒̂ q′ × p′ ∼ q′
strong-is-weak⇒̂ =
  is-weak⇒̂ StepC.left-to-right (λ p∼′q → force p∼′q)
           (λ s tr → step s tr done) ⟶→⇒̂

mutual

  -- Bisimilarity is symmetric.

  symmetric-∼ : ∀ {i p q} → [ i ] p ∼ q → [ i ] q ∼ p
  symmetric-∼ p∼q =
    StepC.⟨ Σ-map id (Σ-map id symmetric-∼′) ∘ StepC.right-to-left p∼q
          , Σ-map id (Σ-map id symmetric-∼′) ∘ StepC.left-to-right p∼q
          ⟩

  symmetric-∼′ : ∀ {i p q} → [ i ] p ∼′ q → [ i ] q ∼′ p
  force (symmetric-∼′ p∼q) = symmetric-∼ (force p∼q)

private

  -- An alternative proof of symmetry.

  alternative-proof-of-symmetry : ∀ {i p q} → [ i ] p ∼ q → [ i ] q ∼ p
  alternative-proof-of-symmetry {i} =
    uncurry [ i ]_∼_ ⁻¹  ⊆⟨ ν-symmetric _ _ swap refl F.id ⟩∎
    uncurry [ i ]_∼_     ∎

-- Strong bisimilarity is transitive.

transitive-∼ : ∀ {i p q r} → [ i ] p ∼ q → [ i ] q ∼ r → [ i ] p ∼ r
transitive-∼ {i} = λ p q →
  StepC.⟨ lr p q
        , Σ-map id (Σ-map id symmetric-∼′) ∘
          lr (symmetric-∼ q) (symmetric-∼ p)
        ⟩
  where
  lr : ∀ {p p′ q r μ} →
       [ i ] p ∼ q → [ i ] q ∼ r → p [ μ ]⟶ p′ →
       ∃ λ r′ → r [ μ ]⟶ r′ × [ i ] p′ ∼′ r′
  lr p q tr =
    let (_ , tr′ , p′) = StepC.left-to-right p tr
        (_ , tr″ , q′) = StepC.left-to-right q tr′
    in  (_ , tr″ , λ { .force → transitive-∼ (force p′) (force q′) })

transitive-∼′ :
  ∀ {i p q r} → [ i ] p ∼′ q → [ i ] q ∼′ r → [ i ] p ∼′ r
force (transitive-∼′ p∼q q∼r) = transitive-∼ (force p∼q) (force q∼r)


open import Agda.Builtin.Reflection
open import Agda.Builtin.List
open import Agda.Builtin.Unit

macro

  m-0 : Term → TC ⊤
  m-0 goal =
    bindTC (inferType goal) λ where
      (pi (arg (arg-info _ (modality _ quantity-0)) _) _) →
        bindTC (quoteTC (λ (_ : Set) → Set))
               (unify goal)
      type → typeError (termErr type ∷ [])

  m-ω : Term → TC ⊤
  m-ω goal =
    bindTC (inferType goal) λ where
      (pi (arg (arg-info _ (modality _ quantity-ω)) _) _) →
        bindTC (quoteTC (λ (_ : Set) → Set))
               (unify goal)
      type → typeError (termErr type ∷ [])

_ : @0 Set → Set₁
_ = m-0

_ : @ω Set → Set₁
_ = m-ω

postulate
  f : @0 Set₁ → Set₁

macro

  m₁ : Set₁ → Term → TC ⊤
  m₁ A goal =
    bindTC (quoteTC A) λ A →
    unify goal
      (def (quote f)
         (arg (arg-info visible (modality relevant quantity-0)) A ∷
          []))

_ : Set₁ → Set₁
_ = λ A → m₁ A

macro

  m₂ : Set₁ → Term → TC ⊤
  m₂ A goal =
    bindTC (quoteTC A) λ A →
    unify goal
      (def (quote f)
                                -- The modality is ignored.
         (arg (arg-info visible (modality irrelevant quantity-ω)) A ∷
          []))

_ : Set₁ → Set₁
_ = λ A → m₂ A


{-# OPTIONS --type-in-type --no-termination-check --no-positivity-check
 #-}

module Data1 where

_-_ : {A : Set}{B : A -> Set}{C : (a : A) -> B a -> Set} ->
      ({a : A}(b : B a) -> C a b) -> (f : (a : A) -> B a) ->
      (a : A) -> C a (f a)
f - g = \ x -> f (g x)

id : {X : Set} -> X -> X
id x = x

konst : {S : Set}{T : S -> Set} -> ({x : S} -> T x) -> (x : S) -> T x
konst c x = c

data Zero : Set where
record One : Set where

record Sig (S : Set)(T : S -> Set) : Set where
  field
    fst : S
    snd : T fst

open module Sig' {S : Set}{T : S -> Set} = Sig {S}{T}

_,_ : forall {S T}(s : S) -> T s -> Sig S T
s , t = record {fst = s; snd = t}

infixr 40 _,_

_*_ : Set -> Set -> Set
S * T = Sig S \ _ -> T

sig : forall {S T}{P : Sig S T -> Set} ->
      ((s : S)(t : T s) -> P (s , t)) -> (st : Sig S T) -> P st
sig f st = f (fst st) (snd st)

data Cx : Set where
  E : Cx
  _/_ : Cx -> Set -> Cx

data _:>_ : Cx -> Set -> Set where
  ze : {G : Cx}{S : Set} -> (G / S) :> S
  su : {G : Cx}{S T : Set} -> G :> T -> (G / S) :> T

data PROP : Set where
  Absu : PROP
  Triv : PROP
  _/\_ : PROP -> PROP -> PROP
  All : (S : Set) -> (S -> PROP) -> PROP

Prf : PROP -> Set
Prf Absu = Zero
Prf Triv = One
Prf (P /\ Q) = Prf P * Prf Q
Prf (All S P) = (x : S) -> Prf (P x)

data CON (G : Cx) : Set where
  ?? : {S : Set} -> G :> S -> S -> CON G
  PrfC : PROP -> CON G
  _*C_ : CON G -> CON G -> CON G
  PiC : (S : Set) -> (S -> CON G) -> CON G
  SiC : (S : Set) -> (S -> CON G) -> CON G
  MuC : (O : Set) -> (O -> CON (G / O)) -> O -> CON G
  NuC : (O : Set) -> (O -> CON (G / O)) -> O -> CON G

Box : (G : Cx) -> ((S : Set) -> (G :> S) -> Set) -> Set
Box E F = One
Box (G / S) F = Box G (\ S n -> F S (su n)) * F S ze

pr : {G : Cx}{S : Set}{F : (S : Set) -> (G :> S) -> Set} -> Box G F -> (n : G :> S) -> F S n
pr fsf ze = snd fsf
pr fsf (su n) = pr (fst fsf) n

Pay : Cx -> Set
Pay G = Box G (\ I _ -> I -> Set)

Arr : (G : Cx) -> Pay G -> Pay G -> Set
Arr G X Y = Box G (\ S n -> (s : S) -> pr X n s -> pr Y n s)

mutual
  [|_|] : {G : Cx} -> CON G -> Pay G -> Set
  [| ?? n i |]     X = pr X n i
  [| PrfC P |]     X = Prf P
  [| C *C D |]     X = [| C |] X * [| D |] X
  [| PiC S C |]    X = (s : S) -> [| C s |] X
  [| SiC S C |]    X = Sig S \ s -> [| C s |] X
  [| MuC O C o |]  X = Mu C X o
  [| NuC O C o |]  X = Nu C X o

  data Mu {G : Cx}{O : Set}(C : O -> CON (G / O))(X : Pay G)(o : O) : Set where
    con : [| C o |] ( X , Mu C X ) -> Mu C X o
  codata Nu {G : Cx}{O : Set}(C : O -> CON (G / O))(X : Pay G)(o : O) : Set where
    con : [| C o |] ( X , Nu C X ) -> Nu C X o

noc :  {G : Cx}{O : Set}{C : O -> CON (G / O)}{X : Pay G}{o : O} ->
       Nu C X o -> [| C o |] ( X , Nu C X )
noc (con xs) = xs

mutual
  map : {G : Cx}{X Y : Pay G} -> (C : CON G) ->
        Arr G X Y ->
        [| C |] X -> [| C |] Y
  map (?? n i)     f = pr f n i
  map (PrfC P)     f = id
  map (C *C D)     f = sig \ c d -> map C f c , map D f d
  map (PiC S C)    f = \ c s -> map (C s) f (c s)
  map (SiC S C)    f = sig \ s c -> s , map (C s) f c
  map (MuC O C o)  f = foldMap C (Mu C _) f (\ o -> con) o
  map (NuC O C o)  f = unfoldMap C (Nu C _) f (\ o -> noc) o

  foldMap :  {G : Cx}{O : Set}(C : O -> CON (G / O)){X Y : Pay G}(Z : O -> Set) ->
          Arr G X Y ->
          ((o : O) -> [| C o |] ( Y , Z ) -> Z o) ->
          (o : O) -> Mu C X o -> Z o
  foldMap C Z f alg o (con xs) = alg o (map (C o) (f , foldMap C Z f alg) xs)

  unfoldMap :  {G : Cx}{O : Set}(C : O -> CON (G / O)){X Y : Pay G}(Z : O -> Set) ->
            Arr G X Y ->
            ((o : O) -> Z o -> [| C o |] ( X , Z )) ->
            (o : O) -> Z o -> Nu C Y o
  unfoldMap C Z f coalg o y = con (map (C o) (f , unfoldMap C Z f coalg) (coalg o y))

ida : (G : Cx) -> (X : Pay G) -> Arr G X X
ida E = id
ida (G / S) = sig \ Xs X -> ida G Xs , \ s x -> x

compa : (G : Cx) -> (X Y Z : Pay G) -> Arr G Y Z -> Arr G X Y -> Arr G X Z
compa E = \ _ _ _ _ _ -> _
compa (G / S) = sig \ Xs X -> sig \ Ys Y -> sig \ Zs Z -> sig \ fs f -> sig \ gs g ->
  compa G Xs Ys Zs fs gs , \ s x -> f s (g s x)

data _==_ {X : Set}(x : X) : {Y : Set} -> Y -> Set where
  refl : x == x

subst : {X : Set}{P : X -> Set}{x y : X} -> x == y -> P x -> P y
subst refl p = p

_=,=_ : {S : Set}{T : S -> Set}
        {s0 : S}{s1 : S} -> s0 == s1 ->
        {t0 : T s0}{t1 : T s1} -> t0 == t1 ->
        _==_ {Sig S T} (s0 , t0) {Sig S T} (s1 , t1)
refl =,= refl = refl

mylem :  (G : Cx) -> (X : Pay G) -> compa G X X X (ida G X) (ida G X) == ida G X
mylem E X = refl
mylem (G / S) X = mylem G (fst X) =,= refl

claim : (G : Cx){O : Set}(C : O -> CON (G / O))(X : Pay G)
        (P : (o : O) -> Mu C X o -> Set) -> (p : (o : O)(y : Mu C X o) -> P o y)
        (o : O) ->
        compa (G / O) (X , Mu C X) (X , (\ o -> Sig (Mu C X o) (P o))) (X , Mu C X)
         (ida G X , konst fst) (ida G X , (\ o y -> (y , p o y)))
        == ida (G / O) (X , Mu C X)
claim G C X P p o = mylem G X =,= refl

idLem : {G : Cx}{S : Set}{X : Pay G}(n : G :> S) ->
        pr (ida G X) n == (\ (s : S)(x : pr X n s) -> x)
idLem ze = refl
idLem (su y) = idLem y

_=$_ : {S : Set}{T : S -> Set}{f g : (s : S) -> T s} ->
        f == g -> (s : S) -> f s == g s
refl =$ s = refl

mutual  -- and too intensional
  mapId : {G : Cx}{X : Pay G} -> (C : CON G) -> (xs : [| C |] X) ->
          map C (ida G X) xs == xs
  mapId (?? y y') xs = (idLem y =$ y') =$ xs
  mapId (PrfC y) xs = refl
  mapId (y *C y') xs = mapId y (fst xs) =,= mapId y' (snd xs)
  mapId (PiC S y) xs = {!!} -- no chance
  mapId (SiC S y) xs = refl =,= mapId (y (fst xs)) (snd xs)
  mapId (MuC O y y') xs = foldMapId y y' xs
  mapId (NuC O y y') xs = {!!}

  foldMapId : {G : Cx}{O : Set}(C : O -> CON (G / O)){X : Pay G}
              (o : O)(z : Mu C X o) ->
              foldMap C (Mu C X) (ida G X) (\ o -> con) o z == z
  foldMapId C o (con xzs) = {!!} -- close but no banana

mapComp :  {G : Cx}{X Y Z : Pay G} (f : Arr G Y Z)(g : Arr G X Y)
           (C : CON G) -> (xs : [| C |] X) ->
           map C f (map C g xs) == map C (compa G X Y Z f g) xs
mapComp = {!!}

_-=-_ : {X : Set}{x y z : X} -> x == y -> y == z -> x == z
refl -=- refl = refl

induction : {G : Cx}{O : Set}(C : O -> CON (G / O)){X : Pay G}
            (P : (o : O) -> Mu C X o -> Set) ->
            ((o : O)(xyps : [| C o |] (X , (\ o -> Sig (Mu C X o) (P o)))) ->
             P o (con (map (C o) (ida G X , konst fst) xyps))) ->
            (o : O)(y : Mu C X o) -> P o y
induction {G} C {X} P p o (con xys) =
  subst {P = \ xys -> P o (con xys)}{y = xys}
    ((mapComp
     (ida G X , konst fst) (ida G X , (\ o y -> (y , induction C P p o y))) (C o) xys
     -=- {!!})
      -=- mapId (C o) xys)
    (p o (map (C o) (ida G X , (\ o y -> (y , induction C P p o y))) xys))


module Impure.LFRef.Welltyped where

open import Prelude

open import Data.List hiding ([_])
open import Data.List.All hiding (map)
open import Data.Vec as Vec hiding ([_]; map)
open import Data.Star hiding (_▻▻_; map)
open import Data.Sum hiding (map)
open import Extensions.List as L using ()

open import Impure.LFRef.Syntax hiding (subst)
open import Relation.Binary.List.Pointwise using (Rel)

Ctx : (n : ℕ) → Set
Ctx n = Vec (Type n) n

-- store typings
World : Set
World = List (Type 0)

weaken₁-tp : ∀ {n} → Type n → Type (suc n)
weaken₁-tp tp = tp tp/ wk

_:+:_ : ∀ {n} → Type n → Ctx n → Ctx (suc n)
a :+: Γ = (weaken₁-tp a) ∷ (Vec.map (flip _tp/_ wk) Γ)

weaken+-tm : ∀ {m} n → Term m → Term (n + m)
weaken+-tm n t = t / (wk⋆ n)

weaken+-tp : ∀ n → Type 0 → Type n
weaken+-tp zero t = t
weaken+-tp (suc n) t = subst Type (+-right-identity (suc n)) (t tp/ (wk⋆ (suc n)))

weaken+-tele : ∀ {m n} k → Tele n m → Tele (n + k) m
weaken+-tele k T = subst (flip Tele _) (+-comm k _) (T tele/ (wk⋆ k))

-- mutually inductive welltypedness judgments for kinds/types and terms respectively
data _,_,_⊢_teleok : ∀ {n m} → (𝕊 : Sig) → World → Ctx n → Tele n m → Set
data _,_,_⊢_::_ : ∀ {n m} (𝕊 : Sig) → World → Ctx n → Type n → Tele n m → Set
data _,_,_⊢_∶_ : ∀ {n} (𝕊 : Sig) → World → Ctx n → Term n → Type n → Set

data _,_,_⊢_teleok where
  ε : ∀ {n 𝕊 Σ} {Γ : Ctx n} → 𝕊 , Σ , Γ ⊢ ε teleok

  _⟶_ : ∀ {n m 𝕊 Σ Γ} {A : Type n} {K : Tele (suc n) m}→
        𝕊 , Σ , Γ ⊢ A :: ε →
        𝕊 , Σ , (A :+: Γ) ⊢ K teleok →
        𝕊 , Σ , Γ ⊢ (A ⟶ K) teleok

data _,_,_⊢_∶ⁿ_ {n} (𝕊 : Sig) (Σ : World) (Γ : Ctx n) :
     ∀ {m} → List (Term n) → Tele n m → Set where

  ε : 𝕊 , Σ , Γ ⊢ [] ∶ⁿ ε

  _⟶_ : ∀ {m A t ts} {B : Tele (suc n) m}→
        𝕊 , Σ , Γ ⊢ t ∶ A →
        𝕊 , Σ , Γ ⊢ ts ∶ⁿ (B tele/ (sub t)) →
        𝕊 , Σ , Γ ⊢ (t ∷ ts) ∶ⁿ (A ⟶ B)

-- specialize the returntype from a constructor from it's welltyped arguments
_con[_/_] : ∀ {n} → (C : ConType) → (ts : List (Term n)) → length ts ≡ (ConType.m C) → Type n
_con[_/_] {n} C ts p =
  (ConType.tp C) [
    map
      (flip _/_ (subst (Vec _) p (fromList ts)))
      (ConType.indices C)
  ]

-- specialize the return type of a function from it's welltyped arguments
_fun[_/_] : ∀ {n m} → Type m → (ts : List (Term n)) → length ts ≡ m → Type n
_fun[_/_] {n} {m} a ts p = a tp/ subst (Vec _) p ((fromList ts))

data _,_,_⊢_::_ where

  Ref : ∀ {n 𝕊 Σ} {Γ : Ctx n} {A} →
        𝕊 , Σ , Γ ⊢ A :: ε →
        ----------------------
        𝕊 , Σ , Γ ⊢ Ref A :: ε

  Unit : ∀ {n 𝕊 Σ} {Γ : Ctx n} →
        ---------------------
        𝕊 , Σ , Γ ⊢ Unit :: ε

  _[_] : ∀ {n 𝕊 Σ} {Γ : Ctx n} {k K ts} →
         (Sig.types 𝕊) L.[ k ]= K →
         𝕊 , [] , [] ⊢ (proj₂ K) teleok →
         𝕊 , Σ , Γ ⊢ ts ∶ⁿ (weaken+-tele n (proj₂ K)) →
         -------------------------
         𝕊 , Σ , Γ ⊢ k [ ts ] :: ε

data _,_,_⊢_∶_ where

  unit : ∀ {n 𝕊 Σ} {Γ : Ctx n} →
        -----------------------
        𝕊 , Σ , Γ ⊢ unit ∶ Unit

  var : ∀ {n 𝕊 Σ} {Γ : Ctx n} {i A} →
        Γ [ i ]= A →
        ---------------------
        𝕊 , Σ , Γ ⊢ var i ∶ A

  con : ∀ {n 𝕊 Σ} {Γ : Ctx n} {c C ts} →
        (Sig.constructors 𝕊) L.[ c ]= C →
        (p : 𝕊 , Σ , Γ ⊢ ts ∶ⁿ weaken+-tele n (ConType.args C)) →
        (q : length ts ≡ (ConType.m C)) →
        ------------------------------------
        𝕊 , Σ , Γ ⊢ con c ts ∶ (C con[ ts / q ])

  loc : ∀ {n 𝕊 Σ} {Γ : Ctx n} {i S} →
        Σ L.[ i ]= S →
        ---------------------
        𝕊 , Σ , Γ ⊢ loc i ∶ Ref (weaken+-tp n S)

data _,_,_⊢ₑ_∶_ : ∀ {n} (𝕊 : Sig) → World → Ctx n → Exp n → Type n → Set where

  tm   : ∀ {n t} {Γ : Ctx n} {𝕊 Σ A} →
         𝕊 , Σ , Γ ⊢ t ∶ A →
         ---------------------
         𝕊 , Σ , Γ ⊢ₑ tm t ∶ A

  _·★[_]_ : ∀ {n fn ts 𝕊 Σ φ} {Γ : Ctx n} →
         (Sig.funs 𝕊) L.[ fn ]= φ →
         (q : length ts ≡ (Fun.m φ)) →
         (p : 𝕊 , Σ , Γ ⊢ ts ∶ⁿ weaken+-tele n (Fun.args φ)) →
         -----------------------------------------------------
         𝕊 , Σ , Γ ⊢ₑ (fn ·★ ts) ∶ ((Fun.returntype φ) fun[ ts / q ])

  ref : ∀ {n x A 𝕊 Σ} {Γ : Ctx n} →
        𝕊 , Σ , Γ ⊢ₑ x ∶ A →
        --------------------------
        𝕊 , Σ , Γ ⊢ₑ ref x ∶ Ref A

  !_  : ∀ {n x A} {Γ : Ctx n} {𝕊 Σ} →

        𝕊 , Σ , Γ ⊢ₑ x ∶ Ref A →
        ----------------------
        𝕊 , Σ , Γ ⊢ₑ (! x) ∶ A

  _≔_ : ∀ {n i x A} {Γ : Ctx n} {𝕊 Σ} →
        𝕊 , Σ , Γ ⊢ₑ i ∶ Ref A →
        𝕊 , Σ , Γ ⊢ₑ x ∶ A →
        --------------------------
        𝕊 , Σ , Γ ⊢ₑ (i ≔ x) ∶ Unit

data _,_,_⊢ₛ_∶_ : ∀ {n} (𝕊 : Sig) → World → Ctx n → SeqExp n → Type n → Set where

  ret  : ∀ {n x A 𝕊 Σ} {Γ : Ctx n} →
         𝕊 , Σ , Γ ⊢ₑ x ∶ A →
         ---------------------
         𝕊 , Σ , Γ ⊢ₛ ret x ∶ A

  lett : ∀ {n x c A B 𝕊 Σ} {Γ : Ctx n} →
         𝕊 , Σ , Γ ⊢ₑ x ∶ A →
         𝕊 , (Σ) , (A :+: Γ) ⊢ₛ c ∶ weaken₁-tp B →
         ---------------------------------------r
         𝕊 , Σ , Γ ⊢ₛ lett x c ∶ B

-- telescopes as context transformers
_⊢⟦_⟧ : ∀ {n m} → Ctx n → Tele n m → Ctx (n + m)
Γ ⊢⟦ ε ⟧ = subst Ctx (sym $ +-right-identity _) Γ
_⊢⟦_⟧ {n} Γ (_⟶_ {m = m} x T) = subst Ctx (sym $ +-suc n m) ((x :+: Γ) ⊢⟦ T ⟧)

_⊢_fnOk : Sig → Fun → Set
_⊢_fnOk 𝕊 φ = 𝕊 , [] , ([] ⊢⟦ Fun.args φ ⟧) ⊢ₑ (Fun.body φ) ∶ (Fun.returntype φ)

-- valid signature contexts
record _,_⊢ok {n} (𝕊 : Sig) (Γ : Ctx n) : Set where
  field
    funs-ok : All (λ x → 𝕊 ⊢ x fnOk) (Sig.funs 𝕊)

-- store welltypedness relation
-- as a pointwise lifting of the welltyped relation on closed expressions between a world and a store
_,_⊢_ : Sig → World → Store → Set
_,_⊢_ 𝕊 Σ μ = Rel (λ A x → 𝕊 , Σ , [] ⊢ (proj₁ x) ∶ A) Σ μ

-- a useful lemma about telescoped terms
tele-fit-length : ∀ {n m 𝕊 Σ Γ ts} {T : Tele n m} → 𝕊 , Σ , Γ ⊢ ts ∶ⁿ T → length ts ≡ m
tele-fit-length ε = refl
tele-fit-length (x ⟶ p) with tele-fit-length p
tele-fit-length (x ⟶ p) | refl = refl


------------------------------------------------------------------------
-- Admissible rules are sometimes not "postulable"
------------------------------------------------------------------------

-- Even though a rule is admissible it may not be sound to postulate
-- it, i.e. add it as an inductive constructor. This was observed by
-- Edsko de Vries in a message to the Coq-club mailing list (Re:
-- [Coq-Club] Adding (inductive) transitivity to weak bisimilarity not
-- sound? (was: Need help with coinductive proof), 2009-08-28).

module AdmissibleButNotPostulable where

open import Codata.Musical.Notation using (∞; ♯_; ♭)
open import Data.Nat
open import Data.Product as Prod
open import Function
open import Relation.Binary.PropositionalEquality as P using (_≡_; [_])
open import Relation.Nullary using (¬_)

------------------------------------------------------------------------
-- The partiality monad

data _⊥ (A : Set) : Set where
  now   : (v : A) → A ⊥
  later : (x : ∞ (A ⊥)) → A ⊥

------------------------------------------------------------------------
-- Weak equality of computations in the partiality monad

module WeakEquality where

  infix 4 _≈_

  data _≈_ {A : Set} : A ⊥ → A ⊥ → Set where
    now    : ∀ {v}                         → now   v ≈ now   v
    later  : ∀ {x y} (x≈y : ∞ (♭ x ≈ ♭ y)) → later x ≈ later y
    laterʳ : ∀ {x y} (x≈y :      x ≈ ♭ y ) →       x ≈ later y
    laterˡ : ∀ {x y} (x≈y :    ♭ x ≈   y ) → later x ≈       y

  -- Some lemmas.

  laterʳ⁻¹ : ∀ {A : Set} {x : A ⊥} {y} → x ≈ later y → x ≈ ♭ y
  laterʳ⁻¹ (later  x≈y)  = laterˡ        (♭ x≈y)
  laterʳ⁻¹ (laterʳ x≈y)  = x≈y
  laterʳ⁻¹ (laterˡ x≈ly) = laterˡ (laterʳ⁻¹ x≈ly)

  laterˡ⁻¹ : ∀ {A : Set} {x} {y : A ⊥} → later x ≈ y → ♭ x ≈ y
  laterˡ⁻¹ (later  x≈y)  = laterʳ         (♭ x≈y)
  laterˡ⁻¹ (laterʳ lx≈y) = laterʳ (laterˡ⁻¹ lx≈y)
  laterˡ⁻¹ (laterˡ x≈y)  = x≈y

  -- Weak equality is an equivalence relation.

  refl : {A : Set} (x : A ⊥) → x ≈ x
  refl (now v)   = now
  refl (later x) = later (♯ refl (♭ x))

  sym : {A : Set} {x y : A ⊥} → x ≈ y → y ≈ x
  sym now          = now
  sym (later  x≈y) = later (♯ sym (♭ x≈y))
  sym (laterʳ x≈y) = laterˡ (sym x≈y)
  sym (laterˡ x≈y) = laterʳ (sym x≈y)

  trans : {A : Set} {x y z : A ⊥} → x ≈ y → y ≈ z → x ≈ z
  trans {x = now v} {z = z} p q = tr p q
    where
    tr : ∀ {y} → now v ≈ y → y ≈ z → now v ≈ z
    tr now          y≈z  = y≈z
    tr (laterʳ v≈y) ly≈z = tr v≈y (laterˡ⁻¹ ly≈z)
  trans {x = later x} lx≈y y≈z = tr lx≈y y≈z
    where
    tr : ∀ {y z} → later x ≈ y → y ≈ z → later x ≈ z
    tr         lx≈ly (later  y≈z) = later  (♯ trans (laterˡ⁻¹ lx≈ly) (laterˡ (♭ y≈z)))
    tr         lx≈y  (laterʳ y≈z) = later  (♯ trans (laterˡ⁻¹ lx≈y)             y≈z  )
    tr         lx≈ly (laterˡ y≈z) =           tr    (laterʳ⁻¹ lx≈ly)            y≈z
    tr (laterˡ  x≈y)         y≈z  = laterˡ (  trans            x≈y              y≈z  )

  -- Non-termination.

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

  -- Weak equality is not trivial (assuming that the argument to _⊥ is
  -- non-empty).

  non-trivial : {A : Set} {v : A} → ¬ now v ≈ never
  non-trivial (laterʳ v≈⊥) = non-trivial v≈⊥

------------------------------------------------------------------------
-- Extended weak equality

module ExtendedWeakEquality where

  infix  4 _≈_
  infix  3 _∎
  infixr 2 _≈⟨_⟩_

  -- Let us try to postulate transitivity using an inductive rule.

  data _≈_ {A : Set} : A ⊥ → A ⊥ → Set where
    now    : ∀ {v}                         → now   v ≈ now   v
    later  : ∀ {x y} (x≈y : ∞ (♭ x ≈ ♭ y)) → later x ≈ later y
    laterʳ : ∀ {x y} (x≈y :      x ≈ ♭ y ) →       x ≈ later y
    laterˡ : ∀ {x y} (x≈y :    ♭ x ≈   y ) → later x ≈       y

    -- Transitivity.
    _≈⟨_⟩_ : ∀ x {y z} (x≈y : x ≈ y) (y≈z : y ≈ z) → x ≈ z

  -- Reflexivity.

  _∎ : {A : Set} (x : A ⊥) → x ≈ x
  now   v ∎ = now
  later x ∎ = later (♯ (♭ x ∎))

  -- Extended weak equality is trivial.

  trivial : {A : Set} (x y : A ⊥) → x ≈ y
  trivial x y =
    x           ≈⟨ laterʳ (x ∎)           ⟩
    later (♯ x) ≈⟨ later  (♯ trivial x y) ⟩
    later (♯ y) ≈⟨ laterˡ (y ∎)           ⟩
    y           ∎

  -- The problem is that there is no "contractive" proof of
  -- transitivity; the proof given above consumes the input
  -- certificate "faster" than it produces the output certificate.

------------------------------------------------------------------------
-- Capretta's definition of equality coincides with weak equality

-- This is not really related to the problem discussed above, I just
-- want to ensure that the definition of weak equality is not too
-- strange.

module Capretta'sEquality where

  infix 4 _⇓_ _≈_

  -- x ⇓ v means that x terminates with the value v.

  data _⇓_ {A : Set} : A ⊥ → A → Set where
    now   : ∀ {v} → now v ⇓ v
    later : ∀ {x v} (x⇓v : ♭ x ⇓ v) → later x ⇓ v

  -- Equality as defined by Capretta in "General Recursion via
  -- Coinductive Types".

  data _≈_ {A : Set} : A ⊥ → A ⊥ → Set where
    now   : ∀ {x y v} (x⇓v : x ⇓ v) (y⇓v : y ⇓ v) → x ≈ y
    later : ∀ {x y} (x≈y : ∞ (♭ x ≈ ♭ y)) → later x ≈ later y

  -- Soundness.

  open WeakEquality using () renaming (_≈_ to _≋_)

  sound : {A : Set} {x y : A ⊥} → x ≈ y → x ≋ y
  sound (later x≈y)   = WeakEquality.later (♯ sound (♭ x≈y))
  sound (now x⇓v y⇓v) = nw x⇓v y⇓v
    where
    nw : ∀ {A : Set} {x y : A ⊥} {v} → x ⇓ v → y ⇓ v → x ≋ y
    nw now         now         = WeakEquality.now
    nw x⇓v         (later y⇓v) = WeakEquality.laterʳ (nw x⇓v y⇓v)
    nw (later x⇓v) y⇓v         = WeakEquality.laterˡ (nw x⇓v y⇓v)

  -- Completeness.

  data _≈P_ {A : Set} : A ⊥ → A ⊥ → Set where
    now    : ∀ {x y v} (x⇓v : x ⇓ v) (y⇓v : y ⇓ v) → x ≈P y
    later  : ∀ {x y} (x≈y : ∞ (♭ x ≈P ♭ y)) → later x ≈P later y
    laterʳ : ∀ {x y} (x≈y :      x ≈P ♭ y ) →       x ≈P later y
    laterˡ : ∀ {x y} (x≈y :    ♭ x ≈P   y ) → later x ≈P       y

  data _≈W_ {A : Set} : A ⊥ → A ⊥ → Set where
    now   : ∀ {x y v} (x⇓v : x ⇓ v) (y⇓v : y ⇓ v) → x ≈W y
    later : ∀ {x y} (x≈y : ♭ x ≈P ♭ y) → later x ≈W later y

  laterʳW : ∀ {A : Set} {x : A ⊥} {y} → x ≈W ♭ y → x ≈W later y
  laterʳW {y = y} x≈y with ♭ y | P.inspect ♭ y
  laterʳW x≈y | y′ | [ eq ] with x≈y
  laterʳW x≈y | y′ | [ eq ] | now {v = v} x⇓v y⇓v =
    now x⇓v (later (P.subst (λ y → y ⇓ v) (P.sym eq) y⇓v))
  laterʳW x≈y | later y′ | [ eq ] | later x′≈y′ =
    later (P.subst (_≈P_ _) (P.sym eq) (laterʳ x′≈y′))

  laterˡW : ∀ {A : Set} {x} {y : A ⊥} → ♭ x ≈W y → later x ≈W y
  laterˡW {x = x} x≈y with ♭ x | P.inspect ♭ x
  laterˡW x≈y | x′ | [ eq ] with x≈y
  laterˡW x≈y | x′ | [ eq ] | now {v = v} x⇓v y⇓v =
    now (later (P.subst (λ x → x ⇓ v) (P.sym eq) x⇓v)) y⇓v
  laterˡW x≈y | later x′ | [ eq ] | later {y = y′} x′≈y′ =
    later (P.subst (λ x → x ≈P ♭ y′) (P.sym eq) (laterˡ x′≈y′))

  whnf : {A : Set} {x y : A ⊥} → x ≈P y → x ≈W y
  whnf (now x⇓v y⇓v) = now x⇓v y⇓v
  whnf (later  x≈y)  = later (♭ x≈y)
  whnf (laterʳ x≈y)  = laterʳW (whnf x≈y)
  whnf (laterˡ x≈y)  = laterˡW (whnf x≈y)

  mutual

    ⟦_⟧W : {A : Set} {x y : A ⊥} → x ≈W y → x ≈ y
    ⟦ now x⇓v y⇓v ⟧W = now x⇓v y⇓v
    ⟦ later x≈y   ⟧W = later (♯ ⟦ x≈y ⟧P)

    ⟦_⟧P : {A : Set} {x y : A ⊥} → x ≈P y → x ≈ y
    ⟦ x≈y ⟧P = ⟦ whnf x≈y ⟧W

  complete : {A : Set} {x y : A ⊥} → x ≋ y → x ≈ y
  complete x≋y = ⟦ completeP x≋y ⟧P
    where
    completeP : {A : Set} {x y : A ⊥} → x ≋ y → x ≈P y
    completeP WeakEquality.now          = now now now
    completeP (WeakEquality.later x≈y)  = later (♯ completeP (♭ x≈y))
    completeP (WeakEquality.laterʳ x≈y) = laterʳ (completeP x≈y)
    completeP (WeakEquality.laterˡ x≈y) = laterˡ (completeP x≈y)

------------------------------------------------------------------------
-- The weak equality above coincides with weak bisimilarity

module WeakBisimilarity {A : Set} where

  -- The function drop n drops n later constructors (if possible).

  drop : ℕ → A ⊥ → A ⊥
  drop zero    x         = x
  drop _       (now v)   = now v
  drop (suc n) (later x) = drop n (♭ x)

  -- Weak simulations and bisimulations. The removal of a later
  -- constructor is treated as a silent transition.

  record IsWeakSimulation (_R_ : A ⊥ → A ⊥ → Set) : Set where
    field
      match-later : ∀ {x y} → later x R y → ∃ λ n → ♭ x   R drop n y
      match-now   : ∀ {v y} → now v   R y → ∃ λ n → now v ≡ drop n y

  record IsWeakBisimulation (_R_ : A ⊥ → A ⊥ → Set) : Set where
    field
      left  : IsWeakSimulation _R_
      right : IsWeakSimulation (flip _R_)

  -- Weak bisimilarity.

  record _≈_ (x y : A ⊥) : Set₁ where
    field
      _R_   : A ⊥ → A ⊥ → Set
      xRy   : x R y
      bisim : IsWeakBisimulation _R_

  open WeakEquality hiding (module _≈_) renaming (_≈_ to _≋_)

  -- Completeness.

  complete : ∀ {x y} → x ≋ y → x ≈ y
  complete x≋y = record
    { _R_   = _≋_
    ; xRy   = x≋y
    ; bisim = record
      { left = record
        { match-later = λ lx≋y → (0 , laterˡ⁻¹ lx≋y)
        ; match-now   = match-now
        }
      ; right = record
        { match-later = λ x≋ly → (0 , laterʳ⁻¹ x≋ly)
        ; match-now   = match-now ∘ sym
        }
      }
    }
    where
    match-now : ∀ {v y} → now v ≋ y → ∃ λ n → now v ≡ drop n y
    match-now now          = (0 , P.refl)
    match-now (laterʳ v≋y) = Prod.map suc id (match-now v≋y)

  -- Soundness.

  module Sound {x y} (x≈y : x ≈ y) where

    open _≈_ x≈y
    open IsWeakBisimulation
    open IsWeakSimulation

    helper₁ : ∀ {x} y → (∃ λ n → now x ≡ drop n y) → now x ≋ y
    helper₁ (now y)   (zero  , P.refl) = now
    helper₁ (now y)   (suc n , P.refl) = now
    helper₁ (later y) (zero  , ())
    helper₁ (later y) (suc n , nx≡y-n) =
      laterʳ (helper₁ (♭ y) (n , nx≡y-n))

    mutual

      helper₂ : ∀ {x} y → (∃ λ n → x R drop n y) → x ≋ y
      helper₂ y         (zero  , xRy)   = sound _ _ xRy
      helper₂ (now y)   (suc n , xRny)  = sound _ _ xRny
      helper₂ (later y) (suc n , xRy-n) =
        laterʳ (helper₂ (♭ y) (n , xRy-n))

      helper₃ : ∀ x {y} → (∃ λ n → drop (suc n) x R y) → x ≋ y
      helper₃ (now x)   (n     , nxRy)  = sound _ _ nxRy
      helper₃ (later x) (zero  , xRy)   = laterˡ (sound _ _ xRy)
      helper₃ (later x) (suc n , x-nRy) =
        laterˡ (helper₃ (♭ x) (n , x-nRy))

      sound : ∀ x y → x R y → x ≋ y
      sound (now x) y nxRy = helper₁ y $ match-now (left bisim) nxRy
      sound (later x) (now y) lxRny =
        sym $ helper₁ (later x) $ match-now (right bisim) lxRny
      sound (later x) (later y) lxRly
        with match-later (left bisim) lxRly
      ... | (suc n , xRy-n) = later (♯ helper₂ (♭ y) (n , xRy-n))
      ... | (zero  , xRly)  with match-later (right bisim) xRly
      ...   | (zero  , xRy)     = later (♯ sound _ _ xRy)
      ...   | (suc n , x-1+nRy) =
        later (♯ helper₃ (♭ x) (n , x-1+nRy))

  sound : ∀ {x y} → x ≈ y → x ≋ y
  sound x≈y = Sound.sound x≈y _ _ (_≈_.xRy x≈y)

-- Note that the problem illustrated in ExtendedWeakEquality is
-- related to the problem of weak bisimulation up to weak
-- bisimilarity. Let R be a relation which is only inhabited for the
-- pair (later (♯ x), later (♯ y)). R is a weak bisimulation up to
-- weak bisimilarity (_≈_):
--
--   later (♯ x)         R later (♯ y)
--       ↓                     =
--       x ≈ later (♯ x) R later (♯ y)
--
--   later (♯ x) R           later (♯ y)
--       =                       ↓
--   later (♯ x) R later (♯ y) ≈ y
--
-- Weak bisimilarity is transitive, so if every relation which is a
-- weak bisimulation up to weak bisimilarity were contained in weak
-- bisimilarity we would have x ≈ y for all x and y.


module OlderBasicILP.Indirect.Gentzen where

open import OlderBasicILP.Indirect public

-- Derivations, as Gentzen-style natural deduction trees.

mutual
  data Tm : Set where
    VAR      : ℕ → Tm
    LAM      : Tm → Tm
    APP      : Tm → Tm → Tm
    MULTIBOX : Cx Tm → Tm → Tm
    DOWN     : Tm → Tm
    PAIR     : Tm → Tm → Tm
    FST      : Tm → Tm
    SND      : Tm → Tm
    UNIT     : Tm

  infix 3 _⊢_
  data _⊢_ (Γ : Cx (Ty Tm)) : Ty Tm → Set where
    var      : ∀ {A}   → A ∈ Γ → Γ ⊢ A
    lam      : ∀ {A B} → Γ , A ⊢ B → Γ ⊢ A ▻ B
    app      : ∀ {A B} → Γ ⊢ A ▻ B → Γ ⊢ A → Γ ⊢ B
    multibox : ∀ {n A} {SS : VCx Tm n} {Ξ : VCx (Ty Tm) n}
               → Γ ⊢⋆ SS ⦂⋆ Ξ → (u : SS ⦂⋆ Ξ ⊢ A)
               → Γ ⊢ [ u ] ⦂ A
    down     : ∀ {A T} → Γ ⊢ T ⦂ A → Γ ⊢ A
    pair     : ∀ {A B} → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ∧ B
    fst      : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ A
    snd      : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ B
    unit     : Γ ⊢ ⊤

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

  [_] : ∀ {A Γ} → Γ ⊢ A → Tm
  [ var i ]         = VAR [ i ]ⁱ
  [ lam t ]         = LAM [ t ]
  [ app t u ]       = APP [ t ] [ u ]
  [ multibox ts u ] = MULTIBOX [ ts ]⋆ [ u ]
  [ down t ]        = DOWN [ t ]
  [ pair t u ]      = PAIR [ t ] [ u ]
  [ fst t ]         = FST [ t ]
  [ snd t ]         = SND [ t ]
  [ unit ]          = UNIT

  [_]⋆ : ∀ {Ξ Γ} → Γ ⊢⋆ Ξ → Cx Tm
  [_]⋆ {∅}     ∙        = ∅
  [_]⋆ {Ξ , A} (ts , t) = [ ts ]⋆ , [ t ]

-- Monotonicity with respect to context inclusion.

mutual
  mono⊢ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ A → Γ′ ⊢ A
  mono⊢ η (var i)         = var (mono∈ η i)
  mono⊢ η (lam t)         = lam (mono⊢ (keep η) t)
  mono⊢ η (app t u)       = app (mono⊢ η t) (mono⊢ η u)
  mono⊢ η (multibox ts u) = multibox (mono⊢⋆ η ts) u
  mono⊢ η (down t)        = down (mono⊢ η t)
  mono⊢ η (pair t u)      = pair (mono⊢ η t) (mono⊢ η u)
  mono⊢ η (fst t)         = fst (mono⊢ η t)
  mono⊢ η (snd t)         = snd (mono⊢ η t)
  mono⊢ η unit            = unit

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

-- Shorthand for variables.

V₀ : Tm
V₀ = VAR 0

V₁ : Tm
V₁ = VAR 1

V₂ : Tm
V₂ = VAR 2

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

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

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

-- Reflexivity.

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

-- Deduction theorem is built-in.

-- Detachment theorem.

DET : Tm → Tm
DET T = APP T V₀

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

-- Cut and multicut.

CUT : Tm → Tm → Tm
CUT T U = APP (LAM U) T

MULTICUT : Cx Tm → Tm → Tm
MULTICUT ∅        U = U
MULTICUT (TS , T) U = APP (MULTICUT TS (LAM U)) T

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

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

-- Transitivity.

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

-- Contraction.

CCONT : Tm
CCONT = LAM (LAM (APP (APP V₁ V₀) V₀))

CONT : Tm → Tm
CONT T = DET (APP CCONT (LAM (LAM T)))

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

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

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

CEXCH : Tm
CEXCH = LAM (LAM (LAM (APP (APP V₂ V₀) V₁)))

EXCH : Tm → Tm
EXCH T = DET (DET (APP CEXCH (LAM (LAM T))))

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

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

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

CCOMP : Tm
CCOMP = LAM (LAM (LAM (APP V₂ (APP V₁ V₀))))

COMP : Tm → Tm → Tm
COMP T U = DET (APP (APP CCOMP (LAM T)) (LAM U))

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

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

-- Useful theorems in functional form.

DIST : Tm → Tm → Tm
DIST T U = MULTIBOX ((∅ , T) , U) (APP (DOWN V₁) (DOWN V₀))

UP : Tm → Tm
UP T = MULTIBOX (∅ , T) V₀

DISTUP : Tm → Tm → Tm
DISTUP T U = DIST T (UP U)

BOX : Tm → Tm
BOX T = MULTIBOX ∅ T

UNBOX : Tm → Tm → Tm
UNBOX T U = APP (LAM U) T

dist : ∀ {A B T U Γ}
       → (t : Γ ⊢ T ⦂ (A ▻ B)) → (u : Γ ⊢ U ⦂ A)
       → Γ ⊢ APP (DOWN V₁) (DOWN V₀) ⦂ B
dist t u = multibox ((∙ , t) , u) (app (down v₁) (down v₀))

up : ∀ {A T Γ}
     → Γ ⊢ T ⦂ A
     → Γ ⊢ V₀ ⦂ T ⦂ A
up t = multibox (∙ , t) v₀

distup : ∀ {A B T U Γ}
         → (t : Γ ⊢ T ⦂ (U ⦂ A ▻ B)) → (u : Γ ⊢ U ⦂ A)
         → Γ ⊢ APP (DOWN V₁) (DOWN V₀) ⦂ B
distup t u = dist t (up u)

box : ∀ {A Γ}
      → (t : ∅ ⊢ A)
      → Γ ⊢ [ t ] ⦂ A
box t = multibox ∙ t

unbox : ∀ {A C T U Γ}
        → Γ ⊢ T ⦂ A → Γ , T ⦂ A ⊢ U ⦂ C
        → Γ ⊢ U ⦂ C
unbox t u = app (lam u) t

-- Useful theorems in combinatory form.

CI : Tm
CI = LAM V₀

CK : Tm
CK = LAM (LAM V₁)

CS : Tm
CS = LAM (LAM (LAM (APP (APP V₂ V₀) (APP V₁ V₀))))

CDIST : Tm
CDIST = LAM (LAM (DIST V₁ V₀))

CUP : Tm
CUP = LAM (UP V₀)

CDOWN : Tm
CDOWN = LAM (DOWN V₀)

CDISTUP : Tm
CDISTUP = LAM (LAM (DIST V₁ (UP V₀)))

CUNBOX : Tm
CUNBOX = LAM (LAM (APP V₀ V₁))

CPAIR : Tm
CPAIR = LAM (LAM (PAIR V₁ V₀))

CFST : Tm
CFST = LAM (FST V₀)

CSND : Tm
CSND = LAM (SND V₀)

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

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

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

cdist : ∀ {A B T U Γ}
        → Γ ⊢ T ⦂ (A ▻ B) ▻ U ⦂ A ▻ APP (DOWN V₁) (DOWN V₀) ⦂ B
cdist = lam (lam (dist v₁ v₀))

cup : ∀ {A T Γ} → Γ ⊢ T ⦂ A ▻ V₀ ⦂ T ⦂ A
cup = lam (up v₀)

cdown : ∀ {A T Γ} → Γ ⊢ T ⦂ A ▻ A
cdown = lam (down v₀)

cdistup : ∀ {A B T U Γ}
          → Γ ⊢ T ⦂ (U ⦂ A ▻ B) ▻ U ⦂ A ▻ APP (DOWN V₁) (DOWN V₀) ⦂ B
cdistup = lam (lam (dist v₁ (up v₀)))

cunbox : ∀ {A C T Γ} → Γ ⊢ T ⦂ A ▻ (T ⦂ A ▻ C) ▻ C
cunbox = lam (lam (app v₀ v₁))

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

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

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

-- Closure under context concatenation.

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

-- Substitution.

mutual
  [_≔_]_ : ∀ {A B Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢ B → Γ ∖ i ⊢ B
  [ i ≔ s ] var j         with i ≟∈ j
  [ i ≔ s ] var .i        | same   = s
  [ i ≔ s ] var ._        | diff j = var j
  [ i ≔ s ] lam t         = lam ([ pop i ≔ mono⊢ weak⊆ s ] t)
  [ i ≔ s ] app t u       = app ([ i ≔ s ] t) ([ i ≔ s ] u)
  [ i ≔ s ] multibox ts u = multibox ([ i ≔ s ]⋆ ts) u
  [ i ≔ s ] down t        = down ([ i ≔ s ] t)
  [ i ≔ s ] pair t u      = pair ([ i ≔ s ] t) ([ i ≔ s ] u)
  [ i ≔ s ] fst t         = fst ([ i ≔ s ] t)
  [ i ≔ s ] snd t         = snd ([ i ≔ s ] t)
  [ i ≔ s ] unit          = unit

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


{-# OPTIONS --rewriting --prop --confluence-check #-}

open import Agda.Primitive
open import Agda.Builtin.Bool
open import Agda.Builtin.Nat
open import Agda.Builtin.List
open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite
open import Agda.Builtin.Sigma
open import Agda.Builtin.Unit
open import Data.Vec.Base
open import Data.Bool
open import Data.Sum

-- sigma type in Prop used to handle telescopes.

record Tel {a b} (A : Prop a) (B : A → Prop b) : Prop (a ⊔ b) where
  constructor _,_
  field
    fstC : A
    sndC : B fstC

open Tel public

infixr 4 _,_

record ΣCov {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    fstCov : A
    sndCov : B fstCov

open ΣCov public

record prod {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    fstprod : A
    sndprod : B

open prod public

variable ℓ ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level

-- a bit of boilerplate to deal with Prop

data ⊥ : Prop where

record ⊤P : Prop ℓ where
  constructor ttP

record Box (A : Prop ℓ) : Set ℓ where
  constructor box
  field
    unbox : A

open Box public

_×_ : ∀ (A : Prop ℓ) (B : Prop ℓ₁) → Prop (ℓ ⊔ ℓ₁)
A × B = Tel A (λ _ → B)

-- we need this for cumulativity

record i (A : Prop ℓ) : Prop (ℓ ⊔ ℓ₁) where
  constructor inj
  field
    uninj : A

open i public

record iS (A : Set ℓ) : Set (ℓ ⊔ ℓ₁) where
  constructor inj
  field
    uninj : A

open iS public

{-
 Axiomatisation of Id, Id-refl, transport (for proposition), cast

 Note that Id-refl, transport are axioms in Prop,
 so we don't need to give them a computation content.

 Also transport-refl is useless for transport on Prop
-}

postulate Id : (A : Set ℓ) → A → A → Prop ℓ

postulate cast : (A B : Set ℓ) (e : Id (Set ℓ) A B) → A → B

postulate Id-refl : {A : Set ℓ} (x : A) → Id A x x

postulate cast-refl : {A : Set ℓ} (e : Id _ A A) (a : A) → Id A (cast A A e a) a

postulate transport : {A : Set ℓ} (P : A → Prop ℓ₁) (x : A) (t : P x) (y : A) (e : Id A x y) → P y

-- cast-refl' : {A : Set ℓ} (e : Id (Set ℓ) A A) (a : A) → Id A (cast A A e a) a
-- cast-refl' {A = A} e a = transport (λ z → Id A z a) {!!} (Id-refl a) {!!} {!!}

-- direct derived functions

ap : {A : Set ℓ} {B : Set ℓ₁} {x y : A} (f : A → B) (e : Id A x y) →
     Id B (f x) (f y)
ap {ℓ} {ℓ₁} {A} {B} {x} {y} f e = transport (λ z → Id B (f x) (f z)) x (Id-refl _) y e

transport-Id : {A : Set ℓ} (P : A → Set ℓ₁) (x : A) (t : P x) (y : A) (e : Id A x y) → P y
transport-Id P x t y e = cast (P x) (P y) (ap P e) t

transport-refl : {A : Set ℓ} (P : A → Set ℓ₁) (x : A) (t : P x) (e : Id A x x) → Id _ (transport-Id P x t x e) t
transport-refl P x t e = cast-refl (ap P e) t

inverse  : (A : Set ℓ) {x y : A} (p : Id {ℓ} A x y) → Id A y x
inverse A {x} {y} p = transport (λ z → Id A z x) x (Id-refl x) y p

concatId : (A : Set ℓ) {x y z : A} (p : Id {ℓ} A x y)
           (q : Id {ℓ} A y z) → Id A x z
concatId A {x} {y} {z} p q = transport (λ t → Id A x t) y p z q

-- we now state rewrite rules for the identity type

postulate Id-Pi : (A : Set ℓ) (B : A → Set ℓ₁) (f g : (a : A) → B a) →
                  Id ((a : A) → B a) f g ≡ ((a : A) → Id (B a) (f a) (g a))

{-# REWRITE Id-Pi #-}

-- rewrite rules on Id-refl are not needed because it is in Prop

refl-Pi : (A : Set ℓ) (B : A → Set ℓ₁) (f : (a : A) → B a) →
          box (Id-refl f) ≡ box (λ a → Id-refl (f a))
refl-Pi A B f = refl

-- sanity check for funext

funext : (A : Set ℓ) (B : A → Set ℓ₁) (f g : (a : A) → B a) →
         ((a : A) → Id (B a) (f a) (g a)) → Id ((a : A) → B a) f g
funext A B f g e = e

postulate Id-Sigma : (A : Set ℓ) (B : A → Set ℓ₁) (a a' : A)
                     (b : B a) (b' : B a') →
                     Id (Σ A B) (a , b) (a' , b') ≡
                     Tel (Id A a a')
                         (λ e → Id (B a') (transport-Id B a b a' e) b')

{-# REWRITE Id-Sigma #-}

postulate Id-SigmaCov : (A : Set ℓ) (B : A → Set ℓ₁) (a a' : A)
                     (b : B a) (b' : B a') →
                     Id (ΣCov A B) (a , b) (a' , b') ≡
                     Tel (Id A a a')
                         (λ e → Id (B a) b (transport-Id B a' b' a (inverse A e)))

{-# REWRITE Id-SigmaCov #-}

postulate Id-prod : (A : Set ℓ) (B : Set ℓ₁) (a a' : A)
                     (b b' : B) →
                     Id (prod A B) (a , b) (a' , b') ≡
                     (Id A a a') × (Id B b b')

{-# REWRITE Id-prod #-}

postulate Id-Box : (A : Prop ℓ) (p q : A) → Id (Box A) (box p) (box q) ≡ ⊤P

{-# REWRITE Id-Box #-}

postulate Id-Unit : (p q : ⊤) → Id ⊤ p q ≡ ⊤P

{-# REWRITE Id-Unit #-}

postulate Id-list-nil-nil : (A : Set ℓ) →
                            Id (List A) [] [] ≡ ⊤P

-- postulate Id-list-nil-cons : (A : Set ℓ) (a' : A) (l' : List A) →
--                              Id (List A) [] (a' ∷ l') ≡ i ⊥

-- postulate Id-list-cons-nil : (A : Set ℓ) (a : A) (l : List A) →
--                              Id (List A) (a ∷ l) [] ≡ i ⊥

postulate Id-list-cons-cons : (A : Set ℓ) (a a' : A) (l l' : List A) →
                             Id (List A) (a ∷ l) (a' ∷ l') ≡
                             Id A a a' × Id (List A) l l'

{-# REWRITE Id-list-nil-nil #-}
{-# REWRITE Id-list-cons-cons #-}

postulate Id-nat-zero-zero : Id Nat 0 0 ≡ ⊤P

-- postulate Id-nat-zero-suc : (n : Nat) →
--                             Id Nat 0 (suc n) ≡ i ⊥

-- postulate Id-nat-suc-zero : (n : Nat) →
--                             Id Nat (suc n) zero ≡ i ⊥

postulate Id-nat-suc-suc : (n n' : Nat) →
                           Id Nat (suc n) (suc n') ≡
                           Id Nat n n'

{-# REWRITE Id-nat-zero-zero #-}
{-# REWRITE Id-nat-suc-suc #-}

postulate Id-bool-true-true : Id Bool true true ≡ ⊤P
postulate Id-bool-false-false : Id Bool false false ≡ ⊤P

{-# REWRITE Id-bool-true-true #-}
{-# REWRITE Id-bool-false-false #-}

postulate Id-sum-inj₁-inj₁ : (A : Set ℓ) (B : Set ℓ₁) (a a' : A) →
                           Id (A ⊎ B) (inj₁ a) (inj₁ a') ≡
                           i {ℓ = ℓ} {ℓ₁ = ℓ₁} (Id A a a')

postulate Id-sum-inj₂-inj₂ : (A : Set ℓ) (B : Set ℓ₁) (b b' : B) →
                           Id (A ⊎ B) (inj₂ b) (inj₂ b') ≡
                           i {ℓ = ℓ₁} {ℓ₁ = ℓ} (Id B b b')

{-# REWRITE Id-sum-inj₁-inj₁ #-}
{-# REWRITE Id-sum-inj₂-inj₂ #-}

-- rewrite rules for the identity type on the universe

telescope-Sigma : Set (lsuc (ℓ ⊔ ℓ₁))
telescope-Sigma {ℓ} {ℓ₁} = ΣCov (Set ℓ) (λ A → A → Set ℓ₁)

postulate Id-Type-Sigma : (A A' : Set ℓ) (B : A → Set ℓ₁) (B' : A' → Set ℓ₁) →
                          Id (Set (ℓ ⊔ ℓ₁)) (Σ A B) (Σ A' B') ≡
                          Id telescope-Sigma (A , B) (A' , B')

{-# REWRITE Id-Type-Sigma #-}

telescope-Forall : Set (lsuc (ℓ ⊔ ℓ₁))
telescope-Forall {ℓ} {ℓ₁} = Σ (Set ℓ) (λ A → A → Set ℓ₁)

postulate Id-Type-Pi : (A A' : Set ℓ) (B : A → Set ℓ₁) (B' : A' → Set ℓ₁) →
                       Id (Set (ℓ ⊔ ℓ₁)) ((a : A) → B a) ((a' : A') → B' a') ≡
                       Id telescope-Forall (A , B) (A' , B')

{-# REWRITE Id-Type-Pi #-}

telescope-Sum : Set (lsuc (ℓ ⊔ ℓ₁))
telescope-Sum {ℓ} {ℓ₁} = Σ (Set ℓ) (λ _ → Set ℓ₁)

postulate Id-Type-Sum : (A A' : Set ℓ) (B B' : Set ℓ₁)  →
                          Id (Set (ℓ ⊔ ℓ₁)) (A ⊎ B) (A' ⊎ B') ≡
                          Id telescope-Sum (A , B) (A' , B')

{-# REWRITE Id-Type-Sum #-}

postulate Id-Type-prod : (A A' : Set ℓ) (B B' : Set ℓ₁)  →
                          Id (Set (ℓ ⊔ ℓ₁)) (prod A B) (prod A' B') ≡
                          Id telescope-Sum (A , B) (A' , B')

{-# REWRITE Id-Type-prod #-}

telescope-List : Set (lsuc ℓ)
telescope-List {ℓ} = Set ℓ

postulate Id-Type-List : (A A' : Set ℓ) →
                       Id (Set ℓ) (List A) (List A') ≡
                       Id telescope-List A A'

{-# REWRITE Id-Type-List #-}

postulate Id-Type-Unit : Id Set ⊤ ⊤ ≡ ⊤P

{-# REWRITE Id-Type-Unit #-}

postulate Id-Type-Nat : Id Set Nat Nat ≡ Id Set ⊤ ⊤

{-# REWRITE Id-Type-Nat #-}

postulate Id-Type-Bool : Id Set Bool Bool ≡ Id Set ⊤ ⊤

{-# REWRITE Id-Type-Bool #-}

telescope-Box : Set (lsuc ℓ)
telescope-Box {ℓ} = Prop ℓ

postulate Id-Type-Box : (P P' : Prop ℓ) → Id (Set ℓ) (Box P) (Box P') ≡ Id telescope-Box P P'

{-# REWRITE Id-Type-Box #-}

-- rewrite rules for the identity type on Prop : Prop ext modulo cumul

postulate Id-prop : (P Q : Prop ℓ) → Id (Prop ℓ) P Q ≡ i (P → Q) × (Q → P)

{-# REWRITE Id-prop #-}

postulate Id-set : Id (Set (lsuc ℓ₁)) (Set ℓ₁) (Set ℓ₁) ≡ ⊤P

{-# REWRITE Id-set #-}

-- non-diagonal cases

{- There are n^2 cases, that's a pain, this is not exhaustive for the moment -}

postulate Id-set-nat : Id _ (Set ℓ) (iS Nat) ≡ i ⊥
postulate Id-nat-set : Id (Set (lsuc ℓ)) (iS Nat) (Set ℓ) ≡ i ⊥
postulate Id-set-bool : Id _ (Set ℓ) (iS Bool) ≡ i ⊥
postulate Id-bool-set : Id (Set (lsuc ℓ)) (iS Bool) (Set ℓ) ≡ i ⊥
postulate Id-bool-nat : Id _ Bool Nat ≡ i ⊥
postulate Id-nat-bool : Id _ Nat Bool ≡ i ⊥
postulate Id-set-pi : (A : Set ℓ₁) (B : A → Set ℓ₂) → Id (Set (lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂)) (iS {lsuc ℓ} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Set ℓ))
                                                                                (iS {ℓ₁ ⊔ ℓ₂} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} ((a : A) → B a)) ≡ i ⊥
postulate Id-pi-set : (A : Set ℓ₁) (B : A → Set ℓ₂) → Id (Set (lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂)) (iS {ℓ₁ ⊔ ℓ₂} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} ((a : A) → B a))
                                                                                (iS {lsuc ℓ} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Set ℓ)) ≡ i ⊥
postulate Id-set-sigma : (A : Set ℓ₁) (B : A → Set ℓ₂) → Id (Set (lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂)) (iS {lsuc ℓ} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Set ℓ))
                                                                                (iS {ℓ₁ ⊔ ℓ₂} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Σ A B)) ≡ i ⊥
postulate Id-sigma-set : (A : Set ℓ₁) (B : A → Set ℓ₂) → Id (Set (lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂)) (iS {ℓ₁ ⊔ ℓ₂} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Σ A B))
                                                                                (iS {lsuc ℓ} {lsuc ℓ ⊔ ℓ₁ ⊔ ℓ₂} (Set ℓ)) ≡ i ⊥

{-# REWRITE Id-set-nat Id-nat-set Id-set-bool Id-bool-set Id-bool-nat Id-nat-bool Id-set-pi Id-pi-set Id-set-sigma Id-sigma-set #-}

--- Contractibility of singletons and J can be defined

contr-sing : (A : Set ℓ) {x y : A} (p : Id {ℓ} A x y) →
    Id (Σ A (λ y → Box (Id A x y))) (x , box (Id-refl x)) (y , box p)
contr-sing A {x} {y} p = p , ttP

J : (A : Set ℓ) (x : A) (P : (y : A) → Id A x y → Prop ℓ₁)
    (t : P x (Id-refl x)) (y : A) (e : Id A x y) → P y e
J A x P t y e = transport (λ z → P (fst z) (unbox (snd z))) (x , box (Id-refl x)) t (y , box e) (contr-sing A e)

-- tranporting back and forth is the identity

-- cast-inv : (A B : Set ℓ) (e : Id _ A B) (a : A) →
--                      Id A (cast B A (inverse (Set ℓ) {x = A} {y = B} e) (cast A B e a)) a
-- cast-inv {ℓ} A B e a = let e-refl = cast-refl (Id-refl A) a in
--                        let e-refl-cast = cast-refl (Id-refl A) (cast A A (Id-refl A) a) in
--                        J (Set ℓ) A (λ B e → Id A (cast B A (inverse (Set ℓ) {x = A} {y = B} e) (cast A B e a)) a)
--                          (concatId A e-refl-cast e-refl) B e

postulate cast-set : (A : Set ℓ) (e : _) → cast (Set ℓ) (Set ℓ) e A ≡ A

{-# REWRITE cast-set #-}

postulate cast-prop : (A : Prop ℓ) (e : _) → cast (Prop ℓ) (Prop ℓ) e A ≡ A

{-# REWRITE cast-prop #-}

postulate cast-type-family : (A A' : Set ℓ) (f : (a : A) → Set ℓ₁) (e : _) →
                    cast ((a : A) → Set ℓ₁) ((a' : A') → Set ℓ₁) e f ≡
                    λ (a' : A') → let a = cast A' A (inverse (Set ℓ) {x = A} {y = A'} (fstC e)) a' in f a

{-# REWRITE cast-type-family #-}

postulate cast-Pi : (A A' : Set ℓ) (B : A → Set ℓ₁) (B' : A' → Set ℓ₁) (f : (a : A) → B a) (e : Id _ ((a : A) → B a) ((a' : A') → B' a')) →
                    cast ((a : A) → B a) ((a' : A') → B' a') e f ≡
                    λ (a' : A') → let a = cast A' A (inverse (Set ℓ) {x = A} {y = A'} (fstC e)) a' in
                                  cast _ _ (sndC e a') (f a)

{-# REWRITE cast-Pi #-}

postulate cast-Sigma : (A A' : Set ℓ) (B : A → Set ℓ₁) (B' : A' → Set ℓ₁) (x : A) (y : B x) (e : _) →
                    let eA = fstC e in
                    let x' = cast A A' eA x in
                    let eB = sndC e x in
                    cast (Σ A B) (Σ A' B') e (x , y) ≡
                    (cast A A' eA x , cast (B x) (B' x') eB y)

{-# REWRITE cast-Sigma #-}

postulate cast-prod : (A A' : Set ℓ) (B B' : Set ℓ₁) (x : A) (y : B) (e : _) →
                    let eA = fstC e in
                    let eB = sndC e in
                    cast (prod A B) (prod A' B') e (x , y) ≡
                    (cast A A' eA x , cast B B' eB y)

{-# REWRITE cast-prod #-}

postulate cast-Sum-inj₁ : (A A' : Set ℓ) (B B' : Set ℓ₁) (a : A) (e : _) →
                    let eA = fstC e in
                    let eB = sndC e in
                    cast (A ⊎ B) (A' ⊎ B') e (inj₁ a) ≡
                    inj₁ (cast A A' eA a)

postulate cast-Sum-inj₂ : (A A' : Set ℓ) (B B' : Set ℓ₁) (b : B) (e : _) →
                    let eA = fstC e in
                    let eB = sndC e in
                    cast (A ⊎ B) (A' ⊎ B') e (inj₂ b) ≡
                    inj₂ (cast B B' eB b)

{-# REWRITE cast-Sum-inj₁ #-}
{-# REWRITE cast-Sum-inj₂ #-}

postulate cast-List-nil : (A A' : Set ℓ) (e : _) →
                          cast (List A) (List A') e [] ≡ []

postulate cast-List-cons : (A A' : Set ℓ) (e : _) (a : A) (l : List A) →
                           cast (List A) (List A') e (a ∷ l) ≡
                           cast A A' e a ∷ cast _ _ e l

{-# REWRITE cast-List-nil #-}

{-# REWRITE cast-List-cons #-}

postulate cast-Nat : (e : _) (n : Nat) → cast Nat Nat e n ≡ n

{-# REWRITE cast-Nat #-}

postulate cast-Bool : (e : _) (b : Bool) → cast Bool Bool e b ≡ b

{-# REWRITE cast-Bool #-}

postulate cast-Unit : (e : _) (t : ⊤) → cast ⊤ ⊤ e t ≡ t

{-# REWRITE cast-Unit #-}

postulate cast-Box : (A A' : Prop ℓ) (a : A) (f : _) (g : _) →
                    cast (Box A) (Box A') (f , g) (box a) ≡ box (uninj f a)

{-# REWRITE cast-Box #-}

-- sanity check on closed terms

foo : transport-Id (λ (T : Σ Set (λ A → Σ A (λ _ → A → Set))) → ((snd (snd T)) (fst (snd T))))
                          (Nat , (0 , λ _ → Nat))
                          3
                          (Nat , (0 , λ _ → Nat))
                          (Id-refl {A = Σ Set (λ A → Σ A (λ _ → A → Set))} (Nat , (0 , λ _ → Nat)))
      ≡ 3
foo = refl

test-J-refl-on-closed-term : (X : Set ℓ) (x : X) →
       transport-Id (λ z → Σ ⊤ (λ z → ⊤)) x (tt , tt) x (Id-refl x) ≡ (tt , tt)
test-J-refl-on-closed-term X x = refl

-- Quotient types

{-
  Note that r s and t are not used in the definitions, they are just here
  to make sure the theory stays consistent, because postulating the quotient,
  we can derive them. In particular, with R = λ - - → ⊥, we would get
  a direct inconsistency using Id-refl
-}

record quotient-data ℓ : Set (lsuc ℓ) where
  constructor q-data
  field
    carrier : Set ℓ
    rel : carrier → carrier → Prop ℓ
    rel-refl : (x : carrier) → rel x x
    rel-sym : (x y : carrier) → rel x y → rel y x
    rel-trans : (x y z : carrier) → rel x y → rel y z → rel x z

open quotient-data public

postulate Quotient : (Q : quotient-data ℓ) →
                     Set ℓ

postulate pi : {Q : quotient-data ℓ} →
               carrier Q → Quotient Q

telescope-Quotient : Set (lsuc ℓ)
telescope-Quotient {ℓ} = Σ (Set ℓ) (λ A → A → A → Prop ℓ)

postulate Id-Quotient : (Q : quotient-data ℓ)
                        (a a' : carrier Q) →
                        Id (Quotient Q)
                           (pi a) (pi a') ≡ rel Q a a'

{-# REWRITE Id-Quotient #-}

postulate Quotient-elim : (Q : quotient-data ℓ)
               (P : Quotient Q → Set ℓ₁)
               (p : (x : carrier Q) → P (pi x))
               (e : (x y : carrier Q) → (r : rel Q x y) →
                    Id _ (transport-Id P (pi x) (p x) (pi y) r) (p y))
               (w : Quotient Q) → P w

postulate Quotient-elim-red : (Q : quotient-data ℓ)
                (P : Quotient Q → Set ℓ₁)
                (p : (x : carrier Q) → P (pi x))
                (e : (x y : carrier Q) → (r : rel Q x y) →
                     Id _ (transport-Id P (pi x) (p x) (pi y) r) (p y))
                (a : carrier Q) →
                Quotient-elim Q P p e (pi a)
                ≡ p a

{-# REWRITE Quotient-elim-red #-}

postulate Quotient-elim-prop : (Q : quotient-data ℓ)
               (P : Quotient Q → Prop ℓ₁)
               (p : (x : carrier Q) → P (pi x))
               (w : Quotient Q) → P w

postulate Id-Type-Quotient : (Q Q' : quotient-data ℓ) →
                Id (Set ℓ) (Quotient Q) (Quotient Q')
                ≡
                Id telescope-Quotient (carrier Q , rel Q) (carrier Q' , rel Q')

{-# REWRITE Id-Type-Quotient #-}

postulate cast-Quotient : (Q Q' : quotient-data ℓ)
                (a : carrier Q) (e : _) →
                cast (Quotient Q) (Quotient Q') e (pi a) ≡
                pi (cast (carrier Q) (carrier Q') (fstC e) a)

{-# REWRITE cast-Quotient #-}

-- double induction principle

Quotient-elim2-aux : (Q : quotient-data ℓ)
                 (P : Quotient Q → Quotient Q → Set ℓ₁)
                 (p : (x y : carrier Q) → P (pi x) (pi y))
                 (e : (x x' y y' : carrier Q) → (Q-rel1 : rel Q x x') → (Q-rel2 : rel Q y y') →
                            Id _ (transport-Id (P (pi x')) (pi y) (transport-Id (λ x → P x (pi y)) (pi x) (p x y) (pi x') Q-rel1) (pi y') Q-rel2) (p x' y')) (x : carrier Q) (w : Quotient Q) → P w (pi x)
Quotient-elim2-aux Q P p e x = Quotient-elim Q (λ w → P w (pi x)) (λ y → p y x) (λ y y' r → let e-cst = e y y' x x r (rel-refl Q x) in transport (λ e → Id (P (pi y') (pi x))
        (e
         (cast (P (pi y) (pi x)) (P (pi y') (pi x)) (ap (λ y → P y (pi x)) r) (p y x)))
        (p y' x)) (cast (P (pi y') (pi x)) (P (pi y') (pi x)) (ap (P (pi y')) (rel-refl Q x)))
           e-cst (λ w → w) λ a → cast-refl {A = P (pi y') (pi x)} (Id-refl (P (pi y') (pi x))) a )

Quotient-elim2 : (Q : quotient-data ℓ)
                 (P : Quotient Q → Quotient Q → Set ℓ₁)
                 (p : (x y : carrier Q) → P (pi x) (pi y))
                 (e : (x x' y y' : carrier Q) → (Q-rel1 : rel Q x x') → (Q-rel2 : rel Q y y') →
                         Id _ (transport-Id (P (pi x')) (pi y) (transport-Id (λ x → P x (pi y)) (pi x) (p x y) (pi x') Q-rel1) (pi y') Q-rel2) (p x' y'))
                 (w w' : Quotient Q) → P w w'
Quotient-elim2 Q P p e w =
  Quotient-elim Q (P w) (λ x → Quotient-elim2-aux Q P p e x w)
                (λ x x' r → Quotient-elim-prop Q (λ w → Id (P w (pi x'))
                (transport-Id (P w) (pi x) (Quotient-elim2-aux Q P p e x w) (pi x') r)
                (Quotient-elim2-aux Q P p e x' w)) (λ y → let e-cst = e y y x x' (rel-refl Q y) r in
                                    transport (λ e → Id (P (pi y) (pi x'))
        (cast (P (pi y) (pi x)) (P (pi y) (pi x')) (ap (P (pi y)) r) (e (p y x)))
        (p y x')) (cast (P (pi y) (pi x)) (P (pi y) (pi x)) (ap (P (pi y)) (rel-refl Q x)))
           e-cst (λ w → w)  λ a → cast-refl {A = P (pi y) (pi x)} (Id-refl (P (pi y) (pi x))) a  ) w)

{-
-- Sanity Check: transport-refl on quotient type

transport-refl-Quotient : (X : Set ℓ)
                  (carrier Q : X -> Set ℓ₁)
                  (R : (x : X) → carrier Q x → carrier Q x → Prop ℓ₁)
                  (r : (z : X) (x : carrier Q z) → R z x x)
                  (s : (z : X) (x y : carrier Q z) → R z x y → R z y x)
                  (t : (zz : X) (x y z : carrier Q zz) → R zz x y → R zz y z → R zz x z)
                  (x : X) (q : Quotient (carrier Q x) (R x) (r x) (s x) (t x))
                  (e : Id X x x) →
                  Id _
                    (transport-Id (λ x → Quotient (carrier Q x) (R x) (r x) (s x) (t x))
                               x q x e)
                    q
transport-refl-Quotient X carrier Q R r s t x q e =
  Quotient-elim-prop (carrier Q x) (R x) (r x) (s x) (t x)
                     ((λ a → Id _ (transport-Id (λ (x : X) → Quotient (carrier Q x) (R x) (r x) (s x) (t x)) x a x e) a))
                     (λ a →  transport (λ a' → R x a' a) a (r x a) (cast (carrier Q x) (carrier Q x) _ a) (inverse (carrier Q x) (transport-refl carrier Q x a e)))
                     q

-}

-- Now for Path

telescope-Path : Set (lsuc ℓ)
telescope-Path {ℓ} = Σ (Set ℓ) (λ A → prod A A)

postulate Id-Path : (A : Set ℓ) (x : A) (y : A) (e e' : _)→
                    Id (x ≡ y) e e' ≡ ⊤P

{-# REWRITE Id-Path #-}

postulate Id-Type-Path : (A A' : Set ℓ) (x y : A) (x' y' : A') →
                       Id (Set ℓ) (x ≡ y) (x' ≡ y') ≡
                       Id telescope-Path
                         (A  , (x , y))
                         (A' , (x' , y' ))

{-# REWRITE Id-Type-Path #-}

-- not enough to get canonicity

-- postulate cast-Path : (A A' : Set ℓ) (x : A) (x' : A') (e : _) →
                    -- cast (x ≡ x) (x' ≡ x') e refl ≡ refl

-- {-# REWRITE cast-Path #-}

transport-Path : {A : Set ℓ} (x : A) (P :  (y : A) → Set ℓ₁)  (t : P x) (y : A) (e : x ≡ y) → P y
transport-Path P x t y refl = t

transport-Path-refl : {A : Set ℓ} (P : A → Prop ℓ₁) (x : A) (t : P x) (y : A) (e : x ≡ y) → P y
transport-Path-refl P x t .x refl = t

path-to-Id : {A : Set ℓ} {x y : A} → x ≡ y → Id A x y
path-to-Id {ℓ} {A} {x} {y} = transport-Path-refl (Id A x) x (Id-refl x) y

-- we treat cast X (a ≡ b) e x as a new constructor of equality

postulate IdPath : {A : Set ℓ} {x y : A} → Id A x y → x ≡ y

postulate transport-Path-cast-refl : {A B : Set ℓ} (a : A) (b b' : B) (e : Id (Set ℓ) (a ≡ a) (b ≡ b')) →
                             cast (a ≡ a) (b ≡ b') e refl ≡
                             IdPath ( let X = fstC (sndC e) in let Y = sndC (sndC e) in concatId B (inverse B X) Y)

{-# REWRITE transport-Path-cast-refl #-}

postulate transport-Path-IdPath : {A : Set ℓ} (x : A) (P : (y : A) → Set ℓ₁) (t : P x) (y : A) (e : Id A x y) →
                        transport-Path x P t y (IdPath e) ≡ transport-Id P x t y e

{-# REWRITE transport-Path-IdPath #-}

postulate transport-Path-cast-IdPath : {A B : Set ℓ} (a a' : A) (b b' : B) (ea : Id A a a') (e : Id (Set ℓ) (a ≡ a') (b ≡ b')) →
                             cast (a ≡ a') (b ≡ b') e (IdPath ea) ≡
                             IdPath (concatId B (inverse B (fstC (sndC e)))
                                          (concatId B  (ap (cast A B (fstC e)) ea) (sndC (sndC e))))

{-# REWRITE transport-Path-cast-IdPath #-}

transport-refl-Path : {A : Set ℓ} (P : A → Set ℓ₁) (x : A) (t : P x) → transport-Path x P t x refl ≡ t
transport-refl-Path P x t = refl

funext-Path : (A : Set ℓ) (B : A → Set ℓ₁) (f g : (a : A) → B a) →
          ((a : A) → f a ≡ g a) → f ≡ g
funext-Path A B f g e = IdPath (λ a → path-to-Id (e a))

etaBool : (a : Bool) → a ≡ (if a then true else false)
etaBool true = refl
etaBool false = refl

eq_fun : (λ (b : Bool) → b) ≡ (λ (b : Bool) → if b then true else false)
eq_fun = funext-Path Bool (λ - → Bool) _ _ λ a → etaBool a

-- standard boolean using equality

std-bool : Bool
std-bool = transport-Path (λ (b : Bool) → b) (λ f → Bool) true (λ (b : Bool) → if b then true else false) eq_fun

sanity-check : std-bool ≡ true
sanity-check = refl


open import Formalization.PredicateLogic.Signature

module Formalization.PredicateLogic.Classical.Semantics.Homomorphism (𝔏 : Signature) where
open Signature(𝔏)

import      Lvl
open import Data
open import Data.Boolean
open import Data.Boolean.Stmt
open import Data.ListSized
open import Data.ListSized.Functions
open import Formalization.PredicateLogic.Classical.Semantics(𝔏)
open import Formalization.PredicateLogic.Syntax(𝔏)
open import Functional using (_∘_ ; _∘₂_ ; _←_)
open import Logic.Propositional as Logic using (_↔_)
import      Logic.Predicate     as Logic
open import Numeral.Finite
open import Numeral.Finite.Bound
open import Numeral.Natural
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Sets.PredicateSet using (PredSet)
open        Sets.PredicateSet.BoundedQuantifiers
open import Structure.Function.Domain
open import Syntax.Function
open import Type.Dependent renaming (intro to _,_)
open import Type.Properties.Decidable
open import Type

private variable ℓ ℓₘ ℓₘ₁ ℓₘ₂ ℓₑ : Lvl.Level
private variable P : Type{ℓₚ}
private variable args n vars : ℕ
private variable 𝔐 : Model

private variable A B C S : Model{ℓₘ}
private variable fi : Obj n
private variable ri : Prop n
private variable xs : List(dom(S))(n)

record Homomorphism (A : Model{ℓₘ₁}) (B : Model{ℓₘ₂}) (f : dom(A) → dom(B)) : Type{ℓₚ Lvl.⊔ ℓₒ Lvl.⊔ Lvl.of(Type.of A) Lvl.⊔ Lvl.of(Type.of B)} where
  field
    preserve-functions : ∀{xs} → (f(fn(A) fi xs) ≡ fn(B) fi (map f xs))
    preserve-relations : ∀{xs} → IsTrue(rel(A) ri xs) → IsTrue(rel(B) ri (map f xs))

_→ₛₜᵣᵤ_ : Model{ℓₘ₁} → Model{ℓₘ₂} → Type
A →ₛₜᵣᵤ B = Logic.∃(Homomorphism A B)

record Embedding (A : Model{ℓₘ₁}) (B : Model{ℓₘ₂}) (f : dom(A) → dom(B)) : Type{ℓₚ Lvl.⊔ ℓₒ Lvl.⊔ Lvl.of(Type.of A) Lvl.⊔ Lvl.of(Type.of B)} where
  constructor intro
  field
    ⦃ homomorphism ⦄ : Homomorphism(A)(B)(f)
    ⦃ injection ⦄    : Injective(f)
    preserve-relations-converse : ∀{xs} → IsTrue(rel(A) ri xs) ← IsTrue(rel(B) ri (map f xs))

open import Data
open import Data.Boolean.Stmt
open import Data.ListSized.Proofs
open import Functional
open import Function.Equals
open import Function.Proofs
open import Lang.Instance
open import Logic.Predicate
open import Structure.Function
open import Structure.Operator
open import Structure.Relator.Properties
open import Structure.Relator
open import Syntax.Transitivity

idₛₜᵣᵤ : A →ₛₜᵣᵤ A
∃.witness idₛₜᵣᵤ = id
Homomorphism.preserve-functions (∃.proof (idₛₜᵣᵤ {A = A})) {n} {fi} {xs} = congruence₁(fn A fi) (symmetry(_≡_) (_⊜_.proof map-id))
Homomorphism.preserve-relations (∃.proof (idₛₜᵣᵤ {A = A})) {n} {ri} {xs} = substitute₁ₗ(IsTrue ∘ rel A ri) (_⊜_.proof map-id)

_∘ₛₜᵣᵤ_ : let _ = A ; _ = B ; _ = C in (B →ₛₜᵣᵤ C) → (A →ₛₜᵣᵤ B) → (A →ₛₜᵣᵤ C)
∃.witness (([∃]-intro f) ∘ₛₜᵣᵤ ([∃]-intro g)) = f ∘ g
Homomorphism.preserve-functions (∃.proof (_∘ₛₜᵣᵤ_ {A = A} {B = B} {C = C} ([∃]-intro f ⦃ hom-f ⦄) ([∃]-intro g ⦃ hom-g ⦄))) {fi = fi} {xs} =
  congruence₁(f) (Homomorphism.preserve-functions hom-g)
  🝖 Homomorphism.preserve-functions hom-f
  🝖 congruence₁(fn C fi) (symmetry(_≡_) (_⊜_.proof (map-[∘] {f = f}{g = g}) {xs}))
Homomorphism.preserve-relations (∃.proof (_∘ₛₜᵣᵤ_ {A = A} {B = B} {C = C} ([∃]-intro f ⦃ hom-f ⦄) ([∃]-intro g ⦃ hom-g ⦄))) {ri = ri} {xs} =
  substitute₁ₗ(IsTrue ∘ rel C ri) (_⊜_.proof map-[∘] {xs})
  ∘ Homomorphism.preserve-relations hom-f
  ∘ Homomorphism.preserve-relations hom-g

import      Data.Tuple as Tuple
open import Function.Equals
open import Function.Equals.Proofs
open import Function.Inverse
open import Lang.Inspect
open import Logic.Predicate.Equiv
open import Structure.Category
open import Structure.Categorical.Properties
open import Structure.Function.Domain.Proofs

instance
  map-function : ∀{ℓ₁ ℓ₂}{A : Type{ℓ₁}}{B : Type{ℓ₂}} → Function ⦃ [⊜]-equiv ⦄ ⦃ [⊜]-equiv ⦄ (map{A = A}{B = B}{n = n})
  map-function {A = A}{B = B} = intro proof where
    proof : ∀{f g : A → B} → (f ⊜ g) → (map{n = n} f ⊜ map g)
    _⊜_.proof (proof p) {∅}     = reflexivity(_≡_)
    _⊜_.proof (proof p) {x ⊰ l} = congruence₂(_⊰_) (_⊜_.proof p) (_⊜_.proof (proof p) {l})

instance
  Structure-Homomorphism-category : Category(_→ₛₜᵣᵤ_ {ℓₘ})
  Category._∘_ Structure-Homomorphism-category = _∘ₛₜᵣᵤ_
  Category.id  Structure-Homomorphism-category = idₛₜᵣᵤ
  BinaryOperator.congruence (Category.binaryOperator Structure-Homomorphism-category) = [⊜][∘]-binaryOperator-raw
  _⊜_.proof (Morphism.Associativity.proof (Category.associativity Structure-Homomorphism-category) {_} {_} {_} {_} {[∃]-intro f} {[∃]-intro g} {[∃]-intro h}) {x} = reflexivity(_≡_) {f(g(h(x)))}
  _⊜_.proof (Morphism.Identityₗ.proof (Tuple.left (Category.identity Structure-Homomorphism-category)) {f = [∃]-intro f}) {x} = reflexivity(_≡_) {f(x)}
  _⊜_.proof (Morphism.Identityᵣ.proof (Tuple.right (Category.identity Structure-Homomorphism-category)) {f = [∃]-intro f}) {x} = reflexivity(_≡_) {f(x)}

module _ {F@([∃]-intro f) : _→ₛₜᵣᵤ_ {ℓₘ}{ℓₘ} A B} where
  isomorphism-surjective-embedding : Morphism.Isomorphism ⦃ [≡∃]-equiv ⦃ [⊜]-equiv ⦄ ⦄ (\{A} → _∘ₛₜᵣᵤ_ {A = A})(idₛₜᵣᵤ)(F) ↔ (Surjective(f) Logic.∧ Embedding(A)(B)(f))
  Tuple.left isomorphism-surjective-embedding (Logic.[∧]-intro surj embed) = [∃]-intro ([∃]-intro _ ⦃ hom ⦄) ⦃ Logic.[∧]-intro inverₗ inverᵣ ⦄ where
    f⁻¹ = invᵣ-surjective f ⦃ surj ⦄
    instance
      inver : Inverse(f)(f⁻¹)
      inver = Logic.[∧]-elimᵣ ([∃]-proof (bijective-to-invertible ⦃ bij = injective-surjective-to-bijective(f)  ⦃ Embedding.injection embed ⦄ ⦃ surj ⦄ ⦄))

    instance
      hom : Homomorphism(B)(A)(f⁻¹)
      Homomorphism.preserve-functions hom {fi = fi} {xs = xs} =
        f⁻¹(fn B fi xs)                   🝖[ _≡_ ]-[ congruence₁(f⁻¹) (congruence₁(fn B fi) (_⊜_.proof map-id)) ]-sym
        f⁻¹(fn B fi (map id xs))          🝖[ _≡_ ]-[ congruence₁(f⁻¹) (congruence₁(fn B fi) (_⊜_.proof (congruence₁(map) (intro (Inverseᵣ.proof(Logic.[∧]-elimᵣ inver)))) {xs})) ]-sym
        f⁻¹(fn B fi (map (f ∘ f⁻¹) xs))   🝖[ _≡_ ]-[ congruence₁(f⁻¹) (congruence₁(fn B fi) (_⊜_.proof map-[∘] {xs})) ]
        f⁻¹(fn B fi (map f (map f⁻¹ xs))) 🝖[ _≡_ ]-[ congruence₁(f⁻¹) (Homomorphism.preserve-functions (Embedding.homomorphism embed)) ]-sym
        f⁻¹(f(fn A fi (map f⁻¹ xs)))      🝖[ _≡_ ]-[ Inverseₗ.proof(Logic.[∧]-elimₗ inver) ]
        fn A fi (map f⁻¹ xs)              🝖-end

      Homomorphism.preserve-relations hom {ri = ri} {xs = xs} p = Embedding.preserve-relations-converse embed {xs = map f⁻¹ xs} (substitute₁(IsTrue ∘ rel B ri) (_⊜_.proof proof {xs}) p) where
        proof =
          id                🝖[ _⊜_ ]-[ map-id ]-sym
          map id            🝖[ _⊜_ ]-[ congruence₁(map) (intro (Inverseᵣ.proof(Logic.[∧]-elimᵣ inver))) ]-sym
          map(f ∘ f⁻¹)      🝖[ _⊜_ ]-[ map-[∘] ]
          (map f ∘ map f⁻¹) 🝖-end

    inverₗ : Morphism.Inverseₗ _ _ F ([∃]-intro f⁻¹)
    Morphism.Inverseₗ.proof inverₗ = intro(Inverseₗ.proof(Logic.[∧]-elimₗ inver))

    inverᵣ : Morphism.Inverseᵣ _ _ F ([∃]-intro f⁻¹)
    Morphism.Inverseᵣ.proof inverᵣ = intro(Inverseᵣ.proof(Logic.[∧]-elimᵣ inver))
  Tuple.right isomorphism-surjective-embedding iso = Logic.[∧]-intro infer (intro (\{n}{ri}{xs} → {!proof!})) where
    instance
      bij : Bijective(f)
    instance
      inj : Injective(f)
      inj = bijective-to-injective(f)
    instance
      surj : Surjective(f)
      surj = bijective-to-surjective(f)
    proof : IsTrue(rel A ri xs) ← IsTrue(rel B ri (map f(xs)))
    proof p relation x = {!!}
    proof2 : let _ = (rel A ri xs) in Surjective(f)
    proof2 = {!!}
-- (intro (proof {!!} {!!}))


-- Hilbert-style formalisation of closed syntax.
-- Sequences of terms.

module OldBasicILP.Syntax.ClosedHilbertSequential where

open import OldBasicILP.Syntax.Common public

-- Mutually-recursive declarations.

data Ty : Set

record Proof (Ξ : Cx Ty) (A : Ty) : Set

data ⊢ᴰ_ : Cx Ty → Set

_⧺ᴰ_ : ∀ {Ξ₁ Ξ₂} → ⊢ᴰ Ξ₁ → ⊢ᴰ Ξ₂ → ⊢ᴰ Ξ₁ ⧺ Ξ₂

-- Types parametrised by closed derivations.

infixr 10 _⦂_
infixl 9 _∧_
infixr 7 _▻_
data Ty where
  α_  : Atom → Ty
  _▻_ : Ty → Ty → Ty
  _⦂_ : ∀ {Ξ A} → Proof Ξ A → Ty → Ty
  _∧_ : Ty → Ty → Ty
  ⊤  : Ty

-- Anti-bug wrappers.

record Proof (Ξ : Cx Ty) (A : Ty) where
  inductive
  constructor [_]
  field
    der : ⊢ᴰ Ξ , A

-- More mutually-recursive declarations.

appᴰ : ∀ {Ξ₁ Ξ₂ A B} → ⊢ᴰ Ξ₁ , A ▻ B → ⊢ᴰ Ξ₂ , A → ⊢ᴰ (Ξ₂ , A) ⧺ (Ξ₁ , A ▻ B) , B

boxᴰ : ∀ {Ξ A} → (d : ⊢ᴰ Ξ , A) → ⊢ᴰ ∅ , [ d ] ⦂ A

-- Derivations.

infix 3 ⊢ᴰ_
data ⊢ᴰ_ where
  nil   : ⊢ᴰ ∅
  mp    : ∀ {Ξ A B}   → A ▻ B ∈ Ξ → A ∈ Ξ → ⊢ᴰ Ξ → ⊢ᴰ Ξ , B
  ci    : ∀ {Ξ A}     → ⊢ᴰ Ξ → ⊢ᴰ Ξ , A ▻ A
  ck    : ∀ {Ξ A B}   → ⊢ᴰ Ξ → ⊢ᴰ Ξ , A ▻ B ▻ A
  cs    : ∀ {Ξ A B C} → ⊢ᴰ Ξ → ⊢ᴰ Ξ , (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C

  nec   : ∀ {Ξ A}     → ∀ {`Ξ} → (`d : ⊢ᴰ `Ξ , A)
                      → ⊢ᴰ Ξ → ⊢ᴰ Ξ , [ `d ] ⦂ A

  cdist : ∀ {Ξ A B}   → ∀ {`Ξ₁ `Ξ₂} → {`d₁ : ⊢ᴰ `Ξ₁ , A ▻ B} → {`d₂ : ⊢ᴰ `Ξ₂ , A}
                      → ⊢ᴰ Ξ → ⊢ᴰ Ξ , [ `d₁ ] ⦂ (A ▻ B) ▻ [ `d₂ ] ⦂ A ▻ [ appᴰ `d₁ `d₂ ] ⦂ B

  cup   : ∀ {Ξ A}     → ∀ {`Ξ} → {`d : ⊢ᴰ `Ξ , A}
                      → ⊢ᴰ Ξ → ⊢ᴰ Ξ , [ `d ] ⦂ A ▻ [ boxᴰ `d ] ⦂ [ `d ] ⦂ A

  cdown : ∀ {Ξ A}     → ∀ {`Ξ} → {`d : ⊢ᴰ `Ξ , A}
                      → ⊢ᴰ Ξ → ⊢ᴰ Ξ , [ `d ] ⦂ A ▻ A

  cpair : ∀ {Ξ A B}   → ⊢ᴰ Ξ → ⊢ᴰ Ξ , A ▻ B ▻ A ∧ B
  cfst  : ∀ {Ξ A B}   → ⊢ᴰ Ξ → ⊢ᴰ Ξ , A ∧ B ▻ A
  csnd  : ∀ {Ξ A B}   → ⊢ᴰ Ξ → ⊢ᴰ Ξ , A ∧ B ▻ B
  unit  : ∀ {Ξ}       → ⊢ᴰ Ξ → ⊢ᴰ Ξ , ⊤

-- Anti-bug wrappers.

infix 3 ⊢_
⊢_ : Ty → Set
⊢ A = ∃ (λ Ξ → ⊢ᴰ Ξ , A)

-- Concatenation of derivations.

d₁ ⧺ᴰ nil       = d₁
d₁ ⧺ᴰ mp i j d₂ = mp (mono∈ weak⊆⧺₂ i) (mono∈ weak⊆⧺₂ j) (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ ci d₂     = ci (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ ck d₂     = ck (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cs d₂     = cs (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ nec `d d₂ = nec `d (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cdist d₂  = cdist (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cup d₂    = cup (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cdown d₂  = cdown (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cpair d₂  = cpair (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ cfst d₂   = cfst (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ csnd d₂   = csnd (d₁ ⧺ᴰ d₂)
d₁ ⧺ᴰ unit d₂   = unit (d₁ ⧺ᴰ d₂)

-- Modus ponens and necessitation in nested form.

appᴰ {Ξ₁} {Ξ₂} {A} {B} d₁ d₂ = mp top (mono∈ (weak⊆⧺₁ (Ξ₁ , A ▻ B)) top) (d₂ ⧺ᴰ d₁)

boxᴰ {Ξ} {A} d = nec d nil

app : ∀ {A B} → ⊢ A ▻ B → ⊢ A → ⊢ B
app {A} {B} (Ξ₁ , d₁) (Ξ₂ , d₂) = Ξ₃ , d₃
  where Ξ₃ = (Ξ₂ , A) ⧺ (Ξ₁ , A ▻ B)
        d₃ = appᴰ d₁ d₂

box : ∀ {A} → (t : ⊢ A) → ⊢ [ π₂ t ] ⦂ A
box (Ξ , d) = ∅ , boxᴰ d


{-# OPTIONS --without-K --safe --overlapping-instances #-}
module Interpreter where

open import Data.Char hiding (_≤_)
open import Data.Bool hiding (_≤_)
open import Data.Nat hiding (_≤_)
open import Data.Unit
import Data.Nat as N
open import Data.Product
open import Data.Sum
open import Relation.Binary.PropositionalEquality as PropEq
open import Relation.Nullary
import Data.String as Str
open import Data.Nat.Show
import Data.List as List
open import Data.Empty

infix 3 _:::_,_
infix 2 _∈_

infix 1 _⊢_

data `Set : Set where
  `ℕ    : `Set
  `Bool : `Set
  _`⇨_  : `Set → `Set → `Set
  `⊤    : `Set
  _`×_  : `Set → `Set → `Set
  _`+_  : `Set → `Set → `Set
infixr 2 _`⇨_

data Var : Set where
  x' : Var
  y' : Var
  z' : Var

-- Inequality proofs on variables
data _≠_ : Var → Var → Set where
  x≠y : x' ≠ y'
  x≠z : x' ≠ z'
  y≠x : y' ≠ x'
  y≠z : y' ≠ z'
  z≠x : z' ≠ x'
  z≠y : z' ≠ y'

⟦_⟧ : `Set → Set
⟦ `ℕ ⟧ = ℕ
⟦ `Bool ⟧ = Bool
⟦ (t `⇨ s) ⟧ =  ⟦ t ⟧ → ⟦ s ⟧
⟦ `⊤ ⟧ = ⊤
⟦ (t `× s) ⟧ = ⟦ t ⟧ × ⟦ s ⟧
⟦ (t `+ s) ⟧ = ⟦ t ⟧ ⊎ ⟦ s ⟧

data Γ : Set where
  ·         : Γ
  _:::_,_   : Var → `Set → Γ → Γ

data _∈_ :  Var → Γ → Set where
  H  : ∀ {x Δ t } → x ∈ x ::: t , Δ
  TH : ∀ {x y Δ t } → ⦃ prf : x ∈ Δ ⦄ → ⦃ neprf : x ≠ y ⦄ → x ∈ y ::: t , Δ

!Γ_[_] : ∀ {x} → (Δ : Γ) → x ∈ Δ → `Set
!Γ_[_] · ()
!Γ _ ::: t , Δ [ H ]     = t
!Γ _ ::: _ , Δ [ TH ⦃ prf = i ⦄ ]  = !Γ Δ [ i ]

infix 30 `v_
infix 30 `n_
infix 30 `¬_

infix 26 _`+_
infix 27 _`*_
infix 27 _`∧_
infix 26 _`∨_
infix 24 _`≤_
infix 24 _`,_
infixl 22 _`₋_

data _⊢_ : Γ → `Set → Set where
  `false           : ∀ {Δ} → Δ ⊢ `Bool
  `true            : ∀ {Δ} → Δ ⊢ `Bool
  `n_              : ∀ {Δ} → ℕ → Δ ⊢ `ℕ
  `v_              : ∀ {Δ} → (x : Var) → ⦃ i : x ∈ Δ ⦄ → Δ ⊢ !Γ Δ [ i ]
  _`₋_             : ∀ {Δ t s} → Δ ⊢ t `⇨ s → Δ ⊢ t → Δ ⊢ s --application
  `λ_`:_⇨_         : ∀ {Δ tr} → (x : Var) → (tx : `Set)
                        → x ::: tx , Δ ⊢ tr → Δ ⊢ tx `⇨ tr
  _`+_             : ∀ {Δ} → Δ ⊢ `ℕ → Δ ⊢ `ℕ → Δ ⊢ `ℕ
  _`*_             : ∀ {Δ} → Δ ⊢ `ℕ →  Δ ⊢ `ℕ → Δ ⊢ `ℕ
  _`∧_             : ∀ {Δ} → Δ ⊢ `Bool → Δ ⊢ `Bool → Δ ⊢ `Bool
  _`∨_             : ∀ {Δ} → Δ ⊢ `Bool →  Δ ⊢ `Bool → Δ ⊢ `Bool
  _`≤_             : ∀ {Δ} → Δ ⊢ `ℕ → Δ ⊢ `ℕ →  Δ ⊢ `Bool
  `¬_              : ∀ {Δ} → Δ ⊢ `Bool →  Δ ⊢ `Bool
  _`,_             : ∀ {Δ t s} → Δ ⊢ t →  Δ ⊢ s →  Δ ⊢ t `× s
  `fst             : ∀ {Δ t s} → Δ ⊢ t `× s → Δ ⊢ t
  `snd             : ∀ {Δ t s} → Δ ⊢ t `× s → Δ ⊢ s
  `left            : ∀ {Δ t s} → Δ ⊢ t → Δ ⊢ t `+ s
  `right           : ∀ {Δ t s} → Δ ⊢ s → Δ ⊢ t `+ s
  `case_`of_||_    : ∀ {Δ t s u} → Δ ⊢ t `+ s
                        → Δ ⊢ t `⇨ u → Δ ⊢ s `⇨ u → Δ ⊢ u
  `tt              : ∀ {Δ} → Δ ⊢ `⊤
  `let_`=_`in_     : ∀ {Δ th tb} → (x : Var)
                       → Δ ⊢ th → x ::: th , Δ ⊢ tb → Δ ⊢ tb
  `if_`then_`else_ : ∀ {Δ t} → Δ ⊢ `Bool → Δ ⊢ t → Δ ⊢ t → Δ ⊢ t

data ⟨_⟩ : Γ → Set₁ where
  []   : ⟨ · ⟩
  _∷_  : ∀ {x t Δ} → ⟦ t ⟧ → ⟨ Δ ⟩ → ⟨ x ::: t , Δ ⟩

!_[_] : ∀ {x Δ} → ⟨ Δ ⟩ → (i : x ∈ Δ) → ⟦ !Γ Δ [ i ] ⟧
!_[_] [] ()
!_[_] (val ∷ env) H      = val
!_[_] (val ∷ env) (TH ⦃ prf = i ⦄) = ! env [ i ]

interpret : ∀ {t} → · ⊢ t → ⟦ t ⟧
interpret = interpret' []
  where interpret' : ∀ {Δ t} → ⟨ Δ ⟩ → Δ ⊢ t → ⟦ t ⟧
        interpret' env `true = true
        interpret' env `false = false
        interpret' env `tt = tt
        interpret' env (`n n) = n
        interpret' env ((`v x) ⦃ i = idx ⦄) = ! env [ idx ]
        interpret' env (f `₋ x) = (interpret' env f) (interpret' env x)
        interpret' env (`λ _ `: tx ⇨ body) = λ (x : ⟦ tx ⟧) → interpret' (x ∷ env) body
        interpret' env (l `+ r) = interpret' env l + interpret' env r
        interpret' env (l `* r) = interpret' env l * interpret' env r
        interpret' env (l `∧ r) = interpret' env l ∧ interpret' env r
        interpret' env (l `∨ r) = interpret' env l ∨ interpret' env r
        interpret' env (l `≤ r) with interpret' env l N.≤? interpret' env r
        ...                             | yes  p = true
        ...                             | no ¬p = false
        interpret' env (`¬ x) = not (interpret' env x)
        interpret' env (f `, s) = interpret' env f ,′ interpret' env s
        interpret' env (`fst p) with interpret' env p
        interpret' env (`fst p) | f , s = f
        interpret' env (`snd p) with interpret' env p
        interpret' env (`snd p) | f , s = s
        interpret' env (`left v) = inj₁ (interpret' env v)
        interpret' env (`right v) = inj₂ (interpret' env v)
        interpret' env (`case s `of le || re) with interpret' env s
        interpret' env (`case s `of le || re) | inj₁ l = (interpret' env le) l
        interpret' env (`case s `of le || re) | inj₂ r = (interpret' env re) r
        interpret' env (`let _ `= h `in b) = let hval = interpret' env h in interpret' (hval ∷ env) b
        interpret' env (`if b `then et `else ef) with interpret' env b
        interpret' env (`if b `then et `else ef) | true = interpret' env et
        interpret' env (`if b `then et `else ef) | false = interpret' env ef

instance
  v_type₁ : ∀ {x Δ t} → x ∈ x ::: t , Δ
  v_type₁ = H

  v_type₂ : ∀ {x y Δ t} → ⦃ prf : x ∈ Δ ⦄ → ⦃ x ≠ y ⦄ → x ∈ y ::: t , Δ
  v_type₂ = TH

instance
  xy : x' ≠ y'
  xy = x≠y

  xz : x' ≠ z'
  xz = x≠z

  yx : y' ≠ x'
  yx = y≠x

  yz : y' ≠ z'
  yz = y≠z

  zx : z' ≠ x'
  zx = z≠x

  zy : z' ≠ y'
  zy = z≠y


module Holes.Prelude where

open import Agda.Primitive public
  using (Level; _⊔_; lzero; lsuc)
open import Agda.Builtin.List public
  using (List; _∷_; [])
open import Agda.Builtin.Unit public
  using (⊤; tt)
open import Agda.Builtin.Bool public
  using (Bool; true; false)
open import Agda.Builtin.Nat public
  using (zero; suc; _+_; _*_)
  renaming (Nat to ℕ; _==_ to _=ℕ?_)
open import Agda.Builtin.Reflection public
open import Agda.Builtin.String public
open import Agda.Builtin.Char public

infixr 4 _,_
infixr 2 _×_
infixr 9 _∘_
infixl 1 _⟨_⟩_
infixr 0 _$_
infix 0 case_of_

--------------------------------------------------------------------------------
--  Datatypes
--------------------------------------------------------------------------------

data Maybe {a} (A : Set a) : Set a where
  just : (x : A) → Maybe A
  nothing : Maybe A

record _×_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  constructor _,_

  field
    proj₁ : A
    proj₂ : B

open _×_ public

--------------------------------------------------------------------------------
--  Combinators
--------------------------------------------------------------------------------

_∘_ : ∀ {a b c}
        {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c} →
        (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
        ((x : A) → C (g x))
f ∘ g = λ x → f (g x)

id : ∀ {a} {A : Set a} → A → A
id x = x

const : ∀ {a b} {A : Set a} {B : Set b} → A → B → A
const x = λ _ → x

flip : ∀ {a b c} {A : Set a}{B : Set b}{C : Set c} → (A → B → C) → B → A → C
flip f y x = f x y

_$_ : ∀ {a b} {A : Set a} {B : A → Set b} →
      ((x : A) → B x) → ((x : A) → B x)
f $ x = f x

_⟨_⟩_ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
        A → (A → B → C) → B → C
x ⟨ f ⟩ y = f x y

curry : ∀ {a b c}{A : Set a}{B : Set b}{C : Set c} → (A × B → C) → A → B → C
curry f x y = f (x , y)

uncurry : ∀ {a b c}{A : Set a}{B : Set b}{C : Set c} → (A → B → C) → A × B → C
uncurry f (x , y) = f x y

case_of_ : ∀ {a b}{A : Set a}{B : Set b} → A → (A → B) → B
case x of f = f x

--------------------------------------------------------------------------------
--  Boolean operations
--------------------------------------------------------------------------------

_∨_ : Bool → Bool → Bool
false ∨ false = false
_ ∨ _ = true

_∧_ : Bool → Bool → Bool
true ∧ true = true
_ ∧ _ = false

not : Bool → Bool
not true = false
not false = true

if_then_else_ : ∀ {a} {A : Set a} → (b : Bool) → A → A → A
if true then x else y = x
if false then x else y = y

--------------------------------------------------------------------------------
--  List operations
--------------------------------------------------------------------------------

singleton : ∀ {a}{A : Set a} → A → List A
singleton x = x ∷ []

map : ∀ {a b}{A : Set a}{B : Set b} → (A → B) → List A → List B
map f [] = []
map f (x ∷ xs) = f x ∷ map f xs

length : ∀ {a}{A : Set a} → List A → ℕ
length [] = 0
length (_ ∷ xs) = suc (length xs)

zip : ∀ {a b}{A : Set a}{B : Set b} → List A → List B → List (A × B)
zip [] _ = []
zip _ [] = []
zip (x ∷ xs) (y ∷ ys) = (x , y) ∷ zip xs ys

unzip : ∀ {a b}{A : Set a}{B : Set b} → List (A × B) → List A × List B
unzip [] = [] , []
unzip ((x , y) ∷ xys) with unzip xys
... | xs , ys = x ∷ xs , y ∷ ys

infixr 6 _++_

_++_ : ∀ {a}{A : Set a} → List A → List A → List A
[] ++ ys = ys
(x ∷ xs) ++ ys = x ∷ xs ++ ys

concatMap : ∀ {a b}{A : Set a}{B : Set b} → (A → List B) → List A → List B
concatMap f [] = []
concatMap f (x ∷ xs) = f x ++ concatMap f xs

concat : ∀ {a}{A : Set a} → List (List A) → List A
concat = concatMap id

intersperse : ∀ {a}{A : Set a} → A → List A → List A
intersperse e [] = []
intersperse e (x ∷ []) = x ∷ []
intersperse e (x ∷ y ∷ xs) = x ∷ e ∷ intersperse e (y ∷ xs)

intercalate : ∀ {a}{A : Set a} → List A → List (List A) → List A
intercalate e = concat ∘ intersperse e

--------------------------------------------------------------------------------
--  Nat operations
--------------------------------------------------------------------------------

-- A comparison view. Taken from "View from the left" (McBride/McKinna) via the
-- Agda standard library

data Ordering : ℕ → ℕ → Set where
  less    : ∀ m k → Ordering m (suc (m + k))
  equal   : ∀ m   → Ordering m m
  greater : ∀ m k → Ordering (suc (m + k)) m

compare : ∀ m n → Ordering m n
compare zero    zero    = equal   zero
compare (suc m) zero    = greater zero m
compare zero    (suc n) = less    zero n
compare (suc m) (suc n) with compare m n
compare (suc .m)           (suc .(suc m + k)) | less    m k = less    (suc m) k
compare (suc .m)           (suc .m)           | equal   m   = equal   (suc m)
compare (suc .(suc m + k)) (suc .m)           | greater m k = greater (suc m) k

_<?_ : ℕ → ℕ → Bool
x <? y with compare x y
... | Ordering.less .x _ = true
... | _ = false

--------------------------------------------------------------------------------
--  Typeclasses
--------------------------------------------------------------------------------

record RawMonoid {f} (M : Set f) : Set f where
  field
    _<>_ : M → M → M
    mempty : M

open RawMonoid {{...}} public

instance
  listMonoid : ∀ {a} {A : Set a} → RawMonoid (List A)
  RawMonoid._<>_ listMonoid = _++_
  RawMonoid.mempty listMonoid = []

record RawMonad {f} (M : Set f → Set f) : Set (lsuc f) where
  infixl 1 _>>=_ _>>_ _>=>_ _>>=²_
  infixr 1 _=<<_ _<=<_
  infixl 4 _<$>_
  infixl 4 _⊛_

  field
    _>>=_ : ∀ {A B} → M A → (A → M B) → M B
    return : ∀ {A} → A → M A

  _>>_ : ∀ {A B} → M A → M B → M B
  x >> y = x >>= λ _ → y

  _>=>_ : ∀ {ℓ}{A : Set ℓ}{B C} → (A → M B) → (B → M C) → A → M C
  f >=> g = λ x → f x >>= g

  _=<<_ : ∀ {A B} → (A → M B) → M A → M B
  f =<< x = x >>= f

  _<=<_ : ∀ {ℓ}{A : Set ℓ}{B C} → (B → M C) → (A → M B) → A → M C
  g <=< f = f >=> g

  _>>=²_ : {A B C : Set f} → M (A × B) → (A → B → M C) → M C
  x >>=² f = x >>= uncurry f

  join : ∀ {A} → M (M A) → M A
  join m = m >>= id

  fmap : ∀ {A B} → (A → B) → M A → M B
  fmap f x = x >>= return ∘ f

  _<$>_ = fmap

  _⊛_ : ∀ {A B} → M (A → B) → M A → M B
  f ⊛ x = f >>= flip fmap x

open RawMonad {{...}} public

record RawTraversable {t} (T : Set t → Set t) : Set (lsuc t) where
  field
    traverse : {M : Set t → Set t} {{monad : RawMonad M}} {A B : Set t} → (A → M B) → T A → M (T B)

  sequence : {M : Set t → Set t} {{monad : RawMonad M}} {A : Set t} → T (M A) → M (T A)
  sequence = traverse id

open RawTraversable {{...}} public

traverse- : {T M : Set → Set} {{traversable : RawTraversable T}} {{monad : RawMonad M}} {A B : Set} → (A → M B) → T A → M ⊤
traverse- f xs = traverse f xs >> return tt

instance
  traversableList : ∀ {t} → RawTraversable (List {t})
  traverse {{traversableList}} f [] = return []
  traverse {{traversableList}} f (x ∷ xs) = _∷_ <$> f x ⊛ traverse f xs

--------------------------------------------------------------------------------
--  Propositional Equality
--------------------------------------------------------------------------------

module PropEq where
  open import Agda.Builtin.Equality using (_≡_; refl) public

  sym : ∀ {a} {A : Set a} {x y : A} → x ≡ y → y ≡ x
  sym refl = refl

  trans : ∀ {a} {A : Set a} {x y z : A} → x ≡ y → y ≡ z → x ≡ z
  trans refl eq = eq

  subst : ∀ {a p} {A : Set a} (P : A → Set p) {x y} → x ≡ y → P x → P y
  subst P refl p = p

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

--------------------------------------------------------------------------------
--  Monads and useful instances
--------------------------------------------------------------------------------

infixl 1 _>>=′_

-- _>>=_ in `RawMonad` only works on a single level, while bindTC is more
-- level-polymorphic, which we will sometimes need.
_>>=′_ = bindTC

data Result {e r} (E : Set e) (R : Set r) : Set (e ⊔ r) where
  ok : (x : R) → Result E R
  err : (e : E) → Result E R

Maybe→Result : ∀ {e r} {E : Set e} {R : Set r} → E → Maybe R → Result E R
Maybe→Result e (just x) = ok x
Maybe→Result e nothing = err e

instance
  List-Monad : ∀ {a} → RawMonad (List {a})
  RawMonad._>>=_ List-Monad = flip concatMap
  RawMonad.return List-Monad x = x ∷ []

  TC-Monad : ∀ {a} → RawMonad {a} TC
  TC-Monad = record
    { return = returnTC
    ; _>>=_ = bindTC
    }

  Maybe-Monad : ∀ {a} → RawMonad {a} Maybe
  (RawMonad._>>=_ Maybe-Monad) (just x) f = f x
  (RawMonad._>>=_ Maybe-Monad) nothing f = nothing
  RawMonad.return Maybe-Monad = just

  Result-Monad : ∀ {ℓ}{E : Set ℓ} → RawMonad {ℓ} (Result E)
  Result-Monad = record
    { return = ok
    ; _>>=_ = λ
      { (err e) _ → err e
      ; (ok x) f → f x
      }
    }

--------------------------------------------------------------------------------
--  RTC: A type checking monad with errors other than normal type errors
--------------------------------------------------------------------------------

record RTC {e r} (E : Set e) (R : Set r) : Set (e ⊔ r) where
  field
    tryRunRTC : TC (Result E R)

open RTC public

runRTC : ∀ {e r} {E : Set e} {R : Set r} → (E → TC R) → RTC E R → TC R
runRTC f x =
  tryRunRTC x >>=′
    λ { (err e) → f e
      ; (ok y) → return y
      }

module RTC′ {e} {E : Set e} where
  bind : ∀ {α β} {A : Set α}{B : Set β} → RTC E A → (A → RTC E B) → RTC E B
  tryRunRTC (bind x f) =
    tryRunRTC x >>=′
      λ { (err e) → return (err e)
        ; (ok y) → tryRunRTC (f y)
        }

instance
  RTC-monad : ∀ {ℓ} {E : Set ℓ} → RawMonad {ℓ} (RTC E)
  RTC-monad = record
    { return = λ x → record { tryRunRTC = return (return x) }
    ; _>>=_ = RTC′.bind
    }

catchRTC : ∀ {ℓ} {E : Set ℓ}{R : Set ℓ} → RTC E R → RTC E R → RTC E R
tryRunRTC (catchRTC rtc fallback) =
  tryRunRTC rtc ⟨ catchTC ⟩ tryRunRTC fallback

--------------------------------------------------------------------------------
--  MonadCatch and instances
--------------------------------------------------------------------------------

record MonadCatch {a e} (E : Set e) (M : Set a → Set a) : Set (lsuc a ⊔ e) where
  field
    catch : ∀ {A : Set a} → (E → M A) → M A → M A

open MonadCatch {{...}} public

instance
  Maybe-MonadCatch : ∀ {a} → MonadCatch {a} ⊤ Maybe
  Maybe-MonadCatch = record
    { catch = λ
      { f (just x) → just x
      ; f nothing → f tt
      }
    }

  Result-MonadCatch : ∀ {ℓ} {E : Set ℓ} → MonadCatch {ℓ} E (Result E)
  Result-MonadCatch = record
    { catch = λ
      { f (err e) → f e
      ; f (ok x) → ok x}
    }

  RTC-MonadCatch : ∀ {ℓ} {E : Set ℓ} → MonadCatch {ℓ} E (RTC E)
  RTC-MonadCatch = record
    { catch = λ f x → record
      { tryRunRTC = tryRunRTC x >>= λ
        { (err e) → tryRunRTC (f e)
        ; (ok y) → return (ok y)
        }
      }
    }

--------------------------------------------------------------------------------
--  MonadThrow and instances
--------------------------------------------------------------------------------

record MonadThrow {a e} (E : Set e) (M : Set a → Set a) : Set (lsuc a ⊔ e) where
  field
    {{monad}} : RawMonad M
    throw : ∀ {A : Set a} → E → M A

open MonadThrow {{...}} public

instance
  Maybe-MonadThrow : ∀ {a} → MonadThrow {a} ⊤ Maybe
  Maybe-MonadThrow = record { throw = λ _ → nothing }

  Result-MonadThrow : ∀ {ℓ} {E : Set ℓ} → MonadThrow {ℓ} E (Result E)
  Result-MonadThrow = record { throw = err }

  RTC-MonadThrow : ∀ {ℓ} {E : Set ℓ} → MonadThrow {ℓ} E (RTC E)
  RTC-MonadThrow = record { throw = λ e → record { tryRunRTC = return (err e) } }

  TC-MonadThrow : ∀ {ℓ} → MonadThrow {ℓ} (List ErrorPart) TC
  TC-MonadThrow = record { throw = typeError }

liftResult′ : ∀ {e ℓ} {E E′ : Set e} {M : Set ℓ → Set ℓ} {{monadThrow : MonadThrow E′ M}} {A : Set ℓ} → (E → E′) → Result E A → M A
liftResult′ _ (ok x) = return x
liftResult′ f (err e) = throw (f e)

liftResult : ∀ {e ℓ} {E : Set e} {M : Set ℓ → Set ℓ} {{monadThrow : MonadThrow E M}} {A : Set ℓ} → Result E A → M A
liftResult = liftResult′ id

liftMaybe : ∀ {e ℓ}{E : Set e} {M : Set ℓ → Set ℓ} {{monadThrow : MonadThrow E M}} {A : Set ℓ} → E → Maybe A → M A
liftMaybe error (just x) = return x
liftMaybe error nothing = throw error

--------------------------------------------------------------------------------
--  Choice and instances
--------------------------------------------------------------------------------

record Choice {a} (F : Set a → Set a) : Set (lsuc a) where
  field
    _<|>_ : ∀ {A} → F A → F A → F A

open Choice {{...}} public

instance
  Maybe-Choice : ∀ {a} → Choice {a} Maybe
  Maybe-Choice = record
    { _<|>_ = λ
      { (just x) _ → just x
      ; nothing (just y) → just y
      ; nothing nothing → nothing
      }
    }

  Result-Choice : ∀ {ℓ}{E : Set ℓ} → Choice {ℓ} (Result E)
  Result-Choice = record
    { _<|>_ = λ
      { (ok x) _ → ok x
      ; (err _) (ok y) → ok y
      ; (err e) (err _) → err e
      }
    }

--------------------------------------------------------------------------------
--  General utility methods
--------------------------------------------------------------------------------

successes : ∀ {ℓ e M} {E : Set e} {{monadCatch : MonadCatch E M}}{{monad : RawMonad M}} {A : Set ℓ} → List (M A) → M (List A)
successes  [] = return []
successes {{monadCatch}} {A = A} (x ∷ xs) =
  successes {{monadCatch}} xs >>= λ xs′ →
  catch {{monadCatch}} (λ _ → return xs′) (fmap (_∷ xs′) x)

mapErr : ∀ {e r}{R : Set r}{E E′ : Set e} → (E → E′) → Result E R → Result E′ R
mapErr f (ok x) = ok x
mapErr f (err e) = err (f e)

discardErr : ∀ {e r} {E : Set e}{R : Set r} → Result E R → Maybe R
discardErr (ok x) = just x
discardErr (err _) = nothing

result : ∀ {e r a}{E : Set e}{R : Set r}{A : Set a} → (E → A) → (R → A) → Result E R → A
result g f (ok x) = f x
result g f (err e) = g e

--------------------------------------------------------------------------------
--  Reflection utility methods
--------------------------------------------------------------------------------

infix 4 _=Name?_

_=Name?_ : Name → Name → Bool
_=Name?_ = primQNameEquality

basicArg : Term → Arg Term
basicArg = arg (arg-info visible relevant)

implicitArg : Term → Arg Term
implicitArg = arg (arg-info hidden relevant)

getArg : Arg Term → Term
getArg (arg _ x) = x

mapArg : ∀ {A B} → (A → B) → Arg A → Arg B
mapArg f (arg i x) = arg i (f x)

getArglist : Term → List (Arg Term)
getArglist (def _ args) = args
getArglist (con _ args) = args
getArglist (var _ args) = args
getArglist _ = []

instance
  traversableArg : RawTraversable Arg
  traverse {{traversableArg}} f (arg i x) = arg i <$> f x

liftTC : ∀ {e r} {E : Set e}{R : Set r} → TC R → RTC E R
tryRunRTC (liftTC x) = x >>=′ return ∘ ok

mapRtcErr : ∀ {ℓ}{R E E′ : Set ℓ} → (E → E′) → RTC E R → RTC E′ R
tryRunRTC (mapRtcErr f x) = tryRunRTC x >>= return ∘ mapErr f

runRtcOrTypeError : ∀ {ℓ} {E R : Set ℓ} → (E → List ErrorPart) → RTC E R → TC R
runRtcOrTypeError error = tryRunRTC >=> result (typeError ∘ error) return

typeError′ : ∀ {ℓ} {E R : Set ℓ} → List ErrorPart → RTC E R
typeError′ = liftTC ∘ typeError

--------------------------------------------------------------------------------
--  Macros that aid debugging
--------------------------------------------------------------------------------

runTCTy : ∀ ℓ → Type → Term → Term → TC ⊤
runTCTy ℓ at-type tc-term target =
  unquoteTC at-type >>=′ λ (ty : Set ℓ) →
  unquoteTC tc-term >>=′ λ (TCx : TC ty) →
  TCx >>=′
  quoteTC >>=′
  unify target

macro
  -- Quotes a term and expands to its reified syntax tree as a `Term`.

  quote-term : Term → Term → TC ⊤
  quote-term term target = quoteTC term >>= unify target

  -- Runs a TC operation and splices its result into the expression. Only works
  -- with operations in Set₀.

  runTCTy′ : ∀ {ℓ} → Type → Term → Term → TC ⊤
  runTCTy′ {ℓ} = runTCTy ℓ

  runTC : ∀ {ℓ} → Term → Term → TC ⊤
  runTC {ℓ} tc-term target =
    inferType target >>=′
    unquoteTC >>=′ λ (ty : Set ℓ) →
    unquoteTC tc-term >>=′ λ (TCx : TC ty) →
    TCx >>=′
    quoteTC >>=′
    unify target


open import Coinduction using ( ∞ ; ♯_ ; ♭ )
open import Data.Product using ( _×_ ; _,_ ; proj₁ )
open import FRP.LTL.Time.Bound using
  ( Time∞ ; _≼_ ; _≺_ ; fin ; +∞ ; +∞-top
  ; ≼-refl ; _≼-trans_ ; ≡-impl-≽ ; ≺-impl-≼ ; ≺-impl-⋡ ; t≺+∞
  ; _≼-case_ ; lt ; eq ; gt )
open import FRP.LTL.Time.Interval using ( [_⟩ ; _⊑_ ; _~_ ; _⌢_∵_ )
open import FRP.LTL.ISet.Causal using ( _⊵_ ; _∙_⊸_∙_ ; done ; inp ; out ; _/_/_ )
open import FRP.LTL.ISet.Core using ( ISet ; [_] ; _,_ ; ⟦_⟧ ; M⟦_⟧ ; splitM⟦_⟧ ; subsumM⟦_⟧ )
open import FRP.LTL.ISet.Stateless using ( _⇒_ )
open import FRP.LTL.Util using ( ⊥-elim ; ≡-relevant )
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl )

module FRP.LTL.ISet.Product where

_∧_ : ISet → ISet → ISet
A ∧ B = [ (λ i → M⟦ A ⟧ i × M⟦ B ⟧ i) , split , subsum ] where

  split : ∀ i j i~j →
    (M⟦ A ⟧ (i ⌢ j ∵ i~j) × M⟦ B ⟧ (i ⌢ j ∵ i~j)) →
      ((M⟦ A ⟧ i × M⟦ B ⟧ i) × (M⟦ A ⟧ j × M⟦ B ⟧ j))
  split i j i~j (σ , τ) with splitM⟦ A ⟧ i j i~j σ | splitM⟦ B ⟧ i j i~j τ
  split i j i~j (σ , τ) | (σ₁ , σ₂) | (τ₁ , τ₂) = ((σ₁ , τ₁) , (σ₂ , τ₂))

  subsum : ∀ i j → (i ⊑ j) → (M⟦ A ⟧ j × M⟦ B ⟧ j) → (M⟦ A ⟧ i × M⟦ B ⟧ i)
  subsum i j i⊑j (σ , τ) = (subsumM⟦ A ⟧ i j i⊑j σ , subsumM⟦ B ⟧ i j i⊑j τ)

-- We could define fst and snd in terms of arr, but we define them explictly for efficiency.

π₁ : ∀ {A B} t → ((A ∧ B) ∙ t ⊸ A ∙ t)
π₁ {A} {B} +∞      = done refl
π₁ {A} {B} (fin t) = inp ≼-refl t≺+∞ P where

  P : ∀ {u} .(t≺u : fin t ≺ u) → M⟦ A ∧ B ⟧ [ t≺u ⟩ → ∞ ((A ∧ B) ∙ u ⊸ A ∙ fin t)
  P {u} t≺u [ σ , τ ] = ♯ out t≺u σ (♯ π₁ u)

fst : ∀ {A B} → ⟦ A ∧ B ⊵ A ⟧
fst = [ (λ t t∈i → π₁ (fin t)) ]

π₂ : ∀ {A B} t → ((A ∧ B) ∙ t ⊸ B ∙ t)
π₂ {A} {B} +∞      = done refl
π₂ {A} {B} (fin t) = inp ≼-refl t≺+∞ Q where

  Q : ∀ {u} .(t≺u : fin t ≺ u) → M⟦ A ∧ B ⟧ [ t≺u ⟩ → ∞ ((A ∧ B) ∙ u ⊸ B ∙ fin t)
  Q {u} t≺u [ σ , τ ] = ♯ out t≺u τ (♯ π₂ u)

snd : ∀ {A B} → ⟦ A ∧ B ⊵ B ⟧
snd = [ (λ t t∈i → π₂ (fin t)) ]

-- Mediating morphism

_&&_ : ∀ {A B C s t} → (A ∙ s ⊸ B ∙ t) → (A ∙ s ⊸ C ∙ t) → (A ∙ s ⊸ (B ∧ C) ∙ t)
inp s≼t t≺∞ P   && Q               = inp s≼t t≺∞ (λ s≺u σ → ♯ (♭ (P s≺u σ)   && (Q / s≺u / σ)))
P               && inp s≼t t≺∞ Q   = inp s≼t t≺∞ (λ s≺u σ → ♯ ((P / s≺u / σ) && ♭ (Q s≺u σ)))
P               && done t≡∞        = done t≡∞
done t≡∞        && Q               = done t≡∞
out {u} t≺u σ P && out {v} t≺v τ Q with u ≼-case v
out t≺u σ P     && out t≺v τ Q | lt u≺v with splitM⟦ _ ⟧ [ t≺u ⟩ [ u≺v ⟩ refl τ
out t≺u σ P     && out t≺v τ Q | lt u≺v | (τ₁ , τ₂) = out t≺u [ σ , τ₁ ] (♯ (♭ P && out u≺v τ₂ Q))
out t≺u σ P     && out t≺v τ Q | eq u≡v with ≡-relevant u≡v
out t≺u σ P     && out t≺v τ Q | eq u≡v | refl      = out t≺u [ σ , τ ] (♯ (♭ P && ♭ Q))
out t≺u σ P     && out t≺v τ Q | gt v≺u with splitM⟦ _ ⟧ [ t≺v ⟩ [ v≺u ⟩ refl σ
out t≺u σ P     && out t≺v τ Q | gt v≺u | (σ₁ , σ₂) = out t≺v [ σ₁ , τ ] (♯ (out v≺u σ₂ P && ♭ Q))

_&&&_ : ∀ {A B C} → ⟦ (A ⊵ B) ⇒ (A ⊵ C) ⇒ (A ⊵ (B ∧ C)) ⟧
[ [ f ] ] &&& [ [ g ] ] = [ (λ t t∈i → f t t∈i && g t t∈i) ]


module IID-Proof-Test where

open import LF
open import Identity
open import IID
open import IIDr
open import DefinitionalEquality
open import IID-Proof-Setup

η : {I : Set}(γ : OPg I)(U : I -> Set) -> Args γ U -> Args γ U
η (ι i)	  U _ = ★
η (σ A γ) U a = < a₀ | η (γ a₀) U a₁ >
  where
    a₀ = π₀ a
    a₁ = π₁ a
η (δ A i γ) U a = < a₀ | η γ U a₁ >
  where
    a₀ = π₀ a
    a₁ = π₁ a

r←→gArgs-equal :
  {I : Set}(γ : OPg I)(U : I -> Set)
  (i : I)(a : Args (ε γ i) U) ->
  _==_ {I × \i -> Args (ε γ i) U}
  < index γ U (r→gArgs γ U i a) |
    g→rArgs γ U (r→gArgs γ U i a)
  > < i | a >
r←→gArgs-equal {I} (ι i) U j < p | ★ > = elim== i (\k q -> _==_ {I × \k -> Args (ε (ι i) k) U}
								< i | < refl | ★ > >
								< k | < q | ★ > >
						  ) refl j p
r←→gArgs-equal {I} (σ A γ)   U j < a | b > = cong f ih
  where ih = r←→gArgs-equal (γ a) U j b
	f : (I × \i -> Args (ε (γ a) i) U) -> (I × \i -> Args (ε (σ A γ) i) U)
	f < k | q > = < k | < a | q > >
r←→gArgs-equal {I} (δ H i γ) U j < g | b > = cong f ih
  where ih = r←→gArgs-equal γ U j b
	f : (I × \k -> Args (ε γ k) U) -> (I × \k -> Args (ε (δ H i γ) k) U)
	f < k | q > = < k | < g | q > >

{-
r←→gArgs-subst-identity' :
  {I  : Set}(γ : OPg I) ->

  (\(U : I -> Set)(C  : (i : I) -> rArgs (ε γ) U i -> Set)
    (a : Args γ U)(h : C (index γ U   (r→gArgs γ U (index γ U (η γ U a)) (g→rArgs γ U (η γ U a))))
			 (g→rArgs γ U (r→gArgs γ U (index γ U (η γ U a)) (g→rArgs γ U (η γ U a))))
		  ) -> r←→gArgs-subst γ U C (index γ U (η γ U a)) (g→rArgs γ U (η γ U a)) h
  ) ==¹
  (\(U : I -> Set)(C  : (i : I) -> rArgs (ε γ) U i -> Set)
    (a : Args γ U)(h : C (index γ U   (r→gArgs γ U (index γ U (η γ U a)) (g→rArgs γ U (η γ U a))))
			 (g→rArgs γ U (r→gArgs γ U (index γ U (η γ U a)) (g→rArgs γ U (η γ U a))))
		  ) -> h
  )
r←→gArgs-subst-identity' (ι i)	   = refl¹
r←→gArgs-subst-identity' (σ A γ)   = ?
r←→gArgs-subst-identity' (δ A i γ) = subst¹ (\ ∙ -> f ∙ ==¹ f (\U C a h -> h))
					    (r←→gArgs-subst-identity' γ) ?
  where
    ih = r←→gArgs-subst-identity' γ
    f = \g U C a h -> g U (\i c -> C i < π₀ a | c >) (π₁ a) h
-}