open import Agda.Primitive using (lzero; lsuc; _⊔_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; setoid; cong; trans)
import Function.Equality
open import Relation.Binary using (Setoid)

import Categories.Category
import Categories.Functor
import Categories.Category.Instance.Setoids
import Categories.Monad.Relative
import Categories.Category.Equivalence
import Categories.Category.Cocartesian

import SecondOrder.Arity
import SecondOrder.Signature
import SecondOrder.Metavariable
import SecondOrder.VRenaming
import SecondOrder.Term
import SecondOrder.IndexedCategory
import SecondOrder.RelativeKleisli

module SecondOrder.Substitution
  {ℓ}
  {𝔸 : SecondOrder.Arity.Arity}
  (Σ : SecondOrder.Signature.Signature ℓ 𝔸)
  where

  open SecondOrder.Signature.Signature Σ
  open SecondOrder.Metavariable Σ
  open SecondOrder.Term Σ
  open SecondOrder.VRenaming Σ

  -- substitution

  infix 4 _⊕_⇒ˢ_

  _⊕_⇒ˢ_ : ∀ (Θ : MContext) (Γ Δ : VContext) → Set ℓ
  Θ ⊕ Γ ⇒ˢ Δ = ∀ {A} (x : A ∈ Γ) → Term Θ Δ A

  -- syntactic equality of substitutions

  infix 5 _≈ˢ_

  _≈ˢ_ : ∀ {Θ} {Γ Δ} (σ τ : Θ ⊕ Γ ⇒ˢ Δ) → Set ℓ
  _≈ˢ_ {Θ} {Γ} σ τ = ∀ {A} (x : A ∈ Γ) → σ x ≈ τ x

  -- equality of substitutions form a setoid
  ≈ˢ-refl : ∀ {Θ Γ Δ} {σ : Θ ⊕ Γ ⇒ˢ Δ}
          → σ ≈ˢ σ
  ≈ˢ-refl x = ≈-refl

  ≈ˢ-sym : ∀ {Θ Γ Δ} {σ τ : Θ ⊕ Γ ⇒ˢ Δ}
          → σ ≈ˢ τ
          → τ ≈ˢ σ
  ≈ˢ-sym eq x = ≈-sym (eq x)

  ≈ˢ-trans : ∀ {Θ Γ Δ} {σ τ μ : Θ ⊕ Γ ⇒ˢ Δ}
           → σ ≈ˢ τ → τ ≈ˢ μ
           → σ ≈ˢ μ
  ≈ˢ-trans eq1 eq2 x = ≈-trans (eq1 x) (eq2 x)

  substitution-setoid : ∀ (Γ Δ : VContext) (Θ : MContext) → Setoid ℓ ℓ
  substitution-setoid Γ Δ Θ =
    record
      { Carrier = Θ ⊕ Γ ⇒ˢ Δ
      ;  _≈_ = λ σ τ → σ ≈ˢ τ
      ; isEquivalence =
                      record
                        { refl = λ {σ} x → ≈ˢ-refl {σ = σ} x
                        ; sym = ≈ˢ-sym
                        ; trans = ≈ˢ-trans
                        }
      }

  congˢ : ∀ {Θ} {Γ Δ} {A} {σ τ : Θ ⊕ Γ ⇒ˢ Δ} {x : A ∈ Γ} → σ ≈ˢ τ → σ x ≈ τ x
  congˢ {x = x} eq = eq x

  congˢ-var :  ∀ {Θ} {Γ Δ} {A} {σ : Θ ⊕ Γ ⇒ˢ Δ} {x y : A ∈ Γ} → x ≡ y → σ x ≈ σ y
  congˢ-var refl = ≈-refl

  -- extension of a substitution

  ⇑ˢ : ∀ {Θ Γ Δ Ξ} → Θ ⊕ Γ ⇒ˢ Δ → Θ ⊕ (Γ ,, Ξ) ⇒ˢ (Δ ,, Ξ)
  ⇑ˢ σ (var-inl x) = [ var-inl ]ᵛ σ x
  ⇑ˢ σ (var-inr y) = tm-var (var-inr y)

  -- extension respects equality of substitutions

  ⇑ˢ-resp-≈ˢ : ∀ {Θ Γ Δ Ξ} {σ τ : Θ ⊕ Γ ⇒ˢ Δ} → σ ≈ˢ τ → ⇑ˢ {Ξ = Ξ} σ ≈ˢ ⇑ˢ {Ξ = Ξ} τ
  ⇑ˢ-resp-≈ˢ ξ (var-inl x) = []ᵛ-resp-≈ (ξ x)
  ⇑ˢ-resp-≈ˢ ξ (var-inr y) = ≈-refl

  -- the action of a renaming on a substitution

  infixr 6 _ᵛ∘ˢ_

  _ᵛ∘ˢ_ : ∀ {Θ} {Γ Δ Ξ} (ρ : Δ ⇒ᵛ Ξ) (σ : Θ ⊕ Γ ⇒ˢ Δ) → Θ ⊕ Γ ⇒ˢ Ξ
  (ρ ᵛ∘ˢ σ) x =  [ ρ ]ᵛ (σ x)

  infixl 6 _ˢ∘ᵛ_

  _ˢ∘ᵛ_ : ∀ {Θ} {Γ Δ Ξ} (σ : Θ ⊕ Δ ⇒ˢ Ξ) (ρ : Γ ⇒ᵛ Δ)  → Θ ⊕ Γ ⇒ˢ Ξ
  (σ ˢ∘ᵛ ρ) x =  σ (ρ x)

  -- extension commutes with renaming action

  ⇑ˢ-resp-ˢ∘ᵛ : ∀ {Θ} {Γ Δ Ξ Ψ} {ρ : Γ ⇒ᵛ Δ} {σ : Θ ⊕ Δ ⇒ˢ Ξ} → ⇑ˢ {Ξ = Ψ} (σ ˢ∘ᵛ ρ) ≈ˢ ⇑ˢ σ ˢ∘ᵛ ⇑ᵛ ρ
  ⇑ˢ-resp-ˢ∘ᵛ (var-inl x) = ≈-refl
  ⇑ˢ-resp-ˢ∘ᵛ (var-inr x) = ≈-refl

  -- the action of a substitution on a term

  infix 6 [_]ˢ_

  [_]ˢ_ : ∀ {Θ Γ Δ A} → Θ ⊕ Γ ⇒ˢ Δ → Term Θ Γ A → Term Θ Δ A
  [ σ ]ˢ (tm-var x) = σ x
  [ σ ]ˢ (tm-meta M ts) = tm-meta M (λ i → [ σ ]ˢ ts i)
  [ σ ]ˢ (tm-oper f es) = tm-oper f (λ i → [ ⇑ˢ σ ]ˢ es i)

  -- composition of substitutions

  infixl 7 _∘ˢ_
  _∘ˢ_ : ∀ {Θ} {Γ Δ Ξ} → Θ ⊕ Δ ⇒ˢ Ξ → Θ ⊕ Γ ⇒ˢ Δ → Θ ⊕ Γ ⇒ˢ Ξ
  (σ ∘ˢ τ) x = [ σ ]ˢ τ x

  -- substitution action respects equality of terms

  []ˢ-resp-≈ : ∀ {Θ} {Γ Δ} {A} (σ : Θ ⊕ Γ ⇒ˢ Δ) {t u : Term Θ Γ A} → t ≈ u → [ σ ]ˢ t ≈  [ σ ]ˢ u
  []ˢ-resp-≈ σ (≈-≡ refl) = ≈-refl
  []ˢ-resp-≈ σ (≈-meta ξ) = ≈-meta (λ i → []ˢ-resp-≈ σ (ξ i))
  []ˢ-resp-≈ σ (≈-oper ξ) = ≈-oper (λ i → []ˢ-resp-≈ (⇑ˢ σ) (ξ i))

  -- substitution action respects equality of substitutions

  []ˢ-resp-≈ˢ : ∀ {Θ} {Γ Δ} {A} {σ τ : Θ ⊕ Γ ⇒ˢ Δ} (t : Term Θ Γ A) → σ ≈ˢ τ → [ σ ]ˢ t ≈ [ τ ]ˢ t
  []ˢ-resp-≈ˢ (tm-var x) ξ = ξ x
  []ˢ-resp-≈ˢ (tm-meta M ts) ξ = ≈-meta (λ i → []ˢ-resp-≈ˢ (ts i) ξ)
  []ˢ-resp-≈ˢ (tm-oper f es) ξ = ≈-oper (λ i → []ˢ-resp-≈ˢ (es i) (⇑ˢ-resp-≈ˢ ξ))

  -- substitution actions respects both equalities

  []ˢ-resp-≈ˢ-≈ : ∀ {Θ} {Γ Δ} {A} {σ τ : Θ ⊕ Γ ⇒ˢ Δ} {t u : Term Θ Γ A} → σ ≈ˢ τ → t ≈ u → [ σ ]ˢ t ≈ [ τ ]ˢ u
  []ˢ-resp-≈ˢ-≈ {τ = τ} {t = t} ζ ξ = ≈-trans ([]ˢ-resp-≈ˢ t ζ) ([]ˢ-resp-≈ τ ξ)

  -- identity substitution

  idˢ : ∀ {Θ Γ} → Θ ⊕ Γ ⇒ˢ Γ
  idˢ = tm-var

  -- extension preserves identity

  ⇑ˢ-resp-idˢ : ∀ {Θ} {Γ Δ} → ⇑ˢ idˢ ≈ˢ idˢ {Θ = Θ} {Γ = Γ ,, Δ}
  ⇑ˢ-resp-idˢ (var-inl x) = ≈-refl
  ⇑ˢ-resp-idˢ (var-inr y) = ≈-refl

  -- the identity substution acts trivially

  [idˢ] : ∀ {Θ} {Γ} {A} {t : Term Θ Γ A} → [ idˢ ]ˢ t ≈ t
  [idˢ] {t = tm-var x} = ≈-refl
  [idˢ] {t = tm-meta M ts} = ≈-meta (λ i → [idˢ])
  [idˢ] {t = tm-oper f es} = ≈-oper (λ i → ≈-trans ([]ˢ-resp-≈ˢ (es i) ⇑ˢ-resp-idˢ) [idˢ])

  -- the identity substitution preserves equality of terms
  [idˢ]-resp-≈ : ∀ {Θ} {Γ} {A} {t s : Term Θ Γ A} → t ≈ s → [ idˢ ]ˢ t ≈ s
  [idˢ]-resp-≈ t≈s = ≈-trans ([]ˢ-resp-≈ idˢ t≈s) [idˢ]

  -- if a substiution is equal to the identity then it acts trivially
  ≈ˢ-idˢ-[]ˢ : ∀ {Θ} {Γ} {A} {σ : Θ ⊕ Γ ⇒ˢ Γ} {t : Term Θ Γ A} → σ ≈ˢ idˢ → [ σ ]ˢ t ≈ t
  ≈ˢ-idˢ-[]ˢ {t = t} ξ = ≈-trans ([]ˢ-resp-≈ˢ t ξ) [idˢ]

  -- interaction of extension and right renaming action

  [⇑ˢ∘ᵛ] : ∀ {Θ} {A} {Γ Δ Ξ Ψ} {σ : Θ ⊕ Δ ⇒ˢ Ξ} {ρ : Γ ⇒ᵛ Δ} (t : Term Θ (Γ ,, Ψ) A) →
         [ ⇑ˢ (σ ˢ∘ᵛ ρ) ]ˢ t ≈ [ ⇑ˢ σ ]ˢ [ ⇑ᵛ ρ ]ᵛ t
  [⇑ˢ∘ᵛ] (tm-var (var-inl x)) = ≈-refl
  [⇑ˢ∘ᵛ] (tm-var (var-inr x)) = ≈-refl
  [⇑ˢ∘ᵛ] (tm-meta M ts) = ≈-meta (λ i → [⇑ˢ∘ᵛ] (ts i))
  [⇑ˢ∘ᵛ] (tm-oper f es) = ≈-oper (λ i → ≈-trans ([]ˢ-resp-≈ˢ (es i) (⇑ˢ-resp-≈ˢ ⇑ˢ-resp-ˢ∘ᵛ)) ([⇑ˢ∘ᵛ] (es i)))

  -- interaction of extension and left renaming action

  ⇑ˢ-resp-ᵛ∘ˢ : ∀ {Θ} {Γ Δ Ξ Ψ} {σ : Θ ⊕ Γ ⇒ˢ Δ} {ρ : Δ ⇒ᵛ Ξ} →
           ⇑ˢ {Ξ = Ψ} (ρ ᵛ∘ˢ σ) ≈ˢ ⇑ᵛ ρ ᵛ∘ˢ ⇑ˢ σ
  ⇑ˢ-resp-ᵛ∘ˢ (var-inl x) = ≈-trans (≈-sym [∘ᵛ]) (≈-trans ([]ᵛ-resp-≡ᵛ (λ _ → refl)) [∘ᵛ])
  ⇑ˢ-resp-ᵛ∘ˢ (var-inr y) = ≈-refl

  [⇑ᵛ∘ˢ] : ∀ {Θ} {A} {Γ Δ Ξ Ψ} {σ : Θ ⊕ Γ ⇒ˢ Δ} {ρ : Δ ⇒ᵛ Ξ} (t : Term Θ (Γ ,, Ψ) A) →
         [ ⇑ˢ (ρ ᵛ∘ˢ σ) ]ˢ t ≈ [ ⇑ᵛ ρ ]ᵛ ([ ⇑ˢ σ ]ˢ t)
  [⇑ᵛ∘ˢ] (tm-var x) = ⇑ˢ-resp-ᵛ∘ˢ x
  [⇑ᵛ∘ˢ] (tm-meta M ts) = ≈-meta (λ i → [⇑ᵛ∘ˢ] (ts i))
  [⇑ᵛ∘ˢ] (tm-oper f es) = ≈-oper (λ i → ≈-trans ([]ˢ-resp-≈ˢ (es i) (⇑ˢ-resp-≈ˢ ⇑ˢ-resp-ᵛ∘ˢ)) ([⇑ᵛ∘ˢ] (es i)))

  -- functoriality of left renaming action

  [ᵛ∘ˢ] : ∀ {Θ} {A} {Γ Δ Ξ} {ρ : Δ ⇒ᵛ Ξ} {σ : Θ ⊕ Γ ⇒ˢ Δ} (t : Term Θ Γ A) →
           [ ρ ᵛ∘ˢ σ ]ˢ t ≈ [ ρ ]ᵛ [ σ ]ˢ t
  [ᵛ∘ˢ] (tm-var x) = ≈-refl
  [ᵛ∘ˢ] (tm-meta M ts) = ≈-meta (λ i → [ᵛ∘ˢ] (ts i))
  [ᵛ∘ˢ] (tm-oper f es) = ≈-oper (λ i → [⇑ᵛ∘ˢ] (es i))

  -- functoriality of right renaming action

  [ˢ∘ᵛ] : ∀ {Θ} {A} {Γ Δ Ξ} {σ : Θ ⊕ Δ ⇒ˢ Ξ} {ρ : Γ ⇒ᵛ Δ} (t : Term Θ Γ A) →
           [ σ ˢ∘ᵛ ρ ]ˢ t ≈ [ σ ]ˢ [ ρ ]ᵛ t
  [ˢ∘ᵛ] (tm-var x) = ≈-refl
  [ˢ∘ᵛ] (tm-meta M ts) = ≈-meta (λ i → [ˢ∘ᵛ] (ts i))
  [ˢ∘ᵛ] (tm-oper f es) = ≈-oper (λ i → [⇑ˢ∘ᵛ] (es i))

  -- composition commutes with extension

  ⇑ˢ-resp-∘ˢ : ∀ {Θ} {Γ Δ Ξ Ψ} {σ : Θ ⊕ Γ ⇒ˢ Δ} {τ : Θ ⊕ Δ ⇒ˢ Ξ} →
          ⇑ˢ {Ξ = Ψ} (τ ∘ˢ σ) ≈ˢ ⇑ˢ τ ∘ˢ ⇑ˢ σ
  ⇑ˢ-resp-∘ˢ {σ = σ} {τ = τ} (var-inl x) =  ≈-trans (≈-sym ([ᵛ∘ˢ] (σ x))) ([ˢ∘ᵛ] (σ x))
  ⇑ˢ-resp-∘ˢ (var-inr y) = ≈-refl

  -- substitition action is functorial

  [∘ˢ] : ∀ {Θ} {Γ Δ Ξ} {A} {σ : Θ ⊕ Γ ⇒ˢ Δ} {τ : Θ ⊕ Δ ⇒ˢ Ξ} (t : Term Θ Γ A) →
         [ τ ∘ˢ σ ]ˢ t ≈ [ τ ]ˢ ([ σ ]ˢ t)
  [∘ˢ] (tm-var x) = ≈-refl
  [∘ˢ] (tm-meta M ts) = ≈-meta (λ i → [∘ˢ] (ts i))
  [∘ˢ] (tm-oper f es) = ≈-oper (λ i → ≈-trans ([]ˢ-resp-≈ˢ (es i) ⇑ˢ-resp-∘ˢ) ([∘ˢ] (es i)))

  -- Terms form a relative monad

  module _ where
    open Categories.Category
    open Categories.Functor using (Functor)
    open Categories.Category.Instance.Setoids
    open Categories.Monad.Relative
    open Function.Equality using () renaming (setoid to Π-setoid)
    open Categories.Category.Equivalence using (StrongEquivalence)
    open import SecondOrder.IndexedCategory
    open import SecondOrder.RelativeKleisli

    -- The embedding of contexts into setoids indexed by sorts

    slots : Functor VContexts (IndexedCategory sort (Setoids ℓ ℓ))
    slots = record
              { F₀ = λ Γ A → setoid (A ∈ Γ)
              ; F₁ = λ ρ A → record { _⟨$⟩_ = ρ ; cong = cong ρ }
              ; identity = λ A ξ → ξ
              ; homomorphism = λ {_} {_} {_} {ρ} {σ} A {_} {_} ξ → cong σ (cong ρ ξ)
              ; F-resp-≈ = λ ξ A ζ → trans (ξ _) (cong _ ζ)
              }

  module _ {Θ : MContext} where
    open Categories.Category
    open Categories.Functor using (Functor)
    open Categories.Category.Instance.Setoids
    open Categories.Monad.Relative
    open Function.Equality using () renaming (setoid to Π-setoid)
    open Categories.Category.Equivalence using (StrongEquivalence)
    open import SecondOrder.IndexedCategory
    open import SecondOrder.RelativeKleisli

    -- The relative monad of terms over contexts

    Term-Monad : Monad slots
    Term-Monad =
      let open Function.Equality using (_⟨$⟩_) renaming (cong to func-cong) in
      record
        { F₀ = Term-setoid Θ
        ; unit = λ A → record { _⟨$⟩_ = idˢ ; cong = λ ξ → ≈-≡ (cong idˢ ξ) }
        ; extend = λ σ A → record { _⟨$⟩_ =  [ (σ _ ⟨$⟩_) ]ˢ_ ; cong = []ˢ-resp-≈ (σ _ ⟨$⟩_)}
        ; identityʳ = λ {_} {_} {σ} A {_} {_} ξ → func-cong (σ A) ξ
        ; identityˡ = λ A → ≈-trans [idˢ]
        ; assoc = λ {_} {_} {_} {σ} {ρ} A {_} {t} ξ → ≈-trans ([]ˢ-resp-≈ _ ξ) ([∘ˢ] t)
        ; extend-≈ = λ {Γ} {Δ} {σ} {ρ} ζ B {s} {t} ξ → []ˢ-resp-≈ˢ-≈ (λ x → ζ _ refl) ξ
        }

    -- the category of contexts and substitutions

    -- we show below that the category of contexts and substitiions is equivalent
    -- to the Kleisli category for the Term relative monad. However, we define
    -- the category of contexts and substitutions directly, as that it is easier
    -- to work with it

    Terms : Category ℓ ℓ ℓ
    Terms =
      record
        { Obj = VContext
        ; _⇒_ = Θ ⊕_⇒ˢ_
        ; _≈_ =  _≈ˢ_
        ; id = idˢ
        ; _∘_ = _∘ˢ_
        ; assoc = λ {Γ} {Δ} {Ξ} {Ψ} {σ} {τ} {ψ} {A} x → [∘ˢ] (σ x)
        ; sym-assoc = λ {Γ} {Δ} {Ξ} {Ψ} {σ} {τ} {ψ} {A} x → ≈-sym ([∘ˢ] (σ x))
        ; identityˡ = λ x → [idˢ]
        ; identityʳ = λ x → ≈-refl
        ; identity² = λ x → ≈-refl
        ; equiv = record { refl = λ {σ} {A} → ≈ˢ-refl {σ = σ} ; sym = ≈ˢ-sym ; trans = ≈ˢ-trans }
        ; ∘-resp-≈ = λ f≈ˢg g≈ˢi x → []ˢ-resp-≈ˢ-≈ f≈ˢg (g≈ˢi x)
        }

    Terms-is-Kleisli : StrongEquivalence Terms (Kleisli Term-Monad)
    Terms-is-Kleisli =
      record
      { F = record
              { F₀ = λ Γ → Γ
              ; F₁ = λ σ A → record { _⟨$⟩_ = λ x → σ x ; cong = λ i≡j → ≈-≡ (cong σ i≡j) }
              ; identity = λ A eq → ≈-≡ (cong idˢ eq)
              ; homomorphism = λ {Γ} {Δ} {Ξ} {σ} {τ} A eq → ≈-≡ (cong (λ x → [ τ ]ˢ σ x) eq)
              ; F-resp-≈ = λ {Γ} {Δ} {σ} {τ} hom_eq A eq
                         → ≈-trans (congˢ hom_eq) (≈-≡ (cong τ eq))
              }
      ; G =
          let open Function.Equality using (_⟨$⟩_) renaming (cong to func-cong) in
          record
              { F₀ = λ Γ → Γ
              ; F₁ = λ {Γ} {Δ} σ {A} → λ x → σ A ⟨$⟩ x
              ; identity = λ x → ≈-refl
              ; homomorphism = λ x → ≈-refl
              ; F-resp-≈ = λ {Γ} {Δ} {σ} {τ} σ≈τ {A} x → σ≈τ A refl
              }
      ; weak-inverse =
          let open Function.Equality using (_⟨$⟩_) renaming (cong to func-cong) in
          record
          { F∘G≈id =
                   record
                   { F⇒G =
                         record
                         { η = λ Γ A → record { _⟨$⟩_ = idˢ
                                               ; cong = λ i≡j → ≈-≡ (cong idˢ i≡j)
                                               }
                         ; commute = λ σ A x≡y → [idˢ]-resp-≈ (≈-≡ (cong (λ x → σ A ⟨$⟩ x) x≡y))
                         ; sym-commute = λ σ A x≡y
                                       → ≈-sym ([idˢ]-resp-≈ (≈-≡ (cong (λ x → σ A ⟨$⟩ x ) (sym x≡y))))
                         }
                   ; F⇐G =
                         record
                         { η = λ Γ A → record { _⟨$⟩_ = idˢ
                                               ; cong = λ i≡j → ≈-≡ (cong idˢ i≡j)
                                               }
                         ; commute = λ σ A x≡y → [idˢ]-resp-≈ (≈-≡ (cong (λ x → σ A ⟨$⟩ x) x≡y))
                         ; sym-commute = λ σ A x≡y
                                       → ≈-sym ([idˢ]-resp-≈ (≈-≡ (cong (λ x → σ A ⟨$⟩ x ) (sym x≡y))))
                         }
                   ; iso = λ Γ → record { isoˡ = λ A x≡y → ≈-≡ (cong tm-var x≡y)
                                         ; isoʳ = λ A x≡y → ≈-≡ (cong tm-var x≡y)
                                         }
                   }
          ; G∘F≈id =
                   record
                   { F⇒G =
                         record
                         { η = λ Γ x → tm-var x
                         ; commute = λ σ x → [idˢ]
                         ; sym-commute = λ σ x → ≈-sym [idˢ]
                         }
                   ; F⇐G =
                         record
                         { η = λ Γ x → tm-var x
                         ; commute = λ σ x → [idˢ]
                         ; sym-commute = λ σ x → ≈-sym [idˢ]
                         }
                   ; iso = λ Γ → record { isoˡ = λ x → ≈-refl
                                         ; isoʳ = λ x → ≈-refl
                                         }
                   }
          }
      }

    -- the binary coproduct structure on Terms

    infixl 7 [_,_]ˢ

    [_,_]ˢ : ∀ {Γ Δ Ξ} (σ : Θ ⊕ Γ ⇒ˢ Ξ) (τ : Θ ⊕ Δ ⇒ˢ Ξ) → Θ ⊕ (Γ ,, Δ) ⇒ˢ Ξ
    [ σ , τ ]ˢ (var-inl x) = σ x
    [ σ , τ ]ˢ (var-inr y) = τ y

    inlˢ : ∀ {Γ Δ} → Θ ⊕ Γ ⇒ˢ Γ ,, Δ
    inlˢ x = tm-var (var-inl x)

    inrˢ : ∀ {Γ Δ} → Θ ⊕ Δ ⇒ˢ Γ ,, Δ
    inrˢ y = tm-var (var-inr y)

    [,]ˢ-resp-≈ˢ : ∀ {Γ Δ Ξ} {σ₁ σ₂ : Θ ⊕ Γ ⇒ˢ Ξ} {τ₁ τ₂ : Θ ⊕ Δ ⇒ˢ Ξ} →
                   σ₁ ≈ˢ σ₂ → τ₁ ≈ˢ τ₂ → [ σ₁ , τ₁ ]ˢ ≈ˢ [ σ₂ , τ₂ ]ˢ
    [,]ˢ-resp-≈ˢ ζ ξ (var-inl x) = ζ x
    [,]ˢ-resp-≈ˢ ζ ξ (var-inr y) = ξ y

    uniqueˢ : ∀ {Γ Δ Ξ} {τ : Θ ⊕ Γ ,, Δ ⇒ˢ Ξ} {ρ : Θ ⊕ Γ ⇒ˢ Ξ} {σ : Θ ⊕ Δ ⇒ˢ Ξ}
              → τ ∘ˢ inlˢ ≈ˢ ρ
              → τ ∘ˢ inrˢ ≈ˢ σ
              → [ ρ , σ ]ˢ ≈ˢ τ
    uniqueˢ ξ ζ (var-inl x) = ≈-sym (ξ x)
    uniqueˢ ξ ζ (var-inr y) = ≈-sym (ζ y)

    Terms-Coproduct : Categories.Category.Cocartesian.BinaryCoproducts Terms
    Terms-Coproduct =
      let open Function.Equality using (_⟨$⟩_) renaming (cong to func-cong) in
      record {
        coproduct =
          λ {Γ Δ} →
          record
            { A+B = Γ ,, Δ
            ; i₁ = inlˢ
            ; i₂ = inrˢ
            ; [_,_] = [_,_]ˢ
            ; inject₁ = λ x → ≈-≡ refl
            ; inject₂ = λ x → ≈-≡ refl
            ; unique = λ {Ξ} {h} p₁ p₂ → uniqueˢ {τ = h} p₁ p₂
            }
      }

    open Categories.Category.Cocartesian.BinaryCoproducts Terms-Coproduct

    -- the sum of subsitutions

    infixl 6 _+ˢ_

    _+ˢ_ : ∀ {Γ₁ Γ₂ Δ₁ Δ₂} (σ : Θ ⊕ Γ₁ ⇒ˢ Δ₁) (τ : Θ ⊕ Γ₂ ⇒ˢ Δ₂) → Θ ⊕ Γ₁ ,, Γ₂ ⇒ˢ Δ₁ ,, Δ₂
    σ +ˢ τ = σ +₁ τ

    -- reassociations of context sums

    assoc-l : ∀ {Γ Δ Ξ} → Θ ⊕ (Γ ,, Δ) ,, Ξ ⇒ˢ Γ ,, (Δ ,, Ξ)
    assoc-l (var-inl (var-inl x)) = tm-var (var-inl x)
    assoc-l (var-inl (var-inr y)) = tm-var (var-inr (var-inl y))
    assoc-l (var-inr z) = tm-var (var-inr (var-inr z))

    assoc-r : ∀ {Γ Δ Ξ} → Θ ⊕ Γ ,, (Δ ,, Ξ) ⇒ˢ (Γ ,, Δ) ,, Ξ
    assoc-r (var-inl x) = tm-var (var-inl (var-inl x))
    assoc-r (var-inr (var-inl y)) = tm-var (var-inl (var-inr y))
    assoc-r (var-inr (var-inr z)) = tm-var (var-inr z)

    assoc-lr : ∀ {Γ Δ Ξ} → assoc-l {Γ = Γ} {Δ = Δ} {Ξ = Ξ} ∘ˢ assoc-r {Γ = Γ} {Δ = Δ} {Ξ = Ξ} ≈ˢ idˢ
    assoc-lr (var-inl x) = ≈-refl
    assoc-lr (var-inr (var-inl y)) = ≈-refl
    assoc-lr (var-inr (var-inr y)) = ≈-refl

    assoc-rl : ∀ {Γ Δ Ξ} → assoc-r {Γ = Γ} {Δ = Δ} {Ξ = Ξ} ∘ˢ assoc-l {Γ = Γ} {Δ = Δ} {Ξ = Ξ} ≈ˢ idˢ
    assoc-rl (var-inl (var-inl x)) = ≈-refl
    assoc-rl (var-inl (var-inr x)) = ≈-refl
    assoc-rl (var-inr z) = ≈-refl

    -- summing with the empty context is the unit

    sum-ctx-empty-r : ∀ {Γ} → Θ ⊕ Γ ,, ctx-empty ⇒ˢ Γ
    sum-ctx-empty-r (var-inl x) = tm-var x

    sum-ctx-empty-l : ∀ {Γ} → Θ ⊕ ctx-empty ,, Γ ⇒ˢ Γ
    sum-ctx-empty-l (var-inr x) = tm-var x


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

open import HoTT
open import cohomology.SuspAdjointLoopIso
open import cohomology.WithCoefficients
open import cohomology.Theory
open import cohomology.Exactness
open import cohomology.Choice

module cohomology.SpectrumModel
  {i} (E : ℤ → Ptd i) (spectrum : (n : ℤ) → ⊙Ω (E (succ n)) == E n) where

module SpectrumModel where

  {- Definition of cohomology group C -}
  module _ (n : ℤ) (X : Ptd i) where
    C : Group i
    C = →Ω-Group X (E (succ n))

    {- convenient abbreviations -}
    CEl = Group.El C
    ⊙CEl = Group.⊙El C
    Cid = Group.ident C

    {- before truncation -}
    ⊙uCEl : Ptd i
    ⊙uCEl = X ⊙→ ⊙Ω (E (succ n))

    uCEl = fst ⊙uCEl
    uCid = snd ⊙uCEl

  {- Cⁿ(X) is an abelian group -}
  C-abelian : (n : ℤ) (X : Ptd i) → is-abelian (C n X)
  C-abelian n X =
    transport (is-abelian ∘ →Ω-Group X) (spectrum (succ n)) C-abelian-lemma
    where
    pt-lemma : ∀ {i} {X : Ptd i} {α β : Ω^ 2 X}
      (γ : α == idp^ 2) (δ : β == idp^ 2)
      → ap2 _∙_ γ δ == conc^2-comm α β ∙ ap2 _∙_ δ γ
    pt-lemma idp idp = idp

    C-abelian-lemma = Trunc-elim
      (λ _ → Π-level (λ _ → =-preserves-level _ Trunc-level))
      (λ {(f , fpt) → Trunc-elim
        (λ _ → =-preserves-level _ Trunc-level)
        (λ {(g , gpt) → ap [_] $ ⊙λ=
          (λ x → conc^2-comm (f x) (g x))
          (pt-lemma fpt gpt)})})

  {- CF, the functorial action of C:
   - contravariant functor from pointed spaces to abelian groups -}
  module _ (n : ℤ) {X Y : Ptd i} where

    {- before truncation - from pointed spaces to pointed spaces -}
    uCF : fst (X ⊙→ Y) → fst (⊙uCEl n Y ⊙→ ⊙uCEl n X)
    uCF f =
      ((λ g → g ⊙∘ f) ,
       pair= idp (∙-unit-r _ ∙ ap-cst idp (snd f)))

    CF-hom : fst (X ⊙→ Y) → (C n Y →ᴳ C n X)
    CF-hom f = record {
      f = Trunc-fmap {n = ⟨0⟩} (fst (uCF f));
      pres-comp = Trunc-elim
        (λ _ → Π-level (λ _ → =-preserves-level _ Trunc-level))
        (λ g → Trunc-elim
          (λ _ → =-preserves-level _ Trunc-level)
          (λ h → ap [_] $ pair= idp (comp-snd _∙_ f g h)))}
      where
      comp-snd : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {C : Type k}
        {c₁ c₂ : C} (_⊕_ : C → C → C) (f : fst (X ⊙→ Y))
        (g : fst (Y ⊙→ ⊙[ C , c₁ ])) (h : fst (Y ⊙→ ⊙[ C , c₂ ]))
        → ap (λ x → fst g x ⊕ fst h x) (snd f) ∙ ap2 _⊕_ (snd g) (snd h)
          == ap2 _⊕_ (ap (fst g) (snd f) ∙ snd g) (ap (fst h) (snd f) ∙ snd h)
      comp-snd _⊕_ (_ , idp) (_ , idp) (_ , idp) = idp

    CF : fst (X ⊙→ Y) → fst (⊙CEl n Y ⊙→ ⊙CEl n X)
    CF F = GroupHom.⊙f (CF-hom F)

  {- CF-hom is a functor from pointed spaces to abelian groups -}
  module _ (n : ℤ) {X : Ptd i} where

    CF-ident : CF-hom n {X} {X} (⊙idf X) == idhom (C n X)
    CF-ident = hom= _ _ $ λ= $ Trunc-elim
      (λ _ → =-preserves-level _ Trunc-level)
      (λ _ → idp)

    CF-comp : {Y Z : Ptd i} (g : fst (Y ⊙→ Z)) (f : fst (X ⊙→ Y))
      → CF-hom n (g ⊙∘ f) == CF-hom n f ∘ᴳ CF-hom n g
    CF-comp g f = hom= _ _ $ λ= $ Trunc-elim
      (λ _ → =-preserves-level _ Trunc-level)
      (λ h → ap [_] (! (⊙∘-assoc h g f)))

  {- Eilenberg-Steenrod Axioms -}

  {- Suspension Axiom -}
  C-Susp : (n : ℤ) (X : Ptd i) → C (succ n) (⊙Susp X) == C n X
  C-Susp n X =
    group-ua (SuspAdjointLoopIso.iso X (E (succ (succ n))))
    ∙ ap (→Ω-Group X) (spectrum (succ n))

  {- Non-truncated Exactness Axiom -}
  module _ (n : ℤ) {X Y : Ptd i} where

    {- [uCF n (⊙cfcod f) ∘ uCF n f] is constant -}
    uC-exact-itok-lemma : (f : fst (X ⊙→ Y)) (g : uCEl n (⊙Cof f))
      → fst (uCF n f) (fst (uCF n (⊙cfcod f)) g) == uCid n X
    uC-exact-itok-lemma (f , fpt) (g , gpt) = ⊙λ=
      (λ x → ap g (! (cfglue f x)) ∙ gpt)
      (ap (g ∘ cfcod f) fpt
       ∙ ap g (ap (cfcod f) (! fpt) ∙ ! (cfglue f (snd X))) ∙ gpt
         =⟨ lemma (cfcod f) g fpt (! (cfglue f (snd X))) gpt ⟩
       ap g (! (cfglue f (snd X))) ∙ gpt
         =⟨ ! (∙-unit-r _) ⟩
       (ap g (! (cfglue f (snd X))) ∙ gpt) ∙ idp ∎)
      where
      lemma : ∀ {i j k} {A : Type i} {B : Type j} {C : Type k}
        {a₁ a₂ : A} {b : B} {c : C} (f : A → B) (g : B → C)
        (p : a₁ == a₂) (q : f a₁ == b) (r : g b == c)
        → ap (g ∘ f) p ∙ ap g (ap f (! p) ∙ q) ∙ r == ap g q ∙ r
      lemma f g idp idp idp = idp

    {- in kernel of [uCF n f] ⇒ in image of [uCF n (⊙cfcod f)] -}
    uC-exact-ktoi-lemma : (f : fst (X ⊙→ Y)) (g : uCEl n Y)
      → fst (uCF n f) g == uCid n X
      → Σ (uCEl n (⊙Cof f)) (λ h → fst (uCF n (⊙cfcod f)) h == g)
    uC-exact-ktoi-lemma (f , fpt) (h , hpt) p =
      ((g , ! q ∙ hpt) ,
       pair= idp (! (∙-assoc q (! q) hpt) ∙ ap (λ w → w ∙ hpt) (!-inv-r q)))
      where
      g : Cofiber f → Ω (E (succ n))
      g = CofiberRec.f f idp h (! ∘ app= (ap fst p))

      q : h (snd Y) == g (cfbase f)
      q = ap g (snd (⊙cfcod (f , fpt)))

  {- Truncated Exactness Axiom -}
  module _ (n : ℤ) {X Y : Ptd i} where

    {- in image of (CF n (⊙cfcod f)) ⇒ in kernel of (CF n f) -}
    abstract
      C-exact-itok : (f : fst (X ⊙→ Y))
        → is-exact-itok (CF n (⊙cfcod f)) (CF n f)
      C-exact-itok f =
        itok-alt-in (CF n (⊙cfcod f)) (CF n f) (Trunc-level {n = ⟨0⟩}) $
          Trunc-elim (λ _ → =-preserves-level _ (Trunc-level {n = ⟨0⟩}))
            (ap [_] ∘ uC-exact-itok-lemma n f)

    {- in kernel of (CF n f) ⇒ in image of (CF n (⊙cfcod f)) -}
    abstract
      C-exact-ktoi : (f : fst (X ⊙→ Y))
        → is-exact-ktoi (CF n (⊙cfcod f)) (CF n f)
      C-exact-ktoi f =
        Trunc-elim
          (λ _ → Π-level (λ _ → raise-level _ Trunc-level))
          (λ h tp → Trunc-rec Trunc-level (lemma h) (–> (Trunc=-equiv _ _) tp))
        where
        lemma : (h : uCEl n Y)
          → fst (uCF n f) h == uCid n X
          → Trunc ⟨-1⟩ (Σ (CEl n (⊙Cof f))
                          (λ tk → fst (CF n (⊙cfcod f)) tk == [ h ]))
        lemma h p = [ [ fst wit ] , ap [_] (snd wit) ]
          where
          wit : Σ (uCEl n (⊙Cof f)) (λ k → fst (uCF n (⊙cfcod f)) k == h)
          wit = uC-exact-ktoi-lemma n f h p

    C-exact : (f : fst (X ⊙→ Y)) → is-exact (CF n (⊙cfcod f)) (CF n f)
    C-exact f = record {itok = C-exact-itok f; ktoi = C-exact-ktoi f}

  {- Additivity Axiom -}
  module _ (n : ℤ) {A : Type i} (X : A → Ptd i)
    (ac : (W : A → Type i) → has-choice ⟨0⟩ A W)
    where

    uie : has-choice ⟨0⟩ A (uCEl n ∘ X)
    uie = ac (uCEl n ∘ X)

    R' : CEl n (⊙BigWedge X) → Trunc ⟨0⟩ (Π A (uCEl n ∘ X))
    R' = Trunc-rec Trunc-level (λ H → [ (λ a → H ⊙∘ ⊙bwin a) ])

    L' : Trunc ⟨0⟩ (Π A (uCEl n ∘ X)) → CEl n (⊙BigWedge X)
    L' = Trunc-rec Trunc-level
      (λ k → [ BigWedgeRec.f idp (fst ∘ k) (! ∘ snd ∘ k) , idp ])

    R = unchoose ∘ R'
    L = L' ∘ (is-equiv.g uie)

    R'-L' : ∀ y → R' (L' y) == y
    R'-L' = Trunc-elim
      (λ _ → =-preserves-level _ Trunc-level)
      (λ K → ap [_] (λ= (λ a → pair= idp $
        ap (BigWedgeRec.f idp (fst ∘ K) (! ∘ snd ∘ K)) (! (bwglue a)) ∙ idp
          =⟨ ∙-unit-r _ ⟩
        ap (BigWedgeRec.f idp (fst ∘ K) (! ∘ snd ∘ K)) (! (bwglue a))
          =⟨ ap-! (BigWedgeRec.f idp (fst ∘ K) (! ∘ snd ∘ K)) (bwglue a) ⟩
        ! (ap (BigWedgeRec.f idp (fst ∘ K) (! ∘ snd ∘ K)) (bwglue a))
          =⟨ ap ! (BigWedgeRec.glue-β idp (fst ∘ K) (! ∘ snd ∘ K) a) ⟩
        ! (! (snd (K a)))
          =⟨ !-! (snd (K a)) ⟩
        snd (K a) ∎)))

    L'-R' : ∀ x → L' (R' x) == x
    L'-R' = Trunc-elim
      {P = λ tH → L' (R' tH) == tH}
      (λ _ → =-preserves-level _ Trunc-level)
      (λ {(h , hpt) → ap [_] (pair=
         (λ= (L-R-fst (h , hpt)))
         (↓-app=cst-in $ ! $
            ap (λ w → w ∙ hpt) (app=-β (L-R-fst (h , hpt)) bwbase)
            ∙ !-inv-l hpt))})
      where
      lemma : ∀ {i j} {A : Type i} {B : Type j} (f : A → B)
        {a₁ a₂ : A} {b : B} (p : a₁ == a₂) (q : f a₁ == b)
        → ! q ∙ ap f p == ! (ap f (! p) ∙ q)
      lemma f idp idp = idp

      l∘r : fst (⊙BigWedge X ⊙→ ⊙Ω (E (succ n)))
        → (BigWedge X → Ω (E (succ n)))
      l∘r (h , hpt) =
        BigWedgeRec.f idp (λ a → h ∘ bwin a)
          (λ a → ! (ap h (! (bwglue a)) ∙ hpt))

      L-R-fst : (h : fst (⊙BigWedge X ⊙→ ⊙Ω (E (succ n))))
        → ∀ w → (l∘r h) w == fst h w
      L-R-fst (h , hpt) = BigWedge-elim
        (! hpt)
        (λ _ _ → idp)
        (λ a → ↓-='-in $
           ! hpt ∙ ap h (bwglue a)
             =⟨ lemma h (bwglue a) hpt ⟩
           ! (ap h (! (bwglue a)) ∙ hpt)
             =⟨ ! (BigWedgeRec.glue-β idp (λ a → h ∘ bwin a)
                     (λ a → ! (ap h (! (bwglue a)) ∙ hpt)) a) ⟩
           ap (l∘r (h , hpt)) (bwglue a) ∎)

    R-is-equiv : is-equiv R
    R-is-equiv = uie ∘ise (is-eq R' L' R'-L' L'-R')

    pres-comp : (tf tg : CEl n (⊙BigWedge X))
      → R (Group.comp (C n (⊙BigWedge X)) tf tg)
        == Group.comp (Πᴳ A (C n ∘ X)) (R tf) (R tg)
    pres-comp = Trunc-elim
      (λ _ → Π-level (λ _ → =-preserves-level _ (Π-level (λ _ → Trunc-level))))
      (λ {(f , fpt) → Trunc-elim
        (λ _ → =-preserves-level _ (Π-level (λ _ → Trunc-level)))
        (λ {(g , gpt) → λ= $ λ a → ap [_] $
          pair= idp (comp-snd f g (! (bwglue a)) fpt gpt)})})
      where
      comp-snd : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A} {b₀ : B}
        {p q : b₀ == b₀} (f : A → b₀ == b₀) (g : A → b₀ == b₀)
        (r : a₁ == a₂) (α : f a₂ == p) (β : g a₂ == q)
        → ap (λ x → f x ∙ g x) r ∙ ap2 _∙_ α β
          == ap2 _∙_ (ap f r ∙ α) (ap g r ∙ β)
      comp-snd f g idp idp idp = idp

    abstract
      C-additive : C n (⊙BigWedge X) == Πᴳ A (C n ∘ X)
      C-additive = group-ua (group-hom R pres-comp , R-is-equiv)

open SpectrumModel

spectrum-cohomology : CohomologyTheory i
spectrum-cohomology = record {
  C = C;
  CF-hom = CF-hom;
  CF-ident = CF-ident;
  CF-comp = CF-comp;
  C-abelian = C-abelian;
  C-Susp = C-Susp;
  C-exact = C-exact;
  C-additive = C-additive}

spectrum-C-S⁰ : (n : ℤ) → C n (⊙Sphere O) == π 1 (ℕ-S≠O _) (E (succ n))
spectrum-C-S⁰ n = Bool⊙→Ω-is-π₁ (E (succ n))


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

open import lib.Basics
open import lib.cubical.Square
open import lib.types.Group
open import lib.types.EilenbergMacLane1.Core

module lib.types.EilenbergMacLane1.PathElim where

module _ {i} (G : Group i) where

  private
    module G = Group G

  module EM₁Level₂PathElim {k} {C : Type k}
    {{C-level : has-level 2 C}}
    (f₁ f₂ : EM₁ G → C)
    (embase* : f₁ embase == f₂ embase)
    (emloop* : ∀ g →
      Square embase* (ap f₁ (emloop g))
             (ap f₂ (emloop g)) embase*)
    (emloop-comp* : ∀ g₁ g₂ →
      emloop* (G.comp g₁ g₂) ⊡v∙
      ap (ap f₂) (emloop-comp' G g₁ g₂)
      ==
      ap (ap f₁) (emloop-comp' G g₁ g₂) ∙v⊡
      ↓-='-square-comp (emloop* g₁) (emloop* g₂))
    where

    private
      module M =
        EM₁Level₁Elim {i} {G}
          {P = λ x → f₁ x == f₂ x}
          {{λ x → has-level-apply C-level _ _}}
          embase*
          (λ g → ↓-='-from-square (emloop* g))
          (λ g₁ g₂ →
            ↓-='-from-square-comp-path (emloop-comp g₁ g₂)
                                       (emloop* g₁)
                                       (emloop* g₂)
                                       (emloop* (G.comp g₁ g₂))
                                       (emloop-comp* g₁ g₂))

    open M public

  module EM₁Level₂PathConstElim {k} {C : Type k}
    {{C-level : has-level 2 C}}
    (f : EM₁ G → C) (c : C)
    (embase* : f embase == c)
    (emloop* : ∀ g →
      Square embase* (ap f (emloop g))
             idp embase*)
    (emloop-comp* : ∀ g₁ g₂ →
      emloop* (G.comp g₁ g₂)
      ==
      (ap (ap f) (emloop-comp' G g₁ g₂) ∙
       ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡
      (emloop* g₁ ⊡h emloop* g₂))
    where

    emloop** : ∀ g → Square embase* (ap f (emloop g))
                                    (ap (cst c) (emloop' G g)) embase*
    emloop** g = emloop* g ⊡v∙ ! (ap-cst c (emloop g))

    private

      emloop-comp** : ∀ g₁ g₂ →
        emloop** (G.comp g₁ g₂) ⊡v∙ ap (ap (cst c)) (emloop-comp' G g₁ g₂)
        ==
        ap (ap f) (emloop-comp g₁ g₂) ∙v⊡
        ↓-='-square-comp (emloop** g₁) (emloop** g₂)
      emloop-comp** g₁ g₂ =
        (emloop* (G.comp g₁ g₂) ⊡v∙ ! (ap-cst c (emloop (G.comp g₁ g₂)))) ⊡v∙ ap (ap (cst c)) (emloop-comp g₁ g₂)
          =⟨ ⊡v∙-assoc (emloop* (G.comp g₁ g₂))
                       (! (ap-cst c (emloop (G.comp g₁ g₂))))
                       (ap (ap (cst c)) (emloop-comp g₁ g₂)) ⟩
        emloop* (G.comp g₁ g₂) ⊡v∙
        (! (ap-cst c (emloop (G.comp g₁ g₂))) ∙
        ap (ap (cst c)) (emloop-comp g₁ g₂))
          =⟨ ap (emloop* (G.comp g₁ g₂) ⊡v∙_) $ =ₛ-out $
             ! (ap-cst c (emloop (G.comp g₁ g₂))) ◃∙
             ap (ap (cst c)) (emloop-comp g₁ g₂) ◃∎
               =ₛ⟨ pre-rotate'-in $
                   post-rotate-in {p = _ ◃∎} $
                   homotopy-naturality-to-cst (ap (cst c)) idp (ap-cst c) (emloop-comp g₁ g₂) ⟩
             ! (ap-cst c (emloop g₁ ∙ emloop g₂)) ◃∎
               =ₛ⟨ !-=ₛ (ap-cst-coh c (emloop g₁) (emloop g₂)) ⟩
             ! (ap2 _∙_ (ap-cst c (emloop g₁)) (ap-cst c (emloop g₂))) ◃∙
             ! (ap-∙ (cst c) (emloop g₁) (emloop g₂)) ◃∎
               =ₛ₁⟨ 0 & 1 & !-ap2 _∙_ (ap-cst c (emloop g₁)) (ap-cst c (emloop g₂)) ⟩
             ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))) ◃∙
             ! (ap-∙ (cst c) (emloop g₁) (emloop g₂)) ◃∎
               =ₛ₁⟨ 1 & 1 & !ap-∙=∙-ap (cst c) (emloop g₁) (emloop g₂) ⟩
             ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))) ◃∙
             ∙-ap (cst c) (emloop g₁) (emloop g₂) ◃∎ ∎ₛ
           ⟩
        emloop* (G.comp g₁ g₂) ⊡v∙
        (ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))) ∙
        ∙-ap (cst c) (emloop g₁) (emloop g₂))
          =⟨ ! $ ⊡v∙-assoc (emloop* (G.comp g₁ g₂))
                           (ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))))
                           (∙-ap (cst c) (emloop g₁) (emloop g₂)) ⟩
        (emloop* (G.comp g₁ g₂) ⊡v∙
        ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂)))) ⊡v∙
        ∙-ap (cst c) (emloop g₁) (emloop g₂)
          =⟨ ap (λ s → (s ⊡v∙
                       ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂)))) ⊡v∙
                       ∙-ap (cst c) (emloop g₁) (emloop g₂))
                (emloop-comp* g₁ g₂) ⟩
        (((ap (ap f) (emloop-comp' G g₁ g₂) ∙
        ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡
        emloop* g₁ ⊡h emloop* g₂) ⊡v∙
        ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂)))) ⊡v∙
        ∙-ap (cst c) (emloop g₁) (emloop g₂)
          =⟨ ap (_⊡v∙ ∙-ap (cst c) (emloop g₁) (emloop g₂)) $
             ((ap (ap f) (emloop-comp' G g₁ g₂) ∙
             ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡
             emloop* g₁ ⊡h emloop* g₂) ⊡v∙
             ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂)))
               =⟨ ∙v⊡-⊡v∙-comm
                    (ap (ap f) (emloop-comp' G g₁ g₂) ∙ ap-∙ f (emloop g₁) (emloop g₂))
                    (emloop* g₁ ⊡h emloop* g₂)
                    (ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂)))) ⟩
             (ap (ap f) (emloop-comp' G g₁ g₂) ∙
             ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡
             ((emloop* g₁ ⊡h emloop* g₂) ⊡v∙
             ap2 _∙_ (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))))
               =⟨ ap ((ap (ap f) (emloop-comp' G g₁ g₂) ∙
                       ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡_) $
                  ⊡h-⊡v∙-comm (emloop* g₁) (emloop* g₂)
                              (! (ap-cst c (emloop g₁))) (! (ap-cst c (emloop g₂))) ⟩
             (ap (ap f) (emloop-comp' G g₁ g₂) ∙
             ap-∙ f (emloop g₁) (emloop g₂)) ∙v⊡
             (emloop** g₁ ⊡h emloop** g₂)
               =⟨ ∙v⊡-assoc (ap (ap f) (emloop-comp' G g₁ g₂))
                            (ap-∙ f (emloop g₁) (emloop g₂))
                            (emloop** g₁ ⊡h emloop** g₂) ⟩
             ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡
             (ap-∙ f (emloop g₁) (emloop g₂) ∙v⊡
             (emloop** g₁ ⊡h emloop** g₂)) =∎
           ⟩
        (ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡
        (ap-∙ f (emloop g₁) (emloop g₂) ∙v⊡
        (emloop** g₁ ⊡h emloop** g₂))) ⊡v∙
        ∙-ap (cst c) (emloop g₁) (emloop g₂)
          =⟨ ∙v⊡-⊡v∙-comm (ap (ap f) (emloop-comp' G g₁ g₂))
                          (ap-∙ f (emloop g₁) (emloop g₂) ∙v⊡ (emloop** g₁ ⊡h emloop** g₂))
                          (∙-ap (cst c) (emloop g₁) (emloop g₂)) ⟩
        ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡
        (ap-∙ f (emloop g₁) (emloop g₂) ∙v⊡ emloop** g₁ ⊡h emloop** g₂) ⊡v∙ ∙-ap (cst c) (emloop g₁) (emloop g₂)
          =⟨ ap (ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡_) $
             ∙v⊡-⊡v∙-comm (ap-∙ f (emloop g₁) (emloop g₂))
                          (emloop** g₁ ⊡h emloop** g₂)
                          (∙-ap (cst c) (emloop g₁) (emloop g₂)) ⟩
        ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡
        ↓-='-square-comp' (emloop** g₁) (emloop** g₂)
          =⟨ ap (ap (ap f) (emloop-comp' G g₁ g₂) ∙v⊡_) $
             ↓-='-square-comp'=↓-='-square-comp {f = f} {g = cst c}
                                                (emloop** g₁) (emloop** g₂) ⟩
        ap (ap f) (emloop-comp g₁ g₂) ∙v⊡
        ↓-='-square-comp (emloop** g₁) (emloop** g₂) =∎

      module M =
        EM₁Level₂PathElim {k} {C}
          {{C-level}}
          f (cst c)
          embase*
          emloop**
          emloop-comp**

    open M public


-- Andreas, 2014-04-23 test case by Andrea Vezzosi
{-# OPTIONS --sized-types --copatterns #-}
-- {-# OPTIONS --show-implicit -v term:60 #-}
module _ where

open import Common.Size

-- Invalid coinductive record, since not recursive.
record ▸ (A : Size → Set) (i : Size) : Set where
  coinductive         -- This should be an error, since non-recursive.
  constructor delay_
  field
    force : ∀ {j : Size< i} → A j

open ▸

-- This fixed-point combinator should not pass the termination checker.
∞fix : ∀ {A : Size → Set} → (∀ {i} → ▸ A i → A i) → ∀ {i} → ▸ A i
force (∞fix {A} f {i}) {j} = f {j} (∞fix {A} f {j})

-- The following fixed-point combinator is not strongly normalizing!
fix : ∀ {A : Size → Set} → (∀ {i} → (∀ {j : Size< i} → A j) → A i) → ∀ {i} {j : Size< i} → A j
fix f = force (∞fix (λ {i} x → f {i} (force x)))

-- test = fix {!!}
-- C-c C-n test gives me a stack overflow


------------------------------------------------------------------------
-- The values that are used by the NBE algorithm
------------------------------------------------------------------------

import Level
open import Data.Universe

module README.DependentlyTyped.NBE.Value
  (Uni₀ : Universe Level.zero Level.zero)
  where

import Axiom.Extensionality.Propositional as E
open import Data.Product renaming (curry to c; uncurry to uc)
open import deBruijn.Substitution.Data
open import Function using (id; _ˢ_; _$_) renaming (const to k)
import README.DependentlyTyped.NormalForm as NF; open NF Uni₀
import README.DependentlyTyped.NormalForm.Substitution as NFS
open NFS Uni₀
import README.DependentlyTyped.Term as Term; open Term Uni₀
open import Relation.Binary.PropositionalEquality as P using (_≡_)
import Relation.Binary.PropositionalEquality.WithK as P

open P.≡-Reasoning

-- A wrapper which is used to make V̌alue "constructor-headed", which
-- in turn makes Agda infer more types for us.

infix 3 _⊢_⟨ne⟩

record _⊢_⟨ne⟩ (Γ : Ctxt) (σ : Type Γ) : Set where
  constructor [_]el
  field t : Γ ⊢ σ ⟨ ne ⟩

mutual

  -- The values.

  V̌alue′ : ∀ Γ sp (σ : IType Γ sp) → Set
  V̌alue′ Γ ⋆           σ = Γ ⊢ ⋆  , σ ⟨ ne ⟩
  V̌alue′ Γ el          σ = Γ ⊢ el , σ ⟨ne⟩
  V̌alue′ Γ (π sp₁ sp₂) σ =
    Σ (V̌alue-π Γ sp₁ sp₂ σ) (W̌ell-behaved sp₁ sp₂ σ)

  V̌alue : (Γ : Ctxt) (σ : Type Γ) → Set
  V̌alue Γ (sp , σ) = V̌alue′ Γ sp σ

  V̌alue-π : ∀ Γ sp₁ sp₂ → IType Γ (π sp₁ sp₂) → Set
  V̌alue-π Γ sp₁ sp₂ σ =
    (Γ₊ : Ctxt₊ Γ)
    (v : V̌alue′ (Γ ++₊ Γ₊) sp₁ (ifst σ /̂I ŵk₊ Γ₊)) →
    V̌alue′ (Γ ++₊ Γ₊) sp₂ (isnd σ /̂I ŵk₊ Γ₊ ↑̂ ∘̂ ŝub ⟦̌ v ⟧)

    -- The use of Ctxt₊ rather than Ctxt⁺ in V̌alue-π is important: it
    -- seems to make it much easier to define weakening for V̌alue.

  W̌ell-behaved :
    ∀ {Γ} sp₁ sp₂ σ → V̌alue-π Γ sp₁ sp₂ σ → Set
  W̌ell-behaved {Γ} sp₁ sp₂ σ f =
    ∀ Γ₊ v → (⟦̌ σ ∣ f ⟧-π /̂Val ŵk₊ Γ₊) ˢ ⟦̌ v ⟧ ≅-Value ⟦̌ f Γ₊ v ⟧

  -- The semantics of a value.

  ⟦̌_⟧ : ∀ {Γ sp σ} → V̌alue′ Γ sp σ → Value Γ (sp , σ)
  ⟦̌ v ⟧ = ⟦ řeify _ v ⟧n

  ⟦̌_∣_⟧-π : ∀ {Γ sp₁ sp₂} σ →
            V̌alue-π Γ sp₁ sp₂ σ → Value Γ (π sp₁ sp₂ , σ)
  ⟦̌ _ ∣ f ⟧-π = ⟦ řeify-π _ _ _ f ⟧n

  -- Neutral terms can be turned into normal terms using reflection
  -- followed by reification.

  ňeutral-to-normal :
    ∀ {Γ} sp {σ} → Γ ⊢ sp , σ ⟨ ne ⟩ → Γ ⊢ sp , σ ⟨ no ⟩
  ňeutral-to-normal sp t = řeify sp (řeflect sp t)

  -- A normal term corresponding to variable zero.

  žero : ∀ {Γ} sp σ → Γ ▻ (sp , σ) ⊢ sp , σ /̂I ŵk ⟨ no ⟩
  žero sp σ = ňeutral-to-normal sp (var zero[ -, σ ])

  -- Reification.

  řeify : ∀ {Γ} sp {σ} → V̌alue′ Γ sp σ → Γ ⊢ sp , σ ⟨ no ⟩
  řeify ⋆           t       = ne ⋆  t
  řeify el          [ t ]el = ne el t
  řeify (π sp₁ sp₂) f       = řeify-π sp₁ sp₂ _ (proj₁ f)

  řeify-π : ∀ {Γ} sp₁ sp₂ σ →
            V̌alue-π Γ sp₁ sp₂ σ → Γ ⊢ π sp₁ sp₂ , σ ⟨ no ⟩
  řeify-π {Γ} sp₁ sp₂ σ f = čast sp₁ σ $
    ƛ (řeify sp₂ (f (fst σ ◅ ε) (řeflect sp₁ (var zero))))

  čast : ∀ {Γ} sp₁ {sp₂} (σ : IType Γ (π sp₁ sp₂)) →
         let ρ̂ = ŵk ↑̂ ∘̂ ŝub ⟦ žero sp₁ (ifst σ) ⟧n in
         Γ ⊢ Type-π (fst σ) (snd σ /̂ ρ̂) ⟨ no ⟩ →
         Γ ⊢ -, σ ⟨ no ⟩
  čast {Γ} sp₁ σ =
    P.subst (λ σ → Γ ⊢ σ ⟨ no ⟩)
            (≅-Type-⇒-≡ $ π-fst-snd-ŵk-ŝub-žero sp₁ σ)

  -- Reflection.

  řeflect : ∀ {Γ} sp {σ} → Γ ⊢ sp , σ ⟨ ne ⟩ → V̌alue Γ (sp , σ)
  řeflect ⋆           t = t
  řeflect el          t = [ t ]el
  řeflect (π sp₁ sp₂) t =
    (λ Γ₊ v → řeflect sp₂ ((t /⊢n Renaming.wk₊ Γ₊) · řeify sp₁ v)) ,
    řeflect-π-well-behaved sp₁ sp₂ t

  abstract

    řeflect-π-well-behaved :
      ∀ {Γ} sp₁ sp₂ {σ} (t : Γ ⊢ π sp₁ sp₂ , σ ⟨ ne ⟩) Γ₊ v →
      let t′ = ňeutral-to-normal sp₂
                 ((t /⊢n Renaming.wk) · žero sp₁ (ifst σ)) in
      (⟦ čast sp₁ σ (ƛ t′) ⟧n /̂Val ŵk₊ Γ₊) ˢ ⟦̌ v ⟧
        ≅-Value
      ⟦ ňeutral-to-normal sp₂ ((t /⊢n Renaming.wk₊ Γ₊) · řeify sp₁ v) ⟧n
    řeflect-π-well-behaved sp₁ sp₂ {σ} t Γ₊ v =
      let t′ = ňeutral-to-normal sp₂
                 ((t /⊢n Renaming.wk) · žero sp₁ (ifst σ))
          v′ = řeify sp₁ v

          lemma′ = begin
            [ ⟦ čast sp₁ σ (ƛ t′) ⟧n /̂Val ŵk₊ Γ₊ ]  ≡⟨ /̂Val-cong (ňeutral-to-normal-identity-π sp₁ sp₂ t) P.refl ⟩
            [ ⟦ t ⟧n                 /̂Val ŵk₊ Γ₊ ]  ≡⟨ t /⊢n-lemma Renaming.wk₊ Γ₊ ⟩
            [ ⟦ t /⊢n Renaming.wk₊ Γ₊ ⟧n         ]  ∎

      in begin
      [ (⟦ čast sp₁ σ (ƛ t′) ⟧n /̂Val ŵk₊ Γ₊) ˢ ⟦ v′ ⟧n            ]  ≡⟨ ˢ-cong lemma′ P.refl ⟩
      [ ⟦ t /⊢n Renaming.wk₊ Γ₊ ⟧n           ˢ ⟦ v′ ⟧n            ]  ≡⟨ P.refl ⟩
      [ ⟦ (t /⊢n Renaming.wk₊ Γ₊) · v′ ⟧n                         ]  ≡⟨ P.sym $ ňeutral-to-normal-identity sp₂ _ ⟩
      [ ⟦ ňeutral-to-normal sp₂ ((t /⊢n Renaming.wk₊ Γ₊) · v′) ⟧n ]  ∎

    -- A given context morphism is equal to the identity.

    ŵk-ŝub-žero :
      ∀ {Γ} sp₁ {sp₂} (σ : IType Γ (π sp₁ sp₂)) →
      ŵk ↑̂ fst σ ∘̂ ŝub ⟦ žero sp₁ (ifst σ) ⟧n ≅-⇨̂ îd[ Γ ▻ fst σ ]
    ŵk-ŝub-žero sp₁ σ = begin
      [ ŵk ↑̂ ∘̂ ŝub ⟦ žero sp₁ (ifst σ) ⟧n ]  ≡⟨ ∘̂-cong (P.refl {x = [ ŵk ↑̂ ]})
                                                       (ŝub-cong (ňeutral-to-normal-identity sp₁ (var zero))) ⟩
      [ ŵk ↑̂ ∘̂ ŝub ⟦ var zero          ⟧n ]  ≡⟨ P.refl ⟩
      [ îd                                ]  ∎

    -- A corollary of the lemma above.

    π-fst-snd-ŵk-ŝub-žero :
      ∀ {Γ} sp₁ {sp₂} (σ : IType Γ (π sp₁ sp₂)) →
      Type-π (fst σ) (snd σ /̂ ŵk ↑̂ ∘̂ ŝub ⟦ žero sp₁ (ifst σ) ⟧n) ≅-Type
      (-, σ)
    π-fst-snd-ŵk-ŝub-žero sp₁ σ = begin
      [ Type-π (fst σ) (snd σ /̂ ŵk ↑̂ ∘̂ ŝub ⟦ žero sp₁ (ifst σ) ⟧n) ]  ≡⟨ Type-π-cong $ /̂-cong (P.refl {x = [ snd σ ]})
                                                                                (ŵk-ŝub-žero sp₁ σ) ⟩
      [ Type-π (fst σ) (snd σ)                                     ]  ≡⟨ P.refl ⟩
      [ -, σ                                                       ]  ∎

    -- In the semantics řeify is a left inverse of řeflect.

    ňeutral-to-normal-identity :
      ∀ {Γ} sp {σ} (t : Γ ⊢ sp , σ ⟨ ne ⟩) →
      ⟦ ňeutral-to-normal sp t ⟧n ≅-Value ⟦ t ⟧n
    ňeutral-to-normal-identity ⋆           t = P.refl
    ňeutral-to-normal-identity el          t = P.refl
    ňeutral-to-normal-identity (π sp₁ sp₂) t =
      ňeutral-to-normal-identity-π sp₁ sp₂ t

    ňeutral-to-normal-identity-π :
      ∀ {Γ} sp₁ sp₂ {σ} (t : Γ ⊢ π sp₁ sp₂ , σ ⟨ ne ⟩) →
      let t′ = ňeutral-to-normal sp₂
                 ((t /⊢n Renaming.wk) · žero sp₁ (ifst σ)) in
      ⟦ čast sp₁ σ (ƛ t′) ⟧n ≅-Value ⟦ t ⟧n
    ňeutral-to-normal-identity-π sp₁ sp₂ {σ} t =
      let t′ = (t /⊢n Renaming.wk) · žero sp₁ (ifst σ)

          lemma = begin
            [ ⟦ ňeutral-to-normal sp₂ t′ ⟧n                   ]  ≡⟨ ňeutral-to-normal-identity sp₂ t′ ⟩
            [ ⟦ t′ ⟧n                                         ]  ≡⟨ P.refl ⟩
            [ ⟦ t /⊢n Renaming.wk ⟧n ˢ ⟦ žero sp₁ (ifst σ) ⟧n ]  ≡⟨ ˢ-cong (P.sym $ t /⊢n-lemma Renaming.wk)
                                                                           (ňeutral-to-normal-identity sp₁ (var zero)) ⟩
            [ (⟦ t ⟧n /̂Val ŵk)       ˢ lookup zero            ]  ≡⟨ P.refl ⟩
            [ uc ⟦ t ⟧n                                       ]  ∎

      in begin
      [ ⟦ čast sp₁ σ (ƛ (ňeutral-to-normal sp₂ t′)) ⟧n ]  ≡⟨ ⟦⟧n-cong $ drop-subst-⊢n id (≅-Type-⇒-≡ $ π-fst-snd-ŵk-ŝub-žero sp₁ σ) ⟩
      [ c ⟦ ňeutral-to-normal sp₂ t′ ⟧n                ]  ≡⟨ curry-cong lemma ⟩
      [ c {C = k El ˢ isnd σ} (uc ⟦ t ⟧n)              ]  ≡⟨ P.refl ⟩
      [ ⟦ t ⟧n                                         ]  ∎

-- An immediate consequence of the somewhat roundabout definition
-- above.

w̌ell-behaved :
  ∀ {Γ sp₁ sp₂ σ} (f : V̌alue Γ (π sp₁ sp₂ , σ)) →
  ∀ Γ₊ v → (⟦̌_⟧ {σ = σ} f /̂Val ŵk₊ Γ₊) ˢ ⟦̌ v ⟧ ≅-Value ⟦̌ proj₁ f Γ₊ v ⟧
w̌ell-behaved = proj₂

-- Values are term-like.

V̌al : Term-like _
V̌al = record
  { _⊢_ = V̌alue
  ; ⟦_⟧ = ⟦̌_⟧
  }

open Term-like V̌al public
  using ([_])
  renaming ( _≅-⊢_ to _≅-V̌alue_
           ; drop-subst-⊢ to drop-subst-V̌alue; ⟦⟧-cong to ⟦̌⟧-cong
           )

abstract

  -- Unfolding lemma for ⟦̌_∣_⟧-π.

  unfold-⟦̌∣⟧-π :
    ∀ {Γ sp₁ sp₂} σ (f : V̌alue-π Γ sp₁ sp₂ σ) →
    ⟦̌ σ ∣ f ⟧-π ≅-Value c ⟦̌ f (fst σ ◅ ε) (řeflect sp₁ (var zero)) ⟧
  unfold-⟦̌∣⟧-π σ _ = ⟦⟧n-cong $
    drop-subst-⊢n id (≅-Type-⇒-≡ $ π-fst-snd-ŵk-ŝub-žero _ σ)

-- Some congruence/conversion lemmas.

≅-⊢n-⇒-≅-Value-⋆ : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ ⋆ , σ₁ ⟨ ne ⟩}
                     {Γ₂ σ₂} {t₂ : Γ₂ ⊢ ⋆ , σ₂ ⟨ ne ⟩} →
                   t₁ ≅-⊢n t₂ → t₁ ≅-V̌alue t₂
≅-⊢n-⇒-≅-Value-⋆ P.refl = P.refl

≅-Value-⋆-⇒-≅-⊢n : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ ⋆ , σ₁ ⟨ ne ⟩}
                     {Γ₂ σ₂} {t₂ : Γ₂ ⊢ ⋆ , σ₂ ⟨ ne ⟩} →
                   t₁ ≅-V̌alue t₂ → t₁ ≅-⊢n t₂
≅-Value-⋆-⇒-≅-⊢n P.refl = P.refl

≅-⊢n-⇒-≅-Value-el : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ el , σ₁ ⟨ ne ⟩}
                      {Γ₂ σ₂} {t₂ : Γ₂ ⊢ el , σ₂ ⟨ ne ⟩} →
                    t₁ ≅-⊢n t₂ → [ t₁ ]el ≅-V̌alue [ t₂ ]el
≅-⊢n-⇒-≅-Value-el P.refl = P.refl

≅-Value-el-⇒-≅-⊢n : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ el , σ₁ ⟨ ne ⟩}
                      {Γ₂ σ₂} {t₂ : Γ₂ ⊢ el , σ₂ ⟨ ne ⟩} →
                    [ t₁ ]el ≅-V̌alue [ t₂ ]el → t₁ ≅-⊢n t₂
≅-Value-el-⇒-≅-⊢n P.refl = P.refl

abstract

  ,-cong : E.Extensionality Level.zero Level.zero →
           ∀ {Γ sp₁ sp₂ σ} {f₁ f₂ : V̌alue Γ (π sp₁ sp₂ , σ)} →
           (∀ Γ₊ v → proj₁ f₁ Γ₊ v ≅-V̌alue proj₁ f₂ Γ₊ v) →
           _≅-V̌alue_ {σ₁ = (π sp₁ sp₂ , σ)} f₁
                     {σ₂ = (π sp₁ sp₂ , σ)} f₂
  ,-cong ext hyp = P.cong (Term-like.[_] {_} {V̌al}) $
    ,-cong′ (ext λ Γ₊ → ext λ v → Term-like.≅-⊢-⇒-≡ V̌al $ hyp Γ₊ v)
            (ext λ _  → ext λ _ → P.≡-irrelevant _ _)
    where
    ,-cong′ : {A : Set} {B : A → Set}
              {x₁ x₂ : A} {y₁ : B x₁} {y₂ : B x₂} →
              (eq : x₁ ≡ x₂) → P.subst B eq y₁ ≡ y₂ →
              _≡_ {A = Σ A B} (x₁ , y₁) (x₂ , y₂)
    ,-cong′ P.refl P.refl = P.refl

ňeutral-to-normal-cong :
  ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ σ₁ ⟨ ne ⟩}
    {Γ₂ σ₂} {t₂ : Γ₂ ⊢ σ₂ ⟨ ne ⟩} →
  t₁ ≅-⊢n t₂ → ňeutral-to-normal _ t₁ ≅-⊢n ňeutral-to-normal _ t₂
ňeutral-to-normal-cong P.refl = P.refl

žero-cong : ∀ {Γ₁} {σ₁ : Type Γ₁}
              {Γ₂} {σ₂ : Type Γ₂} →
            σ₁ ≅-Type σ₂ → žero _ (proj₂ σ₁) ≅-⊢n žero _ (proj₂ σ₂)
žero-cong P.refl = P.refl

řeify-cong : ∀ {Γ₁ σ₁} {v₁ : V̌alue Γ₁ σ₁}
               {Γ₂ σ₂} {v₂ : V̌alue Γ₂ σ₂} →
             v₁ ≅-V̌alue v₂ → řeify _ v₁ ≅-⊢n řeify _ v₂
řeify-cong P.refl = P.refl

řeflect-cong : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ σ₁ ⟨ ne ⟩}
                 {Γ₂ σ₂} {t₂ : Γ₂ ⊢ σ₂ ⟨ ne ⟩} →
               t₁ ≅-⊢n t₂ → řeflect _ t₁ ≅-V̌alue řeflect _ t₂
řeflect-cong P.refl = P.refl


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Homotopy.Connected where

open import Cubical.Core.Everything
open import Cubical.Foundations.Everything
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Path
open import Cubical.Foundations.Univalence
open import Cubical.Functions.Fibration
open import Cubical.Data.Nat
open import Cubical.Data.Prod hiding (map)
open import Cubical.Data.Sigma hiding (_×_)
open import Cubical.HITs.Nullification
open import Cubical.HITs.Susp
open import Cubical.HITs.SmashProduct
open import Cubical.HITs.Truncation as Trunc renaming (rec to trRec)
open import Cubical.Homotopy.Loopspace
open import Cubical.HITs.Pushout
open import Cubical.HITs.Sn.Base
open import Cubical.HITs.S1
open import Cubical.Data.Bool
open import Cubical.Data.Unit

-- Note that relative to most sources, this notation is off by +2
isConnected : ∀ {ℓ} (n : HLevel) (A : Type ℓ) → Type ℓ
isConnected n A = isContr (hLevelTrunc n A)

isConnectedFun : ∀ {ℓ ℓ'} (n : HLevel) {A : Type ℓ} {B : Type ℓ'} (f : A → B) → Type (ℓ-max ℓ ℓ')
isConnectedFun n f = ∀ b → isConnected n (fiber f b)

isTruncatedFun : ∀ {ℓ ℓ'} (n : HLevel) {A : Type ℓ} {B : Type ℓ'} (f : A → B) → Type (ℓ-max ℓ ℓ')
isTruncatedFun n f = ∀ b → isOfHLevel n (fiber f b)

isConnectedSubtr : ∀ {ℓ} {A : Type ℓ} (n m : HLevel)
                → isConnected (m + n) A
                → isConnected n A
isConnectedSubtr {A = A} n m iscon =
  isOfHLevelRetractFromIso 0 (truncOfTruncIso n m) (helper n iscon)
  where
  helper : (n : ℕ) → isConnected (m + n) A → isContr (hLevelTrunc n (hLevelTrunc (m + n) A))
  helper zero iscon = isContrUnit*
  helper (suc n) iscon = ∣ iscon .fst ∣ , (Trunc.elim (λ _ → isOfHLevelPath (suc n) (isOfHLevelTrunc (suc n)) _ _) λ a → cong ∣_∣ (iscon .snd a))

isConnectedFunSubtr : ∀ {ℓ ℓ'} {A : Type ℓ} {B : Type ℓ'} (n m : HLevel) (f : A → B)
                → isConnectedFun (m + n) f
                → isConnectedFun n f
isConnectedFunSubtr n m f iscon b = isConnectedSubtr n m (iscon b)

private
  typeToFiberIso : ∀ {ℓ} (A : Type ℓ) → Iso A (fiber (λ (x : A) → tt) tt)
  Iso.fun (typeToFiberIso A) x = x , refl
  Iso.inv (typeToFiberIso A) = fst
  Iso.rightInv (typeToFiberIso A) b i = fst b , (isOfHLevelSuc 1 (isPropUnit) tt tt (snd b) refl) i
  Iso.leftInv (typeToFiberIso A) a = refl

  typeToFiber : ∀ {ℓ} (A : Type ℓ) → A ≡ fiber (λ (x : A) → tt) tt
  typeToFiber A = isoToPath (typeToFiberIso A)

module elim {ℓ ℓ' : Level} {A : Type ℓ} {B : Type ℓ'} (f : A → B) where
  private
    inv : ∀ {ℓ'''} (n : HLevel) (P : B → TypeOfHLevel ℓ''' (suc n))
        → ((a : A) → P (f a) .fst)
        → (b : B)
        → hLevelTrunc (suc n) (fiber f b) → P b .fst
    inv n P t b =
      Trunc.rec
        (P b .snd)
        (λ {(a , p) → subst (fst ∘ P) p (t a)})

  isIsoPrecompose : ∀ {ℓ'''} (n : ℕ) (P : B → TypeOfHLevel ℓ''' n)
                   → isConnectedFun n f
                   → Iso ((b : B) → P b .fst) ((a : A) → P (f a) .fst)
  isIsoPrecompose zero P fConn = isContr→Iso (isOfHLevelΠ _ (λ b → P b .snd)) (isOfHLevelΠ _ λ a → P (f a) .snd)
  Iso.fun (isIsoPrecompose (suc n) P fConn) = _∘ f
  Iso.inv (isIsoPrecompose (suc n) P fConn) t b = inv n P t b (fConn b .fst)
  Iso.rightInv (isIsoPrecompose (suc n) P fConn) t =
    funExt λ a → cong (inv n P t (f a)) (fConn (f a) .snd ∣ a , refl ∣)
               ∙ substRefl {B = fst ∘ P} (t a)
  Iso.leftInv (isIsoPrecompose (suc n) P fConn) s =
    funExt λ b →
          Trunc.elim
            {B = λ d → inv n P (s ∘ f) b d ≡ s b}
            (λ _ → isOfHLevelPath (suc n) (P b .snd) _ _)
            (λ {(a , p) i → transp (λ j → P (p (j ∨ i)) .fst) i (s (p i))})
            (fConn b .fst)

  isEquivPrecompose : ∀ {ℓ'''} (n : ℕ) (P : B → TypeOfHLevel ℓ''' n)
                   → isConnectedFun n f
                   → isEquiv (λ(s : (b : B) → P b .fst) → s ∘ f)
  isEquivPrecompose zero P fConn = isoToIsEquiv theIso
    where
    theIso : Iso ((b : B) → P b .fst) ((a : A) → P (f a) .fst)
    Iso.fun theIso = λ(s : (b : B) → P b .fst) → s ∘ f
    Iso.inv theIso = λ _ b → P b .snd .fst
    Iso.rightInv theIso g = funExt λ x → P (f x) .snd .snd (g x)
    Iso.leftInv theIso g = funExt λ x → P x .snd .snd (g x)
  isEquivPrecompose (suc n) P fConn = isoToIsEquiv (isIsoPrecompose (suc n) P fConn)

  isConnectedPrecompose : (n : ℕ) → ((P : B → TypeOfHLevel (ℓ-max ℓ ℓ') n)
                                    → hasSection (λ(s : (b : B) → P b .fst) → s ∘ f))
                       → isConnectedFun n f
  isConnectedPrecompose zero P→sect b = isContrUnit*
  isConnectedPrecompose (suc n) P→sect b = c n P→sect b , λ y →  sym (fun n P→sect b y)
    where
    P : (n : HLevel) → ((P : B → TypeOfHLevel ℓ (suc n))
     → hasSection (λ(s : (b : B) → P b .fst) → s ∘ f))
     → B → Type _
    P n s b = hLevelTrunc (suc n) (fiber f b)

    c : (n : HLevel) → ((P : B → TypeOfHLevel (ℓ-max ℓ ℓ') (suc n))
     → hasSection (λ(s : (b : B) → P b .fst) → s ∘ f)) → (b : B)
     → hLevelTrunc (suc n) (fiber f b)
    c n s = (s λ b → (hLevelTrunc (suc n) (fiber f b) , isOfHLevelTrunc _)) .fst
              λ a → ∣ a , refl ∣

    fun : (n : HLevel) (P→sect : ((P : B → TypeOfHLevel (ℓ-max ℓ ℓ') (suc n))
                     → hasSection λ(s : (b : B) → P b .fst) → s ∘ f))
      → (b : B) (w : (hLevelTrunc (suc n) (fiber f b)))
      → w ≡ c n P→sect b
    fun n P→sect b = Trunc.elim (λ x → isOfHLevelPath (suc n) (isOfHLevelTrunc _) _ _)
                                       λ a → J (λ b p → ∣ (fst a) , p ∣ ≡ c n P→sect b)
                                               (c* (fst a))
                                               (snd a)
        where
        c* : ((a : A) → ∣ (a , refl {x = f a}) ∣ ≡ c n P→sect (f a))
        c* a = sym (cong (λ x → x a) (P→sect (λ b → hLevelTrunc (suc n) (fiber f b) , isOfHLevelTrunc _) .snd λ a → ∣ a , refl ∣))

isOfHLevelPrecomposeConnected : ∀ {ℓ ℓ' ℓ''} (k : HLevel) (n : HLevel)
  {A : Type ℓ} {B : Type ℓ'} (P : B → TypeOfHLevel ℓ'' (k + n)) (f : A → B)
  → isConnectedFun n f
  → isOfHLevelFun k (λ(s : (b : B) → P b .fst) → s ∘ f)
isOfHLevelPrecomposeConnected zero n P f fConn =
  elim.isEquivPrecompose f n P fConn .equiv-proof
isOfHLevelPrecomposeConnected (suc k) n P f fConn t =
  isOfHLevelPath'⁻ k
    λ {(s₀ , p₀) (s₁ , p₁) →
      isOfHLevelRetractFromIso k (invIso ΣPathIsoPathΣ)
         (subst (isOfHLevel k)
           (sym (fiberPath (s₀ , p₀) (s₁ , p₁)))
           (isOfHLevelRetract k
            (λ {(q , α) → (funExt⁻ q) , (cong funExt⁻ α)})
            (λ {(h , β) → (funExt h) , (cong funExt β)})
            (λ _ → refl)
            (isOfHLevelPrecomposeConnected k n
              (λ b → (s₀ b ≡ s₁ b) , isOfHLevelPath' (k + n) (P b .snd) _ _)
              f fConn
              (funExt⁻ (p₀ ∙∙ refl ∙∙ sym p₁)))))}

indMapEquiv→conType : ∀ {ℓ} {A : Type ℓ} (n : HLevel)
                   → ((B : TypeOfHLevel ℓ n)
                      → isEquiv (λ (b : (fst B)) → λ (a : A) → b))
                   → isConnected n A
indMapEquiv→conType {A = A} zero BEq = isContrUnit*
indMapEquiv→conType {A = A} (suc n) BEq =
  isOfHLevelRetractFromIso 0 (mapCompIso {n = (suc n)} (typeToFiberIso A))
    (elim.isConnectedPrecompose (λ _ → tt) (suc n)
                                (λ P → ((λ a _ → a) ∘ invIsEq (BEq (P tt)))
                               , λ a → equiv-proof (BEq (P tt)) a .fst .snd)
                                tt)

isConnectedPath : ∀ {ℓ} (n : HLevel) {A : Type ℓ}
  → isConnected (suc n) A
  → (a₀ a₁ : A) → isConnected n (a₀ ≡ a₁)
isConnectedPath zero connA a₀ a₁ = isContrUnit*
isConnectedPath (suc n) connA a₀ a₁ =
  isOfHLevelRetractFromIso 0
    (invIso (PathIdTruncIso (suc n)))
    (isContr→isContrPath connA _ _)

isConnectedPathP : ∀ {ℓ} (n : HLevel) {A : I → Type ℓ}
  → isConnected (suc n) (A i1)
  → (a₀ : A i0) (a₁ : A i1) → isConnected n (PathP A a₀ a₁)
isConnectedPathP n con a₀ a₁ =
  subst (isConnected n) (sym (PathP≡Path _ _ _))
        (isConnectedPath n con _ _)

isConnectedRetract : ∀ {ℓ ℓ'} (n : HLevel)
  {A : Type ℓ} {B : Type ℓ'}
  (f : A → B) (g : B → A)
  (h : (x : A) → g (f x) ≡ x)
  → isConnected n B → isConnected n A
isConnectedRetract zero _ _ _ _ = isContrUnit*
isConnectedRetract (suc n) f g h =
  isContrRetract
    (Trunc.map f)
    (Trunc.map g)
    (Trunc.elim
      (λ _ → isOfHLevelPath (suc n) (isOfHLevelTrunc (suc n)) _ _)
      (λ a → cong ∣_∣ (h a)))

isConnectedRetractFromIso :  ∀ {ℓ ℓ'} (n : HLevel)
    {A : Type ℓ} {B : Type ℓ'}
  → Iso A B
  → isConnected n B → isConnected n A
isConnectedRetractFromIso n e =
  isConnectedRetract n
    (Iso.fun e)
    (Iso.inv e)
    (Iso.leftInv e)

isConnectedPoint : ∀ {ℓ} (n : HLevel) {A : Type ℓ}
  → isConnected (suc n) A
  → (a : A) → isConnectedFun n (λ(_ : Unit) → a)
isConnectedPoint n connA a₀ a =
  isConnectedRetract n
    snd (_ ,_) (λ _ → refl)
    (isConnectedPath n connA a₀ a)

isConnectedPoint2 : ∀ {ℓ} (n : HLevel) {A : Type ℓ} (a : A)
   → isConnectedFun n (λ(_ : Unit) → a)
   → isConnected (suc n) A
isConnectedPoint2 n {A = A} a connMap = indMapEquiv→conType _ λ B → isoToIsEquiv (theIso B)
  where
  module _  {ℓ' : Level} (B : TypeOfHLevel ℓ' (suc n))
    where
    helper : (f : A → fst B) → (a2 : A) → f a2 ≡ f a
    helper f = equiv-proof (elim.isEquivPrecompose (λ _ → a) n (λ a2 → (f a2 ≡ f a) ,
                             isOfHLevelPath' n (snd B) (f a2) (f a)) connMap) (λ _ → refl) .fst .fst

    theIso : Iso (fst B) (A → fst B)
    Iso.fun theIso b a = b
    Iso.inv theIso f = f a
    Iso.rightInv theIso f = funExt λ y → sym (helper f y)
    Iso.leftInv theIso _ = refl

connectedTruncIso : ∀ {ℓ} {A B : Type ℓ} (n : HLevel) (f : A → B)
                   → isConnectedFun n f
                   → Iso (hLevelTrunc n A) (hLevelTrunc n B)
connectedTruncIso {A = A} {B = B} zero f con = isContr→Iso isContrUnit* isContrUnit*
connectedTruncIso {A = A} {B = B} (suc n) f con = g
  where
  back : B → hLevelTrunc (suc n) A
  back y = map fst ((con y) .fst)

  backSection :  (b : B) → Path (hLevelTrunc (suc n) B)
                                (Trunc.rec (isOfHLevelTrunc (suc n))
                                           (λ a → ∣ f a ∣)
                                           (Trunc.rec {n = n}
                                                      {B = hLevelTrunc (suc n) A} (isOfHLevelTrunc (suc n))
                                                      back ∣ b ∣))
                               ∣ b ∣
  backSection b = helper (λ p → map f p ≡ ∣ b ∣)
                         (λ x → (isOfHLevelSuc (suc n) (isOfHLevelTrunc (suc n)))
                                   (map f (map fst x)) ∣ b ∣)
                         (λ a p → cong ∣_∣ p)
                         (fst (con b))
    where
    helper : ∀ {ℓ ℓ' ℓ''} {A : Type ℓ} {B : A → Type ℓ'} (P : hLevelTrunc (suc n) A → Type ℓ'')
           → ((x : hLevelTrunc (suc n) (Σ A B)) → isOfHLevel (suc n) (P (map fst x)))
           → ((a : A) (b : B a) → P ∣ a ∣)
           → (p : hLevelTrunc (suc n) (Σ A B))
           →  P (map fst p)
    helper P hlev pf = Trunc.elim hlev λ pair → pf (fst pair) (snd pair)

  g : Iso (hLevelTrunc (suc n) A) (hLevelTrunc (suc n) B)
  Iso.fun g = map f
  Iso.inv g = Trunc.rec (isOfHLevelTrunc _) back
  Iso.leftInv g = Trunc.elim (λ x → isOfHLevelPath (suc n) (isOfHLevelTrunc _) _ _)
                               λ a → cong (map fst) (con (f a) .snd  ∣ a , refl ∣)
  Iso.rightInv g = Trunc.elim (λ x → isOfHLevelPath (suc n) (isOfHLevelTrunc _) _ _)
                              backSection

connectedTruncIso2 : ∀ {ℓ} {A B : Type ℓ} (n m : HLevel) (f : A → B)
                   → Σ[ x ∈ ℕ ] x + n ≡ m
                   → isConnectedFun m f
                   → Iso (hLevelTrunc n A) (hLevelTrunc n B)
connectedTruncIso2 {A = A} {B = B} n m f (x , pf) con =
  connectedTruncIso n f (isConnectedFunSubtr n x f (transport (λ i → isConnectedFun (pf (~ i)) f) con))

connectedTruncEquiv : ∀ {ℓ} {A B : Type ℓ} (n : HLevel) (f : A → B)
                   → isConnectedFun n f
                   → hLevelTrunc n A ≃ hLevelTrunc n B
connectedTruncEquiv {A = A} {B = B} n f con = isoToEquiv (connectedTruncIso n f con)

-- TODO : Reorganise the following proofs.

inrConnected : ∀ {ℓ ℓ' ℓ''} {A : Type ℓ} {B : Type ℓ'} {C : Type ℓ''} (n : HLevel)
            → (f : C → A) (g : C → B)
            → isConnectedFun n f
            → isConnectedFun n {A = B} {B = Pushout f g} inr
inrConnected {A = A} {B = B} {C = C} n f g iscon =
  elim.isConnectedPrecompose inr n λ P → (k P) , λ b → refl
  where
  module _ {ℓ : Level} (P : (Pushout f g) → TypeOfHLevel ℓ n)
                   (h : (b : B) → typ (P (inr b)))
    where
    Q : A → TypeOfHLevel _ n
    Q a = (P (inl a))

    fun : (c : C) → typ (Q (f c))
    fun c = transport (λ i → typ (P (push c (~ i)))) (h (g c))

    k : (d : Pushout f g) → typ (P d)
    k (inl x) = equiv-proof (elim.isEquivPrecompose f n Q iscon) fun .fst .fst x
    k (inr x) = h x
    k (push a i) =
      hcomp (λ k → λ { (i = i0) → equiv-proof (elim.isEquivPrecompose f n Q iscon) fun .fst .snd i0 a
                     ; (i = i1) → transportTransport⁻ (λ j → typ (P (push a j))) (h (g a)) k })
            (transp (λ j → typ (P (push a (i ∧ j))))
                    (~ i)
                    (equiv-proof (elim.isEquivPrecompose f n Q iscon)
                                 fun .fst .snd i a))


------------------------------------------------------------------------
-- A virtual machine
------------------------------------------------------------------------

open import Prelude

import Lambda.Virtual-machine.Instructions

module Lambda.Virtual-machine
  {Name : Type}
  (open Lambda.Virtual-machine.Instructions Name)
  (def : Name → Code 1)
  where

open import Equality.Propositional

open import Colist equality-with-J as Colist using (Colist)
open import List equality-with-J using (_++_; length)
open import Monad equality-with-J
open import Vec.Data equality-with-J

open import Lambda.Delay-crash using (Delay-crash)
open import Lambda.Delay-crash-trace
open import Lambda.Syntax Name

open Closure Code

-- A single step of the computation.

step : State → Result
step ⟨ var x     ∷ c ,                           s  , ρ  ⟩ = continue ⟨ c       , val (index ρ x) ∷ s ,     ρ  ⟩
step ⟨ clo c′    ∷ c ,                           s  , ρ  ⟩ = continue ⟨ c       , val (lam c′ ρ)  ∷ s ,     ρ  ⟩
step ⟨ app       ∷ c , val v ∷ val (lam c′ ρ′) ∷ s  , ρ  ⟩ = continue ⟨ c′      , ret c ρ         ∷ s , v ∷ ρ′ ⟩
step ⟨ ret       ∷ c , val v ∷       ret c′ ρ′ ∷ s  , ρ  ⟩ = continue ⟨ c′      , val v           ∷ s ,     ρ′ ⟩
step ⟨ cal f     ∷ c ,                   val v ∷ s  , ρ  ⟩ = continue ⟨ def f   , ret c ρ         ∷ s , v ∷ [] ⟩
step ⟨ tcl f     ∷ c ,                   val v ∷ s  , ρ  ⟩ = continue ⟨ def f   ,                   s , v ∷ [] ⟩
step ⟨ con b     ∷ c ,                           s  , ρ  ⟩ = continue ⟨ c       , val (con b)     ∷ s ,     ρ  ⟩
step ⟨ bra c₁ c₂ ∷ c ,         val (con true)  ∷ s  , ρ  ⟩ = continue ⟨ c₁ ++ c ,                   s ,     ρ  ⟩
step ⟨ bra c₁ c₂ ∷ c ,         val (con false) ∷ s  , ρ  ⟩ = continue ⟨ c₂ ++ c ,                   s ,     ρ  ⟩
step ⟨ []            ,                   val v ∷ [] , [] ⟩ = done v
step _                                                     = crash

-- A functional semantics for the VM. The result includes a trace of
-- all the encountered states.

mutual

  exec⁺ : ∀ {i} → State → Delay-crash-trace State Value i
  exec⁺ s = later s λ { .force → exec⁺′ (step s) }

  exec⁺′ : ∀ {i} → Result → Delay-crash-trace State Value i
  exec⁺′ (continue s) = exec⁺ s
  exec⁺′ (done v)     = now v
  exec⁺′ crash        = crash

-- The semantics without the trace of states.

exec : ∀ {i} → State → Delay-crash Value i
exec = delay-crash ∘ exec⁺

-- The stack sizes of all the encountered states.

stack-sizes : ∀ {i} → State → Colist ℕ i
stack-sizes =
  Colist.map (λ { ⟨ _ , s , _ ⟩ → length s }) ∘ trace ∘ exec⁺


-- Andreas, 2018-06-12, issue #2170
-- Reported by tomjack

{-# OPTIONS --irrelevant-projections --subtyping #-}

open import Agda.Builtin.Equality
open import Agda.Builtin.Bool

infixr 42 _,_
record Σ (A : Set) (B : A → Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B fst
open Σ

record Irr (A : Set) : Set where
  constructor irr
  field
    .unirr : A
open Irr

-- Usually,  unirr (irr a) = .(a)  which is  DontCare a  in internal syntax.
-- The DontCare protection of an irrelevant field could be circumvented
-- using subtyping  irr : (.Bool → Irr Bool) ≤ (Bool → Irr Bool):
-- The argument  a  in  (i a)  would not have ArgInfo Irrelevant, even
-- after substitution irr/i.  Since the DontCare came from the ArgInfo of a,
-- there was none.  Now, the record constructor name also carries ArgInfo for
-- each of its fields, which is used to produce a DontCare.

bizarre : Irr (Σ (Bool → Irr Bool) (λ i → Σ (Irr Bool → Bool) (λ u → (a : Bool) → u (i a) ≡ a)))
bizarre = irr (irr , unirr , λ a → refl)  -- Should not pass

-- Expected error:
-- .(a) != a of type Bool
-- when checking that the expression refl has type .(a) ≡ a

-- The rest is proving False.

data ⊥ : Set where

¬_ : Set → Set
¬ A = (A → ⊥)

true≠false : ¬ (true ≡ false)
true≠false ()

infixr 30 _∙_
_∙_ : {A : Set} {x y z : A} → x ≡ y → y ≡ z → x ≡ z
refl ∙ q = q

infix 42 !_
!_ : {A : Set} {x y : A} → x ≡ y → y ≡ x
! refl = refl

map-irr : {A B : Set} → (A → B) → Irr A → Irr B
map-irr f (irr x) = irr (f x)

factivity : ¬ (Irr ⊥)
factivity (irr ())

bad : Irr (true ≡ false)
bad = map-irr (λ { (i , u , s) → ! (s true) ∙ s false }) bizarre

boom : ⊥
boom = factivity (map-irr true≠false bad)

-- the hole here is is .(a) ≡ a
-- bizarre' : Irr (Σ (Irr Bool → Bool) (λ u → (a : Bool) → u (irr a) ≡ a))
-- bizarre' = irr (unirr , (λ a → {!!}))

-- also fails, unirr (irr a) = .(a)
-- bizarre' : Irr (Σ (Bool → Irr Bool) (λ i → Σ (Irr Bool → Bool) (λ u → (a : Bool) → u (i a) ≡ a)))
-- bizarre' = irr (irr , unirr , (λ a → refl {x = unirr (irr a)})


module Cats.Util.Simp where

open import Data.List using (List ; [] ; _∷_)
open import Data.Nat using (ℕ ; zero ; suc)
open import Data.Product using (_×_ ; _,_ ; ∃-syntax)
open import Data.Unit using (⊤)
open import Function using () renaming (_∘_ to _⊚_)
open import Level using (_⊔_) renaming (zero to lzero)
open import Relation.Binary.PropositionalEquality as ≡ using
  (_≡_ ; inspect ; [_])

open import Cats.Category.Base
open import Cats.Util.Reflection

_>>=_ = bindTC
return = returnTC

_>>_ : ∀ {a} {A : Set a} {b} {B : Set b} → TC A → TC B → TC B
ma >> mb = ma >>= λ _ → mb

module _ {lo la l≈} (C : Category lo la l≈) where

  infixr 9 _∘_

  private
    module C = Category C
    module ≈ = C.≈
    open C.≈-Reasoning

  data Tm : C.Obj → C.Obj → Set (lo ⊔ la) where
    var : ∀ {A B} → A C.⇒ B → Tm A B
    id : ∀ {A} → Tm A A
    _∘_ : ∀ {A B C} → Tm B C → Tm A B → Tm A C

  ⟦_⟧ : ∀ {A B} → Tm A B → A C.⇒ B
  ⟦ var f ⟧ = f
  ⟦ id ⟧ = C.id
  ⟦ f ∘ g ⟧ = ⟦ f ⟧ C.∘ ⟦ g ⟧

  Method : Set (lo ⊔ la)
  Method = ∀ {A B} → Tm A B → Tm A B

  Correct : Method → Set (lo ⊔ la ⊔ l≈)
  Correct f = ∀ {A B} (t : Tm A B) → ⟦ t ⟧ C.≈ ⟦ f t ⟧

  Correct→Injective : (f : Method) → Correct f → ∀ {A B} (t u : Tm A B)
    → ⟦ f t ⟧ C.≈ ⟦ f u ⟧
    → ⟦ t ⟧ C.≈ ⟦ u ⟧
  Correct→Injective f fcor t u eq
      = ≈.trans (fcor t) (≈.trans eq (≈.sym (fcor u)))

  ⊚-correct : (f g : Method) → Correct f → Correct g → Correct (f ⊚ g)
  ⊚-correct _ _ fcor gcor t = ≈.trans (gcor _) (fcor _)

  {-# TERMINATING #-}
  reassoc : Method
  reassoc (var f) = var f
  reassoc id = id
  reassoc (var f ∘ g) = var f ∘ reassoc g
  reassoc (id ∘ g) = id ∘ reassoc g
  reassoc ((f ∘ g) ∘ h) = reassoc (f ∘ g ∘ h)

  {-# TERMINATING #-}
  reassoc-correct : Correct reassoc
  reassoc-correct (var f) = C.≈.refl
  reassoc-correct id = C.≈.refl
  reassoc-correct (var f ∘ g) = C.∘-resp-r (reassoc-correct g)
  reassoc-correct (id ∘ f) = C.∘-resp-r (reassoc-correct f)
  reassoc-correct ((f ∘ g) ∘ h) = ≈.trans C.assoc (reassoc-correct (f ∘ g ∘ h))

  simpId : Method
  simpId (var x) = var x
  simpId id = id
  simpId (f ∘ g) with simpId f | simpId g
  ... | var f′  | var g′  = var f′ ∘ var g′
  ... | var f′  | id      = var f′
  ... | var f′  | g′ ∘ g″ = var f′ ∘ g′ ∘ g″
  ... | id      | g′      = g′
  ... | f′ ∘ f″ | var g′  = f′ ∘ f″ ∘ var g′
  ... | f′ ∘ f″ | id      = f′ ∘ f″
  ... | f′ ∘ f″ | g′ ∘ g″ = f′ ∘ f″ ∘ g′ ∘ g″

  simpId-correct : Correct simpId

  simpId-≡ : ∀ {A B} t {u : Tm A B} → simpId t ≡ u → ⟦ t ⟧ C.≈ ⟦ u ⟧

  simpId-correct (var x) = C.≈.refl
  simpId-correct id = C.≈.refl
  simpId-correct (f ∘ g)
    with simpId f | inspect simpId f | simpId g | inspect simpId g
  ... | var f′ | [ eq-f ] | var g′  | [ eq-g ]
      = C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)
  ... | var f′ | [ eq-f ] | id      | [ eq-g ]
      = ≈.trans (C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)) C.id-r
  ... | var f′ | [ eq-f ] | g′ ∘ g″ | [ eq-g ]
      = C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)
  ... | id | [ eq-f ] | g' | [ eq-g ]
      = ≈.trans (C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)) C.id-l
  ... | f′ ∘ f″ | [ eq-f ] | var x   | [ eq-g ]
      = ≈.trans (C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)) C.assoc
  ... | f′ ∘ f″ | [ eq-f ] | id      | [ eq-g ]
      = ≈.trans (C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)) C.id-r
  ... | f′ ∘ f″ | [ eq-f ] | g′ ∘ g″ | [ eq-g ]
      = ≈.trans (C.∘-resp (simpId-≡ f eq-f) (simpId-≡ g eq-g)) C.assoc

  simpId-≡ t eq
      = ≈.trans (simpId-correct t) (≈.reflexive (≡.cong ⟦_⟧ eq))

  simp : Method
  simp = reassoc ⊚ simpId

  simp-correct : Correct simp
  simp-correct
      = ⊚-correct reassoc simpId reassoc-correct simpId-correct

  simp-injective : ∀ {A B} (t u : Tm A B)
    → ⟦ simp t ⟧ C.≈ ⟦ simp u ⟧
    → ⟦ t ⟧ C.≈ ⟦ u ⟧
  simp-injective = Correct→Injective simp simp-correct

  simp-injective′ : ∀ {A B} (t u : Tm A B)
    → simp t ≡ simp u
    → ⟦ t ⟧ C.≈ ⟦ u ⟧
  simp-injective′ t u eq = simp-injective t u (≈.reflexive (≡.cong ⟦_⟧ eq))

reify : Term → Term
reify (def (quote Category.id)
  (argH _ ∷ argH _ ∷ argH _ ∷ argD C ∷ argH _ ∷ []))
    = con (quote id) []
reify (def (quote Category._∘_)
  (argH _ ∷ argH _ ∷ argH _ ∷ argD C ∷ argH _ ∷ argH _ ∷ argH _ ∷ argD f ∷ argD g ∷ []))
    = con (quote _∘_) (argD (reify f) ∷ argD (reify g) ∷ [])
reify t = con (quote Tm.var) (argD t ∷ [])

matchGoal : Term → TC (Term × Term)
matchGoal (def (quote Category._≈_)
  (argH _ ∷ argH _ ∷ argH _ ∷ argD C ∷ argH A ∷ argH B ∷ argD f ∷ argD g ∷ []))
    = return (reify f , reify g)
matchGoal _ = typeError (strErr "matchGoal: not an equality" ∷ [])

macro
  simp! : ∀ {lo la l≈} → Category lo la l≈ → Term → TC ⊤
  simp! C hole = do
      goal ← inferType hole
      blockOnAnyMeta goal
      (lhs , rhs) ← matchGoal goal
      C′ ← quoteTC C
      let solution
            = def (quote simp-injective′)
            ( argD C′
            ∷ argD lhs
            ∷ argD rhs
            ∷ argD (con (quote ≡.refl) [])
            ∷ [])
      unify hole solution

module Test {lo la l≈} {C : Category lo la l≈} (let module C = Category C) {A B C′ D} {f : C′ C.⇒ D} {g : B C.⇒ C′} {h : A C.⇒ B} where

  test : ((C.id C.∘ f) C.∘ g) C.∘ h C.≈ C.id C.∘ f C.∘ g C.∘ h
  test = simp! C


module Languages.FILL.AgdaInterface where

open import nat

open import Utils.HaskellTypes
open import Utils.Exception
open import Languages.FILL.Intermediate
open import Languages.FILL.Syntax

translate : ITerm → Either Exception Term
translate = translate' 0 0
 where
   -- n : the next fresh bound variable name
   -- m : the next fresh bound pattern variable name
   translate' : Name → Name → ITerm → Either Exception Term
   translate' n m Triv = right Triv
   translate' n m Void = right Void
   translate' n m (Var x) = right (FVar x)
   translate' n m (TTensor t₁ t₂) =
     (translate' n m t₁)
       >>=E (λ e₁ → (translate' n m t₂)
         >>=E (λ e₂ → right (Tensor e₁ e₂)))
   translate' n m (TPar t₁ t₂) =
     (translate' n m t₁)
       >>=E (λ e₁ → (translate' n m t₂)
         >>=E (λ e₂ → right (Par e₁ e₂)))
   translate' n m (Lam x a t) =
     (translate' (suc n) m t)
       >>=E (λ e → right (Lam x a (close-t n x BV x e)))
   translate' n m (Let t₁ a PTriv t₂) =
     (translate' n m t₁)
       >>=E (λ e₁ → (translate' n m t₂)
         >>=E (λ e₂ → right (Let e₁ a PTriv e₂)))
   translate' n m (Let t₁ a (PTensor (PVar x) (PVar y)) t₂) =
     (translate' n (suc m) t₁)
       >>=E (λ e₁ → (translate' n (suc m) t₂)
         >>=E (λ e₂ → right (Let e₁ a (PTensor x y) (close-t m x LPV x (close-t m y RPV y e₂)))))
   translate' n m (Let t₁ a (PPar (PVar x) (PVar y)) t₂) =
     (translate' n (suc m) t₁)
       >>=E (λ e₁ → (translate' n (suc m) t₂)
         >>=E (λ e₂ → right (Let e₁ a (PPar x y) (close-t m x LPV x (close-t m y RPV y e₂)))))
   translate' n m (Let _ _ _ _) = error IllformedLetPattern
   translate' n m (App t₁ t₂) =
     (translate' n m t₁)
       >>=E (λ e₁ → (translate' n m t₂)
         >>=E (λ e₂ → right (App e₁ e₂)))

untranslate : Term → ITerm
untranslate Triv = Triv
untranslate Void = Void
untranslate (FVar x) = Var x
untranslate (BVar _ x _) = Var x
untranslate (Let t₁ a PTriv t₂) = Let (untranslate t₁) a PTriv (untranslate t₂)
untranslate (Let t₁ a (PTensor xs ys) t₂) = Let (untranslate t₁) a (PTensor (PVar xs) (PVar ys)) (untranslate t₂)
untranslate (Let t₁ a (PPar xs ys) t₂) = Let (untranslate t₁) a (PPar (PVar xs) (PVar ys)) (untranslate t₂)
untranslate (Lam x a t) = Lam x a (untranslate t)
untranslate (App t₁ t₂) = App (untranslate t₁) (untranslate t₂)
untranslate (Tensor t₁ t₂) = TTensor (untranslate t₁) (untranslate t₂)
untranslate (Par t₁ t₂) = TPar (untranslate t₁) (untranslate t₂)

transUntransId : ITerm → Either Exception ITerm
transUntransId t = (translate t) >>=E (λ e → right (untranslate e))

{-# COMPILED_EXPORT transUntransId transUntransId #-}


open import Oscar.Prelude
open import Oscar.Class.IsPrecategory
open import Oscar.Class.IsCategory
open import Oscar.Class.Category
open import Oscar.Class.IsFunctor
open import Oscar.Class.Functor

open import Oscar.Class.Reflexivity
open import Oscar.Class.Transitivity
open import Oscar.Class.Surjection
open import Oscar.Class.Smap
open import Oscar.Class.Transextensionality
open import Oscar.Class.Transassociativity
open import Oscar.Class.Transleftidentity
open import Oscar.Class.Transrightidentity
open import Oscar.Class.Surjidentity
open import Oscar.Class

module Oscar.Class.Kitten where

productCat : ∀ {𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂} → Category 𝔬₁ 𝔯₁ ℓ₁ → Category 𝔬₂ 𝔯₂ ℓ₂ → Category (𝔬₁ ∙̂ 𝔬₂) (𝔯₁ ∙̂ 𝔯₂) (ℓ₁ ∙̂ ℓ₂)
Category.𝔒 (productCat c₁ c₂) = Category.𝔒 c₁ × Category.𝔒 c₂
Category._∼_ (productCat c₁ c₂) (x₁ , x₂) (y₁ , y₂) = Category._∼_ c₁ x₁ y₁ × Category._∼_ c₂ x₂ y₂
Category._∼̇_ (productCat c₁ c₂) {x₁ , x₂} {y₁ , y₂} (f₁ , g₁) (f₂ , g₂) = Category._∼̇_ c₁ f₁ f₂ × Category._∼̇_ c₂ g₁ g₂
Category.category-ε (productCat c₁ c₂) = (Category.category-ε c₁) , (Category.category-ε c₂)
Category._↦_ (productCat c₁ c₂) (f₁ , f₂) (g₁ , g₂) = (Category._↦_ c₁ f₁ g₁) , (Category._↦_ c₂ f₂ g₂)
Category.`IsCategory (productCat c₁ c₂) .IsCategory.`IsPrecategory .IsPrecategory.`𝓣ransextensionality .⋆ (x₁ , y₁) (x₂ , y₂) = transextensionality x₁ x₂ , transextensionality y₁ y₂
Category.`IsCategory (productCat c₁ c₂) .IsCategory.`IsPrecategory .IsPrecategory.`𝓣ransassociativity .⋆ f g h = transassociativity (f .π₀) (g .π₀) (h .π₀) , transassociativity (f .π₁) (g .π₁) (h .π₁)
Category.`IsCategory (productCat c₁ c₂) .IsCategory.`𝓣ransleftidentity .⋆ = transleftidentity , transleftidentity
Category.`IsCategory (productCat c₁ c₂) .IsCategory.`𝓣ransrightidentity = ∁ (transrightidentity , transrightidentity)

record AFunctor {𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂} (source : Category 𝔬₁ 𝔯₁ ℓ₁) (target : Category 𝔬₂ 𝔯₂ ℓ₂) : Ø 𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂ where
  constructor ∁
  private module S = Category source
  private module T = Category target
  field
    F₀ : S.𝔒 → T.𝔒
    F₁ : Smap.type S._∼_ T._∼_ F₀ F₀
    isFunctor : IsFunctor S._∼_ S._∼̇_ S.category-ε S._↦_ T._∼_ T._∼̇_ T.category-ε T._↦_ F₁

record MonoidalCategory 𝔬 𝔯 ℓ : Ø ↑̂ (𝔬 ∙̂ 𝔯 ∙̂ ℓ) where
  constructor ∁
  field
    thecat : Category 𝔬 𝔯 ℓ
    thefunc : AFunctor (productCat thecat thecat) thecat
  O : Ø 𝔬
  O = Category.𝔒 thecat
  field
    𝟏 : O
  _⟶_ : O → O → Ø 𝔯
  _⟶_ = Category._∼_ thecat
  _⊗_ : O → O → O
  _⊗_ = λ x y → AFunctor.F₀ thefunc (x , y)
  _⨂_ : ∀ {w x y z} → w ⟶ x → y ⟶ z → (w ⊗ y) ⟶ (x ⊗ z)
  _⨂_ f g = AFunctor.F₁ thefunc (f , g)
  _↦_ : ∀ {x y z} (f : x ⟶ y) (g : y ⟶ z) → x ⟶ z
  _↦_ = Category._↦_ thecat
  i : ∀ {x} → x ⟶ x
  i = Category.category-ε thecat
  _≈̇_ = Category._∼̇_ thecat
  -- infixr 9 _⊗_
  field
    associator : ∀ (x y z : O)
                 → Σ (((x ⊗ y) ⊗ z) ⟶ (x ⊗ (y ⊗ z))) λ f
                 → Σ ((x ⊗ (y ⊗ z)) ⟶ ((x ⊗ y) ⊗ z)) λ f⁻¹
                 → ((f ↦ f⁻¹) ≈̇ i) × ((f⁻¹ ↦ f) ≈̇ i)
    left-unitor : ∀ (x : O)
                  → Σ ((𝟏 ⊗ x) ⟶ x) λ f
                  → Σ (x ⟶ (𝟏 ⊗ x)) λ f⁻¹
                  → ((f ↦ f⁻¹) ≈̇ i) × ((f⁻¹ ↦ f) ≈̇ i)
    right-unitor : ∀ (x : O)
                   → Σ ((x ⊗ 𝟏) ⟶ x) λ f
                   → Σ (x ⟶ (x ⊗ 𝟏)) λ f⁻¹
                   → ((f ↦ f⁻¹) ≈̇ i) × ((f⁻¹ ↦ f) ≈̇ i)
  assoc : ∀ (x y z : O) → ((x ⊗ y) ⊗ z) ⟶ (x ⊗ (y ⊗ z))
  assoc x y z = π₀ (associator x y z)
  ru : ∀ x → (x ⊗ 𝟏) ⟶ x
  ru x = π₀ (right-unitor x)
  lu : ∀ x → (𝟏 ⊗ x) ⟶ x
  lu x = π₀ (left-unitor x)
  field
    triangle-identity : ∀ (x y : O)
                        → (ru x ⨂ i) ≈̇ (assoc x 𝟏 y ↦ (i ⨂ lu y))
    pentagon-identity : ∀ (w x y z : O)
      → (((assoc w x y ⨂ i) ↦ assoc w (x ⊗ y) z) ↦ (i ⨂ assoc x y z))
      ≈̇ (assoc (w ⊗ x) y z ↦ assoc w x (y ⊗ z))

module _
  {𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂}
  (source : MonoidalCategory 𝔬₁ 𝔯₁ ℓ₁)
  (target : MonoidalCategory 𝔬₂ 𝔯₂ ℓ₂)
  where
  record LaxMonoidalFunctor : Ø 𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂ where
    module C = MonoidalCategory source
    module D = MonoidalCategory target
    field
      𝓕 : AFunctor C.thecat D.thecat
    open AFunctor 𝓕 public
    field
      e : D.𝟏 D.⟶ F₀ C.𝟏
      μ : ∀ x y → (F₀ x D.⊗ F₀ y) D.⟶ F₀ (x C.⊗ y) -- F A → F B → F (A × B)
      associativity : ∀ x y z
        → ((μ x y D.⨂ D.i) D.↦ (μ (x C.⊗ y) z D.↦ F₁ (C.assoc x y z)))
              D.≈̇
          (D.assoc (F₀ x) (F₀ y) (F₀ z) D.↦ ((D.i D.⨂ μ y z) D.↦ μ x (y C.⊗ z)))
      left-unitality : ∀ x → (D.lu (F₀ x)) D.≈̇ ((e D.⨂ D.i) D.↦ (μ C.𝟏 x D.↦ F₁ (C.lu x)))
      right-unitality : ∀ x → (D.ru (F₀ x)) D.≈̇ ((D.i D.⨂ e) D.↦ (μ x C.𝟏 D.↦ F₁ (C.ru x)))

module _
  {𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂}
  (source : MonoidalCategory 𝔬₁ 𝔯₁ ℓ₁)
  (target : MonoidalCategory 𝔬₂ 𝔯₂ ℓ₂)
  (let module C = MonoidalCategory source)
  (let module D = MonoidalCategory target)
  (𝓕 : AFunctor C.thecat D.thecat)
  (open AFunctor 𝓕)
  (e : D.𝟏 D.⟶ F₀ C.𝟏)
  (μ : ∀ x y → (F₀ x D.⊗ F₀ y) D.⟶ F₀ (x C.⊗ y))
  where
  record IsLaxMonoidalFunctor : Ø 𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂ where
    field
      associativity : ∀ x y z
        → ((μ x y D.⨂ D.i) D.↦ (μ (x C.⊗ y) z D.↦ F₁ (C.assoc x y z)))
              D.≈̇
          (D.assoc (F₀ x) (F₀ y) (F₀ z) D.↦ ((D.i D.⨂ μ y z) D.↦ μ x (y C.⊗ z)))
      left-unitality : ∀ x → (D.lu (F₀ x)) D.≈̇ ((e D.⨂ D.i) D.↦ (μ C.𝟏 x D.↦ F₁ (C.lu x)))
      right-unitality : ∀ x → (D.ru (F₀ x)) D.≈̇ ((D.i D.⨂ e) D.↦ (μ x C.𝟏 D.↦ F₁ (C.ru x)))

record GenericApplicativeRaw
  {lc ld} {Oc : Ø lc} {Od : Ø ld}
  (F : Oc → Od)
  (1c : Oc) (1d : Od)
  (_⊗c_ : Oc → Oc → Oc) (_⊗d_ : Od → Od → Od)
  {ℓc} (_⟶c_ : Oc → Oc → Ø ℓc)
  {ℓd} (_⟶d_ : Od → Od → Ø ℓd)
  : Ø ℓd ∙̂ lc ∙̂ ℓc
  where
  field
    m : ∀ {x y} → x ⟶c y → F x ⟶d F y -- fmap
    e : 1d ⟶d F 1c -- pure
    μ : ∀ {x y} → (F x ⊗d F y) ⟶d F (x ⊗c y) -- apply

  -- _<*>_ : ∀ {x y : Oc} → ? → ? → {!!} --  F (x ⟶c y) → F x ⟶d F y
  -- _<*>_ f x = m ? (μ )

{-
  _<*>_ : ∀ {A B} → F (A → B) → F A → F B
  _<*>_ f x = sfmap (λ {(f , x) → f x}) (f <s> x)
-}

{-
record ContainedGenericApplicativeRaw
  {lc ld} {Oc : Ø lc} {Od : Ø ld}
  (F : Oc → Od)
  (1c : Oc) (1d : Od)
  (_⊗c_ : Oc → Oc → Oc) (_⊗d_ : Od → Od → Od)
  {ℓd} (_⟶d_ : Od → Od → Ø ℓd)
  : Ø ℓd ∙̂ lc
  where
  field
    e : 1d ⟶d F 1c
    μ : ∀ {x y} → (F x ⊗d F y) ⟶d F (x ⊗c y)
-}
open import Oscar.Data.𝟙
open import Oscar.Data.Proposequality

record Action {a b} {A : Set a} {B : Set b} {f g h} (F : A → B → Set f) (G : A → Set g) (H : B → Set h) : Ø a ∙̂ b ∙̂ f ∙̂ g ∙̂ h where
  field
    act : ∀ {x y} → F x y → G x → H y

record SetApplyM {a b} (F : Set a → Set b)
  -- (𝟏ᴬ : Set a) (𝟏ᴮ : Set b) (_⊕_ : Set p → Set p → Set p) (_⟶_ : A → A → Set ℓ)
  : Ø ↑̂ a ∙̂ b where
  field
    sfmap : ∀ {A B} → (A → B) → F A → F B
    sunit : Lift {_} {a} 𝟙 → F (Lift 𝟙)
    _<s>_ : ∀ {A B} → F A → F B → F (A × B)
  _<*>_ : ∀ {A B} → F (A → B) → F A → F B
  _<*>_ f x = sfmap (λ {(f , x) → f x}) (f <s> x)
  _⨂_ : ∀ {A B C D : Ø a} → (A → B) → (C → D) → A × C → B × D
  _⨂_ f g ac = let (a , c) = ac in f a , g c
  assoc : ∀ {A B C : Ø a} → A × (B × C) → (A × B) × C
  assoc abc = let (a , (b , c)) = abc in (a , b) , c
  field
    law-nat : ∀ {A B C D} (f : A → B) (g : C → D) u v → sfmap (f ⨂ g) (u <s> v) ≡ (sfmap f u <s> sfmap g v)
    leftid : ∀ {B} (v : F B) → sfmap π₁ (sunit _ <s> v) ≡ v
    righttid : ∀ {A} (u : F A) → sfmap π₀ (u <s> sunit _) ≡ u
    associativity : ∀ {A B C} u v w → sfmap (assoc {A} {B} {C}) (u <s> (v <s> w)) ≡ ((u <s> v) <s> w)

  source-cat : MonoidalCategory _ _ _
  source-cat .MonoidalCategory.thecat .Category.𝔒 = Ø a
  source-cat .MonoidalCategory.thecat .Category._∼_ = Function
  source-cat .MonoidalCategory.thecat .Category._∼̇_ = _≡̇_
  source-cat .MonoidalCategory.thecat .Category.category-ε = ¡
  source-cat .MonoidalCategory.thecat .Category._↦_ = flip _∘′_
  source-cat .MonoidalCategory.thecat .Category.`IsCategory = {!!}
  source-cat .MonoidalCategory.thefunc .AFunctor.F₀ (A , B) = A × B
  source-cat .MonoidalCategory.thefunc .AFunctor.F₁ = {!!}
  source-cat .MonoidalCategory.thefunc .AFunctor.isFunctor = {!!}
  source-cat .MonoidalCategory.𝟏 = Lift 𝟙
  source-cat .MonoidalCategory.associator = {!!}
  source-cat .MonoidalCategory.left-unitor = {!!}
  source-cat .MonoidalCategory.right-unitor = {!!}
  source-cat .MonoidalCategory.triangle-identity = {!!}
  source-cat .MonoidalCategory.pentagon-identity = {!!}

  target-cat : MonoidalCategory _ _ _
  target-cat .MonoidalCategory.thecat .Category.𝔒 = Ø b
  target-cat .MonoidalCategory.thecat .Category._∼_ = Function
  target-cat .MonoidalCategory.thecat .Category._∼̇_ = _≡̇_
  target-cat .MonoidalCategory.thecat .Category.category-ε = ¡
  target-cat .MonoidalCategory.thecat .Category._↦_ = flip _∘′_
  target-cat .MonoidalCategory.thecat .Category.`IsCategory = {!!}
  target-cat .MonoidalCategory.thefunc .AFunctor.F₀ (A , B) = A × B
  target-cat .MonoidalCategory.thefunc .AFunctor.F₁ = {!!}
  target-cat .MonoidalCategory.thefunc .AFunctor.isFunctor = {!!}
  target-cat .MonoidalCategory.𝟏 = Lift 𝟙
  target-cat .MonoidalCategory.associator = {!!}
  target-cat .MonoidalCategory.left-unitor = {!!}
  target-cat .MonoidalCategory.right-unitor = {!!}
  target-cat .MonoidalCategory.triangle-identity = {!!}
  target-cat .MonoidalCategory.pentagon-identity = {!!}

  module C = MonoidalCategory source-cat
  module D = MonoidalCategory target-cat

  the-functor : AFunctor C.thecat D.thecat
  the-functor .AFunctor.F₀ = F
  the-functor .AFunctor.F₁ = sfmap
  the-functor .AFunctor.isFunctor = {!!}

  open AFunctor the-functor

  the-e : D.𝟏 D.⟶ F₀ C.𝟏 -- F (Lift 𝟙)
  the-e = {!!}

  the-μ : ∀ x y → (F₀ x D.⊗ F₀ y) D.⟶ F₀ (x C.⊗ y) -- F A → F B → F (A × B)
  the-μ A B (FA , FB) = FA <s> FB

toSetApply : ∀ {a b} (F : Set a → Set b) (gar : GenericApplicativeRaw F (Lift 𝟙) (Lift 𝟙) (λ A B → A × B) (λ A B → A × B) Function Function) → SetApplyM F
toSetApply F gar .SetApplyM.sfmap = GenericApplicativeRaw.m gar
toSetApply F gar .SetApplyM.sunit _ = GenericApplicativeRaw.e gar !
toSetApply F gar .SetApplyM._<s>_ FA FB = GenericApplicativeRaw.μ gar (FA , FB)
toSetApply F gar .SetApplyM.law-nat = {!!}
toSetApply F gar .SetApplyM.leftid = {!!}
toSetApply F gar .SetApplyM.righttid = {!!}
toSetApply F gar .SetApplyM.associativity = {!!}

module _
  {𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂}
  (C : MonoidalCategory 𝔬₁ 𝔯₁ ℓ₁)
  (D : MonoidalCategory 𝔬₂ 𝔯₂ ℓ₂)
  where
  record LaxMonoidalFunctorWithStrength : Ø 𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂ where
    field
      laxMonoidalFunctor : LaxMonoidalFunctor C D
    open LaxMonoidalFunctor laxMonoidalFunctor
    {-
    field
      β : ∀ v w → (v D.⊗ F₀ w) D.⟶ F₀ (v C.⊗ w)
      commute-5 : ∀ u v w → (C.assoc u v (F₀ w) C.↦ ((C.i C.⨂ β v w) C.↦ β u (v C.⊗ w))) C.≈̇ (β (u C.⊗ v) w C.↦ F₁ (C.assoc u v w))
      commute-3 : ∀ v → C.lu (F₀ v) C.≈̇ (β C.𝟏 v C.↦ F₁ (C.lu v))
      -- strength : TensorialStrength C (LaxMonoidalFunctor.𝓕 laxMonoidalEndofunctor)
    -}

module _ {𝔬 𝔯 ℓ} (V : MonoidalCategory 𝔬 𝔯 ℓ) (let C = MonoidalCategory.thecat V) (F : AFunctor C C) where
  open MonoidalCategory V
  open AFunctor F
  record TensorialStrength : Ø 𝔬 ∙̂ 𝔯 ∙̂ ℓ where
    field
      β : ∀ v w → (v ⊗ F₀ w) ⟶ F₀ (v ⊗ w)
      commute-5 : ∀ u v w → (assoc u v (F₀ w) ↦ ((i ⨂ β v w) ↦ β u (v ⊗ w))) ≈̇ (β (u ⊗ v) w ↦ F₁ (assoc u v w))
      commute-3 : ∀ v → lu (F₀ v) ≈̇ (β 𝟏 v ↦ F₁ (lu v))

module _
  {𝔬 𝔯 ℓ}
  (C : MonoidalCategory 𝔬 𝔯 ℓ)
  where
  record LaxMonoidalEndofunctorWithStrength : Ø 𝔬 ∙̂ 𝔯 ∙̂ ℓ where
    field
      laxMonoidalEndofunctor : LaxMonoidalFunctor C C
      strength : TensorialStrength C (LaxMonoidalFunctor.𝓕 laxMonoidalEndofunctor)

{- want parameters :
  X : Set a
  Y : Set b
  X₀ : X
  F : X → Y
  _⨁_ : X → X → Set a

  LaxMonoidalFunctor.e = unit : F X₀
  LaxMonoidalFunctor.μ = apply : ∀ {A B : X} → F A → F B → F (A ⨁ B)
  LaxMonoidalFunctor.𝓕.F₀ = F : X → Y

-}

record GenericApplyM {a ℓ} {A : Set a} (F : A → A) (𝟏 : A) (_⊕_ : A → A → A) (_⟶_ : A → A → Set ℓ) : Ø a ∙̂ ℓ where
  field
    gunit : 𝟏 ⟶ F 𝟏
    gproduct : ∀ {x y : A} → (F x ⊕ F y) ⟶ F (x ⊕ y)

open import Oscar.Data.𝟙
-- open import Oscar.Class.Kitten
open import Oscar.Class.Category
open import Oscar.Class.IsPrefunctor
open import Oscar.Class.IsCategory
open import Oscar.Class.IsPrecategory
open import Oscar.Property.Category.Function
open import Oscar.Class
open import Oscar.Class.Fmap
import Oscar.Class.Reflexivity.Function

module _
  {𝔬₁ 𝔬₂} (𝓕 : Ø 𝔬₁ → Ø 𝔬₂)
  (fmapper : Fmap 𝓕)
  (fpure : ∀ {𝔄} → 𝔄 → 𝓕 𝔄)
  (fapply : ∀ {𝔄 𝔅} → 𝓕 (𝔄 → 𝔅) → 𝓕 𝔄 → 𝓕 𝔅)
  where
  -- instance _ = fmapper
  fmap' = λ {A B} (f : A → B) → fapply (fpure f)
  mkProductMonoidalCategory : MonoidalCategory _ _ _
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category.𝔒 = Ø 𝔬₁
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category._∼_ = MFunction 𝓕
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category._∼̇_ = Proposextensequality
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category.category-ε = ε
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category._↦_ = (flip _∘′_)
  mkProductMonoidalCategory .MonoidalCategory.thecat .Category.`IsCategory = ?
  mkProductMonoidalCategory .MonoidalCategory.thefunc .AFunctor.F₀ (A , B) = A × B
  mkProductMonoidalCategory .MonoidalCategory.thefunc .AFunctor.F₁ (f , g) xy = fapply (fmap' _,_ (f (fmap' π₀ xy))) (g (fmap' π₁ xy))
  mkProductMonoidalCategory .MonoidalCategory.thefunc .AFunctor.isFunctor .IsFunctor.`IsPrefunctor .IsPrefunctor.`𝓢urjtranscommutativity = {!!}
  mkProductMonoidalCategory .MonoidalCategory.thefunc .AFunctor.isFunctor .IsFunctor.`IsPrefunctor .IsPrefunctor.`𝓢urjextensionality = {!!}
  mkProductMonoidalCategory .MonoidalCategory.thefunc .AFunctor.isFunctor .IsFunctor.`𝒮urjidentity = {!!}
  mkProductMonoidalCategory .MonoidalCategory.𝟏 = {!!}
  mkProductMonoidalCategory .MonoidalCategory.associator = {!!}
  mkProductMonoidalCategory .MonoidalCategory.left-unitor = {!!}
  mkProductMonoidalCategory .MonoidalCategory.right-unitor = {!!}
  mkProductMonoidalCategory .MonoidalCategory.triangle-identity = {!!}
  mkProductMonoidalCategory .MonoidalCategory.pentagon-identity = {!!}

record HApplicativeFunctor {𝔬₁ 𝔬₂} (𝓕 : Ø 𝔬₁ → Ø 𝔬₂) : Ø (↑̂ (↑̂ 𝔬₁ ∙̂ 𝔬₂)) where
  constructor ∁
  field
    fmapper : Fmap 𝓕
    fpure : ∀ {𝔄} → 𝔄 → 𝓕 𝔄
    fapply : ∀ {𝔄 𝔅} → 𝓕 (𝔄 → 𝔅) → 𝓕 𝔄 → 𝓕 𝔅

  field
    isStrongLaxMonoidalEndofunctor : LaxMonoidalEndofunctorWithStrength (mkProductMonoidalCategory 𝓕 fmapper fpure fapply)

  module LMF = LaxMonoidalFunctor (LaxMonoidalEndofunctorWithStrength.laxMonoidalEndofunctor isStrongLaxMonoidalEndofunctor)
  derive-fpure : ∀ {𝔄} → 𝔄 → 𝓕 𝔄
  derive-fpure = {!LMF!} where

  {-
    ⦃ isFunctor ⦄ : IsFunctor
                       {𝔒₁ = Ø 𝔬₁} (λ A B → A × 𝓕 B)
                         {!(λ { {A} {B} (x₁ , f₁) (x₂ , f₂) → {!(x₁ ≡ x₂) × !}})!} (λ {A} → {!!} , {!!}) {!!}
                       {𝔒₂ = Ø 𝔬₁} {!!}
                         {!!} {!!} {!!}
                       {!!}
  -}

record MonoidalFunctor {𝔬₁ 𝔬₂} (𝓕 : Ø 𝔬₁ → Ø 𝔬₂) : Ø (↑̂ (↑̂ 𝔬₁ ∙̂ 𝔬₂)) where
  constructor ∁
  field
    ⦃ isFmap ⦄ : Fmap 𝓕
    unit : 𝓕 (Lift 𝟙)
    mappend : ∀ {𝔄 𝔅} → 𝓕 𝔄 → 𝓕 𝔅 → 𝓕 (𝔄 × 𝔅)
    {-
    ⦃ isFunctor ⦄ : IsFunctor
                       Function⟦ 𝔬₁ ⟧
                         Proposextensequality ε (flip _∘′_)
                       (MFunction 𝓕)
                         Proposextensequality ε (flip _∘′_)
                       {!!}
    -}
  pure : ∀ {𝔄} → 𝔄 → 𝓕 𝔄
  pure x = fmap (x ∞) unit

  infixl 4 _<*>_
  _<*>_ : ∀ {𝔄 𝔅} → 𝓕 (𝔄 → 𝔅) → 𝓕 𝔄 → 𝓕 𝔅
  f <*> x = fmap (λ {(f , x) → f x}) (mappend f x)

  app-identity : ∀ {𝔄} (v : 𝓕 𝔄) → (pure ¡[ 𝔄 ] <*> v) ≡ v
  app-identity v = {!!}

open MonoidalFunctor ⦃ … ⦄ public using (unit; mappend)

-- record ApplicativeFunctor where
module Purity
  {𝔵₁ 𝔵₂ 𝔯} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂} (F : 𝔛₁ → 𝔛₂) (x₁ : 𝔛₁) (x₂ : 𝔛₂) (_⟶_ : 𝔛₂ → 𝔛₂ → Ø 𝔯)
  = ℭLASS (F , x₁ , x₂ , _⟶_) (x₂ ⟶ F x₁)

module Applicativity
  {𝔵₁ 𝔵₂ 𝔯} {𝔛₁ : Ø 𝔵₁} {𝔛₂ : Ø 𝔵₂} (F : 𝔛₁ → 𝔛₂) (_⊗₁_ : 𝔛₁ → 𝔛₁ → 𝔛₁) (_⊗₂_ : 𝔛₂ → 𝔛₂ → 𝔛₂) (_⟶_ : 𝔛₂ → 𝔛₂ → Ø 𝔯)
  = ℭLASS (F , _⊗₁_ , _⊗₂_ , _⟶_) (∀ x y → (F x ⊗₂ F y) ⟶ F (x ⊗₁ y))

-- FunctionalMonoidalCategory

AFunctorFunction²Function : ∀ {𝔬₁} → AFunctor (productCat (CategoryFunction {𝔬₁}) (CategoryFunction {𝔬₁})) (CategoryFunction {𝔬₁})
AFunctorFunction²Function .AFunctor.F₀ = uncurry _×_
AFunctorFunction²Function .AFunctor.F₁ (f₁ , f₂) (x₁ , x₂) = f₁ x₁ , f₂ x₂
AFunctorFunction²Function .AFunctor.isFunctor .IsFunctor.`IsPrefunctor .IsPrefunctor.`𝓢urjtranscommutativity = {!!}
AFunctorFunction²Function .AFunctor.isFunctor .IsFunctor.`IsPrefunctor .IsPrefunctor.`𝓢urjextensionality = {!!}
AFunctorFunction²Function .AFunctor.isFunctor .IsFunctor.`𝒮urjidentity = {!!}

record LMF {𝔬₁ 𝔬₂} (𝓕 : Ø 𝔬₁ → Ø 𝔬₂) ⦃ _ : Fmap 𝓕 ⦄ : Ø ↑̂ (↑̂ 𝔬₁ ∙̂ 𝔬₂) where
  constructor ∁
  field
    lmf-pure : Purity.type 𝓕 (Lift 𝟙) (Lift 𝟙) Function
    lmf-apply : Applicativity.type 𝓕 _×_ _×_ Function

  lmf-happly : ∀ {𝔄 𝔅} → 𝓕 (𝔄 → 𝔅) → 𝓕 𝔄 → 𝓕 𝔅
  lmf-happly f x = fmap (λ {(f , x) → f x}) (lmf-apply _ _ (f , x))

  field
    ⦃ islmf ⦄ : IsLaxMonoidalFunctor (∁ CategoryFunction (∁ (uncurry _×_) {!!} {!!}) (Lift 𝟙) {!!} {!!} {!!} {!!} {!!}) (∁ CategoryFunction (∁ (uncurry _×_) {!!} {!!}) ((Lift 𝟙)) {!!} {!!} {!!} {!!} {!!}) (record { F₀ = 𝓕 ; F₁ = fmap ; isFunctor = ∁ ⦃ {!!} ⦄ ⦃ ! ⦄ ⦃ ! ⦄ ⦃ {!!} ⦄  }) lmf-pure lmf-apply


{-# OPTIONS --copatterns --sized-types --without-K #-}
module PolyFinalCoalg where

open import Data.Product
open import Data.Nat
open import Data.Fin
open import Data.Unit as Unit
open import Data.Empty
open import Data.Vec hiding (_∈_; [_])
open import Relation.Binary.PropositionalEquality
open import Function
open import Data.Sum as Sum
open import Size

open import Poly

-- | Indexed product
_×ᵢ_ : {I : Set} → Fam I → Fam I → Fam I
(A ×ᵢ B) i = A i × B i

-- | Product of indexed functors
_×ᵢ'_ : {I J K : Set} →
        (Fam K → Fam I) → (Fam J → Fam I) → (Fam K × Fam J → Fam I)
(F ×ᵢ' G) (X , Y) = F X ×ᵢ G Y

record Isomorphic (A B : Set) : Set where
--  constructor isIso
  field
    f : A → B
    f⃖ : B → A
    is-invˡ : (a : A) → f⃖ (f a) ≡ a
    is-invʳ : (b : B) → f (f⃖ b) ≡ b

_≃_ = Isomorphic

_≃'_ : {I : Set} → Fam I → Fam I → Set
_≃'_ {I} A B = (i : I) → A i ≃ B i

-- | Avoid setoids for now
postulate
  Π'-ext : {I J : Set} {u : I → J} {P : Fam I} →
           (j : J) (f g : Π' u P j) →
           ((i : I) → (p : u i ≡ j) → f (i , p) ≡ g (i , p)) → f ≡ g
  Π'-ext' : {I J : Set} {u : I → J} {P : Fam I} →
           (f g : (i : I) → Π' u P (u i)) →
           ((i : I) → app i (f i) ≡ app i (g i)) → (i : I) → f i ≡ g i

-- | Dependent polynomials
record ParamPoly {I J : Set}  : Set₁ where
  constructor ppoly
  field
    -- J ←t- B -p→ A -s→ I
    A : Set  -- ^ Labels
    s : A → I
    B : Set
    p₁ p₂ : B → A
    t₁ : B → J
    t₂ : B → I

first : {I J : Set} → ParamPoly {I} {J} → DPoly {I} {J}
first (ppoly A s B p₁ p₂ t₁ t₂) = dpoly A s B p₁ t₁

second : {I J : Set} → ParamPoly {I} {J} → DPoly {I} {I}
second (ppoly A s B p₁ p₂ t₁ t₂) = dpoly A s B p₂ t₂

R : {I J : Set} → ParamPoly {I} {J} →
    (X : Fam J) → (Y : Fam I) → Fam I
R (ppoly A s B p₁ p₂ t₁ t₂) X Y =
  Σ' s (Π' p₁ ((t₁ *) X) ×ᵢ Π' p₂ ((t₂ *) Y))

data ⊤' (I : Set) : I → Set where
  tt : (i : I) → ⊤' I i

⊤'-unique : {I : Set} {i : I} → (x y : ⊤' I i) → x ≡ y
⊤'-unique (tt i) (tt .i) = refl

triv-param : {I J : Set} → (P : ParamPoly {I} {J}) →
             (Y : Fam I) → R P (⊤' J) Y ≃' T (second P) Y
triv-param {I} {J} (ppoly A s B p₁ p₂ t₁ t₂) Y i =
  record
  { f = f i
  ; f⃖ = g i
  ; is-invˡ = is-invˡ i
  ; is-invʳ = is-invʳ i
  }
  where
    P = ppoly A s B p₁ p₂ t₁ t₂

    f : (i : I) → R P (⊤' J) Y i → T (second P) Y i
    f ._ (ins a (v₁ , v₂)) = ins a v₂

    triv : (a : A) → Π' p₁ ((t₁ *) (⊤' J)) a
    triv a = abs (λ b → tt (t₁ b)) a

    g : (i : I) → T (second P) Y i → R P (⊤' J) Y i
    g ._ (ins a v) = ins a (triv a , v)

    is-invˡ : (i : I) → (x : R P (⊤' J) Y i) → g i (f i x) ≡ x
    is-invˡ ._ (ins a (v₁ , v₂)) =
      Σ'-eq a a (triv a , v₂) (v₁ , v₂)
      refl
      (×-eq (triv a) v₁ v₂ v₂
        (Π'-ext a (triv a) v₁ (lem a v₁))
        refl)
      where
        lem : (a : A) (v₁ :  Π' p₁ ((t₁ *) (⊤' J)) a)
              (i₁ : B) (p : p₁ i₁ ≡ a) →
              triv a (i₁ , p) ≡ v₁ (i₁ , p)
        lem ._ v₁ i₁ refl = ⊤'-unique (tt (t₁ i₁)) (app i₁ v₁)

    is-invʳ : (i : I) → (x : T (second P) Y i) → f i (g i x) ≡ x
    is-invʳ ._ (ins a x) = refl

triv : {I J : Set} (P : DPoly {I} {J}) → T P (⊤' J) ≃' toFam (DPoly.s P)
triv {I} {J} (dpoly A s E p t) i =
  record
  { f = f i
  ; f⃖ = g i
  ; is-invˡ = p₁ i
  ; is-invʳ = p₂ i
  }
  where
    P = dpoly A s E p t

    f : (i : I) → T P (⊤' J) i → toFam s i
    f ._ (ins a v) = (a , refl)

    g : (i : I) → toFam s i → T P (⊤' J) i
    g ._ (a , refl) = ins a (abs'' a v)
      where
        v : (b : E) → p b ≡ a → ⊤' J (t b)
        v b _ = tt (t b)

    p₁ : (i : I) → (x : T (dpoly A s E p t) (⊤' J) i) → g i (f i x) ≡ x
    p₁ ._ (ins a v) =
      Σ'-eq a a (abs'' a (λ b _ → tt (t b))) v
            refl
            (Π'-ext a (abs'' a (λ b _ → tt (t b))) v
                    (λ b p₂ → ⊤'-unique (tt (t b)) (v (b , p₂))))

    p₂ : (i : I) (b : toFam s i) → f i (g i b) ≡ b
    p₂ ._ (a , refl) = refl

foo : {A C P : Set} (B : Fam A) (D : Fam C) (Q : Fam P) (X : Set) →
      Σ (A × C) (λ {(a , c) → (B a → X) × (D c → Σ P (λ p → Q p → X)) })
      ≃
      Σ (A × Σ C (λ c → D c → P))
        (λ {(a , c , t) → ((B a → X) × ((Σ (D c) ((t *) Q)) → X))})
foo {A} {C} {P} B D Q X =
  record
  { f = f
  ; f⃖ = g
  ; is-invˡ = {!!}
  ; is-invʳ = {!!}
  }
  where
    f : Σ (A × C) (λ {(a , c) → (B a → X) × (D c → Σ P (λ p → Q p → X)) }) →
        Σ (A × Σ C (λ c → D c → P))
          (λ {(a , c , t) → ((B a → X) × ((Σ (D c) ((t *) Q)) → X))})
    f ((a , c) , t) =
      ((a , c , (λ d → proj₁ (proj₂ t d) ))
      , ((proj₁ t) , (λ { (d , q) → proj₂ (proj₂ t d) q})))

    g : Σ (A × Σ C (λ c → D c → P))
          (λ {(a , c , t) → ((B a → X) × ((Σ (D c) ((t *) Q)) → X))}) →
        Σ (A × C) (λ {(a , c) → (B a → X) × (D c → Σ P (λ p → Q p → X)) })
    g ((a , c , t₁) , t₂) =
      ((a , c)
      , (proj₁ t₂)
      , (λ d → (t₁ d , (λ q → proj₂ t₂ (d , q)))))

δ : {I : Set} → I → I × I
δ i = (i , i)

bar : {I K L : Set} (t : K → I) (v : L → I) (A : Fam K) (B : Fam L) →
      (Σ' t A ×ᵢ Σ' v B) ≃' (δ *) (Σ' (t ⊗ v) ((proj₁ *) A ×ᵢ (proj₂ *) B))
bar {I} {K} {L} t v A B i =
  record
  { f = f i
  ; f⃖ = g i
  ; is-invˡ = {!!}
  ; is-invʳ = {!!}
  }
  where
    f : (i : I) →
        (Σ' t A ×ᵢ Σ' v B) i → (Σ' (t ⊗ v) ((proj₁ *) A ×ᵢ (proj₂ *) B)) (i , i)
    f .(t k) (ins k x , y) = h k x (t k) refl y
      where
        h : (k : K) (x : A k) (i : I) (p : t k ≡ i) →
            (Σ' v B) i → (Σ' (t ⊗ v) ((proj₁ *) A ×ᵢ (proj₂ *) B)) (i , i)
        h k x ._ p (ins l y) =
          ins' (k , l)
               ((v l) , (v l))
               (⊗-eq {f = t} {v} k l p)
               (x , y)

    g : (i : I) →
        (Σ' (t ⊗ v) ((proj₁ *) A ×ᵢ (proj₂ *) B)) (i , i) → (Σ' t A ×ᵢ Σ' v B) i
    g i x =
      let
        k = proj₁ (p₁' x)
        l = proj₂ (p₁' x)

        q₁ : t k ≡ i
        q₁ = ×-eqˡ (p₌' x)

        q₂ : v l ≡ i
        q₂ = ×-eqʳ (p₌' x)

        u : A k × B l
        u = p₂' x
      in (ins' k i q₁ (proj₁ u) , ins' l i q₂ (proj₂ u))

_∣_ : {I J : Set} → Fam I → Fam J → Fam (I ⊎ J)
X ∣ Y = [ X , Y ]′

PB : {A₁ A₂ I : Set} → (A₁ → I) → (A₂ → I) → Set
PB {A₁} {A₂} f g = Σ[ x ∈ A₁ × A₂ ] (f (proj₁ x) ≡ g (proj₂ x))

PB-idx : {A₁ A₂ I : Set} → (f : A₁ → I) → (g : A₂ → I) → (PB f g → I)
PB-idx f _ ((x₁ , x₂) , _) = f x₁

_⊎'_ : {I J K : Set} → DPoly {I} {J} → DPoly {I} {K} → DPoly {I} {J ⊎ K}
(dpoly A₁ s₁ B₁ p₁ t₁) ⊎' (dpoly A₂ s₂ B₂ p₂ t₂)
  = dpoly A (PB-idx s₁ s₂) (U₁ ⊎ U₂) [ q₁ , q₂ ]′ (Sum.map (t₁ ∘ r₁) (t₂ ∘ r₂))
  where
    A = PB s₁ s₂
    U₁ = PB {A} (proj₁ ∘ proj₁) p₁
    U₂ = PB {A} (proj₂ ∘ proj₁) p₂
    q₁ : U₁ → A
    q₁ = proj₁ ∘ proj₁
    q₂ : U₂ → A
    q₂ = proj₁ ∘ proj₁
    r₁ : U₁ → B₁
    r₁ = proj₂ ∘ proj₁
    r₂ : U₂ → B₂
    r₂ = proj₂ ∘ proj₁

parametric-poly-by-sum : {I J K : Set} →
  (P : DPoly {I} {J}) → (Q : DPoly {I} {K}) →
  ∀ X Y → (T P ×ᵢ' T Q)(X , Y) ≃' T (P ⊎' Q) (X ∣ Y)
parametric-poly-by-sum {I} {J} P Q X Y i =
  record
  { f = f i
  ; f⃖ = g i
  ; is-invˡ = {!!}
  ; is-invʳ = {!!}
  }

  where

  f : (i : I) → (T P ×ᵢ' T Q) (X , Y) i → T (P ⊎' Q) (X ∣ Y) i
  f ._ (ins a v , y) = h a v (DPoly.s P a) refl y
    where
      h : (a : DPoly.A P)
          (v : Π' (DPoly.p P) (λ e → X (DPoly.t P e)) a)
          (i : I)
          (p : DPoly.s P a ≡ i) →
          T Q Y i → T (P ⊎' Q) (X ∣ Y) i
      h a₁ v₁ ._ p (ins a₂ v₂) = ins' a₃ (DPoly.s Q a₂) p₃ v₃
        where
          a₃ : DPoly.A (P ⊎' Q)
          a₃ = (a₁ , a₂) , p

          p₃ : DPoly.s (P ⊎' Q) a₃ ≡ DPoly.s Q a₂
          p₃ = trans refl p

          v₃ : Π' (DPoly.p (P ⊎' Q)) (λ e → (X ∣ Y) (DPoly.t (P ⊎' Q) e)) a₃
          v₃ = abs'' a₃ (v' a₃ refl)
            where
            v' : (u : DPoly.A (P ⊎' Q)) →
                 (a₁ , a₂) ≡ proj₁ u →
                 (i₁ : DPoly.E (P ⊎' Q)) →
                 DPoly.p (P ⊎' Q) i₁ ≡ u →
                 ((X ∣ Y) (DPoly.t (P ⊎' Q) i₁))
            v' u r (inj₁ ((.u , b) , q)) refl = v₁ (b , sym (trans (×-eqˡ r) q))
            v' u r (inj₂ ((.u , b) , q)) refl = v₂ (b , sym (trans (×-eqʳ r) q))

  g :  (i : I) → T (P ⊎' Q) (X ∣ Y) i → (T P ×ᵢ' T Q) (X , Y) i
  g ._ (ins x v) =
    (ins (proj₁ (proj₁ x)) v₁
    , ins' a₂ (DPoly.s P a₁) (sym (proj₂ x)) v₂)
    where
      a₁ = proj₁ (proj₁ x)
      a₂ = proj₂ (proj₁ x)

      v₁ : Π' (DPoly.p P) (((DPoly.t P) *) X) a₁
      v₁ = abs'' a₁ (v₁' a₁ refl)
        where
          v₁' : (a : DPoly.A P) (p : a₁ ≡ a) (b : DPoly.E P) →
                DPoly.p P b ≡ a → X (DPoly.t P b)
          v₁' ._ p b refl = v ((inj₁ ((x , b) , p)) , refl)

      v₂ : Π' (DPoly.p Q) (((DPoly.t Q) *) Y) a₂
      v₂ = abs'' a₂ (v₂' a₂ refl)
        where
          v₂' : (a : DPoly.A Q) (p : a₂ ≡ a) (b : DPoly.E Q) →
                DPoly.p Q b ≡ a → Y (DPoly.t Q b)
          v₂' ._ p b refl = v ((inj₂ ((x , b) , p)) , refl)

{-
 : {I J : Set} → (P : DPoly {I} {J}) → (Q : DPoly {I} {J}) →
       let H = λ X Y → ⟦ P ⟧ X ×ᵢ ⟦ Q ⟧
       in
-}