open import Data.List    using (List; _∷_; [])
open import Data.Product using (_×_; _,_; Σ)
open import Data.Unit    using (⊤; tt)
open import Relation.Binary.PropositionalEquality using (_≡_)

module SystemT where

  {- de Bruijn indices are represented as proofs that
     an element is in a list -}
  data _∈_ {A : Set} : (x : A) (l : List A) → Set where -- type \in
    i0 : {x : A} {xs : List A} → x ∈ (x ∷ xs)
    iS : {x y : A} {xs : List A} → x ∈ xs → x ∈ (y ∷ xs)

  {- types of the STLC -}
  data TType : Set where
    b : TType             -- uninterpreted base type
    _⇒_ : TType → TType → TType -- type \=>

  {- contexts are lists of TType's -}
  Ctx = List
  _,,_ : ∀ {A} → Ctx A → TType → Ctx A
  Γ ,, τ = τ ∷ Γ

  infixr 10 _⇒_
  infixr 9 _,,_
  infixr 8 _⊢_ -- type \entails

  {- Γ ⊢ τ represents a term of type τ in context Γ -}
  data _⊢_ (Γ : Ctx) : TType → Set where
    c   : Γ ⊢ b -- some constant of the base type
    v   : {τ : TType} → τ ∈ Γ → Γ ⊢ τ
    lam : {τ1 τ2 : TType} → Γ ,, τ1 ⊢ τ2 → Γ ⊢ τ1 ⇒ τ2
    app : {τ1 τ2 : TType} → Γ ⊢ τ1 ⇒ τ2 → Γ ⊢ τ1 → Γ ⊢ τ2

  module Examples where
    i : [] ⊢ b ⇒ b
    i = lam (v i0) -- \ x -> x

    k : [] ⊢ b ⇒ b ⇒ b
    k = lam (lam (v (iS i0))) -- \ x -> \ y -> x

    {- TASK 1: Define a term representing  \ x -> \ y -> y -}
    k' : [] ⊢ b ⇒ b ⇒ b
    k' = {!!}

  {- The following proof is like a "0-ary" logical relation.
     It gives a semantics of the STLC in Agda.
     This shows that the STLC is sound, relative to Agda.
  -}
  module Semantics (B : Set) (elB : B) where
    -- works for any interpretation of the base type b

    -- function mapping STLC types to Agda types
    ⟦_⟧t : TType → Set  -- type \(0 and \)0
    ⟦ b ⟧t = B
    ⟦ τ1 ⇒ τ2 ⟧t = ⟦ τ1 ⟧t → ⟦ τ2 ⟧t

    -- function mapping STLC contexts to Agda types
    ⟦_⟧c : Ctx → Set
    ⟦ [] ⟧c = ⊤
    ⟦ τ ∷ Γ ⟧c = ⟦ Γ ⟧c × ⟦ τ ⟧t

    {- TASK 2 : Define the interpretation of terms -}
    ⟦_⟧ : {Γ : Ctx} {τ : TType} → Γ ⊢ τ → ⟦ Γ ⟧c → ⟦ τ ⟧t
    ⟦ e ⟧ γ = {!!}

    {- the following test should pass
    test : ⟦ Examples.k ⟧ == \ γ x y → x
    test = Refl
    -}

  {- you can ignore the implementation of this module.
     the interface for the components you need is listed below
  -}
  module RenamingAndSubstitution where

    -- renamings = variable for variable substitutions

    infix 9 _⊇_

    _⊇_ : Ctx → Ctx → Set -- type \sup=
    Γ' ⊇ [] = ⊤
    Γ' ⊇ (τ ∷ Γ) = (Γ' ⊇ Γ) × (τ ∈ Γ')

    extend⊇ : {Γ : Ctx} (Γ' : Ctx) {τ : TType} → Γ ⊇ Γ' → (Γ ,, τ) ⊇ Γ'
    extend⊇ [] ren = tt
    extend⊇ (τ ∷ Γ') (ρ , x) = extend⊇ Γ' ρ , iS x

    open import Data.List using (_++_)
    extend⊇* : {Γ : Ctx} (Γ' : Ctx) (Γ'' : Ctx) → Γ ⊇ Γ' → (Γ'' ++ Γ) ⊇ Γ'
    extend⊇* Γ' [] ρ = ρ
    extend⊇* Γ' (x ∷ Γ'') ρ = extend⊇ _ (extend⊇* Γ' Γ'' ρ)

    shift : {Γ : Ctx} {τ : TType} (Γ' : Ctx) (i : τ ∈ Γ) → τ ∈ (Γ' ++ Γ)
    shift [] i = i
    shift (τ ∷ Γ) i = iS (shift Γ i)

    ⊇-id : (Γ : Ctx) → Γ ⊇ Γ
    ⊇-id [] = tt
    ⊇-id (τ ∷ Γ) = extend⊇ Γ (⊇-id Γ) , i0

    ⊇-single : {Γ : Ctx} {τ : TType} → (Γ ,, (τ ⊇ Γ))
    ⊇-single = extend⊇ _ (⊇-id _)

    -- you can rename a term

    rename : {Γ Γ' : Ctx} {τ : TType} → Γ' ⊇ Γ → Γ ⊢ τ → Γ' ⊢ τ
    rename ρ c = c
    rename (_ , x') (v i0) = v x'
    rename (ρ , _) (v (iS x)) = rename ρ (v x)
    rename ρ (lam e) = lam (rename (extend⊇ _ ρ , i0) e)
    rename ρ (app e e') = app (rename ρ e) (rename ρ e')

    -- expression-for-variable substitutions

    _⊢c_ : Ctx → Ctx → Set
    Γ' ⊢c [] = tt
    Γ' ⊢c (τ ∷ Γ) = (Γ' ⊢c Γ) × (Γ' ⊢ τ)

    rename-subst : {Γ1 Γ2 Γ3 : Ctx} →  Γ1 ⊇ Γ2 → Γ2 ⊢c Γ3 → Γ1 ⊢c Γ3
    rename-subst {Γ1} {Γ2} {[]} ρ θ = tt
    rename-subst {Γ1} {Γ2} {τ3 ∷ Γ3} ρ (θ , e) = rename-subst ρ θ , rename ρ e

    addvar : {Γ Γ' : Ctx} {τ : TType} → Γ ⊢c Γ' → (Γ ,, τ) ⊢c (Γ' ,, τ)
    addvar θ = rename-subst ⊇-single θ , v i0

    id-subst : {Γ : Ctx} → Γ ⊢c Γ
    id-subst {[]} = tt
    id-subst {τ ∷ Γ} = rename-subst ⊇-single (id-subst {Γ}) , v i0

    subst : {Γ Γ' : Ctx}{τ : TType} → Γ ⊢c Γ' → Γ' ⊢ τ  → Γ ⊢ τ
    subst θ c = c
    subst (θ , e) (v i0) = e
    subst (θ , e) (v (iS x)) = subst θ (v x)
    subst θ (lam e) = lam (subst (addvar θ) e)
    subst θ (app e e') = app (subst θ e) (subst θ e')

    subst1 : {τ τ0 : TType} → [] ⊢ τ0 → ([] ,, τ0) ⊢ τ → [] ⊢ τ
    subst1 e0 e = subst (tt , e0) e

    -- these are not tasks (unless you really want); I didn't get to prove them; sorry!
    compose : {τ1 τ2 : TType} {Γ : Ctx} (θ : [] ⊢c Γ) (e' : [] ⊢ τ1) (e : Γ ,, τ1 ⊢ τ2)
            → subst (θ , e') e ≡ subst1 e' (subst (addvar θ) e)
    compose = {!!}

    ident : {Γ : Ctx} {τ : TType} {e : Γ ⊢ τ} → e ≡ subst (id-subst) e
    ident = {!!}

  open RenamingAndSubstitution using (subst1 ; _⊢c_ ; subst ; ident ; compose)
  {- θ : Γ ⊢c Γ' means θ is a substitution for Γ' in terms of Γ.  It is defined as follows:

    _⊢c_ : Ctx → Ctx → Set
    Γ' ⊢c [] = Unit
    Γ' ⊢c (τ ∷ Γ) = (Γ' ⊢c Γ) × (Γ' ⊢ τ)

    -- apply a substitution to a term
    subst : {Γ Γ' : Ctx}{τ : TType} → Γ ⊢c Γ' → Γ' ⊢ τ  → Γ ⊢ τ

    -- substitution for a single variable
    subst1 : {τ τ0 : TType} → [] ⊢ τ0 → ([] ,, τ0) ⊢ τ → [] ⊢ τ

    -- you will need these two properties:
    compose : {τ1 τ2 : TType} {Γ : Ctx} (θ : [] ⊢c Γ) (e' : [] ⊢ τ1) (e : Γ ,, τ1 ⊢ τ2)
            → subst (θ , e') e == subst1 e' (subst (addvar θ) e)

    ident : {Γ : Ctx} {τ : TType} {e : Γ ⊢ τ} → e == subst (id-subst) e
  -}

  module OpSem where
    -- step relation
    data _↦_ : {τ : TType} → [] ⊢ τ → [] ⊢ τ → Set where
      Step/app  :{τ1 τ2 : TType} {e e' : [] ⊢ τ1 ⇒ τ2} {e1 : [] ⊢ τ1}
             → e ↦ e'
             → (app e e1) ↦ (app e' e1)
      Step/β : {τ1 τ2 : TType} {e : [] ,, τ1 ⊢ τ2} {e1 : [] ⊢ τ1}
             → (app (lam e) e1) ↦ subst1 e1 e

    -- reflexive/transitive closure
    data _↦*_ : {τ : TType} → [] ⊢ τ → [] ⊢ τ → Set where
      Done : {τ : TType} {e : [] ⊢ τ} → e ↦* e
      Step : {τ : TType} {e1 e2 e3 : [] ⊢ τ}
           → e1 ↦ e2  →  e2 ↦* e3
           → e1 ↦* e3
  open OpSem

  {- Next, you will prove "very weak normalization".  The theorem is
     that any closed term *of base type b* evaluates to the constant c.
     No claims are made about terms of function type.
  -}
  module VeryWeakNormalization where

    WN : (τ : TType) → [] ⊢ τ → Set
    -- WN_τ(e) iff e ↦* c
    WN b e = e ↦* c
    -- WN_τ(e) iff for all e1 : τ1, if WN_τ1(e1) then WN_τ2(e e1)
    WN (τ1 ⇒ τ2) e = (e1 : [] ⊢ τ1) → WN τ1 e1 → WN τ2 (app e e1)

    -- extend WN to contexts and substitutions
    WNc : (Γ : Ctx) → [] ⊢c Γ → Set
    WNc [] θ = tt
    WNc (τ ∷ Γ) (θ , e) = WNc Γ θ × WN τ e

    {- TASK 3 : show that the relation is closed under head expansion: -}
    head-expand : (τ : TType) {e e' : [] ⊢ τ} → e ↦ e' → WN τ e' → WN τ e
    head-expand = {!!}

    {- TASK 4 : prove the fundamental theorem

    Hint: you may find it helpful to use
      transport : {A : Set} (B : A → Set) {a1 a2 : A} → a1 == a2 → (B a1 → B a2)
    to coerce by a propositional equality.
    -}
    fund : {Γ : Ctx} {τ : TType} {θ : [] ⊢c Γ}
         → (e : Γ ⊢ τ)
         → WNc Γ θ
         → WN τ (subst θ e)
    fund = {!!}

    {- TASK 5 : conclude weak normalization at base type -}
    corollary : (e : [] ⊢ b) → e ↦* c
    corollary = {!!}

  {- TASK 6 : change the definition of the logical relation so that you also can conclude normalization
              at function type.
  -}
  module WeakNormalization where

    open RenamingAndSubstitution using (addvar)
    --- you will want to use
    --    addvar : {Γ Γ' : Ctx} {τ : TType} → Γ ⊢c Γ' → (Γ ,, τ) ⊢c (Γ' ,, τ)

    -- Hint: you will need a couple of lemmas about ↦* that we didn't need above
    -- (I used three of them)

    {- TASK 6a -}
    corollary1 : (e : [] ⊢ b) → e ↦* c
    corollary1 e = {!!}

    {- TASK 6b -}
    corollary2 : {τ1 τ2 : TType} (e : [] ⊢ τ1 ⇒ τ2) → Σ \(e' : _) → e ↦* (lam e')
    corollary2 e = {!!}


module RAdjunctions.RAdj2RMon where

open import Function
open import Relation.Binary.HeterogeneousEquality
open import Categories
open import Functors
open import RMonads
open import RAdjunctions

open Cat
open Fun
open RAdj

Adj2Mon : ∀{a b c d e f}{C : Cat {a}{b}}{D : Cat {c}{d}}{E : Cat {e}{f}}
          {J : Fun C D} → RAdj J E → RMonad J
Adj2Mon {C = C}{D}{E}{J} A = record{
  T    = OMap (R A) ∘ OMap (L A);
  η    = left A (iden E);
  bind = HMap (R A) ∘ right A;
  law1 = trans (cong (HMap (R A)) (lawa A (iden E))) (fid (R A));
  law2 = λ {_ _ f} →
    trans (cong (comp D (HMap (R A) (right A f)))
                (trans (sym (idr D))
                       (cong (comp D (left A (iden E))) (sym (fid J)))))
          (trans (natleft A (iden C) (right A f) (iden E))
                 (trans (cong (left A)
                              (trans (cong (comp E (right A f))
                                           (trans (idl E) (fid (L A))))
                                     (idr E)))
                        (lawb A f)));
  law3 = λ{_ _ _ f g} →
    trans (cong (HMap (R A))
                (trans (trans (cong (right A)
                                    (cong (comp D (HMap (R A) (right A g)))
                                          (trans (sym (idr D))
                                                 (cong (comp D f)
                                                       (sym (fid J))))))
                              (trans (natright A (iden C) (right A g) f)
                                     (trans (sym (ass E))
                                            (cong (comp E (comp E (right A g)
                                                                  (right A f)))
                                                  (fid (L A))))))
                       (idr E)))
          (fcomp (R A))}


module Subset where

data Subset (A : Set) (P : A -> Set) : Set where
  inn : (a : A) -> .(P a) -> Subset A P

out : forall {A P} -> Subset A P -> A
out (inn a p) = a


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

open import HoTT
open import homotopy.SphereEndomorphism
open import groups.Pointed
open import groups.SphereEndomorphism
open import groups.FromSusp
open import cohomology.Theory

module cohomology.SphereEndomorphism (CT : CohomologyTheory lzero) (n : ℤ) (m : ℕ) where

  open CohomologyTheory CT
  open import cohomology.Cogroup CT n
    (Susp-cogroup-structure (⊙Sphere m))
    (⊙Sphere (S m))

  private
    C-fmap' : Trunc-⊙Sphere-endo (S m) → (C n (⊙Sphere (S m)) →ᴳ C n (⊙Sphere (S m)))
    C-fmap' = Trunc-rec λ f → C-fmap n f

    CEl-fmap' : Trunc-⊙Sphere-endo (S m) → (CEl n (⊙Sphere (S m)) → CEl n (⊙Sphere (S m)))
    CEl-fmap' f = GroupHom.f (C-fmap' f)

    C-fmap'-hom : Trunc-⊙SphereS-endo-group m →ᴳ
      (hom-group (C n (⊙Sphere (S m))) (C-abgroup n (⊙Sphere (S m))))
    C-fmap'-hom = group-hom C-fmap'
      (Trunc-elim λ f → Trunc-elim λ g → C-fmap-preserves-comp f g)

    CEl-fmap'-η : ∀ (f : Trunc-⊙Sphere-endo (S m)) (g : CEl n (⊙Sphere (S m)))
      → CEl-fmap' f g == Group.exp (C n (⊙Sphere (S m))) g (Trunc-⊙SphereS-endo-degree m f)
    CEl-fmap'-η f g =
      CEl-fmap' f g
        =⟨ ! $ ap (λ f → CEl-fmap' f g) $ is-equiv.f-g (Trunc-⊙SphereS-endo-⊙group-is-infinite-cyclic m) f ⟩
      CEl-fmap' (Group.exp (Trunc-⊙SphereS-endo-group m) [ ⊙idf _ ] (Trunc-⊙SphereS-endo-degree m f)) g
        =⟨ GroupHom.pres-exp
            (app-hom g ∘ᴳ C-fmap'-hom)
            [ ⊙idf _ ]
            (Trunc-⊙SphereS-endo-degree m f) ⟩
      Group.exp
        (C n (⊙Sphere (S m)))
        (CEl-fmap' [ ⊙idf _ ] g)
        (Trunc-⊙SphereS-endo-degree m f)
        =⟨ ap (λ g → Group.exp (C n (⊙Sphere (S m))) g (Trunc-⊙SphereS-endo-degree m f)) $
              CEl-fmap-idf n g  ⟩
      Group.exp
        (C n (⊙Sphere (S m))) g
        (Trunc-⊙SphereS-endo-degree m f)
        =∎
  abstract
    CEl-fmap-⊙Sphere-endo-η : ∀ (f : ⊙Sphere-endo (S m)) (g : CEl n (⊙Sphere (S m)))
      → CEl-fmap n f g == Group.exp (C n (⊙Sphere (S m))) g (⊙SphereS-endo-degree m f)
    CEl-fmap-⊙Sphere-endo-η f = CEl-fmap'-η [ f ]


------------------------------------------------------------------------
-- Properties related to negation
------------------------------------------------------------------------

module Relation.Nullary.Negation where

open import Relation.Nullary
open import Relation.Unary
open import Data.Empty
open import Data.Function
open import Data.Product as Prod
open import Data.Fin
open import Data.Fin.Dec
open import Category.Monad

contradiction : ∀ {P whatever} → P → ¬ P → whatever
contradiction p ¬p = ⊥-elim (¬p p)

contraposition : ∀ {P Q} → (P → Q) → ¬ Q → ¬ P
contraposition f ¬q p = contradiction (f p) ¬q

-- Note also the following use of flip:

private
  note : ∀ {P Q} → (P → ¬ Q) → Q → ¬ P
  note = flip

------------------------------------------------------------------------
-- Quantifier juggling

∃⟶¬∀¬ : ∀ {A} {P : Pred A} → ∃ P → ¬ (∀ x → ¬ P x)
∃⟶¬∀¬ = flip uncurry

∀⟶¬∃¬ : ∀ {A} {P : Pred A} → (∀ x → P x) → ¬ ∃ λ x → ¬ P x
∀⟶¬∃¬ ∀xPx (x , ¬Px) = ¬Px (∀xPx x)

¬∃⟶∀¬ : ∀ {A} {P : Pred A} → ¬ ∃ (λ x → P x) → ∀ x → ¬ P x
¬∃⟶∀¬ = curry

∀¬⟶¬∃ : ∀ {A} {P : Pred A} → (∀ x → ¬ P x) → ¬ ∃ (λ x → P x)
∀¬⟶¬∃ = uncurry

∃¬⟶¬∀ : ∀ {A} {P : Pred A} → ∃ (λ x → ¬ P x) → ¬ (∀ x → P x)
∃¬⟶¬∀ = flip ∀⟶¬∃¬

-- When P is a decidable predicate over a finite set the following
-- lemma can be proved.

¬∀⟶∃¬ : ∀ n (P : Pred (Fin n)) → (∀ i → Dec (P i)) →
        ¬ (∀ i → P i) → ∃ λ i → ¬ P i
¬∀⟶∃¬ n P dec ¬P = Prod.map id proj₁ $ ¬∀⟶∃¬-smallest n P dec ¬P

------------------------------------------------------------------------
-- Double-negation

¬¬-map : ∀ {P Q} → (P → Q) → ¬ ¬ P → ¬ ¬ Q
¬¬-map f = contraposition (contraposition f)

¬¬-drop : {P : Set} → ¬ ¬ ¬ P → ¬ P
¬¬-drop ¬¬¬P P = ¬¬¬P (λ ¬P → ¬P P)

¬¬-drop-Dec : {P : Set} → Dec P → ¬ ¬ P → P
¬¬-drop-Dec (yes p) ¬¬p = p
¬¬-drop-Dec (no ¬p) ¬¬p = ⊥-elim (¬¬p ¬p)

¬-drop-Dec : {P : Set} → Dec (¬ ¬ P) → Dec (¬ P)
¬-drop-Dec (yes ¬¬p) = no ¬¬p
¬-drop-Dec (no ¬¬¬p) = yes (¬¬-drop ¬¬¬p)

-- Double-negation is a monad (if we assume that all elements of ¬ ¬ P
-- are equal).

¬¬-Monad : RawMonad (λ P → ¬ ¬ P)
¬¬-Monad = record
  { return = contradiction
  ; _>>=_  = λ x f → ¬¬-drop (¬¬-map f x)
  }

¬¬-push : {P : Set} {Q : P → Set} →
          ¬ ¬ ((x : P) → Q x) → (x : P) → ¬ ¬ Q x
¬¬-push ¬¬P⟶Q P ¬Q = ¬¬P⟶Q (λ P⟶Q → ¬Q (P⟶Q P))

-- A double-negation-translated variant of excluded middle (or: every
-- nullary relation is decidable in the double-negation monad).

excluded-middle : {P : Set} → ¬ ¬ Dec P
excluded-middle ¬h = ¬h (no (λ p → ¬h (yes p)))

-- If whatever is instantiated with ¬ ¬ something, then this function
-- is call with current continuation in the double-negation monad, or,
-- if you will, a double-negation translation of Peirce's law.
--
-- In order to prove ¬ ¬ P one can assume ¬ P and prove ⊥. However, it
-- is sometimes nice to avoid leaving the double-negation monad; in
-- that case this function can be used (with whatever instantiated to
-- ⊥).

call/cc : ∀ {whatever P : Set} → ((P → whatever) → ¬ ¬ P) → ¬ ¬ P
call/cc hyp ¬p = hyp (λ p → ⊥-elim (¬p p)) ¬p


------------------------------------------------------------------------
-- The Agda standard library
--
-- Decidable semi-heterogeneous vector equality over setoids
------------------------------------------------------------------------

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

open import Relation.Binary

module Data.Vec.Relation.Binary.Equality.DecSetoid
  {a ℓ} (DS : DecSetoid a ℓ) where

open import Data.Nat using (ℕ)
import Data.Vec.Relation.Binary.Equality.Setoid as Equality
import Data.Vec.Relation.Binary.Pointwise.Inductive as PW
open import Level using (_⊔_)
open import Relation.Binary using (Decidable)

open DecSetoid DS

------------------------------------------------------------------------
-- Make all definitions from equality available

open Equality setoid public

------------------------------------------------------------------------
-- Additional properties

infix 4 _≋?_

_≋?_ : ∀ {m n} → Decidable (_≋_ {m} {n})
_≋?_ = PW.decidable _≟_

≋-isDecEquivalence : ∀ n → IsDecEquivalence (_≋_ {n})
≋-isDecEquivalence = PW.isDecEquivalence isDecEquivalence

≋-decSetoid : ℕ → DecSetoid a (a ⊔ ℓ)
≋-decSetoid = PW.decSetoid DS


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

open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Addition
open import Numbers.Naturals.Order
open import Numbers.Naturals.Multiplication
open import Semirings.Definition
open import Orders.Total.Definition

module Numbers.Naturals.Naturals where

record subtractionNResult (a b : ℕ) .(p : a ≤N b) : Set where
  field
    result : ℕ
    pr : a +N result ≡ b

subtractionNWellDefined : {a b : ℕ} → {p1 p2 : a ≤N b} → (s : subtractionNResult a b p1) → (t : subtractionNResult a b p2) → (subtractionNResult.result s ≡ subtractionNResult.result t)
subtractionNWellDefined {a} {b} {inl x} {pr2} record { result = result1 ; pr = pr1 } record { result = result ; pr = pr } = canSubtractFromEqualityLeft {a} (transitivity pr1 (equalityCommutative pr))
subtractionNWellDefined {a} {.a} {inr refl} {pr2} record { result = result1 ; pr = pr1 } record { result = result2 ; pr = pr } = transitivity g' (equalityCommutative g)
  where
    g : result2 ≡ 0
    g = canSubtractFromEqualityLeft {a} {_} {0} (transitivity pr (equalityCommutative (addZeroRight a)))
    g' : result1 ≡ 0
    g' = canSubtractFromEqualityLeft {a} {_} {0} (transitivity pr1 (equalityCommutative (addZeroRight a)))

-N : {a : ℕ} → {b : ℕ} → (pr : a ≤N b) → subtractionNResult a b pr
-N {zero} {b} prAB = record { result = b ; pr = refl }
-N {succ a} {zero} (inl ())
-N {succ a} {zero} (inr ())
-N {succ a} {succ b} (inl x) with -N {a} {b} (inl (canRemoveSuccFrom<N x))
-N {succ a} {succ b} (inl x) | record { result = result ; pr = pr } = record { result = result ; pr = applyEquality succ pr }
-N {succ a} {succ b} (inr pr) = record { result = 0 ; pr = transitivity (applyEquality succ (addZeroRight a)) pr }

addOneToWeakInequality : {a b : ℕ} → (a ≤N b) → (succ a ≤N succ b)
addOneToWeakInequality {a} {b} (inl ineq) = inl (succPreservesInequality ineq)
addOneToWeakInequality {a} {.a} (inr refl) = inr refl

bumpUpSubtraction : {a b : ℕ} → (p1 : a ≤N b) → (s : subtractionNResult a b p1) → Sg (subtractionNResult (succ a) (succ b) (addOneToWeakInequality p1)) (λ n → subtractionNResult.result n ≡ subtractionNResult.result s)
bumpUpSubtraction {a} {b} a<=b record { result = result ; pr = pr } = record { result = result ; pr = applyEquality succ pr } , refl

addMinus : {a : ℕ} → {b : ℕ} → (pr : a ≤N b) → subtractionNResult.result (-N {a} {b} pr) +N a ≡ b
addMinus {zero} {zero} p = refl
addMinus {zero} {succ b} pr = applyEquality succ (addZeroRight b)
addMinus {succ a} {zero} (inl (le x ()))
addMinus {succ a} {zero} (inr ())
addMinus {succ a} {succ b} (inl x) with (-N {succ a} {succ b} (inl x))
addMinus {succ a} {succ b} (inl x) | record { result = result ; pr = pr } = transitivity (transitivity (applyEquality (_+N succ a) (transitivity (subtractionNWellDefined {p1 = inl (canRemoveSuccFrom<N x)} {p2 = inl (canRemoveSuccFrom<N x)} (record { result = subtractionNResult.result (-N (inl (canRemoveSuccFrom<N x))) ; pr = transitivity (additionNIsCommutative a _) (addMinus (inl (canRemoveSuccFrom<N x)))}) previous) (equalityCommutative t))) (additionNIsCommutative result (succ a))) pr
  where
    pr'' : (a <N b) || (a ≡ b)
    pr'' = (inl (le (_<N_.x x) (transitivity (equalityCommutative (succExtracts (_<N_.x x) a)) (succInjective (_<N_.proof x)))))
    previous : subtractionNResult a b pr''
    previous = -N pr''
    next : Sg (subtractionNResult (succ a) (succ b) (addOneToWeakInequality pr'')) λ n → subtractionNResult.result n ≡ subtractionNResult.result previous
    next = bumpUpSubtraction pr'' previous
    t : result ≡ subtractionNResult.result (underlying next)
    t = subtractionNWellDefined {succ a} {succ b} {inl x} {addOneToWeakInequality pr''} (record { result = result ; pr = pr }) (underlying next)
addMinus {succ a} {succ .a} (inr refl) = refl

addMinus' : {a b : ℕ} → (pr : a ≤N b) → a +N subtractionNResult.result (-N {a} {b} pr) ≡ b
addMinus' {a} {b} pr rewrite additionNIsCommutative a (subtractionNResult.result (-N {a} {b} pr)) = addMinus {a} {b} pr

additionPreservesInequality : {a b : ℕ} → (c : ℕ) → a <N b → a +N c <N b +N c
additionPreservesInequality {a} {b} zero prAB rewrite additionNIsCommutative a 0 | additionNIsCommutative b 0 = prAB
additionPreservesInequality {a} {b} (succ c) (le x proof) = le x (transitivity (equalityCommutative (additionNIsAssociative (succ x) a (succ c))) (applyEquality (_+N succ c) proof))

additionPreservesInequalityOnLeft : {a b : ℕ} → (c : ℕ) → a <N b → c +N a <N c +N b
additionPreservesInequalityOnLeft {a} {b} c prAB = identityOfIndiscernablesRight (λ a b → a <N b) (identityOfIndiscernablesLeft (λ a b → a <N b) (additionPreservesInequality {a} {b} c prAB) (additionNIsCommutative a c)) (additionNIsCommutative b c)

multiplyIncreases : (a : ℕ) → (b : ℕ) → succ zero <N a → zero <N b → b <N a *N b
multiplyIncreases zero b (le x ()) prB
multiplyIncreases (succ zero) b (le zero ()) prb
multiplyIncreases (succ zero) b (le (succ x) ()) prb
multiplyIncreases (succ (succ a)) (succ b) prA prb = le (b +N a *N succ b) (applyEquality succ (transitivity (Semiring.commutative ℕSemiring _ (succ b)) (transitivity (applyEquality succ (Semiring.commutative ℕSemiring b _)) (Semiring.commutative ℕSemiring _ b))))

canTimesOneOnLeft : {a b : ℕ} → (a <N b) → (a *N (succ zero)) <N b
canTimesOneOnLeft {a} {b} prAB = identityOfIndiscernablesLeft _<N_ prAB (equalityCommutative (productWithOneRight a))

canTimesOneOnRight : {a b : ℕ} → (a <N b) → a <N (b *N (succ zero))
canTimesOneOnRight {a} {b} prAB = identityOfIndiscernablesRight _<N_ prAB (equalityCommutative (productWithOneRight b))

canSwapAddOnLeftOfInequality : {a b c : ℕ} → (a +N b <N c) → (b +N a <N c)
canSwapAddOnLeftOfInequality {a} {b} {c} pr = identityOfIndiscernablesLeft _<N_ pr (additionNIsCommutative a b)

canSwapAddOnRightOfInequality : {a b c : ℕ} → (a <N b +N c) → (a <N c +N b)
canSwapAddOnRightOfInequality {a} {b} {c} pr = identityOfIndiscernablesRight _<N_ pr (additionNIsCommutative b c)

bumpDownOnRight : (a c : ℕ) → c *N succ a ≡ c *N a +N c
bumpDownOnRight a c = transitivity (multiplicationNIsCommutative c (succ a)) (transitivity refl (transitivity (additionNIsCommutative c (a *N c)) ((addingPreservesEqualityRight c (multiplicationNIsCommutative a c) ))))

succIsNonzero : {a : ℕ} → (succ a ≡ zero) → False
succIsNonzero {a} ()

lessImpliesNotEqual : {a b : ℕ} → (a <N b) → a ≡ b → False
lessImpliesNotEqual {a} {.a} prAB refl = TotalOrder.irreflexive ℕTotalOrder prAB

-NIsDecreasing : {a b : ℕ} → (prAB : succ a <N b) → subtractionNResult.result (-N (inl prAB)) <N b
-NIsDecreasing {a} {b} prAB with (-N (inl prAB))
-NIsDecreasing {a} {b} (le x proof) | record { result = result ; pr = pr } = record { x = a ; proof = pr }

sumZeroImpliesSummandsZero : {a b : ℕ} → (a +N b ≡ zero) → ((a ≡ zero) && (b ≡ zero))
sumZeroImpliesSummandsZero {zero} {zero} pr = record { fst = refl ; snd = refl }
sumZeroImpliesSummandsZero {zero} {(succ b)} pr = record { fst = refl ; snd = pr }
sumZeroImpliesSummandsZero {(succ a)} {zero} ()
sumZeroImpliesSummandsZero {(succ a)} {(succ b)} ()

productWithNonzeroZero : (a b : ℕ) → (a *N succ b ≡ zero) → a ≡ zero
productWithNonzeroZero zero b pr = refl
productWithNonzeroZero (succ a) b ()

productOneImpliesOperandsOne : {a b : ℕ} → (a *N b ≡ 1) → (a ≡ 1) && (b ≡ 1)
productOneImpliesOperandsOne {zero} {b} ()
productOneImpliesOperandsOne {succ a} {zero} pr = exFalso absurd''
  where
    absurd : zero *N (succ a) ≡ 1
    absurd' : 0 ≡ 1
    absurd'' : False
    absurd'' = succIsNonzero (equalityCommutative absurd')
    absurd = identityOfIndiscernablesLeft _≡_ pr (productZeroIsZeroRight a)
    absurd' = absurd
productOneImpliesOperandsOne {succ a} {succ b} pr = record { fst = r' ; snd = (applyEquality succ (_&&_.fst q)) }
  where
    p : b +N a *N succ b ≡ zero
    p = succInjective pr
    q : (b ≡ zero) && (a *N succ b ≡ zero)
    q = sumZeroImpliesSummandsZero p
    r : a ≡ zero
    r = productWithNonzeroZero a b (_&&_.snd q)
    r' : succ a ≡ 1
    r' = applyEquality succ r

oneTimesPlusZero : (a : ℕ) → a ≡ a *N succ zero +N zero
oneTimesPlusZero a = identityOfIndiscernablesRight _≡_ (equalityCommutative (productWithOneRight a)) (equalityCommutative (addZeroRight (a *N succ zero)))

equivalentSubtraction' : (a b c d : ℕ) → (a<c : a <N c) → (d<b : d <N b) → (subtractionNResult.result (-N {a} {c} (inl a<c))) ≡ (subtractionNResult.result (-N {d} {b} (inl d<b))) → a +N b ≡ c +N d
equivalentSubtraction' a b c d prac prdb eq with -N (inl prac)
equivalentSubtraction' a b c d prac prdb eq | record { result = result ; pr = pr } with -N (inl prdb)
equivalentSubtraction' a b c d prac prdb refl | record { result = .result ; pr = pr1 } | record { result = result ; pr = pr } rewrite (equalityCommutative pr) = go
  where
    go : a +N (d +N result) ≡ c +N d
    go rewrite (equalityCommutative pr1) = t
      where
        t : a +N (d +N result) ≡ (a +N result) +N d
        t rewrite (additionNIsAssociative a result d) = applyEquality (λ n → a +N n) (additionNIsCommutative d result)

lessThanMeansPositiveSubtr : {a b : ℕ} → (a<b : a <N b) → (subtractionNResult.result (-N (inl a<b)) ≡ 0) → False
lessThanMeansPositiveSubtr {a} {b} a<b pr with -N (inl a<b)
lessThanMeansPositiveSubtr {a} {b} a<b pr | record { result = result ; pr = sub } rewrite pr | addZeroRight a = lessImpliesNotEqual a<b sub

moveOneSubtraction : {a b c : ℕ} → {a<=b : a ≤N b} → (subtractionNResult.result (-N {a} {b} a<=b)) ≡ c → b ≡ a +N c
moveOneSubtraction {a} {b} {zero} {inl a<b} pr rewrite addZeroRight a = exFalso (lessThanMeansPositiveSubtr {a} {b} a<b pr)
moveOneSubtraction {a} {b} {succ c} {inl a<b} pr with -N (inl a<b)
moveOneSubtraction {a} {b} {succ c} {inl a<b} pr | record { result = result ; pr = sub } rewrite pr | sub = refl
moveOneSubtraction {a} {.a} {zero} {inr refl} pr = equalityCommutative (addZeroRight a)
moveOneSubtraction {a} {.a} {succ c} {inr refl} pr = identityOfIndiscernablesRight _≡_ (equalityCommutative (addZeroRight a)) (applyEquality (λ t → a +N t) pr')
  where
    selfSub : (r : ℕ) → subtractionNResult.result (-N {r} {r} (inr refl)) ≡ zero
    selfSub zero = refl
    selfSub (succ r) = refl
    pr' : 0 ≡ succ c
    pr' = transitivity (equalityCommutative (selfSub a)) pr

moveOneSubtraction' : {a b c : ℕ} → {a<=b : a ≤N b} → (b ≡ a +N c) → subtractionNResult.result (-N {a} {b} a<=b) ≡ c
moveOneSubtraction' {a} {b} {c} {inl x} pr with -N (inl x)
moveOneSubtraction' {a} {b} {c} {inl x} pr | record { result = result ; pr = pr1 } rewrite pr = canSubtractFromEqualityLeft pr1
moveOneSubtraction' {a} {b} {c} {inr x} pr with -N (inr x)
moveOneSubtraction' {a} {b} {c} {inr x} pr | record { result = result ; pr = pr1 } rewrite pr = canSubtractFromEqualityLeft pr1

equivalentSubtraction : (a b c d : ℕ) → (a<c : a <N c) → (d<b : d <N b) → a +N b ≡ c +N d → (subtractionNResult.result (-N {a} {c} (inl a<c))) ≡ (subtractionNResult.result (-N {d} {b} (inl d<b)))
equivalentSubtraction zero b c d prac (le x proof) eq with (-N (inl (le x proof)))
equivalentSubtraction zero b c d prac (le x proof) eq | record { result = result ; pr = pr } = equalityCommutative p''
  where
    p : d +N result ≡ c +N d
    p = transitivity pr eq
    p' : d +N result ≡ d +N c
    p' = transitivity p (additionNIsCommutative c d)
    p'' : result ≡ c
    p'' = canSubtractFromEqualityLeft {d} {result} {c} p'
equivalentSubtraction (succ a) b zero d (le x ()) prdb eq
equivalentSubtraction (succ a) b (succ c) d prac prdb eq with (-N (inl (canRemoveSuccFrom<N prac)))
equivalentSubtraction (succ a) b (succ c) d prac prdb eq | record { result = c-a ; pr = prc-a } with -N (inl prdb)
equivalentSubtraction (succ a) b (succ c) d prac prdb eq | record { result = c-a ; pr = prc-a } | record { result = result ; pr = pr } rewrite equalityCommutative prc-a | equalityCommutative pr | equalityCommutative (additionNIsAssociative a d result) | additionNIsCommutative (a +N c-a) d | equalityCommutative (additionNIsAssociative d a c-a) | additionNIsCommutative a d = equalityCommutative (canSubtractFromEqualityLeft eq)

leLemma : (b c : ℕ) → (b ≤N c) ≡ (b +N zero ≤N c +N zero)
leLemma b c rewrite addZeroRight c = q
  where
    q : (b ≤N c) ≡ (b +N zero ≤N c)
    q rewrite addZeroRight b = refl

lessCast : {a b c : ℕ} → (pr : a ≤N b) → (eq : a ≡ c) → c ≤N b
lessCast {a} {b} pr eq rewrite eq = pr

lessCast' : {a b c : ℕ} → (pr : a ≤N b) → (eq : b ≡ c) → a ≤N c
lessCast' {a} {b} pr eq rewrite eq = pr

subtractionCast : {a b c : ℕ} → {pr : a ≤N b} → (eq : a ≡ c) → (p : subtractionNResult a b pr) → Sg (subtractionNResult c b (lessCast pr eq)) (λ res → subtractionNResult.result p ≡ subtractionNResult.result res)
subtractionCast {a} {b} {c} {a<b} eq subt rewrite eq = (subt , refl)

subtractionCast' : {a b c : ℕ} → {pr : a ≤N b} → (eq : b ≡ c) → (p : subtractionNResult a b pr) → Sg (subtractionNResult a c (lessCast' pr eq)) (λ res → subtractionNResult.result p ≡ subtractionNResult.result res)
subtractionCast' {a} {b} {c} {a<b} eq subt rewrite eq = (subt , refl)

addToRightWeakInequality : (a : ℕ) → {b c : ℕ} → (pr : b ≤N c) → (b ≤N c +N a)
addToRightWeakInequality zero {b} {c} (inl x) rewrite (addZeroRight c) = inl x
addToRightWeakInequality (succ a) {b} {c} (inl x) = inl (TotalOrder.<Transitive ℕTotalOrder x (addingIncreases c a))
addToRightWeakInequality zero {b} {.b} (inr refl) = inr (equalityCommutative (addZeroRight b))
addToRightWeakInequality (succ a) {b} {.b} (inr refl) = inl (addingIncreases b a)

addAssocLemma : (a b c : ℕ) → (a +N b) +N c ≡ (a +N c) +N b
addAssocLemma a b c rewrite (additionNIsAssociative a b c) = g
  where
    g : a +N (b +N c) ≡ (a +N c) +N b
    g rewrite (additionNIsAssociative a c b) = applyEquality (λ t → a +N t) (additionNIsCommutative b c)

addIntoSubtraction : (a : ℕ) → {b c : ℕ} → (pr : b ≤N c) → a +N (subtractionNResult.result (-N {b} {c} pr)) ≡ subtractionNResult.result (-N {b} {c +N a} (addToRightWeakInequality a pr))
addIntoSubtraction a {b} {c} pr with (-N {b} {c} pr)
addIntoSubtraction a {b} {c} pr | record { result = c-b ; pr = prc-b } with (-N {b} {c +N a} (addToRightWeakInequality a pr))
addIntoSubtraction a {b} {c} pr | record { result = c-b ; pr = prc-b } | record { result = c+a-b ; pr = prcab } = equalityCommutative g'''
  where
    g : (b +N c+a-b) +N c-b ≡ c +N (a +N c-b)
    g rewrite (equalityCommutative (additionNIsAssociative c a c-b)) = applyEquality (λ t → t +N c-b) prcab
    g' : (b +N c-b) +N c+a-b ≡ c +N (a +N c-b)
    g' = identityOfIndiscernablesLeft _≡_ g (addAssocLemma b c+a-b c-b)
    g'' : c +N c+a-b ≡ c +N (a +N c-b)
    g'' = identityOfIndiscernablesLeft _≡_ g' (applyEquality (λ i → i +N c+a-b) prc-b)
    g''' : c+a-b ≡ a +N c-b
    g''' = canSubtractFromEqualityLeft {c} {c+a-b} {a +N c-b} g''

addStrongInequalities : {a b c d : ℕ} → (a<b : a <N b) → (c<d : c <N d) → (a +N c <N b +N d)
addStrongInequalities {zero} {zero} {c} {d} prab prcd = prcd
addStrongInequalities {zero} {succ b} {c} {d} prab prcd rewrite (additionNIsCommutative b d) = TotalOrder.<Transitive ℕTotalOrder prcd (cannotAddAndEnlarge d b)
addStrongInequalities {succ a} {zero} {c} {d} (le x ()) prcd
addStrongInequalities {succ a} {succ b} {c} {d} prab prcd = succPreservesInequality (addStrongInequalities (canRemoveSuccFrom<N prab) prcd)

addWeakInequalities : {a b c d : ℕ} → (a<=b : a ≤N b) → (c<=d : c ≤N d) → (a +N c) ≤N (b +N d)
addWeakInequalities {a} {b} {c} {d} (inl x) (inl y) = inl (addStrongInequalities x y)
addWeakInequalities {a} {b} {c} {.c} (inl x) (inr refl) = inl (additionPreservesInequality c x)
addWeakInequalities {a} {.a} {c} {d} (inr refl) (inl x) = inl (additionPreservesInequalityOnLeft a x)
addWeakInequalities {a} {.a} {c} {.c} (inr refl) (inr refl) = inr refl

addSubIntoSub : {a b c d : ℕ} → (a<b : a ≤N b) → (c<d : c ≤N d) → (subtractionNResult.result (-N {a} {b} a<b)) +N (subtractionNResult.result (-N {c} {d} c<d)) ≡ subtractionNResult.result (-N {a +N c} {b +N d} (addWeakInequalities a<b c<d))
addSubIntoSub {a}{b}{c}{d} a<b c<d with (-N {a} {b} a<b)
addSubIntoSub {a} {b} {c} {d} a<b c<d | record { result = b-a ; pr = prb-a } with (-N {c} {d} c<d)
addSubIntoSub {a} {b} {c} {d} a<b c<d | record { result = b-a ; pr = prb-a } | record { result = d-c ; pr = prd-c } with (-N {a +N c} {b +N d} (addWeakInequalities a<b c<d))
addSubIntoSub {a} {b} {c} {d} a<b c<d | record { result = b-a ; pr = prb-a } | record { result = d-c ; pr = prd-c } | record { result = b+d-a-c ; pr = pr } = equalityCommutative r
  where
    pr' : (a +N c) +N b+d-a-c ≡ (a +N b-a) +N d
    pr' rewrite prb-a = pr
    pr'' : a +N (c +N b+d-a-c) ≡ (a +N b-a) +N d
    pr'' rewrite (equalityCommutative (additionNIsAssociative a c b+d-a-c)) = pr'
    pr''' : a +N (c +N b+d-a-c) ≡ a +N (b-a +N d)
    pr''' rewrite (equalityCommutative (additionNIsAssociative a b-a d)) = pr''
    q : c +N b+d-a-c ≡ b-a +N d
    q = canSubtractFromEqualityLeft {a} pr'''
    q' : c +N b+d-a-c ≡ b-a +N (c +N d-c)
    q' rewrite prd-c = q
    q'' : c +N b+d-a-c ≡ (b-a +N c) +N d-c
    q'' rewrite additionNIsAssociative b-a c d-c = q'
    q''' : c +N b+d-a-c ≡ (c +N b-a) +N d-c
    q''' rewrite additionNIsCommutative c b-a = q''
    q'''' : c +N b+d-a-c ≡ c +N (b-a +N d-c)
    q'''' rewrite equalityCommutative (additionNIsAssociative c b-a d-c) = q'''
    r : b+d-a-c ≡ b-a +N d-c
    r = canSubtractFromEqualityLeft {c} q''''

subtractProduct : {a b c : ℕ} → (aPos : 0 <N a) → (b<c : b <N c) →
    (a *N (subtractionNResult.result (-N (inl b<c)))) ≡ subtractionNResult.result (-N {a *N b} {a *N c} (inl (lessRespectsMultiplicationLeft b c a b<c aPos)))
subtractProduct {zero} {b} {c} aPos b<c = refl
subtractProduct {succ zero} {b} {c} aPos b<c = s'
  where
    resbc = -N {b} {c} (inl b<c)
    resbc' : Sg (subtractionNResult (b +N 0 *N b) c (lessCast (inl b<c) (equalityCommutative (addZeroRight b)))) ((λ res → subtractionNResult.result resbc ≡ subtractionNResult.result res))
    resbc'' : Sg (subtractionNResult (b +N 0 *N b) (c +N 0 *N c) (lessCast' (lessCast (inl b<c) (equalityCommutative (addZeroRight b))) (equalityCommutative (addZeroRight c)))) (λ res → subtractionNResult.result (underlying resbc') ≡ subtractionNResult.result res)
    g : (rsbc' : Sg (subtractionNResult (b +N 0 *N b) c (lessCast (inl b<c) (equalityCommutative (addZeroRight b)))) (λ res → subtractionNResult.result resbc ≡ subtractionNResult.result res)) → subtractionNResult.result resbc ≡ subtractionNResult.result (underlying rsbc')
    g' : (rsbc'' : Sg (subtractionNResult (b +N 0 *N b) (c +N 0 *N c) (lessCast' (lessCast (inl b<c) (equalityCommutative (addZeroRight b))) (equalityCommutative (addZeroRight c)))) (λ res → subtractionNResult.result (underlying resbc') ≡ subtractionNResult.result res)) → subtractionNResult.result (underlying resbc') ≡ subtractionNResult.result (underlying rsbc'')
    q : subtractionNResult.result resbc ≡ subtractionNResult.result (underlying resbc'')
    r : subtractionNResult.result (underlying resbc'') ≡ subtractionNResult.result (-N {b +N 0 *N b} {c +N 0 *N c} (inl (lessRespectsMultiplicationLeft b c 1 b<c aPos)))
    s : subtractionNResult.result resbc ≡ subtractionNResult.result (-N {b +N 0 *N b} {c +N 0 *N c} (inl (lessRespectsMultiplicationLeft b c 1 b<c aPos)))
    s = transitivity q r
    s' : subtractionNResult.result resbc +N 0 ≡ subtractionNResult.result (-N {b +N 0 *N b} {c +N 0 *N c} (inl (lessRespectsMultiplicationLeft b c 1 b<c aPos)))
    s' = identityOfIndiscernablesLeft _≡_ s (equalityCommutative (addZeroRight _))
    r = subtractionNWellDefined {b +N 0 *N b} {c +N 0 *N c} {(lessCast' (lessCast (inl b<c) (equalityCommutative (addZeroRight b))) (equalityCommutative (addZeroRight c)))} {inl (lessRespectsMultiplicationLeft b c 1 b<c aPos)} (underlying resbc'') (-N {b +N 0 *N b} {c +N 0 *N c} (inl (lessRespectsMultiplicationLeft b c 1 b<c aPos)))
    g (a , b) = b
    g' (a , b) = b
    resbc'' = subtractionCast' {pr = inl (identityOfIndiscernablesLeft _<N_ b<c (equalityCommutative (addZeroRight b)))} (equalityCommutative (addZeroRight c)) (underlying resbc')
    q = transitivity {_} {_} {subtractionNResult.result resbc} {subtractionNResult.result (underlying resbc')} {subtractionNResult.result (underlying resbc'')} (g resbc') (g' resbc'')
    resbc' = subtractionCast {b} {c} {b +N 0 *N b} {inl b<c} (equalityCommutative (addZeroRight b)) resbc

subtractProduct {succ (succ a)} {b} {c} aPos b<c =
  let
    t : (succ a) *N subtractionNResult.result (-N {b} {c} (inl b<c)) ≡ subtractionNResult.result (-N {(succ a) *N b} {(succ a) *N c} (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a))))
    t = subtractProduct {succ a} {b} {c} (succIsPositive a) b<c
  in
    go t  --go t
    where
      go : (succ a) *N subtractionNResult.result (-N {b} {c} (inl b<c)) ≡ subtractionNResult.result (-N {(succ a) *N b} {(succ a) *N c} (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a)))) → (subtractionNResult.result (-N (inl b<c)) +N (subtractionNResult.result (-N (inl b<c)) +N a *N subtractionNResult.result (-N (inl b<c))) ≡ subtractionNResult.result (-N (inl (lessRespectsMultiplicationLeft b c (succ (succ a)) b<c aPos))))
      go t = transitivity {_} {_} {lhs} {middle2} {rhs} u' v
        where
          c-b = subtractionNResult.result (-N {b} {c} (inl b<c))
          lhs = c-b +N (succ a) *N c-b
          middle = subtractionNResult.result (-N {(succ a) *N b} {(succ a) *N c} (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a))))
          middle2 = subtractionNResult.result (-N {b +N (succ a *N b)} {c +N (succ a *N c)} (addWeakInequalities (inl b<c) (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a)))))
          rhs = subtractionNResult.result (-N {succ (succ a) *N b} {(succ (succ a)) *N c} (inl (lessRespectsMultiplicationLeft b c (succ (succ a)) b<c aPos)))
          lhs' : lhs ≡ c-b +N middle
          u : c-b +N middle ≡ middle2
          u' : lhs ≡ middle2
          v : middle2 ≡ rhs
          u'' : c-b +N subtractionNResult.result (-N {(succ a) *N b} {(succ a) *N c} (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a)))) ≡ rhs

          u'' rewrite equalityCommutative v = u
          u' rewrite equalityCommutative u = lhs'
          lhs' rewrite t = refl
          u = addSubIntoSub (inl b<c) (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a)))
          v = subtractionNWellDefined {succ (succ a) *N b} {succ (succ a) *N c} {addWeakInequalities (inl b<c) (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a)))} {inl (lessRespectsMultiplicationLeft b c (succ (succ a)) b<c aPos)} (-N {b +N (succ a *N b)} {c +N (succ a *N c)} (addWeakInequalities (inl b<c) (inl (lessRespectsMultiplicationLeft b c (succ a) b<c (succIsPositive a))))) (-N {(succ (succ a)) *N b} {(succ (succ a)) *N c} (inl (lessRespectsMultiplicationLeft b c (succ (succ a)) b<c aPos)))

subtractProduct' : {a b c : ℕ} → (aPos : 0 <N a) → (b<c : b <N c) →
  (subtractionNResult.result (-N (inl b<c))) *N a ≡ subtractionNResult.result (-N {a *N b} {a *N c} (inl (lessRespectsMultiplicationLeft b c a b<c aPos)))
subtractProduct' {a} aPos b<c = identityOfIndiscernablesLeft _≡_ (subtractProduct aPos b<c) (multiplicationNIsCommutative a _)

equalityDecidable : (a b : ℕ) → (a ≡ b) || ((a ≡ b) → False)
equalityDecidable zero zero = inl refl
equalityDecidable zero (succ b) = inr naughtE
equalityDecidable (succ a) zero = inr λ t → naughtE (equalityCommutative t)
equalityDecidable (succ a) (succ b) with equalityDecidable a b
equalityDecidable (succ a) (succ b) | inl x = inl (applyEquality succ x)
equalityDecidable (succ a) (succ b) | inr x = inr (λ t → x (succInjective t))

cannotAddKeepingEquality : (a b : ℕ) → (a ≡ a +N succ b) → False
cannotAddKeepingEquality zero zero pr = naughtE pr
cannotAddKeepingEquality (succ a) zero pr = cannotAddKeepingEquality a zero (succInjective pr)
cannotAddKeepingEquality zero (succ b) pr = naughtE pr
cannotAddKeepingEquality (succ a) (succ b) pr = cannotAddKeepingEquality a (succ b) (succInjective pr)

<NWellDefined' : {a b c d : ℕ} → a ≡ c → b ≡ d → a <N b → c <N d
<NWellDefined' {a} {b} {c} {d} a=c b=d a<b rewrite a=c | b=d = a<b


{-# OPTIONS --show-implicit #-}
-- {-# OPTIONS -v tc.def.fun:10 -v tc.def.where:100 #-}

module PartialityMonad where

open import Common.Level
open import Common.Coinduction

record RawMonad {f} (M : Set f → Set f) : Set (lsuc f) where
  infixl 1 _>>=_

  field
    return : ∀ {A} → A → M A
    _>>=_  : ∀ {A B} → M A → (A → M B) → M B

------------------------------------------------------------------------
-- The partiality monad

data _⊥ {a} (A : Set a) : Set a where
  now   : (x : A) → A ⊥
  later : (x : ∞ (A ⊥)) → A ⊥

-- Fails if hidden pattern {f} is removed
monad : ∀ {f} → RawMonad {f = f} _⊥
-- monad {f} = record
monad = record
  { return = now
  ; _>>=_  = _>>=_
  }
  where
  _>>=_ : ∀ {A B} → A ⊥ → (A → B ⊥) → B ⊥
  now x   >>= f = f x
  later x >>= f = later (♯ (♭ x >>= f))


module Function.Iteration.Proofs where

import Lvl
open import Functional
open import Function.Names as Names using (_⊜_)
open import Function.Iteration
open import Function.Proofs
open import Logic.Propositional
open import Logic.Predicate
open import Numeral.Natural
open import Numeral.Natural.Oper using (_+_ ; _⋅_ ; _𝄩_)
open import Numeral.Natural.Oper.Proofs
import      Structure.Function.Names as Names
import      Structure.Function
open import Structure.Operator.Properties
open import Structure.Operator.Proofs.Util
import      Structure.Operator.Names as Names
import      Structure.Operator
open import Structure.Relator.Properties
open import Structure.Function.Domain
open import Syntax.Transitivity
open import Type

private variable ℓ ℓₑ ℓₑ₁ ℓₑ₂ ℓₑ₃ ℓₑ₄ : Lvl.Level
private variable T A B C X Y Z : Type{ℓ}

module _ where
  open import Structure.Setoid
  open        Structure.Function
  open        Structure.Operator

  module _ ⦃ equiv-X : Equiv{ℓₑ}(X) ⦄ where
    -- Propositions that state something about arbitrary composed functions also apply to arbitrary function iterations of the first function.
    [^]-from-[∘]-proof : ∀{ℓ₂}{P : (X → X) → Type{ℓ₂}} → (∀{f g : X → X} → P(f ∘ g)) → (∀{f : X → X}{n} → P(f ^ n))
    [^]-from-[∘]-proof {P = P} p {f} {𝟎}   = p{id}{id}
    [^]-from-[∘]-proof {P = P} p {f} {𝐒 n} = p{f}{f ^ n}

    [^]-function-raw : ∀{f : X → X} → Names.Congruence₁(f) → ∀{n} → Names.Congruence₁(f ^ n)
    [^]-function-raw func-f {𝟎}    xy = xy
    [^]-function-raw func-f {𝐒(n)} xy = func-f([^]-function-raw func-f {n} xy)

    -- Iterated function is a function when the function is.
    [^]-function : ∀{f : X → X} → ⦃ func : Function(f) ⦄ → ∀{n} → Function(f ^ n)
    Function.congruence ([^]-function ⦃ intro func-f ⦄ {n}) = [^]-function-raw func-f {n}

    [^]-injective-raw : ∀{f : X → X} → Names.Injective(f) → ∀{n} → Names.Injective(f ^ n)
    [^]-injective-raw inj-f {𝟎}    fnxfny = fnxfny
    [^]-injective-raw inj-f {𝐒(n)} fnxfny = [^]-injective-raw inj-f {n} (inj-f fnxfny)

    -- Iterated function is injective when the function is.
    [^]-injective : ∀{f : X → X} → ⦃ inj : Injective(f) ⦄ → ∀{n} → Injective(f ^ n)
    Injective.proof ([^]-injective ⦃ intro inj-f ⦄ {n}) = [^]-injective-raw inj-f {n}

    [^]-surjective-raw : ∀{f : X → X} → ⦃ func : Function(f) ⦄ → Names.Surjective(f) → ∀{n} → Names.Surjective(f ^ n)
    [^]-surjective-raw     surj-f {𝟎}    {y} = [∃]-intro y ⦃ reflexivity(_≡_) ⦄
    [^]-surjective-raw {f} surj-f {𝐒(n)} {y} = [∃]-map-proof (p ↦ (congruence₁(f) p) 🝖 [∃]-proof(surj-f {y})) ([^]-surjective-raw surj-f {n} {[∃]-witness(surj-f {y})})

    -- Iterated function is surjective when the function is.
    [^]-surjective : ∀{f : X → X} → ⦃ func : Function(f) ⦄ → ⦃ surj : Surjective(f) ⦄ → ∀{n} → Surjective(f ^ n)
    Surjective.proof ([^]-surjective ⦃ _ ⦄ ⦃ intro surj-f ⦄ {n}) = [^]-surjective-raw surj-f {n}

    -- Argument applied to the iterated function is one extra iteration.
    -- Note: This implies: (f ^ n)(f x) ≡ f((f ^ n)(x))
    [^]-inner-value : ∀{f : X → X} → ⦃ func : Function(f) ⦄ → ∀{x}{n} → ((f ^ n)(f x) ≡ (f ^ (𝐒(n)))(x))
    [^]-inner-value {f} {x} {𝟎}   = reflexivity(_≡_)
    [^]-inner-value {f} {x} {𝐒 n} = congruence₁(f) ([^]-inner-value {f} {x} {n})

    -- A fixpoint of the function is also a fixpoint of the iterated function.
    [^]-of-fixpoint : ∀{f : X → X} → ⦃ func : Function(f) ⦄ → ∀{x : X} → ⦃ fix : Fixpoint f(x) ⦄ → ∀{n} → ((f ^ n)(x) ≡ x)
    [^]-of-fixpoint {f} {x} {𝟎}    = reflexivity(_≡_)
    [^]-of-fixpoint {f} {x} {𝐒(n)} =
      (f ^ 𝐒(n))(x)    🝖-[ reflexivity(_≡_) ]
      (f ∘ (f ^ n))(x) 🝖-[ reflexivity(_≡_) ]
      f((f ^ n)(x))    🝖-[ congruence₁(f) ([^]-of-fixpoint {f} {x} {n}) ]
      f(x)             🝖-[ fixpoint f(x) ]
      x                🝖-end

  module _ ⦃ equiv-XX : Equiv{ℓₑ}(X → X) ⦄ where
    [^]-by-1 : ∀{f : X → X} → (f ^ 1 ≡ f)
    [^]-by-1 {f} = reflexivity(_≡_)

    [^]-of-id : ∀{n} → (id ^ n ≡ id)
    [^]-of-id {𝟎}   = reflexivity(_≡_)
    [^]-of-id {𝐒 n} = [^]-of-id {n}

    [^]-inner : ∀{f : X → X} → ⦃ _ : Function(f ∘_) ⦄ → ∀{n} → ((f ^ n) ∘ f ≡ f ^ (𝐒(n)))
    [^]-inner {f} {𝟎}   = reflexivity(_≡_)
    [^]-inner {f} {𝐒 n} = congruence₁(f ∘_) ([^]-inner {f} {n})

    [^]-add : ⦃ [∘]-op : BinaryOperator(_∘_) ⦄ → ∀{f : X → X} → ∀{a b} → ((f ^ a) ∘ (f ^ b) ≡ f ^ (a + b))
    [^]-add {f} {𝟎} {𝟎}     = reflexivity(_≡_)
    [^]-add {f} {𝟎} {𝐒 b}   = reflexivity(_≡_)
    [^]-add {f} {𝐒 a} {𝟎}   = reflexivity(_≡_)
    [^]-add ⦃ [∘]-op ⦄ {f} {𝐒 a} {𝐒 b} =
      (f ^ 𝐒(a)) ∘ (f ^ 𝐒(b))    🝖-[ reflexivity(_≡_) ]
      (f ^ 𝐒(a)) ∘ (f ∘ (f ^ b)) 🝖-[ reflexivity(_≡_) ]
      ((f ^ 𝐒(a)) ∘ f) ∘ (f ^ b) 🝖-[ congruence₂ₗ(_∘_)(f ^ b) ([^]-inner {f} ⦃ [≡]-congruence2-right(_∘_)(f) ⦄ {𝐒(a)}) ]
      f ∘ ((f ^ 𝐒(a)) ∘ (f ^ b)) 🝖-[ reflexivity(_≡_) ]
      (f ∘ (f ^ 𝐒(a))) ∘ (f ^ b) 🝖-[ congruence₂ᵣ(_∘_)(f) ([^]-add{f} {𝐒 a} {b}) ]
      f ∘ (f ^ (𝐒(a) + b))       🝖-[ reflexivity(_≡_) ]
      f ^ (𝐒(a) + 𝐒(b))          🝖-end

    [^]-multiply : ⦃ [∘]-op : BinaryOperator(_∘_) ⦄ → ∀{f : X → X} → ∀{a b} → ((f ^ a) ^ b ≡ f ^ (a ⋅ b))
    [^]-multiply ⦃ [∘]-op ⦄ {f} {𝟎}   {𝟎}   = reflexivity(_≡_)
    [^]-multiply ⦃ [∘]-op ⦄ {f} {𝟎}   {𝐒 b} = [^]-of-id {𝐒 b}
    [^]-multiply ⦃ [∘]-op ⦄ {f} {𝐒 a} {𝟎}   = reflexivity(_≡_)
    [^]-multiply ⦃ [∘]-op ⦄ {f} {𝐒 a} {𝐒 b} =
      (f ^ 𝐒(a)) ^ 𝐒(b)             🝖-[ reflexivity(_≡_) ]
      (f ^ 𝐒(a)) ∘ ((f ^ 𝐒(a)) ^ b) 🝖-[ congruence₂ᵣ(_∘_)(f ^ 𝐒(a)) ([^]-multiply{f} {𝐒 a} {b}) ]
      (f ^ 𝐒(a)) ∘ (f ^ (𝐒(a) ⋅ b)) 🝖-[ [^]-add {f} {𝐒(a)} {𝐒(a) ⋅ b} ]
      f ^ (𝐒(a) + (𝐒(a) ⋅ b))       🝖-[ reflexivity(_≡_) ]
      f ^ (𝐒(a) ⋅ 𝐒(b))             🝖-end

    [^]-distanceₗ : ⦃ [∘]-op : BinaryOperator(_∘_) ⦄ → ∀{f : X → X}{a b} → (f ^ a ≡ f ^ b) ← (f ^ (a 𝄩 b) ≡ id)
    [^]-distanceₗ {f} {𝟎}   {𝟎}   = id
    [^]-distanceₗ {f} {𝟎}   {𝐒 b} = symmetry(_≡_)
    [^]-distanceₗ {f} {𝐒 a} {𝟎}   = id
    [^]-distanceₗ {f} {𝐒 a} {𝐒 b} = congruence₂ᵣ(_∘_)(f) ∘ ([^]-distanceₗ {f} {a} {b})

    [^]-distanceᵣ : ⦃ [∘]-op : BinaryOperator(_∘_) ⦄ → ⦃ [∘]-cancₗ : Cancellationₗ(_∘_) ⦄ → ∀{f : X → X}{a b} → (f ^ a ≡ f ^ b) → (f ^ (a 𝄩 b) ≡ id)
    [^]-distanceᵣ {f} {𝟎}   {𝟎}   = id
    [^]-distanceᵣ {f} {𝟎}   {𝐒 b} = symmetry(_≡_)
    [^]-distanceᵣ {f} {𝐒 a} {𝟎}   = id
    [^]-distanceᵣ {f} {𝐒 a} {𝐒 b} p = [^]-distanceᵣ {f} {a} {b} (cancellationₗ(_∘_) {f} p)

    module _ ⦃ op : BinaryOperator(_∘_) ⦄ ⦃ assoc : Associativity(_∘_) ⦄ where
      [^]-commuting : ∀{f g : X → X} → Names.Commuting(_∘_)(f)(g) → ∀{a b} → Names.Commuting(_∘_)(f ^ a)(g ^ b)
      [^]-commuting {f} {g} com {𝟎}   {𝟎}   = reflexivity(_≡_)
      [^]-commuting {f} {g} com {𝟎}   {𝐒 b} = reflexivity(_≡_)
      [^]-commuting {f} {g} com {𝐒 a} {𝟎}   = reflexivity(_≡_)
      [^]-commuting {f} {g} com {𝐒 a} {𝐒 b} =
        (f ^ 𝐒(a)) ∘ (g ^ 𝐒(b))       🝖-[ reflexivity(_≡_) ]
        (f ∘ (f ^ a)) ∘ (g ∘ (g ^ b)) 🝖-[ One.associate-commute4 {a = f} {f ^ a} {g} {g ^ b} ([^]-commuting {f} {g} com {a} {1}) ]
        (f ∘ g) ∘ ((f ^ a) ∘ (g ^ b)) 🝖-[ congruence₂(_∘_) com ([^]-commuting {f} {g} com {a} {b}) ]
        (g ∘ f) ∘ ((g ^ b) ∘ (f ^ a)) 🝖-[ One.associate-commute4 {a = g} {f} {g ^ b} {f ^ a} ([^]-commuting {f} {g} com {1} {b}) ]
        (g ∘ (g ^ b)) ∘ (f ∘ (f ^ a)) 🝖-[ reflexivity(_≡_) ]
        (g ^ 𝐒(b)) ∘ (f ^ 𝐒(a))       🝖-end

      [^]-of-[∘] :  ∀{f : X → X}{g : X → X} → Names.Commuting(_∘_)(f)(g) → ∀{n} → ((f ∘ g) ^ n ≡ (f ^ n) ∘ (g ^ n))
      [^]-of-[∘] {f}{g} com {𝟎}   = reflexivity(_≡_)
      [^]-of-[∘] {f}{g} com {𝐒 n} =
        (f ∘ g) ^ 𝐒(n)                🝖-[ reflexivity(_≡_) ]
        (f ∘ g) ∘ ((f ∘ g) ^ n)       🝖-[ congruence₂ᵣ(_∘_)(f ∘ g) ([^]-of-[∘] {f}{g} com {n}) ]
        (f ∘ g) ∘ ((f ^ n) ∘ (g ^ n)) 🝖-[ One.associate-commute4 {a = f} {g} {f ^ n}{g ^ n} (symmetry(_≡_) ([^]-commuting {f} {g} com {n} {1})) ]
        (f ∘ (f ^ n)) ∘ (g ∘ (g ^ n)) 🝖-[ reflexivity(_≡_) ]
        (f ^ 𝐒(n)) ∘ (g ^ 𝐒(n))       🝖-end

  module _ {ℓ₁}{ℓ₂} {X : Type{ℓ₁}} ⦃ equiv-x : Equiv{ℓₑ₁}(X) ⦄ {Y : Type{ℓ₂}} ⦃ equiv-y : Equiv{ℓₑ₂}(Y) ⦄ where
    private variable n : ℕ
    private variable x : X
    private variable init : Y

    repeatᵣₗ-flip-equality : ∀{_▫_ : Y → X → Y} → ⦃ op : BinaryOperator(_▫_) ⦄ → (repeatᵣ n (swap(_▫_)) x init ≡ repeatₗ n (_▫_) init x)
    repeatᵣₗ-flip-equality {n = 𝟎}                      = reflexivity(_≡_)
    repeatᵣₗ-flip-equality {n = 𝐒(n)}{x = x}{_▫_ = _▫_} = congruence₂ₗ(_▫_)(x) (repeatᵣₗ-flip-equality {n = n}{_▫_ = _▫_})

    repeatₗᵣ-flip-equality : ∀{_▫_ : X → Y → Y} → ⦃ op : BinaryOperator(_▫_) ⦄ → (repeatₗ n (swap _▫_) init x ≡ repeatᵣ n (_▫_) x init)
    repeatₗᵣ-flip-equality {n = n}{init = init}{x = x}{_▫_ = _▫_} = symmetry(_≡_) (repeatᵣₗ-flip-equality {n = n}{x = x}{init = init}{_▫_ = swap(_▫_)} ⦃ op = swap-binaryOperator ⦄)

  module _ ⦃ equiv-X : Equiv{ℓₑ}(X) ⦄ where
    private variable f : X → X
    private variable _▫_ : X → X → X
    private variable x elem init : X
    private variable n : ℕ

    [^]-from-repeatᵣ-alt : ⦃ func : Function(f) ⦄ → ((f ^ n) ⊜ repeatᵣ(n) (f ∘_) id)
    [^]-from-repeatᵣ-alt    {n = 𝟎}   = reflexivity(_≡_)
    [^]-from-repeatᵣ-alt {f}{n = 𝐒 n} = congruence₁(f) ([^]-from-repeatᵣ-alt {n = n})

    [^]-from-repeatᵣ : ⦃ func : Function(f) ⦄ → ((f ^ n) ⊜ repeatᵣ(n) (_∘_) f id)
    [^]-from-repeatᵣ    {n = 𝟎}   = reflexivity(_≡_)
    [^]-from-repeatᵣ {f}{n = 𝐒 n} = congruence₁(f) ([^]-from-repeatᵣ {f}{n = n})

    -- TODO: Should also be provable using associativity? Prove (CommutingOn(_▫_)(x)(x) → AssociativityOn(_▫_)(x)). Is this helping?
    repeat-swap-side : ⦃ op : BinaryOperator(_▫_) ⦄ ⦃ comm : Commutativity(_▫_) ⦄ → (repeatₗ n (_▫_) x x ≡ repeatᵣ n (_▫_) x x)
    repeat-swap-side            {n = 𝟎}      = reflexivity(_≡_)
    repeat-swap-side {_▫_ = _▫_}{n = 𝐒 n}{x} = congruence₂ₗ(_▫_)(x) (repeat-swap-side {n = n}) 🝖 commutativity(_▫_)

    repeat-swap-side-by-associativity : ⦃ op : BinaryOperator(_▫_) ⦄ ⦃ _ : Associativity(_▫_) ⦄ → (repeatₗ n (_▫_) x x ≡ repeatᵣ n (_▫_) x x)
    repeat-swap-side-by-associativity             {n = 𝟎}         = reflexivity(_≡_)
    repeat-swap-side-by-associativity             {n = 𝐒 𝟎}   {x} = reflexivity(_≡_)
    repeat-swap-side-by-associativity {_▫_ = _▫_} {n = 𝐒(𝐒 n)}{x} =
      repeatₗ (𝐒(𝐒(n))) (_▫_) x x        🝖[ _≡_ ]-[]
      repeatₗ (𝐒(n)) (_▫_) x x ▫ x       🝖[ _≡_ ]-[ congruence₂ₗ(_▫_)(x) (repeat-swap-side-by-associativity {n = 𝐒 n}) ]
      repeatᵣ (𝐒(n)) (_▫_) x x ▫ x       🝖[ _≡_ ]-[]
      (x ▫ repeatᵣ n (_▫_) x x) ▫ x      🝖[ _≡_ ]-[ associativity(_▫_) ]
      x ▫ (repeatᵣ n (_▫_) x x ▫ x)      🝖[ _≡_ ]-[ congruence₂ᵣ(_▫_)(x) (congruence₂ₗ(_▫_)(x) (repeat-swap-side-by-associativity {n = n})) ]-sym
      x ▫ (repeatₗ n (_▫_) x x ▫ x)      🝖[ _≡_ ]-[]
      x ▫ repeatₗ (𝐒(n)) (_▫_) x x       🝖[ _≡_ ]-[ congruence₂ᵣ(_▫_)(x) (repeat-swap-side-by-associativity {n = 𝐒(n)}) ]
      x ▫ repeatᵣ (𝐒(n)) (_▫_) x x       🝖[ _≡_ ]-[]
      repeatᵣ (𝐒(𝐒(n))) (_▫_) x x        🝖[ _≡_ ]-end

    repeat-with-id-swap-side : ⦃ op : BinaryOperator(_▫_) ⦄ ⦃ comm : Commutativity(_▫_) ⦄ ⦃ ident : Identity(_▫_)(init) ⦄ → (repeatₗ n (_▫_) init x ≡ repeatᵣ n (_▫_) x init)
    repeat-with-id-swap-side {n = 𝟎} = reflexivity(_≡_)
    repeat-with-id-swap-side {_▫_ = _▫_}{n = 𝐒 n}{x = x} = congruence₂ₗ(_▫_)(x) (repeat-with-id-swap-side {n = n}) 🝖 commutativity(_▫_)

    repeat-raise-equality : ⦃ op : BinaryOperator(_▫_) ⦄ → (repeatᵣ n (_▫_) elem (x) ≡ ((elem ▫_) ^ n)(x))
    repeat-raise-equality           {n = 𝟎}             = reflexivity(_≡_)
    repeat-raise-equality{_▫_ = _▫_}{n = 𝐒(n)}{elem}{x} = congruence₂ᵣ(_▫_)(elem) (repeat-raise-equality{_▫_ = _▫_}{n = n}{elem}{x})

module _ {X : Type{ℓ}} where
  open import Relator.Equals
  open import Relator.Equals.Proofs

  raise-repeat-equality : ∀{n : ℕ}{f : X → X} → (f ^ n ≡ repeatᵣ n (_∘_) f id)
  raise-repeat-equality{𝟎}       = reflexivity(_≡_)
  raise-repeat-equality{𝐒(n)}{f} = [≡]-with(f ∘_) (raise-repeat-equality{n}{f})

module _ where
  open import Structure.Setoid
  open        Structure.Function
  open        Structure.Operator

  module _ ⦃ equiv-X : Equiv{ℓₑ}(X) ⦄ where
    repeatₗ-by-0 : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → (repeatᵣ 0 (_▫_) x id ≡ id)
    repeatₗ-by-0 {_▫_} {x}{id} ⦃ identᵣ ⦄ = reflexivity(_≡_)

    repeatₗ-by-1 : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → (repeatᵣ 1 (_▫_) x id ≡ x)
    repeatₗ-by-1 {_▫_} {x}{id} ⦃ identᵣ ⦄ = identityᵣ(_▫_)(id)

    repeatₗ-by-sum : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : BinaryOperator(_▫_) ⦄ → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → ∀{a b} → ((repeatₗ a (_▫_) id x) ▫ (repeatₗ b (_▫_) id x) ≡ repeatₗ (a + b) (_▫_) id x)
    repeatₗ-by-sum {_▫_} {x} {id} ⦃ identᵣ ⦄ {a} {𝟎}   =
      (repeatₗ a (_▫_) id x) ▫ (repeatₗ 𝟎 (_▫_) id x) 🝖-[ reflexivity(_≡_) ]
      (repeatₗ a (_▫_) id x) ▫ id                     🝖-[ identityᵣ(_▫_)(id) ]
      repeatₗ a (_▫_) id x                            🝖-[ reflexivity(_≡_) ]
      repeatₗ (a + 𝟎) (_▫_) id x                      🝖-end
    repeatₗ-by-sum {_▫_} {x} {id} ⦃ identᵣ ⦄ {a} {𝐒 b} =
      (repeatₗ a (_▫_) id x) ▫ (repeatₗ (𝐒(b)) (_▫_) id x)  🝖-[ reflexivity(_≡_) ]
      (repeatₗ a (_▫_) id x) ▫ ((repeatₗ b (_▫_) id x) ▫ x) 🝖-[ symmetry(_≡_) (associativity(_▫_)) ]
      ((repeatₗ a (_▫_) id x) ▫ (repeatₗ b (_▫_) id x)) ▫ x 🝖-[ congruence₂ₗ(_▫_)(_) (repeatₗ-by-sum{a = a}{b = b}) ]
      (repeatₗ (a + b) (_▫_) id x) ▫ x                      🝖-[ reflexivity(_≡_) ]
      repeatₗ (a + 𝐒(b)) (_▫_) id x                         🝖-end

    repeatₗ-by-product : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : BinaryOperator(_▫_) ⦄ → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → ∀{a b} → (repeatₗ b (_▫_) id ((repeatₗ a (_▫_) id x)) ≡ repeatₗ (a ⋅ b) (_▫_) id x)
    repeatₗ-by-product {_▫_} {x} {id} ⦃ identᵣ ⦄ {a} {𝟎}   =
      repeatₗ 𝟎 (_▫_) id ((repeatₗ a (_▫_) id x)) 🝖-[ reflexivity(_≡_) ]
      repeatₗ (a ⋅ 𝟎) (_▫_) id x                  🝖-end
    repeatₗ-by-product {_▫_} {x} {id} ⦃ identᵣ ⦄ {a} {𝐒 b} =
      repeatₗ (𝐒(b)) (_▫_) id ((repeatₗ a (_▫_) id x))                       🝖-[ reflexivity(_≡_) ]
      (repeatₗ b (_▫_) id ((repeatₗ a (_▫_) id x))) ▫ (repeatₗ a (_▫_) id x) 🝖-[ congruence₂ₗ(_▫_)(_) (repeatₗ-by-product{a = a}{b = b}) ]
      (repeatₗ (a ⋅ b) (_▫_) id x) ▫ (repeatₗ a (_▫_) id x)                  🝖-[ repeatₗ-by-sum {a = a ⋅ b}{a} ]
      repeatₗ ((a ⋅ b) + a) (_▫_) id x                                       🝖-[ [≡]-to-equivalence (congruence₁(expr ↦ repeatₗ expr (_▫_) id x) {a ⋅ b + a}{a + a ⋅ b} (commutativity(_+_) {a ⋅ b})) ]
      repeatₗ (a ⋅ 𝐒(b)) (_▫_) id x                                          🝖-end
      where
        open import Relator.Equals.Proofs.Equiv using ([≡]-to-equivalence)

    repeatₗ-by-distanceₗ : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : BinaryOperator(_▫_) ⦄ → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → ∀{a b} → (repeatₗ a (_▫_) id x ≡ repeatₗ b (_▫_) id x) ← (repeatₗ (a 𝄩 b) (_▫_) id x ≡ id)
    repeatₗ-by-distanceₗ {_▫_} {x} {id} {𝟎}   {𝟎}   p = p
    repeatₗ-by-distanceₗ {_▫_} {x} {id} {𝟎}   {𝐒 b} p = symmetry(_≡_) p
    repeatₗ-by-distanceₗ {_▫_} {x} {id} {𝐒 a} {𝟎}   p = p
    repeatₗ-by-distanceₗ {_▫_} {x} {id} {𝐒 a} {𝐒 b} p = congruence₂ₗ(_▫_)(_) (repeatₗ-by-distanceₗ {_▫_} {x} {id} {a} {b} p)

    repeatₗ-by-distanceᵣ : ∀{_▫_ : X → X → X}{x id} → ⦃ _ : BinaryOperator(_▫_) ⦄ → ⦃ _ : Identityᵣ(_▫_)(id) ⦄ → ⦃ _ : Associativity(_▫_) ⦄ → ⦃ cancᵣ : Cancellationᵣ(_▫_) ⦄ → ∀{a b} → (repeatₗ a (_▫_) id x ≡ repeatₗ b (_▫_) id x) → (repeatₗ (a 𝄩 b) (_▫_) id x ≡ id)
    repeatₗ-by-distanceᵣ {_▫_} {x} {id} {𝟎}   {𝟎}   p = p
    repeatₗ-by-distanceᵣ {_▫_} {x} {id} {𝟎}   {𝐒 b} p = symmetry(_≡_) p
    repeatₗ-by-distanceᵣ {_▫_} {x} {id} {𝐒 a} {𝟎}   p = p
    repeatₗ-by-distanceᵣ {_▫_} {x} {id} {𝐒 a} {𝐒 b} p = repeatₗ-by-distanceᵣ {_▫_} {x} {id} {a} {b} (cancellationᵣ(_▫_) {x} p)


-- Andreas, 2019-03-02, issue #3601 reported by 3abc

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

open import Agda.Primitive.Cubical renaming
  (primINeg to ~_; primIMin to _∧_; primTransp to transp)

open import Agda.Builtin.Cubical.Path

module _ (A : Set) (x y z t : A) (f : y ≡ z) (g : y ≡ x) (h : z ≡ t) where

  test : PathP (λ i → g i ≡ h i) f (transp (λ i → g i ≡ h i) i0 f)
  test k i = transp (λ i → g (i ∧ k) ≡ h (i ∧ k)) (~ k) f i

-- Should pass.
-- Problem was: Agda did not accept overapplied primTransp

  -- Also should pass.
  -- We had a problem with checking the sides at the right type.
  test2 : ∀ {ℓ} (i : I) {A : Partial i (Set ℓ)}
          → PartialP i (λ z → A z)
          → (r : IsOne i)
          → A r
  test2 i {A} u r = primPOr i i {A} u (λ o → u o) r


------------------------------------------------------------------------
-- The Agda standard library
--
-- Container Morphisms
------------------------------------------------------------------------

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

module Data.Container.Morphism where

open import Data.Container.Core
import Function as F

module _ {s p} (C : Container s p) where

  id : C ⇒ C
  id .shape    = F.id
  id .position = F.id

module _ {s₁ s₂ s₃ p₁ p₂ p₃}
         {C₁ : Container s₁ p₁} {C₂ : Container s₂ p₂} {C₃ : Container s₃ p₃}
         where

  infixr 9 _∘_
  _∘_ : C₂ ⇒ C₃ → C₁ ⇒ C₂ → C₁ ⇒ C₃
  (f ∘ g) .shape    = shape    f F.∘′ shape    g
  (f ∘ g) .position = position g F.∘′ position f


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of the polymorphic unit type
-- Defines Decidable Equality and Decidable Ordering as well
------------------------------------------------------------------------

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

module Data.Unit.Polymorphic.Properties where

open import Level
open import Data.Sum.Base using (inj₁)
open import Data.Unit.Polymorphic.Base using (⊤; tt)
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality

private
  variable
    ℓ : Level

------------------------------------------------------------------------
-- Equality
------------------------------------------------------------------------

infix 4 _≟_

_≟_ : Decidable {A = ⊤ {ℓ}} _≡_
_ ≟ _ = yes refl

≡-setoid : ∀ ℓ → Setoid ℓ ℓ
≡-setoid _ = setoid ⊤

≡-decSetoid : ∀ ℓ → DecSetoid ℓ ℓ
≡-decSetoid _ = decSetoid _≟_

------------------------------------------------------------------------
-- Ordering
------------------------------------------------------------------------

≡-total : Total {A = ⊤ {ℓ}} _≡_
≡-total _ _ = inj₁ refl

≡-antisym : Antisymmetric {A = ⊤ {ℓ}} _≡_ _≡_
≡-antisym p _ = p

------------------------------------------------------------------------
-- Structures

≡-isPreorder : ∀ ℓ → IsPreorder {ℓ} {_} {⊤} _≡_ _≡_
≡-isPreorder ℓ = record
  { isEquivalence = isEquivalence
  ; reflexive     = λ x → x
  ; trans         = trans
  }

≡-isPartialOrder : ∀ ℓ → IsPartialOrder {ℓ} _≡_ _≡_
≡-isPartialOrder ℓ = record
  { isPreorder = ≡-isPreorder ℓ
  ; antisym    = ≡-antisym
  }

≡-isTotalOrder : ∀ ℓ → IsTotalOrder {ℓ} _≡_ _≡_
≡-isTotalOrder ℓ = record
  { isPartialOrder = ≡-isPartialOrder ℓ
  ; total          = ≡-total
  }

≡-isDecTotalOrder : ∀ ℓ → IsDecTotalOrder {ℓ} _≡_ _≡_
≡-isDecTotalOrder ℓ = record
  { isTotalOrder = ≡-isTotalOrder ℓ
  ; _≟_          = _≟_
  ; _≤?_         = _≟_
  }

------------------------------------------------------------------------
-- Bundles

≡-preorder : ∀ ℓ → Preorder ℓ ℓ ℓ
≡-preorder ℓ = record
  { isPreorder = ≡-isPreorder ℓ
  }

≡-poset : ∀ ℓ → Poset ℓ ℓ ℓ
≡-poset ℓ = record
  { isPartialOrder = ≡-isPartialOrder ℓ
  }

≡-totalOrder : ∀ ℓ → TotalOrder ℓ ℓ ℓ
≡-totalOrder ℓ = record
  { isTotalOrder = ≡-isTotalOrder ℓ
  }

≡-decTotalOrder : ∀ ℓ → DecTotalOrder ℓ ℓ ℓ
≡-decTotalOrder ℓ = record
  { isDecTotalOrder = ≡-isDecTotalOrder ℓ
  }


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties satisfied by bounded lattice
------------------------------------------------------------------------

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

open import Relation.Binary.Lattice

module Relation.Binary.Properties.BoundedLattice
  {c ℓ₁ ℓ₂} (L : BoundedLattice c ℓ₁ ℓ₂) where

open BoundedLattice L

open import Algebra.FunctionProperties _≈_
open import Data.Product using (_,_)
open import Relation.Binary using (Setoid)
open import Relation.Binary.Properties.MeetSemilattice meetSemilattice
open import Relation.Binary.Properties.JoinSemilattice joinSemilattice

open Setoid setoid renaming (trans to ≈-trans)

∧-zeroʳ : RightZero ⊥ _∧_
∧-zeroʳ x = y≤x⇒x∧y≈y (minimum x)

∧-zeroˡ : LeftZero ⊥ _∧_
∧-zeroˡ x = ≈-trans (∧-comm ⊥ x) (∧-zeroʳ x)

∧-zero : Zero ⊥ _∧_
∧-zero = ∧-zeroˡ , ∧-zeroʳ

∨-zeroʳ : RightZero ⊤ _∨_
∨-zeroʳ x = x≤y⇒x∨y≈y (maximum x)

∨-zeroˡ : LeftZero ⊤ _∨_
∨-zeroˡ x = ≈-trans (∨-comm ⊤ x) (∨-zeroʳ x)

∨-zero : Zero ⊤ _∨_
∨-zero = ∨-zeroˡ , ∨-zeroʳ


module _ where

open import Common.Prelude renaming (_∸_ to _-_) -- work-around for #1855

_<_ : Nat → Nat → Bool
a < b with b - a
... | zero  = false
... | suc _ = true

insert : Nat → List Nat → List Nat
insert x [] = x ∷ []
insert x (y ∷ xs) = if x < y then x ∷ y ∷ xs else (y ∷ insert x xs)

sort : List Nat → List Nat
sort [] = []
sort (x ∷ xs) = insert x (sort xs)

downFrom : Nat → List Nat
downFrom zero = []
downFrom (suc n) = n ∷ downFrom n

mapM! : {A : Set} → (A → IO Unit) → List A → IO Unit
mapM! f [] = return unit
mapM! f (x ∷ xs) = f x >>= λ _ → mapM! f xs

main : IO Unit
main = mapM! printNat (sort (downFrom 1200))

-- 1000   0.6s
-- 2000   4.8s
-- 4000   36.2s


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

{-
  Useful notation for Epimorphisms, Mononmorphisms, and isomorphisms
-}
module Categories.Morphism.Notation where

open import Level

open import Categories.Category.Core
open import Categories.Morphism

private
  variable
    o ℓ e : Level

_[_↣_] : (𝒞 : Category o ℓ e) → (A B : Category.Obj 𝒞) → Set (o ⊔ ℓ ⊔ e)
𝒞 [ A ↣ B ] = _↣_ 𝒞 A B

_[_↠_] : (𝒞 : Category o ℓ e) → (A B : Category.Obj 𝒞) → Set (o ⊔ ℓ ⊔ e)
𝒞 [ A ↠ B ] = _↠_ 𝒞 A B

_[_≅_] : (𝒞 : Category o ℓ e) → (A B : Category.Obj 𝒞) → Set (ℓ ⊔ e)
𝒞 [ A ≅ B ] = _≅_ 𝒞 A B


module Prelude.Applicative.Indexed {i} {I : Set i} where

open import Agda.Primitive
open import Prelude.Unit
open import Prelude.Functor
open import Prelude.Function
open import Prelude.Equality
open import Prelude.Number
open import Prelude.Semiring
open import Prelude.Fractional

record IApplicative {a b} (F : I → I → Set a → Set b) : Set (i ⊔ lsuc a ⊔ b) where
  infixl 4 _<*>_ _<*_ _*>_
  field
    pure  : ∀ {A i} → A → F i i A
    _<*>_ : ∀ {A B i j k} → F i j (A → B) → F j k A → F i k B
    overlap {{super}} : ∀ {i j} → Functor (F i j)

  _<*_ : ∀ {A B i j k} → F i j A → F j k B → F i k A
  a <* b = ⦇ const a b ⦈

  _*>_ : ∀ {A B i j k} → F i j A → F j k B → F i k B
  a *> b = ⦇ (const id) a b ⦈

open IApplicative {{...}} public

{-# DISPLAY IApplicative.pure  _ = pure  #-}
{-# DISPLAY IApplicative._<*>_ _ = _<*>_ #-}
{-# DISPLAY IApplicative._<*_  _ = _<*_  #-}
{-# DISPLAY IApplicative._*>_  _ = _*>_  #-}

-- Level polymorphic functors
record IApplicative′ {a b} (F : ∀ {a} → I → I → Set a → Set a) : Set (i ⊔ lsuc (a ⊔ b)) where
  infixl 4 _<*>′_
  field
    _<*>′_ : {A : Set a} {B : Set b} {i j k : I} → F i j (A → B) → F j k A → F i k B
    overlap {{super}} : ∀ {i j} → Functor′ {a} {b} (F i j)

open IApplicative′ {{...}} public

module _ {F : ∀ {a} → I → I → Set a → Set a}
         {{_ : ∀ {a} → IApplicative {a = a} F}}
         {{_ : ∀ {a b} → IApplicative′ {a = a} {b = b} F}}
         {a b} {A : Set a} {B : Set b} {i j k} where

  infixl 4 _<*′_ _*>′_
  _<*′_ : F i j A → F j k B → F i k A
  a <*′ b = pure const <*>′ a <*>′ b

  _*>′_ : F i j A → F j k B → F i k B
  a *>′ b = pure (const id) <*>′ a <*>′ b


open import Prelude
open import Nat
open import dynamics-core
open import contexts

module lemmas-disjointness where
  -- disjointness is commutative
  ##-comm : {A : Set} {Δ1 Δ2 : A ctx} → Δ1 ## Δ2 → Δ2 ## Δ1
  ##-comm (π1 , π2) = π2 , π1

  -- the empty context is disjoint from any context
  empty-disj : {A : Set} (Γ : A ctx) → ∅ ## Γ
  empty-disj Γ = ed1 , ed2
   where
    ed1 : {A : Set} (n : Nat) → dom {A} ∅ n → n # Γ
    ed1 n (π1 , ())

    ed2 : {A : Set} (n : Nat) → dom Γ n →  _#_ {A} n ∅
    ed2 _ _ = refl

  -- two singleton contexts with different indices are disjoint
  disjoint-singles : {A : Set} {x y : A} {u1 u2 : Nat} →
                          u1 ≠ u2 →
                          (■ (u1 , x)) ## (■ (u2 , y))
  disjoint-singles {_} {x} {y} {u1} {u2} neq = ds1 , ds2
    where
      ds1 : (n : Nat) → dom (■ (u1 , x)) n → n # (■ (u2 , y))
      ds1 n d with lem-dom-eq d
      ds1 .u1 d | refl with natEQ u2 u1
      ds1 .u1 d | refl | Inl xx = abort (neq (! xx))
      ds1 .u1 d | refl | Inr x₁ = refl

      ds2 : (n : Nat) → dom (■ (u2 , y)) n → n # (■ (u1 , x))
      ds2 n d with lem-dom-eq d
      ds2 .u2 d | refl with natEQ u1 u2
      ds2 .u2 d | refl | Inl x₁ = abort (neq x₁)
      ds2 .u2 d | refl | Inr x₁ = refl

  apart-noteq : {A : Set} (p r : Nat) (q : A) → p # (■ (r , q)) → p ≠ r
  apart-noteq p r q apt with natEQ r p
  apart-noteq p .p q apt | Inl refl = abort (somenotnone apt)
  apart-noteq p r q apt | Inr x₁ = flip x₁

  -- if singleton contexts are disjoint, their indices must be disequal
  singles-notequal : {A : Set} {x y : A} {u1 u2 : Nat} →
                          (■ (u1 , x)) ## (■ (u2 , y)) →
                          u1 ≠ u2
  singles-notequal {A} {x} {y} {u1} {u2} (d1 , d2) = apart-noteq u1 u2 y (d1 u1 (lem-domsingle u1 x))

  -- dual of lem2 above; if two indices are disequal, then either is apart
  -- from the singleton formed with the other
  apart-singleton : {A : Set} → ∀{x y} → {τ : A} →
                          x ≠ y →
                          x # (■ (y , τ))
  apart-singleton {A} {x} {y} {τ} neq with natEQ y x
  apart-singleton neq | Inl x₁ = abort ((flip neq) x₁)
  apart-singleton neq | Inr x₁ = refl

  -- if an index is apart from two contexts, it's apart from their union as
  -- well. used below and in other files, so it's outside the local scope.
  apart-parts : {A : Set} (Γ1 Γ2 : A ctx) (n : Nat) → n # Γ1 → n # Γ2 → n # (Γ1 ∪ Γ2)
  apart-parts Γ1 Γ2 n apt1 apt2 with Γ1 n
  apart-parts _ _ n refl apt2 | .None = apt2

  -- this is just for convenience; it shows up a lot.
  apart-extend1 : {A : Set} → ∀{x y τ} → (Γ : A ctx) → x ≠ y → x # Γ → x # (Γ ,, (y , τ))
  apart-extend1 {A} {x} {y} {τ} Γ neq apt with natEQ y x
  ... | Inl refl = abort (neq refl)
  ... | Inr y≠x  = apt

  -- if an index is in the domain of a union, it's in the domain of one or
  -- the other unand
  dom-split : {A : Set} → (Γ1 Γ2 : A ctx) (n : Nat) → dom (Γ1 ∪ Γ2) n → dom Γ1 n + dom Γ2 n
  dom-split Γ4 Γ5 n (π1 , π2) with Γ4 n
  dom-split Γ4 Γ5 n (π1 , π2) | Some x = Inl (x , refl)
  dom-split Γ4 Γ5 n (π1 , π2) | None = Inr (π1 , π2)

  -- if both parts of a union are disjoint with a target, so is the union
  disjoint-parts : {A : Set} {Γ1 Γ2 Γ3 : A ctx} → Γ1 ## Γ3 → Γ2 ## Γ3 → (Γ1 ∪ Γ2) ## Γ3
  disjoint-parts {_} {Γ1} {Γ2} {Γ3} D13 D23 = d31 , d32
    where
      d31 : (n : Nat) → dom (Γ1 ∪ Γ2) n → n # Γ3
      d31 n D with dom-split Γ1 Γ2 n D
      d31 n D | Inl x = π1 D13 n x
      d31 n D | Inr x = π1 D23 n x

      d32 : (n : Nat) → dom Γ3 n → n # (Γ1 ∪ Γ2)
      d32 n D = apart-parts Γ1 Γ2 n (π2 D13 n D) (π2 D23 n D)

  apart-union1 : {A : Set} (Γ1 Γ2 : A ctx) (n : Nat) → n # (Γ1 ∪ Γ2) → n # Γ1
  apart-union1 Γ1 Γ2 n aprt with Γ1 n
  apart-union1 Γ1 Γ2 n () | Some x
  apart-union1 Γ1 Γ2 n aprt | None = refl

  apart-union2 : {A : Set} (Γ1 Γ2 : A ctx) (n : Nat) → n # (Γ1 ∪ Γ2) → n # Γ2
  apart-union2 Γ1 Γ2 n aprt with Γ1 n
  apart-union2 Γ3 Γ4 n () | Some x
  apart-union2 Γ3 Γ4 n aprt | None = aprt

  -- if a union is disjoint with a target, so is the left unand
  disjoint-union1 : {A : Set} {Γ1 Γ2 Δ : A ctx} → (Γ1 ∪ Γ2) ## Δ → Γ1 ## Δ
  disjoint-union1 {Γ1 = Γ1} {Γ2 = Γ2} {Δ = Δ} (ud1 , ud2) = du11 , du12
    where
      dom-union1 : {A : Set} (Γ1 Γ2 : A ctx) (n : Nat) → dom Γ1 n → dom (Γ1 ∪ Γ2) n
      dom-union1 Γ1 Γ2 n (π1 , π2) with Γ1 n
      dom-union1 Γ1 Γ2 n (π1 , π2) | Some x = x , refl
      dom-union1 Γ1 Γ2 n (π1 , ()) | None

      du11 : (n : Nat) → dom Γ1 n → n # Δ
      du11 n dom = ud1 n (dom-union1 Γ1 Γ2 n dom)

      du12 : (n : Nat) → dom Δ n → n # Γ1
      du12 n dom = apart-union1 Γ1 Γ2 n (ud2 n dom)

  -- if a union is disjoint with a target, so is the right unand
  disjoint-union2 : {A : Set} {Γ1 Γ2 Δ : A ctx} → (Γ1 ∪ Γ2) ## Δ → Γ2 ## Δ
  disjoint-union2 {Γ1 = Γ1} {Γ2 = Γ2} {Δ = Δ} (ud1 , ud2) = du21 , du22
    where
      dom-union2 : {A : Set} (Γ1 Γ2 : A ctx) (n : Nat) → dom Γ2 n → dom (Γ1 ∪ Γ2) n
      dom-union2 Γ1 Γ2 n (π1 , π2) with Γ1 n
      dom-union2 Γ3 Γ4 n (π1 , π2) | Some x = x , refl
      dom-union2 Γ3 Γ4 n (π1 , π2) | None = π1 , π2

      du21 : (n : Nat) → dom Γ2 n → n # Δ
      du21 n dom = ud1 n (dom-union2 Γ1  Γ2 n dom)

      du22 : (n : Nat) → dom Δ n → n # Γ2
      du22 n dom = apart-union2 Γ1 Γ2 n (ud2 n dom)

  -- if x isn't in a context and y is then they can't be equal
  lem-dom-apt : {A : Set} {G : A ctx} {x y : Nat} → x # G → dom G y → x ≠ y
  lem-dom-apt {x = x} {y = y} apt dom with natEQ x y
  lem-dom-apt apt dom | Inl refl = abort (somenotnone (! (π2 dom) · apt))
  lem-dom-apt apt dom | Inr x₁ = x₁


module Prelude.Vec where

open import Prelude.Nat
open import Prelude.Fin
open import Prelude.Function
open import Prelude.Functor
open import Prelude.Applicative
open import Prelude.List
open import Prelude.Equality
open import Prelude.Decidable
open import Prelude.Ord
open import Prelude.Semiring

infixr 5 _∷_
data Vec {a} (A : Set a) : Nat → Set a where
  []  : Vec A zero
  _∷_ : ∀ {n} (x : A) (xs : Vec A n) → Vec A (suc n)

private
  -- These are private. Use pure and _<*>_ instead.
  vec : ∀ {a} {A : Set a} {n} → A → Vec A n
  vec {n = zero } x = []
  vec {n = suc n} x = x ∷ vec x

  vapp : ∀ {a b} {A : Set a} {B : Set b} {n} → Vec (A → B) n → Vec A n → Vec B n
  vapp  []       _       = []
  vapp (f ∷ fs) (x ∷ xs) = f x ∷ vapp fs xs

  vmap : ∀ {a b} {A : Set a} {B : Set b} {n} → (A → B) → Vec A n → Vec B n
  vmap f []       = []
  vmap f (x ∷ xs) = f x ∷ vmap f xs

vecToList : ∀ {a} {A : Set a} {@erased n} → Vec A n → List A
vecToList []       = []
vecToList (x ∷ xs) = x ∷ vecToList xs

listToVec : ∀ {a} {A : Set a} (xs : List A) → Vec A (length xs)
listToVec []       = []
listToVec (x ∷ xs) = x ∷ listToVec xs

--- Lookup ---

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

tabulate : ∀ {a} {A : Set a} {n} → (Fin n → A) → Vec A n
tabulate {n = zero } f = []
tabulate {n = suc n} f = f zero ∷ tabulate (f ∘ suc)

--- Folding ---

vfoldr : ∀ {a b} {A : Set a} {B : Nat → Set b} → (∀ {@erased n} → A → B n → B (suc n)) → B 0 → ∀ {@erased n} → Vec A n → B n
vfoldr f z [] = z
vfoldr f z (x ∷ xs) = f x (vfoldr (λ {n} → f {n}) z xs)

vfoldl : ∀ {a b} {A : Set a} {B : Nat → Set b} → (∀ {n} → B n → A → B (suc n)) → B 0 → ∀ {@erased n} → Vec A n → B n
vfoldl         f z [] = z
vfoldl {B = B} f z (x ∷ xs) = vfoldl {B = B ∘ suc} f (f z x) xs

--- Other list functions ---

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

  infixr 5 _v++_
  _v++_ : ∀ {m n} → Vec A m → Vec A n → Vec A (m + n)
  []       v++ ys = ys
  (x ∷ xs) v++ ys = x ∷ xs v++ ys

  vreverse : ∀ {@erased n} → Vec A n → Vec A n
  vreverse xs = vfoldl {B = Vec A} (flip _∷_) [] xs

--- Equality ---

vcons-inj-head : ∀ {a} {A : Set a} {x y : A} {n}
                   {xs ys : Vec A n} → x Vec.∷ xs ≡ y ∷ ys → x ≡ y
vcons-inj-head refl = refl

vcons-inj-tail : ∀ {a} {A : Set a} {x y : A} {n}
                   {xs ys : Vec A n} → x Vec.∷ xs ≡ y ∷ ys → xs ≡ ys
vcons-inj-tail refl = refl

instance
  EqVec : ∀ {a} {A : Set a} {{EqA : Eq A}} {n} → Eq (Vec A n)
  _==_ {{EqVec}} [] []             = yes refl
  _==_ {{EqVec}} (x ∷ xs) (y ∷ ys) with x == y
  ... | no neq   = no λ eq → neq (vcons-inj-head eq)
  ... | yes eq with xs == ys
  ...   | no neq  = no λ eq₁ → neq (vcons-inj-tail eq₁)
  ...   | yes eq₁ = yes (_∷_ $≡ eq *≡ eq₁)

--- Ord ---

data LessVec {a} {A : Set a} {{OrdA : Ord A}} : ∀ {n} → Vec A n → Vec A n → Set a where
  head< : ∀ {@erased x y n} {@erased xs ys : Vec A n} → x < y → LessVec (x ∷ xs) (y ∷ ys)
  tail< : ∀ {@erased x n} {@erased xs ys : Vec A n}   → LessVec xs ys → LessVec (x ∷ xs) (x ∷ ys)

private
  compareVec : ∀ {a} {A : Set a} {{OrdA : Ord A}} {@erased n} (xs ys : Vec A n) → Comparison LessVec xs ys
  compareVec [] [] = equal refl
  compareVec (x ∷ xs) (y  ∷  ys) with compare x y
  compareVec (x ∷ xs) (y  ∷  ys) | less    x<y  = less    (head< x<y)
  compareVec (x ∷ xs) (y  ∷  ys) | greater y<x  = greater (head< y<x)
  compareVec (x ∷ xs) (.x ∷  ys) | equal   refl with compareVec xs ys
  compareVec (x ∷ xs) (.x ∷  ys) | equal   refl | less    xs<ys = less (tail< xs<ys)
  compareVec (x ∷ xs) (.x ∷  ys) | equal   refl | greater ys<xs = greater (tail< ys<xs)
  compareVec (x ∷ xs) (.x ∷ .xs) | equal   refl | equal   refl  = equal refl

private
  len : ∀ {a} {A : Set a} {n} → Vec A n → Nat
  len {n = n} _ = n

instance
  OrdVec : ∀ {a} {A : Set a} {{OrdA : Ord A}} {n} → Ord (Vec A n)
  OrdVec = defaultOrd compareVec

  OrdLawsVec : ∀ {a} {A : Set a} {{OrdA : Ord/Laws A}} {n} → Ord/Laws (Vec A n)
  Ord/Laws.super OrdLawsVec = it
  less-antirefl {{OrdLawsVec}} {[]} ()
  less-antirefl {{OrdLawsVec {A = A}}} {x ∷ xs} (head< lt) = less-antirefl {A = A} lt
  less-antirefl {{OrdLawsVec {A = A}}} {x ∷ xs} (tail< lt) = less-antirefl {A = Vec A (len xs)} {xs} lt
  less-trans {{OrdLawsVec {A = A}}} {[]} {[]} () _
  less-trans {{OrdLawsVec {A = A}}} {x ∷ xs} {y ∷ ys} {z ∷ zs} (head< lt) (head< lt₁) = head< (less-trans {A = A} lt lt₁)
  less-trans {{OrdLawsVec {A = A}}} {x ∷ xs} {y ∷ ys} {y ∷ zs} (head< lt) (tail< lt₁) = head< lt
  less-trans {{OrdLawsVec {A = A}}} {x ∷ xs} {x ∷ ys} {z ∷ zs} (tail< lt) (head< lt₁) = head< lt₁
  less-trans {{OrdLawsVec {A = A}}} {x ∷ xs} {x ∷ ys} {x ∷ zs} (tail< lt) (tail< lt₁) = tail< (less-trans {A = Vec A (len xs)} lt lt₁)

--- Functor instances ---

instance
  FunctorVec : ∀ {a n} → Functor {a} (flip Vec n)
  fmap {{FunctorVec}} = vmap

  FunctorVec′ : ∀ {a b n} → Functor′ {a} {b} (flip Vec n)
  fmap′ {{FunctorVec′}} = vmap

  ApplicativeVec : ∀ {a n} → Applicative {a} (flip Vec n)
  pure  {{ApplicativeVec}} = vec
  _<*>_ {{ApplicativeVec}} = vapp

  ApplicativeVec′ : ∀ {a b n} → Applicative′ {a} {b} (flip Vec n)
  _<*>′_ {{ApplicativeVec′}} = vapp


module Bool where

data Bool : Set where
  false : Bool
  true  : Bool

data IsTrue : Bool -> Set where
  isTrue : IsTrue true

open import Vec
open import All

allEnvs : {n : Nat} -> List (Vec Bool n)
allEnvs {zero } = ε :: []
allEnvs {suc n} = map (_►_ false) allEnvs ++ map (_►_ true) allEnvs

∈++left : {A : Set}{x : A}{xs ys : List A} -> x ∈ xs -> x ∈ (xs ++ ys)
∈++left (hd p) = hd p
∈++left (tl q) = tl (∈++left q)

∈++right : {A : Set}{x : A}{xs ys : List A} -> x ∈ ys -> x ∈ (xs ++ ys)
∈++right {xs = []}      p = p
∈++right {xs = x :: xs} p = tl (∈++right {xs = xs} p)

∈map : {A B : Set}{f : A -> B}{x : A}{xs : List A} -> x ∈ xs -> f x ∈ map f xs
∈map (hd refl) = hd refl
∈map (tl q)    = tl (∈map q)

covered : {n : Nat} -> (xs : Vec Bool n) -> xs ∈ allEnvs
covered ε            = hd refl
covered (false ► xs) = ∈++left (∈map (covered xs))
covered (true  ► xs) = ∈++right {xs = map (_►_ false) allEnvs}
                                (∈map (covered xs))

Sat : {A : Set} -> (A -> Bool) -> A -> Set
Sat f x = IsTrue (f x)

lem₁ : {n : Nat}(f : Vec Bool n -> Bool) ->
      All (Sat f) allEnvs -> (xs : Vec Bool n) -> Sat f xs
lem₁ f p xs with p ! covered xs
... | (.xs , p , refl) = p

data False : Set where

¬_ : Set -> Set
¬ P = P -> False

data _∨_ (A B : Set) : Set where
  inl : A -> A ∨ B
  inr : B -> A ∨ B

¬IsTrue-false : ¬ IsTrue false
¬IsTrue-false ()

decide : {A : Set}(p : A -> Bool)(x : A) ->
         Sat p x ∨ ¬ Sat p x
decide p x with p x
... | true  = inl isTrue
... | false = inr ¬IsTrue-false

all : {A : Set}(p : A -> Bool)(xs : List A) ->
      All (Sat p) xs ∨ Some (\x -> ¬ Sat p x) xs
all p [] = inl ∅
all p (x :: xs) with decide p x
... | inr ¬px = inr (hd ¬px)
... | inl px  with all p xs
...   | inl ps = inl (px ▹ ps)
...   | inr q  = inr (tl q)

data NoProof : Set where
  no-proof : NoProof

Proof : {n : Nat} -> (Vec Bool n -> Bool) -> Set
Proof {n} f with all f allEnvs
... | inl _ = (xs : Vec Bool n) -> Sat f xs
... | inr _ = NoProof

prove : {n : Nat}(f : Vec Bool n -> Bool) -> Proof f
prove f with all f allEnvs
... | inl ps = lem₁ f ps
... | inr _  = no-proof


{-# OPTIONS --guardedness --cubical #-}

module Issue2799 where

open import Agda.Primitive.Cubical
open import Agda.Builtin.Cubical.Path

record Stream (A : Set) : Set where
  coinductive
  constructor _,_
  field
    head : A
    tail : Stream A

open Stream

mapS : ∀ {A B} → (A → B) → Stream A → Stream B
head (mapS f xs) = f (head xs)
tail (mapS f xs) = mapS f (tail xs)

mapS-id : ∀ {A} {xs : Stream A} → mapS (λ x → x) xs ≡ xs
head (mapS-id {xs = xs} i) = head xs
tail (mapS-id {xs = xs} i) = mapS-id {xs = tail xs} i


-- Andreas, 2019-06-26, issue #3872, make --no-forcing a pragma option

{-# OPTIONS --no-forcing #-}

open import Agda.Builtin.Nat

data List {a} (A : Set a) : Set a where
  [] : List A
  _∷_ : A → List A → List A

pattern [_] x = x ∷ []

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

  _++_ : List A → List A → List A
  []       ++ ys = ys
  (x ∷ xs) ++ ys = x ∷ (xs ++ ys)

  variable
    x y z : A
    @0 xs ys zs : List A

  data FSet : List A → Set a where
    ∅   : FSet []
    _∪_ : FSet xs → FSet ys → FSet (xs ++ ys)
    sg  : (x : A) → FSet [ x ]
    -- x is forced here, but this is not wanted.
    -- We need a way to tell the forcing machinery to not erase x.

  -- Since xs is erased, we cannot get x from sg x : FSet xs (with xs = [ x ])
  -- unless x is retained in sg x.
  elements : {@0 xs : List A} → FSet xs → List A
  elements ∅       = []
  elements (s ∪ t) = elements s ++ elements t
  elements (sg x)  = [ x ]

-- ERROR WAS:
-- Variable x is declared erased, so it cannot be used here
-- when checking that the expression x has type A

mySet = (sg 1 ∪ sg 2) ∪ (sg 3 ∪ sg 4)


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

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import LogicalFormulae
open import Sets.EquivalenceRelations
open import Setoids.Setoids

module Setoids.Union.Definition {a b : _} {A : Set a} (S : Setoid {a} {b} A) where

open Setoid S
open Equivalence eq
open import Setoids.Subset S

unionPredicate : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} (s1 : subset pred1) (s2 : subset pred2) → A → Set (c ⊔ d)
unionPredicate {pred1 = pred1} {pred2} s1 s2 a = pred1 a || pred2 a

union : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} (s1 : subset pred1) (s2 : subset pred2) → subset (unionPredicate s1 s2)
union s1 s2 {x1} {x2} x1=x2 (inl x) = inl (s1 x1=x2 x)
union s1 s2 {x1} {x2} x1=x2 (inr x) = inr (s2 x1=x2 x)


open import Mockingbird.Forest using (Forest)

-- Is There a Sage Bird?
module Mockingbird.Problems.Chapter10 {b ℓ} (forest : Forest {b} {ℓ}) where

open import Data.Product using (_,_; proj₁; proj₂; ∃-syntax)
open import Function using (_$_)

open import Mockingbird.Forest.Birds forest

open Forest forest

module _ ⦃ _ : HasComposition ⦄ ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasLark ⦄ where
  private
    hasMockingbirdComposer : ∃[ A ] (∀ x y → A ∙ x ∙ y ≈ x ∙ (M ∙ y))
    hasMockingbirdComposer = (L , isMockingbirdComposer)
      where
        isMockingbirdComposer = λ x y → begin
          L ∙ x ∙ y    ≈⟨ isLark x y ⟩
          x ∙ (y ∙ y)  ≈˘⟨ congˡ $ isMockingbird y ⟩
          x ∙ (M ∙ y)  ∎

  hasSageBird : HasSageBird
  hasSageBird = record
    { Θ = M ∘ L
    ; isSageBird = λ x → begin
        x ∙ ((M ∘ L) ∙ x)        ≈⟨ congˡ $ isComposition M L x ⟩
        x ∙ (M ∙ (L ∙ x))        ≈⟨ congˡ $ isMockingbird (L ∙ x) ⟩
        x ∙ ((L ∙ x) ∙ (L ∙ x))  ≈˘⟨ isLark x (L ∙ x) ⟩
        (L ∙ x) ∙ (L ∙ x)        ≈˘⟨ isMockingbird (L ∙ x) ⟩
        M ∙ (L ∙ x)              ≈˘⟨ isComposition M L x ⟩
        (M ∘ L) ∙ x              ∎
    }

module _ ⦃ _ : HasComposition ⦄ ⦃ _ : HasMockingbird ⦄
         (hasMockingbirdComposer : ∃[ A ] (∀ x y → A ∙ x ∙ y ≈ x ∙ (M ∙ y))) where

  private
    A = proj₁ hasMockingbirdComposer
    [Ax]y≈x[My] = proj₂ hasMockingbirdComposer

    instance
      hasLark : HasLark
      hasLark = record
        { L = A
        ; isLark = λ x y → begin
            (A ∙ x) ∙ y  ≈⟨ [Ax]y≈x[My] x y ⟩
            x ∙ (M ∙ y)  ≈⟨ congˡ $ isMockingbird y ⟩
            x ∙ (y ∙ y)  ∎
        }

  hasSageBird′ : HasSageBird
  hasSageBird′ = hasSageBird


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

module Dodo.Nullary.Unique where

-- Stdlib imports
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_)
open import Level using (Level)

-- # Definitions

Unique : ∀ {a : Level} (A : Set a) → Set _
Unique A = ∀ (x y : A) → x ≡ y


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

-- Construction of M-types from
-- https://arxiv.org/pdf/1504.02949.pdf
-- "Non-wellfounded trees in Homotopy Type Theory"
-- Benedikt Ahrens, Paolo Capriotti, Régis Spadotti

module Cubical.Codata.M.AsLimit.M.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv using (_≃_)
open import Cubical.Foundations.Function using (_∘_)

open import Cubical.Data.Unit
open import Cubical.Data.Prod
open import Cubical.Data.Nat as ℕ using (ℕ ; suc ; _+_ )
open import Cubical.Data.Sigma hiding (_×_)
open import Cubical.Data.Nat.Algebra

open import Cubical.Foundations.Transport
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Function
open import Cubical.Foundations.Path
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.GroupoidLaws

open import Cubical.Data.Sum

open import Cubical.Codata.M.AsLimit.helper
open import Cubical.Codata.M.AsLimit.Container

open import Cubical.Codata.M.AsLimit.Coalg.Base

open Iso

private
  limit-collapse : ∀ {ℓ} {S : Container ℓ} (X : ℕ → Type ℓ) (l : (n : ℕ) → X n → X (suc n)) → (x₀ : X 0) → ∀ (n : ℕ) → X n
  limit-collapse X l x₀ 0 = x₀
  limit-collapse {S = S} X l x₀ (suc n) = l n (limit-collapse {S = S} X l x₀ n)

lemma11-Iso :
  ∀ {ℓ} {S : Container ℓ} (X : ℕ → Type ℓ) (l : (n : ℕ) → X n → X (suc n))
  → Iso (Σ[ x ∈ ((n : ℕ) → X n)] ((n : ℕ) → x (suc n) ≡ l n (x n)))
         (X 0)
fun (lemma11-Iso X l) (x , y) = x 0
inv (lemma11-Iso {S = S} X l) x₀ = limit-collapse {S = S} X l x₀ , (λ n → refl {x = limit-collapse {S = S} X l x₀ (suc n)})
rightInv (lemma11-Iso X l) _ = refl
leftInv (lemma11-Iso {ℓ = ℓ} {S = S} X l) (x , y) i =
  let temp = χ-prop (x 0) (fst (inv (lemma11-Iso {S = S} X l) (fun (lemma11-Iso {S = S} X l) (x , y))) , refl , (λ n → refl {x = limit-collapse {S = S} X l (x 0) (suc n)})) (x , refl , y)
  in temp i .fst , proj₂ (temp i .snd)
  where
    open AlgebraPropositionality
    open NatSection

    X-fiber-over-ℕ : (x₀ : X 0) -> NatFiber NatAlgebraℕ ℓ
    X-fiber-over-ℕ x₀ = record { Fiber = X ; fib-zero = x₀ ; fib-suc = λ {n : ℕ} xₙ → l n xₙ }

    X-section : (x₀ : X 0) → (z : Σ[ x ∈ ((n : ℕ) → X n)] (x 0 ≡ x₀) × (∀ n → (x (suc n)) ≡ l n (x n))) -> NatSection (X-fiber-over-ℕ x₀)
    X-section = λ x₀ z → record { section = fst z ; sec-comm-zero = proj₁ (snd z) ; sec-comm-suc = proj₂ (snd z) }

    Z-is-Section : (x₀ : X 0) →
      Iso (Σ[ x ∈ ((n : ℕ) → X n)] (x 0 ≡ x₀) × (∀ n → (x (suc n)) ≡ l n (x n)))
          (NatSection (X-fiber-over-ℕ x₀))
    fun (Z-is-Section x₀) (x , (z , y)) = record { section = x ; sec-comm-zero = z ; sec-comm-suc = y }
    inv (Z-is-Section x₀) x = NatSection.section x , (sec-comm-zero x , sec-comm-suc x)
    rightInv (Z-is-Section x₀) _ = refl
    leftInv (Z-is-Section x₀) (x , (z , y)) = refl

    -- S≡T
    χ-prop' : (x₀ : X 0) → isProp (NatSection (X-fiber-over-ℕ x₀))
    χ-prop' x₀ a b = SectionProp.S≡T isNatInductiveℕ (X-section x₀ (inv (Z-is-Section x₀) a)) (X-section x₀ (inv (Z-is-Section x₀) b))

    χ-prop : (x₀ : X 0) → isProp (Σ[ x ∈ ((n : ℕ) → X n) ] (x 0 ≡ x₀) × (∀ n → (x (suc n)) ≡ l n (x n)))
    χ-prop x₀ = subst isProp (sym (isoToPath (Z-is-Section x₀))) (χ-prop' x₀)

-----------------------------------------------------
-- Shifting the limit of a chain is an equivalence --
-----------------------------------------------------

-- Shift is equivalence (12) and (13) in the proof of Theorem 7
-- https://arxiv.org/pdf/1504.02949.pdf
-- "Non-wellfounded trees in Homotopy Type Theory"
-- Benedikt Ahrens, Paolo Capriotti, Régis Spadotti

-- TODO: This definition is inefficient, it should be updated to use some cubical features!
shift-iso : ∀ {ℓ} (S : Container ℓ) -> Iso (P₀ S (M S)) (M S)
shift-iso S@(A , B) =
  P₀ S (M S)
    Iso⟨ Σ-cong-iso-snd
         (λ x → iso (λ f → (λ n z → f z .fst n) , λ n i a → f a .snd n i)
                    (λ (u , q) z → (λ n → u n z) , λ n i → q n i z)
                    (λ _ → refl)
                    (λ _ → refl)) ⟩
  (Σ[ a ∈ A ] (Σ[ u ∈ ((n : ℕ) → B a → X (sequence S) n) ] ((n : ℕ) → π (sequence S) ∘ (u (suc n)) ≡ u n)))
    Iso⟨ invIso α-iso-step-5-Iso ⟩
  (Σ[ a ∈ (Σ[ a ∈ ((n : ℕ) → A) ] ((n : ℕ) → a (suc n) ≡ a n)) ]
        Σ[ u ∈ ((n : ℕ) → B (a .fst n) → X (sequence S) n) ]
           ((n : ℕ) → PathP (λ x → B (a .snd n x) → X (sequence S) n)
                               (π (sequence S) ∘ u (suc n))
                               (u n)))
    Iso⟨ α-iso-step-1-4-Iso-lem-12 ⟩
  M S ∎Iso
    where
      α-iso-step-5-Iso-helper0 :
        ∀ (a : (ℕ -> A))
        → (p : (n : ℕ) → a (suc n) ≡ a n)
        → (n : ℕ)
        → a n ≡ a 0
      α-iso-step-5-Iso-helper0 a p 0 = refl
      α-iso-step-5-Iso-helper0 a p (suc n) = p n ∙ α-iso-step-5-Iso-helper0 a p n

      α-iso-step-5-Iso-helper1-Iso :
        ∀ (a : ℕ -> A)
        → (p : (n : ℕ) → a (suc n) ≡ a n)
        → (u : (n : ℕ) → B (a n) → Wₙ S n)
        → (n : ℕ)
        → (PathP (λ x → B (p n x) → Wₙ S n) (πₙ S ∘ u (suc n)) (u n)) ≡
             (πₙ S ∘ (subst (\k -> B k → Wₙ S (suc n)) (α-iso-step-5-Iso-helper0 a p (suc n))) (u (suc n))
         ≡ subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n))
      α-iso-step-5-Iso-helper1-Iso  a p u n =
        PathP (λ x → B (p n x) → Wₙ S n) (πₙ S ∘ u (suc n)) (u n)
          ≡⟨ PathP≡Path (λ x → B (p n x) → Wₙ S n) (πₙ S ∘ u (suc n)) (u n) ⟩
        subst (λ k → B k → Wₙ S n) (p n) (πₙ S ∘ u (suc n)) ≡ (u n)
          ≡⟨ (λ i → transp (λ j → B (α-iso-step-5-Iso-helper0 a p n (i ∧ j)) → Wₙ S n) (~ i) (subst (λ k → B k → Wₙ S n) (p n) (πₙ S ∘ u (suc n)))
                  ≡ transp (λ j → B (α-iso-step-5-Iso-helper0 a p n (i ∧ j)) → Wₙ S n) (~ i) (u n)) ⟩
        subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (subst (λ k → B k → Wₙ S n) (p n) (πₙ S ∘ u (suc n))) ≡
        subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n)
          ≡⟨ (λ i → sym (substComposite (λ k → B k → Wₙ S n) (p n) (α-iso-step-5-Iso-helper0 a p n) (πₙ S ∘ u (suc n))) i
                  ≡ subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n)) ⟩
        subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p (suc n)) (πₙ S ∘ u (suc n)) ≡
        subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n)
          ≡⟨ (λ i → substCommSlice (λ k → B k → Wₙ S (suc n)) (λ k → B k → Wₙ S n)
                                   (λ a x x₁ → (πₙ S) (x x₁))
                                   (α-iso-step-5-Iso-helper0 a p (suc n)) (u (suc n)) i
                  ≡ subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n)) ⟩
        πₙ S ∘ subst (λ k → B k → Wₙ S (suc n)) (α-iso-step-5-Iso-helper0 a p (suc n)) (u (suc n))
          ≡
        subst (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 a p n) (u n) ∎

      α-iso-step-5-Iso :
        Iso
          (Σ[ a ∈ (Σ[ a ∈ ((n : ℕ) → A) ] ((n : ℕ) → a (suc n) ≡ a n)) ]
            Σ[ u ∈ ((n : ℕ) → B (a .fst n) → X (sequence S) n) ]
              ((n : ℕ) → PathP (λ x → B (a .snd n x) → X (sequence S) n)
                                  (π (sequence S) ∘ u (suc n))
                                  (u n)))
            (Σ[ a ∈ A ] (Σ[ u ∈ ((n : ℕ) → B a → X (sequence S) n) ] ((n : ℕ) → π (sequence S) ∘ (u (suc n)) ≡ u n)))
      α-iso-step-5-Iso =
        Σ-cong-iso (lemma11-Iso {S = S} (λ _ → A) (λ _ x → x)) (λ a,p →
          Σ-cong-iso (pathToIso (cong (λ k → (n : ℕ) → k n) (funExt λ n → cong (λ k → B k → Wₙ S n) (α-iso-step-5-Iso-helper0 (a,p .fst) (a,p .snd) n)))) λ u →
                              pathToIso (cong (λ k → (n : ℕ) → k n) (funExt λ n → α-iso-step-5-Iso-helper1-Iso (a,p .fst) (a,p .snd) u n)))

      α-iso-step-1-4-Iso-lem-12 :
        Iso (Σ[ a ∈ (Σ[ a ∈ ((n : ℕ) → A)] ((n : ℕ) → a (suc n) ≡ a n)) ]
                  (Σ[ u ∈ ((n : ℕ) → B (a .fst n) → X (sequence S) n) ]
                      ((n : ℕ) → PathP (λ x → B (a .snd n x) → X (sequence S) n)
                                          (π (sequence S) ∘ u (suc n))
                                          (u n))))
            (limit-of-chain (sequence S))
      fun α-iso-step-1-4-Iso-lem-12 (a , b) = (λ { 0 → lift tt ; (suc n) → (a .fst n) , (b .fst n)}) , λ { 0 → refl {x = lift tt} ; (suc m) i → a .snd m i , b .snd m i }
      inv α-iso-step-1-4-Iso-lem-12 x = ((λ n → (x .fst) (suc n) .fst) , λ n i → (x .snd) (suc n) i .fst) , (λ n → (x .fst) (suc n) .snd) , λ n i → (x .snd) (suc n) i .snd
      fst (rightInv α-iso-step-1-4-Iso-lem-12 (b , c) i) 0 = lift tt
      fst (rightInv α-iso-step-1-4-Iso-lem-12 (b , c) i) (suc n) = refl i
      snd (rightInv α-iso-step-1-4-Iso-lem-12 (b , c) i) 0 = refl
      snd (rightInv α-iso-step-1-4-Iso-lem-12 (b , c) i) (suc n) = c (suc n)
      leftInv α-iso-step-1-4-Iso-lem-12 (a , b) = refl

shift : ∀ {ℓ} (S : Container ℓ) -> P₀ S (M S) ≡ M S
shift S = isoToPath (shift-iso S) -- lemma 13 & lemma 12

-- Transporting along shift

in-fun : ∀ {ℓ} {S : Container ℓ} -> P₀ S (M S) -> M S
in-fun {S = S} = fun (shift-iso S)

out-fun : ∀ {ℓ} {S : Container ℓ} -> M S -> P₀ S (M S)
out-fun {S = S} = inv (shift-iso S)

-- Property of functions into M-types

lift-to-M : ∀ {ℓ} {A : Type ℓ} {S : Container ℓ}
  → (x : (n : ℕ) -> A → X (sequence S) n)
  → ((n : ℕ) → (a : A) →  π (sequence S) (x (suc n) a) ≡ x n a)
  ---------------
  → (A → M S)
lift-to-M x p a = (λ n → x n a) , λ n i → p n a i

lift-direct-M : ∀ {ℓ} {S : Container ℓ}
  → (x : (n : ℕ) → X (sequence S) n)
  → ((n : ℕ) →  π (sequence S) (x (suc n)) ≡ x n)
  ---------------
  → M S
lift-direct-M x p = x , p


-- A Typed version of a subset of Landin's ISWIM from "The Next 700 Programming
-- Languages"

module ISWIM where

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

_+_ : Nat -> Nat -> Nat
zero  + m = m
suc n + m = suc (n + m)

{-# BUILTIN NATURAL Nat  #-}
{-# BUILTIN ZERO    zero #-}
{-# BUILTIN SUC     suc  #-}
{-# BUILTIN NATPLUS _+_  #-}

data Bool : Set where
  true  : Bool
  false : Bool

module Syntax where

  infixl 100 _∙_
  infixl 80 _WHERE_ _PP_
  infixr 60 _─→_
  infixl 40 _,_

  data Type : Set where
    nat  : Type
    bool : Type
    _─→_ : Type -> Type -> Type

  data Context : Set where
    ε   : Context
    _,_ : Context -> Type -> Context

  data Var : Context -> Type -> Set where
    vz : {Γ : Context}{τ : Type}            -> Var (Γ , τ) τ
    vs : {Γ : Context}{σ τ : Type} -> Var Γ τ -> Var (Γ , σ) τ

  data Expr (Γ : Context) : Type -> Set where
    var   : {τ : Type} -> Var Γ τ -> Expr Γ τ
    litNat  : Nat -> Expr Γ nat
    litBool : Bool -> Expr Γ bool
    plus    : Expr Γ (nat ─→ nat ─→ nat)
    if      : {τ : Type} -> Expr Γ (bool ─→ τ ─→ τ ─→ τ)
    _∙_           : {σ τ : Type} -> Expr Γ (σ ─→ τ) -> Expr Γ σ -> Expr Γ τ
    _WHERE_ : {σ τ ρ : Type} -> Expr (Γ , σ ─→ τ) ρ -> Expr (Γ , σ) τ -> Expr Γ ρ
    _PP_    : {σ τ ρ : Type} -> Expr (Γ , σ ─→ τ) ρ -> Expr (Γ , σ) ρ -> Expr Γ ρ

  -- ƛ x. e = f where f x = e
  ƛ : {Γ : Context}{σ τ : Type} -> Expr (Γ , σ) τ -> Expr Γ (σ ─→ τ)
  ƛ e = var vz WHERE e

module Cont (R : Set) where

  C : Set -> Set
  C a = (a -> R) -> R

  callcc : {a : Set} -> (({b : Set} -> a -> C b) -> C a) -> C a
  callcc {a} g = \k -> g (\x _ -> k x) k

  return : {a : Set} -> a -> C a
  return x = \k -> k x

  infixr 10 _>>=_

  _>>=_ : {a b : Set} -> C a -> (a -> C b) -> C b
  (m >>= k) ret = m \x -> k x ret

module Semantics (R : Set) where

  open module C = Cont R
  open Syntax

  infix 60 _!_
  infixl 40 _||_

  ⟦_⟧type : Type -> Set

  ⟦_⟧type' : Type -> Set
  ⟦ nat    ⟧type' = Nat
  ⟦ bool   ⟧type' = Bool
  ⟦ σ ─→ τ ⟧type' = ⟦ σ ⟧type' -> ⟦ τ ⟧type

  ⟦ τ ⟧type = C ⟦ τ ⟧type'

  data ⟦_⟧ctx : Context -> Set where
    ★    : ⟦ ε ⟧ctx
    _||_ : {Γ : Context}{τ : Type} -> ⟦ Γ ⟧ctx -> ⟦ τ ⟧type' -> ⟦ Γ , τ ⟧ctx

  _!_ : {Γ : Context}{τ : Type} -> ⟦ Γ ⟧ctx -> Var Γ τ -> ⟦ τ ⟧type'
  ★      ! ()
  (ρ || v) ! vz   = v
  (ρ || v) ! vs x = ρ ! x

  ⟦_⟧ : {Γ : Context}{τ : Type} -> Expr Γ τ -> ⟦ Γ ⟧ctx -> ⟦ τ ⟧type
  ⟦ var x   ⟧ ρ = return (ρ ! x)
  ⟦ litNat n        ⟧ ρ = return n
  ⟦ litBool b       ⟧ ρ = return b
  ⟦ plus    ⟧ ρ = return \n -> return \m -> return (n + m)
  ⟦ f ∙ e ⟧ ρ = ⟦ e ⟧ ρ >>= \v ->
                    ⟦ f ⟧ ρ >>= \w ->
                    w v
  ⟦ e WHERE f ⟧ ρ = ⟦ e ⟧ (ρ || (\x -> ⟦ f ⟧ (ρ || x)))
  ⟦ e PP f  ⟧ ρ = callcc \k ->
                    let throw = \x -> ⟦ f ⟧ (ρ || x) >>= k
                    in  ⟦ e ⟧ (ρ || throw)
  ⟦ if        ⟧ ρ = return \x -> return \y -> return \z -> return (iff x y z)
    where
      iff : {A : Set} -> Bool -> A -> A -> A
      iff true  x y = x
      iff false x y = y

module Test where

  open Syntax
  open module C = Cont Nat
  open module S = Semantics Nat

  run : Expr ε nat -> Nat
  run e = ⟦ e ⟧ ★ \x -> x

  -- 1 + 1
  two : Expr ε nat
  two = plus ∙ litNat 1 ∙ litNat 1

  -- f 1 + f 2 where f x = x
  three : Expr ε nat
  three = plus ∙ (var vz ∙ litNat 1) ∙ (var vz ∙ litNat 2) WHERE var vz

  -- 1 + f 1 where pp f x = x
  one : Expr ε nat
  one = plus ∙ litNat 1 ∙ (var vz ∙ litNat 1) PP var vz

open Test

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

twoOK : run two == 2
twoOK = refl

threeOK : run three == 3
threeOK = refl

oneOK : run one == 1
oneOK = refl

open Cont
open Syntax
open Semantics


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

module Relation.Ternary.Separation.Construct.Product where

open import Level
open import Data.Product

open import Relation.Unary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Ternary.Separation
open import Data.Product.Relation.Binary.Pointwise.NonDependent

module _ {ℓ₁ ℓ₂} {C₁ : Set ℓ₁} {C₂ : Set ℓ₂} where

  _×-⊎_ : RawSep C₁ → RawSep C₂ → RawSep (C₁ × C₂)
  R₁ ×-⊎ R₂ =
    let
      module R₁ = RawSep R₁
      module R₂ = RawSep R₂
    in record
    { _⊎_≣_ = λ Φ₁ Φ₂ Φ →
        (proj₁ Φ₁) R₁.⊎ (proj₁ Φ₂) ≣ proj₁ Φ
      × (proj₂ Φ₁) R₂.⊎ (proj₂ Φ₂) ≣ proj₂ Φ }

  instance ×-rawsep : ⦃ _ : RawSep C₁ ⦄ ⦃ _ : RawSep C₂ ⦄ → RawSep (C₁ × C₂)
  ×-rawsep ⦃ R₁ ⦄ ⦃ R₂ ⦄ = R₁ ×-⊎ R₂

module _
  {ℓ₁ ℓ₂} {C₁ : Set ℓ₁} {C₂ : Set ℓ₂}
  {{R₁ : RawSep C₁}} {{R₂ : RawSep C₂}}
  ⦃ s₁ : IsSep R₁ ⦄ ⦃ s₂ : IsSep R₂ ⦄
  where

  instance ×-isSep : IsSep (R₁ ×-⊎ R₂)
  ×-isSep = record
    { ⊎-comm  = λ where (l , r) → ⊎-comm l , ⊎-comm r
    ; ⊎-assoc = λ where
      (l₁  , r₁) (l₂ , r₂) →
        let
          _ , l₃ , l₄ = ⊎-assoc l₁ l₂
          _ , r₃ , r₄ = ⊎-assoc r₁ r₂
        in -, (l₃ , r₃) , l₄ , r₄ }

module _
  {ℓ₁ ℓ₂} {C₁ : Set ℓ₁} {C₂ : Set ℓ₂}
  {{R₁ : RawSep C₁}} {{R₂ : RawSep C₂}} {u₁ u₂}
  ⦃ s₁ : IsUnitalSep R₁ u₁ ⦄ ⦃ s₂ : IsUnitalSep R₂ u₂ ⦄
  where

  instance ×-isUnitalSep : IsUnitalSep ×-rawsep (u₁ , u₂)
  ×-isUnitalSep = record
    { isSep = ×-isSep
    ; ⊎-idˡ = ⊎-idˡ , ⊎-idˡ
    ; ⊎-id⁻ˡ = λ where
      (fst , snd) → cong₂ _,_ (⊎-id⁻ˡ fst) (⊎-id⁻ˡ snd)
    }

  instance _×-ε-separation_ : UnitalSep _
  _×-ε-separation_ = record
    { isUnitalSep = ×-isUnitalSep }

module _
  {ℓ₁ ℓ₂}
  ⦃ s₁ : Separation ℓ₁ ⦄ ⦃ s₂ : Separation ℓ₂ ⦄
  where

  private
    module S₁ = Separation s₁
    module S₂ = Separation s₂

  ×-separation : Separation _
  ×-separation = record
    { isSep = ×-isSep {{ _ }} {{ _ }} ⦃ Separation.isSep s₁ ⦄ ⦃ Separation.isSep s₂ ⦄ }

module _
  {ℓ₁ ℓ₂}
  {C₁ : Set ℓ₁} {C₂ : Set ℓ₂}
  ⦃ sep₁ : RawSep C₁ ⦄ ⦃ sep₂ : RawSep C₂ ⦄
  ⦃ s₁ : IsConcattative sep₁ ⦄ ⦃ s₂ : IsConcattative sep₂ ⦄
  where

  private
    module S₁ = IsConcattative s₁
    module S₂ = IsConcattative s₂

  instance ×-concat : IsConcattative ×-rawsep
  ×-concat = record
    { _∙_ = (λ where (a , b) (c , d) → (a S₁.∙ c , b S₂.∙ d))
    ; ⊎-∙ₗ = λ where (p , q) → ⊎-∙ₗ p , ⊎-∙ₗ q }

{- Some useful type-formers for this instance -}
module _ {ℓ} {A : Set ℓ} {{ r : RawSep A }} {u} {{s : IsUnitalSep r u}} where

  data Π₁ {p} (P : Pred A p) : Pred (A × A) (ℓ ⊔ p) where
    fst : ∀ {a} → P a → Π₁ P (a , ε)

  data Π₂ {p} (P : Pred A p) : Pred (A × A) (ℓ ⊔ p) where
    snd : ∀ {a} → P a → Π₂ P (ε , a)