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

open import lib.Basics
open import lib.types.Nat
open import lib.types.Group
open import lib.types.TLevel

module lib.types.Int where

data ℤ : Type₀ where
  O : ℤ
  pos : (n : ℕ) → ℤ
  neg : (n : ℕ) → ℤ

Int = ℤ

succ : ℤ → ℤ
succ O = pos O
succ (pos n) = pos (S n)
succ (neg O) = O
succ (neg (S n)) = neg n

pred : ℤ → ℤ
pred O = neg O
pred (pos O) = O
pred (pos (S n)) = pos n
pred (neg n) = neg (S n)

abstract
  succ-pred : (n : ℤ) → succ (pred n) == n
  succ-pred O = idp
  succ-pred (pos O) = idp
  succ-pred (pos (S n)) = idp
  succ-pred (neg n) = idp

  pred-succ : (n : ℤ) → pred (succ n) == n
  pred-succ O = idp
  pred-succ (pos n) = idp
  pred-succ (neg O) = idp
  pred-succ (neg (S n)) = idp

succ-equiv : ℤ ≃ ℤ
succ-equiv = equiv succ pred succ-pred pred-succ

pred-injective : (z₁ z₂ : ℤ) → pred z₁ == pred z₂ → z₁ == z₂
pred-injective z₁ z₂ p = ! (succ-pred z₁) ∙ ap succ p ∙ succ-pred z₂

succ-injective : (z₁ z₂ : ℤ) → succ z₁ == succ z₂ → z₁ == z₂
succ-injective z₁ z₂ p = ! (pred-succ z₁) ∙ ap pred p ∙ pred-succ z₂

{- Converting between ℤ, ℕ, and ℕ₋₂ -}

ℤ-to-ℕ₋₂ : ℤ → ℕ₋₂
ℤ-to-ℕ₋₂ O = ⟨0⟩
ℤ-to-ℕ₋₂ (pos m) = S ⟨ m ⟩
ℤ-to-ℕ₋₂ (neg O) = ⟨-1⟩
ℤ-to-ℕ₋₂ (neg _) = ⟨-2⟩

ℕ-to-ℤ : ℕ → ℤ
ℕ-to-ℤ n = pred (pos n)

{- Proof that [ℤ] has decidable equality and hence is a set -}

private
  ℤ-get-pos : ℤ → ℕ
  ℤ-get-pos O = 0
  ℤ-get-pos (pos n) = n
  ℤ-get-pos (neg n) = 0

  ℤ-get-neg : ℤ → ℕ
  ℤ-get-neg O = 0
  ℤ-get-neg (pos n) = 0
  ℤ-get-neg (neg n) = n

  ℤ-neg≠O≠pos-type : ℤ → Type₀
  ℤ-neg≠O≠pos-type O = Unit
  ℤ-neg≠O≠pos-type (pos n) = Empty
  ℤ-neg≠O≠pos-type (neg n) = Empty

  ℤ-neg≠pos-type : ℤ → Type₀
  ℤ-neg≠pos-type O = Unit
  ℤ-neg≠pos-type (pos n) = Empty
  ℤ-neg≠pos-type (neg n) = Unit

abstract
  pos-injective : (n m : ℕ) (p : pos n == pos m) → n == m
  pos-injective n m p = ap ℤ-get-pos p

  neg-injective : (n m : ℕ) (p : neg n == neg m) → n == m
  neg-injective n m p = ap ℤ-get-neg p

  ℤ-O≠pos : (n : ℕ) → O ≠ pos n
  ℤ-O≠pos n p = transport ℤ-neg≠O≠pos-type p unit

  ℤ-pos≠O : (n : ℕ) → pos n ≠ O
  ℤ-pos≠O n p = transport ℤ-neg≠O≠pos-type (! p) unit

  ℤ-O≠neg : (n : ℕ) → O ≠ neg n
  ℤ-O≠neg n p = transport ℤ-neg≠O≠pos-type p unit

  ℤ-neg≠O : (n : ℕ) → neg n ≠ O
  ℤ-neg≠O n p = transport ℤ-neg≠O≠pos-type (! p) unit

  ℤ-neg≠pos : (n m : ℕ) → neg n ≠ pos m
  ℤ-neg≠pos n m p = transport ℤ-neg≠pos-type p unit

  ℤ-pos≠neg : (n m : ℕ) → pos n ≠ neg m
  ℤ-pos≠neg n m p = transport ℤ-neg≠pos-type (! p) unit

  ℤ-has-dec-eq : has-dec-eq ℤ
  ℤ-has-dec-eq O O = inl idp
  ℤ-has-dec-eq O (pos n) = inr (ℤ-O≠pos n)
  ℤ-has-dec-eq O (neg n) = inr (ℤ-O≠neg n)
  ℤ-has-dec-eq (pos n) O = inr (ℤ-pos≠O n)
  ℤ-has-dec-eq (pos n) (pos m) with ℕ-has-dec-eq n m
  ℤ-has-dec-eq (pos n) (pos m) | inl p = inl (ap pos p)
  ℤ-has-dec-eq (pos n) (pos m) | inr p⊥ = inr (λ p → p⊥ (pos-injective n m p))
  ℤ-has-dec-eq (pos n) (neg m) = inr (ℤ-pos≠neg n m)
  ℤ-has-dec-eq (neg n) O = inr (ℤ-neg≠O n)
  ℤ-has-dec-eq (neg n) (pos m) = inr (ℤ-neg≠pos n m)
  ℤ-has-dec-eq (neg n) (neg m) with ℕ-has-dec-eq n m
  ℤ-has-dec-eq (neg n) (neg m) | inl p = inl (ap neg p)
  ℤ-has-dec-eq (neg n) (neg m) | inr p⊥ = inr (λ p → p⊥ (neg-injective n m p))

  ℤ-is-set : is-set ℤ
  ℤ-is-set = dec-eq-is-set ℤ-has-dec-eq

ℤ-level = ℤ-is-set

{-
  ℤ is also a group!
-}

-- inv
ℤ~ : ℤ → ℤ
ℤ~ O = O
ℤ~ (pos n) = neg n
ℤ~ (neg n) = pos n

-- comp
_ℤ+_ : ℤ → ℤ → ℤ
O         ℤ+ z = z
pos O     ℤ+ z = succ z
pos (S n) ℤ+ z = succ (pos n ℤ+ z)
neg O     ℤ+ z = pred z
neg (S n) ℤ+ z = pred (neg n ℤ+ z)

-- unit-l
ℤ+-unit-l : ∀ z → O ℤ+ z == z
ℤ+-unit-l _ = idp

-- unit-r
private
  ℤ+-unit-r-pos : ∀ n → pos n ℤ+ O == pos n
  ℤ+-unit-r-pos O     = idp
  ℤ+-unit-r-pos (S n) = ap succ $ ℤ+-unit-r-pos n

  ℤ+-unit-r-neg : ∀ n → neg n ℤ+ O == neg n
  ℤ+-unit-r-neg O     = idp
  ℤ+-unit-r-neg (S n) = ap pred $ ℤ+-unit-r-neg n

ℤ+-unit-r : ∀ z → z ℤ+ O == z
ℤ+-unit-r O = idp
ℤ+-unit-r (pos n) = ℤ+-unit-r-pos n
ℤ+-unit-r (neg n) = ℤ+-unit-r-neg n

-- assoc
succ-ℤ+ : ∀ z₁ z₂ → succ z₁ ℤ+ z₂ == succ (z₁ ℤ+ z₂)
succ-ℤ+ O _ = idp
succ-ℤ+ (pos n) _ = idp
succ-ℤ+ (neg O) _ = ! $ succ-pred _
succ-ℤ+ (neg (S n)) _ = ! $ succ-pred _

pred-ℤ+ : ∀ z₁ z₂ → pred z₁ ℤ+ z₂ == pred (z₁ ℤ+ z₂)
pred-ℤ+ O _ = idp
pred-ℤ+ (neg n) _ = idp
pred-ℤ+ (pos O) _ = ! $ pred-succ _
pred-ℤ+ (pos (S n)) _ = ! $ pred-succ _

private
  ℤ+-assoc-pos : ∀ n₁ z₂ z₃ → (pos n₁ ℤ+ z₂) ℤ+ z₃ == pos n₁ ℤ+ (z₂ ℤ+ z₃)
  ℤ+-assoc-pos O      z₂ z₃ = succ-ℤ+ z₂ z₃
  ℤ+-assoc-pos (S n₁) z₂ z₃ =
    succ (pos n₁ ℤ+ z₂) ℤ+ z₃     =⟨ succ-ℤ+ (pos n₁ ℤ+ z₂) z₃ ⟩
    succ ((pos n₁ ℤ+ z₂) ℤ+ z₃)   =⟨ ap succ $ ℤ+-assoc-pos n₁ z₂ z₃ ⟩
    succ (pos n₁ ℤ+ (z₂ ℤ+ z₃))   ∎

  ℤ+-assoc-neg : ∀ n₁ z₂ z₃ → (neg n₁ ℤ+ z₂) ℤ+ z₃ == neg n₁ ℤ+ (z₂ ℤ+ z₃)
  ℤ+-assoc-neg O      z₂ z₃ = pred-ℤ+ z₂ z₃
  ℤ+-assoc-neg (S n₁) z₂ z₃ =
    pred (neg n₁ ℤ+ z₂) ℤ+ z₃     =⟨ pred-ℤ+ (neg n₁ ℤ+ z₂) z₃ ⟩
    pred ((neg n₁ ℤ+ z₂) ℤ+ z₃)   =⟨ ap pred $ ℤ+-assoc-neg n₁ z₂ z₃ ⟩
    pred (neg n₁ ℤ+ (z₂ ℤ+ z₃))   ∎

ℤ+-assoc : ∀ z₁ z₂ z₃ → (z₁ ℤ+ z₂) ℤ+ z₃ == z₁ ℤ+ (z₂ ℤ+ z₃)
ℤ+-assoc O        _ _ = idp
ℤ+-assoc (pos n₁) _ _ = ℤ+-assoc-pos n₁ _ _
ℤ+-assoc (neg n₁) _ _ = ℤ+-assoc-neg n₁ _ _

private
  pos-S-ℤ+-neg-S : ∀ n₁ n₂ → pos (S n₁) ℤ+ neg (S n₂) == pos n₁ ℤ+ neg n₂
  pos-S-ℤ+-neg-S O      n₂ = idp
  pos-S-ℤ+-neg-S (S n₁) n₂ = ap succ $ pos-S-ℤ+-neg-S n₁ n₂

  neg-S-ℤ+-pos-S : ∀ n₁ n₂ → neg (S n₁) ℤ+ pos (S n₂) == neg n₁ ℤ+ pos n₂
  neg-S-ℤ+-pos-S O      n₂ = idp
  neg-S-ℤ+-pos-S (S n₁) n₂ = ap pred $ neg-S-ℤ+-pos-S n₁ n₂

  ℤ~-inv-r-pos : ∀ n → pos n ℤ+ neg n == O
  ℤ~-inv-r-pos O     = idp
  ℤ~-inv-r-pos (S n) =
    pos (S n) ℤ+ neg (S n)  =⟨ pos-S-ℤ+-neg-S n n ⟩
    pos n ℤ+ neg n          =⟨ ℤ~-inv-r-pos n ⟩
    O                       ∎

  ℤ~-inv-r-neg : ∀ n → neg n ℤ+ pos n == O
  ℤ~-inv-r-neg O     = idp
  ℤ~-inv-r-neg (S n) =
    neg (S n) ℤ+ pos (S n)  =⟨ neg-S-ℤ+-pos-S n n ⟩
    neg n ℤ+ pos n          =⟨ ℤ~-inv-r-neg n ⟩
    O                       ∎

ℤ~-inv-r : ∀ z → z ℤ+ ℤ~ z == O
ℤ~-inv-r O       = idp
ℤ~-inv-r (pos n) = ℤ~-inv-r-pos n
ℤ~-inv-r (neg n) = ℤ~-inv-r-neg n

ℤ~-inv-l : ∀ z → ℤ~ z ℤ+ z == O
ℤ~-inv-l O       = idp
ℤ~-inv-l (pos n) = ℤ~-inv-r-neg n
ℤ~-inv-l (neg n) = ℤ~-inv-r-pos n

ℤ-group-structure : GroupStructure ℤ
ℤ-group-structure = record
  { ident = O
  ; inv = ℤ~
  ; comp = _ℤ+_
  ; unitl = ℤ+-unit-l
  ; unitr = ℤ+-unit-r
  ; assoc = ℤ+-assoc
  ; invr = ℤ~-inv-r
  ; invl = ℤ~-inv-l
  }

ℤ-group : Group₀
ℤ-group = group _ ℤ-is-set ℤ-group-structure


------------------------------------------------------------------------
-- Code related to the paper "Higher Inductive Type Eliminators
-- Without Paths"
--
-- Nils Anders Danielsson
------------------------------------------------------------------------

-- Note that the code does not follow the paper exactly, and that some
-- of the code has been changed after the paper was published.

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

module README.HITs-without-paths where

import Equality
import Equality.Id
import Equality.Instances-related
import Equality.Path
import Equality.Path.Isomorphisms
import Equality.Propositional
import Function-universe
import H-level
import H-level.Truncation.Propositional
import Quotient
import Suspension

------------------------------------------------------------------------
-- 1: Introduction

-- The propositional truncation operator.

∥_∥ = H-level.Truncation.Propositional.∥_∥

-- Equality defined as an inductive family with a single constructor
-- refl.

_≡_ = Equality.Propositional._≡_

------------------------------------------------------------------------
-- 2: An Axiomatisation of Equality With J

-- The code uses an axiomatisation of "equality with J", as discussed
-- in the text. The axiomatisation is a little convoluted in order to
-- support using equality at all universe levels. Furthermore, also as
-- discussed in the text, the axiomatisation supports choosing
-- specific definitions for other functions, like cong, to make the
-- code more usable when it is instantiated with Cubical Agda paths
-- (for which the canonical definition of cong computes in a different
-- way than typical definitions obtained using J).

-- Equality and reflexivity.

≡-refl = Equality.Reflexive-relation

-- The J rule and its computation rule.

J-J-refl = Equality.Equality-with-J₀

-- Extended variants of the two definitions above.

Equivalence-relation⁺ = Equality.Equivalence-relation⁺
Equality-with-J       = Equality.Equality-with-J
Equality-with-paths   = Equality.Path.Equality-with-paths

-- The extended variants are inhabited for all universe levels if the
-- basic ones are inhabited for all universe levels.

J₀⇒Equivalence-relation⁺             = Equality.J₀⇒Equivalence-relation⁺
J₀⇒J                                 = Equality.J₀⇒J
Equality-with-J₀⇒Equality-with-paths =
  Equality.Path.Equality-with-J₀⇒Equality-with-paths

-- To see how the code is axiomatised, see the module header of, say,
-- Circle.

import Circle

-- Any two notions of equality satisfying the axioms are pointwise
-- isomorphic, and the isomorphisms map canonical proofs of
-- reflexivity to canonical proofs of reflexivity.
--
-- Note that in some cases the code uses bijections (_↔_) instead of
-- equivalences (_≃_).

instances-isomorphic =
  Equality.Instances-related.all-equality-types-isomorphic

-- Cubical Agda paths, the Cubical Agda identity type family, and a
-- definition of equality as an inductive family with a single
-- constructor refl are instances of the axioms. (The last instance is
-- for Equality-with-J rather than Equality-with-paths, because the
-- latter definition is defined in Cubical Agda, and the instance is
-- not.)

paths-instance            = Equality.Path.equality-with-paths
id-instance               = Equality.Id.equality-with-paths
inductive-family-instance = Equality.Propositional.equality-with-J

------------------------------------------------------------------------
-- 3: Homogeneous Paths

-- The path type constructor.

Path = Equality.Path._≡_

-- Zero and one.

0̲ = Equality.Path.0̲
1̲ = Equality.Path.1̲

-- Reflexivity.

reflᴾ = Equality.Path.refl

-- Minimum, maximum and negation.

min = Equality.Path.min
max = Equality.Path.max
-_  = Equality.Path.-_

-- The primitive transport operation.

open Equality.Path using (transport)

-- The J rule and transport-refl.

Jᴾ             = Equality.Path.elim
transport-refl = Equality.Path.transport-refl

-- Transitivity (more general than in the paper).

transᴾ = Equality.Path.htransʳ

------------------------------------------------------------------------
-- 4: Heterogeneous Paths

-- Pathᴴ.

Pathᴴ = Equality.Path.[_]_≡_

-- The eliminator elimᴾ for the propositional truncation operator.

module ∥_∥ where

  elimᴾ = H-level.Truncation.Propositional.elimᴾ′

-- Pathᴴ≡Path and Pathᴴ≃Path.

Pathᴴ≡Path = Equality.Path.heterogeneous≡homogeneous
Pathᴴ≃Path = Equality.Path.heterogeneous↔homogeneous

-- The lemma substᴾ.

substᴾ = Equality.Path.subst

-- The lemmas subst and subst-refl from the axiomatisation.

subst      = Equality.Equality-with-J.subst
subst-refl = Equality.Equality-with-J.subst-refl

-- The axiomatisation makes it possible to choose the implementations
-- of to-path and from-path.

to-path   = Equality.Path.Equality-with-paths.to-path
from-path = Equality.Path.Equality-with-paths.from-path

-- The code mostly uses a pointwise isomorphism between the arbitrary
-- notion of equality and paths instead of from-path and to-path.

≡↔≡ = Equality.Path.Derived-definitions-and-properties.≡↔≡

-- The lemmas subst≡substᴾ, subst≡≃Pathᴴ, subst≡→Pathᴴ and
-- subst≡→substᴾ≡ (which is formulated as a bijection).

subst≡substᴾ   = Equality.Path.Isomorphisms.subst≡subst
subst≡≃Pathᴴ   = Equality.Path.Isomorphisms.subst≡↔[]≡
subst≡→Pathᴴ   = Equality.Path.Isomorphisms.subst≡→[]≡
subst≡→substᴾ≡ = Equality.Path.Isomorphisms.subst≡↔subst≡

-- The lemmas congᴰ and congᴰ-refl from the axiomatisation.

congᴰ      = Equality.Equality-with-J.dcong
congᴰ-refl = Equality.Equality-with-J.dcong-refl

-- The lemmas congᴴ and congᴰᴾ.

congᴴ  = Equality.Path.hcong
congᴰᴾ = Equality.Path.dcong

-- The proofs congᴰ≡congᴰᴾ, congᴰᴾ≡congᴴ, congᴰ≡congᴴ and
-- dependent‐computation‐rule‐lemma.

congᴰ≡congᴰᴾ = Equality.Path.Isomorphisms.dcong≡dcong
congᴰᴾ≡congᴴ = Equality.Path.dcong≡hcong
congᴰ≡congᴴ = Equality.Path.Isomorphisms.dcong≡hcong
dependent‐computation‐rule‐lemma =
  Equality.Path.Isomorphisms.dcong-subst≡→[]≡

------------------------------------------------------------------------
-- 5: The Circle Without Paths

-- The circle and loop.

𝕊¹   = Circle.𝕊¹
loop = Circle.loop

-- The lemmas cong and cong-refl from the axiomatisation.

cong      = Equality.Equality-with-J.cong
cong-refl = Equality.Equality-with-J.cong-refl

-- The lemma non-dependent-computation-rule-lemma.

non-dependent-computation-rule-lemma =
  Equality.Path.Isomorphisms.cong-≡↔≡

-- The lemma trans, which is actually a part of the axiomatisation
-- that I use, but which can be proved using the J rule.

trans    = Equality.Equivalence-relation⁺.trans
J₀⇒trans = Equality.J₀⇒Equivalence-relation⁺

-- The lemma subst-const.

subst-const = Equality.Derived-definitions-and-properties.subst-const

-- The lemma congᴰ≡→cong≡.

congᴰ≡→cong≡ = Equality.Derived-definitions-and-properties.dcong≡→cong≡

-- Eliminators and computation rules.

module 𝕊¹ where

  elimᴾ     = Circle.elimᴾ
  recᴾ      = Circle.recᴾ
  elim      = Circle.elim
  elim-loop = Circle.elim-loop
  rec       = Circle.rec
  rec-loop  = Circle.rec-loop
  rec′      = Circle.rec′
  rec′-loop = Circle.rec′-loop

------------------------------------------------------------------------
-- 6: Set Quotients Without Paths

-- The definition of h-levels.

Contractible   = Equality.Derived-definitions-and-properties.Contractible
H-level        = H-level.H-level
Is-proposition = Equality.Derived-definitions-and-properties.Is-proposition
Is-set         = Equality.Derived-definitions-and-properties.Is-set

-- Set quotients.

_/_ = Quotient._/_

-- Some lemmas.

H-levelᴾ-suc≃H-levelᴾ-Pathᴴ = Equality.Path.H-level-suc↔H-level[]≡
index-irrelevant            = Equality.Path.index-irrelevant
transport-transport         = Equality.Path.transport∘transport
heterogeneous-irrelevance   = Equality.Path.heterogeneous-irrelevance
heterogeneous-UIP           = Equality.Path.heterogeneous-UIP
H-level≃H-levelᴾ            = Equality.Path.Isomorphisms.H-level↔H-level

-- A generalisation of Π-cong. Note that equality is provably
-- extensional in Cubical Agda.

Π-cong         = Function-universe.Π-cong
extensionality = Equality.Path.ext

-- Variants of the constructors.

[]-respects-relation = Quotient.[]-respects-relation
/-is-set             = Quotient./-is-set

-- Eliminators.

module _/_ where

  elimᴾ′ = Quotient.elimᴾ′
  elimᴾ  = Quotient.elimᴾ
  recᴾ   = Quotient.recᴾ
  elim   = Quotient.elim
  rec    = Quotient.rec

------------------------------------------------------------------------
-- 7: More Examples

-- Suspensions.

module Susp where

  Susp          = Suspension.Susp
  elimᴾ         = Suspension.elimᴾ
  recᴾ          = Suspension.recᴾ
  meridian      = Suspension.meridian
  elim          = Suspension.elim
  elim-meridian = Suspension.elim-meridian
  rec           = Suspension.rec
  rec-meridian  = Suspension.rec-meridian

-- The propositional truncation operator.

module Propositional-truncation where

  elimᴾ′  = H-level.Truncation.Propositional.elimᴾ′
  elimᴾ   = H-level.Truncation.Propositional.elimᴾ
  recᴾ    = H-level.Truncation.Propositional.recᴾ
  trivial = H-level.Truncation.Propositional.truncation-is-proposition
  elim    = H-level.Truncation.Propositional.elim
  rec     = H-level.Truncation.Propositional.rec

------------------------------------------------------------------------
-- 8: An Alternative Approach

-- Circle and Circleᴾ, combined into a single definition.

Circle = Circle.Circle

-- Circleᴾ≃Circle, circleᴾ and circle.

Circleᴾ≃Circle = Circle.Circle≃Circle
circleᴾ        = Circle.circleᴾ
circle         = Circle.circle

-- The computation rule for the higher constructor.

elim-loop-circle = Circle.elim-loop-circle

-- Alternative definitions of Circleᴾ≃Circle and circle that (at the
-- time of writing) do not give the "correct" computational behaviour
-- for the point constructor.

Circleᴾ≃Circle′ = Circle.Circle≃Circle′
circle′         = Circle.circle′

------------------------------------------------------------------------
-- 9: Discussion

-- The interval.

import Interval

-- Pushouts.

import Pushout

-- A general truncation operator.

import H-level.Truncation

-- The torus.

import Torus


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

module Categories.Category.Lift where

open import Level
open import Categories.Category
open import Categories.Functor using (Functor)

liftC : ∀ {o ℓ e} o′ ℓ′ e′ → Category o ℓ e → Category (o ⊔ o′) (ℓ ⊔ ℓ′) (e ⊔ e′)
liftC o′ ℓ′ e′ C = record
  { Obj       = Lift o′ Obj
  ; _⇒_       = λ X Y → Lift ℓ′ (lower X ⇒ lower Y)
  ; _≈_       = λ f g → Lift e′ (lower f ≈ lower g)
  ; id        = lift id
  ; _∘_       = λ f g → lift (lower f ∘ lower g)
  ; assoc     = lift assoc
  ; sym-assoc = lift sym-assoc
  ; identityˡ = lift identityˡ
  ; identityʳ = lift identityʳ
  ; identity² = lift identity²
  ; equiv     = record
    { refl  = lift Equiv.refl
    ; sym   = λ eq → lift (Equiv.sym (lower eq))
    ; trans = λ eq eq′ → lift (Equiv.trans (lower eq) (lower eq′))
    }
  ; ∘-resp-≈  = λ eq eq′ → lift (∘-resp-≈ (lower eq) (lower eq′))
  }
  where open Category C

liftF : ∀ {o ℓ e} o′ ℓ′ e′ (C : Category o ℓ e) → Functor C (liftC o′ ℓ′ e′ C)
liftF  o′ ℓ′ e′ C = record
  { F₀           = lift
  ; F₁           = lift
  ; identity     = lift refl
  ; homomorphism = lift refl
  ; F-resp-≈     = lift
  }
  where open Category C
        open Equiv

unliftF : ∀ {o ℓ e} o′ ℓ′ e′ (C : Category o ℓ e) → Functor (liftC o′ ℓ′ e′ C) C
unliftF o′ ℓ′ e′ C = record
  { F₀           = lower
  ; F₁           = lower
  ; identity     = refl
  ; homomorphism = refl
  ; F-resp-≈     = lower
  }
  where open Category C
        open Equiv


------------------------------------------------------------------------
-- The Agda standard library
--
-- Endomorphisms on a Set
------------------------------------------------------------------------

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

module Function.Endomorphism.Propositional {a} (A : Set a) where

open import Algebra using (Magma; Semigroup; Monoid)
open import Algebra.FunctionProperties.Core using (Op₂)
open import Algebra.Morphism; open Definitions
open import Algebra.Structures using (IsMagma; IsSemigroup; IsMonoid)

open import Data.Nat.Base using (ℕ; zero; suc; _+_)
open import Data.Nat.Properties using (+-0-monoid; +-semigroup)
open import Data.Product using (_,_)

open import Function
open import Function.Equality using (_⟨$⟩_)
open import Relation.Binary using (_Preserves_⟶_)
open import Relation.Binary.PropositionalEquality as P using (_≡_; refl)

import Function.Endomorphism.Setoid (P.setoid A) as Setoid

Endo : Set a
Endo = A → A

------------------------------------------------------------------------
-- Conversion back and forth with the Setoid-based notion of Endomorphism

fromSetoidEndo : Setoid.Endo → Endo
fromSetoidEndo = _⟨$⟩_

toSetoidEndo : Endo → Setoid.Endo
toSetoidEndo f = record
  { _⟨$⟩_ = f
  ; cong  = P.cong f
  }

------------------------------------------------------------------------
-- N-th composition

_^_ : Endo → ℕ → Endo
f ^ zero  = id
f ^ suc n = f ∘′ (f ^ n)

^-homo : ∀ f → Homomorphic₂ ℕ Endo _≡_ (f ^_) _+_ _∘′_
^-homo f zero    n = refl
^-homo f (suc m) n = P.cong (f ∘′_) (^-homo f m n)

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

∘-isMagma : IsMagma _≡_ (Op₂ Endo ∋ _∘′_)
∘-isMagma = record
  { isEquivalence = P.isEquivalence
  ; ∙-cong        = P.cong₂ _∘′_
  }

∘-magma : Magma _ _
∘-magma = record { isMagma = ∘-isMagma }

∘-isSemigroup : IsSemigroup _≡_ (Op₂ Endo ∋ _∘′_)
∘-isSemigroup = record
  { isMagma = ∘-isMagma
  ; assoc   = λ _ _ _ → refl
  }

∘-semigroup : Semigroup _ _
∘-semigroup = record { isSemigroup = ∘-isSemigroup }

∘-id-isMonoid : IsMonoid _≡_ _∘′_ id
∘-id-isMonoid = record
  { isSemigroup = ∘-isSemigroup
  ; identity    = (λ _ → refl) , (λ _ → refl)
  }

∘-id-monoid : Monoid _ _
∘-id-monoid = record { isMonoid = ∘-id-isMonoid }

------------------------------------------------------------------------
-- Homomorphism

^-isSemigroupMorphism : ∀ f → IsSemigroupMorphism +-semigroup ∘-semigroup (f ^_)
^-isSemigroupMorphism f = record
  { ⟦⟧-cong = P.cong (f ^_)
  ; ∙-homo  = ^-homo f
  }

^-isMonoidMorphism : ∀ f → IsMonoidMorphism +-0-monoid ∘-id-monoid (f ^_)
^-isMonoidMorphism f = record
  { sm-homo = ^-isSemigroupMorphism f
  ; ε-homo  = refl
  }


{-# OPTIONS --no-auto-inline #-}
module Where where

open import Haskell.Prelude hiding (_+_; _*_; _-_)
open import Agda.Builtin.Nat

postulate
  bool2nat : Bool → Nat

-- no outer arguments
ex1 : Nat
ex1 = mult num + bool2nat true
  where
    num : Nat
    num = 42

    mult : Nat → Nat
    mult = _* 100

-- nested where
ex2 : Nat
ex2 = mult num + bool2nat true
  where
    num : Nat
    num = 42

    mult : Nat → Nat
    mult = _⊗ 100
      where
        _⊗_ = _*_

-- with outer arguments
ex3 : Nat → Bool → Nat
ex3 n b = mult num + bool2nat b
  where
    num = 42 + bool2nat b

    mult : Nat → Nat
    mult = _* n

-- nested where with outer arguments
ex4 : Bool → Nat
ex4 b = mult 42
  where
    mult : Nat → Nat
    mult n = bump n (bool2nat b)
      where
        bump : Nat → Nat → Nat
        bump x y = x * y + (n - bool2nat b)

ex4' : Bool → Nat
ex4' b = mult (bool2nat b)
  where
    mult : Nat → Nat
    mult n = bump n (bool2nat b)
      where
        bump : Nat → Nat → Nat
        bump x y = go (x * y) (n - bool2nat b)
          where
            go : Nat → Nat → Nat
            go z w = z + x + w + y + n + bool2nat b

-- with pattern matching and multiple clauses
ex5 : List Nat → Nat
ex5 [] = zro
  where
    zro : Nat
    zro = 0
ex5 (n ∷ ns) = mult num + 1
  where
    num = 42 + ex5 ns

    mult : Nat → Nat
    mult = _* n

-- mix of patterns + inner multiple clauses + nested where
ex6 : List Nat → Bool → Nat
ex6 [] b = zro
  where
    zro : Nat
    zro = 0
ex6 (n ∷ ns) b = mult (num ∷ 1 ∷ [])
  where
    mult : List Nat → Nat
    mult [] = bump 5 (bool2nat b)
      where
        bump : Nat → Nat → Nat
        bump x y = x * y + n
    mult (m ∷ ms) = bump n m
      where
        bump : Nat → Nat → Nat
        bump x y = x * y + (m - n)

    num = 42 + ex6 ns true

ex7 : Nat → Nat
ex7 n₀ = go₁ n₀
  where
    go₁ : Nat → Nat
    go₁ n₁ = go₂ (n₀ + n₁)
      where
        go₂ : Nat → Nat
        go₂ n₂ = n₀ + n₁ + n₂

ex7' : Nat → Nat
ex7' n₀ = go₁ n₀
  where
    go₁ : Nat → Nat
    go₁ n₁ = go₂ (n₀ + n₁)
      where
        go₂ : Nat → Nat
        go₂ n₂ = go₃ (n₀ + n₁ + n₂)
          where
            go₃ : Nat → Nat
            go₃ n₃ = n₀ + n₁ + n₂ + n₃

ex8 : Nat
ex8 = n₂
  where
    n₁ : Nat
    n₁ = 1
    n₂ = n₁ + 1

{-# COMPILE AGDA2HS bool2nat #-}
{-# COMPILE AGDA2HS ex1 #-}
{-# COMPILE AGDA2HS ex2 #-}
{-# COMPILE AGDA2HS ex3 #-}
{-# COMPILE AGDA2HS ex4 #-}
{-# COMPILE AGDA2HS ex4' #-}
{-# COMPILE AGDA2HS ex5 #-}
{-# COMPILE AGDA2HS ex6 #-}
{-# COMPILE AGDA2HS ex7 #-}
{-# COMPILE AGDA2HS ex7' #-}
{-# COMPILE AGDA2HS ex8 #-}


{-# OPTIONS --cubical #-}

open import Cubical.Core.Everything

Sq : {A : Set} {a0 a1 b0 b1 : A}
     (u  : a0 ≡ a1) (v  : b0 ≡ b1)
     (r0 : a0 ≡ b0) (r1 : a1 ≡ b1)
     → Set
Sq u v r0 r1 = PathP (λ i → r0 i ≡ r1 i) u v

ConstSq : {A : Set} {a : A} (p : a ≡ a) →
          Sq p p p p
ConstSq {A} p i j = compCCHM (λ _ → A)
  (~ i ∨ i ∨ ~ j ∨ j)
  (λ k → λ { (i = i0) → p j
           ; (i = i1) → p (k ∧ j)
           ; (j = i0) → p i
           ; (j = i1) → p (k ∧ i)
           })
  (p (i ∨ j))


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

open import Cubical.Foundations.Prelude

open import Cubical.Data.FinData
open import Cubical.Data.Nat using (ℕ)
open import Cubical.Data.Vec.Base

open import Cubical.Algebra.RingSolver.AlmostRing
open import Cubical.Algebra.RingSolver.NatAsAlmostRing
open import Cubical.Algebra.RingSolver.RingExpression
open import Cubical.Algebra.RingSolver.RawRing renaming (⟨_⟩ to ⟨_⟩ᵣ)
open import Cubical.Algebra.RingSolver.HornerForms
open import Cubical.Algebra.RingSolver.Solver

private
  variable
    ℓ : Level

module MultivariateSolving where
  open AlmostRing ℕAsAlmostRing
  ℕAsRawRing = AlmostRing→RawRing ℕAsAlmostRing
  open EqualityToNormalform ℕAsAlmostRing
  ℕ[X₀,X₁] = IteratedHornerOperations.asRawRing ℕAsRawRing 2

  X₀ : ⟨ ℕ[X₀,X₁] ⟩ᵣ
  X₀ = Variable 2 ℕAsRawRing (Fin.zero)

  X₁ : ⟨ ℕ[X₀,X₁] ⟩ᵣ
  X₁ = Variable 2 ℕAsRawRing (suc Fin.zero)

  Two : ⟨ ℕ[X₀,X₁] ⟩ᵣ
  Two = Constant 2 ℕAsRawRing 2

  _ : eval 2 X₀ (1 ∷ 0 ∷ []) ≡ 1
  _ = refl

  _ : eval 2 X₁ (0 ∷ 1 ∷ []) ≡ 1
  _ = refl

  X : Expr ℕ 3
  X = ∣ Fin.zero

  Y : Expr ℕ 3
  Y = ∣ (suc Fin.zero)

  Z : Expr ℕ 3
  Z = ∣ (suc (suc Fin.zero))

  {-
     'normalize' maps an expression to its Horner Normalform.
     Two expressions evaluating to the same ring element
     have the same Horner Normal form.
     This means equality of the represented ring elements
     can be checked by agda's unification (so refl is a proof)

   -}
  _ : normalize 3 ((K 2) ⊗ X) ≡
      normalize 3 (X ⊕ X)
  _ = refl

  _ : normalize 3 ((K 2) ⊗ X) ≡ normalize 3 (X ⊕ X)
  _ = refl

  _ : normalize 3 (((K 2) ⊗ X) ⊗ Y) ≡ normalize 3 (Y ⊗ (X ⊕ X))
  _ = refl

  _ : normalize 3 (Z ⊗ (((K 2) ⊗ X) ⊗ Y)) ≡ normalize 3 (Z ⊗ (Y ⊗ (X ⊕ X)))
  _ = refl

  {-
    The solver needs to produce an equality between
    actual ring elements. So we need a proof that
    those actual ring elements are equal to a normal form:
  -}
  _ : (x y z : ℕ) →
      eval 3 (normalize 3 ((K 2) ⊗ X ⊗ Y)) (x ∷ y ∷ z ∷ [])
      ≡ 2 · x · y
  _ = λ x y z → isEqualToNormalform 3 ((K 2) ⊗ X ⊗ Y) (x ∷ y ∷ z ∷ [])

  {-
    Now two of these proofs can be plugged together
    to solve an equation:
  -}
  open Eval ℕAsRawRing
  _ : (x y z : ℕ) → 3 + x + y · y ≡ y · y + x + 1 + 2
  _ = let
        lhs = (K 3) ⊕ X ⊕ (Y ⊗ Y)
        rhs = Y ⊗ Y ⊕ X ⊕ (K 1) ⊕ (K 2)
      in (λ x y z →
          ⟦ lhs ⟧ (x ∷ y ∷ z ∷ [])
        ≡⟨ sym (isEqualToNormalform 3 lhs (x ∷ y ∷ z ∷ [])) ⟩
          eval 3 (normalize 3 lhs) (x ∷ y ∷ z ∷ [])
        ≡⟨ refl ⟩
          eval 3 (normalize 3 rhs) (x ∷ y ∷ z ∷ [])
        ≡⟨ isEqualToNormalform 3 rhs (x ∷ y ∷ z ∷ []) ⟩
          ⟦ rhs ⟧ (x ∷ y ∷ z ∷ []) ∎)

  {-
    Parts of that can be automated easily:
  -}
  _ : (x y z : ℕ) → (x + y) · (x + y) ≡ x · x + 2 · x · y + y · y
  _ = λ x y z → let
              lhs = (X ⊕ Y) ⊗ (X ⊕ Y)
              rhs = X ⊗ X ⊕ (K 2) ⊗ X ⊗ Y ⊕ Y ⊗ Y
             in solve lhs rhs (x ∷ y ∷ z ∷ []) refl

  {-
    A bigger example
  -}
  _ : (x y z : ℕ) → (x + y) · (x + y) · (x + y) · (x + y)
                ≡ x · x · x · x + 4 · x · x · x · y + 6 · x · x · y · y
                  +  4 · x · y · y · y + y · y · y · y
  _ = λ x y z → let
              lhs = (X ⊕ Y) ⊗ (X ⊕ Y) ⊗ (X ⊕ Y) ⊗ (X ⊕ Y)
              rhs = X ⊗ X ⊗ X ⊗ X
                  ⊕ (K 4) ⊗ X ⊗ X ⊗ X ⊗ Y
                  ⊕ (K 6) ⊗ X ⊗ X ⊗ Y ⊗ Y
                  ⊕ (K 4) ⊗ X ⊗ Y ⊗ Y ⊗ Y
                  ⊕ Y ⊗ Y ⊗ Y ⊗ Y
             in solve lhs rhs (x ∷ y ∷ z ∷ []) refl
  {-
    this one cannot work so far:

  _ : (x y z : ℕ) → (x + y) · (x - y) ≡ (x · x - (y · y))
  _ = λ x y z → let
                lhs = (X ⊕ Y) ⊗ (X ⊕ (⊝ Y))
                rhs = (X ⊗ X) ⊕ (⊝ (Y ⊗ Y))
              in solve lhs rhs (x ∷ y ∷ z ∷ []) {!!}
  -}

module ExamplesForArbitraryRings (R : AlmostRing {ℓ}) where
  open AlmostRing R
  open EqualityToNormalform R

  X : Expr ⟨ R ⟩ 4
  X = ∣ Fin.zero

  Y : Expr ⟨ R ⟩ 4
  Y = ∣ (suc Fin.zero)

  A : Expr ⟨ R ⟩ 4
  A = ∣ (suc (suc Fin.zero))

  B : Expr ⟨ R ⟩ 4
  B = ∣ (suc (suc (suc Fin.zero)))

  _ : (x y a b : ⟨ R ⟩) → (x + y) + (a + b) ≡ (y + b) + (x + a)
  _ = λ x y a b → let
                lhs = (X ⊕ Y) ⊕ (A ⊕ B)
                rhs = (Y ⊕ B) ⊕ (X ⊕ A)
              in solve lhs rhs (x ∷ y ∷ a ∷ b ∷ []) refl

  _ : (x y a b : ⟨ R ⟩) → (x + y) · (x + y) ≡ x · x + x · y + x · y + y · y
  _ = λ x y a b →
              let
                lhs = (X ⊕ Y) ⊗ (X ⊕ Y)
                rhs = (X ⊗ X) ⊕ (X ⊗ Y) ⊕ (X ⊗ Y) ⊕ (Y ⊗ Y)
              in solve lhs rhs (x ∷ y ∷ a ∷ b ∷ []) refl

  _ : (x y a b : ⟨ R ⟩) → x · a ≡ a · x
  _ = λ x y a b →
              let
                lhs = X ⊗ A
                rhs = A ⊗ X
              in solve lhs rhs (x ∷ y ∷ a ∷ b ∷ []) refl

{-
  this one should work, but doesn't:

  _ : (x y a b : ⟨ R ⟩) → x · (a + b) ≡ a · x + b · x
  _ = λ x y a b →
              let
                lhs = X ⊗ (A ⊕ B)
                rhs = (A ⊗ X) ⊕ (B ⊗ X)
              in solve lhs rhs (x ∷ y ∷ a ∷ b ∷ []) refl

  the reason ist, that lhs and rhs evaluate to definitionally different things:

(0r · x +
 (0r · y +
  ((0r · a + (0r · b + 1r · 1r)) · a +
   ((0r · b + 1r · 1r) · b + 1r · 0r))))
· x
+ 0r

(0r · x +
 (0r · y +
  ((0r · a + (0r · b + 1r · 1r)) · a +
   ((0r · b + 1r · 1r) · b + (0r + 0r · 1r)))))
· x
+ 0r
-}
{-
  '-' is problematic...

  _ : (x y a b : ⟨ R ⟩) → (x + y) · (x - y) ≡ (x · x - (y · y))
  _ = λ x y a b → let
                lhs = (X ⊕ Y) ⊗ (X ⊕ (⊝ Y))
                rhs = (X ⊗ X) ⊕ (⊝ (Y ⊗ Y))
              in solve lhs rhs (x ∷ y ∷ a ∷ b ∷ []) {!!}
-}


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

module Data.Bool where

open import Level
open import Agda.Builtin.Bool using (Bool; true; false) public
open import Data.Unit
open import Data.Empty

bool : ∀ {ℓ} {P : Bool → Type ℓ} (f : P false) (t : P true) → (x : Bool) → P x
bool f t false = f
bool f t true = t

not : Bool → Bool
not false = true
not true = false

infixl 6 _or_
_or_ : Bool → Bool → Bool
false or y = y
true  or y = true

infixl 7 _and_
_and_ : Bool → Bool → Bool
false and y = false
true  and y = y

infixr 0 if_then_else_
if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A
if true  then x else _ = x
if false then _ else x = x

T : Bool → Type₀
T true  = ⊤
T false = ⊥


module sv20.assign2.Second where
-- The solution for the second task starts in line 53

open import Data.Unit using (⊤; tt)
open import Data.Product using (_×_ ; ∃) renaming (_,_ to ⟨_,_⟩)
open import Relation.Nullary using (¬_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function using (_∘_)

-- For the second task. We took one common definition of Subset mentioned in
-- https://stackoverflow.com/q/34183349

-- Notice that this definition is equivalent to the definition presented in the
-- Standard Library where Subset is a "record".

Subset : (A : Set) -> Set _
Subset A = A → Set

-- Because, we are using a different definition from the standard library, we
-- need to define what it means to be included in a subset, what is a relation,
-- what is the range and domain of a relation, and so forth.

_∈_ : ∀ {A} → A → Subset A → Set
a ∈ P = P a

Relation : ∀ A B → Set₁
Relation A B = Subset (A × B)

Range : ∀ {A B} → Relation A B → Subset B
Range R b = ∃ (R ∘ ⟨_, b ⟩)  -- equivalent to ∃ \a → R ⟨ a , b ⟩

Dom : ∀ {A B} → Relation A B → Subset A
Dom R a = ∃ (R ∘ ⟨ a ,_⟩)  -- equivalent to ∃ \b → R ⟨ a , b ⟩

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

_∩_ : ∀ {A} → Subset A → Subset A → Subset A
A ∩ B = λ x → x ∈ A × x ∈ B

_∪_ : ∀ {A} → Subset A → Subset A → Subset A
A ∪ B = λ x → x ∈ A ⊎ x ∈ B

_/_ : ∀ {A} → Subset A → Subset A → Subset A
A / B = λ x → x ∈ A × ¬ (x ∈ B)

-- Practice theorem
∪-range-⊆ : ∀ {A B} {F G : Relation A B}
          → (Range F ∪ Range G) ⊆ Range (F ∪ G)
∪-range-⊆ _ (inj₁ ⟨ a , fab ⟩) = ⟨ a , inj₁ fab ⟩
∪-range-⊆ _ (inj₂ ⟨ a , gab ⟩) = ⟨ a , inj₂ gab ⟩

-- =========== SOLUTION TO TASK 2 STARTS HERE ===========

-- range-theorem-2
range-∩-⊆ : ∀ {A B} {F G : Relation A B}
          → Range (F ∩ G) ⊆ (Range F ∩ Range G)
range-∩-⊆ b b∈RangeF∩G =  -- Given "b ∈ Range (F ∩ G)", we need to prove that "b ∈ (Range F ∩ Range G)"
  let
    ⟨ a , a,b∈F∩G ⟩ = b∈RangeF∩G  -- (1) if "b ∈ Range (F ∩ G)" then there exists 'a' such that "(a,b) ∈ (F ∩ G)"
    ⟨ a,b∈F , a,b∈G ⟩ = a,b∈F∩G   -- (2) per definition of intersection, if "(a,b) ∈ (F ∩ G)" then "(a,b) ∈ F" and "(a,b) ∈ G"
    b∈RangeF = ⟨ a , a,b∈F ⟩      -- (3) from (1) we know there is an 'a' such that "(a,b) ∈ F" (2), so "b ∈ Range F", per definition of Range
    b∈RangeG = ⟨ a , a,b∈G ⟩      -- (4) as above, "b ∈ Range G" because of 'a' (1) and "(a,b) ∈ F" (2)
    b∈RangeF∩RangeG = ⟨ b∈RangeF , b∈RangeG ⟩ -- (5) given "b ∈ Range F" (3) and "b ∈ Range G" (4), and definition of ∩ "b ∈ Range F ∩ Range G". QED
  in b∈RangeF∩RangeG

-- dom-theorem-3
dom-/-⊆ : ∀ {A B} {F G : Relation A B}
        → (Dom F / Dom G) ⊆ Dom (F / G)
dom-/-⊆ a a∈DomF/DomG =  -- Given "a ∈ (Dom F / Dom G)", we need to prove that "a ∈ Dom (F / G)"
  let
    ⟨ a∈DomF , ¬a∈DomG ⟩ = a∈DomF/DomG  -- (1) per definition of '/', "a ∈ (Dom F / Dom G)" is the same as "a ∈ Dom F" and "¬ a ∈ Dom G"
    ⟨ b , a,b∈F ⟩ = a∈DomF              -- (2) per definition of 'Dom', if "a ∈ Dom F" then there exists 'b' such that "(a,b) ∈ F"
    ¬a,b∈G =                            -- (3) From "¬ a ∈ Dom G" we can prove that "¬ (a, b₁) ∈ G" for any 'b₁'
            λ{ a,b∈G →                     -- (a) By contradiction, suposse that "(a, b₁) ∈ G" for some 'b₁'
            let a∈DomG = ⟨ _ , a,b∈G ⟩     -- (b) per definition of 'Dom', "a ∈ Dom G"
            in ¬a∈DomG a∈DomG }            -- We know that "¬ a ∈ Dom G" (3) and "a ∈ Dom G" (b). Nonesense! So, "¬ (a, b₁) ∈ G" for all 'b₁'
    a,b∈F/G = ⟨ a,b∈F , ¬a,b∈G ⟩        -- (4) per definition of '/' and given (2) and (3), "(a, b) ∈ F/G"
    a∈DomF/G = ⟨ b , a,b∈F/G ⟩          -- (5) per definition of 'Dom' and given (4) and 'b', we have that "a ∈ Dom (F / G)". QED
  in a∈DomF/G

-- range-theorem-3
/-range-⊆ : ∀ {A B} {F G : Relation A B}
          → (Range F / Range G) ⊆ Range (F / G)
/-range-⊆ b b∈RangeF/RangeG = -- Given "b ∈ (Range F / Range G)", we need to prove that "b ∈ Range (F / G)"
  let
    ⟨ b∈RangeF , ¬b∈RangeG ⟩ = b∈RangeF/RangeG -- (1) per definition of '/', "b ∈ Range F" and "¬ b ∈ Range G"
    ⟨ a , a,b∈F ⟩ = b∈RangeF                   -- (2) per definition of 'Range', there exists 'a' such that "(a,b) ∈ F"
    ¬a,b∈G =                                   -- (3) by contradiction, we will prove that "¬ (a, b) ∈ G" given (1)
            λ{ a,b∈G →                           -- (a) suppose that there exists 'a₁' such that "(a₁, b) ∈ G"
            let b∈RangeG = ⟨ _ , a,b∈G ⟩         -- (b) per definition of Range and given (a), "b ∈ Range G"
            in ¬b∈RangeG b∈RangeG }              -- (c) we know from (1) that "¬ b ∈ Range G" but (b) says "b ∈ Range G". Nonesense!
    a,b∈F/G = ⟨ a,b∈F , ¬a,b∈G ⟩               -- (4) per definition of '/' and given (2) and (3), then "(a, b) ∈ F / G"
    b∈RangeF/G = ⟨ a , a,b∈F/G ⟩               -- (5) per definition of 'Range' and given 'a' and (4), we know that "b ∈ Range (F / G)". QED
  in b∈RangeF/G

-- Class exercise
-- This is not really an exercise at all because this is the definition of `⊆`!
exercise103a : ∀ {a} {A B : Subset a}
             → (∀ x → x ∈ A → x ∈ B)
             → A ⊆ B
exercise103a x→x∈A→x∈B = x→x∈A→x∈B

open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; cong)

exercise103id→ : ∀ {a} {A B : Subset a}
              → A ≡ B → A ⊆ B × B ⊆ A
exercise103id→ refl = ⟨ (λ{_ p → p}) , (λ{_ p → p}) ⟩

--exercise103id← : ∀ {a} {A B : Subset a}
--              → A ⊆ B × B ⊆ A → A ≡ B
--exercise103id← ⟨ A⊆B , B⊆A ⟩ = ? -- Probably impossible to prove without unification. Possibly, impossible to prove. Needs `postulate`

exercise108a : ∀ {a} {A B : Subset a} (z : a)
             → (z ∈ A) × (z ∈ B) → z ∈ (A ∩ B)
exercise108a _ ⟨ z∈A , z∈B ⟩ = ⟨ z∈A , z∈B ⟩ -- How to explain this?


{-# OPTIONS --without-K #-}
module equality.reasoning where

open import equality.core

module ≡-Reasoning {i} {X : Set i} where
  infix  4 _IsRelatedTo_
  infix  2 _∎
  infixr 2 _≡⟨_⟩_
  infix  1 begin_

  -- This seemingly unnecessary type is used to make it possible to
  -- infer arguments even if the underlying equality evaluates.

  data _IsRelatedTo_ (x y : X) : Set i where
    relTo : x ≡ y → x IsRelatedTo y

  begin_ : ∀ {x y} → x IsRelatedTo y → x ≡ y
  begin relTo p = p

  _≡⟨_⟩_ : ∀ x {y z} → x ≡ y → y IsRelatedTo z → x IsRelatedTo z
  _ ≡⟨ p ⟩ relTo q = relTo (p · q)

  _∎ : ∀ x → x IsRelatedTo x
  _∎ _ = relTo refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- The universal binary relation
------------------------------------------------------------------------

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

module Relation.Binary.Construct.Always where

open import Relation.Binary
open import Relation.Binary.Construct.Constant using (Const)
open import Data.Unit using (⊤; tt)
open import Level using (Lift; lift)

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

Always : ∀ {a ℓ} {A : Set a} → Rel A ℓ
Always = Const (Lift _ ⊤)

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

module _ {a} (A : Set a) ℓ where

  refl : Reflexive {ℓ = ℓ} {A} Always
  refl = lift tt

  sym : Symmetric {ℓ = ℓ} {A} Always
  sym _ = lift tt

  trans : Transitive {ℓ = ℓ} {A} Always
  trans _ _ = lift tt

  isEquivalence : IsEquivalence {ℓ = ℓ} {A} Always
  isEquivalence = record {}

  setoid : Setoid a ℓ
  setoid = record
    { isEquivalence = isEquivalence
    }

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 0.17

Always-setoid = setoid
{-# WARNING_ON_USAGE Always-setoid
"Warning: Always-setoid was deprecated in v0.14.
Please use setoid instead."
#-}


module Numeric.Nat.GCD.Properties where

open import Prelude
open import Numeric.Nat.Properties
open import Numeric.Nat.Divide
open import Numeric.Nat.Divide.Properties
open import Numeric.Nat.GCD
open import Numeric.Nat.GCD.Extended
open import Tactic.Nat
open import Tactic.Cong

gcd-is-gcd : ∀ d a b → gcd! a b ≡ d → IsGCD d a b
gcd-is-gcd d a b refl with gcd a b
... | gcd-res _ isgcd = isgcd

gcd-divides-l : ∀ a b → gcd! a b Divides a
gcd-divides-l a b with gcd a b
... | gcd-res _ i = IsGCD.d|a i

gcd-divides-r : ∀ a b → gcd! a b Divides b
gcd-divides-r a b with gcd a b
... | gcd-res _ i = IsGCD.d|b i

is-gcd-unique : ∀ {a b} d₁ d₂ (g₁ : IsGCD d₁ a b) (g₂ : IsGCD d₂ a b) → d₁ ≡ d₂
is-gcd-unique d d′ (is-gcd d|a d|b gd) (is-gcd d′|a d′|b gd′) =
  divides-antisym (gd′ d d|a d|b)
                  (gd  d′ d′|a d′|b)

gcd-unique : ∀ a b d → IsGCD d a b → gcd! a b ≡ d
gcd-unique a b d pd with gcd a b
... | gcd-res d′ pd′ = is-gcd-unique d′ d pd′ pd

is-gcd-commute : ∀ {d a b} → IsGCD d a b → IsGCD d b a
is-gcd-commute (is-gcd d|a d|b g) = is-gcd d|b d|a (flip ∘ g)

gcd-commute : ∀ a b → gcd! a b ≡ gcd! b a
gcd-commute a b with gcd b a
gcd-commute a b | gcd-res d p = gcd-unique a b d (is-gcd-commute p)

gcd-factor-l : ∀ {a b} → a Divides b → gcd! a b ≡ a
gcd-factor-l {a} (factor! b) =
  gcd-unique a (b * a) a
             (is-gcd divides-refl (divides-mul-r b divides-refl) λ _ k|a _ → k|a)

gcd-factor-r : ∀ {a b} → b Divides a → gcd! a b ≡ b
gcd-factor-r {a} {b} b|a = gcd-commute a b ⟨≡⟩ gcd-factor-l b|a

gcd-idem : ∀ a → gcd! a a ≡ a
gcd-idem a = gcd-factor-l divides-refl

gcd-zero-l : ∀ n → gcd! 0 n ≡ n
gcd-zero-l n = gcd-unique 0 n n (is-gcd (factor! 0) divides-refl λ _ _ k|n → k|n)

gcd-zero-r : ∀ n → gcd! n 0 ≡ n
gcd-zero-r n = gcd-commute n 0 ⟨≡⟩ gcd-zero-l n

zero-is-gcd-l : ∀ {a b} → IsGCD 0 a b → a ≡ 0
zero-is-gcd-l (is-gcd 0|a _ _) = divides-zero 0|a

zero-is-gcd-r : ∀ {a b} → IsGCD 0 a b → b ≡ 0
zero-is-gcd-r (is-gcd _ 0|b _) = divides-zero 0|b

zero-gcd-l : ∀ a b → gcd! a b ≡ 0 → a ≡ 0
zero-gcd-l a b eq with gcd a b
zero-gcd-l a b refl | gcd-res .0 p = zero-is-gcd-l p

zero-gcd-r : ∀ a b → gcd! a b ≡ 0 → b ≡ 0
zero-gcd-r a b eq with gcd a b
zero-gcd-r a b refl | gcd-res .0 p = zero-is-gcd-r p

nonzero-is-gcd-l : ∀ {a b d} {{_ : NonZero a}} → IsGCD d a b → NonZero d
nonzero-is-gcd-l {0} {{}} _
nonzero-is-gcd-l {a@(suc _)} {d = suc _} _ = _
nonzero-is-gcd-l {a@(suc _)} {d = 0} (is-gcd (factor q eq) _ _) = refute eq

nonzero-is-gcd-r : ∀ {a b d} {{_ : NonZero b}} → IsGCD d a b → NonZero d
nonzero-is-gcd-r isgcd = nonzero-is-gcd-l (is-gcd-commute isgcd)

nonzero-gcd-l : ∀ a b {{_ : NonZero a}} → NonZero (gcd! a b)
nonzero-gcd-l a b = nonzero-is-gcd-l (GCD.isGCD (gcd a b))

nonzero-gcd-r : ∀ a b {{_ : NonZero b}} → NonZero (gcd! a b)
nonzero-gcd-r a b = nonzero-is-gcd-r (GCD.isGCD (gcd a b))

private
  _|>_ = divides-trans

gcd-assoc : ∀ a b c → gcd! a (gcd! b c) ≡ gcd! (gcd! a b) c
gcd-assoc a b c with gcd a b | gcd b c
... | gcd-res ab (is-gcd ab|a ab|b gab)
    | gcd-res bc (is-gcd bc|b bc|c gbc) with gcd ab c
...    | gcd-res ab-c (is-gcd abc|ab abc|c gabc) =
  gcd-unique a bc ab-c
             (is-gcd (abc|ab |> ab|a)
                     (gbc ab-c (abc|ab |> ab|b) abc|c)
                     λ k k|a k|bc → gabc k (gab k k|a (k|bc |> bc|b))
                                           (k|bc |> bc|c))

-- Coprimality properties

coprime-sym : ∀ a b → Coprime a b → Coprime b a
coprime-sym a b p = gcd-commute b a ⟨≡⟩ p

coprimeByDivide : ∀ a b → (∀ k → k Divides a → k Divides b → k Divides 1) → Coprime a b
coprimeByDivide a b g = gcd-unique a b 1 (is-gcd one-divides one-divides g)

divide-coprime : ∀ d a b → Coprime a b → d Divides a → d Divides b → d Divides 1
divide-coprime d a b p d|a d|b with gcd a b
divide-coprime d a b refl d|a d|b | gcd-res _ (is-gcd _ _ g) =
  g d d|a d|b

mul-coprime-l : ∀ a b c → Coprime a (b * c) → Coprime a b
mul-coprime-l a b c a⊥bc =
  coprimeByDivide a b λ k k|a k|b →
    divide-coprime k a (b * c) a⊥bc k|a (divides-mul-l c k|b)

mul-coprime-r : ∀ a b c → Coprime a (b * c) → Coprime a c
mul-coprime-r a b c a⊥bc = mul-coprime-l a c b (transport (Coprime a) auto a⊥bc)

is-gcd-factors-coprime : ∀ {a b d} (p : IsGCD d a b) {{_ : NonZero d}} →
                           Coprime (is-gcd-factor₁ p) (is-gcd-factor₂ p)
is-gcd-factors-coprime {a} {b} {0} _ {{}}
is-gcd-factors-coprime {a} {b} {d@(suc _)} p@(is-gcd (factor qa refl) (factor qb refl) g) with gcd qa qb
... | gcd-res j (is-gcd j|qa j|qb gj) = lem₃ j lem₂
  where
    lem : IsGCD (j * d) a b
    lem = is-gcd (divides-mul-cong-r d j|qa) (divides-mul-cong-r d j|qb) λ k k|a k|b →
                 divides-mul-r j (g k k|a k|b)

    lem₂ : d ≡ j * d
    lem₂ = is-gcd-unique d (j * d) p lem

    lem₃ : ∀ j → d ≡ j * d → j ≡ 1
    lem₃ 0 ()
    lem₃ 1 _ = refl
    lem₃ (suc (suc n)) eq = refute eq

private
  mul-gcd-distr-l' : ∀ a b c ⦃ a>0 : NonZero a ⦄ ⦃ b>0 : NonZero b ⦄ → gcd! (a * b) (a * c) ≡ a * gcd! b c
  mul-gcd-distr-l' a b c with gcd b c | gcd (a * b) (a * c)
  ... | gcd-res d gcd-bc@(is-gcd (factor! b′) (factor! c′) _)
      | gcd-res δ gcd-abac@(is-gcd (factor u uδ=ab) (factor v vδ=ac) g) =
    let instance _ = nonzero-is-gcd-l gcd-bc
                 _ : NonZero (d * a)
                 _ = mul-nonzero d a
    in case g (d * a) (factor b′ auto) (factor c′ auto) of λ where
         (factor w wda=δ) →
           let dab′=dauw =
                 d * a * b′        ≡⟨ by uδ=ab ⟩
                 u * δ             ≡⟨ u *_ $≡ wda=δ ⟩ʳ
                 u * (w * (d * a)) ≡⟨ auto ⟩
                 d * a * (u * w)   ∎
               dac′=davw =
                 d * a * c′        ≡⟨ by vδ=ac ⟩
                 v * δ             ≡⟨ v *_ $≡ wda=δ ⟩ʳ
                 v * (w * (d * a)) ≡⟨ auto ⟩
                 d * a * (v * w) ∎

               uw=b′ : u * w ≡ b′
               uw=b′ = sym (mul-inj₂ (d * a) b′ (u * w) dab′=dauw)
               vw=c′ : v * w ≡ c′
               vw=c′ = sym (mul-inj₂ (d * a) c′ (v * w) dac′=davw)
               w=1 : w ≡ 1
               w=1 = divides-one (divide-coprime w b′ c′ (is-gcd-factors-coprime gcd-bc)
                                 (factor u uw=b′)
                                 (factor v vw=c′))
           in case w=1 of λ where refl → by wda=δ

mul-gcd-distr-l : ∀ a b c → gcd! (a * b) (a * c) ≡ a * gcd! b c
mul-gcd-distr-l zero b c = refl
mul-gcd-distr-l a zero c = (λ z → gcd! z (a * c)) $≡ mul-zero-r a
mul-gcd-distr-l a@(suc _) b@(suc _) c = mul-gcd-distr-l' a b c

mul-gcd-distr-r : ∀ a b c → gcd! (a * c) (b * c) ≡ gcd! a b * c
mul-gcd-distr-r a b c =
  gcd! (a * c) (b * c)
    ≡⟨ gcd! $≡ mul-commute a c *≡ mul-commute b c ⟩
  gcd! (c * a) (c * b)
    ≡⟨ mul-gcd-distr-l c a b ⟩
  c * gcd! a b
    ≡⟨ auto ⟩
  gcd! a b * c ∎

-- Divide two numbers by their gcd and return the result, the gcd, and some useful properties.
-- Continuation-passing for efficiency reasons.
gcdReduce' : ∀ {a} {A : Set a} (a b : Nat) ⦃ _ : NonZero b ⦄ →
           ((a′ b′ d : Nat) → (⦃ _ : NonZero a ⦄ → NonZero a′) →
                              ⦃ nzb : NonZero b′ ⦄ → ⦃ nzd : NonZero d ⦄ →
                               a′ * d ≡ a → b′ * d ≡ b →
                               Coprime a′ b′ → A) → A
gcdReduce' a b ret with gcd a b
gcdReduce' a b ret | gcd-res d isgcd@(is-gcd d|a@(factor a′ eqa) d|b@(factor b′ eqb) g)=
  let instance _ = nonzero-is-gcd-r isgcd in
  ret a′ b′ d (nonzero-factor d|a) ⦃ nonzero-factor d|b ⦄
              eqa eqb
              (is-gcd-factors-coprime isgcd)

-- Both arguments are non-zero.
gcdReduce : ∀ {a} {A : Set a} (a b : Nat) ⦃ _ : NonZero a ⦄ ⦃ _ : NonZero b ⦄ →
           ((a′ b′ d : Nat) ⦃ a′>0 : NonZero a′ ⦄ ⦃ b′>0 : NonZero b′ ⦄ ⦃ nzd : NonZero d ⦄ →
            a′ * d ≡ a → b′ * d ≡ b → Coprime a′ b′ → A) → A
gcdReduce a b k = gcdReduce' a b λ a′ b′ d a′>0 eq₁ eq₂ a′⊥b′ →
  let instance _ = a′>0 in k a′ b′ d eq₁ eq₂ a′⊥b′

-- Only the right argument is guaranteed to be non-zero.
gcdReduce-r : ∀ {a} {A : Set a} (a b : Nat) ⦃ _ : NonZero b ⦄ →
           ((a′ b′ d : Nat) ⦃ nzb : NonZero b′ ⦄ ⦃ nzd : NonZero d ⦄ →
            a′ * d ≡ a → b′ * d ≡ b → Coprime a′ b′ → A) → A
gcdReduce-r a b k = gcdReduce' a b λ a′ b′ d _ eq₁ eq₂ a′⊥b′ →
  k a′ b′ d eq₁ eq₂ a′⊥b′

--- Properties ---

coprime-divide-mul-l : ∀ a b c → Coprime a b → a Divides (b * c) → a Divides c
coprime-divide-mul-l a b c p a|bc with extendedGCD a b
coprime-divide-mul-l a b c p a|bc | gcd-res d i e with gcd-unique _ _ _ i ʳ⟨≡⟩ p
coprime-divide-mul-l a b c p (factor q qa=bc) | gcd-res d i (bézoutL x y ax=1+by) | refl =
  factor (x * c - y * q) $
    (x * c - y * q) * a
      ≡⟨ auto ⟩
    a * x * c - y * (q * a)
      ≡⟨ by-cong ax=1+by ⟩
    suc (b * y) * c - y * (q * a)
      ≡⟨ by-cong qa=bc ⟩
    suc (b * y) * c - y * (b * c)
      ≡⟨ auto ⟩
    c ∎
coprime-divide-mul-l a b c p (factor q qa=bc) | gcd-res d i (bézoutR x y by=1+ax) | refl =
  factor (y * q - x * c) $
    (y * q - x * c) * a
      ≡⟨ auto ⟩
    y * (q * a) - a * x * c
      ≡⟨ by-cong qa=bc ⟩
    y * (b * c) - a * x * c
      ≡⟨ auto ⟩
    (b * y) * c - a * x * c
      ≡⟨ by-cong by=1+ax ⟩
    suc (a * x) * c - a * x * c
      ≡⟨ auto ⟩
    c ∎

coprime-divide-mul-r : ∀ a b c → Coprime a c → a Divides (b * c) → a Divides b
coprime-divide-mul-r a b c p a|bc =
  coprime-divide-mul-l a c b p (transport (a Divides_) auto a|bc)

is-gcd-by-coprime-factors : ∀ d a b (d|a : d Divides a) (d|b : d Divides b) ⦃ nzd : NonZero d ⦄ →
                            Coprime (get-factor d|a) (get-factor d|b) →
                            IsGCD d a b
is-gcd-by-coprime-factors d a b d|a@(factor! q) d|b@(factor! r) q⊥r =
  is-gcd d|a d|b λ k k|a k|b →
    let lem : ∀ j → j Divides q → j Divides r → j ≡ 1
        lem j j|q j|r = divides-one (divide-coprime j q r q⊥r j|q j|r)
    in
    case gcd k d of λ where
      (gcd-res g isgcd@(is-gcd (factor k′ k′g=k@refl) (factor d′ d′g=d@refl) sup)) →
        let instance g>0 = nonzero-is-gcd-r isgcd
            k′⊥d′ : Coprime k′ d′
            k′⊥d′ = is-gcd-factors-coprime isgcd
            k′|qd′ : k′ Divides (q * d′)
            k′|qd′ = cancel-mul-divides-r k′ (q * d′) g
                       (transport ((k′ * g) Divides_) {x = q * (d′ * g)} {q * d′ * g} auto k|a)
            k′|rd′ : k′ Divides (r * d′)
            k′|rd′ = cancel-mul-divides-r k′ (r * d′) g
                       (transport ((k′ * g) Divides_) {x = r * (d′ * g)} {r * d′ * g} auto k|b)
            k′|q : k′ Divides q
            k′|q = coprime-divide-mul-r k′ q d′ k′⊥d′ k′|qd′
            k′|r : k′ Divides r
            k′|r = coprime-divide-mul-r k′ r d′ k′⊥d′ k′|rd′
            k′=1 = lem k′ k′|q k′|r
            k=g : k ≡ g
            k=g = case k′=1 of λ where refl → auto
        in factor d′ (d′ *_ $≡ k=g ⟨≡⟩ d′g=d)


-- Andreas, 2012-09-15
-- Positive effects of making Agda recognize constant functions.
-- Arguments to constant functions are ignored in definitional equality.
{-# OPTIONS --copatterns #-}
module NonvariantPolarity where

open import Common.Equality

data ⊥ : Set where
record ⊤ : Set where
  constructor trivial

data Bool : Set where
  true false : Bool

True : Bool → Set
True true  = ⊤
True false = ⊥

module IgnoreArg where

  -- A function ignoring its first argument
  knot : Bool → Bool → Bool
  knot x true  = false
  knot x false = true

  test : (y : Bool) → knot true y ≡ knot false y
  test y = refl

module UnusedModulePar where

  -- An unused module parameter
  module M (x : Bool) where

    not : Bool → Bool
    not true  = false
    not false = true

  open M true
  open M false renaming (not to not′)

  test : (y : Bool) → not y ≡ not′ y
  test y = refl

module Absurd where

  -- Absurd patterns do not count as matches; abort is constant in its 2nd arg.
  abort : (A : Set) → ⊥ → A
  abort A ()

  test : (x y : ⊥) → abort Bool x ≡ abort Bool y
  test x y = refl

module ProofIrrelevance where

  -- Record and absurd patterns do not count as match.
  fun : (b : Bool) → True b → Bool
  fun true  trivial = true
  fun false ()

  -- This gives us a kind of proof irrelevance.
  test : (b : Bool) → (x y : True b) → fun b x ≡ fun b y
  test b x y = refl

module CoinductiveUnit where

  record Unit : Set where
    coinductive
    constructor delay
    field force : Unit
  open Unit

  -- The identity on Unit does not match on its argument, so it is constant.
  id : Unit → Unit
  force (id x) = id (force x)

  idConst : (x y : Unit) → id x ≡ id y
  idConst x y = refl

  -- That does not imply x ≡ y (needs bisimulation).


open import Oscar.Prelude
open import Oscar.Class
open import Oscar.Class.Leftunit
open import Oscar.Class.Reflexivity
open import Oscar.Class.Transitivity
open import Oscar.Class.Transleftidentity

module Oscar.Class.Transleftidentity.ToLeftunit where

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} {_∼_ : 𝔒 → 𝔒 → Ø 𝔯}
  {ℓ} {_∼̇_ : ∀ {x y} → x ∼ y → x ∼ y → Ø ℓ}
  {ε : Reflexivity.type _∼_}
  {transitivity : Transitivity.type _∼_}
  where
  instance
    toLeftunitFromTransleftidentity :
      ⦃ _ : Transleftidentity.class _∼_ _∼̇_ ε transitivity ⦄
      → ∀ {x y} {f : x ∼ y} → Leftunit.class _∼̇_ ε (flip transitivity) f
    toLeftunitFromTransleftidentity .⋆ = transleftidentity


open import Level
open import Ordinals
module OrdUtil {n : Level} (O : Ordinals {n} ) where

open import logic
open import nat
open import Data.Nat renaming ( zero to Zero ; suc to Suc ;  ℕ to Nat ; _⊔_ to _n⊔_ )
open import Data.Empty
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Binary hiding (_⇔_)

open Ordinals.Ordinals  O
open Ordinals.IsOrdinals isOrdinal
open Ordinals.IsNext isNext

o<-dom :   { x y : Ordinal } → x o< y → Ordinal
o<-dom  {x} _ = x

o<-cod :   { x y : Ordinal } → x o< y → Ordinal
o<-cod  {_} {y} _ = y

o<-subst : {Z X z x : Ordinal }  → Z o< X → Z ≡ z  →  X ≡ x  →  z o< x
o<-subst df refl refl = df

o<¬≡ :  { ox oy : Ordinal } → ox ≡ oy  → ox o< oy  → ⊥
o<¬≡ {ox} {oy} eq lt with trio< ox oy
o<¬≡ {ox} {oy} eq lt | tri< a ¬b ¬c = ¬b eq
o<¬≡ {ox} {oy} eq lt | tri≈ ¬a b ¬c = ¬a lt
o<¬≡ {ox} {oy} eq lt | tri> ¬a ¬b c = ¬b eq

o<> :   {x y : Ordinal   }  →  y o< x → x o< y → ⊥
o<> {ox} {oy} lt tl with trio< ox oy
o<> {ox} {oy} lt tl | tri< a ¬b ¬c = ¬c lt
o<> {ox} {oy} lt tl | tri≈ ¬a b ¬c = ¬a tl
o<> {ox} {oy} lt tl | tri> ¬a ¬b c = ¬a tl

osuc-< :  { x y : Ordinal  } → y o< osuc x  → x o< y → ⊥
osuc-< {x} {y} y<ox x<y with osuc-≡< y<ox
osuc-< {x} {y} y<ox x<y | case1 refl = o<¬≡ refl x<y
osuc-< {x} {y} y<ox x<y | case2 y<x = o<> x<y y<x

osucc :  {ox oy : Ordinal } → oy o< ox  → osuc oy o< osuc ox
----   y < osuc y < x < osuc x
----   y < osuc y = x < osuc x
----   y < osuc y > x < osuc x   -> y = x ∨ x < y → ⊥
osucc {ox} {oy} oy<ox with trio< (osuc oy) ox
osucc {ox} {oy} oy<ox | tri< a ¬b ¬c = ordtrans a <-osuc
osucc {ox} {oy} oy<ox | tri≈ ¬a refl ¬c = <-osuc
osucc {ox} {oy} oy<ox | tri> ¬a ¬b c with  osuc-≡< c
osucc {ox} {oy} oy<ox | tri> ¬a ¬b c | case1 eq = ⊥-elim (o<¬≡ (sym eq) oy<ox)
osucc {ox} {oy} oy<ox | tri> ¬a ¬b c | case2 lt = ⊥-elim (o<> lt oy<ox)

osucprev :  {ox oy : Ordinal } → osuc oy o< osuc ox  → oy o< ox
osucprev {ox} {oy} oy<ox with trio< oy ox
osucprev {ox} {oy} oy<ox | tri< a ¬b ¬c = a
osucprev {ox} {oy} oy<ox | tri≈ ¬a b ¬c = ⊥-elim (o<¬≡ (cong (λ k → osuc k) b) oy<ox )
osucprev {ox} {oy} oy<ox | tri> ¬a ¬b c = ⊥-elim (o<> (osucc c) oy<ox )

open _∧_

osuc2 :  ( x y : Ordinal  ) → ( osuc x o< osuc (osuc y )) ⇔ (x o< osuc y)
proj2 (osuc2 x y) lt = osucc lt
proj1 (osuc2 x y) ox<ooy with osuc-≡< ox<ooy
proj1 (osuc2 x y) ox<ooy | case1 ox=oy = o<-subst <-osuc refl ox=oy
proj1 (osuc2 x y) ox<ooy | case2 ox<oy = ordtrans <-osuc ox<oy

_o≤_ :  Ordinal → Ordinal → Set  n
a o≤ b  = a o< osuc b -- (a ≡ b)  ∨ ( a o< b )

xo<ab :  {oa ob : Ordinal } → ( {ox : Ordinal } → ox o< oa  → ox o< ob ) → oa o< osuc ob
xo<ab   {oa} {ob} a→b with trio< oa ob
xo<ab   {oa} {ob} a→b | tri< a ¬b ¬c = ordtrans a <-osuc
xo<ab   {oa} {ob} a→b | tri≈ ¬a refl ¬c = <-osuc
xo<ab   {oa} {ob} a→b | tri> ¬a ¬b c = ⊥-elim ( o<¬≡ refl (a→b c )  )

maxα :   Ordinal  →  Ordinal  → Ordinal
maxα x y with trio< x y
maxα x y | tri< a ¬b ¬c = y
maxα x y | tri> ¬a ¬b c = x
maxα x y | tri≈ ¬a refl ¬c = x

omin :    Ordinal  →  Ordinal  → Ordinal
omin  x y with trio<  x  y
omin x y | tri< a ¬b ¬c = x
omin x y | tri> ¬a ¬b c = y
omin x y | tri≈ ¬a refl ¬c = x

min1 :   {x y z : Ordinal  } → z o< x → z o< y → z o< omin x y
min1  {x} {y} {z} z<x z<y with trio<  x y
min1  {x} {y} {z} z<x z<y | tri< a ¬b ¬c = z<x
min1  {x} {y} {z} z<x z<y | tri≈ ¬a refl ¬c = z<x
min1  {x} {y} {z} z<x z<y | tri> ¬a ¬b c = z<y

--
--  max ( osuc x , osuc y )
--

omax :  ( x y : Ordinal  ) → Ordinal
omax  x y with trio< x y
omax  x y | tri< a ¬b ¬c = osuc y
omax  x y | tri> ¬a ¬b c = osuc x
omax  x y | tri≈ ¬a refl ¬c  = osuc x

omax< :  ( x y : Ordinal  ) → x o< y → osuc y ≡ omax x y
omax<  x y lt with trio< x y
omax<  x y lt | tri< a ¬b ¬c = refl
omax<  x y lt | tri≈ ¬a b ¬c = ⊥-elim (¬a lt )
omax<  x y lt | tri> ¬a ¬b c = ⊥-elim (¬a lt )

omax≤ :  ( x y : Ordinal  ) → x o≤ y → osuc y ≡ omax x y
omax≤  x y le with trio< x y
omax≤  x y le | tri< a ¬b ¬c = refl
omax≤  x y le | tri≈ ¬a refl ¬c = refl
omax≤  x y le | tri> ¬a ¬b c with osuc-≡< le
omax≤ x y le | tri> ¬a ¬b c | case1 eq = ⊥-elim (¬b eq)
omax≤ x y le | tri> ¬a ¬b c | case2 x<y = ⊥-elim (¬a x<y)

omax≡ :  ( x y : Ordinal  ) → x ≡ y → osuc y ≡ omax x y
omax≡  x y eq with trio< x y
omax≡  x y eq | tri< a ¬b ¬c = ⊥-elim (¬b eq )
omax≡  x y eq | tri≈ ¬a refl ¬c = refl
omax≡  x y eq | tri> ¬a ¬b c = ⊥-elim (¬b eq )

omax-x :  ( x y : Ordinal  ) → x o< omax x y
omax-x  x y with trio< x y
omax-x  x y | tri< a ¬b ¬c = ordtrans a <-osuc
omax-x  x y | tri> ¬a ¬b c = <-osuc
omax-x  x y | tri≈ ¬a refl ¬c = <-osuc

omax-y :  ( x y : Ordinal  ) → y o< omax x y
omax-y  x y with  trio< x y
omax-y  x y | tri< a ¬b ¬c = <-osuc
omax-y  x y | tri> ¬a ¬b c = ordtrans c <-osuc
omax-y  x y | tri≈ ¬a refl ¬c = <-osuc

omxx :  ( x : Ordinal  ) → omax x x ≡ osuc x
omxx  x with  trio< x x
omxx  x | tri< a ¬b ¬c = ⊥-elim (¬b refl )
omxx  x | tri> ¬a ¬b c = ⊥-elim (¬b refl )
omxx  x | tri≈ ¬a refl ¬c = refl

omxxx :  ( x : Ordinal  ) → omax x (omax x x ) ≡ osuc (osuc x)
omxxx  x = trans ( cong ( λ k → omax x k ) (omxx x )) (sym ( omax< x (osuc x) <-osuc ))

open _∧_

o≤-refl :  { i  j : Ordinal } → i ≡ j → i o≤ j
o≤-refl {i} {j} eq = subst (λ k → i o< osuc k ) eq <-osuc
OrdTrans :  Transitive  _o≤_
OrdTrans a≤b b≤c with osuc-≡< a≤b | osuc-≡< b≤c
OrdTrans a≤b b≤c | case1 refl | case1 refl = <-osuc
OrdTrans a≤b b≤c | case1 refl | case2 a≤c = ordtrans a≤c <-osuc
OrdTrans a≤b b≤c | case2 a≤c | case1 refl = ordtrans a≤c <-osuc
OrdTrans a≤b b≤c | case2 a<b | case2 b<c = ordtrans (ordtrans a<b  b<c) <-osuc

OrdPreorder :   Preorder n n n
OrdPreorder  = record { Carrier = Ordinal
   ; _≈_  = _≡_
   ; _∼_   = _o≤_
   ; isPreorder   = record {
        isEquivalence = record { refl = refl  ; sym = sym ; trans = trans }
        ; reflexive     = o≤-refl
        ; trans         = OrdTrans
     }
 }

FExists : {m l : Level} → ( ψ : Ordinal  → Set m )
  → {p : Set l} ( P : { y : Ordinal  } →  ψ y → ¬ p )
  → (exists : ¬ (∀ y → ¬ ( ψ y ) ))
  → ¬ p
FExists  {m} {l} ψ {p} P = contra-position ( λ p y ψy → P {y} ψy p )

nexto∅ : {x : Ordinal} → o∅ o< next x
nexto∅ {x} with trio< o∅ x
nexto∅ {x} | tri< a ¬b ¬c = ordtrans a x<nx
nexto∅ {x} | tri≈ ¬a b ¬c = subst (λ k → k o< next x) (sym b) x<nx
nexto∅ {x} | tri> ¬a ¬b c = ⊥-elim ( ¬x<0 c )

next< : {x y z : Ordinal} → x o< next z  → y o< next x → y o< next z
next< {x} {y} {z} x<nz y<nx with trio< y (next z)
next< {x} {y} {z} x<nz y<nx | tri< a ¬b ¬c = a
next< {x} {y} {z} x<nz y<nx | tri≈ ¬a b ¬c = ⊥-elim (¬nx<nx x<nz (subst (λ k → k o< next x) b y<nx)
   (λ w nz=ow → o<¬≡ nz=ow (subst₂ (λ j k → j o< k ) (sym nz=ow) nz=ow (osuc<nx (subst (λ k → w o< k ) (sym nz=ow) <-osuc) ))))
next< {x} {y} {z} x<nz y<nx | tri> ¬a ¬b c = ⊥-elim (¬nx<nx x<nz (ordtrans c y<nx )
   (λ w nz=ow → o<¬≡ (sym nz=ow) (osuc<nx (subst (λ k → w o< k ) (sym nz=ow) <-osuc ))))

osuc< : {x y : Ordinal} → osuc x ≡ y → x o< y
osuc< {x} {y} refl = <-osuc

nexto=n : {x y : Ordinal} → x o< next (osuc y)  → x o< next y
nexto=n {x} {y} x<noy = next< (osuc<nx x<nx) x<noy

nexto≡ : {x : Ordinal} → next x ≡ next (osuc x)
nexto≡ {x} with trio< (next x) (next (osuc x) )
--    next x o< next (osuc x ) ->  osuc x o< next x o< next (osuc x) -> next x ≡ osuc z -> z o o< next x -> osuc z o< next x -> next x o< next x
nexto≡ {x} | tri< a ¬b ¬c = ⊥-elim (¬nx<nx (osuc<nx  x<nx ) a
   (λ z eq → o<¬≡ (sym eq) (osuc<nx  (osuc< (sym eq)))))
nexto≡ {x} | tri≈ ¬a b ¬c = b
--    next (osuc x) o< next x ->  osuc x o< next (osuc x) o< next x -> next (osuc x) ≡ osuc z -> z o o< next (osuc x) ...
nexto≡ {x} | tri> ¬a ¬b c = ⊥-elim (¬nx<nx (ordtrans <-osuc x<nx) c
   (λ z eq → o<¬≡ (sym eq) (osuc<nx  (osuc< (sym eq)))))

next-is-limit : {x y : Ordinal} → ¬ (next x ≡ osuc y)
next-is-limit {x} {y} eq = o<¬≡ (sym eq) (osuc<nx y<nx) where
    y<nx : y o< next x
    y<nx = osuc< (sym eq)

omax<next : {x y : Ordinal} → x o< y → omax x y o< next y
omax<next {x} {y} x<y = subst (λ k → k o< next y ) (omax< _ _ x<y ) (osuc<nx x<nx)

x<ny→≡next : {x y : Ordinal} → x o< y → y o< next x → next x ≡ next y
x<ny→≡next {x} {y} x<y y<nx with trio< (next x) (next y)
x<ny→≡next {x} {y} x<y y<nx | tri< a ¬b ¬c =          -- x < y < next x <  next y ∧ next x = osuc z
     ⊥-elim ( ¬nx<nx y<nx a (λ z eq →  o<¬≡ (sym eq) (osuc<nx (subst (λ k → z o< k ) (sym eq) <-osuc ))))
x<ny→≡next {x} {y} x<y y<nx | tri≈ ¬a b ¬c = b
x<ny→≡next {x} {y} x<y y<nx | tri> ¬a ¬b c =          -- x < y < next y < next x
     ⊥-elim ( ¬nx<nx (ordtrans x<y x<nx) c (λ z eq →  o<¬≡ (sym eq) (osuc<nx (subst (λ k → z o< k ) (sym eq) <-osuc ))))

≤next : {x y : Ordinal} → x o≤ y → next x o≤ next y
≤next {x} {y} x≤y with trio< (next x) y
≤next {x} {y} x≤y | tri< a ¬b ¬c = ordtrans a (ordtrans x<nx <-osuc )
≤next {x} {y} x≤y | tri≈ ¬a refl ¬c = (ordtrans x<nx <-osuc )
≤next {x} {y} x≤y | tri> ¬a ¬b c with osuc-≡< x≤y
≤next {x} {y} x≤y | tri> ¬a ¬b c | case1 refl = o≤-refl refl   -- x = y < next x
≤next {x} {y} x≤y | tri> ¬a ¬b c | case2 x<y = o≤-refl (x<ny→≡next x<y c) -- x ≤ y < next x

x<ny→≤next : {x y : Ordinal} → x o< next y → next x o≤ next y
x<ny→≤next {x} {y} x<ny with trio< x y
x<ny→≤next {x} {y} x<ny | tri< a ¬b ¬c =  ≤next (ordtrans a <-osuc )
x<ny→≤next {x} {y} x<ny | tri≈ ¬a refl ¬c =  o≤-refl refl
x<ny→≤next {x} {y} x<ny | tri> ¬a ¬b c = o≤-refl (sym ( x<ny→≡next c x<ny ))

omax<nomax : {x y : Ordinal} → omax x y o< next (omax x y )
omax<nomax {x} {y} with trio< x y
omax<nomax {x} {y} | tri< a ¬b ¬c    = subst (λ k → osuc y o< k ) nexto≡ (osuc<nx x<nx )
omax<nomax {x} {y} | tri≈ ¬a refl ¬c = subst (λ k → osuc x o< k ) nexto≡ (osuc<nx x<nx )
omax<nomax {x} {y} | tri> ¬a ¬b c    = subst (λ k → osuc x o< k ) nexto≡ (osuc<nx x<nx )

omax<nx : {x y z : Ordinal} → x o< next z → y o< next z → omax x y o< next z
omax<nx {x} {y} {z} x<nz y<nz with trio< x y
omax<nx {x} {y} {z} x<nz y<nz | tri< a ¬b ¬c = osuc<nx y<nz
omax<nx {x} {y} {z} x<nz y<nz | tri≈ ¬a refl ¬c = osuc<nx y<nz
omax<nx {x} {y} {z} x<nz y<nz | tri> ¬a ¬b c = osuc<nx x<nz

nn<omax : {x nx ny : Ordinal} → x o< next nx → x o< next ny → x o< next (omax nx ny)
nn<omax {x} {nx} {ny} xnx xny with trio< nx ny
nn<omax {x} {nx} {ny} xnx xny | tri< a ¬b ¬c = subst (λ k → x o< k ) nexto≡ xny
nn<omax {x} {nx} {ny} xnx xny | tri≈ ¬a refl ¬c = subst (λ k → x o< k ) nexto≡ xny
nn<omax {x} {nx} {ny} xnx xny | tri> ¬a ¬b c = subst (λ k → x o< k ) nexto≡ xnx

record OrdinalSubset (maxordinal : Ordinal) : Set (suc n) where
  field
    os→ : (x : Ordinal) → x o< maxordinal → Ordinal
    os← : Ordinal → Ordinal
    os←limit : (x : Ordinal) → os← x o< maxordinal
    os-iso← : {x : Ordinal} →  os→  (os← x) (os←limit x) ≡ x
    os-iso→ : {x : Ordinal} → (lt : x o< maxordinal ) →  os← (os→ x lt) ≡ x

module o≤-Reasoning {n : Level}  (O : Ordinals {n} )  where

    -- open inOrdinal O

    resp-o< : _o<_ Respects₂ _≡_
    resp-o< =  resp₂ _o<_

    trans1 : {i j k : Ordinal} → i o< j → j o< osuc  k → i o< k
    trans1 {i} {j} {k} i<j j<ok with osuc-≡< j<ok
    trans1 {i} {j} {k} i<j j<ok | case1 refl = i<j
    trans1 {i} {j} {k} i<j j<ok | case2 j<k = ordtrans i<j j<k

    trans2 : {i j k : Ordinal} → i o< osuc j → j o<  k → i o< k
    trans2 {i} {j} {k} i<oj j<k with osuc-≡< i<oj
    trans2 {i} {j} {k} i<oj j<k | case1 refl = j<k
    trans2 {i} {j} {k} i<oj j<k | case2 i<j = ordtrans i<j j<k

    open import Relation.Binary.Reasoning.Base.Triple
      (Preorder.isPreorder OrdPreorder)
         ordtrans --<-trans
         (resp₂ _o<_) --(resp₂ _<_)
         (λ x → ordtrans x <-osuc ) --<⇒≤
         trans1 --<-transˡ
         trans2 --<-transʳ
         public
         -- hiding (_≈⟨_⟩_)


-- Copyright:  (c) 2016 Ertugrul Söylemez
-- License:    BSD3
-- Maintainer: Ertugrul Söylemez <esz@posteo.de>

module Data.Nat where

open import Algebra.Group
open import Classes
open import Core
open import Data.Nat.Core
open import Data.Nat.Core public
  using (ℕ; ℕ-Number; ℕ-Plus; ℕ-Times;
         ℕ,≤; module ℕ,≤;
         suc; zero)

ℕ,+ : Monoid
ℕ,+ =
  record {
    semigroup = record {
      A = ℕ;
      Eq = PropEq ℕ;
      semigroupOver = record {
        _⋄_ = _+_;
        ⋄-cong = cong2 _+_;
        assoc = Props.+-assoc
      }
    };
    isMonoid = record {
      id = 0;
      left-id = Props.+-left-id;
      right-id = Props.+-right-id
    }
  }

module ℕ,+ = Monoid ℕ,+

ℕ,* : Monoid
ℕ,* =
  record {
    semigroup = record {
      A = ℕ;
      Eq = PropEq ℕ;
      semigroupOver = record {
        _⋄_ = _*_;
        ⋄-cong = cong2 _*_;
        assoc = Props.*-assoc
      }
    };
    isMonoid = record {
      id = 1;
      left-id = Props.*-left-id;
      right-id = Props.*-right-id
    }
  }

module ℕ,* = Monoid ℕ,*


open import Oscar.Prelude
open import Oscar.Class

module Oscar.Class.Symmetry where

module Symmetry'
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  x y
  = ℭLASS (x ,, y ,, _∼_) (x ∼ y → y ∼ x)

module Symmetry
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  where
  class = ∀ {x y} → Symmetry'.class _∼_ x y
  type = ∀ {x y} → Symmetry'.type _∼_ x y
  method : ⦃ _ : class ⦄ → type
  method {x = x} {y} = Symmetry'.method _∼_ x y

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} {_∼_ : 𝔒 → 𝔒 → Ø 𝔯}
  where
  symmetry = Symmetry.method _∼_
  syntax symmetry {x} {y} x∼y = x ⟨∼ x∼y ∼⟩ y

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  ⦃ _ : Symmetry.class _∼_ ⦄
  where
  symmetry[_] = λ {x y} (x∼y : x ∼ y) → Symmetry.method _∼_ x∼y


--------------------------------------------------------------------------------
-- This is part of Agda Inference Systems

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

open import Data.Nat
open import Data.Vec
open import Data.Fin
open import Data.Product
open import Data.Sum
open import Data.Unit
open import Data.Empty
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Size

open import is-lib.SInfSys
open import Examples.Lambda.Lambda

module Examples.Lambda.BigStep where

  data Value∞ : Set where
    res : Value → Value∞
    div : Value∞

  U : Set
  U = Term 0 × Value∞

  data BigStepRN : Set where
    VAL APP L-DIV R-DIV : BigStepRN

  data BigStepCoRN : Set where
    COA : BigStepCoRN

  coa-r : FinMetaRule U
  coa-r .Ctx = Term 0
  coa-r .comp t =
    [] ,
    -------------------------
    (t , div)

  val-r : FinMetaRule U
  val-r .Ctx = Value
  val-r .comp v =
    [] ,
    -------------------------
    (term v , res v)

  app-r : FinMetaRule U
  app-r .Ctx = Term 0 × Term 1 × Term 0 × Value × Value∞
  app-r .comp (t1 , t , t2 , v , v∞) =
    (t1 , res (lambda t))  ∷ (t2 , res v) ∷ (subst-0 t (term v) , v∞) ∷ [] ,
    -------------------------
    (app t1 t2 , v∞)

  l-div-r : FinMetaRule U
  l-div-r .Ctx = Term 0 × Term 0
  l-div-r .comp (t1 , t2) =
    (t1 , div) ∷ [] ,
    -------------------------
    (app t1 t2 , div)

  r-div-r : FinMetaRule U
  r-div-r .Ctx = Term 0 × Term 0 × Value
  r-div-r .comp (t1 , t2 , v) =
    (t1 , res v) ∷ (t2 , div) ∷ [] ,
    -------------------------
    (app t1 t2 , div)

  BigStepIS : IS U
  BigStepIS .Names = BigStepRN
  BigStepIS .rules VAL = from val-r
  BigStepIS .rules APP = from app-r
  BigStepIS .rules L-DIV = from l-div-r
  BigStepIS .rules R-DIV = from r-div-r

  BigStepCOIS : IS U
  BigStepCOIS .Names = BigStepCoRN
  BigStepCOIS .rules COA = from coa-r

  _⇓_ : Term 0 → Value∞ → Size → Set
  (t ⇓ v∞) i = SFCoInd⟦ BigStepIS , BigStepCOIS ⟧ (t , v∞) i

  _⇓ᵢ_ : Term 0 → Value∞ → Set
  t ⇓ᵢ v∞ = Ind⟦ BigStepIS ∪ BigStepCOIS ⟧ (t , v∞)

  {- Properties -}

  val-not-reduce⇓ : ∀{v} → ¬ (∀{i} → ((term v) ⇓ div) i)
  val-not-reduce⇓ {lambda _} bs with bs
  val-not-reduce⇓ {lambda _} bs | (sfold (VAL , _ , () , _))

  val-⇓ᵢ-≡ : ∀{v v'} → term v ⇓ᵢ res v' → v ≡ v'
  val-⇓ᵢ-≡ {lambda x} {lambda .x} (fold (inj₁ VAL , .(lambda x) , refl , _)) = refl
  val-⇓ᵢ-≡ {lambda x} {lambda x₁} (fold (inj₁ APP , _ , () , _))
  val-⇓ᵢ-≡ {v} {v'} (fold (inj₂ COA , _ , () , _))

  val-⇓-≡ : ∀{v v'} → (∀{i} → (term v ⇓ res v') i) → v ≡ v'
  val-⇓-≡ bs = val-⇓ᵢ-≡ (sfcoind-to-ind bs)


open import Prelude
open import Nat
open import Agda.Primitive using (Level; lzero; lsuc) renaming (_⊔_ to lmax)

module List where

  -- definitions

  data List (A : Set) : Set where
    [] : List A
    _::_ : A → List A → List A

  _++_ : {A : Set} → List A → List A → List A
  [] ++ l₂ = l₂
  (h :: l₁) ++ l₂ = h :: (l₁ ++ l₂)

  infixl 50 _++_

  ∥_∥ : {A : Set} → List A → Nat
  ∥ [] ∥ = Z
  ∥ a :: as ∥ = 1+ ∥ as ∥

  _⟦_⟧ : {A : Set} → List A → Nat → Maybe A
  [] ⟦ i ⟧ = None
  (a :: as) ⟦ Z ⟧ = Some a
  (a :: as) ⟦ 1+ i ⟧ = as ⟦ i ⟧

  data _In_ : {A : Set} → A → List A → Set where
    LInH : ∀{A} {a : A} {l : List A} → a In (a :: l)
    LInT : ∀{A} {a a' : A} {l : List A} → a In l → a In (a' :: l)

  _⫇_ : {A : Set} → List A → List A → Set
  _⫇_ {A} l1 l2 = (i1 : Nat) (a : A) →
                  l1 ⟦ i1 ⟧ == Some a →
                  Σ[ i2 ∈ Nat ] (l2 ⟦ i2 ⟧ == Some a)

  _≈_ : {A : Set} → List A → List A → Set
  _≈_ {A} l1 l2 = l1 ⫇ l2 ∧ l2 ⫇ l1

  reverse : {A : Set} → List A → List A
  reverse [] = []
  reverse (a :: as) = reverse as ++ (a :: [])

  map : {A B : Set} → (A → B) → List A → List B
  map f [] = []
  map f (a :: as) = f a :: map f as

  foldl : {A B : Set} → (B → A → B) → B → List A → B
  foldl f b [] = b
  foldl f b (a :: as) = foldl f (f b a) as

  foldr : {A B : Set} → (B → A → B) → B → List A → B
  foldr f b = foldl f b ⊙ reverse

  concat : {A : Set} → List (List A) → List A
  concat [] = []
  concat (l1 :: rest) = l1 ++ (concat rest)

  -- if the lists aren't the same length,
  -- the extra elements of the longer list are ignored
  zip : {A B : Set} → List A → List B → List (A ∧ B)
  zip []        _         = []
  zip (a :: as) []        = []
  zip (a :: as) (b :: bs) = (a , b) :: zip as bs

  unzip : {A B : Set} → List (A ∧ B) → (List A ∧ List B)
  unzip [] = ([] , [])
  unzip ((a , b) :: rest)
    with unzip rest
  ... | (as , bs) = (a :: as , b :: bs)

  -- theorems

  list-==-dec :  {A : Set} →
                 (l1 l2 : List A) →
                 ((a1 a2 : A) → a1 == a2 ∨ a1 ≠ a2) →
                 l1 == l2 ∨ l1 ≠ l2
  list-==-dec [] [] A-==-dec       = Inl refl
  list-==-dec [] (_ :: _) A-==-dec = Inr (λ ())
  list-==-dec (_ :: _) [] A-==-dec = Inr (λ ())
  list-==-dec (h1 :: t1) (h2 :: t2) A-==-dec
    with A-==-dec h1 h2
  ... | Inr ne = Inr (λ where refl → ne refl)
  ... | Inl refl
    with list-==-dec t1 t2 A-==-dec
  ... | Inr ne = Inr (λ where refl → ne refl)
  ... | Inl refl = Inl refl

  -- if the items of two lists are equal, then the lists are equal
  ==-per-elem : {A : Set} → {l1 l2 : List A} →
                 ((i : Nat) → l1 ⟦ i ⟧ == l2 ⟦ i ⟧) →
                 l1 == l2
  ==-per-elem {l1 = []} {[]} items== = refl
  ==-per-elem {l1 = []} {h2 :: t2} items== = abort (somenotnone (! (items== Z)))
  ==-per-elem {l1 = h1 :: t1} {[]} items== = abort (somenotnone (items== Z))
  ==-per-elem {l1 = h1 :: t1} {h2 :: t2} items==
    rewrite someinj (items== Z) | ==-per-elem {l1 = t1} {t2} (λ i → items== (1+ i))
      = refl

  -- _++_ theorems
  ++assc : ∀{A a1 a2 a3} → (_++_ {A} a1 a2) ++ a3 == a1 ++ (a2 ++ a3)
  ++assc {A} {[]} {a2} {a3} = refl
  ++assc {A} {x :: a1} {a2} {a3} with a1 ++ a2 ++ a3 | ++assc {A} {a1} {a2} {a3}
  ++assc {A} {x :: a1} {a2} {a3} | _ | refl = refl

  l++[]==l : {A : Set} (l : List A) →
              l ++ [] == l
  l++[]==l [] = refl
  l++[]==l (a :: as)
    rewrite l++[]==l as
      = refl

  -- ∥_∥ theorem
  ∥-++-comm : ∀{A a1 a2} → ∥ a1 ∥ + (∥_∥ {A} a2) == ∥ a1 ++ a2 ∥
  ∥-++-comm {A} {[]} {a2} = refl
  ∥-++-comm {A} {a :: a1} {a2} = 1+ap (∥-++-comm {A} {a1})

  -- _⟦_⟧ and ++ theorem
  ⦇l1++[a]++l2⦈⟦∥l1∥⟧==a : {A : Set} {l1 l2 : List A} {a : A} →
                             (l1 ++ (a :: []) ++ l2) ⟦ ∥ l1 ∥ ⟧ == Some a
  ⦇l1++[a]++l2⦈⟦∥l1∥⟧==a {l1 = []} = refl
  ⦇l1++[a]++l2⦈⟦∥l1∥⟧==a {l1 = a1 :: l1rest} {l2} {a} = ⦇l1++[a]++l2⦈⟦∥l1∥⟧==a {l1 = l1rest} {l2}

  -- packaging of list indexing results
  list-index-dec : {A : Set} (l : List A) (i : Nat) →
                    l ⟦ i ⟧ == None ∨ Σ[ a ∈ A ] (l ⟦ i ⟧ == Some a)
  list-index-dec l i
    with l ⟦ i ⟧
  ... | None   = Inl refl
  ... | Some a = Inr (a , refl)

  -- theorems characterizing the partiality of list indexing
  list-index-some : {A : Set} {l : List A} {i : Nat} →
                     i < ∥ l ∥ →
                     Σ[ a ∈ A ] (l ⟦ i ⟧ == Some a)
  list-index-some {l = []}      {Z}    i<∥l∥ = abort (n≮0 i<∥l∥)
  list-index-some {l = a :: as} {Z}    i<∥l∥ = _ , refl
  list-index-some {l = a :: as} {1+ i} i<∥l∥ = list-index-some (1+n<1+m→n<m i<∥l∥)

  list-index-none : {A : Set} {l : List A} {i : Nat} →
                     ∥ l ∥ ≤ i →
                     l ⟦ i ⟧ == None
  list-index-none {l = []}             i≥∥l∥ = refl
  list-index-none {l = a :: as} {1+ i} i≥∥l∥ = list-index-none (1+n≤1+m→n≤m i≥∥l∥)

  list-index-some-conv : {A : Set} {l : List A} {i : Nat} {a : A} →
                          l ⟦ i ⟧ == Some a →
                          i < ∥ l ∥
  list-index-some-conv {l = l} {i} i≥∥l∥
    with <dec ∥ l ∥ i
  ... | Inr (Inr i<∥l∥) = i<∥l∥
  ... | Inr (Inl refl)
    rewrite list-index-none {l = l} {∥ l ∥} ≤refl
      = abort (somenotnone (! i≥∥l∥))
  ... | Inl ∥l∥<i
    rewrite list-index-none (n<m→n≤m ∥l∥<i)
      = abort (somenotnone (! i≥∥l∥))

  list-index-none-conv : {A : Set} {l : List A} {i : Nat} →
                          l ⟦ i ⟧ == None →
                          ∥ l ∥ ≤ i
  list-index-none-conv {l = l} {i} i≥∥l∥
    with <dec ∥ l ∥ i
  ... | Inl ∥l∥<i       = n<m→n≤m ∥l∥<i
  ... | Inr (Inl refl) = ≤refl
  ... | Inr (Inr i<∥l∥)
    with list-index-some i<∥l∥
  ... | _ , i≱∥l∥ rewrite i≥∥l∥ = abort (somenotnone (! i≱∥l∥))

  ∥l1∥==∥l2∥→l1[i]→l2[i] : {A B : Set} {la : List A} {lb : List B} {i : Nat} {a : A} →
                             ∥ la ∥ == ∥ lb ∥ →
                             la ⟦ i ⟧ == Some a →
                             Σ[ b ∈ B ] (lb ⟦ i ⟧ == Some b)
  ∥l1∥==∥l2∥→l1[i]→l2[i] {i = i} ∥la∥==∥lb∥ la[i]==a
    = list-index-some (tr (λ y → i < y) ∥la∥==∥lb∥ (list-index-some-conv la[i]==a))

  ∥l1∥==∥l2∥→¬l1[i]→¬l2[i] : {A B : Set} {la : List A} {lb : List B} {i : Nat} →
                                ∥ la ∥ == ∥ lb ∥ →
                                la ⟦ i ⟧ == None →
                                lb ⟦ i ⟧ == None
  ∥l1∥==∥l2∥→¬l1[i]→¬l2[i] {i = i} ∥la∥==∥lb∥ la[i]==None
    = list-index-none (tr (λ y → y ≤ i) ∥la∥==∥lb∥ (list-index-none-conv la[i]==None))

  -- more list indexing theorems
  i∈l2→|l1|+i∈l1++l2 : {A : Set} {l1 l2 : List A} {i : Nat} {a : A} →
                          l2 ⟦ i ⟧ == Some a →
                          (l1 ++ l2) ⟦ ∥ l1 ∥ + i ⟧ == Some a
  i∈l2→|l1|+i∈l1++l2 {l1 = []} i∈l2 = i∈l2
  i∈l2→|l1|+i∈l1++l2 {l1 = a1 :: l1} i∈l2 = i∈l2→|l1|+i∈l1++l2 {l1 = l1} i∈l2

  i∈l1→i∈l1++l2 : {A : Set} {l1 l2 : List A} {i : Nat} {a : A} →
                      l1 ⟦ i ⟧ == Some a →
                      (l1 ++ l2) ⟦ i ⟧ == Some a
  i∈l1→i∈l1++l2 {l1 = a1 :: l1} {i = Z} i∈l1 = i∈l1
  i∈l1→i∈l1++l2 {l1 = a1 :: l1} {i = 1+ i} i∈l1 = i∈l1→i∈l1++l2 {l1 = l1} i∈l1

  append-index-split : {A : Set} {l1 l2 : List A} {i : Nat} {a : A} →
                         (l1 ++ l2) ⟦ i ⟧ == Some a →
                         l1 ⟦ i ⟧ == Some a ∨ (Σ[ j ∈ Nat ] (∥ l1 ∥ + j == i ∧ l2 ⟦ j ⟧ == Some a))
  append-index-split {l1 = []} i∈l1==l2 = Inr (_ , refl , i∈l1==l2)
  append-index-split {l1 = a1 :: l1} {i = Z} i∈l1==l2 = Inl i∈l1==l2
  append-index-split {l1 = a1 :: l1} {i = 1+ i} i∈l1==l2
    with append-index-split {l1 = l1} i∈l1==l2
  ... | Inl i∈l1 = Inl i∈l1
  ... | Inr (j , |l1|+j=i , j∈l2) = Inr (_ , 1+ap |l1|+j=i , j∈l2)

  -- reverse theorems
  reverse-single : {A : Set} {a : A} → reverse (a :: []) == a :: []
  reverse-single = refl

  reverse-++ : {A : Set} {l1 l2 : List A} →
                reverse (l1 ++ l2) == reverse l2 ++ reverse l1
  reverse-++ {l1 = []} {l2}
    rewrite l++[]==l (reverse l2)
      = refl
  reverse-++ {l1 = a1 :: as1} {l2}
    rewrite reverse-++ {l1 = as1} {l2}
      = ++assc {a1 = reverse l2}

  reverse-inv : {A : Set} {l : List A} → reverse (reverse l) == l
  reverse-inv {l = []} = refl
  reverse-inv {l = a :: as}
    rewrite reverse-++ {l1 = reverse as} {a :: []} | reverse-inv {l = as}
      = refl

  -- map theorems
  map-ext : ∀{A B : Set} {f g : A → B} {l : List A} →
              ((a : A) → f a == g a) →
              map f l == map g l
  map-ext {l = []} ext = refl
  map-ext {l = a :: l} ext rewrite ext a | map-ext {l = l} ext = refl

  map-id : ∀{A : Set} {l : List A} → map (λ a → a) l == l
  map-id {l = []} = refl
  map-id {l = a :: l} rewrite map-id {l = l} = refl

  map-len : ∀{A B : Set} {f : A → B} {l : List A} → ∥ map f l ∥ == ∥ l ∥
  map-len {l = []} = refl
  map-len {l = _ :: l} = 1+ap map-len

  map-In : ∀{A B : Set} {f : A → B} {l : List A} {a : A} →
             a In l →
             f a In map f l
  map-In LInH = LInH
  map-In (LInT aInL) = LInT (map-In aInL)

  map-++-comm : ∀{A B f a b} → map f a ++ map f b == map {A} {B} f (a ++ b)
  map-++-comm {a = []} = refl
  map-++-comm {A} {B} {f} {h :: t} {b} with map f (t ++ b) | map-++-comm {A} {B} {f} {t} {b}
  map-++-comm {A} {B} {f} {h :: t} {b} | _ | refl = refl

  map^2 : ∀{A B C f g l} → map {B} {C} f (map {A} {B} g l) == map (f ⊙ g) l
  map^2 {l = []} = refl
  map^2 {f = f} {g} {h :: t} rewrite map^2 {f = f} {g} {t} = refl

  reverse-map : {A B : Set} {f : A → B} {l : List A} → reverse (map f l) == map f (reverse l)
  reverse-map {l = []} = refl
  reverse-map {f = f} {a' :: l}
    rewrite reverse-map {f = f} {l}
      = map-++-comm {a = reverse l}

  -- foldl theorem
  foldl-++ : {A B : Set} {l1 l2 : List A} {f : B → A → B} {b0 : B} →
              foldl f b0 (l1 ++ l2) == foldl f (foldl f b0 l1) l2
  foldl-++ {l1 = []} = refl
  foldl-++ {l1 = a1 :: l1rest} = foldl-++ {l1 = l1rest}

  -- fold+map theorem
  foldl-map : {A' A B : Set} {l : List A'} {f-fold : B → A → B} {f-map : A' → A} {b0 : B} →
               foldl f-fold b0 (map f-map l) == foldl (λ b a' → f-fold b (f-map a')) b0 l
  foldl-map {l = []} = refl
  foldl-map {l = a' :: l} {f-fold} {f-map} {b0} = foldl-map {l = l}

  foldr-map : {A' A B : Set} {l : List A'} {f-fold : B → A → B} {f-map : A' → A} {b0 : B} →
               foldr f-fold b0 (map f-map l) == foldr (λ b a' → f-fold b (f-map a')) b0 l
  foldr-map {l = l} {_} {f}
    rewrite reverse-map {f = f} {l}
      = foldl-map {l = reverse l}

  -- zip/unzip theorems
  unzip-inv : {A B : Set} {l : List (A ∧ B)} {la : List A} {lb : List B} →
               unzip l == (la , lb) →
               l == zip la lb
  unzip-inv {l = []} form
    with form
  ... | refl = refl
  unzip-inv {l = (a' , b') :: rest} {a :: as} {b :: bs} form
    with unzip rest  | unzip-inv {l = rest} refl | form
  ...  | (as' , bs') | refl                      | refl = refl

  zip-inv : {A B : Set} {la : List A} {lb : List B} →
             ∥ la ∥ == ∥ lb ∥ →
             unzip (zip la lb) == (la , lb)
  zip-inv {la = []}      {[]}      len-eq = refl
  zip-inv {la = a :: as} {b :: bs} len-eq
    with unzip (zip as bs) | zip-inv (1+inj len-eq)
  ...  | (as' , bs')       | refl                   = refl

  -- _≈_ theorems
  ≈-++-comm-lem' : (A : Set) (l1 l2 : List A) (i : Nat) (a : A) →
                   (l1 ++ l2) ⟦ i ⟧ == Some a →
                   Σ[ i' ∈ Nat ] ((l2 ++ l1) ⟦ i' ⟧ == Some a)
  ≈-++-comm-lem' A l1 l2 i a i∈l1++l2
    with append-index-split {l1 = l1} i∈l1++l2
  ... | Inl i∈l1 = _ , i∈l2→|l1|+i∈l1++l2 {l1 = l2} i∈l1
  ... | Inr (j , |l1|+j=i , j∈l2) = _ , i∈l1→i∈l1++l2 {l1 = l2} j∈l2

  ≈-++-comm : {A : Set} {l1 l2 : List A} → l1 ++ l2 ≈ l2 ++ l1
  ≈-++-comm {A} {l1} {l2} = ≈-++-comm-lem' A l1 l2 , ≈-++-comm-lem' A l2 l1

  ≈-++-dup : {A : Set} {l : List A} → l ++ l ≈ l
  ≈-++-dup {l = []} = (λ i a ()) , (λ i a ())
  ≈-++-dup {A} {a :: l}
    = ≈-++-dup'1 , ≈-++-dup'2
      where
      ≈-++-dup'1 : (i : Nat) (a' : A) →
                   (a :: (l ++ (a :: l))) ⟦ i ⟧ == Some a' →
                   Σ[ i' ∈ Nat ] ((a :: l) ⟦ i' ⟧ == Some a')
      ≈-++-dup'1 Z a' i∈alal = Z , i∈alal
      ≈-++-dup'1 (1+ i) a' i∈alal
        with append-index-split {l1 = l} i∈alal
      ... | Inl i∈l = (1+ i) , i∈l
      ... | Inr (j , |l1|+j=i , j∈al) = j , j∈al

      ≈-++-dup'2 : (i : Nat) (a' : A) →
                   (a :: l) ⟦ i ⟧ == Some a' →
                   Σ[ i' ∈ Nat ] ((a :: (l ++ (a :: l))) ⟦ i' ⟧ == Some a')
      ≈-++-dup'2 i a' i∈al = (1 + ∥ l ∥ + i) , i∈l2→|l1|+i∈l1++l2 {l1 = l} i∈al


-- Andreas, 2014-07-02 Documenting the ETA pragma

open import Common.Equality

mutual
  data Colist (A : Set) : Set where
    [] : Colist A
    _∷_ : A → ∞Colist A → Colist A

  record ∞Colist (A : Set) : Set where
    eta-equality
    coinductive
    constructor delay
    field       force : Colist A

open ∞Colist

-- By default, recursive records do not have eta,
-- but it can be turned on by force.
-- In case of colists, it is ok, since there is no
-- infinite eta-expansion (unlike for streams).

test : {A : Set} (x : ∞Colist A) → x ≡ delay (force x)
test x = refl


-- Minimal implicational modal logic, de Bruijn approach, initial encoding

module Bi.BoxMp where

open import Lib using (List; []; _,_; LMem; lzero; lsuc)

-- Types

infixr 0 _=>_
data Ty : Set where
  UNIT :             Ty
  _=>_ : Ty -> Ty -> Ty
  BOX  : Ty       -> Ty

-- Context and truth/validity judgements

Cx : Set
Cx = List Ty

isTrue : Ty -> Cx -> Set
isTrue a tc = LMem a tc

isValid : Ty -> Cx -> Set
isValid a vc = LMem a vc

-- Terms

module BoxMp where
  infixl 1 _$_
  infixr 0 lam=>_
  data Tm (vc tc : Cx) : Ty -> Set where
    var    : forall {a}   -> isTrue a tc                     -> Tm vc tc a
    lam=>_ : forall {a b} -> Tm vc (tc , a) b                -> Tm vc tc (a => b)
    _$_    : forall {a b} -> Tm vc tc (a => b) -> Tm vc tc a -> Tm vc tc b
    var#   : forall {a}   -> isValid a vc                    -> Tm vc tc a
    box    : forall {a}   -> Tm vc [] a                      -> Tm vc tc (BOX a)
    unbox' : forall {a b} -> Tm vc tc (BOX a)  -> Tm (vc , a) tc b -> Tm vc tc b

  syntax unbox' x' x = unbox x' => x

  v0 : forall {vc tc a} -> Tm vc (tc , a) a
  v0 = var lzero

  v1 : forall {vc tc a b} -> Tm vc (tc , a , b) a
  v1 = var (lsuc lzero)

  v2 : forall {vc tc a b c} -> Tm vc (tc , a , b , c) a
  v2 = var (lsuc (lsuc lzero))

  v0# : forall {vc tc a} -> Tm (vc , a) tc a
  v0# = var# lzero

  v1# : forall {vc tc a b} -> Tm (vc , a , b) tc a
  v1# = var# (lsuc lzero)

  v2# : forall {vc tc a b c} -> Tm (vc , a , b , c) tc a
  v2# = var# (lsuc (lsuc lzero))

  Thm : Ty -> Set
  Thm a = forall {vc tc} -> Tm vc tc a
open BoxMp public

-- Example theorems

rNec : forall {a} -> Thm a -> Thm (BOX a)
rNec x =
  box x

aK : forall {a b} -> Thm (BOX (a => b) => BOX a => BOX b)
aK =
  lam=>
    lam=>
      (unbox v1 =>
        unbox v0 =>
          box (v1# $ v0#))

aT : forall {a} -> Thm (BOX a => a)
aT =
  lam=>
    (unbox v0 => v0#)

a4 : forall {a} -> Thm (BOX a => BOX (BOX a))
a4 =
  lam=>
    (unbox v0 => box (box v0#))

t1 : forall {a} -> Thm (a => BOX (a => a))
t1 =
  lam=> box (lam=> v0)


-- Type interpretation and soundness of typing.

-- Proof of strong normalization for well-typed terms.

module Soundness where

open import Library
open import Terms
open import Substitution
open import SN
open import SN.AntiRename
open import DeclSN using (sn; fromSN)
open import SAT3

-- Type interpretation

⟦_⟧ : (a : Ty) → SAT a
⟦ base  ⟧  =  ⟦⊥⟧
⟦ a →̂ b ⟧  =  ⟦ a ⟧  ⟦→⟧ ⟦ b ⟧

-- Context interpretation (semantic substitutions)

⟦_⟧C : ∀ Γ → ∀ {Δ} (σ : Subst Γ Δ) → Set
⟦ Γ ⟧C σ = ∀ {a} (x : Var Γ a) → σ x ∈ ⟦ a ⟧

Ext : ∀ {a Δ Γ} {t : Tm Δ a} → (𝒕 : t ∈ (⟦ a ⟧)) →
      ∀ {σ : Subst Γ Δ} (θ : ⟦ Γ ⟧C σ) → ⟦ a ∷ Γ ⟧C (t ∷s σ)
Ext {a} 𝒕 θ (zero)  = 𝒕
Ext {a} 𝒕 θ (suc x) = θ x

Rename : ∀ {Δ Δ'} → (ρ : Ren Δ Δ') →
         ∀ {Γ}{σ : Subst Γ Δ} (θ : ⟦ Γ ⟧C σ) →
         ⟦ Γ ⟧C (ρ •s σ)
Rename ρ θ {a} x = ↿ SAT.satRename ⟦ a ⟧ ρ (⇃ θ x)

sound : ∀ {a Γ} (t : Tm Γ a) {Δ} {σ : Subst Γ Δ} → (θ : ⟦ Γ ⟧C σ) → subst σ t ∈ (⟦ a ⟧)
sound (var x) θ = θ x
sound (abs t) {σ = σ} θ = ⟦abs⟧ {𝓐 = ⟦ _ ⟧} {𝓑 = ⟦ _ ⟧} (λ ρ {u} 𝑢 →
  let open ≡-Reasoning
      eq : subst (u ∷s (ρ •s σ)) t ≡ subst0 u (subst (lifts ρ) (subst (lifts σ) t))
      eq = begin

             subst (u ∷s (ρ •s σ)) t

           ≡⟨ subst-ext (cons-to-sgs u _) t ⟩

              subst (sgs u •s lifts (ρ •s σ)) t

           ≡⟨ subst-∙ _ _ t ⟩

             subst0 u (subst (lifts (ρ •s σ)) t)

           ≡⟨ ≡.cong (subst0 u) (subst-ext (lifts-∙ ρ σ) t) ⟩

             subst0 u (subst (lifts ρ •s lifts σ) t)

           ≡⟨ ≡.cong (subst0 u) (subst-∙ (lifts ρ) (lifts σ) t) ⟩

             subst0 u (subst (lifts ρ) (subst (lifts σ) t))
           ∎
  in (≡.subst (_∈ ⟦ _ ⟧) eq (↿ (⇃ sound t (Ext (↿ (⇃ 𝑢)) ((Rename ρ θ)))))))

sound (app t u) θ = ↿ (⇃ ⟦app⟧ {𝓐 = ⟦ _ ⟧} {𝓑 = ⟦ _ ⟧} (sound t θ) (↿ (⇃ sound u θ)))

-- Identity environment.

id-θ : ∀{Γ} → ⟦ Γ ⟧C ids
id-θ {Γ} {a} x = ⟦var⟧ ⟦ a ⟧ x

-- Any well-typed term inhabits its semantic type.

sound' : ∀ {a Γ} (t : Tm Γ a) → t ∈ ⟦ a ⟧
sound' t rewrite ≡.sym (subst-id {vt = `Tm} t) = sound t id-θ

-- Any well-typed term is strongly normalizing.

strong-normalization :  ∀ a {Γ} (t : Tm Γ a) → sn t
strong-normalization a t = fromSN (satSN ⟦ a ⟧ (⇃ sound' t))

-- Q.E.D.


module #12 where

{-
  Using the propositions as types interpretation, derive the following tautologies.
  (i) If A, then (if B then A).
  (ii) If A, then not (not A).
  (iii) If (not A or not B), then not (A and B).
-}

open import Data.Product
open import Data.Sum
open import Relation.Nullary

const : {A B : Set} → A → (B → A)
const = λ z _ → z

double-negation : {A : Set} → A → ¬ (¬ A)
double-negation = λ z z₁ → z₁ z

demorgans-law₁ : {A B C : Set} → ((¬ A) ⊎ (¬ B)) → (¬ (A × B))
demorgans-law₁ (inj₁ ¬x) (a , _) =  ¬x a
demorgans-law₁ (inj₂ ¬y) (_ , b) =  ¬y b


module #13 where

{-
  Using propositions-as-types, derive the double negation of the principle of excluded
  middle, i.e. prove not (not (P or not P)).
-}

open import Data.Sum
open import Relation.Nullary

excluded-middle : (P : Set) → ¬ (¬ (P ⊎ (¬ P)))
excluded-middle P z = z (inj₂ (λ x → z (inj₁ x)))


module Pullback where

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

module Pull (ℂ : Cat) where

  private open module CC = Category.Category ℂ
  private open module U = Uniq ℂ

  record isPull {A B C D A' : Obj}(f : A ─→ B)(g : A ─→ C)(f' : C ─→ D)(g' : B ─→ D)(h₁ : A' ─→ C)(h₂ : A' ─→ B)(commut : f' ∘ h₁ == g' ∘ h₂) : Set1 where
    field unique  : ∃! \(h : A' ─→ A) -> (g ∘ h == h₁) /\ (f ∘ h == h₂)

  record pullback {B C D : Obj}(g' : B ─→ D)(f' : C ─→ D) : Set1 where
    field
      A    : Obj
      f    : A ─→ B
      g    : A ─→ C
      comm : g' ∘ f == f' ∘ g
      pull : (forall {A' : Obj}(h₁ : A' ─→ C)(h₂ : A' ─→ B)(commut : f' ∘ h₁ == g' ∘ h₂) -> isPull f g f' g' h₁ h₂ commut)

  record PullCat : Set2 where
    field pull : {B C D : Obj}(g' : B ─→ D)(f' : C ─→ D) -> pullback g' f'


module Logic.Propositional.Equiv where

import      Lvl
open import Logic
open import Logic.Propositional
import      Logic.Propositional.Theorems as Theorems
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Structure.Setoid
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓₑ ℓₑ₁ ℓₑ₂ : Lvl.Level
private variable T A B : Type{ℓ}

instance
  [↔]-reflexivity : Reflexivity{ℓ₂ = ℓ}(_↔_)
  [↔]-reflexivity = intro Theorems.[↔]-reflexivity

instance
  [↔]-symmetry : Symmetry{ℓ₂ = ℓ}(_↔_)
  [↔]-symmetry = intro Theorems.[↔]-symmetry

instance
  [↔]-transitivity : Transitivity{ℓ₂ = ℓ}(_↔_)
  [↔]-transitivity = intro Theorems.[↔]-transitivity

instance
  [↔]-equivalence : Equivalence{ℓ₂ = ℓ}(_↔_)
  [↔]-equivalence = intro

instance
  [↔]-equiv : Equiv(Stmt{ℓ})
  [↔]-equiv = intro(_↔_) ⦃ [↔]-equivalence ⦄


open import Signature

module Program (Σ : Sig) (V : Set) where

open import Function
open import Data.Empty
open import Data.Unit
open import Data.Product as Prod renaming (Σ to ⨿)
open import Data.Nat
open import Data.Fin
open import Data.List
open import Terms Σ

FinFam : Set → Set
FinFam X = ⨿ ℕ λ n → (Fin n → X)

dom : ∀{X} → FinFam X → Set
dom (n , _) = Fin n

get : ∀{X} (F : FinFam X) → dom F → X
get (_ , f) k = f k

domEmpty : ∀{X} → FinFam X → Set
domEmpty (zero  , _) = ⊤
domEmpty (suc _ , _) = ⊥

record Clause : Set where
  constructor _⊢_
  field
    body : FinFam (T V)
    head : T V
open Clause public

-- | A program consists of two finite sets of inductive and coinductive clauses.
Program : Set
Program = FinFam Clause × FinFam Clause

dom-μ : Program → Set
dom-μ = dom ∘ proj₁

dom-ν : Program → Set
dom-ν = dom ∘ proj₂

geth-μ : (P : Program) → dom-μ P → T V
geth-μ P = head ∘ get (proj₁ P)

getb-μ : (P : Program) → dom-μ P → FinFam (T V)
getb-μ P = body ∘ get (proj₁ P)

geth-ν : (P : Program) → dom-ν P → T V
geth-ν P = head ∘ get (proj₂ P)

getb-ν : (P : Program) → dom-ν P → FinFam (T V)
getb-ν P = body ∘ get (proj₂ P)


------------------------------------------------------------------------
-- Operations on nullary relations (like negation and decidability)
------------------------------------------------------------------------

-- Some operations on/properties of nullary relations, i.e. sets.

module Relation.Nullary where

open import Data.Product
import Relation.Nullary.Core as Core
open import Relation.Binary
open import Relation.Binary.FunctionSetoid
import Relation.Binary.EqReasoning as EqReasoning

------------------------------------------------------------------------
-- Negation

open Core public using (¬_)

------------------------------------------------------------------------
-- Equivalence

infix 3 _⇔_

_⇔_ : Set → Set → Set
P ⇔ Q = (P → Q) × (Q → P)

------------------------------------------------------------------------
-- Decidable relations

open Core public using (Dec; yes; no)

------------------------------------------------------------------------
-- Injections

Injective : ∀ {A B} → A ⟶ B → Set
Injective {A} {B} f = ∀ {x y} → f ⟨$⟩ x ≈₂ f ⟨$⟩ y → x ≈₁ y
  where
  open Setoid A renaming (_≈_ to _≈₁_)
  open Setoid B renaming (_≈_ to _≈₂_)

_LeftInverseOf_ : ∀ {A B} → B ⟶ A → A ⟶ B → Set
_LeftInverseOf_ {A} f g = ∀ x → f ⟨$⟩ (g ⟨$⟩ x) ≈₁ x
  where open Setoid A renaming (_≈_ to _≈₁_)

record Injection (From To : Setoid) : Set where
  field
    to        : From ⟶ To
    injective : Injective to

record LeftInverse (From To : Setoid) : Set where
  field
    to           : From ⟶ To
    from         : To ⟶ From
    left-inverse : from LeftInverseOf to

  open Setoid      From
  open EqReasoning From

  injective : Injective to
  injective {x} {y} eq = begin
    x                    ≈⟨ sym (left-inverse x) ⟩
    from ⟨$⟩ (to ⟨$⟩ x)  ≈⟨ pres from eq ⟩
    from ⟨$⟩ (to ⟨$⟩ y)  ≈⟨ left-inverse y ⟩
    y                    ∎

  injection : Injection From To
  injection = record { to = to; injective = injective }


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

module PiG where

import Level as L
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Data.Nat
open import Function
open import Relation.Binary.PropositionalEquality
open import Relation.Binary

------------------------------------------------------------------------------
-- Reasoning about paths

pathInd : ∀ {u u'} → {A : Set u} →
  (C : (x y : A) → (x ≡ y) → Set u') →
  (c : (x : A) → C x x refl) →
  ((x y : A) (p : x ≡ y) → C x y p)
pathInd C c x .x refl = c x

trans' : {A : Set} → {x y z : A} → (x ≡ y) → (y ≡ z) → (x ≡ z)
trans' {A} {x} {y} {z} p q =
  pathInd
    (λ x y p → ((z : A) → (q : y ≡ z) → (x ≡ z)))
    (pathInd (λ x z q → x ≡ z) (λ _ → refl))
    x y p z q

ap : {A B : Set} {x y : A} → (f : A → B) → (x ≡ y) → (f x ≡ f y)
ap {A} {B} {x} {y} f p =
  pathInd
    (λ x y p → f x ≡ f y)
    (λ x → refl)
    x y p

ap× : {A B : Set} {x y : A} {z w : B} → (x ≡ y) → (z ≡ w) →
      ((x , z) ≡ (y , w))
ap× {A} {B} {x} {y} {z} {w} p₁ p₂ =
  pathInd
    (λ x y p₁ → ((z : B) (w : B) (p₂ : z ≡ w) → ((x , z) ≡ (y , w))))
    (λ x → pathInd
             (λ z w p₂ → ((x , z) ≡ (x , w)))
             (λ z → refl))
    x y p₁ z w p₂

transport : {A : Set} {x y : A} {P : A → Set} → (p : x ≡ y) → P x → P y
transport {A} {x} {y} {P} p =
  pathInd
    (λ x y p → (P x → P y))
    (λ x → id)
    x y p

------------------------------------------------------------------------------
-- Codes for our types

data B : Set where
  -- no zero because we map types to POINTED SETS
  ONE   : B
  PLUS  : B → B → B
  TIMES : B → B → B
  HALF  : B -- used to explore fractionals

{--

Now let's define groups/groupoids

The type 1/2 is modeled by the group { refl , loop }
where loop is self-inverse, i.e., loop . loop = refl

We always get refl, inverses, and associativity for free. We need to
explicitly add the new element 'loop' and the new equation
'loop . loop = refl'

--}

cut-path : {A : Set} → {x : A} → (f g : ( x ≡ x) → (x ≡ x)) → x ≡ x → Set
cut-path f g a = f a ≡ g a

record GroupoidVal : Set₁ where
  constructor •[_,_,_,_,_,_]
  field
    ∣_∣       : Set
    •        : ∣_∣
    path     : • ≡ •        -- the additional path (loop)
    -- truncate : trans' path path ≡ refl  -- the equivalence used to truncate
    -- truncate : (f : • ≡ • → • ≡ •) → (b : • ≡ •) → f b ≡ refl
    path-rel₁ : • ≡ • → • ≡ •
    path-rel₂ : • ≡ • → • ≡ •
    truncate : cut-path path-rel₁ path-rel₂ path

open GroupoidVal public

-- Example:

const-loop : {A : Set} {x : A} → (y : x ≡ x) → x ≡ x
const-loop _ = refl

postulate iloop     : tt ≡ tt
          itruncate : (f : tt ≡ tt → tt ≡ tt) → cut-path f const-loop iloop
              -- (trans' iloop iloop) ≡ refl

2loop : {A : Set} → {x : A} → x ≡ x → x ≡ x
2loop x = trans' x x

half : GroupoidVal
half = •[ ⊤ , tt , iloop , 2loop , const-loop , itruncate 2loop ]

-- Interpretations of types

⟦_⟧ : B → GroupoidVal
⟦ ONE ⟧ = •[ ⊤ , tt , refl , id , id , refl ]
⟦ PLUS b₁ b₂ ⟧ with ⟦ b₁ ⟧ | ⟦ b₂ ⟧
... | •[ B₁ , x₁ , p₁ , f₁ , g₁ , t₁ ] | •[ B₂ , x₂ , p₂ , f₂ , g₂ , t₂ ] =
  •[ B₁ ⊎ B₂ , inj₁ x₁ , ap inj₁ p₁ , id , id ,  refl ]
⟦ TIMES b₁ b₂ ⟧ with ⟦ b₁ ⟧ | ⟦ b₂ ⟧
... | •[ B₁ , x₁ , p₁ , f₁ , g₁ , t₁ ] | •[ B₂ , x₂ , p₂ , f₂ , g₂ , t₂ ] =
  •[ B₁ × B₂ , (x₁ , x₂) , ap× p₁ p₂ , id , id , refl ]
⟦ HALF ⟧ = half

-- Combinators

data _⟷_ : B → B → Set₁ where
  swap× : {b₁ b₂ : B} → TIMES b₁ b₂ ⟷ TIMES b₂ b₁

record GFunctor (G₁ G₂ : GroupoidVal) : Set where
  field
    fun : ∣ G₁ ∣ → ∣ G₂ ∣
    baseP : fun (• G₁) ≡ (• G₂)
    -- dependent paths??
    isoP : {x y : ∣ G₁ ∣} (p : x ≡ y) → (fun x ≡ fun y)

eval : {b₁ b₂ : B} → (b₁ ⟷ b₂) → GFunctor ⟦ b₁ ⟧ ⟦ b₂ ⟧
eval swap× =
  record {
    fun = swap;
    baseP = refl;
    isoP = λ p → ap swap p
  }

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

{-
   Some old scribblings
A-2 : Set
A-2 = ⊤

postulate
  S⁰ : Set
  S¹ : Set

record Trunc-1 (A : Set) : Set where
  field
    embed : A → Trunc-1 A
    hub : (r : S⁰ → Trunc-1 A) → Trunc-1 A
    spoke : (r : S⁰ → Trunc-1 A) → (x : S⁰) → r x ≡ hub r

-}

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

record 2Groupoid : Set₁ where
  constructor ²[_,_,_]
  field
    obj      : Set
    hom     : Rel obj L.zero        -- paths (loop)
    2path    : ∀ {a b} → hom a b → hom a b → Set  -- 2paths, i.e. path eqns

open 2Groupoid public

hom⊤ : Rel ⊤ L.zero
hom⊤ = _≡_

2path⊤ : Rel (hom⊤ tt tt) L.zero
2path⊤ = _≡_

half' : 2Groupoid
half' = ²[ ⊤ , hom⊤ , 2path⊤ ]


module Prelude.Bool where

open import Prelude.Unit
open import Prelude.Empty
open import Prelude.Equality
open import Prelude.Decidable
open import Prelude.Function

open import Agda.Builtin.Bool public

infix 0 if_then_else_
if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A
if true  then x else y = x
if false then x else y = y
{-# INLINE if_then_else_ #-}

infixr 3 _&&_
infixr 2 _||_

_||_ : Bool → Bool → Bool
true  || _ = true
false || x = x
{-# INLINE _||_ #-}

_&&_ : Bool → Bool → Bool
true  && x = x
false && _ = false
{-# INLINE _&&_ #-}

not : Bool → Bool
not true  = false
not false = true
{-# INLINE not #-}

data IsTrue : Bool → Set where
  instance true : IsTrue true

data IsFalse : Bool → Set where
  instance false : IsFalse false

instance
  EqBool : Eq Bool
  _==_ {{EqBool}} false false = yes refl
  _==_ {{EqBool}} false true  = no λ ()
  _==_ {{EqBool}} true  false = no λ ()
  _==_ {{EqBool}} true  true  = yes refl

decBool : ∀ b → Dec (IsTrue b)
decBool false = no λ ()
decBool true  = yes true
{-# INLINE decBool #-}

isYes : ∀ {a} {A : Set a} → Dec A → Bool
isYes (yes _) = true
isYes (no _)  = false

isNo : ∀ {a} {A : Set a} → Dec A → Bool
isNo = not ∘ isYes

infix 0 if′_then_else_
if′_then_else_ : ∀ {a} {A : Set a} (b : Bool) → ({{_ : IsTrue b}} → A) → ({{_ : IsFalse b}} → A) → A
if′ true  then x else _ = x
if′ false then _ else y = y