{-# OPTIONS --without-K --exact-split #-}

module abelian-subgroups where

import abelian-groups
import subgroups
open abelian-groups public
open subgroups public

{- Subsets of abelian groups -}

subset-Ab :
  (l : Level) {l1 : Level} (A : Ab l1) → UU ((lsuc l) ⊔ l1)
subset-Ab l A = subset-Group l (group-Ab A)

is-set-subset-Ab :
  (l : Level) {l1 : Level} (A : Ab l1) → is-set (subset-Ab l A)
is-set-subset-Ab l A = is-set-subset-Group l (group-Ab A)

{- Defining subgroups -}

contains-zero-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) → UU l2
contains-zero-subset-Ab A = contains-unit-subset-Group (group-Ab A)

is-prop-contains-zero-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) →
  is-prop (contains-zero-subset-Ab A P)
is-prop-contains-zero-subset-Ab A =
  is-prop-contains-unit-subset-Group (group-Ab A)

closed-under-add-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) → UU (l1 ⊔ l2)
closed-under-add-subset-Ab A =
  closed-under-mul-subset-Group (group-Ab A)

is-prop-closed-under-add-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) →
  is-prop (closed-under-add-subset-Ab A P)
is-prop-closed-under-add-subset-Ab A =
  is-prop-closed-under-mul-subset-Group (group-Ab A)

closed-under-neg-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) → UU (l1 ⊔ l2)
closed-under-neg-subset-Ab A =
  closed-under-inv-subset-Group (group-Ab A)

is-prop-closed-under-neg-subset-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) →
  is-prop (closed-under-neg-subset-Ab A P)
is-prop-closed-under-neg-subset-Ab A =
  is-prop-closed-under-inv-subset-Group (group-Ab A)

is-subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) → UU (l1 ⊔ l2)
is-subgroup-Ab A = is-subgroup-Group (group-Ab A)

is-prop-is-subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : subset-Ab l2 A) →
  is-prop (is-subgroup-Ab A P)
is-prop-is-subgroup-Ab A = is-prop-is-subgroup-Group (group-Ab A)

{- Introducing the type of all subgroups of a group G -}

Subgroup-Ab :
  (l : Level) {l1 : Level} (A : Ab l1) → UU ((lsuc l) ⊔ l1)
Subgroup-Ab l A = Subgroup l (group-Ab A)

subset-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) →
  ( Subgroup-Ab l2 A) → ( subset-Ab l2 A)
subset-Subgroup-Ab A = subset-Subgroup (group-Ab A)

is-emb-subset-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) → is-emb (subset-Subgroup-Ab {l2 = l2} A)
is-emb-subset-Subgroup-Ab A = is-emb-subset-Subgroup (group-Ab A)

type-subset-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  (type-Ab A → UU l2)
type-subset-Subgroup-Ab A = type-subset-Subgroup (group-Ab A)

is-prop-type-subset-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  (x : type-Ab A) → is-prop (type-subset-Subgroup-Ab A P x)
is-prop-type-subset-Subgroup-Ab A =
  is-prop-type-subset-Subgroup (group-Ab A)

is-subgroup-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  is-subgroup-Ab A (subset-Subgroup-Ab A P)
is-subgroup-Subgroup-Ab A = is-subgroup-Subgroup (group-Ab A)

contains-zero-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  contains-zero-subset-Ab A (subset-Subgroup-Ab A P)
contains-zero-Subgroup-Ab A = contains-unit-Subgroup (group-Ab A)

closed-under-add-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  closed-under-add-subset-Ab A (subset-Subgroup-Ab A P)
closed-under-add-Subgroup-Ab A = closed-under-mul-Subgroup (group-Ab A)

closed-under-neg-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  closed-under-neg-subset-Ab A (subset-Subgroup-Ab A P)
closed-under-neg-Subgroup-Ab A = closed-under-inv-Subgroup (group-Ab A)

{- Given a subgroup of an abelian group, we construct an abelian group -}

type-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) → UU (l1 ⊔ l2)
type-ab-Subgroup-Ab A =  type-group-Subgroup (group-Ab A)

incl-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  type-ab-Subgroup-Ab A P → type-Ab A
incl-ab-Subgroup-Ab A = incl-group-Subgroup (group-Ab A)

is-emb-incl-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  is-emb (incl-ab-Subgroup-Ab A P)
is-emb-incl-ab-Subgroup-Ab A = is-emb-incl-group-Subgroup (group-Ab A)

eq-subgroup-ab-eq-ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  {x y : type-ab-Subgroup-Ab A P} →
  Id (incl-ab-Subgroup-Ab A P x) (incl-ab-Subgroup-Ab A P y) → Id x y
eq-subgroup-ab-eq-ab A =
  eq-subgroup-eq-group (group-Ab A)

set-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) → Subgroup-Ab l2 A → UU-Set (l1 ⊔ l2)
set-ab-Subgroup-Ab A = set-group-Subgroup (group-Ab A)

zero-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) → type-ab-Subgroup-Ab A P
zero-ab-Subgroup-Ab A = unit-group-Subgroup (group-Ab A)

add-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x y : type-ab-Subgroup-Ab A P) → type-ab-Subgroup-Ab A P
add-ab-Subgroup-Ab A = mul-group-Subgroup (group-Ab A)

neg-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  type-ab-Subgroup-Ab A P → type-ab-Subgroup-Ab A P
neg-ab-Subgroup-Ab A = inv-group-Subgroup (group-Ab A)

is-associative-add-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x y z : type-ab-Subgroup-Ab A P) →
  Id (add-ab-Subgroup-Ab A P (add-ab-Subgroup-Ab A P x y) z)
     (add-ab-Subgroup-Ab A P x (add-ab-Subgroup-Ab A P y z))
is-associative-add-ab-Subgroup-Ab A =
  is-associative-mul-group-Subgroup (group-Ab A)

left-zero-law-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x : type-ab-Subgroup-Ab A P) →
  Id (add-ab-Subgroup-Ab A P (zero-ab-Subgroup-Ab A P) x) x
left-zero-law-ab-Subgroup-Ab A =
  left-unit-law-group-Subgroup (group-Ab A)

right-zero-law-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x : type-ab-Subgroup-Ab A P) →
  Id (add-ab-Subgroup-Ab A P x (zero-ab-Subgroup-Ab A P)) x
right-zero-law-ab-Subgroup-Ab A =
  right-unit-law-group-Subgroup (group-Ab A)

left-neg-law-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x : type-ab-Subgroup-Ab A P) →
  Id ( add-ab-Subgroup-Ab A P (neg-ab-Subgroup-Ab A P x) x)
     ( zero-ab-Subgroup-Ab A P)
left-neg-law-ab-Subgroup-Ab A =
  left-inverse-law-group-Subgroup (group-Ab A)

right-neg-law-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x : type-ab-Subgroup-Ab A P) →
  Id ( add-ab-Subgroup-Ab A P x (neg-ab-Subgroup-Ab A P x))
     ( zero-ab-Subgroup-Ab A P)
right-neg-law-ab-Subgroup-Ab A = right-inverse-law-group-Subgroup (group-Ab A)

is-commutative-add-ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  ( x y : type-ab-Subgroup-Ab A P) →
  Id ( add-ab-Subgroup-Ab A P x y) (add-ab-Subgroup-Ab A P y x)
is-commutative-add-ab-Subgroup-Ab A P (pair x p) (pair y q) =
  eq-subgroup-ab-eq-ab A P (is-commutative-add-Ab A x y)

ab-Subgroup-Ab :
  {l1 l2 : Level} (A : Ab l1) → Subgroup-Ab l2 A → Ab (l1 ⊔ l2)
ab-Subgroup-Ab A P =
  pair
    (group-Subgroup (group-Ab A) P) (is-commutative-add-ab-Subgroup-Ab A P)

{- We show that the inclusion from ab-Subgroup-Ab A P → A is a group
   homomorphism -}

preserves-add-incl-ab-Subgroup-Ab :
  { l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  preserves-add (ab-Subgroup-Ab A P) A (incl-ab-Subgroup-Ab A P)
preserves-add-incl-ab-Subgroup-Ab A =
  preserves-mul-incl-group-Subgroup (group-Ab A)

hom-ab-Subgroup-Ab :
  { l1 l2 : Level} (A : Ab l1) (P : Subgroup-Ab l2 A) →
  hom-Ab (ab-Subgroup-Ab A P) A
hom-ab-Subgroup-Ab A = hom-group-Subgroup (group-Ab A)

{- We define another type of subgroups of A as the type of group inclusions -}

emb-Ab :
  { l1 l2 : Level} (A : Ab l1) (B : Ab l2) → UU (l1 ⊔ l2)
emb-Ab A B = emb-Group (group-Ab A) (group-Ab B)

emb-Ab-Slice :
  (l : Level) {l1 : Level} (A : Ab l1) → UU ((lsuc l) ⊔ l1)
emb-Ab-Slice l A = emb-Group-Slice l (group-Ab A)

emb-ab-slice-Subgroup-Ab :
  { l1 l2 : Level} (A : Ab l1) →
  Subgroup-Ab l2 A → emb-Ab-Slice (l1 ⊔ l2) A
emb-ab-slice-Subgroup-Ab A = emb-group-slice-Subgroup (group-Ab A)


module SystemF.WellTyped where

open import Prelude hiding (id; erase)

open import SystemF.Syntax public
open import SystemF.Substitutions
open import Data.Vec hiding ([_])
open import Data.Vec.Properties
open import Data.Product

infix 4 _⊢_∈_
data _⊢_∈_ {ν n} (Γ : Ctx ν n) : Term ν n → Type ν → Set where
  var : (x : Fin n) → Γ ⊢ var x ∈ lookup x Γ
  Λ   : ∀ {t a} → (ctx-weaken Γ) ⊢ t ∈ a → Γ ⊢ Λ t ∈ ∀' a
  λ'  : ∀ {t b} → (a : Type ν) → a ∷ Γ ⊢ t ∈ b → Γ ⊢ λ' a t ∈ a →' b
  _[_] : ∀ {t a} → Γ ⊢ t ∈ ∀' a → (b : Type ν) → Γ ⊢ t [ b ] ∈ a tp[/tp b ]
  _·_  : ∀ {f t a b} → Γ ⊢ f ∈ (a →' b) → Γ ⊢ t ∈ a → Γ ⊢ f · t ∈ b

_⊢_∉_ : ∀ {ν n} → (Γ : Ctx ν n) → Term ν n → Type ν → Set
_⊢_∉_ Γ t τ = ¬ Γ ⊢ t ∈ τ

erase : ∀ {ν n} {Γ : Ctx ν n} {t τ} → Γ ⊢ t ∈ τ → Term ν n
erase (var x) = var x
erase (Λ {t} x) = Λ t
erase (λ' {t} a x) = λ' a t
erase (_[_] {t} x b) = t
erase (_·_ {f} x x₁) = f

⊢f·a-inversion : ∀ {ν n f t b} {Γ : Ctx ν n} → Γ ⊢ f · t ∈ b →
                 ∃ λ a → Γ ⊢ f ∈ a →' b × Γ ⊢ t ∈ a
⊢f·a-inversion (_·_ f∈a→b t∈a) = , (f∈a→b , t∈a)

⊢tc[a]-inversion : ∀ {ν n tc a' b} {Γ : Ctx ν n} → Γ ⊢ tc [ b ] ∈ a' → ∃ λ a → Γ ⊢ tc ∈ ∀' a
⊢tc[a]-inversion (_[_] tc∈∀'a b) = , tc∈∀'a

unique-type : ∀ {ν n} {Γ : Ctx ν n} {t τ τ'} → Γ ⊢ t ∈ τ → Γ ⊢ t ∈ τ' → τ ≡ τ'
unique-type (var x) (var .x) = refl
unique-type (Λ l) (Λ r) = cong ∀' (unique-type l r)
unique-type (λ' a l) (λ' .a r) = cong (λ b → a →' b) (unique-type l r)
unique-type (l [ b ]) (r [ .b ]) = cong (λ{ (∀' fa) → fa tp[/tp b ]; a → a}) (unique-type l r)
unique-type (f · e) (f' · e') = cong (λ{ (a →' b) → b; a → a }) (unique-type f f')

unique-type′ : ∀ {ν n} {Γ : Ctx ν n} {t τ τ'} → Γ ⊢ t ∈ τ → τ ≢ τ' → Γ ⊢ t ∉ τ'
unique-type′ ⊢t∈τ neq ⊢t∈τ' = neq $ unique-type ⊢t∈τ ⊢t∈τ'

-- Collections of typing derivations for well-typed terms.
data _⊢ⁿ_∈_ {m n} (Γ : Ctx n m) :
  ∀ {k} → Vec (Term n m) k → Vec (Type n) k → Set where
    []  : Γ ⊢ⁿ [] ∈ []
    _∷_ : ∀ {t a k} {ts : Vec (Term n m) k} {as : Vec (Type n) k} →
          Γ ⊢ t ∈ a → Γ ⊢ⁿ ts ∈ as → Γ ⊢ⁿ t ∷ ts ∈ (a ∷ as)

-- Lookup a well-typed term in a collection thereof.
lookup-⊢ : ∀ {m n k} {Γ : Ctx n m} {ts : Vec (Term n m) k}
             {as : Vec (Type n) k} →
           (x : Fin k) → Γ ⊢ⁿ ts ∈ as → Γ ⊢ lookup x ts ∈ lookup x as
lookup-⊢ zero    (⊢t ∷ ⊢ts) = ⊢t
lookup-⊢ (suc x) (⊢t ∷ ⊢ts) = lookup-⊢ x ⊢ts


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly-notationZ where

open import Cubical.Foundations.Prelude

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

open import Cubical.Relation.Nullary

open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.FGIdeal
open import Cubical.Algebra.CommRing.QuotientRing
open import Cubical.Algebra.CommRing.Instances.Int

open import Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly
     renaming (PolyCommRing to A[X1,···,Xn] ; Poly to A[x1,···,xn])
open import Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly-Quotient

-- Notations for ℤ polynomial rings

ℤ[X] : CommRing ℓ-zero
ℤ[X] = A[X1,···,Xn] ℤCommRing 1

ℤ[x] : Type ℓ-zero
ℤ[x] = fst ℤ[X]

ℤ[X,Y] : CommRing ℓ-zero
ℤ[X,Y] = A[X1,···,Xn] ℤCommRing 2

ℤ[x,y] : Type ℓ-zero
ℤ[x,y] = fst ℤ[X,Y]

ℤ[X,Y,Z] : CommRing ℓ-zero
ℤ[X,Y,Z] = A[X1,···,Xn] ℤCommRing 3

ℤ[x,y,z] : Type ℓ-zero
ℤ[x,y,z] = fst ℤ[X,Y,Z]

ℤ[X1,···,Xn] : (n : ℕ) → CommRing ℓ-zero
ℤ[X1,···,Xn] n = A[X1,···,Xn] ℤCommRing n

ℤ[x1,···,xn] : (n : ℕ) → Type ℓ-zero
ℤ[x1,···,xn] n = fst (ℤ[X1,···,Xn] n)

-- Notation for quotiented ℤ polynomial ring

<X> : FinVec ℤ[x] 1
<X> = <Xkʲ> ℤCommRing 1 0 1

<X²> : FinVec ℤ[x] 1
<X²> = <Xkʲ> ℤCommRing 1 0 2

<X³> : FinVec ℤ[x] 1
<X³> = <Xkʲ> ℤCommRing 1 0 3

<Xᵏ> : (k  : ℕ) → FinVec ℤ[x] 1
<Xᵏ> k = <Xkʲ> ℤCommRing 1 0 k

ℤ[X]/X : CommRing ℓ-zero
ℤ[X]/X = A[X1,···,Xn]/<Xkʲ> ℤCommRing 1 0 1

ℤ[x]/x : Type ℓ-zero
ℤ[x]/x = fst ℤ[X]/X

ℤ[X]/X² : CommRing ℓ-zero
ℤ[X]/X² = A[X1,···,Xn]/<Xkʲ> ℤCommRing 1 0 2

ℤ[x]/x² : Type ℓ-zero
ℤ[x]/x² = fst ℤ[X]/X²

ℤ[X]/X³ : CommRing ℓ-zero
ℤ[X]/X³ = A[X1,···,Xn]/<Xkʲ> ℤCommRing 1 0 3

ℤ[x]/x³ : Type ℓ-zero
ℤ[x]/x³ = fst ℤ[X]/X³

ℤ[X1,···,Xn]/<X1,···,Xn> : (n : ℕ) → CommRing ℓ-zero
ℤ[X1,···,Xn]/<X1,···,Xn> n = A[X1,···,Xn]/<X1,···,Xn> ℤCommRing n

ℤ[x1,···,xn]/<x1,···,xn> : (n : ℕ) → Type ℓ-zero
ℤ[x1,···,xn]/<x1,···,xn> n = fst (ℤ[X1,···,Xn]/<X1,···,Xn> n)

-- Warning there is two possible definitions of ℤ[X]
-- they only holds up to a path

ℤ'[X]/X : CommRing ℓ-zero
ℤ'[X]/X = A[X1,···,Xn]/<X1,···,Xn> ℤCommRing 1

-- there is a unification problem that keep pop in up everytime I modify something
-- equivℤ[X] : ℤ'[X]/X ≡ ℤ[X]/X
-- equivℤ[X] = cong₂ _/_ refl (cong (λ X → genIdeal (A[X1,···,Xn] ℤCommRing {!!}) X) {!!})


------------------------------------------------------------------------------
-- Properties of the divisibility relation
------------------------------------------------------------------------------

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

module FOTC.Data.Nat.Divisibility.NotBy0.PropertiesATP where

open import FOTC.Base
open import FOTC.Base.PropertiesATP
open import FOTC.Data.Nat
open import FOTC.Data.Nat.Divisibility.NotBy0
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.PropertiesATP
open import FOTC.Data.Nat.PropertiesATP

------------------------------------------------------------------------------
-- Any positive number divides 0.
postulate S∣0 : ∀ n → succ₁ n ∣ zero
{-# ATP prove S∣0 #-}

-- 0 doesn't divide any number.
postulate 0∤x : ∀ {n} → ¬ (zero ∣ n)
{-# ATP prove 0∤x #-}

-- The divisibility relation is reflexive for positive numbers.
--
-- For the proof using the ATPs we added the helper hypothesis
--
-- N (succ zero).
postulate ∣-refl-S-ah : ∀ {n} → N n → N (succ₁ zero) → succ₁ n ∣ succ₁ n
{-# ATP prove ∣-refl-S-ah *-leftIdentity #-}

∣-refl-S : ∀ {n} → N n → succ₁ n ∣ succ₁ n
∣-refl-S Nn = ∣-refl-S-ah Nn (nsucc nzero)

-- If x divides y and z then x divides y ∸ z.
postulate
  x∣y→x∣z→x∣y∸z-helper : ∀ {m n o k k'} → N m → N k → N k' →
                         n ≡ k * succ₁ m →
                         o ≡ k' * succ₁ m →
                         n ∸ o ≡ (k ∸ k') * succ₁ m
{-# ATP prove x∣y→x∣z→x∣y∸z-helper *∸-leftDistributive #-}

x∣y→x∣z→x∣y∸z : ∀ {m n o} → N m → N n → N o → m ∣ n → m ∣ o → m ∣ n ∸ o
x∣y→x∣z→x∣y∸z nzero _ _ (0≢0 , _) m∣o = ⊥-elim (0≢0 refl)
x∣y→x∣z→x∣y∸z (nsucc Nm) Nn No
              (_ , k , Nk , h₁)
              (_ , k' , Nk' , h₂) =
  (λ S≡0 → ⊥-elim (S≢0 S≡0))
  , k ∸ k' , ∸-N Nk Nk' , x∣y→x∣z→x∣y∸z-helper Nm Nk Nk' h₁ h₂

-- If x divides y and z then x divides y + z.
postulate
  x∣y→x∣z→x∣y+z-helper : ∀ {m n o k k'} → N m → N k → N k' →
                         n ≡ k * succ₁ m →
                         o ≡ k' * succ₁ m →
                         n + o ≡ (k + k') * succ₁ m
{-# ATP prove x∣y→x∣z→x∣y+z-helper *+-leftDistributive #-}

x∣y→x∣z→x∣y+z : ∀ {m n o} → N m → N n → N o → m ∣ n → m ∣ o → m ∣ n + o
x∣y→x∣z→x∣y+z nzero      _  _ (0≢0 , _) m∣o = ⊥-elim (0≢0 refl)
x∣y→x∣z→x∣y+z (nsucc Nm) Nn No
              (_ , k , Nk , h₁)
              (_ , k' , Nk' , h₂) =
  (λ S≡0 → ⊥-elim (S≢0 S≡0))
  , k + k' , +-N Nk Nk' , x∣y→x∣z→x∣y+z-helper Nm Nk Nk' h₁ h₂

-- If x divides y and y is positive, then x ≤ y.
postulate x∣S→x≤S-ah₁ : ∀ {m n} → succ₁ n ≡ zero * succ₁ m → ⊥
{-# ATP prove x∣S→x≤S-ah₁ #-}

-- Nice proof by the ATP.
postulate x∣S→x≤S-ah₂ : ∀ {m n o} → N m → N n → N o →
                        succ₁ n ≡ succ₁ o * succ₁ m →
                        succ₁ m ≤ succ₁ n
{-# ATP prove x∣S→x≤S-ah₂ x≤x+y *-N #-}

x∣S→x≤S : ∀ {m n} → N m → N n → m ∣ (succ₁ n) → m ≤ succ₁ n
x∣S→x≤S  nzero Nn (0≢0 , _) = ⊥-elim (0≢0 refl)
x∣S→x≤S (nsucc Nm) Nn (_ , .zero , nzero , Sn≡0*Sm) = ⊥-elim (x∣S→x≤S-ah₁ Sn≡0*Sm)
x∣S→x≤S (nsucc {m} Nm) Nn (_ , .(succ₁ k) , nsucc {k} Nk , Sn≡Sk*Sm) =
  x∣S→x≤S-ah₂ Nm Nn Nk Sn≡Sk*Sm


module Numeral.Natural.Oper.Proofs where

import Lvl
open import Functional
open import Logic
open import Logic.Propositional
open import Logic.Predicate
open import Numeral.Natural
open import Numeral.Natural.Oper
open import Numeral.Natural.Induction
open import Relator.Equals
open import Relator.Equals.Proofs
open import Structure.Function
open import Structure.Function.Domain
open import Structure.Operator
open import Structure.Operator.Proofs.Util
open import Structure.Operator.Properties
import      Structure.Operator.Names as Names
open import Structure.Relator.Properties
open import Syntax.Transitivity

-- TODO: For old code
open import Numeral.Natural.Proofs public
open import Numeral.Natural.Oper.Proofs.Rewrite public

instance
  [+]-identityₗ : Identityₗ(_+_)(0)
  Identityₗ.proof([+]-identityₗ) = [+]-baseₗ

instance
  [+]-identityᵣ : Identityᵣ(_+_)(0)
  Identityᵣ.proof([+]-identityᵣ) {x} = ℕ-elim [≡]-intro (x ↦ [≡]-with(𝐒) {x + 𝟎}{x}) x

instance
  [+]-identity : Identity (_+_) (0)
  [+]-identity = intro

instance
  [+]-associativity : Associativity(_+_)
  Associativity.proof([+]-associativity) {x}{y}{z} = ℕ-elim [≡]-intro (i ↦ [≡]-with(𝐒) {(x + y) + i} {x + (y + i)}) z

instance
  [+]-commutativity : Commutativity (_+_)
  Commutativity.proof([+]-commutativity) {x}{y} = ℕ-elim base next y where
    base = identityᵣ(_+_)(𝟎) 🝖 symmetry(_≡_) (identityₗ(_+_)(𝟎))
    next = \i eq → ([≡]-with(𝐒) {x + i}{i + x} eq) 🝖 symmetry(_≡_) ([+]-stepₗ {i}{x})

[+1]-and-[𝐒] : ∀{x : ℕ} → (x + 1 ≡ 𝐒(x))
[+1]-and-[𝐒] {x} = [≡]-intro

[1+]-and-[𝐒] : ∀{x : ℕ} → (1 + x ≡ 𝐒(x))
[1+]-and-[𝐒] {x} = [+1]-and-[𝐒] {x} 🝖 commutativity(_+_) {x}{1}

[⋅]-absorberₗ-raw : Names.Absorberₗ(_⋅_)(0)
[⋅]-absorberₗ-raw {x} = ℕ-elim [≡]-intro (\i → [≡]-with(0 +_) {0 ⋅ i}{0}) x
{-# REWRITE [⋅]-absorberₗ-raw #-}
instance
  [⋅]-absorberₗ : Absorberₗ(_⋅_)(0)
  Absorberₗ.proof([⋅]-absorberₗ) {x} = [⋅]-absorberₗ-raw {x}

instance
  [⋅]-absorberᵣ : Absorberᵣ(_⋅_)(0)
  Absorberᵣ.proof([⋅]-absorberᵣ) {x} = [≡]-intro

instance
  [⋅]-absorber : Absorber(_⋅_)(0)
  [⋅]-absorber = intro

[⋅]-identityₗ-raw : Names.Identityₗ(_⋅_)(1)
[⋅]-identityₗ-raw {x} = ℕ-elim [≡]-intro (\i eq → (commutativity(_+_) {1} {1 ⋅ i}) 🝖 ([≡]-with(𝐒) {_}{i} eq)) x
{-# REWRITE [⋅]-identityₗ-raw #-}
instance
  [⋅]-identityₗ : Identityₗ(_⋅_)(1)
  Identityₗ.proof([⋅]-identityₗ) {x} = [⋅]-identityₗ-raw {x}

instance
  [⋅]-identityᵣ : Identityᵣ(_⋅_)(1)
  Identityᵣ.proof([⋅]-identityᵣ) = [≡]-intro

instance
  [⋅]-identity : Identity(_⋅_)(1)
  [⋅]-identity = intro

instance
  [⋅][+]-distributivityᵣ : Distributivityᵣ(_⋅_)(_+_)
  Distributivityᵣ.proof([⋅][+]-distributivityᵣ) {x}{y}{z} = ℕ-elim [≡]-intro next z where
    next : ∀(z : ℕ) → ((x + y) ⋅ z) ≡ ((x ⋅ z) + (y ⋅ z)) → ((x + y) ⋅ 𝐒(z)) ≡ ((x ⋅ 𝐒(z)) + (y ⋅ 𝐒(z)))
    next z proof = ([≡]-with((x + y) +_) proof) 🝖 (One.associate-commute4 {a = x}{y}{x ⋅ z}{y ⋅ z} (commutativity(_+_){x = y}))

[⋅]-with-[𝐒]ₗ : ∀{x y} → (𝐒(x) ⋅ y ≡ (x ⋅ y) + y)
[⋅]-with-[𝐒]ₗ {x}{y} = (distributivityᵣ(_⋅_)(_+_) {x}{1}{y}) 🝖 ([≡]-with(expr ↦ (x ⋅ y) + expr) ([⋅]-identityₗ-raw {y}))

[⋅]-with-[𝐒]ᵣ : ∀{x y} → (x ⋅ 𝐒(y) ≡ x + (x ⋅ y))
[⋅]-with-[𝐒]ᵣ = [≡]-intro

instance
  [⋅][+]-distributivityₗ : Distributivityₗ(_⋅_)(_+_)
  Distributivityₗ.proof([⋅][+]-distributivityₗ) {x}{y}{z} = p{x}{y}{z} where
    p : Names.Distributivityₗ(_⋅_)(_+_)
    p {𝟎}   {y}   {z}   = [≡]-intro
    p {𝐒 x} {𝟎}   {z}   = [≡]-intro
    p {𝐒 x} {𝐒 y} {𝟎}   = [≡]-intro
    p {𝐒 x} {𝐒 y} {𝐒 z} = [≡]-with(𝐒 ∘ 𝐒) $
      x + (x + (𝐒 x ⋅ (y + z)))         🝖[ _≡_ ]-[ [≡]-with((x +_) ∘ (x +_)) (p {𝐒 x} {y} {z}) ]
      x + (x + ((𝐒 x ⋅ y) + (𝐒 x ⋅ z))) 🝖[ _≡_ ]-[ [≡]-with(x +_) (One.commuteₗ-assocᵣ ⦃ comm = intro(\{x y} → commutativity(_+_) {x}{y}) ⦄ {a = x}{b = 𝐒 x ⋅ y}{c = 𝐒 x ⋅ z}) ]
      x + ((𝐒 x ⋅ y) + (x + (𝐒 x ⋅ z))) 🝖[ _≡_ ]-[ associativity(_+_) {x = x}{y = 𝐒 x ⋅ y} ]-sym
      (x + (𝐒 x ⋅ y)) + (x + (𝐒 x ⋅ z)) 🝖-end

instance
  [⋅]-associativity : Associativity (_⋅_)
  Associativity.proof([⋅]-associativity) {x}{y}{z} = p{x}{y}{z} where
    p : Names.Associativity(_⋅_)
    p {𝟎}   {𝟎}   {𝟎}   = [≡]-intro
    p {𝟎}   {𝟎}   {𝐒 z} = [≡]-intro
    p {𝟎}   {𝐒 y} {𝟎}   = [≡]-intro
    p {𝟎}   {𝐒 y} {𝐒 z} = [≡]-intro
    p {𝐒 x} {𝟎}   {𝟎}   = [≡]-intro
    p {𝐒 x} {𝟎}   {𝐒 z} = [≡]-intro
    p {𝐒 x} {𝐒 y} {𝟎}   = [≡]-intro
    p {𝐒 x} {𝐒 y} {𝐒 z} = [≡]-with(𝐒) $
      (x + (𝐒 x ⋅ y)) + (𝐒(x + 𝐒 x ⋅ y) ⋅ z)  🝖[ _≡_ ]-[ associativity(_+_) {x = x}{y = 𝐒 x ⋅ y} ]
      x + ((𝐒 x ⋅ y) + (𝐒(x + 𝐒 x ⋅ y) ⋅ z))  🝖[ _≡_ ]-[]
      x + ((𝐒 x ⋅ y) + ((𝐒 x + 𝐒 x ⋅ y) ⋅ z)) 🝖[ _≡_ ]-[]
      x + ((𝐒 x ⋅ y) + ((𝐒 x ⋅ 𝐒 y) ⋅ z))     🝖[ _≡_ ]-[ [≡]-with(expr ↦ x + ((𝐒 x ⋅ y) + expr)) (p{𝐒 x}{𝐒 y}{z}) ]
      x + ((𝐒 x ⋅ y) + (𝐒 x ⋅ (𝐒 y ⋅ z)))     🝖[ _≡_ ]-[ [≡]-with(x +_) (distributivityₗ(_⋅_)(_+_) {x = 𝐒 x}{y = y}{z = 𝐒 y ⋅ z}) ]-sym
      x + (𝐒 x ⋅ (y + (𝐒 y ⋅ z)))             🝖-end

instance
  [⋅]-commutativity : Commutativity (_⋅_)
  Commutativity.proof([⋅]-commutativity) {x}{y} = p {x}{y} where
    p : Names.Commutativity(_⋅_)
    p {𝟎} {𝟎} = [≡]-intro
    p {𝟎} {𝐒 y} = [≡]-intro
    p {𝐒 x} {𝟎} = [≡]-intro
    p {𝐒 x} {𝐒 y} = [≡]-with(𝐒) $
      x + (𝐒 x ⋅ y)     🝖-[ [≡]-with(x +_) ([⋅]-with-[𝐒]ₗ {x}{y}) ]
      x + ((x ⋅ y) + y) 🝖-[ [≡]-with(x +_) (commutativity(_+_) {x ⋅ y}{y}) ]
      x + (y + (x ⋅ y)) 🝖-[ One.commuteₗ-assocᵣ ⦃ comm = intro(\{x y} → commutativity(_+_) {x}{y}) ⦄ {a = x}{b = y}{c = x ⋅ y} ]
      y + (x + (x ⋅ y)) 🝖-[ [≡]-with(expr ↦ y + (x + expr)) (p {x} {y}) ]
      y + (x + (y ⋅ x)) 🝖-[ [≡]-with(y +_) (commutativity(_+_) {x}{y ⋅ x}) ]
      y + ((y ⋅ x) + x) 🝖-[ [≡]-with(y +_) ([⋅]-with-[𝐒]ₗ {y}{x}) ]-sym
      y + (𝐒 y ⋅ x)     🝖-end

[𝐏][𝐒]-inverses : ∀{n} → (𝐏(𝐒(n)) ≡ n)
[𝐏][𝐒]-inverses = [≡]-intro

[+]-sum-is-0 : ∀{a b} → (a + b ≡ 0) → (a ≡ 0)∧(b ≡ 0)
[+]-sum-is-0 {a}{b} proof = [∧]-intro (l{a}{b} proof) r where
  l = \{a b} → ℕ-elim{T = \b → (a + b ≡ 0) → (a ≡ 0)} id (\_ p → p ∘ [≡]-with(𝐏)) b
  r = l{b}{a} (commutativity(_+_) {b}{a} 🝖 proof)

[⋅]-product-is-1ₗ : ∀{a b} → (a ⋅ b ≡ 1) → (a ≡ 1)
[⋅]-product-is-1ₗ {𝟎}   {_}   p = p
[⋅]-product-is-1ₗ {𝐒 a} {𝟎}   ()
[⋅]-product-is-1ₗ {𝐒 a} {𝐒 b} p = [≡]-with(𝐒) ([∧]-elimₗ ([+]-sum-is-0 (injective(𝐒) p)))

[⋅]-product-is-1ᵣ : ∀{a b} → (a ⋅ b ≡ 1) → (b ≡ 1)
[⋅]-product-is-1ᵣ {a}{b} = [⋅]-product-is-1ₗ {b}{a} ∘ (commutativity(_⋅_) {b}{a} 🝖_)

[⋅]-product-is-0 : ∀{a b} → (a ⋅ b ≡ 0) → ((a ≡ 0)∨(b ≡ 0))
[⋅]-product-is-0 {_}   {0}    _   = [∨]-introᵣ [≡]-intro
[⋅]-product-is-0 {0}   {𝐒(_)} _   = [∨]-introₗ [≡]-intro
[⋅]-product-is-0 {𝐒(a)}{𝐒(b)} ab0 with () ← [𝐒]-not-0 {(𝐒(a) ⋅ b) + a} (commutativity(_+_) {𝐒(a) ⋅ b}{𝐒(a)} 🝖 ab0)

[⋅]-product-is-positive : ∀{a b n} → (a ⋅ b ≡ 𝐒(n)) → (∃(n₁ ↦ a ≡ 𝐒(n₁)) ∧ ∃(n₂ ↦ b ≡ 𝐒(n₂)))
[⋅]-product-is-positive {_}   {0}    p with () ← [𝐒]-not-0 (symmetry(_≡_) p)
[⋅]-product-is-positive {0}   {𝐒(_)} p with () ← [𝐒]-not-0 (symmetry(_≡_) p)
[⋅]-product-is-positive {𝐒(a)}{𝐒(b)} p = [∧]-intro ([∃]-intro a) ([∃]-intro b)

instance
  [+]-cancellationᵣ : Cancellationᵣ(_+_)
  Cancellationᵣ.proof([+]-cancellationᵣ) {a}{x}{y} = ℕ-elim{T = \a → (x + a ≡ y + a) → (x ≡ y)} id (\_ → _∘ injective(𝐒)) a

instance
  [+]-cancellationₗ : Cancellationₗ(_+_)
  Cancellationₗ.proof([+]-cancellationₗ) {a}{x}{y} = cancellationᵣ(_+_) ∘ One.commuteBothTemp {a₁ = a}{x}{a}{y}

[^]-of-𝟎ₗ : ∀{x} → (𝟎 ^ 𝐒(x) ≡ 𝟎)
[^]-of-𝟎ₗ = [≡]-intro

[^]-of-𝟏ₗ : ∀{x} → (𝟏 ^ x ≡ 𝟏)
[^]-of-𝟏ₗ {𝟎}   = [≡]-intro
[^]-of-𝟏ₗ {𝐒 x} = [^]-of-𝟏ₗ {x}

[−₀]-absorberₗ-raw : ∀{x} → ((𝟎 −₀ x) ≡ 𝟎)
[−₀]-absorberₗ-raw {n} = ℕ-elim{T = \n → ((𝟎 −₀ n) ≡ 𝟎)} [≡]-intro (\_ _ → [≡]-intro) n
{-# REWRITE [−₀]-absorberₗ-raw #-}
instance
  [−₀]-absorberₗ : Absorberₗ (_−₀_) (𝟎)
  Absorberₗ.proof([−₀]-absorberₗ) {x} = [−₀]-absorberₗ-raw {x}

instance
  [−₀]-identityᵣ : Identityᵣ (_−₀_) (𝟎)
  Identityᵣ.proof([−₀]-identityᵣ) {x} = [≡]-intro

[−₀]-self : ∀{x} → ((x −₀ x) ≡ 𝟎)
[−₀]-self {n} = ℕ-elim{T = \n → ((n −₀ n) ≡ 𝟎)} [≡]-intro (\_ p → p) n
{-# REWRITE [−₀]-self #-}

[−₀]-with-[𝐒]ᵣ : ∀{x y} → ((x −₀ 𝐒(y)) ≡ 𝐏(x −₀ y))
[−₀]-with-[𝐒]ᵣ {𝟎} {𝟎}     = [≡]-intro
[−₀]-with-[𝐒]ᵣ {𝟎} {𝐒 y}   = [≡]-intro
[−₀]-with-[𝐒]ᵣ {𝐒 x} {𝟎}   = [≡]-intro
[−₀]-with-[𝐒]ᵣ {𝐒 x} {𝐒 y} = [−₀]-with-[𝐒]ᵣ {x} {y}

[−₀][−₀]-to-[−₀][+] : ∀{x y z} → ((x −₀ y) −₀ z ≡ x −₀ (y + z))
[−₀][−₀]-to-[−₀][+] {x}{y}{𝟎} = [≡]-intro
[−₀][−₀]-to-[−₀][+] {x}{y}{𝐒 z} =
  (x −₀ y) −₀ 𝐒(z) 🝖[ _≡_ ]-[ [−₀]-with-[𝐒]ᵣ {x −₀ y}{z} ]
  𝐏((x −₀ y) −₀ z) 🝖[ _≡_ ]-[ congruence₁(𝐏) ([−₀][−₀]-to-[−₀][+] {x}{y}{z}) ]
  𝐏(x −₀ (y + z))  🝖[ _≡_ ]-[ [−₀]-with-[𝐒]ᵣ {x}{y + z} ]-sym
  x −₀ 𝐒(y + z)    🝖[ _≡_ ]-[]
  x −₀ (y + 𝐒(z))  🝖-end

[−₀]ₗ[+]ᵣ-nullify : ∀{x y} → ((x + y) −₀ y ≡ x)
[−₀]ₗ[+]ᵣ-nullify{𝟎}   {𝟎}    = [≡]-intro
[−₀]ₗ[+]ᵣ-nullify{𝟎}   {𝐒(y)} = [≡]-intro
[−₀]ₗ[+]ᵣ-nullify{𝐒(x)}{𝐒(y)} = [≡]-intro 🝖 ([−₀]ₗ[+]ᵣ-nullify{𝐒(x)}{y})
[−₀]ₗ[+]ᵣ-nullify{𝐒(x)}{𝟎}    = [≡]-intro
instance
  [+][−₀]-inverseOperatorᵣ : InverseOperatorᵣ(_+_)(_−₀_)
  InverseOperatorᵣ.proof [+][−₀]-inverseOperatorᵣ {x} {y} = [−₀]ₗ[+]ᵣ-nullify {x}{y}

[−₀]ₗ[+]ₗ-nullify : ∀{x y} → ((x + y) −₀ x ≡ y)
[−₀]ₗ[+]ₗ-nullify {x}{y} = [≡]-substitutionᵣ (commutativity(_+_) {y}{x}) {expr ↦ (expr −₀ x ≡ y)} ([−₀]ₗ[+]ᵣ-nullify {y}{x})

[−₀][+]ᵣ-nullify : ∀{x₁ x₂ y} → ((x₁ + y) −₀ (x₂ + y) ≡ x₁ −₀ x₂)
[−₀][+]ᵣ-nullify {_} {_} {𝟎}    = [≡]-intro
[−₀][+]ᵣ-nullify {x₁}{x₂}{𝐒(y)} = [−₀][+]ᵣ-nullify {x₁}{x₂}{y}

[−₀][+]ₗ-nullify : ∀{x y₁ y₂} → ((x + y₁) −₀ (x + y₂) ≡ y₁ −₀ y₂)
[−₀][+]ₗ-nullify {x}{y₁}{y₂} =
  [≡]-with-op(_−₀_) (commutativity(_+_) {x}{y₁}) (commutativity(_+_) {x}{y₂})
  🝖 [−₀][+]ᵣ-nullify{y₁}{y₂}{x}

[−₀]-cases : ∀{x y} → ((x −₀ y) + y ≡ x) ∨ (x −₀ y ≡ 𝟎)
[−₀]-cases {𝟎}   {𝟎}    = [∨]-introᵣ [≡]-intro
[−₀]-cases {𝟎}   {𝐒(_)} = [∨]-introᵣ [≡]-intro
[−₀]-cases {𝐒(_)}{𝟎}    = [∨]-introₗ [≡]-intro
[−₀]-cases {𝐒(x)}{𝐒(y)} with [−₀]-cases {x}{y}
... | [∨]-introₗ proof = [∨]-introₗ ([≡]-with(𝐒) (proof))
... | [∨]-introᵣ proof = [∨]-introᵣ proof

[−₀]-cases-commuted : ∀{x y} → (y + (x −₀ y) ≡ x) ∨ (x −₀ y ≡ 𝟎)
[−₀]-cases-commuted {x}{y} with [−₀]-cases{x}{y}
... | [∨]-introₗ proof = [∨]-introₗ (commutativity(_+_) {y}{x −₀ y} 🝖 proof)
... | [∨]-introᵣ proof = [∨]-introᵣ proof

[𝄩]-𝐒-cases : ∀{x y} → (𝐒(x 𝄩 y) ≡ 𝐒(x) 𝄩 y) ∨ (𝐒(x 𝄩 y) ≡ x 𝄩 𝐒(y))
[𝄩]-𝐒-cases {𝟎}   {𝟎}   = [∨]-introₗ [≡]-intro
[𝄩]-𝐒-cases {𝟎}   {𝐒 y} = [∨]-introᵣ [≡]-intro
[𝄩]-𝐒-cases {𝐒 x} {𝟎}   = [∨]-introₗ [≡]-intro
[𝄩]-𝐒-cases {𝐒 x} {𝐒 y} = [𝄩]-𝐒-cases {x}{y}

[𝄩]-identityₗ-raw : Names.Identityₗ(_𝄩_)(0)
[𝄩]-identityₗ-raw {𝟎}    = [≡]-intro
[𝄩]-identityₗ-raw {𝐒(_)} = [≡]-intro
{-# REWRITE [𝄩]-identityₗ-raw #-}
instance
  [𝄩]-identityₗ : Identityₗ(_𝄩_)(𝟎)
  Identityₗ.proof([𝄩]-identityₗ) {x} = [𝄩]-identityₗ-raw {x}

[𝄩]-identityᵣ-raw : Names.Identityᵣ (_𝄩_) (0)
[𝄩]-identityᵣ-raw {𝟎}    = [≡]-intro
[𝄩]-identityᵣ-raw {𝐒(_)} = [≡]-intro
{-# REWRITE [𝄩]-identityᵣ-raw #-}
instance
  [𝄩]-identityᵣ : Identityᵣ(_𝄩_)(𝟎)
  Identityᵣ.proof([𝄩]-identityᵣ) {x} = [𝄩]-identityᵣ-raw {x}

instance
  [𝄩]-identity : Identity(_𝄩_)(𝟎)
  [𝄩]-identity = intro

[𝄩]-self : ∀{x} → (x 𝄩 x ≡ 𝟎)
[𝄩]-self {𝟎}    = [≡]-intro
[𝄩]-self {𝐒(x)} = [𝄩]-self {x}
{-# REWRITE [𝄩]-self #-}

instance
  [𝄩]-inverseFunctionₗ : InverseFunctionₗ(_𝄩_) ⦃ [∃]-intro 𝟎 ⦄ (id)
  [𝄩]-inverseFunctionₗ = intro \{x} → [𝄩]-self {x}

instance
  [𝄩]-inverseFunctionᵣ : InverseFunctionᵣ(_𝄩_) ⦃ [∃]-intro 𝟎 ⦄ (id)
  [𝄩]-inverseFunctionᵣ = intro \{x} → [𝄩]-self {x}

instance
  [𝄩]-commutativity : Commutativity(_𝄩_)
  Commutativity.proof([𝄩]-commutativity) {x}{y} = p{x}{y} where
    p : Names.Commutativity (_𝄩_)
    p{𝟎}   {𝟎}    = [≡]-intro
    p{𝟎}   {𝐒(y)} = [≡]-intro
    p{𝐒(x)}{𝟎}    = [≡]-intro
    p{𝐒(x)}{𝐒(y)} = p{x}{y}

instance
  [+][𝄩]-inverseOperatorᵣ : InverseOperatorᵣ(_+_)(_𝄩_)
  InverseOperatorᵣ.proof [+][𝄩]-inverseOperatorᵣ {x}{y} = p{x}{y} where
    p : ∀{x y} → ((x + y) 𝄩 y ≡ x)
    p{𝟎}   {𝟎}    = [≡]-intro
    p{𝟎}   {𝐒(y)} = [≡]-intro
    p{𝐒(x)}{𝐒(y)} = [≡]-intro 🝖 (p{𝐒(x)}{y})
    p{𝐒(x)}{𝟎}    = [≡]-intro

instance
  [swap+][𝄩]-inverseOperatorᵣ : InverseOperatorᵣ(swap(_+_))(_𝄩_)
  InverseOperatorᵣ.proof [swap+][𝄩]-inverseOperatorᵣ {x}{y} = congruence₂ₗ(_𝄩_)(y) (commutativity(_+_) {y}{x}) 🝖 inverseOperᵣ(_+_)(_𝄩_) {x}{y}

instance
  [swap+][𝄩]-inverseOperatorₗ : InverseOperatorₗ(swap(_+_))(_𝄩_)
  InverseOperatorₗ.proof [swap+][𝄩]-inverseOperatorₗ {x}{y} = commutativity(_𝄩_) {x}{y + x} 🝖 inverseOperᵣ(_+_)(_𝄩_) {y}{x}

instance
  [+][𝄩]-inverseOperatorₗ : InverseOperatorₗ(_+_)(_𝄩_)
  InverseOperatorₗ.proof [+][𝄩]-inverseOperatorₗ {x}{y} = commutativity(_𝄩_) {x}{x + y} 🝖 inverseOperᵣ(swap(_+_))(_𝄩_) {y}{x}

[𝄩]-with-[+]ᵣ : ∀{x y z} → ((x + z) 𝄩 (y + z) ≡ x 𝄩 y)
[𝄩]-with-[+]ᵣ {𝟎}   {𝟎}   {𝟎}    = [≡]-intro
[𝄩]-with-[+]ᵣ {𝟎}   {𝐒(y)}{𝟎}    = [≡]-intro
[𝄩]-with-[+]ᵣ {𝟎}   {𝟎}   {𝐒(z)} = [≡]-intro
[𝄩]-with-[+]ᵣ {𝟎}   {𝐒(y)}{𝐒(z)} = inverseOperₗ(swap(_+_))(_𝄩_) {z}{_}
[𝄩]-with-[+]ᵣ {𝐒(x)}{𝟎}   {𝟎}    = [≡]-intro
[𝄩]-with-[+]ᵣ {𝐒(x)}{𝐒(y)}{𝟎}    = [≡]-intro
[𝄩]-with-[+]ᵣ {𝐒(x)}{𝟎}   {𝐒(z)} = inverseOperᵣ(_+_)(_𝄩_) {𝐒(x)}{z}
[𝄩]-with-[+]ᵣ {𝐒(x)}{𝐒(y)}{𝐒(z)} = [𝄩]-with-[+]ᵣ {𝐒(x)}{𝐒(y)}{z}

[𝄩]-with-[+]ₗ : ∀{x y z} → ((z + x) 𝄩 (z + y) ≡ x 𝄩 y)
[𝄩]-with-[+]ₗ {𝟎}   {𝟎}   {𝟎}    = [≡]-intro
[𝄩]-with-[+]ₗ {𝟎}   {𝐒(y)}{𝟎}    = [≡]-intro
[𝄩]-with-[+]ₗ {𝟎}   {𝟎}   {𝐒(z)} = [≡]-intro
[𝄩]-with-[+]ₗ {𝟎}   {𝐒(y)}{𝐒(z)} = inverseOperₗ(_+_)(_𝄩_) {z}{𝐒(y)}
[𝄩]-with-[+]ₗ {𝐒(x)}{𝟎}   {𝟎}    = [≡]-intro
[𝄩]-with-[+]ₗ {𝐒(x)}{𝐒(y)}{𝟎}    = [≡]-intro
[𝄩]-with-[+]ₗ {𝐒(x)}{𝟎}   {𝐒(z)} = inverseOperᵣ(swap(_+_))(_𝄩_) {𝐒(x)}{z}
[𝄩]-with-[+]ₗ {𝐒(x)}{𝐒(y)}{𝐒(z)} = [𝄩]-with-[+]ₗ {𝐒(x)}{𝐒(y)}{z}

[𝄩]-equality : ∀{x y} → (x 𝄩 y ≡ 𝟎) → (x ≡ y)
[𝄩]-equality {𝟎}   {𝟎}    [≡]-intro = [≡]-intro
[𝄩]-equality {𝟎}   {𝐒(y)} ()
[𝄩]-equality {𝐒(x)}{𝟎}    ()
[𝄩]-equality {𝐒(x)}{𝐒(y)} proof     = [≡]-with(𝐒) ([𝄩]-equality {x}{y} proof)

instance
  [⋅][𝄩]-distributivityᵣ : Distributivityᵣ(_⋅_)(_𝄩_)
  Distributivityᵣ.proof [⋅][𝄩]-distributivityᵣ {x}{y}{z} = p{x}{y}{z} where
    p : Names.Distributivityᵣ(_⋅_)(_𝄩_)
    p {𝟎} {𝟎} {z} = [≡]-intro
    p {𝟎} {𝐒 y} {z} = [≡]-intro
    p {𝐒 x} {𝟎} {z} = [≡]-intro
    p {𝐒 x} {𝐒 y} {z} =
      (𝐒(x) 𝄩 𝐒(y)) ⋅ z             🝖[ _≡_ ]-[]
      (x 𝄩 y) ⋅ z                   🝖[ _≡_ ]-[ p{x}{y}{z} ]
      (x ⋅ z) 𝄩 (y ⋅ z)             🝖[ _≡_ ]-[ [𝄩]-with-[+]ᵣ {x ⋅ z}{y ⋅ z}{z} ]-sym
      ((x ⋅ z) + z) 𝄩 ((y ⋅ z) + z) 🝖[ _≡_ ]-[ congruence₂(_𝄩_) ([⋅]-with-[𝐒]ₗ {x}{z}) ([⋅]-with-[𝐒]ₗ {y}{z}) ]-sym
      (𝐒(x) ⋅ z) 𝄩 (𝐒(y) ⋅ z)       🝖-end

instance
  [⋅][𝄩]-distributivityₗ : Distributivityₗ(_⋅_)(_𝄩_)
  Distributivityₗ.proof [⋅][𝄩]-distributivityₗ {x}{y}{z} =
    x ⋅ (y 𝄩 z)       🝖[ _≡_ ]-[ commutativity(_⋅_) {x}{y 𝄩 z} ]
    (y 𝄩 z) ⋅ x       🝖[ _≡_ ]-[ distributivityᵣ(_⋅_)(_𝄩_) {y}{z}{x} ]
    (y ⋅ x) 𝄩 (z ⋅ x) 🝖[ _≡_ ]-[ congruence₂(_𝄩_) (commutativity(_⋅_) {y}{x}) (commutativity(_⋅_) {z}{x}) ]
    (x ⋅ y) 𝄩 (x ⋅ z) 🝖-end


-- Minimal propositional logic, vector-based de Bruijn approach, initial encoding

module Vi.Mp where

open import Lib using (Nat; suc; _+_; Fin; fin; Vec; _,_; proj; VMem; mem)

-- Types

infixl 2 _&&_
infixl 1 _||_
infixr 0 _=>_
data Ty : Set where
  UNIT  :             Ty
  _=>_  : Ty -> Ty -> Ty
  _&&_  : Ty -> Ty -> Ty
  _||_  : Ty -> Ty -> Ty
  FALSE :             Ty

infixr 0 _<=>_
_<=>_ : Ty -> Ty -> Ty
a <=> b = (a => b) && (b => a)

NOT : Ty -> Ty
NOT a = a => FALSE

TRUE : Ty
TRUE = FALSE => FALSE

-- Context and truth judgement

Cx : Nat -> Set
Cx n = Vec Ty n

isTrue : forall {tn} -> Ty -> Fin tn -> Cx tn -> Set
isTrue a i tc = VMem a i tc

-- Terms

module Mp where
  infixl 1 _$_
  infixr 0 lam=>_
  data Tm {tn} (tc : Cx tn) : Ty -> Set where
    var    : forall {a i}   -> isTrue a i tc             -> Tm tc a
    lam=>_ : forall {a b}   -> Tm (tc , a) b             -> Tm tc (a => b)
    _$_    : forall {a b}   -> Tm tc (a => b) -> Tm tc a -> Tm tc b
    pair'  : forall {a b}   -> Tm tc a        -> Tm tc b -> Tm tc (a && b)
    fst    : forall {a b}   -> Tm tc (a && b)            -> Tm tc a
    snd    : forall {a b}   -> Tm tc (a && b)            -> Tm tc b
    left   : forall {a b}   -> Tm tc a                   -> Tm tc (a || b)
    right  : forall {a b}   -> Tm tc b                   -> Tm tc (a || b)
    case'  : forall {a b c} -> Tm tc (a || b) -> Tm (tc , a) c -> Tm (tc , b) c -> Tm tc c

  syntax pair' x y    = [ x , y ]
  syntax case' xy x y = case xy => x => y

  v : forall {tn} (k : Nat) {tc : Cx (suc (k + tn))} -> Tm tc (proj tc (fin k))
  v i = var (mem i)

  Thm : Ty -> Set
  Thm a = forall {tn} {tc : Cx tn} -> Tm tc a
open Mp public

-- Example theorems

c1 : forall {a b} -> Thm (a && b <=> b && a)
c1 =
  [ lam=> [ snd (v 0) , fst (v 0) ]
  , lam=> [ snd (v 0) , fst (v 0) ]
  ]

c2 : forall {a b} -> Thm (a || b <=> b || a)
c2 =
  [ lam=>
      (case v 0
        => right (v 0)
        => left  (v 0))
  , lam=>
      (case v 0
        => right (v 0)
        => left  (v 0))
  ]

i1 : forall {a} -> Thm (a && a <=> a)
i1 =
  [ lam=> fst (v 0)
  , lam=> [ v 0 , v 0 ]
  ]

i2 : forall {a} -> Thm (a || a <=> a)
i2 =
  [ lam=>
      (case v 0
        => v 0
        => v 0)
  , lam=> left (v 0)
  ]

l3 : forall {a} -> Thm ((a => a) <=> TRUE)
l3 =
  [ lam=> lam=> v 0
  , lam=> lam=> v 0
  ]

l1 : forall {a b c} -> Thm (a && (b && c) <=> (a && b) && c)
l1 =
  [ lam=>
      [ [ fst (v 0) , fst (snd (v 0)) ]
      , snd (snd (v 0))
      ]
  , lam=>
      [ fst (fst (v 0))
      , [ snd (fst (v 0)) , snd (v 0) ]
      ]
  ]

l2 : forall {a} -> Thm (a && TRUE <=> a)
l2 =
  [ lam=> fst (v 0)
  , lam=> [ v 0 , lam=> v 0 ]
  ]

l4 : forall {a b c} -> Thm (a && (b || c) <=> (a && b) || (a && c))
l4 =
  [ lam=>
      (case snd (v 0)
        => left  [ fst (v 1) , v 0 ]
        => right [ fst (v 1) , v 0 ])
  , lam=>
      (case v 0
        => [ fst (v 0) , left  (snd (v 0)) ]
        => [ fst (v 0) , right (snd (v 0)) ])
  ]

l6 : forall {a b c} -> Thm (a || (b && c) <=> (a || b) && (a || c))
l6 =
  [ lam=>
      (case v 0
        => [ left  (v 0)       , left  (v 0) ]
        => [ right (fst (v 0)) , right (snd (v 0)) ])
  , lam=>
      (case fst (v 0)
        => left (v 0)
        =>
          case snd (v 1)
            => left  (v 0)
            => right [ v 1 , v 0 ])
  ]

l7 : forall {a} -> Thm (a || TRUE <=> TRUE)
l7 =
  [ lam=> lam=> v 0
  , lam=> right (v 0)
  ]

l9 : forall {a b c} -> Thm (a || (b || c) <=> (a || b) || c)
l9 =
  [ lam=>
      (case v 0
        => left (left (v 0))
        =>
          case v 0
            => left  (right (v 0))
            => right (v 0))
  , lam=>
      (case v 0
        =>
          case v 0
            => left  (v 0)
            => right (left (v 0))
        => right (right (v 0)))
  ]

l11 : forall {a b c} -> Thm ((a => (b && c)) <=> (a => b) && (a => c))
l11 =
  [ lam=>
      [ lam=> fst (v 1 $ v 0)
      , lam=> snd (v 1 $ v 0)
      ]
  , lam=>
      lam=> [ fst (v 1) $ v 0 , snd (v 1) $ v 0 ]
  ]

l12 : forall {a} -> Thm ((a => TRUE) <=> TRUE)
l12 =
  [ lam=> lam=> v 0
  , lam=> lam=> v 1
  ]

l13 : forall {a b c} -> Thm ((a => (b => c)) <=> ((a && b) => c))
l13 =
  [ lam=>
      lam=> v 1 $ fst (v 0) $ snd (v 0)
  , lam=>
      lam=>
        lam=> v 2 $ [ v 1 , v 0 ]
  ]

l16 : forall {a b c} -> Thm (((a && b) => c) <=> (a => (b => c)))
l16 =
  [ lam=>
      lam=>
        lam=> v 2 $ [ v 1 , v 0 ]
  , lam=>
      lam=> v 1 $ fst (v 0) $ snd (v 0)
  ]

l17 : forall {a} -> Thm ((TRUE => a) <=> a)
l17 =
  [ lam=> v 0 $ (lam=> v 0)
  , lam=> lam=> v 1
  ]

l19 : forall {a b c} -> Thm (((a || b) => c) <=> (a => c) && (b => c))
l19 =
  [ lam=>
      [ lam=> v 1 $ left  (v 0)
      , lam=> v 1 $ right (v 0)
      ]
  , lam=>
      lam=>
        (case v 0
          => fst (v 2) $ (v 0)
          => snd (v 2) $ (v 0))
  ]


module Data.Fin.Extra where

open import Data.Nat
    renaming (suc to S; zero to Z; _+_ to _ℕ+_; _*_ to _ℕ*_)
open import Data.Fin

open import Function
open import Relation.Nullary.Negation
open import Relation.Binary.PropositionalEquality

inject-1 : ∀ {n} → (i : Fin (S n)) → toℕ i ≢ n → Fin n
inject-1 {Z}   zero    p = contradiction refl p
inject-1 {Z}   (suc i) p = i
inject-1 {S n} zero    p = zero
inject-1 {S n} (suc i) p = suc (inject-1 i (p ∘ cong S))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Homomorphism proofs for negation over polynomials
------------------------------------------------------------------------

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

open import Tactic.RingSolver.Core.Polynomial.Parameters

module Tactic.RingSolver.Core.Polynomial.Homomorphism.Negation
  {r₁ r₂ r₃ r₄}
  (homo : Homomorphism r₁ r₂ r₃ r₄)
  where

open import Data.Product          using (_,_)
open import Data.Vec              using (Vec)
open import Data.Nat              using (_<′_)
open import Data.Nat.Induction

open import Function

open Homomorphism homo
open import Tactic.RingSolver.Core.Polynomial.Homomorphism.Lemmas homo
open import Tactic.RingSolver.Core.Polynomial.Reasoning to
open import Tactic.RingSolver.Core.Polynomial.Base from
open import Tactic.RingSolver.Core.Polynomial.Semantics homo

⊟-step-hom : ∀ {n} (a : Acc _<′_ n) → (xs : Poly n) → ∀ ρ → ⟦ ⊟-step a xs ⟧ ρ ≈ - (⟦ xs ⟧ ρ)
⊟-step-hom (acc _ ) (Κ x  ⊐ i≤n) ρ = -‿homo x
⊟-step-hom (acc wf) (⅀ xs ⊐ i≤n) ρ′ =
  let (ρ , ρs) = drop-1 i≤n ρ′
      neg-zero =
        begin
          0#
        ≈⟨ sym (zeroʳ _) ⟩
          - 0# * 0#
        ≈⟨ -‿*-distribˡ 0# 0# ⟩
          - (0# * 0#)
        ≈⟨ -‿cong (zeroˡ 0#) ⟩
          - 0#
        ∎
  in
  begin
    ⟦ poly-map (⊟-step (wf _ i≤n)) xs ⊐↓ i≤n ⟧ ρ′
  ≈⟨ ⊐↓-hom (poly-map (⊟-step (wf _ i≤n)) xs) i≤n ρ′ ⟩
    ⅀?⟦ poly-map (⊟-step  (wf _ i≤n)) xs ⟧ (ρ , ρs)
  ≈⟨ poly-mapR ρ ρs (⊟-step (wf _ i≤n)) -_ (-‿cong) (λ x y → *-comm x (- y) ⟨ trans ⟩ -‿*-distribˡ y x ⟨ trans ⟩ -‿cong (*-comm _ _)) (λ x y → sym (-‿+-comm x y)) (flip (⊟-step-hom (wf _ i≤n)) ρs) (sym neg-zero ) xs ⟩
    - ⅀⟦ xs ⟧ (ρ , ρs)
  ∎

⊟-hom : ∀ {n}
      → (xs : Poly n)
      → (Ρ : Vec Carrier n)
      → ⟦ ⊟ xs ⟧ Ρ ≈ - ⟦ xs ⟧ Ρ
⊟-hom = ⊟-step-hom (<′-wellFounded _)


module maryjohn2 where

postulate Person        : Set
postulate john          : Person
postulate mary          : Person
postulate barbara       : Person
postulate IsStudent     : Person -> Set
postulate maryIsStudent : IsStudent mary
postulate implication   : IsStudent mary -> IsStudent john

Lemma1 : Set
Lemma1 = IsStudent john

proof-lemma1 : Lemma1
proof-lemma1 = implication maryIsStudent

Lemma2 : Set
Lemma2 = IsStudent john -> IsStudent barbara

proof-lemma2 : Lemma2
proof-lemma2 = \(x : IsStudent john) -> _


open import Data.Nat using (ℕ; suc)
open import Data.Fin using (Fin; zero; suc)

infix  9  #_
infixl 7  _·_
infix  6  ƛ_

data Term : ℕ → Set where
  #_  : ∀ {n : ℕ} → Fin n           → Term n
  ƛ_  : ∀ {n : ℕ} → Term (suc n)    → Term n
  _·_ : ∀ {n : ℕ} → Term n → Term n → Term n

Rename : ℕ → ℕ → Set
Rename n m = Fin n → Fin m

Subst : ℕ → ℕ → Set
Subst n m = Fin n → Term m

ext : ∀ {n m} → Rename n m → Rename (suc n) (suc m)
ext ρ zero    = zero
ext ρ (suc x) = suc (ρ x)

rename : ∀ {n m} → Rename n m → (Term n → Term m)
rename ρ (# x)   = # (ρ x)
rename ρ (ƛ M)   = ƛ (rename (ext ρ) M)
rename ρ (M · N) = rename ρ M · rename ρ N

exts : ∀ {n m} → Subst n m → Subst (suc n) (suc m)
exts σ zero    = # zero
exts σ (suc x) = rename suc (σ x)

subst : ∀ {n m} → Subst n m → (Term n → Term m)
subst σ (# x)   = σ x
subst σ (ƛ M)   = ƛ (subst (exts σ) M)
subst σ (M · N) = subst σ M · subst σ N

subst-zero : ∀ {n} → Term n → Subst (suc n) n
subst-zero M zero    = M
subst-zero M (suc x) = # x

infix 8 _[_]

_[_] : ∀ {n} → Term (suc n) → Term n → Term n
M [ N ] = subst (subst-zero N) M


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

module Data.Binary.Proofs.Multiplication where

open import Relation.Binary.PropositionalEquality
open import Data.Binary.Operations.Unary
open import Data.Binary.Operations.Addition
open import Data.Binary.Operations.Multiplication
open import Data.Binary.Proofs.Unary
open import Data.Binary.Proofs.Addition
open import Data.Binary.Definitions
open import Data.Binary.Operations.Semantics
open import Data.Nat as ℕ using (ℕ; suc; zero)
open import Relation.Binary.PropositionalEquality.FasterReasoning
import Data.Nat.Properties as ℕ
open import Function
open import Data.Nat.Reasoning

mul-homo : ∀ xs ys → ⟦ mul xs ys ⇓⟧⁺ ≡ ⟦ xs ⇓⟧⁺ ℕ.* ⟦ ys ⇓⟧⁺
mul-homo 1ᵇ ys = sym (ℕ.+-identityʳ _)
mul-homo (O ∷ xs) ys = cong 2* (mul-homo xs ys) ⟨ trans ⟩ sym (ℕ.*-distribʳ-+ ⟦ ys ⇓⟧⁺ ⟦ xs ⇓⟧⁺ _)
mul-homo (I ∷ xs) ys =
  begin
    ⟦ add O (O ∷ mul ys xs) ys ⇓⟧⁺
  ≡⟨ add₀-homo (O ∷ mul ys xs) ys ⟩
    2* ⟦ mul ys xs ⇓⟧⁺ ℕ.+ ⟦ ys ⇓⟧⁺
  ≡⟨ ⟦ ys ⇓⟧⁺ ≪+ cong 2* (mul-homo ys xs) ⟩
    2* (⟦ ys ⇓⟧⁺ ℕ.* ⟦ xs ⇓⟧⁺) ℕ.+ ⟦ ys ⇓⟧⁺
  ≡⟨ ℕ.+-comm _ ⟦ ys ⇓⟧⁺ ⟩
    ⟦ ys ⇓⟧⁺ ℕ.+ 2* (⟦ ys ⇓⟧⁺ ℕ.* ⟦ xs ⇓⟧⁺)
  ≡˘⟨ ⟦ ys ⇓⟧⁺ +≫ ℕ.*-distribˡ-+ ⟦ ys ⇓⟧⁺ _ _ ⟩
    ⟦ ys ⇓⟧⁺ ℕ.+ ⟦ ys ⇓⟧⁺ ℕ.* (2* ⟦ xs ⇓⟧⁺)
  ≡⟨ ⟦ ys ⇓⟧⁺ +≫ ℕ.*-comm ⟦ ys ⇓⟧⁺ _ ⟩
    ⟦ ys ⇓⟧⁺ ℕ.+ (2* ⟦ xs ⇓⟧⁺) ℕ.* ⟦ ys ⇓⟧⁺
  ∎

*-homo : ∀ xs ys → ⟦ xs * ys ⇓⟧ ≡ ⟦ xs ⇓⟧ ℕ.* ⟦ ys ⇓⟧
*-homo 0ᵇ ys = refl
*-homo (0< x) 0ᵇ = sym (ℕ.*-zeroʳ ⟦ x ⇓⟧⁺)
*-homo (0< xs) (0< ys) = mul-homo xs ys


------------------------------------------------------------------------
-- The equality can be turned into a groupoid which is sometimes
-- commutative
------------------------------------------------------------------------

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

open import Equality

module Equality.Groupoid
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Bijection eq using (_↔_)
open Derived-definitions-and-properties eq
open import Equality.Tactic eq
open import Groupoid eq
open import Pointed-type eq
open import Prelude hiding (id; _∘_)

------------------------------------------------------------------------
-- _≡_ comes with a groupoid structure

groupoid : ∀ {a} (A : Type a) → Groupoid a a
groupoid A = record
  { Object = A
  ; _∼_    = _≡_

  ; id  = refl _
  ; _∘_ = flip trans
  ; _⁻¹ = sym

  ; left-identity  = trans-reflʳ
  ; right-identity = trans-reflˡ
  ; assoc          = λ z≡u y≡z x≡y → trans-assoc x≡y y≡z z≡u
  ; left-inverse   = trans-symʳ
  ; right-inverse  = trans-symˡ
  }

------------------------------------------------------------------------
-- In some cases transitivity is commutative

-- This proof is based on an informal proof due to Thierry Coquand,
-- based on a result from homotopy theory.

module Transitivity-commutative
  {a} {A : Type a} (e : A) (_∙_ : A → A → A)
  (left-identity  : ∀ x → (e ∙ x) ≡ x)
  (right-identity : ∀ x → (x ∙ e) ≡ x)
  where

  open Groupoid (groupoid A) hiding (left-identity; right-identity)

  abstract

    commutative : (p q : e ≡ e) → p ∘ q ≡ q ∘ p
    commutative p q =
      p ∘ q                                 ≡⟨ cong (_∘_ p) (lem₁ _) ⟩
      p ∘ (ri ∘ li ⁻¹ ∘ q′ ∘ li ∘ ri ⁻¹)    ≡⟨ prove (Trans (Trans (Trans (Trans (Trans (Sym (Lift ri)) (Lift li)) (Lift q′))
                                                                          (Sym (Lift li))) (Lift ri)) (Lift p))
                                                     (Trans (Trans (Sym (Lift ri))
                                                                              (Trans (Trans (Lift li) (Lift q′)) (Sym (Lift li))))
                                                            (Trans (Lift ri) (Lift p)))
                                                     (refl _) ⟩
      (p ∘ ri) ∘ (li ⁻¹ ∘ q′ ∘ li) ∘ ri ⁻¹  ≡⟨ cong₂ (λ p q → p ∘ q ∘ ri ⁻¹) (lem₂ _) (lem₃ _) ⟩
      (ri ∘ lc p) ∘ rc q′ ∘ ri ⁻¹           ≡⟨ prove (Trans (Trans (Sym (Lift ri)) (Lift (rc q′))) (Trans (Lift (lc p)) (Lift ri)))
                                                     (Trans (Trans (Sym (Lift ri)) (Trans (Lift (rc q′)) (Lift (lc p)))) (Lift ri))
                                                     (refl _) ⟩
      ri ∘ (lc p ∘ rc q′) ∘ ri ⁻¹           ≡⟨ cong (λ p → ri ∘ p ∘ ri ⁻¹) (lem₄ _ _) ⟩
      ri ∘ (rc q′ ∘ lc p) ∘ ri ⁻¹           ≡⟨ prove (Trans (Trans (Sym (Lift ri)) (Trans (Lift (lc p)) (Lift (rc q′)))) (Lift ri))
                                                     (Trans (Trans (Trans (Sym (Lift ri)) (Lift (lc p))) (Lift (rc q′))) (Lift ri))
                                                     (refl _) ⟩
      ri ∘ rc q′ ∘ (lc p ∘ ri ⁻¹)           ≡⟨ cong₂ (λ p q → ri ∘ p ∘ q) (sym (lem₃ _)) (lem₅ _) ⟩
      ri ∘ (li ⁻¹ ∘ q′ ∘ li) ∘ (ri ⁻¹ ∘ p)  ≡⟨ prove (Trans (Trans (Trans (Lift p) (Sym (Lift ri)))
                                                                   (Trans (Trans (Lift li) (Lift q′)) (Sym (Lift li))))
                                                            (Lift ri))
                                                     (Trans (Lift p) (Trans (Trans (Trans (Trans (Sym (Lift ri)) (Lift li)) (Lift q′))
                                                                                (Sym (Lift li)))
                                                                            (Lift ri)))
                                                     (refl _) ⟩
      (ri ∘ li ⁻¹ ∘ q′ ∘ li ∘ ri ⁻¹) ∘ p    ≡⟨ cong (λ q → q ∘ p) (sym (lem₁ _)) ⟩∎
      q ∘ p                                 ∎
      where

      -- Abbreviations.

      li : ∀ {x} → (e ∙ x) ≡ x
      li = left-identity _

      ri : ∀ {x} → (x ∙ e) ≡ x
      ri = right-identity _

      q′ : e ≡ e
      q′ = li ∘ ri ⁻¹ ∘ q ∘ ri ∘ li ⁻¹

      lc : ∀ {x y} → x ≡ y → (x ∙ e) ≡ (y ∙ e)
      lc = cong (λ x → (x ∙ e))

      rc : ∀ {x y} → x ≡ y → (e ∙ x) ≡ (e ∙ y)
      rc = cong (λ y → (e ∙ y))

      -- Lemmas.

      lem₁ : (p : e ≡ e) →
             p ≡ ri ∘ li ⁻¹ ∘ (li ∘ ri ⁻¹ ∘ p ∘ ri ∘ li ⁻¹) ∘ li ∘ ri ⁻¹
      lem₁ p =
        p                                                          ≡⟨ prove (Lift p) (Trans (Trans Refl (Lift p)) Refl) (refl _) ⟩
        refl _ ∘ p ∘ refl _                                        ≡⟨ sym (cong₂ (λ q r → q ∘ p ∘ r)
                                                                                (right-inverse _) (right-inverse _)) ⟩
        (ri ∘ ri ⁻¹) ∘ p ∘ (ri ∘ ri ⁻¹)                            ≡⟨ prove (Trans (Trans (Trans (Sym (Lift ri)) (Lift ri)) (Lift p))
                                                                                (Trans (Sym (Lift ri)) (Lift ri)))
                                                                            (Trans (Trans (Trans (Trans (Trans (Trans
                                                                               (Sym (Lift ri)) Refl) (Lift ri)) (Lift p))
                                                                               (Sym (Lift ri))) Refl) (Lift ri))
                                                                            (refl _) ⟩
        ri ∘ refl _ ∘ ri ⁻¹ ∘ p ∘ ri ∘ refl _ ∘ ri ⁻¹              ≡⟨ sym (cong₂ (λ q r → ri ∘ q ∘ ri ⁻¹ ∘ p ∘ ri ∘ r ∘ ri ⁻¹)
                                                                                (left-inverse _) (left-inverse _)) ⟩
        ri ∘ (li ⁻¹ ∘ li) ∘ ri ⁻¹ ∘ p ∘ ri ∘ (li ⁻¹ ∘ li) ∘ ri ⁻¹  ≡⟨ prove (Trans (Trans (Trans (Trans (Trans (Trans
                                                                               (Sym (Lift ri)) (Trans (Lift li) (Sym (Lift li))))
                                                                               (Lift ri)) (Lift p)) (Sym (Lift ri)))
                                                                               (Trans (Lift li) (Sym (Lift li)))) (Lift ri))
                                                                            (Trans (Trans (Trans (Trans
                                                                               (Sym (Lift ri)) (Lift li))
                                                                               (Trans (Trans (Trans (Trans
                                                                                (Sym (Lift li)) (Lift ri)) (Lift p)) (Sym (Lift ri)))
                                                                                (Lift li))) (Sym (Lift li))) (Lift ri))
                                                                            (refl _) ⟩∎
        ri ∘ li ⁻¹ ∘ (li ∘ ri ⁻¹ ∘ p ∘ ri ∘ li ⁻¹) ∘ li ∘ ri ⁻¹    ∎

      lem₂ : ∀ {x y} (p : x ≡ y) → p ∘ ri ≡ ri ∘ lc p
      lem₂ = elim (λ p → p ∘ ri ≡ ri ∘ lc p) λ _ →
        prove (Trans (Lift ri) Refl)
              (Trans (Cong (λ x → (x ∙ e)) Refl) (Lift ri))
              (refl _)

      lem₃ : ∀ {x y} (p : x ≡ y) → li ⁻¹ ∘ p ∘ li ≡ rc p
      lem₃ = elim (λ p → li ⁻¹ ∘ p ∘ li ≡ rc p) λ x →
        li ⁻¹ ∘ refl x ∘ li  ≡⟨ prove (Trans (Trans (Lift li) Refl) (Sym (Lift li)))
                                      (Trans (Lift li) (Sym (Lift li)))
                                      (refl _) ⟩
        li ⁻¹ ∘ li           ≡⟨ left-inverse _ ⟩
        refl (e ∙ x)         ≡⟨ prove Refl (Cong (λ y → (e ∙ y)) Refl) (refl _) ⟩∎
        rc (refl x)          ∎

      lem₄ : (p q : e ≡ e) → lc p ∘ rc q ≡ rc q ∘ lc p
      lem₄ p q = elim
        (λ {x y} x≡y → lc x≡y ∘ cong (λ z → (x ∙ z)) q ≡
                       cong (λ z → (y ∙ z)) q ∘ lc x≡y)
        (λ x → prove (Trans (Cong (λ z → x ∙ z) (Lift q))
                            (Cong (λ x → x ∙ e) Refl))
                     (Trans (Cong (λ x → x ∙ e) Refl)
                            (Cong (λ z → x ∙ z) (Lift q)))
                     (refl _))
        p

      lem₅ : ∀ {x y} (p : x ≡ y) → lc p ∘ ri ⁻¹ ≡ ri ⁻¹ ∘ p
      lem₅ = elim (λ p → lc p ∘ ri ⁻¹ ≡ ri ⁻¹ ∘ p) λ _ →
        prove (Trans (Sym (Lift ri)) (Cong (λ x → (x ∙ e)) Refl))
              (Trans Refl (Sym (Lift ri)))
              (refl _)

-- In particular, transitivity is commutative for proofs in
-- proj₁ (Ω[ 2 + n ] X).

Ω[2+n]-commutative :
  ∀ {x} {X : Pointed-type x} n →
  (p q : proj₁ (Ω[ 2 + n ] X)) → trans p q ≡ trans q p
Ω[2+n]-commutative {X = X} n p q =
  Transitivity-commutative.commutative
    id _∘_ left-identity right-identity q p
  where
  open Groupoid (groupoid (proj₁ (Ω[ n ] X)))


module Data.Collection.Equivalence where

open import Data.Collection.Core

open import Function using (id; _∘_)
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; equivalence)
open Function.Equivalence.Equivalence
open import Relation.Binary.PropositionalEquality

open import Level using (zero)

open import Relation.Nullary
open import Relation.Unary
open import Relation.Binary

nach : ∀ {f t} {A : Set f} {B : Set t} → (A ⇔ B) → A → B
nach = _⟨$⟩_ ∘ to

von : ∀ {f t} {A : Set f} {B : Set t} → (A ⇔ B) → B → A
von = _⟨$⟩_ ∘ from

infixr 5 _≋_

_≋_ : Rel Membership _
A ≋ B = {x : Element} → x ∈ A ⇔ x ∈ B

≋-Reflexive : Reflexive _≋_
≋-Reflexive = equivalence id id

≋-Symmetric : Symmetric _≋_
≋-Symmetric z = record
    { to   = from z
    ; from = to z
    }

≋-Transitive : Transitive _≋_
≋-Transitive P≋Q Q≋R = equivalence (nach Q≋R ∘ nach P≋Q) (von P≋Q ∘ von Q≋R)

≋-IsEquivalence : IsEquivalence _≋_
≋-IsEquivalence = record
    { refl = equivalence id id
    ; sym = ≋-Symmetric
    ; trans = ≋-Transitive
    }

≋-Setoid : Setoid _ _
≋-Setoid = record
    { Carrier = Pred Element zero
    ; _≈_ = _≋_
    ; isEquivalence = ≋-IsEquivalence
    }

--------------------------------------------------------------------------------
--  Conditional Equivalence
--------------------------------------------------------------------------------

_≋[_]_ : ∀ {a} → Membership → Pred Element a → Membership → Set a
A ≋[ P ] B = {x : Element} → P x → x ∈ A ⇔ x ∈ B

-- prefix version of _≋[_]_, with the predicate being the first argument
[_]≋ : ∀ {a} → Pred Element a → Membership → Membership → Set a
[_]≋ P A B = A ≋[ P ] B

≋[]-Reflexive : ∀ {a} {P : Pred Element a} → Reflexive [ P ]≋
≋[]-Reflexive A = equivalence id id

≋[]-Symmetric : ∀ {a} {P : Pred Element a} → Symmetric [ P ]≋
≋[]-Symmetric z ∈P = record
    { to = from (z ∈P)
    ; from = to (z ∈P)
    }

≋[]-Transitive : ∀ {a} {P : Pred Element a} → Transitive [ P ]≋
≋[]-Transitive P≋Q Q≋R ∈P = equivalence (nach (Q≋R ∈P) ∘ nach (P≋Q ∈P)) (von (P≋Q ∈P) ∘ von (Q≋R ∈P))

≋[]-IsEquivalence : ∀ {a} {P : Pred Element a} → IsEquivalence [ P ]≋
≋[]-IsEquivalence {p} = record
    { refl = λ _ → equivalence id id
    ; sym = ≋[]-Symmetric
    ; trans = ≋[]-Transitive
    }


-- {-# OPTIONS -v tc.size.solve:100 #-}

open import Agda.Builtin.Size

data Cx (U : Set) : Set where
  ⌀   : Cx U
  _,_ : Cx U → U → Cx U

module _ {U : Set} where
  data _⊆_ : Cx U → Cx U → Set where
    done : ⌀ ⊆ ⌀
    skip : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊆ (Γ′ , A)
    keep : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → (Γ , A) ⊆ (Γ′ , A)

  data _∈_ (A : U) : Cx U → Set where
    top : ∀ {Γ} → A ∈ (Γ , A)
    pop : ∀ {C Γ} → A ∈ Γ → A ∈ (Γ , C)

  mono∈ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → A ∈ Γ → A ∈ Γ′
  mono∈ done     ()
  mono∈ (skip η) i       = pop (mono∈ η i)
  mono∈ (keep η) top     = top
  mono∈ (keep η) (pop i) = pop (mono∈ η i)

  refl⊆ : ∀ {Γ} → Γ ⊆ Γ
  refl⊆ {⌀}     = done
  refl⊆ {Γ , A} = keep refl⊆

infixr 3 _⊃_
data Ty : Set where
  ι   : Ty
  _⊃_ : Ty → Ty → Ty

infix 1 _⊢⟨_⟩_
data _⊢⟨_⟩_ (Γ : Cx Ty) : Size → Ty → Set where
  var : ∀ {p A}                     → A ∈ Γ → Γ ⊢⟨ p ⟩ A
  lam : ∀ {n A B} {o : Size< n}    → Γ , A ⊢⟨ o ⟩ B → Γ ⊢⟨ n ⟩ A ⊃ B
  app : ∀ {m A B} {l k : Size< m} → Γ ⊢⟨ k ⟩ A ⊃ B → Γ ⊢⟨ l ⟩ A → Γ ⊢⟨ m ⟩ B

mono⊢ : ∀ {m A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢⟨ m ⟩ A → Γ′ ⊢⟨ m ⟩ A
mono⊢ η (var i)   = var (mono∈ η i)
mono⊢ η (lam t)   = lam (mono⊢ (keep η) t)
mono⊢ η (app t u) = app (mono⊢ η t) (mono⊢ η u)

det : ∀ {i A B Γ} {j : Size< i} → Γ ⊢⟨ j ⟩ A ⊃ B → Γ , A ⊢⟨ i ⟩ B
det t = app (mono⊢ (skip refl⊆) t) (var top)

ccont : ∀ {m A B Γ} → Γ ⊢⟨ ↑ ↑ ↑ ↑ m ⟩ (A ⊃ A ⊃ B) ⊃ A ⊃ B
ccont = lam (lam (app (app (var (pop top)) (var top)) (var top)))

cont : ∀ {m A B Γ} {m′ : Size< m} → (Γ , A) , A ⊢⟨ ↑ ↑ m′ ⟩ B → Γ , A ⊢⟨ ↑ ↑ ↑ ↑ ↑ m ⟩ B
cont t = det (app ccont (lam (lam t)))

cont′ : ∀ {q A B Γ} {r : Size< q} → (Γ , A) , A ⊢⟨ {!q!} ⟩ B → Γ , A ⊢⟨ {!!} ⟩ B
cont′ t = det (app ccont (lam (lam t)))


module VecSN where

open import Data.Bool

infixr 20 _◎_

------------------------------------------------------------------------------
-- fix a field F_3 = {0, 1, -1}
-- types B (ZERO,ONE,PLUS,TIMES) determine the DIMENSION of a vector space over F_3
-- values of type B are INDICES for B-dimensional vectors
-- we do not allow superpositions (we have AT MOST one entry in the
-- vector that is non-zero), so we can identify the INDICES with the vectors
--
-- in particular:
--   - ZERO is not the empty type! it is like the 0-dimensional vector space
--     (i.e, the type containing one "annihilating value")
--   - ONE is like a 1-dimensional vector space; it is isomorphic to
--     the scalars (0,+1,-1)
--   - PLUS gives the direct sum of vector spaces (dimension is the sum)
--   - TIMES gives the tensor product of vector spaces (dimension is the product)
--   - DUAL gives the dual space (functionals that map vectors to scalars; i.e.
--     that maps values to scalars)

data B : Set where
  ZERO   : B
  ONE    : B
  PLUS   : B → B → B
  TIMES  : B → B → B
  DUAL   : B → B

-- now we describe the vectors for each B-dimensional vector space
-- the zero vector is everywhere

data BVAL : B → Set where
  zero : {b : B} → BVAL b
  unit : BVAL ONE
  left : {b₁ b₂ : B} → BVAL b₁ → BVAL (PLUS b₁ b₂)
  right : {b₁ b₂ : B} → BVAL b₂ → BVAL (PLUS b₁ b₂)
  pair : {b₁ b₂ : B} → BVAL b₁ → BVAL b₂ → BVAL (TIMES b₁ b₂)
  dual : {b : B} → BVAL b → BVAL (DUAL b)

-- syntactic equality on vectors

b= : { b : B } → BVAL b → BVAL b → Bool
b= zero zero = true
b= unit unit = true
b= (left v₁) (left v₂) = b= v₁ v₂
b= (right v₁) (right v₂) = b= v₁ v₂
b= (pair v₁ v₂) (pair v₁' v₂') = b= v₁ v₁' ∧ b= v₂ v₂'
b= (dual v₁) (dual v₂) = b= v₁ v₂
b= _ _ = false

data Iso : B → B → Set where
  -- (+,0) commutative monoid
  unite₊  : { b : B } → Iso (PLUS ZERO b) b
  uniti₊  : { b : B } → Iso b (PLUS ZERO b)
  swap₊   : { b₁ b₂ : B } → Iso (PLUS b₁ b₂) (PLUS b₂ b₁)
  assocl₊ : { b₁ b₂ b₃ : B } → Iso (PLUS b₁ (PLUS b₂ b₃)) (PLUS (PLUS b₁ b₂) b₃)
  assocr₊ : { b₁ b₂ b₃ : B } → Iso (PLUS (PLUS b₁ b₂) b₃) (PLUS b₁ (PLUS b₂ b₃))
  -- (*,1) commutative monoid
  unite⋆  : { b : B } → Iso (TIMES ONE b) b
  uniti⋆  : { b : B } → Iso b (TIMES ONE b)
  swap⋆   : { b₁ b₂ : B } → Iso (TIMES b₁ b₂) (TIMES b₂ b₁)
  assocl⋆ : { b₁ b₂ b₃ : B } → Iso (TIMES b₁ (TIMES b₂ b₃)) (TIMES (TIMES b₁ b₂) b₃)
  assocr⋆ : { b₁ b₂ b₃ : B } → Iso (TIMES (TIMES b₁ b₂) b₃) (TIMES b₁ (TIMES b₂ b₃))
  -- * distributes over +
  dist    : { b₁ b₂ b₃ : B } →
            Iso (TIMES (PLUS b₁ b₂) b₃) (PLUS (TIMES b₁ b₃) (TIMES b₂ b₃))
  factor  : { b₁ b₂ b₃ : B } →
            Iso (PLUS (TIMES b₁ b₃) (TIMES b₂ b₃)) (TIMES (PLUS b₁ b₂) b₃)
  -- closure
  id⟷   : { b : B } → Iso b b
  sym    : { b₁ b₂ : B } → Iso b₁ b₂ → Iso b₂ b₁
  _◎_    : { b₁ b₂ b₃ : B } → Iso b₁ b₂ → Iso b₂ b₃ → Iso b₁ b₃
  _⊕_    : { b₁ b₂ b₃ b₄ : B } →
           Iso b₁ b₃ → Iso b₂ b₄ → Iso (PLUS b₁ b₂) (PLUS b₃ b₄)
  _⊗_    : { b₁ b₂ b₃ b₄ : B } →
           Iso b₁ b₃ → Iso b₂ b₄ → Iso (TIMES b₁ b₂) (TIMES b₃ b₄)
  -- multiplicative duality
  refe⋆   : { b : B } → Iso (DUAL (DUAL b)) b
  refi⋆   : { b : B } → Iso b (DUAL (DUAL b))
  rile⋆   : { b : B } → Iso (TIMES b (TIMES b (DUAL b))) b
  rili⋆   : { b : B } → Iso b (TIMES b (TIMES b (DUAL b)))
  -- negatives: we have a circuit that requires the value to choose
  -- the incoming value : b₁ can go left or right
  choose : { b₁ b₂ : B } → Iso b₁ b₂ → Iso b₁ b₂ → Iso b₁ b₂

mutual

  eval : {b₁ b₂ : B} → Iso b₁ b₂ → BVAL b₁ → BVAL b₂
  eval unite₊ (left _) = zero
  eval unite₊ (right v) = v
  eval uniti₊ v = right v
  eval swap₊ (left v) = right v
  eval swap₊ (right v) = left v
  eval assocl₊ (left v) = left (left v)
  eval assocl₊ (right (left v)) = left (right v)
  eval assocl₊ (right (right v)) = right v
  eval assocr₊ (left (left v)) = left v
  eval assocr₊ (left (right v)) = right (left v)
  eval assocr₊ (right v) = right (right v)
  eval unite⋆ (pair unit v) = v
  eval uniti⋆ v = pair unit v
  eval swap⋆ (pair v1 v2) = pair v2 v1
  eval assocl⋆ (pair v1 (pair v2 v3)) = pair (pair v1 v2) v3
  eval assocr⋆ (pair (pair v1 v2) v3) = pair v1 (pair v2 v3)
  eval dist (pair (left v1) v3) = left (pair v1 v3)
  eval dist (pair (right v2) v3) = right (pair v2 v3)
  eval factor (left (pair v1 v3)) = pair (left v1) v3
  eval factor (right (pair v2 v3)) = pair (right v2) v3
  eval id⟷ v = v
  eval (sym c) v = evalB c v
  eval (c₁ ◎ c₂) v = eval c₂ (eval c₁ v)
  eval (c₁ ⊕ c₂) (left v) = left (eval c₁ v)
  eval (c₁ ⊕ c₂) (right v) = right (eval c₂ v)
  eval (c₁ ⊗ c₂) (pair v₁ v₂) = pair (eval c₁ v₁) (eval c₂ v₂)
  eval refe⋆ (dual (dual v)) = v
  eval refi⋆ v = dual (dual v)
  eval rile⋆ (pair v (pair v₁ (dual v₂))) with b= v₁ v₂
  eval rile⋆ (pair v (pair v₁ (dual v₂))) | true = v
  eval rile⋆ (pair v (pair v₁ (dual v₂))) | false = zero
  eval rili⋆ v = pair v (pair v (dual v))
-- choose : { b₁ b₂ : B } → Iso b₁ b₂ → Iso b₁ b₂ → Iso b₁ b₂
  eval (choose c₁ c₂) v = {!!}
  eval _ _ = zero

  evalB : {b₁ b₂ : B} → Iso b₁ b₂ → BVAL b₂ → BVAL b₁
  evalB unite₊ v = right v
  evalB uniti₊ (left _) = zero
  evalB uniti₊ (right v) = v
  evalB swap₊ (left v) = right v
  evalB swap₊ (right v) = left v
  evalB assocl₊ (left (left v)) = left v
  evalB assocl₊ (left (right v)) = right (left v)
  evalB assocl₊ (right v) = right (right v)
  evalB assocr₊ (left v) = left (left v)
  evalB assocr₊ (right (left v)) = left (right v)
  evalB assocr₊ (right (right v)) = right v
  evalB unite⋆ v = pair unit v
  evalB uniti⋆ (pair unit v) = v
  evalB swap⋆ (pair v1 v2) = pair v2 v1
  evalB assocl⋆ (pair (pair v1 v2) v3) = pair v1 (pair v2 v3)
  evalB assocr⋆ (pair v1 (pair v2 v3)) = pair (pair v1 v2) v3
  evalB dist (left (pair v1 v3)) = pair (left v1) v3
  evalB dist (right (pair v2 v3)) = pair (right v2) v3
  evalB factor (pair (left v1) v3) = left (pair v1 v3)
  evalB factor (pair (right v2) v3) = right (pair v2 v3)
  evalB id⟷ v = v
  evalB (sym c) v = eval c v
  evalB (c₁ ◎ c₂) v = evalB c₁ (evalB c₂ v)
  evalB (c₁ ⊕ c₂) (left v) = left (evalB c₁ v)
  evalB (c₁ ⊕ c₂) (right v) = right (evalB c₂ v)
  evalB (c₁ ⊗ c₂) (pair v₁ v₂) = pair (evalB c₁ v₁) (evalB c₂ v₂)
  evalB refe⋆ v = dual (dual v)
  evalB refi⋆ (dual (dual v)) = v
  evalB rile⋆ v = pair v (pair v (dual v))
  evalB rili⋆ (pair v (pair v₁ (dual v₂))) with b= v₁ v₂
  evalB rili⋆ (pair v (pair v₁ (dual v₂))) | true = v
  evalB rili⋆ (pair v (pair v₁ (dual v₂))) | false = zero
  evalB _ _ = zero

------------------------------------------------------------------------------
-- example with duals

pibool : B
pibool = PLUS ONE ONE

pitrue : BVAL pibool
pitrue = left unit

pifalse : BVAL pibool
pifalse = right unit

-- swap clause 1 = (T => F)
clause1 : BVAL (TIMES (DUAL pibool) pibool)
clause1 = pair (dual pitrue) pifalse

-- swap clause 2 = (F => T)
clause2 : BVAL (TIMES (DUAL pibool) pibool)
clause2 = pair (dual pifalse) pitrue

-- swap clause 1 applied to true
ex1 : BVAL (TIMES pibool (TIMES (DUAL pibool) pibool))
ex1 = pair pitrue clause1

-- swap clause 1 applied to false
ex2 : BVAL (TIMES pibool (TIMES (DUAL pibool) pibool))
ex2 = pair pifalse clause1

-- swap clause 1 applied to true
ex3 : BVAL (TIMES pibool (TIMES (DUAL pibool) pibool))
ex3 = pair pitrue clause2

-- swap clause 1 applied to false
ex4 : BVAL (TIMES pibool (TIMES (DUAL pibool) pibool))
ex4 = pair pifalse clause2

-- applies one of the clauses to a value
c : Iso (TIMES pibool (TIMES (DUAL pibool) pibool)) pibool
c =         -- (v,(1/t,f))
  assocl⋆ ◎ -- ((v,1/t),f)
  swap⋆ ◎   -- (f,(v,1/t))
  rile⋆     -- f or zero

--
v1 = eval c ex1
v2 = eval c ex2
v3 = eval c ex3
v4 = eval c ex4

-- generalize

-- applies one of the clauses of a function : b -> b to a value : b
apply : {b : B} → Iso (TIMES b (TIMES (DUAL b) b)) b
apply = assocl⋆ ◎ swap⋆ ◎ rile⋆

{--
want:

value is True
in parallel apply clause1 to value and apply clause2 to value
one of these will return False and the other will return zero
merge the results to get False

better:

value is True
we create a new logic variable ALPHA which can be unified with either clause1 or clause2
we create a value (ALPHA - ALPHA) which we apply to evaluate

--}

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


{-

          Types Summer School 2007

                 Bertinoro
             Aug 19 - 31, 2007

                   Agda

                Ulf Norell

-}

{-

  Learn more about Agda on the Agda wiki:

    http://www.cs.chalmers.se/~ulfn/Agda

  This is where you find the exercises for the afternoon.

-}

-- Each Agda file contains a top-level module, whose
-- name corresponds to the file name.

module Basics where

{-

  Expressions (types and terms)

-}

-- The expression language of Agda is your favorite dependently
-- typed λ-calculus.

-- For instance:
id₁ : (A : Set) -> A -> A
id₁ = \ A x -> x

id₂ : (A : Set) -> A -> A
id₂ = \ A x -> id₁ A (id₁ A x)

-- Note: Agda likes white space. This is not correct:
--   id:(A:Set)->A->A

-- Why not? In Agda the following strings are valid identifiers:
--   id:
--   A:Set
--   ->A->A

-- Another useful function, featuring telescopes
-- and typed λs.
compose : (A B C : Set) -> (B -> C) -> (A -> B) -> A -> C
compose = \(A B C : Set) f g x -> f (g x)

compose' : (A B : Set)(C : B -> Set)
           (f : (x : B) -> C x)(g : A -> B) ->
           (x : A) -> C (g x)
compose' = \A B C f g x -> f (g x)

{-

  Implicit arguments

-}

-- Writing down type arguments explicitly soon gets old.
-- Enter implicit arguments.

-- Note the curlies in the telescope. And A mysteriously disappeares
-- in the definition.
id₃ : {A : Set} -> A -> A
id₃ = \ x -> x

-- And it's not there when applying the function.
id₄ : {A : Set} -> A -> A
id₄ = \ x -> (id₃ (id₃ x))

-- If you think the type checker should figure out the value of
-- something explicit, you write _.
id₆ : {A : Set} -> A -> A
id₆ x = id₁ _ x

-- Interesting though it is, eventually you'll get bored
-- with the λ-calculus...

-- Move on to: Datatypes.agda


module Common where

open import Data.Bool
open import Data.Maybe
open import Data.Sum
open import Data.Product

BoolQ : Set₁
BoolQ = (A : Set) -> A -> A -> A

unBoolQ : {A : Set} -> A -> A -> BoolQ -> A
unBoolQ a a' q = q _ a a'

trueQ : BoolQ
trueQ = \_ a a' -> a

falseQ : BoolQ
falseQ = \_ a a' -> a'

fromBoolQ : BoolQ -> Bool
fromBoolQ q = unBoolQ true false q

toBoolQ : Bool -> BoolQ
toBoolQ true  = trueQ
toBoolQ false = falseQ

MaybeQ : Set -> Set₁
MaybeQ A = (B : Set) -> (A -> B) -> B -> B

unMaybeQ : {A B : Set} -> (A -> B) -> B -> MaybeQ A -> B
unMaybeQ f b q = q _ f b

justQ : {A : Set} -> A -> MaybeQ A
justQ a = \_ f b -> f a

nothingQ : {A : Set} -> MaybeQ A
nothingQ = \_ f b -> b

fromMaybeQ : {A : Set} -> MaybeQ A -> Maybe A
fromMaybeQ q = unMaybeQ just nothing q

toMaybeQ : {A : Set} -> Maybe A -> MaybeQ A
toMaybeQ (just a) = justQ a
toMaybeQ nothing  = nothingQ

EitherQ : Set -> Set -> Set₁
EitherQ A B = (C : Set) -> (A -> C) -> (B -> C) -> C

unEitherQ : {A B C : Set} -> (A -> C) -> (B -> C) -> EitherQ A B -> C
unEitherQ f g q = q _ f g

leftQ : {A B : Set} -> A -> EitherQ A B
leftQ a = \_ f g -> f a

rightQ : {A B : Set} -> B -> EitherQ A B
rightQ b = \_ f g -> g b

fromEitherQ : {A B : Set} -> EitherQ A B -> A ⊎ B
fromEitherQ q = unEitherQ inj₁ inj₂ q

toEitherQ : {A B : Set} -> A ⊎ B -> EitherQ A B
toEitherQ (inj₁ a) = leftQ a
toEitherQ (inj₂ b) = rightQ b

PairQ : Set -> Set -> Set₁
PairQ A B = (C : Set) -> (A -> B -> C) -> C

unPairQ : {A B C : Set} -> (A -> B -> C) -> PairQ A B -> C
unPairQ f q = q _ f

pairQ : {A B : Set} -> A -> B -> PairQ A B
pairQ a b = \_ f -> f a b

fromPairQ : {A B : Set} -> PairQ A B -> A × B
fromPairQ q = unPairQ (\a b -> (a , b)) q

toPairQ : {A B : Set} -> A × B -> PairQ A B
toPairQ (a , b) = pairQ a b

fstQ : {A B : Set} -> PairQ A B -> A
fstQ q = unPairQ (\a b -> a) q

sndQ : {A B : Set} -> PairQ A B -> B
sndQ q = unPairQ (\a b -> b) q


-- Example by Simon Huber

{-# OPTIONS --cubical-compatible #-}

data _≡_ {A : Set} (a : A) : A → Set where
  refl : a ≡ a

ap : {A B : Set} (f : A → B) {a b : A} (p : a ≡ b) → f a ≡ f b
ap f refl = refl

-- \bub
_•_ : {A : Set} {a b c : A} → a ≡ b → b ≡ c → a ≡ c
p • refl = p

infixr 30 _•_

! : {A : Set} {a b : A} → a ≡ b → b ≡ a
! refl = refl

-- \.  (NB: not • aka \bub)
_∙_ : {A : Set} {B : A → Set}
      {f g : (a : A) → B a} →
      f ≡ g →
      (x : A) → f x ≡ g x
refl ∙ x = refl

infix 30 _∙_

dotap : {A B : Set} {f g : A → B}
        (p : f ≡ g) (x : A)
      → p  ∙ x ≡ ap (λ F → F x) p
dotap refl x = refl

apcomp : {A B C : Set} (f : B → C) (g : A → B)
         {x y : A} (p : x ≡ y)
       → ap (λ a → f (g a)) p ≡ ap f (ap g p)
apcomp f g refl = refl

-- combinators for equality reasoning
_≡⟨_⟩_ : {A : Set} (x : A) {y z : A} → x ≡ y → y ≡ z → x ≡ z
x ≡⟨ p ⟩ q = p • q

infixr 2 _≡⟨_⟩_

_□ : {A : Set} (x : A) → x ≡ x
x □ = refl

module Wrong (A B : Set) (a0 : A) where
  const : B → (A → B)
  const = λ x _ → x

  to : {x y : B} → x ≡ y → const x ≡ const y
  to = ap const

  from : {x y : B} → const x ≡ const y → x ≡ y
  from = ap (λ F → F a0)

  -- This lemma should not typecheck:
  lem : {x y : B} (p : const x ≡ const y) (a : A)
      → p ∙ a ≡ to (from p) ∙ a
  lem p a = p ∙ a
            ≡⟨ dotap p a ⟩
            ap (λ F → F a) p
            ≡⟨ refl ⟩
            ap (λ F → const (F a) a) p
            ≡⟨ apcomp _ _ p ⟩
            ap (λ G → G a) (ap (λ F → const (F a)) p)
            ≡⟨ ap (ap (λ G → G a)) (apcomp const (λ F → F a) p) ⟩
            ap(λ G → G a) (ap const (ap (λ (F : A → B) → F a) p))
            ≡⟨ ! (dotap (ap const (ap (λ F → F a) p)) a) ⟩
            (to (from p) ∙ a) □


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of products
------------------------------------------------------------------------

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

module Data.Product.Properties where

open import Data.Product
open import Function using (_∘_)
open import Relation.Binary using (Decidable)
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary.Product
import Relation.Nullary.Decidable as Dec

------------------------------------------------------------------------
-- Equality (dependent)

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

  ,-injectiveˡ : ∀ {a c} {b : B a} {d : B c} → (a , b) ≡ (c , d) → a ≡ c
  ,-injectiveˡ refl = refl

  -- See also Data.Product.Properties.WithK.,-injectiveʳ.

------------------------------------------------------------------------
-- Equality (non-dependent)

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

  ,-injectiveʳ : ∀ {a c : A} {b d : B} → (a , b) ≡ (c , d) → b ≡ d
  ,-injectiveʳ refl = refl

  ,-injective : ∀ {a c : A} {b d : B} → (a , b) ≡ (c , d) → a ≡ c × b ≡ d
  ,-injective refl = refl , refl

  ≡-dec : Decidable {A = A} _≡_ → Decidable {A = B} _≡_ →
          Decidable {A = A × B} _≡_
  ≡-dec dec₁ dec₂ (a , b) (c , d) =
    Dec.map′ (uncurry (cong₂ _,_)) ,-injective (dec₁ a c ×-dec dec₂ b d)


------------------------------------------------------------------------------
-- Well-founded induction on the lexicographic order on natural numbers
------------------------------------------------------------------------------

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

-- From the thesis: The induction principle $\Conid{Lexi-wfind}$ is
-- proved by well-founded induction on the usual order $\Conid{LT}$ on
-- (partial) natural numbers which, in turn, can be proved by pattern
-- matching on the proof that the numbers are total.

module FOT.FOTC.Data.Nat.Induction.NonAcc.LexicographicI where

open import FOTC.Base
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.EliminationPropertiesI
open import FOTC.Data.Nat.Inequalities.PropertiesI

open import FOTC.Data.Nat.Induction.NonAcc.WF-I
open module WFI = FOTC.Data.Nat.Induction.NonAcc.WF-I.WFInd

open import FOTC.Data.Nat.Type

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

Lexi-wfind :
  (A : D → D → Set) →
  (∀ {m₁ n₁} → N m₁ → N n₁ →
     (∀ {m₂ n₂} → N m₂ → N n₂ → Lexi m₂ n₂ m₁ n₁ → A m₂ n₂) → A m₁ n₁) →
  ∀ {m n} → N m → N n → A m n
Lexi-wfind A h {m} Nm Nn = <-wfind {!!} {!!} {!!}


-- labeled λ-calculus

module IntrinsicallyTypedLLC where

open import Data.List
open import Data.List.Relation.Unary.All
open import Data.List.Base
open import Data.Vec hiding (_++_)
open import Data.Unit hiding (_≤_)
open import Data.Nat hiding (_≤_)
open import Data.Fin.Subset
open import Data.Fin.Subset.Properties
open import Data.Fin hiding (_≤_)
open import Data.Product
open import Data.Empty
open import Relation.Binary

-- definitions

data Ty (nl : ℕ) : Set where   -- nl ~ (max.) number of labels
  Tunit : Ty nl
  Tlabel : Subset nl → Ty nl
  Tfun : Ty nl → Ty nl → Ty nl

TEnv : ℕ → Set
TEnv nl = List (Ty nl)

data _≤_ {nl} : Ty nl → Ty nl → Set where
  Sunit  : Tunit ≤ Tunit
  Slabel : ∀ {snl snl'} → snl ⊆ snl' → (Tlabel snl) ≤ (Tlabel snl')
  Sfun   : ∀ {A A' B B'} → A' ≤ A → B ≤ B' → (Tfun A B) ≤ (Tfun A' B')

data _∈`_ {nl : ℕ} : Ty nl → TEnv nl → Set where
  here  : ∀ {lt φ} → lt ∈` (lt ∷ φ)
  there : ∀ {lt lt' φ} → lt ∈` φ → lt ∈` (lt' ∷ φ)

data Exp {nl : ℕ} : TEnv nl → Ty nl → Set where
  Unit     : ∀ {φ} → Exp φ Tunit
  Var      : ∀ {φ t} → (x : t ∈` φ) → Exp φ t   -- t ∈` φ gives us the position of "x" in env
  SubType  : ∀ {A A' φ} →  Exp φ A → A ≤ A'
                        →  Exp φ A'
  Lab-I    : ∀ {l snl φ} → l ∈ snl → Exp φ (Tlabel snl)
  Lab-E    : ∀ {snl φ B} → Exp φ (Tlabel snl)
                         → (∀ l → l ∈ snl → Exp φ B)
                         → Exp φ B
  Abs     : ∀ {B A φ} → Exp (A ∷ φ) B
                      → Exp φ (Tfun A B)
  App     : ∀ {A B φ} → Exp φ (Tfun A B)
                      → (ex : Exp φ A)
                      → Exp φ B

-- subtyping properties

≤-trans : ∀ {nl} {t t' t'' : Ty nl} → t ≤ t' → t' ≤ t'' → t ≤ t''
≤-trans Sunit Sunit = Sunit
≤-trans (Slabel snl⊆snl') (Slabel snl'⊆snl'') = Slabel (⊆-trans snl⊆snl' snl'⊆snl'')
≤-trans (Sfun a'≤a b≤b') (Sfun a''≤a' b'≤b'') = Sfun (≤-trans a''≤a' a'≤a) (≤-trans b≤b' b'≤b'')

≤-refl : ∀ {nl} → (t : Ty nl) → t ≤ t
≤-refl Tunit = Sunit
≤-refl (Tlabel x) = Slabel (⊆-refl)
≤-refl (Tfun t t') = Sfun (≤-refl t) (≤-refl t')

-- big-step semantics

Val : ∀ {nl} → Ty nl → Set
Val Tunit = Data.Unit.⊤
Val {nl} (Tlabel snl) = Σ (Fin nl) (λ l → l ∈ snl)
Val (Tfun ty ty₁) = (Val ty) → (Val ty₁)

coerce : ∀ {nl} {t t' : Ty nl} → t ≤ t' → Val t → Val t' -- supertype of a Value is also a Value
coerce Sunit t = tt
coerce (Slabel snl⊆snl') (Finnl , Finnl∈snl) = (Finnl , (snl⊆snl' Finnl∈snl))
coerce (Sfun A'≤A B≤B') f = λ x → coerce B≤B' (f (coerce A'≤A x))

access : ∀ {nl} {t : Ty nl} {φ} → t ∈` φ → All Val φ → Val t
access here (px ∷ ρ) = px
access (there x) (px ∷ ρ) = access x ρ

eval : ∀ {nl φ t} → Exp {nl} φ t → All Val φ → Val t
eval Unit ϱ = tt
eval (Var x) ϱ = access x ϱ
eval (SubType e a≤a') ϱ = coerce a≤a' (eval e ϱ)
eval (Lab-I {l} l∈snl) ϱ = l , (l∈snl)
eval (Lab-E e case) ϱ with eval e ϱ
... | lab , lab∈nl = eval (case lab lab∈nl) ϱ
eval (Abs e) ϱ = λ x → eval e (x ∷ ϱ)
eval (App e e₁) ϱ = (eval e ϱ) (eval e₁ ϱ)

-- small-step semantics
-- substitution taken from PLFA

ext : ∀ {nl φ ψ} → (∀ {A : Ty nl} → A ∈` φ → A ∈` ψ)
                 → (∀ {A B} → A ∈` (B ∷ φ) → A ∈` (B ∷ ψ))
ext ϱ here      = here
ext ϱ (there x) = there (ϱ x)

rename : ∀ {nl φ ψ} → (∀ {A : Ty nl} → A ∈` φ → A ∈` ψ)
                    → (∀ {A} → Exp φ A → Exp ψ A)
rename ϱ Unit                    = Unit
rename ϱ (Var x)                 = Var (ϱ x)
rename ϱ (SubType expr:A' A'≤A)  = SubType (rename ϱ expr:A') A'≤A
rename {ψ} ϱ (Lab-I l∈snl)       = Lab-I {ψ} l∈snl
rename ϱ (Lab-E expr:snl case)   = Lab-E (rename ϱ expr:snl)
                                   λ l l∈snl → (rename ϱ (case l l∈snl))
rename ϱ (Abs expr:B)            = Abs (rename (ext ϱ) expr:B)
rename ϱ (App expr:A->B expr:A)  = App (rename ϱ expr:A->B) (rename ϱ expr:A)

exts : ∀ {nl φ ψ} → (∀ {A : Ty nl} → A ∈` φ → Exp ψ A)
                  → (∀ {A B} → A ∈` (B ∷ φ) → Exp (B ∷ ψ) A)
exts ϱ here      = Var (here)
exts ϱ (there x) = rename there (ϱ x)

subst : ∀ {nl φ ψ} → (∀ {A : Ty nl} → A ∈` φ → Exp ψ A) -- simult. substitution
                   → (∀ {A : Ty nl} → Exp φ A → Exp ψ A)
subst ϱ Unit                       = Unit
subst ϱ (Var x)                    = ϱ x
subst ϱ (SubType expr:A' A'≤A)     = SubType (subst ϱ expr:A') A'≤A
subst {ψ} ϱ (Lab-I l∈snl)          = Lab-I {ψ} l∈snl
subst ϱ (Lab-E expr:snl case)      = Lab-E (subst ϱ expr:snl)
                                     λ l l∈snl → (subst ϱ (case l l∈snl))
subst ϱ (Abs expr:B)               = Abs (subst (exts ϱ) expr:B)
subst ϱ (App expr:A→B expr:A)     = App (subst ϱ expr:A→B) (subst ϱ expr:A)

_[[_]] : ∀ {nl φ} {A B : Ty nl} → Exp (B ∷ φ) A → Exp φ B → Exp φ A -- single substitution
_[[_]] {nl} {φ} {A} {B} N M = subst {nl} {B ∷ φ} {φ} ϱ {A} N
  where
  ϱ : ∀ {A} → A ∈` (B ∷ φ) → Exp φ A
  ϱ here      = M
  ϱ (there x) = Var x

expansionlemma : ∀ {nl} {lt : Ty nl} {φ φ'} → lt ∈` φ' → lt ∈` (φ' ++ φ)
expansionlemma here      = here
expansionlemma (there x) = there (expansionlemma x)

extensionlemma : ∀ {nl} {lt : Ty nl} {φ φ'} → lt ∈` φ → lt ∈` (φ' ++ φ)
extensionlemma {φ' = []}        here     = here
extensionlemma {φ' = x ∷ xs}   here     = there (extensionlemma{φ' = xs} here)
extensionlemma {φ' = []}       (there y) = there y
extensionlemma {φ' = x ∷ xs}  (there y) = there (extensionlemma {φ' = xs} (there y))

inextdebr : ∀ {nl} {B A : Ty nl} {φ' φ} → B ∈` (φ' ++ φ) → B ∈` (φ' ++ (A ∷ φ))
inextdebr {φ' = []} here           = there here
inextdebr {φ' = []} (there x)      = there (there x)
inextdebr {φ' = x ∷ xs} here      = here
inextdebr {φ' = x ∷ xs} (there y) = there (inextdebr{φ' = xs} y)

inext : ∀ {nl} {φ φ'} {A B : Ty nl} → Exp (φ' ++ φ) B → Exp (φ' ++ (A ∷ φ)) B
inext Unit                                 = Unit
inext {φ' = φ'} (Var x)                    = Var (inextdebr{φ' = φ'} x)
inext {φ = φ}{φ' = φ'} (SubType expr b≤b') = SubType (inext{φ = φ}{φ' = φ'} expr) b≤b'
inext (Lab-I x)                            = Lab-I x
inext {φ = φ} {φ' = φ'} (Lab-E x x₁)       = Lab-E (inext{φ = φ}{φ' = φ'} x) λ l x₂ → inext{φ = φ}{φ' = φ'} (x₁ l x₂)
inext {nl} {φ} {φ'} (Abs{A = A°} x)        = Abs (inext{φ = φ}{φ' = A° ∷ φ'} x)
inext {φ = φ} {φ' = φ'} (App x x₁)         = App (inext{φ = φ}{φ' = φ'} x) (inext{φ = φ}{φ' = φ'} x₁)

debrsub : ∀ {nl} {B B' A A' : Ty nl} {φ' φ} → B ∈` (φ' ++ (A ∷ φ)) → A' ≤ A → B ≤ B' → Exp (φ' ++ (A' ∷ φ)) B'
debrsub {φ' = []}  here a'≤a b≤b'          = SubType (Var here) (≤-trans a'≤a b≤b')
debrsub {φ' = []} (there x) a'≤a b≤b'      = SubType (Var (there x)) b≤b'
debrsub {φ' = x ∷ xs} here a'≤a b≤b'      = SubType (Var (here)) b≤b'
debrsub {φ' = x ∷ xs} (there z) a'≤a b≤b' = inext{φ' = []}{A = x} (debrsub{φ' = xs} z a'≤a b≤b')

typesub : ∀ {nl φ φ' A B A' B'} → Exp{nl} (φ' ++ (A ∷ φ)) B → A' ≤ A → B ≤ B' → Exp (φ' ++ (A' ∷ φ)) B'  -- subtyping "substitution"
typesub Unit a'≤a Sunit = Unit
typesub {φ = φ} {φ'} {A} {B} {A'} {B'} (Var x) a'≤a b≤b'                        = debrsub{φ' = φ'}{φ = φ} x a'≤a b≤b'
typesub {nl} {φ} {φ'} (SubType expr x) a'≤a b≤b'                                = typesub{nl}{φ}{φ'} expr a'≤a (≤-trans x b≤b')
typesub (Lab-I l∈snl) a'≤a b≤b'                                                 = SubType (Lab-I l∈snl) b≤b'
typesub {nl} {φ} {φ'} (Lab-E{snl = snl} expr cases) a'≤a b≤b'                   = Lab-E (typesub{nl}{φ}{φ'} expr a'≤a (≤-refl (Tlabel snl))) λ l x → typesub{nl}{φ}{φ'} (cases l x) a'≤a b≤b'
typesub {φ' = φ'}{A = A}{B = A°→B°}{A' = A'}{B' = A°°→B°°} (Abs{A = A°} expr) a'≤a (Sfun{A = .A°}{A' = A°°}{B = B°}{B' = B°°} A°°≤A° B°≤B°°)
                                                                                = SubType (Abs (typesub{φ' = A° ∷ φ'} expr a'≤a B°≤B°°)) (Sfun A°°≤A° (≤-refl B°°))
typesub {nl}{φ}{φ'}{A}{B}{A'}{B'} (App{A = A°}{B = .B} expr expr') a'≤a b≤b'    = SubType (App (typesub{nl}{φ}{φ'} expr a'≤a (≤-refl (Tfun A° B))) (typesub{nl}{φ}{φ'} expr' a'≤a (≤-refl A°))) b≤b'

-- we force values to have type SubType, since Lab-I results in expressions with type {l}
-- and we want to keep the information about which subset l is in
data Val' {n φ} : (t : Ty n) → Exp {n} φ t → Set where
  Vunit :  Val' (Tunit) Unit
  Vlab : ∀ {l snl l∈snl} → Val' (Tlabel snl) (Lab-I{l = l}{snl} l∈snl)
  Vfun : ∀ {A B exp} → Val' (Tfun A B) (Abs exp)

data _~>_ {n φ} : {A : Ty n} → Exp {n} φ A → Exp {n} φ A → Set where  -- small-steps semantics relation (call-by-value)

  ξ-App1 : ∀ {A B} {L L' : (Exp φ (Tfun B A))} {M}
           → L ~> L'
           → App L M ~> App L' M

  ξ-App2 : ∀ {A B} {M M' : Exp φ A} {L : Exp φ (Tfun A B)}
           → Val' (Tfun A B) L
           → M ~> M'
           → App L M ~> App L M'

  β-App : ∀ {A B M exp}
          → Val' B M
          → App{B = A} (Abs exp) M
             ~>
             (exp [[ M ]])

  ξ-SubType : ∀ {A A' A≤A' } {L L' : Exp φ A}
              → L ~> L'
              → SubType{A = A}{A'} L A≤A' ~> SubType{A = A} L' A≤A'

  ξ-Lab-E : ∀ {A snl} {L L' : Exp φ (Tlabel snl)} {cases}
            → L ~> L'
            → Lab-E{B = A} L cases ~> Lab-E L' cases

  β-Lab-E : ∀ {A l snl l∈snl cases}
            → Lab-E{B = A} (Lab-I{l = l}{snl} l∈snl) cases
               ~>
               cases l (l∈snl)

  γ-Lab-I : ∀ {l snl snl'} {l∈snl : l ∈ snl} {snl⊆snl' : snl ⊆ snl'}
            → SubType (Lab-I{l = l}{snl = snl} l∈snl) (Slabel snl⊆snl')
               ~>
               Lab-I (snl⊆snl' l∈snl)

  γ-Abs : ∀ {A B A' B' e} {A'≤A : A' ≤ A} {B≤B' : B ≤ B'}
          → SubType (Abs{B = B}{A = A} e) (Sfun A'≤A B≤B')
             ~>
             Abs{B = B'}{A = A'} (typesub{φ' = []} e A'≤A B≤B')

  γ-SubType : ∀ {A A' A'' A≤A' A'≤A'' expr}
              → SubType{A = A'}{A' = A''} (SubType{A = A} expr A≤A') A'≤A''
                 ~>
                 SubType expr (≤-trans A≤A' A'≤A'')

  -- either we define Unit values to be SubTypes of Unit≤Unit; or we introducte the following rule
  β-SubType-Unit : SubType Unit Sunit
                   ~>
                   Unit

-- properties of small-step evaluation
infix 2 _~>>_ -- refl. transitive closure
infix 1 begin_
infixr 2 _~>⟨_⟩_
infix 3 _∎

data _~>>_ : ∀ {n} {φ} {A : Ty n} → Exp φ A → Exp φ A → Set where
  _∎ : ∀ {n φ} {A : Ty n} (L : Exp φ A)
       → L ~>> L

  _~>⟨_⟩_ : ∀ {n φ} {A : Ty n} (L : Exp φ A) {M N : Exp φ A}
           → L ~> M
           → M ~>> N
           → L ~>> N

begin_ : ∀ {n φ} {A : Ty n} {M N : Exp φ A} → M ~>> N → M ~>> N
begin M~>>N = M~>>N

-- progress theorem
data Progress {n A} (M : Exp{n} [] A) : Set where
  step : ∀ {N : Exp [] A} → M ~> N → Progress M
  done : Val' A M → Progress M

-- proof
progress : ∀ {n A} → (M : Exp{n} [] A) → Progress M
progress Unit                                                                             = done Vunit
progress (Var ())                                                         -- Var requires a proof for A ∈ [] which cannot exist
progress (SubType Unit Sunit)                                                             = step β-SubType-Unit
progress (SubType (Var ()) A'≤A)
progress (SubType (SubType expr:A' x) A'≤A)                                               = step γ-SubType
progress (SubType (Lab-I{l}{snl} l∈snl) (Slabel{snl' = snl'} snl⊆snl'))                  = step γ-Lab-I
progress (SubType (Lab-E expr:A' x) A'≤A) with progress (Lab-E expr:A' x)
...                                           | step a                                    = step (ξ-SubType a)
...                                           | done ()                   -- Lab-E without SubType can't be a value
progress (SubType (Abs expr:A') (Sfun A'≤A B≤B')) with progress (Abs expr:A')
...                                           | step a                                    = step (ξ-SubType a)
...                                           | done Vfun                                 = step γ-Abs
progress (SubType (App expr:A' expr:A'') A'≤A) with progress (expr:A')
...                                           | step a                                    = step (ξ-SubType (ξ-App1 a))
...                                           | done Vfun with progress (expr:A'')
...                                              | step b                                 = step (ξ-SubType (ξ-App2 Vfun b))
...                                              | done val                               = step (ξ-SubType (β-App val))
progress (Lab-I l∈snl)                                                                    = done Vlab
progress (Lab-E expr cases) with progress expr
...                                           | step expr~>expr'                          = step (ξ-Lab-E expr~>expr')
...                                           | done Vlab                                 = step (β-Lab-E)
progress (Abs expr)                                                                       = done Vfun
progress (App L M) with progress L
...                                           | step L~>L'                                = step (ξ-App1 L~>L')
...                                           | done Vfun with progress M
...                                              | step M~>M'                             = step (ξ-App2 Vfun M~>M')
...                                              | done x                                 = step (β-App x)

-- generation of evaluation sequences
-- taken from plfa

data Gas : Set where
  gas : ℕ → Gas

data Finished {n φ A} (N : Exp{n} φ A) : Set where
  done : Val' A N → Finished N
  out-of-gas : Finished N

data Steps : ∀ {n A} → Exp{n} [] A → Set where
  steps : ∀ {n A} {L N : Exp{n} [] A}
          → L ~>> N
          → Finished N
          → Steps L

eval' : ∀ {n A} → Gas → (L : Exp{n} [] A) → Steps L
eval' (gas zero) L              = steps (L ∎) out-of-gas
eval' (gas (suc m)) L with progress L
...      | done VL              = steps (L ∎) (done VL)
...      | step {M} L~>M with eval' (gas m) M
...         | steps M~>>N fin   = steps (L ~>⟨ L~>M ⟩ M~>>N) fin

-- examples
-- (λ (x : Unit) → x) (Unit)
ex0 : Exp{suc zero} [] Tunit
ex0 = App (Abs (Unit{φ = (Tunit ∷ [])})) (Unit)

_ : ex0 ~>> Unit
_ =
  begin
    App (Abs (Unit)) (Unit)
  ~>⟨ β-App (Vunit) ⟩
    Unit
  ∎

ex1 : Exp{suc zero} [] Tunit
ex1 = Lab-E (Lab-I (x∈⁅x⁆ zero)) λ l x → Unit

_ : ex1 ~>> Unit
_ =
  begin
    Lab-E (Lab-I (x∈⁅x⁆ zero)) (λ l x → Unit)
  ~>⟨ β-Lab-E ⟩
    Unit
  ∎

ex2 : Exp{suc zero} [] Tunit
ex2 = App (SubType (Abs Unit) (Sfun Sunit Sunit)) Unit

-- proof that {inside, outside} ⊆ {inside, inside}
-- i.e.                     {0} ⊆ {0, 1}
x⊆y : (inside ∷ outside ∷ []) ⊆ (inside ∷ inside ∷ [])
x⊆y {.zero}    here      = here
x⊆y {.(suc (suc _))} (there (there ()))

-- proof that zero is in (inside ∷ outside ∷ [])
-- i.e.                    0 ∈ {0}
l∈snl : (zero) ∈ (inside ∷ outside ∷ [])
l∈snl = here

-- [({0, 1}→{0, 1} <: {0}→{0, 1}) (λ x : {0, 1} . x)] 0
ex3 : Exp{suc (suc zero)} [] (Tlabel (inside ∷ inside ∷ []))
ex3 = App (SubType (Abs{A = Tlabel (inside ∷ inside ∷ [])}  (Var here)) (Sfun (Slabel{snl = (inside ∷ outside ∷ [])} x⊆y) (≤-refl (Tlabel (inside ∷ inside ∷ [])))))
          (Lab-I l∈snl)


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

module SystemT where

  data _∈_ {A : Set} : (x : A) (l : List A) → Set where -- type \in
    i0 : {x : A} {xs : List A} → x ∈ (x ∷ xs)
    iS : {x y : A} {xs : List A} → x ∈ xs → x ∈ (y ∷ xs)

  data TType : Set where
    base  : TType
    _⟶_ : TType → TType → TType

  Context = List TType
  _,,_ : Context → TType → Context
  Γ ,, τ = τ ∷ Γ

  infixr 10 _⟶_
  infixr 9 _,,_
  infixr 8 _⊢_

  data _⊢_ (Γ : Context) : TType → Set where
    -- Some constant of the base type whose type is immediate.
    c : Γ ⊢ base
    -- Variable.
    var : {τ : TType} → τ ∈ Γ → Γ ⊢ τ
    -- Function introduction.
    lam : {τ₁ τ₂ : TType} → Γ ,, τ₁ ⊢ τ₂ → Γ ⊢ τ₁ ⟶ τ₂
    -- Function application.
    app : {τ₁ τ₂ : TType} → (Γ ⊢ τ₁ ⟶ τ₂) → Γ ⊢ τ₁ → Γ ⊢ τ₂

  module Semantics (B : Set) (elB : B) where

    -- Interpretation of System T types to Agda types.
    ⟦_⟧t : TType → Set
    ⟦ base ⟧t = B
    ⟦ τ₁ ⟶ τ₂ ⟧t = ⟦ τ₁ ⟧t → ⟦ τ₂ ⟧t

    -- Interpretation of System T contexts to Agda types.
    ⟦_⟧c : Context → Set
    ⟦ [] ⟧c = ⊤
    ⟦ τ ∷ Γ ⟧c = ⟦ Γ ⟧c × ⟦ τ ⟧t

    -- Interpretation of terms.
    ⟦_⟧ : {Γ : Context} {τ : TType} → (Γ ⊢ τ) → ⟦ Γ ⟧c → ⟦ τ ⟧t
    ⟦ c ⟧ γ = elB
    ⟦ var x ⟧ γ = {!  !}
    ⟦ lam e ⟧ γ x = {!   !}
    ⟦ app e₁ e₂ ⟧ γ = {!   !}


data Unit : Set where
  unit : Unit

data Maybe (A : Set) : Set where
  nothing : Maybe A
  just : A → Maybe A

data Test : Set where
  map : (Unit → Maybe Test) → Test

-- Accepted:
foo : Test → Unit
foo-aux : Maybe Test → Unit
foo (map f) = foo-aux (f unit)
foo-aux nothing = unit
foo-aux (just x) = foo x

test : Test → Unit
test (map f) with f unit
test (map f) | nothing = unit
test (map f) | just x = test x

-- WAS: Termination checker complains
-- SHOULD: succeed


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

module Lang.Size where

-- Some stuff about sizes that seems to :
-- • Types:
--   • SizeU   : TYPE
--   • Size    : TYPE
--   • <ˢⁱᶻᵉ_  : Size → TYPE
--   • 𝐒ˢⁱᶻᵉ   : Size → Size
--   • ∞ˢⁱᶻᵉ   : Size
--   • _⊔ˢⁱᶻᵉ_ : Size → Size → Size
-- • Subtyping           : ∀s₁∀s₂. (s₁: <ˢⁱᶻᵉ s₂) → (s₁: Size)
-- • Almost irreflexivity: ∀(s: Size). (s ≠ ∞ˢⁱᶻᵉ) → ¬(s: <ˢⁱᶻᵉ s)
-- • Transitivity        : ∀s₁∀s₂∀s₃. (s₁: <ˢⁱᶻᵉ s₂) → (s₂: <ˢⁱᶻᵉ s₃) → (s₁: <ˢⁱᶻᵉ s₃)
-- • Successor           : ∀(s: Size). s: <ˢⁱᶻᵉ 𝐒ˢⁱᶻᵉ(s)
-- • Maximum             : ∀(s: Size). s: <ˢⁱᶻᵉ ∞ˢⁱᶻᵉ
-- • Successor of maximum: 𝐒ˢⁱᶻᵉ(∞ˢⁱᶻᵉ) = ∞ˢⁱᶻᵉ
-- • Max function left   : ∀(s₁: Size)∀(s₂: Size)∀(s₃: Size). ((s₁: <ˢⁱᶻᵉ s₃) ∧ (s₂: <ˢⁱᶻᵉ s₃)) → (((s₁ ⊔ˢⁱᶻᵉ s₂)): <ˢⁱᶻᵉ s₃)
-- • Max function right  : ∀(s₁: Size)∀(s₂: Size)∀(s₃: Size). ((s₁: <ˢⁱᶻᵉ s₂) ∨ (s₁: <ˢⁱᶻᵉ s₃)) → (s₁: <ˢⁱᶻᵉ (s₂ ⊔ˢⁱᶻᵉ s₃))
-- • Max of maximum left : ∀(s: Size). s ⊔ˢⁱᶻᵉ ∞ˢⁱᶻᵉ = ∞ˢⁱᶻᵉ
-- • Max of maximum right: ∀(s: Size). ∞ˢⁱᶻᵉ ⊔ˢⁱᶻᵉ s = ∞ˢⁱᶻᵉ
-- TODO: What is SizeU? See https://github.com/agda/agda/blob/cabe234d3c784e20646636ad082cc1e04ddf007b/src/full/Agda/TypeChecking/Rules/Builtin.hs#L294 , https://github.com/agda/agda/blob/1eec63b1c5566b252c0a4a815ce1df99a772c475/src/full/Agda/TypeChecking/Primitive/Base.hs#L134

{-# BUILTIN SIZEUNIV SizeU #-}
{-# BUILTIN SIZE     Size    #-}
{-# BUILTIN SIZELT   <ˢⁱᶻᵉ_  #-}
{-# BUILTIN SIZESUC  𝐒ˢⁱᶻᵉ   #-}
{-# BUILTIN SIZEINF  ∞ˢⁱᶻᵉ   #-}
{-# BUILTIN SIZEMAX  _⊔ˢⁱᶻᵉ_ #-}

{-
private
  module Test where
    open import Relator.Equals

    types-SizeU : TYPE
    types-SizeU = SizeU

    types-Size : TYPE
    types-Size = Size

    types-<ˢⁱᶻᵉ : Size → TYPE
    types-<ˢⁱᶻᵉ = <ˢⁱᶻᵉ_

    types-𝐒ˢⁱᶻᵉ : Size → Size
    types-𝐒ˢⁱᶻᵉ = 𝐒ˢⁱᶻᵉ

    types-∞ˢⁱᶻᵉ : Size
    types-∞ˢⁱᶻᵉ = ∞ˢⁱᶻᵉ

    types-_⊔ˢⁱᶻᵉ_ : Size → Size → Size
    types-_⊔ˢⁱᶻᵉ_ = _⊔ˢⁱᶻᵉ_

    subtyping : ∀{s₂ : Size}{s₁ : <ˢⁱᶻᵉ s₂} → Size
    subtyping {s₁ = s₁} = s₁

    reflexivity-of-maximum : <ˢⁱᶻᵉ ∞ˢⁱᶻᵉ
    reflexivity-of-maximum = ∞ˢⁱᶻᵉ

    transitivity : ∀{s₃ : Size}{s₂ : <ˢⁱᶻᵉ s₃}{s₁ : <ˢⁱᶻᵉ s₂} → (<ˢⁱᶻᵉ s₃)
    transitivity {s₁ = s₁} = s₁

    maximum : ∀{s : Size} → <ˢⁱᶻᵉ ∞ˢⁱᶻᵉ
    maximum{s} = s

    successor-of-maximum : 𝐒ˢⁱᶻᵉ ∞ˢⁱᶻᵉ ≡ ∞ˢⁱᶻᵉ
    successor-of-maximum = [≡]-intro

    max-of-maximumₗ : ∀{s : Size} → (∞ˢⁱᶻᵉ ⊔ˢⁱᶻᵉ s ≡ ∞ˢⁱᶻᵉ)
    max-of-maximumₗ = [≡]-intro

    max-of-maximumᵣ : ∀{s : Size} → (s ⊔ˢⁱᶻᵉ ∞ˢⁱᶻᵉ ≡ ∞ˢⁱᶻᵉ)
    max-of-maximumᵣ = [≡]-intro

    max-function-left : ∀{s₃ : Size}{s₁ : <ˢⁱᶻᵉ s₃}{s₂ : <ˢⁱᶻᵉ s₃} → (<ˢⁱᶻᵉ s₃)
    max-function-left {s₁ = s₁}{s₂ = s₂} = s₁ ⊔ˢⁱᶻᵉ s₂

    max-function-rightₗ : ∀{s₂ s₃ : Size}{s₁ : <ˢⁱᶻᵉ s₂} → (<ˢⁱᶻᵉ (s₂ ⊔ˢⁱᶻᵉ s₃))
    max-function-rightₗ {s₁ = s₁} = s₁

    max-function-rightᵣ : ∀{s₂ s₃ : Size}{s₁ : <ˢⁱᶻᵉ s₃} → (<ˢⁱᶻᵉ (s₂ ⊔ˢⁱᶻᵉ s₃))
    max-function-rightᵣ {s₁ = s₁} = s₁

    -- TODO: Is this supposed to not work? This is: ∀(sₗ₁ : Size)∀(sₗ₂ : Size)∀(sᵣ : Size) → (sₗ₁ <ˢⁱᶻᵉ sₗ₂) → ((sₗ₁ ⊔ˢⁱᶻᵉ sᵣ) <ˢⁱᶻᵉ (sₗ₂ ⊔ˢⁱᶻᵉ sᵣ))
    max-should-work? : ∀{sₗ₂ sᵣ : Size}{sₗ₁ : <ˢⁱᶻᵉ sₗ₂} → (<ˢⁱᶻᵉ (sₗ₂ ⊔ˢⁱᶻᵉ sᵣ))
    max-should-work? {sᵣ = sᵣ}{sₗ₁ = sₗ₁} = sₗ₁ ⊔ˢⁱᶻᵉ sᵣ
-}


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

module FOT.Common.FOL.Existential.Syntax where

-- We add 3 to the fixities of the Agda standard library 0.8.1 (see
-- Relation.Binary.Core).
infix 7 _≡_

postulate
  D    : Set
  _≡_  : D → D → Set
  refl : ∀ {d} → d ≡ d
  d    : D

module ∃₁ where

  -- We add 3 to the fixities of the Agda standard library 0.8.1 (see
  -- Data/Product.agda, Data/Sum.agda and Relation/Nullary/Core.agda).
  infixr 7 _,_
  infix  5 ∃

  data ∃ (P : D → Set) : Set where
    _,_ : (x : D) → P x → ∃ P

  syntax ∃ (λ x → e)  = ∃[ x ] e

  t₁ : ∃ λ x → x ≡ x
  t₁ = d , refl

  t₂ : ∃[ x ] x ≡ x
  t₂ = d , refl

  t₃ : ∃ λ x → ∃ λ y → x ≡ y
  t₃ = d , d , refl

  t₄ : ∃[ x ] ∃[ y ] x ≡ y
  t₄ = d , d ,  refl

module ∃₂ where
  infixr 7 _,_,_

  data ∃₂ (P : D → D → Set) : Set where
    _,_,_ : (x y : D) → P x y → ∃₂ P

  -- Agda issue: 536
  -- syntax ∃₂ (λ x y → e) = ∃₂[ x , y ] e

  t₁ : ∃₂ λ x y → x ≡ y
  t₁ = d , d , refl


module Stack where

open import Prelude public

-- Stacks, or snoc-lists.

data Stack (X : Set) : Set where
  ∅   : Stack X
  _,_ : Stack X → X → Stack X

-- Stack membership, or de Bruijn indices.

module _ {X : Set} where
  infix 3 _∈_
  data _∈_ (A : X) : Stack X → Set where
    top : ∀ {Γ}   → A ∈ Γ , A
    pop : ∀ {Γ B} → A ∈ Γ → A ∈ Γ , B

  ⌊_⌋∈ : ∀ {Γ A} → A ∈ Γ → Nat
  ⌊ top ⌋∈   = zero
  ⌊ pop i ⌋∈ = suc ⌊ i ⌋∈

  i₀ : ∀ {Γ A} → A ∈ Γ , A
  i₀ = top

  i₁ : ∀ {Γ A B} → A ∈ Γ , A , B
  i₁ = pop i₀

  i₂ : ∀ {Γ A B C} → A ∈ Γ , A , B , C
  i₂ = pop i₁

-- Stack inclusion, or order-preserving embeddings.

module _ {X : Set} where
  infix 3 _⊆_
  data _⊆_ : Stack X → Stack X → Set where
    bot  : ∀ {Γ}      → ∅ ⊆ Γ
    skip : ∀ {Γ Γ′ A} → Γ ⊆ Γ′ → Γ ⊆ Γ′ , A
    keep : ∀ {Γ Γ′ A} → Γ ⊆ Γ′ → Γ , A ⊆ Γ′ , A

  refl⊆ : ∀ {Γ} → Γ ⊆ Γ
  refl⊆ {∅}     = bot
  refl⊆ {Γ , A} = keep refl⊆

  trans⊆ : ∀ {Γ Γ′ Γ″} → Γ ⊆ Γ′ → Γ′ ⊆ Γ″ → Γ ⊆ Γ″
  trans⊆ bot      η′        = bot
  trans⊆ η        (skip η′) = skip (trans⊆ η η′)
  trans⊆ (skip η) (keep η′) = skip (trans⊆ η η′)
  trans⊆ (keep η) (keep η′) = keep (trans⊆ η η′)

  weak⊆ : ∀ {Γ A} → Γ ⊆ Γ , A
  weak⊆ = skip refl⊆

-- Monotonicity of stack membership with respect to stack inclusion.

module _ {X : Set} where
  mono∈ : ∀ {Γ Γ′ : Stack X} {A} → Γ ⊆ Γ′ → A ∈ Γ → A ∈ Γ′
  mono∈ bot      ()
  mono∈ (skip η) i       = pop (mono∈ η i)
  mono∈ (keep η) top     = top
  mono∈ (keep η) (pop i) = pop (mono∈ η i)

-- Pairs of stacks.

infixl 4 _⁏_
record Stack² (X Y : Set) : Set where
  constructor _⁏_
  field
    π₁ : Stack X
    π₂ : Stack Y

open Stack² public

-- Stack pair inclusion.

module _ {X Y : Set} where
  infix 3 _⊆²_
  _⊆²_ : Stack² X Y → Stack² X Y → Set
  Γ ⁏ Δ ⊆² Γ′ ⁏ Δ′ = Γ ⊆ Γ′ ∧ Δ ⊆ Δ′

  refl⊆² : ∀ {Γ Δ} → Γ ⁏ Δ ⊆² Γ ⁏ Δ
  refl⊆² = refl⊆ , refl⊆

  trans⊆² : ∀ {Γ Γ′ Γ″ Δ Δ′ Δ″} → Γ ⁏ Δ ⊆² Γ′ ⁏ Δ′ → Γ′ ⁏ Δ′ ⊆² Γ″ ⁏ Δ″ → Γ ⁏ Δ ⊆² Γ″ ⁏ Δ″
  trans⊆² (η , ρ) (η′ , ρ′) = trans⊆ η η′ , trans⊆ ρ ρ′


module sv20.assign2.Second_old4 where

open import Data.Bool as Bool using (Bool; true; false; T; _∨_; _∧_)
--open import Relation.Nullary using (¬_)
open import Data.Unit using (⊤; tt)
open import Data.Empty using (⊥; ⊥-elim)
open import Data.Nat using (ℕ; zero; suc; _+_; _*_)
open import Level using (Level; _⊔_; 0ℓ) renaming (suc to lsuc)
--open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Product using (_×_; ∃-syntax) renaming (_,_ to ⟨_,_⟩)
open import Function using (_∘_)
--import Relation.Binary.PropositionalEquality as Eq
--open Eq using (_≡_; refl; subst)

Subset : ∀ {α} (A : Set α) -> Set _
Subset A = A → Bool

_∈_ : ∀ {α} {A : Set α} → A → Subset A → Set
a ∈ p = T (p a)

--odd : Subset ℕ
--odd 0 = false
--odd 1 = true
--odd (suc (suc n)) = odd n
--
--_ : 3 ∈ odd
--_ = tt
--
--_ : Subset Set  -- I'm not actually sure, what this is
--_ = λ _ → true

-- _ : true ∈ odd -- Doesn't make sense. Type inference fails
--_ = ?

--∈or∉ : ∀ {A : Set} (SubA : Subset A) (a : A) → (a ∈ SubA) ⊎ ¬ (a ∈ SubA)
--∈or∉ subset a with subset a
--...              | true  = inj₁ tt
--...              | false = inj₂ ⊥-elim
--
--empty-⊥ : ∀ (sub : Subset ⊥) (a : ⊥) → ¬ (a ∈ sub)
--empty-⊥ _ ()

--¬⊥ : ∀ (A : Set) → ¬ (⊥ ∈ A)  -- doesn't make any sense
--¬⊥ = ?

Relation : ∀ {α β} (A : Set α) (B : Set β) → Set (α ⊔ β)
Relation A B = Subset (A × B)

--equalℕ : Relation ℕ ℕ
--equalℕ ⟨ zero    , zero    ⟩ = true
--equalℕ ⟨ zero    , (suc n) ⟩ = false
--equalℕ ⟨ (suc m) , zero    ⟩ = false
--equalℕ ⟨ (suc m) , (suc n) ⟩ = equalℕ ⟨ m , n ⟩

--_ : equalℕ ⟨ 3 , 4 ⟩ ≡ false
--_ = refl

_∪_ : ∀ {A : Set} → Subset A → Subset A → Subset A
A ∪ B = λ x → (A x) ∨ (B x)

_∩_ : ∀ {A : Set} → Subset A → Subset A → Subset A
A ∩ B = λ x → (A x) ∧ (B x)

_⊆_ : ∀ {A : Set} → Subset A → Subset A → Set
A ⊆ B = ∀ x → x ∈ A → x ∈ B

wholeSet : ∀ (A : Set) → Subset A
wholeSet _ = λ _ → true

--odd⊆ℕ : odd ⊆ wholeSet ℕ
----odd⊆ℕ zero ()
----odd⊆ℕ 1 tt = tt
----odd⊆ℕ (suc (suc n)) _ = tt
---- or, simply
--odd⊆ℕ _ _ = tt

-- In general
∀subset⊆set : ∀ {A : Set} {sub : Subset A} → sub ⊆ wholeSet A
∀subset⊆set = λ _ _ → tt

record _⇔_ (A B : Set) : Set where
  field
    to   : A → B
    from : B → A

record Range (A B : Set) : Set where
  field
    range : Relation A B → Subset B
    def : ∀ (rel : Relation A B) (b : B)
        → (∃[ a ] (⟨ a , b ⟩ ∈ rel))
          ⇔ (b ∈ range rel)

--lemma : ∀ {A B : Set}
--        (F G : Relation A B)
--        (b : B)
--      → ∃[ a ] (⟨ a , b ⟩ ∈ (F ∩ G))
--      → ∃[ a ] ((⟨ a , b ⟩ ∈ F) × (⟨ a , b ⟩ ∈ G))
--lemma f g b ⟨ a , f∩ga ⟩ with f ⟨ a , b ⟩ | g ⟨ a , b ⟩
--...                         | true        | true         = ⟨ ? , ⟨ ? , ? ⟩ ⟩
--...                         | true        | false        = ⊥-elim f∩ga
--...                         | false       | true         = ⊥-elim f∩ga
--...                         | false       | false        = ⊥-elim f∩ga
--
--module range-props {A B : Set} (rng : Range A B) where
--  range = Range.range rng
--  def   = Range.def rng
--
--  range-∩-⊆ : (F G : Relation A B)
--            → range (F ∩ G) ⊆ (range F ∩ range G)
--  range-∩-⊆ f g b = ?
--    where
--      --def-rngf-→ = _⇔_.to (def f b)
--
--      -- b ∈ range (f ∩ g) → ∃[ a ] (⟨ a , b ⟩ ∈ (f ∩ g))
--      def-rngf-← = _⇔_.from (def (f ∩ g) b)
--      -- b ∈ range (f ∩ g) → ∃[ a ] ((⟨ a , b ⟩ ∈ F) × (⟨ a , b ⟩ ∈ G))
--      lemma' = (lemma f g b) ∘ def-rngf-←

-- Class exercise
-- Dammit! All definitions of subset I have found for agda make this problem not a problem
exercise103a : ∀ {a} {A B : Subset a}
             → (∀ x → x ∈ A → x ∈ B)
             → A ⊆ B
exercise103a ∀x→x∈A→x∈B = ∀x→x∈A→x∈B