{-# OPTIONS --safe #-}

module Definition.Conversion.FullReduction where

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.EqRelInstance
open import Definition.Typed.Weakening
open import Definition.Conversion
open import Definition.Conversion.Whnf
open import Definition.Conversion.Stability
open import Definition.Typed.Consequences.Inversion
open import Definition.Typed.Consequences.Injectivity
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.NeTypeEq
open import Definition.Typed.Consequences.Equality
open import Definition.Typed.Consequences.RelevanceUnicity
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Properties.Escape
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Fundamental.Reducibility
open import Definition.Typed.Consequences.TypeUnicity

open import Tools.Empty using (⊥; ⊥-elim)
open import Tools.Product
import Tools.PropositionalEquality as PE

mutual
  data NfNeutral (Γ : Con Term) : Term → Set where
    var     : ∀ n                     → NfNeutral Γ (var n)
    ∘ₙ      : ∀ {k u l}     → NfNeutral Γ  k → Nf Γ u → NfNeutral Γ  (k ∘ u ^ l)
    natrecₙ : ∀ {l C c g k} →  Nf (Γ ∙ ℕ ^ [ ! , ι ⁰ ]) C → Nf Γ c → Nf Γ g → NfNeutral Γ  k → NfNeutral Γ  (natrec l C c g k)
    Idₙ : ∀ {A t u} → NfNeutral Γ  A → Nf Γ t → Nf Γ u → NfNeutral Γ  (Id A t u)
    Idℕₙ : ∀ {t u} → NfNeutral Γ  t → Nf Γ u → NfNeutral Γ  (Id ℕ t u)
    Idℕ0ₙ : ∀ {u} → NfNeutral Γ  u → NfNeutral Γ  (Id ℕ zero u)
    IdℕSₙ : ∀ {t u} → Nf Γ t → NfNeutral Γ  u → NfNeutral Γ  (Id ℕ (suc t) u)
    IdUₙ : ∀ {t u l} → NfNeutral Γ  t → Nf Γ u → NfNeutral Γ  (Id (U l) t u)
    IdUℕₙ : ∀ {u l} → NfNeutral Γ  u → NfNeutral Γ  (Id (U l) ℕ u)
    IdUΠₙ : ∀ {A rA lA B lB l u} → Nf Γ A → Nf (Γ ∙ A ^ [ rA , ι lA ]) B → NfNeutral Γ  u
                                 → NfNeutral Γ  (Id (U l) (Π A ^ rA ° lA ▹ B ° lB ° l) u)
    castₙ : ∀ {l A B e t} → NfNeutral Γ  A → Nf Γ B → Nf Γ t → NfNeutral Γ  (cast l A B e t)
    castℕₙ : ∀ {l B e t} → NfNeutral Γ  B → Nf Γ t → NfNeutral Γ  (cast l ℕ B e t)
    castΠₙ : ∀ {l A rA lA P lP B e t} → Nf Γ A → Nf (Γ ∙ A ^ [ rA , ι lA ]) P → NfNeutral Γ  B → Nf Γ t
                                      → NfNeutral Γ (cast l (Π A ^ rA ° lA ▹ P ° lP ° l) B e t)
    castℕℕₙ : ∀ {l e t} → NfNeutral Γ  t → NfNeutral Γ  (cast l ℕ ℕ e t)
    castℕΠₙ : ∀ {l A rA B e t} → Nf Γ A → Nf (Γ ∙ A ^ [ rA , ι ⁰ ]) B → Nf Γ t → NfNeutral Γ  (cast l ℕ (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° l) e t)
    castΠℕₙ : ∀ {l A rA B e t} → Nf Γ A → Nf (Γ ∙ A ^ [ rA , ι ⁰ ]) B → Nf Γ t → NfNeutral Γ  (cast l (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° l) ℕ e t)
    castΠΠ%!ₙ : ∀ {l A B A' B' e t} → Nf Γ A → Nf (Γ ∙ A ^ [ % , ι ⁰ ]) B → Nf Γ A' → Nf (Γ ∙ A' ^ [ ! , ι ⁰ ]) B' → Nf Γ t
                                    → NfNeutral Γ  (cast l (Π A ^ % ° ⁰ ▹ B ° ⁰ ° l) (Π A' ^ ! ° ⁰ ▹ B' ° ⁰ ° l) e t)
    castΠΠ!%ₙ : ∀ {l A B A' B' e t} → Nf Γ A → Nf (Γ ∙ A ^ [ ! , ι ⁰ ]) B → Nf Γ A' → Nf (Γ ∙ A' ^ [ % , ι ⁰ ]) B' → Nf Γ t
                                    → NfNeutral Γ  (cast l (Π A ^ ! ° ⁰ ▹ B ° ⁰ ° l) (Π A' ^ % ° ⁰ ▹ B' ° ⁰ ° l) e t)
    Emptyrecₙ : ∀ {l lEmpty A e} → Nf Γ A → NfNeutral Γ  (Emptyrec l lEmpty A e)

  data Nf (Γ : Con Term) : Term → Set where

    Uₙ    : ∀ {r l} → Nf Γ (Univ r l)
    Πₙ    : ∀ {A r lA B lB l} → Nf Γ A → Nf (Γ ∙ A ^ [ r , ι lA ]) B → Nf Γ (Π A ^ r ° lA ▹ B ° lB ° l)
    ∃ₙ    : ∀ {l A B} → Γ ⊢ A ^ [ % , ι l ] → Nf Γ A → Nf (Γ ∙ A ^ [ % , ι l ]) B → Nf Γ (∃ A ▹ B)
    ℕₙ    : Nf Γ ℕ
    Emptyₙ : ∀ {l} → Nf Γ (Empty l)

    lamₙ  : ∀ {A l rF lF t} → Γ ⊢ A ^ [ rF , ι lF ] → Nf (Γ ∙ A ^ [ rF , ι lF ]) t → Nf Γ (lam A ▹ t ^ l)
    zeroₙ : Nf Γ zero
    sucₙ  : ∀ {t} → Nf Γ t → Nf Γ (suc t)

    ne   : ∀ {n} → NfNeutral Γ  n → Nf Γ n
    sprop   : ∀ {t A l} → Γ ⊢ t ∷ A ^ [ % , l ] → Nf Γ t

NfNeutralNeutral :  ∀ {t Γ} → NfNeutral Γ t → Neutral t
NfNeutralNeutral (var n) = var n
NfNeutralNeutral (∘ₙ X x) = ∘ₙ (NfNeutralNeutral X)
NfNeutralNeutral (natrecₙ x x₁ x₂ X) = natrecₙ (NfNeutralNeutral X)
NfNeutralNeutral (Idₙ X x x₁) = Idₙ (NfNeutralNeutral X)
NfNeutralNeutral (Idℕₙ X x) = Idℕₙ (NfNeutralNeutral X)
NfNeutralNeutral (Idℕ0ₙ X) = Idℕ0ₙ (NfNeutralNeutral X)
NfNeutralNeutral (IdℕSₙ x X) = IdℕSₙ (NfNeutralNeutral X)
NfNeutralNeutral (IdUₙ X x) = IdUₙ (NfNeutralNeutral X)
NfNeutralNeutral (IdUℕₙ X) = IdUℕₙ (NfNeutralNeutral X)
NfNeutralNeutral (IdUΠₙ x x₁ X) = IdUΠₙ (NfNeutralNeutral X)
NfNeutralNeutral (castₙ X x x₁) = castₙ (NfNeutralNeutral X)
NfNeutralNeutral (castℕₙ X x) = castℕₙ (NfNeutralNeutral X)
NfNeutralNeutral (castΠₙ x x₁ X x₂) = castΠₙ (NfNeutralNeutral X)
NfNeutralNeutral (castℕℕₙ X) = castℕℕₙ (NfNeutralNeutral X)
NfNeutralNeutral (castℕΠₙ x x₁ x₂) = castℕΠₙ
NfNeutralNeutral (castΠℕₙ x x₁ x₂) = castΠℕₙ
NfNeutralNeutral (castΠΠ%!ₙ x x₁ x₂ x₃ x₄) = castΠΠ%!ₙ
NfNeutralNeutral (castΠΠ!%ₙ x x₁ x₂ x₃ x₄) = castΠΠ!%ₙ
NfNeutralNeutral (Emptyrecₙ x) = Emptyrecₙ

NfWhnf :  ∀ {Γ t A l} → Γ ⊢ t ∷ A ^ [ ! , l ] → Nf Γ t → Whnf t
NfWhnf ⊢t Uₙ = Uₙ
NfWhnf ⊢t (Πₙ X X₁) = Πₙ
NfWhnf ⊢t (∃ₙ _ X X₁) = ∃ₙ
NfWhnf ⊢t ℕₙ = ℕₙ
NfWhnf ⊢t Emptyₙ = Emptyₙ
NfWhnf ⊢t (lamₙ _ X) = lamₙ
NfWhnf ⊢t zeroₙ = zeroₙ
NfWhnf ⊢t (sucₙ X) = sucₙ
NfWhnf ⊢t (ne x) = ne (NfNeutralNeutral x)
NfWhnf ⊢t (sprop ⊢t') = let !≡% , _  = type-uniq ⊢t ⊢t' in ⊥-elim (!≢% !≡%)

NfΠinversion :  ∀ {F G rF lF lG lΠ Γ} → Nf Γ (Π F ^ rF ° lF ▹ G ° lG ° lΠ ) → Nf Γ F × Nf (Γ ∙ F ^ [ rF , ι lF ]) G
NfΠinversion (Πₙ X Y) = X , Y
NfΠinversion (sprop ⊢t) =
  let _ , _ , _ , _ , _ , _ , e = inversion-Π ⊢t
      %≡! , _ = typelevel-injectivity e
  in ⊥-elim (!≢% (PE.sym %≡!))

NfΠA′ : ∀ {A F G rF lF lG lΠ Γ l} ([Π] : Γ ⊩⟨ l ⟩Π Π F ^ rF ° lF ▹ G ° lG ° lΠ ^[ ! , lΠ ] )
    → Γ ⊩⟨ l ⟩ Π F ^ rF ° lF ▹ G ° lG ° lΠ ≡ A ^ [ ! ,  ι lΠ ] / (Π-intr [Π])
    → Nf Γ A
    → ∃₂ λ H E → Nf Γ H × Nf (Γ ∙ H ^ [ rF , ι lF ]) E × A PE.≡ Π H ^ rF ° lF ▹ E ° lG ° lΠ
NfΠA′ (noemb (Πᵣ 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′]) nfA =
    let _ , rF≡rF′ , lF≡lF′ , _ , lG≡lG′ , _ = Π-PE-injectivity (whnfRed* (red D) Πₙ)
        X = whnfRed* D′ (NfWhnf (un-univ (redFirst* D′)) nfA)
        NfH , NfE =  NfΠinversion (PE.subst (Nf _) X nfA)
    in F′ , G′ , NfH , PE.subst₂ (λ r l →  Nf (_ ∙ _ ^ [ r , ι l ]) _) (PE.sym rF≡rF′) (PE.sym lF≡lF′) NfE ,
       PE.subst (λ r → _ PE.≡ Π _ ^ r ° _ ▹ _ ° _ ° _) (PE.sym rF≡rF′)
         (PE.subst (λ l → _ PE.≡ Π _ ^ _ ° l ▹ _ ° _ ° _) (PE.sym lF≡lF′)
           (PE.subst (λ l → _ PE.≡ Π _ ^ _ ° _ ▹ _ ° l ° _) (PE.sym lG≡lG′) X))
NfΠA′ (emb emb< [Π]) [Π≡A] nfA = NfΠA′ [Π] [Π≡A] nfA
NfΠA′ (emb ∞< [Π]) [Π≡A] nfA = NfΠA′ [Π] [Π≡A] nfA

NfΠA : ∀ {A F G rF lF lG lΠ Γ}
    → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° lΠ ≡ A ^ [ ! ,  ι lΠ ]
    → Nf Γ A
    → ∃₂ λ H E → Nf Γ H × Nf (Γ ∙ H ^ [ rF , ι lF ]) E × A PE.≡ Π H ^ rF ° lF ▹ E ° lG ° lΠ
NfΠA {A} Π≡A whnfA  =
  let X = reducibleEq Π≡A
      [Π] = proj₁ X
      [A] = proj₁ (proj₂ X)
      [Π≡A] = proj₂ (proj₂ X)
  in NfΠA′ (Π-elim [Π]) (irrelevanceEq [Π] (Π-intr (Π-elim [Π])) [Π≡A]) whnfA

mutual

  convNfNeutral :  ∀ {Γ Δ t A r} → ⊢ Γ ≡ Δ → Γ ⊢ t ∷ A ^ r → NfNeutral Γ t → NfNeutral Δ t
  convNfNeutral Γ≡Δ ⊢t (var n) = var n
  convNfNeutral Γ≡Δ ⊢t (∘ₙ X x) =
    let F , rF , lF , G , lG , rG , ⊢k , ⊢u , _ , erl = inversion-app ⊢t
    in ∘ₙ (convNfNeutral Γ≡Δ ⊢k X) (convNf Γ≡Δ ⊢u x)
  convNfNeutral Γ≡Δ ⊢t (natrecₙ x x₁ x₂ X) =
    let _ , ⊢x , ⊢x₁ , ⊢x₂ , ⊢X , _ = inversion-natrec ⊢t
    in natrecₙ (convNf (Γ≡Δ ∙ refl (univ (ℕⱼ (wfTerm ⊢t)))) (un-univ ⊢x) x) (convNf Γ≡Δ ⊢x₁ x₁)
               (convNf Γ≡Δ ⊢x₂ x₂) (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (Idₙ X x x₁) =
    let _ , ⊢X , ⊢x , ⊢x₁ , _ = inversion-Id ⊢t
    in Idₙ (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x x) (convNf Γ≡Δ ⊢x₁ x₁)
  convNfNeutral Γ≡Δ ⊢t (Idℕₙ X x) =
    let _ , _ , ⊢X , ⊢x , _ = inversion-Id ⊢t
    in Idℕₙ (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x x)
  convNfNeutral Γ≡Δ ⊢t (Idℕ0ₙ X) =
    let _ , _ , _ , ⊢X , _ = inversion-Id ⊢t
    in Idℕ0ₙ (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (IdℕSₙ x X) =
    let _ , _ , ⊢x , ⊢X , _ = inversion-Id ⊢t
    in IdℕSₙ (convNf Γ≡Δ (proj₁ (inversion-suc ⊢x)) x) (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (IdUₙ X x) =
    let _ , _ , ⊢X , ⊢x , _ = inversion-Id ⊢t
    in IdUₙ (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x x)
  convNfNeutral Γ≡Δ ⊢t (IdUℕₙ X) =
    let _ , _ , _ , ⊢X , _ = inversion-Id ⊢t
    in IdUℕₙ (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (IdUΠₙ x x₁ X) =
    let _ , _ , ⊢Π , ⊢X , _ = inversion-Id ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
    in IdUΠₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁) (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (castₙ X x x₁) =
    let _ , ⊢X , ⊢x , ⊢e , ⊢x₁ , _ = inversion-cast ⊢t
    in castₙ (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x x) (convNf Γ≡Δ ⊢x₁ x₁)
  convNfNeutral Γ≡Δ ⊢t (castℕₙ X x) =
    let _ , _ , ⊢X , ⊢e , ⊢x , _ = inversion-cast ⊢t
    in castℕₙ (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x x)
  convNfNeutral Γ≡Δ ⊢t (castΠₙ x x₁ X x₂) =
    let _ , ⊢Π , ⊢X , ⊢e , ⊢x₂ , _ = inversion-cast ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
    in castΠₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁) (convNfNeutral Γ≡Δ ⊢X X) (convNf Γ≡Δ ⊢x₂ x₂)
  convNfNeutral Γ≡Δ ⊢t (castℕℕₙ X) =
    let _ , _ , _ , _ , ⊢X , _ = inversion-cast ⊢t
    in castℕℕₙ (convNfNeutral Γ≡Δ ⊢X X)
  convNfNeutral Γ≡Δ ⊢t (castℕΠₙ x x₁ x₂) =
    let _ , _ , ⊢Π , ⊢e , ⊢x₂ , _ = inversion-cast ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
    in castℕΠₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁) (convNf Γ≡Δ ⊢x₂ x₂)
  convNfNeutral Γ≡Δ ⊢t (castΠℕₙ x x₁ x₂) =
    let _ , ⊢Π , _ , ⊢e , ⊢x₂ , _ = inversion-cast ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
    in castΠℕₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁) (convNf Γ≡Δ ⊢x₂ x₂)
  convNfNeutral Γ≡Δ ⊢t (castΠΠ%!ₙ x x₁ x₂ x₃ x₄) =
    let _ , ⊢Π , ⊢Π' , ⊢e , ⊢x₄ , _ = inversion-cast ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
        _ , _ , _ , ⊢F' , ⊢G' , _ = inversion-Π ⊢Π'
    in castΠΠ%!ₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁)
               (convNf Γ≡Δ ⊢F' x₂) (convNf (Γ≡Δ ∙ refl (univ ⊢F')) ⊢G' x₃) (convNf Γ≡Δ ⊢x₄ x₄)
  convNfNeutral Γ≡Δ ⊢t (castΠΠ!%ₙ x x₁ x₂ x₃ x₄) =
    let _ , ⊢Π , ⊢Π' , ⊢e , ⊢x₄ , _ = inversion-cast ⊢t
        _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢Π
        _ , _ , _ , ⊢F' , ⊢G' , _ = inversion-Π ⊢Π'
    in castΠΠ!%ₙ (convNf Γ≡Δ ⊢F x) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G x₁)
               (convNf Γ≡Δ ⊢F' x₂) (convNf (Γ≡Δ ∙ refl (univ ⊢F')) ⊢G' x₃) (convNf Γ≡Δ ⊢x₄ x₄)
  convNfNeutral Γ≡Δ ⊢t (Emptyrecₙ x) =
    let _ , ⊢x , _ = inversion-Emptyrec ⊢t
    in Emptyrecₙ (convNf Γ≡Δ (un-univ ⊢x) x)

  convNf :  ∀ {Γ Δ t A r} → ⊢ Γ ≡ Δ → Γ ⊢ t ∷ A ^ r → Nf Γ t → Nf Δ t
  convNf Γ≡Δ ⊢t Uₙ = Uₙ
  convNf Γ≡Δ ⊢t (Πₙ X X₁) =
    let _ , _ , _ , ⊢F , ⊢G , _ = inversion-Π ⊢t
    in Πₙ (convNf Γ≡Δ ⊢F X) (convNf (Γ≡Δ ∙ refl (univ ⊢F)) ⊢G X₁)
  convNf Γ≡Δ ⊢t (∃ₙ ⊢A X X₁) =
    let l , ⊢F , ⊢G , _ , erl = inversion-∃ ⊢t
        _ , _  , erl  = type-uniq (un-univ ⊢A) ⊢F
        _ , elA = Uinjectivity erl
    in ∃ₙ (stability Γ≡Δ ⊢A) (convNf Γ≡Δ ⊢F X) (convNf (Γ≡Δ ∙ refl (⊢A))
          (PE.subst (λ l → _ ∙ _ ^ [ % , ι l ] ⊢ _ ∷ SProp l ^ [ ! , next l ]) (PE.sym elA) ⊢G) X₁)
  convNf Γ≡Δ ⊢t ℕₙ = ℕₙ
  convNf Γ≡Δ ⊢t Emptyₙ = Emptyₙ
  convNf Γ≡Δ ⊢t (lamₙ ⊢F X) =
    let rF , lF , G , rG , lG , ⊢F' , ⊢t' , _ , erl = inversion-lam ⊢t
        er , el = typelevel-injectivity erl
        _ , _  , erl  = type-uniq (un-univ ⊢F) (un-univ ⊢F')
        erF , elF = Uinjectivity erl
    in lamₙ (stability Γ≡Δ  ⊢F) (convNf (Γ≡Δ ∙ refl ⊢F)
            (PE.subst₂ (λ r l → _ ∙ _ ^ [ r , ι l ] ⊢ _ ∷ G ^ [ rG , ι lG ]) (PE.sym erF) (PE.sym elF) ⊢t')  X)
  convNf Γ≡Δ ⊢t zeroₙ = zeroₙ
  convNf Γ≡Δ ⊢t (sucₙ X) = sucₙ (convNf Γ≡Δ (proj₁ (inversion-suc ⊢t)) X )
  convNf Γ≡Δ ⊢t (ne x) = ne (convNfNeutral Γ≡Δ ⊢t x)
  convNf Γ≡Δ ⊢t (sprop x) = sprop (stabilityTerm Γ≡Δ x)

mutual
  fullRedNe : ∀ {t A l Γ} → Γ ⊢ t ~ t ↑! A ^ l → ∃ λ u → NfNeutral Γ u × Γ ⊢ t ≡ u ∷ A ^ [ ! , l ]
  fullRedNe (var-refl x _) = var _ , var _ , refl x
  fullRedNe (app-cong {rF = !} {lΠ = lΠ} t u) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
        u′ , nfU′ , u≡u′ = fullRedTerm u
    in  (t′ ∘ u′ ^ lΠ) ,
        ∘ₙ nfT′ nfU′ ,
        app-cong t≡t′ u≡u′
  fullRedNe (app-cong {t = a} {rF = %} {lΠ = lΠ} t (%~↑ ⊢a ⊢a')) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
    in  (t′ ∘ a ^ lΠ) ,
        ∘ₙ nfT′ (sprop ⊢a) ,
        app-cong t≡t′ (proof-irrelevance ⊢a ⊢a')
  fullRedNe (natrec-cong {lF = l} C z s n) =
    let C′ , nfC′ , C≡C′ = fullRed C
        z′ , nfZ′ , z≡z′ = fullRedTerm z
        s′ , nfS′ , s≡s′ = fullRedTerm s
        n′ , nfN′ , n≡n′ = fullRedNe′ n
    in  natrec l C′ z′ s′ n′
     , natrecₙ nfC′ nfZ′ nfS′ nfN′
     ,  natrec-cong C≡C′ z≡z′ s≡s′ n≡n′
  fullRedNe (Emptyrec-cong {k = k} {ll = l} {lEmpty = lEmpty} C (%~↑ ⊢e ⊢e')) =
    let C′ , nfC′ , C≡C′ = fullRed C
    in  Emptyrec l lEmpty C′ k , Emptyrecₙ nfC′
     ,  Emptyrec-cong C≡C′ ⊢e ⊢e'
  fullRedNe (Id-cong A t u) =
    let A′ , nfA′ , A≡A′ = fullRedNe′ A
        t′ , nfT′ , t≡t′ = fullRedTerm t
        u′ , nfU′ , u≡u′ = fullRedTerm u
    in Id A′ t′ u′ , Idₙ nfA′ nfT′ nfU′ , Id-cong A≡A′ t≡t′ u≡u′
  fullRedNe (Id-ℕ t u) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
        u′ , nfU′ , u≡u′ = fullRedTerm u
    in Id ℕ t′ u′ , Idℕₙ  nfT′ nfU′ , Id-cong (refl (ℕⱼ (wfEqTerm u≡u′))) t≡t′ u≡u′
  fullRedNe (Id-ℕ0 t) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
    in Id ℕ zero t′ , Idℕ0ₙ  nfT′ , Id-cong (refl (ℕⱼ (wfEqTerm t≡t′))) (refl (zeroⱼ (wfEqTerm t≡t′))) t≡t′
  fullRedNe (Id-ℕS t u) =
    let t′ , nfT′ , t≡t′ = fullRedTerm t
        u′ , nfU′ , u≡u′ = fullRedNe′ u
    in Id ℕ (suc t′) u′ , IdℕSₙ nfT′ nfU′ , Id-cong (refl (ℕⱼ (wfEqTerm u≡u′))) (suc-cong t≡t′) u≡u′
  fullRedNe (Id-U t u) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
        u′ , nfU′ , u≡u′ = fullRedTerm u
    in Id (U _) t′ u′ , IdUₙ nfT′ nfU′ , Id-cong (refl (univ 0<1 (wfEqTerm u≡u′))) t≡t′ u≡u′
  fullRedNe (Id-Uℕ t) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
    in Id (U _) ℕ t′ , IdUℕₙ nfT′ , Id-cong (refl (univ 0<1 (wfEqTerm t≡t′))) (refl (ℕⱼ (wfEqTerm t≡t′))) t≡t′
  fullRedNe (Id-UΠ {rA = rA} A t) =
    let t′ , nfT′ , t≡t′ = fullRedNe′ t
        A′ , nfA′ , A≡A′ = fullRedTerm A
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
    in Id (U _) (Π H ^ rA ° ⁰ ▹ E ° ⁰ ° ⁰) t′ ,
       IdUΠₙ NfH NfE nfT′ ,
       Id-cong (refl (univ 0<1 (wfEqTerm t≡t′))) (PE.subst (λ A → _ ⊢ _ ≡ A ∷ U ⁰ ^ _) Π≡HE A≡A′) t≡t′
  fullRedNe (cast-cong {e = e} {e' = e'} x x₁ x₂ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedNe′ x
        B′ , nfB′ , B≡B′ = fullRedTerm x₁
        t′ , nft′ , t≡t′ = fullRedTerm x₂
    in cast ⁰ A′ B′ e t′ , castₙ nfA′ nfB′ nft′ ,
       cast-cong A≡A′ B≡B′ t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) A≡A′ B≡B′)))
  fullRedNe (cast-ℕ {e = e} {e' = e'} x x₁ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedNe′ x
        t′ , nft′ , t≡t′ = fullRedTerm x₁
    in cast ⁰ _ A′ e t′ , castℕₙ nfA′ nft′ ,
       cast-cong (refl (ℕⱼ (wfTerm ⊢e)) ) A≡A′ t≡t′ ⊢e
                 (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) (refl (ℕⱼ (wfTerm ⊢e))) A≡A′)))
  fullRedNe (cast-ℕℕ {e = e} {e' = e'} x ⊢e ⊢e') =
      let t′ , nft′ , t≡t′ = fullRedNe′ x
    in cast ⁰ _ _ e t′ , castℕℕₙ nft′ ,
       cast-cong (refl (ℕⱼ (wfTerm ⊢e)) ) (refl (ℕⱼ (wfTerm ⊢e)) ) t≡t′ ⊢e ⊢e
  fullRedNe (cast-Π {e = e} {e' = e'} x x₁ x₂ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedTerm x
        B′ , nfB′ , B≡B′ = fullRedNe′ x₁
        t′ , nft′ , t≡t′ = fullRedTerm x₂
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
        nfCast = castΠₙ NfH NfE nfB′ nft′
    in cast ⁰ A′ B′ e t′ , PE.subst (λ A →  NfNeutral _ (cast ⁰ A _ _ _)) (PE.sym Π≡HE) nfCast ,
       cast-cong A≡A′ B≡B′ t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) A≡A′ B≡B′)))
  fullRedNe (cast-Πℕ {e = e} {e' = e'} x x₁ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedTerm x
        t′ , nft′ , t≡t′ = fullRedTerm x₁
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
        nfCast = castΠℕₙ NfH NfE nft′
    in cast ⁰ A′ ℕ e t′ , PE.subst (λ A →  NfNeutral _ (cast ⁰ A _ _ _)) (PE.sym Π≡HE) nfCast ,
       cast-cong A≡A′ (refl (ℕⱼ (wfTerm ⊢e))) t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) A≡A′ (refl (ℕⱼ (wfTerm ⊢e))))))
  fullRedNe (cast-ℕΠ {e = e} {e' = e'} x x₁ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedTerm x
        t′ , nft′ , t≡t′ = fullRedTerm x₁
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
        nfCast = castℕΠₙ NfH NfE nft′
    in cast ⁰ ℕ A′ e t′ , PE.subst (λ A →  NfNeutral _ (cast ⁰ _ A _ _)) (PE.sym Π≡HE) nfCast ,
       cast-cong (refl (ℕⱼ (wfTerm ⊢e))) A≡A′ t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) (refl (ℕⱼ (wfTerm ⊢e))) A≡A′ )))
  fullRedNe (cast-ΠΠ%! {e = e} {e' = e'} x x₁ x₂ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedTerm x
        B′ , nfB′ , B≡B′ = fullRedTerm x₁
        t′ , nft′ , t≡t′ = fullRedTerm x₂
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
        H' , E' , NfH' , NfE' , Π≡HE' = NfΠA (univ B≡B′) nfB′
        nfCast = castΠΠ%!ₙ NfH NfE NfH' NfE' nft′
    in cast ⁰ A′ B′ e t′ , PE.subst₂ (λ A B →  NfNeutral _ (cast ⁰ A B _ _)) (PE.sym Π≡HE) (PE.sym Π≡HE') nfCast ,
       cast-cong A≡A′ B≡B′ t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) A≡A′ B≡B′ )))
  fullRedNe (cast-ΠΠ!% {e = e} {e' = e'} x x₁ x₂ ⊢e ⊢e') =
    let A′ , nfA′ , A≡A′ = fullRedTerm x
        B′ , nfB′ , B≡B′ = fullRedTerm x₁
        t′ , nft′ , t≡t′ = fullRedTerm x₂
        H , E , NfH , NfE , Π≡HE = NfΠA (univ A≡A′) nfA′
        H' , E' , NfH' , NfE' , Π≡HE' = NfΠA (univ B≡B′) nfB′
        nfCast = castΠΠ!%ₙ NfH NfE NfH' NfE' nft′
    in cast ⁰ A′ B′ e t′ , PE.subst₂ (λ A B →  NfNeutral _ (cast ⁰ A B _ _)) (PE.sym Π≡HE) (PE.sym Π≡HE') nfCast ,
       cast-cong A≡A′ B≡B′ t≡t′ ⊢e (conv ⊢e (univ (Id-cong (refl (univ 0<1 (wfTerm ⊢e)) ) A≡A′ B≡B′ )))

  fullRedNe′ : ∀ {t A rA Γ} → Γ ⊢ t ~ t ↓! A ^ rA → ∃ λ u → NfNeutral Γ u × Γ ⊢ t ≡ u ∷ A ^ [ ! , rA ]
  fullRedNe′ ([~] A D whnfB k~l) =
    let u , nf , t≡u = fullRedNe k~l
    in u , nf , conv t≡u (subset* D)

  fullRed : ∀ {A rA Γ} → Γ ⊢ A [conv↑] A ^ rA → ∃ λ B → Nf Γ B × Γ ⊢ A ≡ B ^ rA
  fullRed ([↑] A′ B′ D D′ whnfA′ whnfB′ A′<>B′)
    rewrite whrDet* (D , whnfA′) (D′ , whnfB′) =
    let B″ , nf , B′≡B″ = fullRed′ A′<>B′
    in  B″ , nf , trans (subset* D′) B′≡B″

  fullRed′ : ∀ {A rA Γ} → Γ ⊢ A [conv↓] A ^ rA → ∃ λ B → Nf Γ B × Γ ⊢ A ≡ B ^ rA
  fullRed′ (U-refl {r = r} _ ⊢Γ) = Univ r ¹  , Uₙ , refl (Uⱼ ⊢Γ)
  fullRed′ (univ x) =
    let u , Nfu , u≡u = fullRedTerm′ x
    in u , Nfu , univ u≡u

  fullRedTerm : ∀ {t A l Γ} → Γ ⊢ t [conv↑] t ∷ A ^ l → ∃ λ u → Nf Γ u × Γ ⊢ t ≡ u ∷ A ^ [ ! , l ]
  fullRedTerm ([↑]ₜ B t′ u′ D d d′ whnfB whnft′ whnfu′ t<>u)
    rewrite whrDet*Term (d , whnft′) (d′ , whnfu′) =
    let u″ , nf , u′≡u″ = fullRedTerm′ t<>u
    in  u″ , nf , conv (trans (subset*Term d′) u′≡u″) (sym (subset* D))

  fullRedTerm′ : ∀ {t A l Γ} → Γ ⊢ t [conv↓] t ∷ A ^ l → ∃ λ u → Nf Γ u × Γ ⊢ t ≡ u ∷ A ^ [ ! , l ]
  fullRedTerm′ (U-refl {r = r} _ ⊢Γ) = Univ r ⁰ , Uₙ , refl (univ 0<1 ⊢Γ)
  fullRedTerm′ (ne A) =
    let B , nf , A≡B = fullRedNe′ A
    in  B , ne nf , A≡B
  fullRedTerm′ (ℕ-refl ⊢Γ) = ℕ , ℕₙ , refl (ℕⱼ ⊢Γ)
  fullRedTerm′ (Empty-refl PE.refl ⊢Γ) = Empty _ , Emptyₙ , refl (Emptyⱼ ⊢Γ)
  fullRedTerm′ (Π-cong {rF = rF} PE.refl PE.refl PE.refl PE.refl l< l<' ⊢F F G) =
    let F′ , nfF′ , F≡F′ = fullRedTerm F
        G′ , nfG′ , G≡G′ = fullRedTerm G
    in Π F′ ^ rF ° _ ▹ G′ ° _ ° _  , Πₙ nfF′ (convNf (reflConEq (wf ⊢F) ∙ univ F≡F′) (proj₂ (proj₂ (syntacticEqTerm G≡G′))) nfG′)
    ,  Π-cong l< l<' ⊢F F≡F′ G≡G′
  fullRedTerm′ (∃-cong PE.refl ⊢F F G) =
    let F′ , nfF′ , F≡F′ = fullRedTerm F
        G′ , nfG′ , G≡G′ = fullRedTerm G
    in ∃ F′ ▹ G′ , ∃ₙ (univ (proj₂ (proj₂ (syntacticEqTerm F≡F′)))) nfF′
         (convNf (reflConEq (wf ⊢F) ∙ univ F≡F′) (proj₂ (proj₂ (syntacticEqTerm G≡G′))) nfG′)
     , ∃-cong ⊢F F≡F′ G≡G′
  fullRedTerm′  (ℕ-ins t) =
    let u , nf , t≡u = fullRedNe′ t
    in  u , ne nf , t≡u
  fullRedTerm′ (ne-ins ⊢t _ _ t) =
    let u , nfU , t≡u = fullRedNe′ t
        _ , ⊢t∷M , _ = syntacticEqTerm t≡u
        _ , neT , _ = ne~↓! t
    in  u , ne nfU , conv t≡u (proj₂ (neTypeEq neT ⊢t∷M ⊢t))
  fullRedTerm′ (zero-refl ⊢Γ) = zero , zeroₙ , refl (zeroⱼ ⊢Γ)
  fullRedTerm′ (suc-cong t) =
    let u , nf , t≡u = fullRedTerm t
    in  suc u , sucₙ nf , suc-cong t≡u
  fullRedTerm′ (η-eq {F = F} {l = l} l< l<' ⊢F ⊢t _ _ _ t∘0) =
    let u , nf , t∘0≡u = fullRedTerm t∘0
        _ , _ , ⊢u = syntacticEqTerm t∘0≡u
        ΓF⊢ = wf ⊢F ∙ ⊢F
        wk⊢F = wk (step id) ΓF⊢ ⊢F
        ΓFF'⊢ = ΓF⊢ ∙ wk⊢F
        wk⊢u = wkTerm (lift (step id)) ΓFF'⊢ ⊢u
        λu∘0 = (lam _ ▹ (U.wk (lift (step id)) u) ^ _) ∘ var 0 ^ l
    in  lam _ ▹ u ^ _ ,
        lamₙ ⊢F nf
     ,  η-eq l< l<' ⊢F ⊢t (lamⱼ l< l<' ⊢F ⊢u)
             (trans t∘0≡u (PE.subst₂ (λ x y → _ ⊢ x ≡ λu∘0 ∷ y ^ _)
                                     (wkSingleSubstId u) (wkSingleSubstId _)
                                     (sym (β-red l< l<' wk⊢F wk⊢u (var ΓF⊢ here)))))


open import FRP.JS.Behaviour using ( Beh ; [_] ; map )
open import FRP.JS.Event using ( Evt ; tag )
open import FRP.JS.DOM using
  ( DOM ; click ; element ; text ; _++_ ; element+ ; text+ ; listen+ ; _+++_ )
open import FRP.JS.RSet using ( RSet ; ⟦_⟧ ; ⟨_⟩ ; _⇒_ )
open import FRP.JS.Product using ( _∧_ ; _,_ )
open import FRP.JS.Demo.Calculator.Model using
  ( Button ; State ; digit ; op ; clear ; eq ; plus ; minus ; times ; button$ ; state$ ; model )

module FRP.JS.Demo.Calculator.View where

button : ∀ {w} → Button → ⟦ Beh (DOM w) ∧ Evt ⟨ Button ⟩ ⟧
button b = listen+ click (λ _ → b) (element+ "button" (text+ [ button$ b ]))

keypad : ∀ {w} → ⟦ Beh (DOM w) ∧ Evt ⟨ Button ⟩ ⟧
keypad =
  element+ "div" (button (digit 7) +++ button (digit 8)  +++ button (digit 9) ) +++
  element+ "div" (button (digit 4) +++ button (digit 5)  +++ button (digit 6) ) +++
  element+ "div" (button (digit 1) +++ button (digit 2)  +++ button (digit 3) ) +++
  element+ "div" (button (op eq)   +++ button (digit 0)  +++ button clear     ) +++
  element+ "div" (button (op plus) +++ button (op minus) +++ button (op times))

display : ∀ {w} → ⟦ Beh ⟨ State ⟩ ⇒ Beh (DOM w) ⟧
display σ = element "div" (text (map state$ σ))

view : ∀ {w} → ⟦ Beh (DOM w) ⟧
view with keypad
... | (dom , evt) = display (model evt) ++ dom


module L0E where

open import Common

-- Syntax

data Conj : Set  where
  and or : Conj

data N : Set where
  Saddie Liz Hank : N

data Vᵢ : Set where
  snores sleeps is-boring : Vᵢ

data Vₜ : Set where
  loves hates is-taller-than : Vₜ

data Neg : Set where
  it-is-not-the-case-that : Neg

data VP : Set where
  iv : Vᵢ -> VP
  tv : Vₜ -> N -> VP

data S : Set where
 sconjs : S -> Conj -> S -> S
 negs : Neg -> S -> S
 predvp : N -> VP -> S

example1 : S
example1 = predvp Saddie (iv snores)

example2 : S
example2 = predvp Liz (iv sleeps)

example3 : S
example3 = negs it-is-not-the-case-that (predvp Hank (iv snores))

example41 example42 : S
example41 = sconjs (sconjs (predvp Saddie (iv sleeps)) or (predvp Liz (iv is-boring))) and (predvp Hank (iv snores))
example42 = sconjs (predvp Saddie (iv sleeps)) or (sconjs (predvp Liz (iv is-boring)) and (predvp Hank (iv snores)))

example5 : S
example5 = negs it-is-not-the-case-that (negs it-is-not-the-case-that (predvp Saddie (iv sleeps)))

-- Semantics

data Individual : Set where
  Anwar_Sadat : Individual
  Queen_Elizabeth_II : Individual
  Henry_Kissinger : Individual

⟦_⟧N : N -> Individual
⟦ Saddie ⟧N = Anwar_Sadat
⟦ Liz ⟧N = Queen_Elizabeth_II
⟦ Hank ⟧N = Henry_Kissinger

⟦_⟧̬̬̌Vᵢ : Vᵢ -> Individual -> Bool
⟦ snores ⟧̬̬̌Vᵢ Anwar_Sadat = true
⟦ snores ⟧̬̬̌Vᵢ Queen_Elizabeth_II = true
⟦ snores ⟧̬̬̌Vᵢ Henry_Kissinger = false

⟦ sleeps ⟧̬̬̌Vᵢ Anwar_Sadat = true
⟦ sleeps ⟧̬̬̌Vᵢ Queen_Elizabeth_II = false
⟦ sleeps ⟧̬̬̌Vᵢ Henry_Kissinger = false

⟦ is-boring ⟧̬̬̌Vᵢ Anwar_Sadat = true
⟦ is-boring ⟧̬̬̌Vᵢ Queen_Elizabeth_II = true
⟦ is-boring ⟧̬̬̌Vᵢ Henry_Kissinger = true

⟦_⟧Vₜ : Vₜ -> Individual -> Individual -> Bool
⟦ loves ⟧Vₜ Anwar_Sadat _ = true
⟦ loves ⟧Vₜ Queen_Elizabeth_II Anwar_Sadat = false
⟦ loves ⟧Vₜ Queen_Elizabeth_II Queen_Elizabeth_II = true
⟦ loves ⟧Vₜ Queen_Elizabeth_II Henry_Kissinger = false
⟦ loves ⟧Vₜ Henry_Kissinger Anwar_Sadat = true
⟦ loves ⟧Vₜ Henry_Kissinger Queen_Elizabeth_II = false
⟦ loves ⟧Vₜ Henry_Kissinger Henry_Kissinger = true

⟦ hates ⟧Vₜ Anwar_Sadat _ = false
⟦ hates ⟧Vₜ Queen_Elizabeth_II Anwar_Sadat = true
⟦ hates ⟧Vₜ Queen_Elizabeth_II Queen_Elizabeth_II = false
⟦ hates ⟧Vₜ Queen_Elizabeth_II Henry_Kissinger = true
⟦ hates ⟧Vₜ Henry_Kissinger Anwar_Sadat = false
⟦ hates ⟧Vₜ Henry_Kissinger Queen_Elizabeth_II = true
⟦ hates ⟧Vₜ Henry_Kissinger Henry_Kissinger = false

⟦ is-taller-than ⟧Vₜ Anwar_Sadat Anwar_Sadat = false
⟦ is-taller-than ⟧Vₜ Anwar_Sadat Queen_Elizabeth_II = true
⟦ is-taller-than ⟧Vₜ Anwar_Sadat Henry_Kissinger = false
⟦ is-taller-than ⟧Vₜ Queen_Elizabeth_II _ = false
⟦ is-taller-than ⟧Vₜ Henry_Kissinger Anwar_Sadat = true
⟦ is-taller-than ⟧Vₜ Henry_Kissinger Queen_Elizabeth_II = true
⟦ is-taller-than ⟧Vₜ Henry_Kissinger Henry_Kissinger = false

⟦_⟧VP : VP -> Individual -> Bool
⟦ iv v ⟧VP = ⟦ v ⟧̬̬̌Vᵢ
⟦ tv v n ⟧VP = ⟦ v ⟧Vₜ ⟦ n ⟧N

⟦_⟧S : S -> Bool
⟦ sconjs x and x₂ ⟧S = ⟦ x ⟧S && ⟦ x₂ ⟧S
⟦ sconjs x or x₂ ⟧S = ⟦ x ⟧S || ⟦ x₂ ⟧S
⟦ negs x x₁ ⟧S = neg ⟦ x₁ ⟧S
⟦ predvp x x₁ ⟧S = ⟦ x₁ ⟧VP ⟦ x ⟧N


{-# OPTIONS --rewriting #-}

module Properties.FunctionTypes where

open import FFI.Data.Either using (Either; Left; Right; mapLR; swapLR; cond)
open import Luau.FunctionTypes using (srcⁿ; src; tgt)
open import Luau.Subtyping using (_<:_; _≮:_; Tree; Language; ¬Language; witness; unknown; never; scalar; function; scalar-function; scalar-function-ok; scalar-function-err; scalar-scalar; function-scalar; function-ok; function-err; left; right; _,_)
open import Luau.Type using (Type; Scalar; nil; number; string; boolean; never; unknown; _⇒_; _∪_; _∩_; skalar)
open import Properties.Contradiction using (CONTRADICTION; ¬; ⊥)
open import Properties.Functions using (_∘_)
open import Properties.Subtyping using (<:-refl; ≮:-refl; <:-trans-≮:; skalar-scalar; <:-impl-⊇; skalar-function-ok; language-comp)
open import Properties.TypeNormalization using (FunType; Normal; never; unknown; _∩_; _∪_; _⇒_; normal; <:-normalize; normalize-<:)

-- Properties of src
function-err-srcⁿ : ∀ {T t} → (FunType T) → (¬Language (srcⁿ T) t) → Language T (function-err t)
function-err-srcⁿ (S ⇒ T) p = function-err p
function-err-srcⁿ (S ∩ T) (p₁ , p₂) = (function-err-srcⁿ S p₁ , function-err-srcⁿ T p₂)

¬function-err-srcᶠ : ∀ {T t} → (FunType T) → (Language (srcⁿ T) t) → ¬Language T (function-err t)
¬function-err-srcᶠ (S ⇒ T) p = function-err p
¬function-err-srcᶠ (S ∩ T) (left p) = left (¬function-err-srcᶠ S p)
¬function-err-srcᶠ (S ∩ T) (right p) = right (¬function-err-srcᶠ T p)

¬function-err-srcⁿ : ∀ {T t} → (Normal T) → (Language (srcⁿ T) t) → ¬Language T (function-err t)
¬function-err-srcⁿ never p = never
¬function-err-srcⁿ unknown (scalar ())
¬function-err-srcⁿ (S ⇒ T) p = function-err p
¬function-err-srcⁿ (S ∩ T) (left p) = left (¬function-err-srcᶠ S p)
¬function-err-srcⁿ (S ∩ T) (right p) = right (¬function-err-srcᶠ T p)
¬function-err-srcⁿ (S ∪ T) (scalar ())

¬function-err-src : ∀ {T t} → (Language (src T) t) → ¬Language T (function-err t)
¬function-err-src {T = S ⇒ T} p = function-err p
¬function-err-src {T = nil} p = scalar-function-err nil
¬function-err-src {T = never} p = never
¬function-err-src {T = unknown} (scalar ())
¬function-err-src {T = boolean} p = scalar-function-err boolean
¬function-err-src {T = number} p = scalar-function-err number
¬function-err-src {T = string} p = scalar-function-err string
¬function-err-src {T = S ∪ T} p = <:-impl-⊇ (<:-normalize (S ∪ T)) _ (¬function-err-srcⁿ (normal (S ∪ T)) p)
¬function-err-src {T = S ∩ T} p = <:-impl-⊇ (<:-normalize (S ∩ T)) _ (¬function-err-srcⁿ (normal (S ∩ T)) p)

src-¬function-errᶠ : ∀ {T t} → (FunType T) → Language T (function-err t) → (¬Language (srcⁿ T) t)
src-¬function-errᶠ (S ⇒ T) (function-err p) = p
src-¬function-errᶠ (S ∩ T) (p₁ , p₂) = (src-¬function-errᶠ S p₁ , src-¬function-errᶠ T p₂)

src-¬function-errⁿ : ∀ {T t} → (Normal T) → Language T (function-err t) → (¬Language (srcⁿ T) t)
src-¬function-errⁿ unknown p = never
src-¬function-errⁿ (S ⇒ T) (function-err p) = p
src-¬function-errⁿ (S ∩ T) (p₁ , p₂) = (src-¬function-errᶠ S p₁ , src-¬function-errᶠ T p₂)
src-¬function-errⁿ (S ∪ T) p = never

src-¬function-err : ∀ {T t} → Language T (function-err t) → (¬Language (src T) t)
src-¬function-err {T = S ⇒ T} (function-err p) = p
src-¬function-err {T = unknown} p = never
src-¬function-err {T = S ∪ T} p = src-¬function-errⁿ (normal (S ∪ T)) (<:-normalize (S ∪ T) _ p)
src-¬function-err {T = S ∩ T} p = src-¬function-errⁿ (normal (S ∩ T)) (<:-normalize (S ∩ T) _ p)

fun-¬scalar : ∀ {S T} (s : Scalar S) → FunType T → ¬Language T (scalar s)
fun-¬scalar s (S ⇒ T) = function-scalar s
fun-¬scalar s (S ∩ T) = left (fun-¬scalar s S)

¬fun-scalar : ∀ {S T t} (s : Scalar S) → FunType T → Language T t → ¬Language S t
¬fun-scalar s (S ⇒ T) function = scalar-function s
¬fun-scalar s (S ⇒ T) (function-ok p) = scalar-function-ok s
¬fun-scalar s (S ⇒ T) (function-err p) = scalar-function-err s
¬fun-scalar s (S ∩ T) (p₁ , p₂) = ¬fun-scalar s T p₂

fun-function : ∀ {T} → FunType T → Language T function
fun-function (S ⇒ T) = function
fun-function (S ∩ T) = (fun-function S , fun-function T)

srcⁿ-¬scalar : ∀ {S T t} (s : Scalar S) → Normal T → Language T (scalar s) → (¬Language (srcⁿ T) t)
srcⁿ-¬scalar s never (scalar ())
srcⁿ-¬scalar s unknown p = never
srcⁿ-¬scalar s (S ⇒ T) (scalar ())
srcⁿ-¬scalar s (S ∩ T) (p₁ , p₂) = CONTRADICTION (language-comp (scalar s) (fun-¬scalar s S) p₁)
srcⁿ-¬scalar s (S ∪ T) p = never

src-¬scalar : ∀ {S T t} (s : Scalar S) → Language T (scalar s) → (¬Language (src T) t)
src-¬scalar {T = nil} s p = never
src-¬scalar {T = T ⇒ U} s (scalar ())
src-¬scalar {T = never} s (scalar ())
src-¬scalar {T = unknown} s p = never
src-¬scalar {T = boolean} s p = never
src-¬scalar {T = number} s p = never
src-¬scalar {T = string} s p = never
src-¬scalar {T = T ∪ U} s p = srcⁿ-¬scalar s (normal (T ∪ U)) (<:-normalize (T ∪ U) (scalar s) p)
src-¬scalar {T = T ∩ U} s p = srcⁿ-¬scalar s (normal (T ∩ U)) (<:-normalize (T ∩ U) (scalar s) p)

srcⁿ-unknown-≮: : ∀ {T U} → (Normal U) → (T ≮: srcⁿ U) → (U ≮: (T ⇒ unknown))
srcⁿ-unknown-≮: never (witness t p q) = CONTRADICTION (language-comp t q unknown)
srcⁿ-unknown-≮: unknown (witness t p q) = witness (function-err t) unknown (function-err p)
srcⁿ-unknown-≮: (U ⇒ V) (witness t p q) = witness (function-err t) (function-err q) (function-err p)
srcⁿ-unknown-≮: (U ∩ V) (witness t p q) = witness (function-err t) (function-err-srcⁿ (U ∩ V) q) (function-err p)
srcⁿ-unknown-≮: (U ∪ V) (witness t p q) = witness (scalar V) (right (scalar V)) (function-scalar V)

src-unknown-≮: : ∀ {T U} → (T ≮: src U) → (U ≮: (T ⇒ unknown))
src-unknown-≮: {U = nil} (witness t p q) = witness (scalar nil) (scalar nil) (function-scalar nil)
src-unknown-≮: {U = T ⇒ U} (witness t p q) = witness (function-err t) (function-err q) (function-err p)
src-unknown-≮: {U = never} (witness t p q) = CONTRADICTION (language-comp t q unknown)
src-unknown-≮: {U = unknown} (witness t p q) = witness (function-err t) unknown (function-err p)
src-unknown-≮: {U = boolean} (witness t p q) = witness (scalar boolean) (scalar boolean) (function-scalar boolean)
src-unknown-≮: {U = number} (witness t p q) = witness (scalar number) (scalar number) (function-scalar number)
src-unknown-≮: {U = string} (witness t p q) = witness (scalar string) (scalar string) (function-scalar string)
src-unknown-≮: {U = T ∪ U} p = <:-trans-≮: (normalize-<: (T ∪ U)) (srcⁿ-unknown-≮: (normal (T ∪ U)) p)
src-unknown-≮: {U = T ∩ U} p = <:-trans-≮: (normalize-<: (T ∩ U)) (srcⁿ-unknown-≮: (normal (T ∩ U)) p)

unknown-src-≮: : ∀ {S T U} → (U ≮: S) → (T ≮: (U ⇒ unknown)) → (U ≮: src T)
unknown-src-≮: (witness t x x₁) (witness (scalar s) p (function-scalar s)) = witness t x (src-¬scalar s p)
unknown-src-≮: r (witness (function-ok (scalar s)) p (function-ok (scalar-scalar s () q)))
unknown-src-≮: r (witness (function-ok (function-ok _)) p (function-ok (scalar-function-ok ())))
unknown-src-≮: r (witness (function-err t) p (function-err q)) = witness t q (src-¬function-err p)

-- Properties of tgt
tgt-function-ok : ∀ {T t} → (Language (tgt T) t) → Language T (function-ok t)
tgt-function-ok {T = nil} (scalar ())
tgt-function-ok {T = T₁ ⇒ T₂} p = function-ok p
tgt-function-ok {T = never} (scalar ())
tgt-function-ok {T = unknown} p = unknown
tgt-function-ok {T = boolean} (scalar ())
tgt-function-ok {T = number} (scalar ())
tgt-function-ok {T = string} (scalar ())
tgt-function-ok {T = T₁ ∪ T₂} (left p) = left (tgt-function-ok p)
tgt-function-ok {T = T₁ ∪ T₂} (right p) = right (tgt-function-ok p)
tgt-function-ok {T = T₁ ∩ T₂} (p₁ , p₂) = (tgt-function-ok p₁ , tgt-function-ok p₂)

function-ok-tgt : ∀ {T t} → Language T (function-ok t) → (Language (tgt T) t)
function-ok-tgt (function-ok p) = p
function-ok-tgt (left p) = left (function-ok-tgt p)
function-ok-tgt (right p) = right (function-ok-tgt p)
function-ok-tgt (p₁ , p₂) = (function-ok-tgt p₁ , function-ok-tgt p₂)
function-ok-tgt unknown = unknown

tgt-never-≮: : ∀ {T U} → (tgt T ≮: U) → (T ≮: (skalar ∪ (never ⇒ U)))
tgt-never-≮: (witness t p q) = witness (function-ok t) (tgt-function-ok p) (skalar-function-ok , function-ok q)

never-tgt-≮: : ∀ {T U} → (T ≮: (skalar ∪ (never ⇒ U))) → (tgt T ≮: U)
never-tgt-≮: (witness (scalar s) p (q₁ , q₂)) = CONTRADICTION (≮:-refl (witness (scalar s) (skalar-scalar s) q₁))
never-tgt-≮: (witness function p (q₁ , scalar-function ()))
never-tgt-≮: (witness (function-ok t) p (q₁ , function-ok q₂)) = witness t (function-ok-tgt p) q₂
never-tgt-≮: (witness (function-err (scalar s)) p (q₁ , function-err (scalar ())))

src-tgtᶠ-<: : ∀ {T U V} → (FunType T) → (U <: src T) → (tgt T <: V) → (T <: (U ⇒ V))
src-tgtᶠ-<: T p q (scalar s) r = CONTRADICTION (language-comp (scalar s) (fun-¬scalar s T) r)
src-tgtᶠ-<: T p q function r = function
src-tgtᶠ-<: T p q (function-ok s) r = function-ok (q s (function-ok-tgt r))
src-tgtᶠ-<: T p q (function-err s) r = function-err (<:-impl-⊇ p s (src-¬function-err r))


-- Liang-Ting, 2022-01-14, issue #5734

{-# OPTIONS --cubical-compatible #-}

open import Agda.Builtin.Unit
open import Agda.Builtin.List
open import Agda.Builtin.Sigma
open import Agda.Builtin.Reflection
  renaming (returnTC to return; bindTC to _>>=_)
open import Agda.Primitive

private variable
  A B : Set _

reverseApp : List A → List A → List A
reverseApp []       ys = ys
reverseApp (x ∷ xs) ys = reverseApp xs (x ∷ ys)

reverse : List A → List A
reverse xs = reverseApp xs []

extend*Context : Telescope → TC A → TC A
extend*Context []              m = m
extend*Context ((s , a) ∷ tel) m = extendContext s a (extend*Context tel m)

pattern vArg x = arg (arg-info visible (modality relevant quantity-ω)) x
pattern visible-relevant-erased = arg-info visible (modality relevant quantity-0)
pattern var₀ x = var x []

Γ : Telescope
Γ =   ("ℓ" , arg visible-relevant-erased (quoteTerm Level))
    ∷ ("A" , vArg (agda-sort (set (var₀ 0))))
    ∷ []

macro
  m : Term → TC ⊤
  m hole = do
    _ ← extend*Context Γ do
      _ ← checkType (var₀ 0) (agda-sort (set (var₀ 1)))
      return tt
    inContext (reverse Γ) do
      return tt

_ : ⊤
_ = m


{-# OPTIONS --cubical-compatible --rewriting --confluence-check #-}

open import Agda.Primitive using (Level; _⊔_; Setω; lzero; lsuc)

module Issue4032 where

infix 4 _≡_

data _≡_ {ℓ : Level} {A : Set ℓ} (a : A) : A → Set ℓ where
  refl : a ≡ a

{-# BUILTIN REWRITE _≡_ #-}

run : ∀ {ℓ} {A B : Set ℓ} → A ≡ B → A → B
run refl x = x

nur : ∀ {ℓ} {A B : Set ℓ} → A ≡ B → B → A
nur refl x = x

convert : ∀ {ℓ} {A B : Set ℓ} (p : A ≡ B) (a : A) → nur p (run p a) ≡ a
convert refl a = refl

trevnoc : ∀ {ℓ} {A B : Set ℓ} (p : A ≡ B) (b : B) → run p (nur p b) ≡ b
trevnoc refl b = refl

ap : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) {a₁ a₂} → a₁ ≡ a₂ → f a₁ ≡ f a₂
ap f refl = refl

transport : ∀ {a b} {A : Set a} (B : A → Set b) {x y : A} (p : x ≡ y) → B x → B y
transport B p = run (ap B p)

apD : ∀ {a b} {A : Set a} {B : A → Set b} (f : (x : A) → B x) {a₁ a₂} → (p : a₁ ≡ a₂) → transport B p (f a₁) ≡ f a₂
apD f refl = refl

ap2 : ∀ {a b c} {A : Set a} {B : A → Set b} {C : Set c} (f : (x : A) → B x → C) {a₁ a₂} (pa : a₁ ≡ a₂) {b₁ b₂} → transport B pa b₁ ≡ b₂ → f a₁ b₁ ≡ f a₂ b₂
ap2 f refl refl = refl

postulate fromIso : ∀ {ℓ} {A B : Set ℓ} (f : A → B) (g : B → A) (η : (x : A) → g (f x) ≡ x) (ε : (x : B) → f (g x) ≡ x) (τ : (x : A) → ap f (η x) ≡ ε (f x)) → A ≡ B
postulate fromIso-refl : ∀ {ℓ} {A : Set ℓ} → fromIso {ℓ} {A} {A} (λ x → x) (λ x → x) (λ x → refl) (λ x → refl) (λ x → refl) ≡ refl
{-# REWRITE fromIso-refl #-}

J : ∀ {ℓ₁ ℓ₂} {A : Set ℓ₁} {a : A} (P : (b : A) → a ≡ b → Set ℓ₂) {b : A} (p : a ≡ b) → P a refl → P b p
J R refl r = r

module foo {-ℓ₁-} {ℓ} {Γ : Set ℓ{-₁-}} {xs ys : Γ} (ps : xs ≡ ys) (tA : Γ → Set ℓ) (tB : (γ : Γ) → tA γ → Set ℓ) where
  module pf where
    iso-f : ((a : tA xs) → (tB xs) a) → ((a : tA ys) → (tB ys) a)
    iso-f var1 var0 = run (ap2 tB ps (trevnoc (ap tA ps) var0)) (var1 (nur (ap tA ps) var0))

    iso-g : ((a : tA ys) → (tB ys) a) → ((a : tA xs) → (tB xs) a)
    iso-g var1 var0 = nur (ap2 tB ps refl) (var1 (run (ap tA ps) var0))

    iso-η : (var0 : _) → iso-g (iso-f var0) ≡ var0
    iso-η var0 = J (λ ys (ps : xs ≡ ys) → (λ var1 → nur (ap2 tB ps refl) (run (ap2 tB ps (trevnoc (ap tA ps) (run (ap tA ps) var1))) (var0 (nur (ap tA ps) (run (ap tA ps) var1))))) ≡ var0) ps refl

    iso-ε : (var0 : _) → iso-f (iso-g var0) ≡ var0
    iso-ε = J (λ ys (ps : xs ≡ ys) → (var0 : (a : tA ys) → tB ys a) → (λ var1 → run (ap2 tB ps (trevnoc (ap tA ps) var1)) (nur (ap2 tB ps refl) (var0 (run (ap tA ps) (nur (ap tA ps) var1))))) ≡ var0) ps (λ var0 → refl)
    iso-τ : (var0 : _) → ap iso-f (iso-η var0) ≡ iso-ε (iso-f var0)
    iso-τ var0 = J
                   (λ ys (ps : xs ≡ ys) →
                      ap
                      (λ var1 var2 →
                         run (ap2 tB ps (trevnoc (ap tA ps) var2))
                         (var1 (nur (ap tA ps) var2)))
                      (J
                       (λ ys₁ ps₁ →
                          (λ var1 →
                             nur (ap2 tB ps₁ refl)
                             (run (ap2 tB ps₁ (trevnoc (ap tA ps₁) (run (ap tA ps₁) var1)))
                              (var0 (nur (ap tA ps₁) (run (ap tA ps₁) var1)))))
                          ≡ var0)
                       ps refl)
                      ≡
                      J
                      (λ ys₁ ps₁ →
                         (var1 : (a : tA ys₁) → tB ys₁ a) →
                         (λ var2 →
                            run (ap2 tB ps₁ (trevnoc (ap tA ps₁) var2))
                            (nur (ap2 tB ps₁ refl)
                             (var1 (run (ap tA ps₁) (nur (ap tA ps₁) var2)))))
                         ≡ var1)
                      ps (λ var1 → refl)
                      (λ var1 →
                         run (ap2 tB ps (trevnoc (ap tA ps) var1))
                         (var0 (nur (ap tA ps) var1))))
                   ps refl

  open pf

  pf : ((a : tA xs) → (tB xs) a) ≡ ((a : tA ys) → (tB ys) a)
  pf = fromIso iso-f iso-g iso-η iso-ε iso-τ

module bar {ℓ} {Γ : Set ℓ} {γ : Γ} {A : Γ → Set ℓ} {B : (γ : Γ) → A γ → Set ℓ} where
  g1 : ((a : A γ) → B γ a) → (a : A γ) → B γ a
  g2 : ((a : A γ) → B γ a) → (a : A γ) → B γ a

  g1 = foo.pf.iso-g {xs = γ} refl A B
  g2 = λ f x → f x

  pfg : g1 ≡ g2
  pfg = refl

  baz : foo.pf {xs = γ} refl A B ≡ refl
  baz = pf
    where
      pf : fromIso {_} {(x : A γ) → B γ x} (λ f x → f x) g2 (foo.pf.iso-η refl A B) (foo.pf.iso-ε refl A B) (foo.pf.iso-τ refl A B) ≡ refl
      pf = refl

{- WAS:
refl !=
fromIso (foo.pf.iso-f refl A B) (foo.pf.iso-g refl A B)
(foo.pf.iso-η refl A B) (foo.pf.iso-ε refl A B)
(foo.pf.iso-τ refl A B)
of type ((x : A γ) → B γ x) ≡ ((x : A γ) → B γ x)
when checking that the expression pf has type
foo.pf refl A B ≡ refl
-}


module Proc where

open import Basics

module ProcDef (U : Set)(T : U -> Set)(Name : U -> Set) where

  LT : U -> Set
  LT a = Lift (T a)

  record Tran (a b : U) : Set where
    field
      upV   : T b -> LT a
      downV : T a -> LT b

  mapLT : {a b : U} -> (T a -> LT b) -> List (T a) -> List (T b)
  mapLT f [] = []
  mapLT f (x :: xs) with f x
  ... | bot    = mapLT f xs
  ... | lift y = y :: mapLT f xs

  infixr 40 _!_ _!_+_
  infix  40 >_
  infixr 30 _||_ _/|_

  data Proc (a : U) : Set where
    o     : Proc a
    >_    : (T a -> Proc a) -> Proc a
    _!_   : LT a -> Proc a -> Proc a
    _!_+_ : LT a -> Proc a -> (T a -> Proc a) -> Proc a
    _||_  : Proc a -> Proc a -> Proc a
    _/|_  : {b : U} -> Tran a b -> Proc b -> Proc a
    def   : Name a -> Proc a

  Env : Set
  Env = (a : U) -> Name a -> Proc a

record Param : Set1 where
  field
    U    : Set
    T    : U -> Set
    Name : U -> Set
    env  : ProcDef.Env U T Name

module Process (param : Param) where

  private open module Par = Param param      public
  private open module Pro = ProcDef U T Name public

  infixr 40 _!g_ _!_+g_
  infix  40 >g_
  infixr 30 _||g_ _/|g_

  data Guard {a : U} : Proc a -> Set where
    og     : Guard o
    >g_    : (f : T a -> Proc a)                          -> Guard (> f)
    _!g_   : (w : LT a)(p : Proc a)                       -> Guard (w ! p)
    _!_+g_ : (w : LT a)(p : Proc a)(f : T a -> Proc a)    -> Guard (w ! p + f)
    _||g_  : {p1 p2 : Proc a} -> Guard p1 -> Guard p2     -> Guard (p1 || p2)
    _/|g_  : {b : U}(φ : Tran a b){p : Proc b} -> Guard p -> Guard (φ /| p)
    defg   : (x : Name a) -> Guard (env a x)              -> Guard (def x)

  infix 20 _-[_]->_ _-!_!->_

  open Tran

  data _-[_]->_ {a : U} : Proc a -> LT a -> Proc a -> Set where
    qtau   : {p : Proc a}                    -> p -[ bot ]-> p
    rx-o   : {v : T a}                       -> o -[ lift v ]-> o
    rx-!   : {v : T a}{w : LT a}{p : Proc a} -> w ! p -[ lift v ]-> w ! p
    rx->   : {v : T a}{f : T a -> Proc a}    -> > f -[ lift v ]-> f v
    rx-+   : {v : T a}{w : LT a}{p : Proc a}{f : T a -> Proc a} ->
             w ! p + f -[ lift v ]-> f v
    rx-||  : {v : T a}{p1 p2 p1' p2' : Proc a} ->
             p1 -[ lift v ]-> p1' ->
             p2 -[ lift v ]-> p2' ->
             p1 || p2 -[ lift v ]-> p1' || p2'
    rx-/|  : {v : T a}{b : U}{φ : Tran a b}{q q' : Proc b} ->
             q -[ downV φ v ]-> q'   ->
             φ /| q -[ lift v ]-> φ /| q'
    rx-def : {v : T a}{p : Proc a}{x : Name a} ->
             env a x -[ lift v ]-> p ->
             def x -[ lift v ]-> p

  data _-!_!->_ {a : U} : Proc a -> LT a -> Proc a -> Set where
    tx-!   : {w : LT a}{p : Proc a} -> w ! p -! w !-> p
    tx-+   : {w : LT a}{p : Proc a}{f : T a -> Proc a} ->
             w ! p + f -! w !-> p
    tx-!|  : {w : LT a}{p p' q q' : Proc a} ->
             p -! w !-> p' -> q -[ w ]-> q' ->
             p || q -! w !-> p' || q'
    tx-|!  : {w : LT a}{p p' q q' : Proc a} ->
             p -[ w ]-> p' -> q -! w !-> q' ->
             p || q -! w !-> p' || q'
    tx-/|  : {b : U}{w : LT b}{φ : Tran a b}{q q' : Proc b} ->
             q -! w !-> q' ->
             φ /| q -! upV φ =<< w !-> φ /| q'
    tx-def : {w : LT a}{p : Proc a}{x : Name a} ->
             env a x -! w !-> p ->
             def x -! w !-> p

  data Silent {a : U} : Proc a -> Set where
    silent-o   : Silent o
    silent->   : {f : T a -> Proc a} -> Silent (> f)
    silent-||  : {p1 p2 : Proc a} ->
                 Silent p1 -> Silent p2 -> Silent (p1 || p2)
    silent-def : {x : Name a} ->
                 Silent (env _ x) -> Silent (def x)
    silent-/|  : {b : U}{φ : Tran a b}{p : Proc b} ->
                 Silent p -> Silent (φ /| p)

  infixr 40 _>!>_ _>*>_

  data _-[_]->*_ {a : U} : Proc a -> List (T a) -> Proc a -> Set where
    rnop  : {p : Proc a} -> p -[ [] ]->* p
    _>?>_ : {p q r : Proc a}{x : T a}{xs : List (T a)} ->
            p -[ lift x  ]->  q ->
            q -[ xs      ]->* r ->
            p -[ x :: xs ]->* r

  rx-||* : forall {a xs}{p1 p2 q1 q2 : Proc a} ->
           p1 -[ xs ]->* p2 ->
           q1 -[ xs ]->* q2 ->
           p1 || q1 -[ xs ]->* p2 || q2
  rx-||* rnop rnop = rnop
  rx-||* (s1 >?> t1) (s2 >?> t2) = rx-|| s1 s2 >?> rx-||* t1 t2

  rx-/|* : forall {a b xs}{φ : Tran a b}{p q : Proc b} ->
           p -[ mapLT (downV φ) xs ]->* q ->
           φ /| p -[ xs ]->* φ /| q
  rx-/|* {xs = []}      rnop      = rnop
  rx-/|* {xs = x :: xs}{φ = φ} t  with it (downV φ x) refl
  rx-/|* {xs = x :: xs}{φ}{p}{q} t | it bot eq =
      rx-/| (lem₁ eq) >?> rx-/|* (lem₂ eq t)
    where
      lem₁ : forall {w} -> w == bot -> p -[ w ]-> p
      lem₁ refl = qtau

      lem₂ : downV φ x == bot ->
             p -[ mapLT (downV φ) (x :: xs) ]->* q ->
             p -[ mapLT (downV φ) xs ]->* q
      lem₂ eq   h with downV φ x
      lem₂ refl h | .bot = h
  rx-/|* {a}{b}{x :: xs}{φ}{p}{q} t | it (lift y) eq =
      rx-/| (lem₁ eq t) >?> rx-/|* (lem₂ eq t)
    where
      Eqn = downV φ x == lift y
      Asm = p -[ mapLT (downV φ) (x :: xs) ]->* q

      r : Eqn -> Asm -> Proc b
      r eq t with downV φ x
      r refl (_>?>_ {q = q} _ _) | ._ = q

      lem₁ : (eq : Eqn)(h : Asm) -> p -[ downV φ x ]-> r eq h
      lem₁ eq   t with downV φ x
      lem₁ refl (s >?> _) | ._ = s

      lem₂ : (eq : Eqn)(h : Asm) -> r eq h -[ mapLT (downV φ) xs ]->* q
      lem₂ eq   t with downV φ x
      lem₂ refl (_ >?> t) | ._ = t

  data _-!_!->*_ {a : U} : Proc a -> List (T a) -> Proc a -> Set where
    tnop : {p : Proc a} -> p -! [] !->* p
    _>!>_ : {p q r : Proc a}{x : T a}{xs : List (T a)} ->
            p -! lift x  !->  q ->
            q -! xs      !->* r ->
            p -! x :: xs !->* r
    _>*>_ : {p q r : Proc a}{xs : List (T a)} ->
            p -! bot !->  q ->
            q -! xs  !->* r ->
            p -! xs  !->* r


open import Nat
open import Prelude
open import List
open import core
open import judgemental-erase

module moveerase where
  -- type actions don't change the term other than moving the cursor
  -- around
  moveeraset : {t t' : τ̂} {δ : direction} →
            (t + move δ +> t') →
            (t ◆t) == (t' ◆t)
  moveeraset TMArrChild1 = refl
  moveeraset TMArrChild2 = refl
  moveeraset TMArrParent1 = refl
  moveeraset TMArrParent2 = refl
  moveeraset (TMArrZip1 {t2 = t2} m) = ap1 (λ x → x ==> t2) (moveeraset m)
  moveeraset (TMArrZip2 {t1 = t1} m) = ap1 (λ x → t1 ==> x) (moveeraset m)

  -- the actual movements don't change the erasure
  moveerase : {e e' : ê} {δ : direction} →
            (e + move δ +>e e') →
            (e ◆e) == (e' ◆e)
  moveerase EMAscChild1 = refl
  moveerase EMAscChild2 = refl
  moveerase EMAscParent1 = refl
  moveerase EMAscParent2 = refl
  moveerase EMLamChild1 = refl
  moveerase EMLamParent = refl
  moveerase EMPlusChild1 = refl
  moveerase EMPlusChild2 = refl
  moveerase EMPlusParent1 = refl
  moveerase EMPlusParent2 = refl
  moveerase EMApChild1 = refl
  moveerase EMApChild2 = refl
  moveerase EMApParent1 = refl
  moveerase EMApParent2 = refl
  moveerase EMNEHoleChild1 = refl
  moveerase EMNEHoleParent = refl

  -- this form is essentially the same as above, but for judgemental
  -- erasure, which is sometimes more convenient.
  moveerasee' : {e e' : ê} {e◆ : ė} {δ : direction} →
            erase-e e e◆ →
            (e + move δ +>e e') →
            erase-e e' e◆
  moveerasee' er1 m with erase-e◆ er1
  ... | refl = ◆erase-e _ _ (! (moveerase m))

  moveeraset' : ∀{ t t◆ t'' δ} →
                 erase-t t t◆ →
                 t + move δ +> t'' →
                 erase-t t'' t◆
  moveeraset' er m with erase-t◆ er
  moveeraset' er m | refl = ◆erase-t _ _ (! (moveeraset m))

  -- movements don't change either the type or expression under expression
  -- actions
  mutual
    moveerase-synth : ∀{Γ e e' e◆ t t' δ } →
                       (er : erase-e e e◆) →
                       Γ ⊢ e◆ => t →
                       Γ ⊢ e => t ~ move δ ~> e' => t' →
                       (e ◆e) == (e' ◆e) × t == t'
    moveerase-synth er wt (SAMove x) = moveerase x , refl
    moveerase-synth (EEAscL er) (SAsc x) (SAZipAsc1 x₁) = ap1 (λ q → q ·: _) (moveerase-ana er x x₁) , refl
    moveerase-synth er wt (SAZipAsc2 x x₁ x₂ x₃)
      with (moveeraset x)
    ... | qq = ap1 (λ q → _ ·: q) qq , eq-ert-trans qq x₂ x₁
    moveerase-synth (EEApL er) (SAp wt x x₁) (SAZipApArr x₂ x₃ x₄ d x₅)
      with erasee-det x₃ er
    ... | refl with synthunicity wt x₄
    ... | refl with moveerase-synth er x₄ d
    ... | pp , refl with matcharrunicity x x₂
    ... | refl = (ap1 (λ q → q ∘ _) pp ) , refl
    moveerase-synth (EEApR er) (SAp wt x x₁) (SAZipApAna x₂ x₃ x₄)
      with synthunicity x₃ wt
    ... | refl with matcharrunicity x x₂
    ... | refl = ap1 (λ q → _ ∘ q)  (moveerase-ana er x₁ x₄ ) , refl
    moveerase-synth (EEPlusL er) (SPlus x x₁) (SAZipPlus1 x₂) = ap1 (λ q → q ·+ _) (moveerase-ana er x x₂)  , refl
    moveerase-synth (EEPlusR er) (SPlus x x₁) (SAZipPlus2 x₂) = ap1 (λ q → _ ·+ q) (moveerase-ana er x₁ x₂) , refl
    moveerase-synth er wt (SAZipHole x x₁ d) = ap1 ⦇⌜_⌟⦈ (π1 (moveerase-synth x x₁ d)) , refl

    moveerase-ana : ∀{Γ e e' e◆ t δ } →
                      (er : erase-e e e◆) →
                      Γ ⊢ e◆ <= t →
                      Γ ⊢ e ~ move δ ~> e' ⇐ t →
                      (e ◆e) == (e' ◆e)
    moveerase-ana er wt (AASubsume x x₁ x₂ x₃) = π1 (moveerase-synth x x₁ x₂)
    moveerase-ana er wt (AAMove x) = moveerase x
    moveerase-ana (EELam er) (ASubsume () x₂) _
    moveerase-ana (EELam er) (ALam x₁ x₂ wt) (AAZipLam x₃ x₄ d) with matcharrunicity x₂ x₄
    ... | refl =  ap1 (λ q → ·λ _ q) (moveerase-ana er wt d)


module Issue5563 where

F : (@0 A : Set) → A → A
F A x =
  let
    y : A
    y = x
  in y


------------------------------------------------------------------------
-- The Agda standard library
--
-- Coinductive lists
------------------------------------------------------------------------

{-# OPTIONS --without-K --sized-types --guardedness #-}

module Codata.Musical.Colist where

open import Category.Monad
open import Codata.Musical.Notation
open import Codata.Musical.Conat using (Coℕ; zero; suc)
open import Data.Bool.Base using (Bool; true; false)
open import Data.BoundedVec.Inefficient as BVec
  using (BoundedVec; []; _∷_)
open import Data.Empty using (⊥)
open import Data.Maybe using (Maybe; nothing; just; Is-just)
open import Data.Maybe.Relation.Unary.Any using (just)
open import Data.Nat.Base using (ℕ; zero; suc; _≥′_; ≤′-refl; ≤′-step)
open import Data.Nat.Properties using (s≤′s)
open import Data.List.Base using (List; []; _∷_)
open import Data.List.NonEmpty using (List⁺; _∷_)
open import Data.Product as Prod using (∃; _×_; _,_)
open import Data.Sum as Sum using (_⊎_; inj₁; inj₂; [_,_]′)
open import Function
open import Function.Equality using (_⟨$⟩_)
open import Function.Inverse as Inv using (_↔_; _↔̇_; Inverse; inverse)
open import Level using (_⊔_)
open import Relation.Binary
import Relation.Binary.Construct.FromRel as Ind
import Relation.Binary.Reasoning.Preorder as PreR
import Relation.Binary.Reasoning.PartialOrder as POR
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Nullary.Negation

module ¬¬Monad {p} where
  open RawMonad (¬¬-Monad {p}) public
open ¬¬Monad  -- we don't want the RawMonad content to be opened publicly

------------------------------------------------------------------------
-- The type

infixr 5 _∷_

data Colist {a} (A : Set a) : Set a where
  []  : Colist A
  _∷_ : (x : A) (xs : ∞ (Colist A)) → Colist A

{-# FOREIGN GHC
  data AgdaColist a    = Nil | Cons a (MAlonzo.RTE.Inf (AgdaColist a))
  type AgdaColist' l a = AgdaColist a
  #-}
{-# COMPILE GHC Colist = data AgdaColist' (Nil | Cons) #-}
{-# COMPILE UHC Colist = data __LIST__ (__NIL__ | __CONS__) #-}

module Colist-injective {a} {A : Set a} where

 ∷-injectiveˡ : ∀ {x y : A} {xs ys} → (Colist A ∋ x ∷ xs) ≡ y ∷ ys → x ≡ y
 ∷-injectiveˡ P.refl = P.refl

 ∷-injectiveʳ : ∀ {x y : A} {xs ys} → (Colist A ∋ x ∷ xs) ≡ y ∷ ys → xs ≡ ys
 ∷-injectiveʳ P.refl = P.refl

data Any {a p} {A : Set a} (P : A → Set p) :
         Colist A → Set (a ⊔ p) where
  here  : ∀ {x xs} (px  : P x)          → Any P (x ∷ xs)
  there : ∀ {x xs} (pxs : Any P (♭ xs)) → Any P (x ∷ xs)

module _  {a p} {A : Set a} {P : A → Set p} where

 here-injective : ∀ {x xs p q} → (Any P (x ∷ xs) ∋ here p) ≡ here q → p ≡ q
 here-injective P.refl = P.refl

 there-injective : ∀ {x xs p q} → (Any P (x ∷ xs) ∋ there p) ≡ there q → p ≡ q
 there-injective P.refl = P.refl

data All {a p} {A : Set a} (P : A → Set p) :
         Colist A → Set (a ⊔ p) where
  []  : All P []
  _∷_ : ∀ {x xs} (px : P x) (pxs : ∞ (All P (♭ xs))) → All P (x ∷ xs)

module All-injective {a p} {A : Set a} {P : A → Set p} where

 ∷-injectiveˡ : ∀ {x xs} {px qx pxs qxs} →
                (All P (x ∷ xs) ∋ px ∷ pxs) ≡ qx ∷ qxs → px ≡ qx
 ∷-injectiveˡ P.refl = P.refl

 ∷-injectiveʳ : ∀ {x xs} {px qx pxs qxs} →
                (All P (x ∷ xs) ∋ px ∷ pxs) ≡ qx ∷ qxs → pxs ≡ qxs
 ∷-injectiveʳ P.refl = P.refl

------------------------------------------------------------------------
-- Some operations

null : ∀ {a} {A : Set a} → Colist A → Bool
null []      = true
null (_ ∷ _) = false

length : ∀ {a} {A : Set a} → Colist A → Coℕ
length []       = zero
length (x ∷ xs) = suc (♯ length (♭ xs))

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

fromList : ∀ {a} {A : Set a} → List A → Colist A
fromList []       = []
fromList (x ∷ xs) = x ∷ ♯ fromList xs

take : ∀ {a} {A : Set a} (n : ℕ) → Colist A → BoundedVec A n
take zero    xs       = []
take (suc n) []       = []
take (suc n) (x ∷ xs) = x ∷ take n (♭ xs)

replicate : ∀ {a} {A : Set a} → Coℕ → A → Colist A
replicate zero    x = []
replicate (suc n) x = x ∷ ♯ replicate (♭ n) x

lookup : ∀ {a} {A : Set a} → ℕ → Colist A → Maybe A
lookup n       []       = nothing
lookup zero    (x ∷ xs) = just x
lookup (suc n) (x ∷ xs) = lookup n (♭ xs)

infixr 5 _++_

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

-- Interleaves the two colists (until the shorter one, if any, has
-- been exhausted).

infixr 5 _⋎_

_⋎_ : ∀ {a} {A : Set a} → Colist A → Colist A → Colist A
[]       ⋎ ys = ys
(x ∷ xs) ⋎ ys = x ∷ ♯ (ys ⋎ ♭ xs)

concat : ∀ {a} {A : Set a} → Colist (List⁺ A) → Colist A
concat []                     = []
concat ((x ∷ [])       ∷ xss) = x ∷ ♯ concat (♭ xss)
concat ((x ∷ (y ∷ xs)) ∷ xss) = x ∷ ♯ concat ((y ∷ xs) ∷ xss)

[_] : ∀ {a} {A : Set a} → A → Colist A
[ x ] = x ∷ ♯ []

------------------------------------------------------------------------
-- Any lemmas

-- Any lemma for map.

Any-map : ∀ {a b p} {A : Set a} {B : Set b} {P : B → Set p}
          {f : A → B} {xs} →
          Any P (map f xs) ↔ Any (P ∘ f) xs
Any-map {P = P} {f} {xs} = inverse to from from∘to to∘from
  where
  to : ∀ {xs} → Any P (map f xs) → Any (P ∘ f) xs
  to {[]}     ()
  to {x ∷ xs} (here px) = here px
  to {x ∷ xs} (there p) = there (to p)

  from : ∀ {xs} → Any (P ∘ f) xs → Any P (map f xs)
  from (here px) = here px
  from (there p) = there (from p)

  from∘to : ∀ {xs} (p : Any P (map f xs)) → from (to p) ≡ p
  from∘to {[]}     ()
  from∘to {x ∷ xs} (here px) = P.refl
  from∘to {x ∷ xs} (there p) = P.cong there (from∘to p)

  to∘from : ∀ {xs} (p : Any (P ∘ f) xs) → to (from p) ≡ p
  to∘from (here px) = P.refl
  to∘from (there p) = P.cong there (to∘from p)

-- Any lemma for _⋎_. This lemma implies that every member of xs or ys
-- is a member of xs ⋎ ys, and vice versa.

Any-⋎ : ∀ {a p} {A : Set a} {P : A → Set p} xs {ys} →
        Any P (xs ⋎ ys) ↔ (Any P xs ⊎ Any P ys)
Any-⋎ {P = P} = λ xs → record
  { to         = P.→-to-⟶ (to xs)
  ; from       = P.→-to-⟶ (from xs)
  ; inverse-of = record
    { left-inverse-of  = from∘to xs
    ; right-inverse-of = to∘from xs
    }
  }
  where
  to : ∀ xs {ys} → Any P (xs ⋎ ys) → Any P xs ⊎ Any P ys
  to []       p         = inj₂ p
  to (x ∷ xs) (here px) = inj₁ (here px)
  to (x ∷ xs) (there p) = [ inj₂ , inj₁ ∘ there ]′ (to _ p)

  mutual

    from-left : ∀ {xs ys} → Any P xs → Any P (xs ⋎ ys)
    from-left           (here px) = here px
    from-left {ys = ys} (there p) = there (from-right ys p)

    from-right : ∀ xs {ys} → Any P ys → Any P (xs ⋎ ys)
    from-right []       p = p
    from-right (x ∷ xs) p = there (from-left p)

  from : ∀ xs {ys} → Any P xs ⊎ Any P ys → Any P (xs ⋎ ys)
  from xs = Sum.[ from-left , from-right xs ]

  from∘to : ∀ xs {ys} (p : Any P (xs ⋎ ys)) → from xs (to xs p) ≡ p
  from∘to []                 p                          = P.refl
  from∘to (x ∷ xs)           (here px)                  = P.refl
  from∘to (x ∷ xs) {ys = ys} (there p)                  with to ys p | from∘to ys p
  from∘to (x ∷ xs) {ys = ys} (there .(from-left q))     | inj₁ q | P.refl = P.refl
  from∘to (x ∷ xs) {ys = ys} (there .(from-right ys q)) | inj₂ q | P.refl = P.refl

  mutual

    to∘from-left : ∀ {xs ys} (p : Any P xs) →
                   to xs {ys = ys} (from-left p) ≡ inj₁ p
    to∘from-left           (here px) = P.refl
    to∘from-left {ys = ys} (there p)
      rewrite to∘from-right ys p = P.refl

    to∘from-right : ∀ xs {ys} (p : Any P ys) →
                    to xs (from-right xs p) ≡ inj₂ p
    to∘from-right []                 p = P.refl
    to∘from-right (x ∷ xs) {ys = ys} p
      rewrite to∘from-left {xs = ys} {ys = ♭ xs} p = P.refl

  to∘from : ∀ xs {ys} (p : Any P xs ⊎ Any P ys) → to xs (from xs p) ≡ p
  to∘from xs = Sum.[ to∘from-left , to∘from-right xs ]

------------------------------------------------------------------------
-- Equality

-- xs ≈ ys means that xs and ys are equal.

infix 4 _≈_

data _≈_ {a} {A : Set a} : (xs ys : Colist A) → Set a where
  []  :                                       []     ≈ []
  _∷_ : ∀ x {xs ys} (xs≈ : ∞ (♭ xs ≈ ♭ ys)) → x ∷ xs ≈ x ∷ ys

-- The equality relation forms a setoid.

setoid : ∀ {ℓ} → Set ℓ → Setoid _ _
setoid A = record
  { Carrier       = Colist A
  ; _≈_           = _≈_
  ; isEquivalence = record
    { refl  = refl
    ; sym   = sym
    ; trans = trans
    }
  }
  where
  refl : Reflexive _≈_
  refl {[]}     = []
  refl {x ∷ xs} = x ∷ ♯ refl

  sym : Symmetric _≈_
  sym []        = []
  sym (x ∷ xs≈) = x ∷ ♯ sym (♭ xs≈)

  trans : Transitive _≈_
  trans []        []         = []
  trans (x ∷ xs≈) (.x ∷ ys≈) = x ∷ ♯ trans (♭ xs≈) (♭ ys≈)

module ≈-Reasoning where
  import Relation.Binary.Reasoning.Setoid as EqR
  private
    open module R {a} {A : Set a} = EqR (setoid A) public

-- map preserves equality.

map-cong : ∀ {a b} {A : Set a} {B : Set b}
           (f : A → B) → _≈_ =[ map f ]⇒ _≈_
map-cong f []        = []
map-cong f (x ∷ xs≈) = f x ∷ ♯ map-cong f (♭ xs≈)

-- Any respects pointwise implication (for the predicate) and equality
-- (for the colist).

Any-resp : ∀ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q}
           {xs ys} → (∀ {x} → P x → Q x) → xs ≈ ys →
           Any P xs → Any Q ys
Any-resp f (x ∷ xs≈) (here px) = here (f px)
Any-resp f (x ∷ xs≈) (there p) = there (Any-resp f (♭ xs≈) p)

-- Any maps pointwise isomorphic predicates and equal colists to
-- isomorphic types.

Any-cong : ∀ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q}
           {xs ys} → P ↔̇ Q → xs ≈ ys → Any P xs ↔ Any Q ys
Any-cong {A = A} {P} {Q} {xs} {ys} P↔Q = λ xs≈ys → record
  { to         = P.→-to-⟶ (to xs≈ys)
  ; from       = P.→-to-⟶ (from xs≈ys)
  ; inverse-of = record
    { left-inverse-of  = from∘to _
    ; right-inverse-of = to∘from _
    }
  }
  where
  open Setoid (setoid _) using (sym)

  to : ∀ {xs ys} → xs ≈ ys → Any P xs → Any Q ys
  to xs≈ys = Any-resp (Inverse.to P↔Q ⟨$⟩_) xs≈ys

  from : ∀ {xs ys} → xs ≈ ys → Any Q ys → Any P xs
  from xs≈ys = Any-resp (Inverse.from P↔Q ⟨$⟩_) (sym xs≈ys)

  to∘from : ∀ {xs ys} (xs≈ys : xs ≈ ys) (q : Any Q ys) →
            to xs≈ys (from xs≈ys q) ≡ q
  to∘from (x ∷ xs≈) (there q) = P.cong there (to∘from (♭ xs≈) q)
  to∘from (x ∷ xs≈) (here qx) =
    P.cong here (Inverse.right-inverse-of P↔Q qx)

  from∘to : ∀ {xs ys} (xs≈ys : xs ≈ ys) (p : Any P xs) →
            from xs≈ys (to xs≈ys p) ≡ p
  from∘to (x ∷ xs≈) (there p) = P.cong there (from∘to (♭ xs≈) p)
  from∘to (x ∷ xs≈) (here px) =
    P.cong here (Inverse.left-inverse-of P↔Q px)

------------------------------------------------------------------------
-- Indices

-- Converts Any proofs to indices into the colist. The index can also
-- be seen as the size of the proof.

index : ∀ {a p} {A : Set a} {P : A → Set p} {xs} → Any P xs → ℕ
index (here px) = zero
index (there p) = suc (index p)

-- The position returned by index is guaranteed to be within bounds.

lookup-index : ∀ {a p} {A : Set a} {P : A → Set p} {xs} (p : Any P xs) →
               Is-just (lookup (index p) xs)
lookup-index (here px) = just _
lookup-index (there p) = lookup-index p

-- Any-resp preserves the index.

index-Any-resp :
  ∀ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q}
    {f : ∀ {x} → P x → Q x} {xs ys}
  (xs≈ys : xs ≈ ys) (p : Any P xs) →
  index (Any-resp f xs≈ys p) ≡ index p
index-Any-resp (x ∷ xs≈) (here px) = P.refl
index-Any-resp (x ∷ xs≈) (there p) =
  P.cong suc (index-Any-resp (♭ xs≈) p)

-- The left-to-right direction of Any-⋎ returns a proof whose size is
-- no larger than that of the input proof.

index-Any-⋎ :
  ∀ {a p} {A : Set a} {P : A → Set p} xs {ys} (p : Any P (xs ⋎ ys)) →
  index p ≥′ [ index , index ]′ (Inverse.to (Any-⋎ xs) ⟨$⟩ p)
index-Any-⋎ []                 p         = ≤′-refl
index-Any-⋎ (x ∷ xs)           (here px) = ≤′-refl
index-Any-⋎ (x ∷ xs) {ys = ys} (there p)
  with Inverse.to (Any-⋎ ys) ⟨$⟩ p | index-Any-⋎ ys p
... | inj₁ q | q≤p = ≤′-step q≤p
... | inj₂ q | q≤p = s≤′s    q≤p

------------------------------------------------------------------------
-- Memberships, subsets, prefixes

-- x ∈ xs means that x is a member of xs.

infix 4 _∈_

_∈_ : ∀ {a} → {A : Set a} → A → Colist A → Set a
x ∈ xs = Any (_≡_ x) xs

-- xs ⊆ ys means that xs is a subset of ys.

infix 4 _⊆_

_⊆_ : ∀ {a} → {A : Set a} → Colist A → Colist A → Set a
xs ⊆ ys = ∀ {x} → x ∈ xs → x ∈ ys

-- xs ⊑ ys means that xs is a prefix of ys.

infix 4 _⊑_

data _⊑_ {a} {A : Set a} : Colist A → Colist A → Set a where
  []  : ∀ {ys}                            → []     ⊑ ys
  _∷_ : ∀ x {xs ys} (p : ∞ (♭ xs ⊑ ♭ ys)) → x ∷ xs ⊑ x ∷ ys

-- Any can be expressed using _∈_ (and vice versa).

Any-∈ : ∀ {a p} {A : Set a} {P : A → Set p} {xs} →
        Any P xs ↔ ∃ λ x → x ∈ xs × P x
Any-∈ {P = P} = record
  { to         = P.→-to-⟶ to
  ; from       = P.→-to-⟶ (λ { (x , x∈xs , p) → from x∈xs p })
  ; inverse-of = record
    { left-inverse-of  = from∘to
    ; right-inverse-of = λ { (x , x∈xs , p) → to∘from x∈xs p }
    }
  }
  where
  to : ∀ {xs} → Any P xs → ∃ λ x → x ∈ xs × P x
  to (here  p) = _ , here P.refl , p
  to (there p) = Prod.map id (Prod.map there id) (to p)

  from : ∀ {x xs} → x ∈ xs → P x → Any P xs
  from (here P.refl) p = here p
  from (there x∈xs)  p = there (from x∈xs p)

  to∘from : ∀ {x xs} (x∈xs : x ∈ xs) (p : P x) →
            to (from x∈xs p) ≡ (x , x∈xs , p)
  to∘from (here P.refl) p = P.refl
  to∘from (there x∈xs)  p =
    P.cong (Prod.map id (Prod.map there id)) (to∘from x∈xs p)

  from∘to : ∀ {xs} (p : Any P xs) →
            let (x , x∈xs , px) = to p in from x∈xs px ≡ p
  from∘to (here _)  = P.refl
  from∘to (there p) = P.cong there (from∘to p)

-- Prefixes are subsets.

⊑⇒⊆ : ∀ {a} → {A : Set a} → _⊑_ {A = A} ⇒ _⊆_
⊑⇒⊆ []          ()
⊑⇒⊆ (x ∷ xs⊑ys) (here ≡x)    = here ≡x
⊑⇒⊆ (_ ∷ xs⊑ys) (there x∈xs) = there (⊑⇒⊆ (♭ xs⊑ys) x∈xs)

-- The prefix relation forms a poset.

⊑-Poset : ∀ {ℓ} → Set ℓ → Poset _ _ _
⊑-Poset A = record
  { Carrier        = Colist A
  ; _≈_            = _≈_
  ; _≤_            = _⊑_
  ; isPartialOrder = record
    { isPreorder = record
      { isEquivalence = Setoid.isEquivalence (setoid A)
      ; reflexive     = reflexive
      ; trans         = trans
      }
    ; antisym  = antisym
    }
  }
  where
  reflexive : _≈_ ⇒ _⊑_
  reflexive []        = []
  reflexive (x ∷ xs≈) = x ∷ ♯ reflexive (♭ xs≈)

  trans : Transitive _⊑_
  trans []        _          = []
  trans (x ∷ xs≈) (.x ∷ ys≈) = x ∷ ♯ trans (♭ xs≈) (♭ ys≈)

  tail : ∀ {x xs y ys} → x ∷ xs ⊑ y ∷ ys → ♭ xs ⊑ ♭ ys
  tail (_ ∷ p) = ♭ p

  antisym : Antisymmetric _≈_ _⊑_
  antisym []       [] = []
  antisym (x ∷ p₁) p₂ = x ∷ ♯ antisym (♭ p₁) (tail p₂)

module ⊑-Reasoning where
  private
    open module R {a} {A : Set a} = POR (⊑-Poset A)
      public renaming (_≤⟨_⟩_ to _⊑⟨_⟩_)

-- The subset relation forms a preorder.

⊆-Preorder : ∀ {ℓ} → Set ℓ → Preorder _ _ _
⊆-Preorder A = Ind.preorder (setoid A) _∈_
                 (λ xs≈ys → ⊑⇒⊆ (⊑P.reflexive xs≈ys))
  where module ⊑P = Poset (⊑-Poset A)

module ⊆-Reasoning where
  private
    open module R {a} {A : Set a} = PreR (⊆-Preorder A)
      public renaming (_∼⟨_⟩_ to _⊆⟨_⟩_)

  infix 1 _∈⟨_⟩_

  _∈⟨_⟩_ : ∀ {a} {A : Set a} (x : A) {xs ys} →
           x ∈ xs → xs IsRelatedTo ys → x ∈ ys
  x ∈⟨ x∈xs ⟩ xs⊆ys = (begin xs⊆ys) x∈xs

-- take returns a prefix.

take-⊑ : ∀ {a} {A : Set a} n (xs : Colist A) →
         let toColist = fromList {a} ∘ BVec.toList in
         toColist (take n xs) ⊑ xs
take-⊑ zero    xs       = []
take-⊑ (suc n) []       = []
take-⊑ (suc n) (x ∷ xs) = x ∷ ♯ take-⊑ n (♭ xs)

------------------------------------------------------------------------
-- Finiteness and infiniteness

-- Finite xs means that xs has finite length.

data Finite {a} {A : Set a} : Colist A → Set a where
  []  : Finite []
  _∷_ : ∀ x {xs} (fin : Finite (♭ xs)) → Finite (x ∷ xs)

module Finite-injective {a} {A : Set a} where

 ∷-injective : ∀ {x : A} {xs p q} → (Finite (x ∷ xs) ∋ x ∷ p) ≡ x ∷ q → p ≡ q
 ∷-injective P.refl = P.refl

-- Infinite xs means that xs has infinite length.

data Infinite {a} {A : Set a} : Colist A → Set a where
  _∷_ : ∀ x {xs} (inf : ∞ (Infinite (♭ xs))) → Infinite (x ∷ xs)

module Infinite-injective {a} {A : Set a} where

 ∷-injective : ∀ {x : A} {xs p q} → (Infinite (x ∷ xs) ∋ x ∷ p) ≡ x ∷ q → p ≡ q
 ∷-injective P.refl = P.refl

-- Colists which are not finite are infinite.

not-finite-is-infinite :
  ∀ {a} {A : Set a} (xs : Colist A) → ¬ Finite xs → Infinite xs
not-finite-is-infinite []       hyp with hyp []
... | ()
not-finite-is-infinite (x ∷ xs) hyp =
  x ∷ ♯ not-finite-is-infinite (♭ xs) (hyp ∘ _∷_ x)

-- Colists are either finite or infinite (classically).

finite-or-infinite :
  ∀ {a} {A : Set a} (xs : Colist A) → ¬ ¬ (Finite xs ⊎ Infinite xs)
finite-or-infinite xs = helper <$> excluded-middle
  where
  helper : Dec (Finite xs) → Finite xs ⊎ Infinite xs
  helper (yes fin) = inj₁ fin
  helper (no ¬fin) = inj₂ $ not-finite-is-infinite xs ¬fin

-- Colists are not both finite and infinite.

not-finite-and-infinite :
  ∀ {a} {A : Set a} {xs : Colist A} → Finite xs → Infinite xs → ⊥
not-finite-and-infinite []        ()
not-finite-and-infinite (x ∷ fin) (.x ∷ inf) =
  not-finite-and-infinite fin (♭ inf)

------------------------------------------------------------------------
-- Legacy

import Codata.Colist as C
open import Codata.Thunk
import Size

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

  fromMusical : ∀ {i} → Colist A → C.Colist A i
  fromMusical []       = C.[]
  fromMusical (x ∷ xs) = x C.∷ λ where .force → fromMusical (♭ xs)

  toMusical : C.Colist A Size.∞ → Colist A
  toMusical C.[]       = []
  toMusical (x C.∷ xs) = x ∷ ♯ toMusical (xs .force)


import Level as L
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Data.Product
open import Data.Empty
open import Data.Nat
open import Data.Nat.Properties
open import RevMachine

module PartialRevNoRepeat {ℓ} (M : PartialRevMachine {ℓ}) where
  infix  99 _ᵣₑᵥ
  infixr 10 _∷_
  infixr 10 _++↦_

  open RevMachine.PartialRevMachine M

  is-stuck : State → Set _
  is-stuck st = ∄[ st' ] (st ↦ st')

  is-initial : State → Set _
  is-initial st = ∄[ st' ] (st' ↦ st)

  data _↦ᵣₑᵥ_ : State → State → Set (L.suc ℓ) where
    _ᵣₑᵥ : ∀ {st₁ st₂} → st₁ ↦ st₂ → st₂ ↦ᵣₑᵥ st₁

  data _↦*_ : State → State → Set (L.suc ℓ) where
    ◾ : {st : State} → st ↦* st
    _∷_ : {st₁ st₂ st₃ : State} → st₁ ↦ st₂ → st₂ ↦* st₃ → st₁ ↦* st₃

  _++↦_ : {st₁ st₂ st₃ : State} → st₁ ↦* st₂ → st₂ ↦* st₃ → st₁ ↦* st₃
  ◾ ++↦ st₁↦*st₂ = st₁↦*st₂
  (st₁↦st₁' ∷ st₁'↦*st₂) ++↦ st₂'↦*st₃ = st₁↦st₁' ∷ (st₁'↦*st₂ ++↦ st₂'↦*st₃)

  len↦ : ∀ {st st'} → st ↦* st' → ℕ
  len↦ ◾ = 0
  len↦ (_ ∷ r) = 1 + len↦ r

  data _↦ᵣₑᵥ*_ : State → State → Set (L.suc ℓ) where
    ◾ : ∀ {st} → st ↦ᵣₑᵥ* st
    _∷_ : ∀ {st₁ st₂ st₃} → st₁ ↦ᵣₑᵥ st₂ → st₂ ↦ᵣₑᵥ* st₃ → st₁ ↦ᵣₑᵥ* st₃

  _++↦ᵣₑᵥ_ : ∀ {st₁ st₂ st₃} → st₁ ↦ᵣₑᵥ* st₂ → st₂ ↦ᵣₑᵥ* st₃ → st₁ ↦ᵣₑᵥ* st₃
  ◾ ++↦ᵣₑᵥ st₁↦ᵣₑᵥ*st₂ = st₁↦ᵣₑᵥ*st₂
  (st₁↦ᵣₑᵥst₁' ∷ st₁'↦ᵣₑᵥ*st₂) ++↦ᵣₑᵥ st₂'↦ᵣₑᵥ*st₃ = st₁↦ᵣₑᵥst₁' ∷ (st₁'↦ᵣₑᵥ*st₂ ++↦ᵣₑᵥ st₂'↦ᵣₑᵥ*st₃)

  Rev↦ : ∀ {st₁ st₂} → st₁ ↦* st₂ → st₂ ↦ᵣₑᵥ* st₁
  Rev↦ ◾ = ◾
  Rev↦ (r ∷ rs) = Rev↦ rs ++↦ᵣₑᵥ ((r ᵣₑᵥ) ∷ ◾)

  Rev↦ᵣₑᵥ : ∀ {st₁ st₂} → st₁ ↦ᵣₑᵥ* st₂ → st₂ ↦* st₁
  Rev↦ᵣₑᵥ ◾ = ◾
  Rev↦ᵣₑᵥ ((r ᵣₑᵥ) ∷ rs) = Rev↦ᵣₑᵥ rs ++↦ (r ∷ ◾)

  deterministic* : ∀ {st st₁ st₂} → st ↦* st₁ → st ↦* st₂
                 → is-stuck st₁ → is-stuck st₂
                 → st₁ ≡ st₂
  deterministic* ◾ ◾ stuck₁ stuck₂ = refl
  deterministic* ◾ (r ∷ ↦*₂) stuck₁ stuck₂ = ⊥-elim (stuck₁ (_ , r))
  deterministic* (r ∷ ↦*₁) ◾ stuck₁ stuck₂ = ⊥-elim (stuck₂ (_ , r))
  deterministic* (r₁ ∷ ↦*₁) (r₂ ∷ ↦*₂) stuck₁ stuck₂ with deterministic r₁ r₂
  ... | refl = deterministic* ↦*₁ ↦*₂ stuck₁ stuck₂

  deterministic*' : ∀ {st st₁ st'} → (rs₁ : st ↦* st₁) → (rs : st ↦* st')
                  → is-stuck st'
                  → Σ[ rs' ∈ st₁ ↦* st' ] (len↦ rs ≡ len↦ rs₁ + len↦ rs')
  deterministic*' ◾ rs stuck = rs , refl
  deterministic*' (r ∷ rs₁) ◾ stuck = ⊥-elim (stuck (_ , r))
  deterministic*' (r ∷ rs₁) (r' ∷ rs) stuck with deterministic r r'
  ... | refl with deterministic*' rs₁ rs stuck
  ... | (rs' , eq) = rs' , cong suc eq

  deterministicᵣₑᵥ* : ∀ {st st₁ st₂} → st ↦ᵣₑᵥ* st₁ → st ↦ᵣₑᵥ* st₂ → ¬ (is-fail st)
                    → is-initial st₁ → is-initial st₂
                    → st₁ ≡ st₂
  deterministicᵣₑᵥ* ◾ ◾ _ initial₁ initial₂ = refl
  deterministicᵣₑᵥ* ◾ (r ᵣₑᵥ ∷ ↦ᵣₑᵥ*₂) _ initial₁ initial₂ = ⊥-elim (initial₁ (_ , r))
  deterministicᵣₑᵥ* (r ᵣₑᵥ ∷ ↦ᵣₑᵥ*₁) ◾ _ initial₁ initial₂ = ⊥-elim (initial₂ (_ , r))
  deterministicᵣₑᵥ* (_∷_ {_} {st'} {_} (r₁ ᵣₑᵥ) ↦ᵣₑᵥ*₁) (r₂ ᵣₑᵥ ∷ ↦ᵣₑᵥ*₂) nf initial₁ initial₂ with is-fail? st'
  ... | yes st'-f  = ⊥-elim (fail-is-stuck st'-f (_ , r₁))
  ... | no  st'-nf with deterministicᵣₑᵥ r₁ r₂ nf
  ... | refl = deterministicᵣₑᵥ* ↦ᵣₑᵥ*₁ ↦ᵣₑᵥ*₂ st'-nf initial₁ initial₂

  data _↦[_]_ : State → ℕ → State → Set (L.suc ℓ) where
    ◾ : ∀ {st} → st ↦[ 0 ] st
    _∷_ : ∀ {st₁ st₂ st₃ n} → st₁ ↦ st₂ → st₂ ↦[ n ] st₃ → st₁ ↦[ suc n ] st₃

  _++↦ⁿ_ : ∀ {st₁ st₂ st₃ n m} → st₁ ↦[ n ] st₂ → st₂ ↦[ m ] st₃ → st₁ ↦[ n + m ] st₃
  ◾ ++↦ⁿ st₁↦*st₂ = st₁↦*st₂
  (st₁↦st₁' ∷ st₁'↦*st₂) ++↦ⁿ st₂'↦*st₃ = st₁↦st₁' ∷ (st₁'↦*st₂ ++↦ⁿ st₂'↦*st₃)

  data _↦ᵣₑᵥ[_]_ : State → ℕ → State → Set (L.suc ℓ) where
    ◾ : ∀ {st} → st ↦ᵣₑᵥ[ 0 ] st
    _∷_ : ∀ {st₁ st₂ st₃ n} → st₁ ↦ᵣₑᵥ st₂ → st₂ ↦ᵣₑᵥ[ n ] st₃ → st₁ ↦ᵣₑᵥ[ suc n ] st₃

  _++↦ᵣₑᵥⁿ_ : ∀ {st₁ st₂ st₃ n m} → st₁ ↦ᵣₑᵥ[ n ] st₂ → st₂ ↦ᵣₑᵥ[ m ] st₃ → st₁ ↦ᵣₑᵥ[ n + m ] st₃
  ◾ ++↦ᵣₑᵥⁿ st₁↦*st₂ = st₁↦*st₂
  (st₁↦st₁' ∷ st₁'↦*st₂) ++↦ᵣₑᵥⁿ st₂'↦*st₃ = st₁↦st₁' ∷ (st₁'↦*st₂ ++↦ᵣₑᵥⁿ st₂'↦*st₃)

  deterministicⁿ : ∀ {st st₁ st₂ n} → st ↦[ n ] st₁ → st ↦[ n ] st₂
                 → st₁ ≡ st₂
  deterministicⁿ ◾ ◾ = refl
  deterministicⁿ (r₁ ∷ rs₁) (r₂ ∷ rs₂) with deterministic r₁ r₂
  ... | refl = deterministicⁿ rs₁ rs₂

  deterministicᵣₑᵥⁿ : ∀ {st st₁ st₂ n} → st ↦ᵣₑᵥ[ n ] st₁ → st ↦ᵣₑᵥ[ n ] st₂ → ¬ (is-fail st)
                    → st₁ ≡ st₂
  deterministicᵣₑᵥⁿ ◾ ◾ _ = refl
  deterministicᵣₑᵥⁿ (_∷_ {_} {st'} {_} (r₁ ᵣₑᵥ)  rs₁) (r₂ ᵣₑᵥ ∷ rs₂) nf with is-fail? st'
  ... | yes st'-f  = ⊥-elim (fail-is-stuck st'-f (_ , r₁))
  ... | no  st'-nf with deterministicᵣₑᵥ r₁ r₂ nf
  ... | refl = deterministicᵣₑᵥⁿ rs₁ rs₂ st'-nf

  private
    split↦ⁿ : ∀ {st st'' n m} → st ↦[ n + m ] st''
            → ∃[ st' ] (st ↦[ n ] st' × st' ↦[ m ] st'')
    split↦ⁿ {n = 0} {m} rs = _ , ◾ , rs
    split↦ⁿ {n = suc n} {m} (r ∷ rs) with split↦ⁿ {n = n} {m} rs
    ... | st' , st↦ⁿst' , st'↦ᵐst'' = st' , (r ∷ st↦ⁿst') , st'↦ᵐst''

    split↦ᵣₑᵥⁿ : ∀ {st st'' n m} → st ↦ᵣₑᵥ[ n + m ] st''
               → ∃[ st' ] (st ↦ᵣₑᵥ[ n ] st' × st' ↦ᵣₑᵥ[ m ] st'')
    split↦ᵣₑᵥⁿ {n = 0} {m} rs = _ , ◾ , rs
    split↦ᵣₑᵥⁿ {n = suc n} {m} (r ∷ rs) with split↦ᵣₑᵥⁿ {n = n} {m} rs
    ... | st' , st↦ⁿst' , st'↦ᵐst'' = st' , (r ∷ st↦ⁿst') , st'↦ᵐst''

    diff : ∀ {n m} → n < m → ∃[ k ] (0 < k × n + k ≡ m)
    diff {0} {(suc m)} (s≤s z≤n) = suc m , s≤s z≤n , refl
    diff {(suc n)} {(suc (suc m))} (s≤s (s≤s n≤m)) with diff {n} {suc m} (s≤s n≤m)
    ... | k , 0<k , eq = k , 0<k , cong suc eq

  Revⁿ : ∀ {st st' n} → st ↦[ n ] st' → st' ↦ᵣₑᵥ[ n ] st
  Revⁿ {n = 0} ◾ = ◾
  Revⁿ {n = suc n} (r ∷ rs) rewrite +-comm 1 n = Revⁿ rs ++↦ᵣₑᵥⁿ (r ᵣₑᵥ ∷ ◾)

  NoRepeat : ∀ {st₀ stₙ stₘ n m}
           → is-initial st₀
           → n < m
           → st₀ ↦[ n ] stₙ
           → st₀ ↦[ m ] stₘ
           → stₙ ≢ stₘ
  NoRepeat {stₙ = st} {_} {m} st₀-is-init n<m st₀↦ᵐst st₀↦ᵐ⁺ᵏ⁺¹st refl with diff n<m
  ... | suc k , 0<k , refl with is-fail? st
  ... | yes st-is-f with split↦ⁿ {n = m} {1 + k} st₀↦ᵐ⁺ᵏ⁺¹st
  ... | st′ , st₀↦ᵐst′ , (r ∷ rs) with deterministicⁿ st₀↦ᵐst st₀↦ᵐst′
  ... | refl = ⊥-elim (fail-is-stuck st-is-f (_ , r))
  NoRepeat {stₙ = st} {_} {m} st₀-is-init n<m st₀↦ᵐst st₀↦ᵐ⁺ᵏ⁺¹st refl | suc k , 0<k , refl | no st-is-nf with (Revⁿ st₀↦ᵐst , Revⁿ st₀↦ᵐ⁺ᵏ⁺¹st)
  ... | st'↦ᵐst₀' , st'↦ᵐ⁺ᵏ⁺¹st₀' with split↦ᵣₑᵥⁿ st'↦ᵐ⁺ᵏ⁺¹st₀'
  ... | st′ , st'↦ᵐst′ , st′↦ᵏ⁺¹st₀' with deterministicᵣₑᵥⁿ st'↦ᵐst₀' st'↦ᵐst′ st-is-nf
  ... | refl with st′↦ᵏ⁺¹st₀'
  ... | r ᵣₑᵥ ∷ rs = ⊥-elim (st₀-is-init (_ , r))