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

open import Level
open import Algebra.Structures
open import Relation.Binary
open import Algebra.FunctionProperties

module Comb (K : Set) (_≈_ : Rel K zero)
         (_+_ _⋆_ : Op₂ K) (-_ : Op₁ K) (0# 1# : K)
         (isRing : IsRing _≈_ _+_ _⋆_ -_ 0# 1#) where

open import Size
open import Function hiding (const)
open import Relation.Nullary
open import Stream
open import Data.Nat renaming (_+_ to _+ℕ_)
open import Data.Fin hiding (_+_)
open import Data.Product hiding (_×_)
open import Data.Empty
open import Relation.Binary.PropositionalEquality as P
  using (_≡_) --; refl; sym; trans; cong; module ≡-Reasoning)

K-setoid : Setoid _ _
K-setoid = record { Carrier = K
                  ; _≈_ = _≈_
                  ; isEquivalence = IsRing.isEquivalence isRing }

open Bisim K-setoid
import Relation.Binary.EqReasoning as EqR
open EqR (K-setoid) public hiding (_≡⟨_⟩_)
--open IsEquivalence (IsRing.isEquivalence isRing)
open ∼-Reasoning renaming (begin_ to begin∼_; _∎ to _∎∼)

open IsRing isRing -- using (+-cong ; +-identity)
+-identityˡ = proj₁ +-identity
+-identityʳ = proj₂ +-identity

-- zeroʳ : RightZero 0# _⋆_
-- zeroʳ = ?

----------------------
--- Basic operators --
----------------------

const : ∀ {A} → A → Str A
hd (const x) = x
tl (const x) = const x

-- | Point-wise unary operations
pw₁ : ∀ {A i} → Op₁ A → Op₁ (Str {i} A)
hd (pw₁ f σ) = f (hd σ)
tl (pw₁ f σ) = pw₁ f (tl σ)

-- | Point-wise binary operations
pw₂ : ∀ {A i} → Op₂ A → Op₂ (Str {i} A)
hd (pw₂ f σ τ) = f (hd σ) (hd τ)
tl (pw₂ f σ τ) = pw₂ f (tl σ) (tl τ)

[_] : K → Str K
hd [ r ] = r
tl [ r ] = [ 0# ]

-- | Can, in conjunction with _×_, delay stream.
X : Str K
hd X = 0#
tl X = [ 1# ]

_⊕_ : ∀{i} → Str {i} K → Str {i} K → Str {i} K
_⊕_ = pw₂ _+_

⊝_ : ∀{i} → Str {i} K → Str {i} K
⊝_ = pw₁ (-_)

_×_ : ∀ {i} → Str {i} K → Str {i} K → Str {i} K
hd (σ × τ) = hd σ ⋆ hd τ
tl (σ × τ) = (tl σ × τ) ⊕ ([ hd σ ] × tl τ)

-- | We restrict attention to the case where the head is 1,
-- which is trivially invertible in any ring.
hasInv : ∀ {i} → Str {i} K → Set
hasInv σ = hd σ ≈ 1#

-- | Construct convolution inverse, if it exists.
inv : ∀ {i} → (σ : Str {i} K) → hasInv σ → Str {i} K
hd (inv σ p) = hd σ
tl (inv σ p) = ⊝ (tl σ × inv σ p)

-- | Powers of delay stream
X^_ : ℕ → Str K
X^ 0 = [ 1# ]
X^ (suc k) = X × (X^ k)

{-
record Summable (σ : Str (Str K)) : Set where
  coinductive
  field
    hd-smble : ∃ λ n → δ n (hd σ) ∼ [ 0# ]
    tl-smble : Summable (tl σ)
open Summable
-}

SummableDiag : {A : Set} (f : A → K) → Set
SummableDiag {A} f =
  ∃ λ n →
  Σ (Fin n → A) (λ g →
  (a : A) → ¬ ((f a) ≈ 0#) → ∃ λ i → a ≡ g i)

record Summable {A : Set} (f : A → Str K) : Set where
  coinductive
  field
    hd-smble : SummableDiag (hd ∘ f)
    tl-smble : Summable (tl' ∘ f)
open Summable

-- | Sum a finitely indexed family of elements of K.
-- This relies on commutativity etc. of addition in K.
sum : {n : ℕ} → (Fin n → K) → K
sum {ℕ.zero}  f = 0#
sum {ℕ.suc n} f = f Fin.zero + sum (f ∘ Fin.suc)

-- | Sum a family of streams.
∑ : ∀ {A} → (f : A → Str K) → Summable f → Str K
hd (∑ f p) =
  let (_ , g , _) = hd-smble p
  in  sum (hd ∘ f ∘ g)
tl (∑ f p) = ∑ (tl' ∘ f) (tl-smble p)

-- | Constant family of zero streams is summable
zero-summable : Summable {ℕ} (λ n → [ 0# ])
hd-smble zero-summable
  = (0 , f , q)
  where
    f : Fin 0 → ℕ
    f ()

    q : (n : ℕ) → ¬ (hd [ 0# ] ≈ 0#) → ∃ λ i → n ≡ f i
    q n p = ⊥-elim (p (IsRing.refl isRing))
tl-smble zero-summable = zero-summable

polySeq : ∀{i} → Str {i} (Str K) → Str {i} (Str K)
hd (polySeq σ) = hd σ × (X^ 0)
tl (polySeq σ) = pw₂ _×_ (polySeq (tl σ)) (const X)

polySeq₁ : Str (Str K) → (ℕ → Str K)
polySeq₁ σ = _at_ (polySeq σ)

polySeq₂ : (ℕ → Str K) → (ℕ → Str K)
polySeq₂ f = polySeq₁ (toStr f)

polySeq-summable : (σ : Str (Str K)) → Summable (polySeq₁ σ)
hd-smble (polySeq-summable σ) = q
  where
    q : SummableDiag (hd ∘ polySeq₁ σ)
    q = {!!}
tl-smble (polySeq-summable σ) = {!!}

{-
polySeq-expl : (σ : Str (Str K)) → (i : ℕ) → (polySeq σ at i) ≡ (σ at i) × (X^ i)
polySeq-expl σ zero    = refl
polySeq-expl σ (suc i) =
  polySeq σ at (ℕ.suc i)
    ≡⟨ ? ⟩
  (σ at (ℕ.suc i)) × (X × (X^ i))
    ∎
-}

-- lem : {f : ℕ → Str K} {k n : ℕ} → ¬ (polySeq f k at n) ≈ 0# → k ≡ n
-- lem = {!!}

{-
polySeq-summable-aux : {f : ℕ → Str K} →
                       (n : ℕ) → (g : ℕ → Str K) → Summable (polySeq f)
hd-smble (polySeq-summable-aux f n) = (1 , supp , {!!})
  where
    p = polySeq f

    supp : Fin 1 → ℕ
    supp Fin.zero = n
    supp (Fin.suc ())

    is-supp : (k : ℕ) → ¬ (hd (p k) ≈ 0#) → ∃ λ i → k ≡ supp i
    is-supp k q = (Fin.zero , {!!})

tl-smble (polySeq-summable-aux f n) = {!!}
-}

hd-delay-zero : (σ : Str K) → hd (X × σ) ≈ 0#
hd-delay-zero σ = zeroˡ (hd σ)
  where
    open IsNearSemiring (IsRing.isNearSemiring isRing)

⊕-cong : ∀ {i s t u v} → s ∼[ i ] t → u ∼[ i ] v → (s ⊕ u) ∼[ i ] (t ⊕ v)
hd≈ (⊕-cong s~t u~v) = +-cong (hd≈ s~t) (hd≈ u~v)
tl∼ (⊕-cong s~t u~v) = ⊕-cong (tl∼ s~t) (tl∼ u~v)

⊕-unitˡ : ∀ {i} → LeftIdentity (_∼_ {i}) [ 0# ] _⊕_
hd≈ (⊕-unitˡ s) = proj₁ +-identity (hd s)
tl∼ (⊕-unitˡ s) = ⊕-unitˡ (tl s)

⊕-comm : ∀ {i} → Commutative (_∼_ {i}) _⊕_
hd≈ (⊕-comm s t) = +-comm (hd s) (hd t)
tl∼ (⊕-comm s t) = ⊕-comm (tl s) (tl t)

⊕-unitʳ : ∀ {i} → RightIdentity (_∼_ {i}) [ 0# ] _⊕_
⊕-unitʳ s =
  s-bisim-trans
  (⊕-comm s [ 0# ])
  (⊕-unitˡ s)

×-zeroˡ : ∀ {i} → LeftZero (_∼_ {i}) [ 0# ] _×_
hd≈ (×-zeroˡ s) = zeroˡ (hd s)
  where
    open IsNearSemiring (IsRing.isNearSemiring isRing)
tl∼ (×-zeroˡ s) {j} =
  s-bisim-trans
  (⊕-cong {j} (×-zeroˡ s) (×-zeroˡ (tl s)))
  (⊕-unitˡ [ 0# ])

×-unitˡ : ∀ {i} → LeftIdentity (_∼_ {i}) [ 1# ] _×_
hd≈ (×-unitˡ s) = proj₁ *-identity (hd s)
tl∼ (×-unitˡ s) {j} =
  s-bisim-trans
  (⊕-cong {j} (×-zeroˡ s) (×-unitˡ (tl s)))
  (⊕-unitˡ (tl s))

tl-delay-id : (σ : Str K) → tl (X × σ) ∼ σ
tl-delay-id σ =
  begin∼
    tl (X × σ)
  ∼⟨ S.refl ⟩
    (tl X × σ) ⊕ ([ hd X ] × tl σ)
  ∼⟨ S.refl ⟩
    ([ 1# ] × σ) ⊕ ([ 0# ] × tl σ)
  ∼⟨ ⊕-cong (×-unitˡ σ) (×-zeroˡ (tl σ)) ⟩
    σ ⊕ [ 0# ]
  ∼⟨ ⊕-unitʳ σ ⟩
    σ
  ∎∼
  where
    module S = Setoid stream-setoid

-- | The analogue of the fundamental theorem
decomp-str : (σ : Str K) → σ ∼ ([ hd σ ] ⊕ (X × tl σ))
hd≈ (decomp-str σ) =
    begin
  hd σ
    ≈⟨ sym (+-identityʳ (hd σ)) ⟩
  hd σ + 0#
    ≈⟨ sym (+-cong refl (hd-delay-zero (tl σ))) ⟩
  hd [ hd σ ] + hd (X × tl σ)
    ≈⟨ refl ⟩
  hd ([ hd σ ] ⊕ (X × tl σ))
    ∎
tl∼ (decomp-str σ) =
  begin∼
    tl σ
  ∼⟨ S.sym (tl-delay-id (tl σ)) ⟩
    tl (X × tl σ)
  ∼⟨ S.sym (⊕-unitˡ (tl (X × tl σ))) ⟩
    [ 0# ] ⊕ tl (X × tl σ)
  ∼⟨ S.refl ⟩
    tl ([ hd σ ] ⊕ (X × tl σ))
  ∎∼
  where
    module S = Setoid stream-setoid


------------------------------------------------------------------------
-- Extension The Agda standard library
--
-- Properties of indexed binary relations
------------------------------------------------------------------------

module Relation.Binary.Indexed.Extra where

open import Data.Product
open import Data.Sum
open import Function
open import Level
-- import Relation.Binary.PropositionalEquality.Core as PropEq
-- open import Relation.Binary.Consequences as Consequences
open import Relation.Binary.Core as Core using (_≡_)
open import Relation.Binary.Indexed.Core
open import Relation.Binary.Indexed
import Relation.Binary as B

------------------------------------------------------------------------
-- Simple properties and equivalence relations

-- open Core public hiding (_≡_; refl; _≢_)
--
-- open Consequences public using (Total)

------------------------------------------------------------------------
-- Simple properties of indexed binary relations

-- Implication/containment. Could also be written ⊆.
[_][_]_⇒_ : ∀ {i₁ i₂ a b ℓ₁ ℓ₂} {I₁ : Set i₁} {I₂ : Set i₂}
    (A : I₁ → Set a) (B : I₂ → Set b)
    → REL A B ℓ₁ → REL A B ℓ₂ → Set _
[ A ][ B ] P ⇒ Q = ∀ {i₁ i₂} {x : A i₁} {y : B i₂} → P x y → Q x y

-- _Preserves_⟶_ : ∀ {a b ℓ₁ ℓ₂} {A : Set a} {B : A → Set b} →
--           ((x : A) → B x) → B.Rel A ℓ₁ → Rel B ℓ₂ → Set (ℓ₂ ⊔ (ℓ₁ ⊔ a))
-- f Preserves P ⟶ Q = P =[ f ]⇒ Q

_Respects_ : ∀ {𝒾 a ℓ₁ ℓ₂} {I : Set 𝒾} {A : I → Set a} {i : I}
    → (A i → Set ℓ₁) → Rel A ℓ₂ → Set (ℓ₂ ⊔ (ℓ₁ ⊔ a))
P Respects _∼_ = ∀ {x y} → x ∼ y → P x → P y

[_]_Respects₂_ : ∀ {𝒾 a ℓ₁ ℓ₂} {I : Set 𝒾} (A : I → Set a)
    → Rel A ℓ₁ → Rel A ℓ₂ → Set (ℓ₂ ⊔ (ℓ₁ ⊔ (a ⊔ 𝒾)))
[ A ] P Respects₂ _∼_ =
      (∀ {i} {x : A i} → _Respects_ {A = A} {i = i} (P x) _∼_) ×
      (∀ {i} {y : A i} → _Respects_ {A = A} {i = i} (flip P y) _∼_)

------------------------------------------------------------------------
-- Preorders

record IsPreorder {𝒾 a ℓ₁ ℓ₂} {I : Set 𝒾} (A : I → Set a)
                  (_≈_ : Rel A ℓ₁) -- The underlying equality.
                  (_∼_ : Rel A ℓ₂) -- The relation.
                  : Set (𝒾 ⊔ a ⊔ ℓ₁ ⊔ ℓ₂) where
    field
        isEquivalence : IsEquivalence A _≈_
        -- Reflexivity is expressed in terms of an underlying equality:
        reflexive     : [ A ][ A ] _≈_ ⇒ _∼_
        trans         : Transitive A _∼_

    module Eq = IsEquivalence isEquivalence

    refl : Reflexive A _∼_
    refl = reflexive Eq.refl

    ∼-resp-≈ : [ A ] _∼_ Respects₂ _≈_
    ∼-resp-≈ = (λ x≈y z∼x → trans z∼x (reflexive x≈y))
           , (λ x≈y x∼z → trans (reflexive $ Eq.sym x≈y) x∼z)

record Preorder {i} (I : Set i) c ℓ₁ ℓ₂ : Set (suc (i ⊔ c ⊔ ℓ₁ ⊔ ℓ₂)) where
    infix 4 _≈_ _∼_
    field
        Carrier    : I → Set c
        _≈_        : Rel Carrier ℓ₁  -- The underlying equality.
        _∼_        : Rel Carrier ℓ₂  -- The relation.
        isPreorder : IsPreorder Carrier _≈_ _∼_

    open IsPreorder isPreorder public

SetoidIsPreorder : ∀ {𝒾} {I : Set 𝒾} {c ℓ} (S : Setoid I c ℓ) → IsPreorder (Setoid.Carrier S) (Setoid._≈_ S) (Setoid._≈_ S)
SetoidIsPreorder {𝒾} {I} S = record
    { isEquivalence = isEquivalence
    ; reflexive = id
    ; trans = IsEquivalence.trans isEquivalence
    }
    where
        open Setoid S

Setoid⇒Preorder : ∀ {𝒾} {I : Set 𝒾} {c ℓ} (S : Setoid I c ℓ) → Preorder I c ℓ ℓ
Setoid⇒Preorder S = record { isPreorder = SetoidIsPreorder S }
    -- record
    -- { Carrier = {!   !}
    -- ; _≈_ = {!   !}
    -- ; _∼_ = {!   !}
    -- ; isPreorder = {!   !}
    -- }

    -- IsEquivalence.reflexive (Setoid.isEquivalence S)
    -- where   open IsEquivalence {!   !}
    --
    -- isPreorder : IsPreorder _≡_ _≈_
    -- isPreorder = record
    --     { isEquivalence = PropEq.isEquivalence
    --     ; reflexive     = reflexive
    --     ; trans         = trans
    --     }
      --
      -- preorder : Preorder c c ℓ
      -- preorder = record { isPreorder = isPreorder }


module AndOrNot where

open import Data.Bool
open import Data.Product

test : Bool → Bool → Bool × Bool × Bool
test x y = x ∧ y , x ∨ y , not x


open import Relation.Nullary.Decidable using (False)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; cong; cong₂)
open import Relation.Binary.PropositionalEquality.WithK using (≡-erase)

module AKS.Nat.Divisibility where

open import Data.Nat.Divisibility using (_∣_) public
open import Agda.Builtin.Nat using () renaming (mod-helper to modₕ; div-helper to divₕ) public
open import Data.Nat.DivMod using (_/_; _%_; %-distribˡ-+) public
open import Data.Nat.DivMod.Core using (a≤n⇒a[modₕ]n≡a)
open import Data.Nat.DivMod using (/-congˡ; /-congʳ) renaming (m≡m%n+[m/n]*n to div-lemma)

open import AKS.Nat.Base using (ℕ; _+_; _*_; _∸_; _≟_; lte; _≤_; _<_)
open ℕ
open import AKS.Nat.Properties using (+-suc)
open import AKS.Nat.Properties using (m≤m+n; m≤n+m; suc-mono-≤; ∸-mono-≤ˡ; +-lower-≤; ≤-refl; ≢⇒¬≟; module ≤-Reasoning)
open import AKS.Nat.Properties using (*-comm; +-identityʳ)
open ≤-Reasoning
import Data.Nat.DivMod as Nat
import Data.Nat.Properties as Nat
open import Data.Nat.Properties using (*-zeroʳ)

open import Polynomial.Simple.AlmostCommutativeRing.Instances using (module Nat)
open import Polynomial.Simple.Reflection using (solve)

a[modₕ]n≤n : ∀ acc d n → modₕ acc (acc + n) d n ≤ acc + n
a[modₕ]n≤n acc zero n = m≤m+n
a[modₕ]n≤n acc (suc d) zero = a[modₕ]n≤n zero d (acc + 0)
a[modₕ]n≤n acc (suc d) (suc n) rewrite +-suc acc n = a[modₕ]n≤n (suc acc) d n

n%m<m : ∀ n m {≢0 : False (m ≟ 0)} → (n % m) {≢0} < m
n%m<m n (suc m) = suc-mono-≤ (a[modₕ]n≤n 0 n m)

a[modₕ]n≤a : ∀ acc a n → modₕ acc (acc + n) a n ≤ acc + a
a[modₕ]n≤a acc zero n rewrite +-identityʳ acc = ≤-refl
a[modₕ]n≤a acc (suc a) (suc n) = begin
  modₕ acc (acc + suc n) (suc a) (suc n) ≡⟨ cong (λ v → modₕ acc v (suc a) (suc n)) (+-suc acc n) ⟩
  modₕ acc (suc acc + n) (suc a) (suc n) ≤⟨ a[modₕ]n≤a (suc acc) a n ⟩
  suc acc + a                            ≡⟨ sym (+-suc acc a) ⟩
  acc + suc a                            ∎
a[modₕ]n≤a acc (suc a) zero    = begin
  modₕ acc (acc + 0) (suc a) 0 ≡⟨ cong (λ v → modₕ acc v (suc a) 0) (+-identityʳ acc) ⟩
  modₕ acc acc (suc a) 0       ≤⟨ a[modₕ]n≤a 0 a acc ⟩
  a                            ≤⟨ m≤n+m ⟩
  suc a                        ≤⟨ m≤n+m ⟩
  acc + suc a                  ∎

n%m≤n : ∀ n m {≢0} → (n % m) {≢0} ≤ n
n%m≤n n (suc m) = a[modₕ]n≤a 0 n m

n<m⇒n%m≡n : ∀ {n m} {≢0 : False (m ≟ 0)} → n < m → (n % m) {≢0} ≡ n
n<m⇒n%m≡n {n} {suc m} (lte k refl) = ≡-erase (a≤n⇒a[modₕ]n≡a 0 (n + k) n k)

0%m≡0 : ∀ m {≢0 : False (m ≟ 0)} → (0 % m) {≢0} ≡ 0
0%m≡0 (suc m) = refl

1%m≡1 : ∀ {m} {≢0} → 1 < m → (1 % m) {≢0} ≡ 1
1%m≡1 {suc (suc m)} 1<m = refl

record Euclidean (n : ℕ) (m : ℕ) : Set where
  constructor Euclidean✓
  field
    q : ℕ
    r : ℕ
    division : n ≡ r + m * q
    r<m : r < m

m≡m%n+[m/n]*n : ∀ m n {n≢0 : False (n ≟ 0)} → m ≡ (m % n) {n≢0} + (m / n) {n≢0} * n
m≡m%n+[m/n]*n m (suc n) = div-lemma m n

m%n≡m∸m/n*n : ∀ m n {n≢0 : False (n ≟ 0)} → (m % n) {n≢0} ≡ m ∸ (m / n) {n≢0} * n
m%n≡m∸m/n*n m (suc n) = Nat.m%n≡m∸m/n*n m n

m%n%n≡m%n : ∀ m n {n≢0 : False (n ≟ 0)} → ((m % n) {n≢0} % n) {n≢0} ≡ (m % n) {n≢0}
m%n%n≡m%n m (suc n) = Nat.m%n%n≡m%n m n

n%n≡0 : ∀ n {n≢0 : False (n ≟ 0)} → (n % n) {n≢0} ≡ 0
n%n≡0 (suc n) = Nat.n%n≡0 n

[m+kn]%n≡m%n : ∀ m k n {n≢0 : False (n ≟ 0)} → ((m + k * n) % n) {n≢0} ≡ (m % n) {n≢0}
[m+kn]%n≡m%n m k (suc n) = Nat.[m+kn]%n≡m%n m k n

%-distribˡ-* : ∀ m n d {≢0} → ((m * n) % d) {≢0} ≡ (((m % d) {≢0} * (n % d) {≢0}) % d) {≢0}
%-distribˡ-* m n (suc d) = begin-equality
  (m * n) % suc d                                                           ≡⟨ cong (λ x → (x * n) % suc d) (m≡m%n+[m/n]*n m (suc d)) ⟩
  ((m % suc d + m / suc d * suc d) * n) % suc d                             ≡⟨ cong (λ x → x % suc d) (Nat.*-distribʳ-+ n (m % suc d) (m / suc d * suc d)) ⟩
  ((m % suc d) * n + (m / suc d * suc d) * n) % suc d                       ≡⟨ cong (λ x → ((m % suc d) * n + x) % suc d) (lemma (m / suc d) (suc d) n) ⟩
  ((m % suc d) * n + (m / suc d * n) * suc d) % suc d                       ≡⟨ Nat.[m+kn]%n≡m%n ((m % suc d) * n) (m / suc d * n) d ⟩
  ((m % suc d) * n) % suc d                                                 ≡⟨ cong (λ x → ((m % suc d) * x) % suc d) (m≡m%n+[m/n]*n n (suc d)) ⟩
  ((m % suc d) * (n % suc d + n / suc d * suc d)) % suc d                   ≡⟨ cong (λ x → x % suc d) (Nat.*-distribˡ-+ (m % suc d) (n % suc d) (n / suc d * suc d)) ⟩
  ((m % suc d) * (n % suc d) + (m % suc d) * (n / suc d * suc d)) % suc d   ≡⟨ cong (λ x → ((m % suc d) * (n % suc d) + x) % suc d) (sym (Nat.*-assoc (m % suc d) (n / suc d) (suc d))) ⟩
  ((m % suc d) * (n % suc d) + ((m % suc d) * (n / suc d)) * suc d) % suc d ≡⟨ Nat.[m+kn]%n≡m%n ((m % suc d) * (n % suc d)) ((m % suc d) * (n / suc d)) d ⟩
  ((m % suc d) * (n % suc d)) % suc d                                       ∎
  where
  lemma : ∀ x y z → (x * y) * z ≡ (x * z) * y
  lemma = solve Nat.ring

[m∸n]%n≡m%n : ∀ m n {n≢0 : False (n ≟ 0)} → n ≤ m → ((m ∸ n) % n) {n≢0} ≡ (m % n) {n≢0}
[m∸n]%n≡m%n (suc .(n + k)) (suc n) (lte k refl) = begin-equality
  ((suc n + k) ∸ suc n) % suc n ≡⟨ cong (λ t → t % suc n) (Nat.m+n∸m≡n (suc n) k) ⟩
  k % suc n                     ≡⟨ sym (Nat.[m+n]%n≡m%n k n) ⟩
  (k + suc n) % suc n           ≡⟨ cong (λ t → t % suc n) (Nat.+-comm k (suc n)) ⟩
  (suc n + k) % suc n           ∎

[m∸kn]%n≡m%n : ∀ m k n {n≢0 : False (n ≟ 0)} → k * n ≤ m → ((m ∸ k * n) % n) {n≢0} ≡ (m % n) {n≢0}
[m∸kn]%n≡m%n m zero (suc n) n≤m = refl
[m∸kn]%n≡m%n m (suc k) (suc n) k*n≤m = begin-equality
  (m ∸ (suc n + k * suc n)) % suc n ≡⟨ cong (λ t → t % suc n) (sym (Nat.∸-+-assoc m (suc n) (k * suc n)))  ⟩
  ((m ∸ suc n) ∸ k * suc n) % suc n ≡⟨ [m∸kn]%n≡m%n (m ∸ suc n) k (suc n) k*n≤m∸n ⟩
  (m ∸ suc n) % suc n               ≡⟨ [m∸n]%n≡m%n m (suc n) (+-lower-≤ (k * suc n) k*n≤m) ⟩
  m % suc n                         ∎
  where
  k*n≤m∸n : k * suc n ≤ m ∸ suc n
  k*n≤m∸n = begin
    k * suc n                   ≡⟨ sym (Nat.m+n∸m≡n (suc n) (k * suc n)) ⟩
    (suc n + k * suc n) ∸ suc n ≤⟨ ∸-mono-≤ˡ {suc n} m≤m+n k*n≤m ⟩
    m ∸ suc n                   ∎

%-distribˡ-∸ : ∀ m n d {≢0} → n ≤ m → ((m ∸ n) % d) {≢0} ≡ ((m ∸ (n % d) {≢0}) % d) {≢0}
%-distribˡ-∸ m n (suc d) n≤m = begin-equality
  (m ∸ n) % suc d                               ≡⟨ cong (λ t → (m ∸ t) % suc d) (m≡m%n+[m/n]*n n (suc d)) ⟩
  (m ∸ (n % suc d + n / suc d * suc d)) % suc d ≡⟨ cong (λ t → t % suc d) (sym (Nat.∸-+-assoc m (n % suc d) (n / suc d * suc d))) ⟩
  ((m ∸ n % suc d) ∸ n / suc d * suc d) % suc d ≡⟨ [m∸kn]%n≡m%n (m ∸ n % suc d) (n / suc d) (suc d) n/d*d≤m∸n%d ⟩
  (m ∸ n % suc d) % suc d                       ∎
  where
  n/d*d≤m∸n%d : n / suc d * suc d ≤ m ∸ n % suc d
  n/d*d≤m∸n%d = begin
    n / suc d * suc d                           ≡⟨ sym (Nat.m+n∸m≡n (n % suc d) (n / suc d * suc d)) ⟩
    (n % suc d + n / suc d * suc d) ∸ n % suc d ≡⟨ cong (λ t → t ∸ n % suc d) (sym (m≡m%n+[m/n]*n n (suc d))) ⟩
    n ∸ n % suc d                               ≤⟨ ∸-mono-≤ˡ {n % suc d} (n%m≤n n (suc d)) n≤m ⟩
    m ∸ n % suc d                               ∎

open import AKS.Unsafe using (trustMe)

/-cancelˡ : ∀ c a b {b≢0} {b*c≢0} → ((c * a) / (c * b)) {b*c≢0} ≡ (a / b) {b≢0}
/-cancelˡ (suc c) a (suc b) {b≢0} {b*c≢0} = trustMe

/-cancelʳ : ∀ c a b {b≢0} {b*c≢0} → ((a * c) / (b * c)) {b*c≢0} ≡ (a / b) {b≢0}
/-cancelʳ c a b {b≢0} {b*c≢0} = begin-equality
  (a * c) / (b * c) ≡⟨ /-congˡ {o≢0 = b*c≢0} (*-comm a c) ⟩
  (c * a) / (b * c) ≡⟨ /-congʳ (*-comm b c) ⟩
  (c * a) / (c * b) ≡⟨ /-cancelˡ c a b {b≢0} {c*b≢0} ⟩
  a / b ∎
  where
  c*b≢0 : False (c * b ≟ 0)
  c*b≢0 rewrite *-comm b c = b*c≢0

_div_ : ∀ n m {≢0 : False (m ≟ 0)} → Euclidean n m
n div suc m = Euclidean✓ (n / suc m) (n % suc m) (≡-erase div-proof) (n%m<m n (suc m))
  where
  div-proof : n ≡ n % suc m + suc m * (n / suc m)
  div-proof rewrite *-comm (suc m) (n / suc m) = m≡m%n+[m/n]*n n (suc m)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Order morphisms
------------------------------------------------------------------------

module Relation.Binary.OrderMorphism where

open import Relation.Binary
open Poset
import Function as F
open import Level

record _⇒-Poset_ {p₁ p₂ p₃ p₄ p₅ p₆}
                 (P₁ : Poset p₁ p₂ p₃)
                 (P₂ : Poset p₄ p₅ p₆) : Set (p₁ ⊔ p₃ ⊔ p₄ ⊔ p₆) where
  field
    fun      : Carrier P₁ → Carrier P₂
    monotone : _≤_ P₁ =[ fun ]⇒ _≤_ P₂

_⇒-DTO_ : ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
          DecTotalOrder p₁ p₂ p₃ →
          DecTotalOrder p₄ p₅ p₆ → Set _
DTO₁ ⇒-DTO DTO₂ = poset DTO₁ ⇒-Poset poset DTO₂
  where open DecTotalOrder

open _⇒-Poset_

id : ∀ {p₁ p₂ p₃} {P : Poset p₁ p₂ p₃} → P ⇒-Poset P
id = record
  { fun      = F.id
  ; monotone = F.id
  }

_∘_ : ∀ {p₁ p₂ p₃ p₄ p₅ p₆ p₇ p₈ p₉}
        {P₁ : Poset p₁ p₂ p₃}
        {P₂ : Poset p₄ p₅ p₆}
        {P₃ : Poset p₇ p₈ p₉} →
      P₂ ⇒-Poset P₃ → P₁ ⇒-Poset P₂ → P₁ ⇒-Poset P₃
f ∘ g = record
  { fun      = F._∘_ (fun f)      (fun g)
  ; monotone = F._∘_ (monotone f) (monotone g)
  }

const : ∀ {p₁ p₂ p₃ p₄ p₅ p₆}
          {P₁ : Poset p₁ p₂ p₃}
          {P₂ : Poset p₄ p₅ p₆} →
        Carrier P₂ → P₁ ⇒-Poset P₂
const {P₂ = P₂} x = record
  { fun      = F.const x
  ; monotone = F.const (refl P₂)
  }


module Pi.Syntax where
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product

infixr 12 _×ᵤ_
infixr 11 _+ᵤ_
infixr 50 _⨾_
infixr 10 _↔_
infix  99 !_

-- Types
data 𝕌 : Set where
  𝟘       : 𝕌
  𝟙       : 𝕌
  _+ᵤ_    : 𝕌 → 𝕌 → 𝕌
  _×ᵤ_    : 𝕌 → 𝕌 → 𝕌

⟦_⟧ : (A : 𝕌) → Set
⟦ 𝟘 ⟧ = ⊥
⟦ 𝟙 ⟧ = ⊤
⟦ t₁ +ᵤ t₂ ⟧ = ⟦ t₁ ⟧ ⊎ ⟦ t₂ ⟧
⟦ t₁ ×ᵤ t₂ ⟧ = ⟦ t₁ ⟧ × ⟦ t₂ ⟧

-- Combinators
data _↔_ : 𝕌 → 𝕌 → Set where
  unite₊l  : {t : 𝕌} → 𝟘 +ᵤ t ↔ t
  uniti₊l  : {t : 𝕌} → t ↔ 𝟘 +ᵤ t
  swap₊    : {t₁ t₂ : 𝕌} → t₁ +ᵤ t₂ ↔ t₂ +ᵤ t₁
  assocl₊  : {t₁ t₂ t₃ : 𝕌} → t₁ +ᵤ (t₂ +ᵤ t₃) ↔ (t₁ +ᵤ t₂) +ᵤ t₃
  assocr₊  : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤ t₂) +ᵤ t₃ ↔ t₁ +ᵤ (t₂ +ᵤ t₃)
  unite⋆l  : {t : 𝕌} → 𝟙 ×ᵤ t ↔ t
  uniti⋆l  : {t : 𝕌} → t ↔ 𝟙 ×ᵤ t
  swap⋆    : {t₁ t₂ : 𝕌} → t₁ ×ᵤ t₂ ↔ t₂ ×ᵤ t₁
  assocl⋆  : {t₁ t₂ t₃ : 𝕌} → t₁ ×ᵤ (t₂ ×ᵤ t₃) ↔ (t₁ ×ᵤ t₂) ×ᵤ t₃
  assocr⋆  : {t₁ t₂ t₃ : 𝕌} → (t₁ ×ᵤ t₂) ×ᵤ t₃ ↔ t₁ ×ᵤ (t₂ ×ᵤ t₃)
  absorbr  : {t : 𝕌} → 𝟘 ×ᵤ t ↔ 𝟘
  factorzl : {t : 𝕌} → 𝟘 ↔ 𝟘 ×ᵤ t
  dist     : {t₁ t₂ t₃ : 𝕌} → (t₁ +ᵤ t₂) ×ᵤ t₃ ↔ (t₁ ×ᵤ t₃) +ᵤ (t₂ ×ᵤ t₃)
  factor   : {t₁ t₂ t₃ : 𝕌} → (t₁ ×ᵤ t₃) +ᵤ (t₂ ×ᵤ t₃) ↔ (t₁ +ᵤ t₂) ×ᵤ t₃
  id↔      : {t : 𝕌} → t ↔ t
  _⨾_      : {t₁ t₂ t₃ : 𝕌} → (t₁ ↔ t₂) → (t₂ ↔ t₃) → (t₁ ↔ t₃)
  _⊕_      : {t₁ t₂ t₃ t₄ : 𝕌} → (t₁ ↔ t₃) → (t₂ ↔ t₄) → (t₁ +ᵤ t₂ ↔ t₃ +ᵤ t₄)
  _⊗_      : {t₁ t₂ t₃ t₄ : 𝕌} → (t₁ ↔ t₃) → (t₂ ↔ t₄) → (t₁ ×ᵤ t₂ ↔ t₃ ×ᵤ t₄)

-- Some useful combinators
unite₊r  : {t : 𝕌} → t +ᵤ 𝟘 ↔ t
unite₊r  = swap₊ ⨾ unite₊l

uniti₊r  : {t : 𝕌} → t ↔ t +ᵤ 𝟘
uniti₊r  = uniti₊l ⨾ swap₊

unite⋆r  : {t : 𝕌} → t ×ᵤ 𝟙 ↔ t
unite⋆r  = swap⋆ ⨾ unite⋆l

uniti⋆r  : {t : 𝕌} → t ↔ t ×ᵤ 𝟙
uniti⋆r  = uniti⋆l ⨾ swap⋆

absorbl  : {t : 𝕌} → t ×ᵤ 𝟘 ↔ 𝟘
absorbl  = swap⋆ ⨾ absorbr

factorzr : {t : 𝕌} → 𝟘 ↔ t ×ᵤ 𝟘
factorzr = factorzl ⨾ swap⋆

distl    : {t₁ t₂ t₃ : 𝕌} → t₁ ×ᵤ (t₂ +ᵤ t₃) ↔ (t₁ ×ᵤ t₂) +ᵤ (t₁ ×ᵤ t₃)
distl    = swap⋆ ⨾ dist ⨾ (swap⋆ ⊕ swap⋆)

factorl  : {t₁ t₂ t₃ : 𝕌 } → (t₁ ×ᵤ t₂) +ᵤ (t₁ ×ᵤ t₃) ↔ t₁ ×ᵤ (t₂ +ᵤ t₃)
factorl  = (swap⋆ ⊕ swap⋆) ⨾ factor ⨾ swap⋆

-- Inverses of combinators
!_ : {A B : 𝕌} → A ↔ B → B ↔ A
! unite₊l = uniti₊l
! uniti₊l = unite₊l
! swap₊ = swap₊
! assocl₊ = assocr₊
! assocr₊ = assocl₊
! unite⋆l = uniti⋆l
! uniti⋆l = unite⋆l
! swap⋆ = swap⋆
! assocl⋆ = assocr⋆
! assocr⋆ = assocl⋆
! absorbr = factorzl
! factorzl = absorbr
! dist = factor
! factor = dist
! id↔ = id↔
! (c₁ ⨾ c₂) = ! c₂ ⨾ ! c₁
! (c₁ ⊕ c₂) = (! c₁) ⊕ (! c₂)
! (c₁ ⊗ c₂) = (! c₁) ⊗ (! c₂)


-- Andreas, 2017-01-12, issue #2386

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

data Eq (A : Set) : (x y : id A) → Set where
  refl : (x : A) → Eq A x x

{-# BUILTIN EQUALITY Eq #-}

-- Should be accepted.


module Container.Traversable where

open import Prelude

record Traversable {a} (T : Set a → Set a) : Set (lsuc a) where
  field
    traverse : ∀ {F : Set a → Set a} {A B} {{AppF : Applicative F}} → (A → F B) → T A → F (T B)
    overlap {{super}} : Functor T

open Traversable {{...}} public

record Traversable′ {a b} (T : ∀ {a} → Set a → Set a) : Set (lsuc (a ⊔ b)) where
  field
    traverse′ : ∀ {F : Set b → Set b} {A : Set a} {B : Set b}
                  {{AppF : Applicative F}} → (A → F B) → T A → F (T B)
    overlap {{super}} : Functor′ {a} {b} T

open Traversable′ {{...}} public

{-# DISPLAY Traversable.traverse   _ = traverse #-}
{-# DISPLAY Traversable′.traverse′ _ = traverse′ #-}

--- Instances ---

instance
  TraversableMaybe : ∀ {a} → Traversable {a} Maybe
  traverse {{TraversableMaybe}} f m = maybe (pure nothing) (λ x -> pure just <*> f x) m

  TraversableList : ∀ {a} → Traversable {a} List
  traverse {{TraversableList}} f xs = foldr (λ x fxs → pure _∷_ <*> f x <*> fxs) (pure []) xs

  TraversableVec : ∀ {a n} → Traversable {a} (λ A → Vec A n)
  traverse {{TraversableVec}} f []       = pure []
  traverse {{TraversableVec}} f (x ∷ xs) = ⦇ f x ∷ traverse f xs ⦈

  Traversable′Maybe : ∀ {a b} → Traversable′ {a} {b} Maybe
  traverse′ {{Traversable′Maybe}} f m = maybe (pure nothing) (λ x -> pure just <*> f x) m

  Traversable′List : ∀ {a b} → Traversable′ {a} {b} List
  traverse′ {{Traversable′List}} f xs = foldr (λ x fxs → pure _∷_ <*> f x <*> fxs) (pure []) xs

  Traversable′Vec : ∀ {a b n} → Traversable′ {a} {b} (λ A → Vec A n)
  traverse′ {{Traversable′Vec}} f []       = pure []
  traverse′ {{Traversable′Vec}} f (x ∷ xs) = ⦇ f x ∷ traverse′ f xs ⦈

mapM : ∀ {a b} {F : Set b → Set b} {A : Set a} {B : Set b} {{AppF : Applicative F}} →
         (A → F B) → List A → F (List B)
mapM = traverse′

mapM! : ∀ {a} {F : Set → Set} {A : Set a} {{AppF : Applicative F}} →
          (A → F ⊤) → List A → F ⊤
mapM! f xs = _ <$ mapM f xs


{-# OPTIONS --prop --rewriting #-}

module Examples.Exp2 where

open import Calf.CostMonoid
open import Calf.CostMonoids using (ℕ²-ParCostMonoid)

parCostMonoid = ℕ²-ParCostMonoid
open ParCostMonoid parCostMonoid

open import Calf costMonoid
open import Calf.ParMetalanguage parCostMonoid
open import Calf.Types.Bool
open import Calf.Types.Nat
open import Calf.Types.Bounded costMonoid
open import Calf.Types.BigO costMonoid

open import Relation.Binary.PropositionalEquality as Eq using (_≡_; refl; _≢_; module ≡-Reasoning)
open import Data.Nat as Nat using (_+_; pred; _*_; _^_; _⊔_)
import Data.Nat.Properties as N
open import Data.Nat.PredExp2
open import Data.Product
open import Data.Empty

Correct : cmp (Π nat λ _ → F nat) → Set
Correct exp₂ = (n : ℕ) → ◯ (exp₂ n ≡ ret (2 ^ n))

module Slow where
  exp₂ : cmp (Π nat λ _ → F nat)
  exp₂ zero = ret (suc zero)
  exp₂ (suc n) =
    bind (F nat) (exp₂ n & exp₂ n) λ (r₁ , r₂) →
      step (F nat) (1 , 1) (ret (r₁ + r₂))

  exp₂/correct : Correct exp₂
  exp₂/correct zero    u = refl
  exp₂/correct (suc n) u =
    begin
      exp₂ (suc n)
    ≡⟨⟩
      (bind (F nat) (exp₂ n & exp₂ n) λ (r₁ , r₂) →
        step (F nat) (1 , 1) (ret (r₁ + r₂)))
    ≡⟨ Eq.cong (bind (F nat) (exp₂ n & exp₂ n)) (funext (λ (r₁ , r₂) → step/ext (F nat) _ (1 , 1) u)) ⟩
      (bind (F nat) (exp₂ n & exp₂ n) λ (r₁ , r₂) →
        ret (r₁ + r₂))
    ≡⟨ Eq.cong (λ e → bind (F nat) (e & e) _) (exp₂/correct n u) ⟩
      step (F nat) (𝟘 ⊗ 𝟘) (ret (2 ^ n + 2 ^ n))
    ≡⟨⟩
      ret (2 ^ n + 2 ^ n)
    ≡⟨ Eq.cong ret (lemma/2^suc n) ⟩
      ret (2 ^ suc n)
    ∎
      where open ≡-Reasoning

  exp₂/cost : cmp (Π nat λ _ → cost)
  exp₂/cost zero    = 𝟘
  exp₂/cost (suc n) =
    bind cost (exp₂ n & exp₂ n) λ (r₁ , r₂) → (exp₂/cost n ⊗ exp₂/cost n) ⊕
      ((1 , 1) ⊕ 𝟘)

  exp₂/cost/closed : cmp (Π nat λ _ → cost)
  exp₂/cost/closed n = pred[2^ n ] , n

  exp₂/cost≤exp₂/cost/closed : ∀ n → ◯ (exp₂/cost n ≤ exp₂/cost/closed n)
  exp₂/cost≤exp₂/cost/closed zero    u = ≤-refl
  exp₂/cost≤exp₂/cost/closed (suc n) u =
    let ≡ = exp₂/correct n u in
    let open ≤-Reasoning in
    begin
      exp₂/cost (suc n)
    ≡⟨⟩
      (bind cost (exp₂ n & exp₂ n) λ (r₁ , r₂) → (exp₂/cost n ⊗ exp₂/cost n) ⊕
        ((1 , 1) ⊕ 𝟘))
    ≡⟨ Eq.cong₂ (λ e₁ e₂ → bind cost (e₁ & e₂) λ (r₁ , r₂) → (exp₂/cost n ⊗ exp₂/cost n) ⊕ _) (≡) (≡) ⟩
      (exp₂/cost n ⊗ exp₂/cost n) ⊕ ((1 , 1) ⊕ 𝟘)
    ≡⟨ Eq.cong ((exp₂/cost n ⊗ exp₂/cost n) ⊕_) (⊕-identityʳ _) ⟩
      (exp₂/cost n ⊗ exp₂/cost n) ⊕ (1 , 1)
    ≤⟨ ⊕-monoˡ-≤ (1 , 1) (⊗-mono-≤ (exp₂/cost≤exp₂/cost/closed n u) (exp₂/cost≤exp₂/cost/closed n u)) ⟩
      (exp₂/cost/closed n ⊗ exp₂/cost/closed n) ⊕ (1 , 1)
    ≡⟨ Eq.cong₂ _,_ arithmetic/work arithmetic/span ⟩
        exp₂/cost/closed (suc n)
      ∎
      where
        arithmetic/work : proj₁ (exp₂/cost/closed n ⊗ exp₂/cost/closed n ⊕ (1 , 1)) ≡ proj₁ (exp₂/cost/closed (suc n))
        arithmetic/work =
          begin
            proj₁ (exp₂/cost/closed n ⊗ exp₂/cost/closed n ⊕ (1 , 1))
          ≡⟨⟩
            proj₁ (exp₂/cost/closed n) + proj₁ (exp₂/cost/closed n) + 1
          ≡⟨ N.+-comm _ 1 ⟩
            suc (proj₁ (exp₂/cost/closed n) + proj₁ (exp₂/cost/closed n))
          ≡⟨⟩
            suc (pred[2^ n ] + pred[2^ n ])
          ≡⟨ pred[2^suc[n]] n ⟩
            pred[2^ suc n ]
          ≡⟨⟩
            proj₁ (exp₂/cost/closed (suc n))
          ∎
            where open ≡-Reasoning

        arithmetic/span : proj₂ (exp₂/cost/closed n ⊗ exp₂/cost/closed n ⊕ (1 , 1)) ≡ proj₂ (exp₂/cost/closed (suc n))
        arithmetic/span =
          begin
            proj₂ (exp₂/cost/closed n ⊗ exp₂/cost/closed n ⊕ (1 , 1))
          ≡⟨⟩
            proj₂ (exp₂/cost/closed n) ⊔ proj₂ (exp₂/cost/closed n) + 1
          ≡⟨⟩
            n ⊔ n + 1
          ≡⟨ Eq.cong (_+ 1) (N.⊔-idem n) ⟩
            n + 1
          ≡⟨ N.+-comm _ 1 ⟩
            suc n
          ≡⟨⟩
            proj₂ (exp₂/cost/closed (suc n))
          ∎
            where open ≡-Reasoning

  exp₂≤exp₂/cost : ∀ n → IsBounded nat (exp₂ n) (exp₂/cost n)
  exp₂≤exp₂/cost zero    = bound/ret
  exp₂≤exp₂/cost (suc n) =
    bound/bind (exp₂/cost n ⊗ exp₂/cost n) _ (bound/par (exp₂≤exp₂/cost n) (exp₂≤exp₂/cost n)) λ (r₁ , r₂) →
      bound/step (1 , 1) 𝟘 bound/ret

  exp₂≤exp₂/cost/closed : ∀ n → IsBounded nat (exp₂ n) (exp₂/cost/closed n)
  exp₂≤exp₂/cost/closed n = bound/relax (exp₂/cost≤exp₂/cost/closed n) (exp₂≤exp₂/cost n)

  exp₂/asymptotic : given nat measured-via (λ n → n) , exp₂ ∈𝓞(λ n → 2 ^ n , n)
  exp₂/asymptotic = 0 ≤n⇒f[n]≤g[n]via λ n _ → bound/relax (λ u → N.pred[n]≤n , N.≤-refl) (exp₂≤exp₂/cost/closed n)

module Fast where

  exp₂ : cmp (Π nat λ _ → F nat)
  exp₂ zero = ret (suc zero)
  exp₂ (suc n) =
    bind (F nat) (exp₂ n) λ r →
      step (F nat) (1 , 1) (ret (r + r))

  exp₂/correct : Correct exp₂
  exp₂/correct zero    u = refl
  exp₂/correct (suc n) u =
    begin
      exp₂ (suc n)
    ≡⟨⟩
      (bind (F nat) (exp₂ n) λ r →
        step (F nat) (1 , 1) (ret (r + r)))
    ≡⟨ Eq.cong (bind (F nat) (exp₂ n)) (funext (λ r → step/ext (F nat) _ (1 , 1) u)) ⟩
      (bind (F nat) (exp₂ n) λ r →
        ret (r + r))
    ≡⟨ Eq.cong (λ e → bind (F nat) e _) (exp₂/correct n u) ⟩
      (bind (F nat) (ret {nat} (2 ^ n)) λ r →
        ret (r + r))
    ≡⟨⟩
      ret (2 ^ n + 2 ^ n)
    ≡⟨ Eq.cong ret (lemma/2^suc n) ⟩
      ret (2 ^ suc n)
    ∎
      where open ≡-Reasoning

  exp₂/cost : cmp (Π nat λ _ → cost)
  exp₂/cost zero    = 𝟘
  exp₂/cost (suc n) =
    bind cost (exp₂ n) λ r → exp₂/cost n ⊕
      ((1 , 1) ⊕ 𝟘)

  exp₂/cost/closed : cmp (Π nat λ _ → cost)
  exp₂/cost/closed n = n , n

  exp₂/cost≤exp₂/cost/closed : ∀ n → ◯ (exp₂/cost n ≤ exp₂/cost/closed n)
  exp₂/cost≤exp₂/cost/closed zero    u = ≤-refl
  exp₂/cost≤exp₂/cost/closed (suc n) u =
    let open ≤-Reasoning in
    begin
      exp₂/cost (suc n)
    ≡⟨⟩
      (bind cost (exp₂ n) λ r → exp₂/cost n ⊕
        ((1 , 1) ⊕ 𝟘))
    ≡⟨ Eq.cong (λ e → bind cost e λ r → exp₂/cost n ⊕ _) (exp₂/correct n u) ⟩
      exp₂/cost n ⊕ ((1 , 1) ⊕ 𝟘)
    ≤⟨ ⊕-monoˡ-≤ ((1 , 1) ⊕ 𝟘) (exp₂/cost≤exp₂/cost/closed n u) ⟩
      exp₂/cost/closed n ⊕ ((1 , 1) ⊕ 𝟘)
    ≡⟨ Eq.cong (exp₂/cost/closed n ⊕_) (⊕-identityʳ _) ⟩
      exp₂/cost/closed n ⊕ (1 , 1)
    ≡⟨ Eq.cong₂ _,_ (N.+-comm _ 1) (N.+-comm _ 1) ⟩
      exp₂/cost/closed (suc n)
    ∎

  exp₂≤exp₂/cost : ∀ n → IsBounded nat (exp₂ n) (exp₂/cost n)
  exp₂≤exp₂/cost zero    = bound/ret
  exp₂≤exp₂/cost (suc n) =
    bound/bind (exp₂/cost n) _ (exp₂≤exp₂/cost n) λ r →
      bound/step (1 , 1) 𝟘 bound/ret

  exp₂≤exp₂/cost/closed : ∀ n → IsBounded nat (exp₂ n) (exp₂/cost/closed n)
  exp₂≤exp₂/cost/closed n = bound/relax (exp₂/cost≤exp₂/cost/closed n) (exp₂≤exp₂/cost n)

  exp₂/asymptotic : given nat measured-via (λ n → n) , exp₂ ∈𝓞(λ n → n , n)
  exp₂/asymptotic = 0 ≤n⇒f[n]≤ 1 g[n]via λ n _ → Eq.subst (IsBounded _ _) (Eq.sym (⊕-identityʳ _)) (exp₂≤exp₂/cost/closed n)

slow≡fast : ◯ (Slow.exp₂ ≡ Fast.exp₂)
slow≡fast u = funext λ n →
  begin
    Slow.exp₂ n
  ≡⟨ Slow.exp₂/correct n u ⟩
    ret (2 ^ n)
  ≡˘⟨ Fast.exp₂/correct n u ⟩
    Fast.exp₂ n
  ∎
    where open ≡-Reasoning


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

{-
  Ribbon is an explicit covering space construction.

  This construction is given by Daniel Grayson, Favonia
  and Guillaume Brunerie together.
-}

open import HoTT

-- A is the pointed base space.
-- El is intended to be a (group-)set,
module homotopy.RibbonCover {i : ULevel} where

  -- The HIT ribbon---reconstructed covering space

  private
    π1 = fundamental-group

  private
    module _ (A∙ : Ptd i) {j} (gs : Gset (π1 A∙) j) where
      private
        A = fst A∙
        a = snd A∙
        El = Gset.El gs
        El-level = Gset.El-level gs
        _⊙_ = Gset.act gs

      {-
        What the following data type definition should be
        when we have a proof assistant for HoTT:

        data Ribbon (a₂ : A) : Set where
          trace : El → a =₀ a₂ → Ribbon a₂
          paste : ∀ el loop (p : a =₀ a₂)
                → trace (el ⊙ loop) p == trace el (loop ∙₀ p)
      -}

      data #Ribbon-aux (a₂ : A) : Type (lmax i j) where
        #trace : El → a =₀ a₂ → #Ribbon-aux a₂

      data #Ribbon (a₂ : A) : Type (lmax i j) where
        #ribbon : #Ribbon-aux a₂ → (Unit → Unit) → #Ribbon a₂

  Ribbon : ∀ (A∙ : Ptd i) {j} (gs : Gset (π1 A∙) j)
    → fst A∙ → Type (lmax i j)
  Ribbon = #Ribbon

  module _ {A∙ : Ptd i} {j} {gs : Gset (fundamental-group A∙) j} {a₂ : fst A∙} where
    private
      A = fst A∙
      a = snd A∙
      El = Gset.El gs
      El-level = Gset.El-level gs
      _⊙_ = Gset.act gs

    -- A point in the fiber [a₂].
    {-
      [e] is a point in the [fiber a], and
      [p] is a path to transport [y] to fiber [a₂].
    -}
    trace : El → a =₀ a₂ → Ribbon A∙ gs a₂
    trace el p = #ribbon (#trace el p) _

    {-
      A loop based at [a] can used as a group action
      or for concatination.  Both should be equivalent.
    -}
    postulate -- HIT
      paste : ∀ el loop (p : a =₀ a₂)
        → trace (el ⊙ loop) p == trace el (loop ∙₀ p)

    {-
      Make each fiber a set and cancel all higher structures
      due to [paste].
    -}
    postulate -- HIT
      Ribbon-is-set : is-set (Ribbon A∙ gs a₂)

    Ribbon-level = Ribbon-is-set

    -- Elimination rules.
    module RibbonElim {j} {P : Ribbon A∙ gs a₂ → Type j}
      (P-level : ∀ {r} → is-set (P r))
      (trace* : ∀ el p → P (trace el p))
      (paste* : ∀ el loop p
                → trace* (el ⊙ loop) p == trace* el (loop ∙₀ p)
                  [ P ↓ paste el loop p ]) where

      f : Π (Ribbon A∙ gs a₂) P
      f = f-aux phantom phantom where

        f-aux : Phantom trace* → Phantom paste* → Π (Ribbon A∙ gs a₂) P
        f-aux phantom phantom (#ribbon (#trace el p) _) = trace* el p

    open RibbonElim public using () renaming (f to Ribbon-elim)

    module RibbonRec {j} {P : Type j}
      (P-level : is-set P)
      (trace* : ∀ el p → P)
      (paste* : ∀ el loop p
                → trace* (el ⊙ loop) p == trace* el (loop ∙₀ p)) where

      private
        module M = RibbonElim P-level trace*
          (λ el loop p → ↓-cst-in (paste* el loop p))

      f : Ribbon A∙ gs a₂ → P
      f = M.f

    open RibbonRec public using () renaming (f to Ribbon-rec)

  -- This data structure gives a cover.
  Ribbon-cover : ∀ (A∙ : Ptd i) {j} (gs : Gset (π1 A∙) j)
    → Cover (fst A∙) (lmax i j)
  Ribbon-cover A∙ gs = record
    { Fiber = Ribbon A∙ gs
    ; Fiber-level = λ a → Ribbon-level
    }

  trans-trace : ∀ {A : Type i} {a₁} {j}
    {gs : Gset (π1 (A , a₁)) j}
    {a₂} (q : a₁ == a₂) y p
    → transport (Ribbon (A , a₁) gs) q (trace y p) == trace y (p ∙₀ [ q ])
  trans-trace idp y p = ap (trace y) $ ! $ ∙₀-unit-r p


{-

Definition of the Klein bottle as a HIT

-}
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.HITs.KleinBottle.Base where

open import Cubical.Core.Everything

data KleinBottle : Type where
  point : KleinBottle
  line1 : point ≡ point
  line2 : point ≡ point
  square : PathP (λ i → line1 (~ i) ≡ line1 i) line2 line2


------------------------------------------------------------------------
-- The Agda standard library
--
-- Sum combinators for propositional equality preserving functions
------------------------------------------------------------------------

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

module Data.Sum.Function.Propositional where

open import Data.Sum
open import Data.Sum.Function.Setoid
open import Data.Sum.Relation.Binary.Pointwise using (Pointwise-≡↔≡)
open import Function.Equivalence as Eq using (_⇔_; module Equivalence)
open import Function.Injection as Inj using (_↣_; module Injection)
open import Function.Inverse as Inv using (_↔_; module Inverse)
open import Function.LeftInverse as LeftInv
  using (_↞_; _LeftInverseOf_; module LeftInverse)
open import Function.Related
open import Function.Surjection as Surj using (_↠_; module Surjection)

------------------------------------------------------------------------
-- Combinators for various function types

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

  _⊎-⇔_ : A ⇔ B → C ⇔ D → (A ⊎ C) ⇔ (B ⊎ D)
  _⊎-⇔_ A⇔B C⇔D =
    Inverse.equivalence (Pointwise-≡↔≡ B D) ⟨∘⟩
    (A⇔B ⊎-equivalence C⇔D) ⟨∘⟩
    Eq.sym (Inverse.equivalence (Pointwise-≡↔≡ A C))
    where open Eq using () renaming (_∘_ to _⟨∘⟩_)

  _⊎-↣_ : A ↣ B → C ↣ D → (A ⊎ C) ↣ (B ⊎ D)
  _⊎-↣_ A↣B C↣D =
    Inverse.injection (Pointwise-≡↔≡ B D) ⟨∘⟩
    (A↣B ⊎-injection C↣D) ⟨∘⟩
    Inverse.injection (Inv.sym (Pointwise-≡↔≡ A C))
    where open Inj using () renaming (_∘_ to _⟨∘⟩_)

  _⊎-↞_ : A ↞ B → C ↞ D → (A ⊎ C) ↞ (B ⊎ D)
  _⊎-↞_ A↞B C↞D =
    Inverse.left-inverse (Pointwise-≡↔≡ B D) ⟨∘⟩
    (A↞B ⊎-left-inverse C↞D) ⟨∘⟩
    Inverse.left-inverse (Inv.sym (Pointwise-≡↔≡ A C))
    where open LeftInv using () renaming (_∘_ to _⟨∘⟩_)

  _⊎-↠_ : A ↠ B → C ↠ D → (A ⊎ C) ↠ (B ⊎ D)
  _⊎-↠_ A↠B C↠D =
    Inverse.surjection (Pointwise-≡↔≡ B D) ⟨∘⟩
    (A↠B ⊎-surjection C↠D) ⟨∘⟩
    Inverse.surjection (Inv.sym (Pointwise-≡↔≡ A C))
    where open Surj using () renaming (_∘_ to _⟨∘⟩_)

  _⊎-↔_ : A ↔ B → C ↔ D → (A ⊎ C) ↔ (B ⊎ D)
  _⊎-↔_ A↔B C↔D =
    Pointwise-≡↔≡ B D ⟨∘⟩
    (A↔B ⊎-inverse C↔D) ⟨∘⟩
    Inv.sym (Pointwise-≡↔≡ A C)
    where open Inv using () renaming (_∘_ to _⟨∘⟩_)

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

  _⊎-cong_ : ∀ {k} → A ∼[ k ] B → C ∼[ k ] D → (A ⊎ C) ∼[ k ] (B ⊎ D)
  _⊎-cong_ {implication}         = map
  _⊎-cong_ {reverse-implication} = λ f g → lam (map (app-← f) (app-← g))
  _⊎-cong_ {equivalence}         = _⊎-⇔_
  _⊎-cong_ {injection}           = _⊎-↣_
  _⊎-cong_ {reverse-injection}   = λ f g → lam (app-↢ f ⊎-↣ app-↢ g)
  _⊎-cong_ {left-inverse}        = _⊎-↞_
  _⊎-cong_ {surjection}          = _⊎-↠_
  _⊎-cong_ {bijection}           = _⊎-↔_


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

module Cubical.Categories.Limits.Pullback where

open import Cubical.Foundations.Prelude
open import Cubical.Categories.Limits.Base
open import Cubical.Categories.Category
open import Cubical.Categories.Functor
open import Cubical.Data.Sigma
open import Cubical.Categories.Instances.Cospan public

private
  variable
    ℓ ℓ' : Level

module _ {C : Precategory ℓ ℓ'} where

  open Precategory C
  open Functor

  record Cospan : Type (ℓ-max ℓ ℓ') where
    constructor cospan
    field
      l m r : ob
      s₁ : C [ l , m ]
      s₂ : C [ r , m ]

  record PullbackLegs (cspn : Cospan) (c : ob) : Type (ℓ-max ℓ ℓ') where
    constructor pblegs
    open Cospan cspn
    field
      p₁ : C [ c , l ]
      p₂ : C [ c , r ]

  record PullbackCone (cspn : Cospan) (c : ob) : Type (ℓ-max ℓ ℓ') where
    constructor cone
    open Cospan cspn
    field
      pl : PullbackLegs cspn c
    open PullbackLegs pl public
    field
      sq : p₁ ⋆⟨ C ⟩ s₁ ≡ p₂ ⋆⟨ C ⟩ s₂

  record isPullback (cspn : _) {c} (pb : PullbackLegs cspn c) : Type (ℓ-max ℓ ℓ') where
    open Cospan cspn
    open PullbackLegs
    field
      sq : pb .p₁ ⋆⟨ C ⟩ s₁ ≡ pb .p₂ ⋆⟨ C ⟩ s₂

    open PullbackCone
    field
      up : ∀ {d}
         → (pb' : PullbackCone cspn d)
         → isContr (Σ[ f ∈ (C [ d , c ]) ] ((pb' .p₁ ≡ f ⋆⟨ C ⟩ pb .p₁)
              × (pb' .p₂ ≡ f ⋆⟨ C ⟩ pb .p₂)))

  Cospan→Func : Cospan → Functor CospanCat C
  Cospan→Func (cospan l m r f g) .F-ob ⓪ = l
  Cospan→Func (cospan l m r f g) .F-ob ① = m
  Cospan→Func (cospan l m r f g) .F-ob ② = r
  Cospan→Func (cospan l m r f g) .F-hom {⓪} {①} k = f
  Cospan→Func (cospan l m r f g) .F-hom {②} {①} k = g
  Cospan→Func (cospan l m r f g) .F-hom {⓪} {⓪} k = id l
  Cospan→Func (cospan l m r f g) .F-hom {①} {①} k = id m
  Cospan→Func (cospan l m r f g) .F-hom {②} {②} k = id r
  Cospan→Func (cospan l m r f g) .F-id {⓪} = refl
  Cospan→Func (cospan l m r f g) .F-id {①} = refl
  Cospan→Func (cospan l m r f g) .F-id {②} = refl
  Cospan→Func (cospan l m r f g) .F-seq {⓪} {⓪} {⓪} φ ψ = sym (⋆IdL _)
  Cospan→Func (cospan l m r f g) .F-seq {⓪} {⓪} {①} φ ψ = sym (⋆IdL _)
  Cospan→Func (cospan l m r f g) .F-seq {⓪} {①} {①} φ ψ = sym (⋆IdR _)
  Cospan→Func (cospan l m r f g) .F-seq {①} {①} {①} φ ψ = sym (⋆IdL _)
  Cospan→Func (cospan l m r f g) .F-seq {②} {②} {②} φ ψ = sym (⋆IdL _)
  Cospan→Func (cospan l m r f g) .F-seq {②} {②} {①} φ ψ = sym (⋆IdL _)
  Cospan→Func (cospan l m r f g) .F-seq {②} {①} {①} φ ψ = sym (⋆IdR _)

  -- TODO: show that this definition of Pullback is equivalent to the Cospan limit


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of disjoint lists (setoid equality)
------------------------------------------------------------------------

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

module Data.List.Relation.Binary.Disjoint.Setoid.Properties where

open import Data.List.Base
open import Data.List.Relation.Binary.Disjoint.Setoid
import Data.List.Relation.Unary.Any as Any
open import Data.List.Relation.Unary.All as All
open import Data.List.Relation.Unary.All.Properties using (¬Any⇒All¬)
open import Data.List.Relation.Unary.Any.Properties using (++⁻)
open import Data.Product using (_,_)
open import Data.Sum.Base using (inj₁; inj₂)
open import Relation.Binary
open import Relation.Nullary using (¬_)

------------------------------------------------------------------------
-- Relational properties
------------------------------------------------------------------------

module _ {c ℓ} (S : Setoid c ℓ) where

  sym : Symmetric (Disjoint S)
  sym xs#ys (v∈ys , v∈xs) = xs#ys (v∈xs , v∈ys)

------------------------------------------------------------------------
-- Relationship with other predicates
------------------------------------------------------------------------

module _ {c ℓ} (S : Setoid c ℓ) where

  open Setoid S

  Disjoint⇒AllAll : ∀ {xs ys} → Disjoint S xs ys →
                    All (λ x → All (λ y → ¬ x ≈ y) ys) xs
  Disjoint⇒AllAll xs#ys = All.map (¬Any⇒All¬ _)
    (All.tabulate (λ v∈xs v∈ys → xs#ys (Any.map reflexive v∈xs , v∈ys)))

------------------------------------------------------------------------
-- Introduction (⁺) and elimination (⁻) rules for list operations
------------------------------------------------------------------------
-- concat

module _ {c ℓ} (S : Setoid c ℓ) where

  concat⁺ʳ : ∀ {vs xss} → All (Disjoint S vs) xss → Disjoint S vs (concat xss)
  concat⁺ʳ {xss = xs ∷ xss} (vs#xs ∷ vs#xss) (v∈vs , v∈xs++concatxss)
    with ++⁻ xs v∈xs++concatxss
  ... | inj₁ v∈xs  = vs#xs (v∈vs , v∈xs)
  ... | inj₂ v∈xss = concat⁺ʳ vs#xss (v∈vs , v∈xss)


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

open import Agda.Primitive renaming (_⊔_ to ℓ-max; lsuc to ℓ-suc; lzero to ℓ-zero)

module Number.Postulates where

private
  variable
    ℓ ℓ' ℓ'' : Level

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Relation.Nullary.Base -- ¬_
open import Cubical.Relation.Binary.Base -- Rel
open import Function.Base using (_∋_)

ℕℓ  = ℓ-zero
ℕℓ' = ℓ-zero

postulate
  ℤℓ ℤℓ' : Level
  ℚℓ ℚℓ' : Level
  ℝℓ ℝℓ' : Level
  ℂℓ ℂℓ' : Level

open import Number.Structures
open import Number.Bundles
import Number.Inclusions

import MoreAlgebra

module ℕ* where
  import Cubical.Data.Nat as Nat
  import Cubical.Data.Nat.Order as Order

  open import Agda.Builtin.Nat using () renaming (Nat to ℕ) public

  module Postulates where
    postulate
      min max : ℕ → ℕ → ℕ
      isROrderedCommSemiring : IsROrderedCommSemiring
        (Order._<_)
        (Order._≤_)
        ((MoreAlgebra.Definitions._#'_ {_<_ = Order._<_}))
        (min)
        (max)
        (Nat.zero)
        (1)
        (Nat._+_)
        (Nat._*_)

  module Bundle = ROrderedCommSemiring {ℕℓ} {ℕℓ'}
  Bundle = ROrderedCommSemiring {ℕℓ} {ℕℓ'}

  Carrier = ℕ

  bundle : Bundle
  bundle = (record
    { Carrier = ℕ
    ; _<_ = Order._<_
    ; _≤_ = Order._≤_
    ; _#_ = (MoreAlgebra.Definitions._#'_ { _<_ = Order._<_ })
    ; min = Postulates.min
    ; max = Postulates.max
    ; 0f  = Nat.zero
    ; 1f  = (Nat.suc Nat.zero)
    ; _+_ = Nat._+_
    ; _·_ = Nat._*_
    ; isROrderedCommSemiring = Postulates.isROrderedCommSemiring
    })

  open import Cubical.Data.Nat.Order using (_≤_; _<_) public
  import MoreAlgebra
  open MoreAlgebra.Definitions using (_#'_) public
  open import Agda.Builtin.Nat using () renaming (zero to 0f)  public

  -- _<_ = Bundle._<_ bundle
  -- _≤_ = Bundle._≤_ bundle
  _#_ = Bundle._#_ bundle
  min = Bundle.min bundle
  max = Bundle.max bundle
  -- 0f  = Bundle.0f  bundle
  1f  = Bundle.1f  bundle
  _+_ = Bundle._+_ bundle
  _·_ = Bundle._·_ bundle
  isROrderedCommSemiring = Bundle.isROrderedCommSemiring bundle

  abs : ℕ → ℕ
  abs x = x

  open IsROrderedCommSemiring isROrderedCommSemiring public

module ℕ  = ℕ* hiding (ℕ)
module ℕⁿ = ℕ*
    renaming
    ( Carrier to Carrierⁿ
    ; _<_ to _<ⁿ_
    ; _≤_ to _≤ⁿ_
    ; _#_ to _#ⁿ_
    ; min to minⁿ
    ; max to maxⁿ
    ; 0f  to 0ⁿ
    ; 1f  to 1ⁿ
    ; _+_ to _+ⁿ_
    ; _·_ to _·ⁿ_
    ; isROrderedCommSemiring to isROrderedCommSemiringⁿ
    ; abs to absⁿ
    )

module ℤ* where
  module Postulates where
    postulate
      ℤ           : Type ℤℓ
      _<_ _≤_ _#_ : Rel ℤ ℤ ℤℓ'
      min max     : ℤ → ℤ → ℤ
      0f 1f       : ℤ
      _+_ _·_     : ℤ → ℤ → ℤ
      -_          : ℤ → ℤ

    abs : ℤ → ℤ
    abs x = max x (- x)

    postulate
      isROrderedCommRing : IsROrderedCommRing _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_
      isAbsOrderedCommRing : IsAbsOrderedCommRing _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_ abs

  module Bundle = ROrderedCommRing {ℤℓ} {ℤℓ'}
  Bundle = ROrderedCommRing {ℤℓ} {ℤℓ'}

  open Postulates public

  Carrier = ℤ

  bundle : Bundle
  bundle = (record
    { Carrier = ℤ
    ; _<_ = _<_
    ; _≤_ = _≤_
    ; _#_ = _#_
    ; min = min
    ; max = max
    ; 0f  = 0f
    ; 1f  = 1f
    ; _+_ = _+_
    ; _·_ = _·_
    ; -_  = -_
    ; isROrderedCommRing = isROrderedCommRing
    ; isAbsOrderedCommRing = isAbsOrderedCommRing
    })

  open IsROrderedCommRing isROrderedCommRing public

  -- abs : ℤ → ℤ
  -- abs x = max x (- x)

module ℤ  = ℤ* hiding (ℤ)
module ℤᶻ = ℤ*
    renaming
    ( Carrier to Carrierᶻ
    ; _<_ to _<ᶻ_
    ; _≤_ to _≤ᶻ_
    ; _#_ to _#ᶻ_
    ; min to minᶻ
    ; max to maxᶻ
    ; 0f  to 0ᶻ
    ; 1f  to 1ᶻ
    ; _+_ to _+ᶻ_
    ; _·_ to _·ᶻ_
    ; -_  to -ᶻ_
    ; isROrderedCommRing to isROrderedCommRingᶻ
    ; abs to absᶻ
    )

module ℚ* where
  module Postulates where
    postulate
      ℚ           : Type ℚℓ
      _<_ _≤_ _#_ : Rel ℚ ℚ ℚℓ'
      min max     : ℚ → ℚ → ℚ
      0f 1f       : ℚ
      _+_ _·_     : ℚ → ℚ → ℚ
      -_          : ℚ → ℚ
      _⁻¹         : (x : ℚ) → {{ x # 0f }} → ℚ

    abs : ℚ → ℚ
    abs x = max x (- x)

    postulate
      isROrderedField : IsROrderedField _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_ _⁻¹
      isAbsOrderedCommRing : IsAbsOrderedCommRing _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_ abs

  module Bundle = ROrderedField {ℚℓ} {ℚℓ'}
  Bundle = ROrderedField {ℚℓ} {ℚℓ'}

  open Postulates public

  Carrier = ℚ

  bundle : Bundle
  bundle = (record
    { Carrier = ℚ
    ; _<_ = _<_
    ; _≤_ = _≤_
    ; _#_ = _#_
    ; min = min
    ; max = max
    ; 0f  = 0f
    ; 1f  = 1f
    ; _+_ = _+_
    ; _·_ = _·_
    ; -_  = -_
    ; _⁻¹ = _⁻¹
    ; isROrderedField = isROrderedField
    ; isAbsOrderedCommRing = isAbsOrderedCommRing
    })

  -- abs : ℚ → ℚ
  -- abs x = max x (- x)

  open IsROrderedField isROrderedField public

module ℚ  = ℚ* hiding (ℚ)
module ℚᶠ = ℚ*
  renaming
  ( Carrier to Carrierᶠ
  ; _<_ to _<ᶠ_
  ; _≤_ to _≤ᶠ_
  ; _#_ to _#ᶠ_
  ; min to minᶠ
  ; max to maxᶠ
  ; 0f  to 0ᶠ
  ; 1f  to 1ᶠ
  ; _+_ to _+ᶠ_
  ; _·_ to _·ᶠ_
  ; -_  to -ᶠ_
  ; _⁻¹ to _⁻¹ᶠ
  ; isROrderedField to isROrderedFieldᶠ
  ; abs to absᶠ
  )

module ℝ* where
  private
    module Postulates where
      postulate
        ℝ           : Type ℝℓ
        _<_ _≤_ _#_ : Rel ℝ ℝ ℝℓ'
        min max     : ℝ → ℝ → ℝ
        0f 1f       : ℝ
        _+_ _·_     : ℝ → ℝ → ℝ
        -_          : ℝ → ℝ
        _⁻¹         : (x : ℝ) → {{ x # 0f }} → ℝ

      abs : ℝ → ℝ
      abs x = max x (- x)

      postulate
        isROrderedField : IsROrderedField _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_ _⁻¹
        isAbsOrderedCommRing : IsAbsOrderedCommRing _<_ _≤_ _#_ min max 0f 1f _+_ _·_ -_ abs

        -- square root on ℝ₀⁺
        sqrt : (x : ℝ) → {{0f ≤ x}} → ℝ
        0≤sqrt : ∀ x → {{p : 0f ≤ x}} → 0f ≤ sqrt x {{p}}
        sqrt-reflects-≡ : ∀ x y → {{px : 0f ≤ x}} → {{py : 0f ≤ y}} → sqrt x {{px}} ≡ sqrt y {{py}} → x ≡ y
        sqrt-inv : ∀ x → {{p : 0f ≤ x}} → {{q : 0f ≤ (x · x)}}→ sqrt (x · x) {{q}} ≡ x
        sqrt²-id : ∀ x → {{p : 0f ≤ x}} → sqrt x {{p}} · sqrt x {{p}} ≡ x

  module Bundle = ROrderedField {ℝℓ} {ℝℓ'}
  Bundle = ROrderedField {ℝℓ} {ℝℓ'}

  open Postulates public

  Carrier = ℝ

  bundle : Bundle
  bundle = (record
    { Carrier = ℝ
    ; _<_ = _<_
    ; _≤_ = _≤_
    ; _#_ = _#_
    ; min = min
    ; max = max
    ; 0f  = 0f
    ; 1f  = 1f
    ; _+_ = _+_
    ; _·_ = _·_
    ; -_  = -_
    ; _⁻¹ = _⁻¹
    ; isROrderedField = isROrderedField
    ; isAbsOrderedCommRing = isAbsOrderedCommRing
    })

  -- abs : ℝ → ℝ
  -- abs x = max x (- x)

  open IsROrderedField isROrderedField public

module ℝ  = ℝ* hiding (ℝ)
module ℝʳ = ℝ*
    renaming
    ( Carrier to Carrierʳ
    ; _<_ to _<ʳ_
    ; _≤_ to _≤ʳ_
    ; _#_ to _#ʳ_
    ; min to minʳ
    ; max to maxʳ
    ; 0f  to 0ʳ
    ; 1f  to 1ʳ
    ; _+_ to _+ʳ_
    ; _·_ to _·ʳ_
    ; -_  to -ʳ_
    ; _⁻¹ to _⁻¹ʳ
    ; isROrderedField to isROrderedFieldʳ
    ; isRField to isRFieldʳ
    ; Bundle to Bundleʳ
    ; bundle to bundleʳ
    ; abs to absʳ
    )

module ℂ* where
  open ℝʳ
  module Postulates where
    postulate
      ℂ           : Type ℂℓ
      _#_         : Rel ℂ ℂ ℂℓ'
      0f 1f       : ℂ
      _+_ _·_     : ℂ → ℂ → ℂ
      -_          : ℂ → ℂ
      _⁻¹         : (x : ℂ) → {{ x # 0f }} → ℂ
      isRField : IsRField _#_ 0f 1f _+_ _·_ -_ _⁻¹
      abs         : ℂ → ℝ
      0≤abs       : ∀ x → 0ʳ ≤ʳ abs x
      abs-preserves-0 : ∀ x → x ≡ 0f → abs x ≡ 0ʳ
      abs-reflects-0  : ∀ x → abs x ≡ 0ʳ → x ≡ 0f
      abs-preserves-· : ∀ x y → abs (x · y) ≡ (abs x) ·ʳ (abs y)
      abs-preserves-#0 : ∀ x → x # 0f → abs x #ʳ 0ʳ
      abs-reflects-#0 : ∀ x → abs x #ʳ 0ʳ → x # 0f

  module Bundle = RField {ℂℓ} {ℂℓ'}
  Bundle = RField {ℂℓ} {ℂℓ'}

  open Postulates public

  Carrier = ℂ

  bundle : Bundle
  bundle = (record
    { Carrier  = ℂ
    ; _#_      = _#_
    ; 0f       = 0f
    ; 1f       = 1f
    ; _+_      = _+_
    ; _·_      = _·_
    ; -_       = -_
    ; _⁻¹      = _⁻¹
    ; isRField = isRField
    })

  open IsRField isRField public

module ℂ  = ℂ* hiding (ℂ)
module ℂᶜ = ℂ*
    renaming
    ( Carrier to Carrierᶜ
    ; _#_ to _#ᶜ_
    ; 0f  to 0ᶜ
    ; 1f  to 1ᶜ
    ; _+_ to _+ᶜ_
    ; _·_ to _·ᶜ_
    ; -_  to -ᶜ_
    ; _⁻¹ to _⁻¹ᶜ
    ; isRField to isRFieldᶜ
    ; abs to absᶜ
    )

Isℕ↪ℤ = Number.Inclusions.IsROrderedCommSemiringInclusion
Isℕ↪ℚ = Number.Inclusions.IsROrderedCommSemiringInclusion
Isℕ↪ℂ = Number.Inclusions.IsRCommSemiringInclusion
Isℕ↪ℝ = Number.Inclusions.IsROrderedCommSemiringInclusion
Isℤ↪ℚ = Number.Inclusions.IsROrderedCommRingInclusion
Isℤ↪ℝ = Number.Inclusions.IsROrderedCommRingInclusion
Isℤ↪ℂ = Number.Inclusions.IsRCommRingInclusion
Isℚ↪ℝ = Number.Inclusions.IsROrderedFieldInclusion
Isℚ↪ℂ = Number.Inclusions.IsRFieldInclusion
Isℝ↪ℂ = Number.Inclusions.IsRFieldInclusion

module Isℕ↪ℤ = Number.Inclusions.IsROrderedCommSemiringInclusion
module Isℕ↪ℚ = Number.Inclusions.IsROrderedCommSemiringInclusion
module Isℕ↪ℂ = Number.Inclusions.IsRCommSemiringInclusion
module Isℕ↪ℝ = Number.Inclusions.IsROrderedCommSemiringInclusion
module Isℤ↪ℚ = Number.Inclusions.IsROrderedCommRingInclusion
module Isℤ↪ℝ = Number.Inclusions.IsROrderedCommRingInclusion
module Isℤ↪ℂ = Number.Inclusions.IsRCommRingInclusion
module Isℚ↪ℝ = Number.Inclusions.IsROrderedFieldInclusion
module Isℚ↪ℂ = Number.Inclusions.IsRFieldInclusion
module Isℝ↪ℂ = Number.Inclusions.IsRFieldInclusion

module _ where
  open ℕ* using (ℕ)
  open ℤ* using (ℤ)
  open ℚ* using (ℚ)
  open ℝ* using (ℝ)
  open ℂ* using (ℂ)
  postulate
    ℕ↪ℤ : ℕ → ℤ
    ℕ↪ℚ : ℕ → ℚ
    ℕ↪ℂ : ℕ → ℂ
    ℕ↪ℝ : ℕ → ℝ
    ℤ↪ℚ : ℤ → ℚ
    ℤ↪ℝ : ℤ → ℝ
    ℤ↪ℂ : ℤ → ℂ
    ℚ↪ℝ : ℚ → ℝ
    ℚ↪ℂ : ℚ → ℂ
    ℝ↪ℂ : ℝ → ℂ

    ℕ↪ℤinc : Isℕ↪ℤ (record {ℕ*}) (record {ℤ*}) ℕ↪ℤ
    ℕ↪ℚinc : Isℕ↪ℚ (record {ℕ*}) (record {ℚ*}) ℕ↪ℚ
    ℕ↪ℂinc : Isℕ↪ℂ (record {ℕ*}) (record {ℂ*}) ℕ↪ℂ
    ℕ↪ℝinc : Isℕ↪ℝ (record {ℕ*}) (record {ℝ*}) ℕ↪ℝ
    ℤ↪ℚinc : Isℤ↪ℚ (record {ℤ*}) (record {ℚ*}) ℤ↪ℚ
    ℤ↪ℝinc : Isℤ↪ℝ (record {ℤ*}) (record {ℝ*}) ℤ↪ℝ
    ℤ↪ℂinc : Isℤ↪ℂ (record {ℤ*}) (record {ℂ*}) ℤ↪ℂ
    ℚ↪ℝinc : Isℚ↪ℝ (record {ℚ*}) (record {ℝ*}) ℚ↪ℝ
    ℚ↪ℂinc : Isℚ↪ℂ (record {ℚ*}) (record {ℂ*}) ℚ↪ℂ
    ℝ↪ℂinc : Isℝ↪ℂ (record {ℝ*}) (record {ℂ*}) ℝ↪ℂ

    {-
    ℕ↪ℤinc : Isℕ↪ℤ                    ℕ.bundle    (record { ℤ.Bundle ℤ.bundle }) ℕ↪ℤ
    ℕ↪ℚinc : Isℕ↪ℚ                    ℕ.bundle    (record { ℚ.Bundle ℚ.bundle }) ℕ↪ℚ
    ℕ↪ℂinc : Isℕ↪ℂ                    ℕ.bundle                       ℂ.bundle    ℕ↪ℂ
    ℕ↪ℝinc : Isℕ↪ℝ                    ℕ.bundle    (record { ℝ.Bundle ℝ.bundle }) ℕ↪ℝ
    ℤ↪ℚinc : Isℤ↪ℚ                    ℤ.bundle    (record { ℚ.Bundle ℚ.bundle }) ℤ↪ℚ
    ℤ↪ℝinc : Isℤ↪ℝ                    ℤ.bundle    (record { ℝ.Bundle ℝ.bundle }) ℤ↪ℝ
    ℤ↪ℂinc : Isℤ↪ℂ                    ℤ.bundle                       ℂ.bundle    ℤ↪ℂ
    ℚ↪ℝinc : Isℚ↪ℝ                    ℚ.bundle    (record { ℝ.Bundle ℝ.bundle }) ℚ↪ℝ
    ℚ↪ℂinc : Isℚ↪ℂ (record { ℚ.Bundle ℚ.bundle }) (record { ℂ.Bundle ℂ.bundle }) ℚ↪ℂ
    ℝ↪ℂinc : Isℝ↪ℂ (record { ℝ.Bundle ℝ.bundle }) (record { ℂ.Bundle ℂ.bundle }) ℝ↪ℂ
    -}


open import Agda.Primitive

postulate
  F : (a : Level) → Set a → Set a
  P : (a : Level) (A : Set a) → F a A → Set a
  p : (a : Level) (A : Set a) (x : F a A) → P a A x
  Q : (a : Level) (A : Set a) → A → Set a

variable
  a : Level
  A : Set a

postulate
  q : (x : F _ A) → Q a _ (p a A x)

q' : {a : Level} {A : Set a} (x : F a A) → Q a (P a A x) (p a A x)
q' {a} {A} = q {a} {A}


{-# OPTIONS --without-K #-}
module FT-Nat where

open import Data.Empty
open import Data.Unit
open import Data.Nat renaming (_⊔_ to _⊔ℕ_)
open import Data.Sum renaming (map to _⊎→_)
open import Data.Product
import Data.Fin as F
open import Data.Vec
import Data.List as L
open import Function renaming (_∘_ to _○_)

open import FT
open import SimpleHoTT using (refl; pathInd; _≡_; ap)
open import Equivalences
open import TypeEquivalences using (path⊎)
open import Path2Equiv
open import LeftCancellation

------------------------------------------------------------------
-- Finite Types and the natural numbers are intimately related.
--
-- Basically, this is because both of them are models of
-- unital semirings, and the natural numbers are the initial object
-- in the category of unital semirings.  In this case, things are
-- deeper still, and so we record many of these facts here.
--
-- And, of course, as Pi records the proof-theory of semirings in
-- a fairly explicit manner, we can use all this to link up
-- normalization of natural-numbers expressions and Pi-based paths.

toℕ : FT → ℕ
toℕ ZERO = zero
toℕ ONE = suc zero
toℕ (PLUS b₀ b₁) = (toℕ b₀) + (toℕ b₁)
toℕ (TIMES b₀ b₁) = (toℕ b₀) * (toℕ b₁)

fromℕ : ℕ → FT
fromℕ zero = ZERO
fromℕ (suc n) = PLUS ONE (fromℕ n)

toℕ∘fromℕ : toℕ ○ fromℕ ∼ id
toℕ∘fromℕ zero = refl zero
toℕ∘fromℕ (suc n) =
  pathInd
    (λ {x} {y} _ → suc x ≡ suc y)
    (λ m → refl (suc m))
    (toℕ∘fromℕ n)

assocr : {m : ℕ} (n : ℕ) → (PLUS (fromℕ n) (fromℕ m)) ⇛ fromℕ (n + m)
assocr zero = unite₊⇛
assocr (suc n) = assocr₊⇛ ◎ (id⇛ ⊕ (assocr n))

distr : (n₀ : ℕ) {n₁ : ℕ} → TIMES (fromℕ n₀) (fromℕ n₁) ⇛ fromℕ (n₀ * n₁)
distr zero = distz⇛
distr (suc n) {m} = dist⇛ ◎ (unite⋆⇛ ⊕ distr n) ◎ assocr m

-- normalize a finite type to (1 + (1 + (1 + ... + (1 + 0) ... )))
-- a bunch of ones ending with zero with left biased + in between

normalize : FT → FT
normalize = fromℕ ○ toℕ

normal : (b : FT) → b ⇛ normalize b
normal ZERO = id⇛
normal ONE = uniti₊⇛ ◎ swap₊⇛
normal (PLUS b₀ b₁) = (normal b₀ ⊕ normal b₁) ◎ assocr (toℕ b₀)
normal (TIMES b₀ b₁) = (normal b₀ ⊗ normal b₁) ◎ distr (toℕ b₀)

normalizeC : {B : FT} → ⟦ normalize B ⟧ ≃ ⟦ B ⟧
normalizeC {B} = path2equiv (sym⇛ (normal B))

mapNorm :  {B₁ B₂ : FT} → (⟦ B₁ ⟧ ≃ ⟦ B₂ ⟧) → ⟦ normalize B₁ ⟧ ≃ ⟦ normalize B₂ ⟧
mapNorm {B₁} {B₂} eq = trans≃ (trans≃ (normalizeC {B₁}) eq) (sym≃ (normalizeC {B₂}))

⟦_⟧ℕ : ℕ → Set
⟦ zero ⟧ℕ = ⊥
⟦ suc n ⟧ℕ = ⊤ ⊎ ⟦ n ⟧ℕ

ℕrespects⟦⟧ : {n : ℕ} → ⟦ fromℕ n ⟧ ≃ ⟦ n ⟧ℕ
ℕrespects⟦⟧ {zero} = id≃
ℕrespects⟦⟧ {suc n} = path⊎ id≃ (ℕrespects⟦⟧ {n})

------------------------------------------------------------------------------
-- Understanding the syntactic structure of pi combinators with respect
-- to normalization

data FTNF : Set where
  Z : FTNF
  S : FTNF → FTNF

fromℕNF : ℕ → FTNF
fromℕNF zero = Z
fromℕNF (suc n) = S (fromℕNF n)

normalizeNF : FT → FTNF
normalizeNF = fromℕNF ○ toℕ

data FTN : FT → Set where
  normalized : (b : FT) → FTN (normalize b)

-- in both the following two functions we are missing fundamental information
-- about how a type relates to its normal form...

{--
normalize⇛ : {b₁ b₂ : FT} → (b₁ ⇛ b₂) → (normalize b₁ ⇛ normalize b₂)
normalize⇛ unite₊⇛ = id⇛
normalize⇛ uniti₊⇛ = id⇛
normalize⇛ swap₊⇛ = {!!}
normalize⇛ assocl₊⇛ = {!!}
normalize⇛ assocr₊⇛ = {!!}
normalize⇛ unite⋆⇛ = {!!}
normalize⇛ uniti⋆⇛ = {!!}
normalize⇛ swap⋆⇛ = {!!}
normalize⇛ assocl⋆⇛ = {!!}
normalize⇛ assocr⋆⇛ = {!!}
normalize⇛ distz⇛ = id⇛
normalize⇛ factorz⇛ = id⇛
normalize⇛ dist⇛ = {!!}
normalize⇛ factor⇛ = {!!}
normalize⇛ id⇛ = id⇛
normalize⇛ (sym⇛ c) = sym⇛ (normalize⇛ c)
normalize⇛ (c₁ ◎ c₂) = normalize⇛ c₁ ◎ normalize⇛ c₂
normalize⇛ (c ⊕ c₁) = {!!}
normalize⇛ (c ⊗ c₁) = {!!}
--}

{--
swapii+1 : {b : FT} → (i : ℕ) → (b ⇛ b)
swapii+1 {PLUS ONE (PLUS ONE b)} 0 =
  (assocl₊⇛ {ONE} {ONE} {b}) ◎ (swap₊⇛ ⊕ id⇛) ◎ (assocr₊⇛ {ONE} {ONE} {b})
swapii+1 {PLUS ONE b} (suc n) = id⇛ ⊕ swapii+1 n
swapii+1 _ = {!!}
--}
------------------------------------------------------------------------------

-- needs LeftCancellation

liftℕ : (n₁ n₂ : ℕ) → ⟦ n₁ ⟧ℕ ≃ ⟦ n₂ ⟧ℕ → (fromℕ n₁) ≡ (fromℕ n₂)
liftℕ zero zero eq = refl ZERO
liftℕ zero (suc n₂) (_ , mkisequiv g α h β) with h (inj₁ tt)
liftℕ zero (suc n₂) (_ , mkisequiv g α h β) | ()
liftℕ (suc n₁) zero (f , _) with f (inj₁ tt)
liftℕ (suc n₁) zero (f , _) | ()
liftℕ (suc n₁) (suc n₂) eq = ap (λ x → PLUS ONE x) (liftℕ n₁ n₂ (left-cancel-⊤ eq))

liftNormal : {B₁ B₂ : FT} →  ⟦ normalize B₁ ⟧ ≃ ⟦ normalize B₂ ⟧ → (normalize B₁) ≡ (normalize B₂)
liftNormal {B₁} {B₂} eq =
  liftℕ (toℕ B₁) (toℕ B₂)
    (⟦ toℕ B₁ ⟧ℕ ≃⟨ sym≃ (ℕrespects⟦⟧ {toℕ B₁}) ⟩ ⟦ normalize B₁ ⟧ ≃⟨ eq ⟩ ⟦ normalize B₂ ⟧ ≃⟨ ℕrespects⟦⟧ {toℕ B₂} ⟩ id≃)

sameNorm : {B₁ B₂ : FT} → (⟦ B₁ ⟧ ≃ ⟦ B₂ ⟧) → (normalize B₁) ≡ (normalize B₂)
sameNorm {B₁} {B₂} eq = liftNormal {B₁} {B₂} (mapNorm eq)

bridge : {B₁ B₂ : FT} → (⟦ B₁ ⟧ ≃ ⟦ B₂ ⟧) → (normalize B₁) ⇛ (normalize B₂)
bridge eq =
  pathInd
    (λ {B₃} {B₄} _ → B₃ ⇛ B₄)
    (λ _ → id⇛)
    (sameNorm eq)

{--

Not used

exf : ⊤ ⊎ ℕ → ℕ
exf (inj₁ tt) = 0
exf (inj₂ n) = suc n

exg : ℕ → ⊤ ⊎ ℕ
exg 0 = inj₁ tt
exg (suc n) = inj₂ n

exα : exf ○ exg ∼ id
exα 0 = refl 0
exα (suc n) = refl (suc n)

exβ : exg ○ exf ∼ id
exβ (inj₁ tt) = refl (inj₁ tt)
exβ (inj₂ n) = refl (inj₂ n)

ex : (⊤ ⊎ ℕ) ≃ ℕ
ex = (exf , equiv₁ (mkqinv exg exα exβ))

exf2 : (⊤ ⊎ (⊤ ⊎ ℕ)) → (⊤ ⊎ ℕ)
exf2 (inj₁ tt) = inj₂ 0
exf2 (inj₂ (inj₁ tt)) = inj₂ 1
exf2 (inj₂ (inj₂ 0)) = inj₁ tt
exf2 (inj₂ (inj₂ (suc n))) = inj₂ (suc (suc n))

exg2 : (⊤ ⊎ ℕ) → (⊤ ⊎ (⊤ ⊎ ℕ))
exg2 (inj₁ tt) = inj₂ (inj₂ 0)
exg2 (inj₂ 0) = inj₁ tt
exg2 (inj₂ 1) = inj₂ (inj₁ tt)
exg2 (inj₂ (suc (suc n))) = inj₂ (inj₂ (suc n))

exα2 : exf2 ○ exg2 ∼ id
exα2 (inj₁ tt) = refl (inj₁ tt)
exα2 (inj₂ 0) = refl (inj₂ 0)
exα2 (inj₂ 1) = refl (inj₂ 1)
exα2 (inj₂ (suc (suc n))) = refl (inj₂ (suc (suc n)))

exβ2 : exg2 ○ exf2 ∼ id
exβ2 (inj₁ tt) = refl (inj₁ tt)
exβ2 (inj₂ (inj₁ tt)) = refl (inj₂ (inj₁ tt))
exβ2 (inj₂ (inj₂ 0)) = refl (inj₂ (inj₂ 0))
exβ2 (inj₂ (inj₂ (suc n))) = refl (inj₂ (inj₂ (suc n)))

ex2 : (⊤ ⊎ (⊤ ⊎ ℕ)) ≃ (⊤ ⊎ ℕ)
ex2 = (exf2 , equiv₁ (mkqinv exg2 exα2 exβ2))

--}


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

module Data.Vec.Inductive where

open import Prelude
open import Data.List using (List; _∷_; []; length)

private
  variable
    n m : ℕ

infixr 5 _∷_
data Vec (A : Type a) : ℕ → Type a where
  [] : Vec A 0
  _∷_ : A → Vec A n → Vec A (suc n)

foldr : ∀ {p} (P : ℕ → Type p) →
          (∀ {n} → A → P n → P (suc n)) →
          P zero →
          Vec A n → P n
foldr P f b [] = b
foldr P f b (x ∷ xs) = f x (foldr P f b xs)

foldl : ∀ {p} (P : ℕ → Type p) →
          (∀ {n} → A → P n → P (suc n)) →
          P zero →
          Vec A n → P n
foldl P f b [] = b
foldl P f b (x ∷ xs) = foldl (P ∘ suc) f (f x b) xs

foldr′ : (A → B → B) → B → Vec A n → B
foldr′ f = foldr (const _) (λ x xs → f x xs)

foldl′ : (A → B → B) → B → Vec A n → B
foldl′ f = foldl (const _) (λ x xs → f x xs)

vecFromList : (xs : List A) → Vec A (length xs)
vecFromList [] = []
vecFromList (x ∷ xs) = x ∷ vecFromList xs

vecToList : Vec A n → List A
vecToList [] = []
vecToList (x ∷ xs) = x ∷ vecToList xs


------------------------------------------------------------------------------
-- Filter
------------------------------------------------------------------------------

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

module FOT.FOTC.Data.List.Filter where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.PropertiesI
open import FOTC.Base.List
open import FOTC.Data.Bool.Type
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.PropertiesI
open import FOTC.Data.Nat.Type
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesI

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

postulate
  filter    : D → D → D
  filter-[] : ∀ p → filter p [] ≡ []
  filter-∷  : ∀ p x xs → filter p (x ∷ xs) ≡
              (if p · x then x ∷ filter p xs else filter p xs)

postulate filter-List : ∀ p {xs} → List xs → List (filter p xs)

from-Bool : ∀ {b} → Bool b → b ≡ true ∨ b ≡ false
from-Bool btrue  = inj₁ refl
from-Bool bfalse = inj₂ refl

-- lenght-filter : ∀ p {xs} → (∀ x → Bool (p · x)) → List xs →
--                 length (filter p xs) ≤ length xs
-- lenght-filter p hp lnil =
--   le (length (filter p [])) (length [])
--     ≡⟨ ltLeftCong (lengthCong (filter-[] p)) ⟩
--   le (length []) (length [])
--     ≡⟨ ltCong length-[] (succCong length-[]) ⟩
--   le zero zero
--     ≡⟨ x≤x nzero ⟩
--   true ∎

-- lenght-filter p hp (lcons x {xs} Lxs) = case prf₁ prf₂ (from-Bool (hp x))
--   where
--   prf₁ : p · x ≡ true →
--          le (length (filter p (x ∷ xs))) (length (x ∷ xs)) ≡ true
--   prf₁ h = {!!}

--   prf₂ : p · x ≡ false →
--          le (length (filter p (x ∷ xs))) (length (x ∷ xs)) ≡ true
--   prf₂ h =
--     le (length (filter p (x ∷ xs))) (length (x ∷ xs))
--       ≡⟨ ltLeftCong (lengthCong {!!}) ⟩
--     le (length (filter p xs)) (length (x ∷ xs))
--       ≡⟨ ≤-trans (lengthList-N (filter-List p Lxs))
--                  (lengthList-N Lxs)
--                  (lengthList-N (lcons x Lxs))
--                  (lenght-filter p hp Lxs)
--                  (x<y→x≤y (lengthList-N Lxs) (lengthList-N (lcons x Lxs))
--                           (lg-x<lg-x∷xs x Lxs)) ⟩
--     true ∎


-- A minor variant of code reported by Andreas Abel. The code below
-- should be rejected.

open import Agda.Builtin.Size

data ⊥ : Set where

data SizeLt (i : Size) : Set where
  size : (j : Size< i) → SizeLt i

<∞ : SizeLt ∞
<∞ = size ∞

data D (i : Size) : SizeLt i → Set where
  c : ∀ {i' : Size< i} → ((j : SizeLt i') → D i' j) → D i (size i')

f : D ∞ <∞ → ⊥
f (c h) = f (h <∞)

d : ∀ i s → D i s
d i (size j) = c (d j)

loop : ⊥
loop = f (d ∞ <∞)


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

open import Functions
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import LogicalFormulae
open import Setoids.Subset
open import Setoids.Setoids
open import Setoids.Orders
open import Fields.Fields
open import Rings.Orders.Total.Definition
open import Rings.Orders.Total.Lemmas
open import Rings.Orders.Partial.Definition
open import Rings.Definition
open import Fields.Orders.LeastUpperBounds.Definition
open import Numbers.ClassicalReals.RealField
open import Groups.Definition
open import Groups.Homomorphisms.Definition
open import Rings.Homomorphisms.Definition
open import Sets.EquivalenceRelations
open import Semirings.Definition

open import Numbers.Naturals.Semiring
open import Numbers.Integers.Definition
open import Numbers.Integers.RingStructure.Ring

module Numbers.ClassicalReals.RealField.AllIsomorphic (ℝ : RealField) where

open RealField ℝ
open Ring R
open Group additiveGroup
open Setoid S
open Equivalence eq
open import Rings.Lemmas R
open import Groups.Lemmas additiveGroup

fromN : ℕ → A
fromN zero = 0R
fromN (succ n) = 1R + fromN n

fromNPreserves+ : (a b : ℕ) → fromN (a +N b) ∼ (fromN a) + (fromN b)
fromNPreserves+ zero b = symmetric identLeft
fromNPreserves+ (succ a) b = transitive (+WellDefined reflexive (fromNPreserves+ a b)) +Associative

fromNPreserves* : (a b : ℕ) → fromN (a *N b) ∼ (fromN a) * (fromN b)
fromNPreserves* zero b = symmetric (transitive *Commutative timesZero)
fromNPreserves* (succ a) b = transitive (transitive (fromNPreserves+ b (a *N b)) (+WellDefined (symmetric identIsIdent) (fromNPreserves* a b))) (symmetric *DistributesOver+')

fromNWellDefined : {a b : ℕ} → a ≡ b → fromN a ∼ fromN b
fromNWellDefined refl = reflexive

fromZ : ℤ → A
fromZ (nonneg x) = fromN x
fromZ (negSucc x) = inverse (fromN (succ x))

private
  lemma : (a : ℕ) → (b : ℕ) → (1R + fromN (succ a *N b +N a)) ∼ ((1R + fromN a) * (1R + fromN b))
  lemma a b = transitive (transitive (transitive (+WellDefined reflexive (transitive (fromNPreserves+ ((succ a) *N b) a) (transitive groupIsAbelian (+WellDefined reflexive (transitive (fromNPreserves+ b (a *N b)) (+WellDefined (symmetric identIsIdent) (fromNPreserves* a b))))))) +Associative) (+WellDefined (transitive (symmetric identIsIdent) *Commutative) (symmetric *DistributesOver+'))) (symmetric *DistributesOver+)

fromZPreserves+ : (a b : ℤ) → fromZ (a +Z b) ∼ (fromZ a) + (fromZ b)
fromZPreserves+ (nonneg zero) (nonneg y) = symmetric identLeft
fromZPreserves+ (nonneg (succ x)) (nonneg y) = transitive (+WellDefined reflexive (fromNPreserves+ x y)) +Associative
fromZPreserves+ (nonneg zero) (negSucc y) = symmetric identLeft
fromZPreserves+ (nonneg (succ x)) (negSucc zero) = transitive (transitive (transitive (transitive (symmetric identLeft) (+WellDefined (symmetric invLeft) reflexive)) (symmetric +Associative)) (+WellDefined (inverseWellDefined (symmetric identRight)) reflexive)) (groupIsAbelian)
fromZPreserves+ (nonneg (succ x)) (negSucc (succ y)) = transitive (fromZPreserves+ (nonneg x) (negSucc y)) (transitive (transitive (transitive (transitive (transitive (transitive (transitive (symmetric identLeft) +Associative) groupIsAbelian) (+WellDefined reflexive (+WellDefined (symmetric invLeft) reflexive))) (+WellDefined reflexive (symmetric +Associative))) +Associative) groupIsAbelian) (+WellDefined reflexive (symmetric invContravariant)))
fromZPreserves+ (negSucc zero) (nonneg zero) = symmetric identRight
fromZPreserves+ (negSucc zero) (nonneg (succ y)) = transitive (transitive (symmetric identLeft) (+WellDefined (symmetric (transitive (+WellDefined reflexive (symmetric identRight)) invLeft)) reflexive)) (symmetric +Associative)
fromZPreserves+ (negSucc (succ x)) (nonneg zero) = symmetric identRight
fromZPreserves+ (negSucc (succ x)) (nonneg (succ y)) = transitive (fromZPreserves+ (negSucc x) (nonneg y)) (transitive (transitive (+WellDefined reflexive (transitive (symmetric identLeft) (transitive (+WellDefined (symmetric invLeft) reflexive) (symmetric +Associative)))) +Associative) (+WellDefined (symmetric invContravariant) reflexive))
fromZPreserves+ (negSucc x) (negSucc y) = transitive (transitive invContravariant (transitive (+WellDefined invContravariant reflexive) (transitive (+WellDefined (transitive (transitive (+WellDefined (transitive (inverseWellDefined (fromNPreserves+ x y)) invContravariant) reflexive) (symmetric +Associative)) groupIsAbelian) reflexive) (symmetric +Associative)))) (+WellDefined (symmetric invContravariant) (symmetric invContravariant))

fromZPreserves* : (a b : ℤ) → fromZ (a *Z b) ∼ (fromZ a) * (fromZ b)
fromZPreserves* (nonneg a) (nonneg b) = fromNPreserves* a b
fromZPreserves* (nonneg zero) (negSucc b) = symmetric (transitive *Commutative timesZero)
fromZPreserves* (nonneg (succ a)) (negSucc b) = transitive (inverseWellDefined (lemma a b)) (symmetric ringMinusExtracts)
fromZPreserves* (negSucc a) (nonneg zero) = symmetric timesZero
fromZPreserves* (negSucc a) (nonneg (succ b)) = transitive (inverseWellDefined (transitive (+WellDefined reflexive (fromNWellDefined {(a +N b *N a) +N b} {(b +N a *N b) +N a} (transitivity (Semiring.commutative ℕSemiring _ b) (transitivity (applyEquality (b +N_) (transitivity (Semiring.commutative ℕSemiring a _) (applyEquality (_+N a) (multiplicationNIsCommutative b a)))) (Semiring.+Associative ℕSemiring b _ a))))) (lemma a b))) (symmetric ringMinusExtracts')
fromZPreserves* (negSucc a) (negSucc b) = transitive (transitive (+WellDefined reflexive (fromNWellDefined {b +N a *N succ b} {(b +N a *N b) +N a} (transitivity (applyEquality (b +N_) (transitivity (multiplicationNIsCommutative a (succ b)) (transitivity (Semiring.commutative ℕSemiring a _) (applyEquality (_+N a) (multiplicationNIsCommutative b a))))) (Semiring.+Associative ℕSemiring b _ a)))) (lemma a b)) (symmetric twoNegativesTimes)

ringHom : RingHom ℤRing R fromZ
RingHom.preserves1 ringHom = identRight
RingHom.ringHom ringHom {r} {s} = fromZPreserves* r s
GroupHom.groupHom (RingHom.groupHom ringHom) {r} {s} = fromZPreserves+ r s
GroupHom.wellDefined (RingHom.groupHom ringHom) refl = reflexive


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

open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Numbers.Naturals.Naturals
open import Numbers.Naturals.Multiplication
open import Numbers.Integers.Order
open import Numbers.Integers.RingStructure.Ring
open import Numbers.Integers.RingStructure.IntegralDomain
open import Semirings.Definition
open import Rings.EuclideanDomains.Definition
open import Orders.Total.Definition
open import Numbers.Naturals.EuclideanAlgorithm

module Numbers.Integers.RingStructure.EuclideanDomain where

private
  norm : ℤ → ℕ
  norm (nonneg x) = x
  norm (negSucc x) = succ x

  norm' : {a : ℤ} → (a!=0 : (a ≡ nonneg 0) → False) → ℕ
  norm' {a} _ = norm a

  multiplyIncreasesSuccLemma : (a b : ℕ) → succ (succ a) <N (succ (succ a)) *N (succ (succ b))
  multiplyIncreasesSuccLemma a b with multiplyIncreases (succ (succ b)) (succ (succ a)) (le b (applyEquality succ (Semiring.commutative ℕSemiring b 1))) (le (succ a) (applyEquality (λ i → succ (succ i)) (Semiring.sumZeroRight ℕSemiring a)))
  ... | bl rewrite multiplicationNIsCommutative (succ (succ b)) (succ (succ a)) = bl

  multiplyIncreasesSucc : (a b : ℕ) → succ a ≤N (succ a) *N (succ b)
  multiplyIncreasesSucc zero zero = inr refl
  multiplyIncreasesSucc zero (succ b) = inl (le (b +N 0) (Semiring.commutative ℕSemiring (succ (b +N 0)) 1))
  multiplyIncreasesSucc (succ a) zero = inr (applyEquality (λ i → succ (succ i)) (equalityCommutative (productWithOneRight a)))
  multiplyIncreasesSucc (succ a) (succ b) = inl (multiplyIncreasesSuccLemma a b)

  multiplyIncreasesSuccLemma' : (a b : ℕ) → succ (succ a) <N succ ((succ (succ a)) *N succ b) +N succ a
  multiplyIncreasesSuccLemma' a b = succPreservesInequality (le (b +N succ (b +N a *N succ b)) refl)

  multiplyIncreasesSucc' : (a b : ℕ) → succ a ≤N succ ((b +N a *N b) +N a)
  multiplyIncreasesSucc' zero zero = inr refl
  multiplyIncreasesSucc' zero (succ b) = inl (le b (applyEquality succ (transitivity (Semiring.commutative ℕSemiring b 1) (transitivity (equalityCommutative (Semiring.sumZeroRight ℕSemiring (succ b))) (equalityCommutative (Semiring.sumZeroRight ℕSemiring _))))))
  multiplyIncreasesSucc' (succ a) zero rewrite multiplicationNIsCommutative a zero = inr refl
  multiplyIncreasesSucc' (succ a) (succ b) = inl (multiplyIncreasesSuccLemma' a b)

  normSize : {a b : ℤ} → (a!=0 : (a ≡ nonneg 0) → False) → (b!=0 : (b ≡ nonneg 0) → False) → (c : ℤ) → b ≡ (a *Z c) → (norm a) ≤N (norm b)
  normSize {nonneg a} {nonneg b} a!=0 b!=0 (nonneg zero) b=ac rewrite nonnegInjective b=ac | multiplicationNIsCommutative a 0 = exFalso (b!=0 refl)
  normSize {nonneg a} {nonneg b} a!=0 b!=0 (nonneg (succ 0)) b=ac rewrite nonnegInjective b=ac | multiplicationNIsCommutative a 1 = inr (equalityCommutative (Semiring.sumZeroRight ℕSemiring a))
  normSize {nonneg 0} {nonneg b} a!=0 b!=0 (nonneg (succ (succ c))) b=ac = exFalso (a!=0 refl)
  normSize {nonneg (succ a)} {nonneg (succ b)} a!=0 b!=0 (nonneg (succ (succ c))) b=ac rewrite nonnegInjective b=ac = multiplyIncreasesSucc a (succ c)
  normSize {nonneg 0} {nonneg b} a!=0 b!=0 (negSucc c) bad = exFalso (a!=0 refl)
  normSize {nonneg (succ a)} {nonneg b} a!=0 b!=0 (negSucc c) ()
  normSize {nonneg a} {negSucc b} a!=0 b!=0 (nonneg c) ()
  normSize {nonneg (succ a)} {negSucc b} a!=0 b!=0 (negSucc c) pr rewrite negSuccInjective pr = multiplyIncreasesSucc' a c
  normSize {negSucc a} {nonneg zero} _ b!=0 c pr = exFalso (b!=0 refl)
  normSize {negSucc a} {nonneg (succ b)} _ _ (nonneg zero) ()
  normSize {negSucc a} {nonneg (succ b)} _ _ (nonneg (succ x)) ()
  normSize {negSucc a} {nonneg (succ b)} _ _ (negSucc c) pr rewrite nonnegInjective pr = multiplyIncreasesSucc a c
  normSize {negSucc a} {negSucc b} _ _ (nonneg (succ c)) pr rewrite negSuccInjective pr | multiplicationNIsCommutative c a | Semiring.commutative ℕSemiring (a +N a *N c) c | Semiring.+Associative ℕSemiring c a (a *N c) | Semiring.commutative ℕSemiring c a | equalityCommutative (Semiring.+Associative ℕSemiring a c (a *N c)) | Semiring.commutative ℕSemiring a (c +N a *N c) = multiplyIncreasesSucc' a c

  divAlg : {a b : ℤ} → (a!=0 : (a ≡ nonneg 0) → False) → (b!=0 : (b ≡ nonneg 0) → False) → DivisionAlgorithmResult ℤRing norm' a!=0 b!=0
  divAlg {nonneg zero} {b} a!=0 b!=0 = exFalso (a!=0 refl)
  divAlg {nonneg (succ a)} {nonneg zero} a!=0 b!=0 = exFalso (b!=0 refl)
  divAlg {nonneg (succ a)} {nonneg (succ b)} a!=0 b!=0 with divisionAlg (succ b) (succ a)
  divAlg {nonneg (succ a)} {nonneg (succ b)} a!=0 b!=0 | record { quot = quot ; rem = rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } = record { quotient = nonneg quot ; rem = nonneg rem ; remSmall = u ; divAlg = transitivity (applyEquality nonneg (equalityCommutative pr)) t }
    where
      t : nonneg ((quot +N b *N quot) +N rem) ≡ nonneg (quot *N succ b) +Z nonneg rem
      t rewrite multiplicationNIsCommutative (succ b) quot = +Zinherits (quot *N succ b) rem
      u : (nonneg rem ≡ nonneg 0) || Sg ((nonneg rem ≡ nonneg 0) → False) (λ pr → rem <N succ b)
      u with TotalOrder.totality ℤOrder (nonneg 0) (nonneg rem)
      u | inl (inl 0<rem) = inr ((λ p → TotalOrder.irreflexive ℤOrder (TotalOrder.<WellDefined ℤOrder refl p 0<rem)) , remIsSmall)
      u | inr 0=rem rewrite 0=rem = inl refl
  divAlg {nonneg (succ a)} {negSucc b} _ _ with divisionAlg (succ b) (succ a)
  divAlg {nonneg (succ a)} {negSucc b} _ _ | record { quot = zero ; rem = rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl quotSmall } rewrite multiplicationNIsCommutative b 0 = record { quotient = nonneg 0 ; rem = nonneg rem ; remSmall = inr (p , remIsSmall) ; divAlg = applyEquality nonneg (equalityCommutative pr) }
    where
      p : (nonneg rem ≡ nonneg 0) → False
      p pr2 rewrite pr = naughtE (equalityCommutative (nonnegInjective pr2))
  divAlg {nonneg (succ a)} {negSucc b} _ _ | record { quot = succ quot ; rem = zero ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl quotSmall } = record { quotient = negSucc quot ; rem = nonneg zero ; remSmall = inl refl ; divAlg = applyEquality nonneg (transitivity (equalityCommutative pr) (applyEquality (λ i → i +N 0) (multiplicationNIsCommutative (succ b) (succ quot)))) }
  divAlg {nonneg (succ a)} {negSucc b} _ _ | record { quot = succ quot ; rem = succ rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl quotSmall } = record { quotient = negSucc quot ; rem = nonneg (succ rem) ; remSmall = inr ((λ ()) , remIsSmall) ; divAlg = applyEquality nonneg (transitivity (equalityCommutative pr) (applyEquality (λ i → i +N succ rem) (multiplicationNIsCommutative (succ b) (succ quot)))) }
  divAlg {negSucc a} {nonneg zero} _ b!=0 = exFalso (b!=0 refl)
  divAlg {negSucc a} {nonneg (succ b)} _ _ with divisionAlg (succ b) (succ a)
  divAlg {negSucc a} {nonneg (succ b)} _ _ | record { quot = zero ; rem = zero ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } = exFalso (naughtE (transitivity (equalityCommutative (transitivity (Semiring.sumZeroRight ℕSemiring _) (multiplicationNIsCommutative b 0))) pr))
  divAlg {negSucc a} {nonneg (succ b)} _ _ | record { quot = succ quot ; rem = zero ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } = record { quotient = negSucc quot ; rem = nonneg 0 ; remSmall = inl refl ; divAlg = applyEquality negSucc (succInjective (transitivity (equalityCommutative pr) t)) }
    where
      t : succ (quot +N b *N succ quot) +N 0 ≡ succ ((quot +N b *N quot) +N b)
      t rewrite Semiring.sumZeroRight ℕSemiring (succ (quot +N b *N succ quot)) | Semiring.commutative ℕSemiring (quot +N b *N quot) b | Semiring.+Associative ℕSemiring b quot (b *N quot) | Semiring.commutative ℕSemiring b quot | equalityCommutative (Semiring.+Associative ℕSemiring quot b (b *N quot)) | multiplicationNIsCommutative b (succ quot) | multiplicationNIsCommutative quot b = refl
  divAlg {negSucc a} {nonneg (succ b)} _ _ | record { quot = zero ; rem = succ rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } rewrite multiplicationNIsCommutative b 0 | equalityCommutative (succInjective pr) = record { quotient = nonneg zero ; rem = negSucc rem ; remSmall = inr (((λ ()) , remIsSmall)) ; divAlg = refl }
  divAlg {negSucc a} {nonneg (succ b)} _ _ | record { quot = succ quot ; rem = succ rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } = record { quotient = negSucc quot ; rem = negSucc rem ; remSmall = inr ((λ ()) , remIsSmall) ; divAlg = applyEquality negSucc (succInjective (transitivity (equalityCommutative pr) t)) }
    where
      t : succ b *N succ quot +N succ rem ≡ succ (succ (succ b *N quot +N b) +N rem)
      t rewrite Semiring.commutative ℕSemiring ((quot +N b *N quot) +N b) rem | Semiring.commutative ℕSemiring (succ rem) ((quot +N b *N quot) +N b) | multiplicationNIsCommutative b (succ quot) | equalityCommutative (Semiring.+Associative ℕSemiring quot (b *N quot) b) | Semiring.commutative ℕSemiring b (quot *N b) | multiplicationNIsCommutative b quot = refl
  divAlg {negSucc a} {negSucc b} _ _ with divisionAlg (succ b) (succ a)
  divAlg {negSucc a} {negSucc b} _ _ | record { quot = zero ; rem = zero ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } = exFalso (naughtE (transitivity (equalityCommutative (transitivity (Semiring.sumZeroRight ℕSemiring (b *N 0)) (multiplicationNIsCommutative b 0))) pr))
  divAlg {negSucc a} {negSucc b} _ _ | record { quot = zero ; rem = succ rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } rewrite multiplicationNIsCommutative b 0 | equalityCommutative (succInjective pr) = record { quotient = nonneg 0 ; rem = negSucc rem ; remSmall = inr ((λ ()) , remIsSmall) ; divAlg = refl }
  divAlg {negSucc a} {negSucc b} _ _ | record { quot = succ quot ; rem = zero ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } rewrite Semiring.sumZeroRight ℕSemiring (quot +N b *N succ quot) | Semiring.commutative ℕSemiring b (succ quot) = record { quotient = nonneg (succ quot) ; rem = nonneg 0 ; remSmall = inl refl ; divAlg = applyEquality negSucc (succInjective (transitivity (equalityCommutative pr) t)) }
    where
      t : succ (quot +N b *N succ quot) ≡ succ ((b +N quot *N b) +N quot)
      t rewrite Semiring.commutative ℕSemiring quot (b *N succ quot) | multiplicationNIsCommutative b (succ quot) = refl
  divAlg {negSucc a} {negSucc b} _ _ | record { quot = succ quot ; rem = succ rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = quotSmall } rewrite multiplicationNIsCommutative b (succ quot) | Semiring.commutative ℕSemiring quot (b +N quot *N b) | Semiring.commutative ℕSemiring ((b +N quot *N b) +N quot) (succ rem) | Semiring.commutative ℕSemiring rem ((b +N quot *N b) +N quot) = record { quotient = nonneg (succ quot) ; rem = negSucc rem ; remSmall = inr (((λ ()) , remIsSmall)) ; divAlg = applyEquality negSucc (equalityCommutative (succInjective pr)) }

ℤEuclideanDomain : EuclideanDomain ℤRing
EuclideanDomain.isIntegralDomain ℤEuclideanDomain = ℤIntDom
EuclideanDomain.norm ℤEuclideanDomain = norm'
EuclideanDomain.normSize ℤEuclideanDomain = normSize
EuclideanDomain.divisionAlg ℤEuclideanDomain {a} {b} a!=0 b!=0 = divAlg {a} {b} a!=0 b!=0


import      Lvl
open import Type

module Type.Cardinality.Proofs {ℓₗ : Lvl.Level} where

open import Functional
import      Logic.Predicate
import      Logic.Predicate.Theorems
import      Relator.Equals
import      Relator.Equals.Proofs
import      Type.Cardinality
import      Type.Functions
import      Type.Functions.Proofs
import      Type.Functions.Inverse
import      Type.Functions.Inverse.Proofs

module _ {ℓₒ₁}{ℓₒ₂} {X : Type{ℓₒ₁}} {Y : Type{ℓₒ₂}} where
  open Logic.Predicate               {ℓₗ}
  open Logic.Predicate.Theorems      {ℓₗ}
  open Type.Cardinality              {ℓₗ}
  open Type.Functions                {ℓₗ}
  open Type.Functions.Inverse        {ℓₗ}
  open Type.Functions.Inverse.Proofs {ℓₗ}
  open Type.Functions.Proofs         {ℓₗ}

  instance
    [≍]-to-[≼] : ⦃ _ : X ≍ Y ⦄ → (X ≼ Y)
    [≍]-to-[≼] ⦃ proof ⦄ = [∃]-map (proof ↦ bijective-to-injective ⦃ proof ⦄) proof

  instance
    [≍]-to-[≽] : ⦃ _ : X ≍ Y ⦄ → (X ≼ Y)
    [≍]-to-[≽] ⦃ proof ⦄ = [∃]-map (proof ↦ bijective-to-injective ⦃ proof ⦄) proof

  [≍]-symmetry : (X ≍ Y) → (Y ≍ X)
  [≍]-symmetry ([∃]-intro f ⦃ proof ⦄) = [∃]-intro (inv f ⦃ proof ⦄) ⦃ inv-bijective {ℓₒ₁}{ℓₒ₂} {_}{_} {f} ⦃ proof ⦄ ⦄

  -- TODO: Is it possible to prove these?
  -- [≍]-antisymmetry : (X ≼ Y) → (X ≽ Y) → (X ≍ Y)
  -- [≍]-antisymmetry = [∃]-map ()

  -- [≼][≽]-swap : (X ≼ Y) → (Y ≽ X)
  -- [≽][≼]-swap : (X ≽ Y) → (Y ≼ X)

module _ {ℓₒ} {X : Type{ℓₒ}} where
  open Logic.Predicate          {ℓₗ Lvl.⊔ ℓₒ}
  open Logic.Predicate.Theorems {ℓₗ Lvl.⊔ ℓₒ}
  open Type.Cardinality         {ℓₗ Lvl.⊔ ℓₒ}
  open Type.Functions.Proofs     {ℓₗ Lvl.⊔ ℓₒ}

  instance
    [≍]-reflexivity : (X ≍ X)
    [≍]-reflexivity = [∃]-intro (id) ⦃ id-is-bijective ⦄

  instance
    [≼]-reflexivity : (X ≼ X)
    [≼]-reflexivity = [∃]-intro(id) ⦃ bijective-to-injective ⦃ id-is-bijective ⦄ ⦄

  instance
    [≽]-reflexivity : (X ≽ X)
    [≽]-reflexivity = [∃]-intro(id) ⦃ bijective-to-surjective ⦃ id-is-bijective ⦄ ⦄

module _ {ℓₒ} {X : Type{ℓₒ}} {Y : Type{ℓₒ}} where
  open Type.Cardinality {ℓₗ Lvl.⊔ ℓₒ}
  open Relator.Equals   {ℓₗ Lvl.⊔ ℓₒ}{Lvl.𝐒(ℓₒ)}

  [≡]-to-[≍] : (X ≡ Y) → (X ≍ Y)
  [≡]-to-[≍] [≡]-intro = [≍]-reflexivity


module Common.SumProperties where

open import Function as Fun
open import Data.Sum as Sum
open import Relation.Binary.PropositionalEquality as PE hiding ([_])
open ≡-Reasoning

copair-elimˡ : {A B C : Set} {f : A → C} {g : B → C} (x : A) →
               ([ f , g ]′ ) (inj₁ x) ≡ f x
copair-elimˡ x = refl

copair-elimʳ : {A B C : Set} {f : A → C} {g : B → C} (x : B) →
               ([ f , g ]′ ) (inj₂ x) ≡ g x
copair-elimʳ x = refl

copair-sum-map-merge : {A A' B B' C : Set}
                     {f₁ : A → A'} {f₂ : B → B'} {g₁ : A' → C} {g₂ : B' → C}
                     (x : A ⊎ B) →
                     ([ g₁ , g₂ ]′ ∘ Sum.map f₁ f₂) x ≡ [ g₁ ∘ f₁ , g₂ ∘ f₂ ]′ x
copair-sum-map-merge (inj₁ x) = refl
copair-sum-map-merge (inj₂ y) = refl

copair-cong : {A B C : Set} {f f' : A → C} {g : B → C}
              (p : (x : A) → f x ≡ f' x) →
              (x : A ⊎ B) →
              [ f , g ]′ x ≡ [ f' , g ]′ x
copair-cong {A} {B} {C} {f} {f'} {g} p (inj₁ x) =
  begin
    ([ f , g ]′ (inj₁ x))
  ≡⟨ refl ⟩
    f x
  ≡⟨ p x ⟩
    f' x
  ≡⟨ refl ⟩
    [ f' , g ]′ (inj₁ x)
  ∎
copair-cong p (inj₂ y) = refl

copair-distr : {A B C D : Set} {f : A → C} {g : B → C} {h : C → D}
              (x : A ⊎ B) →
              [ h ∘ f , h ∘ g ]′ x ≡ (h ∘ [ f , g ]′) x
copair-distr (inj₁ x) = refl
copair-distr (inj₂ y) = refl


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.Polynomials.Multivariate.Properties where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat renaming(_+_ to _+n_; _·_ to _·n_)
open import Cubical.Data.Vec
open import Cubical.Data.Vec.OperationsNat

open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing

open import Cubical.Algebra.Polynomials.Multivariate.Base

private variable
  ℓ ℓ' : Level

module Nth-Poly-structure (A' : CommRing ℓ) (n : ℕ) where

  private
    A = fst A'
    Ar = CommRing→Ring A'

  open CommRingStr (snd A')
  open RingTheory Ar

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

  poly-com-adv : (P Q R S : Poly A' n) →  ((P poly+ Q) poly+ (R poly+ S) ≡ (P poly+ R) poly+ (Q poly+ S))
  poly-com-adv P Q R S = ((P poly+ Q) poly+ (R poly+ S)  ≡⟨ poly+Assoc (P poly+ Q) R S ⟩
                         (((P poly+ Q) poly+ R) poly+ S) ≡⟨ cong (λ X → X poly+ S) (sym (poly+Assoc P Q R)) ⟩
                         ((P poly+ (Q poly+ R)) poly+ S) ≡⟨ cong (λ X → (P poly+ X) poly+ S) (poly+Comm Q R) ⟩
                         ((P poly+ (R poly+ Q)) poly+ S) ≡⟨ cong (λ X → X poly+ S) (poly+Assoc P R Q) ⟩
                         (((P poly+ R) poly+ Q) poly+ S) ≡⟨ sym (poly+Assoc (P poly+ R) Q S) ⟩
                         ((P poly+ R) poly+ (Q poly+ S)) ∎)

  poly+IdL : (P : Poly A' n) → 0P poly+ P ≡ P
  poly+IdL P = (poly+Comm 0P P) ∙ (poly+IdR P)

  polyInv : Poly A' n → Poly A' n
  polyInv = Poly-Rec-Set.f A' n (Poly A' n) trunc
            0P
            (λ v a → base v (- a))
            (λ PS RS → PS poly+ RS)
            poly+Assoc
            poly+IdR
            poly+Comm
            (λ v → base v (- 0r) ≡⟨ cong (base v) 0Selfinverse ⟩ base v 0r ≡⟨ base-0P v ⟩ 0P ∎)
            λ v a b → (base-poly+ v (- a) (- b)) ∙ (cong (base v) (-Dist a b))

  polyInvol : (P : Poly A' n) → polyInv (polyInv P) ≡ P
  polyInvol = Poly-Ind-Prop.f A' n (λ P → polyInv (polyInv P) ≡ P) (λ _ → trunc _ _)
              refl
              (λ v a → cong (base v) (-Idempotent a))
              λ {P Q} ind-P ind-Q → cong₂ _poly+_ ind-P ind-Q

  poly+InvR : (P : Poly A' n ) → P poly+ (polyInv P) ≡ 0P
  poly+InvR = Poly-Ind-Prop.f A' n (λ P → (P poly+ polyInv P) ≡ 0P) (λ _ → trunc _ _)
              (poly+IdR 0P)
              (λ v a → (base-poly+ v a (- a)) ∙ cong (base v) (+Rinv a) ∙ base-0P v)
              λ {P Q} ind-P ind-Q → ((P poly+ Q) poly+ ((polyInv P) poly+ (polyInv Q)))
                                              ≡⟨ poly-com-adv P Q (polyInv P) (polyInv Q) ⟩
                                     ((P poly+ polyInv P) poly+ (Q poly+ polyInv Q))
                                              ≡⟨ cong₂ _poly+_ ind-P ind-Q ⟩
                                     (0P poly+ 0P)
                                              ≡⟨ poly+IdR 0P ⟩
                                     0P ∎

  poly+InvL : (P  : Poly A' n) → (polyInv P) poly+ P ≡ 0P
  poly+InvL = Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
              (poly+IdR 0P)
              (λ v a → (base-poly+ v (- a) a) ∙ cong (base v) (snd (+Inv a)) ∙ base-0P v)
              λ {U V} ind-U ind-V → poly-com-adv (polyInv U) (polyInv V) U V
                                     ∙ cong₂ _poly+_ ind-U ind-V
                                     ∙ poly+IdR 0P

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

  _poly*_ : Poly A' n → Poly A' n → Poly A' n
  _poly*_ = -- Induction Left Argument
            Poly-Rec-Set.f A' n (Poly A' n → Poly A' n)
            (λ f g p q i j Q → trunc (f Q) (g Q) (λ X → p X Q) (λ X → q X Q) i j )
            (λ Q    → 0P)
            (λ v a  → -- Induction Right Argument
                       Poly-Rec-Set.f A' n (Poly A' n) trunc
                       0P
                       (λ v' a' → base (v +n-vec v') (a · a'))
                       _poly+_
                       poly+Assoc
                       poly+IdR
                       poly+Comm
                       (λ v' → (cong (base (v +n-vec v')) (0RightAnnihilates a)) ∙ (base-0P (v +n-vec v')))
                       λ v' b c → (base-poly+ (v +n-vec v') (a · b) (a · c))
                                   ∙ (cong (base (v +n-vec v')) (sym (·Rdist+ a b c))))
                       -- End Right induction
            (λ PS QS Q → (PS Q) poly+ (QS Q) )
            (λ PS QS RS i Q → poly+Assoc (PS Q) (QS Q) (RS Q) i)
            (λ PS i Q       → poly+IdR (PS Q) i)
            (λ PS QS i Q    → poly+Comm (PS Q) (QS Q) i)
            (λ v    → funExt (
                             -- Induction Right Argument
                             Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
                             refl
                             (λ v' a' → (cong (base (v +n-vec v')) (0LeftAnnihilates a')) ∙ (base-0P (v +n-vec v')))
                             λ {P Q} ind-P ind-Q → (cong₂ _poly+_ ind-P ind-Q) ∙ (poly+IdR 0P) ))
                             -- End Right Induction
            λ v a b → funExt (
                             -- Induction Right Argument
                             Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
                             (poly+IdR 0P)
                             (λ v' c → (base-poly+ (v +n-vec v') (a · c) (b · c))
                                        ∙ (cong (base (v +n-vec v')) (sym (·Ldist+ a b c))))
                             λ {P Q} ind-P ind-Q → (poly-com-adv _ _ _ _) ∙ (cong₂ _poly+_ ind-P ind-Q))
                             -- End Right Induction
            -- End Left Induction

  poly*Assoc : (P Q R : Poly A' n) → P poly* (Q poly* R) ≡ (P poly* Q) poly* R
  poly*Assoc = Poly-Ind-Prop.f A' n _
                (λ P p q i Q R j → trunc (P poly* (Q poly* R)) ((P poly* Q) poly* R) (p Q R) (q Q R) i j)
                (λ _ _ → refl)
                (λ v a → Poly-Ind-Prop.f A' n _
                          (λ Q p q i R j → trunc (base v a poly* (Q poly* R)) ((base v a poly* Q) poly* R) (p R) (q R) i j)
                          (λ _ → refl)
                          (λ v' a' → Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
                                      refl
                                      (λ v'' a'' → cong₂ base (+n-vec-assoc v v' v'') (·Assoc a a' a''))
                                      (λ {U V} ind-U ind-V → cong₂ _poly+_ ind-U ind-V))
                          (λ {U V} ind-U ind-V R → cong₂ _poly+_ (ind-U R) (ind-V R)))
                λ {U V} ind-U ind-V Q R → cong₂ _poly+_ (ind-U Q R) (ind-V Q R)

  poly*AnnihilL : (P : Poly A' n) → 0P poly* P ≡ 0P
  poly*AnnihilL P = refl

  poly*AnnihilR : (P : Poly A' n) → P poly* 0P ≡ 0P
  poly*AnnihilR = Poly-Ind-Prop.f A' n (λ P → (P poly* 0P) ≡ 0P) (λ _ → trunc _ _)
                           refl
                           (λ _ _ → refl)
                           λ {P Q} ind-P ind-Q → (cong₂ _poly+_ ind-P ind-Q) ∙ (poly+IdR 0P)

  1P : Poly A' n
  1P = base (replicate zero) 1r

  poly*IdR : (P : Poly A' n) → P poly* 1P ≡ P
  poly*IdR = Poly-Ind-Prop.f A' n (λ P → (P poly* 1P) ≡ P) (λ _ → trunc _ _)
              refl
              (λ v a → cong₂ base (+n-vec-rid v) (·Rid a))
              (λ {P Q} ind-P ind-Q → cong₂ _poly+_ ind-P ind-Q)

  poly*IdL : (P : Poly A' n) → 1P poly* P ≡ P
  poly*IdL = Poly-Ind-Prop.f A' n (λ P → (1P poly* P) ≡ P) (λ _ → trunc _ _)
              refl
              (λ v a → cong₂ base (+n-vec-lid v) (·Lid a))
              λ {P Q} ind-P ind-Q → cong₂ _poly+_ ind-P ind-Q

  poly*DistR : (P Q R : Poly A' n) → P poly* (Q poly+ R) ≡ (P poly* Q) poly+ (P poly* R)
  poly*DistR = Poly-Ind-Prop.f A' n _
               (λ P p q i Q R j → trunc (P poly* (Q poly+ R)) ((P poly* Q) poly+ (P poly* R)) (p Q R) (q Q R) i j)
               (λ _ _ → sym (poly+IdR 0P))
               (λ v a → λ Q R → refl)
               λ {U V} ind-U ind-V Q R → (cong₂ _poly+_ (ind-U Q R) (ind-V Q R)) ∙ poly-com-adv (U poly* Q) (U poly* R) (V poly* Q) (V poly* R)

  poly*DistL : (P Q R : Poly A' n) → (P poly+ Q) poly* R ≡ (P poly* R) poly+ (Q poly* R)
  poly*DistL P Q R = refl

  poly*Comm : (P Q : Poly A' n) → P poly* Q ≡ Q poly* P
  poly*Comm = Poly-Ind-Prop.f A' n _
              (λ P p q i Q j → trunc (P poly* Q) (Q poly* P) (p Q) (q Q) i j)
              (λ Q → sym (poly*AnnihilR Q))
              (λ v a → Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
                        refl
                        (λ v' a' → cong₂ base (+n-vec-comm v v') (·Comm a a'))
                        (λ {U V} ind-U ind-V → cong₂ _poly+_ ind-U ind-V))
              λ {U V} ind-U ind-V Q → ((cong₂ _poly+_ (ind-U Q) (ind-V Q)) ∙ sym (poly*DistR Q U V))


module SingleSorted.Example where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open import Data.Product using (_×_; proj₁; proj₂; <_,_>; ∃; ∃-syntax; _,_)
import Function using (_∘_)
open import SingleSorted.AlgebraicTheory
open import Categories.Category.Instance.Sets
open import Categories.Category.Cartesian
open import Agda.Primitive
open import Categories.Category

module Sets₀ where
  𝒮 : Category (lsuc lzero) lzero lzero
  𝒮 = Sets lzero

open Sets₀
open Category 𝒮

-- Function extensionality
postulate
  funext : ∀ {X : Set} {Y : X → Set} {f g : ∀ (x : X) → (Y x)} → (∀ (x : X) → ((f x) ≡ (g x))) → (f ≡ g)

record singleton : Set where
  constructor ⋆

η-singleton : ∀ (x : singleton) → ⋆ ≡ x
η-singleton ⋆ = refl

prod-elem-structure : ∀ {A B : Set} {x : A × B} → ∃[ a ] ∃[ b ] (a , b) ≡ x
prod-elem-structure {A} {B} {x} = proj₁ x Data.Product., (proj₂ x) , refl

first-factor :  ∀ {X A B : Obj} {f : X ⇒ A} {g : X ⇒ B} → proj₁ ∘ < f , g > ≡ f
first-factor {X} {A} {B} {f} {g} = funext λ x → refl

second-factor : ∀ {X A B : Obj} {f : X ⇒ A} {g : X ⇒ B} → proj₂ ∘ < f , g > ≡ g
second-factor {X} {A} {B} {f} {g} = funext λ{ x → refl}

unique-map-into-product : ∀ {X A B : Set} {h : X → A × B} {f : X → A} {g : X → B}  {x : X}
  → (proj₁ ∘ h) x ≡ f x
  → (proj₂ ∘ h) x ≡ g x
  --------------------
  → < f , g > x ≡ h x
unique-map-into-product {X} {A} {B} {h} {f} {g} {x} eq1 eq2
  rewrite first-factor {X} {A} {B} {f} {g} | second-factor {X} {A} {B} {f} {g}
  = {!!}

cartesian-Sets : Cartesian 𝒮
cartesian-Sets =
  record
    { terminal = record
                 { ⊤ = singleton
                 ; ⊤-is-terminal = record
                                   { ! = λ{ x → ⋆}
                                   ; !-unique = λ{ f {x} → η-singleton (f x)}
                                   }
                 }
    ; products = record
                 { product = λ {A} {B} →
                           record
                           { A×B = A × B
                           ; π₁ = proj₁
                           ; π₂ = proj₂
                           ; ⟨_,_⟩ = <_,_>
                           ; project₁ = λ{ → refl}
                           ; project₂ = λ{ → refl}
                           ; unique = λ{ {X} {h} {f} {g} eq1 eq2 {x} → {!!}}
                           }

                 }
    }


module Category.Choice where
open import Data.Sum using (_⊎_)
open import Agda.Primitive using (Level; _⊔_; lsuc)
open import Category.Profunctor

record ChoiceImp {a b : Level} (p : Set a → Set a → Set b) : Set (lsuc (a ⊔ b)) where
  field
    isProfunctor : ProfunctorImp p
    left' : ∀ {x y z : Set a} → p x y → p (x ⊎ z) (y ⊎ z)
    right' : ∀ {x y z : Set a} → p x y → p (z ⊎ x) (z ⊎ y)

module Choice {a b : Level} {p : Set a → Set a → Set b} (isChoice : ChoiceImp p) where
  left : ∀ {x y z : Set a} → p x y → p (x ⊎ z) (y ⊎ z)
  left = ChoiceImp.left' isChoice
  right : ∀ {x y z : Set a} → p x y → p (z ⊎ x) (z ⊎ y)
  right = ChoiceImp.right' isChoice
  open Profunctor (ChoiceImp.isProfunctor isChoice)