module PiFrac.Category where
open import Categories.Category.Monoidal
open import Categories.Category.Monoidal.Braided
open import Categories.Category.Monoidal.Symmetric
open import Categories.Category.Monoidal.Rigid
open import Categories.Category.Monoidal.CompactClosed
open import Categories.Functor.Bifunctor
open import Categories.Category
open import Categories.Category.Product
open import Categories.Category.Groupoid
open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Sum
open import Data.Product
open import Data.Maybe
open import Relation.Binary.Core
open import Relation.Binary hiding (Symmetric)
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Function using (_∘_)
open import PiFrac.Syntax
open import PiFrac.Opsem
open import PiFrac.Interp
open import PiFrac.Properties

Pi∙ : Category _ _ _
Pi∙ = record
    { Obj = Σ[ t ∈ 𝕌 ] ⟦ t ⟧
    ; _⇒_ = λ (A , a) (B , b) → Σ[ c ∈ (A ↔ B) ] (interp c a ≡ just b)
    ; _≈_ = λ {(A , a)} {(B , b)} (c , eq) (c' , eq) → ⊤
    ; id = id↔ , refl
    ; _∘_ = λ (c₂ , eq₂) (c₁ , eq₁) → comp (c₁ , eq₁) (c₂ , eq₂)
    ; assoc = tt
    ; sym-assoc = tt
    ; identityˡ = tt
    ; identityʳ = tt
    ; identity² = tt
    ; equiv = record { refl = tt ; sym = λ _ → tt ; trans = λ _ _ → tt }
    ; ∘-resp-≈ = λ _ _ → tt
    }
    where
    comp : ∀ {A B C a b c} → Σ[ c₁ ∈ (A ↔ B) ] (interp c₁ a ≡ just b) → Σ[ c₂ ∈ (B ↔ C) ] (interp c₂ b ≡ just c)
         → Σ[ c' ∈ (A ↔ C) ] (interp c' a ≡ just c)
    comp {c = c} (c₁ , eq₁) (c₂ , eq₂) = (c₁ ⨾ c₂) , subst (λ x → (x >>= interp c₂) ≡ just c) (sym eq₁) eq₂

Pi∙Groupoid : Groupoid _ _ _
Pi∙Groupoid = record { category = Pi∙
                     ; isGroupoid = record { _⁻¹ = λ (c , eq) → ! c , interp! c _ _ eq
                                           ; iso = record { isoˡ = tt
                                                          ; isoʳ = tt } } }

Pi∙Monoidal : Monoidal Pi∙
Pi∙Monoidal = record
            { ⊗ = record
                { F₀ = λ ((A , a) , (B , b)) → (A ×ᵤ B) , (a , b)
                ; F₁ = λ {((A , a) , (B , b))} {((C , c) , (D , d))} ((c₁ , eq₁) , (c₂ , eq₂)) →
                         (c₁ ⊗ c₂) , subst (λ x → (x >>= (λ v₁' → interp c₂ b >>= λ v₂' → just (v₁' , v₂'))) ≡ just (c , d))
                                           (sym eq₁) (subst (λ x → (x >>= λ v₂' → just (c , v₂')) ≡ just (c , d)) (sym eq₂) refl)
                ; identity = tt
                ; homomorphism = tt
                ; F-resp-≈ = λ _ → tt
                }
            ; unit = (𝟙 , tt)
            ; unitorˡ = record { from = unite⋆l , refl
                               ; to   = uniti⋆l , refl
                               ; iso  = record { isoˡ = tt ; isoʳ = tt } }
            ; unitorʳ = record { from = unite⋆r , refl
                               ; to   = uniti⋆r , refl
                               ; iso  = record { isoˡ = tt ; isoʳ = tt } }
            ; associator = record { from = assocr⋆ , refl
                                  ; to   = assocl⋆ , refl
                                  ; iso  = record { isoˡ = tt ; isoʳ = tt } }
            ; unitorˡ-commute-from = tt
            ; unitorˡ-commute-to = tt
            ; unitorʳ-commute-from = tt
            ; unitorʳ-commute-to = tt
            ; assoc-commute-from = tt
            ; assoc-commute-to = tt
            ; triangle = tt
            ; pentagon = tt
            }

Pi∙Braided : Braided Pi∙Monoidal
Pi∙Braided = record { braiding = record { F⇒G = record { η = λ _ → swap⋆ , refl
                                                       ; commute = λ _ → tt
                                                       ; sym-commute = λ _ → tt }
                                        ; F⇐G = record { η = λ _ → swap⋆ , refl
                                                       ; commute = λ _ → tt
                                                       ; sym-commute = λ _ → tt }
                                        ; iso = λ X → record { isoˡ = tt ; isoʳ = tt } }
                    ; hexagon₁ = tt
                    ; hexagon₂ = tt }

Pi∙Symmetric : Symmetric Pi∙Monoidal
Pi∙Symmetric = record { braided = Pi∙Braided
                      ; commutative = tt}

Pi∙Rigid : LeftRigid Pi∙Monoidal
Pi∙Rigid = record { _⁻¹ = λ {(A , a) → 𝟙/ a , ↻}
                  ; η = λ {(X , x)} → ηₓ x , refl
                  ; ε = λ {(X , x)} → (swap⋆ ⨾ εₓ x) , εₓ≡
                  ; snake₁ = tt
                  ; snake₂ = tt}
           where
             εₓ≡ : ∀ {X} {x : ⟦ X ⟧} → interp (swap⋆ ⨾ εₓ x) (↻ , x) ≡ just tt
             εₓ≡ {X} {x} with x ≟  x
             ... | yes refl = refl
             ... | no  neq  = ⊥-elim (neq refl)

Pi∙CompactClosed : CompactClosed Pi∙Monoidal
Pi∙CompactClosed = record { symmetric = Pi∙Symmetric
                          ; rigid     = inj₁ Pi∙Rigid }


data ⊥ : Set where

postulate
  M : Set → Set
  A : Set
  _>>=_ : ∀ {A B} → M A → (A → M B) → M B
  ma : M A
  f  : A → M ⊥
  pure : ∀ {A} → A → M A

absurd-do : ∀ {A} → M A
absurd-do = do
  x  ← ma
  () ← f x

atabsurd-do : ∀ {A} → M A
atabsurd-do = do
  x      ← ma
  (y@()) ← f x -- I don't know why you would do that but we can support it


module Reright where
  open import Prelude
  open import Tactic.Reflection.Reright
  open import Agda.Builtin.Reflection -- for better pretty-printing of error messages

  -- 'reright' presents the user with changed context variabes, to mimic that done by 'rewrite'.
  simple-reright-test₁ : (A B : Set) (F : Set → Set) → F A → A ≡ B → B → A
  simple-reright-test₁ A B F FA A≡B b = reright A≡B $ λ (FB : F B) → b

  -- the target of the reright (in this case x≡y₁) is excluded from the changed context variables
  simple-reright-test₂ : {a : Level} {A : Set a} {x y : A} (x≡y₁ : x ≡ y) (x≡y₂ : x ≡ y) → y ≡ x
  simple-reright-test₂ {y = y} x≡y₁ x≡y₂ = reright x≡y₁ λ (x≡y₂' : y ≡ y) → refl

  -- the visibility of context variables remains the same in their changed state
  simple-reright-test₃ : {a : Level} {A : Set a} {x y : A} (x≡y₁ : x ≡ y) (x≡y₂ : x ≡ y) {x≡y₃ : x ≡ y} → y ≡ x
  simple-reright-test₃ {y = y} x≡y₁ x≡y₂ {x≡y₃} = reright x≡y₁ λ (x≡y₂' : y ≡ y) {x≡y₃' : y ≡ y} → refl

  -- for some reason, when the first changed variable is hidden, it's impossible to bring it into scope
  {- FAILS - results in unsolved metas
    problematic-visibility : {a : Level} {A : Set a} {x y : A} (x≡y₁ : x ≡ y) {x≡y₃ : x ≡ y} → y ≡ x
    problematic-visibility {y = y} x≡y₁ {x≡y₃} = reright x≡y₁ λ {x≡y₃' : y ≡ y} → refl
  -}

  module Test₁ where
    postulate
      Set≡Set : Set ≡ Set
      A₀ : Set
      A₁ : A₀ → Set
      A₂ : (a₀ : A₀) → A₁ a₀ → Set
      A₃ : (a₀ : A₀) → (a₁ : A₁ a₀) → A₂ a₀ a₁ → Set
      B₀ : Set
      B₁ : B₀ → Set
      B₂ : (b₀ : B₀) → B₁ b₀ → Set
      B₃ : (b₀ : B₀) → (b₁ : B₁ b₀) → B₂ b₀ b₁ → Set
      A₀≡B₀ : A₀ ≡ B₀
      F : Set → Set
      C : (α : Level) (β : Level) → Set α → Set β
      𝑨₀¹ : A₀
      𝑨₀² : A₀
      𝑨₀¹≡𝑨₀² : 𝑨₀¹ ≡ 𝑨₀²
      𝑨₂𝑨₀²⋆ : (a₁𝑨₀² : A₁ 𝑨₀²) → A₂ 𝑨₀² a₁𝑨₀²
      𝑩₀ : B₀
      K₀ : A₀ → Set

    test₀ : (b₀¹ b₀² : B₀) (b₀¹≡b₀² : b₀¹ ≡ b₀²) → Set
    test₀ b₀¹ b₀² b₀¹≡b₀² with b₀¹≡b₀²
    test₀ b₀¹ b₀² b₀¹≡b₀² | b₀¹≡b₀²-with = let b₀¹≡b₀²-let = b₀¹≡b₀²-with in reright b₀¹≡b₀²-let {!!}

    test₁ : ∀ (a₀ : A₀) → a₀ ≡ a₀
    test₁ a₀ = id (reright A₀≡B₀ {!!})

    test₂ : A₀ → B₀
    test₂ a₀ = reright A₀≡B₀ (λ b₀ → 𝑩₀)

    test₃ : A₀ → B₀
    test₃ a₀ = reright Set≡Set (reright A₀≡B₀ (λ b₀ → 𝑩₀))

    test₄ : A₀ → B₀
    test₄ a₀ = reright Set≡Set (reright A₀≡B₀ (λ b₀ → reright A₀≡B₀ {!!}))

    test₅ : A₀ → B₀
    test₅ a₀ = reright Set≡Set 𝑩₀

    test₆ : A₀ → B₀
    test₆ a₀ = reright Set≡Set $ reright A₀≡B₀ $ {!!}

    test₇ : ∀ {α : Level}
            (a₀ : A₀)
            {β : Level}
            (X Y : Set (α ⊔ β))
            → X ≡ Y
            → Y ≡ X
    test₇ {α} a₀ {β} X Y X≡Y = id (reright X≡Y {!!})

    test₈ : (a₁𝑨₀¹ : A₁ 𝑨₀¹) → A₂ 𝑨₀¹ a₁𝑨₀¹
    test₈ a₁𝑨₀¹ = reright 𝑨₀¹≡𝑨₀² (λ a₁𝑨₀² → {!!})

    test₉ : (a₀¹ : A₀) (a₀² : A₀) (a₀¹≡a₀²-1 : a₀¹ ≡ a₀²) (a₁a₀¹ : A₁ a₀¹) (X : Set) (Y : Set) (a₀¹≡a₀²-2 : a₀¹ ≡ a₀²) → F (A₂ a₀¹ a₁a₀¹) → F (A₁ a₀¹) ≡ A₂ a₀¹ a₁a₀¹
    test₉ a₀¹ a₀² a₀¹≡a₀²-1 a₁a₀¹ X Y a₀¹≡a₀²-2 = reright a₀¹≡a₀²-1 {!!}

    module _ (A₂⋆ : (a₀ : A₀) (a₁a₀ : A₁ a₀) → A₂ a₀ a₁a₀) where
      test₁₀ : (a₀ : A₀) (a₁a₀¹ : A₁ a₀) (a₁a₀² : A₁ a₀) (a₁a₀¹≡a₁a₀² : a₁a₀¹ ≡ a₁a₀²) → A₂ a₀ a₁a₀¹
      test₁₀ a₀ a₁a₀¹ a₁a₀² a₁a₀¹≡a₁a₀² = reright a₁a₀¹≡a₁a₀² {!!}

    test₁₁ : (a₀¹ : A₀) (a₀² : A₀) (FA₁a₀¹≡FA₁a₀² : F (A₁ a₀¹) ≡ F (A₁ a₀²)) → F (A₁ a₀¹) → F (A₁ a₀¹) ≡ F (F (A₁ a₀¹))
    test₁₁ a₀¹ a₀² FA₁a₀¹≡FA₁a₀² = reright FA₁a₀¹≡FA₁a₀² {!!}

    test₁₂ : (a₀¹ : A₀) (a₀² : A₀) (FA₁a₀¹≡FA₁a₀² : F (A₁ a₀¹) ≡ F (A₁ a₀²)) → F (A₁ a₀¹) → F (A₁ a₀¹) ≡ F (F (A₁ a₀¹))
    test₁₂ a₀¹ a₀² FA₁a₀¹≡FA₁a₀² FA₁a₀¹ = reright FA₁a₀¹≡FA₁a₀² {!!}

    test₁₃ : (β : Level)
             (a₀¹ : A₀)
             (χ : Level)
             (a₀² : A₀)
             (CA₁a₀¹≡CA₁a₀² : C lzero (β ⊔ χ) (A₁ a₀¹) ≡ C lzero (β ⊔ χ) (A₁ a₀²)) →
             C lzero (β ⊔ χ) (A₁ a₀¹)
             → Nat → Σ _ λ γ → C lzero (β ⊔ χ) (A₁ a₀¹) ≡ C γ (β ⊔ χ) (C lzero γ (A₁ a₀¹))
    test₁₃ β a₀¹ χ a₀² CA₁a₀¹≡CA₁a₀² CA₁a₀¹ = reright CA₁a₀¹≡CA₁a₀² {!!}

    test₁₄ : (a₀ : A₀) (FFA₁a₀≡FA₁a₀ : F (F (A₁ a₀)) ≡ F (A₁ a₀)) → F (F (F (F (A₁ a₀))))
    test₁₄ a₀ FFA₁a₀≡FA₁a₀ = reright FFA₁a₀≡FA₁a₀ (reright FFA₁a₀≡FA₁a₀ (reright FFA₁a₀≡FA₁a₀ {!!}))

    test₁₅ : (a₀ : A₀) (FA₁a₀≡FFA₁a₀ : F (A₁ a₀) ≡ F (F (A₁ a₀))) → F (F (A₁ a₀))
    test₁₅ a₀ FA₁a₀≡FFA₁a₀ = reright FA₁a₀≡FFA₁a₀ (reright FA₁a₀≡FFA₁a₀ {!!})

    test₁₆ : (l : A₀ → Level → Level)
             (β : Level)
             (a₀² : A₀)
             (a₀¹ : A₀)
             (CA₁a₀¹≡CA₁a₀² : C lzero (l a₀¹ β) (A₁ a₀¹) ≡ C lzero (l a₀¹ β) (A₁ a₀²))
             → C lzero (l a₀¹ β) (A₁ a₀¹)
             → Σ _ λ γ → C lzero (l a₀¹ β) (A₁ a₀¹) ≡ C γ (l a₀¹ β) (C lzero γ (A₁ a₀¹))
    test₁₆ l β a₀² a₀¹ CA₁a₀¹≡CA₁a₀² CA₁a₀¹ = reright CA₁a₀¹≡CA₁a₀² {!!}

    test₁₇ : (a₀¹ : A₀)
             (a₀² : A₀)
             (K₀a₀¹ : K₀ a₀¹)
             (a₀¹≡a₀² : a₀¹ ≡ a₀²)
           → Set
    test₁₇ a₀¹ a₀² K₀a₀¹ a₀¹≡a₀² = reright a₀¹≡a₀² {!!}

    test₁₈ : (a₀¹ : A₀)
             (a₀² : A₀)
             (k₀a₀¹ : K₀ a₀¹)
             (FK₀a₀¹ : F (K₀ a₀¹))
             (K₀a₀¹≡K₀a₀² : K₀ a₀¹ ≡ K₀ a₀²)
           → F (F (K₀ a₀¹)) ≡ F (K₀ a₀²)
    test₁₈ a₀¹ a₀² k₀a₀¹ FK₀a₀¹ K₀a₀¹≡K₀a₀² = reright K₀a₀¹≡K₀a₀² {!!}

    test₁₉ : ∀ {a₀¹ : A₀}
               {a₀² : A₀}
               {a₁a₀²-1 a₁a₀²-2 a₁a₀²-3 : A₁ a₀²}
               {a₁a₀²-2=a₁a₀²-3 : A₂ a₀² a₁a₀²-2 ≡ A₂ a₀² a₁a₀²-3}
               (R : ∀ (a₀²' : A₀) → A₂ a₀² a₁a₀²-1 ≡ A₂ a₀² a₁a₀²-2)
               (X : A₂ a₀² a₁a₀²-2 ≡ A₂ a₀² a₁a₀²-3)
               {ignore : Set}
             → A₂ a₀² a₁a₀²-1 ≡ A₂ a₀² a₁a₀²-3
    test₁₉ {a₀¹} {a₀²} {a₁a₀²-1} {a₁a₀²-2} {a₁a₀²-3} {a₁a₀²-2=a₁a₀²-3} R X = reright (R a₀¹) {!!}

    {- FAILS (correctly, though perhaps without the most comprehensible of error messages)
      test₂₀' : (f₁ : A₀) (f₂ : A₀) (A₀f₁≡A₀f₂ : A₁ f₁ ≡ A₁ f₂) (g₁ : A₁ f₁) → A₂ f₁ g₁
      test₂₀' f₁ f₂ A₀f₁≡A₀f₂ g₁ rewrite A₀f₁≡A₀f₂ = {!!}

      test₂₀ : (f₁ : A₀) (f₂ : A₀) (A₀f₁≡A₀f₂ : A₁ f₁ ≡ A₁ f₂) (g₁ : A₁ f₁) → A₂ f₁ g₁
      test₂₀ f₁ f₂ A₀f₁≡A₀f₂ g₁ = reright A₀f₁≡A₀f₂ {!!}
    -}

    test₂₀ : ∀ {a b : Level} {A : Set a} {x y : A} (x≡y : x ≡ y) → Set
    test₂₀ x≡y = reright x≡y {!!}

    test₂₁ : ∀ {a b : Level} {A : Set a} {x y : A} (B : Set b) (x≡y : x ≡ y) → Set
    test₂₁ B x≡y = reright x≡y {!!}

    test₂₂ : ∀ {a : Level} {A : Set a} {B : Set} {x : B} {y : B} (x≡y : x ≡ y) → Set
    test₂₂ x≡y = reright x≡y {!!}

    module _ (l : Level) where
      postulate P : Set

      test₂₃ : (p : P)
               (A : Set)
               (x y : A)
               (x≡y : x ≡ y)
               → Set
      test₂₃ _ _ _ _ x≡y = reright x≡y ?

  module Test₂ where
    record Map
             {K : Set}
             (V : K → Set)
             (Carrier : Nat → Set) {{isDecEquivalence/K : Eq K}} {{isDecEquivalence/V : (k : K) → Eq (V k)}} : Set₁ where
      field
        ∅ : Carrier 0
        _∉_ : ∀ {s} → K → Carrier s → Set
        ∅-is-empty : ∀ {𝑘} {∅ : Carrier 0} → 𝑘 ∉ ∅

      _∈_ : ∀ {s} → K → Carrier s → Set
      _∈_ k m = ¬ k ∉ m

      field
        get : ∀ {k : K} {s} {m : Carrier s} → k ∈ m → V k
        put : ∀ {k₀ : K} (v₀ : V k₀) {s₁} {m₁ : Carrier s₁} → k₀ ∉ m₁ → Σ _ λ (m₀ : Carrier (suc s₁)) → Σ _ λ (k₀∈m₀ : k₀ ∈ m₀) → get k₀∈m₀ ≡ v₀

    postulate
      A : Set

    V : A → Set
    V = λ _ → Nat

    postulate
      M : Nat → Set
      isDecEquivalence/A : Eq A
      isDecEquivalence/V : (a : A) → Eq (V a)

    postulate
      m : Map V M {{isDecEquivalence/A}} {{isDecEquivalence/V}}

    open Map m

    test₁ : (v : Nat) (k : A)
      → (k∈putkv∅ : k ∈ (fst $ put {k₀ = k} v {m₁ = ∅} ∅-is-empty))
      → Set
    test₁ v k k∈putkv∅ = let p = (put {k₀ = k} v {m₁ = ∅} ∅-is-empty) in let r = sym (snd $ snd p) in reright r {!!}

{- expected.out
?0 : b₀² ≡ b₀² → Set
?1 : (b : B₀) → b ≡ b
?2 : B₀ → B₀
?3 : B₀ → B₀
?4 : Y ≡ Y
?5 : A₂ 𝑨₀² a₁𝑨₀²
?6 : (a₁ : A₁ a₀²) → a₀² ≡ a₀² → F (A₂ a₀² a₁) → F (A₁ a₀²) ≡ A₂ a₀² a₁
?7 : A₂ a₀ a₁a₀²
?8 : F (A₁ a₀²) → F (A₁ a₀²) ≡ F (F (A₁ a₀²))
?9 : F (A₁ a₀²) → F (A₁ a₀²) ≡ F (F (A₁ a₀²))
?10 : C lzero (χ ⊔ β) (A₁ a₀²) →
Nat →
Σ Level
(λ γ → C lzero (χ ⊔ β) (A₁ a₀²) ≡ C γ (χ ⊔ β) (C lzero γ (A₁ a₀¹)))
?11 : F (A₁ a₀)
?12 : F (F (F (F (A₁ a₀))))
?13 : C lzero (l a₀¹ β) (A₁ a₀²) →
Σ Level
(λ γ →
   C lzero (l a₀¹ β) (A₁ a₀²) ≡ C γ (l a₀¹ β) (C lzero γ (A₁ a₀¹)))
?14 : K₀ a₀² → Set
?15 : K₀ a₀² → F (K₀ a₀²) → F (F (K₀ a₀²)) ≡ F (K₀ a₀²)
?16 : (A₀ → A₂ a₀² a₁a₀²-2 ≡ A₂ a₀² a₁a₀²-2) →
A₂ a₀² a₁a₀²-2 ≡ A₂ a₀² a₁a₀²-3
?17 : Set
?18 : Set
?19 : Set
?20 : Set
?21 : (k ∉ fst (put (get (fst (snd (put v ∅-is-empty)))) ∅-is-empty) →
 ⊥) →
Set
-}


module list-simplifier where

open import level
open import bool
open import functions
open import eq
open import empty
open import level
open import list
open import list-thms
open import nat
open import neq
open import product
open import product-thms

data 𝕃ʳ : Set → Set lone where
  _ʳ : {A : Set} → 𝕃 A → 𝕃ʳ A
  _++ʳ_ : {A : Set} → 𝕃ʳ A → 𝕃ʳ A → 𝕃ʳ A
  mapʳ : {A B : Set} → (A → B) → 𝕃ʳ A → 𝕃ʳ B
  _::ʳ_ : {A : Set} → A → 𝕃ʳ A → 𝕃ʳ A
  []ʳ : {A : Set} → 𝕃ʳ A

𝕃⟦_⟧ : {A : Set} → 𝕃ʳ A → 𝕃 A
𝕃⟦ l ʳ ⟧ = l
𝕃⟦ t1 ++ʳ t2 ⟧ = 𝕃⟦ t1 ⟧ ++ 𝕃⟦ t2 ⟧
𝕃⟦ mapʳ f t ⟧ = map f 𝕃⟦ t ⟧
𝕃⟦ x ::ʳ t ⟧ = x :: 𝕃⟦ t ⟧
𝕃⟦ []ʳ ⟧ = []

is-emptyʳ : {A : Set} → 𝕃ʳ A → 𝔹
is-emptyʳ []ʳ = tt
is-emptyʳ _ = ff

is-emptyʳ-≡ : {A : Set}(t : 𝕃ʳ A) → is-emptyʳ t ≡ tt → t ≡ []ʳ
is-emptyʳ-≡ []ʳ p = refl
is-emptyʳ-≡ (_ ++ʳ _) ()
is-emptyʳ-≡ (mapʳ _ _) ()
is-emptyʳ-≡ (_ ::ʳ _) ()
is-emptyʳ-≡ (_ ʳ) ()

𝕃ʳ-simp-step : {A : Set}(t : 𝕃ʳ A) → 𝕃ʳ A
𝕃ʳ-simp-step ((t1a ++ʳ t1b) ++ʳ t2) = t1a ++ʳ (t1b ++ʳ t2)
𝕃ʳ-simp-step ((x ::ʳ t1) ++ʳ t2) = x ::ʳ (t1 ++ʳ t2)
𝕃ʳ-simp-step ([]ʳ ++ʳ t2) = t2
𝕃ʳ-simp-step ((l ʳ) ++ʳ t2) =
  if is-emptyʳ t2 then l ʳ else ((l ʳ) ++ʳ t2)
𝕃ʳ-simp-step ((mapʳ f t1) ++ʳ t2) =
  if is-emptyʳ t2 then mapʳ f t1 else ((mapʳ f t1) ++ʳ t2)
𝕃ʳ-simp-step (mapʳ f (t1 ++ʳ t2)) = (mapʳ f t1) ++ʳ (mapʳ f t2)
𝕃ʳ-simp-step (mapʳ f (l ʳ)) = (map f l) ʳ
𝕃ʳ-simp-step (mapʳ f (mapʳ g t)) = mapʳ (f ∘ g) t
𝕃ʳ-simp-step (mapʳ f (x ::ʳ t)) = (f x) ::ʳ (mapʳ f t)
𝕃ʳ-simp-step (mapʳ f []ʳ) = []ʳ
𝕃ʳ-simp-step (l ʳ) = l ʳ
𝕃ʳ-simp-step (x ::ʳ t) = (x ::ʳ t)
𝕃ʳ-simp-step []ʳ = []ʳ

𝕃ʳ-simp-sdev : {A : Set}(t : 𝕃ʳ A) → 𝕃ʳ A
𝕃ʳ-simp-sdev (l ʳ) = (l ʳ)
𝕃ʳ-simp-sdev (t1 ++ʳ t2) = 𝕃ʳ-simp-step ((𝕃ʳ-simp-sdev t1) ++ʳ (𝕃ʳ-simp-sdev t2))
𝕃ʳ-simp-sdev (mapʳ f t1) = 𝕃ʳ-simp-step (mapʳ f (𝕃ʳ-simp-sdev t1))
𝕃ʳ-simp-sdev (x ::ʳ t1) = 𝕃ʳ-simp-step (x ::ʳ (𝕃ʳ-simp-sdev t1))
𝕃ʳ-simp-sdev []ʳ = []ʳ

𝕃ʳ-simp : {A : Set}(t : 𝕃ʳ A) → ℕ → 𝕃ʳ A
𝕃ʳ-simp t 0 = t
𝕃ʳ-simp t (suc n) = 𝕃ʳ-simp-sdev (𝕃ʳ-simp t n)

𝕃ʳ-simp-step-sound : {A : Set}(t : 𝕃ʳ A) → 𝕃⟦ t ⟧ ≡ 𝕃⟦ 𝕃ʳ-simp-step t ⟧
𝕃ʳ-simp-step-sound ((t1a ++ʳ t1b) ++ʳ t2) = ++-assoc 𝕃⟦ t1a ⟧ 𝕃⟦ t1b ⟧ 𝕃⟦ t2 ⟧
𝕃ʳ-simp-step-sound ((x ::ʳ t1) ++ʳ t2) = refl
𝕃ʳ-simp-step-sound ([]ʳ ++ʳ t2) = refl
𝕃ʳ-simp-step-sound ((l ʳ) ++ʳ t2) with keep (is-emptyʳ t2)
𝕃ʳ-simp-step-sound ((l ʳ) ++ʳ t2) | tt , p rewrite p | is-emptyʳ-≡ t2 p | ++[] l = refl
𝕃ʳ-simp-step-sound ((l ʳ) ++ʳ t2) | ff , p rewrite p = refl
𝕃ʳ-simp-step-sound ((mapʳ f t1) ++ʳ t2) with keep (is-emptyʳ t2)
𝕃ʳ-simp-step-sound ((mapʳ f t1) ++ʳ t2) | tt , p rewrite p | is-emptyʳ-≡ t2 p | ++[] (map f 𝕃⟦ t1 ⟧) = refl
𝕃ʳ-simp-step-sound ((mapʳ f t1) ++ʳ t2) | ff , p rewrite p = refl
𝕃ʳ-simp-step-sound (l ʳ) = refl
𝕃ʳ-simp-step-sound (mapʳ f (t1 ++ʳ t2)) = map-append f 𝕃⟦ t1 ⟧ 𝕃⟦ t2 ⟧
𝕃ʳ-simp-step-sound (mapʳ f (l ʳ)) = refl
𝕃ʳ-simp-step-sound (mapʳ f (mapʳ g t)) = map-compose f g 𝕃⟦ t ⟧
𝕃ʳ-simp-step-sound (mapʳ f (x ::ʳ t)) = refl
𝕃ʳ-simp-step-sound (mapʳ f []ʳ) = refl
𝕃ʳ-simp-step-sound (x ::ʳ t) = refl
𝕃ʳ-simp-step-sound []ʳ = refl

𝕃ʳ-simp-sdev-sound : {A : Set}(t : 𝕃ʳ A) → 𝕃⟦ t ⟧ ≡ 𝕃⟦ 𝕃ʳ-simp-sdev t ⟧
𝕃ʳ-simp-sdev-sound (l ʳ) = refl
𝕃ʳ-simp-sdev-sound (t1 ++ʳ t2)
  rewrite sym (𝕃ʳ-simp-step-sound ((𝕃ʳ-simp-sdev t1) ++ʳ (𝕃ʳ-simp-sdev t2))) | 𝕃ʳ-simp-sdev-sound t1 | 𝕃ʳ-simp-sdev-sound t2 = refl
𝕃ʳ-simp-sdev-sound (mapʳ f t1)
  rewrite sym (𝕃ʳ-simp-step-sound (mapʳ f (𝕃ʳ-simp-sdev t1))) | 𝕃ʳ-simp-sdev-sound t1 = refl
𝕃ʳ-simp-sdev-sound (x ::ʳ t1) rewrite 𝕃ʳ-simp-sdev-sound t1 = refl
𝕃ʳ-simp-sdev-sound []ʳ = refl

𝕃ʳ-simp-sound : {A : Set}(t : 𝕃ʳ A)(n : ℕ) → 𝕃⟦ t ⟧ ≡ 𝕃⟦ 𝕃ʳ-simp t n ⟧
𝕃ʳ-simp-sound t 0 = refl
𝕃ʳ-simp-sound t (suc n) rewrite sym (𝕃ʳ-simp-sdev-sound (𝕃ʳ-simp t n)) = 𝕃ʳ-simp-sound t n

module test1 {A B : Set}(f : A → B)(l1 l2 : 𝕃 A) where

  lhs = (mapʳ f (l1 ʳ)) ++ʳ (mapʳ f (l2 ʳ))

  rhs = mapʳ f ((l1 ʳ) ++ʳ (l2 ʳ))

  test-tp : Set
  test-tp = 𝕃⟦ lhs ⟧ ≡ 𝕃⟦ rhs ⟧

  test : test-tp
  test rewrite (𝕃ʳ-simp-sdev-sound rhs) = refl

module test2 {A B : Set}(f : A → B)(l1 l2 l3 : 𝕃 A) where

  lhs = mapʳ f (((l1 ʳ) ++ʳ (l2 ʳ)) ++ʳ (l3 ʳ))

  rhs = 𝕃ʳ-simp lhs 3

  test-tp : Set
  test-tp = 𝕃⟦ lhs ⟧ ≡ 𝕃⟦ rhs ⟧

  test : test-tp
  test = 𝕃ʳ-simp-sound lhs 3

  one-step : 𝕃ʳ B
  one-step = 𝕃ʳ-simp-step lhs

  sdev : 𝕃ʳ B
  sdev = 𝕃ʳ-simp-sdev lhs


module New.LangChanges where

open import New.Lang public
open import New.Changes

isChAlgτ : (τ : Type) → IsChAlg ⟦ τ ⟧Type ⟦ Δt τ ⟧Type

Chτ : (τ : Type) → Set
Chτ τ = ⟦ Δt τ ⟧Type

chAlgt : (τ : Type) → ChAlg ⟦ τ ⟧Type
chAlgt τ = record { Ch = Chτ τ ; isChAlg = isChAlgτ τ}

instance
  ichAlgt : ∀ {τ} → ChAlg ⟦ τ ⟧Type
  ichAlgt {τ} = chAlgt τ

isChAlgτ (σ ⇒ τ) = isChAlg {{funCA {{chAlgt σ}} {{chAlgt τ}}}}
isChAlgτ int = isChAlg {{intCA}}
isChAlgτ (pair σ τ) = isChAlg {{pairCA {{chAlgt σ}} {{chAlgt τ}}}}
isChAlgτ (sum σ τ) = isChAlg {{sumCA {{chAlgt σ}} {{chAlgt τ}}}}

ΔΓ : Context → Context
ΔΓ ∅ = ∅
ΔΓ (τ • Γ) = Δt τ • τ • ΔΓ Γ

module _ where
  ChΓ : ∀ (Γ : Context) → Set
  ChΓ Γ = ⟦ ΔΓ Γ ⟧Context

  _e⊕_ : ∀ {Γ} → ⟦ Γ ⟧Context → ChΓ Γ → ⟦ Γ ⟧Context
  _e⊕_ ∅ ∅ = ∅
  _e⊕_ (v • ρ) (dv • _ • dρ) = v ⊕ dv • ρ e⊕ dρ
  _e⊝_ : ∀ {Γ} → ⟦ Γ ⟧Context → ⟦ Γ ⟧Context → ChΓ Γ
  _e⊝_ ∅ ∅ = ∅
  _e⊝_ (v₂ • ρ₂) (v₁ • ρ₁) = v₂ ⊝ v₁ • v₁ • ρ₂ e⊝ ρ₁

  validΓ : ∀ {Γ} → ⟦ Γ ⟧Context → ChΓ Γ → Set
  validΓ ∅ ∅ = ⊤
  validΓ (v • ρ) (dv • v′ • dρ) = valid v dv × v ≡ v′ × validΓ ρ dρ

  e⊝-valid : ∀ {Γ} → (ρ1 ρ2 : ⟦ Γ ⟧Context) → validΓ ρ1 (ρ2 e⊝ ρ1)
  e⊝-valid ∅ ∅ = tt
  e⊝-valid (v₁ • ρ₁) (v₂ • ρ₂) = ⊝-valid v₁ v₂ , refl , e⊝-valid ρ₁ ρ₂
  e⊕-⊝ : ∀ {Γ} → (ρ2 ρ1 : ⟦ Γ ⟧Context) → ρ1 e⊕ (ρ2 e⊝ ρ1) ≡ ρ2
  e⊕-⊝ ∅ ∅ = refl
  e⊕-⊝ (v₂ • ρ₂) (v₁ • ρ₁) = cong₂ _•_ (⊕-⊝ v₂ v₁) (e⊕-⊝ ρ₂ ρ₁)

  {-# TERMINATING #-}
  isEnvCA : ∀ Γ → IsChAlg ⟦ Γ ⟧Context (ChΓ Γ)

  e⊚-valid : ∀ {Γ} → (ρ : ⟦ Γ ⟧Context) (dρ1 : ChΓ Γ) →
      validΓ ρ dρ1 →
      (dρ2 : ChΓ Γ) →
      validΓ (ρ e⊕ dρ1) dρ2 →
      validΓ ρ (IsChAlg.default-⊚ (isEnvCA Γ) dρ1 ρ dρ2)
  e⊚-correct : ∀ {Γ} → (ρ : ⟦ Γ ⟧Context) (dρ1 : ChΓ Γ) →
      validΓ ρ dρ1 →
      (dρ2 : ChΓ Γ) →
      validΓ (ρ e⊕ dρ1) dρ2 →
      (ρ e⊕ IsChAlg.default-⊚ (isEnvCA Γ) dρ1 ρ dρ2) ≡
      ((ρ e⊕ dρ1) e⊕ dρ2)

  isEnvCA Γ = record
    { _⊕_ = _e⊕_
    ; _⊝_ = _e⊝_
    ; valid = validΓ
    ; ⊝-valid = e⊝-valid
    ; ⊕-⊝ = e⊕-⊝
    ; _⊚[_]_ = IsChAlg.default-⊚ (isEnvCA Γ)
    ; ⊚-valid = e⊚-valid
    ; ⊚-correct = e⊚-correct
    }
  e⊚-valid {Γ} = IsChAlg.default-⊚-valid (isEnvCA Γ)
  e⊚-correct {Γ} = IsChAlg.default-⊚-correct (isEnvCA Γ)

  envCA : ∀ Γ → ChAlg ⟦ Γ ⟧Context
  envCA Γ = record
    { Ch = ChΓ Γ
    ; isChAlg = isEnvCA Γ }

instance
  ienvCA : ∀ {Γ} → ChAlg ⟦ Γ ⟧Context
  ienvCA {Γ} = envCA Γ


-- An implementation of Olivier Danvy's Type-Directed Partial Evaluation (POPL 1996)
-- for STLC with sum types using continuations in form of shift and reset.
--
-- The algorithm was originally for a two-level lambda-calculus.

-- Our use of Kripke semantics makes the effect of fresh variable generation explicit and formal.

open import Library

module DanvyShiftReset where

data Base : Set where
  α β : Base

-- import Formulas   ; open module Form = Formulas    Base
-- import Derivations; open module Der  = Derivations Base
open import Formulas    Base
open import Derivations Base

-- -- Shift-reset continutation monad

-- record M (A B C : Set) : Set where
--   field run : (C → B) → A
-- open M

-- return : ∀{X A} → A → M X X A
-- return x .run k = k x

-- _>>=_ : ∀{X Y Z A B} (m : M X Y A) (f : A → M Y Z B) → M X Z B
-- (m >>= f) .run k = m .run λ a → f a .run k

-- _<$>_ : ∀{X Y A B} (f : A → B) (m : M X Y A) → M X Y B
-- (f <$> m) .run k = m .run (k ∘ f)

-- shift : ∀{X Y A} (f : (A → Y) → M X Y Y) → M X Y A
-- shift f .run k = f k .run id

-- shift' : ∀{X Y A} (f : (A → Y) → X) → M X Y A
-- shift' f .run k = f k

-- reset : ∀{X Y A} (m : M A Y Y) → M X X A
-- reset m .run k = k (m .run id)

-- Shift-reset continutation monad

record M (A B C : Cxt → Set) (Γ : Cxt) : Set where
  field run : KFun (KFun C B) A Γ
open M

return : ∀{X A} → □ A →̇ M X X A
return x .run τ k = k id≤ (x τ)

return' : ∀{X A} → □ A →̇ M X X (□ A)
return' x .run τ k = k id≤ λ τ₁ → x (τ₁ • τ)

_>>=_ : ∀{X Y Z A B Γ} (m : M X Y A Γ) (f : KFun A (M Y Z B) Γ) → M X Z B Γ
(m >>= f) .run σ k = m .run σ λ τ a → f (τ • σ) a .run id≤ λ τ′ → k (τ′ • τ)

_<$>_ : ∀{X Y A B} (f : A →̇ B) → M X Y A →̇ M X Y B
(f <$> m) .run σ k = m .run σ λ τ → k τ ∘ f

-- liftA2 : ∀{X Y A B C} (f : ∀{Γ} → A Γ → B Γ → C Γ) → ∀{Γ} → M X Y A Γ → M X Y B Γ → M X Y C Γ
-- liftA2 f ma mb .run σ k = ma .run σ λ τ a → {!mb .run!}

K$ : ∀{X Y A B} → KFun A B →̇ KFun (M X Y A) (M X Y B)
K$ f τ m .run σ k = m .run σ λ τ′ a →  k τ′ (f (τ′ • (σ • τ)) a)

shift' : ∀{X Y A Γ} (f : KFun (KFun A Y) X Γ) → M X Y A Γ
shift' f .run σ k = f σ k

shift : ∀{X Y A Γ} (f : KFun (KFun A Y) (M X Y Y) Γ) → M X Y A Γ
shift f .run σ k = f σ k .run id≤ λ τ → id

reset' : ∀{Y A} → M A Y Y →̇ A
reset' m = m .run id≤ λ τ → id

reset : ∀{X Y A} → M A Y Y →̇ M X X A
reset m .run σ k = k id≤ (m .run σ λ τ → id)

-- We use a continuation monad with answer type Nf.

M' : (X : Cxt → Set) (Γ : Cxt) → Set
M' X Γ = ∀ {C} → M (Nf' C) (Nf' C) X Γ

T⟦_⟧ : (A : Form) (Γ : Cxt) → Set
T⟦ Atom P ⟧ = Nf' (Atom P)
T⟦ True   ⟧ Γ = ⊤
T⟦ False  ⟧ Γ = ⊥
T⟦ A ∨ B  ⟧ Γ = T⟦ A ⟧ Γ ⊎ T⟦ B ⟧ Γ
T⟦ A ∧ B  ⟧ Γ = T⟦ A ⟧ Γ × T⟦ B ⟧ Γ
T⟦ A ⇒ B  ⟧ Γ = ∀{Δ} (τ : Δ ≤ Γ) → T⟦ A ⟧ Δ → M' T⟦ B ⟧ Δ

-- Monotonicity of the model is proven by induction on the proposition.

-- monT : ∀ A {Γ Δ} (τ : Δ ≤ Γ) → T⟦ A ⟧ Γ → T⟦ A ⟧ Δ

monT : ∀ A → Mon T⟦ A ⟧
monT (Atom P) = monNf
monT True     = _
monT False  τ ()
monT (A ∨ B) τ = map-⊎ (monT A τ) (monT B τ)
monT (A ∧ B) τ (a , b) = monT A τ a , monT B τ b
monT (A ⇒ B) τ f σ = f (σ • τ)

-- Reflection / reification, proven simultaneously by induction on the proposition.

-- Reflection is η-expansion (and recursively reflection);

mutual

  reflect : ∀{Γ} A (t : Ne Γ A) → M' T⟦ A ⟧ Γ
  reflect (Atom P) t = return λ τ → ne (monNe τ t)
  reflect True     t = return _
  reflect False    t = shift' λ τ k → falseE (monNe τ t)
  reflect (A ∨ B)  t = shift' λ τ k → orE (monNe τ t)
    (reset' (K$ k (weak id≤) (inj₁ <$> reflect A (hyp top))))
    (reset' (K$ k (weak id≤) (inj₂ <$> reflect B (hyp top))))

    -- ((inj₂ <$> reflect B (hyp top)) .run λ τ → k (τ • weak id≤))
    -- Wrong:
    --     (reset' (k (weak id≤) <$> (inj₁ <$> reflect A (hyp top))))

  -- reflect (A ∧ B) t = do
  --   a ← reflect A (andE₁ t)
  --   b ← reflect B (andE₂ t)
  --   return (a , b)

  reflect (A ∧ B) t =
    reflect A (andE₁ t          ) >>= λ τ a →
    reflect B (andE₂ (monNe τ t)) >>= λ τ′ b →
    return λ τ₁ → monT A (τ₁ • τ′) a , monT B τ₁ b

  reflect (A ⇒ B)  t = return' λ τ a → reflect B (impE (monNe τ t) (reify A a))

  reify : ∀{Γ} A (⟦f⟧ : T⟦ A ⟧ Γ) → Nf Γ A

  reify (Atom P) t      = t
  reify True _          = trueI
  reify False   ()
  reify (A ∨ B) (inj₁ a) = orI₁ (reify A a)
  reify (A ∨ B) (inj₂ b) = orI₂ (reify B b)
  reify (A ∧ B) (a , b) = andI (reify A a) (reify B b)

  reify (A ⇒ B) f     = impI $ reset' $
    reflect A (hyp top) >>= λ τ a →
    reify B <$> f (τ • weak id≤) a

-- Fundamental theorem

-- Extension of T⟦_⟧ to contexts

G⟦_⟧ : ∀ (Γ Δ : Cxt) → Set
G⟦ ε     ⟧ Δ = ⊤
G⟦ Γ ∙ A ⟧ Δ = G⟦ Γ ⟧ Δ × T⟦ A ⟧ Δ

-- monG : ∀{Γ Δ Φ} (τ : Φ ≤ Δ) → G⟦ Γ ⟧ Δ → G⟦ Γ ⟧ Φ

monG : ∀{Γ} → Mon G⟦ Γ ⟧
monG {ε} τ _ = _
monG {Γ ∙ A} τ (γ , a) = monG τ γ , monT A τ a

-- Variable case.

fundH : ∀{Γ Δ A} (x : Hyp A Γ) (γ : G⟦ Γ ⟧ Δ) → T⟦ A ⟧ Δ
fundH top     = proj₂
fundH (pop x) = fundH x ∘ proj₁

-- The fundamental theorem:
-- A call-by-value interpreter.

fund :  ∀{Γ A} (t : Γ ⊢ A) {Δ} (γ : G⟦ Γ ⟧ Δ) → M' T⟦ A ⟧ Δ

fund (hyp {A} x) γ = return λ τ → monT A τ (fundH x γ)
fund (impI t)    γ = return' λ τ a → fund t (monG τ γ , a)

fund (impE t u)  γ =
  fund t γ          >>= λ τ f →
  fund u (monG τ γ) >>= λ τ′ a →
  f τ′ a

fund (andI {A} {B} t u) γ =
  fund t γ >>= λ τ a →
  fund u (monG τ γ) >>= λ τ′ b →
  return λ τ₁ → monT A (τ₁ • τ′) a , monT B τ₁ b

fund (andE₁ t)    γ = proj₁ <$> fund t γ
fund (andE₂ t)    γ = proj₂ <$> fund t γ
fund (orI₁ t)     γ = inj₁  <$> fund t γ
fund (orI₂ t)     γ = inj₂  <$> fund t γ

fund (orE t u v) γ = fund t γ >>= λ τ →
  [ (λ a → fund u (monG τ γ , a))
  , (λ b → fund v (monG τ γ , b))
  ]

fund (falseE t)  γ = fund t γ >>= λ τ ()
fund trueI       γ = return _

-- Identity environment

ide : ∀ Γ → □ (M' G⟦ Γ ⟧) Γ
ide ε       τ = return _
ide (Γ ∙ A) τ =
  ide Γ (τ • weak id≤)                >>= λ τ₁ γ →
  reflect A (monNe (τ₁ • τ) (hyp top)) >>= λ τ₂ a →
  return λ τ₃ → monG (τ₃ • τ₂) γ , monT A τ₃ a

-- Normalization

norm : ∀{A Γ} (t : Γ ⊢ A) → Nf Γ A
norm {A} {Γ} t = reset' $
  ide Γ id≤ >>= λ _ γ →
  reify A <$> fund t γ

idD : (A : Form) → ε ⊢ (A ⇒ A)
idD A = impI (hyp top)

test : let A = Atom α; B = Atom β in Nf ε (A ∨ B ⇒ A ∨ B)
test = norm (idD (Atom α ∨ Atom β))

test2 = norm (idD (Atom α ∨ Atom β ∨ Atom α))

-- Q.E.D. -}
-- -}
-- -}
-- -}
-- -}
-- -}
-- -}


------------------------------------------------------------------------------
-- Properties related with the forest type
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOTC.Program.Mirror.Forest.PropertiesI where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Base.List.PropertiesI
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesI
  using ( ++-leftCong
        ; ++-leftIdentity
        ; ++-rightCong
        ; mapCong₂
        ; revCong₁
        ; reverseCong
        ; reverse-[x]≡[x]
        )
open import FOTC.Program.Mirror.Forest.TotalityI
open import FOTC.Program.Mirror.Type

------------------------------------------------------------------------------

++-rightIdentity : ∀ {xs} → Forest xs → xs ++ [] ≡ xs
++-rightIdentity fnil                    = ++-leftIdentity []
++-rightIdentity (fcons {x} {xs} Tx Fxs) =
  (x ∷ xs) ++ []
     ≡⟨ ++-∷ x xs [] ⟩
  x ∷ (xs ++ [])
    ≡⟨ ∷-rightCong (++-rightIdentity Fxs) ⟩
  x ∷ xs ∎

++-assoc : ∀ {xs} → Forest xs → ∀ ys zs → (xs ++ ys) ++ zs ≡ xs ++ (ys ++ zs)
++-assoc fnil ys zs =
  ([] ++ ys) ++ zs
    ≡⟨ ++-leftCong (++-leftIdentity ys) ⟩
  ys ++ zs
     ≡⟨ sym (++-leftIdentity (ys ++ zs)) ⟩
  [] ++ ys ++ zs ∎

++-assoc (fcons {x} {xs} Tx Fxs) ys zs =
  ((x ∷ xs) ++ ys) ++ zs
    ≡⟨ ++-leftCong (++-∷ x xs ys) ⟩
  (x ∷ (xs ++ ys)) ++ zs
     ≡⟨ ++-∷ x (xs ++ ys) zs ⟩
  x ∷ ((xs ++ ys) ++ zs)
    ≡⟨ ∷-rightCong (++-assoc Fxs ys zs) ⟩
  x ∷ (xs ++ ys ++ zs)
    ≡⟨ sym (++-∷ x xs (ys ++ zs)) ⟩
  (x ∷ xs) ++ ys ++ zs ∎

map-++ : ∀ f {xs} → (∀ {x} → Tree x → Tree (f · x)) →
         Forest xs →
         ∀ ys → map f (xs ++ ys) ≡ map f xs ++ map f ys
map-++ f h fnil ys =
  map f ([] ++ ys)
    ≡⟨ mapCong₂ (++-leftIdentity ys) ⟩
  map f ys
    ≡⟨ sym (++-leftIdentity (map f ys)) ⟩
  [] ++ map f ys
     ≡⟨ ++-leftCong (sym (map-[] f)) ⟩
  map f [] ++ map f ys ∎

map-++ f h (fcons {x} {xs} Tx Fxs) ys =
  map f ((x ∷ xs) ++ ys)
    ≡⟨ mapCong₂ (++-∷ x xs ys) ⟩
  map f (x ∷ xs ++ ys)
    ≡⟨ map-∷ f x (xs ++ ys) ⟩
  f · x ∷ map f (xs ++ ys)
    ≡⟨ ∷-rightCong (map-++ f h Fxs ys) ⟩
  f · x ∷ (map f xs ++ map f ys)
    ≡⟨ sym (++-∷ (f · x) (map f xs) (map f ys)) ⟩
  (f · x ∷ map f xs) ++ map f ys
     ≡⟨ ++-leftCong (sym (map-∷ f x xs)) ⟩
  map f (x ∷ xs) ++ map f ys ∎

rev-++ : ∀ {xs} → Forest xs → ∀ ys → rev xs ys ≡ rev xs [] ++ ys
rev-++ fnil ys =
  rev [] ys       ≡⟨ rev-[] ys ⟩
  ys              ≡⟨ sym (++-leftIdentity ys) ⟩
  [] ++ ys        ≡⟨ ++-leftCong (sym (rev-[] [])) ⟩
  rev [] [] ++ ys ∎

rev-++ (fcons {x} {xs} Tx Fxs) ys =
  rev (x ∷ xs) ys
    ≡⟨ rev-∷ x xs ys ⟩
  rev xs (x ∷ ys)
    ≡⟨ rev-++ Fxs (x ∷ ys) ⟩
  rev xs [] ++ x ∷ ys
    ≡⟨ ++-rightCong prf ⟩
  rev xs [] ++ (x ∷ []) ++ ys
    ≡⟨ sym (++-assoc (rev-Forest Fxs fnil) (x ∷ []) ys) ⟩
  (rev xs [] ++ (x ∷ [])) ++ ys
    ≡⟨ ++-leftCong (sym (rev-++ Fxs (x ∷ []))) ⟩
  rev xs (x ∷ []) ++ ys
    ≡⟨ ++-leftCong (sym (rev-∷ x xs [])) ⟩
  rev (x ∷ xs) [] ++ ys ∎
    where prf : x ∷ ys ≡ (x ∷ []) ++ ys
          prf = x ∷ ys         ≡⟨ ∷-rightCong (sym (++-leftIdentity ys)) ⟩
                x ∷ ([] ++ ys) ≡⟨ sym (++-∷ x [] ys) ⟩
                (x ∷ []) ++ ys ∎

reverse-++ : ∀ {xs ys} → Forest xs → Forest ys →
             reverse (xs ++ ys) ≡ reverse ys ++ reverse xs
reverse-++ {ys = ys} fnil Fys =
  reverse ([] ++ ys)
    ≡⟨ reverseCong (++-leftIdentity ys) ⟩
  reverse ys
    ≡⟨ sym (++-rightIdentity (reverse-Forest Fys)) ⟩
  reverse ys ++ []
    ≡⟨ ++-rightCong (sym (rev-[] [])) ⟩
  reverse ys ++ reverse [] ∎

reverse-++ (fcons {x} {xs} Tx Fxs) fnil =
  reverse ((x ∷ xs) ++ [])
    ≡⟨ reverseCong (++-rightIdentity (fcons Tx Fxs)) ⟩
  reverse (x ∷ xs)
    ≡⟨ sym (++-leftIdentity (reverse (x ∷ xs))) ⟩
  [] ++ reverse (x ∷ xs)
     ≡⟨ ++-leftCong (sym (rev-[] [])) ⟩
  reverse [] ++ reverse (x ∷ xs) ∎

reverse-++ (fcons {x} {xs} Tx Fxs) (fcons {y} {ys} Ty Fys) =
  rev ((x ∷ xs) ++ y ∷ ys) []
    ≡⟨ revCong₁ (++-∷ x xs (y ∷ ys)) ⟩
  rev (x ∷ (xs ++ y ∷ ys)) []
    ≡⟨ rev-∷ x (xs ++ y ∷ ys) [] ⟩
  rev (xs ++ y ∷ ys) (x ∷ [])
    ≡⟨ rev-++ (++-Forest Fxs (fcons Ty Fys)) (x ∷ []) ⟩
  rev (xs ++ y ∷ ys) [] ++ (x ∷ [])
    ≡⟨ ++-leftCong refl ⟩
  reverse (xs ++ y ∷ ys) ++ (x ∷ [])
    ≡⟨ ++-leftCong (reverse-++ Fxs (fcons Ty Fys)) ⟩
  (reverse (y ∷ ys) ++ reverse xs) ++ x ∷ []
    ≡⟨ ++-assoc (reverse-Forest (fcons Ty Fys))
                (reverse xs)
                (x ∷ [])
    ⟩
  reverse (y ∷ ys) ++ rev xs [] ++ x ∷ []
    ≡⟨ ++-rightCong (sym (rev-++ Fxs (x ∷ []))) ⟩
  reverse (y ∷ ys) ++ rev xs (x ∷ [])
    ≡⟨ ++-rightCong (sym (rev-∷ x xs [])) ⟩
  reverse (y ∷ ys) ++ reverse (x ∷ xs) ∎

reverse-∷ : ∀ {x ys} → Tree x → Forest ys →
            reverse (x ∷ ys) ≡ reverse ys ++ (x ∷ [])
reverse-∷ {x} Tx fnil =
  rev (x ∷ []) []
    ≡⟨ rev-∷ x [] [] ⟩
  rev [] (x ∷ [])
    ≡⟨ rev-[] (x ∷ []) ⟩
  x ∷ []
    ≡⟨ sym (++-leftIdentity (x ∷ [])) ⟩
  [] ++ x ∷ []
     ≡⟨ ++-leftCong (sym (rev-[] [])) ⟩
  rev [] [] ++ x ∷ [] ∎

reverse-∷ {x} Tx (fcons {y} {ys} Ty Fys) = sym (
  reverse (y ∷ ys) ++ x ∷ []
    ≡⟨ ++-rightCong (sym (reverse-[x]≡[x] x)) ⟩
  (reverse (y ∷ ys) ++ reverse (x ∷ []))
    ≡⟨ sym (reverse-++ (fcons Tx fnil) (fcons Ty Fys)) ⟩
  reverse ((x ∷ []) ++ (y ∷ ys))
     ≡⟨ reverseCong (++-∷ x [] (y ∷ ys)) ⟩
   reverse (x ∷ ([] ++ (y ∷ ys)))
     ≡⟨ reverseCong (∷-rightCong (++-leftIdentity (y ∷ ys))) ⟩
   reverse (x ∷ y ∷ ys) ∎
  )


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

open import lib.Basics
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Unit
open import lib.types.Nat
open import lib.types.TLevel
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.NType2

open import lib.types.PathSeq

open import nicolai.pseudotruncations.Liblemmas
open import nicolai.pseudotruncations.SeqColim

open import nicolai.pseudotruncations.wconst-preparation

module nicolai.pseudotruncations.heptagon
  {i} {C : Sequence {i}} (wc : wconst-chain C) (a₀ : fst C O) where

  {- î-def is defined in this module: -}
  open wconst-init {i} {C} wc a₀

  module _ (n : ℕ) (a : A n) where

    {- Let us formulate different simplification steps of the
       heptagon. -}

    full-hepta : seq-loop (ins (S n) (f n a))
    full-hepta = (î-def (S n) (f n a)) ⋯ (‼ (î-def n a) ⋯ toSeq (glue n a))

    remove-g : seq-loop (ins (S n) (f n a))
    remove-g = (î-def (S n) (f n a))
               ⋯ ‼ (ins (S n) (f n a)
                      =⟪ ap (ins (S n)) (wc n _ _) ⟫
                    ins (S n) (lift-point C a₀ (S n))
                      =⟪ ! (lift-point-= C a₀ (S n)) ⟫
                    ins O a₀
                      ∎∎)

    simplify-‼ : seq-loop (ins (S n) (f n a))
    simplify-‼ = (î-def (S n) (f n a))
               ⋯ (ins O a₀
                    =⟪ lift-point-= C a₀ (S n) ⟫
                  ins (S n) (lift-point C a₀ (S n))
                    =⟪ ! (ap (ins (S n)) (wc n _ _)) ⟫
                  ins (S n) (f n a)
                    ∎∎)

    simplify-one-g : seq-loop (ins {C = C} (S n) (f n a))
    simplify-one-g =
                  ins (S n) (f n a)
                    =⟪ glue (S n) (f n a) ⟫
                  ins (S (S n)) (f (S n) (f n a))
                    =⟪ ap (ins (S (S n))) (wc (S n) (f n a) (lift-point C a₀ (S n))) ⟫
                  ins (S (S n)) (lift-point C a₀ (S (S n)))
                    =⟪ ! (glue (S n) (lift-point C a₀ (S n))) ⟫
                  ins (S n) (lift-point C a₀ (S n))
                    =⟪ ! (lift-point-= C a₀ (S n)) ⟫
                  ins O a₀
                    =⟪ lift-point-= C a₀ (S n) ⟫
                  ins (S n) (lift-point C a₀ (S n))
                    =⟪ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n))) ⟫
                  ins (S n) (f n a)
                    ∎∎

    simplify-many-g : seq-loop (ins {C = C} (S n) (f n a))
    simplify-many-g =
                  ins (S n) (f n a)
                    =⟪ glue (S n) (f n a) ⟫
                  ins (S (S n)) (f (S n) (f n a))
                    =⟪ ap (ins (S (S n))) (wc (S n) (f n a) (lift-point C a₀ (S n))) ⟫
                  ins (S (S n)) (lift-point C a₀ (S (S n)))
                    =⟪ ! (glue (S n) (lift-point C a₀ (S n))) ⟫
                  ins (S n) (lift-point C a₀ (S n))
                    =⟪ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n))) ⟫
                  ins (S n) (f n a)
                    ∎∎

    replace-ap-SSn : seq-loop (ins {C = C} (S n) (f n a))
    replace-ap-SSn =
                  ins (S n) (f n a)
                    =⟪ glue (S n) (f n a) ⟫
                  ins (S (S n)) (f (S n) (f n a))
                    =⟪ ap (ins (S (S n))) (ap (f (S n)) (wc n a (lift-point C a₀ n))) ⟫
                  ins (S (S n)) (lift-point C a₀ (S (S n)))
                    =⟪ ! (glue (S n) (lift-point C a₀ (S n))) ⟫
                  ins (S n) (lift-point C a₀ (S n))
                    =⟪ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n))) ⟫
                  ins (S n) (f n a)
                    ∎∎

    {-
      Now, let us generalize:
        x   for   f n a
        y   for   lift-point C a₀ (S n)
        q   for   wc n a (lift C a₀ n)
      Then, we can show that the square commutes by induction on q.
    -}
    generalize-square : (x y : A (S n)) (q : x == y)
              → (↯
                  ins {C = C} (S n) x
                    =⟪ glue (S n) x ⟫
                  ins (S (S n)) (f (S n) x)
                    =⟪ ap (ins (S (S n))) (ap (f (S n)) q) ⟫
                  ins (S (S n)) (f (S n) y)
                    =⟪ ! (glue (S n) y) ⟫
                  ins (S n) y
                    =⟪ ! (ap (ins (S n)) q) ⟫
                  ins (S n) x
                    ∎∎
               ) == idp
    generalize-square x .x idp =
                  glue (S n) x ∙ ! (glue (S n) x) ∙ idp
                    =⟨ ap (λ q → (glue (S n) x) ∙ q) (∙-unit-r _) ⟩
                  glue (S n) x ∙ ! (glue (S n) x)
                    =⟨ !-inv-r (glue (S n) x) ⟩
                  idp
                    ∎

    {- Let us use some ad-hoc lemmas; these could all be done via
       very tedious concatenations of library lemmas.
       Of course, our ad-hoc way is somewhat ugly, but it saves a
       lot of work compared to using concatenations and nestings of
       library lemmas. The strategy is to formulate the equality
       that we need and solve it by trivial path induction.

       This ad-hoc approach is surely not the best way one can
       think of.
       The best way to do it would probably be a more principled
       approach with more powerful high-level lemmas that allow the
       manipulation of paths; however, such a technology is not
       implemented so far. -}
    adhoc-!-∙ : ∀ {i} {X : Type i} {x y z u v w o : X}
                         (p : x == y) (q : y == z) (r : u == z)
                         (s : v == u) (t : v == w) (l : w == o)
                         → (↯ x =⟪ p ⟫ y =⟪ q ⟫ z =⟪ ! (s ∙ r) ⟫ v =⟪ t ⟫ w =⟪ l ⟫ o ∎∎ )
                           ==
                           (↯ x =⟪ p ⟫ y =⟪ q ⟫ z =⟪ ! r ⟫ u =⟪ ! s ⟫ v =⟪ t ⟫ w =⟪ l ⟫ o ∎∎)
    adhoc-!-∙ idp idp idp idp idp idp = idp

    adhoc-cancel-inv : ∀ {i} {X : Type i} {x y z u v w : X}
                         (p : x == y) (q : y == z) (r : z == u)
                         (s : v == u) (t : u == w)
                         → (↯ x =⟪ p ⟫ y =⟪ q ⟫ z =⟪ r ⟫ u =⟪ ! s ⟫ v =⟪ s ⟫ u =⟪ t ⟫ w ∎∎ )
                           ==
                           (↯ x =⟪ p ⟫ y =⟪ q ⟫ z =⟪ r ⟫ u =⟪ t ⟫ w ∎∎)
    adhoc-cancel-inv idp idp idp idp idp = idp

    {- Now, we are ready to show that the heptagon is trivial! -}
    ĝ-def :  (↯ full-hepta) == idp
    ĝ-def =

      (↯ full-hepta)

        =⟨ ap (λ q → (glue (S n) (f n a))
                   ∙ ap (ins (S (S n))) (wc (S n) (f n a) (snd C n (lift-point C a₀ n)))
                   ∙ ! ((lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
                        ∙ glue (S n) (snd C n (lift-point C a₀ n)))
                   ∙ ! (! (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n)))
                   ∙ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n)))
                   ∙ q )
              (!-inv-l (glue n a)) ⟩

      (↯ (remove-g ⋯ (toSeq idp)))

        =⟨ ap (λ q → (glue (S n) (f n a))
                    ∙ ap (ins (S (S n))) (wc (S n) (f n a) (snd C n (lift-point C a₀ n)))
                    ∙ ! ((lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
                    ∙ glue (S n) (snd C n (lift-point C a₀ n)))
                    ∙ ! (! (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n)))
                    ∙ q )
              (∙-unit-r (! (ap (ins (S n)) (wc n a (lift-point C a₀ n))))) ⟩
      (↯ remove-g)

        =⟨ ap (λ q → (glue (S n) (f n a))
                    ∙ ap (ins (S (S n))) (wc (S n) (f n a) (snd C n (lift-point C a₀ n)))
                    ∙ ! ((lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
                    ∙ glue (S n) (snd C n (lift-point C a₀ n)))
                    ∙ q
                    ∙ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n)))
               )
               (!-! (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))) ⟩

      (↯ simplify-‼)

        =⟨ adhoc-!-∙
              (glue (S n) (f n a))
              (ap (ins (S (S n))) (wc (S n) (f n a) (snd C n (lift-point C a₀ n))))
              (glue (S n) (snd C n (lift-point C a₀ n)))
              (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
              (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
              (! (ap (ins (S n)) (wc n a (lift-point C a₀ n)))) ⟩
      (↯ simplify-one-g)
        =⟨ adhoc-cancel-inv
             (glue (S n) (f n a))
             (ap (ins (S (S n))) (wc (S n) (f n a) (snd C n (lift-point C a₀ n))))
             (! (glue (S n) (snd C n (lift-point C a₀ n))))
             (lift-point-= C a₀ n ∙ glue n (lift-point C a₀ n))
             (! (ap (ins (S n)) (wc n a (lift-point C a₀ n)))) ⟩

      (↯ simplify-many-g)

        =⟨ ap (λ q → (glue (S n) (f n a))
                    ∙ q
                    ∙ ! (glue (S n) (snd C n (lift-point C a₀ n)))
                    ∙ ! (ap (ins (S n)) (wc n a (lift-point C a₀ n)))
               )
               (ap-wconst (ins (S (S n)))
                          (ins-wconst (S (S n)))
                          (wc (S n) (f n a) (snd C n (lift-point C a₀ n)))
                          (ap (f (S n)) (wc n a (lift-point C a₀ n)))
               ) ⟩

      (↯ replace-ap-SSn)

        =⟨ generalize-square (f n a) (lift-point C a₀ (S n)) (wc n a (lift-point C a₀ n)) ⟩

      idp
        ∎


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

module Definition.Typed.EqRelInstance where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.Typed.Reduction
open import Definition.Typed.EqualityRelation

open import Tools.Function

-- Judgmental instance of the equality relation

instance eqRelInstance : EqRelSet
eqRelInstance = record {
  _⊢_≅_ = _⊢_≡_;
  _⊢_≅_∷_ = _⊢_≡_∷_;
  _⊢_~_∷_ = _⊢_≡_∷_;
  ~-to-≅ₜ = idᶠ;
  ≅-eq = idᶠ;
  ≅ₜ-eq = idᶠ;
  ≅-univ = univ;
  ≅-sym = sym;
  ≅ₜ-sym = sym;
  ~-sym = sym;
  ≅-trans = trans;
  ≅ₜ-trans = trans;
  ~-trans = trans;
  ≅-conv = conv;
  ~-conv = conv;
  ≅-wk = wkEq;
  ≅ₜ-wk = wkEqTerm;
  ~-wk = wkEqTerm;
  ≅-red = reduction;
  ≅ₜ-red = reductionₜ;
  ≅-Urefl = refl ∘ᶠ Uⱼ;
  ≅-ℕrefl = refl ∘ᶠ ℕⱼ;
  ≅ₜ-ℕrefl = refl ∘ᶠ ℕⱼ;
  ≅-Emptyrefl = refl ∘ᶠ Emptyⱼ;
  ≅ₜ-Emptyrefl = refl ∘ᶠ Emptyⱼ;
  ≅-Unitrefl = refl ∘ᶠ Unitⱼ;
  ≅ₜ-Unitrefl = refl ∘ᶠ Unitⱼ;
  ≅ₜ-η-unit = η-unit;
  ≅-Π-cong = Π-cong;
  ≅ₜ-Π-cong = Π-cong;
  ≅-Σ-cong = Σ-cong;
  ≅ₜ-Σ-cong = Σ-cong;
  ≅ₜ-zerorefl = refl ∘ᶠ zeroⱼ;
  ≅-suc-cong = suc-cong;
  ≅-η-eq = λ x x₁ x₂ x₃ x₄ x₅ → η-eq x x₁ x₂ x₅;
  ≅-Σ-η = λ ⊢F ⊢G ⊢p ⊢r pProd rProd fst≡ snd≡ → Σ-η ⊢F ⊢G ⊢p ⊢r fst≡ snd≡;
  ~-var = refl;
  ~-app = app-cong;
  ~-fst = fst-cong;
  ~-snd = snd-cong;
  ~-natrec = natrec-cong;
  ~-Emptyrec = Emptyrec-cong }


module _ where

open import Common.Prelude
open import Common.String

record IsString {a} (A : Set a) : Set a where
  field
    fromString : String → A

open IsString {{...}} public
{-# BUILTIN FROMSTRING fromString #-}

instance
  StringIsString : IsString String
  StringIsString = record { fromString = λ s → s }

  ListIsString : IsString (List Char)
  ListIsString = record { fromString = stringToList }

foo : List Char
foo = "foo"

open import Common.Equality

thm : "foo" ≡ 'f' ∷ 'o' ∷ 'o' ∷ []
thm = refl


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

module Cubical.Data.FinSet.Binary.Large where

open import Cubical.Functions.Embedding
open import Cubical.Functions.Involution

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

open import Cubical.Data.Bool
open import Cubical.Data.Sigma

open import Cubical.HITs.PropositionalTruncation

private
  variable
    ℓ : Level

isBinary : Type ℓ → Type ℓ
isBinary B = ∥ Bool ≃ B ∥

Binary : ∀ ℓ → Type _
Binary ℓ = Σ (Type ℓ) isBinary

isBinary→isSet : ∀{B : Type ℓ} → isBinary B → isSet B
isBinary→isSet {B} = rec isPropIsSet λ eqv → isOfHLevelRespectEquiv 2 eqv isSetBool

private
  Σ≡Prop²
    : ∀{ℓ ℓ'} {A : Type ℓ} {B : A → Type ℓ'}
    → {w x : Σ A B}
    → isOfHLevelDep 1 B
    → (p q : w ≡ x)
    → cong fst p ≡ cong fst q
    → p ≡ q
  Σ≡Prop² _ _ _ r i j .fst = r i j
  Σ≡Prop² {B = B} {w} {x} Bprp p q r i j .snd
    = isPropDep→isSetDep Bprp (w .snd) (x .snd) (cong snd p) (cong snd q) r i j

BinaryEmbedding : isEmbedding (λ(B : Binary ℓ) → map-snd isBinary→isSet B)
BinaryEmbedding w x = isoToIsEquiv theIso
  where
  open Iso
  f = map-snd isBinary→isSet

  theIso : Iso (w ≡ x) (f w ≡ f x)
  theIso .fun = cong f
  theIso .inv p i .fst = p i .fst
  theIso .inv p i .snd
    = ∥∥-isPropDep (Bool ≃_) (w .snd) (x .snd) (λ i → p i .fst) i
  theIso .rightInv p
    = Σ≡Prop² (isOfHLevel→isOfHLevelDep 1 (λ _ → isPropIsSet)) _ p refl
  theIso .leftInv p
    = Σ≡Prop² (∥∥-isPropDep (Bool ≃_)) _ p refl

Base : Binary _
Base .fst = Bool
Base .snd = ∣ idEquiv Bool ∣

Loop : Base ≡ Base
Loop i .fst = notEq i
Loop i .snd = ∥∥-isPropDep (Bool ≃_) (Base .snd) (Base .snd) notEq i

private
  notEq² : Square notEq refl refl notEq
  notEq² = involPath² {f = not} notnot

Loop² : Square Loop refl refl Loop
Loop² i j .fst = notEq² i j
Loop² i j .snd
  = isPropDep→isSetDep' (∥∥-isPropDep (Bool ≃_))
      notEq² (cong snd Loop) refl refl (cong snd Loop) i j

isGroupoidBinary : isGroupoid (Binary ℓ)
isGroupoidBinary
  = Embedding-into-hLevel→hLevel 2
      (map-snd isBinary→isSet , BinaryEmbedding)
      (isOfHLevelTypeOfHLevel 2)

record BinStructure (B : Type ℓ) : Type ℓ where
  field
    base : B
    loop : base ≡ base
    loop² : Square loop refl refl loop
    trunc : isGroupoid B

structure₀ : BinStructure (Binary ℓ-zero)
structure₀ .BinStructure.base = Base
structure₀ .BinStructure.loop = Loop
structure₀ .BinStructure.loop² = Loop²
structure₀ .BinStructure.trunc = isGroupoidBinary

module Parameterized (B : Type ℓ) where
  Baseᴾ : Bool ≃ B → Binary ℓ
  Baseᴾ P = B , ∣ P ∣

  Loopᴾ : (P Q : Bool ≃ B) → Baseᴾ P ≡ Baseᴾ Q
  Loopᴾ P Q i = λ where
      .fst → ua first i
      .snd → ∥∥-isPropDep (Bool ≃_) ∣ P ∣ ∣ Q ∣ (ua first) i
    where
    first : B ≃ B
    first = compEquiv (invEquiv P) Q

  Loopᴾ² : (P Q R : Bool ≃ B) → Square (Loopᴾ P Q) (Loopᴾ P R) refl (Loopᴾ Q R)
  Loopᴾ² P Q R i = Σ≡Prop (λ _ → squash) (S i)
    where
    PQ : B ≃ B
    PQ = compEquiv (invEquiv P) Q
    PR : B ≃ B
    PR = compEquiv (invEquiv P) R
    QR : B ≃ B
    QR = compEquiv (invEquiv Q) R
    Q-Q : Bool ≃ Bool
    Q-Q = compEquiv Q (invEquiv Q)

    PQRE : compEquiv PQ QR ≡ PR
    PQRE = compEquiv PQ QR
             ≡[ i ]⟨ compEquiv-assoc (invEquiv P) Q QR (~ i) ⟩
           compEquiv (invEquiv P) (compEquiv Q QR)
             ≡[ i ]⟨ compEquiv (invEquiv P) (compEquiv-assoc Q (invEquiv Q) R i) ⟩
           compEquiv (invEquiv P) (compEquiv Q-Q R)
             ≡[ i ]⟨ compEquiv (invEquiv P) (compEquiv (invEquiv-is-rinv Q i) R) ⟩
           compEquiv (invEquiv P) (compEquiv (idEquiv _) R)
             ≡[ i ]⟨ compEquiv (invEquiv P) (compEquivIdEquiv R i) ⟩
           PR ∎

    PQR : ua PQ ∙ ua QR ≡ ua PR
    PQR = ua PQ ∙ ua QR
            ≡[ i ]⟨ uaCompEquiv PQ QR (~ i) ⟩
          ua (compEquiv PQ QR)
            ≡⟨ cong ua PQRE ⟩
          ua PR ∎

    S : Square (ua PQ) (ua PR) refl (ua QR)
    S i j
      = hcomp (λ k → λ where
            (j = i0) → B
            (i = i0) → compPath-filler (ua PQ) (ua QR) (~ k) j
            (i = i1) → ua PR j
            (j = i1) → ua QR (i ∨ ~ k))
          (PQR i j)


-- Andreas, 2017-01-13, regression introduced by the fix of #1899
-- test case and report by Nisse

-- {-# OPTIONS -v term:40 #-}

open import Agda.Builtin.Size

mutual

  D : Size → Set
  D i = D′ i

  record D′ (i : Size) : Set where
    coinductive
    field
      force : {j : Size< i} → D j

test : D′ ω
D′.force test = test

-- Same without sizes (they are not used)

mutual
  E = E'
  record E' : Set where
    coinductive
    field force : E

testE : E'
E'.force testE = testE

-- Both should succeed, no termination errors.


module BasicIS4.Metatheory.Hilbert-TarskiClosedOvergluedImplicit where

open import BasicIS4.Syntax.Hilbert public
open import BasicIS4.Semantics.TarskiClosedOvergluedImplicit public

open ImplicitSyntax (∅ ⊢_) public

-- Completeness with respect to a particular model, for closed terms only.

module _ {{_ : Model}} where
  reify : ∀ {A} → ⊩ A → ∅ ⊢ A
  reify {α P}   s = syn s
  reify {A ▻ B} s = syn s
  reify {□ A}   s = syn s
  reify {A ∧ B} s = pair (reify (π₁ s)) (reify (π₂ s))
  reify {⊤}    s = unit

-- Additional useful equipment.

module _ {{_ : Model}} where
  ⟪K⟫ : ∀ {A B} → ⊩ A → ⊩ B ▻ A
  ⟪K⟫ a = app ck (reify a) ⅋ K a

  ⟪S⟫′ : ∀ {A B C} → ⊩ A ▻ B ▻ C → ⊩ (A ▻ B) ▻ A ▻ C
  ⟪S⟫′ s₁ = app cs (reify s₁) ⅋ λ s₂ →
              app (app cs (reify s₁)) (reify s₂) ⅋ ⟪S⟫ s₁ s₂

  _⟪D⟫_ : ∀ {A B} → ⊩ □ (A ▻ B) → ⊩ □ A → ⊩ □ B
  (t ⅋ s) ⟪D⟫ (u ⅋ a) = app (app cdist t) u ⅋ s ⟪$⟫ a

  _⟪D⟫′_ : ∀ {A B} → ⊩ □ (A ▻ B) → ⊩ □ A ▻ □ B
  _⟪D⟫′_ s = app cdist (reify s) ⅋ _⟪D⟫_ s

  ⟪↑⟫ : ∀ {A} → ⊩ □ A → ⊩ □ □ A
  ⟪↑⟫ s = box (syn s) ⅋ s

  _⟪,⟫′_ : ∀ {A B} → ⊩ A → ⊩ B ▻ A ∧ B
  _⟪,⟫′_ a = app cpair (reify a) ⅋ _,_ a

-- Additional useful equipment, for sequents.

module _ {{_ : Model}} where
  _⟦D⟧_ : ∀ {A B Γ} → ⊩ Γ ⇒ □ (A ▻ B) → ⊩ Γ ⇒ □ A → ⊩ Γ ⇒ □ B
  (s₁ ⟦D⟧ s₂) γ = (s₁ γ) ⟪D⟫ (s₂ γ)

  ⟦↑⟧ : ∀ {A Γ} → ⊩ Γ ⇒ □ A → ⊩ Γ ⇒ □ □ A
  ⟦↑⟧ s γ = ⟪↑⟫ (s γ)

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

eval : ∀ {A Γ} → Γ ⊢ A → Γ ⊨ A
eval (var i)   γ = lookup i γ
eval (app t u) γ = eval t γ ⟪$⟫ eval u γ
eval ci        γ = ci ⅋ I
eval ck        γ = ck ⅋ ⟪K⟫
eval cs        γ = cs ⅋ ⟪S⟫′
eval (box t)   γ = box t ⅋ eval t ∙
eval cdist     γ = cdist ⅋ _⟪D⟫′_
eval cup       γ = cup ⅋ ⟪↑⟫
eval cdown     γ = cdown ⅋ ⟪↓⟫
eval cpair     γ = cpair ⅋ _⟪,⟫′_
eval cfst      γ = cfst ⅋ π₁
eval csnd      γ = csnd ⅋ π₂
eval unit      γ = ∙

-- Correctness of evaluation with respect to conversion.

eval✓ : ∀ {{_ : Model}} {A Γ} {t t′ : Γ ⊢ A} → t ⋙ t′ → eval t ≡ eval t′
eval✓ refl⋙                   = refl
eval✓ (trans⋙ p q)            = trans (eval✓ p) (eval✓ q)
eval✓ (sym⋙ p)                = sym (eval✓ p)
eval✓ (congapp⋙ p q)          = cong² _⟦$⟧_ (eval✓ p) (eval✓ q)
eval✓ (congi⋙ p)              = cong I (eval✓ p)
eval✓ (congk⋙ p q)            = cong² K (eval✓ p) (eval✓ q)
eval✓ (congs⋙ p q r)          = cong³ ⟦S⟧ (eval✓ p) (eval✓ q) (eval✓ r)
eval✓ (congdist⋙ p q)         = cong² _⟦D⟧_ (eval✓ p) (eval✓ q)
eval✓ (congup⋙ p)             = cong ⟦↑⟧ (eval✓ p)
eval✓ (congdown⋙ p)           = cong ⟦↓⟧ (eval✓ p)
eval✓ (congpair⋙ {A} {B} p q) = cong² (_⟦,⟧_ {A} {B}) (eval✓ p) (eval✓ q)
eval✓ (congfst⋙ {A} {B} p)    = cong (⟦π₁⟧ {A} {B}) (eval✓ p)
eval✓ (congsnd⋙ {A} {B} p)    = cong (⟦π₂⟧ {A} {B}) (eval✓ p)
eval✓ beta▻ₖ⋙                 = refl
eval✓ beta▻ₛ⋙                 = refl
eval✓ beta∧₁⋙                 = refl
eval✓ beta∧₂⋙                 = refl
eval✓ eta∧⋙                   = refl
eval✓ eta⊤⋙                  = refl

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { ⊩ᵅ_ = λ P → ∅ ⊢ α P
      }

-- Completeness with respect to all models, or quotation, for closed terms only.

quot₀ : ∀ {A} → ∅ ⊨ A → ∅ ⊢ A
quot₀ t = reify (t ∙)

-- Normalisation by evaluation, for closed terms only.

norm₀ : ∀ {A} → ∅ ⊢ A → ∅ ⊢ A
norm₀ = quot₀ ∘ eval

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


open import Data.Empty
open import Data.Maybe
open import Data.Product
open import Data.Unit

open import Axiom.Extensionality.Propositional
open import Relation.Binary.PropositionalEquality hiding ([_])
open import Relation.Nullary
open import Relation.Nullary.Negation

open import EffectAnnotations
open import Types

module AEff where

-- ARITY ASSIGNMENT TO SIGNATURES OF SIGNALS, INTERRUPTS, AND BASE CONSTANTS

postulate payload : Σₛ → Σ VType (λ X → mobile X)     -- payload type assignment for signal and interrupt names

postulate Σ-base : Set             -- set of base constants
postulate ar-base : Σ-base → BType -- arity assignment to base constants

-- CONTEXTS

data Ctx : Set where
  []  : Ctx
  _∷_ : Ctx → VType → Ctx
  _■  : Ctx → Ctx

infixl 35 _∷_
infixl 35 _■

_++ₖ_ : Ctx → Ctx → Ctx
Γ ++ₖ [] = Γ
Γ ++ₖ (Γ' ∷ X) = (Γ ++ₖ Γ') ∷ X
Γ ++ₖ (Γ' ■) = (Γ ++ₖ Γ') ■

infixl 30 _++ₖ_

-- VARIABLES IN CONTEXTS (I.E., DE BRUIJN INDICES)

data _∈_ (X : VType) : Ctx → Set where
  Hd   : {Γ : Ctx} → X ∈ (Γ ∷ X)
  Tl-v : {Γ : Ctx} {Y : VType} → X ∈ Γ → X ∈ (Γ ∷ Y)
  Tl-■ : {Γ : Ctx} → mobile X → X ∈ Γ → X ∈ (Γ ■)

-- DERIVATIONS OF WELL-TYPED TERMS

mutual

  data _⊢V⦂_ (Γ : Ctx) : VType → Set where

    `_  : {X : VType} →
          X ∈ Γ →
          -------------
          Γ ⊢V⦂ X

    ´_  : (c : Σ-base) →
          --------------
          Γ ⊢V⦂ ``(ar-base c)

    ⋆   : Γ ⊢V⦂ 𝟙

    ƛ   : {X : VType}
          {C : CType} →
          Γ ∷ X ⊢C⦂ C →
          -------------
          Γ ⊢V⦂ X ⇒ C

    ⟨_⟩ : {X : VType} →
          Γ ⊢V⦂ X →
          -------------
          Γ ⊢V⦂ ⟨ X ⟩

    □   : {X : VType} →
          Γ ■ ⊢V⦂ X →
          -------------
          Γ ⊢V⦂ □ X

  infix 40 _·_

  data _⊢C⦂_ (Γ : Ctx) : CType → Set where

    return           : {X : VType}
                       {o : O}
                       {i : I} →
                       Γ ⊢V⦂ X →
                       -----------------
                       Γ ⊢C⦂ X ! (o , i)

    let=_`in_        : {X Y : VType}
                       {o : O}
                       {i : I} →
                       Γ ⊢C⦂ X ! (o , i) →
                       Γ ∷ X ⊢C⦂ Y ! (o , i) →
                       -----------------------
                       Γ ⊢C⦂ Y ! (o , i)

    _·_              : {X : VType}
                       {C : CType} →
                       Γ ⊢V⦂ X ⇒ C →
                       Γ ⊢V⦂ X →
                       -------------
                       Γ ⊢C⦂ C

    ↑                : {X : VType}
                       {o : O}
                       {i : I} →
                       (op : Σₛ) →
                       op ∈ₒ o →
                       Γ ⊢V⦂ proj₁ (payload op) →
                       Γ ⊢C⦂ X ! (o , i) →
                       --------------------------
                       Γ ⊢C⦂ X ! (o , i)

    ↓                : {X : VType}
                       {o : O}
                       {i : I}
                       (op : Σₛ) →
                       Γ ⊢V⦂ proj₁ (payload op) →
                       Γ ⊢C⦂ X ! (o , i) →
                       --------------------------
                       Γ ⊢C⦂ X ! op ↓ₑ (o , i)

    promise_∣_↦_`in_ : {X Y : VType}
                       {o o' : O}
                       {i i' : I} →
                       (op : Σₛ) →
                       (o' , i') ⊑ lkpᵢ op i  →
                       Γ ∷ proj₁ (payload op) ∷ (𝟙 ⇒ (⟨ X ⟩ ! (∅ₒ , ∅ᵢ [ op ↦ just (o' , i') ]ᵢ))) ⊢C⦂ ⟨ X ⟩ ! (o' , i') →
                       Γ ∷ ⟨ X ⟩ ⊢C⦂ Y ! (o , i) →
                       ---------------------------------------------------------------------------------------------------
                       Γ ⊢C⦂ Y ! (o , i)

    await_until_     : {X : VType}
                       {C : CType} →
                       Γ ⊢V⦂ ⟨ X ⟩ →
                       Γ ∷ X ⊢C⦂ C →
                       --------------
                       Γ ⊢C⦂ C

    unbox_`in_       : {X : VType}
                       {C : CType} →
                       Γ ⊢V⦂ □ X →
                       Γ ∷ X ⊢C⦂ C →
                       -------------
                       Γ ⊢C⦂ C

    spawn            : {C D : CType} →
                       Γ ■ ⊢C⦂ C →
                       Γ ⊢C⦂ D →
                       ---------------------
                       Γ ⊢C⦂ D

    coerce           : {X : VType}
                       {o o' : O}
                       {i i' : I} →
                       o ⊑ₒ o' →
                       i ⊑ᵢ i' →
                       Γ ⊢C⦂ X ! (o , i) →
                       -------------------
                       Γ ⊢C⦂ X ! (o' , i')

-- DERIVATIONS OF WELL-TYPED PROCESSES

infix 10 _⊢P⦂_

data _⊢P⦂_ (Γ : Ctx) : {o : O} → PType o → Set where

  run     : {X : VType}
            {o : O}
            {i : I} →
            Γ ⊢C⦂ X ! (o , i) →
            -------------------
            Γ ⊢P⦂ X ‼ o , i

  _∥_     : {o o' : O}
            {PP : PType o} →
            {QQ : PType o'} →
            Γ ⊢P⦂ PP →
            Γ ⊢P⦂ QQ →
            -----------------
            Γ ⊢P⦂ (PP ∥ QQ)

  ↑       : {o : O} →
            {PP : PType o}
            (op : Σₛ) →
            op ∈ₒ o →
            Γ ⊢V⦂ (proj₁ (payload op)) →
            Γ ⊢P⦂ PP →
            ----------------------------
            Γ ⊢P⦂ PP

  ↓       : {o : O}
            {PP : PType o}
            (op : Σₛ) →
            Γ ⊢V⦂ (proj₁ (payload op)) →
            Γ ⊢P⦂ PP →
            ----------------------------
            Γ ⊢P⦂ op ↓ₚ PP

-- ADMISSIBLE TYPING RULES

■-dup-var : {Γ Γ' : Ctx} {X : VType} →
            X ∈ (Γ ■ ++ₖ Γ') →
            --------------------------
            X ∈ (Γ ■ ■ ++ₖ Γ')

■-dup-var {Γ} {[]} (Tl-■ p x) =
  Tl-■ p (Tl-■ p x)
■-dup-var {Γ} {Γ' ∷ Y} Hd =
  Hd
■-dup-var {Γ} {Γ' ∷ Y} (Tl-v x) =
  Tl-v (■-dup-var x)
■-dup-var {Γ} {Γ' ■} (Tl-■ p x) =
  Tl-■ p (■-dup-var x)

mutual

  ■-dup-v : {Γ Γ' : Ctx} {X : VType} →
            (Γ ■ ++ₖ Γ') ⊢V⦂ X →
            --------------------------
            (Γ ■ ■ ++ₖ Γ') ⊢V⦂ X

  ■-dup-v (` x) =
    ` ■-dup-var x
  ■-dup-v (´ c) =
    ´ c
  ■-dup-v ⋆ =
    ⋆
  ■-dup-v (ƛ M) =
    ƛ (■-dup-c M)
  ■-dup-v ⟨ V ⟩ =
    ⟨ ■-dup-v V ⟩
  ■-dup-v (□ V) =
    □ (■-dup-v {Γ' = _ ■} V)

  ■-dup-c : {Γ Γ' : Ctx} {C : CType} →
            (Γ ■ ++ₖ Γ') ⊢C⦂ C →
            --------------------------
            (Γ ■ ■ ++ₖ Γ') ⊢C⦂ C

  ■-dup-c (return V) =
    return (■-dup-v V)
  ■-dup-c (let= M `in N) =
    let= (■-dup-c M) `in (■-dup-c N)
  ■-dup-c (V · W) =
    (■-dup-v V) · (■-dup-v W)
  ■-dup-c (↑ op p V M) =
    ↑ op p (■-dup-v V) (■-dup-c M)
  ■-dup-c (↓ op V M) =
    ↓ op (■-dup-v V) (■-dup-c M)
  ■-dup-c (promise op ∣ x ↦ M `in N) =
    promise op ∣ x ↦ (■-dup-c M) `in (■-dup-c N)
  ■-dup-c (await V until M) =
    await (■-dup-v V) until (■-dup-c M)
  ■-dup-c (unbox V `in M) =
    unbox (■-dup-v V) `in (■-dup-c M)
  ■-dup-c (spawn M N) =
    spawn (■-dup-c {Γ' = _ ■} M) (■-dup-c N)
  ■-dup-c (coerce p q M) =
    coerce p q (■-dup-c M)

■-wk : {Γ : Ctx} {X : VType} →
       mobile X →
       Γ ⊢V⦂ X →
       -----------------------
       Γ ■ ⊢V⦂ X

■-wk p (` x) =
  ` Tl-■ p x
■-wk p (´ c) =
  ´ c
■-wk p ⋆ =
  ⋆
■-wk p (□ V) =
  □ (■-dup-v {_} {[]} V)

■-str-var : {Γ Γ' : Ctx} {X : VType} →
            X ∈ Γ ■ ++ₖ Γ' →
            --------------------------
            X ∈ Γ ++ₖ Γ'

■-str-var {Γ} {[]} (Tl-■ p x) =
  x
■-str-var {Γ} {Γ' ∷ Y} Hd =
  Hd
■-str-var {Γ} {Γ' ∷ Y} (Tl-v x) =
  Tl-v (■-str-var x)
■-str-var {Γ} {Γ' ■} (Tl-■ p x) =
  Tl-■ p (■-str-var x)

mutual

  ■-str-v : {Γ Γ' : Ctx} {X : VType} →
            Γ ■ ++ₖ Γ' ⊢V⦂ X →
            -----------------------
            Γ ++ₖ Γ' ⊢V⦂ X

  ■-str-v (` x) =
    ` ■-str-var x
  ■-str-v (´ c) =
    ´ c
  ■-str-v ⋆ =
    ⋆
  ■-str-v (ƛ M) =
    ƛ (■-str-c M)
  ■-str-v ⟨ V ⟩ =
    ⟨ ■-str-v V ⟩
  ■-str-v {Γ} {Γ'} (□ V) =
    □ (■-str-v {Γ' = _ ■} V)

  ■-str-c : {Γ Γ' : Ctx} {C : CType} →
            Γ ■ ++ₖ Γ' ⊢C⦂ C →
            -----------------------
            Γ ++ₖ Γ' ⊢C⦂ C

  ■-str-c (return V) =
    return (■-str-v V)
  ■-str-c (let= M `in N) =
    let= (■-str-c M) `in (■-str-c N)
  ■-str-c (V · W) =
    (■-str-v V) · (■-str-v W)
  ■-str-c (↑ op p V M) =
    ↑ op p (■-str-v V) (■-str-c M)
  ■-str-c (↓ op V M) =
    ↓ op (■-str-v V) (■-str-c M)
  ■-str-c (promise op ∣ r ↦ M `in N) =
    promise op ∣ r ↦ (■-str-c M) `in (■-str-c N)
  ■-str-c (await V until M) =
    await (■-str-v V) until (■-str-c M)
  ■-str-c (unbox V `in M) =
    unbox (■-str-v V) `in (■-str-c M)
  ■-str-c (spawn M N) =
    spawn (■-str-c {Γ' = _ ■} M) (■-str-c N)
  ■-str-c (coerce p q M) =
    coerce p q (■-str-c M)


------------------------------------------------------------------------
-- Subject reduction for typing in Fω with interval kinds
------------------------------------------------------------------------

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

-- This module proves a variant of subject reduction (aka the
-- "preservation" theorem) for term reduction in Fω with interval
-- kinds.  The subject reduction property proved here is weaker than
-- the one typically found in the literature on other variants of Fω:
-- subject reduction does not hold in arbitrary typing contexts in Fω
-- with interval types because type variables of interval kind can be
-- used to introduce arbitrary typing (in)equalities into the
-- subtyping relation.  In other words, subject reduction does not
-- hold for full β-reduction as reductions under type abstractions are
-- not safe in general.  Note, however, that subject reduction does
-- hold for arbitrary β-reductions in *types* (see `pres-Tp∈-→β*' in
-- the Kinding.Declarative.Validity module for a proof).
--
-- NOTE.  Here we use the CBV evaluation strategy, but this is
-- unnecessarily restrictive.  Other evaluation strategies work so
-- long as they do not allow reductions under type abstractions.
--
-- Together with the "progress" theorem from Typing.Progress, subject
-- reduction ensures type safety.  For details, see e.g.
--
--  * B. C. Pierce, TAPL (2002), pp. 95.
--
--  * A. Wright and M. Felleisen, "A Syntactic Approach to Type
--    Soundness" (1994).

module FOmegaInt.Typing.Preservation where

open import Data.Product using (_,_)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (ε; _◅_)
open import Relation.Binary.PropositionalEquality using (subst)
open import Relation.Nullary.Negation using (contradiction)

open import FOmegaInt.Syntax
open import FOmegaInt.Typing
open import FOmegaInt.Typing.Inversion
open import FOmegaInt.Reduction.Cbv
open import FOmegaInt.Reduction.Full

open Syntax
open TermCtx
open Substitution using (_[_]; weaken-sub)
open Typing
open TypedSubstitution using (Tm∈-[]; <:-[])

-- Types of closed terms are preserved under single-step reduction.
pres : ∀ {a b c} → [] ⊢Tm a ∈ c → a →v b → [] ⊢Tm b ∈ c
pres (∈-var () _ _)
pres (∈-∀-i k-kd a∈b)    ()
pres (∈-→-i a∈* b∈c c∈*) ()
pres (∈-∀-e Λja∈Πkc b∈k) (cont-⊡ j a b) with Tm∈-gen Λja∈Πkc
... | ∈-∀-i j-kd a∈d Πjd<:Πkc =
  let k<∷j , d<:c = <:-∀-inv Πjd<:Πkc
  in ∈-⇑ (Tm∈-[] a∈d (∈-tp (∈-⇑ b∈k k<∷j))) (<:-[] d<:c (∈-tp b∈k))
pres (∈-∀-e a∈Πcd b∈c)   (a→e ⊡ b) = ∈-∀-e (pres a∈Πcd a→e) b∈c
pres (∈-→-e ƛea∈c⇒d v∈c) (cont-· e a v) with Tm∈-gen ƛea∈c⇒d
... | ∈-→-i e∈* a∈f e⇒f<:c⇒d =
  let c<:e , f<:d =  <:-→-inv e⇒f<:c⇒d
  in ∈-⇑ (subst (_ ⊢Tm _ ∈_) (weaken-sub _) (Tm∈-[] a∈f (∈-tm (∈-⇑ v∈c c<:e))))
         f<:d
pres (∈-→-e a∈c⇒d b∈c)   (a→e ·₁ b) = ∈-→-e (pres a∈c⇒d a→e) b∈c
pres (∈-→-e a∈c⇒d b∈c)   (v ·₂ b→e) = ∈-→-e a∈c⇒d (pres b∈c b→e)
pres (∈-⇑ a∈c c<:d)      a→b   = ∈-⇑ (pres a∈c a→b) c<:d

-- Weak preservation (aka subject reduction): types of closed terms
-- are preserved under reduction.
pres* : ∀ {a b c} → [] ⊢Tm a ∈ c → a →v* b → [] ⊢Tm b ∈ c
pres* a∈c ε            = a∈c
pres* a∈d (a→b ◅ b→*c) = pres* (pres a∈d a→b) b→*c


module _ where

open import Agda.Builtin.Nat using (mod-helper)
open import Common.Prelude
open import Common.Equality

_mod_ : Nat → Nat → Nat
n mod zero  = 0
n mod suc m = mod-helper 0 m n m

{-# INLINE _mod_ #-}

primitive
  primForce      : ∀ {a b} {A : Set a} {B : A → Set b} (x : A) → (∀ x → B x) → B x
  primForceLemma : ∀ {a b} {A : Set a} {B : A → Set b} (x : A) (f : ∀ x → B x) → primForce x f ≡ f x

force      = primForce
forceLemma = primForceLemma

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

-- Memory leak without proper seq --

pow′ : Nat → Nat → Nat
pow′ zero    acc = acc
pow′ (suc n) acc = pow′ n $! (acc + acc) mod 234576373

pow : Nat → Nat
pow n = pow′ n 1

main : IO Unit
main = printNat (pow 5000000)


------------------------------------------------------------------------
-- Parallel substitutions (defined using an inductive family)
------------------------------------------------------------------------

open import Data.Universe.Indexed

module deBruijn.Substitution.Data.Basics
  {i u e} {Uni : IndexedUniverse i u e} where

import deBruijn.Context; open deBruijn.Context Uni
open import Function using (id; _∘_; _$_)
open import Level using (_⊔_)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

private
 module Dummy₁ {t} (T : Term-like t) where

  open Term-like T

  infixl 5 _▻_

  -- Substitutions, represented as sequences of terms.

  data Sub : ∀ {Γ Δ} → Γ ⇨̂ Δ → Set (i ⊔ u ⊔ e ⊔ t) where
    ε   : ∀ {Δ} → Sub ε̂[ Δ ]
    _▻_ : ∀ {Γ Δ σ} {ρ̂ : Γ ⇨̂ Δ}
          (ρ : Sub ρ̂) (t : Δ ⊢ σ /̂ ρ̂) → Sub (ρ̂ ▻̂[ σ ] ⟦ t ⟧)

  -- A sequence of matching substitutions. (The reflexive transitive
  -- closure of Sub.)

  data Subs {Γ} : ∀ {Δ} → Γ ⇨̂ Δ → Set (i ⊔ u ⊔ e ⊔ t) where
    ε   : Subs îd[ Γ ]
    _▻_ : ∀ {Δ Ε} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε}
          (ρs : Subs ρ̂₁) (ρ : Sub ρ̂₂) → Subs (ρ̂₁ ∘̂ ρ̂₂)

open Dummy₁ public

-- Originally these substitutions were defined without the context
-- morphism index, but this led to the need to prove lots of lemmas
-- which hold by definition in the current setting. As an example the
-- map function (in deBruijn.Substitution.Data.Map) is currently
-- defined as follows:
--
--   -- Map.
--
--   map : ∀ {Γ Δ Ε} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} →
--         [ T₁ ⟶ T₂ ] ρ̂₂ → Sub T₁ ρ̂₁ → Sub T₂ (ρ̂₁ ∘̂ ρ̂₂)
--   map           f ε        = ε
--   map {ρ̂₂ = ρ̂₂} f (ρ₁ ▻ t) =
--     P.subst (λ v → Sub T₂ (⟦ ρ₁ ⟧⇨ ∘̂ ρ̂₂ ▻̂ v))
--             (≅-Value-⇒-≡ $ P.sym $ corresponds f t)
--             (map f ρ₁ ▻ f · t)
--
-- Previously it was defined roughly as follows (the code below is
-- untested and has been adapted to the current version of the
-- library, as it was at the time of writing, with some imagined
-- changes):
--
--   mutual
--
--     -- Map.
--
--     map : ∀ {Γ Δ Ε} {ρ̂₂ : Δ ⇨̂ Ε} →
--           [ T₁ ⟶ T₂ ] ρ̂₂ → (ρ₁ : Γ ⇨₁ Δ) → Γ ⇨₂ Ε
--     map f ε                 = ε
--     map f (_▻_ {σ = σ} ρ t) =
--       map f ρ ▻
--       P.subst (λ ρ̂ → _ ⊢₂ σ /̂ ρ̂)
--               (≅-⇨̂-⇒-≡ $ P.sym $ map-lemma f ρ)
--               (f · t)
--
--     abstract
--
--       map-lemma :
--         ∀ {Γ Δ Ε} {ρ̂₂ : Δ ⇨̂ Ε} →
--         (f : [ T₁ ⟶ T₂ ] ρ̂₂) (ρ₁ : Γ ⇨₁ Δ) →
--         ⟦ map f ρ₁ ⟧₂⇨ ≅-⇨̂ ⟦ ρ₁ ⟧₁⇨ ∘̂ ρ₂
--       map-lemma           f ε                 = P.refl
--       map-lemma {ρ₂ = ρ₂} f (_▻_ {σ = σ} ρ t) =
--         ▻̂-cong P.refl (map-lemma f ρ) (begin
--           [ ⟦ P.subst (λ ρ̂ → _ ⊢₂ σ /̂ ρ̂)
--                       (≅-⇨̂-⇒-≡ $ P.sym (map-lemma f ρ))
--                       (f · t) ⟧₂                        ]  ≡⟨ Term-like.⟦⟧-cong T₂
--                                                                 (Term-like.drop-subst-⊢ T₂
--                                                                    (λ ρ̂ → σ /̂ ρ̂)
--                                                                    (≅-⇨̂-⇒-≡ $ P.sym (map-lemma f ρ))) ⟩
--           [ ⟦ f · t ⟧₂                                  ]  ≡⟨ P.sym $ corresponds f t ⟩
--           [ ⟦ t ⟧₁ /̂Val ρ₂                              ]  ∎)

private
 module Dummy₂ {t} {T : Term-like t} where

  open Term-like T

  -- Some variants.

  ε⇨[_] : ∀ Δ → Sub T ε̂[ Δ ]
  ε⇨[ _ ] = ε

  infixl 5 _▻⇨[_]_

  _▻⇨[_]_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} →
            Sub T ρ̂ → ∀ σ (t : Δ ⊢ σ /̂ ρ̂) → Sub T (ρ̂ ▻̂[ σ ] ⟦ t ⟧)
  ρ ▻⇨[ _ ] t = ρ ▻ t

  ε⇨⋆[_] : ∀ Γ → Subs T îd[ Γ ]
  ε⇨⋆[ _ ] = ε

  -- Equality of substitutions.

  record [⇨] : Set (i ⊔ u ⊔ e ⊔ t) where
    constructor [_]
    field
      {Γ Δ} : Ctxt
      {ρ̂}   : Γ ⇨̂ Δ
      ρ     : Sub T ρ̂

  infix 4 _≅-⇨_

  _≅-⇨_ : ∀ {Γ₁ Δ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} (ρ₁ : Sub T ρ̂₁)
            {Γ₂ Δ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} (ρ₂ : Sub T ρ̂₂) → Set _
  ρ₁ ≅-⇨ ρ₂ = _≡_ {A = [⇨]} [ ρ₁ ] [ ρ₂ ]

  ≅-⇨-⇒-≡ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} {ρ₁ ρ₂ : Sub T ρ̂} →
            ρ₁ ≅-⇨ ρ₂ → ρ₁ ≡ ρ₂
  ≅-⇨-⇒-≡ P.refl = P.refl

  -- Certain uses of substitutivity can be removed.

  drop-subst-Sub :
    ∀ {a} {A : Set a} {x₁ x₂ : A} {Γ Δ}
    (f : A → Γ ⇨̂ Δ) {ρ} (eq : x₁ ≡ x₂) →
    P.subst (λ x → Sub T (f x)) eq ρ ≅-⇨ ρ
  drop-subst-Sub f P.refl = P.refl

  -- Equality of sequences of substitutions.

  record [⇨⋆] : Set (i ⊔ u ⊔ e ⊔ t) where
    constructor [_]
    field
      {Γ Δ} : Ctxt
      {ρ̂}   : Γ ⇨̂ Δ
      ρs    : Subs T ρ̂

  infix 4 _≅-⇨⋆_

  _≅-⇨⋆_ : ∀ {Γ₁ Δ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} (ρs₁ : Subs T ρ̂₁)
             {Γ₂ Δ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} (ρs₂ : Subs T ρ̂₂) → Set _
  ρs₁ ≅-⇨⋆ ρs₂ = _≡_ {A = [⇨⋆]} [ ρs₁ ] [ ρs₂ ]

  ≅-⇨⋆-⇒-≡ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} {ρs₁ ρs₂ : Subs T ρ̂} →
             ρs₁ ≅-⇨⋆ ρs₂ → ρs₁ ≡ ρs₂
  ≅-⇨⋆-⇒-≡ P.refl = P.refl

  -- Interpretation of substitutions: context morphisms.

  ⟦_⟧⇨ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Sub T ρ̂ → Γ ⇨̂ Δ
  ⟦_⟧⇨ {ρ̂ = ρ̂} _ = ρ̂

  ⟦_⟧⇨⋆ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Subs T ρ̂ → Γ ⇨̂ Δ
  ⟦_⟧⇨⋆ {ρ̂ = ρ̂} _ = ρ̂

  -- Application of substitutions to types.

  infixl 8 _/I_ _/_ _/⋆_

  _/I_ : ∀ {Γ Δ i} {ρ̂ : Γ ⇨̂ Δ} → IType Γ i → Sub T ρ̂ → IType Δ i
  σ /I ρ = σ /̂I ⟦ ρ ⟧⇨

  _/_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Type Γ → Sub T ρ̂ → Type Δ
  σ / ρ = σ /̂ ⟦ ρ ⟧⇨

  _/⋆_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Type Γ → Subs T ρ̂ → Type Δ
  σ /⋆ ρs = σ /̂ ⟦ ρs ⟧⇨⋆

  -- Application of substitutions to values.

  infixl 8 _/Val_

  _/Val_ : ∀ {Γ Δ σ} {ρ̂ : Γ ⇨̂ Δ} →
           Value Γ σ → Sub T ρ̂ → Value Δ (σ /̂ ρ̂)
  v /Val ρ = v /̂Val ⟦ ρ ⟧⇨

  -- Application of substitutions to context extensions.

  infixl 8 _/⁺_ _/⁺⋆_ _/₊_ _/₊⋆_

  _/⁺_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Ctxt⁺ Γ → Sub T ρ̂ → Ctxt⁺ Δ
  Γ⁺ /⁺ ρ = Γ⁺ /̂⁺ ⟦ ρ ⟧⇨

  _/⁺⋆_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Ctxt⁺ Γ → Subs T ρ̂ → Ctxt⁺ Δ
  Γ⁺ /⁺⋆ ρs = Γ⁺ /̂⁺ ⟦ ρs ⟧⇨⋆

  _/₊_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Ctxt₊ Γ → Sub T ρ̂ → Ctxt₊ Δ
  Γ₊ /₊ ρ = Γ₊ /̂₊ ⟦ ρ ⟧⇨

  _/₊⋆_ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Ctxt₊ Γ → Subs T ρ̂ → Ctxt₊ Δ
  Γ₊ /₊⋆ ρs = Γ₊ /̂₊ ⟦ ρs ⟧⇨⋆

  -- Application of substitutions to variables.

  infixl 8 _/∋_ _/∋-lemma_

  _/∋_ : ∀ {Γ Δ σ} {ρ̂ : Γ ⇨̂ Δ} → Γ ∋ σ → (ρ : Sub T ρ̂) → Δ ⊢ σ / ρ
  zero  /∋ (ρ ▻ y) = y
  suc x /∋ (ρ ▻ y) = x /∋ ρ

  abstract

    _/∋-lemma_ : ∀ {Γ Δ σ} {ρ̂ : Γ ⇨̂ Δ} (x : Γ ∋ σ) (ρ : Sub T ρ̂) →
                 x /̂∋ ⟦ ρ ⟧⇨ ≅-Value ⟦ x /∋ ρ ⟧
    zero  /∋-lemma (ρ ▻ y) = P.refl
    suc x /∋-lemma (ρ ▻ y) = x /∋-lemma ρ

  app∋ : ∀ {Γ Δ} {ρ̂ : Γ ⇨̂ Δ} → Sub T ρ̂ → [ Var ⟶ T ] ρ̂
  app∋ ρ = record
    { function    = λ _ x → x /∋       ρ
    ; corresponds = λ _ x → x /∋-lemma ρ
    }

  -- The tail of a nonempty substitution.

  tail : ∀ {Γ Δ σ} {ρ̂ : Γ ▻ σ ⇨̂ Δ} → Sub T ρ̂ → Sub T (t̂ail ρ̂)
  tail (ρ ▻ t) = ρ

  -- The head of a nonempty substitution.

  head : ∀ {Γ Δ σ} {ρ̂ : Γ ▻ σ ⇨̂ Δ} (ρ : Sub T ρ̂) → Δ ⊢ σ / tail ρ
  head (ρ ▻ t) = t

  head-lemma : ∀ {Γ Δ σ} {ρ̂ : Γ ▻ σ ⇨̂ Δ} (ρ : Sub T ρ̂) →
               ĥead ⟦ ρ ⟧⇨ ≅-Value ⟦ head ρ ⟧
  head-lemma (ρ ▻ t) = P.refl

  -- Fold for sequences of substitutions.

  fold : ∀ {f} (F : ∀ {Γ Δ} → Γ ⇨̂ Δ → Set f) {Γ} →
         F (îd {Γ = Γ}) →
         (∀ {Δ Ε} {ρ̂₁ : Γ ⇨̂ Δ} {ρ̂₂ : Δ ⇨̂ Ε} →
            F ρ̂₁ → Sub T ρ̂₂ → F (ρ̂₁ ∘̂ ρ̂₂)) →
         ∀ {Δ} {ρ̂ : Γ ⇨̂ Δ} → Subs T ρ̂ → F ρ̂
  fold F nil cons ε        = nil
  fold F nil cons (ρs ▻ ρ) = cons (fold F nil cons ρs) ρ

  -- Some congruence lemmas.

  ε⇨-cong : ∀ {Δ₁ Δ₂} → Δ₁ ≅-Ctxt Δ₂ → ε⇨[ Δ₁ ] ≅-⇨ ε⇨[ Δ₂ ]
  ε⇨-cong P.refl = P.refl

  ▻⇨-cong :
    ∀ {Γ₁ Δ₁ σ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁} {t₁ : Δ₁ ⊢ σ₁ / ρ₁}
      {Γ₂ Δ₂ σ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} {t₂ : Δ₂ ⊢ σ₂ / ρ₂} →
    σ₁ ≅-Type σ₂ → ρ₁ ≅-⇨ ρ₂ → t₁ ≅-⊢ t₂ →
    ρ₁ ▻⇨[ σ₁ ] t₁ ≅-⇨ ρ₂ ▻⇨[ σ₂ ] t₂
  ▻⇨-cong P.refl P.refl P.refl = P.refl

  ε⇨⋆-cong : ∀ {Γ₁ Γ₂} → Γ₁ ≡ Γ₂ → ε⇨⋆[ Γ₁ ] ≅-⇨⋆ ε⇨⋆[ Γ₂ ]
  ε⇨⋆-cong P.refl = P.refl

  ▻⇨⋆-cong :
    ∀ {Γ₁ Δ₁ Ε₁} {ρ̂₁₁ : Γ₁ ⇨̂ Δ₁} {ρ̂₂₁ : Δ₁ ⇨̂ Ε₁}
      {ρs₁ : Subs T ρ̂₁₁} {ρ₁ : Sub T ρ̂₂₁}
      {Γ₂ Δ₂ Ε₂} {ρ̂₁₂ : Γ₂ ⇨̂ Δ₂} {ρ̂₂₂ : Δ₂ ⇨̂ Ε₂}
      {ρs₂ : Subs T ρ̂₁₂} {ρ₂ : Sub T ρ̂₂₂} →
    ρs₁ ≅-⇨⋆ ρs₂ → ρ₁ ≅-⇨ ρ₂ → ρs₁ ▻ ρ₁ ≅-⇨⋆ ρs₂ ▻ ρ₂
  ▻⇨⋆-cong P.refl P.refl = P.refl

  ⟦⟧⇨-cong : ∀ {Γ₁ Δ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
               {Γ₂ Δ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
             ρ₁ ≅-⇨ ρ₂ → ⟦ ρ₁ ⟧⇨ ≅-⇨̂ ⟦ ρ₂ ⟧⇨
  ⟦⟧⇨-cong P.refl = P.refl

  /I-cong :
    ∀ {i}
      {Γ₁ Δ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {σ₁ : IType Γ₁ i} {ρ₁ : Sub T ρ̂₁}
      {Γ₂ Δ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {σ₂ : IType Γ₂ i} {ρ₂ : Sub T ρ̂₂} →
    σ₁ ≅-IType σ₂ → ρ₁ ≅-⇨ ρ₂ → σ₁ /I ρ₁ ≅-IType σ₂ /I ρ₂
  /I-cong P.refl P.refl = P.refl

  /-cong : ∀ {Γ₁ Δ₁ σ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
             {Γ₂ Δ₂ σ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
           σ₁ ≅-Type σ₂ → ρ₁ ≅-⇨ ρ₂ → σ₁ / ρ₁ ≅-Type σ₂ / ρ₂
  /-cong P.refl P.refl = P.refl

  /⁺-cong : ∀ {Γ₁ Δ₁ Γ⁺₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
              {Γ₂ Δ₂ Γ⁺₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
            Γ⁺₁ ≅-Ctxt⁺ Γ⁺₂ → ρ₁ ≅-⇨ ρ₂ → Γ⁺₁ /⁺ ρ₁ ≅-Ctxt⁺ Γ⁺₂ /⁺ ρ₂
  /⁺-cong P.refl P.refl = P.refl

  /⁺⋆-cong : ∀ {Γ₁ Δ₁ Γ⁺₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρs₁ : Subs T ρ̂₁}
               {Γ₂ Δ₂ Γ⁺₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρs₂ : Subs T ρ̂₂} →
             Γ⁺₁ ≅-Ctxt⁺ Γ⁺₂ → ρs₁ ≅-⇨⋆ ρs₂ →
             Γ⁺₁ /⁺⋆ ρs₁ ≅-Ctxt⁺ Γ⁺₂ /⁺⋆ ρs₂
  /⁺⋆-cong P.refl P.refl = P.refl

  /₊-cong : ∀ {Γ₁ Δ₁ Γ₊₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
              {Γ₂ Δ₂ Γ₊₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
            Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ρ₁ ≅-⇨ ρ₂ → Γ₊₁ /₊ ρ₁ ≅-Ctxt₊ Γ₊₂ /₊ ρ₂
  /₊-cong P.refl P.refl = P.refl

  /₊⋆-cong : ∀ {Γ₁ Δ₁ Γ₊₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρs₁ : Subs T ρ̂₁}
               {Γ₂ Δ₂ Γ₊₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρs₂ : Subs T ρ̂₂} →
             Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ρs₁ ≅-⇨⋆ ρs₂ →
             Γ₊₁ /₊⋆ ρs₁ ≅-Ctxt₊ Γ₊₂ /₊⋆ ρs₂
  /₊⋆-cong P.refl P.refl = P.refl

  /∋-cong : ∀ {Γ₁ Δ₁ σ₁} {x₁ : Γ₁ ∋ σ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
              {Γ₂ Δ₂ σ₂} {x₂ : Γ₂ ∋ σ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
            x₁ ≅-∋ x₂ → ρ₁ ≅-⇨ ρ₂ → x₁ /∋ ρ₁ ≅-⊢ x₂ /∋ ρ₂
  /∋-cong P.refl P.refl = P.refl

  tail-cong : ∀ {Γ₁ Δ₁ σ₁} {ρ̂₁ : Γ₁ ▻ σ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
                {Γ₂ Δ₂ σ₂} {ρ̂₂ : Γ₂ ▻ σ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
              ρ₁ ≅-⇨ ρ₂ → tail ρ₁ ≅-⇨ tail ρ₂
  tail-cong P.refl = P.refl

  head-cong : ∀ {Γ₁ Δ₁ σ₁} {ρ̂₁ : Γ₁ ▻ σ₁ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
                {Γ₂ Δ₂ σ₂} {ρ̂₂ : Γ₂ ▻ σ₂ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
              ρ₁ ≅-⇨ ρ₂ → head ρ₁ ≅-⊢ head ρ₂
  head-cong P.refl = P.refl

  abstract

    -- Some eta-laws.

    ηε : ∀ {Δ} {ρ̂ : ε ⇨̂ Δ} (ρ : Sub T ρ̂) → ρ ≅-⇨ ε⇨[ Δ ]
    ηε ε = P.refl

    η▻ : ∀ {Γ Δ σ} {ρ̂ : Γ ▻ σ ⇨̂ Δ} (ρ : Sub T ρ̂) →
         ρ ≅-⇨ tail ρ ▻⇨[ σ ] head ρ
    η▻ (ρ ▻ t) = P.refl

    -- Two substitutions are equal if their indices are equal and
    -- their projections are pairwise equal.

    extensionality :
      ∀ {Γ Δ₁} {ρ̂₁ : Γ ⇨̂ Δ₁} {ρ₁ : Sub T ρ̂₁}
          {Δ₂} {ρ̂₂ : Γ ⇨̂ Δ₂} {ρ₂ : Sub T ρ̂₂} →
      Δ₁ ≅-Ctxt Δ₂ → (∀ {σ} (x : Γ ∋ σ) → x /∋ ρ₁ ≅-⊢ x /∋ ρ₂) →
      ρ₁ ≅-⇨ ρ₂
    extensionality {ρ₁ = ε}       {ρ₂ = ε}       Δ₁≅Δ₂ hyp = ε⇨-cong Δ₁≅Δ₂
    extensionality {ρ₁ = ρ₁ ▻ t₁} {ρ₂ = ρ₂ ▻ t₂} Δ₁≅Δ₂ hyp =
      ▻⇨-cong P.refl
              (extensionality Δ₁≅Δ₂ (hyp ∘ suc))
              (hyp zero)

open Dummy₂ public


{-# OPTIONS --show-implicit #-}

open import Agda.Builtin.Nat
open import Agda.Builtin.Equality
open import Agda.Builtin.Unit

postulate
  A B C D : Set
  F : (A → A) → (A → A → A) → A
  a : A

data Tree : Set where
  leaf : Tree
  node : (f : (x : A) → Tree) → Tree

tree1 : Nat → Tree
tree1 zero = leaf
tree1 (suc n) = node (λ x → tree1 n)

tree2 : Nat → Tree
tree2 zero = leaf
tree2 (suc n) = node (λ x → tree2 n)

-- In Agda 2.6.0.1, this takes >50 sec and >5GB to typecheck.
test : tree1 5000 ≡ tree2 5000
test = refl


-- Andreas, 2018-06-10, issue #3124
-- Wrong context for error IrrelevantDatatype in the coverage checker.

data Squash (A : Set) : Prop where
  squash : A → Squash A

test : ∀{A} → Squash (Squash A → A)
test = squash λ{ (squash y) → y }

-- WAS: de Bruijn index in error message

-- Expected error:
-- Cannot split on argument of irrelevant datatype (Squash .A)
-- when checking the definition of .extendedlambda0


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

open import lib.Basics
open import lib.types.Bool
open import lib.types.Empty
open import lib.types.Lift
open import lib.types.Sigma
open import lib.types.Pi

module lib.types.Coproduct where

module _ {i j} {A : Type i} {B : Type j} where

  Coprod-elim : ∀ {k} {C : A ⊔ B → Type k}
    → ((a : A) → C (inl a)) → ((b : B) → C (inr b))
    → Π (A ⊔ B) C
  Coprod-elim f g (inl a) = f a
  Coprod-elim f g (inr b) = g b

  ⊔-elim = Coprod-elim

  Coprod-rec : ∀ {k} {C : Type k}
    → (A → C) → (B → C) → (A ⊔ B → C)
  Coprod-rec {C = C} = Coprod-elim {C = λ _ → C}

  ⊔-rec = Coprod-rec

  Coprod= : Coprod A B → Coprod A B → Type (lmax i j)
  Coprod= (inl a₁) (inl a₂) = Lift {j = (lmax i j)} $ a₁ == a₂
  Coprod= (inl a₁) (inr b₂) = Lift Empty
  Coprod= (inr b₁) (inl a₂) = Lift Empty
  Coprod= (inr b₁) (inr b₂) = Lift {j = (lmax i j)} $ b₁ == b₂

  Coprod=-in : {x y : Coprod A B} → (x == y) → Coprod= x y
  Coprod=-in {inl _} idp = lift idp
  Coprod=-in {inr _} idp = lift idp

  Coprod=-out : {x y : Coprod A B} → Coprod= x y → (x == y)
  Coprod=-out {inl _} {inl _} c = ap inl $ lower c
  Coprod=-out {inl _} {inr _} c = Empty-rec $ lower c
  Coprod=-out {inr _} {inl _} c = Empty-rec $ lower c
  Coprod=-out {inr _} {inr _} c = ap inr (lower c)

  Coprod=-in-equiv : (x y : Coprod A B) → (x == y) ≃ Coprod= x y
  Coprod=-in-equiv x y = equiv Coprod=-in Coprod=-out (f-g x y) (g-f x y)
    where f-g : ∀ x' y' → ∀ c → Coprod=-in (Coprod=-out {x'} {y'} c) == c
          f-g (inl a₁) (inl .a₁) (lift idp) = idp
          f-g (inl a₁) (inr b₂) b = Empty-rec $ lower b
          f-g (inr b₁) (inl a₂) b = Empty-rec $ lower b
          f-g (inr b₁) (inr .b₁) (lift idp) = idp

          g-f : ∀ x' y' → ∀ p → Coprod=-out (Coprod=-in {x'} {y'} p) == p
          g-f (inl _) .(inl _) idp = idp
          g-f (inr _) .(inr _) idp = idp

  inl=inl-equiv : (a₁ a₂ : A) → (inl a₁ == inl a₂) ≃ (a₁ == a₂)
  inl=inl-equiv a₁ a₂ = lower-equiv ∘e Coprod=-in-equiv (inl a₁) (inl a₂)

  inr=inr-equiv : (b₁ b₂ : B) → (inr b₁ == inr b₂) ≃ (b₁ == b₂)
  inr=inr-equiv b₁ b₂ = lower-equiv ∘e Coprod=-in-equiv (inr b₁) (inr b₂)

  inl≠inr : (a₁ : A) (b₂ : B) → (inl a₁ ≠ inr b₂)
  inl≠inr a₁ b₂ p = lower $ Coprod=-in p

  inr≠inl : (b₁ : B) (a₂ : A) → (inr b₁ ≠ inl a₂)
  inr≠inl a₁ b₂ p = lower $ Coprod=-in p

  instance
    ⊔-level : ∀ {n} → has-level (S (S n)) A → has-level (S (S n)) B
              → has-level (S (S n)) (Coprod A B)
    ⊔-level {n} pA pB = has-level-in (⊔-level-aux pA pB) where

      instance _ = pA; _ = pB

      ⊔-level-aux : has-level (S (S n)) A → has-level (S (S n)) B
              → has-level-aux (S (S n)) (Coprod A B)
      ⊔-level-aux _ _ (inl a₁) (inl a₂) = equiv-preserves-level (inl=inl-equiv a₁ a₂ ⁻¹)
      ⊔-level-aux _ _ (inl a₁) (inr b₂) = has-level-in (λ p → Empty-rec (inl≠inr a₁ b₂ p))
      ⊔-level-aux _ _ (inr b₁) (inl a₂) = has-level-in (λ p → Empty-rec (inr≠inl b₁ a₂ p))
      ⊔-level-aux _ _ (inr b₁) (inr b₂) = equiv-preserves-level ((inr=inr-equiv b₁ b₂)⁻¹)

  Coprod-level = ⊔-level

module _ {i j} {A : Type i} {B : Type j} where

  Coprod-rec-post∘ : ∀ {k l} {C : Type k} {D : Type l}
    → (h : C → D) (f : A → C) (g : B → C)
    → h ∘ Coprod-rec f g ∼ Coprod-rec (h ∘ f) (h ∘ g)
  Coprod-rec-post∘ h f g (inl _) = idp
  Coprod-rec-post∘ h f g (inr _) = idp

  ⊔-rec-post∘ = Coprod-rec-post∘

module _ {i i' j j'} {A : Type i} {A' : Type i'} {B : Type j} {B' : Type j'}
  (f : A → A') (g : B → B') where

  ⊔-fmap : A ⊔ B → A' ⊔ B'
  ⊔-fmap (inl a) = inl (f a)
  ⊔-fmap (inr b) = inr (g b)

infix 80 _⊙⊔_
_⊙⊔_ : ∀ {i j} → Ptd i → Ptd j → Ptd (lmax i j)
X ⊙⊔ Y = ⊙[ Coprod (de⊙ X) (de⊙ Y) , inl (pt X) ]

_⊔⊙_ : ∀ {i j} → Ptd i → Ptd j → Ptd (lmax i j)
X ⊔⊙ Y = ⊙[ Coprod (de⊙ X) (de⊙ Y) , inr (pt Y) ]

module _ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (f : X ⊙→ Z) (g : Y ⊙→ Z) where

  ⊙⊔-rec : X ⊙⊔ Y ⊙→ Z
  ⊙⊔-rec = Coprod-rec (fst f) (fst g) , snd f

  ⊔⊙-rec : X ⊔⊙ Y ⊙→ Z
  ⊔⊙-rec = Coprod-rec (fst f) (fst g) , snd g

module _ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'}
  (f : X ⊙→ X') (g : Y ⊙→ Y') where

  ⊙⊔-fmap : X ⊙⊔ Y ⊙→ X' ⊙⊔ Y'
  ⊙⊔-fmap = ⊔-fmap (fst f) (fst g) , ap inl (snd f)

  ⊔⊙-fmap : X ⊔⊙ Y ⊙→ X' ⊔⊙ Y'
  ⊔⊙-fmap = ⊔-fmap (fst f) (fst g) , ap inr (snd g)

codiag : ∀ {i} {A : Type i} → A ⊔ A → A
codiag = Coprod-rec (idf _) (idf _)

⊙codiag : ∀ {i} {X : Ptd i} → X ⊙⊔ X ⊙→ X
⊙codiag = (codiag , idp)

-- A binary sigma is a coproduct
ΣBool-equiv-⊔ : ∀ {i} (Pick : Bool → Type i)
  → Σ Bool Pick ≃ Pick true ⊔ Pick false
ΣBool-equiv-⊔ Pick = equiv into out into-out out-into
  where
  into : Σ _ Pick → Pick true ⊔ Pick false
  into (true , a) = inl a
  into (false , b) = inr b

  out : (Pick true ⊔ Pick false) → Σ _ Pick
  out (inl a) = (true , a)
  out (inr b) = (false , b)

  abstract
    into-out : ∀ c → into (out c) == c
    into-out (inl a) = idp
    into-out (inr b) = idp

    out-into : ∀ s → out (into s) == s
    out-into (true , a) = idp
    out-into (false , b) = idp

⊔₁-Empty : ∀ {i} (A : Type i) → Empty ⊔ A ≃ A
⊔₁-Empty A = equiv (λ{(inl ()); (inr a) → a}) inr (λ _ → idp) (λ{(inl ()); (inr _) → idp})

module _ {i j k} {A : Type i} {B : Type j} (P : A ⊔ B → Type k) where
  Π₁-⊔-equiv-× : Π (A ⊔ B) P ≃ Π A (P ∘ inl) × Π B (P ∘ inr)
  Π₁-⊔-equiv-× = equiv to from to-from from-to
    where
    to : Π (A ⊔ B) P → Π A (P ∘ inl) × Π B (P ∘ inr)
    to f = (λ a → f (inl a)) , (λ b → f (inr b))

    from : Π A (P ∘ inl) × Π B (P ∘ inr) → Π (A ⊔ B) P
    from (f , g) (inl a) = f a
    from (f , g) (inr b) = g b

    abstract
      to-from : ∀ fg → to (from fg) == fg
      to-from _ = idp

      from-to : ∀ fg → from (to fg) == fg
      from-to fg = λ= λ where (inl _) → idp
                              (inr _) → idp


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

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

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening as T hiding (wk; wkTerm; wkEqTerm)
open import Definition.Typed.RedSteps
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Weakening
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Application
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Properties
open import Definition.LogicalRelation.Substitution.Reflexivity
open import Definition.LogicalRelation.Substitution.Introductions.Sigma
open import Definition.LogicalRelation.Substitution.Introductions.Fst
open import Definition.LogicalRelation.Substitution.Introductions.Pi
open import Definition.LogicalRelation.Substitution.Introductions.SingleSubst
open import Definition.LogicalRelation.Substitution.MaybeEmbed
open import Definition.LogicalRelation.Substitution.Introductions.Universe

open import Tools.Product
import Tools.PropositionalEquality as PE

-- Valid snd term construction.
sndᵛ : ∀ {F G l∃ tu Γ}
       ([Γ] : ⊩ᵛ Γ) →
       let l    = ∞
           [UF] = maybeEmbᵛ {A = Univ % _} [Γ] (Uᵛ (proj₂ (levelBounded l∃)) [Γ])
           [U∃] = maybeEmbᵛ {A = Univ % _} [Γ] (Uᵛ (proj₂ (levelBounded l∃)) [Γ])
       in
        ([F] : Γ ⊩ᵛ⟨ l ⟩ F ^ [ % , ι l∃ ] / [Γ])
        ([G] : Γ ∙ F ^ [ % , ι l∃ ] ⊩ᵛ⟨ l ⟩ G ^ [ % , ι l∃ ] / [Γ] ∙ [F])
        ([UG] : Γ ∙ F ^ [ % , ι l∃ ] ⊩ᵛ⟨ l ⟩ Univ % l∃ ^ [ ! , next l∃ ] / [Γ] ∙ [F])
        ([Fₜ] : Γ ⊩ᵛ⟨ l ⟩ F ∷ Univ % l∃ ^ [ ! , next l∃ ] / [Γ] / [UF])
        ([Gₜ] : Γ ∙ F ^ [ % , ι l∃ ] ⊩ᵛ⟨ l ⟩ G ∷ Univ % l∃ ^ [ ! , next l∃ ] / [Γ] ∙ [F] / (λ {Δ} {σ} → [UG] {Δ} {σ}))
        ([tu] : Γ ⊩ᵛ⟨ l ⟩ tu ∷ ∃ F ▹ G ^ [ % , ι l∃ ] / [Γ] / ∃ᵛ {F} {G} [Γ] [F] [G])
        → Γ ⊩ᵛ⟨ l ⟩ snd tu ∷ G [ fst tu ] ^ [ % , ι l∃ ] / [Γ] / substS {F} {G} {fst tu} [Γ] [F] [G] (fstᵛ {F} {G} {l∃} {tu} {Γ} [Γ] [F] [G] (λ {Δ} {σ} → [UG] {Δ} {σ}) [Fₜ] [Gₜ] [tu])
sndᵛ {F} {G} {l∃} {tu} {Γ} [Γ] [F] [G] [UG] [Fₜ] [Gₜ] [tu] {Δ = Δ} {σ = σ} ⊢Δ [σ] =
  let [fst] = fstᵛ {F} {G} {l∃} {tu} {Γ} [Γ] [F] [G] (λ {Δ} {σ} → [UG] {Δ} {σ}) [Fₜ] [Gₜ] [tu]
      [G[fsttu]] = substS {F} {G} {fst tu} [Γ] [F] [G] [fst]
      [UF] = maybeEmbᵛ {A = Univ % _} [Γ] (Uᵛ (proj₂ (levelBounded l∃)) [Γ])
      [σUF] = proj₁ ([UF] ⊢Δ [σ])
      [σF] = proj₁ ([F] ⊢Δ [σ])
      ⊢F = escape [σF]
      [σF]ₜ = proj₁ ([Fₜ] ⊢Δ [σ])
      ⊢Fₜ = escapeTerm [σUF] [σF]ₜ
      [σUG] = proj₁ ([UG] {σ = liftSubst σ} (⊢Δ ∙ ⊢F)
                          (liftSubstS {F = F} [Γ] ⊢Δ [F] [σ]))
      [σG]ₜ = proj₁ ([Gₜ] (⊢Δ ∙ ⊢F)
                        (liftSubstS {F = F} [Γ] ⊢Δ [F] [σ]))
      ⊢Gₜ = escapeTerm [σUG] [σG]ₜ
      [σtu] = proj₁ ([tu] (⊢Δ) [σ])
      [∃FG] = ∃ᵛ {F} {G} {Γ} [Γ] [F] [G]
      [σ∃FG] = proj₁ ([∃FG] ⊢Δ [σ])
      ⊢tu = escapeTerm [σ∃FG] [σtu]
      ⊢snd = sndⱼ {F = subst σ F} {G = subst (liftSubst σ) G} {t = subst σ tu}
                   ⊢Fₜ ⊢Gₜ ⊢tu
      [σG[fsttu]] = proj₁ ([G[fsttu]] ⊢Δ [σ])
      ⊢sndSubst = PE.subst (λ x → _ ⊢ _ ∷ x  ^ _) (PE.sym (singleSubstLift G (fst tu))) ⊢snd
   in logRelIrr [σG[fsttu]] ⊢sndSubst ,
      (λ {σ′} [σ]′ [σ≡σ′] → logRelIrrEq [σG[fsttu]] ⊢sndSubst
     let [UF]′ = maybeEmbᵛ {A = Univ % _} [Γ] (Uᵛ (proj₂ (levelBounded l∃)) [Γ])
         [σUF]′ = proj₁ ([UF] ⊢Δ [σ]′)
         [σF]′ = proj₁ ([F] ⊢Δ [σ]′)
         ⊢F′ = escape [σF]′
         [σF]ₜ′ = proj₁ ([Fₜ] ⊢Δ [σ]′)
         ⊢Fₜ′ = escapeTerm [σUF]′ [σF]ₜ′
         [σUG]′ = proj₁ ([UG] {σ = liftSubst σ′} (⊢Δ ∙ ⊢F′)
                             (liftSubstS {F = F} [Γ] ⊢Δ [F] [σ]′))
         [σG]ₜ′ = proj₁ ([Gₜ] (⊢Δ ∙ ⊢F′)
                             (liftSubstS {F = F} [Γ] ⊢Δ [F] [σ]′))
         ⊢Gₜ′ = escapeTerm [σUG]′ [σG]ₜ′
         [σtu]′ = proj₁ ([tu] (⊢Δ) [σ]′)
         [σ∃FG]′ = proj₁ ([∃FG] ⊢Δ [σ]′)
         ⊢tu′ = escapeTerm [σ∃FG]′ [σtu]′
         ⊢snd′ = sndⱼ {F = subst σ′ F} {G = subst (liftSubst σ′) G} {t = subst σ′ tu}
                   ⊢Fₜ′ ⊢Gₜ′ ⊢tu′
         [σG[fsttu]′] = proj₁ ([G[fsttu]] ⊢Δ [σ]′)
         ⊢sndSubst′ = PE.subst (λ x → _ ⊢ _ ∷ x  ^ _) (PE.sym (singleSubstLift G (fst tu))) ⊢snd′
         [σG[fsttu]≡σG[fsttu]′] = proj₂ ([G[fsttu]] ⊢Δ [σ]) [σ]′ [σ≡σ′]
         ⊢σG[fsttu]≡σG[fsttu]′ = escapeEq [σG[fsttu]] [σG[fsttu]≡σG[fsttu]′]
      in conv ⊢sndSubst′ (≅-eq (≅-sym ⊢σG[fsttu]≡σG[fsttu]′)) )