-- Non-indexed (plain) monads in form of Kleisli triple, presented in point-free style.

module Control.Monad.KleisliTriple where

open import Function using (id) renaming (_∘′_ to _∘_)
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

open import Axiom.FunctionExtensionality
open import Control.Functor

record IsKleisliTriple (M : Set → Set) : Set₁ where

  -- Methods.

  field
    return : ∀ {A  } → A → M A
    bind′  : ∀ {A B} → (A → M B) → M A → M B

  -- Laws.

  field

    bind′-β : ∀ {A B} {k : A → M B} →

      bind′ k ∘ return ≡ k

    bind′-η : ∀ {A} →

      bind′ {A = A} return ≡ id

    bind′-∘ : ∀ {A B C} {k : A → M B} {l : B → M C} →

      bind′ (bind′ l ∘ k) ≡ bind′ l ∘ bind′ k

  -- Notations for bind′.

  -- Postfix category-theoretical notation.
  _✶ = bind′

  -- Infix Haskell notation.
  infixl 6 _=<<_
  _=<<_ = bind′

  -- Kleisli composition.

  infixl 6 _<=<_

  _<=<_ : ∀ {A B C : Set} (l : B → M C) (k : A → M B) → (A → M C)
  l <=< k = bind′ l ∘ k

  -- Functoriality.

  isFunctor : IsFunctor M
  isFunctor = record
    { ops  = record { map = map  }
    ; laws = record { map-id = bind′-η ; map-∘ = sym (map-∘-sym _ _) }
    }
    where
      map : ∀ {A B} → (A → B) → M A → M B
      map f = bind′ (return ∘ f)

      map-∘-sym : ∀ {A B C} (f : A → B) (g : B → C) → map g ∘ map f ≡ map (g ∘ f)
      map-∘-sym f g = begin
        map g ∘ map f                            ≡⟨⟩
        bind′ (return ∘ g) ∘ bind′ (return ∘ f)  ≡⟨ sym bind′-∘ ⟩
        bind′ (bind′ (return ∘ g) ∘ return ∘ f)  ≡⟨ cong (λ z → bind′ (z ∘ f)) bind′-β ⟩
        bind′ (return ∘ g ∘ f)                   ≡⟨⟩
        map (g ∘ f)                              ∎

  open IsFunctor isFunctor public

-- Monads in Kleisli Triple presentation.

record KleisliTriple : Set₁ where
  field
    M  : Set → Set
    M! : IsKleisliTriple M

  open IsKleisliTriple M! public


open import Common
open import Global
open import Projection
open import Local

open import Data.Fin
open import Data.Product
open import Data.Vec
open import Relation.Binary.PropositionalEquality

n = 4

Role = Fin n

p : Role
p = zero

q : Role
q = suc zero

r : Role
r = suc (suc zero)

s : Role
s = suc (suc (suc zero))

l : Label
l = 0

l′ : Label
l′ = 1

g₁ : Global n
g₁ = msgSingle′ p q l endG

lp : Local n
lp = sendSingle q l endL

g₁-proj-p-is-lp : project g₁ p ≡ lp
g₁-proj-p-is-lp = refl

lq : Local n
lq = recvSingle p l endL

g₁-proj-q-is-lq : project g₁ q ≡ lq
g₁-proj-q-is-lq = refl

p→q : Action n
p→q = action′ p q l

r→s : Action n
r→s = action′ r s l′

g₂ : Global n
g₂ = msgSingle′ r s l′ g₁

g₂′ : Global n
g₂′ = msgSingle′ r s l′ endG

g₁→end : g₁ - p→q →g endG
g₁→end = →g-prefix

g₂→g₁ : g₂ - r→s →g g₁
g₂→g₁ = →g-prefix

g₂→g₂′ : g₂ - p→q →g g₂′
g₂→g₂′ = →g-cont g₁→end (λ ()) (λ ()) (λ ()) (λ ())

g₁-proj-p→end : (p , project g₁ p) - p→q →l (p , endL)
g₁-proj-p→end = →l-send p refl λ ()

g₁-proj-q→end : (q , project g₁ q) - p→q →l (q , endL)
g₁-proj-q→end = →l-recv q refl λ ()

g₂-proj-p→g₂′-proj-p : (p , project g₂ p) - p→q →l (p , project g₂′ p)
g₂-proj-p→g₂′-proj-p = →l-send p refl λ ()

g₂-proj-q→g₂′-proj-q : (q , project g₂ q) - p→q →l (q , project g₂′ q)
g₂-proj-q→g₂′-proj-q = →l-recv q refl λ ()

c₁ : Configuration n
c₁ = lp ∷ lq ∷ endL ∷ endL ∷ []

cEnd : Configuration n
cEnd = endL ∷ endL ∷ endL ∷ endL ∷ []

c₁→cEnd : c₁ - p→q →c cEnd
c₁→cEnd = →c-comm c₁ (λ ()) refl refl refl g₁-proj-p→end g₁-proj-q→end

g₁↔c₁ : g₁ ↔ c₁
g₁↔c₁ = record { isProj = refls }
  where
    refls : (p : Fin n) -> lookup c₁ p ≡ project g₁ p
    refls zero = refl
    refls (suc zero) = refl
    refls (suc (suc zero)) = refl
    refls (suc (suc (suc zero))) = refl


open import Oscar.Prelude
open import Oscar.Class
open import Oscar.Class.Reflexivity
open import Oscar.Class.Symmetry
open import Oscar.Class.Transitivity
open import Oscar.Class.IsEquivalence
open import Oscar.Class.Setoid
open import Oscar.Data.Proposequality

module Oscar.Property.Setoid.Proposequality where

module _ {𝔬} {𝔒 : Ø 𝔬} where

  instance

    𝓡eflexivityProposequality : Reflexivity.class Proposequality⟦ 𝔒 ⟧
    𝓡eflexivityProposequality .⋆ = !

    𝓢ymmetryProposequality : Symmetry.class Proposequality⟦ 𝔒 ⟧
    𝓢ymmetryProposequality .⋆ ∅ = !

    𝓣ransitivityProposequality : Transitivity.class Proposequality⟦ 𝔒 ⟧
    𝓣ransitivityProposequality .⋆ ∅ y∼z = y∼z

    IsEquivalenceProposequality : IsEquivalence Proposequality⟦ 𝔒 ⟧
    IsEquivalenceProposequality = ∁

module _ {𝔬} (𝔒 : Ø 𝔬) where

  SetoidProposequality : Setoid _ _
  SetoidProposequality = ∁ Proposequality⟦ 𝔒 ⟧


module neq where

open import eq
open import negation

----------------------------------------------------------------------
-- syntax
----------------------------------------------------------------------

infix 4 _≢_

----------------------------------------------------------------------
-- defined types
----------------------------------------------------------------------

_≢_ : ∀ {ℓ}{A : Set ℓ} → A → A → Set ℓ
x ≢ y = ¬ (x ≡ y)

sym-≢ : ∀{ℓ}{A : Set ℓ}{x y : A} → x ≢ y → y ≢ x
sym-≢ p₁ p₂ = p₁ (sym p₂)


module Oscar.Data.Term.Substitution.Core {𝔣} (FunctionName : Set 𝔣) where

open import Oscar.Data.Term.Core FunctionName
open import Oscar.Data.Term.Substitution.Core.bootstrap FunctionName public hiding (_◃Term_; _◃VecTerm_)
open import Oscar.Data.Nat.Core
open import Oscar.Data.Fin.Core
open import Oscar.Data.Vec.Core
open import Oscar.Data.Equality.Core
open import Oscar.Data.Product.Core
open import Oscar.Function
open import Oscar.Level

⊸-Property : {ℓ : Level} → ℕ → Set (lsuc ℓ ⊔ 𝔣)
⊸-Property {ℓ} m = ∀ {n} → m ⊸ n → Set ℓ

_≐_ : {m n : ℕ} → m ⊸ n → m ⊸ n → Set 𝔣
f ≐ g = ∀ x → f x ≡ g x

⊸-Extensional : {ℓ : Level} {m : ℕ} → ⊸-Property {ℓ} m → Set (ℓ ⊔ 𝔣)
⊸-Extensional P = ∀ {m f g} → f ≐ g → P {m} f → P g

⊸-ExtentionalProperty : {ℓ : Level} → ℕ → Set (lsuc ℓ ⊔ 𝔣)
⊸-ExtentionalProperty {ℓ} m = Σ (⊸-Property {ℓ} m) ⊸-Extensional


module DeBruijnExSubst where

open import Prelude -- using (_∘_)       -- composition, identity
open import Data.Maybe
open import Logic.Identity renaming (subst to subst≡)
import Logic.ChainReasoning
module Chain = Logic.ChainReasoning.Poly.Homogenous _≡_ (\x -> refl) (\x y z -> trans)
open Chain

open import DeBruijn

-- untyped de Bruijn terms
data LamE (A : Set) : Set where
    varE  : A -> LamE A
    appE  : LamE A -> LamE A -> LamE A
    absE  : LamE (Maybe A) -> LamE A
    flatE : LamE (LamE A) -> LamE A

-- functoriality of LamE
lamE : {A B : Set} -> (A -> B) -> LamE A -> LamE B
lamE f (varE a)     = varE (f a)
lamE f (appE t1 t2) = appE (lamE f t1) (lamE f t2)
lamE f (absE r)     = absE (lamE (fmap f) r)
lamE f (flatE r)    = flatE (lamE (lamE f) r)

{- from TCS 05 paper
eval' : {A B : Set} -> (A -> B) -> LamE A -> Lam B
eval' f (varE a)     = var (f a)
eval' f (appE t1 t2) = app (eval' f t1) (eval' f t2)
eval' f (absE r)     = abs (eval' (fmap f) r)
eval' f (flatE r)    = subst id (eval' (eval' f) r)

eval : {A : Set} -> LamE A -> Lam A
eval = eval' id
-}

eval : {A : Set} -> LamE A -> Lam A
eval (varE a)     = var a
eval (appE t1 t2) = app (eval t1) (eval t2)
eval (absE r)     = abs (eval r)
eval (flatE r)    = subst eval (eval r)

evalNAT : {A B : Set}(f : A -> B) -> (t : LamE A) ->
  eval (lamE f t) ≡ lam f (eval t)
evalNAT f (varE a)     = refl
evalNAT f (appE t1 t2) =
  chain> eval (lamE f (appE t1 t2))
     === eval (appE (lamE f t1) (lamE f t2))
       by refl
     === app (eval (lamE f t1)) (eval (lamE f t2))
       by refl
     === app (lam f (eval t1))  (eval (lamE f t2))
       by cong (\ x -> app x (eval (lamE f t2))) (evalNAT f t1)
     === app (lam f (eval t1))  (lam f (eval t2))
       by cong (\ x -> app (lam f (eval t1)) x)  (evalNAT f t2)
     === lam f (app (eval t1) (eval t2))
       by refl
     === lam f (eval (appE t1 t2))
       by refl
evalNAT f (absE r) =
  chain> eval (lamE f (absE r))
     === eval (absE (lamE (fmap f) r))   by  refl
     === abs (eval (lamE (fmap f) r))    by  refl
     === abs (lam (fmap f) (eval r))     by  cong abs (evalNAT (fmap f) r)
     === lam f (abs (eval r))            by  refl
     === lam f (eval (absE r))           by  refl
evalNAT f (flatE r) =
  chain> eval (lamE f (flatE r))
     === eval (flatE (lamE (lamE f) r))
       by refl
     === subst eval (eval (lamE (lamE f) r))
       by refl
     === subst eval (lam (lamE f) (eval r))
       by cong (subst eval) (evalNAT (lamE f) r)
     === subst (eval ∘ lamE f) (eval r)
       by substLaw1 (lamE f) eval (eval r)
     === subst (lam f ∘ eval) (eval r)
       by substExt _ _ (evalNAT f) (eval r)
     === lam f (subst eval (eval r))
       by substLaw2 f eval (eval r)
     === lam f (eval (flatE r))
       by refl

evalNATcor : {A : Set}(ee : LamE (LamE A)) ->
  subst id (eval (lamE eval ee)) ≡ eval (flatE ee)
evalNATcor ee =
  chain> subst id (eval (lamE eval ee))
     === subst id (lam eval (eval ee))  by  cong (subst id) (evalNAT eval ee)
     === subst eval (eval ee)           by  substLaw1 eval id (eval ee)
     === eval (flatE ee)                by  refl


{-# OPTIONS --without-K #-}
open import HoTT.Base
open import HoTT.Equivalence
open import HoTT.Identity

module HoTT.Identity.Pi where

open variables
private variable f g h : Π A P

module _ {f g : Π A P} where
  -- Axiom 2.9.3 - function extensionality
  postulate
    happly-isequiv : isequiv (happly {f = f} {g})

  module _ where
    open qinv (isequiv→qinv happly-isequiv) renaming (g to happly⁻¹)
    abstract
      funext : f ~ g → f == g
      funext = happly⁻¹
      =Π-η : funext ∘ happly ~ id
      =Π-η = η
      =Π-β : happly ∘ funext ~ id
      =Π-β = ε

=Π-equiv : f == g ≃ f ~ g
=Π-equiv = happly , happly-isequiv

funext-∙ₕ : (α : f ~ g) (β : g ~ h) → funext (α ∙ₕ β) == funext α ∙ funext β
funext-∙ₕ α β = ap funext (funext λ x → (happly (=Π-β α) x ⋆ happly (=Π-β β) x) ⁻¹) ∙ p
  where
  p : funext (happly (funext α) ∙ₕ happly (funext β)) == funext α ∙ funext β
  p rewrite funext α rewrite funext β = =Π-η refl

funext-ap : {P : A → 𝒰 i} {Q : A → 𝒰 j} {g h : Π A P}
            (f : {a : A} → P a → Q a) (α : g ~ h) →
            funext (ap f ∘ α) == ap (f ∘_) (funext α)
funext-ap f α = ap (λ α → funext (ap f ∘ α)) (=Π-β α) ⁻¹ ∙ p
  where
  p : funext (ap f ∘ happly (funext α)) == ap (f ∘_) (funext α)
  p rewrite funext α = =Π-η refl


{-# OPTIONS --cubical --safe --postfix-projections #-}

module Relation.Nullary.Decidable.Logic where

open import Prelude
open import Data.Sum
open import Relation.Nullary.Decidable

disj : (A → C) → (B → C) → (¬ A → ¬ B → ¬ C) → Dec A → Dec B → Dec C
disj l r n x y .does = x .does or y .does
disj l r n (yes x) y .why = l x
disj l r n (no  x) (yes y) .why = r y
disj l r n (no ¬x) (no ¬y) .why = n ¬x ¬y

conj : (A → B → C) → (¬ A → ¬ C) → (¬ B → ¬ C) → Dec A → Dec B → Dec C
conj c l r x y .does = x .does and y .does
conj c l r (no ¬x) y .why = l ¬x
conj c l r (yes x) (no ¬y) .why = r ¬y
conj c l r (yes x) (yes y) .why = c x y

negate : (A → ¬ B) → (¬ A → B) → Dec A → Dec B
negate t f d .does = not (d .does)
negate t f (yes d) .why = t d
negate t f (no ¬d) .why = f ¬d

! : Dec A → Dec (¬ A)
! = negate (λ x ¬x → ¬x x) id

infixl 7 _&&_
_&&_ : Dec A → Dec B → Dec (A × B)
_&&_ = conj _,_ (_∘ fst) (_∘ snd)

infixl 6 _||_
_||_ : Dec A → Dec B → Dec (A ⊎ B)
_||_ = disj inl inr either


------------------------------------------------------------------------------
-- Properties related with lists using instances of the induction principle
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOT.FOTC.Data.List.Induction.Instances.PropertiesATP where

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Data.List
open import FOTC.Data.Nat.Type

------------------------------------------------------------------------------
-- Totality properties

lengthList-N-ind-instance :
  N (length []) →
  (∀ x {xs} → N (length xs) → N (length (x ∷ xs))) →
  ∀ {xs} → List xs → N (length xs)
lengthList-N-ind-instance = List-ind (λ as → N (length as))

postulate lengthList-N : ∀ {xs} → List xs → N (length xs)
{-# ATP prove lengthList-N lengthList-N-ind-instance #-}

++-List-ind-instance :
  ∀ {ys} →
  List ([] ++ ys) →
  (∀ x {xs} → List (xs ++ ys) → List ((x ∷ xs) ++ ys)) →
  ∀ {xs} → List xs → List (xs ++ ys)
++-List-ind-instance {ys} = List-ind (λ as → List (as ++ ys))

postulate ++-List : ∀ {xs ys} → List xs → List ys → List (xs ++ ys)
{-# ATP prove ++-List ++-List-ind-instance #-}

map-List-ind-instance :
  ∀ {f} →
  List (map f []) →
  (∀ x {xs} → List (map f xs) → List (map f (x ∷ xs))) →
  ∀ {xs} → List xs → List (map f xs)
map-List-ind-instance {f} = List-ind (λ as → List (map f as))

postulate map-List : ∀ f {xs} → List xs → List (map f xs)
{-# ATP prove map-List map-List-ind-instance #-}

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

++-assoc-ind-instance :
  ∀ {ys zs} →
  ([] ++ ys) ++ zs ≡ [] ++ ys ++ zs →
  (∀ x {xs} →
    (xs ++ ys) ++ zs ≡ xs ++ ys ++ zs →
    ((x ∷ xs) ++ ys) ++ zs ≡ (x ∷ xs) ++ ys ++ zs) →
  ∀ {xs} → List xs → (xs ++ ys) ++ zs ≡ xs ++ ys ++ zs
++-assoc-ind-instance {ys} {zs} =
  List-ind (λ as → (as ++ ys) ++ zs ≡ as ++ ys ++ zs )

postulate
  ++-assoc : ∀ {xs} → List xs → ∀ ys zs → (xs ++ ys) ++ zs ≡ xs ++ ys ++ zs
{-# ATP prove ++-assoc ++-assoc-ind-instance #-}

map-++-ind-instance :
  ∀ {f} {ys} →
  map f ([] ++ ys) ≡ map f [] ++ map f ys →
  (∀ x {xs} → map f (xs ++ ys) ≡ map f xs ++ map f ys →
    map f ((x ∷ xs) ++ ys) ≡ map f (x ∷ xs) ++ map f ys) →
  ∀ {xs} → List xs → map f (xs ++ ys) ≡ map f xs ++ map f ys
map-++-ind-instance {f} {ys} =
  List-ind (λ as → map f (as ++ ys) ≡ map f as ++ map f ys)

postulate
  map-++ : ∀ f {xs} → List xs → ∀ ys →
           map f (xs ++ ys) ≡ map f xs ++ map f ys
{-# ATP prove map-++ map-++-ind-instance #-}


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

module Cardinality.Finite.ManifestBishop.Isomorphism where

open import Prelude
open import Data.Fin
open import Data.Fin.Properties

open import Container.List.Isomorphism

import Cardinality.Finite.ManifestBishop.Inductive as 𝕃
import Cardinality.Finite.ManifestBishop.Container as ℒ

open import Cardinality.Finite.SplitEnumerable.Isomorphism

open import Data.Nat.Properties

∈!ℒ⇒∈!𝕃 : ∀ (x : A) l (xs : Fin l → A) → x ℒ.∈! (l , xs) → x 𝕃.∈! ℒ→𝕃 (l , xs)
∈!ℒ⇒∈!𝕃 x (suc l) xs ((f0   , p) , u) = (f0 , p) , lemma
  where
  lemma : (y : x 𝕃.∈ ℒ→𝕃 (suc l , xs)) → (f0 , p) ≡ y
  lemma (f0   , q) = cong (∈ℒ⇒∈𝕃 x (suc l , xs)) (u (f0 , q))
  lemma (fs m , q) =
    let o , r = subst (x ℒ.∈_) (ℒ→𝕃→ℒ l (xs ∘ fs)) (m , q)
    in ⊥-elim (znots (cong (FinToℕ ∘ fst) (u (fs o , r))))
∈!ℒ⇒∈!𝕃 x (suc l) xs ((fs n , p) , u) = 𝕃.push! xs0≢x (∈!ℒ⇒∈!𝕃 x l (xs ∘ fs) ((n , p) , uxss))
  where
  xs0≢x : xs f0 ≢ x
  xs0≢x xs0≡x = snotz (cong (FinToℕ ∘ fst) (u (f0 , xs0≡x)))

  uxss : (y : x ℒ.∈ (l , xs ∘ fs)) → (n , p) ≡ y
  uxss (m , q) = cong (λ { (f0 , q) → ⊥-elim (xs0≢x q) ; (fs m , q) → m , q}) (u (fs m , q))

𝕃⇔ℒ⟨ℬ⟩ : 𝕃.ℬ A ⇔ ℒ.ℬ A
𝕃⇔ℒ⟨ℬ⟩ .fun (sup , cov) = 𝕃→ℒ sup , cov
𝕃⇔ℒ⟨ℬ⟩ .inv ((l , xs) , cov) = ℒ→𝕃 (l , xs) , λ x → ∈!ℒ⇒∈!𝕃 x l xs (cov x)
𝕃⇔ℒ⟨ℬ⟩ .rightInv (sup , cov) i .fst = 𝕃⇔ℒ .rightInv sup i
𝕃⇔ℒ⟨ℬ⟩ .rightInv ((l , xs) , cov) i .snd x =
  J
    (λ ys ys≡ → (zs : x ℒ.∈! ys) → PathP (λ i → x ℒ.∈! sym ys≡ i) zs (cov x))
    (λ _ → isPropIsContr _ _)
    (sym (ℒ→𝕃→ℒ l xs))
    (∈!ℒ⇒∈!𝕃 x l xs (cov x))
    i
𝕃⇔ℒ⟨ℬ⟩ .leftInv  (sup , cov) i .fst = 𝕃⇔ℒ .leftInv sup i
𝕃⇔ℒ⟨ℬ⟩ .leftInv  (sup , cov) i .snd x =
  J
    (λ ys ys≡ → (zs : x 𝕃.∈! ys) → PathP (λ i → x 𝕃.∈! sym ys≡ i) zs (cov x))
    (λ zs → isPropIsContr _ _)
    (sym (𝕃→ℒ→𝕃 sup))
    (∈!ℒ⇒∈!𝕃 x (𝕃.length sup) (sup 𝕃.!_) (cov x))
    i


module Data.List.Relation.Pairwise.Proofs where

open import Data.List
import      Data.List.Functions as List
open import Data.List.Relation.Pairwise
open import Logic
import      Lvl
open import Type

private variable ℓ ℓₗ : Lvl.Level
private variable T A B : Type{ℓ}
private variable x : T
private variable f : A → B
private variable _▫_ : T → T → Type{ℓₗ}
private variable _▫₁_ : A → A → Type{ℓₗ}
private variable _▫₂_ : B → B → Type{ℓₗ}

AdjacentlyPairwise-map : (∀{x y} → (x ▫₁ y) → (f(x) ▫₂ f(y))) → ∀{l} → AdjacentlyPairwise(_▫₁_)(l) → AdjacentlyPairwise(_▫₂_)(List.map f(l))
AdjacentlyPairwise-map p {∅} s = AdjacentlyPairwise.empty
AdjacentlyPairwise-map p {x ⊰ ∅} s = AdjacentlyPairwise.single
AdjacentlyPairwise-map p {x ⊰ y ⊰ l} (AdjacentlyPairwise.step ⦃ s₁ ⦄ ⦃ s₂ ⦄) = AdjacentlyPairwise.step ⦃ p s₁ ⦄ ⦃ AdjacentlyPairwise-map p {y ⊰ l} s₂ ⦄

AdjacentlyPairwise-prepend : (∀{y} → (x ▫ y)) → ∀{l} → AdjacentlyPairwise(_▫_)(l) → AdjacentlyPairwise(_▫_)(x ⊰ l)
AdjacentlyPairwise-prepend xy {∅}     p = AdjacentlyPairwise.single
AdjacentlyPairwise-prepend xy {_ ⊰ _} p = AdjacentlyPairwise.step ⦃ xy ⦄ ⦃ p ⦄


module STLC.Type.Relation where

open import Data.Nat using (ℕ)
open import Data.Fin using (Fin)

open import STLC.Term
open import STLC.Type

open import STLC.Type.Context using (Ctxt)

open import Data.Vec using (_∷_; lookup)

open import Relation.Nullary using (¬_)

open import Relation.Binary.PropositionalEquality as Eq
  using (refl; _≡_; sym; cong)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; step-≡; _∎)

infix 4 _⊢_⦂_

data _⊢_⦂_ { m n } (Γ : Ctxt m n) : Term m -> Type n -> Set where

  ⊢# : ∀ { x : Fin m }
    -- ---------------
    -> Γ ⊢ # x ⦂ lookup Γ x

  ⊢ƛ : ∀  { t τ₁ τ₂ }
    -> τ₁ ∷ Γ ⊢ t ⦂ τ₂
    -- -----------------
    -> Γ ⊢ ƛ t ⦂ τ₁ ⇒ τ₂

  ⊢_·_ : ∀ { t₁ t₂ τ₁ τ₂ }
    -> Γ ⊢ t₁ ⦂ τ₁ ⇒ τ₂
    -> Γ ⊢ t₂ ⦂ τ₁
    -- ------------------
    -> Γ ⊢ t₁ · t₂ ⦂ τ₂

_⊬_⦂_ : ∀ { m n } -> Ctxt m n -> Term m -> Type n -> Set
Γ ⊬ t ⦂ τ = ¬ (Γ ⊢ t ⦂ τ)

module Lemmas₀ where

  ⊢-subst : ∀ { m n } { Γ₁ Γ₂ : Ctxt m n } { t₁ t₂ : Term m } { τ₁ τ₂ : Type n }
    -> Γ₁ ≡ Γ₂ -> t₁ ≡ t₂ -> τ₁ ≡ τ₂ -> Γ₁ ⊢ t₁ ⦂ τ₁ -> Γ₂ ⊢ t₂ ⦂ τ₂
  ⊢-subst refl refl refl hyp = hyp

  ⊢-Γ-subst : ∀ { m n } { Γ₁ Γ₂ : Ctxt m n } { t : Term m } { τ : Type n }
    -> Γ₁ ≡ Γ₂ -> Γ₁ ⊢ t ⦂ τ -> Γ₂ ⊢ t ⦂ τ
  ⊢-Γ-subst ≡-Γ hyp = ⊢-subst ≡-Γ refl refl hyp

  ⊢-τ-subst : ∀ { m n } { Γ : Ctxt m n } { t : Term m } { τ₁ τ₂ : Type n }
    -> τ₁ ≡ τ₂ -> Γ ⊢ t ⦂ τ₁ -> Γ ⊢ t ⦂ τ₂
  ⊢-τ-subst ≡-τ hyp = ⊢-subst refl refl ≡-τ hyp

-- TODO: Substitutions on typing derivations
-- TODO: Equivalent for Value e.g. value ⦂ τ relation w/ some lemmas :)


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Data.Nat.Coprime where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function

open import Cubical.Data.Sigma
open import Cubical.Data.NatPlusOne

open import Cubical.HITs.PropositionalTruncation as PropTrunc

open import Cubical.Data.Nat.Base
open import Cubical.Data.Nat.Properties
open import Cubical.Data.Nat.Divisibility
open import Cubical.Data.Nat.GCD

areCoprime : ℕ × ℕ → Type₀
areCoprime (m , n) = isGCD m n 1

-- Any pair (m , n) can be converted to a coprime pair (m' , n') s.t.
--  m' ∣ m, n' ∣ n if and only if one of m or n is nonzero

module ToCoprime ((m , n) : ℕ × ℕ₊₁) where
  d   = gcd m (ℕ₊₁→ℕ n)
  d∣m = gcdIsGCD m (ℕ₊₁→ℕ n) .fst .fst
  d∣n = gcdIsGCD m (ℕ₊₁→ℕ n) .fst .snd
  gr  = gcdIsGCD m (ℕ₊₁→ℕ n) .snd

  c₁ : ℕ
  p₁ : c₁ · d ≡ m
  c₁ = ∣-untrunc d∣m .fst; p₁ = ∣-untrunc d∣m .snd

  c₂ : ℕ₊₁
  p₂ : (ℕ₊₁→ℕ c₂) · d ≡ (ℕ₊₁→ℕ n)
  c₂ = 1+ (∣s-untrunc d∣n .fst); p₂ = ∣s-untrunc d∣n .snd

  toCoprime : ℕ × ℕ₊₁
  toCoprime = (c₁ , c₂)

  private
    lem : ∀ a {b c d e} → a · b ≡ c → c · d ≡ e → a · (b · d) ≡ e
    lem a p q = ·-assoc a _ _ ∙ cong (_· _) p ∙ q

    gr' : ∀ d' → prediv d' c₁ → prediv d' (ℕ₊₁→ℕ c₂) → (d' · d) ∣ d
    gr' d' (b₁ , q₁) (b₂ , q₂) = gr (d' · d) ((∣ b₁ , lem b₁ q₁ p₁ ∣) ,
                                              (∣ b₂ , lem b₂ q₂ p₂ ∣))

  d-1 = m∣sn→z<m d∣n .fst
  q : d ≡ suc d-1
  q = sym (+-comm 1 d-1 ∙ m∣sn→z<m d∣n .snd)

  private
    -- this only works because d > 0 (<=> m > 0 or n > 0)
    d-cancelʳ : ∀ d' → (d' · d) ∣ d → d' ∣ 1
    d-cancelʳ d' div = ∣-cancelʳ d-1 (∣-trans (subst (λ x → (d' · x) ∣ x) q div)
                                              (∣-refl (sym (·-identityˡ _))))

  toCoprimeAreCoprime : areCoprime (c₁ , ℕ₊₁→ℕ c₂)
  fst toCoprimeAreCoprime = ∣-oneˡ c₁ , ∣-oneˡ (ℕ₊₁→ℕ c₂)
  snd toCoprimeAreCoprime d' (d'∣c₁ , d'∣c₂) = PropTrunc.rec isProp∣ (λ a →
                                               PropTrunc.rec isProp∣ (λ b →
                                                d-cancelʳ d' (gr' d' a b)) d'∣c₂) d'∣c₁

  toCoprime∣ : (c₁ ∣ m) × (ℕ₊₁→ℕ c₂ ∣ ℕ₊₁→ℕ n)
  toCoprime∣ = ∣ d , ·-comm d c₁ ∙ p₁ ∣ , ∣ d , ·-comm d (ℕ₊₁→ℕ c₂) ∙ p₂ ∣

  toCoprime-idem : areCoprime (m , ℕ₊₁→ℕ n) → (c₁ , c₂) ≡ (m , n)
  toCoprime-idem cp i = q₁ i , ℕ₊₁→ℕ-inj q₂ i
    where q₁ = sym (·-identityʳ c₁) ∙ cong (c₁ ·_) (sym (isGCD→gcd≡ cp)) ∙ p₁
          q₂ = sym (·-identityʳ (ℕ₊₁→ℕ c₂)) ∙ cong (ℕ₊₁→ℕ c₂ ·_) (sym (isGCD→gcd≡ cp)) ∙ p₂

open ToCoprime using (toCoprime; toCoprimeAreCoprime; toCoprime∣; toCoprime-idem) public

toCoprime-cancelʳ : ∀ ((m , n) : ℕ × ℕ₊₁) k
                    → toCoprime (m · ℕ₊₁→ℕ k , n ·₊₁ k) ≡ toCoprime (m , n)
toCoprime-cancelʳ (m , n) (1+ k) i =
  inj-·sm {c₁'} {d-1} {c₁} r₁ i , ℕ₊₁→ℕ-inj (inj-·sm {ℕ₊₁→ℕ c₂'} {d-1} {ℕ₊₁→ℕ c₂} r₂) i
  where open ToCoprime (m , n)
        open ToCoprime (m · suc k , n ·₊₁ (1+ k)) using ()
          renaming (c₁ to c₁'; p₁ to p₁'; c₂ to c₂'; p₂ to p₂')

        q₁ : c₁' · d · suc k ≡ m · suc k
        q₁ =   sym (·-assoc c₁' (ToCoprime.d (m , n)) (suc k))
             ∙ cong (c₁' ·_) (sym (gcd-factorʳ m (ℕ₊₁→ℕ n) (suc k)))
             ∙ p₁'
        q₂ : ℕ₊₁→ℕ c₂' · (ToCoprime.d (m , n)) · suc k ≡ ℕ₊₁→ℕ n · suc k
        q₂ =   sym (·-assoc (ℕ₊₁→ℕ c₂') (ToCoprime.d (m , n)) (suc k))
             ∙ cong (ℕ₊₁→ℕ c₂' ·_) (sym (gcd-factorʳ m (ℕ₊₁→ℕ n) (suc k)))
             ∙ p₂'

        r₁ : c₁' · suc d-1 ≡ c₁ · suc d-1
        r₁ = subst (λ z → c₁' · z ≡ c₁ · z) q (inj-·sm q₁ ∙ sym p₁)
        r₂ : ℕ₊₁→ℕ c₂' · suc d-1 ≡ ℕ₊₁→ℕ c₂ · suc d-1
        r₂ = subst (λ z → ℕ₊₁→ℕ c₂' · z ≡ ℕ₊₁→ℕ c₂ · z) q (inj-·sm q₂ ∙ sym p₂)


module Datoid where

  import Equiv
  import Prelude

  open Equiv
  open Prelude

  data Datoid : Set1 where
    datoid : (a : Set) -> DecidableEquiv a -> Datoid

  El : Datoid -> Set
  El (datoid a _) = a

  datoidEq : (a : Datoid) -> DecidableEquiv (El a)
  datoidEq (datoid _ eq) = eq

  datoidRel : (a : Datoid) -> El a -> El a -> Set
  datoidRel d = rel' (datoidEq d)

  datoidDecRel :  (a : Datoid) -> (x y : El a)
               -> Either (datoidRel a x y) (Not (datoidRel a x y))
  datoidDecRel d = decRel (datoidEq d)

  dRefl : (a : Datoid) -> {x : El a} -> datoidRel a x x
  dRefl a = refl (datoidEq a)

  dSym : (a : Datoid) -> {x y : El a}
      -> datoidRel a x y -> datoidRel a y x
  dSym a = sym (datoidEq a)

  dTrans : (a : Datoid) -> {x y z : El a}
      -> datoidRel a x y -> datoidRel a y z -> datoidRel a x z
  dTrans a = trans (datoidEq a)

  data Respects (a : Datoid) (P : El a -> Set) : Set where
    respects : ((x y : El a) -> datoidRel a x y -> P x -> P y) -> Respects a P

  subst :  {a : Datoid} -> {P : El a -> Set} -> Respects a P
        -> (x y : El a) -> datoidRel a x y -> P x -> P y
  subst (respects f) = f

  pairDatoid : (a b : Datoid) -> Datoid
  pairDatoid a b = datoid (Pair (El a) (El b))
                          (pairEquiv (datoidEq a) (datoidEq b))


------------------------------------------------------------------------------
-- First-order logic theorems
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

-- This module contains some examples showing the use of the ATPs for
-- proving first-order logic theorems.

module FOL.TheoremsATP where

open import FOL.Base

------------------------------------------------------------------------------
-- We postulate some formulae and propositional functions.
postulate
  A     : Set
  A¹ B¹ : D → Set
  A²    : D → D → Set

-- The introduction and elimination rules for the quantifiers are theorems.
{-
      φ(x)
  -----------  ∀-intro
    ∀x.φ(x)

    ∀x.φ(x)
  -----------  ∀-elim
     φ(t)

      φ(t)
  -----------  ∃-intro
    ∃x.φ(x)

   ∃x.φ(x)   φ(x) → ψ
 ----------------------  ∃-elim
           ψ
-}

postulate
  ∀-intro : ((x : D) → A¹ x) → ∀ x → A¹ x
  ∀-intro' : ((x : D) → A¹ x) → ⋀ A¹
  ∀-elim  : (t : D) → (∀ x → A¹ x) → A¹ t
  ∀-elim' : (t : D) → ⋀ A¹ → A¹ t
  ∃-intro : (t : D) → A¹ t → ∃ A¹
  ∃-elim  : ∃ A¹ → ((x : D) → A¹ x → A) → A
{-# ATP prove ∀-intro #-}
{-# ATP prove ∀-intro' #-}
{-# ATP prove ∀-elim #-}
{-# ATP prove ∀-elim' #-}
{-# ATP prove ∃-intro #-}
{-# ATP prove ∃-elim #-}

-- Generalization of De Morgan's laws.
postulate
  gDM₁ : ¬ (∀ x → A¹ x) ↔ (∃[ x ] ¬ (A¹ x))
  gDM₂ : ¬ (∃ A¹)       ↔ (∀ x → ¬ (A¹ x))
  gDM₃ : (∀ x → A¹ x)   ↔ ¬ (∃[ x ] ¬ (A¹ x))
  gDM₄ : ∃ A¹           ↔ ¬ (∀ x → ¬ (A¹ x))
{-# ATP prove gDM₁ #-}
{-# ATP prove gDM₂ #-}
{-# ATP prove gDM₃ #-}
{-# ATP prove gDM₄ #-}

-- The order of quantifiers of the same sort is irrelevant.
postulate
  ∀-ord : (∀ x y → A² x y) ↔ (∀ y x → A² x y)
  ∃-ord : (∃[ x ] ∃[ y ] A² x y) ↔ (∃[ y ] ∃[ x ] A² x y)
{-# ATP prove ∀-ord #-}
{-# ATP prove ∃-ord #-}

-- Quantification over a variable that does not occur can be erased or
-- added.
postulate ∃-erase-add : (∃[ x ] A ∧ A¹ x) ↔ A ∧ (∃[ x ] A¹ x)
{-# ATP prove ∃-erase-add #-}

-- Distributes laws for the quantifiers.
postulate
  ∀-dist : (∀ x → A¹ x ∧ B¹ x) ↔ ((∀ x → A¹ x) ∧ (∀ x → B¹ x))
  ∃-dist : (∃[ x ] (A¹ x ∨ B¹ x)) ↔ (∃ A¹ ∨ ∃ B¹)
{-# ATP prove ∀-dist #-}
{-# ATP prove ∃-dist #-}

-- Interchange of quantifiers.
-- The related theorem ∀x∃y.Axy → ∃y∀x.Axy is not (classically) valid.
postulate ∃∀ : ∃[ x ] (∀ y → A² x y) → ∀ y → ∃[ x ] A² x y
{-# ATP prove ∃∀ #-}

-- ∃ in terms of ∀ and ¬.
postulate
  ∃→¬∀¬ : ∃[ x ] A¹ x → ¬ (∀ {x} → ¬ A¹ x)
  ∃¬→¬∀ : ∃[ x ] ¬ A¹ x → ¬ (∀ {x} → A¹ x)
{-# ATP prove ∃→¬∀¬ #-}
{-# ATP prove ∃¬→¬∀ #-}

-- ∀ in terms of ∃ and ¬.
postulate
  ∀→¬∃¬ : (∀ {x} → A¹ x) → ¬ (∃[ x ] ¬ A¹ x)
  ∀¬→¬∃ : (∀ {x} → ¬ A¹ x) → ¬ (∃[ x ] A¹ x)
{-# ATP prove ∀→¬∃¬ #-}
{-# ATP prove ∀¬→¬∃ #-}

-- Distribution of ∃ and ∨.
postulate ∃∨ : ∃[ x ](A¹ x ∨ B¹ x) → (∃[ x ] A¹ x) ∨ (∃[ x ] B¹ x)
{-# ATP prove ∃∨ #-}


{-# OPTIONS --cubical --no-import-sorts  #-}

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Function.Base using (_∋_; _$_)

open import MorePropAlgebra.Bundles
import Cubical.Structures.CommRing as Std

module MorePropAlgebra.Properties.CommRing {ℓ} (assumptions : CommRing {ℓ}) where
open CommRing assumptions renaming (Carrier to R)

import MorePropAlgebra.Properties.Ring
module Ring'Properties = MorePropAlgebra.Properties.Ring   (record { CommRing assumptions })
module Ring'           =                            Ring   (record { CommRing assumptions })
(      Ring')          =                            Ring ∋ (record { CommRing assumptions })

stdIsCommRing : Std.IsCommRing 0r 1r _+_ _·_ (-_)
stdIsCommRing .Std.IsCommRing.isRing = Ring'Properties.stdIsRing
stdIsCommRing .Std.IsCommRing.·-comm = ·-comm

stdCommRing : Std.CommRing {ℓ}
stdCommRing = record { CommRing assumptions ; isCommRing = stdIsCommRing }
--
-- module RingTheory' = Std.Theory stdRing


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

open import LogicalFormulae
open import Categories.Definition
open import Categories.Functor.Definition

module Categories.Functor.Lemmas where

functorCompose : {a b c d e f : _} {B : Category {a} {b}} {C : Category {c} {d}} {D : Category {e} {f}} → (Functor C D) → (Functor B C) → (Functor B D)
functorCompose G F = record { onObj = λ x → Functor.onObj G (Functor.onObj F x) ; onArrow = λ f → Functor.onArrow G (Functor.onArrow F f) ; mapId = λ {T} → mapIdHelp G F T ; mapCompose = λ r s → mapComposeHelp G F r s }
  where
    mapIdHelp : {a b c d e f : _} {B : Category {a} {b}} {C : Category {c} {d}} {D : Category {e} {f}} → (G : Functor C D) → (F : Functor B C) → (T : Category.objects B) → Functor.onArrow G (Functor.onArrow F (Category.id B T)) ≡ Category.id D (Functor.onObj G (Functor.onObj F T))
    mapIdHelp {B = B} {C} {D} record { onObj = onObjG ; onArrow = onArrowG ; mapId = mapIdG ; mapCompose = mapComposeG } record { onObj = onObj ; onArrow = onArrow ; mapId = mapId ; mapCompose = mapCompose } T rewrite mapId {T} = mapIdG {onObj T}
    mapComposeHelp : {a b c d e f : _} {B : Category {a} {b}} {C : Category {c} {d}} {D : Category {e} {f}} → (G : Functor C D) → (F : Functor B C) → {S T U : Category.objects B} → (r : Category.arrows B S T) → (s : Category.arrows B T U) → (Functor.onArrow G (Functor.onArrow F (Category._∘_ B s r))) ≡ (Category._∘_ D (Functor.onArrow G (Functor.onArrow F s)) (Functor.onArrow G (Functor.onArrow F r)))
    mapComposeHelp {B = record { objects = objectsB ; arrows = arrowsB ; id = idB ; _∘_ = _∘B_ ; rightId = rightIdB ; leftId = leftIdB ; compositionAssociative = associativeB }} {record { objects = objectsC ; arrows = arrowsC ; id = idC ; _∘_ = _∘C_ ; rightId = rightIdC ; leftId = leftIdC ; compositionAssociative = associativeC }} {record { objects = objectsD ; arrows = arrowsD ; id = idD ; _∘_ = _∘D_ ; rightId = rightIdD ; leftId = leftIdD ; compositionAssociative = associativeD }} record { onObj = onObjG ; onArrow = onArrowG ; mapId = mapIdG ; mapCompose = mapComposeG } record { onObj = onObjF ; onArrow = onArrowF ; mapId = mapIdF ; mapCompose = mapComposeF } {S} {T} {U} r s rewrite mapComposeF r s | mapComposeG (onArrowF r) (onArrowF s) = refl

idFunctor : {a b : _} (C : Category {a} {b}) → Functor C C
Functor.onObj (idFunctor C) = λ x → x
Functor.onArrow (idFunctor C) = λ f → f
Functor.mapId (idFunctor C) = refl
Functor.mapCompose (idFunctor C) = λ f g → refl


open import Algebra using (CommutativeRing)

module Algebra.Module.Vec.Recursive {r ℓ} {CR : CommutativeRing r ℓ} where

open CommutativeRing CR

open import Algebra.Module using (Module)
open import Data.Vec.Recursive
open import Data.Product using (_×_; _,_)
open import Data.Product.Relation.Binary.Pointwise.NonDependent
open import Relation.Binary using (Rel)
open import Data.Nat using (zero; suc; ℕ)
open import Data.Unit.Polymorphic using (⊤)
open import Assume using (assume)

_^ᴹ_ : ∀ {m ℓm} → Module CR m ℓm → ℕ → Module CR m ℓm
M ^ᴹ n =
  record
  { Carrierᴹ = Carrierᴹ ^ n
  ; _≈ᴹ_ = pointwise _≈ᴹ_
  ; _+ᴹ_ = zipWith _+ᴹ_ n
  ; _*ₗ_ = λ s → map (_*ₗ_ s) n
  ; _*ᵣ_ = λ v s → map (λ x → x *ᵣ s) n v
  ; 0ᴹ = replicate n 0ᴹ
  ; -ᴹ_ = map (-ᴹ_) n
  ; isModule = assume
  }
  where
    open Module M
    open import Data.Unit.Polymorphic

    pointwise : ∀ {a ℓ n} {A : Set a} (~ : Rel A ℓ) → Rel (A ^ n) ℓ
    pointwise {n = ℕ.zero} ~ .tt .tt = ⊤
    pointwise {n = suc ℕ.zero} ~ x y = ~ x y
    pointwise {n = 2+ n} ~ (x , xs) (y , ys) = ~ x y × pointwise ~ xs ys


------------------------------------------------------------------------
-- Properties related to Fin, and operations making use of these
-- properties (or other properties not available in Data.Fin)
------------------------------------------------------------------------

module Data.Fin.Props where

open import Data.Fin
open import Data.Nat as N
  using (ℕ; zero; suc; s≤s; z≤n)
  renaming (_≤_ to _ℕ≤_; _<_ to _ℕ<_; _+_ to _ℕ+_)
open N.≤-Reasoning
import Data.Nat.Properties as N
open import Data.Function
open import Relation.Nullary
open import Relation.Unary
open import Relation.Binary
open import Relation.Binary.FunctionSetoid
open import Relation.Binary.PropositionalEquality as PropEq
  using (_≡_; refl; cong; subst)
open import Category.Functor
open import Category.Applicative

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

private
  drop-suc : ∀ {o} {m n : Fin o} →
             suc m ≡ (Fin (suc o) ∶ suc n) → m ≡ n
  drop-suc refl = refl

preorder : ℕ → Preorder
preorder n = PropEq.preorder (Fin n)

setoid : ℕ → Setoid
setoid n = PropEq.setoid (Fin n)

strictTotalOrder : ℕ → StrictTotalOrder
strictTotalOrder n = record
  { carrier            = Fin n
  ; _≈_                = _≡_
  ; _<_                = _<_
  ; isStrictTotalOrder = record
    { isEquivalence = PropEq.isEquivalence
    ; trans         = N.<-trans
    ; compare       = cmp
    ; <-resp-≈      = PropEq.resp₂ _<_
    }
  }
  where
  cmp : ∀ {n} → Trichotomous _≡_ (_<_ {n})
  cmp zero    zero    = tri≈ (λ())     refl  (λ())
  cmp zero    (suc j) = tri< (s≤s z≤n) (λ()) (λ())
  cmp (suc i) zero    = tri> (λ())     (λ()) (s≤s z≤n)
  cmp (suc i) (suc j) with cmp i j
  ... | tri<  lt ¬eq ¬gt = tri< (s≤s lt)         (¬eq ∘ drop-suc) (¬gt ∘ N.≤-pred)
  ... | tri> ¬lt ¬eq  gt = tri> (¬lt ∘ N.≤-pred) (¬eq ∘ drop-suc) (s≤s gt)
  ... | tri≈ ¬lt  eq ¬gt = tri≈ (¬lt ∘ N.≤-pred) (cong suc eq)    (¬gt ∘ N.≤-pred)

decSetoid : ℕ → DecSetoid
decSetoid n = StrictTotalOrder.decSetoid (strictTotalOrder n)

infix 4 _≟_

_≟_ : {n : ℕ} → Decidable {Fin n} _≡_
_≟_ {n} = DecSetoid._≟_ (decSetoid n)

to-from : ∀ n → toℕ (fromℕ n) ≡ n
to-from zero    = refl
to-from (suc n) = cong suc (to-from n)

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

prop-toℕ-≤ : ∀ {n} (x : Fin n) → toℕ x ℕ≤ N.pred n
prop-toℕ-≤ zero                 = z≤n
prop-toℕ-≤ (suc {n = zero}  ())
prop-toℕ-≤ (suc {n = suc n} i)  = s≤s (prop-toℕ-≤ i)

nℕ-ℕi≤n : ∀ n i → n ℕ-ℕ i ℕ≤ n
nℕ-ℕi≤n n       zero     = begin n ∎
nℕ-ℕi≤n zero    (suc ())
nℕ-ℕi≤n (suc n) (suc i)  = begin
  n ℕ-ℕ i ≤⟨ nℕ-ℕi≤n n i ⟩
  n       ≤⟨ N.n≤1+n n ⟩
  suc n   ∎

inject-lemma : ∀ {n} {i : Fin n} (j : Fin′ i) →
               toℕ (inject j) ≡ toℕ j
inject-lemma {i = zero}  ()
inject-lemma {i = suc i} zero    = refl
inject-lemma {i = suc i} (suc j) = cong suc (inject-lemma j)

inject+-lemma : ∀ m k → m ≡ toℕ (inject+ k (fromℕ m))
inject+-lemma zero    k = refl
inject+-lemma (suc m) k = cong suc (inject+-lemma m k)

inject₁-lemma : ∀ {m} (i : Fin m) → toℕ (inject₁ i) ≡ toℕ i
inject₁-lemma zero    = refl
inject₁-lemma (suc i) = cong suc (inject₁-lemma i)

inject≤-lemma : ∀ {m n} (i : Fin m) (le : m ℕ≤ n) →
                toℕ (inject≤ i le) ≡ toℕ i
inject≤-lemma zero    (N.s≤s le) = refl
inject≤-lemma (suc i) (N.s≤s le) = cong suc (inject≤-lemma i le)

≺⇒<′ : _≺_ ⇒ N._<′_
≺⇒<′ (n ≻toℕ i) = N.≤⇒≤′ (bounded i)

<′⇒≺ : N._<′_ ⇒ _≺_
<′⇒≺ {n} N.≤′-refl    = subst (λ i → i ≺ suc n) (to-from n)
                              (suc n ≻toℕ fromℕ n)
<′⇒≺ (N.≤′-step m≤′n) with <′⇒≺ m≤′n
<′⇒≺ (N.≤′-step m≤′n) | n ≻toℕ i =
  subst (λ i → i ≺ suc n) (inject₁-lemma i) (suc n ≻toℕ (inject₁ i))

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

infixl 6 _+′_

_+′_ : ∀ {m n} (i : Fin m) (j : Fin n) → Fin (N.pred m ℕ+ n)
i +′ j = inject≤ (i + j) (N._+-mono_ (prop-toℕ-≤ i) ≤-refl)
  where open Poset N.poset renaming (refl to ≤-refl)

-- reverse {n} "i" = "n ∸ 1 ∸ i".

reverse : ∀ {n} → Fin n → Fin n
reverse {zero}  ()
reverse {suc n} i  = inject≤ (n ℕ- i) (N.n∸m≤n (toℕ i) (suc n))

-- If there is an injection from a set to a finite set, then equality
-- of the set can be decided.

eq? : ∀ {S n} →
      Injection S (PropEq.setoid (Fin n)) → Decidable (Setoid._≈_ S)
eq? inj x y with to ⟨$⟩ x ≟ to ⟨$⟩ y where open Injection inj
... | yes tox≡toy = yes (Injection.injective inj tox≡toy)
... | no  tox≢toy = no  (tox≢toy ∘ pres (Injection.to inj))

-- Quantification over finite sets commutes with applicative functors.

sequence : ∀ {F n} {P : Pred (Fin n)} → RawApplicative F →
           (∀ i → F (P i)) → F (∀ i → P i)
sequence {F} RA = helper _ _
  where
  open RawApplicative RA

  helper : ∀ n (P : Pred (Fin n)) → (∀ i → F (P i)) → F (∀ i → P i)
  helper zero    P ∀iPi = pure (λ())
  helper (suc n) P ∀iPi =
    combine <$> ∀iPi zero ⊛ helper n (λ n → P (suc n)) (∀iPi ∘ suc)
    where
    combine : P zero → (∀ i → P (suc i)) → ∀ i → P i
    combine z s zero    = z
    combine z s (suc i) = s i

private

  -- Included just to show that sequence above has an inverse (under
  -- an equivalence relation with two equivalence classes, one with
  -- all inhabited sets and the other with all uninhabited sets).

  sequence⁻¹ : ∀ {F A} {P : Pred A} → RawFunctor F →
               F (∀ i → P i) → ∀ i → F (P i)
  sequence⁻¹ RF F∀iPi i = (λ f → f i) <$> F∀iPi
    where open RawFunctor RF


 {-# OPTIONS --type-in-type #-}

module IDescTT where

--********************************************
-- Prelude
--********************************************

-- Some preliminary stuffs, to avoid relying on the stdlib

--****************
-- Sigma and friends
--****************

data Sigma (A : Set) (B : A -> Set) : Set where
  _,_ : (x : A) (y : B x) -> Sigma A B

_*_ : (A : Set)(B : Set) -> Set
A * B = Sigma A \_ -> B

fst : {A : Set}{B : A -> Set} -> Sigma A B -> A
fst (a , _) = a

snd : {A : Set}{B : A -> Set} (p : Sigma A B) -> B (fst p)
snd (a , b) = b

data Zero : Set where
data Unit  : Set where
  Void : Unit

--****************
-- Sum and friends
--****************

data _+_ (A : Set)(B : Set) : Set where
  l : A -> A + B
  r : B -> A + B

--****************
-- Equality
--****************

data _==_ {A : Set}(x : A) : A -> Set where
  refl : x == x

subst : forall {x y} -> x == y -> x -> y
subst refl x = x

cong : {A B : Set}(f : A -> B){x y : A} -> x == y -> f x == f y
cong f refl = refl

cong2 : {A B C : Set}(f : A -> B -> C){x y : A}{z t : B} ->
        x == y -> z == t -> f x z == f y t
cong2 f refl refl = refl

postulate
  reflFun : {A B : Set}(f : A -> B)(g : A -> B)-> ((a : A) -> f a == g a) -> f == g

--****************
-- Meta-language
--****************

-- Note that we could define Nat, Bool, and the related operations in
-- IDesc. But it is awful to code with them, in Agda.

data Nat : Set where
  ze : Nat
  su : Nat -> Nat

data Bool : Set where
  true : Bool
  false : Bool

plus : Nat -> Nat -> Nat
plus ze n' = n'
plus (su n) n' = su (plus n n')

le : Nat -> Nat -> Bool
le ze _ = true
le (su _) ze = false
le (su n) (su n') = le n n'

--********************************************
-- Desc code
--********************************************

data IDesc (I : Set) : Set where
  var : I -> IDesc I
  const : Set -> IDesc I
  prod : IDesc I -> IDesc I -> IDesc I
  sigma : (S : Set) -> (S -> IDesc I) -> IDesc I
  pi : (S : Set) -> (S -> IDesc I) -> IDesc I

--********************************************
-- Desc interpretation
--********************************************

[|_|] : {I : Set} -> IDesc I -> (I -> Set) -> Set
[| var i |] P = P i
[| const X |] P = X
[| prod D D' |] P = [| D |] P * [| D' |] P
[| sigma S T |] P = Sigma S (\s -> [| T s |] P)
[| pi S T |] P = (s : S) -> [| T s |] P

--********************************************
-- Fixpoint construction
--********************************************

data IMu {I : Set}(R : I -> IDesc I)(i : I) : Set where
  con : [| R i |] (\j -> IMu R j) -> IMu R i

--********************************************
-- Predicate: All
--********************************************

All : {I : Set}(D : IDesc I)(P : I -> Set) -> [| D |] P -> IDesc (Sigma I P)
All (var i)     P x        = var (i , x)
All (const X)   P x        = const Unit
All (prod D D') P (d , d') = prod (All D P d) (All D' P d')
All (sigma S T) P (a , b)  = All (T a) P b
All (pi S T)    P f        = pi S (\s -> All (T s) P (f s))

all : {I : Set}(D : IDesc I)(X : I -> Set)
      (R : Sigma I X -> Set)(P : (x : Sigma I X) -> R x) ->
      (xs : [| D |] X) -> [| All D X xs |] R
all (var i) X R P x = P (i , x)
all (const K) X R P k = Void
all (prod D D') X R P (x , y) = ( all D X R P x , all D' X R P y )
all (sigma S T) X R P (a , b) = all (T a) X R P b
all (pi S T) X R P f = \a -> all (T a) X R P (f a)

--********************************************
-- Elimination principle: induction
--********************************************

-- One would like to write the following:

{-
indI : {I : Set}
            (R : I -> IDesc I)
            (P : Sigma I (IMu R) -> Set)
            (m : (i : I)
                 (xs : [| R i |] (IMu R))
                 (hs : [| All (R i) (IMu R) xs |] P) ->
                 P ( i , con xs)) ->
            (i : I)(x : IMu R i) -> P ( i , x )
indI {I} R P m i (con xs) = m i xs (all (R i) (IMu R) P induct xs)
  where induct : (x : Sigma I (IMu R)) -> P x
        induct (i , xs) = indI R P m i xs
-}

-- But the termination-checker complains, so here we go
-- inductive-recursive:

module Elim {I : Set}
            (R : I -> IDesc I)
            (P : Sigma I (IMu R) -> Set)
            (m : (i : I)
                 (xs : [| R i |] (IMu R))
                 (hs : [| All (R i) (IMu R) xs |] P) ->
                 P ( i , con xs ))
       where

  mutual
    indI : (i : I)(x : IMu R i) -> P (i , x)
    indI i (con xs) = m i xs (hyps (R i) xs)

    hyps : (D : IDesc I) ->
           (xs : [| D |] (IMu R)) ->
           [| All D (IMu R) xs |] P
    hyps (var i) x = indI i x
    hyps (const X) x = Void
    hyps (prod D D') (d , d') =  hyps D d , hyps D' d'
    hyps (pi S R) f = \ s -> hyps (R s) (f s)
    hyps (sigma S R) ( a , b ) = hyps (R a) b

indI : {I : Set}
       (R : I -> IDesc I)
       (P : Sigma I (IMu R) -> Set)
       (m : (i : I)
            (xs : [| R i |] (IMu R))
            (hs : [| All (R i) (IMu R) xs |] P) ->
            P ( i , con xs)) ->
       (i : I)(x : IMu R i) -> P ( i , x )
indI = Elim.indI

--********************************************
-- Examples
--********************************************

--****************
-- Nat
--****************

data NatConst : Set where
  Ze : NatConst
  Su : NatConst

natCases : NatConst -> IDesc Unit
natCases Ze = const Unit
natCases Suc = var Void

NatD : Unit -> IDesc Unit
NatD Void = sigma NatConst  natCases

Natd : Unit -> Set
Natd x = IMu NatD x

zed : Natd Void
zed = con (Ze , Void)

sud : Natd Void -> Natd Void
sud n = con (Su , n)

--****************
-- Desc
--****************

data DescDConst : Set where
  lvar   : DescDConst
  lconst : DescDConst
  lprod  : DescDConst
  lpi    : DescDConst
  lsigma : DescDConst

descDChoice : Set -> DescDConst -> IDesc Unit
descDChoice I lvar = const I
descDChoice _ lconst = const Set
descDChoice _ lprod = prod (var Void) (var Void)
descDChoice _ lpi = sigma Set (\S -> pi S (\s -> var Void))
descDChoice _ lsigma = sigma Set (\S -> pi S (\s -> var Void))

descD : (I : Set) -> IDesc Unit
descD I = sigma DescDConst (descDChoice I)

IDescl0 : (I : Set) -> Unit -> Set
IDescl0 I = IMu (\_ -> descD I)

IDescl : (I : Set) -> Set
IDescl I = IDescl0 I _

varl : {I : Set}(i : I) -> IDescl I
varl i = con (lvar ,  i)

constl : {I : Set}(X : Set) -> IDescl I
constl X = con (lconst , X)

prodl : {I : Set}(D D' : IDescl I) -> IDescl I
prodl D D' = con (lprod , (D , D'))

pil : {I : Set}(S : Set)(T : S -> IDescl I) -> IDescl I
pil S T = con (lpi , ( S , T))

sigmal : {I : Set}(S : Set)(T : S -> IDescl I) -> IDescl I
sigmal S T = con (lsigma , ( S , T))

--****************
-- Vec (constraints)
--****************

data VecConst : Set where
  Vnil : VecConst
  Vcons : VecConst

vecDChoice : Set -> Nat -> VecConst -> IDesc Nat
vecDChoice X n Vnil = const (n == ze)
vecDChoice X n Vcons = sigma Nat (\m -> prod (var m) (const (n == su m)))

vecD : Set -> Nat -> IDesc Nat
vecD X n = sigma VecConst (vecDChoice X n)

vec : Set -> Nat -> Set
vec X n = IMu (vecD X) n

--****************
-- Vec (de-tagged, forced)
--****************

data VecConst2 : Nat -> Set where
  Vnil : VecConst2 ze
  Vcons : {n : Nat} -> VecConst2 (su n)

vecDChoice2 : Set -> (n : Nat) -> VecConst2 n -> IDesc Nat
vecDChoice2 X ze Vnil = const Unit
vecDChoice2 X (su n) Vcons = prod (const X) (var n)

vecD2 : Set -> Nat -> IDesc Nat
vecD2 X n = sigma (VecConst2 n) (vecDChoice2 X n)

vec2 : Set -> Nat -> Set
vec2 X n = IMu (vecD2 X) n

--****************
-- Fin (de-tagged)
--****************

data FinConst : Nat -> Set where
  Fz : {n : Nat} -> FinConst (su n)
  Fs : {n : Nat} -> FinConst (su n)

finDChoice : (n : Nat) -> FinConst n -> IDesc Nat
finDChoice ze ()
finDChoice (su n) Fz = const Unit
finDChoice (su n) Fs = var n

finD : Nat -> IDesc Nat
finD n = sigma (FinConst n) (finDChoice n)

fin : Nat -> Set
fin n = IMu finD n

--********************************************
-- Enumerations (hard-coded)
--********************************************

-- Unlike in Desc.agda, we don't carry the levitation of finite sets
-- here. We hard-code them and manipulate with standard Agda
-- machinery. Both presentation are isomorph but, in Agda, the coded
-- one quickly gets unusable.

data EnumU : Set where
  nilE : EnumU
  consE : EnumU -> EnumU

data EnumT : (e : EnumU) -> Set where
  EZe : {e : EnumU} -> EnumT (consE e)
  ESu : {e : EnumU} -> EnumT e -> EnumT (consE e)

spi : (e : EnumU)(P : EnumT e -> Set) -> Set
spi nilE P = Unit
spi (consE e) P = P EZe * spi e (\e -> P (ESu e))

switch : (e : EnumU)(P : EnumT e -> Set)(b : spi e P)(x : EnumT e) -> P x
switch nilE P b ()
switch (consE e) P b EZe = fst b
switch (consE e) P b (ESu n) = switch e (\e -> P (ESu e)) (snd b) n

_++_ : EnumU -> EnumU -> EnumU
nilE ++ e' = e'
(consE e) ++ e' = consE (e ++ e')

-- A special switch, for tagged descriptions. Switching on a
-- concatenation of finite sets:
sswitch : (e : EnumU)(e' : EnumU)(P : Set)
       (b : spi e (\_ -> P))(b' : spi e' (\_ -> P))(x : EnumT (e ++ e')) -> P
sswitch nilE nilE P b b' ()
sswitch nilE (consE e') P b b' EZe = fst b'
sswitch nilE (consE e') P b b' (ESu n) = sswitch nilE e' P b (snd b') n
sswitch (consE e) e' P b b' EZe = fst b
sswitch (consE e) e' P b b' (ESu n) = sswitch e e' P (snd b) b' n

--********************************************
-- Tagged indexed description
--********************************************

FixMenu : Set -> Set
FixMenu I = Sigma EnumU (\e -> (i : I) -> spi e (\_ -> IDesc I))

SensitiveMenu : Set -> Set
SensitiveMenu I = Sigma (I -> EnumU) (\F -> (i : I) -> spi (F i) (\_ -> IDesc I))

TagIDesc : Set -> Set
TagIDesc I = FixMenu I * SensitiveMenu I

toIDesc : (I : Set) -> TagIDesc I -> (I -> IDesc I)
toIDesc I ((E , ED) , (F , FD)) i = sigma (EnumT (E ++ F i))
                                          (\x -> sswitch E (F i) (IDesc I) (ED i) (FD i) x)

--********************************************
-- Catamorphism
--********************************************

cata : (I : Set)
       (R : I -> IDesc I)
       (T : I -> Set) ->
       ((i : I) -> [| R i |] T -> T i) ->
       (i : I) -> IMu R i -> T i
cata I R T phi i x = indI R (\it -> T (fst it)) (\i xs ms -> phi i (replace (R i) T xs ms)) i x
  where replace : (D : IDesc I)(T : I -> Set)
                  (xs : [| D |] (IMu R))
                  (ms : [| All D (IMu R) xs |] (\it -> T (fst it))) ->
                  [| D |] T
        replace (var i) T x y = y
        replace (const Z) T z z' = z
        replace (prod D D') T (x , x') (y , y') = replace D T x y , replace D' T x' y'
        replace (sigma A B) T (a , b) t = a , replace (B a) T b t
        replace (pi A B) T f t = \s -> replace (B s) T (f s) (t s)

--********************************************
-- Hutton's razor
--********************************************

--********************************
-- Types code
--********************************

data Type : Set where
  nat : Type
  bool : Type
  pair : Type -> Type -> Type

--********************************
-- Typed expressions
--********************************

Val : Type -> Set
Val nat = Nat
Val bool = Bool
Val (pair x y) = (Val x) * (Val y)

-- Fix menu:
exprFixMenu : FixMenu Type
exprFixMenu = ( consE (consE nilE) ,
                \ty -> (const (Val ty),                           -- Val t
                       (prod (var bool) (prod (var ty) (var ty)), -- if b then t1 else t2
                       Void)))

-- Indexed menu:
choiceMenu : Type -> EnumU
choiceMenu nat = consE nilE
choiceMenu bool = consE nilE
choiceMenu (pair x y) = nilE

choiceDessert : (ty : Type) -> spi (choiceMenu ty) (\ _ -> IDesc Type)
choiceDessert nat = (prod (var nat) (var nat) , Void)
choiceDessert bool = (prod (var nat) (var nat) , Void )
choiceDessert (pair x y) = Void

exprSensitiveMenu : SensitiveMenu Type
exprSensitiveMenu = ( choiceMenu ,  choiceDessert )

-- Expression:
expr : TagIDesc Type
expr = exprFixMenu , exprSensitiveMenu

exprIDesc : TagIDesc Type -> (Type -> IDesc Type)
exprIDesc D = toIDesc Type D

--********************************
-- Closed terms
--********************************

closeTerm : Type -> IDesc Type
closeTerm = exprIDesc expr

--********************************
-- Closed term evaluation
--********************************

eval : {ty : Type} -> IMu closeTerm ty -> Val ty
eval {ty} term = cata Type closeTerm Val evalOneStep ty term
        where evalOneStep : (ty : Type) -> [| closeTerm ty |] Val -> Val ty
              evalOneStep _ (EZe , t) = t
              evalOneStep _ ((ESu EZe) , (true , ( x , _))) = x
              evalOneStep _ ((ESu EZe) , (false , ( _ , y ))) = y
              evalOneStep nat ((ESu (ESu EZe)) , (x , y)) = plus x y
              evalOneStep nat ((ESu (ESu (ESu ()))) , t)
              evalOneStep bool ((ESu (ESu EZe)) , (x , y) ) =   le x y
              evalOneStep bool ((ESu (ESu (ESu ()))) , _)
              evalOneStep (pair x y) (ESu (ESu ()) , _)

--********************************************
-- Free monad construction
--********************************************

_**_ : {I : Set} (R : TagIDesc I)(X : I -> Set) -> TagIDesc I
((E , ED) , FFD) ** X = ((( consE E ,  \ i -> ( const (X i) , ED i ))) , FFD)

--********************************************
-- Substitution
--********************************************

apply : {I : Set}
        (R : TagIDesc I)(X Y : I -> Set) ->
        ((i : I) -> X i -> IMu (toIDesc I (R ** Y)) i) ->
        (i : I) ->
        [| toIDesc I (R ** X) i |] (IMu (toIDesc I (R ** Y))) ->
        IMu (toIDesc I (R ** Y)) i
apply (( E , ED) , (F , FD)) X Y sig i (EZe , x) = sig i x
apply (( E , ED) , (F , FD)) X Y sig i (ESu n , t) = con (ESu n , t)

substI : {I : Set} (X Y : I -> Set)(R : TagIDesc I)
         (sigma : (i : I) -> X i -> IMu (toIDesc I (R ** Y)) i)
         (i : I)(D : IMu (toIDesc I (R ** X)) i) ->
         IMu (toIDesc I (R ** Y)) i
substI {I} X Y R sig i term = cata I (toIDesc I (R ** X)) (IMu (toIDesc I (R ** Y))) (apply R X Y sig) i term

--********************************************
-- Hutton's razor is free monad
--********************************************

Empty : Type -> Set
Empty _ = Zero

closeTerm' : Type -> IDesc Type
closeTerm' = toIDesc Type (expr ** Empty)

update : {ty : Type} -> IMu closeTerm ty -> IMu closeTerm' ty
update {ty} tm = cata Type closeTerm (IMu closeTerm') (\ _ tagTm -> con (ESu (fst tagTm) , (snd tagTm))) ty tm

--********************************
-- Closed term' evaluation
--********************************

eval' : {ty : Type} -> IMu closeTerm' ty -> Val ty
eval' {ty} term = cata Type closeTerm' Val evalOneStep ty term
        where evalOneStep : (ty : Type) -> [| closeTerm' ty |] Val -> Val ty
              evalOneStep _ (EZe , ())
              evalOneStep _ (ESu EZe , t) = t
              evalOneStep _ ((ESu (ESu EZe)) , (true , ( x , _))) = x
              evalOneStep _ ((ESu (ESu EZe)) , (false , ( _ , y ))) = y
              evalOneStep nat ((ESu (ESu (ESu EZe))) , (x , y)) = plus x y
              evalOneStep nat (((ESu (ESu (ESu (ESu ()))))) , t)
              evalOneStep bool ((ESu (ESu (ESu EZe))) , (x , y) ) =   le x y
              evalOneStep bool ((ESu (ESu (ESu (ESu ())))) , _)
              evalOneStep (pair x y) (ESu (ESu (ESu ())) , _)

--********************************
-- Open terms
--********************************

-- A context is a snoc-list of types
-- put otherwise, a context is a type telescope
data Context : Set where
  [] : Context
  _,_ : Context -> Type -> Context

-- The environment realizes the context, having a value for each type
Env : Context -> Set
Env []      = Unit
Env (G , S)  = Env G * Val S

-- A typed-variable indexes into the context, obtaining a proof that
-- what we get is what you want (WWGIWYW)
Var : Context -> Type -> Set
Var []      T = Zero
Var (G , S) T = Var G T + (S == T)

-- The lookup gets into the context to extract the value
lookup : (G : Context) -> Env G -> (T : Type) -> Var G T -> Val T
lookup []        _       T ()
lookup (G , .T)  (g , t) T (r refl) = t
lookup (G , S)   (g , t) T (l x)    = lookup G g T x

-- Open term: holes are either values or variables in a context
openTerm : Context -> Type -> IDesc Type
openTerm c = toIDesc Type (expr ** (Var c))

--********************************
-- Evaluation of open terms
--********************************

-- |discharge| is the local substitution expected by |substI|. It is
-- just sugar around context lookup
discharge : (context : Context) ->
            Env context ->
            (ty : Type) ->
            Var context ty ->
            IMu closeTerm' ty
discharge ctxt env ty variable = con (ESu EZe , lookup ctxt env ty variable )

-- |substExpr| is the specialized |substI| to expressions. We get it
-- generically from the free monad construction.
substExpr : {ty : Type}
            (context : Context)
            (sigma : (ty : Type) ->
                     Var context ty ->
                     IMu closeTerm' ty) ->
            IMu (openTerm context) ty ->
            IMu closeTerm' ty
substExpr {ty} c sig term =
    substI (Var c) Empty expr sig ty term

-- By first doing substitution to close the term, we can use
-- evaluation of closed terms, obtaining evaluation of open terms
-- under a valid context.
evalOpen : {ty : Type}(context : Context) ->
           Env context ->
           IMu (openTerm context) ty ->
           Val ty
evalOpen ctxt env tm = eval' (substExpr ctxt (discharge ctxt env) tm)

--********************************
-- Tests
--********************************

-- Test context:
-- V 0 :-> true, V 1 :-> 2, V 2 :-> ( false , 1 )
testContext : Context
testContext = (([] , bool) , nat) , pair bool nat
testEnv : Env testContext
testEnv = ((Void , true ) , su (su ze)) , (false , su ze)

-- V 1
test1 : IMu (openTerm testContext) nat
test1 = con (EZe , ( l (r refl) ) )

testSubst1 : IMu closeTerm' nat
testSubst1 = substExpr testContext
                       (discharge testContext testEnv)
                       test1
-- = 2
testEval1 : Val nat
testEval1 = evalOpen testContext testEnv test1

-- add 1 (V 1)
test2 : IMu (openTerm testContext) nat
test2 = con (ESu (ESu (ESu EZe)) , (con (ESu EZe , su ze) , con ( EZe , l (r refl) )) )

testSubst2 : IMu closeTerm' nat
testSubst2 = substExpr testContext
                       (discharge testContext testEnv)
                        test2

-- = 3
testEval2 : Val nat
testEval2 = evalOpen testContext testEnv test2

-- if (V 0) then (V 1) else 0
test3 : IMu (openTerm testContext) nat
test3 = con (ESu (ESu EZe) , (con (EZe , l (l (r refl))) ,
                             (con (EZe , l (r refl)) ,
                              con (ESu EZe , ze))))

testSubst3 : IMu closeTerm' nat
testSubst3 = substExpr testContext
                       (discharge testContext testEnv)
                       test3

-- = 2
testEval3 : Val nat
testEval3 = evalOpen testContext testEnv test3

-- V 2
test4 : IMu (openTerm testContext) (pair bool nat)
test4 = con (EZe ,  r refl  )

testSubst4 : IMu closeTerm' (pair bool nat)
testSubst4 = substExpr testContext
                       (discharge testContext testEnv)
                       test4
-- = (false , 1)
testEval4 : Val (pair bool nat)
testEval4 = evalOpen testContext testEnv test4


------------------------------------------------------------------------
-- Up-to techniques for strong bisimilarity
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Labelled-transition-system

module Bisimilarity.Up-to {ℓ} (lts : LTS ℓ) where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude
open import Prelude.Size

open import Bisimilarity lts
import Bisimilarity.Equational-reasoning-instances
open import Equational-reasoning
open import Relation
import Up-to

open LTS lts

------------------------------------------------------------------------
-- The general up-to machinery, instantiated with the StepC container

open Up-to StepC public

------------------------------------------------------------------------
-- Some examples (based on techniques presented by Pous and Sangiorgi
-- in "Enhancements of the bisimulation proof method")

-- Up to bisimilarity.

Up-to-bisimilarity : Trans₂ ℓ Proc
Up-to-bisimilarity R = Bisimilarity ∞ ⊙ R ⊙ Bisimilarity ∞

-- Up to bisimilarity is monotone.

up-to-bisimilarity-monotone : Monotone Up-to-bisimilarity
up-to-bisimilarity-monotone R⊆S =
  Σ-map id (Σ-map id (Σ-map id (Σ-map R⊆S id)))

-- Up to bisimilarity is size-preserving.

up-to-bisimilarity-size-preserving : Size-preserving Up-to-bisimilarity
up-to-bisimilarity-size-preserving
  R⊆∼i {p₁ , p₄} (p₂ , p₁∼p₂ , p₃ , p₂Rp₃ , p₃∼p₄) =
    p₁  ∼⟨ p₁∼p₂ ⟩
    p₂  ∼⟨ R⊆∼i p₂Rp₃ ⟩
    p₃  ∼⟨ p₃∼p₄ ⟩■
    p₄

-- Up to union with bisimilarity.

Up-to-∪∼ : Trans₂ ℓ Proc
Up-to-∪∼ R = R ∪ Bisimilarity ∞

-- Up to union with bisimilarity is monotone.

up-to-∪∼-monotone : Monotone Up-to-∪∼
up-to-∪∼-monotone R⊆S = ⊎-map R⊆S id

-- Up to union with bisimilarity is size-preserving.
--
-- The proof is similar to parts of the proof of Corollary 6.3.15 from
-- Pous and Sangiorgi's "Enhancements of the bisimulation proof
-- method".

up-to-∪∼-size-preserving : Size-preserving Up-to-∪∼
up-to-∪∼-size-preserving =
  ∪-closure
    id-size-preserving
    (const-size-preserving (Bisimilarity ∞  ⊆⟨ id ⟩∎
                            Bisimilarity ∞  ∎))

-- Up to transitive closure.

Up-to-* : Trans₂ ℓ Proc
Up-to-* R = R *

-- Up to transitive closure is monotone.

up-to-*-monotone : Monotone Up-to-*
up-to-*-monotone R⊆S = Σ-map id (λ {n} → ^^-mono R⊆S n)
  where
  ^^-mono : ∀ {R S} → R ⊆ S →
            ∀ n → R ^^ n ⊆ S ^^ n
  ^^-mono R⊆S zero    = id
  ^^-mono R⊆S (suc n) = Σ-map id (Σ-map R⊆S (^^-mono R⊆S n))

-- Up to transitive closure is size-preserving.

up-to-*-size-preserving : Size-preserving Up-to-*
up-to-*-size-preserving =
  _⇔_.from (monotone→⇔ up-to-*-monotone) drop-*
  where
  drop-* : ∀ {i} → Bisimilarity i * ⊆ Bisimilarity i
  drop-* {x = p , .p} (zero  , refl)           = p ■
  drop-* {x = p , r}  (suc n , q , p∼q , ∼ⁿqr) =
    p  ∼⟨ p∼q ⟩
    q  ∼⟨ drop-* (n , ∼ⁿqr) ⟩■
    r


module Hwequivalence where

open import Relation.Binary.PropositionalEquality
open import Data.Bool

-- A 1-bit adder
bit-adder-spec : Bool → Bool → Bool → Bool
bit-adder-spec A B C = X ∧ Y
  where
  X : Bool
  X = (A ∧ not B ∧ not C)
      ∨
      (not A ∧ B ∧ not C)
      ∨
      (not A ∧ not B ∧ C)
      ∨
      (A ∧ B ∧ C)

  Y : Bool
  Y = (A ∧ B)
      ∨
      (A ∧ C)
      ∨
      (B ∧ C)

bit-adder-imp : Bool → Bool → Bool → Bool
bit-adder-imp A B C = X ∧ Y
  where
  u : Bool
  u = (A ∧ not B)
      ∨
      (not A ∧ B)
  v : Bool
  v = u ∧ C
  w : Bool
  w = A ∧ B
  X : Bool
  X = (u ∧ not C)
      ∨
      (not u ∧ C)
  Y : Bool
  Y = (w ∨ v)

data _≃_ {A : Set} (f : A → A) (g : A → A) : Set where
  f≃g : ((x y : A) → (x ≡ y) → (f x ≡ g y)) → f ≃ g

-- Prove equivalence of specification and implementation using
-- ≃ data type.
equivalence-spec-imp : ∀ (A B : Bool) → (bit-adder-spec A B) ≃ (bit-adder-imp A B)
equivalence-spec-imp A B = f≃g (λ x y x₁ → prove x y x₁)
  where
  pr1 : (x y : Bool) → (bit-adder-spec x y true) ≡ (bit-adder-imp x y true)
  pr1 true true = refl
  pr1 true false = refl
  pr1 false true = refl
  pr1 false false = refl

  pr2 : (x y : Bool) → (bit-adder-spec x y false) ≡ (bit-adder-imp x y false)
  pr2 true true = refl
  pr2 true false = refl
  pr2 false true = refl
  pr2 false false = refl

  prove : (x y : Bool) → (p : x ≡ y) → (bit-adder-spec A B x) ≡ (bit-adder-imp A B y)
  prove true .true refl = pr1 A B
  prove false .false refl = pr2 A B


{-# OPTIONS --injective-type-constructors #-}

module InjectiveTypeConstructors where

data D (A : Set) : Set where

data _==_ (A : Set) : Set → Set where
  refl : A == A

D' = D

injD : ∀ {A B} → D A == D' B → A == B
injD refl = refl


------------------------------------------------------------------------------
-- The Booleans properties
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module LTC-PCF.Data.Bool.Properties where

open import LTC-PCF.Base
open import LTC-PCF.Data.Bool
open import LTC-PCF.Data.Nat.Inequalities
open import LTC-PCF.Data.Nat.Inequalities.Properties
open import LTC-PCF.Data.Nat.Type

------------------------------------------------------------------------------
-- Congruence properties

notCong : ∀ {a b} → a ≡ b → not a ≡ not b
notCong refl = refl

------------------------------------------------------------------------------
-- Basic properties

t&&x≡x : ∀ b → true && b ≡ b
t&&x≡x b = if-true b

f&&x≡f : ∀ b → false && b ≡ false
f&&x≡f b = if-false false

not-t : not true ≡ false
not-t = if-true false

not-f : not false ≡ true
not-f = if-false true

&&-Bool : ∀ {a b} → Bool a → Bool b → Bool (a && b)
&&-Bool {b = b} btrue  Bb = subst Bool (sym (t&&x≡x b)) Bb
&&-Bool {b = b} bfalse Bb = subst Bool (sym (f&&x≡f b)) bfalse

not-Bool : ∀ {b} → Bool b → Bool (not b)
not-Bool btrue  = subst Bool (sym not-t) bfalse
not-Bool bfalse = subst Bool (sym not-f) btrue

&&-comm : ∀ {a b} → Bool a → Bool b → a && b ≡ b && a
&&-comm btrue  btrue  = refl
&&-comm btrue  bfalse = trans (t&&x≡x false) (sym (f&&x≡f true))
&&-comm bfalse btrue  = trans (f&&x≡f true) (sym (t&&x≡x false))
&&-comm bfalse bfalse = refl

x≢not-x : ∀ {b} → Bool b → b ≢ not b
x≢not-x btrue  h = t≢f (trans h not-t)
x≢not-x bfalse h = t≢f (sym (trans h not-f))

not-x≢x : ∀ {b} → Bool b → not b ≢ b
not-x≢x Bb h = x≢not-x Bb (sym h)

not-involutive : ∀ {b} → Bool b → not (not b) ≡ b
not-involutive btrue  = trans (notCong not-t) not-f
not-involutive bfalse = trans (notCong not-f) not-t

------------------------------------------------------------------------------
-- Properties with inequalities

lt-Bool : ∀ {m n} → N m → N n → Bool (lt m n)
lt-Bool nzero          nzero          = subst Bool (sym lt-00) bfalse
lt-Bool nzero          (nsucc {n} Nn) = subst Bool (sym (lt-0S n)) btrue
lt-Bool (nsucc {m} Nm) nzero          = subst Bool (sym (lt-S0 m)) bfalse
lt-Bool (nsucc {m} Nm) (nsucc {n} Nn) = subst Bool (sym (lt-SS m n)) (lt-Bool Nm Nn)

le-Bool : ∀ {m n} → N m → N n → Bool (le m n)
le-Bool {n = n} nzero         Nn             = subst Bool (sym (lt-0S n)) btrue
le-Bool        (nsucc Nm)     nzero          = subst Bool (sym (Sx≰0 Nm)) bfalse
le-Bool        (nsucc {m} Nm) (nsucc {n} Nn) =
  subst Bool (sym (lt-SS m (succ₁ n))) (le-Bool Nm Nn)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Universal lifting of predicates over Vectors
------------------------------------------------------------------------

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

module Data.Vec.Functional.Relation.Unary.All where

open import Data.Fin.Base
open import Data.Fin.Properties
open import Data.Nat.Base
open import Data.Product using (_,_)
open import Data.Vec.Functional as VF hiding (map)
open import Level using (Level)
open import Relation.Unary

private
  variable
    a p q ℓ : Level
    A : Set a

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

All : Pred A ℓ → ∀ {n} → Vector A n → Set ℓ
All P xs = ∀ i → P (xs i)

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

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

  map : P ⊆ Q → ∀ {n} → All P {n = n} ⊆ All Q
  map p⊆q ps i = p⊆q (ps i)

------------------------------------------------------------------------
-- Properties of predicates preserved by All

module _ {P : Pred A p} where

  all : Decidable P → ∀ {n} → Decidable (All P {n = n})
  all p? xs = all? λ i → p? (xs i)

  universal : Universal P → ∀ {n} → Universal (All P {n = n})
  universal uni xs i = uni (xs i)

  satisfiable : Satisfiable P → ∀ {n} → Satisfiable (All P {n = n})
  satisfiable (x , px) = (λ _ → x) , (λ _ → px)


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

open import Algebra.Structures.Bundles.Field

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

open import Level using (_⊔_)
open import Data.Product hiding (map)
open import Data.Fin using (Fin; toℕ; fromℕ; _≤_)
open import Data.Fin.Properties using (¬Fin0)
open import Data.Nat hiding (_⊔_; _≤_) renaming (_+_ to _+ℕ_; _*_ to _*ℕ_)
open import Data.Nat.Properties using (1+n≢0)

open import Relation.Binary
import Data.Vec.Relation.Binary.Pointwise.Inductive as PW
open import Algebra.Linear.Core

import Data.Vec.Properties as VP

open import Algebra.Structures.Field.Utils K

import Algebra.Linear.Construct.Vector K as V
open V
  using (Vec; zipWith; replicate)
  renaming
    ( _+_ to _+v_
    ; _∙_ to _∙v_
    ; -_  to -v_
    )

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

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ʳ
    )

Matrix : ℕ -> ℕ -> Set k
Matrix n p = Vec (Vec K' p) n

private
  M : ℕ -> ℕ -> Set k
  M = Matrix

_≈ʰ_ : ∀ {n p n' p'} (A : M n p) (B : M n' p') → Set (k ⊔ ℓ)
_≈ʰ_ = PW.Pointwise V._≈ʰ_

module _ {n p} where
  setoid : Setoid k (k ⊔ ℓ)
  setoid = record
    { Carrier       = M n p
    ; _≈_           = _≈ʰ_ {n} {p}
    ; isEquivalence = PW.isEquivalence (V.≈-isEquiv {p}) n
    }

  open Setoid setoid public
    renaming
      ( refl  to ≈-refl
      ; sym   to ≈-sym
      ; trans to ≈-trans
      ; reflexive to ≈-reflexive
      ; isEquivalence to ≈-isEquiv
      )

import Algebra.FunctionProperties as FP

tabulate : ∀ {n p} -> (Fin n -> Fin p -> K') -> M n p
tabulate f = V.tabulate λ i -> V.tabulate λ j -> f i j

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

fromVec : ∀ {n p} -> V.Vec K' (n *ℕ p) -> M n p
fromVec {0} V.[] = V.[]
fromVec {suc n} {p} xs =
  let (vp , vnp , _) = V.splitAt p xs
  in vp V.∷ fromVec {n} vnp

toVec : ∀ {n p} -> M n p -> V.Vec K' (n *ℕ p)
toVec {n} {p} = V.concat {m = p} {n = n}

concat∘fromVec : ∀ {n p} (v : V.Vec K' (n *ℕ p)) -> toVec {n} {p} (fromVec v) V.≈ v
concat∘fromVec {0} V.[] = PW.[]
concat∘fromVec {suc n} {p} v =
  let (vn , vnp , r) = V.splitAt p v
  in begin
    toVec {suc n} {p} (fromVec v)
  ≡⟨⟩
    vn V.++ toVec {n} {p} (fromVec vnp)
  ≈⟨ V.++-cong {p} {n *ℕ p} V.≈-refl (concat∘fromVec {n} {p} vnp) ⟩
    vn V.++ vnp
  ≈⟨ V.≈-sym (V.≈-reflexive r) ⟩
    v
  ∎
  where open import Relation.Binary.EqReasoning (V.setoid (suc n *ℕ p))

_++_ : ∀ {n p q} -> M n p -> M n q -> M n (p +ℕ q)
_++_ = zipWith V._++_

_‡_ : ∀ {n m p} -> M n p -> M m p -> M (n +ℕ m) p
_‡_ = V._++_

lookup : ∀ {n p} -> M n p -> Fin n -> Fin p -> K'
lookup A i j = V.lookup (V.lookup A i) j

_⟪_,_⟫ : ∀ {n p} -> M n p -> Fin n -> Fin p -> K'
_⟪_,_⟫ = lookup

lookup-cong : ∀ {n p} {A B : M n p} (i : Fin n) (j : Fin p) -> A ≈ B -> (A ⟪ i , j ⟫) ≈ᵏ (B ⟪ i , j ⟫)
lookup-cong i j rs = PW.lookup (PW.lookup rs i) j

tabulate∘lookup : ∀ {n p} (A : M n p) → tabulate (lookup A) ≡ A
tabulate∘lookup A =
  begin
    tabulate (lookup A)
  ≡⟨⟩
    V.tabulate (λ i -> V.tabulate λ j -> V.lookup (V.lookup A i) j)
  ≡⟨ VP.tabulate-cong (λ i → VP.tabulate∘lookup (V.lookup A i)) ⟩
    V.tabulate (λ i -> V.lookup A i)
  ≡⟨ VP.tabulate∘lookup A ⟩
    A
  ∎
  where open import Relation.Binary.PropositionalEquality as Eq
        open Eq.≡-Reasoning

lookup∘tabulate : ∀ {n p} (f : Fin n -> Fin p -> K') (i : Fin n) (j : Fin p)
                -> lookup (tabulate f) i j ≡ f i j
lookup∘tabulate {suc n} f i j =
  begin
    lookup (tabulate f) i j
  ≡⟨⟩
    V.lookup (V.lookup (V.tabulate λ i′ -> V.tabulate λ j′ -> f i′ j′) i) j
  ≡⟨ cong (λ u → V.lookup u j) (VP.lookup∘tabulate (λ i′ -> V.tabulate λ j′ -> f i′ j′) i) ⟩
    V.lookup (V.tabulate λ j′ -> f i j′) j
  ≡⟨ VP.lookup∘tabulate (λ j′ -> f i j′) j ⟩
    f i j
  ∎
  where open import Relation.Binary.PropositionalEquality as Eq
        open Eq.≡-Reasoning

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

tabulate-cong : ∀ {n p} {f g : Fin n -> Fin p -> K'} -> (∀ i j -> f i j ≈ᵏ g i j) -> tabulate f ≈ tabulate g
tabulate-cong {f = f} {g = g} r = PW.tabulate⁺ (λ i → V.tabulate-cong (λ j → r i j))

transpose : ∀ {n p} -> M n p -> M p n
transpose A = tabulate λ i j -> A ⟪ j , i ⟫

_ᵀ : ∀ {n p} -> M n p -> M p n
_ᵀ = transpose

transpose-involutive : ∀ {n p} (A : M n p) -> ((A ᵀ) ᵀ) ≡ A
transpose-involutive A =
  begin
    ((A ᵀ)ᵀ)
  ≡⟨⟩
    (tabulate λ i j -> (tabulate λ i′ j′ -> A ⟪ j′ , i′ ⟫) ⟪ j , i ⟫)
  ≡⟨ tabulate-cong-≡ (λ i j → lookup∘tabulate (λ i′ j′ -> A ⟪ j′ , i′ ⟫) j i) ⟩
    (tabulate λ i j -> A ⟪ i , j ⟫)
  ≡⟨ tabulate∘lookup A ⟩
    A
  ∎
  where open import Relation.Binary.PropositionalEquality as Eq
        open Eq.≡-Reasoning

map : ∀ {n p} -> (K' -> K') -> M n p -> M n p
map f = V.map (V.map f)

mapRows : ∀ {n p q} -> (V.Vec K' p -> V.Vec K' q) -> M n p -> M n q
mapRows = V.map

mapCols : ∀ {n m p} -> (V.Vec K' n -> V.Vec K' m) -> M n p -> M m p
mapCols f A = (V.map f (A ᵀ)) ᵀ

map-cong : ∀ {n p} {f g : K' -> K'} -> f ≗ g -> map {n} {p} f ≗ map g
map-cong r = VP.map-cong (VP.map-cong r)

mapRows-cong : ∀ {n p q} {f g : V.Vec K' p -> V.Vec K' q}
             -> f ≗ g -> mapRows {n} f ≗ mapRows g
mapRows-cong = VP.map-cong

mapCols-cong : ∀ {n m p} {f g : V.Vec K' n -> V.Vec K' m}
             -> f ≗ g -> mapCols {n} {m} {p} f ≗ mapCols g
mapCols-cong r A = P.cong transpose (VP.map-cong r (transpose A))

open import Data.Nat.DivMod

_+_ : ∀ {n p} -> FP.Op₂ (M n p)
_+_ = zipWith V._+_

_∙_ : ∀ {n p} -> ScalarMultiplication K' (M n p)
_∙_ k = V.map (k V.∙_)

-_ : ∀ {n p} -> FP.Op₁ (M n p)
-_ = V.map V.-_

0# : ∀ {n p} -> M n p
0# = replicate V.0#

+-cong : ∀ {n p} {A B C D : M n p} -> A ≈ B -> C ≈ D -> (A + C) ≈ (B + D)
+-cong PW.[] PW.[] = PW.[]
+-cong (r₁ PW.∷ rs₁) (r₂ PW.∷ rs₂) = V.+-cong r₁ r₂ PW.∷ +-cong rs₁ rs₂

+-assoc : ∀ {n p} (A B C : M n p) -> ((A + B) + C) ≈ (A + (B + C))
+-assoc V.[] V.[] V.[] = PW.[]
+-assoc (u V.∷ us) (v V.∷ vs) (w V.∷ ws) =
  V.+-assoc u v w PW.∷ +-assoc us vs ws

+-identityˡ : ∀ {n p} (A : M n p) -> (0# + A) ≈ A
+-identityˡ V.[] = PW.[]
+-identityˡ (u V.∷ us) = V.+-identityˡ u PW.∷ +-identityˡ us

+-identityʳ : ∀ {n p} (A : M n p) -> (A + 0#) ≈ A
+-identityʳ V.[] = PW.[]
+-identityʳ (u V.∷ us) = V.+-identityʳ u PW.∷ +-identityʳ us

+-identity : ∀ {n p} -> ((∀ (A : M n p) -> ((0# + A) ≈ A)) × (∀ (A : M n p) -> ((A + 0#) ≈ A)))
+-identity = +-identityˡ , +-identityʳ

+-comm : ∀ {n p} (A B : M n p) -> (A + B) ≈ (B + A)
+-comm V.[] V.[] = PW.[]
+-comm (u V.∷ us) (v V.∷ vs) = (V.+-comm u v) PW.∷ (+-comm us vs)

*ᵏ-∙-compat : ∀ {n p} (a b : K') (A : M n p) -> ((a *ᵏ b) ∙ A) ≈ (a ∙ (b ∙ A))
*ᵏ-∙-compat a b V.[] = PW.[]
*ᵏ-∙-compat a b (u V.∷ us) = (V.*ᵏ-∙-compat a b u) PW.∷ (*ᵏ-∙-compat a b us)

∙-+-distrib : ∀ {n p} (a : K') (A B : M n p) -> (a ∙ (A + B)) ≈ ((a ∙ A) + (a ∙ B))
∙-+-distrib a V.[] V.[] = PW.[]
∙-+-distrib a (u V.∷ us) (v V.∷ vs) = (V.∙-+-distrib a u v) PW.∷ (∙-+-distrib a us vs)

∙-+ᵏ-distrib : ∀ {n p} (a b : K') (A : M n p) -> ((a +ᵏ b) ∙ A) ≈ ((a ∙ A) + (b ∙ A))
∙-+ᵏ-distrib a b V.[] = PW.[]
∙-+ᵏ-distrib a b (u V.∷ us) = (V.∙-+ᵏ-distrib a b u) PW.∷ (∙-+ᵏ-distrib a b us)

∙-cong : ∀ {n p} {a b : K'} {A B : M n p} → a ≈ᵏ b -> A ≈ B -> (a ∙ A) ≈ (b ∙ B)
∙-cong rᵏ PW.[] = PW.[]
∙-cong rᵏ (r PW.∷ rs) = (V.∙-cong rᵏ r) PW.∷ (∙-cong rᵏ rs)

∙-identity : ∀ {n p} (A : M n p) → (1ᵏ ∙ A) ≈ A
∙-identity V.[] = PW.[]
∙-identity (u V.∷ us) = (V.∙-identity u) PW.∷ (∙-identity us)

∙-absorbˡ : ∀ {n p} (A : M n p) → (0ᵏ ∙ A) ≈ 0#
∙-absorbˡ V.[] = PW.[]
∙-absorbˡ (u V.∷ us) = (V.∙-absorbˡ u) PW.∷ (∙-absorbˡ us)

-‿inverseˡ : ∀ {n p} (A : M n p) -> ((- A) + A) ≈ 0#
-‿inverseˡ V.[] = PW.[]
-‿inverseˡ (u V.∷ us) = (V.-‿inverseˡ u) PW.∷ (-‿inverseˡ us)

-‿inverseʳ : ∀ {n p} (A : M n p) -> (A + (- A)) ≈ 0#
-‿inverseʳ V.[] = PW.[]
-‿inverseʳ (u V.∷ us) = (V.-‿inverseʳ u) PW.∷ (-‿inverseʳ us)

-‿inverse : ∀ {n p} → (∀ (A : M n p) -> ((- A) + A) ≈ 0#) × (∀ (A : M n p) -> (A + (- A)) ≈ 0#)
-‿inverse = -‿inverseˡ , -‿inverseʳ

-‿cong : ∀ {n p} {A B : M n p} -> A ≈ B -> (- A) ≈ (- B)
-‿cong PW.[] = PW.[]
-‿cong (r PW.∷ rs) = (V.-‿cong r) PW.∷ (-‿cong rs)

concat-+ : ∀ {n p} (A B : M n p) -> V.concat (V.zipWith V._+_ A B) V.≈ (V.concat A) +v (V.concat B)
concat-+ {0} {p} V.[] V.[] =
  V.≈-trans (PW.concat⁺ {m = p} {p = 0} PW.[]) (V.≈-sym (V.+-identityˡ V.[]))
concat-+ {suc n} {p} (u V.∷ us) (v V.∷ vs) =
  begin
    V.concat (V.zipWith V._+_ (u V.∷ us) (v V.∷ vs))
  ≡⟨⟩
    (u V.+ v) V.++ V.concat (V.zipWith V._+_ us vs)
  ≈⟨ V.++-cong V.≈-refl (concat-+ {n} {p} us vs) ⟩
    (u V.+ v) V.++ (V.concat us V.+ V.concat vs)
  ≈⟨ V.≈-sym (V.+-++-distrib u (V.concat us) v (V.concat vs)) ⟩
    (V.concat (u V.∷ us)) V.+ (V.concat (v V.∷ vs))
  ∎
  where open import Relation.Binary.EqReasoning (V.setoid (p +ℕ n *ℕ p))

concat-0# : ∀ {n p} -> V.concat (0# {n} {p}) V.≈ (V.0# {n *ℕ p})
concat-0# {0} = PW.[]
concat-0# {suc n} {p} =
  begin
    V.concat (0# {suc n} {p})
  ≡⟨⟩
    V.0# {p} V.++ V.concat (0# {n} {p})
  ≈⟨ PW.++⁺ V.≈-refl (concat-0# {n} {p}) ⟩
    V.0# {p} V.++ V.0# {n *ℕ p}
  ≈⟨ V.0++0≈0 {p} {n *ℕ p} ⟩
    V.0# {p +ℕ n *ℕ p}
  ∎
  where open import Relation.Binary.EqReasoning (V.setoid (p +ℕ n *ℕ p))

concat-∙ : ∀ {n p} (c : K') (A : M n p) -> V.concat (c ∙ A) V.≈ (c V.∙ V.concat A)
concat-∙ {0} c V.[] = PW.[]
concat-∙ {suc n} {p} c (u V.∷ us) =
  begin
    V.concat ((c V.∙ u) V.∷ (c ∙ us))
  ≡⟨⟩
    (c V.∙ u) V.++ (V.concat (c ∙ us))
  ≈⟨ V.++-cong V.≈-refl (concat-∙ {n} {p} c us) ⟩
    (c V.∙ u) V.++ (c V.∙ V.concat us)
  ≈⟨ V.≈-sym (V.∙-++-distrib c u (V.concat us)) ⟩
    c V.∙ V.concat (u V.∷ us)
  ∎
  where open import Relation.Binary.EqReasoning (V.setoid (p +ℕ n *ℕ p))

I : ∀ {n p} -> M n p
I = tabulate δ

_*_ : ∀ {n p q} -> M n p -> M p q -> M n q
A * B = tabulate λ i j -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ (B ⟪ k , j ⟫)

*-cong : ∀ {n p q} {A B : M n p} {C D : M p q}
       -> A ≈ B -> C ≈ D -> (A * C) ≈ (B * D)
*-cong {n} {q = q} {A} {B} {C} {D} r₁ r₂ =
  begin
    A * C
  ≡⟨⟩
    tabulate (λ i j -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ (C ⟪ k , j ⟫))
  ≈⟨ tabulate⁺ (λ i j -> V.sum-tab-cong λ k → *ᵏ-cong (lookup-cong i k r₁) (lookup-cong k j r₂)) ⟩
    B * D
  ∎
  where open import Relation.Binary.EqReasoning (setoid {n} {q})

*-assoc : ∀ {n p q r} (A : M n p) (B : M p q) (C : M q r)
        -> ((A * B) * C) ≈ (A * (B * C))
*-assoc {n} {r = r} A B C = tabulate⁺ λ i j ->
  begin
    V.sum-tab (λ k′ -> ((A * B) ⟪ i , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫))
  ≈⟨ V.sum-tab-cong (λ k′ -> *ᵏ-cong (≈ᵏ-reflexive (lookup∘tabulate
                   (λ i′ j′ -> V.sum-tab λ k -> (A ⟪ i′ , k ⟫) *ᵏ (B ⟪ k , j′ ⟫)) i k′)) ≈ᵏ-refl) ⟩
    V.sum-tab (λ k′ -> (V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ (B ⟪ k , k′ ⟫)) *ᵏ (C ⟪ k′ , j ⟫))
  ≈⟨ V.sum-tab-cong (λ k′ -> V.*ᵏ-sum-tab-distribʳ (C ⟪ k′ , j ⟫) λ k -> (A ⟪ i , k ⟫) *ᵏ (B ⟪ k , k′ ⟫)) ⟩
    V.sum-tab (λ k′ -> V.sum-tab λ k -> ((A ⟪ i , k ⟫) *ᵏ (B ⟪ k , k′ ⟫)) *ᵏ (C ⟪ k′ , j ⟫))
  ≈⟨ V.sum-tab-cong (λ k′ -> V.sum-tab-cong λ k -> *ᵏ-assoc (A ⟪ i , k ⟫) (B ⟪ k , k′ ⟫) (C ⟪ k′ , j ⟫)) ⟩
    V.sum-tab (λ k′ -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ ((B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)))
  ≈⟨ V.sum-tab-cong (λ k′ -> V.sum-tab-cong λ k -> *ᵏ-comm (A ⟪ i , k ⟫) ((B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫))) ⟩
    V.sum-tab (λ k′ -> V.sum-tab λ k -> ((B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)) *ᵏ (A ⟪ i , k ⟫))
  ≈⟨ V.sum-tab-swap (λ k k′ -> (B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)) (λ k -> A ⟪ i , k ⟫) ⟩
    V.sum-tab (λ k -> V.sum-tab λ k′ -> ((B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)) *ᵏ (A ⟪ i , k ⟫))
  ≈⟨ V.sum-tab-cong (λ k -> ≈ᵏ-trans
                       (≈ᵏ-sym (V.*ᵏ-sum-tab-distribʳ (A ⟪ i , k ⟫) λ k′ -> (B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)))
                       (*ᵏ-comm (V.sum-tab λ k′ -> (B ⟪ k , k′ ⟫) *ᵏ (C ⟪ k′ , j ⟫)) (A ⟪ i , k ⟫))) ⟩
    V.sum-tab (λ k -> (A ⟪ i , k ⟫) *ᵏ V.sum-tab (λ k′ -> (B ⟪ k , k′ ⟫) *ᵏ  (C ⟪ k′ , j ⟫)))
  ≈⟨ V.sum-tab-cong (λ k -> *ᵏ-cong ≈ᵏ-refl
                   (≈ᵏ-sym (≈ᵏ-reflexive (lookup∘tabulate
                     (λ i′ j′ -> V.sum-tab λ k′ -> (B ⟪ i′ , k′ ⟫) *ᵏ (C ⟪ k′ , j′ ⟫)) k j)))) ⟩
    V.sum-tab (λ k -> (A ⟪ i , k ⟫) *ᵏ ((B * C) ⟪ k , j ⟫))
  ∎
  where open import Relation.Binary.EqReasoning (Field.setoid K)

*-identityˡ : ∀ {n p} (A : M n p) -> I * A ≈ A
*-identityˡ {n} {p} A =
  begin
    I * A
  ≡⟨⟩
    tabulate (λ i j -> V.sum-tab λ k -> (I ⟪ i , k ⟫) *ᵏ (A ⟪ k , j ⟫))
  ≈⟨ tabulate-cong (λ i j -> V.sum-tab-cong {n} λ k -> *ᵏ-cong (≈ᵏ-reflexive (lookup∘tabulate δ i k)) ≈ᵏ-refl) ⟩
    tabulate (λ i j -> V.sum-tab λ k -> δ i k *ᵏ (A ⟪ k , j ⟫))
  ≈⟨ tabulate-cong (λ i j -> V.sum-tab-δ (λ k -> A ⟪ k , j ⟫) i) ⟩
    tabulate (λ i j -> A ⟪ i , j ⟫)
  ≡⟨ tabulate∘lookup A ⟩
    A
  ∎
  where open import Relation.Binary.EqReasoning (setoid {n} {p})

*-identityʳ : ∀ {n p} (A : M n p) -> A * I ≈ A
*-identityʳ {n} {p} A =
  begin
    A * I
  ≡⟨⟩
    tabulate (λ i j -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ (I ⟪ k , j ⟫))
  ≈⟨ tabulate-cong (λ i j -> V.sum-tab-cong {p} λ k -> *ᵏ-cong ≈ᵏ-refl (≈ᵏ-reflexive (lookup∘tabulate δ k j))) ⟩
    tabulate (λ i j -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ δ k j)
  ≈⟨ tabulate-cong (λ i j -> V.sum-tab-cong {p} λ k -> *ᵏ-cong ≈ᵏ-refl (δ-comm k j)) ⟩
    tabulate (λ i j -> V.sum-tab λ k -> (A ⟪ i , k ⟫) *ᵏ δ j k)
  ≈⟨ tabulate-cong (λ i j -> ≈ᵏ-trans (V.sum-tab-cong {p} (λ k -> *ᵏ-comm ((A ⟪ i , k ⟫)) (δ j k)))
                                      (V.sum-tab-δ (λ k -> A ⟪ i , k ⟫) j)) ⟩
    tabulate (λ i j -> A ⟪ i , j ⟫)
  ≡⟨ tabulate∘lookup A ⟩
    A
  ∎
  where open import Relation.Binary.EqReasoning (setoid {n} {p})

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

  open FP (_≈_ {n} {p})

  open import Algebra.Structures (_≈_ {n} {p})
  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.Structures.FiniteDimensional K
  open import Algebra.Linear.Morphism.VectorSpace K
  open import Algebra.Linear.Morphism.Bundles K

  open import Algebra.Morphism.Definitions (M n p) (Vec K' (n *ℕ p)) V._≈_
  open import Algebra.Linear.Morphism.Definitions K (M n p) (Vec K' (n *ℕ p)) V._≈_
  import Relation.Binary.Morphism.Definitions (M n p) (Vec K' (n *ℕ p)) as R
  open import Function
  open import Relation.Binary.EqReasoning (V.setoid (n *ℕ p))

  ⟦_⟧ : M n p -> Vec K' (n *ℕ p)
  ⟦_⟧ = V.concat

  ⟦⟧-cong : R.Homomorphic₂ _≈_ (V._≈_ {n *ℕ p}) ⟦_⟧
  ⟦⟧-cong = PW.concat⁺

  +-homo : Homomorphic₂ ⟦_⟧ _+_ _+v_
  +-homo = concat-+

  0#-homo : Homomorphic₀ ⟦_⟧ 0# V.0#
  0#-homo = concat-0# {n} {p}

  ∙-homo : ScalarHomomorphism ⟦_⟧ _∙_ _∙v_
  ∙-homo = concat-∙

  ⟦⟧-injective : Injective (_≈_ {n} {p}) (V._≈_ {n *ℕ p}) ⟦_⟧
  ⟦⟧-injective {A} {B} r = PW.concat⁻ A B r

  ⟦⟧-surjective : Surjective (_≈_ {n} {p}) (V._≈_ {n *ℕ p}) ⟦_⟧
  ⟦⟧-surjective v = fromVec {n} {p} v , concat∘fromVec {n} {p} v

  embed : LinearIsomorphism vectorSpace (V.vectorSpace {n *ℕ p})
  embed = record
    { ⟦_⟧ = ⟦_⟧
    ; isLinearIsomorphism = record
      { isLinearMonomorphism = record
        { isLinearMap = record
          { isAbelianGroupMorphism = record
            { gp-homo = record
              { mn-homo = record
                { sm-homo = record
                  { ⟦⟧-cong = ⟦⟧-cong
                  ; ∙-homo = +-homo
                  }
                ; ε-homo = 0#-homo
                }
              }
            }
          ; ∙-homo = ∙-homo
          }
        ; injective = ⟦⟧-injective
        }
      ; surjective = ⟦⟧-surjective
      }
    }

  isFiniteDimensional : IsFiniteDimensional _≈_ _+_ _∙_ -_ 0# (n *ℕ p)
  isFiniteDimensional = record
    { isVectorSpace = isVectorSpace
    ; embed         = embed
    }


module sort where

open import bool
open import eq
open import nat
open import list
open import nondet
open import nondet-thms

-- non-deterministic insert.
--This takes a nondeterministic list because I need to be able to call it with the result of perm
--
--implementation in curry:
-- ndinsert x [] = []
-- ndinsert x (y:ys) = (x : y : xs) ? (y : insert x ys)
ndinsert : {A : Set} -> A -> ND (list A) -> ND (list A)
ndinsert x (Val [] )       = Val ( x :: [])
ndinsert x (Val (y :: ys)) = (Val ( x :: y :: ys )) ?? ((_::_ y) $* (ndinsert x (Val ys)))
ndinsert x (l ?? r)        = (ndinsert x l) ?? (ndinsert x r)

--non-deterministic permutation
--this is identical to the curry code (except for the Val constructor)
perm : {A : Set} -> (list A) -> ND (list A)
perm []        = Val []
perm (x :: xs) = ndinsert x (perm xs)

--insert a value into a sorted list.
--this is identical to curry or haskell code.
--
--note that the structure here is identical to ndinsert
insert : {A : Set} -> (A -> A -> 𝔹) -> A -> list A -> list A
insert _   x []        = x :: []
insert _<_ x (y :: ys) = if x < y
                         then (x :: y :: ys)
                         else (y :: insert _<_ x ys)

--simple insertion sort
--again this is identical to curry or haskell
--also, note that the structure is identical to perm
sort : {A : Set} -> (A -> A -> 𝔹) -> list A -> list A
sort _ []     = []
sort p (x :: xs) = insert p x (sort p xs)

-- Proof:  if xs ∈ nxs then x::xs ∈ ndinsert x nxs
insId : {A : Set} -> (x : A) -> (xs : list A) -> (x :: xs) ∈ (ndinsert x (Val xs))
insId x [] = ndrefl
insId x (y :: xs) = left (Val (x :: y :: xs)) ((_::_ y) $* (ndinsert x (Val xs))) ndrefl

--If introduction rule for non-deterministic values.
--if x and y are both possible values in z then
--∀ c. if c then x else y will give us either x or y, so it must be a possible value of z.
--
ifIntro : {A : Set} -> (x : A) -> (y : A) -> (z : ND A) -> (p : x ∈ z) -> (q : y ∈ z) -> (c : 𝔹) -> (if c then x else y) ∈ z
ifIntro x y z p q tt = p
ifIntro x y z p q ff = q

---------------------------------------------------------------------------
--
-- this should prove that if xs ∈ nxs then, insert x xs ∈ ndinsert x nxs
-- parameters:
-- c         : a comparison operator that is needed for insert
-- x         : the value we are inserting into the list
-- xs        : the list
-- nxs       : a non-deterministic list, we know that xs is a possible value for nxs
-- xs ∈ nxs : the proof that xs is somewhere in nxs
--
--retruns: insert c x xs ∈ ndinsert x nxs
--  a proof that inserting a value in a list is ok with non-deterministic lists
insert=ndinsert : {A : Set} -> (c : A -> A -> 𝔹) -> (x : A) -> (xs : list A) -> (nxs : ND (list A)) -> xs ∈ nxs -> (insert c x xs) ∈ (ndinsert x nxs)

--the first two cases are simple, either we are inserting into an empty list, in which case it's trivial
--or, the list doesn't match the non-deterministic case.  This is an imposible case
insert=ndinsert _ x [] (Val [])       _  = ndrefl
insert=ndinsert _ x [] (Val (_ :: _)) ()

--the next two cases are just structural recursion on the non-deterministic tree.
insert=ndinsert c x ys (l ?? r) (left  .l .r p) = left  (ndinsert x l) (ndinsert x r) (insert=ndinsert c x ys l p)
insert=ndinsert c x ys (l ?? r) (right .l .r p) = right (ndinsert x l) (ndinsert x r) (insert=ndinsert c x ys r p)

--The final case is the interesting one.
--We reached a leaf in the non-deterministic tree, so we have a deterministic value.
--by definition this is equal to the deterministic list (y :: ys)
--At this point we have two possible cases.
--Either x is smaller then every element in (y :: ys), in which case it's inserted at the front,
--or x is larger than y, in which case it's inserted somewhere in ys.
--Since both of these cases are covered by ndinsert we can invoke the ifIntro lemma, to say that we don't care which case it is.
--
--variables:
-- step    : one step of insert
-- l       : the left hand side of insert c x xs (the then branch)
-- r       : the right hand side of insert c x xs (the else branch)
-- nr      : a non-deterministic r
-- (Val l) : a non-deterministic l (but since ndinsert only has a deterministic value on the left it's not very interesting)
-- rec     : The recursive call.  If x isn't inserted into the front, then we need to find it.
-- l∈step : a proof that l is a possible value for step
-- r∈step : a proof that r is a possible value for step
insert=ndinsert _<_ x (y :: ys) (Val (.y :: .ys)) ndrefl = ifIntro l r step l∈step r∈step (x < y)
  where step   = ndinsert x (Val (y :: ys))
        l      = ( x :: y :: ys)
        r      = y :: insert _<_ x ys
        nr     = (_::_ y) $* (ndinsert x (Val ys))
        rec    = ∈-$* (_::_ y) (insert _<_ x ys) (ndinsert x (Val ys)) (insert=ndinsert _<_ x ys (Val ys) ndrefl)
        r∈step = right (Val l) nr rec
        l∈step = left  (Val l) nr ndrefl

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

-- main theorem.  Sorting a list preserves permutations
--all of the work is really done by insert=ndinsert
sortPerm : {A : Set} -> (c : A -> A -> 𝔹) -> (xs : list A) -> sort c xs ∈ perm xs
sortPerm _<_ []        = ndrefl
sortPerm _<_ (x :: xs) = insert=ndinsert _<_ x (sort _<_ xs) (perm xs) (sortPerm _<_ xs)


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.CommRing.BinomialThm where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function

open import Cubical.Data.Nat renaming ( _+_ to _+ℕ_ ; _·_ to _·ℕ_ ; _^_ to _^ℕ_
                                      ; +-comm to +ℕ-comm
                                      ; ·-assoc to ·ℕ-assoc ; ·-comm to ·ℕ-comm
                                      ; _choose_ to _ℕchoose_ ; snotz to ℕsnotz)
open import Cubical.Data.Nat.Order
open import Cubical.Data.FinData
open import Cubical.Data.Empty as ⊥

open import Cubical.Algebra.Monoid.BigOp
open import Cubical.Algebra.Ring.BigOps
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.RingSolver.Reflection

private
  variable
    ℓ : Level

module BinomialThm (R' : CommRing ℓ) where
 open CommRingStr (snd R')
 open Exponentiation R'
 open CommRingTheory R'
 open Sum (CommRing→Ring R')
 private R = fst R'

 _choose_ : ℕ → ℕ → R
 n choose zero = 1r
 zero choose suc k = 0r
 suc n choose suc k = n choose (suc k) + n choose k

 n<k→nChooseK≡0 : ∀ n k → n < k → n choose k ≡ 0r
 n<k→nChooseK≡0 zero zero (m , p) = ⊥.rec (ℕsnotz (sym (+ℕ-comm m 1) ∙ p))
 n<k→nChooseK≡0 zero (suc k) (m , p) = refl
 n<k→nChooseK≡0 (suc n) zero (m , p) = ⊥.rec (ℕsnotz {n = m +ℕ (suc n)} (sym (+-suc _ _) ∙ p))
 n<k→nChooseK≡0 (suc n) (suc k) (m , p) = cong₂ (_+_) p1 p2 ∙ +Lid 0r
  where
  p1 : n choose suc k ≡ 0r
  p1 = n<k→nChooseK≡0 n (suc k) (suc m , sym (+-suc _ _) ∙ p)
  p2 : n choose k ≡ 0r
  p2 =  n<k→nChooseK≡0 n k (m , injSuc (sym (+-suc _ _) ∙ p))

 nChooseN+1 : ∀ n → n choose (suc n) ≡ 0r
 nChooseN+1 n = n<k→nChooseK≡0 n (suc n) (0 , refl)

 BinomialVec : (n : ℕ) → R → R → FinVec R (suc n)
 BinomialVec n x y i = (n choose (toℕ i)) · x ^ (toℕ i) · y ^ (n ∸ toℕ i)

 BinomialThm : ∀ (n : ℕ) (x y : R) → (x + y) ^ n ≡ ∑ (BinomialVec n x y)
 BinomialThm zero x y = solve R'
 BinomialThm (suc n) x y =
     (x + y) ^ suc n
  ≡⟨ refl ⟩
     (x + y) · (x + y) ^ n
  ≡⟨ cong ((x + y) ·_) (BinomialThm n x y) ⟩
     (x + y) · ∑ (BinomialVec n x y)
  ≡⟨ ·Ldist+ _ _ _ ⟩
     x · ∑ (BinomialVec n x y) + y · ∑ (BinomialVec n x y)
  ≡⟨ cong₂ (_+_) (∑Mulrdist _ (BinomialVec n x y)) (∑Mulrdist _ (BinomialVec n x y)) ⟩
     ∑ (λ i → x · BinomialVec n x y i)
   + ∑ (λ i → y · BinomialVec n x y i)
  ≡⟨ refl ⟩
     ∑ {n = suc n} (λ i → x · ((n choose (toℕ i)) · x ^ (toℕ i) · y ^ (n ∸ toℕ i)))
   + ∑ {n = suc n} (λ i → y · ((n choose (toℕ i)) · x ^ (toℕ i) · y ^ (n ∸ toℕ i)))
  ≡⟨ cong₂ (_+_) (∑Ext xVecPath) (∑Ext yVecPath) ⟩
     ∑ xVec + ∑ yVec
  ≡⟨ cong (_+ ∑ yVec) (∑Last xVec) ⟩
     ∑ (xVec ∘ weakenFin) + xⁿ⁺¹ + (yⁿ⁺¹ + ∑ (yVec ∘ suc))
  ≡⟨ solve3 _ _ _ _ ⟩
     yⁿ⁺¹  + (∑ (xVec ∘ weakenFin) + ∑ (yVec ∘ suc)) + xⁿ⁺¹
  ≡⟨ cong (λ s → yⁿ⁺¹  + s + xⁿ⁺¹) (sym (∑Split _ _))  ⟩
     yⁿ⁺¹  + (∑ middleVec) + xⁿ⁺¹
  ≡⟨ cong (λ s → yⁿ⁺¹  + s + xⁿ⁺¹) (∑Ext middlePath) ⟩
     yⁿ⁺¹ + ∑ ((BinomialVec (suc n) x y) ∘ weakenFin ∘ suc) + xⁿ⁺¹
  ≡⟨ refl ⟩
     ∑ ((BinomialVec (suc n) x y) ∘ weakenFin) + xⁿ⁺¹
  ≡⟨ cong (∑ ((BinomialVec (suc n) x y) ∘ weakenFin) +_) xⁿ⁺¹Path
   ∙ sym (∑Last (BinomialVec (suc n) x y)) ⟩
     ∑ (BinomialVec (suc n) x y) ∎
  where
  xVec : FinVec R (suc n)
  xVec i = (n choose (toℕ i)) · x ^ (suc (toℕ i)) · y ^ (n ∸ toℕ i)

  solve1 : ∀ x nci xⁱ yⁿ⁻ⁱ → x · (nci · xⁱ · yⁿ⁻ⁱ) ≡ nci · (x · xⁱ) · yⁿ⁻ⁱ
  solve1 = solve R'

  xVecPath : ∀ (i : Fin (suc n)) → x · ((n choose (toℕ i)) · x ^ (toℕ i) · y ^ (n ∸ toℕ i)) ≡ xVec i
  xVecPath i = solve1 _ _ _ _

  yVec : FinVec R (suc n)
  yVec i = (n choose (toℕ i)) · x ^ (toℕ i) · y ^ (suc (n ∸ toℕ i))

  solve2 : ∀ y nci xⁱ yⁿ⁻ⁱ → y · (nci · xⁱ · yⁿ⁻ⁱ) ≡ nci · xⁱ · (y · yⁿ⁻ⁱ)
  solve2 = solve R'

  yVecPath : ∀ (i : Fin (suc n)) → y · ((n choose (toℕ i)) · x ^ (toℕ i) · y ^ (n ∸ toℕ i)) ≡ yVec i
  yVecPath i = solve2 _ _ _ _

  xⁿ⁺¹ : R
  xⁿ⁺¹ = xVec (fromℕ n)
  yⁿ⁺¹ : R
  yⁿ⁺¹ = yVec zero

  xⁿ⁺¹Path : xⁿ⁺¹ ≡ BinomialVec (suc n) x y (fromℕ (suc n))
  xⁿ⁺¹Path = cong (λ m → m · (x · x ^ toℕ (fromℕ n)) · y ^ (n ∸ toℕ (fromℕ n)))
                  (sym (+Lid _) ∙ cong (_+ (n choose toℕ (fromℕ n)))
                  (sym (subst (λ m → (n choose suc m) ≡ 0r) (sym (toFromId n)) (nChooseN+1 n))))

  solve3 : ∀ sx sy xⁿ⁺¹ yⁿ⁺¹ → sx + xⁿ⁺¹ + (yⁿ⁺¹ + sy) ≡ yⁿ⁺¹ + (sx + sy) + xⁿ⁺¹
  solve3 = solve R'

  middleVec : FinVec R n
  middleVec i = xVec (weakenFin i) + yVec (suc i)

  middlePath : ∀ (i : Fin n) → middleVec i ≡ BinomialVec (suc n) x y (weakenFin (suc i))
  middlePath i = transport (λ j →
     (n choose toℕ (weakenFin i)) · (x · x ^ toℕ (weakenFin i)) · y ^ (n ∸ toℕ (weakenFin i))
   + (n choose suc (weakenRespToℕ i j)) · (x · x ^ (weakenRespToℕ i j)) · sym yHelper j
   ≡ ((n choose suc (toℕ (weakenFin i))) + (n choose toℕ (weakenFin i)))
   · (x · x ^ toℕ (weakenFin i)) · y ^ (n ∸ toℕ (weakenFin i)))
   (solve4 _ _ _ _)
   where
   yHelper : (y · y ^ (n ∸ suc (toℕ i))) ≡ y ^ (n ∸ toℕ (weakenFin i))
   yHelper = cong (λ m → y · y ^ (n ∸ suc m)) (sym (weakenRespToℕ i))
           ∙ cong (y ^_) (≤-∸-suc (subst (λ m → suc m ≤ n) (sym (weakenRespToℕ _)) (toℕ<n i)))

   solve4 : ∀ nci ncsi xxⁱ yⁿ⁻ⁱ → nci · xxⁱ · yⁿ⁻ⁱ + ncsi · xxⁱ · yⁿ⁻ⁱ ≡ (ncsi + nci) · xxⁱ · yⁿ⁻ⁱ
   solve4 = solve R'


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

module Algebra.Linear.Core where

open import Relation.Binary
open import Relation.Nullary using (¬_)
open import Level using (_⊔_)
open import Data.Fin using (Fin; suc; zero)

VectorAddition : ∀ {c} -> Set c -> Set c
VectorAddition V = V -> V -> V

ScalarMultiplication : ∀ {c k} -> Set k -> Set c -> Set (k ⊔ c)
ScalarMultiplication K V = K -> V -> V


module Utilities.ExistsSyntax where

open import Level using (Level ; _⊔_)
open import Data.Product using (Σ)

variable
  a b : Level
  A : Set a
  B : Set b

∃-syntax : (A : Set a) → (A → Set b) → Set (a ⊔ b)
∃-syntax = Σ

syntax ∃-syntax A (λ x → B) = ∃ x ∶ A • B


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

module Categories.Functor where

open import Prelude
open import Categories

record Functor {ℓ₁ ℓ₂ ℓ₃ ℓ₄} (C : PreCategory ℓ₁ ℓ₂) (D : PreCategory ℓ₃ ℓ₄) : Type (ℓ₁ ℓ⊔ ℓ₂ ℓ⊔ ℓ₃ ℓ⊔ ℓ₄) where
  private module C = PreCategory C
  private module D = PreCategory D
  field
    F₀ : C.Ob → D.Ob
    F₁ : ∀ {X Y} → (X C.⟶ Y) → (F₀ X D.⟶ F₀ Y)

    identity : ∀ {X} → F₁ (C.Id {X}) ≡ D.Id
    homomorphism : ∀ {X Y Z} → (f : X C.⟶ Y) (g : Y C.⟶ Z) →
                   F₁ (g C.· f) ≡ F₁ g D.· F₁ f


------------------------------------------------------------------------
-- The Agda standard library
--
-- An inductive definition of the sublist relation with respect to a
-- setoid. This is a generalisation of what is commonly known as Order
-- Preserving Embeddings (OPE).
------------------------------------------------------------------------

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

open import Relation.Binary using (Setoid; Rel)

module Data.List.Relation.Binary.Sublist.Setoid
  {c ℓ} (S : Setoid c ℓ) where

open import Level using (_⊔_)
open import Data.List.Base using (List; []; _∷_)
import Data.List.Relation.Binary.Equality.Setoid as SetoidEquality
import Data.List.Relation.Binary.Sublist.Heterogeneous as Heterogeneous
import Data.List.Relation.Binary.Sublist.Heterogeneous.Core
  as HeterogeneousCore
import Data.List.Relation.Binary.Sublist.Heterogeneous.Properties
  as HeterogeneousProperties
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_)

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

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

infix 4 _⊆_
_⊆_ : Rel (List A) (c ⊔ ℓ)
_⊆_ = Heterogeneous.Sublist _≈_

------------------------------------------------------------------------
-- Re-export definitions and operations from heterogeneous sublists

open HeterogeneousCore _≈_ using ([]; _∷_; _∷ʳ_) public
open Heterogeneous {R = _≈_} hiding (Sublist; []; _∷_; _∷ʳ_) public
  renaming
  (toAny to to∈; fromAny to from∈)

------------------------------------------------------------------------
-- Relational properties holding for Setoid case

⊆-reflexive : _≋_ ⇒ _⊆_
⊆-reflexive = HeterogeneousProperties.fromPointwise

⊆-refl : Reflexive _⊆_
⊆-refl = HeterogeneousProperties.refl refl

⊆-trans : Transitive _⊆_
⊆-trans = HeterogeneousProperties.trans trans

⊆-antisym : Antisymmetric _≋_ _⊆_
⊆-antisym = HeterogeneousProperties.antisym (λ x≈y _ → x≈y)

⊆-isPreorder : IsPreorder _≋_ _⊆_
⊆-isPreorder = record
  { isEquivalence = ≋-isEquivalence
  ; reflexive     = ⊆-reflexive
  ; trans         = ⊆-trans
  }

⊆-isPartialOrder : IsPartialOrder _≋_ _⊆_
⊆-isPartialOrder = record
  { isPreorder = ⊆-isPreorder
  ; antisym    = ⊆-antisym
  }

⊆-preorder : Preorder c (c ⊔ ℓ) (c ⊔ ℓ)
⊆-preorder = record
  { isPreorder = ⊆-isPreorder
  }

⊆-poset : Poset c (c ⊔ ℓ) (c ⊔ ℓ)
⊆-poset = record
  { isPartialOrder = ⊆-isPartialOrder
  }


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

module Experiment.ListConstruction where

open import Level renaming (zero to lzero; suc to lsuc)

data ⊥ : Set where

record ⊤ : Set where
  constructor tt

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

module Inductive where
  data List (A : Set) : Set where
    [] : List A
    _∷_ : A → List A → List A

data Bool : Set where
  true false : Bool

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

∃ : ∀ {a b} {A : Set a} (B : A → Set b) → Set (a ⊔ b)
∃ {a} {b} {A} B = Σ A B

if : ∀ {p} {P : Bool → Set p} → ∀ b → P true → P false → P b
if true  t e = t
if false t e = e

_⊎_ : (A B : Set) → Set
A ⊎ B = ∃ λ b → if b A B

inj₁ : {A B : Set} → A → A ⊎ B
inj₁ x = true , x

inj₂ : {A B : Set} → B → A ⊎ B
inj₂ x = false , x

⊎-elim : ∀ {p} {A B : Set} {P : A ⊎ B → Set p} →
  (∀ x → P (inj₁ x)) → (∀ y → P (inj₂ y)) → ∀ s → P s
⊎-elim x y (true  , snd) = x snd
⊎-elim x y (false , snd) = y snd

_×_ : (A B : Set) → Set
A × B = ∀ b → if b A B

proj₁ : {A B : Set} → A × B → A
proj₁ x = x true

proj₂ : {A B : Set} → A × B → B
proj₂ x = x false

Fin : ℕ → Set
Fin zero    = ⊥
Fin (suc n) = ⊤ ⊎ Fin n

Vec : Set → ℕ → Set
Vec A n = Fin n → A

module VecList where
  List : Set → Set
  List A = ∃ λ n → Vec A n