-- We define ZigZag-complete relations and prove that quasi equivalence relations
-- give rise to equivalences on the set quotients.
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Relation.ZigZag.Base where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels
open import Cubical.Data.Sigma
open import Cubical.HITs.SetQuotients
open import Cubical.HITs.PropositionalTruncation as Trunc
open import Cubical.Relation.Binary.Base

open isEquivRel

private
 variable
  ℓ ℓ' : Level

isZigZagComplete : {A B : Type ℓ} (R : A → B → Type ℓ') → Type (ℓ-max ℓ ℓ')
isZigZagComplete R = ∀ {a b a' b'} → R a b → R a' b → R a' b' → R a b'

ZigZagRel : (A B : Type ℓ) (ℓ' : Level) → Type (ℓ-max ℓ (ℓ-suc ℓ'))
ZigZagRel A B ℓ' = Σ[ R ∈ (A → B → Type ℓ') ] (isZigZagComplete R)

record isQuasiEquivRel {A B : Type ℓ} (R : A → B → Type ℓ') : Type (ℓ-max ℓ ℓ') where
  field
    zigzag : isZigZagComplete R
    fwd : (a : A) → ∃[ b ∈ B ] R a b
    bwd : (b : B) → ∃[ a ∈ A ] R a b

open isQuasiEquivRel

QuasiEquivRel : (A B : Type ℓ) (ℓ' : Level) → Type (ℓ-max ℓ (ℓ-suc ℓ'))
QuasiEquivRel A B ℓ' =
  Σ[ R ∈ PropRel A B ℓ' ] isQuasiEquivRel (R .fst)

invQER : {A B : Type ℓ} {ℓ' : Level} → QuasiEquivRel A B ℓ' → QuasiEquivRel B A ℓ'
invQER (R , qer) .fst = invPropRel R
invQER (R , qer) .snd .zigzag aRb aRb' a'Rb' = qer .zigzag a'Rb' aRb' aRb
invQER (R , qer) .snd .fwd = qer .bwd
invQER (R , qer) .snd .bwd = qer .fwd

QER→EquivRel : {A B : Type ℓ}
  → QuasiEquivRel A B ℓ' → EquivPropRel A (ℓ-max ℓ ℓ')
QER→EquivRel (R , sim) .fst = compPropRel R (invPropRel R)
QER→EquivRel (R , sim) .snd .reflexive a = Trunc.map (λ {(b , r) → b , r , r}) (sim .fwd a)
QER→EquivRel (R , sim) .snd .symmetric _ _ = Trunc.map (λ {(b , r₀ , r₁) → b , r₁ , r₀})
QER→EquivRel (R , sim) .snd .transitive _ _ _ =
  Trunc.map2 (λ {(b , r₀ , r₁) (b' , r₀' , r₁') → b , r₀ , sim .zigzag r₁' r₀' r₁})

-- The following result is due to Carlo Angiuli
module QER→Equiv {A B : Type ℓ} (R : QuasiEquivRel A B ℓ') where

  Rᴸ = QER→EquivRel R .fst .fst
  Rᴿ = QER→EquivRel (invQER R) .fst .fst

  private
    sim = R .snd

  private
    f : (a : A) → ∃[ b ∈ B ] R .fst .fst a b → B / Rᴿ
    f a =
      Trunc.rec→Set squash/
        ([_] ∘ fst)
        (λ {(b , r) (b' , r') → eq/ b b' ∣ a , r , r' ∣})

    fPath :
      (a₀ : A) (s₀ : ∃[ b ∈ B ] R .fst .fst a₀ b)
      (a₁ : A) (s₁ : ∃[ b ∈ B ] R .fst .fst a₁ b)
      → Rᴸ a₀ a₁
      → f a₀ s₀ ≡ f a₁ s₁
    fPath a₀ =
      Trunc.elim (λ _ → isPropΠ3 λ _ _ _ → squash/ _ _)
        (λ {(b₀ , r₀) a₁ →
          Trunc.elim (λ _ → isPropΠ λ _ → squash/ _ _)
            (λ {(b₁ , r₁) →
              Trunc.elim (λ _ → squash/ _ _)
                (λ {(b' , r₀' , r₁') → eq/ b₀ b₁ ∣ a₀ , r₀ , sim .zigzag r₀' r₁' r₁ ∣})})})

    φ : A / Rᴸ → B / Rᴿ
    φ [ a ] = f a (sim .fwd a)
    φ (eq/ a₀ a₁ r i) =  fPath a₀ (sim .fwd a₀) a₁ (sim .fwd a₁) r i
    φ (squash/ _ _ p q j i) = squash/ _ _ (cong φ p) (cong φ q) j i

  relToFwd≡ : ∀ {a b} → R .fst .fst a b → φ [ a ] ≡ [ b ]
  relToFwd≡ {a} {b} r =
    Trunc.elim {P = λ s → f a s ≡ [ b ]} (λ _ → squash/ _ _)
      (λ {(b' , r') → eq/ b' b ∣ a , r' , r ∣})
      (sim .fwd a)

  fwd≡ToRel : ∀ {a b} → φ [ a ] ≡ [ b ] → R .fst .fst a b
  fwd≡ToRel {a} {b} =
    Trunc.elim {P = λ s → f a s ≡ [ b ] → R .fst .fst a b}
      (λ _ → isPropΠ λ _ → R .fst .snd _ _)
      (λ {(b' , r') p →
        Trunc.rec (R .fst .snd _ _)
          (λ {(a' , s' , s) → R .snd .zigzag r' s' s})
          (effective
            (QER→EquivRel (invQER R) .fst .snd)
            (QER→EquivRel (invQER R) .snd)
            b' b p)})
      (sim .fwd a)

  private
    g : (b : B) → ∃[ a ∈ A ] R .fst .fst a b → A / Rᴸ
    g b =
      Trunc.rec→Set squash/
        ([_] ∘ fst)
        (λ {(a , r) (a' , r') → eq/ a a' ∣ b , r , r' ∣})

    gPath :
      (b₀ : B) (s₀ : ∃[ a ∈ A ] R .fst .fst a b₀)
      (b₁ : B) (s₁ : ∃[ a ∈ A ] R .fst .fst a b₁)
      → Rᴿ b₀ b₁
      → g b₀ s₀ ≡ g b₁ s₁
    gPath b₀ =
      Trunc.elim (λ _ → isPropΠ3 λ _ _ _ → squash/ _ _)
        (λ {(a₀ , r₀) b₁ →
          Trunc.elim (λ _ → isPropΠ λ _ → squash/ _ _)
            (λ {(a₁ , r₁) →
              Trunc.elim (λ _ → squash/ _ _)
                (λ {(a' , r₀' , r₁') → eq/ a₀ a₁ ∣ b₀ , r₀ , sim .zigzag r₁ r₁' r₀' ∣})})})

    ψ : B / Rᴿ → A / Rᴸ
    ψ [ b ] = g b (sim .bwd b)
    ψ (eq/ b₀ b₁ r i) = gPath b₀ (sim .bwd b₀) b₁ (sim .bwd b₁) r i
    ψ (squash/ _ _ p q j i) = squash/ _ _ (cong ψ p) (cong ψ q) j i

  relToBwd≡ : ∀ {a b} → R .fst .fst a b → ψ [ b ] ≡ [ a ]
  relToBwd≡ {a} {b} r =
    Trunc.elim {P = λ s → g b s ≡ [ a ]} (λ _ → squash/ _ _)
      (λ {(a' , r') → eq/ a' a ∣ b , r' , r ∣})
      (sim .bwd b)

  private
    η : ∀ qb → φ (ψ qb) ≡ qb
    η =
      elimProp (λ _ → squash/ _ _)
        (λ b →
          Trunc.elim {P = λ s → φ (g b s) ≡ [ b ]} (λ _ → squash/ _ _)
            (λ {(a , r) → relToFwd≡ r})
            (sim .bwd b))

    ε : ∀ qa → ψ (φ qa) ≡ qa
    ε =
      elimProp (λ _ → squash/ _ _)
        (λ a →
          Trunc.elim {P = λ s → ψ (f a s) ≡ [ a ]} (λ _ → squash/ _ _)
            (λ {(b , r) → relToBwd≡ r})
            (sim .fwd a))

  bwd≡ToRel : ∀ {a b} → ψ [ b ] ≡ [ a ] → R .fst .fst a b
  bwd≡ToRel {a} {b} p = fwd≡ToRel (cong φ (sym p) ∙ η [ b ])

  Thm : (A / Rᴸ) ≃ (B / Rᴿ)
  Thm = isoToEquiv (iso φ ψ η ε)


open import MLib.Algebra.PropertyCode
open import MLib.Algebra.PropertyCode.Structures

module MLib.Matrix.Mul {c ℓ} (struct : Struct bimonoidCode c ℓ) where

open import MLib.Prelude
open import MLib.Matrix.Core
open import MLib.Matrix.Equality struct
open import MLib.Matrix.Plus struct
open import MLib.Algebra.Operations struct

open FunctionProperties
open Table using (head; tail; rearrange; fromList; toList; _≗_)

1● : ∀ {n} → Matrix S n n
1● i j with i Fin.≟ j
1● i j | yes _ = 1′
1● i j | no  _ = 0′

infixl 7 _⊗_

_⊗_ : ∀ {m n o} → Matrix S m n → Matrix S n o → Matrix S m o
_⊗_ {n = n} A B i k = ∑[ j < n ] (A i j *′ B j k)

⊗-cong : ∀ {m n o} {A A′ : Matrix S m n} {B B′ : Matrix S n o} → A ≈ A′ → B ≈ B′ → (A ⊗ B) ≈ (A′ ⊗ B′)
⊗-cong A≈A′ B≈B′ i k = sumₜ-cong (λ j → cong * (A≈A′ i j) (B≈B′ j k))

open _≃_

1●-cong-≃ : ∀ {m n} → m ≡ n → 1● {m} ≃ 1● {n}
1●-cong-≃ ≡.refl = ≃-refl

⊗-cong-≃ : ∀ {m n o m′ n′ o′} (A : Matrix S m n) (B : Matrix S n o) (A′ : Matrix S m′ n′) (B′ : Matrix S n′ o′) → A ≃ A′ → B ≃ B′ → (A ⊗ B) ≃ (A′ ⊗ B′)
⊗-cong-≃ A B A′ B′ p q .m≡p = p .m≡p
⊗-cong-≃ A B A′ B′ p q .n≡q = q .n≡q
⊗-cong-≃ {n = n} {n′ = n′} A B A′ B′ p q .equal {i} {i′} {k} {k′} i≅i′ k≅k′ =
  begin
    (A  ⊗ B ) i k                       ≡⟨⟩
    ∑[ j  < n  ] (A  i  j  *′ B  j  k)  ≈⟨ sumₜ-cong′ {n} {n′} (p .n≡q , λ _ _ j≅j′ → cong * (p .equal i≅i′ j≅j′) (q .equal j≅j′ k≅k′)) ⟩
    ∑[ j′ < n′ ] (A′ i′ j′ *′ B′ j′ k′) ≡⟨⟩
    (A′ ⊗ B′) i′ k′                     ∎
  where
    open EqReasoning S.setoid

⊗-assoc :
  ⦃ props : Has ( * ⟨ distributesOverˡ ⟩ₚ +
                ∷ * ⟨ distributesOverʳ ⟩ₚ +
                ∷ associative on *
                ∷ 0# is leftZero for *
                ∷ 0# is rightZero for *
                ∷ 0# is leftIdentity for +
                ∷ associative on +
                ∷ commutative on +
                ∷ []) ⦄
  → ∀ {m n p q} (A : Matrix S m n) (B : Matrix S n p) (C : Matrix S p q)
  → ((A ⊗ B) ⊗ C) ≈ (A ⊗ (B ⊗ C))
⊗-assoc ⦃ props ⦄ {m} {n} {p} {q} A B C i l =
  begin
    ((A ⊗ B) ⊗ C) i l                                    ≡⟨⟩
    ∑[ k < p ] (∑[ j < n ] (A i j *′ B j k) *′ C k l)     ≈⟨ sumₜ-cong {p} (λ k → sumDistribʳ ⦃ weaken props ⦄ {n} _ _) ⟩
    ∑[ k < p ] (∑[ j < n ] ((A i j *′ B j k) *′ C k l))   ≈⟨ sumₜ-cong {p} (λ k → sumₜ-cong {n} (λ j → from props (associative on *) _ _ _)) ⟩
    ∑[ k < p ] (∑[ j < n ] (A i j *′ (B j k *′ C k l)))   ≈⟨ ∑-comm ⦃ weaken props ⦄ p n _ ⟩
    ∑[ j < n ] (∑[ k < p ] (A i j *′ (B j k *′ C k l)))   ≈⟨ sumₜ-cong {n} (λ j → S.sym (sumDistribˡ ⦃ weaken props ⦄ {p} _ _)) ⟩
    ∑[ j < n ] (A i j *′ ∑[ k < p ] (B j k *′ C k l))     ≡⟨⟩
    (A ⊗ (B ⊗ C)) i l                                    ∎
  where open EqReasoning S.setoid

1-*-comm :
  ⦃ props : Has (1# is leftIdentity for * ∷ 1# is rightIdentity for * ∷ 0# is leftZero for * ∷ 0# is rightZero for * ∷ []) ⦄ →
  ∀ {n} (i j : Fin n) x → 1● i j *′ x ≈′ x *′ 1● i j
1-*-comm i j x with i Fin.≟ j
1-*-comm ⦃ props ⦄ i j x | yes p = S.trans (from props (1# is leftIdentity for *) _) (S.sym (from props (1# is rightIdentity for *) _))
1-*-comm ⦃ props ⦄ i j x | no ¬p = S.trans (from props (0# is leftZero for *) _) (S.sym (from props (0# is rightZero for *) _))

1-selectˡ :
  ⦃ props : Has (1# is leftIdentity for * ∷ 0# is leftZero for * ∷ []) ⦄ →
  ∀ {n} (i : Fin n) t j → (1● i j *′ lookup t j) ≈′ lookup (Table.select 0′ i t) j
1-selectˡ i t j with i Fin.≟ j | j Fin.≟ i
1-selectˡ ⦃ props ⦄ i t .i | yes ≡.refl | yes q = from props (1# is leftIdentity for *) _
1-selectˡ .j t j | yes ≡.refl | no ¬q = ⊥-elim (¬q ≡.refl)
1-selectˡ i t .i | no ¬p | yes ≡.refl = ⊥-elim (¬p ≡.refl)
1-selectˡ ⦃ props ⦄ i t j | no _ | no ¬p = from props (0# is leftZero for *) _

1-selectʳ :
  ⦃ props : Has (1# is rightIdentity for * ∷ 0# is rightZero for * ∷ []) ⦄ →
  ∀ {n} (i : Fin n) t j → (lookup t j *′ 1● j i) ≈′ lookup (Table.select 0′ i t) j
1-selectʳ i t j with i Fin.≟ j | j Fin.≟ i
1-selectʳ ⦃ props ⦄ i t .i | yes ≡.refl | yes q = from props (1# is rightIdentity for *) _
1-selectʳ .j t j | yes ≡.refl | no ¬q = ⊥-elim (¬q ≡.refl)
1-selectʳ i t .i | no ¬p | yes ≡.refl = ⊥-elim (¬p ≡.refl)
1-selectʳ ⦃ props ⦄ i t j | no _ | no ¬p = from props (0# is rightZero for *) _

1-sym : ∀ {n} (i j : Fin n) → 1● i j ≈′ 1● j i
1-sym i j with i Fin.≟ j | j Fin.≟ i
1-sym i j | yes _ | yes _ = S.refl
1-sym i j | no _  | no _ = S.refl
1-sym i .i | yes ≡.refl | no ¬q = ⊥-elim (¬q ≡.refl)
1-sym i .i | no ¬p | yes ≡.refl = ⊥-elim (¬p ≡.refl)

⊗-identityˡ :
  ⦃ props : Has ( 1# is leftIdentity for *
                ∷ 0# is leftZero for *
                ∷ 0# is leftIdentity for +
                ∷ 0# is rightIdentity for +
                ∷ associative on +
                ∷ commutative on +
                ∷ []) ⦄ →
  ∀ {m n} → ∀ (A : Matrix S m n) → (1● ⊗ A) ≈ A
⊗-identityˡ ⦃ props ⦄ {m} A i k =
  begin
    ∑[ j < m ] (1● i j *′ A j k)                        ≈⟨ sumₜ-cong (1-selectˡ ⦃ weaken props ⦄ i _) ⟩
    sumₜ (Table.select 0′ i (tabulate (λ x → A x k)))   ≈⟨ select-sum ⦃ weaken props ⦄ {m} _ ⟩
    A i k                                               ∎
  where open EqReasoning S.setoid

⊗-identityʳ :
  ⦃ props : Has ( 1# is rightIdentity for *
                ∷ 0# is rightZero for *
                ∷ 0# is leftIdentity for +
                ∷ 0# is rightIdentity for +
                ∷ associative on +
                ∷ commutative on +
                ∷ []) ⦄ →
  ∀ {m n} → ∀ (A : Matrix S m n) → (A ⊗ 1●) ≈ A
⊗-identityʳ ⦃ props ⦄ {n = n} A i k =
  begin
    ∑[ j < n ] (A i j *′ 1● j k)                ≈⟨ sumₜ-cong (1-selectʳ ⦃ weaken props ⦄ k _) ⟩
    sumₜ (Table.select 0′ k (tabulate (A i)))   ≈⟨ select-sum ⦃ weaken props ⦄ {n} _ ⟩
    A i k                                       ∎
  where open EqReasoning S.setoid

⊗-distributesOverˡ-⊕ :
  ⦃ props : Has (* ⟨ distributesOverˡ ⟩ₚ + ∷ 0# is leftIdentity for + ∷ associative on + ∷ commutative on + ∷ []) ⦄ →
  ∀ {m n o} (M : Matrix S m n) (A B : Matrix S n o) →
  M ⊗ (A ⊕ B) ≈ (M ⊗ A) ⊕ (M ⊗ B)
⊗-distributesOverˡ-⊕ ⦃ props ⦄ {n = n} M A B i k =
  begin
    ∑[ j < n ] (M i j *′ (A j k +′ B j k))                      ≈⟨ sumₜ-cong (λ j → from props (* ⟨ distributesOverˡ ⟩ₚ +) (M i j) (A j k) (B j k)) ⟩
    ∑[ j < n ] (M i j *′ A j k +′ M i j *′ B j k)               ≈⟨ S.sym (∑-+′-hom ⦃ weaken props ⦄ n (λ j → M i j *′ A j k) (λ j → M i j *′ B j k)) ⟩
    ∑[ j < n ] (M i j *′ A j k) +′ ∑[ j < n ] (M i j *′ B j k)  ∎
  where open EqReasoning S.setoid


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties related to All
------------------------------------------------------------------------

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

module Data.List.Relation.Unary.All.Properties where

open import Axiom.Extensionality.Propositional using (Extensionality)
open import Data.Bool.Base using (Bool; T)
open import Data.Bool.Properties
open import Data.Empty
open import Data.Fin using (Fin) renaming (zero to fzero; suc to fsuc)
open import Data.List.Base
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
open import Data.List.Relation.Unary.Any as Any using (Any; here; there)
import Data.List.Relation.Binary.Equality.Setoid as ListEq using (_≋_; []; _∷_)
open import Data.List.Relation.Binary.Pointwise using (Pointwise; []; _∷_)
open import Data.List.Relation.Binary.Subset.Propositional using (_⊆_)
open import Data.Maybe as Maybe using (Maybe; just; nothing)
open import Data.Maybe.Relation.Unary.All as MAll using (just; nothing)
open import Data.Nat using (zero; suc; z≤n; s≤s; _<_)
open import Data.Product as Prod using (_×_; _,_; uncurry; uncurry′)
open import Function
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; equivalence; Equivalence)
open import Function.Inverse using (_↔_; inverse)
open import Function.Surjection using (_↠_; surjection)
open import Relation.Binary using (Setoid; _Respects_)
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Unary
  using (Decidable; Pred; Universal) renaming (_⊆_ to _⋐_)

------------------------------------------------------------------------
-- Lemmas relating Any, All and negation.

module _ {a p} {A : Set a} {P : A → Set p} where

  ¬Any⇒All¬ : ∀ xs → ¬ Any P xs → All (¬_ ∘ P) xs
  ¬Any⇒All¬ []       ¬p = []
  ¬Any⇒All¬ (x ∷ xs) ¬p = ¬p ∘ here ∷ ¬Any⇒All¬ xs (¬p ∘ there)

  All¬⇒¬Any : ∀ {xs} → All (¬_ ∘ P) xs → ¬ Any P xs
  All¬⇒¬Any []        ()
  All¬⇒¬Any (¬p ∷ _)  (here  p) = ¬p p
  All¬⇒¬Any (_  ∷ ¬p) (there p) = All¬⇒¬Any ¬p p

  ¬All⇒Any¬ : Decidable P → ∀ xs → ¬ All P xs → Any (¬_ ∘ P) xs
  ¬All⇒Any¬ dec []       ¬∀ = ⊥-elim (¬∀ [])
  ¬All⇒Any¬ dec (x ∷ xs) ¬∀ with dec x
  ... | yes p = there (¬All⇒Any¬ dec xs (¬∀ ∘ _∷_ p))
  ... | no ¬p = here ¬p

  Any¬→¬All : ∀ {xs} → Any (¬_ ∘ P) xs → ¬ All P xs
  Any¬→¬All (here  ¬p) = ¬p           ∘ All.head
  Any¬→¬All (there ¬p) = Any¬→¬All ¬p ∘ All.tail

  ¬Any↠All¬ : ∀ {xs} → (¬ Any P xs) ↠ All (¬_ ∘ P) xs
  ¬Any↠All¬ = surjection (¬Any⇒All¬ _) All¬⇒¬Any to∘from
    where
    to∘from : ∀ {xs} (¬p : All (¬_ ∘ P) xs) → ¬Any⇒All¬ xs (All¬⇒¬Any ¬p) ≡ ¬p
    to∘from []         = P.refl
    to∘from (¬p ∷ ¬ps) = P.cong₂ _∷_ P.refl (to∘from ¬ps)

    -- If equality of functions were extensional, then the surjection
    -- could be strengthened to a bijection.

    from∘to : Extensionality _ _ →
              ∀ xs → (¬p : ¬ Any P xs) → All¬⇒¬Any (¬Any⇒All¬ xs ¬p) ≡ ¬p
    from∘to ext []       ¬p = ext λ ()
    from∘to ext (x ∷ xs) ¬p = ext λ
      { (here p)  → P.refl
      ; (there p) → P.cong (λ f → f p) $ from∘to ext xs (¬p ∘ there)
      }

  Any¬⇔¬All : ∀ {xs} → Decidable P → Any (¬_ ∘ P) xs ⇔ (¬ All P xs)
  Any¬⇔¬All dec = equivalence Any¬→¬All (¬All⇒Any¬ dec _)
    where
    -- If equality of functions were extensional, then the logical
    -- equivalence could be strengthened to a surjection.
    to∘from : Extensionality _ _ →
              ∀ {xs} (¬∀ : ¬ All P xs) → Any¬→¬All (¬All⇒Any¬ dec xs ¬∀) ≡ ¬∀
    to∘from ext ¬∀ = ext (⊥-elim ∘ ¬∀)

------------------------------------------------------------------------
-- Properties of operations over `All`
------------------------------------------------------------------------
-- map

module _ {a p q} {A : Set a} {P : Pred A p} {Q : Pred A q} {f : P ⋐ Q} where

  map-cong : ∀ {xs} {g : P ⋐ Q} (ps : All P xs) →
             (∀ {x} → f {x} P.≗ g) → All.map f ps ≡ All.map g ps
  map-cong []        _   = P.refl
  map-cong (px ∷ ps) feq = P.cong₂ _∷_ (feq px) (map-cong ps feq)

  map-id : ∀ {xs} (ps : All P xs) → All.map id ps ≡ ps
  map-id []        = P.refl
  map-id (px ∷ ps) = P.cong (px ∷_)  (map-id ps)

  map-compose : ∀ {r} {R : Pred A r} {xs} {g : Q ⋐ R} (ps : All P xs) →
                All.map g (All.map f ps) ≡ All.map (g ∘ f) ps
  map-compose []        = P.refl
  map-compose (px ∷ ps) = P.cong (_ ∷_) (map-compose ps)

  lookup-map : ∀ {xs x} (ps : All P xs) (i : x ∈ xs) →
               All.lookup (All.map f ps) i ≡ f (All.lookup ps i)
  lookup-map (px ∷ pxs) (here P.refl) = P.refl
  lookup-map (px ∷ pxs) (there i) = lookup-map pxs i

------------------------------------------------------------------------
-- _[_]%=_/updateAt

module _ {a p} {A : Set a} {P : Pred A p} where

  updateAt-updates : ∀ {x xs px} (pxs : All P xs) (i : x ∈ xs)
                     {f : P x → P x} → All.lookup pxs i ≡ px →
                     All.lookup (pxs All.[ i ]%= f) i ≡ f px
  updateAt-updates [] ()
  updateAt-updates (px ∷ pxs) (here P.refl) P.refl = P.refl
  updateAt-updates (px ∷ pxs) (there i) = updateAt-updates pxs i

  updateAt-cong : ∀ {x xs} (pxs : All P xs) (i : x ∈ xs)
                  {f g : P x → P x} →
                  f (All.lookup pxs i) ≡ g (All.lookup pxs i) →
                  (pxs All.[ i ]%= f) ≡ (pxs All.[ i ]%= g)
  updateAt-cong (px ∷ pxs) (here P.refl)     = P.cong (_∷ pxs)
  updateAt-cong (px ∷ pxs) (there i)     f≗g = P.cong (px ∷_) (updateAt-cong pxs i f≗g)

  updateAt-id : ∀ {x xs} (pxs : All P xs) (i : x ∈ xs) →
                (pxs All.[ i ]%= id) ≡ pxs
  updateAt-id (px ∷ pxs) (here P.refl) = P.refl
  updateAt-id (px ∷ pxs) (there i)     = P.cong (px ∷_) (updateAt-id pxs i)

  updateAt-compose : ∀ {x xs} (pxs : All P xs) (i : x ∈ xs) {f g : P x → P x} →
                     (pxs All.[ i ]%= f All.[ i ]%= g) ≡ pxs All.[ i ]%= (g ∘ f)
  updateAt-compose (px ∷ pxs) (here P.refl) = P.refl
  updateAt-compose (px ∷ pxs) (there i)     = P.cong (px ∷_) (updateAt-compose pxs i)

  map-updateAt : ∀ {q x} {Q : Pred A q} {xs} →
                 ∀ {f : P ⋐ Q} {g : P x → P x} {h : Q x → Q x}
                 (pxs : All P xs) (i : x ∈ xs) →
                 f (g (All.lookup pxs i)) ≡ h (f (All.lookup pxs i)) →
                 All.map f (pxs All.[ i ]%= g) ≡ (All.map f pxs) All.[ i ]%= h
  map-updateAt (px ∷ pxs) (here P.refl) = P.cong (_∷ _)
  map-updateAt (px ∷ pxs) (there i) feq = P.cong (_ ∷_) (map-updateAt pxs i feq)

------------------------------------------------------------------------
-- Introduction (⁺) and elimination (⁻) rules for list operations
------------------------------------------------------------------------
-- map

module _ {a b p} {A : Set a} {B : Set b} {P : B → Set p} {f : A → B} where

  map⁺ : ∀ {xs} → All (P ∘ f) xs → All P (map f xs)
  map⁺ []       = []
  map⁺ (p ∷ ps) = p ∷ map⁺ ps

  map⁻ : ∀ {xs} → All P (map f xs) → All (P ∘ f) xs
  map⁻ {xs = []}    []       = []
  map⁻ {xs = _ ∷ _} (p ∷ ps) = p ∷ map⁻ ps

-- A variant of All.map.

module _ {a b p q} {A : Set a} {B : Set b} {f : A → B}
         {P : A → Set p} {Q : B → Set q} where

  gmap : P ⋐ Q ∘ f → All P ⋐ All Q ∘ map f
  gmap g = map⁺ ∘ All.map g

------------------------------------------------------------------------
-- mapMaybe

module _ {a b p} {A : Set a} {B : Set b}
         (P : B → Set p) {f : A → Maybe B} where

  mapMaybe⁺ : ∀ {xs} → All (MAll.All P) (map f xs) → All P (mapMaybe f xs)
  mapMaybe⁺ {[]}     [] = []
  mapMaybe⁺ {x ∷ xs} (px ∷ pxs) with f x
  ... | nothing = mapMaybe⁺ pxs
  ... | just v with px
  ...   | just pv = pv ∷ mapMaybe⁺ pxs

------------------------------------------------------------------------
-- _++_

module _ {a p} {A : Set a} {P : A → Set p} where

  ++⁺ : ∀ {xs ys} → All P xs → All P ys → All P (xs ++ ys)
  ++⁺ []         pys = pys
  ++⁺ (px ∷ pxs) pys = px ∷ ++⁺ pxs pys

  ++⁻ˡ : ∀ xs {ys} → All P (xs ++ ys) → All P xs
  ++⁻ˡ []       p          = []
  ++⁻ˡ (x ∷ xs) (px ∷ pxs) = px ∷ (++⁻ˡ _ pxs)

  ++⁻ʳ : ∀ xs {ys} → All P (xs ++ ys) → All P ys
  ++⁻ʳ []       p          = p
  ++⁻ʳ (x ∷ xs) (px ∷ pxs) = ++⁻ʳ xs pxs

  ++⁻ : ∀ xs {ys} → All P (xs ++ ys) → All P xs × All P ys
  ++⁻ []       p          = [] , p
  ++⁻ (x ∷ xs) (px ∷ pxs) = Prod.map (px ∷_) id (++⁻ _ pxs)

  ++↔ : ∀ {xs ys} → (All P xs × All P ys) ↔ All P (xs ++ ys)
  ++↔ {xs} = inverse (uncurry ++⁺) (++⁻ xs) ++⁻∘++⁺ (++⁺∘++⁻ xs)
    where
    ++⁺∘++⁻ : ∀ xs {ys} (p : All P (xs ++ ys)) →
              uncurry′ ++⁺ (++⁻ xs p) ≡ p
    ++⁺∘++⁻ []       p          = P.refl
    ++⁺∘++⁻ (x ∷ xs) (px ∷ pxs) = P.cong (_∷_ px) $ ++⁺∘++⁻ xs pxs

    ++⁻∘++⁺ : ∀ {xs ys} (p : All P xs × All P ys) →
              ++⁻ xs (uncurry ++⁺ p) ≡ p
    ++⁻∘++⁺ ([]       , pys) = P.refl
    ++⁻∘++⁺ (px ∷ pxs , pys) rewrite ++⁻∘++⁺ (pxs , pys) = P.refl

------------------------------------------------------------------------
-- concat

module _ {a p} {A : Set a} {P : A → Set p} where

  concat⁺ : ∀ {xss} → All (All P) xss → All P (concat xss)
  concat⁺ []           = []
  concat⁺ (pxs ∷ pxss) = ++⁺ pxs (concat⁺ pxss)

  concat⁻ : ∀ {xss} → All P (concat xss) → All (All P) xss
  concat⁻ {[]}       []  = []
  concat⁻ {xs ∷ xss} pxs = ++⁻ˡ xs pxs ∷ concat⁻ (++⁻ʳ xs pxs)

------------------------------------------------------------------------
-- take and drop

module _ {a p} {A : Set a} {P : A → Set p} where

  drop⁺ : ∀ {xs} n → All P xs → All P (drop n xs)
  drop⁺ zero    pxs        = pxs
  drop⁺ (suc n) []         = []
  drop⁺ (suc n) (px ∷ pxs) = drop⁺ n pxs

  take⁺ : ∀ {xs} n → All P xs → All P (take n xs)
  take⁺ zero    pxs        = []
  take⁺ (suc n) []         = []
  take⁺ (suc n) (px ∷ pxs) = px ∷ take⁺ n pxs

------------------------------------------------------------------------
-- applyUpTo

module _ {a p} {A : Set a} {P : A → Set p} where

  applyUpTo⁺₁ : ∀ f n → (∀ {i} → i < n → P (f i)) → All P (applyUpTo f n)
  applyUpTo⁺₁ f zero    Pf = []
  applyUpTo⁺₁ f (suc n) Pf = Pf (s≤s z≤n) ∷ applyUpTo⁺₁ (f ∘ suc) n (Pf ∘ s≤s)

  applyUpTo⁺₂ : ∀ f n → (∀ i → P (f i)) → All P (applyUpTo f n)
  applyUpTo⁺₂ f n Pf = applyUpTo⁺₁ f n (λ _ → Pf _)

  applyUpTo⁻ : ∀ f n → All P (applyUpTo f n) → ∀ {i} → i < n → P (f i)
  applyUpTo⁻ f zero    pxs        ()
  applyUpTo⁻ f (suc n) (px ∷ _)   (s≤s z≤n)       = px
  applyUpTo⁻ f (suc n) (_  ∷ pxs) (s≤s (s≤s i<n)) =
    applyUpTo⁻ (f ∘ suc) n pxs (s≤s i<n)

------------------------------------------------------------------------
-- tabulate

module _ {a p} {A : Set a} {P : A → Set p} where

  tabulate⁺ : ∀ {n} {f : Fin n → A} →
              (∀ i → P (f i)) → All P (tabulate f)
  tabulate⁺ {zero}  Pf = []
  tabulate⁺ {suc n} Pf = Pf fzero ∷ tabulate⁺ (Pf ∘ fsuc)

  tabulate⁻ : ∀ {n} {f : Fin n → A} →
              All P (tabulate f) → (∀ i → P (f i))
  tabulate⁻ {zero}  pf       ()
  tabulate⁻ {suc n} (px ∷ _) fzero    = px
  tabulate⁻ {suc n} (_ ∷ pf) (fsuc i) = tabulate⁻ pf i

------------------------------------------------------------------------
-- remove

module _ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q} where

  ─⁺ : ∀ {xs} (p : Any P xs) → All Q xs → All Q (xs Any.─ p)
  ─⁺ (here px) (_ ∷ qs) = qs
  ─⁺ (there p) (q ∷ qs) = q ∷ ─⁺ p qs

  ─⁻ : ∀ {xs} (p : Any P xs) → Q (Any.lookup p) → All Q (xs Any.─ p) → All Q xs
  ─⁻ (here px) q qs        = q ∷ qs
  ─⁻ (there p) q (q′ ∷ qs) = q′ ∷ ─⁻ p q qs

------------------------------------------------------------------------
-- filter

module _ {a p} {A : Set a} {P : A → Set p} (P? : Decidable P) where

  all-filter : ∀ xs → All P (filter P? xs)
  all-filter []       = []
  all-filter (x ∷ xs) with P? x
  ... | yes Px = Px ∷ all-filter xs
  ... | no  _  = all-filter xs

  filter⁺ : ∀ {q} {Q : A → Set q} {xs} → All Q xs → All Q (filter P? xs)
  filter⁺ {xs = _}     [] = []
  filter⁺ {xs = x ∷ _} (Qx ∷ Qxs) with P? x
  ... | no  _ = filter⁺ Qxs
  ... | yes _ = Qx ∷ filter⁺ Qxs

------------------------------------------------------------------------
-- zipWith

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where

  zipWith⁺ : ∀ {p} (P : C → Set p) (f : A → B → C) {xs ys} →
             Pointwise (λ x y → P (f x y)) xs ys →
             All P (zipWith f xs ys)
  zipWith⁺ P f []              = []
  zipWith⁺ P f (Pfxy ∷ Pfxsys) = Pfxy ∷ zipWith⁺ P f Pfxsys

------------------------------------------------------------------------
-- Operations for constructing lists
------------------------------------------------------------------------
-- singleton

module _ {a p} {A : Set a} {P : A → Set p} where

  singleton⁻ : ∀ {x} → All P [ x ] → P x
  singleton⁻ (px ∷ []) = px

------------------------------------------------------------------------
-- snoc

module _ {a p} {A : Set a} {P : A → Set p} where

  ∷ʳ⁺ : ∀ {xs x} → All P xs → P x → All P (xs ∷ʳ x)
  ∷ʳ⁺ pxs px = ++⁺ pxs (px ∷ [])

  ∷ʳ⁻ : ∀ {xs x} → All P (xs ∷ʳ x) → All P xs × P x
  ∷ʳ⁻ {xs} pxs = Prod.map₂ singleton⁻ $ ++⁻ xs pxs

------------------------------------------------------------------------
-- fromMaybe

  fromMaybe⁺ : ∀ {mx} → MAll.All P mx → All P (fromMaybe mx)
  fromMaybe⁺ (just px) = px ∷ []
  fromMaybe⁺ nothing   = []

  fromMaybe⁻ : ∀ mx → All P (fromMaybe mx) → MAll.All P mx
  fromMaybe⁻ (just x) (px ∷ []) = just px
  fromMaybe⁻ nothing  p         = nothing

------------------------------------------------------------------------
-- replicate

  replicate⁺ : ∀ n {x} → P x → All P (replicate n x)
  replicate⁺ zero    px = []
  replicate⁺ (suc n) px = px ∷ replicate⁺ n px

  replicate⁻ : ∀ {n x} → All P (replicate (suc n) x) → P x
  replicate⁻ (px ∷ _) = px

module _ {a p} {A : Set a} {P : A → Set p} where

------------------------------------------------------------------------
-- inits

  inits⁺ : ∀ {xs} → All P xs → All (All P) (inits xs)
  inits⁺ []         = [] ∷ []
  inits⁺ (px ∷ pxs) = [] ∷ gmap (px ∷_) (inits⁺ pxs)

  inits⁻ : ∀ xs → All (All P) (inits xs) → All P xs
  inits⁻ []               pxs                   = []
  inits⁻ (x ∷ [])         ([] ∷ p[x] ∷ [])      = p[x]
  inits⁻ (x ∷ xs@(_ ∷ _)) ([] ∷ pxs@(p[x] ∷ _)) =
    singleton⁻ p[x] ∷ inits⁻ xs (All.map (drop⁺ 1) (map⁻ pxs))

------------------------------------------------------------------------
-- tails

  tails⁺ : ∀ {xs} → All P xs → All (All P) (tails xs)
  tails⁺ []             = [] ∷ []
  tails⁺ pxxs@(_ ∷ pxs) = pxxs ∷ tails⁺ pxs

  tails⁻ : ∀ xs → All (All P) (tails xs) → All P xs
  tails⁻ []       pxs        = []
  tails⁻ (x ∷ xs) (pxxs ∷ _) = pxxs

------------------------------------------------------------------------
-- all

module _ {a} {A : Set a} (p : A → Bool) where

  all⁺ : ∀ xs → T (all p xs) → All (T ∘ p) xs
  all⁺ []       _     = []
  all⁺ (x ∷ xs) px∷xs with Equivalence.to (T-∧ {p x}) ⟨$⟩ px∷xs
  ... | (px , pxs) = px ∷ all⁺ xs pxs

  all⁻ : ∀ {xs} → All (T ∘ p) xs → T (all p xs)
  all⁻ []         = _
  all⁻ (px ∷ pxs) = Equivalence.from T-∧ ⟨$⟩ (px , all⁻ pxs)

------------------------------------------------------------------------
-- All is anti-monotone.

anti-mono : ∀ {a p} {A : Set a} {P : A → Set p} {xs ys} →
            xs ⊆ ys → All P ys → All P xs
anti-mono xs⊆ys pys = All.tabulate (All.lookup pys ∘ xs⊆ys)

all-anti-mono : ∀ {a} {A : Set a} (p : A → Bool) {xs ys} →
                xs ⊆ ys → T (all p ys) → T (all p xs)
all-anti-mono p xs⊆ys = all⁻ p ∘ anti-mono xs⊆ys ∘ all⁺ p _

------------------------------------------------------------------------
-- Interactions with pointwise equality
------------------------------------------------------------------------

module _ {c ℓ} (S : Setoid c ℓ) where

  open Setoid S renaming (Carrier to A)
  open ListEq S

  respects : ∀ {p} {P : Pred A p} → P Respects _≈_ → (All P) Respects _≋_
  respects p≈ []            []         = []
  respects p≈ (x≈y ∷ xs≈ys) (px ∷ pxs) = p≈ x≈y px ∷ respects p≈ xs≈ys pxs

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

-- Version 0.16

All-all = all⁻
{-# WARNING_ON_USAGE All-all
"Warning: All-all was deprecated in v0.16.
Please use all⁻ instead."
#-}
all-All = all⁺
{-# WARNING_ON_USAGE all-All
"Warning: all-All was deprecated in v0.16.
Please use all⁺ instead."
#-}
All-map = map⁺
{-# WARNING_ON_USAGE All-map
"Warning: All-map was deprecated in v0.16.
Please use map⁺ instead."
#-}
map-All = map⁻
{-# WARNING_ON_USAGE map-All
"Warning: map-All was deprecated in v0.16.
Please use map⁻ instead."
#-}

-- Version 1.0

filter⁺₁ = all-filter
{-# WARNING_ON_USAGE filter⁺₁
"Warning: filter⁺₁ was deprecated in v1.0.
Please use all-filter instead."
#-}
filter⁺₂ = filter⁺
{-# WARNING_ON_USAGE filter⁺₂
"Warning: filter⁺₂ was deprecated in v1.0.
Please use filter⁺ instead."
#-}


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Logical relation for erasure (Def. 3.8 and Lemma 3.9)
------------------------------------------------------------------------

import Parametric.Syntax.Type as Type
import Parametric.Syntax.Term as Term
import Parametric.Denotation.Value as Value
import Parametric.Denotation.Evaluation as Evaluation
import Parametric.Change.Validity as Validity
import Parametric.Change.Specification as Specification
import Parametric.Change.Type as ChangeType
import Parametric.Change.Value as ChangeValue
import Parametric.Change.Derive as Derive

module Parametric.Change.Implementation
    {Base : Type.Structure}
    (Const : Term.Structure Base)
    (⟦_⟧Base : Value.Structure Base)
    (⟦_⟧Const : Evaluation.Structure Const ⟦_⟧Base)
    (ΔBase : ChangeType.Structure Base)
    {{validity-structure : Validity.Structure ⟦_⟧Base}}
    (⟦apply-base⟧ : ChangeValue.ApplyStructure Const ⟦_⟧Base ΔBase)
    (⟦diff-base⟧ : ChangeValue.DiffStructure Const ⟦_⟧Base ΔBase)
    (⟦nil-base⟧ : ChangeValue.NilStructure Const ⟦_⟧Base ΔBase)
    (derive-const : Derive.Structure Const ΔBase)
  where

open Type.Structure Base
open Term.Structure Base Const
open Value.Structure Base ⟦_⟧Base
open Evaluation.Structure Const ⟦_⟧Base ⟦_⟧Const
open Validity.Structure ⟦_⟧Base {{validity-structure}}
open ChangeType.Structure Base ΔBase
open ChangeValue.Structure Const ⟦_⟧Base ΔBase ⟦apply-base⟧ ⟦diff-base⟧ ⟦nil-base⟧
open Derive.Structure Const ΔBase derive-const

open import Base.Denotation.Notation

open import Relation.Binary.PropositionalEquality
open import Postulate.Extensionality

record Structure : Set₁ where

  ----------------
  -- Parameters --
  ----------------

  field
    -- Extension point 1: Logical relation on base types.
    --
    -- In the paper, we assume that the logical relation is equality on base types
    -- (see Def. 3.8a). Here, we only require that plugins define what the logical
    -- relation is on base types, and provide proofs for the two extension points
    -- below.
    implements-base : ∀ ι {v : ⟦ ι ⟧Base} → Δ₍ ι ₎ v → ⟦ ΔBase ι ⟧Type → Set

    -- Extension point 2: Differences on base types are logically related.
    u⊟v≈u⊝v-base : ∀ ι {u v : ⟦ ι ⟧Base} →
      implements-base ι (u ⊟₍ ι ₎ v) (⟦diff-base⟧ ι u v)

    nil-v≈⟦nil⟧-v-base : ∀ ι {v : ⟦ ι ⟧Base} →
      implements-base ι (nil₍ ι ₎ v) (⟦nil-base⟧ ι v)
    -- Extension point 3: Lemma 3.1 for base types.
    carry-over-base : ∀ {ι}
      {v : ⟦ ι ⟧Base}
      (Δv : Δ₍ ι ₎ v)
      {Δv′ : ⟦ ΔBase ι ⟧Type} (Δv≈Δv′ : implements-base ι Δv Δv′) →
        v ⊞₍ base ι ₎ Δv ≡ v ⟦⊕₍ base ι ₎⟧ Δv′

  ------------------------
  -- Logical relation ≈ --
  ------------------------

  infix 4 implements
  syntax implements τ u v = u ≈₍ τ ₎ v
  implements : ∀ τ {v} → Δ₍ τ ₎ v → ⟦ ΔType τ ⟧ → Set

  implements (base ι) Δf Δf′ = implements-base ι Δf Δf′
  implements (σ ⇒ τ) {f} Δf Δf′ =
    (w : ⟦ σ ⟧) (Δw : Δ₍ σ ₎ w)
    (Δw′ : ⟦ ΔType σ ⟧) (Δw≈Δw′ : implements σ {w} Δw Δw′) →
    implements τ {f w} (call-change {σ} {τ} Δf w Δw) (Δf′ w Δw′)

  infix 4 _≈_
  _≈_ : ∀ {τ v} → Δ₍ τ ₎ v → ⟦ ΔType τ ⟧ → Set
  _≈_ {τ} {v} = implements τ {v}

  data implements-env : ∀ Γ → {ρ : ⟦ Γ ⟧} (dρ : Δ₍ Γ ₎ ρ) → ⟦ mapContext ΔType Γ ⟧ → Set where
    ∅ : implements-env ∅ {∅} ∅ ∅
    _•_ : ∀ {τ Γ v ρ dv dρ v′ ρ′} →
      (dv≈v′ : implements τ {v} dv v′) →
      (dρ≈ρ′ : implements-env Γ {ρ} dρ ρ′) →
      implements-env (τ • Γ) {v • ρ} (dv • dρ) (v′ • ρ′)

  ----------------
  -- carry-over --
  ----------------

  -- This is lemma 3.10.
  carry-over : ∀ {τ}
    {v : ⟦ τ ⟧}
    (Δv : Δ₍ τ ₎ v)
    {Δv′ : ⟦ ΔType τ ⟧} (Δv≈Δv′ : Δv ≈₍ τ ₎ Δv′) →
      after₍ τ ₎ Δv ≡ before₍ τ ₎ Δv ⟦⊕₍ τ ₎⟧ Δv′

  u⊟v≈u⊝v : ∀ {τ : Type} {u v : ⟦ τ ⟧} →
    u ⊟₍ τ ₎ v ≈₍ τ ₎ u ⟦⊝₍ τ ₎⟧ v

  u⊟v≈u⊝v {base ι} {u} {v} = u⊟v≈u⊝v-base ι {u} {v}
  u⊟v≈u⊝v {σ ⇒ τ} {g} {f} w Δw Δw′ Δw≈Δw′ =
    subst
      (λ □ → (g □ ⊟₍ τ ₎ f (before₍ σ ₎ Δw)) ≈₍ τ ₎ g (before₍ σ ₎ Δw ⟦⊕₍ σ ₎⟧ Δw′) ⟦⊝₍ τ ₎⟧ f (before₍ σ ₎ Δw))
      (sym (carry-over {σ} Δw Δw≈Δw′))
      (u⊟v≈u⊝v {τ} {g (before₍ σ ₎ Δw ⟦⊕₍ σ ₎⟧ Δw′)} {f (before₍ σ ₎ Δw)})

  nil-v≈⟦nil⟧-v : ∀ {τ : Type} {v : ⟦ τ ⟧} →
    nil₍ τ ₎ v ≈₍ τ ₎ (⟦nil₍ τ ₎⟧ v)
  nil-v≈⟦nil⟧-v {base ι} = nil-v≈⟦nil⟧-v-base ι
  nil-v≈⟦nil⟧-v {σ ⇒ τ} {f} = u⊟v≈u⊝v {σ ⇒ τ} {f} {f}

  carry-over {base ι} Δv Δv≈Δv′ = carry-over-base Δv Δv≈Δv′
  carry-over {σ ⇒ τ} {f} Δf {Δf′} Δf≈Δf′ =
    ext (λ v →
      carry-over {τ} {f v} (call-change {σ} {τ} Δf v (nil₍ σ ₎ v))
        {Δf′ v (⟦nil₍ σ ₎⟧ v)}
        (Δf≈Δf′ v (nil₍ σ ₎ v) (⟦nil₍ σ ₎⟧ v) (nil-v≈⟦nil⟧-v {σ} {v})))

  -- A property relating `alternate` and the subcontext relation Γ≼ΔΓ
  ⟦Γ≼ΔΓ⟧ : ∀ {Γ} (ρ : ⟦ Γ ⟧) (dρ : ⟦ mapContext ΔType Γ ⟧) →
    ρ ≡ ⟦ Γ≼ΔΓ ⟧≼ (alternate ρ dρ)
  ⟦Γ≼ΔΓ⟧ ∅ ∅ = refl
  ⟦Γ≼ΔΓ⟧ (v • ρ) (dv • dρ) = cong₂ _•_ refl (⟦Γ≼ΔΓ⟧ ρ dρ)

  -- A specialization of the soundness of weakening
  ⟦fit⟧ : ∀ {τ Γ} (t : Term Γ τ) (ρ : ⟦ Γ ⟧) (dρ : ⟦ mapContext ΔType Γ ⟧) →
    ⟦ t ⟧ ρ ≡ ⟦ fit t ⟧ (alternate ρ dρ)
  ⟦fit⟧ t ρ dρ =
    trans (cong ⟦ t ⟧ (⟦Γ≼ΔΓ⟧ ρ dρ)) (sym (weaken-sound t _))


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

module PermutationProperties where

open import Data.Nat using (ℕ; _+_)
open import Data.Fin using (Fin)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; trans; cong; module ≡-Reasoning; proof-irrelevance; setoid)
open import Function.Equality   using    (_⟨$⟩_)
open import Data.Sum using (_⊎_)
open import Data.Product using (_,_; proj₁; proj₂)
--

import FinEquivPlusTimes using (module Plus) -- don't open, just import
import FinEquivTypeEquiv using (module PlusE) -- don't open, just import
open FinEquivPlusTimes.Plus using (⊎≃+; +≃⊎)
open FinEquivTypeEquiv.PlusE using (_+F_)
open import ConcretePermutation
open import Permutation
open import SEquivSCPermEquiv
open import Equiv using (_●_; id≃; sym≃; _⊎≃_)
open import EquivEquiv
  using (id≋; sym≋; ●-assoc; _◎_; lid≋; rid≋; linv≋; rinv≋;
     module ≋-Reasoning)
open import TypeEquivEquiv using (_⊎≋_)
open import FinEquivEquivPlus using ([id+id]≋id; +●≋●+)

open ≋-Reasoning

------------------------------------------------------------------------------
-- Composition

assocp : ∀ {m₁ m₂ m₃ n₁} → {p₁ : CPerm m₁ n₁} → {p₂ : CPerm m₂ m₁} →
  {p₃ : CPerm m₃ m₂} →
  (p₁ ●p p₂) ●p p₃ ≡ p₁ ●p (p₂ ●p p₃)
assocp {p₁ = p₁} {p₂} {p₃} =
  let e₁ = p⇒e p₁ in let e₂ = p⇒e p₂ in let e₃ = p⇒e p₃ in
  ≋⇒≡ (begin (
  p⇒e (e⇒p (e₁ ● e₂)) ● e₃
    ≋⟨ left-α-over-● (e₁ ● e₂) e₃ ⟩
  (e₁ ● e₂) ● e₃
    ≋⟨ ●-assoc {f = e₃} {e₂} {e₁} ⟩
  e₁ ● (e₂ ● e₃)
    ≋⟨ sym≋ (right-α-over-● e₁ (e₂ ● e₃)) ⟩
   e₁ ● (p⇒e (e⇒p (e₂ ● e₃))) ∎))

lidp : ∀ {m₁ m₂} {p : CPerm m₂ m₁} → idp ●p p ≡ p
lidp {p = p} = trans (≋⇒≡ (begin (
  (p⇒e (e⇒p id≃)) ● (p⇒e p)
    ≋⟨ left-α-over-● id≃ (p⇒e p) ⟩
  id≃ ● (p⇒e p)
    ≋⟨ lid≋ ⟩
  (p⇒e p) ∎))) (βu refl)

ridp : ∀ {m₁ m₂} {p : CPerm m₂ m₁} → p ●p idp ≡ p
ridp {p = p} = trans (≋⇒≡ (begin (
  (p⇒e p) ● (p⇒e (e⇒p id≃))
    ≋⟨ right-α-over-● (p⇒e p) id≃ ⟩
  (p⇒e p) ● id≃
    ≋⟨ rid≋ ⟩
  (p⇒e p) ∎))) (βu refl)

-- Inverses

rinv : ∀ {m₁ m₂} (p : CPerm m₂ m₁) → p ●p (symp p) ≡ idp
rinv p = let e = p⇒e p in ≋⇒≡ (begin (
  e ● (p⇒e (e⇒p (sym≃ e)))
    ≋⟨ right-α-over-● e (sym≃ e) ⟩
  e ● (sym≃ e)
    ≋⟨ rinv≋ e ⟩
  id≃ ∎))

linv : ∀ {m₁ m₂} (p : CPerm m₂ m₁) → (symp p) ●p p ≡ idp
linv p = let e = p⇒e p in ≋⇒≡ (begin (
  (p⇒e (e⇒p (sym≃ e))) ● e
    ≋⟨ left-α-over-● (sym≃ e) e ⟩
  (sym≃ e) ● e
    ≋⟨ linv≋ e ⟩
  id≃ ∎))

-- p₁ ⊎p p₂ = e⇒p ((p⇒e p₁) +F (p⇒e p₂))
--   Fm≃Fn +F Fo≃Fp = ⊎≃+ ● Fm≃Fn ⊎≃ Fo≃Fp ● +≃⊎
⊎p●p≡●p⊎p : {m₁ m₂ n₁ n₂ o₁ o₂ : ℕ} →
  {f : CPerm n₁ m₁} {g : CPerm n₂ m₂} {h : CPerm o₁ n₁} {i : CPerm o₂ n₂} →
  ((f ●p h) ⊎p (g ●p i)) ≡ ((f ⊎p g) ●p (h ⊎p i))
⊎p●p≡●p⊎p {f = f} {g} {h} {i} =
  let e₁ = p⇒e f in let e₂ = p⇒e g in let e₃ = p⇒e h in let e₄ = p⇒e i in
  let f≋ = id≋ {x = ⊎≃+} in
  let g≋ = id≋ {x = +≃⊎} in
  ≋⇒≡ (begin -- inline ⊎p
  p⇒e (e⇒p (e₁ ● e₃)) +F p⇒e (e⇒p (e₂ ● e₄))
    ≋⟨ id≋ ⟩ -- inline +F
  ⊎≃+ ● (p⇒e (e⇒p (e₁ ● e₃)) ⊎≃ p⇒e (e⇒p (e₂ ● e₄))) ● +≃⊎
    ≋⟨ f≋ ◎ ((α₁ ⊎≋ α₁) ◎ g≋) ⟩
  ⊎≃+ ● ((e₁ ● e₃) ⊎≃ (e₂ ● e₄)) ● +≃⊎
    ≋⟨ +●≋●+ ⟩
  (e₁ +F e₂) ● (e₃ +F e₄)
    ≋⟨ sym≋ ((α₁ {e = e₁ +F e₂}) ◎ (α₁ {e = e₃ +F e₄})) ⟩
  (p⇒e (e⇒p (e₁ +F e₂)) ● p⇒e (e⇒p (e₃ +F e₄))) ∎)

-- Additives

1p⊎1p≡1p : ∀ {m n} → idp {m} ⊎p idp {n} ≡ idp {m + n}
1p⊎1p≡1p {m} {n} =
  let em = p⇒e (e⇒p (id≃ {A = Fin m})) in
  let en = p⇒e (e⇒p (id≃ {A = Fin n})) in
  let f≋ = id≋ {x = ⊎≃+ {m} {n}} in
  let g≋ = id≋ {x = +≃⊎ {m} {n}} in
  ≋⇒≡ (begin (
  em +F en
    ≋⟨ id≋ ⟩
  ⊎≃+ ● em ⊎≃ en ● +≃⊎
    ≋⟨ f≋ ◎ ((α₁ ⊎≋ α₁) ◎ g≋) ⟩
  ⊎≃+ ● (id≃ {A = Fin m}) ⊎≃ id≃ ● +≃⊎
    ≋⟨ [id+id]≋id ⟩
  id≃ {A = Fin (m + n)} ∎))

-- interaction with composition
{-  The underlying permutations are no longer defined!
unite+p∘[0⊎x]≡x∘unite+p : ∀ {m n} (p : CPerm m n) →
  transp unite+p (0p ⊎p p) ≡ transp p unite+p
unite+p∘[0⊎x]≡x∘unite+p p = p≡ unite+∘[0⊎x]≡x∘unite+

uniti+p∘x≡[0⊎x]∘uniti+p : ∀ {m n} (p : CPerm m n) →
  transp uniti+p p ≡ transp (0p ⊎p p) uniti+p
uniti+p∘x≡[0⊎x]∘uniti+p p = p≡ (uniti+∘x≡[0⊎x]∘uniti+ {x = CPerm.π p})

uniti+rp∘[x⊎0]≡x∘uniti+rp : ∀ {m n} (p : CPerm m n) →
  transp uniti+rp (p ⊎p 0p) ≡ transp p uniti+rp
uniti+rp∘[x⊎0]≡x∘uniti+rp p = p≡ uniti+r∘[x⊎0]≡x∘uniti+r
-}

{-
unite+rp∘[x⊎0]≡x∘unite+rp : ∀ {m n} (p : CPerm m n) →
  transp unite+rp p ≡ transp (p ⊎p 0p) unite+rp
unite+rp∘[x⊎0]≡x∘unite+rp p = p≡ unite+r∘[x⊎0]≡x∘unite+r
-}

-- Multiplicatives
{-
1p×1p≡1p : ∀ {m n} → idp {m} ×p idp {n} ≡ idp
1p×1p≡1p {m} = p≡ (1C×1C≡1C {m})

×p-distrib :  ∀ {m₁ m₂ m₃ m₄ n₁ n₂} → {p₁ : CPerm m₁ n₁} → {p₂ : CPerm m₂ n₂}
    → {p₃ : CPerm m₃ m₁} → {p₄ : CPerm m₄ m₂} →
      (transp p₁ p₃) ×p (transp p₂ p₄) ≡ transp (p₁ ×p p₂) (p₃ ×p p₄)
×p-distrib {p₁ = p₁} = p≡ (sym (×c-distrib {p₁ = CPerm.π p₁}))
-}
------------------------------------------------------------------------------


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

open import lib.Basics hiding (take; drop; take-drop-split)
open import lib.types.Pointed
open import lib.types.Paths

module lib.types.FunctionSeq where

infixr 80 _◃∘_
data FunctionSeq {i} : Type i → Type i → Type (lsucc i) where
  idf-seq : {A : Type i} → FunctionSeq A A
  _◃∘_ : {A B C : Type i} (g : B → C) (fs : FunctionSeq A B) → FunctionSeq A C

infix 30 _–→_
_–→_ = FunctionSeq

infix 90 _◃idf
_◃idf : ∀ {i} {A B : Type i} → (A → B) → A –→ B
_◃idf fs = fs ◃∘ idf-seq

compose : ∀ {i} {A B : Type i} → (A –→ B) → A → B
compose idf-seq = idf _
compose (g ◃∘ fs) = g ∘ compose fs

de⊙-seq : ∀ {i} {X Y : Ptd i} → (X ⊙–→ Y) → (de⊙ X –→ de⊙ Y)
de⊙-seq ⊙idf-seq = idf-seq
de⊙-seq (f ◃⊙∘ fs) = fst f ◃∘ de⊙-seq fs

de⊙-seq-⊙compose : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y)
  → compose (de⊙-seq fs) == fst (⊙compose fs)
de⊙-seq-⊙compose ⊙idf-seq = idp
de⊙-seq-⊙compose (f ◃⊙∘ fs) = ap (fst f ∘_) (de⊙-seq-⊙compose fs)

private
  pt-seq' : ∀ {i} {j} {X Y : Ptd i} {C : Type j} (g : de⊙ Y → C) (fs : X ⊙–→ Y)
    → g (compose (de⊙-seq fs) (pt X)) =-= g (pt Y)
  pt-seq' g ⊙idf-seq = []
  pt-seq' g (f ◃⊙∘ fs) =
    pt-seq' (g ∘ fst f) fs ∙∙
    ap g (snd f) ◃∎

pt-seq : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y)
  → compose (de⊙-seq fs) (pt X) =-= pt Y
pt-seq ⊙idf-seq = []
pt-seq (f ◃⊙∘ fs) = pt-seq' (fst f) fs ∙▹ snd f

private
  ⊙compose-pt' : ∀ {i} {j} {X Y : Ptd i} {C : Type j} (g : de⊙ Y → C) (fs : X ⊙–→ Y)
    → ap g (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
      ap g (snd (⊙compose fs)) ◃∎
      =ₛ
      ↯ (pt-seq' g fs) ◃∎
  ⊙compose-pt' g ⊙idf-seq = =ₛ-in idp
  ⊙compose-pt' {_} {_} {X} g (f ◃⊙∘ fs) =
    ap g (app= (ap (fst f ∘_) (de⊙-seq-⊙compose fs)) (pt X)) ◃∙
    ap g (snd (⊙compose (f ◃⊙∘ fs))) ◃∎
      =ₛ₁⟨ 0 & 1 & ∘-ap g (_$ pt X) (ap (fst f ∘_) (de⊙-seq-⊙compose fs)) ∙
                   ∘-ap (λ h → g (h (pt X))) (fst f ∘_) (de⊙-seq-⊙compose fs) ∙
                   ap-∘ (g ∘ fst f) (_$ pt X) (de⊙-seq-⊙compose fs) ⟩
    ap (g ∘ fst f) (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    ap g (snd (⊙compose (f ◃⊙∘ fs))) ◃∎
      =ₛ⟨ 1 & 1 & ap-seq-∙ g (ap (fst f) (snd (⊙compose fs)) ◃∙ snd f ◃∎) ⟩
    ap (g ∘ fst f) (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    ap g (ap (fst f) (snd (⊙compose fs))) ◃∙
    ap g (snd f) ◃∎
      =ₛ₁⟨ 1 & 1 & ∘-ap g (fst f) (snd (⊙compose fs)) ⟩
    ap (g ∘ fst f) (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    ap (g ∘ fst f) (snd (⊙compose fs)) ◃∙
    ap g (snd f) ◃∎
      =ₛ⟨ 0 & 2 & ⊙compose-pt' (g ∘ fst f) fs ⟩
    ↯ (pt-seq' (g ∘ fst f) fs) ◃∙
    ap g (snd f) ◃∎
      =ₛ₁⟨ ! (↯-∙∙ (pt-seq' (g ∘ fst f) fs) (ap g (snd f) ◃∎)) ⟩
    ↯ (pt-seq' (g ∘ fst f) fs ∙▹ ap g (snd f)) ◃∎ ∎ₛ

⊙compose-pt : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y)
  → app= (de⊙-seq-⊙compose fs) (pt X) ◃∙
    snd (⊙compose fs) ◃∎
    =ₛ
    ↯ (pt-seq fs) ◃∎
⊙compose-pt ⊙idf-seq = =ₛ-in idp
⊙compose-pt {_} {X} {Y} (f ◃⊙∘ fs) =
  app= (de⊙-seq-⊙compose (f ◃⊙∘ fs)) (pt X) ◃∙
  snd (f ⊙∘ ⊙compose fs) ◃∎
    =ₛ⟨ 1 & 1 & expand (ap (fst f) (snd (⊙compose fs)) ◃∙ snd f ◃∎) ⟩
  app= (de⊙-seq-⊙compose (f ◃⊙∘ fs)) (pt X) ◃∙
  ap (fst f) (snd (⊙compose fs)) ◃∙
  snd f ◃∎
    =ₛ₁⟨ 0 & 1 & ∘-ap (_$ pt X) (fst f ∘_) (de⊙-seq-⊙compose fs) ∙
                 ap-∘ (fst f) (_$ pt X) (de⊙-seq-⊙compose fs) ⟩
  ap (fst f) (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
  ap (fst f) (snd (⊙compose fs)) ◃∙
  snd f ◃∎
    =ₛ⟨ 0 & 2 & ⊙compose-pt' (fst f) fs ⟩
  ↯ (pt-seq' (fst f) fs) ◃∙
  snd f ◃∎
    =ₛ₁⟨ ! (↯-∙∙ (pt-seq' (fst f) fs) (snd f ◃∎)) ⟩
  ↯ (pt-seq' (fst f) fs ∙∙ snd f ◃∎) ◃∎ ∎ₛ

record _=∘_ {i} {A B : Type i} (fs gs : A –→ B) : Type i where
  constructor =∘-in
  field
    =∘-out : compose fs == compose gs
open _=∘_ public

abstract
  de⊙-seq-=⊙∘ : ∀ {i} {X Y : Ptd i} {fs gs : X ⊙–→ Y}
    → fs =⊙∘ gs
    → de⊙-seq fs =∘ de⊙-seq gs
  de⊙-seq-=⊙∘ {fs = fs} {gs = gs} p = =∘-in $
    compose (de⊙-seq fs)
      =⟨ de⊙-seq-⊙compose fs ⟩
    fst (⊙compose fs)
      =⟨ ap fst (=⊙∘-out p) ⟩
    fst (⊙compose gs)
      =⟨ ! (de⊙-seq-⊙compose gs) ⟩
    compose (de⊙-seq gs) =∎

  ⊙seq-λ= : ∀ {i} {X Y : Ptd i} {fs gs : X ⊙–→ Y}
    → (p : compose (de⊙-seq fs) ∼ compose (de⊙-seq gs))
    → pt-seq fs =ₛ p (pt X) ◃∙ pt-seq gs
    → fs =⊙∘ gs
  ⊙seq-λ= {_} {X} {Y} {fs} {gs} p q =
    =⊙∘-in $
    pair= (! (de⊙-seq-⊙compose fs) ∙ λ= p ∙ de⊙-seq-⊙compose gs) $
    ↓-app=cst-in $ =ₛ-out $
    snd (⊙compose fs) ◃∎
      =ₛ⟨ pre-rotate-in (⊙compose-pt fs) ⟩
    ! (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    ↯ (pt-seq fs) ◃∎
      =ₛ₁⟨ 1 & 1 & =ₛ-out q ⟩
    ! (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    ↯ (p (pt X) ◃∙ pt-seq gs) ◃∎
      =ₛ⟨ 1 & 1 & =ₛ-in {t = p (pt X) ◃∙ ↯ (pt-seq gs) ◃∎} $
                  ↯-∙∙ (p (pt X) ◃∎) (pt-seq gs) ⟩
    ! (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    p (pt X) ◃∙
    ↯ (pt-seq gs) ◃∎
      =ₛ⟨ 2 & 1 & !ₛ (⊙compose-pt gs) ⟩
    ! (app= (de⊙-seq-⊙compose fs) (pt X)) ◃∙
    p (pt X) ◃∙
    app= (de⊙-seq-⊙compose gs) (pt X) ◃∙
    snd (⊙compose gs) ◃∎
      =ₛ₁⟨ 0 & 1 & !-ap (_$ pt X) (de⊙-seq-⊙compose fs) ⟩
    app= (! (de⊙-seq-⊙compose fs)) (pt X) ◃∙
    p (pt X) ◃∙
    app= (de⊙-seq-⊙compose gs) (pt X) ◃∙
    snd (⊙compose gs) ◃∎
      =ₛ₁⟨ 1 & 1 & ! (app=-β p (pt X)) ⟩
    app= (! (de⊙-seq-⊙compose fs)) (pt X) ◃∙
    app= (λ= p) (pt X) ◃∙
    app= (de⊙-seq-⊙compose gs) (pt X) ◃∙
    snd (⊙compose gs) ◃∎
      =ₛ⟨ 0 & 3 & ∙-ap-seq (_$ pt X) (_ ◃∙ _ ◃∙ _ ◃∎) ⟩
    app= (! (de⊙-seq-⊙compose fs) ∙ λ= p ∙ de⊙-seq-⊙compose gs) (pt X) ◃∙
    snd (⊙compose gs) ◃∎ ∎ₛ

abstract
  !⊙∘ : ∀ {i} {X Y : Ptd i} {fs gs : X ⊙–→ Y} → fs =⊙∘ gs → gs =⊙∘ fs
  !⊙∘ (=⊙∘-in p) = =⊙∘-in (! p)

⊙expand : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) → (⊙compose fs ◃⊙idf) =⊙∘ fs
⊙expand fs = =⊙∘-in idp

⊙contract : ∀ {i} {X Y : Ptd i} {fs : X ⊙–→ Y} → fs =⊙∘ (⊙compose fs ◃⊙idf)
⊙contract = =⊙∘-in idp

private
  _∙⊙∘_ : ∀ {i} {X Y : Ptd i} {fs gs hs : X ⊙–→ Y} → fs =⊙∘ gs → gs =⊙∘ hs → fs =⊙∘ hs
  _∙⊙∘_ (=⊙∘-in p) (=⊙∘-in q) = =⊙∘-in (p ∙ q)

  point-from-target : ∀ {i} {X Y : Ptd i}
    → ℕ → (X ⊙–→ Y) → Ptd i
  point-from-target {Y = Y} O fs = Y
  point-from-target {Y = Y} (S n) ⊙idf-seq = Y
  point-from-target (S n) (f ◃⊙∘ fs) = point-from-target n fs

  take : ∀ {i} (n : ℕ) {X Y : Ptd i} (fs : X ⊙–→ Y) → point-from-target n fs ⊙–→ Y
  take O s = ⊙idf-seq
  take (S n) ⊙idf-seq = ⊙idf-seq
  take (S n) (f ◃⊙∘ fs) = f ◃⊙∘ take n fs

  drop : ∀ {i} (n : ℕ) {X Y : Ptd i} (fs : X ⊙–→ Y) → X ⊙–→ point-from-target n fs
  drop 0 fs = fs
  drop (S n) ⊙idf-seq = ⊙idf-seq
  drop (S n) (f ◃⊙∘ fs) = drop n fs

  infixr 80 _⊙∘∘_
  _⊙∘∘_ : ∀ {i} {X Y Z : Ptd i} (gs : Y ⊙–→ Z) (fs : X ⊙–→ Y) → (X ⊙–→ Z)
  ⊙idf-seq ⊙∘∘ fs = fs
  (g ◃⊙∘ gs) ⊙∘∘ fs = g ◃⊙∘ (gs ⊙∘∘ fs)

  ⊙compose-⊙∘∘ : ∀ {i} {X Y Z : Ptd i} (gs : Y ⊙–→ Z) (fs : X ⊙–→ Y)
    → ⊙compose (gs ⊙∘∘ fs) == ⊙compose gs ⊙∘ ⊙compose fs
  ⊙compose-⊙∘∘ ⊙idf-seq fs = ! (⊙λ= (⊙∘-unit-l (⊙compose fs)))
  ⊙compose-⊙∘∘ (g ◃⊙∘ gs) fs =
    ap (g ⊙∘_) (⊙compose-⊙∘∘ gs fs) ∙
    ! (⊙λ= (⊙∘-assoc g (⊙compose gs) (⊙compose fs)))

  take-drop-split' : ∀ {i} {X Y : Ptd i} (n : ℕ) (fs : X ⊙–→ Y)
    → fs == take n fs ⊙∘∘ drop n fs
  take-drop-split' O fs = idp
  take-drop-split' (S n) ⊙idf-seq = idp
  take-drop-split' (S n) (f ◃⊙∘ fs) = ap (f ◃⊙∘_) (take-drop-split' n fs)

  take-drop-split : ∀ {i} {X Y : Ptd i} (n : ℕ) (s : X ⊙–→ Y)
    → ⊙compose s ◃⊙idf =⊙∘ ⊙compose (take n s) ◃⊙∘ ⊙compose (drop n s) ◃⊙idf
  take-drop-split n fs = =⊙∘-in $
    ⊙compose fs
      =⟨ ap ⊙compose (take-drop-split' n fs) ⟩
    ⊙compose (take n fs ⊙∘∘ drop n fs)
      =⟨ ⊙compose-⊙∘∘ (take n fs) (drop n fs) ⟩
    ⊙compose (take n fs) ⊙∘ ⊙compose (drop n fs) =∎

abstract
  private
    infixr 10 _⊙compose=⟨_&_&_&_⟩_
    _⊙compose=⟨_&_&_&_⟩_ : ∀ {i} {X Y : Ptd i} {f : X ⊙→ Y}
      → (fs : X ⊙–→ Y)
      → (n : ℕ) (m : ℕ)
      → (gs : point-from-target m (drop n fs) ⊙–→ point-from-target n fs)
      → ⊙compose (take m (drop n fs)) == ⊙compose gs
      → ⊙compose (take n fs ⊙∘∘ gs ⊙∘∘ drop m (drop n fs)) == f
      → ⊙compose fs == f
    _⊙compose=⟨_&_&_&_⟩_ {f = f} fs n m gs p p' =
      ⊙compose fs
        =⟨ =⊙∘-out (take-drop-split n fs) ⟩
      ⊙compose (take n fs) ⊙∘ ⊙compose (drop n fs)
        =⟨ ap (⊙compose (take n fs) ⊙∘_) (=⊙∘-out (take-drop-split m (drop n fs))) ⟩
      ⊙compose (take n fs) ⊙∘ ⊙compose (take m (drop n fs)) ⊙∘ ⊙compose (drop m (drop n fs))
        =⟨ ap (λ v → ⊙compose (take n fs) ⊙∘ v ⊙∘ ⊙compose (drop m (drop n fs))) p ⟩
      ⊙compose (take n fs) ⊙∘ ⊙compose gs ⊙∘ ⊙compose (drop m (drop n fs))
        =⟨ ap (⊙compose (take n fs) ⊙∘_) (! (⊙compose-⊙∘∘ gs (drop m (drop n fs)))) ⟩
      ⊙compose (take n fs) ⊙∘ ⊙compose (gs ⊙∘∘ drop m (drop n fs))
        =⟨ ! (⊙compose-⊙∘∘ (take n fs) (gs ⊙∘∘ drop m (drop n fs))) ⟩
      ⊙compose (take n fs ⊙∘∘ gs ⊙∘∘ drop m (drop n fs))
        =⟨ p' ⟩
      f =∎

  infixr 10 _=⊙∘⟨_⟩_
  _=⊙∘⟨_⟩_ : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) {gs hs : X ⊙–→ Y}
    → fs =⊙∘ gs
    → gs =⊙∘ hs
    → fs =⊙∘ hs
  _=⊙∘⟨_⟩_ _ p q = p ∙⊙∘ q

  infixr 10 _=⊙∘⟨_&_&_⟩_
  _=⊙∘⟨_&_&_⟩_ : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) {gs : X ⊙–→ Y}
    → (m n : ℕ)
    → {hs : point-from-target n (drop m fs) ⊙–→ point-from-target m fs}
    → take n (drop m fs) =⊙∘ hs
    → take m fs ⊙∘∘ hs ⊙∘∘ drop n (drop m fs) =⊙∘ gs
    → fs =⊙∘ gs
  _=⊙∘⟨_&_&_⟩_ fs m n {hs} p p' = =⊙∘-in (fs ⊙compose=⟨ m & n & hs & =⊙∘-out p ⟩ =⊙∘-out p')

  infixr 10 _=⊙∘₁⟨_⟩_
  _=⊙∘₁⟨_⟩_ : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) {gs : X ⊙–→ Y}
    → {h : X ⊙→ Y}
    → ⊙compose fs == h
    → h ◃⊙idf =⊙∘ gs
    → fs =⊙∘ gs
  _=⊙∘₁⟨_⟩_ fs p p' = =⊙∘-in p ∙⊙∘ p'

  infixr 10 _=⊙∘₁⟨_&_&_⟩_
  _=⊙∘₁⟨_&_&_⟩_ : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) {gs : X ⊙–→ Y}
    → (m n : ℕ)
    → {h : point-from-target n (drop m fs) ⊙→ point-from-target m fs}
    → ⊙compose (take n (drop m fs)) == h
    → take m fs ⊙∘∘ h ◃⊙∘ drop n (drop m fs) =⊙∘ gs
    → fs =⊙∘ gs
  _=⊙∘₁⟨_&_&_⟩_ fs m n {h} p p' = fs =⊙∘⟨ m & n & (=⊙∘-in {gs = h ◃⊙idf} p) ⟩ p'

infix 15 _∎⊙∘
_∎⊙∘ : ∀ {i} {X Y : Ptd i} (fs : X ⊙–→ Y) → fs =⊙∘ fs
_∎⊙∘ _ = =⊙∘-in idp

⊙coe-seq : ∀ {i} {X Y : Ptd i}
  → X =-= Y
  → X ⊙–→ Y
⊙coe-seq [] = ⊙idf-seq
⊙coe-seq (p ◃∙ ps) = ⊙coe-seq ps ⊙∘∘ ⊙coe p ◃⊙idf

⊙coe-seq-∙ : ∀ {i} {X Y : Ptd i} (ps : X =-= Y)
  → ⊙coe (↯ ps) ◃⊙idf =⊙∘ ⊙coe-seq ps
⊙coe-seq-∙ [] = =⊙∘-in idp
⊙coe-seq-∙ (p ◃∙ ps) =
  ⊙coe (↯ (p ◃∙ ps)) ◃⊙idf
    =⊙∘₁⟨ ap ⊙coe (↯-∙∙ (p ◃∎) ps) ⟩
  ⊙coe (p ∙ ↯ ps) ◃⊙idf
    =⊙∘⟨ ⊙coe-∙ p (↯ ps) ⟩
  ⊙coe (↯ ps) ◃⊙∘ ⊙coe p ◃⊙idf
    =⊙∘⟨ 0 & 1 & ⊙coe-seq-∙ ps ⟩
  ⊙coe-seq ps ⊙∘∘ ⊙coe p ◃⊙idf ∎⊙∘

⊙coe-seq-=ₛ : ∀ {i} {X Y : Ptd i} {ps qs : X =-= Y}
  → ps =ₛ qs
  → ⊙coe-seq ps =⊙∘ ⊙coe-seq qs
⊙coe-seq-=ₛ {ps = ps} {qs = qs} r =
  ⊙coe-seq ps
    =⊙∘⟨ !⊙∘ (⊙coe-seq-∙ ps) ⟩
  ⊙coe (↯ ps) ◃⊙idf
    =⊙∘₁⟨ ap ⊙coe (=ₛ-out r) ⟩
  ⊙coe (↯ qs) ◃⊙idf
    =⊙∘⟨ ⊙coe-seq-∙ qs ⟩
  ⊙coe-seq qs ∎⊙∘


module STLC.Coquand.Substitution where

open import STLC.Coquand.Renaming public
open import Category

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

-- Substitutions
infix 3 _⊢⋆_
data _⊢⋆_ : 𝒞 → 𝒞 → Set
  where
    ∅   : ∀ {Γ} → Γ ⊢⋆ ∅

    _,_ : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ) (M : Γ ⊢ A)
                    → Γ ⊢⋆ Ξ , A

putₛ : ∀ {Ξ Γ} → (∀ {A} → Ξ ⊢ A → Γ ⊢ A) → Γ ⊢⋆ Ξ
putₛ {∅}     f = ∅
putₛ {Γ , A} f = putₛ (λ M → f (wk M)) , f 0

getₛ : ∀ {Γ Ξ A} → Γ ⊢⋆ Ξ → Ξ ∋ A → Γ ⊢ A
getₛ (σ , M) zero    = M
getₛ (σ , M) (suc i) = getₛ σ i

wkₛ : ∀ {A Γ Ξ} → Γ ⊢⋆ Ξ → Γ , A ⊢⋆ Ξ
wkₛ ∅       = ∅
wkₛ (σ , M) = wkₛ σ , wk M

liftₛ : ∀ {A Γ Ξ} → Γ ⊢⋆ Ξ → Γ , A ⊢⋆ Ξ , A
liftₛ σ = wkₛ σ , 0

idₛ : ∀ {Γ} → Γ ⊢⋆ Γ
idₛ {∅}     = ∅
idₛ {Γ , A} = liftₛ idₛ

sub : ∀ {Γ Ξ A} → Γ ⊢⋆ Ξ → Ξ ⊢ A → Γ ⊢ A
sub σ (𝓋 i)   = getₛ σ i
sub σ (ƛ M)   = ƛ (sub (liftₛ σ) M)
sub σ (M ∙ N) = sub σ M ∙ sub σ N

cut : ∀ {Γ A B} → Γ ⊢ A → Γ , A ⊢ B → Γ ⊢ B
cut M N = sub (idₛ , M) N

-- NOTE: _●_ = sub⋆
_●_ : ∀ {Γ Ξ Φ} → Γ ⊢⋆ Ξ → Ξ ⊢⋆ Φ → Γ ⊢⋆ Φ
σ₁ ● ∅        = ∅
σ₁ ● (σ₂ , M) = σ₁ ● σ₂ , sub σ₁ M

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

-- (wkgetₛ)
natgetₛ : ∀ {Γ Ξ A B} → (σ : Γ ⊢⋆ Ξ) (i : Ξ ∋ A)
                      → getₛ (wkₛ {B} σ) i ≡ (wk ∘ getₛ σ) i
natgetₛ (σ , M) zero    = refl
natgetₛ (σ , M) (suc i) = natgetₛ σ i

idgetₛ : ∀ {Γ A} → (i : Γ ∋ A)
                 → getₛ idₛ i ≡ 𝓋 i
idgetₛ zero    = refl
idgetₛ (suc i) = natgetₛ idₛ i
               ⦙ wk & idgetₛ i
               ⦙ 𝓋 & ( natgetᵣ idᵣ i
                     ⦙ suc & idgetᵣ i
                     )

idsub : ∀ {Γ A} → (M : Γ ⊢ A)
                → sub idₛ M ≡ M
idsub (𝓋 i)   = idgetₛ i
idsub (ƛ M)   = ƛ & idsub M
idsub (M ∙ N) = _∙_ & idsub M
                    ⊗ idsub N

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

⌊_⌋ : ∀ {Γ Γ′} → Γ′ ∋⋆ Γ → Γ′ ⊢⋆ Γ
⌊ ∅ ⌋     = ∅
⌊ η , i ⌋ = ⌊ η ⌋ , 𝓋 i

-- NOTE: _◐_ = getₛ⋆
_◐_ : ∀ {Γ Ξ Ξ′} → Γ ⊢⋆ Ξ′ → Ξ′ ∋⋆ Ξ → Γ ⊢⋆ Ξ
σ ◐ η = σ ● ⌊ η ⌋

-- NOTE: _◑_ = ren⋆
_◑_ : ∀ {Γ Γ′ Ξ} → Γ′ ∋⋆ Γ → Γ ⊢⋆ Ξ → Γ′ ⊢⋆ Ξ
η ◑ σ = ⌊ η ⌋ ● σ

⌊get⌋ : ∀ {Γ Γ′ A} → (η : Γ′ ∋⋆ Γ) (i : Γ ∋ A)
                   → getₛ ⌊ η ⌋ i ≡ 𝓋 (getᵣ η i)
⌊get⌋ (η , j) zero    = refl
⌊get⌋ (η , j) (suc i) = ⌊get⌋ η i

⌊wk⌋ : ∀ {Γ Γ′ A} → (η : Γ′ ∋⋆ Γ)
                  → wkₛ {A} ⌊ η ⌋ ≡ ⌊ wkᵣ η ⌋
⌊wk⌋ ∅       = refl
⌊wk⌋ (η , i) = (wkₛ ⌊ η ⌋ ,_) & (𝓋 & natgetᵣ idᵣ i)
             ⦙ _,_ & ⌊wk⌋ η
                   ⊗ 𝓋 ∘ suc & idgetᵣ i

⌊lift⌋ : ∀ {Γ Γ′ A} → (η : Γ′ ∋⋆ Γ)
                    → liftₛ {A} ⌊ η ⌋ ≡ ⌊ liftᵣ η ⌋
⌊lift⌋ η = (_, 0) & ⌊wk⌋ η

⌊id⌋ : ∀ {Γ} → idₛ {Γ} ≡ ⌊ idᵣ ⌋
⌊id⌋ {∅}     = refl
⌊id⌋ {Γ , A} = (_, 0) & ( wkₛ & ⌊id⌋
                        ⦙ ⌊wk⌋ idᵣ
                        )

⌊sub⌋ : ∀ {Γ Γ′ A} → (η : Γ′ ∋⋆ Γ) (M : Γ ⊢ A)
                   → sub ⌊ η ⌋ M ≡ ren η M
⌊sub⌋ η (𝓋 i)   = ⌊get⌋ η i
⌊sub⌋ η (ƛ M)   = ƛ & ( (λ σ → sub σ M) & ⌊lift⌋ η
                      ⦙ ⌊sub⌋ (liftᵣ η) M
                      )
⌊sub⌋ η (M ∙ N) = _∙_ & ⌊sub⌋ η M
                      ⊗ ⌊sub⌋ η N

⌊○⌋ : ∀ {Γ Γ′ Γ″} → (η₁ : Γ″ ∋⋆ Γ′) (η₂ : Γ′ ∋⋆ Γ)
                  → ⌊ η₁ ○ η₂ ⌋ ≡ ⌊ η₁ ⌋ ● ⌊ η₂ ⌋
⌊○⌋ η₁ ∅        = refl
⌊○⌋ η₁ (η₂ , i) = _,_ & ⌊○⌋ η₁ η₂
                      ⊗ (⌊get⌋ η₁ i ⁻¹)

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

zap◐ : ∀ {Γ Ξ Ξ′ A} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (M : Γ ⊢ A)
                    → (σ , M) ◐ wkᵣ η ≡ σ ◐ η
zap◐ σ ∅       M = refl
zap◐ σ (η , i) M = (_, getₛ σ i) & zap◐ σ η M

rid◐ : ∀ {Γ Ξ} → (σ : Γ ⊢⋆ Ξ)
               → σ ◐ idᵣ ≡ σ
rid◐ ∅       = refl
rid◐ (σ , M) = (_, M) & ( zap◐ σ idᵣ M
                        ⦙ rid◐ σ
                        )

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

get◐ : ∀ {Γ Ξ Ξ′ A} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (i : Ξ ∋ A)
                    → getₛ (σ ◐ η) i ≡ (getₛ σ ∘ getᵣ η) i
get◐ σ (η , j) zero    = refl
get◐ σ (η , j) (suc i) = get◐ σ η i

wk◐ : ∀ {Γ Ξ Ξ′ A} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ)
                   → wkₛ {A} (σ ◐ η) ≡ wkₛ σ ◐ η
wk◐ σ ∅       = refl
wk◐ σ (η , i) = _,_ & wk◐ σ η
                    ⊗ (natgetₛ σ i ⁻¹)

lift◐ : ∀ {Γ Ξ Ξ′ A} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ)
                     → liftₛ {A} (σ ◐ η) ≡ liftₛ σ ◐ liftᵣ η
lift◐ σ η = (_, 0) & ( wk◐ σ η
                   ⦙ zap◐ (wkₛ σ) η 0 ⁻¹
                   )

wkrid◐ : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                   → wkₛ {A} σ ◐ idᵣ ≡ wkₛ σ
wkrid◐ σ = wk◐ σ idᵣ ⁻¹
         ⦙ wkₛ & rid◐ σ

liftwkrid◐ : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                       → liftₛ {A} σ ◐ wkᵣ idᵣ ≡ wkₛ σ
liftwkrid◐ σ = zap◐ (wkₛ σ) idᵣ 0
             ⦙ wkrid◐ σ

mutual
  sub◐ : ∀ {Γ Ξ Ξ′ A} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (M : Ξ ⊢ A)
                      → sub (σ ◐ η) M ≡ (sub σ ∘ ren η) M
  sub◐ σ η (𝓋 i)   = get◐ σ η i
  sub◐ σ η (ƛ M)   = ƛ & sublift◐ σ η M
  sub◐ σ η (M ∙ N) = _∙_ & sub◐ σ η M
                         ⊗ sub◐ σ η N

  sublift◐ : ∀ {Γ Ξ Ξ′ A B} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (M : Ξ , B ⊢ A)
                            → sub (liftₛ {B} (σ ◐ η)) M ≡
                               (sub (liftₛ σ) ∘ ren (liftᵣ η)) M
  sublift◐ σ η M = (λ σ′ → sub σ′ M) & lift◐ σ η
                 ⦙ sub◐ (liftₛ σ) (liftᵣ η) M

subwk◐ : ∀ {Γ Ξ Ξ′ A B} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (M : Ξ ⊢ A)
                        → sub (wkₛ {B} (σ ◐ η)) M ≡
                           (sub (wkₛ σ) ∘ ren η) M
subwk◐ σ η M = (λ σ′ → sub σ′ M) & wk◐ σ η
             ⦙ sub◐ (wkₛ σ) η M

subliftwk◐ : ∀ {Γ Ξ Ξ′ A B} → (σ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (M : Ξ ⊢ A)
                            → sub (wkₛ {B} (σ ◐ η)) M ≡
                               (sub (liftₛ σ) ∘ ren (wkᵣ η)) M
subliftwk◐ σ η M = (λ σ′ → sub σ′ M) & ( wk◐ σ η
                                        ⦙ zap◐ (wkₛ σ) η 0 ⁻¹
                                        )
                 ⦙ sub◐ (liftₛ σ) (wkᵣ η) M

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

zap◑ : ∀ {Γ Γ′ Ξ A} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (i : Γ′ ∋ A)
                    → (η , i) ◑ wkₛ σ ≡ η ◑ σ
zap◑ η ∅       i = refl
zap◑ η (σ , j) i = _,_ & zap◑ η σ i
                       ⊗ ( sub◐ (⌊ η ⌋ , 𝓋 i) (wkᵣ idᵣ) j ⁻¹
                         ⦙ (λ σ′ → sub σ′ j)
                           & ( zap◐ ⌊ η ⌋ idᵣ (𝓋 i)
                             ⦙ rid◐ ⌊ η ⌋
                             )
                         )

lid◑ : ∀ {Γ Ξ} → (σ : Γ ⊢⋆ Ξ)
               → idᵣ ◑ σ ≡ σ
lid◑ ∅       = refl
lid◑ (σ , M) = _,_ & lid◑ σ
                   ⊗ ( (λ σ′ → sub σ′ M) & ⌊id⌋ ⁻¹
                     ⦙ idsub M
                     )

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

zap● : ∀ {Γ Ξ Φ A} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (M : Γ ⊢ A)
                   → (σ₁ , M) ● wkₛ σ₂ ≡ σ₁ ● σ₂
zap● σ₁ ∅        M = refl
zap● σ₁ (σ₂ , N) M = _,_ & zap● σ₁ σ₂ M
                         ⊗ ( sub◐ (σ₁ , M) (wkᵣ idᵣ) N ⁻¹
                           ⦙ (λ σ′ → sub σ′ N)
                             & ( zap◐ σ₁ idᵣ M
                               ⦙ rid◐ σ₁
                               )
                           )

lid● : ∀ {Γ Ξ} → (σ : Γ ⊢⋆ Ξ)
               → idₛ ● σ ≡ σ
lid● ∅       = refl
lid● (σ , M) = _,_ & lid● σ
                   ⊗ idsub M

rid● : ∀ {Γ Ξ} → (σ : Γ ⊢⋆ Ξ)
               → σ ● idₛ ≡ σ
rid● σ = (σ ●_) & ⌊id⌋ ⦙ rid◐ σ

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

get◑ : ∀ {Γ Γ′ Ξ A} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (i : Ξ ∋ A)
                    → getₛ (η ◑ σ) i ≡ (ren η ∘ getₛ σ) i
get◑ η (σ , M) zero    = ⌊sub⌋ η M
get◑ η (σ , M) (suc i) = get◑ η σ i

wk◑ : ∀ {Γ Γ′ Ξ A} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ)
                   → wkₛ {A} (η ◑ σ) ≡ wkᵣ η ◑ σ
wk◑ η ∅       = refl
wk◑ η (σ , M) = _,_ & wk◑ η σ
                    ⊗ ( ren (wkᵣ idᵣ) & ⌊sub⌋ η M
                      ⦙ ren○ (wkᵣ idᵣ) η M ⁻¹
                      ⦙ (λ η′ → ren η′ M) & wklid○ η
                      ⦙ ⌊sub⌋ (wkᵣ η) M ⁻¹
                      )

lift◑ : ∀ {Γ Γ′ Ξ A} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ)
                     → liftₛ {A} (η ◑ σ) ≡ liftᵣ η ◑ liftₛ σ
lift◑ η σ = (_, 0) & ( wk◑ η σ
                     ⦙ zap● ⌊ wkᵣ η ⌋ σ 0 ⁻¹
                     )

wklid◑ : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                   → wkᵣ {A} idᵣ ◑ σ ≡ wkₛ σ
wklid◑ σ = wk◑ idᵣ σ ⁻¹
         ⦙ wkₛ & lid◑ σ

liftwklid◑ : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                       → (liftᵣ {A} idᵣ ◑ wkₛ σ) ≡ wkₛ σ
liftwklid◑ σ = zap◑ (wkᵣ idᵣ) σ 0
             ⦙ wklid◑ σ

mutual
  sub◑ : ∀ {Γ Γ′ Ξ A} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (M : Ξ ⊢ A)
                      → sub (η ◑ σ) M ≡ (ren η ∘ sub σ) M
  sub◑ η σ (𝓋 i)   = get◑ η σ i
  sub◑ η σ (ƛ M)   = ƛ & sublift◑ η σ M
  sub◑ η σ (M ∙ N) = _∙_ & sub◑ η σ M
                         ⊗ sub◑ η σ N

  sublift◑ : ∀ {Γ Γ′ Ξ A B} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (M : Ξ , B ⊢ A)
                            → sub (liftₛ {B} (η ◑ σ)) M ≡
                               (ren (liftᵣ η) ∘ sub (liftₛ σ)) M
  sublift◑ η σ M = (λ σ′ → sub σ′ M) & lift◑ η σ
                 ⦙ sub◑ (liftᵣ η) (liftₛ σ) M

subwk◑ : ∀ {Γ Γ′ Ξ A B} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (M : Ξ ⊢ A)
                        → sub (wkₛ {B} (η ◑ σ)) M ≡
                           (ren (wkᵣ η) ∘ sub σ) M
subwk◑ η σ M = (λ σ′ → sub σ′ M) & wk◑ η σ
             ⦙ sub◑ (wkᵣ η) σ M
subliftwk◑ : ∀ {Γ Γ′ Ξ A B} → (η : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ) (M : Ξ ⊢ A)
                            → sub (wkₛ {B} (η ◑ σ)) M ≡
                               (ren (liftᵣ η) ∘ sub (wkₛ σ)) M
subliftwk◑ η σ M = (λ σ′ → sub σ′ M) & ( wk◑ η σ
                                        ⦙ zap◑ (wkᵣ η) σ 0 ⁻¹
                                        )
                 ⦙ sub◑ (liftᵣ η) (wkₛ σ) M

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

get● : ∀ {Γ Ξ Φ A} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (i : Φ ∋ A)
                   → getₛ (σ₁ ● σ₂) i ≡ (sub σ₁ ∘ getₛ σ₂) i
get● σ₁ (σ₂ , M) zero    = refl
get● σ₁ (σ₂ , M) (suc i) = get● σ₁ σ₂ i

wk● : ∀ {Γ Ξ Φ A} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ)
                  → wkₛ {A} (σ₁ ● σ₂) ≡ wkₛ σ₁ ● σ₂
wk● σ₁ ∅        = refl
wk● σ₁ (σ₂ , M) = _,_ & wk● σ₁ σ₂
                      ⊗ ( sub◑ (wkᵣ idᵣ) σ₁ M ⁻¹
                        ⦙ (λ σ′ → sub σ′ M) & wklid◑ σ₁
                        )

lift● : ∀ {Γ Ξ Φ A} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ)
                    → liftₛ {A} (σ₁ ● σ₂) ≡ liftₛ σ₁ ● liftₛ σ₂
lift● σ₁ σ₂ = (_, 0) & ( wk● σ₁ σ₂
                       ⦙ zap● (wkₛ σ₁) σ₂ 0 ⁻¹
                       )

wklid● : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                   → wkₛ {A} idₛ ● σ ≡ wkₛ σ
wklid● σ = wk● idₛ σ ⁻¹
         ⦙ wkₛ & lid● σ

wkrid● : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                   → wkₛ {A} σ ● idₛ ≡ wkₛ σ
wkrid● σ = wk● σ idₛ ⁻¹
         ⦙ wkₛ & rid● σ

liftwkrid● : ∀ {Γ Ξ A} → (σ : Γ ⊢⋆ Ξ)
                       → liftₛ {A} σ ● wkₛ idₛ ≡ wkₛ σ
liftwkrid● σ = zap● (wkₛ σ) idₛ 0
             ⦙ wkrid● σ

mutual
  sub● : ∀ {Γ Ξ Φ A} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (M : Φ ⊢ A)
                     → sub (σ₁ ● σ₂) M ≡ (sub σ₁ ∘ sub σ₂) M
  sub● σ₁ σ₂ (𝓋 i)   = get● σ₁ σ₂ i
  sub● σ₁ σ₂ (ƛ M)   = ƛ & sublift● σ₁ σ₂ M
  sub● σ₁ σ₂ (M ∙ N) = _∙_ & sub● σ₁ σ₂ M
                           ⊗ sub● σ₁ σ₂ N

  sublift● : ∀ {Γ Ξ Φ A B} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (M : Φ , B ⊢ A)
                           → sub (liftₛ {B} (σ₁ ● σ₂)) M ≡
                              (sub (liftₛ σ₁) ∘ sub (liftₛ σ₂)) M
  sublift● σ₁ σ₂ M = (λ σ′ → sub σ′ M) & lift● σ₁ σ₂
                   ⦙ sub● (liftₛ σ₁) (liftₛ σ₂) M

subwk● : ∀ {Γ Ξ Φ A B} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (M : Φ ⊢ A)
                       → sub (wkₛ {B} (σ₁ ● σ₂)) M ≡
                          (sub (wkₛ σ₁) ∘ sub σ₂) M
subwk● σ₁ σ₂ M = (λ σ′ → sub σ′ M) & wk● σ₁ σ₂
               ⦙ sub● (wkₛ σ₁) σ₂ M

subliftwk● : ∀ {Γ Ξ Φ A B} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (M : Φ ⊢ A)
                           → sub (wkₛ {B} (σ₁ ● σ₂)) M ≡
                              (sub (liftₛ σ₁) ∘ sub (wkₛ σ₂)) M
subliftwk● σ₁ σ₂ M = (λ σ′ → sub σ′ M) & ( wk● σ₁ σ₂
                                          ⦙ zap● (wkₛ σ₁) σ₂ 0 ⁻¹
                                          )
                   ⦙ sub● (liftₛ σ₁) (wkₛ σ₂) M

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

comp◐○ : ∀ {Γ Ξ Ξ′ Ξ″} → (σ : Γ ⊢⋆ Ξ″) (η₁ : Ξ″ ∋⋆ Ξ′) (η₂ : Ξ′ ∋⋆ Ξ)
                       → σ ◐ (η₁ ○ η₂) ≡ (σ ◐ η₁) ◐ η₂
comp◐○ σ η₁ ∅        = refl
comp◐○ σ η₁ (η₂ , i) = _,_ & comp◐○ σ η₁ η₂
                           ⊗ (get◐ σ η₁ i ⁻¹)

comp◑○ : ∀ {Γ Γ′ Γ″ Ξ} → (η₁ : Γ″ ∋⋆ Γ′) (η₂ : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ)
                       → η₁ ◑ (η₂ ◑ σ) ≡ (η₁ ○ η₂) ◑ σ
comp◑○ η₁ η₂ ∅       = refl
comp◑○ η₁ η₂ (σ , M) = _,_ & comp◑○ η₁ η₂ σ
                           ⊗ ( sub● ⌊ η₁ ⌋ ⌊ η₂ ⌋ M ⁻¹
                             ⦙ (λ σ′ → sub σ′ M)
                               & ⌊○⌋ η₁ η₂ ⁻¹
                             )

comp◑◐ : ∀ {Γ Γ′ Ξ Ξ′} → (η₁ : Γ′ ∋⋆ Γ) (σ : Γ ⊢⋆ Ξ′) (η₂ : Ξ′ ∋⋆ Ξ)
                       → η₁ ◑ (σ ◐ η₂) ≡ (η₁ ◑ σ) ◐ η₂
comp◑◐ η₁ σ ∅        = refl
comp◑◐ η₁ σ (η₂ , i) = _,_ & comp◑◐ η₁ σ η₂
                           ⊗ ( ⌊sub⌋ η₁ (getₛ σ i)
                             ⦙ get◑ η₁ σ i ⁻¹
                             )

comp◑● : ∀ {Γ Γ′ Ξ Φ} → (η : Γ′ ∋⋆ Γ) (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ)
                      → η ◑ (σ₁ ● σ₂) ≡ (η ◑ σ₁) ● σ₂
comp◑● η σ₁ ∅        = refl
comp◑● η σ₁ (σ₂ , M) = _,_ & comp◑● η σ₁ σ₂
                           ⊗ (sub● ⌊ η ⌋ σ₁ M ⁻¹)

comp●◐ : ∀ {Γ Ξ Φ Φ′} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ′) (η : Φ′ ∋⋆ Φ)
                      → σ₁ ● (σ₂ ◐ η) ≡ (σ₁ ● σ₂) ◐ η
comp●◐ σ₁ σ₂ ∅       = refl
comp●◐ σ₁ σ₂ (η , i) = _,_ & comp●◐ σ₁ σ₂ η
                           ⊗ (get● σ₁ σ₂ i ⁻¹)

comp●◑ : ∀ {Γ Ξ Ξ′ Φ} → (σ₁ : Γ ⊢⋆ Ξ′) (η : Ξ′ ∋⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ)
                      → σ₁ ● (η ◑ σ₂) ≡ (σ₁ ◐ η) ● σ₂
comp●◑ σ₁ η ∅        = refl
comp●◑ σ₁ η (σ₂ , M) = _,_ & comp●◑ σ₁ η σ₂
                           ⊗ (sub● σ₁ ⌊ η ⌋ M ⁻¹)

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

assoc● : ∀ {Γ Ξ Φ Ψ} → (σ₁ : Γ ⊢⋆ Ξ) (σ₂ : Ξ ⊢⋆ Φ) (σ₃ : Φ ⊢⋆ Ψ)
                     → σ₁ ● (σ₂ ● σ₃) ≡ (σ₁ ● σ₂) ● σ₃
assoc● σ₁ σ₂ ∅        = refl
assoc● σ₁ σ₂ (σ₃ , M) = _,_ & assoc● σ₁ σ₂ σ₃
                            ⊗ (sub● σ₁ σ₂ M ⁻¹)

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

𝗦𝗧𝗟𝗖 : Category 𝒞 _⊢⋆_
𝗦𝗧𝗟𝗖 =
  record
    { idₓ    = idₛ
    ; _⋄_    = flip _●_
    ; lid⋄   = rid●
    ; rid⋄   = lid●
    ; assoc⋄ = assoc●
    }

subPsh : 𝒯 → Presheaf₀ 𝗦𝗧𝗟𝗖
subPsh A =
  record
    { Fₓ  = _⊢ A
    ; F   = sub
    ; idF = fext! idsub
    ; F⋄  = λ σ₁ σ₂ → fext! (sub● σ₂ σ₁)
    }

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


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

open import lib.Basics

module lib.types.Sigma where

-- pointed [Σ]
⊙Σ : ∀ {i j} (X : Ptd i) → (de⊙ X → Ptd j) → Ptd (lmax i j)
⊙Σ ⊙[ A , a₀ ] Y = ⊙[ Σ A (de⊙ ∘ Y) , (a₀ , pt (Y a₀)) ]

-- Cartesian product
_×_ : ∀ {i j} (A : Type i) (B : Type j) → Type (lmax i j)
A × B = Σ A (λ _ → B)

_⊙×_ : ∀ {i j} → Ptd i → Ptd j → Ptd (lmax i j)
X ⊙× Y = ⊙Σ X (λ _ → Y)

infixr 80 _×_ _⊙×_

-- XXX Do we really need two versions of [⊙fst]?
⊙fstᵈ : ∀ {i j} {X : Ptd i} (Y : de⊙ X → Ptd j) → ⊙Σ X Y ⊙→ X
⊙fstᵈ Y = fst , idp

⊙fst : ∀ {i j} {X : Ptd i} {Y : Ptd j} → X ⊙× Y ⊙→ X
⊙fst = ⊙fstᵈ _

⊙snd : ∀ {i j} {X : Ptd i} {Y : Ptd j} → X ⊙× Y ⊙→ Y
⊙snd = (snd , idp)

fanout : ∀ {i j k} {A : Type i} {B : Type j} {C : Type k}
  → (A → B) → (A → C) → (A → B × C)
fanout f g x = f x , g x

⊙fanout-pt : ∀ {i j} {A : Type i} {B : Type j}
  {a₀ a₁ : A} (p : a₀ == a₁) {b₀ b₁ : B} (q : b₀ == b₁)
  → (a₀ , b₀) == (a₁ , b₁) :> A × B
⊙fanout-pt = pair×=

⊙fanout : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  → X ⊙→ Y → X ⊙→ Z → X ⊙→ Y ⊙× Z
⊙fanout (f , fpt) (g , gpt) = fanout f g , ⊙fanout-pt fpt gpt

diag : ∀ {i} {A : Type i} → (A → A × A)
diag a = a , a

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

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

⊙×-inr : ∀ {i j} (X : Ptd i) (Y : Ptd j) → Y ⊙→ X ⊙× Y
⊙×-inr X Y = (λ y → pt X , y) , idp

⊙fst-fanout : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (f : X ⊙→ Y) (g : X ⊙→ Z)
  → ⊙fst ⊙∘ ⊙fanout f g == f
⊙fst-fanout (f , idp) (g , idp) = idp

⊙snd-fanout : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (f : X ⊙→ Y) (g : X ⊙→ Z)
  → ⊙snd ⊙∘ ⊙fanout f g == g
⊙snd-fanout (f , idp) (g , idp) = idp

⊙fanout-pre∘ : ∀ {i j k l} {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
  (f : Y ⊙→ Z) (g : Y ⊙→ W) (h : X ⊙→ Y)
  → ⊙fanout f g ⊙∘ h == ⊙fanout (f ⊙∘ h) (g ⊙∘ h)
⊙fanout-pre∘ (f , idp) (g , idp) (h , idp) = idp

module _ {i j} {A : Type i} {B : A → Type j} where

  pair : (a : A) (b : B a) → Σ A B
  pair a b = (a , b)

  -- pair= has already been defined

  fst= : {ab a'b' : Σ A B} (p : ab == a'b') → (fst ab == fst a'b')
  fst= = ap fst

  snd= : {ab a'b' : Σ A B} (p : ab == a'b')
    → (snd ab == snd a'b' [ B ↓ fst= p ])
  snd= {._} {_} idp = idp

  fst=-β : {a a' : A} (p : a == a')
    {b : B a} {b' : B a'} (q : b == b' [ B ↓ p ])
    → fst= (pair= p q) == p
  fst=-β idp idp = idp

  snd=-β : {a a' : A} (p : a == a')
    {b : B a} {b' : B a'} (q : b == b' [ B ↓ p ])
    → snd= (pair= p q) == q [ (λ v → b == b' [ B ↓ v ]) ↓ fst=-β p q ]
  snd=-β idp idp = idp

  pair=-η : {ab a'b' : Σ A B} (p : ab == a'b')
    → p == pair= (fst= p) (snd= p)
  pair=-η {._} {_} idp = idp

  pair== : {a a' : A} {p p' : a == a'} (α : p == p')
           {b : B a} {b' : B a'} {q : b == b' [ B ↓ p ]} {q' : b == b' [ B ↓ p' ]}
           (β : q == q' [ (λ u → b == b' [ B ↓ u ]) ↓ α ])
    → pair= p q == pair= p' q'
  pair== idp idp = idp

module _ {i j} {A : Type i} {B : Type j} where

  fst×= : {ab a'b' : A × B} (p : ab == a'b') → (fst ab == fst a'b')
  fst×= = ap fst

  snd×= : {ab a'b' : A × B} (p : ab == a'b')
    → (snd ab == snd a'b')
  snd×= = ap snd

  fst×=-β : {a a' : A} (p : a == a')
    {b b' : B} (q : b == b')
    → fst×= (pair×= p q) == p
  fst×=-β idp idp = idp

  snd×=-β : {a a' : A} (p : a == a')
    {b b' : B} (q : b == b')
    → snd×= (pair×= p q) == q
  snd×=-β idp idp = idp

  pair×=-η : {ab a'b' : A × B} (p : ab == a'b')
    → p == pair×= (fst×= p) (snd×= p)
  pair×=-η {._} {_} idp = idp

module _ {i j} {A : Type i} {B : A → Type j} where

  =Σ : (x y : Σ A B) → Type (lmax i j)
  =Σ (a , b) (a' , b') = Σ (a == a') (λ p → b == b' [ B ↓ p ])

  =Σ-econv : (x y : Σ A B) →  (=Σ x y) ≃ (x == y)
  =Σ-econv x y =
    equiv (λ pq → pair= (fst pq) (snd pq)) (λ p → fst= p , snd= p)
          (λ p → ! (pair=-η p))
          (λ pq → pair= (fst=-β (fst pq) (snd pq)) (snd=-β (fst pq) (snd pq)))

  =Σ-conv : (x y : Σ A B) → (=Σ x y) == (x == y)
  =Σ-conv x y = ua (=Σ-econv x y)

Σ= : ∀ {i j} {A A' : Type i} (p : A == A') {B : A → Type j} {B' : A' → Type j}
  (q : B == B' [ (λ X → (X → Type j)) ↓ p ]) → Σ A B == Σ A' B'
Σ= idp idp = idp

instance
  Σ-level : ∀ {i j} {n : ℕ₋₂} {A : Type i} {P : A → Type j}
    → has-level n A → ((x : A) → has-level n (P x))
      → has-level n (Σ A P)
  Σ-level {n = ⟨-2⟩} p q = has-level-in ((contr-center p , (contr-center (q (contr-center p)))) , lemma)
    where abstract lemma = λ y → pair= (contr-path p _) (from-transp! _ _ (contr-path (q _) _))
  Σ-level {n = S n} p q = has-level-in lemma where
    abstract
      lemma = λ x y → equiv-preserves-level (=Σ-econv x y)
        {{Σ-level (has-level-apply p _ _) (λ _ →
          equiv-preserves-level ((to-transp-equiv _ _)⁻¹) {{has-level-apply (q _) _ _}})}}

×-level : ∀ {i j} {n : ℕ₋₂} {A : Type i} {B : Type j}
  → (has-level n A → has-level n B → has-level n (A × B))
×-level pA pB = Σ-level pA (λ x → pB)

-- Equivalences in a Σ-type

Σ-fmap-l : ∀ {i j k} {A : Type i} {B : Type j} (P : B → Type k)
  → (f : A → B) → (Σ A (P ∘ f) → Σ B P)
Σ-fmap-l P f (a , r) = (f a , r)

×-fmap-l : ∀ {i₀ i₁ j} {A₀ : Type i₀} {A₁ : Type i₁} (B : Type j)
  → (f : A₀ → A₁) → (A₀ × B → A₁ × B)
×-fmap-l B = Σ-fmap-l (λ _ → B)

Σ-isemap-l : ∀ {i j k} {A : Type i} {B : Type j} (P : B → Type k) {h : A → B}
  → is-equiv h → is-equiv (Σ-fmap-l P h)
Σ-isemap-l {A = A} {B = B} P {h} e = is-eq _ g f-g g-f
  where f = Σ-fmap-l P h

        g : Σ B P → Σ A (P ∘ h)
        g (b , s) = (is-equiv.g e b , transport P (! (is-equiv.f-g e b)) s)

        f-g : ∀ y → f (g y) == y
        f-g (b , s) = pair= (is-equiv.f-g e b) (transp-↓ P (is-equiv.f-g e b) s)

        g-f : ∀ x → g (f x) == x
        g-f (a , r) =
          pair= (is-equiv.g-f e a)
                (transport (λ q → transport P (! q) r == r [ P ∘ h ↓ is-equiv.g-f e a ])
                           (is-equiv.adj e a)
                           (transp-ap-↓ P h (is-equiv.g-f e a) r))

×-isemap-l : ∀ {i₀ i₁ j} {A₀ : Type i₀} {A₁ : Type i₁} (B : Type j) {h : A₀ → A₁}
  → is-equiv h → is-equiv (×-fmap-l B h)
×-isemap-l B = Σ-isemap-l (λ _ → B)

Σ-emap-l : ∀ {i j k} {A : Type i} {B : Type j} (P : B → Type k)
  → (e : A ≃ B) → (Σ A (P ∘ –> e) ≃ Σ B P)
Σ-emap-l P (f , e) = _ , Σ-isemap-l P e

×-emap-l : ∀ {i₀ i₁ j} {A₀ : Type i₀} {A₁ : Type i₁} (B : Type j)
  → (e : A₀ ≃ A₁) → (A₀ × B ≃ A₁ × B)
×-emap-l B = Σ-emap-l (λ _ → B)

Σ-fmap-r : ∀ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  → (∀ x → B x → C x) → (Σ A B → Σ A C)
Σ-fmap-r h (a , b) = (a , h a b)

×-fmap-r : ∀ {i j₀ j₁} (A : Type i) {B₀ : Type j₀} {B₁ : Type j₁}
  → (h : B₀ → B₁) → (A × B₀ → A × B₁)
×-fmap-r A h = Σ-fmap-r (λ _ → h)

Σ-isemap-r : ∀ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  {h : ∀ x → B x → C x} → (∀ x → is-equiv (h x)) → is-equiv (Σ-fmap-r h)
Σ-isemap-r {A = A} {B = B} {C = C} {h} k = is-eq _ g f-g g-f
  where f = Σ-fmap-r h

        g : Σ A C → Σ A B
        g (a , c) = (a , is-equiv.g (k a) c)

        f-g : ∀ p → f (g p) == p
        f-g (a , c) = pair= idp (is-equiv.f-g (k a) c)

        g-f : ∀ p → g (f p) == p
        g-f (a , b) = pair= idp (is-equiv.g-f (k a) b)

×-isemap-r : ∀ {i j₀ j₁} (A : Type i) {B₀ : Type j₀} {B₁ : Type j₁}
  → {h : B₀ → B₁} → is-equiv h → is-equiv (×-fmap-r A h)
×-isemap-r A e = Σ-isemap-r (λ _ → e)

Σ-emap-r : ∀ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  → (∀ x → B x ≃ C x) → (Σ A B ≃ Σ A C)
Σ-emap-r k = _ , Σ-isemap-r (λ x → snd (k x))

×-emap-r : ∀ {i j₀ j₁} (A : Type i) {B₀ : Type j₀} {B₁ : Type j₁}
  → (e : B₀ ≃ B₁) → (A × B₀ ≃ A × B₁)
×-emap-r A e = Σ-emap-r (λ _ → e)

hfiber-Σ-fmap-r : ∀ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  → (h : ∀ x → B x → C x) → {a : A} → (c : C a)
  → hfiber (Σ-fmap-r h) (a , c) ≃ hfiber (h a) c
hfiber-Σ-fmap-r h {a} c = equiv to from to-from from-to

  where to : hfiber (Σ-fmap-r h) (a , c) → hfiber (h a) c
        to ((_ , b) , idp) = b , idp

        from : hfiber (h a) c → hfiber (Σ-fmap-r h) (a , c)
        from (b , idp) = (a , b) , idp

        abstract
          to-from : (x : hfiber (h a) c) → to (from x) == x
          to-from (b , idp) = idp

          from-to : (x : hfiber (Σ-fmap-r h) (a , c)) → from (to x) == x
          from-to ((_ , b) , idp) = idp

{-
-- 2016/08/20 favonia: no one is using the following two functions.

-- Two ways of simultaneously applying equivalences in each component.
module _ {i₀ i₁ j₀ j₁} {A₀ : Type i₀} {A₁ : Type i₁}
         {B₀ : A₀ → Type j₀} {B₁ : A₁ → Type j₁} where
  Σ-emap : (u : A₀ ≃ A₁) (v : ∀ a → B₀ (<– u a) ≃ B₁ a) → Σ A₀ B₀ ≃ Σ A₁ B₁
  Σ-emap u v = Σ A₀ B₀           ≃⟨ Σ-emap-l _ (u ⁻¹) ⁻¹ ⟩
               Σ A₁ (B₀ ∘ <– u)  ≃⟨ Σ-emap-r v ⟩
               Σ A₁ B₁           ≃∎

  Σ-emap' : (u : A₀ ≃ A₁) (v : ∀ a → B₀ a ≃ B₁ (–> u a)) → Σ A₀ B₀ ≃ Σ A₁ B₁
  Σ-emap' u v = Σ A₀ B₀           ≃⟨ Σ-emap-r v ⟩
                Σ A₀ (B₁ ∘ –> u)  ≃⟨ Σ-emap-l _ u ⟩
                Σ A₁ B₁           ≃∎
-}

×-fmap : ∀ {i₀ i₁ j₀ j₁} {A₀ : Type i₀} {A₁ : Type i₁} {B₀ : Type j₀} {B₁ : Type j₁}
  → (h : A₀ → A₁) (k : B₀ → B₁) → (A₀ × B₀ → A₁ × B₁)
×-fmap u v = ×-fmap-r _ v ∘ ×-fmap-l _ u

⊙×-fmap : ∀ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'}
  → X ⊙→ X' → Y ⊙→ Y' → X ⊙× Y ⊙→ X' ⊙× Y'
⊙×-fmap (f , fpt) (g , gpt) = ×-fmap f g , pair×= fpt gpt

×-isemap : ∀ {i₀ i₁ j₀ j₁} {A₀ : Type i₀} {A₁ : Type i₁} {B₀ : Type j₀} {B₁ : Type j₁}
  {h : A₀ → A₁} {k : B₀ → B₁} → is-equiv h → is-equiv k → is-equiv (×-fmap h k)
×-isemap eh ek = ×-isemap-r _ ek ∘ise ×-isemap-l _ eh

×-emap : ∀ {i₀ i₁ j₀ j₁} {A₀ : Type i₀} {A₁ : Type i₁} {B₀ : Type j₀} {B₁ : Type j₁}
  → (u : A₀ ≃ A₁) (v : B₀ ≃ B₁) → (A₀ × B₀ ≃ A₁ × B₁)
×-emap u v = ×-emap-r _ v ∘e ×-emap-l _ u

⊙×-emap : ∀ {i i' j j'} {X : Ptd i} {X' : Ptd i'} {Y : Ptd j} {Y' : Ptd j'}
  → X ⊙≃ X' → Y ⊙≃ Y' → X ⊙× Y ⊙≃ X' ⊙× Y'
⊙×-emap (F , F-ise) (G , G-ise) = ⊙×-fmap F G , ×-isemap F-ise G-ise

-- Implementation of [_∙'_] on Σ
Σ-∙' : ∀ {i j} {A : Type i} {B : A → Type j}
  {x y z : A} {p : x == y} {p' : y == z}
  {u : B x} {v : B y} {w : B z}
  (q : u == v [ B ↓ p ]) (r : v == w [ B ↓ p' ])
  → (pair= p q ∙' pair= p' r) == pair= (p ∙' p') (q ∙'ᵈ r)
Σ-∙' {p = idp} {p' = idp} q idp = idp

-- Implementation of [_∙_] on Σ
Σ-∙ : ∀ {i j} {A : Type i} {B : A → Type j}
  {x y z : A} {p : x == y} {p' : y == z}
  {u : B x} {v : B y} {w : B z}
  (q : u == v [ B ↓ p ]) (r : v == w [ B ↓ p' ])
  → (pair= p q ∙ pair= p' r) == pair= (p ∙ p') (q ∙ᵈ r)
Σ-∙ {p = idp} {p' = idp} idp r = idp

-- Implementation of [!] on Σ
Σ-! : ∀ {i j} {A : Type i} {B : A → Type j}
  {x y : A} {p : x == y}
  {u : B x} {v : B y}
  (q : u == v [ B ↓ p ])
  → ! (pair= p q) == pair= (! p) (!ᵈ q)
Σ-! {p = idp} idp = idp

-- Implementation of [_∙'_] on ×
×-∙' : ∀ {i j} {A : Type i} {B : Type j}
  {x y z : A} (p : x == y) (p' : y == z)
  {u v w : B} (q : u == v) (q' : v == w)
  → (pair×= p q ∙' pair×= p' q') == pair×= (p ∙' p') (q ∙' q')
×-∙' idp idp q idp = idp

-- Implementation of [_∙_] on ×
×-∙ : ∀ {i j} {A : Type i} {B : Type j}
  {x y z : A} (p : x == y) (p' : y == z)
  {u v w : B} (q : u == v) (q' : v == w)
  → (pair×= p q ∙ pair×= p' q') == pair×= (p ∙ p') (q ∙ q')
×-∙ idp idp idp r = idp

-- Implementation of [!] on ×
×-! : ∀ {i j} {A : Type i} {B : Type j}
  {x y : A} (p : x == y) {u v : B} (q : u == v)
  → ! (pair×= p q) == pair×= (! p) (! q)
×-! idp idp = idp

-- Special case of [ap-,]
ap-cst,id : ∀ {i j} {A : Type i} (B : A → Type j)
  {a : A} {x y : B a} (p : x == y)
  → ap (λ x → _,_ {B = B} a x) p == pair= idp p
ap-cst,id B idp = idp

-- hfiber fst == B
module _ {i j} {A : Type i} {B : A → Type j} where

  private
    to : ∀ a → hfiber (fst :> (Σ A B → A)) a → B a
    to a ((.a , b) , idp) = b

    from : ∀ (a : A) → B a → hfiber (fst :> (Σ A B → A)) a
    from a b = (a , b) , idp

    to-from : ∀ (a : A) (b : B a) → to a (from a b) == b
    to-from a b = idp

    from-to : ∀ a b′ → from a (to a b′) == b′
    from-to a ((.a , b) , idp) = idp

  hfiber-fst : ∀ a → hfiber (fst :> (Σ A B → A)) a ≃ B a
  hfiber-fst a = to a , is-eq (to a) (from a) (to-from a) (from-to a)

{- Dependent paths in a Σ-type -}
module _ {i j k} {A : Type i} {B : A → Type j} {C : (a : A) → B a → Type k}
  where

  ↓-Σ-in : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    (q : r == r' [ B ↓ p ])
    → s == s' [ uncurry C ↓ pair= p q ]
    → (r , s) == (r' , s') [ (λ x → Σ (B x) (C x)) ↓ p ]
  ↓-Σ-in {p = idp} idp t = pair= idp t

  ↓-Σ-fst : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    → (r , s) == (r' , s') [ (λ x → Σ (B x) (C x)) ↓ p ]
    → r == r' [ B ↓ p ]
  ↓-Σ-fst {p = idp} u = fst= u

  ↓-Σ-snd : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    → (u : (r , s) == (r' , s') [ (λ x → Σ (B x) (C x)) ↓ p ])
    → s == s' [ uncurry C ↓ pair= p (↓-Σ-fst u) ]
  ↓-Σ-snd {p = idp} idp = idp

  ↓-Σ-β-fst : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    (q : r == r' [ B ↓ p ])
    (t : s == s' [ uncurry C ↓ pair= p q ])
    → ↓-Σ-fst (↓-Σ-in q t) == q
  ↓-Σ-β-fst {p = idp} idp idp = idp

  ↓-Σ-β-snd : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    (q : r == r' [ B ↓ p ])
    (t : s == s' [ uncurry C ↓ pair= p q ])
    → ↓-Σ-snd (↓-Σ-in q t) == t
      [ (λ q' → s == s' [ uncurry C ↓ pair= p q' ]) ↓ ↓-Σ-β-fst q t ]
  ↓-Σ-β-snd {p = idp} idp idp = idp

  ↓-Σ-η : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x r} {s' : C x' r'}
    (u : (r , s) == (r' , s') [ (λ x → Σ (B x) (C x)) ↓ p ])
    → ↓-Σ-in (↓-Σ-fst u) (↓-Σ-snd u) == u
  ↓-Σ-η {p = idp} idp = idp

{- Dependent paths in a ×-type -}
module _ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k}
  where

  ↓-×-in : {x x' : A} {p : x == x'} {r : B x} {r' : B x'}
    {s : C x} {s' : C x'}
    → r == r' [ B ↓ p ]
    → s == s' [ C ↓ p ]
    → (r , s) == (r' , s') [ (λ x → B x × C x) ↓ p ]
  ↓-×-in {p = idp} q t = pair×= q t

{- Dependent paths over a ×-type -}
module _ {i j k} {A : Type i} {B : Type j} (C : A → B → Type k)
  where

  ↓-over-×-in : {x x' : A} {p : x == x'} {y y' : B} {q : y == y'}
    {u : C x y} {v : C x' y} {w : C x' y'}
    → u == v [ (λ a → C a y) ↓ p ]
    → v == w [ (λ b → C x' b) ↓ q ]
    → u == w [ uncurry C ↓ pair×= p q ]
  ↓-over-×-in {p = idp} {q = idp} idp idp = idp

  ↓-over-×-in' : {x x' : A} {p : x == x'} {y y' : B} {q : y == y'}
    {u : C x y} {v : C x y'} {w : C x' y'}
    → u == v [ (λ b → C x b) ↓ q ]
    → v == w [ (λ a → C a y') ↓ p ]
    → u == w [ uncurry C ↓ pair×= p q ]
  ↓-over-×-in' {p = idp} {q = idp} idp idp = idp

module _ where
  -- An orphan lemma.
  ↓-cst×app-in : ∀ {i j k} {A : Type i}
    {B : Type j} {C : A → B → Type k}
    {a₁ a₂ : A} (p : a₁ == a₂)
    {b₁ b₂ : B} (q : b₁ == b₂)
    {c₁ : C a₁ b₁}{c₂ : C a₂ b₂}
    → c₁ == c₂ [ uncurry C ↓ pair×= p q ]
    → (b₁ , c₁) == (b₂ , c₂) [ (λ x → Σ B (C x)) ↓ p ]
  ↓-cst×app-in idp idp idp = idp

{- pair= and pair×= where one argument is reflexivity -}
pair=-idp-l : ∀ {i j} {A : Type i} {B : A → Type j} (a : A) {b₁ b₂ : B a}
  (q : b₁ == b₂) → pair= {B = B} idp q == ap (λ y → (a , y)) q
pair=-idp-l _ idp = idp

pair×=-idp-l : ∀ {i j} {A : Type i} {B : Type j} (a : A) {b₁ b₂ : B}
  (q : b₁ == b₂) → pair×= idp q == ap (λ y → (a , y)) q
pair×=-idp-l _ idp = idp

pair×=-idp-r : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A} (p : a₁ == a₂)
  (b : B) → pair×= p idp == ap (λ x → (x , b)) p
pair×=-idp-r idp _ = idp

pair×=-split-l : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A} (p : a₁ == a₂)
  {b₁ b₂ : B} (q : b₁ == b₂)
  → pair×= p q == ap (λ a → (a , b₁)) p ∙ ap (λ b → (a₂ , b)) q
pair×=-split-l idp idp = idp

pair×=-split-r : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A} (p : a₁ == a₂)
  {b₁ b₂ : B} (q : b₁ == b₂)
  → pair×= p q == ap (λ b → (a₁ , b)) q ∙ ap (λ a → (a , b₂)) p
pair×=-split-r idp idp = idp

-- Commutativity of products and derivatives.
module _ {i j} {A : Type i} {B : Type j} where
  ×-comm : Σ A (λ _ → B) ≃ Σ B (λ _ → A)
  ×-comm = equiv (λ {(a , b) → (b , a)}) (λ {(b , a) → (a , b)}) (λ _ → idp) (λ _ → idp)

module _ {i j k} {A : Type i} {B : A → Type j} {C : A → Type k} where
  Σ₂-×-comm : Σ (Σ A B) (λ ab → C (fst ab)) ≃ Σ (Σ A C) (λ ac → B (fst ac))
  Σ₂-×-comm = Σ-assoc ⁻¹ ∘e Σ-emap-r (λ a → ×-comm) ∘e Σ-assoc

module _ {i j k} {A : Type i} {B : Type j} {C : A → B → Type k} where
  Σ₁-×-comm : Σ A (λ a → Σ B (λ b → C a b)) ≃ Σ B (λ b → Σ A (λ a → C a b))
  Σ₁-×-comm = Σ-assoc ∘e Σ-emap-l _ ×-comm ∘e Σ-assoc ⁻¹


open import Prelude

module Implicits.Resolution.GenericFinite.Algorithm.Soundness where

open import Induction.WellFounded
open import Induction.Nat
open import Data.List
open import Data.List.Any
open Membership-≡
open import Data.Fin.Substitution
open import Data.Nat.Base using (_<′_)
open import Data.Maybe as Maybe
open import Data.Nat hiding (_<_)
open import Data.Nat.Properties
open import Relation.Binary using (module DecTotalOrder)
open DecTotalOrder decTotalOrder using () renaming (refl to ≤-refl; trans to ≤-trans)

open import Implicits.Syntax
open import Implicits.Syntax.Type.Unification
open import Implicits.Syntax.Type.Unification.Lemmas as mgu hiding (sound)
open import Implicits.Substitutions
open import Implicits.Substitutions.Lemmas
open import Implicits.Resolution.GenericFinite.Resolution
open import Implicits.Resolution.GenericFinite.Algorithm
open import Implicits.Resolution.GenericFinite.TerminationCondition
open import Implicits.Resolution.Termination

open import Data.Vec hiding (_∈_)
open import Extensions.Bool as Bl

private
  module M = MetaTypeMetaSubst

module ResolutionSound (cond : TerminationCondition) where

  open TerminationCondition cond
  open ResolutionAlgorithm cond
  open ResolutionRules cond

  open Lemmas

  postulate lem₄ : ∀ {m ν} (a : MetaType m (suc ν)) u us →
                   from-meta ((M.open-meta a) M./ (us M.↑) M./ (M.sub u))
                     ≡ (from-meta (a M./ (us M.↑tp))) tp[/tp from-meta u ]

  open-↓-∀ : ∀ {ν m} {Δ : ICtx ν} {Φ} (a : MetaType m (suc ν)) τ u us →
        Δ , Φ ⊢ (from-meta ((open-meta a) M./ (u ∷ us))) ↓ τ →
        Δ , Φ ⊢ (from-meta (∀' a M./ us)) ↓ τ
  open-↓-∀ {Δ = Δ} {Φ = Φ} a τ u us p = (i-tabs (from-meta u) (subst (λ v → Δ , Φ ⊢ v ↓ τ) (begin
        from-meta (M._/_ (open-meta a) (u ∷ us))
          ≡⟨ cong (λ v → from-meta (M._/_ (open-meta a) v)) (sym $ us↑-⊙-sub-u≡u∷us u us) ⟩
        from-meta ((open-meta a) M./ (us M.↑ M.⊙ (M.sub u)))
          ≡⟨ cong from-meta (/-⊙ (open-meta a)) ⟩
        from-meta ((open-meta a) M./ us M.↑ M./ (M.sub u))
          ≡⟨ lem₄ a u us ⟩
        from-meta (M._/_ a (us M.↑tp)) tp[/tp from-meta u ] ∎) p))
      where open MetaTypeMetaLemmas hiding (subst)

  mutual
    match'-sound : ∀ {ν m} (Δ : ICtx ν) (Φ : TCtx) τ (r : MetaType m ν) →
                  (Φ↓ : T-Acc Φ) → (m↓ : m<-Acc r) →
                  Maybe.All (λ u → Δ , Φ ⊢ from-meta (r M./ u) ↓ τ) (match' Δ Φ τ r Φ↓ m↓)
    match'-sound Δ Φ τ (a ⇒ b) Φ↓ (acc m↓)
      with match'       Δ Φ τ b Φ↓ (m↓ _ (b-m<-a⇒b a b))
         | match'-sound Δ Φ τ b Φ↓ (m↓ _ (b-m<-a⇒b a b))
    match'-sound Δ Φ τ (a ⇒ b) Φ↓ (acc m↓) | just u | just pu
      with (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ) <? Φ
    match'-sound Δ Φ τ (a ⇒ b) (acc Φ↓) (acc m↓) | just u | just pu | no Φ> = nothing
    match'-sound Δ Φ τ (a ⇒ b) (acc Φ↓) (acc m↓) | just u | just pu | yes Φ<
      with resolve' Δ (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ)
                    (from-meta (a M./ u)) (Φ↓ _ Φ<)
         | sound'   Δ (step Φ Δ (from-meta (a M./ u)) (from-meta (b M./ u)) τ)
                    (from-meta (a M./ u)) (Φ↓ _ Φ<)
    match'-sound Δ Φ τ (a ⇒ b) (acc Φ↓) (acc m↓) | just u | just pu | yes Φ< | true | true px =
      just (i-iabs Φ< px pu)
    match'-sound Δ Φ τ (a ⇒ b) (acc Φ↓) (acc m↓) | just u | just pu | yes Φ< | false | false = nothing
    match'-sound Δ Φ τ (a ⇒ b) (acc Φ↓) (acc m↓) | nothing | _ = nothing
    match'-sound Δ Φ τ (∀' r) Φ↓ (acc m↓)
      with match'       Δ Φ τ (open-meta r) Φ↓ (m↓ _ (open-meta-a-m<-∀'a r))
         | match'-sound Δ Φ τ (open-meta r) Φ↓ (m↓ _ (open-meta-a-m<-∀'a r))
    match'-sound Δ Φ τ (∀' r) Φ↓ (acc m↓) | just (u ∷ us) | just px = just (open-↓-∀ r τ u us px)
    match'-sound Δ Φ τ (∀' r) Φ↓ (acc m↓) | nothing | nothing = nothing
    match'-sound Δ Φ τ (simpl x) Φ↓ m↓ with mgu (simpl x) τ | mgu.sound (simpl x) τ
    match'-sound Δ Φ τ (simpl x) Φ↓ m↓ | just _ | just px =
      just (subst (λ z → Δ , Φ ⊢ z ↓ τ) (sym px) (i-simp τ))
    match'-sound Δ Φ τ (simpl x) Φ↓ m↓ | nothing | nothing = nothing

    match-sound : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) τ r → (Φ↓ : T-Acc Φ) →
                  Bl.All (Δ , Φ ⊢ r ↓ τ) (match Δ Φ τ r Φ↓)
    match-sound Δ Φ τ r Φ↓ with
      match' Δ Φ τ (to-meta {zero} r) Φ↓ (m<-well-founded _) |
      match'-sound Δ Φ τ (to-meta {zero} r) Φ↓ (m<-well-founded _)
    match-sound Δ Φ τ r Φ↓ | just x | just px = true (subst (λ z → Δ , Φ ⊢ z ↓ τ) eq px)
      where
        eq : ∀ {ν} {a : Type ν} {s} → from-meta (to-meta {zero} a M./ s) ≡ a
        eq {a = a} {s = []} = begin
            from-meta (M._/_ (to-meta {zero} a) [])
          ≡⟨ cong (λ q → from-meta q) (MetaTypeMetaLemmas.id-vanishes (to-meta {zero} a)) ⟩
            from-meta (to-meta {zero} a)
          ≡⟨ to-meta-zero-vanishes ⟩
            a ∎
    match-sound Δ Φ τ r Φ↓ | nothing | q = false

    match1st-sound : ∀ {ν} (Δ : ICtx ν) (Φ : TCtx) (ρs : ICtx ν) → (τ : SimpleType ν) → (Φ↓ : T-Acc Φ) →
                    Bl.All (∃ λ r → (r ∈ ρs) × (Δ , Φ ⊢ r ↓ τ)) (match1st Δ Φ ρs τ Φ↓)
    match1st-sound Δ Φ [] τ Φ↓ = false
    match1st-sound Δ Φ (x ∷ ρs) τ Φ↓ with match Δ Φ τ x Φ↓ | match-sound Δ Φ τ x Φ↓
    match1st-sound Δ Φ (x ∷ ρs) τ Φ↓ | true | true px = true (x , (here refl , px))
    match1st-sound Δ Φ (x ∷ ρs) τ Φ↓ | false | false =
      all-map (match1st-sound Δ Φ ρs τ Φ↓) (λ{ (r , r∈ρs , r↓τ) → r , ((there r∈ρs) , r↓τ) })

    sound' : ∀ {ν} (Δ : ICtx ν) Φ r → (Φ↓ : T-Acc Φ) → Bl.All (Δ , Φ ⊢ᵣ r) (resolve' Δ Φ r Φ↓)
    sound' Δ Φ (simpl x) Φ↓ =
      all-map (match1st-sound Δ Φ Δ x Φ↓) (λ{ (r , r∈Δ , r↓τ) → r-simp r∈Δ r↓τ })
    sound' Δ Φ (a ⇒ b) Φ↓ = all-map (sound' (a ∷ Δ) Φ b Φ↓) (λ x → r-iabs a x)
    sound' Δ Φ (∀' r) Φ↓ = all-map (sound' (ictx-weaken Δ) Φ r Φ↓) r-tabs

    sound : ∀ {ν} (Δ : ICtx ν) r {Φ} → (Φ↓ : T-Acc Φ) → Bl.All (Δ , Φ ⊢ᵣ r) (resolve Δ Φ r)
    sound Δ r Φ↓ = sound' Δ _ r (wf-< _)


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

data ⊥ : Set where

[_] : {A : Set} → A → List A
[ x ] = x ∷ []

vArg : {A : Set} → A → Arg A
vArg x = arg (arg-info visible relevant) x

macro
  getFunDef : Name → Term → TC ⊤
  getFunDef f hole = do
    function cls ← getDefinition f
      where _ → error
    niceCls ← quoteTC cls
    unify hole niceCls

    where postulate error : _

noooo : (A : Set) → ⊥ → A
noooo A ()

-- Quoting the term `noooo Nat x` used to produce an absurd lambda
-- applied to `x`, which should not happen. It should instead simply
-- keep the term `noooo Nat x` as-is.
fooooo : ⊥ → Nat
fooooo x = noooo Nat x

test : getFunDef fooooo ≡
         [
           clause [ "x" , vArg (def (quote ⊥) []) ] [ vArg (var 0) ]
                  (def (quote noooo) (vArg (def (quote Nat) []) ∷ vArg (var 0 []) ∷ []))
         ]
test = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Sublist-related properties
------------------------------------------------------------------------

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

module Data.List.Relation.Binary.Sublist.Propositional.Properties
  {a} {A : Set a} where

open import Data.List.Base using (List; []; _∷_;  map)
open import Data.List.Membership.Propositional using (_∈_)
open import Data.List.Relation.Unary.All using (All; []; _∷_)
open import Data.List.Relation.Unary.Any using (Any; here; there)
open import Data.List.Relation.Unary.Any.Properties
  using (here-injective; there-injective)
open import Data.List.Relation.Binary.Sublist.Propositional
  hiding (map)
import Data.List.Relation.Binary.Sublist.Setoid.Properties
  as SetoidProperties
open import Data.Product using (∃; _,_; proj₂)
open import Function.Base
open import Level using (Level)
open import Relation.Binary using (_Respects_)
open import Relation.Binary.PropositionalEquality
open import Relation.Unary using (Pred)

private
  variable
    b ℓ : Level
    B : Set b

------------------------------------------------------------------------
-- Re-exporting setoid properties

open SetoidProperties (setoid A) public
  hiding (map⁺)

map⁺ : ∀ {as bs} (f : A → B) → as ⊆ bs → map f as ⊆ map f bs
map⁺ {B = B} f = SetoidProperties.map⁺ (setoid A) (setoid B) (cong f)

------------------------------------------------------------------------
-- Category laws for _⊆_

⊆-trans-idˡ : ∀ {xs ys : List A} {τ : xs ⊆ ys} →
              ⊆-trans ⊆-refl τ ≡ τ
⊆-trans-idˡ {_}     {τ = []    } = refl
⊆-trans-idˡ {_}     {τ = _ ∷  _} = cong (_ ∷_ ) ⊆-trans-idˡ
⊆-trans-idˡ {[]}    {τ = _ ∷ʳ _} = cong (_ ∷ʳ_) ⊆-trans-idˡ
⊆-trans-idˡ {_ ∷ _} {τ = _ ∷ʳ _} = cong (_ ∷ʳ_) ⊆-trans-idˡ

⊆-trans-idʳ : ∀ {xs ys : List A} {τ : xs ⊆ ys} →
              ⊆-trans τ ⊆-refl ≡ τ
⊆-trans-idʳ {τ = []      } = refl
⊆-trans-idʳ {τ = _ ∷ʳ _  } = cong (_  ∷ʳ_ ) ⊆-trans-idʳ
⊆-trans-idʳ {τ = refl ∷ _} = cong (refl ∷_) ⊆-trans-idʳ

-- Note: The associativity law is oriented such that rewriting with it
-- may trigger reductions of ⊆-trans, which matches first on its
-- second argument and then on its first argument.

⊆-trans-assoc : ∀ {ws xs ys zs : List A}
                {τ₁ : ws ⊆ xs} {τ₂ : xs ⊆ ys} {τ₃ : ys ⊆ zs} →
                ⊆-trans τ₁ (⊆-trans τ₂ τ₃) ≡ ⊆-trans (⊆-trans τ₁ τ₂) τ₃
⊆-trans-assoc {τ₁ = _}        {_}      {_ ∷ʳ _} = cong (_ ∷ʳ_) ⊆-trans-assoc
⊆-trans-assoc {τ₁ = _}        {_ ∷ʳ _} {_ ∷  _} = cong (_ ∷ʳ_) ⊆-trans-assoc
⊆-trans-assoc {τ₁ = _ ∷ʳ _  } {_ ∷  _} {_ ∷  _} = cong (_ ∷ʳ_) ⊆-trans-assoc
⊆-trans-assoc {τ₁ = refl ∷ _} {_ ∷  _} {_ ∷  _} = cong (_ ∷_ ) ⊆-trans-assoc
⊆-trans-assoc {τ₁ = []}       {[]}     {[]}     = refl

------------------------------------------------------------------------
-- Laws concerning ⊆-trans and ∷ˡ⁻

⊆-trans-∷ˡ⁻ᵣ : ∀ {y} {xs ys zs : List A} {τ : xs ⊆ ys} {σ : (y ∷ ys) ⊆ zs} →
               ⊆-trans τ (∷ˡ⁻ σ) ≡ ⊆-trans (y ∷ʳ τ) σ
⊆-trans-∷ˡ⁻ᵣ {σ = x ∷ σ} = refl
⊆-trans-∷ˡ⁻ᵣ {σ = y ∷ʳ σ} = cong (y ∷ʳ_) ⊆-trans-∷ˡ⁻ᵣ

⊆-trans-∷ˡ⁻ₗ : ∀ {x} {xs ys zs : List A} {τ : (x ∷ xs) ⊆ ys} {σ : ys ⊆ zs} →
              ⊆-trans (∷ˡ⁻ τ) σ ≡ ∷ˡ⁻ (⊆-trans τ σ)
⊆-trans-∷ˡ⁻ₗ                {σ = y   ∷ʳ σ} = cong (y ∷ʳ_) ⊆-trans-∷ˡ⁻ₗ
⊆-trans-∷ˡ⁻ₗ {τ = y   ∷ʳ τ} {σ = refl ∷ σ} = cong (y ∷ʳ_) ⊆-trans-∷ˡ⁻ₗ
⊆-trans-∷ˡ⁻ₗ {τ = refl ∷ τ} {σ = refl ∷ σ} = refl

⊆-∷ˡ⁻trans-∷ : ∀ {y} {xs ys zs : List A} {τ : xs ⊆ ys} {σ : (y ∷ ys) ⊆ zs} →
               ∷ˡ⁻ (⊆-trans (refl ∷ τ) σ) ≡ ⊆-trans (y ∷ʳ τ) σ
⊆-∷ˡ⁻trans-∷ {σ = y   ∷ʳ σ} = cong (y ∷ʳ_) ⊆-∷ˡ⁻trans-∷
⊆-∷ˡ⁻trans-∷ {σ = refl ∷ σ} = refl

------------------------------------------------------------------------
-- Relationships to other predicates

-- All P is a contravariant functor from _⊆_ to Set.

All-resp-⊆ : {P : Pred A ℓ} → (All P) Respects _⊇_
All-resp-⊆ []          []       = []
All-resp-⊆ (_    ∷ʳ p) (_ ∷ xs) = All-resp-⊆ p xs
All-resp-⊆ (refl ∷  p) (x ∷ xs) = x ∷ All-resp-⊆ p xs

-- Any P is a covariant functor from _⊆_ to Set.

Any-resp-⊆ : {P : Pred A ℓ} → (Any P) Respects _⊆_
Any-resp-⊆ = lookup

------------------------------------------------------------------------
-- Functor laws for All-resp-⊆

-- First functor law: identity.

All-resp-⊆-refl : ∀ {P : Pred A ℓ} {xs : List A} →
                  All-resp-⊆ ⊆-refl ≗ id {A = All P xs}
All-resp-⊆-refl []       = refl
All-resp-⊆-refl (p ∷ ps) = cong (p ∷_) (All-resp-⊆-refl ps)

-- Second functor law: composition.

All-resp-⊆-trans : ∀ {P : Pred A ℓ} {xs ys zs} {τ : xs ⊆ ys} (τ′ : ys ⊆ zs) →
                   All-resp-⊆ {P = P} (⊆-trans τ τ′) ≗ All-resp-⊆ τ ∘ All-resp-⊆ τ′
All-resp-⊆-trans                (_    ∷ʳ τ′) (p ∷ ps) = All-resp-⊆-trans τ′ ps
All-resp-⊆-trans {τ = _ ∷ʳ _  } (refl ∷  τ′) (p ∷ ps) = All-resp-⊆-trans τ′ ps
All-resp-⊆-trans {τ = refl ∷ _} (refl ∷  τ′) (p ∷ ps) = cong (p ∷_) (All-resp-⊆-trans τ′ ps)
All-resp-⊆-trans {τ = []      } ([]        ) []       = refl

------------------------------------------------------------------------
-- Functor laws for Any-resp-⊆ / lookup

-- First functor law: identity.

Any-resp-⊆-refl : ∀ {P : Pred A ℓ} {xs} →
                  Any-resp-⊆ ⊆-refl ≗ id {A = Any P xs}
Any-resp-⊆-refl (here p)  = refl
Any-resp-⊆-refl (there i) = cong there (Any-resp-⊆-refl i)

lookup-⊆-refl = Any-resp-⊆-refl

-- Second functor law: composition.

Any-resp-⊆-trans : ∀ {P : Pred A ℓ} {xs ys zs} {τ : xs ⊆ ys} (τ′ : ys ⊆ zs) →
                   Any-resp-⊆ {P = P} (⊆-trans τ τ′) ≗ Any-resp-⊆ τ′ ∘ Any-resp-⊆ τ
Any-resp-⊆-trans                (_ ∷ʳ τ′) i         = cong there (Any-resp-⊆-trans τ′ i)
Any-resp-⊆-trans {τ = _   ∷ʳ _} (_ ∷  τ′) i         = cong there (Any-resp-⊆-trans τ′ i)
Any-resp-⊆-trans {τ = _    ∷ _} (_ ∷  τ′) (there i) = cong there (Any-resp-⊆-trans τ′ i)
Any-resp-⊆-trans {τ = refl ∷ _} (_ ∷  τ′) (here _)  = refl
Any-resp-⊆-trans {τ = []      } []        ()

lookup-⊆-trans = Any-resp-⊆-trans

------------------------------------------------------------------------
-- The `lookup` function for `xs ⊆ ys` is injective.
--
-- Note: `lookup` can be seen as a strictly increasing reindexing function
-- for indices into `xs`, producing indices into `ys`.

lookup-injective : ∀ {P : Pred A ℓ} {xs ys} {τ : xs ⊆ ys} {i j : Any P xs} →
                   lookup τ i ≡ lookup τ j → i ≡ j
lookup-injective {τ = _  ∷ʳ _}                     = lookup-injective ∘′ there-injective
lookup-injective {τ = x≡y ∷ _} {here  _} {here  _} = cong here ∘′ subst-injective x≡y ∘′ here-injective
  -- Note: instead of using subst-injective, we could match x≡y against refl on the lhs.
  -- However, this turns the following clause into a non-strict match.
lookup-injective {τ = _   ∷ _} {there _} {there _} = cong there ∘′ lookup-injective ∘′ there-injective

-------------------------------------------------------------------------
-- from∈ ∘ to∈ turns a sublist morphism τ : x∷xs ⊆ ys into a morphism
-- [x] ⊆ ys.  The same morphism is obtained by pre-composing τ with
-- the canonial morphism [x] ⊆ x∷xs.
--
-- Note: This lemma does not hold for Sublist.Setoid, but could hold for
-- a hypothetical Sublist.Groupoid where trans refl = id.

from∈∘to∈ : ∀ {x : A} {xs ys} (τ : x ∷ xs ⊆ ys) →
            from∈ (to∈ τ) ≡ ⊆-trans (refl ∷ minimum xs) τ
from∈∘to∈ (x≡y ∷ τ) = cong (x≡y ∷_) ([]⊆-irrelevant _ _)
from∈∘to∈ (y  ∷ʳ τ) = cong (y  ∷ʳ_) (from∈∘to∈ τ)

from∈∘lookup : ∀{x : A} {xs ys} (τ : xs ⊆ ys) (i : x ∈ xs) →
               from∈ (lookup τ i) ≡ ⊆-trans (from∈ i) τ
from∈∘lookup (y   ∷ʳ τ)  i          = cong (y ∷ʳ_) (from∈∘lookup τ i)
from∈∘lookup (_    ∷ τ) (there i)   = cong (_ ∷ʳ_) (from∈∘lookup τ i)
from∈∘lookup (refl ∷ τ) (here refl) = cong (refl ∷_) ([]⊆-irrelevant _ _)

------------------------------------------------------------------------
-- Weak pushout (wpo)

-- A raw pushout is a weak pushout if the pushout square commutes.

IsWeakPushout : ∀{xs ys zs : List A} {τ : xs ⊆ ys} {σ : xs ⊆ zs} →
                RawPushout τ σ → Set a
IsWeakPushout {τ = τ} {σ = σ} rpo =
  ⊆-trans τ (RawPushout.leg₁ rpo) ≡
  ⊆-trans σ (RawPushout.leg₂ rpo)

-- Joining two list extensions with ⊆-pushout produces a weak pushout.

⊆-pushoutˡ-is-wpo : ∀{xs ys zs : List A} (τ : xs ⊆ ys) (σ : xs ⊆ zs) →
                    IsWeakPushout (⊆-pushoutˡ τ σ)
⊆-pushoutˡ-is-wpo [] σ
  rewrite ⊆-trans-idʳ {τ = σ}
        = ⊆-trans-idˡ {xs = []}
⊆-pushoutˡ-is-wpo (y   ∷ʳ τ) σ          = cong (y   ∷ʳ_) (⊆-pushoutˡ-is-wpo τ σ)
⊆-pushoutˡ-is-wpo (x≡y  ∷ τ) (z   ∷ʳ σ) = cong (z   ∷ʳ_) (⊆-pushoutˡ-is-wpo (x≡y ∷ τ) σ)
⊆-pushoutˡ-is-wpo (refl ∷ τ) (refl ∷ σ) = cong (refl ∷_) (⊆-pushoutˡ-is-wpo τ σ)

------------------------------------------------------------------------
-- Properties of disjointness

-- From τ₁ ⊎ τ₂ = τ, compute the injection ι₁ such that τ₁ = ⊆-trans ι₁ τ.

DisjointUnion-inj₁ : ∀ {xs ys zs xys : List A} {τ₁ : xs ⊆ zs} {τ₂ : ys ⊆ zs} {τ : xys ⊆ zs} →
                      DisjointUnion τ₁ τ₂ τ → ∃ λ (ι₁ : xs ⊆ xys) → ⊆-trans ι₁ τ ≡ τ₁
DisjointUnion-inj₁ []         = []       , refl
DisjointUnion-inj₁ (y   ∷ₙ d) = _        , cong (y  ∷ʳ_) (proj₂ (DisjointUnion-inj₁ d))
DisjointUnion-inj₁ (x≈y ∷ₗ d) = refl ∷ _ , cong (x≈y ∷_) (proj₂ (DisjointUnion-inj₁ d))
DisjointUnion-inj₁ (x≈y ∷ᵣ d) = _ ∷ʳ _   , cong (_  ∷ʳ_) (proj₂ (DisjointUnion-inj₁ d))

-- From τ₁ ⊎ τ₂ = τ, compute the injection ι₂ such that τ₂ = ⊆-trans ι₂ τ.

DisjointUnion-inj₂ : ∀ {xs ys zs xys : List A} {τ₁ : xs ⊆ zs} {τ₂ : ys ⊆ zs} {τ : xys ⊆ zs} →
                      DisjointUnion τ₁ τ₂ τ → ∃ λ (ι₂ : ys ⊆ xys) → ⊆-trans ι₂ τ ≡ τ₂
DisjointUnion-inj₂ []         = []       , refl
DisjointUnion-inj₂ (y   ∷ₙ d) = _        , cong (y  ∷ʳ_) (proj₂ (DisjointUnion-inj₂ d))
DisjointUnion-inj₂ (x≈y ∷ᵣ d) = refl ∷ _ , cong (x≈y ∷_) (proj₂ (DisjointUnion-inj₂ d))
DisjointUnion-inj₂ (x≈y ∷ₗ d) = _ ∷ʳ _   , cong (_  ∷ʳ_) (proj₂ (DisjointUnion-inj₂ d))

-- A sublist σ disjoint to both τ₁ and τ₂ is an equalizer
-- for the separators of τ₁ and τ₂.

equalize-separators : ∀ {us xs ys zs : List A}
  {σ : us ⊆ zs} {τ₁ : xs ⊆ zs} {τ₂ : ys ⊆ zs} (let s = separateˡ τ₁ τ₂) →
  Disjoint σ τ₁ → Disjoint σ τ₂ →
  ⊆-trans σ (Separation.separator₁ s) ≡
  ⊆-trans σ (Separation.separator₂ s)
equalize-separators [] [] = refl
equalize-separators (y    ∷ₙ d₁) (.y   ∷ₙ d₂) = cong (y ∷ʳ_) (equalize-separators d₁ d₂)
equalize-separators (y    ∷ₙ d₁) (refl ∷ᵣ d₂) = cong (y ∷ʳ_) (equalize-separators d₁ d₂)
equalize-separators (refl ∷ᵣ d₁) (y    ∷ₙ d₂) = cong (y ∷ʳ_) (equalize-separators d₁ d₂)
equalize-separators {τ₁ = refl ∷ _} {τ₂ = refl ∷ _}  -- match here to work around deficiency of Agda's forcing translation
                    (_    ∷ᵣ d₁) (_   ∷ᵣ d₂) = cong (_ ∷ʳ_) (cong (_ ∷ʳ_) (equalize-separators d₁ d₂))
equalize-separators (x≈y  ∷ₗ d₁) (.x≈y ∷ₗ d₂) = cong (trans x≈y refl ∷_) (equalize-separators d₁ d₂)