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

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

module Numbers.BinaryNaturals.Definition where

data Bit : Set where
  zero : Bit
  one : Bit

BinNat : Set
BinNat = List Bit

::Inj : {xs ys : BinNat} {i : Bit} → i :: xs ≡ i :: ys → xs ≡ ys
::Inj {i = zero} refl = refl
::Inj {i = one} refl = refl

nonEmptyNotEmpty : {a : _} {A : Set a} {l1 : List A} {i : A} → i :: l1 ≡ [] → False
nonEmptyNotEmpty {l1 = l1} {i} ()

-- TODO - maybe we should do the floating-point style of assuming there's a leading bit and not storing it.
-- That way, everything is already canonical.

canonical : BinNat → BinNat
canonical [] = []
canonical (zero :: n) with canonical n
canonical (zero :: n) | [] = []
canonical (zero :: n) | x :: bl = zero :: x :: bl
canonical (one :: n) = one :: canonical n

Canonicalised : Set
Canonicalised = Sg BinNat (λ i → canonical i ≡ i)

binNatToN : BinNat → ℕ
binNatToN [] = 0
binNatToN (zero :: b) = 2 *N binNatToN b
binNatToN (one :: b) = 1 +N (2 *N binNatToN b)

incr : BinNat → BinNat
incr [] = one :: []
incr (zero :: n) = one :: n
incr (one :: n) = zero :: (incr n)

incrNonzero : (x : BinNat) → canonical (incr x) ≡ [] → False

incrPreservesCanonical : (x : BinNat) → (canonical x ≡ x) → canonical (incr x) ≡ incr x
incrPreservesCanonical [] pr = refl
incrPreservesCanonical (zero :: xs) pr with canonical xs
incrPreservesCanonical (zero :: xs) pr | x :: t = applyEquality (one ::_) (::Inj pr)
incrPreservesCanonical (one :: xs) pr with inspect (canonical (incr xs))
incrPreservesCanonical (one :: xs) pr | [] with≡ x = exFalso (incrNonzero xs x)
incrPreservesCanonical (one :: xs) pr | (x₁ :: y) with≡ x rewrite x = applyEquality (zero ::_) (transitivity (equalityCommutative x) (incrPreservesCanonical xs (::Inj pr)))

incrPreservesCanonical' : (x : BinNat) → canonical (incr x) ≡ incr (canonical x)

incrC : Canonicalised → Canonicalised
incrC (a , b) = incr a , incrPreservesCanonical a b

NToBinNat : ℕ → BinNat
NToBinNat zero = []
NToBinNat (succ n) with NToBinNat n
NToBinNat (succ n) | t = incr t

NToBinNatC : ℕ → Canonicalised
NToBinNatC zero = [] , refl
NToBinNatC (succ n) = incrC (NToBinNatC n)

incrInj : {x y : BinNat} → incr x ≡ incr y → canonical x ≡ canonical y

incrNonzero' : (x : BinNat) → (incr x) ≡ [] → False
incrNonzero' (zero :: xs) ()
incrNonzero' (one :: xs) ()

canonicalRespectsIncr' : {x y : BinNat} → canonical (incr x) ≡ canonical (incr y) → canonical x ≡ canonical y

binNatToNSucc : (n : BinNat) → binNatToN (incr n) ≡ succ (binNatToN n)
NToBinNatSucc : (n : ℕ) → incr (NToBinNat n) ≡ NToBinNat (succ n)

binNatToNZero : (x : BinNat) → binNatToN x ≡ 0 → canonical x ≡ []
binNatToNZero' : (x : BinNat) → canonical x ≡ [] → binNatToN x ≡ 0

NToBinNatZero : (n : ℕ) → NToBinNat n ≡ [] → n ≡ 0
NToBinNatZero zero pr = refl
NToBinNatZero (succ n) pr with NToBinNat n
NToBinNatZero (succ n) pr | zero :: bl = exFalso (nonEmptyNotEmpty pr)
NToBinNatZero (succ n) pr | one :: bl = exFalso (nonEmptyNotEmpty pr)

canonicalAscends : {i : Bit} → (a : BinNat) → 0 <N binNatToN a → i :: canonical a ≡ canonical (i :: a)

canonicalAscends' : {i : Bit} → (a : BinNat) → (canonical a ≡ [] → False) → i :: canonical a ≡ canonical (i :: a)
canonicalAscends' {i} a pr = canonicalAscends {i} a (t a pr)
  where
    t : (a : BinNat) → (canonical a ≡ [] → False) → 0 <N binNatToN a
    t a pr with TotalOrder.totality ℕTotalOrder 0 (binNatToN a)
    t a pr | inl (inl x) = x
    t a pr | inr x = exFalso (pr (binNatToNZero a (equalityCommutative x)))

canonicalIdempotent : (a : BinNat) → canonical a ≡ canonical (canonical a)
canonicalIdempotent [] = refl
canonicalIdempotent (zero :: a) with inspect (canonical a)
canonicalIdempotent (zero :: a) | [] with≡ y rewrite y = refl
canonicalIdempotent (zero :: a) | (x :: bl) with≡ y = transitivity (equalityCommutative (canonicalAscends' {zero} a λ p → contr p y)) (transitivity (applyEquality (zero ::_) (canonicalIdempotent a)) (equalityCommutative v))
  where
    contr : {a : _} {A : Set a} {l1 l2 : List A} → {x : A} → l1 ≡ [] → l1 ≡ x :: l2 → False
    contr {l1 = []} p1 ()
    contr {l1 = x :: l1} () p2
    u : canonical (canonical (zero :: a)) ≡ canonical (zero :: canonical a)
    u = applyEquality canonical (equalityCommutative (canonicalAscends' {zero} a λ p → contr p y))
    v : canonical (canonical (zero :: a)) ≡ zero :: canonical (canonical a)
    v = transitivity u (equalityCommutative (canonicalAscends' {zero} (canonical a) λ p → contr (transitivity (canonicalIdempotent a) p) y))
canonicalIdempotent (one :: a) rewrite equalityCommutative (canonicalIdempotent a) = refl

canonicalAscends'' : {i : Bit} → (a : BinNat) → canonical (i :: canonical a) ≡ canonical (i :: a)

binNatToNInj : (x y : BinNat) → binNatToN x ≡ binNatToN y → canonical x ≡ canonical y

NToBinNatInj : (x y : ℕ) → canonical (NToBinNat x) ≡ canonical (NToBinNat y) → x ≡ y

NToBinNatIsCanonical : (x : ℕ) → NToBinNat x ≡ canonical (NToBinNat x)
NToBinNatIsCanonical zero = refl
NToBinNatIsCanonical (succ x) with NToBinNatC x
NToBinNatIsCanonical (succ x) | a , b = equalityCommutative (incrPreservesCanonical (NToBinNat x) (equalityCommutative (NToBinNatIsCanonical x)))

contr' : {a : _} {A : Set a} {l1 l2 : List A} → {x : A} → l1 ≡ [] → l1 ≡ x :: l2 → False
contr' {l1 = []} p1 ()
contr' {l1 = x :: l1} () p2

binNatToNIsCanonical : (x : BinNat) → binNatToN (canonical x) ≡ binNatToN x
binNatToNIsCanonical [] = refl
binNatToNIsCanonical (zero :: x) with inspect (canonical x)
binNatToNIsCanonical (zero :: x) | [] with≡ t rewrite t | binNatToNZero' x t = refl
binNatToNIsCanonical (zero :: x) | (x₁ :: bl) with≡ t rewrite (equalityCommutative (canonicalAscends' {zero} x λ p → contr' p t)) | binNatToNIsCanonical x = refl
binNatToNIsCanonical (one :: x) rewrite binNatToNIsCanonical x = refl

-- The following two theorems demonstrate that Canonicalised is isomorphic to ℕ

nToN : (x : ℕ) → binNatToN (NToBinNat x) ≡ x

binToBin : (x : BinNat) → NToBinNat (binNatToN x) ≡ canonical x
binToBin x = transitivity (NToBinNatIsCanonical (binNatToN x)) (binNatToNInj (NToBinNat (binNatToN x)) x (nToN (binNatToN x)))

doubleIsBitShift' : (a : ℕ) → NToBinNat (2 *N succ a) ≡ zero :: NToBinNat (succ a)
doubleIsBitShift' zero = refl
doubleIsBitShift' (succ a) with doubleIsBitShift' a
... | bl rewrite Semiring.commutative ℕSemiring a (succ (succ (a +N 0))) | Semiring.commutative ℕSemiring (succ (a +N 0)) a | Semiring.commutative ℕSemiring a (succ (a +N 0)) | Semiring.commutative ℕSemiring (a +N 0) a | bl = refl

doubleIsBitShift : (a : ℕ) → (0 <N a) → NToBinNat (2 *N a) ≡ zero :: NToBinNat a
doubleIsBitShift zero ()
doubleIsBitShift (succ a) _ = doubleIsBitShift' a

canonicalDescends : {a : Bit} (as : BinNat) → (prA : a :: as ≡ canonical (a :: as)) → as ≡ canonical as
canonicalDescends {zero} as pr with canonical as
canonicalDescends {zero} as pr | x :: bl = ::Inj pr
canonicalDescends {one} as pr = ::Inj pr

--- Proofs

parity : (a b : ℕ) → succ (2 *N a) ≡ 2 *N b → False
doubleInj : (a b : ℕ) → (2 *N a) ≡ (2 *N b) → a ≡ b

incrNonzeroTwice : (x : BinNat) → canonical (incr (incr x)) ≡ one :: [] → False
incrNonzeroTwice (zero :: xs) pr with canonical (incr xs)
incrNonzeroTwice (zero :: xs) () | []
incrNonzeroTwice (zero :: xs) () | x :: bl
incrNonzeroTwice (one :: xs) pr = exFalso (incrNonzero xs (::Inj pr))

canonicalRespectsIncr' {[]} {[]} pr = refl
canonicalRespectsIncr' {[]} {zero :: y} pr with canonical y
canonicalRespectsIncr' {[]} {zero :: y} pr | [] = refl
canonicalRespectsIncr' {[]} {one :: y} pr with canonical (incr y)
canonicalRespectsIncr' {[]} {one :: y} () | []
canonicalRespectsIncr' {[]} {one :: y} () | x :: bl
canonicalRespectsIncr' {zero :: xs} {y} pr with canonical xs
canonicalRespectsIncr' {zero :: xs} {[]} pr | [] = refl
canonicalRespectsIncr' {zero :: xs} {zero :: y} pr | [] with canonical y
canonicalRespectsIncr' {zero :: xs} {zero :: y} pr | [] | [] = refl
canonicalRespectsIncr' {zero :: xs} {one :: y} pr | [] with canonical (incr y)
canonicalRespectsIncr' {zero :: xs} {one :: y} () | [] | []
canonicalRespectsIncr' {zero :: xs} {one :: y} () | [] | x :: bl
canonicalRespectsIncr' {zero :: xs} {zero :: ys} pr | x :: bl with canonical ys
canonicalRespectsIncr' {zero :: xs} {zero :: ys} pr | x :: bl | x₁ :: th = applyEquality (zero ::_) (::Inj pr)
canonicalRespectsIncr' {zero :: xs} {one :: ys} pr | x :: bl with canonical (incr ys)
canonicalRespectsIncr' {zero :: xs} {one :: ys} () | x :: bl | []
canonicalRespectsIncr' {zero :: xs} {one :: ys} () | x :: bl | x₁ :: th
canonicalRespectsIncr' {one :: xs} {[]} pr with canonical (incr xs)
canonicalRespectsIncr' {one :: xs} {[]} () | []
canonicalRespectsIncr' {one :: xs} {[]} () | x :: bl
canonicalRespectsIncr' {one :: xs} {zero :: ys} pr with canonical ys
canonicalRespectsIncr' {one :: xs} {zero :: ys} pr | [] with canonical (incr xs)
canonicalRespectsIncr' {one :: xs} {zero :: ys} () | [] | []
canonicalRespectsIncr' {one :: xs} {zero :: ys} () | [] | x :: t
canonicalRespectsIncr' {one :: xs} {zero :: ys} pr | x :: bl with canonical (incr xs)
canonicalRespectsIncr' {one :: xs} {zero :: ys} () | x :: bl | []
canonicalRespectsIncr' {one :: xs} {zero :: ys} () | x :: bl | x₁ :: t
canonicalRespectsIncr' {one :: xs} {one :: ys} pr with inspect (canonical (incr xs))
canonicalRespectsIncr' {one :: xs} {one :: ys} pr | [] with≡ x = exFalso (incrNonzero xs x)
canonicalRespectsIncr' {one :: xs} {one :: ys} pr | (x+1 :: x+1s) with≡ bad rewrite bad = applyEquality (one ::_) ans
  where
    ans : canonical xs ≡ canonical ys
    ans with inspect (canonical (incr ys))
    ans | [] with≡ x = exFalso (incrNonzero ys x)
    ans | (x₁ :: y) with≡ x rewrite x = canonicalRespectsIncr' {xs} {ys} (transitivity bad (transitivity (::Inj pr) (equalityCommutative x)))

binNatToNInj[] : (y : BinNat) → 0 ≡ binNatToN y → [] ≡ canonical y
binNatToNInj[] [] pr = refl
binNatToNInj[] (zero :: y) pr with productZeroImpliesOperandZero {2} {binNatToN y} (equalityCommutative pr)
binNatToNInj[] (zero :: y) pr | inr x with inspect (canonical y)
binNatToNInj[] (zero :: y) pr | inr x | [] with≡ canYPr rewrite canYPr = refl
binNatToNInj[] (zero :: ys) pr | inr x | (y :: canY) with≡ canYPr with binNatToNZero ys x
... | r with canonical ys
binNatToNInj[] (zero :: ys) pr | inr x | (y :: canY) with≡ canYPr | r | [] = refl
binNatToNInj[] (zero :: ys) pr | inr x | (y :: canY) with≡ canYPr | () | x₁ :: t

binNatToNInj [] y pr = binNatToNInj[] y pr
binNatToNInj (zero :: xs) [] pr = equalityCommutative (binNatToNInj[] (zero :: xs) (equalityCommutative pr))
binNatToNInj (zero :: xs) (zero :: ys) pr with doubleInj (binNatToN xs) (binNatToN ys) pr
... | x=y with binNatToNInj xs ys x=y
... | t with canonical xs
binNatToNInj (zero :: xs) (zero :: ys) pr | x=y | t | [] with canonical ys
binNatToNInj (zero :: xs) (zero :: ys) pr | x=y | t | [] | [] = refl
binNatToNInj (zero :: xs) (zero :: ys) pr | x=y | t | x :: cxs with canonical ys
binNatToNInj (zero :: xs) (zero :: ys) pr | x=y | t | x :: cxs | x₁ :: cys = applyEquality (zero ::_) t
binNatToNInj (zero :: xs) (one :: ys) pr = exFalso (parity (binNatToN ys) (binNatToN xs) (equalityCommutative pr))
binNatToNInj (one :: xs) (zero :: ys) pr = exFalso (parity (binNatToN xs) (binNatToN ys) pr)
binNatToNInj (one :: xs) (one :: ys) pr = applyEquality (one ::_) (binNatToNInj xs ys (doubleInj (binNatToN xs) (binNatToN ys) (succInjective pr)))

NToBinNatInj zero zero pr = refl
NToBinNatInj zero (succ y) pr with NToBinNat y
NToBinNatInj zero (succ y) pr | bl = exFalso (incrNonzero bl (equalityCommutative pr))
NToBinNatInj (succ x) zero pr with NToBinNat x
... | bl = exFalso (incrNonzero bl pr)
NToBinNatInj (succ x) (succ y) pr with inspect (NToBinNat x)
NToBinNatInj (succ zero) (succ zero) pr | [] with≡ nToBinXPr = refl
NToBinNatInj (succ zero) (succ (succ y)) pr | [] with≡ nToBinXPr with NToBinNat y
NToBinNatInj (succ zero) (succ (succ y)) pr | [] with≡ nToBinXPr | y' = exFalso (incrNonzeroTwice y' (equalityCommutative pr))
NToBinNatInj (succ (succ x)) (succ y) pr | [] with≡ nToBinXPr with NToBinNat x
NToBinNatInj (succ (succ x)) (succ y) pr | [] with≡ nToBinXPr | t = exFalso (incrNonzero' t nToBinXPr)
NToBinNatInj (succ x) (succ y) pr | (x₁ :: nToBinX) with≡ nToBinXPr with NToBinNatInj x y (canonicalRespectsIncr' {NToBinNat x} {NToBinNat y} pr)
NToBinNatInj (succ x) (succ .x) pr | (x₁ :: nToBinX) with≡ nToBinXPr | refl = refl

incrInj {[]} {[]} pr = refl
incrInj {[]} {zero :: ys} pr rewrite equalityCommutative (::Inj pr) = refl
incrInj {zero :: xs} {[]} pr rewrite ::Inj pr = refl
incrInj {zero :: xs} {zero :: .xs} refl = refl
incrInj {one :: xs} {one :: ys} pr = applyEquality (one ::_) (incrInj {xs} {ys} (l (incr xs) (incr ys) pr))
  where
    l : (a : BinNat) → (b : BinNat) → zero :: a ≡ zero :: b → a ≡ b
    l a .a refl = refl

doubleInj zero zero pr = refl
doubleInj (succ a) (succ b) pr = applyEquality succ (doubleInj a b u)
  where
    t : a +N a ≡ b +N b
    t rewrite Semiring.commutative ℕSemiring (succ a) 0 | Semiring.commutative ℕSemiring (succ b) 0 | Semiring.commutative ℕSemiring a (succ a) | Semiring.commutative ℕSemiring b (succ b) = succInjective (succInjective pr)
    u : a +N (a +N zero) ≡ b +N (b +N zero)
    u rewrite Semiring.commutative ℕSemiring a 0 | Semiring.commutative ℕSemiring b 0 = t

binNatToNZero [] pr = refl
binNatToNZero (zero :: xs) pr with inspect (canonical xs)
binNatToNZero (zero :: xs) pr | [] with≡ x rewrite x = refl
binNatToNZero (zero :: xs) pr | (x₁ :: y) with≡ x with productZeroImpliesOperandZero {2} {binNatToN xs} pr
binNatToNZero (zero :: xs) pr | (x₁ :: y) with≡ x | inr pr' with binNatToNZero xs pr'
... | bl with canonical xs
binNatToNZero (zero :: xs) pr | (x₁ :: y) with≡ x | inr pr' | bl | [] = refl
binNatToNZero (zero :: xs) pr | (x₁ :: y) with≡ x | inr pr' | () | x₂ :: t

binNatToNSucc [] = refl
binNatToNSucc (zero :: n) = refl
binNatToNSucc (one :: n) rewrite Semiring.commutative ℕSemiring (binNatToN n) zero | Semiring.commutative ℕSemiring (binNatToN (incr n)) 0 | binNatToNSucc n = applyEquality succ (Semiring.commutative ℕSemiring (binNatToN n) (succ (binNatToN n)))

incrNonzero (one :: xs) pr with inspect (canonical (incr xs))
incrNonzero (one :: xs) pr | [] with≡ x = incrNonzero xs x
incrNonzero (one :: xs) pr | (y :: ys) with≡ x with canonical (incr xs)
incrNonzero (one :: xs) pr | (y :: ys) with≡ () | []
incrNonzero (one :: xs) () | (.x₁ :: .th) with≡ refl | x₁ :: th

nToN zero = refl
nToN (succ x) with inspect (NToBinNat x)
nToN (succ x) | [] with≡ pr = ans
  where
    t : x ≡ zero
    t = NToBinNatInj x 0 (applyEquality canonical pr)
    ans : binNatToN (incr (NToBinNat x)) ≡ succ x
    ans rewrite t | pr = refl
nToN (succ x) | (bit :: xs) with≡ pr = transitivity (binNatToNSucc (NToBinNat x)) (applyEquality succ (nToN x))

parity zero (succ b) pr rewrite Semiring.commutative ℕSemiring b (succ (b +N 0)) = bad pr
  where
    bad : (1 ≡ succ (succ ((b +N 0) +N b))) → False
    bad ()
parity (succ a) (succ b) pr rewrite Semiring.commutative ℕSemiring b (succ (b +N 0)) | Semiring.commutative ℕSemiring a (succ (a +N 0)) | Semiring.commutative ℕSemiring (a +N 0) a | Semiring.commutative ℕSemiring (b +N 0) b = parity a b (succInjective (succInjective pr))

binNatToNZero' [] pr = refl
binNatToNZero' (zero :: xs) pr with inspect (canonical xs)
binNatToNZero' (zero :: xs) pr | [] with≡ p2 = ans
  where
    t : binNatToN xs ≡ 0
    t = binNatToNZero' xs p2
    ans : 2 *N binNatToN xs ≡ 0
    ans rewrite t = refl
binNatToNZero' (zero :: xs) pr | (y :: ys) with≡ p rewrite p = exFalso (bad pr)
  where
    bad : zero :: y :: ys ≡ [] → False
    bad ()
binNatToNZero' (one :: xs) ()

canonicalAscends {zero} a 0<a with inspect (canonical a)
canonicalAscends {zero} (zero :: a) 0<a | [] with≡ x = exFalso (contr'' (binNatToN a) t v)
  where
    u : binNatToN (zero :: a) ≡ 0
    u = binNatToNZero' (zero :: a) x
    v : binNatToN a ≡ 0
    v with inspect (binNatToN a)
    v | zero with≡ x = x
    v | succ a' with≡ x with inspect (binNatToN (zero :: a))
    v | succ a' with≡ x | zero with≡ pr2 rewrite pr2 = exFalso (TotalOrder.irreflexive ℕTotalOrder 0<a)
    v | succ a' with≡ x | succ y with≡ pr2 rewrite u = exFalso (TotalOrder.irreflexive ℕTotalOrder 0<a)
    t : 0 <N binNatToN a
    t with binNatToN a
    t | succ bl rewrite Semiring.commutative ℕSemiring (succ bl) 0 = succIsPositive bl
    contr'' : (x : ℕ) → (0 <N x) → (x ≡ 0) → False
    contr'' x 0<x x=0 rewrite x=0 = TotalOrder.irreflexive ℕTotalOrder 0<x
canonicalAscends {zero} a 0<a | (x₁ :: y) with≡ x rewrite x = refl
canonicalAscends {one} a 0<a = refl

canonicalAscends'' {i} a with inspect (canonical a)
canonicalAscends'' {zero} a | [] with≡ x rewrite x = refl
canonicalAscends'' {one} a | [] with≡ x rewrite x = refl
canonicalAscends'' {i} a | (x₁ :: y) with≡ x = transitivity (applyEquality canonical (canonicalAscends' {i} a λ p → contr p x)) (equalityCommutative (canonicalIdempotent (i :: a)))
  where
    contr : {a : _} {A : Set a} {l1 l2 : List A} → {x : A} → l1 ≡ [] → l1 ≡ x :: l2 → False
    contr {l1 = []} p1 ()
    contr {l1 = x :: l1} () p2

incrPreservesCanonical' [] = refl
incrPreservesCanonical' (zero :: xs) with inspect (canonical xs)
incrPreservesCanonical' (zero :: xs) | [] with≡ x rewrite x = refl
incrPreservesCanonical' (zero :: xs) | (x₁ :: y) with≡ x rewrite x = refl
incrPreservesCanonical' (one :: xs) with inspect (canonical (incr xs))
... | [] with≡ pr = exFalso (incrNonzero xs pr)
... | (_ :: _) with≡ pr rewrite pr = applyEquality (zero ::_) (transitivity (equalityCommutative pr) (incrPreservesCanonical' xs))

NToBinNatSucc zero = refl
NToBinNatSucc (succ n) with NToBinNat n
... | [] = refl
... | a :: as = refl


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

open import Categories.Category

module Categories.Category.CartesianClosed.Locally {o ℓ e} (C : Category o ℓ e) where

open import Level using (levelOfTerm)

open import Categories.Category.CartesianClosed
open import Categories.Category.Cartesian
open import Categories.Category.Cartesian.Properties C
open import Categories.Category.Complete.Finitely C
open import Categories.Category.Slice C
open import Categories.Category.Slice.Properties C

open import Categories.Object.Product
open import Categories.Object.Exponential
open import Categories.Object.Terminal C
import Categories.Diagram.Pullback as P
import Categories.Diagram.Pullback.Properties C as Pₚ
import Categories.Morphism.Reasoning as MR

open Category C

record Locally : Set (levelOfTerm C) where
  field
    sliceCCC : ∀ A → CartesianClosed (Slice A)

  module sliceCCC A = CartesianClosed (sliceCCC A)

  pullbacks : ∀ {X A B} (f : A ⇒ X) (g : B ⇒ X) → P.Pullback C f g
  pullbacks {X} f g = product⇒pullback product
    where C/X = sliceCCC X
          open CartesianClosed C/X using (product)

  -- the slice categories also have pullbacks, because slice of slice is slice.
  slice-pullbacks : ∀ {A} {B X Y : SliceObj A} (f : Slice⇒ X B) (g : Slice⇒ Y B) → P.Pullback (Slice A) f g
  slice-pullbacks {A} {B} {X} {Y} f g = record
    { P               = sliceobj (X.arr ∘ p.p₁)
    ; p₁              = slicearr refl
    ; p₂              = slicearr comm
    ; commute         = p.commute
    ; universal       = λ {Z} {h i} eq → slicearr {h = p.universal eq} (pullʳ p.p₁∘universal≈h₁ ○ Slice⇒.△ h)
    ; unique          = λ eq₁ eq₂ → p.unique eq₁ eq₂
    ; p₁∘universal≈h₁ = p.p₁∘universal≈h₁
    ; p₂∘universal≈h₂ = p.p₂∘universal≈h₂
    }
    where open HomReasoning
          module X = SliceObj X
          module Y = SliceObj Y
          module B = SliceObj B
          module f = Slice⇒ f
          module g = Slice⇒ g
          module p = P.Pullback (pullbacks f.h g.h)
          open MR C

          comm : Y.arr ∘ p.p₂ ≈ X.arr ∘ p.p₁
          comm = begin
            Y.arr ∘ p.p₂         ≈˘⟨ g.△ ⟩∘⟨refl ⟩
            (B.arr ∘ g.h) ∘ p.p₂ ≈˘⟨ pushʳ p.commute ⟩
            B.arr ∘ f.h ∘ p.p₁   ≈⟨ pullˡ f.△ ⟩
            X.arr ∘ p.p₁         ∎

module _ (LCCC : Locally) (t : Terminal) where
  open Locally LCCC
  open Terminal t
  open HomReasoning

  cartesian : Cartesian C
  cartesian = record
    { terminal = t
    ; products = record
      { product = λ {A B} → Pₚ.pullback-⊤⇒product t (product⇒pullback product)
      }
    }
    where open sliceCCC ⊤ using (product)

  private
    module cartesian = Cartesian cartesian

  cartesianClosed : CartesianClosed C
  cartesianClosed = record
    { cartesian = cartesian
    ; exp       = λ {A B} →
      let open Exponential (exp {sliceobj (! {A})} {sliceobj (! {B})})
      in record
      { B^A      = Y B^A
      ; product  =
        -- this is tricky. how product is implemented requires special care. for example, the following
        -- code also gives a product that type checks, but it is impossible to work with.
        -- Pₚ.pullback-⊤⇒product t (Pₚ.pullback-resp-≈ (product⇒pullback product) !-unique₂ refl)
        --
        -- another quirk of proof relevance.
        let open Product (Slice ⊤) (exp.product {sliceobj (! {A})} {sliceobj (! {B})})
        in record
           { A×B      = Y A×B
           ; π₁       = h π₁
           ; π₂       = h π₂
           ; ⟨_,_⟩    = λ f g → h ⟨ slicearr {h = f} (⟺ (!-unique _)) , slicearr {h = g} (⟺ (!-unique _)) ⟩
           ; project₁ = project₁
           ; project₂ = project₂
           ; unique   = λ eq eq′ → unique {h = slicearr (⟺ (!-unique _))} eq eq′
           }
      ; eval     = h eval
      ; λg       = λ {X} p f → h (λg (pullback⇒product′ t (Pₚ.product⇒pullback-⊤ t p)) (lift t f))
      ; β        = λ p → ∘-resp-≈ʳ (exp.product.⟨⟩-cong₂ refl refl) ○ β (pullback⇒product′ t (Pₚ.product⇒pullback-⊤ t p))
      ; λ-unique = λ p eq → λ-unique (pullback⇒product′ t (Pₚ.product⇒pullback-⊤ t p))
                                     {h = slicearr (⟺ (!-unique _))}
                                     (∘-resp-≈ʳ (exp.product.⟨⟩-cong₂ refl refl) ○ eq)
      }
    }
    where open sliceCCC ⊤ using (exp)
          open SliceObj
          open Slice⇒

  finitelyComplete : FinitelyComplete
  finitelyComplete = record
    { cartesian = cartesian
    ; equalizer = λ f g → prods×pullbacks⇒equalizers cartesian.products
                                                     (λ {_ _ X} h i → product⇒pullback (sliceCCC.product X))
    }


open import Prelude

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

  -- list append, structural on the left
  _++_ : {A : Set} → List A → List A → List A
  [] ++ l₂ = l₂
  (h :: l₁) ++ l₂ = h :: (l₁ ++ l₂)

  -- list membership, as a proposition
  data _inList_ {A : Set} : A → List A → Set where
    InH : {a : A} {as : List A} → a inList (a :: as)
    InT : {a a' : A} {as : List A} → a inList as → a inList (a' :: as)

  -- if everything isn't in a list, it's the empty list
  ∅∈l→l==[] : {A : Set} {l : List A} → ((a : A) → a inList l → ⊥) → l == []
  ∅∈l→l==[] {l = []} h = refl
  ∅∈l→l==[] {l = a' :: as} h = abort (h a' InH)

  -- if a isn't in a cons, it's not in the tail either
  a∉a'::as→a∉as : {A : Set} {as : List A} {a a' : A} →
                  (a inList (a' :: as) → ⊥) →
                   a inList as →
                   ⊥
  a∉a'::as→a∉as h h' = h (InT h')

  -- if x isn't in either side of an append, it's not in the result
  not-in-append-comm : {A : Set} {x : A} {l₁ l₂ : List A} →
                       dec A →
                       (x inList l₁ → ⊥) →
                       (x inList l₂ → ⊥) →
                       x inList (l₁ ++ l₂) → ⊥
  not-in-append-comm {l₁ = []} dec h₁ h₂ h₃ = h₂ h₃
  not-in-append-comm {x = x} {a₁ :: _} dec h₁ h₂ h₃   with  dec a₁ x
  not-in-append-comm {l₁ = l₁} dec h₁ h₂ h₃          | Inl h = h₁ (tr (λ y → y inList l₁) h InH)
  not-in-append-comm dec h₁ h₂ InH                   | Inr h = abort (h refl)
  not-in-append-comm dec h₁ h₂ (InT h₃)              | Inr h = not-in-append-comm dec (a∉a'::as→a∉as h₁) h₂ h₃

  remove-all : {A : Set} → dec A → List A → A → List A
  remove-all dec [] a = []
  remove-all dec (a' :: as) a
    with dec a a' | remove-all dec as a
  ...  | Inl _      | l'                    = l'
  ...  | Inr _      | l'                    = a' :: l'

  -- (homomorphism) removing distributes over append
  remove-all-append-comm : {A : Set} →
                           (dec : dec A) →
                           (l₁ l₂ : List A) →
                           (a : A) →
                           remove-all dec l₁ a ++ remove-all dec l₂ a == remove-all dec (l₁ ++ l₂) a
  remove-all-append-comm dec [] l₂ a = refl
  remove-all-append-comm dec (a₁ :: as₁) l₂ a with dec a a₁ | remove-all-append-comm dec as₁ l₂ a
  remove-all-append-comm dec (a₁ :: as₁) l₂ a    | Inl refl   | h                                      = h
  remove-all-append-comm dec (a₁ :: as₁) l₂ a    | Inr _      | h                                      = ap1 (λ y → a₁ :: y) h

  -- an element isn't in the list that results from removing all instances of it
  remove-all-not-in : {A : Set} →
                      (dec : dec A) →
                      (l : List A) →
                      (a : A) →
                      a inList remove-all dec l a →
                      ⊥
  remove-all-not-in dec [] a ()
  remove-all-not-in dec (a' :: as) a h with    dec a a'
  remove-all-not-in dec (a' :: as) a h       | Inl refl   = remove-all-not-in dec as a' h
  remove-all-not-in dec (a' :: as) .a' InH   | Inr a≠a'   = a≠a' refl
  remove-all-not-in dec (a' :: as) a (InT h) | Inr a≠a'   = remove-all-not-in dec as a h

  -- an element that was never in a list still isn't once you remove any element.
  a∉l→a∉remove-all-l-a' : {A : Set} →
                            {l : List A} →
                            {a a' : A} →
                            (dec : dec A) →
                            (a inList l → ⊥) →
                            a inList remove-all dec l a' →
                            ⊥
  a∉l→a∉remove-all-l-a' {l = []} {a} _ h₁ h₂ = h₁ h₂
  a∉l→a∉remove-all-l-a' {l = lh :: lt} {a} {a'} dec h₁ h₂   with  dec a lh | dec a' lh
  a∉l→a∉remove-all-l-a' {l = lh :: lt} {a} dec h₁ h₂       | Inl a==lh  | _            = h₁ (tr (λ y → a inList (y :: lt) ) a==lh InH)
  a∉l→a∉remove-all-l-a' {l = lh :: lt} dec h₁ h₂       | Inr _      | Inl _        = a∉l→a∉remove-all-l-a' dec (a∉a'::as→a∉as h₁) h₂
  a∉l→a∉remove-all-l-a' {l = lh :: lt} dec h₁ InH    | Inr a≠lh   | Inr _        = a≠lh refl
  a∉l→a∉remove-all-l-a' {l = lh :: lt} dec h₁ (InT h₂) | Inr _      | Inr _        = a∉l→a∉remove-all-l-a' dec (a∉a'::as→a∉as h₁) h₂

  -- if an element is in an append, it's in one side or the other
  inList++ : {A : Set} {x : A} → ∀{l1 l2} → dec A → x inList (l1 ++ l2) → (x inList l1) + (x inList l2)
  inList++ {l1 = []} dec xin = Inr xin
  inList++ {x = x} {l1 = a :: l1} dec inl with dec x a
  inList++ {x = .a} {a :: l1} dec inl | Inl refl = Inl InH
  inList++ {x = .a} {a :: l1} dec InH | Inr x₁ = abort (x₁ refl)
  inList++ {x = x} {a :: l1} dec (InT inl) | Inr x₁ with inList++ {l1 = l1} dec inl
  inList++ {x = x} {a :: l1} dec (InT inl) | Inr x₁ | Inl x₂ = Inl (InT x₂)
  inList++ {x = x} {a :: l1} dec (InT inl) | Inr x₁ | Inr x₂ = Inr x₂


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

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

module Data.Vec.Recursive.Properties where

open import Level using (Level)
open import Data.Nat.Base hiding (_^_)
open import Data.Product
open import Data.Vec.Recursive
open import Data.Vec.Base using (Vec; _∷_)
open import Function.Inverse using (_↔_; inverse)
open import Relation.Binary.PropositionalEquality as P
open ≡-Reasoning

private
  variable
    a : Level
    A : Set a

------------------------------------------------------------------------
-- Basic proofs

cons-head-tail-identity : ∀ n (as : A ^ suc n) → cons n (head n as) (tail n as) ≡ as
cons-head-tail-identity 0       as = P.refl
cons-head-tail-identity (suc n) as = P.refl

head-cons-identity : ∀ n a (as : A ^ n) → head n (cons n a as) ≡ a
head-cons-identity 0       a as = P.refl
head-cons-identity (suc n) a as = P.refl

tail-cons-identity : ∀ n a (as : A ^ n) → tail n (cons n a as) ≡ as
tail-cons-identity 0       a as = P.refl
tail-cons-identity (suc n) a as = P.refl

append-cons-commute : ∀ m n a (xs : A ^ m) ys →
  append (suc m) n (cons m a xs) ys ≡ cons (m + n) a (append m n xs ys)
append-cons-commute 0       n a xs ys = P.refl
append-cons-commute (suc m) n a xs ys = P.refl

append-splitAt-identity : ∀ m n (as : A ^ (m + n)) → uncurry (append m n) (splitAt m n as) ≡ as
append-splitAt-identity 0       n as = P.refl
append-splitAt-identity (suc m) n as = begin
  let x         = head (m + n) as in
  let (xs , ys) = splitAt m n (tail (m + n) as) in
  append (suc m) n (cons m (head (m + n) as) xs) ys
    ≡⟨ append-cons-commute m n x xs ys ⟩
  cons (m + n) x (append m n xs ys)
    ≡⟨ P.cong (cons (m + n) x) (append-splitAt-identity m n (tail (m + n) as)) ⟩
  cons (m + n) x (tail (m + n) as)
    ≡⟨ cons-head-tail-identity (m + n) as ⟩
  as
    ∎

------------------------------------------------------------------------
-- Conversion to and from Vec

fromVec∘toVec : ∀ n (xs : A ^ n) → fromVec (toVec n xs) ≡ xs
fromVec∘toVec 0       _  = P.refl
fromVec∘toVec (suc n) xs = begin
  cons n (head n xs) (fromVec (toVec n (tail n xs)))
    ≡⟨ P.cong (cons n (head n xs)) (fromVec∘toVec n (tail n xs)) ⟩
  cons n (head n xs) (tail n xs)
    ≡⟨ cons-head-tail-identity n xs ⟩
  xs ∎

toVec∘fromVec : ∀ {n} (xs : Vec A n) → toVec n (fromVec xs) ≡ xs
toVec∘fromVec             Vec.[]       = P.refl
toVec∘fromVec {n = suc n} (x Vec.∷ xs) = begin
  head n (cons n x (fromVec xs)) Vec.∷ toVec n (tail n (cons n x (fromVec xs)))
    ≡⟨ P.cong₂ (λ x xs → x Vec.∷ toVec n xs) hd-prf tl-prf ⟩
  x Vec.∷ toVec n (fromVec xs)
    ≡⟨ P.cong (x Vec.∷_) (toVec∘fromVec xs) ⟩
  x Vec.∷ xs
    ∎ where

  hd-prf = head-cons-identity _ x (fromVec xs)
  tl-prf = tail-cons-identity _ x (fromVec xs)

↔Vec : ∀ n → A ^ n ↔ Vec A n
↔Vec n = inverse (toVec n) fromVec (fromVec∘toVec n) toVec∘fromVec


module Structure.Operator.Group.Proofs where

open import Functional hiding (id)
open import Function.Iteration.Order
import      Lvl
open import Lang.Instance
open import Logic.Propositional
open import Logic.Predicate
open import Structure.Setoid
open import Structure.Function.Domain
open import Structure.Operator.Group
open import Structure.Operator.Monoid
open import Structure.Operator.Properties
open import Structure.Operator.Proofs
open import Structure.Relator.Properties
open import Syntax.Transitivity
open import Type

{-
module _ {ℓ₁ ℓ₂} {X : Type{ℓ₁}} ⦃ _ : Equiv(X) ⦄ {_▫X_ : X → X → X} ⦃ structureₗ : Group(_▫X_) ⦄ {Y : Type{ℓ₂}} ⦃ _ : Equiv(Y) ⦄ {_▫Y_ : Y → Y → Y} ⦃ structureᵣ : Group(_▫Y_) ⦄ (f : X → Y) where
  monomorphic-cyclic : ⦃ (_▫X_) ↣ (_▫Y_) ⦄ → Cyclic(_▫X_) → Cyclic(_▫Y_)
  monomorphic-cyclic ⦃ [∃]-intro θ ⦃ θ-proof ⦄ ⦄ ([∃]-intro index ⦃ intro a ⦄) = {!!}
-}

{-
module _ {T : Type{ℓ₂}} {_▫_ : T → T → T} ⦃ group : Group(_▫_) ⦄ where
  open Group  {T} ⦃ [≡]-equiv ⦄ {_▫_} (group)
  open Monoid {T} ⦃ [≡]-equiv ⦄ {_▫_} (monoid)

  commutationₗ : ∀{x y} → (x ▫ y ≡ y ▫ x) ← ((x ▫ y) ▫ inv (x) ≡ y)
  commutationₗ {x}{y} (comm) =
    symmetry(
      (congruence₁(_▫ x)
        (symmetry comm)
      )
      🝖 (associativity)
      🝖 (congruence₁((x ▫ y) ▫_)) (inverseₗ)
      🝖 (identityᵣ)
    )
  -- (x▫y)▫inv(x) = y //comm
  -- y = (x▫y)▫inv(x) //[≡]-symmetry
  -- y▫x
  -- = ((x▫y)▫inv(x))▫x //congruence₁(expr ↦ expr ▫ x) (..)
  -- = (x▫y)▫(inv(x)▫x) //Group.associativity
  -- = (x▫y)▫id //congruence₁(_) Group.inverseₗ
  -- = x▫y //Group.identityᵣ
  -- x▫y = y▫x //[≡]-symmetry

  commutationᵣ : ∀{x y} → (x ▫ y ≡ y ▫ x) → ((x ▫ y) ▫ inv(x) ≡ y)
  commutationᵣ {x}{y} (comm) =
    (congruence₁(_▫ inv(x)) comm)
    🝖 (associativity)
    🝖 (congruence₁(y ▫_) (inverseᵣ))
    🝖 (identityᵣ)
  -- x▫y = y▫x //comm
  -- (x▫y)▫inv(x)
  -- = (y▫x)▫inv(x) //congruence₁(expr ↦ expr ▫ inv(x)) (..)
  -- = y▫(x▫inv(x)) //Group.associativity
  -- = y▫id //congruence₁(expr ↦ y ▫ expr) Group.inverseᵣ
  -- = y //Group.identityᵣ

module _ {T : Type} {_▫_ : T → T → T} ⦃ commGroup : CommutativeGroup(_▫_) ⦄ where
  open CommutativeGroup {T} ⦃ [≡]-equiv ⦄ {_▫_} (commGroup)
  open Group            {T} ⦃ [≡]-equiv ⦄ {_▫_} (group)
  open Monoid           {T} ⦃ [≡]-equiv ⦄ {_▫_} (monoid)

  commutation : ∀{x y} → ((x ▫ y) ▫ inv(x) ≡ y)
  commutation = commutationᵣ(commutativity)

module _ {T : Type} {_▫_ : T → T → T} ⦃ associativity : Associativity(_▫_) ⦄ where

-}


-- Reported by Nils Anders Danielsson in Issue #3960.

open import Agda.Builtin.Unit
open import Agda.Primitive

id : ∀ {a} (A : Set a) → A → A
id _ a = a

apply : ∀ {a b} {A : Set a} {B : Set b} → A → (A → B) → B
apply = λ x f → f x

postulate
  P : ∀ {a} {A : Set a} → A → A → Set a

Q : ∀ {ℓ} → Set ℓ → Set ℓ
Q A = {x y : A} → P x y

postulate
  F : ∀ {a b} → Set a → Set b → Set (a ⊔ b)
  f : ∀ {a b} {A : Set a} {B : Set b} → Q A → Q (F A B)

g : ∀ {a} (A : Set a) → Q A → ⊤
g A q = apply (λ {_ _} → f q) (id (Q (F A A) → ⊤) _)

-- The previous error message was:
--
-- Cannot instantiate the metavariable _47 to solution
-- piSort _49 (λ z → Set (a ⊔ _b_54 (A = A) (q = q)))
-- since it contains the variable a
-- which is not in scope of the metavariable or irrelevant in the metavariable but relevant in the solution
-- when checking that the expression λ {_} → f q has type
-- {_ = z : _50} → P (_x_59 (A = A) (q = q)) (_y_60 (A = A) (q = q))


{-# OPTIONS --without-K --safe #-}
module Categories.Category.Construction.Presheaves where

-- The Category of Presheaves over a Category C, i.e.
-- the Functor Category [ C.op , Setoids ]
-- Again, the levels are made explicit to show the generality and constraints.

-- CoPreasheaves are defined here as well, for convenience
-- The main reson to name them is that some things (like CoYoneda)
-- look more natural/symmetric then.

open import Level

open import Categories.Category
open import Categories.Category.Construction.Functors
open import Categories.Category.Instance.Setoids using (Setoids)

Presheaves′ : ∀ o′ ℓ′ {o ℓ e : Level} → Category o ℓ e →
  Category (o ⊔ ℓ ⊔ e ⊔ suc (o′ ⊔ ℓ′)) (o ⊔ ℓ ⊔ o′ ⊔ ℓ′) (o ⊔ o′ ⊔ ℓ′)
Presheaves′ o′ ℓ′ C = Functors (Category.op C) (Setoids o′ ℓ′)

Presheaves : ∀ {o ℓ e o′ ℓ′ : Level} → Category o ℓ e →
  Category (o ⊔ ℓ ⊔ e ⊔ suc (o′ ⊔ ℓ′)) (o ⊔ ℓ ⊔ o′ ⊔ ℓ′) (o ⊔ o′ ⊔ ℓ′)
Presheaves {o} {ℓ} {e} {o′} {ℓ′} C = Presheaves′ o′ ℓ′ C

CoPresheaves′ : ∀ o′ ℓ′ {o ℓ e : Level} → Category o ℓ e →
  Category (o ⊔ ℓ ⊔ e ⊔ suc (o′ ⊔ ℓ′)) (o ⊔ ℓ ⊔ o′ ⊔ ℓ′) (o ⊔ o′ ⊔ ℓ′)
CoPresheaves′ o′ ℓ′ C = Functors C (Setoids o′ ℓ′)

CoPresheaves : ∀ {o ℓ e o′ ℓ′ : Level} → Category o ℓ e →
  Category (o ⊔ ℓ ⊔ e ⊔ suc (o′ ⊔ ℓ′)) (o ⊔ ℓ ⊔ o′ ⊔ ℓ′) (o ⊔ o′ ⊔ ℓ′)
CoPresheaves {o} {ℓ} {e} {o′} {ℓ′} C = CoPresheaves′ o′ ℓ′ C


module PLRTree.Complete.Properties {A : Set} where

open import Data.Empty
open import Data.Sum renaming (_⊎_ to _∨_)
open import PLRTree {A}
open import PLRTree.Complete {A}
open import PLRTree.Equality {A}
open import PLRTree.Equality.Properties {A}

lemma-⋗-≃ : {t t' t'' : PLRTree} → t ⋗ t' → t' ≃ t'' → t ⋗ t''
lemma-⋗-≃ (⋗lf x) ≃lf = ⋗lf x
lemma-⋗-≃ (⋗nd x x' l≃r l'≃r' l⋗l') (≃nd .x' x'' _ l''≃r'' l'≃l'') = ⋗nd x x'' l≃r l''≃r'' (lemma-⋗-≃ l⋗l' l'≃l'')

lemma-≃-⋗ : {t t' t'' : PLRTree} → t ≃ t' → t ⋗ t'' → t' ⋗ t''
lemma-≃-⋗ ≃lf t⋗t'' = t⋗t''
lemma-≃-⋗ (≃nd x x' ≃lf ≃lf ≃lf) (⋗lf .x) = ⋗lf x'
lemma-≃-⋗ (≃nd x _ ≃lf (≃nd _ _ _ _ _) ()) (⋗lf .x)
lemma-≃-⋗ (≃nd x x' l≃r l'≃r' l≃l') (⋗nd .x x'' _ l''≃r'' l⋗l'') = ⋗nd x' x'' l'≃r' l''≃r'' (lemma-≃-⋗ l≃l' l⋗l'')

lemma-⋗* : {t t' t'' : PLRTree} → t ⋗ t' → t ⋗ t'' → t' ≃ t''
lemma-⋗* (⋗lf x) (⋗lf .x) = ≃lf
lemma-⋗* (⋗lf x) (⋗nd .x _ _ _ ())
lemma-⋗* (⋗nd x  _ _ _ ()) (⋗lf .x)
lemma-⋗* (⋗nd x x' l≃r l'≃r' l⋗l') (⋗nd .x x'' _ l''≃r'' l'⋗l'') = ≃nd x' x'' l'≃r' l''≃r'' (lemma-⋗* l⋗l' l'⋗l'')

lemma-*⋗ : {t t' t'' : PLRTree} → t ⋗ t' → t'' ⋗ t' → t ≃ t''
lemma-*⋗ (⋗lf x) (⋗lf y) = ≃nd x y ≃lf ≃lf ≃lf
lemma-*⋗ (⋗nd x x' l≃r l'≃r' l⋗l') (⋗nd x'' .x' l''≃r'' _ l''⋗l') = ≃nd x x'' l≃r l''≃r'' (lemma-*⋗ l⋗l' l''⋗l')

lemma-⋗refl-⊥ : {t : PLRTree} → t ⋗ t → ⊥
lemma-⋗refl-⊥ (⋗nd x .x _ _ t⋗t)
    with lemma-⋗refl-⊥ t⋗t
... | ()

lemma-⋙-⋗ : {t t' t'' : PLRTree} → t ⋙ t' → t ⋗ t'' → t' ⋘ t'' ∨ t' ≃ t''
lemma-⋙-⋗ (⋙p (⋗lf x)) (⋗lf .x) = inj₂ ≃lf
lemma-⋙-⋗ (⋙p (⋗nd x _ _ _ ())) (⋗lf .x)
lemma-⋙-⋗ (⋙p (⋗lf x)) (⋗nd .x _ _ _ ())
lemma-⋙-⋗ (⋙p (⋗nd x x' l≃r l'≃r' l⋗l')) (⋗nd .x x'' _ l''≃r'' l⋗l'') = inj₂ (≃nd x' x'' l'≃r' l''≃r'' (lemma-⋗* l⋗l' l⋗l''))
lemma-⋙-⋗ (⋙l x _ _ _ ()) (⋗lf .x)
lemma-⋙-⋗ (⋙l x x' l≃r l'⋘r' l⋗r') (⋗nd .x x'' _ l''≃r'' l⋗l'') = inj₁ (l⋘ x' x'' l'⋘r' l''≃r'' (lemma-⋗* l⋗r' l⋗l''))
lemma-⋙-⋗ (⋙r x x' ≃lf (⋙p ()) ≃lf) (⋗lf .x)
lemma-⋙-⋗ (⋙r x x' ≃lf (⋙l _ _ _ _ _) ()) (⋗lf .x)
lemma-⋙-⋗ (⋙r x x' ≃lf (⋙r _ _ _ _ _) ()) (⋗lf .x)
lemma-⋙-⋗ (⋙r x x' l≃r l'⋙r' l≃l') (⋗nd .x x'' _ l''≃r'' l⋗l'') = inj₁ (r⋘ x' x'' l'⋙r' l''≃r'' (lemma-≃-⋗ l≃l' l⋗l''))


open import Oscar.Prelude
open import Oscar.Class.IsFunctor
open import Oscar.Class.Reflexivity
open import Oscar.Class.Smap
open import Oscar.Class.Surjection
open import Oscar.Class.Transitivity

module Oscar.Class.Functor where

record Functor 𝔬₁ 𝔯₁ ℓ₁ 𝔬₂ 𝔯₂ ℓ₂ : Ø ↑̂ (𝔬₁ ∙̂ 𝔯₁ ∙̂ ℓ₁ ∙̂ 𝔬₂ ∙̂ 𝔯₂ ∙̂ ℓ₂) where
  constructor ∁
  field
    {𝔒₁} : Ø 𝔬₁
    _∼₁_ : 𝔒₁ → 𝔒₁ → Ø 𝔯₁
    _∼̇₁_ : ∀ {x y} → x ∼₁ y → x ∼₁ y → Ø ℓ₁
    ε₁ : Reflexivity.type _∼₁_
    _↦₁_ : Transitivity.type _∼₁_
    {𝔒₂} : Ø 𝔬₂
    _∼₂_ : 𝔒₂ → 𝔒₂ → Ø 𝔯₂
    _∼̇₂_ : ∀ {x y} → x ∼₂ y → x ∼₂ y → Ø ℓ₂
    ε₂ : Reflexivity.type _∼₂_
    _↦₂_ : Transitivity.type _∼₂_
    {μ} : Surjection.type 𝔒₁ 𝔒₂
    functor-smap : Smap.type _∼₁_ _∼₂_ μ μ -- FIXME cannot name this § or smap b/c of namespace conflict
    ⦃ `IsFunctor ⦄ : IsFunctor _∼₁_ _∼̇₁_ ε₁ _↦₁_ _∼₂_ _∼̇₂_ ε₂ _↦₂_ functor-smap


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

module Categories.Category.Finite.Fin.Instance.Triangle where

open import Data.Nat using (ℕ)
open import Data.Fin
open import Data.Fin.Patterns
open import Relation.Binary.PropositionalEquality as ≡

open import Categories.Category.Finite.Fin
open import Categories.Category

private
  variable
    a b c d : Fin 3

-- the diagram is the following:
--
--  0
--  | \
--  |  \
--  |   \
--  |    \
--  v     v
--  1 ---> 2
--
-- all morphisms are 0 (because there is at most one morphism between each pair of objects).
TriangleShape : FinCatShape
TriangleShape = record
  { size     = 3
  ; ∣_⇒_∣    = morph
  ; hasShape = record
    { id        = id
    ; _∘_       = _∘_
    ; assoc     = assoc
    ; identityˡ = identityˡ
    ; identityʳ = identityʳ
    }
  }
  where morph : Fin 3 → Fin 3 → ℕ
        morph 0F 0F = 1
        morph 0F 1F = 1
        morph 0F 2F = 1
        morph 1F 1F = 1
        morph 1F 2F = 1
        morph 2F 2F = 1
        morph _ _   = 0

        id : Fin (morph a a)
        id {0F} = 0F
        id {1F} = 0F
        id {2F} = 0F

        _∘_ : ∀ {a b c} → Fin (morph b c) → Fin (morph a b) → Fin (morph a c)
        _∘_ {0F} {0F} {0F} 0F 0F = 0F
        _∘_ {0F} {0F} {1F} 0F 0F = 0F
        _∘_ {0F} {0F} {2F} 0F 0F = 0F
        _∘_ {0F} {1F} {1F} 0F 0F = 0F
        _∘_ {0F} {1F} {2F} 0F 0F = 0F
        _∘_ {0F} {2F} {2F} 0F 0F = 0F
        _∘_ {1F} {1F} {1F} 0F 0F = 0F
        _∘_ {1F} {1F} {2F} 0F 0F = 0F
        _∘_ {1F} {2F} {2F} 0F 0F = 0F
        _∘_ {2F} {2F} {2F} 0F 0F = 0F

        assoc : ∀ {f : Fin (morph a b)} {g : Fin (morph b c)} {h : Fin (morph c d)} →
                  ((h ∘ g) ∘ f) ≡ (h ∘ (g ∘ f))
        assoc {0F} {0F} {0F} {0F} {0F} {0F} {0F} = refl
        assoc {0F} {0F} {0F} {1F} {0F} {0F} {0F} = refl
        assoc {0F} {0F} {0F} {2F} {0F} {0F} {0F} = refl
        assoc {0F} {0F} {1F} {1F} {0F} {0F} {0F} = refl
        assoc {0F} {0F} {1F} {2F} {0F} {0F} {0F} = refl
        assoc {0F} {0F} {2F} {2F} {0F} {0F} {0F} = refl
        assoc {0F} {1F} {1F} {1F} {0F} {0F} {0F} = refl
        assoc {0F} {1F} {1F} {2F} {0F} {0F} {0F} = refl
        assoc {0F} {1F} {2F} {2F} {0F} {0F} {0F} = refl
        assoc {0F} {2F} {2F} {2F} {0F} {0F} {0F} = refl
        assoc {1F} {1F} {1F} {1F} {0F} {0F} {0F} = refl
        assoc {1F} {1F} {1F} {2F} {0F} {0F} {0F} = refl
        assoc {1F} {1F} {2F} {2F} {0F} {0F} {0F} = refl
        assoc {1F} {2F} {2F} {2F} {0F} {0F} {0F} = refl
        assoc {2F} {2F} {2F} {2F} {0F} {0F} {0F} = refl

        identityˡ : ∀ {f : Fin (morph a b)} → (id ∘ f) ≡ f
        identityˡ {0F} {0F} {0F} = refl
        identityˡ {0F} {1F} {0F} = refl
        identityˡ {0F} {2F} {0F} = refl
        identityˡ {1F} {1F} {0F} = refl
        identityˡ {1F} {2F} {0F} = refl
        identityˡ {2F} {2F} {0F} = refl

        identityʳ : ∀ {f : Fin (morph a b)} → (f ∘ id) ≡ f
        identityʳ {0F} {0F} {0F} = refl
        identityʳ {0F} {1F} {0F} = refl
        identityʳ {0F} {2F} {0F} = refl
        identityʳ {1F} {1F} {0F} = refl
        identityʳ {1F} {2F} {0F} = refl
        identityʳ {2F} {2F} {0F} = refl

Triangle : Category _ _ _
Triangle = FinCategory TriangleShape

module Triangle = Category Triangle


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

module Dodo.Binary.Flip where

-- Stdlib imports
open import Level using (Level; _⊔_)
open import Function.Base using (flip)
open import Relation.Binary using (Rel; REL)
open import Relation.Binary using (Symmetric)
-- Local imports
open import Dodo.Binary.Equality

-- # Operations

-- ## Operations: ⊆₂

module _ {a b ℓ₁ ℓ₂ : Level} {A : Set a} {B : Set b}
    {P : REL A B ℓ₁} {Q : REL A B ℓ₂} where

  flip-⊆₂ : P ⊆₂ Q → flip P ⊆₂ flip Q
  flip-⊆₂ (⊆: P⊆Q) = ⊆: (flip P⊆Q)

module _ {a ℓ₁ : Level} {A : Set a} {P : Rel A ℓ₁} where

  flip-sym-⊆₂ : Symmetric P → P ⊆₂ flip P
  flip-sym-⊆₂ symP = ⊆: (λ _ _ → symP)

-- ## Operations: ⇔₂

module _ {a b ℓ₁ ℓ₂ : Level} {A : Set a} {B : Set b}
    {P : REL A B ℓ₁} {Q : REL A B ℓ₂} where

  flip-⇔₂ : P ⇔₂ Q → flip P ⇔₂ flip Q
  flip-⇔₂ = ⇔₂-compose flip-⊆₂ flip-⊆₂

module _ {a ℓ₁ : Level} {A : Set a} {P : Rel A ℓ₁} where

  flip-sym-⇔₂ : Symmetric P → P ⇔₂ flip P
  flip-sym-⇔₂ symP = ⇔₂-intro (flip-sym-⊆₂ symP) (flip-sym-⊆₂ symP)


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

module Circuits where

open import PiLevel0
  using (U; ZERO; ONE; PLUS; TIMES; BOOL; BOOL²;
         _⟷_;
        unite₊; uniti₊; swap₊; assocl₊; assocr₊;
        unite⋆; uniti⋆; swap⋆; assocl⋆; assocr⋆;
        absorbr; absorbl; factorzr; factorzl; dist; factor;
        id⟷; _◎_; _⊕_; _⊗_;
        _⟷⟨_⟩_; _□;
        NOT; CNOT; TOFFOLI)

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

SWAP : BOOL² ⟷ BOOL²
SWAP = swap⋆

CONTROL : {t : U} → (t ⟷ t) → (TIMES BOOL t ⟷ TIMES BOOL t)
CONTROL {t} f = TIMES BOOL t
                  ⟷⟨ id⟷ ⟩
                TIMES (PLUS x y) t
                  ⟷⟨ dist ⟩
                PLUS (TIMES x t) (TIMES y t)
                  ⟷⟨ id⟷ ⊕ (id⟷ ⊗ f) ⟩
                PLUS (TIMES x t) (TIMES y t)
                  ⟷⟨ factor ⟩
                TIMES (PLUS x y) t
                  ⟷⟨ id⟷ ⟩
                TIMES BOOL t □
  where x = ONE; y = ONE

FREDKIN : TIMES BOOL BOOL² ⟷ TIMES BOOL BOOL²
FREDKIN = CONTROL SWAP

------------------------------------------------------------------------------
-- Fig. 4 in http://arxiv.org/pdf/1110.2574v2.pdf

-- (e) cycles

-- (c) reversible truth table

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


module Lists.Reverse where

open import Lists
open import Nats
open import Equality
open import Function

------------------------------------------------------------------------
-- internal stuffs

private

  -- rev$v:a=a:rev$v : ∀ {n m} {A : Set n} (a : A) (v : List A m) →
  --                 rev (v ∷ʳ a) ≡ a ∷ rev v
  -- rev$v:a=a:rev$v _ [] = refl
  -- rev$v:a=a:rev$v a (_ ∷ xs) with rev (xs ∷ʳ a) | rev$v:a=a:rev$v a xs
  -- ...                            | .(a ∷ rev xs) | refl = refl

  rev$v:a=a:rev$v : ∀ {n} {A : Set n} (a : A) (v : List A) →
                  reverse (v ∷ʳ a) ≡ a ∷ reverse v
  rev$v:a=a:rev$v _ [] = refl
  rev$v:a=a:rev$v a (_ ∷ xs)
    rewrite rev$v:a=a:rev$v a xs
            = refl

  rev∘rev=id : ∀ {n} {A : Set n} (v : List A) → reverse (reverse v) ≡ v
  rev∘rev=id [] = refl
  rev∘rev=id (x ∷ xs)
    rewrite rev$v:a=a:rev$v x $ reverse xs
          | rev∘rev=id xs
            = refl

------------------------------------------------------------------------
-- public aliases

list-rev-rev-id : ∀ {n} {A : Set n} (v : List A) → reverse (reverse v) ≡ v
list-rev-rev-id = rev∘rev=id


-- {-# OPTIONS -v tc.meta:100 #-}
-- Andreas, 2011-04-20
-- see Abel Pientka TLCA 2011
module PruningNonMillerPattern where

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

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

-- bad variable y in head position
test : let X : Nat -> Nat -> Nat
           X = _
           Y : Nat -> Nat -> Nat
           Y = _
       in  (C : Set) ->
           (({x y : Nat} -> X x x ≡ suc (Y x y)) ->
            ({x y : Nat} -> Y x x ≡ x)           ->
            ({x y : Nat} -> X (Y x y) y ≡ X x x) -> C) -> C
test C k = k refl refl refl
{- none of these equations is immediately solvable.  However,
   from 1. we deduce that Y does not depend on its second argument, thus
   from 2. we solve Y x y = x, and then
   eqn. 3. simplifies to X x y = X x x, thus, X does not depend on its second arg,
   we can then solve using 1.  X x y = suc x
-}

-- a variant, where pruning is even triggered from a non-pattern
test' : let X : Nat -> Nat -> Nat
            X = _
            Y : Nat -> Nat -> Nat
            Y = _
        in  (C : Set) ->
            (({x y : Nat} -> X x (suc x) ≡ suc (Y x y)) ->  -- non-pattern lhs
             ({x y : Nat} -> Y x x ≡ x)           ->
             ({x y : Nat} -> X (Y x y) y ≡ X x x) -> C) -> C
test' C k = k refl refl refl

-- another variant, where the pruned argument does not have an offending
-- variable in the head, but in a non-eliminateable position
-- (argument to a datatype)

data Sing {A : Set} : A → Set where
  sing : (x : A) -> Sing x

-- bad rigid under a data type constructor
test2 : let X : Nat -> Nat -> Nat
            X = _
            Y : Nat → Set -> Nat
            Y = _
        in  (C : Set) ->
            (({x y : Nat} -> X x x ≡ suc (Y x (Sing (suc y)))) ->
             ({x y : Nat} -> Y x (Sing x) ≡ x)           ->
             ({x y : Nat} -> X (Y x (Sing y)) y ≡ X x x) -> C) -> C
test2 C k = k refl refl refl

T : Nat → Set
T zero    = Nat
T (suc _) = Nat → Nat

-- bad rigid y under a Pi type constructor
test3 : let X : Nat -> Nat -> Nat
            X = _
            Y : Nat → Set -> Nat
            Y = _
        in  (C : Set) ->
            (({x y : Nat} -> X x x ≡ suc (Y x (T y -> T y))) ->
             ({x y : Nat} -> Y x (Sing x) ≡ x)           ->
             ({x y : Nat} -> X (Y x (Sing y)) y ≡ X x x) -> C) -> C
test3 C k = k refl refl refl

-- bad rigid y in head position under a lambda
test4 : let X : Nat -> Nat -> Nat
            X = _
            Y : Nat → (Nat → Nat) -> Nat
            Y = _
        in  (C : Set) ->
            ((∀ {x : Nat} {y : Nat → Nat} -> X x x ≡ suc (Y x (λ k → y zero))) ->
             (∀ {x : Nat} {y : Nat → Nat} -> Y x (λ k → y zero) ≡ x)           ->
             (∀ {x : Nat} {y : Nat } -> X (Y x (λ k → y)) y ≡ X x x) -> C) -> C
test4 C k = k refl refl refl

-- bad variable in irrelevant position
test5 : let X : Nat -> Nat -> Nat
            X = _
            Y : Nat -> .Nat -> Nat
            Y = _
        in  (C : Set) ->
            (({x y : Nat} -> X x (suc x) ≡ suc (Y x (suc y))) ->  -- non-pattern lhs
             ({x y : Nat} -> Y x x ≡ x)           ->
             ({x y : Nat} -> X (Y x (suc y)) y ≡ X x x) -> C) -> C
test5 C k = k refl refl refl


{-# OPTIONS --cubical-compatible --rewriting --confluence-check #-}

postulate
  _↦_ : ∀ {i} {A : Set i} → A → A → Set i
  idr : ∀ {i} {A : Set i} {a : A} → a ↦ a
{-# BUILTIN REWRITE _↦_ #-}

data _==_ {i} {A : Set i} (a : A) : A → Set i where
  idp : a == a

PathOver : ∀ {i j} {A : Set i} (B : A → Set j)
  {x y : A} (p : x == y) (u : B x) (v : B y) → Set j
PathOver B idp u v = (u == v)

syntax PathOver B p u v =
  u == v [ B ↓ p ]

postulate
  PathOver-rewr : ∀ {i j} {A : Set i} {B : Set j} {x y : A} (p : x == y) (u v : B) →
    (PathOver (λ _ → B) p u v) ↦ (u == v)
  {-# REWRITE PathOver-rewr #-}

ap : ∀ {i j} {A : Set i} {B : A → Set j} (f : (a : A) → B a) {x y : A}
  → (p : x == y) → PathOver B p (f x) (f y)
ap f idp = idp

postulate
  Circle : Set
  base : Circle
  loop : base == base

module _ {i} {P : Circle → Set i} (base* : P base) (loop* : base* == base* [ P ↓ loop ])
 where
  postulate
    Circle-elim : (x : Circle) → P x
    Circle-base-β : Circle-elim base ↦ base*
    {-# REWRITE Circle-base-β #-}
    Circle-loop-β : ap Circle-elim loop ↦ loop*
    {-# REWRITE Circle-loop-β #-}

idCircle : Circle → Circle
idCircle = Circle-elim base loop


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

open import Prelude
open import Relation.Binary
open import Function.Injective

module Relation.Binary.Construct.On
  {a b ℓ₁ ℓ₂} {A : Type a} {B : Type b}
  (f : A → B) (f-inj : Injective f)
  (ord : TotalOrder B ℓ₁ ℓ₂)
  where

open TotalOrder ord renaming (refl to ≤-refl)

_<′_ : A → A → Type _
x <′ y = f x < f y

_≤′_ : A → A → Type _
x ≤′ y = f x ≤ f y

on-ord : TotalOrder A ℓ₁ ℓ₂
StrictPreorder._<_   (StrictPartialOrder.strictPreorder (TotalOrder.strictPartialOrder on-ord)) = _<′_
StrictPreorder.trans (StrictPartialOrder.strictPreorder (TotalOrder.strictPartialOrder on-ord)) = <-trans
StrictPreorder.irrefl  (StrictPartialOrder.strictPreorder (TotalOrder.strictPartialOrder on-ord)) = irrefl
StrictPartialOrder.conn (TotalOrder.strictPartialOrder on-ord) p q = f-inj _ _ (conn p q)
Preorder._≤_   (PartialOrder.preorder (TotalOrder.partialOrder on-ord)) = _≤′_
Preorder.refl  (PartialOrder.preorder (TotalOrder.partialOrder on-ord)) = ≤-refl
Preorder.trans (PartialOrder.preorder (TotalOrder.partialOrder on-ord)) = ≤-trans
PartialOrder.antisym (TotalOrder.partialOrder on-ord) p q = f-inj _ _ (antisym p q)
TotalOrder._<?_ on-ord x y = f x <? f y
TotalOrder.≰⇒> on-ord = ≰⇒>
TotalOrder.≮⇒≥ on-ord = ≮⇒≥


{-# OPTIONS --safe #-}
module Cubical.Data.Nat.Order.Recursive where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport

open import Cubical.Data.Empty as Empty
open import Cubical.Data.Sigma
open import Cubical.Data.Sum as Sum
open import Cubical.Data.Unit

open import Cubical.Data.Nat.Base
open import Cubical.Data.Nat.Properties

open import Cubical.Induction.WellFounded

open import Cubical.Relation.Nullary

infix 4 _≤_ _<_

_≤_ : ℕ → ℕ → Type₀
zero ≤ _ = Unit
suc m ≤ zero = ⊥
suc m ≤ suc n = m ≤ n

_<_ : ℕ → ℕ → Type₀
m < n = suc m ≤ n

_≤?_ : (m n : ℕ) → Dec (m ≤ n)
zero  ≤? _     = yes tt
suc m ≤? zero  = no λ ()
suc m ≤? suc n = m ≤? n

data Trichotomy (m n : ℕ) : Type₀ where
  lt : m < n → Trichotomy m n
  eq : m ≡ n → Trichotomy m n
  gt : n < m → Trichotomy m n

private
  variable
    ℓ : Level
    R : Type ℓ
    P : ℕ → Type ℓ
    k l m n : ℕ

isProp≤ : isProp (m ≤ n)
isProp≤ {zero} = isPropUnit
isProp≤ {suc m} {zero}  = isProp⊥
isProp≤ {suc m} {suc n} = isProp≤ {m} {n}

≤-k+ : m ≤ n → k + m ≤ k + n
≤-k+ {k = zero}  m≤n = m≤n
≤-k+ {k = suc k} m≤n = ≤-k+ {k = k} m≤n

≤-+k : m ≤ n → m + k ≤ n + k
≤-+k {m} {n} {k} m≤n
  = transport (λ i → +-comm k m i ≤ +-comm k n i) (≤-k+ {m} {n} {k} m≤n)

≤-refl : ∀ m → m ≤ m
≤-refl zero = _
≤-refl (suc m) = ≤-refl m

≤-trans : k ≤ m → m ≤ n → k ≤ n
≤-trans {zero} _ _ = _
≤-trans {suc k} {suc m} {suc n} = ≤-trans {k} {m} {n}

≤-antisym : m ≤ n → n ≤ m → m ≡ n
≤-antisym {zero} {zero} _ _ = refl
≤-antisym {suc m} {suc n} m≤n n≤m = cong suc (≤-antisym m≤n n≤m)

≤-k+-cancel : k + m ≤ k + n → m ≤ n
≤-k+-cancel {k =  zero} m≤n = m≤n
≤-k+-cancel {k = suc k} m≤n = ≤-k+-cancel {k} m≤n

≤-+k-cancel : m + k ≤ n + k → m ≤ n
≤-+k-cancel {m} {k} {n}
  = ≤-k+-cancel {k} {m} {n} ∘ transport λ i → +-comm m k i ≤ +-comm n k i

¬m<m : ¬ m < m
¬m<m {suc m} = ¬m<m {m}

≤0→≡0 : n ≤ 0 → n ≡ 0
≤0→≡0 {zero} _ = refl

¬m+n<m : ¬ m + n < m
¬m+n<m {suc m} = ¬m+n<m {m}

<-weaken : m < n → m ≤ n
<-weaken {zero} _ = _
<-weaken {suc m} {suc n} = <-weaken {m}

<-trans : k < m → m < n → k < n
<-trans {k} {m} {n} k<m m<n
  = ≤-trans {suc k} {m} {n} k<m (<-weaken {m} m<n)

<-asym : m < n → ¬ n < m
<-asym {m} m<n n<m = ¬m<m {m} (<-trans {m} {_} {m} m<n n<m)

<→≢ : n < m → ¬ n ≡ m
<→≢ {n} {m} p q = ¬m<m {m = m} (subst {x = n} (_< m) q p)

Trichotomy-suc : Trichotomy m n → Trichotomy (suc m) (suc n)
Trichotomy-suc (lt m<n) = lt m<n
Trichotomy-suc (eq m≡n) = eq (cong suc m≡n)
Trichotomy-suc (gt n<m) = gt n<m

_≟_ : ∀ m n → Trichotomy m n
zero  ≟ zero = eq refl
zero  ≟ suc n = lt _
suc m ≟ zero = gt _
suc m ≟ suc n = Trichotomy-suc (m ≟ n)

k≤k+n : ∀ k → k ≤ k + n
k≤k+n zero    = _
k≤k+n (suc k) = k≤k+n k

n≤k+n : ∀ n → n ≤ k + n
n≤k+n {k} n = transport (λ i → n ≤ +-comm n k i) (k≤k+n n)

≤-split : m ≤ n → (m < n) ⊎ (m ≡ n)
≤-split {zero} {zero} m≤n = inr refl
≤-split {zero} {suc n} m≤n = inl _
≤-split {suc m} {suc n} m≤n
  = Sum.map (idfun _) (cong suc) (≤-split {m} {n} m≤n)

module WellFounded where
  wf-< : WellFounded _<_
  wf-rec-< : ∀ n → WFRec _<_ (Acc _<_) n

  wf-< n = acc (wf-rec-< n)

  wf-rec-< (suc n) m m≤n with ≤-split {m} {n} m≤n
  ... | inl m<n = wf-rec-< n m m<n
  ... | inr m≡n = subst⁻ (Acc _<_) m≡n (wf-< n)

wf-elim : (∀ n → (∀ m → m < n → P m) → P n) → ∀ n → P n
wf-elim = WFI.induction WellFounded.wf-<

wf-rec : (∀ n → (∀ m → m < n → R) → R) → ℕ → R
wf-rec {R = R} = wf-elim {P = λ _ → R}

module Minimal where
  Least : ∀{ℓ} → (ℕ → Type ℓ) → (ℕ → Type ℓ)
  Least P m = P m × (∀ n → n < m → ¬ P n)

  isPropLeast : (∀ m → isProp (P m)) → ∀ m → isProp (Least P m)
  isPropLeast pP m
    = isPropΣ (pP m) (λ _ → isPropΠ3 λ _ _ _ → isProp⊥)

  Least→ : Σ _ (Least P) → Σ _ P
  Least→ = map-snd fst

  search
    : (∀ m → Dec (P m))
    → ∀ n → (Σ[ m ∈ ℕ ] Least P m) ⊎ (∀ m → m < n → ¬ P m)
  search dec zero = inr (λ _ b _ → b)
  search {P = P} dec (suc n) with search dec n
  ... | inl tup = inl tup
  ... | inr ¬P<n with dec n
  ... | yes Pn = inl (n , Pn , ¬P<n)
  ... | no ¬Pn = inr λ m m≤n
      → case ≤-split m≤n of λ where
          (inl m<n) → ¬P<n m m<n
          (inr m≡n) → subst⁻ (¬_ ∘ P) m≡n ¬Pn

  →Least : (∀ m → Dec (P m)) → Σ _ P → Σ _ (Least P)
  →Least dec (n , Pn) with search dec n
  ... | inl least = least
  ... | inr ¬P<n  = n , Pn , ¬P<n

  Least-unique : ∀ m n → Least P m → Least P n → m ≡ n
  Least-unique m n (Pm , ¬P<m) (Pn , ¬P<n) with m ≟ n
  ... | lt m<n = Empty.rec (¬P<n m m<n Pm)
  ... | eq m≡n = m≡n
  ... | gt n<m = Empty.rec (¬P<m n n<m Pn)

  isPropΣLeast : (∀ m → isProp (P m)) → isProp (Σ _ (Least P))
  isPropΣLeast pP (m , LPm) (n , LPn)
    = ΣPathP λ where
        .fst → Least-unique m n LPm LPn
        .snd → isOfHLevel→isOfHLevelDep 1 (isPropLeast pP)
                LPm LPn (Least-unique m n LPm LPn)

  Decidable→Collapsible
    : (∀ m → isProp (P m)) → (∀ m → Dec (P m)) → Collapsible (Σ ℕ P)
  Decidable→Collapsible pP dP = λ where
    .fst → Least→ ∘ →Least dP
    .snd x y → cong Least→ (isPropΣLeast pP (→Least dP x) (→Least dP y))

open Minimal using (Decidable→Collapsible) public


{-# OPTIONS --rewriting #-}

{- Lower can be a record if using type-in-type or allowing large eliminations:
{-# OPTIONS --type-in-type #-}
record Lower (A : Set₁) : Set where
  constructor lower
  field raise : A
open Lower
-}

postulate
  _≡_ : ∀ {A : Set₁} → A → A → Set
  Lower : (A : Set₁) → Set
  lower : ∀ {A} → A → Lower A
  raise : ∀ {A} → Lower A → A
  beta : ∀ {A} {a : A} → raise (lower a) ≡ a

{-# BUILTIN REWRITE _≡_ #-}
{-# REWRITE beta #-}

data ⊥ : Set where

℘ : ∀ {ℓ} → Set ℓ → Set _
℘ {ℓ} S = S → Set

U : Set
U = Lower (∀ (X : Set) → (℘ (℘ X) → X) → ℘ (℘ X))

τ : ℘ (℘ U) → U
τ t = lower (λ X f p → t (λ x → p (f (raise x X f))))

σ : U → ℘ (℘ U)
σ s = raise s U τ

Δ : ℘ U
Δ y = Lower (∀ p → σ y p → p (τ (σ y))) → ⊥

Ω : U
Ω = τ (λ p → (∀ x → σ x p → p x))

R : ∀ p → (∀ x → σ x p → p x) → p Ω
R _ 𝟙 = 𝟙 Ω (λ x → 𝟙 (τ (σ x)))

M : ∀ x → σ x Δ → Δ x
M _ 𝟚 𝟛 =
  let 𝟛 = raise 𝟛
  in 𝟛 Δ 𝟚 (lower (λ p → 𝟛 (λ y → p (τ (σ y)))))

L : (∀ p → (∀ x → σ x p → p x) → p Ω) → ⊥
L 𝟘 = 𝟘 Δ M (lower (λ p → 𝟘 (λ y → p (τ (σ y)))))

false : ⊥
false = L R


------------------------------------------------------------------------
-- The Agda standard library
--
-- Closure of a unary relation with respect to a preorder
------------------------------------------------------------------------

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

open import Relation.Binary

module Relation.Unary.Closure.Preorder {a r e} (P : Preorder a e r) where

open Preorder P
open import Relation.Unary using (Pred)

-- Specialising the results proven generically in `Base`.
import Relation.Unary.Closure.Base _∼_ as Base
open Base public using (□; map; Closed)

module _ {t} {T : Pred Carrier t} where

  reindex : ∀ {x y} → x ∼ y → □ T x → □ T y
  reindex = Base.reindex trans

  extract : ∀ {x} → □ T x → T x
  extract = Base.extract refl

  duplicate : ∀ {x} → □ T x → □ (□ T) x
  duplicate = Base.duplicate trans

□-closed : ∀ {t} {T : Pred Carrier t} → Closed (□ T)
□-closed = Base.□-closed trans


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

module LeqLemmas where

open import Data.Nat
  using (ℕ; zero; suc; _+_; _*_; _<_; _≤_; _≤?_; z≤n; s≤s;
    module ≤-Reasoning)
open import Data.Nat.Properties.Simple
  using (*-comm; +-right-identity; +-comm; +-assoc)
open import Data.Nat.Properties
  using (cancel-+-left-≤; n≤m+n)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; cong; module ≡-Reasoning)
open import Relation.Binary using (Decidable)
open import Relation.Binary.Core using (Transitive; _⇒_)
open import Data.Fin
  using (Fin; zero; suc; toℕ; fromℕ; raise; inject≤)
open import Data.Fin.Properties using (bounded; to-from)

------------------------------------------------------------------------------
-- Proofs and definitions about ≤ on natural numbers

_<?_ : Decidable _<_
i <? j = suc i ≤? j

-- buried in Data.Nat

refl′ : _≡_ ⇒ _≤_
refl′ {0} refl = z≤n
refl′ {suc m} refl = s≤s (refl′ refl)

trans< : Transitive _<_
trans< (s≤s z≤n) (s≤s _) = s≤s z≤n
trans< (s≤s (s≤s i≤j)) (s≤s sj<k) = s≤s (trans< (s≤s i≤j) sj<k)

trans≤ : Transitive _≤_
trans≤ z≤n x = z≤n
trans≤ (s≤s m≤n) (s≤s n≤o) = s≤s (trans≤ m≤n n≤o)

i*1≡i : (i : ℕ) → i * 1 ≡ i
i*1≡i i = begin (i * 1
                   ≡⟨ *-comm i 1 ⟩
                 1 * i
                   ≡⟨ refl ⟩
                 i + 0
                   ≡⟨ +-right-identity i ⟩
                 i ∎)
  where open ≡-Reasoning

i≤i : (i : ℕ) → i ≤ i
i≤i 0 = z≤n
i≤i (suc i) = s≤s (i≤i i)

i≤si : (i : ℕ) → i ≤ suc i
i≤si 0       = z≤n
i≤si (suc i) = s≤s (i≤si i)

i≤j+i : ∀ {i j} → i ≤ j + i
i≤j+i {i} {0} = i≤i i
i≤j+i {i} {suc j} =
  begin (i
           ≤⟨ i≤j+i {i} {j} ⟩
         j + i
           ≤⟨ i≤si (j + i) ⟩
         suc j + i ∎)
  where open ≤-Reasoning

cong+r≤ : ∀ {i j} → i ≤ j → (k : ℕ) → i + k ≤ j + k
cong+r≤ {0}     {j}     z≤n       k = n≤m+n j k
cong+r≤ {suc i} {0}     ()        k -- absurd
cong+r≤ {suc i} {suc j} (s≤s i≤j) k = s≤s (cong+r≤ {i} {j} i≤j k)

cong+l≤ : ∀ {i j} → i ≤ j → (k : ℕ) → k + i ≤ k + j
cong+l≤ {i} {j} i≤j k =
  begin (k + i
           ≡⟨ +-comm k i ⟩
         i + k
           ≤⟨ cong+r≤ i≤j k ⟩
         j + k
           ≡⟨ +-comm j k ⟩
         k + j ∎)
  where open ≤-Reasoning

cong*r≤ : ∀ {i j} → i ≤ j → (k : ℕ) → i * k ≤ j * k
cong*r≤ {0}     {j}     z≤n       k = z≤n
cong*r≤ {suc i} {0}     ()        k -- absurd
cong*r≤ {suc i} {suc j} (s≤s i≤j) k = cong+l≤ (cong*r≤ i≤j k) k

sinj : ∀ {i j} → suc i ≡ suc j → i ≡ j
sinj = cong (λ { 0 → 0 ; (suc x) → x })

sinj≤ : ∀ {i j} → suc i ≤ suc j → i ≤ j
sinj≤ {0}     {j}     _        = z≤n
sinj≤ {suc i} {0}     (s≤s ()) -- absurd
sinj≤ {suc i} {suc j} (s≤s p)  = p

i*n+n≤sucm*n : ∀ {m n} → (i : Fin (suc m)) → toℕ i * n + n ≤ suc m * n
i*n+n≤sucm*n {0} {n} zero =
  begin (n
           ≡⟨ sym (+-right-identity n) ⟩
         n + 0
           ≤⟨ i≤i (n + 0) ⟩
         n + 0 ∎)
  where open ≤-Reasoning
i*n+n≤sucm*n {0} {n} (suc ())
i*n+n≤sucm*n {suc m} {n} i =
  begin (toℕ i * n + n
           ≡⟨ +-comm (toℕ i * n) n ⟩
         n + toℕ i * n
           ≡⟨ refl ⟩
         suc (toℕ i) * n
           ≤⟨ cong*r≤ (bounded i) n ⟩
         suc (suc m) * n ∎)
  where open ≤-Reasoning

i*n+k≤m*n : ∀ {m n} → (i : Fin m) → (k : Fin n) →
            (suc (toℕ i * n + toℕ k) ≤ m * n)
i*n+k≤m*n {0} {_} () _
i*n+k≤m*n {_} {0} _ ()
i*n+k≤m*n {suc m} {suc n} i k =
  begin (suc (toℕ i * suc n + toℕ k)
           ≡⟨  cong suc (+-comm (toℕ i * suc n) (toℕ k))  ⟩
         suc (toℕ k + toℕ i * suc n)
           ≡⟨ refl ⟩
         suc (toℕ k) + (toℕ i * suc n)
           ≤⟨ cong+r≤ (bounded k) (toℕ i * suc n) ⟩
         suc n + (toℕ i * suc n)
           ≤⟨ cong+l≤ (cong*r≤ (sinj≤ (bounded i)) (suc n)) (suc n) ⟩
         suc n + (m * suc n)
           ≡⟨ refl ⟩
         suc m * suc n ∎)
  where open ≤-Reasoning

bounded' : (m : ℕ) → (j : Fin (suc m)) → (toℕ j ≤ m)
bounded' m j with bounded j
... | s≤s pr = pr

simplify-≤ : {m n m' n' : ℕ} → (m ≤ n) → (m ≡ m') → (n ≡ n') → (m' ≤ n')
simplify-≤ leq refl refl = leq

cancel-Fin : ∀ (n y z : ℕ) → ((x : Fin (suc n)) → toℕ x + y ≤ n + z) → y ≤ z
cancel-Fin 0 y z pf = pf zero
cancel-Fin (suc n) y z pf =
  cancel-+-left-≤ n (trans≤ leq (sinj≤ (pf (fromℕ (suc n)))))
  where leq : n + y ≤ toℕ (fromℕ n) + y
        leq = begin (n + y
                       ≡⟨ cong (λ x → x + y) (sym (to-from n)) ⟩
                     toℕ (fromℕ n) + y ∎)
              where open ≤-Reasoning

≤-proof-irrelevance : {m n : ℕ} → (p q : m ≤ n) → p ≡ q
≤-proof-irrelevance z≤n z≤n = refl
≤-proof-irrelevance (s≤s p) (s≤s q) = cong s≤s (≤-proof-irrelevance p q)

raise-suc' : (n a b n+a : ℕ) (n+a≡ : n+a ≡ n + a) →
  (leq : suc a ≤ b) →
  (leq' : suc n+a ≤ n + b) →
  raise n (inject≤ (fromℕ a) leq) ≡ inject≤ (fromℕ n+a) leq'
raise-suc' 0 a b .a refl leq leq' =
  cong (inject≤ (fromℕ a)) (≤-proof-irrelevance leq leq')
raise-suc' (suc n) a b .(suc (n + a)) refl leq (s≤s leq') =
  cong suc (raise-suc' n a b (n + a) refl leq leq')

raise-suc : (m n : ℕ) (j : Fin (suc m)) (d : Fin (suc n))
  (leq : suc (toℕ j * suc n + toℕ d) ≤ suc m * suc n) →
  (leq' : suc (toℕ (suc j) * suc n + toℕ d) ≤ suc (suc m) * suc n) →
  raise (suc n) (inject≤ (fromℕ (toℕ j * suc n + toℕ d)) leq) ≡
  inject≤ (fromℕ (toℕ (suc j) * suc n + toℕ d)) leq'
raise-suc m n j d leq leq' =
  raise-suc'
    (suc n)
    (toℕ j * suc n + toℕ d)
    (suc m * suc n)
    (toℕ (suc j) * suc n + toℕ d)
    (begin (toℕ (suc j) * suc n + toℕ d
              ≡⟨ refl ⟩
            (suc n + toℕ j * suc n) + toℕ d
              ≡⟨ +-assoc (suc n) (toℕ j * suc n) (toℕ d) ⟩
            suc n + (toℕ j * suc n + toℕ d) ∎))
    leq
    leq'
  where open ≡-Reasoning

{-
-- buried in Data.Nat
refl′ : _≡_ ⇒ _≤_
refl′ {0} refl = z≤n
refl′ {suc m} refl = s≤s (refl′ refl)

trans< : Transitive _<_
trans< (s≤s z≤n) (s≤s _) = s≤s z≤n
trans< (s≤s (s≤s i≤j)) (s≤s sj<k) = s≤s (trans< (s≤s i≤j) sj<k)

trans≤ : Transitive _≤_
trans≤ z≤n x = z≤n
trans≤ (s≤s m≤n) (s≤s n≤o) = s≤s (trans≤ m≤n n≤o)

i*1≡i : (i : ℕ) → i * 1 ≡ i
i*1≡i i = begin (i * 1
                   ≡⟨ *-comm i 1 ⟩
                 1 * i
                   ≡⟨ refl ⟩
                 i + 0
                   ≡⟨ +-right-identity i ⟩
                 i ∎)
  where open ≡-Reasoning

i≤i : (i : ℕ) → i ≤ i
i≤i 0 = z≤n
i≤i (suc i) = s≤s (i≤i i)

i≤si : (i : ℕ) → i ≤ suc i
i≤si 0       = z≤n
i≤si (suc i) = s≤s (i≤si i)

i≤j+i : ∀ {i j} → i ≤ j + i
i≤j+i {i} {0} = i≤i i
i≤j+i {i} {suc j} =
  begin (i
           ≤⟨ i≤j+i {i} {j} ⟩
         j + i
           ≤⟨ i≤si (j + i) ⟩
         suc j + i ∎)
  where open ≤-Reasoning

sinj : ∀ {i j} → suc i ≡ suc j → i ≡ j
sinj = cong (λ { 0 → 0 ; (suc x) → x })

i*n+n≤sucm*n : ∀ {m n} → (i : Fin (suc m)) → toℕ i * n + n ≤ suc m * n
i*n+n≤sucm*n {0} {n} zero =
  begin (n
           ≡⟨ sym (+-right-identity n) ⟩
         n + 0
           ≤⟨ i≤i (n + 0) ⟩
         n + 0 ∎)
  where open ≤-Reasoning
i*n+n≤sucm*n {0} {n} (suc ())
i*n+n≤sucm*n {suc m} {n} i =
  begin (toℕ i * n + n
           ≡⟨ +-comm (toℕ i * n) n ⟩
         n + toℕ i * n
           ≡⟨ refl ⟩
         suc (toℕ i) * n
           ≤⟨ cong*r≤ (bounded i) n ⟩
         suc (suc m) * n ∎)
  where open ≤-Reasoning

i*n+k≤m*n : ∀ {m n} → (i : Fin m) → (k : Fin n) →
            (suc (toℕ i * n + toℕ k) ≤ m * n)
i*n+k≤m*n {0} {_} () _
i*n+k≤m*n {_} {0} _ ()
i*n+k≤m*n {suc m} {suc n} i k =
  begin (suc (toℕ i * suc n + toℕ k)
           ≡⟨  cong suc (+-comm (toℕ i * suc n) (toℕ k))  ⟩
         suc (toℕ k + toℕ i * suc n)
           ≡⟨ refl ⟩
         suc (toℕ k) + (toℕ i * suc n)
           ≤⟨ cong+r≤ (bounded k) (toℕ i * suc n) ⟩
         suc n + (toℕ i * suc n)
           ≤⟨ cong+l≤ (cong*r≤ (sinj≤ (bounded i)) (suc n)) (suc n) ⟩
         suc n + (m * suc n)
           ≡⟨ refl ⟩
         suc m * suc n ∎)
  where open ≤-Reasoning

bounded' : (m : ℕ) → (j : Fin (suc m)) → (toℕ j ≤ m)
bounded' m j with bounded j
... | s≤s pr = pr

simplify-≤ : {m n m' n' : ℕ} → (m ≤ n) → (m ≡ m') → (n ≡ n') → (m' ≤ n')
simplify-≤ leq refl refl = leq

cancel-Fin : ∀ (n y z : ℕ) → ((x : Fin (suc n)) → toℕ x + y ≤ n + z) → y ≤ z
cancel-Fin 0 y z pf = pf zero
cancel-Fin (suc n) y z pf =
  cancel-+-left-≤ n (trans≤ leq (sinj≤ (pf (fromℕ (suc n)))))
  where leq : n + y ≤ toℕ (fromℕ n) + y
        leq = begin (n + y
                       ≡⟨ cong (λ x → x + y) (sym (to-from n)) ⟩
                     toℕ (fromℕ n) + y ∎)
              where open ≤-Reasoning

≤-proof-irrelevance : {m n : ℕ} → (p q : m ≤ n) → p ≡ q
≤-proof-irrelevance z≤n z≤n = refl
≤-proof-irrelevance (s≤s p) (s≤s q) = cong s≤s (≤-proof-irrelevance p q)

raise-suc' : (n a b n+a : ℕ) (n+a≡ : n+a ≡ n + a) →
  (leq : suc a ≤ b) →
  (leq' : suc n+a ≤ n + b) →
  raise n (inject≤ (fromℕ a) leq) ≡ inject≤ (fromℕ n+a) leq'
raise-suc' 0 a b .a refl leq leq' =
  cong (inject≤ (fromℕ a)) (≤-proof-irrelevance leq leq')
raise-suc' (suc n) a b .(suc (n + a)) refl leq (s≤s leq') =
  cong suc (raise-suc' n a b (n + a) refl leq leq')

raise-suc : (m n : ℕ) (j : Fin (suc m)) (d : Fin (suc n))
  (leq : suc (toℕ j * suc n + toℕ d) ≤ suc m * suc n) →
  (leq' : suc (toℕ (suc j) * suc n + toℕ d) ≤ suc (suc m) * suc n) →
  raise (suc n) (inject≤ (fromℕ (toℕ j * suc n + toℕ d)) leq) ≡
  inject≤ (fromℕ (toℕ (suc j) * suc n + toℕ d)) leq'
raise-suc m n j d leq leq' =
  raise-suc'
    (suc n)
    (toℕ j * suc n + toℕ d)
    (suc m * suc n)
    (toℕ (suc j) * suc n + toℕ d)
    (begin (toℕ (suc j) * suc n + toℕ d
              ≡⟨ refl ⟩
            (suc n + toℕ j * suc n) + toℕ d
              ≡⟨ +-assoc (suc n) (toℕ j * suc n) (toℕ d) ⟩
            suc n + (toℕ j * suc n + toℕ d) ∎))
    leq
    leq'
  where open ≡-Reasoning
-}


module Examples where

open import LF
open import IIRD
open import IIRDr

-- Some helper functions

infixl 50 _+OP_

_+OP_ : {I : Set}{D : I -> Set1}{E : Set1} -> OP I D E -> OP I D E -> OP I D E
γ₀ +OP γ₁ = σ Two (\x -> case₂ x γ₀ γ₁)

-- First something simple.

bool : OPr One (\_ -> One')
bool _ = ι★r +OP ι★r

Bool : Set
Bool = Ur bool ★

false : Bool
false = intror < ★₀ | ★ >

true : Bool
true = intror < ★₁ | ★ >

-- We don't have universe subtyping, and we only setup large elimination rules.
if_then_else_ : {A : Set1} -> Bool -> A -> A -> A
if_then_else_ {A} b x y = Rr bool (\_ _ -> A) (\_ a _ -> case₂ (π₀ a) y x) ★ b

-- Something recursive

nat : OPr One (\_ -> One')
nat _ = ι★r +OP δ One (\_ -> ★) (\_ -> ι★r)

Nat : Set
Nat = Ur nat ★

zero : Nat
zero = intror < ★₀ | ★ >

suc : Nat -> Nat
suc n = intror < ★₁ | < (\_ -> n) | ★ > >


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

open import Prelude hiding (A; B)
open import Categories

module Categories.Pushout {ℓ₁ ℓ₂} (C : Category ℓ₁ ℓ₂) where

open Category C

private
  variable
    A B : Ob
    h₁ h₂ j : A ⟶ B

record Pushout (f : X ⟶ Y) (g : X ⟶ Z) : Type (ℓ₁ ℓ⊔ ℓ₂) where
  field
    {Q} : Ob
    i₁ : Y ⟶ Q
    i₂ : Z ⟶ Q
    commute : i₁ · f ≡ i₂ · g
    universal : h₁ · f ≡ h₂ · g → Q ⟶ Codomain h₁
    unique : ∀ {eq : h₁ · f ≡ h₂ · g} →
             j · i₁ ≡ h₁ → j · i₂ ≡ h₂ →
             j ≡ universal eq

    universal·i₁≡h₁  : ∀ {eq : h₁ · f ≡ h₂ · g} →
                         universal eq · i₁ ≡ h₁
    universal·i₂≡h₂  : ∀ {eq : h₁ · f ≡ h₂ · g} →
                         universal eq · i₂ ≡ h₂

HasPushouts : Type (ℓ₁ ℓ⊔ ℓ₂)
HasPushouts = ∀ {X Y Z} → (f : X ⟶ Y) → (g : X ⟶ Z) → Pushout f g


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.DStructures.Structures.Group where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Structure
open import Cubical.Functions.FunExtEquiv

open import Cubical.Homotopy.Base

open import Cubical.Data.Sigma

open import Cubical.Relation.Binary

open import Cubical.Algebra.Group
open import Cubical.Structures.LeftAction

open import Cubical.DStructures.Base
open import Cubical.DStructures.Meta.Properties
open import Cubical.DStructures.Structures.Constant

open import Cubical.DStructures.Structures.Type

private
  variable
    ℓ ℓ' : Level

open URGStr

-------------------------------------------
-- URG structure on the type of groups
-------------------------------------------

𝒮-group : (ℓ : Level) → URGStr (Group {ℓ}) ℓ
𝒮-group ℓ ._≅_ = GroupEquiv
𝒮-group ℓ .ρ = idGroupEquiv
𝒮-group ℓ .uni = isUnivalent'→isUnivalent GroupEquiv
                                          idGroupEquiv
                                          λ G H → invEquiv (GroupPath G H)

-------------------------------------------
-- 𝒮ᴰ-hierarchies on top of 𝒮-group
--
-- Notation:
--
-- G - group
-- G² - pair of groups
-- F - morphism forth
-- B - morphism back
--
-- F B (FB)
-- \ | /
--   G
--   |
--   G
-------------------------------------------

module _ (ℓ ℓ' : Level) where

  ---- Underlying types

  -- pairs of groups
  G² = Group {ℓ} × Group {ℓ'}
  -- pairs of groups + a morphism forth
  G²F = Σ[ (G , H) ∈ G² ] GroupHom G H
  -- pairs of groups + a morphism back
  G²B = Σ[ (G , H) ∈ G² ] GroupHom H G
  -- pairs of groups + morphisms forth and back
  G²FB = Σ[ (G , H) ∈ G² ] GroupHom G H × GroupHom H G

  ---- 𝒮 and 𝒮ᴰ-structures

  -- Group morphisms displayed over pairs of groups
  𝒮ᴰ-G²\F : URGStrᴰ (𝒮-group ℓ ×𝒮 𝒮-group ℓ')
                    (λ (G , H) → GroupHom G H)
                    (ℓ-max ℓ ℓ')
  𝒮ᴰ-G²\F =
    make-𝒮ᴰ (λ {(G , H)} {(G' , H')} f (eG , eH) f' → (g : ⟨ G ⟩) → GroupEquiv.eq eH .fst ((f .fun) g) ≡ (f' .fun) (GroupEquiv.eq eG .fst g))
            (λ _ _ → refl)
            λ (G , H) f → isContrRespectEquiv (Σ-cong-equiv-snd (λ f' → isoToEquiv (invIso (GroupMorphismExtIso f f'))))
                                              (isContrSingl f)
    where open GroupHom

  -- URG structure on type of two groups with a group morphism
  𝒮-G²F : URGStr G²F (ℓ-max ℓ ℓ')
  𝒮-G²F = ∫⟨ 𝒮-group ℓ ×𝒮 𝒮-group ℓ' ⟩ 𝒮ᴰ-G²\F

  -- Same as 𝒮-G²F but with the morphism going the other way
  𝒮ᴰ-G²\B : URGStrᴰ (𝒮-group ℓ ×𝒮 𝒮-group ℓ')
                             (λ (G , H) → GroupHom H G)
                             (ℓ-max ℓ ℓ')
  𝒮ᴰ-G²\B =
    make-𝒮ᴰ (λ {(_ , H)} f (eG , eH) f' → (h : ⟨ H ⟩) → GroupEquiv.eq eG .fst (f .fun h) ≡ f' .fun (GroupEquiv.eq eH .fst h))
                (λ _ _ → refl)
                λ _ f → isContrRespectEquiv (Σ-cong-equiv-snd (λ f' → isoToEquiv (invIso (GroupMorphismExtIso f f')))) (isContrSingl f)
    where open GroupHom

  -- Type of two groups with a group morphism going back
  𝒮-G²B : URGStr G²B (ℓ-max ℓ ℓ')
  𝒮-G²B = ∫⟨ 𝒮-group ℓ ×𝒮 𝒮-group ℓ' ⟩ 𝒮ᴰ-G²\B

  -- Morphisms going forth and back displayed over pairs of groups
  𝒮ᴰ-G²\FB : URGStrᴰ (𝒮-group ℓ ×𝒮 𝒮-group ℓ')
                   (λ (G , H) → GroupHom G H × GroupHom H G)
                   (ℓ-max ℓ ℓ')
  𝒮ᴰ-G²\FB = combine-𝒮ᴰ 𝒮ᴰ-G²\F 𝒮ᴰ-G²\B

  -- URG structure on type of pairs of groups with morphisms going forth and back
  𝒮-G²FB : URGStr G²FB (ℓ-max ℓ ℓ')
  𝒮-G²FB = ∫⟨ 𝒮-group ℓ ×𝒮 𝒮-group ℓ' ⟩ 𝒮ᴰ-G²\FB


-- ----------------------------------------------------------------------
-- The Agda Descriptor Library
--
-- (Closed) Sets
-- ----------------------------------------------------------------------

module Data.Set where

open import Data.Empty using (⊥)
open import Data.Fin using (Fin; suc; zero)
open import Data.Nat using (ℕ)
open import Data.Product using (Σ)
open import Data.Unit using (⊤)

open import Data.Desc using (Desc; μ; _##_; _⟶_; Π)

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

-- ----------------------------------------------------------------------
-- Definition
--
-- A `Desc is a closed dependent type theory of
-- strictly positive functors : Set/A → Set/A
--
-- A `Set is a open dependent type theory of
-- the Agda model
--
-- To ensure a closed universe, we use
-- induction recursion.

data `Set : Set
⟦_⟧ : `Set → Set

private
  variable
    A : `Set

data `Desc (A : `Set) : Set
⟪_⟫ : `Desc A → Desc ⟦ A ⟧

data `Set where
  `Fin : ℕ → `Set
  `Σ `Π : (B : `Set) → (⟦ B ⟧ → `Set) → `Set
  `μ : `Desc A → ⟦ A ⟧ → `Set

⟦ `Fin n ⟧ = Fin n
⟦ `Σ A B ⟧ = Σ ⟦ A ⟧ (λ a → ⟦ B a ⟧ )
⟦ `Π A B ⟧ = (a : ⟦ A ⟧) → ⟦ B a ⟧
⟦ `μ d a ⟧ = μ ⟪ d ⟫ a

infixr 6 _⟶_

data `Desc A where
  `Π : (B : `Set) → (⟦ B ⟧ → `Desc A) → `Desc A
  _⟶_ : `Desc A → `Desc A → `Desc A
  _`##_ : (`Set → `Set) → ⟦ A ⟧ → `Desc A

⟪ `Π B d ⟫ = Π ⟦ B ⟧ (λ b → ⟪ d b ⟫)
⟪ d₁ ⟶ d₂ ⟫ = ⟪ d₁ ⟫ ⟶ ⟪ d₂ ⟫
⟪ f `## a ⟫ = {!   !} ## a

-- ----------------------------------------------------------------------
-- Example (Basic)

-- `⊤ : `Set
-- `⊤ = `Fin 1

-- `tt : ⟦ `⊤ ⟧
-- `tt = zero

-- `const : `Set → `Desc `⊤
-- `const A = `Π A λ _ → `# `tt

-- _`≡_ : ⟦ A ⟧ → ⟦ A ⟧ → `Set
-- x `≡ y = `μ (`# x) y

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

-- pattern nil = zero
-- pattern cons = (suc zero)

-- TODO: First order isomorphisms

-- _ : ⟦ zero `≡ zero ⟧
-- _ = {! refl  !}

-- ListD : `Set → `Desc `⊤
-- ListD A = `Π (`Fin 2)
--   λ{ nil → `# `tt
--    ; cons → `const A ⟶ `# `tt ⟶ `# `tt }

-- `List : `Set → `Set
-- `List A = `μ (ListD A) `tt

-- RoseD : `Set → `Desc `⊤
-- RoseD A = `const A ⟶ `List $ (`# `tt) ⟶ `# `tt

-- _#_ : (Set → Set) → A → Desc A
--
-- Computional:
-- f (X a)
--
-- Propositional:
-- f (a′ ≡ a)


module Issue470 where

data Bool : Set where
  true false : Bool

_and_ : Bool → Bool → Bool
true  and x = x
false and x = false

infixr 5 _∷_

data Foo : Bool → Set where
  []  : Foo true
  _∷_ : ∀ {b} (x : Bool) → Foo b → Foo (x and b)

Baz : Bool → Set
Baz true = Bool
Baz false = Foo false

data Bar : ∀ {b} → Foo b → Set where
  []  : Bar []
  _∷_ : ∀ {b} {foo : Foo b} {x} (g : Baz x) → (bar : Bar foo) → Bar (x ∷ foo)

foo : Foo false
foo = false ∷ true ∷ []

bar : Bar foo
bar = (false ∷ []) ∷ false ∷ [] -- ← is yellow

{-
_59 := _55 ∷ false ∷ [] [blocked by problem 75]
[75] ["apply" (_53 ∷ true ∷ [])] == ["apply" (false ∷ true ∷ [])] : Foo
(_53 and (true and true)) → Set [blocked by problem 76]
[76] (_53 and (true and true)) = false : Bool
_54 := false ∷ [] :? Baz _53
-}


{-
  Copyright 2019 Lisandra Silva

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}

open import Prelude
open import Data.Fin hiding (_≟_; _<_; _+_; pred; _≤_; lift)
open import Data.List
open import Data.List.Properties
open import Relation.Nullary.Negation using (contradiction ; contraposition)

open import StateMachineModel

module Examples.ProducerConsumer2
  (Message : Set) -- Message type
  where

  -----------------------------------------------------------------------------
  -- SPECIFICATION
  -----------------------------------------------------------------------------
  record State : Set where
    field
     produced   : List Message
     consumed   : List Message
  open State

  data MyEvent : Set where
    produce : Message → MyEvent
    consume : Message → MyEvent

  data MyEnabled : MyEvent → State → Set where
    prodEnabled : ∀ {st : State} {msg} -- always enabled
                  → MyEnabled (produce msg) st
    consEnabled : ∀ {st : State} {msg}
                  → (cons<prod : length (consumed st) < length (produced st) )
                  → msg ≡ lookup (produced st) (fromℕ≤ cons<prod)
                  → MyEnabled (consume msg) st

  MyAction : ∀ {preState : State} {event : MyEvent}
             → MyEnabled event preState
             → State
  MyAction {preSt} {produce m} enabled = record preSt
                                          { produced  = produced preSt ++ [ m ]
                                          }
  MyAction {preSt} {consume m} enabled = record preSt
                                          { consumed  = consumed preSt ++ [ m ]
                                          }

  initialState : State
  initialState = record
                   { produced   = []
                   ; consumed   = []
                   }

  MyStateMachine : StateMachine State MyEvent
  MyStateMachine = record
                     { initial = λ st → produced st ≡ [] × consumed st ≡ []
                     ; enabled = MyEnabled
                     ; action  = MyAction
                     }

  MyEventSet : EventSet {Event = MyEvent}
  MyEventSet (produce m) = ⊥
  MyEventSet (consume m) = ⊤

  data MyWeakFairness : EventSet → Set where
    wf : MyWeakFairness MyEventSet

  MySystem : System State MyEvent
  MySystem = record
             { stateMachine = MyStateMachine
             ; weakFairness = MyWeakFairness
             }

  -----------------------------------------------------------------------------
  -- PROOFS
  -----------------------------------------------------------------------------
  open LeadsTo State MyEvent MySystem

  myWFR : ∀ {n} → Z → State → Set
  myWFR {n} d st =  d + length (consumed st) ≡ n × n ≤ length (produced st)

  length-suc : ∀ {x : Message} l → length (l ++ [ x ]) ≡ 1 + length l
  length-suc {x} l rewrite length-++ l {[ x ]} | +-comm (length l) 1 = refl

  <⇒≤-l : ∀ {n} (m : Message) (l : List Message)
             → length l < n
             → length (l ++ [ m ]) ≤ n
  <⇒≤-l m l l<n rewrite length-suc {m} l = l<n

  suc-+-l : ∀ {w} (m : Message) (l : List Message)
               →  w + length (l ++ m ∷ []) ≡ suc (w + length l)
  suc-+-l {w} m l rewrite length-suc {m} l = +-suc w (length l)

  inv-cons≤prod : Invariant
                    MyStateMachine
                    λ state → length (consumed state) ≤ length (produced state)
  inv-cons≤prod (init (refl , refl)) = z≤n
  inv-cons≤prod (step {st} {produce m} rs enEv)
    rewrite length-suc {m} (produced st)
      = ≤-step (inv-cons≤prod rs)
  inv-cons≤prod (step {st} {consume m} rs (consEnabled c<p x₁))
      = <⇒≤-l m (consumed st) c<p
      --subst (_≤ length (produced st)) (sym (length-suc {m} (consumed st))) c<p

  m≤n⇒m≡n⊎m<n : ∀ {m n} → m ≤ n → m < n ⊎ m ≡ n
  m≤n⇒m≡n⊎m<n {0} {0} z≤n = inj₂ refl
  m≤n⇒m≡n⊎m<n {0} {suc n} x = inj₁ (s≤s z≤n)
  m≤n⇒m≡n⊎m<n {suc m} {suc n} (s≤s x)
    with m≤n⇒m≡n⊎m<n x
  ... | inj₁ m<n  = inj₁ (s≤s m<n)
  ... | inj₂ refl = inj₂ refl

  [Q∪Fx] : ∀ {st : State} {n}
           → length (produced st) ≡ n
           → length (consumed st) ≤ n
           → length (consumed st) ≡ n ⊎ ∃[ x ] myWFR {n} x st
  [Q∪Fx] {st} {n} refl cons≤n
    with m≤n⇒m≡n⊎m<n cons≤n
  ... | inj₂ cons≡n = inj₁ cons≡n
  ... | inj₁ cons<n = inj₂ ( n ∸ length (consumed st) , m∸n+n≡m cons≤n , ≤-refl )

  wfr-l++ : ∀ {m : Message} {n} l
                → n < length l
                → length (l ++ [ m ]) ∸ 1 ∸ n + n ≡ length l
                × length l ≤ length (l ++ [ m ])
  wfr-l++ {m} {n} l n<l rewrite length-suc {m} l
    = (m∸n+n≡m (<⇒≤ n<l)) , ≤-step ≤-refl

{-  [P]l-t[Q∪Fx] : ∀ {n}
                 → λ preSt → length (produced preSt) ≡ n
                           × length (consumed preSt) < n
                   l-t
                   (λ posSt → length (consumed posSt) ≡ n) ∪ [∃ x ∶ myWFR {n} x ]
-}
  [P]l-t[Q∪Fx] : ∀ {n}
                 → (_≡ n) ∘ length ∘ produced   ∩   (_< n) ∘ length ∘ consumed
                   l-t
                   (_≡ n) ∘ length ∘ consumed   ∪   [∃ x ∶ myWFR {n} x ]
  [P]l-t[Q∪Fx] =
    viaEvSet
      MyEventSet
      wf
      ( λ { (consume m) evSet
              → hoare λ { {st} (p≡n , c<p) enEv → let state = MyAction {st} enEv
                                                      cons  = consumed st
                                                      c≤p   = <⇒≤-l m cons c<p
                                                  in [Q∪Fx] {state} p≡n c≤p }
          }
      )
      (λ { (produce m) x
             → hoare λ { {st} (refl , c<p) enEv
                         → let l = length (produced st ++ [ m ]) ∸ 1
                               c = length (consumed st)
                           in inj₂ (inj₂ ( l ∸ c , wfr-l++ (produced st) c<p))}
         ; (consume x₁) ⊥ → ⊥-elim (⊥ tt)
         }
      )
      λ { {state} rs (refl , c<p)
          → consume (lookup (produced state) (fromℕ≤ c<p))
          , tt
          , (consEnabled c<p refl) }

  m+n<o⇒n<o : ∀ {l} w m → w + m < l → m < l
  m+n<o⇒n<o w m w+m<l rewrite sym (+-suc w m) = m+n≤o⇒n≤o w w+m<l

  mono-l++ : ∀ {n} (m : Message) l → n ≤ length l → n ≤ length (l ++ [ m ])
  mono-l++ m l n<l rewrite length-suc {m} l = ≤-step n<l

{-[Fw]l-t[Q∪Fx] : ∀ {w n}
                  → myWFR {n} w
                    l-t
                    (λ posSt → length (consumed posSt) ≡ n)
                    ∪ [∃ x ⇒ _< w ∶ myWFR {n} x ]
-}
  [Fw]l-t[Q∪Fx] : ∀ {n} w
                  → myWFR {n} w
                    l-t
                    (_≡ n) ∘ length ∘ consumed   ∪   [∃ x ⇒ _< w ∶ myWFR {n} x ]
  [Fw]l-t[Q∪Fx] 0 = viaInv λ { rs (c≡n , c<p) → inj₁ c≡n }
  [Fw]l-t[Q∪Fx] (suc w) =
    viaEvSet
      MyEventSet
      wf
      (λ { (consume m) ⊤ → hoare λ { {st} (refl , c<p) (consEnabled cons<prod x)
                           → inj₂ (w , ≤-refl , suc-+-l m (consumed st) , c<p)}
         }
      )
      (λ { (produce m) ⊥ → hoare λ { {st} (c≡n , n≤p) enEv
                                 → inj₁ (c≡n , mono-l++ m (produced st) n≤p) }
         ; (consume m) ⊥ → ⊥-elim (⊥ tt)
         }
      )
      λ { {st} rs (refl , n<p) → let c<l = m+n<o⇒n<o w (length (consumed st)) n<p
                                 in consume (lookup (produced st) (fromℕ≤ c<l))
                                  , tt
                                  , (consEnabled c<l refl) }

{-P2-l-t-Q : ∀ {n}
            → λ preSt → length (produced preSt) ≡ n
                      × length (consumed preSt) < n
              l-t
              λ posSt → length (consumed posSt) ≡ n
-}
  cons<n-l-t-cons≡n : ∀ {n}
             → (_≡ n) ∘ length ∘ produced   ∩   (_< n) ∘ length ∘ consumed
               l-t
               (_≡ n) ∘ length ∘ consumed
  cons<n-l-t-cons≡n = viaWFR myWFR [P]l-t[Q∪Fx] [Fw]l-t[Q∪Fx]

  P⊆P1∪P2 : ∀ {m n : ℕ} {ℓ} { P : Set ℓ } → P × m ≤ n → P × m ≡ n ⊎ P × m < n
  P⊆P1∪P2 {m} {n} (p , m≤n)
    with m ≟ n
  ... | yes prf = inj₁ (p , prf)
  ... | no  imp = inj₂ (p , (≤∧≢⇒< m≤n imp))

{-c≢n-l-t-c<n : ∀ {n} → ( λ preSt → length (produced preSt) ≡ n
                                  × length (consumed preSt) ≢ n )
                        l-t
                        ( λ posSt → length (produced posSt) ≡ n
                                  × length (consumed posSt) < n )
-}
  p≡n-l-t-c≤n : ∀ {n}
                → (_≡ n) ∘ length ∘ produced
                  l-t
                  (_≡ n) ∘ length ∘ produced   ∩  (_≤ n) ∘ length ∘ consumed
  p≡n-l-t-c≤n = viaInv (λ { {st} rs refl → refl , (inv-cons≤prod rs) })

  progressOnLength : ∀ n
                     → (_≡ n) ∘ length ∘ produced
                       l-t
                       (_≡ n) ∘ length ∘ consumed
  progressOnLength n =
    viaTrans
      p≡n-l-t-c≤n
      (viaDisj
         (λ { {st} (p≡n , c≤n) → P⊆P1∪P2 (p≡n , c≤n) })
         (viaInv (λ { {st} rs (_ , c≡n) → c≡n }))
         cons<n-l-t-cons≡n )

  lookup-++ : ∀ {n} {m : Message} l₁ l₂
                 → (finl₁ : n < length l₁) → (finl₂ : n < length l₂)
                 → (fin++ : n < length (l₂ ++ [ m ]))
                 → lookup l₁ (fromℕ≤ finl₁) ≡ lookup l₂ (fromℕ≤ finl₂)
                 → lookup l₁ (fromℕ≤ finl₁) ≡ lookup (l₂ ++ [ m ]) (fromℕ≤ fin++)
  lookup-++ {zero}  (x ∷ l₁) (x₁ ∷ l₂) finl₁ finl₂ fin++ ll₁≡ll₂ = ll₁≡ll₂
  lookup-++ {suc n} (x ∷ l₁) (x₁ ∷ l₂) finl₁ finl₂ fin++ ll₁≡ll₂
    = lookup-++ l₁ l₂ (≤-pred finl₁) (≤-pred finl₂) (≤-pred fin++) ll₁≡ll₂

  n≤l++⇒n≤l⊎n≡l : ∀ {m : Message} {n} l
                  → n ≤ length (l ++ [ m ])
                  → n ≤ length l ⊎ n ≡ 1 + length l
  n≤l++⇒n≤l⊎n≡l {m} l n≤l++ rewrite length-suc {m} l
    with m≤n⇒m≡n⊎m<n n≤l++
  ... | inj₁ 1+n≤1+l = inj₁ (≤-pred 1+n≤1+l)
  ... | inj₂ n≡1+l   = inj₂ n≡1+l

  lookup-length : ∀ {m : Message} l
                     → (prf : length l < length (l ++ [ m ]))
                     → lookup (l ++ [ m ]) (fromℕ≤ prf) ≡ m
  lookup-length [] prf = refl
  lookup-length (x ∷ l) prf = lookup-length l (≤-pred prf)

  <-refl-l : ∀ {m : Message} l → length l < length (l ++ [ m ])
  <-refl-l [] = s≤s z≤n
  <-refl-l (x ∷ l) = s≤s (<-refl-l l)

  lookup-c≡lookup-p : ∀ {n}
                    → Invariant
                        MyStateMachine
                        λ st → (prfC : n < length (consumed st))
                             → (prfP : n < length (produced st))
                             → lookup (consumed st) (fromℕ≤ prfC)
                             ≡ lookup (produced st) (fromℕ≤ prfP)
  lookup-c≡lookup-p {n} (init (refl , refl)) () ()
  lookup-c≡lookup-p {n} (step {st} {produce x} rs enEv) prfC prfP
    = let c<p = <-transˡ prfC (inv-cons≤prod rs)
      in lookup-++
           (consumed st) (produced st)
           prfC c<p prfP
           (lookup-c≡lookup-p rs prfC c<p)
  lookup-c≡lookup-p {n} (step {st} {consume m} rs enEv) prfC prfP
    with enEv
  ... | consEnabled cons<prod m≡lookup
      with n≤l++⇒n≤l⊎n≡l (consumed st) prfC
  ... | inj₁ n<c  = sym (lookup-++
                          (produced st) (consumed st)
                          prfP n<c prfC
                          (sym (lookup-c≡lookup-p rs n<c prfP)))
  ... | inj₂ refl = trans
                      (lookup-length (consumed st) (<-refl-l (consumed st)))
                      m≡lookup

  -- TODO : Generalize all functions about lists
  take-n-l++ : ∀ {n} (l₁ l₂ : List Message)
               → n ≤ length l₁
               → take n l₁ ≡ take n (l₁ ++ l₂)
  take-n-l++ {zero}  l₁ l₂ n≤l₁ = refl
  take-n-l++ {suc n} (x ∷ l₁) l₂ n≤l₁
    rewrite take-n-l++ l₁ l₂ (≤-pred n≤l₁) = refl

  l₁≡l₂ : ∀ {m₁ m₂ : Message} {l₁ l₂}
          → m₁ ∷ l₁ ≡ m₂ ∷ l₂
          → m₁ ≡ m₂ × l₁ ≡ l₂
  l₁≡l₂ refl = refl , refl

  m₁≡m∧l₁≡l₂₂⇒l≡l : ∀ {m₁ m₂ : Message} {l₁ l₂}
                    → m₁ ≡ m₂
                    → l₁ ≡ l₂
                    → m₁ ∷ l₁ ≡ m₂ ∷ l₂
  m₁≡m∧l₁≡l₂₂⇒l≡l refl refl = refl

  taken×lookup : ∀ {m : Message} l₁ l₂
                 → (prf : length l₁ < length l₂)
                 → l₁ ≡ take (length l₁) l₂
                 → m ≡ lookup l₂ (fromℕ≤ prf)
                 → l₁ ++ [ m ] ≡ take (suc (length l₁)) l₂
  taken×lookup [] (x₂ ∷ l₂) l₁<l₂ l₁≡take refl = refl
  taken×lookup (x₂ ∷ l₁) (x₃ ∷ l₂) l₁<l₂ l₁≡tk m≡lkp
    rewrite taken×lookup l₁ l₂ (≤-pred l₁<l₂) (proj₂ (l₁≡l₂ l₁≡tk)) m≡lkp
      = m₁≡m∧l₁≡l₂₂⇒l≡l (proj₁ (l₁≡l₂ l₁≡tk)) refl

  take1+l≡takel++m : ∀ {m : Message} {n} l
                         → n ≡ 1 + length l
                         → take n (l ++ [ m ]) ≡ l ++ [ m ]
  take1+l≡takel++m [] refl = refl
  take1+l≡takel++m {m} (x₁ ∷ l) refl
    rewrite take1+l≡takel++m {m} l refl = refl

  take-length≡l : ∀ (l : List Message) → take (length l) l ≡ l
  take-length≡l [] = refl
  take-length≡l (x ∷ l) rewrite take-length≡l l = refl

  [c]-prefix-[p] : ∀ {n}
                   → Invariant
                       MyStateMachine
                       λ st → n ≤ length (consumed st)
                            → take n (consumed st) ≡ take n (produced st)
  [c]-prefix-[p] {n} (init (refl , refl)) n<lc = refl
  [c]-prefix-[p] {n} (step {st} {produce m} rs enEv) n<lc
    = trans
        ([c]-prefix-[p] rs n<lc)
        (take-n-l++ (produced st) ([ m ]) (≤-trans n<lc (inv-cons≤prod rs)))
  [c]-prefix-[p] {n} (step {st} {consume m} rs enEv) n≤c++
     with enEv
  ... | consEnabled cons<prod m≡lookup
       with n≤l++⇒n≤l⊎n≡l (consumed st) n≤c++
  ... | inj₁ n≤c   = trans
                       (sym (take-n-l++ (consumed st) [ m ] n≤c))
                       ([c]-prefix-[p] rs n≤c)
  ... | inj₂ refl =  let tc≡tp = [c]-prefix-[p] rs ≤-refl
                     in trans
                          (take1+l≡takel++m (consumed st) refl)
                          (taken×lookup
                            (consumed st) (produced st) cons<prod
                            (trans (sym (take-length≡l (consumed st))) tc≡tp)
                            m≡lookup)

  take-presrv-prfix : ∀ {l₁ l₂} {m : Message}
                          → take (length l₂) l₁ ≡ l₂
                          → take (length l₂) (l₁ ++ [ m ]) ≡ l₂
  take-presrv-prfix {[]} {[]} {m} tl₁≡l₂ = refl
  take-presrv-prfix {m₁ ∷ l₁} {[]} {m} tl₁≡l₂ = refl
  take-presrv-prfix {m₁ ∷ l₁} {m₂ ∷ l₂} {m} tl₁≡l₂
    with l₁≡l₂ tl₁≡l₂
  ... | m₁≡m₂ , tll₁≡l₂
      with take-presrv-prfix {m = m} tll₁≡l₂
  ... | tl₁+m≡l₂ = m₁≡m∧l₁≡l₂₂⇒l≡l m₁≡m₂ tl₁+m≡l₂

  stable-produced : ∀ {msgs}
                    → Stable MyStateMachine
                             λ st → take (length msgs) (produced st) ≡ msgs
  stable-produced {msgs} {st} {produce m}  enEv tkp≡m = take-presrv-prfix tkp≡m
  stable-produced {msgs} {st} {consume x₁} enEv tkp≡m = tkp≡m

  aux : ∀ {n m} {l₁ l₂ : List Message}
        → n ≡ m
        → take n l₁ ≡ take n l₂
        → take n l₁ ≡ take m l₂
  aux refl prf = prf

  lc≡lm-l-t-c≡m : ∀ {msgs}
        → (λ preSt → length (consumed preSt) ≡ length msgs
             × take (length msgs) (produced preSt) ≡ msgs )
          l-t
           λ posSt → consumed posSt ≡ msgs
  lc≡lm-l-t-c≡m {msgs} =
    viaInv
      (λ { {st} rs (lc≡lm , tp≡msgs)
            → trans
                (trans
                  (sym (take-length≡l (consumed st)))
                  (aux lc≡lm ([c]-prefix-[p] rs ≤-refl)))
                tp≡msgs })

  msgsPrefixProd : ∀ {msgs} → (λ st → produced st ≡ msgs)
                              l-t
                              (λ st →  length (produced st) ≡ length msgs
                                     × take (length msgs) (produced st) ≡ msgs)
  msgsPrefixProd = viaInv (λ { {st} rs refl → refl , take-length≡l (produced st) })

  progress : ∀ {msgs} → (_≡ msgs) ∘ produced l-t (_≡ msgs) ∘ consumed
  progress {msgs} = viaStable
                      stable-produced
                      msgsPrefixProd
                      (progressOnLength (length msgs))
                      lc≡lm-l-t-c≡m

{-
  Another way of proving without the viaStable rule

  progressOnLength' : ∀ {msgs} → (λ st₁ → length (produced st₁) ≡ length msgs
                                     × take (length msgs) (produced st₁) ≡ msgs)
                                 l-t
                                 λ st₂ → length (consumed st₂) ≡ length msgs
                                        × take (length msgs) (produced st₂) ≡ msgs

  progress : ∀ {msgs} → (_≡ msgs) ∘ produced l-t (_≡ msgs) ∘ consumed
  progress {msgs} =
      viaTrans
      (viaInv inv-prodPrefix)
      (viaTrans
        progressOnLength'
        lc≡lm-l-t-c≡m)
-}


module Category.Universality where

open import Level
open import Category.Core
open import Category.Comma
open import Category.Instance

-- something is universal from c to S when it's an initial object in c / S
universal : {𝒸₀ ℓ₀ 𝒸₁ ℓ₁ : Level}
    → {C : Category 𝒸₀ ℓ₀} {D : Category 𝒸₁ ℓ₁}
    → {c : Category.Object C} → {S : Functor D C}
    → (init : Category.Object (point c ↓ S))
    → Set (𝒸₁ ⊔ (ℓ₀ ⊔ 𝒸₀))
universal {c = c} {S = S} init = initial (point c ↓ S) init
    where
        initial : ∀ {𝒸 ℓ} → (C : Category 𝒸 ℓ) → Category.Object C → Set 𝒸
        initial C init = ∀ (other : Object) → init ⇒ other
            where
                open Category C

initial : ∀ {𝒸 ℓ} (C : Category 𝒸 ℓ) → Category.Object C → Set (𝒸 ⊔ (ℓ ⊔ 𝒸))
initial C c = universal {C = C} {D = C} {c} {identity C} (record
        { source = tt
        ; target = c
        ; morphism = id c
        })
        where
            open import Data.Unit
            open Category C

initial-prop : ∀ {𝒸 ℓ} {C : Category 𝒸 ℓ}
    → (init : Category.Object C)
    → initial C init
    → (other : Category.Object C)
    → Category._⇒_ C init other
initial-prop {C = C} obj obj-init other = Comma.morphismBetweenSources {!   !}
    where
        open Category C

-- prop1 : {!   !}
-- prop1 = {! initial  !}