module Data.Nat.Properties.Extra where

open import Data.Nat
open import Data.Product
open import Data.Nat.Properties
open import Data.Nat.Properties.Simple
open import Function
open import Level using ()
open import Relation.Binary
open import Relation.Binary.Core
open import Relation.Nullary.Negation
open import Relation.Nullary
-- open import Relation.Nullary.Decidable
open import Relation.Binary.PropositionalEquality hiding (isPreorder)
open ≡-Reasoning
open ≤-Reasoning renaming (begin_ to start_; _∎ to _□; _≡⟨_⟩_ to _≈⟨_⟩_)
open DecTotalOrder decTotalOrder using (reflexive) renaming (refl to ≤-refl)

------------------------------------------------------------------------
-- Misc
------------------------------------------------------------------------

isDecTotalOrder : IsDecTotalOrder {A = ℕ} _≡_ _≤_
isDecTotalOrder = DecTotalOrder.isDecTotalOrder decTotalOrder

isTotalOrder : IsTotalOrder {A = ℕ} _≡_ _≤_
isTotalOrder = IsDecTotalOrder.isTotalOrder isDecTotalOrder

isPartialOrder : IsPartialOrder {A = ℕ} _≡_ _≤_
isPartialOrder = IsTotalOrder.isPartialOrder isTotalOrder

isPreorder : IsPreorder {A = ℕ} _≡_ _≤_
isPreorder = IsPartialOrder.isPreorder isPartialOrder

open import Algebra.Structures
import Algebra.FunctionProperties as P; open P (_≡_ {A = ℕ})

isSemiringWithoutOne : IsSemiringWithoutOne _≡_ _⊔_ _⊓_ zero
isSemiringWithoutOne = IsCommutativeSemiringWithoutOne.isSemiringWithoutOne ⊔-⊓-0-isCommutativeSemiringWithoutOne

+-isCommutativeMonoid : IsCommutativeMonoid _≡_ _⊔_ zero
+-isCommutativeMonoid = IsSemiringWithoutOne.+-isCommutativeMonoid  isSemiringWithoutOne

cmp : Trichotomous _≡_ _<_
cmp = StrictTotalOrder.compare strictTotalOrder

⊓-comm : Commutative _⊓_
⊓-comm = IsCommutativeSemiringWithoutOne.*-comm ⊔-⊓-0-isCommutativeSemiringWithoutOne

⊔-comm : Commutative _⊔_
⊔-comm = IsCommutativeMonoid.comm +-isCommutativeMonoid

------------------------------------------------------------------------
-- Equational
------------------------------------------------------------------------

-- ℕ
cancel-suc : ∀ {x y} → suc x ≡ suc y → x ≡ y
cancel-suc {x} {.x} refl = refl
-- cancel-suc : ∀ {x y} → suc x ≡ suc y → x ≡ y
-- cancel-suc {x} {.x} refl = refl

-- _+_
[a+b]+c≡[a+c]+b : ∀ a b c → a + b + c ≡ a + c + b
[a+b]+c≡[a+c]+b a b c =
    begin
        a + b + c
    ≡⟨ +-assoc a b c ⟩
        a + (b + c)
    ≡⟨ cong (λ x → a + x) (+-comm b c) ⟩
        a + (c + b)
    ≡⟨ sym (+-assoc a c b) ⟩
        a + c + b
    ∎

a+[b+c]≡b+[a+c] : ∀ a b c → a + (b + c) ≡ b + (a + c)
a+[b+c]≡b+[a+c] a b c =
    begin
        a + (b + c)
    ≡⟨ sym (+-assoc a b c) ⟩
        a + b + c
    ≡⟨ cong (λ x → x + c) (+-comm a b) ⟩
        b + a + c
    ≡⟨ +-assoc b a c ⟩
        b + (a + c)
    ∎

cancel-+-right : ∀ k {i j} → i + k ≡ j + k → i ≡ j
cancel-+-right zero {i} {j} p  =
    begin
        i
    ≡⟨ sym (+-right-identity i) ⟩
        i + zero
    ≡⟨ p ⟩
        j + zero
    ≡⟨ +-right-identity j ⟩
        j
    ∎
cancel-+-right (suc k) {i} {j} p = cancel-+-right k lemma
    where   lemma : i + k ≡ j + k
            lemma = cancel-suc $
                begin
                    suc (i + k)
                ≡⟨ sym (+-suc i k) ⟩
                    i + suc k
                ≡⟨ p ⟩
                    j + suc k
                ≡⟨ +-suc j k ⟩
                    suc (j + k)
                ∎

-- _*_
*-right-identity : ∀ n → n * 1 ≡ n
*-right-identity zero    = refl
*-right-identity (suc n) = cong suc (*-right-identity n)

*-left-identity : ∀ n → 1 * n ≡ n
*-left-identity zero    = refl
*-left-identity (suc n) = cong suc (*-left-identity n)

distrib-left-*-+ : ∀ m n o → m * (n + o) ≡ m * n + m * o
distrib-left-*-+ m n o =
    begin
        m * (n + o)
    ≡⟨ *-comm m (n + o) ⟩
        (n + o) * m
    ≡⟨ distribʳ-*-+ m n o ⟩
        n * m + o * m
    ≡⟨ cong₂ _+_ (*-comm n m) (*-comm o m) ⟩
        m * n + m * o
    ∎

-- _∸_
m∸n+n≡m : ∀ {m n} → n ≤ m → m ∸ n + n ≡ m
m∸n+n≡m {m} {n} n≤m =
    begin
        m ∸ n + n
    ≡⟨ +-comm (m ∸ n) n ⟩
        n + (m ∸ n)
    ≡⟨ m+n∸m≡n n≤m ⟩
        m
    ∎

m∸[o∸n]+o≡m+n : ∀ m n o
    → n ≤ o
    → o ∸ n ≤ m
    → m ∸ ( o ∸ n ) + o ≡ m + n
m∸[o∸n]+o≡m+n m n o n≤o o∸n≤m =
    begin
        m ∸ (o ∸ n) + o
    ≡⟨ +-comm (m ∸ (o ∸ n)) o ⟩
        o + (m ∸ (o ∸ n))
    ≡⟨ cong (λ x → x + (m ∸ (o ∸ n))) (sym (m+n∸m≡n {n} {o} n≤o)) ⟩
        (n + (o ∸ n)) + (m ∸ (o ∸ n))
    ≡⟨ +-assoc n (o ∸ n) (m ∸ (o ∸ n)) ⟩
        n + ((o ∸ n) + (m ∸ (o ∸ n)))
    ≡⟨ cong (λ w → n + w) (m+n∸m≡n {o ∸ n} o∸n≤m) ⟩
        n + m
    ≡⟨ +-comm n m ⟩
        m + n
    ∎

------------------------------------------------------------------------
-- Relational
------------------------------------------------------------------------

-- _≤_
≤-trans : Transitive _≤_
≤-trans = IsPreorder.trans isPreorder

<⇒≤ : _<_ ⇒ _≤_
<⇒≤ {zero} p = z≤n
<⇒≤ {suc i} (s≤s p) = s≤s (<⇒≤ p)

>⇒≰ : _>_ ⇒ _≰_
>⇒≰ {zero}  ()      q
>⇒≰ {suc m} (s≤s p) (s≤s q) = >⇒≰ p q

≤⇒≯ : _≤_ ⇒ _≯_
≤⇒≯ {zero} p ()
≤⇒≯ {suc m} (s≤s p) (s≤s q) = ≤⇒≯ p q

<⇒≱ : _<_ ⇒ _≱_
<⇒≱ {zero} () z≤n
<⇒≱ {suc i} (s≤s p) (s≤s q) = <⇒≱ p q

>⇒≢ : _>_ ⇒ _≢_
>⇒≢ {zero} () refl
>⇒≢ {suc m} (s≤s m>n) refl = >⇒≢ m>n refl

≥⇒≮ : _≥_ ⇒ _≮_
≥⇒≮ z≤n     ()
≥⇒≮ (s≤s m) (s≤s p) = ≥⇒≮ m p

<⇒≢ : _<_ ⇒ _≢_
<⇒≢ {zero} () refl
<⇒≢ {suc m} (s≤s m<n) refl = <⇒≢ m<n refl

≤∧≢⇒< : ∀ {m n} → m ≤ n → m ≢ n → m < n
≤∧≢⇒< {zero}  {zero}  p       q = contradiction refl q
≤∧≢⇒< {zero}  {suc n} p       q = s≤s z≤n
≤∧≢⇒< {suc m} {zero}  ()      q
≤∧≢⇒< {suc m} {suc n} (s≤s p) q = s≤s (≤∧≢⇒< p (q ∘ cong suc))

≥∧≢⇒> : ∀ {m n} → m ≥ n → m ≢ n → m > n
≥∧≢⇒> {zero}  {zero}  p       q = contradiction refl q
≥∧≢⇒> {zero}  {suc n} ()      q
≥∧≢⇒> {suc m} {zero}  p       q = s≤s z≤n
≥∧≢⇒> {suc m} {suc n} (s≤s p) q = s≤s (≥∧≢⇒> p (q ∘ cong suc))

≤0⇒≡0 : ∀ n → n ≤ 0 → n ≡ 0
≤0⇒≡0 zero    n≤0 = refl
≤0⇒≡0 (suc n) ()

-- ≤-suc : ∀ {m n} → m ≤ n → suc m ≤ suc n
-- ≤-suc z≤n = s≤s z≤n
-- ≤-suc (s≤s rel) = s≤s (s≤s rel)

-- i*j>0⇒i>0∧j>0 : ∀ i j → i * j > 0 → (i > 0 × j > 0)
-- i*j>0⇒i>0∧j>0 zero j ()
-- i*j>0⇒i>0∧j>0 (suc i) zero p = contradiction (proj₂ (i*j>0⇒i>0∧j>0 i 0 p)) (λ ())
-- i*j>0⇒i>0∧j>0 (suc i) (suc j) p = s≤s z≤n , s≤s z≤n

-- _+_
-- m≡n+o⇒m≥o : ∀ {m} {o} n → m ≡ n + o → m ≥ o
-- m≡n+o⇒m≥o {.(n + o)} {o} n refl = n≤m+n o n

n+-mono : ∀ n → (λ x → n + x) Preserves _≤_ ⟶ _≤_
n+-mono n = _+-mono_ {n} {n} ≤-refl

n+-mono-inverse : ∀ n → ∀ {a b} → n + a ≤ n + b → a ≤ b
n+-mono-inverse zero    a≤b       = a≤b
n+-mono-inverse (suc n) (s≤s a≤b) = n+-mono-inverse n a≤b

+n-mono : ∀ n → (λ x → x + n) Preserves _≤_ ⟶ _≤_
+n-mono n {a} {b} a≤b = _+-mono_ {a} {b} {n} {n} a≤b ≤-refl

+n-mono-inverse : ∀ n → ∀ {a b} → a + n ≤ b + n → a ≤ b
+n-mono-inverse zero    {a} {b} p =
    start
        a
    ≈⟨ sym (+-right-identity a) ⟩
        a + 0
    ≤⟨ p ⟩
        b + 0
    ≈⟨ +-right-identity b ⟩
        b
    □
+n-mono-inverse (suc n) {a} {b} p = +n-mono-inverse n p'
    where   p' : a + n ≤ b + n
            p' = ≤-pred $ start
                    suc (a + n)
                ≈⟨ sym (+-suc a n) ⟩
                    a + suc n
                ≤⟨ p ⟩
                    b + suc n
                ≈⟨ +-suc b n ⟩
                    suc (b + n)
                □

+-mono-contra : ∀ {a b c d} → a ≥ b → a + c < b + d → c < d
+-mono-contra {zero} {zero} p q = q
+-mono-contra {zero} {suc b} () q
+-mono-contra {suc a} {zero} {c} {d} p q =
    start
        suc c
    ≤⟨ n≤m+n (suc a) (suc c) ⟩
        suc a + suc c
    ≈⟨ +-suc (suc a) c ⟩
        suc (suc a) + c
    ≤⟨ q ⟩
        d
    □
+-mono-contra {suc a} {suc b} (s≤s p) (s≤s q) = +-mono-contra p q

-- _∸_
∸n-mono : ∀ n → (λ x → x ∸ n) Preserves _≤_ ⟶ _≤_
∸n-mono n {a} {b} a≤b = ∸-mono {a} {b} {n} {n} a≤b ≤-refl

∸n-mono-inverse :  ∀ n → ∀ {a b} → a > n → a ∸ n ≤ b ∸ n → a ≤ b
∸n-mono-inverse zero                    p q = q
∸n-mono-inverse (suc n) {zero}  {zero}  p q = q
∸n-mono-inverse (suc n) {zero}  {suc b} p q = z≤n
∸n-mono-inverse (suc n) {suc a} {zero}  p q = contradiction (≤-pred p) (≤⇒≯ q')
    where
        q' : a ≤ n
        q' = ∸n-mono-inverse n (≤-pred p) $
            start
                a ∸ n
            ≤⟨ q ⟩
                zero
            ≈⟨ sym (n∸n≡0 n) ⟩
                n ∸ n
            □
∸n-mono-inverse (suc n) {suc a} {suc b} p q = s≤s (∸n-mono-inverse n (≤-pred p) q)

n∸-mono : ∀ n → (λ x → n ∸ x) Preserves _≥_ ⟶ _≤_
n∸-mono n {a} {b} a≥b = ∸-mono {n} {n} {a} {b} ≤-refl a≥b

m≥n+o⇒m∸o≥n : ∀ m n o → m ≥ n + o → m ∸ o ≥ n
m≥n+o⇒m∸o≥n m n o p =
    start
        n
    ≈⟨ sym (m+n∸n≡m n o) ⟩
        n + o ∸ o
    ≤⟨ ∸-mono {n + o} {m} {o} p ≤-refl ⟩
        m ∸ o
    □

cancel-∸-right : ∀ {m n} o → m ≥ o → n ≥ o → m ∸ o ≡ n ∸ o → m ≡ n
cancel-∸-right                 zero    p  q  eq = eq
cancel-∸-right {zero}          (suc o) () q  eq
cancel-∸-right {suc m} {zero}  (suc o) p  () eq
cancel-∸-right {suc m} {suc n} (suc o) p  q  eq = cong suc (cancel-∸-right o (≤-pred p) (≤-pred q) eq)

-- _*_
n*-mono : ∀ n → (λ x → n * x) Preserves _≤_ ⟶ _≤_
n*-mono n = _*-mono_ {n} {n} ≤-refl

n*-mono-strict-inverse : ∀ n {a} {b} → n * a < n * b → a < b
n*-mono-strict-inverse zero ()
n*-mono-strict-inverse (suc n) {a} {b} p with suc a ≤? b
n*-mono-strict-inverse (suc n) {a} {b} p | yes q = q
n*-mono-strict-inverse (suc n) {a} {b} p | no ¬q = contradiction p ¬p
    where
        ¬q' : b ≤ a
        ¬q' = ≤-pred (≰⇒> ¬q)
        ¬p : suc (suc n * a) ≰ suc n * b
        ¬p = >⇒≰ (s≤s (n*-mono (suc n) ¬q'))

*n-mono : ∀ n → (λ x → x * n) Preserves _≤_ ⟶ _≤_
*n-mono n {a} {b} a≤b = _*-mono_ {a} {b} {n} {n} a≤b ≤-refl

*-comm-mono : ∀ {a b c d} → a * b ≤ c * d → b * a ≤ d * c
*-comm-mono {a} {b} {c} {d} p =
    start
        b * a
    ≈⟨ *-comm b a ⟩
        a * b
    ≤⟨ p ⟩
        c * d
    ≈⟨ *-comm c d ⟩
        d * c
    □

*-comm-mono-strict : ∀ {a b c d} → a * b < c * d → b * a < d * c
*-comm-mono-strict {a} {b} {c} {d} p =
    start
        suc (b * a)
    ≈⟨ cong suc (*-comm b a) ⟩
        suc (a * b)
    ≤⟨ p ⟩
        c * d
    ≈⟨ *-comm c d ⟩
        d * c
    □

*n-mono-inverse : ∀ n {a} {b} → a * (suc n) ≤ b * (suc n) → a ≤ b
*n-mono-inverse n {zero}          p       = z≤n
*n-mono-inverse n {suc a} {zero}  ()
*n-mono-inverse n {suc a} {suc b} (s≤s p) = s≤s (*n-mono-inverse n (n+-mono-inverse n p))

*n-mono-strict-inverse : ∀ n {a} {b} → a * n < b * n → a < b
*n-mono-strict-inverse n {a} {b} p = n*-mono-strict-inverse n (*-comm-mono-strict {a} {n} {b} {n} p)

m≤m*1+n : ∀ m n → m ≤ m * suc n
m≤m*1+n m zero = reflexive (sym (*-right-identity m))
m≤m*1+n m (suc n) =
    start
        m
    ≤⟨ m≤m+n m (m * suc n) ⟩
        m + m * suc n
    ≈⟨ sym (+-*-suc m (suc n)) ⟩
        m * suc (suc n)
    □

-- _⊔_
m≤n⊔m : ∀ m n → m ≤ n ⊔ m
m≤n⊔m zero    n       = z≤n
m≤n⊔m (suc m) zero    = s≤s (m≤n⊔m m zero)
m≤n⊔m (suc m) (suc n) = s≤s (m≤n⊔m m n)

m⊓n≤n : ∀ m n → m ⊓ n ≤ n
m⊓n≤n zero n          = z≤n
m⊓n≤n (suc m) zero    = z≤n
m⊓n≤n (suc m) (suc n) = s≤s (m⊓n≤n m n)

⊔-upper-bound : ∀ m n o → m + n ≥ o → o ⊔ n ≤ m + n
⊔-upper-bound zero zero zero p = p
⊔-upper-bound zero zero (suc o) ()
⊔-upper-bound zero (suc n) zero p = s≤s (⊔-upper-bound zero n zero z≤n)
⊔-upper-bound zero (suc n) (suc o) p = s≤s (⊔-upper-bound zero n o (≤-pred p))
⊔-upper-bound (suc m) zero zero p = p
⊔-upper-bound (suc m) zero (suc o) p = p
⊔-upper-bound (suc m) (suc n) zero p =
    start
        suc n
    ≤⟨ s≤s (≤-step ≤-refl) ⟩
        suc (suc n)
    ≤⟨ s≤s (n≤m+n m (suc n)) ⟩
        suc (m + suc n)
    □
⊔-upper-bound (suc m) (suc n) (suc o) p =
    start
        suc (o ⊔ n)
    ≤⟨ s≤s (⊔-upper-bound (suc m) n o $
        start
            o
        ≤⟨ ≤-pred p ⟩
            m + suc n
        ≈⟨ +-suc m n ⟩
            suc (m + n)
        □
    ) ⟩
        suc (suc (m + n))
    ≈⟨ cong suc (sym (+-suc m n)) ⟩
        suc (m + suc n)
    □

m⊔n≤m+n : ∀ m n → m ⊔ n ≤ m + n
m⊔n≤m+n zero n = ≤-refl
m⊔n≤m+n (suc m) zero = s≤s (m≤m+n m zero)
m⊔n≤m+n (suc m) (suc n) = s≤s $
    start
        m ⊔ n
    ≤⟨ m⊔n≤m+n m n ⟩
        m + n
    ≤⟨ n+-mono m (n≤m+n 1 n) ⟩
        m + suc n
    □

-- double : ∀ m → m + m ≡ m * 2
-- double m =
--     begin
--         m + m
--     ≡⟨ cong (λ w → m + w) (sym (+-right-identity m)) ⟩
--         m + (m + zero)
--     ≡⟨ *-comm (suc (suc zero)) m ⟩
--         m * suc (suc zero)
--     ∎

-- cancel-∸-right-inverse : ∀ m n o → n ≥ o → m + o ≡ n → m ≡ n ∸ o
-- cancel-∸-right-inverse m n zero p eq =
--     begin
--         m
--     ≡⟨ sym (+-right-identity m) ⟩
--         m + 0
--     ≡⟨ eq ⟩
--         n
--     ∎
-- cancel-∸-right-inverse m zero (suc o) () eq
-- cancel-∸-right-inverse m (suc n) (suc o) p eq =
--     begin
--         m
--     ≡⟨ cancel-∸-right-inverse m n o (≤-pred p) $ cancel-+-left 1 $
--         begin
--             suc m + o
--         ≡⟨ sym (+-suc m o) ⟩
--             m + suc o
--         ≡⟨ eq ⟩
--             suc n
--         ∎
--      ⟩
--         n ∸ o
--     ∎


-- ASR (29 September 2014). Adapted from the example in issue 1269.

-- Case: quote η-reduced and quoteTerm η-reduced.

open import Common.Equality
open import Common.Level
open import Common.Prelude renaming (Nat to ℕ)
open import Common.Product
open import Common.Reflection

data Even  : ℕ → Set where
  isEven0  : Even 0
  isEven+2 : ∀ {n} → Even n → Even (suc (suc n))

pattern expected =
  def (quote Σ)
      ( arg (argInfo hidden relevant) (def (quote Common.Level.lzero) []) ∷
        arg (argInfo hidden relevant) (def (quote Common.Level.lzero) []) ∷
        arg (argInfo visible relevant) (def (quote ℕ) []) ∷
        arg (argInfo visible relevant) (def (quote Even) []) ∷
        [] )

isExpected : Term → Bool
isExpected expected = true
isExpected _        = false

`_ : Bool → Term
` true  = con (quote true) []
` false = con (quote false) []

macro
  checkExpectedType : QName → Tactic
  checkExpectedType i hole =
    bindTC (getType i)   λ t →
    bindTC (normalise t) λ t → give (` isExpected t) hole

input0 : ∃ Even
input0 = 0 , isEven0

input1 : ∃ (λ n → Even n)
input1 = 0 , isEven0

quote0 : Bool
quote0 = checkExpectedType input0

quote1 : Bool
quote1 = checkExpectedType input1

ok0 : quote0 ≡ true
ok0 = refl

ok1 : quote1 ≡ true
ok1 = refl

------------------------------------------------------------------------------
-- For debugging purpose

quotedTerm0 : Term
quotedTerm0 = quoteTerm (∃ Even)

quotedTerm1 : Term
quotedTerm1 = quoteTerm (∃ (λ n → Even n))

b : quotedTerm0 ≡ expected
b = refl

c : quotedTerm1 ≡ expected
c = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Results concerning double negation elimination.
------------------------------------------------------------------------

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

module Axiom.DoubleNegationElimination where

open import Axiom.ExcludedMiddle
open import Level
open import Relation.Nullary
open import Relation.Nullary.Negation

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

-- The classical statement of double negation elimination says that
-- if a property is not not true then it is true.

DoubleNegationElimination : (ℓ : Level) → Set (suc ℓ)
DoubleNegationElimination ℓ = {P : Set ℓ} → ¬ ¬ P → P

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

-- Double negation elimination is equivalent to excluded middle

em⇒dne : ∀ {ℓ} → ExcludedMiddle ℓ → DoubleNegationElimination ℓ
em⇒dne em = decidable-stable em

dne⇒em : ∀ {ℓ} → DoubleNegationElimination ℓ → ExcludedMiddle ℓ
dne⇒em dne = dne excluded-middle


module UniDB.Basic where

open import UniDB.Spec
open import UniDB.Subst
open import Function

module _
  {T : STX} {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}} {{apVrT : ApVr T}} {{apWkTT : ApWk T T}}
  {Ξ : MOR} {{upΞ : Up Ξ}} {{compΞ : Comp Ξ}} {{lkTΞ : Lk T Ξ}} {{lkUpTΞ : LkUp T Ξ}}
    {{lkCompTΞ : LkCompAp T Ξ}} where

  ⊙-↑₁-pointwise : {γ₁ γ₂ γ₃ : Dom} (ξ₁ : Ξ γ₁ γ₂) (ξ₂ : Ξ γ₂ γ₃) →
    [ T ] ((ξ₁ ⊙ ξ₂) ↑₁) ≃ (ξ₁ ↑₁ ⊙ ξ₂ ↑₁)
  lk≃ (⊙-↑₁-pointwise ξ₁ ξ₂) zero = begin
    lk {T} ((ξ₁ ⊙ ξ₂) ↑₁) zero          ≡⟨ lk-↑₁-zero (ξ₁ ⊙ ξ₂) ⟩
    vr zero                             ≡⟨ sym (lk-↑₁-zero ξ₂) ⟩
    lk (ξ₂ ↑₁) zero                     ≡⟨ sym (ap-vr {T} (ξ₂ ↑₁) zero) ⟩
    ap {T} (ξ₂ ↑₁) (vr zero)            ≡⟨ cong (ap {T} (ξ₂ ↑₁)) (sym (lk-↑₁-zero ξ₁)) ⟩
    ap {T} (ξ₂ ↑₁) (lk (ξ₁ ↑₁) zero)    ≡⟨ sym (lk-⊙-ap {T} (ξ₁ ↑₁) (ξ₂ ↑₁) zero) ⟩
    lk (ξ₁ ↑₁ ⊙ ξ₂ ↑₁) zero             ∎
  lk≃ (⊙-↑₁-pointwise ξ₁ ξ₂) (suc i) = begin
    lk ((ξ₁ ⊙ ξ₂) ↑₁) (wk₁ i)           ≡⟨ lk-↑₁-suc (ξ₁ ⊙ ξ₂) i ⟩
    wk₁ (lk (ξ₁ ⊙ ξ₂) i)                ≡⟨ cong wk₁ (lk-⊙-ap {T} ξ₁ ξ₂ i) ⟩
    wk₁ (ap {T} ξ₂ (lk ξ₁ i))           ≡⟨ sym (ap-wk₁ {T} ξ₂ (lk ξ₁ i)) ⟩
    ap {T} (ξ₂ ↑₁) (wk₁ (lk ξ₁ i))      ≡⟨ cong (ap {T} (ξ₂ ↑₁)) (sym (lk-↑₁-suc ξ₁ i)) ⟩
    ap {T} (ξ₂ ↑₁) (lk (ξ₁ ↑₁) (wk₁ i)) ≡⟨ sym (lk-⊙-ap {T} (ξ₁ ↑₁) (ξ₂ ↑₁) (wk₁ i) ) ⟩
    lk (ξ₁ ↑₁ ⊙ ξ₂ ↑₁) (wk₁ i)          ∎


open import Oscar.Prelude
open import Oscar.Class.IsDecidable
open import Oscar.Data.Fin
open import Oscar.Data.Decidable
open import Oscar.Data.Proposequality

module Oscar.Class.IsDecidable.Fin where

instance

  IsDecidableFin : ∀ {n} → IsDecidable (Fin n)
  IsDecidableFin .IsDecidable._≟_ ∅ ∅ = ↑ ∅
  IsDecidableFin .IsDecidable._≟_ ∅ (↑ _) = ↓ λ ()
  IsDecidableFin .IsDecidable._≟_ (↑ _) ∅ = ↓ λ ()
  IsDecidableFin .IsDecidable._≟_ (↑ x) (↑ y) with x ≟ y
  … | ↑ ∅ = ↑ ∅
  … | ↓ x≢y = ↓ λ {∅ → x≢y ∅}


module ZisRing where

open import Data.Product using (_×_; _,_)
open import Function using (_∘_)
open import Relation.Binary.PropositionalEquality
  as PropEq using (_≡_; refl; cong; sym)

open import Integer10 -- 整数の定義

-- ---------- record ----------
record IsSemiGroup (A : Set) (_∙_ : A → A → A) : Set where
  field
    assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
record IsMonoid (A : Set) (_∙_ : A → A → A) (e : A) : Set where
  field
    isSemiGroup : IsSemiGroup A _∙_
    identity : (∀ x → e ∙ x ≡ x) × (∀ x → x ∙ e ≡ x)
record IsGroup (A : Set) (_∙_ : A → A → A) (e : A) (iF : A → A) : Set where
  field
    isMonoid : IsMonoid A _∙_ e
    inv : (∀ x → (iF x) ∙ x ≡ e) × (∀ x → x ∙ (iF x) ≡ e)
record IsAbelianGroup (A : Set) (_∙_ : A → A → A) (e : A) (iF : A → A) : Set where
  field
    isGroup : IsGroup A _∙_ e iF
    comm : ∀ x y → x ∙ y ≡ y ∙ x
record IsRing (A : Set) (_⊕_ _⊗_ : A → A → A) (eP eT : A) (iF : A → A) : Set where
  field
    ⊕isAbelianGroup : IsAbelianGroup A _⊕_ eP iF
    ⊗isMonoid : IsMonoid A _⊗_ eT
    isDistR : (x y z : A) → (x ⊕ y) ⊗ z ≡ (x ⊗ z) ⊕ (y ⊗ z)
    isDistL : (x y z : A) → x ⊗ (y ⊕ z) ≡ (x ⊗ y) ⊕ (x ⊗ z)
-- ----------------------------

-- Z,+が半群であること
lemma5 : (x : ℤ) → x + zero ≡ x
lemma5 zero = refl
lemma5 (succ _) = refl
lemma5 (pred _) = refl
postulate -- ここだけ
  succPred : (x : ℤ) → succ (pred x) ≡ x
  predSucc : (x : ℤ) → pred (succ x) ≡ x
mutual
  succOut1 : (x y : ℤ) → succ x + y ≡ succ (x + y)
  succOut1 x zero = cong succ (sym (lemma5 x))
  succOut1 x (succ y) rewrite succOut1 x y
                                            | succOut2 x y = refl
  succOut1 x (pred y) rewrite predOut2 x y
                                            | succPred (x + y) = refl
  succOut2 : (x y : ℤ) → x + succ y ≡ succ (x + y)
  succOut2 zero y = refl
  succOut2 (succ x) y = refl
  succOut2 (pred x) y rewrite predOut1 x y
                                            | succPred (x + y) = refl
  predOut1 : (x y : ℤ) → pred x + y ≡ pred (x + y)
  predOut1 x zero = cong pred (sym (lemma5 x))
  predOut1 x (succ y) rewrite succOut2 x y
                                            | predSucc (x + y) = refl
  predOut1 x (pred y) rewrite predOut1 x y
                                            | predOut2 x y = refl
  predOut2 : (x y : ℤ) → x + pred y ≡ pred (x + y)
  predOut2 zero y = refl
  predOut2 (succ x) y rewrite succOut1 x y
                                            | predSucc (x + y) = refl
  predOut2 (pred x) y = refl

ℤ+-assoc : ∀ x y z → (x + y) + z ≡ x + (y + z)
ℤ+-assoc zero y z = refl
ℤ+-assoc (succ x) y z rewrite succOut1 x y
                                             | succOut1 (x + y) z
                                             | succOut1 x (y + z) = cong succ (ℤ+-assoc x y z)
ℤ+-assoc (pred x) y z rewrite predOut1 x y
                                             | predOut1 (x + y) z
                                             | predOut1 x (y + z) = cong pred (ℤ+-assoc x y z)
ℤ+-isSemiGroup : IsSemiGroup ℤ _+_
ℤ+-isSemiGroup = record { assoc = ℤ+-assoc }

-- Z,+がモノイドであること
ℤ+Zero-isMonoid : IsMonoid ℤ _+_ zero
ℤ+Zero-isMonoid = record { isSemiGroup = ℤ+-isSemiGroup ;
                                              identity = (zero+x≡x , x+zero≡x) }
  where
    zero+x≡x : ∀ x → zero + x ≡ x
    zero+x≡x _ = refl
    x+zero≡x : ∀ x → x + zero ≡ x
    x+zero≡x = lemma5

-- Z,+が群であること
leftInv : ∀ x → (opposite x + x) ≡ zero
leftInv zero = refl
leftInv (succ x) = leftInv x
leftInv (pred x) = leftInv x
rightInv : ∀ x → (x + opposite x) ≡ zero
rightInv zero = refl
rightInv (succ x) = rightInv x
rightInv (pred x) = rightInv x
ℤ+ZeroOpposite-isGroup : IsGroup ℤ _+_ zero opposite
ℤ+ZeroOpposite-isGroup = record { isMonoid = ℤ+Zero-isMonoid ;
                                                           inv = (leftInv , rightInv) }

-- Z,+がアーベル群であること
ℤ+ZeroOpposite-Comm : ∀ x y → (x + y) ≡ (y + x)
ℤ+ZeroOpposite-Comm zero y = sym (lemma5 y)
ℤ+ZeroOpposite-Comm (succ x) y rewrite succOut1 x y
                                                                 | succOut2 y x
          = cong succ (ℤ+ZeroOpposite-Comm x y)
ℤ+ZeroOpposite-Comm (pred x) y rewrite predOut1 x y
                                                                 | predOut2 y x
          = cong pred (ℤ+ZeroOpposite-Comm x y)
ℤ+ZeroOpposite-isAbelianGroup : IsAbelianGroup ℤ _+_ zero opposite
ℤ+ZeroOpposite-isAbelianGroup
  = record { isGroup = ℤ+ZeroOpposite-isGroup ;
                    comm = ℤ+ZeroOpposite-Comm }

-- Z,*が半群であること
lemma7 : (x y z : ℤ) → x * (y + z) ≡ (x * y) + (x * z)
lemma7 x y zero rewrite lemma5 y | lemma5 (x * y) = refl
lemma7 x y (succ z) rewrite succOut2 y z | lemma7 x y z
  = ℤ+-assoc (x * y) (x * z) x
lemma7 x y (pred z) rewrite predOut2 y z | lemma7 x y z
  = ℤ+-assoc (x * y) (x * z) (opposite x)
lemma8-3 : (x y : ℤ) → opposite x + opposite y ≡ opposite (x + y)
lemma8-3 x zero rewrite lemma5 (opposite x) | lemma5 x = refl
lemma8-3 x (succ y) rewrite predOut2 (opposite x) (opposite y)
                                            | succOut2 x y = cong pred (lemma8-3 x y)
lemma8-3 x (pred y) rewrite succOut2 (opposite x) (opposite y)
                                            | predOut2 x y = cong succ (lemma8-3 x y)
lemma8-5 : (x y : ℤ) → opposite x + y ≡ opposite (x + (opposite y))
lemma8-5 x zero rewrite lemma5 (opposite x) | lemma5 x = refl
lemma8-5 x (succ y) rewrite succOut2 (opposite x) y
                                           | predOut2 x (opposite y) = cong succ (lemma8-5 x y)
lemma8-5 x (pred y) rewrite predOut2 (opposite x) y
                                           | succOut2 x (opposite y) = cong pred (lemma8-5 x y)
lemma8-2 : (x y : ℤ) → x * opposite y ≡ opposite (x * y)
lemma8-2 x zero = refl
lemma8-2 x (succ y) rewrite lemma8-2 x y | lemma8-3 (x * y) x = refl
lemma8-2 x (pred y) rewrite lemma8-2 x y | lemma8-5 (x * y) x = refl
ℤ*-isSemiGroup : IsSemiGroup ℤ _*_
ℤ*-isSemiGroup = record { assoc = ℤ*-assoc }
  where
    ℤ*-assoc : ∀ x y z → ((x * y) * z) ≡ (x * (y * z))
    ℤ*-assoc x y zero = refl
    ℤ*-assoc x y (succ z) rewrite lemma7 x (y * z) y
      = cong (_+ (x * y)) (ℤ*-assoc x y z)
    ℤ*-assoc x y (pred z) rewrite lemma7 x (y * z) (opposite y)
                                                | lemma8-2 x y
      = cong (_+ opposite (x * y)) (ℤ*-assoc x y z)

-- Z,*がモノイドであること
one = succ zero
ℤ*One-isMonoid : IsMonoid ℤ _*_ one
ℤ*One-isMonoid = record { isSemiGroup = ℤ*-isSemiGroup ;
                                              identity = (one*x≡x , x*one≡x) }
  where
    one*x≡x : ∀ x → (succ zero) * x ≡ x
    one*x≡x zero = refl
    one*x≡x (succ x) rewrite succOut2 (succ zero * x) zero
                                          | lemma5 (succ zero * x) = cong succ (one*x≡x x)
    one*x≡x (pred x) rewrite predOut2 (succ zero * x) zero
                                          | lemma5 (succ zero * x) = cong pred (one*x≡x x)
    x*one≡x : ∀ x → x * one ≡ x
    x*one≡x _ = refl

-- Zが環であること
ℤ+ZeroOppo-*One-isRing : IsRing ℤ _+_ _*_ zero one opposite
ℤ+ZeroOppo-*One-isRing
  = record { ⊕isAbelianGroup = ℤ+ZeroOpposite-isAbelianGroup ;
                    ⊗isMonoid = ℤ*One-isMonoid ;
                    isDistR = isDistR-Z ;
                    isDistL = lemma7 }
    where
      lemma9 : ∀ a b c d → ((a + b) + (c + d)) ≡ ((a + c) + (b + d))
      lemma9 zero b c d rewrite sym (ℤ+-assoc b c d)
                                               | ℤ+ZeroOpposite-Comm b c
         = ℤ+-assoc c b d
      lemma9 (succ a) b c d rewrite succOut1 a b
                                              | succOut1 (a + b) (c + d)
                                              | succOut1 a c
                                              | succOut1 (a + c) (b + d)
         = cong succ (lemma9 a b c d)
      lemma9 (pred a) b c d  rewrite predOut1 a b
                                              | predOut1 (a + b) (c + d)
                                              | predOut1 a c
                                              | predOut1 (a + c) (b + d)
         = cong pred (lemma9 a b c d)
      isDistR-Z : ∀ x y z → ((x + y) * z) ≡ ((x * z) + (y * z))
      isDistR-Z x y zero = refl
      isDistR-Z x y (succ z) rewrite isDistR-Z x y z = lemma9 (x * z) (y * z) x y
      isDistR-Z x y (pred z) rewrite sym (lemma8-3 x y) | isDistR-Z x y z
        = lemma9 (x * z) (y * z) (opposite x) (opposite y)

-- (-1) * (-1) = (+1) その2
-- 「反数の反数」を使ってはいけないらしい
-- (-1) * 0 ≡ (-1) * (-1) + (-1)
-- rewrite x * 0 ≡ 0
-- x ≡ y → x + 1 ≡ y + 1
-- rewrite 0 + 1 ≡ 1
-- 結合法則 rewrite (x + y) + z ≡ x + (y + z)
-- rewrite (-1) + 1 ≡ 0
-- rewrite x + 0 ≡ x
-- 左辺と右辺を入れ替える

-₁ = pred zero
seed2 : (x : ℤ) → x * zero ≡ x * -₁ + x
seed2 x rewrite leftInv x = refl
lemma1 : (-₁ * zero ≡ -₁ * -₁ + -₁) → (zero ≡ -₁ * -₁ + -₁)
lemma1 prf = prf
lemma2 : (zero ≡ -₁ * -₁ + -₁) → (zero + one ≡ -₁ * -₁ + -₁ + one)
lemma2 prf = cong (_+ one) prf
lemma3 : (zero + one ≡ -₁ * -₁ + -₁ + one) → (one ≡ -₁ * -₁)
lemma3 prf = prf
lemma4 : (one ≡ -₁ * -₁) → (-₁ * -₁ ≡ one)
lemma4 p = sym p

theorem2 : pred zero * pred zero ≡ succ zero
theorem2 = (lemma4 ∘ lemma3 ∘ lemma2 ∘ lemma1) (seed2 -₁)


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.RingSolver.RingExpression where

open import Cubical.Foundations.Prelude

open import Cubical.Data.FinData
open import Cubical.Data.Nat using (ℕ)
open import Cubical.Data.Nat.Order using (zero-≤)
open import Cubical.Data.Vec.Base
open import Cubical.Algebra.RingSolver.AlmostRing
open import Cubical.Algebra.RingSolver.RawRing renaming (⟨_⟩ to ⟨_⟩ᵣ)

private
  variable
    ℓ : Level

infixl 6 _⊕_
infixl 7 _⊗_

-- Expression in a ring on A with n variables
data Expr {ℓ} (A : Type ℓ) (n : ℕ) : Type ℓ where
  K : A → Expr A n
  ∣ : Fin n → Expr A n
  _⊕_ : Expr A n → Expr A n → Expr A n
  _⊗_ : Expr A n → Expr A n → Expr A n
--  _⊛_ : Expr A n → ℕ → Expr A n    -- exponentiation
  ⊝_ : Expr A n → Expr A n

module Eval (R : RawRing {ℓ}) where
  open import Cubical.Data.Vec
  open RawRing R

  ⟦_⟧ : ∀ {n} → Expr ⟨ R ⟩ᵣ n → Vec ⟨ R ⟩ᵣ n → ⟨ R ⟩ᵣ
  ⟦ K r ⟧ v = r
  ⟦ ∣ k ⟧ v = lookup k v
  ⟦ x ⊕ y ⟧ v =  ⟦ x ⟧ v + ⟦ y ⟧ v
  ⟦ x ⊗ y ⟧ v = ⟦ x ⟧ v · ⟦ y ⟧ v
--  ⟦ x ⊛ l ⟧ v =  ⟦ x ⟧ v ^ l
  ⟦ ⊝ x ⟧ v = - ⟦ x ⟧ v


{-# OPTIONS --cubical --safe #-}
module Cubical.Data.SumFin.Base where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Empty using (⊥; ⊥-elim) public
open import Cubical.Data.Unit using (tt) renaming (Unit to ⊤) public
open import Cubical.Data.Sum using (_⊎_; inl; inr) public

open import Cubical.Data.Nat

open import Cubical.Relation.Nullary
open import Cubical.Relation.Nullary.DecidableEq

private variable k : ℕ

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

pattern fzero = inl tt
pattern fsuc n = inr n

finj : Fin k → Fin (suc k)
finj {suc k} fzero    = fzero
finj {suc k} (fsuc n) = fsuc (finj {k} n)

toℕ : Fin k → ℕ
toℕ {suc k} (inl tt) = zero
toℕ {suc k} (inr x)  = suc (toℕ {k} x)

toℕ-injective : {m n : Fin k} → toℕ m ≡ toℕ n → m ≡ n
toℕ-injective {suc k} {fzero}  {fzero}  _ = refl
toℕ-injective {suc k} {fzero}  {fsuc x} p = ⊥-elim (znots p)
toℕ-injective {suc k} {fsuc m} {fzero}  p = ⊥-elim (snotz p)
toℕ-injective {suc k} {fsuc m} {fsuc x} p = cong fsuc (toℕ-injective (injSuc p))

-- Thus, Fin k is discrete
discreteFin : Discrete (Fin k)
discreteFin fj fk with discreteℕ (toℕ fj) (toℕ fk)
... | yes p = yes (toℕ-injective p)
... | no ¬p = no (λ p → ¬p (cong toℕ p))

isSetFin : isSet (Fin k)
isSetFin = Discrete→isSet discreteFin


module Numeral.Natural.Function.GreatestCommonDivisor where

import Lvl
open import Data
open import Data.Tuple as Tuple using (_⨯_ ; _,_)
open import Functional
open import Logic.Propositional
open import Numeral.CoordinateVector as Vector using (Vector)
open import Numeral.Finite using (𝕟 ; 𝟎 ; 𝐒)
open import Numeral.Natural
open import Numeral.Natural.Oper
open import Numeral.Natural.Oper.FlooredDivision
open import Numeral.Natural.Oper.Modulo
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Relation.Divisibility
open import Numeral.Natural.Relation.Order
open import Numeral.Natural.Relation.Order.Classical
open import Type

-- TODO: Prove that gcd is the infimum in a lattice of ℕ with divisibility as its ordering

{-# TERMINATING #-}
gcdFold : ∀{ℓ}{T : Type{ℓ}} → ((a : ℕ) → (b : ℕ) → (a ≥ b) → (b > 𝟎) → T → T → T) → ((a : ℕ) → (b : ℕ) → (a < b) → (b > 𝟎) → T → T → T) → T → ℕ → ℕ → (ℕ ⨯ T)
gcdFold f g x (a)(𝟎) = (a , x)
gcdFold f g x (a)(𝐒(b)) with [≥]-or-[<] {a}{𝐒(b)}
... | [∨]-introₗ ab = Tuple.mapRight (f a (𝐒(b)) ab (succ min) x) (gcdFold f g x (𝐒(b))(a mod 𝐒(b)))
... | [∨]-introᵣ ba = Tuple.mapRight (g a (𝐒(b)) ba (succ min) x) (gcdFold f g x (𝐒(b))(a))

-- An algorithm for computing the greatest common divisor for two numbers.
-- Also called: Euclid's algorithm.
-- Termination: See `Gcd-existence` for a functionally equal variant of this function that passes the termination checker. It is equal in the sense that the same algorithm is used to construct the existence and to compute the value of this function. This is even more evident when looking at `Gcd-gcd`.
-- Alternative implementation:
--   gcd(a)(𝟎) = a
--   gcd(a)(𝐒(b)) with [≥]-or-[<] {a}{𝐒(b)}
--   ... | [∨]-introₗ _ = gcd(𝐒(b))(a mod 𝐒(b))
--   ... | [∨]-introᵣ _ = gcd(𝐒(b))(a)
gcd : ℕ → ℕ → ℕ
gcd a b = Tuple.left(gcdFold(\_ _ _ _ _ _ → <>) (\_ _ _ _ _ _ → <>) (<>{Lvl.𝟎}) a b)

lcm : ℕ → ℕ → ℕ
lcm(a)(b) = (a ⋅ b) ⌊/⌋₀ gcd(a)(b)

-- `Gcd a b D` is the specialization for 2 elements and states that `D` is a divisor of both `a` and `b`, and the greatest one of them.
-- Example:
--   Divisor(24) = {1,2,3,4,  6,8,   12,      24}
--   Divisor(60) = {1,2,3,4,5,6  ,10,12,15,20,   30,60}
--   24 = 2³ ⋅ 3¹
--   60 = 2² ⋅ 3¹ ⋅ 5¹
--   Gcd 24 60 = {max(Divisor(24) ∩ Divisor(60))} = 2² ⋅ 3¹ = 12
--   Divisor of first : 24 / 12 = 2
--   Divisor of second: 60 / 12 = 5
record GreatestCommonDivisor(n : ℕ) (v : Vector(n)(ℕ)) (D : ℕ) : Type{Lvl.𝟎} where
  constructor intro
  field
    divisor : ∀(i) → (D ∣ v(i))
    maximum : ∀{d} → (∀(i) → (d ∣ v(i))) → (d ∣ D)

Gcd = GreatestCommonDivisor(2) ∘₂ Vector.pair
module Gcd {a b D} where
  intro₂ : _ → _ → (∀{d} → _ → _ → (d ∣ D)) → Gcd a b D
  intro₂ divisorₗ divisorᵣ maximum = intro{2}{Vector.pair a b}
    (\{𝟎 → divisorₗ ; (𝐒(𝟎)) → divisorᵣ})
    (\dv → maximum (dv 𝟎) (dv (𝐒 𝟎)))
  module _ (inst : Gcd a b D) where
    open GreatestCommonDivisor(inst) public
    divisorₗ = divisor 𝟎
    divisorᵣ = divisor(𝐒 𝟎)
    maximum₂ = \{d} a b → maximum{d} \{𝟎 → a ; (𝐒(𝟎)) → b}

-- `Lcm a b M` is the specialization for 2 elements and states that `M` is a multiple of both `a` and `b`, and the smallest one of them.
-- Example:
--   360  = 2³ ⋅ 3² ⋅ 5¹
--   8400 = 2⁴ ⋅ 3¹ ⋅ 5² ⋅ 7¹
--   Lcm 360 8400 = {min(Multiple(360) ∩ Multiple(8400))} = 2⁴ ⋅ 3² ⋅ 5² ⋅ 7¹ = 25200
--   Multiple of first : 360 ⋅ 2¹ ⋅ 5¹ ⋅ 7¹ = 360 ⋅ 70 = 25200
--   Multiple of second: 8400 ⋅ 3¹ = 25200
record LeastCommonMultiple(n : ℕ) (v : Vector(n)(ℕ)) (M : ℕ) : Type{Lvl.𝟎} where
  constructor intro
  field
    multiple : ∀(i) → (v(i) ∣ M)
    minimum  : ∀{m} → (∀(i) → (v(i) ∣ m)) → (M ∣ m)

Lcm = LeastCommonMultiple(2) ∘₂ Vector.pair
module Lcm {a b M} where
  intro₂ : _ → _ → (∀{m} → _ → _ → (M ∣ m)) → Lcm a b M
  intro₂ multipleₗ multipleᵣ minimum = intro{2}{Vector.pair a b}
    (\{𝟎 → multipleₗ ; (𝐒(𝟎)) → multipleᵣ})
    (\dv → minimum (dv 𝟎) (dv (𝐒 𝟎)))
  module _ (inst : Lcm a b M) where
    open LeastCommonMultiple(inst) public
    multipleₗ = multiple 𝟎
    multipleᵣ = multiple(𝐒 𝟎)
    minimum₂ = \{m} a b → minimum{m} \{𝟎 → a ; (𝐒(𝟎)) → b}

open import Logic.Predicate
open import Numeral.Natural.Inductions
open import Numeral.Natural.Oper.Modulo.Proofs
open import Numeral.Natural.Relation.Divisibility.Proofs
open import Numeral.Natural.Relation.Divisibility.Proofs.Modulo
open import Numeral.Natural.Relation.Order.Proofs
open import Relator.Equals
open import Relator.Equals.Proofs
open import Sets.PredicateSet using (_∈_ ; _⊆_)
open import Structure.Relator.Properties
open import Structure.Setoid.Uniqueness
open import Syntax.Function

private variable a b d : ℕ

Gcd-unique : Unique(Gcd a b)
Gcd-unique p q = antisymmetry(_∣_)(_≡_)
  (Gcd.maximum₂ q (Gcd.divisorₗ p) (Gcd.divisorᵣ p))
  (Gcd.maximum₂ p (Gcd.divisorₗ q) (Gcd.divisorᵣ q))

Gcd-base : (a ∈ Gcd(a)(𝟎))
Gcd-base = Gcd.intro₂
  divides-reflexivity
  Div𝟎
  const

Gcd-step : (a ≥ 𝐒(b)) → Gcd(a mod 𝐒(b))(𝐒(b)) ⊆ Gcd(a)(𝐒(b))
Gcd-step ab p = Gcd.intro₂
  ([↔]-to-[←] (divides-mod (Gcd.divisorᵣ p)) (Gcd.divisorₗ p))
  (Gcd.divisorᵣ p)
  (\da db → Gcd.maximum₂ p ([↔]-to-[→] (divides-mod db) da) db)

Gcd-swap : Gcd(a)(b) ⊆ Gcd(b)(a)
Gcd-swap p = Gcd.intro₂
  (Gcd.divisorᵣ p)
  (Gcd.divisorₗ p)
  (swap(Gcd.maximum₂ p))

-- Note: The construction for the existence is following the same steps as in the definition of the function `gcd`, but unlike `gcd` which does not pass the termination checker, this uses [ℕ]-strong-induction to pass it.
Gcd-existence : ∃(Gcd a b)
Gcd-existence{a}{b} = [ℕ]-strong-induction {φ = b ↦ ∀{a} → ∃(Gcd a b)} base step {b}{a} where
  base : ∀{a} → ∃(Gcd a 𝟎)
  base{a} = [∃]-intro a ⦃ Gcd-base ⦄

  step : ∀{i} → (∀{j} → (j ≤ i) → ∀{a} → ∃(Gcd a j)) → ∀{a} → ∃(Gcd a (𝐒(i)))
  step {i} prev {a} with [≥]-or-[<] {a}{𝐒(i)}
  ... | [∨]-introₗ ia = [∃]-map-proof (Gcd-step ia ∘ Gcd-swap) (prev{a mod 𝐒(i)} ([≤]-without-[𝐒] (mod-maxᵣ{a}{𝐒(i)})) {𝐒(i)})
  ... | [∨]-introᵣ (succ ai) = [∃]-map-proof Gcd-swap(prev {a} ai {𝐒(i)})

Gcd-gcdFold : ∀{a b}{ℓ}{T : Type{ℓ}}{f}{g}{x : T} → Gcd a b (Tuple.left(gcdFold f g x a b))
Gcd-gcdFold{a}{b}{f = f}{g}{x} = [ℕ]-strong-induction {φ = b ↦ ∀{a} → Gcd a b (Tuple.left(gcdFold f g x a b))} base step {b}{a} where
  base : ∀{a} → Gcd a 𝟎 (Tuple.left(gcdFold f g x a 𝟎))
  base{a} = Gcd-base

  step : ∀{i} → (∀{j} → (j ≤ i) → ∀{a} → Gcd a j (Tuple.left(gcdFold f g x a j))) → ∀{a} → Gcd a (𝐒(i)) (Tuple.left(gcdFold f g x a (𝐒(i))))
  step {i} prev {a} with [≥]-or-[<] {a}{𝐒(i)}
  ... | [∨]-introₗ ia = (Gcd-step ia ∘ Gcd-swap) (prev{a mod 𝐒(i)} ([≤]-without-[𝐒] (mod-maxᵣ{a}{𝐒(i)})) {𝐒(i)})
  ... | [∨]-introᵣ (succ ai) = Gcd-swap(prev {a} ai {𝐒(i)})

-- Usage: This allows the transferrence of proofs between `Gcd` and `gcd`. It is sometimes easier to prove properties by using `Gcd` first and then transfering them so that the proofs also hold for `gcd`.
Gcd-gcdFold-value : ∀{a b D}{ℓ}{T : Type{ℓ}}{f}{g}{x : T} → (Gcd a b D) ↔ (Tuple.left(gcdFold f g x a b) ≡ D)
Gcd-gcdFold-value = [↔]-intro (\{[≡]-intro → Gcd-gcdFold}) (Gcd-unique Gcd-gcdFold)

Gcd-gcd : Gcd a b (gcd a b)
Gcd-gcd = Gcd-gcdFold

Gcd-gcd-value : (Gcd a b d) ↔ (gcd a b ≡ d)
Gcd-gcd-value = Gcd-gcdFold-value


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

open import Relation.Binary
open import Prelude

module Data.List.Sort.MergeSort {e} {E : Type e} {r₁ r₂} (totalOrder : TotalOrder E r₁ r₂) where

open TotalOrder totalOrder hiding (refl)

open import Data.List.Sort.InsertionSort totalOrder using (insert; insert-sort)

open import Data.List
open import Data.List.Properties

open import TreeFold

open import Algebra
open import Relation.Nullary.Decidable.Properties using (from-reflects)
open import Path.Reasoning

mergeˡ :  E → (List E → List E) → List E → List E
mergeˡ x xs []       = x ∷ xs []
mergeˡ x xs (y ∷ ys) = if x ≤ᵇ y then x ∷ xs (y ∷ ys) else y ∷ mergeˡ x xs ys

_⋎_ : List E → List E → List E
_⋎_ = foldr mergeˡ id

merge-idʳ : ∀ xs → xs ⋎ [] ≡ xs
merge-idʳ [] = refl
merge-idʳ (x ∷ xs) = cong (x ∷_) (merge-idʳ xs)

merge-assoc : Associative _⋎_
merge-assoc []       ys zs = refl
merge-assoc (x ∷ xs) [] zs = cong (λ xs′ → mergeˡ x (xs′ ⋎_) zs) (merge-idʳ xs)
merge-assoc (x ∷ xs) (y ∷ ys) [] = merge-idʳ ((x ∷ xs) ⋎ (y ∷ ys)) ; cong ((x ∷ xs) ⋎_) (sym (merge-idʳ (y ∷ ys)))
merge-assoc (x ∷ xs) (y ∷ ys) (z ∷ zs)
  with merge-assoc xs (y ∷ ys) (z ∷ zs)
     | merge-assoc (x ∷ xs) ys (z ∷ zs)
     | merge-assoc (x ∷ xs) (y ∷ ys) zs
     | x ≤? y in x≤?y
     | y ≤? z in y≤?z
... | _ | _ | r | no x≰y | no y≰z rewrite y≤?z rewrite x≤?y =
  cong (z ∷_) r ;
  cong (bool′ _ _)
    (sym (from-reflects false (x ≤? z) (<⇒≱ (<-trans (≰⇒> y≰z) (≰⇒> x≰y)))))
... | _ | r | _ | no  x≰y | yes y≤z rewrite y≤?z rewrite x≤?y = cong (y ∷_) r
... | r | _ | _ | yes x≤y | yes y≤z rewrite y≤?z rewrite x≤?y =
  cong (bool′ _ _)
    (from-reflects true (x ≤? z) (≤-trans x≤y y≤z)) ;
  cong (x ∷_) r
merge-assoc (x ∷ xs) (y ∷ ys) (z ∷ zs) | rx≤z | _ | rx≰z | yes x≤y | no y≰z with x ≤? z
... | no  x≰z rewrite x≤?y = cong (z ∷_) rx≰z
... | yes x≤z rewrite y≤?z = cong (x ∷_) rx≤z

merge-sort : List E → List E
merge-sort = treeFold _⋎_ [] ∘ map (_∷ [])

merge-insert : ∀ x xs → (x ∷ []) ⋎ xs ≡ insert x xs
merge-insert x [] = refl
merge-insert x (y ∷ xs) with x ≤ᵇ y
... | false = cong (y ∷_) (merge-insert x xs)
... | true  = refl

merge≡insert-sort : ∀ xs → merge-sort xs ≡ insert-sort xs
merge≡insert-sort xs =
  merge-sort xs                      ≡⟨⟩
  treeFold _⋎_ [] (map (_∷ []) xs)   ≡⟨ treeFoldHom _⋎_ [] merge-assoc (map (_∷ []) xs) ⟩
  foldr _⋎_ [] (map (_∷ []) xs)      ≡⟨ map-fusion _⋎_ [] (_∷ []) xs ⟩
  foldr (λ x → (x ∷ []) ⋎_) [] xs    ≡⟨ cong (λ f → foldr f [] xs) (funExt (funExt ∘ merge-insert)) ⟩
  foldr insert [] xs                 ≡⟨⟩
  insert-sort xs ∎


module Test where

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

module Q where
  module R where
    f : Nat -> Nat
    f n = suc n

module B (n : Nat) where
  open Q.R public
  q = f n

module Bz = B zero

postulate
  _==_ : {A : Set} -> A -> A -> Set
  refl : {A : Set}{x : A} -> x == x


module Oscar.Category.Semigroupoid where

open import Oscar.Data.Equality
open import Oscar.Level
open import Oscar.Relation

record Semigroupoid
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔪} (_⊸_ : 𝔒 → 𝔒 → Ø 𝔪)
  : Ø 𝔬 ∙̂ 𝔪 where
  infixr 9 _∙_
  field
    _∙_ : ∀ {m n} → m ⊸ n → ∀ {l} → l ⊸ m → l ⊸ n
    ∙-associativity : ∀ {k l} (f : k ⊸ l) {m} (g : l ⊸ m) {n} (h : m ⊸ n) → (h ∙ g) ∙ f ≡ h ∙ g ∙ f

record Category
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔪} (_⊸_ : 𝔒 → 𝔒 → Ø 𝔪)
  : Ø 𝔬 ∙̂ 𝔪 where
  field
    semigroupoid : Semigroupoid _⊸_
  open Semigroupoid semigroupoid public

  field
    ε : ∀ {n} → n ⊸ n
    ε-left-identity : ∀ {m n} (σ : m ⊸ n) → ε ∙ σ ≡ σ
    ε-right-identity : ∀ {m n} (σ : m ⊸ n) → σ ∙ ε ≡ σ

record RCategory
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔪} {_⊸_ : 𝔒 → 𝔒 → Ø 𝔪}
  (category : Category _⊸_)
  : Ø 𝔬 ∙̂ 𝔪 where
  open Category category public hiding (semigroupoid)

module MCategory
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔪} {_⊸_ : 𝔒 → 𝔒 → Ø 𝔪}
  (category : Category _⊸_)
  where
  open Category category public hiding (semigroupoid)

-- open import Oscar.Category.Morphism
-- open import Oscar.Category.Setoid
-- open import Oscar.Level
-- open import Oscar.Relation

-- module _ {𝔬} {𝔪} {𝔮} (𝔐 : Morphism 𝔬 𝔪 𝔮) (open Morphism 𝔐) where
--   record IsSemigroupoid (_∙_ : Transitive _↦_) : Set (lsuc (𝔬 ⊔ 𝔪 ⊔ 𝔮)) where
--     field
--       extensionality : Extensional _∙_ _≞_
--       associativity : Associative _∙_ _≞_

-- open IsSemigroupoid ⦃ … ⦄ public

-- infixr 4 _,_
-- record Semigroupoid 𝔬 𝔪 𝔮 : Set (lsuc (𝔬 ⊔ 𝔪 ⊔ 𝔮))
--   where
--   constructor _,_

--   field
--     𝔐 : Morphism 𝔬 𝔪 𝔮

--   open Morphism 𝔐 public

--   infixl 7 _∙_
--   field _∙_ : Transitive _↦_

--   field ⦃ isSemigroupoid ⦄ : IsSemigroupoid 𝔐 _∙_
--   open IsSemigroupoid isSemigroupoid public


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

open import LogicalFormulae
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Functions.Definition
open import Setoids.Setoids
open import Setoids.Subset
open import Graphs.Definition
open import Sets.FinSet.Definition
open import Sets.FinSet.Lemmas
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Sets.EquivalenceRelations
open import Graphs.PathGraph

module Graphs.CycleGraph where

CycleGraph : (n : ℕ) → .(2 <N n) → Graph _ (reflSetoid (FinSet n))
Graph._<->_ (CycleGraph n _) x y = ((toNat x ≡ succ (toNat y)) || (toNat y ≡ succ (toNat x))) || (((toNat x ≡ 0) && (toNat y ≡ n)) || ((toNat y ≡ 0) && (toNat x ≡ n)))
Graph.noSelfRelation (CycleGraph n _) y (inl (inl x)) = nNotSucc x
Graph.noSelfRelation (CycleGraph n _) y (inl (inr x)) = nNotSucc x
Graph.noSelfRelation (CycleGraph (succ n) _) y (inr (inl (fst ,, snd))) = naughtE (transitivity (equalityCommutative fst) snd)
Graph.noSelfRelation (CycleGraph (succ n) _) y (inr (inr (fst ,, snd))) = naughtE (transitivity (equalityCommutative fst) snd)
Graph.symmetric (CycleGraph n _) (inl (inl x)) = inl (inr x)
Graph.symmetric (CycleGraph n _) (inl (inr x)) = inl (inl x)
Graph.symmetric (CycleGraph n _) (inr (inl x)) = inr (inr x)
Graph.symmetric (CycleGraph n _) (inr (inr x)) = inr (inl x)
Graph.wellDefined (CycleGraph n _) refl refl i = i


module Lvl where

open import Type

open import Agda.Primitive public
  using (Level; _⊔_)
  renaming (lzero to 𝟎; lsuc to 𝐒)

-- Wraps a lower level set in a higher level wrapper set.
record Up {ℓ₁ ℓ₂} (T : Type{ℓ₂}) : Type{ℓ₁ ⊔ ℓ₂} where
  constructor up
  field obj : T

of : ∀{ℓ} → Type{ℓ} → Level
of {ℓ} _ = ℓ
{-# INLINE of #-}

ofType : ∀{ℓ} → Type{𝐒(ℓ)} → Level
ofType {ℓ} _ = ℓ
{-# INLINE ofType #-}


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

open import Categories.Category

module Categories.Category.Cartesian.Properties {o ℓ e} (C : Category o ℓ e) where

open import Function using (_$_)

open import Categories.Category.Cartesian C

open import Categories.Diagram.Pullback C
open import Categories.Diagram.Equalizer C
open import Categories.Morphism.Reasoning C

private
  open Category C
  variable
    A B X Y : Obj
    f g : A ⇒ B

-- all binary products and pullbacks implies equalizers
module _ (prods : BinaryProducts) (pullbacks : ∀ {A B X} (f : A ⇒ X) (g : B ⇒ X) → Pullback f g) where
  open BinaryProducts prods
  open HomReasoning

  prods×pullbacks⇒equalizers : Equalizer f g
  prods×pullbacks⇒equalizers {f = f} {g = g} = record
    { arr       = pb′.p₁
    ; equality  = begin
      f ∘ pb′.p₁           ≈⟨ refl⟩∘⟨ helper₁ ⟩
      f ∘ pb.p₁ ∘ pb′.p₂   ≈⟨ pullˡ pb.commute ⟩
      (g ∘ pb.p₂) ∘ pb′.p₂ ≈˘⟨ pushʳ helper₂ ⟩
      g ∘ pb′.p₁           ∎
    ; equalize  = λ {_ i} eq → pb′.universal $ begin
      ⟨ id , id ⟩ ∘ i                                       ≈⟨ ⟨⟩∘ ⟩
      ⟨ id ∘ i , id ∘ i ⟩                                   ≈⟨ ⟨⟩-cong₂ identityˡ identityˡ ⟩
      ⟨ i ,  i ⟩                                            ≈˘⟨ ⟨⟩-cong₂ pb.p₁∘universal≈h₁ pb.p₂∘universal≈h₂ ⟩
      ⟨ pb.p₁ ∘ pb.universal eq , pb.p₂ ∘ pb.universal eq ⟩ ≈˘⟨ ⟨⟩∘ ⟩
      h ∘ pb.universal eq                                   ∎
    ; universal = ⟺ pb′.p₁∘universal≈h₁
    ; unique    = λ eq → pb′.unique (⟺ eq) (pb.unique (pullˡ (⟺ helper₁) ○ ⟺ eq) (pullˡ (⟺ helper₂) ○ ⟺ eq))
    }
    where pb : Pullback f g
          pb         = pullbacks _ _
          module pb  = Pullback pb
          h          = ⟨ pb.p₁ , pb.p₂ ⟩
          pb′ : Pullback ⟨ id , id ⟩ h
          pb′        = pullbacks _ _
          module pb′ = Pullback pb′
          helper₁ : pb′.p₁ ≈ pb.p₁ ∘ pb′.p₂
          helper₁    = begin
            pb′.p₁                    ≈˘⟨ cancelˡ project₁ ⟩
            π₁ ∘ ⟨ id , id ⟩ ∘ pb′.p₁ ≈⟨ refl⟩∘⟨ pb′.commute ⟩
            π₁ ∘ h ∘ pb′.p₂           ≈⟨ pullˡ project₁ ⟩
            pb.p₁ ∘ pb′.p₂            ∎
          helper₂ : pb′.p₁ ≈ pb.p₂ ∘ pb′.p₂
          helper₂    = begin
            pb′.p₁                    ≈˘⟨ cancelˡ project₂ ⟩
            π₂ ∘ ⟨ id , id ⟩ ∘ pb′.p₁ ≈⟨ refl⟩∘⟨ pb′.commute ⟩
            π₂ ∘ h ∘ pb′.p₂           ≈⟨ pullˡ project₂ ⟩
            pb.p₂ ∘ pb′.p₂            ∎


module Slice where

open import Logic.Relations
open import Logic.Equivalence
open import Logic.Base
open import Category

module SliceCat (ℂ : Cat)(Γ : Category.Obj ℂ) where

  open module CC = Category.Category ℂ

  record SlObj : Set1 where
    field
      dom : Obj
      arr : dom ─→ Γ

  record _Sl→_ (f f' : SlObj) : Set where
    field
      h : (SlObj.dom f) ─→ (SlObj.dom f')
      π : (SlObj.arr f') ∘ h == (SlObj.arr f)

  SlId : {f : SlObj} -> f Sl→ f
  SlId = record
    { h = id
    ; π = idRight
    }

  _o_ : {f f' f'' : SlObj} -> f' Sl→ f'' -> f Sl→ f' -> f Sl→ f''
  _o_ {F} {F'} {F''} F₁ F₂ =
    let f   = SlObj.arr F   in
    let f'  = SlObj.arr F'  in
    let f'' = SlObj.arr F'' in
    let h'  = _Sl→_.h F₁     in
    let h   = _Sl→_.h F₂     in
    record
      { h = (_Sl→_.h F₁) ∘ (_Sl→_.h F₂)
      -- Proof of f'' ∘ (h' ∘ h) == f
      ; π = trans (trans (sym assoc)
                         (congL (_Sl→_.π F₁)))
                  (_Sl→_.π F₂)
      }

  SlRel : {A B : SlObj} -> Rel (A Sl→ B)
  SlRel f f' = (_Sl→_.h f) == (_Sl→_.h f')

  SlRefl : {A B : SlObj} -> Reflexive {A Sl→ B} SlRel
  SlRefl = refl

  SlSym : {A B : SlObj} -> Symmetric {A Sl→ B} SlRel
  SlSym = sym

  SlTrans : {A B : SlObj} -> Transitive {A Sl→ B} SlRel
  SlTrans = trans

  SlEq : {A B : SlObj} -> Equivalence (A Sl→ B)
  SlEq {A} {B} = record
    { _==_  = SlRel {A} {B}
    ; refl  = \{f     : A Sl→ B} -> SlRefl  {A}{B}{f}
    ; sym   = \{f g   : A Sl→ B} -> SlSym   {A}{B}{f}{g}
    ; trans = \{f g h : A Sl→ B} -> SlTrans {A}{B}{f}{g}{h}
    }

  SlCong : {A B C : SlObj}{f f' : B Sl→ C}{g g' : A Sl→ B} ->
    SlRel f f' -> SlRel g g' -> SlRel (f o g) (f' o g')
  SlCong = cong

  SlIdLeft : {A B : SlObj}{f : A Sl→ B} -> SlRel (SlId o f) f
  SlIdLeft = idLeft

  SlIdRight : {A B : SlObj}{f : A Sl→ B} -> SlRel (f o SlId) f
  SlIdRight = idRight

  SlAssoc : {A B C D : SlObj}{f : C Sl→ D}{g : B Sl→ C}{h : A Sl→ B} ->
    SlRel ((f o g) o h) (f o (g o h))
  SlAssoc = assoc

  Slice : Cat
  Slice = record
    { Obj     = SlObj
    ; _─→_    = _Sl→_
    ; id      = SlId
    ; _∘_     = _o_
    ; Eq      = SlEq
    ; cong    = \{A B C : SlObj}{f f' : B Sl→ C}{g g' : A Sl→ B} -> SlCong {A}{B}{C}{f}{f'}{g}{g'}
    ; idLeft  = \{A B : SlObj}{f : A Sl→ B} -> SlIdLeft {A} {B} {f}
    ; idRight = \{A B : SlObj}{f : A Sl→ B} -> SlIdRight {A} {B} {f}
    ; assoc   = \{A B C D : SlObj}{f : C Sl→ D}{g : B Sl→ C}{h : A Sl→ B} ->
        SlAssoc {A}{B}{C}{D}{f}{g}{h}
    }


{-# OPTIONS --safe #-}
module Cubical.Algebra.Group.Instances.DiffInt where

open import Cubical.HITs.SetQuotients
open import Cubical.Foundations.Prelude
open import Cubical.Data.Int.MoreInts.DiffInt renaming (ℤ to ℤType ; _+_ to _+ℤ_ ; _-_ to _-ℤ_)
open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Semigroup.Base
open import Cubical.Algebra.Monoid.Base

open GroupStr

ℤ-isGroup : IsGroup {G = ℤType} ([ 0 , 0 ]) (_+ℤ_) (-ℤ_)
IsSemigroup.is-set (IsMonoid.isSemigroup (IsGroup.isMonoid ℤ-isGroup)) = ℤ-isSet
IsSemigroup.assoc (IsMonoid.isSemigroup (IsGroup.isMonoid ℤ-isGroup)) = +ℤ-assoc
IsMonoid.identity (IsGroup.isMonoid ℤ-isGroup) = λ x → (zero-identityʳ 0 x  , zero-identityˡ 0 x)
IsGroup.inverse ℤ-isGroup = λ x → (-ℤ-invʳ x , -ℤ-invˡ x)

ℤ : Group₀
fst ℤ = ℤType
1g (snd ℤ) = [ 0 , 0 ]
_·_ (snd ℤ) = _+ℤ_
inv (snd ℤ) = -ℤ_
isGroup (snd ℤ) = ℤ-isGroup


module Serializer.Fin where

open import Data.Fin hiding (_+_)
open import Data.Nat
open import Function using (_∘_ ; _$_ ; _∋_ ; id ; const)
open import Function.Bijection
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl ; cong ; setoid )

open import Serializer

instance
  serializerFin : ∀ {n} -> Serializer (Fin n)
  serializerFin {n} = record {
    size = n ;
    from = Function.id ;
    to = Function.id ;
    bijection = Function.Bijection.id }


open import Agda.Primitive using (lzero; lsuc; _⊔_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import SingleSorted.Substitution
open import Data.Nat using (ℕ; zero; suc)
import MultiSorted.Context as Context

module MultiSorted.Group where

data 𝒜 : Set₀ where
  A : 𝒜

single-sort : ∀ (X : 𝒜) → X ≡ A
single-sort A = refl

open import MultiSorted.AlgebraicTheory

open import MultiSorted.AlgebraicTheory
data GroupOp : Set where
  e : GroupOp
  inv : GroupOp
  mul : GroupOp

ctx : ∀ (n : ℕ) → Context.Context 𝒜
ctx zero = Context.ctx-empty
ctx (suc n) = Context.ctx-concat (ctx n) (Context.ctx-slot A)

-- the signature of the theory of small groups
-- has one constant, one unary operation, one binary operation
Σ : Signature {lzero} {lzero}
Σ = record { sort = 𝒜
           ; oper = GroupOp
           ; oper-arity = λ{ e → ctx 0 ; inv → ctx 1 ; mul → ctx 2}
           ; oper-sort = λ{ e → A ; inv → A ; mul → A}
           }

open Signature Σ

singleton-context : (var (ctx-slot A)) → var (ctx-concat ctx-empty (ctx-slot A))
singleton-context (var-var {A}) = var-inr (var-var {A})

single-sort-context : ∀ {Γ : Context} (x : var Γ) → sort-of Γ x ≡ A
single-sort-context {Γ} x = single-sort (Context.sort-of 𝒜  Γ x)

single-sort-terms : ∀ {X : 𝒜} {Γ : Context} → Term Γ X ≡ Term Γ A
single-sort-terms {A} = refl

σ : ∀ {Γ : Context} {t : Term Γ A} →  Γ ⇒s (ctx 1)
σ {Γ} {t} = λ{ (var-inr var-var) → t}

δ : ∀ {Γ : Context} {t : Term Γ A} {s : Term Γ A} → Γ ⇒s (ctx 2)
δ {Γ} {t} {s} = sub
  where
  sub : Γ ⇒s (ctx 2)
  sub (var-inl x) rewrite (single-sort-terms {(sort-of (ctx 2) (var-inl x))} {Γ}) = t
  sub (var-inr y) rewrite (single-sort-terms {(sort-of (ctx 2) (var-inr y))} {Γ}) = s

-- helper functions for creating terms
e' : ∀ {Γ : Context} → Term Γ A
e' {Γ} = tm-oper e λ()

_∗_ : ∀ {Γ} → Term Γ A → Term Γ A → Term Γ A
t ∗ s =  tm-oper mul λ{ xs → δ {t = t} {s = s} xs}

_ⁱ : ∀ {Γ : Context} →  Term Γ A → Term Γ A
t ⁱ =  tm-oper inv λ{ x → σ {t = t} x}

-- _∗_ : ∀ {Γ} → Term Γ A → Term Γ A → Term Γ A
-- _∗_ {Γ} t s  =  tm-oper mul λ{ (var-inl i) → {!!} ; (var-inr i) → {!!}}

-- _ⁱ : ∀ {Γ : Context} →  Term Γ A → Term Γ A
-- t ⁱ =  tm-oper inv λ{ x → t }

infixl 5 _∗_
infix 6 _ⁱ

_ : Term (ctx 2) A
_ = tm-var (var-inl (var-inr var-var)) ∗ tm-var (var-inr var-var)

-- group equations
data GroupEq : Set where
  mul-assoc e-left e-right inv-left inv-right : GroupEq

mul-assoc-eq : Equation Σ
e-left-eq : Equation Σ
e-right-eq : Equation Σ
inv-left-eq : Equation Σ
inv-right-eq : Equation Σ

mul-assoc-eq = record { eq-ctx = ctx 3
                      ; eq-lhs = x ∗ y ∗ z
                      ; eq-rhs = x ∗ (y ∗ z)
                      }
             where
             x : Term (ctx 3) A
             y : Term (ctx 3) A
             z : Term (ctx 3) A
             x = tm-var (var-inl (var-inl (var-inr var-var)))
             y = tm-var (var-inl (var-inr var-var))
             z = tm-var (var-inr var-var)

e-left-eq = record { eq-ctx = ctx 1 ; eq-lhs = e' ∗ x ; eq-rhs = x }
  where
  x : Term (ctx 1) A
  x = tm-var (var-inr var-var)

e-right-eq = record { eq-ctx = ctx 1 ; eq-lhs = x ∗ e' ; eq-rhs = x }
  where
  x : Term (ctx 1) A
  x = tm-var (var-inr var-var)

inv-left-eq = record { eq-ctx = ctx 1 ; eq-lhs = x ⁱ ∗ x ; eq-rhs = e' }
  where
  x : Term (ctx 1) A
  x = tm-var (var-inr var-var)

inv-right-eq = record { eq-ctx = ctx 1 ; eq-lhs = x ∗ x ⁱ ; eq-rhs = e' }
  where
  x : Term (ctx 1) A
  x = tm-var (var-inr var-var)

𝒢 : Theory lzero Σ
𝒢 = record { ax = GroupEq
            ; ax-eq = λ{ mul-assoc → mul-assoc-eq
                       ; e-left → e-left-eq
                       ; e-right → e-right-eq
                       ; inv-left → inv-left-eq
                       ; inv-right → inv-right-eq
                       }
            }


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

open import lib.Basics
open import lib.types.Group
open import lib.types.Lift
open import lib.groups.Homomorphisms

module lib.groups.Lift where

Lift-group-structure : ∀ {i j} {A : Type i}
  → GroupStructure A → GroupStructure (Lift {j = j} A)
Lift-group-structure GS = record
  { ident = lift ident
  ; inv = λ {(lift x) → lift (inv x)}
  ; comp = λ {(lift x) (lift y) → lift (comp x y)}
  ; unitl = λ {(lift y) → ap lift (unitl y)}
  ; unitr = λ {(lift x) → ap lift (unitr x)}
  ; assoc = λ {(lift x) (lift y) (lift z) → ap lift (assoc x y z)}
  ; invr = λ {(lift x) → ap lift (invr x)}
  ; invl = λ {(lift x) → ap lift (invl x)}
  }
  where open GroupStructure GS

Lift-group : ∀ {i j} → Group i → Group (lmax i j)
Lift-group {j = j} G = group (Lift {j = j} El) (Lift-level El-level)
                             (Lift-group-structure group-struct)
  where open Group G

lift-hom : ∀ {i j} {G : Group i} → (G →ᴳ Lift-group {j = j} G)
lift-hom = record {f = lift; pres-comp = λ _ _ → idp}

lower-hom : ∀ {i j} {G : Group i} → (Lift-group {j = j} G →ᴳ G)
lower-hom = record {f = lower; pres-comp = λ _ _ → idp}

lift-iso : ∀ {i j} {G : Group i} → (G ≃ᴳ Lift-group {j = j} G)
lift-iso = (lift-hom , snd lift-equiv)

lower-iso : ∀ {i j} {G : Group i} → (Lift-group {j = j} G ≃ᴳ G)
lower-iso = (lower-hom , snd lower-equiv)


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

open import Categories.Category

module Categories.Diagram.Equalizer.Properties {o ℓ e} (C : Category o ℓ e) where

open import Categories.Diagram.Equalizer C
open import Categories.Morphism C
open import Categories.Morphism.Reasoning C

private
  module C = Category C
  open C

  variable
    X Y Z : Obj

module _ {f g : X ⇒ Y} (equalizer : Equalizer f g) where
  open Equalizer equalizer
  open HomReasoning

  equalizer-≈⇒≈ : ∀ {h} → arr ∘ h ≈ id → f ≈ g
  equalizer-≈⇒≈ {h} eq = begin
    f             ≈⟨ introʳ eq ⟩
    f ∘ arr ∘ h   ≈⟨ pullˡ equality ⟩
    (g ∘ arr) ∘ h ≈⟨ cancelʳ eq ⟩
    g             ∎


module Functors where

open import Library
open import Categories
open Cat

record Fun {a b c d} (C : Cat {a}{b})(D : Cat {c}{d}) : Set (a ⊔ b ⊔ c ⊔ d)
  where
  constructor functor
  field OMap  : Obj C → Obj D
        HMap  : ∀{X Y} → Hom C X Y → Hom D (OMap X) (OMap Y)
        fid   : ∀{X} → HMap (iden C {X}) ≅ iden D {OMap X}
        fcomp : ∀{X Y Z}{f : Hom C Y Z}{g : Hom C X Y} →
                HMap (comp C f g) ≅ comp D (HMap f) (HMap g)
open Fun

IdF : ∀{a b}(C : Cat {a}{b}) → Fun C C
IdF C = record{OMap = id;HMap = id;fid = refl;fcomp = refl}

_○_ : ∀{a b c d e f}{C : Cat {a}{b}}{D : Cat {c}{d}}{E : Cat {e}{f}} →
      Fun D E → Fun C D → Fun C E
_○_ {C = C}{D}{E} F G = record{
  OMap  = OMap F ∘ OMap G;
  HMap   = HMap F ∘ HMap G;
  fid    =
    proof
    HMap F (HMap G (iden C))
    ≅⟨ cong (HMap F) (fid G) ⟩
    HMap F (iden D)
    ≅⟨ fid F ⟩
    iden E
    ∎;
  fcomp  = λ {_}{_}{_}{f}{g} →
    proof
    HMap F (HMap G (comp C f g))
    ≅⟨ cong (HMap F) (fcomp G)  ⟩
    HMap F (comp D (HMap G f) (HMap G g))
    ≅⟨ fcomp F ⟩
    comp E (HMap F (HMap G f)) (HMap F (HMap G g))
    ∎}
infix 10 _○_

FunctorEq : ∀{a b c d}{C : Cat {a}{b}}{D : Cat {c}{d}}(F G : Fun C D) →
            OMap F ≅ OMap G →
            (λ {X Y} → HMap F {X}{Y}) ≅ (λ {X}{Y} → HMap G {X}{Y}) → F ≅ G
FunctorEq (functor fo fh _ _) (functor .fo .fh _ _) refl refl =
  cong₂ (functor fo fh)
        (iext λ _ → ir _ _)
        (iext λ _ → iext λ _ → iext λ _ → iext λ _ → iext λ _ → ir _ _)


import      Lvl
open import Type

module Type.Functions.Inverse {ℓₗ : Lvl.Level}{ℓₒ₁}{ℓₒ₂} {X : Type{ℓₒ₁}} {Y : Type{ℓₒ₂}} where

open import Function.Domains
open import Type.Functions {ℓₗ}{ℓₒ₁}{ℓₒ₂} {X}{Y}
open import Type.Properties.Empty
open import Type.Properties.Singleton {ℓₒ₁}{ℓₒ₂}

inv : (f : X → Y) → ⦃ _ : Bijective(f) ⦄ → (Y → X)
inv f ⦃ Bijective.intro(proof) ⦄ (y) with proof{y}
... | IsUnit.intro (Unapply.intro x) _ = x

invᵣ : (f : X → Y) → ⦃ _ : Surjective(f) ⦄ → (Y → X)
invᵣ f ⦃ Surjective.intro(proof) ⦄ (y) with proof{y}
... | ◊.intro ⦃ Unapply.intro x ⦄ = x


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.Monoid.Instances.NatPlusBis where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat

open import Cubical.Algebra.Monoid

open PlusBis

NatPlusBis-Monoid : Monoid ℓ-zero
fst NatPlusBis-Monoid = ℕ
MonoidStr.ε (snd NatPlusBis-Monoid) = 0
MonoidStr._·_ (snd NatPlusBis-Monoid) = _+'_
MonoidStr.isMonoid (snd NatPlusBis-Monoid) = makeIsMonoid isSetℕ +'-assoc +'-rid +'-lid


-- Proof: if we non-deterministically select an element
-- which is less-than-or-equal than all other elements,
-- such a result is the minimum of the list.
-- This holds for any non-deterministically selected element.

-- Basically, this is the translation of the Curry rule:
--
--   min-nd xs@(_++[x]++_) | all (x<=) xs = x
--

open import bool

module minlist-is-correct
  (Choice : Set)
  (choose : Choice → 𝔹)
  (lchoice : Choice → Choice)
  (rchoice : Choice → Choice)
 where

open import eq
open import bool-thms
open import bool-thms2
open import nat
open import nat-thms
open import list
open import maybe
open import inspect

----------------------------------------------------------------------
-- Some auxiliaries:

-- We define our own less-or-equal since the standard definition with
-- if-then-else produces too many branches:

_<=_ : ℕ → ℕ → 𝔹
0 <= y = tt
(suc x) <= 0 = ff
(suc x) <= (suc y) = x <= y

-- Some properties about less-or-equal:

<=-refl : ∀ (x : ℕ) → x <= x ≡ tt
<=-refl 0 = refl
<=-refl (suc x) = <=-refl x

<=-trans : ∀ (x y z : ℕ) → x <= y ≡ tt → y <= z ≡ tt → x <= z ≡ tt
<=-trans zero y z p1 p2 = refl
<=-trans (suc x) zero z p1 p2 = 𝔹-contra p1
<=-trans (suc x) (suc y) zero p1 p2 = 𝔹-contra p2
<=-trans (suc x) (suc y) (suc z) p1 p2 = <=-trans x y z p1 p2

<=-< : ∀ (x y : ℕ) → x <= y ≡ ff → y < x ≡ tt
<=-< zero x ()
<=-< (suc x) zero p = refl
<=-< (suc x) (suc y) p = <=-< x y p

<-<= : ∀ (x y : ℕ) → x < y ≡ tt → y <= x ≡ ff
<-<= x zero p rewrite <-0 x = 𝔹-contra p
<-<= zero (suc y) p = refl
<-<= (suc x) (suc y) p = <-<= x y p

<-<=-ff : ∀ (x y : ℕ) → x < y ≡ ff → y <= x ≡ tt
<-<=-ff zero zero p = refl
<-<=-ff zero (suc y) p = 𝔹-contra (sym p)
<-<=-ff (suc x) zero p = refl
<-<=-ff (suc x) (suc y) p = <-<=-ff x y p

<-<=-trans : ∀ (x y z : ℕ) → x < y ≡ tt → y <= z ≡ tt → x < z ≡ tt
<-<=-trans zero zero z p1 p2 = 𝔹-contra p1
<-<=-trans zero (suc y) zero p1 p2 = 𝔹-contra p2
<-<=-trans zero (suc y) (suc z) p1 p2 = refl
<-<=-trans (suc x) zero z p1 p2 = 𝔹-contra p1
<-<=-trans (suc x) (suc y) zero p1 p2 = 𝔹-contra p2
<-<=-trans (suc x) (suc y) (suc z) p1 p2 = <-<=-trans x y z p1 p2

----------------------------------------------------------------------
-- More lemmas about ordering relations:

leq-if : ∀ (x y z : ℕ)
      → y <= x && y <= z ≡ (if x <= z then y <= x else y <= z)
leq-if x y z with inspect (y <= x)
leq-if x y z | it tt p1 with inspect (x <= z)
... | it tt p2 rewrite p1 | p2 | <=-trans y x z p1 p2 = refl
... | it ff p2 rewrite p1 | p2 = refl
leq-if x y z | it ff p1 with inspect (x <= z)
... | it tt p2 rewrite p1 | p2 = refl
... | it ff p2 rewrite p1 | p2
           | <-<= z y (<-trans {z} {x} {y} (<=-< x z p2) (<=-< y x p1)) = refl

le-if : ∀ (x y z : ℕ)
     → y < x && y < z ≡ (if x <= z then y < x else y < z)
le-if x y z with inspect (y < x)
le-if x y z | it tt p1 with inspect (x <= z)
... | it tt p2 rewrite p1 | p2 | <-<=-trans y x z p1 p2 = refl
... | it ff p2 rewrite p1 | p2 = refl
le-if x y z | it ff p1 with inspect (x <= z)
... | it tt p2 rewrite p1 | p2 = refl
... | it ff p2 rewrite p1 | p2
         | <-asym {z} {y} (<-<=-trans z x y (<=-< x z p2) (<-<=-ff y x p1))
  = refl

----------------------------------------------------------------------
-- A lemma relating equality and orderings:

=ℕ-not-le : ∀ (m n : ℕ) → m =ℕ n  ≡ ~ (m < n) && m <= n
=ℕ-not-le zero zero = refl
=ℕ-not-le zero (suc m) = refl
=ℕ-not-le (suc n) zero = refl
=ℕ-not-le (suc n) (suc m) = =ℕ-not-le n m

----------------------------------------------------------------------
-- This is the translation of the Curry program:

-- Check whether all elements of a list satisfy a given predicate:
all : {A : Set} → (A → 𝔹) → 𝕃 A → 𝔹
all _ [] = tt
all p (x :: xs) = p x && all p xs

-- Deterministic min-d:
min-d : (l : 𝕃 ℕ) → is-empty l ≡ ff → ℕ
min-d []        ()
min-d (x :: []) _ = x
min-d (x :: y :: xs) _ =
  --if x <= min-d (y :: xs) refl then x else min-d (y :: xs) refl
  let z = min-d (y :: xs) refl
   in if x <= z then x else z

-- Select some element from a list:
select : {A : Set} → Choice → 𝕃 A -> maybe A
select _  []        = nothing
select ch (x :: xs) = if choose ch then just x
                                   else select (lchoice ch) xs

-- Select elements with a property from a list:
select-with : {A : Set} → Choice → (A → 𝔹) → 𝕃 A  → maybe A
select-with _  p [] = nothing
select-with ch p (x :: xs) =
  if choose ch then (if p x then just x else nothing)
               else select-with (lchoice ch) p xs

{-
-- more or less original definition:
min-nd : Choice → 𝕃 ℕ -> maybe ℕ
min-nd ch xs = (select ch xs) ≫=maybe
               (λ y → if all (_<=_ y) xs then just y else nothing)
-}

min-nd : Choice → (xs : 𝕃 ℕ) → maybe ℕ
min-nd ch xs = select-with ch (λ x → all (_<=_ x) xs) xs

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

-- Proof of the correctness of the operation select-with:
select-with-correct : ∀ {A : Set}
                   → (ch : Choice) (p : A → 𝔹) (xs : 𝕃 A) (z : A)
                   → select-with ch p xs ≡ just z → p z ≡ tt
select-with-correct ch p [] z ()
select-with-correct ch p (x :: xs) z u with choose ch
select-with-correct ch p (x :: xs) z u | tt with inspect (p x)
select-with-correct ch p (x :: xs) z u | tt | it tt v rewrite v | u | down-≡ u
 = v
select-with-correct ch p (x :: xs) z u | tt | it ff v rewrite v with u
select-with-correct ch p (x :: xs) z u | tt | it ff v | ()
select-with-correct ch p (x :: xs) z u | ff =
  select-with-correct (lchoice ch) p xs z u

----------------------------------------------------------------------
-- First step: if y smaller than all elements, y is smaller than the minimum:
all-leq-min : ∀ (y x : ℕ) (xs : 𝕃 ℕ)
           → all (_<=_ y) (x :: xs) ≡ y <= min-d (x :: xs) refl
all-leq-min y x [] = &&-tt (y <= x)
all-leq-min y x (z :: zs)
  rewrite all-leq-min y z zs
    | ite-arg (_<=_ y) (x <= min-d (z :: zs) refl) x (min-d (z :: zs) refl)
 = leq-if x y (min-d (z :: zs) refl)

-- Now we can prove:
-- if min-nd selects an element, it is smaller or equal than the minimum:
min-nd-select-min-d : ∀ (ch : Choice) (x : ℕ) (xs : 𝕃 ℕ) (z : ℕ)
      → min-nd ch (x :: xs) ≡ just z → z <= min-d (x :: xs) refl ≡ tt
min-nd-select-min-d ch x xs z u
 rewrite sym (all-leq-min z x xs)
       | select-with-correct ch (λ y → all (_<=_ y) (x :: xs)) (x :: xs) z u
 = refl

----------------------------------------------------------------------
-- Next step: if y smaller than all elements, y is smaller than the minimum:
all-less-min : ∀ (y x : ℕ) (xs : 𝕃 ℕ)
           → all (_<_ y) (x :: xs) ≡ y < min-d (x :: xs) refl
all-less-min y x [] rewrite &&-tt (y < x) = refl
all-less-min y x (z :: zs)
  rewrite all-less-min y z zs
    | ite-arg (_<_ y) (x <= min-d (z :: zs) refl) x (min-d (z :: zs) refl)
 = le-if x y (min-d (z :: zs) refl)

-- Next want to prove that the element selected by min-nd cannot be smaller
-- than the minimum.

-- For this purpose, we prove an auxiliary lemma:
-- If an element is selected from a list, it cannot be smaller than all elements
select-with-all<-ff : ∀ (ch : Choice) (p : ℕ → 𝔹) (xs : 𝕃 ℕ) (z : ℕ)
                   → select-with ch p xs ≡ just z → all (_<_ z) xs ≡ ff
select-with-all<-ff ch _ [] z ()
select-with-all<-ff ch p (x :: xs) z u with (choose ch)
select-with-all<-ff ch p (x :: xs) z u | tt with (p x)
select-with-all<-ff ch p (x :: xs) z u | tt | tt rewrite down-≡ u | <-irrefl z
  = refl
select-with-all<-ff ch p (x :: xs) z () | tt | ff
select-with-all<-ff ch p (x :: xs) z u | ff
  rewrite select-with-all<-ff (lchoice ch) p xs z u | &&-ff (z < x) = refl

-- Now we can prove: if min-nd selects an element, it cannot be smaller
-- than all other elements:
min-nd-select-all<-ff : ∀ (ch : Choice) (xs : 𝕃 ℕ) (z : ℕ)
      → min-nd ch xs ≡ just z → all (_<_ z) xs ≡ ff
min-nd-select-all<-ff ch xs z u
 rewrite select-with-all<-ff ch (λ y → all (_<=_ y) xs) xs z u  = refl

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

-- This is the main theorem:
min-nd-theorem : ∀ (ch : Choice) (x : ℕ) (xs : 𝕃 ℕ) (z : ℕ)
           → min-nd ch (x :: xs) ≡ just z → z =ℕ min-d (x :: xs) refl ≡ tt
min-nd-theorem ch x xs z u
 rewrite
   =ℕ-not-le z (min-d (x :: xs) refl)   -- split equality into no less and leq
 | min-nd-select-min-d ch x xs z u       -- min-nd selects leq min. elements
 | sym (all-less-min z x xs)             -- less-than min. elements satisfy all<
 | min-nd-select-all<-ff ch (x :: xs) z u -- min-nd can't select any all< element
 = refl

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


{-# OPTIONS --without-K --safe #-}
module Categories.Category.Instance.Cats where

-- The (large) category of (small) categories.
-- Even though Agda can figure out the levels, it is worth making them explicit,
-- to see the large level jumps involved.

open import Level
open import Categories.Category using (Category)
open import Categories.Functor using (Functor; id; _∘F_)
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (NaturalIsomorphism; associator; unitorˡ; unitorʳ; unitor²; isEquivalence; _ⓘₕ_; sym)
private
  variable
    o ℓ e : Level
    C D E : Category o ℓ e
    F G H I : Functor C D

Cats : ∀ o ℓ e → Category (suc (o ⊔ ℓ ⊔  e)) (o ⊔ ℓ ⊔ e) (o ⊔ ℓ ⊔ e)
Cats o ℓ e = record
  { Obj       = Category o ℓ e
  ; _⇒_       = Functor
  ; _≈_       = NaturalIsomorphism
  ; id        = id
  ; _∘_       = _∘F_
  ; assoc     = λ {_ _ _ _ F G H} → associator F G H
  ; sym-assoc = λ {_ _ _ _ F G H} → sym (associator F G H)
  ; identityˡ = unitorˡ
  ; identityʳ = unitorʳ
  ; identity² = unitor²
  ; equiv     = isEquivalence
  ; ∘-resp-≈  = _ⓘₕ_
  }


module AocVec where
open import Data.Vec as Vec
open import Data.Nat

addToBack : {A : Set} → {N : ℕ} → A → Vec A N → Vec A (suc N)
addToBack v [] = v ∷ []
addToBack v (x ∷ vs) = x ∷ addToBack v vs

dupFrontToBack : {A : Set} → {N : ℕ} → Vec A (suc N) → Vec A (suc (suc N))
dupFrontToBack (x ∷ vs) = addToBack x (x ∷ vs)

dupFirstNToBack : {A : Set} → {M : ℕ} → (N : ℕ) → Vec A (suc M) → Vec A (N + (suc M))
dupFirstNToBack zero vs = vs
dupFirstNToBack (suc N) (x ∷ vs) = x ∷ dupFirstNToBack N (addToBack x vs)


module Ual.Eq where

open import Agda.Primitive
open import Ual.Void

record Eq {a} (A : Set a) : Set (lsuc a) where
  infix 30 _==_
  infix 30 _≠_
  field
    _==_  : A → A → Set
    self  : {x : A} → x == x
    sym   : {x y : A} → x == y → y == x
    trans : {x y z : A} → x == y → y == z → x == z
  _≠_  : A → A → Set
  x ≠ y = ¬ (x == y)

open Eq ⦃ ... ⦄ public

data Equality {a} {A : Set a} ⦃ eqA : Eq A ⦄ : A → A → Set where
  equal    : {x y : A} → x == y → Equality x y
  notEqual : {x y : A} → x ≠ y → Equality x y

record Test {a} (A : Set a) : Set (lsuc a) where
  field
    ⦃ eqA ⦄ : Eq A
    test  : (x : A) → (y : A) → Equality x y


module Function.Multi where

open import Data
open import Data.Tuple
open import Data.Tuple.Raise
open import Data.Tuple.RaiseTypeᵣ
open import Functional
import      Lvl
import      Lvl.MultiFunctions as Lvl
open import Numeral.Natural
open import Type

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level
private variable n : ℕ

infixr 0 _⇉_

-- The type of a multivariate function (nested by repeated application of (_→_)) of different types and levels constructed by a tuple list of types.
-- This is useful when one needs a function of arbitrary length or by arbitrary argument types.
-- Essentially:
--   ((A,B,C,D,..) ⇉ R)
--   = (A → (B → (C → (D → (.. → R)))))
--   = (A → B → C → D → .. → R)
-- Example:
--   open import Syntax.Number
--   f : (Unit{0} , Unit{1} , Unit{2}) ⇉ Unit{3}
--   f <> <> <> = <>
_⇉_ : ∀{ℓ𝓈 : Lvl.Level ^ n} → Types(ℓ𝓈) → Type{ℓ} → Type{ℓ Lvl.⊔ (Lvl.⨆ ℓ𝓈)}
_⇉_ {n = 0}       _        B = B
_⇉_ {n = 1}       A        B = A → B
_⇉_ {n = 𝐒(𝐒(n))} (A , As) B = A → (As ⇉ B)
-- TODO: Does it work with Functional.swap(foldᵣ(_→ᶠ_)) ?

_⇉ᵢₘₚₗ_ : ∀{ℓ𝓈 : Lvl.Level ^ n} → Types(ℓ𝓈) → Type{ℓ} → Type{ℓ Lvl.⊔ (Lvl.⨆ ℓ𝓈)}
_⇉ᵢₘₚₗ_ {n = 0}       _        B = B
_⇉ᵢₘₚₗ_ {n = 1}       A        B = {A} → B
_⇉ᵢₘₚₗ_ {n = 𝐒(𝐒(n))} (A , As) B = {A} → (As ⇉ᵢₘₚₗ B)

_⇉ᵢₙₛₜ_ : ∀{ℓ𝓈 : Lvl.Level ^ n} → Types(ℓ𝓈) → Type{ℓ} → Type{ℓ Lvl.⊔ (Lvl.⨆ ℓ𝓈)}
_⇉ᵢₙₛₜ_ {n = 0}       _        B = B
_⇉ᵢₙₛₜ_ {n = 1}       A        B = ⦃ A ⦄ → B
_⇉ᵢₙₛₜ_ {n = 𝐒(𝐒(n))} (A , As) B = ⦃ A ⦄ → (As ⇉ᵢₙₛₜ B)


module Ag10 where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ)
open import Function using (_∘_)
open import Ag09 using (_≃_; _≲_; extensionality; _⇔_)
open Ag09.≃-Reasoning
open import Data.Product using (_×_; proj₁; proj₂) renaming (_,_ to ⟨_,_⟩)
open import Data.Unit using (⊤; tt)
open import Data.Sum using (_⊎_; inj₁; inj₂) renaming ([_,_] to case-⊎)
open import Data.Empty using (⊥; ⊥-elim)
import Function.Equivalence using (_⇔_)

⇔≃× : ∀ {A B : Set} → (A ⇔ B) ≃ ((A → B) × (B → A))
⇔≃× =
  record
  { to = λ{ x → ⟨ Ag09._⇔_.to x , Ag09._⇔_.from x ⟩}
  ; from = λ{ ⟨ x , y ⟩ → record { to = x ; from = y } }
  ; from∘to = λ{ x → refl }
  ; to∘from = λ{ x → refl }
  }

⊎-comm : ∀ {A B : Set} → (A ⊎ B) ≃ (B ⊎ A)
⊎-comm =
  record
  { to = λ{ (inj₁ x) → inj₂ x ; (inj₂ y) → inj₁ y }
  ; from =  λ{ (inj₁ x) → inj₂ x ; (inj₂ y) → inj₁ y}
  ; from∘to = λ{ (inj₁ x) → refl ; (inj₂ y) → refl }
  ; to∘from = λ{ (inj₁ x) → refl ; (inj₂ y) → refl}
  }

⊎-assoc : ∀ {A B C : Set} → (A ⊎ B) ⊎ C ≃ A ⊎ (B ⊎ C)
⊎-assoc =
  record
  { to = λ{ (inj₁ (inj₁ x)) → inj₁ x ; (inj₁ (inj₂ y)) → inj₂ (inj₁ y) ; (inj₂ z) → inj₂ (inj₂ z) }
  ; from = λ{ (inj₁ x) → inj₁ (inj₁ x) ; (inj₂ (inj₁ y)) → inj₁ (inj₂ y) ; (inj₂ (inj₂ z)) → inj₂ z }
  ; from∘to = λ{ (inj₁ (inj₁ x)) → refl ; (inj₁ (inj₂ y)) → refl ; (inj₂ z) → refl }
  ; to∘from = λ{ (inj₁ x) → refl ; (inj₂ (inj₁ y)) → refl ; (inj₂ (inj₂ z)) → refl }
  }

⊥-identityˡ : ∀ {A : Set} → ⊥ ⊎ A ≃ A
⊥-identityˡ =
    record
    { to = λ{ (inj₂ a) → a }
    ; from = λ{ a → (inj₂ a) }
    ; from∘to = λ{ (inj₂ a) → refl }
    ; to∘from = λ{ a → refl }
    }

⊥-identityʳ : ∀ {A : Set} → A ⊎ ⊥ ≃ A
⊥-identityʳ {A} =
    ≃-begin
      (A ⊎ ⊥)
    ≃⟨ ⊎-comm ⟩
      (⊥ ⊎ A)
    ≃⟨ ⊥-identityˡ ⟩
      A
    ≃-∎

⊎-weak-× : ∀ {A B C : Set} → (A ⊎ B) × C → A ⊎ (B × C)
⊎-weak-× ⟨ inj₁ x , snd ⟩ = inj₁ x
⊎-weak-× ⟨ inj₂ y , snd ⟩ = inj₂ ⟨ y , snd ⟩

⊎×-implies-×⊎ : ∀ {A B C D : Set} → (A × B) ⊎ (C × D) → (A ⊎ C) × (B ⊎ D)
⊎×-implies-×⊎ (inj₁ ⟨ fst , snd ⟩) = ⟨ inj₁ fst , inj₁ snd ⟩
⊎×-implies-×⊎ (inj₂ ⟨ fst , snd ⟩) = ⟨ inj₂ fst , inj₂ snd ⟩


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

module Categories.Category.Instance.Monoidals where

open import Level

open import Categories.Category
open import Categories.Category.Helper
open import Categories.Category.Monoidal
open import Categories.Functor.Monoidal
open import Categories.Functor.Monoidal.Properties
open import Categories.NaturalTransformation.NaturalIsomorphism

module _ o ℓ e where

  Monoidals : Category (suc (o ⊔ ℓ ⊔ e)) (o ⊔ ℓ ⊔ e) (o ⊔ ℓ ⊔ e)
  Monoidals = categoryHelper record
    { Obj       = MonoidalCategory o ℓ e
    ; _⇒_       = MonoidalFunctor
    ; _≈_       = λ F G → M.F F ≃ M.F G
    ; id        = idF-Monoidal _
    ; _∘_       = ∘-Monoidal
    ; assoc     = λ {_ _ _ _ F G H} → associator (M.F F) (M.F G) (M.F H)
    ; identityˡ = unitorˡ
    ; identityʳ = unitorʳ
    ; equiv     = record
      { refl  = ≃.refl
      ; sym   = ≃.sym
      ; trans = ≃.trans
      }
    ; ∘-resp-≈  = _ⓘₕ_
    }
    where module M = MonoidalFunctor

  StrongMonoidals : Category (suc (o ⊔ ℓ ⊔ e)) (o ⊔ ℓ ⊔ e) (o ⊔ ℓ ⊔ e)
  StrongMonoidals = categoryHelper record
    { Obj       = MonoidalCategory o ℓ e
    ; _⇒_       = StrongMonoidalFunctor
    ; _≈_       = λ F G → M.F F ≃ M.F G
    ; id        = idF-StrongMonoidal _
    ; _∘_       = ∘-StrongMonoidal
    ; assoc     = λ {_ _ _ _ F G H} → associator (M.F F) (M.F G) (M.F H)
    ; identityˡ = unitorˡ
    ; identityʳ = unitorʳ
    ; equiv     = record
      { refl  = ≃.refl
      ; sym   = ≃.sym
      ; trans = ≃.trans
      }
    ; ∘-resp-≈  = _ⓘₕ_
    }
    where module M = StrongMonoidalFunctor


module UniDB.Core where

open import Data.Nat public using (ℕ; suc; zero)
open import Data.Fin public using (Fin; suc; zero)
open import Function public using (id; _∘_)
open import Relation.Binary.PropositionalEquality as PropEq public
  using (_≡_; _≢_; refl; sym; cong; cong₂; trans; subst; module ≡-Reasoning)

open ≡-Reasoning public

Inj : {A B : Set} (f : A → B) → Set
Inj {A} f = {x y : A} → f x ≡ f y → x ≡ y

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

Dom : Set
Dom = ℕ

infixl 10 _∪_
_∪_ : Dom → Dom → Dom
γ ∪ zero  = γ
γ ∪ suc δ = suc (γ ∪ δ)

∪-assoc : (γ₁ γ₂ γ₃ : Dom) → γ₁ ∪ (γ₂ ∪ γ₃) ≡ (γ₁ ∪ γ₂) ∪ γ₃
∪-assoc γ₁ γ₂ zero     = refl
∪-assoc γ₁ γ₂ (suc γ₃) = cong suc (∪-assoc γ₁ γ₂ γ₃)

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

STX : Set₁
STX = Dom → Set

MOR : Set₁
MOR = Dom → Dom → Set

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

Ix : STX
Ix = Fin


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommRing.QuotientRing where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat
open import Cubical.Data.FinData

open import Cubical.HITs.SetQuotients as SQ renaming (_/_ to _/sq_)
open import Cubical.HITs.PropositionalTruncation as PT

open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.Ideal
open import Cubical.Algebra.CommRing.FGIdeal
open import Cubical.Algebra.Ring
open import Cubical.Algebra.Ring.QuotientRing renaming (_/_ to _/Ring_) hiding (asRing)

private
  variable
    ℓ ℓ' : Level

_/_ : (R : CommRing ℓ) → (I : IdealsIn R) → CommRing ℓ
R / I =
  fst asRing , commringstr _ _ _ _ _
                 (iscommring (RingStr.isRing (snd asRing))
                             (elimProp2 (λ _ _ → squash/ _ _)
                                        commEq))
   where
       asRing = (CommRing→Ring R) /Ring (CommIdeal→Ideal I)
       _·/_ : fst asRing → fst asRing → fst asRing
       _·/_ = RingStr._·_ (snd asRing)
       commEq : (x y : fst R) → ([ x ] ·/ [ y ]) ≡ ([ y ] ·/ [ x ])
       commEq x y i = [ CommRingStr.·Comm (snd R) x y i ]

[_]/ : {R : CommRing ℓ} {I : IdealsIn R} → (a : fst R) → fst (R / I)
[ a ]/ = [ a ]

--

module Quotient-FGideal-CommRing-Ring
  (A'@(A , Ar) : CommRing ℓ)
  (B'@(B , Br) : Ring ℓ')
  (g'@(g , gr) : RingHom (CommRing→Ring A') B')
  where

  open CommRingStr Ar using ()
    renaming
    ( 0r        to 0A
    ; 1r        to 1A
    ; _+_       to _+A_
    ; -_        to -A_
    ; _·_       to _·A_ )

  open RingStr Br using ()
    renaming
    ( 0r        to 0B
    ; 1r        to 1B
    ; _+_       to _+B_
    ; -_        to -B_
    ; _·_       to _·B_
    ; +Lid     to +BIdL
    ; is-set    to isSetB)

  open CommRingStr
  open IsRingHom

  module _
    {n : ℕ}
    (v : FinVec A n)
    (gnull : (k : Fin n) → g ( v k) ≡ 0B)
    where

    f : RingHom (CommRing→Ring (A' / (generatedIdeal _ v))) B'
    fst f = SQ.rec (isSetB)
            g
            λ a b → PT.rec (isSetB _ _)
                     λ x → g a                                   ≡⟨ cong g (sym (+Rid Ar a)) ⟩
                     g (a +A 0A)                                  ≡⟨ cong (λ X → g (a +A X)) (sym (snd (+Inv Ar b))) ⟩
                     g (a +A ((-A b) +A b))                       ≡⟨ cong g (+Assoc Ar a (-A b) b) ⟩
                     g ((a +A -A b) +A b)                         ≡⟨ pres+ gr (a +A -A b) b ⟩
                     (g(a +A -A b) +B g b)                        ≡⟨ cong (λ X → g X +B g b) (snd x) ⟩
                     (g (linearCombination A' (fst x) v) +B g b)  ≡⟨ cong (λ X → X +B g b) (cancelLinearCombination A' B' g' n (fst x) v gnull) ⟩
                     0B +B g b                                    ≡⟨ +BIdL (g b) ⟩
                     g b ∎
    snd f = makeIsRingHom
            (pres1 gr)
            (elimProp (λ x p q i y j → isSetB _ _ (p y) (q y) i j)
                      λ a → elimProp (λ _ → isSetB _ _)
                             λ a' → pres+ gr a a')
            (elimProp (λ x p q i y j → isSetB _ _ (p y) (q y) i j)
                      λ a → elimProp (λ _ → isSetB _ _)
                             λ a' → pres· gr a a')

module Quotient-FGideal-CommRing-CommRing
  (A'@(A , Ar) : CommRing ℓ)
  (B'@(B , Br) : CommRing ℓ')
  (g'@(g , gr) : CommRingHom A' B')
  {n : ℕ}
  (v : FinVec A n)
  (gnull : (k : Fin n) → g ( v k) ≡ CommRingStr.0r (snd B'))
  where

  f : CommRingHom (A' / (generatedIdeal _ v)) B'
  f = Quotient-FGideal-CommRing-Ring.f A' (CommRing→Ring B') g' v gnull


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

module TypeTheory.HoTT.Function.Properties where

-- agda-stdlib
open import Axiom.Extensionality.Propositional
open import Level

-- agda-misc
open import TypeTheory.HoTT.Base

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

module _ {a b} {A : Set a} {B : Set b} where
  isProp-→ : Extensionality a b → isProp B → isProp (A → B)
  isProp-→ ext B-isProp f g = ext λ x → B-isProp (f x) (g x)


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

open import lib.Basics
open import lib.types.Group
open import lib.types.Unit
open import lib.groups.Homomorphisms
open import lib.groups.Lift

module lib.groups.Unit where

Unit-group-structure : GroupStructure Unit
Unit-group-structure = record
  { ident = unit
  ; inv = λ _ → unit
  ; comp = λ _ _ → unit
  ; unitl = λ _ → idp
  ; unitr = λ _ → idp
  ; assoc = λ _ _ _ → idp
  ; invr = λ _ → idp
  ; invl = λ _ → idp
  }

Unit-group : Group lzero
Unit-group = group _ Unit-is-set Unit-group-structure

Lift-Unit-group : ∀ {i} → Group i
Lift-Unit-group = Lift-group Unit-group

0ᴳ = Lift-Unit-group

contr-is-0ᴳ : ∀ {i} (G : Group i) → is-contr (Group.El G) → G == 0ᴳ
contr-is-0ᴳ G pA = group-ua
  (group-hom (λ _ → lift unit) (λ _ _ → idp) , snd (contr-equiv-LiftUnit pA))

0ᴳ-hom-out-level : ∀ {i j} {G : Group i}
  → is-contr (0ᴳ {j} →ᴳ G)
0ᴳ-hom-out-level {G = G} =
  (cst-hom ,
   λ φ → hom= _ _ (λ= (λ {(lift unit) → ! (GroupHom.pres-ident φ)})))

0ᴳ-hom-in-level : ∀ {i j} {G : Group i}
  → is-contr (G →ᴳ 0ᴳ {j})
0ᴳ-hom-in-level {G = G} =
  (cst-hom , λ φ → hom= _ _ (λ= (λ _ → idp)))


{-# OPTIONS --injective-type-constructors #-}
module InjectiveTypeConstructors where

data D (A : Set) : Set where

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

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


module Prelude.Bool where

data Bool : Set where
  true  : Bool
  false : Bool

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

not : Bool -> Bool
not true  = false
not false = true

notnot : Bool -> Bool
notnot true  = not (not true)
notnot false = not (not false)

infix 90 if_then_else_
infix 90 if'_then_else_

if_then_else_ : ∀{ P : Bool -> Set} -> (b : Bool) -> P true -> P false -> P b
if true  then a else b = a
if false then a else b = b

if'_then_else_ : ∀{ P : Set} -> (b : Bool) -> P -> P  -> P
if' true  then a else b = a
if' false then a else b = b


module Common.Coinduction where

open import Common.Level

infix 1000 ♯_

postulate
  ∞  : ∀ {a} (A : Set a) → Set a
  ♯_ : ∀ {a} {A : Set a} → A → ∞ A
  ♭  : ∀ {a} {A : Set a} → ∞ A → A

{-# BUILTIN INFINITY ∞  #-}
{-# BUILTIN SHARP    ♯_ #-}
{-# BUILTIN FLAT     ♭  #-}

private

  my-♯ : ∀ {a} {A : Set a} → A → ∞ A
  my-♯ x = ♯ x


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

open import HoTT
open import cohomology.FunctionOver
open import cohomology.Theory

{- Cohomology groups are independent of basepoint, and the action of
 - the cohomology is independent of the basepoint-preservation path -}

module cohomology.BaseIndependence {i} (CT : CohomologyTheory i) where

open CohomologyTheory CT

C-base-indep : (n : ℤ) {A : Type i} (a₀ a₁ : A)
  → C n (A , a₀) ≃ᴳ C n (A , a₁)
C-base-indep n a₀ a₁ =
  C-Susp n (_ , a₁) ∘eᴳ (C-Susp n (_ , a₀))⁻¹ᴳ

CF-base-indep : (n : ℤ) {X Y : Ptd i}
  (f : fst X → fst Y) (p₁ p₂ : f (snd X) == snd Y)
  → CF-hom n (f , p₁) == CF-hom n (f , p₂)
CF-base-indep n {X} {Y} f p₁ p₂ = transport
  (λ q → CF-hom n (f , p₁) == CF-hom n (f , p₂) [ uncurry _→ᴳ_ ↓ q ])
  (!-inv-l (pair×= (group-ua (C-Susp n Y)) (group-ua (C-Susp n X))))
  (!ᵈ (C-Susp-↓ n (f , p₁)) ∙ᵈ C-Susp-↓ n (f , p₂))

CF-λ= : (n : ℤ) {X Y : Ptd i} {f g : fst (X ⊙→ Y)}
  → (∀ x → fst f x == fst g x)
  → CF-hom n f == CF-hom n g
CF-λ= n h = CF-base-indep n _ _ _ ∙ ap (CF-hom n) (⊙λ= h idp)

CF-↓dom= : (n : ℤ) {X Y Z : Ptd i}
  {f : fst (X ⊙→ Y)} {g : fst (X ⊙→ Z)} {p : Y == Z}
  → fst f == fst g [ (λ V → fst X → fst V) ↓ p ]
  → CF-hom n f == CF-hom n g [ (λ G → G →ᴳ C n X) ↓ ap (C n) p ]
CF-↓dom= n {p = idp} r =
  CF-base-indep n _ _ _ ∙ ap (CF-hom n) (pair= r (from-transp! _ _ idp))

CF-↓cod= : (n : ℤ) {X Y Z : Ptd i}
  {f : fst (X ⊙→ Z)} {g : fst (Y ⊙→ Z)} {p : X == Y}
  → fst f == fst g [ (λ U → fst U → fst Z) ↓ p ]
  → CF-hom n f == CF-hom n g [ (λ G → C n Z →ᴳ G) ↓ ap (C n) p ]
CF-↓cod= n {p = idp} r =
  CF-base-indep n _ _ _ ∙ ap (CF-hom n) (pair= r (from-transp! _ _ idp))


{-

This file contains:

- Properties of set truncations

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

open import Cubical.Core.Glue

open import Cubical.HITs.SetTruncation.Base

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels

private
  variable
    ℓ : Level
    A : Type ℓ

-- lemma 6.9.1 in HoTT book
elimSetTrunc : {B : ∥ A ∥₀ → Type ℓ} →
               (Bset : (x : ∥ A ∥₀) → isSet (B x)) →
               (g : (a : A) → B (∣ a ∣₀)) →
               (x : ∥ A ∥₀) → B x
elimSetTrunc Bset g ∣ a ∣₀ = g a
elimSetTrunc {A = A} {B = B} Bset g (squash₀ x y p q i j) =
  isOfHLevel→isOfHLevelDep {n = 2} Bset  (elimSetTrunc Bset g x) (elimSetTrunc Bset g y)
    (cong (elimSetTrunc Bset g) p) (cong (elimSetTrunc Bset g) q) (squash₀ x y p q) i j

setTruncUniversal : {B : Type ℓ} → (isSet B) → (∥ A ∥₀ → B) ≃ (A → B)
setTruncUniversal Bset = isoToEquiv (iso intro elim leftInv rightInv)
  where
  intro = (λ h a → h ∣ a ∣₀)
  elim = elimSetTrunc (λ x → Bset)

  leftInv : ∀ g → intro (elim g) ≡ g
  leftInv g = refl

  rightInv : ∀ h → elim (intro h) ≡ h
  rightInv h i x = elimSetTrunc (λ x → isProp→isSet (Bset (elim (intro h) x) (h x)))
                                (λ a → refl) x i

elimSetTrunc2 : {B : ∥ A ∥₀ → ∥ A ∥₀ → Type ℓ}
                (Bset : ((x y : ∥ A ∥₀) → isSet (B x y)))
                (g : (a b : A) → B ∣ a ∣₀ ∣ b ∣₀)
                (x y : ∥ A ∥₀) → B x y
elimSetTrunc2 Bset g = elimSetTrunc (λ _ → hLevelPi 2 (λ _ → Bset _ _)) (λ a →
                       elimSetTrunc (λ _ → Bset _ _) (λ b → g a b))

elimSetTrunc3 : {B : (x y z : ∥ A ∥₀) → Type ℓ}
                (Bset : ((x y z : ∥ A ∥₀) → isSet (B x y z)))
                (g : (a b c : A) → B ∣ a ∣₀ ∣ b ∣₀ ∣ c ∣₀)
                (x y z : ∥ A ∥₀) → B x y z
elimSetTrunc3 Bset g = elimSetTrunc2 (λ _ _ → hLevelPi 2 λ _ → Bset _ _ _) (λ a b →
                       elimSetTrunc (λ _ → Bset _ _ _) (λ c → g a b c))


module Prelude.Equality where

open import Prelude.Decidable

open import Agda.Builtin.Equality public

record Eq {a} (A : Set a) : Set a where
  infix 4 _==_
  field
    _==_ : (x y : A) → Dec (x ≡ y)

open Eq {{...}} public

sym : ∀ {a} {A : Set a} {x y : A} → x ≡ y → y ≡ x
sym refl = refl

infixr 0 _⟨≡⟩_ _⟨≡⟩ʳ_ _ʳ⟨≡⟩_ _ʳ⟨≡⟩ʳ_

trans : ∀ {a} {A : Set a} {x y z : A} → x ≡ y → y ≡ z → x ≡ z
trans refl eq = eq

_⟨≡⟩_ = trans

_⟨≡⟩ʳ_ : ∀ {a} {A : Set a} {x y z : A} → x ≡ y → z ≡ y → x ≡ z
eq ⟨≡⟩ʳ refl = eq

_ʳ⟨≡⟩_ : ∀ {a} {A : Set a} {x y z : A} → y ≡ x → y ≡ z → x ≡ z
refl ʳ⟨≡⟩ eq = eq

_ʳ⟨≡⟩ʳ_ : ∀ {a} {A : Set a} {x y z : A} → y ≡ x → z ≡ y → x ≡ z
refl ʳ⟨≡⟩ʳ refl = refl

infixl 4 cong _*≡_
syntax cong f eq = f $≡ eq
cong : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) {x y} → x ≡ y → f x ≡ f y
cong f refl = refl

_*≡_ : ∀ {a b} {A : Set a} {B : Set b} {f g : A → B} {x y} → f ≡ g → x ≡ y → f x ≡ g y
refl *≡ refl = refl

cong₂ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} (f : A → B → C) {x₁ x₂ y₁ y₂} →
        x₁ ≡ x₂ → y₁ ≡ y₂ → f x₁ y₁ ≡ f x₂ y₂
cong₂ f refl refl = refl

transport : ∀ {a b} {A : Set a} (B : A → Set b) {x y} → x ≡ y → B x → B y
transport B refl bx = bx

transport₂ : ∀ {a b c} {A : Set a} {B : Set b} (C : A → B → Set c) {x₁ x₂ y₁ y₂} → x₁ ≡ x₂ → y₁ ≡ y₂ → C x₁ y₁ → C x₂ y₂
transport₂ C refl refl cxy = cxy

-- Decidable equality helpers --

decEq₁ : ∀ {a b} {A : Set a} {B : Set b} {f : A → B} → (∀ {x y} → f x ≡ f y → x ≡ y) →
           ∀ {x y} → Dec (x ≡ y) → Dec (f x ≡ f y)
decEq₁ f-inj (yes refl) = yes refl
decEq₁ f-inj (no  neq)  = no λ eq → neq (f-inj eq)

decEq₂ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} {f : A → B → C} →
           (∀ {x y z w} → f x y ≡ f z w → x ≡ z) →
           (∀ {x y z w} → f x y ≡ f z w → y ≡ w) →
           ∀ {x y z w} → Dec (x ≡ y) → Dec (z ≡ w) → Dec (f x z ≡ f y w)
decEq₂ f-inj₁ f-inj₂ (no neq)    _         = no λ eq → neq (f-inj₁ eq)
decEq₂ f-inj₁ f-inj₂  _         (no neq)   = no λ eq → neq (f-inj₂ eq)
decEq₂ f-inj₁ f-inj₂ (yes refl) (yes refl) = yes refl

{-# INLINE decEq₁ #-}
{-# INLINE decEq₂ #-}

-- Equality reasoning --

infixr 0 eqReasoningStep eqReasoningStepʳ
infix  1 _∎

-- Giving the proofs in the reverse order means the values of x and y
-- are inferred before checking the x ≡ y proof. This leads to significant
-- performance improvements in some cases.

syntax eqReasoningStep x q p = x ≡⟨ p ⟩ q
eqReasoningStep : ∀ {a} {A : Set a} (x : A) {y z} → y ≡ z → x ≡ y → x ≡ z
x ≡⟨ refl ⟩ p = p

syntax eqReasoningStepʳ x q p = x ≡⟨ p ⟩ʳ q
eqReasoningStepʳ : ∀ {a} {A : Set a} (x : A) {y z} → y ≡ z → y ≡ x → x ≡ z
x ≡⟨ refl ⟩ʳ p = p

_∎ : ∀ {a} {A : Set a} (x : A) → x ≡ x
x ∎ = refl

-- Instances --

instance
  EqEq : ∀ {a} {A : Set a} {x y : A} → Eq (x ≡ y)
  _==_ {{EqEq}} refl refl = yes refl


{-

Finite Structures over Finite Set
  In short, the type of structures should be finite set itself.

This file contains:
- Definition and properties of finite sets equipped with finite structures;
- The type of finitely-structured finite sets is Rijke finite,
  so that we can count their number up to equivalence/isomorphism.

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

module Cubical.Data.FinType.FiniteStructure where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv renaming (_∙ₑ_ to _⋆_)

open import Cubical.HITs.PropositionalTruncation as Prop
open import Cubical.HITs.SetTruncation as Set

open import Cubical.Data.Nat
open import Cubical.Data.Sigma

open import Cubical.Data.FinSet
open import Cubical.Data.FinSet.Induction
open import Cubical.Data.FinSet.Cardinality
open import Cubical.Data.FinType
open import Cubical.Data.FinType.Sigma

private
  variable
    ℓ ℓ' : Level
    n : ℕ
    S : FinSet ℓ → FinSet ℓ'

-- type of finite sets with finite structure

FinSetWithStr : (S : FinSet ℓ → FinSet ℓ') → Type (ℓ-max (ℓ-suc ℓ) ℓ')
FinSetWithStr {ℓ = ℓ} S = Σ[ X ∈ FinSet ℓ ] S X .fst

-- type of finite sets with a fixed cardinal

FinSetOfCard : (ℓ : Level) (n : ℕ) → Type (ℓ-suc ℓ)
FinSetOfCard ℓ n = Σ[ X ∈ FinSet ℓ ] (card X ≡ n)

FinSetWithStrOfCard : (S : FinSet ℓ → FinSet ℓ') (n : ℕ) → Type (ℓ-max (ℓ-suc ℓ) ℓ')
FinSetWithStrOfCard {ℓ = ℓ} S n = Σ[ X ∈ FinSetOfCard ℓ n ] S (X .fst) .fst

FinSetOfCard≡ : (X Y : FinSetOfCard ℓ n) → (X .fst ≡ Y .fst) ≃ (X ≡ Y)
FinSetOfCard≡ _ _ = Σ≡PropEquiv (λ _ → isSetℕ _ _)

open Iso

∥FinSetOfCard∥₂≡ : (X Y : FinSetOfCard ℓ n) → ∥ X .fst ≡ Y .fst ∥ → ∣ X ∣₂ ≡ ∣ Y ∣₂
∥FinSetOfCard∥₂≡ _ _ =
  Prop.rec (squash₂ _ _) (λ p → PathIdTrunc₀Iso .inv ∣ FinSetOfCard≡ _ _ .fst p ∣)

isPathConnectedFinSetOfCard : isContr ∥ FinSetOfCard ℓ n ∥₂
isPathConnectedFinSetOfCard {n = n} .fst = ∣ 𝔽in n , card𝔽in n ∣₂
isPathConnectedFinSetOfCard {n = n} .snd =
  Set.elim (λ _ → isProp→isSet (squash₂ _ _)) (λ (X , p) → sym (∥FinSetOfCard∥₂≡ _ _ (card≡n p)))

isFinTypeFinSetOfCard : isFinType 1 (FinSetOfCard ℓ n)
isFinTypeFinSetOfCard .fst = isPathConnected→isFinType0 isPathConnectedFinSetOfCard
isFinTypeFinSetOfCard .snd X Y =
  isFinSet→isFinType 0 (EquivPresIsFinSet (FinSet≡ _ _ ⋆ FinSetOfCard≡ _ _) (isFinSetType≡Eff (X .fst) (Y .fst)))

-- the type of finitely-structured finite sets is Rijke finite
-- in particular, we can count their number up to equivalence

isFinTypeFinSetWithStrOfCard :
  (S : FinSet ℓ → FinSet ℓ') (n : ℕ)
  → isFinType 0 (FinSetWithStrOfCard S n)
isFinTypeFinSetWithStrOfCard S n =
  isFinTypeΣ {n = 0} (_ , isFinTypeFinSetOfCard) (λ X → _ , isFinSet→isFinType 0 (S (X .fst) .snd))


{-# 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.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+-Lid : (P : Poly A' n) → 0P Poly+ P ≡ P
  Poly+-Lid P = (Poly+-comm 0P P) ∙ (Poly+-Rid P)

  Poly-inv : Poly A' n → Poly A' n
  Poly-inv = Poly-Rec-Set.f A' n (Poly A' n) trunc
             0P
             (λ v a → base v (- a))
             (λ PS RS → PS Poly+ RS)
             Poly+-assoc
             Poly+-Rid
             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))

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

  Poly+-rinv : (P : Poly A' n ) → P Poly+ (Poly-inv P) ≡ 0P
  Poly+-rinv = Poly-Ind-Prop.f A' n (λ P → (P Poly+ Poly-inv P) ≡ 0P) (λ _ → trunc _ _)
               (Poly+-Rid 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+ ((Poly-inv P) Poly+ (Poly-inv Q)))
                                               ≡⟨ Poly-com-adv P Q (Poly-inv P) (Poly-inv Q) ⟩
                                      ((P Poly+ Poly-inv P) Poly+ (Q Poly+ Poly-inv Q))
                                               ≡⟨ cong₂ _Poly+_ ind-P ind-Q ⟩
                                      (0P Poly+ 0P)
                                               ≡⟨ Poly+-Rid 0P ⟩
                                      0P ∎

  Poly+-linv : (P  : Poly A' n) → (Poly-inv P) Poly+ P ≡ 0P
  Poly+-linv = Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
               (Poly+-Rid 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 (Poly-inv U) (Poly-inv V) U V ∙ cong₂ _Poly+_ ind-U ind-V ∙ Poly+-Rid 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+-Rid
                       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+-Rid (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+-Rid 0P) ))
                             -- End Right Induction
            λ v a b → funExt (
                             -- Induction Right Argument
                             Poly-Ind-Prop.f A' n _ (λ _ → trunc _ _)
                             (Poly+-Rid 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)

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

  0PRightAnnihilatesPoly : (P : Poly A' n) → P Poly* 0P ≡ 0P
  0PRightAnnihilatesPoly = 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+-Rid 0P)

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

  Poly*-Rid : (P : Poly A' n) → P Poly* 1P ≡ P
  Poly*-Rid = 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*-Lid : (P : Poly A' n) → 1P Poly* P ≡ P
  Poly*-Lid = 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*-Rdist : (P Q R : Poly A' n) → P Poly* (Q Poly+ R) ≡ (P Poly* Q) Poly+ (P Poly* R)
  Poly*-Rdist = 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+-Rid 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*-Ldist : (P Q R : Poly A' n) → (P Poly+ Q) Poly* R ≡ (P Poly* R) Poly+ (Q Poly* R)
  Poly*-Ldist 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 (0PRightAnnihilatesPoly 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*-Rdist Q U V))