{- Byzantine Fault Tolerant Consensus Verification in Agda, version 0.9.

   Copyright (c) 2020, 2021, Oracle and/or its affiliates.
   Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.oracle.com/licenses/upl
-}
open import LibraBFT.Prelude
open import LibraBFT.Lemmas
open import LibraBFT.Base.Types
open import LibraBFT.Abstract.Types.EpochConfig
open        WithAbsVote

-- This module defines abstract records (the initial or "genesis" record, blocks, and quorum
-- certificates), along with related definitions and properties.

module LibraBFT.Abstract.Records
    (UID    : Set)
    (_≟UID_ : (u₀ u₁ : UID) → Dec (u₀ ≡ u₁)) -- Needed to prove ≟Block and ≈?QC
    (NodeId : Set)
    (𝓔 : EpochConfig UID NodeId)
    (𝓥 : VoteEvidence UID NodeId 𝓔)
    where
  open import LibraBFT.Abstract.Types UID NodeId
  open        EpochConfig 𝓔

  -- Abstract blocks do /not/ need to carry the state hash. Since the
  -- state hash of a concrete block is supposed to be hashed in the
  -- UID of an abstract one; the connection between states is implicit.
  -- Our proofs all work modulo injectivity of UID anyway.
  record Block  : Set where
    constructor Block∙new
    field
      bRound   : Round
      bId      : UID
      bPrevQC  : Maybe UID -- 'nothing' indicates it extends the genesis block.
  open Block public

  Block-η : {b b' : Block}
         → bRound b ≡ bRound b'
         → bId b ≡ bId b'
         → bPrevQC b ≡ bPrevQC b'
         → b ≡ b'
  Block-η refl refl refl = refl

  -- We define a Vote as an AbsVoteData applied
  -- to the correct parameters; This helps in defining
  -- and manipulating the 𝓥 vote evidence predicate.
  Vote : Set
  Vote = AbsVoteData UID NodeId 𝓔

  vRound      : Vote → Round
  vRound      = abs-vRound

  vMember     : Vote → EpochConfig.Member 𝓔
  vMember     = abs-vMember

  vBlockUID   : Vote → UID
  vBlockUID   = abs-vBlockUID

  Vote-η : {v v' : Vote}
         → vRound v ≡ vRound v' → vMember v ≡ vMember v'
         → vBlockUID v ≡ vBlockUID v'
         → v ≡ v'
  Vote-η refl refl refl = refl

  -- * Quorum Certificates
  --
  -- A valid quorum certificate contains at least 'QuorumSize ec'
  -- votes from different authors.
  record QC : Set where
   constructor QC∙new
   field
     qRound         : Round
     qCertBlockId   : UID -- this is the id for the block it certifies.
     qVotes         : List Vote
     -- The voters form a quorum
     qVotes-C1      : IsQuorum (List-map vMember qVotes)
     -- All votes are for the same blockId
     qVotes-C2      : All (λ v → vBlockUID v ≡ qCertBlockId) qVotes
     -- Likewise for rounds
     qVotes-C3      : All (λ v → vRound v ≡ qRound) qVotes
     -- And we have evidence for all votes
     qVotes-C4      : All 𝓥 qVotes
  open QC public

  ------------------------
  -- QC's make a setoid --
  ------------------------

  -- Two QC's are said to be equivalent if they have the same ID;
  -- that is, they certify the same block. As we are talking about
  -- /abstract/ QCs, we have proofs that both have at least QuorumSize
  -- votes for /the same block/!
  --
  -- It might be tempting to want qRound q₀ ≡ qRound q₁ in here,
  -- but the proof of ←-≈Rec in LibraBFT.Abstract.Records.Extends
  -- would be impossible.
  _≈QC_ : QC → QC → Set
  q₀ ≈QC q₁ = qCertBlockId q₀ ≡ qCertBlockId q₁

  _≈QC?_ : (q₀ q₁ : QC) → Dec (q₀ ≈QC q₁)
  q₀ ≈QC? q₁ with qCertBlockId q₀ ≟UID qCertBlockId q₁
  ...| yes refl = yes refl
  ...| no  neq  = no λ x → neq x

  ≈QC-refl : Reflexive _≈QC_
  ≈QC-refl = refl

  ≈QC-sym : Symmetric _≈QC_
  ≈QC-sym refl = refl

  ≈QC-trans : Transitive _≈QC_
  ≈QC-trans refl x = x

  QC-setoid : Setoid ℓ0 ℓ0
  QC-setoid = record
    { Carrier       = QC
    ; _≈_           = _≈QC_
    ; isEquivalence = record
        { refl  = λ {q}         → ≈QC-refl {q}
        ; sym   = λ {q} {u}     → ≈QC-sym {q} {u}
        ; trans = λ {q} {u} {l} → ≈QC-trans {q} {u} {l}
        }
    }

  -- Accessing common fields in different Records types is a nuissance; yet, Blocks,
  -- votes and QC's all have three important common fields: author, round and maybe the
  -- ID of a previous record.  Therefore we declare a type-class that provide "getters"
  -- for commonly used fields.
  record HasRound (A : Set) : Set where
    constructor is-librabft-record
    field
      getRound  : A → Round
  open HasRound {{...}} public

  instance
    block-is-record : HasRound Block
    block-is-record = is-librabft-record bRound

    vote-is-record : HasRound Vote
    vote-is-record = is-librabft-record vRound

    qc-is-record : HasRound QC
    qc-is-record = is-librabft-record qRound

  _≟Block_ : (b₀ b₁ : Block) → Dec (b₀ ≡ b₁)
  b₀ ≟Block b₁
     with bRound b₀ ≟ bRound b₁
  ...| no neq = no λ x → neq (cong bRound x)
  ...| yes r≡
     with (bId b₀) ≟UID (bId b₁)
  ...| no neq = no λ x → neq (cong bId x)
  ...| yes i≡
     with Maybe-≡-dec {A = UID} _≟UID_ (bPrevQC b₀) (bPrevQC b₁)
  ...| no neq = no λ x → neq (cong bPrevQC x)
  ...| yes p≡ = yes (Block-η r≡ i≡ p≡)

  qcVotes : QC → List Vote
  qcVotes = qVotes

  -- Now we can state whether an author has voted in a given QC.
  _∈QC_  : Member → QC → Set
  a ∈QC qc = Any (λ v → vMember v ≡ a) (qcVotes qc)

  ∈QC-Member : ∀{α}(q : QC)(v : α ∈QC q)
             → α ≡ vMember (List-lookup (qcVotes q) (Any-index v))
  ∈QC-Member {α} q v = aux v
    where
      aux : ∀{vs}(p : Any (λ v → vMember v ≡ α) vs)
          → α ≡ vMember (List-lookup vs (Any-index p))
      aux (here px) = sym px
      aux (there p) = aux p

  -- Gets the vote of a ∈QC
  -- TODO-1: make q explicit; a implicit
  ∈QC-Vote : {a : Member} (q : QC) → (a ∈QC q) → Vote
  ∈QC-Vote q a∈q = Any-lookup a∈q

  ∈QC-Vote-correct : ∀ q → {a : Member} → (p : a ∈QC q)
                   → (∈QC-Vote {a} q p) ∈ qcVotes q
  ∈QC-Vote-correct q a∈q = Any-lookup-correct a∈q

  -- Same vote in two QC's means the QCs are equivalent
  ∈QC-Vote-≈ : {v : Vote}{q q' : QC}
             → v ∈ qcVotes q → v ∈ qcVotes q'
             → q ≈QC q'
  ∈QC-Vote-≈ {v} {q} {q'} vq vq'
    = trans (sym (All-lookup (qVotes-C2 q)  vq))
                 (All-lookup (qVotes-C2 q') vq')

  -- A record is either one of the types introduced above or the initial/genesis record.
  data Record : Set where
    I :             Record
    B : Block     → Record
    Q : QC        → Record

  -- Records are equivalent if and only if they are either not
  -- QCs and propositionally equal or they are equivalent qcs.
  data _≈Rec_ : Record → Record → Set where
    eq-I :                        I    ≈Rec I
    eq-Q : ∀{q₀ q₁} → q₀ ≈QC q₁ → Q q₀ ≈Rec Q q₁
    eq-B : ∀{b₀ b₁} → b₀  ≡  b₁ → B b₀ ≈Rec B b₁

  ≈Rec-refl : Reflexive _≈Rec_
  ≈Rec-refl {I}   = eq-I
  ≈Rec-refl {B x} = eq-B refl
  ≈Rec-refl {Q x} = eq-Q (≈QC-refl {x})

  ≈Rec-sym : Symmetric _≈Rec_
  ≈Rec-sym {I}         eq-I       = eq-I
  ≈Rec-sym {B x}       (eq-B prf) = eq-B (sym prf)
  ≈Rec-sym {Q x} {Q y} (eq-Q prf) = eq-Q (≈QC-sym {x} {y} prf)

  ≈Rec-trans : Transitive _≈Rec_
  ≈Rec-trans {I}                eq-I      eq-I      = eq-I
  ≈Rec-trans {B x}             (eq-B p₀) (eq-B p₁) = eq-B (trans p₀ p₁)
  ≈Rec-trans {Q x} {Q y} {Q z} (eq-Q p₀) (eq-Q p₁) = eq-Q (≈QC-trans {x} {y} {z} p₀ p₁)

  Rec-setoid : Setoid ℓ0 ℓ0
  Rec-setoid = record
    { Carrier       = Record
    ; _≈_           = _≈Rec_
    ; isEquivalence = record
        { refl  = λ {q}         → ≈Rec-refl {q}
        ; sym   = λ {q} {u}     → ≈Rec-sym {q} {u}
        ; trans = λ {q} {u} {l} → ≈Rec-trans {q} {u} {l}
        }
    }

  -- Record unique ids carry whether the abstract id was assigned
  -- to a QC or a Block; this can be useful to avoid having to deal
  -- with 'blockId ≟ initialAgreedID' in order to decide whether
  -- a block is the genesis block or not.
  data TypedUID : Set where
    id-I   : TypedUID
    id-B∨Q : UID -> TypedUID

  id-I≢id-B∨Q : ∀{id} → id-I ≡ id-B∨Q id → ⊥
  id-I≢id-B∨Q ()

  id-B∨Q-inj : ∀{u₁ u₂} → id-B∨Q u₁ ≡ id-B∨Q u₂ → u₁ ≡ u₂
  id-B∨Q-inj refl = refl

  uid : Record → TypedUID
  uid I     = id-I
  uid (B b) = id-B∨Q (bId   b)
  uid (Q q) = id-B∨Q (qCertBlockId q)

  -- Each record has a round
  round : Record → Round
  round I     = 0
  round (B b) = getRound b
  round (Q q) = getRound q


module FStream.Core where

------------------------------------------------------------------------
-- Core module of effectful streams
------------------------------------------------------------------------

open import Library

mutual
  record FStream {i : Size} {ℓ₁ ℓ₂} (C : Container ℓ₁) (A : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    inductive
    field
      inF : ⟦ C ⟧ (FStream' {i} C A)
  record FStream' {i : Size} {ℓ₁ ℓ₂} (C : Container ℓ₁) (A : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    constructor _≺_
    coinductive
    field
      head : A
      tail : {j : Size< i} → FStream {j} C A
open FStream public
open FStream' public

-- TODO Remove nonworking things and put all auxiliary functions in Util.agda

postulate
  _►_ : ∀ {ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} →
    ⟦ C ⟧ A → FStream C A → FStream C A
--inF (a ► as) = fmap (λ x → x ≺ as) a

-- Caution, this one pushes the side effects down one tick
mutual
  _►'_ : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} →
    A → FStream {i} C A → FStream {i} C A
  inF (a ►' as) = fmap (aux a) (inF as)

  aux  : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} →
    A → FStream' {i} C A → FStream' {i} C A
  head (aux a as) = a
  tail (aux a as) = head as ►' tail as

-- TODO Write without the direct recursion
{-# TERMINATING #-}
_►⋯' : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} → A → FStream {i} C A
a ►⋯' = a ►' (a ►⋯')

mutual
  map : ∀ {i ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁} {A : Set ℓ₂} {B : Set ℓ₃} →
    (A → B) → FStream {i} C A → FStream {i} C B
  inF (map f as) = fmap (map' f) (inF as)

  map' : ∀ {i ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁} {A : Set ℓ₂} {B : Set ℓ₃} →
    (A → B) → FStream' {i} C A → FStream' {i} C B
  head (map' f as) = f (head as)
  tail (map' f as) = map f (tail as)

mutual
  constantly : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} →
    ⟦ C ⟧ A → FStream {i} C A
  inF (constantly ca) = fmap (constantly' ca) ca

  constantly' : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {A : Set ℓ₂} →
    ⟦ C ⟧ A → A → FStream' {i} C A
  head (constantly' ca a) = a
  tail (constantly' ca a) = constantly ca

repeat : {A : Set} → {C : Container ℓ₀} → ⟦ C ⟧ A -> FStream C A
proj₁ (inF (repeat (proj₁ , proj₂))) = proj₁
head (proj₂ (inF (repeat (proj₁ , proj₂))) x) = proj₂ x
tail (proj₂ (inF (repeat (proj₁ , proj₂))) x) = repeat (proj₁ , proj₂)

mutual
  corec : ∀ {i ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁} {A : Set ℓ₂} {X : Set ℓ₃} →
    (X → A × ⟦ C ⟧ X) → ⟦ C ⟧ X → FStream {i} C A
  inF (corec f x) = fmap (corec' f) x

  corec' : ∀ {i ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁} {A : Set ℓ₂} {X : Set ℓ₃} →
    (X → A × ⟦ C ⟧ X) → X → FStream' {i} C A
  head (corec' f x) = proj₁ (f x)
  tail (corec' f x) = corec f (proj₂ (f x))

{-
Stuff that doesn't obviously work:
* drop, _at_ (Since side effects would have to be thrown away)
  (One could at most delay the side effects somehow?)
* _▸⋯  (Only if the given value is effectful or the functor is pointed, i.e. has a null effect (like Applicative or Monad), or by delaying the side effects)
-}


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

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

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Properties
open import Definition.LogicalRelation.Substitution.Conversion
open import Definition.LogicalRelation.Substitution.Reduction
open import Definition.LogicalRelation.Substitution.Reflexivity
open import Definition.LogicalRelation.Substitution.ProofIrrelevance
open import Definition.LogicalRelation.Substitution.MaybeEmbed
open import Definition.LogicalRelation.Substitution.Introductions.Nat
open import Definition.LogicalRelation.Substitution.Introductions.Natrec
open import Definition.LogicalRelation.Substitution.Introductions.Empty
open import Definition.LogicalRelation.Substitution.Introductions.Emptyrec
open import Definition.LogicalRelation.Substitution.Introductions.Universe
open import Definition.LogicalRelation.Substitution.Introductions.Pi
open import Definition.LogicalRelation.Substitution.Introductions.Sigma
open import Definition.LogicalRelation.Substitution.Introductions.Id
open import Definition.LogicalRelation.Substitution.Introductions.IdUPiPi
open import Definition.LogicalRelation.Substitution.Introductions.Cast
open import Definition.LogicalRelation.Substitution.Introductions.CastPi
open import Definition.LogicalRelation.Substitution.Introductions.Lambda
open import Definition.LogicalRelation.Substitution.Introductions.Application
open import Definition.LogicalRelation.Substitution.Introductions.Pair
open import Definition.LogicalRelation.Substitution.Introductions.Fst
open import Definition.LogicalRelation.Substitution.Introductions.Snd
open import Definition.LogicalRelation.Substitution.Introductions.SingleSubst
open import Definition.LogicalRelation.Substitution.Introductions.Transp
open import Definition.LogicalRelation.Fundamental.Variable
import Definition.LogicalRelation.Substitution.ProofIrrelevance as PI
import Definition.LogicalRelation.Substitution.Irrelevance as S
open import Definition.LogicalRelation.Substitution.Weakening

open import Tools.Product
open import Tools.Unit
open import Tools.Nat
import Tools.PropositionalEquality as PE

Idreflᵛ : ∀{Γ A l t}
  → ([Γ] : ⊩ᵛ Γ)
  → ([A] : Γ ⊩ᵛ⟨ ∞ ⟩ A ^ [ ! , ι l ] / [Γ])
  → ([t] : Γ ⊩ᵛ⟨ ∞ ⟩ t ∷ A ^ [ ! , ι l ] / [Γ] / [A])
  → let [Id] = Idᵛ {A = A} {t = t} {u = t } [Γ] [A] [t] [t]
    in Γ ⊩ᵛ⟨ ∞ ⟩ Idrefl A t ∷ Id A t t ^ [ % , ι l ] / [Γ] / [Id]

Idreflᵛ {Γ} {A} {l} {t} [Γ] [A] [t]  =
  let [Id] = Idᵛ {A = A} {t = t} {u = t } [Γ] [A] [t] [t]
  in validityIrr {A = Id A t t} {t = Idrefl A t} [Γ] [Id] λ ⊢Δ [σ] → Idreflⱼ (escapeTerm (proj₁ ([A] ⊢Δ [σ])) (proj₁ ([t] ⊢Δ [σ])))

castreflᵛ : ∀{Γ A t}
  → ([Γ] : ⊩ᵛ Γ)
  → ([UA] : Γ ⊩ᵛ⟨ ∞ ⟩ U ⁰ ^ [ ! , next ⁰ ] / [Γ])
  → ([A]ₜ : Γ ⊩ᵛ⟨ ∞ ⟩ A ∷ U ⁰ ^ [ ! , next ⁰ ] / [Γ] / [UA] )
  → ([A] : Γ ⊩ᵛ⟨ ∞ ⟩ A ^ [ ! , ι ⁰ ] / [Γ])
  → ([t]ₜ : Γ ⊩ᵛ⟨ ∞ ⟩ t ∷ A ^ [ ! , ι ⁰ ] / [Γ] / [A])
  → let [Id] : Γ ⊩ᵛ⟨ ∞ ⟩ Id A t (cast ⁰ A A (Idrefl (U ⁰) A) t) ^ [ % , ι ⁰ ] / [Γ]
        [Id] = Idᵛ {A = A} {t = t} {u = cast ⁰ A A (Idrefl (U ⁰) A) t} [Γ] [A] [t]ₜ
                   (castᵗᵛ {A = A} {B = A} {t = t} {e = Idrefl (U ⁰) A} [Γ] [UA] [A]ₜ [A]ₜ [A] [A] [t]ₜ
                           (Idᵛ {A = U _} {t = A} {u = A} [Γ] [UA] [A]ₜ [A]ₜ)
                           (Idreflᵛ {A = U _} {t = A} [Γ] [UA] [A]ₜ))
    in Γ ⊩ᵛ⟨ ∞ ⟩ castrefl A t ∷ Id A t (cast ⁰ A A (Idrefl (U ⁰) A) t) ^ [ % , ι ⁰ ] / [Γ] / [Id]

castreflᵛ {Γ} {A} {t} [Γ] [UA] [A]ₜ [A] [t]ₜ =
  let [Id] : Γ ⊩ᵛ⟨ ∞ ⟩ Id A t (cast ⁰ A A (Idrefl (U ⁰) A) t) ^ [ % , ι ⁰ ] / [Γ]
      [Id] = Idᵛ {A = A} {t = t} {u = cast ⁰ A A (Idrefl (U ⁰) A) t} [Γ] [A] [t]ₜ
                 (castᵗᵛ {A = A} {B = A} {t = t} {e = Idrefl (U ⁰) A} [Γ] [UA] [A]ₜ [A]ₜ [A] [A] [t]ₜ
                 (Idᵛ {A = U _} {t = A} {u = A} [Γ] [UA] [A]ₜ [A]ₜ)
                 (Idreflᵛ {A = U _} {t = A} [Γ] [UA] [A]ₜ))
  in  validityIrr {A = Id A t (cast ⁰ A A (Idrefl (U ⁰) A) t)} {t = castrefl A t}
                  [Γ] [Id] λ ⊢Δ [σ] → castreflⱼ (escapeTerm (proj₁ ([UA] ⊢Δ [σ])) (proj₁ ([A]ₜ ⊢Δ [σ]))) (escapeTerm (proj₁ ([A] ⊢Δ [σ])) (proj₁ ([t]ₜ ⊢Δ [σ])))


open import Mockingbird.Forest using (Forest)

-- Gödel's Forest
module Mockingbird.Problems.Chapter17 {b ℓ} (forest : Forest {b} {ℓ}) where

open import Data.Empty using (⊥-elim)
open import Data.Product using (_×_; _,_; proj₁; proj₂; ∃-syntax)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function using (_$_; _⇔_; Equivalence; mk⇔)
open import Function.Equivalence.Reasoning renaming (begin_ to ⇔-begin_; _∎ to _⇔-∎)
open import Level using (_⊔_)
open import Relation.Binary using (_Respects_)
open import Relation.Nullary using (¬_)
open import Relation.Unary using (Pred; _∈_)

open Forest forest
open import Mockingbird.Problems.Chapter15 forest using (⇔-¬; ⇔-↯)

record HasMate {s} (Sings : Pred Bird s) : Set (s ⊔ b) where
  field
    _′ : Bird → Bird
    isMate : ∀ {x} y → Sings (x ′ ∙ y) ⇔ (¬ Sings (x ∙ y))

open HasMate ⦃ ... ⦄

record HasAssociate {s} (Sings : Pred Bird s) : Set (s ⊔ b) where
  field
    _* : Bird → Bird
    isAssociate : ∀ {x} y → Sings (x * ∙ y) ⇔ Sings (x ∙ (y ∙ y))

open HasAssociate ⦃ ... ⦄

weakLEM : ∀ {a} (A : Set a) → ¬ ¬ (A ⊎ ¬ A)
weakLEM A ¬[A⊎¬A] =
  let ¬A : ¬ A
      ¬A a = ¬[A⊎¬A] (inj₁ a)

      A⊎¬A : A ⊎ ¬ A
      A⊎¬A = inj₂ ¬A
  in ¬[A⊎¬A] A⊎¬A

module _ {n s} (IsNightingale : Pred Bird n) (Sings : Pred Bird s) (𝒩 : Bird)
         (respects : Sings Respects _≈_)
         (LEM : ∀ x → Sings x ⊎ (¬ Sings x))
         (C₁ : ∀ {x} → IsNightingale x → Sings x)
         ⦃ C₂ : HasMate Sings ⦄
         ⦃ C₃ : HasAssociate Sings ⦄
         (C₄ : ∀ x → Sings (𝒩 ∙ x) ⇔ IsNightingale x) where

  private
    doubleNegation : ∀ x → ¬ ¬ Sings x → Sings x
    doubleNegation x ¬¬x-sings with LEM x
    ... | inj₁ x-sings = x-sings
    ... | inj₂ ¬x-sings = ⊥-elim $ ¬¬x-sings ¬x-sings

    doubleNegation-⇔ : ∀ x → (¬ ¬ Sings x) ⇔ Sings x
    doubleNegation-⇔ x = mk⇔ (doubleNegation x) (λ x-sings ¬x-sings → ¬x-sings x-sings)

  problem₁ : ∃[ 𝒢 ] (Sings 𝒢 × ¬ IsNightingale 𝒢)
  problem₁ = (𝒢 , conclusion)
    where
      𝒩′ = 𝒩 ′
      𝒩′* = 𝒩′ *
      𝒢 = 𝒩′* ∙ 𝒩′*

      𝒢-sings⇔¬𝒩𝒢-sings : Sings 𝒢 ⇔ (¬ Sings (𝒩 ∙ 𝒢))
      𝒢-sings⇔¬𝒩𝒢-sings = ⇔-begin
        Sings 𝒢                   ⇔⟨⟩
        Sings (𝒩′* ∙ 𝒩′*)         ⇔⟨ isAssociate 𝒩′* ⟩
        Sings (𝒩′ ∙ (𝒩′* ∙ 𝒩′*))  ⇔⟨⟩
        Sings (𝒩′ ∙ 𝒢)            ⇔⟨ isMate 𝒢 ⟩
        (¬ Sings (𝒩 ∙ 𝒢))         ⇔-∎

      ¬𝒢-sings⇔𝒢-isNightingale : (¬ Sings 𝒢) ⇔ IsNightingale 𝒢
      ¬𝒢-sings⇔𝒢-isNightingale = ⇔-begin
        ¬ Sings 𝒢          ⇔⟨ ⇔-¬ 𝒢-sings⇔¬𝒩𝒢-sings ⟩
        ¬ ¬ Sings (𝒩 ∙ 𝒢)  ⇔⟨ doubleNegation-⇔ $ 𝒩 ∙ 𝒢 ⟩
        Sings (𝒩 ∙ 𝒢)      ⇔⟨ C₄ 𝒢 ⟩
        IsNightingale 𝒢    ⇔-∎

      𝒢-sings⇔¬𝒢-isNightingale : Sings 𝒢 ⇔ (¬ IsNightingale 𝒢)
      𝒢-sings⇔¬𝒢-isNightingale = ⇔-begin
        Sings 𝒢              ⇔˘⟨ doubleNegation-⇔ 𝒢 ⟩
        ¬ ¬ Sings 𝒢          ⇔⟨ ⇔-¬ ¬𝒢-sings⇔𝒢-isNightingale ⟩
        (¬ IsNightingale 𝒢)  ⇔-∎

      conclusion : Sings 𝒢 × ¬ IsNightingale 𝒢
      conclusion with LEM 𝒢
      ... | inj₁ 𝒢-sings = (𝒢-sings , Equivalence.f 𝒢-sings⇔¬𝒢-isNightingale 𝒢-sings)
      ... | inj₂ ¬𝒢-sings = ⊥-elim $ ¬𝒢-sings $ C₁ $ Equivalence.f ¬𝒢-sings⇔𝒢-isNightingale ¬𝒢-sings

  problem₂ : ∃[ 𝒢 ] (Sings 𝒢 × ¬ IsNightingale 𝒢)
  problem₂ = (𝒢 , conclusion)
    where
      𝒩* = 𝒩 *
      𝒩*′ = 𝒩* ′
      𝒢 = 𝒩*′ ∙ 𝒩*′

      -- Note: this proof (and the definition of 𝒢) is the only difference to
      -- the proof of problem 1.
      𝒢-sings⇔¬𝒩𝒢-sings : Sings 𝒢 ⇔ (¬ Sings (𝒩 ∙ 𝒢))
      𝒢-sings⇔¬𝒩𝒢-sings = ⇔-begin
        Sings 𝒢 ⇔⟨⟩
        Sings (𝒩*′ ∙ 𝒩*′)          ⇔⟨ isMate 𝒩*′ ⟩
        ¬ Sings (𝒩* ∙ 𝒩*′)         ⇔⟨ ⇔-¬ $ isAssociate 𝒩*′ ⟩
        ¬ Sings (𝒩 ∙ (𝒩*′ ∙ 𝒩*′))  ⇔⟨⟩
        (¬ Sings (𝒩 ∙ 𝒢))          ⇔-∎

      ¬𝒢-sings⇔𝒢-isNightingale : (¬ Sings 𝒢) ⇔ IsNightingale 𝒢
      ¬𝒢-sings⇔𝒢-isNightingale = ⇔-begin
        ¬ Sings 𝒢          ⇔⟨ ⇔-¬ 𝒢-sings⇔¬𝒩𝒢-sings ⟩
        ¬ ¬ Sings (𝒩 ∙ 𝒢)  ⇔⟨ doubleNegation-⇔ $ 𝒩 ∙ 𝒢 ⟩
        Sings (𝒩 ∙ 𝒢)      ⇔⟨ C₄ 𝒢 ⟩
        IsNightingale 𝒢    ⇔-∎

      𝒢-sings⇔¬𝒢-isNightingale : Sings 𝒢 ⇔ (¬ IsNightingale 𝒢)
      𝒢-sings⇔¬𝒢-isNightingale = ⇔-begin
        Sings 𝒢              ⇔˘⟨ doubleNegation-⇔ 𝒢 ⟩
        ¬ ¬ Sings 𝒢          ⇔⟨ ⇔-¬ ¬𝒢-sings⇔𝒢-isNightingale ⟩
        (¬ IsNightingale 𝒢)  ⇔-∎

      conclusion : Sings 𝒢 × ¬ IsNightingale 𝒢
      conclusion with LEM 𝒢
      ... | inj₁ 𝒢-sings = (𝒢-sings , Equivalence.f 𝒢-sings⇔¬𝒢-isNightingale 𝒢-sings)
      ... | inj₂ ¬𝒢-sings = ⊥-elim $ ¬𝒢-sings $ C₁ $ Equivalence.f ¬𝒢-sings⇔𝒢-isNightingale ¬𝒢-sings

  _Represents_ : ∀ {r} → (A : Bird) → (𝒮 : Pred Bird r) → Set (b ⊔ s ⊔ r)
  A Represents 𝒮 = ∀ x → Sings (A ∙ x) ⇔ x ∈ 𝒮

  IsSociety : ∀ {r} → (𝒮 : Pred Bird r) → Set (b ⊔ s ⊔ r)
  IsSociety 𝒮 = ∃[ A ] A Represents 𝒮

  _ : IsSociety IsNightingale
  _ = (𝒩 , C₄)

  problem₃ : ¬ IsSociety Sings
  problem₃ 𝒮@(A , A-represents) =
    let A′ = A ′
        A′* = A′ *

        𝒮ᶜ : IsSociety λ x → ¬ Sings x
        𝒮ᶜ =
          ( A′
          , λ x → ⇔-begin
              Sings (A′ ∙ x)   ⇔⟨ isMate x ⟩
              ¬ Sings (A ∙ x)  ⇔⟨ ⇔-¬ $ A-represents x ⟩
              (¬ Sings x)      ⇔-∎
          )

        A′*A′*-sings⇔¬A′*A′*-sings : Sings (A′* ∙ A′*) ⇔ (¬ Sings (A′* ∙ A′*))
        A′*A′*-sings⇔¬A′*A′*-sings = ⇔-begin
          Sings (A′* ∙ A′*)         ⇔⟨ isAssociate A′* ⟩
          Sings (A′ ∙ (A′* ∙ A′*))  ⇔⟨ proj₂ 𝒮ᶜ (A′* ∙ A′*) ⟩
          (¬ Sings (A′* ∙ A′*))     ⇔-∎

    in ⇔-↯ A′*A′*-sings⇔¬A′*A′*-sings


module BTA10 where
----------------------------------------
--equivalence between two typing systems
----------------------------------------
------------
--system one
------------
  --  module annotated-type-one where
  --  open import Data.Bool
  --  open import Data.List
  --  open import Data.Nat

  --  infix 4 _∈_
  --  data _∈_ {A : Set} : A → List A → Set where
  --    hd : ∀ {x xs} → x ∈ (x ∷ xs)
  --    tl : ∀ {x y xs} → x ∈ xs → x ∈ (y ∷ xs)

  --  data BT : Set where
  --    S : BT
  --    D : BT

  --  data _⊑_ : BT → BT → Set where
  --    bt-refl   : ∀ {bt} → bt ⊑ bt
  --    bt-≤-dyn  : ∀ {bt} → bt ⊑ D

  --  data atype : BT → Set where
  --   AInt : (bt : BT) → atype bt
  --   AFun : (bt : BT) → ∀ {bt₁ bt₂} → atype bt₁ → atype bt₂ → bt ⊑ bt₁ → bt ⊑ bt₂ → atype bt
  --   ⊎ : (bt : BT) → ∀ {bt₁ bt₂} → atype bt₁ → atype bt₂ → bt ⊑ bt₁ → bt ⊑ bt₂ → atype bt
  --   • : (bt : BT) → ∀ {bt₁ bt₂} → atype bt₁ → atype bt₂ → bt ⊑ bt₁ → bt ⊑ bt₂ → atype bt

  --  -- btof : ∀ {bt : BT} → atype bt → BT
  --  -- btof (AInt bt) = bt
  --  -- btof (AFun bt ty ty₁ x x₁) = bt
  --  -- btof (⊎ bt ty ty₁ x x₁) = bt
  --  -- btof (• bt ty ty₁ x x₁) = bt

  --  ----------------
  --  --typing context
  --  ----------------
  --  ----------------------
  --  --auxiliary definition
  --  ----------------------
  --  record Sg (S : Set) (T : S → Set) : Set where
  --    constructor _,_
  --    field
  --      ffst : S
  --      ssnd : T ffst
  --  open Sg public

  --  _of_ : (BT : Set) → (BT → Set) → Set
  --  BT of atype = Sg BT \ x → atype x

  --  AType : Set
  --  AType = BT of atype

  --  ---------
  --  --context
  --  ---------
  --  ACtx = List AType

  --  ---------
  --  --example
  --  ---------
  --  context1 : ACtx
  --  context1 = (D , AInt D) ∷ (S , AInt S) ∷ (S , AFun S (AInt D) (AInt D) bt-≤-dyn bt-≤-dyn) ∷ []

  --  ------------------
  --  --typed expression
  --  ------------------
  --  data AExp (Δ : ACtx) : AType → Set where
  --    Var : ∀ {α} → α ∈ Δ → AExp Δ α
  --    Int : (bt : BT) → ℕ → AExp Δ (bt , AInt bt)
  --    Add : ∀ {bt₁ bt₂} → (bt : BT) → AExp Δ (bt₁ , AInt bt₁) → AExp Δ (bt₂ , AInt bt₂)
  --                      → bt ⊑ bt₁ → bt ⊑ bt₂ → AExp Δ (bt , AInt bt)
  --    ALam : ∀ {b b' : BT} {α₁ : atype b} {α₂ : atype b'}  → (bt : BT)  → AExp ((b , α₁) ∷ Δ) (b' , α₂)
  --                      → (e₁ : bt ⊑ b) → (e₂ : bt ⊑ b') → AExp Δ (bt , AFun bt α₁ α₂ e₁ e₂)
  --    AApp : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} {e₁ : bt ⊑ b} {e₂ : bt ⊑ b'}  → AExp Δ (bt , AFun bt α₁ α₂ e₁ e₂)
  --                      → AExp Δ (b , α₁) → AExp Δ (b' , α₂)
  -- -- Static pairs and sums
  --    _,_  : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} → AExp Δ (b , α₁) → AExp Δ (b' , α₂)
  --                     → (e₁ : bt ⊑ b) → (e₂ : bt ⊑ b') → AExp Δ (bt , • bt α₁ α₂ e₁ e₂)
  --    Tl   : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} → AExp Δ (b , α₁) → (e₁ : bt ⊑ b) → (e₂ : bt ⊑ b')
  --                     → AExp Δ (bt , ⊎ bt α₁ α₂ e₁ e₂)
  --    Tr   : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} → AExp Δ (b' , α₂) → (e₁ : bt ⊑ b) → (e₂ : bt ⊑ b')
  --                     → AExp Δ (bt , ⊎ bt α₁ α₂ e₁ e₂)
  --    Fst  : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} {e₁ : bt ⊑ b} {e₂ : bt ⊑ b'} → AExp Δ (bt , • bt α₁ α₂ e₁ e₂) → AExp Δ (b , α₁)
  --    Snd  : ∀ {b b' bt : BT} {α₁ : atype b} {α₂ : atype b'} {e₁ : bt ⊑ b} {e₂ : bt ⊑ b'} → AExp Δ (bt , • bt α₁ α₂ e₁ e₂) → AExp Δ (b' , α₂)
  --    Case : ∀ {b b' b'' bt : BT} {α₁ : atype b} {α₂ : atype b'} {α₃ : atype b''} {e₁ : bt ⊑ b} {e₂ : bt ⊑ b'}
  --                                 → AExp Δ (bt , ⊎ bt α₁ α₂ e₁ e₂) → AExp ((b , α₁) ∷ Δ) (b'' , α₃)
  --                                 → AExp ((b' , α₂) ∷ Δ) (b'' , α₃)
  --                                 → bt ⊑ b'' → AExp Δ (b'' , α₃)
  -- -- Liftable static terms
  -- -- ↑     : ∀ {α} → Liftable α → AExp Δ α  → AExp Δ (D (typeof α))

------------
--system two
------------

   module annotated-type-two where
   open import Data.Bool
   open import Data.List
   open import Data.Nat

   module traditional-representation where
   infix 4 _∈_
   data _∈_ {A : Set} : A → List A → Set where
     hd : ∀ {x xs} → x ∈ (x ∷ xs)
     tl : ∀ {x y xs} → x ∈ xs → x ∈ (y ∷ xs)

   data BT : Set where
     S : BT
     D : BT

   ------------------------------
   --binding time annotated types
   ------------------------------
   ------------------------------
   --Note
   --[BType] for ?
   --[AType] for annotated type
   ------------------------------
   mutual
     data AType : Set where
       an  : BT → BType → AType

     data BType : Set where
       BInt : BType
       BFun : AType → AType → BType
       _•_  : AType → AType → BType
       _⊎_  : AType → AType → BType

   ACtx = List AType

   ------------------
   --well-formed type
   ------------------
   ---------------------
   --auxiliaries
   ---------------------
   btof : AType → BT
   btof (an a x₁) = a

   data _⊑_ : BT → BT → Set where
     reflS : S ⊑ S
     reflD : D ⊑ D
     S≤D  :  S ⊑ D

   -----
   --wft
   -----
   data wft  : AType → Set where
     wf-int  : ∀ {a : BT} → wft (an a BInt)
     wf-fun  : ∀ {a : BT} {α₁ α₂ : AType} → wft α₁ → wft α₂
               → a ⊑ btof α₁ → a ⊑ btof α₂ → wft (an a (BFun α₁ α₂))
     wf-pair : ∀ {a : BT} {α₁ α₂ : AType} → wft α₁ → wft α₂
               → a ⊑ btof α₁ → a ⊑ btof α₂ → wft (an a (α₁ • α₂))
     wf-sum  : ∀ {a : BT} {α₁ α₂ : AType} → wft α₁ → wft α₂
               → a ⊑ btof α₁ → a ⊑ btof α₂ → wft (an a (α₁ ⊎ α₂))

   ----------
   --Liftable
   ----------
   -----------------------------------------------------------------
   --Note
   --• liftable defines a subset of annotated expressions whose type
   --  annotation can be "lifted" to "D"
   --• liftable dynamic types are trivially lifted
   --• assume that we only consider the set of well-formed types
   -----------------------------------------------------------------
   mutual
     data Liftable : AType → Set where
       --dynamic type can be trivially lifted
       D-aty  : ∀ {σ} → Liftable (an D σ)
       --static integer can be lifted to a dynamic one
       --same holds for static pair and sum
       S-int  : Liftable (an S BInt)
       S-pair : ∀ {α₁ α₂} → Liftable α₁ → Liftable α₂
                          → Liftable (an S (α₁ • α₂))
       S-sum  : ∀ {α₁ α₂} → Liftable α₁ → Liftable α₂
                          → Liftable (an S (α₁ ⊎ α₂))
       --a liftable static functional type must have
       --1. dynamic value as input
       --2. higher-order static value with dynamic output
       --3. higher-order static value as input with no
       --   static inputs
       S-fun  : ∀ {α₁ α₂} → Liftable⁻ α₁ → Liftable α₂
                          → Liftable (an S (BFun α₁ α₂))
     data Liftable⁻ : AType → Set where
       D-aty  : ∀ {σ} → Liftable⁻ (an D σ)
       S-pair : ∀ {α₁ α₂} → Liftable⁻ α₁ → Liftable⁻ α₂
                          → Liftable⁻ (an S (α₁ • α₂))
       S-fun  : ∀ {α₁ α₂} → Liftable α₁ → Liftable⁻ α₂
                          → Liftable⁻ (an S (BFun α₁ α₂))

   data liftable : AType → Set where
     ↑ : ∀ {α} → wft α → Liftable α → liftable α
   ----------------------------
   --typed annotated expression
   ----------------------------
   ----------------------------------------------------
   --Note
   --constructor name begins with an "A" for "annotated"
   --typing constructor as [AExp]
   --typing context as [Δ]
   --"a" for both expression and type annotation
   --"α" for [AType]
   --"σ" for [BType]
   ----------------------------------------------------
   data AExp (Δ : ACtx) : AType → Set where
     Var   : ∀ {α} → α ∈ Δ → AExp Δ α
     AInt  : (a : BT) → ℕ → AExp Δ (an a BInt)
     AAdd  : (a : BT) → AExp Δ (an a BInt) → AExp Δ (an a BInt) → AExp Δ (an a BInt)
     ALam  : ∀ {α₁ α₂} → (a : BT) → wft (an a (BFun α₁ α₂)) → AExp (α₁ ∷ Δ) α₂
                       → AExp Δ (an a (BFun α₁ α₂))
     AApp  : ∀ {α₁ α₂} → (a : BT) → wft (an a (BFun α₁ α₂)) → AExp Δ (an a (BFun α₁ α₂))
                       → AExp Δ α₁ → AExp Δ α₂
     APair : ∀ {α₁ α₂} → (a : BT) → wft (an a (α₁ • α₂))  → AExp Δ α₁ → AExp Δ α₂
                       → AExp Δ (an a (α₁ • α₂))
     ATl   : ∀ {α₁ α₂} → (a : BT) → wft (an a (α₁ ⊎ α₂))  → AExp Δ α₁
                       → AExp Δ (an a (α₁ ⊎ α₂))
     ATr   : ∀ {α₁ α₂} → (a : BT) → wft (an a (α₁ ⊎ α₂))  → AExp Δ α₂
                       → AExp Δ (an a (α₁ ⊎ α₂))
     ACase : ∀ {α₁ α₂ α₃} {a : BT} → a ⊑ btof α₃ → AExp Δ (an a (α₁ ⊎ α₂)) → AExp (α₁ ∷ Δ) α₃
                       → AExp (α₂ ∷ Δ) α₃ → AExp Δ α₃
     Lift  : ∀ {a} {σ} → liftable (an a σ) → AExp Δ (an a σ) → AExp Δ (an D σ)

   module two-level-representation where
   ---------------
   --residual type
   ---------------
   data type : Set where
     Int : type
     Fun : type → type → type
     _•_ : type → type → type
     _⊎_ : type → type → type

   ----------------
   --annotated type
   ----------------
   ---------------------------------------------
   --Note
   --• static type constructor starting with "S"
   --  except for pair and sum
   --• residual type used for building dynamic
   --  type
   ---------------------------------------------
   data atype : Set where
    SInt  : atype
    SFun  : atype → atype → atype
    D     : type  → atype
    _•_   : atype → atype → atype
    _⊎_   : atype → atype → atype

   btof-γ : atype → BT
   btof-γ SInt = S
   btof-γ (SFun γ γ₁) = S
   btof-γ (D x) = D
   btof-γ (γ • γ₁) = S
   btof-γ (γ ⊎ γ₁) = S

   actx = List atype

   ----------
   --Liftable
   ----------
   --Note
   --"τ" for residual type [type]
   --"γ" for [atype]
   mutual
     data Liftok : atype → Set where
       D-aty  : ∀ τ → Liftok (D τ)
       S-int  : Liftok SInt
       S-sum  : ∀ {γ₁ γ₂} → Liftok γ₁ → Liftok γ₂ → Liftok (γ₁ ⊎ γ₂)
       S-pair : ∀ {γ₁ γ₂} → Liftok γ₁ → Liftok γ₂ → Liftok (γ₁ • γ₂)
       S-fun  : ∀ {γ₁ γ₂} → Liftok⁻ γ₁ → Liftok γ₂ → Liftok (SFun γ₁ γ₂)

     data Liftok⁻ : atype → Set where
       D-aty : ∀ τ → Liftok⁻ (D τ)
       _•_   : ∀ {γ₁ γ₂} → Liftok⁻ γ₁ → Liftok⁻ γ₂ → Liftok⁻ (γ₁ • γ₂)
       S-fun : ∀ {γ₁ γ₂} → Liftok γ₁ → Liftok⁻ γ₂ → Liftok⁻ (SFun γ₁ γ₂)

   ----------------------------
   --typed two-level annotated expression
   ----------------------------
   --------------------
   --auxiliary function
   --------------------
   typeof : atype → type
   typeof SInt = Int
   typeof (SFun γ₁ γ₂) = Fun (typeof γ₁) (typeof γ₂)
   typeof (D τ) = τ
   typeof (γ₁ • γ₂) = typeof γ₁ • typeof γ₂
   typeof (γ₁ ⊎ γ₂) = typeof γ₁ ⊎ typeof γ₂

   --------
   --[aexp]
   --------
   --Note
   --• "γ" for [atype]
   --• "τ" for [type]
   --• typed terms starting with "S" for "static"
   --  and "D" for "dynamic",except for pair
   --• "Ω" for typing context
   data aexp (Ω : actx) : atype → Set where
     Var    : ∀ {γ} → γ ∈ Ω → aexp Ω γ
     SInt   : ℕ → aexp Ω SInt
     SAdd   : aexp Ω SInt → aexp Ω SInt → aexp Ω SInt
     SLam   : ∀ {γ₁ γ₂}   → aexp (γ₁ ∷ Ω) γ₂ → aexp Ω (SFun γ₁ γ₂)
     SApp   : ∀ {γ₁ γ₂}   → aexp Ω (SFun γ₁ γ₂) → aexp Ω γ₁ → aexp Ω γ₂
     DInt   : ℕ → aexp Ω (D Int)
     DAdd   : aexp Ω (D Int) → aexp Ω (D Int) → aexp Ω (D Int)
     DLam   : ∀ {τ₁ τ₂}   → aexp ((D τ₁) ∷ Ω) (D τ₂) → aexp Ω (D (Fun τ₁ τ₂))
     DApp   : ∀ {τ₁ τ₂}   → aexp Ω (D (Fun τ₁ τ₂)) → aexp Ω (D τ₁) → aexp Ω (D τ₂)
  -- Static pairs and sums
     _,_    : ∀ {γ₁ γ₂} → aexp Ω γ₁ → aexp Ω γ₂ → aexp Ω (γ₁ • γ₂)
     STl    : ∀ {γ₁ γ₂} → aexp Ω γ₁ → aexp Ω (γ₁ ⊎ γ₂)
     STr    : ∀ {γ₁ γ₂} → aexp Ω γ₂ → aexp Ω (γ₁ ⊎ γ₂)
     SFst   : ∀ {γ₁ γ₂} → aexp Ω (γ₁ • γ₂) → aexp Ω γ₁
     SSnd   : ∀ {γ₁ γ₂} → aexp Ω (γ₁ • γ₂) → aexp Ω γ₂
     SCase  : ∀ {γ₁ γ₂ γ₃} → aexp Ω (γ₁ ⊎ γ₂) → aexp (γ₁ ∷ Ω) γ₃ → aexp (γ₂ ∷ Ω) γ₃ → aexp Ω γ₃
  -- Dynamic pairs and sums
     _ḋ_    : ∀ {τ₁ τ₂} → aexp Ω (D τ₁) → aexp Ω (D τ₂) → aexp Ω (D (τ₁ • τ₂))
     DTl    : ∀ {τ₁ τ₂} → aexp Ω (D τ₁) → aexp Ω (D (τ₁ ⊎ τ₂))
     DTr    : ∀ {τ₁ τ₂} → aexp Ω (D τ₂) → aexp Ω (D (τ₁ ⊎ τ₂))
     DFst   : ∀ {τ₁ τ₂} → aexp Ω (D (τ₁ • τ₂)) → aexp Ω (D τ₁)
     DSnd   : ∀ {τ₁ τ₂} → aexp Ω (D (τ₁ • τ₂)) → aexp Ω (D τ₂)
     DCase  : ∀ {τ₁ τ₂ τ₃} → aexp Ω (D (τ₁ ⊎ τ₂)) → aexp ((D τ₁) ∷ Ω) (D τ₃) → aexp ((D τ₂) ∷ Ω) (D τ₃) → aexp Ω (D τ₃)
  -- Liftable static terms
     Lift   : ∀ {γ} → Liftok γ → aexp Ω γ  → aexp Ω (D (typeof γ))

   ---------------------------------------------------------------
   --Isomorphism between terms typed by [AExp] and terms by [aexp]
   ---------------------------------------------------------------
   module isomorphism where
   open import Data.Product
   open import Relation.Binary.PropositionalEquality
   ----------
   --Analysis
   ----------
   --consider static integer in [aexp],
   --SInt 0 : aexp Ω SInt,∀ Ω
   --naturally its counter-part in [AExp] should be,
   --AInt S 0 : AExp Δ (an S BInt)

   --for dynamic integer,
   --DInt 0 : aexp Ω (D Int)
   --AInt D 0 : aexp Δ (an D BInt)

   --Notice the two kinds of isomorphisms involved in above example
   --a. isomorphism w.r.t. types
   --b. isomorphism w.r.t. typed terms

   ---------------------
   --a. type isomorphism
   ---------------------

   --a.1. [atype] to [AType]
   --integer
   --SInt ==> an S BInt
   --D Int ==> an D BInt
   --function
   --SFun (D Int)(D Int) ==> an S (BFun (an D BInt) (an D BInt))
   --D (Fun Int Int) ==> an D (BFun (an D BInt) (an D BInt))

   ------------------------------------
   --projection from [atype] to [AType]
   ------------------------------------
   projT₁ : atype → AType
   projT₁ SInt = an S BInt
   projT₁ (SFun aty aty₁) = an S (BFun (projT₁ aty) (projT₁ aty₁))
   projT₁ (D Int) = an D BInt
   projT₁ (D (Fun x x₁)) = an D (BFun (projT₁ (D x)) (projT₁ (D x₁)))
   projT₁ (D (x • x₁)) = an D (projT₁ (D x) • projT₁ (D x₁))
   projT₁ (D (x ⊎ x₁)) = an D (projT₁ (D x) ⊎ projT₁ (D x₁))
   projT₁ (aty • aty₁) = an S ((projT₁ aty) • projT₁ aty₁)
   projT₁ (aty ⊎ aty₁) = an S ((projT₁ aty) ⊎ projT₁ aty₁)

   --a.2. [AType] to [atype]
   --imposing [wft] upon types defined in [AType] before projection
   --consider the following type,
   --an D (BFun (an S BInt) (an S BInt)) which has no counter part
   --in [atype] for the following lemma,
   --                ∀ γ, wft (projT₁ γ)
   --suppose we focus on these types satisfying [wft],
   --integer
   --an S BInt ==> SInt
   --an D BInt ==> D Int
   --function
   --an S BFun (an D BInt) (an D BInt) ==> SFun (D Int) (D Int)
   --an D BFun (an D BInt) (an D BInt) ==> D (Fun Int Int)

   ------------------------------------------------
   --projection from well-formed [AType] to [atype]
   ------------------------------------------------
   --------
   --helper
   --------
   ------------------------
   --from [atype] to [type]
   ------------------------
   γ→τ : atype → type
   γ→τ SInt = Int
   γ→τ (SFun aty aty₁) = Fun (γ→τ aty) (γ→τ aty₁)
   γ→τ (D x) = x
   γ→τ (aty • aty₁) = (γ→τ aty) • (γ→τ aty₁)
   γ→τ (aty ⊎ aty₁) = (γ→τ aty) ⊎ (γ→τ aty₁)

   projT₂ : (α : AType) → wft α → atype
   projT₂ .(an S BInt) (wf-int {S}) = SInt
   projT₂ .(an D BInt) (wf-int {D}) = D Int
   projT₂ .(an S (BFun α₁ α₂)) (wf-fun {S} {α₁} {α₂} wft wft₁ x x₁) = SFun (projT₂ α₁ wft) (projT₂ α₂ wft₁)
   projT₂ .(an D (BFun α₁ α₂)) (wf-fun {D} {α₁} {α₂} wft wft₁ x x₁) = D (Fun (γ→τ (projT₂ α₁ wft)) (γ→τ (projT₂ α₂ wft₁)))
   projT₂ .(an S (α₁ • α₂)) (wf-pair {S} {α₁} {α₂} wft wft₁ x x₁) = (projT₂ α₁ wft) • (projT₂ α₂ wft₁)
   projT₂ .(an D (α₁ • α₂)) (wf-pair {D} {α₁} {α₂} wft wft₁ x x₁) = D (γ→τ (projT₂ α₁ wft) • γ→τ (projT₂ α₂ wft₁))
   projT₂ .(an S (α₁ ⊎ α₂)) (wf-sum {S} {α₁} {α₂} wft wft₁ x x₁) = (projT₂ α₁ wft) ⊎ (projT₂ α₂ wft₁)
   projT₂ .(an D (α₁ ⊎ α₂)) (wf-sum {D} {α₁} {α₂} wft wft₁ x x₁) = D (γ→τ (projT₂ α₁ wft) ⊎ γ→τ (projT₂ α₂ wft₁))

   -------------------------------------
   --lemmas w.r.t. isomorphism of types
   -------------------------------------
   -----------------------------------------------
   --direction one:"starting from type in [atype]"
   -----------------------------------------------
   -----------------------
   --some auxiliary lemmas
   -----------------------
   S⊑* : ∀ {α} → S ⊑ btof α
   S⊑* {an S x₁} = reflS
   S⊑* {an D x₁} = S≤D

   a-pDatype : ∀ {τ : type} → btof (projT₁ (D τ)) ≡ D
   a-pDatype {Int} = refl
   a-pDatype {Fun τ τ₁} = refl
   a-pDatype {τ • τ₁} = refl
   a-pDatype {τ ⊎ τ₁} = refl

   D-a-pDatype : ∀ {τ : type} → D ⊑ (btof (projT₁ (D τ)))
   D-a-pDatype {τ} rewrite a-pDatype {τ} = reflD

   isoT₁ : ∀ (γ : atype) → Σ (wft (projT₁ γ)) \ x → γ ≡ projT₂ (projT₁ γ) x
   isoT₁ SInt = wf-int , refl
   isoT₁ (SFun γ γ₁)  with (proj₂ (isoT₁ γ)) | (proj₂ (isoT₁ γ₁))
   ... | I | I' = wf-fun (proj₁ (isoT₁ γ)) (proj₁ (isoT₁ γ₁)) (S⊑* {(projT₁ γ)}) (S⊑* {projT₁ γ₁}) , cong₂ SFun I I'
   isoT₁ (D Int) = wf-int , refl
   isoT₁ (D (Fun x x₁)) = wf-fun (proj₁ (isoT₁ (D x))) (proj₁ (isoT₁ (D x₁))) (D-a-pDatype {x}) (D-a-pDatype {x₁}) ,
                                  cong
                                    D
                                  (cong₂ Fun (cong γ→τ (proj₂ (isoT₁ (D x))))
                                  (cong γ→τ (proj₂ (isoT₁ (D x₁)))))
   isoT₁ (D (x • x₁)) = wf-pair (proj₁ (isoT₁ (D x))) (proj₁ (isoT₁ (D x₁))) (D-a-pDatype {x}) (D-a-pDatype {x₁}) ,
                                  cong
                                    D
                                  (cong₂ _•_ (cong γ→τ (proj₂ (isoT₁ (D x))))
                                  (cong γ→τ (proj₂ (isoT₁ (D x₁)))))
   isoT₁ (D (x ⊎ x₁)) = wf-sum (proj₁ (isoT₁ (D x))) (proj₁ (isoT₁ (D x₁))) (D-a-pDatype {x}) (D-a-pDatype {x₁}) ,
                                  cong
                                    D
                                  (cong₂ _⊎_ (cong γ→τ (proj₂ (isoT₁ (D x))))
                                  (cong γ→τ (proj₂ (isoT₁ (D x₁)))))
   isoT₁ (γ • γ₁) = wf-pair (proj₁ (isoT₁ γ)) (proj₁ (isoT₁ γ₁)) (S⊑* {projT₁ γ}) (S⊑* {projT₁ γ₁}) ,
                             cong₂ _•_ (proj₂ (isoT₁ γ)) (proj₂ (isoT₁ γ₁))
   isoT₁ (γ ⊎ γ₁) = wf-sum (proj₁ (isoT₁ γ)) (proj₁ (isoT₁ γ₁)) (S⊑* {projT₁ γ}) (S⊑* {projT₁ γ₁}) ,
                             cong₂ _⊎_ (proj₂ (isoT₁ γ)) (proj₂ (isoT₁ γ₁))

   -----------------------------------------------------------
   --direction two:"starting from type in well-formed [AType]"
   -----------------------------------------------------------
   -----------------------
   --some auxiliary lemmas
   -----------------------
   eqDγ→τprojT₂ : ∀ {α : AType} {wf : wft α} → D ⊑ btof α → D (γ→τ (projT₂ α wf)) ≡ projT₂ α wf
   eqDγ→τprojT₂ {an S BInt} {wf-int} ()
   eqDγ→τprojT₂ {an D BInt} {wf-int} e = refl
   eqDγ→τprojT₂ {an S (BFun α₁ α₂)} {wf-fun wf wf₁ x x₁} ()
   eqDγ→τprojT₂ {an D (BFun α₁ α₂)} {wf-fun wf wf₁ x x₁} e = refl
   eqDγ→τprojT₂ {an S (α₁ • α₂)} {wf-pair wf wf₁ x x₁} ()
   eqDγ→τprojT₂ {an D (α₁ • α₂)} {wf-pair wf wf₁ x x₁} e = refl
   eqDγ→τprojT₂ {an S (α₁ ⊎ α₂)} {wf-sum wf wf₁ x x₁} ()
   eqDγ→τprojT₂ {an D (α₁ ⊎ α₂)} {wf-sum wf wf₁ x x₁} e = refl

   isoT₂ : ∀ {α : AType} → (wf : wft α) → α ≡  projT₁ (projT₂ α wf)
   isoT₂ {an S BInt} wf-int = refl
   isoT₂ {an S (BFun x x₁)} (wf-fun wf wf₁ x₂ x₃) = cong₂ an refl (cong₂ BFun (isoT₂ {x} wf) (isoT₂ {x₁} wf₁))
   isoT₂ {an S (x • x₁)} (wf-pair wf wf₁ x₂ x₃) = cong₂ an refl (cong₂ _•_ (isoT₂ {x} wf) (isoT₂ {x₁} wf₁))
   isoT₂ {an S (x ⊎ x₁)} (wf-sum wf wf₁ x₂ x₃) = cong₂ an refl (cong₂ _⊎_ (isoT₂ {x} wf) (isoT₂ {x₁} wf₁))
   isoT₂ {an D BInt} wf-int = refl
   isoT₂ {an D (BFun x x₁)} (wf-fun wf wf₁ x₂ x₃) =
       cong₂ an refl
         (cong₂ BFun
          (trans (isoT₂ {x} wf)
           (sym (cong projT₁ (eqDγ→τprojT₂ {x} {wf} x₂))))
          (trans (isoT₂ {x₁} wf₁)
           (sym (cong projT₁ (eqDγ→τprojT₂ {x₁} {wf₁} x₃)))))
   isoT₂ {an D (x • x₁)} (wf-pair wf wf₁ x₂ x₃) =
         cong₂ an refl
           (cong₂ _•_
            (trans (isoT₂ {x} wf)
             (sym (cong projT₁ (eqDγ→τprojT₂ {x} {wf} x₂))))
            (trans (isoT₂ {x₁} wf₁)
             (sym (cong projT₁ (eqDγ→τprojT₂ {x₁} {wf₁} x₃)))))
   isoT₂ {an D (x ⊎ x₁)} (wf-sum wf wf₁ x₂ x₃) =
         cong₂ an refl
           (cong₂ _⊎_
            (trans (isoT₂ {x} wf)
             (sym (cong projT₁ (eqDγ→τprojT₂ {x} {wf} x₂))))
            (trans (isoT₂ {x₁} wf₁)
             (sym (cong projT₁ (eqDγ→τprojT₂ {x₁} {wf₁} x₃)))))

   ------------------------------------------------
   --lemmas w.r.t. isomorphism of typed expressions
   ------------------------------------------------
   ----------------
   --some instances
   ----------------
   ------------------
   --[AExp] to [aexp]
   ------------------
   --Var hd : AExp [an D (BFun (an D BInt) (an D BInt))]
   --               (an D (BFun (an D BInt) (an D BInt)))
   --==>projt₂*
   --Var hd : aexp [D (Fun Int Int)] (D (Fun Int Int))
   --*Note that projection from expression in [AExp] to
   -- that in [aexp] requires that both the type and the
   -- typing environment are well-formed

   --AInt S 0 : AExp Δ (an S BInt),∀ Δ
   --==>projt₂
   --SInt 0 : aexp Ω* SInt,∀ Ω
   --*Note that the corresponding typing environment in [aexp] satisfies,
   -- Ω = projection of Δ

   --ALam S (wf-fun wf-int wf-int *≤D *≤D) (Var hd)
   --:AExp [] (an S (BFun (an D BInt) (an D BInt)))
   --==>projt₂
   --SLam (Var hd) : aexp [] (SFun (D Int) (D Int))
   --ALam D (wf-fun wf-int wf-int refl refl) (Var hd)
   --:AExp [] (an D (BFun (an D BInt) (an D BInt)))
   --==>projt₂
   --DLam (Var hd) : aexp [] (D (Fun Int Int))

   -----------------------
   --summary upon [projt₂]
   -----------------------
   --• projection only of terms which are typed both by
   --  well-formed [AType] and under well-formed Δ
   --• the results are typed under Ω which is the projection
   --  of Δ
   ------------------------------------------
   --some auxiliary functions and definitions
   ------------------------------------------
   ---------------
   --Well-formed Δ
   ---------------
   data wfe : ACtx → Set where
     wf-[]  : wfe []
     wf-∷   : ∀ {α : AType} {AEnv : ACtx}
                 → wft α → wfe AEnv
                 → wfe (α ∷ AEnv)

   -----------------------------
   --projΔ (similar to [projT₂])
   -----------------------------
   projΔ : (Δ : ACtx) → wfe Δ → actx
   projΔ [] wfe = []
   projΔ (x ∷ Δ) (wf-∷ x₁ wfe) = projT₂ x x₁ ∷ projΔ Δ wfe

   --------
   --projt₂
   --------
   -----------------------
   --some auxiliary lemmas
   -----------------------
   -- --------
   -- --is-≡-α
   -- --------
   -- is-≡-α : (α₁ α₂ : AType) → Bool
   -- is-≡-α (an S BInt) (an S BInt) = true
   -- is-≡-α (an S BInt) (an S (BFun x x₁)) = false
   -- is-≡-α (an S BInt) (an S (x • x₁)) = false
   -- is-≡-α (an S BInt) (an S (x ⊎ x₁)) = false
   -- is-≡-α (an S (BFun x x₁)) (an S BInt) = false
   -- is-≡-α (an S (BFun x x₁)) (an S (BFun x₂ x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃
   -- is-≡-α (an S (BFun x x₁)) (an S (x₂ • x₃)) = false
   -- is-≡-α (an S (BFun x x₁)) (an S (x₂ ⊎ x₃)) = false
   -- is-≡-α (an S (x • x₁)) (an S BInt) = false
   -- is-≡-α (an S (x • x₁)) (an S (BFun x₂ x₃)) = false
   -- is-≡-α (an S (x • x₁)) (an S (x₂ • x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃
   -- is-≡-α (an S (x • x₁)) (an S (x₂ ⊎ x₃)) = false
   -- is-≡-α (an S (x ⊎ x₁)) (an S BInt) = false
   -- is-≡-α (an S (x ⊎ x₁)) (an S (BFun x₂ x₃)) = false
   -- is-≡-α (an S (x ⊎ x₁)) (an S (x₂ • x₃)) = false
   -- is-≡-α (an S (x ⊎ x₁)) (an S (x₂ ⊎ x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃
   -- is-≡-α (an S x₁) (an D x₃) = false
   -- is-≡-α (an D x₁) (an S x₃) = false
   -- is-≡-α (an D BInt) (an D BInt) = true
   -- is-≡-α (an D BInt) (an D (BFun x x₁)) = false
   -- is-≡-α (an D BInt) (an D (x • x₁)) = false
   -- is-≡-α (an D BInt) (an D (x ⊎ x₁)) = false
   -- is-≡-α (an D (BFun x x₁)) (an D BInt) = false
   -- is-≡-α (an D (BFun x x₁)) (an D (BFun x₂ x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃
   -- is-≡-α (an D (BFun x x₁)) (an D (x₂ • x₃)) = false
   -- is-≡-α (an D (BFun x x₁)) (an D (x₂ ⊎ x₃)) = false
   -- is-≡-α (an D (x • x₁)) (an D BInt) = false
   -- is-≡-α (an D (x • x₁)) (an D (BFun x₂ x₃)) = false
   -- is-≡-α (an D (x • x₁)) (an D (x₂ • x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃
   -- is-≡-α (an D (x • x₁)) (an D (x₂ ⊎ x₃)) = false
   -- is-≡-α (an D (x ⊎ x₁)) (an D BInt) = false
   -- is-≡-α (an D (x ⊎ x₁)) (an D (BFun x₂ x₃)) = false
   -- is-≡-α (an D (x ⊎ x₁)) (an D (x₂ • x₃)) = false
   -- is-≡-α (an D (x ⊎ x₁)) (an D (x₂ ⊎ x₃)) = is-≡-α x x₂ ∧ is-≡-α x₁ x₃

   -- --------
   -- --is-≡-τ
   -- --------
   -- is-≡-τ : (τ₁ τ₂ : type) → Bool
   -- is-≡-τ Int Int = true
   -- is-≡-τ Int (Fun τ₂ τ₃) = false
   -- is-≡-τ Int (τ₂ • τ₃) = false
   -- is-≡-τ Int (τ₂ ⊎ τ₃) = false
   -- is-≡-τ (Fun τ₁ τ₂) Int = false
   -- is-≡-τ (Fun τ₁ τ₂) (Fun τ₃ τ₄) = is-≡-τ τ₁ τ₃ ∧ is-≡-τ τ₂ τ₄
   -- is-≡-τ (Fun τ₁ τ₂) (τ₃ • τ₄) = false
   -- is-≡-τ (Fun τ₁ τ₂) (τ₃ ⊎ τ₄) = false
   -- is-≡-τ (τ₁ • τ₂) Int = false
   -- is-≡-τ (τ₁ • τ₂) (Fun τ₃ τ₄) = false
   -- is-≡-τ (τ₁ • τ₂) (τ₃ • τ₄) = is-≡-τ τ₁ τ₃ ∧ is-≡-τ τ₂ τ₄
   -- is-≡-τ (τ₁ • τ₂) (τ₃ ⊎ τ₄) = false
   -- is-≡-τ (τ₁ ⊎ τ₂) Int = false
   -- is-≡-τ (τ₁ ⊎ τ₂) (Fun τ₃ τ₄) = false
   -- is-≡-τ (τ₁ ⊎ τ₂) (τ₃ • τ₄) = false
   -- is-≡-τ (τ₁ ⊎ τ₂) (τ₃ ⊎ τ₄) = is-≡-τ τ₁ τ₃ ∧ is-≡-τ τ₂ τ₄
   -- --------
   -- --is-≡-γ
   -- --------
   -- is-≡-γ : (γ₁ γ₂ : atype) → Bool
   -- is-≡-γ SInt SInt = true
   -- is-≡-γ SInt (SFun γ₂ γ₃) = false
   -- is-≡-γ SInt (D x) = false
   -- is-≡-γ SInt (γ₂ • γ₃) = false
   -- is-≡-γ SInt (γ₂ ⊎ γ₃) = false
   -- is-≡-γ (SFun γ₁ γ₂) SInt = false
   -- is-≡-γ (SFun γ₁ γ₂) (SFun γ₃ γ₄) = is-≡-γ γ₁ γ₃ ∧ is-≡-γ γ₂ γ₄
   -- is-≡-γ (SFun γ₁ γ₂) (D x) = false
   -- is-≡-γ (SFun γ₁ γ₂) (γ₃ • γ₄) = false
   -- is-≡-γ (SFun γ₁ γ₂) (γ₃ ⊎ γ₄) = false
   -- is-≡-γ (D x) SInt = false
   -- is-≡-γ (D x) (SFun γ₂ γ₃) = false
   -- is-≡-γ (D x) (D x₁) = is-≡-τ x x₁
   -- is-≡-γ (D x) (γ₂ • γ₃) = false
   -- is-≡-γ (D x) (γ₂ ⊎ γ₃) = false
   -- is-≡-γ (γ₁ • γ₂) SInt = false
   -- is-≡-γ (γ₁ • γ₂) (SFun γ₃ γ₄) = false
   -- is-≡-γ (γ₁ • γ₂) (D x) = false
   -- is-≡-γ (γ₁ • γ₂) (γ₃ • γ₄) = is-≡-γ γ₁ γ₃ ∧ is-≡-γ γ₂ γ₄
   -- is-≡-γ (γ₁ • γ₂) (γ₃ ⊎ γ₄) = false
   -- is-≡-γ (γ₁ ⊎ γ₂) SInt = false
   -- is-≡-γ (γ₁ ⊎ γ₂) (SFun γ₃ γ₄) = false
   -- is-≡-γ (γ₁ ⊎ γ₂) (D x) = false
   -- is-≡-γ (γ₁ ⊎ γ₂) (γ₃ • γ₄) = false
   -- is-≡-γ (γ₁ ⊎ γ₂) (γ₃ ⊎ γ₄) = is-≡-γ γ₁ γ₃ ∧ is-≡-γ γ₂ γ₄

   -- is-≡-γ→τ : {γ₁ γ₂ : atype} → btof-γ γ₁ ≡ D → btof-γ γ₁ ≡ btof-γ γ₂
   --             → is-≡-γ γ₁ γ₂ ≡ is-≡-τ (γ→τ γ₁) (γ→τ γ₂)
   -- is-≡-γ→τ {SInt} {SInt} eq eq' = refl
   -- is-≡-γ→τ {SInt} {SFun γ₂ γ₃} eq eq' = refl
   -- is-≡-γ→τ {SInt} {D x} () ()
   -- is-≡-γ→τ {SInt} {γ₂ • γ₃} () eq'
   -- is-≡-γ→τ {SInt} {γ₂ ⊎ γ₃} () eq'
   -- is-≡-γ→τ {SFun γ₁ γ₂} {SInt} () eq'
   -- is-≡-γ→τ {SFun γ₁ γ₂} {SFun γ₃ γ₄} () eq'
   -- is-≡-γ→τ {SFun γ₁ γ₂} {D x} () eq'
   -- is-≡-γ→τ {SFun γ₁ γ₂} {γ₃ • γ₄} () eq'
   -- is-≡-γ→τ {SFun γ₁ γ₂} {γ₃ ⊎ γ₄} () eq'
   -- is-≡-γ→τ {D x} {SInt} eq ()
   -- is-≡-γ→τ {D x} {SFun γ₂ γ₃} eq ()
   -- is-≡-γ→τ {D x} {D x₁} eq eq' = refl
   -- is-≡-γ→τ {D x} {γ₂ • γ₃} eq ()
   -- is-≡-γ→τ {D x} {γ₂ ⊎ γ₃} eq ()
   -- is-≡-γ→τ {γ₁ • γ₂} {SInt} eq eq' = refl
   -- is-≡-γ→τ {γ₁ • γ₂} {SFun γ₃ γ₄} () eq'
   -- is-≡-γ→τ {γ₁ • γ₂} {D x} eq ()
   -- is-≡-γ→τ {γ₁ • γ₂} {γ₃ • γ₄} () eq'
   -- is-≡-γ→τ {γ₁ • γ₂} {γ₃ ⊎ γ₄} () eq'
   -- is-≡-γ→τ {γ₁ ⊎ γ₂} {SInt} () eq'
   -- is-≡-γ→τ {γ₁ ⊎ γ₂} {SFun γ₃ γ₄} () eq'
   -- is-≡-γ→τ {γ₁ ⊎ γ₂} {D x} eq ()
   -- is-≡-γ→τ {γ₁ ⊎ γ₂} {γ₃ • γ₄} () eq'
   -- is-≡-γ→τ {γ₁ ⊎ γ₂} {γ₃ ⊎ γ₄} () eq'

   -- btof-γ-projT₂-α : ∀ {a : BT} {σ : BType} {wft : wft (an a σ)}
   --                     → btof-γ (projT₂ (an a σ) wft) ≡ a
   -- btof-γ-projT₂-α {S} {BInt} {wf-int} = refl
   -- btof-γ-projT₂-α {D} {BInt} {wf-int} = refl
   -- btof-γ-projT₂-α {S} {BFun α₁ α₂} {wf-fun wft wft₁ x x₁} = refl
   -- btof-γ-projT₂-α {D} {BFun α₁ α₂} {wf-fun wft wft₁ x x₁} = refl
   -- btof-γ-projT₂-α {S} {α₁ • α₂} {wf-pair wft wft₁ x x₁} = refl
   -- btof-γ-projT₂-α {D} {α₁ • α₂} {wf-pair wft wft₁ x x₁} = refl
   -- btof-γ-projT₂-α {S} {α₁ ⊎ α₂} {wf-sum wft wft₁ x x₁} = refl
   -- btof-γ-projT₂-α {D} {α₁ ⊎ α₂} {wf-sum wft wft₁ x x₁} = refl

   -- is-≡-projT₂ : {α₁ α₂ : AType} {wft₁ : wft α₁} {wft₂ : wft α₂}
   --                → (is-≡-α α₁ α₂) ≡ (is-≡-γ (projT₂ α₁ wft₁) (projT₂ α₂ wft₂))
   -- is-≡-projT₂ {an S BInt} {an S BInt} {wf-int} {wf-int} = refl
   -- is-≡-projT₂ {an S BInt} {an D BInt} {wf-int} {wf-int} = refl
   -- is-≡-projT₂ {an D BInt} {an S BInt} {wf-int} {wf-int} = refl
   -- is-≡-projT₂ {an D BInt} {an D BInt} {wf-int} {wf-int} = refl
   -- is-≡-projT₂ {an S BInt} {an S (BFun α₁ α₂)} {wf-int} {wf-fun wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S BInt} {an D (BFun α₁ α₂)} {wf-int} {wf-fun wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an S (BFun α₁ α₂)} {wf-int} {wf-fun wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an D (BFun α₁ α₂)} {wf-int} {wf-fun wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S BInt} {an S (α₁ • α₂)} {wf-int} {wf-pair wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S BInt} {an D (α₁ • α₂)} {wf-int} {wf-pair wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an S (α₁ • α₂)} {wf-int} {wf-pair wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an D (α₁ • α₂)} {wf-int} {wf-pair wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S BInt} {an S (α₁ ⊎ α₂)} {wf-int} {wf-sum wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S BInt} {an D (α₁ ⊎ α₂)} {wf-int} {wf-sum wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an S (α₁ ⊎ α₂)} {wf-int} {wf-sum wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an D BInt} {an D (α₁ ⊎ α₂)} {wf-int} {wf-sum wft₂ wft₃ x x₁} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an S BInt} {wf-fun wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an D BInt} {wf-fun wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an S BInt} {wf-fun wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an D BInt} {wf-fun wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an S (BFun α₂ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃}
   --    rewrite (is-≡-projT₂ {α₁} {α₂} {wft₁} {wft₃}) | (is-≡-projT₂ {α₃} {α₄} {wft₂} {wft₄}) = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an D (BFun α₂ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an S (BFun α₂ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun (an D σ₁) (an D σ₃))} {an D (BFun (an D σ₂) (an D σ₄))} {wf-fun wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃}
   --    rewrite ((is-≡-projT₂ {an D σ₁} {an D σ₂} {wft₁} {wft₃})) | ((is-≡-projT₂ {an D σ₃} {an D σ₄} {wft₂} {wft₄}))
   --            | is-≡-γ→τ {projT₂ (an D σ₁) wft₁} {projT₂ (an D σ₂) wft₃}
   --              (btof-γ-projT₂-α {D} {σ₁} {wft₁}) (trans (btof-γ-projT₂-α {D} {σ₁} {wft₁}) (sym (btof-γ-projT₂-α {D} {σ₂} {wft₃})))
   --            | is-≡-γ→τ {projT₂ (an D σ₃) wft₂} {projT₂ (an D σ₄) wft₄}
   --              (btof-γ-projT₂-α {D} {σ₃} {wft₂}) (trans (btof-γ-projT₂-α {D} {σ₃} {wft₂}) (sym (btof-γ-projT₂-α {D} {σ₄} {wft₄})))
   --    = refl
   -- is-≡-projT₂ {an D (BFun (an D σ₁) (an D σ₃))} {an D (BFun (an D σ₂) (an S σ₄))} {wf-fun wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ ()}
   -- is-≡-projT₂ {an D (BFun (an D σ₁) (an S σ₃))} {an D (BFun (an D σ₂) (an a₄ σ₄))} {wf-fun wft₁ wft₂ x ()} {wf-fun wft₃ wft₄ x₂ x₃}
   -- is-≡-projT₂ {an D (BFun (an D σ₁) (an a₃ σ₃))} {an D (BFun (an S σ₂) (an a₄ σ₄))} {wf-fun wft₁ wft₂ x x₁}  {wf-fun wft₃ wft₄ () x₃}
   -- is-≡-projT₂ {an D (BFun (an S σ₁) (an a₃ σ₃))} {an D (BFun (an D σ₂) (an a₄ σ₄))} {wf-fun wft₁ wft₂ () x₁} {wf-fun wft₃ wft₄ x₂ x₃}
   -- is-≡-projT₂ {an D (BFun (an S σ₁) (an a₃ σ₃))} {an D (BFun (an S σ₂) (an a₄ σ₄))} {wf-fun wft₁ wft₂ () x₁} {wf-fun wft₃ wft₄ () x₃}
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an S (α₂ • α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an D (α₂ • α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an S (α₂ • α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an D (α₂ • α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an S (α₂ ⊎ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-sum wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (BFun α₁ α₃)} {an D (α₂ ⊎ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-sum wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an S (α₂ ⊎ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-sum wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (BFun α₁ α₃)} {an D (α₂ ⊎ α₄)} {wf-fun wft₁ wft₂ x x₁} {wf-sum wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an S BInt} {wf-pair wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an D BInt} {wf-pair wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an S BInt} {wf-pair wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an D BInt} {wf-pair wft₁ wft₂ x x₁} {wf-int} = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an S (BFun α₂ α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an D (BFun α₂ α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an S (BFun α₂ α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an D (BFun α₂ α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-fun wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an S (α₂ • α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃}
   --   rewrite (is-≡-projT₂ {α₁} {α₂} {wft₁} {wft₃}) | (is-≡-projT₂ {α₃} {α₄} {wft₂} {wft₄})
   --   = refl
   -- is-≡-projT₂ {an S (α₁ • α₃)} {an D (α₂ • α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an S (α₂ • α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = refl
   -- is-≡-projT₂ {an D (α₁ • α₃)} {an D (α₂ • α₄)} {wf-pair wft₁ wft₂ x x₁} {wf-pair wft₃ wft₄ x₂ x₃} = {!!}
   -- is-≡-projT₂ {an a (α₁ • α₃)} {(an a₁ (α₂ ⊎ α₄))} {wf-pair wft₁ wft₂ x x₁} {wf-sum wft₃ wft₄ x₂ x₃} = {!!}
   -- is-≡-projT₂ {(an a (α₁ ⊎ α₃))} {α₂} {wf-sum wft₁ wft₂ x x₁} {wft'} = {!!}
   ⊑-≡ : ∀ {a b : BT} → (e : a ⊑ b) → (e' : a ⊑ b) → e ≡ e'
   ⊑-≡ {S} {S} reflS reflS = refl
   ⊑-≡ {S} {D} S≤D S≤D = refl
   ⊑-≡ {D} {S} () e'
   ⊑-≡ {D} {D} reflD reflD = refl

   wft-≡ : ∀ {α : AType} → (α₁ : wft α) → (α₂ : wft α) → α₁ ≡ α₂
   wft-≡ wf-int wf-int = refl
   wft-≡ (wf-fun wft₁ wft₂ x x₁) (wf-fun wft₃ wft₄ x₂ x₃) rewrite wft-≡ wft₁ wft₃ | wft-≡ wft₂ wft₄ | ⊑-≡ x x₂ | ⊑-≡ x₁ x₃
      = refl
   wft-≡ (wf-pair wft₁ wft₂ x x₁) (wf-pair wft₃ wft₄ x₂ x₃) rewrite wft-≡ wft₁ wft₃ | wft-≡ wft₂ wft₄ | ⊑-≡ x x₂ | ⊑-≡ x₁ x₃
      = refl
   wft-≡ (wf-sum wft₁ wft₂ x x₁) (wf-sum wft₃ wft₄ x₂ x₃) rewrite wft-≡ wft₁ wft₃ | wft-≡ wft₂ wft₄ | ⊑-≡ x x₂ | ⊑-≡ x₁ x₃
      = refl

   id-projT₂&Δ :  {α : AType} {Δ : ACtx}
                 → (wft : wft α) → (wfe : wfe Δ)
                 → (id : α ∈ Δ)
                 →   projT₂ α wft ∈ (projΔ Δ wfe)
   id-projT₂&Δ {α} {[]} wft wfe ()
   id-projT₂&Δ {.x} {x ∷ Δ} wft (wf-∷ x₁ wfe) hd rewrite wft-≡ x₁ wft = hd {x = projT₂ x wft} {xs = projΔ Δ wfe}
   id-projT₂&Δ {α} {x ∷ Δ} wft (wf-∷ x₁ wfe) (tl x₂) = tl (id-projT₂&Δ wft wfe x₂)

   αAExp→wft : ∀ {α : AType} {Δ : ACtx} → (e : AExp Δ α) → wft α
   αAExp→wft {α} {Δ} e = {!!}

   projt₂ : ∀ {α : AType} {Δ : ACtx} → (wft : wft α) → (wfe : wfe Δ)
              → AExp Δ α → aexp (projΔ Δ wfe) (projT₂ α wft)
   projt₂ {an x x₁} wft wfe (Var x₂) = Var (id-projT₂&Δ wft wfe x₂)
   projt₂ {an S BInt} wf-int wfe (AInt .S x₁) = SInt x₁
   projt₂ {an D BInt} wf-int wfe (AInt .D x₁) = DInt x₁
   projt₂ {an S BInt} wf-int wfe (AAdd .S e e₁) = SAdd (projt₂ wf-int wfe e) (projt₂ wf-int wfe e₁)
   projt₂ {an D BInt} wf-int wfe (AAdd .D e e₁) = DAdd (projt₂ wf-int wfe e) (projt₂ wf-int wfe e₁)
   projt₂ {an S (BFun α₁ α₂)} {Δ} (wf-fun wft wft₁ x x₁) wfe (ALam .S x₂ e) = SLam (projt₂ {α₂} {α₁ ∷ Δ} wft₁ (wf-∷ wft wfe) e)
   projt₂ {an D (BFun α₁ α₂)} {Δ} (wf-fun wft wft₁ x x₁) wfe (ALam .D x₂ e) =
    DLam myrewrite
       where myrewrite :  aexp (D (γ→τ (projT₂ α₁ wft)) ∷ projΔ Δ wfe) (D (γ→τ (projT₂ α₂ wft₁)))
             myrewrite rewrite (cong₂ aexp (cong₂  (_∷_) {D (γ→τ (projT₂ α₁ wft))} {projT₂ α₁ wft} {projΔ Δ wfe} {projΔ Δ wfe}
                               (eqDγ→τprojT₂ {α₁} {wft} x) refl) (eqDγ→τprojT₂ {α₂} {wft₁} x₁))
               = projt₂ {α₂} {α₁ ∷ Δ} wft₁ (wf-∷ wft wfe) e

   projt₂ {an x x₁} wft wfe (AApp {α₁} S (wf-fun x₂ x₃ x₄ x₅) e e₁) rewrite sym (wft-≡ x₃ wft)
     = SApp {γ₁ = (projT₂ α₁ x₂)} (projt₂ (wf-fun x₂ x₃ x₄ x₅) wfe e) (projt₂ x₂ wfe e₁)
   projt₂ {an S x₁} wft wfe (AApp D (wf-fun x₂ x₃ x ()) e e₁)
   projt₂ {an D x₁} wft wfe (AApp {an S x₂} D (wf-fun x₃ x₄ () x₅) e e₁)
   projt₂ {an D x₁} {Δ} wft wfe (AApp {an D x₂} D (wf-fun x₃ x₄ x x₅) e e₁)
     rewrite sym (wft-≡ x₄ wft)
       = myrewrite'
           where myrewrite' : aexp (projΔ Δ wfe) (projT₂ (an D x₁) x₄)
                 myrewrite' rewrite sym (cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {an D x₁} {x₄} x₅))
                       = DApp (projt₂ (wf-fun x₃ x₄ x x₅) wfe e) myrewrite
                                  where myrewrite : aexp (projΔ Δ wfe) (D (γ→τ (projT₂ (an D x₂) x₃)))
                                        myrewrite rewrite cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {an D x₂} {x₃} x)
                                            = projt₂ x₃ wfe e₁
   projt₂ {an S (α₁ • α₂)} (wf-pair wft wft₁ x x₁) wfe (APair .S x₂ e e₁) = projt₂ wft wfe e , projt₂ wft₁ wfe e₁
   projt₂ {an D (α₁ • α₂)} {Δ} (wf-pair wft wft₁ x x₁) wfe (APair .D x₂ e e₁)
     = myrewrite1 ḋ myrewrite2
          where myrewrite1 : aexp (projΔ Δ wfe) (D (γ→τ (projT₂ α₁ wft)))
                myrewrite1 rewrite cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {α₁} {wft} x)
                     =  projt₂ wft wfe e
                myrewrite2 : aexp (projΔ Δ wfe) (D (γ→τ (projT₂ α₂ wft₁)))
                myrewrite2 rewrite cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {α₂} {wft₁} x₁)
                     =  projt₂ wft₁ wfe e₁
   projt₂ {an S (α₁ ⊎ α₂)} (wf-sum wft wft₁ x x₁) wfe (ATl .S x₂ e) = STl (projt₂ wft wfe e)
   projt₂ {an D (α₁ ⊎ α₂)} {Δ} (wf-sum wft wft₁ x x₁) wfe (ATl .D x₂ e)
      = DTl myrewrite
           where myrewrite : aexp (projΔ Δ wfe) (D (γ→τ (projT₂ α₁ wft)))
                 myrewrite rewrite  cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {α₁} {wft} x)
                      = projt₂ wft wfe e
   projt₂ {an S (α₁ ⊎ α₂)} (wf-sum wft wft₁ x x₁) wfe (ATr .S x₂ e) = STr (projt₂ wft₁ wfe e)
   projt₂ {an D (α₁ ⊎ α₂)} {Δ} (wf-sum wft wft₁ x x₁) wfe (ATr .D x₂ e)
      = DTr myrewrite
           where myrewrite : aexp (projΔ Δ wfe) (D (γ→τ (projT₂ α₂ wft₁)))
                 myrewrite rewrite cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {α₂} {wft₁} x₁)
                   = projt₂ wft₁ wfe e
   projt₂ {an x x₁} wft wfe (ACase {α₁} {α₂} {.(an x x₁)} {S} x₂ e e₁ e₂) with  αAExp→wft e
   projt₂ {an x₂ x₃} wft wfe (ACase {α₁} {α₂} {.(an x₂ x₃)} {S} x₄ e e₁ e₂) | wf-sum WFT WFT₁ x x₁
      = SCase {γ₁ = projT₂ α₁ WFT} {γ₂ = projT₂ α₂ WFT₁}
          {γ₃ = projT₂ (an x₂ x₃) wft} (projt₂ (wf-sum WFT WFT₁ x x₁) wfe e) (projt₂ wft (wf-∷ WFT wfe) e₁) (projt₂ wft (wf-∷ WFT₁ wfe) e₂)
   projt₂ {an S x₁} wft wfe (ACase {α₁} {α₂} {.(an S x₁)} {D} () e e₁ e₂)
   projt₂ {an D x₁} wft wfe (ACase {α₁} {α₂} {.(an D x₁)} {D} x₂ e e₁ e₂) with αAExp→wft e
   projt₂ {an D x₂} {Δ} wft wfe (ACase {α₁} {α₂} {.(an D x₂)} {D} x₃ e e₁ e₂) | wf-sum WFT WFT₁ x x₁
     = myrewrite
         where myrewrite :  aexp (projΔ Δ wfe) (projT₂ (an D x₂) wft)
               myrewrite  rewrite sym (cong₂ aexp {(projΔ Δ wfe)} {(projΔ Δ wfe)} refl (eqDγ→τprojT₂ {an D x₂} {wft} x₃))
                = DCase (projt₂ (wf-sum WFT WFT₁ x x₁) wfe e) myrewrite1 myrewrite2
                       where myrewrite1 : aexp (D (γ→τ (projT₂ α₁ WFT)) ∷ projΔ Δ wfe) (D (γ→τ (projT₂ (an D x₂) wft)))
                             myrewrite1 rewrite (cong₂ aexp (cong₂  (_∷_) {D (γ→τ (projT₂ α₁ WFT))} {projT₂ α₁ WFT} {projΔ Δ wfe} {projΔ Δ wfe}
                                (eqDγ→τprojT₂ {α₁} {WFT} x) refl) (eqDγ→τprojT₂ {an D x₂} {wft} x₃))
                                  = projt₂ wft (wf-∷ WFT wfe) e₁
                             myrewrite2 : aexp (D (γ→τ (projT₂ α₂ WFT₁)) ∷ projΔ Δ wfe) (D (γ→τ (projT₂ (an D x₂) wft)))
                             myrewrite2 rewrite (cong₂ aexp (cong₂  (_∷_) {D (γ→τ (projT₂ α₂ WFT₁))} {projT₂ α₂ WFT₁} {projΔ Δ wfe} {projΔ Δ wfe}
                                (eqDγ→τprojT₂ {α₂} {WFT₁} x₁) refl) (eqDγ→τprojT₂ {an D x₂} {wft} x₃))
                                  = projt₂ wft (wf-∷ WFT₁ wfe) e₂

   projt₂ {an D x₁} wft wfe (Lift x e) = {!!}


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

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

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening renaming (wk to TWwk)
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Properties.Escape
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties.Conversion

open import Tools.Product
open import Tools.Empty
import Tools.PropositionalEquality as PE

import Data.Nat as Nat

mutual
  -- Helper function for transitivity of type equality using shape views.
  transEqT : ∀ {Γ A B C r l l′ l″}
             {[A] : Γ ⊩⟨ l ⟩ A ^ r} {[B] : Γ ⊩⟨ l′ ⟩ B ^ r} {[C] : Γ ⊩⟨ l″ ⟩ C ^ r}
           → ShapeView₃ Γ l l′ l″ A B C r r r [A] [B] [C]
           → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
           → Γ ⊩⟨ l′ ⟩ B ≡ C ^ r / [B]
           → Γ ⊩⟨ l ⟩  A ≡ C ^ r / [A]
  transEqT (Uᵥ UA UB UC) A≡B B≡C =
    let
      U≡U : Univ (LogRel._⊩¹U_^_.r UA) (LogRel._⊩¹U_^_.l′ UA) PE.≡ Univ (LogRel._⊩¹U_^_.r UB) (LogRel._⊩¹U_^_.l′ UB)
      U≡U = whrDet* (A≡B , Uₙ) ((_⊢_:⇒*:_^_.D (LogRel._⊩¹U_^_.d UB)) , Uₙ)
      r≡r , l≡l = Univ-PE-injectivity U≡U
    in
    PE.subst₂ (λ X Y → _ ⊢ _ ⇒* Univ X Y ^ [ ! , _ ]) (PE.sym r≡r) (PE.sym l≡l) B≡C
  transEqT (ℕᵥ D D′ D″) A≡B B≡C = B≡C
  transEqT (Emptyᵥ D D′ D″) A≡B B≡C = B≡C
  transEqT (ne (ne K [[ ⊢A , ⊢B , D ]] neK K≡K) (ne K₁ D₁ neK₁ _)
               (ne K₂ D₂ neK₂ _))
           (ne₌ M D′ neM K≡M) (ne₌ M₁ D″ neM₁ K≡M₁)
           rewrite whrDet* (red D₁ , ne neK₁) (red D′ , ne neM)
                 | whrDet* (red D₂ , ne neK₂) (red D″ , ne neM₁) =
    ne₌ M₁ D″ neM₁
        (~-trans K≡M K≡M₁)
  transEqT {Γ}  {r = [ r , ι lΠ ]} {l = l} {l′ = l′} {l″ = l″}
           (Πᵥ (Πᵣ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
               (Πᵣ rF₁ lF₁ lG₁ _ _ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁)
               (Πᵣ rF₂ lF₂ lG₂ _ _ F₂ G₂ D₂ ⊢F₂ ⊢G₂ A≡A₂ [F]₂ [G]₂ G-ext₂))
           (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
           (Π₌ F″ G″ D″ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
    let ΠF₁G₁≡ΠF′G′    = whrDet* (red D₁ , Πₙ) (D′  , Πₙ)
        F₁≡F′ , rF₁≡rF′ , lF₁≡lF′ , G₁≡G′ , lG₁≡lG′ , _ = Π-PE-injectivity ΠF₁G₁≡ΠF′G′
        F₂≡F″ , rF₂≡rF′ , lF₂≡lF′ , G₂≡G″ , lG₂≡lG″  , _ = Π-PE-injectivity (whrDet* (red D₂ , Πₙ) (D″ , Πₙ))
        substLift {Δ} {l} {a} {r} ρ x = Δ ⊩⟨ l ⟩ wk (lift ρ) x [ a ] ^ r
        [F′] : ∀ {ρ Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′ ^ [ rF₁ , ι lF₁ ]
        [F′] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x ^ _) F₁≡F′ ([F]₁ [ρ] ⊢Δ)
        [F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l″ ⟩ wk ρ F″ ^ [ rF₂ , ι lF₂ ]
        [F″] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x ^ _) F₂≡F″ ([F]₂ [ρ] ⊢Δ)
        [F′≡F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′ ≡ wk ρ F″ ^ [ rF₁ , ι lF₁ ] / [F′] [ρ] ⊢Δ
        [F′≡F″] {ρ} [ρ] ⊢Δ = irrelevanceEq′ (PE.cong (wk ρ) F₁≡F′) PE.refl PE.refl
                                      ([F]₁ [ρ] ⊢Δ) ([F′] [ρ] ⊢Δ) ([F≡F′]₁ [ρ] ⊢Δ)
        [G′] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ ^ [ rF₁ , ι lF₁ ] / [F′] [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′ [ a ] ^ [ r , ι lG₁ ]
        [G′] {ρ} [ρ] ⊢Δ [a] =
             let [a′] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′)) PE.refl PE.refl
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a]
             in  PE.subst (substLift ρ) G₁≡G′ ([G]₁ [ρ] ⊢Δ [a′])
        [G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ a ∷ wk ρ F″ ^ [ rF₂ , ι lF₂ ] / [F″] [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ wk (lift ρ) G″ [ a ] ^ [ r , ι lG₂ ]
        [G″] {ρ} [ρ] ⊢Δ [a] =
          let [a″] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₂≡F″)) PE.refl PE.refl
                                      ([F″] [ρ] ⊢Δ) ([F]₂ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₂≡G″ ([G]₂ [ρ] ⊢Δ [a″])
        [G′≡G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
                  ([a] : Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ ^ [ rF₁ , ι lF₁ ] / [F′] [ρ] ⊢Δ)
                → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′  [ a ]
                          ≡ wk (lift ρ) G″ [ a ] ^ [ r , ι lG₁ ] / [G′] [ρ] ⊢Δ [a]
        [G′≡G″] {ρ} [ρ] ⊢Δ [a′] =
          let [a]₁ = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′)) PE.refl PE.refl
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a′]
          in  irrelevanceEq′ (PE.cong (λ x → wk (lift ρ) x [ _ ]) G₁≡G′) PE.refl PE.refl
                             ([G]₁ [ρ] ⊢Δ [a]₁) ([G′] [ρ] ⊢Δ [a′])
                             ([G≡G′]₁ [ρ] ⊢Δ [a]₁)
                             -- Γ ⊢ .C ⇒* Π F″ ^ rF ▹ G″ ^ r
    in  Π₌ F″ G″ (PE.subst₃ _ rF₁≡rF′ lF₁≡lF′ lG₁≡lG′ D″) (PE.subst₃ _ rF₁≡rF′ lF₁≡lF′ lG₁≡lG′ (≅-trans A≡B (PE.subst (λ x → Γ ⊢ x ≅ Π F″ ^ rF₁ ° lF₁ ▹ G″ ° lG₁ ° lΠ ^ [ r , ι lΠ ]) ΠF₁G₁≡ΠF′G′ A≡B₁)))
           (λ ρ ⊢Δ → transEq′ PE.refl PE.refl (PE.sym rF₁≡rF′) (PE.sym rF₂≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) (PE.cong ι (PE.sym lF₂≡lF′))
           ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ)
           ([F≡F′] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ))
           (λ ρ ⊢Δ [a] →
              let [a′] = convTerm₁′ (PE.sym rF₁≡rF′) (PE.cong ι (PE.sym lF₁≡lF′)) ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F≡F′] ρ ⊢Δ) [a]
                  [a″] = convTerm₁′ (PE.sym rF₂≡rF′) (PE.cong ι (PE.sym lF₂≡lF′)) ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ) [a′]
              in transEq′ PE.refl PE.refl PE.refl PE.refl (PE.cong ι (PE.sym lG₁≡lG′)) (PE.cong ι (PE.sym lG₂≡lG″))
                          ([G] ρ ⊢Δ [a]) ([G′] ρ ⊢Δ [a′]) ([G″] ρ ⊢Δ [a″])
                          ([G≡G′] ρ ⊢Δ [a]) ([G′≡G″] ρ ⊢Δ [a′]))
  transEqT {Γ}  {r = r} {l = l} {l′ = l′} {l″ = l″}
           (∃ᵥ (∃ᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
               (∃ᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁)
               (∃ᵣ F₂ G₂ D₂ ⊢F₂ ⊢G₂ A≡A₂ [F]₂ [G]₂ G-ext₂))
           (∃₌ F′ G′ D′ A≡B [F≡F′] [G≡G′])
           (∃₌ F″ G″ D″ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
    let ∃F₁G₁≡∃F′G′    = whrDet* (red D₁ , ∃ₙ) (D′  , ∃ₙ)
        F₁≡F′ ,  G₁≡G′ = ∃-PE-injectivity ∃F₁G₁≡∃F′G′
        F₂≡F″ ,  G₂≡G″  = ∃-PE-injectivity (whrDet* (red D₂ , ∃ₙ) (D″ , ∃ₙ))
        substLift {Δ} {l} {a} {r} ρ x = Δ ⊩⟨ l ⟩ wk (lift ρ) x [ a ] ^ r
        lr = TypeInfo.l r
        [F′] : ∀ {ρ Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′ ^ [ % , lr ]
        [F′] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x ^ _) F₁≡F′ ([F]₁ [ρ] ⊢Δ)
        [F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l″ ⟩ wk ρ F″ ^ [ % , lr ]
        [F″] {ρ} [ρ] ⊢Δ = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk ρ x ^ _) F₂≡F″ ([F]₂ [ρ] ⊢Δ)
        [F′≡F″] : ∀ {ρ} {Δ} [ρ] ⊢Δ → Δ ⊩⟨ l′ ⟩ wk ρ F′ ≡ wk ρ F″ ^ [ % , lr ] / [F′] [ρ] ⊢Δ
        [F′≡F″] {ρ} [ρ] ⊢Δ = irrelevanceEq′ (PE.cong (wk ρ) F₁≡F′) PE.refl PE.refl
                                      ([F]₁ [ρ] ⊢Δ) ([F′] [ρ] ⊢Δ) ([F≡F′]₁ [ρ] ⊢Δ)
        [G′] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ ^ [ % , lr ] / [F′] [ρ] ⊢Δ
             → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′ [ a ] ^ r
        [G′] {ρ} [ρ] ⊢Δ [a] =
          let [a′] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′)) PE.refl PE.refl
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₁≡G′ ([G]₁ [ρ] ⊢Δ [a′])
        [G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ a ∷ wk ρ F″ ^ [ % , lr ] / [F″] [ρ] ⊢Δ
             → Δ ⊩⟨ l″ ⟩ wk (lift ρ) G″ [ a ] ^ r
        [G″] {ρ} [ρ] ⊢Δ [a] =
          let [a″] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₂≡F″)) PE.refl PE.refl
                                      ([F″] [ρ] ⊢Δ) ([F]₂ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₂≡G″ ([G]₂ [ρ] ⊢Δ [a″])
        [G′≡G″] : ∀ {ρ Δ a} [ρ] ⊢Δ
                  ([a] : Δ ⊩⟨ l′ ⟩ a ∷ wk ρ F′ ^ [ % , lr ] / [F′] [ρ] ⊢Δ)
                → Δ ⊩⟨ l′ ⟩ wk (lift ρ) G′  [ a ]
                          ≡ wk (lift ρ) G″ [ a ] ^ r / [G′] [ρ] ⊢Δ [a]
        [G′≡G″] {ρ} [ρ] ⊢Δ [a′] =
          let [a]₁ = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′)) PE.refl PE.refl
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a′]
          in  irrelevanceEq′ (PE.cong (λ x → wk (lift ρ) x [ _ ]) G₁≡G′) PE.refl PE.refl
                             ([G]₁ [ρ] ⊢Δ [a]₁) ([G′] [ρ] ⊢Δ [a′])
                             ([G≡G′]₁ [ρ] ⊢Δ [a]₁)
                             -- Γ ⊢ .C ⇒* ∃ F″ ^ rF ▹ G″ ^ r
    in  ∃₌ F″ G″ D″ (≅-trans A≡B (PE.subst (λ x → Γ ⊢ x ≅ ∃ F″ ▹ G″ ^ [ % , lr ]) ∃F₁G₁≡∃F′G′ A≡B₁))
           (λ ρ ⊢Δ → transEq′ PE.refl PE.refl PE.refl PE.refl PE.refl PE.refl
           ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ)
           ([F≡F′] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ))
           (λ ρ ⊢Δ [a] →
              let [a′] = convTerm₁′ PE.refl PE.refl ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F≡F′] ρ ⊢Δ) [a]
                  [a″] = convTerm₁′ PE.refl PE.refl ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ) [a′]
              in  transEq ([G] ρ ⊢Δ [a]) ([G′] ρ ⊢Δ [a′]) ([G″] ρ ⊢Δ [a″])
                          ([G≡G′] ρ ⊢Δ [a]) ([G′≡G″] ρ ⊢Δ [a′]))
  transEqT (emb⁰¹¹ S) A≡B B≡C = transEqT S A≡B B≡C
  transEqT (emb¹⁰¹ S) A≡B B≡C = transEqT S A≡B B≡C
  transEqT (emb¹¹⁰ S) A≡B B≡C = transEqT S A≡B B≡C
  transEqT (emb¹∞∞ S) A≡B B≡C = transEqT S A≡B B≡C
  transEqT (emb∞¹∞ S) A≡B B≡C = transEqT S A≡B B≡C
  transEqT (emb∞∞¹ S) A≡B B≡C = transEqT S A≡B B≡C

  -- Transitivty of type equality.
  transEq : ∀ {Γ A B C r l l′ l″}
            ([A] : Γ ⊩⟨ l ⟩ A ^ r) ([B] : Γ ⊩⟨ l′ ⟩ B ^ r) ([C] : Γ ⊩⟨ l″ ⟩ C ^ r)
          → Γ ⊩⟨ l ⟩  A ≡ B ^ r / [A]
          → Γ ⊩⟨ l′ ⟩ B ≡ C ^ r / [B]
          → Γ ⊩⟨ l ⟩  A ≡ C ^ r / [A]
  transEq [A] [B] [C] A≡B B≡C =
    transEqT (combine (goodCases [A] [B] A≡B) (goodCases [B] [C] B≡C)) A≡B B≡C

  -- Transitivity of type equality with some propositonally equal types.
  transEq′ : ∀ {Γ A B B′ C C′ r r' r'' ll ll' ll'' l l′ l″} → B PE.≡ B′ → C PE.≡ C′ → r PE.≡ r' → r' PE.≡ r'' → ll PE.≡ ll' → ll' PE.≡ ll''
           → ([A] : Γ ⊩⟨ l ⟩ A ^ [ r , ll ]) ([B] : Γ ⊩⟨ l′ ⟩ B ^ [ r' , ll' ]) ([C] : Γ ⊩⟨ l″ ⟩ C ^ [ r'' , ll'' ])
           → Γ ⊩⟨ l ⟩  A ≡ B′ ^ [ r , ll ] / [A]
           → Γ ⊩⟨ l′ ⟩ B ≡ C′ ^ [ r' , ll' ] / [B]
           → Γ ⊩⟨ l ⟩  A ≡ C  ^ [ r , ll ] / [A]
  transEq′ PE.refl PE.refl PE.refl PE.refl PE.refl PE.refl [A] [B] [C] A≡B B≡C = transEq [A] [B] [C] A≡B B≡C

transEqTermNe : ∀ {Γ n n′ n″ A r}
              → Γ ⊩neNf n  ≡ n′  ∷ A ^ r
              → Γ ⊩neNf n′ ≡ n″ ∷ A ^ r
              → Γ ⊩neNf n  ≡ n″ ∷ A ^ r
transEqTermNe (neNfₜ₌ neK neM k≡m) (neNfₜ₌ neK₁ neM₁ k≡m₁) =
  neNfₜ₌ neK neM₁ (~-trans k≡m k≡m₁)

mutual
  transEqTermℕ : ∀ {Γ n n′ n″}
               → Γ ⊩ℕ n  ≡ n′  ∷ℕ
               → Γ ⊩ℕ n′ ≡ n″ ∷ℕ
               → Γ ⊩ℕ n  ≡ n″ ∷ℕ
  transEqTermℕ (ℕₜ₌ k k′ d d′ t≡u prop)
               (ℕₜ₌ k₁ k″ d₁ d″ t≡u₁ prop₁) =
    let k₁Whnf = naturalWhnf (proj₁ (split prop₁))
        k′Whnf = naturalWhnf (proj₂ (split prop))
        k₁≡k′ = whrDet*Term (redₜ d₁ , k₁Whnf) (redₜ d′ , k′Whnf)
        prop′ = PE.subst (λ x → [Natural]-prop _ x _) k₁≡k′ prop₁
    in  ℕₜ₌ k k″ d d″ (≅ₜ-trans t≡u (PE.subst (λ x → _ ⊢ x ≅ _ ∷ _ ^ _) k₁≡k′ t≡u₁))
            (transNatural-prop prop prop′)

  transNatural-prop : ∀ {Γ k k′ k″}
                    → [Natural]-prop Γ k k′
                    → [Natural]-prop Γ k′ k″
                    → [Natural]-prop Γ k k″
  transNatural-prop (sucᵣ x) (sucᵣ x₁) = sucᵣ (transEqTermℕ x x₁)
  transNatural-prop (sucᵣ x) (ne (neNfₜ₌ () neM k≡m))
  transNatural-prop zeroᵣ prop₁ = prop₁
  transNatural-prop prop zeroᵣ = prop
  transNatural-prop (ne (neNfₜ₌ neK () k≡m)) (sucᵣ x₃)
  transNatural-prop (ne [k≡k′]) (ne [k′≡k″]) =
    ne (transEqTermNe [k≡k′] [k′≡k″])

-- Empty
transEmpty-prop : ∀ {Γ k k′ k″ l}
  → [Empty]-prop Γ k k′ l
  → [Empty]-prop Γ k′ k″ l
  → [Empty]-prop Γ k k″ l
transEmpty-prop (ne a b) (ne c d) = ne a d

transEqTermEmpty : ∀ {Γ n n′ n″ l}
  → Γ ⊩Empty n  ≡ n′ ∷Empty^ l
  → Γ ⊩Empty n′ ≡ n″ ∷Empty^ l
  → Γ ⊩Empty n  ≡ n″ ∷Empty^ l
transEqTermEmpty (Emptyₜ₌ (ne a b)) (Emptyₜ₌ (ne c d)) = Emptyₜ₌ (ne a d)

-- Transitivity of term equality.
transEqTerm⁰ : ∀ {Γ A t u v r}
               ([A] : Γ ⊩⟨ ι ⁰ ⟩ A ^ r)
             → Γ ⊩⟨ ι ⁰ ⟩ t ≡ u ∷ A ^ r / [A]
             → Γ ⊩⟨ ι ⁰ ⟩ u ≡ v ∷ A ^ r / [A]
             → Γ ⊩⟨ ι ⁰ ⟩ t ≡ v ∷ A ^ r / [A]
transEqTerm⁰ (ℕᵣ D) [t≡u] [u≡v] = transEqTermℕ [t≡u] [u≡v]
transEqTerm⁰ (Emptyᵣ D) [t≡u] [u≡v] = transEqTermEmpty [t≡u] [u≡v]
transEqTerm⁰ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ₌ k m d d′ (neNfₜ₌ neK₁ neM k≡m))
                              (neₜ₌ k₁ m₁ d₁ d″ (neNfₜ₌ neK₂ neM₁ k≡m₁)) =
  let k₁≡m = whrDet*Term (redₜ d₁ , ne neK₂) (redₜ d′ , ne neM)
  in  neₜ₌ k m₁ d d″
           (neNfₜ₌ neK₁ neM₁
                   (~-trans k≡m (PE.subst (λ x → _ ⊢ x ~ _ ∷ _ ^ _) k₁≡m k≡m₁)))
transEqTerm⁰ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ₌ d d′)
                              (neₜ₌ d₁ d″) = neₜ₌ d d″
transEqTerm⁰ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g])
            (Πₜ₌ f₁ g₁ d₁ d₁′ funcF₁ funcG₁ f≡g₁ [f]₁ [g]₁ [f≡g]₁)
            rewrite whrDet*Term (redₜ d′ , functionWhnf funcG)
                            (redₜ d₁ , functionWhnf funcF₁) =
  Πₜ₌ f g₁ d d₁′ funcF funcG₁ (≅ₜ-trans f≡g f≡g₁) [f] [g]₁
      (λ ρ ⊢Δ [a] → transEqTerm⁰ ([G] ρ ⊢Δ [a])
                                ([f≡g] ρ ⊢Δ [a])
                                ([f≡g]₁ ρ ⊢Δ [a]))
transEqTerm⁰ {r = [ % , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′
transEqTerm⁰ {r = [ % , l ]} (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′

transEqTerm¹ : ∀ {Γ A t u v r}
               ([A] : Γ ⊩⟨ ι ¹ ⟩ A ^ r)
             → Γ ⊩⟨ ι ¹ ⟩ t ≡ u ∷ A ^ r / [A]
             → Γ ⊩⟨ ι ¹ ⟩ u ≡ v ∷ A ^ r / [A]
             → Γ ⊩⟨ ι ¹ ⟩ t ≡ v ∷ A ^ r / [A]
transEqTerm¹ {Γ} {A} {t} {u} {v} {r} (Uᵣ (Uᵣ rU ⁰ l< eq d)) (Uₜ₌ [t] [u] A≡B [t≡u])
  (Uₜ₌ [u]′ [v] B′≡C [u≡v]) =
  let
    ti = LogRel._⊩¹U_∷_^_/_.[t]
    ⊢Γ = wf (_⊢_:⇒*:_^_.⊢A d)
    B≡B′ = whrDet*Term (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]))
      (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]′) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]′))
    A≡C = ≅ₜ-trans (PE.subst (λ X → EqRelSet._⊢_≅_∷_^_ eqrel Γ (LogRel._⊩¹U_∷_^_/_.K [t]) X (Univ rU ⁰) ([ ! , ι ¹ ])) B≡B′ A≡B) B′≡C
    [t≡v] = λ {ρ} {Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) ⊢Δ →
      transEq (ti [t] [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) (ti [v] [ρ] ⊢Δ) ([t≡u] [ρ] ⊢Δ)
        (irrelevanceEq (ti [u]′ [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) ([u≡v] [ρ] ⊢Δ))
  in
  Uₜ₌ [t] [v] A≡C [t≡v]
transEqTerm¹ (ℕᵣ D) [t≡u] [u≡v] = transEqTermℕ [t≡u] [u≡v]
transEqTerm¹ (Emptyᵣ D) [t≡u] [u≡v] = transEqTermEmpty [t≡u] [u≡v]
transEqTerm¹ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ₌ k m d d′ (neNfₜ₌ neK₁ neM k≡m))
                              (neₜ₌ k₁ m₁ d₁ d″ (neNfₜ₌ neK₂ neM₁ k≡m₁)) =
  let k₁≡m = whrDet*Term (redₜ d₁ , ne neK₂) (redₜ d′ , ne neM)
  in  neₜ₌ k m₁ d d″
           (neNfₜ₌ neK₁ neM₁
                   (~-trans k≡m (PE.subst (λ x → _ ⊢ x ~ _ ∷ _ ^ _) k₁≡m k≡m₁)))
transEqTerm¹ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ₌ d d′)
                              (neₜ₌ d₁ d″) = neₜ₌ d d″
transEqTerm¹ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g])
            (Πₜ₌ f₁ g₁ d₁ d₁′ funcF₁ funcG₁ f≡g₁ [f]₁ [g]₁ [f≡g]₁)
            rewrite whrDet*Term (redₜ d′ , functionWhnf funcG)
                            (redₜ d₁ , functionWhnf funcF₁) =
  Πₜ₌ f g₁ d d₁′ funcF funcG₁ (≅ₜ-trans f≡g f≡g₁) [f] [g]₁
      (λ ρ ⊢Δ [a] → transEqTerm¹ ([G] ρ ⊢Δ [a])
                                ([f≡g] ρ ⊢Δ [a])
                                ([f≡g]₁ ρ ⊢Δ [a]))
transEqTerm¹ {r = [ % , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′
transEqTerm¹ {r = [ % , l ]} (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′
transEqTerm¹ (emb l< [A]) [t≡u] [u≡v] = transEqTerm⁰ [A] [t≡u] [u≡v]

transEqTerm∞ : ∀ {Γ A t u v r}
               ([A] : Γ ⊩⟨ ∞ ⟩ A ^ r)
             → Γ ⊩⟨ ∞ ⟩ t ≡ u ∷ A ^ r / [A]
             → Γ ⊩⟨ ∞ ⟩ u ≡ v ∷ A ^ r / [A]
             → Γ ⊩⟨ ∞ ⟩ t ≡ v ∷ A ^ r / [A]
transEqTerm∞ {Γ} {A} {t} {u} {v} {r} (Uᵣ (Uᵣ rU ⁰ l< eq d)) (Uₜ₌ [t] [u] A≡B [t≡u])
  (Uₜ₌ [u]′ [v] B′≡C [u≡v]) =
  let
    ti = LogRel._⊩¹U_∷_^_/_.[t]
    ⊢Γ = wf (_⊢_:⇒*:_^_.⊢A d)
    B≡B′ = whrDet*Term (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]))
      (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]′) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]′))
    A≡C = ≅ₜ-trans (PE.subst (λ X → EqRelSet._⊢_≅_∷_^_ eqrel Γ (LogRel._⊩¹U_∷_^_/_.K [t]) X (Univ rU ⁰) ([ ! , ι ¹ ])) B≡B′ A≡B) B′≡C
    [t≡v] = λ {ρ} {Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) ⊢Δ →
      transEq (ti [t] [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) (ti [v] [ρ] ⊢Δ) ([t≡u] [ρ] ⊢Δ)
        (irrelevanceEq (ti [u]′ [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) ([u≡v] [ρ] ⊢Δ))
  in
  Uₜ₌ [t] [v] A≡C [t≡v]
transEqTerm∞ {Γ} {A} {t} {u} {v} {r} (Uᵣ (Uᵣ rU ¹ l< eq d)) (Uₜ₌ [t] [u] A≡B [t≡u])
  (Uₜ₌ [u]′ [v] B′≡C [u≡v]) =
  let
    ti = LogRel._⊩¹U_∷_^_/_.[t]
    ⊢Γ = wf (_⊢_:⇒*:_^_.⊢A d)
    B≡B′ = whrDet*Term (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]))
      (_⊢_:⇒*:_∷_^_.d (LogRel._⊩¹U_∷_^_/_.d [u]′) , typeWhnf (LogRel._⊩¹U_∷_^_/_.typeK [u]′))
    A≡C = ≅ₜ-trans (PE.subst (λ X → EqRelSet._⊢_≅_∷_^_ eqrel Γ (LogRel._⊩¹U_∷_^_/_.K [t]) X (Univ rU ¹) ([ ! , ∞ ])) B≡B′ A≡B) B′≡C
    [t≡v] = λ {ρ} {Δ} ([ρ] : ρ ∷ Δ ⊆ Γ) ⊢Δ →
      transEq (ti [t] [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) (ti [v] [ρ] ⊢Δ) ([t≡u] [ρ] ⊢Δ)
        (irrelevanceEq (ti [u]′ [ρ] ⊢Δ) (ti [u] [ρ] ⊢Δ) ([u≡v] [ρ] ⊢Δ))
  in
  Uₜ₌ [t] [v] A≡C [t≡v]
transEqTerm∞ (ℕᵣ D) [t≡u] [u≡v] = transEqTermℕ [t≡u] [u≡v]
transEqTerm∞ (Emptyᵣ D) [t≡u] [u≡v] = transEqTermEmpty [t≡u] [u≡v]
transEqTerm∞ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ₌ k m d d′ (neNfₜ₌ neK₁ neM k≡m))
                              (neₜ₌ k₁ m₁ d₁ d″ (neNfₜ₌ neK₂ neM₁ k≡m₁)) =
  let k₁≡m = whrDet*Term (redₜ d₁ , ne neK₂) (redₜ d′ , ne neM)
  in  neₜ₌ k m₁ d d″
           (neNfₜ₌ neK₁ neM₁
                   (~-trans k≡m (PE.subst (λ x → _ ⊢ x ~ _ ∷ _ ^ _) k₁≡m k≡m₁)))
transEqTerm∞ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ₌ d d′)
                              (neₜ₌ d₁ d″) = neₜ₌ d d″
transEqTerm∞ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g])
            (Πₜ₌ f₁ g₁ d₁ d₁′ funcF₁ funcG₁ f≡g₁ [f]₁ [g]₁ [f≡g]₁)
            rewrite whrDet*Term (redₜ d′ , functionWhnf funcG)
                            (redₜ d₁ , functionWhnf funcF₁) =
  Πₜ₌ f g₁ d d₁′ funcF funcG₁ (≅ₜ-trans f≡g f≡g₁) [f] [g]₁
      (λ ρ ⊢Δ [a] → transEqTerm∞ ([G] ρ ⊢Δ [a])
                                ([f≡g] ρ ⊢Δ [a])
                                ([f≡g]₁ ρ ⊢Δ [a]))
transEqTerm∞ {r = [ % , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′
transEqTerm∞ {r = [ % , l ]} (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (d , d′)
            (d₁ , d₁′) = d , d₁′
transEqTerm∞ (emb {l′ = ι ¹} l< [A]) [t≡u] [u≡v] = transEqTerm¹ [A] [t≡u] [u≡v]

transEqTerm : ∀ {l Γ A t u v r}
              ([A] : Γ ⊩⟨ l ⟩ A ^ r)
            → Γ ⊩⟨ l ⟩ t ≡ u ∷ A ^ r / [A]
            → Γ ⊩⟨ l ⟩ u ≡ v ∷ A ^ r / [A]
            → Γ ⊩⟨ l ⟩ t ≡ v ∷ A ^ r / [A]
transEqTerm {l = ι ⁰} = transEqTerm⁰
transEqTerm {l = ι ¹} = transEqTerm¹
transEqTerm {l = ∞} = transEqTerm∞


------------------------------------------------------------------------
-- The Agda standard library
--
-- The Cowriter type and some operations
------------------------------------------------------------------------

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

-- Disabled to prevent warnings from BoundedVec
{-# OPTIONS --warn=noUserWarning #-}

module Codata.Cowriter where

open import Size
open import Level as L using (Level)
open import Codata.Thunk using (Thunk; force)
open import Codata.Conat
open import Codata.Delay using (Delay; later; now)
open import Codata.Stream as Stream using (Stream; _∷_)
open import Data.Unit
open import Data.List.Base using (List; []; _∷_)
open import Data.List.NonEmpty using (List⁺; _∷_)
open import Data.Nat.Base as Nat using (ℕ; zero; suc)
open import Data.Product as Prod using (_×_; _,_)
open import Data.Sum.Base as Sum using (_⊎_; inj₁; inj₂)
open import Data.Vec.Base using (Vec; []; _∷_)
open import Data.Vec.Bounded as Vec≤ using (Vec≤; _,_)
open import Function

private
  variable
    a b w x : Level
    A : Set a
    B : Set b
    W : Set w
    X : Set x

------------------------------------------------------------------------
-- Definition

data Cowriter (W : Set w) (A : Set a) (i : Size) : Set (a L.⊔ w) where
  [_] : A → Cowriter W A i
  _∷_ : W → Thunk (Cowriter W A) i → Cowriter W A i

------------------------------------------------------------------------
-- Relationship to Delay.

fromDelay : ∀ {i} → Delay A i → Cowriter ⊤ A i
fromDelay (now a)    = [ a ]
fromDelay (later da) = _ ∷ λ where .force → fromDelay (da .force)

toDelay : ∀ {i} → Cowriter W A i → Delay A i
toDelay [ a ]    = now a
toDelay (_ ∷ ca) = later λ where .force → toDelay (ca .force)

------------------------------------------------------------------------
-- Basic functions.

fromStream : ∀ {i} → Stream W i → Cowriter W A i
fromStream (w ∷ ws) = w ∷ λ where .force → fromStream (ws .force)

repeat : W → Cowriter W A ∞
repeat = fromStream ∘′ Stream.repeat

length : ∀ {i} → Cowriter W A i → Conat i
length [ _ ]    = zero
length (w ∷ cw) = suc λ where .force → length (cw .force)

splitAt : ∀ (n : ℕ) → Cowriter W A ∞ → (Vec W n × Cowriter W A ∞) ⊎ (Vec≤ W n × A)
splitAt zero    cw       = inj₁ ([] , cw)
splitAt (suc n) [ a ]    = inj₂ (Vec≤.[] , a)
splitAt (suc n) (w ∷ cw) = Sum.map (Prod.map₁ (w ∷_)) (Prod.map₁ (w Vec≤.∷_))
                         $ splitAt n (cw .force)

take : ∀ (n : ℕ) → Cowriter W A ∞ → Vec W n ⊎ (Vec≤ W n × A)
take n = Sum.map₁ Prod.proj₁ ∘′ splitAt n

infixr 5 _++_ _⁺++_
_++_ : ∀ {i} → List W → Cowriter W A i → Cowriter W A i
[]       ++ ca = ca
(w ∷ ws) ++ ca = w ∷ λ where .force → ws ++ ca

_⁺++_ : ∀ {i} → List⁺ W → Thunk (Cowriter W A) i → Cowriter W A i
(w ∷ ws) ⁺++ ca = w ∷ λ where .force → ws ++ ca .force

concat : ∀ {i} → Cowriter (List⁺ W) A i → Cowriter W A i
concat [ a ]    = [ a ]
concat (w ∷ ca) = w ⁺++ λ where .force → concat (ca .force)

------------------------------------------------------------------------
-- Functor, Applicative and Monad

map : ∀ {i} → (W → X) → (A → B) → Cowriter W A i → Cowriter X B i
map f g [ a ]    = [ g a ]
map f g (w ∷ cw) = f w ∷ λ where .force → map f g (cw .force)

map₁ : ∀ {i} → (W → X) → Cowriter W A i → Cowriter X A i
map₁ f = map f id

map₂ : ∀ {i} → (A → X) → Cowriter W A i → Cowriter W X i
map₂ = map id

ap : ∀ {i} → Cowriter W (A → X) i → Cowriter W A i → Cowriter W X i
ap [ f ]    ca = map₂ f ca
ap (w ∷ cf) ca = w ∷ λ where .force → ap (cf .force) ca

_>>=_ : ∀ {i} → Cowriter W A i → (A → Cowriter W X i) → Cowriter W X i
[ a ]    >>= f = f a
(w ∷ ca) >>= f = w ∷ λ where .force → ca .force >>= f

------------------------------------------------------------------------
-- Construction.

unfold : ∀ {i} → (X → (W × X) ⊎ A) → X → Cowriter W A i
unfold next seed with next seed
... | inj₁ (w , seed') = w ∷ λ where .force → unfold next seed'
... | inj₂ a           = [ a ]

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 1.3

open import Data.BoundedVec as BVec using (BoundedVec)

splitAt′ : ∀ (n : ℕ) → Cowriter W A ∞ → (Vec W n × Cowriter W A ∞) ⊎ (BoundedVec W n × A)
splitAt′ zero    cw       = inj₁ ([] , cw)
splitAt′ (suc n) [ a ]    = inj₂ (BVec.[] , a)
splitAt′ (suc n) (w ∷ cw) = Sum.map (Prod.map₁ (w ∷_)) (Prod.map₁ (w BVec.∷_))
                         $ splitAt′ n (cw .force)
{-# WARNING_ON_USAGE splitAt′
"Warning: splitAt′ (and Data.BoundedVec) was deprecated in v1.3.
Please use splitAt (and Data.Vec.Bounded) instead."
#-}

take′ : ∀ (n : ℕ) → Cowriter W A ∞ → Vec W n ⊎ (BoundedVec W n × A)
take′ n = Sum.map₁ Prod.proj₁ ∘′ splitAt′ n
{-# WARNING_ON_USAGE take′
"Warning: take′ (and Data.BoundedVec) was deprecated in v1.3.
Please use take (and Data.Vec.Bounded) instead."
#-}


module localInstances where

open import Agda.Builtin.Nat renaming (Nat to ℕ)
open import Agda.Builtin.Bool
open import Common.String
open import Common.List

record Eq (A : Set) : Set where
  field eq : A → A → Bool

strlen : String → ℕ
strlen s = length (stringToList s)

if_then_else_ : {A : Set} → Bool → A → A → A
if true  then x else y = x
if false then x else y = y

open Eq {{...}}

instance
  eqℕ : Eq ℕ
  eqℕ = record { eq = λ x x' → x == x' }

eqString₁ : String → String → Bool
eqString₁ s₁ s₂ = strEq s₁ s₂

eqString₂ : String → String → Bool
eqString₂ s₁ s₂ = eq (strlen s₁) (strlen s₂)

testWhere : Bool → Bool
testWhere lengthEq = if eq "abcd" "dcba" then false else true
  where
    instance
      eqLocal : Eq String
      eqLocal = record { eq = if lengthEq then eqString₂ else eqString₁ }

testLet : Bool → Bool
testLet lengthEq =
  let instance
        eqLocal : Eq String
        eqLocal = record { eq = if lengthEq then eqString₂ else eqString₁ }
  in if eq "abcd" "dcba" then false else true

test1 : Bool
test1 = testWhere true

test2 : Bool
test2 = testLet true