{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Properties.Conversion {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.RedSteps
open import Definition.Typed.Reduction
import Definition.Typed.Weakening as W
open import Definition.Typed.Properties
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance

open import Tools.Product
import Tools.PropositionalEquality as PE

import Data.Fin as Fin
import Data.Nat as Nat

-- Conversion of syntactic reduction closures.
convRed:*: : ∀ {t u A B Γ l} → Γ ⊢ t :⇒*: u ∷ A ^ l → Γ ⊢ A ≡ B ^ [ ! , l ] → Γ ⊢ t :⇒*: u ∷ B ^ l
convRed:*: [[ ⊢t , ⊢u , d ]] A≡B = [[ conv ⊢t  A≡B , conv ⊢u  A≡B , conv* d  A≡B ]]

-- helper functions for the universe
convTermTUniv :  ∀ {Γ A B t l l' r ll l< d r' ll' l<' el' d'}
                      (er : r PE.≡ r') (ellll' : ll PE.≡ ll') →
                      Γ ⊩⟨ l ⟩  t ∷ A ^ [ ! , next ll ] / Uᵣ (Uᵣ r ll l< PE.refl d) →
                      Γ ⊩⟨ l' ⟩ t ∷ B ^ [ ! , next ll ] / Uᵣ (Uᵣ r' ll' l<' el' d')
convTermTUniv {l< = l<} {l<' = l<'} {d' = d'} er ellll' (Uₜ K d typeK K≡K [t]) =
    let dd = PE.subst (λ x → _ ⊢ _ :⇒*: Univ x _ ^ _) (PE.sym er) (PE.subst (λ x → _ ⊢ _ :⇒*: Univ _ x ^ [ ! , next x ]) (PE.sym ellll') d') in
    reduction-irrelevant-Univ {l< = l<} {l<' = l<'} {el = PE.refl} {D = dd} {D' = d'} er (Uₜ K d typeK K≡K [t])

convEqTermTUniv : ∀ {Γ A B t u l r ll l< d dd} →
                    Γ ⊩⟨ l ⟩ t ≡ u ∷ A ^ [ ! , next ll ] / Uᵣ (Uᵣ r ll l< PE.refl d) →
                    Γ ⊩⟨ l ⟩ t ≡ u ∷ B ^ [ ! , next ll ] / Uᵣ (Uᵣ r ll l< PE.refl dd)
convEqTermTUniv {l = ι ¹} {r = r} {⁰} (Uₜ₌ [t] [u] A≡B [t≡u]) =
                   Uₜ₌ (convTermTUniv PE.refl PE.refl [t]) (convTermTUniv PE.refl PE.refl [u]) A≡B [t≡u]
convEqTermTUniv {l = ∞} {r = r} {¹} (Uₜ₌ [t] [u] A≡B [t≡u]) =
                   Uₜ₌ (convTermTUniv PE.refl PE.refl [t]) (convTermTUniv PE.refl PE.refl [u]) A≡B [t≡u]

mutual
  -- Helper function for conversion of terms converting from left to right.

  convTermT₁ : ∀ {Γ A B r t l l′} {[A] : Γ ⊩⟨ l ⟩ A ^ r} {[B] : Γ ⊩⟨ l′ ⟩ B ^ r}
            → ShapeView Γ l l′ A B r r [A] [B]
            → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
            → Γ ⊩⟨ l ⟩  t ∷ A ^ r / [A]
            → Γ ⊩⟨ l′ ⟩ t ∷ B ^ r / [B]

  convTermT₁ (ℕᵥ D D′) A≡B t = t
  convTermT₁ (Emptyᵥ D D′) A≡B t = t

  convTermT₁ {r = [ ! , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M) (neₜ k d (neNfₜ neK₂ ⊢k k≡k)) =
    let K≡K₁ = PE.subst (λ x → _ ⊢ _ ≡ x ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (≅-eq (~-to-≅ K≡M))
    in  neₜ k (convRed:*: d K≡K₁)
              (neNfₜ neK₂ (conv ⊢k K≡K₁) (~-conv k≡k K≡K₁))
  convTermT₁ {r = [ % , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
             (neₜ d) = let K≡K₁ = PE.subst (λ x → _ ⊢ _ ≡ x ^ _)
                                          (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                                          (≅-eq (~-to-≅ K≡M))
                       in neₜ (conv d (reduction  (red D) (red D₁) (ne neK) (ne neK₁) K≡K₁))
  convTermT₁ {Γ = Γ} {r = [ ! , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             (Πₜ f d funcF f≡f [f] [f]₁) =
    let ΠF₁G₁≡ΠF′G′   = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
        F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ , lΠ≡lΠ₁ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
        ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ ! , ι ll ]) (PE.sym ΠF₁G₁≡ΠF′G′)
                             (≅-eq A≡B)
    in  Πₜ f (convRed:*: d ΠFG≡ΠF₁G₁) funcF (≅-conv f≡f ΠFG≡ΠF₁G₁)
           (λ {ρ} [ρ] ⊢Δ [a] [b] [a≡b] →
              let [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                           ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                  [a]₁ = convTerm₂′ PE.refl (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                  [b]₁ = convTerm₂′ PE.refl (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [b]
                  [a≡b]₁ = convEqTerm₂′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a≡b]
                  [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                    (PE.sym G₁≡G′))
                                           ([G] [ρ] ⊢Δ [a]₁)
                                           ([G≡G′] [ρ] ⊢Δ [a]₁)
              in  convEqTerm₁′ PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]₁) ([G]₁ [ρ] ⊢Δ [a]) [G≡G₁]
                              ([f] [ρ] ⊢Δ [a]₁ [b]₁ [a≡b]₁))
          (λ {ρ} [ρ] ⊢Δ [a] →
             let [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                          ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                 [a]₁ = convTerm₂′ PE.refl (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                 [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                   (PE.sym G₁≡G′))
                                          ([G] [ρ] ⊢Δ [a]₁)
                                          ([G≡G′] [ρ] ⊢Δ [a]₁)
             in  convTerm₁′ PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]₁) ([G]₁ [ρ] ⊢Δ [a]) [G≡G₁] ([f]₁ [ρ] ⊢Δ [a]₁))
  convTermT₁ {Γ = Γ} {r = [ % , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             d = let ΠF₁G₁≡ΠF′G′   = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
                     F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
                     ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ % , ι ll ]) (PE.sym ΠF₁G₁≡ΠF′G′)
                                          (≅-eq A≡B)
                 in conv d ΠFG≡ΠF₁G₁
  convTermT₁ {Γ = Γ} {r = [ % , ll ]} (∃ᵥ (∃ᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (∃ᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             d = let ∃F₁G₁≡∃F′G′   = whrDet* (red D₁ , ∃ₙ) (D′ , ∃ₙ)
                     F₁≡F′ , G₁≡G′ = ∃-PE-injectivity ∃F₁G₁≡∃F′G′
                     ∃FG≡∃F₁G₁ = PE.subst (λ x → Γ ⊢ ∃ F ▹ G ≡ x ^ [ % , ll ]) (PE.sym ∃F₁G₁≡∃F′G′)
                                          (≅-eq A≡B)
                 in conv d ∃FG≡∃F₁G₁
  convTermT₁ (Uᵥ (Uᵣ r l l< PE.refl d) (Uᵣ r' l' l<' el' d')) A≡B X =
    let U≡U   = whrDet* (A≡B , Uₙ) (red d' , Uₙ)
        r≡r , l≡l = Univ-PE-injectivity U≡U
    in convTermTUniv r≡r l≡l X
  convTermT₁ (emb⁰¹ X) A≡B t = convTermT₁ X A≡B t
  convTermT₁ (emb¹⁰ X) A≡B t = convTermT₁ X A≡B t
  convTermT₁ (emb¹∞ X) A≡B t = convTermT₁ X A≡B t
  convTermT₁ (emb∞¹ X) A≡B t = convTermT₁ X A≡B t

  -- Helper function for conversion of terms converting from right to left.
  convTermT₂ : ∀ {l l′ Γ A B r t} {[A] : Γ ⊩⟨ l ⟩ A ^ r} {[B] : Γ ⊩⟨ l′ ⟩ B ^ r}
           → ShapeView Γ l l′ A B r r [A] [B]
           → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
           → Γ ⊩⟨ l′ ⟩ t ∷ B ^ r / [B]
           → Γ ⊩⟨ l ⟩  t ∷ A ^ r / [A]
  convTermT₂ (ℕᵥ D D′) A≡B t = t
  convTermT₂ (Emptyᵥ D D′) A≡B t = t
  convTermT₂ {r = [ ! , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
             (neₜ k d (neNfₜ neK₂ ⊢k k≡k)) =
    let K₁≡K = PE.subst (λ x → _ ⊢ x ≡ _ ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (sym (≅-eq (~-to-≅ K≡M)))
    in  neₜ k (convRed:*: d K₁≡K)
            (neNfₜ neK₂ (conv ⊢k K₁≡K) (~-conv k≡k K₁≡K))
  convTermT₂ {r = [ % , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
             (neₜ d) = let K₁≡K = PE.subst (λ x → _ ⊢ x ≡ _ ^ _)
                                           (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                                           (sym (≅-eq (~-to-≅ K≡M)))
                       in neₜ (conv d (reduction  (red D₁) (red D) (ne neK₁) (ne neK) K₁≡K))
  convTermT₂ {Γ = Γ} {r = [ ! , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             (Πₜ f d funcF f≡f [f] [f]₁) =
    let ΠF₁G₁≡ΠF′G′   = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
        F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ , lΠ≡lΠ₁ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
        ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ ! , ι ll ])
                             (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
    in  Πₜ f (convRed:*: d (sym ΠFG≡ΠF₁G₁)) funcF (≅-conv f≡f (sym ΠFG≡ΠF₁G₁))
           (λ {ρ} [ρ] ⊢Δ [a] [b] [a≡b] →
              let [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                           ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                  [a]₁ = convTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                  [b]₁ = convTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [b]
                  [a≡b]₁ = convEqTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a≡b]
                  [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                    (PE.sym G₁≡G′))
                                           ([G] [ρ] ⊢Δ [a])
                                           ([G≡G′] [ρ] ⊢Δ [a])
              in  convEqTerm₂′ PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁)
                              [G≡G₁] ([f] [ρ] ⊢Δ [a]₁ [b]₁ [a≡b]₁))
           (λ {ρ} [ρ] ⊢Δ [a] →
              let [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                           ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                  [a]₁ = convTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                  [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                    (PE.sym G₁≡G′))
                                           ([G] [ρ] ⊢Δ [a])
                                           ([G≡G′] [ρ] ⊢Δ [a])
              in  convTerm₂′ PE.refl PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁)
                            [G≡G₁] ([f]₁ [ρ] ⊢Δ [a]₁))
  convTermT₂ {Γ = Γ} {r = [ % , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             d = let ΠF₁G₁≡ΠF′G′   = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
                     F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
                     ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ % , ι ll ])
                                          (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
                 in  conv d (sym ΠFG≡ΠF₁G₁)
  convTermT₂ {Γ = Γ} {r = [ % , ll ]} (∃ᵥ (∃ᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                         (∃ᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
             (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
             d = let ∃F₁G₁≡∃F′G′   = whrDet* (red D₁ , ∃ₙ) (D′ , ∃ₙ)
                     F₁≡F′ ,  G₁≡G′ = ∃-PE-injectivity ∃F₁G₁≡∃F′G′
                     ∃FG≡∃F₁G₁ = PE.subst (λ x → Γ ⊢ ∃ F ▹ G ≡ x ^ [ % , ll ])
                                          (PE.sym ∃F₁G₁≡∃F′G′) (≅-eq A≡B)
                 in  conv d (sym ∃FG≡∃F₁G₁)
  convTermT₂ (Uᵥ (Uᵣ r l l< el d) (Uᵣ r' l' l<' PE.refl d')) A≡B X =
    let U≡U   = whrDet* (A≡B , Uₙ) (red d' , Uₙ)
        r≡r , l≡l = Univ-PE-injectivity U≡U
    in convTermTUniv (PE.sym r≡r) (PE.sym l≡l) X
  convTermT₂ (emb⁰¹ X) A≡B t = convTermT₂ X A≡B t
  convTermT₂ (emb¹⁰ X) A≡B t = convTermT₂ X A≡B t
  convTermT₂ (emb¹∞ X) A≡B t = convTermT₂ X A≡B t
  convTermT₂ (emb∞¹ X) A≡B t = convTermT₂ X A≡B t

  -- Conversion of terms converting from left to right.
  convTerm₁ : ∀ {Γ A B r ll t l l′} ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r , ll ])
            → Γ ⊩⟨ l ⟩  A ≡ B ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l ⟩  t ∷ A ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l′ ⟩ t ∷ B ^ [ r , ll ] / [B]
  convTerm₁ [A] [B] A≡B t = convTermT₁ (goodCases [A] [B] A≡B) A≡B t

  -- Conversion of terms converting from left to right. with PE
  convTerm₁′ : ∀ {Γ A B r r' ll ll' t l l′} (eq : r PE.≡ r') (eql : ll PE.≡ ll')
                 ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r' , ll' ])
            → Γ ⊩⟨ l ⟩  A ≡ B ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l ⟩  t ∷ A ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l′ ⟩ t ∷ B ^ [ r' , ll' ] / [B]
  convTerm₁′ PE.refl PE.refl [A] [B] A≡B t = convTerm₁ [A] [B] A≡B t

  -- Conversion of terms converting from right to left.
  convTerm₂ : ∀ {Γ A B r t l l′} ([A] : Γ ⊩⟨ l ⟩ A ^ r) ([B] : Γ ⊩⟨ l′ ⟩ B ^ r)
          → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
          → Γ ⊩⟨ l′ ⟩ t ∷ B ^ r / [B]
          → Γ ⊩⟨ l ⟩  t ∷ A ^ r / [A]
  convTerm₂ [A] [B] A≡B t = convTermT₂ (goodCases [A] [B] A≡B) A≡B t

  -- Conversion of terms converting from right to left
  -- with some propsitionally equal types.
  convTerm₂′ : ∀ {Γ A r r' ll ll' B B′ t l l′} → B PE.≡ B′ → r PE.≡ r' → ll PE.≡ ll'
          → ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ] ) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r' , ll' ])
          → Γ ⊩⟨ l ⟩  A ≡ B′ ^ [ r , ll ] / [A]
          → Γ ⊩⟨ l′ ⟩ t ∷ B ^ [ r' , ll' ] / [B]
          → Γ ⊩⟨ l ⟩  t ∷ A ^ [ r , ll ] / [A]
  convTerm₂′ PE.refl PE.refl PE.refl [A] [B] A≡B t = convTerm₂ [A] [B] A≡B t

  -- Helper function for conversion of term equality converting from left to right.
  convEqTermT₁ : ∀ {l l′ Γ A B r t u} {[A] : Γ ⊩⟨ l ⟩ A ^ r} {[B] : Γ ⊩⟨ l′ ⟩ B ^ r}
               → ShapeView Γ l l′ A B r r [A] [B]
               → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
               → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ r / [A]
               → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ r / [B]
  convEqTermT₁ (ℕᵥ D D′) A≡B t≡u = t≡u
  convEqTermT₁ (Emptyᵥ D D′) A≡B t≡u = t≡u
  convEqTermT₁ {r = [ ! , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
               (neₜ₌ k m d d′ (neNfₜ₌ neK₂ neM₁ k≡m)) =
    let K≡K₁ = PE.subst (λ x → _ ⊢ _ ≡ x ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (≅-eq (~-to-≅ K≡M))
    in  neₜ₌ k m (convRed:*: d K≡K₁)
                 (convRed:*: d′ K≡K₁)
                 (neNfₜ₌ neK₂ neM₁ (~-conv k≡m K≡K₁))
  convEqTermT₁ {r = [ % , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
               (neₜ₌ d d′) =
    let K≡K₁ = PE.subst (λ x → _ ⊢ _ ≡ x ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (≅-eq (~-to-≅ K≡M))
    in neₜ₌ (conv d (reduction  (red D) (red D₁) (ne neK) (ne neK₁) K≡K₁))
            (conv d′ (reduction  (red D) (red D₁) (ne neK) (ne neK₁) K≡K₁))
  convEqTermT₁ {Γ = Γ} {r = [ ! , ι ll ]} (Πᵥ (Πᵣ rF lF lG lF≤ lG≤  F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (Πᵣ rF₁ lF₁ lG₁ lF₁≤ lG₁≤  F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (Πₜ₌ f g d d′ funcF funcG t≡u [t] [u] [t≡u]) =
    let [A] = Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext
        [B] = Πᵣ′ rF₁ lF₁ lG₁ lF₁≤ lG₁≤ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁
        [A≡B] = Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]
        ΠF₁G₁≡ΠF′G′ = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
        ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ ! , ι ll ])
                             (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
    in  Πₜ₌ f g (convRed:*: d ΠFG≡ΠF₁G₁) (convRed:*: d′ ΠFG≡ΠF₁G₁)
            funcF funcG (≅-conv t≡u ΠFG≡ΠF₁G₁)
            (convTerm₁ [A] [B] [A≡B] [t]) (convTerm₁ [A] [B] [A≡B] [u])
            (λ {ρ} [ρ] ⊢Δ [a] →
               let F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ , lΠ≡lΠ₁ = Π-PE-injectivity (whrDet* (red D₁ , Πₙ) (D′ , Πₙ))
                   [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                            ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                   [a]₁ = convTerm₂′ PE.refl (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                   [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                     (PE.sym G₁≡G′))
                                            ([G] [ρ] ⊢Δ [a]₁)
                                            ([G≡G′] [ρ] ⊢Δ [a]₁)
               in  convEqTerm₁′ PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]₁) ([G]₁ [ρ] ⊢Δ [a])
                               [G≡G₁] ([t≡u] [ρ] ⊢Δ [a]₁))
  convEqTermT₁ {Γ = Γ} {r = [ % , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (d , d′) = let ΠF₁G₁≡ΠF′G′ = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
                              ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ % , ι ll ])
                                                   (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
                          in conv d ΠFG≡ΠF₁G₁ , conv d′ ΠFG≡ΠF₁G₁
  convEqTermT₁ {Γ = Γ} {r = [ % , ll ]} (∃ᵥ (∃ᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (∃ᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (d , d′) = let ∃F₁G₁≡∃F′G′ = whrDet* (red D₁ , ∃ₙ) (D′ , ∃ₙ)
                              ∃FG≡∃F₁G₁ = PE.subst (λ x → Γ ⊢ ∃ F ▹ G ≡ x ^ [ % , ll ])
                                                   (PE.sym ∃F₁G₁≡∃F′G′) (≅-eq A≡B)
                          in (conv d ∃FG≡∃F₁G₁) , conv d′ ∃FG≡∃F₁G₁
  convEqTermT₁ (Uᵥ (Uᵣ r ll l< PE.refl d) (Uᵣ r' ll' l<' el' d')) A≡B X =
    let U≡U   = whrDet* (A≡B , Uₙ) (red d' , Uₙ)
        r≡r , l≡l = Univ-PE-injectivity U≡U
        dd = PE.subst (λ x → _ ⊢ _ :⇒*: Univ x _ ^ _) (PE.sym r≡r) (PE.subst (λ x → _ ⊢ _ :⇒*: Univ _ x ^ [ ! , next x ]) (PE.sym l≡l) d')
    in reduction-irrelevant-Univ= {l< = l<} {l<' = l<'} {el = PE.refl} {el' = el'} {D = dd} {D' = d'} r≡r (convEqTermTUniv X)
  convEqTermT₁ (emb⁰¹ X) A≡B t≡u = convEqTermT₁ X A≡B t≡u
  convEqTermT₁ (emb¹⁰ X) A≡B t≡u = convEqTermT₁ X A≡B t≡u
  convEqTermT₁ (emb¹∞ X) A≡B t≡u = convEqTermT₁ X A≡B t≡u
  convEqTermT₁ (emb∞¹ X) A≡B t≡u = convEqTermT₁ X A≡B t≡u

  -- Helper function for conversion of term equality converting from right to left.
  convEqTermT₂ : ∀ {l l′ Γ A B t u r} {[A] : Γ ⊩⟨ l ⟩ A ^ r} {[B] : Γ ⊩⟨ l′ ⟩ B ^ r}
             → ShapeView Γ l l′ A B r r [A] [B]
             → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
             → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ r / [B]
             → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ r / [A]
  convEqTermT₂ (ℕᵥ D D′) A≡B t≡u = t≡u
  convEqTermT₂ (Emptyᵥ D D′) A≡B t≡u = t≡u
  convEqTermT₂ {r = [ ! , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
               (neₜ₌ k m d d′ (neNfₜ₌ neK₂ neM₁ k≡m)) =
    let K₁≡K = PE.subst (λ x → _ ⊢ x ≡ _ ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (sym (≅-eq (~-to-≅ K≡M)))
    in  neₜ₌ k m (convRed:*: d K₁≡K) (convRed:*: d′ K₁≡K)
                 (neNfₜ₌ neK₂ neM₁ (~-conv k≡m K₁≡K))
  convEqTermT₂ {r = [ % , ll ]} (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
               (neₜ₌ d d′) =
    let K₁≡K = PE.subst (λ x → _ ⊢ x ≡ _ ^ _)
                        (whrDet* (red D′ , ne neM) (red D₁ , ne neK₁))
                        (sym (≅-eq (~-to-≅ K≡M)))
    in neₜ₌ (conv d (reduction  (red D₁) (red D) (ne neK₁) (ne neK) K₁≡K))
            (conv d′ (reduction  (red D₁) (red D) (ne neK₁) (ne neK) K₁≡K))
  convEqTermT₂ {Γ = Γ} {r = [ ! , ι ll ]} (Πᵥ (Πᵣ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (Πᵣ rF₁ lF₁ lG₁ lF₁≤ lG₁≤ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (Πₜ₌ f g d d′ funcF funcG t≡u [t] [u] [t≡u]) =
    let [A] = Πᵣ′ rF lF lG lF≤ lG≤ F G D ⊢F ⊢G A≡A [F] [G] G-ext
        [B] = Πᵣ′ rF₁ lF₁ lG₁ lF₁≤ lG₁≤ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁
        [A≡B] = Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]
        ΠF₁G₁≡ΠF′G′ = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
        ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ ! , ι ll ])
                             (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
    in  Πₜ₌ f g (convRed:*: d (sym ΠFG≡ΠF₁G₁)) (convRed:*: d′ (sym ΠFG≡ΠF₁G₁))
            funcF funcG (≅-conv t≡u (sym ΠFG≡ΠF₁G₁))
            (convTerm₂ [A] [B] [A≡B] [t]) (convTerm₂ [A] [B] [A≡B] [u])
            (λ {ρ} [ρ] ⊢Δ [a] →
               let F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ , lΠ≡lΠ₁ = Π-PE-injectivity (whrDet* (red D₁ , Πₙ) (D′ , Πₙ))
                   [F≡F₁] = irrelevanceEqR′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                            ([F] [ρ] ⊢Δ) ([F≡F′] [ρ] ⊢Δ)
                   [a]₁ = convTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [F≡F₁] [a]
                   [G≡G₁] = irrelevanceEqR′ (PE.cong (λ x → wk (lift ρ) x [ _ ])
                                                     (PE.sym G₁≡G′))
                                            ([G] [ρ] ⊢Δ [a])
                                            ([G≡G′] [ρ] ⊢Δ [a])
               in  convEqTerm₂′ PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) ([G] [ρ] ⊢Δ [a]) ([G]₁ [ρ] ⊢Δ [a]₁)
                               [G≡G₁] ([t≡u] [ρ] ⊢Δ [a]₁))
  convEqTermT₂ {Γ = Γ} {r = [ % , ι ll ]} (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (d , d′) =
               let ΠF₁G₁≡ΠF′G′ = whrDet* (red D₁ , Πₙ) (D′ , Πₙ)
                   ΠFG≡ΠF₁G₁ = PE.subst (λ x → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° ll ≡ x ^ [ % , ι ll ])
                                        (PE.sym ΠF₁G₁≡ΠF′G′) (≅-eq A≡B)
               in (conv d (sym ΠFG≡ΠF₁G₁)) , (conv d′ (sym ΠFG≡ΠF₁G₁))
  convEqTermT₂ {Γ = Γ} {r = [ % , ll ]} (∃ᵥ (∃ᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                           (∃ᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
               (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
               (d , d′) =
               let ∃F₁G₁≡∃F′G′ = whrDet* (red D₁ , ∃ₙ) (D′ , ∃ₙ)
                   ∃FG≡∃F₁G₁ = PE.subst (λ x → Γ ⊢ ∃ F ▹ G ≡ x ^ [ % , ll ])
                                        (PE.sym ∃F₁G₁≡∃F′G′) (≅-eq A≡B)
               in (conv d (sym ∃FG≡∃F₁G₁)) , (conv d′ (sym ∃FG≡∃F₁G₁))
  convEqTermT₂ (Uᵥ (Uᵣ r l l< el d) (Uᵣ r' l' l<' PE.refl d')) A≡B X =
    let U≡U   = whrDet* (A≡B , Uₙ) (red d' , Uₙ)
        r≡r , l≡l = Univ-PE-injectivity (PE.sym U≡U)
        dd = PE.subst (λ x → _ ⊢ _ :⇒*: Univ x _ ^ _) (PE.sym r≡r) (PE.subst (λ x → _ ⊢ _ :⇒*: Univ _ x ^ [ ! , next x ]) (PE.sym l≡l) d)
    in reduction-irrelevant-Univ= {l< = l<'} {el = PE.refl} {D = dd} {D' = d} r≡r (convEqTermTUniv X)
  convEqTermT₂ (emb⁰¹ X) A≡B t≡u = convEqTermT₂ X A≡B t≡u
  convEqTermT₂ (emb¹⁰ X) A≡B t≡u = convEqTermT₂ X A≡B t≡u
  convEqTermT₂ (emb¹∞ X) A≡B t≡u = convEqTermT₂ X A≡B t≡u
  convEqTermT₂ (emb∞¹ X) A≡B t≡u = convEqTermT₂ X A≡B t≡u

  -- Conversion of term equality converting from left to right.
  convEqTerm₁ : ∀ {l l′ Γ A B t u r} ([A] : Γ ⊩⟨ l ⟩ A ^ r) ([B] : Γ ⊩⟨ l′ ⟩ B ^ r)
              → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
              → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ r / [A]
              → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ r / [B]
  convEqTerm₁ [A] [B] A≡B t≡u = convEqTermT₁ (goodCases [A] [B] A≡B) A≡B t≡u

  -- Conversion of term equality converting from left to right. with PE
  convEqTerm₁′ : ∀ {l l′ Γ A B t u r r' ll ll'} (eq : r PE.≡ r') (eql : ll PE.≡ ll')
                   ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r' , ll' ])
              → Γ ⊩⟨ l ⟩  A ≡ B ^ [ r , ll ] / [A]
              → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ [ r , ll ] / [A]
              → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ [ r' , ll' ] / [B]
  convEqTerm₁′ PE.refl PE.refl [A] [B] A≡B t≡u = convEqTerm₁ [A] [B] A≡B t≡u

  -- Conversion of term equality converting from right to left.
  convEqTerm₂ : ∀ {l l′ Γ A B t u r ll} ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r , ll ])
            → Γ ⊩⟨ l ⟩  A ≡ B ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ [ r , ll ] / [B]
            → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ [ r , ll ] / [A]
  convEqTerm₂ [A] [B] A≡B t≡u = convEqTermT₂ (goodCases [A] [B] A≡B) A≡B t≡u

  -- Conversion of term equality converting from right to left with PE
  convEqTerm₂′ : ∀ {l l′ Γ A B t u r r' ll ll'} (eq : r PE.≡ r') (eql : ll PE.≡ ll')
                   ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r' , ll' ])
            → Γ ⊩⟨ l ⟩  A ≡ B ^ [ r , ll ] / [A]
            → Γ ⊩⟨ l′ ⟩ t ≡ u ∷ B ^ [ r' , ll' ] / [B]
            → Γ ⊩⟨ l ⟩  t ≡ u ∷ A ^ [ r , ll ] / [A]
  convEqTerm₂′ PE.refl PE.refl [A] [B] A≡B t≡u = convEqTerm₂ [A] [B] A≡B t≡u


------------------------------------------------------------------------
-- The Agda standard library
--
-- de Bruijn-aware generic traversal of reflected terms.
------------------------------------------------------------------------

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

open import Category.Applicative using (RawApplicative)
open import Category.Monad using (RawMonad)

module Leftovers.Everywhere {F : Set → Set} (MF : RawMonad F) where

open import Data.Nat     using (ℕ; zero; suc; _+_)
open import Data.List    using (List; []; _∷_; _++_; reverse; length)
open import Data.Product using (_×_; _,_)
open import Data.String  using (String)
open import Function     using (_∘_)
open import Reflection

open RawMonad MF

------------------------------------------------------------------------
-- Context representation

-- Track both number of variables and actual context, to avoid having to
-- compute the length of the context everytime it's needed.
record Cxt : Set where
  constructor _,,_
  pattern
  field
    len     : ℕ
    context : List (String × Arg Term)

private
  _∷cxt_ : String × Arg Term → Cxt → Cxt
  e ∷cxt (n ,, Γ) = (suc n ,, (e ∷ Γ))

  _++cxt_ : List (String × Arg Term) → Cxt → Cxt
  es ++cxt (n ,, Γ) = ((length es + n) ,, (es ++ Γ))

------------------------------------------------------------------------
-- Actions

Action : Set → Set
Action A = Cxt → A → F A
-- A traversal gets to operate on variables, metas, and names.
record Actions : Set where
  field
    onVar  : Action ℕ
    onMeta : Action Meta
    onCon  : Action Name
    onDef  : Action Name

-- Default action: do nothing.
defaultActions : Actions
defaultActions .Actions.onVar  _ = pure
defaultActions .Actions.onMeta _ = pure
defaultActions .Actions.onCon  _ = pure
defaultActions .Actions.onDef  _ = pure

------------------------------------------------------------------------
-- Traversal functions
private
 module Params (actions : Actions) (everyAction : Action Term) where

  open Actions actions

  everywhereTerm    : Action Term
  everywhereTerm'    : Action Term
  everywhereSort    : Action Sort
  everywherePattern : Action Pattern
  everywhereArgs    : Action (List (Arg Term))
  everywhereArg     : Action (Arg Term)
  everywherePats    : Action (List (Arg Pattern))
  everywhereAbs     : Arg Term → Action (Abs Term)
  everywhereClauses : Action Clauses
  everywhereClause  : Action Clause
  everywhereTel     : Action (List (String × Arg Term))

  everywhereTerm Γ t = everyAction Γ =<< everywhereTerm' Γ t

  everywhereTerm' Γ (var x args)      = var       <$> onVar Γ x ⊛ everywhereArgs Γ args
  everywhereTerm' Γ (con c args)      = con       <$> onCon Γ c ⊛ everywhereArgs Γ args
  everywhereTerm' Γ (def f args)      = def       <$> onDef Γ f ⊛ everywhereArgs Γ args
  everywhereTerm' Γ (pat-lam cs args) = pat-lam   <$> everywhereClauses Γ cs ⊛ everywhereArgs Γ args
  everywhereTerm' Γ (pi a b)          = pi        <$> everywhereArg Γ a ⊛ everywhereAbs a Γ b
  everywhereTerm' Γ (agda-sort s)     = agda-sort <$> everywhereSort Γ s
  everywhereTerm' Γ (meta x args)     = meta      <$> onMeta Γ x ⊛ everywhereArgs Γ args
  everywhereTerm' Γ t@(lit _)         = pure t
  everywhereTerm' Γ t@unknown         = pure t
  everywhereTerm' Γ (lam v t)         = lam v     <$> everywhereAbs (arg (arg-info v m) unknown) Γ t
    where
    m = defaultModality

  everywhereArg Γ (arg i t) = arg i <$> everywhereTerm Γ t
  everywhereArgs Γ []       = pure []
  everywhereArgs Γ (a ∷ as) = _∷_ <$> everywhereArg Γ a ⊛ everywhereArgs Γ as

  everywhereAbs ty Γ (abs x t) = abs x <$> everywhereTerm ((x , ty) ∷cxt Γ) t

  everywhereClauses Γ []       = pure []
  everywhereClauses Γ (c ∷ cs) = _∷_ <$> everywhereClause Γ c ⊛ everywhereClauses Γ cs

  everywhereClause Γ (Clause.clause tel ps t) =
      Clause.clause <$> everywhereTel Γ tel
                     ⊛  everywherePats Γ′ ps
                     ⊛ everywhereTerm Γ′ t
    where Γ′ = reverse tel ++cxt Γ
  everywhereClause Γ (Clause.absurd-clause tel ps) =
      Clause.absurd-clause <$> everywhereTel Γ tel
                            ⊛  everywherePats Γ′ ps
    where Γ′ = reverse tel ++cxt Γ

  everywhereTel Γ [] = pure []
  everywhereTel Γ ((x , ty) ∷ tel) =
    _∷_ ∘ (x ,_) <$> everywhereArg Γ ty ⊛ everywhereTel ((x , ty) ∷cxt Γ) tel

  everywhereSort Γ (Sort.set t)       = Sort.set <$> everywhereTerm Γ t
  everywhereSort Γ t@(Sort.lit _)     = pure t
  everywhereSort Γ (Sort.prop t)      = Sort.prop <$> everywhereTerm Γ t
  everywhereSort Γ t@(Sort.propLit _) = pure t
  everywhereSort Γ t@(Sort.inf _)     = pure t
  everywhereSort Γ t@Sort.unknown     = pure t

  everywherePattern Γ (Pattern.con c ps) = Pattern.con <$> onCon Γ c ⊛ everywherePats Γ ps
  everywherePattern Γ (Pattern.dot t)    = Pattern.dot <$> everywhereTerm Γ t
  everywherePattern Γ (Pattern.var x)    = Pattern.var <$> onVar Γ x
  everywherePattern Γ p@(Pattern.lit _)  = pure p
  everywherePattern Γ p@(Pattern.proj _) = pure p
  everywherePattern Γ (Pattern.absurd x) = Pattern.absurd <$> onVar Γ x

  everywherePats Γ [] = pure []
  everywherePats Γ (arg i p ∷ ps) = _∷_ ∘ arg i <$> everywherePattern Γ p ⊛ everywherePats Γ ps

everywhere : (actions : Actions) → (everyAction : Action Term) → Term → F Term
everywhere actions every = Params.everywhereTerm actions every (0 ,, [])


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

module C where

open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Relation.Binary.PropositionalEquality

open import Singleton

infixr 70 _×ᵤ_
infixr 60 _+ᵤₗ_
infixr 60 _+ᵤᵣ_
infixr 50 _⊚_

------------------------------------------------------------------------------
-- Pi

data 𝕌 : Set
⟦_⟧ : 𝕌 → Σ[ A ∈ Set ] A
data _⟷_ : 𝕌 → 𝕌 → Set

data 𝕌 where
  𝟙       : 𝕌
  _+ᵤₗ_    : 𝕌 → 𝕌 → 𝕌
  _+ᵤᵣ_    : 𝕌 → 𝕌 → 𝕌
  _×ᵤ_    : 𝕌 → 𝕌 → 𝕌
  Singᵤ : 𝕌 → 𝕌
  Recipᵤ : 𝕌 → 𝕌

⟦ 𝟙 ⟧ = ⊤ , tt
⟦ T₁ ×ᵤ T₂ ⟧ = zip _×_ _,_ ⟦ T₁ ⟧ ⟦ T₂ ⟧
⟦ T₁ +ᵤₗ T₂ ⟧ = zip _⊎_ (λ x _ → inj₁ x) ⟦ T₁ ⟧ ⟦ T₂ ⟧
⟦ T₁ +ᵤᵣ T₂ ⟧ = zip _⊎_ (λ _ y → inj₂ y) ⟦ T₁ ⟧ ⟦ T₂ ⟧
⟦ Singᵤ T ⟧ = < uncurry Singleton , (λ y → proj₂ y , refl) > ⟦ T ⟧
⟦ Recipᵤ T ⟧ = < uncurry Recip , (λ _ _ → tt) > ⟦ T ⟧

data _⟷_ where
  swap₊₁   : {t₁ t₂ : 𝕌} → t₁ +ᵤₗ t₂ ⟷ t₂ +ᵤᵣ t₁
  swap₊₂   : {t₁ t₂ : 𝕌} → t₁ +ᵤᵣ t₂ ⟷ t₂ +ᵤₗ t₁
  assocl₊₁ : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤₗ (t₂ +ᵤₗ t₃) ⟷ (t₁ +ᵤₗ t₂) +ᵤₗ t₃
  assocl₊₂ : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤₗ (t₂ +ᵤᵣ t₃) ⟷ (t₁ +ᵤₗ t₂) +ᵤₗ t₃
  assocl₊₃ : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤᵣ (t₂ +ᵤₗ t₃) ⟷ (t₁ +ᵤᵣ t₂) +ᵤₗ t₃
  assocl₊₄ : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤᵣ (t₂ +ᵤᵣ t₃) ⟷ (t₁ +ᵤₗ t₂) +ᵤᵣ t₃
  assocl₊₅ : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤᵣ (t₂ +ᵤᵣ t₃) ⟷ (t₁ +ᵤᵣ t₂) +ᵤᵣ t₃
  assocr₊₁ : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤₗ t₂) +ᵤₗ t₃ ⟷ t₁ +ᵤₗ (t₂ +ᵤᵣ t₃)
  assocr₊₂ : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤₗ t₂) +ᵤₗ t₃ ⟷ t₁ +ᵤₗ (t₂ +ᵤₗ t₃)
  assocr₊₃ : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤᵣ t₂) +ᵤₗ t₃ ⟷ t₁ +ᵤᵣ (t₂ +ᵤₗ t₃)
  assocr₊₄ : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤₗ t₂) +ᵤᵣ t₃ ⟷ t₁ +ᵤᵣ (t₂ +ᵤᵣ t₃)
  assocr₊₅ : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤᵣ t₂) +ᵤᵣ t₃ ⟷ t₁ +ᵤᵣ (t₂ +ᵤᵣ t₃)
  unite⋆l : {t : 𝕌} → 𝟙 ×ᵤ t ⟷ t
  uniti⋆l : {t : 𝕌} → t ⟷ 𝟙 ×ᵤ t
  unite⋆r : {t : 𝕌} → t ×ᵤ 𝟙 ⟷ t
  uniti⋆r : {t : 𝕌} → t ⟷ t ×ᵤ 𝟙
  swap⋆   : {t₁ t₂ : 𝕌} → t₁ ×ᵤ t₂ ⟷ t₂ ×ᵤ t₁
  assocl⋆ : {t₁ t₂ t₃ : 𝕌} → t₁ ×ᵤ (t₂ ×ᵤ t₃) ⟷ (t₁ ×ᵤ t₂) ×ᵤ t₃
  assocr⋆ : {t₁ t₂ t₃ : 𝕌} → (t₁ ×ᵤ t₂) ×ᵤ t₃ ⟷ t₁ ×ᵤ (t₂ ×ᵤ t₃)
  dist₁    : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤₗ t₂) ×ᵤ t₃ ⟷ (t₁ ×ᵤ t₃) +ᵤₗ (t₂ ×ᵤ t₃)
  dist₂    : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤᵣ t₂) ×ᵤ t₃ ⟷ (t₁ ×ᵤ t₃) +ᵤᵣ (t₂ ×ᵤ t₃)
  factor₁  : {t₁ t₂ t₃ : 𝕌} → (t₁ ×ᵤ t₃) +ᵤₗ (t₂ ×ᵤ t₃) ⟷ (t₁ +ᵤₗ t₂) ×ᵤ t₃
  factor₂  : {t₁ t₂ t₃ : 𝕌} → (t₁ ×ᵤ t₃) +ᵤᵣ (t₂ ×ᵤ t₃) ⟷ (t₁ +ᵤᵣ t₂) ×ᵤ t₃
  distl₁   : {t₁ t₂ t₃ : 𝕌} → t₁ ×ᵤ (t₂ +ᵤₗ t₃) ⟷ (t₁ ×ᵤ t₂) +ᵤₗ (t₁ ×ᵤ t₃)
  distl₂   : {t₁ t₂ t₃ : 𝕌} → t₁ ×ᵤ (t₂ +ᵤᵣ t₃) ⟷ (t₁ ×ᵤ t₂) +ᵤᵣ (t₁ ×ᵤ t₃)
  factorl₁ : {t₁ t₂ t₃ : 𝕌 } → (t₁ ×ᵤ t₂) +ᵤₗ (t₁ ×ᵤ t₃) ⟷ t₁ ×ᵤ (t₂ +ᵤₗ t₃)
  factorl₂ : {t₁ t₂ t₃ : 𝕌 } → (t₁ ×ᵤ t₂) +ᵤᵣ (t₁ ×ᵤ t₃) ⟷ t₁ ×ᵤ (t₂ +ᵤᵣ t₃)
  id⟷     : {t : 𝕌} → t ⟷ t
  _⊚_     : {t₁ t₂ t₃ : 𝕌} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)
  _⊕₁_     : {t₁ t₂ t₃ t₄ : 𝕌} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (t₁ +ᵤₗ t₂ ⟷ t₃ +ᵤₗ t₄)
  _⊕₂_     : {t₁ t₂ t₃ t₄ : 𝕌} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (t₁ +ᵤᵣ t₂ ⟷ t₃ +ᵤᵣ t₄)
  _⊗_     : {t₁ t₂ t₃ t₄ : 𝕌} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (t₁ ×ᵤ t₂ ⟷ t₃ ×ᵤ t₄)
  -- monad
  return : (T : 𝕌) → T ⟷ Singᵤ T
  join : (T : 𝕌) → Singᵤ (Singᵤ T) ⟷ Singᵤ T
  unjoin : (T : 𝕌) → Singᵤ T ⟷ Singᵤ (Singᵤ T)
  tensorl : (T₁ T₂ : 𝕌) → (Singᵤ T₁ ×ᵤ T₂) ⟷ Singᵤ (T₁ ×ᵤ T₂)
  tensorr : (T₁ T₂ : 𝕌) → (T₁ ×ᵤ Singᵤ T₂) ⟷ Singᵤ (T₁ ×ᵤ T₂)
  tensor : (T₁ T₂ : 𝕌) → (Singᵤ T₁ ×ᵤ Singᵤ T₂) ⟷ Singᵤ (T₁ ×ᵤ T₂)
  untensor : (T₁ T₂ : 𝕌) → Singᵤ (T₁ ×ᵤ T₂) ⟷ (Singᵤ T₁ ×ᵤ Singᵤ T₂)
  plusl : (T₁ T₂ : 𝕌) → (Singᵤ T₁ +ᵤₗ T₂) ⟷ Singᵤ (T₁ +ᵤₗ T₂)
  plusr : (T₁ T₂ : 𝕌) → (T₁ +ᵤᵣ Singᵤ T₂) ⟷ Singᵤ (T₁ +ᵤᵣ T₂)
  -- comonad
  extract : (T : 𝕌) → Singᵤ T ⟷ T
  cojoin : (T : 𝕌) → Singᵤ T ⟷ Singᵤ (Singᵤ T)
  counjoin : (T : 𝕌) → Singᵤ (Singᵤ T) ⟷ Singᵤ T
  cotensorl : (T₁ T₂ : 𝕌) → Singᵤ (T₁ ×ᵤ T₂) ⟷ (Singᵤ T₁ ×ᵤ T₂)
  cotensorr : (T₁ T₂ : 𝕌) → Singᵤ (T₁ ×ᵤ T₂) ⟷ (T₁ ×ᵤ Singᵤ T₂)
  coplusl : (T₁ T₂ : 𝕌) → Singᵤ (T₁ +ᵤₗ T₂) ⟷ (Singᵤ T₁ +ᵤₗ T₂)
  coplusr : (T₁ T₂ : 𝕌) → Singᵤ (T₁ +ᵤᵣ T₂) ⟷ (T₁ +ᵤᵣ Singᵤ T₂)
  -- both?
  Singᵤ : (T₁ T₂ : 𝕌) → (T₁ ⟷ T₂) → (Singᵤ T₁ ⟷ Singᵤ T₂)
  -- eta/epsilon
  η : (T : 𝕌) → 𝟙 ⟷ (Singᵤ T ×ᵤ Recipᵤ T)
  ε : (T : 𝕌) → (Singᵤ T ×ᵤ Recipᵤ T) ⟷ 𝟙

!_ : {t₁ t₂ : 𝕌} → t₁ ⟷ t₂ → t₂ ⟷ t₁
! swap₊₁ = swap₊₂
! swap₊₂ = swap₊₁
! assocl₊₁ = assocr₊₂
! assocl₊₂ = assocr₊₁
! assocl₊₃ = assocr₊₃
! assocl₊₄ = assocr₊₄
! assocl₊₅ = assocr₊₅
! assocr₊₁ = assocl₊₂
! assocr₊₂ = assocl₊₁
! assocr₊₃ = assocl₊₃
! assocr₊₄ = assocl₊₄
! assocr₊₅ = assocl₊₅
! unite⋆l = uniti⋆l
! uniti⋆l = unite⋆l
! unite⋆r = uniti⋆r
! uniti⋆r = unite⋆r
! swap⋆ = swap⋆
! assocl⋆ = assocr⋆
! assocr⋆ = assocl⋆
! dist₁ = factor₁
! dist₂ = factor₂
! factor₁ = dist₁
! factor₂ = dist₂
! distl₁ = factorl₁
! distl₂ = factorl₂
! factorl₁ = distl₁
! factorl₂ = distl₂
! id⟷ = id⟷
! (c ⊚ c₁) = (! c₁) ⊚ (! c)
! (c ⊕₁ c₁) = (! c) ⊕₁ (! c₁)
! (c ⊕₂ c₁) = (! c) ⊕₂ (! c₁)
! (c ⊗ c₁) = (! c) ⊗ (! c₁)
! return T = extract T
! join T = return (Singᵤ T)
! unjoin T = join T
! tensorl T₁ T₂ = cotensorl T₁ T₂
! tensorr T₁ T₂ = cotensorr T₁ T₂
! tensor T₁ T₂ = untensor T₁ T₂
! untensor T₁ T₂ = tensor T₁ T₂
! plusl T₁ T₂ = coplusl T₁ T₂
! plusr T₁ T₂ = coplusr T₁ T₂
! extract T = return T
! cojoin T = join T
! counjoin T = return (Singᵤ T)
! cotensorl T₁ T₂ = tensorl T₁ T₂
! cotensorr T₁ T₂ = tensorr T₁ T₂
! coplusl T₁ T₂ = plusl T₁ T₂
! coplusr T₁ T₂ = plusr T₁ T₂
! Singᵤ T₁ T₂ c = Singᵤ T₂ T₁ (! c)
! η T = ε T
! ε T = η T


module ExplicitImplicitConversion where
open import ExpressionChanging
open import Data.Nat
open import Data.List
open import ParseTree
open import ScopeState
open import Data.Vec using (Vec ; _∷_)
open import Data.Fin
open import Data.List.NonEmpty
open import AgdaHelperFunctions
open import Data.Bool
open import ParseTreeOperations

-- is given types before conversion
-- namedArgument for type, never just implicit!!!!!!
conversionIns : {n : ℕ} -> Vec Expr n -> Fin n -> (List Expr -> List Expr)
conversionIns Vec.[] () l
conversionIns _ _ [] = []
conversionIns (namedArgument _ {false} ∷ x₁) zero (implicit y ∷ list) = y ∷ list
-- expecting implicit and getting explicit, i.e. argument to be moved
-- isn't specified.
conversionIns (namedArgument _ {false} ∷ x₁) zero (y ∷ list) =  newUnderscore ∷ y ∷ list
conversionIns (x ∷ x₁) zero (x₂ ∷ list) = implicit x₂ ∷ list
conversionIns (namedArgument _ {false} ∷ x₁) (suc n) (implicit x₂ ∷ list) =
  implicit x₂ ∷ conversionIns x₁ n list
conversionIns (namedArgument _ {false} ∷ x₁) (suc n) (x₂ ∷ list) =
  let result = conversionIns x₁ n (x₂ ∷ list)
  in if headImplicit? result then (implicit newUnderscore) ∷ result
        else result
    where headImplicit? : List Expr -> Bool
          headImplicit? (implicit x ∷ l) = true
          headImplicit? _ = false
conversionIns (x ∷ x₁) (suc n) (x₂ ∷ list) = x₂ ∷ conversionIns x₁ n list

convertInExpr : TypeSignature -> ℕ -> ScopeState (List⁺ Expr -> List⁺ Expr)
convertInExpr t n = makeInstruction t n conversionIns

convertInSignature : {n : ℕ} -> Vec Expr (suc n)  -> Fin n -> ScopeState (Vec Expr (suc n))
convertInSignature (namedArgument arg {isExplicit} {c}{c2} ∷ l) zero =
  return $ namedArgument arg {not isExplicit}{c}{c2} ∷ l
convertInSignature (implicit x ∷ l) zero = return $ x ∷ l
convertInSignature (x ∷ l) zero = do
    newName <- getUniqueIdentifier
    return $ namedArgument (typeSignature newName x) {false} {[]} {[]} ∷ l
convertInSignature (x ∷ v) (suc f) = do
    rest <- convertInSignature v f
    return $ x ∷ rest

convert : List ParseTree -> (funcID : ℕ) -> (whichArgument : ℕ) -> ScopeState (List ParseTree)
convert code funcId whichArgument = doForArgument code funcId whichArgument convertInSignature convertInExpr


{-# OPTIONS --without-K #-}
module well-typed-syntax-helpers where
open import common
open import well-typed-syntax

infixl 3 _‘'’ₐ_
infixr 1 _‘→'’_
infixl 3 _‘t’_
infixl 3 _‘t’₁_
infixl 3 _‘t’₂_
infixr 2 _‘∘’_

WS∀ : ∀ {Γ T T' A B} {a : Term {Γ = Γ} T} → Term {Γ = Γ ▻ T'} (W ((A ‘→’ B) ‘’ a)) → Term {Γ ▻ T'} (W ((A ‘’ a) ‘→’ (B ‘’₁ a)))
WS∀ = weakenTyp-substTyp-tProd

_‘→'’_ : ∀ {Γ} → Typ Γ → Typ Γ → Typ Γ
_‘→'’_ {Γ = Γ} A B = _‘→’_ {Γ = Γ} A (W {Γ = Γ} {A = A} B)

_‘'’ₐ_ : ∀ {Γ A B} → Term {Γ} (A ‘→'’ B) → Term A → Term B
_‘'’ₐ_ {Γ} {A} {B} f x = SW (_‘’ₐ_ {Γ} {A} {W B} f x)

_‘t’_ : ∀ {Γ A} {B : Typ (Γ ▻ A)} → (b : Term {Γ = Γ ▻ A} B) → (a : Term {Γ} A) → Term {Γ} (B ‘’ a)
b ‘t’ a = ‘λ∙’ b ‘’ₐ a

substTyp-tProd : ∀ {Γ T A B} {a : Term {Γ} T} →
                         Term {Γ} ((A ‘→’ B) ‘’ a)
                         → Term {Γ} (_‘→’_ {Γ = Γ} (A ‘’ a) (B ‘’₁ a))
substTyp-tProd {Γ} {T} {A} {B} {a} x = SW ((WS∀ (w x)) ‘t’ a)

S∀ = substTyp-tProd

‘λ'∙’ : ∀ {Γ A B} → Term {Γ ▻ A} (W B) -> Term (A ‘→'’ B)
‘λ'∙’ f = ‘λ∙’ f

SW1V : ∀ {Γ A T} → Term {Γ ▻ A} (W1 T ‘’ ‘VAR₀’) → Term {Γ ▻ A} T
SW1V = substTyp-weakenTyp1-VAR₀

S₁∀ : ∀ {Γ T T' A B} {a : Term {Γ} T} → Term {Γ ▻ T' ‘’ a} ((A ‘→’ B) ‘’₁ a) → Term {Γ ▻ T' ‘’ a} ((A ‘’₁ a) ‘→’ (B ‘’₂ a))
S₁∀ = substTyp1-tProd

un‘λ∙’ : ∀ {Γ A B} → Term (A ‘→’ B) → Term {Γ ▻ A} B
un‘λ∙’ f = SW1V (weakenTyp-tProd (w f) ‘’ₐ ‘VAR₀’)

weakenProd : ∀ {Γ A B C} →
                          Term {Γ} (A ‘→’ B)
                          → Term {Γ = Γ ▻ C} (W A ‘→’ W1 B)
weakenProd {Γ} {A} {B} {C} x = weakenTyp-tProd (w x)
w∀ = weakenProd

w1 : ∀ {Γ A B C} → Term {Γ = Γ ▻ B} C → Term {Γ = Γ ▻ A ▻ W {Γ = Γ} {A = A} B} (W1 {Γ = Γ} {A = A} {B = B} C)
w1 x = un‘λ∙’ (weakenTyp-tProd (w (‘λ∙’ x)))

_‘t’₁_ : ∀ {Γ A B C} → (c : Term {Γ = Γ ▻ A ▻ B} C) → (a : Term {Γ} A) → Term {Γ = Γ ▻ B ‘’ a} (C ‘’₁ a)
f ‘t’₁ x = un‘λ∙’ (S∀ (‘λ∙’ (‘λ∙’ f) ‘’ₐ x))
_‘t’₂_ : ∀ {Γ A B C D} → (c : Term {Γ = Γ ▻ A ▻ B ▻ C} D) → (a : Term {Γ} A) → Term {Γ = Γ ▻ B ‘’ a ▻ C ‘’₁ a} (D ‘’₂ a)
f ‘t’₂ x = un‘λ∙’ (S₁∀ (un‘λ∙’ (S∀ (‘λ∙’ (‘λ∙’ (‘λ∙’ f)) ‘’ₐ x))))

S₁₀W' : ∀ {Γ C T A} {a : Term {Γ} C} {b : Term {Γ} (T ‘’ a)} → Term {Γ} (A ‘’ a) → Term {Γ} (W A ‘’₁ a ‘’ b)
S₁₀W' = substTyp1-substTyp-weakenTyp-inv

S₁₀W : ∀ {Γ C T A} {a : Term {Γ} C} {b : Term {Γ} (T ‘’ a)} → Term {Γ} (W A ‘’₁ a ‘’ b) → Term {Γ} (A ‘’ a)
S₁₀W = substTyp1-substTyp-weakenTyp

substTyp1-substTyp-weakenTyp-weakenTyp : ∀ {Γ T A} {B : Typ (Γ ▻ A)}
    → {a : Term {Γ} A}
    → {b : Term {Γ} (B ‘’ a)}
    → Term {Γ} (W (W T) ‘’₁ a ‘’ b)
    → Term {Γ} T
substTyp1-substTyp-weakenTyp-weakenTyp x = SW (S₁₀W x)

S₁₀WW = substTyp1-substTyp-weakenTyp-weakenTyp

S₂₁₀W : ∀ {Γ A B C T} {a : Term {Γ} A} {b : Term {Γ} (B ‘’ a)} {c : Term {Γ} (C ‘’₁ a ‘’ b)}
      → Term {Γ} (W T ‘’₂ a ‘’₁ b ‘’ c)
      → Term {Γ} (T ‘’₁ a ‘’ b)
S₂₁₀W = substTyp2-substTyp1-substTyp-weakenTyp

substTyp2-substTyp1-substTyp-weakenTyp-weakenTyp : ∀ {Γ A B C T}
         {a : Term {Γ} A}
         {b : Term {Γ} (B ‘’ a)}
         {c : Term {Γ} (C ‘’₁ a ‘’ b)} →
    Term {Γ} (W (W T) ‘’₂ a ‘’₁ b ‘’ c)
    → Term {Γ} (T ‘’ a)
substTyp2-substTyp1-substTyp-weakenTyp-weakenTyp x = S₁₀W (S₂₁₀W x)

S₂₁₀WW = substTyp2-substTyp1-substTyp-weakenTyp-weakenTyp

W1W : ∀ {Γ A B C} → Term {Γ ▻ A ▻ W B} (W1 (W C)) → Term {Γ ▻ A ▻ W B} (W (W C))
W1W = weakenTyp1-weakenTyp

W1W1W : ∀ {Γ A B C T} → Term {Γ ▻ A ▻ B ▻ W (W C)} (W1 (W1 (W T))) → Term {Γ ▻ A ▻ B ▻ W (W C)} (W1 (W (W T)))
W1W1W = weakenTyp1-weakenTyp1-weakenTyp

weakenTyp-tProd-nd : ∀ {Γ A B C} →
                          Term {Γ = Γ ▻ C} (W (A ‘→'’ B))
                          → Term {Γ = Γ ▻ C} (W A ‘→'’ W B)
weakenTyp-tProd-nd x = ‘λ'∙’ (W1W (SW1V (weakenTyp-tProd (w (weakenTyp-tProd x)) ‘’ₐ ‘VAR₀’)))

weakenProd-nd : ∀ {Γ A B C} →
                             Term (A ‘→'’ B)
                             → Term {Γ = Γ ▻ C} (W A ‘→'’ W B)
weakenProd-nd {Γ} {A} {B} {C} x = weakenTyp-tProd-nd (w x)

weakenTyp-tProd-nd-tProd-nd : ∀ {Γ A B C D} →
    Term {Γ = Γ ▻ D} (W (A ‘→'’ B ‘→'’ C))
    → Term {Γ = Γ ▻ D} (W A ‘→'’ W B ‘→'’ W C)
weakenTyp-tProd-nd-tProd-nd x = ‘λ∙’ (weakenTyp-tProd-inv (‘λ∙’ (W1W1W (SW1V (w∀ (weakenTyp-tProd (weakenTyp-weakenTyp-tProd (w→ (weakenTyp-tProd-nd x) ‘'’ₐ ‘VAR₀’))) ‘’ₐ ‘VAR₀’)))))

weakenProd-nd-Prod-nd : ∀ {Γ A B C D} →
    Term (A ‘→'’ B ‘→'’ C)
    → Term {Γ = Γ ▻ D} (W A ‘→'’ W B ‘→'’ W C)
weakenProd-nd-Prod-nd {Γ} {A} {B} {C} {D} x = weakenTyp-tProd-nd-tProd-nd (w x)
w→→ = weakenProd-nd-Prod-nd

S₁W1 : ∀ {Γ A B C} {a : Term {Γ} A} → Term {Γ ▻ W B ‘’ a} (W1 C ‘’₁ a) → Term {Γ ▻ B} C
S₁W1 = substTyp1-weakenTyp1

W1S₁W' : ∀ {Γ A T'' T' T} {a : Term {Γ} A}
      → Term {Γ ▻ T'' ▻ W (T' ‘’ a)} (W1 (W (T ‘’ a)))
      → Term {Γ ▻ T'' ▻ W (T' ‘’ a)} (W1 (W T ‘’₁ a))
W1S₁W' = weakenTyp1-substTyp-weakenTyp1-inv

substTyp-weakenTyp1-inv : ∀ {Γ A T' T}
         {a : Term {Γ} A} →
    Term {Γ = (Γ ▻ T' ‘’ a)} (W (T ‘’ a))
    → Term {Γ = (Γ ▻ T' ‘’ a)} (W T ‘’₁ a)
substTyp-weakenTyp1-inv {a = a} x = S₁W1 (W1S₁W' (w1 x) ‘t’₁ a)

S₁W' = substTyp-weakenTyp1-inv

_‘∘’_ : ∀ {Γ A B C}
    → Term {Γ} (A ‘→'’ B)
    → Term {Γ} (B ‘→'’ C)
    → Term {Γ} (A ‘→'’ C)
g ‘∘’ f = ‘λ∙’ (w→ f ‘'’ₐ (w→ g ‘'’ₐ ‘VAR₀’))

WS₀₀W1 : ∀ {Γ T' B A} {b : Term {Γ} B} {a : Term {Γ ▻ B} (W A)} {T : Typ (Γ ▻ A)}
      → Term {Γ ▻ T'} (W (W1 T ‘’ a ‘’ b))
      → Term {Γ ▻ T'} (W (T ‘’ (SW (a ‘t’ b))))
WS₀₀W1 = weakenTyp-substTyp-substTyp-weakenTyp1

WS₀₀W1' : ∀ {Γ T' B A} {b : Term {Γ} B} {a : Term {Γ ▻ B} (W A)} {T : Typ (Γ ▻ A)}
      → Term {Γ ▻ T'} (W (T ‘’ (SW (a ‘t’ b))))
      → Term {Γ ▻ T'} (W (W1 T ‘’ a ‘’ b))
WS₀₀W1' = weakenTyp-substTyp-substTyp-weakenTyp1-inv

substTyp-substTyp-weakenTyp1-inv-arr : ∀ {Γ B A}
         {b : Term {Γ} B}
         {a : Term {Γ ▻ B} (W A)}
         {T : Typ (Γ ▻ A)}
         {X} →
    Term {Γ} (T ‘’ (SW (a ‘t’ b)) ‘→'’ X)
    → Term {Γ} (W1 T ‘’ a ‘’ b ‘→'’ X)
substTyp-substTyp-weakenTyp1-inv-arr x = ‘λ∙’ (w→ x ‘'’ₐ WS₀₀W1 ‘VAR₀’)

S₀₀W1'→ = substTyp-substTyp-weakenTyp1-inv-arr

substTyp-substTyp-weakenTyp1-arr-inv : ∀ {Γ B A}
         {b : Term {Γ} B}
         {a : Term {Γ ▻ B} (W A)}
         {T : Typ (Γ ▻ A)}
         {X} →
    Term {Γ} (X ‘→'’ T ‘’ (SW (a ‘t’ b)))
    → Term {Γ} (X ‘→'’ W1 T ‘’ a ‘’ b)
substTyp-substTyp-weakenTyp1-arr-inv x = ‘λ∙’ (WS₀₀W1' (un‘λ∙’ x))

S₀₀W1'← = substTyp-substTyp-weakenTyp1-arr-inv

substTyp-substTyp-weakenTyp1 : ∀ {Γ B A}
         {b : Term {Γ} B}
         {a : Term {Γ ▻ B} (W A)}
         {T : Typ (Γ ▻ A)} →
    Term {Γ} (W1 T ‘’ a ‘’ b)
    → Term {Γ} (T ‘’ (SW (a ‘t’ b)))
substTyp-substTyp-weakenTyp1 x = (SW (WS₀₀W1 (w x) ‘t’ x))
S₀₀W1 = substTyp-substTyp-weakenTyp1

SW1W : ∀ {Γ T} {A : Typ Γ} {B : Typ Γ}
      → {a : Term {Γ = Γ ▻ T} (W {Γ = Γ} {A = T} B)}
      → Term {Γ = Γ ▻ T} (W1 (W A) ‘’ a)
      → Term {Γ = Γ ▻ T} (W A)
SW1W = substTyp-weakenTyp1-weakenTyp

S₂₀₀W1WW : ∀ {Γ A} {T : Typ (Γ ▻ A)} {T' C B} {a : Term {Γ} A} {b : Term {Γ = (Γ ▻ C ‘’ a)} (B ‘’₁ a)}
         {c : Term {Γ = (Γ ▻ T')} (W (C ‘’ a))}
         → Term {Γ = (Γ ▻ T')} (W1 (W (W T) ‘’₂ a ‘’ b) ‘’ c)
         → Term {Γ = (Γ ▻ T')} (W (T ‘’ a))
S₂₀₀W1WW = substTyp2-substTyp-substTyp-weakenTyp1-weakenTyp-weakenTyp

S₁₀W2W : ∀ {Γ T' A B T} {a : Term {Γ ▻ T'} (W A)} {b : Term {Γ ▻ T'} (W1 B ‘’ a)}
      → Term {Γ ▻ T'} (W2 (W T) ‘’₁ a ‘’ b)
      → Term {Γ ▻ T'} (W1 T ‘’ a)
S₁₀W2W = substTyp1-substTyp-weakenTyp2-weakenTyp


open import Relation.Binary using (IsDecEquivalence)
open import Agda.Builtin.Equality

-- module UnifyMguFTerminationHunt (FunctionName : Set) ⦃ isDecEquivalenceA : IsDecEquivalence (_≡_ {A = FunctionName}) ⦄ where

module UnifyMguFTerminationHunt where

postulate
  FunctionName : Set
  instance isDecEquivalenceA : IsDecEquivalence (_≡_ {A = FunctionName})

open IsDecEquivalence isDecEquivalenceA using () renaming (_≟_ to _≟F_)

open import UnifyTermF FunctionName

open import Data.Product using (∃; _,_)
open import Data.Maybe using (Maybe; just; nothing)
open import Category.Monad using (RawMonad)
import Level
open RawMonad (Data.Maybe.monad {Level.zero})
open import Relation.Nullary using (Dec; yes; no)

open import Data.Fin using (Fin)
open import Data.Nat using (suc)
postulate
  _◃′_ : ∀ {m n} -> (f : m ~> n) -> Term m -> Term n
  _for'_ : ∀ {n} (t' : Term n) (x : Fin (suc n)) -> Fin (suc n) -> Term n

amterm : ∀ {m} (s t : Term m) (acc : ∃ (AList m)) -> Maybe (∃ (AList m))
amterm leaf leaf acc = just acc
amterm leaf (function _ _) acc = nothing
amterm leaf (s' fork t') acc = nothing
amterm (s' fork t') leaf acc = nothing
amterm (s' fork t') (function _ _) acc = nothing
amterm (s1 fork s2) (t1 fork t2) acc = amterm s2 t2 =<< amterm s1 t1 acc {- Data.Maybe.maybe (⋆amgu.amgu ⋆amguTerm' s2 t2) nothing (⋆amgu.amgu ⋆amguTerm' s1 t1 acc) -}
amterm (function fn₁ ts₁) leaf acc = nothing
amterm (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc = nothing
amterm (function fn₁ ts₁) (_ fork _) acc = nothing
amterm (i x) (i y) (m , anil) = nothing
amterm (i x) t     (m , anil) = nothing
amterm t     (i x) (m , anil) = nothing
amterm s     t  (n , σ asnoc r / z) =
         (λ σ -> σ ∃asnoc r / z) <$>
           -- amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ)
           amterm ((r for' z) ◃′ s) ((r for' z) ◃′ t) (n , σ)

-- instance ⋆amguTerm' : ⋆amgu Term
-- ⋆amguTerm' .⋆amgu.amgu leaf leaf acc = just acc
-- ⋆amguTerm' .⋆amgu.amgu leaf (function _ _) acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu leaf (s' fork t') acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (s' fork t') leaf acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (s' fork t') (function _ _) acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (s1 fork s2) (t1 fork t2) acc = amgu s2 t2 =<< amgu s1 t1 acc {- Data.Maybe.maybe (⋆amgu.amgu ⋆amguTerm' s2 t2) nothing (⋆amgu.amgu ⋆amguTerm' s1 t1 acc) -}
-- ⋆amguTerm' .⋆amgu.amgu (function fn₁ ts₁) leaf acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (function fn₁ ts₁) (_ fork _) acc = nothing
-- ⋆amguTerm' .⋆amgu.amgu (i x) (i y) (m , anil) = nothing
-- ⋆amguTerm' .⋆amgu.amgu (i x) t     (m , anil) = nothing
-- ⋆amguTerm' .⋆amgu.amgu t     (i x) (m , anil) = nothing
-- ⋆amguTerm' .⋆amgu.amgu s     t  (n , σ asnoc r / z) =
--          (λ σ -> σ ∃asnoc r / z) <$>
--            -- amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ)
--            amgu ((r for z) ◃′ s) ((r for z) ◃′ t) (n , σ)

-- -- instance ⋆amguTerm' : ⋆amgu Term
-- -- ⋆amgu.amgu ⋆amguTerm' leaf leaf acc = just acc
-- -- ⋆amgu.amgu ⋆amguTerm' leaf (function _ _) acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' leaf (s' fork t') acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (s' fork t') leaf acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (s' fork t') (function _ _) acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (s1 fork s2) (t1 fork t2) acc = {!amgu s2 t2 =<< amgu s1 t1 acc!} {- Data.Maybe.maybe (⋆amgu.amgu ⋆amguTerm' s2 t2) nothing (⋆amgu.amgu ⋆amguTerm' s1 t1 acc) -}
-- -- ⋆amgu.amgu ⋆amguTerm' (function fn₁ ts₁) leaf acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (function fn₁ ts₁) (_ fork _) acc = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (i x) (i y) (m , anil) = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' (i x) t     (m , anil) = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' t     (i x) (m , anil) = nothing
-- -- ⋆amgu.amgu ⋆amguTerm' s     t  (n , σ asnoc r / z) =
-- --          (λ σ -> σ ∃asnoc r / z) <$>
-- --            -- amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ)
-- --            amgu ((r for z) ◃′ s) ((r for z) ◃′ t) (n , σ)

-- -- -- mutual

-- -- --   instance ⋆amguTerm : ⋆amgu Term
-- -- --   ⋆amgu.amgu ⋆amguTerm leaf leaf acc = just acc
-- -- --   ⋆amgu.amgu ⋆amguTerm leaf (function _ _) acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm leaf (s' fork t') acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm (s' fork t') leaf acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm (s' fork t') (function _ _) acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm (s1 fork s2) (t1 fork t2) acc =
-- -- --                     amgu s2 t2 =<< amgu s1 t1 acc
-- -- --   ⋆amgu.amgu ⋆amguTerm (function fn₁ ts₁) leaf acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc
-- -- --    with fn₁ ≟F fn₂
-- -- --   … | no _ = nothing
-- -- --   … | yes _ with n₁ ≟ n₂
-- -- --   … | no _ = nothing
-- -- --   … | yes refl = amgu ts₁ ts₂ acc
-- -- --   ⋆amgu.amgu ⋆amguTerm (function fn₁ ts₁) (_ fork _) acc = nothing
-- -- --   ⋆amgu.amgu ⋆amguTerm (i x) (i y) (m , anil) = just (flexFlex x y)
-- -- --   ⋆amgu.amgu ⋆amguTerm (i x) t     (m , anil) = flexRigid x t
-- -- --   ⋆amgu.amgu ⋆amguTerm t     (i x) (m , anil) = flexRigid x t
-- -- --   ⋆amgu.amgu ⋆amguTerm s     t  (n , σ asnoc r / z) =
-- -- --            (λ σ -> σ ∃asnoc r / z) <$>
-- -- --              amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ)

-- -- --   instance ⋆amguVecTerm : ∀ {N} → ⋆amgu (flip Vec N ∘ Term)
-- -- --   ⋆amgu.amgu ⋆amguVecTerm [] [] acc = just acc
-- -- --   ⋆amgu.amgu ⋆amguVecTerm (t₁ ∷ t₁s) (t₂ ∷ t₂s) acc = amgu t₁s t₂s =<< amgu t₁ t₂ acc

-- -- -- mgu : ∀ {m} -> (s t : Term m) -> Maybe (∃ (AList m))
-- -- -- mgu {m} s t = amgu s t (m , anil)


module cedille-syntax where
open import lib
open import ctxt
open import syntax-util
open import general-util
open import is-free
open import cedille-types

-- App, AppTp
infixl 10 _`_ _-_ _·_
-- Pattern and constructor
pattern _`_ t t' = App t NotErased t'
pattern _-_ t t' = App t Erased t'
pattern _·_ t T = AppTp t T

-- Beta
infix 9 β<_> β[_] β<_>[_] `β<_> `β[_] `β<_>[_]
pattern β = Beta _ NoTerm NoTerm
pattern β<_> t = Beta _ (SomeTerm t _) NoTerm
pattern β[_] t = Beta _ NoTerm (SomeTerm t _)
pattern β<_>[_] t t' = Beta _ (SomeTerm t _) (SomeTerm t' _)
`β = Beta pi-gen NoTerm NoTerm
`β<_> = λ t → Beta pi-gen (SomeTerm t pi-gen) NoTerm
`β[_] = λ t → Beta pi-gen NoTerm (SomeTerm t pi-gen)
`β<_>[_] = λ t t' → Beta pi-gen (SomeTerm t pi-gen) (SomeTerm t' pi-gen)

-- Chi
infixr 8 χ-_ χ_-_ `χ-_ `χ_-_
pattern χ-_ t = Chi _ NoType t
pattern χ_-_ T t = Chi _ (SomeType T) t
`χ-_ = Chi pi-gen NoType
`χ_-_ = λ T t → Chi pi-gen (SomeType T) t

-- Delta
infixr 8 δ-_ δ_-_ `δ-_ `δ_-_
pattern δ-_ t = Delta _ NoType t
pattern δ_-_ T t = Delta _ (SomeType T) t
`δ-_ = Delta pi-gen NoType
`δ_-_ = λ T t → Delta pi-gen (SomeType T) t

-- Epsilon
infixr 8 ε_ εl_ εr_ ε-_ εl-_ εr-_ `ε_ `εl_ `εr_ `ε-_ `εl-_ `εr-_
pattern ε_ t = Epsilon _ Both ff t
pattern εl_ t = Epsilon _ Left ff t
pattern εr_ t = Epsilon _ Right ff t
pattern ε-_ t = Epsilon _ Both tt t
pattern εl-_ t = Epsilon _ Left tt t
pattern εr-_ t = Epsilon _ Right tt t
`ε_ = Epsilon pi-gen Both ff
`εl_ = Epsilon pi-gen Left ff
`εr_ = Epsilon pi-gen Right ff
`ε-_ = Epsilon pi-gen Both tt
`εl-_ = Epsilon pi-gen Left tt
`εr-_ = Epsilon pi-gen Right tt

-- Hole
pattern ● = Hole _
`● = Hole pi-gen

-- IotaPair
pattern [_`,_] t₁ t₂ = IotaPair _ t₁ t₂ NoGuide _
pattern [_`,_*_₊_] t₁ t₂ x T = IotaPair _ t₁ t₂ (Guide _ x T) _ -- "₊" = "\_+"
`[_`,_] = λ t₁ t₂ → IotaPair pi-gen t₁ t₂ NoGuide pi-gen
`[_`,_*_₊_] = λ t₁ t₂ x T → IotaPair pi-gen t₁ t₂ (Guide pi-gen x T) pi-gen

-- IotaProj
infixl 5 _₊1 _₊2 _₊#_ _`₊1 _`₊2 _`₊#_
pattern _₊1 t = IotaProj t "1" _
pattern _₊2 t = IotaProj t "2" _
pattern _₊#_ t n = IotaProj t n _
_`₊1 = λ t → IotaProj t "1" pi-gen
_`₊2 = λ t → IotaProj t "2" pi-gen
_`₊#_ = λ t n → IotaProj t n pi-gen

-- Lam
infixr 4 λ`_₊_ λ`_:ₜ_₊_ λ`_:ₖ_₊_ Λ_₊_ Λ_:ₜ_₊_ Λ_:ₖ_₊_ `λ_₊_ `λ_:ₜ_₊_ `λ_:ₖ_₊_ `Λ_₊_ `Λ_:ₜ_₊_ `Λ_:ₖ_₊_
pattern λ`_₊_ x t = Lam _ NotErased _ x NoClass t
pattern λ`_:ₜ_₊_ x T t = Lam _ NotErased _ x (SomeClass (Tkt T)) t
pattern λ`_:ₖ_₊_ x k t = Lam _ NotErased _ x (SomeClass (Tkk k)) t
pattern Λ_₊_ x t = Lam _ Erased _ x NoClass t
pattern Λ_:ₜ_₊_ x T t = Lam _ Erased _ x (SomeClass (Tkt T)) t
pattern Λ_:ₖ_₊_ x k t = Lam _ Erased _ x (SomeClass (Tkk k)) t
`λ_₊_ = λ x t → Lam pi-gen NotErased pi-gen x NoClass t
`λ_:ₜ_₊_ = λ x T t → Lam pi-gen NotErased pi-gen x (SomeClass (Tkt T)) t
`λ_:ₖ_₊_ = λ x k t → Lam pi-gen NotErased pi-gen x (SomeClass (Tkk k)) t
`Λ_₊_ = λ x t → Lam pi-gen Erased pi-gen x NoClass t
`Λ_:ₜ_₊_ = λ x T t → Lam pi-gen Erased pi-gen x (SomeClass (Tkt T)) t
`Λ_:ₖ_₊_ = λ x k t → Lam pi-gen Erased pi-gen x (SomeClass (Tkk k)) t

-- Let
infixr 8 [_`=_]-_ [_:ₜ_`=_]-_ [_:ₖ_`=_]-_ -[_`=_]-_ -[_:ₜ_`=_]-_ -[_:ₖ_`=_]-_ `[_`=_]-_ `[_:ₜ_`=_]-_ `[_:ₖ_`=_]-_ `-[_`=_]-_ `-[_:ₜ_`=_]-_ `-[_:ₖ_`=_]-_
pattern [_`=_]-_ x t t' = Let _ NotErased (DefTerm _ x NoType t _) t'
pattern [_:ₜ_`=_]-_ x T t t' = Let _ NotErased (DefTerm _ x (SomeType T) t _) t'
pattern [_:ₖ_`=_]-_ x k T t' = Let _ NotErased (DefType _ x k T _) t'
pattern -[_`=_]-_ x t t' = Let _ Erased (DefTerm _ x NoType t _) t'
pattern -[_:ₜ_`=_]-_ x T t t' = Let _ Erased (DefTerm _ x (SomeType T) t _) t'
pattern -[_:ₖ_`=_]-_ x k T t' = Let _ Erased (DefType _ x k T _) t'
`[_`=_]-_ = λ x t t' → Let pi-gen NotErased (DefTerm pi-gen x NoType t) t'
`[_:ₜ_`=_]-_ = λ x T t t' → Let pi-gen NotErased (DefTerm pi-gen x (SomeType T) t) t'
`[_:ₖ_`=_]-_ = λ x k T t' → Let pi-gen NotErased (DefType pi-gen x k T) t'
`-[_`=_]-_ = λ x t t' → Let pi-gen Erased (DefTerm pi-gen x NoType t) t'
`-[_:ₜ_`=_]-_ = λ x T t t' → Let pi-gen Erased (DefTerm pi-gen x (SomeType T) t) t'
`-[_:ₖ_`=_]-_ = λ x k T t' → Let pi-gen Erased (DefType pi-gen x k T) t'

-- Open
infixr 8 open`_-_ `open_-_ close`_-_ `close_-_
pattern open`_-_ x t = Open _ OpacTrans _ x t
pattern close`_-_ x t = Open _ OpacOpaque _ x t
`open_-_ = Open pi-gen OpacTrans pi-gen
`close_-_ = Open pi-gen OpacOpaque pi-gen

--Parens
infix 4 ⦅_⦆ `⦅_⦆
pattern ⦅_⦆ t = Parens _ t _
`⦅_⦆ = λ t → Parens pi-gen t pi-gen

-- Phi
infix 8 φ_-_[_] `φ_-_[_]
pattern φ_-_[_] eq t₁ t₂ = Phi _ eq t₁ t₂ _
`φ_-_[_] = λ eq t₁ t₂ → Phi pi-gen eq t₁ t₂ pi-gen

-- Rho
infixr 8 ρ_-_ ρ<_>_-_ ρ_*_₊_-_ ρ<_>_*_₊_-_ ρ+_-_ ρ+<_>_-_ ρ+_*_₊_-_ ρ+<_>_*_₊_-_ `ρ_-_ `ρ<_>_-_ `ρ_*_₊_-_ `ρ<_>_*_₊_-_ `ρ+_-_ `ρ+<_>_-_ `ρ+_*_₊_-_ `ρ+<_>_*_₊_-_
pattern ρ_-_ t t' = Rho _ ff NoNums t NoGuide t'
pattern ρ<_>_-_ ns t t' = Rho _ ff (SomeNums ns) t NoGuide t'
pattern ρ_*_₊_-_ t x T t' = Rho _ ff NoNums t (Guide _ x T) t'
pattern ρ<_>_*_₊_-_ ns t x T t' = Rho _ ff (SomeNums ns) t (Guide _ x T) t'
pattern ρ+_-_ t t' = Rho _ tt NoNums t NoGuide t'
pattern ρ+<_>_-_ ns t t' = Rho _ tt (SomeNums ns) t NoGuide t'
pattern ρ+_*_₊_-_ t x T t' = Rho _ tt NoNums t (Guide _ x T) t'
pattern ρ+<_>_*_₊_-_ ns t x T t' = Rho _ tt (SomeNums ns) t (Guide _ x T) t'
`ρ_-_ = λ t t' → Rho pi-gen ff NoNums t NoGuide t'
`ρ<_>_-_ = λ ns t t' → Rho pi-gen ff (SomeNums ns) t NoGuide t'
`ρ_*_₊_-_ = λ t x T t' → Rho pi-gen ff NoNums t (Guide pi-gen x T) t'
`ρ<_>_*_₊_-_ = λ ns t x T t' → Rho pi-gen ff (SomeNums ns) t (Guide pi-gen x T) t'
`ρ+_-_ = λ t t' → Rho pi-gen tt NoNums t NoGuide t'
`ρ+<_>_-_ = λ ns t t' → Rho pi-gen tt (SomeNums ns) t NoGuide t'
`ρ+_*_₊_-_ = λ t x T t' → Rho pi-gen tt NoNums t (Guide pi-gen x T) t'
`ρ+<_>_*_₊_-_ = λ ns t x T t' → Rho pi-gen tt (SomeNums ns) t (Guide pi-gen x T) t'

-- Sigma
infixr 9 ς_ `ς_
pattern ς_ t = Sigma _ t
`ς_ = Sigma pi-gen

-- Theta
infix 9 θ_`_ θ+_`_ θ<_>_`_ `θ_`_ `θ+_`_ `θ<_>_`_
pattern θ_`_ t ts = Theta _ Abstract t ts
pattern θ+_`_ t ts = Theta _ AbstractEq t ts
pattern θ<_>_`_ vs t ts = Theta _ (AbstractVars vs) t ts
`θ_`_ = λ t ts → Theta pi-gen Abstract t ts
`θ+_`_ = λ t ts → Theta pi-gen AbstractEq t ts
`θ<_>_`_ = λ vs t ts → Theta pi-gen (AbstractVars vs) t ts

-- Mu
infix 9 μ_₊_[_] μ_₊_*_[_] `μ_₊_[_] `μ_₊_*_[_]
pattern μ_₊_[_] x t ms = Mu _ _ x t NoType _ ms _
pattern μ_₊_*_[_] x t T ms = Mu _ _ x t (SomeType T) _ ms _
`μ_₊_[_] = λ x t ms → Mu pi-gen pi-gen x t NoType pi-gen ms pi-gen
`μ_₊_*_[_] = λ x t T ms → Mu pi-gen pi-gen x t (SomeType T) pi-gen ms pi-gen

-- Mu'
infix 9 μ'_[_] μ'_*_[_] μ'<_>_[_] μ'<_>_*_[_] `μ'_[_] `μ'_*_[_] `μ'_[_] `μ'_*_[_]
pattern μ'_[_] t ms = Mu' _ NoTerm t NoType _ ms _
pattern μ'_*_[_] t T ms = Mu' _ NoTerm t (SomeType T) _ ms _
pattern μ'<_>_[_] t t' ms = Mu' _ (SomeTerm t) t' NoType _ ms _
pattern μ'<_>_*_[_] t t' T ms = Mu' _ (SomeTerm t) t' (SomeType T) _ ms _
`μ'_[_] = λ t ms → Mu' pi-gen NoTerm t NoType pi-gen ms pi-gen
`μ'_*_[_] = λ t T ms → Mu' pi-gen NoTerm t (SomeType T) pi-gen ms pi-gen
`μ'<_>_[_] = λ t t' ms → Mu' pi-gen (SomeTerm t pi-gen) t' NoType pi-gen ms pi-gen
`μ'<_>_*_[_] = λ t t' T ms → Mu' pi-gen (SomeTerm t pi-gen) t' (SomeType T) pi-gen ms pi-gen

-- Var
infixr 11 vₓ_ `vₓ_
pattern vₓ_ x = Var _ x
`vₓ_ = Var pi-gen

-- Abs
infixr 5 ∀`_:ₜ_₊_ ∀`_:ₖ_₊_ Π_:ₜ_₊_ Π_:ₖ_₊_ `∀_:ₜ_₊_ `∀_:ₖ_₊_ `Π_:ₜ_₊_ `Π_:ₖ_₊_
pattern ∀`_:ₜ_₊_ x T T' = Abs _ All _ x (Tkt T) T'
pattern ∀`_:ₖ_₊_ x k T = Abs _ All _ x (Tkk k) T
pattern Π_:ₜ_₊_ x T T' = Abs _ Pi _ x (Tkt T) T'
pattern Π_:ₖ_₊_ x k T = Abs _ Pi _ x (Tkk k) T
`∀_:ₜ_₊_ = λ x T T' → Abs pi-gen All pi-gen x (Tkt T) T'
`∀_:ₖ_₊_ = λ x k T → Abs pi-gen All pi-gen x (Tkk k) T
`Π_:ₜ_₊_ = λ x T T' → Abs pi-gen Pi pi-gen x (Tkt T) T'
`Π_:ₖ_₊_ = λ x k T → Abs pi-gen Pi pi-gen x (Tkk k) T

-- Iota
infix 4 ι_:ₜ_₊_ `ι_:ₜ_₊_
pattern ι_:ₜ_₊_ x T T' = Iota _ _ x T T'
`ι_:ₜ_₊_ = Iota pi-gen pi-gen

-- Lft
infix 4 ↑_₊_:ₗ_ `↑_₊_:ₗ_
pattern ↑_₊_:ₗ_ X t lT = Lft _ _ X t lT
`↑_₊_:ₗ_ = Lft pi-gen pi-gen

-- NoSpans
infix 4 [^_^] `[^_^]
pattern [^_^] T = NoSpans T _
`[^_^] = λ T → NoSpans T pi-gen

-- TpLet
infixr 5 [_`=_]ₜ-_ [_:ₜ_`=_]ₜ-_ [_:ₖ_`=_]ₜ-_ `[_`=_]ₜ-_ `[_:ₜ_`=_]ₜ-_ `[_:ₖ_`=_]ₜ-_
pattern [_`=_]ₜ-_ x t t' = TpLet _ (DefTerm _ x NoType t _) t'
pattern [_:ₜ_`=_]ₜ-_ x T t t' = TpLet _ (DefTerm _ x (SomeType T) t _) t'
pattern [_:ₖ_`=_]ₜ-_ x k T t' = TpLet _ (DefType _ x k T _) t'
`[_`=_]ₜ-_ = λ x t t' → TpLet pi-gen (DefTerm pi-gen x NoType t) t'
`[_:ₜ_`=_]ₜ-_ = λ x T t t' → TpLet pi-gen (DefTerm pi-gen x (SomeType T) t) t'
`[_:ₖ_`=_]ₜ-_ = λ x k T t' → TpLet pi-gen (DefType pi-gen x k T) t'

-- TpApp, TpAppt
infixl 10 _·ₜ_ _`ₜ_
-- Pattern and constructor
pattern _·ₜ_ T T' = TpApp T T'
pattern _`ₜ_ T t = TpAppt T t

-- TpArrow
infixr 5 _➔_ _➾_  -- "➔" = "\r" (↕ 5, ↔ 1), "➾" = "\r" (↕ 7, ↔ 8)
pattern _➔_ T T' = TpArrow T NotErased T'
pattern _➾_ T T' = TpArrow T Erased T'

-- TpEq
infix 4 [_≃_] `[_≃_]
pattern [_≃_] t₁ t₂ = TpEq _ t₁ t₂ _
`[_≃_] = λ t₁ t₂ → TpEq pi-gen t₁ t₂ pi-gen

-- TpHole
pattern ●ₜ = TpHole _
`●ₜ = TpHole pi-gen

-- TpLambda
infixr 4 λₜ_:ₜ_₊_ λₜ_:ₖ_₊_ `λₜ_:ₜ_₊_ `λₜ_:ₖ_₊_
pattern λₜ_:ₜ_₊_ x T T' = TpLambda _ _ x (Tkt T) T'
pattern λₜ_:ₖ_₊_ x k T = TpLambda _ _ x (Tkk k) T
`λₜ_:ₜ_₊_ = λ x T T' → TpLambda pi-gen pi-gen x (Tkt T) T'
`λₜ_:ₖ_₊_ = λ x k T → TpLambda pi-gen pi-gen x (Tkk k) T

-- TpParens
infix 4 ⦅_⦆ₜ `⦅_⦆ₜ
pattern ⦅_⦆ₜ T = TpParens _ T _
`⦅_⦆ₜ = λ T → TpParens pi-gen T pi-gen

-- TpVar
infix 11 Vₓ_ `Vₓ_
pattern Vₓ_ x = TpVar _ x
`Vₓ_ = TpVar pi-gen

-- KndArrow : kind → kind → kind

-- KndParens : posinfo → kind → posinfo → kind
infix 4 ⦅_⦆ₖ `⦅_⦆ₖ
pattern ⦅_⦆ₖ k = KndParens _ k _
`⦅_⦆ₖ = λ k → KndParens pi-gen k pi-gen

-- KndPi : posinfo → posinfo → bvar → tk → kind → kind

-- KndTpArrow : type → kind → kind

-- KndVar
infix 11 κ_`_ `κ_`_
pattern κ_`_ x as = KndVar _ x as
`κ_`_ = KndVar pi-gen

-- Star
pattern ★ = Star _
`★ = Star pi-gen


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

open import Data.Product
open import Data.List using (List; []; _∷_; _∷ʳ_; _++_)
open import Data.List.Properties using (∷-injective)

open import Relation.Nullary
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; cong)

open import Common

module Trace {ℙ : Set} (message : Message ℙ)
  where

open import Action message public
open import SessionType message

Trace : Set
Trace = List Action

--| CO-TRACES |--

co-trace : Trace -> Trace
co-trace = Data.List.map co-action

co-trace-involution : (φ : Trace) -> co-trace (co-trace φ) ≡ φ
co-trace-involution [] = refl
co-trace-involution (α ∷ φ) rewrite co-action-involution α | co-trace-involution φ = refl

co-trace-++ : (φ ψ : Trace) -> co-trace (φ ++ ψ) ≡ co-trace φ ++ co-trace ψ
co-trace-++ []      _ = refl
co-trace-++ (α ∷ φ) ψ = cong (co-action α ∷_) (co-trace-++ φ ψ)

co-trace-injective : ∀{φ ψ} -> co-trace φ ≡ co-trace ψ -> φ ≡ ψ
co-trace-injective {[]} {[]} eq = refl
co-trace-injective {x ∷ φ} {x₁ ∷ ψ} eq with ∷-injective eq
... | eq1 , eq2 rewrite co-action-injective eq1 | co-trace-injective eq2 = refl

--| PREFIX RELATION |--

data _⊑_ : Trace -> Trace -> Set where
  none : ∀{φ}     -> [] ⊑ φ
  some : ∀{φ ψ α} -> φ ⊑ ψ -> (α ∷ φ) ⊑ (α ∷ ψ)

⊑-refl : (φ : Trace) -> φ ⊑ φ
⊑-refl [] = none
⊑-refl (_ ∷ φ) = some (⊑-refl φ)

⊑-tran : ∀{φ ψ χ} -> φ ⊑ ψ -> ψ ⊑ χ -> φ ⊑ χ
⊑-tran none _ = none
⊑-tran (some le1) (some le2) = some (⊑-tran le1 le2)

⊑-++ : ∀{φ χ} -> φ ⊑ (φ ++ χ)
⊑-++ {[]} = none
⊑-++ {_ ∷ φ} = some (⊑-++ {φ})

⊑-precong-++ : ∀{φ ψ χ} -> ψ ⊑ χ -> (φ ++ ψ) ⊑ (φ ++ χ)
⊑-precong-++ {[]} le = le
⊑-precong-++ {_ ∷ _} le = some (⊑-precong-++ le)

⊑-co-trace : ∀{φ ψ} -> φ ⊑ ψ -> co-trace φ ⊑ co-trace ψ
⊑-co-trace none = none
⊑-co-trace (some le) = some (⊑-co-trace le)

⊑-trace : ∀{φ ψ} -> ψ ⊑ φ -> ∃[ φ' ] (φ ≡ ψ ++ φ')
⊑-trace {φ} none = φ , refl
⊑-trace {α ∷ φ} (some le) with ⊑-trace le
... | φ' , eq rewrite eq = φ' , refl

absurd-++-≡ : ∀{φ ψ : Trace}{α} -> (φ ++ α ∷ ψ) ≢ []
absurd-++-≡ {[]} ()
absurd-++-≡ {_ ∷ _} ()

absurd-++-⊑ : ∀{φ α ψ} -> ¬ (φ ++ α ∷ ψ) ⊑ []
absurd-++-⊑ {[]} ()
absurd-++-⊑ {_ ∷ _} ()

--| STRICT PREFIX RELATION |--

data _⊏_ : Trace -> Trace -> Set where
  none : ∀{α φ} -> [] ⊏ (α ∷ φ)
  some : ∀{φ ψ α} -> φ ⊏ ψ -> (α ∷ φ) ⊏ (α ∷ ψ)

⊏-irreflexive : ∀{φ} -> ¬ φ ⊏ φ
⊏-irreflexive (some lt) = ⊏-irreflexive lt

⊏-++ : ∀{φ ψ α} -> φ ⊏ (φ ++ (ψ ∷ʳ α))
⊏-++ {[]} {[]} = none
⊏-++ {[]} {_ ∷ _} = none
⊏-++ {_ ∷ φ} = some (⊏-++ {φ})

⊏->≢ : ∀{φ ψ} -> φ ⊏ ψ -> φ ≢ ψ
⊏->≢ (some lt) refl = ⊏-irreflexive lt

⊏->⊑ : ∀{φ ψ} -> φ ⊏ ψ -> φ ⊑ ψ
⊏->⊑ none = none
⊏->⊑ (some lt) = some (⊏->⊑ lt)


module Oscar.Prelude where

module _ where -- Objectevel

  open import Agda.Primitive public
    using ()
    renaming ( Level to Ł
             ; lzero to ∅̂
             ; lsuc to ↑̂_
             ; _⊔_ to _∙̂_ )

  infix 0 Ø_
  Ø_ : ∀ 𝔬 → Set (↑̂ 𝔬)
  Ø_ 𝔬 = Set 𝔬

  Ø₀ = Ø ∅̂
  Ø₁ = Ø (↑̂ ∅̂)

postulate
  magic : ∀ {a} {A : Ø a} → A

module _ where -- Function

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

  infixr 9 _∘′_
  _∘′_ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
           (B → C) → (A → B) → (A → C)
  f ∘′ g = f ∘ g

  ¡ : ∀ {𝔬} {𝔒 : Ø 𝔬} → 𝔒 → 𝔒
  ¡ 𝓞 = 𝓞

  infixl -10 ¡
  syntax ¡ {𝔒 = A} x = x ofType A

  ¡[_] : ∀ {𝔬} (𝔒 : Ø 𝔬) → 𝔒 → 𝔒
  ¡[ _ ] = ¡

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

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

  _∞⟦_⟧ : ∀ {a} {A : Set a} → A → ∀ {b} (B : Set b) → B → A
  x ∞⟦ B ⟧ = _∞ x {B = B}

  _∞₁ : ∀ ..{a} ..{A : Set a} → A → ∀ ..{b} ..{B : Set b} → ∀ ..{h} ..{H : Set h} → .(_ : B) .{_ : H} → A
  _∞₁ f _ = f

  _∞₃ : ∀ ..{a} ..{A : Set a} → A → ∀ ..{b} ..{B : Set b} → ∀ ..{h₁ h₂ h₃} ..{H₁ : Set h₁} ..{H₂ : Set h₂} ..{H₃ : Set h₃} → .(_ : B) .{_ : H₁} .{_ : H₂} .{_ : H₃} → A
  _∞₃ f _ = f

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

  it : ∀ {a} {A : Set a} {{x : A}} → A
  it {{x}} = x
  {-# INLINE it #-}

  ! = it

  asInstance : ∀ {a b} {A : Set a} {B : A → Set b} (x : A) → (∀ {{x}} → B x) → B x
  asInstance x f = f {{x}}
  {-# INLINE asInstance #-}

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

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

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

  -- The S combinator. (Written infix as in Conor McBride's paper
  -- "Outrageous but Meaningful Coincidences: Dependent type-safe syntax
  -- and evaluation".)

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

  infixr 0 case_of_ case_return_of_

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

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

  infixl 8 _on_
  _on_ : ∀ {a b c} {A : Set a} {B : A → Set b} {C : ∀ x y → B x → B y → Set c} →
           (∀ {x y} (z : B x) (w : B y) → C x y z w) → (f : ∀ x → B x) → ∀ x y →
           C x y (f x) (f y)
  h on f = λ x y → h (f x) (f y)
  {-# INLINE _on_ #-}

Function : ∀ {a} → Ø a → Ø a → Ø a
Function A B = A → B

Function⟦_⟧ : ∀ a → Ø a → Ø a → Ø a
Function⟦ a ⟧ = Function {a = a}

MFunction : ∀ {a b} (M : Ø a → Ø b) → Ø a → Ø a → Ø b
MFunction M A B = M A → M B

Arrow : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔞} {𝔟} → (𝔛 → Ø 𝔞) → (𝔛 → Ø 𝔟) → 𝔛 → 𝔛 → Ø 𝔞 ∙̂ 𝔟
Arrow 𝔄 𝔅 x y = 𝔄 x → 𝔅 y

module _ where

  Extension : ∀ {𝔬} {𝔒 : Ø 𝔬} {𝔭} (𝔓 : 𝔒 → Ø 𝔭) → 𝔒 → 𝔒 → Ø 𝔭
  Extension 𝔓 = Arrow 𝔓 𝔓

module _ where

  _⟨_⟩→_ : ∀ {𝔬} {𝔒 : Ø 𝔬} → 𝔒 → ∀ {𝔭} → (𝔒 → Ø 𝔭) → 𝔒 → Ø 𝔭
  m ⟨ 𝔓 ⟩→ n = Extension 𝔓 m n

π̂ : ∀ {𝔵} ℓ (𝔛 : Ø 𝔵) → Ø 𝔵 ∙̂ ↑̂ ℓ
π̂ ℓ 𝔛 = 𝔛 → Ø ℓ

infixl 21 _←̂_
_←̂_ = π̂

π̇ : ∀ {𝔞 𝔟} (𝔄 : Ø 𝔞) (𝔅 : 𝔄 → Ø 𝔟) → Ø 𝔞 ∙̂ 𝔟
π̇ 𝔄 𝔅 = (𝓐 : 𝔄) → 𝔅 𝓐

infixl 20 π̇
syntax π̇ 𝔄 (λ 𝓐 → 𝔅𝓐) = 𝔅𝓐 ← 𝓐 ≔ 𝔄

π̇-hidden-quantifier-syntax = π̇
infixl 20 π̇-hidden-quantifier-syntax
syntax π̇-hidden-quantifier-syntax 𝔄 (λ _ → 𝔅𝓐) = 𝔅𝓐 ← 𝔄

π̂² : ∀ {𝔞} ℓ → Ø 𝔞 → Ø 𝔞 ∙̂ ↑̂ ℓ
π̂² ℓ 𝔄 = ℓ ←̂ 𝔄 ← 𝔄

_→̂²_ : ∀ {𝔞} → Ø 𝔞 → ∀ ℓ → Ø 𝔞 ∙̂ ↑̂ ℓ
_→̂²_ 𝔒 ℓ = π̂² ℓ 𝔒

record Lift {a ℓ} (A : Set a) : Set (a ∙̂ ℓ) where
  instance constructor lift
  field lower : A

open Lift public

record Wrap {𝔵} (𝔛 : Ø 𝔵) : Ø 𝔵 where
  constructor ∁
  field
    π₀ : 𝔛

open Wrap public

∀̇ : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔞}
  → (∀ ℓ (𝔄 : Ø 𝔞) → Ø 𝔞 ∙̂ ↑̂ ℓ)
  → ∀ ℓ → (𝔛 → Ø 𝔞) → Ø 𝔵 ∙̂ 𝔞 ∙̂ ↑̂ ℓ
∀̇ Q ℓ 𝔄 = ∀ {x} → Q ℓ (𝔄 x)

Ṙelation : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔞} ℓ → (𝔞 ←̂ 𝔛) → Ø 𝔵 ∙̂ 𝔞 ∙̂ ↑̂ ℓ
Ṙelation ℓ P = Wrap (∀̇ π̂² ℓ P)

Pointwise : ∀ {𝔞} {𝔄 : Ø 𝔞} {𝔟} {𝔅 : Ø 𝔟} {ℓ} → 𝔅 →̂² ℓ → (𝔅 ← 𝔄) → (𝔄 → 𝔅) → Ø 𝔞 ∙̂ ℓ
Pointwise _≈_ = λ f g → ∀ x → f x ≈ g x

Ṗroperty : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔬} ℓ → (𝔵 ∙̂ 𝔬 ∙̂ ↑̂ ℓ) ←̂ (𝔬 ←̂ 𝔛)
Ṗroperty ℓ P = Wrap (∀̇ π̂ ℓ P)

LeftṖroperty : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔶} {𝔜 : 𝔛 → Ø 𝔶} {𝔯} → ∀ ℓ → ((x : 𝔛) → 𝔜 x → Ø 𝔯) → 𝔛 → Ø 𝔶 ∙̂ 𝔯 ∙̂ ↑̂ ℓ
LeftṖroperty ℓ _↦_ = Ṗroperty ℓ ∘ _↦_

ArrowṖroperty : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔭₁ 𝔭₂} → ∀ ℓ → (𝔛 → Ø 𝔭₁) → (𝔛 → Ø 𝔭₂) → 𝔛 → Ø 𝔵 ∙̂ 𝔭₁ ∙̂ 𝔭₂ ∙̂ ↑̂ ℓ
ArrowṖroperty ℓ 𝔒₁ 𝔒₂ = LeftṖroperty ℓ (Arrow 𝔒₁ 𝔒₂)

module _ where

  infixr 5 _,_
  record Σ {𝔬} (𝔒 : Ø 𝔬) {𝔭} (𝔓 : 𝔒 → Ø 𝔭) : Ø 𝔬 ∙̂ 𝔭 where
    instance constructor _,_
    field
      π₀ : 𝔒
      π₁ : 𝔓 π₀

  open Σ public

  infixr 5 _,,_
  record Σ′ {𝔬} (𝔒 : Ø 𝔬) {𝔭} (𝔓 : Ø 𝔭) : Ø 𝔬 ∙̂ 𝔭 where
    instance constructor _,,_
    field
      π₀ : 𝔒
      π₁ : 𝔓

  open Σ′ public

  _×_ : ∀ {𝔬₁ 𝔬₂} (𝔒₁ : Ø 𝔬₁) (𝔒₂ : Ø 𝔬₂) → Ø 𝔬₁ ∙̂ 𝔬₂
  _×_ O₁ O₂ = Σ O₁ (λ _ → O₂)

  ∃_ : ∀ {𝔬} {𝔒 : Ø 𝔬} {𝔭} (𝔓 : 𝔒 → Ø 𝔭) → Ø 𝔬 ∙̂ 𝔭
  ∃_ = Σ _

  uncurry : ∀ {a b c} {A : Set a} {B : A → Set b} {C : ∀ x → B x → Set c} →
              (∀ x (y : B x) → C x y) → (p : Σ A B) → C (π₀ p) (π₁ p)
  uncurry f (x , y) = f x y

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

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

ExtensionṖroperty : ∀ {𝔵} {𝔛 : Ø 𝔵} {𝔬} {ℓ̇}
  ℓ (𝔒 : 𝔛 → Ø 𝔬) (_↦_ : ∀̇ π̂² ℓ̇ 𝔒)
  → Ø 𝔵 ∙̂ 𝔬 ∙̂ ↑̂ ℓ ∙̂ ℓ̇
ExtensionṖroperty ℓ 𝔒 _↦_ = Σ (Ṗroperty ℓ 𝔒) (λ P → ∀ {x} {f g : 𝔒 x} → f ↦ g → Extension (π₀ P) f g)

LeftExtensionṖroperty : ∀ {𝔶} {𝔜 : Ø 𝔶} {𝔵} {𝔛 : 𝔜 → Ø 𝔵} {𝔬} {ℓ̇}
  ℓ (𝔒 : (y : 𝔜) → 𝔛 y → Ø 𝔬) (_↦_ : ∀ {y} → ∀̇ π̂² ℓ̇ (𝔒 y))
  → 𝔜
  → Ø 𝔵 ∙̂ 𝔬 ∙̂ ↑̂ ℓ ∙̂ ℓ̇
LeftExtensionṖroperty ℓ 𝔒 _↦_ y = ExtensionṖroperty ℓ (𝔒 y) _↦_

ArrowExtensionṖroperty : ∀
  {𝔵} {𝔛 : Ø 𝔵}
  {𝔬₁} ℓ (𝔒₁ : 𝔛 → Ø 𝔬₁)
  {𝔬₂} (𝔒₂ : 𝔛 → Ø 𝔬₂)
  → ∀ {ℓ̇} (_↦_ : ∀̇ π̂² ℓ̇ 𝔒₂)
  → 𝔛
  → Ø 𝔵 ∙̂ 𝔬₁ ∙̂ 𝔬₂ ∙̂ ↑̂ ℓ ∙̂ ℓ̇
ArrowExtensionṖroperty ℓ 𝔒₁ 𝔒₂ _↦_ = LeftExtensionṖroperty ℓ (Arrow 𝔒₁ 𝔒₂) (Pointwise _↦_)

record Instance {a} (A : Set a) : Set a where
  constructor ∁
  field {{x}} : A

mkInstance : ∀ {a} {A : Set a} → A → Instance A
mkInstance x = ∁ {{x}}


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

-- Theorems about noninterference.

open import Calf.CostMonoid

module Calf.Noninterference (costMonoid : CostMonoid) where

open import Calf.Prelude
open import Calf.Metalanguage
open import Calf.Step costMonoid
open import Calf.PhaseDistinction
open import Calf.Types.Eq

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

oblivious : ∀ {A B} (f : cmp (F A) → val (◯⁺ B)) →
      ∀ c e → f (step (F A) c e) ≡ f e
oblivious {A} {B} f c e = funext/Ω (λ u →
  begin
  f (step (F A) c e) u ≡⟨ cong (λ e → f e u) (step/ext (F A) e c u) ⟩
  f e u
  ∎
  )
  where open ≡-Reasoning

unique : ∀ {A} → (a : val (● A)) → (u : ext) → a ≡ ∗ u
unique {A} a u =
  eq/ref
  (●/ind {A} a (λ a → F (eq (● A) a (∗ u)))
  (λ a → ret (eq/intro (η≡∗ a u)))
  (λ u → ret (eq/intro refl))
  (λ a u → eq/uni _ _ u))

constant : ∀ {A B} (f : val (● A) → val (◯⁺ B)) →
  Σ (val (◯⁺ B)) λ b → f ≡ λ _ → b
constant f =
  (λ u → f (∗ u) u) , funext (λ a → funext/Ω (λ u →
    P.cong (λ a → f a u) (unique a u)))

optimization : ∀ {C B : tp pos} {A : val C → tp pos}
  (f : val (Σ++ C λ c → ● (A c)) → val (◯⁺ B)) →
  Σ (val C → val (◯⁺ B)) λ f' → ∀ c a → f (c , a) ≡ f' c
optimization {C} {B} {A} f =
  (λ c →
    let g : val (● (A c)) → val (◯⁺ B)
        g a = f (c , a) in
    let (b , h) = constant {A c} {B} g in
    b) ,
    λ c a →
    let g : val (● (A c)) → val (◯⁺ B)
        g a = f (c , a) in
    let (b , h) = constant {A c} {B} g in
    P.cong-app h a


{-# OPTIONS --rewriting #-}

module Luau.OpSem where

open import Agda.Builtin.Equality using (_≡_)
open import Agda.Builtin.Float using (Float; primFloatPlus; primFloatMinus; primFloatTimes; primFloatDiv; primFloatEquality; primFloatLess; primFloatInequality)
open import Agda.Builtin.Bool using (Bool; true; false)
open import Agda.Builtin.String using (primStringEquality; primStringAppend)
open import Utility.Bool using (not; _or_; _and_)
open import Agda.Builtin.Nat using () renaming (_==_ to _==ᴬ_)
open import FFI.Data.Maybe using (Maybe; just; nothing)
open import Luau.Heap using (Heap; _≡_⊕_↦_; _[_]; function_is_end)
open import Luau.Substitution using (_[_/_]ᴮ)
open import Luau.Syntax using (Value; Expr; Stat; Block; nil; addr; val; var; function_is_end; _$_; block_is_end; local_←_; _∙_; done; return; name; fun; arg; binexp; BinaryOperator; +; -; *; /; <; >; ==; ~=; <=; >=; ··; number; bool; string)
open import Luau.RuntimeType using (RuntimeType; valueType)
open import Properties.Product using (_×_; _,_)

evalEqOp : Value → Value → Bool
evalEqOp Value.nil Value.nil = true
evalEqOp (addr x) (addr y) = (x ==ᴬ y)
evalEqOp (number x) (number y) = primFloatEquality x y
evalEqOp (bool true) (bool y) = y
evalEqOp (bool false) (bool y) = not y
evalEqOp _ _ = false

evalNeqOp : Value → Value → Bool
evalNeqOp (number x) (number y) = primFloatInequality x y
evalNeqOp x y = not (evalEqOp x y)

data _⟦_⟧_⟶_ : Value → BinaryOperator → Value → Value → Set where
  + : ∀ m n → (number m) ⟦ + ⟧ (number n) ⟶ number (primFloatPlus m n)
  - : ∀ m n → (number m) ⟦ - ⟧ (number n) ⟶ number (primFloatMinus m n)
  / : ∀ m n → (number m) ⟦ / ⟧ (number n) ⟶ number (primFloatTimes m n)
  * : ∀ m n → (number m) ⟦ * ⟧ (number n) ⟶ number (primFloatDiv m n)
  < : ∀ m n → (number m) ⟦ < ⟧ (number n) ⟶ bool (primFloatLess m n)
  > : ∀ m n → (number m) ⟦ > ⟧ (number n) ⟶ bool (primFloatLess n m)
  <= : ∀ m n → (number m) ⟦ <= ⟧ (number n) ⟶ bool ((primFloatLess m n) or (primFloatEquality m n))
  >= : ∀ m n → (number m) ⟦ >= ⟧ (number n) ⟶ bool ((primFloatLess n m) or (primFloatEquality m n))
  == : ∀ v w → v ⟦ == ⟧ w ⟶ bool (evalEqOp v w)
  ~= : ∀ v w → v ⟦ ~= ⟧ w ⟶ bool (evalNeqOp v w)
  ·· : ∀ x y → (string x) ⟦ ·· ⟧ (string y) ⟶ string (primStringAppend x y)

data _⊢_⟶ᴮ_⊣_ {a} : Heap a → Block a → Block a → Heap a → Set
data _⊢_⟶ᴱ_⊣_ {a} : Heap a → Expr a → Expr a → Heap a → Set

data _⊢_⟶ᴱ_⊣_  where

  function : ∀ a {H H′ F B} →

    H′ ≡ H ⊕ a ↦ (function F is B end) →
    -------------------------------------------
    H ⊢ (function F is B end) ⟶ᴱ val(addr a) ⊣ H′

  app₁ : ∀ {H H′ M M′ N} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    -----------------------------
    H ⊢ (M $ N) ⟶ᴱ (M′ $ N) ⊣ H′

  app₂ : ∀ v {H H′ N N′} →

    H ⊢ N ⟶ᴱ N′ ⊣ H′ →
    -----------------------------
    H ⊢ (val v $ N) ⟶ᴱ (val v $ N′) ⊣ H′

  beta : ∀ O v {H a F B} →

    (O ≡ function F is B end) →
    H [ a ] ≡ just(O) →
    -----------------------------------------------------------------------------
    H ⊢ (val (addr a) $ val v) ⟶ᴱ (block (fun F) is (B [ v / name(arg F) ]ᴮ) end) ⊣ H

  block : ∀ {H H′ B B′ b} →

    H ⊢ B ⟶ᴮ B′ ⊣ H′ →
    ----------------------------------------------------
    H ⊢ (block b is B end) ⟶ᴱ (block b is B′ end) ⊣ H′

  return : ∀ v {H B b} →

    --------------------------------------------------------
    H ⊢ (block b is return (val v) ∙ B end) ⟶ᴱ val v ⊣ H

  done : ∀ {H b} →

    --------------------------------------------
    H ⊢ (block b is done end) ⟶ᴱ (val nil) ⊣ H

  binOp₀ : ∀ {H op v₁ v₂ w} →

    v₁ ⟦ op ⟧ v₂ ⟶ w →
    --------------------------------------------------
    H ⊢ (binexp (val v₁) op (val v₂)) ⟶ᴱ (val w) ⊣ H

  binOp₁ : ∀ {H H′ x x′ op y} →

    H ⊢ x ⟶ᴱ x′ ⊣ H′ →
    ---------------------------------------------
    H ⊢ (binexp x op y) ⟶ᴱ (binexp x′ op y) ⊣ H′

  binOp₂ : ∀ {H H′ x op y y′} →

    H ⊢ y ⟶ᴱ y′ ⊣ H′ →
    ---------------------------------------------
    H ⊢ (binexp x op y) ⟶ᴱ (binexp x op y′) ⊣ H′

data _⊢_⟶ᴮ_⊣_  where

  local : ∀ {H H′ x M M′ B} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    -------------------------------------------------
    H ⊢ (local x ← M ∙ B) ⟶ᴮ (local x ← M′ ∙ B) ⊣ H′

  subst : ∀ v {H x B} →

    ------------------------------------------------------
    H ⊢ (local x ← val v ∙ B) ⟶ᴮ (B [ v / name x ]ᴮ) ⊣ H

  function : ∀ a {H H′ F B C} →

    H′ ≡ H ⊕ a ↦ (function F is C end) →
    --------------------------------------------------------------
    H ⊢ (function F is C end ∙ B) ⟶ᴮ (B [ addr a / name(fun F) ]ᴮ) ⊣ H′

  return : ∀ {H H′ M M′ B} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    --------------------------------------------
    H ⊢ (return M ∙ B) ⟶ᴮ (return M′ ∙ B) ⊣ H′

data _⊢_⟶*_⊣_ {a} : Heap a → Block a → Block a → Heap a → Set where

  refl : ∀ {H B} →

    ----------------
    H ⊢ B ⟶* B ⊣ H

  step : ∀ {H H′ H″ B B′ B″} →
    H ⊢ B ⟶ᴮ B′ ⊣ H′ →
    H′ ⊢ B′ ⟶* B″ ⊣ H″ →
    ------------------
    H ⊢ B ⟶* B″ ⊣ H″


------------------------------------------------------------------------
-- A parametrised coinductive definition that can be used to define
-- strong and weak bisimilarity as well as expansion
------------------------------------------------------------------------

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

open import Prelude

open import Labelled-transition-system

module Bisimilarity.General
         {ℓ}
         (lts : LTS ℓ)
         (open LTS lts)
         (_[_]↝₁_ _[_]↝₂_ : Proc → Label → Proc → Type ℓ)
         (⟶→↝₁ : ∀ {p μ q} → p [ μ ]⟶ q → p [ μ ]↝₁ q)
         (⟶→↝₂ : ∀ {p μ q} → p [ μ ]⟶ q → p [ μ ]↝₂ q)
         where

open import Equality.Propositional as Eq hiding (Extensionality)
open import Logical-equivalence using (_⇔_)
open import Prelude.Size

open import Bijection equality-with-J as Bijection using (_↔_)
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J

open import Bisimilarity.Step lts _[_]↝₁_ _[_]↝₂_ as Step public
  using (StepC)
open import Indexed-container hiding (⟨_⟩; Bisimilarity)
open import Indexed-container.Combinators hiding (id; _∘_)
open import Relation
import Similarity.Step lts _[_]↝₁_ as Step₁
import Similarity.Step lts _[_]↝₂_ as Step₂

open Indexed-container public using (force)

------------------------------------------------------------------------
-- Bisimilarity

-- Bisimilarity. Note that this definition is small.

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

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

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

[_]_∼_ : Size → Proc → Proc → Type ℓ
[_]_∼_ i = curry (Bisimilarity i)

[_]_∼′_ : Size → Proc → Proc → Type ℓ
[_]_∼′_ i = curry (Bisimilarity′ i)

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

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

-- Bisimilarity is reflexive.

mutual

  reflexive-∼ : ∀ {p i} → [ i ] p ∼ p
  reflexive-∼ =
    StepC.⟨ (λ p⟶p′ → _ , ⟶→↝₁ p⟶p′ , reflexive-∼′)
          , (λ q⟶q′ → _ , ⟶→↝₂ q⟶q′ , reflexive-∼′)
          ⟩

  reflexive-∼′ : ∀ {p i} → [ i ] p ∼′ p
  force reflexive-∼′ = reflexive-∼

≡⇒∼ : ∀ {p q} → p ≡ q → p ∼ q
≡⇒∼ refl = reflexive-∼

-- Functions that can be used to aid the instance resolution
-- mechanism.

infix -2 ∼:_ ∼′:_

∼:_ : ∀ {i p q} → [ i ] p ∼ q → [ i ] p ∼ q
∼:_ = id

∼′:_ : ∀ {i p q} → [ i ] p ∼′ q → [ i ] p ∼′ q
∼′:_ = id

------------------------------------------------------------------------
-- Bisimilarity for bisimilarity

-- Bisimilarity of bisimilarity proofs.

infix 4 [_]_≡_ [_]_≡′_

[_]_≡_ : ∀ {p q} → Size → (_ _ : ν StepC ∞ (p , q)) → Type ℓ
[_]_≡_ i = curry (ν-bisimilar i)

[_]_≡′_ : ∀ {p q} → Size → (_ _ : ν′ StepC ∞ (p , q)) → Type ℓ
[_]_≡′_ i = curry (ν′-bisimilar i)

-- An alternative characterisation of bisimilarity of bisimilarity
-- proofs.

[]≡↔ :
  Eq.Extensionality ℓ ℓ →
  ∀ {p q} {i : Size} (p∼q₁ p∼q₂ : ν StepC ∞ (p , q)) →

  [ i ] p∼q₁ ≡ p∼q₂

    ↔

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′∼q′₁ = StepC.left-to-right p∼q₁ p⟶p′
       q′₂ , q⟶q′₂ , p′∼q′₂ = StepC.left-to-right p∼q₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝₁_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′∼q′₁ ≡′ p′∼q′₂)
    ×
  (∀ {q′ μ} (q⟶q′ : q [ μ ]⟶ q′) →
   let p′₁ , p⟶p′₁ , p′∼q′₁ = StepC.right-to-left p∼q₁ q⟶q′
       p′₂ , p⟶p′₂ , p′∼q′₂ = StepC.right-to-left p∼q₂ q⟶q′
   in ∃ λ (p′₁≡p′₂ : p′₁ ≡ p′₂) →
        subst (p [ μ ]↝₂_) p′₁≡p′₂ p⟶p′₁ ≡ p⟶p′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (_, q′)) p′₁≡p′₂ p′∼q′₁ ≡′ p′∼q′₂)

[]≡↔ ext {p} {q} {i} p∼q₁@(s₁ , f₁) p∼q₂@(s₂ , f₂) =

  [ i ] p∼q₁ ≡ p∼q₂                                                    ↝⟨ ν-bisimilar↔ ext p∼q₁ p∼q₂ ⟩

  ⟦ StepC₁ ⟷ StepC₂ ⟧₂ (ν′-bisimilar i) (p∼q₁ , p∼q₂)                  ↝⟨ ⟦⟷⟧₂↔ ext StepC₁ StepC₂ (ν′-bisimilar i) p∼q₁ p∼q₂ ⟩

  ⟦ StepC₁ ⟧₂ (ν′-bisimilar i)
    ((proj₁ s₁ , f₁ ∘ inj₁) , (proj₁ s₂ , f₂ ∘ inj₁))
    ×
  ⟦ StepC₂ ⟧₂ (ν′-bisimilar i)
    ( (proj₂ s₁ , λ p → f₁ (inj₂ (_ , refl , p)))
    , (proj₂ s₂ , λ p → f₂ (inj₂ (_ , refl , p)))
    )                                                                  ↝⟨ Step₁.⟦StepC⟧₂↔ ext (ν′-bisimilar i) (proj₁ s₁ , f₁ ∘ inj₁)
                                                                                (proj₁ s₂ , f₂ ∘ inj₁)
                                                                            ×-cong
                                                                          Step₂.⟦StepC⟧₂↔ ext (ν′-bisimilar i)
                                                                            (proj₂ s₁ , λ p → f₁ (inj₂ (_ , refl , p)))
                                                                            (proj₂ s₂ , λ p → f₂ (inj₂ (_ , refl , p))) ⟩
  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′∼q′₁ =
         Step₁.StepC.challenge (proj₁ s₁ , f₁ ∘ inj₁) p⟶p′
       q′₂ , q⟶q′₂ , p′∼q′₂ =
         Step₁.StepC.challenge (proj₁ s₂ , f₂ ∘ inj₁) p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝₁_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′∼q′₁ ≡′ p′∼q′₂)
    ×
  ((∀ {q′ μ} (q⟶q′ : q [ μ ]⟶ q′) →
   let p′₁ , p⟶p′₁ , p′∼q′₁ =
          Step₂.StepC.challenge
             (proj₂ s₁ , λ p → f₁ (inj₂ (_ , refl , p))) q⟶q′
       p′₂ , p⟶p′₂ , p′∼q′₂ =
          Step₂.StepC.challenge
             (proj₂ s₂ , λ p → f₂ (inj₂ (_ , refl , p))) q⟶q′
   in ∃ λ (p′₁≡p′₂ : p′₁ ≡ p′₂) →
        subst (p [ μ ]↝₂_) p′₁≡p′₂ p⟶p′₁ ≡ p⟶p′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (_, q′)) p′₁≡p′₂ p′∼q′₁ ≡′ p′∼q′₂))  ↔⟨⟩

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′∼q′₁ = StepC.left-to-right p∼q₁ p⟶p′
       q′₂ , q⟶q′₂ , p′∼q′₂ = StepC.left-to-right p∼q₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝₁_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′∼q′₁ ≡′ p′∼q′₂)
    ×
  (∀ {q′ μ} (q⟶q′ : q [ μ ]⟶ q′) →
   let p′₁ , p⟶p′₁ , p′∼q′₁ = StepC.right-to-left p∼q₁ q⟶q′
       p′₂ , p⟶p′₂ , p′∼q′₂ = StepC.right-to-left p∼q₂ q⟶q′
   in ∃ λ (p′₁≡p′₂ : p′₁ ≡ p′₂) →
        subst (p [ μ ]↝₂_) p′₁≡p′₂ p⟶p′₁ ≡ p⟶p′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (_, q′)) p′₁≡p′₂ p′∼q′₁ ≡′ p′∼q′₂)   □

  where
  open Container

  StepC₁ = Step₁.StepC
  StepC₂ = Step₂.StepC

module Bisimilarity-of-∼
         (ext : Eq.Extensionality ℓ ℓ)
         {p q} {i : Size}
         (p∼q₁ p∼q₂ : ν StepC ∞ (p , q))
         where

  -- A "constructor".

  ⟨_,_,_,_,_⟩ :
    (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
     let q′₁ , q⟶q′₁ , p′∼q′₁ = StepC.left-to-right p∼q₁ p⟶p′
         q′₂ , q⟶q′₂ , p′∼q′₂ = StepC.left-to-right p∼q₂ p⟶p′
     in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
          subst (q [ μ ]↝₁_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
            ×
          [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′∼q′₁ ≡′ p′∼q′₂) →
    (∀ {q′ μ} (q⟶q′ : q [ μ ]⟶ q′) →
     let p′₁ , p⟶p′₁ , p′∼q′₁ = StepC.right-to-left p∼q₁ q⟶q′
         p′₂ , p⟶p′₂ , p′∼q′₂ = StepC.right-to-left p∼q₂ q⟶q′
     in ∃ λ (p′₁≡p′₂ : p′₁ ≡ p′₂) →
          subst (p [ μ ]↝₂_) p′₁≡p′₂ p⟶p′₁ ≡ p⟶p′₂
            ×
          [ i ] subst (ν′ StepC ∞ ∘ (_, q′)) p′₁≡p′₂ p′∼q′₁ ≡′ p′∼q′₂) →
    [ i ] p∼q₁ ≡ p∼q₂
  ⟨_,_,_,_,_⟩ = curry (_↔_.from ([]≡↔ ext p∼q₁ p∼q₂))

  -- Some "projections".

  left-to-right :
    [ i ] p∼q₁ ≡ p∼q₂ →
    ∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
    let q′₁ , q⟶q′₁ , p′∼q′₁ = StepC.left-to-right p∼q₁ p⟶p′
        q′₂ , q⟶q′₂ , p′∼q′₂ = StepC.left-to-right p∼q₂ p⟶p′
    in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
         subst (q [ μ ]↝₁_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
           ×
         [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′∼q′₁ ≡′ p′∼q′₂
  left-to-right = proj₁ ∘ _↔_.to ([]≡↔ ext p∼q₁ p∼q₂)

  right-to-left :
    [ i ] p∼q₁ ≡ p∼q₂ →
    ∀ {q′ μ} (q⟶q′ : q [ μ ]⟶ q′) →
    let p′₁ , p⟶p′₁ , p′∼q′₁ = StepC.right-to-left p∼q₁ q⟶q′
        p′₂ , p⟶p′₂ , p′∼q′₂ = StepC.right-to-left p∼q₂ q⟶q′
    in ∃ λ (p′₁≡p′₂ : p′₁ ≡ p′₂) →
         subst (p [ μ ]↝₂_) p′₁≡p′₂ p⟶p′₁ ≡ p⟶p′₂
           ×
         [ i ] subst (ν′ StepC ∞ ∘ (_, q′)) p′₁≡p′₂ p′∼q′₁ ≡′ p′∼q′₂
  right-to-left = proj₂ ∘ _↔_.to ([]≡↔ ext p∼q₁ p∼q₂)

-- A statement of extensionality for bisimilarity.

Extensionality : Type ℓ
Extensionality = ν′-extensionality StepC

-- This form of extensionality can be used to derive another form
-- (in the presence of extensionality for functions).

extensionality :
  Eq.Extensionality ℓ ℓ →
  Extensionality →
  ∀ {p q} {p∼q₁ p∼q₂ : ν StepC ∞ (p , q)} →
  [ ∞ ] p∼q₁ ≡ p∼q₂ → p∼q₁ ≡ p∼q₂
extensionality ext ν-ext = ν-extensionality ext ν-ext

open StepC public using (⟨_,_⟩; left-to-right; right-to-left)


module Examples.Syntax where

open import Agda.Builtin.Equality using (_≡_; refl)
open import FFI.Data.String using (_++_)
open import Luau.Syntax using (var; _$_; return; nil; function_is_end; local_←_; done; _∙_; _⟨_⟩)
open import Luau.Syntax.ToString using (exprToString; blockToString)

ex1 : exprToString(function "" ⟨ var "x" ⟩ is return (var "f" $ var "x") ∙ done end) ≡
  "function(x)\n" ++
  "  return f(x)\n" ++
  "end"
ex1 = refl

ex2 : blockToString(local var "x" ← nil ∙ return (var "x") ∙ done) ≡
  "local x = nil\n" ++
  "return x"
ex2 = refl

ex3 : blockToString(function "f" ⟨ var "x" ⟩ is return (var "x") ∙ done end ∙ return (var "f") ∙ done) ≡
  "local function f(x)\n" ++
  "  return x\n" ++
  "end\n" ++
  "return f"
ex3 = refl


{- Denotational semantics of the terms in the category of temporal types. -}
module Semantics.Terms where

open import Syntax.Context renaming (_,_ to _,,_)
open import Syntax.Terms
open import Syntax.Types

open import Semantics.Types
open import Semantics.Context

open import CategoryTheory.Instances.Reactive
open import CategoryTheory.Linear
open import TemporalOps.Box
open import TemporalOps.Diamond
open import TemporalOps.OtherOps
open import TemporalOps.Linear
open import TemporalOps.StrongMonad

open import CategoryTheory.Functor
open import CategoryTheory.NatTrans renaming (_⟹_ to ⟹)
import CategoryTheory.Monad as M
import CategoryTheory.Comonad as W

open import Data.Product
open import Data.Sum hiding ([_,_])
import Data.Nat as N

open W.Comonad W-□
open M.Monad M-◇
private module F-◇ = Functor F-◇
private module F-□ = Functor F-□
-- open Linear ℝeactive-linear

mutual
    -- Denotation of pure terms as morphisms from contexts to types.
    ⟦_⟧ₘ : ∀{Γ A} -> Γ ⊢ A -> (⟦ Γ ⟧ₓ ⇴ ⟦ A ⟧ⱼ)
    ⟦ var top       ⟧ₘ = π₂
    ⟦ var (pop x)   ⟧ₘ = ⟦ var x ⟧ₘ ∘ π₁
    ⟦ lam M         ⟧ₘ = Λ ⟦ M ⟧ₘ
    ⟦ F $ M         ⟧ₘ = eval ∘ ⟨ ⟦ F ⟧ₘ , ⟦ M ⟧ₘ ⟩
    ⟦ unit          ⟧ₘ = !
    ⟦ [ M ,, N ]    ⟧ₘ = ⟨ ⟦ M ⟧ₘ , ⟦ N ⟧ₘ ⟩
    ⟦ fst M         ⟧ₘ = π₁ ∘ ⟦ M ⟧ₘ
    ⟦ snd M         ⟧ₘ = π₂ ∘ ⟦ M ⟧ₘ
    ⟦ inl M         ⟧ₘ = ι₁ ∘ ⟦ M ⟧ₘ
    ⟦ inr M         ⟧ₘ = ι₂ ∘ ⟦ M ⟧ₘ
    ⟦ case M inl↦ B₁ ||inr↦ B₂ ⟧ₘ = [ ⟦ B₁ ⟧ₘ ⁏ ⟦ B₂ ⟧ₘ ] ∘ dist ∘ ⟨ id , ⟦ M ⟧ₘ ⟩
    ⟦ sample {A} S  ⟧ₘ = ε.at ⟦ A ⟧ₜ ∘ ⟦ S ⟧ₘ
    ⟦ stable {Γ} S  ⟧ₘ = F-□.fmap ⟦ S ⟧ₘ ∘ ⟦ Γ ˢ⟧□
    ⟦ sig S         ⟧ₘ = ⟦ S ⟧ₘ
    ⟦ letSig S In B ⟧ₘ = ⟦ B ⟧ₘ ∘ ⟨ id , ⟦ S ⟧ₘ ⟩
    ⟦ event E       ⟧ₘ = ⟦ E ⟧ᵐ

    -- Helper function for interpreting bound events
    bindEvent : ∀ Γ {⟦A⟧ ⟦B⟧} -> (⟦E⟧ : ⟦ Γ ⟧ₓ ⇴ ◇ ⟦A⟧) (⟦C⟧ : ⟦ Γ ˢ ⟧ₓ ⊗ ⟦A⟧ ⇴ ◇ ⟦B⟧)
                  -> (⟦ Γ ⟧ₓ ⇴ ◇ ⟦B⟧)
    bindEvent Γ {⟦A⟧}{⟦B⟧} ⟦E⟧ ⟦C⟧ =
        μ.at ⟦B⟧ ∘ F-◇.fmap (⟦C⟧ ∘ ε.at ⟦ Γ ˢ ⟧ₓ * id) ∘ st ⟦ Γ ˢ ⟧ₓ ⟦A⟧ ∘ ⟨ ⟦ Γ ˢ⟧□ , ⟦E⟧ ⟩

    -- Denotation of computational terms as Kleisli morphisms from contexts to types.
    ⟦_⟧ᵐ : ∀{Γ A} -> Γ ⊨ A -> (⟦ Γ ⟧ₓ ⇴ ◇ ⟦ A ⟧ⱼ)
    ⟦ pure {A} M     ⟧ᵐ = η.at ⟦ A ⟧ⱼ ∘ ⟦ M ⟧ₘ
    ⟦ letSig S InC C ⟧ᵐ = ⟦ C ⟧ᵐ ∘ ⟨ id , ⟦ S ⟧ₘ ⟩
    ⟦ letEvt_In_ {Γ} E C ⟧ᵐ = bindEvent Γ ⟦ E ⟧ₘ ⟦ C ⟧ᵐ
    ⟦ select_↦_||_↦_||both↦_ {Γ} E₁ C₁ E₂ C₂ C₃ ⟧ᵐ =
        bindEvent Γ (⟪ ⟦ E₁ ⟧ₘ , ⟦ E₂ ⟧ₘ ⟫) (handle ⟦ C₁ ⟧ᵐ ⟦ C₂ ⟧ᵐ ⟦ C₃ ⟧ᵐ)


module Implicits.Resolution.Infinite.NormalFormEquiv where

open import Prelude

open import Implicits.Syntax
open import Implicits.Resolution.Infinite.Resolution
open import Implicits.Resolution.Embedding
open import Implicits.Resolution.Embedding.Lemmas

open import SystemF.Everything as F using ()
open import SystemF.NormalForm

open import Data.Vec hiding ([_])
open import Relation.Binary.HeterogeneousEquality as H using ()
open import Data.List.Any
open import Data.List.Properties
open import Data.Vec.Properties as VP using ()
open import Extensions.Vec
open import Function.Equivalence using (_⇔_; equivalence)

mutual

  from-⇓ : ∀ {ν n t a τ} {Γ : F.Ctx ν n} → Γ ⊢ t ⇓ a → ⟦ Γ ⟧ctx← ⊢ ⟦ a ⟧tp← ↓ τ →
           ∃ λ i → ⟦ Γ ⟧ctx← ⊢ ⟦ lookup i Γ ⟧tp← ↓ τ
  from-⇓ {Γ = Γ} (nvar i) ↓τ = i , ↓τ
  from-⇓ (napp p x) ↓τ = from-⇓ p (i-iabs (from-⇑ x) ↓τ)
  from-⇓ {Γ = Γ} (ntapp {a = a} b p) ↓τ = from-⇓ p
    (i-tabs ⟦ b ⟧tp← (subst (λ z → ⟦ Γ ⟧ctx← ⊢ z ↓ _) (⟦a/sub⟧tp← a b) ↓τ))

  from-⇑ : ∀ {ν n t a} {Γ : F.Ctx ν n} → Γ ⊢ t ⇑ a → ⟦ Γ ⟧ctx← ⊢ᵣ ⟦ a ⟧tp←
  from-⇑ (nbase b x) with ⟦base⟧tp← b
  from-⇑ (nbase b x) | τ , eq with from-⇓ x (subst (λ z → _ ⊢ z ↓ τ) (sym $ eq) (i-simp τ))
  from-⇑ (nbase b x) | τ , eq | i , lookup-i↓tc =
    subst (λ z → _ ⊢ᵣ z) (sym $ eq) (r-simp (lookup-∈ i _) lookup-i↓tc)
  from-⇑ (nabs p) = r-iabs (from-⇑ p)
  from-⇑ (ntabs p) = r-tabs (subst (λ z → z ⊢ᵣ _) (⟦weaken⟧ctx← _) (from-⇑ p))

  to-⇓ : ∀ {ν} {Δ : ICtx ν} {a τ t₁} → Δ ⊢ a ↓ τ → ⟦ Δ ⟧ctx→ ⊢ t₁ ⇓ ⟦ a ⟧tp→ →
        ∃ λ t₂ → ⟦ Δ ⟧ctx→ ⊢ t₂ ⇓ ⟦ simpl τ ⟧tp→
  to-⇓ (i-simp a) q = , q
  to-⇓ (i-iabs x p) q = to-⇓ p (napp q (proj₂ $ to-⇑ x))
  to-⇓ {Δ = Δ} (i-tabs {ρ = a} b p) q =
    to-⇓ p (subst (λ z → ⟦ Δ ⟧ctx→ ⊢ _ ⇓ z) (sym $ ⟦a/sub⟧tp→ a b) (ntapp ⟦ b ⟧tp→ q))

  to-⇑ : ∀ {ν} {Δ : ICtx ν} {a} → Δ ⊢ᵣ a → ∃ λ t → ⟦ Δ ⟧ctx→ ⊢ t ⇑ ⟦ a ⟧tp→
  to-⇑ {Δ = Δ} (r-simp {r = a} r r↓τ) = , nbase (⟦simpl⟧tp→ _) (proj₂ $ to-⇓ r↓τ var⇓a)
    where
      var⇓a : ⟦ Δ ⟧ctx→ ⊢ _ ⇓ ⟦ a ⟧tp→
      var⇓a = let (i , eq) = ∈⟶index (VP.List-∈⇒∈ r) in
        let i' = (subst Fin (sym $ length-map _ Δ) i) in
          subst (λ z → ⟦ Δ ⟧ctx→ ⊢ (F.var i') ⇓ z) (lookup⟦⟧ Δ i eq) (nvar i')
  to-⇑ (r-iabs p) =
    , nabs (proj₂ $ to-⇑ p)
  to-⇑ {Δ = Δ} (r-tabs {ρ = a} p) =
    , ntabs (⇑-subst-n (length-weaken-Δ Δ) (H.sym $ ⟦weaken⟧ctx→ Δ) (proj₂ (to-⇑ p)))

-- System F η-long-β-normal forms are isomorphic to infinite resolution derivations
equivalent : ∀ {ν} (Δ : ICtx ν) r → Δ ⊢ᵣ r ⇔ (∃ λ t → ⟦ Δ ⟧ctx→ ⊢ t ⇑ ⟦ r ⟧tp→)
equivalent Δ r = equivalence
  (λ x → to-⇑ x)
  (λ x → subst₂ (λ Δ' r' → Δ' ⊢ᵣ r') (ctx→← _) (tp→← r) (from-⇑ (proj₂ x)))

{-}
open import Function.Inverse
open import Function.Equality

-- from-⇑ : ∀ {ν n t a} {Γ : F.Ctx ν n} → Γ ⊢ t ⇑ a → ⟦ Γ ⟧ctx← ⊢ᵣ ⟦ a ⟧tp←
from-⇑' : ∀ {ν a} {Δ : ICtx ν} → (∃ λ t → ⟦ Δ ⟧ctx→ ⊢ t ⇑ ⟦ a ⟧tp→) → Δ ⊢ᵣ a
from-⇑' (_ , p) = subst₂ (λ Δ a → Δ ⊢ᵣ a) (ctx→← _) (tp→← _) (from-⇑ p)

-- from-⇓' : ∀ {ν a} {Δ : ICtx ν} {τ} → (∃ λ t → ⟦ Δ ⟧ctx→ ⊢ t ⇓ ⟦ a ⟧tp→) → Δ ⊢ Δ ⊢ a ↓ τ
-- from-⇓' {τ = τ} (_ , p) = subst₂ (λ Δ a → Δ ⊢ a ↓ τ) (ctx→← _) (tp→← _) (from-⇓ p)

-- from-to-⇓ : ∀ {ν a} {Δ : ICtx ν} {τ} → (p : Δ ⊢ a ↓ τ) → from-⇓' (to-⇓ p) ≡ p
-- from-to-⇓ p = ?

from-to-⇑ : ∀ {ν a} {Δ : ICtx ν} → (p : Δ ⊢ᵣ a) → from-⇑' (to-⇑ p) ≡ p
from-to-⇑ (r-simp x x₁) = {!!}
from-to-⇑ (r-iabs p) = begin
  from-⇑' (, (nabs (proj₂ (to-⇑ p))))
    ≡⟨ refl ⟩
  subst₂ (λ Δ a → Δ ⊢ᵣ a) (ctx→← _) (tp→← _) (from-⇑ (nabs (proj₂ (to-⇑ p))))
    ≡⟨ refl ⟩
  subst₂ (λ Δ a → Δ ⊢ᵣ a) (ctx→← _) (tp→← _) (r-iabs (from-⇑ (proj₂ (to-⇑ p))))
    ≡⟨ {!!} ⟩
  r-iabs (subst₂ (λ Δ a → Δ ⊢ᵣ a) (ctx→← _) (tp→← _) (from-⇑ (proj₂ (to-⇑ p))))
    ≡⟨ refl ⟩
  r-iabs (from-⇑' (to-⇑ p))
    ≡⟨ Prelude.cong r-iabs (from-to-⇑ p) ⟩
  r-iabs p ∎
from-to-⇑ (r-tabs p) with from-to-⇑ p
from-to-⇑ (r-tabs p) | x = {!x!}
  where
    lem : ∀ {ν a} {Δ : ICtx ν} →

to-from-⇑ : ∀ {ν a} {Δ : ICtx ν} → (p : ∃ λ t → ⟦ Δ ⟧ctx→ ⊢ t ⇑ ⟦ a ⟧tp→) → to-⇑ (from-⇑' p) ≡ p
to-from-⇑ p = {!!}

iso' : ∀ {ν a} {Δ : ICtx ν} → (→-to-⟶ (to-⇑ {Δ = Δ} {a = a})) InverseOf (→-to-⟶ from-⇑')
iso' = record { left-inverse-of = to-from-⇑; right-inverse-of = from-to-⇑ }
-}


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

module Cubical.HITs.Delooping.Two.Properties where

open import Cubical.Functions.Involution

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Univalence

open import Cubical.Data.Bool
open import Cubical.Data.Empty
open import Cubical.Data.Unit

open import Cubical.HITs.Delooping.Two.Base
open import Cubical.HITs.PropositionalTruncation

private
  variable
    ℓ : Level

module Embed where
  isSetIsPropDep : isOfHLevelDep {ℓ' = ℓ} 1 isSet
  isSetIsPropDep = isOfHLevel→isOfHLevelDep 1 (λ _ → isPropIsSet)

  notSet : PathP (λ i → isSet (notEq i)) isSetBool isSetBool
  notSet = isSetIsPropDep isSetBool isSetBool notEq

  notNot² : Square notEq refl refl notEq
  notNot² = involPath² notnot

  notNotSet
    : SquareP (λ i j → isSet (notNot² i j)) notSet refl refl notSet
  notNotSet = isPropDep→isSetDep'
                isSetIsPropDep
                (involPath² notnot)
                notSet refl refl notSet

  Code : Bℤ₂ → hSet ℓ-zero
  Code = Elim.rec
    (Bool , isSetBool)
    (λ i → notEq i , notSet i)
    (λ i j → λ where
      .fst → notNot² i j
      .snd → notNotSet i j)
    (isOfHLevelTypeOfHLevel 2)

  El : Bℤ₂ → Type₀
  El b = Code b .fst

module BINARY where
  open import Cubical.Data.FinSet.Binary.Large

  sem : Bℤ₂ → Binary _
  sem = Elim.rec Base Loop Loop² isGroupoidBinary

  loop? : Bool → base ≡ base
  loop? false = refl
  loop?  true = loop

  Loop²-coh : (a b c : Bool) → Type₀
  Loop²-coh false false false = Unit
  Loop²-coh false  true  true = Unit
  Loop²-coh  true false  true = Unit
  Loop²-coh  true  true false = Unit
  Loop²-coh     _     _     _ = ⊥

  rf : Bool ≡ Bool → Bool
  rf P = transport P false

  Loop²-coh-lemma₀
    : ∀(p q r : Bool)
    → r ⊕ p ≡ q
    → Loop²-coh p q r
  Loop²-coh-lemma₀ false false false sq = _
  Loop²-coh-lemma₀ false  true  true sq = _
  Loop²-coh-lemma₀  true false  true sq = _
  Loop²-coh-lemma₀  true  true false sq = _
  Loop²-coh-lemma₀ false  true false = false≢true
  Loop²-coh-lemma₀ false false  true = true≢false
  Loop²-coh-lemma₀  true false false = true≢false
  Loop²-coh-lemma₀  true  true  true = false≢true

  Loop²-coh-lemma
    : ∀(P Q R : Bool ≡ Bool)
    → Square P Q refl R
    → Loop²-coh (rf P) (rf Q) (rf R)
  Loop²-coh-lemma P Q R sq = Loop²-coh-lemma₀ p q r eqn
    where
    p = rf P
    q = rf Q
    r = rf R

    open BoolReflection

    cmp : P ∙ R ≡ Q
    cmp i j
      = hcomp (λ k → λ where
            (i = i0) → compPath-filler P R k j
            (i = i1) → Q j
            (j = i0) → Bool
            (j = i1) → R (i ∨ k))
          (sq i j)

    rcmp : ⊕-Path (r ⊕ p) ≡ ⊕-Path q
    rcmp = ⊕-Path (r ⊕ p)
             ≡[ i ]⟨ ⊕-comp p r (~ i) ⟩
           ⊕-Path p ∙ ⊕-Path r
             ≡[ i ]⟨ ⊕-complete P (~ i) ∙ ⊕-complete R (~ i) ⟩
           P ∙ R
             ≡⟨ cmp ⟩
           Q
             ≡⟨ ⊕-complete Q ⟩
           ⊕-Path q ∎
    open Iso
    eqn : r ⊕ p ≡ q
    eqn = transport (λ i →
              reflectIso .leftInv (r ⊕ p) i ≡ reflectIso .leftInv q i)
            (cong (reflectIso .inv) rcmp)

  loop²?
    : ∀ p q r → Loop²-coh p q r
    → Square (loop? p) (loop? q) refl (loop? r)
  loop²? false false false _ = refl
  loop²? false  true  true _ = λ i j → loop (i ∧ j)
  loop²?  true false  true _ = loop²
  loop²?  true  true false _ = refl

  module _ (B : Type₀) where
    based : (P : Bool ≃ B) → Bℤ₂
    based _ = base

    pull₀ : (P Q : Bool ≃ B) → Bool ≡ Bool
    pull₀ P Q i
      = hcomp (λ k → λ where
            (i = i0) → ua P (~ k)
            (i = i1) → ua Q (~ k))
          B

    pull₁ : (P Q : Bool ≃ B) → Square (ua P) (ua Q) (pull₀ P Q) refl
    pull₁ P Q i j
      = hcomp (λ k → λ where
            (i = i0) → ua P (~ k ∨ j)
            (i = i1) → ua Q (~ k ∨ j)
            (j = i1) → B)
          B

    pull₂
      : (P Q R : Bool ≃ B)
      → Square (pull₀ P Q) (pull₀ P R) refl (pull₀ Q R)
    pull₂ P Q R i j
      = hcomp (λ k → λ where
            (j = i0) → ua P (~ k)
            (i = i0) (j = i1) → ua Q (~ k)
            (i = i1) (j = i1) → ua R (~ k))
          B

    pull₃
      : (P Q R : Bool ≃ B)
      → Cube (pull₁ P Q) (pull₁ P R)
            (λ _ → ua P)  (pull₁ Q R)
            (pull₂ P Q R) (λ _ _ → B)
    pull₃ P Q R i j k
      = hcomp (λ τ → λ where
            (j = i0) → ua P (~ τ ∨ k)
            (i = i0) (j = i1) → ua Q (~ τ ∨ k)
            (i = i1) (j = i1) → ua R (~ τ ∨ k)
            (k = i1) → B)
          B

    looped : (P Q : Bool ≃ B) → based P ≡ based Q
    looped P Q = loop? b
      where
      b : Bool
      b = rf (pull₀ P Q)

    looped²
      : (P Q R : Bool ≃ B)
      → Square (looped P Q) (looped P R) refl (looped Q R)
    looped² P Q R = loop²? pq pr qr pqr
      where
      pq = rf (pull₀ P Q)
      pr = rf (pull₀ P R)
      qr = rf (pull₀ Q R)

      pqr : Loop²-coh pq pr qr
      pqr = Loop²-coh-lemma (pull₀ P Q) (pull₀ P R) (pull₀ Q R) (pull₂ P Q R)

  syn : Binary _ → Bℤ₂
  syn (B , tP) = rec→Gpd trunc (based B) 3k tP
    where
    open 3-Constant
    3k : 3-Constant (based B)
    3k .link = looped B
    3k .coh₁ = looped² B