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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Properties {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Irrelevance
     using (irrelevanceSubst′)
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties
import Definition.LogicalRelation.Weakening as LR

open import Tools.Unit
open import Tools.Product
import Tools.PropositionalEquality as PE

-- Valid substitutions are well-formed
wellformedSubst : ∀ {Γ Δ σ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
      → Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ
      → Δ ⊢ˢ σ ∷ Γ
wellformedSubst ε′ ⊢Δ [σ] = id
wellformedSubst ([Γ] ∙′ [A]) ⊢Δ ([tailσ] , [headσ]) =
  wellformedSubst [Γ] ⊢Δ [tailσ]
  , escapeTerm (proj₁ ([A] ⊢Δ [tailσ])) [headσ]

-- Valid substitution equality is well-formed
wellformedSubstEq : ∀ {Γ Δ σ σ′} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
      ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
      → Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ]
      → Δ ⊢ˢ σ ≡ σ′ ∷ Γ
wellformedSubstEq ε′ ⊢Δ [σ] [σ≡σ′] = id
wellformedSubstEq ([Γ] ∙′ [A]) ⊢Δ ([tailσ] , [headσ]) ([tailσ≡σ′] , [headσ≡σ′]) =
  wellformedSubstEq [Γ] ⊢Δ [tailσ] [tailσ≡σ′]
  , ≅ₜ-eq (escapeTermEq (proj₁ ([A] ⊢Δ [tailσ])) [headσ≡σ′])

-- Extend a valid substitution with a term
consSubstS : ∀ {l σ t A Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
           ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
           ([A] : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
           ([t] : Δ ⊩⟨ l ⟩ t ∷ subst σ A / proj₁ ([A] ⊢Δ [σ]))
         → Δ ⊩ˢ consSubst σ t ∷ Γ ∙ A / [Γ] ∙″ [A] / ⊢Δ
consSubstS [Γ] ⊢Δ [σ] [A] [t] = [σ] , [t]

-- Extend a valid substitution equality with a term
consSubstSEq : ∀ {l σ σ′ t A Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
             ([σ]    : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
             ([σ≡σ′] : Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ])
             ([A] : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
             ([t] : Δ ⊩⟨ l ⟩ t ∷ subst σ A / proj₁ ([A] ⊢Δ [σ]))
           → Δ ⊩ˢ consSubst σ t ≡ consSubst σ′ t ∷ Γ ∙ A / [Γ] ∙″ [A] / ⊢Δ
               / consSubstS {t = t} {A = A} [Γ] ⊢Δ [σ] [A] [t]
consSubstSEq [Γ] ⊢Δ [σ] [σ≡σ′] [A] [t] =
  [σ≡σ′] , reflEqTerm (proj₁ ([A] ⊢Δ [σ])) [t]

-- Weakening of valid substitutions
wkSubstS : ∀ {ρ σ Γ Δ Δ′} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ) (⊢Δ′ : ⊢ Δ′)
           ([ρ] : ρ ∷ Δ′ ⊆ Δ)
           ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
         → Δ′ ⊩ˢ ρ •ₛ σ ∷ Γ / [Γ] / ⊢Δ′
wkSubstS ε′ ⊢Δ ⊢Δ′ ρ [σ] = tt
wkSubstS {σ = σ} {Γ = Γ ∙ A} ([Γ] ∙′ x) ⊢Δ ⊢Δ′ ρ [σ] =
  let [tailσ] = wkSubstS [Γ] ⊢Δ ⊢Δ′ ρ (proj₁ [σ])
  in  [tailσ]
   ,  irrelevanceTerm′ (wk-subst A)
        (LR.wk ρ ⊢Δ′ (proj₁ (x ⊢Δ (proj₁ [σ]))))
        (proj₁ (x ⊢Δ′ [tailσ]))
        (LR.wkTerm ρ ⊢Δ′ (proj₁ (x ⊢Δ (proj₁ [σ]))) (proj₂ [σ]))

-- Weakening of valid substitution equality
wkSubstSEq : ∀ {ρ σ σ′ Γ Δ Δ′} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ) (⊢Δ′ : ⊢ Δ′)
             ([ρ] : ρ ∷ Δ′ ⊆ Δ)
             ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
             ([σ≡σ′] : Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ])
           → Δ′ ⊩ˢ ρ •ₛ σ ≡ ρ •ₛ σ′ ∷ Γ / [Γ]
                / ⊢Δ′ / wkSubstS [Γ] ⊢Δ ⊢Δ′ [ρ] [σ]
wkSubstSEq ε′ ⊢Δ ⊢Δ′ ρ [σ] [σ≡σ′] = tt
wkSubstSEq {Γ = Γ ∙ A} ([Γ] ∙′ x) ⊢Δ ⊢Δ′ ρ [σ] [σ≡σ′] =
  wkSubstSEq [Γ] ⊢Δ ⊢Δ′ ρ (proj₁ [σ]) (proj₁ [σ≡σ′])
  , irrelevanceEqTerm′ (wk-subst A) (LR.wk ρ ⊢Δ′ (proj₁ (x ⊢Δ (proj₁ [σ]))))
                            (proj₁ (x ⊢Δ′ (wkSubstS [Γ] ⊢Δ ⊢Δ′ ρ (proj₁ [σ]))))
                            (LR.wkEqTerm ρ ⊢Δ′ (proj₁ (x ⊢Δ (proj₁ [σ]))) (proj₂ [σ≡σ′]))

-- Weaken a valid substitution by one type
wk1SubstS : ∀ {F σ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
            (⊢F : Δ ⊢ F)
            ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
          → (Δ ∙ F) ⊩ˢ wk1Subst σ ∷ Γ / [Γ]
                            / (⊢Δ ∙ ⊢F)
wk1SubstS {F} {σ} {Γ} {Δ} [Γ] ⊢Δ ⊢F [σ] =
  wkSubstS [Γ] ⊢Δ (⊢Δ ∙ ⊢F) (step id) [σ]

-- Weaken a valid substitution equality by one type
wk1SubstSEq : ∀ {F σ σ′ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
              (⊢F : Δ ⊢ F)
              ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
              ([σ≡σ′] : Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ])
            → (Δ ∙ F) ⊩ˢ wk1Subst σ ≡ wk1Subst σ′ ∷ Γ / [Γ]
                            / (⊢Δ ∙ ⊢F) / wk1SubstS [Γ] ⊢Δ ⊢F [σ]
wk1SubstSEq {l} {F} {σ} {Γ} {Δ} [Γ] ⊢Δ ⊢F [σ] [σ≡σ′] =
  wkSubstSEq [Γ] ⊢Δ (⊢Δ ∙ ⊢F) (step id) [σ] [σ≡σ′]

-- Lift a valid substitution
liftSubstS : ∀ {l F σ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
             ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
             ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
           → (Δ ∙ subst σ F) ⊩ˢ liftSubst σ ∷ Γ ∙ F / [Γ] ∙″ [F]
                             / (⊢Δ ∙ escape (proj₁ ([F] ⊢Δ [σ])))
liftSubstS {F = F} {σ = σ} {Δ = Δ} [Γ] ⊢Δ [F] [σ] =
  let ⊢F = escape (proj₁ ([F] ⊢Δ [σ]))
      [tailσ] = wk1SubstS {F = subst σ F} [Γ] ⊢Δ (escape (proj₁ ([F] ⊢Δ [σ]))) [σ]
      var0 = var (⊢Δ ∙ ⊢F) (PE.subst (λ x → 0 ∷ x ∈ (Δ ∙ subst σ F))
                                     (wk-subst F) here)
  in  [tailσ] , neuTerm (proj₁ ([F] (⊢Δ ∙ ⊢F) [tailσ])) (var 0)
                        var0 (~-var var0)

-- Lift a valid substitution equality
liftSubstSEq : ∀ {l F σ σ′ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
             ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
             ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
             ([σ≡σ′] : Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ])
           → (Δ ∙ subst σ F) ⊩ˢ liftSubst σ ≡ liftSubst σ′ ∷ Γ ∙ F / [Γ] ∙″ [F]
                             / (⊢Δ ∙ escape (proj₁ ([F] ⊢Δ [σ])))
                             / liftSubstS {F = F} [Γ] ⊢Δ [F] [σ]
liftSubstSEq {F = F} {σ = σ} {σ′ = σ′} {Δ = Δ} [Γ] ⊢Δ [F] [σ] [σ≡σ′] =
  let ⊢F = escape (proj₁ ([F] ⊢Δ [σ]))
      [tailσ] = wk1SubstS {F = subst σ F} [Γ] ⊢Δ (escape (proj₁ ([F] ⊢Δ [σ]))) [σ]
      [tailσ≡σ′] = wk1SubstSEq [Γ] ⊢Δ (escape (proj₁ ([F] ⊢Δ [σ]))) [σ] [σ≡σ′]
      var0 = var (⊢Δ ∙ ⊢F) (PE.subst (λ x → 0 ∷ x ∈ (Δ ∙ subst σ F)) (wk-subst F) here)
  in  [tailσ≡σ′] , neuEqTerm (proj₁ ([F] (⊢Δ ∙ ⊢F) [tailσ])) (var 0) (var 0)
                         var0 var0 (~-var var0)

mutual
  -- Valid contexts are well-formed
  soundContext : ∀ {Γ} → ⊩ᵛ Γ → ⊢ Γ
  soundContext ε′ = ε
  soundContext (x ∙′ x₁) =
    soundContext x ∙ escape (irrelevance′ (subst-id _)
                                             (proj₁ (x₁ (soundContext x)
                                                        (idSubstS x))))

  -- From a valid context we can constuct a valid identity substitution
  idSubstS : ∀ {Γ} ([Γ] : ⊩ᵛ Γ) → Γ ⊩ˢ idSubst ∷ Γ / [Γ] / soundContext [Γ]
  idSubstS ε′ = tt
  idSubstS {Γ = Γ ∙ A} ([Γ] ∙′ [A]) =
    let ⊢Γ = soundContext [Γ]
        ⊢Γ∙A = soundContext ([Γ] ∙″ [A])
        ⊢Γ∙A′ = ⊢Γ ∙ escape (proj₁ ([A] ⊢Γ (idSubstS [Γ])))
        [A]′ = wk1SubstS {F = subst idSubst A} [Γ] ⊢Γ
                         (escape (proj₁ ([A] (soundContext [Γ])
                                                (idSubstS [Γ]))))
                         (idSubstS [Γ])
        [tailσ] = irrelevanceSubst′ (PE.cong (_∙_ Γ) (subst-id A))
                                    [Γ] [Γ] ⊢Γ∙A′ ⊢Γ∙A [A]′
        var0 = var ⊢Γ∙A (PE.subst (λ x → 0 ∷ x ∈ (Γ ∙ A))
                                  (wk-subst A)
                                  (PE.subst (λ x → 0 ∷ wk1 (subst idSubst A)
                                                     ∈ (Γ ∙ x))
                                            (subst-id A) here))
    in  [tailσ]
    ,   neuTerm (proj₁ ([A] ⊢Γ∙A [tailσ]))
                (var 0)
                var0 (~-var var0)

-- Reflexivity valid substitutions
reflSubst : ∀ {σ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
            ([σ] : Δ ⊩ˢ σ ∷ Γ / [Γ] / ⊢Δ)
          → Δ ⊩ˢ σ ≡ σ ∷ Γ / [Γ] / ⊢Δ / [σ]
reflSubst ε′ ⊢Δ [σ] = tt
reflSubst ([Γ] ∙′ x) ⊢Δ [σ] =
  reflSubst [Γ] ⊢Δ (proj₁ [σ]) , reflEqTerm (proj₁ (x ⊢Δ (proj₁ [σ]))) (proj₂ [σ])

-- Reflexivity of valid identity substitution
reflIdSubst : ∀ {Γ} ([Γ] : ⊩ᵛ Γ)
            → Γ ⊩ˢ idSubst ≡ idSubst ∷ Γ / [Γ] / soundContext [Γ] / idSubstS [Γ]
reflIdSubst [Γ] = reflSubst [Γ] (soundContext [Γ]) (idSubstS [Γ])

-- Symmetry of valid substitution
symS : ∀ {σ σ′ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
       ([σ]  : Δ ⊩ˢ σ  ∷ Γ / [Γ] / ⊢Δ)
       ([σ′] : Δ ⊩ˢ σ′ ∷ Γ / [Γ] / ⊢Δ)
     → Δ ⊩ˢ σ ≡ σ′ ∷ Γ / [Γ] / ⊢Δ / [σ]
     → Δ ⊩ˢ σ′ ≡ σ ∷ Γ / [Γ] / ⊢Δ / [σ′]
symS ε′ ⊢Δ [σ] [σ′] [σ≡σ′] = tt
symS ([Γ] ∙′ x) ⊢Δ [σ] [σ′] [σ≡σ′] =
  symS [Γ] ⊢Δ (proj₁ [σ]) (proj₁ [σ′]) (proj₁ [σ≡σ′])
  , let [σA]           = proj₁ (x ⊢Δ (proj₁ [σ]))
        [σ′A]          = proj₁ (x ⊢Δ (proj₁ [σ′]))
        [σA≡σ′A]       = (proj₂ (x ⊢Δ (proj₁ [σ]))) (proj₁ [σ′]) (proj₁ [σ≡σ′])
        [headσ′≡headσ] = symEqTerm [σA] (proj₂ [σ≡σ′])
    in  convEqTerm₁ [σA] [σ′A] [σA≡σ′A] [headσ′≡headσ]

-- Transitivity of valid substitution
transS : ∀ {σ σ′ σ″ Γ Δ} ([Γ] : ⊩ᵛ Γ) (⊢Δ : ⊢ Δ)
         ([σ]   : Δ ⊩ˢ σ   ∷ Γ / [Γ] / ⊢Δ)
         ([σ′]  : Δ ⊩ˢ σ′  ∷ Γ / [Γ] / ⊢Δ)
         ([σ″] : Δ ⊩ˢ σ″ ∷ Γ / [Γ] / ⊢Δ)
       → Δ ⊩ˢ σ  ≡ σ′  ∷ Γ / [Γ] / ⊢Δ / [σ]
       → Δ ⊩ˢ σ′ ≡ σ″ ∷ Γ / [Γ] / ⊢Δ / [σ′]
       → Δ ⊩ˢ σ  ≡ σ″ ∷ Γ / [Γ] / ⊢Δ / [σ]
transS ε′ ⊢Δ [σ] [σ′] [σ″] [σ≡σ′] [σ′≡σ″] = tt
transS ([Γ] ∙′ x) ⊢Δ [σ] [σ′] [σ″] [σ≡σ′] [σ′≡σ″] =
  transS [Γ] ⊢Δ (proj₁ [σ]) (proj₁ [σ′]) (proj₁ [σ″])
         (proj₁ [σ≡σ′]) (proj₁ [σ′≡σ″])
  , let [σA]   = proj₁ (x ⊢Δ (proj₁ [σ]))
        [σ′A]  = proj₁ (x ⊢Δ (proj₁ [σ′]))
        [σ″A] = proj₁ (x ⊢Δ (proj₁ [σ″]))
        [σ′≡σ″]′ = convEqTerm₂ [σA] [σ′A]
                                ((proj₂ (x ⊢Δ (proj₁ [σ]))) (proj₁ [σ′])
                                        (proj₁ [σ≡σ′])) (proj₂ [σ′≡σ″])
    in  transEqTerm [σA] (proj₂ [σ≡σ′]) [σ′≡σ″]′


{-# OPTIONS --with-K #-}

open import Axiom.UniquenessOfIdentityProofs.WithK using (uip)

open import Relation.Binary.PropositionalEquality hiding (Extensionality)
open ≡-Reasoning

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

open import Data.Vec using (Vec)

open import FLA.Axiom.Extensionality.Propositional
open import FLA.Algebra.Structures
open import FLA.Algebra.Properties.Field
open import FLA.Algebra.LinearMap
open import FLA.Algebra.LinearMap.Properties
open import FLA.Algebra.LinearAlgebra
open import FLA.Algebra.LinearAlgebra.Matrix

module FLA.Algebra.LinearAlgebra.Properties.Matrix where

private
  variable
    ℓ : Level
    A : Set ℓ
    m n p q : ℕ
    ⦃ F ⦄ : Field A

-------------------------------------------------------------------------------
--                   M without the inner product proof: Mat↓                   --
-------------------------------------------------------------------------------

private
  data Mat↓_∶_×_ (A : Set ℓ) ⦃ F : Field A ⦄ (m n : ℕ) : Set ℓ where
    ⟦_,_⟧ : (M : n ⊸ m ) → (Mᵀ : m ⊸ n ) → Mat↓ A ∶ m × n

  _·↓_ : ⦃ F : Field A ⦄ → Mat↓ A ∶ m × n → Vec A n → Vec A m
  ⟦ f , a ⟧ ·↓ x = f ·ˡᵐ x

  _ᵀ↓ : ⦃ F : Field A ⦄ → Mat↓ A ∶ m × n → Mat↓ A ∶ n × m
  ⟦ f , a ⟧ ᵀ↓ = ⟦ a , f ⟧

  infixr 20 _·↓_
  infixl 25 _ᵀ↓

  Mat→Mat↓ : ⦃ F : Field A ⦄ → Mat m × n → Mat↓ A ∶ m × n
  Mat→Mat↓ ⟦ M , Mᵀ , p ⟧ = ⟦ M , Mᵀ ⟧

  Mat↓→Mat : ⦃ F : Field A ⦄
        → (Mat↓ : Mat↓ A ∶ m × n)
        → (p : (x : Vec A m) → (y : Vec A n)
              → ⟨ x , Mat↓ ·↓ y ⟩ ≡ ⟨ y , Mat↓ ᵀ↓ ·↓ x ⟩ )
        → Mat m × n
  Mat↓→Mat ⟦ M , Mᵀ ⟧ p = ⟦ M , Mᵀ , p ⟧

  ⟨x,Ay⟩≡⟨y,Aᵀx⟩-UIP : {ℓ : Level} → {A : Set ℓ} → ⦃ F : Field A ⦄
                    → (C : n ⊸ m) → (Cᵀ : m ⊸ n)
                    → (p q : (x : Vec A m) (y : Vec A n)
                            → ⟨ x , C ·ˡᵐ y ⟩ ≡ ⟨ y , Cᵀ ·ˡᵐ x ⟩)
                    → p ≡ q
  ⟨x,Ay⟩≡⟨y,Aᵀx⟩-UIP {m} {n} {ℓ} {A} C Cᵀ p q =
    extensionality (λ x → extensionality (λ y → f C Cᵀ p q x y))
    where
      f : {m n : ℕ}
            → {A : Set ℓ}
            → ⦃ F : Field A ⦄
            → (C : n ⊸ m) → (Cᵀ : m ⊸ n)
            → (p q : (x : Vec A m) (y : Vec A n)
                    → ⟨ x , C ·ˡᵐ y ⟩ ≡ ⟨ y , Cᵀ ·ˡᵐ x ⟩)
            → (x : Vec A m) →  (y : Vec A n) → p x y ≡ q x y
      f C Cᵀ p q (x) (y) = uip (p x y) (q x y)

  Mat↓≡→Mat≡ : ⦃ F : Field A ⦄ → (C D : Mat m × n) → (Mat→Mat↓ C ≡ Mat→Mat↓ D) → C ≡ D
  Mat↓≡→Mat≡ ⟦ C , Cᵀ , p ⟧ ⟦ .C , .Cᵀ , q ⟧ refl rewrite
    ⟨x,Ay⟩≡⟨y,Aᵀx⟩-UIP C Cᵀ p q = refl

-------------------------------------------------------------------------------
--                                Proofs on M                                --
-------------------------------------------------------------------------------

module _ {ℓ : Level} {A : Set ℓ} ⦃ F : Field A ⦄ where
  open Field F

  ᵀᵀ : (B : Mat m × n) → B ᵀ ᵀ ≡ B
  ᵀᵀ B = Mat↓≡→Mat≡ (B ᵀ ᵀ) B (ᵀᵀ↓ B)
    where
      ᵀᵀ↓ : (B : Mat m × n) → Mat→Mat↓ (B ᵀ ᵀ) ≡ Mat→Mat↓ B
      ᵀᵀ↓ ⟦ M , Mᵀ , p ⟧ = refl

  ᵀ-distr-* : (L : Mat m × n) (R : Mat n × p)
            → (L *ᴹ R) ᵀ ≡ (R ᵀ *ᴹ L ᵀ)
  ᵀ-distr-* L R = Mat↓≡→Mat≡ ((L *ᴹ R) ᵀ) (R ᵀ *ᴹ L ᵀ) (ᵀ-distr-*↓ L R)
    where
      ᵀ-distr-*↓ : (L : Mat m × n) (R : Mat n × p)
                → Mat→Mat↓ ((L *ᴹ R) ᵀ) ≡ Mat→Mat↓ (R ᵀ *ᴹ L ᵀ)
      ᵀ-distr-*↓ ⟦ L , Lᵀ , p ⟧ ⟦ R , Rᵀ₁ , q ⟧ = refl

  ᵀ-distr-+ : (L R : Mat m × n) → (L +ᴹ R) ᵀ ≡ L ᵀ +ᴹ R ᵀ
  ᵀ-distr-+ L R = Mat↓≡→Mat≡ ((L +ᴹ R) ᵀ) (L ᵀ +ᴹ R ᵀ) (ᵀ-distr-+↓ L R)
    where
      ᵀ-distr-+↓ : (L R : Mat m × n)
                 → Mat→Mat↓ ((L +ᴹ R) ᵀ) ≡ Mat→Mat↓ (L ᵀ +ᴹ R ᵀ)
      ᵀ-distr-+↓ ⟦ L , Lᵀ , p ⟧ ⟦ R , Rᵀ , q ⟧ = refl

  *ᴹ-distr-+ᴹₗ : (X : Mat m × n) (Y Z : Mat n × p)
               → X *ᴹ (Y +ᴹ Z) ≡ X *ᴹ Y +ᴹ X *ᴹ Z
  *ᴹ-distr-+ᴹₗ X Y Z = Mat↓≡→Mat≡ (X *ᴹ (Y +ᴹ Z)) (X *ᴹ Y +ᴹ X *ᴹ Z)
                              (*ᴹ-distr-+ᴹ↓ₗ X Y Z)
    where
      *ᴹ-distr-+ᴹ↓ₗ : (X : Mat m × n) (Y Z : Mat n × p)
                    → Mat→Mat↓ (X *ᴹ (Y +ᴹ Z)) ≡ Mat→Mat↓ (X *ᴹ Y +ᴹ X *ᴹ Z)
      *ᴹ-distr-+ᴹ↓ₗ ⟦ X , Xᵀ , Xₚ ⟧ ⟦ Y , Yᵀ , Yₚ ⟧ ⟦ Z , Zᵀ , Zₚ ⟧ rewrite
          *ˡᵐ-distr-+ˡᵐₗ X Y Z
        | *ˡᵐ-distr-+ˡᵐᵣ Yᵀ Zᵀ Xᵀ
        = refl

  *ᴹ-distr-+ᴹᵣ : (X Y : Mat m × n) (Z : Mat n × p)
               → (X +ᴹ Y) *ᴹ Z ≡ X *ᴹ Z +ᴹ Y *ᴹ Z
  *ᴹ-distr-+ᴹᵣ X Y Z = Mat↓≡→Mat≡ ((X +ᴹ Y) *ᴹ Z) (X *ᴹ Z +ᴹ Y *ᴹ Z)
                              (*ᴹ-distr-+ᴹ↓ᵣ X Y Z)
    where
      *ᴹ-distr-+ᴹ↓ᵣ : (X Y : Mat m × n) (Z : Mat n × p)
                    → Mat→Mat↓ ((X +ᴹ Y) *ᴹ Z) ≡ Mat→Mat↓ (X *ᴹ Z +ᴹ Y *ᴹ Z)
      *ᴹ-distr-+ᴹ↓ᵣ ⟦ X , Xᵀ , Xₚ ⟧ ⟦ Y , Yᵀ , Yₚ ⟧ ⟦ Z , Zᵀ , Zₚ ⟧ rewrite
          *ˡᵐ-distr-+ˡᵐᵣ X Y Z
        | *ˡᵐ-distr-+ˡᵐₗ Zᵀ Xᵀ Yᵀ
        = refl


------------------------------------------------------------------------
-- Theorems relating the coinductive definition of the delay
-- monad to the alternative one and to another type
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --sized-types #-}

open import Prelude hiding (↑)

module Delay-monad.Alternative.Equivalence {a} {A : Type a} where

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

open import Bijection equality-with-J using (_↔_)
open import Equality.Decision-procedures equality-with-J
open import Function-universe equality-with-J hiding (_∘_)
open import H-level equality-with-J
open import Surjection equality-with-J using (_↠_)

open import Delay-monad as D hiding (Delay)
open import Delay-monad.Alternative
open import Delay-monad.Alternative.Properties
open import Delay-monad.Bisimilarity as Bisimilarity
  using ([_]_∼_; now; later; force)

private
  variable
    i : Size

-- Building blocks used in the theorems below.

module Delay⇔Delay where

  -- The function to₁ is basically π from Section 7.1 of
  -- "Quotienting the Delay Monad by Weak Bisimilarity" by Chapman,
  -- Uustalu and Veltri, but the function proj₁ ∘ from is different
  -- from their function ε, which is defined so that (with the
  -- terminology used here) ε (now x) (suc n) = nothing.

  to₁ : (ℕ → Maybe A) → D.Delay A i
  to₁ f = to′ (f 0)
    module To where
    to′ : Maybe A → D.Delay A i
    to′ (just x) = now x
    to′ nothing  =
      later λ { .force → to₁ (f ∘ suc) }

  to : Delay A → D.Delay A i
  to = to₁ ∘ proj₁

  from : D.Delay A ∞ → Delay A
  from = λ x → from₁ x , from₂ x
    where
    from₁ : D.Delay A ∞ → ℕ → Maybe A
    from₁ (now x)   _       = just x
    from₁ (later x) zero    = nothing
    from₁ (later x) (suc n) = from₁ (force x) n

    from₂ : ∀ x → Increasing (from₁ x)
    from₂ (now x)   _       = inj₁ refl
    from₂ (later x) zero    = proj₁ LE-nothing-contractible
    from₂ (later x) (suc n) = from₂ (force x) n

-- The two definitions of the delay monad are logically equivalent.

Delay⇔Delay : Delay A ⇔ D.Delay A ∞
Delay⇔Delay = record { to = Delay⇔Delay.to; from = Delay⇔Delay.from }

-- There is a split surjection from D.Delay A ∞ to Delay A.

Delay↠Delay : D.Delay A ∞ ↠ Delay A
Delay↠Delay = record
  { logical-equivalence = inverse Delay⇔Delay
  ; right-inverse-of    = from∘to
  }
  where
  open Delay⇔Delay

  from∘to : ∀ x → from (to x) ≡ x
  from∘to x = Σ-≡,≡→≡
    (⟨ext⟩ (proj₁∘from∘to x))
    (⟨ext⟩ λ n →

       subst Increasing
             (⟨ext⟩ (proj₁∘from∘to x))
             (proj₂ (from (to x)))
             n                          ≡⟨ sym $ push-subst-application (⟨ext⟩ (proj₁∘from∘to x)) (flip Increasing-at) ⟩

       subst (Increasing-at n)
             (⟨ext⟩ (proj₁∘from∘to x))
             (proj₂ (from (to x)) n)    ≡⟨ uncurry proj₂∘from∘to x n _ refl ⟩∎

       proj₂ x n                        ∎)

    where

    proj₁∘from∘to′ :
      (g : ℕ → Maybe A) →
      Increasing g →
      ∀ x → g 0 ≡ x → ∀ n →
      proj₁ (from (To.to′ g x)) n ≡ g n
    proj₁∘from∘to′ g g-inc (just x) g0↓x n       = sym $ ↓-upwards-closed₀ g-inc g0↓x n
    proj₁∘from∘to′ g g-inc nothing  g0↑  zero    = sym g0↑
    proj₁∘from∘to′ g g-inc nothing  g0↑  (suc n) =
        proj₁∘from∘to′ (g ∘ suc) (g-inc ∘ suc) _ refl n

    proj₁∘from∘to :
      (x : Delay A) →
      ∀ n → proj₁ (from (to x)) n ≡ proj₁ x n
    proj₁∘from∘to (g , g-inc) = proj₁∘from∘to′ g g-inc (g 0) refl

    ↓-lemma :
      (g : ℕ → Maybe A) →
      (g-increasing : Increasing g) →
      ∀ n x (g0↓x : g 0 ↓ x) p → p ≡ g-increasing 0 →

      subst (Increasing-at n)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-increasing g0↓x))
            (inj₁ refl)
        ≡
      g-increasing n

    ↓-lemma g _ _ x g0↓x (inj₂ (g0↑ , _)) _ =
      ⊥-elim $ ⊎.inj₁≢inj₂ (
        nothing  ≡⟨ sym g0↑ ⟩
        g 0      ≡⟨ g0↓x ⟩∎
        just x   ∎)

    ↓-lemma g g-inc zero x g0↓x (inj₁ g0≡g1) ≡g-inc0 =

      subst (Increasing-at zero)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x))
            (inj₁ refl)                                         ≡⟨ push-subst-inj₁
                                                                     {y≡z = ⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x)}
                                                                     (λ f → f 0 ≡ f 1) (λ f → f 0 ↑ × ¬ f 1 ↑) ⟩
      inj₁ (subst (λ f → f 0 ≡ f 1)
                  (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x))
                  refl)                                         ≡⟨ cong inj₁ lemma ⟩

      inj₁ g0≡g1                                                ≡⟨ ≡g-inc0 ⟩∎

      g-inc zero                                                ∎

      where
      eq    = ↓-upwards-closed₀ g-inc g0↓x
      lemma =

        subst (λ f → f 0 ≡ f 1) (⟨ext⟩ (sym ∘ eq)) refl      ≡⟨ subst-in-terms-of-trans-and-cong {x≡y = ⟨ext⟩ (sym ∘ eq)} ⟩

        trans (sym (cong (_$ 0) (⟨ext⟩ (sym ∘ eq))))
              (trans refl (cong (_$ 1) (⟨ext⟩ (sym ∘ eq))))  ≡⟨ cong (trans (sym (cong (_$ 0) (⟨ext⟩ (sym ∘ eq))))) $
                                                                  trans-reflˡ (cong (_$ 1) (⟨ext⟩ (sym ∘ eq))) ⟩
        trans (sym (cong (_$ 0) (⟨ext⟩ (sym ∘ eq))))
              (cong (_$ 1) (⟨ext⟩ (sym ∘ eq)))               ≡⟨ cong₂ (λ p q → trans (sym p) q) (cong-ext (sym ∘ eq)) (cong-ext (sym ∘ eq)) ⟩

        trans (sym $ sym $ eq 0) (sym $ eq 1)                ≡⟨ cong (λ eq′ → trans eq′ (sym $ eq 1)) $ sym-sym _ ⟩

        trans (eq 0) (sym $ eq 1)                            ≡⟨⟩

        trans g0↓x (sym $ ↓.step g-inc g0↓x (g-inc 0))       ≡⟨ cong (λ eq′ → trans g0↓x (sym $ ↓.step g-inc g0↓x eq′)) $ sym $ ≡g-inc0 ⟩

        trans g0↓x (sym $ ↓.step g-inc g0↓x (inj₁ g0≡g1))    ≡⟨⟩

        trans g0↓x (sym $ trans (sym g0≡g1) g0↓x)            ≡⟨ cong (trans g0↓x) $ sym-trans (sym g0≡g1) g0↓x ⟩

        trans g0↓x (trans (sym g0↓x) (sym (sym g0≡g1)))      ≡⟨ trans--[trans-sym] _ _ ⟩

        sym (sym g0≡g1)                                      ≡⟨ sym-sym _ ⟩∎

        g0≡g1                                                ∎

    ↓-lemma g g-inc (suc n) x g0↓x (inj₁ g0≡g1) ≡g-inc0 =

      subst (Increasing-at (suc n))
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x))
            (inj₁ refl)                                                   ≡⟨⟩

      subst (Increasing-at n ∘ (_∘ suc))
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x))
            (inj₁ refl)                                                   ≡⟨ subst-∘ (Increasing-at n) (_∘ suc)
                                                                                (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x)) ⟩
      subst (Increasing-at n)
            (cong (_∘ suc) (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x)))
            (inj₁ refl)                                                   ≡⟨ cong (λ eq → subst (Increasing-at n) eq _) $
                                                                               cong-pre-∘-ext (sym ∘ ↓-upwards-closed₀ g-inc g0↓x) ⟩
      subst (Increasing-at n)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ g-inc g0↓x ∘ suc))
            (inj₁ refl)                                                   ≡⟨⟩

      subst (Increasing-at n)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ (g-inc ∘ suc)
                            (↓.step g-inc g0↓x (g-inc 0))))
            (inj₁ refl)                                                   ≡⟨ cong (λ p → subst (Increasing-at n)
                                                                                (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ (g-inc ∘ suc)
                                                                                (↓.step g-inc g0↓x p)))
                                                                                (inj₁ refl))
                                                                                (sym ≡g-inc0) ⟩
      subst (Increasing-at n)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ (g-inc ∘ suc)
                            (↓.step g-inc g0↓x (inj₁ g0≡g1))))
            (inj₁ refl)                                                   ≡⟨⟩

      subst (Increasing-at n)
            (⟨ext⟩ (sym ∘ ↓-upwards-closed₀ (g-inc ∘ suc)
                            (trans (sym g0≡g1) g0↓x)))
            (inj₁ refl)                                                   ≡⟨ ↓-lemma (g ∘ suc) (g-inc ∘ suc) n _ _ _ refl ⟩∎

      g-inc (suc n)                                                       ∎

    proj₂∘from∘to :
      (g : ℕ → Maybe A) →
      (g-increasing : Increasing g) →
      ∀ n y (g0≡y : g 0 ≡ y) →

      subst (Increasing-at n)
            (⟨ext⟩ (proj₁∘from∘to′ g g-increasing y g0≡y))
            (proj₂ (from (To.to′ g y)) n)
        ≡
      g-increasing n

    proj₂∘from∘to g g-inc n (just y) g0↓y =
      ↓-lemma g g-inc n y g0↓y _ refl

    proj₂∘from∘to g g-inc zero nothing g0↑ =
      (                                   $⟨ mono₁ 0 LE-nothing-contractible ⟩
       Is-proposition (LE nothing (g 1))  ↝⟨ subst (λ x → Is-proposition (LE x (g 1))) (sym g0↑) ⟩□
       Is-proposition (LE (g 0) (g 1))    □) _ _

    proj₂∘from∘to g g-inc (suc n) nothing g0↑ =

      subst (Increasing-at (suc n))
            (⟨ext⟩ (proj₁∘from∘to′ g g-inc nothing g0↑))
            (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)                 ≡⟨⟩

      subst (Increasing-at n ∘ (_∘ suc))
            (⟨ext⟩ (proj₁∘from∘to′ g g-inc nothing g0↑))
            (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)                 ≡⟨ subst-∘ (Increasing-at n) (_∘ suc)
                                                                                (⟨ext⟩ (proj₁∘from∘to′ g g-inc nothing g0↑)) ⟩
      subst (Increasing-at n)
            (cong (_∘ suc) (⟨ext⟩ (proj₁∘from∘to′ g g-inc nothing g0↑)))
            (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)                 ≡⟨ cong (λ eq → subst (Increasing-at n) eq
                                                                                (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)) $
                                                                               cong-pre-∘-ext (proj₁∘from∘to′ g g-inc nothing g0↑) ⟩
      subst (Increasing-at n)
            (⟨ext⟩ (proj₁∘from∘to′ g g-inc nothing g0↑ ∘ suc))
            (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)                 ≡⟨⟩

      subst (Increasing-at n)
            (⟨ext⟩ (proj₁∘from∘to′ (g ∘ suc) (g-inc ∘ suc) _ refl))
            (proj₂ (from (to (g ∘ suc , g-inc ∘ suc))) n)                 ≡⟨ proj₂∘from∘to (g ∘ suc) (g-inc ∘ suc) n _ refl ⟩∎

      g-inc (suc n)                                                       ∎

-- The two definitions of the delay monad are isomorphic (assuming
-- extensionality).

Delay↔Delay : Bisimilarity.Extensionality a →
              Delay A ↔ D.Delay A ∞
Delay↔Delay delay-ext = record
  { surjection = record
    { logical-equivalence = Delay⇔Delay
    ; right-inverse-of    = delay-ext ∘ to∘from
    }
  ; left-inverse-of = _↠_.right-inverse-of Delay↠Delay
  }
  where
  open Delay⇔Delay

  to∘from : ∀ x → [ i ] to (from x) ∼ x
  to∘from (now x)   = now
  to∘from (later x) = later λ { .force → to∘from (force x) }

-- There is a split surjection from ℕ → Maybe A (without any
-- requirement that the sequences are increasing) to D.Delay A ∞
-- (assuming extensionality).
--
-- Chapman, Uustalu and Veltri prove this result in "Quotienting the
-- Delay Monad by Weak Bisimilarity".

→↠Delay-coinductive : Bisimilarity.Extensionality a →
                      (ℕ → Maybe A) ↠ D.Delay A ∞
→↠Delay-coinductive delay-ext = record
  { logical-equivalence = record
    { to   = Delay⇔Delay.to₁
    ; from = proj₁ ∘ Delay⇔Delay.from
    }
  ; right-inverse-of = _↔_.right-inverse-of (Delay↔Delay delay-ext)
  }

-- There is also a split surjection from ℕ → Maybe A to Delay A.

→↠Delay-function : (ℕ → Maybe A) ↠ Delay A
→↠Delay-function = record
  { logical-equivalence = record
    { to   = Delay⇔Delay.from ∘ Delay⇔Delay.to₁
    ; from = proj₁
    }
  ; right-inverse-of = _↠_.right-inverse-of Delay↠Delay
  }


open import SingleSorted.AlgebraicTheory

import SingleSorted.Interpretation as Interpretation
import SingleSorted.SyntacticCategory as SyntacticCategory
import SingleSorted.Substitution as Substitution

module SingleSorted.UniversalInterpretation
  {ℓt}
  {Σ : Signature}
  (T : Theory ℓt Σ) where

  open Theory T
  open Substitution T
  open SyntacticCategory T

  -- The universal interpretation in the syntactic category
  ℐ : Interpretation.Interpretation Σ cartesian-𝒮
  ℐ =
    record
     { interp-carrier = ctx-slot
     ; interp-pow = power-𝒮
     ; interp-oper = λ f var-var → tm-oper f (λ i → tm-var i)
     }

  open Interpretation.Interpretation ℐ

  -- A term is essentially interpreted by itself
  interp-term-self : ∀ {Γ} (t : Term Γ) y → Γ ⊢ interp-term t y ≈ t
  interp-term-self (tm-var x) _ = eq-refl
  interp-term-self (tm-oper f xs) y = eq-congr (λ i → interp-term-self (xs i) var-var)


-- Andreas, 2016-12-20, issue #2349

{-# OPTIONS --show-implicit #-}

open import Agda.Primitive
open import Agda.Builtin.Equality

data D {{a}} (A : Set a) : Set a where
  c : {{a : A}} (b : A) → D A

test : ∀ ℓ (A : Set ℓ) (x : D A) (a : A) → x ≡ c {{ℓ}} {{a = a}} a
test ℓ A x a = refl

-- ERROR:
-- x != c {{a}} of type D {{ℓ}} A
-- when checking that the expression refl has type
-- _≡_ {ℓ} {D {{ℓ}} A} x (c {{a}})

-- Note that (c {{a}}) is ill-typed here.


postulate
  A : Set

f : (B : Set) → B → A
f A a = a

-- Expected error:
-- A !=< A of type Set
-- (because one is a variable and one a defined identifier)
-- when checking that the expression a has type A


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

module sets.unit where

record ⊤ : Set where
  constructor tt

⊤-elim : (P : ⊤ → Set) → P tt → (x : ⊤) → P x
⊤-elim P ptt tt = ptt


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

open import HoTT
open import cohomology.Theory
open import groups.ExactSequence
open import groups.Exactness
open import groups.HomSequence
open import groups.KernelImageUniqueFactorization
import cw.cohomology.GridPtdMap as GPM

open import cw.CW

module cw.cohomology.HigherCohomologyGroups {i} (OT : OrdinaryTheory i)
  {n} (⊙skel : ⊙Skeleton {i} (S (S (S n)))) (ac : ⊙has-cells-with-choice 0 ⊙skel i) where

private
  Sn≤SSSn : S n ≤ S (S (S n))
  Sn≤SSSn = lteSR lteS

  n≤SSn : n ≤ S (S n)
  n≤SSn = lteSR lteS

  n≤SSSn : n ≤ S (S (S n))
  n≤SSSn = lteSR (lteSR lteS)

  ⊙skel₋₁ = ⊙cw-init ⊙skel
  ac₋₁ = ⊙init-has-cells-with-choice ⊙skel ac

  ⊙skel₋₂ = ⊙cw-init ⊙skel₋₁

  ⊙skel₋₃ = ⊙cw-take n≤SSSn ⊙skel
  ac₋₃ = ⊙take-has-cells-with-choice n≤SSSn ⊙skel ac

open OrdinaryTheory OT
open import cw.cohomology.HigherCoboundaryGrid OT
open import cw.cohomology.Descending OT
open import cw.cohomology.InnerGrid OT (ℕ-to-ℤ (S (S n)))
  (⊙cw-incl-last ⊙skel₋₂)
  (⊙cw-incl-last ⊙skel₋₁)
  (⊙cw-incl-last ⊙skel)
open import cw.cohomology.WedgeOfCells OT
open import cw.cohomology.HigherCoboundary OT ⊙skel
import cw.cohomology.HigherCoboundary OT ⊙skel₋₁ as LC
import cw.cohomology.GridLongExactSequence cohomology-theory as GLES

{-
              H          apex
   Coker ≃ C(X₂/X₀)<---C(X₃/X₀) ≃ C(X)
              ^           ^
              |           |
              |           |
           C(X₂/X₁)<---C(X₃/X₁) ≃ Ker
             WoC          G

    WoC := Wedges of Cells
-}

private
  C-apex : Group i
  C-apex = C (ℕ-to-ℤ (S (S n))) (⊙Cofiber (⊙cw-incl-tail n≤SSSn ⊙skel))

  open import cohomology.LongExactSequence cohomology-theory
    (ℕ-to-ℤ (S n)) (⊙cw-incl-tail n≤SSSn ⊙skel)

  C-apex-iso-C-cw : C-apex ≃ᴳ C (ℕ-to-ℤ (S (S n))) ⊙⟦ ⊙skel ⟧
  C-apex-iso-C-cw = Exact2.G-trivial-and-L-trivial-implies-H-iso-K
    (exact-seq-index 1 C-cofiber-exact-seq)
    (exact-seq-index 2 C-cofiber-exact-seq)
    (C-cw-at-higher ⊙skel₋₃ ltS ac₋₃)
    (C-cw-at-higher ⊙skel₋₃ (ltSR ltS) ac₋₃)

  G : Group i
  G = C (ℕ-to-ℤ (S (S n))) (⊙Cofiber (⊙cw-incl-tail Sn≤SSSn ⊙skel))

  G-iso-Ker : G ≃ᴳ Ker.grp cw-co∂-last
  G-iso-Ker = Ker-cw-co∂-last ⊙skel ac

  H : Group i
  H = C (ℕ-to-ℤ (S (S n))) (⊙Cofiber (⊙cw-incl-tail n≤SSn ⊙skel₋₁))

  Coker-iso-H : LC.CokerCo∂ ≃ᴳ H
  Coker-iso-H = Coker-cw-co∂-last ⊙skel₋₁ ac₋₁

  module GLES-top = GLES (ℕ-to-ℤ (S n))
    (⊙cw-incl-nth Sn≤SSSn ⊙skel) (⊙cw-incl-tail Sn≤SSSn ⊙skel)
  module GPM-top = GPM
    (⊙cw-incl-nth Sn≤SSSn ⊙skel) (⊙cw-incl-tail Sn≤SSSn ⊙skel)

  G-to-C-apex : G →ᴳ C-apex
  G-to-C-apex = C-fmap (ℕ-to-ℤ (S (S n))) GPM-top.Z/X-to-Z/Y

  abstract
    G-to-C-apex-is-surj : is-surjᴳ G-to-C-apex
    G-to-C-apex-is-surj = Exact.K-trivial-implies-φ-is-surj
      (exact-seq-index 2 GLES-top.C-grid-cofiber-exact-seq)
      (CXₙ/Xₙ₋₁->-is-trivial ⊙skel₋₂ ltS (⊙take-has-cells-with-choice Sn≤SSSn ⊙skel ac))

  module GLES-right = GLES (ℕ-to-ℤ (S n))
    (⊙cw-incl-tail n≤SSn ⊙skel₋₁) (⊙cw-incl-last ⊙skel)
  module GPM-right = GPM
    (⊙cw-incl-tail n≤SSn ⊙skel₋₁) (⊙cw-incl-last ⊙skel)

  C-apex-to-H : C-apex →ᴳ H
  C-apex-to-H = C-fmap (ℕ-to-ℤ (S (S n))) GPM-right.Y/X-to-Z/X

  abstract
    C-apex-to-H-is-inj : is-injᴳ C-apex-to-H
    C-apex-to-H-is-inj = Exact.G-trivial-implies-ψ-is-inj
      (exact-seq-index 2 GLES-right.C-grid-cofiber-exact-seq)
      (CXₙ/Xₙ₋₁-<-is-trivial ⊙skel ltS ac)

  C-WoC : Group i
  C-WoC = C (ℕ-to-ℤ (S (S n))) (⊙Cofiber (⊙cw-incl-last ⊙skel₋₁))

  G-to-C-WoC : G →ᴳ C-WoC
  G-to-C-WoC = C-fmap (ℕ-to-ℤ (S (S n)))
    (GPM.Y/X-to-Z/X (⊙cw-incl-last ⊙skel₋₁) (⊙cw-incl-last ⊙skel))

  C-WoC-to-H : C-WoC →ᴳ H
  C-WoC-to-H = C-fmap (ℕ-to-ℤ (S (S n)))
    (GPM.Z/X-to-Z/Y (⊙cw-incl-nth Sn≤SSSn ⊙skel) (⊙cw-incl-last ⊙skel₋₁))

open import groups.KernelImage cw-co∂-last LC.cw-co∂-last
  (CXₙ/Xₙ₋₁-is-abelian ⊙skel₋₁ (ℕ-to-ℤ (S (S n))))

C-cw-iso-ker/im : C (ℕ-to-ℤ (S (S n))) ⊙⟦ ⊙skel ⟧ ≃ᴳ Ker/Im
C-cw-iso-ker/im = lemma ∘eᴳ C-apex-iso-C-cw ⁻¹ᴳ where
  lemma : C-apex ≃ᴳ Ker/Im
  lemma = H-iso-Ker/Im
    cw-co∂-last LC.cw-co∂-last
    (CXₙ/Xₙ₋₁-is-abelian ⊙skel₋₁ (ℕ-to-ℤ (S (S n))))
    φ₁ φ₁-is-surj φ₂ φ₂-is-inj lemma-comm
    where
      φ₁ = G-to-C-apex ∘ᴳ GroupIso.g-hom G-iso-Ker
      abstract
        φ₁-is-surj : is-surjᴳ φ₁
        φ₁-is-surj = ∘-is-surj G-to-C-apex-is-surj (equiv-is-surj (GroupIso.g-is-equiv G-iso-Ker))

      φ₂ = GroupIso.g-hom Coker-iso-H ∘ᴳ C-apex-to-H
      abstract
        φ₂-is-inj : is-injᴳ φ₂
        φ₂-is-inj = ∘-is-inj (equiv-is-inj (GroupIso.g-is-equiv Coker-iso-H)) C-apex-to-H-is-inj

      abstract
        lemma-comm : ∀ g →
             GroupIso.g Coker-iso-H (GroupHom.f (C-apex-to-H ∘ᴳ G-to-C-apex) (GroupIso.g G-iso-Ker g))
          == q[ fst g ]
        lemma-comm g =
          GroupIso.g Coker-iso-H (GroupHom.f C-apex-to-H (GroupHom.f G-to-C-apex (GroupIso.g G-iso-Ker g)))
            =⟨ ap (GroupIso.g Coker-iso-H) (! (C-inner-grid-commutes □$ᴳ GroupIso.g G-iso-Ker g)) ⟩
          GroupIso.g Coker-iso-H (GroupHom.f C-WoC-to-H (GroupHom.f G-to-C-WoC (GroupIso.g G-iso-Ker g)))
            =⟨ ap (GroupIso.g Coker-iso-H ∘ GroupHom.f C-WoC-to-H ∘ fst) (GroupIso.f-g G-iso-Ker g) ⟩
          GroupIso.g Coker-iso-H (GroupHom.f C-WoC-to-H (fst g))
            =⟨ GroupIso.g-f Coker-iso-H q[ fst g ] ⟩
          q[ fst g ]
            =∎


module Data.Num.Surjection where

open import Data.Num.Core
open import Data.Num.Continuous

open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.Properties.Simple
open import Data.Nat.Properties.Extra

open import Data.Fin as Fin
    using (Fin; fromℕ≤; inject≤)
    renaming (zero to z; suc to s)
open import Data.Fin.Properties using (toℕ-fromℕ≤; bounded)
open import Data.Product
open import Data.Empty using (⊥)
open import Data.Unit using (⊤; tt)

open import Function
open import Function.Surjection hiding (_∘_)
open Surjective
open import Function.Equality using (_⟶_; _⟨$⟩_)
open import Relation.Nullary.Decidable
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Relation.Binary
open import Relation.Binary.PropositionalEquality

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

-- fromℕ that preserves equality

-- ℕ⟶Num : ∀ {b d o}
--     → (True (Continuous? b (suc d) o))
--     → (val : ℕ)
--     → (True (o ≤? val))
--     → Num b (suc d) o

-- ℕ⟶Num : ∀ b d o → True (Continuous? b (suc d) o) → setoid ℕ ⟶ setoid (Num b (suc d) o)
-- ℕ⟶Num b d o cont = record
--     { _⟨$⟩_ = fromℕ cont
--     ; cong = cong (fromℕ cont)
--     }
--
-- -- Surjective? : ∀ b d o → (True (Continuous? b (suc d) o)) → Dec (Surjective (ℕ⟶Num b d o))
-- -- Surjective? b d o with Continuous? b (suc d) o
-- -- Surjective? b d o | yes cont = {!   !}
-- -- Surjective? b d o | no ¬cont = {!   !}
--
-- Num⟶ℕ-Surjective : ∀ b d o → (cont : True (Continuous? b (suc d) o)) → Surjective (Num⟶ℕ b (suc d) o)
-- Num⟶ℕ-Surjective b d o cont = record
--     { from = ℕ⟶Num b d o cont
--     ; right-inverse-of = {! toℕ-fromℕ cont  !}
--     }

-- Surjective? : ∀ b d o → Dec (Surjective (Num⟶ℕ b d o))
-- Surjective? b d o with surjectionView b d o
-- Surjective? b d o | Surj cond = yes (record
--     { from = ℕ⟶Num b d o
--     ; right-inverse-of = toℕ-fromℕ {b} {d} {o} {SurjCond⇒IsSurj cond}
--     })
-- Surjective? b d o | NonSurj reason = no (NonSurjCond⇏Surjective reason)
--

-- Conditions of surjectiveness
-- data SurjCond : ℕ → ℕ → ℕ → Set where
--     WithZerosUnary : ∀ {  d}               → (d≥2 : d ≥ 2) → SurjCond 1 d 0
--     WithZeros      : ∀ {b d} (b≥2 : b ≥ 2) → (d≥b : d ≥ b) → SurjCond b d 0
--     Zeroless       : ∀ {b d} (b≥1 : b ≥ 1) → (d≥b : d ≥ b) → SurjCond b d 1
--
-- -- Conditions of non-surjectiveness
-- data NonSurjCond : ℕ → ℕ → ℕ → Set where
--     Base≡0             : ∀ {  d o}                 → NonSurjCond 0 d o
--     NoDigits           : ∀ {b   o}                 → NonSurjCond b 0 o
--     Offset≥2           : ∀ {b d o} → o ≥ 2         → NonSurjCond b d o
--     UnaryWithOnlyZeros :                              NonSurjCond 1 1 0
--     NotEnoughDigits    : ∀ {b d o} → d ≥ 1 → d ≱ b → NonSurjCond b d o
--
-- data SurjectionView : ℕ → ℕ → ℕ → Set where
--     Surj    : ∀ {b d o} →    SurjCond b d o → SurjectionView b d o
--     NonSurj : ∀ {b d o} → NonSurjCond b d o → SurjectionView b d o
--
--
-- surjectionView : (b d o : ℕ) → SurjectionView b d o
-- surjectionView 0             d             o             = NonSurj Base≡0
-- surjectionView (suc b)       0             o             = NonSurj NoDigits
-- surjectionView (suc b)       (suc d)       o             with suc b ≤? suc d
-- surjectionView 1             1             0             | yes p = NonSurj UnaryWithOnlyZeros
-- surjectionView 1             (suc (suc d)) 0             | yes p = Surj    (WithZerosUnary (s≤s (s≤s z≤n)))
-- surjectionView (suc (suc b)) (suc d)       0             | yes p = Surj    (WithZeros (s≤s (s≤s z≤n)) p)
-- surjectionView (suc b)       (suc d)       1             | yes p = Surj    (Zeroless (s≤s z≤n) p)
-- surjectionView (suc b)       (suc d)       (suc (suc o)) | yes p = NonSurj (Offset≥2 (s≤s (s≤s z≤n)))
-- surjectionView (suc b)       (suc d)       o             | no ¬p = NonSurj (NotEnoughDigits (s≤s z≤n) ¬p)
--
-- IsSurjective : ℕ → ℕ → ℕ → Set
-- IsSurjective b d o with surjectionView b d o
-- IsSurjective b d o | Surj    _ = ⊤
-- IsSurjective b d o | NonSurj _ = ⊥
--
-- SurjCond⇒b≥1 : ∀ {b d o} → SurjCond b d o → b ≥ 1
-- SurjCond⇒b≥1 (WithZerosUnary d≥2)      = s≤s z≤n
-- SurjCond⇒b≥1 (WithZeros (s≤s b≥1) d≥b) = s≤s z≤n
-- SurjCond⇒b≥1 (Zeroless b≥1 d≥b)        = b≥1
--
-- SurjCond⇒d≥b : ∀ {b d o} → SurjCond b d o → d ≥ b
-- SurjCond⇒d≥b (WithZerosUnary (s≤s d≥1)) = s≤s z≤n
-- SurjCond⇒d≥b (WithZeros b≥2 d≥b)        = d≥b
-- SurjCond⇒d≥b (Zeroless b≥1 d≥b)         = d≥b
--
-- SurjCond⇒IsSurj : ∀ {b d o} → SurjCond b d o → IsSurjective b d o
-- SurjCond⇒IsSurj {b} {d} {o} cond with surjectionView b d o
-- SurjCond⇒IsSurj cond | Surj x = tt
-- SurjCond⇒IsSurj (WithZeros () d≥b)        | NonSurj Base≡0
-- SurjCond⇒IsSurj (Zeroless () d≥b)         | NonSurj Base≡0
-- SurjCond⇒IsSurj (WithZerosUnary ())       | NonSurj NoDigits
-- SurjCond⇒IsSurj (WithZeros () z≤n)        | NonSurj NoDigits
-- SurjCond⇒IsSurj (Zeroless () z≤n)         | NonSurj NoDigits
-- SurjCond⇒IsSurj (WithZerosUnary _)        | NonSurj (Offset≥2 ())
-- SurjCond⇒IsSurj (WithZeros _ _)           | NonSurj (Offset≥2 ())
-- SurjCond⇒IsSurj (Zeroless _ _)            | NonSurj (Offset≥2 (s≤s ()))
-- SurjCond⇒IsSurj (WithZerosUnary (s≤s ())) | NonSurj UnaryWithOnlyZeros
-- SurjCond⇒IsSurj (WithZeros (s≤s ()) _)    | NonSurj UnaryWithOnlyZeros
-- SurjCond⇒IsSurj (WithZerosUnary _)        | NonSurj (NotEnoughDigits d≥1 d≱1) = contradiction d≥1 d≱1
-- SurjCond⇒IsSurj (WithZeros _ d≥b)         | NonSurj (NotEnoughDigits _ d≱b) = contradiction d≥b d≱b
-- SurjCond⇒IsSurj (Zeroless _ d≥b)          | NonSurj (NotEnoughDigits _ d≱b) = contradiction d≥b d≱b
--
-- ------------------------------------------------------------------------
-- -- Operations on Num (which does not necessary needs to be Surj)
-- ------------------------------------------------------------------------
--
-- starting-digit : ∀ {b d o} → SurjCond b d o → Digit d
-- starting-digit (WithZerosUnary d≥2) = fromℕ≤ {1} d≥2
-- starting-digit (WithZeros b≥2 d≥b) = fromℕ≤ {1} (≤-trans b≥2 d≥b)
-- starting-digit (Zeroless b≥1 d≥b) = fromℕ≤ {0} (≤-trans b≥1 d≥b)

-- 1+ : ∀ {b d o} → Num b d o → Num b d o
-- 1+ {b} {d} {o} xs with surjectionView b d o
-- 1+ ∙        | Surj cond = starting-digit cond ∷ ∙
-- 1+ (x ∷ xs) | Surj cond with greatest x
-- 1+ (x ∷ xs) | Surj cond | yes p = digit+1-b-legacy x (SurjCond⇒b≥1 cond) p ∷ 1+ xs -- carry
-- 1+ (x ∷ xs) | Surj cond | no ¬p = digit+1   x ¬p                     ∷    xs
-- 1+ ∙        | NonSurj reason = ∙
-- 1+ (x ∷ xs) | NonSurj reason = xs
--
--
-- n+ : ∀ {b d o} → ℕ → Num b d o → Num b d o
-- n+ zero xs = xs
-- n+ (suc n) xs = 1+ (n+ n xs)
--
-- fromℕ : ∀ {b d o} → ℕ → Num b d o
-- fromℕ {b} {d} {o} n with surjectionView b d o
-- fromℕ n |    Surj x = n+ n ∙
-- fromℕ n | NonSurj x = ∙
--
--
-- -- fromℕ that preserves equality
-- ℕ⟶Num : ∀ b d o → setoid ℕ ⟶ setoid (Num b d o)
-- ℕ⟶Num b d o = record
--     { _⟨$⟩_ = fromℕ
--     ; cong = cong fromℕ
--     }
--
-- toℕ-digit+1-b : ∀ {d b} (x : Digit d)
--     → (b≥1 : b ≥ 1) → (p : suc (Fin.toℕ x) ≡ d)     -- required props
--     → Fin.toℕ (digit+1-b-legacy x b≥1 p) ≡ suc (Fin.toℕ x) ∸ b
-- toℕ-digit+1-b {d} {b} x b≥1 p = toℕ-fromℕ≤ $ start
--         suc (suc (Fin.toℕ x) ∸ b)
--     ≤⟨ s≤s (∸-mono ≤-refl b≥1) ⟩
--         suc (Fin.toℕ x)
--     ≈⟨ p ⟩
--         d
--     □
--
-- ------------------------------------------------------------------------
-- -- toℕ-1+ : toℕ (1+ xs) ≡ suc (toℕ xs)
-- ------------------------------------------------------------------------
--
-- toℕ-1+-x∷xs-greatest-lemma : ∀ {b d o}
--     → (x : Digit d) → (xs : Num b d o)
--     → (cond : SurjCond b d o)
--     → (p : suc (Fin.toℕ x) ≡ d)
--     → (toℕ-1+-xs : toℕ (1+ xs) ≡ suc (toℕ xs))
--     → toℕ (digit+1-b-legacy x (SurjCond⇒b≥1 cond) p ∷ 1+ xs) ≡ suc (toℕ (x ∷ xs))
-- toℕ-1+-x∷xs-greatest-lemma {b} {d} {o} x xs cond p toℕ-1+-xs =
--     begin
--         toℕ (digit+1-b-legacy x (SurjCond⇒b≥1 cond) p ∷ 1+ xs)
--     -- toℕ-fromℕ≤ : toℕ (fromℕ≤ m<n) ≡ m
--     ≡⟨ cong (λ w → o + w + toℕ (1+ xs) * b) (toℕ-fromℕ≤ ((digit+1-b-legacy-lemma x (SurjCond⇒b≥1 cond) p))) ⟩
--         o + (suc (Fin.toℕ x) ∸ b) + toℕ (1+ xs) * b
--     -- induction hypothesis
--     ≡⟨ cong (λ w → o + (suc (Fin.toℕ x) ∸ b) + w * b) toℕ-1+-xs ⟩
--         o + (suc (Fin.toℕ x) ∸ b) + (b + toℕ xs * b)
--     ≡⟨ +-assoc o (suc (Fin.toℕ x) ∸ b) (b + toℕ xs * b) ⟩
--         o + (suc (Fin.toℕ x) ∸ b + (b + toℕ xs * b))
--     ≡⟨ cong (λ w → o + w) (sym (+-assoc (suc (Fin.toℕ x) ∸ b) b (toℕ xs * b))) ⟩
--         o + (suc (Fin.toℕ x) ∸ b + b + toℕ xs * b)
--     ≡⟨ cong (λ w → o + (w + toℕ xs * b)) (+-comm (suc (Fin.toℕ x) ∸ b) b) ⟩
--         o + (b + (suc (Fin.toℕ x) ∸ b) + toℕ xs * b)
--     -- m+n∸m≡n : m + (n ∸ m) ≡ n
--     ≡⟨ cong (λ w → o + (w + toℕ xs * b)) (m+n∸m≡n (
--             start
--                 b
--             ≤⟨ SurjCond⇒d≥b cond ⟩
--                 d
--             ≈⟨ sym p ⟩
--                 suc (Fin.toℕ x)
--             □)) ⟩
--         o + suc (Fin.toℕ x + toℕ xs * b)
--     ≡⟨ +-suc o (Fin.toℕ x + toℕ xs * b) ⟩
--         suc (o + (Fin.toℕ x + toℕ xs * b))
--     ≡⟨ cong suc (sym (+-assoc o (Fin.toℕ x) (toℕ xs * b))) ⟩
--         suc (o + Fin.toℕ x + toℕ xs * b)
--     ∎
--
-- toℕ-1+-x∷xs-not-greatest-lemma : ∀ {b d o}
--     → (x : Digit d) → (xs : Num b d o)
--     → (¬p : suc (Fin.toℕ x) ≢ d)
--     → toℕ (digit+1 x ¬p ∷ xs) ≡ suc (toℕ (x ∷ xs))
-- toℕ-1+-x∷xs-not-greatest-lemma {b} {d} {o} x xs ¬p =
--     begin
--         o + Fin.toℕ (fromℕ≤ (≤∧≢⇒< (bounded x) ¬p)) + toℕ xs * b
--     -- toℕ-fromℕ≤
--     ≡⟨ cong (λ w → o + w + toℕ xs * b) (toℕ-fromℕ≤ (≤∧≢⇒< (bounded x) ¬p)) ⟩
--         o + suc (Fin.toℕ x) + toℕ xs * b
--     ≡⟨ +-assoc o (suc (Fin.toℕ x)) (toℕ xs * b) ⟩
--         o + suc (Fin.toℕ x + toℕ xs * b)
--     ≡⟨ +-suc o (Fin.toℕ x + toℕ xs * b) ⟩
--         suc (o + (Fin.toℕ x + toℕ xs * b))
--     ≡⟨ cong suc (sym (+-assoc o (Fin.toℕ x) (toℕ xs * b))) ⟩
--         suc (o + Fin.toℕ x + toℕ xs * b)
--     ∎
--
-- toℕ-1+ : ∀ {b d o}
--     → {isSurj : IsSurjective b d o}
--     → (xs : Num b d o)
--     → toℕ (1+ xs) ≡ suc (toℕ xs)
-- toℕ-1+ {b} {d} {o} xs with surjectionView b d o
-- toℕ-1+ {1} {d} {0} ∙ | Surj (WithZerosUnary d≥2) =
--     begin
--         Fin.toℕ (fromℕ≤ d≥2) + zero
--     ≡⟨ +-right-identity (Fin.toℕ (fromℕ≤ d≥2)) ⟩
--         Fin.toℕ (fromℕ≤ d≥2)
--     ≡⟨ toℕ-fromℕ≤ d≥2 ⟩
--         suc zero
--     ∎
-- toℕ-1+ {b} {d} {0} ∙ | Surj (WithZeros b≥2 d≥b) =
--     begin
--         Fin.toℕ (fromℕ≤ (≤-trans b≥2 d≥b)) + zero * b
--     ≡⟨ +-right-identity (Fin.toℕ (fromℕ≤ (≤-trans b≥2 d≥b))) ⟩
--         Fin.toℕ (fromℕ≤ (≤-trans b≥2 d≥b))
--     ≡⟨ toℕ-fromℕ≤ (≤-trans b≥2 d≥b) ⟩
--         suc zero
--     ∎
-- toℕ-1+ {b} {d} {_} ∙ | Surj (Zeroless b≥1 d≥b) =
--     begin
--         suc (Fin.toℕ (fromℕ≤ (≤-trans b≥1 d≥b)) + zero)
--     ≡⟨ +-right-identity (suc (Fin.toℕ (fromℕ≤ (≤-trans b≥1 d≥b)))) ⟩
--         suc (Fin.toℕ (fromℕ≤ (≤-trans b≥1 d≥b)))
--     ≡⟨ cong suc (toℕ-fromℕ≤ (≤-trans b≥1 d≥b)) ⟩
--         suc zero
--     ∎
-- toℕ-1+ {b} {d} {o} (x ∷ xs) | Surj condition with greatest x
-- toℕ-1+ {b} {d} {o} (x ∷ xs) | Surj condition | yes p = toℕ-1+-x∷xs-greatest-lemma x xs condition p (toℕ-1+ {isSurj = SurjCond⇒IsSurj condition} xs)
-- toℕ-1+ {b} {d} {o} (x ∷ xs) | Surj condition | no ¬p = toℕ-1+-x∷xs-not-greatest-lemma x xs ¬p
-- toℕ-1+ {isSurj = ()} xs | NonSurj reason
--
-- ------------------------------------------------------------------------
-- -- toℕ-n+ : toℕ (n+ n xs) ≡ n + (toℕ xs)
-- ------------------------------------------------------------------------
--
-- toℕ-n+ : ∀ {b d o}
--     → {isSurj : IsSurjective b d o}
--     → (n : ℕ)
--     → (xs : Num b d o)
--     → toℕ (n+ n xs) ≡ n + (toℕ xs)
-- toℕ-n+ {b} {d} {o} n xs with surjectionView b d o
-- toℕ-n+ zero xs | Surj cond = refl
-- toℕ-n+ (suc n) xs | Surj cond =
--     begin
--         toℕ (n+ (suc n) xs)
--     ≡⟨ refl ⟩
--         toℕ (1+ (n+ n xs))
--     ≡⟨ toℕ-1+ {isSurj = SurjCond⇒IsSurj cond} (n+ n xs) ⟩
--         suc (toℕ (n+ n xs))
--     ≡⟨ cong suc (toℕ-n+ {isSurj = SurjCond⇒IsSurj cond} n xs) ⟩
--         suc (n + toℕ xs)
--     ∎
-- toℕ-n+ {isSurj = ()} n xs | NonSurj reason
--
-- ------------------------------------------------------------------------
-- -- toℕ-fromℕ : toℕ (fromℕ n) ≡ n
-- ------------------------------------------------------------------------
--
-- toℕ-fromℕ : ∀ {b d o}
--     → {isSurjective : IsSurjective b d o}
--     → (n : ℕ)
--     → toℕ (fromℕ {b} {d} {o} n) ≡ n
-- toℕ-fromℕ {b} {d} {o} n       with surjectionView b d o
-- toℕ-fromℕ             zero    | Surj (WithZerosUnary d≥2) = refl
-- toℕ-fromℕ {_} {d} {_} (suc n) | Surj (WithZerosUnary d≥2) =
--     begin
--         toℕ (1+ (n+ {1} {d} {0} n ∙))
--     ≡⟨ toℕ-1+ {1} {d} {0} {SurjCond⇒IsSurj (WithZerosUnary d≥2)} (n+ n ∙) ⟩
--         suc (toℕ (n+ n ∙))
--     ≡⟨ cong suc (toℕ-n+ {1} {d} {0} {SurjCond⇒IsSurj (WithZerosUnary d≥2)} n ∙) ⟩
--         suc (n + zero)
--     ≡⟨ cong suc (+-right-identity n) ⟩
--         suc n
--     ∎
-- toℕ-fromℕ             zero    | Surj (WithZeros b≥2 d≥b) = refl
-- toℕ-fromℕ {b} {d} {_} (suc n) | Surj (WithZeros b≥2 d≥b) =
--     begin
--         toℕ (1+ (n+ {b} {d} {0} n ∙))
--     ≡⟨ toℕ-1+ {b} {d} {0} {SurjCond⇒IsSurj (WithZeros b≥2 d≥b)} (n+ n ∙) ⟩
--         suc (toℕ (n+ n ∙))
--     ≡⟨ cong suc (toℕ-n+ {b} {d} {0} {SurjCond⇒IsSurj (WithZeros b≥2 d≥b)} n ∙) ⟩
--         suc (n + zero)
--     ≡⟨ cong suc (+-right-identity n) ⟩
--         suc n
--     ∎
-- toℕ-fromℕ {b} {d} {_} zero    | Surj (Zeroless b≥1 d≥b) = refl
-- toℕ-fromℕ {b} {d} {_} (suc n) | Surj (Zeroless b≥1 d≥b) =
--     begin
--         toℕ (1+ (n+ {b} {d} {1} n ∙))
--     ≡⟨ toℕ-1+ {b} {d} {1} {SurjCond⇒IsSurj (Zeroless b≥1 d≥b)} (n+ n ∙) ⟩
--         suc (toℕ (n+ n ∙))
--     ≡⟨ cong suc (toℕ-n+ {b} {d} {1} {SurjCond⇒IsSurj (Zeroless b≥1 d≥b)} n ∙) ⟩
--         suc (n + zero)
--     ≡⟨ cong suc (+-right-identity n) ⟩
--         suc n
--     ∎
-- toℕ-fromℕ {isSurjective = ()} n | NonSurj x
--
--
-- ------------------------------------------------------------------------
-- -- Lemmata for proving Spurious cases not surjective
-- ------------------------------------------------------------------------
--
-- NonSurjCond-Base≡0 : ∀ {d o} → (xs : Num 0 d o) → toℕ xs ≢ suc (o + d)
-- NonSurjCond-Base≡0 {d} {o} ∙        ()
-- NonSurjCond-Base≡0 {d} {o} (x ∷ xs) p = contradiction p (<⇒≢ ⟦x∷xs⟧<1+o+d)
--     where
--         ⟦x∷xs⟧<1+o+d : o + Fin.toℕ x + toℕ xs * 0 < suc (o + d)
--         ⟦x∷xs⟧<1+o+d = s≤s $
--             start
--                 o + Fin.toℕ x + toℕ xs * zero
--             ≈⟨ cong (λ w → o + Fin.toℕ x + w) (*-right-zero (toℕ xs)) ⟩
--                 o + Fin.toℕ x + zero
--             ≈⟨ +-right-identity (o + Fin.toℕ x) ⟩
--                 o + Fin.toℕ x
--             ≤⟨ n+-mono o (
--                 start
--                     Fin.toℕ x
--                 ≤⟨ n≤1+n (Fin.toℕ x) ⟩
--                     suc (Fin.toℕ x)
--                 ≤⟨ bounded x ⟩
--                     d
--                 □
--             )⟩
--                 o + d
--             □
--
-- NonSurjCond-NoDigits : ∀ {b o} → (xs : Num b 0 o) → toℕ xs ≢ 1
-- NonSurjCond-NoDigits ∙         ()
-- NonSurjCond-NoDigits (() ∷ xs)
--
-- NonSurjCond-Offset≥2 : ∀ {b d o} → o ≥ 2 → (xs : Num b d o) → toℕ xs ≢ 1
-- NonSurjCond-Offset≥2                   o≥2      ∙        ()
-- NonSurjCond-Offset≥2 {o = 0}           ()       (x ∷ xs) p
-- NonSurjCond-Offset≥2 {o = 1}           (s≤s ()) (x ∷ xs) p
-- NonSurjCond-Offset≥2 {o = suc (suc o)} o≥2      (x ∷ xs) ()
--
-- NonSurjCond-UnaryWithOnlyZeros : (xs : Num 1 1 0) → toℕ xs ≢ 1
-- NonSurjCond-UnaryWithOnlyZeros ∙ ()
-- NonSurjCond-UnaryWithOnlyZeros (z ∷ xs) p = contradiction (
--     begin
--         toℕ xs
--     ≡⟨ sym (*-right-identity (toℕ xs)) ⟩
--         toℕ xs * 1
--     ≡⟨ p ⟩
--         suc zero
--     ∎) (NonSurjCond-UnaryWithOnlyZeros xs)
-- NonSurjCond-UnaryWithOnlyZeros (s () ∷ xs)
--
-- NonSurjCond-NotEnoughDigits : ∀ {b d o} → d ≥ 1 → b ≰ d → (xs : Num b d o) → toℕ xs ≢ o + d
-- NonSurjCond-NotEnoughDigits {_} {_} {o} d≥1 b≰d ∙        = <⇒≢ (≤-steps o d≥1)
-- NonSurjCond-NotEnoughDigits {b} {d} {o} d≥1 b≰d (x ∷ xs) p with toℕ xs ≤? 0
-- NonSurjCond-NotEnoughDigits {b} {d} {o} d≥1 b≰d (x ∷ xs) p | yes q =
--     contradiction p (<⇒≢ ⟦x∷xs⟧>o+d)
--     where
--         ⟦xs⟧≡0 : toℕ xs ≡ 0
--         ⟦xs⟧≡0 = ≤0⇒≡0 (toℕ xs) q
--         ⟦x∷xs⟧>o+d : o + Fin.toℕ x + toℕ xs * b < o + d
--         ⟦x∷xs⟧>o+d = start
--                 suc (o + Fin.toℕ x + toℕ xs * b)
--             ≈⟨ begin
--                     suc (o + Fin.toℕ x + toℕ xs * b)
--                 ≡⟨ cong (λ w → suc (o + Fin.toℕ x + w * b)) ⟦xs⟧≡0 ⟩
--                     suc (o + Fin.toℕ x + zero)
--                 ≡⟨ +-right-identity (suc (o + Fin.toℕ x)) ⟩
--                     suc (o + Fin.toℕ x)
--                 ≡⟨ sym (+-suc o (Fin.toℕ x)) ⟩
--                     o + suc (Fin.toℕ x)
--                 ∎ ⟩
--                 o + suc (Fin.toℕ x)
--             ≤⟨ n+-mono o (bounded x) ⟩
--                 o + d
--             □
--
-- NonSurjCond-NotEnoughDigits {b} {d} {o} d≥1 b≰d (x ∷ xs) p | no ¬q =
--     contradiction p (>⇒≢ ⟦x∷xs⟧>o+d)
--     where
--         ⟦x∷xs⟧>o+d : o + Fin.toℕ x + toℕ xs * b > o + d
--         ⟦x∷xs⟧>o+d = start
--                 suc (o + d)
--             ≈⟨ sym (+-suc o d) ⟩
--                 o + suc d
--             ≤⟨ n+-mono o (
--                 start
--                     suc d
--                 ≤⟨ ≰⇒> b≰d ⟩
--                     b
--                 ≈⟨ sym (*-left-identity b) ⟩
--                     1 * b
--                 ≤⟨ _*-mono_ {1} {toℕ xs} {b} {b} (≰⇒> ¬q) ≤-refl ⟩
--                     toℕ xs * b
--                 ≤⟨ n≤m+n (Fin.toℕ x) (toℕ xs * b) ⟩
--                     Fin.toℕ x + toℕ xs * b
--                 □
--             ) ⟩
--                 o + (Fin.toℕ x + toℕ xs * b)
--             ≈⟨ sym (+-assoc o (Fin.toℕ x) (toℕ xs * b)) ⟩
--                 o + Fin.toℕ x + toℕ xs * b
--             □
--
-- NonSurjCond⇏Surjective : ∀ {b} {d} {o} → NonSurjCond b d o → ¬ (Surjective (Num⟶ℕ b d o))
-- NonSurjCond⇏Surjective {_} {d} {o} Base≡0  claim =
--     NonSurjCond-Base≡0
--         (from             claim ⟨$⟩ suc o + d)
--         (right-inverse-of claim   (suc (o + d)))
-- NonSurjCond⇏Surjective NoDigits claim =
--     NonSurjCond-NoDigits
--         (from             claim ⟨$⟩ 1)
--         (right-inverse-of claim     1)
-- NonSurjCond⇏Surjective (Offset≥2 p) claim =
--     NonSurjCond-Offset≥2 p
--         (from             claim ⟨$⟩ 1)
--         (right-inverse-of claim     1)
-- NonSurjCond⇏Surjective UnaryWithOnlyZeros claim =
--     NonSurjCond-UnaryWithOnlyZeros
--         (from             claim ⟨$⟩ 1)
--         (right-inverse-of claim     1)
-- NonSurjCond⇏Surjective {_} {d} {o} (NotEnoughDigits p q) claim =
--     NonSurjCond-NotEnoughDigits p q
--         (from             claim ⟨$⟩ o + d)
--         (right-inverse-of claim    (o + d))
--
-- SurjCond⇒Surjective : ∀ {b} {d} {o} → SurjCond b d o → Surjective (Num⟶ℕ b d o)
-- SurjCond⇒Surjective {b} {d} {o} cond = record
--     { from = ℕ⟶Num b d o
--     ; right-inverse-of = toℕ-fromℕ {b} {d} {o} {SurjCond⇒IsSurj cond}
--     }
--
-- Surjective? : ∀ b d o → Dec (Surjective (Num⟶ℕ b d o))
-- Surjective? b d o with surjectionView b d o
-- Surjective? b d o | Surj cond = yes (record
--     { from = ℕ⟶Num b d o
--     ; right-inverse-of = toℕ-fromℕ {b} {d} {o} {SurjCond⇒IsSurj cond}
--     })
-- Surjective? b d o | NonSurj reason = no (NonSurjCond⇏Surjective reason)
--
-- ------------------------------------------------------------------------
-- --
-- ------------------------------------------------------------------------
--
-- -- Surjective⇒SurjCond {b} {d} {o} surj = {!   !}
-- Surjective⇒SurjCond : ∀ {b} {d} {o}
--     → Surjective (Num⟶ℕ b d o)
--     → SurjCond b d o
-- Surjective⇒SurjCond {b} {d} {o} surj with surjectionView b d o
-- Surjective⇒SurjCond surj | Surj condition = condition
-- Surjective⇒SurjCond surj | NonSurj reason = contradiction surj (NonSurjCond⇏Surjective reason)
--
-- Surjective⇒IsSurj : ∀ {b} {d} {o} → Surjective (Num⟶ℕ b d o) → IsSurjective b d o
-- Surjective⇒IsSurj = SurjCond⇒IsSurj ∘ Surjective⇒SurjCond
--
-- Surjective⇒b≥1 : ∀ {b} {d} {o} → Surjective (Num⟶ℕ b d o) → b ≥ 1
-- Surjective⇒b≥1 = SurjCond⇒b≥1 ∘ Surjective⇒SurjCond
--
-- Surjective⇒d≥b : ∀ {b} {d} {o} → Surjective (Num⟶ℕ b d o) → b ≤ d
-- Surjective⇒d≥b = SurjCond⇒d≥b ∘ Surjective⇒SurjCond


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

module Math.Combinatorics.Function.Properties.Lemma where

open import Data.Unit using (tt)
open import Data.Product
open import Data.Sum
open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.DivMod
open import Data.Nat.Solver using (module +-*-Solver)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Function
open import Algebra.FunctionProperties

open ≤-Reasoning

1≤n⇒n≢0 : ∀ {n} → 1 ≤ n → n ≢ 0
1≤n⇒n≢0 .{suc _} (s≤s z≤n) ()

-- TODO: use m<n⇒0<n∸m
m<n⇒n∸m≢0 : ∀ {m n} → m < n → n ∸ m ≢ 0
m<n⇒n∸m≢0 {m} {n} m<n n∸m≡0 = (λ x → x (sym n∸m≡0)) $ <⇒≢ $ +-cancelʳ-< 0 (n ∸ m) $ begin-strict
  m           <⟨ m<n ⟩
  n           ≡⟨ sym $ m∸n+n≡m (<⇒≤ m<n) ⟩
  (n ∸ m) + m ∎

≤⇒≡∨< : ∀ {m n} → m ≤ n → (m ≡ n) ⊎ (m < n)
≤⇒≡∨< {m} {n} m≤n with m ≟ n
... | yes m≡n = inj₁ m≡n
... | no  m≢n = inj₂ (≤∧≢⇒< m≤n m≢n)

^-monoˡ-≤ : ∀ n → (_^ n) Preserves _≤_ ⟶ _≤_
^-monoˡ-≤ 0       {m} {o} m≤o = ≤-refl
^-monoˡ-≤ (suc n) {m} {o} m≤o = begin
  m ^ suc n ≡⟨⟩
  m * m ^ n ≤⟨ *-mono-≤ m≤o (^-monoˡ-≤ n m≤o) ⟩
  o * o ^ n ≡⟨⟩
  o ^ suc n ∎

1≤[1+m]^n : ∀ m n → 1 ≤ (1 + m) ^ n
1≤[1+m]^n m zero = ≤-refl
1≤[1+m]^n m (suc n) = begin
  1 * 1             ≤⟨ *-mono-≤ (s≤s {0} {m} z≤n) (1≤[1+m]^n m n) ⟩
  suc m * suc m ^ n ∎

^-monoʳ-≤ : ∀ n → (suc n ^_) Preserves _≤_ ⟶ _≤_
^-monoʳ-≤ n {.0}       {o}        z≤n       = begin
  1         ≤⟨ 1≤[1+m]^n n o ⟩
  suc n ^ o ∎
^-monoʳ-≤ n {.(suc _)} {.(suc _)} (s≤s {m} {o} m≤o) = begin
  suc n ^ suc m     ≡⟨⟩
  suc n * suc n ^ m ≤⟨ *-monoʳ-≤ (suc n) (^-monoʳ-≤ n m≤o) ⟩
  suc n * suc n ^ o ≡⟨⟩
  suc n ^ suc o     ∎

^-mono-≤ : ∀ {m n o p} → m ≤ n → o ≤ p → suc m ^ o ≤ suc n ^ p
^-mono-≤ {m} {n} {o} {p} m≤n o≤p = begin
  suc m ^ o ≤⟨ ^-monoˡ-≤ o (s≤s m≤n) ⟩
  suc n ^ o ≤⟨ ^-monoʳ-≤ n o≤p ⟩
  suc n ^ p ∎

*-cancelʳ-≤′ : ∀ m n {o} → False (o ≟ 0) → m * o ≤ n * o → m ≤ n
*-cancelʳ-≤′ m n {suc o} tt = *-cancelʳ-≤ m n o

-- TODO upadte stdlib
*-cancelʳ-≡′ : ∀ m n {o} → False (o ≟ 0) → m * o ≡ n * o → m ≡ n
*-cancelʳ-≡′ m n {suc o} tt = *-cancelʳ-≡ m n

*-monoʳ-<′ : ∀ n → False (n ≟ 0) → (n *_) Preserves _<_ ⟶ _<_
*-monoʳ-<′ (suc n) tt gt = *-monoʳ-< n gt

*-monoˡ-<′ : ∀ n → False (n ≟ 0) → (_* n) Preserves _<_ ⟶ _<_
*-monoˡ-<′ (suc n) tt gt = *-monoˡ-< n gt

m≡n+o⇒m∸o≡n : ∀ m n o → m ≡ n + o → m ∸ o ≡ n
m≡n+o⇒m∸o≡n m n o m≡n+o = trans (cong (_∸ o) m≡n+o) (m+n∸n≡m n o)

lemma₃ : ∀ m n → (∃ λ o → (n ≡ o + m)) ⊎ (n < m)
lemma₃ m n with compare m n
lemma₃ m              .(suc (m + k)) | less    .m k = inj₁ (suc k , cong suc (+-comm m k))
lemma₃ m              .m             | equal   .m   = inj₁ (0 , +-identityˡ m)
lemma₃ .(suc (n + k)) n              | greater .n k = inj₂ (s≤s (≤-stepsʳ k ≤-refl))

m≡n*o⇒n≡m/o : ∀ m n o → (wit : False (o ≟ 0)) → m ≡ n * o → n ≡ _/_ m o {wit}
m≡n*o⇒n≡m/o m n o@(suc o-1) tt m≡n*o = sym $ begin-equality
  m / o       ≡⟨ cong (_/ o) $ m≡n*o ⟩
  (n * o) / o ≡⟨ m*n/n≡m n o ⟩
  n            ∎

m*n≡o⇒m≡o/n : ∀ m n o → (wit : False (n ≟ 0)) → m * n ≡ o → m ≡ _/_ o n {wit}
m*n≡o⇒m≡o/n m n o wit m*n≡o = m≡n*o⇒n≡m/o o m n wit (sym m*n≡o)

*-pres-≢0 : ∀ {a b} → a ≢ 0 → b ≢ 0 → a * b ≢ 0
*-pres-≢0 {0}     {b} a≢0 b≢0 a*b≡0 = a≢0 refl
*-pres-≢0 {suc a} {0} a≢0 b≢0 a*b≡0 = b≢0 refl

-- TODO numbering
lemma₅ : ∀ m n o p → (m + n) * (o * p) ≡ (o * (m * p)) + n * (o * p)
lemma₅ = solve 4 (λ m n o p →
  (m :+ n) :* (o :* p) := (o :* (m :* p)) :+ (n :* (o :* p))) refl
  where open +-*-Solver

lemma₇ : ∀ m n o → m * n * o ≡ m * o * n
lemma₇ = solve 3 (λ m n o → m :* n :* o := m :* o :* n) refl
  where open +-*-Solver

lemma₈ : ∀ m n o → m * (n * o) ≡ n * m * o
lemma₈ = solve 3 (λ m n o → m :* (n :* o) := n :* m :* o) refl
  where open +-*-Solver

lemma₉ : ∀ m n o p → m * n * (o * p) ≡ (m * o) * (n * p)
lemma₉ = solve 4 (λ m n o p → m :* n :* (o :* p) := (m :* o) :* (n :* p)) refl
  where open +-*-Solver

lemma₁₀ : ∀ m n o p → m * n * o * p ≡ (m * o) * (n * p)
lemma₁₀ = solve 4 (λ m n o p → m :* n :* o :* p := (m :* o) :* (n :* p)) refl
  where open +-*-Solver

lemma₁₁ : ∀ m n o p → (m * n) * (o * p) ≡ m * o * p * n
lemma₁₁ = solve 4 (λ m n o p → (m :* n) :* (o :* p) := m :* o :* p :* n) refl
  where open +-*-Solver

lemma₁₂ : ∀ m n o → m * n * o ≡ n * (m * o)
lemma₁₂ = solve 3 (λ m n o → m :* n :* o := n :* (m :* o)) refl
  where open +-*-Solver

lemma₁₃ : ∀ m n o → m * n * n * o ≡ o * m * n * n
lemma₁₃ = solve 3 (λ m n o → m :* n :* n :* o := o :* m :* n :* n) refl
  where open +-*-Solver

lemma₁₄ : ∀ m n o → m * n * (n * o * o) ≡ m * (n * o) * (n * o)
lemma₁₄ = solve 3 (λ m n o → m :* n :* (n :* o :* o) := m :* (n :* o) :* (n :* o)) refl
  where open +-*-Solver

lemma₁₅ : ∀ n → (2 + 2 * n) * (1 + 2 * n) ≡ 2 * (1 + 2 * n) * (1 + n)
lemma₁₅ = solve 1 (λ n →
  (con 2 :+ con 2 :* n) :* (con 1 :+ con 2 :* n) :=
  con 2 :* (con 1 :+ con 2 :* n) :* (con 1 :+ n)
  ) refl
  where open +-*-Solver

lemma₁₆ : ∀ m n o p → m * n * (o * p * p) ≡ m * o * (n * p * p)
lemma₁₆ = solve 4 (λ m n o p →
  m :* n :* (o :* p :* p) := m :* o :* (n :* p :* p)
  ) refl
  where open +-*-Solver

lemma₁₇ : ∀ m n o p → m * n * (o * p) ≡ o * m * (n * p)
lemma₁₇ = solve 4 (λ m n o p → m :* n :* (o :* p) := o :* m :* (n :* p)) refl
  where open +-*-Solver


{-# OPTIONS --allow-unsolved-metas #-}

module AgdaFeatureInstanceResolutionViaConstraint where

postulate A : Set
postulate y : A

Appy : (A → Set) → Set
Appy H = H y

record Foo (T : Set) : Set where field foo : T

open Foo ⦃ … ⦄ public

postulate
  S : A → Set

record Failing : Set where
  no-eta-equality

  postulate
    instance FooInstance : {R : A → Set} → Foo (Appy R)

  works1 : Appy S
  works1 = foo ⦃ FooInstance {R = S} ⦄

  fails2 : Appy S
  fails2 = foo ⦃ FooInstance {R = {!!}} ⦄
  {-
    [21] (_R_11 y) =< (S y) : Set
    _12 := λ → Foo.foo FooInstance [blocked by problem 21]
  -}

  fails3 : Appy S
  fails3 = foo

record Succeeds : Set where
  no-eta-equality

  record Bar (B : A → Set) : Set where
    no-eta-equality

  postulate
    instance BarInstance : Bar S
    instance FooInstance : {R : A → Set} ⦃ _ : Bar R ⦄ → Foo (Appy R)

  works1 : Appy S
  works1 = foo ⦃ FooInstance {R = S} ⦄

  works2 : Appy S
  works2 = foo ⦃ FooInstance {R = {!!}} ⦄

  works3 : Appy S
  works3 = foo


-- This module introduces built-in types and primitive functions.

module Introduction.Built-in where

{- Agda supports four built-in types :

    - integers,
    - floating point numbers,
    - characters, and
    - strings.

   Note that strings are not defined as lists of characters (as is the case in
   Haskell).

   To use the built-in types they first have to be bound to Agda types. The
   reason for this is that there are no predefined names in Agda.
-}

-- To be able to use the built-in types we first introduce a new set for each
-- built-in type.
postulate
  Int    : Set
  Float  : Set
  Char   : Set
  String : Set

-- We can then bind the built-in types to these new sets using the BUILTIN
-- pragma.
{-# BUILTIN INTEGER Int    #-}
{-# BUILTIN FLOAT   Float  #-}
{-# BUILTIN CHAR    Char   #-}
{-# BUILTIN STRING  String #-}

pi : Float
pi = 3.141593

forAll : Char
forAll = '∀'

hello : String
hello = "Hello World!"

-- There are no integer literals. Instead there are natural number literals. To
-- use these you have to tell the type checker which type to use for natural
-- numbers.

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

{-# BUILTIN NATURAL Nat  #-}

-- Now we can define
fortyTwo : Nat
fortyTwo = 42

-- To anything interesting with values of the built-in types we need functions
-- to manipulate them. To this end Agda provides a set of primitive functions.
-- To gain access to a primitive function one simply declares it. For instance,
-- the function for floating point addition is called primFloatPlus. See below
-- for a complete list of primitive functions. At the moment the name that you
-- bring into scope is always the name of the primitive function. In the future
-- we might allow a primitive function to be introduced with any name.

module FloatPlus where  -- We put it in a module to prevent it from clashing with
                      -- the plus function in the complete list of primitive
                      -- functions below.
  primitive
    primFloatPlus : Float -> Float -> Float

  twoPi = primFloatPlus pi pi

-- Some primitive functions returns elements of non-primitive types. For
-- instance, the integer comparison functions return booleans. To be able to
-- use these functions we have to explain which type to use for booleans.

data Bool : Set where
  false : Bool
  true  : Bool

{-# BUILTIN BOOL  Bool  #-}
{-# BUILTIN TRUE  true  #-}
{-# BUILTIN FALSE false #-}

module FloatLess where

  primitive
    primFloatLess : Float -> Float -> Bool

-- There are functions to convert a string to a list of characters, so we need
-- to say which list type to use.

data List (A : Set) : Set where
  nil  : List A
  _::_ : A -> List A -> List A

{-# BUILTIN LIST List #-}
{-# BUILTIN NIL  nil  #-}
{-# BUILTIN CONS _::_ #-}

module StringToList where

  primitive
    primStringToList : String -> List Char

-- Below is a partial version of the complete list of primitive
-- functions.

primitive

    -- Integer functions
  primIntegerPlus     : Int -> Int -> Int
  primIntegerMinus    : Int -> Int -> Int
  primIntegerTimes    : Int -> Int -> Int
  primIntegerDiv      : Int -> Int -> Int  -- partial
  primIntegerMod      : Int -> Int -> Int  -- partial
  primIntegerEquality : Int -> Int -> Bool
  primIntegerLess     : Int -> Int -> Bool
  primIntegerAbs      : Int -> Nat
  primNatToInteger    : Nat -> Int
  primShowInteger     : Int -> String

    -- Floating point functions
  primIntegerToFloat : Int -> Float
  primFloatPlus      : Float -> Float -> Float
  primFloatMinus     : Float -> Float -> Float
  primFloatTimes     : Float -> Float -> Float
  primFloatDiv       : Float -> Float -> Float
  primFloatLess      : Float -> Float -> Bool
  primRound          : Float -> Int
  primFloor          : Float -> Int
  primCeiling        : Float -> Int
  primExp            : Float -> Float
  primLog            : Float -> Float     -- partial
  primSin            : Float -> Float
  primShowFloat      : Float -> String

    -- Character functions
  primCharEquality : Char -> Char -> Bool
  primIsLower      : Char -> Bool
  primIsDigit      : Char -> Bool
  primIsAlpha      : Char -> Bool
  primIsSpace      : Char -> Bool
  primIsAscii      : Char -> Bool
  primIsLatin1     : Char -> Bool
  primIsPrint      : Char -> Bool
  primIsHexDigit   : Char -> Bool
  primToUpper      : Char -> Char
  primToLower      : Char -> Char
  primCharToNat    : Char -> Nat
  primNatToChar    : Nat  -> Char -- partial
  primShowChar     : Char -> String

    -- String functions
  primStringToList   : String -> List Char
  primStringFromList : List Char -> String
  primStringAppend   : String -> String -> String
  primStringEquality : String -> String -> Bool
  primShowString     : String -> String


------------------------------------------------------------------------
-- The Agda standard library
--
-- The Colist type and some operations
------------------------------------------------------------------------

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

module Codata.Colist where

open import Size
open import Data.Unit
open import Data.Nat.Base
open import Data.Product using (_×_ ; _,_)
open import Data.These using (These; this; that; these)
open import Data.Maybe using (Maybe; nothing; just)
open import Data.List.Base using (List; []; _∷_)
open import Data.List.NonEmpty using (List⁺; _∷_)
open import Data.Vec as Vec using (Vec; []; _∷_)
open import Data.BoundedVec as BVec using (BoundedVec)
open import Function

open import Codata.Thunk using (Thunk; force)
open import Codata.Conat as Conat using (Conat ; zero ; suc)
open import Codata.Cowriter as CW using (Cowriter; _∷_)
open import Codata.Delay as Delay using (Delay ; now ; later)
open import Codata.Stream using (Stream ; _∷_)

data Colist {a} (A : Set a) (i : Size) : Set a where
  []  : Colist A i
  _∷_ : A → Thunk (Colist A) i → Colist A i

module _ {w a} {W : Set w} {A : Set a} where

------------------------------------------------------------------------
-- Relationship to Cowriter.

  fromCowriter : ∀ {i} → Cowriter W A i → Colist W i
  fromCowriter CW.[ _ ] = []
  fromCowriter (w ∷ ca) = w ∷ λ where .force → fromCowriter (ca .force)

module _ {a} {A : Set a} where

  toCowriter : ∀ {i} → Colist A i → Cowriter A ⊤ i
  toCowriter []       = CW.[ _ ]
  toCowriter (a ∷ as) = a ∷ λ where .force → toCowriter (as .force)

------------------------------------------------------------------------
-- Basic functions.

  [_] : A → Colist A ∞
  [ a ] = a ∷ λ where .force → []

  length : ∀ {i} → Colist A i → Conat i
  length []       = zero
  length (x ∷ xs) = suc λ where .force → length (xs .force)

  replicate : ∀ {i} → Conat i → A → Colist A i
  replicate zero    a = []
  replicate (suc n) a = a ∷ λ where .force → replicate (n .force) a

  infixr 5 _++_ _⁺++_
  _++_ : ∀ {i} → Colist A i → Colist A i → Colist A i
  []       ++ ys = ys
  (x ∷ xs) ++ ys = x ∷ λ where .force → xs .force ++ ys

  lookup : ℕ → Colist A ∞ → Maybe A
  lookup n       []       = nothing
  lookup zero    (a ∷ as) = just a
  lookup (suc n) (a ∷ as) = lookup n (as .force)

  colookup : ∀ {i} → Conat i → Colist A i → Delay (Maybe A) i
  colookup n       []       = now nothing
  colookup zero    (a ∷ as) = now (just a)
  colookup (suc n) (a ∷ as) =
    later λ where .force → colookup (n .force) (as .force)

  take : ∀ (n : ℕ) → Colist A ∞ → BoundedVec A n
  take zero    xs       = BVec.[]
  take n       []       = BVec.[]
  take (suc n) (x ∷ xs) = x BVec.∷ take n (xs .force)

  cotake : ∀ {i} → Conat i → Stream A i → Colist A i
  cotake zero    xs       = []
  cotake (suc n) (x ∷ xs) = x ∷ λ where .force → cotake (n .force) (xs .force)

  fromList : List A → Colist A ∞
  fromList []       = []
  fromList (x ∷ xs) = x ∷ λ where .force → fromList xs

  _⁺++_ : ∀ {i} → List⁺ A → Thunk (Colist A) i → Colist A i
  (x ∷ xs) ⁺++ ys = x ∷ λ where .force → fromList xs ++ ys .force

  fromStream : ∀ {i} → Stream A i → Colist A i
  fromStream = cotake Conat.infinity

module _ {ℓ} {A : Set ℓ} where

  chunksOf : (n : ℕ) → Colist A ∞ → Cowriter (Vec A n) (BoundedVec A n) ∞
  chunksOf n = chunksOfAcc n id id module ChunksOf where

    chunksOfAcc : ∀ {i} m →
      -- We have two continuations but we are only ever going to use one.
      -- If we had linear types, we would write the type using the & conjunction here.
      (k≤ : BoundedVec A m → BoundedVec A n) →
      (k≡ : Vec A m → Vec A n) →
      -- Finally we chop up the input stream.
      Colist A ∞ → Cowriter (Vec A n) (BoundedVec A n) i
    chunksOfAcc zero    k≤ k≡ as       = k≡ [] ∷ λ where .force → chunksOfAcc n id id as
    chunksOfAcc (suc k) k≤ k≡ []       = CW.[ k≤ BVec.[] ]
    chunksOfAcc (suc k) k≤ k≡ (a ∷ as) =
      chunksOfAcc k (k≤ ∘ (a BVec.∷_)) (k≡ ∘ (a ∷_)) (as .force)

module _ {a b} {A : Set a} {B : Set b} where

 map : ∀ {i} (f : A → B) → Colist A i → Colist B i
 map f []       = []
 map f (a ∷ as) = f a ∷ λ where .force → map f (as .force)

 unfold :  ∀ {i} → (A → Maybe (A × B)) → A → Colist B i
 unfold next seed with next seed
 ... | nothing          = []
 ... | just (seed′ , b) = b ∷ λ where .force → unfold next seed′

 scanl : ∀ {i} → (B → A → B) → B → Colist A i → Colist B i
 scanl c n []       = n ∷ λ where .force → []
 scanl c n (a ∷ as) = n ∷ λ where .force → scanl c (c n a) (as .force)

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where

  alignWith : ∀ {i} → (These A B → C) → Colist A i → Colist B i → Colist C i
  alignWith f []         bs       = map (f ∘′ that) bs
  alignWith f as@(_ ∷ _) []       = map (f ∘′ this) as
  alignWith f (a ∷ as)   (b ∷ bs) =
    f (these a b) ∷ λ where .force → alignWith f (as .force) (bs .force)

  zipWith : ∀ {i} → (A → B → C) → Colist A i → Colist B i → Colist C i
  zipWith f []       bs       = []
  zipWith f as       []       = []
  zipWith f (a ∷ as) (b ∷ bs) =
    f a b ∷ λ where .force → zipWith f (as .force) (bs .force)

module _ {a b} {A : Set a} {B : Set b} where

  align : ∀ {i} → Colist A i → Colist B i → Colist (These A B) i
  align = alignWith id

  zip : ∀ {i} → Colist A i → Colist B i → Colist (A × B) i
  zip = zipWith _,_

  ap : ∀ {i} → Colist (A → B) i → Colist A i → Colist B i
  ap = zipWith _$′_