------------------------------------------------------------------------
-- Simple kinding of Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Kinding.Simple where

open import Data.Context.WellFormed
open import Data.Fin using (Fin; zero; suc)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Fin.Substitution.Typed
open import Data.List as List using ([]; _∷_; _∷ʳ_; map)
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Product as Prod using (∃; _,_; _×_)
open import Data.Vec as Vec using ([]; _∷_)
import Data.Vec.Properties as VecProps
open import Function using (_∘_)
import Level
open import Relation.Binary.PropositionalEquality as PropEq using (refl; _≡_)
open import Relation.Nullary.Negation

open import FOmegaInt.Syntax
open import FOmegaInt.Syntax.SingleVariableSubstitution
open import FOmegaInt.Syntax.HereditarySubstitution
open import FOmegaInt.Syntax.Normalization

------------------------------------------------------------------------
-- Simple kinding derivations.
--
-- TODO: explain the point of this and how "simple" kinding differs
-- from "canonical" kinding.

module Kinding where
  open SimpleCtx
  open Syntax

  infix 4 _⊢Var_∈_ _⊢_kds _⊢Nf_∈_ _⊢Ne_∈_ _⊢_∋∙_∈_ _⊢_wfs

  -- Simple kinding of variables.
  data _⊢Var_∈_ {n} (Γ : Ctx n) : Fin n → SKind → Set where
    ∈-var : ∀ {k} x → lookup Γ x ≡ kd k → Γ ⊢Var x ∈ k

  mutual

    -- Simplified well-formedness of kinds: a variant of
    -- well-formedness for η-long β-normal kinds based on simple
    -- kinding.
    data _⊢_kds {n} (Γ : Ctx n) : Kind Elim n → Set where
      kds-⋯ : ∀ {a b} → Γ ⊢Nf a ∈ ★ → Γ ⊢Nf b ∈ ★           → Γ ⊢ a ⋯ b kds
      kds-Π : ∀ {j k} → Γ ⊢ j kds   → kd ⌊ j ⌋ ∷ Γ ⊢ k kds  → Γ ⊢ Π j k kds

    -- Simple kinding of η-long β-normal types.
    data _⊢Nf_∈_ {n} (Γ : Ctx n) : Elim n → SKind → Set where
      ∈-⊥-f : Γ ⊢Nf ⊥∙ ∈ ★
      ∈-⊤-f : Γ ⊢Nf ⊤∙ ∈ ★
      ∈-∀-f : ∀ {k a} → Γ ⊢ k kds → kd ⌊ k ⌋ ∷ Γ ⊢Nf a ∈ ★ →
              Γ ⊢Nf ∀∙ k a ∈ ★
      ∈-→-f : ∀ {a b} → Γ ⊢Nf a ∈ ★ → Γ ⊢Nf b ∈ ★ → Γ ⊢Nf a ⇒∙ b ∈ ★
      ∈-Π-i : ∀ {j a k} → Γ ⊢ j kds → kd ⌊ j ⌋ ∷ Γ ⊢Nf a ∈ k →
              Γ ⊢Nf Λ∙ j a ∈ ⌊ j ⌋ ⇒ k
      ∈-ne  : ∀ {a} → Γ ⊢Ne a ∈ ★ → Γ ⊢Nf a ∈ ★

    -- Simple kinding of neutral types.
    data _⊢Ne_∈_ {n} (Γ : Ctx n) : Elim n → SKind → Set where
      ∈-∙ : ∀ {x j k} {as : Spine n} → Γ ⊢Var x ∈ j →
            Γ ⊢ j ∋∙ as ∈ k → Γ ⊢Ne var x ∙ as ∈ k

    -- Simple spine kinding.
    data _⊢_∋∙_∈_ {n} (Γ : Ctx n) : SKind → Spine n → SKind → Set where
      ∈-[] : ∀ {k} → Γ ⊢ k ∋∙ [] ∈ k
      ∈-∷  : ∀ {a as j k l} → Γ ⊢Nf a ∈ j → Γ ⊢ k ∋∙ as ∈ l →
             Γ ⊢ j ⇒ k ∋∙ a ∷ as ∈ l

  open ContextConversions using (⌊_⌋Ctx)

  -- Simple well-formedness of ascriptions
  data _⊢_wfs {n} (Γ : Ctx n) : ElimAsc n → Set where
    wfs-kd : ∀ {a} → Γ ⊢ a kds   → Γ ⊢ kd a wfs
    wfs-tp : ∀ {a} → Γ ⊢Nf a ∈ ★ → Γ ⊢ tp a wfs

  -- Simply well-formed contexts and context extensions.
  module SimplyWfCtx = ContextFormation (λ Γ a → ⌊ Γ ⌋Ctx ⊢ a wfs)
  open SimplyWfCtx public using () renaming (_wf to _ctxs; _⊢_wfExt to _⊢_exts)

open Syntax
open SimpleCtx
open Kinding
open PropEq

-- An admissible kinding rule for spine concatenation.
∈-++ : ∀ {n} {Γ : Ctx n} {as bs j k l} →
       Γ ⊢ j ∋∙ as ∈ k → Γ ⊢ k ∋∙ bs ∈ l →
       Γ ⊢ j ∋∙ as List.++ bs ∈ l
∈-++ ∈-[]               k∋as∈l = k∋as∈l
∈-++ (∈-∷ b∈j₁ j₂∋as∈k) k∋as∈l = ∈-∷ b∈j₁ (∈-++ j₂∋as∈k k∋as∈l)

-- An admissible kinding rule for appending a normal form to a spine.
∈-∷ʳ : ∀ {n} {Γ : Ctx n} {as a j k l} →
       Γ ⊢ j ∋∙ as ∈ k ⇒ l → Γ ⊢Nf a ∈ k →
       Γ ⊢ j ∋∙ as ∷ʳ a ∈ l
∈-∷ʳ j∋as∈k⇒k a∈k = ∈-++ j∋as∈k⇒k (∈-∷ a∈k ∈-[])

-- An admissible kinding rule for post-application to neutral types.
Ne∈-Π-e : ∀ {n} {Γ : Ctx n} {a b j k} →
          Γ ⊢Ne a ∈ j ⇒ k → Γ ⊢Nf b ∈ j → Γ ⊢Ne a ⌜·⌝ b ∈ k
Ne∈-Π-e (∈-∙ x∈j j∋as∈k) b∈k = ∈-∙ x∈j (∈-∷ʳ j∋as∈k b∈k)

-- An inversion lemma for _⊢_wf.
wfs-kd-inv : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ kd k wfs → Γ ⊢ k kds
wfs-kd-inv (wfs-kd k-kds) = k-kds

-- An inversion lemma for type operators.
Nf∈-⇒-inv : ∀ {n} {Γ : Ctx n} {a k₁ k₂} → Γ ⊢Nf a ∈ k₁ ⇒ k₂ →
            ∃ λ j → ∃ λ b →
              Γ ⊢ j kds × kd k₁ ∷ Γ ⊢Nf b ∈ k₂ × ⌊ j ⌋ ≡ k₁ × a ≡ Λ∙ j b
Nf∈-⇒-inv (∈-Π-i j-kds b∈k₂) = _ , _ , j-kds , b∈k₂ , refl , refl

----------------------------------------------------------------------
-- Well-kinded substitutions (i.e. substitution lemmas) in canonical
-- types

-- Well-kinded variable substitutions (renamings).
module KindedRenaming where

  open ⊤-WellFormed SimpleCtx.weakenOps

  typedVarSubst : TypedVarSubst SAsc Level.zero
  typedVarSubst = record
    { _⊢_wf              = _⊢_wf
    ; typeExtension      = SimpleCtx.weakenOps
    ; typeVarApplication = record { _/_ = λ k _ → k }
    ; /-wk               = refl
    ; id-vanishes        = λ _ → refl
    ; /-⊙                = λ _ → refl
    ; wf-wf            = λ _ → ctx-wf _
    }
  open TypedVarSubst typedVarSubst public
    hiding (∈-weaken)
    renaming (_⊢Var_∈_ to _⊢Var′_∈_; lookup to ∈-lookup)

  open Substitution hiding (subst; _/Var_) renaming (_Elim/Var_ to _/Var_)
  open RenamingCommutes using (Kind[∈⌊⌋]-/Var)
  open PropEq
  open ≡-Reasoning

  -- A helper.
  ∈-↑′ : ∀ {m n} {Δ : Ctx n} {Γ : Ctx m} {k ρ} →
         Δ ⊢/Var ρ ∈ Γ →
         kd ⌊ k Kind′/Var ρ ⌋ ∷ Δ ⊢/Var ρ VarSubst.↑ ∈ kd ⌊ k ⌋ ∷ Γ
  ∈-↑′ {Δ = Δ} {_} {k} ρ∈Γ =
    subst (λ j → kd j ∷ Δ ⊢/Var _ ∈ _) (sym (⌊⌋-Kind′/Var k)) (∈-↑ _ ρ∈Γ)

  -- Convert between well-kindedness judgments for variables.
  Var∈′-Var∈ : ∀ {n} {Γ : Ctx n} {x a k} → a ≡ kd k →
               Γ ⊢Var′ x ∈ a → Γ ⊢Var x ∈ k
  Var∈′-Var∈ Γ[x]≡kd-k (∈-var x Γ-ctx) = ∈-var x Γ[x]≡kd-k

  -- Renamings preserve synthesized kinds of variables.
  Var∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {x k ρ} →
              Γ ⊢Var x ∈ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Var Vec.lookup ρ x ∈ k
  Var∈-/Var (∈-var x Γ[x]≡kd-k) ρ∈Γ = Var∈′-Var∈ Γ[x]≡kd-k (∈-lookup ρ∈Γ x)

  mutual

    -- Renamings preserve well-formedness of kinds.
    kds-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {k ρ} →
               Γ ⊢ k kds → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ k Kind′/Var ρ kds
    kds-/Var (kds-⋯ a∈★ b∈★) ρ∈Γ = kds-⋯ (Nf∈-/Var a∈★ ρ∈Γ) (Nf∈-/Var b∈★ ρ∈Γ)
    kds-/Var (kds-Π j-kds  k-kds) ρ∈Γ =
      kds-Π (kds-/Var j-kds ρ∈Γ) (kds-/Var k-kds (∈-↑′ ρ∈Γ))

    -- Renamings preserve synthesized kinds of normal types.
    Nf∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k ρ} →
               Γ ⊢Nf a ∈ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Nf a /Var ρ ∈ k
    Nf∈-/Var ∈-⊥-f              ρ∈Γ = ∈-⊥-f
    Nf∈-/Var ∈-⊤-f              ρ∈Γ = ∈-⊤-f
    Nf∈-/Var (∈-∀-f k-kds a∈★)  ρ∈Γ =
      ∈-∀-f (kds-/Var k-kds ρ∈Γ) (Nf∈-/Var a∈★ (∈-↑′ ρ∈Γ))
    Nf∈-/Var (∈-→-f a∈★ b∈★)    ρ∈Γ =
      ∈-→-f (Nf∈-/Var a∈★ ρ∈Γ) (Nf∈-/Var b∈★ ρ∈Γ)
    Nf∈-/Var (∈-Π-i {j} {a} {k} j-kds a∈k) ρ∈Γ =
      subst (λ l → _ ⊢Nf Λ∙ j a /Var _ ∈ l ⇒ k) (⌊⌋-Kind′/Var j)
            (∈-Π-i (kds-/Var j-kds ρ∈Γ) (Nf∈-/Var a∈k (∈-↑′ ρ∈Γ)))
    Nf∈-/Var (∈-ne a∈★)         ρ∈Γ = ∈-ne (Ne∈-/Var a∈★ ρ∈Γ)

    -- Renamings preserve synthesized kinds of neutral types.
    Ne∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k ρ} →
               Γ ⊢Ne a ∈ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Ne a /Var ρ ∈ k
    Ne∈-/Var (∈-∙ x∈j k∈as∈l) ρ∈Γ =
      ∈-∙ (Var∈-/Var x∈j ρ∈Γ) (Sp∈-/Var k∈as∈l ρ∈Γ)

    Sp∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {as j k ρ} →
               Γ ⊢ j ∋∙ as ∈ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ j ∋∙ as //Var ρ  ∈ k
    Sp∈-/Var ∈-[]                ρ∈Γ = ∈-[]
    Sp∈-/Var (∈-∷ a∈j k[a]∈as∈l) ρ∈Γ =
      ∈-∷ (Nf∈-/Var a∈j ρ∈Γ) (Sp∈-/Var k[a]∈as∈l ρ∈Γ)

  -- Renamings preserve well-formedness of ascriptions.
  wfs-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a ρ} →
             Γ ⊢ a wfs → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ a ElimAsc/Var ρ wfs
  wfs-/Var (wfs-kd k-kds) ρ∈Γ = wfs-kd (kds-/Var k-kds ρ∈Γ)
  wfs-/Var (wfs-tp a∈★)   ρ∈Γ = wfs-tp (Nf∈-/Var a∈★ ρ∈Γ)

  -- Weakening preserves simple well-formedness of kinds.

  kds-weaken : ∀ {n} {Γ : Ctx n} {k a} →
               Γ ⊢ k kds → a ∷ Γ ⊢ weakenKind′ k kds
  kds-weaken k-kds = kds-/Var k-kds (∈-wk _)

  kds-weaken⋆ : ∀ {m n} (Δ : CtxExt m n) {Γ : Ctx m} {k} →
                Γ ⊢ k kds → Δ ++ Γ ⊢ weakenKind′⋆ n k kds
  kds-weaken⋆ []       k-kds = k-kds
  kds-weaken⋆ (_ ∷ Δ′) k-kds = kds-weaken (kds-weaken⋆ Δ′ k-kds)

  -- Weakening preserves kinding of normal forms.

  Nf∈-weaken : ∀ {n} {Γ : Ctx n} {a b k} →
               Γ ⊢Nf b ∈ k → (a ∷ Γ) ⊢Nf weakenElim b ∈ k
  Nf∈-weaken b∈k = Nf∈-/Var b∈k (∈-wk _)

  Nf∈-weaken⋆ : ∀ {m n} (Δ : CtxExt m n) {Γ : Ctx m} {a k} →
                Γ ⊢Nf a ∈ k → Δ ++ Γ ⊢Nf weakenElim⋆ n a ∈ k
  Nf∈-weaken⋆ []       a∈k = a∈k
  Nf∈-weaken⋆ (_ ∷ Δ′) a∈k = Nf∈-weaken (Nf∈-weaken⋆ Δ′ a∈k)

  -- Weakening preserves kinding of neutral forms.
  Ne∈-weaken : ∀ {n} {Γ : Ctx n} {a b k} →
               Γ ⊢Ne b ∈ k → (a ∷ Γ) ⊢Ne weakenElim b ∈ k
  Ne∈-weaken b∈k = Ne∈-/Var b∈k (∈-wk _)

  -- Weakening preserves spine kinding.
  Sp∈-weaken : ∀ {n} {Γ : Ctx n} {a bs j k} →
               Γ ⊢ j ∋∙ bs ∈ k → (a ∷ Γ) ⊢ j ∋∙ weakenSpine bs ∈ k
  Sp∈-weaken j∋bs∈k = Sp∈-/Var j∋bs∈k (∈-wk _)

  -- Weakening preserves simple well-formedness of ascriptions.
  wfs-weaken : ∀ {n} {Γ : Ctx n} {a b} →
               Γ ⊢ b wfs → a ∷ Γ ⊢ weakenElimAsc b wfs
  wfs-weaken b-wfs = wfs-/Var b-wfs (∈-wk _)

-- Operations on simply well-formed contexts that require weakening of
-- well-formedness judgments.
module WfsCtxOps where
  open SimplyWfCtx
  open ContextConversions using (⌊_⌋Ctx)
  open KindedRenaming using (wfs-weaken)

  wfsWeakenOps : WellFormedWeakenOps ElimCtx.weakenOps
  wfsWeakenOps = record { wf-weaken = λ _ → wfs-weaken }

  private module W = WellFormedWeakenOps wfsWeakenOps
  open W public hiding (wf-weaken)

  -- Lookup the kind of a type variable in a well-formed context.
  lookup-kd : ∀ {m} {Γ : ElimCtx.Ctx m} {a} x →
              Γ ctxs → ElimCtx.lookup Γ x ≡ kd a → ⌊ Γ ⌋Ctx ⊢ a kds
  lookup-kd x Γ-ctxs Γ[x]≡kd-a =
    wfs-kd-inv (subst (_ ⊢_wfs) Γ[x]≡kd-a (W.lookup Γ-ctxs x))

module KindedHereditarySubstitution where
  open Data.Fin         using (zero; raise; lift)
  open Substitution     hiding (subst; sub; _↑; _↑⋆_)
  open KindedRenaming   using (_⊢/Var_∈_; Nf∈-/Var; Nf∈-weaken⋆; ∈-wk)
  open RenamingCommutes using (wk-/⟨⟩-↑⋆; /Var-wk-↑⋆-hsub-vanishes)
  open PropEq
  open ≡-Reasoning
  private module V = VarSubst

  infix 4 _⊢/⟨_⟩_∈_ _⊢?⟨_⟩_∈_

  -- Simply well-kinded hereditary substitions and lookup results.

  data _⊢/⟨_⟩_∈_ : ∀ {m n} → Ctx n → SKind → SVSub m n → Ctx m → Set where
    ∈-hsub : ∀ {n} {Γ : Ctx n} {k a} →
             Γ ⊢Nf a ∈ k → Γ ⊢/⟨ k ⟩ sub a ∈ kd k ∷ Γ
    ∈-H↑   : ∀ {m n Δ k Γ} {σ : SVSub m n} {a} →
             Δ ⊢/⟨ k ⟩ σ ∈ Γ → a ∷ Δ ⊢/⟨ k ⟩ σ ↑ ∈ a ∷ Γ

  data _⊢?⟨_⟩_∈_ {n} (Γ : Ctx n) (k : SKind) : SVRes n → SAsc n → Set where
    ∈-hit  : ∀ {a}   → Γ ⊢Nf a ∈ k    → Γ ⊢?⟨ k ⟩ hit a ∈ kd k
    ∈-miss : ∀ y {a} → lookup Γ y ≡ a → Γ ⊢?⟨ k ⟩ miss y ∈ a

  -- A variant of `∈-H↑' that applies `σ' in the target context

  ∈-H↑′ : ∀ {m n Δ k Γ} {σ : SVSub m n} {j} →
          Δ ⊢/⟨ k ⟩ σ ∈ Γ →
          kd ⌊ j Kind/⟨ k ⟩ σ ⌋ ∷ Δ ⊢/⟨ k ⟩ σ ↑ ∈ kd ⌊ j ⌋ ∷ Γ
  ∈-H↑′ σ∈Γ =
    subst (λ k → kd k ∷ _ ⊢/⟨ _ ⟩ _ ↑ ∈ kd _ ∷ _)
          (sym (⌊⌋-Kind/⟨⟩ _)) (∈-H↑ σ∈Γ)

  -- Lift a kinded hereditary substitution over multiple additional
  -- variables.

  ∈-H↑⋆ : ∀ {m n i} (E : CtxExt m i) {Δ k Γ} {σ : SVSub m n} →
          Δ ⊢/⟨ k ⟩ σ ∈ Γ → re-idx E ++ Δ ⊢/⟨ k ⟩ σ ↑⋆ i ∈ E ++ Γ
  ∈-H↑⋆ []      σ∈Γ = σ∈Γ
  ∈-H↑⋆ (a ∷ E) σ∈Γ = ∈-H↑ (∈-H↑⋆ E σ∈Γ)

  -- Renamings preserve well-kinded SV results.

  ?∈-/Var : ∀ {m n Γ k Δ r a} {ρ : Sub Fin m n} →
            Γ ⊢?⟨ k ⟩ r ∈ a → Δ ⊢/Var ρ ∈ Γ → Δ ⊢?⟨ k ⟩ r ?/Var ρ ∈ a
  ?∈-/Var                 (∈-hit a∈k)     ρ∈Γ = ∈-hit (Nf∈-/Var a∈k ρ∈Γ)
  ?∈-/Var {Γ = Γ} {k} {Δ} (∈-miss y refl) ρ∈Γ = helper refl (TV.lookup ρ∈Γ y)
    where
      module TV = TypedVarSubst KindedRenaming.typedVarSubst

      helper : ∀ {x a} → a ≡ lookup Γ y → Δ TV.⊢Var x ∈ a →
               Δ ⊢?⟨ k ⟩ miss x ∈ lookup Γ y
      helper Δ[x]≡Γ[y] (TV.∈-var x _) = ∈-miss x Δ[x]≡Γ[y]

  ?∈-weaken : ∀ {n} {Γ : Ctx n} {k r a b} →
              Γ ⊢?⟨ k ⟩ r ∈ a → (b ∷ Γ) ⊢?⟨ k ⟩ weakenSVRes r ∈ a
  ?∈-weaken r∈a = ?∈-/Var r∈a (∈-wk _)

  -- Look up a variable in a well-kinded hereditary substitution.

  lookup-/⟨⟩∈ : ∀ {m n Δ k Γ} {σ : SVSub m n} →
                Δ ⊢/⟨ k ⟩ σ ∈ Γ → (x : Fin m) →
                Δ ⊢?⟨ k ⟩ lookupSV σ x ∈ lookup Γ x
  lookup-/⟨⟩∈                (∈-hsub a∈k) zero    = ∈-hit a∈k
  lookup-/⟨⟩∈ {Γ = kd k ∷ Γ} (∈-hsub a∈k) (suc x) =
    ∈-miss x (cong (λ Γ → Vec.lookup Γ x) (sym (VecProps.map-id (toVec Γ))))
  lookup-/⟨⟩∈ (∈-H↑                     σ∈Γ) zero    = ∈-miss zero refl
  lookup-/⟨⟩∈ (∈-H↑ {Δ = Δ} {_} {Γ} {σ} σ∈Γ) (suc x) =
    ?∈-weaken (subst (λ as → Δ ⊢?⟨ _ ⟩ lookupSV σ x ∈ Vec.lookup as x)
                     (sym (VecProps.map-id (toVec Γ))) (lookup-/⟨⟩∈ σ∈Γ x))

  -- Lookup a hit in a well-kinded hereditary substitution.
  --
  -- NOTE. Since `j ≡ k', we could have formulated this lemma
  -- differently.  In particular, given the premise `Γ ⊢Var x ∈ j', it
  -- would have been more intuitive for the kinding judgment in the
  -- conclusion to use the shape `j' rather than `k' (preserving the
  -- kind of the variable, as usual for substitution lemmas).
  -- However, it is important that the shape `k' of the normal type
  -- returned by this function is syntactically equal to the shape
  -- annotation of the hereditary substitution `Δ ⊢/⟨ k ⟩ σ ∈ Γ'
  -- because it serves as the termination measure for the (hereditary)
  -- substitution lemmas below.

  lookup-/⟨⟩∈-Hit : ∀ {m n Δ k} {σ : SVSub m n} {Γ x j a} →
                    Δ ⊢/⟨ k ⟩ σ ∈ Γ → Γ ⊢Var x ∈ j → Hit σ x a →
                    Δ ⊢Nf a ∈ k × j ≡ k
  lookup-/⟨⟩∈-Hit σ∈Γ (∈-var {j} x Γ[x]≡kd-j) hitP =
    let a∈k , Γ[x]≡kd-k = helper (lookup-Hit hitP) (lookup-/⟨⟩∈ σ∈Γ x)
    in  a∈k , kd-inj′ (trans (sym Γ[x]≡kd-j) Γ[x]≡kd-k)
    where
      helper : ∀ {n} {Δ : Ctx n} {r a b k} →
               r ≡ hit a → Δ ⊢?⟨ k ⟩ r ∈ b → Δ ⊢Nf a ∈ k × b ≡ kd k
      helper refl (∈-hit a∈k) = a∈k , refl

  lookup-/⟨⟩∈-Miss : ∀ {m n Δ k} {σ : SVSub m n} {Γ x y j} →
                     Δ ⊢/⟨ k ⟩ σ ∈ Γ → Γ ⊢Var x ∈ j → Miss σ x y →
                     lookup Δ y ≡ lookup Γ x
  lookup-/⟨⟩∈-Miss σ∈Γ (∈-var {j} x Γ[x]≡kd-j) missP =
    helper (lookup-Miss missP) (lookup-/⟨⟩∈ σ∈Γ x)
    where
      helper : ∀ {n} {Δ : Ctx n} {r y b k} →
               r ≡ miss y → Δ ⊢?⟨ k ⟩ r ∈ b → lookup Δ y ≡ b
      helper refl (∈-miss y Δ[y]≡b) = Δ[y]≡b

  open KindSimpLemmas simpLemmasKind′

  -- TODO: explain why this terminates.
  mutual

    -- Hereditary substitutions preserve simple well-formedness and
    -- kinding (of kinds, normal types and spines).

    kds-/⟨⟩ : ∀ {m n Γ k Δ} {σ : SVSub m n} {j} →
              Γ ⊢ j kds → Δ ⊢/⟨ k ⟩ σ ∈ Γ → Δ ⊢ j Kind/⟨ k ⟩ σ kds
    kds-/⟨⟩ (kds-⋯ a∈★ b∈★)     σ∈Γ = kds-⋯ (Nf∈-/⟨⟩ a∈★ σ∈Γ) (Nf∈-/⟨⟩ b∈★ σ∈Γ)
    kds-/⟨⟩ (kds-Π j-kds k-kds) σ∈Γ =
      kds-Π (kds-/⟨⟩ j-kds σ∈Γ) (kds-/⟨⟩ k-kds (∈-H↑′ σ∈Γ))

    Nf∈-/⟨⟩ : ∀ {m n Γ k Δ} {σ : SVSub m n} {a j} →
              Γ ⊢Nf a ∈ j → Δ ⊢/⟨ k ⟩ σ ∈ Γ → Δ ⊢Nf a /⟨ k ⟩ σ ∈ j
    Nf∈-/⟨⟩ ∈-⊥-f             σ∈Γ = ∈-⊥-f
    Nf∈-/⟨⟩ ∈-⊤-f             σ∈Γ = ∈-⊤-f
    Nf∈-/⟨⟩ (∈-∀-f k-kds a∈★) σ∈Γ =
      ∈-∀-f (kds-/⟨⟩ k-kds σ∈Γ) (Nf∈-/⟨⟩ a∈★ (∈-H↑′ σ∈Γ))
    Nf∈-/⟨⟩ (∈-→-f a∈★ b∈★)   σ∈Γ =
      ∈-→-f (Nf∈-/⟨⟩ a∈★ σ∈Γ) (Nf∈-/⟨⟩ b∈★ σ∈Γ)
    Nf∈-/⟨⟩ (∈-Π-i {j} {a} {k} j-kds a∈k) σ∈Γ =
      subst (λ l → _ ⊢Nf Λ∙ j a /⟨ _ ⟩ _ ∈ l ⇒ k) (⌊⌋-Kind/⟨⟩ j)
            (∈-Π-i (kds-/⟨⟩ j-kds σ∈Γ) (Nf∈-/⟨⟩ a∈k (∈-H↑′ σ∈Γ)))
    Nf∈-/⟨⟩ (∈-ne a∈★)        σ∈Γ = Ne∈-/⟨⟩ a∈★ σ∈Γ

    Sp∈-/⟨⟩ : ∀ {m n Γ k Δ} {σ : SVSub m n} {as j₁ j₂} →
              Γ ⊢ j₁ ∋∙ as ∈ j₂ → Δ ⊢/⟨ k ⟩ σ ∈ Γ → Δ ⊢ j₁ ∋∙ as //⟨ k ⟩ σ ∈ j₂
    Sp∈-/⟨⟩ ∈-[]                   σ∈Γ = ∈-[]
    Sp∈-/⟨⟩ (∈-∷ a∈j₁ j₂[a]∈as∈j₃) σ∈Γ =
      ∈-∷ (Nf∈-/⟨⟩ a∈j₁ σ∈Γ) (Sp∈-/⟨⟩ j₂[a]∈as∈j₃ σ∈Γ)

    -- Hereditary substitutions preserve the shapes of neutral types
    -- (but not neutrality itself).

    Ne∈-/⟨⟩ : ∀ {m n Γ k Δ} {σ : SVSub m n} {a} →
              Γ ⊢Ne a ∈ ★ → Δ ⊢/⟨ k ⟩ σ ∈ Γ → Δ ⊢Nf a /⟨ k ⟩ σ ∈ ★
    Ne∈-/⟨⟩ (∈-∙ (∈-var x Γ[x]≡kd-j) j∈as∈l) σ∈Γ =
      ?⟨⟩∈-?∙∙ (subst (_ ⊢?⟨ _ ⟩ _ ∈_) Γ[x]≡kd-j (lookup-/⟨⟩∈ σ∈Γ x))
               (Sp∈-/⟨⟩ j∈as∈l σ∈Γ)

    -- Applications in simple kinding are admissible.

    ?⟨⟩∈-?∙∙ : ∀ {n} {Γ : Ctx n} {r as k j} →
               Γ ⊢?⟨ k ⟩ r ∈ kd j → Γ ⊢ j ∋∙ as ∈ ★ → Γ ⊢Nf r ?∙∙⟨ k ⟩ as ∈ ★
    ?⟨⟩∈-?∙∙ (∈-hit a∈j)          j∋as∈★ = Nf∈-∙∙ a∈j j∋as∈★
    ?⟨⟩∈-?∙∙ (∈-miss y Γ[y]≡kd-j) j∋as∈★ = ∈-ne (∈-∙ (∈-var y Γ[y]≡kd-j) j∋as∈★)

    Nf∈-∙∙ : ∀ {n} {Γ : Ctx n} {a as j k} →
             Γ ⊢Nf a ∈ j → Γ ⊢ j ∋∙ as ∈ k → Γ ⊢Nf a ∙∙⟨ j ⟩ as ∈ k
    Nf∈-∙∙ a∈j   ∈-[]             = a∈j
    Nf∈-∙∙ a∈j⇒k (∈-∷ b∈j k∋as∈l) = Nf∈-∙∙ (Nf∈-Π-e a∈j⇒k b∈j) k∋as∈l

    Nf∈-Π-e : ∀ {n} {Γ : Ctx n} {a b j k} →
              Γ ⊢Nf a ∈ j ⇒ k → Γ ⊢Nf b ∈ j → Γ ⊢Nf a ⌜·⌝⟨ j ⇒ k ⟩ b ∈ k
    Nf∈-Π-e (∈-Π-i j-kds a∈k) b∈⌊j⌋ = Nf∈-/⟨⟩ a∈k (∈-hsub b∈⌊j⌋)

  -- Concatenation of simply well-formed spines results in application.
  Nf∈-++-∙∙⟨⟩ : ∀ {n} {Γ : Ctx n} {a bs cs j k l} →
                Γ ⊢Nf a ∈ j → Γ ⊢ j ∋∙ bs ∈ k → Γ ⊢ k ∋∙ cs ∈ l →
                a ∙∙⟨ j ⟩ (bs List.++ cs) ≡ a ∙∙⟨ j ⟩ bs ∙∙⟨ k ⟩ cs
  Nf∈-++-∙∙⟨⟩ a∈j     ∈-[]                  k∋cs∈l = refl
  Nf∈-++-∙∙⟨⟩ a∈j₁⇒j₂ (∈-∷ b∈j₁ j₂[b]∋bs∈k) k∋cs∈l =
    Nf∈-++-∙∙⟨⟩ (Nf∈-Π-e a∈j₁⇒j₂ b∈j₁) j₂[b]∋bs∈k k∋cs∈l

  -- Another admissible kinding rule for applications.
  Nf∈-Π-e′ : ∀ {n} {Γ : Ctx n} {a b j k} →
             Γ ⊢Nf a ∈ j ⇒ k → Γ ⊢Nf b ∈ j → Γ ⊢Nf a ↓⌜·⌝ b ∈ k
  Nf∈-Π-e′ (∈-Π-i j-kds a∈k) b∈⌊j⌋ = Nf∈-/⟨⟩ a∈k (∈-hsub b∈⌊j⌋)

  mutual

    -- Simply well-kinded hereditary substitutions in simply
    -- well-formed kinds and simply well-kinded types commute.
    --
    -- NOTE: this is a variant of the `sub-commutes' lemma from
    -- Data.Fin.Substitution.Lemmas adapted to hereditary
    -- substitutions.

    kds-[]-/⟨⟩-↑⋆ : ∀ {i m n} (E : CtxExt (suc m) i) {Γ Δ}
                    {j b k l} {σ : SVSub m n} →
                    E ++ kd k ∷ Γ ⊢ j kds → Γ ⊢Nf b ∈ k → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                    j Kind/⟨ k ⟩ (sub b ↑⋆ i) Kind/⟨ l ⟩ σ ↑⋆ i ≡
                      j Kind/⟨ l ⟩ (σ ↑) ↑⋆ i Kind/⟨ k ⟩ sub (b /⟨ l ⟩ σ) ↑⋆ i
    kds-[]-/⟨⟩-↑⋆ E (kds-⋯ a∈★ b∈★)     c∈k σ∈Γ =
      cong₂ _⋯_ (Nf∈-[]-/⟨⟩-↑⋆ E a∈★ c∈k σ∈Γ) (Nf∈-[]-/⟨⟩-↑⋆ E b∈★ c∈k σ∈Γ)
    kds-[]-/⟨⟩-↑⋆ E (kds-Π j-kds k-kds) b∈k σ∈Γ =
      cong₂ Π (kds-[]-/⟨⟩-↑⋆ E j-kds b∈k σ∈Γ)
              (kds-[]-/⟨⟩-↑⋆ (_ ∷ E) k-kds b∈k σ∈Γ)

    Nf∈-[]-/⟨⟩-↑⋆ : ∀ {i m n} (E : CtxExt (suc m) i) {Γ Δ}
                    {a b j k l} {σ : SVSub m n} →
                    E ++ kd k ∷ Γ ⊢Nf a ∈ j → Γ ⊢Nf b ∈ k → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                    a /⟨ k ⟩ (sub b ↑⋆ i) /⟨ l ⟩ σ ↑⋆ i ≡
                      a /⟨ l ⟩ (σ ↑) ↑⋆ i /⟨ k ⟩ sub (b /⟨ l ⟩ σ) ↑⋆ i
    Nf∈-[]-/⟨⟩-↑⋆ E ∈-⊥-f b∈k σ∈Γ = refl
    Nf∈-[]-/⟨⟩-↑⋆ E ∈-⊤-f b∈k σ∈Γ = refl
    Nf∈-[]-/⟨⟩-↑⋆ E (∈-∀-f k-kds a∈★) b∈k σ∈Γ =
      cong (_∙ []) (cong₂ Π (kds-[]-/⟨⟩-↑⋆ E k-kds b∈k σ∈Γ)
                            (Nf∈-[]-/⟨⟩-↑⋆ (_ ∷ E) a∈★ b∈k σ∈Γ))
    Nf∈-[]-/⟨⟩-↑⋆ E (∈-→-f a∈★ b∈★)   c∈k σ∈Γ =
      cong (_∙ []) (cong₂ _⇒_ (Nf∈-[]-/⟨⟩-↑⋆ E a∈★ c∈k σ∈Γ)
                              (Nf∈-[]-/⟨⟩-↑⋆ E b∈★ c∈k σ∈Γ))
    Nf∈-[]-/⟨⟩-↑⋆ E (∈-Π-i j-kds a∈l) b∈k σ∈Γ =
      cong (_∙ []) (cong₂ Λ (kds-[]-/⟨⟩-↑⋆ E j-kds b∈k σ∈Γ)
                            (Nf∈-[]-/⟨⟩-↑⋆ (_ ∷ E) a∈l b∈k σ∈Γ))
    Nf∈-[]-/⟨⟩-↑⋆ E (∈-ne a∈★)        b∈k σ∈Γ = Ne∈-[]-/⟨⟩-↑⋆ E a∈★ b∈k σ∈Γ

    Ne∈-[]-/⟨⟩-↑⋆ : ∀ {i m n} (E : CtxExt (suc m) i) {Γ Δ}
                    {a b j k l} {σ : SVSub m n} →
                    E ++ kd k ∷ Γ ⊢Ne a ∈ j → Γ ⊢Nf b ∈ k → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                    a /⟨ k ⟩ (sub b ↑⋆ i) /⟨ l ⟩ σ ↑⋆ i ≡
                      a /⟨ l ⟩ (σ ↑) ↑⋆ i /⟨ k ⟩ sub (b /⟨ l ⟩ σ) ↑⋆ i
    Ne∈-[]-/⟨⟩-↑⋆ {i} E {b = b} (∈-∙ (∈-var x _) j∋as∈l) b∈k σ∈Γ
      with hit? (sub b ↑⋆ i) x
    Ne∈-[]-/⟨⟩-↑⋆ {i} E {_} {_} {var x ∙ as} {b} {_} {k} {l} {σ}
                  (∈-∙ (∈-var x Γ[x]=kd-j) j∋as∈l) b∈k σ∈Γ
      | hit a hitP =
      let σ₁′ = sub b ↑⋆ i
          σ₂′ = sub (b /⟨ l ⟩ σ) ↑⋆ i
          as₁ = as //⟨ k ⟩ σ₁′
          as₂ = as //⟨ l ⟩ ((σ ↑) ↑⋆ i)
          [b/i]∈E++k∷Γ = ∈-H↑⋆ E (∈-hsub b∈k)
          _ , j≡k   = lookup-/⟨⟩∈-Hit [b/i]∈E++k∷Γ (∈-var x Γ[x]=kd-j) hitP
          k∋as∈l    = subst (_ ⊢_∋∙ as ∈ _) j≡k j∋as∈l
          σ↑⋆i∈E++Γ = ∈-H↑⋆ (re-idx E) σ∈Γ
      in begin
        lookupSV σ₁′ x ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨ cong (λ r → r ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ _)
              (trans (lookup-Hit hitP) (cong hit (Hit-sub-↑⋆₂ i hitP))) ⟩
        weakenElim⋆ i b ∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨ Nf∈-∙∙-/⟨⟩ (Nf∈-weaken⋆ (re-idx E) b∈k)
                    (Sp∈-/⟨⟩ k∋as∈l [b/i]∈E++k∷Γ) σ↑⋆i∈E++Γ ⟩
        (weakenElim⋆ i b /⟨ l ⟩ σ ↑⋆ i) ∙∙⟨ k ⟩ (as₁ //⟨ l ⟩ σ ↑⋆ i)
      ≡⟨ cong₂ (_∙∙⟨ k ⟩_) (sym (weaken⋆-/⟨⟩-↑⋆ i b))
               (Sp∈-[]-/⟨⟩-↑⋆ E j∋as∈l b∈k σ∈Γ) ⟩
        weakenElim⋆ i (b /⟨ l ⟩ σ) ∙∙⟨ k ⟩ (as₂ //⟨ k ⟩ σ₂′)
      ≡˘⟨ cong (_?∙∙⟨ k ⟩ (as₂ //⟨ k ⟩ σ₂′)) (lookup-Hit (Hit-sub-↑⋆ i)) ⟩
        lookupSV σ₂′ (raise i zero) ?∙∙⟨ k ⟩ (as₂ //⟨ k ⟩ σ₂′)
      ≡⟨⟩
        miss (raise i zero) ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ r → r ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′)
               (lookup-Miss (Miss-↑⋆ i under)) ⟩
        lookupSV ((σ ↑) ↑⋆ i) (raise i zero) ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ y → lookupSV ((σ ↑) ↑⋆ i) y ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′)
               (Hit-sub-↑⋆₁ i hitP) ⟩
        lookupSV ((σ ↑) ↑⋆ i) x ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ∎
    Ne∈-[]-/⟨⟩-↑⋆ {i} E {_} {_} {var x ∙ as} {b} {_} {k} {l} {σ}
                  (∈-∙ (∈-var x Γ[x]=kd-j) j∋as∈l) b∈k σ∈Γ
      | miss y missP-[a/i] with hit? (σ ↑⋆ i) y
    ... | hit a hitP =
      let σ₁′ = sub b ↑⋆ i
          σ₂′ = sub (b /⟨ l ⟩ σ) ↑⋆ i
          as₁ = as //⟨ k ⟩ σ₁′
          as₂ = as //⟨ l ⟩ ((σ ↑) ↑⋆ i)
          hitP-σ↑↑⋆ = Hit-↑-↑⋆ i hitP
          σ₂′∈E++Γ  = ∈-H↑⋆ (re-idx E) (∈-hsub (Nf∈-/⟨⟩ b∈k σ∈Γ))
          σ∈E++k∷Γ  = ∈-H↑⋆ E (∈-H↑ σ∈Γ)
          x≡i+1+y   = Miss-sub-↑⋆′ i y missP-[a/i]
          i+1+y∈j   = subst (_ ⊢Var_∈ _) x≡i+1+y (∈-var x Γ[x]=kd-j)
          a∈l , j≡l = lookup-/⟨⟩∈-Hit σ∈E++k∷Γ i+1+y∈j hitP-σ↑↑⋆
          l∋as₂∈l   = subst (_ ⊢_∋∙ as₂ ∈ _) j≡l (Sp∈-/⟨⟩ j∋as∈l σ∈E++k∷Γ)
      in begin
        lookupSV σ₁′ x ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨ cong (λ r → r ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i) (lookup-Miss missP-[a/i]) ⟩
        var y ∙ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨⟩
        lookupSV (σ ↑⋆ i) y ?∙∙⟨ l ⟩ (as₁ //⟨ l ⟩ σ ↑⋆ i)
      ≡⟨ cong₂ (_?∙∙⟨ l ⟩_) (lookup-Hit hitP) (Sp∈-[]-/⟨⟩-↑⋆ E j∋as∈l b∈k σ∈Γ) ⟩
        a ∙∙⟨ l ⟩ (as₂ //⟨ k ⟩ σ₂′)
      ≡˘⟨ cong (_∙∙⟨ l ⟩ (as₂ //⟨ k ⟩ σ₂′)) (/Var-wk-↑⋆-hsub-vanishes i a) ⟩
        (a Elim/Var V.wk V.↑⋆ i /⟨ k ⟩ σ₂′) ∙∙⟨ l ⟩ (as₂ //⟨ k ⟩ σ₂′)
      ≡˘⟨ Nf∈-∙∙-/⟨⟩ a∈l l∋as₂∈l σ₂′∈E++Γ ⟩
        (a Elim/Var V.wk V.↑⋆ i) ∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ r → r ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′) (lookup-Hit hitP-σ↑↑⋆) ⟩
        lookupSV ((σ ↑) ↑⋆ i) (lift i suc y) ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ z → lookupSV ((σ ↑) ↑⋆ i) z ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′) x≡i+1+y ⟩
        lookupSV ((σ ↑) ↑⋆ i) x ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ∎
    ... | miss z missP =
      let σ₁′ = sub b ↑⋆ i
          σ₂′ = sub (b /⟨ l ⟩ σ) ↑⋆ i
          as₁ = as //⟨ k ⟩ σ₁′
          as₂ = as //⟨ l ⟩ ((σ ↑) ↑⋆ i)
          missP-σ↑↑⋆ = Miss-↑-↑⋆ i missP
          x≡i+1+y    = Miss-sub-↑⋆′ i y missP-[a/i]
      in begin
        lookupSV σ₁′ x ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨ cong (λ r → r ?∙∙⟨ k ⟩ as₁ /⟨ l ⟩ σ ↑⋆ i) (lookup-Miss missP-[a/i]) ⟩
        var y ∙ as₁ /⟨ l ⟩ σ ↑⋆ i
      ≡⟨⟩
        lookupSV (σ ↑⋆ i) y ?∙∙⟨ l ⟩ (as₁ //⟨ l ⟩ σ ↑⋆ i)
      ≡⟨ cong₂ (_?∙∙⟨ l ⟩_) (lookup-Miss missP)
               (Sp∈-[]-/⟨⟩-↑⋆ E j∋as∈l b∈k σ∈Γ) ⟩
        var z ∙ (as₂ //⟨ k ⟩ σ₂′)
      ≡˘⟨ cong (_?∙∙⟨ k ⟩ (as₂ //⟨ k ⟩ σ₂′)) (lookup-Miss (Miss-sub-↑⋆ i z)) ⟩
        lookupSV σ₂′ (lift i suc z) ?∙∙⟨ k ⟩ (as₂ //⟨ k ⟩ σ₂′)
      ≡⟨⟩
        var (lift i suc z) ∙ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ r → r ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′) (lookup-Miss missP-σ↑↑⋆) ⟩
        lookupSV ((σ ↑) ↑⋆ i) (lift i suc y) ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ≡˘⟨ cong (λ z → lookupSV ((σ ↑) ↑⋆ i) z ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′) x≡i+1+y ⟩
        lookupSV ((σ ↑) ↑⋆ i) x ?∙∙⟨ l ⟩ as₂ /⟨ k ⟩ σ₂′
      ∎

    Sp∈-[]-/⟨⟩-↑⋆ : ∀ {i m n} (E : CtxExt (suc m) i) {Γ Δ}
                    {as b j₁ j₂ k l} {σ : SVSub m n} →
                    E ++ kd k ∷ Γ ⊢ j₁ ∋∙ as ∈ j₂ →
                    Γ ⊢Nf b ∈ k → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                    as //⟨ k ⟩ (sub b ↑⋆ i) //⟨ l ⟩ σ ↑⋆ i ≡
                      as //⟨ l ⟩ (σ ↑) ↑⋆ i //⟨ k ⟩ sub (b /⟨ l ⟩ σ) ↑⋆ i
    Sp∈-[]-/⟨⟩-↑⋆ E ∈-[]                b∈k σ∈Γ = refl
    Sp∈-[]-/⟨⟩-↑⋆ E (∈-∷ a∈j₁ j₂∋as∈j₃) b∈k σ∈Γ =
      cong₂ _∷_ (Nf∈-[]-/⟨⟩-↑⋆ E a∈j₁ b∈k σ∈Γ) (Sp∈-[]-/⟨⟩-↑⋆ E j₂∋as∈j₃ b∈k σ∈Γ)

    -- Reducing applications commute with hereditary substitution.

    Nf∈-∙∙-/⟨⟩ : ∀ {l m n Γ Δ} {σ : SVSub m n} {a as j k} →
                 Γ ⊢Nf a ∈ j → Γ ⊢ j ∋∙ as ∈ k → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                 a ∙∙⟨ j ⟩ as /⟨ l ⟩ σ ≡ (a /⟨ l ⟩ σ) ∙∙⟨ j ⟩ (as //⟨ l ⟩ σ)
    Nf∈-∙∙-/⟨⟩ a∈j ∈-[] σ∈Γ = refl
    Nf∈-∙∙-/⟨⟩ {l} {σ = σ} {a} {b ∷ bs} {j ⇒ k} a∈j⇒k (∈-∷ b∈j k∋bs∈l) σ∈Γ =
      begin
        a ⌜·⌝⟨ j ⇒ k ⟩ b ∙∙⟨ k ⟩ bs /⟨ l ⟩ σ
      ≡⟨ Nf∈-∙∙-/⟨⟩ (Nf∈-Π-e a∈j⇒k b∈j) k∋bs∈l σ∈Γ ⟩
        (a ⌜·⌝⟨ j ⇒ k ⟩ b /⟨ l ⟩ σ) ∙∙⟨ _ ⟩ (bs //⟨ l ⟩ σ)
      ≡⟨ cong (_∙∙⟨ k ⟩ (bs //⟨ l ⟩ σ)) (Nf∈-Π-e-/⟨⟩ a∈j⇒k b∈j σ∈Γ) ⟩
        (a /⟨ l ⟩ σ) ⌜·⌝⟨ j ⇒ k ⟩ (b /⟨ l ⟩ σ) ∙∙⟨ k ⟩ (bs //⟨ l ⟩ σ)
      ∎

    Nf∈-Π-e-/⟨⟩ : ∀ {l m n Γ Δ} {σ : SVSub m n} {a b j k} →
                  Γ ⊢Nf a ∈ j ⇒ k → Γ ⊢Nf b ∈ j → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                  a ⌜·⌝⟨ j ⇒ k ⟩ b /⟨ l ⟩ σ ≡
                    (a /⟨ l ⟩ σ) ⌜·⌝⟨ j ⇒ k ⟩ (b /⟨ l ⟩ σ)
    Nf∈-Π-e-/⟨⟩ (∈-Π-i j-kds a∈k) b∈⌊j⌋ σ∈Γ = Nf∈-[]-/⟨⟩-↑⋆ [] a∈k b∈⌊j⌋ σ∈Γ

  -- Potentially reducing applications commute with hereditary
  -- substitution.

  Nf∈-Π-e-/⟨⟩′ : ∀ {l m n Γ Δ} {σ : SVSub m n} {a b j k} →
                Γ ⊢Nf a ∈ j ⇒ k → Γ ⊢Nf b ∈ j → Δ ⊢/⟨ l ⟩ σ ∈ Γ →
                a ↓⌜·⌝ b /⟨ l ⟩ σ ≡ (a /⟨ l ⟩ σ) ↓⌜·⌝ (b /⟨ l ⟩ σ)
  Nf∈-Π-e-/⟨⟩′ {l} {σ = σ} {_} {b} (∈-Π-i {j} {a} j-kds a∈k) b∈⌊j⌋ σ∈Γ =
    begin
      (a [ b ∈ ⌊ j ⌋ ]) /⟨ l ⟩ σ
    ≡⟨ Nf∈-[]-/⟨⟩-↑⋆ [] a∈k b∈⌊j⌋ σ∈Γ ⟩
      (a /⟨ l ⟩ σ ↑) [ b /⟨ l ⟩ σ ∈ ⌊ j ⌋ ]
    ≡⟨ cong ((a /⟨ l ⟩ σ ↑) [ b /⟨ l ⟩ σ ∈_]) (sym (⌊⌋-Kind/⟨⟩ j)) ⟩
      (a /⟨ l ⟩ σ ↑) [ b /⟨ l ⟩ σ ∈ ⌊ j Kind/⟨ l ⟩ σ ⌋ ]
    ∎


module Haskell.RangedSets.RangedSet where
open import Agda.Builtin.Equality

open import Haskell.Prim
open import Haskell.Prim.Ord
open import Haskell.Prim.Bool
open import Haskell.Prim.Maybe
open import Haskell.Prim.Enum
open import Haskell.Prim.Num
open import Haskell.Prim.Eq
open import Haskell.Prim.Foldable
open import Haskell.Prim.Monoid
open import Haskell.Prim.Int
open import Haskell.Prim.List
open import Haskell.Prim.Integer
open import Haskell.Prim.Real

open import Haskell.RangedSets.Boundaries
open import Haskell.RangedSets.Ranges
open import Haskell.RangedSetsProp.library
open import Haskell.RangedSetsProp.BoundariesProperties

{-# FOREIGN AGDA2HS
import Haskell.RangedSets.Boundaries
#-}
{-# FOREIGN AGDA2HS
import Haskell.RangedSets.Ranges
#-}
{-# FOREIGN AGDA2HS
import Data.List
#-}

infixl 7 _-/\-_
infixl 6 _-\/-_ _-!-_
infixl 5 _-<=-_ _-<-_ _-?-_

okAdjacent : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → Range a → Range a → Bool
okAdjacent r@(Rg lower1 upper1) r2@(Rg lower2 upper2) = rangeLower r <= rangeUpper r && rangeUpper r <= rangeLower r2 && rangeLower r2 <= rangeUpper r2
{-# COMPILE AGDA2HS okAdjacent #-}

validRangeList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Range a) → Bool
validRangeList [] = true
validRangeList (x ∷ []) = (rangeLower x) <= (rangeUpper x)
validRangeList (x ∷ rs@(r1 ∷ rss)) = (okAdjacent x r1) && (validRangeList rs)
{-# COMPILE AGDA2HS validRangeList #-}

validBoundaryList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Boundary a) → Bool
validBoundaryList [] = true
validBoundaryList (x ∷ []) = true
validBoundaryList (x ∷ rs@(r1 ∷ rss)) = (x <= r1) && (validBoundaryList rs)

data RSet (a : Set) ⦃ o : Ord a ⦄ ⦃ dio : DiscreteOrdered a ⦄ : Set where
    RS : (rg : List (Range a)) → {IsTrue (validRangeList rg)} → RSet a

{-# COMPILE AGDA2HS RSet #-}

rSetRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → RSet a → List (Range a)
rSetRanges (RS ranges) = ranges
{-# COMPILE AGDA2HS rSetRanges #-}

overlap1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → Range a → Range a → Bool
overlap1 (Rg _ upper1) (Rg lower2 _) = (upper1 >= lower2)
{-# COMPILE AGDA2HS overlap1 #-}

sortedRangeList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Range a) → Bool
sortedRangeList [] = true
sortedRangeList (r ∷ []) = true
sortedRangeList (r1 ∷ r2 ∷ rs) = (r1 <= r2) && (sortedRangeList (r2 ∷ rs))

validRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Range a) → Bool
validRanges [] = true
validRanges (r ∷ rs) = (rangeLower r <= rangeUpper r) && (validRanges rs)

headandtailSorted : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      → ⦃ ne : NonEmpty rs ⦄ → (IsTrue (sortedRangeList rs))
      → (IsTrue (sortedRangeList (tail rs ⦃ ne ⦄)))
headandtailValidRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      → ⦃ ne : NonEmpty rs ⦄ → (IsTrue (validRanges rs))
      → (IsTrue (validRanges (tail rs ⦃ ne ⦄)))

normalise : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rg : List (Range a))
   → ⦃ IsTrue (sortedRangeList rg) ⦄
   → ⦃ IsTrue (validRanges rg) ⦄
   → List (Range a)

postulate
   sortedList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      -> IsTrue (sortedRangeList (sort (filter (λ r → (rangeIsEmpty r) == false) rs)))

   validRangesList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      -> IsTrue (validRanges (sort (filter (λ r → (rangeIsEmpty r) == false) rs)))

unsafeRangedSet : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                → (rg : List (Range a))
                → ⦃ IsTrue (validRangeList ⦃ o ⦄ ⦃ dio ⦄ rg) ⦄ → RSet a
unsafeRangedSet rs ⦃ prf ⦄  = RS rs {prf}

{-# COMPILE AGDA2HS unsafeRangedSet #-}

validFunction2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a) → (f : Boundary a → Boundary a) → Bool
validFunction2 b f = (f b) > b

validFunction : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a) → (g : Boundary a → Maybe (Boundary a)) → Bool
validFunction b g with (g b)
...                       | Nothing = true
...                       | Just b2 = (b2 > b)

ranges3 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ →
      Maybe (Boundary a) → (f : Boundary a → Boundary a) → (g : Boundary a → Maybe (Boundary a))
      → List (Range a)
{-# TERMINATING #-}
ranges2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
      → (f : Boundary a → Boundary a) → (g : Boundary a → Maybe (Boundary a))
      → List (Range a)
ranges2 b upperFunc succFunc = (Rg b (upperFunc b)) ∷ (ranges3 (succFunc b) upperFunc succFunc)

ranges3 (Just b1) upperFunc succFunc = if ((validFunction2 b1 upperFunc) && (validFunction b1 succFunc)) then
                              (ranges2 b1 upperFunc succFunc) else []
ranges3 Nothing _ _ = []
{-# COMPILE AGDA2HS ranges2 #-}
{-# COMPILE AGDA2HS ranges3 #-}

setBounds1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (xs : List (Boundary a))
   → List (Boundary a)
setBounds1 (BoundaryBelowAll ∷ xs) = xs
setBounds1 xs = (BoundaryBelowAll ∷ xs)
{-# COMPILE AGDA2HS setBounds1 #-}

bounds1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
         → List (Range a) → List (Boundary a)
bounds1 (r ∷ rs) = (rangeLower r) ∷ (rangeUpper r) ∷ (bounds1 rs)
bounds1 [] = []
{-# COMPILE AGDA2HS bounds1 #-}

ranges1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Boundary a)
   → List (Range a)
ranges1 (b1 ∷ b2 ∷ bs) = (Rg b1 b2) ∷ (ranges1 bs)
ranges1 (BoundaryAboveAll ∷ [])  = []
ranges1 (b ∷ []) = (Rg b BoundaryAboveAll) ∷ []
ranges1 _ = []
{-# COMPILE AGDA2HS ranges1 #-}

merge1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Range a) → List (Range a) → List (Range a)
merge1 [] [] = []
merge1 ms1@(h1 ∷ t1) [] = ms1
merge1 [] ms2@(h2 ∷ t2) = ms2
merge1 ms1@(h1 ∷ t1) ms2@(h2 ∷ t2) = if_then_else_ (h1 < h2) (h1 ∷ (merge1 t1 ms2)) (h2 ∷ (merge1 ms1 t2))
{-# COMPILE AGDA2HS merge1 #-}

merge2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → List (Range a) → List (Range a) → List (Range a)
merge2 [] [] = []
merge2 ms1@(h1 ∷ t1) [] = []
merge2 [] ms2@(h2 ∷ t2) = []
merge2 ms1@(h1 ∷ t1) ms2@(h2 ∷ t2) =
   (rangeIntersection h1 h2) ∷
   (if_then_else_ (rangeUpper h1 < rangeUpper h2) (merge2 t1 ms2) (merge2 ms1 t2))
{-# COMPILE AGDA2HS merge2 #-}

postulate

   sortedListComposed : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
            → (r1 r2 : Range a) → (ranges : List (Range a))
            → IsTrue (sortedRangeList (r1 ∷ r2 ∷ ranges))
            → IsTrue (sortedRangeList ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ ranges))

   validRangesComposed : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
            → (r1 r2 : Range a) → (ranges : List (Range a))
            → IsTrue (sortedRangeList (r1 ∷ r2 ∷ ranges))
            → IsTrue (validRanges (r1 ∷ r2 ∷ ranges))
            → IsTrue (validRanges ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ ranges))

   rangesLTEQ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b : Boundary a)
                          → (f : Boundary a → Boundary a) → IsTrue (f b > b) → IsTrue (b <= f b)

   stupid1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b b1 : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → (cond : Bool) -- this condition is (validFunction2 b1 f) && (validFunction b1 g)
                          → IsTrue cond
                          → IsTrue (validFunction2 b1 f)
   stupid2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b b1 : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → (cond : Bool) -- this condition is (validFunction2 b1 f) && (validFunction b1 g)
                          → IsTrue cond
                          → IsTrue (validFunction b1 g)

   -- used only when h <= h3
   okSorted : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (h h3 : Range a)
         → sortedRangeList (h ∷ h3 ∷ []) ≡ true

   -- holds only when upper h <= lower(head ms)
   validList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (h : Range a) → (ms : List (Range a))
         → IsTrue (validRangeList ms)
         → validRangeList (h ∷ ms) ≡ true

   validIsSorted :  ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (ms : List (Range a))
                     → IsTrue (validRangeList ms) → IsTrue (sortedRangeList ms)

   -- holds only when h < (head ms)
   validSortedList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (h : Range a) → (ms : List (Range a))
         → IsTrue (sortedRangeList ms)
         → sortedRangeList (h ∷ ms) ≡ true

normalise (r1 ∷ r2 ∷ rs) ⦃ prf ⦄ ⦃ prf2 ⦄ =
   if_then_else_ (overlap1 r1 r2)
      (normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs)
      ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄ )
         (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄ ))
normalise rs = rs
{-# COMPILE AGDA2HS normalise #-}

-- some useful proofs
headandtail : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
      → ⦃ ne : NonEmpty (rSetRanges rs) ⦄ → (IsTrue (validRangeList (rSetRanges rs)))
      → (IsTrue (validRangeList (tail (rSetRanges rs) ⦃ ne ⦄)))
okAdjValid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a)) → (r : Range a)
      → ⦃ ne : NonEmpty rs ⦄ → (IsTrue (validRangeList (r ∷ rs))) → IsTrue (okAdjacent r (head rs ⦃ ne ⦄))
headandtail2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      → ⦃ ne : NonEmpty rs ⦄ → (IsTrue (validRangeList rs))
      → (IsTrue (validRangeList (tail rs ⦃ ne ⦄)))
tailandhead : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
      → ⦃ ne : NonEmpty (rSetRanges rs) ⦄ → (IsTrue (validRangeList (rSetRanges rs)))
      → (IsTrue (validRangeList ((head (rSetRanges rs) ⦃ ne ⦄) ∷ [])))
tailandhead2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : List (Range a))
      → ⦃ ne : NonEmpty rs ⦄ → (IsTrue (validRangeList rs))
      → (IsTrue (validRangeList ((head rs ⦃ ne ⦄) ∷ [])))

okAdjIsTrue :  ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (r : Range a) → (r2 : Range a)
               → okAdjacent r r2 ≡ ((rangeLower r <= rangeUpper r) && (rangeUpper r <= rangeLower r2) && (rangeLower r2 <= rangeUpper r2))

normalisedSortedList0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
         → (ms : List (Range a))
         → (prf : IsTrue (sortedRangeList ms))
         → (prf2 : IsTrue (validRanges ms))
         → validRangeList (normalise ms ⦃ prf ⦄ ⦃ prf2 ⦄) ≡ true

normalisedSortedList00 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
         → (rs : List (Range a))
         → (r1 r2 : Range a) → (prf : IsTrue (sortedRangeList (r1 ∷ r2 ∷ rs))) → (prf2 : IsTrue (validRanges (r1 ∷ r2 ∷ rs)))
         → (b : Bool)
         → validRangeList (if_then_else_ b
      (normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄)
         (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄))) ≡ true

normalisedSortedList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
  → (ms : List (Range a))
  → (prf : IsTrue (sortedRangeList ms))
  → (prf2 : IsTrue (validRanges ms))
  → IsTrue (validRangeList (normalise ms ⦃ prf ⦄ ⦃ prf2 ⦄))

merge1HasValidRanges0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (rs1 rs2 : RSet a)
                          → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
                          → (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2))) ≡ true

merge1HasValidRanges00 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (rs1 rs2 : RSet a) → (h1 h2 : Range a)
                          → IsTrue (validRangeList (h1 ∷ (rSetRanges rs1))) → IsTrue (validRangeList (h2 ∷ (rSetRanges rs2)))
                          → (b : Bool)
                          → validRanges (if_then_else_ b
                          (h1 ∷ (merge1 (rSetRanges rs1) (h2 ∷ (rSetRanges rs2))))
                          (h2 ∷ (merge1 (h1 ∷ (rSetRanges rs1)) (rSetRanges rs2)))) ≡ true

validListMeansValidRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (rs1 : RSet a)
                          → IsTrue (validRangeList (rSetRanges rs1))
                          → (validRanges (rSetRanges rs1)) ≡ true

merge1HasValidRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → (rs1 rs2 : RSet a)
   → IsTrue (validRangeList (rSetRanges rs1))
   → IsTrue (validRangeList (rSetRanges rs2))
   → IsTrue (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2)))

ranges2HasValidRanges0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → (validRanges (ranges2 b f g)) ≡ true
ranges2HasValidRanges00 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → (mb : Maybe (Boundary a))
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → (validRanges ((Rg b (f b)) ∷ (ranges3 mb f g))) ≡ true
ranges2HasValidRanges000 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b b1 : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → (cond : Bool)
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → (validRanges ((Rg b (f b)) ∷ (if cond then (ranges2 b1 f g) else []))) ≡ true

ranges2HasValidRanges : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
                          → (b : Boundary a)
                          → (f : Boundary a → Boundary a)
                          → (g : Boundary a → Maybe (Boundary a))
                          → IsTrue (validFunction2 b f) → IsTrue (validFunction b g)
                          → IsTrue (validRanges (ranges2 b f g))

unfoldSorted : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
            → (f : Boundary a → Boundary a)
            → (g : Boundary a → Maybe (Boundary a))
            → IsTrue (sortedRangeList (ranges2 b f g))

unfoldIsSorted00 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
            → (f : Boundary a → Boundary a)
            → (g : Boundary a → Maybe (Boundary a))
            → (b1 : Boundary a) → (cond : Bool)
            → sortedRangeList ((Rg b (f b)) ∷ (if_then_else_ cond (ranges2 b1 f g) [])) ≡ true

unfoldIsSorted0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
            → (f : Boundary a → Boundary a)
            → (g : Boundary a → Maybe (Boundary a))
            → (mb : Maybe (Boundary a)) → sortedRangeList ((Rg b (f b)) ∷ (ranges3 mb f g)) ≡ true

unfoldIsSorted : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
            → (f : Boundary a → Boundary a)
            → (g : Boundary a → Maybe (Boundary a))
            → sortedRangeList (ranges2 b f g) ≡ true

-- the following proofs are used for proving that intersection is valid
intersection2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
                  → ⦃ ne1 : NonEmpty (rSetRanges rs1) ⦄ → ⦃ ne2 : NonEmpty (rSetRanges rs2) ⦄
                  → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
                  → (b : Bool)
                  →     validRangeList (
       if_then_else_ b

      ((rangeIntersection (head (rSetRanges rs1) ⦃ ne1 ⦄) (head (rSetRanges rs2) ⦃ ne2 ⦄))
      ∷ (filter (λ x → (rangeIsEmpty x == false))
       (if_then_else_ (rangeUpper (head (rSetRanges rs1) ⦃ ne1 ⦄) < rangeUpper (head (rSetRanges rs2) ⦃ ne2 ⦄))
        (merge2 (tail (rSetRanges rs1) ⦃ ne1 ⦄) (rSetRanges rs2))
        (merge2 (rSetRanges rs1) (tail (rSetRanges rs2) ⦃ ne2 ⦄)))))

       (filter (λ x → (rangeIsEmpty x == false))
         (if_then_else_ (rangeUpper (head (rSetRanges rs1) ⦃ ne1 ⦄) < rangeUpper (head (rSetRanges rs2) ⦃ ne2 ⦄))
          (merge2 (tail (rSetRanges rs1) ⦃ ne1 ⦄) (rSetRanges rs2))
          (merge2 (rSetRanges rs1) (tail (rSetRanges rs2) ⦃ ne2 ⦄)))) ) ≡ true
intersection3 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
                  → ⦃ ne1 : NonEmpty (rSetRanges rs1) ⦄ → ⦃ ne2 : NonEmpty (rSetRanges rs2) ⦄
                  → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
                  → (b : Bool)
                  → validRangeList ((filter (λ x → (rangeIsEmpty x == false))
                        (if_then_else_ b
                           (merge2 (tail (rSetRanges rs1) ⦃ ne1 ⦄) (rSetRanges rs2))
                           (merge2 (rSetRanges rs1) (tail (rSetRanges rs2) ⦃ ne2 ⦄))))) ≡ true

intersectionHolds : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
                  → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
                  → validRangeList (filter (λ x → (rangeIsEmpty x == false)) (merge2 (rSetRanges rs1) (rSetRanges rs2))) ≡ true

-- intersection of two valid ranged sets is also valid range set
intersection0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
      → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
      → IsTrue (validRangeList (filter (λ x → (rangeIsEmpty x == false))
                  (merge2 (rSetRanges rs1) (rSetRanges rs2))))
intersection0 ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 = subst IsTrue (sym (intersectionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2)) IsTrue.itsTrue

-- used for proving that union is valid (merge1 returns a valid range list)
merge1IsSorted0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (h1 h2 : Range a) → (t1 t2 : RSet a) → (b : Bool)
         → IsTrue (validRangeList (h1 ∷ (rSetRanges t1))) → IsTrue (validRangeList (h2 ∷ (rSetRanges t2)))
         → sortedRangeList (if_then_else_ b (h1 ∷ (merge1 (rSetRanges t1) (h2 ∷ (rSetRanges t2))))
            (h2 ∷ (merge1 (h1 ∷ (rSetRanges t1)) (rSetRanges t2)))) ≡ true
merge1IsSorted1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
         → IsTrue (validRangeList (rSetRanges rs1)) → IsTrue (validRangeList (rSetRanges rs2))
         → sortedRangeList (merge1 (rSetRanges rs1) (rSetRanges rs2)) ≡ true

merge1Sorted : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → (rs1 : RSet a) → (rs2 : RSet a)
   → IsTrue (validRangeList (rSetRanges rs1))
   → IsTrue (validRangeList (rSetRanges rs2))
   → IsTrue (sortedRangeList (merge1 (rSetRanges rs1) (rSetRanges rs2)))

merge1Sorted ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 = subst IsTrue (sym (merge1IsSorted1 rs1 rs2 prf1 prf2)) IsTrue.itsTrue

-- union of two valid ranged sets is also valid range set
unionHolds : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
         → (rs1 : RSet a) → (rs2 : RSet a)
         → (prf1 : IsTrue (validRangeList (rSetRanges rs1)))
         → (prf2 : IsTrue (validRangeList (rSetRanges rs2)))
         → IsTrue (validRangeList (normalise (merge1 (rSetRanges rs1) (rSetRanges rs2))
          ⦃ merge1Sorted rs1 rs2 prf1 prf2 ⦄ ⦃ merge1HasValidRanges rs1 rs2 prf1 prf2 ⦄))
unionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 = normalisedSortedList
   (merge1 (rSetRanges rs1) (rSetRanges rs2)) (merge1Sorted rs1 rs2 prf1 prf2)
      (merge1HasValidRanges rs1 rs2 prf1 prf2)

-- used for proving negation is valid
{-# TERMINATING #-}
valid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
   → {IsTrue (validRangeList (rSetRanges rs))}
   → (validRangeList (rSetRanges rs)) ≡ (validBoundaryList (bounds1 (rSetRanges rs)))

validSetBounds : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → (bs : List (Boundary a)) → validBoundaryList (setBounds1 bs) ≡ validBoundaryList bs

validRanges1 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (bs : List (Boundary a))
   → validRangeList (ranges1 bs) ≡ validBoundaryList bs

-- negation of valid ranged set is also valid
prop3 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
   → IsTrue (validRangeList (rSetRanges rs))
   → validRangeList (rSetRanges rs) ≡ validBoundaryList (setBounds1 (bounds1 (rSetRanges rs)))
prop3 ⦃ o ⦄ ⦃ dio ⦄ rs prf = trans (valid rs {prf}) (sym (validSetBounds (bounds1 (rSetRanges rs))))

prop4 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
   → IsTrue (validRangeList (rSetRanges rs))
   → validRangeList (rSetRanges rs) ≡ validRangeList (ranges1 (setBounds1 (bounds1 (rSetRanges rs))))

prop4 ⦃ o ⦄ ⦃ dio ⦄ rs prf = trans (prop3 rs prf) (sym (validRanges1 (setBounds1 (bounds1 (rSetRanges rs)))))

negation : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
   → (IsTrue (validRangeList (rSetRanges rs)))
   → (IsTrue (validRangeList (ranges1 (setBounds1 (bounds1 (rSetRanges rs))))))
negation ⦃ o ⦄ ⦃ dio ⦄ rs prf = subst IsTrue (prop4 rs prf) prf

-- empty range is valid
emptyRangeValid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (validRangeList ⦃ o ⦄ ⦃ dio ⦄ []) ≡ true
emptyRangeValid = refl
empty : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → IsTrue (validRangeList ⦃ o ⦄ ⦃ dio ⦄ [])
empty ⦃ o ⦄ ⦃ dio ⦄ = subst IsTrue (sym (emptyRangeValid ⦃ o ⦄ ⦃ dio ⦄)) IsTrue.itsTrue

-- full range is valid
fullRangeValid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ →
      (validRangeList ⦃ o ⦄ ⦃ dio ⦄ (fullRange ⦃ o ⦄ ⦃ dio ⦄ ∷ [])) ≡ true
full0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ →
      IsTrue (validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg BoundaryBelowAll BoundaryAboveAll) ∷ []))
full0 ⦃ o ⦄ ⦃ dio ⦄ = subst IsTrue (sym (fullRangeValid ⦃ o ⦄ ⦃ dio ⦄)) IsTrue.itsTrue

-- any singleton range is valid
singletonRangeValid0 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (x : a) → (validRangeList ((singletonRange x) ∷ [])) ≡ true
singletonRangeValid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (x : a) → IsTrue (validRangeList ((singletonRange x) ∷ []))
singletonRangeValid x = subst IsTrue (sym (singletonRangeValid0 x)) IsTrue.itsTrue

rSingleton : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → a → RSet a
rSingleton ⦃ o ⦄ ⦃ dio ⦄ a = RS ((singletonRange a) ∷ []) {singletonRangeValid a}
{-# COMPILE AGDA2HS rSingleton #-}

normaliseRangeList : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → List (Range a) → List (Range a)
normaliseRangeList [] = []
normaliseRangeList rs@(r1 ∷ rss) =
   normalise (sort (filter (λ r → (rangeIsEmpty r) == false) rs))
      ⦃ sortedList rs ⦄ ⦃ validRangesList rs ⦄
{-# COMPILE AGDA2HS normaliseRangeList #-}

makeRangedSet : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
   → List (Range a) → RSet a
makeRangedSet ⦃ o ⦄ ⦃ dio ⦄ [] = RS [] {empty ⦃ o ⦄ ⦃ dio ⦄}
makeRangedSet ⦃ o ⦄ ⦃ dio ⦄ rs@(r1 ∷ rss) = RS (normaliseRangeList rs)
   { normalisedSortedList (sort (filter (λ r → (rangeIsEmpty r) == false) rs)) (sortedList rs) (validRangesList rs) }
{-# COMPILE AGDA2HS makeRangedSet #-}

rangesAreEmpty : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → List (Range a) → Bool
rangesAreEmpty [] = true
rangesAreEmpty (r ∷ rs) = false
{-# COMPILE AGDA2HS rangesAreEmpty #-}

rSetIsEmpty : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a) → Bool
rSetIsEmpty ⦃ o ⦄ ⦃ dio ⦄ rset@(RS ranges) = rangesAreEmpty ⦃ o ⦄ ⦃ dio ⦄ ranges
{-# COMPILE AGDA2HS rSetIsEmpty #-}

rSetNegation : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rg : RSet a) → {(IsTrue (validRangeList (rSetRanges rg)))} → RSet a
rSetNegation ⦃ o ⦄ ⦃ dio ⦄ set@(RS ranges) {prf} =
   RS (ranges1 (setBounds1 (bounds1 ranges))) {negation set prf}

{-# COMPILE AGDA2HS rSetNegation #-}

rSetIsFull : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
      → {IsTrue (validRangeList (rSetRanges rs))} → Bool
rSetIsFull set {prf} = rSetIsEmpty (rSetNegation set {prf})
{-# COMPILE AGDA2HS rSetIsFull #-}

rSetEmpty : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → RSet a
rSetEmpty ⦃ o ⦄ ⦃ dio ⦄ = RS [] {empty ⦃ o ⦄ ⦃ dio ⦄}
{-# COMPILE AGDA2HS rSetEmpty #-}

rSetFull : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → RSet a
rSetFull ⦃ o ⦄ ⦃ dio ⦄ = RS ((Rg BoundaryBelowAll BoundaryAboveAll) ∷ []) {full0 ⦃ o ⦄ ⦃ dio ⦄}
{-# COMPILE AGDA2HS rSetFull #-}

rSetHas : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a) → {IsTrue (validRangeList (rSetRanges rs))} → a → Bool
rSetHas (RS []) _ = false
rSetHas ⦃ o ⦄ ⦃ dio ⦄ (RS ls@(r ∷ [])) value = rangeHas ⦃ o ⦄ r value
rSetHas ⦃ o ⦄ ⦃ dio ⦄ rst@(RS ls@(r ∷ rs)) {prf} value = (rangeHas ⦃ o ⦄ r value) || (rSetHas (RS rs {headandtail rst prf}) {headandtail rst prf} value)
{-# COMPILE AGDA2HS rSetHas #-}

_-?-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a) → {IsTrue (validRangeList (rSetRanges rs))} → a → Bool
_-?-_ rs {prf} = rSetHas rs {prf}
{-# COMPILE AGDA2HS _-?-_ #-}

negationHelper : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
         → {tr0 : IsTrue (validRangeList (rSetRanges rs))}
         → validRangeList (rSetRanges (rSetNegation rs {tr0})) ≡ validRangeList (ranges1 (setBounds1 (bounds1 (rSetRanges rs))))

negation2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs : RSet a)
         → {tr0 : IsTrue (validRangeList (rSetRanges rs))}
         → (tr : IsTrue (validRangeList (ranges1 (setBounds1 (bounds1 (rSetRanges rs))))))
         → IsTrue (validRangeList (rSetRanges (rSetNegation rs {tr0})))
negation2 rs {prf0} prf = subst IsTrue (sym (negationHelper rs {prf0})) prf

rSetUnion : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
rSetUnion ⦃ o ⦄ ⦃ dio ⦄ r1@(RS ls1) {prf1} r2@(RS ls2) {prf2} =
   RS (normalise (merge1 ls1 ls2) ⦃ merge1Sorted r1 r2 prf1 prf2 ⦄
    ⦃ merge1HasValidRanges r1 r2 prf1 prf2 ⦄)
    {unionHolds r1 r2 prf1 prf2}

{-# COMPILE AGDA2HS rSetUnion #-}

_-\/-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
_-\/-_ rs1 {prf1} rs2 {prf2} = rSetUnion rs1 {prf1} rs2 {prf2}
{-# COMPILE AGDA2HS _-\/-_ #-}

unionHelper : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
         → (prf1 : IsTrue (validRangeList (rSetRanges rs1))) → (prf2 : IsTrue (validRangeList (rSetRanges rs2)))
         → validRangeList (rSetRanges (rSetUnion rs1 {prf1} rs2 {prf2}))
         ≡ validRangeList (normalise (merge1 (rSetRanges rs1) (rSetRanges rs2)) ⦃ merge1Sorted rs1 rs2 prf1 prf2 ⦄ ⦃ merge1HasValidRanges rs1 rs2 prf1 prf2 ⦄)
unionHelper ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ls1) rs2@(RS ls2) prf1 prf2 =
  begin
     validRangeList (rSetRanges (rSetUnion rs1 {prf1} rs2 {prf2}))
  =⟨⟩
     validRangeList (rSetRanges (RS (normalise (merge1 (rSetRanges rs1) (rSetRanges rs2)) ⦃ merge1Sorted rs1 rs2 prf1 prf2 ⦄  ⦃ merge1HasValidRanges rs1 rs2 prf1 prf2 ⦄) {unionHolds rs1 rs2 prf1 prf2}))
  =⟨⟩
     validRangeList (normalise (merge1 (rSetRanges rs1) (rSetRanges rs2)) ⦃ merge1Sorted rs1 rs2 prf1 prf2 ⦄  ⦃ merge1HasValidRanges rs1 rs2 prf1 prf2 ⦄)
  end

union2 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
         → (prf1 : IsTrue (validRangeList (rSetRanges rs1))) → (prf2 : IsTrue (validRangeList (rSetRanges rs2)))
         → IsTrue (validRangeList (normalise (merge1 (rSetRanges rs1) (rSetRanges rs2)) ⦃ merge1Sorted rs1 rs2 prf1 prf2 ⦄  ⦃ merge1HasValidRanges rs1 rs2 prf1 prf2 ⦄))
         → IsTrue (validRangeList (rSetRanges (rSetUnion rs1 {prf1} rs2 {prf2})))
union2 rs1 rs2 prf1 prf2 prf = subst IsTrue (sym (unionHelper rs1 rs2 prf1 prf2)) prf

rSetIntersection : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
rSetIntersection ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ls1) {prf1} rs2@(RS ls2) {prf2} =
    RS ⦃ o ⦄ ⦃ dio ⦄
      (filter (λ x → rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false) (merge2 ⦃ o ⦄ ⦃ dio ⦄ ls1 ls2))
         {intersection0 rs1 rs2 prf1 prf2}
{-# COMPILE AGDA2HS rSetIntersection #-}

_-/\-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
_-/\-_ rs1 {prf1} rs2 {prf2} = rSetIntersection rs1 {prf1} rs2 {prf2}
{-# COMPILE AGDA2HS _-/\-_ #-}

intersectHelper : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
         → (prf1 : IsTrue (validRangeList (rSetRanges rs1))) → (prf2 : IsTrue (validRangeList (rSetRanges rs2)))
         → validRangeList (rSetRanges (rSetIntersection rs1 {prf1} rs2 {prf2})) ≡ validRangeList (filter (λ r → (rangeIsEmpty r) == false) (merge2 (rSetRanges rs1) (rSetRanges rs2)))

intersection22 : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (rs1 : RSet a) → (rs2 : RSet a)
         → (prf1 : IsTrue (validRangeList (rSetRanges rs1))) → (prf2 : IsTrue (validRangeList (rSetRanges rs2)))
         → IsTrue (validRangeList (filter (λ r → (rangeIsEmpty r) == false) (merge2 (rSetRanges rs1) (rSetRanges rs2))))
         → IsTrue (validRangeList (rSetRanges (rSetIntersection rs1 {prf1} rs2 {prf2})))
intersection22 ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 prf = subst IsTrue (sym (intersectHelper rs1 rs2 prf1 prf2)) prf

rSetDifference : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
rSetDifference ⦃ o ⦄ ⦃ dio ⦄ rs1 {prf1} rs2 {prf2} =
   rSetIntersection rs1 {prf1} (rSetNegation rs2 {prf2}) {negation2 rs2 (negation rs2 prf2)}
{-# COMPILE AGDA2HS rSetDifference #-}

_-!-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → RSet a
_-!-_ rs1 {prf1} rs2 {prf2} = rSetDifference rs1 {prf1} rs2 {prf2}
{-# COMPILE AGDA2HS _-!-_ #-}

rSetIsSubset : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → Bool
rSetIsSubset rs1 {prf1} rs2 {prf2} = rSetIsEmpty (rSetDifference rs1 {prf1} rs2 {prf2})
{-# COMPILE AGDA2HS rSetIsSubset #-}

_-<=-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → Bool
_-<=-_ rs1 {prf1} rs2 {prf2}  = rSetIsSubset rs1 {prf1} rs2 {prf2}
{-# COMPILE AGDA2HS _-<=-_ #-}

rSetIsSubsetStrict : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → Bool
rSetIsSubsetStrict rs1 {prf1} rs2 {prf2} = rSetIsEmpty
   (rSetDifference rs1 {prf1} rs2 {prf2}) && not (rSetIsEmpty (rSetDifference rs2 {prf2} rs1 {prf1}))
{-# COMPILE AGDA2HS rSetIsSubsetStrict #-}

_-<-_ : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄
      → (rs1 : RSet a) → {IsTrue (validRangeList (rSetRanges rs1))}
      → (rs2 : RSet a) → {IsTrue (validRangeList (rSetRanges rs2))} → Bool
_-<-_ rs1 {prf1} rs2 {prf2} = rSetIsSubsetStrict rs1 {prf1} rs2 {prf2}
{-# COMPILE AGDA2HS _-<-_ #-}

instance
    isRangedSetSemigroup : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → Semigroup (RSet a)
    isRangedSetSemigroup ._<>_ r1@(RS l1 {prf1}) r2@(RS l2 {prf2}) = rSetUnion r1 {prf1} r2 {prf2}

    isRangedSetMonoid : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → Monoid (RSet a)
    isRangedSetMonoid .mempty = rSetEmpty

rSetUnfold : ⦃ o : Ord a ⦄ → ⦃ dio : DiscreteOrdered a ⦄ → (b : Boundary a)
            → (f : Boundary a → Boundary a)
            → (g : Boundary a → Maybe (Boundary a))
            → ⦃ IsTrue (validFunction2 b f) ⦄
            → ⦃ IsTrue (validFunction b g) ⦄ → RSet a

rSetUnfold ⦃ o ⦄ ⦃ dio ⦄ bound upperFunc succFunc ⦃ fValid ⦄ ⦃ gValid ⦄ =
   RS (normalise (ranges2 bound upperFunc succFunc)
               ⦃ unfoldSorted bound upperFunc succFunc ⦄ ⦃ ranges2HasValidRanges bound upperFunc succFunc fValid gValid ⦄)
      { normalisedSortedList (ranges2 bound upperFunc succFunc)
               (unfoldSorted bound upperFunc succFunc) (ranges2HasValidRanges bound upperFunc succFunc fValid gValid) }

{-# COMPILE AGDA2HS rSetUnfold #-}

okAdjIsTrue ⦃ o ⦄ ⦃ dio ⦄ r@(Rg l1 u1) r2@(Rg l2 u2) = refl

headandtailSorted rs@(r ∷ []) prf = IsTrue.itsTrue
headandtailSorted rs@(r ∷ rss@(r2 ∷ rsss)) prf = isTrue&&₂ {r <= r2} prf

headandtail rs@(RS (r ∷ [])) prf = IsTrue.itsTrue
headandtail rs@(RS (r ∷ rss@(r2 ∷ rsss))) prf = isTrue&&₂ {okAdjacent r r2} prf

tailandhead rs@(RS (r ∷ [])) prf = prf
tailandhead rs@(RS (r ∷ rss@(r2 ∷ rsss))) prf = isTrue&&₁ {rangeLower r <= rangeUpper r}
      (subst IsTrue (okAdjIsTrue r r2) (isTrue&&₁ {okAdjacent r r2} prf))

okAdjValid rs@(r2 ∷ rss) r prf = isTrue&&₁ {okAdjacent r r2} prf

tailandhead2 rs@(r ∷ []) prf = prf
tailandhead2 rs@(r ∷ rss@(r2 ∷ rsss)) prf = isTrue&&₁ {rangeLower r <= rangeUpper r}
      (subst IsTrue (okAdjIsTrue r r2) (isTrue&&₁ {okAdjacent r r2} prf))

headandtail2 rs@(r ∷ []) prf = IsTrue.itsTrue
headandtail2 rs@(r ∷ rss@(r2 ∷ rsss)) prf = isTrue&&₂ {okAdjacent r r2} prf

headandtailValidRanges rs@(r ∷ []) prf = IsTrue.itsTrue
headandtailValidRanges rs@(r ∷ rss@(r2 ∷ rsss)) prf = isTrue&&₂ {rangeLower r <= rangeUpper r} prf

ranges2HasValidRanges ⦃ o ⦄ ⦃ dio ⦄ b f g prf1 prf2 = subst IsTrue (sym (ranges2HasValidRanges0 b f g prf1 prf2)) IsTrue.itsTrue

ranges2HasValidRanges000 ⦃ o ⦄ ⦃ dio ⦄ b b1 f g false prf1 prf2 =
   begin
      (validRanges ⦃ o ⦄ ⦃ dio ⦄ ((Rg b (f b)) ∷ []))
  =⟨⟩
      b <= (f b) && (validRanges ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
      b <= (f b) && true
  =⟨ prop_and_true (b <= (f b)) ⟩
      b <= (f b)
  =⟨ propIsTrue (b <= (f b)) (rangesLTEQ b f prf1) ⟩
     true
   end
ranges2HasValidRanges000 ⦃ o ⦄ ⦃ dio ⦄ b b1 f g true prf1 prf2 =
   begin
      (validRanges ((Rg b (f b)) ∷ (ranges2 b1 f g)))
  =⟨⟩
      b <= (f b) && (validRanges (ranges2 b1 f g))
  =⟨ cong (b <= (f b) &&_) (ranges2HasValidRanges0 b1 f g (stupid1 b b1 f g prf1 prf2 true IsTrue.itsTrue)
         (stupid2 b b1 f g prf1 prf2 true IsTrue.itsTrue) ) ⟩
      b <= (f b) && true
  =⟨ prop_and_true (b <= (f b)) ⟩
      b <= (f b)
  =⟨ propIsTrue (b <= (f b)) (rangesLTEQ b f prf1) ⟩
     true
   end

ranges2HasValidRanges00 ⦃ o ⦄ ⦃ dio ⦄ b f g Nothing prf1 prf2 =
   begin
      (validRanges ⦃ o ⦄ ⦃ dio ⦄ ((Rg b (f b)) ∷ (ranges3 Nothing f g)))
  =⟨⟩
      validRanges ⦃ o ⦄ ⦃ dio ⦄ ((Rg b (f b)) ∷ [])
  =⟨⟩
      (b <= (f b)) && (validRanges ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
      (b <= (f b)) && true
  =⟨ prop_and_true (b <= (f b)) ⟩
      b <= (f b)
  =⟨ propIsTrue (b <= (f b)) (rangesLTEQ b f prf1) ⟩
     true
   end
ranges2HasValidRanges00 ⦃ o ⦄ ⦃ dio ⦄ b f g (Just b1) prf1 prf2 =
   begin
      (validRanges ((Rg b (f b)) ∷ (ranges3 (Just b1) f g)))
  =⟨⟩
      validRanges ((Rg b (f b)) ∷ (if ((validFunction2 b1 f) && (validFunction b1 g)) then (ranges2 b1 f g) else []))
  =⟨ ranges2HasValidRanges000 b b1 f g ((validFunction2 b1 f) && (validFunction b1 g)) prf1 prf2 ⟩
     true
   end

ranges2HasValidRanges0 ⦃ o ⦄ ⦃ dio ⦄ b f g prf1 prf2 =
   begin
      (validRanges (ranges2 b f g))
  =⟨⟩
      validRanges ((Rg b (f b)) ∷ (ranges3 (g b) f g))
  =⟨ ranges2HasValidRanges00 b f g (g b) prf1 prf2 ⟩
      true
   end

merge1HasValidRanges ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 = subst IsTrue (sym (merge1HasValidRanges0 rs1 rs2 prf1 prf2)) IsTrue.itsTrue

validListMeansValidRanges ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) prf =
   begin
      (validRanges ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
  true
 end
validListMeansValidRanges ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS (r1 ∷ [])) prf =
   begin
      (validRanges (r1 ∷ []))
  =⟨⟩
      (rangeLower r1 <= rangeUpper r1) && (validRanges ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
      (rangeLower r1 <= rangeUpper r1) && true
  =⟨ prop_and_true (rangeLower r1 <= rangeUpper r1) ⟩
   (rangeLower r1 <= rangeUpper r1)
  =⟨ propIsTrue (rangeLower r1 <= rangeUpper r1) prf ⟩
  true
 end
validListMeansValidRanges ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges@(r1 ∷ r2 ∷ r3)) prf =
   begin
      (validRanges ranges)
  =⟨⟩
      (rangeLower r1 <= rangeUpper r1) && (validRanges ⦃ o ⦄ ⦃ dio ⦄ (r2 ∷ r3))
  =⟨ cong ((rangeLower r1 <= rangeUpper r1) &&_) (validListMeansValidRanges (RS (r2 ∷ r3) {headandtail rs1 prf}) (headandtail rs1 prf)) ⟩
      (rangeLower r1 <= rangeUpper r1) && true
  =⟨ prop_and_true (rangeLower r1 <= rangeUpper r1) ⟩
   (rangeLower r1 <= rangeUpper r1)
  =⟨ propIsTrue (rangeLower r1 <= rangeUpper r1) (tailandhead rs1 prf) ⟩
  true
 end

merge1HasValidRanges00 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS t1) rs2@(RS t2) h1 h2 prf1 prf2 true =
   begin
      validRanges (h1 ∷ (merge1 (rSetRanges rs1) (h2 ∷ (rSetRanges rs2))))
  =⟨⟩
      (rangeLower h1 <= rangeUpper h1) && (validRanges (merge1 t1 (h2 ∷ t2)))
  =⟨ cong ((rangeLower h1 <= rangeUpper h1) &&_)
   (merge1HasValidRanges0 rs1 (RS (h2 ∷ t2) {prf2}) (headandtail (RS (h1 ∷ t1) {prf1}) prf1) prf2) ⟩
   (rangeLower h1 <= rangeUpper h1) && true
  =⟨ prop_and_true (rangeLower h1 <= rangeUpper h1) ⟩
   (rangeLower h1 <= rangeUpper h1)
  =⟨ propIsTrue (rangeLower h1 <= rangeUpper h1) (tailandhead (RS (h1 ∷ t1) {prf1}) prf1) ⟩
   true
 end
merge1HasValidRanges00 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS t1) rs2@(RS t2) h1 h2 prf1 prf2 false =
   begin
      validRanges (h2 ∷ (merge1 (h1 ∷ t1) t2))
  =⟨⟩
      (rangeLower h2 <= rangeUpper h2) && (validRanges (merge1 (h1 ∷ t1) t2))
  =⟨ cong ((rangeLower h2 <= rangeUpper h2) &&_)
   (merge1HasValidRanges0 (RS (h1 ∷ t1) {prf1}) rs2 prf1 (headandtail (RS (h2 ∷ t2) {prf2}) prf2)) ⟩
   (rangeLower h2 <= rangeUpper h2) && true
  =⟨ prop_and_true (rangeLower h2 <= rangeUpper h2) ⟩
   (rangeLower h2 <= rangeUpper h2)
  =⟨ propIsTrue (rangeLower h2 <= rangeUpper h2) (tailandhead (RS (h2 ∷ t2) {prf2}) prf2) ⟩
   true
 end

merge1HasValidRanges0 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS []) prf1 prf2 =
   begin
      (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2)))
  =⟨⟩
      validRanges ⦃ o ⦄ ⦃ dio ⦄ []
  =⟨⟩
  true
 end
merge1HasValidRanges0 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS (h1 ∷ t1)) rs2@(RS []) prf1 prf2 =
   begin
      (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2)))
  =⟨⟩
      validRanges (h1 ∷ t1)
  =⟨ validListMeansValidRanges rs1 prf1 ⟩
  true
 end
merge1HasValidRanges0 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS (h2 ∷ t2)) prf1 prf2 =
   begin
      (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2)))
  =⟨⟩
      validRanges (h2 ∷ t2)
  =⟨ validListMeansValidRanges rs2 prf2 ⟩
  true
 end
merge1HasValidRanges0 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS (h1 ∷ t1)) rs2@(RS (h2 ∷ t2)) prf1 prf2 =
   begin
      (validRanges (merge1 (rSetRanges rs1) (rSetRanges rs2)))
  =⟨⟩
      validRanges (if_then_else_ (h1 < h2) (h1 ∷ (merge1 t1 (h2 ∷ t2))) (h2 ∷ (merge1 (h1 ∷ t1) (t2))))
  =⟨ merge1HasValidRanges00 (RS t1 {headandtail rs1 prf1}) (RS t2 {headandtail rs2 prf2}) h1 h2 prf1 prf2 (h1 < h2) ⟩
  true
 end

normalisedSortedList ⦃ o ⦄ ⦃ dio ⦄ ms prf prf2 = subst IsTrue (sym (normalisedSortedList0 ms prf prf2)) IsTrue.itsTrue

normalisedSortedList00 ⦃ o ⦄ ⦃ dio ⦄ rs r1 r2 prf prf2 true =
   begin
      validRangeList (if_then_else_ true
      (normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄)
         (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄)))
  =⟨⟩
      validRangeList ((normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄) )
  =⟨ normalisedSortedList0 ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) (sortedListComposed r1 r2 rs prf) (validRangesComposed r1 r2 rs prf prf2) ⟩
  true
 end
normalisedSortedList00 ⦃ o ⦄ ⦃ dio ⦄ rs r1 r2 prf prf2 false =
   begin
      validRangeList (if_then_else_ false
      (normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄)
         (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄)))
  =⟨⟩
      validRangeList (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄))
  =⟨ validList r1 (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄)
  (normalisedSortedList (r2 ∷ rs) (headandtailSorted (r1 ∷ r2 ∷ rs) prf) (headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2))  ⟩
  true
 end

normalisedSortedList0 ⦃ o ⦄ ⦃ dio ⦄ ms@([]) prf prf2 =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (normalise [] ⦃ prf ⦄ ⦃ prf2 ⦄)
  =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
  =⟨⟩
      true
   end
normalisedSortedList0 ⦃ o ⦄ ⦃ dio ⦄ ms@(m ∷ []) prf prf2 =
   begin
      validRangeList (normalise ms ⦃ prf ⦄ ⦃ prf2 ⦄)
  =⟨⟩
      validRangeList (m ∷ [])
  =⟨⟩
      (rangeLower m <= rangeUpper m)
  =⟨ sym (prop_and_true (rangeLower m <= rangeUpper m))  ⟩
      (rangeLower m <= rangeUpper m) && true
  =⟨⟩
      (rangeLower m <= rangeUpper m) && (validRanges ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
      validRanges ms
  =⟨ propIsTrue (validRanges ms) prf2 ⟩
      true
  end
normalisedSortedList0 ⦃ o ⦄ ⦃ dio ⦄ ms@(r1 ∷ r2 ∷ rs) prf prf2 =
   begin
      validRangeList (normalise ms ⦃ prf ⦄ ⦃ prf2 ⦄)
  =⟨⟩
      validRangeList (if_then_else_ (overlap1 r1 r2)
      (normalise ((Rg (rangeLower r1) (max (rangeUpper r1) (rangeUpper r2))) ∷ rs) ⦃ sortedListComposed r1 r2 rs prf ⦄ ⦃ validRangesComposed r1 r2 rs prf prf2 ⦄)
         (r1 ∷ (normalise (r2 ∷ rs) ⦃ headandtailSorted (r1 ∷ r2 ∷ rs) prf ⦄ ⦃ headandtailValidRanges (r1 ∷ r2 ∷ rs) prf2 ⦄)))
  =⟨ normalisedSortedList00 rs r1 r2 prf prf2 (overlap1 r1 r2) ⟩
      true
  end

unfoldIsSorted00 ⦃ o ⦄ ⦃ dio ⦄ b f g b1 false =
     begin
      sortedRangeList ((Rg b (f b)) ∷ (if_then_else_ false (ranges2 b1 f g) []))
  =⟨⟩
      sortedRangeList ((Rg b (f b)) ∷ [])
  =⟨⟩
   true
   end
unfoldIsSorted00 ⦃ o ⦄ ⦃ dio ⦄ b f g b1 true =
     begin
      sortedRangeList ((Rg b (f b)) ∷ (if_then_else_ true (ranges2 b1 f g) []))
  =⟨⟩
      sortedRangeList ((Rg b (f b)) ∷ (ranges2 b1 f g))
  =⟨ validSortedList (Rg b (f b)) (ranges2 b1 f g) (unfoldSorted b1 f g) ⟩
   true
   end

unfoldIsSorted0 ⦃ o ⦄ ⦃ dio ⦄ b f g Nothing =
     begin
      sortedRangeList ((Rg b (f b)) ∷ (ranges3 Nothing f g))
  =⟨⟩
      sortedRangeList ((Rg b (f b)) ∷ [])
  =⟨⟩
   true
   end
unfoldIsSorted0 ⦃ o ⦄ ⦃ dio ⦄ b f g mb@(Just b1) =
     begin
      sortedRangeList ((Rg b (f b)) ∷ (ranges3 mb f g))
  =⟨⟩
      sortedRangeList ((Rg b (f b)) ∷ (if_then_else_ ((validFunction2 b1 f) && (validFunction b1 g)) (ranges2 b1 f g) []))
  =⟨ unfoldIsSorted00 b f g b1 ((validFunction2 b1 f) && (validFunction b1 g)) ⟩
   true
   end

unfoldIsSorted ⦃ o ⦄ ⦃ dio ⦄ b f g =
   begin
      sortedRangeList (ranges2 b f g)
  =⟨⟩
      sortedRangeList ((Rg b (f b)) ∷ (ranges3 (g b) f g))
  =⟨ unfoldIsSorted0 b f g (g b) ⟩
   true
   end

unfoldSorted ⦃ o ⦄ ⦃ dio ⦄ b f g = subst IsTrue (sym (unfoldIsSorted b f g)) IsTrue.itsTrue

merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS []) rs2@(RS []) true prf1 prf2 =
   begin
      sortedRangeList (h1 ∷ (merge1 [] (h2 ∷ [])))
  =⟨⟩
      sortedRangeList (h1 ∷ h2 ∷ [])
  =⟨ okSorted h1 h2 ⟩
      true
   end
merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS []) rs2@(RS t2@(h4 ∷ tt2)) true prf1 prf2 =
   begin
      sortedRangeList (h1 ∷ (merge1 [] (h2 ∷ t2)))
  =⟨⟩
      sortedRangeList (h1 ∷ (h2 ∷ t2))
  =⟨ validSortedList h1 (h2 ∷ t2) (validIsSorted (h2 ∷ t2) prf2) ⟩
      true
   end
merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS t1@(h3 ∷ tt1)) rs2@(RS t2) true prf1 prf2 =
   begin
      sortedRangeList (h1 ∷ (merge1 t1 (h2 ∷ t2)))
  =⟨ validSortedList h1 (merge1 t1 (h2 ∷ t2)) (merge1Sorted rs1 (RS (h2 ∷ t2) {prf2}) (headandtail (RS (h1 ∷ t1) {prf1}) prf1) prf2) ⟩
      true
   end
merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS []) rs2@(RS []) false prf1 prf2 =
   begin
      sortedRangeList (h2 ∷ (merge1 (h1 ∷ []) []))
  =⟨⟩
      sortedRangeList (h2 ∷ h1 ∷ [])
  =⟨ okSorted h2 h1 ⟩
      true
   end
merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS t1@(h3 ∷ tt1)) rs2@(RS []) false prf1 prf2 =
   begin
      sortedRangeList (h2 ∷ (merge1 (h1 ∷ t1) []))
  =⟨⟩
      sortedRangeList (h2 ∷ h1 ∷ t1)
  =⟨ validSortedList h2 (h1 ∷ t1) (validIsSorted (h1 ∷ t1) prf1) ⟩
      true
   end
merge1IsSorted0 ⦃ o ⦄ ⦃ dio ⦄ h1 h2 rs1@(RS t1) rs2@(RS t2@(h4 ∷ tt2)) false prf1 prf2 =
   begin
      sortedRangeList (h2 ∷ (merge1 (h1 ∷ t1) t2))
  =⟨ validSortedList h2 (merge1 (h1 ∷ t1) t2) (merge1Sorted (RS (h1 ∷ t1) {prf1}) rs2 prf1 (headandtail (RS (h2 ∷ t2) {prf2}) prf2)) ⟩
      true
   end

merge1IsSorted1 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS []) prf1 prf2 = refl
merge1IsSorted1 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ms1@(h1 ∷ t1)) rs2@(RS []) prf1 prf2 = propIsTrue (sortedRangeList (merge1 (rSetRanges rs1) (rSetRanges rs2))) (validIsSorted (merge1 (rSetRanges rs1) (rSetRanges rs2)) prf1)
merge1IsSorted1 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS ms1@(h1 ∷ t1)) prf1 prf2 = propIsTrue (sortedRangeList (merge1 (rSetRanges rs1) (rSetRanges rs2))) (validIsSorted (merge1 (rSetRanges rs1) (rSetRanges rs2)) prf2)
merge1IsSorted1 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ms1@(h1 ∷ t1)) rs2@(RS ms2@(h2 ∷ t2)) prf1 prf2 =
   begin
      sortedRangeList (merge1 (rSetRanges rs1) (rSetRanges rs2))
  =⟨⟩
      sortedRangeList (if_then_else_ (h1 < h2) (h1 ∷ (merge1 t1 ms2)) (h2 ∷ (merge1 ms1 t2)))
  =⟨ merge1IsSorted0 h1 h2 (RS t1 {headandtail rs1 prf1}) (RS t2 {headandtail rs2 prf2}) (h1 < h2) prf1 prf2 ⟩
      true
   end

singletonRangeValid0 ⦃ o ⦄ ⦃ dio ⦄ x =
  begin
   validRangeList ((singletonRange x) ∷ [])
  =⟨⟩
    ((rangeLower (singletonRange x)) <= (rangeUpper (singletonRange x)))
  =⟨⟩
    ((rangeLower (Rg (BoundaryBelow x) (BoundaryAbove x))) <= (rangeUpper (Rg (BoundaryBelow x) (BoundaryAbove x))))
  =⟨⟩
    (BoundaryBelow x <= BoundaryAbove x)
  =⟨⟩
    ((compare (BoundaryBelow x) (BoundaryAbove x) == LT) || (compare (BoundaryBelow x) (BoundaryAbove x) == EQ))
  =⟨⟩
    (((if_then_else_ (x > x) (if_then_else_ (adjacent x x) EQ GT) LT) == LT) || (compare (BoundaryBelow x) (BoundaryAbove x) == EQ))
  =⟨ cong (_|| (compare (BoundaryBelow x) (BoundaryAbove x) == EQ)) (cong (_== LT) (propIf3 (x > x) (lt x x refl))) ⟩
    ((LT == LT) || (compare (BoundaryBelow x) (BoundaryAbove x) == EQ))
  =⟨⟩
    (true || (compare (BoundaryBelow x) (BoundaryAbove x) == EQ))
  =⟨⟩
    true
  end

intersectHelper ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ls1) rs2@(RS ls2) prf1 prf2 =
  begin
     validRangeList (rSetRanges (rSetIntersection rs1 {prf1} rs2 {prf2}))
  =⟨⟩
     validRangeList (rSetRanges (RS (filter (λ r → (rangeIsEmpty r) == false) (merge2 (rSetRanges rs1) (rSetRanges rs2))) {intersection0 rs1 rs2 prf1 prf2}))
  =⟨⟩
     validRangeList (filter (λ r → (rangeIsEmpty r) == false) (merge2 (rSetRanges rs1) (rSetRanges rs2)))
  end

negationHelper ⦃ o ⦄ ⦃ dio ⦄ rs@(RS ranges) {prf} =
  begin
     validRangeList (rSetRanges (rSetNegation rs {prf}))
  =⟨⟩
     validRangeList (rSetRanges (RS ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ ranges))) {negation rs prf}))
  =⟨⟩
    validRangeList (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ ranges)))
  =⟨⟩
    validRangeList (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs))))
  end

fullRangeValid ⦃ o ⦄ ⦃ dio ⦄ =
  begin
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (fullRange ⦃ o ⦄ ⦃ dio ⦄ ∷ [])
  =⟨⟩
    ((rangeLower (fullRange ⦃ o ⦄ ⦃ dio ⦄)) <= (rangeUpper (fullRange ⦃ o ⦄ ⦃ dio ⦄)))
  =⟨⟩
    true
  end

intersection3 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges1@(r1 ∷ rss1)) rs2@(RS ranges2@(r2 ∷ rss2)) ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 true =
  begin
    validRangeList (filter (λ x → (rangeIsEmpty x == false))
         (if_then_else_ true
            (merge2 rss1 ranges2) (merge2 ⦃ o ⦄ ⦃ dio ⦄ ranges1 rss2)))
   =⟨⟩
    validRangeList (filter (λ x → (rangeIsEmpty x == false)) (merge2 rss1 ranges2))
   =⟨ intersectionHolds
 ⦃ o ⦄ ⦃ dio ⦄ (RS rss1 {headandtail rs1 ⦃ NonEmpty.itsNonEmpty ⦄ prf1}) rs2 (headandtail ⦃ o ⦄ ⦃ dio ⦄ rs1 ⦃ ne1 ⦄ prf1) prf2 ⟩
      true
   end
intersection3 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges1@(r1 ∷ rss1)) rs2@(RS ranges2@(r2 ∷ rss2)) ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 false =
  begin
    validRangeList (filter (λ x → (rangeIsEmpty x == false))
         (if_then_else_ false
            (merge2 ⦃ o ⦄ ⦃ dio ⦄ rss1 ranges2) (merge2 ranges1 rss2)))
   =⟨⟩
    validRangeList (filter (λ x → (rangeIsEmpty x == false)) (merge2 ranges1 rss2))
   =⟨ intersectionHolds
 ⦃ o ⦄ ⦃ dio ⦄ rs1 (RS rss2 {headandtail rs2 ⦃ NonEmpty.itsNonEmpty ⦄ prf2}) prf1 (headandtail ⦃ o ⦄ ⦃ dio ⦄ rs2 ⦃ ne2 ⦄ prf2) ⟩
      true
   end

intersection2 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges1@(r1 ∷ rss1)) rs2@(RS ranges2@(r2 ∷ rss2)) ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 false =
  begin
        validRangeList (
       if_then_else_ false

      ((rangeIntersection ⦃ o ⦄ ⦃ dio ⦄ r1 r2) ∷ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false))
       (if_then_else_ ((rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1) < (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2))
       (merge2 ⦃ o ⦄ ⦃ dio ⦄ rss1 ranges2) (merge2 ⦃ o ⦄ ⦃ dio ⦄ ranges1 rss2))))

       (filter (λ x → (rangeIsEmpty x == false))
         (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
         (merge2 rss1 ranges2) (merge2 ranges1 rss2)))
      )
   =⟨⟩
    validRangeList (filter (λ x → (rangeIsEmpty x == false))
    (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
    (merge2 rss1 ranges2) (merge2 ranges1 rss2)))
   =⟨ intersection3 ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2) ⟩
    true
   end
intersection2 ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges1@(r1 ∷ rss1)) rs2@(RS ranges2@(r2 ∷ rss2)) ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 true =
  begin
        validRangeList (
       if_then_else_ true

      ((rangeIntersection ⦃ o ⦄ ⦃ dio ⦄ r1 r2) ∷ (filter (λ x → (rangeIsEmpty x == false))
       (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
       (merge2 rss1 ranges2) (merge2 ranges1 rss2))))

       (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false))
         (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
         (merge2 ⦃ o ⦄ ⦃ dio ⦄ rss1 ranges2) (merge2 ⦃ o ⦄ ⦃ dio ⦄ ranges1 rss2)))
      )
   =⟨⟩
    validRangeList ((rangeIntersection ⦃ o ⦄ ⦃ dio ⦄ r1 r2) ∷ (filter (λ x → (rangeIsEmpty x == false))
       (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
       (merge2 rss1 ranges2) (merge2 ranges1 rss2))))
   =⟨ validList (rangeIntersection ⦃ o ⦄ ⦃ dio ⦄ r1 r2) (filter (λ x → (rangeIsEmpty x == false))
       (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)
       (merge2 rss1 ranges2) (merge2 ranges1 rss2)))
       (subst IsTrue (sym
       (intersection3 ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 ⦃ ne1 ⦄ ⦃ ne2 ⦄ prf1 prf2 (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2)))
       IsTrue.itsTrue) ⟩
    true
   end

intersectionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS []) _ _ =
   begin
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs1) (rSetRanges rs2)))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ [] []))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) [])
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
   =⟨⟩
    true
   end
intersectionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges@(r ∷ rs)) rs2@(RS []) _ _ =
   begin
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs1) (rSetRanges rs2)))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ ranges []))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) [])
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
   =⟨⟩
    true
   end

intersectionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS []) rs2@(RS ranges@(r ∷ rs)) _ _ =
   begin
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs1) (rSetRanges rs2)))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) (merge2 ⦃ o ⦄ ⦃ dio ⦄ [] ranges))
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ (filter (λ x → (rangeIsEmpty ⦃ o ⦄ ⦃ dio ⦄ x == false)) [])
   =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
   =⟨⟩
    true
   end

intersectionHolds ⦃ o ⦄ ⦃ dio ⦄ rs1@(RS ranges1@(r1 ∷ rss1)) rs2@(RS ranges2@(r2 ∷ rss2)) prf1 prf2 =
   begin
    validRangeList (filter (λ x → (rangeIsEmpty x == false)) (merge2 (rSetRanges rs1) (rSetRanges rs2)))
   =⟨⟩
    validRangeList (filter (λ x → (rangeIsEmpty x == false))
      ((rangeIntersection r1 r2) ∷ (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2) (merge2 rss1 ranges2) (merge2 ranges1 rss2))))
   =⟨⟩
    validRangeList (
       if_then_else_ (rangeIsEmpty (rangeIntersection r1 r2) == false)

      ((rangeIntersection r1 r2) ∷ (filter (λ x → (rangeIsEmpty x == false))
       (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2) (merge2 rss1 ranges2) (merge2 ranges1 rss2))))

       (filter (λ x → (rangeIsEmpty x == false))
         (if_then_else_ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r1 < rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r2) (merge2 rss1 ranges2) (merge2 ranges1 rss2)))
      )
   =⟨ intersection2 ⦃ o ⦄ ⦃ dio ⦄ rs1 rs2 prf1 prf2 (rangeIsEmpty (rangeIntersection r1 r2) == false) ⟩
     true
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ [] =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 [])
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
   =⟨⟩
      true
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []
   end
validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (BoundaryAboveAll ∷ []) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (BoundaryAboveAll  ∷ []))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
   =⟨⟩
      true
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (BoundaryAboveAll ∷ [])
   end
validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (BoundaryBelowAll ∷ []) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (BoundaryBelowAll ∷ []))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg BoundaryBelowAll BoundaryAboveAll) ∷ [])
   =⟨⟩
      true
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (BoundaryBelowAll ∷ [])
   end
validRanges1 ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryAbove x) ∷ []) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryAbove x) ∷ []))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg (BoundaryAbove x) BoundaryAboveAll) ∷ [])
   =⟨⟩
      (BoundaryAbove x) <= BoundaryAboveAll
   =⟨⟩
      true
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryAbove x) ∷ [])
   end
validRanges1 ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryBelow x) ∷ []) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryBelow x) ∷ []))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg (BoundaryBelow x) (BoundaryAboveAll)) ∷ [])
   =⟨⟩
      (BoundaryBelow x) <= BoundaryAboveAll
   =⟨⟩
      true
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ ((BoundaryBelow x) ∷ [])
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ []) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ []))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ ranges1([]))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ [])
   =⟨⟩
      (b1 <= b2)
   =⟨ sym (prop_and_true (b1 <= b2)) ⟩
      ((b1 <= b2) && true)
   =⟨⟩
      ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ [])
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs@(b3@(BoundaryBelow x) ∷ [])) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (Rg b3 (BoundaryAboveAll)) ∷ [])
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 (BoundaryAboveAll))) && (validRangeList ⦃ o ⦄ ⦃ dio ⦄ []))
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 BoundaryAboveAll)) && true)
   =⟨⟩
      (((b1 <= b2) && (b2 <= b3) && (b3 <= BoundaryAboveAll)) && true)
   =⟨ prop_and_true ((b1 <= b2) && (b2 <= b3) && (b3 <= BoundaryAboveAll)) ⟩
      ((b1 <= b2) && (b2 <= b3) && true)
   =⟨⟩
      ((b1 <= b2) && (b2 <= b3) && (validBoundaryList (b3 ∷ [])))
   =⟨⟩
       ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b2 ∷ b3 ∷ [])))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs)
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs@(b3@(BoundaryAbove x) ∷ [])) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (Rg b3 (BoundaryAboveAll)) ∷ [])
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 BoundaryAboveAll)) && (validRangeList ⦃ o ⦄ ⦃ dio ⦄ []))
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 BoundaryAboveAll)) && true)
   =⟨⟩
      (((b1 <= b2) && (b2 <= b3) && (b3 <= BoundaryAboveAll)) && true)
   =⟨ prop_and_true ((b1 <= b2) && (b2 <= b3) && (b3 <= BoundaryAboveAll)) ⟩
      ((b1 <= b2) && (b2 <= b3) && true)
   =⟨⟩
      ((b1 <= b2) && (b2 <= b3) && (validBoundaryList (b3 ∷ [])))
   =⟨⟩
       ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b2 ∷ b3 ∷ [])))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs)
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs@(b3@(BoundaryBelowAll) ∷ [])) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (Rg b3 (BoundaryAboveAll)) ∷ [])
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 (BoundaryAboveAll))) && (validRangeList ⦃ o ⦄ ⦃ dio ⦄ []))
   =⟨⟩
      ((okAdjacent (Rg b1 b2) (Rg b3 (BoundaryAboveAll))) && true)
   =⟨⟩
      (((b1 <= b2) && (b2 <= b3) && (b3 <= (BoundaryAboveAll))) && true)
   =⟨ prop_and_true ((b1 <= b2) && (b2 <= b3) && (b3 <= (BoundaryAboveAll))) ⟩
      ((b1 <= b2) && (b2 <= b3) && true)
   =⟨⟩
      ((b1 <= b2) && (b2 <= b3) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b3 ∷ [])))
   =⟨⟩
       ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b2 ∷ b3 ∷ [])))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs)
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs@(b3@(BoundaryAboveAll) ∷ [])) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄  (b1 ∷ b2 ∷ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄  bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄  ((Rg b1 b2) ∷ [])
   =⟨⟩
      (b1 <= b2)
   =⟨ sym (prop_and_true (b1 <= b2)) ⟩
      ((b1 <= b2) && true)
   =⟨⟩
      ((b1 <= b2) && (b2 <= b3))
   =⟨ sym (prop_and_true ((b1 <= b2) && (b2 <= b3))) ⟩
      (((b1 <= b2) && (b2 <= b3)) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄  (b3 ∷ [])))
   =⟨ prop_and_assoc (b1 <= b2) (b2 <= b3) (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄  (b3 ∷ [])) ⟩
      ((b1 <= b2) && (b2 <= b3) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄  (b3 ∷ [])))
   =⟨⟩
      ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b2 ∷ b3 ∷ [])))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs)
   end

validRanges1 ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs@(b3 ∷ bss@(b4 ∷ bsss))) =
   begin
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ (ranges1 ⦃ o ⦄ ⦃ dio ⦄  (b1 ∷ b2 ∷ bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄ ((Rg b1 b2) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄  bs))
   =⟨⟩
      validRangeList ⦃ o ⦄ ⦃ dio ⦄  ((Rg b1 b2) ∷ (Rg b3 b4) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bsss))
   =⟨⟩
      (okAdjacent (Rg b1 b2) (Rg b3 b4)) && validRangeList ((Rg b3 b4) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bsss))
   =⟨⟩
      (((b1 <= b2) && (b2 <= b3) && (b3 <= b4)) && (validRangeList ((Rg b3 b4) ∷ (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bsss))))
   =⟨⟩
      (((b1 <= b2) && (b2 <= b3) && (b3 <= b4)) && (validRangeList (ranges1 ⦃ o ⦄ ⦃ dio ⦄ bs)))
   =⟨ cong (((b1 <= b2) && (b2 <= b3) && (b3 <= b4)) &&_) (validRanges1 ⦃ o ⦄ ⦃ dio ⦄ bs) ⟩
      (((b1 <= b2) && ((b2 <= b3) && (b3 <= b4))) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs))
   =⟨ prop_and_assoc (b1 <= b2) ((b2 <= b3) && (b3 <= b4)) (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs) ⟩
      ((b1 <= b2) && (((b2 <= b3) && (b3 <= b4)) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs)))
   =⟨ cong ((b1 <= b2) &&_) (prop_and_assoc (b2 <= b3) (b3 <= b4) (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs)) ⟩
      ((b1 <= b2) && ((b2 <= b3) && (b3 <= b4) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs)))
   =⟨⟩
      ((b1 <= b2) && ((b2 <= b3) && (b3 <= b4) && (b3 <= b4 && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bss))))
   =⟨ cong ((b1 <= b2) &&_) (cong (b2 <= b3 &&_) (prop_and_cancel (b3 <= b4))) ⟩
      ((b1 <= b2) && (b2 <= b3) && (b3 <= b4) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bss))
   =⟨⟩
      ((b1 <= b2) && (b2 <= b3) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs))
   =⟨⟩
      ((b1 <= b2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b2 ∷ bs)))
   =⟨⟩
      validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (b1 ∷ b2 ∷ bs)
   end

validSetBounds ⦃ o ⦄ ⦃ dio ⦄ [] =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []
   end

validSetBounds ⦃ o ⦄ ⦃ dio ⦄ bs@(BoundaryBelowAll ∷ []) =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ bs)
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []
  =⟨⟩
   true
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bs
   end

validSetBounds ⦃ o ⦄ ⦃ dio ⦄ bs@(b0@(BoundaryBelowAll) ∷ bss@(b1 ∷ b2)) =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ bs)
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bss
  =⟨⟩
    (true && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bss))
  =⟨ cong (_&& (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ bss)) (sym (BoundaryBelowAllSmaller ⦃ o ⦄ ⦃ dio ⦄ b1)) ⟩
    ((BoundaryBelowAll <= b1) && (validBoundaryList bss))
  =⟨⟩
    validBoundaryList bs
  end

validSetBounds ⦃ o ⦄ ⦃ dio ⦄ bs@(b@(BoundaryBelow x) ∷ bss) =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ bs)
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (BoundaryBelowAll ∷ bs)
  =⟨⟩
    ((BoundaryBelowAll <= b) && (validBoundaryList bs))
  =⟨⟩
    (true && (validBoundaryList bs))
  =⟨⟩
    (validBoundaryList bs)
   end

validSetBounds ⦃ o ⦄ ⦃ dio ⦄ bs@(b@(BoundaryAboveAll) ∷ bss) =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ bs)
  =⟨⟩
    validBoundaryList (BoundaryBelowAll ∷ bs)
  =⟨⟩
    ((BoundaryBelowAll <= b) && (validBoundaryList bs))
  =⟨⟩
    (true && (validBoundaryList bs))
  =⟨⟩
    (validBoundaryList bs)
   end
validSetBounds ⦃ o ⦄ ⦃ dio ⦄ bs@(b@(BoundaryAbove x) ∷ bss) =
  begin
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (setBounds1 ⦃ o ⦄ ⦃ dio ⦄ bs)
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (BoundaryBelowAll ∷ bs)
  =⟨⟩
    ((BoundaryBelowAll <= b) && (validBoundaryList bs))
  =⟨⟩
    (true && (validBoundaryList bs))
  =⟨⟩
    (validBoundaryList bs)
   end

valid ⦃ o ⦄ ⦃ dio ⦄ rs@(RS []) {_} =
  begin
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs)
  =⟨⟩
    validRangeList ⦃ o ⦄ ⦃ dio ⦄ []
  =⟨⟩
    true
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ [])
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs))
  end
valid ⦃ o ⦄ ⦃ dio ⦄ rs@(RS (r ∷ [])) {_} =
  begin
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs)
  =⟨⟩
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (r ∷ [])
  =⟨⟩
    ((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r) <= (rangeUpper ⦃ o ⦄ ⦃ dio ⦄  r))
  =⟨ sym (prop_and_true ((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r) <= (rangeUpper ⦃ o ⦄ ⦃ dio ⦄  r))) ⟩
    (((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r) <= (rangeUpper ⦃ o ⦄ ⦃ dio ⦄  r)) && true)
  =⟨⟩
    (((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r) <= (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r)) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ []))
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ ((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r) ∷ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄ r) ∷ [])
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ (r ∷ []))
  =⟨⟩
    validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs))
  end

valid ⦃ o ⦄ ⦃ dio ⦄ rs@(RS ranges@(r@(Rg l1 u1) ∷ r1@(r2@(Rg l2 u2) ∷ rss))) {prf} =
  begin
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (rSetRanges rs)
  =⟨⟩
     validRangeList ⦃ o ⦄ ⦃ dio ⦄ (r ∷ (r2 ∷ rss))
  =⟨⟩
     ((okAdjacent r r2) && (validRangeList ⦃ o ⦄ ⦃ dio ⦄ r1))
  =⟨⟩
    ((l1 <= u1 && (u1 <= l2 && l2 <= u2)) && (validRangeList ⦃ o ⦄ ⦃ dio ⦄ r1))
  =⟨ cong ((l1 <= u1 && u1 <= l2 && l2 <= u2) &&_) (valid ⦃ o ⦄ ⦃ dio ⦄ (RS r1 {headandtail rs prf}) {headandtail rs prf}) ⟩
     ((l1 <= u1 && (u1 <= l2 && l2 <= u2)) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ r1)))
  =⟨ prop_and_assoc (l1 <= u1) (u1 <= l2 && l2 <= u2) (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ r1)) ⟩
    (l1 <= u1 && ((u1 <= l2  && l2 <= u2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ r1))))
  =⟨ cong (l1 <= u1 &&_) (prop_and_assoc (u1 <= l2) (l2 <= u2) (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ r1))) ⟩
    (l1 <= u1 && u1 <= l2  && (l2 <= u2  && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ r1))))
  =⟨⟩
    (l1 <= u1 && u1 <= l2  && (l2 <= u2
         && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ ((rangeLower ⦃ o ⦄ ⦃ dio ⦄  r2) ∷ (rangeUpper ⦃ o ⦄ ⦃ dio ⦄  r2) ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss)))))
  =⟨⟩
    (l1 <= u1 && u1 <= l2 && l2 <= u2 && l2 <= u2
         && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ ((rangeUpper ⦃ o ⦄ ⦃ dio ⦄  r2) ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss))))
  =⟨ cong (l1 <= u1 &&_) (cong (u1 <= l2 &&_) (prop_and_cancel (l2 <= u2))) ⟩
    (l1 <= u1 && u1 <= l2 && ((l2 <= u2) && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (u2 ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss)))))
  =⟨⟩
    (l1 <= u1 && u1 <= l2 && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (l2 ∷ (u2 ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss)))))
  =⟨⟩
    (l1 <= u1 && (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (u1 ∷ l2 ∷ u2 ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss))))
  =⟨⟩
   (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (l1 ∷ u1 ∷ l2 ∷ u2 ∷ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ rss)))
  =⟨⟩
   (validBoundaryList ⦃ o ⦄ ⦃ dio ⦄ (bounds1 ⦃ o ⦄ ⦃ dio ⦄ ranges))
  end


open import Data.List using ( [] )
open import Data.Product using ( proj₁ ; proj₂ )
open import Data.Sum using ( _⊎_ ; inj₁ ; inj₂ )
open import Relation.Binary.PropositionalEquality using ( refl )
open import Web.Semantic.DL.ABox.Interp using ( ⌊_⌋ ; ind ; _*_ )
open import Web.Semantic.DL.ABox.Interp.Morphism using ( _,_ )
open import Web.Semantic.DL.ABox.Model using
  ( _⊨a_ ; on-bnode ; bnodes ; _,_ ; ⊨a-resp-≲ )
open import Web.Semantic.DL.Category.Composition using ( _∙_ )
open import Web.Semantic.DL.Category.Properties.Composition.Assoc using
  ( compose-assoc )
open import Web.Semantic.DL.Category.Properties.Composition.Lemmas using
  ( compose-left ; compose-right ; compose-resp-⊨a )
open import Web.Semantic.DL.Category.Properties.Composition.RespectsEquiv using
  ( compose-resp-≣ )
open import Web.Semantic.DL.Category.Properties.Equivalence using
  ( ≣-refl ; ≣-sym ; ≣-trans )
open import Web.Semantic.DL.Category.Properties.Tensor.Coherence using
  ( symm-unit )
open import Web.Semantic.DL.Category.Properties.Tensor.Lemmas using
  ( tensor-up ; tensor-down ; tensor-resp-⊨a )
open import Web.Semantic.DL.Category.Properties.Tensor.SymmNatural using
  ( symm-natural )
open import Web.Semantic.DL.Category.Object using ( Object ; IN ; fin )
open import Web.Semantic.DL.Category.Morphism using
  ( _⇒_ ; BN ; impl ; _⊑_ ; _≣_ ; _,_ )
open import Web.Semantic.DL.Category.Tensor using ( _⟨⊗⟩_ )
open import Web.Semantic.DL.Category.Unit using ( I )
open import Web.Semantic.DL.Category.Wiring using
  ( wires-≈ ; wires-≈⁻¹ ; identity ; unit₁ ; unit₂ ; symm )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox )
open import Web.Semantic.DL.TBox.Interp using ( Δ ; _⊨_≈_ ; ≈-refl ; ≈-sym )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( ≲-refl )
open import Web.Semantic.Util using
  ( _∘_ ; False ; _⊕_⊕_ ; inode ; bnode ; enode ; left ; right ; up ; down )

module Web.Semantic.DL.Category.Properties.Tensor.UnitNatural
  {Σ : Signature} {S T : TBox Σ} where

unit₁-natural : ∀ {A B : Object S T} (F : A ⇒ B) →
  ((identity I ⟨⊗⟩ F) ∙ unit₁ B ≣ unit₁ A ∙ F)
unit₁-natural {A} {B} F = (LHS⊑RHS , RHS⊑LHS) where

  LHS⊑RHS : ((identity I ⟨⊗⟩ F) ∙ unit₁ B ⊑ unit₁ A ∙ F)
  LHS⊑RHS J J⊨STA J⊨LHS = (f , J⊨RHS) where

    f : False ⊕ IN A ⊕ BN F → Δ ⌊ J ⌋
    f (inode ())
    f (bnode x) = ind J (inode (inj₂ x))
    f (enode v) = ind J (bnode (inode (inj₂ v)))

    f✓ : ∀ x → ⌊ J ⌋ ⊨ ind J (left (down x)) ≈ on-bnode f (ind J) (right x)
    f✓ (inode x) = ≈-refl ⌊ J ⌋
    f✓ (bnode v) = ≈-refl ⌊ J ⌋
    f✓ (enode y) = wires-≈ inj₂ (proj₂ (fin B) y)
      (compose-right (identity I ⟨⊗⟩ F) (unit₁ B) J J⊨LHS)

    J⊨RHS : bnodes J f ⊨a impl (unit₁ A ∙ F)
    J⊨RHS = compose-resp-⊨a (unit₁ A) F (bnodes J f)
      (wires-≈⁻¹ inj₂ (λ x → ≈-refl ⌊ J ⌋) (proj₁ (fin A)))
      (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , f✓) (impl F)
        (tensor-down (identity I) F (left * J)
          (compose-left (identity I ⟨⊗⟩ F) (unit₁ B) J J⊨LHS)))

  RHS⊑LHS : (unit₁ A ∙ F ⊑ (identity I ⟨⊗⟩ F) ∙ unit₁ B)
  RHS⊑LHS J J⊨STA J⊨RHS = (f , J⊨LHS) where

    f : ((False ⊎ BN F) ⊕ (False ⊎ IN B) ⊕ False) → Δ ⌊ J ⌋
    f (inode (inj₁ ()))
    f (inode (inj₂ v)) = ind J (bnode (enode v))
    f (bnode (inj₁ ()))
    f (bnode (inj₂ y)) = ind J (enode y)
    f (enode ())

    f✓ : ∀ x → ⌊ J ⌋ ⊨ ind J (right x) ≈ on-bnode f (ind J) (left (down x))
    f✓ (inode x) = ≈-sym ⌊ J ⌋ (wires-≈ inj₂ (proj₂ (fin A) x) (compose-left (unit₁ A) F J J⊨RHS))
    f✓ (bnode v) = ≈-refl ⌊ J ⌋
    f✓ (enode y) = ≈-refl ⌊ J ⌋

    J⊨LHS : bnodes J f ⊨a impl ((identity I ⟨⊗⟩ F) ∙ unit₁ B)
    J⊨LHS = compose-resp-⊨a (identity I ⟨⊗⟩ F) (unit₁ B) (bnodes J f)
      (tensor-resp-⊨a (identity I) F (left * bnodes J f)
        (wires-≈⁻¹ {I = up * left * bnodes J f} (λ ()) (λ ()) [])
        (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , f✓) (impl F)
          (compose-right (unit₁ A) F J J⊨RHS)))
      (wires-≈⁻¹ inj₂ (λ x → ≈-refl ⌊ J ⌋) (proj₁ (fin B)))

unit₂-natural : ∀ {A B : Object S T} (F : A ⇒ B) →
  ((F ⟨⊗⟩ identity I) ∙ unit₂ B ≣ unit₂ A ∙ F)
unit₂-natural {A} {B} F =
  ≣-trans
    (compose-resp-≣ (≣-refl (F ⟨⊗⟩ identity I)) (≣-sym (symm-unit B)))
  (≣-trans
    (≣-sym (compose-assoc (F ⟨⊗⟩ identity I) (symm B I) (unit₁ B)))
  (≣-trans
    (compose-resp-≣ (symm-natural F (identity I)) (≣-refl (unit₁ B)))
  (≣-trans
    (compose-assoc (symm A I) (identity I ⟨⊗⟩ F) (unit₁ B))
  (≣-trans
    (compose-resp-≣ (≣-refl (symm A I)) (unit₁-natural F))
  (≣-trans
    (≣-sym (compose-assoc (symm A I) (unit₁ A) F))
    (compose-resp-≣ (symm-unit A) (≣-refl F)))))))