{-# OPTIONS --safe #-}

open import Generics.Prelude
open import Generics.Telescope
open import Generics.Desc
open import Generics.HasDesc
open import Generics.All

module Generics.Constructions.Mu
  {P I ℓ} (A : Indexed P I ℓ) (H : HasDesc A)
  (open HasDesc H)
  {p} where

private
  variable
    V : ExTele P
    i : ⟦ I ⟧tel p
    v : ⟦ V ⟧tel p

open import Generics.Mu public

toData : (x : ⟦ D ⟧Data A′ (p , i)) → AllDataω Acc D x → μ D (p , i)

to-wf : (x : A′ (p , i)) → Acc x → μ D (p , i)
to-wf x (acc a) = toData (split x) a

toData (k , x) a = ⟨ k , toCon (lookupCon D k) x a ⟩
  where
    toIndArg : (C : ConDesc P V I)
               (x : ⟦ C ⟧IndArg A′ (p , v))
             → AllIndArgω Acc C x
             → ⟦ C ⟧IndArgω (μ D) (p , v)
    toIndArg (var _) x a = to-wf x a
    toIndArg (π ai _ C) x a s = toIndArg C (app< ai > x s) (a s)
    toIndArg (A ⊗ B) (xa , xb) (aa , ab)
      = toIndArg A xa aa , toIndArg B xb ab

    toCon : (C : ConDesc P V I)
            (x : ⟦ C ⟧Con A′ (p , v , i))
          → AllConω Acc C x
          → ⟦ C ⟧Conω (μ D) (p , v , i)
    toCon (var _) refl _ = liftω refl
    toCon (π _ _ C) (s , x) a = s , toCon C x a
    toCon (A ⊗ B) (xa , xb) (aa , ab)
      = toIndArg A xa aa , toCon B xb ab

to : A′ (p , i) → μ D (p , i)
to x = to-wf x (wf x)

from : μ D (p , i) → A′ (p , i)

fromIndArg : (C : ConDesc P V I)
           → ⟦ C ⟧IndArgω (μ D) (p , v)
           → ⟦ C ⟧IndArg A′ (p , v)
fromIndArg (var _) x = from x
fromIndArg (π ai S C) x = fun< ai > λ s → fromIndArg C (x s)
fromIndArg (A ⊗ B) (xa , xb) = fromIndArg A xa , fromIndArg B xb

fromCon : (C : ConDesc P V I)
        → ⟦ C ⟧Conω (μ D) (p , v , i)
        → ⟦ C ⟧Con A′ (p , v , i)
fromCon (var _) (liftω refl) = refl
fromCon (π _ S C) (s , x) = s , fromCon C x
fromCon (A ⊗ B) (xa , xb) = fromIndArg A xa , fromCon B xb

from ⟨ k , x ⟩ = constr (k , fromCon (lookupCon D k) x)


module Issue756a where

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

data T : Nat → Set where
  [_] : ∀ n → T n

bad : ∀ n → T n → Nat
bad .zero                   [ zero  ] = zero
bad .(suc (let x = n in x)) [ suc n ] = zero


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

open import Fragment.Equational.Theory

module Fragment.Equational.Model.Properties (Θ : Theory) where

open import Fragment.Equational.Model.Base Θ
open import Fragment.Equational.Model.Synthetic Θ
open import Fragment.Algebra.Homomorphism (Σ Θ)
open import Fragment.Algebra.Free (Σ Θ)
open import Fragment.Setoid.Morphism using (_↝_)

open import Level using (Level)
open import Function using (_∘_)

open import Data.Nat using (ℕ; zero; suc)
open import Data.Fin using (Fin; zero; suc; fromℕ)
open import Data.Vec using (Vec; []; _∷_; map)
open import Data.Product using (proj₁; proj₂)
open import Data.Vec.Relation.Binary.Pointwise.Inductive
  using (Pointwise; []; _∷_)

open import Relation.Binary using (Setoid)
import Relation.Binary.Reasoning.Setoid as Reasoning

private
  variable
    a ℓ : Level

module _ {n} (A : Model {a} {ℓ}) (θ : Env ∥ A ∥ₐ n) where

  private

    module S = Setoid ∥ A ∥/≈
    open Reasoning ∥ A ∥/≈

    mutual
      map-∣interp∣-cong : ∀ {m} {xs ys : Vec ∥ J n ∥ m}
                          → Pointwise ≈[ J n ] xs ys
                          → Pointwise ≈[ A ] (map ∣ inst ∥ A ∥ₐ θ ∣ xs)
                                             (map ∣ inst ∥ A ∥ₐ θ ∣ ys)
      map-∣interp∣-cong []       = []
      map-∣interp∣-cong (p ∷ ps) = ∣interp∣-cong p ∷ map-∣interp∣-cong ps

      ∣interp∣-cong : Congruent ≈[ J n ] ≈[ A ] ∣ inst ∥ A ∥ₐ θ ∣
      ∣interp∣-cong refl          = S.refl
      ∣interp∣-cong (sym p)       = S.sym (∣interp∣-cong p)
      ∣interp∣-cong (trans p q)   = S.trans (∣interp∣-cong p) (∣interp∣-cong q)
      ∣interp∣-cong (inherit p)   = ∣ inst ∥ A ∥ₐ θ ∣-cong p
      ∣interp∣-cong (cong f {xs = xs} {ys = ys} ps) = begin
          ∣ inst ∥ A ∥ₐ θ ∣ (term f xs)
        ≈⟨ S.sym (∣ inst ∥ A ∥ₐ θ ∣-hom f xs) ⟩
          A ⟦ f ⟧ (map ∣ inst ∥ A ∥ₐ θ ∣ xs)
        ≈⟨ (A ⟦ f ⟧-cong) (map-∣interp∣-cong ps) ⟩
          A ⟦ f ⟧ (map ∣ inst ∥ A ∥ₐ θ ∣ ys)
        ≈⟨ ∣ inst ∥ A ∥ₐ θ ∣-hom f ys ⟩
          ∣ inst ∥ A ∥ₐ θ ∣ (term f ys)
        ∎
      ∣interp∣-cong (axiom eq θ') = begin
          ∣ inst ∥ A ∥ₐ θ ∣ (∣ inst (F n) θ' ∣ lhs)
        ≈⟨ inst-assoc (F n) θ' (inst ∥ A ∥ₐ θ) {x = lhs} ⟩
          ∣ inst ∥ A ∥ₐ (∣ inst ∥ A ∥ₐ θ ∣ ∘ θ') ∣ lhs
        ≈⟨ ∥ A ∥ₐ-models eq _ ⟩
          ∣ inst ∥ A ∥ₐ (∣ inst ∥ A ∥ₐ θ ∣ ∘ θ') ∣ rhs
        ≈⟨ S.sym (inst-assoc (F n) θ' (inst ∥ A ∥ₐ θ) {x = rhs}) ⟩
          ∣ inst ∥ A ∥ₐ θ ∣ (∣ inst (F n) θ' ∣ rhs)
        ∎
        where lhs = proj₁ (Θ ⟦ eq ⟧ₑ)
              rhs = proj₂ (Θ ⟦ eq ⟧ₑ)

    ∣interp∣⃗ : ∥ J n ∥/≈ ↝ ∥ A ∥/≈
    ∣interp∣⃗ = record { ∣_∣      = ∣ inst ∥ A ∥ₐ θ ∣
                       ; ∣_∣-cong = ∣interp∣-cong
                       }

  interp : ∥ J n ∥ₐ ⟿ ∥ A ∥ₐ
  interp = record { ∣_∣⃗    = ∣interp∣⃗
                  ; ∣_∣-hom = ∣ inst ∥ A ∥ₐ θ ∣-hom
                  }

atomise : (n : ℕ) → ∥ J (suc n) ∥
atomise n = atom (dyn (fromℕ n))

up : ∀ {n} → Fin n → Fin (suc n)
up zero    = zero
up (suc n) = suc (up n)

raise : ∀ {n} → ∥ J n ∥ₐ ⟿ ∥ J (suc n) ∥ₐ
raise {n} = interp (J (suc n)) (λ k → atom (dyn (up k)))

step : ∀ {n} → ∥ J n ∥ₐ ⟿ ∥ J (suc n) ∥ₐ
step {n} = interp (J (suc n)) (λ k → atom (dyn (suc k)))

mutual

  map-step-raise : ∀ {n m} → (xs : Vec ∥ J n ∥ m)
                   → Pointwise ≈[ J (suc (suc n)) ]
                               (map ∣ step {suc n} ⊙ raise {n} ∣ xs)
                               (map ∣ raise {suc n} ⊙ step {n} ∣ xs)
  map-step-raise []       = []
  map-step-raise (x ∷ xs) = step-raise {x = x} ∷ map-step-raise xs

  step-raise : ∀ {n} → step {suc n} ⊙ raise {n} ≗ raise {suc n} ⊙ step {n}
  step-raise {n} {atom (dyn k)} = refl
  step-raise {n} {term f xs}    = begin
      ∣ step ⊙ raise ∣ (term f xs)
    ≈⟨ sym (∣ step ⊙ raise ∣-hom f xs) ⟩
      term f (map ∣ step ⊙ raise ∣ xs)
    ≈⟨ cong f (map-step-raise xs) ⟩
      term f (map ∣ raise ⊙ step ∣ xs)
    ≈⟨ ∣ raise ⊙ step ∣-hom f xs ⟩
      ∣ raise ⊙ step ∣ (term f xs)
    ∎
    where open Reasoning ∥ J (suc (suc n)) ∥/≈


module cry.gfp where

-- open import Agda.Primitive using (lsuc; _⊔_)
open import Level

-- import Agda.Builtin.FromNat
open import Agda.Builtin.Bool
open import Agda.Builtin.Equality
open import Algebra.FunctionProperties.Core using (Op₁; Op₂)
open import Relation.Nullary
-- open import Relation.Binary.Core using (Rel; Decidable; Substitutive)
-- open import Relation.Binary.PropositionalEquality.Core using (subst)
Rel : ∀ {a} → Set a → (ℓ : Level) → Set (a ⊔ suc ℓ)
Rel A ℓ = A → A → Set ℓ
Decidable : ∀ {a ℓ} {A : Set a} → Rel A ℓ → Set _
Decidable _∼_ = ∀ x y → Dec (x ∼ y)
_Respects_ : ∀ {a ℓ₁ ℓ₂} {A : Set a} → (A → Set ℓ₁) → Rel A ℓ₂ → Set _
P Respects _∼_ = ∀ {x y} → x ∼ y → P x → P y
Substitutive : ∀ {a ℓ₁} {A : Set a} → Rel A ℓ₁ → (ℓ₂ : Level) → Set _
Substitutive {A = A} _∼_ p = (P : A → Set p) → P Respects _∼_

subst : ∀ {a p} {A : Set a} → Substitutive (_≡_ {A = A}) p
subst P refl p = p

record RawField c ℓ : Set (suc (c ⊔ ℓ)) where
  infix  10 _⁻¹
  infix  9 -_
  infixl 8 _²
  infixl 7 _*_
  infixl 6 _+_ _-_
  infix  4 _≈_ _≈?_
  field
    Carrier : Set c
    _≈_     : Rel Carrier ℓ
    _≈?_    : Decidable _≈_
    _?≈_    : Carrier → Carrier → Bool
    _+_     : Op₂ Carrier
    _-_     : Op₂ Carrier
    _*_     : Op₂ Carrier
    _²      : Op₁ Carrier
    -_      : Op₁ Carrier
    _⁻¹     : Op₁ Carrier
    0#      : Carrier
    1#      : Carrier

open import Agda.Builtin.Nat as N using () renaming (Nat to ℕ)

_mod_ : (dividend divisor : ℕ) → ℕ
(d mod 0) = 0
(d mod N.suc s) = N.mod-helper 0 s d s

_div_ : (dividend divisor : ℕ) → ℕ
(d div 0) = 0
(d div N.suc s) = N.div-helper 0 s d s

{-# TERMINATING #-}
times : ∀ {a} {A : Set a} →
  (one : A) (dbl : A → A) (add : A → A → A) →
  A → ℕ → A
times {A = A} one dbl add = mul where
  mul : A → ℕ → A
  f : A → ℕ → A
  -- f u n = uⁿ
  g : A → A → ℕ → A
  -- g t u n = uⁿ t

  mul x 0 = one
  mul x n = f x n

  f u 1 = u
  f u n with n mod 2
  ... | 0 = f (dbl u) (n div 2)
  ... | _ = g u (dbl u) ((n N.- 1) div 2)

  g t u 1 = add t u
  g t u n with n mod 2
  ... | 0 = g t (dbl u) (n div 2)
  ... | _ = g (add t u) (dbl u) ((n N.- 1) div 2)

module 𝔽ₚ (p : ℕ) where
    𝔽 : Set
    𝔽 = ℕ

    to𝔽 : ℕ → 𝔽
    to𝔽 i = i mod p

    _==_ : 𝔽 → 𝔽 → Set
    x == y = x ≡ y

    open N using (zero; suc)
    pred : ℕ → ℕ
    pred zero = 0
    pred (suc n) = n

    _≟_ : (x y : 𝔽) → Dec (x == y)
    zero  ≟ zero   = yes refl
    suc m ≟ suc n  with m ≟ n
    suc m ≟ suc .m | yes refl = yes refl
    suc m ≟ suc n  | no prf   = no λ x → prf ((λ p → subst (λ x → m ≡ pred x) p refl) x)
    zero  ≟ suc n  = no λ()
    suc m ≟ zero   = no λ()

    infix  10 _⁻¹
    infix  9 -_
    infixl 8 _²
    infixl 7 _*_
    infixl 6 _+_ _-_

    _+_ : 𝔽 → 𝔽 → 𝔽
    n + m = to𝔽 (n N.+ m)

    _*_ : 𝔽 → 𝔽 → 𝔽
    n * m = to𝔽 (n N.* m)

    _² : 𝔽 → 𝔽
    x ² = x * x

    -_ : 𝔽 → 𝔽
    - 0 = 0
    - N.suc n = (p N.- N.suc n)

    _-_ : 𝔽 → 𝔽 → 𝔽
    x - y = x + (- y)

    0# : 𝔽
    0# = 0

    1# : 𝔽
    1# = 1

    _^_ : 𝔽 → ℕ → 𝔽
    _^_ = times 1# _² _*_

    _⁻¹ : 𝔽 → 𝔽
    x ⁻¹ = x ^ (p N.- 2)

gfp : ℕ → RawField _ _
gfp p = record
  { Carrier = 𝔽
  ; _≈_ = _==_
  ; _≈?_ = _≟_
  ; _?≈_ = N._==_
  ; _+_ = _+_
  ; _-_ = _-_
  ; _*_ = _*_
  ; _² = _²
  ; -_ = -_
  ; _⁻¹ = _⁻¹
  ; 0# = 0#
  ; 1# = 1#
  } where open 𝔽ₚ p


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

open import lib.Basics
open import lib.types.Paths
open import lib.types.Coproduct

open import Graphs.Definition

open import Coequalizers.Definition

module Coequalizers.TrivialExtension where

module TExtensionCoeq-l {i : ULevel} {V : Type i} (v : V) where
  instance
    tegph : Graph ⊤ (V ⊔ ⊤)
    tegph = record { π₀ = λ _ → inl v ; π₁ = λ _ → inr unit }

  extn-equiv : V ≃ ((V ⊔ ⊤) / ⊤)
  extn-equiv = equiv ((c[_] ∘ inl)) (((Coeq-rec (⊔-rec (idf V) (λ _ → v)) λ e → idp))) ((Coeq-elim _ (λ { (inl v') → idp ; (inr x) → quot x}) λ {unit → ↓-app=idf-in (∙'-unit-l _ ∙ ! lem ∙ᵣ quot unit)}))
                      λ _ → idp
    where
      lem : ap (c[_] ∘ inl ∘ (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp ))) (quot unit) == idp
      lem =
        ap (c[_] ∘ inl ∘ (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp ))) (quot unit)
          =⟨ ap-∘ (c[_] ∘ inl) (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp )) (quot unit) ⟩
        ap (c[_] ∘ inl) (ap (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp )) (quot unit))
          =⟨ ap (ap (c[_] ∘ inl)) (Coeq-rec-β= _ _ _) ⟩
        ap (c[_] ∘ inl)  idp
          =⟨ idp ⟩
        idp
          =∎

module TExtensionCoeq-r {i : ULevel} {V : Type i} (v : V) where
  instance
    tegph : Graph ⊤ (V ⊔ ⊤)
    tegph = record { π₀ = λ _ → inr unit ; π₁ = λ _ → inl v }

  extn-equiv : V ≃ ((V ⊔ ⊤) / ⊤)
  extn-equiv = equiv ((c[_] ∘ inl)) (((Coeq-rec (⊔-rec (idf V) (λ _ → v)) λ e → idp))) ((Coeq-elim _ (λ { (inl v') → idp ; (inr x) → ! (quot x)}) λ {unit → ↓-app=idf-in ((!-inv'-l (quot unit) ∙ ! lem ∙ ! (∙-unit-r _)))}))
                      λ _ → idp
    where
      lem : ap (c[_] ∘ inl ∘ (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp ))) (quot unit) == idp
      lem =
        ap (c[_] ∘ inl ∘ (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp ))) (quot unit)
          =⟨ ap-∘ (c[_] ∘ inl) (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp )) (quot unit) ⟩
        ap (c[_] ∘ inl) (ap (Coeq-rec (⊔-rec (λ x → x) (λ _ → v)) (λ e → idp )) (quot unit))
          =⟨ ap (ap (c[_] ∘ inl)) (Coeq-rec-β= _ _ _) ⟩
        ap (c[_] ∘ inl)  idp
          =⟨ idp ⟩
        idp
          =∎


module Relation.Ternary.Separation.Monad.Reader where

open import Level
open import Function using (_∘_; case_of_)
open import Relation.Binary.PropositionalEquality using (refl)
open import Relation.Unary
open import Relation.Unary.PredicateTransformer using (PT)
open import Relation.Ternary.Separation
open import Relation.Ternary.Separation.Morphisms
open import Relation.Ternary.Separation.Monad
open import Relation.Ternary.Separation.Allstar

open import Data.Product
open import Data.List hiding (concat; lookup)
open import Data.Unit

private
  variable
    ℓv : Level
    A  : Set ℓv
    Γ Γ₁ Γ₂ Γ₃ : List A

{- Something not unlike a indexed relative monad transformer in a bicartesian closed category -}
module ReaderTransformer {ℓ}
  -- types
  {T : Set ℓ}
  -- runtime resource
  {C : Set ℓ} {{rc : RawSep C}} {u} {{sc : IsUnitalSep rc u}} {{cc : IsConcattative rc}}
  --
  {B : Set ℓ} {{rb : RawSep B}}
  (j : Morphism C B) {{sb : IsUnitalSep rb (Morphism.j j u)}}
  (V : T → Pred C ℓ) -- values
  (M : PT C B ℓ ℓ)
  {{monad : Monads.Monad {{jm = j}} ⊤ ℓ (λ _ _ → M) }}
  where

  open Morphism j hiding (j) public
  open Monads {{jm = j}} using (Monad; str; typed-str)
  open import Relation.Ternary.Separation.Construct.List T

  module _ where
    open Monad monad

    variable
      P Q R : Pred C ℓ

    Reader : ∀ (Γ₁ Γ₂ : List T) (P : Pred C ℓ) → Pred B ℓ
    Reader Γ₁ Γ₂ P = J (Allstar V Γ₁) ─✴ M (P ✴ Allstar V Γ₂)

    instance
      reader-monad : Monad (List T) _ Reader
      app (Monad.return reader-monad px) (inj e) s = return px &⟨ s ⟩ e
      app (app (Monad.bind reader-monad f) mp σ₁) env σ₂ =
        let _ , σ₃ , σ₄ = ⊎-assoc σ₁ σ₂ in
        app (bind (wand λ where
          (px ×⟨ σ₅ ⟩ env') σ₆ →
            let _ , τ₁ , τ₂ = ⊎-unassoc σ₆ (j-⊎ σ₅) in
            app (app f px τ₁) (inj env') τ₂)) (app mp env σ₄) σ₃

    frame : Γ₁ ⊎ Γ₃ ≣ Γ₂ → ∀[ Reader Γ₁ ε P ⇒ Reader Γ₂ Γ₃ P ]
    app (frame sep c) (inj env) σ = do
      let E₁ ×⟨ σ₁ ⟩ E₂ = repartition sep env
      let Φ , σ₂ , σ₃   = ⊎-unassoc σ (j-⊎ σ₁)
      (v ×⟨ σ₄ ⟩ nil) ×⟨ σ₅ ⟩ E₃ ← app c (inj E₁) σ₂ &⟨ Allstar _ _ ∥ σ₃ ⟩ E₂
      case ⊎-id⁻ʳ σ₄ of λ where
        refl → return (v ×⟨ σ₅ ⟩ E₃)

    ask : ε[ Reader Γ ε (Allstar V Γ) ]
    app ask (inj env) σ with ⊎-id⁻ˡ σ
    ... | refl = return (env ×⟨ ⊎-idʳ ⟩ nil)

    prepend : ∀[ Allstar V Γ₁ ⇒ⱼ Reader Γ₂ (Γ₁ ∙ Γ₂) Emp ]
    app (prepend env₁) (inj env₂) s with j-⊎⁻ s
    ... | _ , refl , s' = return (empty ×⟨ ⊎-idˡ ⟩ (concat (env₁ ×⟨ s' ⟩ env₂)))

    append : ∀[ Allstar V Γ₁ ⇒ⱼ Reader Γ₂ (Γ₂ ∙ Γ₁) Emp ]
    app (append env₁) (inj env₂) s with j-⊎⁻ s
    ... | _ , refl , s' = return (empty ×⟨ ⊎-idˡ ⟩ (concat (✴-swap (env₁ ×⟨ s' ⟩ env₂))))

    liftM : ∀[ M P ⇒ Reader Γ Γ P ]
    app (liftM mp) (inj env) σ = do
      mp &⟨ σ ⟩ env

    runReader : ∀[ Allstar V Γ ⇒ⱼ Reader Γ ε P ─✴ M P ]
    app (runReader env) mp σ = do
      px ×⟨ σ ⟩ nil ← app mp (inj env) (⊎-comm σ)
      case ⊎-id⁻ʳ σ of λ where
        refl → return px

  module _ where
    open Monad reader-monad

    lookup : ∀ {a} → ε[ Reader [ a ] [] (V a) ]
    lookup = do
      v :⟨ σ ⟩: nil ← ask
      case ⊎-id⁻ʳ σ of λ where
        refl → return v

module ReaderMonad {ℓ}
  -- types
  {T : Set ℓ}
  -- runtime resource
  {C : Set ℓ} {{rc : RawSep C}} {u} {{sc : IsUnitalSep rc u}} {{cc : IsConcattative rc}}
  -- values
  (V : T → Pred C ℓ)
  where

  open import Relation.Ternary.Separation.Monad.Identity
  open ReaderTransformer id-morph V Identity.Id {{ monad = Identity.id-monad }} public


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

open import homotopy.3x3.Common
open import homotopy.3x3.PushoutPushout

module homotopy.3x3.Transpose where

open M using (Pushout^2)

type-of : ∀ {i} {A : Type i} (u : A) → Type i
type-of {A = A} _ = A

module _ {i} (d : Span^2 {i}) where

  open Span^2 d

  transpose-f : (f : type-of H₁₁ → _) (g : type-of H₃₃ → _) → Span^2
  transpose-f f g = span^2 A₀₀ A₂₀ A₄₀ A₀₂ A₂₂ A₄₂ A₀₄ A₂₄ A₄₄
                     f₁₀ f₃₀ f₁₂ f₃₂ f₁₄ f₃₄ f₀₁ f₀₃ f₂₁ f₂₃ f₄₁ f₄₃
                     (f H₁₁) H₃₁ H₁₃ (g H₃₃)

  transpose : Span^2
  transpose = span^2 A₀₀ A₂₀ A₄₀ A₀₂ A₂₂ A₄₂ A₀₄ A₂₄ A₄₄
                     f₁₀ f₃₀ f₁₂ f₃₂ f₁₄ f₃₄ f₀₁ f₀₃ f₂₁ f₂₃ f₄₁ f₄₃
                     (! ∘ H₁₁) H₃₁ H₁₃ (! ∘ H₃₃)

ch1 : ∀ {i j} {A : Type i} {x y : A} {p : x == y} {f g : A → Type j} {a : f x → g x} {b : f y → g y}
  → (a == b [ (λ u → f u → g u) ↓ p ]) → (a == b [ (λ u → fst u → snd u) ↓ pair×= (ap f p) (ap g p)])
ch1 {p = idp} α = α

ch2 : ∀ {i j} {X : Type i} {x y : X} {p : x == y}
  {A B₁ B₂ C : X → Type j}
  {f₁ : (x : X) → A x → B₁ x} {g₁ : (x : X) → B₁ x → C x}
  {f₂ : (x : X) → A x → B₂ x} {g₂ : (x : X) → B₂ x → C x}
  {a : _} {b : _}
  → (a == b [ (λ z → (x : A z) → g₁ z (f₁ z x) == g₂ z (f₂ z x)) ↓ p ])
  → (a == b [ (λ u → (x : SquareFunc.A u) → SquareFunc.g₁ u (SquareFunc.f₁ u x) == SquareFunc.g₂ u (SquareFunc.f₂ u x))
            ↓ square=-raw (ap A p) (ap B₁ p) (ap B₂ p) (ap C p) (ch1 (apd f₁ p)) (ch1 (apd f₂ p)) (ch1 (apd g₁ p)) (ch1 (apd g₂ p)) ])
ch2 {p = idp} α = α

ap-span^2=-priv : ∀ {i} {d d' : Span^2 {i}} (p : d == d')
    → ap transpose p == span^2=-raw (ap Span^2.A₀₀ p) (ap Span^2.A₂₀ p) (ap Span^2.A₄₀ p)
                                    (ap Span^2.A₀₂ p) (ap Span^2.A₂₂ p) (ap Span^2.A₄₂ p)
                                    (ap Span^2.A₀₄ p) (ap Span^2.A₂₄ p) (ap Span^2.A₄₄ p)
                                    (ch1 (apd Span^2.f₁₀ p)) (ch1 (apd Span^2.f₃₀ p))
                                    (ch1 (apd Span^2.f₁₂ p)) (ch1 (apd Span^2.f₃₂ p))
                                    (ch1 (apd Span^2.f₁₄ p)) (ch1 (apd Span^2.f₃₄ p))
                                    (ch1 (apd Span^2.f₀₁ p)) (ch1 (apd Span^2.f₀₃ p))
                                    (ch1 (apd Span^2.f₂₁ p)) (ch1 (apd Span^2.f₂₃ p))
                                    (ch1 (apd Span^2.f₄₁ p)) (ch1 (apd Span^2.f₄₃ p))
                                    (ch2 (ap↓ (λ u → ! ∘ u) (apd Span^2.H₁₁ p))) (ch2 (apd Span^2.H₃₁ p))
                                    (ch2 (apd Span^2.H₁₃ p)) (ch2 (ap↓ (λ u → ! ∘ u) (apd Span^2.H₃₃ p)))
ap-span^2=-priv {i} {d} {.d} idp = idp

ap-span^2=-priv2 : ∀ {i}
  {A₀₀ A₀₀' : Type i} (eq-A₀₀ : A₀₀ == A₀₀')
  {A₀₂ A₀₂' : Type i} (eq-A₀₂ : A₀₂ == A₀₂')
  {A₀₄ A₀₄' : Type i} (eq-A₀₄ : A₀₄ == A₀₄')
  {A₂₀ A₂₀' : Type i} (eq-A₂₀ : A₂₀ == A₂₀')
  {A₂₂ A₂₂' : Type i} (eq-A₂₂ : A₂₂ == A₂₂')
  {A₂₄ A₂₄' : Type i} (eq-A₂₄ : A₂₄ == A₂₄')
  {A₄₀ A₄₀' : Type i} (eq-A₄₀ : A₄₀ == A₄₀')
  {A₄₂ A₄₂' : Type i} (eq-A₄₂ : A₄₂ == A₄₂')
  {A₄₄ A₄₄' : Type i} (eq-A₄₄ : A₄₄ == A₄₄')
  {f₀₁ : A₀₂ → A₀₀} {f₀₁' : A₀₂' → A₀₀'} (eq-f₀₁ : f₀₁ == f₀₁' [ (λ u → fst u → snd u) ↓ pair×= eq-A₀₂ eq-A₀₀ ])
  {f₀₃ : A₀₂ → A₀₄} {f₀₃' : A₀₂' → A₀₄'} (eq-f₀₃ : f₀₃ == f₀₃' [ (λ u → fst u → snd u) ↓ pair×= eq-A₀₂ eq-A₀₄ ])
  {f₂₁ : A₂₂ → A₂₀} {f₂₁' : A₂₂' → A₂₀'} (eq-f₂₁ : f₂₁ == f₂₁' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₂ eq-A₂₀ ])
  {f₂₃ : A₂₂ → A₂₄} {f₂₃' : A₂₂' → A₂₄'} (eq-f₂₃ : f₂₃ == f₂₃' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₂ eq-A₂₄ ])
  {f₄₁ : A₄₂ → A₄₀} {f₄₁' : A₄₂' → A₄₀'} (eq-f₄₁ : f₄₁ == f₄₁' [ (λ u → fst u → snd u) ↓ pair×= eq-A₄₂ eq-A₄₀ ])
  {f₄₃ : A₄₂ → A₄₄} {f₄₃' : A₄₂' → A₄₄'} (eq-f₄₃ : f₄₃ == f₄₃' [ (λ u → fst u → snd u) ↓ pair×= eq-A₄₂ eq-A₄₄ ])
  {f₁₀ : A₂₀ → A₀₀} {f₁₀' : A₂₀' → A₀₀'} (eq-f₁₀ : f₁₀ == f₁₀' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₀ eq-A₀₀ ])
  {f₃₀ : A₂₀ → A₄₀} {f₃₀' : A₂₀' → A₄₀'} (eq-f₃₀ : f₃₀ == f₃₀' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₀ eq-A₄₀ ])
  {f₁₂ : A₂₂ → A₀₂} {f₁₂' : A₂₂' → A₀₂'} (eq-f₁₂ : f₁₂ == f₁₂' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₂ eq-A₀₂ ])
  {f₃₂ : A₂₂ → A₄₂} {f₃₂' : A₂₂' → A₄₂'} (eq-f₃₂ : f₃₂ == f₃₂' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₂ eq-A₄₂ ])
  {f₁₄ : A₂₄ → A₀₄} {f₁₄' : A₂₄' → A₀₄'} (eq-f₁₄ : f₁₄ == f₁₄' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₄ eq-A₀₄ ])
  {f₃₄ : A₂₄ → A₄₄} {f₃₄' : A₂₄' → A₄₄'} (eq-f₃₄ : f₃₄ == f₃₄' [ (λ u → fst u → snd u) ↓ pair×= eq-A₂₄ eq-A₄₄ ])
  {H₁₁ : (x : A₂₂) → f₁₀ (f₂₁ x) == f₀₁ (f₁₂ x)} {H₁₁' : (x : A₂₂') → f₁₀' (f₂₁' x) == f₀₁' (f₁₂' x)}
  (eq-H₁₁ : H₁₁ == H₁₁' [ (λ u → ((x : SquareFunc.A u) → SquareFunc.g₁ u (SquareFunc.f₁ u x) == SquareFunc.g₂ u (SquareFunc.f₂ u x)))
                        ↓ square=-raw eq-A₂₂ eq-A₂₀ eq-A₀₂ eq-A₀₀ eq-f₂₁ eq-f₁₂ eq-f₁₀ eq-f₀₁ ])
  {H₁₃ : (x : A₂₂) → f₀₃ (f₁₂ x) == f₁₄ (f₂₃ x)} {H₁₃' : (x : A₂₂') → f₀₃' (f₁₂' x) == f₁₄' (f₂₃' x)}
  (eq-H₁₃ : H₁₃ == H₁₃' [ (λ u → ((x : SquareFunc.A u) → SquareFunc.g₁ u (SquareFunc.f₁ u x) == SquareFunc.g₂ u (SquareFunc.f₂ u x)))
                        ↓ square=-raw eq-A₂₂ eq-A₀₂ eq-A₂₄ eq-A₀₄ eq-f₁₂ eq-f₂₃ eq-f₀₃ eq-f₁₄ ])
  {H₃₁ : (x : A₂₂) → f₃₀ (f₂₁ x) == f₄₁ (f₃₂ x)} {H₃₁' : (x : A₂₂') → f₃₀' (f₂₁' x) == f₄₁' (f₃₂' x)}
  (eq-H₃₁ : H₃₁ == H₃₁' [ (λ u → ((x : SquareFunc.A u) → SquareFunc.g₁ u (SquareFunc.f₁ u x) == SquareFunc.g₂ u (SquareFunc.f₂ u x)))
                        ↓ square=-raw eq-A₂₂ eq-A₂₀ eq-A₄₂ eq-A₄₀ eq-f₂₁ eq-f₃₂ eq-f₃₀ eq-f₄₁ ])
  {H₃₃ : (x : A₂₂) → f₄₃ (f₃₂ x) == f₃₄ (f₂₃ x)} {H₃₃' : (x : A₂₂') → f₄₃' (f₃₂' x) == f₃₄' (f₂₃' x)}
  (eq-H₃₃ : H₃₃ == H₃₃' [ (λ u → ((x : SquareFunc.A u) → SquareFunc.g₁ u (SquareFunc.f₁ u x) == SquareFunc.g₂ u (SquareFunc.f₂ u x)))
                        ↓ square=-raw eq-A₂₂ eq-A₄₂ eq-A₂₄ eq-A₄₄ eq-f₃₂ eq-f₂₃ eq-f₄₃ eq-f₃₄ ])
    → ap transpose (span^2=-raw eq-A₀₀ eq-A₀₂ eq-A₀₄ eq-A₂₀ eq-A₂₂ eq-A₂₄ eq-A₄₀ eq-A₄₂ eq-A₄₄ eq-f₀₁ eq-f₀₃ eq-f₂₁ eq-f₂₃ eq-f₄₁ eq-f₄₃ eq-f₁₀ eq-f₃₀ eq-f₁₂ eq-f₃₂ eq-f₁₄ eq-f₃₄ eq-H₁₁ eq-H₁₃ eq-H₃₁ eq-H₃₃)
   == span^2=-raw eq-A₀₀ eq-A₂₀ eq-A₄₀ eq-A₀₂ eq-A₂₂ eq-A₄₂ eq-A₀₄ eq-A₂₄ eq-A₄₄ eq-f₁₀ eq-f₃₀ eq-f₁₂ eq-f₃₂ eq-f₁₄ eq-f₃₄ eq-f₀₁ eq-f₀₃ eq-f₂₁ eq-f₂₃ eq-f₄₁ eq-f₄₃ (square-thing _ _ _ _ _ _ _ _ (ap↓ (λ u → ! ∘ u) eq-H₁₁)) eq-H₃₁ eq-H₁₃ (square-thing _ _ _ _ _ _ _ _ (ap↓ (λ u → ! ∘ u) eq-H₃₃))
ap-span^2=-priv2 idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp = idp

module _ {i} (d : Span^2 {i}) where

  open Span^2 d

  transpose-transpose : transpose (transpose d) == d
  transpose-transpose = span^2=-raw idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp
                        (λ= (!-! ∘ Span^2.H₁₁ d)) idp idp (λ= (!-! ∘ Span^2.H₃₃ d))

module _ {i} (d : Span^2 {i}) where

  transpose-equiv : Span^2 {i} ≃ Span^2
  transpose-equiv = equiv transpose transpose transpose-transpose transpose-transpose

module _ {i} (d : Span^2 {i}) where

  open Span^2 d

  d' : (H₁₁' : type-of H₁₁) (H₃₃' : type-of H₃₃) → Span^2
  d' H₁₁' H₃₃' = record d {H₁₁ = H₁₁'; H₃₃ = H₃₃'}

  e : {H₁₁' : type-of H₁₁} (eq₁ : H₁₁ == H₁₁') {H₃₃' : type-of H₃₃} (eq₃ : H₃₃ == H₃₃') (c : M.A₂∙ (d' H₁₁' H₃₃'))
      → left (M.f₁∙ (d' H₁₁' H₃₃') c) == right (M.f₃∙ (d' H₁₁' H₃₃') c)
  e {H₁₁'} eq₁ {H₃₃'} eq₃ = E.f  module _ where

    e-glue : {H₁₁' : type-of H₁₁} (eq₁ : H₁₁ == H₁₁') {H₃₃' : type-of H₃₃} (eq₃ : H₃₃ == H₃₃') (c : Span^2.A₂₂ (d' H₁₁' H₃₃'))
      → glue (left (f₂₁ c)) == glue (right (f₂₃ c)) [ (λ z → left (M.f₁∙ (d' H₁₁' H₃₃') z) == right (M.f₃∙ (d' H₁₁' H₃₃') z)) ↓ glue c ]
    e-glue idp idp c = apd glue (glue c)

    module E = PushoutElim {P = λ c → left (M.f₁∙ (d' H₁₁' H₃₃') c) == right (M.f₃∙ (d' H₁₁' H₃₃') c) :> Pushout^2 d} (glue ∘ left) (glue ∘ right) (e-glue eq₁ eq₃)

  module F {H₁₁' : type-of H₁₁} (eq₁ : H₁₁ == H₁₁') {H₃₃' : type-of H₃₃} (eq₃ : H₃₃ == H₃₃')
    = PushoutRec {d = M.v-h-span (d' H₁₁' H₃₃')} {D = Pushout^2 d}
                 left right (e eq₁ eq₃)

  tr-tr-fun : Pushout^2 (transpose (transpose d)) → Pushout^2 d
  tr-tr-fun = F.f (! (λ= (!-! ∘ H₁₁))) (! (λ= (!-! ∘ H₃₃)))

  lemma12 : (c : M.A₂∙ d) → e idp idp c == glue c
  lemma12 = Pushout-elim (λ a → idp) (λ b → idp) (λ c → ↓-=-in (! (E.glue-β idp idp c)))

  result' : {H₁₁' : type-of H₁₁} (eq₁ : H₁₁ == H₁₁') {H₃₃' : type-of H₃₃} (eq₃ : H₃₃ == H₃₃') (x : Pushout^2 (d' H₁₁' H₃₃'))
    → transport Pushout^2 (span^2=-raw idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp (! eq₁) idp idp (! eq₃) :> (d' H₁₁' H₃₃' == d)) x == F.f eq₁ eq₃ x
  result' idp idp = Pushout-elim (λ a → idp) (λ b → idp) (λ c → ↓-='-in (F.glue-β idp idp c ∙ lemma12 c ∙ ! (ap-idf (glue c))))

  result : {H₁₁' : type-of H₁₁} (eq₁ : H₁₁' == H₁₁) {H₃₃' : type-of H₃₃} (eq₃ : H₃₃' == H₃₃) (x : Pushout^2 (d' H₁₁' H₃₃'))
    → transport Pushout^2 (span^2=-raw idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp eq₁ idp idp eq₃ :> (d' H₁₁' H₃₃' == d)) x == F.f (! eq₁) (! eq₃) x
  result eq₁ eq₃ x = ap (λ u → transport Pushout^2 (span^2=-raw idp idp idp idp idp idp idp idp idp idp idp idp idp
                                                                idp idp idp idp idp idp idp idp (fst u) idp idp (snd u) :> (_ == d)) x)
                        (pair×= (! (!-! eq₁)) (! (!-! eq₃)))
                     ∙ result' (! eq₁) (! eq₃) x

  result2 : (x : Pushout^2 (transpose (transpose d))) → transport Pushout^2 (transpose-transpose d) x == tr-tr-fun x
  result2 = result (λ= (!-! ∘ Span^2.H₁₁ d)) (λ= (!-! ∘ Span^2.H₃₃ d))

-- module _ {i} where

--   postulate
--     to : (d : Span^2 {i}) → Pushout^2 d → Pushout^2 (transpose d)

--   from : (d : Span^2 {i}) → Pushout^2 (transpose d) → Pushout^2 d
--   from d = tr-tr-fun d ∘ to (transpose d)

--   postulate
--     from-to : (d : Span^2 {i}) (x : Pushout^2 d) → from d (to d x) == x

--   lemma3 : {d d' : Span^2 {i}} (p : d == d') (x : Pushout^2 d) → transport (Pushout^2 ∘ transpose) p (to d x) == to d' (transport Pushout^2 p x)
--   lemma3 {d} {.d} idp x = idp

--   lemma34 : (d : Span^2 {i}) (x : Pushout^2 (transpose (transpose d))) → transport (Pushout^2 ∘ transpose) (transpose-transpose d) (to (transpose (transpose d)) x) == to d (tr-tr-fun d x)
--   lemma34 d x = lemma3 (transpose-transpose d) x ∙ ap (to d) (result2 d x)

--   lm2 : (d : Span^2 {i}) → ap transpose (transpose-transpose d) == transpose-transpose (transpose d)
--   lm2 d = ap-span^2=-priv2 idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp
--                         (! (! (λ= (!-! ∘ Span^2.H₁₁ d)))) idp idp (! (! (λ= (!-! ∘ Span^2.H₃₃ d)))) ∙ ap (λ u → span^2=-raw idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp idp (fst u) idp idp (snd u)) (pair×= (lm3 (Span^2.H₁₁ d)) (lm3 (Span^2.H₃₃ d))) where

--     lm3 : ∀ {i j} {A : Type i} {B : Type j} {f g : A → B} (p : (a : A) → f a == g a)
--       → ap (λ f → ! ∘ f) (! (! (λ= (!-! ∘ p)))) == ! (! (λ= (!-! ∘ ! ∘ p)))
--     lm3 p = transport (λ u → ap (λ f → ! ∘ f) (! (! (λ= (!-! ∘ u)))) == ! (! (λ= (!-! ∘ ! ∘ u)))) (λ= (app=-β p)) (lm3' (λ= p))  where

--       lm3' : ∀ {i j} {A : Type i} {B : Type j} {f g : A → B} (p : f == g)
--         → ap (λ f → ! ∘ f) (! (! (λ= (!-! ∘ app= p)))) == ! (! (λ= (!-! ∘ ! ∘ app= p)))
--       lm3' idp = ap (λ u → ap (λ u → ! ∘ u) (! (! u))) (! (λ=-η idp)) ∙ ap (! ∘ !) (λ=-η idp)

--   lemma345 : (d : Span^2 {i}) (x : Pushout^2 (transpose (transpose (transpose d))))
--     → transport (Pushout^2 ∘ transpose) (transpose-transpose d) x == tr-tr-fun (transpose d) x
--   lemma345 d x =
--     transport (Pushout^2 ∘ transpose) (transpose-transpose d) x
--       =⟨ ap (λ u → coe u x) (ap-∘ Pushout^2 transpose (transpose-transpose d)) ⟩
--     transport Pushout^2 (ap transpose (transpose-transpose d)) x
--       =⟨ ap (λ u → transport Pushout^2 u x) (lm2 d) ⟩
--     transport Pushout^2 (transpose-transpose (transpose d)) x
--       =⟨ result2 (transpose d) x ⟩
--     tr-tr-fun (transpose d) x ∎

--   to-from : (d : Span^2 {i}) (x : Pushout^2 (transpose d)) → to d (from d x) == x
--   to-from d x =
--     to d (tr-tr-fun d (to (transpose d) x))
--       =⟨ ! (lemma34 d (to (transpose d) x)) ⟩
--     transport (Pushout^2 ∘ transpose) (transpose-transpose d) (to (transpose (transpose d)) (to (transpose d) x))
--       =⟨ lemma345 d (to (transpose (transpose d)) (to (transpose d) x)) ⟩
--     tr-tr-fun (transpose d) (to (transpose (transpose d)) (to (transpose d) x))
--       =⟨ from-to (transpose d) x ⟩
--     x ∎


module Show where

open import Data.Char hiding (show)
open import Data.List
open import Data.String hiding (show)
open import Function

record Show (A : Set) : Set where
  field
    show : A → String

open Show {{...}} public

fromHaskell : {A : Set} → (A → List Char) → Show A
fromHaskell primShow = record { show = fromList ∘ primShow }


------------------------------------------------------------------------
-- The Agda standard library
--
-- Some properties about subsets
------------------------------------------------------------------------

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

module Data.Fin.Subset.Properties where

import Algebra.Definitions as AlgebraicDefinitions
import Algebra.Structures as AlgebraicStructures
open import Algebra.Bundles
import Algebra.Properties.Lattice as L
import Algebra.Properties.DistributiveLattice as DL
import Algebra.Properties.BooleanAlgebra as BA
open import Data.Bool.Base using (not)
open import Data.Bool.Properties
open import Data.Fin.Base using (Fin; suc; zero)
open import Data.Fin.Subset
open import Data.Fin.Properties using (any?; decFinSubset)
open import Data.Nat.Base
import Data.Nat.Properties as ℕₚ
open import Data.Product as Product using (∃; ∄; _×_; _,_; proj₁)
open import Data.Sum.Base as Sum using (_⊎_; inj₁; inj₂; [_,_]′)
open import Data.Vec.Base
open import Data.Vec.Properties
open import Function.Base using (_∘_; const; id; case_of_)
open import Function.Equivalence using (_⇔_; equivalence)
open import Relation.Binary as B hiding (Decidable)
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary using (Dec; yes; no)
import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Nullary.Sum using (_⊎-dec_)
open import Relation.Unary using (Pred; Decidable; Satisfiable)

------------------------------------------------------------------------
-- Constructor mangling

drop-there : ∀ {s n x} {p : Subset n} → suc x ∈ s ∷ p → x ∈ p
drop-there (there x∈p) = x∈p

drop-not-there : ∀ {s n x} {p : Subset n} → suc x ∉ s ∷ p → x ∉ p
drop-not-there x∉sp x∈p = contradiction (there x∈p) x∉sp

drop-∷-⊆ : ∀ {n s₁ s₂} {p₁ p₂ : Subset n} → s₁ ∷ p₁ ⊆ s₂ ∷ p₂ → p₁ ⊆ p₂
drop-∷-⊆ p₁s₁⊆p₂s₂ x∈p₁ = drop-there (p₁s₁⊆p₂s₂ (there x∈p₁))

drop-∷-⊂ : ∀ {n s} {p₁ p₂ : Subset n} → s ∷ p₁ ⊂ s ∷ p₂ → p₁ ⊂ p₂
drop-∷-⊂ {s = inside} (_       , zero  ,      x∈sp₂ , x∉sp₁) = contradiction here x∉sp₁
drop-∷-⊂ {s}          (sp₁⊆sp₂ , suc x , there x∈p₂ , x∉sp₁) = drop-∷-⊆ sp₁⊆sp₂ , x , x∈p₂ , drop-not-there x∉sp₁

module _ {n} {p q : Subset n} where

  out⊆ : ∀ {y} → p ⊆ q → outside ∷ p ⊆ y ∷ q
  out⊆ p⊆q (there ∈p) = there (p⊆q ∈p)

  out⊆-⇔ : ∀ {y} → p ⊆ q ⇔ outside ∷ p ⊆ y ∷ q
  out⊆-⇔ = equivalence out⊆ drop-∷-⊆

  in⊆in : p ⊆ q → inside ∷ p ⊆ inside ∷ q
  in⊆in p⊆q here = here
  in⊆in p⊆q (there ∈p) = there (p⊆q ∈p)

  in⊆in-⇔ : p ⊆ q ⇔ inside ∷ p ⊆ inside ∷ q
  in⊆in-⇔ = equivalence in⊆in drop-∷-⊆

  s⊆s : ∀ {s} →  p ⊆ q → s ∷ p ⊆ s ∷ q
  s⊆s p⊆q here = here
  s⊆s p⊆q (there ∈p) = there (p⊆q ∈p)

  out⊂ : ∀ {y} → p ⊂ q → outside ∷ p ⊂ y ∷ q
  out⊂ (p⊆q , x , x∈q , x∉p) = out⊆ p⊆q , suc x , there x∈q , x∉p ∘ drop-there

  out⊂in : p ⊆ q → outside ∷ p ⊂ inside ∷ q
  out⊂in p⊆q = out⊆ p⊆q , zero , here , λ ()

  out⊂in-⇔ : p ⊆ q ⇔ outside ∷ p ⊂ inside ∷ q
  out⊂in-⇔ = equivalence out⊂in (drop-∷-⊆ ∘ proj₁)

  out⊂out-⇔ : p ⊂ q ⇔ outside ∷ p ⊂ outside ∷ q
  out⊂out-⇔ = equivalence out⊂ drop-∷-⊂

  in⊂in : p ⊂ q → inside ∷ p ⊂ inside ∷ q
  in⊂in (p⊆q , x , x∈q , x∉p) = in⊆in p⊆q , suc x , there x∈q , x∉p ∘ drop-there

  in⊂in-⇔ : p ⊂ q ⇔ inside ∷ p ⊂ inside ∷ q
  in⊂in-⇔ = equivalence in⊂in drop-∷-⊂

------------------------------------------------------------------------
-- _∈_

infix 4 _∈?_
_∈?_ : ∀ {n} x (p : Subset n) → Dec (x ∈ p)
zero  ∈? inside  ∷ p = yes here
zero  ∈? outside ∷ p = no  λ()
suc n ∈? s       ∷ p = Dec.map′ there drop-there (n ∈? p)

------------------------------------------------------------------------
-- Empty

drop-∷-Empty : ∀ {n s} {p : Subset n} → Empty (s ∷ p) → Empty p
drop-∷-Empty ¬∃∈ (x , x∈p) = ¬∃∈ (suc x , there x∈p)

Empty-unique : ∀ {n} {p : Subset n} → Empty p → p ≡ ⊥
Empty-unique {_} {[]}          ¬∃∈ = refl
Empty-unique {_} {inside  ∷ p} ¬∃∈ = contradiction (zero , here) ¬∃∈
Empty-unique {_} {outside ∷ p} ¬∃∈ =
  cong (outside ∷_) (Empty-unique (drop-∷-Empty ¬∃∈))

nonempty? : ∀ {n} → Decidable (Nonempty {n})
nonempty? p = any? (_∈? p)

------------------------------------------------------------------------
-- ∣_∣

∣p∣≤n : ∀ {n} (p : Subset n) → ∣ p ∣ ≤ n
∣p∣≤n = count≤n (_≟ inside)

------------------------------------------------------------------------
-- ⊥

∉⊥ : ∀ {n} {x : Fin n} → x ∉ ⊥
∉⊥ (there p) = ∉⊥ p

⊥⊆ : ∀ {n} {p : Subset n} → ⊥ ⊆ p
⊥⊆ x∈⊥ = contradiction x∈⊥ ∉⊥

∣⊥∣≡0 : ∀ n → ∣ ⊥ {n = n} ∣ ≡ 0
∣⊥∣≡0 zero    = refl
∣⊥∣≡0 (suc n) = ∣⊥∣≡0 n

------------------------------------------------------------------------
-- ⊤

∈⊤ : ∀ {n} {x : Fin n} → x ∈ ⊤
∈⊤ {x = zero}  = here
∈⊤ {x = suc x} = there ∈⊤

⊆⊤ : ∀ {n} {p : Subset n} → p ⊆ ⊤
⊆⊤ = const ∈⊤

∣⊤∣≡n : ∀ n → ∣ ⊤ {n = n} ∣ ≡ n
∣⊤∣≡n zero    = refl
∣⊤∣≡n (suc n) = cong suc (∣⊤∣≡n n)

∣p∣≡n⇒p≡⊤ : ∀ {n} {p : Subset n} → ∣ p ∣ ≡ n → p ≡ ⊤
∣p∣≡n⇒p≡⊤ {p = []}          _     = refl
∣p∣≡n⇒p≡⊤ {p = outside ∷ p} |p|≡n = contradiction |p|≡n (ℕₚ.<⇒≢ (s≤s (∣p∣≤n p)))
∣p∣≡n⇒p≡⊤ {p = inside  ∷ p} |p|≡n = cong (inside ∷_) (∣p∣≡n⇒p≡⊤ (ℕₚ.suc-injective |p|≡n))

------------------------------------------------------------------------
-- ⁅_⁆

x∈⁅x⁆ : ∀ {n} (x : Fin n) → x ∈ ⁅ x ⁆
x∈⁅x⁆ zero    = here
x∈⁅x⁆ (suc x) = there (x∈⁅x⁆ x)

x∈⁅y⁆⇒x≡y : ∀ {n x} (y : Fin n) → x ∈ ⁅ y ⁆ → x ≡ y
x∈⁅y⁆⇒x≡y zero    here      = refl
x∈⁅y⁆⇒x≡y zero    (there p) = contradiction p ∉⊥
x∈⁅y⁆⇒x≡y (suc y) (there p) = cong suc (x∈⁅y⁆⇒x≡y y p)

x∈⁅y⁆⇔x≡y : ∀ {n} {x y : Fin n} → x ∈ ⁅ y ⁆ ⇔ x ≡ y
x∈⁅y⁆⇔x≡y {_} {x} {y} = equivalence
  (x∈⁅y⁆⇒x≡y y)
  (λ x≡y → subst (λ y → x ∈ ⁅ y ⁆) x≡y (x∈⁅x⁆ x))

x≢y⇒x∉⁅y⁆ : ∀ {n} {x y : Fin n} → x ≢ y → x ∉ ⁅ y ⁆
x≢y⇒x∉⁅y⁆ x≢y = x≢y ∘ x∈⁅y⁆⇒x≡y _

x∉⁅y⁆⇒x≢y : ∀ {n} {x y : Fin n} → x ∉ ⁅ y ⁆ → x ≢ y
x∉⁅y⁆⇒x≢y x∉⁅x⁆ refl = x∉⁅x⁆ (x∈⁅x⁆ _)

∣⁅x⁆∣≡1 : ∀ {n} (i : Fin n) → ∣ ⁅ i ⁆ ∣ ≡ 1
∣⁅x⁆∣≡1 {suc n} zero    = cong suc (∣⊥∣≡0 n)
∣⁅x⁆∣≡1 {_}     (suc i) = ∣⁅x⁆∣≡1 i

------------------------------------------------------------------------
-- _⊆_

⊆-refl : ∀ {n} → Reflexive (_⊆_ {n})
⊆-refl = id

⊆-reflexive : ∀ {n} → _≡_ ⇒ (_⊆_ {n})
⊆-reflexive refl = ⊆-refl

⊆-trans : ∀ {n} → Transitive (_⊆_ {n})
⊆-trans p⊆q q⊆r x∈p = q⊆r (p⊆q x∈p)

⊆-antisym : ∀ {n} → Antisymmetric _≡_ (_⊆_ {n})
⊆-antisym {i = []}     {[]}     p⊆q q⊆p = refl
⊆-antisym {i = x ∷ xs} {y ∷ ys} p⊆q q⊆p with x | y
... | inside  | inside  = cong₂ _∷_ refl (⊆-antisym (drop-∷-⊆ p⊆q) (drop-∷-⊆ q⊆p))
... | inside  | outside = contradiction (p⊆q here) λ()
... | outside | inside  = contradiction (q⊆p here) λ()
... | outside | outside = cong₂ _∷_ refl (⊆-antisym (drop-∷-⊆ p⊆q) (drop-∷-⊆ q⊆p))

⊆-min : ∀ {n} → Minimum (_⊆_ {n}) ⊥
⊆-min (x ∷ xs) (there v∈⊥) = there (⊆-min xs v∈⊥)

⊆-max : ∀ {n} → Maximum (_⊆_ {n}) ⊤
⊆-max (inside ∷ xs) here         = here
⊆-max (x      ∷ xs) (there v∈xs) = there (⊆-max xs v∈xs)

infix 4 _⊆?_
_⊆?_ : ∀ {n} → B.Decidable (_⊆_ {n = n})
[]          ⊆? []          = yes id
outside ∷ p ⊆?       y ∷ q = Dec.map out⊆-⇔ (p ⊆? q)
inside  ∷ p ⊆? outside ∷ q = no (λ p⊆q → case (p⊆q here) of λ())
inside  ∷ p ⊆? inside  ∷ q = Dec.map in⊆in-⇔ (p ⊆? q)

module _ (n : ℕ) where

  ⊆-isPreorder : IsPreorder _≡_ (_⊆_ {n})
  ⊆-isPreorder = record
    { isEquivalence = isEquivalence
    ; reflexive     = ⊆-reflexive
    ; trans         = ⊆-trans
    }

  ⊆-preorder : Preorder _ _ _
  ⊆-preorder = record
    { isPreorder = ⊆-isPreorder
    }

  ⊆-isPartialOrder : IsPartialOrder _≡_ (_⊆_ {n})
  ⊆-isPartialOrder = record
    { isPreorder = ⊆-isPreorder
    ; antisym    = ⊆-antisym
    }

  ⊆-poset : Poset _ _ _
  ⊆-poset = record
    { isPartialOrder = ⊆-isPartialOrder
    }

p⊆q⇒∣p∣≤∣q∣ : ∀ {n} {p q : Subset n} → p ⊆ q → ∣ p ∣ ≤ ∣ q ∣
p⊆q⇒∣p∣≤∣q∣ {p = []}          {[]}          p⊆q = z≤n
p⊆q⇒∣p∣≤∣q∣ {p = outside ∷ p} {outside ∷ q} p⊆q = p⊆q⇒∣p∣≤∣q∣ (drop-∷-⊆ p⊆q)
p⊆q⇒∣p∣≤∣q∣ {p = outside ∷ p} {inside  ∷ q} p⊆q = ℕₚ.≤-step (p⊆q⇒∣p∣≤∣q∣ (drop-∷-⊆ p⊆q))
p⊆q⇒∣p∣≤∣q∣ {p = inside  ∷ p} {outside ∷ q} p⊆q = contradiction (p⊆q here) λ()
p⊆q⇒∣p∣≤∣q∣ {p = inside  ∷ p} {inside  ∷ q} p⊆q = s≤s (p⊆q⇒∣p∣≤∣q∣ (drop-∷-⊆ p⊆q))

------------------------------------------------------------------------
-- _⊂_

p⊂q⇒p⊆q : ∀ {n} → {p q : Subset n} → p ⊂ q → p ⊆ q
p⊂q⇒p⊆q = proj₁

⊂-trans : ∀ {n} → Transitive (_⊂_ {n})
⊂-trans (p⊆q , x , x∈q , x∉p) (q⊆r , _ , _ , _) = ⊆-trans p⊆q q⊆r , x , q⊆r x∈q , x∉p

⊂-⊆-trans : ∀ {n} → Trans {A = Subset n} _⊂_ _⊆_ _⊂_
⊂-⊆-trans (p⊆q , x , x∈q , x∉p) q⊆r = ⊆-trans p⊆q q⊆r , x , q⊆r x∈q , x∉p

⊆-⊂-trans : ∀ {n} → Trans {A = Subset n} _⊆_ _⊂_ _⊂_
⊆-⊂-trans p⊆q (q⊆r , x , x∈r , x∉q) = ⊆-trans p⊆q q⊆r , x , x∈r , x∉q ∘ p⊆q

⊂-irref : ∀ {n} → Irreflexive _≡_ (_⊂_ {n})
⊂-irref refl (_ , x , x∈p , x∉q) = contradiction x∈p x∉q

⊂-antisym : ∀ {n} → Antisymmetric _≡_ (_⊂_ {n})
⊂-antisym (p⊆q , _) (q⊆p , _) = ⊆-antisym p⊆q q⊆p

⊂-asymmetric : ∀ {n} → Asymmetric (_⊂_ {n})
⊂-asymmetric (p⊆q , _) (_ , x , x∈p , x∉q) = contradiction (p⊆q x∈p) x∉q

infix 4 _⊂?_

_⊂?_ : ∀ {n} → B.Decidable (_⊂_ {n = n})
[]          ⊂? []          = no λ ()
outside ∷ p ⊂? outside ∷ q = Dec.map out⊂out-⇔ (p ⊂? q)
outside ∷ p ⊂? inside  ∷ q = Dec.map out⊂in-⇔ (p ⊆? q)
inside  ∷ p ⊂? outside ∷ q = no (λ {(p⊆q , _) → case (p⊆q here) of λ ()})
inside  ∷ p ⊂? inside  ∷ q = Dec.map in⊂in-⇔ (p ⊂? q)

module _ (n : ℕ) where

  ⊂-isStrictPartialOrder : IsStrictPartialOrder _≡_ (_⊂_ {n})
  ⊂-isStrictPartialOrder = record
    { isEquivalence = isEquivalence
    ; irrefl = ⊂-irref
    ; trans = ⊂-trans
    ; <-resp-≈ = (λ {refl → id}) , (λ {refl → id})
    }

  ⊂-strictPartialOrder : StrictPartialOrder _ _ _
  ⊂-strictPartialOrder = record
    { isStrictPartialOrder = ⊂-isStrictPartialOrder
    }

  ⊂-isDecStrictPartialOrder : IsDecStrictPartialOrder _≡_ (_⊂_ {n})
  ⊂-isDecStrictPartialOrder = record
    { isStrictPartialOrder = ⊂-isStrictPartialOrder
    ; _≟_ = ≡-dec _≟_
    ; _<?_ = _⊂?_
    }

  ⊂-decStrictPartialOrder : DecStrictPartialOrder _ _ _
  ⊂-decStrictPartialOrder = record
    { isDecStrictPartialOrder = ⊂-isDecStrictPartialOrder
    }

p⊂q⇒∣p∣<∣q∣ : ∀ {n} → {p q : Subset n} → p ⊂ q → ∣ p ∣ < ∣ q ∣
p⊂q⇒∣p∣<∣q∣ {p = outside ∷ p} {outside ∷ q} op⊂oq@(_     , _     , _ , _)    = p⊂q⇒∣p∣<∣q∣ (drop-∷-⊂ op⊂oq)
p⊂q⇒∣p∣<∣q∣ {p = outside ∷ p} {inside  ∷ q}       (op⊆iq , _     , _ , _)    = s≤s (p⊆q⇒∣p∣≤∣q∣ (drop-∷-⊆ op⊆iq))
p⊂q⇒∣p∣<∣q∣ {p = inside  ∷ p} {outside ∷ q}       (ip⊆oq , _     , _ , _)    = contradiction (ip⊆oq here) (λ ())
p⊂q⇒∣p∣<∣q∣ {p = inside  ∷ p} {inside  ∷ q}       (_     , zero  , _ , x∉ip) = contradiction here x∉ip
p⊂q⇒∣p∣<∣q∣ {p = inside  ∷ p} {inside  ∷ q} ip⊂iq@(_     , suc x , _ , _)    = s≤s (p⊂q⇒∣p∣<∣q∣ (drop-∷-⊂ ip⊂iq))

------------------------------------------------------------------------
-- ∁

x∈p⇒x∉∁p : ∀ {n x} {p : Subset n} → x ∈ p → x ∉ ∁ p
x∈p⇒x∉∁p (there x∈p) (there x∈∁p) = x∈p⇒x∉∁p x∈p x∈∁p

x∈∁p⇒x∉p : ∀ {n x} {p : Subset n} → x ∈ ∁ p → x ∉ p
x∈∁p⇒x∉p (there x∈∁p) (there x∈p) = x∈∁p⇒x∉p x∈∁p x∈p

x∉∁p⇒x∈p : ∀ {n x} {p : Subset n} → x ∉ ∁ p → x ∈ p
x∉∁p⇒x∈p {x = zero}  {outside ∷ p} x∉∁p = contradiction here x∉∁p
x∉∁p⇒x∈p {x = zero}  {inside  ∷ p} x∉∁p = here
x∉∁p⇒x∈p {x = suc x} {_       ∷ p} x∉∁p = there (x∉∁p⇒x∈p (x∉∁p ∘ there))

x∉p⇒x∈∁p : ∀ {n x} {p : Subset n} → x ∉ p → x ∈ ∁ p
x∉p⇒x∈∁p {x = zero}  {outside ∷ p} x∉p = here
x∉p⇒x∈∁p {x = zero}  {inside  ∷ p} x∉p = contradiction here x∉p
x∉p⇒x∈∁p {x = suc x} {_       ∷ p} x∉p = there (x∉p⇒x∈∁p (x∉p ∘ there))

p∪∁p≡⊤ : ∀ {n} (p : Subset n) → p ∪ ∁ p ≡ ⊤
p∪∁p≡⊤ []            = refl
p∪∁p≡⊤ (outside ∷ p) = cong (inside ∷_) (p∪∁p≡⊤ p)
p∪∁p≡⊤ (inside  ∷ p) = cong (inside ∷_) (p∪∁p≡⊤ p)

∣∁p∣≡n∸∣p∣ : ∀ {n} (p : Subset n) → ∣ ∁ p ∣ ≡ n ∸ ∣ p ∣
∣∁p∣≡n∸∣p∣ []            = refl
∣∁p∣≡n∸∣p∣ (inside  ∷ p) = ∣∁p∣≡n∸∣p∣ p
∣∁p∣≡n∸∣p∣ (outside ∷ p) = begin
  suc ∣ ∁ p ∣     ≡⟨ cong suc (∣∁p∣≡n∸∣p∣ p) ⟩
  suc (_ ∸ ∣ p ∣) ≡⟨ sym (ℕₚ.+-∸-assoc 1 (∣p∣≤n p)) ⟩
  suc  _ ∸ ∣ p ∣  ∎
  where open ≡-Reasoning

------------------------------------------------------------------------
-- _∩_

module _ {n : ℕ} where

  open AlgebraicDefinitions {A = Subset n} _≡_

  ∩-assoc : Associative _∩_
  ∩-assoc = zipWith-assoc ∧-assoc

  ∩-comm : Commutative _∩_
  ∩-comm = zipWith-comm ∧-comm

  ∩-idem : Idempotent _∩_
  ∩-idem = zipWith-idem ∧-idem

  ∩-identityˡ : LeftIdentity ⊤ _∩_
  ∩-identityˡ = zipWith-identityˡ ∧-identityˡ

  ∩-identityʳ : RightIdentity ⊤ _∩_
  ∩-identityʳ = zipWith-identityʳ ∧-identityʳ

  ∩-identity : Identity ⊤ _∩_
  ∩-identity = ∩-identityˡ , ∩-identityʳ

  ∩-zeroˡ : LeftZero ⊥ _∩_
  ∩-zeroˡ = zipWith-zeroˡ ∧-zeroˡ

  ∩-zeroʳ : RightZero ⊥ _∩_
  ∩-zeroʳ = zipWith-zeroʳ ∧-zeroʳ

  ∩-zero : Zero ⊥ _∩_
  ∩-zero = ∩-zeroˡ , ∩-zeroʳ

  ∩-inverseˡ : LeftInverse ⊥ ∁ _∩_
  ∩-inverseˡ = zipWith-inverseˡ ∧-inverseˡ

  ∩-inverseʳ : RightInverse ⊥ ∁ _∩_
  ∩-inverseʳ = zipWith-inverseʳ ∧-inverseʳ

  ∩-inverse : Inverse ⊥ ∁ _∩_
  ∩-inverse = ∩-inverseˡ , ∩-inverseʳ

module _ (n : ℕ) where

  open AlgebraicStructures {A = Subset n} _≡_

  ∩-isMagma : IsMagma _∩_
  ∩-isMagma = record
    { isEquivalence = isEquivalence
    ; ∙-cong        = cong₂ _∩_
    }

  ∩-magma : Magma _ _
  ∩-magma = record
    { isMagma = ∩-isMagma
    }

  ∩-isSemigroup : IsSemigroup _∩_
  ∩-isSemigroup = record
    { isMagma = ∩-isMagma
    ; assoc   = ∩-assoc
    }

  ∩-semigroup : Semigroup _ _
  ∩-semigroup = record
    { isSemigroup = ∩-isSemigroup
    }

  ∩-isBand : IsBand _∩_
  ∩-isBand = record
    { isSemigroup = ∩-isSemigroup
    ; idem        = ∩-idem
    }

  ∩-band : Band _ _
  ∩-band = record
    { isBand = ∩-isBand
    }

  ∩-isSemilattice : IsSemilattice _∩_
  ∩-isSemilattice = record
    { isBand = ∩-isBand
    ; comm   = ∩-comm
    }

  ∩-semilattice : Semilattice _ _
  ∩-semilattice = record
    { isSemilattice = ∩-isSemilattice
    }

  ∩-isMonoid : IsMonoid _∩_ ⊤
  ∩-isMonoid = record
    { isSemigroup = ∩-isSemigroup
    ; identity    = ∩-identity
    }

  ∩-monoid : Monoid _ _
  ∩-monoid = record
    { isMonoid = ∩-isMonoid
    }

  ∩-isCommutativeMonoid : IsCommutativeMonoid _∩_ ⊤
  ∩-isCommutativeMonoid = record
    { isMonoid = ∩-isMonoid
    ; comm     = ∩-comm
    }

  ∩-commutativeMonoid : CommutativeMonoid _ _
  ∩-commutativeMonoid = record
    { isCommutativeMonoid = ∩-isCommutativeMonoid
    }

  ∩-isIdempotentCommutativeMonoid : IsIdempotentCommutativeMonoid _∩_ ⊤
  ∩-isIdempotentCommutativeMonoid = record
    { isCommutativeMonoid = ∩-isCommutativeMonoid
    ; idem                = ∩-idem
    }

  ∩-idempotentCommutativeMonoid : IdempotentCommutativeMonoid _ _
  ∩-idempotentCommutativeMonoid = record
    { isIdempotentCommutativeMonoid = ∩-isIdempotentCommutativeMonoid
    }

p∩q⊆p : ∀ {n} (p q : Subset n) → p ∩ q ⊆ p
p∩q⊆p []            []           x∈p∩q        = x∈p∩q
p∩q⊆p (inside  ∷ p) (inside ∷ q) here         = here
p∩q⊆p (inside  ∷ p) (_      ∷ q) (there ∈p∩q) = there (p∩q⊆p p q ∈p∩q)
p∩q⊆p (outside ∷ p) (_      ∷ q) (there ∈p∩q) = there (p∩q⊆p p q ∈p∩q)

p∩q⊆q : ∀ {n} (p q : Subset n) → p ∩ q ⊆ q
p∩q⊆q p q rewrite ∩-comm p q = p∩q⊆p q p

x∈p∩q⁺ : ∀ {n} {p q : Subset n} {x} → x ∈ p × x ∈ q → x ∈ p ∩ q
x∈p∩q⁺ (here      , here)      = here
x∈p∩q⁺ (there x∈p , there x∈q) = there (x∈p∩q⁺ (x∈p , x∈q))

x∈p∩q⁻ : ∀ {n} (p q : Subset n) {x} → x ∈ p ∩ q → x ∈ p × x ∈ q
x∈p∩q⁻ (inside ∷ p) (inside ∷ q) here          = here , here
x∈p∩q⁻ (s      ∷ p) (t      ∷ q) (there x∈p∩q) =
  Product.map there there (x∈p∩q⁻ p q x∈p∩q)

∩⇔× : ∀ {n} {p q : Subset n} {x} → x ∈ p ∩ q ⇔ (x ∈ p × x ∈ q)
∩⇔× = equivalence (x∈p∩q⁻ _ _) x∈p∩q⁺

module _ {n} (p q : Subset n) where

  ∣p∩q∣≤∣p∣ : ∣ p ∩ q ∣ ≤ ∣ p ∣
  ∣p∩q∣≤∣p∣ = p⊆q⇒∣p∣≤∣q∣ (p∩q⊆p p q)

  ∣p∩q∣≤∣q∣ : ∣ p ∩ q ∣ ≤ ∣ q ∣
  ∣p∩q∣≤∣q∣ = p⊆q⇒∣p∣≤∣q∣ (p∩q⊆q p q)

  ∣p∩q∣≤∣p∣⊓∣q∣ : ∣ p ∩ q ∣ ≤ ∣ p ∣ ⊓ ∣ q ∣
  ∣p∩q∣≤∣p∣⊓∣q∣ = ℕₚ.⊓-pres-m≤ ∣p∩q∣≤∣p∣ ∣p∩q∣≤∣q∣

------------------------------------------------------------------------
-- _∪_

module _ {n : ℕ} where

  open AlgebraicDefinitions {A = Subset n} _≡_

  ∪-assoc : Associative _∪_
  ∪-assoc = zipWith-assoc ∨-assoc

  ∪-comm : Commutative _∪_
  ∪-comm = zipWith-comm ∨-comm

  ∪-idem : Idempotent _∪_
  ∪-idem = zipWith-idem ∨-idem

  ∪-identityˡ : LeftIdentity ⊥ _∪_
  ∪-identityˡ = zipWith-identityˡ ∨-identityˡ

  ∪-identityʳ : RightIdentity ⊥ _∪_
  ∪-identityʳ = zipWith-identityʳ ∨-identityʳ

  ∪-identity : Identity ⊥ _∪_
  ∪-identity = ∪-identityˡ , ∪-identityʳ

  ∪-zeroˡ : LeftZero ⊤ _∪_
  ∪-zeroˡ = zipWith-zeroˡ ∨-zeroˡ

  ∪-zeroʳ : RightZero ⊤ _∪_
  ∪-zeroʳ = zipWith-zeroʳ ∨-zeroʳ

  ∪-zero : Zero ⊤ _∪_
  ∪-zero = ∪-zeroˡ , ∪-zeroʳ

  ∪-inverseˡ : LeftInverse ⊤ ∁ _∪_
  ∪-inverseˡ = zipWith-inverseˡ ∨-inverseˡ

  ∪-inverseʳ : RightInverse ⊤ ∁ _∪_
  ∪-inverseʳ = zipWith-inverseʳ ∨-inverseʳ

  ∪-inverse : Inverse ⊤ ∁ _∪_
  ∪-inverse = ∪-inverseˡ , ∪-inverseʳ

  ∪-distribˡ-∩ : _∪_ DistributesOverˡ _∩_
  ∪-distribˡ-∩ = zipWith-distribˡ ∨-distribˡ-∧

  ∪-distribʳ-∩ : _∪_ DistributesOverʳ _∩_
  ∪-distribʳ-∩ = zipWith-distribʳ ∨-distribʳ-∧

  ∪-distrib-∩ : _∪_ DistributesOver _∩_
  ∪-distrib-∩ = ∪-distribˡ-∩ , ∪-distribʳ-∩

  ∩-distribˡ-∪ : _∩_ DistributesOverˡ _∪_
  ∩-distribˡ-∪ = zipWith-distribˡ ∧-distribˡ-∨

  ∩-distribʳ-∪ : _∩_ DistributesOverʳ _∪_
  ∩-distribʳ-∪ = zipWith-distribʳ ∧-distribʳ-∨

  ∩-distrib-∪ : _∩_ DistributesOver _∪_
  ∩-distrib-∪ = ∩-distribˡ-∪ , ∩-distribʳ-∪

  ∪-abs-∩ : _∪_ Absorbs _∩_
  ∪-abs-∩ = zipWith-absorbs ∨-abs-∧

  ∩-abs-∪ : _∩_ Absorbs _∪_
  ∩-abs-∪ = zipWith-absorbs ∧-abs-∨

module _ (n : ℕ) where

  open AlgebraicStructures {A = Subset n} _≡_

  ∪-isMagma : IsMagma _∪_
  ∪-isMagma = record
    { isEquivalence = isEquivalence
    ; ∙-cong        = cong₂ _∪_
    }

  ∪-magma : Magma _ _
  ∪-magma = record
    { isMagma = ∪-isMagma
    }

  ∪-isSemigroup : IsSemigroup _∪_
  ∪-isSemigroup = record
    { isMagma = ∪-isMagma
    ; assoc   = ∪-assoc
    }

  ∪-semigroup : Semigroup _ _
  ∪-semigroup = record
    { isSemigroup = ∪-isSemigroup
    }

  ∪-isBand : IsBand _∪_
  ∪-isBand = record
    { isSemigroup = ∪-isSemigroup
    ; idem        = ∪-idem
    }

  ∪-band : Band _ _
  ∪-band = record
    { isBand = ∪-isBand
    }

  ∪-isSemilattice : IsSemilattice _∪_
  ∪-isSemilattice = record
    { isBand = ∪-isBand
    ; comm   = ∪-comm
    }

  ∪-semilattice : Semilattice _ _
  ∪-semilattice = record
    { isSemilattice = ∪-isSemilattice
    }

  ∪-isMonoid : IsMonoid _∪_ ⊥
  ∪-isMonoid = record
    { isSemigroup = ∪-isSemigroup
    ; identity    = ∪-identity
    }

  ∪-monoid : Monoid _ _
  ∪-monoid = record
    { isMonoid = ∪-isMonoid
    }

  ∪-isCommutativeMonoid : IsCommutativeMonoid _∪_ ⊥
  ∪-isCommutativeMonoid = record
    { isMonoid = ∪-isMonoid
    ; comm     = ∪-comm
    }

  ∪-commutativeMonoid : CommutativeMonoid _ _
  ∪-commutativeMonoid = record
    { isCommutativeMonoid = ∪-isCommutativeMonoid
    }

  ∪-isIdempotentCommutativeMonoid : IsIdempotentCommutativeMonoid _∪_ ⊥
  ∪-isIdempotentCommutativeMonoid = record
    { isCommutativeMonoid = ∪-isCommutativeMonoid
    ; idem                = ∪-idem
    }

  ∪-idempotentCommutativeMonoid : IdempotentCommutativeMonoid _ _
  ∪-idempotentCommutativeMonoid = record
    { isIdempotentCommutativeMonoid = ∪-isIdempotentCommutativeMonoid
    }

  ∪-∩-isLattice : IsLattice _∪_ _∩_
  ∪-∩-isLattice = record
    { isEquivalence = isEquivalence
    ; ∨-comm        = ∪-comm
    ; ∨-assoc       = ∪-assoc
    ; ∨-cong        = cong₂ _∪_
    ; ∧-comm        = ∩-comm
    ; ∧-assoc       = ∩-assoc
    ; ∧-cong        = cong₂ _∩_
    ; absorptive    = ∪-abs-∩ , ∩-abs-∪
    }

  ∪-∩-lattice : Lattice _ _
  ∪-∩-lattice = record
    { isLattice = ∪-∩-isLattice
    }

  ∪-∩-isDistributiveLattice : IsDistributiveLattice _∪_ _∩_
  ∪-∩-isDistributiveLattice = record
    { isLattice    = ∪-∩-isLattice
    ; ∨-distribʳ-∧ = ∪-distribʳ-∩
    }

  ∪-∩-distributiveLattice : DistributiveLattice _ _
  ∪-∩-distributiveLattice = record
    { isDistributiveLattice = ∪-∩-isDistributiveLattice
    }

  ∪-∩-isBooleanAlgebra : IsBooleanAlgebra _∪_ _∩_ ∁ ⊤ ⊥
  ∪-∩-isBooleanAlgebra = record
    { isDistributiveLattice = ∪-∩-isDistributiveLattice
    ; ∨-complementʳ         = ∪-inverseʳ
    ; ∧-complementʳ         = ∩-inverseʳ
    ; ¬-cong                = cong ∁
    }

  ∪-∩-booleanAlgebra : BooleanAlgebra _ _
  ∪-∩-booleanAlgebra = record
    { isBooleanAlgebra = ∪-∩-isBooleanAlgebra
    }

  ∩-∪-isLattice : IsLattice _∩_ _∪_
  ∩-∪-isLattice = L.∧-∨-isLattice ∪-∩-lattice

  ∩-∪-lattice : Lattice _ _
  ∩-∪-lattice = L.∧-∨-lattice ∪-∩-lattice

  ∩-∪-isDistributiveLattice : IsDistributiveLattice _∩_ _∪_
  ∩-∪-isDistributiveLattice = DL.∧-∨-isDistributiveLattice ∪-∩-distributiveLattice

  ∩-∪-distributiveLattice : DistributiveLattice _ _
  ∩-∪-distributiveLattice = DL.∧-∨-distributiveLattice ∪-∩-distributiveLattice

  ∩-∪-isBooleanAlgebra : IsBooleanAlgebra _∩_ _∪_ ∁ ⊥ ⊤
  ∩-∪-isBooleanAlgebra = BA.∧-∨-isBooleanAlgebra ∪-∩-booleanAlgebra

  ∩-∪-booleanAlgebra : BooleanAlgebra _ _
  ∩-∪-booleanAlgebra = BA.∧-∨-booleanAlgebra ∪-∩-booleanAlgebra

p⊆p∪q : ∀ {n p} (q : Subset n) → p ⊆ p ∪ q
p⊆p∪q (s ∷ q) here        = here
p⊆p∪q (s ∷ q) (there x∈p) = there (p⊆p∪q q x∈p)

q⊆p∪q : ∀ {n} (p q : Subset n) → q ⊆ p ∪ q
q⊆p∪q p q rewrite ∪-comm p q = p⊆p∪q p

x∈p∪q⁻ :  ∀ {n} (p q : Subset n) {x} → x ∈ p ∪ q → x ∈ p ⊎ x ∈ q
x∈p∪q⁻ (inside  ∷ p) (t       ∷ q) here          = inj₁ here
x∈p∪q⁻ (outside ∷ p) (inside  ∷ q) here          = inj₂ here
x∈p∪q⁻ (s       ∷ p) (t       ∷ q) (there x∈p∪q) =
  Sum.map there there (x∈p∪q⁻ p q x∈p∪q)

x∈p∪q⁺ : ∀ {n} {p q : Subset n} {x} → x ∈ p ⊎ x ∈ q → x ∈ p ∪ q
x∈p∪q⁺ (inj₁ x∈p) = p⊆p∪q _   x∈p
x∈p∪q⁺ (inj₂ x∈q) = q⊆p∪q _ _ x∈q

∪⇔⊎ : ∀ {n} {p q : Subset n} {x} → x ∈ p ∪ q ⇔ (x ∈ p ⊎ x ∈ q)
∪⇔⊎ = equivalence (x∈p∪q⁻ _ _) x∈p∪q⁺

module _ {n} (p q : Subset n) where

  ∣p∣≤∣p∪q∣ : ∣ p ∣ ≤ ∣ p ∪ q ∣
  ∣p∣≤∣p∪q∣ = p⊆q⇒∣p∣≤∣q∣ (p⊆p∪q {p = p} q)

  ∣q∣≤∣p∪q∣ : ∣ q ∣ ≤ ∣ p ∪ q ∣
  ∣q∣≤∣p∪q∣ = p⊆q⇒∣p∣≤∣q∣ (q⊆p∪q p q)

  ∣p∣⊔∣q∣≤∣p∪q∣ : ∣ p ∣ ⊔ ∣ q ∣ ≤ ∣ p ∪ q ∣
  ∣p∣⊔∣q∣≤∣p∪q∣ = ℕₚ.⊔-pres-≤m ∣p∣≤∣p∪q∣ ∣q∣≤∣p∪q∣

------------------------------------------------------------------------
-- Lift

Lift? : ∀ {n p} {P : Pred (Fin n) p} → Decidable P → Decidable (Lift P)
Lift? P? p = decFinSubset (_∈? p) (λ {x} _ → P? x)

------------------------------------------------------------------------
-- Other

module _ {p} {P : Pred (Subset zero) p} where

  ∃-Subset-zero : ∃⟨ P ⟩ → P []
  ∃-Subset-zero ([] , P[]) = P[]

  ∃-Subset-[]-⇔ : P [] ⇔ ∃⟨ P ⟩
  ∃-Subset-[]-⇔ = equivalence ([] ,_) ∃-Subset-zero

module _ {p n} {P : Pred (Subset (suc n)) p} where

  ∃-Subset-suc : ∃⟨ P ⟩ → ∃⟨ P ∘ (inside ∷_) ⟩ ⊎ ∃⟨ P ∘ (outside ∷_) ⟩
  ∃-Subset-suc (outside ∷ p , Pop) = inj₂ (p , Pop)
  ∃-Subset-suc ( inside ∷ p , Pip) = inj₁ (p , Pip)

  ∃-Subset-∷-⇔ : (∃⟨ P ∘ (inside ∷_) ⟩ ⊎ ∃⟨ P ∘ (outside ∷_) ⟩) ⇔ ∃⟨ P ⟩
  ∃-Subset-∷-⇔ = equivalence
    [ Product.map _ id , Product.map _ id ]′
    ∃-Subset-suc

anySubset? : ∀ {p n} {P : Pred (Subset n) p} → Decidable P → Dec ∃⟨ P ⟩
anySubset? {n = zero}  P? = Dec.map ∃-Subset-[]-⇔ (P? [])
anySubset? {n = suc n} P? =
  Dec.map ∃-Subset-∷-⇔ (anySubset? (P? ∘ ( inside ∷_)) ⊎-dec
                        anySubset? (P? ∘ (outside ∷_)))

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

-- Version 1.3

p⊆q⇒∣p∣<∣q∣ = p⊆q⇒∣p∣≤∣q∣
{-# WARNING_ON_USAGE p⊆q⇒∣p∣<∣q∣
"Warning: p⊆q⇒∣p∣<∣q∣ was deprecated in v1.3.
Please use p⊆q⇒∣p∣≤∣q∣ instead."
#-}


------------------------------------------------------------------------
-- The All predicate, defined using _∈_
------------------------------------------------------------------------

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

open import Equality

module List.All {c⁺} (eq : ∀ {a p} → Equality-with-J a p c⁺) where

open Derived-definitions-and-properties eq

open import Logical-equivalence using (_⇔_)
open import Prelude

open import Bag-equivalence eq
open import Bijection eq as Bijection using (_↔_)
import Equality.Groupoid eq as EG
open import Function-universe eq as F hiding (id; _∘_)
open import Groupoid eq
open import H-level eq
open import H-level.Closure eq
open import List eq as L using (_++_)
open import Monad eq hiding (map)
open import Vec.Function eq as Vec using (Vec)

private
  variable
    a b ℓ p q : Level
    A B       : Type a
    P         : A → Type p
    Q         : A → Type q
    k x y     : A
    n         : ℕ
    xs ys     : List A

-- All P xs means that P holds for every element of xs.

All : {A : Type a} → (A → Type p) → List A → Type (a ⊔ p)
All P xs = ∀ x → x ∈ xs → P x

-- The type xs ⊆ ys means that every element of xs is also an element
-- of ys.

infix 4 _⊆_

_⊆_ : {A : Type a} → List A → List A → Type a
xs ⊆ ys = All (_∈ ys) xs

-- The _⊆_ relation matches _∼[ implication ]_.

⊆↔∼[implication] : xs ⊆ ys ↔ xs ∼[ implication ] ys
⊆↔∼[implication] {xs = xs} {ys = ys} =
  xs ⊆ ys                  ↔⟨⟩
  All (_∈ ys) xs           ↔⟨⟩
  (∀ x → x ∈ xs → x ∈ ys)  ↔⟨⟩
  xs ∼[ implication ] ys   □

-- Some rearrangement lemmas.

All-[] :
  ∀ {k} {A : Type a} (P : A → Type p) →
  Extensionality? k a (a ⊔ p) →
  All P [] ↝[ k ] ⊤
All-[] {a = a} {k = k} {A} P ext =
  All P []              ↔⟨⟩
  (∀ y → y ∈ [] → P y)  ↔⟨⟩
  (∀ y → ⊥ → P y)       ↝⟨ (∀-cong ext λ _ → Π⊥↔⊤ (lower-extensionality? k a a ext)) ⟩
  (A → ⊤)               ↔⟨ →-right-zero ⟩□
  ⊤                     □

module _ {k} {A : Type a} {P : A → Type p}
         (ext : Extensionality? k a (a ⊔ p)) where

  private
    ext′ : Extensionality? k a p
    ext′ = lower-extensionality? k a a ext

  All-∷ : All P (x ∷ xs) ↝[ k ] P x × All P xs
  All-∷ {x = x} {xs = xs} =
    All P (x ∷ xs)                              ↔⟨⟩
    (∀ y → y ∈ x ∷ xs → P y)                    ↔⟨⟩
    (∀ y → y ≡ x ⊎ y ∈ xs → P y)                ↝⟨ ∀-cong ext (λ _ → Π⊎↔Π×Π ext′) ⟩
    (∀ y → (y ≡ x → P y) × (y ∈ xs → P y))      ↔⟨ ΠΣ-comm ⟩
    (∀ y → y ≡ x → P y) × (∀ y → y ∈ xs → P y)  ↝⟨ (×-cong₁ λ _ → ∀-cong ext λ _ → →-cong₁ ext′ ≡-comm) ⟩
    (∀ y → x ≡ y → P y) × (∀ y → y ∈ xs → P y)  ↝⟨ inverse-ext? (λ ext → ∀-intro _ ext) ext ×-cong F.id ⟩□
    P x × All P xs                              □

  All-++ : All P (xs ++ ys) ↝[ k ] All P xs × All P ys
  All-++ {xs = xs} {ys = ys} =
    All P (xs ++ ys)                             ↔⟨⟩
    (∀ x → x ∈ xs ++ ys → P x)                   ↝⟨ (∀-cong ext λ _ → →-cong₁ ext′ (Any-++ _ _ _)) ⟩
    (∀ x → x ∈ xs ⊎ x ∈ ys → P x)                ↝⟨ (∀-cong ext λ _ → Π⊎↔Π×Π ext′) ⟩
    (∀ x → (x ∈ xs → P x) × (x ∈ ys → P x))      ↔⟨ ΠΣ-comm ⟩
    (∀ x → x ∈ xs → P x) × (∀ x → x ∈ ys → P x)  ↔⟨⟩
    All P xs × All P ys                          □

  All-concat : All P (L.concat xs) ↝[ k ] All (All P) xs
  All-concat {xs = xss} =
    (∀ x → x ∈ L.concat xss → P x)              ↝⟨ ∀-cong ext (λ _ → →-cong₁ ext′ (Any-concat _ _)) ⟩
    (∀ x → Any (x ∈_) xss → P x)                ↝⟨ ∀-cong ext (λ _ → →-cong₁ ext′ (Any-∈ _ _)) ⟩
    (∀ x → (∃ λ xs → x ∈ xs × xs ∈ xss) → P x)  ↝⟨ ∀-cong ext (λ _ → from-bijection currying) ⟩
    (∀ x → ∀ xs → x ∈ xs × xs ∈ xss → P x)      ↔⟨ Π-comm ⟩
    (∀ xs → ∀ x → x ∈ xs × xs ∈ xss → P x)      ↝⟨ ∀-cong ext (λ _ → ∀-cong ext λ _ → from-bijection currying) ⟩
    (∀ xs → ∀ x → x ∈ xs → xs ∈ xss → P x)      ↝⟨ ∀-cong ext (λ _ → ∀-cong ext λ _ → from-bijection Π-comm) ⟩
    (∀ xs → ∀ x → xs ∈ xss → x ∈ xs → P x)      ↝⟨ ∀-cong ext (λ _ → from-bijection Π-comm) ⟩□
    (∀ xs → xs ∈ xss → ∀ x → x ∈ xs → P x)      □

All-map :
  ∀ {k} {A : Type a} {B : Type b} {P : B → Type p}
    {f : A → B} {xs : List A} →
  (ext : Extensionality? k (a ⊔ b) (a ⊔ b ⊔ p)) →
  All P (L.map f xs) ↝[ k ] All (P ∘ f) xs
All-map {a = a} {b = b} {p = p} {k = k} {P = P} {f} {xs} ext =
  (∀ x → x ∈ L.map f xs → P x)              ↝⟨ (∀-cong ext₁ λ _ → →-cong₁ ext₂ (Any-map _ _ _)) ⟩
  (∀ x → Any (λ y → x ≡ f y) xs → P x)      ↝⟨ (∀-cong ext₃ λ _ → →-cong₁ ext₄ (Any-∈ _ _)) ⟩
  (∀ x → (∃ λ y → x ≡ f y × y ∈ xs) → P x)  ↝⟨ (∀-cong ext₃ λ _ → from-bijection currying) ⟩
  (∀ x → ∀ y → x ≡ f y × y ∈ xs → P x)      ↝⟨ (∀-cong ext₃ λ _ → ∀-cong ext₅ λ _ → from-bijection currying) ⟩
  (∀ x → ∀ y → x ≡ f y → y ∈ xs → P x)      ↔⟨ Π-comm ⟩
  (∀ x → ∀ y → y ≡ f x → x ∈ xs → P y)      ↝⟨ (∀-cong ext₅ λ _ → ∀-cong ext₃ λ _ → →-cong₁ ext₆ ≡-comm) ⟩
  (∀ x → ∀ y → f x ≡ y → x ∈ xs → P y)      ↝⟨ (∀-cong ext₅ λ _ → inverse-ext? (λ ext → ∀-intro _ ext) ext₃) ⟩□
  (∀ x → x ∈ xs → P (f x))                  □
  where
  ext₁ = lower-extensionality? k a     a       ext
  ext₂ = lower-extensionality? k a     (a ⊔ b) ext
  ext₃ = lower-extensionality? k a     lzero   ext
  ext₄ = lower-extensionality? k lzero (a ⊔ b) ext
  ext₅ = lower-extensionality? k b     lzero   ext
  ext₆ = lower-extensionality? k a     b       ext

All->>= :
  ∀ {k} {A B : Type ℓ} {P : B → Type p} {f : A → List B} {xs : List A} →
  (ext : Extensionality? k ℓ (ℓ ⊔ p)) →
  All P (xs >>= f) ↝[ k ] All (All P ∘ f) xs
All->>= {P = P} {f = f} {xs = xs} ext =
  All P (L.concat (L.map f xs))  ↝⟨ All-concat ext ⟩
  All (All P) (L.map f xs)       ↝⟨ All-map ext ⟩□
  All (All P ∘ f) xs             □

All-const :
  {A : Type a} {B : Type b} {xs : List B} →
  Extensionality? k b a →
  All (const A) xs ↝[ k ] Vec A (L.length xs)
All-const {A = A} {xs = xs} ext =
  (∀ x → x ∈ xs → A)       ↔⟨ inverse currying ⟩
  (∃ (_∈ xs) → A)          ↝⟨ →-cong₁ ext (Fin-length _) ⟩□
  (Fin (L.length xs) → A)  □

private

  -- The following lemma that relates All-const and Fin-length holds
  -- by definition.

  All-const-Fin-length :
    ∀ {xs : List A} {bs : All (const B) xs} {i} →
    All-const _ bs i ≡
    uncurry bs (_↔_.from (Fin-length xs) i)
  All-const-Fin-length = refl _

All-const-replicate :
  {A : Type a} →
  Extensionality? k lzero a →
  All (const A) (L.replicate n tt) ↝[ k ] Vec A n
All-const-replicate {n = n} {A = A} ext =
  All (const A) (L.replicate n tt)     ↝⟨ All-const ext ⟩
  Vec A (L.length (L.replicate n tt))  ↝⟨ →-cong₁ ext $ ≡⇒↝ bijection $ cong Fin (L.length-replicate _) ⟩□
  Vec A n                              □

All-Σ :
  {A : Type a} {P : A → Type p} {Q : ∀ x → P x → Type q} {xs : List A} →
  Extensionality? k a (a ⊔ p ⊔ q) →
  All (λ x → Σ (P x) (Q x)) xs ↝[ k ]
  ∃ λ (ps : All P xs) → ∀ x (x∈xs : x ∈ xs) → Q x (ps x x∈xs)
All-Σ {P = P} {Q = Q} {xs = xs} ext =
  All (λ x → Σ (P x) (Q x)) xs                                   ↔⟨⟩

  (∀ x → x ∈ xs → Σ (P x) (Q x))                                 ↝⟨ (∀-cong ext λ _ → from-isomorphism ΠΣ-comm) ⟩

  (∀ x → ∃ λ (ps : x ∈ xs → P x) →
     (x∈xs : x ∈ xs) → Q x (ps x∈xs))                            ↔⟨ ΠΣ-comm ⟩

  (∃ λ (ps : ∀ x → x ∈ xs → P x) →
     ∀ x (x∈xs : x ∈ xs) → Q x (ps x x∈xs))                      ↔⟨⟩

  (∃ λ (ps : All P xs) → ∀ x (x∈xs : x ∈ xs) → Q x (ps x x∈xs))  □

-- Some abbreviations.

nil : All P []
nil {P = P} = _⇔_.from (All-[] P _) _

cons : P x → All P xs → All P (x ∷ xs)
cons = curry (_⇔_.from (All-∷ _))

head : All P (x ∷ xs) → P x
head = proj₁ ∘ _⇔_.to (All-∷ _)

tail : All P (x ∷ xs) → All P xs
tail = proj₂ ∘ _⇔_.to (All-∷ _)

append : All P xs → All P ys → All P (xs ++ ys)
append = curry (_⇔_.from (All-++ _))

-- All preserves h-levels (assuming extensionality).

H-level-All :
  {A : Type a} {P : A → Type p} →
  Extensionality a (a ⊔ p) →
  ∀ n →
  (∀ x → H-level n (P x)) →
  (∀ xs → H-level n (All P xs))
H-level-All {a = a} ext n h xs =
  Π-closure ext n λ _ →
  Π-closure (lower-extensionality a a ext) n λ _ →
  h _

-- Some lemmas related to append.

append-Any-++-inj₁ :
  ∀ {xs ys} {ps : All P xs} {qs : All P ys}
  (x∈xs : x ∈ xs) →
  append ps qs _ (_↔_.from (Any-++ _ _ _) (inj₁ x∈xs)) ≡
  ps _ x∈xs
append-Any-++-inj₁ {P = P} {x = x} {ps = ps} {qs} x∈xs =
  append ps qs _ (_↔_.from (Any-++ _ _ _) (inj₁ x∈xs))       ≡⟨⟩

  [ ps _ , qs _ ] (_↔_.to (Any-++ _ _ _)
                     (_↔_.from (Any-++ _ _ _) (inj₁ x∈xs)))  ≡⟨ cong [ ps _ , qs _ ] (_↔_.right-inverse-of (Any-++ _ _ _) _) ⟩

  [_,_] {C = λ _ → P x} (ps _) (qs _) (inj₁ x∈xs)            ≡⟨⟩

  ps _ x∈xs                                                  ∎

append-Any-++-inj₂ :
  ∀ xs {ys} {ps : All P xs} {qs : All P ys} {y∈ys : y ∈ ys} →
  append ps qs _ (_↔_.from (Any-++ _ xs _) (inj₂ y∈ys)) ≡
  qs _ y∈ys
append-Any-++-inj₂ {P = P} {y = y} xs {ps = ps} {qs} {y∈ys} =
  append ps qs _ (_↔_.from (Any-++ _ xs _) (inj₂ y∈ys))       ≡⟨⟩

  [ ps _ , qs _ ] (_↔_.to (Any-++ _ _ _)
                     (_↔_.from (Any-++ _ xs _) (inj₂ y∈ys)))  ≡⟨ cong [ ps _ , qs _ ] (_↔_.right-inverse-of (Any-++ _ _ _) _) ⟩

  [_,_] {C = λ _ → P y} (ps _) (qs _) (inj₂ y∈ys)             ≡⟨⟩

  qs _ y∈ys                                                   ∎

-- Some congruence lemmas.

All-cong :
  ∀ {k} {A : Type a} {P : A → Type p} {Q : A → Type q} {xs ys} →
  Extensionality? ⌊ k ⌋-sym a (a ⊔ p ⊔ q) →
  (∀ x → P x ↝[ ⌊ k ⌋-sym ] Q x) →
  xs ∼[ ⌊ k ⌋-sym ] ys →
  All P xs ↝[ ⌊ k ⌋-sym ] All Q ys
All-cong {a = a} {k = k} {P = P} {Q} {xs} {ys} ext P↝Q xs∼ys =
  All P xs              ↔⟨⟩
  (∀ x → x ∈ xs → P x)  ↝⟨ ∀-cong ext (λ _ → →-cong (lower-extensionality? ⌊ k ⌋-sym a a ext) (xs∼ys _) (P↝Q _)) ⟩
  (∀ x → x ∈ ys → Q x)  ↔⟨⟩
  All Q ys              □

All-cong-→ :
  (∀ x → P x → Q x) →
  ys ∼[ implication ] xs →
  All P xs → All Q ys
All-cong-→ {P = P} {Q = Q} {ys = ys} {xs = xs} P→Q ys∼xs =
  All P xs              ↔⟨⟩
  (∀ x → x ∈ xs → P x)  ↝⟨ ∀-cong _ (λ _ → →-cong-→ (ys∼xs _) (P→Q _)) ⟩
  (∀ x → x ∈ ys → Q x)  ↔⟨⟩
  All Q ys              □

map : (∀ x → P x → Q x) → ys ⊆ xs → All P xs → All Q ys
map P→Q ys∼xs = All-cong-→ P→Q ys∼xs

map₁ : (∀ x → P x → Q x) → All P xs → All Q xs
map₁ P→Q = map P→Q (λ _ → id)

map₂ : ys ⊆ xs → All P xs → All P ys
map₂ ys⊆xs = map (λ _ → id) ys⊆xs

private

  map₁-∘ :
    ∀ {P : A → Type p} {xs} {f : ∀ x → P x → Q x} {ps : All P xs} →
    map₁ f ps ≡ λ _ → f _ ∘ ps _
  map₁-∘ = refl _

  map₂-∘ :
    ∀ {xs ys} {ys⊆xs : ys ⊆ xs} {ps : All P xs} →
    map₂ ys⊆xs ps ≡ λ _ → ps _ ∘ ys⊆xs _
  map₂-∘ = refl _

-- Some properties that hold by definition.

map₂∘map₂ :
  ∀ {xs ys zs} {xs⊆ys : xs ⊆ ys} {ys⊆zs : ys ⊆ zs} {ps : All P zs} →
  map₂ xs⊆ys (map₂ ys⊆zs ps) ≡ map₂ (map₁ ys⊆zs xs⊆ys) ps
map₂∘map₂ = refl _

map₂-inj₂-∘ :
  ∀ {xs ys} {f : ys ⊆ xs} {p : P x} {ps : All P xs} →
  map₂ (λ _ → inj₂ ∘ f _) (cons p ps) ≡ map₂ f ps
map₂-inj₂-∘ = refl _

map₂-id :
  ∀ {xs} {ps : All P xs} →
  map₂ (λ _ → id) ps ≡ ps
map₂-id = refl _

map₂-inj₂ :
  ∀ {xs} {p : P x} {ps : All P xs} →
  map₂ (λ _ → inj₂) (cons p ps) ≡ ps
map₂-inj₂ = refl _

-- Some rearrangement lemmas for map₂.

map₂-⊎-map-id :
  {A : Type a} {P : A → Type p} →
  Extensionality a (a ⊔ p) →
  ∀ {x xs ys} {f : ys ⊆ xs} {p : P x} {ps : All P xs} →
  map₂ (λ _ → ⊎-map id (f _)) (cons p ps) ≡ cons p (map₂ f ps)
map₂-⊎-map-id {a = a} ext {f = f} {p} {ps} =
  apply-ext ext λ _ →
  apply-ext (lower-extensionality lzero a ext)
    [ (λ _ → refl _) , (λ _ → refl _) ]

map₂-⊎-map-id-inj₂ :
  {A : Type a} {P : A → Type p} →
  Extensionality a (a ⊔ p) →
  ∀ {x y xs} {p : P x} {q : P y} {ps : All P xs} →
  map₂ (λ _ → ⊎-map id inj₂) (cons p (cons q ps)) ≡ cons p ps
map₂-⊎-map-id-inj₂ ext {p = p} {q} {ps} =
  map₂ (λ _ → ⊎-map id inj₂) (cons p (cons q ps))  ≡⟨ map₂-⊎-map-id ext ⟩
  cons p (map₂ (λ _ → inj₂) (cons q ps))           ≡⟨⟩
  cons p ps                                        ∎

map₂-++-cong :
  {A : Type a} {P : A → Type p} →
  Extensionality a (a ⊔ p) →
  ∀ xs₁ {ys₁ xs₂ ys₂} {ps : All P xs₂} {qs : All P ys₂}
  {f : xs₁ ⊆ xs₂} {g : ys₁ ⊆ ys₂} →
  map₂ (++-cong f g) (append ps qs) ≡
  append (map₂ f ps) (map₂ g qs)
map₂-++-cong {a = a} ext _ {ps = ps} {qs} {f} {g} =
  apply-ext ext λ _ →
  apply-ext (lower-extensionality lzero a ext) λ x∈ →

    let lemma : ∀ x → [ ps _ , qs _ ] (⊎-map (f _) (g _) x) ≡
                      [ ps _ ∘ f _ , qs _ ∘ g _ ] x
        lemma = [ (λ _ → refl _) , (λ _ → refl _) ]
    in

    [ ps _ , qs _ ] (_↔_.to (Any-++ _ _ _) (_↔_.from (Any-++ _ _ _)
                       (⊎-map (f _) (g _) (_↔_.to (Any-++ _ _ _) x∈))))  ≡⟨ cong [ ps _ , qs _ ] (_↔_.right-inverse-of (Any-++ _ _ _) _) ⟩

    [ ps _ , qs _ ] (⊎-map (f _) (g _) (_↔_.to (Any-++ _ _ _) x∈))       ≡⟨ lemma (_↔_.to (Any-++ _ _ _) x∈) ⟩∎

    [ ps _ ∘ f _ , qs _ ∘ g _ ] (_↔_.to (Any-++ _ _ _) x∈)               ∎


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

data Unit : Set where
  unit : Unit

data Empty : Set where

qq : ∀ {a} {A : Set a} → A → Term → TC _
qq t hole = withNormalisation true do
  `t ← quoteTC t
  ``t ← quoteTC `t
  unify hole ``t

macro

  qU  = qq {A = Unit → Set → Set}
  qE  = qq {A = Empty → Set → Set}
  qA  = qq {A = Set → Set}

case_of_ : ∀ {a b} {A : Set a} {B : Set b} → A → (A → B) → B
case x of f = f x

pattern vArg x = arg (arg-info visible relevant) x
pattern [_] x = x ∷ []

map : {A B : Set} → (A → B) → List A → List B
map f [] = []
map f (x ∷ xs) = f x ∷ map f xs

mkArgs : List Nat → List (Arg Term)
mkArgs = map λ i → vArg (var i [])

unit,X=>_∙_ : Nat → List Nat → Term
unit,X=> n ∙ args =
  pat-lam [ clause [ "X" , vArg (agda-sort (lit 0)) ]
                   (vArg (con (quote unit) []) ∷ vArg (var 0) ∷ [])
                   (var n []) ]
          (mkArgs args)

abs,X=>∙_ : List Nat → Term
abs,X=>∙ is = pat-lam [ absurd-clause ( ("()" , vArg (def (quote Empty) [])) ∷  ("X" , vArg (agda-sort (lit 0))) ∷ [])
                                      (vArg absurd ∷ vArg (var 0) ∷ []) ]
                      (mkArgs is)

abs=>∙_ : List Nat → Term
abs=>∙ is = pat-lam [ absurd-clause [ "()" , vArg (def (quote Empty) []) ] (vArg absurd ∷ []) ]
                    (mkArgs is)

_ : qU (λ { unit X → X }) ≡ unit,X=> 0 ∙ []
_ = refl

_ : (B : Set) → qU (λ { unit X → B }) ≡ unit,X=> 1 ∙ []
_ = λ _ → refl

_ : qE (λ { () X }) ≡ abs,X=>∙ []
_ = refl

_ : (B : Set) → qE (λ { () X }) ≡ abs,X=>∙ []
_ = λ _ → refl

_ : (u : Unit) → qA (case u of λ { unit X → X }) ≡ (unit,X=> 0 ∙ [ 0 ])
_ = λ _ → refl

_ : (B : Set) (u : Unit) → qA (case u of λ { unit X → B }) ≡ unit,X=> 2 ∙ [ 0 ]
_ = λ _ _ → refl

_ : (B : Set) (e : Empty) → qA (case e of λ { () X }) ≡ abs,X=>∙ [ 0 ]
_ = λ _ _ → refl

_ : qE (λ ()) ≡ abs=>∙ []
_ = refl

_ : (B : Set) → qE (λ ()) ≡ abs=>∙ []
_ = λ _ → refl

_ : (B : Set) (e : Empty) → qA (case e of λ ()) ≡ abs=>∙ [ 0 ]
_ = λ _ _ → refl

module _ (A : Set) where

  _ : qU (λ { unit X → X }) ≡ unit,X=> 0 ∙ []
  _ = refl

  _ : (B : Set) → qU (λ { unit X → B }) ≡ unit,X=> 1 ∙ []
  _ = λ _ → refl

  _ : qE (λ { () X }) ≡ abs,X=>∙ []
  _ = refl

  _ : (u : Unit) → qA (case u of λ { unit X → X }) ≡ (unit,X=> 0 ∙ [ 0 ])
  _ = λ _ → refl

  _ : (B : Set) (u : Unit) → qA (case u of λ { unit X → B }) ≡ unit,X=> 2 ∙ [ 0 ]
  _ = λ _ _ → refl

  _ : (B : Set) (e : Empty) → qA (case e of λ { () X }) ≡ abs,X=>∙ [ 0 ]
  _ = λ _ _ → refl

  _ : qE (λ ()) ≡ abs=>∙ []
  _ = refl

  _ : (B : Set) → qE (λ ()) ≡ abs=>∙ []
  _ = λ _ → refl

  _ : (B : Set) (e : Empty) → qA (case e of λ ()) ≡ abs=>∙ [ 0 ]
  _ = λ _ _ → refl


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

open import HoTT
open import cw.CW
open import cw.FinCW
open import cw.FinBoundary
open import cohomology.Theory

module cw.cohomology.cochainequiv.FirstCoboundaryCommSquare (OT : OrdinaryTheory lzero)
  (⊙fin-skel : ⊙FinSkeleton 1) where

open OrdinaryTheory OT
open import cw.cohomology.WedgeOfCells OT ⊙⦉ ⊙fin-skel ⦊
open import cw.cohomology.reconstructed.TipAndAugment OT ⊙⦉ ⊙fcw-init ⊙fin-skel ⦊
open import cw.cohomology.reconstructed.TipCoboundary OT ⊙⦉ ⊙fin-skel ⦊
open import cw.cohomology.cochainequiv.DualizedFirstBoundary OT ⊙fin-skel
open import cw.cohomology.cochainequiv.FirstCoboundary OT ⊙fin-skel

private
  fin-skel = ⊙FinSkeleton.skel ⊙fin-skel
  I = AttachedFinSkeleton.numCells fin-skel
  ac = ⊙FinSkeleton-has-cells-with-choice 0 ⊙fin-skel lzero

  ⊙fin-skel₋₁ = ⊙fcw-init ⊙fin-skel
  fin-skel₋₁ = ⊙FinSkeleton.skel ⊙fin-skel₋₁
  ac₋₁ = ⊙FinSkeleton-has-cells-with-choice 0 ⊙fin-skel₋₁ lzero

abstract
  rephrase-dualized-first-boundary-comm-sqr :
    CommSquareEquiv
      (λ g <I → Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (fdegree-last fin-skel <I <I₋₁)))
      (_∘ᴳ fboundary-last fin-skel)
      (FreeAbGroup-extend (C2-abgroup 0))
      (FreeAbGroup-extend (C2-abgroup 0))
  rephrase-dualized-first-boundary-comm-sqr =
    comm-sqr (λ g →
      equiv-adj'
        (GroupIso.f-equiv (FreeAbGroup-extend-iso (C2-abgroup 0)))
        (λ= λ <I → ! (rephrase-dualized-first-boundary-in-degree g <I))) ,
    FreeAbGroup-extend-is-equiv (C2-abgroup 0) ,
    FreeAbGroup-extend-is-equiv (C2-abgroup 0)

  rephrase-cw-co∂-head-comm-sqr :
    CommSquareEquiv
      (λ g <I → Group.sum (C2 0) (λ <I' → Group.exp (C2 0) (g <I') (fdegree-last fin-skel <I <I')))
      (GroupHom.f cw-co∂-head)
      (GroupIso.g (C2×CX₀-diag-β ac₋₁))
      (GroupIso.g (CXₙ/Xₙ₋₁-diag-β ac))
  rephrase-cw-co∂-head-comm-sqr =
    comm-sqr (λ g →
      equiv-adj'
        (GroupIso.g-equiv (CXₙ/Xₙ₋₁-diag-β ac))
        (λ= λ <I → ! (rephrase-cw-co∂-head-in-degree g <I))) ,
    (GroupIso.g-is-equiv (C2×CX₀-diag-β ac₋₁)) ,
    (GroupIso.g-is-equiv (CXₙ/Xₙ₋₁-diag-β ac))

  first-coboundary-comm-sqr :
    CommSquareEquiv
      (GroupHom.f cw-co∂-head)
      (_∘ᴳ fboundary-last fin-skel)
      (FreeAbGroup-extend (C2-abgroup 0) ∘ GroupIso.f (C2×CX₀-diag-β ac₋₁))
      (FreeAbGroup-extend (C2-abgroup 0) ∘ GroupIso.f (CXₙ/Xₙ₋₁-diag-β ac))
  first-coboundary-comm-sqr =
    CommSquareEquiv-∘v
      rephrase-dualized-first-boundary-comm-sqr
      (CommSquareEquiv-inverse-v rephrase-cw-co∂-head-comm-sqr)

  first-coboundary-comm-sqrᴳ :
    CommSquareEquivᴳ
      cw-co∂-head
      (pre∘ᴳ-hom (C2-abgroup 0) (fboundary-last fin-skel))
      (FreeAbGroup-extend-hom (C2-abgroup 0) ∘ᴳ GroupIso.f-hom (C2×CX₀-diag-β ac₋₁))
      (FreeAbGroup-extend-hom (C2-abgroup 0) ∘ᴳ GroupIso.f-hom (CXₙ/Xₙ₋₁-diag-β ac))
  first-coboundary-comm-sqrᴳ =
    comm-sqrᴳ (fst first-coboundary-comm-sqr □$_) , snd first-coboundary-comm-sqr


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

module Data.Binary.Relations.Raw where

-- This module defines the "raw" relations on the binary types. In other words,
-- these relations are all functions which return either ⊤ or ⊥.
--
-- Each relation is generic of its strictness: if its first argument is O, it's a
-- strict relation (_<_); otherwise it's non-strict (_≤_).

open import Data.Binary.Bits
open import Data.Binary.Definitions
open import Data.Unit using (⊤; tt)
open import Data.Empty using (⊥; ⊥-elim)
open import Relation.Nullary
open import Data.Bool.Properties using (T?)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Binary.Operations.Unary
open import Data.Binary.Operations.Addition

------------------------------------------------------------------------
-- Definition of relations

infix 4 ⟅_⟆_≺ᵇ_ ⟅_⟆_≺⁺_ ⟅_⟆_≺_

⟅_⟆_≺ᵇ_ : Bit → Bit → Bit → Bit
⟅ p ⟆ I ≺ᵇ q = q ∧ p
⟅ p ⟆ O ≺ᵇ q = p ∨ q

⟅_⟆_≺⁺_ : Bit → 𝔹⁺ → 𝔹⁺ → Set
⟅ p ⟆ 1ᵇ       ≺⁺ (y ∷ ys) = ⊤
⟅ p ⟆ 1ᵇ       ≺⁺ 1ᵇ = T p
⟅ p ⟆ (x ∷ xs) ≺⁺ 1ᵇ = ⊥
⟅ p ⟆ (x ∷ xs) ≺⁺ (y ∷ ys) = ⟅ ⟅ p ⟆ x ≺ᵇ y ⟆ xs ≺⁺ ys

⟅_⟆_≺_ : Bit → 𝔹 → 𝔹 → Set
⟅ p ⟆ 0ᵇ ≺ 0ᵇ = T p
⟅ p ⟆ 0ᵇ ≺ (0< _) = ⊤
⟅ p ⟆ 0< xs ≺ 0ᵇ = ⊥
⟅ p ⟆ 0< xs ≺ 0< ys = ⟅ p ⟆ xs ≺⁺ ys

------------------------------------------------------------------------
-- Properties

weaken⁺ : ∀ x xs ys → ⟅ x ⟆ xs ≺⁺ ys → ⟅ I ⟆ xs ≺⁺ ys
weaken⁺ x (O ∷ xs) 1ᵇ xs<ys = xs<ys
weaken⁺ x (O ∷ xs) (y ∷ ys) xs<ys = weaken⁺ (x ∨ y) xs ys xs<ys
weaken⁺ x (I ∷ xs) 1ᵇ xs<ys = xs<ys
weaken⁺ x (I ∷ xs) (O ∷ ys) xs<ys = xs<ys
weaken⁺ x (I ∷ xs) (I ∷ ys) xs<ys = weaken⁺ x xs ys xs<ys
weaken⁺ O 1ᵇ 1ᵇ xs<ys = tt
weaken⁺ O 1ᵇ (x ∷ xs) xs<ys = tt
weaken⁺ I 1ᵇ ys xs<ys = xs<ys

weaken : ∀ xs ys → ⟅ O ⟆ xs ≺ ys → ⟅ I ⟆ xs ≺ ys
weaken (0< xs) (0< ys) xs<ys = weaken⁺ O xs ys xs<ys
weaken (0< x) 0ᵇ xs<ys = xs<ys
weaken 0ᵇ (0< x) xs<ys = tt
weaken 0ᵇ 0ᵇ xs<ys = tt

≺⁺-trans : ∀ x y xs ys zs → ⟅ x ⟆ xs ≺⁺ ys → ⟅ y ⟆ ys ≺⁺ zs → ⟅ x ∧ y ⟆ xs ≺⁺ zs
≺⁺-trans c₁ c₂ 1ᵇ ys (x ∷ zs) xs<ys ys<zs = tt
≺⁺-trans c₁ c₂ (x ∷ xs) 1ᵇ 1ᵇ xs<ys ys<zs = xs<ys
≺⁺-trans c₁ c₂ (x ∷ xs) 1ᵇ (z ∷ zs) () ys<zs
≺⁺-trans c₁ c₂ (x ∷ xs) (y ∷ ys) 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans O O 1ᵇ 1ᵇ 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans O O 1ᵇ (x ∷ xs) 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans O I 1ᵇ 1ᵇ 1ᵇ xs<ys ys<zs = xs<ys
≺⁺-trans O I 1ᵇ (x ∷ xs) 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans I O 1ᵇ 1ᵇ 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans I O 1ᵇ (x ∷ xs) 1ᵇ xs<ys ys<zs = ys<zs
≺⁺-trans I I 1ᵇ ys 1ᵇ xs<ys ys<zs = tt
≺⁺-trans I c₂ (I ∷ xs) (O ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans O (c₂ ∨ O) xs ys zs xs<ys ys<zs
≺⁺-trans I c₂ (I ∷ xs) (I ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans I O xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (I ∷ xs) (O ∷ ys) (I ∷ zs) xs<ys ys<zs = ≺⁺-trans O (c₂ ∨ I) xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (I ∷ xs) (I ∷ ys) (I ∷ zs) xs<ys ys<zs = ≺⁺-trans O c₂ xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (I ∷ xs) (O ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans O (c₂ ∨ O) xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (I ∷ xs) (I ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans O O xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (O ∷ xs) (y ∷ ys) (I ∷ zs) xs<ys ys<zs = weaken⁺ (y ∧ (⟅ c₂ ⟆ y ≺ᵇ I)) xs zs (≺⁺-trans y (⟅ c₂ ⟆ y ≺ᵇ I) xs ys zs xs<ys ys<zs)
≺⁺-trans O c₂ (O ∷ xs) (O ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans O (c₂ ∨ O) xs ys zs xs<ys ys<zs
≺⁺-trans O c₂ (O ∷ xs) (I ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans I O xs ys zs xs<ys ys<zs
≺⁺-trans I O (I ∷ xs) (O ∷ ys) (I ∷ zs) xs<ys ys<zs = ≺⁺-trans O I xs ys zs xs<ys ys<zs
≺⁺-trans I O (I ∷ xs) (I ∷ ys) (I ∷ zs) xs<ys ys<zs = ≺⁺-trans I O xs ys zs xs<ys ys<zs
≺⁺-trans I O (O ∷ xs) (y ∷ ys) (I ∷ zs) xs<ys ys<zs = weaken⁺ (⟅ O ⟆ y ≺ᵇ I) xs zs (≺⁺-trans I (⟅ O ⟆ y ≺ᵇ I) xs ys zs xs<ys ys<zs)
≺⁺-trans I O (O ∷ xs) (O ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans I O xs ys zs xs<ys ys<zs
≺⁺-trans I O (O ∷ xs) (I ∷ ys) (O ∷ zs) xs<ys ys<zs = ≺⁺-trans I O xs ys zs xs<ys ys<zs
≺⁺-trans I I (I ∷ xs) (O ∷ ys) (I ∷ zs) xs<ys ys<zs = weaken⁺ O xs zs (≺⁺-trans (O ∧ I) (⟅ I ⟆ O ≺ᵇ I) xs ys zs xs<ys ys<zs)
≺⁺-trans I I (I ∷ xs) (I ∷ ys) (I ∷ zs) xs<ys ys<zs = ≺⁺-trans (I ∧ I) (⟅ I ⟆ I ≺ᵇ I) xs ys zs xs<ys ys<zs
≺⁺-trans I I (O ∷ xs) (y ∷ ys) (z ∷ zs) xs<ys ys<zs = weaken⁺ (⟅ I ⟆ y ≺ᵇ z) xs zs (≺⁺-trans I (⟅ I ⟆ y ≺ᵇ z) xs ys zs xs<ys ys<zs)

≺-trans : ∀ x y xs ys zs → ⟅ x ⟆ xs ≺ ys → ⟅ y ⟆ ys ≺ zs → ⟅ x ∧ y ⟆ xs ≺ zs
≺-trans x y (0< x₁) (0< x₃) (0< x₂) xs<ys ys<zs = ≺⁺-trans x y x₁ x₃ x₂ xs<ys ys<zs
≺-trans x y (0< x₁) 0ᵇ (0< x₂) () ys<zs
≺-trans x y (0< x₁) (0< x₂) 0ᵇ xs<ys ys<zs = ys<zs
≺-trans x y (0< x₁) 0ᵇ 0ᵇ xs<ys ys<zs = xs<ys
≺-trans x y 0ᵇ ys (0< x₁) xs<ys ys<zs = tt
≺-trans x y 0ᵇ (0< x₁) 0ᵇ tt ()
≺-trans O O 0ᵇ 0ᵇ 0ᵇ xs<ys ys<zs = ys<zs
≺-trans O I 0ᵇ 0ᵇ 0ᵇ xs<ys ys<zs = xs<ys
≺-trans I O 0ᵇ 0ᵇ 0ᵇ xs<ys ys<zs = ys<zs
≺-trans I I 0ᵇ 0ᵇ 0ᵇ xs<ys ys<zs = tt

asym-≺⁺ : ∀ c xs ys → ⟅ c ⟆ ys ≺⁺ xs → ¬ ⟅ not c ⟆ xs ≺⁺ ys
asym-≺⁺ O 1ᵇ 1ᵇ ys<xs xs<ys = ys<xs
asym-≺⁺ O 1ᵇ (x ∷ xs) ys<xs xs<ys = ys<xs
asym-≺⁺ O (O ∷ xs) 1ᵇ ys<xs xs<ys = xs<ys
asym-≺⁺ O (O ∷ xs) (O ∷ ys) ys<xs xs<ys = asym-≺⁺ I ys xs xs<ys ys<xs
asym-≺⁺ O (O ∷ xs) (I ∷ ys) ys<xs xs<ys = asym-≺⁺ I ys xs xs<ys ys<xs
asym-≺⁺ O (I ∷ xs) 1ᵇ ys<xs xs<ys = xs<ys
asym-≺⁺ O (I ∷ xs) (O ∷ ys) ys<xs xs<ys = asym-≺⁺ O ys xs xs<ys ys<xs
asym-≺⁺ O (I ∷ xs) (I ∷ ys) ys<xs xs<ys = asym-≺⁺ I ys xs xs<ys ys<xs
asym-≺⁺ I 1ᵇ 1ᵇ ys<xs xs<ys = xs<ys
asym-≺⁺ I 1ᵇ (x ∷ xs) ys<xs xs<ys = ys<xs
asym-≺⁺ I (O ∷ xs) 1ᵇ ys<xs xs<ys = xs<ys
asym-≺⁺ I (O ∷ xs) (O ∷ ys) ys<xs xs<ys = asym-≺⁺ O ys xs xs<ys ys<xs
asym-≺⁺ I (O ∷ xs) (I ∷ ys) ys<xs xs<ys = asym-≺⁺ I ys xs xs<ys ys<xs
asym-≺⁺ I (I ∷ xs) 1ᵇ ys<xs xs<ys = xs<ys
asym-≺⁺ I (I ∷ xs) (O ∷ ys) ys<xs xs<ys = asym-≺⁺ O ys xs xs<ys ys<xs
asym-≺⁺ I (I ∷ xs) (I ∷ ys) ys<xs xs<ys = asym-≺⁺ O ys xs xs<ys ys<xs

asym-≺ : ∀ c xs ys → ⟅ c ⟆ ys ≺ xs → ¬ ⟅ not c ⟆ xs ≺ ys
asym-≺ c (0< xs) (0< ys) ys<xs xs<ys = asym-≺⁺ c xs ys ys<xs xs<ys
asym-≺ c (0< x) 0ᵇ ys<xs xs<ys = xs<ys
asym-≺ c 0ᵇ (0< x) ys<xs xs<ys = ys<xs
asym-≺ c 0ᵇ 0ᵇ ys<xs xs<ys = asym-≺⁺ c 1ᵇ 1ᵇ ys<xs xs<ys

pos-asym-≺⁺ : ∀ c xs ys → ¬ ⟅ c ⟆ ys ≺⁺ xs → ⟅ not c ⟆ xs ≺⁺ ys
pos-asym-≺⁺ c 1ᵇ (x ∷ ys) ys≺xs = tt
pos-asym-≺⁺ c (x ∷ xs) 1ᵇ ys≺xs = ys≺xs tt
pos-asym-≺⁺ c (I ∷ xs) (I ∷ ys) ys≺xs = pos-asym-≺⁺ c xs ys ys≺xs
pos-asym-≺⁺ O (O ∷ xs) (O ∷ ys) ys≺xs = pos-asym-≺⁺ O xs ys ys≺xs
pos-asym-≺⁺ I (O ∷ xs) (O ∷ ys) ys≺xs = pos-asym-≺⁺ I xs ys ys≺xs
pos-asym-≺⁺ O (O ∷ xs) (I ∷ ys) ys≺xs = pos-asym-≺⁺ O xs ys ys≺xs
pos-asym-≺⁺ I (O ∷ xs) (I ∷ ys) ys≺xs = pos-asym-≺⁺ O xs ys ys≺xs
pos-asym-≺⁺ O (I ∷ xs) (O ∷ ys) ys≺xs = pos-asym-≺⁺ I xs ys ys≺xs
pos-asym-≺⁺ I (I ∷ xs) (O ∷ ys) ys≺xs = pos-asym-≺⁺ I xs ys ys≺xs
pos-asym-≺⁺ O 1ᵇ 1ᵇ ys≺xs = tt
pos-asym-≺⁺ I 1ᵇ 1ᵇ ys≺xs = ys≺xs tt

pos-asym-≺ : ∀ c xs ys → ¬ ⟅ c ⟆ ys ≺ xs → ⟅ not c ⟆ xs ≺ ys
pos-asym-≺ c (0< xs) (0< ys) ys≮xs = pos-asym-≺⁺ c xs ys ys≮xs
pos-asym-≺ c (0< x) 0ᵇ ys≮xs = ys≮xs tt
pos-asym-≺ c 0ᵇ (0< x) ys≮xs = tt
pos-asym-≺ O 0ᵇ 0ᵇ ys≮xs = tt
pos-asym-≺ I 0ᵇ 0ᵇ ys≮xs = ys≮xs tt

≺⁺-antisym : ∀ c → Antisymmetric _≡_ ⟅ c ⟆_≺⁺_
≺⁺-antisym c {1ᵇ} {1ᵇ} xs<ys ys<xs = refl
≺⁺-antisym c {1ᵇ} {x ∷ ys} xs<ys ()
≺⁺-antisym c {x ∷ xs} {1ᵇ} () ys<xs
≺⁺-antisym c {O ∷ xs} {O ∷ ys} xs<ys ys<xs = cong (O ∷_) (≺⁺-antisym (c ∨ O) xs<ys ys<xs)
≺⁺-antisym c {O ∷ xs} {I ∷ ys} xs<ys ys<xs = ⊥-elim (asym-≺⁺ O xs ys ys<xs (weaken⁺ (c ∨ I) xs ys xs<ys))
≺⁺-antisym c {I ∷ xs} {O ∷ ys} xs<ys ys<xs = ⊥-elim (asym-≺⁺ O ys xs xs<ys (weaken⁺ (c ∨ I) ys xs ys<xs))
≺⁺-antisym c {I ∷ xs} {I ∷ ys} xs<ys ys<xs = cong (I ∷_) (≺⁺-antisym c xs<ys ys<xs)

≺-antisym : ∀ c → Antisymmetric _≡_ ⟅ c ⟆_≺_
≺-antisym c {0< x} {0< x₁} xs<ys ys<xs = cong 0<_ (≺⁺-antisym c xs<ys ys<xs)
≺-antisym c {0< x} {0ᵇ} () ys<xs
≺-antisym c {0ᵇ} {0< x} xs<ys ()
≺-antisym c {0ᵇ} {0ᵇ} xs<ys ys<xs = refl

≼-refl : ∀ xs → ⟅ I ⟆ xs ≺⁺ xs
≼-refl 1ᵇ = tt
≼-refl (O ∷ xs) = ≼-refl xs
≼-refl (I ∷ xs) = ≼-refl xs

≺⁺-inc⁺⁺ : ∀ x xs → ⟅ x ⟆ xs ≺⁺ inc⁺⁺ xs
≺⁺-inc⁺⁺ _ 1ᵇ = tt
≺⁺-inc⁺⁺ c (I ∷ xs) = ≺⁺-inc⁺⁺ O xs
≺⁺-inc⁺⁺ O (O ∷ xs) = ≼-refl xs
≺⁺-inc⁺⁺ I (O ∷ xs) = ≼-refl xs

≺⁺-add : ∀ ys xs c₁ c₂  → ⟅ c₁ ⟆ xs ≺⁺ add c₂ ys xs
≺⁺-add 1ᵇ 1ᵇ c₁ O = tt
≺⁺-add 1ᵇ 1ᵇ c₁ I = tt
≺⁺-add 1ᵇ (x ∷ xs) c₁ O = ≺⁺-inc⁺⁺ c₁ (x ∷ xs)
≺⁺-add 1ᵇ (x ∷ xs) c₁ I = ≺⁺-inc⁺⁺ (⟅ c₁ ⟆ x ≺ᵇ x) xs
≺⁺-add (y ∷ ys) (x ∷ xs) c₁ c₂ = ≺⁺-add ys xs (⟅ c₁ ⟆ x ≺ᵇ sumᵇ c₂ y x) (carryᵇ c₂ y x)
≺⁺-add (y ∷ ys) 1ᵇ c₁ I = tt
≺⁺-add (O ∷ ys) 1ᵇ c₁ O = tt
≺⁺-add (I ∷ ys) 1ᵇ c₁ O = tt

≺-add : ∀ ys xs → ⟅ I ⟆ xs ≺ ys + xs
≺-add (0< x) (0< x₁) = ≺⁺-add x x₁ I O
≺-add (0< x) 0ᵇ = tt
≺-add 0ᵇ (0< x) = ≼-refl x
≺-add 0ᵇ 0ᵇ = tt
------------------------------------------------------------------------
-- Decidability

infix 4 _!_≺⁺?_ _!_≺?_

_!_≺⁺?_ : ∀ x xs ys → Dec (⟅ x ⟆ xs ≺⁺ ys)
c ! 1ᵇ ≺⁺? x ∷ xs = yes tt
c ! 1ᵇ ≺⁺? 1ᵇ = T? c
c ! x ∷ xs ≺⁺? 1ᵇ = no (λ z → z)
c ! x ∷ xs ≺⁺? y ∷ ys = (⟅ c ⟆ x ≺ᵇ y) ! xs ≺⁺? ys

_!_≺?_ : ∀ x xs ys → Dec (⟅ x ⟆ xs ≺ ys)
c ! 0< xs ≺? 0< ys = c ! xs ≺⁺? ys
c ! 0< xs ≺? 0ᵇ = no (λ z → z)
c ! 0ᵇ ≺? 0< _ = yes tt
c ! 0ᵇ ≺? 0ᵇ = T? c

compare-≺ : ∀ c → Conn ⟅ c ⟆_≺_ ⟅ not c ⟆_≺_
compare-≺ c xs ys with c ! xs ≺? ys
compare-≺ c xs ys | yes p = inj₁ p
compare-≺ c xs ys | no ¬p = inj₂ (pos-asym-≺ c ys xs ¬p)


{-# OPTIONS --safe #-}

module Definition.Typed.Consequences.RelevanceUnicity where

open import Definition.Untyped hiding (U≢ℕ; U≢Π; U≢ne; ℕ≢Π; ℕ≢ne; Π≢ne; U≢Empty; ℕ≢Empty; Empty≢Π; Empty≢ne)
open import Definition.Untyped.Properties using (subst-Univ-either)
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening
open import Definition.Typed.Consequences.Equality
import Definition.Typed.Consequences.Inequality as Ineq
open import Definition.Typed.Consequences.Inversion
open import Definition.Typed.Consequences.Injectivity
open import Definition.Typed.Consequences.NeTypeEq
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.PiNorm
open import Definition.Typed.Consequences.Substitution

open import Tools.Product
open import Tools.Empty
open import Tools.Sum using (_⊎_; inj₁; inj₂)
import Tools.PropositionalEquality as PE

ℕ-relevant-term : ∀ {Γ A r} → Γ ⊢ ℕ ∷ A ^ r → Whnf A → A PE.≡ Univ ! ⁰
ℕ-relevant-term [ℕ] whnfA = let [[N]] , e = inversion-ℕ [ℕ]
                             in U≡A-whnf (sym (PE.subst (λ r → _ ⊢ _ ≡ _ ^ r) e [[N]])) whnfA

ℕ-relevant : ∀ {Γ r} → Γ ⊢ ℕ ^ r → r PE.≡ [ ! , ι ⁰ ]
ℕ-relevant (univ [ℕ]) = let er , el = Univ-PE-injectivity (ℕ-relevant-term [ℕ] Uₙ)
                         in PE.cong₂ (λ x y → [ x , ι y ]) er el

Empty-irrelevant-term : ∀ {Γ A lEmpty r} → Γ ⊢ Empty lEmpty ∷ A ^ r → Whnf A → A PE.≡ SProp lEmpty
Empty-irrelevant-term [Empty] whnfA = let [[Empty]] , e = inversion-Empty [Empty]
                                      in U≡A-whnf (sym (PE.subst (λ r → _ ⊢ _ ≡ _ ^ r) e [[Empty]])) whnfA

Empty-irrelevant : ∀ {Γ lEmpty r} → Γ ⊢ Empty lEmpty ^ r → r PE.≡ [ % , ι lEmpty ]
Empty-irrelevant (univ [Empty]) = let er , el = Univ-PE-injectivity (Empty-irrelevant-term [Empty] Uₙ)
                                  in PE.cong₂ (λ x y → [ x , ι y ]) er el

Univ-relevant-term : ∀ {Γ A rU lU r} → Γ ⊢ Univ rU lU ∷ A ^ r → Whnf A → A PE.≡ U ¹ × lU PE.≡ ⁰
Univ-relevant-term [U] whnfA = U≡A-whnf (sym (proj₁ (inversion-U [U]))) whnfA , proj₂ (proj₂ (inversion-U [U]))

Univ-relevant : ∀ {Γ rU lU r} → Γ ⊢ Univ rU lU ^ r → r PE.≡ [ ! , next lU ]
Univ-relevant (Uⱼ _) = PE.refl
Univ-relevant (univ [U]) = let er , el = Univ-PE-injectivity (proj₁ (Univ-relevant-term [U] Uₙ))
                           in PE.cong₂ (λ x y → [ x , y ]) er
                                       (PE.trans (PE.cong ι el) (PE.cong next (PE.sym  (proj₂ (Univ-relevant-term [U] Uₙ)))))
mutual
  Univ-uniq′ : ∀ {Γ A T₁ T₂ r₁ r₂ l₁ l₁' l₂ l₂'} → Γ ⊢ T₁ ≡ Univ r₁ l₁ ^ [ ! , l₁' ] → Γ ⊢ T₂ ≡ Univ r₂ l₂ ^ [ ! , l₂' ]
    → next l₁ PE.≡ l₁' → next l₂ PE.≡ l₂'
    → ΠNorm A
    → Γ ⊢ A ∷ T₁ ^ [ ! , l₁' ] → Γ ⊢ A ∷ T₂ ^ [ ! , l₂' ] → r₁ PE.≡ r₂ × l₁' PE.≡ l₂'
  Univ-uniq′ e₁ e₂ el₁ el₂ w (univ 0<1 x₁) (univ 0<1 x₃) =
    let er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.refl
  Univ-uniq′ e₁ e₂ el₁ PE.refl w (ℕⱼ x) y =
    let e₁′ , el₁′  = Uinjectivity e₁
        e₂′ , el₂′ = Uinjectivity (trans (sym e₂) (proj₁ (inversion-ℕ y)) )
    in PE.sym (PE.trans e₂′ e₁′) , PE.cong next (PE.sym el₂′)
  Univ-uniq′ e₁ e₂ el₁ el₂ w (Emptyⱼ x) y =
    let e₁′ , el₁′  = Uinjectivity e₁
        e₂′ , el₂′ = Uinjectivity (trans (sym e₂) (proj₁ (inversion-Empty y)) )
    in PE.sym (PE.trans e₂′ e₁′) , PE.trans (PE.cong next (PE.sym el₂′)) el₂
  Univ-uniq′ e₁ e₂ el₁ el₂ w (Πⱼ a ▹ b ▹ x ▹ x₁) (Πⱼ a' ▹ b' ▹ y ▹ y₁) =
    let er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
        res = Univ-uniq′ (refl (Ugenⱼ (wfTerm x₁))) (refl (Ugenⱼ (wfTerm x₁)))
                                                    PE.refl PE.refl (ΠNorm-Π w) x₁ y₁
    in PE.trans (PE.sym er₁) (PE.trans (proj₁ res) er₂) , PE.refl
  Univ-uniq′ e₁ e₂ el₁ el₂ (∃ₙ w) (∃ⱼ x ▹ x₁) (∃ⱼ y ▹ y₁) =
    let er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
        _ , el = Univ-uniq w x y
    in  PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ w (var _ x) (var _ y) =
    let T≡T , e = varTypeEq′ x y
        _ , el = typelevel-injectivity e
        ⊢T≡T = PE.subst (λ T → _ ⊢ _ ≡ T ^ _) T≡T (refl (proj₁ (syntacticEq e₁)))
    in proj₁ (Uinjectivity (trans (trans (sym e₁) ⊢T≡T) (PE.subst (λ lx → _ ⊢ _ ≡ _ ^ [ _ , lx ]) (PE.sym el) e₂))) , el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne ()) (lamⱼ x x₁ x₂ X) y
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (∘ₙ n)) (_∘ⱼ_ {G = G} x x₁) (_∘ⱼ_ {G = G₁} y y₁) =
    let F≡F , rF≡rF , lF≡lF , lG≡lG , G≡G = injectivity (proj₂ (neTypeEq n x y))
        r≡r , _ = Uinjectivity (trans (sym e₁) (trans (substitutionEq G≡G (substRefl (singleSubst x₁)) (wfEq F≡F))
                                               (PE.subst (λ lx → _ ⊢ _ ≡ _ ^ [ _ , ι lx ]) (PE.sym lG≡lG) e₂)))
    in r≡r , PE.cong ι lG≡lG
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne ()) (zeroⱼ x) y
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne ()) (sucⱼ X) y
  Univ-uniq′ e₁ e₂ el₁ el₂ w (natrecⱼ x x₁ x₂ x₃) (natrecⱼ x₄ y y₁ y₂) = proj₁ (Uinjectivity (trans (sym e₁) e₂)) , PE.refl
  Univ-uniq′ e₁ e₂ el₁ el₂ w (Emptyrecⱼ x x₁) (Emptyrecⱼ y y₁) = proj₁ (Uinjectivity (trans (sym e₁) e₂)) , PE.refl
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (Idₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq (ne x) X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (Idℕₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq ℕₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (Idℕ0ₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq ℕₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (IdℕSₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq ℕₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (IdUₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq Uₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (IdUℕₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq Uₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ (ne (IdUΠₙ x)) (Idⱼ X X₁ X₂) (Idⱼ {l = ll} Y Y₁ Y₂) =
    let _ , el = Univ-uniq Uₙ X Y
        er₁ , _ = Uinjectivity e₁
        er₂ , _ = Uinjectivity e₂
    in PE.trans (PE.sym er₁) er₂ , PE.cong next el
  Univ-uniq′ e₁ e₂ el₁ el₂ w (castⱼ X X₁ X₂ X₃) (castⱼ y y₁ y₂ y₃) = proj₁ (Uinjectivity (trans (sym e₁) e₂)) , PE.refl
  Univ-uniq′ e₁ e₂ el₁ el₂ w (conv x x₁) y = Univ-uniq′ (trans x₁ e₁) e₂ el₁ el₂ w x y
  Univ-uniq′ e₁ e₂ el₁ el₂ w x (conv y y₁) = Univ-uniq′ e₁ (trans y₁ e₂) el₁ el₂ w x y

  Univ-uniq : ∀ {Γ A r₁ r₂ l₁ l₂} → ΠNorm A
    → Γ ⊢ A ∷ Univ r₁ l₁ ^ [ ! , next l₁ ] → Γ ⊢ A ∷ Univ r₂ l₂ ^ [ ! , next l₂ ] → r₁ PE.≡ r₂ × l₁ PE.≡ l₂
  Univ-uniq n ⊢A₁ ⊢A₂ =
    let ⊢Γ = wfTerm ⊢A₁
        er , el =  Univ-uniq′ (refl (Ugenⱼ ⊢Γ)) (refl (Ugenⱼ ⊢Γ)) PE.refl PE.refl n ⊢A₁ ⊢A₂
    in er , next-inj el

relevance-unicity′ : ∀ {Γ A r₁ r₂ l₁ l₂} → ΠNorm A → Γ ⊢ A ^ [ r₁ , l₁ ] → Γ ⊢ A ^ [ r₂ , l₂ ] → r₁ PE.≡ r₂ × l₁ PE.≡ l₂
relevance-unicity′ n (Uⱼ x) (Uⱼ x₁) = PE.refl , PE.refl
relevance-unicity′ n (Uⱼ x) (univ x₁) = let _ , _ , ¹≡⁰ = inversion-U x₁ in ⊥-elim (⁰≢¹ (PE.sym  ¹≡⁰))
relevance-unicity′ n (univ x) (Uⱼ x₁) = let _ , _ , ¹≡⁰ = inversion-U x in ⊥-elim (⁰≢¹ (PE.sym  ¹≡⁰))
relevance-unicity′ n (univ x) (univ x₁) = let er , el = Univ-uniq n x x₁ in er , PE.cong ι el

relevance-unicity : ∀ {Γ A r₁ r₂ l₁ l₂} → Γ ⊢ A ^ [ r₁ , l₁ ] → Γ ⊢ A ^ [ r₂ , l₂ ] → r₁ PE.≡ r₂ × l₁ PE.≡ l₂
relevance-unicity ⊢A₁ ⊢A₂ with doΠNorm ⊢A₁
... | _ with doΠNorm ⊢A₂
relevance-unicity ⊢A₁ ⊢A₂ | B , nB , ⊢B , rB | C , nC , ⊢C , rC =
  let e = detΠNorm* nB nC rB rC
  in relevance-unicity′ nC (PE.subst _ e ⊢B) ⊢C

-- inequalities at any relevance
U≢ℕ : ∀ {r r′ l l′ Γ} → Γ ⊢ Univ r l ≡ ℕ ^ [ r′ , l′ ] → ⊥
U≢ℕ U≡ℕ = Ineq.U≢ℕ! (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ])
                              (proj₁ (relevance-unicity (proj₂ (syntacticEq U≡ℕ))
                                                 (univ (ℕⱼ (wfEq U≡ℕ)))))
                              U≡ℕ)

U≢Π : ∀ {rU lU  F rF G lF lG lΠ r Γ} → Γ ⊢ Univ rU lU ≡ Π F ^ rF ° lF ▹ G ° lG ° lΠ  ^ [ r , ι lΠ ] → ⊥
U≢Π U≡Π =
  let r≡! , _ = relevance-unicity (proj₁ (syntacticEq U≡Π)) (Ugenⱼ (wfEq U≡Π))
  in Ineq.U≢Π! (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡! U≡Π)

U≢ne : ∀ {rU lU r l K Γ} → Neutral K → Γ ⊢ Univ rU lU ≡ K ^ [ r , ι l ] → ⊥
U≢ne neK U≡K =
  let r≡! , _ = relevance-unicity (proj₁ (syntacticEq U≡K)) (Ugenⱼ (wfEq U≡K))
  in Ineq.U≢ne! neK (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡! U≡K)

ℕ≢Π : ∀ {F rF G lF lG r Γ} → Γ ⊢ ℕ ≡ Π F ^ rF ° lF ▹ G ° lG ° ⁰ ^ [ r , ι ⁰ ] → ⊥
ℕ≢Π ℕ≡Π =
  let r≡! , _ = relevance-unicity (proj₁ (syntacticEq ℕ≡Π)) (univ (ℕⱼ (wfEq ℕ≡Π)))
  in Ineq.ℕ≢Π! (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡! ℕ≡Π)

Empty≢Π : ∀ {F rF G lF lG lΠ r Γ} → Γ ⊢ Empty lΠ ≡ Π F ^ rF ° lF ▹ G ° lG ° lΠ  ^ [ r , ι lΠ ] → ⊥
Empty≢Π Empty≡Π =
  let r≡% , _ = relevance-unicity (proj₁ (syntacticEq Empty≡Π)) (univ (Emptyⱼ (wfEq Empty≡Π)))
  in Ineq.Empty≢Π% (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡% Empty≡Π)

ℕ≢ne : ∀ {K r Γ} → Neutral K → Γ ⊢ ℕ ≡ K ^ [ r , ι ⁰ ] → ⊥
ℕ≢ne neK ℕ≡K =
  let r≡! , _ = relevance-unicity (proj₁ (syntacticEq ℕ≡K)) (univ (ℕⱼ (wfEq ℕ≡K)))
  in Ineq.ℕ≢ne! neK (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡! ℕ≡K)

Empty≢ne : ∀ {K r l Γ} → Neutral K → Γ ⊢ Empty l ≡ K ^ [ r , ι l ] → ⊥
Empty≢ne neK Empty≡K =
  let r≡% , _ = relevance-unicity (proj₁ (syntacticEq Empty≡K)) (univ (Emptyⱼ (wfEq Empty≡K)))
  in Ineq.Empty≢ne% neK (PE.subst (λ rx → _ ⊢ _ ≡ _ ^ [ rx , _ ]) r≡% Empty≡K)

-- U != Empty is given easily by relevances
U≢Empty : ∀ {Γ rU lU lEmpty r′} → Γ ⊢ Univ rU lU ≡ Empty lEmpty ^ r′ → ⊥
U≢Empty U≡Empty =
  let ⊢U , ⊢Empty = syntacticEq U≡Empty
      e₁ , _ = relevance-unicity ⊢U (Ugenⱼ (wfEq U≡Empty))
      e₂ , _ = relevance-unicity ⊢Empty (univ (Emptyⱼ (wfEq U≡Empty)))
  in !≢% (PE.trans (PE.sym e₁) e₂)

-- ℕ and Empty also by relevance
ℕ≢Empty : ∀ {Γ r l} → Γ ⊢ ℕ ≡ Empty l ^ r → ⊥
ℕ≢Empty ℕ≡Empty =
  let ⊢ℕ , ⊢Empty = syntacticEq ℕ≡Empty
      e₁ , _ = relevance-unicity ⊢ℕ (univ (ℕⱼ (wfEq ℕ≡Empty)))
      e₂ , _ = relevance-unicity ⊢Empty (univ (Emptyⱼ (wfEq ℕ≡Empty)))
  in !≢% (PE.trans (PE.sym e₁) e₂)


{-

Closure properties of FinSet under several type constructors.

-}
{-# OPTIONS --safe #-}

module Cubical.Data.FinSet.Constructors where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv

open import Cubical.HITs.PropositionalTruncation renaming (rec to TruncRec)

open import Cubical.Data.Nat
open import Cubical.Data.Unit
open import Cubical.Data.Empty renaming (rec to EmptyRec)
open import Cubical.Data.Sum
open import Cubical.Data.Sigma

open import Cubical.Data.Fin
open import Cubical.Data.SumFin renaming (Fin to SumFin) hiding (discreteFin)
open import Cubical.Data.FinSet.Base
open import Cubical.Data.FinSet.Properties
open import Cubical.Data.FinSet.FiniteChoice

open import Cubical.Relation.Nullary

open import Cubical.Functions.Embedding
open import Cubical.Functions.Surjection

private
  variable
    ℓ ℓ' ℓ'' ℓ''' : Level

module _
  (X : Type ℓ)(p : ≃Fin X) where

  ≃Fin∥∥ : ≃Fin ∥ X ∥
  ≃Fin∥∥ = ≃SumFin→Fin (_ , compEquiv (propTrunc≃ (≃Fin→SumFin p .snd)) (SumFin∥∥≃ _))

module _
  (X : Type ℓ )(p : ≃Fin X)
  (Y : Type ℓ')(q : ≃Fin Y) where

  ≃Fin⊎ : ≃Fin (X ⊎ Y)
  ≃Fin⊎ = ≃SumFin→Fin (_ , compEquiv (⊎-equiv (≃Fin→SumFin p .snd) (≃Fin→SumFin q .snd)) (SumFin⊎≃ _ _))

  ≃Fin× : ≃Fin (X × Y)
  ≃Fin× = ≃SumFin→Fin (_ , compEquiv (Σ-cong-equiv (≃Fin→SumFin p .snd) (λ _ → ≃Fin→SumFin q .snd)) (SumFin×≃ _ _))

module _
  (X : Type ℓ )(p : ≃Fin X)
  (Y : X → Type ℓ')(q : (x : X) → ≃Fin (Y x)) where

  private
    p' = ≃Fin→SumFin p

    m = p' .fst
    e = p' .snd

    q' : (x : X) → ≃SumFin (Y x)
    q' x = ≃Fin→SumFin (q x)

    f : (x : X) → ℕ
    f x = q' x .fst

  ≃SumFinΣ : ≃SumFin (Σ X Y)
  ≃SumFinΣ = _ ,
      Σ-cong-equiv {B' = λ x → Y (invEq (p' .snd) x)} (p' .snd) (transpFamily p')
    ⋆ Σ-cong-equiv-snd (λ x → q' (invEq e x) .snd)
    ⋆ SumFinΣ≃ _ _

  ≃SumFinΠ : ≃SumFin ((x : X) → Y x)
  ≃SumFinΠ = _ ,
      equivΠ {B' = λ x → Y (invEq (p' .snd) x)} (p' .snd) (transpFamily p')
    ⋆ equivΠCod (λ x → q' (invEq e x) .snd)
    ⋆ SumFinΠ≃ _ _

  ≃FinΣ : ≃Fin (Σ X Y)
  ≃FinΣ = ≃SumFin→Fin ≃SumFinΣ

  ≃FinΠ : ≃Fin ((x : X) → Y x)
  ≃FinΠ = ≃SumFin→Fin ≃SumFinΠ

module _
  (X : FinSet ℓ)
  (Y : X .fst → FinSet ℓ') where

  isFinSetΣ : isFinSet (Σ (X .fst) (λ x → Y x .fst))
  isFinSetΣ =
    elim2 (λ _ _ → isPropIsFinSet {A = Σ (X .fst) (λ x → Y x .fst)})
          (λ p q → ∣ ≃FinΣ (X .fst) p (λ x → Y x .fst) q ∣)
          (X .snd) (choice X (λ x → ≃Fin (Y x .fst)) (λ x → Y x .snd))

  isFinSetΠ : isFinSet ((x : X .fst) → Y x .fst)
  isFinSetΠ =
    elim2 (λ _ _ → isPropIsFinSet {A = ((x : X .fst) → Y x .fst)})
          (λ p q → ∣ ≃FinΠ (X .fst) p (λ x → Y x .fst) q ∣)
          (X .snd) (choice X (λ x → ≃Fin (Y x .fst)) (λ x → Y x .snd))

module _
  (X : FinSet ℓ)
  (Y : X .fst → FinSet ℓ')
  (Z : (x : X .fst) → Y x .fst → FinSet ℓ'') where

  isFinSetΠ2 : isFinSet ((x : X .fst) → (y : Y x .fst) → Z x y .fst)
  isFinSetΠ2 = isFinSetΠ X (λ x → _ , isFinSetΠ (Y x) (Z x))

module _
  (X : FinSet ℓ)
  (Y : X .fst → FinSet ℓ')
  (Z : (x : X .fst) → Y x .fst → FinSet ℓ'')
  (W : (x : X .fst) → (y : Y x .fst) → Z x y .fst → FinSet ℓ''') where

  isFinSetΠ3 : isFinSet ((x : X .fst) → (y : Y x .fst) → (z : Z x y .fst) → W x y z .fst)
  isFinSetΠ3 = isFinSetΠ X (λ x → _ , isFinSetΠ2 (Y x) (Z x) (W x))

module _
  (X : FinSet ℓ) where

  isFinSet≡ : (a b : X .fst) → isFinSet (a ≡ b)
  isFinSet≡ a b = isDecProp→isFinSet (isFinSet→isSet (X .snd) a b) (isFinSet→Discrete (X .snd) a b)

  isFinSetIsContr : isFinSet (isContr (X .fst))
  isFinSetIsContr = isFinSetΣ X (λ x → _ , (isFinSetΠ X (λ y → _ , isFinSet≡ x y)))

  isFinSet∥∥ : isFinSet ∥ X .fst ∥
  isFinSet∥∥ = TruncRec isPropIsFinSet (λ p → ∣ ≃Fin∥∥ (X .fst) p ∣) (X .snd)

module _
  (X : FinSet ℓ )
  (Y : FinSet ℓ')
  (f : X .fst → Y .fst) where

  isFinSetFiber : (y : Y .fst) → isFinSet (fiber f y)
  isFinSetFiber y = isFinSetΣ X (λ x → _ , isFinSet≡ Y (f x) y)

  isFinSetIsEquiv : isFinSet (isEquiv f)
  isFinSetIsEquiv =
    EquivPresIsFinSet
      (invEquiv (isEquiv≃isEquiv' f))
      (isFinSetΠ Y (λ y → _ , isFinSetIsContr (_ , isFinSetFiber y)))

module _
  (X : FinSet ℓ )
  (Y : FinSet ℓ') where

  isFinSet⊎ : isFinSet (X .fst ⊎ Y .fst)
  isFinSet⊎ = elim2 (λ _ _ → isPropIsFinSet) (λ p q → ∣ ≃Fin⊎ (X .fst) p (Y .fst) q ∣) (X .snd) (Y .snd)

  isFinSet× : isFinSet (X .fst × Y .fst)
  isFinSet× = elim2 (λ _ _ → isPropIsFinSet) (λ p q → ∣ ≃Fin× (X .fst) p (Y .fst) q ∣) (X .snd) (Y .snd)

  isFinSet→ : isFinSet (X .fst → Y .fst)
  isFinSet→ = isFinSetΠ X (λ _ → Y)

  isFinSet≃ : isFinSet (X .fst ≃ Y .fst)
  isFinSet≃ = isFinSetΣ (_ , isFinSet→) (λ f → _ , isFinSetIsEquiv X Y f)

module _
  (X : FinSet ℓ) where

  isFinSet¬ : isFinSet (¬ (X .fst))
  isFinSet¬ = isFinSet→ X (⊥ , ∣ 0 , uninhabEquiv (λ x → x) ¬Fin0 ∣)

module _
  (X : FinSet ℓ) where

  isFinSetNonEmpty : isFinSet (NonEmpty (X .fst))
  isFinSetNonEmpty = isFinSet¬ (_ , isFinSet¬ X)

module _
  (X : FinSet ℓ )
  (Y : FinSet ℓ')
  (f : X .fst → Y .fst) where

  isFinSetIsEmbedding : isFinSet (isEmbedding f)
  isFinSetIsEmbedding =
    isFinSetΠ2 X (λ _ → X)
      (λ a b → _ , isFinSetIsEquiv (_ , isFinSet≡ X a b) (_ , isFinSet≡ Y (f a) (f b)) (cong f))

  isFinSetIsSurjection : isFinSet (isSurjection f)
  isFinSetIsSurjection =
    isFinSetΠ Y (λ y → _ , isFinSet∥∥ (_ , isFinSetFiber X Y f y))

module _
  (X : FinSet ℓ )
  (Y : FinSet ℓ') where

  isFinSet↪ : isFinSet (X .fst ↪ Y .fst)
  isFinSet↪ = isFinSetΣ (_ , isFinSet→ X Y) (λ f → _ , isFinSetIsEmbedding X Y f)

  isFinSet↠ : isFinSet (X .fst ↠ Y .fst)
  isFinSet↠ = isFinSetΣ (_ , isFinSet→ X Y) (λ f → _ , isFinSetIsSurjection X Y f)