------------------------------------------------------------------------
-- The Agda standard library
--
-- Lists, based on the Kleene star and plus, basic types and operations.
------------------------------------------------------------------------

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

module Data.List.Kleene.Base where

open import Data.Product as Product using (_×_; _,_; map₂; map₁; proj₁; proj₂)
open import Data.Nat     as ℕ       using (ℕ; suc; zero)
open import Data.Maybe   as Maybe   using (Maybe; just; nothing)
open import Data.Sum     as Sum     using (_⊎_; inj₁; inj₂)
open import Level        as Level   using (Level)

open import Algebra.Core using (Op₂)
open import Function.Base

private
  variable
    a b c : Level
    A : Set a
    B : Set b
    C : Set c

------------------------------------------------------------------------
-- Definitions
--
-- These lists are exactly equivalent to normal lists, except the "cons"
-- case is split into its own data type. This lets us write all the same
-- functions as before, but it has 2 advantages:
--
-- * Some functions are easier to express on the non-empty type. Head,
--   for instance, has a natural safe implementation. Having the
--   non-empty type be defined mutually with the normal type makes the
--   use of this non-empty type occasionally more ergonomic.
-- * It can make some proofs easier. By using the non-empty type where
--   possible, we can avoid an extra pattern match, which can really
--   simplify certain proofs.

infixr 5 _&_ ∹_

record _+ {a} (A : Set a) : Set a
data _* {a} (A : Set a) : Set a

-- Non-Empty Lists
record _+ A where
  inductive
  constructor _&_
  field
    head : A
    tail : A *

-- Possibly Empty Lists
data _* A where
  [] : A *
  ∹_ : A + → A *
open _+ public

------------------------------------------------------------------------
-- Uncons

uncons : A * → Maybe (A +)
uncons []     = nothing
uncons (∹ xs) = just xs

------------------------------------------------------------------------
-- FoldMap

foldMap+ : Op₂ B → (A → B) → A + → B
foldMap+ _∙_ f (x & [])   = f x
foldMap+ _∙_ f (x & ∹ xs) = f x ∙ foldMap+ _∙_ f xs

foldMap* : Op₂ B → B → (A → B) → A * → B
foldMap* _∙_ ε f []     = ε
foldMap* _∙_ ε f (∹ xs) = foldMap+ _∙_ f xs

------------------------------------------------------------------------
-- Folds

module _ (f : A → B → B) (b : B) where
  foldr+ : A + → B
  foldr* : A * → B

  foldr+ (x & xs) = f x (foldr* xs)

  foldr* []     = b
  foldr* (∹ xs) = foldr+ xs

module _ (f : B → A → B) where
  foldl+ : B → A + → B
  foldl* : B → A * → B

  foldl+ b (x & xs) = foldl* (f b x) xs

  foldl* b []     = b
  foldl* b (∹ xs) = foldl+ b xs

------------------------------------------------------------------------
-- Concatenation

module Concat where
  _++++_ : A + → A + → A +
  _+++*_ : A + → A * → A +
  _*+++_ : A * → A + → A +
  _*++*_ : A * → A * → A *

  head (xs +++* ys) = head xs
  tail (xs +++* ys) = tail xs *++* ys

  xs *++* ys = foldr* (λ x zs → ∹ x & zs) ys xs

  xs ++++ ys = foldr+ (λ x zs → x & ∹ zs) ys xs

  []     *+++ ys = ys
  (∹ xs) *+++ ys = xs ++++ ys
open Concat public using () renaming (_++++_ to _+++_; _*++*_ to _++*_)

------------------------------------------------------------------------
-- Mapping

module _  (f : A → B) where
  map+ : A + → B +
  map* : A * → B *

  head (map+ xs) = f (head xs)
  tail (map+ xs) = map* (tail xs)

  map* []     = []
  map* (∹ xs) = ∹ map+ xs

module _ (f : A → Maybe B) where
  mapMaybe+ : A + → B *
  mapMaybe* : A * → B *

  mapMaybe+ (x & xs) with f x
  ... | just y  = ∹ y & mapMaybe* xs
  ... | nothing = mapMaybe* xs

  mapMaybe* []     = []
  mapMaybe* (∹ xs) = mapMaybe+ xs

------------------------------------------------------------------------
-- Applicative Operations

pure+ : A → A +
head (pure+ x) = x
tail (pure+ x) = []

pure* : A → A *
pure* x = ∹ pure+ x

module Apply where
  _*<*>*_ : (A → B) * → A * → B *
  _+<*>*_ : (A → B) + → A * → B *
  _*<*>+_ : (A → B) * → A + → B *
  _+<*>+_ : (A → B) + → A + → B +

  []     *<*>* xs = []
  (∹ fs) *<*>* xs = fs +<*>* xs

  fs +<*>* xs = map* (head fs) xs ++* (tail fs *<*>* xs)

  []     *<*>+ xs = []
  (∹ fs) *<*>+ xs = ∹ fs +<*>+ xs

  fs +<*>+ xs = map+ (head fs) xs Concat.+++* (tail fs *<*>+ xs)
open Apply public using () renaming (_*<*>*_ to _<*>*_; _+<*>+_ to _<*>+_)

------------------------------------------------------------------------
-- Monadic Operations

module Bind where
  _+>>=+_ : A + → (A → B +) → B +
  _+>>=*_ : A + → (A → B *) → B *
  _*>>=+_ : A * → (A → B +) → B *
  _*>>=*_ : A * → (A → B *) → B *

  (x & xs) +>>=+ k = k x Concat.+++* (xs *>>=+ k)

  (x & xs) +>>=* k = k x Concat.*++* (xs *>>=* k)

  []     *>>=* k = []
  (∹ xs) *>>=* k = xs +>>=* k

  []     *>>=+ k = []
  (∹ xs) *>>=+ k = ∹ xs +>>=+ k
open Bind public using () renaming (_*>>=*_ to _>>=*_; _+>>=+_ to _>>=+_)

------------------------------------------------------------------------
-- Scans

module Scanr (f : A → B → B) (b : B) where
  cons : A → B + → B +
  head (cons x xs) = f x (head xs)
  tail (cons x xs) = ∹ xs

  scanr+ : A + → B +
  scanr* : A * → B +

  scanr* = foldr* cons (b & [])
  scanr+ = foldr+ cons (b & [])
open Scanr public using (scanr+; scanr*)

module _ (f : B → A → B) where
  scanl* : B → A * → B +
  head (scanl* b xs)     = b
  tail (scanl* b [])     = []
  tail (scanl* b (∹ xs)) = ∹ scanl* (f b (head xs)) (tail xs)

  scanl+ : B → A + → B +
  head (scanl+ b xs) = b
  tail (scanl+ b xs) = ∹ scanl* (f b (head xs)) (tail xs)

  scanl₁ : B → A + → B +
  scanl₁ b xs = scanl* (f b (head xs)) (tail xs)

------------------------------------------------------------------------
-- Accumulating maps

module _ (f : B → A → (B × C)) where
  mapAccumˡ* : B → A * → (B × C *)
  mapAccumˡ+ : B → A + → (B × C +)

  mapAccumˡ* b []     = b , []
  mapAccumˡ* b (∹ xs) = map₂ ∹_ (mapAccumˡ+ b xs)

  mapAccumˡ+ b (x & xs) =
    let y , ys = f b x
        z , zs = mapAccumˡ* y xs
    in z , ys & zs

module _ (f : A → B → (C × B)) (b : B) where
  mapAccumʳ* : A * → (C * × B)
  mapAccumʳ+ : A + → (C + × B)

  mapAccumʳ* [] = [] , b
  mapAccumʳ* (∹ xs) = map₁ ∹_ (mapAccumʳ+ xs)

  mapAccumʳ+ (x & xs) =
    let ys , y = mapAccumʳ* xs
        zs , z = f x y
    in zs & ys , z

------------------------------------------------------------------------
-- Non-Empty Folds

last : A + → A
last (x & [])   = x
last (_ & ∹ xs) = last xs

module _ (f : A → A → A) where
  foldr₁ : A + → A
  foldr₁ (x & [])   = x
  foldr₁ (x & ∹ xs) = f x (foldr₁ xs)

  foldl₁ : A + → A
  foldl₁ (x & xs) = foldl* f x xs

module _ (f : A → Maybe B → B) where
  foldrMaybe* : A * → Maybe B
  foldrMaybe+ : A + → B

  foldrMaybe* []     = nothing
  foldrMaybe* (∹ xs) = just (foldrMaybe+ xs)

  foldrMaybe+ xs = f (head xs) (foldrMaybe* (tail xs))

------------------------------------------------------------------------
-- Indexing

_[_]* : A * → ℕ → Maybe A
_[_]+ : A + → ℕ → Maybe A

[]     [ _ ]* = nothing
(∹ xs) [ i ]* = xs [ i ]+

xs [ zero  ]+ = just (head xs)
xs [ suc i ]+ = tail xs [ i ]*

applyUpTo* : (ℕ → A) → ℕ → A *
applyUpTo+ : (ℕ → A) → ℕ → A +

applyUpTo* f zero    = []
applyUpTo* f (suc n) = ∹ applyUpTo+ f n

head (applyUpTo+ f n) = f zero
tail (applyUpTo+ f n) = applyUpTo* (f ∘ suc) n

upTo* : ℕ → ℕ *
upTo* = applyUpTo* id

upTo+ : ℕ → ℕ +
upTo+ = applyUpTo+ id

------------------------------------------------------------------------
-- Manipulation

module ZipWith (f : A → B → C) where
  +zipWith+ : A + → B + → C +
  *zipWith+ : A * → B + → C *
  +zipWith* : A + → B * → C *
  *zipWith* : A * → B * → C *

  head (+zipWith+ xs ys) = f (head xs) (head ys)
  tail (+zipWith+ xs ys) = *zipWith* (tail xs) (tail ys)

  *zipWith+ [] ys     = []
  *zipWith+ (∹ xs) ys = ∹ +zipWith+ xs ys

  +zipWith* xs []     = []
  +zipWith* xs (∹ ys) = ∹ +zipWith+ xs ys

  *zipWith* [] ys     = []
  *zipWith* (∹ xs) ys = +zipWith* xs ys
open ZipWith public renaming (+zipWith+ to zipWith+; *zipWith* to zipWith*)

module Unzip (f : A → B × C) where
  cons : B × C → B * × C * → B + × C +
  cons = Product.zip′ _&_ _&_

  unzipWith* : A * → B * × C *
  unzipWith+ : A + → B + × C +

  unzipWith* = foldr* (λ x xs → Product.map ∹_ ∹_ (cons (f x) xs)) ([] , [])

  unzipWith+ xs = cons (f (head xs)) (unzipWith* (tail xs))
open Unzip using (unzipWith+; unzipWith*) public

module Partition (f : A → B ⊎ C) where
  cons : B ⊎ C → B * × C * → B * × C *
  proj₁ (cons (inj₁ x) xs) = ∹ x & proj₁ xs
  proj₂ (cons (inj₁ x) xs) = proj₂ xs
  proj₂ (cons (inj₂ x) xs) = ∹ x & proj₂ xs
  proj₁ (cons (inj₂ x) xs) = proj₁ xs

  partitionSumsWith* : A * → B * × C *
  partitionSumsWith+ : A + → B * × C *

  partitionSumsWith* = foldr* (cons ∘ f) ([] , [])

  partitionSumsWith+ = foldr+ (cons ∘ f) ([] , [])
open Partition using (partitionSumsWith+; partitionSumsWith*) public

tails* : A * → (A +) *
tails+ : A + → (A +) +

head (tails+ xs) = xs
tail (tails+ xs) = tails* (tail xs)

tails* []     = []
tails* (∹ xs) = ∹ tails+ xs

reverse* : A * → A *
reverse* = foldl* (λ xs x → ∹ x & xs) []

reverse+ : A + → A +
reverse+ (x & xs) = foldl* (λ ys y → y & ∹ ys) (x & []) xs


module Implicits.Resolution.Ambiguous.SystemFEquiv where

open import Prelude

open import Function.Equivalence using (_⇔_; equivalence)
open import Data.List.Properties
open import Relation.Binary.HeterogeneousEquality as H using ()
open import Data.Vec.Properties as VP using ()
open import Data.Vec hiding ([_])
open import Extensions.Vec
open import Data.List as List using ()
open import Data.Product hiding (map)

open import Implicits.Syntax
open import Implicits.Resolution.Ambiguous.Resolution
open import Implicits.Resolution.Embedding
open import Implicits.Resolution.Embedding.Lemmas

open import SystemF.Everything
     as F using ()

⟦_⟧term→ : ∀ {ν} {Δ : ICtx ν} {r} → Δ ⊢ᵣ r → F.Term ν (List.length (List.map ⟦_⟧tp→ Δ))
⟦_⟧term→ {ν} {Δ} (r-tabs x) = F.Λ (subst (F.Term (suc ν)) (length-weaken-Δ Δ) ⟦ x ⟧term→)
⟦ r-tapp a x ⟧term→ = ⟦ x ⟧term→ F.[ ⟦ a ⟧tp→ ]
⟦_⟧term→ {Δ = Δ} (r-ivar x) =
  F.var (subst Fin (sym $ length-map _ Δ) (proj₁ $ ∈⟶index (VP.List-∈⇒∈ x)))
⟦ r-iabs {a = a} x ⟧term→ = F.λ' ⟦ a ⟧tp→ ⟦ x ⟧term→
⟦ r-iapp f e ⟧term→ = ⟦ f ⟧term→ F.· ⟦ e ⟧term→

from-⊢ : ∀ {ν n t a} {Γ : F.Ctx ν n} → Γ F.⊢ t ∈ a → ⟦ Γ ⟧ctx← ⊢ᵣ ⟦ a ⟧tp←
from-⊢ (F.var x) = r-ivar (lookup-∈ x _)
from-⊢ {Γ = Γ} (F.Λ x) = r-tabs (subst (λ u → u ⊢ᵣ _) (⟦weaken⟧ctx← Γ) (from-⊢ x))
from-⊢ (F.λ' {b = b} a x) = r-iabs (from-⊢ x)
from-⊢ {Γ = Γ} (F._[_] {a = a} x b) = subst
  (λ u → ⟦ Γ ⟧ctx← ⊢ᵣ u)
  (sym (⟦a/sub⟧tp← a b))
  (r-tapp ⟦ b ⟧tp← (from-⊢ x))
from-⊢ (a F.· b) = r-iapp (from-⊢ a) (from-⊢ b)

to-⊢ : ∀ {ν} {Δ : ICtx ν} {r} → (p : Δ ⊢ᵣ r) → ⟦ Δ ⟧ctx→ F.⊢ ⟦ p ⟧term→ ∈ ⟦ r ⟧tp→
to-⊢ {Δ = Δ} (r-tabs {r = r} p) with to-⊢ p
to-⊢ {Δ = Δ} (r-tabs {r = r} p) | x =
  F.Λ (⊢subst-n (length-weaken-Δ Δ) (H.sym (⟦weaken⟧ctx→ Δ)) x)
to-⊢ (r-tapp a p) with to-⊢ p
to-⊢ {Δ = Δ} (r-tapp {r = a} b p) | x =
  subst
    (λ u → ⟦ Δ ⟧ctx→ F.⊢ ⟦ p ⟧term→ F.[ ⟦ b ⟧tp→ ] ∈ u)
    (sym $ ⟦a/sub⟧tp→ a b)
    (x F.[ ⟦ b ⟧tp→ ])
to-⊢ {Δ = Δ} {r = r} (r-ivar x) =
  let i , eq = ∈⟶index (VP.List-∈⇒∈ x) in
      let i' = (subst Fin (sym $ length-map _ Δ) i) in
        subst (λ u → ⟦ Δ ⟧ctx→ F.⊢ (F.var i') ∈ u) (lookup⟦⟧ Δ i eq) (F.var i')
to-⊢ (r-iabs {a = a} p) = F.λ' ⟦ a ⟧tp→ (to-⊢ p)
to-⊢ (r-iapp p p₁) = (to-⊢ p) F.· (to-⊢ p₁)

equivalent : ∀ {ν} (Δ : ICtx ν) r → Δ ⊢ᵣ r ⇔ (∃ λ t → ⟦ Δ ⟧ctx→ F.⊢ t ∈ ⟦ r ⟧tp→)
equivalent Δ r = equivalence
  (λ x → , (to-⊢ x))
  (λ x → subst₂ (λ Δ' r' → Δ' ⊢ᵣ r') (ctx→← _) (tp→← r) (from-⊢ (proj₂ x)))

⊢subst : ∀ {ν n n'} {a a' : F.Type ν} {Γ : F.Ctx ν n} {Γ' : F.Ctx ν n'} →
          n ≡ n' → a ≡ a' → Γ H.≅ Γ' →
          (∃ λ t → Γ F.⊢ t ∈ a) →
          ∃ λ t → Γ' F.⊢ t ∈ a'
⊢subst refl refl H.refl p = p

equivalent' : ∀ {ν n} (Γ : F.Ctx ν n) r → ⟦ Γ ⟧ctx← ⊢ᵣ ⟦ r ⟧tp← ⇔ (∃ λ t → Γ F.⊢ t ∈ r)
equivalent' Γ r = equivalence
  (λ x → ⊢subst
    (length-map-toList (map ⟦_⟧tp← Γ)) (tp←→ r) (ctx←→ Γ) (, to-⊢ x))
  (λ x → (from-⊢ (proj₂ x)))


open import Data.Product using ( ∃ ; _×_ ; _,_ ; proj₁ ; proj₂ )
open import Relation.Unary using ( _∈_ )
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl ; cong )
open import Web.Semantic.DL.ABox.Interp using ( Interp ; _,_ ; ⌊_⌋ ; ind ; _*_ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox.Interp using
  ( Δ ; _⊨_≈_ ; ≈-refl ; ≈-sym ; ≈-trans ) renaming
  ( Interp to Interp′ )
open import Web.Semantic.DL.TBox.Interp.Morphism using
  ( ≲-image ; ≃-image ; ≃-image⁻¹ ; ≲-resp-≈ ; ≃-resp-≈ ; ≃-iso ) renaming
  ( _≲_ to _≲′_ ; _≃_ to _≃′_
  ; ≲-refl to ≲′-refl ; ≲-trans to ≲′-trans
  ; ≃-refl to ≃′-refl ; ≃-sym to ≃′-sym ; ≃-trans to ≃′-trans
  ; ≃-impl-≲ to ≃-impl-≲′ )
open import Web.Semantic.Util using
  ( _∘_ ; _⊕_⊕_ ; inode ; bnode ; enode ; →-dist-⊕ )

module Web.Semantic.DL.ABox.Interp.Morphism {Σ : Signature} where

infix 2 _≲_ _≃_ _≋_
infixr 4 _,_
infix 5 _**_

data _≲_ {X} (I J : Interp Σ X) : Set where
  _,_ : ∀ (I≲J : ⌊ I ⌋ ≲′ ⌊ J ⌋) →
    (i≲j : ∀ x → (⌊ J ⌋ ⊨ ≲-image I≲J (ind I x) ≈ ind J x)) →
      (I ≲ J)

≲⌊_⌋ : ∀ {X} {I J : Interp Σ X} → (I ≲ J) → (⌊ I ⌋ ≲′ ⌊ J ⌋)
≲⌊_⌋ (I≲J , i≲j) = I≲J

≲-resp-ind : ∀ {X} {I J : Interp Σ X} → (I≲J : I ≲ J) →
  ∀ x → (⌊ J ⌋ ⊨ ≲-image ≲⌊ I≲J ⌋ (ind I x) ≈ ind J x)
≲-resp-ind (I≲J , i≲j) = i≲j

≲-refl : ∀ {X} (I : Interp Σ X) → (I ≲ I)
≲-refl I = (≲′-refl ⌊ I ⌋ , λ x → ≈-refl ⌊ I ⌋)

≲-trans : ∀ {X} {I J K : Interp Σ X} → (I ≲ J) → (J ≲ K) → (I ≲ K)
≲-trans {X} {I} {J} {K} I≲J J≲K =
  (≲′-trans ≲⌊ I≲J ⌋ ≲⌊ J≲K ⌋ , λ x → ≈-trans ⌊ K ⌋ (≲-resp-≈ ≲⌊ J≲K ⌋ (≲-resp-ind I≲J x)) (≲-resp-ind J≲K x))

≡-impl-≈ : ∀ {X : Set} (I : Interp′ Σ) {i j : X → Δ I} →
  (i ≡ j) → (∀ x → I ⊨ i x ≈ j x)
≡-impl-≈ I refl x = ≈-refl I

≡³-impl-≈ : ∀ {V X Y : Set} (I : Interp′ Σ) (i j : (X ⊕ V ⊕ Y) → Δ I) →
  (→-dist-⊕ i ≡ →-dist-⊕ j) → (∀ x → I ⊨ i x ≈ j x)
≡³-impl-≈ I i j i≡j (inode x) = ≡-impl-≈ I (cong proj₁ i≡j) x
≡³-impl-≈ I i j i≡j (bnode v) = ≡-impl-≈ I (cong proj₁ (cong proj₂ i≡j)) v
≡³-impl-≈ I i j i≡j (enode y) = ≡-impl-≈ I (cong proj₂ (cong proj₂ i≡j)) y

≡-impl-≲ : ∀ {X : Set} (I : Interp Σ X) j →
  (ind I ≡ j) → (I ≲ (⌊ I ⌋ , j))
≡-impl-≲ (I , i) .i refl = ≲-refl (I , i)

≡³-impl-≲ : ∀ {V X Y : Set} (I : Interp Σ (X ⊕ V ⊕ Y)) j →
  (→-dist-⊕ (ind I) ≡ →-dist-⊕ j) → (I ≲ (⌊ I ⌋ , j))
≡³-impl-≲ (I , i) j i≡j = (≲′-refl I , ≡³-impl-≈ I i j i≡j)

_**_ : ∀ {X Y I J} (f : Y → X) → (I ≲ J) → (f * I ≲ f * J)
f ** I≲J = (≲⌊ I≲J ⌋ , λ x → ≲-resp-ind I≲J (f x))

_≋_ : ∀ {X} {I J : Interp Σ X} → (I ≲ J) → (I ≲ J) → Set
_≋_ {X} {I} {J} I≲₁J I≲₂J = ∀ x → (⌊ J ⌋ ⊨ ≲-image ≲⌊ I≲₁J ⌋ x ≈ ≲-image ≲⌊ I≲₂J ⌋ x)

data _≃_ {X} (I J : Interp Σ X) : Set₁ where
  _,_ : (I≃J : ⌊ I ⌋ ≃′ ⌊ J ⌋) →
    (∀ x → (⌊ J ⌋ ⊨ ≃-image I≃J (ind I x) ≈ ind J x)) →
      (I  ≃ J)

≃⌊_⌋ : ∀ {X} {I J : Interp Σ X} → (I ≃ J) → (⌊ I ⌋ ≃′ ⌊ J ⌋)
≃⌊_⌋ (I≃J , i≃j) = I≃J

≃-impl-≲ : ∀ {X} {I J : Interp Σ X} → (I ≃ J) → (I ≲ J)
≃-impl-≲ (I≃J , i≃j) = (≃-impl-≲′ I≃J , i≃j)

≃-resp-ind : ∀ {X} {I J : Interp Σ X} → (I≃J : I ≃ J) →
  ∀ x → (⌊ J ⌋ ⊨ ≃-image ≃⌊ I≃J ⌋ (ind I x) ≈ ind J x)
≃-resp-ind (I≃J , i≃j) = i≃j

≃-resp-ind⁻¹ : ∀ {X} {I J : Interp Σ X} → (I≃J : I ≃ J) →
  ∀ x → (⌊ I ⌋ ⊨ ≃-image⁻¹ ≃⌊ I≃J ⌋ (ind J x) ≈ ind I x)
≃-resp-ind⁻¹ {X} {I , i} {J , j} (I≃J , i≃j) x =
  ≈-trans I (≃-resp-≈ (≃′-sym I≃J) (≈-sym J (i≃j x))) (≃-iso I≃J (i x))

≃-refl : ∀ {X} (I : Interp Σ X) → (I ≃ I)
≃-refl (I , i) = (≃′-refl I , λ x → ≈-refl I)

≃-symm : ∀ {X} {I J : Interp Σ X} → (I ≃ J) → (J ≃ I)
≃-symm {X} {I , i} {J , j} (I≃J , i≃j) =
  (≃′-sym I≃J , ≃-resp-ind⁻¹ {X} {I , i} {J , j} (I≃J , i≃j))

≃-trans : ∀ {X} {I J K : Interp Σ X} → (I ≃ J) → (J ≃ K) → (I ≃ K)
≃-trans {X} {I , i} {J , j} {K , k} (I≃J , i≃j) (J≃K , j≃k) =
  (≃′-trans I≃J J≃K , λ x → ≈-trans K (≃-resp-≈ J≃K (i≃j x)) (j≃k x))


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

open import HoTT
open import homotopy.EilenbergMacLane
open import homotopy.EilenbergMacLane1
open import homotopy.EilenbergMacLaneFunctor
open import homotopy.EM1HSpaceAssoc
open import lib.types.TwoSemiCategory
open import lib.two-semi-categories.FunCategory
open import lib.two-semi-categories.FundamentalCategory

module cohomology.CupProduct.OnEM.InLowDegrees {i} {j} (G : AbGroup i) (H : AbGroup j) where

private
  module G = AbGroup G
  module H = AbGroup H
  module G⊗H = TensorProduct G H
  open G⊗H using (_⊗_)
  open EMExplicit

⊙×-cp₀₀' : G.⊙El ⊙× H.⊙El ⊙→ G⊗H.⊙El
⊙×-cp₀₀' = uncurry G⊗H._⊗_ , G⊗H.⊗-unit-l H.ident

⊙×-cp₀₀-seq : (⊙EM G 0 ⊙× ⊙EM H 0) ⊙–→ ⊙EM G⊗H.abgroup 0
⊙×-cp₀₀-seq =
  ⊙–> (⊙emloop-equiv G⊗H.grp) ◃⊙∘
  ⊙×-cp₀₀' ◃⊙∘
  ⊙×-fmap (⊙<– (⊙emloop-equiv G.grp)) (⊙<– (⊙emloop-equiv H.grp)) ◃⊙idf

⊙×-cp₀₀ : ⊙EM G 0 ⊙× ⊙EM H 0 ⊙→ ⊙EM G⊗H.abgroup 0
⊙×-cp₀₀ = ⊙compose ⊙×-cp₀₀-seq

×-cp₀₀ : EM G 0 × EM H 0 → EM G⊗H.abgroup 0
×-cp₀₀ = fst ⊙×-cp₀₀

open EM₁HSpaceAssoc G⊗H.abgroup hiding (comp-functor) renaming (mult to EM₁-mult) public

cp₀₁ : G.El → EM₁ H.grp → EM₁ G⊗H.grp
cp₀₁ g = EM₁-fmap (G⊗H.ins-r-hom g)

abstract
  cp₀₁-emloop-β : ∀ g h → ap (cp₀₁ g) (emloop h) == emloop (g ⊗ h)
  cp₀₁-emloop-β g = EM₁-fmap-emloop-β (G⊗H.ins-r-hom g)

cp₀₁-distr-l : (g₁ g₂ : G.El) (y : EM₁ H.grp)
  → cp₀₁ (G.comp g₁ g₂) y == EM₁-mult (cp₀₁ g₁ y) (cp₀₁ g₂ y)
cp₀₁-distr-l g₁ g₂ =
  EM₁-set-elim
    {P = λ x → f x == g x}
    {{λ x → has-level-apply (EM₁-level₁ G⊗H.grp) _ _}}
    idp loop'
  where
  f : EM₁ H.grp → EM₁ G⊗H.grp
  f x = cp₀₁ (G.comp g₁ g₂) x
  g : EM₁ H.grp → EM₁ G⊗H.grp
  g x = EM₁-mult (cp₀₁ g₁ x) (cp₀₁ g₂ x)
  abstract
    loop' : (h : H.El) → idp == idp [ (λ x → f x == g x) ↓ emloop h ]
    loop' h = ↓-='-in {f = f} {g = g} {p = emloop h} {u = idp} {v = idp} $
      idp ∙' ap g (emloop h)
        =⟨ ∙'-unit-l (ap g (emloop h)) ⟩
      ap g (emloop h)
        =⟨ ! (ap2-diag (λ x y → EM₁-mult (cp₀₁ g₁ x) (cp₀₁ g₂ y)) (emloop h)) ⟩
      ap2 (λ x y → EM₁-mult (cp₀₁ g₁ x) (cp₀₁ g₂ y)) (emloop h) (emloop h)
        =⟨ =ₛ-out $ ap2-out (λ x y → EM₁-mult (cp₀₁ g₁ x) (cp₀₁ g₂ y)) (emloop h) (emloop h) ⟩
      ap (λ x → EM₁-mult (cp₀₁ g₁ x) embase) (emloop h) ∙ ap (cp₀₁ g₂) (emloop h)
        =⟨ ap2 _∙_ part (cp₀₁-emloop-β g₂ h) ⟩
      emloop (g₁ ⊗ h) ∙ emloop (g₂ ⊗ h)
        =⟨ ! (emloop-comp (g₁ ⊗ h) (g₂ ⊗ h)) ⟩
      emloop (G⊗H.comp (g₁ ⊗ h) (g₂ ⊗ h))
        =⟨ ap emloop (! (G⊗H.⊗-lin-l g₁ g₂ h)) ⟩
      emloop (G.comp g₁ g₂ ⊗ h)
        =⟨ ! (cp₀₁-emloop-β (G.comp g₁ g₂) h) ⟩
      ap f (emloop h)
        =⟨ ! (∙-unit-r (ap f (emloop h))) ⟩
      ap f (emloop h) ∙ idp =∎
      where
      part : ap (λ x → EM₁-mult (cp₀₁ g₁ x) embase) (emloop h) == emloop (g₁ ⊗ h)
      part =
        ap (λ x → EM₁-mult (cp₀₁ g₁ x) embase) (emloop h)
          =⟨ ap-∘ (λ u → EM₁-mult u embase) (cp₀₁ g₁) (emloop h) ⟩
        ap (λ u → EM₁-mult u embase) (ap (cp₀₁ g₁) (emloop h))
          =⟨ ap (ap (λ u → EM₁-mult u embase)) (cp₀₁-emloop-β g₁ h) ⟩
        ap (λ u → EM₁-mult u embase) (emloop (g₁ ⊗ h))
          =⟨ mult-emloop-β (g₁ ⊗ h) embase ⟩
        emloop (g₁ ⊗ h) =∎

module _ (g₁ g₂ g₃ : G.El) (y : EM₁ H.grp) where

  cp₀₁-distr-l₁ : cp₀₁ (G.comp (G.comp g₁ g₂) g₃) y =-=
                  EM₁-mult (cp₀₁ g₁ y) (EM₁-mult (cp₀₁ g₂ y) (cp₀₁ g₃ y))
  cp₀₁-distr-l₁ =
    cp₀₁ (G.comp (G.comp g₁ g₂) g₃) y
      =⟪ cp₀₁-distr-l (G.comp g₁ g₂) g₃ y ⟫
    EM₁-mult (cp₀₁ (G.comp g₁ g₂) y) (cp₀₁ g₃ y)
      =⟪ ap (λ s → EM₁-mult s (cp₀₁ g₃ y)) (cp₀₁-distr-l g₁ g₂ y) ⟫
    EM₁-mult (EM₁-mult (cp₀₁ g₁ y) (cp₀₁ g₂ y)) (cp₀₁ g₃ y)
      =⟪ H-⊙EM₁-assoc (cp₀₁ g₁ y) (cp₀₁ g₂ y) (cp₀₁ g₃ y) ⟫
    EM₁-mult (cp₀₁ g₁ y) (EM₁-mult (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ∎∎

  cp₀₁-distr-l₂ : cp₀₁ (G.comp (G.comp g₁ g₂) g₃) y =-=
                  EM₁-mult (cp₀₁ g₁ y) (EM₁-mult (cp₀₁ g₂ y) (cp₀₁ g₃ y))
  cp₀₁-distr-l₂ =
    cp₀₁ (G.comp (G.comp g₁ g₂) g₃) y
      =⟪ ap (λ s → cp₀₁ s y) (G.assoc g₁ g₂ g₃) ⟫
    cp₀₁ (G.comp g₁ (G.comp g₂ g₃)) y
      =⟪ cp₀₁-distr-l g₁ (G.comp g₂ g₃) y ⟫
    EM₁-mult (cp₀₁ g₁ y) (cp₀₁ (G.comp g₂ g₃) y)
      =⟪ ap (EM₁-mult (cp₀₁ g₁ y)) (cp₀₁-distr-l g₂ g₃ y) ⟫
    EM₁-mult (cp₀₁ g₁ y) (EM₁-mult (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ∎∎

abstract
  cp₀₁-distr-l-coh : (g₁ g₂ g₃ : G.El) (y : EM₁ H.grp)
    → cp₀₁-distr-l₁ g₁ g₂ g₃ y =ₛ cp₀₁-distr-l₂ g₁ g₂ g₃ y
  cp₀₁-distr-l-coh g₁ g₂ g₃ =
    EM₁-prop-elim
      {P = λ y → cp₀₁-distr-l₁ g₁ g₂ g₃ y =ₛ cp₀₁-distr-l₂ g₁ g₂ g₃ y}
      {{λ y → =ₛ-level (EM₁-level₁ G⊗H.grp)}} $
    idp ◃∙ idp ◃∙ idp ◃∎
      =ₛ₁⟨ 0 & 1 & ! (ap-cst embase (G.assoc g₁ g₂ g₃)) ⟩
    ap (cst embase) (G.assoc g₁ g₂ g₃) ◃∙ idp ◃∙ idp ◃∎ ∎ₛ

cp₀₁-functor :
  TwoSemiFunctor
    (group-to-cat G.grp)
    (fun-cat (EM₁ H.grp) EM₁-2-semi-category)
cp₀₁-functor =
  record
  { F₀ = λ _ _ → unit
  ; F₁ = λ g x → cp₀₁ g x
  ; pres-comp = λ g₁ g₂ → λ= (cp₀₁-distr-l g₁ g₂)
  ; pres-comp-coh = λ g₁ g₂ g₃ → pres-comp-coh g₁ g₂ g₃
    -- TODO: for some reason, this last field takes a really long time to check
    -- it is recommended to comment it out
  }
  where
  abstract
    pres-comp-coh : ∀ g₁ g₂ g₃ →
      λ= (cp₀₁-distr-l (G.comp g₁ g₂) g₃) ◃∙
      ap (λ s y → EM₁-mult (s y) (cp₀₁ g₃ y)) (λ= (cp₀₁-distr-l g₁ g₂)) ◃∙
      λ= (λ y → H-⊙EM₁-assoc (cp₀₁ g₁ y) (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ◃∎
      =ₛ
      ap (λ s → cp₀₁ s) (G.assoc g₁ g₂ g₃) ◃∙
      λ= (cp₀₁-distr-l g₁ (G.comp g₂ g₃)) ◃∙
      ap (λ s y → EM₁-mult (cp₀₁ g₁ y) (s y)) (λ= (cp₀₁-distr-l g₂ g₃)) ◃∎
    pres-comp-coh g₁ g₂ g₃ =
      λ= (cp₀₁-distr-l (G.comp g₁ g₂) g₃) ◃∙
      ap (λ s y → EM₁-mult (s y) (cp₀₁ g₃ y)) (λ= (cp₀₁-distr-l g₁ g₂)) ◃∙
      λ= (λ y → H-⊙EM₁-assoc (cp₀₁ g₁ y) (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ◃∎
        =ₛ₁⟨ 1 & 1 & λ=-ap (λ y s → EM₁-mult s (cp₀₁ g₃ y)) (cp₀₁-distr-l g₁ g₂) ⟩
      λ= (cp₀₁-distr-l (G.comp g₁ g₂) g₃) ◃∙
      λ= (λ y → ap (λ s → EM₁-mult s (cp₀₁ g₃ y)) (cp₀₁-distr-l g₁ g₂ y)) ◃∙
      λ= (λ y → H-⊙EM₁-assoc (cp₀₁ g₁ y) (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ◃∎
        =ₛ⟨ ∙∙-λ= (cp₀₁-distr-l (G.comp g₁ g₂) g₃)
                  (λ y → ap (λ s → EM₁-mult s (cp₀₁ g₃ y)) (cp₀₁-distr-l g₁ g₂ y))
                  (λ y → H-⊙EM₁-assoc (cp₀₁ g₁ y) (cp₀₁ g₂ y) (cp₀₁ g₃ y)) ⟩
      λ= (λ y → ↯ (cp₀₁-distr-l₁ g₁ g₂ g₃ y)) ◃∎
        =ₛ₁⟨ ap λ= (λ= (λ y → =ₛ-out (cp₀₁-distr-l-coh g₁ g₂ g₃ y))) ⟩
      λ= (λ y → ↯ (cp₀₁-distr-l₂ g₁ g₂ g₃ y)) ◃∎
        =ₛ⟨ λ=-∙∙ (λ y → ap (λ s → cp₀₁ s y) (G.assoc g₁ g₂ g₃))
                  (cp₀₁-distr-l g₁ (G.comp g₂ g₃))
                  (λ y → ap (EM₁-mult (cp₀₁ g₁ y)) (cp₀₁-distr-l g₂ g₃ y)) ⟩
      λ= (λ y → ap (λ s → cp₀₁ s y) (G.assoc g₁ g₂ g₃)) ◃∙
      λ= (cp₀₁-distr-l g₁ (G.comp g₂ g₃)) ◃∙
      λ= (λ y → ap (EM₁-mult (cp₀₁ g₁ y)) (cp₀₁-distr-l g₂ g₃ y)) ◃∎
        =ₛ₁⟨ 0 & 1 & ap λ= (λ= (λ y → ap-∘ (λ f → f y) cp₀₁ (G.assoc g₁ g₂ g₃))) ⟩
      λ= (app= (ap (λ s → cp₀₁ s) (G.assoc g₁ g₂ g₃))) ◃∙
      λ= (cp₀₁-distr-l g₁ (G.comp g₂ g₃)) ◃∙
      λ= (λ y → ap (EM₁-mult (cp₀₁ g₁ y)) (cp₀₁-distr-l g₂ g₃ y)) ◃∎
        =ₛ₁⟨ 0 & 1 & ! (λ=-η (ap (λ s → cp₀₁ s) (G.assoc g₁ g₂ g₃))) ⟩
      ap (λ s → cp₀₁ s) (G.assoc g₁ g₂ g₃) ◃∙
      λ= (cp₀₁-distr-l g₁ (G.comp g₂ g₃)) ◃∙
      λ= (λ y → ap (EM₁-mult (cp₀₁ g₁ y)) (cp₀₁-distr-l g₂ g₃ y)) ◃∎
        =ₛ₁⟨ 2 & 1 & ! (λ=-ap (λ y s → EM₁-mult (cp₀₁ g₁ y) s) (cp₀₁-distr-l g₂ g₃)) ⟩
      ap (λ s → cp₀₁ s) (G.assoc g₁ g₂ g₃) ◃∙
      λ= (cp₀₁-distr-l g₁ (G.comp g₂ g₃)) ◃∙
      ap (λ s y → EM₁-mult (cp₀₁ g₁ y) (s y)) (λ= (cp₀₁-distr-l g₂ g₃)) ◃∎ ∎ₛ

comp-functor :
  TwoSemiFunctor
    EM₁-2-semi-category
    (=ₜ-fundamental-cat (Susp (EM₁ G⊗H.grp)))
comp-functor =
  record
  { F₀ = λ _ → [ north ]
  ; F₁ = λ x → [ η x ]
  ; pres-comp = comp
  ; pres-comp-coh = comp-coh
  }
  -- this is *exactly* the same as
  --   `EM₁HSpaceAssoc.comp-functor G⊗H.abgroup`
  -- inlined but Agda chokes on this shorter definition

module _ where

  private
    T : TwoSemiCategory (lmax i j) (lmax i j)
    T = fun-cat (EM₁ H.grp) (=ₜ-fundamental-cat (Susp (EM₁ G⊗H.grp)))

    module T = TwoSemiCategory T
    cst-north : T.El
    cst-north = λ _ → [ north ]
    T-comp' = T.comp {x = cst-north} {y = cst-north} {z = cst-north}
    T-assoc' = T.assoc {x = cst-north} {y = cst-north} {z = cst-north} {w = cst-north}

  group-to-EM₁→EM₂ : TwoSemiFunctor (group-to-cat G.grp) T
  group-to-EM₁→EM₂ =
    cp₀₁-functor –F→
    fun-functor-map (EM₁ H.grp) comp-functor

  abstract
    app=-group-to-EM₁→EM₂-pres-comp-embase : ∀ g₁ g₂ →
      app= (TwoSemiFunctor.pres-comp group-to-EM₁→EM₂ g₁ g₂) embase ==
      comp embase embase
    app=-group-to-EM₁→EM₂-pres-comp-embase g₁ g₂ = =ₛ-out $
      app= (TwoSemiFunctor.pres-comp group-to-EM₁→EM₂ g₁ g₂) embase ◃∎
        =ₛ⟨ ap-seq-=ₛ (λ f → f embase) (comp-functors-pres-comp-β cp₀₁-functor G₁₂ g₁ g₂) ⟩
      app= (ap (TwoSemiFunctor.F₁ G₁₂) (TwoSemiFunctor.pres-comp cp₀₁-functor g₁ g₂)) embase ◃∙
      app= (TwoSemiFunctor.pres-comp G₁₂ (cp₀₁ g₁) (cp₀₁ g₂)) embase ◃∎
        =ₛ⟨ 0 & 1 & =ₛ-in {t = []} $
            app= (ap (λ f x → [ η (f x) ]) (λ= (cp₀₁-distr-l g₁ g₂))) embase
              =⟨ ap (λ w → app= w embase) (λ=-ap (λ _ y → [ η y ]) (cp₀₁-distr-l g₁ g₂)) ⟩
            app= (λ= (λ a → ap (λ y → [ η y ]) (cp₀₁-distr-l g₁ g₂ a))) embase
              =⟨ app=-β (λ a → ap (λ y → [ η y ]) (cp₀₁-distr-l g₁ g₂ a)) embase ⟩
            idp =∎
          ⟩
      app= (TwoSemiFunctor.pres-comp G₁₂ (cp₀₁ g₁) (cp₀₁ g₂)) embase ◃∎
        =ₛ₁⟨ app=-β (λ y → comp (cp₀₁ g₁ y) (cp₀₁ g₂ y)) embase ⟩
      comp embase embase ◃∎ ∎ₛ
      where
        G₁₂ = fun-functor-map (EM₁ H.grp) comp-functor

module CP₁₁ where

  private
    C : Type (lmax i j)
    C = EM₁ H.grp → EM G⊗H.abgroup 2

    C-level : has-level 2 C
    C-level = Π-level (λ _ → EM-level G⊗H.abgroup 2)

    D₀ : TwoSemiCategory lzero i
    D₀ = group-to-cat G.grp

    D₁' : TwoSemiCategory (lmax i j) (lmax i j)
    D₁' = =ₜ-fundamental-cat (Susp (EM₁ G⊗H.grp))

    D₁ : TwoSemiCategory (lmax i j) (lmax i j)
    D₁ = fun-cat (EM₁ H.grp) D₁'

    D₂' : TwoSemiCategory (lmax i j) (lmax i j)
    D₂' = 2-type-fundamental-cat (EM G⊗H.abgroup 2)

    D₂ : TwoSemiCategory (lmax i j) (lmax i j)
    D₂ = fun-cat (EM₁ H.grp) D₂'

    D₃ : TwoSemiCategory (lmax i j) (lmax i j)
    D₃ = 2-type-fundamental-cat (EM₁ H.grp → EM G⊗H.abgroup 2) {{C-level}}

    F₀₁ : TwoSemiFunctor D₀ D₁
    F₀₁ = group-to-EM₁→EM₂

    F₁₂' : TwoSemiFunctor D₁' D₂'
    F₁₂' = =ₜ-to-2-type-fundamental-cat (Susp (EM₁ G⊗H.grp))

    F₁₂ : TwoSemiFunctor D₁ D₂
    F₁₂ = fun-functor-map (EM₁ H.grp) F₁₂'

  F₀₂ : TwoSemiFunctor D₀ D₂
  F₀₂ = F₀₁ –F→ F₁₂

  module F₂₃-Funext = FunextFunctors (EM₁ H.grp) (EM G⊗H.abgroup 2) {{⟨⟩}}
  F₂₃ : TwoSemiFunctor D₂ D₃
  F₂₃ = F₂₃-Funext.λ=-functor

  module F₀₂ = TwoSemiFunctor F₀₂
  module F₂₃ = TwoSemiFunctor F₂₃

  private
    module F₀₃-Comp = FunctorComposition F₀₂ F₂₃
    F₀₃ : TwoSemiFunctor D₀ D₃
    F₀₃ = F₀₃-Comp.composition

    module F₀₁  = TwoSemiFunctor F₀₁
    module F₁₂  = TwoSemiFunctor F₁₂
    module F₁₂' = TwoSemiFunctor F₁₂'
    module F₀₃  = TwoSemiFunctor F₀₃

    module CP₁₁-Rec = EM₁Rec {G = G.grp} {C = C} {{C-level}} F₀₃

  abstract
    cp₁₁ : EM₁ G.grp → EM₁ H.grp → EM G⊗H.abgroup 2
    cp₁₁ = CP₁₁-Rec.f

    cp₁₁-embase-β : cp₁₁ embase ↦ (λ _ → [ north ])
    cp₁₁-embase-β = CP₁₁-Rec.embase-β
    {-# REWRITE cp₁₁-embase-β #-}

    cp₁₁-emloop-β : ∀ g → ap cp₁₁ (emloop g) == λ= (λ y → ap [_] (η (cp₀₁ g y)))
    cp₁₁-emloop-β g = CP₁₁-Rec.emloop-β g

    app=-F₀₂-pres-comp-embase-β : ∀ g₁ g₂ →
      app= (F₀₂.pres-comp g₁ g₂) embase ◃∎
      =ₛ
      ap (ap [_]) (comp-l embase) ◃∙
      ap-∙ [_] (η embase) (η embase) ◃∎
    app=-F₀₂-pres-comp-embase-β g₁ g₂ =
      app= (F₀₂.pres-comp g₁ g₂) embase ◃∎
        =ₛ⟨ ap-seq-=ₛ (λ f → f embase) (comp-functors-pres-comp-β F₀₁ F₁₂ g₁ g₂) ⟩
      app= (ap (λ α y → <– (=ₜ-equiv [ north ] [ north ]) (α y))
               (F₀₁.pres-comp g₁ g₂)) embase ◃∙
      app= (F₁₂.pres-comp {x = λ _ → [ north ]} {y = λ _ → [ north ]} {z = λ _ → [ north ]}
                          (λ y → [ η (cp₀₁ g₁ y) ]₁) (λ y → [ η (cp₀₁ g₂ y) ]₁)) embase ◃∎
        =ₛ₁⟨ 0 & 1 & step₂ ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      app= (F₁₂.pres-comp {x = λ _ → [ north ]} {y = λ _ → [ north ]} {z = λ _ → [ north ]}
                          (λ y → [ η (cp₀₁ g₁ y) ]₁) (λ y → [ η (cp₀₁ g₂ y) ]₁)) embase ◃∎
        =ₛ₁⟨ 1 & 1 & step₃ ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap-∙ [_] (η embase) (η embase) ◃∎ ∎ₛ
      where
      step₂ :
        app= (ap (λ α y → <– (=ₜ-equiv [ north ] [ north ]) (α y))
             (F₀₁.pres-comp g₁ g₂)) embase
        == ap (ap [_]) (comp-l embase)
      step₂ =
        app= (ap (λ α y → <– (=ₜ-equiv [ north ] [ north ]) (α y)) (F₀₁.pres-comp g₁ g₂)) embase
          =⟨ ∘-ap (λ f → f embase)
                  (λ α y → <– (=ₜ-equiv [ north ] [ north ]) (α y))
                  (F₀₁.pres-comp g₁ g₂) ⟩
        ap (λ α → <– (=ₜ-equiv [ north ] [ north ]) (α embase))
           (F₀₁.pres-comp g₁ g₂)
          =⟨ ap-∘ (<– (=ₜ-equiv [ north ] [ north ]))
                  (λ f → f embase)
                  (F₀₁.pres-comp g₁ g₂) ⟩
        ap (<– (=ₜ-equiv [ north ] [ north ]))
           (app= (F₀₁.pres-comp g₁ g₂) embase)
          =⟨ ap (ap (<– (=ₜ-equiv [ north ] [ north ])))
                (app=-group-to-EM₁→EM₂-pres-comp-embase g₁ g₂) ⟩
        ap (<– (=ₜ-equiv [ north ] [ north ])) (comp embase embase)
          =⟨ ap (ap (<– (=ₜ-equiv [ north ] [ north ])))
                (comp-unit-l embase) ⟩
        ap (<– (=ₜ-equiv [ north ] [ north ]))
           (comp-l₁ embase)
          =⟨ ∘-ap (<– (=ₜ-equiv [ north ] [ north ])) [_]₁ (comp-l embase) ⟩
        ap (ap [_]) (comp-l embase) =∎
      step₃ :
        app= (F₁₂.pres-comp {x = λ _ → [ north ]₂} {y = λ _ → [ north ]₂} {z = λ _ → [ north ]₂}
                            (λ y → [ η (cp₀₁ g₁ y) ]₁) (λ y → [ η (cp₀₁ g₂ y) ]₁)) embase
        == ap-∙ [_] (η embase) (η embase)
      step₃ =
        app= (F₁₂.pres-comp {x = λ _ → [ north ]} {y = λ _ → [ north ]} {z = λ _ → [ north ]}
                            (λ y → [ η (cp₀₁ g₁ y) ]₁) (λ y → [ η (cp₀₁ g₂ y) ]₁)) embase
           =⟨ app=-β (λ y → F₁₂'.pres-comp {x = [ north ]} {y = [ north ]} {z = [ north ]}
                                           [ η (cp₀₁ g₁ y) ]₁ [ η (cp₀₁ g₂ y) ]₁) embase ⟩
        F₁₂'.pres-comp {x = [ north ]} {y = [ north ]} {z = [ north ]} [ η embase ]₁ [ η embase ]₁
          =⟨ =ₜ-to-2-type-fundamental-cat-pres-comp-β (Susp (EM₁ G⊗H.grp)) (η embase) (η embase) ⟩
        ap-∙ [_] (η embase) (η embase) =∎

    app=-F₀₂-pres-comp-embase-coh : ∀ g₁ g₂ →
      app= (F₀₂.pres-comp g₁ g₂) embase ◃∙
      ap2 _∙_ (ap (ap [_]) (!-inv-r (merid embase)))
              (ap (ap [_]) (!-inv-r (merid embase))) ◃∎
      =ₛ
      ap (ap [_]) (!-inv-r (merid embase)) ◃∎
    app=-F₀₂-pres-comp-embase-coh g₁ g₂ =
      app= (F₀₂.pres-comp g₁ g₂) embase ◃∙
      ap2 _∙_ (ap (ap [_]) (!-inv-r (merid embase)))
              (ap (ap [_]) (!-inv-r (merid embase))) ◃∎
        =ₛ⟨ 0 & 1 & app=-F₀₂-pres-comp-embase-β g₁ g₂ ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap-∙ [_] (η embase) (η embase) ◃∙
      ap2 _∙_ (ap (ap [_]) (!-inv-r (merid embase)))
              (ap (ap [_]) (!-inv-r (merid embase))) ◃∎
        =ₛ₁⟨ 2 & 1 & ap2-ap-lr _∙_ (ap [_]) (ap [_]) (!-inv-r (merid embase)) (!-inv-r (merid embase)) ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap-∙ [_] (η embase) (η embase) ◃∙
      ap2 (λ s t → ap [_] s ∙ ap [_] t) (!-inv-r (merid embase)) (!-inv-r (merid embase)) ◃∎
        =ₛ⟨ 1 & 2 & !ₛ $
            homotopy-naturality2 (λ s t → ap [_] (s ∙ t))
                                 (λ s t → ap [_] s ∙ ap [_] t)
                                 (ap-∙ [_])
                                 (!-inv-r (merid embase))
                                 (!-inv-r (merid embase)) ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap2 (λ s t → ap [_] (s ∙ t)) (!-inv-r (merid embase)) (!-inv-r (merid embase)) ◃∙
      idp ◃∎
        =ₛ⟨ 2 & 1 & expand [] ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap2 (λ s t → ap [_] (s ∙ t)) (!-inv-r (merid embase)) (!-inv-r (merid embase)) ◃∎
        =ₛ₁⟨ 1 & 1 & ! (ap-ap2 (ap [_]) _∙_ (!-inv-r (merid embase)) (!-inv-r (merid embase))) ⟩
      ap (ap [_]) (comp-l embase) ◃∙
      ap (ap [_]) (ap2 _∙_ (!-inv-r (merid embase)) (!-inv-r (merid embase))) ◃∎
        =ₛ⟨ ap-seq-=ₛ (ap [_]) step₇' ⟩
      ap (ap [_]) (!-inv-r (merid embase)) ◃∎ ∎ₛ
      where
        helper : ∀ {i} {A : Type i} {a₀ a₁ : A} (p : a₀ == a₁)
          → add-path-inverse-l (p ∙ ! p) p ◃∙ ap2 _∙_ (!-inv-r p) (!-inv-r p) ◃∎
            =ₛ !-inv-r p ◃∎
        helper idp = =ₛ-in idp
        step₇' : comp-l embase ◃∙ ap2 _∙_ (!-inv-r (merid embase)) (!-inv-r (merid embase)) ◃∎
                =ₛ !-inv-r (merid embase) ◃∎
        step₇' = helper (merid embase)

    app=-ap-cp₁₁-seq : ∀ g y → app= (ap cp₁₁ (emloop g)) y =-= ap [_] (η (cp₀₁ g y))
    app=-ap-cp₁₁-seq g y =
      app= (ap cp₁₁ (emloop g)) y
        =⟪ ap (λ p → app= p y) (cp₁₁-emloop-β g) ⟫
      app= (λ= (λ x → ap [_] (η (cp₀₁ g x)))) y
        =⟪ app=-β (λ x → ap [_] (η (cp₀₁ g x))) y ⟫
      ap [_] (η (cp₀₁ g y)) ∎∎

    app=-ap-cp₁₁ : ∀ g y → app= (ap cp₁₁ (emloop g)) y == ap [_] (η (cp₀₁ g y))
    app=-ap-cp₁₁ g y = ↯ (app=-ap-cp₁₁-seq g y)

  app=-ap-cp₁₁-coh-seq₁ : ∀ g₁ g₂ y →
    app= (ap cp₁₁ (emloop (G.comp g₁ g₂))) y =-= ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y))
  app=-ap-cp₁₁-coh-seq₁ g₁ g₂ y =
    app= (ap cp₁₁ (emloop (G.comp g₁ g₂))) y
      =⟪ ap (λ u → app= (ap cp₁₁ u) y) (emloop-comp g₁ g₂) ⟫
    app= (ap cp₁₁ (emloop g₁ ∙ emloop g₂)) y
      =⟪ ap (λ u → app= u y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ⟫
    app= (ap cp₁₁ (emloop g₁) ∙ ap cp₁₁ (emloop g₂)) y
      =⟪ ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ⟫
    app= (ap cp₁₁ (emloop g₁)) y ∙ app= (ap cp₁₁ (emloop g₂)) y
      =⟪ ap2 _∙_ (app=-ap-cp₁₁ g₁ y) (app=-ap-cp₁₁ g₂ y) ⟫
    ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y)) ∎∎

  app=-ap-cp₁₁-coh-seq₂ : ∀ g₁ g₂ y →
    app= (ap cp₁₁ (emloop (G.comp g₁ g₂))) y =-= ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y))
  app=-ap-cp₁₁-coh-seq₂ g₁ g₂ y =
    app= (ap cp₁₁ (emloop (G.comp g₁ g₂))) y
      =⟪ app=-ap-cp₁₁ (G.comp g₁ g₂) y ⟫
    ap [_] (η (cp₀₁ (G.comp g₁ g₂) y))
      =⟪ app= (F₀₂.pres-comp g₁ g₂) y ⟫
    ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y)) ∎∎

  abstract
    app=-ap-cp₁₁-coh : ∀ g₁ g₂ y →
      app=-ap-cp₁₁-coh-seq₁ g₁ g₂ y =ₛ app=-ap-cp₁₁-coh-seq₂ g₁ g₂ y
    app=-ap-cp₁₁-coh g₁ g₂ y =
      ap (λ u → app= (ap cp₁₁ u) y) (emloop-comp g₁ g₂) ◃∙
      ap (λ u → app= u y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ◃∙
      ap2 _∙_ (app=-ap-cp₁₁ g₁ y) (app=-ap-cp₁₁ g₂ y) ◃∎
        =ₛ⟨ 3 & 2 & ap2-seq-∙ _∙_ (app=-ap-cp₁₁-seq g₁ y) (app=-ap-cp₁₁-seq g₂ y) ⟩
      ap (λ u → app= (ap cp₁₁ u) y) (emloop-comp g₁ g₂) ◃∙
      ap (λ u → app= u y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ◃∙
      ap2 _∙_ (ap (λ z → app= z y) (cp₁₁-emloop-β g₁)) (ap (λ z → app= z y) (cp₁₁-emloop-β g₂)) ◃∙
      ap2 _∙_ (app=-β (λ x → ap [_] (η (cp₀₁ g₁ x))) y) (app=-β (λ x → ap [_] (η (cp₀₁ g₂ x))) y) ◃∎
        =ₛ₁⟨ 3 & 1 & ap2-ap-lr _∙_ (λ z → app= z y) (λ z → app= z y) (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ⟩
      ap (λ u → app= (ap cp₁₁ u) y) (emloop-comp g₁ g₂) ◃∙
      ap (λ u → app= u y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ◃∙
      ap2 (λ a b → app= a y ∙ app= b y) (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ◃∙
      ap2 _∙_ (app=-β (λ x → ap [_] (η (cp₀₁ g₁ x))) y) (app=-β (λ x → ap [_] (η (cp₀₁ g₂ x))) y) ◃∎
        =ₛ⟨ 2 & 2 & !ₛ $
            homotopy-naturality2 (λ a b → app= (a ∙ b) y)
                                 (λ a b → app= a y ∙ app= b y)
                                 (ap-∙ (λ f → f y))
                                 (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ⟩
      ap (λ u → app= (ap cp₁₁ u) y) (emloop-comp g₁ g₂) ◃∙
      ap (λ u → app= u y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap2 (λ a b → app= (a ∙ b) y) (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ◃∙
      ap-∙ (λ f → f y) (λ= (λ y' → ap [_] (η (cp₀₁ g₁ y')))) (λ= (λ y' → ap [_] (η (cp₀₁ g₂ y')))) ◃∙
      ap2 _∙_ (app=-β (λ x → ap [_] (η (cp₀₁ g₁ x))) y) (app=-β (λ x → ap [_] (η (cp₀₁ g₂ x))) y) ◃∎
        =ₛ⟨ 3 & 2 &
            app=-β-coh (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))) y ⟩
      ap (λ x → app= (ap cp₁₁ x) y) (emloop-comp g₁ g₂) ◃∙
      ap (λ p → app= p y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap2 (λ a b → app= (a ∙ b) y) (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ◃∙
      ap (λ p → app= p y) (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))))) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ₁⟨ 0 & 1 & ap-∘ (λ p → app= p y) (ap cp₁₁) (emloop-comp g₁ g₂) ⟩
      ap (λ p → app= p y) (ap (ap cp₁₁) (emloop-comp g₁ g₂)) ◃∙
      ap (λ p → app= p y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap2 (λ a b → app= (a ∙ b) y) (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂) ◃∙
      ap (λ p → app= p y) (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))))) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ₁⟨ 2 & 1 & ! (ap-ap2 (λ p → app= p y) _∙_ (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂)) ⟩
      ap (λ p → app= p y) (ap (ap cp₁₁) (emloop-comp g₁ g₂)) ◃∙
      ap (λ p → app= p y) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap (λ p → app= p y) (ap2 _∙_ (cp₁₁-emloop-β g₁) (cp₁₁-emloop-β g₂)) ◃∙
      ap (λ p → app= p y) (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))))) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ⟨ 0 & 3 & ap-seq-=ₛ (λ p → app= p y) (CP₁₁-Rec.emloop-comp-path g₁ g₂) ⟩
      ap (λ p → app= p y) (cp₁₁-emloop-β (G.comp g₁ g₂)) ◃∙
      ap (λ p → app= p y) (F₀₃.pres-comp g₁ g₂) ◃∙
      ap (λ p → app= p y) (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))))) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ⟨ 1 & 2 & step₈ ⟩
      ap (λ p → app= p y) (cp₁₁-emloop-β (G.comp g₁ g₂)) ◃∙
      ap (λ p → app= p y) (ap λ= (F₀₂.pres-comp g₁ g₂)) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ₁⟨ 1 & 1 & ∘-ap (λ p → app= p y) λ= (F₀₂.pres-comp g₁ g₂) ⟩
      ap (λ p → app= p y) (cp₁₁-emloop-β (G.comp g₁ g₂)) ◃∙
      ap (λ γ → app= (λ= γ) y) (F₀₂.pres-comp g₁ g₂) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ g₁ x)) ∙ ap [_] (η (cp₀₁ g₂ x))) y ◃∎
        =ₛ⟨ 1 & 2 &
            homotopy-naturality (λ γ → app= (λ= γ) y)
                                (λ γ → γ y)
                                (λ γ → app=-β γ y)
                                (F₀₂.pres-comp g₁ g₂) ⟩
      ap (λ p → app= p y) (cp₁₁-emloop-β (G.comp g₁ g₂)) ◃∙
      app=-β (λ x → ap [_] (η (cp₀₁ (G.comp g₁ g₂) x))) y ◃∙
      app= (F₀₂.pres-comp g₁ g₂) y ◃∎
        =ₛ⟨ 0 & 2 & contract ⟩
      app=-ap-cp₁₁ (G.comp g₁ g₂) y ◃∙
      app= (F₀₂.pres-comp g₁ g₂) y ◃∎ ∎ₛ
      where
      step₈ :
        ap (λ p → app= p y) (F₀₃.pres-comp g₁ g₂) ◃∙
        ap (λ p → app= p y) (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))))) ◃∎
        =ₛ
        ap (λ p → app= p y) (ap λ= (F₀₂.pres-comp g₁ g₂)) ◃∎
      step₈ = ap-seq-=ₛ (λ p → app= p y) $
        F₀₃.pres-comp g₁ g₂ ◃∙
        =ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x)))) ◃∎
          =ₛ⟨ 0 & 1 & F₀₃-Comp.pres-comp-β g₁ g₂ ⟩
        ap λ= (F₀₂.pres-comp g₁ g₂) ◃∙
        F₂₃.pres-comp (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))) ◃∙
        =ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x)))) ◃∎
          =ₛ₁⟨ 1 & 1 & F₂₃-Funext.λ=-functor-pres-comp=λ=-∙ (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x))) ⟩
        ap λ= (F₀₂.pres-comp g₁ g₂) ◃∙
        =ₛ-out (λ=-∙ (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x)))) ◃∙
        =ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x)))) ◃∎
          =ₛ⟨ 1 & 2 & seq-!-inv-l (=ₛ-out (∙-λ= (λ x → ap [_] (η (cp₀₁ g₁ x))) (λ x → ap [_] (η (cp₀₁ g₂ x)))) ◃∎) ⟩
        ap λ= (F₀₂.pres-comp g₁ g₂) ◃∎ ∎ₛ

    ap-cp₁₁-seq : ∀ g y → ap (λ x → cp₁₁ x y) (emloop g) =-= ap [_] (η (cp₀₁ g y))
    ap-cp₁₁-seq g y =
      ap (λ x → cp₁₁ x y) (emloop g)
        =⟪ ap-∘ (λ f → f y) cp₁₁ (emloop g) ⟫
      ap (λ f → f y) (ap cp₁₁ (emloop g))
        =⟪ app=-ap-cp₁₁ g y ⟫
      ap [_] (η (cp₀₁ g y)) ∎∎

    ap-cp₁₁ : ∀ g y → ap (λ x → cp₁₁ x y) (emloop g) == ap [_] (η (cp₀₁ g y))
    ap-cp₁₁ g y = ↯ (ap-cp₁₁-seq g y)

  ap-cp₁₁-coh-seq₁ : ∀ g₁ g₂ y →
    ap (λ x → cp₁₁ x y) (emloop (G.comp g₁ g₂)) =-= ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y))
  ap-cp₁₁-coh-seq₁ g₁ g₂ y =
    ap (λ x → cp₁₁ x y) (emloop (G.comp g₁ g₂))
      =⟪ ap (ap (λ x → cp₁₁ x y)) (emloop-comp g₁ g₂) ⟫
    ap (λ x → cp₁₁ x y) (emloop g₁ ∙ emloop g₂)
      =⟪ ap-∙ (λ x → cp₁₁ x y) (emloop g₁) (emloop g₂) ⟫
    ap (λ x → cp₁₁ x y) (emloop g₁) ∙ ap (λ x → cp₁₁ x y) (emloop g₂)
      =⟪ ap2 _∙_ (ap-cp₁₁ g₁ y) (ap-cp₁₁ g₂ y) ⟫
    ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y)) ∎∎

  ap-cp₁₁-coh-seq₂ : ∀ g₁ g₂ y →
    ap (λ x → cp₁₁ x y) (emloop (G.comp g₁ g₂)) =-= ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y))
  ap-cp₁₁-coh-seq₂ g₁ g₂ y =
    ap (λ x → cp₁₁ x y) (emloop (G.comp g₁ g₂))
      =⟪ ap-cp₁₁ (G.comp g₁ g₂) y ⟫
    ap [_] (η (cp₀₁ (G.comp g₁ g₂) y))
      =⟪ app= (F₀₂.pres-comp g₁ g₂) y ⟫
    ap [_] (η (cp₀₁ g₁ y)) ∙ ap [_] (η (cp₀₁ g₂ y)) ∎∎

  abstract
    ap-cp₁₁-coh : ∀ g₁ g₂ y →
      ap-cp₁₁-coh-seq₁ g₁ g₂ y =ₛ ap-cp₁₁-coh-seq₂ g₁ g₂ y
    ap-cp₁₁-coh g₁ g₂ y =
      ap (ap (λ x → cp₁₁ x y)) (emloop-comp g₁ g₂) ◃∙
      ap-∙ (λ x → cp₁₁ x y) (emloop g₁) (emloop g₂) ◃∙
      ap2 _∙_ (ap-cp₁₁ g₁ y) (ap-cp₁₁ g₂ y) ◃∎
        =ₛ⟨ 2 & 1 & ap2-seq-∙ _∙_ (ap-cp₁₁-seq g₁ y) (ap-cp₁₁-seq g₂ y) ⟩
      ap (ap (λ x → cp₁₁ x y)) (emloop-comp g₁ g₂) ◃∙
      ap-∙ (λ x → cp₁₁ x y) (emloop g₁) (emloop g₂) ◃∙
      ap2 _∙_ (ap-∘ (λ f → f y) cp₁₁ (emloop g₁)) (ap-∘ (λ f → f y) cp₁₁ (emloop g₂)) ◃∙
      ap2 _∙_ (app=-ap-cp₁₁ g₁ y) (app=-ap-cp₁₁ g₂ y) ◃∎
        =ₛ⟨ 1 & 2 & ap-∘-∙-coh (λ f → f y) cp₁₁ (emloop g₁) (emloop g₂) ⟩
      ap (ap (λ x → cp₁₁ x y)) (emloop-comp g₁ g₂) ◃∙
      ap-∘ (λ f → f y) cp₁₁ (emloop g₁ ∙ emloop g₂) ◃∙
      ap (ap (λ f → f y)) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ◃∙
      ap2 _∙_ (app=-ap-cp₁₁ g₁ y) (app=-ap-cp₁₁ g₂ y) ◃∎
        =ₛ⟨ 0 & 2 & homotopy-naturality {A = embase' G.grp == embase} {B = cp₁₁ embase y == cp₁₁ embase y}
                                        (ap (λ x → cp₁₁ x y)) (λ p → app= (ap cp₁₁ p) y)
                                        (ap-∘ (λ f → f y) cp₁₁) (emloop-comp g₁ g₂) ⟩
      ap-∘ (λ f → f y) cp₁₁ (emloop (G.comp g₁ g₂)) ◃∙
      ap (λ p → app= (ap cp₁₁ p) y) (emloop-comp g₁ g₂) ◃∙
      ap (ap (λ f → f y)) (ap-∙ cp₁₁ (emloop g₁) (emloop g₂)) ◃∙
      ap-∙ (λ f → f y) (ap cp₁₁ (emloop g₁)) (ap cp₁₁ (emloop g₂)) ◃∙
      ap2 _∙_ (app=-ap-cp₁₁ g₁ y) (app=-ap-cp₁₁ g₂ y) ◃∎
        =ₛ⟨ 1 & 4 & app=-ap-cp₁₁-coh g₁ g₂ y ⟩
      ap-∘ (λ f → f y) cp₁₁ (emloop (G.comp g₁ g₂)) ◃∙
      app=-ap-cp₁₁ (G.comp g₁ g₂) y ◃∙
      app= (F₀₂.pres-comp g₁ g₂) y ◃∎
        =ₛ₁⟨ 0 & 2 & idp ⟩
      ap-cp₁₁ (G.comp g₁ g₂) y ◃∙
      app= (F₀₂.pres-comp g₁ g₂) y ◃∎ ∎ₛ

  ap-cp₁₁-embase-seq : ∀ g →
    ap (λ x → cp₁₁ x embase) (emloop g) =-= idp
  ap-cp₁₁-embase-seq g =
    ap (λ x → cp₁₁ x embase) (emloop g)
      =⟪ ap-cp₁₁ g embase ⟫
    ap [_] (η (cp₀₁ g embase))
      =⟪idp⟫
    ap [_] (η embase)
      =⟪ ap (ap [_]) (!-inv-r (merid embase)) ⟫
    ap [_] (idp {a = north})
      =⟪idp⟫
    idp ∎∎

  ap-cp₁₁-embase : ∀ g →
    ap (λ x → cp₁₁ x embase) (emloop g) == idp
  ap-cp₁₁-embase g = ↯ (ap-cp₁₁-embase-seq g)

  ap-cp₁₁-embase-coh-seq : ∀ g₁ g₂ →
    ap (λ x → cp₁₁ x embase) (emloop (G.comp g₁ g₂)) =-= idp
  ap-cp₁₁-embase-coh-seq g₁ g₂ =
    ap (λ x → cp₁₁ x embase) (emloop (G.comp g₁ g₂))
      =⟪ ap (ap (λ x → cp₁₁ x embase)) (emloop-comp g₁ g₂) ⟫
    ap (λ x → cp₁₁ x embase) (emloop g₁ ∙ emloop g₂)
      =⟪ ap-∙ (λ x → cp₁₁ x embase) (emloop g₁) (emloop g₂) ⟫
    ap (λ x → cp₁₁ x embase) (emloop g₁) ∙ ap (λ x → cp₁₁ x embase) (emloop g₂)
      =⟪ ap2 _∙_ (ap-cp₁₁-embase g₁) (ap-cp₁₁-embase g₂) ⟫
    idp ∎∎

  ap-cp₁₁-embase-coh : ∀ g₁ g₂ →
    ap-cp₁₁-embase (G.comp g₁ g₂) ◃∎ =ₛ ap-cp₁₁-embase-coh-seq g₁ g₂
  ap-cp₁₁-embase-coh g₁ g₂ =
    ap-cp₁₁-embase (G.comp g₁ g₂) ◃∎
      =ₛ⟨ expand (ap-cp₁₁-embase-seq (G.comp g₁ g₂)) ⟩
    ap-cp₁₁ (G.comp g₁ g₂) embase ◃∙
    ap (ap [_]) (!-inv-r (merid embase)) ◃∎
      =ₛ⟨ 1 & 1 & !ₛ (app=-F₀₂-pres-comp-embase-coh g₁ g₂) ⟩
    ap-cp₁₁ (G.comp g₁ g₂) embase ◃∙
    app= (F₀₂.pres-comp g₁ g₂) embase ◃∙
    ap2 _∙_ (ap (ap [_]) (!-inv-r (merid embase))) (ap (ap [_]) (!-inv-r (merid embase))) ◃∎
      =ₛ⟨ 0 & 2 & !ₛ (ap-cp₁₁-coh g₁ g₂ embase) ⟩
    ap (ap (λ x → cp₁₁ x embase)) (emloop-comp g₁ g₂) ◃∙
    ap-∙ (λ x → cp₁₁ x embase) (emloop g₁) (emloop g₂) ◃∙
    ap2 _∙_ (ap-cp₁₁ g₁ embase) (ap-cp₁₁ g₂ embase) ◃∙
    ap2 _∙_ (ap (ap [_]) (!-inv-r (merid embase))) (ap (ap [_]) (!-inv-r (merid embase))) ◃∎
      =ₛ⟨ 2 & 2 & ∙-ap2-seq _∙_ (ap-cp₁₁-embase-seq g₁) (ap-cp₁₁-embase-seq g₂) ⟩
    ap (ap (λ x → cp₁₁ x embase)) (emloop-comp g₁ g₂) ◃∙
    ap-∙ (λ x → cp₁₁ x embase) (emloop g₁) (emloop g₂) ◃∙
    ap2 _∙_ (ap-cp₁₁-embase g₁) (ap-cp₁₁-embase g₂) ◃∎ ∎ₛ

open CP₁₁ public


open import Prelude
open import Data.List.Properties
open import Data.List.Any hiding (map)
open import Extensions.Vec
open import Data.Vec
open import Data.List.All as All hiding (map; lookup)

module Implicits.Resolution.Infinite.Semantics where

open import Implicits.Syntax
open import Implicits.Resolution.Infinite.Resolution
open import Implicits.Substitutions
open import Implicits.Substitutions.Lemmas
open import Implicits.Semantics
open import Implicits.Semantics.Lemmas

open import SystemF.Everything as F using ()

module DerivationSemantics where

  ⟦_,_⟧r : ∀ {ν n} {Δ : ICtx ν} {Γ : Ctx ν n} {a} → Δ ⊢ᵣ a → Γ # Δ →
           ∃ λ t → ⟦ Γ ⟧ctx→ F.⊢ t ∈ ⟦ a ⟧tp→
  ⟦_,_⟧r {Γ = Γ} (r-simp {r = r} r∈Δ r↓τ) m with ∈⟶index (All.lookup m r∈Δ)
  ⟦_,_⟧r {Γ = Γ} (r-simp {r = r} r∈Δ r↓τ) m | i , lookup-i≡r =
    ⟦ subst (λ u → _ F.⊢ F.var i ∈ u) eq (F.var i) , r↓τ , m ⟧r↓
    where
      eq = begin
        lookup i ⟦ Γ ⟧ctx→
          ≡⟨ sym $ lookup-⟦⟧ctx→ Γ i ⟩
        ⟦ lookup i Γ ⟧tp→
            ≡⟨ cong ⟦_⟧tp→ lookup-i≡r ⟩
        ⟦ r ⟧tp→ ∎

      ⟦_,_,_⟧r↓ : ∀ {ν n} {Δ : ICtx ν} {Γ : Ctx ν n} {a ta τ} →
                ⟦ Γ ⟧ctx→ F.⊢ ta ∈ ⟦ a ⟧tp→ → Δ ⊢ a ↓ τ → Γ # Δ →
                ∃ λ tτ → ⟦ Γ ⟧ctx→ F.⊢ tτ ∈ ⟦ simpl τ ⟧tp→
      ⟦ ⊢ta , i-simp τ , m ⟧r↓ = , ⊢ta
      ⟦ ⊢ta , i-iabs {ρ₁ = a} ⊢ᵣa b↓τ , m ⟧r↓ =
        , (proj₂ ⟦ ⊢ta F.· (proj₂ ⟦ ⊢ᵣa , m ⟧r) , b↓τ , m ⟧r↓)
      ⟦ ⊢ta , i-tabs {ρ = a} b p , m ⟧r↓ =
        ⟦ subst (λ u → _ F.⊢ _ ∈ u) (sym $ ⟦a/sub⟧tp→ a b) (⊢ta F.[ ⟦ b ⟧tp→ ]) , p , m ⟧r↓

  ⟦ r-iabs {ρ₁ = a} {ρ₂ = b} ⊢b , m ⟧r = , F.λ' ⟦ a ⟧tp→ (proj₂ ⟦ ⊢b , #ivar a m ⟧r)

  ⟦_,_⟧r {Γ = Γ} (r-tabs {ρ = r} p) m with ⟦ p , #tvar m ⟧r
  ⟦_,_⟧r {Δ = Δ} {Γ = Γ} (r-tabs {ρ = r} p) m | _ , x =
    , F.Λ (subst (λ u → u F.⊢ _ ∈ ⟦ r ⟧tp→) (⟦weaken⟧ctx→ Γ) x)

open Semantics _⊢ᵣ_ DerivationSemantics.⟦_,_⟧r public


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

open import lib.Basics
open import lib.NType2
open import lib.PathGroupoid

open import lib.types.Bool
open import lib.types.IteratedSuspension
open import lib.types.Lift
open import lib.types.LoopSpace
open import lib.types.Nat
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.types.Suspension
open import lib.types.TLevel
open import lib.types.Unit

open SuspensionRec public using () renaming (f to Susp-rec)

open import nicolai.pseudotruncations.Preliminary-definitions
open import nicolai.pseudotruncations.Liblemmas
open import nicolai.pseudotruncations.pointed-O-Sphere

module nicolai.pseudotruncations.LoopsAndSpheres where

{- We greatly benefit from Evan Cavallo's code - thank you! -}
open import homotopy.PtdAdjoint
open import homotopy.SuspAdjointLoop

isNull : ∀ {i j} {A : Type i} {B : Type j} (b : B) (f : A → B) → Type _
isNull {A = A} b f = (a : A) → f a == b

module null {i} {j} {Â : Ptd i} {B̂ : Ptd j} (ĝ : Â →̇ B̂) where

  A = fst Â
  a₀ = snd Â
  B = fst B̂
  b₀ = snd B̂
  g = fst ĝ
  p = snd ĝ

  -- derived isNull
  isNulld = (a : fst Â) → g a == b₀

  -- pointed isNull; we state it in the equivalence form (slightly easier to handle)
  isNull∙' = Σ ((a : A) → g a == b₀) λ pr → pr a₀ == p

  -- the 'real' pointed isNull
  isNull∙ = ĝ == ((λ _ → b₀) , idp)

  {- The two versions are equivalent -}
  isNull-equiv : isNull∙ ≃ isNull∙'
  isNull-equiv =
               ĝ == ((λ _ → b₀) , idp)
                 ≃⟨ (=Σ-eqv _ _) ⁻¹ ⟩
               =Σ ĝ ((λ _ → b₀) , idp)
                 ≃⟨ equiv-Σ' {A₀ = g == λ _ → b₀}
                             app=-equiv
                             (λ h → (p == idp [ (λ f → f a₀ == b₀) ↓ h ])
                                      ≃⟨ to-transp-equiv _ _ ⟩
                                    (transport (λ f → f a₀ == b₀) h p) == idp
                                      ≃⟨ coe-equiv
                                           (ap (λ x → x == idp)
                                               (trans-ap₁ (λ f → f a₀)
                                                          b₀ h p)) ⟩
                                    (! (app= h a₀) ∙ p) == idp
                                      ≃⟨ adhoc-=-eqv (app= h a₀) p ⟩
                                    (app= h a₀ == p)
                                      ≃∎) ⟩
               (Σ ((a : A) → g a == b₀) λ pr → pr a₀ == p)
                 ≃∎

-- Lemma 4.4: pointed and non-pointed 'nullness' are logically equivalent;
-- First, one direction:
  null-lequiv : isNulld → isNull∙'
  null-lequiv isnull = (λ a → isnull a ∙ ! (isnull a₀) ∙ p) , (
                 isnull a₀ ∙ ! (isnull a₀) ∙ p
                   =⟨ ! (∙-assoc (isnull a₀) _ _) ⟩
                 (isnull a₀ ∙ ! (isnull a₀)) ∙ p
                   =⟨ ap (λ t → t ∙ p) (!-inv-r (isnull a₀)) ⟩
                 p
                   ∎)

-- The other direction is very easy; we do it using the non-prime variant:
  null-lequiv-easy : isNull∙ → isNulld
  null-lequiv-easy isn = app= (ap fst isn)

-- uncomment this if you want to wait forever for typechecking...
-- Σ⊣Ω-unitCounit : CounitUnitAdjoint Σ⊣Ω.SuspFunctor Σ⊣Ω.LoopFunctor
Σ⊣Ω-unitCounit = Σ⊣Ω.adj

Σ⊣Ω-homset : ∀ {i} → HomAdjoint {i} {i} Σ⊣Ω.SuspFunctor Σ⊣Ω.LoopFunctor
Σ⊣Ω-homset = counit-unit-to-hom Σ⊣Ω-unitCounit

module hom-adjoint {i} (Â : Ptd i) (B̂ : Ptd i) where

  A = fst Â
  B = fst B̂
  a₀ = snd Â
  b₀ = snd B̂

  Φeq : (⊙Susp Â →̇ B̂) ≃ (Â →̇ ⊙Ω B̂)
  Φeq = HomAdjoint.eq Σ⊣Ω-homset Â B̂

  {- This is Lemma 4.1 -}
  Φ : (⊙Susp Â →̇ B̂) → (Â →̇ ⊙Ω B̂)
  Φ = –> Φeq

  Φ⁻¹ : (Â →̇ ⊙Ω B̂) → (⊙Susp Â →̇ B̂)
  Φ⁻¹ = <– Φeq

  open PtdFunctor
  open Σ⊣Ω
  open CounitUnitAdjoint

  {- Some lemmas which are easy on paper and thus not explicitly
     mentioned in the paper. It still takes some effort to
     formalize them. -}
  module simplify where

    simpl-⊙ap : (⊙ap {X = obj SuspFunctor Â} ((λ _ → b₀) , idp))
                 ==
                ((λ _ → idp) , idp)
    simpl-⊙ap =  →̇-maps-to
                   ⊙ap ((λ _ → b₀) , idp)
                   ((λ _ → idp) , idp)
                   (λ= (λ _ → ap-cst b₀ _))

                   ((app= (λ= (λ _ → ap-cst b₀ _)) _) ∙ idp
                     =⟨ ∙-unit-r _ ⟩
                   app= (λ= (λ _ → ap-cst b₀ _)) _
                     =⟨ app=-β _ _ ⟩
                   ap-cst b₀ (idp {a = snd B̂})
                     =⟨ idp ⟩ --  !
                   idp
                     =⟨ idp ⟩ --  !
                   snd (⊙ap {X = obj SuspFunctor Â} ((λ _ → b₀) , idp))
                     ∎ )

    simpl-comp : ((λ (_ : Ω (⊙Susp Â)) → idp {a = b₀}) , idp)
                   ⊙∘ (⊙η Â)
                  ==
                 (λ _ → idp) , idp
    simpl-comp = pair= idp ((ap-cst idp (snd (⊙η Â))) ∙ᵣ idp)

  open simplify

  {- Lemma 4.2 -}
  Φ-is-pointed-map : Φ ((λ _ → b₀) , idp) == ((λ _ → idp) , idp)
  Φ-is-pointed-map = Φ ((λ _ → b₀) , idp)
                       =⟨ idp ⟩
                     (    arr LoopFunctor ((λ _ → b₀) , idp)
                      ⊙∘ (CounitUnitAdjoint.η adj Â))
                       =⟨ idp ⟩
                     (    (⊙ap {X = obj SuspFunctor Â} ((λ _ → b₀) , idp)
                      ⊙∘ (⊙η Â)))
                       =⟨ ap (λ f → f ⊙∘ (⊙η Â)) simpl-⊙ap ⟩
                     ((λ _ → idp) , idp) ⊙∘ (⊙η Â)
                       =⟨ simpl-comp ⟩
                     (λ _ → idp) , idp
                       ∎

-- fix i
module _ {i} where

  open hom-adjoint

  open HomAdjoint
  open null

  -- Lemma 4.3
  Φ-snd-nat : {Â B̂ Ĉ : Ptd i} (f : ⊙Susp Â →̇ B̂) (g : B̂ →̇ Ĉ)
              → Φ Â Ĉ (g ⊙∘ f) == ⊙ap g ⊙∘ Φ Â B̂ f
  Φ-snd-nat {Â} {B̂} {Ĉ} f g = ! (nat-cod Σ⊣Ω-homset Â {B̂} {Ĉ} g f)

  -- Lemma 4.4 is above (before 4.2).

  -- Lemma 4.5
  isnull-Φ : {Â B̂ : Ptd i} (g : ⊙Susp Â →̇ B̂) → (isNull∙ g) ≃ isNull∙ (Φ Â B̂ g)
  isnull-Φ {Â} {B̂} g =
    isNull∙ g
      ≃⟨ equiv-ap (Φeq Â B̂) _ _ ⟩
    (Φ Â B̂ g) == Φ Â B̂ ((λ _ → snd B̂) , idp)
      ≃⟨ coe-equiv
           {A = (Φ Â B̂ g) == Φ Â B̂ ((λ _ → snd B̂) , idp)}
           {B = (Φ Â B̂ g) == (λ _ → idp) , idp}
           (ap (λ q → (Φ Â B̂ g == q)) (Φ-is-pointed-map _ _ )) ⟩
    (Φ Â B̂ g) == (λ _ → idp) , idp
      ≃∎

  -- combination of 4.3 and 4.5
  combine-isnull-nat : {Â B̂ Ĉ : Ptd i} (f : ⊙Susp Â →̇ B̂) (g : B̂ →̇ Ĉ)
               → (isNull∙ (g ⊙∘ f)) ≃ (isNull∙ (⊙ap g ⊙∘ Φ Â B̂ f)) --
  combine-isnull-nat {Â} {B̂} {Ĉ} f g =
    isNull∙ (g ⊙∘ f)
      ≃⟨ isnull-Φ _ ⟩
    isNull∙ (Φ Â Ĉ (g ⊙∘ f))
      ≃⟨ coe-equiv (ap (λ q → isNull∙ q) (Φ-snd-nat f g)) ⟩
    isNull∙ (⊙ap g ⊙∘ Φ Â B̂ f)
      ≃∎

  combine-isnull-nat' : {Â B̂ Ĉ : Ptd i} (f : Â →̇ ⊙Ω B̂) (g : B̂ →̇ Ĉ)
               → (isNull∙ (g ⊙∘ (Φ⁻¹ Â B̂ f))) ≃ (isNull∙ (⊙ap g ⊙∘ f))
  combine-isnull-nat' {Â} {B̂} {Ĉ} f g =
   isNull∙ (g ⊙∘ (Φ⁻¹ Â B̂ f))
      ≃⟨ combine-isnull-nat (Φ⁻¹ Â B̂ f) g ⟩
    isNull∙ (⊙ap g ⊙∘ (Φ Â B̂ (Φ⁻¹ Â B̂ f)))
      ≃⟨ coe-equiv (ap (λ h → isNull∙ (⊙ap g ⊙∘ h)) (<–-inv-r (Φeq Â B̂) f)) ⟩
    isNull∙ (⊙ap g ⊙∘ f)
      ≃∎

module _ {i} where

  open hom-adjoint

  -- This was tricky (todo: could explain why)
  Φ-iter : (Â B̂ : Ptd i) (n : Nat)
           → ((⊙Susp-iter' n Â) →̇ B̂)
           → (Â →̇ (⊙Ω^ n B̂))
  Φ-iter Â B̂ O f = f
  Φ-iter Â B̂ (S n) f = Φ Â (⊙Ω^ n B̂) (Φ-iter (⊙Susp Â) B̂ n f)

  Φ-iter-equiv : (Â B̂ : Ptd i) (n : Nat) → is-equiv (Φ-iter Â B̂ n)
  Φ-iter-equiv Â B̂ O = snd (ide _)
  Φ-iter-equiv Â B̂ (S n) =
    snd ((Φeq Â (⊙Ω^ n B̂)) ∘e ((Φ-iter (⊙Susp Â) B̂ n) , Φ-iter-equiv (⊙Susp Â) B̂ n) )

module _ {i} where

  open null
  open hom-adjoint

  -- Lemma 4.7 -- generalized, because we need to do it for Susp first before it works for Sphere!
  isNull-Φ-many : (m : Nat)
                  → (Â B̂ Ĉ : Ptd i)
                  → (f : ⊙Susp-iter' m Â →̇ B̂) (g : B̂ →̇ Ĉ)
                  → isNull∙ (g ⊙∘ f)
                    ≃
                    isNull∙ ((ap^ m g) ⊙∘ Φ-iter Â B̂ m f)
  isNull-Φ-many O Â B̂ Ĉ f g = ide _
  isNull-Φ-many (S m) Â B̂ Ĉ f g =
                    isNull∙ (g ⊙∘ f)
                      ≃⟨ isNull-Φ-many m (⊙Susp Â) B̂ Ĉ f g   ⟩
                    isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Susp Â) B̂ m f)
                      ≃⟨ combine-isnull-nat (Φ-iter (⊙Susp Â) B̂ m f) (ap^ m g) ⟩
                    (isNull∙
                      (⊙ap (ap^ m g) ⊙∘
                        Φ Â (⊙Ω^ m B̂)
                          (Φ-iter (⊙Susp Â) B̂ m f)))
                      ≃∎

  -- Lemma 4.7 (special with k = 0)
  module _ {B̂ Ĉ : Ptd i} (m : Nat)
           (f : ⊙Sphere' {i} m →̇ B̂) (g : B̂ →̇ Ĉ) where

    isNull-Φ-Sphere : isNull∙ (g ⊙∘ f)
                      ≃
                      isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Sphere' {i} O) B̂ m f)
    isNull-Φ-Sphere = isNull-Φ-many m _ _ _ f g

  open bool-neutral

  module _ {B̂ Ĉ : Ptd i} (m : Nat)
           (g : B̂ →̇ Ĉ) where

    c₀ = snd (⊙Ω^ m Ĉ)

    {- Lemma 4.8 -}
    null-on-pspaces :
      ((f : (⊙Sphere' {i} m) →̇ B̂) → isNull∙ (g ⊙∘ f))
      ≃
      isNulld (ap^ m g)

    null-on-pspaces = -- {!equiv-Π-l!}

      ((f : (⊙Sphere' {i} m) →̇ B̂) → isNull∙ (g ⊙∘ f))

        ≃⟨ equiv-Π-r (λ f → isNull-Φ-Sphere m f g) ⟩

      ((f : (⊙Sphere' {i} m) →̇ B̂) → isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Sphere' {i} O) B̂ m f))

        ≃⟨ equiv-Π-l
             {A = (⊙Sphere' {i} m) →̇ B̂}
             {B = (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)}
             (λ f' → isNull∙ ((ap^ m g) ⊙∘ f'))
             {h = Φ-iter (⊙Sphere' {i} O) B̂ m}
             (Φ-iter-equiv _ _ m)
              ⟩

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)) → isNull∙ ((ap^ m g) ⊙∘ f'))

        ≃⟨ equiv-Π-r {A = ⊙Sphere' {i} O →̇ (⊙Ω^ m B̂)} (λ _ → isNull-equiv _) ⟩

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)) → isNull∙' ((ap^ m g) ⊙∘ f'))

        ≃⟨ ide _ ⟩

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)) → Σ ((x : bool) → fst ((ap^ m g) ⊙∘ f') x == _) λ h → h tt₀ == _)

        ≃⟨ equiv-Π-r {A = ⊙Sphere' {i} O →̇ (⊙Ω^ m B̂)}
                     (λ fp → reduction (λ b → fst (ap^ m g ⊙∘ fp) b == null.b₀ (ap^ m g ⊙∘ fp)) _) ⟩

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)) → fst ((ap^ m g) ⊙∘ f') ff₀ == _)

        ≃⟨ ide _ ⟩

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)) → fst (ap^ m g) (fst f' ff₀) == _)

        ≃⟨ equiv-Π-l {A = (⊙Sphere' {i} O) →̇ (⊙Ω^ m B̂)}
                     {B = fst (⊙Ω^ m B̂)}
                     _
                     (snd (reduction (λ _ → fst (⊙Ω^ m B̂)) _)) ⟩

      ((x : fst (⊙Ω^ m B̂)) → fst (ap^ m g) x == c₀)

        ≃⟨ ide _ ⟩

      isNulld (ap^ m g)
        ≃∎


open import Relation.Binary.Core

module BBHeap.Push {A : Set}
                  (_≤_ : A → A → Set)
                  (tot≤ : Total _≤_)
                  (trans≤ : Transitive _≤_) where

open import BBHeap _≤_
open import BBHeap.Equality _≤_
open import BBHeap.Equality.Properties _≤_
open import Bound.Lower A
open import Bound.Lower.Order _≤_
open import Bound.Lower.Order.Properties _≤_ trans≤
open import Data.Sum renaming (_⊎_ to _∨_)
open import Order.Total _≤_ tot≤

mutual
  push⋘ : {b : Bound}{x y : A}{l r : BBHeap (val y)} → LeB b (val x) → LeB b (val y) → l ⋘ r → BBHeap b
  push⋘ b≤x _ lf⋘ = left b≤x lf⋘
  push⋘ {x = x} b≤x b≤y (ll⋘ {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =  left b≤x (ll⋘ (lexy x≤y₁) (lexy (trans≤ x≤y₁ y₁≤y₂)) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂)
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋘r' = ll⋘ (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        pl'⋘r' = lemma≈⋘ pl'≈l' l'⋘r'
                   in left (transLeB b≤y y≤y₁) pl'⋘r'
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ = left b≤x (ll⋘ (lexy (trans≤ x≤y₂ y₂≤y₁)) (lexy x≤y₂) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂)
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋘r' = ll⋘ (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        l'⋘pr' = lemma⋘≈ l'⋘r' r'≈pr'
                   in left (transLeB b≤y y≤y₂) l'⋘pr'
  push⋘ {x = x} b≤x b≤y (lr⋘ {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =  left b≤x (lr⋘ (lexy x≤y₁) (lexy (trans≤ x≤y₁ y₁≤y₂)) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂)
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let pl'≈l' = lemma-push⋙ (lexy y₁≤x) (lexy refl≤) l₁⋙r₁ ;
                        l'⋘r' = lr⋘ (lexy refl≤) (lexy y₁≤y₂) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        pl'⋘r' = lemma≈⋘ pl'≈l' l'⋘r'
                   in left (transLeB b≤y y≤y₁) pl'⋘r'
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ = left b≤x (lr⋘ (lexy (trans≤ x≤y₂ y₂≤y₁)) (lexy x≤y₂) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂)
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋘r' = lr⋘ (lexy y₂≤y₁) (lexy refl≤) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        l'⋘pr'  = lemma⋘≈ l'⋘r' r'≈pr'
                   in left (transLeB b≤y y≤y₂) l'⋘pr'

  push⋙ : {b : Bound}{x y : A}{l r : BBHeap (val y)} → LeB b (val x) → LeB b (val y) → l ⋙ r → BBHeap b
  push⋙ {x = x} b≤x b≤y (⋙lf {x = z} y≤z)
      with tot≤ x z
  ... | inj₁ x≤z = right b≤x (⋙lf (lexy x≤z))
  ... | inj₂ z≤x = right (transLeB b≤y y≤z) (⋙lf (lexy z≤x))
  push⋙ {x = x} b≤x b≤y (⋙rl {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ = right b≤x (⋙rl (lexy x≤y₁) (lexy (trans≤ x≤y₁ y₁≤y₂)) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂)
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋙r' = ⋙rl (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        pl'⋙r' = lemma≈⋙ pl'≈l' l'⋙r'
                   in right (transLeB b≤y y≤y₁) pl'⋙r'
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ = right b≤x (⋙rl (lexy (trans≤ x≤y₂ y₂≤y₁)) (lexy x≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂)
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋙r' = ⋙rl (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        l'⋙pr' = lemma⋙≈ l'⋙r' r'≈pr'
                   in right (transLeB b≤y y≤y₂) l'⋙pr'
  push⋙ {x = x} b≤x b≤y (⋙rr {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ = right b≤x (⋙rr (lexy x≤y₁) (lexy (trans≤ x≤y₁ y₁≤y₂)) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂)
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋙r' = ⋙rr (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        pl'⋙r' = lemma≈⋙ pl'≈l' l'⋙r'
                   in right (transLeB b≤y y≤y₁) pl'⋙r'
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ = right b≤x (⋙rr (lexy (trans≤ x≤y₂ y₂≤y₁)) (lexy x≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂)
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let pr'≈r' = lemma-push⋙ (lexy y₂≤x) (lexy refl≤) l₂⋙r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋙r' = ⋙rr (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        l'⋙pr' = lemma⋙≈ l'⋙r' r'≈pr'
                   in right (transLeB b≤y y≤y₂) l'⋙pr'

  lemma-push⋘ : {b : Bound}{x y : A}{l r : BBHeap (val y)}(b≤x : LeB b (val x))(b≤y : LeB b (val y))(l⋘r : l ⋘ r) → push⋘ b≤x b≤y l⋘r ≈ left b≤y l⋘r
  lemma-push⋘ b≤x b≤y lf⋘ = ≈left b≤x b≤y lf⋘ lf⋘ ≈leaf ≈leaf
  lemma-push⋘ {x = x} b≤x b≤y (ll⋘ {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =
                   let l⋘r = ll⋘ y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        x≤y₂ = lexy (trans≤ x≤y₁ y₁≤y₂) ;
                        l'⋘r' = ll⋘ (lexy x≤y₁) x≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        l'≈l = ≈left (lexy x≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈left x≤y₂ y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈left b≤x b≤y l'⋘r' l⋘r l'≈l r'≈r
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let l⋘r = ll⋘ y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋘r' = ll⋘ (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        pl'⋘r' = lemma≈⋘ pl'≈l' l'⋘r' ;
                        l'≈l = ≈left (lexy refl≤) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        pl'≈l = trans≈ pl'≈l' l'≈l ;
                        r'≈r = ≈left (lexy y₁≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈left (transLeB b≤y y≤y₁) b≤y pl'⋘r' l⋘r pl'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ =
                   let l⋘r = ll⋘ y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        x≤y₁ = lexy (trans≤ x≤y₂ y₂≤y₁) ;
                        l'⋘r' = ll⋘ x≤y₁ (lexy x≤y₂) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        r'≈r = ≈left (lexy x≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈ ;
                        l'≈l = ≈left x≤y₁ y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈
                   in ≈left b≤x b≤y l'⋘r' l⋘r l'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let l⋘r = ll⋘ y≤y₁ y≤y₂ l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋘r' = ll⋘ (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₂⋘r₂ l₂≃r₂ r₁≃l₂ ;
                        l'⋘pr' = lemma⋘≈ l'⋘r' r'≈pr' ;
                        r'≈r = ≈left (lexy refl≤) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈ ;
                        pr'≈r = trans≈ pr'≈r' r'≈r ;
                        l'≈l = ≈left (lexy y₂≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈
                   in ≈left (transLeB b≤y y≤y₂) b≤y l'⋘pr' l⋘r l'≈l pr'≈r
  lemma-push⋘ {x = x} b≤x b≤y (lr⋘ {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =
                   let l⋘r = lr⋘ y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        x≤y₂ = lexy (trans≤ x≤y₁ y₁≤y₂) ;
                        l'⋘r' = lr⋘ (lexy x≤y₁) x≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        l'≈l = ≈right (lexy x≤y₁) y≤y₁ l₁⋙r₁ l₁⋙r₁ refl≈ refl≈ ;
                        r'≈r = ≈left x≤y₂ y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈left b≤x b≤y l'⋘r' l⋘r l'≈l r'≈r
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let l⋘r = lr⋘ y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        pl'≈l' = lemma-push⋙ (lexy y₁≤x) (lexy refl≤) l₁⋙r₁ ;
                        l'⋘r' = lr⋘ (lexy refl≤) (lexy y₁≤y₂) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        pl'⋘r' = lemma≈⋘ pl'≈l' l'⋘r' ;
                        l'≈l = ≈right (lexy refl≤) y≤y₁ l₁⋙r₁ l₁⋙r₁ refl≈ refl≈ ;
                        pl'≈l = trans≈ pl'≈l' l'≈l ;
                        r'≈r = ≈left (lexy y₁≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈left (transLeB b≤y y≤y₁) b≤y pl'⋘r' l⋘r pl'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ =
                   let l⋘r = lr⋘ y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        x≤y₁ = lexy (trans≤ x≤y₂ y₂≤y₁) ;
                        l'⋘r' = lr⋘ x≤y₁ (lexy x≤y₂) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        l'≈l = ≈right x≤y₁ y≤y₁ l₁⋙r₁ l₁⋙r₁ refl≈ refl≈ ;
                        r'≈r = ≈left (lexy x≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈left b≤x b≤y l'⋘r' l⋘r l'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let l⋘r = lr⋘ y≤y₁ y≤y₂ l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋘r' = lr⋘ (lexy y₂≤y₁) (lexy refl≤) l₁⋙r₁ l₂⋘r₂ l₂≃r₂ l₁⋗l₂ ;
                        l'⋘pr'  = lemma⋘≈ l'⋘r' r'≈pr' ;
                        l'≈l = ≈right (lexy y₂≤y₁) y≤y₁ l₁⋙r₁ l₁⋙r₁ refl≈ refl≈ ;
                        r'≈r = ≈left (lexy refl≤) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈ ;
                        pr'≈r = trans≈ pr'≈r' r'≈r
                   in ≈left (transLeB b≤y y≤y₂) b≤y l'⋘pr' l⋘r l'≈l pr'≈r

  lemma-push⋙ : {b : Bound}{x y : A}{l r : BBHeap (val y)}(b≤x : LeB b (val x))(b≤y : LeB b (val y))(l⋙r : l ⋙ r) → push⋙ b≤x b≤y l⋙r ≈ right b≤y l⋙r
  lemma-push⋙ {x = x} b≤x b≤y (⋙lf {x = z} y≤z)
      with tot≤ x z
  ... | inj₁ x≤z = ≈right b≤x b≤y (⋙lf (lexy x≤z)) (⋙lf y≤z) (≈left (lexy x≤z) y≤z lf⋘ lf⋘ ≈leaf ≈leaf) ≈leaf
  ... | inj₂ z≤x = ≈right (transLeB b≤y y≤z) b≤y (⋙lf (lexy z≤x)) (⋙lf y≤z) (≈left (lexy z≤x) y≤z lf⋘ lf⋘ ≈leaf ≈leaf) ≈leaf
  lemma-push⋙ {x = x} b≤x b≤y (⋙rl {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =
                   let l⋙r = ⋙rl y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        x≤y₂ = lexy (trans≤ x≤y₁ y₁≤y₂) ;
                        l'⋙r' = ⋙rl (lexy x≤y₁) x≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        l'≈l = ≈left (lexy x≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈left x≤y₂ y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈right b≤x b≤y l'⋙r' l⋙r l'≈l r'≈r
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let l⋙r = ⋙rl y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋙r' = ⋙rl (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        pl'⋙r' = lemma≈⋙ pl'≈l' l'⋙r' ;
                        l'≈l = ≈left (lexy refl≤) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        pl'≈l = trans≈ pl'≈l' l'≈l ;
                        r'≈r = ≈left (lexy y₁≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈right (transLeB b≤y y≤y₁) b≤y pl'⋙r' l⋙r pl'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ =
                   let l⋙r = ⋙rl y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        x≤y₁ = lexy (trans≤ x≤y₂ y₂≤y₁) ;
                        l'⋙r' = ⋙rl x≤y₁ (lexy x≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        l'≈l = ≈left x≤y₁ y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈left (lexy x≤y₂) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈
                   in ≈right b≤x b≤y l'⋙r' l⋙r l'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let l⋙r = ⋙rl y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        pr'≈r' = lemma-push⋘ (lexy y₂≤x) (lexy refl≤) l₂⋘r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋙r' = ⋙rl (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₁≃r₁ l₂⋘r₂ l₁⋗r₂ ;
                        l'⋙pr' = lemma⋙≈ l'⋙r' r'≈pr' ;
                        l'≈l = ≈left (lexy y₂≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈left (lexy refl≤) y≤y₂ l₂⋘r₂ l₂⋘r₂ refl≈ refl≈ ;
                        pr'≈r = trans≈ pr'≈r' r'≈r
                   in ≈right (transLeB b≤y y≤y₂) b≤y l'⋙pr' l⋙r l'≈l pr'≈r
  lemma-push⋙ {x = x} b≤x b≤y (⋙rr {x = y₁} {x' = y₂} y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂)
      with tot≤ y₁ y₂ | tot≤ x y₁ | tot≤ x y₂
  ... | inj₁ y₁≤y₂ | inj₁ x≤y₁ | _ =
                   let l⋙r = ⋙rr y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        x≤y₂ = lexy (trans≤ x≤y₁ y₁≤y₂) ;
                        l'⋙r' = ⋙rr (lexy x≤y₁) x≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        l'≈l = ≈left (lexy x≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈right x≤y₂ y≤y₂ l₂⋙r₂ l₂⋙r₂ refl≈ refl≈
                   in ≈right b≤x b≤y l'⋙r' l⋙r l'≈l r'≈r
  ... | inj₁ y₁≤y₂ | inj₂ y₁≤x | _ =
                   let l⋙r = ⋙rr y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        pl'≈l' = lemma-push⋘ (lexy y₁≤x) (lexy refl≤) l₁⋘r₁ ;
                        l'⋙r' = ⋙rr (lexy refl≤) (lexy y₁≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        pl'⋙r' = lemma≈⋙ pl'≈l' l'⋙r' ;
                        l'≈l = ≈left (lexy refl≤) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        pl'≈l = trans≈ pl'≈l' l'≈l ;
                        r'≈r = ≈right (lexy y₁≤y₂) y≤y₂ l₂⋙r₂ l₂⋙r₂ refl≈ refl≈
                   in ≈right (transLeB b≤y y≤y₁) b≤y pl'⋙r' l⋙r pl'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₁ x≤y₂ =
                   let l⋙r = ⋙rr y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        x≤y₁ = lexy (trans≤ x≤y₂ y₂≤y₁) ;
                        l'⋙r' = ⋙rr x≤y₁ (lexy x≤y₂) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        l'≈l = ≈left x≤y₁ y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈right (lexy x≤y₂) y≤y₂ l₂⋙r₂ l₂⋙r₂ refl≈ refl≈
                   in ≈right b≤x b≤y l'⋙r' l⋙r l'≈l r'≈r
  ... | inj₂ y₂≤y₁ | _ | inj₂ y₂≤x =
                   let l⋙r = ⋙rr y≤y₁ y≤y₂ l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        pr'≈r' = lemma-push⋙ (lexy y₂≤x) (lexy refl≤) l₂⋙r₂ ;
                        r'≈pr' = sym≈ pr'≈r' ;
                        l'⋙r' = ⋙rr (lexy y₂≤y₁) (lexy refl≤) l₁⋘r₁ l₁≃r₁ l₂⋙r₂ l₁≃l₂ ;
                        l'⋙pr' = lemma⋙≈ l'⋙r' r'≈pr' ;
                        l'≈l = ≈left (lexy y₂≤y₁) y≤y₁ l₁⋘r₁ l₁⋘r₁ refl≈ refl≈ ;
                        r'≈r = ≈right (lexy refl≤) y≤y₂ l₂⋙r₂ l₂⋙r₂ refl≈ refl≈ ;
                        pr'≈r = trans≈ pr'≈r' r'≈r
                   in ≈right (transLeB b≤y y≤y₂) b≤y l'⋙pr' l⋙r l'≈l pr'≈r

push : {b : Bound}{x : A} → LeB b (val x) → BBHeap b → BBHeap b
push _ leaf = leaf
push b≤x (left b≤y l⋘r) = push⋘ b≤x b≤y l⋘r
push b≤x (right b≤y l⋙r) = push⋙ b≤x b≤y l⋙r


open import Common.Prelude
open import Common.Reflect

id : Nat → Nat
id x = x

-- Used to give error:
-- unquote must be applied to a term
-- when checking that the expression unquote has type _3 x
i : Nat → Nat
i x = unquote (def (quote id) []) x


module Issue512 where

open import Common.Equality

data A : Set where
  a b : A

proof : a ≡ a
proof = refl

f : A → A → A
f x y rewrite proof = {!!}

-- gave error below (now complains about LEVELZERO instead of STRING):
-- No binding for builtin thing STRING, use {-# BUILTIN STRING name #-} to bind it to 'name'
-- when checking that the type of the generated with function
-- (w : A) → _≡_ {"Max []"} {A} w w → (x y : A) → A is well-formed

-- Andreas, 2014-05-17: After fixing 1110, there is no error any more,
-- just an unsolved meta.


------------------------------------------------------------------------
-- The "time complexity" of the compiled program matches the one
-- obtained from the instrumented interpreter
------------------------------------------------------------------------

open import Prelude hiding (_+_; _*_)

import Lambda.Syntax

module Lambda.Compiler-correctness.Steps-match
  {Name : Type}
  (open Lambda.Syntax Name)
  (def : Name → Tm 1)
  where

import Equality.Propositional as E
open import Logical-equivalence using (_⇔_)
import Tactic.By.Propositional as By
open import Prelude.Size

open import Conat E.equality-with-J as Conat
  using (Conat; zero; suc; force; ⌜_⌝; _+_; _*_; max;
         [_]_≤_; step-≤; step-∼≤; _∎≤)
open import Function-universe E.equality-with-J hiding (_∘_)
open import List E.equality-with-J using (_++_)
open import Monad E.equality-with-J using (return; _>>=_; _⟨$⟩_)
open import Vec.Data E.equality-with-J

open import Delay-monad
open import Delay-monad.Bisimilarity
open import Delay-monad.Quantitative-weak-bisimilarity

open import Lambda.Compiler def
open import Lambda.Delay-crash
open import Lambda.Interpreter.Steps def
open import Lambda.Virtual-machine.Instructions Name hiding (crash)
open import Lambda.Virtual-machine comp-name

private
  module C = Closure Code
  module T = Closure Tm

------------------------------------------------------------------------
-- Some lemmas

-- A rearrangement lemma for ⟦_⟧.

⟦⟧-· :
  ∀ {n} (t₁ t₂ : Tm n) {ρ} {k : T.Value → Delay-crash C.Value ∞} →
  ⟦ t₁ · t₂ ⟧ ρ >>= k ∼
  ⟦ t₁ ⟧ ρ >>= λ v₁ → ⟦ t₂ ⟧ ρ >>= λ v₂ → v₁ ∙ v₂ >>= k
⟦⟧-· t₁ t₂ {ρ} {k} =
  ⟦ t₁ · t₂ ⟧ ρ >>= k      ∼⟨⟩

  (do v₁ ← ⟦ t₁ ⟧ ρ
      v₂ ← ⟦ t₂ ⟧ ρ
      v₁ ∙ v₂) >>= k       ∼⟨ symmetric (associativity (⟦ t₁ ⟧ _) _ _) ⟩

  (do v₁ ← ⟦ t₁ ⟧ ρ
      (do v₂ ← ⟦ t₂ ⟧ ρ
          v₁ ∙ v₂) >>= k)  ∼⟨ (⟦ t₁ ⟧ _ ∎) >>=-cong (λ _ → symmetric (associativity (⟦ t₂ ⟧ _) _ _)) ⟩

  (do v₁ ← ⟦ t₁ ⟧ ρ
      v₂ ← ⟦ t₂ ⟧ ρ
      v₁ ∙ v₂ >>= k)       ∎

-- Lemmas related to conatural numbers.

private

  lemma₁ : ∀ {δ} → [ ∞ ] δ ≤ ⌜ 1 ⌝ + δ
  lemma₁ = Conat.≤suc

  lemma₂ : ∀ {δ} → [ ∞ ] δ ≤ max ⌜ 1 ⌝ δ
  lemma₂ = Conat.ʳ≤max ⌜ 1 ⌝ _

  lemma₃ : ∀ {δ} → [ ∞ ] max ⌜ 1 ⌝ δ ≤ max ⌜ 1 ⌝ δ + ⌜ 1 ⌝
  lemma₃ = Conat.m≤m+n

  lemma₄ : ∀ {δ} → [ ∞ ] δ ≤ max ⌜ 1 ⌝ δ + ⌜ 1 ⌝
  lemma₄ {δ} =
    δ                    ≤⟨ lemma₂ ⟩
    max ⌜ 1 ⌝ δ          ≤⟨ lemma₃ {δ = δ} ⟩
    max ⌜ 1 ⌝ δ + ⌜ 1 ⌝  ∎≤

  lemma₅ : ∀ {δ} → [ ∞ ] ⌜ 1 ⌝ + δ ≤ δ + ⌜ 1 ⌝
  lemma₅ {δ} =
    ⌜ 1 ⌝ + δ  ∼⟨ Conat.+-comm ⌜ 1 ⌝ ⟩≤
    δ + ⌜ 1 ⌝  ∎≤

  lemma₆ : ∀ {δ} → [ ∞ ] ⌜ 1 ⌝ + δ ≤ max ⌜ 1 ⌝ δ + ⌜ 1 ⌝
  lemma₆ {δ} =
    ⌜ 1 ⌝ + δ            ≤⟨ lemma₅ ⟩
    δ + ⌜ 1 ⌝            ≤⟨ lemma₂ Conat.+-mono (⌜ 1 ⌝ ∎≤) ⟩
    max ⌜ 1 ⌝ δ + ⌜ 1 ⌝  ∎≤

  lemma₇ : ∀ {δ} → [ ∞ ] max ⌜ 1 ⌝ (⌜ 1 ⌝ + δ) ≤ ⌜ 1 ⌝ + δ
  lemma₇ {δ} = suc λ { .force →
    δ  ∎≤ }

  lemma₈ : ∀ {δ} → [ ∞ ] max ⌜ 1 ⌝ (max ⌜ 1 ⌝ δ) ≤ max ⌜ 1 ⌝ δ
  lemma₈ {δ} = suc λ { .force →
    Conat.pred δ  ∎≤ }

  lemma₉ :
    ∀ {δ} → [ ∞ ] max ⌜ 1 ⌝ (max ⌜ 1 ⌝ (max ⌜ 1 ⌝ δ)) ≤ max ⌜ 1 ⌝ δ
  lemma₉ {δ} =
    max ⌜ 1 ⌝ (max ⌜ 1 ⌝ (max ⌜ 1 ⌝ δ)) ≤⟨ lemma₈ ⟩
    max ⌜ 1 ⌝ (max ⌜ 1 ⌝ δ)             ≤⟨ lemma₈ ⟩
    max ⌜ 1 ⌝ δ                         ∎≤

  lemma₁₀ : ∀ {δ} → [ ∞ ] ⌜ 1 ⌝ + ⌜ 0 ⌝ ≤ max ⌜ 1 ⌝ δ
  lemma₁₀ = suc λ { .force → zero }

  lemma₁₁ : Conat.[ ∞ ] max ⌜ 1 ⌝ ⌜ 1 ⌝ ∼ ⌜ 1 ⌝
  lemma₁₁ = suc λ { .force → zero }

  lemma₁₂ : Conat.[ ∞ ] ⌜ 1 ⌝ + ⌜ 1 ⌝ ∼ ⌜ 2 ⌝
  lemma₁₂ = Conat.symmetric-∼ (Conat.⌜⌝-+ 1)

------------------------------------------------------------------------
-- Well-formed continuations and stacks

-- A continuation is OK with respect to a certain state and conatural
-- number if the following property is satisfied.

Cont-OK :
  Size → State → (T.Value → Delay-crash C.Value ∞) → Conat ∞ → Type
Cont-OK i ⟨ c , s , ρ ⟩ k δ =
  ∀ v → [ i ∣ ⌜ 1 ⌝ ∣ δ ] exec ⟨ c , val (comp-val v) ∷ s , ρ ⟩ ≳ k v

-- If the In-tail-context parameter indicates that we are in a tail
-- context, then the stack must have a certain shape, and it must be
-- related to the continuation and the conatural number in a certain
-- way.

data Stack-OK
       (i : Size) (k : T.Value → Delay-crash C.Value ∞) (δ : Conat ∞) :
       In-tail-context → Stack → Type where
  unrestricted : ∀ {s} → Stack-OK i k δ false s
  restricted   : ∀ {s n} {c : Code n} {ρ : C.Env n} →
                 Cont-OK i ⟨ c , s , ρ ⟩ k δ →
                 Stack-OK i k δ true (ret c ρ ∷ s)

-- A lemma that can be used to show that certain stacks are OK.

ret-ok :
  ∀ {p i s n c} {ρ : C.Env n} {k δ} →
  Cont-OK i ⟨ c , s , ρ ⟩ k δ →
  Stack-OK i k (⌜ 1 ⌝ + δ) p (ret c ρ ∷ s)
ret-ok {true}  c-ok = restricted (weakenˡ lemma₁ ∘ c-ok)
ret-ok {false} _    = unrestricted

------------------------------------------------------------------------
-- The semantics of the compiled program matches that of the source
-- code

mutual

  -- Some lemmas making up the main part of the compiler correctness
  -- result.

  ⟦⟧-correct :
    ∀ {i n} (t : Tm n) (ρ : T.Env n) {c s}
      {k : T.Value → Delay-crash C.Value ∞} {tc δ} →
    Stack-OK i k δ tc s →
    Cont-OK i ⟨ c , s , comp-env ρ ⟩ k δ →
    [ i ∣ ⌜ 1 ⌝ ∣ max ⌜ 1 ⌝ δ ]
      exec ⟨ comp tc t c , s , comp-env ρ ⟩ ≳ ⟦ t ⟧ ρ >>= k

  ⟦⟧-correct (var x) ρ {c} {s} {k} _ c-ok =
    exec ⟨ var x ∷ c , s , comp-env ρ ⟩                              ≳⟨ later (λ { .force →
      exec ⟨ c , val By.⟨ index (comp-env ρ) x ⟩ ∷ s , comp-env ρ ⟩       ≡⟨ By.⟨by⟩ (comp-index ρ x) ⟩ˢ
      exec ⟨ c , val (comp-val (index ρ x)) ∷ s , comp-env ρ ⟩            ≳⟨ weakenˡ lemma₄ (c-ok (index ρ x)) ⟩ˢ
      k (index ρ x)                                                       ∎ }) ⟩ˢ
    ⟦ var x ⟧ ρ >>= k                                                ∎

  ⟦⟧-correct (lam t) ρ {c} {s} {k} _ c-ok =
    exec ⟨ clo (comp-body t) ∷ c , s , comp-env ρ ⟩             ≳⟨ later (λ { .force →
      exec ⟨ c , val (comp-val (T.lam t ρ)) ∷ s , comp-env ρ ⟩       ≳⟨ weakenˡ lemma₄ (c-ok (T.lam t ρ)) ⟩ˢ
      k (T.lam t ρ)                                                  ∎ }) ⟩ˢ
    ⟦ lam t ⟧ ρ >>= k                                           ∎

  ⟦⟧-correct (t₁ · t₂) ρ {c} {s} {k} _ c-ok =
    exec ⟨ comp false t₁ (comp false t₂ (app ∷ c))
         , s
         , comp-env ρ
         ⟩                                                   ≳⟨ weakenˡ lemma₉ (⟦⟧-correct t₁ _ unrestricted λ v₁ →

      exec ⟨ comp false t₂ (app ∷ c)
           , val (comp-val v₁) ∷ s
           , comp-env ρ
           ⟩                                                       ≳⟨ (⟦⟧-correct t₂ _ unrestricted λ v₂ →

        exec ⟨ app ∷ c
             , val (comp-val v₂) ∷ val (comp-val v₁) ∷ s
             , comp-env ρ
             ⟩                                                           ≳⟨ ∙-correct v₁ v₂ c-ok ⟩ˢ

        v₁ ∙ v₂ >>= k                                                    ∎) ⟩ˢ

      (⟦ t₂ ⟧ ρ >>= λ v₂ → v₁ ∙ v₂ >>= k)                          ∎) ⟩ˢ

    (⟦ t₁ ⟧ ρ >>= λ v₁ → ⟦ t₂ ⟧ ρ >>= λ v₂ → v₁ ∙ v₂ >>= k)  ∼⟨ symmetric (⟦⟧-· t₁ t₂) ⟩

    ⟦ t₁ · t₂ ⟧ ρ >>= k                                      ∎

  ⟦⟧-correct (call f t) ρ {c} {s} {k} unrestricted c-ok =
    exec ⟨ comp false (call f t) c , s , comp-env ρ ⟩         ∼⟨⟩ˢ

    exec ⟨ comp false t (cal f ∷ c) , s , comp-env ρ ⟩        ≳⟨ (⟦⟧-correct t _ unrestricted λ v →

      exec ⟨ cal f ∷ c , val (comp-val v) ∷ s , comp-env ρ ⟩        ≳⟨ (later λ { .force → weakenˡ lemma₅ (

        exec ⟨ comp-name f
             , ret c (comp-env ρ) ∷ s
             , comp-val v ∷ []
             ⟩                                                            ≳⟨ body-lemma (def f) [] c-ok ⟩ˢ

        (⟦ def f ⟧ (v ∷ []) >>= k)                                        ∎) }) ⟩ˢ

      (T.lam (def f) [] ∙ v >>= k)                                  ∎) ⟩ˢ

    (⟦ t ⟧ ρ >>= λ v → T.lam (def f) [] ∙ v >>= k)            ∼⟨ associativity (⟦ t ⟧ ρ) _ _ ⟩

    (⟦ t ⟧ ρ >>= λ v → T.lam (def f) [] ∙ v) >>= k            ∼⟨⟩

    ⟦ call f t ⟧ ρ >>= k                                      ∎

  ⟦⟧-correct (call f t) ρ {c} {ret c′ ρ′ ∷ s} {k} (restricted c-ok) _ =
    exec ⟨ comp true (call f t) c , ret c′ ρ′ ∷ s , comp-env ρ ⟩    ∼⟨⟩ˢ

    exec ⟨ comp false t (tcl f ∷ c) , ret c′ ρ′ ∷ s , comp-env ρ ⟩  ≳⟨ (⟦⟧-correct t _ unrestricted λ v →

      exec ⟨ tcl f ∷ c
           , val (comp-val v) ∷ ret c′ ρ′ ∷ s
           , comp-env ρ
           ⟩                                                              ≳⟨ (later λ { .force → weakenˡ lemma₅ (

        exec ⟨ comp-name f
             , ret c′ ρ′ ∷ s
             , comp-val v ∷ []
             ⟩                                                                  ≳⟨ body-lemma (def f) [] c-ok ⟩ˢ

        ⟦ def f ⟧ (v ∷ []) >>= k                                                ∎) }) ⟩ˢ

      T.lam (def f) [] ∙ v >>= k                                          ∎) ⟩ˢ

    (⟦ t ⟧ ρ >>= λ v → T.lam (def f) [] ∙ v >>= k)                  ∼⟨ associativity (⟦ t ⟧ ρ) _ _ ⟩

    (⟦ t ⟧ ρ >>= λ v → T.lam (def f) [] ∙ v) >>= k                  ∼⟨⟩

    ⟦ call f t ⟧ ρ >>= k                                            ∎

  ⟦⟧-correct (con b) ρ {c} {s} {k} _ c-ok =
    exec ⟨ con b ∷ c , s , comp-env ρ ⟩                       ≳⟨ later (λ { .force →
      exec ⟨ c , val (comp-val (T.con b)) ∷ s , comp-env ρ ⟩       ≳⟨ weakenˡ lemma₄ (c-ok (T.con b)) ⟩ˢ
      k (T.con b)                                                  ∎ }) ⟩ˢ
    ⟦ con b ⟧ ρ >>= k                                         ∎

  ⟦⟧-correct (if t₁ t₂ t₃) ρ {c} {s} {k} {tc} s-ok c-ok =
    exec ⟨ comp false t₁ (bra (comp tc t₂ []) (comp tc t₃ []) ∷ c)
         , s
         , comp-env ρ
         ⟩                                                          ≳⟨ weakenˡ lemma₈
                                                                         (⟦⟧-correct t₁ _ unrestricted λ v₁ → ⟦if⟧-correct v₁ t₂ t₃ s-ok c-ok) ⟩ˢ

    (⟦ t₁ ⟧ ρ >>= λ v₁ → ⟦if⟧ v₁ t₂ t₃ ρ >>= k)                     ∼⟨ associativity (⟦ t₁ ⟧ ρ) _ _ ⟩

    (⟦ t₁ ⟧ ρ >>= λ v₁ → ⟦if⟧ v₁ t₂ t₃ ρ) >>= k                     ∼⟨⟩

    ⟦ if t₁ t₂ t₃ ⟧ ρ >>= k                                         ∎

  body-lemma :
    ∀ {i n n′} (t : Tm (suc n)) ρ {ρ′ : C.Env n′} {c s v}
      {k : T.Value → Delay-crash C.Value ∞} {δ} →
    Cont-OK i ⟨ c , s , ρ′ ⟩ k δ →
    [ i ∣ ⌜ 1 ⌝ ∣ ⌜ 1 ⌝ + δ ]
      exec ⟨ comp-body t , ret c ρ′ ∷ s , comp-val v ∷ comp-env ρ ⟩ ≳
      ⟦ t ⟧ (v ∷ ρ) >>= k
  body-lemma t ρ {ρ′} {c} {s} {v} {k} c-ok =
    exec ⟨ comp-body t , ret c ρ′ ∷ s , comp-val v ∷ comp-env ρ ⟩  ∼⟨⟩ˢ

    exec ⟨ comp-body t , ret c ρ′ ∷ s , comp-env (v ∷ ρ) ⟩         ≳⟨ weakenˡ lemma₇ (⟦⟧-correct t (_ ∷ _) (ret-ok c-ok) λ v′ →

      exec ⟨ ret ∷ []
           , val (comp-val v′) ∷ ret c ρ′ ∷ s
           , comp-env (v ∷ ρ)
           ⟩                                                            ≳⟨⟩ˢ

      exec ⟨ c , val (comp-val v′) ∷ s , ρ′ ⟩                           ≳⟨ c-ok v′ ⟩ˢ

      k v′                                                              ∎) ⟩ˢ

    ⟦ t ⟧ (v ∷ ρ) >>= k                                            ∎

  ∙-correct :
    ∀ {i n} v₁ v₂ {ρ : C.Env n} {c s}
      {k : T.Value → Delay-crash C.Value ∞} {δ} →
    Cont-OK i ⟨ c , s , ρ ⟩ k δ →
    [ i ∣ ⌜ 1 ⌝ ∣ max ⌜ 1 ⌝ δ ]
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val v₁) ∷ s , ρ ⟩ ≳
      v₁ ∙ v₂ >>= k

  ∙-correct (T.lam t₁ ρ₁) v₂ {ρ} {c} {s} {k} c-ok =
    exec ⟨ app ∷ c
         , val (comp-val v₂) ∷ val (comp-val (T.lam t₁ ρ₁)) ∷ s
         , ρ
         ⟩                                                             ≳⟨ later (λ { .force → weakenˡ lemma₆ (

      exec ⟨ comp-body t₁ , ret c ρ ∷ s , comp-val v₂ ∷ comp-env ρ₁ ⟩       ≳⟨ body-lemma t₁ _ c-ok ⟩ˢ

      ⟦ t₁ ⟧ (v₂ ∷ ρ₁) >>= k                                                ∎) }) ⟩ˢ

    T.lam t₁ ρ₁ ∙ v₂ >>= k                                             ∎

  ∙-correct (T.con b) v₂ {ρ} {c} {s} {k} _ = weakenˡ lemma₁₀ (
    exec ⟨ app ∷ c
         , val (comp-val v₂) ∷ val (comp-val (T.con b)) ∷ s
         , ρ
         ⟩                                                   ≳⟨⟩ˢ

    crash                                                    ∼⟨⟩ˢ

    T.con b ∙ v₂ >>= k                                       ∎ˢ)

  ⟦if⟧-correct :
    ∀ {i n} v₁ (t₂ t₃ : Tm n) {ρ : T.Env n} {c s}
      {k : T.Value → Delay-crash C.Value ∞} {tc δ} →
    Stack-OK i k δ tc s →
    Cont-OK i ⟨ c , s , comp-env ρ ⟩ k δ →
    [ i ∣ ⌜ 1 ⌝ ∣ max ⌜ 1 ⌝ δ ]
      exec ⟨ bra (comp tc t₂ []) (comp tc t₃ []) ∷ c
           , val (comp-val v₁) ∷ s
           , comp-env ρ
           ⟩ ≳
      ⟦if⟧ v₁ t₂ t₃ ρ >>= k

  ⟦if⟧-correct (T.lam t₁ ρ₁) t₂ t₃ {ρ} {c} {s} {k} {tc} _ _ =
    weakenˡ lemma₁₀ (

      exec ⟨ bra (comp tc t₂ []) (comp tc t₃ []) ∷ c
           , val (comp-val (T.lam t₁ ρ₁)) ∷ s
           , comp-env ρ
           ⟩                                          ≳⟨⟩ˢ

      crash                                           ∼⟨⟩ˢ

      ⟦if⟧ (T.lam t₁ ρ₁) t₂ t₃ ρ >>= k                ∎ˢ)

  ⟦if⟧-correct (T.con true) t₂ t₃ {ρ} {c} {s} {k} {tc} s-ok c-ok =
    exec ⟨ bra (comp tc t₂ []) (comp tc t₃ []) ∷ c
         , val (comp-val (T.con true)) ∷ s
         , comp-env ρ
         ⟩                                          ≳⟨ later (λ { .force → weakenˡ lemma₃ (

      exec ⟨ comp tc t₂ [] ++ c , s , comp-env ρ ⟩       ≡⟨ By.by (comp-++ _ t₂) ⟩ˢ

      exec ⟨ comp tc t₂ c , s , comp-env ρ ⟩             ≳⟨ ⟦⟧-correct t₂ _ s-ok c-ok ⟩ˢ

      ⟦ t₂ ⟧ ρ >>= k                                     ∎) }) ⟩ˢ

    ⟦if⟧ (T.con true) t₂ t₃ ρ >>= k                 ∎

  ⟦if⟧-correct (T.con false) t₂ t₃ {ρ} {c} {s} {k} {tc} s-ok c-ok =
    exec ⟨ bra (comp tc t₂ []) (comp tc t₃ []) ∷ c
         , val (comp-val (T.con false)) ∷ s
         , comp-env ρ
         ⟩                                          ≳⟨ later (λ { .force → weakenˡ lemma₃ (

      exec ⟨ comp tc t₃ [] ++ c , s , comp-env ρ ⟩       ≡⟨ By.by (comp-++ _ t₃) ⟩ˢ

      exec ⟨ comp tc t₃ c , s , comp-env ρ ⟩             ≳⟨ ⟦⟧-correct t₃ _ s-ok c-ok ⟩ˢ

      ⟦ t₃ ⟧ ρ >>= k                                     ∎) }) ⟩ˢ

    ⟦if⟧ (T.con false) t₂ t₃ ρ >>= k                ∎

-- The "time complexity" of the compiled program is linear in the time
-- complexity obtained from the instrumented interpreter, and vice
-- versa.

steps-match :
  (t : Tm 0) →
  [ ∞ ] steps (⟦ t ⟧ []) ≤ steps (exec ⟨ comp₀ t , [] , [] ⟩)
    ×
  [ ∞ ] steps (exec ⟨ comp₀ t , [] , [] ⟩) ≤
        ⌜ 1 ⌝ + ⌜ 2 ⌝ * steps (⟦ t ⟧ [])
steps-match t =                                                  $⟨ ⟦⟧-correct t [] unrestricted (λ v → laterˡ (return (comp-val v) ∎ˢ)) ⟩
  [ ∞ ∣ ⌜ 1 ⌝ ∣ max ⌜ 1 ⌝ ⌜ 1 ⌝ ]
    exec ⟨ comp₀ t , [] , [] ⟩ ≳ comp-val ⟨$⟩ ⟦ t ⟧ []           ↝⟨ proj₂ ∘ _⇔_.to ≳⇔≈×steps≤steps² ⟩

  [ ∞ ] steps (comp-val ⟨$⟩ ⟦ t ⟧ []) ≤
        steps (exec ⟨ comp₀ t , [] , [] ⟩) ×
  [ ∞ ] steps (exec ⟨ comp₀ t , [] , [] ⟩) ≤
        max ⌜ 1 ⌝ ⌜ 1 ⌝ +
        (⌜ 1 ⌝ + ⌜ 1 ⌝) * steps (comp-val ⟨$⟩ ⟦ t ⟧ [])          ↝⟨ _⇔_.to (steps-⟨$⟩ Conat.≤-cong-∼ (_ Conat.∎∼)
                                                                              ×-cong
                                                                            (_ Conat.∎∼)
                                                                              Conat.≤-cong-∼
                                                                            lemma₁₁ Conat.+-cong lemma₁₂ Conat.*-cong steps-⟨$⟩) ⟩□
  [ ∞ ] steps (⟦ t ⟧ []) ≤ steps (exec ⟨ comp₀ t , [] , [] ⟩) ×
  [ ∞ ] steps (exec ⟨ comp₀ t , [] , [] ⟩) ≤
        ⌜ 1 ⌝ + ⌜ 2 ⌝ * steps (⟦ t ⟧ [])                         □


module _ where

import Agda.Builtin.Equality
open import Agda.Builtin.Sigma
open import Agda.Builtin.Unit
open import Agda.Primitive
open import Common.IO

data ⊥ : Set where

record R₁ a : Set (lsuc a) where
  field
    R : {A : Set a} → A → A → Set a
    r : {A : Set a} (x : A) → R x x

  P : Set a → Set a
  P A = (x y : A) → R x y

record R₂ (r : ∀ ℓ → R₁ ℓ) : Set₁ where
  field
    f :
      {X Y : Σ Set (R₁.P (r lzero))} →
      R₁.R (r (lsuc lzero)) X Y → fst X → fst Y

module M (r₁ : ∀ ℓ → R₁ ℓ) (r₂ : R₂ r₁) where

  open module R₁′ {ℓ} = R₁ (r₁ ℓ) public using (P)
  open module R₂′     = R₂ r₂     public

  ⊥-elim : {A : Set} → ⊥ → A
  ⊥-elim ()

  p : P ⊥
  p x = ⊥-elim x

open Agda.Builtin.Equality

r₁ : ∀ ℓ → R₁ ℓ
R₁.R (r₁ _) = _≡_
R₁.r (r₁ _) = λ _ → refl

r₂ : R₂ r₁
R₂.f r₂ refl x = x

open M r₁ r₂

data Unit : Set where
  unit : Unit

g : Σ Unit λ _ → P (Σ Set P) → ⊥
g = unit , λ h → f (h (⊤ , λ _ _ → refl) (⊥ , p)) tt

main : IO ⊤
main = return _


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

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

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

open import Tools.Product
import Tools.PropositionalEquality as PE

IdSymᵗᵛ : ∀ {A l t u e Γ}
         ([Γ] : ⊩ᵛ Γ)
         ([U] : Γ ⊩ᵛ⟨ ∞ ⟩ U l ^ [ ! , next l ] / [Γ])
         ([AU] : Γ ⊩ᵛ⟨ ∞ ⟩ A ∷ U l ^ [ ! , next l ] / [Γ] / [U])
         ([A] : Γ ⊩ᵛ⟨ ∞ ⟩ A ^ [ ! , ι l ] / [Γ])
         ([t] : Γ ⊩ᵛ⟨ ∞ ⟩ t ∷ A ^ [ ! , ι l ] / [Γ] / [A])
         ([u] : Γ ⊩ᵛ⟨ ∞ ⟩ u ∷ A ^ [ ! , ι l ] / [Γ] / [A])
         ([Id] : Γ ⊩ᵛ⟨ ∞ ⟩ Id A t u ^ [ % , ι l ] / [Γ]) →
         ([Idinv] : Γ ⊩ᵛ⟨ ∞ ⟩ Id A u t ^ [ % , ι l ] / [Γ]) →
         ([e] : Γ ⊩ᵛ⟨ ∞ ⟩ e ∷ Id A t u ^ [ % , ι l ] / [Γ] / [Id] ) →
         Γ ⊩ᵛ⟨ ∞ ⟩ Idsym A t u e ∷ Id A u t  ^ [ % , ι l ] / [Γ] / [Idinv]
IdSymᵗᵛ {A} {l} {t} {u} {e} {Γ} [Γ] [U] [AU] [A] [t] [u] [Id] [Idinv] [e] = validityIrr {A = Id A u t} {t = Idsym A t u e} [Γ] [Idinv] λ {Δ} {σ} ⊢Δ [σ] →
  PE.subst (λ X → Δ ⊢ X ∷ subst σ (Id A u t) ^ [ % , ι l ] ) (PE.sym (subst-Idsym σ A t u e))
    (Idsymⱼ {A = subst σ A} {x = subst σ t} {y = subst σ u} (escapeTerm (proj₁ ([U] {Δ} {σ} ⊢Δ [σ])) (proj₁ ([AU] ⊢Δ [σ])))
            (escapeTerm (proj₁ ([A] {Δ} {σ} ⊢Δ [σ])) (proj₁ ([t] ⊢Δ [σ])))
            (escapeTerm (proj₁ ([A] {Δ} {σ} ⊢Δ [σ])) (proj₁ ([u] ⊢Δ [σ])))
            (escapeTerm (proj₁ ([Id] {Δ} {σ} ⊢Δ [σ])) (proj₁ ([e] ⊢Δ [σ]))))

abstract
  transpᵗᵛ : ∀ {A P l t s u e Γ}
           ([Γ] : ⊩ᵛ Γ)
           ([A] : Γ ⊩ᵛ⟨ ∞ ⟩ A ^ [ ! , l ] / [Γ])
           ([P] : Γ ∙ A ^ [ ! , l ] ⊩ᵛ⟨ ∞ ⟩ P ^ [ % , l ] / (_∙_ {A = A} [Γ] [A]))
           ([t] : Γ ⊩ᵛ⟨ ∞ ⟩ t ∷ A ^ [ ! , l ] / [Γ] / [A])
           ([s] : Γ ⊩ᵛ⟨ ∞ ⟩ s ∷ P [ t ]  ^ [ % , l ] / [Γ] / substS {A} {P} {t} [Γ] [A] [P] [t])
           ([u] : Γ ⊩ᵛ⟨ ∞ ⟩ u ∷ A ^ [ ! , l ] / [Γ] / [A])
           ([Id] : Γ ⊩ᵛ⟨ ∞ ⟩ Id A t u ^ [ % , l ] / [Γ]) →
           ([e] : Γ ⊩ᵛ⟨ ∞ ⟩ e ∷ Id A t u ^ [ % , l ] / [Γ] / [Id] ) →
           Γ ⊩ᵛ⟨ ∞ ⟩ transp A P t s u e ∷ P [ u ]  ^ [ % , l ] / [Γ] / substS {A} {P} {u} [Γ] [A] [P] [u]
  transpᵗᵛ {A} {P} {l} {t} {s} {u} {e} {Γ} [Γ] [A] [P] [t] [s] [u] [Id] [e] =
    validityIrr {A = P [ u ]} {t = transp A P t s u e } [Γ] (substS {A} {P} {u} [Γ] [A] [P] [u]) λ {Δ} {σ} ⊢Δ [σ] →
    let [liftσ] = liftSubstS {F = A} [Γ] ⊢Δ [A] [σ]
        [A]σ = proj₁ ([A] {Δ} {σ} ⊢Δ [σ])
        [P[t]]σ = I.irrelevance′ (singleSubstLift P t) (proj₁ (substS {A} {P} {t} [Γ] [A] [P] [t] {Δ} {σ} ⊢Δ [σ]))
        X = transpⱼ (escape [A]σ) (escape (proj₁ ([P] {Δ ∙ subst σ A ^ [ ! , l ]} {liftSubst σ} (⊢Δ ∙ (escape [A]σ)) [liftσ])))
                            (escapeTerm [A]σ (proj₁ ([t] ⊢Δ [σ]))) (escapeTerm [P[t]]σ (I.irrelevanceTerm′ (singleSubstLift P t) PE.refl PE.refl (proj₁ (substS {A} {P} {t} [Γ] [A] [P] [t] {Δ} {σ} ⊢Δ [σ])) [P[t]]σ (proj₁ ([s] ⊢Δ [σ]))))
                            (escapeTerm [A]σ (proj₁ ([u] ⊢Δ [σ]))) (escapeTerm (proj₁ ([Id] {Δ} {σ} ⊢Δ [σ])) (proj₁ ([e] ⊢Δ [σ])))
    in PE.subst (λ X → Δ ⊢ transp (subst σ A) ( subst (liftSubst σ) P) (subst σ t) (subst σ s) (subst σ u) (subst σ e) ∷ X ^ [ % , l ] ) (PE.sym (singleSubstLift P u)) X