{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Group.MorphismProperties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.SIP
open import Cubical.Foundations.Function using (_∘_)
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Functions.Embedding
open import Cubical.Data.Sigma

open import Cubical.Structures.Axioms
open import Cubical.Structures.Pointed
open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.Monoid

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Properties
open import Cubical.Algebra.Group.Morphism

open import Cubical.Reflection.StrictEquiv

private
  variable
    ℓ ℓ' ℓ'' : Level

open Iso
open GroupStr
open GroupHom

isPropIsGroupHom : (G : Group {ℓ}) (H : Group {ℓ'}) {f : ⟨ G ⟩ → ⟨ H ⟩} → isProp (isGroupHom G H f)
isPropIsGroupHom G H {f} = isPropΠ2 λ a b → GroupStr.is-set (snd H) _ _

isSetGroupHom : {G : Group {ℓ}} {H : Group {ℓ'}} → isSet (GroupHom G H)
isSetGroupHom {G = G} {H = H} = isOfHLevelRespectEquiv 2 equiv (isSetΣ (isSetΠ λ _ → is-set (snd H)) λ _ → isProp→isSet (isPropIsGroupHom G H)) where
  equiv : (Σ[ g ∈ (⟨ G ⟩ → ⟨ H ⟩) ] (isGroupHom G H g)) ≃ (GroupHom G H)
  equiv =  isoToEquiv (iso (λ (g , m) → grouphom g m) (λ hom → fun hom , isHom hom) (λ _ → refl) λ _ → refl)

-- Morphism composition
isGroupHomComp : {F : Group {ℓ}} {G : Group {ℓ'}} {H : Group {ℓ''}} →
  (f : GroupHom F G) → (g : GroupHom G H) → isGroupHom F H (fun g ∘ fun f)
isGroupHomComp f g x y = cong (fun g) (isHom f _ _) ∙ isHom g _ _

compGroupHom : {F : Group {ℓ}} {G : Group {ℓ'}} {H : Group {ℓ''}} → GroupHom F G → GroupHom G H → GroupHom F H
fun (compGroupHom f g) = fun g ∘ fun f
isHom (compGroupHom f g) = isGroupHomComp f g

open GroupEquiv
compGroupEquiv : {F : Group {ℓ}} {G : Group {ℓ'}} {H : Group {ℓ''}} → GroupEquiv F G → GroupEquiv G H → GroupEquiv F H
eq (compGroupEquiv f g) = compEquiv (eq f) (eq g)
isHom (compGroupEquiv f g) = isHom (compGroupHom (hom f) (hom g))

idGroupEquiv : (G : Group {ℓ}) → GroupEquiv G G
eq (idGroupEquiv G) = idEquiv ⟨ G ⟩
isHom (idGroupEquiv G) = λ _ _ → refl

-- Isomorphism inversion
isGroupHomInv : {G : Group {ℓ}} {H : Group {ℓ'}} (f : GroupEquiv G H) → isGroupHom H G (invEq (GroupEquiv.eq f))
isGroupHomInv {G = G} {H = H}  f h h' = isInj-f _ _ (
  f' (g (h ⋆² h')) ≡⟨ retEq (eq f) _ ⟩
  (h ⋆² h') ≡⟨ sym (cong₂ _⋆²_ (retEq (eq f) h) (retEq (eq f) h')) ⟩
  (f' (g h) ⋆² f' (g h')) ≡⟨ sym (isHom (hom f) _ _) ⟩
  f' (g h ⋆¹ g h') ∎)
  where
  f' = fst (eq f)
  _⋆¹_ = _+_ (snd G)
  _⋆²_ = _+_ (snd H)
  g = invEq (eq f)

  isInj-f : (x y : ⟨ G ⟩) → f' x ≡ f' y → x ≡ y
  isInj-f x y = invEq (_ , isEquiv→isEmbedding (snd (eq f)) x y)

invGroupEquiv : {G : Group {ℓ}} {H : Group {ℓ'}} → GroupEquiv G H → GroupEquiv H G
eq (invGroupEquiv f) = invEquiv (eq f)
isHom (invGroupEquiv f) = isGroupHomInv f

dirProdEquiv : ∀ {ℓ ℓ' ℓ'' ℓ'''} {A : Group {ℓ}} {B : Group {ℓ'}} {C : Group {ℓ''}} {D : Group {ℓ'''}}
           → GroupEquiv A C → GroupEquiv B D
           → GroupEquiv (dirProd A B) (dirProd C D)
eq (dirProdEquiv eq1 eq2) = ≃-× (eq eq1) (eq eq2)
isHom (dirProdEquiv eq1 eq2) = isHom (×hom (GroupEquiv.hom eq1) (GroupEquiv.hom eq2))

groupHomEq : {G : Group {ℓ}} {H : Group {ℓ'}} {f g : GroupHom G H} → (fun f ≡ fun g) → f ≡ g
fun (groupHomEq p i) = p i
isHom (groupHomEq {G = G} {H = H} {f = f} {g = g} p i) = p-hom i
  where
  p-hom : PathP (λ i → isGroupHom G H (p i)) (isHom f) (isHom g)
  p-hom = toPathP (isPropIsGroupHom G H _ _)

groupEquivEq : {G : Group {ℓ}} {H : Group {ℓ'}} {f g : GroupEquiv G H} → (eq f ≡ eq g) → f ≡ g
eq (groupEquivEq {G = G} {H = H} {f} {g} p i) = p i
isHom (groupEquivEq {G = G} {H = H} {f} {g} p i) = p-hom i
  where
  p-hom : PathP (λ i → isGroupHom G H (p i .fst)) (isHom f) (isHom g)
  p-hom = toPathP (isPropIsGroupHom G H _ _)

module GroupΣTheory {ℓ} where

  RawGroupStructure : Type ℓ → Type ℓ
  RawGroupStructure = SemigroupΣTheory.RawSemigroupStructure

  RawGroupEquivStr : StrEquiv RawGroupStructure _
  RawGroupEquivStr = SemigroupΣTheory.RawSemigroupEquivStr

  rawGroupUnivalentStr : UnivalentStr RawGroupStructure _
  rawGroupUnivalentStr = SemigroupΣTheory.rawSemigroupUnivalentStr

  -- The neutral element and the inverse function will be derived from the
  -- axioms, instead of being defined in the RawGroupStructure in order
  -- to have that group equivalences are equivalences that preserves
  -- multiplication (so we don't have to show that they also preserve inversion
  -- and neutral element, although they will preserve them).
  GroupAxioms : (G : Type ℓ) → RawGroupStructure G → Type ℓ
  GroupAxioms G _·_ =
      IsSemigroup _·_
    × (Σ[ e ∈ G ] ((x : G) → (x · e ≡ x) × (e · x ≡ x))
                × ((x : G) → Σ[ x' ∈ G ] (x · x' ≡ e) × (x' · x ≡ e)))

  GroupStructure : Type ℓ → Type ℓ
  GroupStructure = AxiomsStructure RawGroupStructure GroupAxioms

  GroupΣ : Type (ℓ-suc ℓ)
  GroupΣ = TypeWithStr ℓ GroupStructure

  -- Structured equivalences for groups are those for monoids (but different axioms)
  GroupEquivStr : StrEquiv GroupStructure ℓ
  GroupEquivStr = AxiomsEquivStr RawGroupEquivStr GroupAxioms

  open MonoidTheory

  isSetGroupΣ : (G : GroupΣ)
               → isSet _
  isSetGroupΣ (_ , _ , (isSemigroup-G , _ , _)) = IsSemigroup.is-set isSemigroup-G

  isPropGroupAxioms : (G : Type ℓ)
                      → (s : RawGroupStructure G)
                      → isProp (GroupAxioms G s)
  isPropGroupAxioms G _+_ = isPropΣ (isPropIsSemigroup _) γ
    where
    γ : (h : IsSemigroup _+_) →
        isProp (Σ[ e ∈ G ] ((x : G) → (x + e ≡ x) × (e + x ≡ x))
                         × ((x : G) → Σ[ x' ∈ G ] (x + x' ≡ e) × (x' + x ≡ e)))
    γ h (e , P , _) (e' , Q , _) =
      Σ≡Prop (λ x → isPropΣ (isPropΠ λ _ → isProp× ((IsSemigroup.is-set h) _ _) ((IsSemigroup.is-set h) _ _)) (β x))
             (sym (fst (Q e)) ∙ snd (P e'))
      where
      β : (e : G) → ((x : G) → (x + e ≡ x) × (e + x ≡ x))
        → isProp ((x : G) → Σ[ x' ∈ G ] (x + x' ≡ e) × (x' + x ≡ e))
      β e He =
        isPropΠ λ { x (x' , _ , P) (x'' , Q , _) →
                Σ≡Prop (λ _ → isProp× ((IsSemigroup.is-set h) _ _) ((IsSemigroup.is-set h) _ _))
                       (inv-lemma ℳ x x' x'' P Q) }
        where
        ℳ : Monoid
        ℳ = makeMonoid e _+_ (IsSemigroup.is-set h) (IsSemigroup.assoc h) (λ x → He x .fst) (λ x → He x .snd)

  Group→GroupΣ : Group → GroupΣ
  Group→GroupΣ (G , GS) = _ , _ , (isSemigroup GS , _ , identity GS , λ x → (- GS) x , inverse GS x)

  GroupΣ→Group : GroupΣ → Group
  GroupΣ→Group (G , _ , SG , _ , H0g , invertible ) =
     group _ _ _ (λ x → invertible x .fst) (isgroup (ismonoid SG H0g) λ x → invertible x .snd)

  GroupIsoGroupΣ : Iso Group GroupΣ
  GroupIsoGroupΣ = iso Group→GroupΣ GroupΣ→Group (λ _ → refl) (λ _ → refl)

  groupUnivalentStr : UnivalentStr GroupStructure GroupEquivStr
  groupUnivalentStr = axiomsUnivalentStr _ isPropGroupAxioms rawGroupUnivalentStr

  GroupΣPath : (G H : GroupΣ) → (G ≃[ GroupEquivStr ] H) ≃ (G ≡ H)
  GroupΣPath = SIP groupUnivalentStr

  GroupEquivΣ : (G H : Group) → Type ℓ
  GroupEquivΣ G H = Group→GroupΣ G ≃[ GroupEquivStr ] Group→GroupΣ H

  GroupIsoΣPath : {G H : Group} → Iso (GroupEquiv G H) (GroupEquivΣ G H)
  fun GroupIsoΣPath f       = (eq f) , isHom f
  inv GroupIsoΣPath (e , h) = groupequiv e h
  rightInv GroupIsoΣPath _  = refl
  leftInv GroupIsoΣPath _   = refl

  GroupPath : (G H : Group) → (GroupEquiv G H) ≃ (G ≡ H)
  GroupPath G H =
    GroupEquiv G H                  ≃⟨ strictIsoToEquiv GroupIsoΣPath ⟩
    GroupEquivΣ G H                 ≃⟨ GroupΣPath _ _ ⟩
    Group→GroupΣ G ≡ Group→GroupΣ H ≃⟨ isoToEquiv (invIso (congIso GroupIsoGroupΣ)) ⟩
    G ≡ H ■

  RawGroupΣ : Type (ℓ-suc ℓ)
  RawGroupΣ = TypeWithStr ℓ RawGroupStructure

  Group→RawGroupΣ : Group → RawGroupΣ
  Group→RawGroupΣ (G , GS) = G , _+_ GS

  InducedGroup : (G : Group) (H : RawGroupΣ) (e : ⟨ G ⟩ ≃ H .fst)
               → RawGroupEquivStr (Group→RawGroupΣ G) H e → Group
  InducedGroup G H e r =
    GroupΣ→Group (inducedStructure rawGroupUnivalentStr (Group→GroupΣ G) H (e , r))

  InducedGroupPath : (G : Group {ℓ}) (H : RawGroupΣ) (e : ⟨ G ⟩ ≃ H .fst)
                     (E : RawGroupEquivStr (Group→RawGroupΣ G) H e)
                   → G ≡ InducedGroup G H e E
  InducedGroupPath G H e E =
    GroupPath G (InducedGroup G H e E) .fst (groupequiv e E)

-- Extract the characterization of equality of groups
GroupPath : (G H : Group {ℓ}) → (GroupEquiv G H) ≃ (G ≡ H)
GroupPath = GroupΣTheory.GroupPath

InducedGroup : (G : Group {ℓ}) (H : GroupΣTheory.RawGroupΣ) (e : ⟨ G ⟩ ≃ H .fst)
             → GroupΣTheory.RawGroupEquivStr (GroupΣTheory.Group→RawGroupΣ G) H e
             → Group
InducedGroup = GroupΣTheory.InducedGroup

InducedGroupPath : (G : Group {ℓ}) (H : GroupΣTheory.RawGroupΣ) (e : ⟨ G ⟩ ≃ H .fst)
                   (E : GroupΣTheory.RawGroupEquivStr (GroupΣTheory.Group→RawGroupΣ G) H e)
                 → G ≡ InducedGroup G H e E
InducedGroupPath = GroupΣTheory.InducedGroupPath

uaGroup : {G H : Group {ℓ}} → GroupEquiv G H → G ≡ H
uaGroup {G = G} {H = H} = equivFun (GroupPath G H)

carac-uaGroup : {G H : Group {ℓ}} (f : GroupEquiv G H) → cong ⟨_⟩ (uaGroup f) ≡ ua (GroupEquiv.eq f)
carac-uaGroup f = ua (eq f) ∙ refl ≡⟨ sym (rUnit _)  ⟩
                  ua (eq f) ∎

-- Group-ua functoriality

Group≡ : (G H : Group {ℓ}) → (
  Σ[ p ∈ ⟨ G ⟩ ≡ ⟨ H ⟩ ]
  Σ[ q ∈ PathP (λ i → p i) (0g (snd G)) (0g (snd H)) ]
  Σ[ r ∈ PathP (λ i → p i → p i → p i) (_+_ (snd G)) (_+_ (snd H)) ]
  Σ[ s ∈ PathP (λ i → p i → p i) (- snd G) (- snd H) ]
  PathP (λ i → IsGroup (q i) (r i) (s i)) (isGroup (snd G)) (isGroup (snd H)))
  ≃ (G ≡ H)
Group≡ G H = isoToEquiv theIso
  where
  theIso : Iso _ _
  fun theIso (p , q , r , s , t) i = p i , groupstr (q i) (r i) (s i) (t i)
  inv theIso x = cong ⟨_⟩ x , cong (0g ∘ snd) x , cong (_+_ ∘ snd) x , cong (-_ ∘ snd) x , cong (isGroup ∘ snd) x
  rightInv theIso _ = refl
  leftInv theIso _ = refl

caracGroup≡ : {G H : Group {ℓ}} (p q : G ≡ H) → cong ⟨_⟩ p ≡ cong ⟨_⟩ q → p ≡ q
caracGroup≡ {G = G} {H = H} p q P = sym (transportTransport⁻ (ua (Group≡ G H)) p)
                                 ∙∙ cong (transport (ua (Group≡ G H))) helper
                                 ∙∙ transportTransport⁻ (ua (Group≡ G H)) q
  where
  helper : transport (sym (ua (Group≡ G H))) p ≡ transport (sym (ua (Group≡ G H))) q
  helper = Σ≡Prop
             (λ _ → isPropΣ
                       (isOfHLevelPathP' 1 (is-set (snd H)) _ _)
                       λ _ → isPropΣ (isOfHLevelPathP' 1 (isSetΠ2 λ _ _ → is-set (snd H)) _ _)
                       λ _ → isPropΣ (isOfHLevelPathP' 1 (isSetΠ λ _ → is-set (snd H)) _ _)
                       λ _ → isOfHLevelPathP 1 (isPropIsGroup _ _ _) _ _)
             (transportRefl (cong ⟨_⟩ p) ∙ P ∙ sym (transportRefl (cong ⟨_⟩ q)))

uaGroupId : (G : Group {ℓ}) → uaGroup (idGroupEquiv G) ≡ refl
uaGroupId G = caracGroup≡ _ _ (carac-uaGroup (idGroupEquiv G) ∙ uaIdEquiv)

uaCompGroupEquiv : {F G H : Group {ℓ}} (f : GroupEquiv F G) (g : GroupEquiv G H) → uaGroup (compGroupEquiv f g) ≡ uaGroup f ∙ uaGroup g
uaCompGroupEquiv f g = caracGroup≡ _ _ (
  cong ⟨_⟩ (uaGroup (compGroupEquiv f g))
    ≡⟨ carac-uaGroup (compGroupEquiv f g) ⟩
  ua (eq (compGroupEquiv f g))
    ≡⟨ uaCompEquiv _ _ ⟩
  ua (eq f) ∙ ua (eq g)
    ≡⟨ cong (_∙ ua (eq g)) (sym (carac-uaGroup f)) ⟩
  cong ⟨_⟩ (uaGroup f) ∙ ua (eq g)
    ≡⟨ cong (cong ⟨_⟩ (uaGroup f) ∙_) (sym (carac-uaGroup g)) ⟩
  cong ⟨_⟩ (uaGroup f) ∙ cong ⟨_⟩ (uaGroup g)
    ≡⟨ sym (cong-∙ ⟨_⟩ (uaGroup f) (uaGroup g)) ⟩
  cong ⟨_⟩ (uaGroup f ∙ uaGroup g) ∎) where
  open GroupEquiv


postulate
  Nat : Set
  Fin : Nat → Set
  Finnat : Nat → Set
  fortytwo : Nat
  finnatic : Finnat fortytwo
  _==_ : Finnat fortytwo → Finnat fortytwo → Set

record Fixer : Set where
  field fix : ∀ {x} → Finnat x → Finnat fortytwo
open Fixer {{...}}

postulate
  Fixidentity : {{_ : Fixer}} → Set
  instance
    fixidentity : {{fx : Fixer}} {{fxi : Fixidentity}} {f : Finnat fortytwo} → fix f == f

  InstanceWrapper : Set
  iwrap : InstanceWrapper
  instance
    IrrFixerInstance : .InstanceWrapper → Fixer

instance
  FixerInstance : Fixer
  FixerInstance = IrrFixerInstance iwrap

instance
  postulate FixidentityInstance : Fixidentity

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

fails : Fixer.fix FixerInstance finnatic == finnatic
fails = fixidentity

works : Fixer.fix FixerInstance finnatic == finnatic
works = fixidentity {{fx = it}}

works₂ : Fixer.fix FixerInstance finnatic == finnatic
works₂ = fixidentity {{fxi = it}}


open import Agda.Builtin.Equality
open import Agda.Builtin.List
open import Agda.Builtin.Reflection renaming (bindTC to _>>=_)
open import Agda.Builtin.Unit

macro
  pickWhatever : Term → TC ⊤
  pickWhatever hole@(meta m _) = do
    (cand ∷ _) ← getInstances m
      where [] -> typeError (strErr "No candidates!" ∷ [])
    unify hole cand
  pickWhatever _ = typeError (strErr "Already solved!" ∷ [])

-- Testing basic functionality
data YesNo : Set where instance yes no : YesNo

test₁ : YesNo
test₁ = pickWhatever

test₁-ok : test₁ ≡ yes
test₁-ok = refl

-- Testing if candidates are correctly filtered
data IsYesNo : YesNo → Set where instance
  isYes : IsYesNo yes
  isNo : IsYesNo no

test₂ : IsYesNo no
test₂ = pickWhatever

-- Testing local candidates
test₃ : {{YesNo}} → YesNo
test₃ = pickWhatever

test₃-ok : ∀ {x} → test₃ {{x}} ≡ x
test₃-ok = refl


{-# OPTIONS --safe #-}

module Definition.Conversion.Stability where

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.Conversion
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.Substitution

open import Tools.Product
import Tools.PropositionalEquality as PE

-- Equality of contexts.
data ⊢_≡_ : (Γ Δ : Con Term) → Set where
  ε : ⊢ ε ≡ ε
  _∙_ : ∀ {Γ Δ A B r} → ⊢ Γ ≡ Δ → Γ ⊢ A ≡ B ^ r → ⊢ Γ ∙ A ^ r ≡ Δ ∙ B ^ r

mutual
  -- Syntactic validity and conversion substitution of a context equality.
  contextConvSubst : ∀ {Γ Δ} → ⊢ Γ ≡ Δ → ⊢ Γ × ⊢ Δ × Δ ⊢ˢ idSubst ∷ Γ
  contextConvSubst ε = ε , ε , id
  contextConvSubst (_∙_ {Γ} {Δ} {A} {B} Γ≡Δ A≡B) =
    let ⊢Γ , ⊢Δ , [σ] = contextConvSubst Γ≡Δ
        ⊢A , ⊢B = syntacticEq A≡B
        Δ⊢B = stability Γ≡Δ ⊢B
    in  ⊢Γ ∙ ⊢A , ⊢Δ ∙ Δ⊢B
        , (wk1Subst′ ⊢Γ ⊢Δ Δ⊢B [σ]
        , conv (var (⊢Δ ∙ Δ⊢B) here)
               (PE.subst (λ x → _ ⊢ _ ≡ x ^ _)
                         (wk1-tailId A)
                         (wkEq (step id) (⊢Δ ∙ Δ⊢B) (stabilityEq Γ≡Δ (sym A≡B)))))

  -- Stability of types under equal contexts.
  stability : ∀ {A rA Γ Δ} → ⊢ Γ ≡ Δ → Γ ⊢ A ^ rA → Δ ⊢ A ^ rA
  stability Γ≡Δ A =
    let ⊢Γ , ⊢Δ , σ = contextConvSubst Γ≡Δ
        q = substitution A σ ⊢Δ
    in  PE.subst (λ x → _ ⊢ x ^ _) (subst-id _) q

  -- Stability of type equality.
  stabilityEq : ∀ {A B rA Γ Δ} → ⊢ Γ ≡ Δ → Γ ⊢ A ≡ B ^ rA → Δ ⊢ A ≡ B ^ rA
  stabilityEq Γ≡Δ A≡B =
    let ⊢Γ , ⊢Δ , σ = contextConvSubst Γ≡Δ
        q = substitutionEq A≡B (substRefl σ) ⊢Δ
    in  PE.subst₂ (λ x y → _ ⊢ x ≡ y ^ _) (subst-id _) (subst-id _) q

-- Reflexivity of context equality.
reflConEq : ∀ {Γ} → ⊢ Γ → ⊢ Γ ≡ Γ
reflConEq ε = ε
reflConEq (⊢Γ ∙ ⊢A) = reflConEq ⊢Γ ∙ refl ⊢A

-- Symmetry of context equality.
symConEq : ∀ {Γ Δ} → ⊢ Γ ≡ Δ → ⊢ Δ ≡ Γ
symConEq ε = ε
symConEq (Γ≡Δ ∙ A≡B) = symConEq Γ≡Δ ∙ stabilityEq Γ≡Δ (sym A≡B)

-- Stability of terms.
stabilityTerm : ∀ {t A rA Γ Δ} → ⊢ Γ ≡ Δ → Γ ⊢ t ∷ A ^ rA → Δ ⊢ t ∷ A ^ rA
stabilityTerm Γ≡Δ t =
  let ⊢Γ , ⊢Δ , σ = contextConvSubst Γ≡Δ
      q = substitutionTerm t σ ⊢Δ
  in  PE.subst₂ (λ x y → _ ⊢ x ∷ y ^ _) (subst-id _) (subst-id _) q

-- Stability of term reduction.
stabilityRedTerm : ∀ {t u A Γ Δ l} → ⊢ Γ ≡ Δ → Γ ⊢ t ⇒ u ∷ A ^ l → Δ ⊢ t ⇒ u ∷ A ^ l
stabilityRedTerm Γ≡Δ (conv d x) =
  conv (stabilityRedTerm Γ≡Δ d) (stabilityEq Γ≡Δ x)
stabilityRedTerm Γ≡Δ (app-subst d x) =
  app-subst (stabilityRedTerm Γ≡Δ d) (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (β-red l< l<' x x₁ x₂) =
  β-red l< l<' (stability Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl x) x₁)
        (stabilityTerm Γ≡Δ x₂)
stabilityRedTerm Γ≡Δ (natrec-subst x x₁ x₂ d) =
  let ⊢Γ , _ , _ = contextConvSubst Γ≡Δ
  in  natrec-subst (stability (Γ≡Δ ∙ refl (univ (ℕⱼ ⊢Γ))) x) (stabilityTerm Γ≡Δ x₁)
                   (stabilityTerm Γ≡Δ x₂) (stabilityRedTerm Γ≡Δ d)
stabilityRedTerm Γ≡Δ (natrec-zero x x₁ x₂) =
  let ⊢Γ , _ , _ = contextConvSubst Γ≡Δ
  in  natrec-zero (stability (Γ≡Δ ∙ refl (univ (ℕⱼ ⊢Γ))) x) (stabilityTerm Γ≡Δ x₁)
                  (stabilityTerm Γ≡Δ x₂)
stabilityRedTerm Γ≡Δ (natrec-suc x x₁ x₂ x₃) =
  let ⊢Γ , _ , _ = contextConvSubst Γ≡Δ
  in  natrec-suc (stabilityTerm Γ≡Δ x) (stability (Γ≡Δ ∙ refl (univ (ℕⱼ ⊢Γ))) x₁)
                 (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
stabilityRedTerm Γ≡Δ (Id-subst X x x₁) = Id-subst (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
stabilityRedTerm Γ≡Δ (Id-ℕ-subst X x) = Id-ℕ-subst (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (Id-ℕ-0-subst X) = Id-ℕ-0-subst (stabilityRedTerm Γ≡Δ X)
stabilityRedTerm Γ≡Δ (Id-ℕ-S-subst x X) = Id-ℕ-S-subst (stabilityTerm Γ≡Δ x) (stabilityRedTerm Γ≡Δ X)
stabilityRedTerm Γ≡Δ (Id-U-subst X x) = Id-U-subst (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (Id-U-ℕ-subst X) = Id-U-ℕ-subst (stabilityRedTerm Γ≡Δ X)
stabilityRedTerm Γ≡Δ (Id-U-Π-subst x x₁ X) = Id-U-Π-subst (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁) (stabilityRedTerm Γ≡Δ X)
stabilityRedTerm Γ≡Δ (Id-Π l< l<' x x₁ x₂ x₃) = Id-Π l< l<' (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
stabilityRedTerm Γ≡Δ (Id-ℕ-00 x) = let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ in Id-ℕ-00 ⊢Δ
stabilityRedTerm Γ≡Δ (Id-ℕ-SS x x₁) = Id-ℕ-SS (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
stabilityRedTerm Γ≡Δ (Id-U-ΠΠ x x₁ x₂ x₃) = Id-U-ΠΠ (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm (Γ≡Δ ∙ refl (univ x₂)) x₃)
stabilityRedTerm Γ≡Δ (Id-U-ℕℕ x) = let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ in Id-U-ℕℕ ⊢Δ
stabilityRedTerm Γ≡Δ (Id-SProp x x₁) = Id-SProp (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
stabilityRedTerm Γ≡Δ (Id-ℕ-0S x) = Id-ℕ-0S (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (Id-ℕ-S0 x) = Id-ℕ-S0 (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (Id-U-ℕΠ x x₁) = Id-U-ℕΠ (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁)
stabilityRedTerm Γ≡Δ (Id-U-Πℕ x x₁) = Id-U-Πℕ (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁)
stabilityRedTerm Γ≡Δ (Id-U-ΠΠ!% x x₁ x₂ x₃ x₄) =
  Id-U-ΠΠ!% x (stabilityTerm Γ≡Δ x₁) (stabilityTerm (Γ≡Δ ∙ refl (univ x₁)) x₂) (stabilityTerm Γ≡Δ x₃) (stabilityTerm (Γ≡Δ ∙ refl (univ x₃)) x₄)
stabilityRedTerm Γ≡Δ (cast-subst X x x₁ x₂) = cast-subst (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂)
stabilityRedTerm Γ≡Δ (cast-ℕ-subst X x x₁) =
  cast-ℕ-subst (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
stabilityRedTerm Γ≡Δ (cast-Π-subst x x₁ X x₂ x₃) =
  cast-Π-subst (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁) (stabilityRedTerm Γ≡Δ X) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
stabilityRedTerm Γ≡Δ (cast-Π x x₁ x₂ x₃ x₄ x₅) =
  cast-Π (stabilityTerm Γ≡Δ x) (stabilityTerm (Γ≡Δ ∙ refl (univ x)) x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm (Γ≡Δ ∙ refl (univ x₂)) x₃) (stabilityTerm Γ≡Δ x₄) (stabilityTerm Γ≡Δ x₅)
stabilityRedTerm Γ≡Δ (cast-ℕ-0 x) = cast-ℕ-0 (stabilityTerm Γ≡Δ x)
stabilityRedTerm Γ≡Δ (cast-ℕ-S x x₁) = cast-ℕ-S (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
stabilityRedTerm Γ≡Δ (cast-ℕ-cong x X) = cast-ℕ-cong (stabilityTerm Γ≡Δ x) (stabilityRedTerm Γ≡Δ X)

-- Stability of type reductions.
stabilityRed : ∀ {A B r Γ Δ} → ⊢ Γ ≡ Δ → Γ ⊢ A ⇒ B ^ r → Δ ⊢ A ⇒ B ^ r
stabilityRed Γ≡Δ (univ x) = univ (stabilityRedTerm Γ≡Δ x)

-- Stability of type reduction closures.
stabilityRed* : ∀ {A B r Γ Δ} → ⊢ Γ ≡ Δ → Γ ⊢ A ⇒* B ^ r → Δ ⊢ A ⇒* B ^ r
stabilityRed* Γ≡Δ (id x) = id (stability Γ≡Δ x)
stabilityRed* Γ≡Δ (x ⇨ D) = stabilityRed Γ≡Δ x ⇨ stabilityRed* Γ≡Δ D

-- Stability of term reduction closures.
stabilityRed*Term : ∀ {t u A Γ Δ l} → ⊢ Γ ≡ Δ → Γ ⊢ t ⇒* u ∷ A ^ l → Δ ⊢ t ⇒* u ∷ A ^ l
stabilityRed*Term Γ≡Δ (id x) = id (stabilityTerm Γ≡Δ x)
stabilityRed*Term Γ≡Δ (x ⇨ d) = stabilityRedTerm Γ≡Δ x ⇨ stabilityRed*Term Γ≡Δ d

mutual
  -- Stability of algorithmic equality of neutrals.
  stability~↑! : ∀ {k l A Γ Δ lA}
              → ⊢ Γ ≡ Δ
              → Γ ⊢ k ~ l ↑! A ^ lA
              → Δ ⊢ k ~ l ↑! A ^ lA
  stability~↑! Γ≡Δ (var-refl x x≡y) =
    var-refl (stabilityTerm Γ≡Δ x) x≡y
  stability~↑! Γ≡Δ (app-cong {rF = ! } k~l x) =
    app-cong (stability~↓! Γ≡Δ k~l) (stabilityConv↑Term Γ≡Δ x)
  stability~↑! Γ≡Δ (app-cong {rF = %} k~l x) =
    app-cong (stability~↓! Γ≡Δ k~l) (stability~↑% Γ≡Δ x)
  stability~↑! Γ≡Δ (natrec-cong x₁ x₂ x₃ k~l) =
    let ⊢Γ , _ , _ = contextConvSubst Γ≡Δ
    in natrec-cong (stabilityConv↑ (Γ≡Δ ∙ (refl (univ (ℕⱼ ⊢Γ)))) x₁)
                   (stabilityConv↑Term Γ≡Δ x₂)
                   (stabilityConv↑Term Γ≡Δ x₃)
                   (stability~↓! Γ≡Δ k~l)
  stability~↑! Γ≡Δ (Emptyrec-cong x₁ k~l) =
    Emptyrec-cong (stabilityConv↑ Γ≡Δ x₁)
                (stability~↑% Γ≡Δ k~l)
  stability~↑! Γ≡Δ (Id-cong X x x₁) = Id-cong (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁)
  stability~↑! Γ≡Δ (Id-ℕ X x) = Id-ℕ (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x)
  stability~↑! Γ≡Δ (Id-ℕ0 X) = Id-ℕ0 (stability~↓! Γ≡Δ X)
  stability~↑! Γ≡Δ (Id-ℕS x X) = Id-ℕS (stabilityConv↑Term Γ≡Δ x) (stability~↓! Γ≡Δ X)
  stability~↑! Γ≡Δ (Id-U X x) = Id-U (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x)
  stability~↑! Γ≡Δ (Id-Uℕ X) = Id-Uℕ (stability~↓! Γ≡Δ X)
  stability~↑! Γ≡Δ (Id-UΠ x X) = Id-UΠ (stabilityConv↑Term Γ≡Δ x) (stability~↓! Γ≡Δ X)
  stability~↑! Γ≡Δ (cast-cong X x x₁ x₂ x₃) = cast-cong (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
  stability~↑! Γ≡Δ (cast-ℕ X x x₁ x₂) = cast-ℕ (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x) (stabilityTerm Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂)
  stability~↑! Γ≡Δ (cast-ℕℕ X x x₁) = cast-ℕℕ (stability~↓! Γ≡Δ X) (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
  stability~↑! Γ≡Δ (cast-Π x X x₁ x₂ x₃) = cast-Π (stabilityConv↑Term Γ≡Δ x) (stability~↓! Γ≡Δ X) (stabilityConv↑Term Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
  stability~↑! Γ≡Δ (cast-Πℕ x x₁ x₂ x₃) = cast-Πℕ (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
  stability~↑! Γ≡Δ (cast-ℕΠ x x₁ x₂ x₃) = cast-ℕΠ (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁) (stabilityTerm Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃)
  stability~↑! Γ≡Δ (cast-ΠΠ%! x x₁ x₂ x₃ x₄) = cast-ΠΠ%! (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁) (stabilityConv↑Term Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃) (stabilityTerm Γ≡Δ x₄)
  stability~↑! Γ≡Δ (cast-ΠΠ!% x x₁ x₂ x₃ x₄) = cast-ΠΠ!% (stabilityConv↑Term Γ≡Δ x) (stabilityConv↑Term Γ≡Δ x₁) (stabilityConv↑Term Γ≡Δ x₂) (stabilityTerm Γ≡Δ x₃) (stabilityTerm Γ≡Δ x₄)

  stability~↑% : ∀ {k l A Γ Δ lA}
              → ⊢ Γ ≡ Δ
              → Γ ⊢ k ~ l ↑% A ^ lA
              → Δ ⊢ k ~ l ↑% A ^ lA
  stability~↑% Γ≡Δ (%~↑ ⊢k ⊢l) = %~↑ (stabilityTerm Γ≡Δ ⊢k) (stabilityTerm Γ≡Δ ⊢l)

  stability~↑ : ∀ {k l A rA lA Γ Δ}
              → ⊢ Γ ≡ Δ
              → Γ ⊢ k ~ l ↑ A ^ [ rA , lA ]
              → Δ ⊢ k ~ l ↑ A ^ [ rA , lA ]
  stability~↑ Γ≡Δ (~↑! x) = ~↑! (stability~↑! Γ≡Δ x)
  stability~↑ Γ≡Δ (~↑% x) = ~↑% (stability~↑% Γ≡Δ x)

  -- Stability of algorithmic equality of neutrals of types in WHNF.
  stability~↓! : ∀ {k l A lA Γ Δ}
              → ⊢ Γ ≡ Δ
              → Γ ⊢ k ~ l ↓! A ^ lA
              → Δ ⊢ k ~ l ↓! A ^ lA
  stability~↓! Γ≡Δ ([~] A D whnfA k~l) =
    [~] A (stabilityRed* Γ≡Δ D) whnfA (stability~↑! Γ≡Δ k~l)

  -- Stability of algorithmic equality of types.
  stabilityConv↑ : ∀ {A B r Γ Δ}
                 → ⊢ Γ ≡ Δ
                 → Γ ⊢ A [conv↑] B ^ r
                 → Δ ⊢ A [conv↑] B ^ r
  stabilityConv↑ Γ≡Δ ([↑] A′ B′ D D′ whnfA′ whnfB′ A′<>B′) =
    [↑] A′ B′ (stabilityRed* Γ≡Δ D) (stabilityRed* Γ≡Δ D′) whnfA′ whnfB′
        (stabilityConv↓ Γ≡Δ A′<>B′)

  -- Stability of algorithmic equality of types in WHNF.
  stabilityConv↓ : ∀ {A B r Γ Δ}
                 → ⊢ Γ ≡ Δ
                 → Γ ⊢ A [conv↓] B ^ r
                 → Δ ⊢ A [conv↓] B ^ r
  stabilityConv↓ Γ≡Δ (U-refl PE.refl x) =
    let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ
    in  U-refl PE.refl ⊢Δ
  stabilityConv↓ Γ≡Δ (univ x) = univ (stabilityConv↓Term Γ≡Δ x)

  -- Stability of algorithmic equality of terms.
  stabilityConv↑Term : ∀ {t u A lA Γ Δ}
                     → ⊢ Γ ≡ Δ
                     → Γ ⊢ t [conv↑] u ∷ A ^ lA
                     → Δ ⊢ t [conv↑] u ∷ A ^ lA
  stabilityConv↑Term Γ≡Δ ([↑]ₜ B t′ u′ D d d′ whnfB whnft′ whnfu′ t<>u) =
    [↑]ₜ B t′ u′ (stabilityRed* Γ≡Δ D) (stabilityRed*Term Γ≡Δ d)
                 (stabilityRed*Term Γ≡Δ d′) whnfB whnft′ whnfu′
                 (stabilityConv↓Term Γ≡Δ t<>u)

  -- Stability of algorithmic equality of terms in WHNF.
  stabilityConv↓Term : ∀ {t u A lA Γ Δ}
                     → ⊢ Γ ≡ Δ
                     → Γ ⊢ t [conv↓] u ∷ A ^ lA
                     → Δ ⊢ t [conv↓] u ∷ A ^ lA
  stabilityConv↓Term Γ≡Δ (U-refl PE.refl x) =
    let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ
    in  U-refl PE.refl ⊢Δ
  stabilityConv↓Term Γ≡Δ (ne x) = ne (stability~↓! Γ≡Δ x)
  stabilityConv↓Term Γ≡Δ (ℕ-refl x) =
    let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ
    in  ℕ-refl ⊢Δ
  stabilityConv↓Term Γ≡Δ (Empty-refl x _) =
    let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ
    in  Empty-refl x ⊢Δ
  stabilityConv↓Term Γ≡Δ (Π-cong PE.refl PE.refl PE.refl PE.refl l< l<' F A<>B A<>B₁) =
    Π-cong PE.refl PE.refl PE.refl PE.refl l< l<' (stability Γ≡Δ F) (stabilityConv↑Term Γ≡Δ A<>B)
           (stabilityConv↑Term (Γ≡Δ ∙ refl F) A<>B₁)
  stabilityConv↓Term Γ≡Δ (∃-cong PE.refl F A<>B A<>B₁) =
    ∃-cong PE.refl (stability Γ≡Δ F) (stabilityConv↑Term Γ≡Δ A<>B)
           (stabilityConv↑Term (Γ≡Δ ∙ refl F) A<>B₁)
  stabilityConv↓Term Γ≡Δ (ℕ-ins x) =
    ℕ-ins (stability~↓! Γ≡Δ x)
  -- stabilityConv↓Term Γ≡Δ (Empty-ins x) =
  --   Empty-ins (stability~↓% Γ≡Δ x)
  stabilityConv↓Term Γ≡Δ (ne-ins t u neN x) =
    ne-ins (stabilityTerm Γ≡Δ t) (stabilityTerm Γ≡Δ u) neN (stability~↓! Γ≡Δ x)
  stabilityConv↓Term Γ≡Δ (zero-refl x) =
    let _ , ⊢Δ , _ = contextConvSubst Γ≡Δ
    in  zero-refl ⊢Δ
  stabilityConv↓Term Γ≡Δ (suc-cong t<>u) = suc-cong (stabilityConv↑Term Γ≡Δ t<>u)
  stabilityConv↓Term Γ≡Δ (η-eq l< l<' F x x₁ y y₁ t<>u) =
    η-eq l< l<' (stability Γ≡Δ F) (stabilityTerm Γ≡Δ x) (stabilityTerm Γ≡Δ x₁)
         y y₁ (stabilityConv↑Term (Γ≡Δ ∙ refl F) t<>u)


module Syntax where

open import Agda.Builtin.FromNat public
open import Agda.Builtin.FromNeg public

record plus-syntax (A : Set) (B : A → Set) (C : ∀ x → B x → Set) : Set where
  infixl 6 _+_
  field _+_ : ∀ x (y : B x) → C x y

plus-syntax-simple = λ A B C → plus-syntax A (λ _ → B) (λ _ _ → C)

open plus-syntax {{…}} public

record minus-syntax (A : Set) (B : A → Set) (C : ∀ x → B x → Set) : Set where
  infixl 6 _-_
  field _-_ : ∀ x (y : B x) → C x y

minus-syntax-simple = λ A B C → minus-syntax A (λ _ → B) (λ _ _ → C)

open minus-syntax {{…}} public

record times-syntax (A : Set) (B : A → Set) (C : ∀ x → B x → Set) : Set where
  infixl 7 _*_
  field _*_ : ∀ x (y : B x) → C x y

times-syntax-simple = λ A B C → times-syntax A (λ _ → B) (λ _ _ → C)

open times-syntax {{…}} public

record raise-syntax (A : Set) (B : A → Set) (C : ∀ x → B x → Set) : Set where
  infixl 8 _^_
  field _^_ : ∀ x (y : B x) → C x y

raise-syntax-simple = λ A B C → raise-syntax A (λ _ → B) (λ _ _ → C)

open raise-syntax {{…}} public


open import Mockingbird.Forest using (Forest)

-- Mockingbirds, Warblers, and Starlings
module Mockingbird.Problems.Chapter12 {b ℓ} (forest : Forest {b} {ℓ}) where

open import Data.Product using (_×_; _,_; proj₁; ∃-syntax)
open import Function using (_$_)

open import Mockingbird.Forest.Birds forest
import Mockingbird.Problems.Chapter11 forest as Chapter₁₁

open Forest forest

problem₁ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄ → HasDoubleMockingbird
problem₁ = record
  { M₂ = B ∙ M
  ; isDoubleMockingbird = λ x y → begin
     B ∙ M ∙ x ∙ y    ≈⟨ isBluebird M x y ⟩
     M ∙ (x ∙ y)      ≈⟨ isMockingbird (x ∙ y) ⟩
     x ∙ y ∙ (x ∙ y)  ∎
  }

problem₂-BCM : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasLark
problem₂-BCM = record
  { L = C ∙ B ∙ M
  ; isLark = λ x y → begin
      C ∙ B ∙ M ∙ x ∙ y  ≈⟨ congʳ $ isCardinal B M x ⟩
      B ∙ x ∙ M ∙ y      ≈⟨ isBluebird x M y ⟩
      x ∙ (M ∙ y)        ≈⟨ congˡ $ isMockingbird y ⟩
      x ∙ (y ∙ y)        ∎
  } where instance hasCardinal = Chapter₁₁.problem₂₁′

problem₂-BRM : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasLark
problem₂-BRM = record
  { L = R ∙ M ∙ B
  ; isLark = λ x y → begin
      R ∙ M ∙ B ∙ x ∙ y  ≈⟨ congʳ $ isRobin M B x ⟩
      B ∙ x ∙ M ∙ y      ≈⟨ isBluebird x M y ⟩
      x ∙ (M ∙ y)        ≈⟨ congˡ $ isMockingbird y ⟩
      x ∙ (y ∙ y)        ∎
  } where instance hasRobin = Chapter₁₁.problem₂₀

problem₃ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasWarbler ⦄ → HasLark
problem₃ = record
  { L = B ∙ W ∙ B
  ; isLark = λ x y → begin
      B ∙ W ∙ B ∙ x ∙ y  ≈⟨ congʳ $ isBluebird W B x ⟩
      W ∙ (B ∙ x) ∙ y    ≈⟨ isWarbler (B ∙ x) y ⟩
      B ∙ x ∙ y ∙ y      ≈⟨ isBluebird x y y ⟩
      x ∙ (y ∙ y)        ∎
  }

problem₄ : ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasQueerBird ⦄ → HasLark
problem₄ = record
  { L = Q ∙ M
  ; isLark = λ x y → begin
      Q ∙ M ∙ x ∙ y  ≈⟨ isQueerBird M x y ⟩
      x ∙ (M ∙ y)    ≈⟨ congˡ $ isMockingbird y ⟩
      x ∙ (y ∙ y)    ∎
  }

problem₅ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasRobin ⦄ ⦃ _ : HasMockingbird ⦄ → HasConverseWarbler
problem₅ = record
  { W′ = M₂ ∙ R
  ; isConverseWarbler = λ x y → begin
      M₂ ∙ R ∙ x ∙ y       ≈⟨ congʳ $ isDoubleMockingbird R x ⟩
      R ∙ x ∙ (R ∙ x) ∙ y  ≈⟨ isRobin x (R ∙ x) y ⟩
      R ∙ x ∙ y ∙ x        ≈⟨ isRobin x y x ⟩
      y ∙ x ∙ x            ∎
  } where instance hasDoubleMockingbird = problem₁

problem₆′ : ⦃ _ : HasCardinal ⦄ ⦃ _ : HasConverseWarbler ⦄ → HasWarbler
problem₆′ = record
  { W = C ∙ W′
  ; isWarbler = λ x y → begin
      C ∙ W′ ∙ x ∙ y ≈⟨ isCardinal W′ x y ⟩
      W′ ∙ y ∙ x     ≈⟨ isConverseWarbler y x ⟩
      x ∙ y ∙ y      ∎
  }

problem₆ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasRobin ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasMockingbird ⦄ → HasWarbler
problem₆ = record
  { W = C ∙ (B ∙ M ∙ R)
  ; isWarbler = λ x y → begin
      C ∙ (B ∙ M ∙ R) ∙ x ∙ y  ≈⟨⟩
      C ∙ (M₂ ∙ R) ∙ x ∙ y     ≈⟨⟩
      C ∙ W′ ∙ x ∙ y           ≈⟨⟩
      W ∙ x ∙ y                ≈⟨ isWarbler x y ⟩
      x ∙ y ∙ y                ∎
  } where
  instance
    hasDoubleMockingbird = problem₁
    hasConverseWarbler = problem₅
    hasWarbler = problem₆′

problem₇ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasWarbler
problem₇ = record
  { W = B ∙ (T ∙ (B ∙ M ∙ (B ∙ B ∙ T))) ∙ (B ∙ B ∙ T)
  ; isWarbler = λ x y → begin
      B ∙ (T ∙ (B ∙ M ∙ (B ∙ B ∙ T))) ∙ (B ∙ B ∙ T) ∙ x ∙ y  ≈˘⟨ congʳ $ congʳ $ isWarbler′ ⟩
      W ∙ x ∙ y                                              ≈⟨ isWarbler x y ⟩
      x ∙ y ∙ y                                              ∎
  } where
  instance
    hasRobin = Chapter₁₁.problem₂₀
    hasCardinal = Chapter₁₁.problem₂₁-bonus
    hasWarbler = problem₆

  isWarbler′ : W ≈ B ∙ (T ∙ (B ∙ M ∙ (B ∙ B ∙ T))) ∙ (B ∙ B ∙ T)
  isWarbler′ = begin
    W                                                            ≈⟨⟩
    C ∙ (B ∙ M ∙ R)                                              ≈⟨⟩
    C ∙ (B ∙ M ∙ (B ∙ B ∙ T))                                    ≈⟨⟩
    B ∙ (T ∙ (B ∙ B ∙ T)) ∙ (B ∙ B ∙ T) ∙ (B ∙ M ∙ (B ∙ B ∙ T))  ≈⟨ isBluebird (T ∙ (B ∙ B ∙ T)) (B ∙ B ∙ T) (B ∙ M ∙ (B ∙ B ∙ T)) ⟩
    T ∙ (B ∙ B ∙ T) ∙ (B ∙ B ∙ T ∙ (B ∙ M ∙ (B ∙ B ∙ T)))        ≈⟨ isThrush (B ∙ B ∙ T) (B ∙ B ∙ T ∙ (B ∙ M ∙ (B ∙ B ∙ T))) ⟩
    B ∙ B ∙ T ∙ (B ∙ M ∙ (B ∙ B ∙ T)) ∙ (B ∙ B ∙ T)              ≈⟨ congʳ $ isBluebird B T (B ∙ M ∙ (B ∙ B ∙ T)) ⟩
    B ∙ (T ∙ (B ∙ M ∙ (B ∙ B ∙ T))) ∙ (B ∙ B ∙ T)                ∎

-- TODO: other expression in problem 7.

-- NOTE: the bluebird B is not necessary.
problem₈ : ⦃ _ : HasThrush ⦄ ⦃ _ : HasWarbler ⦄ → HasMockingbird
problem₈ = record
  { M = W ∙ T
  ; isMockingbird = λ x → begin
      W ∙ T ∙ x  ≈⟨ isWarbler T x ⟩
      T ∙ x ∙ x  ≈⟨ isThrush x x ⟩
      x ∙ x      ∎
  }

problem₉-W* : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasWarblerOnceRemoved
problem₉-W* = record
  { W* = B ∙ W
  ; isWarblerOnceRemoved = λ x y z → begin
      B ∙ W ∙ x ∙ y ∙ z  ≈⟨ congʳ $ isBluebird W x y ⟩
      W ∙ (x ∙ y) ∙ z    ≈⟨ isWarbler (x ∙ y) z ⟩
      x ∙ y ∙ z ∙ z      ∎
  } where instance hasWarbler = problem₇

problem₉-W** : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasWarblerTwiceRemoved
problem₉-W** = record
  { W** = B ∙ W*
  ; isWarblerTwiceRemoved = λ x y z w → begin
      B ∙ W* ∙ x ∙ y ∙ z ∙ w  ≈⟨ congʳ $ congʳ $ isBluebird W* x y ⟩
      W* ∙ (x ∙ y) ∙ z ∙ w    ≈⟨ isWarblerOnceRemoved (x ∙ y) z w ⟩
      x ∙ y ∙ z ∙ w ∙ w       ∎
  } where
  instance
    hasWarbler = problem₇
    hasWarblerOnceRemoved = problem₉-W*

problem₁₀ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄ → HasHummingbird
problem₁₀ = record
  { H = B ∙ W ∙ (B ∙ C)
  ; isHummingbird = λ x y z → begin
      B ∙ W ∙ (B ∙ C) ∙ x ∙ y ∙ z   ≈⟨ congʳ $ congʳ $ isBluebird W (B ∙ C) x ⟩
      W ∙ (B ∙ C ∙ x) ∙ y ∙ z       ≈⟨ congʳ $ isWarbler (B ∙ C ∙ x) y ⟩
      B ∙ C ∙ x ∙ y ∙ y ∙ z         ≈⟨ congʳ $ congʳ $ isBluebird C x y ⟩
      C ∙ (x ∙ y) ∙ y ∙ z           ≈⟨ isCardinal (x ∙ y) y z ⟩
      x ∙ y ∙ z ∙ y                 ∎
  }

problem₁₁ : ⦃ _ : HasRobin ⦄ ⦃ _ : HasHummingbird ⦄ → HasWarbler
problem₁₁ = problem₆′
  where
    instance
      hasCardinal = Chapter₁₁.problem₂₁

      hasConverseWarbler : HasConverseWarbler
      hasConverseWarbler = record
        { W′ = H ∙ R
        ; isConverseWarbler = λ x y → begin
            H ∙ R ∙ x ∙ y  ≈⟨ isHummingbird R x y ⟩
            R ∙ x ∙ y ∙ x  ≈⟨ isRobin x y x ⟩
            y ∙ x ∙ x      ∎
        }

problem₁₂ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasStarling
problem₁₂ = record
  { S = W** ∙ G
  ; isStarling = λ x y z → begin
      W** ∙ G ∙ x ∙ y ∙ z  ≈⟨ isWarblerTwiceRemoved G x y z ⟩
      G ∙ x ∙ y ∙ z ∙ z    ≈⟨ isGoldfinch x y z z ⟩
      x ∙ z ∙ (y ∙ z)      ∎
  } where
  instance
    hasWarblerTwiceRemoved = problem₉-W**
    hasGoldfinch = Chapter₁₁.problem₄₇

problem₁₃ : ⦃ _ : HasStarling ⦄ ⦃ _ : HasRobin ⦄ → HasHummingbird
problem₁₃ = record
  { H = S ∙ R
  ; isHummingbird = λ x y z → begin
      S ∙ R ∙ x ∙ y ∙ z    ≈⟨ congʳ $ isStarling R x y ⟩
      R ∙ y ∙ (x ∙ y) ∙ z  ≈⟨ isRobin y (x ∙ y) z ⟩
      x ∙ y ∙ z ∙ y        ∎
  }

problem₁₄-SR : ⦃ _ : HasStarling ⦄ ⦃ _ : HasRobin ⦄ → HasWarbler
problem₁₄-SR = record
  { W = R ∙ (S ∙ R ∙ R) ∙ R
  ; isWarbler = λ x y → begin
      R ∙ (S ∙ R ∙ R) ∙ R ∙ x ∙ y      ≈˘⟨ congʳ $ congʳ $ isRobin R R (S ∙ R ∙ R) ⟩
      R ∙ R ∙ R ∙ (S ∙ R ∙ R) ∙ x ∙ y  ≈⟨⟩
      C ∙ (S ∙ R ∙ R) ∙ x ∙ y          ≈⟨⟩
      C ∙ (H ∙ R) ∙ x ∙ y              ≈⟨⟩
      W ∙ x ∙ y                        ≈⟨ isWarbler x y ⟩
      x ∙ y ∙ y                        ∎
  } where
  instance
    hasCardinal = Chapter₁₁.problem₂₁
    hasHummingbird = problem₁₃
    hasWarbler = problem₁₁

problem₁₄-SC : ⦃ _ : HasStarling ⦄ ⦃ _ : HasCardinal ⦄ → HasWarbler
problem₁₄-SC = record
  { W = C ∙ (S ∙ (C ∙ C) ∙ (C ∙ C))
  ; isWarbler = λ x y → begin
      C ∙ (S ∙ (C ∙ C) ∙ (C ∙ C)) ∙ x ∙ y  ≈⟨⟩
      C ∙ (S ∙ R ∙ R) ∙ x ∙ y              ≈⟨ isCardinal (S ∙ R ∙ R) x y ⟩
      S ∙ R ∙ R ∙ y ∙ x                    ≈⟨ congʳ $ isStarling R R y ⟩
      R ∙ y ∙ (R ∙ y) ∙ x                  ≈⟨ isRobin y (R ∙ y) x ⟩
      R ∙ y ∙ x ∙ y                        ≈⟨ isRobin y x y ⟩
      x ∙ y ∙ y                            ∎
  } where instance hasRobin = Chapter₁₁.problem₂₃

problem₁₅ : ⦃ _ : HasThrush ⦄ ⦃ _ : HasStarling ⦄ → HasWarbler
problem₁₅ = record
  { W = S ∙ T
  ; isWarbler = λ x y → begin
      S ∙ T ∙ x ∙ y    ≈⟨ isStarling T x y ⟩
      T ∙ y ∙ (x ∙ y)  ≈⟨ isThrush y (x ∙ y) ⟩
      x ∙ y ∙ y        ∎
  }

problem₁₆ : ⦃ _ : HasThrush ⦄ ⦃ _ : HasStarling ⦄ → HasMockingbird
problem₁₆ = record
  { M = S ∙ T ∙ T
  ; isMockingbird = λ x → begin
      S ∙ T ∙ T ∙ x    ≈⟨ isStarling T T x ⟩
      T ∙ x ∙ (T ∙ x)  ≈⟨ isThrush x (T ∙ x) ⟩
      T ∙ x ∙ x        ≈⟨ isThrush x x ⟩
      x ∙ x            ∎
  }

module Exercises where
  exercise₁-a : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄
    → ∃[ G₁ ] (∀ x y z w v → G₁ ∙ x ∙ y ∙ z ∙ w ∙ v ≈ x ∙ y ∙ v ∙ (z ∙ w))
  exercise₁-a =
    ( D₁ ∙ C*
    , λ x y z w v → begin
        D₁ ∙ C* ∙ x ∙ y ∙ z ∙ w ∙ v  ≈⟨ congʳ $ isDickcissel C* x y z w ⟩
        C* ∙ x ∙ y ∙ (z ∙ w) ∙ v     ≈⟨ isCardinalOnceRemoved x y (z ∙ w) v ⟩
        x ∙ y ∙ v ∙ (z ∙ w)          ∎
    )
    where
      instance
        hasCardinal = Chapter₁₁.problem₂₁′
        hasCardinalOnceRemoved = Chapter₁₁.problem₃₁
        hasDickcissel = Chapter₁₁.problem₉

  -- Note: in the solutions, the bluebird is used, but the exercise does not
  -- state that the bluebird may be used.
  exercise₁-b : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄
    → ∃[ G₁ ] (∀ x y z w v → G₁ ∙ x ∙ y ∙ z ∙ w ∙ v ≈ x ∙ y ∙ v ∙ (z ∙ w))
    → ∃[ G₂ ] (∀ x y z w → G₂ ∙ x ∙ y ∙ z ∙ w ≈ x ∙ w ∙ (x ∙ w) ∙ (y ∙ z))
  exercise₁-b (G₁ , isG₁) =
    ( G₁ ∙ (B ∙ M)
    , λ x y z w → begin
        G₁ ∙ (B ∙ M) ∙ x ∙ y ∙ z ∙ w  ≈⟨ isG₁ (B ∙ M) x y z w ⟩
        B ∙ M ∙ x ∙ w ∙ (y ∙ z)       ≈⟨ congʳ $ isBluebird M x w ⟩
        M ∙ (x ∙ w) ∙ (y ∙ z)         ≈⟨ congʳ $ isMockingbird (x ∙ w) ⟩
        x ∙ w ∙ (x ∙ w) ∙ (y ∙ z)     ∎
    )

  exercise₁-c : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasIdentity ⦄
    → ∃[ I₂ ] (∀ x → I₂ ∙ x ≈ x ∙ I ∙ I)
  exercise₁-c =
    ( B ∙ (T ∙ I) ∙ (T ∙ I)
    , λ x → begin
        B ∙ (T ∙ I) ∙ (T ∙ I) ∙ x  ≈⟨ isBluebird (T ∙ I) (T ∙ I) x ⟩
        T ∙ I ∙ (T ∙ I ∙ x)        ≈⟨ isThrush I (T ∙ I ∙ x) ⟩
        T ∙ I ∙ x ∙ I              ≈⟨ congʳ $ isThrush I x ⟩
        x ∙ I ∙ I                  ∎
    )

  exercise₁-d : ⦃ _ : HasIdentity ⦄ ⦃ _ : HasFinch ⦄
    → (hasI₂ : ∃[ I₂ ] (∀ x → I₂ ∙ x ≈ x ∙ I ∙ I))
    → ∀ x → proj₁ hasI₂ ∙ (F ∙ x) ≈ x
  exercise₁-d (I₂ , isI₂) x = begin
    I₂ ∙ (F ∙ x)   ≈⟨ isI₂ (F ∙ x) ⟩
    F ∙ x ∙ I ∙ I  ≈⟨ isFinch x I I ⟩
    I ∙ I ∙ x      ≈⟨ congʳ $ isIdentity I ⟩
    I ∙ x          ≈⟨ isIdentity x ⟩
    x              ∎

  exercise₁-e : ⦃ _ : HasFinch ⦄ ⦃ _ : HasQueerBird ⦄ ⦃ _ : HasIdentity ⦄
    → (hasG₂ : ∃[ G₂ ] (∀ x y z w → G₂ ∙ x ∙ y ∙ z ∙ w ≈ x ∙ w ∙ (x ∙ w) ∙ (y ∙ z)))
    → (hasI₂ : ∃[ I₂ ] (∀ x → I₂ ∙ x ≈ x ∙ I ∙ I))
    → IsWarbler (proj₁ hasG₂ ∙ F ∙ (Q ∙ proj₁ hasI₂))
  exercise₁-e (G₂ , isG₂) (I₂ , isI₂) x y = begin
    G₂ ∙ F ∙ (Q ∙ I₂) ∙ x ∙ y       ≈⟨ isG₂ F (Q ∙ I₂) x y ⟩
    F ∙ y ∙ (F ∙ y) ∙ (Q ∙ I₂ ∙ x)  ≈⟨ isFinch y (F ∙ y) (Q ∙ I₂ ∙ x) ⟩
    Q ∙ I₂ ∙ x ∙ (F ∙ y) ∙ y        ≈⟨ congʳ $ isQueerBird I₂ x (F ∙ y) ⟩
    x ∙ (I₂ ∙ (F ∙ y)) ∙ y          ≈⟨ congʳ $ congˡ $ exercise₁-d (I₂ , isI₂) y ⟩
    x ∙ y ∙ y                       ∎

  exercise₂ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄
    → IsStarling (B ∙ (B ∙ (B ∙ W) ∙ C) ∙ (B ∙ B))
  exercise₂ x y z = begin
    B ∙ (B ∙ (B ∙ W) ∙ C) ∙ (B ∙ B) ∙ x ∙ y ∙ z  ≈⟨ congʳ $ congʳ $ isBluebird (B ∙ (B ∙ W) ∙ C) (B ∙ B) x ⟩
    B ∙ (B ∙ W) ∙ C ∙ (B ∙ B ∙ x) ∙ y ∙ z        ≈⟨ congʳ $ congʳ $ isBluebird (B ∙ W) C (B ∙ B ∙ x) ⟩
    B ∙ W ∙ (C ∙ (B ∙ B ∙ x)) ∙ y ∙ z            ≈⟨ congʳ $ isBluebird W (C ∙ (B ∙ B ∙ x)) y ⟩
    W ∙ (C ∙ (B ∙ B ∙ x) ∙ y) ∙ z                ≈⟨ isWarbler (C ∙ (B ∙ B ∙ x) ∙ y) z ⟩
    C ∙ (B ∙ B ∙ x) ∙ y ∙ z ∙ z                  ≈⟨ congʳ $ isCardinal (B ∙ B ∙ x) y z ⟩
    B ∙ B ∙ x ∙ z ∙ y ∙ z                        ≈⟨ congʳ $ congʳ $ isBluebird B x z ⟩
    B ∙ (x ∙ z) ∙ y ∙ z                          ≈⟨ isBluebird (x ∙ z) y z ⟩
    x ∙ z ∙ (y ∙ z)                              ∎

  exercise₃ : ⦃ _ : HasStarling ⦄ ⦃ _ : HasBluebird ⦄ → HasPhoenix
  exercise₃ = record
    { Φ = B ∙ (B ∙ S) ∙ B
    ; isPhoenix = λ x y z w → begin
        B ∙ (B ∙ S) ∙ B ∙ x ∙ y ∙ z ∙ w  ≈⟨ congʳ $ congʳ $ congʳ $ isBluebird (B ∙ S) B x ⟩
        B ∙ S ∙ (B ∙ x) ∙ y ∙ z ∙ w      ≈⟨ congʳ $ congʳ $ isBluebird S (B ∙ x) y ⟩
        S ∙ (B ∙ x ∙ y) ∙ z ∙ w          ≈⟨ isStarling (B ∙ x ∙ y) z w ⟩
        B ∙ x ∙ y ∙ w ∙ (z ∙ w)          ≈⟨ congʳ $ isBluebird x y w ⟩
        x ∙ (y ∙ w) ∙ (z ∙ w)            ∎
    }

  exercise₄ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄ → HasPsiBird
  exercise₄ = record
    { Ψ = H* ∙ D₂
    ; isPsiBird = λ x y z w → begin
        H* ∙ D₂ ∙ x ∙ y ∙ z ∙ w     ≈⟨⟩
        B ∙ H ∙ D₂ ∙ x ∙ y ∙ z ∙ w  ≈⟨ congʳ $ congʳ $ congʳ $ isBluebird H D₂ x ⟩
        H ∙ (D₂ ∙ x) ∙ y ∙ z ∙ w    ≈⟨ congʳ $ isHummingbird (D₂ ∙ x) y z ⟩
        D₂ ∙ x ∙ y ∙ z ∙ y ∙ w      ≈⟨ isDovekie x y z y w ⟩
        x ∙ (y ∙ z) ∙ (y ∙ w)       ∎
    } where
    instance
      hasHummingbird = problem₁₀
      hasDovekie = Chapter₁₁.problem₁₁
    H* = B ∙ H

  -- NOTE: my copy of the book contains a mistake (looking at the given
  -- solutions): it says Γxyzwv = y(zw)(xywv) instead of Γxyzwv = y(zw)(xyzwv).
  exercise₅-a : ⦃ _ : HasPhoenix ⦄ ⦃ _ : HasBluebird ⦄
    → ∃[ Γ ] (∀ x y z w v → Γ ∙ x ∙ y ∙ z ∙ w ∙ v ≈ y ∙ (z ∙ w) ∙ (x ∙ y ∙ z ∙ w ∙ v))
  exercise₅-a =
    ( Φ ∙ (Φ ∙ (Φ ∙ B)) ∙ B
    , λ x y z w v → begin
        Φ ∙ (Φ ∙ (Φ ∙ B)) ∙ B ∙ x ∙ y ∙ z ∙ w ∙ v    ≈⟨ congʳ $ congʳ $ congʳ $ isPhoenix (Φ ∙ (Φ ∙ B)) B x y ⟩
        Φ ∙ (Φ ∙ B) ∙ (B ∙ y) ∙ (x ∙ y) ∙ z ∙ w ∙ v  ≈⟨ congʳ $ congʳ $ isPhoenix (Φ ∙ B) (B ∙ y) (x ∙ y) z ⟩
        Φ ∙ B ∙ (B ∙ y ∙ z) ∙ (x ∙ y ∙ z) ∙ w ∙ v    ≈⟨ congʳ $ isPhoenix B (B ∙ y ∙ z) (x ∙ y ∙ z) w ⟩
        B ∙ (B ∙ y ∙ z ∙ w) ∙ (x ∙ y ∙ z ∙ w) ∙ v    ≈⟨ isBluebird (B ∙ y ∙ z ∙ w) (x ∙ y ∙ z ∙ w) v ⟩
        B ∙ y ∙ z ∙ w ∙ (x ∙ y ∙ z ∙ w ∙ v)          ≈⟨ congʳ $ isBluebird y z w ⟩
        y ∙ (z ∙ w) ∙ (x ∙ y ∙ z ∙ w ∙ v)            ∎
    )

  exercise₅-b : ⦃ _ : HasKestrel ⦄
    → ∃[ Γ ] (∀ x y z w v → Γ ∙ x ∙ y ∙ z ∙ w ∙ v ≈ y ∙ (z ∙ w) ∙ (x ∙ y ∙ z ∙ w ∙ v))
    → HasPsiBird
  exercise₅-b (Γ , isΓ) = record
    { Ψ = Γ ∙ (K ∙ K)
    ; isPsiBird = λ x y z w → begin
        Γ ∙ (K ∙ K) ∙ x ∙ y ∙ z ∙ w            ≈⟨ isΓ (K ∙ K) x y z w ⟩
        x ∙ (y ∙ z) ∙ (K ∙ K ∙ x ∙ y ∙ z ∙ w)  ≈⟨ congˡ $ congʳ $ congʳ $ congʳ $ isKestrel K x ⟩
        x ∙ (y ∙ z) ∙ (K ∙ y ∙ z ∙ w)          ≈⟨ congˡ $ congʳ $ isKestrel y z ⟩
        x ∙ (y ∙ z) ∙ (y ∙ w)                  ∎
    }

  exercise₅ : ⦃ _ : HasPhoenix ⦄ ⦃ _ : HasBluebird ⦄ ⦃ _ : HasKestrel ⦄ → HasPsiBird
  exercise₅ = exercise₅-b exercise₅-a

  exercise₆-a : ⦃ _ : HasStarling ⦄ ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄
    → ∃[ S′ ] (∀ x y z → S′ ∙ x ∙ y ∙ z ≈ y ∙ z ∙ (x ∙ z))
  exercise₆-a =
    ( C ∙ S
    , λ x y z → begin
        C ∙ S ∙ x ∙ y ∙ z  ≈⟨ congʳ $ isCardinal S x y ⟩
        S ∙ y ∙ x ∙ z      ≈⟨ isStarling y x z ⟩
        y ∙ z ∙ (x ∙ z)    ∎
    )
    where instance hasCardinal = Chapter₁₁.problem₂₁′

  exercise₆-b : ⦃ _ : HasIdentity ⦄
    → ∃[ S′ ] (∀ x y z → S′ ∙ x ∙ y ∙ z ≈ y ∙ z ∙ (x ∙ z))
    → HasWarbler
  exercise₆-b (S′ , isS′) = record
    { W = S′ ∙ I
    ; isWarbler = λ x y → begin
        S′ ∙ I ∙ x ∙ y   ≈⟨ isS′ I x y ⟩
        x ∙ y ∙ (I ∙ y)  ≈⟨ congˡ $ isIdentity y ⟩
        x ∙ y ∙ y        ∎
    }

  exercise₇ : ⦃ _ : HasQueerBird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄
    → ∃[ Q̂ ] IsStarling (C ∙ Q̂ ∙ W)
  exercise₇ = let Q̂ = Q ∙ (Q ∙ Q ∙ (Q ∙ Q)) ∙ Q in
    ( Q̂
    , λ x y z → begin
        C ∙ Q̂ ∙ W ∙ x ∙ y ∙ z ≈⟨ congʳ $ congʳ $ isCardinal Q̂ W x ⟩
        Q̂ ∙ x ∙ W ∙ y ∙ z                          ≈⟨⟩
        Q ∙ (Q ∙ Q ∙ (Q ∙ Q)) ∙ Q ∙ x ∙ W ∙ y ∙ z  ≈⟨ congʳ $ congʳ $ congʳ $ isQueerBird (Q ∙ Q ∙ (Q ∙ Q)) Q  x ⟩
        Q ∙ (Q ∙ Q ∙ (Q ∙ Q) ∙ x) ∙ W ∙ y ∙ z      ≈⟨ congʳ $ isQueerBird (Q ∙ Q ∙ (Q ∙ Q) ∙ x) W y ⟩
        W ∙ (Q ∙ Q ∙ (Q ∙ Q) ∙ x ∙ y) ∙ z          ≈⟨ isWarbler (Q ∙ Q ∙ (Q ∙ Q) ∙ x ∙ y) z ⟩
        Q ∙ Q ∙ (Q ∙ Q) ∙ x ∙ y ∙ z ∙ z            ≈⟨ congʳ $ congʳ $ congʳ $ isQueerBird Q (Q ∙ Q) x ⟩
        Q ∙ Q ∙ (Q ∙ x) ∙ y ∙ z ∙ z                ≈⟨ congʳ $ congʳ $ isQueerBird Q (Q ∙ x) y ⟩
        Q ∙ x ∙ (Q ∙ y) ∙ z ∙ z                    ≈⟨ congʳ $ isQueerBird x (Q ∙ y) z ⟩
        Q ∙ y ∙ (x ∙ z) ∙ z                        ≈⟨ isQueerBird y (x ∙ z) z ⟩
        x ∙ z ∙ (y ∙ z)                            ∎
    )


module Issue478 where

record Ko (Q : Set) : Set₁ where
 field
  T : Set

module Bo (P : Set) (ko : Ko P) where
  open Ko ko

  err : T
  err = Set

{- The error message was:
  Set₁ !=< T P ko of type Set₂
  when checking that the expression Set has type T P ko

  We now get the desired error message:

  Set₁ !=< T of type Set₂
  when checking that the expression Set has type T

-}


open import Agda.Builtin.Unit
open import Agda.Builtin.Bool
open import Agda.Builtin.List
open import Agda.Builtin.Reflection
open import Agda.Builtin.Equality

variable
  A B : Set

data Eqn (A : Set) : Set where
  eqn : (x y : A) → x ≡ y → Eqn A

infix 3 _==_
pattern _==_ x y = eqn x y refl

infixl 0 such-that
syntax such-that a (λ x → P) p = a is x such-that p proves P
such-that : (x : A) (P : A → Set) → P x → A
such-that x _ _ = x

infix -1 check_
check_ : A → ⊤
check _ = _

infixl -2 _and_
_and_ : ⊤ → A → ⊤
_ and _ = _

-- Record fields can be marked with @tactic

data ShouldRun : Set where
  run norun : ShouldRun

defaultTo : {A : Set} (x : A) → ShouldRun → Term → TC ⊤
defaultTo _ norun _    = typeError (strErr "Should not run!" ∷ [])
defaultTo x run   hole = bindTC (quoteTC x) (unify hole)

record Class (r : ShouldRun) : Set where
  constructor con
  field
    x : Bool
    @(tactic defaultTo x r) {y} : Bool
open Class

-- # Cases where the tactic should run

test₁ test₂ test₃ : Class run
test₁    = con true             -- Constructor application
test₂    = record { x = true }  -- Record construction
test₃ .x = true                 -- Missing copattern clause

_ = check test₁ == con true {true}
      and test₂ == con true {true}
      and test₃ == con true {true}

-- More elaborate missing copatterns
test₃′ : List Bool → Class run
test₃′ []      .x = false
test₃′ []      .y = true
test₃′ (b ∷ _) .x = b

_ =
  check test₃′ []             == con false {true}
    and λ b → test₃′ (b ∷ []) == con b {b}

-- # Cases where the tactic should not run

-- ## Giving the field explicitly

test₄ test₅ : Class norun
test₄ = con true {_}              is c such-that refl proves c .y ≡ false
test₅ = record{ x = true; y = _ } is c such-that refl proves c .y ≡ false

_ = check test₄ == con true {false}
      and test₅ == con true {false}

-- ## Eta-expansion

-- Eta-expansion of record metas should *not* trigger the tactic, since that
-- would make eta-expansion potentially lose solutions. For instance,
-- `con true {false} == _1`. Here solving `_1 := con true {false}` is valid, but
-- eta-expanding to `con _2 {_3}` and running `defaultTo _2 _3` leads to an error.

test₆ : Class norun
test₆ =
  _ is c such-that refl proves c .x ≡ true
    is c such-that refl proves c .y ≡ false

check₆ : test₆ ≡ con true {false}
check₆ = refl


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

{-
This file contains a bunch of basic stuff which is needed early.
Maybe it should be organised better.
-}

module lib.Base where

{- Universes and typing

Agda has explicit universe polymorphism, which means that there is an actual
type of universe levels on which you can quantify. This type is called [ULevel]
and comes equipped with the following operations:

- [lzero] : [ULevel] (in order to have at least one universe)
- [lsucc] : [ULevel → ULevel] (the [i]th universe level is a term in the
  [lsucc i]th universe)
- [lmax] : [ULevel → ULevel → ULevel] (in order to type dependent products (where
  the codomain is in a uniform universe level)

This type is postulated below and linked to Agda’s universe polymorphism
mechanism via the built-in module Agda.Primitive (it’s the new way).

In plain Agda, the [i]th universe is called [Set i], which is not a very good
name from the point of view of HoTT, so we define [Type] as a synonym of [Set]
and [Set] should never be used again.
-}

open import Agda.Primitive public using (lzero)
  renaming (Level to ULevel; lsuc to lsucc; _⊔_ to lmax)

Type : (i : ULevel) → Set (lsucc i)
Type i = Set i

Type₀ = Type lzero
Type0 = Type lzero

Type₁ = Type (lsucc lzero)
Type1 = Type (lsucc lzero)

{-
There is no built-in or standard way to coerce an ambiguous term to a given type
(like [u : A] in ML), the symbol [:] is reserved, and the Unicode [∶] is really
a bad idea.
So we’re using the symbol [_:>_], which has the advantage that it can micmic
Coq’s [u = v :> A].
-}

of-type : ∀ {i} (A : Type i) (u : A) → A
of-type A u = u

infix 40 of-type
syntax of-type A u =  u :> A

{- Instance search -}

⟨⟩ : ∀ {i} {A : Type i} {{a : A}} → A
⟨⟩ {{a}} = a

{- Identity type

The identity type is called [Path] and [_==_] because the symbol [=] is
reserved in Agda.
The constant path is [idp]. Note that all arguments of [idp] are implicit.
-}

infix 30 _==_
data _==_ {i} {A : Type i} (a : A) : A → Type i where
  idp : a == a

Path = _==_

{-# BUILTIN EQUALITY _==_ #-}

{- Paulin-Mohring J rule

At the time I’m writing this (July 2013), the identity type is somehow broken in
Agda dev, it behaves more or less as the Martin-Löf identity type instead of
behaving like the Paulin-Mohring identity type.
So here is the Paulin-Mohring J rule -}

J : ∀ {i j} {A : Type i} {a : A} (B : (a' : A) (p : a == a') → Type j) (d : B a idp)
  {a' : A} (p : a == a') → B a' p
J B d idp = d

J' : ∀ {i j} {A : Type i} {a : A} (B : (a' : A) (p : a' == a) → Type j) (d : B a idp)
  {a' : A} (p : a' == a) → B a' p
J' B d idp = d

{- Rewriting

This is a new pragma added to Agda to help create higher inductive types.
-}

infix 30 _↦_
postulate  -- HIT
  _↦_ : ∀ {i} {A : Type i} → A → A → Type i

{-# BUILTIN REWRITE _↦_ #-}

{- Unit type

The unit type is defined as record so that we also get the η-rule definitionally.
-}

record ⊤ : Type₀ where
  instance constructor unit

Unit = ⊤

{-# BUILTIN UNIT ⊤ #-}

{- Dependent paths

The notion of dependent path is a very important notion.
If you have a dependent type [B] over [A], a path [p : x == y] in [A] and two
points [u : B x] and [v : B y], there is a type [u == v [ B ↓ p ]] of paths from
[u] to [v] lying over the path [p].
By definition, if [p] is a constant path, then [u == v [ B ↓ p ]] is just an
ordinary path in the fiber.
-}

PathOver : ∀ {i j} {A : Type i} (B : A → Type j)
  {x y : A} (p : x == y) (u : B x) (v : B y) → Type j
PathOver B idp u v = (u == v)

infix 30 PathOver
syntax PathOver B p u v =
  u == v [ B ↓ p ]

{- Ap, coe and transport

Given two fibrations over a type [A], a fiberwise map between the two fibrations
can be applied to any dependent path in the first fibration ([ap↓]).
As a special case, when [A] is [Unit], we find the familiar [ap] ([ap] is
defined in terms of [ap↓] because it shouldn’t change anything for the user
and this is helpful in some rare cases)
-}

ap : ∀ {i j} {A : Type i} {B : Type j} (f : A → B) {x y : A}
  → (x == y → f x == f y)
ap f idp = idp

ap↓ : ∀ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  (g : {a : A} → B a → C a) {x y : A} {p : x == y}
  {u : B x} {v : B y}
  → (u == v [ B ↓ p ] → g u == g v [ C ↓ p ])
ap↓ g {p = idp} p = ap g p

{-
[apd↓] is defined in lib.PathOver. Unlike [ap↓] and [ap], [apd] is not
definitionally a special case of [apd↓]
-}

apd : ∀ {i j} {A : Type i} {B : A → Type j} (f : (a : A) → B a) {x y : A}
  → (p : x == y) → f x == f y [ B ↓ p ]
apd f idp = idp

{-
An equality between types gives two maps back and forth
-}

coe : ∀ {i} {A B : Type i} (p : A == B) → A → B
coe idp x = x

coe! : ∀ {i} {A B : Type i} (p : A == B) → B → A
coe! idp x = x

{-
The operations of transport forward and backward are defined in terms of [ap]
and [coe], because this is more convenient in practice.
-}

transport : ∀ {i j} {A : Type i} (B : A → Type j) {x y : A} (p : x == y)
  → (B x → B y)
transport B p = coe (ap B p)

transport! : ∀ {i j} {A : Type i} (B : A → Type j) {x y : A} (p : x == y)
  → (B y → B x)
transport! B p = coe! (ap B p)

{- Π-types

Shorter notation for Π-types.
-}

Π : ∀ {i j} (A : Type i) (P : A → Type j) → Type (lmax i j)
Π A P = (x : A) → P x

{- Σ-types

Σ-types are defined as a record so that we have definitional η.
-}

infixr 60 _,_

record Σ {i j} (A : Type i) (B : A → Type j) : Type (lmax i j) where
  constructor _,_
  field
    fst : A
    snd : B fst
open Σ public

pair= : ∀ {i j} {A : Type i} {B : A → Type j}
  {a a' : A} (p : a == a') {b : B a} {b' : B a'}
  (q : b == b' [ B ↓ p ])
  → (a , b) == (a' , b')
pair= idp q = ap (_ ,_) q

pair×= : ∀ {i j} {A : Type i} {B : Type j}
  {a a' : A} (p : a == a') {b b' : B} (q : b == b')
  → (a , b) == (a' , b')
pair×= idp q = pair= idp q

{- Empty type

We define the eliminator of the empty type using an absurd pattern. Given that
absurd patterns are not consistent with HIT, we will not use empty patterns
anymore after that.
-}

data ⊥ : Type₀ where

Empty = ⊥

⊥-elim : ∀ {i} {P : ⊥ → Type i} → ((x : ⊥) → P x)
⊥-elim ()

Empty-elim = ⊥-elim

{- Negation and disequality -}

¬ : ∀ {i} (A : Type i) → Type i
¬ A = A → ⊥

_≠_ : ∀ {i} {A : Type i} → (A → A → Type i)
x ≠ y = ¬ (x == y)

{- Natural numbers -}

data ℕ : Type₀ where
  O : ℕ
  S : (n : ℕ) → ℕ

Nat = ℕ

{-# BUILTIN NATURAL ℕ #-}

{- Lifting to a higher universe level

The operation of lifting enjoys both β and η definitionally.
It’s a bit annoying to use, but it’s not used much (for now).
-}

record Lift {i j} (A : Type i) : Type (lmax i j) where
  instance constructor lift
  field
    lower : A
open Lift public

{- Equational reasoning

Equational reasoning is a way to write readable chains of equalities.
The idea is that you can write the following:

  t : a == e
  t = a =⟨ p ⟩
      b =⟨ q ⟩
      c =⟨ r ⟩
      d =⟨ s ⟩
      e ∎

where [p] is a path from [a] to [b], [q] is a path from [b] to [c], and so on.

You often have to apply some equality in some context, for instance [p] could be
[ap ctx thm] where [thm] is the interesting theorem used to prove that [a] is
equal to [b], and [ctx] is the context.
In such cases, you can use instead [thm |in-ctx ctx]. The advantage is that
[ctx] is usually boring whereas the first word of [thm] is the most interesting
part.

_=⟨_⟩ is not definitionally the same thing as concatenation of paths _∙_ because
we haven’t defined concatenation of paths yet, and also you probably shouldn’t
reason on paths constructed with equational reasoning.
If you do want to reason on paths constructed with equational reasoning, check
out lib.types.PathSeq instead.
-}

infixr 10 _=⟨_⟩_
infix  15 _=∎

_=⟨_⟩_ : ∀ {i} {A : Type i} (x : A) {y z : A} → x == y → y == z → x == z
_ =⟨ idp ⟩ idp = idp

_=∎ : ∀ {i} {A : Type i} (x : A) → x == x
_ =∎ = idp

infixl 40 ap
syntax ap f p = p |in-ctx f

{- Various basic functions and function operations

The identity function on a type [A] is [idf A] and the constant function at some
point [b] is [cst b].

Composition of functions ([_∘_]) can handle dependent functions.
-}

idf : ∀ {i} (A : Type i) → (A → A)
idf A = λ x → x

cst : ∀ {i j} {A : Type i} {B : Type j} (b : B) → (A → B)
cst b = λ _ → b

infixr 80 _∘_

_∘_ : ∀ {i j k} {A : Type i} {B : A → Type j} {C : (a : A) → (B a → Type k)}
  → (g : {a : A} → Π (B a) (C a)) → (f : Π A B) → Π A (λ a → C a (f a))
g ∘ f = λ x → g (f x)

-- Application
infixr 0 _$_
_$_ : ∀ {i j} {A : Type i} {B : A → Type j} → (∀ x → B x) → (∀ x → B x)
f $ x = f x

-- (Un)curryfication
curry : ∀ {i j k} {A : Type i} {B : A → Type j} {C : Σ A B → Type k}
  → (∀ s → C s) → (∀ x y → C (x , y))
curry f x y = f (x , y)

uncurry : ∀ {i j k} {A : Type i} {B : A → Type j} {C : ∀ x → B x → Type k}
  → (∀ x y → C x y) → (∀ s → C (fst s) (snd s))
uncurry f (x , y) = f x y

{- Truncation levels

The type of truncation levels is isomorphic to the type of natural numbers but
"starts at -2".
-}

data TLevel : Type₀ where
  ⟨-2⟩ : TLevel
  S : (n : TLevel) → TLevel

ℕ₋₂ = TLevel

⟨_⟩₋₂ : ℕ → ℕ₋₂
⟨ O ⟩₋₂ = ⟨-2⟩
⟨ S n ⟩₋₂ = S ⟨ n ⟩₋₂

{- Coproducts and case analysis -}

data Coprod {i j} (A : Type i) (B : Type j) : Type (lmax i j) where
  inl : A → Coprod A B
  inr : B → Coprod A B

infixr 80 _⊔_
_⊔_ = Coprod

Dec : ∀ {i} (P : Type i) → Type i
Dec P = P ⊔ ¬ P

{-
Pointed types and pointed maps.

[A ⊙→ B] was pointed, but it was never used as a pointed type.
-}

infix 60 ⊙[_,_]

record Ptd (i : ULevel) : Type (lsucc i) where
  constructor ⊙[_,_]
  field
    de⊙ : Type i
    pt : de⊙
open Ptd public

ptd : ∀ {i} (A : Type i) → A → Ptd i
ptd = ⊙[_,_]

ptd= : ∀ {i} {A A' : Type i} (p : A == A')
  {a : A} {a' : A'} (q : a == a' [ idf _ ↓ p ])
  → ⊙[ A , a ] == ⊙[ A' , a' ]
ptd= idp q = ap ⊙[ _ ,_] q

Ptd₀ = Ptd lzero

infixr 0 _⊙→_
_⊙→_ : ∀ {i j} → Ptd i → Ptd j → Type (lmax i j)
⊙[ A , a₀ ] ⊙→ ⊙[ B , b₀ ] = Σ (A → B) (λ f → f a₀ == b₀)

⊙idf : ∀ {i} (X : Ptd i) → X ⊙→ X
⊙idf X = (λ x → x) , idp

⊙cst : ∀ {i j} {X : Ptd i} {Y : Ptd j} → X ⊙→ Y
⊙cst {Y = Y} = (λ x → pt Y) , idp

{-
Used in a hack to make HITs maybe consistent. This is just a parametrized unit
type (positively)
-}

data Phantom {i} {A : Type i} (a : A) : Type₀ where
  phantom : Phantom a

{-
Numeric literal overloading

This enables writing numeric literals
-}

record FromNat {i} (A : Type i) : Type (lsucc i) where
  field
    in-range : ℕ → Type i
    read : ∀ n → ⦃ _ : in-range n ⦄ → A

open FromNat ⦃...⦄ public using () renaming (read to from-nat)

{-# BUILTIN FROMNAT from-nat #-}

record FromNeg {i} (A : Type i) : Type (lsucc i) where
  field
    in-range : ℕ → Type i
    read : ∀ n → ⦃ _ : in-range n ⦄ → A

open FromNeg ⦃...⦄ public using () renaming (read to from-neg)

{-# BUILTIN FROMNEG from-neg #-}

instance
  ℕ-reader : FromNat ℕ
  FromNat.in-range ℕ-reader _ = ⊤
  FromNat.read ℕ-reader n = n

  TLevel-reader : FromNat TLevel
  FromNat.in-range TLevel-reader _ = ⊤
  FromNat.read TLevel-reader n = S (S ⟨ n ⟩₋₂)

  TLevel-neg-reader : FromNeg TLevel
  FromNeg.in-range TLevel-neg-reader O = ⊤
  FromNeg.in-range TLevel-neg-reader 1 = ⊤
  FromNeg.in-range TLevel-neg-reader 2 = ⊤
  FromNeg.in-range TLevel-neg-reader (S (S (S _))) = ⊥
  FromNeg.read TLevel-neg-reader O = S (S ⟨-2⟩)
  FromNeg.read TLevel-neg-reader 1 = S ⟨-2⟩
  FromNeg.read TLevel-neg-reader 2 = ⟨-2⟩
  FromNeg.read TLevel-neg-reader (S (S (S _))) ⦃()⦄


------------------------------------------------------------------------
-- A simple backend
------------------------------------------------------------------------

module RecursiveDescent.Hybrid.Simple where

open import Data.Bool renaming (true to ⊤; false to ⊥)
open import Data.Product.Record
import Data.Product as Prod
open import Data.Maybe
open import Data.BoundedVec.Inefficient
import Data.List as L
open import Data.Nat
open import Data.Function using (id; _∘_)
open import Category.Applicative.Indexed
open import Category.Monad.Indexed
open import Category.Monad.State

open import RecursiveDescent.Hybrid.Type
open import Utilities

------------------------------------------------------------------------
-- Parser monad

private

  P : Set -> IFun ℕ
  P tok = IStateT (BoundedVec tok) L.List

  open module M₁ {tok} =
    RawIMonadPlus (StateTIMonadPlus (BoundedVec tok) L.monadPlus)
    renaming (return to ret)

  open module M₂ {tok} =
    RawIMonadState (StateTIMonadState (BoundedVec tok) L.monad)
    using (get; put; modify)

------------------------------------------------------------------------
-- Run function for the parsers

-- For every successful parse the run function returns the remaining
-- string. (Since there can be several successful parses a list of
-- strings is returned.)

-- This function is structurally recursive with respect to the
-- following lexicographic measure:
--
-- 1) The upper bound of the length of the input string.
-- 2) The parser's proper left corner tree.

private

 module Dummy {tok nt} (g : Grammar tok nt) where

  mutual
    parse : forall n {e c r} ->
            Parser tok nt (e , c) r ->
            P tok n (if e then n else pred n) r
    parse n       (! x)           = parse n (g x)
    parse zero    symbol          = ∅
    parse (suc n) symbol          = eat =<< get
    parse n       (return x)      = ret x
    parse n       fail            = ∅
    parse n       (p₁ ?>>= p₂)    = parse  n      p₁ >>= parse  n ∘′ p₂
    parse zero    (p₁ !>>= p₂)    = ∅
    parse (suc n) (p₁ !>>= p₂)    = parse (suc n) p₁ >>= parse↑ n ∘′ p₂
    parse n       (alt ⊤ _ p₁ p₂) = parse  n      p₁ ∣   parse↑ n    p₂
    parse n       (alt ⊥ ⊤ p₁ p₂) = parse↑ n      p₁ ∣   parse  n    p₂
    parse n       (alt ⊥ ⊥ p₁ p₂) = parse  n      p₁ ∣   parse  n    p₂

    parse↑ : forall n {e c r} -> Parser tok nt (e , c) r -> P tok n n r
    parse↑ n       {⊤} p = parse n p
    parse↑ zero    {⊥} p = ∅
    parse↑ (suc n) {⊥} p = parse (suc n) p >>= \r ->
                           modify ↑ >>
                           ret r

    eat : forall {n} -> BoundedVec tok (suc n) -> P tok (suc n) n tok
    eat []      = ∅
    eat (c ∷ s) = put s >> ret c

-- Exported run function.

parse :  forall {tok nt i r}
      -> Parser tok nt i r -> Grammar tok nt
      -> L.List tok -> L.List (Prod._×_ r (L.List tok))
parse p g s = L.map (Prod.map id toList)
                    (Dummy.parse g _ p (fromList s))

-- A variant which only returns parses which leave no remaining input.

parse-complete :  forall {tok nt i r}
               -> Parser tok nt i r -> Grammar tok nt
               -> L.List tok -> L.List r
parse-complete p g s =
  L.map Prod.proj₁ (L.filter (L.null ∘ Prod.proj₂) (parse p g s))


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

open import Prelude
open import GSeTT.Syntax
open import GSeTT.Rules
open import Sets ℕ eqdecℕ

{- PS-contexts -}
module CaTT.Ps-contexts where

  {- Rules for PS-contexts -}
  data _⊢ps_#_ : Pre-Ctx → ℕ → Pre-Ty → Set₁ where
    pss : (nil :: (O , ∗)) ⊢ps O # ∗
    psd : ∀ {Γ f A x y} → Γ ⊢ps f # (⇒ A (Var x) (Var y)) → Γ ⊢ps y # A
    pse : ∀ {Γ x A y z C t Tt} → Γ ⊢ps x # A  →  y == length Γ → z == S (length Γ) → C == ⇒ A (Var x) (Var y) → z == t → C == Tt → ((Γ :: (y , A)) :: (z , C)) ⊢ps t # Tt

  data _⊢ps : Pre-Ctx → Set₁ where
    ps : ∀ {Γ x} → Γ ⊢ps x # ∗ → Γ ⊢ps

  psd↓ : ∀ {Γ f A x y z} → (Γ⊢ps₁ : Γ ⊢ps f # (⇒ A (Var x) (Var z))) → (Γ⊢ps₂ : Γ ⊢ps f # (⇒ A (Var y) (Var z))) → (p : x == y) → transport p Γ⊢ps₁ == Γ⊢ps₂ → psd Γ⊢ps₁ == psd Γ⊢ps₂
  psd↓ Γ⊢ps₁ .Γ⊢ps₁ idp idp = idp

  pse↓ : ∀ {Γ A B C x y a f z} → (Γ⊢ps₁ : Γ ⊢ps x # A) (a= : a == length Γ) (f= : f == S (length Γ)) (B= : B == ⇒ A (Var x) (Var a)) (z= : f == z) (C= : B == C)
                                → (Γ⊢ps₂ : Γ ⊢ps y # A) (a=' : a == length Γ) (f=' : f == S (length Γ)) (B=' : B == ⇒ A (Var y) (Var a)) (z=' : f == z) (C=' : B == C)
                                → (p : x == y)  → transport p Γ⊢ps₁ == Γ⊢ps₂ → (pse Γ⊢ps₁ a= f= B= z= C=) == (pse Γ⊢ps₂ a=' f=' B=' z=' C=')
  pse↓ _ _ _ _ _ _ _ _ _ _ _ _ idp idp = ap⁶ pse idp (is-prop-has-all-paths (is-setℕ _ _) _ _)
                                                      (is-prop-has-all-paths (is-setℕ _ _) _ _)
                                                      (is-prop-has-all-paths (eqdec-is-set eqdec-PreTy _ _) _ _)
                                                      (is-prop-has-all-paths (is-setℕ _ _) _ _)
                                                      (is-prop-has-all-paths (eqdec-is-set eqdec-PreTy _ _) _ _)

  ps↓ : ∀ {Γ x y} → (Γ⊢ps₁ : Γ ⊢ps x # ∗) → (Γ⊢ps₂ : Γ ⊢ps y # ∗)
                  → (p : x == y)  → transport {B = λ z → Γ ⊢ps z # ∗} p Γ⊢ps₁ == Γ⊢ps₂ → (ps Γ⊢ps₁) == (ps Γ⊢ps₂)
  ps↓ _ _ idp idp = idp

  {- PS-contexts define valid contexts -}
  Γ⊢ps→Γ⊢ : ∀ {Γ} → Γ ⊢ps → Γ ⊢C
  Γ⊢psx:A→Γ⊢x:A : ∀ {Γ x A} → Γ ⊢ps x # A → Γ ⊢t (Var x) # A

  Γ⊢ps→Γ⊢ (ps Γ⊢psx) = Γ⊢t:A→Γ⊢ (Γ⊢psx:A→Γ⊢x:A Γ⊢psx)
  Γ⊢psx:A→Γ⊢x:A pss = var (cc ec (ob ec) idp) (inr (idp , idp))
  Γ⊢psx:A→Γ⊢x:A (psd Γ⊢psf:x⇒y) with Γ⊢t:A→Γ⊢A (Γ⊢psx:A→Γ⊢x:A Γ⊢psf:x⇒y)
  Γ⊢psx:A→Γ⊢x:A (psd Γ⊢psf:x⇒y) | ar _ Γ⊢y:A = Γ⊢y:A
  Γ⊢psx:A→Γ⊢x:A (pse Γ⊢psx:A idp idp idp idp idp) with (cc (Γ⊢t:A→Γ⊢ (Γ⊢psx:A→Γ⊢x:A Γ⊢psx:A)) (Γ⊢t:A→Γ⊢A (Γ⊢psx:A→Γ⊢x:A Γ⊢psx:A)) idp)
  ...                          | Γ,y:A⊢ = var (cc Γ,y:A⊢ (ar (wkt (Γ⊢psx:A→Γ⊢x:A Γ⊢psx:A) Γ,y:A⊢) (var Γ,y:A⊢ (inr (idp , idp)))) idp) (inr (idp , idp))

  Γ⊢psx:A→Γ⊢ : ∀ {Γ x A} → Γ ⊢ps x # A → Γ ⊢C
  Γ⊢psx:A→Γ⊢ Γ⊢psx:A = Γ⊢t:A→Γ⊢ (Γ⊢psx:A→Γ⊢x:A Γ⊢psx:A)

  -- some renamings so that useful lemmas are faster to write
  -- TODO restrict these as local names
  psv : ∀ {Γ x A} → Γ ⊢ps x # A → Γ ⊢C --psvalid
  psv Γ⊢ps = Γ⊢psx:A→Γ⊢ Γ⊢ps

  psvar : ∀ {Γ x A} → Γ ⊢ps x # A → Γ ⊢t (Var x) # A
  psvar Γ⊢psx = Γ⊢psx:A→Γ⊢x:A Γ⊢psx

  ps-ctx : Set₁
  ps-ctx = Σ Pre-Ctx (λ Γ → Γ ⊢ps)

  {- Eric's trick : is a src-var -}
  srcᵢ-var : ∀ {Γ x A} → ℕ → Γ ⊢ps x # A → list ℕ
  srcᵢ-var i pss = if i ≡ 0 then nil else (nil :: 0)
  srcᵢ-var i (psd Γ⊢psx) = srcᵢ-var i Γ⊢psx
  srcᵢ-var i (pse {Γ = Γ} {A = A} Γ⊢psx idp idp idp idp idp) with dec-≤ i (S (dim A))
  ... | inl i≤SdimA = srcᵢ-var i Γ⊢psx
  ... | inr SdimA<i = (srcᵢ-var i Γ⊢psx :: length Γ) :: (S (length Γ))

  drop : ∀ {i} {A : Set i} → list A → list A
  drop nil = nil
  drop (l :: a) = l

  tgtᵢ-var : ∀ {Γ x A} → ℕ → Γ ⊢ps x # A → list ℕ
  tgtᵢ-var i pss = if i ≡ 0 then nil else (nil :: 0)
  tgtᵢ-var i (psd Γ⊢psx) = tgtᵢ-var i Γ⊢psx
  tgtᵢ-var i (pse {Γ = Γ} {A = A} Γ⊢psx idp idp idp idp idp) with dec-≤ i (S (dim A))
  ... | inl i≤SdimA = if i ≡ S (dim A) then drop(tgtᵢ-var i Γ⊢psx) :: length Γ else tgtᵢ-var i Γ⊢psx
  ... | inr SdimA<i = (tgtᵢ-var i Γ⊢psx :: length Γ) :: (S (length Γ))

  src-var : (Γ : ps-ctx) → set
  src-var (Γ , ps Γ⊢psx) = set-of-list (srcᵢ-var (dimC Γ) Γ⊢psx)

  tgt-var : (Γ : ps-ctx) → set
  tgt-var (Γ , ps Γ⊢psx) = set-of-list (tgtᵢ-var (dimC Γ) Γ⊢psx)

  dim-psx-not-𝔻0 : ∀ {Γ x A} → (Γ⊢ps : Γ ⊢ps x # A) → (Γ ≠ (nil :: (0 , ∗))) → 0 < dimC Γ
  dim-psx-not-𝔻0 {.(nil :: (0 , ∗))} {.0} {.∗} pss Γ≠𝔻0 = ⊥-elim (Γ≠𝔻0 idp)
  dim-psx-not-𝔻0 {Γ} {x} {A} (psd Γ⊢psx) Γ≠𝔻0 = dim-psx-not-𝔻0 Γ⊢psx Γ≠𝔻0
  dim-psx-not-𝔻0 {_} {x} {A} (pse {Γ = Γ} Γ⊢psx idp idp idp idp idp) Γ+≠𝔻0 = ≤T (S≤ (0≤ _)) (m≤max (max (dimC Γ) _) (dim A))

  dim-ps-not-𝔻0 : ∀ {Γ} → (Γ⊢ps : Γ ⊢ps) → (Γ ≠ (nil :: (0 , ∗))) → 0 < dimC Γ
  dim-ps-not-𝔻0 (ps Γ⊢psx) Γ≠𝔻0 = dim-psx-not-𝔻0 Γ⊢psx Γ≠𝔻0

  dim-dangling : ∀ {Γ x A} → Γ ⊢ps x # A → dim A ≤ dimC Γ
  dim-dangling pss = 0≤ _
  dim-dangling (psd Γ⊢psf) = Sn≤m→n≤m (dim-dangling Γ⊢psf)
  dim-dangling (pse {Γ = Γ} Γ⊢psx idp idp idp idp idp) = m≤max (max (dimC Γ) _) _

  {- Definition of a few ps-contexts and their source and target in the theory CaTT -}
  -- It is not necessary to define the pre contexts, as they can be infered with the derivation tree. We do it just as a sanity check
  Pre-Γc : Pre-Ctx
  Pre-Γc = ((((nil :: (0 , ∗)) :: (1 , ∗)) :: (2 , ⇒ ∗ (Var 0) (Var 1))) :: (3 , ∗)) :: (4 , ⇒ ∗ (Var 1) (Var 3))

  Pre-Γw : Pre-Ctx
  Pre-Γw = ((((((nil :: (0 , ∗)) :: (1 , ∗)) :: (2 , ⇒ ∗ (Var 0) (Var 1))) :: (3 , ⇒ ∗ (Var 0) (Var 1))) :: (4 , ⇒ (⇒ ∗ (Var 0) (Var 1)) (Var 2) (Var 3))) :: (5 , ∗)) :: (6 , ⇒ ∗ (Var 1) (Var 5))

  Pre-Γ₁ : Pre-Ctx
  Pre-Γ₁ = ((nil :: (0 , ∗)) :: (1 , ∗)) :: (2 , ⇒ ∗ (Var 0) (Var 1))

  Pre-Γ₂ : Pre-Ctx
  Pre-Γ₂ = ((((nil :: (0 , ∗)) :: (1 , ∗)) :: (2 , ⇒ ∗ (Var 0) (Var 1))) :: (3 , ⇒ ∗ (Var 0) (Var 1))) :: (4 , ⇒ (⇒ ∗ (Var 0) (Var 1)) (Var 2) (Var 3))

  Γc⊢ps : Pre-Γc ⊢ps
  Γc⊢ps = ps (psd (pse (psd (pse pss idp idp idp idp idp)) idp idp idp idp idp))

  Γw⊢ps : Pre-Γw ⊢ps
  Γw⊢ps = ps (psd (pse (psd (psd (pse (pse pss idp idp idp idp idp) idp idp idp idp idp))) idp idp idp idp idp))

  Γ₁⊢ps : Pre-Γ₁ ⊢ps
  Γ₁⊢ps = ps (psd (pse pss idp idp idp idp idp))

  Γ₂⊢ps : Pre-Γ₂ ⊢ps
  Γ₂⊢ps = ps (psd (psd (pse (pse pss idp idp idp idp idp) idp idp idp idp idp)))

  Γc : ps-ctx
  Γc = _ , Γc⊢ps

  Γw : ps-ctx
  Γw = _ , Γw⊢ps

  Γ₁ : ps-ctx
  Γ₁ = _ , Γ₁⊢ps

  Γ₂ : ps-ctx
  Γ₂ = _ , Γ₂⊢ps

  src-Γc : src-var Γc ≗ singleton 0
  src-Γc = (λ _ x → x) , λ _ x → x

  tgt-Γc : tgt-var Γc ≗ singleton 3
  tgt-Γc = (λ _ x → x) , λ _ x → x

  src-Γ₁ : src-var Γ₁ ≗ singleton 0
  src-Γ₁ = (λ _ x → x) , λ _ x → x

  tgt-Γ₁ : tgt-var Γ₁ ≗ singleton 1
  tgt-Γ₁ = (λ _ x → x) , λ _ x → x

  src-Γ₂ : src-var Γ₂ ≗ set-of-list (((nil :: 0) :: 1) :: 2)
  src-Γ₂ = (λ _ x → x) , λ _ x → x

  tgt-Γ₂ : tgt-var Γ₂ ≗ set-of-list (((nil :: 0) :: 1) :: 3)
  tgt-Γ₂ = (λ _ x → x) , λ _ x → x

  src-Γw : src-var Γw ≗ set-of-list (((((nil :: 0) :: 1) :: 2) :: 5) :: 6)
  src-Γw = (λ _ x → x) , λ _ x → x

  tgt-Γw : tgt-var Γw ≗ set-of-list (((((nil :: 0) :: 1) :: 3) :: 5) :: 6)
  tgt-Γw = (λ _ x → x) , λ _ x → x

-- TODO : cleanup and unite these two lemmas
  x∉ : ∀ {Γ x} → Γ ⊢C → length Γ ≤ x → (∀ {A} → ¬ (Γ ⊢t (Var x) # A))
  x∉ (cc Γ⊢ _ idp) l≤x (var _ (inl x∈Γ)) = x∉ Γ⊢ (Sn≤m→n≤m l≤x) (var Γ⊢ x∈Γ)
  x∉ (cc Γ⊢ _ idp) l≤x (var _ (inr (idp , idp))) = Sn≰n _ l≤x

  l∉ : ∀ {Γ x} → Γ ⊢C → length Γ ≤ x → ¬ (x ∈ Γ)
  l∉ (cc Γ⊢ _ idp) l≤x (inl x∈Γ) = l∉ Γ⊢ (Sn≤m→n≤m l≤x) x∈Γ
  l∉ (cc Γ⊢ _ idp) l≤x (inr idp) = Sn≰n _ l≤x


open import Nat
open import Prelude
open import List
open import judgemental-erase
open import moveerase
open import statics-checks
open import statics-core

module aasubsume-min where
  -- this predicate on derivations of actions bans the cases that induce
  -- non-determinism.
  mutual
    aasubmin-synth : ∀{Γ e t α e' t'} → (Γ ⊢ e => t ~ α ~> e' => t') → Set
    aasubmin-synth (SAZipAsc1 x) = aasubmin-ana x
    aasubmin-synth (SAZipLam2  x x₁ x₂ d) = aasubmin-synth d
    aasubmin-synth (SAZipApArr x x₁ x₂ d x₃) = aasubmin-synth d
    aasubmin-synth (SAZipApAna x x₁ x₂) = aasubmin-ana x₂
    aasubmin-synth (SAZipPlus1 x) = aasubmin-ana x
    aasubmin-synth (SAZipPlus2 x) = aasubmin-ana x
    aasubmin-synth (SAZipNEHole x x₁ d) = aasubmin-synth d
    aasubmin-synth (SAZipPair1 x x₁ d x₂) = aasubmin-synth d
    aasubmin-synth (SAZipPair2 x x₁ x₂ d) = aasubmin-synth d
    aasubmin-synth (SAZipFst x x₁ x₂ x₃ d) = aasubmin-synth d
    aasubmin-synth (SAZipSnd x x₁ x₂ x₃ d) = aasubmin-synth d
    aasubmin-synth _ = ⊤

    aasubmin-ana : ∀{Γ e α e' t} → (Γ ⊢ e ~ α ~> e' ⇐ t) → Set
    aasubmin-ana (AASubsume x x₁ SAConAsc x₃) = ⊥
    aasubmin-ana (AASubsume x x₁ SAConInl x₃) = ⊥
    aasubmin-ana (AASubsume x x₁ SAConInr x₃) = ⊥
    aasubmin-ana (AASubsume x x₁ (SAConLam x₃) x₄) = ⊥
    aasubmin-ana (AASubsume EETop SEHole (SAConCase1 a b c) x₄) = ⊥
    aasubmin-ana (AASubsume x x₁ s x₃) = aasubmin-synth s
    aasubmin-ana (AAZipLam x₁ x₂ d) = aasubmin-ana d
    aasubmin-ana (AAZipInl x y) = aasubmin-ana y
    aasubmin-ana (AAZipInr x y) = aasubmin-ana y
    aasubmin-ana (AAZipCase1 a b c d e f g h) = aasubmin-synth e
    aasubmin-ana (AAZipCase2 a b c d e) = aasubmin-ana e
    aasubmin-ana (AAZipCase3 a b c d f) = aasubmin-ana f
    aasubmin-ana _ = ⊤

  -- the minimization predicate propagates through subsumption rules
  min-ana-lem : ∀{e e' e◆ Γ t t' t'' α} {a : erase-e e e◆}
                 {b : Γ ⊢ e◆ => t'} {c : t ~ t''} →
                 (d : Γ ⊢ e => t' ~ α ~> e' => t'') →
                 aasubmin-ana (AASubsume a b d c) →
                 aasubmin-synth d
  min-ana-lem (SAMove x) min = <>
  min-ana-lem (SADel) min = <>
  min-ana-lem (SAConAsc) min = <>
  min-ana-lem (SAConVar p) min = <>
  min-ana-lem (SAConLam x₁) min = <>
  min-ana-lem (SAConApArr x) min = <>
  min-ana-lem (SAConApOtw x) min = <>
  min-ana-lem (SAConNumlit) min = <>
  min-ana-lem (SAConPlus1 x) min = <>
  min-ana-lem (SAConPlus2 x) min = <>
  min-ana-lem (SAConNEHole) min = <>
  min-ana-lem (SAFinish x) min = <>
  min-ana-lem (SAZipAsc1 x) min = min
  min-ana-lem (SAZipAsc2 x x₁ x₂ x₃) min = <>
  min-ana-lem (SAZipApArr x x₁ x₂ c x₃) min = min
  min-ana-lem (SAZipApAna x x₁ x₂) min = min
  min-ana-lem (SAZipPlus1 x) min = min
  min-ana-lem (SAZipPlus2 x) min = min
  min-ana-lem (SAZipNEHole x x₁ c) min = min
  min-ana-lem SAConInl _ = <>
  min-ana-lem SAConInr _ = <>
  min-ana-lem (SAConCase1 x₁ x₂ x₃) _ = <>
  min-ana-lem (SAConCase2 x₁ x₂ x₃) _ = <>
  min-ana-lem SAConPair _ = <>
  min-ana-lem (SAConFst1 pr) _ = <>
  min-ana-lem (SAConFst2 inc) _ = <>
  min-ana-lem (SAConSnd1 pr) _ = <>
  min-ana-lem (SAConSnd2 inc) _ = <>
  min-ana-lem (SAZipPair1 x x₁ x₂ x₃) min = min
  min-ana-lem (SAZipPair2 x x₁ x₂ x₃) min = min
  min-ana-lem (SAZipFst x x₁ x₂ x₃ x₄) min = min
  min-ana-lem (SAZipSnd x x₁ x₂ x₃ x₄) min = min
  min-ana-lem (SAZipLam1 x x₁ x₂ x₃ x₄ x₅) min = min
  min-ana-lem (SAZipLam2 x x₁ x₂ x₃) min = min

  -- any derivation of an action can be minimized to avoid this cases that
  -- induce non-determinism.
  mutual
    min-synth : ∀{Γ e t α e' t'} →
                (d : Γ ⊢ e => t ~ α ~> e' => t') →
                Σ[ e'' ∈ zexp ] Σ[ d' ∈ Γ ⊢ e => t ~ α ~> e'' => t' ] aasubmin-synth d'
    min-synth (SAMove x) = _ , SAMove x , <>
    min-synth SADel = _ , SADel , <>
    min-synth SAConAsc = _ , SAConAsc , <>
    min-synth (SAConVar p) = _ , SAConVar p , <>
    min-synth (SAConLam x₁) = _ , SAConLam x₁ , <>
    min-synth (SAConApArr x) = _ , SAConApArr x , <>
    min-synth (SAConApOtw x) = _ , SAConApOtw x , <>
    min-synth SAConNumlit = _ , SAConNumlit , <>
    min-synth (SAConPlus1 x) = _ , SAConPlus1 x , <>
    min-synth (SAConPlus2 x) = _ , SAConPlus2 x , <>
    min-synth SAConNEHole = _ , SAConNEHole , <>
    min-synth (SAFinish x) = _ , SAFinish x , <>
    min-synth (SAZipAsc2 x x₁ x₂ x₃) = _ , SAZipAsc2 x x₁ x₂ x₃ , <>
    min-synth (SAZipAsc1 x) with min-ana x
    ... | _ , a , b = _ ,  SAZipAsc1 a , b
    min-synth (SAZipApArr x x₁ x₂ d x₃) with min-synth d
    ... | _ , a , b = _ , (SAZipApArr x x₁ x₂ a x₃) , b
    min-synth (SAZipApAna x x₁ x₂) with min-ana x₂
    ... | _ , a , b = _ , SAZipApAna x x₁ a , b
    min-synth (SAZipPlus1 x) with min-ana x
    ... | _ , a , b = _ , SAZipPlus1 a , b
    min-synth (SAZipPlus2 x) with min-ana x
    ... | _ , a , b = _ , SAZipPlus2 a , b
    min-synth (SAZipNEHole x x₁ d) with min-synth d
    ... | _ , a , b = _ , SAZipNEHole x x₁ a , b
    min-synth SAConInl = _ , SAConInl , <>
    min-synth SAConInr = _ , SAConInr , <>
    min-synth (SAConCase1 a b c) = _ , SAConCase1 a b c , <>
    min-synth (SAConCase2 a b c) = _ , SAConCase2 a b c , <>
    min-synth SAConPair = _ , SAConPair , <>
    min-synth (SAConFst1 pr) = _ , SAConFst1 pr , <>
    min-synth (SAConFst2 inc) = _ , SAConFst2 inc , <>
    min-synth (SAConSnd1 pr) = _ , SAConSnd1 pr , <>
    min-synth (SAConSnd2 inc) = _ , SAConSnd2 inc , <>
    min-synth (SAZipPair1 x x₁ d x₃) with min-synth d
    ... | _ , a , b = _ , (SAZipPair1 x x₁ a x₃) , b
    min-synth (SAZipPair2 x x₁ x₂ d) with min-synth d
    ... | _ , a , b = _ , (SAZipPair2 x x₁ x₂ a) , b
    min-synth (SAZipFst x x₁ x₂ x₃ d) with min-synth d
    ... | _ , a , b = _ , (SAZipFst x x₁ x₂ x₃ a) , b
    min-synth (SAZipSnd x x₁ x₂ x₃ d) with min-synth d
    ... | _ , a , b = _ , (SAZipSnd x x₁ x₂ x₃ a) , b
    min-synth (SAZipLam1 x x₁ x₂ x₃ x₄ x₅) = _ , SAZipLam1 x x₁ x₂ x₃ x₄ x₅ , <>
    min-synth (SAZipLam2 x x₁ x₂ d) with min-synth d
    ... | _ , a , b = _ , SAZipLam2 x x₁ x₂ a , b

    min-ana : ∀{Γ e α e' t} →
              (d : Γ ⊢ e ~ α ~> e' ⇐ t) →
              Σ[ e'' ∈ zexp ] Σ[ d' ∈  Γ ⊢ e ~ α ~> e'' ⇐ t ] aasubmin-ana d'
    min-ana (AASubsume {Γ = Γ} x x₁ (SAMove x₂) x₃) = _ , AAMove x₂ , <>
    min-ana (AASubsume x x₁ (SADel) x₃) = _ , AADel , <>
    min-ana (AASubsume {Γ = Γ}  {t = t} {t' = t'}  x x₁ SAConAsc x₃) =
      _ , AAConAsc {Γ = Γ} {t = t} , <>
    min-ana (AASubsume x x₁ (SAConVar p) x₃) = _ , AASubsume x x₁ (SAConVar p) x₃ , <>
    min-ana (AASubsume EETop SEHole (SAConLam x₃) TCRefl) = _ , AAConLam1 x₃ MAArr , <>
    min-ana (AASubsume EETop SEHole (SAConLam x₃) TCHole2)  = _ , AAConLam1 x₃ MAHole , <>
    min-ana (AASubsume EETop SEHole (SAConLam x₃) (TCArr x₄ x₅)) = _ , AAConLam1 x₃ MAArr , <>
    min-ana (AASubsume x x₁ (SAConApArr x₂) x₃) =
      _ , AASubsume x x₁ (SAConApArr x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConApOtw x₂) x₃) =
      _ , AASubsume x x₁ (SAConApOtw x₂) x₃ , <>
    min-ana (AASubsume x x₁ SAConNumlit x₃) = _ , AASubsume x x₁ SAConNumlit x₃ , <>
    min-ana (AASubsume x x₁ (SAConPlus1 x₂) x₃) =
      _ , AASubsume x x₁ (SAConPlus1 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConPlus2 x₂) x₃) =
      _ , AASubsume x x₁ (SAConPlus2 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConNEHole) x₃) =
      _ , AASubsume x x₁ (SAConNEHole) x₃ , <>
    min-ana (AASubsume x x₁ (SAFinish x₂) x₃) = _ ,  AASubsume x x₁ (SAFinish x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAZipAsc2 x₂ x₃ x₄ x₅) x₆) =
      _ , AASubsume x x₁ (SAZipAsc2 x₂ x₃ x₄ x₅) x₆ , <>
    min-ana (AASubsume x x₁ (SAZipAsc1 x₂) x₃) with min-ana x₂
    ... | a , b , c = _ , AASubsume x x₁ (SAZipAsc1 b) x₃ , c
    min-ana (AASubsume x x₁ (SAZipApArr x₂ x₃ x₄ x₅ x₆) x₇) with min-synth x₅
    ... | a , b , c = _ , AASubsume x x₁ (SAZipApArr x₂ x₃ x₄ b x₆) x₇ , c
    min-ana (AASubsume x x₁ (SAZipApAna x₂ x₃ x₄) x₅) with min-ana x₄
    ... | a , b , c = _ , AASubsume x x₁ (SAZipApAna x₂ x₃ b) x₅ , c
    min-ana (AASubsume x x₁ (SAZipPlus1 x₂) x₃) with min-ana x₂
    ... | a , b , c = _ , AASubsume x x₁ (SAZipPlus1 b) x₃ , c
    min-ana (AASubsume x x₁ (SAZipPlus2 x₂) x₃) with min-ana x₂
    ... | a , b , c = _ , AASubsume x x₁ (SAZipPlus2 b) x₃ , c
    min-ana (AASubsume x x₁ (SAZipNEHole x₂ x₃ x₄) x₅) with min-synth x₄
    ... | a , b , c = _ , AASubsume x x₁ (SAZipNEHole x₂ x₃ b) x₅ , c
    min-ana (AASubsume EETop SEHole (SAConCase1 a b c) z) =
      _ , AAConCase a b , <>
    min-ana (AASubsume EETop (SAsc x₁) (SAConCase1 a b c) z) =
      _ , AASubsume EETop (SAsc x₁) (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop (SVar x₁) (SAConCase1 a b c) z) =
      _ , AASubsume EETop (SVar x₁) (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop (SLam x x₁) (SAConCase1 x₂ x₄ x₅) x₃) =
      _ , AASubsume EETop (SLam x x₁) (SAConCase1 x₂ x₄ x₅) x₃ , <>
    min-ana (AASubsume EETop (SAp y₁ x₁ x₂) (SAConCase1 a b c) z) =
      _ , AASubsume EETop (SAp y₁ x₁ x₂) (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop SNum (SAConCase1 a b c) z) =
      _ , AASubsume EETop SNum (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop (SPlus x₁ x₂) (SAConCase1 a b c) z) =
      _ , AASubsume EETop (SPlus x₁ x₂) (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop (SNEHole y₁) (SAConCase1 a b c) z) =
      _ , AASubsume EETop (SNEHole y₁) (SAConCase1 a b c) z , <>
    min-ana (AASubsume EETop (SPair x₁ x₆) (SAConCase1 x₂ x₄ x₅) x₃) =
      _ , (AASubsume EETop (SPair x₁ x₆) (SAConCase1 x₂ x₄ x₅) x₃) , <>
    min-ana (AASubsume EETop (SFst x x₁) (SAConCase1 x₂ x₃ x₄) x₅) =
      _ , AASubsume EETop (SFst x x₁) (SAConCase1 x₂ x₃ x₄) x₅ , <>
    min-ana (AASubsume EETop (SSnd x x₁) (SAConCase1 x₂ x₃ x₄) x₅) =
      _ , AASubsume EETop (SSnd x x₁) (SAConCase1 x₂ x₃ x₄) x₅ , <>
    min-ana (AASubsume x y (SAConCase2 a b c) z) =
      _ , AASubsume x y (SAConCase2 a b c) z , <>

    min-ana (AAMove x) = _ , AAMove x , <>
    min-ana AADel = _ , AADel , <>
    min-ana AAConAsc = _ , AAConAsc , <>
    min-ana (AAConVar x₁ p) = _ , AAConVar x₁ p , <>
    min-ana (AAConLam1 x₁ x₂) = _ , AAConLam1 x₁ x₂ , <>
    min-ana (AAConLam2 x₁ x₂) = _ , AAConLam2 x₁ x₂ , <>
    min-ana (AAConNumlit x) = _ , AAConNumlit x , <>
    min-ana (AAFinish x) = _ , AAFinish x , <>
    min-ana (AAZipLam x₁ x₂ d) with min-ana d
    ... | a , b , c = _ , AAZipLam x₁ x₂ b , c
    min-ana (AASubsume x x₁ SAConInl TCRefl) = _ , AAConInl1 MSSum , <>
    min-ana (AASubsume x x₁ SAConInl TCHole2) = _ , AAConInl1 MSHole , <>
    min-ana (AASubsume x x₁ SAConInl (TCSum x₃ x₄)) = _ , AAConInl1 MSSum , <>
    min-ana (AASubsume x x₁ SAConInr TCRefl) = _ , AAConInr1 MSSum , <>
    min-ana (AASubsume x x₁ SAConInr TCHole2) = _ , AAConInr1 MSHole , <>
    min-ana (AASubsume x x₁ SAConInr (TCSum x₃ x₄)) = _ , AAConInr1 MSSum , <>
    min-ana (AAConInl1 x) = _ , AAConInl1 x , <>
    min-ana (AAConInl2 x) = _ , AAConInl2 x , <>
    min-ana (AAConInr1 x) = _ , AAConInr1 x , <>
    min-ana (AAConInr2 x) = _ , AAConInr2 x , <>
    min-ana (AAConCase x₁ x₂) = _ , AAConCase x₁ x₂ , <>
    min-ana (AAZipInl x x₁) with min-ana x₁
    ... | a , b , c = _ , AAZipInl x b , c
    min-ana (AAZipInr x x₁) with min-ana x₁
    ... | a , b , c = _ , AAZipInr x b , c
    min-ana (AAZipCase1 x₁ x₂ x₃ x₄ x₅ x₆ x₇ x₈) with min-synth x₅
    ... | a , b , c = _ , AAZipCase1 x₁ x₂ x₃ x₄ b x₆ x₇ x₈ , c
    min-ana (AAZipCase2 x₁ x₂ x₃ x₄ x₅) with min-ana x₅
    ... | a , b , c = _ , AAZipCase2 x₁ x₂ x₃ x₄ b , c
    min-ana (AAZipCase3 x₁ x₂ x₃ x₄ x₆) with min-ana x₆
    ... | a , b , c = _ , AAZipCase3 x₁ x₂ x₃ x₄ b , c

    min-ana (AASubsume x x₁ (SAZipLam1 x₂ x₄ x₅ x₆ x₇ x₈) x₃) =
      _ , AASubsume x x₁ (SAZipLam1 x₂ x₄ x₅ x₆ x₇ x₈) x₃ , <>
    min-ana (AASubsume x x₁ (SAZipLam2 x₂ x₄ x₅ x₆) x₃) with min-synth x₆
    ... | a , b , c = _ , AASubsume x x₁ (SAZipLam2 x₂ x₄ x₅ b) x₃ , c
    min-ana (AASubsume x x₁ SAConPair x₃) =
      _ , AASubsume x x₁ SAConPair x₃ , <>
    min-ana (AASubsume x x₁ (SAConFst1 x₂) x₃) = _ , AASubsume x x₁ (SAConFst1 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConFst2 x₂) x₃) =
      _ , AASubsume x x₁ (SAConFst2 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConSnd1 x₂) x₃) = _ , AASubsume x x₁ (SAConSnd1 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAConSnd2 x₂) x₃) =
      _ , AASubsume x x₁ (SAConSnd2 x₂) x₃ , <>
    min-ana (AASubsume x x₁ (SAZipPair1 x₂ x₃ x₄ x₅) x₆) with min-synth x₄
    ... | a , b , c = _ , (AASubsume x x₁ (SAZipPair1 x₂ x₃ b x₅) x₆) , c
    min-ana (AASubsume x x₁ (SAZipPair2 x₂ x₃ x₄ x₅) x₆) with min-synth x₅
    ... | a , b , c = _ , AASubsume x x₁ (SAZipPair2 x₂ x₃ x₄ b) x₆ , c
    min-ana (AASubsume x x₁ (SAZipFst x₂ x₃ x₄ x₅ x₆) x₇) with min-synth x₆
    ... | a , b , c = _ , AASubsume x x₁ (SAZipFst x₂ x₃ x₄ x₅ b) x₇ , c
    min-ana (AASubsume x x₁ (SAZipSnd x₂ x₃ x₄ x₅ x₆) x₇) with min-synth x₆
    ... | a , b , c = _ , AASubsume x x₁ (SAZipSnd x₂ x₃ x₄ x₅ b) x₇ , c

  -- these theorems argue that if a derivation is already subsumption
  -- minimal than the minimzer does not change the resultant
  -- expression--that it's conservative in this sense. they do not argue
  -- that the derivation that's computed is itself the same as the input
  -- derivation.
  mutual
    min-fixed-synth : ∀{Γ e t α e' t'} →
                      (d : Γ ⊢ e => t ~ α ~> e' => t') →
                      aasubmin-synth d →
                      e' == π1 (min-synth d)
    min-fixed-synth (SAMove x) min = refl
    min-fixed-synth SADel min = refl
    min-fixed-synth SAConAsc min = refl
    min-fixed-synth (SAConVar p) min = refl
    min-fixed-synth (SAConLam x₁) min = refl
    min-fixed-synth (SAConApArr x) min = refl
    min-fixed-synth (SAConApOtw x) min = refl
    min-fixed-synth SAConNumlit min = refl
    min-fixed-synth (SAConPlus1 x) min = refl
    min-fixed-synth (SAConPlus2 x) min = refl
    min-fixed-synth SAConNEHole min = refl
    min-fixed-synth (SAFinish x) min = refl
    min-fixed-synth (SAZipAsc1 x) min with min-fixed-ana x min
    ... | qq with min-ana x
    ... | (e'' , d' , min') = ap1 (λ q → q ·:₁ _) qq
    min-fixed-synth (SAZipAsc2 x x₁ x₂ x₃) min = refl
    min-fixed-synth (SAZipApArr x x₁ x₂ d x₃) min with min-fixed-synth d min
    ... | qq with min-synth d
    ... | (e'' , d' , min') = ap1 (λ q → q ∘₁ _) qq
    min-fixed-synth (SAZipApAna x x₁ x₂) min with min-fixed-ana x₂ min
    ... | qq with min-ana x₂
    ... | (e'' , _ , _) = ap1 (λ q → _ ∘₂ q) qq
    min-fixed-synth (SAZipPlus1 x) min with min-fixed-ana x min
    ... | qq with min-ana x
    ... | (e'' , _ , _) = ap1 (λ q → q ·+₁ _) qq
    min-fixed-synth (SAZipPlus2 x) min with min-fixed-ana x min
    ... | qq with min-ana x
    ... | (e'' , _ , _) = ap1 (λ q → _ ·+₂ q) qq
    min-fixed-synth (SAZipNEHole x x₁ d) min with min-fixed-synth d min
    ... | qq with min-synth d
    ... | (e'' , _ , _) = ap1 ⦇⌜_⌟⦈[ _ ] qq
    min-fixed-synth SAConInl min = refl
    min-fixed-synth SAConInr min = refl
    min-fixed-synth (SAConCase1 x₁ x₂ x₃) min = refl
    min-fixed-synth (SAConCase2 x₁ x₂ x₃) min = refl
    min-fixed-synth SAConPair min = refl
    min-fixed-synth (SAConFst1 x₁) min = refl
    min-fixed-synth (SAConFst2 x₁) min = refl
    min-fixed-synth (SAConSnd1 x₁) min = refl
    min-fixed-synth (SAConSnd2 x₁) min = refl
    min-fixed-synth (SAZipPair1 x x₁ d x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → ⟨ q , _ ⟩₁) qq
    min-fixed-synth (SAZipPair2 x x₁ x₂ d) min  with min-fixed-synth d min
    ... | qq = ap1 (λ q → ⟨ _ , q ⟩₂) qq
    min-fixed-synth (SAZipFst x x₁ x₂ x₃ d) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → fst q) qq
    min-fixed-synth (SAZipSnd x x₁ x₂ x₃ d) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → snd q) qq
    min-fixed-synth (SAZipLam1 x x₁ x₂ x₃ x₄ x₅) min = refl
    min-fixed-synth (SAZipLam2 x x₁ x₂ d) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → ·λ _ ·[ _ ]₂ q) qq

    min-fixed-ana : ∀{Γ e t α e' } →
                    (d : Γ ⊢ e ~ α ~> e' ⇐ t) →
                    aasubmin-ana d →
                    e' == π1 (min-ana d)
    min-fixed-ana (AASubsume x x₁ (SAMove x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ SADel x₃) min = refl
    min-fixed-ana (AASubsume x x₁ SAConAsc x₃) min = abort min
    min-fixed-ana (AASubsume x₁ x₂ (SAConVar p) x₃) min = refl
    min-fixed-ana (AASubsume x₁ x₂ (SAConLam x₃) x₄) min = abort min
    min-fixed-ana (AASubsume x x₁ (SAConApArr x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConApOtw x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ SAConNumlit x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConPlus1 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConPlus2 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ SAConNEHole x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAFinish x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAZipAsc1 x₂) x₃) min with min-fixed-ana x₂ min
    ... | qq with min-ana x₂
    ... | (e'' , _ , _) = ap1 (λ q → q ·:₁ _) qq
    min-fixed-ana (AASubsume x x₁ (SAZipAsc2 x₂ x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume x x₁ (SAZipApArr x₂ x₃ x₄ x₅ x₆) x₇) min with min-fixed-synth x₅ min
    ... | qq with min-synth x₅
    ... | (e'' , d' , min') = ap1 (λ q → q ∘₁ _) qq
    min-fixed-ana (AASubsume x x₁ (SAZipApAna x₂ x₃ x₄) x₅) min with min-fixed-ana x₄ min
    ... | qq with min-ana x₄
    ... | (e'' , _ , _) = ap1 (λ q → _ ∘₂ q) qq
    min-fixed-ana (AASubsume x x₁ (SAZipPlus1 x₂) x₃) min with min-fixed-ana x₂ min
    ... | qq with min-ana x₂
    ... | (e'' , _ , _) = ap1 (λ q → q ·+₁ _) qq
    min-fixed-ana (AASubsume x x₁ (SAZipPlus2 x₂) x₃) min with min-fixed-ana x₂ min
    ... | qq with min-ana x₂
    ... | (e'' , _ , _) = ap1 (λ q → _ ·+₂ q) qq
    min-fixed-ana (AASubsume x x₁ (SAZipNEHole x₂ x₃ x₄) x₅) min with min-fixed-synth x₄ min
    ... | qq with min-synth x₄
    ... | (e'' , _ , _) = ap1 ⦇⌜_⌟⦈[ _ ] qq
    min-fixed-ana (AAMove x) min = refl
    min-fixed-ana AADel min = refl
    min-fixed-ana AAConAsc min = refl
    min-fixed-ana (AAConVar x₁ p) min = refl
    min-fixed-ana (AAConLam1 x₁ x₂) min = refl
    min-fixed-ana (AAConLam2 x₁ x₂) min = refl
    min-fixed-ana (AAConNumlit x) min = refl
    min-fixed-ana (AAFinish x) min = refl
    min-fixed-ana (AAZipLam x₁ x₂ d) min with min-fixed-ana d min
    ... | qq with min-ana d
    ... | (e'' , _ , _) = ap1 (λ q → ·λ _ q) qq
    min-fixed-ana (AASubsume x x₁ SAConInl x₃) min = abort min
    min-fixed-ana (AASubsume x x₁ SAConInr x₃) min = abort min
    min-fixed-ana (AASubsume EETop (SAsc x₁) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop (SVar x₁) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop (SLam x x₁) (SAConCase1 x₂ x₄ x₅) x₃) min = refl
    min-fixed-ana (AASubsume EETop (SPair x₁ x₆) (SAConCase1 x₂ x₄ x₅) x₃) min = refl
    min-fixed-ana (AASubsume EETop (SAp x₂ x₁ x₃) (SAConCase1 x₄ x₅ x₆) x₇) min = refl
    min-fixed-ana (AASubsume EETop SNum (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop (SPlus x₁ x₂) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop SEHole (SAConCase1 x₃ x₄ x₅) x₆) min = abort min
    min-fixed-ana (AASubsume EETop (SNEHole x₂) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop (SFst x x₁) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume EETop (SSnd x x₁) (SAConCase1 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AASubsume x₁ x₂ (SAConCase2 x₃ x₄ x₅) x₆) min = refl
    min-fixed-ana (AAConInl1 x) min = refl
    min-fixed-ana (AAConInl2 x) min = refl
    min-fixed-ana (AAConInr1 x) min = refl
    min-fixed-ana (AAConInr2 x) min = refl
    min-fixed-ana (AAConCase x₁ x₂) min = refl
    min-fixed-ana (AAZipInl x d) min with min-fixed-ana d min
    ... | qq with min-ana d
    ... | (e'' , _ , _) = ap1 inl qq
    min-fixed-ana (AAZipInr x d) min with min-fixed-ana d min
    ... | qq with min-ana d
    ... | (e'' , _ , _) = ap1 inr qq
    min-fixed-ana (AAZipCase1 x₁ x₂ x₃ x₄ x₅ x₆ x₇ x₈) min with min-fixed-synth x₅ min
    ... | qq with min-synth x₅
    ... | (e'' , _ , _) = ap1 (λ q → case₁ q _ _ _ _) qq
    min-fixed-ana (AAZipCase2 x₁ x₂ x₃ x₄ d) min with min-fixed-ana d min
    ... | qq with min-ana d
    ... | (e'' , _ , _) = ap1 (λ q → case₂ _ _ q _ _) qq
    min-fixed-ana (AAZipCase3 x₁ x₂ x₃ x₄ d) min with min-fixed-ana d min
    ... | qq with min-ana d
    ... | (e'' , _ , _) = ap1 (λ q → case₃ _ _ _ _ q) qq

    min-fixed-ana (AASubsume x x₁ SAConPair x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConFst1 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConFst2 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConSnd1 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAConSnd2 x₂) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAZipPair1 x₂ x₄ d x₆) x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → ⟨ q , _ ⟩₁) qq
    min-fixed-ana (AASubsume x x₁ (SAZipPair2 x₂ x₄ x₅ d) x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → ⟨ _ , q ⟩₂) qq
    min-fixed-ana (AASubsume x x₁ (SAZipFst x₂ x₄ x₅ x₆ d) x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → fst q) qq
    min-fixed-ana (AASubsume x x₁ (SAZipSnd x₂ x₄ x₅ x₆ d) x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → snd q) qq
    min-fixed-ana (AASubsume x x₁ (SAZipLam1 x₂ x₄ x₅ x₆ x₇ x₈) x₃) min = refl
    min-fixed-ana (AASubsume x x₁ (SAZipLam2 x₂ x₄ x₅ d) x₃) min with min-fixed-synth d min
    ... | qq = ap1 (λ q → ·λ _ ·[ _ ]₂ q) qq


{-# OPTIONS --type-in-type #-}
-- {-# OPTIONS -v tc.pos:10 -v tc.polarity:10 #-}
-- Andreas, 2012-09-06, message on Agda list "Forget Hurken's paradox..."

module Issue690 where

infix 4 _≡_

data _≡_ {A : Set} (x : A) : A → Set where
  refl : x ≡ x

data ⊥ : Set where

data D : Set where
  abs : ∀ {E : Set} → D ≡ E → (E → ⊥) → D

lam : (D → ⊥) → D
lam f = abs refl f

app : D → D → ⊥
app (abs refl f) d = f d

omega : D
omega = lam (λ x → app x x)

Omega : ⊥
Omega = app omega omega


module PiFrac.NoRepeat where
open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Sum
open import Data.Product
open import Relation.Binary.Core
open import Relation.Binary
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Function using (_∘_)
open import PiFrac.Syntax
open import PiFrac.Opsem
open import PiFrac.AuxLemmas
import PartialRevNoRepeat

-- Forward deterministic
deterministic : ∀ {st st₁ st₂} → st ↦ st₁ → st ↦ st₂ → st₁ ≡ st₂
deterministic (↦₁ {b = b₁}) (↦₁ {b = b₂}) with base-is-prop _ b₁ b₂
... | refl = refl
deterministic ↦₂ ↦₂ = refl
deterministic ↦₃ ↦₃ = refl
deterministic ↦₄ ↦₄ = refl
deterministic ↦₅ ↦₅ = refl
deterministic ↦₆ ↦₆ = refl
deterministic ↦₇ ↦₇ = refl
deterministic ↦₈ ↦₈ = refl
deterministic ↦₉ ↦₉ = refl
deterministic ↦₁₀ ↦₁₀ = refl
deterministic ↦₁₁ ↦₁₁ = refl
deterministic ↦₁₂ ↦₁₂ = refl
deterministic ↦η ↦η = refl
deterministic ↦ε₁ ↦ε₁ = refl
deterministic (↦ε₁ {eq = eq}) (↦ε₂ {neq = neq}) = ⊥-elim (neq eq)
deterministic (↦ε₂ {neq = neq}) (↦ε₁ {eq = eq}) = ⊥-elim (neq eq)
deterministic ↦ε₂ ↦ε₂ = refl

-- Backward deterministic
deterministicᵣₑᵥ : ∀ {st st₁ st₂} → st₁ ↦ st → st₂ ↦ st → st ≢ ⊠ → st₁ ≡ st₂
deterministicᵣₑᵥ {［ c ∣ _ ∣ κ ］} {⟨ c ∣ v ∣ κ ⟩} {⟨ c' ∣ v' ∣ κ' ⟩} ↦₁ r st≠⊠ with Lemma₁ r
... | refl , refl with Lemma₂ r
deterministicᵣₑᵥ {［ uniti₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ x             ∣ κ ⟩} {⟨ _ ∣ x             ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ unite₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ x        ∣ κ ⟩} {⟨ _ ∣ inj₂ x        ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} ↦₁ () _ | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} ↦₁ () _ | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} ↦₁ () _ | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} ↦₁ () _ | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ unite⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ (tt , v)      ∣ κ ⟩} {⟨ _ ∣ (tt , v)      ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ uniti⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v             ∣ κ ⟩} {⟨ _ ∣ v             ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap⋆    ∣ _ ∣ κ ］} {⟨ _ ∣ (x , y)       ∣ κ ⟩} {⟨ _ ∣ (x , y)       ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ (x , (y , z)) ∣ κ ⟩} {⟨ _ ∣ (x , (y , z)) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ ((x , y) , z) ∣ κ ⟩} {⟨ _ ∣ ((x , y) , z) ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ (inj₁ x , z)  ∣ κ ⟩} {⟨ _ ∣ (inj₁ x , z)  ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ (inj₂ y , z)  ∣ κ ⟩} {⟨ _ ∣ (inj₂ y , z)  ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (x , z)  ∣ κ ⟩} {⟨ _ ∣ inj₁ (x , z)  ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (y , z)  ∣ κ ⟩} {⟨ _ ∣ inj₂ (y , z)  ∣ κ ⟩} ↦₁ ↦₁ _ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ unite⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ uniti⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ swap⋆    ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocl⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocr⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ id↔      ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⨾ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⊕ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⊗ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {_} {_} {_} ↦₂ ↦₂ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₃ ↦₃ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₄ ↦₄ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₅ ↦₅ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₆ ↦₆ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₇ ↦₇ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₈ ↦₈ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₉ ↦₉ st≠⊠ = refl
deterministicᵣₑᵥ {_} {_} {_} ↦₁₀ ↦₁₀ st≠⊠ = refl
deterministicᵣₑᵥ {.(［ _ ⊕ _ ∣ inj₁ _ ∣ _ ］)} {_} {⟨ c ∣ v ∣ κ ⟩} ↦₁₁ r st≠⊠ with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with Lemma₃ r
... | inj₂ (inj₂ refl) with Lemma₄ r
... | inj₁ ()
... | inj₂ ()
deterministicᵣₑᵥ {［ _ ⊕ _ ∣ inj₁ _ ∣ _ ］} {_} {_} ↦₁₁ ↦₁₁ st≠⊠ = refl
deterministicᵣₑᵥ {［ _ ⊕ _ ∣ inj₂ _ ∣ _ ］} {［ _ ∣ _ ∣ _ ⊕☐• _ ］} {⟨ c ∣ v ∣ κ ⟩} ↦₁₂ r st≠⊠ with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with Lemma₃ r
... | inj₂ (inj₂ refl) with Lemma₄ r
... | inj₁ ()
... | inj₂ ()
deterministicᵣₑᵥ {［ _ ⊕ _ ∣ inj₂ _ ∣ _ ］} {_} {_} ↦₁₂ ↦₁₂ st≠⊠ = refl
deterministicᵣₑᵥ {［ ηₓ v ∣ _ ∣ _ ］} {⟨ ηₓ v ∣ tt ∣ _ ⟩} {⟨ _ ∣ v' ∣ _ ⟩} ↦η r st≠⊠ with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with v'
... | tt = refl
deterministicᵣₑᵥ {［ εₓ v ∣ tt ∣ _ ］} {_} {_} (↦ε₁ {eq = refl}) (↦ε₁ {eq = refl}) st≠⊠ = refl
deterministicᵣₑᵥ {⊠} {⟨ εₓ v ∣ _ , ↻ ∣ _ ⟩} {_} ↦ε₂ ↦ε₂ st≠⊠ = ⊥-elim (st≠⊠ refl)

-- Non-repeating Lemma

⊠-is-stuck : ∀ {st} → st ≡ ⊠ → ∄[ st' ] (st ↦ st')
⊠-is-stuck refl = λ ()

open PartialRevNoRepeat (record { State = State
                                ; _↦_ = _↦_
                                ; is-fail = λ st → st ≡ ⊠
                                ; is-fail? = (λ { ⊠ → yes refl
                                                ; ⟨ _ ∣ _ ∣ _ ⟩ → no (λ ())
                                                ; ［ _ ∣ _ ∣ _ ］ → no (λ ())})
                                ; fail-is-stuck = ⊠-is-stuck
                                ; deterministic = deterministic
                                ; deterministicᵣₑᵥ = deterministicᵣₑᵥ}) public