module Mystuff where

-- open import Axiom.Extensionality.Propositional as AEP
open import Categories.Category.Cartesian.Bundle
open import Categories.Category.CartesianClosed.Canonical
open import Categories.Category.Core
open import Categories.Category.Slice
-- open import Categories.Functor
open import Categories.Category.BinaryProducts
-- open import Categories.Morphism
open import Categories.Object.Exponential -- 𝒞 hiding (repack)
open import Data.Product
open import Function using (const)
open import Level
open import Relation.Binary.Core using (Rel)
open import Relation.Binary.PropositionalEquality
open Relation.Binary.PropositionalEquality.≡-Reasoning

private
  variable
    a b c d e : Level

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

simple : Category (suc zero) zero zero
simple =
 record
  -- structure
  { Obj = Set × Set
  ; _⇒_ = _⇒_
  ; _≈_ = _≡_
  ; id  = (λ z → z) , proj₁
  ; _∘_ = _∘_
  -- properties
  ; assoc     = refl
  ; sym-assoc = refl
  ; identityˡ  = refl
  ; identityʳ  = refl
  ; identity² = refl
  -- whew, they're all trivial!
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = ∘-resp
  }
  where
  _⇒_ : Rel (Set × Set) zero
  (i , x) ⇒ (j , y) = (i → j) × (x × i → y)
  _∘_ : {A B C : Set × Set} → B ⇒ C → A ⇒ B → A ⇒ C
  _∘_ {i , x} {j , y} {_} (u , f) (v , g) =
   (λ t → u (v t)) , λ t → f (g t , v (proj₂ t))
  ∘-resp :
    {A B C : Set × Set} {f h : B ⇒ C} {g i : A ⇒ B} →
    --------------------------------------------------
    f ≡ h → g ≡ i → (f ∘ g) ≡ (h ∘ i)
  ∘-resp {A} {B} {C} {f} {h} {g} {i} x y =
   trans (cong (λ t → t ∘ g) x) (cong (λ t → h ∘ t) y)

data t : Set where
 ⊤ : t

!-unique-lemma : ∀ (x : t) → ⊤ ≡ x
!-unique-lemma ⊤ = refl

postulate
 -- I'm lazy.
 respSetC : {A B C : Set} {f h : B → C} {g i : A → B} →
  f ≡ h →
  g ≡ i →
  (λ a → f (g a)) ≡ (λ a → h (i a))
 extensionality : ∀ {A B : Set} {f g : A → B} →
  (∀ (x : A) → f x ≡ g x) →
  f ≡ g

-- I need the category of Sets...
SetC : Category (suc zero) zero zero
SetC =
 record
  { Obj = Set
  ; _⇒_ = λ x y → x → y
  ; _≈_ = _≡_
  ; id = λ x → x
  ; _∘_ = λ f g a → f (g a)
  ; assoc = refl
  ; sym-assoc = refl
  ; identityˡ = refl
  ; identityʳ = refl
  ; identity² = refl
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = respSetC
  }

_·_ : {I : Set} {A B C : Set} → (B × I → C) → (A × I → B) → A × I → C
_·_ {I} f g (x , i) = f (g(x , i) , i)

fiber-of-simple : {I : Set} → Category (suc zero) zero zero
fiber-of-simple {I} =
 record
  { Obj = Set
  ; _⇒_ = λ X Y → X × I → Y
  ; _≈_ = _≡_
  ; id  = proj₁
  ; _∘_ = _·_ -- this is the Kleisli composition
  ; assoc     = refl
  ; sym-assoc = refl
  ; identityˡ  = refl
  ; identityʳ  = refl
  ; identity² = refl
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = rresp
  }
  where
  rresp : {A B C : Set} {f h : B × I → C} {g i : A × I → B} → f ≡ h → g ≡ i → (f · g) ≡ (h · i)
  rresp {A} {B} {C} {f} {h} {g} {i} x y = trans (cong (λ t → t · g) x) (cong (_·_ h) y)

-- each fiber of a simple fibration is cartesian
fiber-of-simple-cart : ∀ {I : Set} → CartesianCategory (suc zero) zero zero
fiber-of-simple-cart {I} =
 record
  { U = fiber-of-simple {I}
  ; cartesian =
    record
     { terminal =
       record { ⊤ = t
           ; ⊤-is-terminal =
             record
              { ! = const ⊤
              ; !-unique =
                λ f → extensionality (λ x → !-unique-lemma (f x))
              }
           }
     ; products =
       record
        { product = λ {A} {B} →
          record
           { A×B = A × B
           ; π₁ = λ x → proj₁ (proj₁ x)
           ; π₂ = λ x → proj₂ (proj₁ x)
           ; ⟨_,_⟩ = λ f g t → f t , g t
           ; project₁ = refl
           ; project₂ = refl
           ; unique = λ {refl refl → refl}
           } } } }

-- but also the total category is cartesian.
simple-cart : CartesianCategory (suc zero) zero zero
simple-cart = record
 { U = simple
 ; cartesian = record
   { terminal = record { ⊤ = t , t
                       ; ⊤-is-terminal =
                         record { ! = const ⊤ , const ⊤
                                ; !-unique = bang-uniq
                                } }
   ; products = record { product = λ {A} {B} → record
              { A×B = (proj₁ A × proj₁ B) , (proj₂ A × proj₂ B)
              ; π₁ = proj₁ , λ x → proj₁ (proj₁ x)
              ; π₂ = proj₂ , (λ x → proj₂ (proj₁ x))
              ; ⟨_,_⟩ = ⟨_,_⟩
              ; project₁ = refl
              ; project₂ = refl
              ; unique = λ {refl refl → refl}
              } } } }
              where
               bang-uniq :
                {A : Category.Obj simple}
                -------------------------
                (f : (simple Category.⇒ A) (t , t)) →
                --------------------------------------
                (simple Category.≈ (const ⊤ , const ⊤)) f
               bang-uniq {I , X} (u , k) =
                cong₂ Data.Product._,_
                 (extensionality (λ x → !-unique-lemma (u x)))
                 (extensionality (λ x → !-unique-lemma (k x)))
               ⟨_,_⟩ :
                {A B C : Set × Set} →
                (simple Category.⇒ C) A →
                (simple Category.⇒ C) B →
                (simple Category.⇒ C) ((proj₁ A × proj₁ B) , (proj₂ A × proj₂ B))
               ⟨_,_⟩ {A0 , A1} {B0 , B1} {C0 , C1} (u , h) (v , k) =
                (λ x → (u x) , (v x)) , λ x → (h x) , (k x)

fib-of-simple-ccc : {I : Set} → CartesianClosed (fiber-of-simple {I})
fib-of-simple-ccc {I} =
 record
  { ⊤ = t
  ; _×_ = _×_
  ; ! = λ x → ⊤
  ; π₁ = proj₁ ⋆ proj₁
  ; π₂ = proj₂ ⋆ proj₁
  ; ⟨_,_⟩ = λ f g ci → (f ci) , (g ci)
  ; !-unique = λ f → extensionality λ x → !-unique-lemma (f x)
  ; π₁-comp = refl
  ; π₂-comp = refl
  ; ⟨,⟩-unique = λ {refl refl → refl}
  ; _^_ = λ A B → B → A
  ; eval = evev
  ; curry = λ f ci a → f (((proj₁ ci) , a) , (proj₂ ci))
  ; eval-comp = refl
  ; curry-resp-≈ = λ {refl → refl}
  ; curry-unique = λ {refl → refl}
  }
  where
  evev : {B A : Category.Obj (fiber-of-simple {I})} → (fiber-of-simple {I} Category.⇒ ((A → B) × A)) B
  evev ((f , a) , i) = f a

{-
the arrow category has objects the morphisms of `Set` and
morphisms the commutative squares

A → B
↓   ↓
C → D
-}
arrow : Category (suc zero) zero zero
arrow = record
  { Obj = Σ (Set × Set) (λ x → proj₁ x → proj₂ x)
  ; _⇒_ = arr
  ; _≈_ = _≡_
  ; id = (λ z → z) , λ z → z
  ; _∘_ = λ {A} {B} {C} f g → comp {A} {B} {C} f g
  ; assoc = refl
  ; sym-assoc = refl
  ; identityˡ = refl
  ; identityʳ = refl
  ; identity² = refl
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = λ {refl refl → refl}
  }
  where
  arr : Rel (Σ (Set × Set) (λ x → proj₁ x → proj₂ x)) zero
  arr ((a , b) , x1) ((c , d) , y1) = (a → c) × (b → d)
  comp : {A B C : Σ (Set × Set) (λ x → proj₁ x → proj₂ x)} → arr B C → arr A B → arr A C
  comp {A} {B} {C} (f0 , f1) (g0 , g1) = (λ x → f0 (g0 x)) , λ x → f1 (g1 x)

arrow-ccc : CartesianClosed arrow
arrow-ccc = record
 { ⊤ = (t , t) , (λ _ → ⊤)
 ; _×_ = prod
 ; ! = (λ _ → ⊤) , (λ _ → ⊤)
 ; π₁ = proj₁ , proj₁
 ; π₂ = proj₂ , proj₂
 ; ⟨_,_⟩ = λ {A} {B} {C} f g → pair {A} {B} {C} f g
 ; !-unique = λ {A} → bang-uniq {A}
 ; π₁-comp = refl
 ; π₂-comp = refl
 ; ⟨,⟩-unique = λ {refl refl → refl}
 ; _^_ = to-the
 ; eval = λ {B} {A} → ev {B} {A}
 ; curry = λ {C} {A} {B} x → cur {C} {A} {B} x
 ; eval-comp = refl
 ; curry-resp-≈ = λ {refl → refl}
 ; curry-unique = {!   !}
 }
 where
 prod : Category.Obj arrow → Category.Obj arrow → Category.Obj arrow
 prod ((X , Y) , u) ((A , B) , v) =
  ((X × A) , (Y × B)) , λ x → (u (proj₁ x)) , (v (proj₂ x))
 pair : {C A B : Category.Obj arrow} →
  (arrow Category.⇒ C) A →
  (arrow Category.⇒ C) B →
  (arrow Category.⇒ C) (prod A B)
 pair {(X , Y) , u} {(A , B) , v} {(E , F) , w} (f0 , f1) (g0 , g1) =
  (λ x → (f0 x) , (g0 x)) , (λ x → (f1 x) , (g1 x))
 bang-uniq : {A : Category.Obj arrow} →
  (f : (arrow Category.⇒ A) ((t , t) , (λ _ → ⊤))) →
  ((λ _ → ⊤) , (λ _ → ⊤)) ≡ f
 bang-uniq (f0 , f1) =
  cong₂ _,_
   (extensionality λ x → !-unique-lemma (f0 x))
   (extensionality (λ x → !-unique-lemma (f1 x)))
 to-the : Category.Obj arrow →
  Category.Obj arrow →
  Category.Obj arrow
 to-the ((B0 , B1) , u) ((A0 , A1) , v) =
  (p , (A1 → B1)) , (λ x a1 → proj₂ (proj₁ x) a1)
   where
   p : Set
   p = Σ ((A0 → B0) × (A1 → B1)) (λ r → (λ x → u ((proj₁ r) x)) ≡ (λ x → (proj₂ r) (v x)))
 -- ^ it's the pullback-hom
 -- λ x → u (s x) ≡ λ x → t (v x)
 ev : {B A : Category.Obj arrow} → (arrow Category.⇒ prod (to-the B A) A) B
 ev {(B0 , B1) , u} {(A0 , A1)  , v} = dis , dat
  where
  dis : proj₁ (proj₁ (prod (to-the ((B0 , B1) , u) ((A0 , A1) , v)) ((A0 , A1) , v))) → B0
  dis ((d , p) , a0) = (proj₁ d) a0
  dat : proj₂ (proj₁ (prod (to-the ((B0 , B1) , u) ((A0 , A1) , v)) ((A0 , A1) , v))) → B1
  dat (fst , snd) = fst snd
 cur : {C A B : Category.Obj arrow} →
  (arrow Category.⇒ (prod C A)) B →
  (arrow Category.⇒ C) (to-the B A)
 cur {(C0 , C1) , u} {(A0 , A1) , v} {(B0 , B1) , w} (fst , snd) =
  (λ x → ((λ t → fst (x , t)) , λ t → snd (u x , t)) , extensionality (λ a0 → {!   !})) , (λ z z₁ → snd (z , z₁))


open import DeBruijn

infix 8 _*

_* : ∀ {n} → Term n → Term n
(# x) *       = # x
(ƛ M) *       = ƛ M *
((ƛ M) · N) * = M * [ N * ]
(L · N) *     = L * · N *


{-

This file contains:
  - Eliminators of direct limit, especially an index-shifting version;
  - Connectivity of inclusion maps.

-}
{-# OPTIONS --safe #-}
module Cubical.HITs.SequentialColimit.Properties where

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

open import Cubical.Data.Nat hiding (elim)
open import Cubical.HITs.SequentialColimit.Base
open import Cubical.Homotopy.Connected

private
  variable
    ℓ ℓ' : Level

module _
  (X : Sequence ℓ) where

  open Sequence

  private
    inl∞ : (n : ℕ) → X .space n → Lim→ X
    inl∞ n = inl {n = n}

  record ElimData (P : Lim→ X → Type ℓ') : Type (ℓ-max ℓ ℓ') where
    field
      finl  : {k : ℕ}(x : X .space k) → P (inl x)
      fpush : {k : ℕ}(x : X .space k) → PathP (λ i → P (push x i)) (finl x) (finl (X .map x))

  record ElimShiftData (n : ℕ)(P : Lim→ X → Type ℓ') : Type (ℓ-max ℓ ℓ') where
    field
      finl  : {k : ℕ}(x : X .space (k + n)) → P (inl x)
      fpush : {k : ℕ}(x : X .space (k + n)) → PathP (λ i → P (push x i)) (finl x) (finl (X .map x))

  open ElimData
  open ElimShiftData

  ElimData→ElimShiftData : (n : ℕ)(P : Lim→ X → Type ℓ')
    → ElimData P → ElimShiftData n P
  ElimData→ElimShiftData n P datum .finl  = datum .finl
  ElimData→ElimShiftData n P datum .fpush = datum .fpush

  -- Preliminary lemmas
  -- The difficulty is mainly due to natural numbers having no strict +-commutativity

  private
    alg-path : (a b : ℕ) → a + (1 + b) ≡ 1 + (a + b)
    alg-path a b = +-assoc a 1 b ∙ (λ i → +-comm a 1 i + b)

  module _
    (P : Lim→ X → Type ℓ')
    (datum : ElimShiftData 0 P) where

    +-zero-0 : refl ≡ +-zero 0
    +-zero-0 i j = isSet→SquareP (λ i j → isSetℕ) refl refl refl (+-zero 0) j i

    finl'-filler : (k : ℕ) → (i : I) → (x : X .space (+-zero k i)) → P (inl x)
    finl'-filler k i = transport-filler (λ i → (x : X .space (+-zero k i)) → P (inl x)) (datum .finl) i

    finl' : (k : ℕ) → (x : X .space k) → P (inl x)
    finl' k = finl'-filler k i1

    finl'-0-eq : datum .finl ≡ finl' 0
    finl'-0-eq = sym (transportRefl _)
      ∙ (λ j → transport (λ i → (x : X .space (+-zero-0 j i)) → P (inl x)) (datum .finl {k = 0}))

    fpush' : (k : ℕ)
      → (x : X .space k)
      → PathP (λ i → P (push x i)) (finl' k x) (finl' (1 + k) (X .map x))
    fpush' k = transport
      (λ i → (x : X .space (+-zero k i)) →
        PathP (λ i → P (push x i)) (finl'-filler k i x) (finl'-filler (1 + k) i (X .map x))) (datum .fpush)

    ElimShiftData0→ElimData : ElimData P
    ElimShiftData0→ElimData .finl  = finl'  _
    ElimShiftData0→ElimData .fpush = fpush' _

  module _
    (n : ℕ)(P : Lim→ X → Type ℓ')
    (datum : ElimShiftData (1 + n) P) where

    alg-path-0 : (b : ℕ) → refl ≡ alg-path 0 b
    alg-path-0 b i j = isSet→SquareP (λ i j → isSetℕ) refl refl refl (alg-path 0 b) j i

    finl-n-filler : (k : ℕ) → (i : I) → ((x : X .space (alg-path k n i)) → P (inl x))
    finl-n-filler k i = transport-filler (λ i → (x : X .space (alg-path k n i)) → P (inl x)) (datum .finl) i

    finl-n : (k : ℕ) → (x : X .space ((1 + k) + n)) → P (inl x)
    finl-n k = finl-n-filler k i1

    fpush-n : (k : ℕ)
      → (x : X .space ((1 + k) + n))
      → PathP (λ i → P (push x i)) (finl-n k x) (finl-n (1 + k) (X .map x))
    fpush-n k =
      transport
        (λ i → (x : X .space (alg-path k n i)) →
          PathP (λ i → P (push x i)) (finl-n-filler k i x) (finl-n-filler (1 + k) i (X .map x))) (datum .fpush)

    finl-0-filler : (x : X .space n) → (i : I) → P (push x i)
    finl-0-filler x i = transport-filler (λ i → P (push x (~ i))) (datum .finl {k = 0} (X .map x)) (~ i)

    finl-0 : (x : X .space n) → P (inl x)
    finl-0 x = finl-0-filler x i0

    finl-0-eq : datum .finl {k = 0} ≡ finl-n 0
    finl-0-eq = sym (transportRefl _)
      ∙ (λ j → transport (λ i → (x : X .space (alg-path-0 n j i)) → P (inl x)) (datum .finl {k = 0}))

    fpush-0 :
        (x : X .space n)
      → PathP (λ i → P (push x i)) (finl-0 x) (finl-n 0 (X .map x))
    fpush-0 x i =
      hcomp (λ j → λ
          { (i = i0) → finl-0 x
          ; (i = i1) → finl-0-eq j (X .map x) })
        (finl-0-filler x i)

    elimShiftDataSuc : ElimShiftData n P
    elimShiftDataSuc .finl  {k = 0} = finl-0
    elimShiftDataSuc .finl  {k = suc k} = finl-n k
    elimShiftDataSuc .fpush {k = 0} = fpush-0
    elimShiftDataSuc .fpush {k = suc k} = fpush-n k

  -- The eliminators

  elim :
      (P : Lim→ X → Type ℓ')
    → ElimData P
    → (x : Lim→ X) → P x
  elim P datum (inl x) = datum .finl x
  elim P datum (push x i) = datum .fpush x i

  elimShift : (n : ℕ)
    → (P : Lim→ X → Type ℓ')
    → ElimShiftData n P
    → (x : Lim→ X) → P x
  elimShift 0 _ datum = elim _ (ElimShiftData0→ElimData _ datum)
  elimShift (suc n) _ datum = elimShift n _ (elimShiftDataSuc _ _ datum)

  elimShiftβ : (n : ℕ)(k : ℕ)
    → (P : Lim→ X → Type ℓ')
    → (datum : ElimShiftData n P)
    → elimShift _ _ datum ∘ inl∞ (k + n) ≡ datum .finl
  elimShiftβ 0 0 _ datum = sym (finl'-0-eq _ datum)
  elimShiftβ 0 (suc k) P datum =
    transport (λ i → elimShift _ _ datum ∘ inl∞ (+-zero (suc k) (~ i))
      ≡ finl'-filler P datum (suc k) (~ i)) refl
  elimShiftβ (suc n) 0 _ datum =
      elimShiftβ n _ _ (elimShiftDataSuc _ _ datum)
    ∙ sym (finl-0-eq _ _ datum)
  elimShiftβ (suc n) (suc k) P datum =
    transport (λ i → elimShift _ _ datum ∘ inl∞ (alg-path (suc k) n (~ i))
      ≡ finl-n-filler n P datum (suc k) (~ i))
      (elimShiftβ n (suc (suc k)) P (elimShiftDataSuc _ _ datum))

  -- Lemma to lift sections

  open Iso

  private
    transpSec :
      (n : ℕ)(Y : Lim→ X → Type ℓ')
      (sec : (x : X .space n) → Y (inl x))
      → (x : X .space n) → Y (inl (X .map x))
    transpSec n Y sec x = transport (λ i → Y (push x i)) (sec x)

    module _
      (d : ℕ)(n : ℕ)
      (conn : isConnectedFun d (X .map {n = n}))
      (Y : Lim→ X → TypeOfHLevel ℓ' d) where

      module _
        (sec : (x : X .space n) → Y (inl (X .map x)) .fst) where

        lift-iso = elim.isIsoPrecompose _ d (Y ∘ inl) conn

        liftSec' : (x : X .space (1 + n)) → Y (inl x) .fst
        liftSec' = lift-iso .inv sec

        liftSecPath' : (x : X .space n) → sec x ≡ liftSec' (X .map x)
        liftSecPath' x i = lift-iso .rightInv sec (~ i) x

      module _
        (sec : (x : X .space n) → Y (inl x) .fst) where

        liftSec : (x : X .space (1 + n)) → Y (inl x) .fst
        liftSec = liftSec' (transpSec n (λ x → Y x .fst) sec)

        liftSecPath :
          (x : X .space n)
          → PathP (λ i → Y (push x i) .fst) (sec x) (liftSec (X .map x))
        liftSecPath x i =
          hcomp (λ j → λ
            { (i = i0) → sec x
            ; (i = i1) → liftSecPath'
                (transpSec n (λ x → Y x .fst) sec) x j })
          (transport-filler (λ i → Y (push x i) .fst) (sec x) i)

  module _
    (d : ℕ)(n : ℕ)
    (conn : (k : ℕ) → isConnectedFun d (X .map {n = k + n})) where

    private
      module _
        (Y : Lim→ X → TypeOfHLevel ℓ' d) where

        lifting : (k : ℕ)(sec : (x : X .space n) → Y (inl x) .fst)
          → (x : X .space (k + n)) → Y (inl x) .fst
        lifting 0 sec = sec
        lifting (suc k) sec = liftSec d _ (conn _) Y (lifting k sec)

        liftingPath : (k : ℕ)(sec : (x : X .space n) → Y (inl x) .fst)
          → (x : X .space (k + n))
          → PathP (λ i → Y (push x i) .fst) (lifting k sec x) (lifting (1 + k) sec (X .map x))
        liftingPath k sec = liftSecPath d _ (conn _) Y (lifting k sec)

        liftingData : ((x : X .space n) → Y (inl x) .fst) → ElimShiftData n (λ x → Y x .fst)
        liftingData sec .finl  = lifting _ sec
        liftingData sec .fpush = liftingPath _ sec

        hasSectionInl∘ : hasSection (λ (sec : (x : Lim→ X) → Y x .fst) → sec ∘ inl {n = n})
        hasSectionInl∘ .fst sec = elimShift  _ _   (liftingData sec)
        hasSectionInl∘ .snd sec = elimShiftβ _ _ _ (liftingData sec)

    -- Connectivity of inclusion map

    isConnectedInl∞ : isConnectedFun d (inl∞ n)
    isConnectedInl∞ = elim.isConnectedPrecompose _ _ hasSectionInl∘


open import Nat
open import Prelude
open import contexts
open import dynamics-core
open import type-assignment-unicity

module canonical-indeterminate-forms where

  -- this type gives somewhat nicer syntax for the output of the canonical
  -- forms lemma for indeterminates at num type
  data cif-num : (Δ : hctx) (d : ihexp) → Set where
    CIFNPlus1 : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ]
        ((d == d1 ·+ d2) ×
         (Δ , ∅ ⊢ d1 :: num) ×
         (Δ , ∅ ⊢ d2 :: num) ×
         (d1 indet) ×
         (d2 final)
        )
        → cif-num Δ d
    CIFNPlus2 : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ]
        ((d == d1 ·+ d2) ×
         (Δ , ∅ ⊢ d1 :: num) ×
         (Δ , ∅ ⊢ d2 :: num) ×
         (d1 final) ×
         (d2 indet)
        )
        → cif-num Δ d
    CIFNAp : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ] Σ[ τ2 ∈ htyp ]
        ((d == d1 ∘ d2) ×
         (Δ , ∅ ⊢ d1 :: τ2 ==> num) ×
         (Δ , ∅ ⊢ d2 :: τ2) ×
         (d1 indet) ×
         (d2 final) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d1' : ihexp) → d1 ≠ (d1' ⟨ τ3 ==> τ4 ⇒ τ3' ==> τ4' ⟩))
        )
        → cif-num Δ d
    CIFNCase : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ x ∈ Nat ] Σ[ d2 ∈ ihexp ] Σ[ y ∈ Nat ] Σ[ d3 ∈ ihexp ]
      Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ]
        ((d == case d1 x d2 y d3) ×
         (Δ , ∅ ⊢ d1 :: τ1 ⊕ τ2) ×
         (Δ , ■ (x , τ1) ⊢ d2 :: num) ×
         (Δ , ■ (y , τ2) ⊢ d3 :: num) ×
         (d1 indet) ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inl τ d') ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inr τ d') ×
         ((τ3 τ4 τ3' τ4' : htyp) (d' : ihexp) →
           d1 ≠ (d' ⟨(τ3 ⊕ τ4) ⇒ (τ3' ⊕ τ4')⟩))
        )
        → cif-num Δ d
    CIFNFst : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == fst d') ×
         (Δ , ∅ ⊢ d' :: num ⊠ τ') ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-num Δ d
    CIFNSnd : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == snd d') ×
         (Δ , ∅ ⊢ d' :: τ' ⊠ num) ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-num Δ d
    CIFNEHole : ∀{Δ d} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ]
        ((d == ⦇-⦈⟨ u , σ ⟩) ×
         ((u :: num [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
        → cif-num Δ d
    CIFNNEHole : ∀{Δ d} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ] Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == ⦇⌜ d' ⌟⦈⟨ u , σ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (d' final) ×
         ((u :: num [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
        → cif-num Δ d
    CIFNCast : ∀{Δ d} →
      Σ[ d' ∈ ihexp ]
        ((d == d' ⟨ ⦇-⦈ ⇒ num ⟩) ×
         (Δ , ∅ ⊢ d' :: ⦇-⦈) ×
         (d' indet) ×
         ((d'' : ihexp) (τ' : htyp) → d' ≠ (d'' ⟨ τ' ⇒ ⦇-⦈ ⟩))
        )
        → cif-num Δ d
    CIFNFailedCast : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == d' ⟨ τ' ⇒⦇-⦈⇏ num ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (τ' ground) ×
         (τ' ≠ num)
        )
        → cif-num Δ d

  canonical-indeterminate-forms-num : ∀{Δ d} →
                                      Δ , ∅ ⊢ d :: num →
                                      d indet →
                                      cif-num Δ d
  canonical-indeterminate-forms-num TANum ()
  canonical-indeterminate-forms-num (TAPlus wt wt₁) (IPlus1 x x₁) = CIFNPlus1 (_ , _ , refl , wt , wt₁ , x , x₁)
  canonical-indeterminate-forms-num (TAPlus wt wt₁) (IPlus2 x x₁) = CIFNPlus2 (_ , _  , refl , wt , wt₁ , x , x₁)
  canonical-indeterminate-forms-num (TAVar x₁) ()
  canonical-indeterminate-forms-num (TAAp wt wt₁) (IAp x ind x₁) = CIFNAp (_ , _ , _ , refl , wt , wt₁ , ind , x₁ , x)
  canonical-indeterminate-forms-num {Δ = Δ} (TACase {τ1 = τ1} {τ2 = τ2} {x = x} {d1 = d1} {y = y} {d2 = d2} wt _ wt₁ _ wt₂) (ICase ninl ninr ncast ind) = CIFNCase (_ , _ , _ , _ , _ , _ , _ , refl , wt , tr (λ Γ' → Δ , Γ' ⊢ d1 :: num) (extend-empty x τ1) wt₁ , tr (λ Γ' → Δ , Γ' ⊢ d2 :: num) (extend-empty y τ2) wt₂ , ind , ninl , ninr , ncast)
  canonical-indeterminate-forms-num (TAFst wt) (IFst x x₁ ind) = CIFNFst (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-num (TASnd wt) (ISnd x x₁ ind) = CIFNSnd (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-num (TAEHole x x₁) IEHole = CIFNEHole (_ , _ , _ , refl , x , x₁)
  canonical-indeterminate-forms-num (TANEHole x wt x₁) (INEHole x₂) = CIFNNEHole (_ , _ , _ , _ , _ , refl , wt , x₂ , x , x₁)
  canonical-indeterminate-forms-num (TACast wt x) (ICastHoleGround x₁ ind x₂) = CIFNCast (_ , refl , wt , ind , x₁)
  canonical-indeterminate-forms-num (TAFailedCast x x₁ x₂ x₃) (IFailedCast x₄ x₅ x₆ x₇) = CIFNFailedCast (_ , _ , refl , x , x₅ , x₇)

  -- this type gives somewhat nicer syntax for the output of the canonical
  -- forms lemma for indeterminates at arrow type
  data cif-arr : (Δ : hctx) (d : ihexp) (τ1 τ2 : htyp) → Set where
    CIFAEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ]
        ((d == ⦇-⦈⟨ u , σ ⟩) ×
         ((u :: (τ1 ==> τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
      → cif-arr Δ d τ1 τ2
    CIFANEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ] Σ[ Γ ∈ tctx ]
        ((d == ⦇⌜ d' ⌟⦈⟨ u , σ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (d' final) ×
         ((u :: (τ1 ==> τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
        → cif-arr Δ d τ1 τ2
    CIFAAp : ∀{d Δ τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ] Σ[ τ2' ∈ htyp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ]
        ((d == d1 ∘ d2) ×
         (Δ , ∅ ⊢ d1 :: τ2' ==> (τ1 ==> τ2)) ×
         (Δ , ∅ ⊢ d2 :: τ2') ×
         (d1 indet) ×
         (d2 final) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d1' : ihexp) → d1 ≠ (d1' ⟨ τ3 ==> τ4 ⇒ τ3' ==> τ4' ⟩))
        )
        → cif-arr Δ d τ1 τ2
    CIFACase : ∀{Δ d τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ x ∈ Nat ] Σ[ d2 ∈ ihexp ] Σ[ y ∈ Nat ] Σ[ d3 ∈ ihexp ]
      Σ[ τ3 ∈ htyp ] Σ[ τ4 ∈ htyp ]
        ((d == case d1 x d2 y d3) ×
         (Δ , ∅ ⊢ d1 :: τ3 ⊕ τ4) ×
         (Δ , ■ (x , τ3) ⊢ d2 :: τ1 ==> τ2) ×
         (Δ , ■ (y , τ4) ⊢ d3 :: τ1 ==> τ2) ×
         (d1 indet) ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inl τ d') ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inr τ d') ×
         ((τ5 τ6 τ5' τ6' : htyp) (d' : ihexp) →
           d1 ≠ (d' ⟨(τ5 ⊕ τ6) ⇒ (τ5' ⊕ τ6')⟩))
        )
        → cif-arr Δ d τ1 τ2
    CIFAFst : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == fst d') ×
         (Δ , ∅ ⊢ d' :: (τ1 ==> τ2) ⊠ τ') ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-arr Δ d τ1 τ2
    CIFASnd : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == snd d') ×
         (Δ , ∅ ⊢ d' :: τ' ⊠ (τ1 ==> τ2)) ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-arr Δ d τ1 τ2
    CIFACast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ] Σ[ τ1' ∈ htyp ] Σ[ τ2' ∈ htyp ]
        ((d == d' ⟨ (τ1' ==> τ2') ⇒ (τ1 ==> τ2) ⟩) ×
         (Δ , ∅ ⊢ d' :: τ1' ==> τ2') ×
         (d' indet) ×
         ((τ1' ==> τ2') ≠ (τ1 ==> τ2))
        )
       → cif-arr Δ d τ1 τ2
    CIFACastHole : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ]
        ((d == (d' ⟨ ⦇-⦈ ⇒ ⦇-⦈ ==> ⦇-⦈ ⟩)) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: ⦇-⦈) ×
         (d' indet) ×
         ((d'' : ihexp) (τ' : htyp) → d' ≠ (d'' ⟨ τ' ⇒ ⦇-⦈ ⟩))
        )
        → cif-arr Δ d τ1 τ2
    CIFAFailedCast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == (d' ⟨ τ' ⇒⦇-⦈⇏ ⦇-⦈ ==> ⦇-⦈ ⟩) ) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (τ' ground) ×
         (τ' ≠ (⦇-⦈ ==> ⦇-⦈))
        )
        → cif-arr Δ d τ1 τ2

  canonical-indeterminate-forms-arr : ∀{Δ d τ1 τ2 } →
                                      Δ , ∅ ⊢ d :: (τ1 ==> τ2) →
                                      d indet →
                                      cif-arr Δ d τ1 τ2
  canonical-indeterminate-forms-arr (TAVar x₁) ()
  canonical-indeterminate-forms-arr (TALam _ wt) ()
  canonical-indeterminate-forms-arr (TAAp wt wt₁) (IAp x ind x₁) = CIFAAp (_ , _ , _ , _ , _ , refl , wt , wt₁ , ind , x₁ , x)
  canonical-indeterminate-forms-arr {Δ = Δ} (TACase {τ1 = τ1} {τ2 = τ2} {τ = τ3 ==> τ4} {x = x} {d1 = d1} {y = y} {d2 = d2} wt _ wt₁ _ wt₂) (ICase ninl ninr ncast ind) = CIFACase (_ , _ , _ , _ , _ , _ , _ , refl , wt , tr (λ Γ' → Δ , Γ' ⊢ d1 :: τ3 ==> τ4) (extend-empty x τ1) wt₁ , tr (λ Γ' → Δ , Γ' ⊢ d2 :: τ3 ==> τ4) (extend-empty y τ2) wt₂ , ind , ninl , ninr , ncast)
  canonical-indeterminate-forms-arr (TAFst wt) (IFst x x₁ ind) = CIFAFst (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-arr (TASnd wt) (ISnd x x₁ ind) = CIFASnd (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-arr (TAEHole x x₁) IEHole = CIFAEHole (_ , _ , _ , refl , x , x₁)
  canonical-indeterminate-forms-arr (TANEHole x wt x₁) (INEHole x₂) = CIFANEHole (_ , _ , _ , _ , _ , refl , wt , x₂ , x , x₁)
  canonical-indeterminate-forms-arr (TACast wt x) (ICastArr x₁ ind) = CIFACast (_ , _ , _ , _ , _ , refl , wt , ind , x₁)
  canonical-indeterminate-forms-arr (TACast wt TCHole2) (ICastHoleGround x₁ ind GArrHole) = CIFACastHole (_ , refl , refl , refl , wt , ind , x₁)
  canonical-indeterminate-forms-arr (TAFailedCast x x₁ GArrHole x₃) (IFailedCast x₄ x₅ GArrHole x₇) = CIFAFailedCast (_ , _ , refl , refl , refl , x , x₅ , x₇)

  -- this type gives somewhat nicer syntax for the output of the canonical
  -- forms lemma for indeterminates at sum type
  data cif-sum : (Δ : hctx) (d : ihexp) (τ1 τ2 : htyp) → Set where
    CIFSEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ]
        ((d == ⦇-⦈⟨ u , σ ⟩) ×
         ((u :: (τ1 ⊕ τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
      → cif-sum Δ d τ1 τ2
    CIFSNEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ] Σ[ Γ ∈ tctx ]
        ((d == ⦇⌜ d' ⌟⦈⟨ u , σ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (d' final) ×
         ((u :: (τ1 ⊕ τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
        → cif-sum Δ d τ1 τ2
    CIFSAp : ∀{d Δ τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ] Σ[ τ2' ∈ htyp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ]
        ((d == d1 ∘ d2) ×
         (Δ , ∅ ⊢ d1 :: τ2' ==> (τ1 ⊕ τ2)) ×
         (Δ , ∅ ⊢ d2 :: τ2') ×
         (d1 indet) ×
         (d2 final) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d1' : ihexp) → d1 ≠ (d1' ⟨ τ3 ==> τ4 ⇒ τ3' ==> τ4' ⟩))
        )
        → cif-sum Δ d τ1 τ2
    CIFSInl : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ]
        ((d == inl τ2 d') ×
         (Δ , ∅ ⊢ d' :: τ1) ×
         (d' indet)
        )
        → cif-sum Δ d τ1 τ2
    CIFSInr : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ]
        ((d == inr τ1 d') ×
         (Δ , ∅ ⊢ d' :: τ2) ×
         (d' indet)
        )
        → cif-sum Δ d τ1 τ2
    CIFSCase : ∀{Δ d τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ x ∈ Nat ] Σ[ d2 ∈ ihexp ] Σ[ y ∈ Nat ] Σ[ d3 ∈ ihexp ]
      Σ[ τ3 ∈ htyp ] Σ[ τ4 ∈ htyp ]
        ((d == case d1 x d2 y d3) ×
         (Δ , ∅ ⊢ d1 :: τ3 ⊕ τ4) ×
         (Δ , ■ (x , τ3) ⊢ d2 :: τ1 ⊕ τ2) ×
         (Δ , ■ (y , τ4) ⊢ d3 :: τ1 ⊕ τ2) ×
         (d1 indet) ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inl τ d') ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inr τ d') ×
         ((τ5 τ6 τ5' τ6' : htyp) (d' : ihexp) →
           d1 ≠ (d' ⟨(τ5 ⊕ τ6) ⇒ (τ5' ⊕ τ6')⟩))
        )
        → cif-sum Δ d τ1 τ2
    CIFSFst : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == fst d') ×
         (Δ , ∅ ⊢ d' :: (τ1 ⊕ τ2) ⊠ τ') ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-sum Δ d τ1 τ2
    CIFSSnd : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == snd d') ×
         (Δ , ∅ ⊢ d' :: τ' ⊠ (τ1 ⊕ τ2)) ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-sum Δ d τ1 τ2
    CIFSCast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ] Σ[ τ1' ∈ htyp ] Σ[ τ2' ∈ htyp ]
        ((d == d' ⟨ (τ1' ⊕ τ2') ⇒ (τ1 ⊕ τ2) ⟩) ×
         (Δ , ∅ ⊢ d' :: τ1' ⊕ τ2') ×
         (d' indet) ×
         ((τ1' ⊕ τ2') ≠ (τ1 ⊕ τ2))
        )
       → cif-sum Δ d τ1 τ2
    CIFSCastHole : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ]
        ((d == (d' ⟨ ⦇-⦈ ⇒ ⦇-⦈ ⊕ ⦇-⦈ ⟩)) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: ⦇-⦈) ×
         (d' indet) ×
         ((d'' : ihexp) (τ' : htyp) → d' ≠ (d'' ⟨ τ' ⇒ ⦇-⦈ ⟩))
        )
        → cif-sum Δ d τ1 τ2
    CIFSFailedCast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == (d' ⟨ τ' ⇒⦇-⦈⇏ ⦇-⦈ ⊕ ⦇-⦈ ⟩) ) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (τ' ground) ×
         (τ' ≠ (⦇-⦈ ⊕ ⦇-⦈))
        )
        → cif-sum Δ d τ1 τ2

  canonical-indeterminate-forms-sum : ∀{Δ d τ1 τ2 } →
                                      Δ , ∅ ⊢ d :: (τ1 ⊕ τ2) →
                                      d indet →
                                      cif-sum Δ d τ1 τ2
  canonical-indeterminate-forms-sum (TAAp wt wt₁) (IAp x ind x₁) = CIFSAp (_ , _ , _ , _ , _ , refl , wt , wt₁ , ind , x₁ , x)
  canonical-indeterminate-forms-sum (TAInl wt) (IInl ind) = CIFSInl (_ , refl , wt , ind)
  canonical-indeterminate-forms-sum (TAInr wt) (IInr ind) = CIFSInr (_ , refl , wt , ind)
  canonical-indeterminate-forms-sum {Δ = Δ} (TACase {τ1 = τ1} {τ2 = τ2} {τ = τ3 ⊕ τ4} {x = x} {d1 = d1} {y = y} {d2 = d2} wt _ wt₁ _ wt₂) (ICase ninl ninr ncast ind) = CIFSCase (_ , _ , _ , _ , _ , _ , _ , refl , wt , tr (λ Γ' → Δ , Γ' ⊢ d1 :: (τ3 ⊕ τ4)) (extend-empty x τ1) wt₁ , tr (λ Γ' → Δ , Γ' ⊢ d2 :: (τ3 ⊕ τ4)) (extend-empty y τ2) wt₂ , ind , ninl , ninr , ncast)
  canonical-indeterminate-forms-sum (TAFst wt) (IFst x x₁ ind) = CIFSFst (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-sum (TASnd wt) (ISnd x x₁ ind) = CIFSSnd (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-sum (TAEHole x x₁) IEHole = CIFSEHole (_ , _ , _ , refl , x , x₁)
  canonical-indeterminate-forms-sum (TANEHole x wt x₁) (INEHole x₂) = CIFSNEHole (_ , _ , _ , _ , _ , refl , wt , x₂ , x , x₁)
  canonical-indeterminate-forms-sum (TACast wt x) (ICastSum x₁ ind) = CIFSCast (_ , _ , _ , _ , _ , refl , wt , ind , x₁)
  canonical-indeterminate-forms-sum (TACast wt x) (ICastHoleGround x₁ ind GSumHole) = CIFSCastHole (_ , refl , refl , refl , wt , ind , x₁)
  canonical-indeterminate-forms-sum (TAFailedCast wt x x₁ x₂) (IFailedCast x₃ x₄ GSumHole x₆) = CIFSFailedCast (_ , _ , refl , refl , refl , wt , x₄ , x₆)
  canonical-indeterminate-forms-sum (TAVar x) ()

  -- this type gives somewhat nicer syntax for the output of the canonical
  -- forms lemma for indeterminates at product type
  data cif-prod : (Δ : hctx) (d : ihexp) (τ1 τ2 : htyp) → Set where
    CIFPEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ]
        ((d == ⦇-⦈⟨ u , σ ⟩) ×
         ((u :: (τ1 ⊠ τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
      → cif-prod Δ d τ1 τ2
    CIFPNEHole : ∀{d Δ τ1 τ2} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ] Σ[ Γ ∈ tctx ]
        ((d == ⦇⌜ d' ⌟⦈⟨ u , σ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (d' final) ×
         ((u :: (τ1 ⊠ τ2) [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
        → cif-prod Δ d τ1 τ2
    CIFPAp : ∀{d Δ τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ] Σ[ τ2' ∈ htyp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ]
        ((d == d1 ∘ d2) ×
         (Δ , ∅ ⊢ d1 :: τ2' ==> (τ1 ⊠ τ2)) ×
         (Δ , ∅ ⊢ d2 :: τ2') ×
         (d1 indet) ×
         (d2 final) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d1' : ihexp) → d1 ≠ (d1' ⟨ τ3 ==> τ4 ⇒ τ3' ==> τ4' ⟩))
        )
        → cif-prod Δ d τ1 τ2
    CIFPCase : ∀{Δ d τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ x ∈ Nat ] Σ[ d2 ∈ ihexp ] Σ[ y ∈ Nat ] Σ[ d3 ∈ ihexp ]
      Σ[ τ3 ∈ htyp ] Σ[ τ4 ∈ htyp ]
        ((d == case d1 x d2 y d3) ×
         (Δ , ∅ ⊢ d1 :: τ3 ⊕ τ4) ×
         (Δ , ■ (x , τ3) ⊢ d2 :: τ1 ⊠ τ2) ×
         (Δ , ■ (y , τ4) ⊢ d3 :: τ1 ⊠ τ2) ×
         (d1 indet) ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inl τ d') ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inr τ d') ×
         ((τ5 τ6 τ5' τ6' : htyp) (d' : ihexp) →
           d1 ≠ (d' ⟨(τ5 ⊕ τ6) ⇒ (τ5' ⊕ τ6')⟩))
        )
        → cif-prod Δ d τ1 τ2
    CIFPPair1 : ∀{Δ d τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ]
        ((d == ⟨ d1 , d2 ⟩) ×
         (Δ , ∅ ⊢ d1 :: τ1) ×
         (Δ , ∅ ⊢ d2 :: τ2) ×
         d1 indet ×
         d2 final
        )
        → cif-prod Δ d τ1 τ2
    CIFPPair2 : ∀{Δ d τ1 τ2} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ]
        ((d == ⟨ d1 , d2 ⟩) ×
         (Δ , ∅ ⊢ d1 :: τ1) ×
         (Δ , ∅ ⊢ d2 :: τ2) ×
         d1 final ×
         d2 indet
        )
        → cif-prod Δ d τ1 τ2
    CIFPFst : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == fst d') ×
         (Δ , ∅ ⊢ d' :: (τ1 ⊠ τ2) ⊠ τ') ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-prod Δ d τ1 τ2
    CIFPSnd : ∀{Δ d τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == snd d') ×
         (Δ , ∅ ⊢ d' :: τ' ⊠ (τ1 ⊠ τ2)) ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-prod Δ d τ1 τ2
    CIFPCast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ] Σ[ τ1' ∈ htyp ] Σ[ τ2' ∈ htyp ]
        ((d == d' ⟨ (τ1' ⊠ τ2') ⇒ (τ1 ⊠ τ2) ⟩) ×
         (Δ , ∅ ⊢ d' :: τ1' ⊠ τ2') ×
         (d' indet) ×
         ((τ1' ⊠ τ2') ≠ (τ1 ⊠ τ2))
        )
       → cif-prod Δ d τ1 τ2
    CIFPCastHole : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ]
        ((d == (d' ⟨ ⦇-⦈ ⇒ ⦇-⦈ ⊠ ⦇-⦈ ⟩)) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: ⦇-⦈) ×
         (d' indet) ×
         ((d'' : ihexp) (τ' : htyp) → d' ≠ (d'' ⟨ τ' ⇒ ⦇-⦈ ⟩))
        )
        → cif-prod Δ d τ1 τ2
    CIFPFailedCast : ∀{d Δ τ1 τ2} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == (d' ⟨ τ' ⇒⦇-⦈⇏ ⦇-⦈ ⊠ ⦇-⦈ ⟩) ) ×
         (τ1 == ⦇-⦈) ×
         (τ2 == ⦇-⦈) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (τ' ground) ×
         (τ' ≠ (⦇-⦈ ⊠ ⦇-⦈))
        )
        → cif-prod Δ d τ1 τ2

  canonical-indeterminate-forms-prod : ∀{Δ d τ1 τ2} →
                                       Δ , ∅ ⊢ d :: (τ1 ⊠ τ2) →
                                       d indet →
                                       cif-prod Δ d τ1 τ2
  canonical-indeterminate-forms-prod (TAVar x) ()
  canonical-indeterminate-forms-prod (TAAp wt wt₁) (IAp x ind x₁) = CIFPAp (_ , _ , _ , _ , _ , refl , wt , wt₁ , ind , x₁ , x)
  canonical-indeterminate-forms-prod {Δ = Δ} (TACase {τ1 = τ1} {τ2 = τ2} {τ = τ3 ⊠ τ4} {x = x} {d1 = d1} {y = y} {d2 = d2} wt _ wt₁ _ wt₂) (ICase ninl ninr ncast ind) = CIFPCase (_ , _ , _ , _ , _ , _ , _ , refl , wt , tr (λ Γ' → Δ , Γ' ⊢ d1 :: (τ3 ⊠ τ4)) (extend-empty x τ1) wt₁ , tr (λ Γ' → Δ , Γ' ⊢ d2 :: (τ3 ⊠ τ4)) (extend-empty y τ2) wt₂ , ind , ninl , ninr , ncast)
  canonical-indeterminate-forms-prod (TAPair wt wt₁) (IPair1 ind x) = CIFPPair1 (_ , _ , refl , wt , wt₁ , ind , x)
  canonical-indeterminate-forms-prod (TAPair wt wt₁) (IPair2 x ind) = CIFPPair2 (_ , _ , refl , wt , wt₁ , x , ind)
  canonical-indeterminate-forms-prod (TAFst wt) (IFst x x₁ ind) = CIFPFst (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-prod (TASnd wt) (ISnd x x₁ ind) = CIFPSnd (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-prod (TAEHole x x₁) IEHole = CIFPEHole (_ , _ , _ , refl , x , x₁)
  canonical-indeterminate-forms-prod (TANEHole x wt x₁) (INEHole x₂) = CIFPNEHole (_ , _ , _ , _ , _ , refl , wt , x₂ , x , x₁)
  canonical-indeterminate-forms-prod (TACast wt x) (ICastProd x₁ ind) = CIFPCast (_ , _ , _ , _ , _ , refl , wt , ind , x₁)
  canonical-indeterminate-forms-prod (TACast wt x) (ICastHoleGround x₁ ind GProdHole) = CIFPCastHole (_ , refl , refl , refl , wt , ind , x₁)
  canonical-indeterminate-forms-prod (TAFailedCast wt x GProdHole x₂) (IFailedCast x₃ x₄ GProdHole x₆) = CIFPFailedCast (_ , _ , refl , refl , refl , wt , x₄ , x₆)

  -- this type gives somewhat nicer syntax for the output of the canonical
  -- forms lemma for indeterminates at hole type
  data cif-hole : (Δ : hctx) (d : ihexp) → Set where
    CIFHEHole : ∀{Δ d} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ Γ ∈ tctx ]
        ((d == ⦇-⦈⟨ u , σ ⟩) ×
         ((u :: ⦇-⦈ [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
      → cif-hole Δ d
    CIFHNEHole : ∀{Δ d} →
      Σ[ u ∈ Nat ] Σ[ σ ∈ env ] Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ] Σ[ Γ ∈ tctx ]
        ((d == ⦇⌜ d' ⌟⦈⟨ u , σ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (d' final) ×
         ((u :: ⦇-⦈ [ Γ ]) ∈ Δ) ×
         (Δ , ∅ ⊢ σ :s: Γ)
        )
      → cif-hole Δ d
    CIFHAp : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ d2 ∈ ihexp ] Σ[ τ2 ∈ htyp ]
        ((d == d1 ∘ d2) ×
         (Δ , ∅ ⊢ d1 :: (τ2 ==> ⦇-⦈)) ×
         (Δ , ∅ ⊢ d2 :: τ2) ×
         (d1 indet) ×
         (d2 final) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d1' : ihexp) → d1 ≠ (d1' ⟨ τ3 ==> τ4 ⇒ τ3' ==> τ4' ⟩))
        )
      → cif-hole Δ d
    CIFHCase : ∀{Δ d} →
      Σ[ d1 ∈ ihexp ] Σ[ x ∈ Nat ] Σ[ d2 ∈ ihexp ] Σ[ y ∈ Nat ] Σ[ d3 ∈ ihexp ]
      Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ]
        ((d == case d1 x d2 y d3) ×
         (Δ , ∅ ⊢ d1 :: τ1 ⊕ τ2) ×
         (Δ , ■ (x , τ1) ⊢ d2 :: ⦇-⦈) ×
         (Δ , ■ (y , τ2) ⊢ d3 :: ⦇-⦈) ×
         (d1 indet) ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inl τ d') ×
         ((τ : htyp) (d' : ihexp) → d1 ≠ inr τ d') ×
         ((τ3 τ4 τ3' τ4' : htyp) (d' : ihexp) →
           d1 ≠ (d' ⟨(τ3 ⊕ τ4) ⇒ (τ3' ⊕ τ4')⟩))
        )
        → cif-hole Δ d
    CIFHFst : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == fst d') ×
         (Δ , ∅ ⊢ d' :: ⦇-⦈ ⊠ τ') ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-hole Δ d
    CIFHSnd : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == snd d') ×
         (Δ , ∅ ⊢ d' :: τ' ⊠ ⦇-⦈) ×
         (d' indet) ×
         ((d1 d2 : ihexp) → d' ≠ ⟨ d1 , d2 ⟩) ×
         ((τ3 τ4 τ3' τ4' : htyp) (d'' : ihexp) →
           d' ≠ (d'' ⟨(τ3 ⊠ τ4) ⇒ (τ3' ⊠ τ4')⟩))
        )
        → cif-hole Δ d
    CIFHCast : ∀{Δ d} →
      Σ[ d' ∈ ihexp ] Σ[ τ' ∈ htyp ]
        ((d == d' ⟨ τ' ⇒ ⦇-⦈ ⟩) ×
         (Δ , ∅ ⊢ d' :: τ') ×
         (τ' ground) ×
         (d' indet)
        )
      → cif-hole Δ d

  canonical-indeterminate-forms-hole : ∀{Δ d} →
                                       Δ , ∅ ⊢ d :: ⦇-⦈ →
                                       d indet →
                                       cif-hole Δ d
  canonical-indeterminate-forms-hole (TAVar x₁) ()
  canonical-indeterminate-forms-hole (TAAp wt wt₁) (IAp x ind x₁) = CIFHAp (_ , _ , _ , refl , wt , wt₁ , ind , x₁ , x)
  canonical-indeterminate-forms-hole {Δ = Δ} (TACase {τ1 = τ1} {τ2 = τ2} {x = x} {d1 = d1} {y = y} {d2 = d2} wt _ wt₁ _ wt₂) (ICase ninl ninr ncast ind) = CIFHCase (_ , _ , _ , _ , _ , _ , _ , refl , wt , tr (λ Γ' → Δ , Γ' ⊢ d1 :: ⦇-⦈) (extend-empty x τ1) wt₁ , tr (λ Γ' → Δ , Γ' ⊢ d2 :: ⦇-⦈) (extend-empty y τ2) wt₂ , ind , ninl , ninr , ncast)
  canonical-indeterminate-forms-hole (TAFst wt) (IFst x x₁ ind) = CIFHFst (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-hole (TASnd wt) (ISnd x x₁ ind) = CIFHSnd (_ , _ , refl , wt , ind , x , x₁)
  canonical-indeterminate-forms-hole (TAEHole x x₁) IEHole = CIFHEHole (_ , _ , _ , refl , x , x₁)
  canonical-indeterminate-forms-hole (TANEHole x wt x₁) (INEHole x₂) = CIFHNEHole (_ , _ , _ , _ , _ , refl , wt , x₂ , x , x₁)
  canonical-indeterminate-forms-hole (TACast wt x) (ICastGroundHole x₁ ind) = CIFHCast (_ , _ , refl , wt , x₁ , ind)
  canonical-indeterminate-forms-hole (TACast wt x) (ICastHoleGround x₁ ind ())
  canonical-indeterminate-forms-hole (TAFailedCast x x₁ () x₃) (IFailedCast x₄ x₅ x₆ x₇)

  canonical-indeterminate-forms-coverage : ∀{Δ d τ} →
                                           Δ , ∅ ⊢ d :: τ →
                                           d indet →
                                           τ ≠ num →
                                           ((τ1 : htyp) (τ2 : htyp) → τ ≠ (τ1 ==> τ2)) →
                                           ((τ1 : htyp) (τ2 : htyp) → τ ≠ (τ1 ⊕ τ2)) →
                                           ((τ1 : htyp) (τ2 : htyp) → τ ≠ (τ1 ⊠ τ2)) →
                                           τ ≠ ⦇-⦈ →
                                           ⊥
  canonical-indeterminate-forms-coverage {τ = num} wt ind nn na ns np nh = nn refl
  canonical-indeterminate-forms-coverage {τ = ⦇-⦈} wt ind nn na ns np nh = nh refl
  canonical-indeterminate-forms-coverage {τ = τ ==> τ₁} wt ind nn na ns np nh = na τ τ₁ refl
  canonical-indeterminate-forms-coverage {τ = τ ⊕ τ₁} wt ind nn na ns np nh = ns τ τ₁ refl
  canonical-indeterminate-forms-coverage {τ = τ ⊠ τ₁} wt ind nn na ns np nh = np τ τ₁ refl


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

open import HoTT
import homotopy.RelativelyConstantToSetExtendsViaSurjection as SurjExt

module homotopy.vankampen.Code {i j k l}
  (span : Span {i} {j} {k})
  {D : Type l} (h : D → Span.C span) (h-is-surj : is-surj h) where

  open Span span
  open import homotopy.vankampen.CodeAP span h h-is-surj
  open import homotopy.vankampen.CodeBP span h h-is-surj

  pcAA-to-pcBA : ∀ d₀ {a} → precodeAA (f (h d₀)) a → precodeBA (g (h d₀)) a
  pcAB-to-pcBB : ∀ d₀ {b} → precodeAB (f (h d₀)) b → precodeBB (g (h d₀)) b
  pcAA-to-pcBA d₀ (pc-a pA) = pc-bba d₀ (pc-b idp₀) pA
  pcAA-to-pcBA d₀ (pc-aba d pc pA) = pc-bba d (pcAB-to-pcBB d₀ pc) pA
  pcAB-to-pcBB d₀ (pc-aab d pc pB) = pc-bab d (pcAA-to-pcBA d₀ pc) pB

  abstract
    pcAA-to-pcBA-rel : ∀ d₀ {a} {pcAA₁ pcAA₂ : precodeAA (f (h d₀)) a}
      → precodeAA-rel pcAA₁ pcAA₂
      → precodeBA-rel (pcAA-to-pcBA d₀ pcAA₁) (pcAA-to-pcBA d₀ pcAA₂)
    pcAB-to-pcBB-rel : ∀ d₀ {a} {pcAB₁ pcAB₂ : precodeAB (f (h d₀)) a}
      → precodeAB-rel pcAB₁ pcAB₂
      → precodeBB-rel (pcAB-to-pcBB d₀ pcAB₁) (pcAB-to-pcBB d₀ pcAB₂)
    pcAA-to-pcBA-rel d₀ (pcAAr-idp₀-idp₀ pcAA) = pcBAr-idp₀-idp₀ (pcAA-to-pcBA d₀ pcAA)
    pcAA-to-pcBA-rel d₀ (pcAAr-cong r pA) = pcBAr-cong (pcAB-to-pcBB-rel d₀ r) pA
    pcAB-to-pcBB-rel d₀ (pcABr-idp₀-idp₀ pcAB) = pcBBr-idp₀-idp₀ (pcAB-to-pcBB d₀ pcAB)
    pcAB-to-pcBB-rel d₀ (pcABr-switch pcAB pC) = pcBBr-switch (pcAB-to-pcBB d₀ pcAB) pC
    pcAB-to-pcBB-rel d₀ (pcABr-cong r pB) = pcBBr-cong (pcAA-to-pcBA-rel d₀ r) pB

  cAA-to-cBA : ∀ d₀ {a} → codeAA (f (h d₀)) a → codeBA (g (h d₀)) a
  cAA-to-cBA d₀ = SetQuot-rec SetQuot-level (q[_] ∘ pcAA-to-pcBA d₀) (quot-rel ∘ pcAA-to-pcBA-rel d₀)
  cAB-to-cBB : ∀ d₀ {b} → codeAB (f (h d₀)) b → codeBB (g (h d₀)) b
  cAB-to-cBB d₀ = SetQuot-rec SetQuot-level (q[_] ∘ pcAB-to-pcBB d₀) (quot-rel ∘ pcAB-to-pcBB-rel d₀)

  -- BX to AX
  pcBA-to-pcAA : ∀ d₀ {a} → precodeBA (g (h d₀)) a → precodeAA (f (h d₀)) a
  pcBB-to-pcAB : ∀ d₀ {b} → precodeBB (g (h d₀)) b → precodeAB (f (h d₀)) b
  pcBA-to-pcAA d₀ (pc-bba d pc pA) = pc-aba d (pcBB-to-pcAB d₀ pc) pA
  pcBB-to-pcAB d₀ (pc-b pB) = pc-aab d₀ (pc-a idp₀) pB
  pcBB-to-pcAB d₀ (pc-bab d pc pB) = pc-aab d (pcBA-to-pcAA d₀ pc) pB

  abstract
    pcBA-to-pcAA-rel : ∀ d₀ {a} {pcBA₁ pcBA₂ : precodeBA (g (h d₀)) a}
      → precodeBA-rel pcBA₁ pcBA₂
      → precodeAA-rel (pcBA-to-pcAA d₀ pcBA₁) (pcBA-to-pcAA d₀ pcBA₂)
    pcBB-to-pcAB-rel : ∀ d₀ {a} {pcBB₁ pcBB₂ : precodeBB (g (h d₀)) a}
      → precodeBB-rel pcBB₁ pcBB₂
      → precodeAB-rel (pcBB-to-pcAB d₀ pcBB₁) (pcBB-to-pcAB d₀ pcBB₂)
    pcBA-to-pcAA-rel d₀ (pcBAr-idp₀-idp₀ pcBA) = pcAAr-idp₀-idp₀ (pcBA-to-pcAA d₀ pcBA)
    pcBA-to-pcAA-rel d₀ (pcBAr-cong r pA) = pcAAr-cong (pcBB-to-pcAB-rel d₀ r) pA
    pcBB-to-pcAB-rel d₀ (pcBBr-idp₀-idp₀ pcBB) = pcABr-idp₀-idp₀ (pcBB-to-pcAB d₀ pcBB)
    pcBB-to-pcAB-rel d₀ (pcBBr-switch pcBB pC) = pcABr-switch (pcBB-to-pcAB d₀ pcBB) pC
    pcBB-to-pcAB-rel d₀ (pcBBr-cong r pB) = pcABr-cong (pcBA-to-pcAA-rel d₀ r) pB

  cBA-to-cAA : ∀ d₀ {a} → codeBA (g (h d₀)) a → codeAA (f (h d₀)) a
  cBA-to-cAA d₀ = SetQuot-rec SetQuot-level (q[_] ∘ pcBA-to-pcAA d₀) (quot-rel ∘ pcBA-to-pcAA-rel d₀)
  cBB-to-cAB : ∀ d₀ {b} → codeBB (g (h d₀)) b → codeAB (f (h d₀)) b
  cBB-to-cAB d₀ = SetQuot-rec SetQuot-level (q[_] ∘ pcBB-to-pcAB d₀) (quot-rel ∘ pcBB-to-pcAB-rel d₀)

  -- roundtrips
  abstract
    pcBA-to-pcAA-to-pcBA : ∀ d₀ {a} (pcBA : precodeBA _ a)
      → q[ pcAA-to-pcBA d₀ (pcBA-to-pcAA d₀ pcBA) ] == q[ pcBA ] :> codeBA _ a
    pcBB-to-pcAB-to-pcBB : ∀ d₀ {b} (pcBB : precodeBB _ b)
      → q[ pcAB-to-pcBB d₀ (pcBB-to-pcAB d₀ pcBB) ] == q[ pcBB ] :> codeBB _ b
    pcBA-to-pcAA-to-pcBA d₀ (pc-bba d pc pA) = pcBB-to-pcAB-to-pcBB d₀ pc |in-ctx λ c → c-bba d c pA
    pcBB-to-pcAB-to-pcBB d₀ (pc-b pB) = pcBB-idp₀-idp₀-head pB
    pcBB-to-pcAB-to-pcBB d₀ (pc-bab d pc pB) = pcBA-to-pcAA-to-pcBA d₀ pc |in-ctx λ c → c-bab d c pB

    pcAA-to-pcBA-to-pcAA : ∀ d₀ {a} (pcAA : precodeAA _ a)
      → q[ pcBA-to-pcAA d₀ (pcAA-to-pcBA d₀ pcAA) ] == q[ pcAA ] :> codeAA _ a
    pcAB-to-pcBB-to-pcAB : ∀ d₀ {b} (pcAB : precodeAB _ b)
      → q[ pcBB-to-pcAB d₀ (pcAB-to-pcBB d₀ pcAB) ] == q[ pcAB ] :> codeAB _ b
    pcAA-to-pcBA-to-pcAA d₀ (pc-aba d pc pA) = pcAB-to-pcBB-to-pcAB d₀ pc |in-ctx λ c → c-aba d c pA
    pcAA-to-pcBA-to-pcAA d₀ (pc-a pA) = pcAA-idp₀-idp₀-head pA
    pcAB-to-pcBB-to-pcAB d₀ (pc-aab d pc pB) = pcAA-to-pcBA-to-pcAA d₀ pc |in-ctx λ c → c-aab d c pB

  private
    pcAA-lemma : ∀ d₁ d₂ {a₁} (p : h d₂ == h d₁) (pcAA : precodeAA _ a₁)
      →  q[ pcBA-prepend d₁ [ ap g p ] (pcAA-to-pcBA d₁ pcAA) ]
      == q[ pcAA-to-pcBA d₂ (pcAA-prepend d₁ [ ap f p ] pcAA) ]
    pcAB-lemma : ∀ d₁ d₂ {b₁} (p : h d₂ == h d₁) (pcAB : precodeAB _ b₁)
      →  q[ pcBB-prepend d₁ [ ap g p ] (pcAB-to-pcBB d₁ pcAB) ]
      == q[ pcAB-to-pcBB d₂ (pcAB-prepend d₁ [ ap f p ] pcAB) ]
    pcAA-lemma d₁ d₂ p (pc-a pA) = ap (λ c → c-bba d₁ c pA) $ ! $
      q[ ⟧b idp₀ bb⟦ _ ⟧a [ ap f p ] ba⟦ _ ⟧b idp₀ ]
        =⟨ quot-rel (pcBBr-switch (⟧b idp₀) [ p ]) ⟩
      q[ ⟧b idp₀ bb⟦ _ ⟧a idp₀ ba⟦ _ ⟧b [ ap g p ] ]
        =⟨ pcBB-idp₀-idp₀-head [ ap g p ] ⟩
      q[ ⟧b [ ap g p ] ]
        =⟨ ! (quot-rel (pcBBr-idp₀-idp₀ (pc-b [ ap g p ]))) ⟩
      q[ ⟧b [ ap g p ] bb⟦ _ ⟧a idp₀ ba⟦ _ ⟧b idp₀ ]
        =∎
    pcAA-lemma d₁ d₂ p (pc-aba d pc pA) = pcAB-lemma d₁ d₂ p pc |in-ctx (λ c → c-bba d c pA)
    pcAB-lemma d₁ d₂ p (pc-aab d pc pB) = pcAA-lemma d₁ d₂ p pc |in-ctx (λ c → c-bab d c pB)

  module CodeAAEquivCodeBA (a₁ : A) where

    eqv-on-image : (d₀ : D) → codeAA (f (h d₀)) a₁ ≃ codeBA (g (h d₀)) a₁
    eqv-on-image d₀ = equiv to from to-from from-to where
      to : codeAA (f (h d₀)) a₁ → codeBA (g (h d₀)) a₁
      to = cAA-to-cBA d₀

      from : codeBA (g (h d₀)) a₁ → codeAA (f (h d₀)) a₁
      from = cBA-to-cAA d₀

      abstract
        to-from : ∀ cBA → to (from cBA) == cBA
        to-from = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (pcBA-to-pcAA-to-pcBA d₀)
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

        from-to : ∀ cAA → from (to cAA) == cAA
        from-to = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (pcAA-to-pcBA-to-pcAA d₀)
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    abstract
      eqv-is-const : ∀ d₁ d₂ (p : h d₁ == h d₂)
        → eqv-on-image d₁ == eqv-on-image d₂
        [ (λ c → codeAA (f c) a₁ ≃ codeBA (g c) a₁) ↓ p ]
      eqv-is-const d₁ d₂ p = ↓-Subtype-in (λ d → is-equiv-prop) $
        ↓-→-from-transp $ λ= $
          SetQuot-elim (λ _ → =-preserves-set SetQuot-is-set)
            (λ pcAA →
              transport (λ c → codeBA (g c) a₁) p q[ pcAA-to-pcBA d₁ pcAA ]
                =⟨ ap-∘ (λ b → codeBA b a₁) g p |in-ctx (λ p → coe p q[ pcAA-to-pcBA d₁ pcAA ]) ⟩
              transport (λ b → codeBA b a₁) (ap g p) q[ pcAA-to-pcBA d₁ pcAA ]
                =⟨ transp-cBA-l d₁ (ap g p) (pcAA-to-pcBA d₁ pcAA) ⟩
              q[ pcBA-prepend d₁ [ ! $ ap g p ] (pcAA-to-pcBA d₁ pcAA) ]
                =⟨ !-ap g p |in-ctx (λ p → q[ pcBA-prepend d₁ [ p ] (pcAA-to-pcBA d₁ pcAA) ]) ⟩
              q[ pcBA-prepend d₁ [ ap g (! p) ] (pcAA-to-pcBA d₁ pcAA) ]
                =⟨ pcAA-lemma d₁ d₂ (! p) pcAA ⟩
              q[ pcAA-to-pcBA d₂ (pcAA-prepend d₁ [ ap f (! p) ] pcAA) ]
                =⟨ ap-! f p |in-ctx (λ p → q[ pcAA-to-pcBA d₂ (pcAA-prepend d₁ [ p ] pcAA) ]) ⟩
              q[ pcAA-to-pcBA d₂ (pcAA-prepend d₁ [ ! $ ap f p ] pcAA) ]
                =⟨ ! $ transp-cAA-l d₁ (ap f p) pcAA |in-ctx cAA-to-cBA d₂ ⟩
              cAA-to-cBA d₂ (transport (λ a → codeAA a a₁) (ap f p) q[ pcAA ])
                =⟨ ∘-ap (λ c → codeAA c a₁) f p |in-ctx (λ p → coe p q[ pcAA ]) |in-ctx cAA-to-cBA d₂ ⟩
              cAA-to-cBA d₂ (transport (λ c → codeAA (f c) a₁) p q[ pcAA ])
                =∎)
            (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    module SE = SurjExt
      (λ c → ≃-is-set SetQuot-is-set SetQuot-is-set)
      h h-is-surj
      eqv-on-image
      eqv-is-const

    abstract
      eqv : ∀ c → codeAA (f c) a₁ ≃ codeBA (g c) a₁
      eqv = SE.ext

      eqv-β : ∀ d → eqv (h d) == eqv-on-image d
      eqv-β = SE.β

  module CodeABEquivCodeBB (b₁ : B) where

    eqv-on-image : (d₀ : D) → codeAB (f (h d₀)) b₁ ≃ codeBB (g (h d₀)) b₁
    eqv-on-image d₀ = equiv to from to-from from-to where
      to : codeAB (f (h d₀)) b₁ → codeBB (g (h d₀)) b₁
      to = cAB-to-cBB d₀

      from : codeBB (g (h d₀)) b₁ → codeAB (f (h d₀)) b₁
      from = cBB-to-cAB d₀

      abstract
        to-from : ∀ cBB → to (from cBB) == cBB
        to-from = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (pcBB-to-pcAB-to-pcBB d₀)
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

        from-to : ∀ cAB → from (to cAB) == cAB
        from-to = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (pcAB-to-pcBB-to-pcAB d₀)
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    abstract
      eqv-is-const : ∀ d₁ d₂ (p : h d₁ == h d₂)
        → eqv-on-image d₁ == eqv-on-image d₂
        [ (λ c → codeAB (f c) b₁ ≃ codeBB (g c) b₁) ↓ p ]
      eqv-is-const d₁ d₂ p = ↓-Subtype-in (λ d → is-equiv-prop) $
        ↓-→-from-transp $ λ= $
          SetQuot-elim (λ _ → =-preserves-set SetQuot-is-set)
            (λ pcAB →
              transport (λ c → codeBB (g c) b₁) p q[ pcAB-to-pcBB d₁ pcAB ]
                =⟨ ap-∘ (λ b → codeBB b b₁) g p |in-ctx (λ p → coe p q[ pcAB-to-pcBB d₁ pcAB ]) ⟩
              transport (λ b → codeBB b b₁) (ap g p) q[ pcAB-to-pcBB d₁ pcAB ]
                =⟨ transp-cBB-l d₁ (ap g p) (pcAB-to-pcBB d₁ pcAB) ⟩
              q[ pcBB-prepend d₁ [ ! $ ap g p ] (pcAB-to-pcBB d₁ pcAB) ]
                =⟨ !-ap g p |in-ctx (λ p → q[ pcBB-prepend d₁ [ p ] (pcAB-to-pcBB d₁ pcAB) ]) ⟩
              q[ pcBB-prepend d₁ [ ap g (! p) ] (pcAB-to-pcBB d₁ pcAB) ]
                =⟨ pcAB-lemma d₁ d₂ (! p) pcAB ⟩
              q[ pcAB-to-pcBB d₂ (pcAB-prepend d₁ [ ap f (! p) ] pcAB) ]
                =⟨ ap-! f p |in-ctx (λ p → q[ pcAB-to-pcBB d₂ (pcAB-prepend d₁ [ p ] pcAB) ]) ⟩
              q[ pcAB-to-pcBB d₂ (pcAB-prepend d₁ [ ! $ ap f p ] pcAB) ]
                =⟨ ! $ transp-cAB-l d₁ (ap f p) pcAB |in-ctx cAB-to-cBB d₂ ⟩
              cAB-to-cBB d₂ (transport (λ a → codeAB a b₁) (ap f p) q[ pcAB ])
                =⟨ ∘-ap (λ c → codeAB c b₁) f p |in-ctx (λ p → coe p q[ pcAB ]) |in-ctx cAB-to-cBB d₂ ⟩
              cAB-to-cBB d₂ (transport (λ c → codeAB (f c) b₁) p q[ pcAB ])
                =∎)
            (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    module SE = SurjExt
      (λ c → ≃-is-set SetQuot-is-set SetQuot-is-set)
      h h-is-surj
      eqv-on-image
      eqv-is-const

    abstract
      eqv : ∀ c → codeAB (f c) b₁ ≃ codeBB (g c) b₁
      eqv = SE.ext

      eqv-β : ∀ d → eqv (h d) == eqv-on-image d
      eqv-β = SE.β

  module EqvPAIdEqvPB where
    abstract
      path-on-image : ∀ d₀ d₁
        →  CodeAAEquivCodeBA.eqv (f (h d₁)) (h d₀)
        == CodeABEquivCodeBB.eqv (g (h d₁)) (h d₀)
        [ (λ p → codeAP (f (h d₀)) p ≃ codeBP (g (h d₀)) p) ↓ glue (h d₁) ]
      path-on-image d₀ d₁ =
        CodeAAEquivCodeBA.eqv-β (f (h d₁)) d₀
        ∙ᵈ lemma
        ∙'ᵈ ! (CodeABEquivCodeBB.eqv-β (g (h d₁)) d₀) where
        lemma : CodeAAEquivCodeBA.eqv-on-image (f (h d₁)) d₀
             == CodeABEquivCodeBB.eqv-on-image (g (h d₁)) d₀
             [ (λ p → codeAP (f (h d₀)) p ≃ codeBP (g (h d₀)) p) ↓ glue (h d₁) ]
        lemma = ↓-Subtype-in (λ d → is-equiv-prop) $
          ↓-→-from-transp $ λ= $ SetQuot-elim
            (λ _ → =-preserves-set SetQuot-is-set)
            (λ pcAA →
              transport (λ p → codeBP (g (h d₀)) p) (glue (h d₁)) q[ pcAA-to-pcBA d₀ pcAA ]
                =⟨ transp-cBP-glue d₁ (pcAA-to-pcBA d₀ pcAA) ⟩
              q[ pcAA-to-pcBA d₀ pcAA ba⟦ d₁ ⟧b idp₀ ]
                =⟨ ! $ transp-cAP-glue d₁ pcAA |in-ctx cAB-to-cBB d₀ ⟩
              cAB-to-cBB d₀ (transport (λ p → codeAP (f (h d₀)) p) (glue (h d₁)) q[ pcAA ])
                =∎)
            (λ _ → prop-has-all-paths-↓ $ SetQuot-is-set _ _)

    abstract
      path : ∀ c₀ c₁
        →  CodeAAEquivCodeBA.eqv (f c₁) c₀
        == CodeABEquivCodeBB.eqv (g c₁) c₀
        [ (λ p → codeAP (f c₀) p ≃ codeBP (g c₀) p) ↓ glue c₁ ]
      path = SurjExt.ext
        (λ c₀ → Π-is-set λ c₁ → ↓-preserves-level $ ≃-is-set SetQuot-is-set SetQuot-is-set)
        h h-is-surj
        (λ d₀ → SurjExt.ext
          (λ c₁ → ↓-preserves-level $ ≃-is-set SetQuot-is-set SetQuot-is-set)
          h h-is-surj
          (λ d₁ → path-on-image d₀ d₁)
          (λ _ _ _ → prop-has-all-paths-↓ $ ↓-level $ ≃-is-set SetQuot-is-set SetQuot-is-set))
        (λ _ _ _ → prop-has-all-paths-↓ $ Π-is-prop λ _ → ↓-level $ ≃-is-set SetQuot-is-set SetQuot-is-set)

  cAP-equiv-cBP : ∀ c₀ p₁ → codeAP (f c₀) p₁ ≃ codeBP (g c₀) p₁
  cAP-equiv-cBP c₀ = Pushout-elim
    {P = λ p₁ → codeAP (f c₀) p₁ ≃ codeBP (g c₀) p₁}
    (λ a₁ → CodeAAEquivCodeBA.eqv a₁ c₀)
    (λ b₁ → CodeABEquivCodeBB.eqv b₁ c₀)
    (λ c₁ → EqvPAIdEqvPB.path c₀ c₁)

  module Code = PushoutRec
    {D = Pushout span → Type (lmax (lmax (lmax i j) k) l)}
    codeAP codeBP (λ c₀ → λ= $ ua ∘ cAP-equiv-cBP c₀)

  code : Pushout span → Pushout span → Type (lmax (lmax (lmax i j) k) l)
  code = Code.f

  abstract
    code-level : ∀ {p₀ p₁} → is-set (code p₀ p₁)
    code-level {p₀} {p₁} = Pushout-elim
      {P = λ p₀ → ∀ p₁ → is-set (code p₀ p₁)}
      (λ a₀ p₁ → codeAP-level {a₀} {p₁})
      (λ b₀ p₁ → codeBP-level {b₀} {p₁})
      (λ c₀ → prop-has-all-paths-↓ $ Π-is-prop λ p₁ → is-set-is-prop)
      p₀ p₁
  code-is-set = code-level

  abstract
    transp-cPA-glue : ∀ d₀ {a₁} (pcAA : precodeAA (f (h d₀)) a₁)
      → transport (λ p₀ → code p₀ (left a₁)) (glue (h d₀)) q[ pcAA  ] == q[ pcAA-to-pcBA d₀ pcAA ]
    transp-cPA-glue d₀ {a₁} pcAA =
      transport (λ p₀ → code p₀ (left a₁)) (glue (h d₀)) q[ pcAA  ]
        =⟨ ap (λ e → coe e q[ pcAA ])
            ( ap-∘ (_$ left a₁) code (glue (h d₀))
            ∙ ap (ap (_$ left a₁)) (Code.glue-β (h d₀))
            ∙ app=-β (ua ∘ cAP-equiv-cBP (h d₀)) (left a₁)
            ∙ ap ua (CodeAAEquivCodeBA.eqv-β a₁ d₀)) ⟩
      coe (ua (CodeAAEquivCodeBA.eqv-on-image a₁ d₀)) q[ pcAA  ]
        =⟨ coe-β (CodeAAEquivCodeBA.eqv-on-image a₁ d₀) q[ pcAA ] ⟩
      q[ pcAA-to-pcBA d₀ pcAA ]
        =∎

    transp-cPB-glue : ∀ d₀ {b₁} (pcAB : precodeAB (f (h d₀)) b₁)
      → transport (λ p₀ → code p₀ (right b₁)) (glue (h d₀)) q[ pcAB  ] == q[ pcAB-to-pcBB d₀ pcAB ]
    transp-cPB-glue d₀ {b₁} pcAB =
      transport (λ p₀ → code p₀ (right b₁)) (glue (h d₀)) q[ pcAB  ]
        =⟨ ap (λ e → coe e q[ pcAB ])
            ( ap-∘ (_$ right b₁) code (glue (h d₀))
            ∙ ap (ap (_$ right b₁)) (Code.glue-β (h d₀))
            ∙ app=-β (ua ∘ cAP-equiv-cBP (h d₀)) (right b₁)
            ∙ ap ua (CodeABEquivCodeBB.eqv-β b₁ d₀)) ⟩
      coe (ua (CodeABEquivCodeBB.eqv-on-image b₁ d₀)) q[ pcAB  ]
        =⟨ coe-β (CodeABEquivCodeBB.eqv-on-image b₁ d₀) q[ pcAB ] ⟩
      q[ pcAB-to-pcBB d₀ pcAB ]
        =∎

  -- code to path
  abstract
    pcAA-to-pcBA-to-path : ∀ d₀ {a₁} (pcAA : precodeAA _ a₁) →
         pcBA-to-path (pcAA-to-pcBA d₀ pcAA)
      == !₀ [ glue (h d₀) ] ∙₀' pcAA-to-path pcAA
    pcAB-to-pcBB-to-path : ∀ d₀ {b₁} (pcAB : precodeAB _ b₁) →
         pcBB-to-path (pcAB-to-pcBB d₀ pcAB)
      == !₀ [ glue (h d₀) ] ∙₀' pcAB-to-path pcAB
    pcAA-to-pcBA-to-path d₀ (pc-a pA) = ∙₀'-unit-l (!₀ [ glue (h d₀) ] ∙₀' ap₀ left pA)
    pcAA-to-pcBA-to-path d₀ (pc-aba d pc pA) =
        ap (λ p → p ∙₀' !₀ [ glue (h d) ] ∙₀' ap₀ left pA) (pcAB-to-pcBB-to-path d₀ pc)
      ∙ ∙₀'-assoc (!₀ [ glue (h d₀) ]) (pcAB-to-path pc) (!₀ [ glue (h d) ] ∙₀' ap₀ left pA)
    pcAB-to-pcBB-to-path d₀ (pc-aab d pc pB) =
        ap (λ p → p ∙₀' [ glue (h d) ] ∙₀' ap₀ right pB) (pcAA-to-pcBA-to-path d₀ pc)
      ∙ ∙₀'-assoc (!₀ [ glue (h d₀) ]) (pcAA-to-path pc) ([ glue (h d) ] ∙₀' ap₀ right pB)

  abstract
    decodeAA-is-decodeBA : ∀ {a₁} c₀ →
      decodeAA {f c₀} {a₁} == decodeBA {g c₀} {a₁}
      [ (λ p₀ → code p₀ (left a₁) → p₀ =₀ left a₁) ↓ glue c₀ ]
    decodeAA-is-decodeBA {a₁ = a₁} = SurjExt.ext
      (λ _ → ↓-preserves-level $ Π-is-set λ _ → Trunc-level) h h-is-surj
      (λ d₀ → ↓-→-from-transp $ λ= $ SetQuot-elim
        {P = λ cAA → transport (_=₀ left a₁) (glue (h d₀)) (decodeAA cAA)
                  == decodeBA (transport (λ p₀ → code p₀ (left a₁)) (glue (h d₀)) cAA)}
        (λ _ → =-preserves-set Trunc-level)
        (λ pcAA →
          transport (_=₀ left a₁) (glue (h d₀)) (pcAA-to-path pcAA)
            =⟨ transp₀-idf=₀cst [ glue (h d₀) ] (pcAA-to-path pcAA) ⟩
          !₀ [ glue (h d₀) ] ∙₀ pcAA-to-path pcAA
            =⟨ ∙₀=∙₀' [ ! (glue (h d₀)) ] (pcAA-to-path pcAA) ⟩
          !₀ [ glue (h d₀) ] ∙₀' pcAA-to-path pcAA
            =⟨ ! $ pcAA-to-pcBA-to-path d₀ pcAA ⟩
          pcBA-to-path (pcAA-to-pcBA d₀ pcAA)
            =⟨ ! $ ap (λ e → decodeBA (–> e q[ pcAA ])) (CodeAAEquivCodeBA.eqv-β a₁ d₀) ⟩
          decodeBA (–> (CodeAAEquivCodeBA.eqv a₁ (h d₀)) q[ pcAA ])
            =⟨ ! $ ap decodeBA (coe-β (CodeAAEquivCodeBA.eqv a₁ (h d₀)) q[ pcAA ]) ⟩
          decodeBA (coe (ua (CodeAAEquivCodeBA.eqv a₁ (h d₀))) q[ pcAA ])
            =⟨ ! $ ap (λ p → decodeBA (coe p q[ pcAA ])) (app=-β (ua ∘ cAP-equiv-cBP (h d₀)) (left a₁)) ⟩
          decodeBA (coe (app= (λ= λ p₁ → ua (cAP-equiv-cBP (h d₀) p₁)) (left a₁)) q[ pcAA ])
            =⟨ ! $ ap (λ p → decodeBA (coe (app= p (left a₁)) q[ pcAA ])) (Code.glue-β (h d₀)) ⟩
          decodeBA (coe (app= (ap code (glue (h d₀))) (left a₁)) q[ pcAA ])
            =⟨ ap (λ p → decodeBA (coe p q[ pcAA ])) (∘-ap (λ f₁ → f₁ (left a₁)) code (glue (h d₀))) ⟩
          decodeBA (transport (λ p₀ → code p₀ (left a₁)) (glue (h d₀)) q[ pcAA ])
            =∎)
        (λ _ → prop-has-all-paths-↓ $ Trunc-level {n = 0} _ _))
      (λ _ _ _ → prop-has-all-paths-↓ $ ↓-level $ Π-is-set λ _ → Trunc-level)

  abstract
    decodeAB-is-decodeBB : ∀ {b₁} c₀ →
      decodeAB {f c₀} {b₁} == decodeBB {g c₀} {b₁}
      [ (λ p₀ → code p₀ (right b₁) → p₀ =₀ right b₁) ↓ glue c₀ ]
    decodeAB-is-decodeBB {b₁ = b₁} = SurjExt.ext
      (λ _ → ↓-preserves-level $ Π-is-set λ _ → Trunc-level) h h-is-surj
      (λ d₀ → ↓-→-from-transp $ λ= $ SetQuot-elim
        {P = λ cAB → transport (_=₀ right b₁) (glue (h d₀)) (decodeAB cAB)
                  == decodeBB (transport (λ p₀ → code p₀ (right b₁)) (glue (h d₀)) cAB)}
        (λ _ → =-preserves-set Trunc-level)
        (λ pcAB →
          transport (_=₀ right b₁) (glue (h d₀)) (pcAB-to-path pcAB)
            =⟨ transp₀-idf=₀cst [ glue (h d₀) ] (pcAB-to-path pcAB) ⟩
          !₀ [ glue (h d₀) ] ∙₀ pcAB-to-path pcAB
            =⟨ ∙₀=∙₀' [ ! (glue (h d₀)) ] (pcAB-to-path pcAB) ⟩
          !₀ [ glue (h d₀) ] ∙₀' pcAB-to-path pcAB
            =⟨ ! $ pcAB-to-pcBB-to-path d₀ pcAB ⟩
          pcBB-to-path (pcAB-to-pcBB d₀ pcAB)
            =⟨ ! $ ap (λ e → decodeBB (–> e q[ pcAB ])) (CodeABEquivCodeBB.eqv-β b₁ d₀) ⟩
          decodeBB (–> (CodeABEquivCodeBB.eqv b₁ (h d₀)) q[ pcAB ])
            =⟨ ! $ ap decodeBB (coe-β (CodeABEquivCodeBB.eqv b₁ (h d₀)) q[ pcAB ]) ⟩
          decodeBB (coe (ua (CodeABEquivCodeBB.eqv b₁ (h d₀))) q[ pcAB ])
            =⟨ ! $ ap (λ p → decodeBB (coe p q[ pcAB ])) (app=-β (ua ∘ cAP-equiv-cBP (h d₀)) (right b₁)) ⟩
          decodeBB (coe (app= (λ= λ p₁ → ua (cAP-equiv-cBP (h d₀) p₁)) (right b₁)) q[ pcAB ])
            =⟨ ! $ ap (λ p → decodeBB (coe (app= p (right b₁)) q[ pcAB ])) (Code.glue-β (h d₀)) ⟩
          decodeBB (coe (app= (ap code (glue (h d₀))) (right b₁)) q[ pcAB ])
            =⟨ ap (λ p → decodeBB (coe p q[ pcAB ])) (∘-ap (λ f₁ → f₁ (right b₁)) code (glue (h d₀))) ⟩
          decodeBB (transport (λ p₀ → code p₀ (right b₁)) (glue (h d₀)) q[ pcAB ])
            =∎)
        (λ _ → prop-has-all-paths-↓ $ Trunc-level {n = 0} _ _))
      (λ _ _ _ → prop-has-all-paths-↓ $ ↓-level $ Π-is-set λ _ → Trunc-level)

  abstract
    decodeAP-is-decodeBP : ∀ c₀ p₁
      → decodeAP {f c₀} {p₁} == decodeBP {g c₀} {p₁}
        [ (λ p₀ → code p₀ p₁ → p₀ =₀ p₁) ↓ glue c₀ ]
    decodeAP-is-decodeBP c₀ = Pushout-elim
      {P = λ p₁ → decodeAP {f c₀} {p₁} == decodeBP {g c₀} {p₁}
        [ (λ p₀ → code p₀ p₁ → p₀ =₀ p₁) ↓ glue c₀ ]}
      (λ a₁ → decodeAA-is-decodeBA {a₁ = a₁} c₀)
      (λ b₁ → decodeAB-is-decodeBB {b₁ = b₁} c₀)
      (λ _ → prop-has-all-paths-↓ $ ↓-level $ Π-is-set λ _ → Trunc-level)

  decode : ∀ {p₀ p₁} → code p₀ p₁ → p₀ =₀ p₁
  decode {p₀} {p₁} = Pushout-elim
    {P = λ p₀ → code p₀ p₁ → p₀ =₀ p₁}
    (λ a₀ → decodeAP {a₀} {p₁})
    (λ b₀ → decodeBP {b₀} {p₁})
    (λ c₀ → decodeAP-is-decodeBP c₀ p₁)
    p₀


open import Nat
open import Prelude
open import List
open import core
open import contexts
open import typed-expansion
open import lemmas-freevars

module livelit-reasoning-principles where
  record reasoning-principles (Φ : livelitctx)
                              (Γ : tctx)
                              (a : livelitname)
                              (dm : iexp)
                              (τsplice : typ)
                              (êsplice : uexp)
                              (eresult : eexp)
                              (τresult : typ) : Set where
    field
      π   : livelitdef
      domain : dom (Φ ₁) a
      eexpanded : eexp
      esplice : eexp

      expanded-application-form : eresult == (eexpanded ·: τsplice ==> τresult) ∘ esplice
      expansion-typing          : (Γ ⊢ eresult => τresult) × (τresult == livelitdef.expansion-type π)
      responsibility            : Σ[ denc ∈ iexp ] (((livelitdef.expand π) ∘ dm) ⇓ denc × denc ↑ eexpanded)
      splice-typing             : Φ , Γ ⊢ êsplice ~~> esplice ⇐ τsplice × Γ ⊢ esplice <= τsplice
      context-independence      : free-vars (eexpanded ·: τsplice ==> τresult) == []

  livelit-reasoning-principles : ∀{Φ Γ a dm τsplice psplice eresult τresult u} →
        Φ , Γ ⊢ ＄ a ⟨ dm ⁏ (τsplice , psplice) :: [] ⟩[ u ] ~~> eresult ⇒ τresult →
        reasoning-principles Φ Γ a dm τsplice psplice eresult τresult
  livelit-reasoning-principles h@(SPEApLivelit {dm = dm} {π} {denc} {eexpanded} {esplice = esplice} x x₁ x₂ x₃ x₄ x₅ x₆ x₇) =
       record
         { π                         = π
         ; domain                    = _ , x₂
         ; eexpanded                 = eexpanded
         ; esplice                   = esplice
         ; expanded-application-form = refl
         ; expansion-typing          = typed-expansion-synth h , refl
         ; responsibility            = denc , x₄ , x₅
         ; splice-typing             = x₆ , typed-expansion-ana x₆
         ; context-independence      = ∅∈l→l==[] (λ x → fv-lemma-ana refl x₇)
         }


module noetherian where

open import sn-calculus
open import Esterel.Lang
open import Esterel.Lang.Properties
open import Esterel.Context
open import Esterel.Context.Properties
open import Esterel.Environment
open import Esterel.Variable.Signal as Signal using (Signal)
open import Esterel.Variable.Shared as SharedVar using (SharedVar)
open import Esterel.Variable.Sequential as SeqVar using (SeqVar)
open import Data.Nat as Nat
  using (ℕ ; zero ; suc ; _+_ ; _>_ ; _≤′_ ; _≤_ ; _*_ ; ≤′-refl)
open import Data.Nat.Properties
  using (s≤′s ; z≤′n ; m≤′m+n ; n≤′m+n ; ≤⇒pred≤)
open import Data.Nat.Properties.Simple using (+-comm ; +-assoc ; +-suc)
open import Data.MoreNatProp
open import Data.Sum as Sum using (inj₁ ; inj₂ ; _⊎_)
open import Data.Maybe using (just ; nothing)
open import Data.Product using (_×_ ; _,_ ; _,′_ ; proj₁)
open import Agda.Builtin.Equality using (refl ; _≡_)
open import Data.List using (List ; _∷_ ; [])
open import Data.Bool using (Bool ; true ; false)
open import Relation.Nullary.Decidable using (⌊_⌋)
open import Relation.Nullary
  using (Dec ; yes ; no ; ¬_)
open import Relation.Binary.PropositionalEquality
  using (sym ; inspect ; Reveal_·_is_ ; trans ; cong)
open import Data.Empty using (⊥-elim ; ⊥)

SM = SigMap.Map Signal.Status
ShM = ShrMap.Map (SharedVar.Status × ℕ)

is-unknownp : Signal.Status -> Bool
is-unknownp x = ⌊ x Signal.≟ₛₜ Signal.unknown ⌋

is-notreadyp : SharedVar.Status × ℕ -> Bool
is-notreadyp (SharedVar.ready , _) = false
is-notreadyp (SharedVar.new   , _) = true
is-notreadyp (SharedVar.old   , _) = true

is-notreadyp-status : SharedVar.Status -> Bool
is-notreadyp-status SharedVar.ready = false
is-notreadyp-status SharedVar.new = true
is-notreadyp-status SharedVar.old = true

is-notreadyp' : SharedVar.Status × ℕ -> Bool
is-notreadyp' x = is-notreadyp-status (proj₁ x)

hunch : ∀ x -> is-notreadyp x ≡ is-notreadyp' x
hunch (SharedVar.ready , proj₃) = refl
hunch (SharedVar.new , proj₃) = refl
hunch (SharedVar.old , proj₃) = refl

numstepsSig : SM -> ℕ
numstepsSig m = SigMap.count is-unknownp m

numstepsShr : ShM -> ℕ
numstepsShr m = ShrMap.count is-notreadyp m

numstepsθ : Env -> ℕ
numstepsθ (Θ sig₁ shr₁ var₁) = (numstepsSig sig₁) + (numstepsShr shr₁)

∥_∥s : Term -> ℕ
∥ nothin ∥s = 0
∥ pause ∥s = 0

{- one for stepping to ρ θ p
   one for (maybe) setting S to absent
   one for (maybe) this ρ mergeing with another one
   (same idea for `shared` and `var`) -}
∥ (signl S p) ∥s = suc (suc (suc (∥ p ∥s)))

∥ (present S ∣⇒ p ∣⇒ q) ∥s = suc (∥ p ∥s + ∥ q ∥s)
∥ (emit S) ∥s = 1
∥ (p ∥ q) ∥s = suc (∥ p ∥s + ∥ q ∥s)
∥ (loop p) ∥s = suc (suc (∥ p ∥s + ∥ p ∥s))
∥ (loopˢ p q) ∥s = suc (∥ p ∥s + ∥ q ∥s)
∥ (p >> q) ∥s = suc (∥ p ∥s + ∥ q ∥s)
∥ (suspend p S) ∥s = suc (∥ p ∥s)
∥ (trap p) ∥s = suc (∥ p ∥s)
∥ (exit x) ∥s = 0
∥ (shared s ≔ e in: p) ∥s = suc (suc (suc (∥ p ∥s)))
∥ (s ⇐ e) ∥s = 1
∥ (var x ≔ e in: p) ∥s = suc (suc (∥ p ∥s))
∥ (x ≔ e) ∥s = 1
∥ (if x ∣⇒ p ∣⇒ q) ∥s = suc (∥ p ∥s + ∥ q ∥s)
∥ (ρ⟨ θ , A ⟩· p) ∥s = suc (numstepsθ θ + ∥ p ∥s)

arith1 : ∀ a b c -> suc b ≤′ c -> suc (suc (a + b)) ≤′ suc (a + c)
arith1 a b c sucb≤′c rewrite sym (+-suc a b) = s≤′s (≤′+r {z = a} sucb≤′c)

arith2 : ∀ x y z -> suc x ≤′ y -> suc (suc (x + z)) ≤′ suc (y + z)
arith2 x y z sucx≤′y = s≤′s (≤′+l{z = z} sucx≤′y)

arith3 : ∀ x y z -> suc x ≤′ y -> suc (suc (z + x)) ≤′ suc (z + y)
arith3 x y z sucx≤′y rewrite sym (+-suc z x) = s≤′s (≤′+r {z = z} sucx≤′y)

arith4 : ∀ x y z -> suc (suc x + y + z) ≤′ suc (x + suc y + z)
arith4 x y z rewrite +-suc x y = s≤′s ≤′-refl

arith5 : ∀ x y -> suc x ≤′ y -> suc (suc (suc (x + x))) ≤′ suc (suc (y + y))
arith5 x y sucx≤′y rewrite sym (+-suc x x) =
  s≤′s (s≤′s (≤′+b x (suc x) y y (suc≤′⇒≤′ x y sucx≤′y)  sucx≤′y))

decompose∥∥s : ∀ E p -> ∥ E ⟦ p ⟧e ∥s ≡ ∥ E ⟦ nothin ⟧e ∥s + ∥ p ∥s
decompose∥∥s [] p = refl
decompose∥∥s (x ∷ E) p with decompose∥∥s E
decompose∥∥s (epar₁ q ∷ E) p | R
  rewrite R p
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ p ∥s  ∥ q ∥s
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ q ∥s  ∥ p ∥s
  | +-comm (∥ p ∥s) (∥ q ∥s)
  = cong suc refl
decompose∥∥s (epar₂ p ∷ E) p₁ | R
  rewrite R p₁
  | +-assoc ∥ p ∥s ∥ E ⟦ nothin ⟧e ∥s ∥ p₁ ∥s
  = cong suc refl
decompose∥∥s (eseq q ∷ E) p | R
  rewrite R p
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ p ∥s  ∥ q ∥s
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ q ∥s  ∥ p ∥s
  | +-comm (∥ p ∥s) (∥ q ∥s)
  = cong suc refl
decompose∥∥s (eloopˢ q ∷ E) p | R
  rewrite R p
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ p ∥s  ∥ q ∥s
  | +-assoc (∥ E ⟦ nothin ⟧e ∥s) ∥ q ∥s  ∥ p ∥s
  | +-comm (∥ p ∥s) (∥ q ∥s)
  = cong suc refl
decompose∥∥s (esuspend S ∷ E) p | R rewrite R p = refl
decompose∥∥s (etrap ∷ E) p | R rewrite R p = refl

eplug≤ : ∀ p q E ->
  ∥ p ∥s ≤′ ∥ q ∥s ->
  ∥ E ⟦ p ⟧e ∥s ≤′ ∥ E ⟦ q ⟧e ∥s
eplug≤ p q E ∥p∥s≤′∥q∥s
  rewrite decompose∥∥s E p | decompose∥∥s E q
  = ≤′+r{z = ∥ E ⟦ nothin ⟧e ∥s} ∥p∥s≤′∥q∥s

eplug< : ∀ p q E ->
  suc ∥ p ∥s ≤′ ∥ q ∥s ->
  suc (∥ (E ⟦ p ⟧e) ∥s) ≤′ (∥ (E ⟦ q ⟧e) ∥s)
eplug< p q E ∥p∥s<∥q∥s
  rewrite decompose∥∥s E p | decompose∥∥s E q
        | sym (+-suc ∥ E ⟦ nothin ⟧e ∥s  ∥ p ∥s)
  = ≤′+r{z = ∥ E ⟦ nothin ⟧e ∥s} ∥p∥s<∥q∥s

valuemax-norm : ∀ {p q} ->
  (donep : done p) -> (doneq : done q) ->
  (haltedporq : halted p ⊎ halted q) ->
  ∥ value-max donep doneq haltedporq ∥s ≤′ ∥ p ∥s + ∥ q ∥s
valuemax-norm (dhalted hnothin) (dhalted p/halted₁) _ = ≤′-refl
valuemax-norm (dhalted (hexit n)) (dhalted hnothin) _ = ≤′-refl
valuemax-norm (dhalted (hexit n)) (dhalted (hexit m)) _ = ≤′-refl
valuemax-norm (dhalted hnothin) (dpaused p/paused) _ = ≤′-refl
valuemax-norm{q = q} (dhalted (hexit n)) (dpaused p/paused) _ = m≤′m+n 0 ∥ q ∥s
valuemax-norm{p = p} (dpaused p/paused) (dhalted hnothin) _ = m≤′m+n ∥ p ∥s 0
valuemax-norm (dpaused p/paused) (dhalted (hexit n)) _ = z≤′n
valuemax-norm (dpaused p/paused) (dpaused p/paused₁) (inj₁ x) =
  ⊥-elim (halted-paused-disjoint x p/paused)
valuemax-norm (dpaused p/paused) (dpaused p/paused₁) (inj₂ y) =
  ⊥-elim (halted-paused-disjoint y p/paused₁)

halted-norm : ∀ {p} -> (haltedp : halted p) ->  ∥ ↓ haltedp ∥s ≡ 0
halted-norm hnothin = refl
halted-norm (hexit zero) = refl
halted-norm (hexit (suc n)) = refl

noetherian₁ :
  ∀ {p q} ->
  p sn⟶₁ q ->
  (suc (∥ q ∥s)) ≤′ (∥ p ∥s)
noetherian₁ (rpar-done-right{p}{q} haltedp doneq) =
  s≤′s (valuemax-norm (dhalted haltedp) doneq (inj₁ haltedp))
noetherian₁ (rpar-done-left{p}{q} donep haltedq) =
  s≤′s (valuemax-norm donep (dhalted haltedq) (inj₂ haltedq))
noetherian₁ (ris-present{θ}{S = S}{p = p}{q = q}{E = E} S∈ x x₁) with unplug x₁
... | refl =
  arith1 (numstepsθ θ)
         ∥ E ⟦ p ⟧e ∥s
         ∥ E ⟦ present S ∣⇒ p ∣⇒ q ⟧e ∥s
         (eplug< p (present S ∣⇒ p ∣⇒ q) E (s≤′s (m≤′m+n ∥ p ∥s ∥ q ∥s)))

noetherian₁ (ris-absent{θ}{S}{p = p}{q = q}{E = E} S∈ x x₁) with unplug x₁
... | refl =
  arith1 (numstepsθ θ)
         ∥ E ⟦ q ⟧e ∥s
         ∥ E ⟦ present S ∣⇒ p ∣⇒ q ⟧e ∥s
         (eplug< q (present S ∣⇒ p ∣⇒ q) E (s≤′s (n≤′m+n ∥ p ∥s ∥ q ∥s)))

noetherian₁ (remit {θ} {_} {S} {E} S∈ ¬S≡a x) with unplug x
noetherian₁ (remit {θ} {_} {S} {E} S∈ ¬S≡a x) | refl with
    is-unknownp (SigMap.lookup{k = S} (sig θ) S∈)
  | inspect is-unknownp (SigMap.lookup{k = S} (sig θ) S∈)
  | SigMap.lookup{k = S} (sig θ) S∈
  | inspect (SigMap.lookup{k = S} (sig θ)) S∈
... | true  | Reveal_·_is_.[ eq1 ] | Signal.present | Reveal_·_is_.[ eq ]
  rewrite eq = ⊥-elim (false-not-true eq1) where
  false-not-true : false ≡ true -> ⊥
  false-not-true ()
... | false | Reveal_·_is_.[ eq1 ] | Signal.absent  | Reveal_·_is_.[ eq ]
  = ⊥-elim (¬S≡a refl)
... | true  | Reveal_·_is_.[ eq1 ] | Signal.absent  | Reveal_·_is_.[ eq ]
  = ⊥-elim (¬S≡a refl)
... | false | Reveal_·_is_.[ eq1 ] | Signal.unknown | Reveal_·_is_.[ eq ]
  rewrite eq = ⊥-elim (true-not-false eq1) where
  true-not-false : true ≡ false -> ⊥
  true-not-false ()
noetherian₁ (remit {θ} {.(E ⟦ emit S ⟧e)} {S} {E} S∈ ¬S≡a x)
   | refl | false | Reveal_·_is_.[ eq1 ] | Signal.present | Reveal_·_is_.[ eq ]
   with sym (SigMap.getput-m S (sig θ) S∈)
... | updateθ-S-to-present≡θ rewrite eq | updateθ-S-to-present≡θ =
  arith1 (numstepsθ θ)
         ∥ E ⟦ nothin ⟧e ∥s
         ∥ E ⟦ emit S ⟧e ∥s
         (eplug< nothin (emit S) E ≤′-refl)
noetherian₁ (remit {θ} {.(E ⟦ emit S ⟧e)} {S} {E} S∈ ¬S≡a x)
  | refl | true  | Reveal_·_is_.[ eq1 ] | Signal.unknown | Reveal_·_is_.[ eq ]
  rewrite SigMap.change-one-count-goes-down
   (sig θ) is-unknownp
   S S∈ eq1 Signal.present refl =
   s≤′s (s≤′s (≤′+r {z = numstepsθ (set-sig{S} θ S∈ Signal.present)}
                    (eplug≤ nothin (emit S) E (Nat.≤′-step ≤′-refl))))

noetherian₁ rloop-unroll = ≤′-refl
noetherian₁ rseq-done = ≤′-refl
noetherian₁ (rseq-exit{q}) = s≤′s z≤′n
noetherian₁ (rloopˢ-exit{q}) = s≤′s z≤′n
noetherian₁ (rsuspend-done x) = ≤′-refl
noetherian₁ (rtrap-done{p} haltedp)
  rewrite halted-norm haltedp =
  s≤′s z≤′n
noetherian₁ (rraise-signal{p}{S}) = ans where
  oneunknown≡1 : ∀ S ->
     numstepsθ (Θ SigMap.[ S ↦ Signal.unknown ] [] []) ≡ 1
  oneunknown≡1 (zero Signal.ₛ) = refl
  oneunknown≡1 (suc n Signal.ₛ) = oneunknown≡1 (n Signal.ₛ)

  ans : suc (suc (numstepsθ (Θ SigMap.[ S ↦ Signal.unknown ] [] []) +  ∥ p ∥s))
     ≤′ suc (suc (suc ∥ p ∥s))
  ans rewrite oneunknown≡1 S = ≤′-refl

noetherian₁ (rraise-shared{θ}{_}{s}{e}{p}{E} e' x) with unplug x
... | refl =
  arith1 (numstepsθ θ)
         ∥ E ⟦ ρ⟨ [s,δe]-env  s (δ e') , WAIT ⟩· p ⟧e ∥s
         ∥ E ⟦ shared s ≔ e in: p ⟧e ∥s
         (eplug< (ρ⟨ [s,δe]-env s (δ e') , WAIT ⟩· p)
                 (shared s ≔ e in: p) E newθ≡1) where

   silly : ∀ s -> numstepsθ ([s,δe]-env s (δ e')) ≡ 1
   silly (zero SharedVar.ₛₕ) = refl
   silly (suc x₁ SharedVar.ₛₕ) = silly (x₁ SharedVar.ₛₕ)

   newθ≡1 : suc (suc (numstepsθ ([s,δe]-env s (δ e')) + ∥ p ∥s)) ≤′
            suc (suc (suc ∥ p ∥s))
   newθ≡1 rewrite silly s = ≤′-refl

noetherian₁ (rset-shared-value-old{θ}{_}{s}{e}{E} e' s∈ θ[s]≡old x) with unplug x
... | refl  = ans where

  snotready : is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈) ≡ true
  snotready rewrite hunch (ShrMap.lookup{k = s} (shr θ) s∈)
            | θ[s]≡old = refl

  θsame : numstepsθ (set-shr{s} θ s∈ (SharedVar.new) (δ e'))
        ≡ numstepsθ θ
  θsame rewrite
     ShrMap.change-nothing-count-stays-same
       (shr θ) is-notreadyp s s∈
       (SharedVar.new , δ e') snotready = refl

  ans : suc ∥ (ρ⟨ (set-shr{s} θ s∈ (SharedVar.new) (δ e')) , GO ⟩·
                 E ⟦ nothin ⟧e) ∥s ≤′
        ∥ ρ⟨ θ , GO ⟩· E ⟦ s ⇐ e ⟧e ∥s
  ans rewrite θsame =
    arith1 (numstepsθ θ)
            ∥ E ⟦ nothin ⟧e ∥s
            ∥ E ⟦ s ⇐ e ⟧e ∥s
            (eplug< nothin (s ⇐ e) E ≤′-refl)

noetherian₁ (rset-shared-value-new{θ}{_}{s}{e}{E} e' s∈ θ[s]≡new x) with unplug x
... | refl = ans where

  snotready : is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈) ≡ true
  snotready rewrite hunch (ShrMap.lookup{k = s} (shr θ) s∈)
            | θ[s]≡new = refl

  θsame : numstepsθ (set-shr{s} θ s∈ (SharedVar.new) (shr-vals{s} θ s∈ + δ e'))
        ≡ numstepsθ θ
  θsame rewrite
     ShrMap.change-nothing-count-stays-same
       (shr θ) is-notreadyp s s∈
       (SharedVar.new , shr-vals{s} θ s∈ + δ e') snotready = refl

  ans : suc ∥ (ρ⟨ (set-shr{s} θ s∈ (SharedVar.new) (shr-vals{s} θ s∈ + δ e')) , GO ⟩·
                 E ⟦ nothin ⟧e) ∥s ≤′
        ∥ ρ⟨ θ , GO ⟩· E ⟦ s ⇐ e ⟧e ∥s
  ans rewrite θsame =
     arith1 (numstepsθ θ)
            ∥ E ⟦ nothin ⟧e ∥s
            ∥ E ⟦ s ⇐ e ⟧e ∥s
            (eplug< nothin (s ⇐ e) E ≤′-refl)

noetherian₁ (rraise-var{θ}{r}{x}{p}{e}{E} e' x₁) with unplug x₁
... | refl =
  arith1 (numstepsθ θ)
         ∥ E ⟦ ρ⟨ [x,δe]-env x (δ e') , WAIT ⟩· p ⟧e ∥s
         ∥ E ⟦ var x ≔ e in: p ⟧e ∥s
         (eplug< (ρ⟨ [x,δe]-env x (δ e') , WAIT ⟩· p)
                 (var x ≔ e in: p)
                 E
                 ≤′-refl)
noetherian₁ (rset-var{θ}{_}{x}{e}{E} x∈ e' x₁) with unplug x₁
... | refl =
  arith1 (numstepsθ θ)
          ∥ E ⟦ nothin ⟧e ∥s
          ∥ E ⟦ x ≔ e ⟧e ∥s
          (eplug< nothin (x ≔ e) E ≤′-refl)
noetherian₁ (rif-false{θ}{p = p}{q = q}{x = x}{E = E} x∈ x₁ x₂) with unplug x₂
... | refl = arith1 (numstepsθ θ)
                    ∥ E ⟦ q ⟧e ∥s
                    ∥ E ⟦ if x ∣⇒ p ∣⇒ q ⟧e ∥s
                    (eplug< q (if x ∣⇒ p ∣⇒ q) E (s≤′s (n≤′m+n ∥ p ∥s ∥ q ∥s)))
noetherian₁ (rif-true{θ}{p = p}{q = q}{x = x}{E = E} x∈ x₁ x₂) with unplug x₂
... | refl = arith1 (numstepsθ θ)
                    ∥ E ⟦ p ⟧e ∥s
                    ∥ E ⟦ if x ∣⇒ p ∣⇒ q ⟧e ∥s
                    (eplug< p (if x ∣⇒ p ∣⇒ q) E (s≤′s (m≤′m+n ∥ p ∥s ∥ q ∥s)))
noetherian₁ (rabsence{θ = θ}{S = S} S∈ θ[S]≡unk S̸S∉Canθ)
    with is-unknownp (SigMap.lookup{k = S} (sig θ) S∈)
       | inspect is-unknownp (SigMap.lookup{k = S} (sig θ) S∈)
... | false | Reveal_·_is_.[ eq ] rewrite θ[S]≡unk
  = ⊥-elim (absurd eq) where
      absurd : true ≡ false -> ⊥
      absurd ()
... | true  | Reveal_·_is_.[ eq ] with
   SigMap.change-one-count-goes-down
     (sig θ) is-unknownp
     S S∈ eq Signal.absent refl
... | thing rewrite thing = ≤′-refl

noetherian₁ (rreadyness {θ}{p}{s} s∈ (inj₁ θs=old) s∉Canθ)
  with is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈)
     | inspect is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈)
... | false | Reveal_·_is_.[ eq ]
  rewrite hunch (ShrMap.lookup{k = s} (shr θ) s∈) |  θs=old
  = ⊥-elim (absurd eq) where
       absurd : true ≡ false -> ⊥
       absurd ()
... | true  | Reveal_·_is_.[ eq ] rewrite θs=old with
  ShrMap.change-one-count-goes-down
     (shr θ) is-notreadyp
     s s∈ eq (SharedVar.ready ,′ shr-vals{s} θ s∈) refl
... | thing rewrite thing =
    arith4 (numstepsSig (sig θ))
           (numstepsShr (shr (set-shr{s} θ s∈ (SharedVar.ready)
                                         (shr-vals{s} θ s∈))))
           (∥ p ∥s)

noetherian₁ (rreadyness {θ}{p}{s} s∈ (inj₂ θs=new) s∉Canθ)
  with is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈)
     | inspect is-notreadyp (ShrMap.lookup{k = s} (shr θ) s∈)
... | false | Reveal_·_is_.[ eq ]
  rewrite hunch (ShrMap.lookup{k = s} (shr θ) s∈) |  θs=new
  = ⊥-elim (absurd eq) where
       absurd : true ≡ false -> ⊥
       absurd ()
... | true  | Reveal_·_is_.[ eq ] rewrite θs=new with
  ShrMap.change-one-count-goes-down
     (shr θ) is-notreadyp
     s s∈ eq (SharedVar.ready ,′ shr-vals{s} θ s∈) refl
... | thing rewrite thing =
    arith4 (numstepsSig (sig θ))
           (numstepsShr (shr (set-shr{s} θ s∈ (SharedVar.ready)
                                         (shr-vals{s} θ s∈))))
           (∥ p ∥s)

noetherian₁ (rmerge{θ₁}{θ₂}{_}{p}{E}{A₁}{A₂} x) with unplug x
... | refl rewrite decompose∥∥s E (ρ⟨ θ₂ , A₂ ⟩· p)
                 | decompose∥∥s E p
  = s≤′s
    (rearrange-things
      (numstepsSig (SigMap.union (sig θ₁) (sig θ₂)))
      (numstepsShr (ShrMap.union (shr θ₁) (shr θ₂)))
      ∥ E ⟦ nothin ⟧e ∥s
      ∥ p ∥s
      (numstepsSig (sig θ₁))
      (numstepsShr (shr θ₁))
      (numstepsSig (sig θ₂))
      (numstepsShr (shr θ₂))
      (≤′+b (numstepsSig (SigMap.union (sig θ₁) (sig θ₂)))
            (numstepsShr (ShrMap.union (shr θ₁) (shr θ₂)))
            (numstepsSig (sig θ₁) + numstepsSig (sig θ₂))
            (numstepsShr (shr θ₁) + numstepsShr (shr θ₂))
            (SigMap.count-merge≤′sum-count
             (sig θ₁) (sig θ₂) is-unknownp)
            (ShrMap.count-merge≤′sum-count
             (shr θ₁) (shr θ₂) is-notreadyp))) where

  rearrange-more :
    ∀ ab cd a b c d ->
      ab + cd ≤′ a + b + (c + d) ->
      ab + cd ≤′ a + c + (b + d)
  rearrange-more ab cd a b c d simple rewrite
     sym (+-assoc (a + c) b d)
   | +-assoc a c b
   | +-comm c b
   | sym (+-assoc a b c)
   | +-assoc (a + b) c d
   = simple

  rearrange-things : ∀ ab cd n p a c b d ->
    ab + cd ≤′ (a + b) + (c + d) ->
    suc (ab + cd + (n + p)) ≤′ a + c + (n + (suc (b + d + p)))
  rearrange-things ab cd n p a c b d simple rewrite
     +-suc n (b + d + p)
   | +-suc (a + c) (n + (b + d + p))
   | +-comm (b + d) p
   | sym (+-assoc n p (b + d))
   | +-comm (n + p) (b + d)
   | sym (+-assoc (a + c) (b + d) (n + p))
   = s≤′s (≤′+l {z = n + p} (rearrange-more ab cd a b c d simple))

csteps : ∀ {p q C C⟦p⟧ C⟦q⟧} ->
  (suc (∥ q ∥s)) ≤′ (∥ p ∥s) ->
  C⟦p⟧ ≐ C ⟦ p ⟧c ->
  C⟦q⟧ ≐ C ⟦ q ⟧c ->
  (suc ∥ C⟦q⟧ ∥s) ≤′ ∥ C⟦p⟧ ∥s
csteps sq≤p dchole dchole = sq≤p
csteps sq≤p (dcpar₁{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcpar₁{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith2 ∥ qp ∥s ∥ pp ∥s  ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcpar₂{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcpar₂{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith1 ∥ qp ∥s ∥ qq ∥s  ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcseq₁{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcseq₁{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith2 ∥ qp ∥s ∥ pp ∥s ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcseq₂{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcseq₂{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith1 ∥ pp ∥s ∥ qq ∥s ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcsuspend{p} C⟦p⟧=C⟦p⟧c) (dcsuspend{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = s≤′s sucnsp≰sp₁
csteps sq≤p (dctrap{p} C⟦p⟧=C⟦p⟧c) (dctrap{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = s≤′s sucnsp≰sp₁
csteps sq≤p (dcsignl{p} C⟦p⟧=C⟦p⟧c) (dcsignl{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = s≤′s (s≤′s (s≤′s sucnsp≰sp₁))
csteps sq≤p (dcpresent₁{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcpresent₁{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith2 ∥ qp ∥s  ∥ pp ∥s  ∥ pq ∥s  sucnsp≰sp₁
csteps sq≤p (dcpresent₂{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcpresent₂{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith1 ∥ pp ∥s ∥ qq ∥s ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcloop{p} C⟦p⟧=C⟦p⟧c) (dcloop{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith5 ∥ q ∥s ∥ p ∥s sucnsp≰sp₁
csteps sq≤p (dcloopˢ₁{pp}{pq} C⟦p⟧=C⟦p⟧c) (dcloopˢ₁{qp}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ =  arith2  ∥ qp ∥s  ∥ pp ∥s  ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcloopˢ₂{pp}{pq} C⟦p⟧=C⟦p⟧c) (dcloopˢ₂{qp}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith1 ∥ pp ∥s  ∥ qq ∥s ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcshared{p} C⟦p⟧=C⟦p⟧c) (dcshared{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = s≤′s (s≤′s (s≤′s sucnsp≰sp₁))
csteps sq≤p (dcvar{p} C⟦p⟧=C⟦p⟧c) (dcvar{q} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = s≤′s (s≤′s sucnsp≰sp₁)
csteps sq≤p (dcif₁{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcif₁{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith2 ∥ qp ∥s  ∥ pp ∥s  ∥ pq ∥s  sucnsp≰sp₁
csteps sq≤p (dcif₂{pp}{pr}{pq} C⟦p⟧=C⟦p⟧c) (dcif₂{qp}{qr}{qq} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith1 ∥ pp ∥s  ∥ qq ∥s  ∥ pq ∥s sucnsp≰sp₁
csteps sq≤p (dcenv{p = p1}{θ = θ} C⟦p⟧=C⟦p⟧c) (dcenv{p = p2} C⟦q⟧=C⟦q⟧c)
  with csteps sq≤p C⟦p⟧=C⟦p⟧c C⟦q⟧=C⟦q⟧c
... | sucnsp≰sp₁ = arith3  ∥ p2 ∥s  ∥ p1 ∥s (numstepsθ θ) sucnsp≰sp₁

noetherian : ∀ {p q} ->
  p sn⟶ q ->
  (suc (∥ q ∥s)) ≤′ (∥ p ∥s)
noetherian (rcontext C dc pinsn⟶₁qin) =
  csteps (noetherian₁ pinsn⟶₁qin) dc Crefl


module lob--2015-06-14--III where

data ℕ : Set where
  zero : ℕ
  suc  : ℕ → ℕ

{-# BUILTIN NATURAL ℕ    #-}

record Σ {X : Set} (F : X → Set) : Set where
  constructor _,_
  field fst : X
        snd : F fst

data ⊤ : Set where
  unit : ⊤

data Tree (X : Set) : Set where
  br : Tree X → Tree X → Tree X
  leaf : X → Tree X

indtree : {X : Set} → (P : Tree X → Set)
        → ((s t : Tree X) → P s → P t → P (br s t))
        → ((x : X) → P (leaf x))
        → (t : Tree X) → P t
indtree P b l (br s t) = b s t (indtree P b l s) (indtree P b l t)
indtree P b l (leaf x) = l x

data Preterm : Set where
  type : ℕ → Preterm
  pi : Preterm → Preterm → Preterm
  sig : Preterm → Preterm → Preterm
  bot : Preterm
  top : Preterm
  unit : Preterm
  var : ℕ → Preterm
  lam : Preterm → Preterm → Preterm
  app : Preterm → Preterm → Preterm
  tree : Preterm → Preterm
  br : Preterm → Preterm → Preterm → Preterm
  leaf : Preterm → Preterm → Preterm
  ind : Preterm → Preterm → Preterm → Preterm → Preterm → Preterm

‘λ→’ = lam

data _::_ : Preterm → Preterm → Set where
  ::type : (ℓ : ℕ) → type ℓ :: type (suc ℓ)
  -- ...

□ : Preterm → Set
□ T = Σ \(t : Preterm) → t :: T

num : ℕ → Preterm
num 0 = leaf top unit
num (suc n) = br top unit (num n)

data List (X : Set) : Set where
  nil : List X
  _,_ : X → List X → List X

infixr 1 _,_

list : List Preterm → Preterm
list nil = leaf top unit
list (x , xs) = br top x (list xs)

cons : ℕ → List Preterm → Preterm
cons n xs = br top (num n) (list xs)

‘type’ : Preterm → Preterm
‘type’ n = cons 0 (n , nil)

‘pi’ : Preterm → Preterm → Preterm
‘pi’ x f = cons 1 (x , f , nil)

‘sig’ : Preterm → Preterm → Preterm
‘sig’ x f = cons 2 (x , f , nil)

‘bot’ : Preterm
‘bot’ = cons 3 nil

‘top’ : Preterm
‘top’ = cons 4 nil

‘unit’ : Preterm
‘unit’ = cons 5 nil

‘var’ : Preterm → Preterm
‘var’ n = cons 6 (n , nil)

‘lam’ : Preterm → Preterm → Preterm
‘lam’ t b = cons 7 (t , b , nil)

‘app’ : Preterm → Preterm → Preterm
‘app’ f x = cons 8 (f , x , nil)

‘tree’ : Preterm → Preterm
‘tree’ x = cons 9 (x , nil)

‘br’ : Preterm → Preterm → Preterm → Preterm
‘br’ x l r = cons 10 (x , l , r , nil)

‘leaf’ : Preterm → Preterm → Preterm
‘leaf’ x y = cons 11 (x , y , nil)

‘ind’ : Preterm → Preterm → Preterm → Preterm → Preterm → Preterm
‘ind’ x p b l t = cons 12 (x , p , b , l , t , nil)

‘Preterm’ : Preterm
‘Preterm’ = tree top

‘Preterm’::type₀ : ‘Preterm’ :: type 0
‘Preterm’::type₀ = {!!}

‘□’ : Preterm
‘□’ = {!!}

_‘→’_ : Preterm → Preterm → Preterm
A ‘→’ B = pi A {!!}

quot : Preterm → Preterm
quot (type n) = ‘type’ (num n)
quot (pi x f) = ‘pi’ (quot x) (quot f)
quot (sig x f) = ‘sig’ (quot x) (quot f)
quot bot = ‘bot’
quot top = ‘top’
quot unit = ‘unit’
quot (var n) = ‘var’ (num n)
quot (lam x b) = ‘lam’ (quot x) (quot b)
quot (app f x) = ‘app’ (quot f) (quot x)
quot (tree x) = ‘tree’ (quot x)
quot (br x l r) = ‘br’ (quot x) (quot l) (quot r)
quot (leaf x y) = ‘leaf’ (quot x) (quot y)
quot (ind x p b l t) = ‘ind’ (quot x) (quot p) (quot b) (quot l) (quot t)

‘quot’ : Preterm
‘quot’ = {!!}

‘‘→’’ : Preterm
‘‘→’’ = {!!}

⌜_⌝ = quot

_‘’_ = app

infixl 1 _‘’_

postulate
  X        : Set
  ‘X’       : Preterm
  ‘X’::type₀ : ‘X’ :: type 0

App : {‘A’ ‘B’ : Preterm}
      → □ (‘A’ ‘→’ ‘B’)
      → □ ‘A’
      → □ ‘B’
App □‘A→B’ □‘A’ = {!!}

‘App’ : Preterm
‘App’ = {!!}

Quot : {‘T’ : Preterm}
       → □ ‘T’
       → □ (‘□’ ‘’ ⌜ ‘T’ ⌝)
Quot □T = {!!}

‘Quot’ : Preterm
‘Quot’ = {!!}

‘L₀’ : Preterm
‘L₀’ = (‘λ→’ ‘Preterm’ ((‘app’ ⌜ ‘□’ ⌝ (‘app’ (var 0) (‘quot’ ‘’ (var 0)))) ‘’ ‘‘→’’ ‘’ ⌜ ‘X’ ⌝ ))

L : Preterm
L = (λ (h : Preterm) → (app ‘□’ (app h (⌜ h ⌝))) ‘→’ ‘X’) ‘L₀’

‘L’ = app ‘L₀’ ⌜ ‘L₀’ ⌝

Conv : □ (‘□’ ‘’ ⌜ L ⌝) → □ (‘□’ ‘’ ‘L’)
Conv = {!!}

‘Conv’ : Preterm
‘Conv’ = {!!}

postulate
  f    : □ ‘X’ → X
  ‘f’  : Preterm
  d‘f’ : ‘f’ :: (app ‘□’ (quot ‘X’) ‘→’ ‘X’)

y : X
y = (λ (ℓ : □ L) → f (App ℓ (Conv (Quot ℓ))))
      ( ‘λ→’ (‘□’ ‘’ ‘L’) (‘f’ ‘’ (‘App’ ‘’ (var 0) ‘’ (‘Conv’ ‘’ (‘Quot’ ‘’ var 0))))
      , {!!})


------------------------------------------------------------------------
-- The Agda standard library
--
-- Core definitions for Functions
------------------------------------------------------------------------

-- The contents of this file should usually be accessed from `Function`.

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

module Function.Core where

------------------------------------------------------------------------
-- Types

Fun₁ : ∀ {a} → Set a → Set a
Fun₁ A = A → A

Fun₂ : ∀ {a} → Set a → Set a
Fun₂ A = A → A → A


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

module Music where

open import Data.Nat     using (ℕ; zero; suc; _+_; _*_)
open import Data.Integer using (ℤ; +_)
open import Data.List    using (List; foldr; []; _∷_; reverse)
open import Data.Product using (_×_; _,_)
open import Data.Vec     using (Vec; []; _∷_; replicate; concat; map; zipWith; toList; _++_; sum; foldr₁)
open import Function     using (_∘_)

open import Note
open import Pitch
open import Interval

-- A point in the music grid, which can either be a tone,
-- a continuation of a previous tone, or a rest.
data Point : Set where
  tone : Pitch → Point
  hold : Pitch → Point
  rest : Point

data Melody (n : ℕ) : Set where
  melody : Vec Point n → Melody n

unmelody : {n : ℕ} → Melody n → Vec Point n
unmelody (melody ps) = ps

note→melody : (n : Note) → Melody (noteDuration n)
note→melody (tone (duration zero)    p) = melody []
note→melody (tone (duration (suc d)) p) = melody (tone p ∷ replicate (hold p))
note→melody (rest _)                    = melody (replicate rest)

-- Assumes melody is well-formed in that a held note has the
-- same pitch as the note before it.
-- Does not consolidate rests.
melody→notes : {n : ℕ} → Melody n → List Note
melody→notes (melody m) = (reverse ∘ mn 0 ∘ reverse ∘ toList) m
  where mn : ℕ → List Point → List Note -- c is the number of held points
        mn c []            = []
        mn c (tone p ∷ ps) = tone (duration (suc c)) p ∷ mn 0 ps
        mn c (hold _ ∷ ps) = mn (suc c) ps
        mn c (rest ∷ ps)   = rest (duration 1) ∷ mn 0 ps

pitches→melody : {n : ℕ} → (d : Duration) → (ps : Vec Pitch n) → Melody (n * unduration d)
pitches→melody d ps = melody (concat (map (unmelody ∘ note→melody ∘ tone d) ps))

transposePoint : ℤ → Point → Point
transposePoint k (tone p) = tone (transposePitch k p)
transposePoint k (hold p) = hold (transposePitch k p)
transposePoint k rest     = rest

transposeMelody : {n : ℕ} → ℤ → Melody n → Melody n
transposeMelody k = melody ∘ map (transposePoint k) ∘ unmelody

data Chord (n : ℕ) : Set where
  chord : Vec Point n → Chord n

unchord : {n : ℕ} → Chord n → Vec Point n
unchord (chord ps) = ps

-- We represent music as a v × d grid where v is the number of voices and d is the duration.
-- The primary representation is as parallel melodies (counterpoint).
data Counterpoint (v : ℕ) (d : ℕ): Set where
  cp : Vec (Melody d) v → Counterpoint v d

uncp : {v d : ℕ} → Counterpoint v d → Vec (Melody d) v
uncp (cp m) = m

-- An alternative representation of music is as a series of chords (harmonic progression).
data Harmony (v : ℕ) (d : ℕ): Set where
  harmony : Vec (Chord v) d → Harmony v d

unharmony : {v d : ℕ} → Harmony v d → Vec (Chord v) d
unharmony (harmony h) = h

pitches→harmony : {n : ℕ} (d : Duration) → (ps : Vec Pitch n) → Harmony n (unduration d)
pitches→harmony (duration zero)    ps = harmony []
pitches→harmony (duration (suc d)) ps = harmony (chord (map tone ps) ∷ replicate (chord (map hold ps)))

pitchPair→Harmony : (d : Duration) → PitchPair → Harmony 2 (unduration d)
pitchPair→Harmony d (p , q) = pitches→harmony d (p ∷ q ∷ [])

pitchInterval→Harmony : (d : Duration) → PitchInterval → Harmony 2 (unduration d)
pitchInterval→Harmony d = pitchPair→Harmony d ∘ pitchIntervalToPitchPair

{-
pitchIntervalsToCounterpoint : PitchInterval → Counterpoint
pitchIntervalsToCounterpoint = pitchPairToCounterpoint ∘ pitchIntervalToPitchPair
-}

addEmptyVoice : {v d : ℕ} → Harmony v d → Harmony (suc v) d
addEmptyVoice (harmony h) = harmony (map (chord ∘ (rest ∷_) ∘ unchord) h)

infixl 5 _+H+_
_+H+_ : {v d d' : ℕ} → Harmony v d → Harmony v d' → Harmony v (d + d')
h +H+ h' = harmony (unharmony h ++ unharmony h')

foldIntoHarmony : {k n : ℕ} (ds : Vec Duration (suc k)) → (pss : Vec (Vec Pitch n) (suc k)) → Harmony n (foldr₁ _+_ (map unduration ds))
foldIntoHarmony (d ∷ [])      (ps ∷ [])        = pitches→harmony d ps
foldIntoHarmony (d ∷ d' ∷ ds) (ps ∷ ps' ∷ pss) = (pitches→harmony d ps) +H+ (foldIntoHarmony (d' ∷ ds) (ps' ∷ pss))

-- matrix transposition
mtranspose : {A : Set}{m n : ℕ} → Vec (Vec A n) m → Vec (Vec A m) n
mtranspose []         = replicate []
mtranspose (xs ∷ xss) = zipWith _∷_ xs (mtranspose xss)

counterpoint→harmony : {v d : ℕ} → Counterpoint v d → Harmony v d
counterpoint→harmony = harmony ∘ map chord ∘ mtranspose ∘ map unmelody ∘ uncp

harmony→counterpoint : {v d : ℕ} → Harmony v d → Counterpoint v d
harmony→counterpoint = cp ∘ map melody ∘ mtranspose ∘ map unchord ∘ unharmony