------------------------------------------------------------------------------
-- Agda-Prop Library.
-- Theorems of ∨ connective.
------------------------------------------------------------------------------

open import Data.Nat using ( ℕ )

module Data.PropFormula.Theorems.Disjunction ( n : ℕ ) where

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

open import Data.PropFormula.Syntax n
open import Data.PropFormula.Theorems.Classical n
open import Data.PropFormula.Properties n

open import Data.PropFormula.Theorems.Conjunction n using ( ∧-dmorgan₁ )
open import Data.PropFormula.Theorems.Implication n using ( vanDalen244e )

open import Function using ( _$_; _∘_ )
open import Relation.Binary.PropositionalEquality using ( _≡_ )

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

-- Theorem.
∨-assoc₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∨ (ψ ∨ γ)
  → Γ ⊢ (φ ∨ ψ) ∨ γ

-- Proof.
∨-assoc₁ {Γ}{φ}{ψ}{γ} =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∨-intro₁ γ
          (∨-intro₁ ψ
            (assume φ)))
        (⊃-elim
          (⊃-intro
            (∨-elim {Γ = Γ , ψ ∨ γ}
              (∨-intro₁ γ
                (∨-intro₂ φ (assume {Γ = Γ , ψ ∨ γ} ψ)))
              (∨-intro₂ (φ ∨ ψ) (assume {Γ = Γ , ψ ∨ γ} γ))))
          (assume (ψ ∨ γ)))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-assoc₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∨ ψ) ∨ γ
  → Γ ⊢ φ ∨ (ψ ∨ γ)

-- Proof.
∨-assoc₂ {Γ}{φ}{ψ}{γ} =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (⊃-elim
          (⊃-intro
            (∨-elim {Γ  = Γ , φ ∨ ψ}
              (∨-intro₁ (ψ ∨ γ)
                (assume {Γ = Γ , φ ∨ ψ} φ))
              (∨-intro₂ φ
                (∨-intro₁ γ
                  (assume {Γ = Γ , φ ∨ ψ} ψ)))))
          (assume (φ ∨ ψ)))
        (∨-intro₂ φ
          (∨-intro₂ ψ
            (assume γ)))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-assoc
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∨ (ψ ∨ γ)) ⇔ ((φ ∨ ψ) ∨ γ)

-- Proof.
∨-assoc {φ = φ}{ψ}{γ} =
  ⇔-intro
    (∨-assoc₁ (assume (φ ∨ (ψ ∨ γ))))
    (∨-assoc₂ (assume (φ ∨ ψ ∨ γ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-comm
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ ψ ∨ φ

-- Proof.
∨-comm {φ = φ}{ψ} =
  ⊃-elim $
    ⊃-intro
      (∨-elim
        (∨-intro₂ ψ
          (assume φ))
        (∨-intro₁ φ $
           assume ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dist₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∨ (ψ ∧ γ)
  → Γ ⊢ (φ ∨ ψ) ∧ (φ ∨ γ)

-- Proof.
∨-dist₁ {Γ}{φ}{ψ}{γ} =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∧-intro
          (∨-intro₁ ψ
            (assume φ))
          (∨-intro₁ γ
            (assume φ)))
        (∧-intro
          (∨-intro₂ φ
            (∧-proj₁
              (assume (ψ ∧ γ))))
          (∨-intro₂ φ
            (∧-proj₂
              (assume (ψ ∧ γ)))))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dist₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∨ ψ) ∧ (φ ∨ γ)
  → Γ ⊢ φ ∨ (ψ ∧ γ)

-- Proof.
∨-dist₂ {Γ}{φ}{ψ}{γ} Γ⊢⟪φ∨ψ⟫∧⟪φ∨γ⟫ =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∨-intro₁ (ψ ∧ γ)
          (assume φ))
        (⊃-elim
          (⊃-intro
            (∨-elim
              (∨-intro₁ (ψ ∧ γ)
                (assume {Γ = Γ , ψ} φ))
              (∨-intro₂ φ
                (∧-intro
                  (weaken γ
                    (assume ψ))
                  (assume {Γ = Γ , ψ} γ)))))
          (∧-proj₂
            (weaken ψ Γ⊢⟪φ∨ψ⟫∧⟪φ∨γ⟫)))))
    (∧-proj₁ Γ⊢⟪φ∨ψ⟫∧⟪φ∨γ⟫)
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dist
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∨ (ψ ∧ γ)) ⇔ ((φ ∨ ψ) ∧ (φ ∨ γ))

-- Proof.
∨-dist {φ = φ}{ψ}{γ} =
  ⇔-intro
    (∨-dist₁ (assume (φ ∨ (ψ ∧ γ))))
    (∨-dist₂ (assume (φ ∨ ψ ∧ (φ ∨ γ))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-equiv
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ ¬ (¬ φ ∧ ¬ ψ)
∨-to-¬¬∧¬ = ∨-equiv

-- Proof.
∨-equiv {Γ}{φ}{ψ} Γ⊢φ∨ψ =
  ¬-intro
    (⊃-elim
      (⊃-intro
        (∨-elim {Γ = Γ , ¬ φ ∧ ¬ ψ}
          (¬-elim
            (weaken φ
              (∧-proj₁
                (assume (¬ φ ∧ ¬ ψ))))
            (assume {Γ = Γ , ¬ φ ∧ ¬ ψ }  φ))
          (¬-elim
            (weaken ψ
              (∧-proj₂
                (assume (¬ φ ∧ ¬ ψ))))
            (assume {Γ = Γ , ¬ φ ∧ ¬ ψ}  ψ))))
      (weaken (¬ φ ∧ ¬ ψ) Γ⊢φ∨ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dmorgan₁
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ (φ ∨ ψ)
  → Γ ⊢ ¬ φ ∧ ¬ ψ
¬∨-to-¬∧¬ = ∨-dmorgan₁

-- Proof.
∨-dmorgan₁ {Γ}{φ}{ψ} =
  ⊃-elim $
    ⊃-intro $
      ∧-intro
        (¬-intro $
          ¬-elim
            (weaken φ $
              assume (¬ (φ ∨ ψ)))
            (∨-intro₁ ψ $
              assume {Γ = Γ , ¬ (φ ∨ ψ)} φ))
        (¬-intro $
          ¬-elim
            (weaken ψ $
              assume (¬ (φ ∨ ψ)))
            (∨-intro₂ φ $
              assume {Γ = Γ , ¬ (φ ∨ ψ)} ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dmorgan₂
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ φ ∧ ¬ ψ
  → Γ ⊢ ¬ (φ ∨ ψ)
¬∧¬-to-¬∨ = ∨-dmorgan₂

-- Proof.
∨-dmorgan₂ {φ = φ}{ψ} Γ⊢¬φ∧¬ψ  =
  ¬-intro
    (∨-elim
      (¬-elim
        (weaken φ
          (∧-proj₁ Γ⊢¬φ∧¬ψ ))
        (assume φ))
      (¬-elim
        (weaken ψ
          (∧-proj₂ Γ⊢¬φ∧¬ψ ))
        (assume ψ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-dmorgan
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ (φ ∨ ψ) ⇔ (¬ φ ∧ ¬ ψ)
¬∨-⇔-¬∧¬ = ∨-dmorgan

-- Proof.
∨-dmorgan {φ = φ}{ψ} =
  ⇔-intro
    (∨-dmorgan₁
      (assume (¬ (φ ∨ ψ))))
    (∨-dmorgan₂
      (assume (¬ φ ∧ ¬ ψ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
lem1
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ ¬ φ ∨ ¬ ¬ ψ
  → Γ ⊢ φ ∨ ψ
¬¬∨¬¬-to-∨ = lem1

-- Proof.
lem1 {φ = φ}{ψ} =
  ⊃-elim $
    ⊃-intro $
      ∨-elim
        (∨-intro₁ ψ $
          ⊃-elim vanDalen244e $ assume $ ¬ ¬ φ)
        (∨-intro₂ φ $
          ⊃-elim vanDalen244e $ assume $ ¬ ¬ ψ)
-------------------------------------------------------------------------- ∎

-- Theorem.
lem2
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ (φ ∨ ψ) ∧ ¬ ψ
  → Γ ⊢ φ

-- Proof.
lem2 {φ = φ}{ψ} Γ⊢⟪φ∨ψ⟫∧¬ψ =
  ⊃-elim
    (⊃-intro $
      (∨-elim
        (assume φ)
        (⊥-elim φ
          (¬-elim
            (weaken ψ (∧-proj₂ Γ⊢⟪φ∨ψ⟫∧¬ψ))
            (assume ψ)))))
    (∧-proj₁ Γ⊢⟪φ∨ψ⟫∧¬ψ)
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₀
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∨ ψ → Γ ⊢ ¬ φ ∨ γ
  → Γ ⊢ ψ ∨ γ

-- Proof.
resolve₀ {Γ}{φ}{ψ}{γ} Γ⊢φ∨ψ Γ⊢¬φ∨γ =
 lem1 $
   ∧-dmorgan₁ $
     ¬-intro $
       ¬-elim
         (lem2 {Γ = Γ , ¬ ψ ∧ ¬ γ} $
           ∧-intro
             (weaken (¬ ψ ∧ ¬ γ) Γ⊢¬φ∨γ)
             (∧-proj₂ $ assume $ ¬ ψ ∧ ¬ γ))
         (lem2 $
           ∧-intro
             (weaken (¬ ψ ∧ ¬ γ) Γ⊢φ∨ψ)
             (∧-proj₁ $ assume $ ¬ ψ ∧ ¬ γ))
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ ψ ∨ φ → Γ ⊢ ¬ φ ∨ γ
  → Γ ⊢ ψ ∨ γ

-- Proof.
resolve₁ = resolve₀ ∘ ∨-comm
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∨ ψ → Γ ⊢ γ ∨ ¬ φ
  → Γ ⊢ ψ ∨ γ

-- Proof.
resolve₂ Γ⊢φ∨ψ Γ⊢γ∨¬φ = resolve₀ Γ⊢φ∨ψ (∨-comm Γ⊢γ∨¬φ)
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₃
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ ψ ∨ φ → Γ ⊢ γ ∨ ¬ φ
  → Γ ⊢ ψ ∨ γ

-- Proof.
resolve₃ Γ⊢ψ∨φ Γ⊢γ∨¬φ = resolve₀ (∨-comm Γ⊢ψ∨φ) (∨-comm Γ⊢γ∨¬φ)
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₄
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ φ ∨ ψ → Γ ⊢ φ
  → Γ ⊢ ψ

-- Proof.
resolve₄ {φ = φ}{ψ} Γ⊢¬φ∨ψ Γ⊢φ =
 ⊃-elim
   (⊃-intro $
     ∨-elim
       (assume ψ)
       (assume ψ))
   (resolve₀
     (∨-intro₁ ψ Γ⊢φ)
     Γ⊢¬φ∨ψ)
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₅
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ψ ∨ ¬ φ
  → Γ ⊢ φ
  → Γ ⊢ ψ

-- Proof.
resolve₅ = resolve₄ ∘ ∨-comm
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₆
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ψ ∨ φ → Γ ⊢ ¬ φ
  → Γ ⊢ ψ

-- Proof.
resolve₆ {φ = φ}{ψ} Γ⊢ψ∨φ Γ⊢¬φ =
 ⊃-elim
   (⊃-intro $
     ∨-elim
       (assume ψ)
       (assume ψ))
   (resolve₀ (∨-comm Γ⊢ψ∨φ) (∨-intro₁ ψ Γ⊢¬φ))
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₇
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ∨ ψ → Γ ⊢ ¬ φ
  → Γ ⊢ ψ

-- Proof.
resolve₇ Γ⊢φ∨ψ Γ⊢¬φ = resolve₆ (∨-comm Γ⊢φ∨ψ) Γ⊢¬φ
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₈
  : ∀ {Γ} {φ}
  → Γ ⊢ φ → Γ ⊢ ¬ φ
  → Γ ⊢ ⊥

-- Proof.
resolve₈ Γ⊢φ Γ⊢¬φ = ¬-elim Γ⊢¬φ Γ⊢φ
-------------------------------------------------------------------------- ∎

-- Theorem.
resolve₉
  : ∀ {Γ} {φ}
  → Γ ⊢ ¬ φ → Γ ⊢ φ
  → Γ ⊢ ⊥

-- Proof.
resolve₉ = ¬-elim
-------------------------------------------------------------------------- ∎

-- Theorem.
subst⊢∨₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ⊃ γ
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ γ ∨ ψ

-- Proof.
subst⊢∨₁ {φ = φ}{ψ}{γ} Γ⊢φ⊃γ Γ⊢φ∨ψ =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∨-intro₁ ψ (⊃-elim (weaken φ Γ⊢φ⊃γ) (assume φ)))
        (∨-intro₂ γ (assume ψ))))
    Γ⊢φ∨ψ
-------------------------------------------------------------------------- ∎

-- Theorem.
subst⊢∨₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ ψ ⊃ γ
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ φ ∨ γ

-- Proof.
subst⊢∨₂ {φ = φ}{ψ}{γ} Γ⊢ψ⊃γ Γ⊢φ∨ψ =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∨-intro₁ γ (assume φ))
        (∨-intro₂ φ (⊃-elim (weaken ψ Γ⊢ψ⊃γ) (assume ψ)))))
    Γ⊢φ∨ψ
-------------------------------------------------------------------------- ∎

-- Theorem.
∨-to-¬⊃
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ ¬ φ ⊃ ψ

-- Proof.
∨-to-¬⊃ {Γ}{φ}{ψ} Γ⊢φ∨ψ =
  ⊃-intro
    (⊃-elim
      (⊃-intro
        (∨-elim {Γ = Γ , ¬ φ}
          (⊥-elim ψ
            (¬-elim
              (weaken φ
                (assume (¬ φ)))
            (assume {Γ = Γ , ¬ φ} φ)))
          (assume {Γ = Γ , ¬ φ} ψ)))
      (weaken (¬ φ) Γ⊢φ∨ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
φ∨⊥-to-φ
 : ∀ {Γ} {φ}
 → Γ ⊢ φ ∨ ⊥
 → Γ ⊢ φ

-- Proof.
φ∨⊥-to-φ {φ = φ} Γ⊢φ∨⊥ =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (assume φ)
        (⊥-elim φ (assume ⊥))))
    Γ⊢φ∨⊥
-------------------------------------------------------------------------- ∎

-- Theorem.
subst⊢∨₁≡
  : ∀ {Γ} {φ ψ γ}
  → φ ≡ γ
  → Γ ⊢ φ ∨ ψ
  → Γ ⊢ γ ∨ ψ

-- Proof.
subst⊢∨₁≡ {φ = φ}{ψ}{γ} φ≡γ Γ⊢φ∨ψ =
  ⊃-elim
    (⊃-intro
      (∨-elim
         (∨-intro₁ ψ (subst φ≡γ (assume φ)))
        (∨-intro₂ γ (assume ψ))))
    Γ⊢φ∨ψ
-------------------------------------------------------------------------- ∎


-- Andreas and James, Nov 2011 and Oct 2012
-- function with flexible arity
-- {-# OPTIONS --no-coverage-check #-}
-- {-# OPTIONS -v tc.cover:20 #-}
module FlexibleInterpreter where

open import Common.Prelude using (Nat; zero; suc)
open import Common.MAlonzo hiding (main)
open import Common.Equality

data Ty : Set where
  nat : Ty
  arr : Ty -> Ty -> Ty

data Exp : Ty -> Set where
  zero : Exp nat
  suc  : Exp (arr nat nat)
  pred : Exp (arr nat nat)
  app  : {a b : Ty} -> Exp (arr a b) -> Exp a -> Exp b

Sem : Ty -> Set
Sem nat = Nat
Sem (arr a b) = Sem a -> Sem b

module Flex where
  -- Haskell does not seem to handle this

  eval' : (a : Ty) -> Exp a -> Sem a
  eval' ._ zero = zero
  eval' ._ suc  = suc
  eval' b (app f e) = eval' _ f (eval' _ e)
  eval' .(arr nat nat) pred zero = zero
  eval' ._ pred (suc n) = n

  eval : {a : Ty} -> Exp a -> Sem a
  eval zero         = zero
  eval suc          = suc
  eval pred zero    = zero
  eval pred (suc n) = n
  eval (app f e)    = eval f (eval e)

  testEvalSuc : ∀ {n} → eval suc n ≡ suc n
  testEvalSuc = refl

  testEvalPredZero : eval pred zero ≡ zero
  testEvalPredZero = refl

  testEvalPredSuc : ∀ {n} → eval pred (suc n) ≡ n
  testEvalPredSuc = refl

module Rigid where
  -- This executes fine

  eval : {a : Ty} -> Exp a -> Sem a
  eval zero      = zero
  eval suc       = suc
  eval pred      = λ { zero    -> zero
                     ; (suc n) -> n
                     }
  eval (app f e) = eval f (eval e)

open Flex

expr = (app pred (app suc (app suc zero)))
test = eval expr
main = mainPrintNat test
-- should print 1


------------------------------------------------------------------------
-- Library stuff

data ⊥ : Set where

record ⊤ : Set where
  constructor tt

data Bool : Set where
  true false : Bool

if_then_else_ : ∀ {A : Set} → Bool → A → A → A
if true  then t else f = t
if false then t else f = f

data ℕ : Set where
  zero : ℕ
  suc : ℕ → ℕ

{-# BUILTIN NATURAL ℕ #-}

_∸_ : ℕ → ℕ → ℕ
m      ∸ zero  = m
zero   ∸ suc n = zero
suc m  ∸ suc n = m ∸ n

_≤_ : ℕ → ℕ → Bool
zero   ≤ n      = true
suc m  ≤ zero   = false
suc m  ≤ suc n  = m ≤ n

data _⊎_ (A : Set) (B : Set) : Set where
  inj₁ : (x : A) → A ⊎ B
  inj₂ : (y : B) → A ⊎ B

[_,_]₁ : ∀ {A : Set} {B : Set} {C : A ⊎ B → Set₁} →
        ((x : A) → C (inj₁ x)) → ((x : B) → C (inj₂ x)) →
        ((x : A ⊎ B) → C x)
[ f , g ]₁ (inj₁ x) = f x
[ f , g ]₁ (inj₂ y) = g y

data _≡_ {A : Set}(x : A) : A → Set where
  refl : x ≡ x

record Σ (X : Set) (Y : X → Set) : Set where
  constructor _,_
  field
    proj₁ : X
    proj₂ : Y proj₁

_×_ : Set → Set → Set
X × Y = Σ X λ _ → Y

record Sig : Set₁ where
  constructor _◃_
  field
    Parameter : Set
    Arity     : Parameter → Set

open Sig public

⟦_⟧ : Sig → Set → Set
⟦ P ◃ A ⟧ X = Σ P λ p → A p → X

Alg : Sig → Set → Set
Alg Σ X = ⟦ Σ ⟧ X → X

data μ (C : Sig) : Set where
  sup : Alg C (μ C)

const^C : Set → Sig
const^C X = X ◃ λ _ → ⊥

_⊎^C_ : Sig → Sig → Sig
(P₁ ◃ A₁) ⊎^C (P₂ ◃ A₂) = (P₁ ⊎ P₂) ◃ [ A₁ , A₂ ]₁

_⋆^C_ : Sig → Set → Sig
C ⋆^C X = const^C X ⊎^C C

_⋆_ : Sig → Set → Set
C ⋆ X = μ (C ⋆^C X)

do : ∀ {C X} → Alg C (C ⋆ X)
do (p , k) = sup (inj₂ p , k)

record Monad (T : Set → Set) : Set₁ where
  infixl 1 _>>=_
  field
    return  : ∀ {X} → X → T X
    _>>=_   : ∀ {X Y} → T X → (X → T Y) → T Y

monad : ∀ {Σ} → Monad (_⋆_ Σ)
monad {Σ} = record
  { return = λ x → sup (inj₁ x , λ ())
  ; _>>=_  = _>>=_
  }
  where
  _>>=_ : ∀ {X Y} → Σ ⋆ X → (X → Σ ⋆ Y) → Σ ⋆ Y
  sup (inj₁ x , _) >>= f = f x
  sup (inj₂ p , k) >>= f = do (p , λ a → k a >>= f)

generic : ∀ {Σ} (p : Parameter Σ) → Σ ⋆ Arity Σ p
generic p = do (p , return)
  where
  open Monad monad

------------------------------------------------------------------------
-- Example

Π : (X : Set) → (X → Set) → Set
Π X Y = (x : X) → (X ◃ Y) ⋆ Y x

call : ∀ {X Y} → Π X Y
call x = generic x

dom : ∀ {X Y} → ∀ {x} → (X ◃ Y) ⋆ Y x → (X → Set) → Set
dom          (sup (inj₁ _ , _))  R = ⊤
dom {Y = Y}  (sup (inj₂ x , k))  R
  = R x × ((y : Y x) → dom (k y) R)

data Dom {X}{Y} (f : (x : X) → (X ◃ Y) ⋆ Y x) : X → Set
  where
  ⟨_⟩ : ∀ {x} → dom (f x) (Dom f) → Dom f x

dom-map : ∀ {X Y} {R₁ R₂ : X → Set} {x}
          {w : (X ◃ Y) ⋆ Y x} →
          (∀ {x} → R₁ x → R₂ x) → dom w R₁ → dom w R₂
dom-map {w = sup (inj₁ _ , _)} f  _         = tt
dom-map {w = sup (inj₂ _ , _)} f  (y₁ , k)  =
  f y₁ , λ y₂ → dom-map f (k y₂)

{-# TERMINATING #-}
bove-capretta : ∀ {X Y} (f : Π X Y) → (x : X) → Dom f x → Y x
bove-capretta {X}{Y} f x ⟨ d ⟩
  = helper (f x) (dom-map (λ {x′} → bove-capretta f x′) d)
  where
  helper : ∀ {x} (w : (X ◃ Y) ⋆ Y x) → dom w Y → Y x
  helper (sup (inj₁ y , _))  _        = y
  helper (sup (inj₂ _ , k)) (d , dk)  = helper (k d) (dk d)

module _ where
  open Monad monad
  gcd : Π (ℕ × ℕ) (λ _ →  ℕ)
  gcd (0 , n)          = return n
  gcd (m , 0)          = return m
  gcd (suc m , suc n)  = if m ≤ n
                         then  call (suc m , (n ∸ m))
                         else  call ((m ∸ n) , suc n)

acc : Dom gcd (8 , 12)
acc = ⟨ ⟨ ⟨ ⟨ _ ⟩ , _ ⟩ , _ ⟩ , _ ⟩ -- ⟨ ⟨ ⟨ ⟨ tt ⟩ , (λ _ → tt) ⟩ , (λ _ → tt) ⟩ , (λ _ → tt) ⟩

test-gcd′ : bove-capretta gcd (8 , 12) acc ≡ 4
test-gcd′ = refl


{-# OPTIONS --rewriting --confluence-check #-}

open import Agda.Builtin.Nat using (Nat; zero; suc)
open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite

variable
  k l m n : Nat

postulate
  max : Nat → Nat → Nat
  max-0l : max 0 n ≡ n
  max-0r : max m 0 ≡ m
  max-diag : max m m ≡ m
  max-ss : max (suc m) (suc n) ≡ suc (max m n)
  max-assoc : max (max k l) m ≡ max k (max l m)

{-# REWRITE max-0l #-}
{-# REWRITE max-0r #-}
{-# REWRITE max-diag #-}
{-# REWRITE max-ss #-}
--{-# REWRITE max-assoc #-} -- not confluent!

postulate
  _+_ : Nat → Nat → Nat
  plus-0l : 0 + n ≡ n
  plus-0r : m + 0 ≡ m
  plus-suc-l : (suc m) + n ≡ suc (m + n)
  plus-suc-r : m + (suc n) ≡ suc (m + n)
  plus-assoc : (k + l) + m ≡ k + (l + m)

{-# REWRITE plus-0l #-}
{-# REWRITE plus-0r #-}
{-# REWRITE plus-suc-l #-}
{-# REWRITE plus-suc-r #-}
{-# REWRITE plus-assoc #-}

postulate
  _*_ : Nat → Nat → Nat
  mult-0l : 0 * n ≡ 0
  mult-0r : m * 0 ≡ 0
  mult-suc-l : (suc m) * n ≡ n + (m * n)
  mult-suc-r : m * (suc n) ≡ (m * n) + m
  plus-mult-distr-l : k * (l + m) ≡ (k * l) + (k * m)
  plus-mult-distr-r : (k + l) * m ≡ (k * m) + (l * m)
  mult-assoc : (k * l) * m ≡ k * (l * m)

{-# REWRITE mult-0l #-}
{-# REWRITE mult-0r #-}
{-# REWRITE mult-suc-l #-}
{-# REWRITE mult-suc-r #-} -- requires rule plus-assoc!
--{-# REWRITE plus-mult-distr-l #-}
--{-# REWRITE plus-mult-distr-r #-}
--{-# REWRITE mult-assoc #-}


module Lvl.Decidable where

open import Data.Boolean.Stmt
open import Logic.Propositional
open import Logic.Propositional.Theorems
import      Lvl
open import Type.Properties.Decidable
open import Type.Properties.Decidable.Proofs
open import Type

private variable ℓ ℓ₁ : Lvl.Level

-- Changing classical propositions' universe levels by using their boolean representation.
module _ (P : Type{ℓ}) ⦃ dec : Decidable(0)(P) ⦄ where
  Convert : Type{ℓ₁}
  Convert = Lvl.Up(IsTrue(decide(0)(P)))

  -- LvlConvert is satisfied whenever its proposition is.
  Convert-correctness : P ↔ Convert{ℓ₁}
  Convert-correctness = [↔]-transitivity decider-true ([↔]-intro Lvl.Up.obj Lvl.up)


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

open import HoTT

-- an attempt to speed up [QuotGroup (quot-of-sub (ker-prop ...) (im-nprop ...))]
-- which removes most intermediate constructions

module groups.KernelImage {i j k}
  {G : Group i} {H : Group j} {K : Group k}
  (ψ : H →ᴳ K) (φ : G →ᴳ H) (H-ab : is-abelian H) where

  --  G ---φ--→ᴳ H ---ψ--→ᴳ K

  private
    module G = Group G
    module H = AbGroup (H , H-ab)
    module K = Group K
    module ψ = GroupHom ψ
    module φ = GroupHom φ

    ker-El : Type (lmax j k)
    ker-El = Σ H.El (λ h → ψ.f h == K.ident)

    ker-subtype : SubtypeProp H.El k
    ker-subtype = (λ h → ψ.f h == K.ident) , level
      where abstract level : ∀ h → is-prop (ψ.f h == K.ident)
                     level h = K.El-is-set _ _

  ker-El=-out = Subtype=-out ker-subtype

  ker/im-rel' : Rel H.El (lmax i j)
  ker/im-rel' h₁ h₂ = Trunc -1 (hfiber φ.f (H.diff h₁ h₂))

  ker/im-rel : Rel ker-El (lmax i j)
  ker/im-rel (h₁ , _) (h₂ , _) = Trunc -1 (hfiber φ.f (H.diff h₁ h₂))

  private
    ker/im-El : Type (lmax (lmax i j) k)
    ker/im-El = SetQuot ker/im-rel

  ker/im-struct : GroupStructure ker/im-El
  ker/im-struct = record {M} where
    module M where
      ident : ker/im-El
      ident = q[ H.ident , lemma ]
        where abstract lemma = ψ.pres-ident

      inv : ker/im-El → ker/im-El
      inv = SetQuot-rec SetQuot-level inv' inv-rel
        where
          inv' : ker-El → ker/im-El
          inv' (h , ψh=0) = q[ H.inv h , lemma ]
            where abstract lemma = ψ.pres-inv h ∙ ap K.inv ψh=0 ∙ K.inv-ident

          abstract
            inv-rel : ∀ {ker₁ ker₂} → ker/im-rel ker₁ ker₂ → inv' ker₁ == inv' ker₂
            inv-rel {h₁ , _} {h₂ , _} = Trunc-rec (SetQuot-level _ _)
              λ{(g , φg=h₁h₂⁻¹) → quot-rel
                [ G.inv g , φ.pres-inv g ∙ ap H.inv φg=h₁h₂⁻¹
                          ∙ H.inv-diff h₁ h₂ ∙ H.comm h₂ (H.inv h₁)
                          ∙ ap (H.comp (H.inv h₁)) (! (H.inv-inv h₂)) ]}

      comp : ker/im-El → ker/im-El → ker/im-El
      comp = SetQuot-rec level comp' comp-rel where
        abstract
          level : is-set (ker/im-El → ker/im-El)
          level = →-is-set SetQuot-level

        comp' : ker-El → ker/im-El → ker/im-El
        comp' (h₁ , ψh₁=0) = SetQuot-rec SetQuot-level comp'' comp'-rel where

          comp'' : ker-El → ker/im-El
          comp'' (h₂ , ψh₂=0) = q[ H.comp h₁ h₂ , lemma ]
            where abstract lemma = ψ.pres-comp h₁ h₂ ∙ ap2 K.comp ψh₁=0 ψh₂=0
                                 ∙ K.unit-l K.ident

          abstract
            comp'-rel : ∀ {ker₂ ker₂'} → ker/im-rel ker₂ ker₂' → comp'' ker₂ == comp'' ker₂'
            comp'-rel {h₂ , _} {h₂' , _} = Trunc-rec (SetQuot-level _ _)
              λ{(g , φg=h₂h₂'⁻¹) → quot-rel
                [ g , ! ( ap (H.comp (H.comp h₁ h₂)) (H.inv-comp h₁ h₂')
                        ∙ H.assoc h₁ h₂ (H.comp (H.inv h₂') (H.inv h₁))
                        ∙ ap (H.comp h₁) (! $ H.assoc h₂ (H.inv h₂') (H.inv h₁))
                        ∙ H.comm h₁ (H.comp (H.comp h₂ (H.inv h₂')) (H.inv h₁))
                        ∙ H.assoc (H.comp h₂ (H.inv h₂')) (H.inv h₁) h₁
                        ∙ ap2 H.comp (! φg=h₂h₂'⁻¹) (H.inv-l h₁)
                        ∙ H.unit-r (φ.f g) )]}

        abstract
          comp-rel : ∀ {ker₁ ker₁'} → ker/im-rel ker₁ ker₁' → comp' ker₁ == comp' ker₁'
          comp-rel {h₁ , _} {h₁' , _} = Trunc-rec (level _ _)
            λ{(g , φg=h₁h₁'⁻¹) → λ= $ SetQuot-elim (λ _ → =-preserves-set SetQuot-level)
              (λ{(h₂ , _) → quot-rel
                  [ g , ! ( ap (H.comp (H.comp h₁ h₂)) (H.inv-comp h₁' h₂)
                          ∙ H.assoc h₁ h₂ (H.comp (H.inv h₂) (H.inv h₁'))
                          ∙ ap (H.comp h₁) ( ! (H.assoc h₂ (H.inv h₂) (H.inv h₁'))
                                          ∙ ap (λ h → H.comp h (H.inv h₁')) (H.inv-r h₂)
                                          ∙ H.unit-l (H.inv h₁'))
                          ∙ ! φg=h₁h₁'⁻¹ )]})
              (λ _ → prop-has-all-paths-↓ (SetQuot-level _ _))}

      abstract
        unit-l : ∀ k/i → comp ident k/i == k/i
        unit-l = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-level)
          (λ{(h , _) → ap q[_] $ ker-El=-out (H.unit-l h)})
          (λ _ → prop-has-all-paths-↓ (SetQuot-level _ _))

        assoc : ∀ k/i₁ k/i₂ k/i₃ → comp (comp k/i₁ k/i₂) k/i₃ == comp k/i₁ (comp k/i₂ k/i₃)
        assoc = SetQuot-elim
          (λ _ → Π-is-set λ _ → Π-is-set λ _ → =-preserves-set SetQuot-level)
          (λ{(h₁ , _) → SetQuot-elim
            (λ _ → Π-is-set λ _ → =-preserves-set SetQuot-level)
            (λ{(h₂ , _) → SetQuot-elim
              (λ _ → =-preserves-set SetQuot-level)
              (λ{(h₃ , _) → ap q[_] $ ker-El=-out (H.assoc h₁ h₂ h₃)})
              (λ _ → prop-has-all-paths-↓ (SetQuot-level _ _))})
            (λ _ → prop-has-all-paths-↓ (Π-is-prop λ _ → SetQuot-level _ _))})
          (λ _ → prop-has-all-paths-↓ (Π-is-prop λ _ → Π-is-prop λ _ → SetQuot-level _ _))

        inv-l : ∀ k/i → comp (inv k/i) k/i == ident
        inv-l = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-level)
          (λ{(h , _) → ap q[_] $ ker-El=-out (H.inv-l h)})
          (λ _ → prop-has-all-paths-↓ (SetQuot-level _ _))

  Ker/Im : Group (lmax i (lmax j k))
  Ker/Im = group _ SetQuot-level ker/im-struct

  module Ker/Im = Group Ker/Im

  {- correctness -}

  Ker/Im-β : Ker/Im ≃ᴳ QuotGroup (quot-of-sub (ker-propᴳ ψ) (im-npropᴳ φ H-ab))
  Ker/Im-β = ≃-to-≃ᴳ (ide _) to-pres-comp where
    abstract
      to-pres-comp : preserves-comp Ker/Im.comp
        (QuotGroup.comp (quot-of-sub (ker-propᴳ ψ) (im-npropᴳ φ H-ab))) (idf _)
      to-pres-comp = SetQuot-elim
        (λ _ → Π-is-set λ _ → =-preserves-set SetQuot-level)
        (λ _ → SetQuot-elim
          (λ _ → =-preserves-set SetQuot-level)
          (λ _ → ap q[_] $ ker-El=-out idp)
          (λ _ → prop-has-all-paths-↓ (SetQuot-level _ _)))
        (λ _ → prop-has-all-paths-↓ (Π-is-prop λ _ → SetQuot-level _ _))


open import Prelude
open import core

module lemmas-ground where
  -- not ground types aren't hole to hole
  ground-arr-not-hole : ∀{τ} →
                      (τ ground → ⊥) →
                      (τ ≠ (⦇·⦈ ==> ⦇·⦈))
  ground-arr-not-hole notg refl = notg GHole

  ground-prod-not-hole : ∀{τ} →
                     (τ ground → ⊥) →
                     (τ ≠ (⦇·⦈ ⊗ ⦇·⦈))
  ground-prod-not-hole notg refl = notg GProd

  -- not ground types either have to be hole or an arrow or a prod
  notground : ∀{τ} →
              (τ ground → ⊥) →
              (τ == ⦇·⦈) +
                (Σ[ τ1 ∈ typ ] Σ[ τ2 ∈ typ ] (τ == (τ1 ==> τ2))) +
                (Σ[ τ1 ∈ typ ] Σ[ τ2 ∈ typ ] (τ == (τ1 ⊗ τ2)))
  notground {b} gnd = abort (gnd GBase)
  notground {⦇·⦈} gnd = Inl refl
  notground {τ ==> τ₁} gnd = Inr (Inl (τ , τ₁ , refl))
  notground {τ ⊗ τ₁} gnd = Inr (Inr (τ , τ₁ , refl))

  ground-arr-lem :
                   (τ : typ) →
                   ((τ ground) → ⊥) →
                   (τ ≠  ⦇·⦈) →
                   Σ[ τ1 ∈ typ ] Σ[ τ2 ∈ typ ]
                     (((τ == (τ1 ==> τ2)) × ((τ1 ==> τ2) ≠ (⦇·⦈ ==> ⦇·⦈))) +
                      ((τ == (τ1 ⊗ τ2))  × ((τ1 ⊗ τ2)   ≠ (⦇·⦈ ⊗ ⦇·⦈))))
  ground-arr-lem b ng nh = abort (ng GBase)
  ground-arr-lem ⦇·⦈ ng nh = abort (nh refl)
  ground-arr-lem (τ1 ==> τ2) ng nh = τ1 , τ2 , Inl (refl , λ x → ng (lem' x))
    where
      lem' : ∀{τ1 τ2} → τ1 ==> τ2 == ⦇·⦈ ==> ⦇·⦈ → (τ1 ==> τ2) ground
      lem' refl = GHole
  ground-arr-lem (τ1 ⊗ τ2) ng nh = τ1 , τ2 , Inr (refl , (λ x → ng (lem' x)))
    where
      lem' : ∀{τ1 τ2} → τ1 ⊗ τ2 == ⦇·⦈ ⊗ ⦇·⦈ → (τ1 ⊗ τ2) ground
      lem' refl = GProd


{-# OPTIONS --without-K  #-}
module sets.fin.properties where

open import sum
open import decidable
open import equality
open import function.core
open import function.extensionality
open import function.isomorphism
open import function.overloading
open import sets.core
open import sets.nat.core
  hiding (_≟_; pred)
open import sets.nat.ordering
open import sets.fin.core
open import sets.empty
open import sets.properties
open import hott.level.core
open import hott.level.closure
open import hott.level.sets

pred : ∀ {n}(i : Fin (suc n))
     → ¬ (i ≡ zero) → Fin n
pred zero u = ⊥-elim (u refl)
pred (suc j) _ = j

pred-β : ∀ {n}(i : Fin (suc n))
       → (u : ¬ (i ≡ zero))
       → suc (pred i u) ≡ i
pred-β zero u = ⊥-elim (u refl)
pred-β (suc i) u = refl

pred-inj : ∀ {n}{i j : Fin (suc n)}
         → (u : ¬ (i ≡ zero))
         → (v : ¬ (j ≡ zero))
         → pred i u ≡ pred j v
         → i ≡ j
pred-inj {n} {zero} u v p = ⊥-elim (u refl)
pred-inj {n} {suc i} {zero} u v p = ⊥-elim (v refl)
pred-inj {n} {suc i} {suc j} u v p = ap suc p

toℕ : ∀ {n} → Fin n → ℕ
toℕ zero = 0
toℕ (suc i) = suc (toℕ i)

toℕ-sound : ∀ {n}(i : Fin n) → suc (toℕ i) ≤ n
toℕ-sound {zero} ()
toℕ-sound {suc n} zero = s≤s z≤n
toℕ-sound {suc n} (suc i) = s≤s (toℕ-sound i)

fromℕ : ∀ {n i} → (suc i ≤ n) → Fin n
fromℕ {zero} ()
fromℕ {suc n} {0} _ = zero
fromℕ {suc n} {suc i} (s≤s p) = suc (fromℕ p)

toℕ-iso : ∀ {n} → Fin n ≅ (Σ ℕ λ i → suc i ≤ n)
toℕ-iso {n} = record
  { to = λ i → toℕ i , toℕ-sound i
  ; from = λ { (_ , p) → fromℕ p }
  ; iso₁ = α
  ; iso₂ = λ { (i , p) → unapΣ (β i p , h1⇒prop ≤-level _ _) } }
  where
    α : ∀ {n} (i : Fin n) → fromℕ (toℕ-sound i) ≡ i
    α zero = refl
    α (suc i) = ap suc (α i)

    β : ∀ {n} (i : ℕ)(p : suc i ≤ n) → toℕ (fromℕ p) ≡ i
    β {0} _ ()
    β {suc n} 0 _ = refl
    β {suc n} (suc i) (s≤s p) = ap suc (β i p)

toℕ-inj : ∀ {n} → injective (toℕ {n = n})
toℕ-inj p = iso⇒inj toℕ-iso (unapΣ (p , h1⇒prop ≤-level _ _))

#_ : ∀ {n} i {p : True (suc i ≤? n)} → Fin n
#_ {n} i {p} = fromℕ (witness p)

transpose : ∀ {n} → Fin n → Fin n → Fin n → Fin n
transpose i j k with i ≟ k
... | yes _ = j
... | no _ with j ≟ k
... | yes _ = i
... | no _ = k

transpose-β₂ : ∀ {n}(i j : Fin n)
             → transpose i j j ≡ i
transpose-β₂ i j with i ≟ j
... | yes p = sym p
... | no u with j ≟ j
... | yes p = refl
... | no v = ⊥-elim (v refl)

abstract
  transpose-invol : ∀ {n}(i j k : Fin n)
                  → transpose i j (transpose i j k) ≡ k
  transpose-invol i j k with i ≟ k
  transpose-invol i j k | yes p with i ≟ j
  transpose-invol i j k | yes p | yes p' = sym p' · p
  transpose-invol i j k | yes p | no u with j ≟ j
  transpose-invol i j k | yes p | no u | yes p' = p
  transpose-invol i j k | yes p | no u | no v = ⊥-elim (v refl)
  transpose-invol i j k | no u with j ≟ k
  transpose-invol i j k | no u | yes p with i ≟ i
  transpose-invol i j k | no u | yes p | yes p' = p
  transpose-invol i j k | no u | yes p | no v = ⊥-elim (v refl)
  transpose-invol i j k | no u | no v with i ≟ k
  transpose-invol i j k | no u | no v | yes p = ⊥-elim (u p)
  transpose-invol i j k | no u | no v | no w with j ≟ k
  transpose-invol i j k | no u | no v | no w | yes p = ⊥-elim (v p)
  transpose-invol i j k | no u | no v | no w | no z = refl

transpose-iso : ∀ {n}(i j : Fin n) → Fin n ≅ Fin n
transpose-iso i j = record
  { to = transpose i j
  ; from = transpose i j
  ; iso₁ = transpose-invol i j
  ; iso₂ = transpose-invol i j }

fin-remove₀-iso : ∀ {n} → (Fin (suc n) minus zero) ≅ Fin n
fin-remove₀-iso = record
  { to = uncurry pred
  ; from = λ i → suc i , λ p → fin-disj _ (sym p)
  ; iso₁ = λ { (i , u) → unapΣ (pred-β _ u , h1⇒prop ¬-h1 _ _) }
  ; iso₂ = λ _ → refl }

fin-remove-iso : ∀ {n}(i : Fin (suc n))
               → (Σ (Fin (suc n)) λ j → ¬ (j ≡ i))
               ≅ Fin n
fin-remove-iso {n} i = begin
    (Σ (Fin (suc n)) λ j → ¬ (j ≡ i))
  ≅⟨ ( Σ-ap-iso (transpose-iso zero i) λ _
     → Π-ap-iso lem λ _ → refl≅ ) ⟩
    (Σ (Fin (suc n)) λ j → ¬ (j ≡ zero))
  ≅⟨ fin-remove₀-iso ⟩
    Fin n
  ∎
  where
    open ≅-Reasoning

    lem : ∀ {x} → (x ≡ i) ≅ (transpose zero i x ≡ zero)
    lem {x} = begin
        (x ≡ i)
      ≅⟨ iso≡ (transpose-iso zero i) ⟩
        ( transpose zero i x ≡ transpose zero i i )
      ≅⟨ trans≡-iso' (transpose-β₂ zero i) ⟩
        ( transpose zero i x ≡ zero )
      ∎ where open ≅-Reasoning

transpose-inj : ∀ {n m}(i j : Fin m)
              → (f : Fin n → Fin m)
              → injective f
              → injective (transpose i j ∘' f)
transpose-inj i j f inj = inj ∘' iso⇒inj (transpose-iso i j)

transpose-inj-iso' : ∀ {n} (i j : Fin n)
                   → (Fin n ↣ Fin n)
                   ≅ (Fin n ↣ Fin n)
transpose-inj-iso' {n} i j
  = Σ-ap-iso (Π-ap-iso refl≅ λ _ → tiso) λ f
  → mk-prop-iso (inj-level f (fin-set _))
                (inj-level _ (fin-set _))
                (transpose-inj i j f)
                (λ inj p → inj (ap (apply tiso) p))
   where
     tiso : Fin n ≅ Fin n
     tiso = transpose-iso i j

transpose-inj-iso : ∀ {n} (i j : Fin n)
                  → (Fin n ↣ Fin n)
                  ≅ (Fin n ↣ Fin n)
transpose-inj-iso {n} i j
  = Σ-ap-iso (Π-ap-iso tiso λ _ → refl≅) λ f
  → mk-prop-iso (inj-level f (fin-set _))
                (inj-level _ (fin-set _))
                (λ inj → iso⇒inj tiso ∘' inj)
                (λ inj p → iso⇒inj tiso (inj (
                           ap f (_≅_.iso₁ tiso _)
                         · p
                         · sym (ap f (_≅_.iso₁ tiso _)))))
   where
     tiso : Fin n ≅ Fin n
     tiso = transpose-iso i j

inj-nonsurj : ∀ {n i}{A : Set i}
            → (f : A → Fin (suc n))
            → injective f
            → {i : Fin (suc n)}
            → ((x : A) → ¬ (f x ≡ i))
            → A ↣ Fin n
inj-nonsurj {n}{i}{A} f inj {z} u = g , g-inj
 where
   f' : A → Σ (Fin (suc n)) λ k → ¬ (k ≡ z)
   f' i = f i , u i

   inj' : injective f'
   inj' p = inj (ap proj₁ p)

   abstract
    φ : (Σ (Fin (suc n)) λ k → ¬ (k ≡ z)) ≅ Fin n
    φ = fin-remove-iso z

   g : A → Fin n
   g = apply φ ∘' f'

   g-inj : injective g
   g-inj p = inj' (iso⇒inj φ p)

preimage : ∀ {i n}{A : Set i}
         → (f : Fin n → A)
         → (dec : (x y : A) → Dec (x ≡ y))
         → (x : A)
         → ( (Σ (Fin n) λ i → f i ≡ x)
            ⊎ ((i : Fin n) → ¬ (f i ≡ x)) )
preimage {n = 0} f dec x = inj₂ (λ ())
preimage {n = suc n} f dec x with dec (f zero) x
... | yes p = inj₁ (zero , p)
... | no u with preimage (λ i → f (suc i)) dec x
... | inj₁ (i , p) = inj₁ (suc i , p)
... | inj₂ v = inj₂ λ { zero → u ; (suc i) → v i }

fin-inj-remove₀ : ∀ {n m}
                → (f : Fin (suc n) ↣ Fin (suc m))
                → (apply f zero ≡ zero)
                → Fin n ↣ Fin m
fin-inj-remove₀ {n}{m} (f , inj) p = g , g-inj
  where
    nz : {i : Fin n} → ¬ (f (suc i) ≡ zero)
    nz q = fin-disj _ (inj (p · sym q))

    g : Fin n → Fin m
    g i = pred (f (suc i)) nz

    g-inj : injective g
    g-inj q = fin-suc-inj (inj (pred-inj nz nz q))

fin-inj-remove : ∀ {n m}
               → (Fin (suc n) ↣ Fin (suc m))
               → (Fin n ↣ Fin m)
fin-inj-remove {n}{m} (f , f-inj)
  = fin-inj-remove₀
      ( transpose zero (f zero) ∘' f
      , transpose-inj zero (f zero) f f-inj)
      ( transpose-β₂ zero (f zero))

fin-inj-add : ∀ {n m}
            → (Fin n ↣ Fin m)
            → (Fin (suc n) ↣ Fin (suc m))
fin-inj-add {n}{m} (f , f-inj) = g , g-inj
  where
    g : Fin (suc n) → Fin (suc m)
    g zero = zero
    g (suc i) = suc (f i)

    g-inj : injective g
    g-inj {zero} {zero} p = refl
    g-inj {zero} {suc x'} p = ⊥-elim (fin-disj _ p)
    g-inj {suc x} {zero} p = ⊥-elim (fin-disj _ (sym p))
    g-inj {suc x} {suc x'} p = ap suc (f-inj (fin-suc-inj p))

fin-lt : ∀ {n m}(f : Fin n ↣ Fin m) → n ≤ m
fin-lt {0} _ = z≤n
fin-lt {suc n} {0} (f , _) with f zero
... | ()
fin-lt {suc n}{suc m} f = s≤s (fin-lt (fin-inj-remove f))

inj⇒retr : ∀ {n}(f : Fin n → Fin n)
         → injective f
         → retraction f
inj⇒retr {0} f inj ()
inj⇒retr {suc n} f inj y with preimage f _≟_ y
... | inj₁ t = t
... | inj₂ u = ⊥-elim (suc≰ (fin-lt (inj-nonsurj f inj u)))

inj⇒iso : ∀ {n}(f : Fin n → Fin n)
         → injective f
         → Fin n ≅ Fin n
inj⇒iso f inj = inj+retr⇒iso f inj (inj⇒retr f inj)

Fin-inj : ∀ {n m} → Fin n ≅ Fin m → n ≡ m
Fin-inj {n}{m} isoF
  = antisym≤ (fin-lt (_ , iso⇒inj isoF))
             (fin-lt (_ , iso⇒inj (sym≅ isoF)))


{-# OPTIONS --cubical --safe #-}
module Cubical.HITs.AssocList.Properties where

open import Cubical.HITs.AssocList.Base as AL
open import Cubical.Foundations.Everything
open import Cubical.HITs.FiniteMultiset as FMS
open import Cubical.Data.Nat   using (ℕ; zero; suc; _+_)

private
  variable
    ℓ : Level
    A : Type₀

multiPer : (a b : A) (m n : ℕ) (xs : AssocList A)
          → ⟨ a , m ⟩∷ ⟨ b , n ⟩∷ xs ≡ ⟨ b , n ⟩∷ ⟨ a , m ⟩∷ xs
multiPer a b zero n xs = del a (⟨ b , n ⟩∷ xs) ∙ cong (λ ys → ⟨ b , n ⟩∷ ys) (sym (del a xs))
multiPer a b (suc m) zero xs = cong (λ ys → ⟨ a , suc m ⟩∷ ys) (del b xs) ∙ sym (del b (⟨ a , suc m ⟩∷ xs))
multiPer a b (suc m) (suc n) xs =

 ⟨ a , suc m ⟩∷ ⟨ b , suc n ⟩∷ xs               ≡⟨ sym (agg a 1 m (⟨ b , suc n ⟩∷ xs)) ⟩
 ⟨ a , 1 ⟩∷ ⟨ a , m ⟩∷ ⟨ b , suc n ⟩∷ xs        ≡⟨ cong (λ ys → ⟨ a , 1 ⟩∷ ys) (multiPer a b m (suc n) xs) ⟩
 ⟨ a , 1 ⟩∷ ⟨ b , suc n ⟩∷ ⟨ a , m ⟩∷ xs        ≡⟨ cong (λ ys → ⟨ a , 1 ⟩∷ ys) (sym (agg b 1 n (⟨ a , m ⟩∷ xs))) ⟩
 ⟨ a , 1 ⟩∷ ⟨ b , 1 ⟩∷ ⟨ b , n ⟩∷ ⟨ a , m ⟩∷ xs ≡⟨ per a b (⟨ b , n ⟩∷ ⟨ a , m ⟩∷ xs) ⟩
 ⟨ b , 1 ⟩∷ ⟨ a , 1 ⟩∷ ⟨ b , n ⟩∷ ⟨ a , m ⟩∷ xs ≡⟨ cong (λ ys → ⟨ b , 1 ⟩∷ ⟨ a , 1 ⟩∷ ys) (multiPer b a n m xs) ⟩
 ⟨ b , 1 ⟩∷ ⟨ a , 1 ⟩∷ ⟨ a , m ⟩∷ ⟨ b , n ⟩∷ xs ≡⟨ cong (λ ys → ⟨ b , 1 ⟩∷ ys) (agg a 1 m (⟨ b , n ⟩∷ xs)) ⟩
 ⟨ b , 1 ⟩∷ ⟨ a , suc m ⟩∷ ⟨ b , n ⟩∷ xs        ≡⟨ cong (λ ys → ⟨ b , 1 ⟩∷ ys) (multiPer a b (suc m) n xs) ⟩
 ⟨ b , 1 ⟩∷ ⟨ b , n ⟩∷ ⟨ a , suc m ⟩∷ xs        ≡⟨ agg b 1 n (⟨ a , suc m ⟩∷ xs) ⟩
 ⟨ b , suc n ⟩∷ ⟨ a , suc m ⟩∷ xs               ∎

-- Show that association lists and finite multisets are equivalent
multi-∷ : A → ℕ → FMSet A → FMSet A
multi-∷ x zero xs = xs
multi-∷ x (suc n) xs = x ∷ multi-∷ x n xs

multi-∷-agg : (x : A) (m n : ℕ) (b : FMSet A) → multi-∷ x m (multi-∷ x n b) ≡ multi-∷ x (m + n) b
multi-∷-agg x zero n b = refl
multi-∷-agg x (suc m) n b i = x ∷ (multi-∷-agg x m n b i)

AL→FMS : AssocList A → FMSet A
AL→FMS = AL.Rec.f FMS.trunc [] multi-∷ comm multi-∷-agg λ _ _ → refl

FMS→AL : FMSet A → AssocList A
FMS→AL = FMS.Rec.f AL.trunc ⟨⟩ (λ x xs → ⟨ x , 1 ⟩∷ xs) per

AL→FMS∘FMS→AL≡id : section {A = AssocList A} AL→FMS FMS→AL
AL→FMS∘FMS→AL≡id = FMS.ElimProp.f (FMS.trunc _ _) refl (λ x p → cong (λ ys → x ∷ ys) p)

-- need a little lemma for other direction
multi-∷-id : (x : A) (n : ℕ) (u : FMSet A)
            → FMS→AL (multi-∷ x n u) ≡ ⟨ x , n ⟩∷ FMS→AL u
multi-∷-id x zero u = sym (del x (FMS→AL u))
multi-∷-id x (suc n) u = FMS→AL (multi-∷ x (suc n) u)     ≡⟨ cong (λ ys → ⟨ x , 1 ⟩∷ ys) (multi-∷-id x n u) ⟩
                         ⟨ x , 1 ⟩∷ ⟨ x , n ⟩∷ (FMS→AL u) ≡⟨ agg x 1 n (FMS→AL u) ⟩
                         ⟨ x , (suc n) ⟩∷ (FMS→AL u)      ∎

FMS→AL∘AL→FMS≡id : retract {A = AssocList A} AL→FMS FMS→AL
FMS→AL∘AL→FMS≡id = AL.ElimProp.f (AL.trunc _ _) refl (λ x n {xs} p → (multi-∷-id x n (AL→FMS xs)) ∙ cong (λ ys → ⟨ x , n ⟩∷ ys) p)

AssocList≃FMSet : AssocList A ≃ FMSet A
AssocList≃FMSet = isoToEquiv (iso AL→FMS FMS→AL AL→FMS∘FMS→AL≡id FMS→AL∘AL→FMS≡id)

AssocList≡FMSet : AssocList A ≡ FMSet A
AssocList≡FMSet = ua AssocList≃FMSet


module Lectures.Four where

open import Lectures.One public
open import Lectures.Two public
open import Lectures.Three public

_∘_ : {A B C : Set} → (B → C) → (A → B) → (A → C)
(f ∘ g) x = f (g x)

record _↔_ (A B : Set) : Set where
  constructor Isomorphism
  field
    to : A → B
    from : B → A
    from∘to : ∀ x → (from ∘ to) x ≡ x
    to∘from : ∀ y → (to ∘ from) y ≡ y

open List

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

foldr-ℕ : {A : Set} → (A → A) → A → ℕ → A
foldr-ℕ f z zero = z
foldr-ℕ f z (suc n) = f (foldr-ℕ f z n)

ℕ↔List⊤ : ℕ ↔ List ⊤
ℕ↔List⊤ = Isomorphism to from from∘to to∘from
  where

  to : ℕ → List ⊤
  to = foldr-ℕ (tt ∷_) []

  from : List ⊤ → ℕ
  from = foldr-List (λ _ → suc) 0

  from∘to : ∀ x → (from ∘ to) x ≡ x
  from∘to zero = refl
  from∘to (suc x) = cong suc (from∘to x)

  to∘from : ∀ x → (to ∘ from) x ≡ x
  to∘from [] = refl
  to∘from (tt ∷ xs) = cong (tt ∷_) (to∘from xs)

ℕ↔Even : ℕ ↔ Σ ℕ _isEven
ℕ↔Even = Isomorphism to from from∘to to∘from
  where

  to : ℕ → Σ ℕ _isEven
  to zero = zero , tt
  to (suc n) with to n
  to (suc n) | (2n , 2nEven) = (suc (suc 2n) , 2nEven)

  from : (Σ ℕ _isEven) → ℕ
  from (n , nEven) = half n nEven

  from∘to : ∀ x → (from ∘ to) x ≡ x
  from∘to zero = refl
  from∘to (suc n) with to n | from∘to n
  from∘to (suc .(half (proj₁ q) (proj₂ q))) | q | refl = refl

  to∘from : ∀ x → (to ∘ from) x ≡ x
  to∘from (zero , tt) = refl
  to∘from (suc (suc n) , nEven) rewrite to∘from (n , nEven) = refl

data Fin : ℕ → Set where
  zero : ∀ {n} → Fin (suc n)
  suc : ∀ {n} → Fin n → Fin (suc n)

noFin0 : Fin 0 → ⊥
noFin0 ()

open Vec
lookup : ∀ {n} {A : Set} → Vec A n → Fin n → A
lookup (x ∷ xs) zero = x
lookup (x ∷ xs) (suc i) = lookup xs i

-- For next time:
-- Fin
-- Constructive leq


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

module Cubical.Data.Strict2Group.Explicit.Notation where

open import Cubical.Foundations.Prelude hiding (comp)
open import Cubical.Data.Group.Base

module S2GBaseNotation {ℓ : Level} (C₀ C₁ : Group ℓ) (s t : morph C₁ C₀) (i : morph C₀ C₁) (∘ : (g f : Group.type C₁) → (s .fst) g ≡ (t .fst f) → Group.type C₁) where

  -- group operations of the object group
  TC₀ = Group.type C₀
  1₀ = isGroup.id (Group.groupStruc C₀)
  _∙₀_ = isGroup.comp (Group.groupStruc C₀)
  C₀⁻¹ = isGroup.inv (Group.groupStruc C₀)
  rUnit₀ = isGroup.rUnit (Group.groupStruc C₀)
  lUnit₀ = isGroup.lUnit (Group.groupStruc C₀)
  assoc₀ = isGroup.assoc (Group.groupStruc C₀)
  lCancel₀ = isGroup.lCancel (Group.groupStruc C₀)
  rCancel₀ = isGroup.lCancel (Group.groupStruc C₀)

  -- group operation of the morphism group
  TC₁ = Group.type C₁
  1₁ = isGroup.id (Group.groupStruc C₁)
  C₁⁻¹ = isGroup.inv (Group.groupStruc C₁)
  _∙₁_ = isGroup.comp (Group.groupStruc C₁)
  rUnit₁ = isGroup.rUnit (Group.groupStruc C₁)
  lUnit₁ = isGroup.lUnit (Group.groupStruc C₁)
  assoc₁ = isGroup.assoc (Group.groupStruc C₁)
  lCancel₁ = isGroup.lCancel (Group.groupStruc C₁)
  rCancel₁ = isGroup.lCancel (Group.groupStruc C₁)

  -- interface for source, target and identity map
  src = fst s
  src∙₁ = snd s
  tar = fst t
  tar∙₁ = snd t
  id = fst i
  id∙₀ = snd i

  ∙c : {g f g' f' : Group.type C₁} → (coh1 : s .fst g ≡ t .fst f) → (coh2 : s .fst g' ≡ t .fst f') → s .fst (g ∙₁ g') ≡ t .fst (f ∙₁ f')
  ∙c {g} {f} {g'} {f'} coh1 coh2 = (s .snd g g') ∙ ((cong (_∙₀ (s .fst g')) coh1) ∙ (cong ((t .fst f) ∙₀_) coh2) ∙ (sym (t .snd f f')))

  -- for two morphisms the condition for them to be composable
  CohCond : (g f : TC₁) → Type ℓ
  CohCond g f = src g ≡ tar f


module Basic.Axiomatic.TotalImpliesPartial where

open import Basic.AST
open import Basic.BigStep
open import Basic.Axiomatic.Total as T
  renaming (〈_〉_〈_〉 to total〈_〉_〈_〉)
open import Basic.Axiomatic.Partial as P
  renaming (〈_〉_〈_〉 to partial〈_〉_〈_〉) hiding (_==>_; _∧_)

open import Function
open import Data.Product

{-
The proof that total correctness implies partial correctness (exercise 6.33)
is fortunately really simple.

We already proved soundness and completeness for both systems, so instead of
trying to construct the partial proof directly from the total proof, we can
just take a detour and prove the analoguous implication about the *validity*
of triples.
-}

{-
The total validity of Hoare triples implies partial validity, if the language semantics is
deterministic.
-}
P==>wp→P==>wlp : ∀{n S}{P Q : State n → Set} → (P ==> wp S Q) → (P ==> wlp S Q)
P==>wp→P==>wlp pwp ps runS with pwp ps
... | _ , runS' , qs' rewrite deterministic runS runS' = qs'

{- And now we just do an excursion to semantics-land and then back -}
total→partial : ∀ {n S}{P Q : State n → Set} → total〈 P 〉 S 〈 Q 〉 → partial〈 P 〉 S 〈 Q 〉
total→partial = P.complete _ ∘ P==>wp→P==>wlp ∘ T.sound


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

module Math.NumberTheory.Summation.Nat.Properties where

-- agda-stdlib
open import Algebra
import Algebra.Operations.CommutativeMonoid as CommutativeMonoidOperations
open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.Solver
open import Relation.Binary.PropositionalEquality
open import Function.Base

-- agda-misc
open import Math.NumberTheory.Summation.Generic
open import Math.NumberTheory.Summation.Generic.Properties
import Math.NumberTheory.Summation.Nat.Properties.Lemma as Lemma

-- DO NOT change this line
open MonoidSummation (Semiring.+-monoid *-+-semiring)
open CommutativeMonoidOperations (Semiring.+-commutativeMonoid *-+-semiring)

-- TODO? rename _≈_ to _≡_ this is tedious
open SemiringSummationProperties *-+-semiring public
  renaming
  ( Σ<-const to Σ<-const-×
  ; Σ≤-const to Σ≤-const-×
  ; Σ<range-const to Σ<range-const-×
  ; Σ≤range-const to Σ≤range-const-×
  )

-- TODO move somewhere
private
  m×n≡m*n : ∀ m n → m × n ≡ m * n
  m×n≡m*n zero    n = refl
  m×n≡m*n (suc m) n = cong (n +_) $ m×n≡m*n m n

Σ<-const : ∀ m n → Σ< m (λ _ → n) ≡ m * n
Σ<-const m n = begin-equality
  Σ< m (λ _ → n) ≡⟨ Σ<-const-× m n ⟩
  m × n          ≡⟨ m×n≡m*n m n ⟩
  m * n          ∎
  where open ≤-Reasoning

Σ≤-const : ∀ m n → Σ≤ m (λ _ → n) ≡ suc m * n
Σ≤-const m n = Σ<-const (suc m) n

Σ<range-const : ∀ m n x → Σ<range m n (const x) ≡ (n ∸ m) * x
Σ<range-const m n x = trans (Σ<range-const-× m n x) (m×n≡m*n (n ∸ m) x)

Σ≤range-const : ∀ m n x → Σ≤range m n (const x) ≡ (suc n ∸ m) * x
Σ≤range-const m n x = Σ<range-const m (suc n) x

2*Σ≤[n,id]≡n*[1+n] : ∀ n → 2 * Σ≤ n id ≡ n * (suc n)
2*Σ≤[n,id]≡n*[1+n] zero    = refl
2*Σ≤[n,id]≡n*[1+n] (suc n) = begin-equality
  2 * Σ≤ (suc n) id       ≡⟨⟩
  2 * (Σ≤ n id + suc n)   ≡⟨ *-distribˡ-+ 2 (Σ≤ n id) (suc n) ⟩
  2 * Σ≤ n id + 2 * suc n ≡⟨ cong (_+ 2 * suc n) $ 2*Σ≤[n,id]≡n*[1+n] n ⟩
  n * suc n + 2 * suc n   ≡⟨ sym $ *-distribʳ-+ (suc n) n 2 ⟩
  (n + 2) * suc n         ≡⟨ *-comm (n + 2) (suc n) ⟩
  suc n * (n + 2)         ≡⟨ cong (suc n *_) $ +-comm n 2 ⟩
  suc n * suc (suc n)     ∎
  where open ≤-Reasoning

6*Σ≤[n,^2]≡n*[1+n]*[1+2*n] : ∀ n → 6 * Σ≤ n (_^ 2) ≡ n * (1 + n) * (1 + 2 * n)
6*Σ≤[n,^2]≡n*[1+n]*[1+2*n] 0       = refl
6*Σ≤[n,^2]≡n*[1+n]*[1+2*n] (suc n) = begin-equality
  6 * Σ≤ (suc n) (_^ 2)                          ≡⟨⟩
  6 * (Σ≤ n (_^ 2) + suc n ^ 2)                  ≡⟨ *-distribˡ-+ 6 (Σ≤ n (_^ 2)) (suc n ^ 2) ⟩
  6 *  Σ≤ n (_^ 2) + 6 * (suc n ^ 2)             ≡⟨ cong (_+ 6 * (suc n ^ 2)) $ 6*Σ≤[n,^2]≡n*[1+n]*[1+2*n] n ⟩
   n * (1 + n) * (1 + 2 * n) + 6 * ((1 + n) ^ 2) ≡⟨ Lemma.lemma₁ n ⟩
  (1 + n) * (2 + n) * (1 + 2 * (1 + n))          ∎
  where open ≤-Reasoning

-- Arithmetic sequence
{-
2*Σ<[n,i→d+a*i]≡??? : ∀ n d a → 2 * Σ< n (λ i → d + a * i) ≡ n * (2 * d + a * suc n)
2*Σ<[n,i→d+a*i]≡??? n d a = begin-equality
  2 * Σ< n (λ i → d + a * i)
    ≡⟨ {!   !} ⟩
  2 * (Σ< n (λ _ → d) + Σ< n (λ i → a * i))

  2 * (n * d + a * Σ< n id)
  2 * (n * d) + 2 * (a * Σ< n id)
  2 * n * d + a * (2 * Σ< n id)
  2 * n * d + a * (n * suc n)
  n * (2 * d + a * suc n)
    ∎
  where oepn ≤-Reasoning
-}


module Cats.Functor where

open import Level
open import Relation.Binary using (_Preserves_⟶_)

open import Cats.Category.Base
open import Cats.Util.Function using () renaming (_∘_ to _⊙_)
open import Cats.Util.SetoidMorphism using (_⇒_ ; IsInjective ; IsSurjective)
open import Cats.Util.SetoidMorphism.Iso using
  ( IsIso ; _≅_ ; IsIso→≅ ; Injective∧Surjective→Iso ; Iso→Injective
  ; Iso→Surjective )

import Cats.Category.Constructions.Iso as Iso

open Iso.Build._≅_

infixr 9 _∘_

record Functor {lo la l≈ lo′ la′ l≈′}
  (C : Category lo la l≈)
  (D : Category lo′ la′ l≈′)
  : Set (lo ⊔ la ⊔ l≈ ⊔ lo′ ⊔ la′ ⊔ l≈′) where
  private
    module C = Category C
    module C≅ = Iso.Build C
    module D = Category D
    module D≅ = Iso.Build D

  field
    fobj : C.Obj → D.Obj
    fmap : ∀ {A B} → A C.⇒ B → fobj A D.⇒ fobj B
    fmap-resp : ∀ {A B} → fmap {A} {B} Preserves C._≈_ ⟶ D._≈_
    fmap-id : ∀ {A} → fmap (C.id {A}) D.≈ D.id
    fmap-∘ : ∀ {A B C} {f : B C.⇒ C} {g : A C.⇒ B}
      → fmap f D.∘ fmap g D.≈ fmap (f C.∘ g)

  sfmap : ∀ {a b} → C.Hom a b ⇒ D.Hom (fobj a) (fobj b)
  sfmap = record
      { arr = fmap
      ; resp = fmap-resp
      }

  fobj-resp : fobj Preserves C≅._≅_ ⟶ D≅._≅_
  fobj-resp {i} {j} x≅y
      = record
      { forth = fmap (forth x≅y)
      ; back = fmap (back x≅y)
      ; back-forth
          = begin
              fmap (back x≅y) D.∘ fmap (forth x≅y)
            ≈⟨ fmap-∘ ⟩
              fmap (back x≅y C.∘ forth x≅y)
            ≈⟨  fmap-resp (back-forth x≅y) ⟩
              fmap C.id
            ≈⟨ fmap-id ⟩
              D.id
            ∎
      ; forth-back
          = begin
            fmap (forth x≅y) D.∘ fmap (back x≅y)
          ≈⟨ fmap-∘ ⟩
            fmap (forth x≅y C.∘ back x≅y)
          ≈⟨ fmap-resp (forth-back x≅y) ⟩
            fmap C.id
          ≈⟨ fmap-id ⟩
            D.id
          ∎
      }
    where
      open D.≈-Reasoning

open Functor public

id : ∀ {lo la l≈} {C : Category lo la l≈} → Functor C C
id {C = C} = record
    { fobj = λ x → x
    ; fmap = λ f → f
    ; fmap-resp = λ eq → eq
    ; fmap-id = C.≈.refl
    ; fmap-∘ = C.≈.refl
    }
  where
    module C = Category C

_∘_ : ∀ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  → {C : Category lo la l≈}
  → {D : Category lo′ la′ l≈′}
  → {E : Category lo″ la″ l≈″}
  → Functor D E → Functor C D → Functor C E
_∘_ {E = E} F G = record
    { fobj = fobj F ⊙ fobj G
    ; fmap = fmap F ⊙ fmap G
    ; fmap-resp = fmap-resp F ⊙ fmap-resp G
    ; fmap-id = E.≈.trans (fmap-resp F (fmap-id G)) (fmap-id F)
    ; fmap-∘ = E.≈.trans (fmap-∘ F) (fmap-resp F (fmap-∘ G))
    }
  where
    module E = Category E

module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
  (F : Functor C D)
  where

  private
    module C = Category C
    module D = Category D

  IsFaithful : Set (lo ⊔ la ⊔ l≈ ⊔ l≈′)
  IsFaithful = ∀ {a b} → IsInjective (sfmap F {a} {b})

  IsFull : Set (lo ⊔ la ⊔ la′ ⊔ l≈′)
  IsFull = ∀ {a b} → IsSurjective (sfmap F {a} {b})

  IsEmbedding : Set (lo ⊔ la ⊔ l≈ ⊔ la′ ⊔ l≈′)
  IsEmbedding = ∀ {a b} → IsIso (sfmap F {a} {b})

  Embedding→≅ : IsEmbedding → ∀ {a b} → C.Hom a b ≅ D.Hom (fobj F a) (fobj F b)
  Embedding→≅ embedding = IsIso→≅ (sfmap F) embedding

  Full∧Faithful→Embedding : IsFull → IsFaithful → IsEmbedding
  Full∧Faithful→Embedding full faithful
      = Injective∧Surjective→Iso faithful full

  Embedding→Full : IsEmbedding → IsFull
  Embedding→Full embedding = Iso→Surjective embedding

  Embedding→Faithful : IsEmbedding → IsFaithful
  Embedding→Faithful embedding = Iso→Injective embedding


module Data.Num.Core where

open import Data.Num.Digit public

open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.Properties.Simple
open import Data.Nat.Properties.Extra
open import Data.Fin as Fin
    using (Fin; fromℕ≤; inject≤; toℕ; zero; suc; #_)
open import Data.Fin.Properties as FinProps using (bounded; toℕ-fromℕ≤)

open import Function
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Relation.Nullary.Decidable
open import Relation.Binary
open import Function.Equality using (_⟶_)

open ≡-Reasoning
open ≤-Reasoning renaming (begin_ to start_; _∎ to _□; _≡⟨_⟩_ to _≈⟨_⟩_)
open DecTotalOrder decTotalOrder using (reflexive) renaming (refl to ≤-refl)

------------------------------------------------------------------------
-- Numbers
------------------------------------------------------------------------

infixr 5 _∷_

data Numeral : ℕ → ℕ → ℕ → Set where
    _∙  : ∀ {b d o} → Digit d → Numeral b d o
    _∷_ : ∀ {b d o} → Digit d → Numeral b d o → Numeral b d o

MMXVI : Numeral 10 10 0
MMXVI = # 6 ∷ # 1 ∷ # 0 ∷ (# 2) ∙

------------------------------------------------------------------------
-- Converting from Numeral to ℕ
------------------------------------------------------------------------

⟦_⟧ : ∀ {b d o} → (xs : Numeral b d o) → ℕ
⟦_⟧ {_} {_} {o} (x ∙)    = Digit-toℕ x o
⟦_⟧ {b} {_} {o} (x ∷ xs) = Digit-toℕ x o + ⟦ xs ⟧ * b

Num-lower-bound : ∀ {b d o}
    → (xs : Numeral b (suc d) o)
    → ⟦ xs ⟧ ≥ o
Num-lower-bound {_} {_} {o} (x ∙) = Digit-lower-bound o x
Num-lower-bound {b} {d} {o} (x ∷ xs) =
    start
        o
    ≤⟨ m≤m+n o (⟦ xs ⟧ * b) ⟩
        o + ⟦ xs ⟧ * b
    ≤⟨ +n-mono (⟦ xs ⟧ * b) (Digit-lower-bound o x) ⟩
        Digit-toℕ x o + ⟦ xs ⟧ * b
    □

-- the least significant digit
lsd : ∀ {b d o} → (xs : Numeral b d o) → Digit d
lsd (x ∙   ) = x
lsd (x ∷ xs) = x

lsd-toℕ : ∀ {b d o} → (xs : Numeral b d o) → Digit-toℕ (lsd xs) o ≤ ⟦ xs ⟧
lsd-toℕ (x ∙) = ≤-refl
lsd-toℕ {b} {d} {o} (x ∷ xs) = m≤m+n (Digit-toℕ x o) (⟦ xs ⟧ * b)

------------------------------------------------------------------------
-- View of Num
------------------------------------------------------------------------

data NumView : ℕ → ℕ → ℕ → Set where
    NullBase    : ∀   d o                            → NumView 0       (suc d) o
    NoDigits    : ∀ b o                              → NumView b       0       o
    AllZeros    : ∀ b                                → NumView (suc b) 1       0
    Proper      : ∀ b d o → (proper : suc d + o ≥ 2) → NumView (suc b) (suc d) o

numView : ∀ b d o → NumView b d o
numView b       zero          o       = NoDigits b o
numView zero    (suc d)       o       = NullBase d o
numView (suc b) (suc zero)    zero    = AllZeros b
numView (suc b) (suc zero)    (suc o) = Proper b zero (suc o) (s≤s (s≤s z≤n))
numView (suc b) (suc (suc d)) o       = Proper b (suc d) o (s≤s (s≤s z≤n))

------------------------------------------------------------------------
-- Properties of Num
------------------------------------------------------------------------

NoDigits-explode : ∀ {b o a} {Whatever : Set a}
    → (xs : Numeral b 0 o)
    → Whatever
NoDigits-explode (() ∙   )
NoDigits-explode (() ∷ xs)

toℕ-NullBase : ∀ {d o}
    → (x : Digit d)
    → (xs : Numeral 0 d o)
    → ⟦ x ∷ xs ⟧ ≡ Digit-toℕ x o
toℕ-NullBase {d} {o} x xs =
    begin
        Digit-toℕ x o + ⟦ xs ⟧ * 0
    ≡⟨ cong (λ w → Digit-toℕ x o + w) (*-right-zero ⟦ xs ⟧) ⟩
        Digit-toℕ x o + 0
    ≡⟨ +-right-identity (Digit-toℕ x o) ⟩
        Digit-toℕ x o
    ∎

toℕ-AllZeros : ∀ {b} → (xs : Numeral b 1 0) → ⟦ xs ⟧ ≡ 0
toℕ-AllZeros     (zero   ∙   ) = refl
toℕ-AllZeros     (suc () ∙   )
toℕ-AllZeros {b} (zero   ∷ xs) = cong (λ w → w * b) (toℕ-AllZeros xs)
toℕ-AllZeros     (suc () ∷ xs)

n∷-mono-strict : ∀ {b d o}
    → (x : Fin d) (xs : Numeral (suc b) d o)
    → (y : Fin d) (ys : Numeral (suc b) d o)
    → Digit-toℕ x o ≡ Digit-toℕ y o
    → ⟦ xs ⟧ < ⟦ ys ⟧
    → ⟦ x ∷ xs ⟧ < ⟦ y ∷ ys ⟧
n∷-mono-strict {b} {d} {o} x xs y ys ⟦x⟧≡⟦y⟧ ⟦xs⟧<⟦ys⟧ =
    start
        suc (Digit-toℕ x o) + ⟦ xs ⟧ * suc b
    ≈⟨ sym (+-suc (Digit-toℕ x o) (⟦ xs ⟧ * suc b)) ⟩
        Digit-toℕ x o + suc (⟦ xs ⟧ * suc b)
    ≤⟨ n+-mono (Digit-toℕ x o) (s≤s (n≤m+n b (⟦ xs ⟧ * suc b))) ⟩
        Digit-toℕ x o + (suc ⟦ xs ⟧) * suc b
    ≤⟨ (reflexive ⟦x⟧≡⟦y⟧) +-mono (*n-mono (suc b) ⟦xs⟧<⟦ys⟧) ⟩
        Digit-toℕ y o + ⟦ ys ⟧ * suc b
    □

∷ns-mono-strict : ∀ {b d o}
    → (x : Fin d) (xs : Numeral b d o)
    → (y : Fin d) (ys : Numeral b d o)
    → ⟦ xs ⟧ ≡ ⟦ ys ⟧
    → Digit-toℕ x o < Digit-toℕ y o
    → ⟦ x ∷ xs ⟧ < ⟦ y ∷ ys ⟧
∷ns-mono-strict {b} {d} {o} x xs y ys ⟦xs⟧≡⟦ys⟧ ⟦x⟧<⟦y⟧ =
    start
        suc ⟦ x ∷ xs ⟧
    ≤⟨ ⟦x⟧<⟦y⟧ +-mono *n-mono b (reflexive ⟦xs⟧≡⟦ys⟧) ⟩
        ⟦ y ∷ ys ⟧
    □

tail-mono-strict : ∀ {b d o}
    → (x : Fin d) (xs : Numeral b d o)
    → (y : Fin d) (ys : Numeral b d o)
    → Greatest x
    → ⟦ x ∷ xs ⟧ < ⟦ y ∷ ys ⟧
    → ⟦ xs ⟧ < ⟦ ys ⟧
tail-mono-strict {b} {_} {o} x xs y ys greatest p
    = *n-mono-strict-inverse b ⟦∷xs⟧<⟦∷ys⟧
    where
        ⟦x⟧≥⟦y⟧ : Digit-toℕ x o ≥ Digit-toℕ y o
        ⟦x⟧≥⟦y⟧ = greatest-of-all o x y greatest
        ⟦∷xs⟧<⟦∷ys⟧ : ⟦ xs ⟧ * b < ⟦ ys ⟧ * b
        ⟦∷xs⟧<⟦∷ys⟧ = +-mono-contra ⟦x⟧≥⟦y⟧ p

tail-mono-strict-Null : ∀ {b d o}
    → (x : Fin d)
    → (y : Fin d) (ys : Numeral b d o)
    → Greatest x
    → ⟦ _∙ {b} {d} {o} x ⟧ < ⟦ y ∷ ys ⟧
    → 0 < ⟦ ys ⟧
tail-mono-strict-Null {b} {_} {o} x y ys greatest p
    = *n-mono-strict-inverse b ⟦∷∙⟧<⟦∷ys⟧
    where
        ⟦x⟧≥⟦y⟧ : Digit-toℕ x o ≥ Digit-toℕ y o
        ⟦x⟧≥⟦y⟧ = greatest-of-all o x y greatest
        ⟦∷∙⟧<⟦∷ys⟧ : 0 < ⟦ ys ⟧ * b
        ⟦∷∙⟧<⟦∷ys⟧ = +-mono-contra ⟦x⟧≥⟦y⟧ $
            start
                suc (Digit-toℕ x o) + 0
            ≈⟨ +-right-identity (suc (Digit-toℕ x o)) ⟩
                suc (Digit-toℕ x o)
            ≤⟨ p ⟩
                ⟦ y ∷ ys ⟧
            □


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

syntax Unit | U

type
  𝟙 : 0-ary

term
  unit : 𝟙

theory
  (𝟙η) u : 𝟙 |> u = unit
-}

module Unit.Equality where

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core
open import SOAS.Families.Build
open import SOAS.ContextMaps.Inductive

open import Unit.Signature
open import Unit.Syntax

open import SOAS.Metatheory.SecondOrder.Metasubstitution U:Syn
open import SOAS.Metatheory.SecondOrder.Equality U:Syn

private
  variable
    α β γ τ : UT
    Γ Δ Π : Ctx

infix 1 _▹_⊢_≋ₐ_

-- Axioms of equality
data _▹_⊢_≋ₐ_ : ∀ 𝔐 Γ {α} → (𝔐 ▷ U) α Γ → (𝔐 ▷ U) α Γ → Set where
  𝟙η : ⁅ 𝟙 ⁆̣ ▹ ∅ ⊢ 𝔞 ≋ₐ unit

open EqLogic _▹_⊢_≋ₐ_
open ≋-Reasoning


{-# OPTIONS --warning=error --safe --without-K #-}

open import LogicalFormulae
open import Orders.WellFounded.Definition
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Semirings.Definition

module Numbers.Naturals.Order.WellFounded where
open Semiring ℕSemiring

<NWellfounded : WellFounded _<N_
<NWellfounded = λ x → access (go x)
  where
    lemma : {a b c : ℕ} → a <N b → b <N succ c → a <N c
    lemma {a} {b} {c} (le y succYAeqB) (le z zbEqC') = le (y +N z) p
      where
        zbEqC : z +N b ≡ c
        zSuccYAEqC : z +N (succ y +N a) ≡ c
        zSuccYAEqC' : z +N (succ (y +N a)) ≡ c
        zSuccYAEqC'' : succ (z +N (y +N a)) ≡ c
        zSuccYAEqC''' : succ ((z +N y) +N a) ≡ c
        p : succ ((y +N z) +N a) ≡ c
        p = identityOfIndiscernablesLeft _≡_ zSuccYAEqC''' (applyEquality (λ n → succ (n +N a)) (commutative z y))
        zSuccYAEqC''' = identityOfIndiscernablesLeft _≡_ zSuccYAEqC'' (applyEquality succ (+Associative z y a))
        zSuccYAEqC'' = identityOfIndiscernablesLeft _≡_ zSuccYAEqC' (succExtracts z (y +N a))
        zSuccYAEqC' = identityOfIndiscernablesLeft _≡_ zSuccYAEqC (applyEquality (λ r → z +N r) refl)
        zbEqC = succInjective zbEqC'
        zSuccYAEqC = identityOfIndiscernablesLeft _≡_ zbEqC (applyEquality (λ r → z +N r) (equalityCommutative succYAeqB))
    go : ∀ n m → m <N n → Accessible _<N_ m
    go zero m (le x ())
    go (succ n) zero mLessN = access (λ y ())
    go (succ n) (succ m) smLessSN = access (λ o (oLessSM : o <N succ m) → go n o (lemma oLessSM smLessSN))


module Prelude.List.Relations.Permutation where

open import Prelude.List.Base
open import Prelude.List.Relations.Any

data Permutation {a} {A : Set a} : List A → List A → Set a where
  []  : Permutation [] []
  _∷_ : ∀ {x xs ys} (i : x ∈ ys) → Permutation xs (deleteIx ys i) →
          Permutation (x ∷ xs) ys


module Metalogic.Linear.Syntax {ℓₚ} (Proposition : Set(ℓₚ)) where

import Lvl

-- https://en.wikipedia.org/wiki/Linear_logic
-- https://plato.stanford.edu/entries/logic-linear/#ProSys

data Formula : Set(ℓₚ) where
  •_ : Proposition → Formula

  -- Top
  ⊤ : Formula

  -- Bottom
  ⊥ : Formula

  -- Classical conjunction
  _∧_ : Formula → Formula → Formula

  -- Classical disjunction
  _∨_ : Formula → Formula → Formula

  -- Additive and
  _&_ : Formula → Formula → Formula

  -- Additive or
  _⊕_ : Formula → Formula → Formula

  -- Multiplicative and
  _⊗_ : Formula → Formula → Formula

  -- Multiplicative or
  _⅋_ : Formula → Formula → Formula

  -- Linear implication
  _⊸_ : Formula → Formula → Formula


module Introduction.Universes where

data Nat : Set where
  zero : Nat
  suc  : Nat -> Nat

postulate IsEven : Nat -> Prop

data Even : Set where
  even : (n : Nat) -> IsEven n -> Even


module Cxt (K : Set) where

open import Basics
open import Pr
open import Nom

mutual
  data Cxt : Set where
    EC : Cxt
    _[_-_] : (G : Cxt)(x : Nom) -> K -> {p : [| G Hasn't x |]} -> Cxt

  HAS : Cxt -> Nom -> Bool
  HAS EC x = false
  HAS (G [ y - S ]) x with nomEq y x
  HAS (G [ y - S ]) .y   | yes refl = true
  ...                    | no n = HAS G x

  _Has_ : Cxt -> Nom -> Pr
  G Has x = So (HAS G x)

  _Hasn't_ : Cxt -> Nom -> Pr
  G Hasn't x = So (not (HAS G x))

GooN : (G : Cxt)(T : K) -> Nom -> Pr
GooN EC T y = ff
GooN (G [ x - S ]) T y with nomEq x y
GooN (G [ x - S ]) T .x   | yes refl   = S eq T
GooN (G [ x - S ]) T y    | no n       = GooN G T y

_?-_ : (G : Cxt)(x : Nom){p : [| G Has x |]} -> K :- \ T -> GooN G T x
(EC ?- y) {}
((G [ x - S ]) ?- y) {_} with nomEq x y
((G [ x - S ]) ?- .x) {_}   | yes refl  = [ S / refl ]
((G [ x - S ]) ?- y) {p}    | no n      = (G ?- y) {p}

topGooN : (G : Cxt)(x : Nom){p : [| G Hasn't x |]}(S : K) ->
          [| GooN ((G [ x - S ]) {p}) S x |]
topGooN G x S with nomEq x x
topGooN G x S | yes refl  = refl
topGooN G x S | no n      = magic (n refl)


module tests.Mutual where

open import Prelude.IO
open import Prelude.String
open import Prelude.Unit

mutual
  data G : Set where
    GA : {g : G}(f : F g) -> G
    GB : G

  data F : G -> Set where
    FA : (g : G) -> F g
    FB : F GB

mutual
  incG : G -> G
  incG GB     = GA FB
  incG (GA f) = GA (incF f)

  incF : {g : G} -> F g -> F (incG g)
  incF FB     = FA (GA FB)
  incF (FA g) = FA (incG g)

mutual
  PrintF : {g : G} -> F g -> String
  PrintF FB = "FB"
  PrintF (FA g) = "(FA " +S+ PrintG g +S+ ")"

  PrintG : G -> String
  PrintG GB     = "GB"
  PrintG (GA f) = "(GA " +S+ PrintF f +S+ ")"

main : IO Unit
main =
    putStrLn (PrintF (FA (GA (FA GB)))) ,,
    putStrLn (PrintG (incG (GA (incF FB)))) ,, --
    return unit


{-# OPTIONS --cubical-compatible --universe-polymorphism #-}

module WithoutK where

open import Common.Level

-- Propositional equality.

data _≡_ {A : Set} : A → A → Set where
  refl : ∀ x → x ≡ x

-- The J rule.

J : {A : Set} (P : {x y : A} → x ≡ y → Set) →
    (∀ x → P (refl x)) →
    ∀ {x y} (x≡y : x ≡ y) → P x≡y
J P p (refl x) = p x

-- Christine Paulin-Mohring's version of the J rule.

J′ : {A : Set} {x : A} (P : {y : A} → x ≡ y → Set) →
     P (refl x) →
     ∀ {y} (x≡y : x ≡ y) → P x≡y
J′ P p (refl x) = p

-- A variant of _≡_.

data _≡′_ {A : Set} (x : A) : A → Set where
  refl : x ≡′ x

-- We normalise before checking index well-formedness.

const : ∀ {a b} {A : Set a} {B : Set b} → A → B → A
const x _ = x

id : {A : Set} {x y : A} → const x y ≡′ const y x → x ≡′ y
id refl = refl

-- We can handle more complicated indices as well.

data ⊥ : Set where

data Bool : Set where
  true false : Bool

true≢false : true ≡ false → ⊥
true≢false ()

data D : Set where
  c₀ : D
  c₂ : (i₁ i₂ : D) → D

f : ∀ {x y z} → x ≡ y → c₂ y c₀ ≡ c₂ c₀ z → x ≡ z
f x≡y (refl .(c₂ c₀ c₀)) = x≡y

-- The indices can contain literals.

data ℕ : Set where
  zero : ℕ
  suc  : (n : ℕ) → ℕ

{-# BUILTIN NATURAL ℕ    #-}

g : 2 ≡ 3 → 3 ≡ 5
g ()

h : ∀ {n} → 2 ≡ suc n → n ≡ 1
h (refl .2) = refl _


module Function.Equals where

import      Lvl
open import Functional
import      Function.Names as Names
open import Logic
open import Logic.Propositional
open import Structure.Setoid
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Type

module Dependent {ℓ₁ ℓ₂ ℓₑ₂} {A : Type{ℓ₁}} {B : A → Type{ℓ₂}} ⦃ equiv-B : ∀{a} → Equiv{ℓₑ₂}(B(a)) ⦄ where
  infixl 15 _⊜_

  -- Function equivalence. When the types and all their values are shared/equivalent.
  record _⊜_ (f : (a : A) → B(a)) (g : (a : A) → B(a)) : Stmt{ℓ₁ Lvl.⊔ ℓ₂ Lvl.⊔ ℓₑ₂} where
    constructor intro
    field
      proof : (f Names.⊜ g)

  instance
    [⊜]-reflexivity : Reflexivity(_⊜_)
    Reflexivity.proof([⊜]-reflexivity) = intro(reflexivity(_≡_) ⦃ Equiv.reflexivity(equiv-B) ⦄)

  instance
    [⊜]-symmetry : Symmetry(_⊜_)
    Symmetry.proof([⊜]-symmetry) (intro fg) = intro(symmetry(_≡_) ⦃ Equiv.symmetry(equiv-B) ⦄ fg)

  instance
    [⊜]-transitivity : Transitivity(_⊜_)
    Transitivity.proof([⊜]-transitivity) (intro fg) (intro gh) = intro(transitivity(_≡_) ⦃ Equiv.transitivity(equiv-B) ⦄ fg gh)

  instance
    [⊜]-equivalence : Equivalence(_⊜_)
    [⊜]-equivalence = record{}

  instance
    [⊜]-equiv : Equiv((a : A) → B(a))
    [⊜]-equiv = intro(_⊜_) ⦃ [⊜]-equivalence ⦄

  instance
    [⊜]-sub : (_≡_) ⊆₂ (_⊜_)
    _⊆₂_.proof [⊜]-sub (intro proof) = intro proof

module _ {ℓ₁ ℓ₂ ℓₑ₂} {A : Type{ℓ₁}} {B : Type{ℓ₂}} ⦃ equiv-B : Equiv{ℓₑ₂}(B) ⦄ where
  private module D = Dependent {A = A} {B = const B} ⦃ equiv-B ⦄
  open D using (module _⊜_ ; intro) public
  _⊜_              = D._⊜_
  [⊜]-reflexivity  = D.[⊜]-reflexivity
  [⊜]-symmetry     = D.[⊜]-symmetry
  [⊜]-transitivity = D.[⊜]-transitivity
  [⊜]-equivalence  = D.[⊜]-equivalence
  [⊜]-equiv        = D.[⊜]-equiv
  [⊜]-sub          = D.[⊜]-sub


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

open import HoTT
open import homotopy.Bouquet
open import homotopy.SphereEndomorphism
open import groups.SphereEndomorphism
open import groups.CoefficientExtensionality
open import cw.CW
open import cw.FinCW
open import cw.WedgeOfCells
open import cw.DegreeByProjection {lzero}

module cw.FinBoundary where

  fdegree-last : ∀ {n} (fin-skel : FinSkeleton (S n))
    → cells-last (FinSkeleton-realize fin-skel)
    → cells-last (cw-init (FinSkeleton-realize fin-skel)) → ℤ
  fdegree-last fin-skel = degree-last (FinSkeleton-realize fin-skel) (FinSkeleton-has-cells-with-dec-eq fin-skel)

  fdegree-nth : ∀ {m n} (Sm≤n : S m ≤ n) (fin-skel : FinSkeleton n)
    → cells-nth Sm≤n (FinSkeleton-realize fin-skel)
    → cells-last (cw-init (cw-take Sm≤n (FinSkeleton-realize fin-skel))) → ℤ
  fdegree-nth Sm≤n fin-skel = degree-nth Sm≤n (FinSkeleton-realize fin-skel) (FinSkeleton-has-cells-with-dec-eq fin-skel)

  FinSkeleton-has-degrees-with-finite-support : ∀ {n} (fin-skel : FinSkeleton n)
    → has-degrees-with-finite-support
        (FinSkeleton-realize fin-skel)
        (FinSkeleton-has-cells-with-dec-eq fin-skel)
  FinSkeleton-has-degrees-with-finite-support {n = O} _ = _
  FinSkeleton-has-degrees-with-finite-support {n = S O} _ = _ , (λ _ → Fin→-has-finite-support _)
  FinSkeleton-has-degrees-with-finite-support {n = S (S n)} (attached-fin-skeleton skel _ _) =
    FinSkeleton-has-degrees-with-finite-support {n = S n} skel , (λ _ → Fin→-has-finite-support _)

  fboundary'-last : ∀ {n} (fin-skel : FinSkeleton (S n))
    → cells-last (FinSkeleton-realize fin-skel)
    → FreeAbGroup.El (cells-last (cw-init (FinSkeleton-realize fin-skel)))
  fboundary'-last fin-skel upper = boundary'-last
    (FinSkeleton-realize fin-skel)
    (FinSkeleton-has-cells-with-dec-eq fin-skel)
    (FinSkeleton-has-degrees-with-finite-support fin-skel)
    upper

  fboundary-last : ∀ {n} (fin-skel : FinSkeleton (S n))
    →  FreeAbGroup.grp (cells-last (FinSkeleton-realize fin-skel))
    →ᴳ FreeAbGroup.grp (cells-last (cw-init (FinSkeleton-realize fin-skel)))
  fboundary-last fin-skel = FreeAbGroup-extend
    (FreeAbGroup (cells-last (cw-init (FinSkeleton-realize fin-skel))))
    (fboundary'-last fin-skel)


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

module Data.Fin.Subset.Properties where

open import Algebra
import Algebra.Properties.BooleanAlgebra as BoolProp
open import Data.Empty using (⊥-elim)
open import Data.Fin using (Fin); open Data.Fin.Fin
open import Data.Fin.Subset
open import Data.Nat using (ℕ)
open import Data.Product
open import Data.Sum as Sum
open import Data.Vec hiding (_∈_)
open import Function
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence
  using (_⇔_; equivalence; module Equivalence)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_)

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

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

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

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

------------------------------------------------------------------------
-- Properties involving ⊥

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

⊥⊆ : ∀ {n} {p : Subset n} → ⊥ ⊆ p
⊥⊆ x∈⊥ with ∉⊥ x∈⊥
... | ()

Empty-unique : ∀ {n} {p : Subset n} →
               Empty p → p ≡ ⊥
Empty-unique {p = []}           ¬∃∈ = P.refl
Empty-unique {p = s ∷ p}        ¬∃∈ with Empty-unique (drop-∷-Empty ¬∃∈)
Empty-unique {p = outside ∷ .⊥} ¬∃∈ | P.refl = P.refl
Empty-unique {p = inside  ∷ .⊥} ¬∃∈ | P.refl =
  ⊥-elim (¬∃∈ (zero , here))

------------------------------------------------------------------------
-- Properties involving ⊤

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

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

------------------------------------------------------------------------
-- A property involving ⁅_⁆

x∈⁅y⁆⇔x≡y : ∀ {n} {x y : Fin n} → x ∈ ⁅ y ⁆ ⇔ x ≡ y
x∈⁅y⁆⇔x≡y {x = x} {y} =
  equivalence (to y) (λ x≡y → P.subst (λ y → x ∈ ⁅ y ⁆) x≡y (x∈⁅x⁆ x))
  where

  to : ∀ {n x} (y : Fin n) → x ∈ ⁅ y ⁆ → x ≡ y
  to (suc y) (there p) = P.cong suc (to y p)
  to zero    here      = P.refl
  to zero    (there p) with ∉⊥ p
  ... | ()

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

------------------------------------------------------------------------
-- A property involving _∪_

∪⇔⊎ : ∀ {n} {p₁ p₂ : Subset n} {x} → x ∈ p₁ ∪ p₂ ⇔ (x ∈ p₁ ⊎ x ∈ p₂)
∪⇔⊎ = equivalence (to _ _) from
  where
  to : ∀ {n} (p₁ p₂ : Subset n) {x} → x ∈ p₁ ∪ p₂ → x ∈ p₁ ⊎ x ∈ p₂
  to []             []             ()
  to (inside  ∷ p₁) (s₂      ∷ p₂) here            = inj₁ here
  to (outside ∷ p₁) (inside  ∷ p₂) here            = inj₂ here
  to (s₁      ∷ p₁) (s₂      ∷ p₂) (there x∈p₁∪p₂) =
    Sum.map there there (to p₁ p₂ x∈p₁∪p₂)

  ⊆∪ˡ : ∀ {n p₁} (p₂ : Subset n) → p₁ ⊆ p₁ ∪ p₂
  ⊆∪ˡ []       ()
  ⊆∪ˡ (s ∷ p₂) here         = here
  ⊆∪ˡ (s ∷ p₂) (there x∈p₁) = there (⊆∪ˡ p₂ x∈p₁)

  ⊆∪ʳ : ∀ {n} (p₁ p₂ : Subset n) → p₂ ⊆ p₁ ∪ p₂
  ⊆∪ʳ p₁ p₂ rewrite BooleanAlgebra.∨-comm (booleanAlgebra _) p₁ p₂
    = ⊆∪ˡ p₁

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

------------------------------------------------------------------------
-- _⊆_ is a partial order

-- The "natural poset" associated with the boolean algebra.

module NaturalPoset where
  private
    open module BA {n} = BoolProp (booleanAlgebra n) public
      using (poset)
    open module Po {n} = Poset (poset {n = n}) public

  -- _⊆_ is equivalent to the natural lattice order.

  orders-equivalent : ∀ {n} {p₁ p₂ : Subset n} → p₁ ⊆ p₂ ⇔ p₁ ≤ p₂
  orders-equivalent = equivalence (to _ _) (from _ _)
    where
    to : ∀ {n} (p₁ p₂ : Subset n) → p₁ ⊆ p₂ → p₁ ≤ p₂
    to []             []             p₁⊆p₂ = P.refl
    to (inside  ∷ p₁) (_       ∷ p₂) p₁⊆p₂ with p₁⊆p₂ here
    to (inside  ∷ p₁) (.inside ∷ p₂) p₁⊆p₂ | here = P.cong (_∷_ inside)  (to p₁ p₂ (drop-∷-⊆ p₁⊆p₂))
    to (outside ∷ p₁) (_       ∷ p₂) p₁⊆p₂        = P.cong (_∷_ outside) (to p₁ p₂ (drop-∷-⊆ p₁⊆p₂))

    from : ∀ {n} (p₁ p₂ : Subset n) → p₁ ≤ p₂ → p₁ ⊆ p₂
    from []             []       p₁≤p₂ x               = x
    from (.inside ∷ _)  (_ ∷ _)  p₁≤p₂ here            rewrite P.cong head p₁≤p₂ = here
    from (_       ∷ p₁) (_ ∷ p₂) p₁≤p₂ (there xs[i]=x) =
      there (from p₁ p₂ (P.cong tail p₁≤p₂) xs[i]=x)

-- _⊆_ is a partial order.

poset : ℕ → Poset _ _ _
poset n = record
  { Carrier        = Subset n
  ; _≈_            = _≡_
  ; _≤_            = _⊆_
  ; isPartialOrder = record
    { isPreorder = record
      { isEquivalence = P.isEquivalence
      ; reflexive     = λ i≡j → from ⟨$⟩ reflexive i≡j
      ; trans         = λ x⊆y y⊆z → from ⟨$⟩ trans (to ⟨$⟩ x⊆y) (to ⟨$⟩ y⊆z)
      }
    ; antisym = λ x⊆y y⊆x → antisym (to ⟨$⟩ x⊆y) (to ⟨$⟩ y⊆x)
    }
  }
  where
  open NaturalPoset
  open module E {p₁ p₂} =
    Equivalence (orders-equivalent {n = n} {p₁ = p₁} {p₂ = p₂})


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

open import Algebra.Structures.Bundles.Field

module Algebra.Linear.Construct.Vector
  {k ℓ} (K : Field k ℓ)
  where

open import Level using (_⊔_)
open import Data.Product using (_×_; _,_)
open import Data.Vec public hiding (sum)
import Data.Vec.Properties as VP
import Data.Vec.Relation.Binary.Pointwise.Inductive as PW
open import Data.Nat hiding (_⊔_) renaming (_+_ to _+ℕ_)
open import Data.Fin using (Fin)

open import Relation.Binary
open import Algebra.Linear.Core
open import Algebra.FunctionProperties

open import Relation.Binary.PropositionalEquality as P
  using (_≡_; subst; subst-subst-sym)
  renaming
  ( refl  to ≡-refl
  ; sym   to ≡-sym
  ; trans to ≡-trans
  )

open import Algebra.Structures.Field.Utils K
import Algebra.Linear.Structures.VectorSpace as VS

open VS.VectorSpaceField K

open import Data.Nat.Properties
  using
    ( ≤-refl
    ; ≤-reflexive
    ; ≤-antisym
    ; n∸n≡0
    ; m+[n∸m]≡n
    ; m≤m+n
    ; suc-injective
    )
  renaming
    ( +-identityˡ to +ℕ-identityˡ
    ; +-identityʳ to +ℕ-identityʳ
    )

private
  V : ℕ -> Set k
  V = Vec K'

splitAt' : ∀ {n} (i : ℕ) (j : ℕ) -> i +ℕ j ≡ n -> V n → V i × V j
splitAt' {0} 0 0 r [] = ([] , [])
splitAt' {suc n} 0 (suc j) r u = ([] , subst V (≡-sym r) u)
splitAt' {suc n} (suc i) j r (x ∷ xs) =
  let (xs₁ , xs₂) = splitAt' {n} i j (suc-injective r) xs
  in (x ∷ xs₁ , xs₂)

open PW public
  using ()
  renaming
    ( _∷_    to ≈-cons
    ; []     to ≈-null
    ; head   to ≈-head
    ; tail   to ≈-tail
    ; uncons to ≈-uncons
    ; lookup to ≈-lookup
    ; map      to ≈-map
    )

_≈ʰ_ : ∀ {m n} (xs : Vec K' m) (ys : Vec K' n) → Set (k ⊔ ℓ)
_≈ʰ_ = PW.Pointwise _≈ᵏ_

_≈_ : ∀ {n} (xs : Vec K' n) (ys : Vec K' n) → Set (k ⊔ ℓ)
_≈_ = _≈ʰ_

≈-isEquiv : ∀ {n} -> IsEquivalence (_≈_ {n})
≈-isEquiv {n} = PW.isEquivalence ≈ᵏ-isEquiv n

setoid : ℕ -> Setoid k (k ⊔ ℓ)
setoid n = record { isEquivalence = ≈-isEquiv {n} }

module _ {n} where
  open IsEquivalence (≈-isEquiv {n}) public
    using ()
    renaming
      ( refl  to ≈-refl
      ; sym   to ≈-sym
      ; trans to ≈-trans
      ; reflexive to ≈-reflexive
      )

0# : ∀ {n} → V n
0# {0}     = []
0# {suc n} = 0ᵏ ∷ 0# {n}

-_ : ∀ {n} → V n → V n
-_ = map (-ᵏ_)

infixr 25 _+_
_+_ : ∀ {n} → Op₂ (V n)
_+_ = zipWith _+ᵏ_

infixr 35 _∙_
_∙_ : ∀ {n} → K' → V n → V n
_∙_ k = map (k *ᵏ_)

{-
module HeterogeneousEquivalence where
  import Relation.Binary.Indexed.Heterogeneous as IH

  data _≈ʰ_ : IH.IRel V (k ⊔ ℓ) where
    ≈ʰ-null : ∀ {n p} {u : V n} {v : V p} -> n ≡ 0 -> p ≡ 0 -> u ≈ʰ v
    ≈ʰ-cons : ∀ {n p} {x y : K'} {u : V n} {v : V p} -> n ≡ p -> x ≈ᵏ y -> u ≈ʰ v -> (x ∷ u) ≈ʰ (y ∷ v)

  pattern ≈ʰ-zero = ≈ʰ-null ≡-refl ≡-refl

  ≈ʰ-refl : IH.Reflexive V _≈ʰ_
  ≈ʰ-refl {0} {[]} = ≈ʰ-zero
  ≈ʰ-refl {suc n} {x ∷ xs} = ≈ʰ-cons (≡-refl) ≈ᵏ-refl (≈ʰ-refl {n} {xs})

  ≈ʰ-sym : IH.Symmetric V _≈ʰ_
  ≈ʰ-sym {0} ≈ʰ-zero = ≈ʰ-zero
  ≈ʰ-sym {suc n} (≈ʰ-cons rn r rs) = ≈ʰ-cons (≡-sym rn) (≈ᵏ-sym r) (≈ʰ-sym rs)

  ≈ʰ-trans : IH.Transitive V _≈ʰ_
  ≈ʰ-trans (≈ʰ-null rn rp) (≈ʰ-null rn' rp') = ≈ʰ-null rn rp'
  ≈ʰ-trans (≈ʰ-cons rn r rs) (≈ʰ-cons rn' r' rs') = ≈ʰ-cons (≡-trans rn rn') (≈ᵏ-trans r r') (≈ʰ-trans rs rs')

  ≈ʰ-isEquiv : IH.IsIndexedEquivalence V _≈ʰ_
  ≈ʰ-isEquiv = record
    { refl  = ≈ʰ-refl
    ; sym   = ≈ʰ-sym
    ; trans = ≈ʰ-trans
    }

  ≈-to-≈ʰ : ∀ {n} {u v : V n} -> u ≈ v -> u ≈ʰ v
  ≈-to-≈ʰ {0} ≈-null = ≈ʰ-zero
  ≈-to-≈ʰ {suc n} (≈-cons r rs) = ≈ʰ-cons ≡-refl r (≈-to-≈ʰ rs)

  ≈ʰ-to-≈ : ∀ {n} {u v : V n} -> u ≈ʰ v -> u ≈ v
  ≈ʰ-to-≈ {0} {[]} {[]} ≈ʰ-zero = ≈-null
  ≈ʰ-to-≈ {suc n} (≈ʰ-cons _ r rs) = ≈-cons r (≈ʰ-to-≈ rs)

  ≈ʰ-tail : ∀ {n p} {x y : K'} {u : V n} {v : V p} -> (x ∷ u) ≈ʰ (x ∷ v) -> u ≈ʰ v
  ≈ʰ-tail (≈ʰ-cons _ _ r) = r

  indexedSetoid : IH.IndexedSetoid ℕ k (ℓ ⊔ k)
  indexedSetoid = record
    { Carrier       = V
    ; _≈_           = _≈ʰ_
    ; isEquivalence = ≈ʰ-isEquiv
    }
setoid : ℕ -> Setoid k (k ⊔ ℓ)
setoid n = record
  { Carrier       = V n
  ; _≈_           = _≈_
  ; isEquivalence = ≈-isEquiv
  }
-}

++-identityˡ : ∀ {n} (u : V n) -> ([] ++ u) ≈ u
++-identityˡ _ = ≈-refl

++-cong : ∀ {n p} {u₁ v₁ : V n} {u₂ v₂ : V p}
        -> u₁ ≈ v₁ -> u₂ ≈ v₂ -> (u₁ ++ u₂) ≈ (v₁ ++ v₂)
++-cong  ≈-null        r₂ = r₂
++-cong (≈-cons r₁ rs₁) r₂ = ≈-cons r₁ (++-cong rs₁ r₂)

++-split : ∀ {n p} {u₁ v₁ : V n} {u₂ v₂ : V p}
        ->  (u₁ ++ u₂) ≈ (v₁ ++ v₂) -> (u₁ ≈ v₁ × u₂ ≈ v₂)
++-split {0} {p} {[]} {[]} r = ≈-null , r
++-split {suc n} {p} {x ∷ xs} {y ∷ ys} (≈-cons r rs) =
  let (r₁ , r₂) = ++-split {n} {p} rs
  in (≈-cons r r₁) , r₂

0++0≈0 : ∀ {n p} -> (0# {n} ++ 0# {p}) ≈ 0# {n +ℕ p}
0++0≈0 {zero} = ++-identityˡ 0#
0++0≈0 {suc n} = ≈-cons ≈ᵏ-refl (0++0≈0 {n})

{-
open HeterogeneousEquivalence

++-identityʳ-≈ʰ : ∀ {n} (u : V n) -> (u ++ []) ≈ʰ u
++-identityʳ-≈ʰ [] = ≈ʰ-refl
++-identityʳ-≈ʰ {suc n} (x ∷ xs) = ≈ʰ-cons (+ℕ-identityʳ n) ≈ᵏ-refl (++-identityʳ-≈ʰ {n} xs)

++-cong-≈ʰ : ∀ {n p} {u₁ v₁ : V n} {u₂ v₂ : V p}
           -> u₁ ≈ʰ v₁ -> u₂ ≈ʰ v₂ -> (u₁ ++ u₂) ≈ʰ (v₁ ++ v₂)
++-cong-≈ʰ r₁ r₂ = ≈-to-≈ʰ (++-cong (≈ʰ-to-≈ r₁) (≈ʰ-to-≈ r₂))

splitAt-identityˡ : ∀ {n} (u : V n) -> let (x₁ , x₂) = splitAt 0 z≤n u in (x₁ ≈ʰ []) × (x₂ ≈ʰ u)
splitAt-identityˡ {0}_ = ≈ʰ-zero , ≈ʰ-zero
splitAt-identityˡ {suc n} _ = ≈ʰ-zero , ≈ʰ-refl

splitAt-identityʳ : ∀ {n} (u : V n) -> let (x₁ , x₂) = splitAt n ≤-refl u in (x₁ ≈ʰ u) × (x₂ ≈ʰ [])
splitAt-identityʳ {0} [] = ≈ʰ-refl , ≈ʰ-refl
splitAt-identityʳ {suc n} (x ∷ xs) = ≈ʰ-cons ≡-refl ≈ᵏ-refl (proj₁ (splitAt-identityʳ xs))
                                   , ≈ʰ-null (n∸n≡0 (suc n)) ≡-refl

++-splitAt : ∀ {n} (k : ℕ) (r : k ≤ n) (u : V n) -> let (x₁ , x₂, _) = splitAt n u in (x₁ ++ x₂) ≡ u
++-splitAt {0} 0 r u = ≈ʰ-null (m+[n∸m]≡n r) ≡-refl
++-splitAt {suc n} 0 r (x ∷ xs) = ≈ʰ-refl
++-splitAt {suc n} (suc k) sk≤sn (x ∷ xs) =
  let k≤n = ≤-pred sk≤sn
  in ≈ʰ-cons (m+[n∸m]≡n k≤n) ≈ᵏ-refl (++-splitAt k k≤n xs)

splitAt-++ : ∀ {n p} (u : Vec n) (v : Vec p) ->
  let (u' , v') = splitAt n (m≤m+n n p) (u ++ v) in (u ≈ʰ u') × (v ≈ʰ v')
splitAt-++ {0} {0} [] [] =  ≈ʰ-zero , ≈ʰ-zero
splitAt-++ {0} {suc p} [] (y ∷ ys) = ≈ʰ-zero , ≈ʰ-refl
splitAt-++ {suc n} {0} (x ∷ xs) [] =
  let (rxs , rv) = splitAt-++ xs []
  in ≈ʰ-cons ≡-refl ≈ᵏ-refl rxs , rv
splitAt-++ {suc n} {suc p} (x ∷ xs) (y ∷ ys) =
  let (u' , v') = splitAt (suc n) (m≤m+n (suc n) (suc p)) (x ∷ xs ++ y ∷ ys)
      (ru , rys) = splitAt-++ {suc n} {p} (x ∷ xs) ys
      (rxs , rv) = splitAt-++ {n} {suc p} xs (y ∷ ys)
  in ≈ʰ-cons ≡-refl ≈ᵏ-refl rxs , rv

++-splitAt' : ∀ {n p} (u : Vec (n +ℕ p)) -> let (x₁ , x₂) = splitAt' n p ≡-refl u in x₁ ++ x₂ ≈ u
++-splitAt' {0} [] = ≈-null
++-splitAt' {0} (x ∷ xs) = ≈-refl
++-splitAt' {suc n} (x ∷ xs) = ≈-cons ≈ᵏ-refl (++-splitAt' {n} xs)

splitAt'-++ : ∀ {n p} (u : Vec n) (v : Vec p) ->
  let (u' , v') = splitAt' n p ≡-refl (u ++ v) in (u ≈ u') × (v ≈ v')
splitAt'-++ {0} {0} [] [] =  ≈-null , ≈-null
splitAt'-++ {0} {suc p} [] (y ∷ ys) = ≈-null , ≈-refl
splitAt'-++ {suc n} {0} (x ∷ xs) [] =
  let (rxs , rv) = splitAt'-++ xs []
  in ≈-cons ≈ᵏ-refl rxs , rv
splitAt'-++ {suc n} {suc p} (x ∷ xs) (y ∷ ys) =
  let (u' , v') = splitAt' (suc n) (suc p) ≡-refl (x ∷ xs ++ y ∷ ys)
      (ru , rys) = splitAt'-++ {suc n} {p} (x ∷ xs) ys
      (rxs , rv) = splitAt'-++ {n} {suc p} xs (y ∷ ys)
  in ≈-cons ≈ᵏ-refl rxs , rv
-}

+-cong : ∀ {n} → Congruent₂ (_≈_ {n}) _+_
+-cong ≈-null ≈-null = ≈-null
+-cong (≈-cons r₁ rs₁) (≈-cons r₂ rs₂) = ≈-cons (+ᵏ-cong r₁ r₂) (+-cong rs₁ rs₂)

+-assoc : ∀ {n} → Associative (_≈_ {n}) _+_
+-assoc [] [] [] = ≈-null
+-assoc (x ∷ xs) (y ∷ ys) (z ∷ zs) = ≈-cons (+ᵏ-assoc x y z) (+-assoc xs ys zs)

+-identityˡ : ∀ {n} → LeftIdentity (_≈_ {n}) 0# _+_
+-identityˡ [] = ≈-null
+-identityˡ (x ∷ xs) = ≈-cons (+ᵏ-identityˡ x) (+-identityˡ xs)

+-identityʳ : ∀ {n} → RightIdentity (_≈_ {n}) 0# _+_
+-identityʳ [] = ≈-null
+-identityʳ (x ∷ xs) = ≈-cons (+ᵏ-identityʳ x) (+-identityʳ xs)

+-identity : ∀ {n} → Identity (_≈_ {n}) 0# _+_
+-identity = +-identityˡ , +-identityʳ

+-comm : ∀ {n} -> Commutative (_≈_ {n}) _+_
+-comm [] [] = ≈-null
+-comm (x ∷ xs) (y ∷ ys) = ≈-cons (+ᵏ-comm x y) (+-comm xs ys)

*ᵏ-∙-compat : ∀ {n} (a b : K') (u : V n) -> ((a *ᵏ b) ∙ u) ≈ (a ∙ (b ∙ u))
*ᵏ-∙-compat a b [] = ≈-null
*ᵏ-∙-compat a b (x ∷ xs) = ≈-cons (*ᵏ-assoc a b x) (*ᵏ-∙-compat a b xs)

∙-+-distrib : ∀ {n} (a : K') (u v : V n) -> (a ∙ (u + v)) ≈ ((a ∙ u) + (a ∙ v))
∙-+-distrib a [] [] = ≈-null
∙-+-distrib a (x ∷ xs) (y ∷ ys) = ≈-cons (*ᵏ-+ᵏ-distribˡ a x y) (∙-+-distrib a xs ys)

∙-+ᵏ-distrib : ∀ {n} (a b : K') (u : V n) -> ((a +ᵏ b) ∙ u) ≈ ((a ∙ u) + (b ∙ u))
∙-+ᵏ-distrib a b [] = ≈-null
∙-+ᵏ-distrib a b (x ∷ u) = ≈-cons (*ᵏ-+ᵏ-distribʳ x a b) (∙-+ᵏ-distrib a b u)

∙-cong : ∀ {n} {a b : K'} {u v : V n} → a ≈ᵏ b -> u ≈ v -> (a ∙ u) ≈ (b ∙ v)
∙-cong rᵏ ≈-null = ≈-null
∙-cong rᵏ (≈-cons r rs) = ≈-cons (*ᵏ-cong rᵏ r) (∙-cong rᵏ rs)

∙-identity : ∀ {n} (x : V n) → (1ᵏ ∙ x) ≈ x
∙-identity [] = ≈-null
∙-identity (x ∷ xs) = ≈-cons (*ᵏ-identityˡ x) (∙-identity xs)

∙-absorbˡ : ∀ {n} (x : V n) → (0ᵏ ∙ x) ≈ 0#
∙-absorbˡ [] = ≈-null
∙-absorbˡ (x ∷ xs) = ≈-cons (*ᵏ-zeroˡ x) (∙-absorbˡ xs)

-‿inverseˡ : ∀ {n} → LeftInverse  (_≈_ {n}) 0# -_ _+_
-‿inverseˡ [] = ≈-null
-‿inverseˡ (x ∷ xs) = ≈-cons (-ᵏ‿inverseˡ x) (-‿inverseˡ xs)

-‿inverseʳ : ∀ {n} → RightInverse (_≈_ {n}) 0# -_ _+_
-‿inverseʳ [] = ≈-null
-‿inverseʳ (x ∷ xs) = ≈-cons (-ᵏ‿inverseʳ x) (-‿inverseʳ xs)

-‿inverse : ∀ {n} → Inverse (_≈_ {n}) 0# -_ _+_
-‿inverse = -‿inverseˡ , -‿inverseʳ

-‿cong : ∀ {n} -> Congruent₁ (_≈_ {n}) -_
-‿cong ≈-null = ≈-null
-‿cong (≈-cons r rs) = ≈-cons (-ᵏ‿cong r) (-‿cong rs)

+-++-distrib : ∀ {n p}
                 (u₁ : V n) (u₂ : V p)
                 (v₁ : V n) (v₂ : V p)
             -> ((u₁ ++ u₂) + (v₁ ++ v₂)) ≈ ((u₁ + v₁) ++ (u₂ + v₂))
+-++-distrib [] [] [] [] = ≈-null
+-++-distrib (x₁ ∷ xs₁) [] (y₁ ∷ ys₁) [] = ++-cong ≈-refl (+-++-distrib xs₁ [] ys₁ [])
+-++-distrib {0} {suc p} [] (x₂ ∷ xs₂) [] (y₂ ∷ ys₂) = ++-identityˡ ((x₂ +ᵏ y₂) ∷ (xs₂ + ys₂))
+-++-distrib (x₁ ∷ xs₁) u₂ (y₁ ∷ ys₁) v₂ =
  ≈-cons ≈ᵏ-refl (++-cong ≈-refl (+-++-distrib xs₁ u₂ ys₁ v₂))

∙-++-distrib : ∀ {n p} (a : K') (u : V n) (v : V p) -> (a ∙ (u ++ v)) ≈ ((a ∙ u) ++ (a ∙ v))
∙-++-distrib a [] v = ≈-refl
∙-++-distrib a (x ∷ xs) v = ≈-cons ≈ᵏ-refl (∙-++-distrib a xs v)

sum : ∀ {n} -> V n -> K'
sum = foldr _ _+ᵏ_ 0ᵏ

sum-tab : ∀ {n} -> (Fin n -> K') -> K'
sum-tab f = sum (tabulate f)

sum-cong : ∀ {n} {u v : V n} -> u ≈ v -> sum u ≈ᵏ sum v
sum-cong {0} PW.[] = ≈ᵏ-refl
sum-cong {suc n} (r PW.∷ rs) = +ᵏ-cong r (sum-cong {n} rs)

tabulate-cong-≡ : ∀ {n} {f g : Fin n -> K'} -> (∀ i -> f i ≡ g i) -> tabulate f ≡ tabulate g
tabulate-cong-≡ = VP.tabulate-cong

tabulate-cong : ∀ {n} {f g : Fin n -> K'} -> (∀ i -> f i ≈ᵏ g i) -> tabulate f ≈ tabulate g
tabulate-cong {0} r = PW.[]
tabulate-cong {suc n} r = r Fin.zero PW.∷ tabulate-cong (λ i → r (Fin.suc i))

sum-tab-cong : ∀ {n} {f g : Fin n -> K'} -> (∀ i -> f i ≈ᵏ g i) -> sum-tab f ≈ᵏ sum-tab g
sum-tab-cong r = sum-cong (tabulate-cong r)

*ᵏ-sum-tab-distribʳ : ∀ {n} (a : K') (f : Fin n -> K')
                    → (sum-tab f *ᵏ a) ≈ᵏ sum-tab (λ k -> f k *ᵏ a)
*ᵏ-sum-tab-distribʳ {0} a f = *ᵏ-zeroˡ a
*ᵏ-sum-tab-distribʳ {suc n} a f =
  begin
    sum-tab f *ᵏ a
  ≈⟨ *ᵏ-+ᵏ-distribʳ a (f Fin.zero) (sum-tab (λ k -> f (Fin.suc k))) ⟩
     (f Fin.zero *ᵏ a) +ᵏ (sum-tab (λ k -> f (Fin.suc k)) *ᵏ a)
  ≈⟨ +ᵏ-cong ≈ᵏ-refl  (*ᵏ-sum-tab-distribʳ a (λ k -> f (Fin.suc k))) ⟩
     sum-tab (λ k -> f k *ᵏ a)
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)

sum-assoc : ∀ {n} (f g : Fin n -> K')
          -> (sum-tab f +ᵏ sum-tab g) ≈ᵏ sum-tab (λ k -> f k +ᵏ g k)
sum-assoc {0} f g = +ᵏ-identityˡ 0ᵏ
sum-assoc {suc n} f g =
  begin
    sum-tab f +ᵏ sum-tab g
  ≡⟨⟩
    (f Fin.zero +ᵏ sum-tab {n} (λ k -> f (Fin.suc k))) +ᵏ (g Fin.zero +ᵏ sum-tab {n} (λ k -> g (Fin.suc k)))
  ≈⟨ +ᵏ-assoc (f Fin.zero) (sum-tab {n} (λ k -> f (Fin.suc k))) (g Fin.zero +ᵏ sum-tab {n} (λ k -> g (Fin.suc k))) ⟩
     f Fin.zero +ᵏ (sum-tab (λ k → f (Fin.suc k)) +ᵏ (g Fin.zero +ᵏ sum-tab (λ k → g (Fin.suc k))))
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (+ᵏ-comm (sum-tab (λ k -> f (Fin.suc k))) ((g Fin.zero +ᵏ sum-tab (λ k → g (Fin.suc k))))) ⟩
     f Fin.zero +ᵏ ((g Fin.zero +ᵏ sum-tab (λ k → g (Fin.suc k))) +ᵏ sum-tab (λ k → f (Fin.suc k)))
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (+ᵏ-assoc (g Fin.zero) (sum-tab (λ k -> g (Fin.suc k))) (sum-tab (λ k -> f (Fin.suc k)))) ⟩
     f Fin.zero +ᵏ (g Fin.zero +ᵏ (sum-tab (λ k → g (Fin.suc k)) +ᵏ sum-tab (λ k → f (Fin.suc k))))
  ≈⟨ ≈ᵏ-sym (+ᵏ-assoc (f Fin.zero) (g Fin.zero) (sum-tab (λ k → g (Fin.suc k)) +ᵏ sum-tab (λ k → f (Fin.suc k)))) ⟩
     (f Fin.zero +ᵏ g Fin.zero) +ᵏ (sum-tab (λ k → g (Fin.suc k)) +ᵏ sum-tab (λ k → f (Fin.suc k)))
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (+ᵏ-comm (sum-tab λ k -> g (Fin.suc k)) (sum-tab λ k -> f (Fin.suc k))) ⟩
     (f Fin.zero +ᵏ g Fin.zero) +ᵏ (sum-tab (λ k → f (Fin.suc k)) +ᵏ sum-tab (λ k → g (Fin.suc k)))
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (sum-assoc {n} (λ k -> f (Fin.suc k)) (λ k -> g (Fin.suc k))) ⟩
     sum-tab (λ k -> f k +ᵏ g k)
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)

sum-tab-0ᵏ : ∀ {n} -> sum-tab {n} (λ k -> 0ᵏ) ≈ᵏ 0ᵏ
sum-tab-0ᵏ {0} = ≈ᵏ-refl
sum-tab-0ᵏ {suc n} = ≈ᵏ-trans (+ᵏ-identityˡ (sum-tab {n} λ k -> 0ᵏ)) (sum-tab-0ᵏ {n})

sum-tab-swap : ∀ {n p} (f : Fin n -> Fin p -> K') (g : Fin n -> K')
             -> sum-tab (λ k′ -> sum-tab λ k -> f k k′ *ᵏ g k)
             ≈ᵏ sum-tab (λ k -> sum-tab λ k′ -> f k k′ *ᵏ g k)
sum-tab-swap {n} {0} f g = ≈ᵏ-sym (sum-tab-0ᵏ {n})
sum-tab-swap {n} {suc p} f g =
  begin
    sum-tab (λ k′ -> sum-tab λ k -> f k k′ *ᵏ g k)
  ≡⟨⟩
    (sum-tab λ k -> f k Fin.zero *ᵏ g k) +ᵏ (sum-tab λ k′ -> sum-tab λ k -> f k (Fin.suc k′) *ᵏ g k)
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (sum-tab-swap {n} {p} (λ k k′ -> f k (Fin.suc k′)) g) ⟩
     sum-tab (λ k → f k Fin.zero *ᵏ g k) +ᵏ sum-tab (λ k → sum-tab (λ k′ → f k (Fin.suc k′) *ᵏ g k))
  ≈⟨ +ᵏ-cong ≈ᵏ-refl (sum-tab-cong λ k -> ≈ᵏ-sym (*ᵏ-sum-tab-distribʳ {p} (g k) λ k′ -> f k (Fin.suc k′))) ⟩
     sum-tab (λ k → f k Fin.zero *ᵏ g k) +ᵏ sum-tab (λ k → sum-tab (λ k′ → f k (Fin.suc k′)) *ᵏ g k)
  ≈⟨ sum-assoc (λ k -> f k Fin.zero *ᵏ g k) (λ k -> sum-tab (λ k′ -> f k (Fin.suc k′)) *ᵏ g k) ⟩
     sum-tab (λ k -> (f k Fin.zero *ᵏ g k) +ᵏ (sum-tab (λ k′ -> f k (Fin.suc k′)) *ᵏ g k))
  ≈⟨ sum-tab-cong (λ k -> ≈ᵏ-sym (*ᵏ-+ᵏ-distribʳ (g k) (f k Fin.zero) (sum-tab λ k′ -> f k (Fin.suc k′)))) ⟩
     sum-tab (λ k -> sum-tab (λ k′ -> f k k′) *ᵏ g k)
  ≈⟨ sum-tab-cong (λ k -> *ᵏ-sum-tab-distribʳ {suc p} (g k) (λ k′ -> f k k′)) ⟩
     sum-tab (λ k -> sum-tab λ k′ -> f k k′ *ᵏ g k)
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)

sum-tab-δ : ∀ {n} (f : Fin n -> K') (i : Fin n) -> sum-tab (λ k -> δ i k *ᵏ f k) ≈ᵏ f i
sum-tab-δ {suc n} f i@Fin.zero =
  begin
    sum-tab (λ k -> δ i k *ᵏ f k)
  ≡⟨⟩
    (δ i i *ᵏ f i) +ᵏ sum-tab (λ k -> δ i (Fin.suc k) *ᵏ f (Fin.suc k))
  ≈⟨ +ᵏ-cong (*ᵏ-identityˡ (f i))
     (sum-tab-cong (λ k -> ≈ᵏ-trans (*ᵏ-cong (δ-cancelˡ {n} k) ≈ᵏ-refl) (*ᵏ-zeroˡ (f (Fin.suc k))))) ⟩
    f i +ᵏ sum-tab {n} (λ k -> 0ᵏ)
  ≈⟨ ≈ᵏ-trans (+ᵏ-cong ≈ᵏ-refl (sum-tab-0ᵏ {n})) (+ᵏ-identityʳ (f i)) ⟩
    f i
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)
sum-tab-δ {suc n} f (Fin.suc i) =
  begin
    sum-tab (λ k -> δ (Fin.suc i) k *ᵏ f k)
  ≡⟨⟩
    (δ (Fin.suc i) (Fin.zero {suc n}) *ᵏ f Fin.zero) +ᵏ sum-tab (λ k -> δ (Fin.suc i) (Fin.suc k) *ᵏ f (Fin.suc k))
  ≈⟨ +ᵏ-cong (≈ᵏ-trans (*ᵏ-cong (δ-cancelʳ {p = suc n} (Fin.suc i)) ≈ᵏ-refl) (*ᵏ-zeroˡ (f Fin.zero))) ≈ᵏ-refl ⟩
    0ᵏ +ᵏ sum-tab (λ k → δ (Fin.suc i) (Fin.suc k) *ᵏ f (Fin.suc k))
  ≈⟨ +ᵏ-identityˡ (sum-tab λ k -> δ (Fin.suc i) (Fin.suc k) *ᵏ f (Fin.suc k)) ⟩
    sum-tab (λ k → δ (Fin.suc i) (Fin.suc k) *ᵏ f (Fin.suc k))
  ≈⟨ sum-tab-δ (λ k -> f (Fin.suc k)) i ⟩
    f (Fin.suc i)
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)

module _ {n} where
  open import Algebra.Structures (_≈_ {n})
  open import Algebra.Linear.Structures.Bundles

  isMagma : IsMagma _+_
  isMagma = record
    { isEquivalence = ≈-isEquiv
    ; ∙-cong        = +-cong
    }

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

  isMonoid : IsMonoid _+_ 0#
  isMonoid = record
    { isSemigroup = isSemigroup
    ; identity    = +-identity
    }

  isGroup : IsGroup _+_ 0# -_
  isGroup = record
    { isMonoid = isMonoid
    ; inverse  = -‿inverse
    ; ⁻¹-cong   = -‿cong
    }

  isAbelianGroup : IsAbelianGroup _+_ 0# -_
  isAbelianGroup = record
    { isGroup = isGroup
    ; comm    = +-comm
    }

  open VS K

  isVectorSpace : VS.IsVectorSpace K (_≈_ {n}) _+_ _∙_ -_ 0#
  isVectorSpace = record
    { isAbelianGroup  = isAbelianGroup
    ; *ᵏ-∙-compat     = *ᵏ-∙-compat
    ; ∙-+-distrib     = ∙-+-distrib
    ; ∙-+ᵏ-distrib    = ∙-+ᵏ-distrib
    ; ∙-cong          = ∙-cong
    ; ∙-identity      = ∙-identity
    ; ∙-absorbˡ        = ∙-absorbˡ
    }

  vectorSpace : VectorSpace K k (k ⊔ ℓ)
  vectorSpace = record { isVectorSpace = isVectorSpace }

  open import Algebra.Linear.Morphism.VectorSpace K

  open import Algebra.Linear.Morphism.Bundles K

  open import Function

  embed : LinearIsomorphism vectorSpace vectorSpace
  embed = record
    { ⟦_⟧ = id
    ; isLinearIsomorphism = record
        { isLinearMonomorphism = record
            { isLinearMap = record
                { isAbelianGroupMorphism = record
                    { gp-homo = record
                        { mn-homo = record
                          { sm-homo = record
                              { ⟦⟧-cong = id
                              ; ∙-homo = λ x y → ≈-refl
                              }
                          ; ε-homo = ≈-refl
                          }
                        }
                    }
                ; ∙-homo = λ c u → ≈-refl
                }
            ; injective = id
            }
        ; surjective = λ y → y , ≈-refl
        }
    }


open import Algebra.Apartness using (HeytingField)

module Data.Real.Abstract.Structures {c ℓ₁ ℓ₂} (f : HeytingField c ℓ₁ ℓ₂) where

open HeytingField f

open import Relation.Binary using (Rel; IsStrictTotalOrder)
open import Level using (_⊔_)
open import Data.Product using (_×_; ∃-syntax)

import Data.Nat as ℕ
open ℕ using (ℕ; zero; suc)

record IsOrderedHeytingField
  {r} (_<_ : Rel Carrier r) : Set (c ⊔ ℓ₁ ⊔ r)
  where

  field
    isStrictTotalOrder : IsStrictTotalOrder _≈_ _<_
    ordered : ∀ a b c → a < b → (a + c) < (b + c) × (c + a) < (c + b)

_ℕ*_ : ℕ → Carrier → Carrier
zero ℕ* x = 1#
suc n ℕ* x = x * (n ℕ* x)

record IsArchimedanHeytingField
  {r} (_<_ : Rel Carrier r) : Set (c ⊔ ℓ₁ ⊔ r)
  where

  field
    dense : ∀ a b → a < b → ∃[ c ] (a < c) × (c < b)
    archimedan : ∀ a b → 0# < a → 0# < b → ∃[ n ] (b < (n ℕ* a))


{-# OPTIONS --without-K --safe #-}
open import Categories.Category
open import Categories.Functor hiding (id)

-- category of cones "over a Functor F"

module Categories.Category.Construction.Cones
  {o ℓ e} {o′ ℓ′ e′} {C : Category o ℓ e} {J : Category o′ ℓ′ e′} (F : Functor J C) where

open import Data.Product
import Categories.Diagram.Cone as Co
import Categories.Morphism as Mor
import Categories.Morphism.IsoEquiv as IsoEquiv
import Categories.Morphism.Reasoning as Reas

open Category C
open HomReasoning
open Mor C using (_≅_)
open IsoEquiv C using (_≃_; ⌞_⌟)
open Reas C

open Co F public
open Apex
open Cone
open Cone⇒

Cones : Category _ _ _
Cones = record
  { Obj       = Cone
  ; _⇒_       = Cone⇒
  ; _≈_       = λ f g → arr f ≈ arr g
  ; id        = record
    { arr     = id
    ; commute = identityʳ
    }
  ; _∘_       = λ {A B C} f g → record
    { arr     = arr f ∘ arr g
    ; commute = λ {X} → begin
      ψ C X ∘ arr f ∘ arr g ≈⟨ pullˡ (commute f) ⟩
      ψ B X ∘ arr g         ≈⟨ commute g ⟩
      ψ A X                 ∎
    }
  ; assoc     = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv     = record
    { refl  = refl
    ; sym   = sym
    ; trans = trans
    }
  ; ∘-resp-≈  = ∘-resp-≈
  }

module Cones = Category Cones

private
  variable
    K K′ : Cone
    X : Obj
  module CM = Mor Cones
  module CI = IsoEquiv Cones

open CM using () renaming (_≅_ to _⇔_)
open CI using () renaming (_≃_ to _↮_)

cone-resp-iso : ∀ (κ : Cone) → Cone.N κ ≅ X → Σ[ κ′ ∈ Cone ] κ ⇔ κ′
cone-resp-iso {X = X} κ κ≅X = record
  { apex = record
    { ψ       = λ Y → Cone.ψ κ Y ∘ to
    ; commute = λ f → pullˡ (Cone.commute κ f)
    }
  } , record
  { from = record
    { arr     = from
    ; commute = cancelʳ isoˡ
    }
  ; to   = record
    { arr     = to
    ; commute = refl
    }
  ; iso  = record
    { isoˡ    = isoˡ
    ; isoʳ    = isoʳ
    }
  }
  where open _≅_ κ≅X
        open Cone
        open Apex

iso-cone⇒iso-apex : K ⇔ K′ → N K ≅ N K′
iso-cone⇒iso-apex K⇔K′ = record
  { from = arr from
  ; to   = arr to
  ; iso  = record
    { isoˡ = isoˡ
    ; isoʳ = isoʳ
    }
  }
  where open _⇔_ K⇔K′

↮⇒-≃ : ∀ {i₁ i₂ : K ⇔ K′} → i₁ ↮ i₂ → iso-cone⇒iso-apex i₁ ≃ iso-cone⇒iso-apex i₂
↮⇒-≃ i₁↮i₂ = ⌞ from-≈ ⌟
  where open _↮_ i₁↮i₂

-- -- .up-to-iso-cone-unique : ∀ L L′ → (i : proj-cone L ⇿ proj-cone L′) → up-to-iso-cone L L′ ≜ⁱ i
-- -- up-to-iso-cone-unique L L′ i = T.up-to-iso-unique (Cones F) (terminal L) (terminal L′) i

-- -- -- XXX probably not true -- what is?  only the above?
-- -- -- .up-to-iso-unique : ∀ L L′ → (i : vertex L ≅ vertex L′) → up-to-iso L L′ ≡ⁱ i
-- -- -- up-to-iso-unique L L′ i = ≜ⁱ⇒≡ⁱ {!up-to-iso-unique-cone L L′ !}

-- -- .up-to-iso-cone-invˡ : ∀ {L κ} {i : proj-cone L ⇿ κ} → up-to-iso-cone L (transport-by-iso-cone L i) ≜ⁱ i
-- -- up-to-iso-cone-invˡ {L} {i = i} = up-to-iso-cone-unique L (transport-by-iso-cone L i) i

-- -- .up-to-iso-invˡ : ∀ {L X} {i : vertex L ≅ X} → up-to-iso L (transport-by-iso L i) ≡ⁱ i
-- -- up-to-iso-invˡ {L₁} {i = i} = ≜ⁱ⇒≡ⁱ (up-to-iso-cone-invˡ {L₁} {i = proj₂ (cone-resp-iso (proj-cone L₁) i)})

-- -- up-to-iso-cone-invʳ : ∀ {L L′} → proj-cone (transport-by-iso-cone L (up-to-iso-cone L L′)) ≜ proj-cone L′
-- -- up-to-iso-cone-invʳ {L} {L′} = ≜-refl

-- -- up-to-iso-invʳ : ∀ {L L′} → vertex (transport-by-iso L (up-to-iso L L′)) ≣ vertex L′
-- -- up-to-iso-invʳ {t} {t′} = ≣-refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Natural numbers represented in binary natively in Agda.
------------------------------------------------------------------------

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

module Data.Nat.Binary where

open import Data.Nat.Binary.Base public
open import Data.Nat.Binary.Properties public using (_≟_)


import      Lvl
open import Structure.Setoid
open import Type

module Automaton.Deterministic.Finite where

open import Automaton.Deterministic
open import Data.Boolean
open import Data.Boolean.Stmt
open import Data.List renaming (∅ to ε ; _⊰_ to _·_)
open import Data.List.Setoid
open import Data.List.Functions using (postpend ; _++_)
open import Data.List.Proofs
open import Functional
open import Logic.Propositional
open import Logic
open import Sets.ExtensionalPredicateSet using (PredSet ; intro ; _∈_ ; _∋_ ; ⊶ ; [∋]-binaryRelator)
import      Structure.Function.Names as Names
open import Structure.Operator
open import Structure.Relator.Properties
open import Type.Size.Finite

private variable ℓₚ ℓₛ ℓₑ₁ ℓₐ ℓₑ₂ : Lvl.Level

module _
  {ℓₚ}
  (State : Type{ℓₛ}) ⦃ equiv-state : Equiv{ℓₑ₁}(State) ⦄
  (Alphabet : Type{ℓₐ}) ⦃ equiv-alphabet : Equiv{ℓₑ₂}(Alphabet) ⦄
  where

  record DFA : Type{ℓₛ Lvl.⊔ ℓₑ₁ Lvl.⊔ ℓₑ₂ Lvl.⊔ ℓₐ Lvl.⊔ Lvl.𝐒(ℓₚ)} where
    field
      ⦃ State-finite ⦄ : Finite(State)
      ⦃ Alphabet-finite ⦄ : Finite(Alphabet)
      automata : Deterministic{ℓₚ = ℓₚ}(State)(Alphabet)
    open Deterministic(automata) hiding (transitionedAutomaton ; wordTransitionedAutomaton) public

    transitionedAutomaton : Alphabet → DFA
    transitionedAutomaton c = record{automata = Deterministic.transitionedAutomaton(automata) c}

    wordTransitionedAutomaton : Word → DFA
    wordTransitionedAutomaton w = record{automata = Deterministic.wordTransitionedAutomaton(automata) w}

    postulate isFinal : State → Bool
    postulate isFinal-correctness : ∀{s} → IsTrue(isFinal s) ↔ (s ∈ Final)

    isWordAccepted : Word → Bool
    isWordAccepted(w) = isFinal(wordTransition(start)(w))

  pattern dfa {fin-Q} {fin-Σ} δ {δ-op} q₀ F = record{State-finite = fin-Q ; Alphabet-finite = fin-Σ ; automata = deterministic δ ⦃ δ-op ⦄ q₀ F}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Lists where every consecutative pair of elements is related.
------------------------------------------------------------------------

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

module Data.List.Relation.Unary.Linked {a} {A : Set a} where

open import Data.List.Base using (List; []; _∷_)
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
open import Data.Product as Prod using (_,_; _×_; uncurry; <_,_>)
open import Function using (id; _∘_)
open import Level using (Level; _⊔_)
open import Relation.Binary as B using (Rel; _⇒_)
open import Relation.Binary.Construct.Intersection renaming (_∩_ to _∩ᵇ_)
open import Relation.Binary.PropositionalEquality
open import Relation.Unary as U renaming (_∩_ to _∩ᵘ_) hiding (_⇒_)
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Decidable as Dec using (map′)
open import Relation.Nullary.Product using (_×-dec_)

private
  variable
    p q r ℓ : Level

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

-- Linked R xs means that every consecutative pair of elements
-- in xs is a member of relation R.

infixr 5 _∷_

data Linked (R : Rel A ℓ) : List A → Set (a ⊔ ℓ) where
  []  : Linked R []
  [-] : ∀ {x} → Linked R (x ∷ [])
  _∷_ : ∀ {x y xs} → R x y → Linked R (y ∷ xs) → Linked R (x ∷ y ∷ xs)

------------------------------------------------------------------------
-- Operations

module _ {R : Rel A p} {x y xs} where

  head : Linked R (x ∷ y ∷ xs) → R x y
  head (Rxy ∷ Rxs) = Rxy

  tail : Linked R (x ∷ y ∷ xs) → Linked R (y ∷ xs)
  tail (Rxy ∷ Rxs) = Rxs

module _ {R : Rel A p} {S : Rel A q} where

  map : R ⇒ S → Linked R ⊆ Linked S
  map R⇒S []           = []
  map R⇒S [-]          = [-]
  map R⇒S (x~xs ∷ pxs) = R⇒S x~xs ∷ map R⇒S pxs

module _ {P : Rel A p} {Q : Rel A q} {R : Rel A r} where

  zipWith : P ∩ᵇ Q ⇒ R → Linked P ∩ᵘ Linked Q ⊆ Linked R
  zipWith f ([] , [])             = []
  zipWith f ([-] , [-])           = [-]
  zipWith f (px ∷ pxs , qx ∷ qxs) = f (px , qx) ∷ zipWith f (pxs , qxs)

  unzipWith : R ⇒ P ∩ᵇ Q → Linked R ⊆ Linked P ∩ᵘ Linked Q
  unzipWith f []         = [] , []
  unzipWith f [-]        = [-] , [-]
  unzipWith f (rx ∷ rxs) = Prod.zip _∷_ _∷_ (f rx) (unzipWith f rxs)

module _ {P : Rel A p} {Q : Rel A q} where

  zip : Linked P ∩ᵘ Linked Q ⊆ Linked (P ∩ᵇ Q)
  zip = zipWith id

  unzip : Linked (P ∩ᵇ Q) ⊆ Linked P ∩ᵘ Linked Q
  unzip = unzipWith id

------------------------------------------------------------------------
-- Properties of predicates preserved by Linked

module _ {R : Rel A ℓ} where

  linked? : B.Decidable R → U.Decidable (Linked R)
  linked? R? []           = yes []
  linked? R? (x ∷ [])     = yes [-]
  linked? R? (x ∷ y ∷ xs) =
    map′ (uncurry _∷_) < head , tail > (R? x y ×-dec linked? R? (y ∷ xs))

  irrelevant : B.Irrelevant R → U.Irrelevant (Linked R)
  irrelevant irr []           []           = refl
  irrelevant irr [-]          [-]          = refl
  irrelevant irr (px₁ ∷ pxs₁) (px₂ ∷ pxs₂) =
    cong₂ _∷_ (irr px₁ px₂) (irrelevant irr pxs₁ pxs₂)

  satisfiable : U.Satisfiable (Linked R)
  satisfiable = [] , []