{-# OPTIONS --without-K --rewriting --termination-depth=2 #-}

open import HoTT
open import cw.CW
open import cw.examples.Sphere

module cw.examples.Torus where

⊤-has-dec-eq : has-dec-eq ⊤
⊤-has-dec-eq unit unit = inl idp

⊤-is-set : is-set ⊤
⊤-is-set = dec-eq-is-set ⊤-has-dec-eq

cw-torus-skel : Skeleton {lzero} (S (S 0))

CWTorus : Type₀
CWTorus = ⟦ cw-torus-skel ⟧

α₁ : Bool → S⁰ → ⊤
α₁ _ _ = unit

X₁ = attached-skeleton (⊤ , ⊤-is-set) (Bool , Bool-is-set) α₁

α₂ : ⊤ → S¹ → (Attached α₁)
α₂ unit = Pushout-rec ψ ψ (cst idp)
    where
    ψ : ⊤ → Attached α₁
    ψ unit = PushoutGeneric.from-cc (inl unit)

X₂ = attached-skeleton X₁ (⊤ , ⊤-is-set) α₂

cw-torus-skel = X₂


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

open import Generics.Prelude hiding (lookup)
open import Generics.Telescope
open import Generics.Desc
open import Generics.HasDesc

module Generics.Constructions.Case
  {P I ℓ} {A : Indexed P I ℓ}
  (H : HasDesc A) (open HasDesc H)
  {p c} (Pr : Pred′ I λ i → A′ (p , i) → Set c)
  where

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

Pr′ : A′ (p , i) → Set c
Pr′ {i} = unpred′ I _ Pr i

--------------------------
-- Types of motives

levelCase : ConDesc P V I → Level
levelCase (var x) = c
levelCase (π {ℓ′} _ _ C) = ℓ′ ⊔ levelCase C
levelCase (A ⊗ B) = levelIndArg A ℓ ⊔ levelCase B

MotiveCon : (C : ConDesc P V I)
          → (∀ {i} → ⟦ C ⟧Con A′ (p , v , i) → Set c)
          → Set (levelCase C)
MotiveCon (var γ) X = X refl
MotiveCon (π ia S C) X = Π< ia > (S _) λ s → MotiveCon C (X ∘ (s ,_))
MotiveCon (A ⊗ B) X = (x : ⟦ A ⟧IndArg A′ (p , _))
                    → MotiveCon B (X ∘ (x ,_))

Motives : ∀ k → Set (levelCase (lookupCon D k))
Motives k = MotiveCon (lookupCon D k) (λ x → Pr′ (constr (k , x)))

--------------------------
-- Case-analysis principle

module _ (methods : Els Motives) where

  caseData : ∀ {i} → (x : ⟦ D ⟧Data A′ (p , i)) → Pr′ (constr x)
  caseData (k , x) = caseCon (lookupCon D k) (methods k) x
    where
      caseCon
        : (C   : ConDesc P V I)
          {mk  : ∀ {i} → ⟦ C ⟧Con A′ (p , v , i) → ⟦ D ⟧Data A′ (p , i)}
          (mot : MotiveCon C λ x → Pr′ (constr (mk x)))
          (x   : ⟦ C ⟧Con A′ (p , v , i))
        → Pr′ (constr (mk x))
      caseCon (var γ) mot refl = mot
      caseCon (π ia _ C) mot (s , x) = caseCon C (app< ia > mot s) x
      caseCon (A ⊗ B) mot (a , b) = caseCon B (mot a) b

  case : (x : A′ (p , i)) → Pr′ x
  case x = subst Pr′ (constr∘split x) (caseData (split x))

deriveCase : Arrows Motives (Pred′ I (λ i → (x : A′ (p , i)) → Pr′ x))
deriveCase = curryₙ (λ m → pred′ I _ λ i → case m)


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

module Polynomial.Simple.Reflection where

open import Agda.Builtin.Reflection
open import Reflection.Helpers

open import Polynomial.Simple.Solver renaming (solve to solve-fn)
open AlmostCommutativeRing

open import Data.Nat.Table

open import Data.Fin   as Fin   using (Fin)
open import Data.Vec   as Vec   using (Vec; _∷_; [])
open import Data.List  as List  using (List; _∷_; [])
open import Data.Maybe as Maybe using (Maybe; just; nothing; fromMaybe)
open import Agda.Builtin.Nat    using (_<_)
open import Data.Nat            using (ℕ; suc; zero)
open import Data.Bool           using (Bool; if_then_else_; true; false)
open import Data.Unit           using (⊤)
open import Data.String         using (String)
open import Data.Product        using (_,_)
open import Function

module Internal where
  _∈Ring : Term → TC Term
  ring ∈Ring = checkType ring (def (quote AlmostCommutativeRing) (unknown ⟨∷⟩ unknown ⟨∷⟩ []))

  vars : Term → Maybe Table
  vars = go []
    where
    go : Table → Term → Maybe Table
    go t (con (quote List._∷_) (_ ∷ _ ∷ var i [] ⟨∷⟩ xs ⟨∷⟩ _)) = go (insert i t) xs
    go t (con (quote List.List.[]) _) = just t
    go _ _ = nothing

  module OverRing (ring : Term) where
    _∈List⟨Carrier⟩ : Term → TC Term
    t ∈List⟨Carrier⟩ =
      checkType t
        (quote List ⟨ def ⟩ 1 ⋯⟅∷⟆ def (quote Carrier) (2 ⋯⟅∷⟆ ring ⟨∷⟩ []) ⟨∷⟩ []) >>= normalise

    record Ring⇓ : Set where
      constructor +⇒_*⇒_^⇒_-⇒_
      field +′ *′ ^′ -′ : Maybe Name

    ring⇓ : TC Ring⇓
    ring⇓ = ⦇ +⇒ ⟦ quote _+_ ⇓⟧ₙ *⇒ ⟦ quote _*_ ⇓⟧ₙ ^⇒ ⟦ quote _^_ ⇓⟧ₙ -⇒ ⟦ quote -_ ⇓⟧ₙ ⦈
      where
      ⟦_⇓⟧ₙ : Name → TC (Maybe Name)
      ⟦ nm ⇓⟧ₙ =
        normalise (nm ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ [])
          <&> λ where (def f args) → just f
                      _ → nothing

    module _ (nms : Ring⇓) where
      open Ring⇓ nms

      module _ (numVars : ℕ) where

        -- This function applies the hidden arguments that the constructors
        -- that Expr needs. The first is the universe level, the second is the
        -- type it contains, and the third is the number of variables it's
        -- indexed by. All three of these could likely be inferred, but to
        -- make things easier we supply the third because we know it.
        infixr 5 E⟅∷⟆_
        E⟅∷⟆_ : List (Arg Term) → List (Arg Term)
        E⟅∷⟆ xs = 1 ⋯⟅∷⟆
                  (quote Carrier ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ []) ⟅∷⟆
                  ℕ′ numVars ⟅∷⟆
                  xs

        -- A constant expression.
        Κ′ : Term → Term
        Κ′ x = quote Κ ⟨ con ⟩ E⟅∷⟆ x ⟨∷⟩ []

        _⇓≟_ : Maybe Name → Name → Bool
        nothing ⇓≟ _ = false
        just x  ⇓≟ y = primQNameEquality x y
        {-# INLINE _⇓≟_ #-}

        module ToExpr (Ι′ : ℕ → Maybe Term) where
          mutual
            -- Application of a ring operator often doesn't have a type as
            -- simple as "Carrier → Carrier → Carrier": there may be hidden
            -- arguments, etc. Here, we do our best to handle those cases,
            -- by just taking the last two explicit arguments.
            E⟨_⟩₂ : Name → List (Arg Term) → Term
            E⟨ nm ⟩₂ (x ⟨∷⟩ y ⟨∷⟩ []) = nm ⟨ con ⟩ E⟅∷⟆ E x ⟨∷⟩ E y ⟨∷⟩ []
            E⟨ nm ⟩₂ (x ∷ xs) = E⟨ nm ⟩₂ xs
            E⟨ nm ⟩₂ _ = unknown

            E⟨_⟩₁ : Name → List (Arg Term) → Term
            E⟨ nm ⟩₁ (x ⟨∷⟩ []) = nm ⟨ con ⟩ E⟅∷⟆ E x ⟨∷⟩ []
            E⟨ nm ⟩₁ (x ∷ xs) = E⟨ nm ⟩₁ xs
            E⟨ _ ⟩₁ _ = unknown

            E⟨^⟩ : List (Arg Term) → Term
            E⟨^⟩ (x ⟨∷⟩ y ⟨∷⟩ []) = quote _⊛_ ⟨ con ⟩ E⟅∷⟆ E x ⟨∷⟩ y ⟨∷⟩ []
            E⟨^⟩ (x ∷ xs) = E⟨^⟩ xs
            E⟨^⟩ _ = unknown

            -- When trying to figure out the shape of an expression, one of
            -- the difficult tasks is recognizing where constants in the
            -- underlying ring are used. If we were only dealing with ℕ, we
            -- might look for its constructors: however, we want to deal with
            -- arbitrary types which implement AlmostCommutativeRing. If the
            -- Term type contained type information we might be able to
            -- recognize it there, but it doesn't.
            --
            -- We're in luck, though, because all other cases in the following
            -- function *are* recognizable. As a result, the "catch-all" case
            -- will just assume that it has a constant expression.
            E : Term → Term
            E (def (quote _+_) xs) = E⟨ quote _⊕_ ⟩₂ xs
            E (def (quote _*_) xs) = E⟨ quote _⊗_ ⟩₂ xs
            E (def (quote _^_) xs) = E⟨^⟩ xs
            E (def (quote -_) xs) = E⟨ quote ⊝_ ⟩₁ xs
            E (def nm xs) = if +′ ⇓≟ nm then E⟨ quote _⊕_ ⟩₂ xs else
                            if *′ ⇓≟ nm then E⟨ quote _⊗_ ⟩₂ xs else
                            if ^′ ⇓≟ nm then E⟨^⟩ xs else
                            if -′ ⇓≟ nm then E⟨ quote ⊝_ ⟩₁ xs else
                            Κ′ (def nm xs)
            E (con (quote ℕ.suc) (x ⟨∷⟩ [])) = quote _⊕_ ⟨ con ⟩ E⟅∷⟆ Κ′ (ℕ′ (ℕ.suc ℕ.zero)) ⟨∷⟩ E x ⟨∷⟩ []
            E v@(var x _) = fromMaybe (Κ′ v) (Ι′ x)
            E t = Κ′ t

        callSolver : Vec String numVars → Term → Term → List (Arg Type)
        callSolver nms lhs rhs =
            2 ⋯⟅∷⟆ ring ⟨∷⟩ ℕ′ numVars ⟨∷⟩
            vlams nms (quote _⊜_ ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ ℕ′ numVars ⟨∷⟩ E lhs ⟨∷⟩ E rhs ⟨∷⟩ []) ⟨∷⟩
            hlams nms (quote refl ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ 1 ⋯⟅∷⟆ []) ⟨∷⟩
            []
          where
          Ι′ : ℕ → Maybe Term
          Ι′ i = if i < numVars then just (var i []) else nothing
          open ToExpr Ι′

        constructSoln : Table → Term → Term → Term
        constructSoln t lhs rhs =
            quote trans ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ 3 ⋯⟅∷⟆
              (quote sym ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ 2 ⋯⟅∷⟆
                  (quote Ops.correct ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ 1 ⋯⟅∷⟆ E lhs ⟨∷⟩ ρ ⟨∷⟩ []) ⟨∷⟩ [])
              ⟨∷⟩
              (quote Ops.correct ⟨ def ⟩ 2 ⋯⟅∷⟆ ring ⟨∷⟩ 1 ⋯⟅∷⟆ E rhs ⟨∷⟩ ρ ⟨∷⟩ []) ⟨∷⟩
              []
          where
          Ι′ : ℕ → Maybe Term
          Ι′ i = Maybe.map (λ x → quote Ι ⟨ con ⟩ E⟅∷⟆ Fin′ x ⟨∷⟩ []) (member i t)

          open ToExpr Ι′
          ρ : Term
          ρ = curriedTerm t
open Internal

-- This is the main macro you'll probably be using. Call it like this:
--
--   lemma : ∀ x y → x + y ≈ y + x
--   lemma = solve TypeRing
--
-- where TypRing is your implementation of AlmostCommutativeRing. (Find some
-- example implementations in Polynomial.Solver.Ring.AlmostCommutativeRing.Instances).
macro
  solve : Name → Term → TC ⊤
  solve ring hole = do
    ring′ ← def ring [] ∈Ring
    commitTC
    let open OverRing ring′
    nms ← ring⇓
    hole′ ← inferType hole >>= reduce
    let i , k , xs = underPi hole′
    just (lhs ∷ rhs ∷ []) ← pure (getArgs 2 xs)
      where nothing → typeError (strErr "Malformed call to solve." ∷
                                 strErr "Expected target type to be like: ∀ x y → x + y ≈ y + x." ∷
                                 strErr "Instead: " ∷
                                 termErr hole′ ∷
                                 [])
    unify hole (quote solve-fn ⟨ def ⟩ callSolver nms i k lhs rhs)

-- Use this macro when you want to solve something *under* a lambda. For example:
-- say you have a long proof, and you just want the solver to deal with an
-- intermediate step. Call it like so:
--
--   lemma₃ : ∀ x y → x + y * 1 + 3 ≈ 2 + 1 + y + x
--   lemma₃ x y = begin
--     x + y * 1 + 3 ≈⟨ +-comm x (y * 1) ⟨ +-cong ⟩ refl ⟩
--     y * 1 + x + 3 ≈⟨ solveOver (x ∷ y ∷ []) Int.ring ⟩
--     3 + y + x     ≡⟨ refl ⟩
--     2 + 1 + y + x ∎
--
-- The first argument is the free variables, and the second is the
-- ring implementation (as before).
--
-- One thing to note here is that we need to be able to infer *both* sides of
-- the equality, which the normal equaltional reasoning combinators don't let you
-- do. You'll need the combinators defined in Relation.Binary.Reasoning.Inference.
-- These are just as powerful as the others, but have slightly better inference properties.

solveOver-macro : Term → Name → Term → TC ⊤
solveOver-macro i ring hole = do
  ring′ ← def ring [] ∈Ring
  commitTC
  let open OverRing ring′
  nms ← ring⇓
  i′ ← i ∈List⟨Carrier⟩
  commitTC
  hole′ ← inferType hole >>= reduce
  just vars′ ← pure (vars i′)
    where nothing → typeError (strErr "Malformed call to solveOver." ∷
                                strErr "First argument should be a list of free variables." ∷
                                strErr "Instead: " ∷
                                termErr i′ ∷
                                [])
  just (lhs ∷ rhs ∷ []) ← pure (getArgs 2 hole′)
    where nothing → typeError (strErr "Malformed call to solveOver." ∷
                                strErr "First argument should be a list of free variables." ∷
                                strErr "Instead: " ∷
                                termErr hole′ ∷
                                [])
  unify hole (constructSoln nms (List.length vars′) vars′ lhs rhs)

macro
  solveOver : Term → Name → Term → TC ⊤
  solveOver = solveOver-macro


------------------------------------------------------------------------
-- The Agda standard library
--
-- Sums of binary relations
------------------------------------------------------------------------

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

module Data.Sum.Relation.Binary.LeftOrder where

open import Data.Sum as Sum
open import Data.Sum.Relation.Binary.Pointwise as PW
  using (Pointwise; inj₁; inj₂)
open import Data.Product
open import Data.Empty
open import Function
open import Level
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_)

----------------------------------------------------------------------
-- Definition

infixr 1 _⊎-<_

data _⊎-<_ {a₁ a₂} {A₁ : Set a₁} {A₂ : Set a₂}
           {ℓ₁ ℓ₂} (_∼₁_ : Rel A₁ ℓ₁) (_∼₂_ : Rel A₂ ℓ₂) :
           Rel (A₁ ⊎ A₂) (a₁ ⊔ a₂ ⊔ ℓ₁ ⊔ ℓ₂) where
  ₁∼₂ : ∀ {x y}                 → (_∼₁_ ⊎-< _∼₂_) (inj₁ x) (inj₂ y)
  ₁∼₁ : ∀ {x y} (x∼₁y : x ∼₁ y) → (_∼₁_ ⊎-< _∼₂_) (inj₁ x) (inj₁ y)
  ₂∼₂ : ∀ {x y} (x∼₂y : x ∼₂ y) → (_∼₁_ ⊎-< _∼₂_) (inj₂ x) (inj₂ y)

----------------------------------------------------------------------
-- Some properties which are preserved by _⊎-<_

module _ {a₁ a₂} {A₁ : Set a₁} {A₂ : Set a₂}
         {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂}
         where

  drop-inj₁ : ∀ {x y} → (∼₁ ⊎-< ∼₂) (inj₁ x) (inj₁ y) → ∼₁ x y
  drop-inj₁ (₁∼₁ x∼₁y) = x∼₁y

  drop-inj₂ : ∀ {x y} → (∼₁ ⊎-< ∼₂) (inj₂ x) (inj₂ y) → ∼₂ x y
  drop-inj₂ (₂∼₂ x∼₂y) = x∼₂y

  ⊎-<-refl : Reflexive ∼₁ → Reflexive ∼₂ →
             Reflexive (∼₁ ⊎-< ∼₂)
  ⊎-<-refl refl₁ refl₂ {inj₁ x} = ₁∼₁ refl₁
  ⊎-<-refl refl₁ refl₂ {inj₂ y} = ₂∼₂ refl₂

  ⊎-<-transitive : Transitive ∼₁ → Transitive ∼₂ →
                   Transitive (∼₁ ⊎-< ∼₂)
  ⊎-<-transitive trans₁ trans₂ ₁∼₂        (₂∼₂ x∼₂y)  = ₁∼₂
  ⊎-<-transitive trans₁ trans₂ (₁∼₁ x∼₁y) ₁∼₂         = ₁∼₂
  ⊎-<-transitive trans₁ trans₂ (₁∼₁ x∼₁y) (₁∼₁ x∼₁y₁) = ₁∼₁ (trans₁ x∼₁y x∼₁y₁)
  ⊎-<-transitive trans₁ trans₂ (₂∼₂ x∼₂y) (₂∼₂ x∼₂y₁) = ₂∼₂ (trans₂ x∼₂y x∼₂y₁)

  ⊎-<-asymmetric : Asymmetric ∼₁ → Asymmetric ∼₂ →
                   Asymmetric (∼₁ ⊎-< ∼₂)
  ⊎-<-asymmetric asym₁ asym₂ ₁∼₂ ()
  ⊎-<-asymmetric asym₁ asym₂ (₁∼₁ x∼₁y) (₁∼₁ x∼₁y₁) = asym₁ x∼₁y x∼₁y₁
  ⊎-<-asymmetric asym₁ asym₂ (₂∼₂ x∼₂y) (₂∼₂ x∼₂y₁) = asym₂ x∼₂y x∼₂y₁

  ⊎-<-total : Total ∼₁ → Total ∼₂ → Total (∼₁ ⊎-< ∼₂)
  ⊎-<-total total₁ total₂ = total
    where
    total : Total (_ ⊎-< _)
    total (inj₁ x) (inj₁ y) = Sum.map ₁∼₁ ₁∼₁ $ total₁ x y
    total (inj₁ x) (inj₂ y) = inj₁ ₁∼₂
    total (inj₂ x) (inj₁ y) = inj₂ ₁∼₂
    total (inj₂ x) (inj₂ y) = Sum.map ₂∼₂ ₂∼₂ $ total₂ x y

  ⊎-<-decidable : Decidable ∼₁ → Decidable ∼₂ →
                  Decidable (∼₁ ⊎-< ∼₂)
  ⊎-<-decidable dec₁ dec₂ (inj₁ x) (inj₁ y) = Dec.map′ ₁∼₁ drop-inj₁ (dec₁ x y)
  ⊎-<-decidable dec₁ dec₂ (inj₁ x) (inj₂ y) = yes ₁∼₂
  ⊎-<-decidable dec₁ dec₂ (inj₂ x) (inj₁ y) = no λ()
  ⊎-<-decidable dec₁ dec₂ (inj₂ x) (inj₂ y) = Dec.map′ ₂∼₂ drop-inj₂ (dec₂ x y)

module _ {a₁ a₂} {A₁ : Set a₁} {A₂ : Set a₂}
         {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {≈₁ : Rel A₁ ℓ₂}
         {ℓ₃ ℓ₄} {∼₂ : Rel A₂ ℓ₃} {≈₂ : Rel A₂ ℓ₄}
         where

  ⊎-<-reflexive : ≈₁ ⇒ ∼₁ → ≈₂ ⇒ ∼₂ →
                  (Pointwise ≈₁ ≈₂) ⇒ (∼₁ ⊎-< ∼₂)
  ⊎-<-reflexive refl₁ refl₂ (inj₁ x) = ₁∼₁ (refl₁ x)
  ⊎-<-reflexive refl₁ refl₂ (inj₂ x) = ₂∼₂ (refl₂ x)

  ⊎-<-irreflexive : Irreflexive ≈₁ ∼₁ → Irreflexive ≈₂ ∼₂ →
                    Irreflexive (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-irreflexive irrefl₁ irrefl₂ (inj₁ x) (₁∼₁ x∼₁y) = irrefl₁ x x∼₁y
  ⊎-<-irreflexive irrefl₁ irrefl₂ (inj₂ x) (₂∼₂ x∼₂y) = irrefl₂ x x∼₂y

  ⊎-<-antisymmetric : Antisymmetric ≈₁ ∼₁ → Antisymmetric ≈₂ ∼₂ →
                      Antisymmetric (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-antisymmetric antisym₁ antisym₂ (₁∼₁ x∼₁y) (₁∼₁ x∼₁y₁) = inj₁ (antisym₁ x∼₁y x∼₁y₁)
  ⊎-<-antisymmetric antisym₁ antisym₂ (₂∼₂ x∼₂y) (₂∼₂ x∼₂y₁) = inj₂ (antisym₂ x∼₂y x∼₂y₁)
  -- Remove in Agda 2.6.0?
  ⊎-<-antisymmetric antisym₁ antisym₂ ₁∼₂ ()

  ⊎-<-respectsʳ : ∼₁ Respectsʳ ≈₁ → ∼₂ Respectsʳ ≈₂ →
                  (∼₁ ⊎-< ∼₂) Respectsʳ (Pointwise ≈₁ ≈₂)
  ⊎-<-respectsʳ resp₁ resp₂ (inj₁ x₁) (₁∼₁ x∼₁y) = ₁∼₁ (resp₁ x₁ x∼₁y)
  ⊎-<-respectsʳ resp₁ resp₂ (inj₂ x₁) ₁∼₂        = ₁∼₂
  ⊎-<-respectsʳ resp₁ resp₂ (inj₂ x₁) (₂∼₂ x∼₂y) = ₂∼₂ (resp₂ x₁ x∼₂y)

  ⊎-<-respectsˡ : ∼₁ Respectsˡ ≈₁ → ∼₂ Respectsˡ ≈₂ →
                  (∼₁ ⊎-< ∼₂) Respectsˡ (Pointwise ≈₁ ≈₂)
  ⊎-<-respectsˡ resp₁ resp₂ (inj₁ x) ₁∼₂        = ₁∼₂
  ⊎-<-respectsˡ resp₁ resp₂ (inj₁ x) (₁∼₁ x∼₁y) = ₁∼₁ (resp₁ x x∼₁y)
  ⊎-<-respectsˡ resp₁ resp₂ (inj₂ x) (₂∼₂ x∼₂y) = ₂∼₂ (resp₂ x x∼₂y)

  ⊎-<-respects₂ : ∼₁ Respects₂ ≈₁ → ∼₂ Respects₂ ≈₂ →
                  (∼₁ ⊎-< ∼₂) Respects₂ (Pointwise ≈₁ ≈₂)
  ⊎-<-respects₂ (r₁ , l₁) (r₂ , l₂) = ⊎-<-respectsʳ r₁ r₂ , ⊎-<-respectsˡ l₁ l₂

  ⊎-<-trichotomous : Trichotomous ≈₁ ∼₁ → Trichotomous ≈₂ ∼₂ →
                     Trichotomous (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-trichotomous tri₁ tri₂ (inj₁ x) (inj₂ y) = tri< ₁∼₂ (λ()) (λ())
  ⊎-<-trichotomous tri₁ tri₂ (inj₂ x) (inj₁ y) = tri> (λ()) (λ()) ₁∼₂
  ⊎-<-trichotomous tri₁ tri₂ (inj₁ x) (inj₁ y) with tri₁ x y
  ... | tri< x<y x≉y x≯y = tri< (₁∼₁ x<y) (x≉y ∘ PW.drop-inj₁) (x≯y ∘ drop-inj₁)
  ... | tri≈ x≮y x≈y x≯y = tri≈ (x≮y ∘ drop-inj₁) (inj₁ x≈y) (x≯y ∘ drop-inj₁)
  ... | tri> x≮y x≉y x>y = tri> (x≮y ∘ drop-inj₁) (x≉y ∘ PW.drop-inj₁) (₁∼₁ x>y)
  ⊎-<-trichotomous tri₁ tri₂ (inj₂ x) (inj₂ y) with tri₂ x y
  ... | tri< x<y x≉y x≯y = tri< (₂∼₂ x<y) (x≉y ∘ PW.drop-inj₂) (x≯y ∘ drop-inj₂)
  ... | tri≈ x≮y x≈y x≯y = tri≈ (x≮y ∘ drop-inj₂) (inj₂ x≈y) (x≯y ∘ drop-inj₂)
  ... | tri> x≮y x≉y x>y = tri> (x≮y ∘ drop-inj₂) (x≉y ∘ PW.drop-inj₂) (₂∼₂ x>y)

----------------------------------------------------------------------
-- Some collections of properties which are preserved

module _ {a₁ a₂} {A₁ : Set a₁} {A₂ : Set a₂}
         {ℓ₁ ℓ₂} {≈₁ : Rel A₁ ℓ₁} {∼₁ : Rel A₁ ℓ₂}
         {ℓ₃ ℓ₄} {≈₂ : Rel A₂ ℓ₃} {∼₂ : Rel A₂ ℓ₄} where

  ⊎-<-isPreorder : IsPreorder ≈₁ ∼₁ → IsPreorder ≈₂ ∼₂ →
                   IsPreorder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isPreorder pre₁ pre₂ = record
    { isEquivalence = PW.⊎-isEquivalence (isEquivalence pre₁) (isEquivalence pre₂)
    ; reflexive     = ⊎-<-reflexive (reflexive pre₁) (reflexive pre₂)
    ; trans         = ⊎-<-transitive (trans pre₁) (trans pre₂)
    }
    where open IsPreorder

  ⊎-<-isPartialOrder : IsPartialOrder ≈₁ ∼₁ →
                       IsPartialOrder ≈₂ ∼₂ →
                       IsPartialOrder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isPartialOrder po₁ po₂ = record
    { isPreorder = ⊎-<-isPreorder (isPreorder po₁) (isPreorder po₂)
    ; antisym    = ⊎-<-antisymmetric (antisym po₁) (antisym    po₂)
    }
    where open IsPartialOrder

  ⊎-<-isStrictPartialOrder : IsStrictPartialOrder ≈₁ ∼₁ →
                             IsStrictPartialOrder ≈₂ ∼₂ →
                             IsStrictPartialOrder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isStrictPartialOrder spo₁ spo₂ = record
    { isEquivalence = PW.⊎-isEquivalence (isEquivalence spo₁) (isEquivalence spo₂)
    ; irrefl        = ⊎-<-irreflexive   (irrefl spo₁) (irrefl   spo₂)
    ; trans         = ⊎-<-transitive    (trans spo₁)  (trans    spo₂)
    ; <-resp-≈      = ⊎-<-respects₂   (<-resp-≈ spo₁) (<-resp-≈ spo₂)
    }
    where open IsStrictPartialOrder

  ⊎-<-isTotalOrder : IsTotalOrder ≈₁ ∼₁ →
                     IsTotalOrder ≈₂ ∼₂ →
                     IsTotalOrder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isTotalOrder to₁ to₂ = record
    { isPartialOrder = ⊎-<-isPartialOrder (isPartialOrder to₁) (isPartialOrder to₂)
    ; total          = ⊎-<-total (total to₁) (total to₂)
    }
    where open IsTotalOrder

  ⊎-<-isDecTotalOrder : IsDecTotalOrder ≈₁ ∼₁ →
                        IsDecTotalOrder ≈₂ ∼₂ →
                        IsDecTotalOrder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isDecTotalOrder to₁ to₂ = record
    { isTotalOrder = ⊎-<-isTotalOrder (isTotalOrder to₁) (isTotalOrder to₂)
    ; _≟_          = PW.⊎-decidable (_≟_  to₁) (_≟_  to₂)
    ; _≤?_         = ⊎-<-decidable (_≤?_ to₁) (_≤?_ to₂)
    }
    where open IsDecTotalOrder

  ⊎-<-isStrictTotalOrder : IsStrictTotalOrder ≈₁ ∼₁ →
                           IsStrictTotalOrder ≈₂ ∼₂ →
                           IsStrictTotalOrder (Pointwise ≈₁ ≈₂) (∼₁ ⊎-< ∼₂)
  ⊎-<-isStrictTotalOrder sto₁ sto₂ = record
    { isEquivalence = PW.⊎-isEquivalence (isEquivalence sto₁) (isEquivalence sto₂)
    ; trans         = ⊎-<-transitive (trans sto₁) (trans sto₂)
    ; compare       = ⊎-<-trichotomous (compare sto₁) (compare sto₂)
    }
    where open IsStrictTotalOrder

------------------------------------------------------------------------
-- "Packages" can also be combined.

module _ {a b c d e f} where

  ⊎-<-preorder : Preorder a b c →
                 Preorder d e f →
                 Preorder _ _ _
  ⊎-<-preorder p₁ p₂ = record
    { isPreorder   =
        ⊎-<-isPreorder (isPreorder p₁) (isPreorder p₂)
    } where open Preorder

  ⊎-<-poset : Poset a b c →
              Poset a b c →
              Poset _ _ _
  ⊎-<-poset po₁ po₂ = record
    { isPartialOrder =
        ⊎-<-isPartialOrder (isPartialOrder po₁) (isPartialOrder po₂)
    } where open Poset

  ⊎-<-strictPartialOrder : StrictPartialOrder a b c →
                           StrictPartialOrder d e f →
                           StrictPartialOrder _ _ _
  ⊎-<-strictPartialOrder spo₁ spo₂ = record
    { isStrictPartialOrder =
      ⊎-<-isStrictPartialOrder (isStrictPartialOrder spo₁) (isStrictPartialOrder spo₂)
    } where open StrictPartialOrder

  ⊎-<-totalOrder : TotalOrder a b c →
                   TotalOrder d e f →
                   TotalOrder _ _ _
  ⊎-<-totalOrder to₁ to₂ = record
    { isTotalOrder = ⊎-<-isTotalOrder (isTotalOrder to₁) (isTotalOrder to₂)
    } where open TotalOrder

  ⊎-<-decTotalOrder : DecTotalOrder a b c →
                      DecTotalOrder d e f →
                      DecTotalOrder _ _ _
  ⊎-<-decTotalOrder to₁ to₂ = record
    { isDecTotalOrder = ⊎-<-isDecTotalOrder (isDecTotalOrder to₁) (isDecTotalOrder to₂)
    } where open DecTotalOrder

  ⊎-<-strictTotalOrder : StrictTotalOrder a b c →
                         StrictTotalOrder a b c →
                         StrictTotalOrder _ _ _
  ⊎-<-strictTotalOrder sto₁ sto₂ = record
    { isStrictTotalOrder = ⊎-<-isStrictTotalOrder (isStrictTotalOrder sto₁) (isStrictTotalOrder sto₂)
    } where open StrictTotalOrder


module bool-to-string where

open import bool
open import string

𝔹-to-string : 𝔹 → string
𝔹-to-string tt = "tt"
𝔹-to-string ff = "ff"


open import Formalization.PredicateLogic.Signature

module Formalization.PredicateLogic.Minimal.NaturalDeduction.NegativeTranslations (𝔏 : Signature) where
open Signature(𝔏)

open import Data.Either as Either using (Left ; Right)
open import Data.ListSized using (List)
import      Logic.Propositional as Meta
import      Logic.Predicate as Meta
import      Lvl
open import Formalization.PredicateLogic.Minimal.NaturalDeduction (𝔏)
import      Formalization.PredicateLogic.Classical.NaturalDeduction
private module Classical = Formalization.PredicateLogic.Classical.NaturalDeduction (𝔏)
open import Formalization.PredicateLogic.Syntax(𝔏)
open import Formalization.PredicateLogic.Syntax.NegativeTranslations(𝔏)
open import Formalization.PredicateLogic.Syntax.Substitution(𝔏)
open import Functional using (_∘_ ; _∘₂_ ; _∘₃_ ; _∘₄_ ; swap ; _←_)
open import Numeral.Finite
open import Numeral.Natural
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Sets.PredicateSet using (PredSet ; _∈_ ; _∉_ ; _∪_ ; _∪•_ ; _∖_ ; _⊆_ ; _⊇_ ; ∅ ; [≡]-to-[⊆] ; [≡]-to-[⊇] ; map ; unmap) renaming (•_ to · ; _≡_ to _≡ₛ_)
open import Structure.Relator
open import Type

-- TODO: Move this module
module _ where
  private variable ℓ : Lvl.Level
  private variable T A B : Type{ℓ}
  private variable S S₁ S₂ : PredSet{ℓ}(T)
  private variable f : A → B
  private variable x : T

  postulate map-preserves-union : (map f(S₁ ∪ S₂) ⊆ ((map f(S₁)) ∪ (map f(S₂))))

  postulate map-preserves-singleton : (map f(S₁ ∪ S₂) ⊆ ((map f(S₁)) ∪ (map f(S₂))))

  postulate map-preserves-union-singleton : (map f(S ∪ · x) ⊆ ((map f(S)) ∪ ·(f(x))))

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level
private variable args n vars : ℕ
private variable Γ Γ₁ Γ₂ : PredSet{ℓ}(Formula(vars))
private variable φ ψ γ φ₁ ψ₁ γ₁ φ₂ ψ₂ γ₂ φ₃ ψ₃ φ₄ ψ₄ φ₅ ψ₅ δ₁ δ₂ : Formula(vars)
private variable p : Prop(args)
private variable x : List(Term(vars))(args)

[⊢]-functionₗ : (Γ₁ ≡ₛ Γ₂) → ((Γ₁ ⊢_) ≡ₛ (Γ₂ ⊢_))
[⊢]-functionₗ Γ₁Γ₂ = Meta.[↔]-intro (weaken (Meta.[↔]-to-[←] Γ₁Γ₂)) (weaken (Meta.[↔]-to-[→] Γ₁Γ₂))

[⊢][→]-elim : ((Γ ∪ · φ) ⊢ ψ) → ((Γ ⊢ φ) → (Γ ⊢ ψ))
[⊢][→]-elim Γφψ Γφ = [∨]-elim Γφψ Γφψ ([∨]-introₗ Γφ)

[⟶]-intro-inverse : (Γ ⊢ (φ ⟶ ψ)) → ((Γ ∪ · φ) ⊢ ψ)
[⟶]-intro-inverse p = [⟶]-elim (direct (Right [≡]-intro)) (weaken-union p)

weaken-closure-union : (((Γ₁ ⊢_) ∪ Γ₂) ⊢ φ) → ((((Γ₁ ∪ Γ₂) ⊢_)) ⊢ φ)
weaken-closure-union {Γ₁ = Γ₁}{Γ₂ = Γ₂}{φ = φ} = weaken sub where
  sub : ((Γ₁ ⊢_) ∪ Γ₂) ⊆ ((Γ₁ ∪ Γ₂) ⊢_)
  sub (Left  p) = weaken-union p
  sub (Right p) = direct (Right p)

{-
assume-closure : ((Γ ⊢_) ⊢ φ) → (Γ ⊢ φ)
assume-closure (direct     p)     = p
assume-closure [⊤]-intro          = [⊤]-intro
assume-closure ([∧]-intro  p q)   = [∧]-intro (assume-closure p) (assume-closure q)
assume-closure ([∧]-elimₗ  p)     = [∧]-elimₗ (assume-closure p)
assume-closure ([∧]-elimᵣ  p)     = [∧]-elimᵣ (assume-closure p)
assume-closure ([∨]-introₗ p)     = [∨]-introₗ (assume-closure p)
assume-closure ([∨]-introᵣ p)     = [∨]-introᵣ (assume-closure p)
assume-closure ([∨]-elim   p q r) = [∨]-elim {!assume-closure(weaken-closure-union p)!} ([⟶]-elim (direct(Right [≡]-intro)) {!!}) (assume-closure r)
assume-closure ([⟶]-intro  p)     = [⟶]-intro (assume-closure {!weaken-closure-union p!})
assume-closure ([⟶]-elim   p q)   = [⟶]-elim (assume-closure p) (assume-closure q)
assume-closure ([Ɐ]-intro  p)     = [Ɐ]-intro (assume-closure p)
assume-closure ([Ɐ]-elim   p)     = [Ɐ]-elim (assume-closure p)
assume-closure ([∃]-intro  p)     = [∃]-intro (assume-closure p)
assume-closure ([∃]-elim   p q)   = [∃]-elim (assume-closure {!!}) (assume-closure q)
-}

-- 2.1.8B1
[¬¬]-intro-[⟶] : (Γ ⊢ (φ ⟶ (¬¬ φ)))
[¬¬]-intro-[⟶] = [⟶]-intro ([¬¬]-intro (direct (Right [≡]-intro)))

-- 2.1.8.A1
[⟶]-const : Γ ⊢ (φ ⟶ ψ ⟶ φ)
[⟶]-const = [⟶]-intro ([⟶]-intro (direct (Left (Right [≡]-intro))))

[⟶]-refl : Γ ⊢ (φ ⟶ φ)
[⟶]-refl = [⟶]-intro (direct (Right [≡]-intro))

[⟷]-refl : Γ ⊢ (φ ⟷ φ)
[⟷]-refl = [⟷]-intro (direct (Right [≡]-intro)) (direct (Right [≡]-intro))

[⟶]-trans : (Γ ⊢ ((φ ⟶ ψ) ⟶ (ψ ⟶ γ) ⟶ (φ ⟶ γ)))
[⟶]-trans = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) (direct (Left (Left (Right [≡]-intro))))) (direct (Left (Right [≡]-intro))))))

[⟶]-contrapositiveᵣ : (Γ ⊢ (φ ⟶ ψ) ⟶ ((¬ φ) ⟵ (¬ ψ)))
[⟶]-contrapositiveᵣ = [⟶]-trans

[⟶]-double-contrapositiveᵣ : (Γ ⊢ (φ ⟶ ψ) ⟶ ((¬¬ φ) ⟶ (¬¬ ψ)))
[⟶]-double-contrapositiveᵣ = [⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [⟶]-contrapositiveᵣ) [⟶]-contrapositiveᵣ)

[⟶]-elim₂ : (Γ ⊢ φ₁) → (Γ ⊢ φ₂) → (Γ ⊢ (φ₁ ⟶ φ₂ ⟶ ψ)) → (Γ ⊢ ψ)
[⟶]-elim₂ = swap(_∘_) [⟶]-elim ∘ (swap(_∘_) ∘ [⟶]-elim)

[⟶]-elim₃ : (Γ ⊢ φ₁) → (Γ ⊢ φ₂) → (Γ ⊢ φ₃) → (Γ ⊢ (φ₁ ⟶ φ₂ ⟶ φ₃ ⟶ ψ)) → (Γ ⊢ ψ)
[⟶]-elim₃ = swap(_∘_) [⟶]-elim ∘₂ (swap(_∘_) ∘₂ [⟶]-elim₂)

[⟶]-elim₄ : (Γ ⊢ φ₁) → (Γ ⊢ φ₂) → (Γ ⊢ φ₃) → (Γ ⊢ φ₄) → (Γ ⊢ (φ₁ ⟶ φ₂ ⟶ φ₃ ⟶ φ₄ ⟶ ψ)) → (Γ ⊢ ψ)
[⟶]-elim₄ = swap(_∘_) [⟶]-elim ∘₃ (swap(_∘_) ∘₃ [⟶]-elim₃)

[⟶]-elim₅ : (Γ ⊢ φ₁) → (Γ ⊢ φ₂) → (Γ ⊢ φ₃) → (Γ ⊢ φ₄) → (Γ ⊢ φ₅) → (Γ ⊢ (φ₁ ⟶ φ₂ ⟶ φ₃ ⟶ φ₄ ⟶ φ₅ ⟶ ψ)) → (Γ ⊢ ψ)
[⟶]-elim₅ = swap(_∘_) [⟶]-elim ∘₄ (swap(_∘_) ∘₄ [⟶]-elim₄)

-- 2.1.8B2
[¬¬¬]-elim : Γ ⊢ ((¬ ¬ ¬ φ) ⟶ (¬ φ))
[¬¬¬]-elim = [⟶]-intro ([⟶]-intro ([⟶]-elim ([¬¬]-intro (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))

[∨]-introₗ-by-[¬∧] : Γ ⊢ ((¬ φ) ⟶ ¬(φ ∧ ψ))
[∨]-introₗ-by-[¬∧] = [⟶]-intro ([⟶]-intro ([⟶]-elim ([∧]-elimₗ (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))

[∨]-introᵣ-by-[¬∧] : Γ ⊢ ((¬ ψ) ⟶ ¬(φ ∧ ψ))
[∨]-introᵣ-by-[¬∧] = [⟶]-intro ([⟶]-intro ([⟶]-elim ([∧]-elimᵣ (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))

[∨]-introₗ-by-[¬¬∧¬] : Γ ⊢ (φ ⟶ ¬((¬ φ) ∧ (¬ ψ)))
[∨]-introₗ-by-[¬¬∧¬] = [⟶]-intro ([⟶]-intro ([⟶]-elim (direct (Left (Right [≡]-intro))) ([∧]-elimₗ (direct (Right [≡]-intro)))))

[∨]-introᵣ-by-[¬¬∧¬] : Γ ⊢ (ψ ⟶ ¬((¬ φ) ∧ (¬ ψ)))
[∨]-introᵣ-by-[¬¬∧¬] = [⟶]-intro ([⟶]-intro ([⟶]-elim (direct (Left (Right [≡]-intro))) ([∧]-elimᵣ (direct (Right [≡]-intro)))))

[∃]-intro-by-[¬Ɐ] : ∀{t} → (Γ ⊢ ((¬(substitute0 t φ)) ⟶ ¬(Ɐ φ)))
[∃]-intro-by-[¬Ɐ] = [⟶]-intro ([⟶]-intro ([⟶]-elim ([Ɐ]-elim (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))

[∃]-intro-by-[¬Ɐ¬] : ∀{t} → (Γ ⊢ ((substitute0 t φ) ⟶ ¬(Ɐ(¬ φ))))
[∃]-intro-by-[¬Ɐ¬] = [⟶]-intro ([⟶]-intro ([⟶]-elim (direct (Left (Right [≡]-intro))) ([Ɐ]-elim (direct (Right [≡]-intro)))))

[∧]-map : Γ ⊢ ((φ₁ ⟶ φ₂) ⟶ (ψ₁ ⟶ ψ₂) ⟶ ((φ₁ ∧ ψ₁) ⟶ (φ₂ ∧ ψ₂)))
[∧]-map = [⟶]-intro ([⟶]-intro ([⟶]-intro ([∧]-intro ([⟶]-elim ([∧]-elimₗ (direct (Right [≡]-intro))) (direct (Left (Left (Right [≡]-intro))))) ([⟶]-elim ([∧]-elimᵣ (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))))

[∨]-map : Γ ⊢ ((φ₁ ⟶ φ₂) ⟶ (ψ₁ ⟶ ψ₂) ⟶ ((φ₁ ∨ ψ₁) ⟶ (φ₂ ∨ ψ₂)))
[∨]-map = [⟶]-intro ([⟶]-intro ([⟶]-intro ([∨]-elim ([∨]-introₗ ([⟶]-intro-inverse (direct (Left (Left (Right [≡]-intro)))))) ([∨]-introᵣ ([⟶]-intro-inverse (direct (Left (Right [≡]-intro))))) (direct (Right [≡]-intro)))))

[Ɐ]-map : Γ ⊢ (Ɐ(φ₁ ⟶ φ₂) ⟶ ((Ɐ φ₁) ⟶ (Ɐ φ₂)))
[Ɐ]-map = [⟶]-intro ([⟶]-intro ([Ɐ]-intro ([⟶]-elim ([Ɐ]-elim (direct (Right [≡]-intro))) ([Ɐ]-elim(direct (Left (Right [≡]-intro)))))))

[⟶]-map : Γ ⊢ ((φ₁ ⟵ φ₂) ⟶ (ψ₁ ⟶ ψ₂) ⟶ ((φ₁ ⟶ ψ₁) ⟶ (φ₂ ⟶ ψ₂)))
[⟶]-map = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) (direct (Left (Left (Left (Right [≡]-intro)))))) (direct (Left (Right [≡]-intro)))) (direct (Left (Left (Right [≡]-intro))))))))

[⟶]-map₂ : Γ ⊢ ((φ₁ ⟵ φ₂) ⟶ (ψ₁ ⟵ ψ₂) ⟶ (γ₁ ⟶ γ₂) ⟶ ((φ₁ ⟶ ψ₁ ⟶ γ₁) ⟶ (φ₂ ⟶ ψ₂ ⟶ γ₂)))
[⟶]-map₂ = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim₂ (direct (Left (Left (Right [≡]-intro)))) ([⟶]-elim₂ (direct (Left (Right [≡]-intro))) (direct (Right [≡]-intro)) [⟶]-map) [⟶]-map)))

[⟶]-map₃ : Γ ⊢ ((φ₁ ⟵ φ₂) ⟶ (ψ₁ ⟵ ψ₂) ⟶ (γ₁ ⟵ γ₂) ⟶ (δ₁ ⟶ δ₂) ⟶ ((φ₁ ⟶ ψ₁ ⟶ γ₁ ⟶ δ₁) ⟶ (φ₂ ⟶ ψ₂ ⟶ γ₂ ⟶ δ₂)))
[⟶]-map₃ = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim₃ (direct (Left (Left (Left (Right [≡]-intro))))) (direct (Left (Left (Right [≡]-intro)))) ([⟶]-elim₂ (direct (Left (Right [≡]-intro))) (direct (Right [≡]-intro)) [⟶]-map) [⟶]-map₂))))

[¬]-map : Γ ⊢ ((φ ⟵ ψ) ⟶ (¬ φ) ⟶ (¬ ψ))
[¬]-map = [⟶]-contrapositiveᵣ

[⟶]-trans₂ : (Γ ⊢ ((φ₁ ⟶ φ₂ ⟶ ψ) ⟶ (ψ ⟶ γ) ⟶ (φ₁ ⟶ φ₂ ⟶ γ)))
[⟶]-trans₂ = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim ([⟶]-elim₂ (direct (Left (Right [≡]-intro))) (direct (Right [≡]-intro)) (direct (Left (Left (Left (Right [≡]-intro)))))) (direct (Left (Left (Right [≡]-intro))))))))

-- 2.1.8B3
[¬¬]-preserve-[⟶] : Γ ⊢ (¬¬(φ ⟶ ψ) ⟶ ((¬¬ φ) ⟶ (¬¬ ψ)))
[¬¬]-preserve-[⟶] = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Left (Left (Right [≡]-intro)))) ([⟶]-elim (direct(Right [≡]-intro)) [⟶]-double-contrapositiveᵣ)) ([¬¬]-intro (direct (Left (Right [≡]-intro)))))) (direct (Left (Left (Right [≡]-intro)))))))

-- 2.1.8B4
[¬¬]-preserve-[∧] : Γ ⊢ (¬¬(φ ∧ ψ) ⟷ ((¬¬ φ) ∧ (¬¬ ψ)))
[¬¬]-preserve-[∧] {Γ = Γ}{φ = φ} =
  [⟷]-intro
    ([⟶]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([∧]-intro (direct (Left (Right [≡]-intro))) (direct (Right [≡]-intro))) (direct (Left (Left (Right [≡]-intro)))))) ([∧]-elimᵣ (direct (Left (Left (Right [≡]-intro))))))) ([∧]-elimₗ (direct (Left (Right [≡]-intro))))))
    ([∧]-intro
      ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∨]-introₗ-by-[¬∧]) (direct (Left (Right [≡]-intro)))))
      ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∨]-introᵣ-by-[¬∧]) (direct (Left (Right [≡]-intro)))))
    )

-- 2.1.8B5
[¬]-preserve-[∨][∧] : Γ ⊢ (¬(φ ∨ ψ) ⟷ ((¬ φ) ∧ (¬ ψ)))
[¬]-preserve-[∨][∧] =
  [⟷]-intro
    ([⟶]-intro ([∨]-elim
      ([⟶]-elim (direct (Right [≡]-intro)) ([∧]-elimₗ (direct (Left (Left (Right [≡]-intro))))))
      ([⟶]-elim (direct (Right [≡]-intro)) ([∧]-elimᵣ (direct (Left (Left (Right [≡]-intro))))))
      (direct (Right [≡]-intro))
    ))
    ([∧]-intro
      ([⟶]-intro ([⟶]-elim ([∨]-introₗ (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))
      ([⟶]-intro ([⟶]-elim ([∨]-introᵣ (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro)))))
    )

-- 2.1.8B6
[¬¬]-preserve-[Ɐ] : Γ ⊢ (¬¬(Ɐ φ) ⟶ (Ɐ(¬¬ φ)))
[¬¬]-preserve-[Ɐ] = [⟶]-intro ([Ɐ]-intro ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∃]-intro-by-[¬Ɐ]) (direct (Left (Right [≡]-intro))))))

[¬]-preserve-[∃][Ɐ] : Γ ⊢ (¬(∃ φ) ⟷ (Ɐ(¬ φ)))
[¬]-preserve-[∃][Ɐ] =
  [⟷]-intro
    ([⟶]-intro ([∃]-elim ([⟶]-intro-inverse ([Ɐ]-elim (direct (Left (Right [≡]-intro))))) (direct (Right [≡]-intro))))
    ([Ɐ]-intro ([⟶]-intro ([⟶]-elim ([∃]-intro (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro))))))

open import Lang.Instance

-- 2.3.1
data NegativeFragment : Formula(vars) → Type{ℓₚ Lvl.⊔ ℓₒ} where
  atom   : NegativeFragment(¬(p $ x))
  bottom : NegativeFragment{vars}(⊥)
  top    : NegativeFragment{vars}(⊤)
  and    : NegativeFragment(φ) → NegativeFragment(ψ) → NegativeFragment(φ ∧ ψ)
  impl   : NegativeFragment(φ) → NegativeFragment(ψ) → NegativeFragment(φ ⟶ ψ)
  all    : (∀{t} → NegativeFragment(substitute0 t φ)) → NegativeFragment(Ɐ φ)
pattern neg p = NegativeFragment.impl p bottom
instance _ = \{vars} {p}{args}{x} → atom{vars}{p = p}{args}{x = x}
instance _ = \{vars} → bottom{vars}
instance _ = \{vars} → top{vars}
instance _ = \{vars} {φ} ⦃ neg-φ ⦄ {ψ} ⦃ neg-ψ ⦄ → and{vars}{φ = φ}{ψ = ψ} neg-φ neg-ψ
instance _ = \{vars} {φ} ⦃ neg-φ ⦄ {ψ} ⦃ neg-ψ ⦄ → impl{vars}{φ = φ}{ψ = ψ} neg-φ neg-ψ
instance _ = \{vars} {φ} ⦃ neg-φ : ∀{_} → _ ⦄ → all{vars}{φ = φ} (\{t} → neg-φ{t})

-- 2.3.2
[¬¬]-elim-on-negativeFragment : NegativeFragment(φ) → (Γ ⊢ ((¬¬ φ) ⟶ φ))
[¬¬]-elim-on-negativeFragment atom             = [¬¬¬]-elim
[¬¬]-elim-on-negativeFragment bottom           = [¬¬]-intro [⟶]-refl
[¬¬]-elim-on-negativeFragment top              = [⟶]-intro [⊤]-intro
[¬¬]-elim-on-negativeFragment (and  negφ negψ) =
  [⟶]-intro ([∧]-intro
    ([⟶]-elim ([∧]-elimₗ([⟷]-elimᵣ (direct (Right [≡]-intro)) [¬¬]-preserve-[∧])) ([¬¬]-elim-on-negativeFragment negφ))
    ([⟶]-elim ([∧]-elimᵣ([⟷]-elimᵣ (direct (Right [≡]-intro)) [¬¬]-preserve-[∧])) ([¬¬]-elim-on-negativeFragment negψ))
  )
[¬¬]-elim-on-negativeFragment (impl negφ negψ) =
  [⟶]-intro ([⟶]-intro ([⟶]-elim
    ([⟶]-elim₂ (direct (Left (Right [≡]-intro))) ([¬¬]-intro (direct (Right [≡]-intro))) [¬¬]-preserve-[⟶])
    ([¬¬]-elim-on-negativeFragment negψ)
  ))
[¬¬]-elim-on-negativeFragment (all  negφ)      = [⟶]-intro ([Ɐ]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∃]-intro-by-[¬Ɐ]) (direct (Left (Right [≡]-intro))))) ([¬¬]-elim-on-negativeFragment negφ)))

Stable : ∀{ℓ}{vars} → Formula(vars) → Type
Stable{ℓ = ℓ} (φ) = ∀{Γ : PredSet{ℓ}(_)} → (Γ ⊢ (¬¬ φ ⟶ φ))

[¬]-stability : Stable{ℓ}(¬ φ)
[¬]-stability = [¬¬¬]-elim

-- [∨]-stability : Stable(φ) → Stable(ψ) → Stable{ℓ}(φ ∨ ψ)
-- [∨]-stability sφ sψ = [⟶]-intro ([∨]-elim ([∨]-introₗ {!!}) ([∨]-introᵣ {!!}) {!!})

[∨]-elim-by-[¬∧] : Stable(φ) → Stable(ψ) → Stable(γ) → (Γ ⊢ (((¬ φ) ⟶ γ) ⟶ ((¬ ψ) ⟶ γ) ⟶ ¬(φ ∧ ψ) ⟶ γ))
[∨]-elim-by-[¬∧] negφ negψ negγ = [⟶]-intro ([⟶]-intro ([⟶]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([⟶]-elim₃ negφ negψ ([∧]-intro ([⟶]-elim (direct (Right [≡]-intro)) ([⟶]-elim (direct (Left (Left (Left (Right [≡]-intro))))) [⟶]-contrapositiveᵣ)) ([⟶]-elim (direct (Right [≡]-intro)) ([⟶]-elim (direct (Left (Left (Right [≡]-intro)))) [⟶]-contrapositiveᵣ))) [∧]-map) (direct (Left (Right [≡]-intro))))) negγ)))

[∨]-elim-by-[¬¬∧¬] : (∀{ℓ} → Stable{ℓ}(γ)) → (Γ ⊢ ((φ ⟶ γ) ⟶ (ψ ⟶ γ) ⟶ ¬((¬ φ) ∧ (¬ ψ)) ⟶ γ))
[∨]-elim-by-[¬¬∧¬] negγ = [⟶]-elim₅ (pp negγ) (pp negγ) [⟶]-refl [⟶]-refl ([∨]-elim-by-[¬∧] [¬]-stability [¬]-stability negγ) [⟶]-map₃ where
  pp : Stable(ψ) → (Γ ⊢ ((φ ⟶ ψ) ⟶ ((¬¬ φ) ⟶ ψ)))
  pp negψ = [⟶]-elim₂ [⟶]-double-contrapositiveᵣ negψ [⟶]-trans₂

[⟷]-to-[⟵] : (Γ ⊢ (φ ⟷ ψ)) → (Γ ⊢ (φ ⟵ ψ))
[⟷]-to-[⟵] p = [⟶]-intro ([⟷]-elimₗ (direct (Right [≡]-intro)) (weaken-union p))

[⟷]-to-[⟶] : (Γ ⊢ (φ ⟷ ψ)) → (Γ ⊢ (φ ⟶ ψ))
[⟷]-to-[⟶] p = [⟶]-intro ([⟷]-elimᵣ (direct (Right [≡]-intro)) (weaken-union p))

[Ɐ][→]-distributivity : Γ ⊢ (Ɐ(φ ⟶ ψ) ⟶ (Ɐ φ) ⟶ (Ɐ ψ))
[Ɐ][→]-distributivity = [⟶]-intro ([⟶]-intro ([Ɐ]-intro ([⟶]-elim ([Ɐ]-elim (direct (Right [≡]-intro))) ([Ɐ]-elim (direct (Left (Right [≡]-intro)))))))

[¬∃]-to-[∀¬] : Γ ⊢ (¬(∃ φ) ⟶ Ɐ(¬ φ))
[¬∃]-to-[∀¬] = [⟶]-intro ([Ɐ]-intro ([⟶]-intro ([⟶]-elim ([∃]-intro (direct (Right [≡]-intro))) (direct (Left (Right [≡]-intro))))))

[¬Ɐ]-to-[∃¬] : (∀{t} → Stable(substitute0 t φ)) → Stable(∃(¬ φ)) → (Γ ⊢ (¬(Ɐ φ) ⟶ ∃(¬ φ)))
[¬Ɐ]-to-[∃¬] negφ nege = [⟶]-intro ([⟶]-elim ([⟶]-intro ([⟶]-elim ([⟶]-elim₂ ([Ɐ]-intro negφ) ([⟶]-elim (direct (Right [≡]-intro)) [¬∃]-to-[∀¬]) [Ɐ][→]-distributivity) (direct (Left (Right [≡]-intro))))) nege)

{-
test : (Γ ⊢ (¬¬ φ ⟶ φ)) → (Γ ⊢ (¬ Ɐ(¬ φ))) → (Γ ⊢ ∃ φ)
test negφ p = [∃]-elim ([∃]-intro {![⟶]-intro negφ!}) ([⟶]-elim p test2)
-}

[∃]-elim-by-[¬¬∧¬] : (∀{t} → (Γ ∪ ·(substitute0 t φ)) ⊢ ψ) → (Γ ⊢ ¬(Ɐ(¬ φ))) → (Γ ⊢ ψ)
[∃]-elim-by-[¬¬∧¬] p q = [∃]-elim p {!!}

-- test : ∀{t} → (Γ₁ ≡ₛ Γ₂) → (Γ₁ ⊢ φ) → (Γ₂ ⊢ substitute0 t φ)
-- test p = {!p!}

{-
test : ∀{t} → (∀{Γ : PredSet{ℓ}(Formula(𝐒(vars)))} → (Γ ⊢ φ)) → (∀{Γ : PredSet{ℓ}(Formula(vars))} → (Γ ⊢ substitute0 t φ))
test {t = t} Γφ {Γ} with Γφ{unmap(substitute0 t) Γ}
... | direct     p     = direct p
... | [⊤]-intro        = [⊤]-intro
... | [∧]-intro  p q   = [∧]-intro {!test p!} {!!}
... | [∧]-elimₗ  p     = {!!}
... | [∧]-elimᵣ  p     = {!!}
... | [∨]-introₗ p     = {!!}
... | [∨]-introᵣ p     = {!!}
... | [∨]-elim   p q r = {!!}
... | [⟶]-intro  p     = {!!}
... | [⟶]-elim   p q   = {!!}
... | [Ɐ]-intro  p     = {!!}
... | [Ɐ]-elim   p     = {!!}
... | [∃]-intro  p     = {!!}
... | [∃]-elim   p q   = {!!}
-}

substitute0-negativeFragment : NegativeFragment(φ) → ∀{t} → NegativeFragment(substitute0 t φ)
substitute0-negativeFragment atom       = atom
substitute0-negativeFragment bottom     = bottom
substitute0-negativeFragment top        = top
substitute0-negativeFragment (and  p q) = and (substitute0-negativeFragment p) (substitute0-negativeFragment q)
substitute0-negativeFragment (impl p q) = impl (substitute0-negativeFragment p) (substitute0-negativeFragment q)
substitute0-negativeFragment (all  p)   = {!!} -- all (substitute0-negativeFragment p)

ggTrans-negativeFragment : NegativeFragment(ggTrans(φ))
ggTrans-negativeFragment {φ = p $ x} = neg atom
ggTrans-negativeFragment {φ = ⊤}     = top
ggTrans-negativeFragment {φ = ⊥}     = bottom
ggTrans-negativeFragment {φ = φ ∧ ψ} = and ggTrans-negativeFragment ggTrans-negativeFragment
ggTrans-negativeFragment {φ = φ ∨ ψ} = neg(and(neg ggTrans-negativeFragment) (neg ggTrans-negativeFragment))
ggTrans-negativeFragment {φ = φ ⟶ ψ} = impl ggTrans-negativeFragment ggTrans-negativeFragment
ggTrans-negativeFragment {φ = Ɐ φ}   = all (substitute0-negativeFragment (ggTrans-negativeFragment {φ = φ}))
ggTrans-negativeFragment {φ = ∃ φ}   = neg (all (neg (substitute0-negativeFragment (ggTrans-negativeFragment {φ = φ}))))

-- [¬¬]-elim-of-koTrans : (Γ ⊢ (¬¬ ggTrans(φ))) ← (Γ ⊢ (ggTrans φ))

ggTrans-substitute0 : ∀{t} → (ggTrans(substitute0 t φ) ≡ substitute0 t (ggTrans φ))
ggTrans-substitute0 {φ = P $ x} = [≡]-intro
ggTrans-substitute0 {φ = ⊤}     = [≡]-intro
ggTrans-substitute0 {φ = ⊥}     = [≡]-intro
ggTrans-substitute0 {φ = φ ∧ ψ}{t}
  rewrite ggTrans-substitute0 {φ = φ}{t}
  rewrite ggTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
ggTrans-substitute0 {φ = φ ∨ ψ}{t}
  rewrite ggTrans-substitute0 {φ = φ}{t}
  rewrite ggTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
ggTrans-substitute0 {φ = φ ⟶ ψ}{t}
  rewrite ggTrans-substitute0 {φ = φ}{t}
  rewrite ggTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
ggTrans-substitute0 {φ = Ɐ φ}{t} = {!!}
  -- rewrite ggTrans-substitute0 {φ = φ}{termVar𝐒 t}
  -- = [≡]-intro
ggTrans-substitute0 {φ = ∃ φ}{t} = {!!}
  -- rewrite ggTrans-substitute0 {φ = φ}{termVar𝐒 t}
  -- = [≡]-intro

koTrans-substitute0 : ∀{t} → (koTrans(substitute0 t φ) ≡ substitute0 t (koTrans φ))
koTrans-substitute0 {φ = f $ x} = [≡]-intro
koTrans-substitute0 {φ = ⊤}     = [≡]-intro
koTrans-substitute0 {φ = ⊥}     = [≡]-intro
koTrans-substitute0 {φ = φ ∧ ψ}{t}
  rewrite koTrans-substitute0 {φ = φ}{t}
  rewrite koTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
koTrans-substitute0 {φ = φ ∨ ψ}{t}
  rewrite koTrans-substitute0 {φ = φ}{t}
  rewrite koTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
koTrans-substitute0 {φ = φ ⟶ ψ}{t}
  rewrite koTrans-substitute0 {φ = φ}{t}
  rewrite koTrans-substitute0 {φ = ψ}{t}
  = [≡]-intro
koTrans-substitute0 {φ = Ɐ φ}{t} = {!!}
  -- rewrite koTrans-substitute0 {φ = φ}{termVar𝐒 t}
  -- = [≡]-intro
koTrans-substitute0 {φ = ∃ φ}{t} = {!!}
  -- rewrite koTrans-substitute0 {φ = φ}{termVar𝐒 t}
  -- = [≡]-intro

-- 2.3.4 (ii)
ggTrans-correctnessₗ : (Γ Classical.⊢ φ) ← (map ggTrans Γ ⊢ (ggTrans φ))
ggTrans-correctnessᵣ : (Γ Classical.⊢ φ) → (map ggTrans Γ ⊢ (ggTrans φ))

ggTrans-correctnessᵣ (Classical.direct p) = direct (Meta.[∃]-intro _ ⦃ Meta.[∧]-intro p [≡]-intro ⦄)
ggTrans-correctnessᵣ (Classical.[⊤]-intro) = [⊤]-intro
ggTrans-correctnessᵣ {Γ = Γ}{φ = φ} (Classical.[⊥]-elim p) = [⟶]-elim ([⟶]-intro(weaken{Γ₂ = (map ggTrans Γ) ∪ ·(ggTrans(¬ φ))} map-preserves-union-singleton (ggTrans-correctnessᵣ p))) ([¬¬]-elim-on-negativeFragment ggTrans-negativeFragment)
ggTrans-correctnessᵣ (Classical.[∧]-intro p q) = [∧]-intro (ggTrans-correctnessᵣ p) (ggTrans-correctnessᵣ q)
ggTrans-correctnessᵣ (Classical.[∧]-elimₗ p) = [∧]-elimₗ (ggTrans-correctnessᵣ p)
ggTrans-correctnessᵣ (Classical.[∧]-elimᵣ p) = [∧]-elimᵣ (ggTrans-correctnessᵣ p)
ggTrans-correctnessᵣ (Classical.[∨]-introₗ p) = [⟶]-elim (ggTrans-correctnessᵣ p) [∨]-introₗ-by-[¬¬∧¬]
ggTrans-correctnessᵣ (Classical.[∨]-introᵣ p) = [⟶]-elim (ggTrans-correctnessᵣ p) [∨]-introᵣ-by-[¬¬∧¬]
ggTrans-correctnessᵣ (Classical.[∨]-elim p q r) = [⟶]-elim₃ ([⟶]-intro(weaken map-preserves-union-singleton (ggTrans-correctnessᵣ p))) ([⟶]-intro(weaken map-preserves-union-singleton (ggTrans-correctnessᵣ q))) (ggTrans-correctnessᵣ r) ([∨]-elim-by-[¬¬∧¬] ([¬¬]-elim-on-negativeFragment ggTrans-negativeFragment))
ggTrans-correctnessᵣ (Classical.[⟶]-intro p) = [⟶]-intro (weaken map-preserves-union-singleton (ggTrans-correctnessᵣ p))
ggTrans-correctnessᵣ (Classical.[⟶]-elim p q) = [⟶]-elim (ggTrans-correctnessᵣ p) (ggTrans-correctnessᵣ q)
ggTrans-correctnessᵣ (Classical.[Ɐ]-intro p) = [Ɐ]-intro (substitute₁(_ ⊢_) ggTrans-substitute0 (ggTrans-correctnessᵣ p))
ggTrans-correctnessᵣ (Classical.[Ɐ]-elim p) = substitute₁ₗ(_ ⊢_) ggTrans-substitute0 ([Ɐ]-elim (ggTrans-correctnessᵣ p))
ggTrans-correctnessᵣ (Classical.[∃]-intro p) = [⟶]-elim (substitute₁(_ ⊢_) ggTrans-substitute0 (ggTrans-correctnessᵣ p)) [∃]-intro-by-[¬Ɐ¬]
ggTrans-correctnessᵣ (Classical.[∃]-elim p q) = [⟶]-elim ([⟶]-intro ([⟶]-elim ([Ɐ]-intro ([⟶]-intro ([⟶]-elim (weaken {!map-preserves-union-singleton!} (ggTrans-correctnessᵣ p)) (direct (Left (Right [≡]-intro)))))) (weaken-union (ggTrans-correctnessᵣ q)))) ([¬¬]-elim-on-negativeFragment ggTrans-negativeFragment)

koTrans-stability : Stable{ℓ}(koTrans(φ))
koTrans-stability {φ = f $ x} = [¬¬¬]-elim
koTrans-stability {φ = ⊤}     = [⟶]-intro [⊤]-intro
koTrans-stability {φ = ⊥}     = [⟶]-intro ([⟶]-elim [⟶]-refl (direct (Right [≡]-intro)))
koTrans-stability {φ = φ ∧ ψ} = [¬¬¬]-elim
koTrans-stability {φ = φ ∨ ψ} = [¬¬¬]-elim
koTrans-stability {φ = φ ⟶ ψ} = [¬¬¬]-elim
koTrans-stability {φ = Ɐ φ}   = [¬¬¬]-elim
koTrans-stability {φ = ∃ φ}   = [¬¬¬]-elim

{-
koTrans-to-[¬¬] : Γ ⊢ (koTrans(φ) ⟶ (¬¬ φ))
koTrans-to-[¬¬] {φ = f $ x} = [⟶]-refl
koTrans-to-[¬¬] {φ = ⊤}     = [¬¬]-intro-[⟶]
koTrans-to-[¬¬] {φ = ⊥}     = [¬¬]-intro-[⟶]
koTrans-to-[¬¬] {φ = φ ∧ ψ} = [⟶]-intro ([⟷]-elimₗ ([⟶]-elim₃ koTrans-to-[¬¬] koTrans-to-[¬¬] {!!} [∧]-map) [¬¬]-preserve-[∧])
koTrans-to-[¬¬] {φ = φ ∨ ψ} = {!!}
koTrans-to-[¬¬] {φ = φ ⟶ ψ} = {!!}
koTrans-to-[¬¬] {φ = Ɐ φ}   = {!!}
koTrans-to-[¬¬] {φ = ∃ φ}   = {!!}
-}

ggTrans-koTransₗ : Γ ⊢ ((ggTrans φ) ⟵ (koTrans φ))
ggTrans-koTransᵣ : Γ ⊢ ((ggTrans φ) ⟶ (koTrans φ))

[∧]-elimₗ-koTrans : (Γ ⊢ (koTrans(φ ∧ ψ) ⟶ koTrans(φ)))
[∧]-elimₗ-koTrans = [⟶]-intro ([∧]-elimₗ ([⟶]-elim₃ koTrans-stability koTrans-stability ([⟷]-elimᵣ (direct (Right [≡]-intro)) [¬¬]-preserve-[∧]) [∧]-map))

[∧]-elimᵣ-koTrans : (Γ ⊢ (koTrans(φ ∧ ψ) ⟶ koTrans(ψ)))
[∧]-elimᵣ-koTrans = [⟶]-intro ([∧]-elimᵣ ([⟶]-elim₃ koTrans-stability koTrans-stability ([⟷]-elimᵣ (direct (Right [≡]-intro)) [¬¬]-preserve-[∧]) [∧]-map))

-- [⟶]-elim-koTrans : (Γ ⊢ (koTrans(φ) ⟶ koTrans(φ ⟶ ψ) ⟶ koTrans(ψ)))

-- [⟶]-intro ([⟶]-elim ([∧]-elimₗ ([⟷]-elimᵣ ([⟶]-intro-inverse {![¬¬]-intro-[⟶]!}) [¬¬]-preserve-[∧])) koTrans-stability)
-- {![∧]-elimₗ(Meta.[↔]-to-[→] [¬¬][∧] Γφψ)!}

-- [¬]-preserve-[∨][∧] : Γ ⊢ (¬(φ ∨ ψ) ⟷ ((¬ φ) ∧ (¬ ψ)))

-- Alternative proof of the (_∧_)-case:
--   [⟶]-intro ([∧]-intro
--     ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∧]-elimₗ-koTrans) (weaken-union(ggTrans-koTransₗ {φ = φ})))
--     ([⟶]-elim ([⟶]-elim (direct (Right [≡]-intro)) [∧]-elimᵣ-koTrans) (weaken-union(ggTrans-koTransₗ {φ = ψ})))
--   )
ggTrans-koTransₗ {φ = P $ x} = [⟶]-refl
ggTrans-koTransₗ {φ = ⊤}     = [⟶]-refl
ggTrans-koTransₗ {φ = ⊥}     = [⟶]-refl
ggTrans-koTransₗ {φ = φ ∧ ψ} = [⟶]-elim₃
  ([⟶]-elim₂ ([⟷]-to-[⟶] [¬¬]-preserve-[∧]) ([⟶]-elim₂ koTrans-stability koTrans-stability [∧]-map) [⟶]-trans)
  [⟶]-refl
  ([⟶]-elim₂ (ggTrans-koTransₗ {φ = φ}) (ggTrans-koTransₗ {φ = ψ}) [∧]-map)
  [⟶]-map
ggTrans-koTransₗ {φ = φ ∨ ψ} = [⟶]-elim₃
  ([⟶]-elim ([⟷]-to-[⟵] [¬]-preserve-[∨][∧]) [⟶]-contrapositiveᵣ)
  [⟶]-refl
  ([⟶]-elim ([⟶]-elim₂ ([⟶]-elim (ggTrans-koTransₗ {φ = φ}) [¬]-map) ([⟶]-elim (ggTrans-koTransₗ {φ = ψ}) [¬]-map) [∧]-map) [¬]-map)
  [⟶]-map
ggTrans-koTransₗ {φ = φ ⟶ ψ} = [⟶]-elim₃
  (([⟶]-elim₂ [¬¬]-preserve-[⟶] ([⟶]-elim₂ [¬¬]-intro-[⟶] koTrans-stability [⟶]-map) [⟶]-trans))
  [⟶]-refl
  (([⟶]-elim₂ (ggTrans-koTransᵣ {φ = φ}) (ggTrans-koTransₗ {φ = ψ}) [⟶]-map))
  [⟶]-map
ggTrans-koTransₗ {Γ = Γ}{φ = Ɐ φ} = [⟶]-elim₃
  ([⟶]-elim₂ [¬¬]-preserve-[Ɐ] ([⟶]-elim ([Ɐ]-intro (substitute₁(\φ → Γ ⊢ ((¬¬ φ) ⟶ φ)) koTrans-substitute0 koTrans-stability)) ([Ɐ]-map {φ₂ = koTrans φ})) [⟶]-trans)
  [⟶]-refl
  ([⟶]-elim ([Ɐ]-intro (substitute₂(\a b → Γ ⊢ (a ⟵ b)) ggTrans-substitute0 koTrans-substitute0 (ggTrans-koTransₗ {φ = substitute0 _ φ}))) [Ɐ]-map)
  [⟶]-map
ggTrans-koTransₗ {φ = ∃ φ} = [⟶]-elim₃
  ([⟶]-elim ([⟷]-to-[⟵] [¬]-preserve-[∃][Ɐ]) [⟶]-contrapositiveᵣ)
  [⟶]-refl
  ([⟶]-elim ([⟶]-elim ([Ɐ]-intro ([⟶]-elim (substitute₂(\a b → _ ⊢ (a ⟵ b)) ggTrans-substitute0 koTrans-substitute0 (ggTrans-koTransₗ {φ = substitute0 _ φ})) [¬]-map)) [Ɐ]-map) [¬]-map)
  [⟶]-map

ggTrans-koTransᵣ {φ = P $ x} = [⟶]-refl
ggTrans-koTransᵣ {φ = ⊤}     = [⟶]-refl
ggTrans-koTransᵣ {φ = ⊥}     = [⟶]-refl
ggTrans-koTransᵣ {φ = φ ∧ ψ} =
  [⟶]-intro ([¬¬]-intro ([∧]-intro
    ([⟶]-elim ([∧]-elimₗ (direct (Right [≡]-intro))) (weaken-union(ggTrans-koTransᵣ {φ = φ})))
    ([⟶]-elim ([∧]-elimᵣ (direct (Right [≡]-intro))) (weaken-union(ggTrans-koTransᵣ {φ = ψ})))
  ))
ggTrans-koTransᵣ {φ = φ ∨ ψ} =
  [⟶]-intro ([¬¬]-intro ([⟶]-elim₃
    {!!}
    {!!}
    {!direct (Right [≡]-intro)!}
    ([∨]-elim-by-[¬¬∧¬] {!koTrans-stability!})
  ))
ggTrans-koTransᵣ {φ = φ ⟶ ψ} =
  [⟶]-intro ([¬¬]-intro ([⟶]-intro
    ([⟶]-elim
      ([⟶]-elim ([⟵]-elim (direct (Right [≡]-intro)) (weaken-union(weaken-union(ggTrans-koTransₗ {φ = φ})))) (direct (Left (Right [≡]-intro))))
      (weaken-union(weaken-union(ggTrans-koTransᵣ {φ = ψ})))
    )
  ))
ggTrans-koTransᵣ {φ = Ɐ φ}   = {!!}
ggTrans-koTransᵣ {φ = ∃ φ}   = {!!}


module BasicIS4.Metatheory.ClosedHilbert-TarskiClosedOvergluedImplicit where

open import BasicIS4.Syntax.ClosedHilbert public
open import BasicIS4.Semantics.TarskiClosedOvergluedImplicit public

open ImplicitSyntax (⊢_) public

-- Completeness with respect to a particular model.

module _ {{_ : Model}} where
  reify : ∀ {A} → ⊩ A → ⊢ A
  reify {α P}   s = syn s
  reify {A ▻ B} s = syn s
  reify {□ A}   s = syn s
  reify {A ∧ B} s = pair (reify (π₁ s)) (reify (π₂ s))
  reify {⊤}    s = unit

-- Additional useful equipment.

module _ {{_ : Model}} where
  ⟪K⟫ : ∀ {A B} → ⊩ A → ⊩ B ▻ A
  ⟪K⟫ a = app ck (reify a) ⅋ K a

  ⟪S⟫′ : ∀ {A B C} → ⊩ A ▻ B ▻ C → ⊩ (A ▻ B) ▻ A ▻ C
  ⟪S⟫′ s₁ = app cs (reify s₁) ⅋ λ s₂ →
              app (app cs (reify s₁)) (reify s₂) ⅋ ⟪S⟫ s₁ s₂

  _⟪D⟫_ : ∀ {A B} → ⊩ □ (A ▻ B) → ⊩ □ A → ⊩ □ B
  (t ⅋ s) ⟪D⟫ (u ⅋ a) = app (app cdist t) u ⅋ s ⟪$⟫ a

  _⟪D⟫′_ : ∀ {A B} → ⊩ □ (A ▻ B) → ⊩ □ A ▻ □ B
  _⟪D⟫′_ s = app cdist (reify s) ⅋ _⟪D⟫_ s

  ⟪↑⟫ : ∀ {A} → ⊩ □ A → ⊩ □ □ A
  ⟪↑⟫ s = box (syn s) ⅋ s

  _⟪,⟫′_ : ∀ {A B} → ⊩ A → ⊩ B ▻ A ∧ B
  _⟪,⟫′_ a = app cpair (reify a) ⅋ _,_ a

-- Soundness with respect to all models, or evaluation, for closed terms only.

eval₀ : ∀ {A} → ⊢ A → ⊨ A
eval₀ (app t u) = eval₀ t ⟪$⟫ eval₀ u
eval₀ ci        = ci ⅋ I
eval₀ ck        = ck ⅋ ⟪K⟫
eval₀ cs        = cs ⅋ ⟪S⟫′
eval₀ (box t)   = box t ⅋ eval₀ t
eval₀ cdist     = cdist ⅋ _⟪D⟫′_
eval₀ cup       = cup ⅋ ⟪↑⟫
eval₀ cdown     = cdown ⅋ ⟪↓⟫
eval₀ cpair     = cpair ⅋ _⟪,⟫′_
eval₀ cfst      = cfst ⅋ π₁
eval₀ csnd      = csnd ⅋ π₂
eval₀ unit      = ∙

-- Correctness of evaluation with respect to conversion.

eval₀✓ : ∀ {{_ : Model}} {A} {t t′ : ⊢ A} → t ⋙ t′ → eval₀ t ≡ eval₀ t′
eval₀✓ refl⋙           = refl
eval₀✓ (trans⋙ p q)    = trans (eval₀✓ p) (eval₀✓ q)
eval₀✓ (sym⋙ p)        = sym (eval₀✓ p)
eval₀✓ (congapp⋙ p q)  = cong² _⟪$⟫_ (eval₀✓ p) (eval₀✓ q)
eval₀✓ (congi⋙ p)      = cong I (eval₀✓ p)
eval₀✓ (congk⋙ p q)    = cong² K (eval₀✓ p) (eval₀✓ q)
eval₀✓ (congs⋙ p q r)  = cong³ ⟪S⟫ (eval₀✓ p) (eval₀✓ q) (eval₀✓ r)
eval₀✓ (congdist⋙ p q) = cong² _⟪D⟫_ (eval₀✓ p) (eval₀✓ q)
eval₀✓ (congup⋙ p)     = cong ⟪↑⟫ (eval₀✓ p)
eval₀✓ (congdown⋙ p)   = cong ⟪↓⟫ (eval₀✓ p)
eval₀✓ (congpair⋙ p q) = cong² _,_ (eval₀✓ p) (eval₀✓ q)
eval₀✓ (congfst⋙ p)    = cong π₁ (eval₀✓ p)
eval₀✓ (congsnd⋙ p)    = cong π₂ (eval₀✓ p)
eval₀✓ beta▻ₖ⋙         = refl
eval₀✓ beta▻ₛ⋙         = refl
eval₀✓ beta□⋙          = refl
eval₀✓ eta□⋙           = refl
eval₀✓ beta∧₁⋙         = refl
eval₀✓ beta∧₂⋙         = refl
eval₀✓ eta∧⋙           = refl
eval₀✓ eta⊤⋙          = refl

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { ⊩ᵅ_ = λ P → ⊢ α P
      }

-- Completeness with respect to all models, or quotation, for closed terms only.

quot₀ : ∀ {A} → ⊨ A → ⊢ A
quot₀ s = reify s

-- Normalisation by evaluation, for closed terms only.

norm₀ : ∀ {A} → ⊢ A → ⊢ A
norm₀ = quot₀ ∘ eval₀

-- Correctness of normalisation with respect to conversion.

norm₀✓ : ∀ {{_ : Model}} {A} {t t′ : ⊢ A} → t ⋙ t′ → norm₀ t ≡ norm₀ t′
norm₀✓ p = cong reify (eval₀✓ p)


open import Prelude
open import Nat
open import dynamics-core
open import contexts
open import contraction
open import weakening
open import exchange
open import lemmas-disjointness
open import binders-disjoint-checks

module lemmas-subst-ta where
  -- this is what makes the binders-unique assumption below good enough: it
  -- tells us that we can pick fresh variables
  mutual
    binders-envfresh : ∀{Δ Γ Γ' y σ} →
                       Δ , Γ ⊢ σ :s: Γ' →
                       y # Γ →
                       unbound-in-σ y σ →
                       binders-unique-σ σ →
                       envfresh y σ
    binders-envfresh {Γ' = Γ'} {y = y} (STAId x) apt unbound unique with ctxindirect Γ' y
    binders-envfresh {Γ' = Γ'} {y = y} (STAId x₁) apt unbound unique | Inl x = abort (somenotnone (! (x₁ y (π1 x) (π2 x)) · apt))
    binders-envfresh (STAId x₁) apt unbound unique | Inr x = EFId x
    binders-envfresh {Γ = Γ} {y = y} (STASubst  {y = z} subst x₁) apt (UBσSubst x₂ unbound neq) (BUσSubst zz x₃ x₄) =
                                                                                EFSubst (binders-fresh {y = y} x₁ zz x₂ apt)
                                                                                (binders-envfresh subst (apart-extend1 Γ neq apt) unbound x₃)
                                                                                neq

    binders-fresh : ∀{Δ Γ d2 τ y} →
                    Δ , Γ ⊢ d2 :: τ →
                    binders-unique d2 →
                    unbound-in y d2 →
                    Γ y == None →
                    fresh y d2
    binders-fresh TANum BUHole UBNum apt = FNum
    binders-fresh {y = y} (TAVar {x = x} x₁)  BUVar UBVar apt with natEQ y x
    binders-fresh (TAVar x₂) BUVar UBVar apt | Inl refl = abort (somenotnone (! x₂ · apt))
    binders-fresh (TAVar x₂) BUVar UBVar apt | Inr x₁ = FVar x₁
    binders-fresh {y = y} (TALam {x = x} x₁ wt) bu2 ub apt  with natEQ y x
    binders-fresh (TALam x₂ wt) bu2 (UBLam2 x₁ ub) apt | Inl refl = abort (x₁ refl)
    binders-fresh {Γ = Γ} (TALam {x = x} x₂ wt) (BULam bu2 x₃) (UBLam2 x₄ ub) apt | Inr x₁ =  FLam x₁ (binders-fresh wt bu2 ub (apart-extend1 Γ x₄ apt))
    binders-fresh (TAAp wt wt₁)  (BUAp bu2 bu3 x) (UBAp ub ub₁) apt = FAp (binders-fresh wt bu2 ub apt) (binders-fresh wt₁ bu3 ub₁ apt)
    binders-fresh (TAEHole x₁ x₂) (BUEHole x) (UBHole x₃) apt = FHole (binders-envfresh x₂ apt x₃ x )
    binders-fresh (TANEHole x₁ wt x₂) (BUNEHole bu2 x) (UBNEHole x₃ ub) apt = FNEHole (binders-envfresh x₂ apt x₃ x) (binders-fresh wt bu2  ub apt)
    binders-fresh (TACast wt x₁) (BUCast bu2) (UBCast ub) apt = FCast (binders-fresh wt  bu2  ub apt)
    binders-fresh (TAFailedCast wt x x₁ x₂) (BUFailedCast bu2) (UBFailedCast ub) apt = FFailedCast (binders-fresh wt  bu2  ub apt)
    binders-fresh (TAInl wt) (BUInl bu) (UBInl ub) apt = FInl (binders-fresh wt bu ub apt)
    binders-fresh (TAInr wt) (BUInr bu) (UBInr ub) apt = FInr (binders-fresh wt bu ub apt)
    binders-fresh {Γ = Γ} (TACase wt x wt₁ x₁ wt₂) (BUCase bu bu₁ bu₂ x₂ x₃ x₄ x₅ x₆ x₇ x₈ x₉ x₁₀) (UBCase ub x₁₁ ub₁ x₁₂ ub₂) apt = FCase (binders-fresh wt bu ub apt) x₁₁ (binders-fresh wt₁ bu₁ ub₁ (apart-extend1 Γ x₁₁ apt)) x₁₂ (binders-fresh wt₂ bu₂ ub₂ (apart-extend1 Γ x₁₂ apt))
    binders-fresh (TAPlus wt wt₁) (BUPlus bu bu₁ x) (UBPlus ub ub₁) apt = FPlus (binders-fresh wt bu ub apt) (binders-fresh wt₁ bu₁ ub₁ apt)
    binders-fresh (TAPair wt wt₁) (BUPair bu bu₁ x) (UBPair ub ub₁) apt = FPair (binders-fresh wt bu ub apt) (binders-fresh wt₁ bu₁ ub₁ apt)
    binders-fresh (TAFst wt) (BUFst bu) (UBFst ub) apt = FFst (binders-fresh wt bu ub apt)
    binders-fresh (TASnd wt) (BUSnd bu) (UBSnd ub) apt = FSnd (binders-fresh wt bu ub apt)

  -- the substition lemma for preservation
  lem-subst : ∀{Δ Γ x τ1 d1 τ d2} →
              x # Γ →
              binders-disjoint d1 d2 →
              binders-unique d2 →
              Δ , Γ ,, (x , τ1) ⊢ d1 :: τ →
              Δ , Γ ⊢ d2 :: τ1 →
              Δ , Γ ⊢ [ d2 / x ] d1 :: τ
  lem-subst apt bd bu2 (TANum) wt = TANum
  lem-subst apt (BDPlus bd bd₁) bu2 (TAPlus wt1 wt2) wt3 = TAPlus (lem-subst apt bd bu2 wt1 wt3) (lem-subst apt bd₁ bu2 wt2 wt3)
  lem-subst {x = x} apt bd bu2 (TAVar {x = x'} x₂) wt2 with natEQ x' x
  ... | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl with someinj x₂
  ... | refl = wt2
  lem-subst {x = x} apt bd bu2 (TAVar {x = x'} x₂) wt2 | Inr x'≠x with natEQ x x'
  ... | Inl refl = abort (x'≠x refl)
  ... | Inr x'≠x = TAVar x₂
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d2 = d2} x#Γ (BDLam bd bd') bu2 (TALam {x = y} {τ1 = τ1} {d = d} {τ2 = τ2} x₂ wt1) wt2 with natEQ y x
  ... | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone x₂)
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d2 = d2} x#Γ (BDLam bd bd') bu2 (TALam {x = y} {τ1 = τ1} {d = d} {τ2 = τ2} x₂ wt1) wt2 | Inr y≠x with natEQ x y
  ... | Inl refl = abort (y≠x refl)
  ... | Inr x≠y = TALam x₂ (lem-subst (apart-extend1 Γ x≠y x#Γ) bd bu2 (exchange-ta-Γ x≠y wt1) (weaken-ta (binders-fresh wt2 bu2 bd' x₂) wt2))
  lem-subst apt (BDAp bd bd₁) bu3 (TAAp wt1 wt2) wt3 = TAAp (lem-subst apt bd bu3 wt1 wt3) (lem-subst apt bd₁ bu3 wt2 wt3)
  lem-subst apt bd bu2 (TAEHole inΔ sub) wt2 = TAEHole inΔ (STASubst sub wt2)
  lem-subst apt (BDNEHole x₁ bd) bu2 (TANEHole x₃ wt1 x₄) wt2 = TANEHole x₃ (lem-subst apt bd bu2 wt1 wt2) (STASubst x₄ wt2)
  lem-subst apt (BDCast bd) bu2 (TACast wt1 x₁) wt2 = TACast (lem-subst apt bd bu2 wt1 wt2) x₁
  lem-subst apt (BDFailedCast bd) bu2 (TAFailedCast wt1 x₁ x₂ x₃) wt2 = TAFailedCast (lem-subst apt bd bu2 wt1 wt2) x₁ x₂ x₃
  lem-subst apt (BDInl bd) bu (TAInl wt1) wt2 = TAInl (lem-subst apt bd bu wt1 wt2)
  lem-subst apt (BDInr bd) bu (TAInr wt1) wt2 = TAInr (lem-subst apt bd bu wt1 wt2)
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d1 = .(case _ _ _ _ _)} {d2 = d2} apt (BDCase bd x₁ bd₁ x₂ bd₂) bu (TACase {d = d} {τ1 = τ1} {τ2 = τ2} {x = y} {y = z} wt1 x₃ wt3 x₄ wt4) wt2
    with natEQ y x | natEQ z x
  ... | Inl refl | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone x₄)
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d1 = .(case _ _ _ _ _)} {d2 = d2} apt (BDCase bd x₁ bd₁ x₂ bd₂) bu (TACase {d = d} {τ1 = τ1} {τ2 = τ2} {x = y} {y = z} wt1 x₃ wt3 x₄ wt4) wt2 | Inl refl | Inr z≠x with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone x₃)
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d1 = .(case _ _ _ _ _)} {d2 = d2} apt (BDCase bd x₁ bd₁ x₂ bd₂) bu (TACase {d = d} {τ1 = τ1} {τ2 = τ2} {x = y} {y = z} wt1 x₃ wt3 x₄ wt4) wt2 | Inr y≠x  | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone x₄)
  lem-subst {Δ = Δ} {Γ = Γ} {x = x} {d1 = .(case _ _ _ _ _)} {d2 = d2} apt (BDCase bd x₁ bd₁ x₂ bd₂) bu (TACase {d = d} {τ1 = τ1} {τ2 = τ2} {x = y} {y = z} wt1 x₃ wt3 x₄ wt4) wt2 | Inr y≠x  | Inr z≠x with natEQ x z
  ... | Inl refl = abort (z≠x refl)
  ... | Inr x≠z with natEQ x y
  ... | Inl refl = abort (y≠x refl)
  ... | Inr x≠y = TACase (lem-subst apt bd bu wt1 wt2) x₃ (lem-subst (apart-extend1 Γ x≠y apt) bd₁ bu (exchange-ta-Γ x≠y wt3) (weaken-ta (binders-fresh wt2 bu x₁ x₃) wt2)) x₄ (lem-subst (apart-extend1 Γ x≠z apt) bd₂ bu (exchange-ta-Γ x≠z wt4) (weaken-ta (binders-fresh wt2 bu x₂ x₄) wt2))
  lem-subst apt (BDPair bd bd₁) bu (TAPair wt1 wt3) wt2 = TAPair (lem-subst apt bd bu wt1 wt2) (lem-subst apt bd₁ bu wt3 wt2)
  lem-subst apt (BDFst bd) bu (TAFst wt1) wt2 = TAFst (lem-subst apt bd bu wt1 wt2)
  lem-subst apt (BDSnd bd) bu (TASnd wt1) wt2 = TASnd (lem-subst apt bd bu wt1 wt2)

  lem-subst-cast-sta : ∀{Δ Γ x τ1 τ2 σ Γ'} →
                       x # Γ →
                       τ1 ~ τ2 →
                       Δ , Γ ,, (x , τ2) ⊢ σ :s: Γ' →
                       Δ , Γ ,, (x , τ1) ⊢ Subst (X x ⟨ τ1 ⇒ τ2 ⟩) x σ :s: Γ'
  lem-subst-cast-sta {Γ = Γ} {x = x} {τ1 = τ1} {τ2 = τ2} {Γ' = Γ'} x#Γ con (STAId sub) = STASubst (STAId Γ'⊆) (TACast (TAVar (ctx-top Γ x τ1 x#Γ)) con)
      where
        Γ'⊆ : (y : Nat) (τ : htyp) → (y , τ) ∈ Γ' → (y , τ) ∈ (Γ ,, (x , τ1) ,, (x , τ2))
        Γ'⊆ y τ y∈Γ' with lem-dom-union {Δ1 = ■(x , τ2)} {Δ2 = Γ} (sub y τ y∈Γ')
        ... | Inl y∈x with natEQ x y
        ... | Inr x≠y = abort (somenotnone (! y∈x))
        ... | Inl refl = y∈x
        Γ'⊆ y τ y∈Γ' | Inr y∈Γ with natEQ x y
        ... | Inl refl = abort (somenotnone ((! y∈Γ) · x#Γ))
        ... | Inr x≠y with natEQ x y
        ... | Inl refl = abort (x≠y refl)
        ... | Inr x≠y' = y∈Γ
  lem-subst-cast-sta {Δ = Δ} {Γ = Γ} {x = x} {τ1 = τ1} {τ2 = τ2} {Γ' = Γ'} x#Γ con (STASubst {σ = σ} {y = y} {d = d} {τ = τ} wsta wt) = STASubst (STASubst (tr (λ c → Δ , c ,, (y , τ) ⊢ σ :s: Γ') (! (update Γ x τ1 τ2)) wsta) (tr (λ c → Δ , c ⊢ d :: τ) (! (update Γ x τ1 τ2)) wt)) (TACast (TAVar (ctx-top Γ x τ1 x#Γ)) con)

  lem-subst-cast-ta : ∀{Δ Γ d x τ1 τ2 τ} →
                      x # Γ →
                      binders-unique d →
                      τ1 ~ τ2 →
                      Δ , Γ ,, (x , τ2) ⊢ d :: τ →
                      Δ , Γ ,, (x , τ1) ⊢ [ (X x ⟨ τ1 ⇒ τ2 ⟩) / x ] d :: τ
  lem-subst-cast-ta apt bu con TANum = TANum
  lem-subst-cast-ta apt (BUPlus bu bu₁ x) con (TAPlus wt wt₁) = TAPlus (lem-subst-cast-ta apt bu con wt) (lem-subst-cast-ta apt bu₁ con wt₁)
  lem-subst-cast-ta {Γ = Γ} {x = x} {τ1 = τ1} {τ2 = τ2} apt bu con (TAVar {x = x'} x₁)
      with natEQ x x'
  ... | Inl refl with natEQ x' x
  ... | Inr x'≠x = abort (x'≠x refl)
  ... | Inl refl with Γ x
  ... | None with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl with someinj x₁
  ... | refl = TACast (TAVar (x∈∪l (■ (x , τ1)) Γ x τ1 (x∈■ x τ1))) con
  lem-subst-cast-ta {Γ = Γ} {x = x} {τ1 = τ1} {τ2 = τ2} apt bu con (TAVar {x = x} x₁) | Inl refl | Inl refl | Some t with someinj x₁
  ... | refl = abort (somenotnone apt)
  lem-subst-cast-ta {Γ = Γ} {x = x} {τ1 = τ1} {τ2 = τ2} {τ = τ} apt bu con (TAVar {x = x'} x₁) | Inr x≠x' with natEQ x' x
  ... | Inl refl = abort (x≠x' refl)
  ... | Inr x'≠x = TAVar (x∈∪r (■(x , τ1)) Γ x' τ x₁ (apart-singleton (λ refl → abort (x'≠x refl))))
  lem-subst-cast-ta {Δ = Δ} {Γ = Γ} {x = x} {τ = τ1 ==> τ3} apt (BULam bu x₁) con (TALam {x = y} x₂ wt)
    with natEQ x y
  ... | Inl refl = abort (somenotnone x₂)
  ... | Inr x≠y with natEQ y x
  ... | Inl refl = abort (x≠y refl)
  ... | Inr y≠x = TALam (apart-extend1 Γ y≠x x₂) (exchange-ta-Γ y≠x (lem-subst-cast-ta (apart-extend1 Γ x≠y apt) bu con (exchange-ta-Γ x≠y wt)))
  lem-subst-cast-ta {Γ = Γ} apt (BUAp bu bu₁ x) con (TAAp wt wt₁) = TAAp (lem-subst-cast-ta {Γ = Γ} apt bu con wt) (lem-subst-cast-ta {Γ = Γ} apt bu₁ con wt₁)
  lem-subst-cast-ta {Γ = Γ} apt (BUInl bu) con (TAInl wt) = TAInl (lem-subst-cast-ta {Γ = Γ} apt bu con wt)
  lem-subst-cast-ta {Γ = Γ} apt (BUInr bu) con (TAInr wt) = TAInr (lem-subst-cast-ta {Γ = Γ} apt bu con wt)
  lem-subst-cast-ta {x = x} apt (BUCase bu bu₁ bu₂ x₂ x₃ x₄ x₅ x₆ x₇ x₈ x₉ x₁₀) con (TACase {x = y} {y = z} wt apty wt₁ aptz wt₂) with natEQ y x | natEQ z x
  ... | Inl refl | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone aptz)
  lem-subst-cast-ta {x = x} apt (BUCase bu bu₁ bu₂ x₂ x₃ x₄ x₅ x₆ x₇ x₈ x₉ x₁₀) con (TACase {x = y} {y = z} wt apty wt₁ aptz wt₂) | Inl refl | Inr z≠x with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone apty)
  lem-subst-cast-ta {x = x} apt (BUCase bu bu₁ bu₂ x₂ x₃ x₄ x₅ x₆ x₇ x₈ x₉ x₁₀) con (TACase {x = y} {y = z} wt apty wt₁ aptz wt₂) | Inr y≠x  | Inl refl with natEQ x x
  ... | Inr x≠x = abort (x≠x refl)
  ... | Inl refl = abort (somenotnone aptz)
  lem-subst-cast-ta {Γ = Γ} {x = x} apt (BUCase bu bu₁ bu₂ x₂ x₃ x₄ x₅ x₆ x₇ x₈ x₉ x₁₀) con (TACase {x = y} {y = z} wt apty wt₁ aptz wt₂) | Inr y≠x  | Inr z≠x with natEQ x z
  ... | Inl refl = abort (z≠x refl)
  ... | Inr x≠z with natEQ x y
  ... | Inl refl = abort (y≠x refl)
  ... | Inr x≠y = TACase (lem-subst-cast-ta apt bu con wt) (apart-extend1 Γ y≠x apty) (exchange-ta-Γ y≠x (lem-subst-cast-ta (apart-extend1 Γ x≠y apt) bu₁ con (exchange-ta-Γ x≠y wt₁))) (apart-extend1 Γ z≠x aptz) (exchange-ta-Γ z≠x (lem-subst-cast-ta (apart-extend1 Γ x≠z apt) bu₂ con (exchange-ta-Γ x≠z wt₂)))
  lem-subst-cast-ta apt (BUEHole x₂) con (TAEHole x x₁) = TAEHole x (lem-subst-cast-sta apt con x₁)
  lem-subst-cast-ta apt (BUNEHole bu x₂) con (TANEHole x wt x₁) = TANEHole x (lem-subst-cast-ta apt bu con wt) (lem-subst-cast-sta apt con x₁)
  lem-subst-cast-ta apt (BUCast bu) con (TACast wt x) = TACast (lem-subst-cast-ta apt bu con wt) x
  lem-subst-cast-ta apt (BUFailedCast bu) con (TAFailedCast wt x x₁ x₂) = TAFailedCast (lem-subst-cast-ta apt bu con wt) x x₁ x₂
  lem-subst-cast-ta apt (BUPair bu bu₁ x) con (TAPair wt wt₁) = TAPair (lem-subst-cast-ta apt bu con wt) (lem-subst-cast-ta apt bu₁ con wt₁)
  lem-subst-cast-ta apt (BUFst bu) con (TAFst wt) = TAFst (lem-subst-cast-ta apt bu con wt)
  lem-subst-cast-ta apt (BUSnd bu) con (TASnd wt) = TASnd (lem-subst-cast-ta apt bu con wt)


module _ where

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

variable
  A B : Set
  x y : A
  xs  : List A

infix 3 _∈_
data _∈_ {A : Set} (x : A) : List A → Set where
  zero : x ∈ x ∷ xs
  suc  : x ∈ xs → x ∈ y ∷ xs

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

search : Nat → Term → Term → TC ⊤
search zero i hole = typeError (strErr "Not found" ∷ [])
search (suc n) i hole = do
  catchTC (noConstraints (unify hole i))
          (search n (con (quote _∈_.suc) (vArg i ∷ [])) hole)

findElem : Nat → Term → TC ⊤
findElem depth hole = search depth (con (quote _∈_.zero) []) hole

index : (x : A) (xs : List A) {@(tactic findElem 10) i : x ∈ xs} → Nat
index x xs {zero}  = zero
index x xs {suc i} = suc (index x _ {i})

test₁ : index 3 (1 ∷ 2 ∷ 3 ∷ []) ≡ 2
test₁ = refl

test₂ : index x (y ∷ x ∷ x ∷ []) ≡ 1
test₂ = refl


--
-- Created by Dependently-Typed Lambda Calculus on 2019-05-15
-- imports
-- Author: ice1000
--

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

import Relation.Binary.PropositionalEquality
open import Relation.Binary.PropositionalEquality
import Relation.Binary.PropositionalEquality using ()
import Relation.Binary.PropositionalEquality using (sym) hiding (cong)
import Relation.Binary.PropositionalEquality renaming (sym to symBla)


module Implicits.Resolution.Embedding where

open import Prelude

open import Data.Vec
open import Data.List as List hiding (map)

open import Implicits.Syntax
open import SystemF.Everything as F using ()

⟦_⟧tp← : ∀ {ν} → F.Type ν → Type ν
⟦ F.tc x ⟧tp← = simpl (tc x)
⟦ F.tvar n ⟧tp← = simpl (tvar n)
⟦ a F.→' b ⟧tp← = (⟦ a ⟧tp← ⇒ ⟦ b ⟧tp←)
⟦ a F.⟶ b ⟧tp← = simpl (⟦ a ⟧tp← →' ⟦ b ⟧tp←)
⟦ F.∀' x ⟧tp← = ∀' ⟦ x ⟧tp←

⟦_⟧tps← : ∀ {ν n} → Vec (F.Type ν) n → Vec (Type ν) n
⟦ v ⟧tps← = map (⟦_⟧tp←) v

⟦_⟧ctx← : ∀ {ν n} → Vec (F.Type ν) n → List (Type ν)
⟦ v ⟧ctx← = toList $ map ⟦_⟧tp← v

⟦_⟧tp→ : ∀ {ν} → Type ν → F.Type ν
⟦ simpl (tc x) ⟧tp→ = F.tc x
⟦ simpl (tvar n) ⟧tp→ = F.tvar n
⟦ simpl (a →' b) ⟧tp→ = ⟦ a ⟧tp→ F.⟶ ⟦ b ⟧tp→
⟦ a ⇒ b ⟧tp→ = ⟦ a ⟧tp→ F.→' ⟦ b ⟧tp→
⟦ ∀' x ⟧tp→ = F.∀' ⟦ x ⟧tp→

⟦_⟧tps→ : ∀ {ν n} → Vec (Type ν) n → Vec (F.Type ν) n
⟦ v ⟧tps→ = map (⟦_⟧tp→) v

⟦_⟧ctx→ : ∀ {ν} → (Δ : ICtx ν) → Vec (F.Type ν) (List.length (List.map ⟦_⟧tp→ Δ))
⟦ Δ ⟧ctx→ = fromList (List.map ⟦_⟧tp→ Δ)


module _ where

-- open import Common.Prelude

module Id (A : Set) where

  id : A → A
  id x = x

module _ (A : Set) where
  open Id A

  id2 = id


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

--
-- Prelude.agda - Some base definitions
--

module Prelude where

  open import Agda.Primitive public

  record ⊤ : Set where
    constructor tt

  {-# BUILTIN UNIT ⊤ #-}

  record Σ {i j} (A : Set i) (B : A → Set j) : Set (i ⊔ j) where
    constructor _,_
    field
      fst : A
      snd : B fst

  open Σ public

  _×_ : ∀ {i j} (A : Set i) (B : Set j) → Set (i ⊔ j)
  A × B = Σ A (λ a → B)

  id : ∀ {i} {A : Set i} → A → A
  id x = x

  uncurry : ∀ {i j k} {A : Set i} {B : A → Set j} {C : Set k} →
            (φ : (a : A) → (b : B a) → C) →
            Σ A B → C
  uncurry φ (a , b) = φ a b

  curry : ∀ {i j k} {A : Set i} {B : A → Set j} {C : Set k} →
          (ψ : Σ A B → C) →
          (a : A) → (b : B a) → C
  curry ψ a b = ψ (a , b)

  {- Equality -}

  infix 30 _==_

  data _==_ {i} {A : Set i} (a : A) : A → Set i where
    idp : a == a
  {-# BUILTIN EQUALITY _==_ #-}
  {-# BUILTIN REWRITE _==_ #-}

  infixl 20 _>>_

  _>>_ : ∀ {i} {A : Set i} {a b c : A} → a == b → b == c → a == c
  idp >> idp = idp

  _^ : ∀ {i} {A : Set i} {a b : A} → a == b → b == a
  _^ idp = idp

  coe : ∀ {a} {A B : Set a} → A == B → A → B
  coe idp x = x

  coe^ : ∀ {a} {A B : Set a} (p : A == B) {a : A} {b : B} → a == (coe (p ^) b) → (coe p a) == b
  coe^ idp q = q

  coe= : ∀ {a} {A B : Set a} (p : A == B) {a b : A} → a == b → coe p a == coe p b
  coe= p idp = idp

  ap : ∀ {i j} {A : Set i} {C : Set j} {M N : A} (f : A → C) → M == N → (f M) == (f N)
  ap f idp = idp

  ap² : ∀ {i j k} {A : Set i} {B : Set k} {C : Set j} {a a' : A} {b b' : B} (f : A → B → C) → a == a' →  b == b' → (f a b) == (f a' b')
  ap² f idp idp = idp

  ap³ : ∀ {i j k l} {A : Set i} {B : Set k} {C : Set j} {D : Set l} {a a' : A} {b b' : B} {c c' : C} (f : A → B → C → D) → a == a' →  b == b' → c == c' → (f a b c) == (f a' b' c')
  ap³ f idp idp idp = idp

  ap⁴ : ∀ {i j k l m} {A : Set i} {B : Set k} {C : Set j} {D : Set l} {E : Set m} {a a' : A} {b b' : B} {c c' : C} {d d' : D} (f : A → B → C → D → E) → a == a' →  b == b' → c == c' → d == d' → (f a b c d) == (f a' b' c' d')
  ap⁴ f idp idp idp idp = idp

  ap⁵ : ∀ {i j k l m n} {A : Set i} {B : Set k} {C : Set j} {D : Set l} {E : Set m} {F : Set n} {a a' : A} {b b' : B} {c c' : C} {d d' : D} {e e' : E} (f : A → B → C → D → E → F) → a == a' →  b == b' → c == c' → d == d' → e == e' → (f a b c d e) == (f a' b' c' d' e')
  ap⁵ f idp idp idp idp idp = idp

  ap⁶ : ∀ {i j k l m n o} {A : Set i} {B : Set k} {C : Set j} {D : Set l} {E : Set m} {F : Set n} {G : Set o} {a a' : A} {b b' : B} {c c' : C} {d d' : D} {e e' : E} {f f' : F} (α : A → B → C → D → E → F → G) → a == a' →  b == b' → c == c' → d == d' → e == e' → f == f' → (α a b c d e f) == (α a' b' c' d' e' f')
  ap⁶ f idp idp idp idp idp idp = idp

  ap⁷ : ∀ {i j k l m n o p} {A : Set i} {B : Set k} {C : Set j} {D : Set l} {E : Set m} {F : Set n} {G : Set o} {H : Set p} {a a' : A} {b b' : B} {c c' : C} {d d' : D} {e e' : E} {f f' : F} {g g' : G} (α : A → B → C → D → E → F → G → H) → a == a' →  b == b' → c == c' → d == d' → e == e' → f == f' → g == g' → (α a b c d e f g) == (α a' b' c' d' e' f' g')
  ap⁷ f idp idp idp idp idp idp idp = idp

  transport : ∀ {i j} {A : Set i} {B : A → Set j} {a a' : A} (pₐ : a == a') → B a → B a'
  transport pₐ b = coe (ap _ pₐ) b

  transport₂ : ∀ {i j k} {A : Set i} {B : Set j} {C : A → B → Set k} {a a' : A} {b b' : B} (pₐ : a == a') (q : b == b') → C a b → C a' b'
  transport₂ pₐ q c = coe (ap² _ pₐ q) c

  hfiber : ∀ {i} {A B : Set i} (f : A → B) (b : B) → Set i
  hfiber {A = A} f b = Σ A (λ a → f a == b)

  PathOver : ∀ {i j} {A : Set i} (B : A → Set j) {a₀ a₁ : A} (p : a₀ == a₁) (b₉ : B a₀) (b₁ : B a₁) → Set j
  PathOver B idp b₀ b₁ = b₀ == b₁

  infix 30 PathOver
  syntax PathOver B p u v =
    u == v [ B ↓ p ]

  ×= : ∀{i j} {A : Set i} {B : Set j}  {a a' : A} {b b' : B} → a == a' → b == b' → (a , b) == (a' , b')
  ×= idp idp = idp

  Σ= : ∀ {i j} {A : Set i} {B : A → Set j}  {a a' : A} {b : B a} {b' : B a'} → (pₐ : a == a') → transport pₐ b == b' → (a , b) == (a' , b')
  Σ= idp idp = idp

  ,= : ∀ {i j} {A : Set i} {B : Set j}  {a a' : A} {b b' : B} → a == a' → b == b' → (a , b) == (a' , b')
  ,= idp idp = idp

  Σ-r : ∀ {i j k} {A : Set i} {B : A → Set j} (C : Σ A B → Set k) → A → Set (j ⊔ k)
  Σ-r {A = A} {B = B} C a = Σ (B a) (λ b → C (a , b))

  Σ-in : ∀ {i j k} {A : Set i} {B : A → Set j} (C : (a : A) → B a → Set k) → A → Set (j ⊔ k)
  Σ-in {A = A} {B = B} C a = Σ (B a) (λ b → C a b)

  =, : ∀ {i j} {A : Set i} {B : Set j} {a a' : A} {b b' : B} → (a , b) == (a' , b') → (a == a') × (b == b')
  =, idp = idp , idp

  fst-is-inj : ∀ {i j} {A : Set i} {B : A → Set j} {x y : Σ A B} → x == y → fst x == fst y
  fst-is-inj idp = idp

  {- False and negation -}

  data ⊥ {i} : Set i where

  ⊥-elim : ∀ {i j} {A : Set i} → ⊥ {j} → A
  ⊥-elim ()

  ¬_ : ∀ {i} → Set i → Set i
  ¬ A = A → ⊥ {lzero}

  _≠_ : ∀{i} {A : Set i} (a b : A) → Set i
  a ≠ b = ¬ (a == b)

  data _+_  {i j} (A : Set i) (B : Set j) : Set (i ⊔ j) where
    inl : A → A + B
    inr : B → A + B

  inl= : ∀ {i j} {A : Set i} {B : Set j} {a b : A} → a == b → _==_ {i ⊔ j} {A + B} (inl a) (inl b)
  inl= idp = idp

  inr= : ∀ {i j} {A : Set i} {B : Set j} {a b : B} → a == b → _==_ {i ⊔ j} {A + B} (inr a) (inr b)
  inr= idp = idp

  {- Booleans -}
  data Bool : Set where
    true : Bool
    false : Bool

  {- Decidability -}
  dec : ∀ {i} → Set i → Set i
  dec A = A + (¬ A)

  eqdec : ∀ {i} → Set i → Set i
  eqdec A = ∀ (a b : A) → dec (a == b)

  {- Natural numbers -}
  data ℕ : Set where
    O : ℕ
    S : ℕ → ℕ

  {-# BUILTIN NATURAL ℕ #-}

  S= : ∀{n m} → n == m → S n == S m
  S= idp = idp

  pred : ℕ → ℕ
  pred O = O
  pred (S n) = n

  S-is-inj : ∀ n m → (S n == S m) → n == m
  S-is-inj n m p = ap pred p

  S-≠ : ∀ {n m : ℕ} (p : n ≠ m) → S n ≠ S m
  S-≠ {n} {m} n≠m  p = n≠m (S-is-inj n m p)

  private
    S≠O-type : ℕ → Set
    S≠O-type O = ⊥
    S≠O-type (S n) = ⊤

  S≠O : (n : ℕ) → S n ≠ O
  S≠O n p = coe (ap S≠O-type p) tt

  O≠S : (n : ℕ) → (O ≠ S n)
  O≠S n p = S≠O n (p ^)

  n≠Sn : (n : ℕ) → (n ≠ S n)
  n≠Sn O ()
  n≠Sn (S n) n=Sn = n≠Sn n (S-is-inj _ _ n=Sn)

  Sn≠n : (n : ℕ) → (S n ≠ n)
  Sn≠n O ()
  Sn≠n (S n) Sn=n = Sn≠n n (S-is-inj _ _ Sn=n)

  eqdecℕ : eqdec ℕ
  eqdecℕ O O = inl idp
  eqdecℕ O (S b) = inr (O≠S b)
  eqdecℕ (S a) O = inr (S≠O a)
  eqdecℕ (S a) (S b) with (eqdecℕ a b)
  ...                 | inl idp = inl idp
  ...                 | inr a≠b = inr (S-≠ a≠b)

  {- Order on ℕ -}
  data _≤_ : ℕ → ℕ → Set where
    0≤ : ∀ n → O ≤ n
    S≤ : ∀ {n m} → n ≤ m → S n ≤ S m

  n≤n : ∀ (n : ℕ) → n ≤ n
  n≤n O = 0≤ O
  n≤n (S n) = S≤ (n≤n n)

  ≤-antisymetry : ∀ {n m} → n ≤ m → m ≤ n → n == m
  ≤-antisymetry (0≤ _) (0≤ _) = idp
  ≤-antisymetry (S≤ n≤m) (S≤ m≤n) = ap S (≤-antisymetry n≤m m≤n)

  n≤Sn : ∀ (n : ℕ) → n ≤ S n
  n≤Sn O = 0≤ _
  n≤Sn (S n) = S≤ (n≤Sn _)

  S≤S : ∀ {n m} → S n ≤ S m → n ≤ m
  S≤S (S≤ n≤m) = n≤m

  Sn≰n : ∀ (n : ℕ) → ¬ (S n ≤ n)
  Sn≰n .(S _) (S≤ Sn≤n) = Sn≰n _ Sn≤n

  Sn≰n-t : ∀ {n m} → n == m → ¬ (S n ≤ m)
  Sn≰n-t idp Sn≤n = Sn≰n _ Sn≤n

  Sn≰0 : ∀ (n : ℕ) → ¬ (S n ≤ O)
  Sn≰0 n ()

  n≤m→n≤Sm : ∀ {n m : ℕ} → n ≤ m → n ≤ S m
  n≤m→n≤Sm (0≤ n) = 0≤ (S n)
  n≤m→n≤Sm (S≤ n≤m) = S≤ (n≤m→n≤Sm n≤m)

  Sn≤m→n≤m : ∀ {n m : ℕ} → S n ≤ m → n ≤ m
  Sn≤m→n≤m (S≤ n≤m) = n≤m→n≤Sm n≤m

  dec-≤ : ∀ n m → dec (n ≤ m)
  dec-≤ O m = inl (0≤ m)
  dec-≤ (S n) O = inr λ ()
  dec-≤ (S n) (S m) with (dec-≤ n m)
  ...               | inl n≤m = inl (S≤ n≤m)
  ...               | inr n≰m = inr λ {(S≤ n≤m) → n≰m n≤m}

  ≤S : ∀ (n m : ℕ) → n ≤ S m → (n ≤ m) + (n == S m)
  ≤S .0 m (0≤ .(S m)) = inl (0≤ _)
  ≤S .1 O (S≤ (0≤ .0)) = inr idp
  ≤S .(S _) (S m) (S≤ n≤Sm) with ≤S _ _ n≤Sm
  ... | inl n≤m = inl (S≤ n≤m)
  ... | inr n=Sm = inr (ap S n=Sm)

  ≤-= : ∀ {n m k} → n ≤ m → m == k → n ≤ k
  ≤-= n≤m idp = n≤m

  =-≤ : ∀ {n m k} → n == m → m ≤ k → n ≤ k
  =-≤ idp m≤k = m≤k

  =-≤-= : ∀ {n m k l} → n == m → m ≤ k → k == l → n ≤ l
  =-≤-= idp m≤k idp = m≤k

  ≤T : ∀ {n m k} → n ≤ m → m ≤ k → n ≤ k
  ≤T (0≤ _) _ = 0≤ _
  ≤T (S≤ n≤m) (S≤ m≤k) = S≤ (≤T n≤m m≤k)

  {- Strict order on ℕ -}
  _<_ : ℕ → ℕ → Set
  n < m = S n ≤ m

  ≤×≠→< : ∀ {n m} → n ≤ m → n ≠ m → n < m
  ≤×≠→< {.0} {.0} (0≤ O) n≠m = ⊥-elim (n≠m idp)
  ≤×≠→< {.0} {.(S m)} (0≤ (S m)) n≠m = S≤ (0≤ _)
  ≤×≠→< (S≤ n≤m) Sn≠Sm = S≤ (≤×≠→< n≤m λ n=m → Sn≠Sm (ap S n=m))

  ≰ : ∀ {n m } → ¬ (n ≤ m) → m < n
  ≰ {O} {m} n≰m = ⊥-elim (n≰m (0≤ _))
  ≰ {S n} {O} n≰m = S≤ (0≤ _)
  ≰ {S n} {S m} n≰m = S≤ (≰ λ n≤m → n≰m (S≤ n≤m))

  ℕ-trichotomy : ∀ n m → ((n < m) + (m < n)) + (n == m)
  ℕ-trichotomy n m with dec-≤ n m
  ... | inr n≰m = inl (inr (≰ n≰m))
  ... | inl n≤m with eqdecℕ n m
  ... | inl n=m = inr n=m
  ... | inr n≠m = inl (inl (≤×≠→< n≤m n≠m))

  {- Minimum and maximum -}
  max : ℕ → ℕ → ℕ
  max n m with dec-≤ n m
  ...     | inl _ = m
  ...     | inr _ = n

  n≤max : ∀ n m → n ≤ max n m
  n≤max n m with dec-≤ n m
  ... | inl n≤m = n≤m
  ... | inr m≤n = n≤n _

  m≤max : ∀ n m → m ≤ max n m
  m≤max n m with dec-≤ n m
  ... | inl n≤m = n≤n _
  ... | inr n≰m = Sn≤m→n≤m (≰ n≰m)

  up-max : ∀ {n m k} → n ≤ k → m ≤ k → max n m ≤ k
  up-max {n} {m} {k} n≤k m≤k with dec-≤ n m
  ... | inl n≤m = m≤k
  ... | inr n≰m = n≤k

  up-maxS : ∀ {n m k} → S n ≤ k → S m ≤ k → S (max n m) ≤ k
  up-maxS {n} {m} {k} n≤k m≤k with dec-≤ n m
  ... | inl n≤m = m≤k
  ... | inr n≰m = n≤k

  simplify-max-l : ∀ {n m} → m ≤ n → max n m == n
  simplify-max-l {n} {m} m≤n with dec-≤ n m
  ... | inl n≤m = ≤-antisymetry m≤n n≤m
  ... | inr _ = idp

  simplify-max-r : ∀ {n m} → n ≤ m → max n m == m
  simplify-max-r {n} {m} n≤m with dec-≤ n m
  ... | inl _ = idp
  ... | inr n≰m = ⊥-elim (n≰m n≤m)

  min : ℕ → ℕ → ℕ
  min n m with dec-≤ n m
  ...     | inl _ = n
  ...     | inr _ = m

  min≤m : ∀ n m → min n m ≤ m
  min≤m n m with dec-≤ n m
  ... | inl n≤m = n≤m
  ... | inr n≰m = n≤n _

  simplify-min-l : ∀ {n m} → n ≤ m → min n m == n
  simplify-min-l {n} {m} n≤m with dec-≤ n m
  ... | inl _ = idp
  ... | inr n≰m = ⊥-elim (n≰m n≤m)

  simplify-min-r : ∀ {n m} → m ≤ n → min n m == m
  simplify-min-r {n} {m} n≤m with dec-≤ n m
  ... | inl m≤n = ≤-antisymetry m≤n n≤m
  ... | inr _ = idp

  min<l : ∀ {n m} → min n m < n → m ≤ n
  min<l {n} {m} min<n with dec-≤ n m
  ... | inl _ = ⊥-elim (Sn≰n _ min<n)
  ... | inr _ = Sn≤m→n≤m min<n

  greater-than-min-r : ∀ {n m k} → k < n → min n m ≤ k → m ≤ k
  greater-than-min-r {n} {m} {k} k<n min≤k with dec-≤ n m
  ... | inl _ = ⊥-elim (Sn≰n _ (≤T (S≤ min≤k) k<n))
  ... | inr _ = min≤k

  {- Conditional branchings -}
  ifdec_>_then_else_ : ∀ {i j} {A : Set i} (B : Set j) → (dec B) → A → A → A
  ifdec b > inl x then A else B = A
  ifdec b > inr x then A else B = B

  iftrue : ∀ {i j}{A : Set i} {B : Set j} → (H : dec B) → (a : A) {a' : A} → B → (ifdec B > H then a else a') == a
  iftrue (inl _) a b = idp
  iftrue (inr ¬B) a b = ⊥-elim (¬B b)

  iffalse : ∀ {i j}{A : Set i} {B : Set j} → (H : dec B) → {a : A} (a' : A) → ¬ B → (ifdec B > H then a else a') == a'
  iffalse (inl b) a' ¬B = ⊥-elim (¬B b)
  iffalse (inr ¬B) a' b = idp

  if_≡_then_else_ : ∀ {i} {A : Set i} → ℕ → ℕ → A → A → A
  if v ≡ w then A else B = ifdec (v == w) > (eqdecℕ v w) then A else B

  if= : ∀ {i} {A : Set i} {n m : ℕ} (p : n == m) (a : A) {a' : A} → (if n ≡ m then a else a') == a
  if= p a = iftrue (eqdecℕ _ _) a p

  if≠ : ∀ {i} {A : Set i} {n m : ℕ} (p : n ≠ m) {a : A} (a' : A) → (if n ≡ m then a else a') == a'
  if≠ p a' = iffalse (eqdecℕ _ _) a' p

  simplify-if : ∀ {i} {A : Set i} {n} {a b : A} → 0 < n → (if n ≡ 0 then a else b) == b
  simplify-if {n = n} {b = b} 0<n with eqdecℕ n 0
  ... | inl idp = ⊥-elim (Sn≰0 _ 0<n)
  ... | inr _ = idp

  -- logical equivalence
  _↔_ : ∀{i j} (A : Set i) (B : Set j) → Set (i ⊔ j)
  A ↔ B = (A → B) × (B → A)

  {- Lists -}
  data list {i} : Set i → Set (lsuc i) where
    nil : ∀{A} → list A
    _::_ : ∀ {A} → list A → (a : A) → list A

  ::= : ∀ {i} {A : Set i} {l l' : list A} {a a' : A} → l == l' → a == a' → (l :: a) == (l' :: a')
  ::= idp idp = idp

  =:: : ∀ {i} {A : Set i} {a b : A} {l k} → (l :: a) == (k :: b) → (l == k) × (a == b)
  =:: idp = idp , idp

  cons≠nil : ∀ {i} {A : Set i} {l : list A} {a : A} → (l :: a) ≠ nil
  cons≠nil ()

  length : ∀ {i} {A : Set i} → list A → ℕ
  length nil = 0
  length (l :: _) = S (length l)

  _++_ : ∀ {i} {A : Set i} → list A → list A → list A
  l ++ nil = l
  l ++ (l' :: a) = (l ++ l') :: a

  _∈-list_ : ∀ {i} {A : Set i} → A → list A → Set i
  x ∈-list nil = ⊥
  x ∈-list (l :: a) = (x ∈-list l) + (x == a)

  dec-∈-list : ∀ {i} {A : Set i} → eqdec A →  ∀ (a : A) (l : list A) → dec (a ∈-list l)
  dec-∈-list eqdecA a nil = inr λ{()}
  dec-∈-list eqdecA a (l :: b) with eqdecA a b
  ...                    | inl idp = inl (inr idp)
  ...                    | inr a≠b with dec-∈-list eqdecA a l
  ...                             | inl a∈l = inl (inl a∈l)
  ...                             | inr a∉l = inr λ{(inl a∈l) → a∉l a∈l; (inr a=b) → a≠b a=b}

{- Univalence and known consequences -}

  record is-equiv {i j} {A : Set i} {B : Set j} (f : A → B) : Set (i ⊔ j)
    where
    field
      g : B → A
      f-g : (b : B) → f (g b) == b
      g-f : (a : A) → g (f a) == a
      adj : (a : A) → ap f (g-f a) == f-g (f a)

  _≃_ : ∀ {i j} → (A : Set i) (B : Set j) → Set (i ⊔ j)
  A ≃ B = Σ (A → B) λ f → is-equiv f

  postulate
    univalence : ∀ {i} {A B : Set i} → (A == B) ≃ (A ≃ B)
    funext : ∀ {i} {A B : Set i} (f g : A → B) → (∀ x → f x == g x) → f == g
    funext-dep : ∀ {i} {A : Set i} {B : A → Set i} (f g : ∀ a → B a) → (∀ a → f a == g a) → f == g

{- Definition of the first H-levels -}

  is-contr : ∀ {i} → Set i → Set i
  is-contr A = Σ A (λ x → ((y : A) → x == y))

  is-prop : ∀ {i} → Set i → Set i
  is-prop A = ∀ (x y : A) → is-contr (x == y)

  is-set : ∀{i} → Set i → Set i
  is-set A = ∀ (x y : A) → is-prop (x == y)

  has-all-paths : ∀ {i} → Set i → Set i
  has-all-paths A = ∀ (a b : A) → a == b

  {- Known properties of H-levels -}
  postulate
    has-all-paths-idp : ∀ {i} (A : Set i) (pathsA : has-all-paths A) (a : A) → pathsA a a == idp
    has-all-paths-is-prop : ∀ {i} → {A : Set i} → has-all-paths A → is-prop A
    is-prop-has-all-paths : ∀ {i} → {A : Set i} → is-prop A → has-all-paths A
    eqdec-is-set : ∀ {i} {A : Set i} → eqdec A → is-set A

  is-setℕ : is-set ℕ
  is-setℕ = eqdec-is-set eqdecℕ


module Tactic.Reflection.DeBruijn where

open import Prelude hiding (abs)
open import Builtin.Reflection
open import Container.Traversable

record DeBruijn {a} (A : Set a) : Set a where
  field
    strengthenFrom : (from n : Nat) → A → Maybe A
    weakenFrom     : (from n : Nat) → A → A

  strengthen : Nat → A → Maybe A
  strengthen 0 = just
  strengthen n = strengthenFrom 0 n

  weaken : Nat → A → A
  weaken zero = id
  weaken n    = weakenFrom 0 n

open DeBruijn {{...}} public

patternBindings : List (Arg Pattern) → Nat
patternBindings = binds
  where
    binds : List (Arg Pattern) → Nat
    bind  : Pattern → Nat

    binds []             = 0
    binds (arg _ a ∷ as) = bind a + binds as

    bind (con c ps) = binds ps
    bind dot        = 1
    bind (var _)    = 1
    bind (lit l)    = 0
    bind (proj x)   = 0
    bind absurd     = 0

private
  Str : Set → Set
  Str A = Nat → Nat → A → Maybe A

  strVar : Str Nat
  strVar lo n x = if      x <? lo     then just x
                  else if x <? lo + n then nothing
                  else                just (x - n)

  strArgs    : Str (List (Arg Term))
  strArg     : Str (Arg Term)
  strSort    : Str Sort
  strClauses : Str (List Clause)
  strClause  : Str Clause
  strAbsTerm : Str (Abs Term)
  strAbsType : Str (Abs Type)

  strTerm : Str Term
  strTerm lo n (var x args)  = var <$> strVar lo n x <*> strArgs lo n args
  strTerm lo n (con c args)  = con c <$> strArgs lo n args
  strTerm lo n (def f args)  = def f <$> strArgs lo n args
  strTerm lo n (meta x args) = meta x <$> strArgs lo n args
  strTerm lo n (lam v t)     = lam v <$> strAbsTerm lo n t
  strTerm lo n (pi a b)      = pi    <$> strArg lo n a <*> strAbsType lo n b
  strTerm lo n (agda-sort s) = agda-sort <$> strSort lo n s
  strTerm lo n (lit l)       = just (lit l)
  strTerm lo n (pat-lam _ _) = just unknown -- todo
  strTerm lo n unknown       = just unknown

  strAbsTerm lo n (abs s t)  = abs s <$> strTerm (suc lo) n t
  strAbsType lo n (abs s t)  = abs s <$> strTerm (suc lo) n t

  strArgs    lo n []         = just []
  strArgs    lo n (x ∷ args) = _∷_   <$> strArg  lo n x <*> strArgs lo n args
  strArg     lo n (arg i v)  = arg i <$> strTerm lo n v
  strSort    lo n (set t)    = set   <$> strTerm lo n t
  strSort    lo n (lit l)    = just (lit l)
  strSort    lo n unknown    = just unknown

  strClauses lo k [] = just []
  strClauses lo k (c ∷ cs) = _∷_ <$> strClause lo k c <*> strClauses lo k cs

  strClause lo k (clause ps b)      = clause ps <$> strTerm (lo + patternBindings ps) k b
  strClause lo k (absurd-clause ps) = just (absurd-clause ps)

private
  Wk : Set → Set
  Wk A = Nat → Nat → A → A

  wkVar : Wk Nat
  wkVar lo k x = if x <? lo then x else x + k

  wkArgs    : Wk (List (Arg Term))
  wkArg     : Wk (Arg Term)
  wkSort    : Wk Sort
  wkClauses : Wk (List Clause)
  wkClause  : Wk Clause
  wkAbsTerm : Wk (Abs Term)

  wk : Wk Term
  wk lo k (var x args)  = var (wkVar lo k x) (wkArgs lo k args)
  wk lo k (con c args)  = con c (wkArgs lo k args)
  wk lo k (def f args)  = def f (wkArgs lo k args)
  wk lo k (meta x args) = meta x (wkArgs lo k args)
  wk lo k (lam v t)     = lam v (wkAbsTerm lo k t)
  wk lo k (pi a b)      = pi (wkArg lo k a) (wkAbsTerm lo k b)
  wk lo k (agda-sort s) = agda-sort (wkSort lo k s)
  wk lo k (lit l)       = lit l
  wk lo k (pat-lam cs args) = pat-lam (wkClauses lo k cs) (wkArgs lo k args)
  wk lo k unknown       = unknown

  wkAbsTerm lo k (abs s t)  = abs s (wk (suc lo) k t)
  wkArgs    lo k [] = []
  wkArgs    lo k (x ∷ args) = wkArg lo k x ∷ wkArgs lo k args
  wkArg     lo k (arg i v)  = arg i (wk lo k v)
  wkSort    lo k (set t)    = set (wk lo k t)
  wkSort    lo k (lit n)    = lit n
  wkSort    lo k unknown    = unknown

  wkClauses lo k [] = []
  wkClauses lo k (c ∷ cs) = wkClause lo k c ∷ wkClauses lo k cs

  wkClause lo k (clause ps b)      = clause ps (wk (lo + patternBindings ps) k b)
  wkClause lo k (absurd-clause ps) = absurd-clause ps

-- Instances --

DeBruijnTraversable : ∀ {a} {F : Set a → Set a} {{_ : Traversable F}}
                        {A : Set a} {{_ : DeBruijn A}} → DeBruijn (F A)
strengthenFrom {{DeBruijnTraversable}} lo k = traverse (strengthenFrom lo k)
weakenFrom     {{DeBruijnTraversable}} lo k = fmap     (weakenFrom lo k)

instance
  DeBruijnNat : DeBruijn Nat
  strengthenFrom {{DeBruijnNat}} = strVar
  weakenFrom     {{DeBruijnNat}} = wkVar

  DeBruijnTerm : DeBruijn Term
  strengthenFrom {{DeBruijnTerm}} = strTerm
  weakenFrom     {{DeBruijnTerm}} = wk

  DeBruijnList : ∀ {a} {A : Set a} {{_ : DeBruijn A}} → DeBruijn (List A)
  DeBruijnList = DeBruijnTraversable

  DeBruijnVec : ∀ {a} {A : Set a} {{_ : DeBruijn A}} {n : Nat} → DeBruijn (Vec A n)
  DeBruijnVec = DeBruijnTraversable

  DeBruijnArg : {A : Set} {{_ : DeBruijn A}} → DeBruijn (Arg A)
  DeBruijnArg = DeBruijnTraversable

  DeBruijnMaybe : {A : Set} {{_ : DeBruijn A}} → DeBruijn (Maybe A)
  DeBruijnMaybe = DeBruijnTraversable

-- Strip bound names (to ensure _==_ checks α-equality)
-- Doesn't touch pattern variables in pattern lambdas.

mutual

  stripBoundNames : Term → Term
  stripBoundNames (var x args)      = var x (stripArgs args)
  stripBoundNames (con c args)      = con c (stripArgs args)
  stripBoundNames (def f args)      = def f (stripArgs args)
  stripBoundNames (lam v t)         = lam v (stripAbs t)
  stripBoundNames (pat-lam cs args) = pat-lam (stripClauses cs) (stripArgs args)
  stripBoundNames (pi a b)          = pi (stripArg a) (stripAbs b)
  stripBoundNames (agda-sort s)     = agda-sort (stripSort s)
  stripBoundNames (lit l)           = lit l
  stripBoundNames (meta x args)     = meta x (stripArgs args)
  stripBoundNames unknown           = unknown

  private
    stripArgs : List (Arg Term) → List (Arg Term)
    stripArgs [] = []
    stripArgs (x ∷ xs) = stripArg x ∷ stripArgs xs

    stripArg : Arg Term → Arg Term
    stripArg (arg i t) = arg i (stripBoundNames t)

    stripAbs : Abs Term → Abs Term
    stripAbs (abs _ t) = abs "" (stripBoundNames t)

    stripClauses : List Clause → List Clause
    stripClauses [] = []
    stripClauses (x ∷ xs) = stripClause x ∷ stripClauses xs

    stripClause : Clause → Clause
    stripClause (clause ps t) = clause ps (stripBoundNames t)
    stripClause (absurd-clause ps) = absurd-clause ps

    stripSort : Sort → Sort
    stripSort (set t) = set (stripBoundNames t)
    stripSort (lit n) = lit n
    stripSort unknown = unknown


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

module Cubical.Data.Strict2Group.Explicit.Interface where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Group.Base
open import Cubical.Data.Sigma
open import Cubical.Data.Strict2Group.Explicit.Base
open import Cubical.Data.Strict2Group.Explicit.Notation

module S2GInterface {ℓ : Level} ((strict2groupexp C₀ C₁ s t i ∘ si ti s∘ t∘ isMorph∘ assoc∘ lUnit∘ rUnit∘) : Strict2GroupExp ℓ) where

  open S2GBaseNotation C₀ C₁ s t i ∘ public

  module Identities1 where
    -- to be consistent with the other notation
    tarId = ti
    srcId = si
    -- identity is preserved by id
    id1₀≡1₁ : id 1₀ ≡ 1₁
    id1₀≡1₁ = morphId {G = C₀} {H = C₁} i

  open Identities1 public

  module C₁×₀C₁ where

    -- the composable morphisms as record type
    record Co : Type ℓ where
      constructor co
      field
        g f : TC₁
        coh : CohCond g f

    -- syntax
    𝓁 𝓇 : Co → TC₁
    𝓁 = Co.g
    𝓇 = Co.f
    𝒸 : (gfc : Co) → CohCond (𝓁 gfc) (𝓇 gfc)
    𝒸 = Co.coh

    -- compose a co object using ∘
    ⊙ : Co → TC₁
    ⊙ gfc = ∘ (𝓁 gfc) (𝓇 gfc) (𝒸 gfc)

    -- basically ∘, but for the sake of interfacing, we don't want to use ∘
    ⊙' : (g f : TC₁) → CohCond g f → TC₁
    ⊙' g f c = ⊙ (co g f c)

    -- interface for those names aswell
    src⊙ : (gfc : Co) → src (⊙ gfc) ≡ src (𝓇 gfc)
    src⊙ (co g f c) = s∘ g f c

    tar⊙ : (gfc : Co) → tar (⊙ gfc) ≡ tar (𝓁 gfc)
    tar⊙ (co g f c) = t∘ g f c

    src⊙' : (g f : TC₁) → (c : CohCond g f) → src (⊙' g f c) ≡ src f
    src⊙' g f c = src⊙ (co g f c)

    tar⊙' : (g f : TC₁) → (c : CohCond g f) → tar (⊙' g f c) ≡ tar g
    tar⊙' g f c = tar⊙ (co g f c)

    -- multiplication in C₁×₀C₁
    _∙Co_ : (gfc gfc' : Co) → Co
    (co g f coh) ∙Co (co g' f' coh') =
      co (g ∙₁ g') (f ∙₁ f')
        (src∙₁ g g' ∙ cong (_∙₀ src g') coh ∙ cong (tar f ∙₀_) coh' ∙ sym (tar∙₁ f f'))

    -- unit element w.r.t. ∙c. Too bad there is no \_c
    1c : Co
    1c = co 1₁ 1₁ ((cong src (sym id1₀≡1₁)) ∙∙ si 1₀ ∙∙ sym (ti 1₀) ∙ cong tar id1₀≡1₁)

    -- the interchange law reformulated using ⊙
    isMorph⊙ : (gfc gfc' : Co) → ⊙ (gfc ∙Co gfc') ≡ ⊙ gfc ∙₁ ⊙ gfc'
    isMorph⊙ (co _ _ c) (co _ _ c') = isMorph∘ c c'

    -- associator notation
    assoc⊙' : (h g f : TC₁) → (c : CohCond g f) → (c' : CohCond h g) → ⊙' (⊙' h g c') f ((src⊙' h g c') ∙ c) ≡ ⊙' h (⊙' g f c) (c' ∙ (sym (tar⊙' g f c)))
    assoc⊙' h g f c c' = assoc∘ c c'

    -- the left and right unit laws reformulated using ⊙
    lUnit⊙ : (f : TC₁) → ⊙ (co (id (tar f)) f (srcId (tar f))) ≡ f
    lUnit⊙ = lUnit∘
    rUnit⊙ : (f : TC₁) → ⊙ (co f (id (src f)) (sym (tarId (src f)))) ≡ f
    rUnit⊙ = rUnit∘

    -- the path component of f in C₁
    ΣC₁p : (f : TC₁) → Type ℓ
    ΣC₁p f = Σ[ f' ∈ TC₁ ] (f ≡ f')

    private
      -- for given g, the type of f that g can be precomposed with
      _∘* : TC₁ → Type ℓ
      g ∘* = Σ[ f ∈ TC₁ ] (CohCond g f)
      -- for given f, the type of g that f can be postcomposed with
      *∘_ : TC₁ → Type ℓ
      *∘ f = Σ[ g ∈ TC₁ ] (CohCond g f)

      -- alternate notation for ∘
      -- this is used in ∘*≡ to λ-abstract in cong
      _∘*_ : (g : TC₁) (fc : g ∘*) → TC₁
      _∘*_ g (f , c) = ∘ g f c
      _*∘_ : (f : TC₁) (gc : *∘ f) → TC₁
      _*∘_ f (g , c) = ∘ g f c

      -- since we have proof irrelevance in C₀ we can show that f ≡ f' → g∘f ≡ g∘f'
      ∘*≡ : (g : TC₁) → (fc : g ∘*) → (f'p : ΣC₁p (fst fc)) → g ∘* fc ≡ g ∘* ((fst f'p) , snd fc ∙ cong tar (snd f'p))
      ∘*≡ g fc f'p = cong (g ∘*_) (ΣPathP (snd f'p , isProp→PathP (λ j → Group.setStruc C₀ (src g) (tar (snd f'p j))) (snd fc) (snd fc ∙ cong tar (snd f'p))))
      *∘≡ : (f : TC₁) → (gc : *∘ f) → (g'p : ΣC₁p (fst gc)) → f *∘ gc ≡ f *∘ (fst g'p , ((cong src (sym (snd g'p))) ∙ snd gc))
      *∘≡ f gc g'p = cong (_*∘_ f) (ΣPathP ((snd g'p) , (isProp→PathP (λ j → Group.setStruc C₀ (src (snd g'p j)) (tar f)) (snd gc) (cong src (sym (snd g'p)) ∙ snd gc))))

    -- ⊙ respecs paths on the right
    ⊙≡ : ((co g f c) : Co) → (f'p : ΣC₁p f) → ⊙ (co g f c) ≡ ⊙ (co g (fst f'p) (c ∙ (cong tar (snd f'p))))
    ⊙≡ (co g f c) (f' , f≡f') = ∘*≡ g (f , c) (f' , f≡f')

    -- ⊙ respects paths on the left
    ≡⊙ : ((co g f c) : Co) → ((g' , g≡g') : ΣC₁p g) → ⊙ (co g f c) ≡ ⊙ (co g' f (cong src (sym g≡g') ∙ c))
    ≡⊙ (co g f c) (g' , g≡g') = *∘≡ f (g , c) (g' , g≡g')

    -- ⊙ resepcts paths on the coherence condition
    ⊙≡c : ((co g f c) : Co) → (c' : CohCond g f) → ⊙ (co g f c) ≡ ⊙ (co g f c')
    ⊙≡c (co g f c) c' = cong (λ z → ⊙ (co g f z)) (Group.setStruc C₀ (src g) (tar f) c c')

    -- implicit version of ⊙≡c
    ⊙≡c~ : {g f : TC₁} (c c' : CohCond g f) → ⊙ (co g f c) ≡ ⊙ (co g f c')
    ⊙≡c~ {g} {f} c c' = cong (λ z → ⊙ (co g f z)) (Group.setStruc C₀ (src g) (tar f) c c')

    -- ⊙ respecting paths on the left also changes the coherence condition so this should be used instead
    ≡⊙c* : {g g' f : TC₁} (c : CohCond g f) (g≡g' : g ≡ g') (c' : CohCond g' f) → ⊙' g f c ≡ ⊙' g' f c'
    ≡⊙c* {g} {g'} {f} c g≡g' c' = (≡⊙ (co g f c) (g' , g≡g')) ∙ ⊙≡c~ ((cong src (sym g≡g')) ∙ c) c'

    -- ⊙ respecting paths on the right also changes the coherence condition so this should be used instead
    ⊙≡c* : {g f f' : TC₁} (c : CohCond g f) (f≡f' : f ≡ f') (c' : CohCond g f') → ⊙' g f c ≡ ⊙' g f' c'
    ⊙≡c* {g} {f} {f'} c f≡f' c' = (⊙≡ (co g f c) (f' , f≡f')) ∙ ⊙≡c~ (c ∙ cong tar f≡f') c'

    -- use the left and right unit law with an arbitrary coherence proof c
    lUnit⊙c : (f : TC₁) → (c : CohCond (id (tar f)) f) → ⊙ (co (id (tar f)) f c) ≡ f
    lUnit⊙c f c = (⊙≡c (co (id (tar f)) f c) (srcId (tar f))) ∙ (lUnit⊙ f)
    rUnit⊙c : (f : TC₁) → (c : CohCond f (id (src f))) → ⊙ (co f (id (src f)) c) ≡ f
    rUnit⊙c f c = (⊙≡c (co f (id (src f)) c) (sym (tarId (src f)))) ∙ (rUnit⊙ f)

  open C₁×₀C₁ public

  module Identities2 where
    -- source and target of unit element
    tar1₁≡1₀ : tar 1₁ ≡ 1₀
    tar1₁≡1₀ = morphId {G = C₁} {H = C₀} t
    src1₁≡1₀ = morphId {G = C₁} {H = C₀} s

    -- taking the source is the same as the target of the identity of the source
    src≡tarIdSrc : (f : TC₁) → CohCond f (id (src f))
    src≡tarIdSrc f = sym (ti (src f))

  open Identities2 public