module Implicits.Resolution.Infinite.Expressiveness where

open import Prelude hiding (Bool)

open import Data.Fin.Substitution
open import Data.List
open import Data.List.Any
open Membership-≡

open import Extensions.ListFirst
open import SystemF.Everything as F using ()

open import Implicits.Resolution.Ambiguous.Resolution as A
open import Implicits.Resolution.Deterministic.Resolution as D
open import Implicits.Resolution.Infinite.Resolution as I
open import Implicits.Syntax
open import Implicits.Substitutions

module Deterministic⊆Infinite where

  complete : ∀ {ν} {a : Type ν} {Δ : ICtx ν} → Δ D.⊢ᵣ a → Δ I.⊢ᵣ a
  complete (r-simp x r↓a) = r-simp (proj₁ $ first⟶∈ x) (lem r↓a)
    where
      lem : ∀ {ν} {Δ : ICtx ν} {r a} → Δ D.⊢ r ↓ a → Δ I.⊢ r ↓ a
      lem (i-simp a) = i-simp a
      lem (i-iabs x₁ x₂) = i-iabs (complete x₁) (lem x₂)
      lem (i-tabs b x₁) = i-tabs b (lem x₁)
  complete (r-iabs ρ₁ x) = r-iabs (complete x)
  complete (r-tabs x) = r-tabs (complete x)

module Deterministic⊂Infinite where

  open import Implicits.Resolution.Deterministic.Incomplete as Inc

  -- We gave an example of a query that the det rules could not resolve
  --   ((Int ⇒ Bool) List.∷ Bool List.∷ List.[]) ⊢ᵣ Bool
  -- Here we derive it for the Infinite rules, to prove that we are strictly
  -- more expressive
  infinite-can : Δ I.⊢ᵣ Bool
  infinite-can = r-simp (there (here refl)) p
    where
      p : Δ I.⊢ Bool ↓ tc 0
      p = i-simp (tc 0)

module Infinite⇔Ambiguous
  (nf : ∀ {ν n} {Γ : F.Ctx ν n} {t a} → Γ F.⊢ t ∈ a → ∃ λ (t₂ : F.Term ν n) → Γ F.⊢ t₂ ⇑ a)
  where

  open import Implicits.Resolution.Ambiguous.SystemFEquiv hiding (equivalent)
  open import Implicits.Resolution.Infinite.NormalFormEquiv hiding (equivalent)
  open import Implicits.Resolution.Embedding.Lemmas
  open import Function.Equivalence

  sound : ∀ {ν} {a : Type ν} {Δ : ICtx ν} → Δ I.⊢ᵣ a → Δ A.⊢ᵣ a
  sound (r-simp x p) = lem p (r-ivar x)
    where
      lem : ∀ {ν} {Δ : ICtx ν} {a τ} → Δ I.⊢ a ↓ τ → Δ A.⊢ᵣ a → Δ A.⊢ᵣ (simpl τ)
      lem (i-simp τ) ⊢y = ⊢y
      lem (i-iabs ⊢a b↓τ) ⊢b = lem b↓τ (r-iapp ⊢b (sound ⊢a))
      lem (i-tabs b a) ⊢y = lem a (r-tapp b ⊢y)
  sound (r-iabs p) = r-iabs (sound p)
  sound (r-tabs p) = r-tabs (sound p)

  complete : ∀ {ν} {a : Type ν} {Δ : ICtx ν} → Δ A.⊢ᵣ a → Δ I.⊢ᵣ a
  complete {a = a} p = subst₂ (λ Δ r → Δ I.⊢ᵣ r) (ctx→← _) (tp→← a) (from-⇑ (proj₂ (nf (to-⊢ p))))

  equivalent : ∀ {ν} (Δ : ICtx ν) r → Δ I.⊢ᵣ r ⇔ Δ A.⊢ᵣ r
  equivalent Δ r = equivalence sound complete


open import Agda.Builtin.List
open import Agda.Builtin.Nat
open import Agda.Builtin.Reflection
open import Agda.Builtin.Unit
open import Common.Product

{-# TERMINATING #-}

Loop : Set
Loop = Loop

postulate
  loop : Loop

data Box (A : Set) : Set where
  box : A → Box A

{-# TERMINATING #-}

Boxed-loop : Set
Boxed-loop = Box Boxed-loop

postulate
  boxed-loop : Boxed-loop

test₁ : Boxed-loop
test₁ = quoteGoal g in boxed-loop

test₂ : Term
test₂ = quoteTerm Loop

test₃ : Loop → List (Arg Term)
test₃ _ = quoteContext

macro

  m₄ : Term → Term → TC ⊤
  m₄ _ hole = unify hole (def (quote ⊤) [])

test₄ : Set
test₄ = m₄ Loop

macro

  m₅ : Term → TC ⊤
  m₅ hole = bindTC (inferType (def (quote loop) [])) λ A →
            unify hole A

test₅ : Set
test₅ = m₅

macro

  m₆ : Term → TC ⊤
  m₆ hole =
    bindTC (checkType (def (quote Loop) []) (agda-sort (lit 0))) λ A →
    unify hole A

test₆ : Set
test₆ = m₆

macro

  m₇ : Term → TC ⊤
  m₇ hole =
    bindTC (quoteTC Loop) λ A →
    unify hole A

test₇ : Set
test₇ = m₇

Vec : Set → Nat → Set
Vec A zero    = ⊤
Vec A (suc n) = A × Vec A n

macro

  m₈ : Term → TC ⊤
  m₈ hole =
    bindTC (reduce V) λ V →
    unify hole V
    where
    varg = arg (arg-info visible relevant)
    V    = def (quote Vec) (varg (def (quote Boxed-loop) []) ∷
                            varg (lit (nat 1)) ∷
                            [])

test₈ : Set
test₈ = m₈


{-# OPTIONS --safe --experimental-lossy-unification #-}

module Cubical.Algebra.Group.EilenbergMacLane.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.GroupoidLaws renaming (assoc to ∙assoc)
open import Cubical.Foundations.Path
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Transport

open import Cubical.Data.Unit
open import Cubical.Data.Sigma
open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Properties
open import Cubical.Homotopy.Connected
open import Cubical.HITs.Truncation as Trunc renaming (rec to trRec; elim to trElim)
open import Cubical.HITs.EilenbergMacLane1 hiding (elim)
open import Cubical.Algebra.AbGroup.Base
open import Cubical.Data.Empty
  renaming (rec to ⊥-rec) hiding (elim)
open import Cubical.HITs.Truncation
  renaming (elim to trElim ; rec to trRec ; rec2 to trRec2)
open import Cubical.Data.Nat hiding (_·_ ; elim)
open import Cubical.HITs.Susp
open import Cubical.Functions.Morphism
open import Cubical.Foundations.Path

private
  variable ℓ ℓ' : Level

  _* = AbGroup→Group

EM-raw : (G : AbGroup ℓ) (n : ℕ) → Type ℓ
EM-raw G zero = fst G
EM-raw G (suc zero) = EM₁ (G *)
EM-raw G (suc (suc n)) = Susp (EM-raw G (suc n))

ptEM-raw : {n : ℕ} {G : AbGroup ℓ} → EM-raw G n
ptEM-raw {n = zero} {G = G} = AbGroupStr.0g (snd G)
ptEM-raw {n = suc zero} {G = G} = embase
ptEM-raw {n = suc (suc n)} {G = G} = north

raw-elim : (G : AbGroup ℓ) (n : ℕ) {A : EM-raw G (suc n) → Type ℓ'}
            → ((x : _) → isOfHLevel (suc n) (A x) )
            → A ptEM-raw
            → (x : _) → A x
raw-elim G zero hlev b = elimProp _ hlev b
raw-elim G (suc n) hlev b north = b
raw-elim G (suc n) {A = A} hlev b south = subst A (merid ptEM-raw) b
raw-elim G (suc n) {A = A} hlev b (merid a i) = help a i
  where
  help : (a : _) → PathP (λ i → A (merid a i)) b (subst A (merid ptEM-raw) b)
  help = raw-elim G n (λ _ → isOfHLevelPathP' (suc n) (hlev _) _ _)
         λ i → transp (λ j → A (merid ptEM-raw (j ∧ i))) (~ i) b

EM : (G : AbGroup ℓ) (n : ℕ) → Type ℓ
EM G zero = EM-raw G zero
EM G (suc zero) = EM-raw G 1
EM G (suc (suc n)) = hLevelTrunc (4 + n) (EM-raw G (suc (suc n)))

0ₖ : {G : AbGroup ℓ} (n : ℕ) → EM G n
0ₖ {G = G} zero = AbGroupStr.0g (snd G)
0ₖ (suc zero) = embase
0ₖ (suc (suc n)) = ∣ ptEM-raw ∣

EM∙ : (G : AbGroup ℓ) (n : ℕ) → Pointed ℓ
EM∙ G n = EM G n , (0ₖ n)

EM-raw∙ : (G : AbGroup ℓ) (n : ℕ) → Pointed ℓ
EM-raw∙ G n = EM-raw G n , ptEM-raw

hLevelEM : (G : AbGroup ℓ) (n : ℕ) → isOfHLevel (2 + n) (EM G n)
hLevelEM G zero = AbGroupStr.is-set (snd G)
hLevelEM G (suc zero) = emsquash
hLevelEM G (suc (suc n)) = isOfHLevelTrunc (4 + n)

EM-raw→EM : (G : AbGroup ℓ) (n : ℕ) → EM-raw G n → EM G n
EM-raw→EM G zero x = x
EM-raw→EM G (suc zero) x = x
EM-raw→EM G (suc (suc n)) = ∣_∣

elim : {G : AbGroup ℓ} (n : ℕ) {A : EM G n → Type ℓ'}
        → ((x : _) → isOfHLevel (2 + n) (A x))
        → ((x : EM-raw G n) → A (EM-raw→EM G n x))
        → (x : _) → A x
elim zero hlev hyp x = hyp x
elim (suc zero) hlev hyp x = hyp x
elim (suc (suc n)) hlev hyp = trElim (λ _ → hlev _) hyp


{-# OPTIONS --cubical --no-import-sorts --safe --experimental-lossy-unification #-}
module Cubical.ZCohomology.Groups.Wedge where

open import Cubical.ZCohomology.Base
open import Cubical.ZCohomology.Properties
open import Cubical.ZCohomology.MayerVietorisUnreduced
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Function
open import Cubical.HITs.Wedge
open import Cubical.HITs.SetTruncation renaming (rec to sRec ; rec2 to pRec2 ; elim to sElim ; elim2 to sElim2 ; map to sMap)
open import Cubical.HITs.PropositionalTruncation renaming (rec to pRec ; ∣_∣ to ∣_∣₁)
open import Cubical.HITs.Truncation renaming (elim to trElim ; rec to trRec ; elim2 to trElim2)
open import Cubical.Data.Nat
open import Cubical.Algebra.Group

open import Cubical.ZCohomology.Groups.Unit
open import Cubical.ZCohomology.Groups.Sn

open import Cubical.HITs.Pushout
open import Cubical.Data.Sigma

open import Cubical.Foundations.Isomorphism
open import Cubical.Homotopy.Connected
open import Cubical.HITs.Susp
open import Cubical.HITs.S1
open import Cubical.HITs.Sn
open import Cubical.Foundations.Equiv

module _ {ℓ ℓ'} (A : Pointed ℓ) (B : Pointed ℓ') where
  module I = MV (typ A) (typ B) Unit (λ _ → pt A) (λ _ → pt B)

  Hⁿ-⋁ : (n : ℕ) → GroupIso (coHomGr (suc n) (A ⋁ B)) (×coHomGr (suc n) (typ A) (typ B))
  Hⁿ-⋁ zero = BijectionIsoToGroupIso bijIso
    where
    surj-helper : (x : coHom 0 Unit) → isInIm _ _ (I.Δ 0) x
    surj-helper =
      sElim (λ _ → isOfHLevelSuc 1 propTruncIsProp)
            λ f → ∣ (∣ (λ _ → f tt) ∣₂ , 0ₕ 0) , cong ∣_∣₂ (funExt λ _ → -rUnitₖ 0 (f tt)) ∣₁

    helper : (x : coHom 1 (A ⋁ B)) → isInIm _ _ (I.d 0) x → x ≡ 0ₕ 1
    helper x inim =
      pRec (setTruncIsSet _ _)
           (λ p → sym (snd p) ∙
                       MV.Im-Δ⊂Ker-d _ _ Unit (λ _ → pt A) (λ _ → pt B) 0 (fst p) (surj-helper (fst p)))
             inim

    bijIso : BijectionIso (coHomGr 1 (A ⋁ B)) (×coHomGr 1 (typ A) (typ B))
    BijectionIso.map' bijIso = I.i 1
    BijectionIso.inj bijIso =
      sElim (λ _ → isSetΠ λ _ → isProp→isSet (setTruncIsSet _ _))
            λ f inker → helper ∣ f ∣₂ (I.Ker-i⊂Im-d 0 ∣ f ∣₂ inker)
    BijectionIso.surj bijIso p = I.Ker-Δ⊂Im-i 1 p (isContr→isProp (isContrHⁿ-Unit 0) _ _)

  Hⁿ-⋁ (suc n) = Iso+Hom→GrIso mainIso
                                (sElim2 (λ _ _ → isOfHLevelPath 2 (isOfHLevel× 2 setTruncIsSet setTruncIsSet) _ _)
                                         λ _ _ → refl)
    where
    helpIso : ∀ {ℓ'''} {C : Type ℓ'''} → Iso (A ⋁ B → C) (Σ[ f ∈ (typ A → C) × (typ B → C) ] (fst f) (pt A) ≡ (snd f) (pt B))
    Iso.fun helpIso f = ((λ x → f (inl x)) , λ x → f (inr x)) , cong f (push tt)
    Iso.inv helpIso ((f , g) , p) (inl x) = f x
    Iso.inv helpIso ((f , g) , p) (inr x) = g x
    Iso.inv helpIso ((f , g) , p) (push a i) = p i
    Iso.rightInv helpIso ((f , g) , p) = ΣPathP (ΣPathP (refl , refl) , refl)
    Iso.leftInv helpIso f = funExt λ {(inl a) → refl ; (inr a) → refl ; (push a i) → refl}

    mainIso : Iso (coHom (2 + n) (A ⋁ B))
                  (coHom (2 + n) (typ A) × coHom (2 + n) (typ B))
    mainIso = compIso (setTruncIso helpIso) (compIso theIso setTruncOfProdIso)
      where
      forget :  ∥ (Σ[ f ∈ (typ A → coHomK (2 + n)) × (typ B → coHomK (2 + n)) ] (fst f) (pt A) ≡ (snd f) (pt B)) ∥₂
                     → ∥ (typ A → coHomK (2 + n)) × (typ B → coHomK (2 + n)) ∥₂
      forget = sMap (λ {((f , g) , _) → f , g})

      isEq :  (f :  ∥ (typ A → coHomK (2 + n)) × (typ B → coHomK (2 + n)) ∥₂) → isContr (fiber forget f)
      isEq = sElim (λ _ → isOfHLevelSuc 1 isPropIsContr) (uncurry λ f g → helper f g (f (pt A)) (g (pt B)) refl refl)
        where
        helper : (f : (typ A → coHomK (2 +  n))) (g : (typ B → coHomK (2 + n))) (x y : coHomK (2 + n))
               → f (pt A) ≡ x
               → g (pt B) ≡ y
               → isContr (fiber forget ∣ f , g ∣₂)
        helper f g = trElim2 (λ _ _ → isProp→isOfHLevelSuc (3 + n)
                               (isPropΠ2 λ _ _ → isPropIsContr))
                             (suspToPropElim2 (ptSn (suc n))
                                              (λ _ _ → isPropΠ2 λ _ _ → isPropIsContr)
                                              λ p q → (∣ (f , g) , (p ∙ sym q) ∣₂
                         , refl)
                         , uncurry (sElim (λ _ → isSetΠ λ _ → isOfHLevelPath 2 (isOfHLevelΣ 2 setTruncIsSet λ _ → isOfHLevelPath 2 setTruncIsSet _ _) _ _)
                               λ { ((f' , g') , id1) y →
                                     Σ≡Prop (λ _ → setTruncIsSet _ _)
                                              (pRec (setTruncIsSet _ _)
                                                 (λ id2 → trRec (setTruncIsSet _ _)
                                                   (λ pathp → cong ∣_∣₂ (ΣPathP ((sym id2) , pathp)))
                                                   (isConnectedPathP 1
                                                     {A = λ i → (fst (id2 (~ i)) (pt A) ≡ snd (id2 (~ i)) (pt B))}
                                                       (isConnectedPath 2 (isConnectedSubtr 3 n
                                                                          (subst (λ m → isConnected m (coHomK (2 + n))) (+-comm 3 n)
                                                                                (isConnectedKn (suc n)))) _ _)
                                                       (p ∙ sym q) id1 .fst))
                                                       (Iso.fun PathIdTrunc₀Iso y))}))
      theIso : Iso ∥ (Σ[ f ∈ (typ A → coHomK (2 + n)) × (typ B → coHomK (2 + n)) ] (fst f) (pt A) ≡ (snd f) (pt B)) ∥₂
                   ∥ (typ A → coHomK (2 + n)) × (typ B → coHomK (2 + n)) ∥₂
      theIso = equivToIso (forget , record { equiv-proof = isEq })

   {- Alternative, less direct proof :
       vSES→GroupIso _ _
         (ses (isOfHLevelSuc 0 (isContrHⁿ-Unit n))
              (isOfHLevelSuc 0 (isContrHⁿ-Unit (suc n)))
              (I.d (suc n))
              (I.Δ (suc (suc n)))
              (I.i (suc (suc n)))
              (I.Ker-i⊂Im-d (suc n))
              (I.Ker-Δ⊂Im-i (suc (suc n))))
   -}

  wedgeConnected : ((x : typ A) → ∥ pt A ≡ x ∥) → ((x : typ B) → ∥ pt B ≡ x ∥) → (x : A ⋁ B) → ∥ inl (pt A) ≡ x ∥
  wedgeConnected conA conB =
    PushoutToProp (λ _ → propTruncIsProp)
                  (λ a → pRec propTruncIsProp (λ p → ∣ cong inl p ∣₁) (conA a))
                   λ b → pRec propTruncIsProp (λ p → ∣ push tt ∙ cong inr p ∣₁) (conB b)


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

open import Definition.Typed.EqualityRelation

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

open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Substitution

open import Tools.Product

-- Reflexivity of valid types.
reflᵛ : ∀ {A Γ l}
        ([Γ] : ⊩ᵛ Γ)
        ([A] : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
      → Γ ⊩ᵛ⟨ l ⟩ A ≡ A / [Γ] / [A]
reflᵛ [Γ] [A] ⊢Δ [σ] =
  reflEq (proj₁ ([A] ⊢Δ [σ]))

-- Reflexivity of valid terms.
reflᵗᵛ : ∀ {A t Γ l}
         ([Γ] : ⊩ᵛ Γ)
         ([A] : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
         ([t] : Γ ⊩ᵛ⟨ l ⟩ t ∷ A / [Γ] / [A])
       → Γ ⊩ᵛ⟨ l ⟩ t ≡ t ∷ A / [Γ] / [A]
reflᵗᵛ [Γ] [A] [t] ⊢Δ [σ] =
  reflEqTerm (proj₁ ([A] ⊢Δ [σ])) (proj₁ ([t] ⊢Δ [σ]))


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

open import HoTT

module Reflective where

  record ReflectiveSubuniverse {ℓ} : Type (lsucc ℓ) where
    field

      P : Type ℓ → Type ℓ
      R : Type ℓ → Type ℓ

      η : (A : Type ℓ) → A → R A

      -- replete : (A B : Type ℓ) → P A → A ≃ B → P B


module nodcap.NF.Typing where

open import Data.Nat as ℕ using (ℕ; suc; zero)
open import Data.Pos as ℕ⁺ using (ℕ⁺; suc; _+_)
open import Data.Environment
open import Data.List as L using (List; []; _∷_; _++_)
open import Data.List.Any as LA using (Any; here; there)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import nodcap.Base
open import nodcap.Typing as FF using (⊢_)

-- Typing Rules.

infix 1 ⊢ⁿᶠ_

data ⊢ⁿᶠ_ : Environment → Set where

  send : {Γ Δ : Environment} {A B : Type} →

       ⊢ⁿᶠ A ∷ Γ → ⊢ⁿᶠ B ∷ Δ →
       -------------------
       ⊢ⁿᶠ A ⊗ B ∷ Γ ++ Δ

  recv : {Γ : Environment} {A B : Type} →

       ⊢ⁿᶠ A ∷ B ∷ Γ →
       -------------
       ⊢ⁿᶠ A ⅋ B ∷ Γ

  sel₁ : {Γ : Environment} {A B : Type} →

       ⊢ⁿᶠ A ∷ Γ →
       -----------
       ⊢ⁿᶠ A ⊕ B ∷ Γ

  sel₂ : {Γ : Environment} {A B : Type} →

       ⊢ⁿᶠ B ∷ Γ →
       -----------
       ⊢ⁿᶠ A ⊕ B ∷ Γ

  case : {Γ : Environment} {A B : Type} →

       ⊢ⁿᶠ A ∷ Γ → ⊢ⁿᶠ B ∷ Γ →
       -------------------
       ⊢ⁿᶠ A & B ∷ Γ

  halt :

       --------
       ⊢ⁿᶠ 𝟏 ∷ []

  wait : {Γ : Environment} →

       ⊢ⁿᶠ Γ →
       -------
       ⊢ⁿᶠ ⊥ ∷ Γ

  loop : {Γ : Environment} →

       -------
       ⊢ⁿᶠ ⊤ ∷ Γ

  mk?₁ : {Γ : Environment} {A : Type} →

       ⊢ⁿᶠ A ∷ Γ →
       ---------------------
       ⊢ⁿᶠ ?[ 1 ] A ∷ Γ

  mk!₁ : {Γ : Environment} {A : Type} →

       ⊢ⁿᶠ A ∷ Γ →
       ---------------------
       ⊢ⁿᶠ ![ 1 ] A ∷ Γ

  cont : {Γ : Environment} {A : Type} {m n : ℕ⁺} →

       ⊢ⁿᶠ ?[ m ] A ∷ ?[ n ] A ∷ Γ →
       ------------------------------
       ⊢ⁿᶠ ?[ m + n ] A ∷ Γ

  pool : {Γ Δ : Environment} {A : Type} {m n : ℕ⁺} →

       ⊢ⁿᶠ ![ m ] A ∷ Γ → ⊢ⁿᶠ ![ n ] A ∷ Δ →
       -------------------------------------
       ⊢ⁿᶠ ![ m + n ] A ∷ Γ ++ Δ

  exch : {Γ Δ : Environment} →

       Γ ∼[ bag ] Δ → ⊢ⁿᶠ Γ →
       --------------------
       ⊢ⁿᶠ Δ

fromNF : {Γ : Environment} → ⊢ⁿᶠ Γ → ⊢ Γ
fromNF (send x y) = FF.send (fromNF x) (fromNF y)
fromNF (recv x)   = FF.recv (fromNF x)
fromNF (sel₁ x)   = FF.sel₁ (fromNF x)
fromNF (sel₂ x)   = FF.sel₂ (fromNF x)
fromNF (case x y) = FF.case (fromNF x) (fromNF y)
fromNF  halt      = FF.halt
fromNF (wait x)   = FF.wait (fromNF x)
fromNF  loop      = FF.loop
fromNF (mk?₁ x)   = FF.mk?₁ (fromNF x)
fromNF (mk!₁ x)   = FF.mk!₁ (fromNF x)
fromNF (cont x)   = FF.cont (fromNF x)
fromNF (pool x y) = FF.pool (fromNF x) (fromNF y)
fromNF (exch x y) = FF.exch x (fromNF y)

-- -}
-- -}
-- -}
-- -}
-- -}


record _×_ (A B : Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B

open _×_
infixr 1 _,_

_∩_ : {I : Set} → (I → Set) → (I → Set) → I → Set
P ∩ Q = λ i → P i × Q i

record Preorder : Set₁ where
  no-eta-equality
  field I : Set

infix 4 _∈_

postulate
  Ty World Cxt : Set
  All : (P : Ty → Set) → Cxt → Set
  _∈_ : Ty → World → Set

module Monotone (pre : Preorder) where
  open Preorder pre

  postulate
    Monotone : (I → Set) → Set

  instance
    postulate
      all-monotone : {Γ : Cxt} {C : Ty → I → Set}
                     ⦃ w : ∀ {ty} → Monotone (C ty) ⦄ →
                     Monotone (λ W → All (λ ty → C ty W) Γ)

module Monad (pre : Preorder) (let open Preorder pre)
             (M : (I → Set) → I → Set) where
  postulate
    _>>=_ : ∀ {P Q W} → M P W → (∀ {W'} → P W' → M Q W') → M Q W

postulate
  M   : (World → Set) → World → Set
  Val : World → Set

preorder : Preorder
preorder .Preorder.I = World

module Inner (Dummy : Set) where -- Succeeds if no Dummy

  private
    -- Succeeds if type is given
    pre : _ -- Preorder
    pre = preorder

  open Monotone pre
  open Monad pre M

  postulate
    R : World → Set

    instance
      any-monotone : ∀ {ty} → Monotone (ty ∈_)

    local : ∀ Γ {W} → R W × All (_∈ W) Γ → M Val W
    ^     : ∀ {Q : World → Set} ⦃ m : Monotone Q ⦄ → ∀ {W} → Q W → M (R ∩ Q) W

  eval-method : ∀ Γ {W} → All (_∈ W) Γ → M Val W
  eval-method Γ args =
    ^ args >>= local _ -- Succeeds if giving Γ for _


-- Displayed SIP
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.DStructures.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.Properties
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Path
open import Cubical.Foundations.SIP
open import Cubical.Data.Sigma

open import Cubical.Relation.Binary

private
  variable
    ℓ ℓ' ℓ'' ℓ₁ ℓ₁' ℓ₁'' ℓ₂ ℓA ℓ≅A ℓB ℓ≅B ℓC ℓ≅C ℓ≅ᴰ : Level

-- a univalent reflexive graph structure on a type
record URGStr (A : Type ℓA) (ℓ≅A : Level) : Type (ℓ-max ℓA (ℓ-suc ℓ≅A)) where
  no-eta-equality
  constructor urgstr
  field
    _≅_ : Rel A A ℓ≅A
    ρ : isRefl _≅_
    uni : isUnivalent _≅_ ρ

-- another constructor for URGStr that uses that if the
-- relational singleton is contractible, the relation is univalent
make-𝒮 : {A : Type ℓA} {_≅_ : Rel A A ℓ≅A}
         (ρ : isRefl _≅_) (contrTotal : contrRelSingl _≅_)
         → URGStr A ℓ≅A
make-𝒮 {_≅_ = _≅_} _ _ .URGStr._≅_ = _≅_
make-𝒮 ρ _ .URGStr.ρ = ρ
make-𝒮 {_≅_ = _≅_} ρ contrTotal .URGStr.uni = contrRelSingl→isUnivalent _≅_ ρ contrTotal

module _ where
  open URGStr
  η-URGStr : {A : Type ℓA} {ℓ≅A : Level}
             (𝒮-A : URGStr A ℓ≅A) → urgstr (_≅_ 𝒮-A) (ρ 𝒮-A) (uni 𝒮-A) ≡ 𝒮-A
  _≅_ (η-URGStr 𝒮-A i) = _≅_ 𝒮-A
  ρ (η-URGStr 𝒮-A i) = ρ 𝒮-A
  uni (η-URGStr 𝒮-A i) = uni 𝒮-A

-- a displayed univalent reflexive graph structure over a URGStr on a type
record URGStrᴰ {A : Type ℓA} (𝒮-A : URGStr A ℓ≅A)
               (B : A → Type ℓB) (ℓ≅ᴰ : Level) : Type (ℓ-max (ℓ-max (ℓ-max ℓA ℓB) ℓ≅A) (ℓ-suc ℓ≅ᴰ)) where
  no-eta-equality
  constructor urgstrᴰ
  open URGStr 𝒮-A

  field
    _≅ᴰ⟨_⟩_ : {a a' : A} → B a → a ≅ a' → B a' → Type ℓ≅ᴰ
    ρᴰ : {a : A} → isRefl _≅ᴰ⟨ ρ a ⟩_
    uniᴰ : {a : A} → isUnivalent _≅ᴰ⟨ ρ a ⟩_ ρᴰ

open URGStrᴰ

-- another constructor for URGStrᴰ, using contractability of relational singletons
-- rather than univalence
make-𝒮ᴰ : {A : Type ℓA} {𝒮-A : URGStr A ℓ≅A}
          {B : A → Type ℓB}
          (_≅ᴰ⟨_⟩_ : {a a' : A} → B a → URGStr._≅_ 𝒮-A a a' → B a' → Type ℓ≅ᴰ)
          (ρᴰ : {a : A} → isRefl _≅ᴰ⟨ URGStr.ρ 𝒮-A a ⟩_)
          (contrTotal : (a : A) → contrRelSingl _≅ᴰ⟨ URGStr.ρ 𝒮-A a ⟩_)
          → URGStrᴰ 𝒮-A B ℓ≅ᴰ
make-𝒮ᴰ {A = A} {𝒮-A = 𝒮-A} _≅ᴰ⟨_⟩_ ρᴰ contrTotal ._≅ᴰ⟨_⟩_ = _≅ᴰ⟨_⟩_
make-𝒮ᴰ {A = A} {𝒮-A = 𝒮-A} _≅ᴰ⟨_⟩_ ρᴰ contrTotal .ρᴰ = ρᴰ
make-𝒮ᴰ {A = A} {𝒮-A = 𝒮-A} _≅ᴰ⟨_⟩_ ρᴰ contrTotal .uniᴰ {a} b b' = contrRelSingl→isUnivalent (_≅ᴰ⟨ URGStr.ρ 𝒮-A a ⟩_) (ρᴰ {a}) (contrTotal a) b b'

-- abbreviation to obtain contractibility of total space
𝒮→cTS : {A : Type ℓA} (StrA : URGStr A ℓ≅A) → contrRelSingl (URGStr._≅_ StrA)
𝒮→cTS StrA = isUnivalent→contrRelSingl _≅_ ρ uni
  where open URGStr StrA


------------------------------------------------------------------------
-- A lemma
------------------------------------------------------------------------

open import Mixfix.Expr
open import Mixfix.Acyclic.PrecedenceGraph using (acyclic)

module Mixfix.Acyclic.Lemma
         (g : PrecedenceGraphInterface.PrecedenceGraph acyclic)
         where

open import Data.List using ([]; _∷_)
open import Data.List.NonEmpty as List⁺ using (List⁺; _∷_; foldl; _⁺∷ʳ_)
open import Data.Product using (uncurry)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl)

open PrecedenceCorrect acyclic g
open import Mixfix.Fixity

-- A generalisation of Mixfix.Acyclic.Grammar.Prec.appˡ.

appˡ : ∀ {p} → Outer p left → List⁺ (Outer p left → ExprIn p left) →
       ExprIn p left
appˡ e fs = foldl (λ e f → f (similar e)) (λ f → f e) fs

appˡ-∷ʳ : ∀ {p} (e : Outer p left) fs f →
          appˡ e (fs ⁺∷ʳ f) ≡ f (similar (appˡ e fs))
appˡ-∷ʳ e (f′ ∷ fs) = helper e f′ fs
  where
  helper : ∀ {p} (e : Outer p left) f′ fs f →
           appˡ e ((f′ ∷ fs) ⁺∷ʳ f) ≡ f (similar (appˡ e (f′ ∷ fs)))
  helper e f′ []        f = refl
  helper e f′ (f″ ∷ fs) f = helper (similar (f′ e)) f″ fs f


------------------------------------------------------------------------
-- Breadth-first labelling of trees
------------------------------------------------------------------------

-- This module just defines breadth-first labelling. For a full
-- development including a specification and proof, see BreadthFirst.

module BreadthFirstWithoutProof where

open import Codata.Musical.Notation
open import Codata.Musical.Stream
open import Data.Product

open import Tree using (Tree; leaf; node)

------------------------------------------------------------------------
-- Universe

data U : Set₁ where
  tree   : (a : U) → U
  stream : (a : U) → U
  _⊗_    : (a b : U) → U
  ⌈_⌉    : (A : Set) → U

El : U → Set
El (tree a)   = Tree (El a)
El (stream a) = Stream (El a)
El (a ⊗ b)    = El a × El b
El ⌈ A ⌉      = A

------------------------------------------------------------------------
-- Programs

infixr 5 _∷_
infixr 4 _,_

mutual

  data ElP : U → Set₁ where
    ↓   : ∀ {a} (w : ElW a) → ElP a
    fst : ∀ {a b} (p : ElP (a ⊗ b)) → ElP a
    snd : ∀ {a b} (p : ElP (a ⊗ b)) → ElP b
    lab : ∀ {A B} (t : Tree A) (bss : ElP (stream ⌈ Stream B ⌉)) →
          ElP (tree ⌈ B ⌉ ⊗ stream ⌈ Stream B ⌉)

  -- The term WHNF is a bit of a misnomer here; only recursive
  -- /coinductive/ arguments are suspended (in the form of programs).

  data ElW : U → Set₁ where
    leaf : ∀ {a} → ElW (tree a)
    node : ∀ {a}
           (l : ∞ (ElP (tree a))) (x : ElW a) (r : ∞ (ElP (tree a))) →
           ElW (tree a)
    _∷_  : ∀ {a} (x : ElW a) (xs : ∞ (ElP (stream a))) → ElW (stream a)
    _,_  : ∀ {a b} (x : ElW a) (y : ElW b) → ElW (a ⊗ b)
    ⌈_⌉  : ∀ {A} (x : A) → ElW ⌈ A ⌉

fstW : ∀ {a b} → ElW (a ⊗ b) → ElW a
fstW (x , y) = x

sndW : ∀ {a b} → ElW (a ⊗ b) → ElW b
sndW (x , y) = y

-- Uses the n-th stream to label the n-th level in the tree. Returns
-- the remaining stream elements (for every level).

labW : ∀ {A B} → Tree A → ElW (stream ⌈ Stream B ⌉) →
       ElW (tree ⌈ B ⌉ ⊗ stream ⌈ Stream B ⌉)
labW leaf         bss                = (leaf , bss)
labW (node l _ r) (⌈ b ∷ bs ⌉ ∷ bss) =
  (node (♯ fst x) ⌈ b ⌉ (♯ fst y) , ⌈ ♭ bs ⌉ ∷ ♯ snd y)
  where
  x = lab (♭ l) (♭ bss)
  y = lab (♭ r) (snd x)

whnf : ∀ {a} → ElP a → ElW a
whnf (↓ w)       = w
whnf (fst p)     = fstW (whnf p)
whnf (snd p)     = sndW (whnf p)
whnf (lab t bss) = labW t (whnf bss)

mutual

  ⟦_⟧W : ∀ {a} → ElW a → El a
  ⟦ leaf       ⟧W = leaf
  ⟦ node l x r ⟧W = node (♯ ⟦ ♭ l ⟧P) ⟦ x ⟧W (♯ ⟦ ♭ r ⟧P)
  ⟦ x ∷ xs     ⟧W = ⟦ x ⟧W ∷ ♯ ⟦ ♭ xs ⟧P
  ⟦ (x , y)    ⟧W = (⟦ x ⟧W , ⟦ y ⟧W)
  ⟦ ⌈ x ⌉      ⟧W = x

  ⟦_⟧P : ∀ {a} → ElP a → El a
  ⟦ p ⟧P = ⟦ whnf p ⟧W

------------------------------------------------------------------------
-- Breadth-first labelling

label′ : ∀ {A B} → Tree A → Stream B →
         ElP (tree ⌈ B ⌉ ⊗ stream ⌈ Stream B ⌉)
label′ t bs = lab t (↓ (⌈ bs ⌉ ∷ ♯ snd (label′ t bs)))

label : ∀ {A B} → Tree A → Stream B → Tree B
label t bs = ⟦ fst (label′ t bs) ⟧P


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

module TreeFold where

open import Prelude
open import Data.List
open import Algebra using (Associative)
open import Data.List.Properties using (foldr-fusion; foldl-fusion; foldl′-foldl)

infixr 5 _^_&_
data Spine (A : Type a) : Type a where
  &0 : Spine A
  _^_&_ : A → ℕ → Spine A → Spine A

private variable n : ℕ

module Cons (f : A → A → A) where
  infixr 5 _^_∹_
  _^_∹_ : A → ℕ → Spine A → Spine A
  x ^ n ∹ &0 = x ^ n & &0
  x ^ n ∹ y ^ zero  & xs = f x y ^ suc n ∹ xs
  x ^ n ∹ y ^ suc m & xs = x ^ n & y ^ m & xs

  _∹_ : A → Spine A → Spine A
  _∹_ = _^ zero ∹_

module TreeFoldR (f : A → A → A) (z : A) where
  open Cons f public

  fold : Spine A → A
  fold &0 = z
  fold (x ^ n & xs) = f x (fold xs)

  spine : List A → Spine A
  spine = foldr _∹_ &0

  treeFold : List A → A
  treeFold = fold ∘ spine

  ∹-hom : Associative f → ∀ x xs → fold (x ^ n ∹ xs) ≡ f x (fold xs)
  ∹-hom p x &0 = refl
  ∹-hom p x (y ^ zero  & xs) = ∹-hom p (f x y) xs ; p x y (fold xs)
  ∹-hom p x (y ^ suc n & xs) = refl

  treeFoldHom : Associative f → ∀ xs → treeFold xs ≡ foldr f z xs
  treeFoldHom f-assoc = foldr-fusion fold &0 (∹-hom f-assoc)

module TreeFold1 (f : A → A → A) where
  open import Data.Maybe
  open import Data.Maybe.Properties
  open import Data.List.Properties

  f? : Maybe A → Maybe A → Maybe A
  f? nothing  = id
  f? (just x) = just ∘ maybe x (f x)

  open TreeFoldR f? nothing

  treeFoldMay : List A → Maybe A
  treeFoldMay = treeFold ∘ map just

  treeFoldMayHom : Associative f → ∀ xs → treeFoldMay xs ≡ foldrMay f xs
  treeFoldMayHom f-assoc xs = treeFoldHom f?-assoc (map just xs) ; map-fusion f? nothing just xs
    where
    f?-assoc : Associative f?
    f?-assoc nothing y z = refl
    f?-assoc (just x) nothing z = refl
    f?-assoc (just x) (just y) nothing = refl
    f?-assoc (just x) (just y) (just z) = cong just (f-assoc x y z)

  isJustSpine? : Spine (Maybe A) → Bool
  isJustSpine? &0 = false
  isJustSpine? (x ^ y & xs) = is-just x

  IsJustSpine : Spine (Maybe A) → Type
  IsJustSpine = T ∘ isJustSpine?

  isJust-cons : ∀ x n xs → IsJustSpine (just x ^ n ∹ xs)
  isJust-cons x n &0 = tt
  isJust-cons x n (y ^ zero  & xs) = isJust-cons (maybe x (f x) y) (suc n) xs
  isJust-cons x n (y ^ suc m & xs) = tt

  isJust-spine : ∀ xs → NonEmpty xs → IsJustSpine (spine (map just xs))
  isJust-spine (x ∷ xs) p = isJust-cons x 0 (spine (map just xs))

  isJust-fold : ∀ xs → IsJustSpine xs → IsJust (fold xs)
  isJust-fold (just _ ^ _ & _) _ = tt

  isJust-treeFoldMay : ∀ xs → NonEmpty xs → IsJust (treeFoldMay xs)
  isJust-treeFoldMay xs xsne = isJust-fold (spine (map just xs)) (isJust-spine xs xsne)

  treeFold1 : (xs : List A) → ⦃ NonEmpty xs ⦄ → A
  treeFold1 xs ⦃ xsne ⦄ = fromJust (treeFoldMay xs)
    where instance _ = isJust-treeFoldMay xs xsne

  -- treeFold1-hom : Associative f → ∀ xs → ⦃ xsne : NonEmpty xs ⦄ → treeFold1 xs ≡ foldr1 f xs
  -- treeFold1-hom f-assoc xs ⦃ xsne ⦄ = {!!}
open TreeFold1 using (treeFoldMay; treeFoldMayHom; treeFold1) public

open TreeFoldR using (treeFold; treeFoldHom) public

module TreeFoldL (f : A → A → A) (z : A) where
  open TreeFoldR (flip f) z using (_∹_; fold; ∹-hom) public

  spine : List A → Spine A
  spine = foldl (flip _∹_) &0

  treeFoldL : List A → A
  treeFoldL = fold ∘ spine

  treeFoldLHom : Associative f → ∀ xs → treeFoldL xs ≡ foldl f z xs
  treeFoldLHom p = foldl-fusion fold &0 (flip (∹-hom λ x y z → sym (p z y x)))

open TreeFoldL using (treeFoldL; treeFoldLHom) public

module StrictCons (f : A → A → A) where
  infixr 5 _^_∹_
  _^_∹_ : A → ℕ → Spine A → Spine A
  x ^ n ∹ &0 = x ^ n & &0
  x ^ n ∹ y ^ zero  & xs = (_^ suc n ∹ xs) $! (f x y)
  x ^ n ∹ y ^ suc m & xs = x ^ n & y ^ m & xs

  _∹_ : A → Spine A → Spine A
  _∹_ = _^ zero ∹_

  private module Lazy = Cons f

  strict-lazy-cons : ∀ x n xs → x ^ n ∹ xs ≡ x Lazy.^ n ∹ xs
  strict-lazy-cons x n &0 = refl
  strict-lazy-cons x n (y ^ zero  & xs) = $!-≡ (_^ suc n ∹ xs) (f x y) ; strict-lazy-cons (f x y) (suc n) xs
  strict-lazy-cons x n (y ^ suc m & xs) = refl

module TreeFoldL′ (f : A → A → A) (z : A) where
  open StrictCons (flip f)

  spine : List A → Spine A
  spine = foldl′ (flip _∹_) &0

  private module Lazy = TreeFoldL f z

  open Lazy using (fold)

  treeFoldL′ : List A → A
  treeFoldL′ = fold ∘ spine

  treeFoldL′Hom : Associative f → ∀ xs → treeFoldL′ xs ≡ foldl f z xs
  treeFoldL′Hom p xs =
    cong fold (foldl′-foldl (flip _∹_) &0 xs ; cong (λ c → foldl c &0 xs) (funExt λ ys → funExt λ y → strict-lazy-cons y 0 ys)) ;
    Lazy.treeFoldLHom p xs

open TreeFoldL′ using (treeFoldL′; treeFoldL′Hom) public


open import Nat
open import Prelude
open import List

open import contexts
open import core

module results-checks where
  Coerce-preservation : ∀{Δ Σ' r v τ} →
                          Δ , Σ' ⊢ r ·: τ →
                          Coerce r := v →
                          Σ' ⊢ v ::ⱽ τ
  Coerce-preservation (TAFix x x₁) ()
  Coerce-preservation (TAApp ta-r ta-r₁) ()
  Coerce-preservation TAUnit CoerceUnit = TSVUnit
  Coerce-preservation (TAPair ta-r1 ta-r2) (CoercePair c-r1 c-r2)
    = TSVPair (Coerce-preservation ta-r1 c-r1) (Coerce-preservation ta-r2 c-r2)
  Coerce-preservation (TAFst ta-r) ()
  Coerce-preservation (TASnd ta-r) ()
  Coerce-preservation (TACtor h1 h2 ta-r) (CoerceCtor c-r)
    = TSVCtor h1 h2 (Coerce-preservation ta-r c-r)
  Coerce-preservation (TACase x x₁ ta-r x₂ x₃) ()
  Coerce-preservation (TAHole x x₁) ()

  {- TODO : We should revive this - it's a good sanity check.
            Of course, we have to decide what version of "complete" we want to use
            for results
  -- all complete finals (that type-check) are values
  mutual
    complete-finals-values : ∀{Δ Σ' r τ} →
                               Δ , Σ' ⊢ r ·: τ →
                               r rcomplete →
                               r final →
                               r value
    complete-finals-values (TALam Γ⊢E _) (RCLam E-cmp e-cmp) (FLam E-fin) =
      VLam (env-complete-final-values Γ⊢E E-cmp E-fin) e-cmp
    complete-finals-values (TAFix Γ⊢E _) (RCFix E-cmp e-cmp) (FFix E-fin) =
      VFix (env-complete-final-values Γ⊢E E-cmp E-fin) e-cmp
    complete-finals-values (TAApp ta1 ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2)
      with complete-finals-values ta1 cmp1 fin1
    complete-finals-values (TAApp ta1 ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2) | VLam _ _ = abort (h1 refl)
    complete-finals-values (TAApp ta1 ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2) | VFix _ _ = abort (h2 refl)
    complete-finals-values (TAApp () ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2)  | VTpl _
    complete-finals-values (TAApp () ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2)  | VCon _
    complete-finals-values (TAApp ta1 ta2) (RCAp cmp1 cmp2) (FAp fin1 fin2 h1 h2) | VPF _ = {!!}
    complete-finals-values (TATpl ∥rs⊫=∥τs∥ ta) (RCTpl h1) (FTpl h2) =
      VTpl λ {i} i<∥rs∥ → complete-finals-values (ta i<∥rs∥ (tr (λ y → i < y) ∥rs⊫=∥τs∥ i<∥rs∥)) (h1 i<∥rs∥) (h2 i<∥rs∥)
    complete-finals-values (TAGet i<∥τs∥ ta) (RCGet cmp) (FGet fin x) = {!!}
    complete-finals-values (TACtor _ _ ta) (RCCtor cmp) (FCon fin) =
      VCon (complete-finals-values ta cmp fin)
    complete-finals-values (TACase x x₁ ta x₂ x₃) (RCCase x₄ cmp x₅) (FCase fin x₆ x₇) = {!!}
    complete-finals-values (TAHole _ _) () fin
    complete-finals-values (TAPF x) (RCPF x₁) (FPF x₂) = {!!}

    env-complete-final-values : ∀{Δ Σ' Γ E} →
                                  Δ , Σ' , Γ ⊢ E →
                                  E env-complete →
                                  E env-final →
                                  E env-values
    env-complete-final-values ta (ENVC E-cmp) (EF E-fin) =
      EF (λ rx∈E → complete-finals-values (π2 (π2 (env-all-E ta rx∈E))) (E-cmp rx∈E) (E-fin rx∈E))
  -}


------------------------------------------------------------------------
-- The Agda standard library
--
-- The lifting of a non-strict order to incorporate a new supremum
------------------------------------------------------------------------

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

-- This module is designed to be used with
-- Relation.Nullary.Construct.Add.Supremum

open import Relation.Binary

module Relation.Binary.Construct.Add.Supremum.NonStrict
  {a ℓ} {A : Set a} (_≤_ : Rel A ℓ) where

open import Level using (_⊔_)
open import Data.Sum as Sum
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
import Relation.Binary.PropositionalEquality as P
open import Relation.Nullary.Construct.Add.Supremum
import Relation.Binary.Construct.Add.Supremum.Equality as Equality

------------------------------------------------------------------------
-- Definition

data _≤⁺_ : Rel (A ⁺) (a ⊔ ℓ) where
  [_]  : {k l : A} → k ≤ l → [ k ] ≤⁺ [ l ]
  _≤⊤⁺ : (k : A ⁺)         → k     ≤⁺ ⊤⁺

------------------------------------------------------------------------
-- Properties

[≤]-injective : ∀ {k l} → [ k ] ≤⁺ [ l ] → k ≤ l
[≤]-injective [ p ] = p

module _ {e} {_≈_ : Rel A e} where

  open Equality _≈_

  ≤⁺-reflexive : (_≈_ ⇒ _≤_) → (_≈⁺_ ⇒ _≤⁺_)
  ≤⁺-reflexive ≤-reflexive [ p ] = [ ≤-reflexive p ]
  ≤⁺-reflexive ≤-reflexive ⊤⁺≈⊤⁺ = ⊤⁺ ≤⊤⁺

  ≤⁺-antisym : Antisymmetric _≈_ _≤_ → Antisymmetric _≈⁺_ _≤⁺_
  ≤⁺-antisym ≤-antisym [ p ]    [ q ]    = [ ≤-antisym p q ]
  ≤⁺-antisym ≤-antisym (⊤⁺ ≤⊤⁺) (⊤⁺ ≤⊤⁺) = ⊤⁺≈⊤⁺

≤⁺-trans : Transitive _≤_ → Transitive _≤⁺_
≤⁺-trans ≤-trans [ p ] [ q ]   = [ ≤-trans p q ]
≤⁺-trans ≤-trans p     (l ≤⊤⁺) = _ ≤⊤⁺

≤⁺-maximum : Maximum _≤⁺_ ⊤⁺
≤⁺-maximum = _≤⊤⁺

≤⁺-dec : Decidable _≤_ → Decidable _≤⁺_
≤⁺-dec _≤?_ k     ⊤⁺    = yes (k ≤⊤⁺)
≤⁺-dec _≤?_ ⊤⁺    [ l ] = no (λ ())
≤⁺-dec _≤?_ [ k ] [ l ] = Dec.map′ [_] [≤]-injective (k ≤? l)

≤⁺-total : Total _≤_ → Total _≤⁺_
≤⁺-total ≤-total k     ⊤⁺    = inj₁ (k ≤⊤⁺)
≤⁺-total ≤-total ⊤⁺    l     = inj₂ (l ≤⊤⁺)
≤⁺-total ≤-total [ k ] [ l ] = Sum.map [_] [_] (≤-total k l)

≤⁺-irrelevant : Irrelevant _≤_ → Irrelevant _≤⁺_
≤⁺-irrelevant ≤-irr [ p ]   [ q ]    = P.cong _ (≤-irr p q)
≤⁺-irrelevant ≤-irr (k ≤⊤⁺) (k ≤⊤⁺) = P.refl

------------------------------------------------------------------------
-- Structures

module _ {e} {_≈_ : Rel A e} where

  open Equality _≈_

  ≤⁺-isPreorder : IsPreorder _≈_ _≤_ → IsPreorder _≈⁺_ _≤⁺_
  ≤⁺-isPreorder ≤-isPreorder = record
    { isEquivalence = ≈⁺-isEquivalence isEquivalence
    ; reflexive     = ≤⁺-reflexive reflexive
    ; trans         = ≤⁺-trans trans
    } where open IsPreorder ≤-isPreorder

  ≤⁺-isPartialOrder : IsPartialOrder _≈_ _≤_ → IsPartialOrder _≈⁺_ _≤⁺_
  ≤⁺-isPartialOrder ≤-isPartialOrder = record
    { isPreorder = ≤⁺-isPreorder isPreorder
    ; antisym    = ≤⁺-antisym antisym
    } where open IsPartialOrder ≤-isPartialOrder

  ≤⁺-isDecPartialOrder : IsDecPartialOrder _≈_ _≤_ → IsDecPartialOrder _≈⁺_ _≤⁺_
  ≤⁺-isDecPartialOrder ≤-isDecPartialOrder = record
    { isPartialOrder = ≤⁺-isPartialOrder isPartialOrder
    ; _≟_            = ≈⁺-dec _≟_
    ; _≤?_           = ≤⁺-dec _≤?_
    } where open IsDecPartialOrder ≤-isDecPartialOrder

  ≤⁺-isTotalOrder : IsTotalOrder _≈_ _≤_ → IsTotalOrder _≈⁺_ _≤⁺_
  ≤⁺-isTotalOrder ≤-isTotalOrder = record
    { isPartialOrder = ≤⁺-isPartialOrder isPartialOrder
    ; total          = ≤⁺-total total
    } where open IsTotalOrder ≤-isTotalOrder

  ≤⁺-isDecTotalOrder : IsDecTotalOrder _≈_ _≤_ → IsDecTotalOrder _≈⁺_ _≤⁺_
  ≤⁺-isDecTotalOrder ≤-isDecTotalOrder = record
    { isTotalOrder = ≤⁺-isTotalOrder isTotalOrder
    ; _≟_          = ≈⁺-dec _≟_
    ; _≤?_         = ≤⁺-dec _≤?_
    } where open IsDecTotalOrder ≤-isDecTotalOrder


------------------------------------------------------------------------
-- A type soundness result
------------------------------------------------------------------------

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

module Lambda.Delay-monad.Type-soundness where

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

open import Function-universe equality-with-J
open import Maybe equality-with-J
open import Monad equality-with-J
open import Vec.Function equality-with-J

open import Delay-monad.Always
open import Delay-monad.Bisimilarity
open import Delay-monad.Monad

open import Lambda.Delay-monad.Interpreter
open import Lambda.Syntax

open Closure Tm

-- If we can prove □ ∞ (WF-MV σ) (run x), then x does not "go wrong".

does-not-go-wrong : ∀ {σ} {x : M ∞ Value} →
                    □ ∞ (WF-MV σ) (run x) → ¬ x ≈M fail
does-not-go-wrong (now {x = nothing} ())
does-not-go-wrong (now {x = just x} x-wf) ()
does-not-go-wrong (later x-wf)            (laterˡ x↯) =
  does-not-go-wrong (force x-wf) x↯

-- A "constructor" for □ i ∘ WF-MV.

_>>=-wf_ :
  ∀ {i σ τ} {x : M ∞ Value} {f : Value → M ∞ Value} →
  □ i (WF-MV σ) (run x) →
  (∀ {v} → WF-Value σ v → □ i (WF-MV τ) (run (f v))) →
  □ i (WF-MV τ) (MaybeT.run (x >>= f))
x-wf >>=-wf f-wf =
  □->>= x-wf λ { {nothing} ()
               ; {just v}  v-wf → f-wf v-wf
               }

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

mutual

  ⟦⟧-wf : ∀ {i n Γ} (t : Tm n) {σ} → Γ ⊢ t ∈ σ →
          ∀ {ρ} → WF-Env Γ ρ →
          □ i (WF-MV σ) (run (⟦ t ⟧ ρ))
  ⟦⟧-wf (con i)   con             ρ-wf = now con
  ⟦⟧-wf (var x)   var             ρ-wf = now (ρ-wf x)
  ⟦⟧-wf (ƛ t)     (ƛ t∈)          ρ-wf = now (ƛ t∈ ρ-wf)
  ⟦⟧-wf (t₁ · t₂) (t₁∈ · t₂∈) {ρ} ρ-wf =
    ⟦⟧-wf t₁ t₁∈ ρ-wf >>=-wf λ f-wf →
    ⟦⟧-wf t₂ t₂∈ ρ-wf >>=-wf λ v-wf →
    ∙-wf f-wf v-wf

  ∙-wf : ∀ {i σ τ f v} →
         WF-Value (σ ⇾ τ) f → WF-Value (force σ) v →
         □ i (WF-MV (force τ)) (run (f ∙ v))
  ∙-wf (ƛ t₁∈ ρ₁-wf) v₂-wf =
    later λ { .force → ⟦⟧-wf _ t₁∈ (cons-wf v₂-wf ρ₁-wf) }

type-soundness : ∀ {t : Tm 0} {σ} →
                 nil ⊢ t ∈ σ → ¬ ⟦ t ⟧ nil ≈M fail
type-soundness {t} {σ} =
  nil ⊢ t ∈ σ                      ↝⟨ (λ t∈ → ⟦⟧-wf _ t∈ nil-wf) ⟩
  □ ∞ (WF-MV σ) (run (⟦ t ⟧ nil))  ↝⟨ does-not-go-wrong ⟩□
  ¬ ⟦ t ⟧ nil ≈M fail              □


------------------------------------------------------------------------
-- The Agda standard library
--
-- List Zippers, basic types and operations
------------------------------------------------------------------------

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

module Data.List.Zipper where

open import Data.Nat.Base
open import Data.Maybe.Base as Maybe using (Maybe ; just ; nothing)
open import Data.List.Base as List using (List ; [] ; _∷_)
open import Function

-- Definition
------------------------------------------------------------------------

-- A List Zipper represents a List together with a particular sub-List
-- in focus. The user can attempt to move the focus left or right, with
-- a risk of failure if one has already reached the corresponding end.

-- To make these operations efficient, the `context` the sub List in
-- focus lives in is stored *backwards*. This is made formal by `toList`
-- which returns the List a Zipper represents.

record Zipper {a} (A : Set a) : Set a where
  constructor mkZipper
  field context : List A
        value   : List A

  toList : List A
  toList = List.reverse context List.++ value
open Zipper public

-- Embedding Lists as Zippers without any context
fromList : ∀ {a} {A : Set a} → List A → Zipper A
fromList = mkZipper []

-- Fundamental operations of a Zipper: Moving around
------------------------------------------------------------------------

module _ {a} {A : Set a} where

 left : Zipper A → Maybe (Zipper A)
 left (mkZipper []        val) = nothing
 left (mkZipper (x ∷ ctx) val) = just (mkZipper ctx (x ∷ val))

 right : Zipper A → Maybe (Zipper A)
 right (mkZipper ctx [])        = nothing
 right (mkZipper ctx (x ∷ val)) = just (mkZipper (x ∷ ctx) val)

-- Focus-respecting operations
------------------------------------------------------------------------

module _ {a} {A : Set a} where

 reverse : Zipper A → Zipper A
 reverse (mkZipper ctx val) = mkZipper val ctx

 -- If we think of a List [x₁⋯xₘ] split into a List [xₙ₊₁⋯xₘ] in focus
 -- of another list [x₁⋯xₙ] then there are 4 places (marked {k} here) in
 -- which we can insert new values: [{1}x₁⋯xₙ{2}][{3}xₙ₊₁⋯xₘ{4}]

 -- The following 4 functions implement these 4 insertions.

 -- `xs ˢ++ zp` inserts `xs` on the `s` side of the context of the Zipper `zp`
 -- `zp ++ˢ xs` insert `xs` on the `s` side of the value in focus of the Zipper `zp`

 infixr 5 _ˡ++_ _ʳ++_
 infixl 5 _++ˡ_ _++ʳ_
 -- {1}
 _ˡ++_ : List A → Zipper A → Zipper A
 xs ˡ++ mkZipper ctx val = mkZipper (ctx List.++ List.reverse xs) val

 -- {2}
 _ʳ++_ : List A → Zipper A → Zipper A
 xs ʳ++ mkZipper ctx val = mkZipper (List.reverse xs List.++ ctx) val

 -- {3}
 _++ˡ_ : Zipper A → List A → Zipper A
 mkZipper ctx val ++ˡ xs = mkZipper ctx (xs List.++ val)

 -- {4}
 _++ʳ_ : Zipper A → List A → Zipper A
 mkZipper ctx val ++ʳ xs = mkZipper ctx (val List.++ xs)

-- List-like operations
------------------------------------------------------------------------

module _ {a} {A : Set a} where

 length : Zipper A → ℕ
 length (mkZipper ctx val) = List.length ctx + List.length val

module _ {a b} {A : Set a} {B : Set b} where

 map : (A → B) → Zipper A → Zipper B
 map f (mkZipper ctx val) = (mkZipper on List.map f) ctx val

 foldr : (A → B → B) → B → Zipper A → B
 foldr c n (mkZipper ctx val) = List.foldl (flip c) (List.foldr c n val) ctx

-- Generating all the possible foci of a list
------------------------------------------------------------------------

module _ {a} {A : Set a} where

 allFociIn : List A → List A → List (Zipper A)
 allFociIn ctx []           = List.[ mkZipper ctx [] ]
 allFociIn ctx xxs@(x ∷ xs) = mkZipper ctx xxs ∷ allFociIn (x ∷ ctx) xs

 allFoci : List A → List (Zipper A)
 allFoci = allFociIn []


data D : Set where

record R : Set where
  constructor d
  field x : D

record R′ : Set where
  coinductive
  constructor d
  field r : R′

f : R → D
f r = let d x = r in x


open import Level using (Level; _⊔_)
open import Function using (id; case_of_)
open import Data.Nat using (ℕ; suc)
open import Data.Fin using (Fin; suc; zero)
open import Data.Vec as Vec using (Vec) renaming (_∷_ to _,_; [] to ∅)
open import Data.List using (_∷_; [])
open import Data.List as List using (List; _++_) renaming (_∷_ to _,_; [] to ∅)
open import Data.Product as Product using (∃; _×_; _,_)
open import Relation.Binary.PropositionalEquality as P using (_≡_; refl; sym; cong)

module IntuitionisticLogic (U : Set) (⟦_⟧ᵁ : U → Set) where

infix  30 _⊗_
infixr 20 _⇒_

data Type : Set where
  el    : (A : U) → Type
  _⊗_   : Type → Type → Type
  _⇒_  : Type → Type → Type

module Implicit where

  infix  4  _⊢_

  data _⊢_ : ∀ {k} (Γ : Vec Type k) (A : Type) → Set where
    var   : ∀ {k} {Γ : Vec Type k} (x : Fin k) → Γ ⊢ Vec.lookup x Γ
    abs   : ∀ {A B} {k} {Γ : Vec Type k} → A , Γ ⊢ B → Γ ⊢ A ⇒ B
    app   : ∀ {A B} {k} {Γ : Vec Type k} → Γ ⊢ A ⇒ B → Γ ⊢ A → Γ ⊢ B
    pair  : ∀ {A B} {k} {Γ : Vec Type k} → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ⊗ B
    case  : ∀ {A B C} {k} {Γ : Vec Type k} → Γ ⊢ A ⊗ B → A , B , Γ ⊢ C → Γ ⊢ C

  swap : ∀ {A B} {k} {Γ : Vec Type k} → Γ ⊢ A ⊗ B ⇒ B ⊗ A
  swap = abs (case (var zero) (pair (var (suc zero)) (var zero)))

  Vec-exch : ∀ {k} (i : Fin k) → Vec Type (suc k) → Vec Type (suc k)
  Vec-exch  zero    (A , B , Γ)  = B , A , Γ
  Vec-exch (suc i)  (A , Γ)      = A , (Vec-exch i Γ)

  lemma-var : ∀ {k} {Γ : Vec Type (suc k)} → ∀ i x → ∃ λ y → Vec.lookup x Γ ≡ Vec.lookup y (Vec-exch i Γ)
  lemma-var {Γ = A , B , Γ} zero     zero           = suc zero , refl
  lemma-var {Γ = A , B , Γ} zero     (suc zero)     = zero , refl
  lemma-var {Γ = A , B , Γ} zero     (suc (suc x))  = suc (suc x) , refl
  lemma-var {Γ = A , Γ} (suc i)  zero           = zero , refl
  lemma-var {Γ = A , Γ} (suc i)  (suc x)        = Product.map suc id (lemma-var {Γ = Γ} i x)

  exch : ∀ {k} {Γ : Vec Type (suc k)} {A} → ∀ i → Γ ⊢ A → Vec-exch i Γ ⊢ A
  exch {Γ = Γ} i (var x) with lemma-var {Γ = Γ} i x
  exch {Γ = Γ} i (var x) | y , p rewrite p  = var y
  exch i (abs t)     = abs (exch (suc i) t)
  exch i (app s t)   = app (exch i s) (exch i t)
  exch i (pair s t)  = pair (exch i s) (exch i t)
  exch i (case s t)  = case (exch i s) (exch (suc (suc i)) t)

module Explicit where

  infix  4  _⊢_

  data _⊢_ : ∀ (X : List Type) (A : Type) → Set where
    var   : ∀ {A} → A , ∅ ⊢ A
    abs   : ∀ {X A B} → A , X ⊢ B → X ⊢ A ⇒ B
    app   : ∀ {X Y A B} → X ⊢ A ⇒ B → Y ⊢ A → X ++ Y ⊢ B
    pair  : ∀ {X Y A B} → X ⊢ A → Y ⊢ B → X ++ Y ⊢ A ⊗ B
    case  : ∀ {X Y A B C} → X ⊢ A ⊗ B → A , B , Y ⊢ C → X ++ Y ⊢ C
    weak  : ∀ {X Y A} → X ⊢ A → X ++ Y ⊢ A
    cont  : ∀ {X A B} → A , A , X ⊢ B → A , X ⊢ B
    exch  : ∀ {X Y Z W A} →  (X ++ Z) ++ (Y ++ W) ⊢ A
          →  (X ++ Y) ++ (Z ++ W) ⊢ A

  exch₀ : ∀ {X A B C} → B , A , X ⊢ C → A , B , X ⊢ C
  exch₀ {X} {A} {B} = exch {∅} {A , ∅} {B , ∅} {X}

  swap : ∀ {X A B} → X ⊢ A ⊗ B ⇒ B ⊗ A
  swap {X} {A} {B} = abs (case var (exch₀ (pair var (weak {A , ∅} {X} var))))

  record Reify {a b : Level} (A : Set a) (B : Set b) : Set (a ⊔ b) where
    field
      ⟦_⟧ : A → B

  instance
    ReifyType : Reify Type Set
    ReifyType = record { ⟦_⟧ = ⟦_⟧ }
      where

      ⟦_⟧ : Type → Set
      ⟦ el A    ⟧ = ⟦ A ⟧ᵁ
      ⟦ A ⊗ B   ⟧ = ⟦ A ⟧ × ⟦ B ⟧
      ⟦ A ⇒ B  ⟧ = ⟦ A ⟧ → ⟦ B ⟧

  data Ctxt : ∀ (X : List Set) → Set₁ where
    ∅ : Ctxt ∅
    _,_ : ∀ {A X} → A → Ctxt X → Ctxt (A , X)

  open Reify {{...}} using (⟦_⟧)

  instance
    ReifyCtxt : Reify (List Type) (List Set)
    ReifyCtxt = record { ⟦_⟧ = List.map ⟦_⟧ }

  Ctxt-insert : {A : Type} {X Y : List Type} → ⟦ A ⟧ → Ctxt ⟦ X ++ Y ⟧ → Ctxt ⟦ X ++ (A , Y) ⟧
  Ctxt-insert {A} {∅} {Y} A′ E = A′ , E
  Ctxt-insert {A} {B , X} {Y} A′ (B′ , E) = B′ , Ctxt-insert {A} {X} {Y} A′ E

  Ctxt-exch : {X Y Z W : List Type} → Ctxt ⟦ (X ++ Y) ++ (Z ++ W) ⟧ → Ctxt ⟦ (X ++ Z) ++ (Y ++ W) ⟧
  Ctxt-exch {X = ∅} {Y = ∅} {Z} {W} E = E
  Ctxt-exch {X = ∅} {Y = A , Y} {Z} {W} (A′ , E) = Ctxt-insert {A} {Z} {Y ++ W} A′ (Ctxt-exch {∅} {Y} {Z} {W} E)
  Ctxt-exch {X = A , X} {Y} {Z} {W} (A′ , E) = A′ , Ctxt-exch {X} {Y} {Z} {W} E

  Ctxt-split : {X Y : List Type} → Ctxt ⟦ X ++ Y ⟧ → Ctxt ⟦ X ⟧ × Ctxt ⟦ Y ⟧
  Ctxt-split {∅} {Y} E = ∅ , E
  Ctxt-split {A , X} {Y} (A′ , E) with Ctxt-split {X} {Y} E
  ... | Eˣ , Eʸ = ((A′ , Eˣ) , Eʸ)

  reify : {A : Type} {X : List Type} → X ⊢ A → (Ctxt ⟦ X ⟧ → ⟦ A ⟧)
  reify var         (x , ∅)   = x
  reify (abs t)     E         = λ x → reify t (x , E)
  reify (app s t)   E         with Ctxt-split E
  ... | Eˢ , Eᵗ               = (reify s Eˢ) (reify t Eᵗ)
  reify (pair s t)  E         with Ctxt-split E
  ... | Eˢ , Eᵗ               = (reify s Eˢ , reify t Eᵗ)
  reify (case s t)  E         with Ctxt-split E
  ... | Eˢ , Eᵗ               = case reify s Eˢ of λ{ (x , y) → reify t (x , y , Eᵗ)}
  reify (weak {X} s)    E         with Ctxt-split {X} E
  ... | Eˢ , Eᵗ               = reify s Eˢ
  reify (cont t)    (x , E)   = reify t (x , x , E)
  reify (exch {X} {Y} {Z} {W} t)    E         = reify t (Ctxt-exch {X} {Y} {Z} {W} E)

  [_] : {A : Type} {X : List Type} → X ⊢ A → (Ctxt ⟦ X ⟧ → ⟦ A ⟧)
  [_] = reify

  swap′ : ∀ {A B} → ⟦ A ⟧ × ⟦ B ⟧ → ⟦ B ⟧ × ⟦ A ⟧
  swap′ {A} {B} = [ swap {∅} {A} {B} ] ∅


module Coverage where

infixr 40 _::_
data List (A : Set) : Set where
  []   : List A
  _::_ : A -> List A -> List A

data D : Set where
  c1 : D -> D
  c2 : D
  c3 : D -> D -> D -> D
  c4 : D -> D -> D

f : D -> D -> D -> D -> List D
f (c3 a (c1 b) (c1 c2)) (c1 (c1 c)) d (c1 (c1 (c1 e))) = a :: b :: c :: d :: e :: []
f (c3 (c4 c2 a) (c1 b) (c1 c)) d (c1 (c1 e)) (c1 (c1 (c1 f))) = a :: b :: c :: d :: e :: f :: []
f a b (c1 c) (c3 d (c1 e) (c1 f)) = a :: b :: c :: d :: e :: f :: []
f (c3 (c4 a c2) b (c1 c)) (c1 d) (c1 (c1 (c1 e))) (c1 (c1 (c1 f))) = a :: b :: c :: d :: e :: f :: []
-- f (c3 a (c1 b) c) (c1 (c1 (c1 d))) (c1 (c1 (c1 e))) f = a :: b :: c :: d :: e :: f :: []
-- f a b (c1 (c1 c)) (c1 (c1 (c1 c2))) = a :: b :: c :: []
f a b c d = a :: b :: c :: d :: []


module BasicIS4.Metatheory.DyadicGentzen-TarskiGluedDyadicImplicit where

open import BasicIS4.Syntax.DyadicGentzen public
open import BasicIS4.Semantics.TarskiGluedDyadicImplicit public

open ImplicitSyntax (_⊢_) public

-- Soundness with respect to all models, or evaluation.

-- FIXME
postulate
  reify⋆ : ∀ {{_ : Model}} {Ξ Γ Δ} → Γ ⁏ Δ ⊩⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ

eval : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊨ A
eval (var i)           γ δ = lookup i γ
eval (lam t)           γ δ = λ ψ a → eval t (mono²⊩⋆ ψ γ , a) (mono²⊩⋆ ψ δ)
eval (app {A} {B} t u) γ δ = _⟪$⟫_ {A} {B} (eval t γ δ) (eval u γ δ)
eval (mvar i)          γ δ = mlookup i δ
eval (box t)           γ δ = λ ψ → let δ′ = mono²⊩⋆ ψ δ
                                    in  mmulticut (reify⋆ δ′) (box t) ⅋
                                          eval t ∙ δ′
eval (unbox t u)       γ δ = eval u γ (δ , λ ψ →
                               let γ′ = mono²⊩⋆ ψ γ
                                   δ′ = mono²⊩⋆ ψ δ
                               in  multicut² (reify⋆ γ′) (reify⋆ δ′) t ⅋
                                     ⟪↓⟫ (eval t γ′ δ′))
eval (pair t u)        γ δ = eval t γ δ , eval u γ δ
eval (fst t)           γ δ = π₁ (eval t γ δ)
eval (snd t)           γ δ = π₂ (eval t γ δ)
eval unit              γ δ = ∙

-- TODO: Correctness of evaluation with respect to conversion.

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { _⊩ᵅ_    = λ Π P → Π ⊢ α P
      ; mono²⊩ᵅ = mono²⊢
      }

-- Soundness and completeness with respect to the canonical model.

mutual
  reflectᶜ : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊩ A
  reflectᶜ {α P}   t = t
  reflectᶜ {A ▻ B} t = λ ψ → let t′ = mono²⊢ ψ t
                              in  λ a → reflectᶜ (app t′ (reifyᶜ a))
  reflectᶜ {□ A}   t = λ ψ → let t′ = mono²⊢ ψ t
                              in  t′ ⅋ reflectᶜ (down t′)
  reflectᶜ {A ∧ B} t = reflectᶜ (fst t) , reflectᶜ (snd t)
  reflectᶜ {⊤}    t = ∙

  reifyᶜ : ∀ {A Γ Δ} → Γ ⁏ Δ ⊩ A → Γ ⁏ Δ ⊢ A
  reifyᶜ {α P}   s = s
  reifyᶜ {A ▻ B} s = lam (reifyᶜ (s (weak⊆²₁) (reflectᶜ {A} v₀)))
  reifyᶜ {□ A}   s = syn (s refl⊆²)
  reifyᶜ {A ∧ B} s = pair (reifyᶜ (π₁ s)) (reifyᶜ (π₂ s))
  reifyᶜ {⊤}    s = unit

reflectᶜ⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ ⊩⋆ Ξ
reflectᶜ⋆ {∅}     ∙        = ∙
reflectᶜ⋆ {Ξ , A} (ts , t) = reflectᶜ⋆ ts , reflectᶜ t

reifyᶜ⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊩⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
reifyᶜ⋆ {∅}     ∙        = ∙
reifyᶜ⋆ {Ξ , A} (ts , t) = reifyᶜ⋆ ts , reifyᶜ t

-- Reflexivity and transitivity.

refl⊩⋆ : ∀ {Γ Δ} → Γ ⁏ Δ ⊩⋆ Γ
refl⊩⋆ = reflectᶜ⋆ refl⊢⋆

mrefl⊩⋆ : ∀ {Γ Δ} → Γ ⁏ Δ ⊩⋆ □⋆ Δ
mrefl⊩⋆ = reflectᶜ⋆ mrefl⊢⋆

trans⊩⋆ : ∀ {Γ Γ′ Δ Δ′ Ξ} → Γ ⁏ Δ ⊩⋆ Γ′ ⧺ (□⋆ Δ′) → Γ′ ⁏ Δ′ ⊩⋆ Ξ → Γ ⁏ Δ ⊩⋆ Ξ
trans⊩⋆ ts us = reflectᶜ⋆ (trans⊢⋆ (reifyᶜ⋆ ts) (reifyᶜ⋆ us))

-- Completeness with respect to all models, or quotation.

quot : ∀ {A Γ Δ} → Γ ⁏ Δ ⊨ A → Γ ⁏ Δ ⊢ A
quot s = reifyᶜ (s refl⊩⋆ mrefl⊩⋆)

-- Normalisation by evaluation.

norm : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A
norm = quot ∘ eval

-- TODO: Correctness of normalisation with respect to conversion.


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

module F1b where

open import Data.Unit
open import Data.Sum hiding (map; [_,_])
open import Data.Product hiding (map; ,_)
open import Function using (flip)
open import Relation.Binary.Core using (IsEquivalence; Reflexive; Symmetric; Transitive)
open import Relation.Binary

open import Groupoid

infixr 90 _⊗_
infixr 80 _⊕_
infixr 60 _∘_
infix  30 _⟷_

---------------------------------------------------------------------------
-- Paths

-- Path relation should be an equivalence
data Path {A : Set} : A → A → Set where
  _⇛_ : (x : A) → (y : A) → Path x y

data _≣⇛_ {A : Set} {a b : A} (x : Path a b) : Path a b → Set where
  refl⇛ : x ≣⇛ x

id⇛ : {A : Set} → (a : A) → Path a a
id⇛ a = a ⇛ a

ap : {A B : Set} → (f : A → B) → {a a' : A} → Path a a' → Path (f a) (f a')
ap f (a ⇛ a') = (f a) ⇛ (f a')

_∙⇛_ : {A : Set} {a b c : A} → Path b c → Path a b → Path a c
(b ⇛ c) ∙⇛ (a ⇛ .b) = a ⇛ c

_⇚ : {A : Set} {a b : A} → Path a b → Path b a
(x ⇛ y) ⇚ = y ⇛ x

lid⇛ : {A : Set} {x y : A} (α : Path x y) → (id⇛ y ∙⇛ α) ≣⇛ α
lid⇛ (x ⇛ y) = refl⇛

rid⇛ : {A : Set} {x y : A} (α : Path x y) → (α ∙⇛ id⇛ x) ≣⇛ α
rid⇛ (x ⇛ y) = refl⇛

assoc⇛ : {A : Set} {w x y z : A} (α : Path y z) (β : Path x y) (δ : Path w x) → ((α ∙⇛ β) ∙⇛ δ) ≣⇛ (α ∙⇛ (β ∙⇛ δ))
assoc⇛ (y ⇛ z) (x ⇛ .y) (w ⇛ .x) = refl⇛

l⇚ : {A : Set}  {x y : A} (α : Path x y) → ((α ⇚) ∙⇛ α) ≣⇛ id⇛ x
l⇚ (x ⇛ y) = refl⇛

r⇚ : {A : Set} {x y : A} (α : Path x y) → (α ∙⇛ (α ⇚)) ≣⇛ id⇛ y
r⇚ (x ⇛ y) = refl⇛

sym⇛ : {A : Set} {x y : A} {α β : Path x y} → α ≣⇛ β → β ≣⇛ α
sym⇛ refl⇛ = refl⇛

trans⇛ : {A : Set} {x y : A} {α β δ : Path x y} → α ≣⇛ β → β ≣⇛ δ → α ≣⇛ δ
trans⇛ refl⇛ refl⇛ = refl⇛

equiv≣⇛ : {A : Set} {x y : A} → IsEquivalence {_} {_} {Path x y} (_≣⇛_)
equiv≣⇛ = record { refl = refl⇛; sym = sym⇛; trans = trans⇛ }

resp≣⇛ : {A : Set} {x y z : A} {f h : Path y z} {g i : Path x y} →
  f ≣⇛ h → g ≣⇛ i → (f ∙⇛ g) ≣⇛ (h ∙⇛ i)
resp≣⇛ refl⇛ refl⇛ = refl⇛

------------------------------------------------------------------------------
-- pi types with exactly one level of reciprocals

data B0 : Set where
  ONE    : B0
  PLUS0  : B0 → B0 → B0
  TIMES0 : B0 → B0 → B0

-- interpretation of B0 as discrete groupoids

record 0-type : Set₁ where
  constructor G₀
  field
    ∣_∣₀ : Set

open 0-type public

plus : 0-type → 0-type → 0-type
plus t₁ t₂ = G₀ (∣ t₁ ∣₀ ⊎ ∣ t₂ ∣₀)

times : 0-type → 0-type → 0-type
times t₁ t₂ = G₀ (∣ t₁ ∣₀ × ∣ t₂ ∣₀)

⟦_⟧₀ : B0 → 0-type
⟦ ONE ⟧₀ = G₀ ⊤
⟦ PLUS0 b₁ b₂ ⟧₀ = plus ⟦ b₁ ⟧₀ ⟦ b₂ ⟧₀
⟦ TIMES0 b₁ b₂ ⟧₀ = times ⟦ b₁ ⟧₀ ⟦ b₂ ⟧₀

ı₀ : B0 → Set
ı₀ b = ∣ ⟦ b ⟧₀ ∣₀

point : (b : B0) → ı₀ b
point ONE = tt
point (PLUS0 b _) = inj₁ (point b)
point (TIMES0 b₀ b₁) = point b₀ , point b₁

-- isos

data _⟷_ : B0 → B0 → Set where
  -- +
  swap₊   : { b₁ b₂ : B0 } → PLUS0 b₁ b₂ ⟷ PLUS0 b₂ b₁
  assocl₊ : { b₁ b₂ b₃ : B0 } → PLUS0 b₁ (PLUS0 b₂ b₃) ⟷ PLUS0 (PLUS0 b₁ b₂) b₃
  assocr₊ : { b₁ b₂ b₃ : B0 } → PLUS0 (PLUS0 b₁ b₂) b₃ ⟷ PLUS0 b₁ (PLUS0 b₂ b₃)
  -- *
  unite⋆  : { b : B0 } → TIMES0 ONE b ⟷ b
  uniti⋆  : { b : B0 } → b ⟷ TIMES0 ONE b
  swap⋆   : { b₁ b₂ : B0 } → TIMES0 b₁ b₂ ⟷ TIMES0 b₂ b₁
  assocl⋆ : { b₁ b₂ b₃ : B0 } → TIMES0 b₁ (TIMES0 b₂ b₃) ⟷ TIMES0 (TIMES0 b₁ b₂) b₃
  assocr⋆ : { b₁ b₂ b₃ : B0 } → TIMES0 (TIMES0 b₁ b₂) b₃ ⟷ TIMES0 b₁ (TIMES0 b₂ b₃)
  -- * distributes over +
  dist    : { b₁ b₂ b₃ : B0 } →
            TIMES0 (PLUS0 b₁ b₂) b₃ ⟷ PLUS0 (TIMES0 b₁ b₃) (TIMES0 b₂ b₃)
  factor  : { b₁ b₂ b₃ : B0 } →
            PLUS0 (TIMES0 b₁ b₃) (TIMES0 b₂ b₃) ⟷ TIMES0 (PLUS0 b₁ b₂) b₃
  -- congruence
  id⟷   : { b : B0 } → b ⟷ b
  sym    : { b₁ b₂ : B0 } → (b₁ ⟷ b₂) → (b₂ ⟷ b₁)
  _∘_    : { b₁ b₂ b₃ : B0 } → (b₁ ⟷ b₂) → (b₂ ⟷ b₃) → (b₁ ⟷ b₃)
  _⊕_    : { b₁ b₂ b₃ b₄ : B0 } →
           (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (PLUS0 b₁ b₂ ⟷ PLUS0 b₃ b₄)
  _⊗_    : { b₁ b₂ b₃ b₄ : B0 } →
           (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (TIMES0 b₁ b₂ ⟷ TIMES0 b₃ b₄)
{--
--}

-- interpret isos as paths

eval0 : {b₁ b₂ : B0} → (c : b₁ ⟷ b₂) → (x : ı₀ b₁) → ı₀ b₂
eval0 _ = {!!}

--comb0 : {b₁ b₂ : B0} → (c : b₁ ⟷ b₂) → (x : ı₀ b₁) → Path x (eval0 c x)
--comb0 _ = ?

------------------------------------------------------------------------------
-- interpretation of B1 types as 1-groupoids

data B1 : Set where
  LIFT0  : B0 → B1
  PLUS1  : B1 → B1 → B1
  TIMES1 : B1 → B1 → B1
  RECIP1 : B0 → B1

open 1Groupoid

allPaths : Set → 1Groupoid
allPaths a =  record
    { set = a
    ; _↝_ = Path
    ; _≈_ = _≣⇛_
    ; id = λ {x} → id⇛ x
    ; _∘_ = _∙⇛_
    ; _⁻¹ = _⇚
    ; lneutr = lid⇛
    ; rneutr = rid⇛
    ; assoc = assoc⇛
    ; linv = l⇚
    ; rinv = r⇚
    ; equiv = equiv≣⇛
    ;  ∘-resp-≈ = resp≣⇛}

⟦_⟧₁ : B1 → 1Groupoid
⟦ LIFT0 b0 ⟧₁ = discrete (ı₀ b0)
⟦ PLUS1 b₁ b₂ ⟧₁ = ⟦ b₁ ⟧₁ ⊎G ⟦ b₂ ⟧₁
⟦ TIMES1 b₁ b₂ ⟧₁ =  ⟦ b₁ ⟧₁ ×G ⟦ b₂ ⟧₁
⟦ RECIP1 b0 ⟧₁ = allPaths (ı₀ b0)

ı₁ : B1 → Set
ı₁ b = set ⟦ b ⟧₁

test10 = ⟦ LIFT0 ONE ⟧₁
test11 = ⟦ LIFT0 (PLUS0 ONE ONE) ⟧₁
test12 = ⟦ RECIP1 (PLUS0 ONE ONE) ⟧₁

-- interpret isos as functors

data _⟷₁_ : B1 → B1 → Set where
  -- +
  swap₊   : { b₁ b₂ : B1 } → PLUS1 b₁ b₂ ⟷₁ PLUS1 b₂ b₁
{--
  assocl₊ : { b₁ b₂ b₃ : B1 } → PLUS b₁ (PLUS b₂ b₃) ⟷₁ PLUS (PLUS b₁ b₂) b₃
  assocr₊ : { b₁ b₂ b₃ : B1 } → PLUS (PLUS b₁ b₂) b₃ ⟷₁ PLUS b₁ (PLUS b₂ b₃)
  -- *
--}
  unite⋆  : { b : B1 } → TIMES1 (LIFT0 ONE) b ⟷₁ b
  uniti⋆  : { b : B1 } → b ⟷₁ TIMES1 (LIFT0 ONE) b
{--
  swap⋆   : { b₁ b₂ : B } → TIMES b₁ b₂ ⟷₁ TIMES b₂ b₁
  assocl⋆ : { b₁ b₂ b₃ : B } → TIMES b₁ (TIMES b₂ b₃) ⟷₁ TIMES (TIMES b₁ b₂) b₃
  assocr⋆ : { b₁ b₂ b₃ : B } → TIMES (TIMES b₁ b₂) b₃ ⟷₁ TIMES b₁ (TIMES b₂ b₃)
  -- * distributes over +
  dist    : { b₁ b₂ b₃ : B } →
            TIMES (PLUS b₁ b₂) b₃ ⟷₁ PLUS (TIMES b₁ b₃) (TIMES b₂ b₃)
  factor  : { b₁ b₂ b₃ : B } →
            PLUS (TIMES b₁ b₃) (TIMES b₂ b₃) ⟷₁ TIMES (PLUS b₁ b₂) b₃
  -- congruence
  id⟷₁   : { b : B } → b ⟷₁ b
  sym    : { b₁ b₂ : B } → (b₁ ⟷₁ b₂) → (b₂ ⟷₁ b₁)
  _∘_    : { b₁ b₂ b₃ : B } → (b₁ ⟷₁ b₂) → (b₂ ⟷₁ b₃) → (b₁ ⟷₁ b₃)
  _⊕_    : { b₁ b₂ b₃ b₄ : B } →
           (b₁ ⟷₁ b₃) → (b₂ ⟷₁ b₄) → (PLUS b₁ b₂ ⟷₁ PLUS b₃ b₄)
  _⊗_    : { b₁ b₂ b₃ b₄ : B } →
           (b₁ ⟷₁ b₃) → (b₂ ⟷₁ b₄) → (TIMES b₁ b₂ ⟷₁ TIMES b₃ b₄)
  η⋆ : (b : B0) → LIFT0 ONE ⟷₁ TIMES1 (LIFT0 b) (RECIP1 b)
  ε⋆ : (b : B0) → TIMES1 (LIFT0 b) (RECIP1 b) ⟷₁ LIFT0 ONE
--}

record 1-functor (A B : 1Groupoid) : Set where
  constructor 1F
  private module A = 1Groupoid A
  private module B = 1Groupoid B

  field
    F₀ : set A → set B
    F₁ : ∀ {X Y : set A} → A [ X , Y ] → B [ F₀ X , F₀ Y ]
    -- identity : ∀ {X} → B._≈_ (F₁ (A.id {X})) B.id
    -- F-resp-≈ : ∀ {X Y} {F G : A [ X , Y ]} → A._≈_ F G → B._≈_ (F₁ F) (F₁ G)

open 1-functor public

ipath : (b : B1) → ı₁ b → ı₁ b → Set
ipath b x y = Path {ı₁ b} x y

swap⊎ : {A B : Set} → A ⊎ B → B ⊎ A
swap⊎ (inj₁ a) = inj₂ a
swap⊎ (inj₂ b) = inj₁ b

intro1⋆ : {b : B1} {x y : ı₁ b} → ipath b x y → ipath (TIMES1 (LIFT0 ONE) b) (tt , x) (tt , y)
intro1⋆ (y ⇛ z) = (tt , y) ⇛ (tt , z)

objη⋆ : (b : B0) → ı₁ (LIFT0 ONE) → ı₁ (TIMES1 (LIFT0 b) (RECIP1 b))
objη⋆ b tt = point b , point b

objε⋆ : (b : B0) → ı₁ (TIMES1 (LIFT0 b) (RECIP1 b)) → ı₁ (LIFT0 ONE)
objε⋆ b (x , y) = tt

elim1∣₁ : (b : B1) → ı₁ (TIMES1 (LIFT0 ONE) b) → ı₁ b
elim1∣₁ b (tt , x) = x

intro1∣₁ : (b : B1) → ı₁ b → ı₁ (TIMES1 (LIFT0 ONE) b)
intro1∣₁ b x = (tt , x)

swapF : {b₁ b₂ : B1} →
      let G = ⟦ b₁ ⟧₁ ⊎G ⟦ b₂ ⟧₁
          G' = ⟦ b₂ ⟧₁ ⊎G ⟦ b₁ ⟧₁ in
      {X Y : set G} → G [ X , Y ] → G' [ swap⊎ X , swap⊎ Y ]
swapF {X = inj₁ _} {inj₁ _} f = f
swapF {X = inj₁ _} {inj₂ _} ()
swapF {X = inj₂ _} {inj₁ _} ()
swapF {X = inj₂ _} {inj₂ _} f = f

{-
eta : (b : B0) → List (ipath (LIFT0 ONE)) → List (ipath (TIMES1 (LIFT0 b) (RECIP1 b)))
-- note how the input list is not used at all!
eta b _ = prod (λ a a' → _↝_ (a , tt) (a' , tt)) (elems0 b) (elems0 b)

eps : (b : B0) → ipath (TIMES1 (LIFT0 b) (RECIP1 b)) → ipath (LIFT0 ONE)
eps b0 (a ⇛ b) = tt ⇛ tt
-}

Funite⋆ : {b₁ : B1} → ∀ {X Y : set (discrete (ı₀ ONE) ×G ⟦ b₁ ⟧₁)} →  DPath (proj₁ X) (proj₁ Y) ×  (_↝_ ⟦ b₁ ⟧₁) (proj₂ X) (proj₂ Y) →  _↝_ ⟦ b₁ ⟧₁ (proj₂ X) (proj₂ Y)
Funite⋆ {b₁} {tt , _} {tt , _} (reflD , y) = y

Funiti⋆ : {b₁ : B1} → ∀ {X Y : set (discrete (ı₀ ONE) ×G ⟦ b₁ ⟧₁)} →  _↝_ ⟦ b₁ ⟧₁ (proj₂ X) (proj₂ Y) → DPath (proj₁ X) (proj₁ Y) ×  (_↝_ ⟦ b₁ ⟧₁) (proj₂ X) (proj₂ Y)
Funiti⋆ y = reflD , y

mutual
  eval : {b₁ b₂ : B1} → (b₁ ⟷₁ b₂) → 1-functor ⟦ b₁ ⟧₁ ⟦ b₂ ⟧₁
  eval (swap₊ {b₁} {b₂}) = 1F swap⊎ (λ {X Y} → swapF {b₁} {b₂} {X} {Y})
  eval (unite⋆ {b}) = 1F (elim1∣₁ b) (Funite⋆ {b})
  eval (uniti⋆ {b}) = 1F (intro1∣₁ b) (Funiti⋆ {b})
--  eval (η⋆ b) = F₁ (objη⋆ b) (eta b )
--  eval (ε⋆ b) = F₁ (objε⋆ b) (map (eps b))

  evalB : {b₁ b₂ : B1} → (b₁ ⟷₁ b₂) → 1-functor ⟦ b₂ ⟧₁ ⟦ b₁ ⟧₁
  evalB (swap₊ {b₁} {b₂}) = 1F swap⊎ ((λ {X Y} → swapF {b₂} {b₁} {X} {Y}))
  evalB (unite⋆ {b}) = 1F (intro1∣₁ b) (Funiti⋆ {b})
  evalB (uniti⋆ {b}) = 1F (elim1∣₁ b) (Funite⋆ {b})
--  evalB (η⋆ b) = F₁ (objε⋆ b) (map (eps b))
--  evalB (ε⋆ b) = F₁ (objη⋆ b) (eta b)

{- eval assocl₊ = ? -- : { b₁ b₂ b₃ : B } → PLUS b₁ (PLUS b₂ b₃) ⟷₁ PLUS (PLUS b₁ b₂) b₃
eval assocr₊ = ? -- : { b₁ b₂ b₃ : B } → PLUS (PLUS b₁ b₂) b₃ ⟷₁ PLUS b₁ (PLUS b₂ b₃)
eval uniti⋆ = ? -- : { b : B } → b ⟷₁ TIMES ONE b
eval swap⋆ = ? --  : { b₁ b₂ : B } → TIMES b₁ b₂ ⟷₁ TIMES b₂ b₁
eval assocl⋆ = ? -- : { b₁ b₂ b₃ : B } → TIMES b₁ (TIMES b₂ b₃) ⟷₁ TIMES (TIMES b₁ b₂) b₃
eval assocr⋆ = ? -- : { b₁ b₂ b₃ : B } → TIMES (TIMES b₁ b₂) b₃ ⟷₁ TIMES b₁ (TIMES b₂ b₃)
eval dist = ? -- : { b₁ b₂ b₃ : B } → TIMES (PLUS b₁ b₂) b₃ ⟷₁ PLUS (TIMES b₁ b₃) (TIMES b₂ b₃)
eval factor = ? -- : { b₁ b₂ b₃ : B } → PLUS (TIMES b₁ b₃) (TIMES b₂ b₃) ⟷₁ TIMES (PLUS b₁ b₂) b₃
eval id⟷₁ = ? --  : { b : B } → b ⟷₁ b
eval (sym c) = ? -- : { b₁ b₂ : B } → (b₁ ⟷₁ b₂) → (b₂ ⟷₁ b₁)
eval (c₁ ∘ c₂) = ? -- : { b₁ b₂ b₃ : B } → (b₁ ⟷₁ b₂) → (b₂ ⟷₁ b₃) → (b₁ ⟷₁ b₃)
eval (c₁ ⊕ c₂) = ? -- : { b₁ b₂ b₃ b₄ : B } → (b₁ ⟷₁ b₃) → (b₂ ⟷₁ b₄) → (PLUS b₁ b₂ ⟷₁ PLUS b₃ b₄)
eval (c₁ ⊗ c₂) = ? -- : { b₁ b₂ b₃ b₄ : B } → (b₁ ⟷₁ b₃) → (b₂ ⟷₁ b₄) → (TIMES b₁ b₂ ⟷₁ TIMES b₃ b₄)

---------------------------------------------------------------------------
--}


{-# OPTIONS --without-K --exact-split --allow-unsolved-metas #-}

module 14-image where

import 13-propositional-truncation
open 13-propositional-truncation public

{- We introduce the image inclusion of a map. -}

precomp-emb :
  { l1 l2 l3 l4 : Level} {X : UU l1} {A : UU l2} (f : A → X)
  {B : UU l3} ( i : B ↪ X) (q : hom-slice f (map-emb i)) →
  {C : UU l4} ( j : C ↪ X) (r : hom-slice (map-emb i) (map-emb j)) →
  hom-slice f (map-emb j)
precomp-emb f i q j r =
  pair
    ( ( map-hom-slice (map-emb i) (map-emb j) r) ∘
      ( map-hom-slice f (map-emb i) q))
    ( ( triangle-hom-slice f (map-emb i) q) ∙h
      ( ( triangle-hom-slice (map-emb i) (map-emb j) r) ·r
        ( map-hom-slice f (map-emb i) q)))

is-prop-hom-slice :
  { l1 l2 l3 : Level} {X : UU l1} {A : UU l2} (f : A → X) →
  { B : UU l3} (i : B ↪ X) → is-prop (hom-slice f (map-emb i))
is-prop-hom-slice {X = X} f i =
  is-prop-is-equiv
    ( (x : X) → fib f x → fib (map-emb i) x)
    ( fiberwise-hom-hom-slice f (map-emb i))
    ( is-equiv-fiberwise-hom-hom-slice f (map-emb i))
    ( is-prop-Π
      ( λ x → is-prop-Π
        ( λ p → is-prop-map-is-emb (map-emb i) (is-emb-map-emb i) x)))

universal-property-image :
  ( l : Level) {l1 l2 l3 : Level} {X : UU l1} {A : UU l2} (f : A → X) →
  { B : UU l3} (i : B ↪ X) (q : hom-slice f (map-emb i)) →
  UU (lsuc l ⊔ l1 ⊔ l2 ⊔ l3)
universal-property-image l {X = X} f i q =
  ( C : UU l) (j : C ↪ X) → is-equiv (precomp-emb f i q j)

universal-property-image' :
  ( l : Level) {l1 l2 l3 : Level} {X : UU l1} {A : UU l2} (f : A → X) →
  { B : UU l3} (i : B ↪ X) (q : hom-slice f (map-emb i)) →
  UU (lsuc l ⊔ l1 ⊔ l2 ⊔ l3)
universal-property-image' l {X = X} f i q =
  ( C : UU l) (j : C ↪ X) →
    hom-slice f (map-emb j) → hom-slice (map-emb i) (map-emb j)

universal-property-image-universal-property-image' :
  ( l : Level) {l1 l2 l3 : Level} {X : UU l1} {A : UU l2} (f : A → X) →
  { B : UU l3} (i : B ↪ X) (q : hom-slice f (map-emb i)) →
  universal-property-image' l f i q → universal-property-image l f i q
universal-property-image-universal-property-image' l f i q up' C j =
  is-equiv-is-prop
    ( is-prop-hom-slice (map-emb i) j)
    ( is-prop-hom-slice f j)
    ( up' C j)

-- Section 13.4

is-small :
  (l : Level) {l1 : Level} (A : UU l1) → UU (lsuc l ⊔ l1)
is-small l A = Σ (UU l) (λ X → A ≃ X)

is-small-map :
  (l : Level) {l1 l2 : Level} {A : UU l1} {B : UU l2} →
  (A → B) → UU (lsuc l ⊔ (l1 ⊔ l2))
is-small-map l {B = B} f = (b : B) → is-small l (fib f b)

is-locally-small :
  (l : Level) {l1 : Level} (A : UU l1) → UU (lsuc l ⊔ l1)
is-locally-small l A = (x y : A) → is-small l (Id x y)

total-subtype :
  {l1 l2 : Level} {A : UU l1} (P : A → UU-Prop l2) → UU (l1 ⊔ l2)
total-subtype {A = A} P = Σ A (λ x → pr1 (P x))

equiv-subtype-equiv :
  {l1 l2 l3 l4 : Level}
  {A : UU l1} {B : UU l2} (e : A ≃ B)
  (C : A → UU-Prop l3) (D : B → UU-Prop l4) →
  ((x : A) → (C x) ↔ (D (map-equiv e x))) →
  total-subtype C ≃ total-subtype D
equiv-subtype-equiv e C D H =
  equiv-toto (λ y → type-Prop (D y)) e
    ( λ x → equiv-iff (C x) (D (map-equiv e x)) (H x))

equiv-comp-equiv' :
  {l1 l2 l3 : Level} {A : UU l1} {B : UU l2} →
  (A ≃ B) → (C : UU l3) → (B ≃ C) ≃ (A ≃ C)
equiv-comp-equiv' e C =
  equiv-subtype-equiv
    ( equiv-precomp-equiv e C)
    ( is-equiv-Prop)
    ( is-equiv-Prop)
    ( λ g →
      pair
        ( is-equiv-comp' g (map-equiv e) (is-equiv-map-equiv e))
        ( λ is-equiv-eg →
          is-equiv-left-factor'
            g (map-equiv e) is-equiv-eg (is-equiv-map-equiv e)))

is-prop-is-small :
  (l : Level) {l1 : Level} (A : UU l1) → is-prop (is-small l A)
is-prop-is-small l A =
  is-prop-is-contr-if-inh
    ( λ Xe →
      is-contr-equiv'
        ( Σ (UU l) (λ Y → (pr1 Xe) ≃ Y))
        ( equiv-tot ((λ Y → equiv-comp-equiv' (pr2 Xe) Y)))
        ( is-contr-total-equiv (pr1 Xe)))

is-prop-is-locally-small :
  (l : Level) {l1 : Level} (A : UU l1) → is-prop (is-locally-small l A)
is-prop-is-locally-small l A =
  is-prop-Π (λ x → is-prop-Π (λ y → is-prop-is-small l (Id x y)))


module _ where

module First where
  postulate
    C  : Set → Set
    it : {A : Set} ⦃ _ : C A ⦄ → A
    X  : Set

module Second where
  open First
  postulate instance iCX : C X

module Nested where
  open First
  x : X
  x = it   -- Second.iCX is in scope


module Esterel.CompletionCode where

open import Data.Nat
  using (ℕ ; zero ; suc) renaming (_≟_ to _≟ℕ_ ; _⊔_ to _⊔ℕ_ ; _≤_ to _≤N_ ; _≤?_ to _≤?N_)
open import Data.Nat.Properties
  using (⊔-⊓-isCommutativeSemiringWithoutOne)
open import Function
  using (_∘_)
open import Relation.Nullary
  using (Dec ; yes ; no)
open import Relation.Binary
  using (Decidable)
open import Relation.Binary.PropositionalEquality
  using (_≡_ ; refl ; cong)
import Level
import Relation.Binary
open import Data.List using (List ; _∷_ ; [] ; _++_)
open import Data.List.Any.Properties using (++⁻)
  renaming (++⁺ˡ to ++ˡ ; ++⁺ʳ to ++ʳ)
open import Data.Sum using (_⊎_ ; inj₁ ; inj₂)
open import Data.Empty using (⊥-elim)
import Data.Bool
open import Algebra.Structures
  using (IsCommutativeSemiringWithoutOne ; IsCommutativeMonoid)

open import utility

data CompletionCode : Set where
  nothin : CompletionCode
  pause  : CompletionCode
  exit   : ℕ → CompletionCode

↓* : CompletionCode → CompletionCode
↓* nothin         = nothin
↓* pause          = pause
↓* (exit zero)    = nothin
↓* (exit (suc n)) = exit n

exit-injective : ∀{n m} → exit n ≡ exit m → n ≡ m
exit-injective refl = refl

_≟_ : Decidable {A = CompletionCode} _≡_
nothin ≟ nothin = yes refl
nothin ≟ pause  = no λ()
nothin ≟ exit _ = no λ()
pause  ≟ nothin = no λ()
pause  ≟ pause  = yes refl
pause  ≟ exit _ = no λ()
exit _ ≟ nothin = no λ()
exit _ ≟ pause  = no λ()
exit n ≟ exit m with n ≟ℕ m
... | yes n≡m = yes (cong exit n≡m)
... | no ¬n≡m = no (¬n≡m ∘ exit-injective)

open ListSet _≟_

_⊔_ : CompletionCode → CompletionCode → CompletionCode
nothin ⊔ r      = r
pause  ⊔ nothin = pause
pause  ⊔ r      = r
exit n ⊔ nothin = exit n
exit n ⊔ pause  = exit n
exit n ⊔ exit m = exit (n ⊔ℕ m)

⊔-comm : ∀ c₁ c₂ → c₁ ⊔ c₂ ≡ c₂ ⊔ c₁
⊔-comm nothin   nothin   = refl
⊔-comm nothin   pause    = refl
⊔-comm nothin   (exit m) = refl
⊔-comm pause    nothin   = refl
⊔-comm pause    pause    = refl
⊔-comm pause    (exit m) = refl
⊔-comm (exit n) nothin   = refl
⊔-comm (exit n) pause    = refl
⊔-comm (exit n) (exit m)
  rewrite IsCommutativeMonoid.comm
            (IsCommutativeSemiringWithoutOne.+-isCommutativeMonoid
              ⊔-⊓-isCommutativeSemiringWithoutOne) n m
  = refl

data _≤_ : Relation.Binary.Rel CompletionCode Level.zero where
  nothin≤c     : ∀ {c}                 -> nothin ≤ c
  pause≤pause  :                          pause  ≤ pause
  pause≤exit   : ∀ {n}                 -> pause  ≤ exit n
  exit≤exit    : ∀ {n} {m} -> (n ≤N m) -> exit n ≤ exit m

_≤?_ : Decidable _≤_
nothin ≤? c2 = yes nothin≤c
pause ≤? nothin = no (λ ())
pause ≤? pause = yes pause≤pause
pause ≤? exit x = yes pause≤exit
exit n ≤? nothin = no (λ ())
exit n ≤? pause = no (λ ())
exit n ≤? exit m with n ≤?N m
exit n ≤? exit m | yes n≤m = yes (exit≤exit n≤m)
exit n ≤? exit m | no ¬n≤m = no ¬≤ where
  ¬≤ : Relation.Nullary.¬ (exit n ≤ exit m)
  ¬≤ (exit≤exit n) = ¬n≤m n

codessub : (List CompletionCode) → (List CompletionCode) → Set
codessub codes' codes = (∀ a → a ∈ codes' → a ∈ codes)
codesub++ll : ∀{a b c} →  codessub a b → codessub a (b ++ c)
codesub++ll sub a a∈ = ++ˡ (sub a a∈)

codesub++both : ∀{a b c d} →  codessub a c → codessub b d → codessub (a ++ b) (c ++ d)
codesub++both{a}{b}{c}{d} a⊂c b⊂d z z∈ with ++⁻ a z∈
... | inj₁ z∈1 = ++ˡ (a⊂c z z∈1)
... | inj₂ z∈2 = ++ʳ c (b⊂d z z∈2)

codesub- : ∀{a b} z → codessub a b → codessub (set-remove a z) (set-remove b z)
codesub-{a}{b} z a⊂b x x∈ with z ≟ x
... | yes refl = ⊥-elim (set-remove-removed{x}{a} x∈)
... | no ¬refl = set-remove-not-removed ¬refl (a⊂b x (set-remove-mono-∈ z x∈))


-- Andreas, 2016-10-01, issue #2231
-- The termination checker should not always see through abstract definitions.

abstract
  data Nat : Set where
    zero' : Nat
    suc'  : Nat → Nat

  -- abstract hides constructor nature of zero and suc.
  zero = zero'
  suc  = suc'

data D : Nat → Set where
  c1 : ∀ n → D n → D (suc n)
  c2 : ∀ n → D n → D n

-- To see that this is terminating the termination checker has to look at the
-- natural number index, which is in a dot pattern.
f : ∀ n → D n → Nat
f .(suc n) (c1  n d) = f n (c2 n (c2 n d))
f n        (c2 .n d) = f n d

-- Termination checking based on dot patterns should fail,
-- since suc is abstract.


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

module Cubical.Categories.Structures where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels using (isSetΣ)
open import Cubical.Data.Sigma

open import Cubical.Categories.Category

_∋_ : ∀ {ℓ} → (A : Type ℓ) → A → A
_∋_ A x = x

private
  variable
    ℓ ℓ' ℓ'' ℓ''' : Level
    𝒞 : Precategory ℓ ℓ'

record StructureOver (𝒞 : Precategory ℓ ℓ') ℓ'' ℓ''' : Type (ℓ-suc (ℓ-max (ℓ-max ℓ ℓ') (ℓ-max ℓ'' ℓ'''))) where
  field
    Struct : 𝒞 .ob → Type ℓ''
    IsHomo : ∀ {x y} → Struct x → Struct y → 𝒞 .hom x y → Type ℓ'''
    isPropIsHomo : ∀ {x y α β f} → isProp (IsHomo {x} {y} α β f)

    idnIsHomo : ∀ {x} (α : Struct x) → IsHomo α α (𝒞 .idn x)
    homoSeq : ∀ {x y z α β γ} {f : 𝒞 .hom x y} {g : 𝒞 .hom y z} →
                IsHomo α β f → IsHomo β γ g → IsHomo α γ (𝒞 .seq f g)

  _⊆_ : ∀ {x} → Struct x → Struct x → Type ℓ'''
  _⊆_ {x} α β = IsHomo α β (𝒞 .idn x)

open StructureOver public renaming (_⊆_ to _[_⊆_])

record StrIsUnivalent {𝒞 : Precategory ℓ ℓ'} (S : StructureOver 𝒞 ℓ'' ℓ''') : Type (ℓ-suc (ℓ-max (ℓ-max ℓ ℓ') (ℓ-max ℓ'' ℓ'''))) where
  field
    subAntisym : ∀ {x} {α β : S .Struct x} → S [ α ⊆ β ] → S [ β ⊆ α ] → α ≡ β

open StrIsUnivalent public

isPropP : {A : I → Type ℓ} {x : A i0} {y : A i1} → isProp (A i1) → PathP A x y
isPropP Aprop = toPathP (Aprop _ _)

STRUCTURES : (𝒞 : Precategory ℓ ℓ') → StructureOver 𝒞 ℓ'' ℓ''' → Precategory (ℓ-max ℓ ℓ'') (ℓ-max ℓ' ℓ''')
STRUCTURES 𝒞 S .ob = Σ (𝒞 .ob) (S .Struct)
STRUCTURES 𝒞 S .hom (x , α) (y , β) = Σ (𝒞 .hom x y) (S .IsHomo α β)
STRUCTURES 𝒞 S .idn (x , α) = 𝒞 .idn x , S .idnIsHomo α
STRUCTURES 𝒞 S .seq (f , hᶠ) (g , hᵍ) = 𝒞 .seq f g , S .homoSeq hᶠ hᵍ
STRUCTURES 𝒞 S .seq-λ (f , hᶠ) = cong₂ _,_ (𝒞 .seq-λ f) (toPathP (S .isPropIsHomo _ _))
STRUCTURES 𝒞 S .seq-ρ (f , hᶠ) = cong₂ _,_ (𝒞 .seq-ρ f) (toPathP (S .isPropIsHomo _ _))
STRUCTURES 𝒞 S .seq-α (f , hᶠ) (g , hᵍ) (h , hʰ) = cong₂ _,_ (𝒞 .seq-α f g h) (toPathP (S .isPropIsHomo _ _))

STRUCTURESisCat : ⦃ 𝒞-cat : isCategory 𝒞 ⦄ {S : StructureOver 𝒞 ℓ'' ℓ'''} → isCategory (STRUCTURES 𝒞 S)
STRUCTURESisCat {{𝒞-cat}} {S = S} .homIsSet {x = x , α} {y = y , β} = isSetΣ (𝒞-cat .homIsSet) (λ _ → isProp→isSet (S .isPropIsHomo))

SIP : ∀ ⦃ 𝒞-cat : isCategory 𝒞 ⦄ ⦃ 𝒞-univ : isUnivalent 𝒞 ⦄ → (S : StructureOver 𝒞 ℓ'' ℓ''') → ⦃ S-univ : StrIsUnivalent S ⦄ → isUnivalent (STRUCTURES 𝒞 S)
SIP {𝒞 = 𝒞} {{𝒞-cat}} {{𝒞-univ}} S {{S-univ}} .univ (x , α) (y , β) = isoToIsEquiv (iso (pathToIso (x , α) (y , β)) catisoToPath sect ret)
  where
    catisoToPath : ∀ {x y : 𝒞 .ob} {α : S .Struct x} {β : S .Struct y} → CatIso (STRUCTURES 𝒞 S) (x , α) (y , β) → (x , α) ≡ (y , β)
    catisoToPath {x = x} {y} {α} {β} (catiso (f , hᶠ) (g , hᵍ) sec ret) = ΣPathTransport→PathΣ _ _ (x≡y , α≡β x≡y α β (subst (S .IsHomo α β) f≡h hᶠ) (subst (S .IsHomo β α) g≡h⁻¹ hᵍ))
      where
        x≅y : CatIso 𝒞 x y
        x≅y = catiso f g (cong fst sec) (cong fst ret)

        x≡y : x ≡ y
        x≡y = uva 𝒞-univ x≅y

        f≡h : f ≡ pathMor 𝒞 x≡y
        f≡h = sym (uvaPathMor 𝒞-univ x≅y)

        g≡h⁻¹ : g ≡ pathMor⁻ 𝒞 x≡y
        g≡h⁻¹ = sym (uvaPathMor⁻ 𝒞-univ x≅y)

        α≡β : ∀ {x y : 𝒞 .ob} (p : x ≡ y)
                (α : S .Struct x) (β : S .Struct y)
                (h⇒ : S .IsHomo α β (pathMor 𝒞 p)) (h⇐ : S .IsHomo β α (pathMor⁻ 𝒞 p))
                → subst (S .Struct) p α ≡ β
        α≡β {x} {y} =
          J (λ z q → ∀ (α : S .Struct x) (β : S .Struct z) (h⇒ : S .IsHomo α β (pathMor 𝒞 q)) (h⇐ : S .IsHomo β α (pathMor⁻ 𝒞 q)) → subst (S .Struct) q α ≡ β)
          (λ α β h⇒ h⇐ → transportRefl α ∙ subAntisym S-univ (subst (S .IsHomo α β) (pathMorRefl {𝒞 = 𝒞}) h⇒) (subst (S .IsHomo β α) (pathMor⁻Refl {𝒞 = 𝒞}) h⇐))

    sect : section (pathToIso (x , α) (y , β)) catisoToPath
    sect (catiso h h⁻¹ sec ret) = {!   !}

    ret : retract (pathToIso (x , α) (y , β)) catisoToPath
    ret = J (λ z q → catisoToPath (pathToIso (x , α) z q) ≡ q)
      (subst {y = (pathToIso (x , α) (x , α) refl)} (λ x → catisoToPath x ≡ refl) (sym (JRefl (λ z _ → (x , α) ≅ z) ?)))


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

module PointedPi where

open import Level using (_⊔_) renaming (zero to l0; suc to lsuc)
open import Universe using (Universe)

open import Categories.Category using (Category)
open import Categories.Groupoid using (Groupoid)
open import Categories.Functor using (Functor)

open import Data.Empty using (⊥)
open import Data.Unit using (⊤; tt)
open import Data.Sum hiding ([_,_])
open import Data.Product
open import Relation.Binary.PropositionalEquality as P
open import Function using (flip)
open import Data.Nat using (ℕ; zero; suc)
open import Data.Nat.Properties.Simple using (+-right-identity)
open import Data.Integer using (ℤ;+_;-[1+_]) renaming (suc to ℤsuc; -_ to ℤ-; _+_ to _ℤ+_)

open import Data.List using (List; foldr; replicate; _++_) renaming (map to Lmap; [] to nil; _∷_ to _∷:_)
open import Data.List.Any using (module Membership-≡; here; there)
open Membership-≡
open import Data.List.Properties using ()

open import Categories.Groupoid.Sum using () renaming (Sum to GSum)
open import Categories.Groupoid.Product using () renaming (Product to GProduct)

open import PiU using (U; ZERO; ONE; PLUS; TIMES; toℕ)
open import PiLevel0
open import PiLevel1
open import PiEquiv renaming (eval to ap; evalB to apB)
open import Equiv

open import PiIter

data Pointed (t : U) : Set where
  ∙ : ⟦ t ⟧ → Pointed t

-- yes, re-use name eval on purpose here
eval : {t₁ t₂ : U} → (t₁ ⟷ t₂) → Pointed t₁ → Pointed t₂
eval c (∙ x) = ∙ (ap c x)

-- all our values will be 'subtypes' of this:
record V (t : U) : Set where
  constructor v
  field
    pt : ⟦ t ⟧
    auto : t ⟷ t

evalV : {t₁ t₂ : U} → (t₁ ⟷ t₂) → V t₁ → V t₂
evalV c (v pt auto) = v (ap c pt) (! c ◎ auto ◎ c)

-- V equivalence
record _≈_ {t : U} (a : V t) (b : V t) : Set where
  constructor veq
  field
    pt-eq : V.pt a P.≡ V.pt b
    auto-eq : V.auto a ⇔ V.auto b

-- and in general, all our morphisms will be 'subtypes' of
record H {s t : U} (a : V s) (b : V t) : Set where
  constructor mor
  field
    transp : s ⟷ t
    t-eq : evalV transp a ≈ b

--- Generating sets: lists of combinators over a type
GenSet : U → Set
GenSet t = List (t ⟷ t)

-- We will need to use a choice between 3 things.  Don't want
-- to abuse ⊎.  The 3 things can be understood as
-- Forward, Backward and Neutral.  So we name it ⇕.
infix 30 _⇕_

data _⇕_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  Fwd : (x : A) → A ⇕ B
  Bwd : (y : B) → A ⇕ B
  Neu :           A ⇕ B

-- note that this uses ≡ (inside ∈), not ⇔.  On purpose.
inGS : {t : U} → GenSet t → Set
inGS {t} S = Σ (t ⟷ t) (λ p → (p ∈ S) ⇕ (p ∈ S))

-- flip a ⇕
flip⇕ : ∀ {a b} {A : Set a} {B : Set b} → A ⇕ B → B ⇕ A
flip⇕ (Fwd x) = Bwd x
flip⇕ (Bwd y) = Fwd y
flip⇕ Neu = Neu

-- type of sequences of applications from GenSet
CombS : {t : U} → GenSet t → Set
CombS {t} S = List ( inGS S )

extract : {t : U} {S : GenSet t} → inGS S → (t ⟷ t)
extract (p , Fwd x) = p
extract (p , Bwd y) = ! p
extract (p , Neu  ) = id⟷

interp : {t : U} {S : GenSet t} → CombS S → (t ⟷ t)
interp l = foldr _◎_ id⟷ (Lmap extract l)

-- the combinator (CombS S) acts as a sort of reference point
data U↑ : Set where
  ↑ : U → U↑
  # : {τ : U} → (S : GenSet τ) → CombS S → U↑
  1/ : {τ : U} → (S : GenSet τ) → CombS S → U↑
  _⊞_ : U↑ → U↑ → U↑
  _⊠_ : U↑ → U↑ → U↑

infix 40 _⇿_

data _⇿_ : U↑ → U↑ → Set where
  ↑ : {t₁ t₂ : U} → t₁ ⟷ t₂  → ↑ t₁ ⇿ ↑ t₂
  ev : {t₁ : U} {S : GenSet t₁} → (q : CombS S) → ((# S q) ⊠ (1/ S q)) ⇿ ↑ ONE
  coev : {t₁ : U} {S : GenSet t₁} → (q : CombS S) → ↑ ONE ⇿ ((# S q) ⊠ (1/ S q))
  id⇿ : {t₁ : U↑} → t₁ ⇿ t₁ -- needed for coev of ⊠

UG : Universe l0 (lsuc l0)
UG = record {
    U = U↑
 ;  El = λ T → Σ[ ℂ ∈ Category l0 l0 l0 ] (Groupoid ℂ)
 }

-- for convenience, create some types which are embedable into V
D : U → Set
D t = ⟦ t ⟧

embedD : {t : U} → D t → V t
embedD w = v w id⟷

-- and D equality lifts to ≈
embedDeq : {τ : U} → (s t : D τ) → s ≡ t → embedD s ≈ embedD t
embedDeq s .s P.refl = veq P.refl id⇔

-- even more to the point, it lifts to describing Hom sets
-- note how we can't use embedDeq directly!
embedDHom : {τ : U} → (s t : D τ) → s ≡ t → H (embedD s) (embedD t)
embedDHom s .s P.refl = mor id⟷ (veq P.refl (trans⇔ idl◎l idl◎l))

-- this is the same as in 2DTypes/groupoid
discreteC : U → Category _ _ _
discreteC t = record {
     Obj = D t
    ; _⇒_ = λ s₁ s₂ → s₁ P.≡ s₂ -- see embedDHom
    ; _≡_ = λ _ _ → ⊤ -- hard-code proof-irrelevance
    ; id = P.refl
    ; _∘_ = flip P.trans
    ; assoc = tt
    ; identityˡ = tt
    ; identityʳ = tt
    ; equiv = record { refl = tt ; sym = λ _ → tt ; trans = λ _ _ → tt }
    ; ∘-resp-≡ = λ _ _ → tt
    }
-- ditto
discreteG : (t : U) → Groupoid (discreteC t)
discreteG S = record
  { _⁻¹ = λ { {A} {.A} refl → P.refl }
  ; iso = record { isoˡ = tt; isoʳ = tt }
  }

-----------

--- Structure theorems for ⇕
extract-GS[]≡id⟷ : ∀ {t : U} → (gs : GenSet t) → gs ≡ nil →
  (∀ (y : inGS gs) → extract y ≡ id⟷)
extract-GS[]≡id⟷ .nil refl (p , Fwd ())
extract-GS[]≡id⟷ .nil refl (p , Bwd ())
extract-GS[]≡id⟷ .nil refl (p , Neu) = refl

private
  Iter→CombS :  ∀ {τ : U} (c : τ ⟷ τ) → Iter c → CombS (c ∷: nil)
  Iter→CombS p (iter (+_ n) _ _) = replicate n (p , Fwd (here refl))
  Iter→CombS p (iter (-[1+_] n) _ _) = replicate (suc n) (p , Bwd (here refl))

  CombS→Iter : ∀ {τ : U} (c : τ ⟷ τ) → CombS (c ∷: nil) → Iter c
  CombS→Iter p nil = iter (+ 0) id⟷ id⇔
  CombS→Iter p (x ∷: xs) with CombS→Iter p xs
  CombS→Iter p ((.p , Fwd (here refl)) ∷: xs) | iter i q pf =
      iter (ℤsuc i) (p ◎ q) (trans⇔ (id⇔ ⊡ pf)
        (trans⇔ (idr◎r ⊡ id⇔) (2! (lower (+ 1) i))))
  CombS→Iter p ((p' , Fwd (there ())) ∷: xs) | iter i q pf
  CombS→Iter p ((.p , Bwd (here refl)) ∷: xs) | iter i q pf =
     iter (i ℤ+ -[1+ 0 ]) (q ◎ (! p))
       (trans⇔ (pf ⊡ id⇔) (2! (lower i -[1+ 0 ])))
  CombS→Iter p ((p' , Bwd (there ())) ∷: xs) | iter i q pf
  CombS→Iter p ((p' , Neu) ∷: xs) | iter i q pf = iter i q pf

  -- split Fwd case from Bwd
  preserve-iter : ∀ {τ : U} (c : τ ⟷ τ) → (n : ℕ) →
    let p = CombS→Iter c (replicate n (c , Fwd (here refl))) in
    Iter.i p ≡ + n
  preserve-iter c zero = refl
  preserve-iter c (suc n) = cong ℤsuc (preserve-iter c n)

  preserve-iterB : ∀ {τ : U} (c : τ ⟷ τ) → (n : ℕ) →
    let p = CombS→Iter c (replicate (suc n) (c , Bwd (here refl))) in
    Iter.i p ≡ -[1+ n ]
  preserve-iterB c zero = refl
  preserve-iterB c (suc i) = P.trans
    (cong (λ z → z ℤ+ -[1+ 0 ]) (preserve-iterB c i))
    (cong (λ z → -[1+ (suc z) ]) (+-right-identity i))

  P2C2P : ∀ {τ : U} (c : τ ⟷ τ) → (p : Iter c) → (CombS→Iter c (Iter→CombS c p)) ≡c p
  P2C2P c (iter (+_ zero) p' p'⇔p^i) = eqc refl (2! p'⇔p^i)
  P2C2P c (iter (+_ (suc n)) p' p'⇔p^i) with CombS→Iter c (replicate n (c , Fwd (here refl))) | P.inspect (λ nn → CombS→Iter c (replicate nn (c , Fwd (here refl)))) n
  ... | iter i q pf | [ eq ] =
    let i=n = (P.trans (cong Iter.i (P.sym eq)) (preserve-iter c n)) in
    eqc (cong ℤsuc i=n) (trans⇔ (id⇔ {c = c} ⊡ pf)
        (trans⇔ (id⇔ ⊡ P.subst (λ j → c ^ i ⇔ c ^ j) i=n (id⇔ {c = c ^ i})) (2! p'⇔p^i)))
  P2C2P c (iter (-[1+_] n) p' p'⇔p^i) with CombS→Iter c (replicate (suc n) (c , Bwd (here refl))) | P.inspect (λ nn → CombS→Iter c (replicate (suc nn) (c , Bwd (here refl)))) n
  ... | iter i q pf | [ eq ] =
    let i=n = P.trans (cong Iter.i (P.sym eq)) (preserve-iterB c n) in
    eqc i=n (trans⇔ (P.subst (λ j → q ⇔ c ^ j) i=n pf) (2! p'⇔p^i))

  C2P2C :  ∀ {τ : U} (c : τ ⟷ τ) → (q : CombS (c ∷: nil)) →
    interp (Iter→CombS c (CombS→Iter c q)) ⇔ interp q
  C2P2C c nil = id⇔
  C2P2C c (x ∷: q) with CombS→Iter c q | (C2P2C c) q
  C2P2C c ((.c , Fwd (here refl)) ∷: q) | iter (+_ n) p' pf | pf2 = id⇔ ⊡ pf2
  C2P2C c ((.c , Fwd (here refl)) ∷: q) | iter (-[1+_] zero) p' pf | pf2 =
    trans⇔ (linv◎r {c = c}) (id⇔ ⊡ trans⇔ idr◎r pf2)
  C2P2C c ((.c , Fwd (here refl)) ∷: q) | iter (-[1+_] (suc n)) p' pf | pf2 =
    trans⇔ (trans⇔ idl◎r (linv◎r ⊡ id⇔)) (trans⇔ assoc◎r (id⇔ ⊡ pf2))
  C2P2C c ((comb , Fwd (there ())) ∷: q) | iter i p' pf | pf2
  C2P2C c ((.c , Bwd (here refl)) ∷: q) | iter (+_ zero) p' pf | pf2 = id⇔ ⊡ pf2
  C2P2C c ((.c , Bwd (here refl)) ∷: q) | iter (+_ (suc n)) p' pf | pf2 =
    trans⇔ (trans⇔ idl◎r (rinv◎r ⊡ id⇔)) (trans⇔ assoc◎r (id⇔ ⊡ pf2))
  C2P2C c ((.c , Bwd (here refl)) ∷: q) | iter (-[1+_] n) p' pf | pf2 =
    id⇔ {c = ! c} ⊡ (P.subst (λ j → ! c ◎ foldr _◎_ id⟷ (Lmap extract (replicate j (c , Bwd (here refl)))) ⇔ foldr _◎_ id⟷ (Lmap extract q)) (P.sym (+-right-identity n)) pf2)
  C2P2C c ((comb , Bwd (there ())) ∷: q) | iter i p' pf | pf2
  C2P2C c ((comb , Neu) ∷: q) | iter i p' pf | pf2 = trans⇔ pf2 idl◎r

-- we would like to say:
--    Iter[p]≃CombS[p] : ∀ {τ : U} (p : τ ⟷ τ) → Iter p ≃ CombS (p ∷: nil)
-- but the homotopies have the wrong type (≡ rather than ≡c and ⇔).

-----------
-- Generalization of # p to # S.

orderC : {τ : U} → (S : GenSet τ) → Category _ _ _
orderC {τ} S = record {
     Obj = CombS S
   ; _⇒_ = λ q₁ q₂ → interp q₁ ⇔ interp q₂
   ; _≡_ = λ _ _ → ⊤
   ; id = id⇔
   ; _∘_ = flip trans⇔
   ; assoc = tt
   ; identityˡ = tt
   ; identityʳ = tt
   ; equiv = record { refl = tt; sym = λ _ → tt; trans = λ _ _ → tt }
   ; ∘-resp-≡ = λ _ _ → tt
   }

orderG : {τ : U} → (S : GenSet τ) → Groupoid (orderC S)
orderG {τ} S = record {
    _⁻¹ = 2!
  ; iso = record {
        isoˡ = tt
      ; isoʳ = tt
      }
  }

commute-interp-++ : ∀ {τ : U} {S : GenSet τ} (f g : CombS S) →
  interp (f ++ g) ⇔ interp f ◎ interp g
commute-interp-++ nil g = idl◎r
commute-interp-++ (x ∷: f) g = trans⇔ (id⇔ ⊡ commute-interp-++ f g) assoc◎l

1/orderC : {τ : U} → (S : GenSet τ) → Category _ _ _
1/orderC S = record {
    Obj = ⊤
  ; _⇒_ = λ _ _ → CombS S
  ; _≡_ = λ p q → interp p ⇔ interp q
  ; id = nil
  ; _∘_ = _++_
  ; assoc = λ { {f = f} {g} {h} → trans⇔ (commute-interp-++ (h ++ g) f) (
      trans⇔ (commute-interp-++ h g ⊡ id⇔) (
      trans⇔ assoc◎r (
      trans⇔ (id⇔ ⊡ (2! (commute-interp-++ g f))) (
              2! (commute-interp-++ h (g ++ f)))))) }
  ; identityˡ = id⇔ -- could also use idl◎l like below
  ; identityʳ = λ { {f = f} → trans⇔ (commute-interp-++ f nil) idr◎l }
  ; equiv = record { refl = id⇔ ; sym = 2! ; trans = trans⇔ }
  ; ∘-resp-≡ = λ { {f = f} {h} {g} {i} f⇔h g⇔i →
      trans⇔ (commute-interp-++ f g) (trans⇔ (f⇔h ⊡ g⇔i) (2! (commute-interp-++ h i))) }
  }

flipGS : {τ : U} {S : GenSet τ} → inGS S → inGS S
flipGS (c , pf) = c , flip⇕ pf

invert-CombS : {τ : U} {S : GenSet τ} → CombS S → CombS S
invert-CombS pl = Data.List.map flipGS (Data.List.reverse pl)

-- sometimes we need to do some list manipulations under interp
lift-≡ : {t₁ t₂ : U} {p q : t₁ ⟷ t₂} → p ≡ q → p ⇔ q
lift-≡ refl = id⇔

interp-invert : {τ : U} {S : GenSet τ} → (x : inGS S) → (f : CombS S) →
  interp (invert-CombS (x ∷: f)) ⇔ interp (invert-CombS f) ◎ extract (flipGS x)
interp-invert (c , Fwd x) f = {!!}
interp-invert (c , Bwd y) f = {!!}
interp-invert (c , Neu) f = {!!}

extract-flipGS : {τ : U} {S : GenSet τ} → (x : inGS S) → extract (flipGS x) ⇔ ! (extract x)
extract-flipGS (proj₁ , Fwd x) = id⇔
extract-flipGS (proj₁ , Bwd y) = 2! !!⇔id
extract-flipGS (proj₁ , Neu) = id⇔

private
  left-invCS : {τ : U} {S : GenSet τ} (f : CombS S) → interp (invert-CombS f ++ f) ⇔ id⟷
  left-invCS nil = id⇔
  left-invCS (x ∷: f) = trans⇔
    (commute-interp-++ (invert-CombS (x ∷: f)) (x ∷: f)) (trans⇔
    (interp-invert x f ⊡ id⇔) (trans⇔
    assoc◎r (trans⇔
    (id⇔ ⊡ assoc◎l) (trans⇔
    (id⇔ ⊡ trans⇔ (trans⇔ (extract-flipGS x ⊡ id⇔) rinv◎l ⊡ id⇔) idl◎l) (trans⇔
    (2! (commute-interp-++ (invert-CombS f) f)) (left-invCS f))))))

  right-invCS : {τ : U} {S : GenSet τ} (f : CombS S) → interp (f ++ invert-CombS f) ⇔ id⟷
  right-invCS l = {!!}

1/orderG : {τ : U} → (S : GenSet τ) → Groupoid (1/orderC S)
1/orderG s = record {
    _⁻¹ = invert-CombS
  ; iso = λ {_} {_} {f} → record {
      isoˡ = left-invCS f
    ; isoʳ = right-invCS f
    }
  }

-----------
--- Our semantics into groupoids
⟦_⟧↑ : (T : U↑) → Universe.El UG T
⟦ ↑ S ⟧↑ = , discreteG S
⟦ # S q ⟧↑ = , orderG S -- the base point doesn't matter here?
⟦ 1/ S q ⟧↑ = , 1/orderG S -- ditto?
⟦ x ⊞ y ⟧↑ with ⟦ x ⟧↑ | ⟦ y ⟧↑
... | (_ , G₁) | (_ , G₂) = , GSum G₁ G₂
⟦ x ⊠ y ⟧↑ with ⟦ x ⟧↑ | ⟦ y ⟧↑
... | (_ , G₁) | (_ , G₂) = , GProduct G₁ G₂

record W (t : U↑) : Set where
  constructor w
  field
    pt : Category.Obj (proj₁ ⟦ t ⟧↑)
    auto : Category._⇒_ (proj₁ ⟦ t ⟧↑) pt pt

evalW : {s t : U↑} → s ⇿ t → W s → W t
evalW (↑ x) (w pt auto) = w (ap x pt) refl
evalW (ev q) (w (cc , tt) (cc⇔cc-pf , cc')) = w tt refl -- this is cheating, cc <=> cc' ??
evalW (coev q) (w tt refl) = w (q , tt) (id⇔ , q) -- but this isn't.
evalW id⇿ (w pt auto) = w pt auto

-- This should actually be Hom-set inhabitation, aka categorical equivalence
-- (as we are in a groupoid setting).
ObjEq : (t : U↑) → Category.Obj (proj₁ ⟦ t ⟧↑) → Category.Obj (proj₁ ⟦ t ⟧↑) → Set
ObjEq (↑ x) a b = a P.≡ b
ObjEq (# S x) a b = interp a ⇔ interp b
ObjEq (1/ S x) a b = ⊤
ObjEq (s ⊞ t) (inj₁ x) (inj₁ y) = ObjEq s x y
ObjEq (s ⊞ t) (inj₁ x) (inj₂ y) = ⊥
ObjEq (s ⊞ t) (inj₂ y) (inj₁ x) = ⊥
ObjEq (s ⊞ t) (inj₂ x) (inj₂ y) = ObjEq t x y
ObjEq (s ⊠ t) (a , b) (c , d) = ObjEq s a c × ObjEq t b d

HomEq : (t : U↑) → (a b : Category.Obj (proj₁ ⟦ t ⟧↑)) →
  Category._⇒_ (proj₁ ⟦ t ⟧↑) a b → Category._⇒_ (proj₁ ⟦ t ⟧↑) a b → Set
HomEq (↑ x) a b h1 h2 = ⊤ -- this is basically proof-irrelevance h1 h2
HomEq (# S x) a b h1 h2 = ⊤ -- because h1 and h2 are the exact same ⇔
HomEq (1/ S x) tt tt h1 h2 = interp h1 ⇔ interp h2 -- here!
HomEq (s ⊞ t) (inj₁ x) (inj₁ x₁) h1 h2 = HomEq s x x₁ h1 h2
HomEq (s ⊞ t) (inj₁ x) (inj₂ y) (Level.lift ()) h2
HomEq (s ⊞ t) (inj₂ y) (inj₁ x) (Level.lift ()) h2
HomEq (s ⊞ t) (inj₂ y) (inj₂ y₁) h1 h2 = HomEq t y y₁ h1 h2
HomEq (s ⊠ t) (a , b) (c , d) (a⇒c , b⇒d) (a⇒c' , b⇒d') =
  HomEq s a c a⇒c a⇒c' × HomEq t b d b⇒d b⇒d'

-- W equivalence; note that transp is restricted to be the Homs of cat
--   we might want to restrict that more.
record _≈W_ {t : U↑} (a : W t) (b : W t) : Set where
  constructor weq
  cat = proj₁ ⟦ t ⟧↑
  gpd = proj₂ ⟦ t ⟧↑
  _∘_ = Category._∘_ cat
  field
    pt-eq : ObjEq t (W.pt a) (W.pt b)
    transp : Category._⇒_ cat (W.pt a) (W.pt b)
    auto-eq : HomEq t (W.pt a) (W.pt b) (W.auto b ∘ (transp ∘ (W.auto a))) transp