------------------------------------------------------------------------
-- Validity of declarative kinding of Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Kinding.Declarative.Validity where

open import Data.Fin using (zero)
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Product as Prod using (_,_; _×_; proj₁; proj₂)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; subst)

open import FOmegaInt.Syntax
open import FOmegaInt.Kinding.Declarative

------------------------------------------------------------------------
-- Validity of declarative kinding, subkinding and subtyping

open Syntax
open Substitution hiding (subst)
open TermCtx
open Kinding
open WfCtxOps using (lookup-kd)
open KindedSubstitution
open WfSubstitutionEquality

-- An admissible rule for kinding η-expanded type operators.
Tp∈-η : ∀ {n} {Γ : Ctx n} {a j k} →
        Γ ⊢Tp a ∈ Π j k → Γ ⊢ j kd → kd j ∷ Γ ⊢ k kd →
        Γ ⊢Tp Λ j (weaken a · var zero) ∈ Π j k
Tp∈-η {k = k} a∈Πjk j-kd k-kd =
  ∈-Π-i j-kd (subst (_ ⊢Tp _ ∈_) k′[z]≡k (∈-Π-e a∈Πjk′ z∈k k-kd′ k[z]-kd)) k-kd
  where
    module KL = TermLikeLemmas termLikeLemmasKind

    j-wf    = wf-kd j-kd
    j-kd′   = kd-weaken j-wf j-kd
    k-kd′   = kd-/Var k-kd (Var∈-↑ (wf-kd j-kd′) (Var∈-wk j-wf))
    z∈k     = ∈-var zero (j-wf ∷ kd-ctx j-kd) refl
    a∈Πjk′  = Tp∈-weaken j-wf a∈Πjk
    k[z]-kd = kd-[] k-kd′ (∈-tp z∈k)
    k′[z]≡k = Kind-wk↑-sub-zero-vanishes k

mutual

  -- Validity of kinding: the kinds of well-kinded types are well-formed.
  Tp∈-valid : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ⊢ k kd
  Tp∈-valid (∈-var x Γ-ctx Γ[x]≡kd-k) = lookup-kd x Γ-ctx Γ[x]≡kd-k
  Tp∈-valid (∈-⊥-f Γ-ctx)             = *-kd Γ-ctx
  Tp∈-valid (∈-⊤-f Γ-ctx)             = *-kd Γ-ctx
  Tp∈-valid (∈-∀-f k-kd a∈*)          = *-kd (kd-ctx k-kd)
  Tp∈-valid (∈-→-f a∈* b∈*)           = *-kd (Tp∈-ctx a∈*)
  Tp∈-valid (∈-Π-i j-kd a∈k k-kd)     = kd-Π j-kd (Tp∈-valid a∈k)
  Tp∈-valid (∈-Π-e a∈Πjk b∈j k-kd k[b]-kd) = k[b]-kd
  Tp∈-valid (∈-s-i a∈b⋯c)             = let a∈* = Tp∈-⋯-* a∈b⋯c in kd-⋯ a∈* a∈*
  Tp∈-valid (∈-⇑ a∈j j<∷k)            = proj₂ (<∷-valid j<∷k)

  -- Validity of subkinding: subkinds are well-formed.
  <∷-valid : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j <∷ k → Γ ⊢ j kd × Γ ⊢ k kd
  <∷-valid (<∷-⋯ a₂<:a₁∈* b₁<:b₂∈*)      =
    let a₂∈* , a₁∈* = <:-valid a₂<:a₁∈*
        b₁∈* , b₂∈* = <:-valid b₁<:b₂∈*
    in kd-⋯ a₁∈* b₁∈* , kd-⋯ a₂∈* b₂∈*
  <∷-valid (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd) =
    Πj₁k₁-kd ,
    kd-Π (proj₁ (<∷-valid j₂<∷j₁)) (proj₂ (<∷-valid k₁<∷k₂))

  -- Validity of subtyping: subtypes that are related in some kind `k'
  -- inhabit `k'.
  <:-valid : ∀ {n} {Γ : Ctx n} {a b k} →
             Γ ⊢ a <: b ∈ k → Γ ⊢Tp a ∈ k × Γ ⊢Tp b ∈ k
  <:-valid (<:-refl a∈k)            = a∈k , a∈k
  <:-valid (<:-trans a<:b∈k b<:c∈k) =
    proj₁ (<:-valid a<:b∈k) , proj₂ (<:-valid b<:c∈k)
  <:-valid (<:-β₁ a∈k b∈j a[b]∈k[b] k-kd k[b]-kd) =
    ∈-Π-e (∈-Π-i j-kd a∈k k-kd) b∈j k-kd k[b]-kd , a[b]∈k[b]
    where j-kd = wf-kd-inv (wf-∷₁ (Tp∈-ctx a∈k))
  <:-valid (<:-β₂ a∈k b∈j a[b]∈k[b] k-kd k[b]-kd) =
    a[b]∈k[b] , ∈-Π-e (∈-Π-i j-kd a∈k k-kd) b∈j k-kd k[b]-kd
    where j-kd = wf-kd-inv (wf-∷₁ (Tp∈-ctx a∈k))
  <:-valid (<:-η₁ a∈Πjk) with Tp∈-valid a∈Πjk
  ... | (kd-Π j-kd k-kd) = Tp∈-η a∈Πjk j-kd k-kd , a∈Πjk
  <:-valid (<:-η₂ a∈Πjk) with Tp∈-valid a∈Πjk
  ... | (kd-Π j-kd k-kd) = a∈Πjk , Tp∈-η a∈Πjk j-kd k-kd
  <:-valid (<:-⊥ b∈c⋯d) = ∈-⊥-f (Tp∈-ctx b∈c⋯d) , Tp∈-⋯-* b∈c⋯d
  <:-valid (<:-⊤ b∈c⋯d) = Tp∈-⋯-* b∈c⋯d , ∈-⊤-f (Tp∈-ctx b∈c⋯d)
  <:-valid (<:-∀ k₂<∷k₁ a₁<:a₂∈* ∀k₁a₁∈*) =
    ∀k₁a₁∈* ,
    ∈-∀-f (proj₁ (<∷-valid k₂<∷k₁)) (proj₂ (<:-valid a₁<:a₂∈*))
  <:-valid (<:-→ a₂<:a₁∈* b₁<:b₂∈*) =
    let a₂∈* , a₁∈* = <:-valid a₂<:a₁∈*
        b₁∈* , b₂∈* = <:-valid b₁<:b₂∈*
    in ∈-→-f a₁∈* b₁∈* , ∈-→-f a₂∈* b₂∈*
  <:-valid (<:-λ a₁<:a₂∈k Λj₁a₁∈Πjk Λj₂a₂∈Πjk) = Λj₁a₁∈Πjk , Λj₂a₂∈Πjk
  <:-valid (<:-· a₁<:a₂∈Πjk b₁≃b₂∈j b₁∈j k-kd k[b₁]-kd) =
    let a₁∈Πjk , a₂∈Πjk = <:-valid a₁<:a₂∈Πjk
        b₁∈j   , b₂∈j   = ≃-valid  b₁≃b₂∈j
    in ∈-Π-e a₁∈Πjk b₁∈j k-kd k[b₁]-kd ,
       ∈-⇑ (∈-Π-e a₂∈Πjk b₂∈j k-kd (kd-[] k-kd (∈-tp b₂∈j)))
           (≅⇒<∷ (kd-[≃′] k-kd b₂∈j b₁∈j (≃-sym b₁≃b₂∈j)))
  <:-valid (<:-⟨| a∈b⋯c) with Tp∈-valid a∈b⋯c
  ... | (kd-⋯ b∈* c∈*) = b∈* , Tp∈-⋯-* a∈b⋯c
  <:-valid (<:-|⟩ a∈b⋯c) with Tp∈-valid a∈b⋯c
  ... | (kd-⋯ b∈* c∈*) = Tp∈-⋯-* a∈b⋯c , c∈*
  <:-valid (<:-⋯-i a<:b∈c⋯d) =
    let a∈c⋯d , b∈c⋯d = <:-valid a<:b∈c⋯d
        a<:b∈*        = <:-⋯-*   a<:b∈c⋯d
    in ∈-⇑ (∈-s-i a∈c⋯d) (<∷-⋯ (<:-refl (Tp∈-⋯-* a∈c⋯d)) a<:b∈*) ,
       ∈-⇑ (∈-s-i b∈c⋯d) (<∷-⋯ a<:b∈* (<:-refl (Tp∈-⋯-* b∈c⋯d)))
  <:-valid (<:-⇑ a<:b∈j j<∷k) =
    let a∈j , b∈j = <:-valid a<:b∈j
    in ∈-⇑ a∈j j<∷k , ∈-⇑ b∈j j<∷k

  -- Validity of type equality: types that are equal in some kind `k'
  -- inhabit `k'.
  ≃-valid : ∀ {n} {Γ : Ctx n} {a b k} →
            Γ ⊢ a ≃ b ∈ k → Γ ⊢Tp a ∈ k × Γ ⊢Tp b ∈ k
  ≃-valid (<:-antisym a<:b∈k b<:a∈k) = <:-valid a<:b∈k

  -- Subtypes inhabiting interval kinds are proper types.
  <:-⋯-* : ∀ {n} {Γ : Ctx n} {a b c d} → Γ ⊢ a <: b ∈ c ⋯ d → Γ ⊢ a <: b ∈ *
  <:-⋯-* a<:b∈c⋯d =
    let a∈c⋯d , b∈c⋯d = <:-valid a<:b∈c⋯d
    in <:-⇑ (<:-⋯-i a<:b∈c⋯d) (<∷-⋯ (<:-⊥ a∈c⋯d) (<:-⊤ b∈c⋯d))

-- Validity of kind equality: equal kinds are well-formed.
≅-valid : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ⊢ j kd × Γ ⊢ k kd
≅-valid (<∷-antisym j<∷k k<∷j) = <∷-valid j<∷k

-- A corollary.
<:-valid-kd : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a <: b ∈ k → Γ ⊢ k kd
<:-valid-kd a<:b∈k = Tp∈-valid (proj₁ (<:-valid a<:b∈k))

----------------------------------------------------------------------
-- Strengthened versions of kind formation and kinding functionality.

-- Functionality of kind formation (strong version).
kd-[≃] : ∀ {n} {Γ : Ctx n} {a b j k} →
         kd j ∷ Γ ⊢ k kd → Γ ⊢ a ≃ b ∈ j → Γ ⊢ k Kind[ a ] ≅ k Kind[ b ]
kd-[≃] k-kd a≃b∈j = let a∈j , b∈j = ≃-valid a≃b∈j in kd-[≃′] k-kd a∈j b∈j a≃b∈j

-- Functionality of kinding (strong version).
Tp∈-[≃] : ∀ {n} {Γ : Ctx n} {a b c j k} →
          kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢ b ≃ c ∈ j →
          Γ ⊢ a [ b ] ≃ a [ c ] ∈ k Kind[ b ]
Tp∈-[≃] a∈k b≃c∈j = let b∈j , c∈j = ≃-valid b≃c∈j in Tp∈-[≃′] a∈k b∈j c∈j b≃c∈j


-- Andreas, 2012-09-15
{-# OPTIONS --show-implicit #-}
-- {-# OPTIONS -v tc.meta:50 #-}
-- {-# OPTIONS -v tc.conv:50 #-}
-- {-# OPTIONS -v tc.polarity:10 #-}
-- {-# OPTIONS -v tc.constr.findInScope:50 #-}
module BrokenInferenceDueToNonvariantPolarity where

import Common.Level

data ⊥ : Set where
record ⊤ : Set where

data Nat : Set where
  zero : Nat
  suc  : Nat → Nat

False : Nat → Set
False zero    = ⊥
False (suc n) = False n

module Invariant where
  record Bla (n : Nat)(p : False n) : Set where
  -- phantom arguments to Bla get polarity 'Invariant'

module Nonvariant where
  Bla : (n : Nat) → False n → Set
  Bla n p = ⊤
  -- polarity checker infers arguments to be 'Nonvariant'

-- open Invariant  -- succeeds
open Nonvariant -- fails

module Works where

  drop-suc : {n : Nat}{{p : False n}} → Bla (suc n) p → Bla n p
  drop-suc _ = _

  works :  (n : Nat) → {{p : False n}} → Bla n p → ⊥
  works zero {{()}} b
  works (suc n) b = works n (drop-suc {n} b)

module Fails where

  drop-suc : {n : Nat}{{p : False n}} → Bla (suc n) p → Bla n p
  drop-suc _ = _

  bla : (n : Nat) → {p : False n} → Bla n p → ⊥
  bla zero {()} b
  bla (suc n) b = bla n (drop-suc b)
  -- Since Bla is analysed as constant function, the constraint
  -- Bla n p = Bla X Y does not provide unique solutions for X and Y.
  -- And since the positivity checker runs after constraint solving,
  -- Agda does not recognize that bla is Nonvariant in argument p
  -- and that it hence could search for any solution.


module _ where

open import Agda.Primitive
open import Agda.Builtin.List
open import Agda.Builtin.Nat hiding (_==_)
open import Agda.Builtin.Equality
open import Agda.Builtin.Unit
open import Agda.Builtin.Bool

infix -1 _,_
record _×_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  constructor _,_
  field fst : A
        snd : B
open _×_

data Constraint : Set₁ where
  mkConstraint : {A : Set} (x y : A) → x ≡ y → Constraint

infix 0 _==_
pattern _==_ x y = mkConstraint x y refl

T : Bool → Set
T false = Nat
T true  = Nat → Nat

bla : (Nat → Nat) → Nat → Nat
bla f zero = zero
bla f = f  -- underapplied clause!

pred : Nat → Nat
pred zero = zero
pred (suc n) = n

-- 0 and 1 are both solutions, so should not be solved.
bad! : Constraint
bad! = bla pred _ == zero

-- Should not fail, since 2 is a solution!
more-bad! : Constraint
more-bad! = bla pred _ == 1

-- Same thing for projections

blabla : Nat → Nat × Nat
blabla zero    = 1 , 1
blabla (suc n) = 0 , n

-- Don't fail: 0 is a valid solution
oops : Constraint
oops = fst (blabla _) == 1

bla₂ : Bool → Nat × Nat
bla₂ false = 0 , 1
bla₂ true .fst = 0
bla₂ true .snd = 1

-- Don't solve: false and true are both solutions
wrong : Constraint
wrong = bla₂ _ .fst == 0


{-
This second-order term syntax was created from the following second-order syntax description:

syntax Monad | M

type
  T : 1-ary

term
  ret  : α  ->  T α
  bind : T α  α.(T β)  ->  T β | _>>=_ r10

theory
  (LU) a : α   b : α.(T β) |> bind (ret(a), x. b[x]) =  b[a]
  (RU) t : T α |> bind (t, x. ret(x)) = t
  (AS) t : T α  b : α.(T β)  c : β.(T γ) |> bind (bind (t, x.b[x]), y.c[y]) = bind (t, x. bind (b[x], y.c[y]))
-}

module Monad.Syntax where

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core
open import SOAS.Construction.Structure
open import SOAS.ContextMaps.Inductive

open import SOAS.Metatheory.Syntax

open import Monad.Signature

private
  variable
    Γ Δ Π : Ctx
    α β : MT
    𝔛 : Familyₛ

-- Inductive term declaration
module M:Terms (𝔛 : Familyₛ) where

  data M : Familyₛ where
    var  : ℐ ⇾̣ M
    mvar : 𝔛 α Π → Sub M Π Γ → M α Γ

    ret   : M α Γ → M (T α) Γ
    _>>=_ : M (T α) Γ → M (T β) (α ∙ Γ) → M (T β) Γ

  infixr 10 _>>=_

  open import SOAS.Metatheory.MetaAlgebra ⅀F 𝔛

  Mᵃ : MetaAlg M
  Mᵃ = record
    { 𝑎𝑙𝑔 = λ where
      (retₒ  ⋮ a)     → ret   a
      (bindₒ ⋮ a , b) → _>>=_ a b
    ; 𝑣𝑎𝑟 = var ; 𝑚𝑣𝑎𝑟 = λ 𝔪 mε → mvar 𝔪 (tabulate mε) }

  module Mᵃ = MetaAlg Mᵃ

  module _ {𝒜 : Familyₛ}(𝒜ᵃ : MetaAlg 𝒜) where

    open MetaAlg 𝒜ᵃ

    𝕤𝕖𝕞 : M ⇾̣ 𝒜
    𝕊 : Sub M Π Γ → Π ~[ 𝒜 ]↝ Γ
    𝕊 (t ◂ σ) new = 𝕤𝕖𝕞 t
    𝕊 (t ◂ σ) (old v) = 𝕊 σ v
    𝕤𝕖𝕞 (mvar 𝔪 mε) = 𝑚𝑣𝑎𝑟 𝔪 (𝕊 mε)
    𝕤𝕖𝕞 (var v) = 𝑣𝑎𝑟 v

    𝕤𝕖𝕞 (ret   a)   = 𝑎𝑙𝑔 (retₒ  ⋮ 𝕤𝕖𝕞 a)
    𝕤𝕖𝕞 (_>>=_ a b) = 𝑎𝑙𝑔 (bindₒ ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)

    𝕤𝕖𝕞ᵃ⇒ : MetaAlg⇒ Mᵃ 𝒜ᵃ 𝕤𝕖𝕞
    𝕤𝕖𝕞ᵃ⇒ = record
      { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → ⟨𝑎𝑙𝑔⟩ t }
      ; ⟨𝑣𝑎𝑟⟩ = refl
      ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪 = 𝔪}{mε} → cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-tab mε)) }  }
      where
      open ≡-Reasoning
      ⟨𝑎𝑙𝑔⟩ : (t : ⅀ M α Γ) → 𝕤𝕖𝕞 (Mᵃ.𝑎𝑙𝑔 t) ≡ 𝑎𝑙𝑔 (⅀₁ 𝕤𝕖𝕞 t)
      ⟨𝑎𝑙𝑔⟩ (retₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (bindₒ ⋮ _) = refl

      𝕊-tab : (mε : Π ~[ M ]↝ Γ)(v : ℐ α Π) → 𝕊 (tabulate mε) v ≡ 𝕤𝕖𝕞 (mε v)
      𝕊-tab mε new = refl
      𝕊-tab mε (old v) = 𝕊-tab (mε ∘ old) v

    module _ (g : M ⇾̣ 𝒜)(gᵃ⇒ : MetaAlg⇒ Mᵃ 𝒜ᵃ g) where

      open MetaAlg⇒ gᵃ⇒

      𝕤𝕖𝕞! : (t : M α Γ) → 𝕤𝕖𝕞 t ≡ g t
      𝕊-ix : (mε : Sub M Π Γ)(v : ℐ α Π) → 𝕊 mε v ≡ g (index mε v)
      𝕊-ix (x ◂ mε) new = 𝕤𝕖𝕞! x
      𝕊-ix (x ◂ mε) (old v) = 𝕊-ix mε v
      𝕤𝕖𝕞! (mvar 𝔪 mε) rewrite cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-ix mε))
        = trans (sym ⟨𝑚𝑣𝑎𝑟⟩) (cong (g ∘ mvar 𝔪) (tab∘ix≈id mε))
      𝕤𝕖𝕞! (var v) = sym ⟨𝑣𝑎𝑟⟩

      𝕤𝕖𝕞! (ret a) rewrite 𝕤𝕖𝕞! a = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (_>>=_ a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩

-- Syntax instance for the signature
M:Syn : Syntax
M:Syn = record
  { ⅀F = ⅀F
  ; ⅀:CS = ⅀:CompatStr
  ; mvarᵢ = M:Terms.mvar
  ; 𝕋:Init = λ 𝔛 → let open M:Terms 𝔛 in record
    { ⊥ = M ⋉ Mᵃ
    ; ⊥-is-initial = record { ! = λ{ {𝒜 ⋉ 𝒜ᵃ} → 𝕤𝕖𝕞 𝒜ᵃ ⋉ 𝕤𝕖𝕞ᵃ⇒ 𝒜ᵃ }
    ; !-unique = λ{ {𝒜 ⋉ 𝒜ᵃ} (f ⋉ fᵃ⇒) {x = t} → 𝕤𝕖𝕞! 𝒜ᵃ f fᵃ⇒ t } } } }

-- Instantiation of the syntax and metatheory
open Syntax M:Syn public
open M:Terms public
open import SOAS.Families.Build public
open import SOAS.Syntax.Shorthands Mᵃ public
open import SOAS.Metatheory M:Syn public


open import Data.Nat    using (ℕ; _+_; _≤?_)
open import Data.Bool   using (Bool; true; false; not; _∧_)
open import Data.String using (String; _≟_)
open import Relation.Nullary using (¬_; yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym)
open import Function.Equivalence using (_⇔_; equivalence)

open import IMP

data ⦅_,_⦆⇒_ : com → state → state → Set where
  Skip : ∀{s} → ⦅ SKIP , s ⦆⇒ s
  Loc : ∀{x a s}
      → ⦅ x ::= a , s ⦆⇒ (s [ x ::= aval a s ])
  Comp : ∀{c₁ c₂ s₁ s₂ s₃}
       → ⦅ c₁ , s₁ ⦆⇒ s₂
       → ⦅ c₂ , s₂ ⦆⇒ s₃
       → ⦅ c₁ :: c₂ , s₁ ⦆⇒ s₃
  IfTrue : ∀{c₁ c₂ b s t}
         → bval b s ≡ true
         → ⦅ c₁ , s ⦆⇒ t
         → ⦅ IF b THEN c₁ ELSE c₂ , s ⦆⇒ t
  IfFalse : ∀{c₁ c₂ b s t}
          → bval b s ≡ false
          → ⦅ c₂ , s ⦆⇒ t
          → ⦅ IF b THEN c₁ ELSE c₂ , s ⦆⇒ t
  WhileFalse : ∀{c b s}
             → bval b s ≡ false
             → ⦅ WHILE b DO c , s ⦆⇒ s
  WhileTrue  : ∀{c b s₁ s₂ s₃}
             → bval b s₁ ≡ true
             → ⦅ c , s₁ ⦆⇒ s₂
             → ⦅ WHILE b DO c , s₂ ⦆⇒ s₃
             → ⦅ WHILE b DO c , s₁ ⦆⇒ s₃

true≢false : ¬ (true ≡ false)
true≢false = λ ()

deterministic : ∀{c s t t′}
              → ⦅ c , s ⦆⇒ t
              → ⦅ c , s ⦆⇒ t′
              → t ≡ t′
deterministic Skip Skip = refl
deterministic Loc Loc = refl
deterministic (Comp r₁ r₂)  (Comp r₁′ r₂′) rewrite deterministic r₁ r₁′ = deterministic r₂ r₂′
deterministic (IfTrue v r)  (IfTrue  v′ r′) = deterministic r r′
deterministic (IfTrue v r)  (IfFalse v′ r′) rewrite v  = contradiction v′ true≢false
deterministic (IfFalse v r) (IfTrue  v′ r′) rewrite v′ = contradiction v true≢false
deterministic (IfFalse v r) (IfFalse v′ r′) = deterministic r r′
deterministic (WhileFalse v) (WhileFalse x₁) = refl
deterministic (WhileFalse v) (WhileTrue x₁ r₁ r₂) rewrite x₁ = contradiction v  true≢false
deterministic (WhileTrue v r₁ r₂) (WhileFalse v′) rewrite v  = contradiction v′ true≢false
deterministic (WhileTrue v r₁ r₂) (WhileTrue v′ r₁′ r₂′) rewrite deterministic r₁ r₁′ = deterministic r₂ r₂′

lemma2-3-5 : ∀{s t}
           → ¬ ( ⦅ WHILE (Bc true) DO SKIP , s ⦆⇒ t )
lemma2-3-5 (WhileTrue x Skip (WhileTrue v r₁ r₂)) = lemma2-3-5 r₂

infixl 19 _∼_
_∼_ : com → com → Set
c ∼ c′ = ∀{s t} → ⦅ c , s  ⦆⇒ t ⇔ ⦅ c′ , s ⦆⇒ t

lemma2-4-3 : ∀{b c} → (WHILE b DO c) ∼ (IF b THEN (c :: (WHILE b DO c)) ELSE SKIP)
lemma2-4-3 = equivalence (λ { (WhileFalse x) → IfFalse x Skip ; (WhileTrue x r r₁) → IfTrue x (Comp r r₁) })
                         (λ { (IfTrue x (Comp r r₁)) → WhileTrue x r r₁ ; (IfFalse x Skip) → WhileFalse x })

data ⦅_,_⦆→⦅_,_⦆ : com → state → com → state → Set where
  Loc : ∀{x a s}
      → ⦅ x ::= a , s ⦆→⦅ SKIP , s [ x ::= aval a s ] ⦆
  Comp₁ : ∀{c s}
        → ⦅ SKIP :: c , s ⦆→⦅ c , s ⦆
  Comp₂ : ∀{c₁ c₁′ c₂ s s′}
        → ⦅ c₁       , s ⦆→⦅ c₁′       , s′ ⦆
        → ⦅ c₁ :: c₂ , s ⦆→⦅ c₁′ :: c₂ , s′ ⦆
  IfTrue  : ∀{b s c₁ c₂}
          → bval b s ≡ true
          → ⦅ IF b THEN c₁ ELSE c₂ , s ⦆→⦅ c₁ , s ⦆
  IfFalse : ∀{b s c₁ c₂}
          → bval b s ≡ false
          → ⦅ IF b THEN c₁ ELSE c₂ , s ⦆→⦅ c₂ , s ⦆
  While : ∀{b s c}
        → ⦅ WHILE b DO c , s ⦆→⦅ IF b THEN (c :: (WHILE b DO c)) ELSE SKIP , s ⦆

infix  3 ⦅_,_⦆∎
infixr 2 ⦅_,_⦆→⟨_⟩_ ⦅_,_⦆→*⟨_⟩_

data  ⦅_,_⦆→*⦅_,_⦆ : com → state → com → state → Set where
  ⦅_,_⦆∎ : ∀ c s → ⦅ c , s ⦆→*⦅ c , s ⦆
  ⦅_,_⦆→⟨_⟩_ : ∀ c s {c′ c″ s′ s″}
            → ⦅ c  , s  ⦆→⦅  c′ , s′ ⦆
            → ⦅ c′ , s′ ⦆→*⦅ c″ , s″ ⦆
            → ⦅ c  , s  ⦆→*⦅ c″ , s″ ⦆

⦅_,_⦆→*⟨_⟩_ : ∀ c s {c′ c″ s′ s″}
           → ⦅ c  , s  ⦆→*⦅ c′ , s′ ⦆
           → ⦅ c′ , s′ ⦆→*⦅ c″ , s″ ⦆
           → ⦅ c  , s  ⦆→*⦅ c″ , s″ ⦆
⦅ c , s ⦆→*⟨ ⦅ _ , _ ⦆∎ ⟩        b = b
⦅ c , s ⦆→*⟨ ⦅ _ , _ ⦆→⟨ x ⟩ r ⟩ b = ⦅ _ , _ ⦆→⟨ x ⟩ ⦅ _ , _ ⦆→*⟨ r ⟩ b

lemma2-5-6 : ∀{c₁ c₁′ c₂ s s′}
      → ⦅ c₁ , s       ⦆→*⦅ c₁′       ,  s′ ⦆
      → ⦅ c₁ :: c₂ , s ⦆→*⦅ c₁′ :: c₂ ,  s′ ⦆
lemma2-5-6  ⦅ _ , _ ⦆∎         = ⦅ _ , _ ⦆∎
lemma2-5-6 (⦅ _ , _ ⦆→⟨ x ⟩ r) = ⦅ _ , _ ⦆→⟨ Comp₂ x ⟩ lemma2-5-6 r

big-small : ∀{c s t}
          → ⦅ c , s ⦆⇒ t
          → ⦅ c , s ⦆→*⦅ SKIP , t ⦆
big-small (Skip {s}) =
  ⦅ SKIP , s ⦆∎
big-small (Loc {x}{s}{a}) =
  ⦅ x ::= s , a                    ⦆→⟨ Loc ⟩
  ⦅ SKIP    , a [ x ::= aval s a ] ⦆∎
big-small (Comp {c₁}{c₂}{s}{s′}{t} r₁ r₂) =
  ⦅ c₁   :: c₂ , s  ⦆→*⟨ lemma2-5-6 (big-small r₁) ⟩
  ⦅ SKIP :: c₂ , s′ ⦆→⟨  Comp₁ ⟩
  ⦅ c₂         , s′ ⦆→*⟨ big-small r₂ ⟩
  ⦅ SKIP       , t  ⦆∎
big-small (IfTrue {c₁}{c₂}{b}{s}{t} v r₁) =
  ⦅ IF b THEN c₁ ELSE c₂ , s ⦆→⟨ IfTrue v ⟩
  ⦅ c₁                   , s ⦆→*⟨ big-small r₁ ⟩
  ⦅ SKIP                 , t ⦆∎
big-small (IfFalse {c₁}{c₂}{b}{s}{t} v r₂) =
  ⦅ IF b THEN c₁ ELSE c₂ , s ⦆→⟨ IfFalse v ⟩
  ⦅ c₂                   , s ⦆→*⟨ big-small r₂ ⟩
  ⦅ SKIP                 , t ⦆∎
big-small (WhileFalse {c}{b}{s} v) =
  ⦅ WHILE b DO c                            , s ⦆→⟨ While ⟩
  ⦅ IF b THEN c :: (WHILE b DO c) ELSE SKIP , s ⦆→⟨ IfFalse v ⟩
  ⦅ SKIP                                    , s ⦆∎
big-small (WhileTrue {c}{b}{s}{s′}{t} v r₁ r₂) =
  ⦅ WHILE b DO c                            , s  ⦆→⟨ While ⟩
  ⦅ IF b THEN c :: (WHILE b DO c) ELSE SKIP , s  ⦆→⟨ IfTrue v ⟩
  ⦅ c :: (WHILE b DO c)                     , s  ⦆→*⟨ lemma2-5-6 (big-small r₁) ⟩
  ⦅ SKIP :: (WHILE b DO c)                  , s′ ⦆→⟨ Comp₁ ⟩
  ⦅ WHILE b DO c                            , s′ ⦆→*⟨ big-small r₂ ⟩
  ⦅ SKIP                                    , t  ⦆∎

lemma2-5-8 : ∀{c s c′ s′ t}
           → ⦅ c  , s  ⦆→⦅ c′ , s′ ⦆ → ⦅ c′ , s′ ⦆⇒ t
           → ⦅ c  , s  ⦆⇒ t
lemma2-5-8 Loc Skip = Loc
lemma2-5-8 Comp₁ r₁ = Comp Skip r₁
lemma2-5-8 (Comp₂ x) (Comp r₁ r₂) = Comp (lemma2-5-8 x r₁) r₂
lemma2-5-8 (IfTrue x) r₁ = IfTrue x r₁
lemma2-5-8 (IfFalse x) r₁ = IfFalse x r₁
lemma2-5-8 While (IfTrue x (Comp r₁ r₂)) = WhileTrue x r₁ r₂
lemma2-5-8 While (IfFalse x Skip) = WhileFalse x

small-big : ∀{c s t}
          → ⦅ c , s ⦆→*⦅ SKIP , t ⦆
          → ⦅ c , s ⦆⇒ t
small-big ⦅ SKIP , s ⦆∎ = Skip
small-big (⦅ c , s ⦆→⟨ x ⟩ r) = lemma2-5-8 x (small-big r)


open import Nat
open import Prelude
open import dynamics-core
open import contexts
open import typed-elaboration
open import lemmas-gcomplete
open import lemmas-complete

module complete-elaboration where
  mutual
    complete-elaboration-synth : ∀{e τ Γ Δ d} →
                                 Γ gcomplete →
                                 e ecomplete →
                                 Γ ⊢ e ⇒ τ ~> d ⊣ Δ →
                                 (d dcomplete × τ tcomplete × Δ == ∅)
    complete-elaboration-synth gc ec ESNum = DCNum , TCNum , refl
    complete-elaboration-synth gc (ECPlus ec₁ ec₂) (ESPlus {Δ1 = Δ1} {Δ2 = Δ2} apt dis x₁ x₂)
      with complete-elaboration-ana gc ec₁ TCNum x₁ | complete-elaboration-ana gc ec₂ TCNum x₂
    ... | d1comp , t1comp , refl | d2comp , t2comp , refl =
      DCPlus (DCCast d1comp t1comp TCNum) (DCCast d2comp t2comp TCNum) , TCNum , refl
    complete-elaboration-synth gc ec (ESVar x₁) = DCVar , gc _ _ x₁ , refl
    complete-elaboration-synth gc (ECLam2 ec x₁) (ESLam x₂ exp)
      with complete-elaboration-synth (gcomp-extend gc x₁ x₂) ec exp
    ... | ih1 , ih2 , ih3 = DCLam ih1 x₁ , TCArr x₁ ih2 , ih3
    complete-elaboration-synth gc (ECAp ec ec₁) (ESAp _ _ x MAHole x₂ x₃)
      with comp-synth gc ec x
    ... | ()
    complete-elaboration-synth gc (ECAp ec ec₁) (ESAp {Δ1 = Δ1} {Δ2 = Δ2} _ _ x MAArr x₂ x₃)
      with comp-synth gc ec x
    ... | TCArr t1 t2
      with complete-elaboration-ana gc ec (TCArr t1 t2) x₂ | complete-elaboration-ana gc ec₁ t1 x₃
    ... | ih1 , _ ,  ih4 | ih2 , _ , ih3 = DCAp (DCCast ih1 (comp-ana gc x₂ ih1) (TCArr t1 t2)) (DCCast ih2 (comp-ana gc x₃ ih2) t1) ,
                                  t2 ,
                                  tr (λ qq → (qq ∪ Δ2) == ∅) (! ih4) (tr (λ qq → (∅ ∪ qq) == ∅) (! ih3) refl)
    complete-elaboration-synth gc () ESEHole
    complete-elaboration-synth gc () (ESNEHole _ exp)
    complete-elaboration-synth gc (ECAsc x ec) (ESAsc x₁)
      with complete-elaboration-ana gc ec x x₁
    ... | ih1 , _ , ih2 = DCCast ih1 (comp-ana gc x₁ ih1) x , x , ih2
    complete-elaboration-synth gc (ECPair ec ec₁) (ESPair {Δ1 = Δ1} {Δ2 = Δ2} x x₁ x₂ x₃)
      with complete-elaboration-synth gc ec x₂ | complete-elaboration-synth gc ec₁ x₃
    ... | ih1 , ih2 , ih3 | ih1' , ih2' , ih3' = DCPair ih1 ih1' , TCProd ih2 ih2' , tr (λ qq → (qq ∪ Δ2) == ∅) (! ih3) ih3'
    complete-elaboration-synth gc (ECFst ec) (ESFst x MPHole x₂)
      with comp-synth gc ec x
    ... | ()
    complete-elaboration-synth gc (ECFst ec) (ESFst x MPProd x₂)
      with comp-synth gc ec x
    ... | TCProd τc1 τc2 with complete-elaboration-ana gc ec (TCProd τc1 τc2) x₂
    ... | ih1 , ih2 , ih3 = DCFst (DCCast ih1 ih2 (TCProd τc1 τc2)) , τc1 , ih3
    complete-elaboration-synth gc (ECSnd ec) (ESSnd x MPHole x₂)
      with comp-synth gc ec x
    ... | ()
    complete-elaboration-synth gc (ECSnd ec) (ESSnd x MPProd x₂)
      with comp-synth gc ec x
    ... | TCProd τc1 τc2 with complete-elaboration-ana gc ec (TCProd τc1 τc2) x₂
    ... | ih1 , ih2 , ih3 = DCSnd (DCCast ih1 ih2 (TCProd τc1 τc2)) , τc2 , ih3

    complete-elaboration-ana : ∀{e τ τ' Γ Δ d} →
                               Γ gcomplete →
                               e ecomplete →
                               τ tcomplete →
                               Γ ⊢ e ⇐ τ ~> d :: τ' ⊣ Δ →
                               (d dcomplete × τ' tcomplete × Δ == ∅)
    complete-elaboration-ana gc (ECLam1 ec) () (EALam x₁ MAHole exp)
    complete-elaboration-ana gc (ECLam1 ec) (TCArr t1 t2)  (EALam x₁ MAArr exp)
      with complete-elaboration-ana (gcomp-extend gc t1 x₁) ec t2 exp
    ... | ih , ih3 , ih2 = DCLam ih t1 , TCArr t1 ih3 , ih2
    complete-elaboration-ana gc ec tc (EASubsume x x₁ x₂ x₃)
      with complete-elaboration-synth gc ec x₂
    ... | ih1 , ih2 , ih3 = ih1 , ih2 , ih3
    complete-elaboration-ana gc (ECInl ec) (TCSum tc tc₁) (EAInl MSSum x₁)
      with complete-elaboration-ana gc ec tc x₁
    ... | ih1 , ih2 , ih3 = DCInl tc₁ ih1 , TCSum ih2 tc₁ , ih3
    complete-elaboration-ana gc (ECInr ec) (TCSum tc tc₁) (EAInr MSSum x₁)
      with complete-elaboration-ana gc ec tc₁ x₁
    ... | ih1 , ih2 , ih3 = DCInr tc ih1 , TCSum tc ih2 , ih3
    complete-elaboration-ana gc (ECCase ec ec₁ ec₂) tc (EACase x x₁ x₂ x₃ x₄ x₅ x₆ x₇ x₈ MSHole x₁₀ x₁₁)
      with complete-elaboration-synth gc ec x₈
    ... | ih1 , () , ih3
    complete-elaboration-ana gc (ECCase ec ec₁ ec₂) tc (EACase {Δ = Δ} {Δ1 = Δ1} {Δ2 = Δ2} x x₁ x₂ x₃ x₄ x₅ x₆ x₇ x₈ MSSum x₁₀ x₁₁)
      with complete-elaboration-synth gc ec x₈
    ... | ih1 , TCSum ih2 ih4 , ih3  with complete-elaboration-ana (gcomp-extend gc ih2 x₆) ec₁ tc x₁₀
    ... | ih1' , ih2' , ih3' with complete-elaboration-ana (gcomp-extend gc ih4 x₇) ec₂ tc x₁₁
    ... | ih1'' , ih2'' , ih3'' = DCCase (DCCast ih1 (TCSum ih2 ih4) (TCSum ih2 ih4)) (DCCast ih1' ih2' tc) (DCCast ih1'' ih2'' tc) , tc , tr (λ qq → qq ∪ (Δ1 ∪ Δ2) == ∅) (! ih3) (tr (λ qq → ∅ ∪ (qq ∪ Δ2) == ∅) (! ih3') (tr (λ qq → ∅ ∪ (∅ ∪ qq) == ∅) (! ih3'') refl))

    -- this is just a convenience since it shows up a few times above
    comp-ana : ∀{Γ e τ d τ' Δ} →
               Γ gcomplete →
               Γ ⊢ e ⇐ τ ~> d :: τ' ⊣ Δ →
               d dcomplete →
               τ' tcomplete
    comp-ana gc ex dc = complete-ta gc (π2 (typed-elaboration-ana ex)) dc


{-# OPTIONS --cubical #-}
module Agda.Primitive.Cubical where
{-# BUILTIN INTERVAL I    #-} -- I : Setω

{-# BUILTIN IZERO    i0   #-}
{-# BUILTIN IONE     i1   #-}

infix 30 primINeg
infixr 20 primIMin primIMax

primitive
    primIMin : I → I → I
    primIMax : I → I → I
    primINeg : I → I

{-# BUILTIN ISONE        IsOne #-} -- IsOne : I → Setω

postulate
  itIsOne : IsOne i1
  IsOne1  : ∀ i j → IsOne i → IsOne (primIMax i j)
  IsOne2  : ∀ i j → IsOne j → IsOne (primIMax i j)

{-# BUILTIN ITISONE      itIsOne  #-}
{-# BUILTIN ISONE1       IsOne1   #-}
{-# BUILTIN ISONE2       IsOne2   #-}
{-# BUILTIN PARTIAL      Partial  #-}
{-# BUILTIN PARTIALP     PartialP #-}

postulate
  isOneEmpty : ∀ {a} {A : Partial i0 (Set a)} → PartialP i0 A
{-# BUILTIN ISONEEMPTY isOneEmpty #-}

primitive
  primPFrom1 : ∀ {a} {A : I → Set a} → A i1 → ∀ i j → Partial i (A (primIMax i j))
  primPOr : ∀ {a} (i j : I) {A : Partial (primIMax i j) (Set a)}
            → PartialP i (λ z → A (IsOne1 i j z)) → PartialP j (λ z → A (IsOne2 i j z))
            → PartialP (primIMax i j) A
  primComp : ∀ {a} (A : (i : I) → Set (a i)) (φ : I) → (∀ i → Partial φ (A i)) → (a : A i0) → A i1

syntax primPOr p q u t = [ p ↦ u , q ↦ t ]

primitive
  primTransp : ∀ {a} (A : (i : I) → Set (a i)) (φ : I) → (a : A i0) → A i1
  primHComp : ∀ {a} {A : Set a} {φ : I} → (∀ i → Partial φ A) → A → A


open import FRP.JS.Bool using ( Bool )
open import FRP.JS.RSet using ( RSet ; ⟦_⟧ ; ⟨_⟩ ; _⇒_ )
open import FRP.JS.Behaviour using ( Beh ; map2 )
open import FRP.JS.Event using ( Evt ; ∅ ; _∪_ ; map )
open import FRP.JS.Product using ( _∧_ ; _,_ )
open import FRP.JS.String using ( String )

module FRP.JS.DOM where

infixr 2 _≟*_
infixr 4 _++_ _+++_

postulate
  Mouse Keyboard : RSet
  EventType : RSet → Set
  click : EventType Mouse
  press : EventType Keyboard

{-# COMPILED_JS click "click" #-}
{-# COMPILED_JS press "press" #-}

postulate
  DOW : Set
  unattached : DOW
  left right : DOW → DOW
  child : String → DOW → DOW
  events : ∀ {A} → EventType A → DOW → ⟦ Evt A ⟧

{-# COMPILED_JS unattached require("agda.frp").unattached() #-}
{-# COMPILED_JS left function(w) { return w.left(); } #-}
{-# COMPILED_JS right function(w) { return w.right(); } #-}
{-# COMPILED_JS child function(a) { return function(w) { return w.child(a); }; } #-}
{-# COMPILED_JS events function(A) { return function(t) { return function(w) { return function(s) { return w.events(t); }; }; }; } #-}

postulate
  DOM : DOW → RSet
  text : ∀ {w} → ⟦ Beh ⟨ String ⟩ ⇒ Beh (DOM w) ⟧
  attr : ∀ {w} → String → ⟦ Beh ⟨ String ⟩ ⇒ Beh (DOM w) ⟧
  element : ∀ a {w} → ⟦ Beh (DOM (child a w)) ⇒ Beh (DOM w) ⟧
  [] : ∀ {w} → ⟦ Beh (DOM w) ⟧
  _++_ : ∀ {w} → ⟦ Beh (DOM (left w)) ⇒ Beh (DOM (right w)) ⇒ Beh (DOM w) ⟧

{-# COMPILED_JS attr function(w) { return function(k) { return function(s) { return function(b) { return b.attribute(k); }; }; }; } #-}
{-# COMPILED_JS text function(w) { return function(s) { return function(b) { return b.text(); }; }; } #-}
{-# COMPILED_JS element function(a) { return function(w) { return function(s) { return function(b) { return w.element(a,b); }; }; }; } #-}
{-# COMPILED_JS [] function(w) { return require("agda.frp").empty; } #-}
{-# COMPILED_JS _++_ function(w) { return function(s) { return function(a) { return function(b) { return a.concat(b); }; }; }; } #-}

listen : ∀ {A w} → EventType A → ⟦ Beh (DOM w) ⇒ Evt A ⟧
listen {A} {w} t b = events t w

{-# COMPILED_JS listen function(A) { return function(w) { return function(t) { return function(s) { return function(b) { return w.events(t); }; }; }; }; } #-}

private
  postulate
    _≟_ : ∀ {w} → ⟦ DOM w ⇒ DOM w ⇒ ⟨ Bool ⟩ ⟧

  {-# COMPILED_JS _≟_ function(w) { return function(s) { return function(a) { return function(b) { return a.equals(b); }; }; }; } #-}

_≟*_ : ∀ {w} → ⟦ Beh (DOM w) ⇒ Beh (DOM w) ⇒ Beh ⟨ Bool ⟩ ⟧
_≟*_  = map2 _≟_

[+] : ∀ {A w} → ⟦ Beh (DOM w) ∧ Evt A ⟧
[+] = ([] , ∅)

_+++_ : ∀ {A w} → ⟦ (Beh (DOM (left w)) ∧ Evt A) ⇒ (Beh (DOM (right w)) ∧ Evt A) ⇒ (Beh (DOM w) ∧ Evt A) ⟧
(dom₁ , evt₁) +++ (dom₂ , evt₂) = ((dom₁ ++ dom₂) , (evt₁ ∪ evt₂))

text+ : ∀ {A w} → ⟦ Beh ⟨ String ⟩ ⇒ (Beh (DOM w) ∧ Evt A) ⟧
text+ msg = (text msg , ∅)

attr+ : ∀ {A w} → String → ⟦ Beh ⟨ String ⟩ ⇒ (Beh (DOM w) ∧ Evt A) ⟧
attr+ key val = (attr key val , ∅)

element+ : ∀ a {A w} → ⟦ (Beh (DOM (child a w)) ∧ Evt A) ⇒ (Beh (DOM w) ∧ Evt A) ⟧
element+ a (dom , evt) = (element a dom , evt)

listen+ : ∀ {A B w} → EventType A → ⟦ A ⇒ B ⟧ → ⟦ (Beh (DOM w) ∧ Evt B) ⇒ (Beh (DOM w) ∧ Evt B) ⟧
listen+ t f (dom , evt) = (dom , map f (listen t dom) ∪ evt)


open import Agda.Builtin.Unit
open import Agda.Builtin.Bool
open import Agda.Builtin.Nat
open import Agda.Builtin.Equality
open import Agda.Builtin.IO
open import Agda.Builtin.String

postulate
  putStr : String → IO ⊤

{-# FOREIGN GHC import qualified Data.Text.IO #-}
{-# COMPILE GHC putStr = Data.Text.IO.putStr #-}

data Sing : Nat → Set where
  sing : Sing zero

data Vec : Nat → Set where
  nil : Vec zero
  cons : ∀ n → Vec n → Vec (suc n)

isTailNil : ∀ m n → Sing m → Vec n → Bool
isTailNil .0 .1 sing (cons zero v) = true  -- two `zero`s are buried in the dot patterns, which to resurrect?
isTailNil _ _ _ _ = false

shouldBeFalse = isTailNil 0 2 sing (cons 1 (cons 0 nil))

isFalse : false ≡ shouldBeFalse
isFalse = refl

magic : false ≡ true → String
magic ()

f : (b : Bool) → false ≡ b → String
f false eq = "Phew!"
f true  eq = magic eq

main = putStr (f shouldBeFalse refl)


module Pi.Opsem where
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Base
open import Pi.Syntax

infix  1  _↦_

-- Base combinators
base : ∀ {A B} (c : A ↔ B) → Set
base unite₊l = ⊤
base uniti₊l = ⊤
base swap₊ = ⊤
base assocl₊ = ⊤
base assocr₊ = ⊤
base unite⋆l = ⊤
base uniti⋆l = ⊤
base swap⋆ = ⊤
base assocl⋆ = ⊤
base assocr⋆ = ⊤
base absorbr = ⊤
base factorzl = ⊤
base dist = ⊤
base factor = ⊤
base _ = ⊥

base-is-prop : ∀ {A B} (c : A ↔ B) → is-prop (base c)
base-is-prop unite₊l tt tt = refl
base-is-prop uniti₊l tt tt = refl
base-is-prop swap₊ tt tt = refl
base-is-prop assocl₊ tt tt = refl
base-is-prop assocr₊ tt tt = refl
base-is-prop unite⋆l tt tt = refl
base-is-prop uniti⋆l tt tt = refl
base-is-prop swap⋆ tt tt = refl
base-is-prop assocl⋆ tt tt = refl
base-is-prop assocr⋆ tt tt = refl
base-is-prop absorbr tt tt = refl
base-is-prop factorzl tt tt = refl
base-is-prop dist tt tt = refl
base-is-prop factor tt tt = refl

-- Evaluator for base combinators
δ : ∀ {A B} (c : A ↔ B) {_ : base c} → ⟦ A ⟧ → ⟦ B ⟧
δ unite₊l (inj₂ v) = v
δ uniti₊l v = inj₂ v
δ swap₊ (inj₁ x) = inj₂ x
δ swap₊ (inj₂ y) = inj₁ y
δ assocl₊ (inj₁ v) = inj₁ (inj₁ v)
δ assocl₊ (inj₂ (inj₁ v)) = inj₁ (inj₂ v)
δ assocl₊ (inj₂ (inj₂ v)) = inj₂ v
δ assocr₊ (inj₁ (inj₁ v)) = inj₁ v
δ assocr₊ (inj₁ (inj₂ v)) = inj₂ (inj₁ v)
δ assocr₊ (inj₂ v) = inj₂ (inj₂ v)
δ unite⋆l (tt , v) = v
δ uniti⋆l v = (tt , v)
δ swap⋆ (x , y) = (y , x)
δ assocl⋆ (v₁ , (v₂ , v₃)) = ((v₁ , v₂) , v₃)
δ assocr⋆ ((v₁ , v₂) , v₃) = (v₁ , (v₂ , v₃))
δ absorbr ()
δ factorzl ()
δ dist (inj₁ v₁ , v₃) = inj₁ (v₁ , v₃)
δ dist (inj₂ v₂ , v₃) = inj₂ (v₂ , v₃)
δ factor (inj₁ (v₁ , v₃)) = (inj₁ v₁ , v₃)
δ factor (inj₂ (v₂ , v₃)) = (inj₂ v₂ , v₃)

-- Context
data Context : {A B : 𝕌} → Set where
  ☐ : ∀ {A B} → Context {A} {B}
  ☐⨾_•_ : ∀ {A B C} → (c₂ : B ↔ C) → Context {A} {C} → Context {A} {B}
  _⨾☐•_ : ∀ {A B C} → (c₁ : A ↔ B) → Context {A} {C} → Context {B} {C}
  ☐⊕_•_ : ∀ {A B C D} → (c₂ : C ↔ D) → Context {A +ᵤ C} {B +ᵤ D} → Context {A} {B}
  _⊕☐•_ : ∀ {A B C D} → (c₁ : A ↔ B) → Context {A +ᵤ C} {B +ᵤ D} → Context {C} {D}
  ☐⊗[_,_]•_ : ∀ {A B C D} → (c₂ : C ↔ D) → ⟦ C ⟧ → Context {A ×ᵤ C} {B ×ᵤ D} → Context {A} {B}
  [_,_]⊗☐•_ : ∀ {A B C D} → (c₁ : A ↔ B) → ⟦ B ⟧ → Context {A ×ᵤ C} {B ×ᵤ D} → Context {C} {D}

-- Machine state
data State : Set where
  ⟨_∣_∣_⟩ : ∀ {A B} → (c : A ↔ B) → ⟦ A ⟧ → Context {A} {B} → State
  ［_∣_∣_］ : ∀ {A B} → (c : A ↔ B) → ⟦ B ⟧ → Context {A} {B} → State

-- Reduction relation
data _↦_ : State → State → Set where
  ↦₁  : ∀ {A B} {c : A ↔ B} {b : base c} {v : ⟦ A ⟧} {κ : Context} → ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c ∣ δ c {b} v ∣ κ ］
  ↦₂  : ∀ {A} {v : ⟦ A ⟧} {κ : Context} → ⟨ id↔ ∣ v ∣ κ ⟩ ↦ ［ id↔ ∣ v ∣ κ ］
  ↦₃  : ∀ {A B C} {c₁ : A ↔ B} {c₂ : B ↔ C} {v : ⟦ A ⟧} {κ : Context}
      → ⟨ c₁ ⨾ c₂ ∣ v ∣ κ ⟩ ↦ ⟨ c₁ ∣ v ∣ ☐⨾ c₂ • κ ⟩
  ↦₄  : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {x : ⟦ A ⟧} {κ : Context}
      → ⟨ c₁ ⊕ c₂ ∣ inj₁ x ∣ κ ⟩ ↦ ⟨ c₁ ∣ x ∣ ☐⊕ c₂ • κ ⟩
  ↦₅  : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {y : ⟦ C ⟧} {κ : Context}
      → ⟨ c₁ ⊕ c₂ ∣ inj₂ y ∣ κ ⟩ ↦ ⟨ c₂ ∣ y ∣ c₁ ⊕☐• κ ⟩
  ↦₆  : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {x : ⟦ A ⟧} {y : ⟦ C ⟧} {κ : Context}
      → ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ κ ⟩ ↦ ⟨ c₁ ∣ x ∣ ☐⊗[ c₂ , y ]• κ ⟩
  ↦₇  : ∀ {A B C} {c₁ : A ↔ B} {c₂ : B ↔ C} {v : ⟦ B ⟧} {κ : Context}
      → ［ c₁ ∣ v ∣ ☐⨾ c₂ • κ ］ ↦ ⟨ c₂ ∣ v ∣ (c₁ ⨾☐• κ) ⟩
  ↦₈  : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {x : ⟦ B ⟧} {y : ⟦ C ⟧} {κ : Context}
      → ［ c₁ ∣ x ∣ ☐⊗[ c₂ , y ]• κ ］ ↦ ⟨ c₂ ∣ y ∣ [ c₁ , x ]⊗☐• κ ⟩
  ↦₉  : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {x : ⟦ B ⟧} {y : ⟦ D ⟧} {κ : Context}
      → ［ c₂ ∣ y ∣ [ c₁ , x ]⊗☐• κ ］ ↦ ［ c₁ ⊗ c₂ ∣ (x , y) ∣ κ ］
  ↦₁₀ : ∀ {A B C} {c₁ : A ↔ B} {c₂ : B ↔ C} {v : ⟦ C ⟧} {κ : Context}
      → ［ c₂ ∣ v ∣ (c₁ ⨾☐• κ) ］ ↦ ［ c₁ ⨾ c₂ ∣ v ∣ κ ］
  ↦₁₁ : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {x : ⟦ B ⟧} {κ : Context}
      → ［ c₁ ∣ x ∣ ☐⊕ c₂ • κ ］ ↦ ［ c₁ ⊕ c₂ ∣ inj₁ x ∣ κ ］
  ↦₁₂ : ∀ {A B C D} {c₁ : A ↔ B} {c₂ : C ↔ D} {y : ⟦ D ⟧} {κ : Context}
      → ［ c₂ ∣ y ∣ c₁ ⊕☐• κ ］ ↦ ［ c₁ ⊕ c₂ ∣ inj₂ y ∣ κ ］


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

open import HoTT
open import cohomology.Theory
open import groups.ExactSequence
open import groups.HomSequence

module cw.cohomology.grid.LongExactSequence {i} (CT : CohomologyTheory i)
  {X Y Z : Ptd i} (n : ℤ) (f : X ⊙→ Y) (g : Y ⊙→ Z) where

open CohomologyTheory CT
open import cohomology.PtdMapSequence CT
open import cw.cohomology.grid.CofiberSplit (fst f) (fst g)
open import cw.cohomology.grid.PtdMap f g

{-
  X --> Y ----> Z
  |     |       |
  v     v       v
  1 -> Y/X --> Z/X
        | this  |
        v   one v
        1 ---> Z/Y
-}

private
  ⊙D-span : ⊙Span
  ⊙D-span = ⊙span Y/X Z Y (⊙cfcod' f) g

  ⊙D : Ptd i
  ⊙D = ⊙Pushout ⊙D-span

  Y/X-to-D : Y/X ⊙→ ⊙D
  Y/X-to-D = B/A-to-D , idp

  open import cohomology.LongExactSequence CT n Y/X-to-D

  ⊙E : Ptd i
  ⊙E = ⊙Cofiber (⊙left ⊙D-span)

  Z/Y-to-E : Z/Y ⊙→ ⊙E
  Z/Y-to-E = C/B-to-E , idp

  Z/X-to-D : Z/X ⊙→ ⊙D
  Z/X-to-D = C/A-to-D , idp

grid-co∂ : C n Y/X →ᴳ C (succ n) Z/Y
grid-co∂ = record {f = CEl-fmap (succ n) Z/Y-to-E ∘ GroupHom.f co∂ ; pres-comp = lemma} where
  abstract lemma = ∘ᴳ-pres-comp (C-fmap (succ n) Z/Y-to-E) co∂

grid-∂-before-Susp : C/B → Susp B/A
grid-∂-before-Susp = extract-glue ∘ C/B-to-E

⊙grid-∂-before-Susp : Z/Y ⊙→ ⊙Susp Y/X
⊙grid-∂-before-Susp = grid-∂-before-Susp , lemma where
  abstract lemma = snd (⊙extract-glue ⊙∘ Z/Y-to-E)

abstract
  grid-co∂-β : GroupHom.f grid-co∂ ∼ CEl-fmap (succ n) ⊙grid-∂-before-Susp ∘ <– (CEl-Susp n Y/X)
  grid-co∂-β x = ∘-CEl-fmap (succ n) Z/Y-to-E ⊙extract-glue _

abstract
  grid-∂-before-Susp-glue-β : ∀ x →
    ap (fst ⊙grid-∂-before-Susp) (glue x) == merid (cfcod x)
  grid-∂-before-Susp-glue-β x =
      ap-∘ extract-glue C/B-to-E (glue x)
    ∙ ap (ap extract-glue) (C/B-to-E-glue-β x)
    ∙ ap-∙' extract-glue (glue (cfcod x)) (ap cfcod (glue x))
    ∙ ap2 _∙'_
        (ExtractGlue.glue-β (cfcod x))
        (∘-ap extract-glue cfcod (glue x) ∙ ap-cst south (glue x))

C-grid-cofiber-seq : HomSequence (C n Z/X) (C (succ n) Y/X)
C-grid-cofiber-seq =
  C n Z/X         →⟨ C-fmap n Y/X-to-Z/X        ⟩ᴳ
  C n Y/X         →⟨ grid-co∂                   ⟩ᴳ
  C (succ n) Z/Y  →⟨ C-fmap (succ n) Z/X-to-Z/Y ⟩ᴳ
  C (succ n) Z/X  →⟨ C-fmap (succ n) Y/X-to-Z/X ⟩ᴳ
  C (succ n) Y/X  ⊣|ᴳ

private
  C-cofiber-seq-to-C-grid-cofiber-seq :
    HomSeqMap C-cofiber-seq C-grid-cofiber-seq
      (C-fmap n Z/X-to-D) (idhom _)
  C-cofiber-seq-to-C-grid-cofiber-seq =
    C-fmap n Z/X-to-D         ↓⟨ comm-sqrᴳ (λ d → ! (C-fmap-idf n _)
                                                ∙ (C-comm-square n B/A-to-C/A-comm-square □$ᴳ d)) ⟩ᴳ
    idhom _                   ↓⟨ comm-sqrᴳ (λ _ → idp) ⟩ᴳ
    C-fmap (succ n) Z/Y-to-E  ↓⟨ C-comm-square (succ n) C/A-to-C/B-comm-square ⟩ᴳ
    C-fmap (succ n) Z/X-to-D  ↓⟨ comm-sqrᴳ (λ d → ! (C-fmap-idf (succ n) _)
                                                ∙ (C-comm-square (succ n) B/A-to-C/A-comm-square □$ᴳ d)) ⟩ᴳ
    idhom _                   ↓|ᴳ

  C-cofiber-seq-equiv-C-grid-cofiber-seq :
    HomSeqEquiv C-cofiber-seq C-grid-cofiber-seq
      (C-fmap n Z/X-to-D) (idhom _)
  C-cofiber-seq-equiv-C-grid-cofiber-seq =
    C-cofiber-seq-to-C-grid-cofiber-seq ,
      CEl-isemap n Z/X-to-D C/A-to-D-is-equiv ,
      idf-is-equiv _ ,
      CEl-isemap (succ n) Z/Y-to-E C/B-to-E-is-equiv ,
      CEl-isemap (succ n) Z/X-to-D C/A-to-D-is-equiv ,
      idf-is-equiv _

abstract
  C-grid-cofiber-seq-is-exact : is-exact-seq C-grid-cofiber-seq
  C-grid-cofiber-seq-is-exact = seq-equiv-preserves-exact
    C-cofiber-seq-equiv-C-grid-cofiber-seq C-cofiber-seq-is-exact

C-grid-cofiber-exact-seq : ExactSequence (C n Z/X) (C (succ n) Y/X)
C-grid-cofiber-exact-seq = C-grid-cofiber-seq , C-grid-cofiber-seq-is-exact


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

module ky where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; _≢_; refl; cong; cong₂; sym)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _≡⟨_⟩_; _∎)
open import Data.Rational using (ℚ; _+_; _*_; _-_)
open import Data.Bool
open import Data.Bool.Properties
open import Data.Rational
open import Data.Nat as ℕ using (ℕ; zero; suc; ⌊_/2⌋)
open import Data.Empty
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Agda.Builtin.Size
open import Data.Product
open import Data.Unit
open import Data.Maybe
open import Data.List

TyCon = List Set

data Env : TyCon → Set₁ where
  nil : Env []
  cons : ∀{t Γ} →  t → Env Γ → Env (t ∷ Γ)

data Idx : Set → TyCon → Set₁ where
  fst : ∀{t Γ} → Idx t (t ∷ Γ)
  nxt : ∀{t t' Γ} → Idx t Γ → Idx t (t' ∷ Γ)

get : ∀{t Γ} → Idx t Γ → Env Γ → t
get fst       (cons v _)  = v
get (nxt idx) (cons _ vs) = get idx vs

upd : ∀{t Γ} → Idx t Γ → t -> Env Γ → Env Γ
upd fst       v' (cons _ vs) = cons v' vs
upd (nxt idx) v' (cons v vs) = cons v (upd idx v' vs)

data Exp : TyCon → Set → Set₁ where
  EVal : ∀{t Γ} → t → Exp Γ t
  EVar : ∀{t Γ} → Idx t Γ → Exp Γ t
  EPlus : ∀{Γ} → Exp Γ ℚ → Exp Γ ℚ → Exp Γ ℚ

record Cotree (i : Size) (A : Set) (Γ : TyCon) : Set₁

data Pattern : Size → TyCon → Set₁ where
  mkPattern : ∀{i Γ} →  --{j₁ j₂ : Size< i} →
    (Cotree i Bool Γ → Cotree i Bool Γ → Cotree i Bool Γ) →
    Pattern i Γ

data Com : Size → TyCon → Set → Set₁ where
  CSkip : ∀{i Γ} → Com i Γ ⊤
  CPrim : ∀{i t Γ} → Pattern i Γ → Com i Γ t → Com i Γ t → Com i Γ t
  --CFlip : ∀{i t Γ} → Com i Γ t → Com i Γ t → Com i Γ t
  CUpd : ∀{i t Γ} → Idx t Γ → Exp Γ t → Com i Γ ⊤
  --CAlloc : ∀{t Γ} → Exp Γ t → Com (t ∷ Γ) ⊤
  CIte : ∀{i t Γ} → Exp Γ Bool → Com i Γ t → Com i Γ t → Com i Γ t
  CSeq : ∀{i t Γ} → Com i Γ ⊤ → Com i Γ t → Com i Γ t
  CWhile : ∀{i t Γ} → Exp Γ Bool → Com i Γ t → Com i Γ ⊤

eval : ∀{t Γ} → Env Γ → Exp Γ t → t
eval ρ (EVal v) = v
eval ρ (EVar x) = get x ρ
eval ρ (EPlus e₁ e₂) = eval ρ e₁ + eval ρ e₂

-- Cf. Abel 2017 (Equational Reasoning about Formal Languages in Coalgebraic Style)
record Cotree i A Γ where
  coinductive
  field
    ν : Env Γ -> Maybe (Env Γ)
    δ : ∀{j : Size< i} → Env Γ → A →  Cotree j A Γ

open Cotree

∅ : ∀{i A Γ} → Cotree i A Γ
ν ∅ _   = nothing
δ ∅ _ _ = ∅

flip : ∀(i Γ) → Cotree i Bool Γ → Cotree i Bool Γ → Cotree i Bool Γ
flip i Γ t₁ t₂ = t
  where t : Cotree i Bool Γ
        ν t ρ       = nothing
        δ t ρ true  = t₁
        δ t ρ false = t₂

interp : ∀(i){τ} → ∀(Γ) → Com i Γ τ → Cotree i Bool Γ
interp i Γ CSkip = t
  where t : ∀{j} → Cotree j Bool Γ
        ν t ρ   = just ρ
        δ t ρ _ = ∅
interp i Γ (CPrim (mkPattern f) c₁ c₂) = t
  where t : Cotree i Bool Γ
        ν t ρ   = nothing
        δ t ρ _ = f (interp i Γ c₁) (interp i Γ c₂)
{-interp i Γ (CFlip c₁ c₂) = t
  where t : Cotree i Bool Γ
        ν t ρ       = nothing
        δ t ρ true  = interp i Γ c₁
        δ t ρ false = interp i Γ c₂-}
interp i Γ (CUpd x e) = t
  where t : ∀{j} → Cotree j Bool Γ
        ν t ρ   = just (upd x (eval ρ e) ρ)
        δ t ρ _ = ∅
interp i Γ (CIte e c₁ c₂) = t
  where t : Cotree i Bool Γ
        ν t ρ with eval ρ e
        ...      | true  = ν (interp i Γ c₁) ρ
        ...      | false = ν (interp i Γ c₂) ρ
        δ t ρ b with eval ρ e
        ...        | true  = δ (interp i Γ c₁) ρ b
        ...        | false = δ (interp i Γ c₂) ρ b
interp i Γ (CSeq c₁ c₂) = t
  where t : Cotree i Bool Γ
        ν t ρ   = nothing
        δ t ρ b with ν (interp i Γ c₁) ρ
        ...        | nothing = δ (interp i Γ c₁) ρ  b
        ...        | just ρ' = δ (interp i Γ c₂) ρ' b
interp i Γ (CWhile e c) = t
  where t : Cotree i Bool Γ
        ν t ρ with eval ρ e
        ...      | true  = ν (interp i Γ c) ρ
        ...      | false = just ρ
        δ t ρ b with eval ρ e
        ...        | true  = δ (interp i Γ c) ρ b
        ...        | false = ∅

-- Flip as a derived command
cflip : ∀(i Γ){τ} → Com i Γ τ → Com i Γ τ → Com i Γ τ
cflip i Γ = CPrim (mkPattern (flip i Γ))


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

open import HoTT

open import stash.modalities.gbm.GbmUtil

module stash.modalities.gbm.PullbackSplit where

--     L --> B    K = A ×_D C / (f,h)       d₁ = A -> D <- C
--     |     |g   L = B ×_A K / (g,left)    d₂ = B -> A <- K
--     v     v                              d  = B -> D <- C
--     K --> A
--     |     |f
--     v     v
--     C --> D
--        h

module PullbackLSplit {i j k l} {A : Type i} {B : Type j} {C : Type k}
  {D : Type l} (f : A → D) (g : B → A) (h : C → D) where

  private
    d₁ : Cospan
    d₁ = cospan C A D h f

    d₂ : Cospan
    d₂ = cospan (Pullback d₁) B A Pullback.b g

    d : Cospan
    d = cospan C B D h (f ∘ g)

  split-equiv : Pullback d ≃ Pullback d₂
  split-equiv = equiv to from to-from from-to

    where to : Pullback d → Pullback d₂
          to (pullback c b p) = pullback (pullback c (g b) p) b idp

          from : Pullback d₂ → Pullback d
          from (pullback (pullback c a p) b idp) = pullback c b p

          to-from : (x : Pullback d₂) → to (from x) == x
          to-from (pullback _ b idp) = idp

          from-to : (x : Pullback d) → from (to x) == x
          from-to _ = idp

record CospanMap {i₀ j₀ k₀ i₁ j₁ k₁} (cospan₀ : Cospan {i₀} {j₀} {k₀}) (cospan₁ : Cospan {i₁} {j₁} {k₁})
  : Type (lmax (lmax (lmax i₀ j₀) k₀) (lmax (lmax i₁ j₁) k₁)) where
  constructor cospan-map
  module cospan₀ = Cospan cospan₀
  module cospan₁ = Cospan cospan₁
  field
    hA : cospan₀.A → cospan₁.A
    hB : cospan₀.B → cospan₁.B
    hC : cospan₀.C → cospan₁.C
    f-commutes : CommSquare cospan₀.f cospan₁.f hA hC
    g-commutes : CommSquare cospan₀.g cospan₁.g hB hC

CospanEquiv : ∀ {i₀ j₀ k₀ i₁ j₁ k₁} (cospan₀ : Cospan {i₀} {j₀} {k₀}) (cospan₁ : Cospan {i₁} {j₁} {k₁})
  → Type (lmax (lmax (lmax i₀ j₀) k₀) (lmax (lmax i₁ j₁) k₁))
CospanEquiv cospan₀ cospan₁ = Σ (CospanMap cospan₀ cospan₁)
  (λ cospan-map → is-equiv (CospanMap.hA cospan-map)
                 × is-equiv (CospanMap.hB cospan-map)
                 × is-equiv (CospanMap.hC cospan-map))

CospanEquiv-inverse : ∀ {i₀ j₀ k₀ i₁ j₁ k₁} {cospan₀ : Cospan {i₀} {j₀} {k₀}} {cospan₁ : Cospan {i₁} {j₁} {k₁}}
  → CospanEquiv cospan₀ cospan₁ → CospanEquiv cospan₁ cospan₀
CospanEquiv-inverse (cospan-map hA hB hC f-commutes g-commutes , hA-ise , hB-ise , hC-ise)
  = (cospan-map hA.g hB.g hC.g
        (CommSquare-inverse-v f-commutes hA-ise hC-ise)
        (CommSquare-inverse-v g-commutes hB-ise hC-ise))
      , ( is-equiv-inverse hA-ise
        , is-equiv-inverse hB-ise
        , is-equiv-inverse hC-ise)
   where module hA = is-equiv hA-ise
         module hB = is-equiv hB-ise
         module hC = is-equiv hC-ise

module PullbackFmap {i₀ j₀ k₀ i₁ j₁ k₁} {cospan₀ : Cospan {i₀} {j₀} {k₀}}
  {cospan₁ : Cospan {i₁} {j₁} {k₁}} (cospan-map : CospanMap cospan₀ cospan₁) where

  f : Pullback cospan₀ → Pullback cospan₁
  f (pullback a b h) = pullback
    (CospanMap.hA cospan-map a)
    (CospanMap.hB cospan-map b)
    (! (commutes (CospanMap.f-commutes cospan-map) a) ∙
      ap (λ x → CospanMap.hC cospan-map x) h ∙
      (commutes (CospanMap.g-commutes cospan-map) b))

Pullback-fmap = PullbackFmap.f

module _ {i₀ j₀ k₀ i₁ j₁ k₁} {cospan₀ : Cospan {i₀} {j₀} {k₀}}
  {cospan₁ : Cospan {i₁} {j₁} {k₁}} (cospan-equiv : CospanEquiv cospan₀ cospan₁) where

  private
    module cospan₀ = Cospan cospan₀
    module cospan₁ = Cospan cospan₁
    cospan-to = fst cospan-equiv
    cospan-from = fst (CospanEquiv-inverse cospan-equiv)
    module cospan-to = CospanMap cospan-to
    module cospan-from = CospanMap cospan-from
    module To = PullbackFmap cospan-to
    module From = PullbackFmap cospan-from
    cospan-ise = snd cospan-equiv
    hA-ise = fst cospan-ise
    hB-ise = fst (snd cospan-ise)
    hC-ise = snd (snd cospan-ise)
    module hA-ise = is-equiv hA-ise
    module hB-ise = is-equiv hB-ise
    module hC-ise = is-equiv hC-ise

    f = cospan₀.f
    g = cospan₀.g
    f' = cospan₁.f
    g' = cospan₁.g

    α = commutes cospan-to.f-commutes
    β = commutes cospan-to.g-commutes

    α' = commutes cospan-from.f-commutes
    β' = commutes cospan-from.g-commutes

    hA = cospan-to.hA
    hB = cospan-to.hB
    hC = cospan-to.hC

    kA = cospan-from.hA
    kB = cospan-from.hB
    kC = cospan-from.hC

    module kA-ise = is-equiv (is-equiv-inverse hA-ise)
    module kB-ise = is-equiv (is-equiv-inverse hB-ise)
    module kC-ise = is-equiv (is-equiv-inverse hC-ise)

    kA-ise = is-equiv-inverse hA-ise
    kB-ise = is-equiv-inverse hB-ise
    kC-ise = is-equiv-inverse hC-ise

    to = To.f
    from = From.f

    from-to : ∀ x → from (to x) == x
    from-to (pullback a b h) = pullback= _ (hA-ise.g-f a) (hB-ise.g-f b) goal

      where eq₀ = hC-ise.g-f (f a) ∙ h ∙ ! (hC-ise.g-f (g b))
                    =⟨ ! (ap-idf h) |in-ctx (λ x → hC-ise.g-f (f a) ∙ x ∙ ! (hC-ise.g-f (g b))) ⟩
                  hC-ise.g-f (f a) ∙ ap (idf _) h ∙ ! (hC-ise.g-f (g b))
                    =⟨ ! (∙-assoc (hC-ise.g-f (f a)) _ _) ⟩
                  (hC-ise.g-f (f a) ∙ ap (idf _) h) ∙ ! (hC-ise.g-f (g b))
                    =⟨ ↓-==-out (apd hC-ise.g-f h) |in-ctx (λ x → x ∙ ! (hC-ise.g-f (g b))) ⟩
                  (ap (kC ∘ hC) h ∙ hC-ise.g-f (g b)) ∙ ! (hC-ise.g-f (g b))
                    =⟨ ∙-assoc (ap (kC ∘ hC) h) _ _ ⟩
                  ap (kC ∘ hC) h ∙ hC-ise.g-f (g b) ∙ ! (hC-ise.g-f (g b))
                    =⟨ !-inv-r (hC-ise.g-f (g b)) |in-ctx (λ x → ap (kC ∘ hC) h ∙ x) ⟩
                  ap (kC ∘ hC) h ∙ idp
                    =⟨ ∙-unit-r (ap (kC ∘ hC) h) ⟩
                  ap (kC ∘ hC) h ∎

            eq₁ = β' (hB b)
                    =⟨ ! (hB-ise.adj b) |in-ctx (λ x → ap kC (! (β (kB (hB b)) ∙ ap g' x)) ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ap kC (! (β (kB (hB b)) ∙ ap g' (ap hB (hB-ise.g-f b)))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ∘-ap g' hB (hB-ise.g-f b) |in-ctx (λ x → ap kC (! (β (kB (hB b)) ∙ x)) ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ap kC (! (β (kB (hB b)) ∙ ap (g' ∘ hB) (hB-ise.g-f b))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ↓-==-out (apd β (hB-ise.g-f b)) |in-ctx (λ x → ap kC (! x) ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ap kC (! (ap (hC ∘ g) (hB-ise.g-f b) ∙ β b)) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ap-! kC (ap (hC ∘ g) (hB-ise.g-f b) ∙ β b) |in-ctx (λ x → x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (ap (hC ∘ g) (hB-ise.g-f b) ∙ β b)) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ap-∙ kC (ap (hC ∘ g) (hB-ise.g-f b)) (β b) |in-ctx (λ x → ! x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (ap (hC ∘ g) (hB-ise.g-f b)) ∙ ap kC (β b)) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ !-∙ (ap kC (ap (hC ∘ g) (hB-ise.g-f b))) (ap kC (β b)) |in-ctx (λ x → x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  (! (ap kC (β b)) ∙ ! (ap kC (ap (hC ∘ g) (hB-ise.g-f b)))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ∙-assoc (! (ap kC (β b))) _ _ ⟩
                  ! (ap kC (β b)) ∙ ! (ap kC (ap (hC ∘ g) (hB-ise.g-f b))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ∘-ap kC (hC ∘ g) (hB-ise.g-f b) |in-ctx (λ x → ! (ap kC (β b)) ∙ ! x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (β b)) ∙ ! (ap (kC ∘ hC ∘ g) (hB-ise.g-f b)) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ! (∘-ap (kC ∘ hC) g (hB-ise.g-f b)) |in-ctx (λ x → ! (ap kC (β b)) ∙ ! x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (β b)) ∙ ! (ap (kC ∘ hC) (ap g (hB-ise.g-f b))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ eqv-square' (hC , hC-ise) (ap g (hB-ise.g-f b)) |in-ctx (λ x → ! (ap kC (β b)) ∙ ! x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (β b)) ∙ ! (hC-ise.g-f (g (kB (hB b))) ∙ (ap g (hB-ise.g-f b)) ∙ ! (hC-ise.g-f (g b))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ !-∙ (hC-ise.g-f (g (kB (hB b)))) ((ap g (hB-ise.g-f b)) ∙ ! (hC-ise.g-f (g b))) |in-ctx (λ x → ! (ap kC (β b)) ∙ x ∙ hC-ise.g-f (g (kB (hB b)))) ⟩
                  ! (ap kC (β b)) ∙ (! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b))) ∙ ! (hC-ise.g-f (g (kB (hB b))))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ ∙-assoc (! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b)))) _ _ |in-ctx (λ x → ! (ap kC (β b)) ∙ x) ⟩
                  ! (ap kC (β b)) ∙ ! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b))) ∙ ! (hC-ise.g-f (g (kB (hB b)))) ∙ hC-ise.g-f (g (kB (hB b)))
                    =⟨ !-inv-l (hC-ise.g-f (g (kB (hB b)))) |in-ctx (λ x → ! (ap kC (β b)) ∙ ! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b))) ∙ x) ⟩
                  ! (ap kC (β b)) ∙ ! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b))) ∙ idp
                    =⟨ ∙-unit-r (! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b)))) |in-ctx (λ x → ! (ap kC (β b)) ∙ x) ⟩
                  ! (ap kC (β b)) ∙ ! (ap g (hB-ise.g-f b) ∙ ! (hC-ise.g-f (g b)))
                    =⟨ !-∙ (ap g (hB-ise.g-f b)) (! (hC-ise.g-f (g b))) |in-ctx (λ x → ! (ap kC (β b)) ∙ x) ⟩
                  ! (ap kC (β b)) ∙ ! (! (hC-ise.g-f (g b))) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-! (hC-ise.g-f (g b)) |in-ctx (λ x → ! (ap kC (β b)) ∙ x ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (ap kC (β b)) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b)) ∎

            eq₂ = α' (hA a)
                    =⟨ ! (hA-ise.adj a) |in-ctx (λ x → ap kC (! (α (kA (hA a)) ∙ ap f' x)) ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ap kC (! (α (kA (hA a)) ∙ ap f' (ap hA (hA-ise.g-f a)))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ∘-ap f' hA (hA-ise.g-f a) |in-ctx (λ x → ap kC (! (α (kA (hA a)) ∙ x)) ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ap kC (! (α (kA (hA a)) ∙ ap (f' ∘ hA) (hA-ise.g-f a))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ↓-==-out (apd α (hA-ise.g-f a)) |in-ctx (λ x → ap kC (! x) ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ap kC (! (ap (hC ∘ f) (hA-ise.g-f a) ∙ α a)) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ap-! kC (ap (hC ∘ f) (hA-ise.g-f a) ∙ α a) |in-ctx (λ x → x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (ap (hC ∘ f) (hA-ise.g-f a) ∙ α a)) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ap-∙ kC (ap (hC ∘ f) (hA-ise.g-f a)) (α a) |in-ctx (λ x → ! x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (ap (hC ∘ f) (hA-ise.g-f a)) ∙ ap kC (α a)) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ !-∙ (ap kC (ap (hC ∘ f) (hA-ise.g-f a))) (ap kC (α a)) |in-ctx (λ x → x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  (! (ap kC (α a)) ∙ ! (ap kC (ap (hC ∘ f) (hA-ise.g-f a)))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ∙-assoc (! (ap kC (α a))) _ _ ⟩
                  ! (ap kC (α a)) ∙ ! (ap kC (ap (hC ∘ f) (hA-ise.g-f a))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ∘-ap kC (hC ∘ f) (hA-ise.g-f a) |in-ctx (λ x → ! (ap kC (α a)) ∙ ! x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (α a)) ∙ ! (ap (kC ∘ hC ∘ f) (hA-ise.g-f a)) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ! (∘-ap (kC ∘ hC) f (hA-ise.g-f a)) |in-ctx (λ x → ! (ap kC (α a)) ∙ ! x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (α a)) ∙ ! (ap (kC ∘ hC) (ap f (hA-ise.g-f a))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ eqv-square' (hC , hC-ise) (ap f (hA-ise.g-f a)) |in-ctx (λ x → ! (ap kC (α a)) ∙ ! x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (α a)) ∙ ! (hC-ise.g-f (f (kA (hA a))) ∙ (ap f (hA-ise.g-f a)) ∙ ! (hC-ise.g-f (f a))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ !-∙ (hC-ise.g-f (f (kA (hA a)))) ((ap f (hA-ise.g-f a)) ∙ ! (hC-ise.g-f (f a))) |in-ctx (λ x → ! (ap kC (α a)) ∙ x ∙ hC-ise.g-f (f (kA (hA a)))) ⟩
                  ! (ap kC (α a)) ∙ (! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a))) ∙ ! (hC-ise.g-f (f (kA (hA a))))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ ∙-assoc (! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a)))) _ _ |in-ctx (λ x → ! (ap kC (α a)) ∙ x) ⟩
                  ! (ap kC (α a)) ∙ ! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a))) ∙ ! (hC-ise.g-f (f (kA (hA a)))) ∙ hC-ise.g-f (f (kA (hA a)))
                    =⟨ !-inv-l (hC-ise.g-f (f (kA (hA a)))) |in-ctx (λ x → ! (ap kC (α a)) ∙ ! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a))) ∙ x) ⟩
                  ! (ap kC (α a)) ∙ ! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a))) ∙ idp
                    =⟨ ∙-unit-r (! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a)))) |in-ctx (λ x → ! (ap kC (α a)) ∙ x) ⟩
                  ! (ap kC (α a)) ∙ ! (ap f (hA-ise.g-f a) ∙ ! (hC-ise.g-f (f a)))
                    =⟨ !-∙ (ap f (hA-ise.g-f a)) (! (hC-ise.g-f (f a))) |in-ctx (λ x → ! (ap kC (α a)) ∙ x) ⟩
                  ! (ap kC (α a)) ∙ ! (! (hC-ise.g-f (f a))) ∙ ! (ap f (hA-ise.g-f a))
                    =⟨ !-! (hC-ise.g-f (f a)) |in-ctx (λ x → ! (ap kC (α a)) ∙ x ∙ ! (ap f (hA-ise.g-f a))) ⟩
                  ! (ap kC (α a)) ∙ hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a)) ∎

            lem = ! (α' (hA a)) ∙ ap kC (! (α a) ∙ ap hC h ∙ β b) ∙ β' (hB b)
                    =⟨ ap-∙ kC (! (α a)) (ap hC h ∙ β b) |in-ctx (λ x → ! (α' (hA a)) ∙ x ∙ β' (hB b)) ⟩
                  ! (α' (hA a)) ∙ (ap kC (! (α a)) ∙ ap kC (ap hC h ∙ β b)) ∙ β' (hB b)
                    =⟨ ∙-assoc (ap kC (! (α a))) (ap kC (ap hC h ∙ β b)) (β' (hB b)) |in-ctx (λ x → ! (α' (hA a)) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap kC (ap hC h ∙ β b) ∙ β' (hB b)
                    =⟨ ap-∙ kC (ap hC h) (β b) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x ∙ β' (hB b)) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (ap kC (ap hC h) ∙ ap kC (β b)) ∙ β' (hB b)
                    =⟨ ∙-assoc (ap kC (ap hC h)) (ap kC (β b)) (β' (hB b)) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap kC (ap hC h) ∙ ap kC (β b) ∙ β' (hB b)
                    =⟨ ∘-ap kC hC h |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x ∙ ap kC (β b) ∙ β' (hB b)) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ ap kC (β b) ∙ β' (hB b)
                    =⟨ eq₁ |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ ap kC (β b) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ ap kC (β b) ∙ ! (ap kC (β b)) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! (∙-assoc (ap kC (β b)) _ _) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ (ap kC (β b) ∙ ! (ap kC (β b))) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-inv-r (ap kC (β b)) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ x ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ap (kC ∘ hC) h ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! eq₀ |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h ∙ ! (hC-ise.g-f (g b))) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! (∙-assoc (hC-ise.g-f (f a)) _ _) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ ((hC-ise.g-f (f a) ∙ h) ∙ ! (hC-ise.g-f (g b))) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ∙-assoc (hC-ise.g-f (f a) ∙ h) _ _ |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h) ∙ ! (hC-ise.g-f (g b)) ∙ hC-ise.g-f (g b) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! (∙-assoc (! (hC-ise.g-f (g b))) _ _) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h) ∙ (! (hC-ise.g-f (g b)) ∙ hC-ise.g-f (g b)) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-inv-l (hC-ise.g-f (g b)) |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h) ∙ x ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ (hC-ise.g-f (f a) ∙ h) ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ∙-assoc (hC-ise.g-f (f a)) _ _ |in-ctx (λ x → ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ x) ⟩
                  ! (α' (hA a)) ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ eq₂ |in-ctx (λ x → ! x ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (! (ap kC (α a)) ∙ hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-∙ (! (ap kC (α a))) _ |in-ctx (λ x → x ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  (! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ ! (! (ap kC (α a)))) ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ∙-assoc (! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a)))) _ _ ⟩
                  ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ ! (! (ap kC (α a))) ∙ ap kC (! (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ap-! kC (α a) |in-ctx (λ x → ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ ! (! (ap kC (α a))) ∙ x ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ ! (! (ap kC (α a))) ∙ ! (ap kC (α a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! (∙-assoc (! (! (ap kC (α a)))) _ _) |in-ctx (λ x → ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ x) ⟩
                  ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ (! (! (ap kC (α a))) ∙ ! (ap kC (α a))) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-inv-l (! (ap kC (α a))) |in-ctx (λ x → ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ x ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (hC-ise.g-f (f a) ∙ ! (ap f (hA-ise.g-f a))) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-∙ (hC-ise.g-f (f a)) _  |in-ctx (λ x → x ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  (! (! (ap f (hA-ise.g-f a))) ∙ ! (hC-ise.g-f (f a))) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ∙-assoc (! (! (ap f (hA-ise.g-f a)))) _ _ ⟩
                  ! (! (ap f (hA-ise.g-f a))) ∙ ! (hC-ise.g-f (f a)) ∙ hC-ise.g-f (f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ ! (∙-assoc (! (hC-ise.g-f (f a))) _ _) |in-ctx (λ x → ! (! (ap f (hA-ise.g-f a))) ∙ x) ⟩
                  ! (! (ap f (hA-ise.g-f a))) ∙ (! (hC-ise.g-f (f a)) ∙ hC-ise.g-f (f a)) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-inv-l (hC-ise.g-f (f a)) |in-ctx (λ x → ! (! (ap f (hA-ise.g-f a))) ∙ x ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ! (! (ap f (hA-ise.g-f a))) ∙ h ∙ ! (ap g (hB-ise.g-f b))
                    =⟨ !-! (ap f (hA-ise.g-f a)) |in-ctx (λ x → x ∙ h ∙ ! (ap g (hB-ise.g-f b))) ⟩
                  ap f (hA-ise.g-f a) ∙ h ∙ ! (ap g (hB-ise.g-f b)) ∎

            goal = (! (α' (hA a)) ∙ ap kC (! (α a) ∙ ap hC h ∙ β b) ∙ β' (hB b)) ∙ ap g (hB-ise.g-f b)
                     =⟨ lem |in-ctx (λ x → x ∙ ap g (hB-ise.g-f b)) ⟩
                   (ap f (hA-ise.g-f a) ∙ h ∙ ! (ap g (hB-ise.g-f b))) ∙ ap g (hB-ise.g-f b)
                     =⟨ ∙-assoc (ap f (hA-ise.g-f a)) (h ∙ ! (ap g (hB-ise.g-f b))) _ ⟩
                   ap f (hA-ise.g-f a) ∙ (h ∙ ! (ap g (hB-ise.g-f b))) ∙ ap g (hB-ise.g-f b)
                     =⟨ ∙-assoc h _ _ |in-ctx (λ x → ap f (hA-ise.g-f a) ∙ x) ⟩
                   ap f (hA-ise.g-f a) ∙ h ∙ ! (ap g (hB-ise.g-f b)) ∙ ap g (hB-ise.g-f b)
                     =⟨ !-inv-l (ap g (hB-ise.g-f b)) |in-ctx (λ x → ap f (hA-ise.g-f a) ∙ h ∙ x) ⟩
                   ap f (hA-ise.g-f a) ∙ h ∙ idp
                     =⟨ ∙-unit-r h |in-ctx (λ x → ap f (hA-ise.g-f a) ∙ x) ⟩
                   ap f (hA-ise.g-f a) ∙ h ∎

    postulate
      to-from : ∀ y → to (from y) == y
      -- to-from (pullback a b h) = pullback= _ (hA-ise.f-g a) (hB-ise.f-g b) {!!}

  Pullback-emap : Pullback cospan₀ ≃ Pullback cospan₁
  Pullback-emap = equiv to from to-from from-to


open import core

module focus-formation where
  -- every ε is an evaluation context -- trivially, here, since we don't
  -- include any of the premises in red brackets about finality
  focus-formation : ∀{d d' ε} → d == ε ⟦ d' ⟧ → ε evalctx
  focus-formation FHOuter = ECDot
  focus-formation (FHAp1 sub) = ECAp1 (focus-formation sub)
  focus-formation (FHAp2 sub) = ECAp2 (focus-formation sub)
  focus-formation (FHNEHole sub) = ECNEHole (focus-formation sub)
  focus-formation (FHCast sub) = ECCast (focus-formation sub)
  focus-formation (FHFailedCast x) = ECFailedCast (focus-formation x)


open import Data.Product using ( _×_ ; _,_ )
open import Data.Sum using ( inj₁ ; inj₂ )
open import Relation.Binary.PropositionalEquality using ( refl )
open import Relation.Unary using ( _∈_ ; _∉_ ; _⊆_ )
open import Web.Semantic.DL.Concept.Model using
  ( _⟦_⟧₁ ; ⟦⟧₁-resp-≈ ; ⟦⟧₁-resp-≃; ⟦⟧₁-refl-≃ )
open import Web.Semantic.DL.Role.Model using
  ( _⟦_⟧₂ ; ⟦⟧₂-resp-≈ ; ⟦⟧₂-resp-≃ ; ⟦⟧₂-refl-≃ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using
  ( TBox ; Axioms ; ε ; _,_ ;_⊑₁_ ; _⊑₂_ ; Dis ; Ref ; Irr ; Tra )
open import Web.Semantic.DL.TBox.Interp using ( Interp )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( _≃_ ; ≃-sym )
open import Web.Semantic.Util using ( True ; tt ; _∘_ )

module Web.Semantic.DL.TBox.Model {Σ : Signature} where

infixr 2 _⊨t_

_⊨t_ : Interp Σ → TBox Σ → Set
I ⊨t ε         = True
I ⊨t (T , U)   = (I ⊨t T) × (I ⊨t U)
I ⊨t (C ⊑₁ D)  = I ⟦ C ⟧₁ ⊆ I ⟦ D ⟧₁
I ⊨t (Q ⊑₂ R)  = I ⟦ Q ⟧₂ ⊆ I ⟦ R ⟧₂
I ⊨t (Dis Q R) = ∀ {xy} → (xy ∈ I ⟦ Q ⟧₂) → (xy ∉ I ⟦ R ⟧₂)
I ⊨t (Ref R)   = ∀ x → ((x , x) ∈ I ⟦ R ⟧₂)
I ⊨t (Irr R)   = ∀ x → ((x , x) ∉ I ⟦ R ⟧₂)
I ⊨t (Tra R)   = ∀ {x y z} →
  ((x , y) ∈ I ⟦ R ⟧₂) → ((y , z) ∈ I ⟦ R ⟧₂) → ((x , z) ∈ I ⟦ R ⟧₂)

Axioms✓ : ∀ I T {t} → (t ∈ Axioms T) → (I ⊨t T) → (I ⊨t t)
Axioms✓ I ε         ()         I⊨T
Axioms✓ I (T , U)   (inj₁ t∈T) (I⊨T , I⊨U) = Axioms✓ I T t∈T I⊨T
Axioms✓ I (T , U)   (inj₂ t∈U) (I⊨T , I⊨U) = Axioms✓ I U t∈U I⊨U
Axioms✓ I (C ⊑₁ D)  refl       I⊨T         = I⊨T
Axioms✓ I (Q ⊑₂ R)  refl       I⊨T         = I⊨T
Axioms✓ I (Dis Q R) refl       I⊨T         = I⊨T
Axioms✓ I (Ref R)   refl       I⊨T         = I⊨T
Axioms✓ I (Irr R)   refl       I⊨T         = I⊨T
Axioms✓ I (Tra R)   refl       I⊨T         = I⊨T

⊨t-resp-≃ : ∀ {I J : Interp Σ} → (I ≃ J) → ∀ T → (I ⊨t T) → (J ⊨t T)
⊨t-resp-≃ {I} {J} I≃J ε _ =
  tt
⊨t-resp-≃ {I} {J} I≃J (T , U) (I⊨T , I⊨U) =
  (⊨t-resp-≃ I≃J T I⊨T , ⊨t-resp-≃ I≃J U I⊨U)
⊨t-resp-≃ {I} {J} I≃J (C ⊑₁ D) I⊨C⊑D =
  ⟦⟧₁-refl-≃ I≃J D ∘ I⊨C⊑D ∘ ⟦⟧₁-resp-≃ (≃-sym I≃J) C
⊨t-resp-≃ {I} {J} I≃J (Q ⊑₂ R) I⊨Q⊑R = ⟦⟧₂-refl-≃ I≃J R ∘ I⊨Q⊑R ∘
  ⟦⟧₂-resp-≃ (≃-sym I≃J) Q
⊨t-resp-≃ {I} {J} I≃J (Dis Q R) I⊨DisQR = λ xy∈⟦Q⟧ xy∈⟦R⟧ →
  I⊨DisQR (⟦⟧₂-resp-≃ (≃-sym I≃J) Q xy∈⟦Q⟧) (⟦⟧₂-resp-≃ (≃-sym I≃J) R xy∈⟦R⟧)
⊨t-resp-≃ {I} {J} I≃J (Ref R) I⊨RefR = λ x → ⟦⟧₂-refl-≃ I≃J R (I⊨RefR _)
⊨t-resp-≃ {I} {J} I≃J (Irr R) I⊨IrrR = λ x xx∈⟦R⟧ →
  I⊨IrrR _ (⟦⟧₂-resp-≃ (≃-sym I≃J) R xx∈⟦R⟧)
⊨t-resp-≃ {I} {J} I≃J (Tra R) I⊨TraR = λ xy∈⟦R⟧ yz∈⟦R⟧ →
  ⟦⟧₂-refl-≃ I≃J R (I⊨TraR
    (⟦⟧₂-resp-≃ (≃-sym I≃J) R xy∈⟦R⟧)
    (⟦⟧₂-resp-≃ (≃-sym I≃J) R yz∈⟦R⟧))


open import Agda.Builtin.Reflection
open import Agda.Builtin.String
open import Agda.Builtin.List
open import Agda.Builtin.Nat
open import Agda.Builtin.Unit
open import Agda.Builtin.Equality
open import Agda.Builtin.Sigma

data Wrap (A : Set) : Set where
  [_] : A → Wrap A

macro
  give : Wrap Term → Term → TC ⊤
  give [ t ] hole = unify hole t

pattern vArg x = arg (arg-info visible relevant) x
pattern hArg x = arg (arg-info hidden  relevant) x

-- Naming the variable "_" doesn't affect the deBruijn indices for lambda and pi

`id : Term
`id = lam visible (abs "_" (var 0 []))

`IdType : Term
`IdType = pi (hArg (agda-sort (lit 0))) (abs "_" (pi (vArg (var 0 [])) (abs "_" (var 1 []))))

id : give [ `IdType ]
id = give [ `id ]

id-ok : (λ {A : Set} (x : A) → x) ≡ id
id-ok = refl

-- Underscores should behave the same for clauses as for lambda and pi

idClause : Clause
idClause = clause
             (("_" , vArg unknown) ∷ ("_" , hArg unknown) ∷ [])
             (hArg (var 1) ∷ vArg (var 0) ∷ [])
             (var 0 [])

infixr 4 _>>=_
_>>=_ = bindTC

unquoteDecl id₂ =
  declareDef (vArg id₂) `IdType >>= λ _ →
  defineFun id₂ (idClause ∷ [])

id₂-ok : (λ {A : Set} (x : A) → x) ≡ id₂
id₂-ok = refl


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

module CauchyProofs where

-- Proofs about permutations defined in module Cauchy (everything
-- except the multiplicative ones which are defined in CauchyProofsT and
-- CauchyProofsS

open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; trans; subst; subst₂; cong; cong₂; setoid;
        proof-irrelevance; module ≡-Reasoning)
open import Data.Nat.Properties
  using (m≤m+n; n≤m+n; n≤1+n; cancel-*-right-≤; ≰⇒>; ¬i+1+j≤i)
open import Data.Nat.Properties.Simple
  using (+-right-identity; +-suc; +-assoc; +-comm;
        *-assoc; *-comm; *-right-zero; distribʳ-*-+; +-*-suc)

open import Data.Nat using (ℕ; suc; _+_; _∸_; _*_; _<_; _≮_; _≤_; _≰_;
  _≥_; z≤n; s≤s; _≟_; _≤?_; ≤-pred; module ≤-Reasoning)
open import Data.Fin
  using (Fin; zero; suc; toℕ; fromℕ; fromℕ≤; _ℕ-_; _≺_; reduce≥;
         raise; inject+; inject₁; inject≤; _≻toℕ_)
  renaming (_+_ to _F+_)
open import Data.Fin.Properties using (bounded; inject+-lemma; to-from)
open import Data.Vec.Properties
  using (lookup∘tabulate; tabulate∘lookup; lookup-allFin; tabulate-∘;
         tabulate-allFin; allFin-map; lookup-++-inject+; lookup-++-≥)

open import Data.Vec
  using (Vec; tabulate; []; _∷_; tail; lookup; zip; zipWith; splitAt;
         _[_]≔_; allFin; toList)
  renaming (_++_ to _++V_; map to mapV; concat to concatV)
open import Function using (id; _∘_; _$_; _∋_; flip)

open import Proofs
open import Cauchy

------------------------------------------------------------------------------
-- Proofs about sequential composition

-- sequential composition with id on the right is identity

scomprid : ∀ {n} → (perm : Cauchy n) → scompcauchy perm (idcauchy n) ≡ perm
scomprid {n} perm =
  begin (scompcauchy perm (idcauchy n)
           ≡⟨ refl ⟩
         tabulate (λ i → lookup (lookup i perm) (allFin n))
           ≡⟨ finext (λ i → lookup-allFin (lookup i perm)) ⟩
         tabulate (λ i → lookup i perm)
           ≡⟨ tabulate∘lookup perm ⟩
         perm ∎)
  where open ≡-Reasoning

-- sequential composition with id on the left is identity

scomplid : ∀ {n} → (perm : Cauchy n) → scompcauchy (idcauchy n) perm ≡ perm
scomplid {n} perm =
  trans (finext (λ i → cong (λ x → lookup x perm) (lookup-allFin i)))
        (tabulate∘lookup perm)

-- sequential composition is associative
scompassoc : ∀ {n} → (π₁ π₂ π₃ : Cauchy n) →
  scompcauchy π₁ (scompcauchy π₂ π₃) ≡ scompcauchy (scompcauchy π₁ π₂) π₃
scompassoc π₁ π₂ π₃ = finext (lookupassoc π₁ π₂ π₃)

------------------------------------------------------------------------------
-- Proofs about additive permutations

lookup-subst2 : ∀ {m m'}
  (i : Fin m) (xs : Vec (Fin m) m) (eq : m ≡ m') →
  lookup (subst Fin eq i)
              (subst Cauchy eq xs) ≡ subst Fin eq (lookup i xs)
lookup-subst2 i xs refl = refl

allFin+ : (m n : ℕ) → allFin (m + n) ≡
          mapV (inject+ n) (allFin m) ++V mapV (raise m) (allFin n)
allFin+ m n = trans (tabulate-split {m} {n})
   (cong₂ _++V_ (tabulate-allFin {m} (inject+  n)) (tabulate-allFin {n} (raise m)))

-- swap+ is idempotent
--
-- outline of swap+idemp proof
--
-- allFin (m + n) ≡ mapV (inject+ n) (allFin m) ++V mapV (raise m) (allFin n)
-- zero-m : Vec (Fin (m + n)) m ≡ mapV (inject+ n) (allFin m)
-- m-sum  : Vec (Fin (m + n)) n ≡ mapV (raise m) (allFin n)
-- allFin (n + m) ≡ mapV (inject+ m) (allFin n) ++V mapV (raise n) (allFin m)
-- zero-n : Vec (Fin (n + m)) n ≡ mapV (inject+ m) (allFin n)
-- n-sum  : Vec (Fin (n + m)) m ≡ mapV (raise n) (allFin m)
--
-- first swap re-arranges allFin (n + m) to n-sum ++V zero-n
-- second swap re-arranges allfin (m + n) to m-sum ++V zero-m
--
-- for i = 0, ..., m-1, we have inject+ n i : Fin (m + n)
-- lookup (lookup (inject+ n i) (n-sum ++V zero-n)) (m-sum ++V zero-m) ==>
-- lookup (lookup i n-sum) (m-sum ++V zero-m) ==>
-- lookup (raise n i) (m-sum ++V zero-m) ==>
-- lookup i zero-m ==>
-- inject+ n i
--
-- for i = m, ..., m+n-1, we have raise m i : Fin (m + n)
-- lookup (lookup (raise m i) (n-sum ++V zero-n)) (m-sum ++V zero-m) ==>
-- lookup (lookup i zero-n) (m-sum ++V zero-m) ==>
-- lookup (inject+ m i) (m-sum ++V zero-m) ==>
-- lookup i m-sum ==>
-- raise m i

swap+-left : (m n : ℕ) → ∀ (i : Fin m) →
  let q = subst Cauchy (+-comm n m) (swap+cauchy n m) in
  lookup (lookup (inject+ n i) (swap+cauchy m n)) q ≡ inject+ n i
swap+-left m n i =
  let q = subst Cauchy (+-comm n m) (swap+cauchy n m) in
  begin (
  lookup (lookup (inject+ n i) (swap+cauchy m n)) q
    ≡⟨ cong (flip lookup q) ( (lookup-++-inject+ (tabulate {m} (id+ {m} {n} ∘ raise n))
                                                                          (tabulate {n} (id+ {m} {n} ∘ inject+ m)) i)) ⟩
  lookup (lookup i (tabulate (λ x → subst Fin (+-comm n m) (raise n x)))) q
    ≡⟨ cong (flip lookup q) (lookup∘tabulate (λ x → subst Fin (+-comm n m) (raise n x)) i) ⟩
  lookup (subst Fin (+-comm n m) (raise n i)) q
    ≡⟨  lookup-subst2 (raise n i) (swap+cauchy n m) (+-comm n m) ⟩
  subst Fin (+-comm n m) (lookup (raise n i) (swap+cauchy n m))
    ≡⟨  cong (subst Fin (+-comm n m))
                   (lookup-++-raise (tabulate {n} (id+ {n} ∘ raise m)) (tabulate {m} (id+ {n} ∘ inject+ n)) i) ⟩
  subst Fin (+-comm n m)
    (lookup i (tabulate (λ z → subst Fin (+-comm m n) (inject+ n z))))
    ≡⟨  subst-lookup-tabulate-inject+ i ⟩
  inject+ n i ∎)
  where open ≡-Reasoning

swap+-right : (m n : ℕ) → (i : Fin n) →
  let q = subst Cauchy (+-comm n m) (swap+cauchy n m) in
  lookup (lookup (raise m i) (swap+cauchy m n)) q ≡ raise m i
swap+-right m n i =
  let q = subst Cauchy (+-comm n m) (swap+cauchy n m) in
  begin (
  lookup (lookup (raise m i) (swap+cauchy m n)) q
    ≡⟨ cong (flip lookup q) ( (lookup-++-raise (tabulate {m} (id+ {m} {n} ∘ raise n))
                                                                       (tabulate {n} (id+ {m} {n} ∘ inject+ m)) i)) ⟩
  lookup (lookup i (tabulate (λ x → subst Fin (+-comm n m) (inject+ m x)))) q
    ≡⟨ cong (flip lookup q) (lookup∘tabulate (λ x → subst Fin (+-comm n m) (inject+ m x)) i) ⟩
  lookup (subst Fin (+-comm n m) (inject+ m i)) q
    ≡⟨  lookup-subst2 (inject+ m i) (swap+cauchy n m) (+-comm n m) ⟩
  subst Fin (+-comm n m) (lookup (inject+ m i) (swap+cauchy n m))
    ≡⟨  cong (subst Fin (+-comm n m))
                   (lookup-++-inject+ (tabulate {n} (id+ {n} ∘ raise m)) (tabulate {m} (id+ {n} ∘ inject+ n)) i) ⟩
  subst Fin (+-comm n m)
    (lookup i (tabulate (λ z → subst Fin (+-comm m n) (raise m z))))
    ≡⟨  subst-lookup-tabulate-raise i ⟩
  raise m i ∎)
  where open ≡-Reasoning

swap+idemp : (m n : ℕ) →
  scompcauchy
    (swap+cauchy m n)
    (subst Cauchy (+-comm n m) (swap+cauchy n m))
  ≡
  allFin (m + n)
swap+idemp m n =
  let q = subst Cauchy (+-comm n m) (swap+cauchy n m) in
  begin
    (tabulate (λ i → lookup (lookup i (swap+cauchy m n)) q)
         ≡⟨ tabulate-split {m} {n} ⟩
     tabulate {m} (λ i → lookup (lookup (inject+ n i) (swap+cauchy m n)) q)
         ++V
     tabulate {n} (λ i → lookup (lookup (raise m i) (swap+cauchy m n)) q)
         ≡⟨ cong₂ _++V_ (finext (swap+-left m n))
                                     (finext (swap+-right m n)) ⟩
     tabulate {m} (inject+ n) ++V tabulate {n} (raise m)
         ≡⟨ unSplit {m} {n} id ⟩
     allFin (m + n) ∎)
  where open ≡-Reasoning

-- Behaviour of parallel additive composition wrt sequential

-- a direct proof is hard, but this is really a statement about vectors

lookup-left : ∀ {m n} → (i : Fin m) → (pm : Cauchy m) → (pn : Cauchy n) →
  lookup (inject+ n i) (mapV (inject+ n) pm ++V mapV (raise m) pn)
  ≡ inject+ n (lookup i pm)
lookup-left {m} {n} i pm pn = look-left i (inject+ n) (raise m) pm pn

-- as is this

lookup-right : ∀ {m n} → (i : Fin n) → (pm : Cauchy m) → (pn : Cauchy n) →
  lookup (raise m i) (mapV (inject+ n) pm ++V mapV (raise m) pn)
  ≡ raise m (lookup i pn)
lookup-right {m} {n} i pm pn = look-right i (inject+ n) (raise m) pm pn

--- find a better name
look-left' : ∀ {m n} → (pm qm : Cauchy m) → (pn qn : Cauchy n) →
      (i : Fin m) →
      (lookup (lookup (inject+ n i) (pcompcauchy pm pn)) (pcompcauchy qm qn))
      ≡ inject+ n (lookup (lookup i pm) qm)
look-left' {m} {n} pm qm pn qn i =
  let pp = pcompcauchy pm pn in
  let qq = pcompcauchy qm qn in
  begin (
    lookup (lookup (inject+ n i) pp) qq
          ≡⟨ cong (flip lookup qq) (lookup-++-inject+ (mapV (inject+ n) pm) (mapV (raise m) pn) i) ⟩
    lookup (lookup i (mapV (inject+ n) pm)) qq
          ≡⟨ cong (flip lookup qq) (lookup-map i (inject+ n) pm) ⟩
    lookup (inject+ n (lookup i pm)) qq
         ≡⟨ lookup-left (lookup i pm) qm qn ⟩
    inject+ n (lookup (lookup i pm) qm) ∎)
  where open ≡-Reasoning

look-right' : ∀ {m n} → (pm qm : Cauchy m) → (pn qn : Cauchy n) →
      (i : Fin n) →
      (lookup (lookup (raise m i) (pcompcauchy pm pn)) (pcompcauchy qm qn))
      ≡ raise m (lookup (lookup i pn) qn)
look-right' {m} {n} pm qm pn qn i =
  let pp = pcompcauchy pm pn in
  let qq = pcompcauchy qm qn in
  begin (
    lookup (lookup (raise m i) pp) qq
          ≡⟨ cong (flip lookup qq) (lookup-++-raise (mapV (inject+ n) pm) (mapV (raise m) pn) i) ⟩
    lookup (lookup i (mapV (raise m) pn)) qq
          ≡⟨ cong (flip lookup qq) (lookup-map i (raise m) pn) ⟩
    lookup (raise m (lookup i pn)) qq
         ≡⟨ lookup-right (lookup i pn) qm qn ⟩
    raise m (lookup (lookup i pn) qn) ∎)
  where open ≡-Reasoning

pcomp-dist : ∀ {m n} → (pm qm : Cauchy m) → (pn qn : Cauchy n) →
    scompcauchy (pcompcauchy pm pn) (pcompcauchy qm qn) ≡
    pcompcauchy (scompcauchy pm qm) (scompcauchy pn qn)
pcomp-dist {m} {n} pm qm pn qn =
  let pp = pcompcauchy pm pn in
  let qq = pcompcauchy qm qn in
  let look = λ i → lookup (lookup i pp) qq in
  begin (scompcauchy pp qq
           ≡⟨ refl ⟩
         tabulate look
           ≡⟨ tabulate-split {m} {n} ⟩
         splitV+ {m} {n} {f = look}
           ≡⟨ cong₂ _++V_ (finext {m} (look-left' pm qm pn qn))
                                      (finext {n} (look-right' pm qm pn qn)) ⟩
         tabulate (λ i → (inject+ n) (lookup (lookup i pm) qm)) ++V
         tabulate (λ i → (raise m) (lookup (lookup i pn) qn))
            ≡⟨ cong₂ _++V_
               (tabulate-∘ (inject+ n) (λ i → lookup (lookup i pm) qm))
               (tabulate-∘ (raise m) (λ i → lookup (lookup i pn) qn)) ⟩
         mapV (inject+ n) (tabulate (λ i → lookup (lookup i pm) qm)) ++V
         mapV (raise m) (tabulate (λ i → lookup (lookup i pn) qn))
            ≡⟨ refl ⟩
         pcompcauchy (scompcauchy pm qm) (scompcauchy pn qn) ∎)
  where open ≡-Reasoning

pcomp-id : ∀ {m n} → pcompcauchy (idcauchy m) (idcauchy n) ≡ idcauchy (m + n)
pcomp-id {m} {n} =
  begin (mapV (inject+ n) (idcauchy m) ++V (mapV (raise m) (idcauchy n))
    ≡⟨ refl ⟩
  mapV (inject+ n) (allFin m) ++V mapV (raise m) (allFin n)
    ≡⟨ cong₂ _++V_
       (sym (tabulate-allFin {m} (inject+ n)))
       (sym (tabulate-allFin (raise m))) ⟩
  tabulate {m} (inject+ n) ++V tabulate {n} (raise m)
    ≡⟨ unSplit {m} {n} id ⟩
  idcauchy (m + n) ∎)
  where open ≡-Reasoning

------------------------------------------------------------------------------


module Data.QuadTree.LensProofs.Valid-LensWrappedTree where

open import Haskell.Prelude renaming (zero to Z; suc to S)
open import Data.Lens.Lens
open import Data.Logic
open import Data.QuadTree.InternalAgda
open import Agda.Primitive
open import Data.Lens.Proofs.LensLaws
open import Data.Lens.Proofs.LensPostulates
open import Data.Lens.Proofs.LensComposition
open import Data.QuadTree.Implementation.QuadrantLenses
open import Data.QuadTree.Implementation.Definition
open import Data.QuadTree.Implementation.ValidTypes
open import Data.QuadTree.Implementation.SafeFunctions
open import Data.QuadTree.Implementation.PublicFunctions
open import Data.QuadTree.Implementation.DataLenses

---- Lens laws for lensWrappedTree

ValidLens-WrappedTree-ViewSet :
    {t : Set} {{eqT : Eq t}} {dep : Nat}
    -> ViewSet (lensWrappedTree {t} {dep})
ValidLens-WrappedTree-ViewSet (CVQuadrant qdi) (CVQuadTree (Wrapper (w , h) qdo)) = refl

ValidLens-WrappedTree-SetView :
    {t : Set} {{eqT : Eq t}} {dep : Nat}
    -> SetView (lensWrappedTree {t} {dep})
ValidLens-WrappedTree-SetView (CVQuadTree (Wrapper (w , h) qdo)) = refl

ValidLens-WrappedTree-SetSet :
    {t : Set} {{eqT : Eq t}} {dep : Nat}
    -> SetSet (lensWrappedTree {t} {dep})
ValidLens-WrappedTree-SetSet (CVQuadrant qd1) (CVQuadrant qd2) (CVQuadTree (Wrapper (w , h) qdo)) = refl

ValidLens-WrappedTree :
    {t : Set} {{eqT : Eq t}} {dep : Nat}
    -> ValidLens (VQuadTree t {dep}) (VQuadrant t {dep})
ValidLens-WrappedTree = CValidLens lensWrappedTree (ValidLens-WrappedTree-ViewSet) (ValidLens-WrappedTree-SetView) (ValidLens-WrappedTree-SetSet)


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

open import Definition.Typed.EqualityRelation

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

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.Typed.Properties
open import Definition.LogicalRelation

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

-- Reducible types are well-formed.
escape : ∀ {l Γ A} → Γ ⊩⟨ l ⟩ A → Γ ⊢ A
escape (Uᵣ′ l′ l< ⊢Γ) = Uⱼ ⊢Γ
escape (ℕᵣ [ ⊢A , ⊢B , D ]) = ⊢A
escape (ne′ K [ ⊢A , ⊢B , D ] neK K≡K) = ⊢A
escape (Πᵣ′ F G [ ⊢A , ⊢B , D ] ⊢F ⊢G A≡A [F] [G] G-ext) = ⊢A
escape (emb′ 0<1 [A]) = escape [A]

-- Reducible type equality respect the equality relation.
escapeEq : ∀ {l Γ A B} → ([A] : Γ ⊩⟨ l ⟩ A)
            → Γ ⊩⟨ l ⟩ A ≡ B / [A]
            → Γ ⊢ A ≅ B
escapeEq (Uᵣ′ l' l< ⊢Γ) (U₌ PE.refl) = ≅-Urefl ⊢Γ
escapeEq (ℕᵣ [ ⊢A , ⊢B , D ]) (ιx (ℕ₌ D′)) = ≅-red D D′ ℕₙ ℕₙ (≅-ℕrefl (wf ⊢A))
escapeEq (ne′ K D neK K≡K) (ιx (ne₌ M D′ neM K≡M)) = ≅-red (red D) (red D′) (ne neK) (ne neM) (~-to-≅ K≡M)
escapeEq (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
  ≅-red (red D) D′ Πₙ Πₙ A≡B
escapeEq (emb′ 0<1 A) (ιx A≡B) = escapeEq A A≡B

-- Reducible terms are well-formed.
escapeTerm : ∀ {l Γ A t} → ([A] : Γ ⊩⟨ l ⟩ A)
              → Γ ⊩⟨ l ⟩ t ∷ A / [A]
              → Γ ⊢ t ∷ A
escapeTerm (Uᵣ′ l′ l< ⊢Γ) (Uₜ A [ ⊢t , ⊢u , d ] typeA A≡A [A]) = ⊢t
escapeTerm (ℕᵣ D) (ιx (ℕₜ n [ ⊢t , ⊢u , d ] t≡t prop)) =
  conv ⊢t (sym (subset* (red D)))
escapeTerm (ne′ K D neK K≡K) (ιx (neₜ k [ ⊢t , ⊢u , d ] nf)) =
  conv ⊢t (sym (subset* (red D)))
escapeTerm (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
               (f , [ ⊢t , ⊢u , d ] , funcF , f≡f , [f] , [f]₁) =
  conv ⊢t (sym (subset* (red D)))
escapeTerm (emb′ 0<1 A) (ιx t) = escapeTerm A t

-- Reducible term equality respect the equality relation.
escapeTermEq : ∀ {l Γ A t u} → ([A] : Γ ⊩⟨ l ⟩ A)
                → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / [A]
                → Γ ⊢ t ≅ u ∷ A
escapeTermEq (Uᵣ′ l′ l< ⊢Γ) (Uₜ₌ A B d d′ typeA typeB A≡B [A] [B] [A≡B]) =
  ≅ₜ-red (id (Uⱼ ⊢Γ)) (redₜ d) (redₜ d′) Uₙ (typeWhnf typeA) (typeWhnf typeB) A≡B
escapeTermEq (ℕᵣ D) (ιx (ℕₜ₌ k k′ d d′ k≡k′ prop)) =
  let natK , natK′ = split prop
  in  ≅ₜ-red (red D) (redₜ d) (redₜ d′) ℕₙ
             (naturalWhnf natK) (naturalWhnf natK′) k≡k′
escapeTermEq (ne′ K D neK K≡K)
                 (ιx (neₜ₌ k m d d′ (neNfₜ₌ neT neU t≡u))) =
  ≅ₜ-red (red D) (redₜ d) (redₜ d′) (ne neK) (ne neT) (ne neU)
         (~-to-≅ₜ t≡u)
escapeTermEq (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                 (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g]) =
  ≅ₜ-red (red D) (redₜ d) (redₜ d′) Πₙ (functionWhnf funcF) (functionWhnf funcG) f≡g
escapeTermEq (emb′ 0<1 A) (ιx t≡u) = escapeTermEq A t≡u


------------------------------------------------------------------------
-- An example showing that transitivity-like proofs that are not
-- size-preserving can sometimes be used in a compositional way
------------------------------------------------------------------------

-- One can use the technique from "Beating the Productivity Checker
-- Using Embedded Languages" to make it possible to use
-- transitivity-like proofs that are not size-preserving in
-- corecursive definitions. However, it is unclear if this is ever
-- useful: the example presented below is rather contrived.

{-# OPTIONS --sized-types #-}

open import Prelude

module Delay-monad.Bisimilarity.Transitivity-constructor
  {a} {A : Type a} where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude.Size

open import Function-universe equality-with-J hiding (Kind)

open import Delay-monad
open import Delay-monad.Bisimilarity hiding (never≉now)
open import Delay-monad.Bisimilarity.Negative

------------------------------------------------------------------------
-- Proof programs

-- There are two kinds of proof "programs", corresponding to strong
-- and weak bisimilarity.

data Kind : Type where
  strong weak : Kind

mutual

  -- Proof "programs".

  data Prog (i : Size) : Kind → Delay A ∞ → Delay A ∞ → Type a where

    -- Congruences.

    now   : ∀ {k x} → Prog i k (now x) (now x)
    later : ∀ {k x y} →
            Prog′ i k (force x) (force y) →
            Prog i k (later x) (later y)

    -- Weak bisimilarity.

    laterʳ : ∀ {x y} → Prog i weak x (force y) → Prog i weak x (later y)
    laterˡ : ∀ {x y} → Prog i weak (force x) y → Prog i weak (later x) y

    -- Equational reasoning.
    --
    -- Note that if A is inhabited, then there is no size-preserving
    -- transitivity-like proof taking strong bisimilarity and weak
    -- bisimilarity to weak bisimilarity (see
    -- Delay-monad.Bisimilarity.Negative.size-preserving-transitivity-∼≈ˡ⇔uninhabited).

    reflP  : ∀ {k} x → Prog i k x x
    symP   : ∀ {k x y} → Prog i k x y → Prog i k y x
    transP : ∀ {k x y z} →
             Prog i strong x y → Prog i k y z → Prog i k x z

  record Prog′ (i : Size) (k : Kind) (x y : Delay A ∞) : Type a where
    coinductive
    field
      force : {j : Size< i} → Prog j k x y

open Prog′ public

------------------------------------------------------------------------
-- Completeness

-- Proof programs are complete with respect to strong and weak
-- bisimilarity. Note that these proofs are size-preserving.

complete-strong : ∀ {i x y} →
                  [ i ] x ∼ y → Prog i strong x y
complete-strong now       = now
complete-strong (later p) =
  later λ { .force → complete-strong (force p) }

complete-weak : ∀ {i x y} → [ i ] x ≈ y → Prog i weak x y
complete-weak now        = now
complete-weak (laterʳ p) = laterʳ (complete-weak p)
complete-weak (laterˡ p) = laterˡ (complete-weak p)
complete-weak (later  p) =
  later λ { .force → complete-weak (force p) }

------------------------------------------------------------------------
-- Soundness

-- Proof WHNFs.

data WHNF (i : Size) : Kind → Delay A ∞ → Delay A ∞ → Type a where
  now    : ∀ {k x} → WHNF i k (now x) (now x)
  later  : ∀ {k x y} →
           Prog′ i k (force x) (force y) →
           WHNF i k (later x) (later y)
  laterʳ : ∀ {x y} →
           WHNF i weak x (force y) → WHNF i weak x (later y)
  laterˡ : ∀ {x y} →
           WHNF i weak (force x) y → WHNF i weak (later x) y

-- Reflexivity.

reflW : ∀ {i k} x → WHNF i k x x
reflW (now   x) = now
reflW (later x) = later λ { .force → reflP (force x) }

-- Symmetry.

symW : ∀ {i k x y} → WHNF i k x y → WHNF i k y x
symW now        = now
symW (later  p) = later λ { .force → symP (force p) }
symW (laterʳ p) = laterˡ (symW p)
symW (laterˡ p) = laterʳ (symW p)

-- Transitivity for strong WHNFs.

trans∼∼W : ∀ {i x y z} →
           WHNF i strong x y → WHNF i strong y z → WHNF i strong x z
trans∼∼W now       q         = q
trans∼∼W (later p) (later q) =
  later λ { .force → transP (force p) (force q) }

-- Strong equality programs can be turned into WHNFs.

whnf∼ : ∀ {i x y} → Prog i strong x y → WHNF i strong x y
whnf∼ now          = now
whnf∼ (later p)    = later p
whnf∼ (reflP x)    = reflW x
whnf∼ (symP p)     = symW (whnf∼ p)
whnf∼ (transP p q) = trans∼∼W (whnf∼ p) (whnf∼ q)

-- Strong proof programs are sound with respect to strong
-- bisimilarity. Note that these proofs are size-preserving.

mutual

  sound-strong : ∀ {i x y} →
                 Prog i strong x y → [ i ] x ∼ y
  sound-strong p = soundW-strong (whnf∼ p)

  soundW-strong : ∀ {i x y} →
                  WHNF i strong x y → [ i ] x ∼ y
  soundW-strong now       = now
  soundW-strong (later p) =
    later λ { .force → sound-strong (force p) }

-- Another transitivity lemma. This lemma cannot, in general, be made
-- fully size-preserving (see not-fully-size-preserving below).

trans∼-W : ∀ {i k x y z} →
           WHNF ∞ strong x y → WHNF i k y z → WHNF i k x z
trans∼-W now       q          = q
trans∼-W p         (laterʳ q) = laterʳ (trans∼-W p q)
trans∼-W (later p) (laterˡ q) = laterˡ (trans∼-W (whnf∼ (force p)) q)
trans∼-W (later p) (later  q) =
  later λ { .force → transP (force p) (force q) }

-- All fully defined programs can be turned into WHNFs.

whnf : ∀ {i k x y} → Prog ∞ k x y → WHNF i k x y
whnf now          = now
whnf (later p)    = later p
whnf (laterʳ p)   = laterʳ (whnf p)
whnf (laterˡ p)   = laterˡ (whnf p)
whnf (reflP x)    = reflW x
whnf (symP p)     = symW (whnf p)
whnf (transP p q) = trans∼-W (whnf p) (whnf q)

-- Weak proof programs are sound with respect to weak bisimilarity.
-- Note that these proofs are /not/ guaranteed to be size-preserving.

mutual

  sound-weak : ∀ {i x y} → Prog ∞ weak x y → [ i ] x ≈ y
  sound-weak p = soundW-weak (whnf p)

  soundW-weak : ∀ {i x y} → WHNF ∞ weak x y → [ i ] x ≈ y
  soundW-weak now        = now
  soundW-weak (laterʳ p) = laterʳ  (soundW-weak p)
  soundW-weak (laterˡ p) = laterˡ  (soundW-weak p)
  soundW-weak (later  p) =
    later λ { .force → sound-weak (force p) }

------------------------------------------------------------------------
-- Some negative results

-- The soundness proof for weak proof programs can be made
-- size-preserving iff A is uninhabited.

size-preserving⇔uninhabited :
  (∀ {i x y} → Prog i weak x y → [ i ] x ≈ y) ⇔ ¬ A
size-preserving⇔uninhabited = record
  { to   = (∀ {i x y} → Prog i weak x y → [ i ] x ≈ y)  ↝⟨ (λ sound p q → sound (transP (complete-strong p) (complete-weak q))) ⟩
           Transitivity-∼≈ˡ                             ↝⟨ _⇔_.to size-preserving-transitivity-∼≈ˡ⇔uninhabited ⟩□
           ¬ A                                          □
  ; from = ¬ A                                          ↝⟨ uninhabited→trivial ⟩
           (∀ x y → x ≈ y)                              ↝⟨ (λ trivial {_ _ _} _ → trivial _ _) ⟩□
           (∀ {i x y} → Prog i weak x y → [ i ] x ≈ y)  □
  }

-- The lemma trans∼-W cannot be made fully size-preserving (assuming
-- that A is inhabited).

not-fully-size-preserving :
  (∀ {i k x y z} →
   WHNF i strong x y → WHNF i k y z → WHNF i k x z) →
  ¬ A
not-fully-size-preserving trans x = contradiction ∞
  where

  never≉now : ∀ {i} → ¬ WHNF i weak never (now x)
  never≉now (laterˡ p) = never≉now p

  mutual

    never≈now : ∀ {i} → WHNF i weak never (now x)
    never≈now =
      trans (later {y = record { force = now x }} never∼now)
            (laterˡ now)

    never∼now : ∀ {i} → Prog′ i strong never (now x)
    force never∼now {j = j} = ⊥-elim (contradiction j)

    contradiction : Size → ⊥
    contradiction i = never≉now (never≈now {i = i})

-- One might wonder why the counterexample above cannot be adapted so
-- that it also applies to programs, for which a size-preserving
-- transitivity constructor exists. The following lemma documents one
-- attempt at adapting the counterexample. Note that the new
-- counterexample is based on the assumption that one can convert a
-- strong bisimilarity program /of any size/ relating never and x to a
-- proof that x is /equal/ to never.

partially-adapted-counterexample :
  (∀ {i x} → Prog i strong never x → x ≡ never) →
  ¬ A
partially-adapted-counterexample convert x = contradiction ∞
  where
  mutual

    now∼→≡now : ∀ {i x y} → Prog i strong (now x) y → y ≡ now x
    now∼→≡now now          = refl
    now∼→≡now (reflP _)    = refl
    now∼→≡now (symP p)     = ∼now→≡now p
    now∼→≡now (transP p q) rewrite now∼→≡now p = now∼→≡now q

    ∼now→≡now : ∀ {i x y} → Prog i strong x (now y) → x ≡ now y
    ∼now→≡now now          = refl
    ∼now→≡now (reflP _)    = refl
    ∼now→≡now (symP p)     = now∼→≡now p
    ∼now→≡now (transP p q) rewrite ∼now→≡now q = ∼now→≡now p

  mutual

    now≉Pnever : ∀ {i} → ¬ Prog i weak (now x) never
    now≉Pnever (laterʳ p)   = now≉Pnever p
    now≉Pnever (symP p)     = never≉Pnow p
    now≉Pnever (transP p q) rewrite now∼→≡now p = now≉Pnever q

    never≉Pnow : ∀ {i} → ¬ Prog i weak never (now x)
    never≉Pnow (laterˡ p)   = never≉Pnow p
    never≉Pnow (symP p)     = now≉Pnever p
    never≉Pnow (transP p q) rewrite convert p = never≉Pnow q

  mutual

    never≈Pnow : ∀ {i} → Prog i weak never (now x)
    never≈Pnow =
      transP (later {y = record { force = now x }} never∼Pnow)
             (laterˡ now)

    never∼Pnow : ∀ {i} → Prog′ i strong never (now x)
    force never∼Pnow {j = j} = ⊥-elim (contradiction j)

    contradiction : Size → ⊥
    contradiction i = never≉Pnow (never≈Pnow {i = i})

------------------------------------------------------------------------
-- A contrived example

-- Note the use of a strong bisimilarity program of size i as the
-- first argument to transitivity in the following example; this is
-- not supported by transitive-∼ˡ, which requires the first argument
-- to have size ∞.

exampleP : ∀ {i} → Prog i weak never never
exampleP {i} =
  transP (reflP {i = i} never) (later λ { .force → exampleP })

example : ∀ {i} → [ i ] never ≈ never
example = sound-weak exampleP

-- However, note that the first argument could just as well have been
-- given the size ∞, in which case transitive-∼ˡ works:

counterargument : ∀ {i} → [ i ] never ≈ never {A = A}
counterargument =
  transitive-∼ˡ (reflexive never)
                (later λ { .force → counterargument })

-- Are there any applications in which strong and weak bisimilarities
-- are proved simultaneously? One can observe that, if the first
-- argument is never, then weak bisimilarity is contained in strong
-- (in a size-preserving way), as witnessed by →∼-neverˡ. Perhaps one
-- can find some compelling application of the technique presented
-- above if it can be combined with a lemma like →∼-neverˡ. However,
-- it is unclear if such a combination is possible: the soundness
-- proof above relies on the fact that strong bisimilarity programs
-- contain no weak bisimilarity programs (in particular, no
-- transitivity proofs with a weak bisimilarity as the second
-- argument).


------------------------------------------------------------------------------
-- All the Peano arithmetic modules
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module PA.Everything where

open import PA.Axiomatic.Mendelson.Base
open import PA.Axiomatic.Mendelson.Base.Consistency.Axioms
open import PA.Axiomatic.Mendelson.PropertiesATP
open import PA.Axiomatic.Mendelson.PropertiesI
open import PA.Axiomatic.Mendelson.Relation.Binary.EqReasoning
open import PA.Axiomatic.Mendelson.Relation.Binary.PropositionalEqualityI
open import PA.Axiomatic.Mendelson.Relation.Binary.PropositionalEqualityATP

open import PA.Axiomatic.Standard.Base
open import PA.Axiomatic.Standard.Base.Consistency.Axioms
open import PA.Axiomatic.Standard.PropertiesATP
open import PA.Axiomatic.Standard.PropertiesI

open import PA.Inductive.Base
open import PA.Inductive.Base.Core
open import PA.Inductive.Existential
open import PA.Inductive.PropertiesATP
open import PA.Inductive.PropertiesI
open import PA.Inductive.PropertiesByInduction
open import PA.Inductive.PropertiesByInductionATP
open import PA.Inductive.PropertiesByInductionI
open import PA.Inductive.Relation.Binary.EqReasoning
open import PA.Inductive.Relation.Binary.PropositionalEquality

open import PA.Inductive2Mendelson
open import PA.Inductive2Standard


------------------------------------------------------------------------
-- A parametrised coinductive definition that can be used to define
-- various forms of similarity
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Prelude

open import Labelled-transition-system

module Similarity.General
         {ℓ}
         (lts : LTS ℓ)
         (open LTS lts)
         (_[_]↝_ : Proc → Label → Proc → Type ℓ)
         (⟶→↝ : ∀ {p μ q} → p [ μ ]⟶ q → p [ μ ]↝ q)
         where

open import Equality.Propositional as Eq hiding (Extensionality)
open import Prelude.Size

open import Bijection equality-with-J using (_↔_)
open import Function-universe equality-with-J hiding (id; _∘_)

open import Indexed-container hiding (⟨_⟩)
open import Relation
open import Similarity.Step lts _[_]↝_ as Step public
  using (StepC)

open Indexed-container public using (force)
open StepC public using (⟨_⟩; challenge)

-- Similarity. Note that this definition is small.

infix 4 _≤_ _≤′_ [_]_≤_ [_]_≤′_

Similarity : Size → Rel₂ ℓ Proc
Similarity i = ν StepC i

Similarity′ : Size → Rel₂ ℓ Proc
Similarity′ i = ν′ StepC i

[_]_≤_ : Size → Proc → Proc → Type ℓ
[_]_≤_ i = curry (Similarity i)

[_]_≤′_ : Size → Proc → Proc → Type ℓ
[_]_≤′_ i = curry (Similarity′ i)

_≤_ : Proc → Proc → Type ℓ
_≤_ = [ ∞ ]_≤_

_≤′_ : Proc → Proc → Type ℓ
_≤′_ = [ ∞ ]_≤′_

-- Similarity is reflexive.

mutual

  reflexive-≤ : ∀ {p i} → [ i ] p ≤ p
  reflexive-≤ =
    StepC.⟨ (λ p⟶p′ → _ , ⟶→↝ p⟶p′ , reflexive-≤′)
          ⟩

  reflexive-≤′ : ∀ {p i} → [ i ] p ≤′ p
  force reflexive-≤′ = reflexive-≤

≡⇒≤ : ∀ {p q} → p ≡ q → p ≤ q
≡⇒≤ refl = reflexive-≤

-- Functions that can be used to aid the instance resolution
-- mechanism.

infix -2 ≤:_ ≤′:_

≤:_ : ∀ {i p q} → [ i ] p ≤ q → [ i ] p ≤ q
≤:_ = id

≤′:_ : ∀ {i p q} → [ i ] p ≤′ q → [ i ] p ≤′ q
≤′:_ = id

-- Bisimilarity of similarity proofs.

infix 4 [_]_≡_ [_]_≡′_

[_]_≡_ : ∀ {p q} → Size → (_ _ : ν StepC ∞ (p , q)) → Type ℓ
[_]_≡_ i = curry (ν-bisimilar i)

[_]_≡′_ : ∀ {p q} → Size → (_ _ : ν′ StepC ∞ (p , q)) → Type ℓ
[_]_≡′_ i = curry (ν′-bisimilar i)

-- An alternative characterisation of bisimilarity of similarity
-- proofs.

[]≡↔ :
  Eq.Extensionality ℓ ℓ →
  ∀ {p q} {i : Size} (p≤q₁ p≤q₂ : ν StepC ∞ (p , q)) →

  [ i ] p≤q₁ ≡ p≤q₂

    ↔

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′≤q′₁ = StepC.challenge p≤q₁ p⟶p′
       q′₂ , q⟶q′₂ , p′≤q′₂ = StepC.challenge p≤q₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′≤q′₁ ≡′ p′≤q′₂)

[]≡↔ ext {p} {q} {i} p≤q₁@(s₁ , f₁) p≤q₂@(s₂ , f₂) =
  [ i ] p≤q₁ ≡ p≤q₂                                                     ↝⟨ ν-bisimilar↔ ext (s₁ , f₁) (s₂ , f₂) ⟩

  (∃ λ (eq : s₁ ≡ s₂) →
   ∀ {o} (p : Container.Position StepC s₁ o) →
   [ i ] f₁ p ≡′ f₂ (subst (λ s → Container.Position StepC s o) eq p))  ↝⟨ Step.⟦StepC⟧₂↔ ext (ν′-bisimilar i) p≤q₁ p≤q₂ ⟩□

  (∀ {p′ μ} (p⟶p′ : p [ μ ]⟶ p′) →
   let q′₁ , q⟶q′₁ , p′≤q′₁ = StepC.challenge p≤q₁ p⟶p′
       q′₂ , q⟶q′₂ , p′≤q′₂ = StepC.challenge p≤q₂ p⟶p′
   in ∃ λ (q′₁≡q′₂ : q′₁ ≡ q′₂) →
        subst (q [ μ ]↝_) q′₁≡q′₂ q⟶q′₁ ≡ q⟶q′₂
          ×
        [ i ] subst (ν′ StepC ∞ ∘ (p′ ,_)) q′₁≡q′₂ p′≤q′₁ ≡′ p′≤q′₂)    □

-- A statement of extensionality for similarity.

Extensionality : Type ℓ
Extensionality = ν′-extensionality StepC

-- This form of extensionality can be used to derive another form (in
-- the presence of extensionality for functions).

extensionality :
  Eq.Extensionality ℓ ℓ →
  Extensionality →
  ∀ {p q} {p≤q₁ p≤q₂ : ν StepC ∞ (p , q)} →
  [ ∞ ] p≤q₁ ≡ p≤q₂ → p≤q₁ ≡ p≤q₂
extensionality ext ν-ext = ν-extensionality ext ν-ext