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

open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Semirings.Definition
open import Sets.CantorBijection.Order
open import Orders.Total.Definition
open import Orders.WellFounded.Induction

module Sets.CantorBijection.Proofs where

open Sets.CantorBijection.Order using (order ; totalOrder ; orderWellfounded)

cantorInverseLemma : (ℕ && ℕ) → (ℕ && ℕ)
cantorInverseLemma (0 ,, s) = (succ s) ,, 0
cantorInverseLemma (succ r ,, 0) = r ,, 1
cantorInverseLemma (succ r ,, succ s) = (r ,, succ (succ s))

cantorInverse : ℕ → (ℕ && ℕ)
cantorInverse zero = (0 ,, 0)
cantorInverse (succ z) = cantorInverseLemma (cantorInverse z)

cantorInverseLemmaInjective : (a b : ℕ && ℕ) → cantorInverseLemma a ≡ cantorInverseLemma b → a ≡ b
cantorInverseLemmaInjective (zero ,, b) (zero ,, .b) refl = refl
cantorInverseLemmaInjective (zero ,, b) (succ c ,, zero) ()
cantorInverseLemmaInjective (zero ,, b) (succ c ,, succ d) ()
cantorInverseLemmaInjective (succ a ,, zero) (zero ,, d) ()
cantorInverseLemmaInjective (succ a ,, succ b) (zero ,, d) ()
cantorInverseLemmaInjective (succ a ,, zero) (succ .a ,, zero) refl = refl
cantorInverseLemmaInjective (succ a ,, succ b) (succ .a ,, succ .b) refl = refl

cantorInverseLemmaSurjective : (a : ℕ && ℕ) → (a ≡ (0 ,, 0)) || (Sg (ℕ && ℕ) (λ b → cantorInverseLemma b ≡ a))
cantorInverseLemmaSurjective (zero ,, zero) = inl refl
cantorInverseLemmaSurjective (zero ,, succ zero) = inr ((1 ,, 0) , refl)
cantorInverseLemmaSurjective (zero ,, succ (succ b)) = inr ((1 ,, succ b) , refl)
cantorInverseLemmaSurjective (succ a ,, zero) = inr ((0 ,, a) , refl)
cantorInverseLemmaSurjective (succ a ,, succ zero) = inr ((succ (succ a) ,, 0) , refl)
cantorInverseLemmaSurjective (succ a ,, succ (succ b)) = inr ((succ (succ a) ,, succ b) , refl)

cantorInverseLemmaNotZero : (a : ℕ && ℕ) → (cantorInverseLemma a ≡ (0 ,, 0)) → False
cantorInverseLemmaNotZero (succ a ,, zero) ()
cantorInverseLemmaNotZero (succ a ,, succ b) ()

cantorInverseLemmaIncreases : (x : ℕ && ℕ) → order x (cantorInverseLemma x)
cantorInverseLemmaIncreases (zero ,, b) = inl (identityOfIndiscernablesRight _<N_ (a<SuccA b) (applyEquality succ (equalityCommutative (Semiring.sumZeroRight ℕSemiring b))))
cantorInverseLemmaIncreases (succ a ,, zero) = inr (transitivity (applyEquality succ (Semiring.sumZeroRight ℕSemiring a)) (Semiring.commutative ℕSemiring 1 a) ,, (le 0 refl))
cantorInverseLemmaIncreases (succ a ,, succ b) = inr (transitivity (applyEquality succ (Semiring.commutative ℕSemiring a (succ b))) (Semiring.commutative ℕSemiring (succ (succ b)) a) ,, (le 0 refl))

cantorInverseOrderPreserving : (x y : ℕ) → (x <N y) → order (cantorInverse x) (cantorInverse y)
cantorInverseOrderPreserving zero (succ y) x<y with TotalOrder.totality totalOrder (0 ,, 0) (cantorInverseLemma (cantorInverse y))
cantorInverseOrderPreserving zero (succ y) x<y | inl (inl bl) = bl
cantorInverseOrderPreserving zero (succ y) x<y | inl (inr bl) = exFalso (leastElement bl)
cantorInverseOrderPreserving zero (succ y) x<y | inr x = exFalso (leastElement {cantorInverse y} (identityOfIndiscernablesRight order (cantorInverseLemmaIncreases (cantorInverse y)) (equalityCommutative x)))
cantorInverseOrderPreserving (succ x) (succ y) x<y with cantorInverseOrderPreserving x y (canRemoveSuccFrom<N x<y)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr with cantorInverse x
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | x1 ,, x2 with cantorInverse y
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | zero ,, succ y2 = inl (succPreservesInequality (succIsPositive (y2 +N 0)))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | succ y1 ,, zero with TotalOrder.totality ℕTotalOrder 1 (y1 +N 1)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | succ y1 ,, zero | inl (inl pr1) = inl pr1
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | succ y1 ,, zero | inl (inr pr1) rewrite Semiring.commutative ℕSemiring y1 1 = exFalso (zeroNeverGreater (canRemoveSuccFrom<N pr1))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | succ y1 ,, zero | inr pr1 = inr (pr1 ,, le 0 refl)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, zero | succ y1 ,, succ y2 rewrite Semiring.commutative ℕSemiring y1 (succ (succ y2)) = inl (succPreservesInequality (succIsPositive (y2 +N y1)))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, zero | zero ,, succ y2 rewrite Semiring.commutative ℕSemiring x1 1 | Semiring.sumZeroRight ℕSemiring y2 | Semiring.sumZeroRight ℕSemiring x1 = inl (TotalOrder.<Transitive ℕTotalOrder pr (a<SuccA _))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, zero | succ y1 ,, zero rewrite Semiring.commutative ℕSemiring x1 1 | Semiring.commutative ℕSemiring y1 1 | Semiring.sumZeroRight ℕSemiring x1 | Semiring.sumZeroRight ℕSemiring y1 = inl pr
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, zero | succ y1 ,, succ y2 rewrite Semiring.commutative ℕSemiring x1 1 | Semiring.sumZeroRight ℕSemiring x1 = inl (identityOfIndiscernablesRight _<N_ pr (transitivity (applyEquality succ (Semiring.commutative ℕSemiring y1 (succ y2))) (Semiring.commutative ℕSemiring (succ (succ y2)) y1)))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, succ x2 | zero ,, succ y2 rewrite Semiring.sumZeroRight ℕSemiring x2 | Semiring.sumZeroRight ℕSemiring y2 = inl (succPreservesInequality pr)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, succ x2 | succ y1 ,, zero rewrite Semiring.commutative ℕSemiring y1 1 | Semiring.sumZeroRight ℕSemiring y1 | Semiring.sumZeroRight ℕSemiring x2 = ans
  where
    ans : (succ (succ x2) <N succ y1) || (succ (succ x2) ≡ succ y1) && (zero <N 1)
    ans with TotalOrder.totality ℕTotalOrder (succ (succ x2)) (succ y1)
    ans | inl (inl x) = inl x
    ans | inl (inr x) = exFalso (noIntegersBetweenXAndSuccX (succ x2) pr x)
    ans | inr x = inr (x ,, le zero refl)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | zero ,, succ x2 | succ y1 ,, succ y2 rewrite Semiring.commutative ℕSemiring y1 1 | Semiring.sumZeroRight ℕSemiring y1 | Semiring.sumZeroRight ℕSemiring x2 = ans
  where
    ans : (succ (succ x2) <N y1 +N succ (succ y2)) || (succ (succ x2) ≡ y1 +N succ (succ y2)) && (zero <N succ (succ y2))
    ans with TotalOrder.totality ℕTotalOrder (succ (succ x2)) (y1 +N succ (succ y2))
    ans | inl (inl x) = inl x
    ans | inl (inr x) = exFalso (noIntegersBetweenXAndSuccX (succ x2) pr (identityOfIndiscernablesLeft _<N_ x (transitivity (Semiring.commutative ℕSemiring y1 (succ (succ y2))) (applyEquality succ (Semiring.commutative ℕSemiring (succ y2) y1)))))
    ans | inr x = inr (x ,, succIsPositive (succ y2))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, succ x2 | zero ,, succ y2 rewrite Semiring.sumZeroRight ℕSemiring y2 = inl (TotalOrder.<Transitive ℕTotalOrder (identityOfIndiscernablesLeft _<N_ pr (transitivity (applyEquality succ (Semiring.commutative ℕSemiring x1 (succ x2))) (Semiring.commutative ℕSemiring (succ (succ x2)) x1))) (a<SuccA (succ y2)))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, succ x2 | succ y1 ,, zero rewrite Semiring.commutative ℕSemiring y1 1 | Semiring.sumZeroRight ℕSemiring y1 | Semiring.commutative ℕSemiring x1 (succ x2) | Semiring.commutative ℕSemiring x1 (succ (succ x2)) = inl pr
cantorInverseOrderPreserving (succ x) (succ y) x<y | inl pr | succ x1 ,, succ x2 | succ y1 ,, succ y2 rewrite Semiring.commutative ℕSemiring x1 (succ x2) | Semiring.commutative ℕSemiring y1 (succ y2) | Semiring.commutative ℕSemiring x1 (succ (succ x2)) | Semiring.commutative ℕSemiring y1 (succ (succ y2)) = inl pr
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) with cantorInverse x
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) | x1 ,, x2 with cantorInverse y
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, ()) | zero ,, zero | zero ,, zero
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (() ,, snd) | zero ,, zero | zero ,, succ y2
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (refl ,, snd) | zero ,, succ .y2 | zero ,, succ y2 = exFalso (TotalOrder.irreflexive ℕTotalOrder snd)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (refl ,, snd) | zero ,, succ .(y1 +N succ y2) | succ y1 ,, succ y2 = exFalso (TotalOrder.irreflexive ℕTotalOrder (TotalOrder.<Transitive ℕTotalOrder snd (identityOfIndiscernablesRight _<N_ (addingIncreases (succ y2) y1) (Semiring.commutative ℕSemiring (succ y2) (succ y1)))))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) | succ x1 ,, zero | zero ,, succ y2 rewrite Semiring.sumZeroRight ℕSemiring x1 | succInjective fst | Semiring.commutative ℕSemiring y2 1 | Semiring.sumZeroRight ℕSemiring y2 = inl (le zero refl)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) | succ x1 ,, zero | succ y1 ,, succ y2 rewrite Semiring.commutative ℕSemiring x1 1 | Semiring.sumZeroRight ℕSemiring x1 = inr (transitivity fst (transitivity (applyEquality succ (Semiring.commutative ℕSemiring y1 (succ y2))) (Semiring.commutative ℕSemiring (succ (succ y2)) y1)) ,, succPreservesInequality snd)
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) | succ x1 ,, succ x2 | zero ,, succ y2 rewrite Semiring.sumZeroRight ℕSemiring y2 | Semiring.commutative ℕSemiring x1 (succ x2) | Semiring.commutative ℕSemiring (succ (succ x2)) x1 | fst = inl (succPreservesInequality (le zero refl))
cantorInverseOrderPreserving (succ x) (succ y) x<y | inr (fst ,, snd) | succ x1 ,, succ x2 | succ y1 ,, succ y2 = inr (transitivity (transitivity (Semiring.commutative ℕSemiring x1 (succ (succ x2))) (applyEquality succ (Semiring.commutative ℕSemiring (succ x2) x1))) (transitivity fst (transitivity (applyEquality succ (Semiring.commutative ℕSemiring y1 (succ y2))) (Semiring.commutative ℕSemiring (succ (succ y2)) y1))) ,, succPreservesInequality snd)

cantorInverseInjective : (a b : ℕ) → cantorInverse a ≡ cantorInverse b → a ≡ b
cantorInverseInjective zero zero pr = refl
cantorInverseInjective zero (succ b) pr = exFalso (cantorInverseLemmaNotZero _ (equalityCommutative pr))
cantorInverseInjective (succ a) zero pr = exFalso (cantorInverseLemmaNotZero _ pr)
cantorInverseInjective (succ a) (succ b) pr = applyEquality succ (cantorInverseInjective a b (cantorInverseLemmaInjective (cantorInverse a) (cantorInverse b) pr))

-- Some unnecessary things on the way to the final proof
cantorInverseDiscrete : (a : ℕ) → (c : ℕ && ℕ) → order (cantorInverse a) c → order c (cantorInverse (succ a)) → False
cantorInverseDiscrete zero (zero ,, zero) (inl ()) c<sa
cantorInverseDiscrete zero (zero ,, zero) (inr ()) c<sa
cantorInverseDiscrete zero (zero ,, succ c) a<c (inl x) = zeroNeverGreater (canRemoveSuccFrom<N x)
cantorInverseDiscrete zero (succ b ,, zero) a<c (inl x) = zeroNeverGreater (canRemoveSuccFrom<N x)
cantorInverseDiscrete zero (succ b ,, succ c) a<c (inl x) = zeroNeverGreater (canRemoveSuccFrom<N x)
cantorInverseDiscrete (succ a) (b ,, c) a<c c<sa with cantorInverse a
cantorInverseDiscrete (succ a) (zero ,, zero) (inl ()) (inl x) | zero ,, zero
cantorInverseDiscrete (succ a) (zero ,, zero) (inr ()) (inl x) | zero ,, zero
cantorInverseDiscrete (succ a) (zero ,, succ zero) a<c (inl x) | zero ,, zero = TotalOrder.irreflexive ℕTotalOrder x
cantorInverseDiscrete (succ a) (zero ,, succ (succ c)) a<c (inl x) | zero ,, zero = zeroNeverGreater (canRemoveSuccFrom<N x)
cantorInverseDiscrete (succ a) (succ b ,, c) a<c (inl x) | zero ,, zero = zeroNeverGreater (canRemoveSuccFrom<N x)
cantorInverseDiscrete (succ a) (b ,, zero) (inl x) (inr (fst ,, snd)) | zero ,, zero = TotalOrder.irreflexive ℕTotalOrder (identityOfIndiscernablesRight _<N_ x fst)
cantorInverseDiscrete (succ a) (b ,, succ c) a<c (inr (fst ,, snd)) | zero ,, zero = zeroNeverGreater (canRemoveSuccFrom<N snd)
cantorInverseDiscrete (succ a) (b ,, c) (inl y) (inl x) | zero ,, succ snd rewrite Semiring.commutative ℕSemiring snd 1 | Semiring.sumZeroRight ℕSemiring snd = TotalOrder.irreflexive ℕTotalOrder (TotalOrder.<Transitive ℕTotalOrder x y)
cantorInverseDiscrete (succ a) (b ,, succ c) (inr (fst ,, _)) (inl x) | zero ,, succ snd rewrite Semiring.commutative ℕSemiring snd 1 | Semiring.sumZeroRight ℕSemiring snd | fst = TotalOrder.irreflexive ℕTotalOrder x
cantorInverseDiscrete (succ a) (b ,, zero) (inl x) (inr (fst ,, snd₁)) | zero ,, succ snd rewrite fst | Semiring.commutative ℕSemiring snd 1 | Semiring.sumZeroRight ℕSemiring snd = TotalOrder.irreflexive ℕTotalOrder x
cantorInverseDiscrete (succ a) (b ,, succ c) (inl x) (inr (fst ,, snd1)) | zero ,, succ snd = zeroNeverGreater (canRemoveSuccFrom<N snd1)
cantorInverseDiscrete (succ a) (b ,, succ zero) (inr (fst ,, _)) (inr (fst₁ ,, bad)) | zero ,, succ snd = TotalOrder.irreflexive ℕTotalOrder bad
cantorInverseDiscrete (succ a) (b ,, succ (succ c)) (inr (fst ,, _)) (inr (fst₁ ,, bad)) | zero ,, succ snd = zeroNeverGreater (canRemoveSuccFrom<N bad)
cantorInverseDiscrete (succ a) (b ,, c) (inl x1) (inl x) | succ zero ,, zero = noIntegersBetweenXAndSuccX 1 x1 x
cantorInverseDiscrete (succ a) (zero ,, succ zero) (inr (fst ,, snd)) (inl x) | succ zero ,, zero = TotalOrder.irreflexive ℕTotalOrder snd
cantorInverseDiscrete (succ a) (succ b ,, succ c) (inr (fst ,, snd)) (inl x) | succ zero ,, zero rewrite Semiring.commutative ℕSemiring b (succ c) = bad fst
  where
    bad : {a : ℕ} → 1 ≡ succ (succ a) → False
    bad ()
cantorInverseDiscrete (succ a) (b ,, c) (inl y) (inl x) | succ (succ fst) ,, zero rewrite Semiring.commutative ℕSemiring fst 2 | Semiring.commutative ℕSemiring fst 1 = TotalOrder.irreflexive ℕTotalOrder (TotalOrder.<Transitive ℕTotalOrder y x)
cantorInverseDiscrete (succ a) (b ,, c) (inr (bad ,, _)) (inl x) | succ (succ fst) ,, zero rewrite Semiring.commutative ℕSemiring fst 2 | Semiring.commutative ℕSemiring fst 1 = TotalOrder.irreflexive ℕTotalOrder (identityOfIndiscernablesRight _<N_ x bad)
cantorInverseDiscrete (succ a) (b ,, c) (inl x) (inr (bad ,, snd)) | succ (succ fst) ,, zero rewrite Semiring.commutative ℕSemiring fst 2 | Semiring.commutative ℕSemiring fst 1 = TotalOrder.irreflexive ℕTotalOrder (identityOfIndiscernablesRight _<N_ x bad)
cantorInverseDiscrete (succ a) (b ,, c) (inr (_ ,, bad)) (inr (fst₁ ,, snd)) | succ (succ fst) ,, zero rewrite Semiring.commutative ℕSemiring fst 2 | Semiring.commutative ℕSemiring fst 1 = noIntegersBetweenXAndSuccX 1 bad snd
cantorInverseDiscrete (succ a) (b ,, c) (inl x) (inl y) | succ zero ,, succ snd rewrite Semiring.sumZeroRight ℕSemiring snd = noIntegersBetweenXAndSuccX (succ (succ snd)) x y
cantorInverseDiscrete (succ a) (zero ,, c) (inr (fst ,, snd1)) (inl y) | succ zero ,, succ snd rewrite Semiring.sumZeroRight ℕSemiring snd = noIntegersBetweenXAndSuccX (succ (succ snd)) snd1 y
cantorInverseDiscrete (succ a) (succ b ,, c) (inr (fst ,, bad)) (inl y) | succ zero ,, succ snd rewrite Semiring.sumZeroRight ℕSemiring snd | fst = TotalOrder.irreflexive ℕTotalOrder (TotalOrder.<Transitive ℕTotalOrder bad (identityOfIndiscernablesRight _<N_ (addingIncreases c b) (Semiring.commutative ℕSemiring c (succ b))))
cantorInverseDiscrete (succ a) (b ,, c) (inl x) (inl y) | succ (succ fst) ,, succ snd rewrite Semiring.commutative ℕSemiring fst (succ (succ (succ snd))) | Semiring.commutative ℕSemiring (succ (succ snd)) fst = TotalOrder.irreflexive ℕTotalOrder (TotalOrder.<Transitive ℕTotalOrder y x)
cantorInverseDiscrete (succ a) (b ,, c) (inl x) (inr (y ,, z)) | succ (succ fst) ,, succ snd rewrite y | Semiring.commutative ℕSemiring fst (succ (succ (succ snd))) | Semiring.commutative ℕSemiring (succ (succ snd)) fst = TotalOrder.irreflexive ℕTotalOrder x
cantorInverseDiscrete (succ a) (b ,, c) (inr (x ,, y)) (inl z) | succ (succ fst) ,, succ snd rewrite equalityCommutative x | Semiring.commutative ℕSemiring fst (succ (succ (succ snd))) | Semiring.commutative ℕSemiring (succ (succ snd)) fst = TotalOrder.irreflexive ℕTotalOrder z
cantorInverseDiscrete (succ a) (b ,, c) (inr (x ,, y)) (inr (m ,, n)) | succ (succ fst) ,, succ snd = noIntegersBetweenXAndSuccX (succ (succ snd)) y n

boundedInversesExist : (a : ℕ && ℕ) (s : ℕ) → order a (cantorInverse s) → Sg ℕ (λ i → cantorInverse i ≡ a)
boundedInversesExist a zero a<s = exFalso (leastElement a<s)
boundedInversesExist a (succ s) a<s with TotalOrder.totality totalOrder a (cantorInverse s)
boundedInversesExist a (succ s) _ | inl (inl a<s) = boundedInversesExist a s a<s
boundedInversesExist a (succ s) a<s | inl (inr s<a) = exFalso (cantorInverseDiscrete s a s<a a<s)
boundedInversesExist a (succ s) a<s | inr a=s = s , equalityCommutative a=s

cantorInverseSurjective : (x : ℕ && ℕ) → Sg ℕ (λ i → (cantorInverse i) ≡ x)
cantorInverseSurjective = rec orderWellfounded (λ z → Sg ℕ (λ z₁ → (cantorInverse z₁) ≡ z)) go
  where
    go : (a : ℕ && ℕ) → (pr : (x : ℕ && ℕ) (x₁ : order x a) → Sg ℕ (λ z → (cantorInverse z) ≡ x)) → Sg ℕ (λ i → (cantorInverse i) ≡ a)
    go a pr with cantorInverseLemmaSurjective a
    go .(0 ,, 0) pr | inl refl = 0 , refl
    go a ind | inr (decr , proof) with ind decr (identityOfIndiscernablesRight order (cantorInverseLemmaIncreases decr) proof)
    go a ind | inr (decr , proof) | boundForDecr , boundIsBound = succ boundForDecr , transitivity (applyEquality cantorInverseLemma boundIsBound) proof


------------------------------------------------------------------------
-- The Agda standard library
--
-- Lexicographic products of binary relations
------------------------------------------------------------------------

-- The definition of lexicographic product used here is suitable if
-- the left-hand relation is a (non-strict) partial order.

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

module Data.Product.Relation.Binary.Lex.NonStrict where

open import Data.Product using (_×_; _,_; proj₁; proj₂)
open import Data.Sum using (inj₁; inj₂)
open import Relation.Binary
open import Relation.Binary.Consequences
import Relation.Binary.Construct.NonStrictToStrict as Conv
open import Data.Product.Relation.Binary.Pointwise.NonDependent as Pointwise
  using (Pointwise)
import Data.Product.Relation.Binary.Lex.Strict as Strict

module _ {a₁ a₂ ℓ₁ ℓ₂} {A₁ : Set a₁} {A₂ : Set a₂} where

------------------------------------------------------------------------
-- A lexicographic ordering over products

  ×-Lex : (_≈₁_ _≤₁_ : Rel A₁ ℓ₁) (_≤₂_ : Rel A₂ ℓ₂) → Rel (A₁ × A₂) _
  ×-Lex _≈₁_ _≤₁_ _≤₂_ = Strict.×-Lex _≈₁_ (Conv._<_ _≈₁_ _≤₁_) _≤₂_

------------------------------------------------------------------------
-- Some properties which are preserved by ×-Lex (under certain
-- assumptions).

  ×-reflexive : ∀ _≈₁_ _≤₁_ {_≈₂_} _≤₂_ →
                _≈₂_ ⇒ _≤₂_ →
                (Pointwise _≈₁_ _≈₂_) ⇒ (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-reflexive _≈₁_ _≤₁_ _≤₂_ refl₂ =
    Strict.×-reflexive _≈₁_ (Conv._<_ _≈₁_ _≤₁_) _≤₂_ refl₂

  ×-transitive : ∀ {_≈₁_ _≤₁_} → IsPartialOrder _≈₁_ _≤₁_ →
                 ∀ {_≤₂_} → Transitive _≤₂_ →
                 Transitive (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-transitive {_≈₁_} {_≤₁_} po₁ {_≤₂_} trans₂ =
    Strict.×-transitive
      {_<₁_ = Conv._<_ _≈₁_ _≤₁_}
      isEquivalence (Conv.<-resp-≈ _ _ isEquivalence ≤-resp-≈)
      (Conv.<-trans _ _ po₁)
      {_≤₂_} trans₂
    where open IsPartialOrder po₁

  ×-antisymmetric :
    ∀ {_≈₁_ _≤₁_} → IsPartialOrder _≈₁_ _≤₁_ →
    ∀ {_≈₂_ _≤₂_} → Antisymmetric _≈₂_ _≤₂_ →
    Antisymmetric (Pointwise _≈₁_ _≈₂_) (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-antisymmetric {_≈₁_} {_≤₁_}
                  po₁ {_≤₂_ = _≤₂_} antisym₂ =
    Strict.×-antisymmetric {_<₁_ = Conv._<_ _≈₁_ _≤₁_}
                           ≈-sym₁ irrefl₁ asym₁
                           {_≤₂_ = _≤₂_} antisym₂
    where
    open IsPartialOrder po₁
    open Eq renaming (refl to ≈-refl₁; sym to ≈-sym₁)

    irrefl₁ : Irreflexive _≈₁_ (Conv._<_ _≈₁_ _≤₁_)
    irrefl₁ = Conv.<-irrefl _≈₁_ _≤₁_

    asym₁ : Asymmetric (Conv._<_ _≈₁_ _≤₁_)
    asym₁ = trans∧irr⟶asym {_≈_ = _≈₁_}
                           ≈-refl₁ (Conv.<-trans _ _ po₁) irrefl₁

  ×-respects₂ :
    ∀ {_≈₁_ _≤₁_} → IsEquivalence _≈₁_ → _≤₁_ Respects₂ _≈₁_ →
    ∀ {_≈₂_ _≤₂_ : Rel A₂ ℓ₂} → _≤₂_ Respects₂ _≈₂_ →
    (×-Lex _≈₁_ _≤₁_ _≤₂_) Respects₂ (Pointwise _≈₁_ _≈₂_)
  ×-respects₂ eq₁ resp₁ resp₂ =
    Strict.×-respects₂ eq₁ (Conv.<-resp-≈ _ _ eq₁ resp₁) resp₂

  ×-decidable : ∀ {_≈₁_ _≤₁_} → Decidable _≈₁_ → Decidable _≤₁_ →
                ∀ {_≤₂_} → Decidable _≤₂_ →
                Decidable (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-decidable dec-≈₁ dec-≤₁ dec-≤₂ =
    Strict.×-decidable dec-≈₁ (Conv.<-decidable _ _ dec-≈₁ dec-≤₁)
                       dec-≤₂

  ×-total : ∀ {_≈₁_ _≤₁_} → Symmetric _≈₁_ → Decidable _≈₁_ →
                            Antisymmetric _≈₁_ _≤₁_ → Total _≤₁_ →
            ∀ {_≤₂_} → Total _≤₂_ →
            Total (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-total {_≈₁_} {_≤₁_} sym₁ dec₁ antisym₁ total₁ {_≤₂_} total₂ =
    total
    where
    tri₁ : Trichotomous _≈₁_ (Conv._<_ _≈₁_ _≤₁_)
    tri₁ = Conv.<-trichotomous _ _ sym₁ dec₁ antisym₁ total₁

    total : Total (×-Lex _≈₁_ _≤₁_ _≤₂_)
    total x y with tri₁ (proj₁ x) (proj₁ y)
    ... | tri< x₁<y₁ x₁≉y₁ x₁≯y₁ = inj₁ (inj₁ x₁<y₁)
    ... | tri> x₁≮y₁ x₁≉y₁ x₁>y₁ = inj₂ (inj₁ x₁>y₁)
    ... | tri≈ x₁≮y₁ x₁≈y₁ x₁≯y₁ with total₂ (proj₂ x) (proj₂ y)
    ...   | inj₁ x₂≤y₂ = inj₁ (inj₂ (x₁≈y₁ , x₂≤y₂))
    ...   | inj₂ x₂≥y₂ = inj₂ (inj₂ (sym₁ x₁≈y₁ , x₂≥y₂))

  -- Some collections of properties which are preserved by ×-Lex
  -- (under certain assumptions).

  ×-isPartialOrder :
    ∀ {_≈₁_ _≤₁_} → IsPartialOrder _≈₁_ _≤₁_ →
    ∀ {_≈₂_ _≤₂_} → IsPartialOrder _≈₂_ _≤₂_ →
    IsPartialOrder (Pointwise _≈₁_ _≈₂_) (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-isPartialOrder {_≈₁_} {_≤₁_} po₁
                     {_≤₂_ = _≤₂_} po₂ = record
    { isPreorder = record
        { isEquivalence = Pointwise.×-isEquivalence
                            (isEquivalence po₁)
                            (isEquivalence po₂)
        ; reflexive     = ×-reflexive _≈₁_ _≤₁_ _≤₂_ (reflexive po₂)
        ; trans         = ×-transitive po₁ {_≤₂_ = _≤₂_} (trans po₂)
        }
    ; antisym = ×-antisymmetric {_≤₁_ = _≤₁_} po₁
                                {_≤₂_ = _≤₂_} (antisym po₂)
    }
    where open IsPartialOrder

  ×-isTotalOrder :
    ∀ {_≈₁_ _≤₁_} → Decidable _≈₁_ → IsTotalOrder _≈₁_ _≤₁_ →
    ∀ {_≈₂_ _≤₂_} → IsTotalOrder _≈₂_ _≤₂_ →
    IsTotalOrder (Pointwise _≈₁_ _≈₂_) (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-isTotalOrder {_≤₁_ = _≤₁_} ≈₁-dec to₁ {_≤₂_ = _≤₂_} to₂ = record
    { isPartialOrder = ×-isPartialOrder
                         (isPartialOrder to₁) (isPartialOrder to₂)
    ; total          = ×-total {_≤₁_ = _≤₁_} (Eq.sym to₁) ≈₁-dec
                                             (antisym to₁) (total to₁)
                               {_≤₂_ = _≤₂_} (total to₂)
    }
    where open IsTotalOrder

  ×-isDecTotalOrder :
    ∀ {_≈₁_ _≤₁_} → IsDecTotalOrder _≈₁_ _≤₁_ →
    ∀ {_≈₂_ _≤₂_} → IsDecTotalOrder _≈₂_ _≤₂_ →
    IsDecTotalOrder (Pointwise _≈₁_ _≈₂_) (×-Lex _≈₁_ _≤₁_ _≤₂_)
  ×-isDecTotalOrder {_≤₁_ = _≤₁_} to₁ {_≤₂_ = _≤₂_} to₂ = record
    { isTotalOrder = ×-isTotalOrder (_≟_ to₁)
                                    (isTotalOrder to₁)
                                    (isTotalOrder to₂)
    ; _≟_          = Pointwise.×-decidable (_≟_ to₁) (_≟_ to₂)
    ; _≤?_         = ×-decidable (_≟_ to₁) (_≤?_ to₁) (_≤?_ to₂)
    }
    where open IsDecTotalOrder

------------------------------------------------------------------------
-- "Packages" can also be combined.

module _ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} where

  ×-poset : Poset ℓ₁ ℓ₂ _ → Poset ℓ₃ ℓ₄ _ → Poset _ _ _
  ×-poset p₁ p₂ = record
    { isPartialOrder = ×-isPartialOrder
                         (isPartialOrder p₁) (isPartialOrder p₂)
    } where open Poset

  ×-totalOrder : DecTotalOrder ℓ₁ ℓ₂ _ → TotalOrder ℓ₃ ℓ₄ _ →
                 TotalOrder _ _ _
  ×-totalOrder t₁ t₂ = record
    { isTotalOrder = ×-isTotalOrder T₁._≟_ T₁.isTotalOrder T₂.isTotalOrder
    }
    where
    module T₁ = DecTotalOrder t₁
    module T₂ =    TotalOrder t₂

  ×-decTotalOrder : DecTotalOrder ℓ₁ ℓ₂ _ → DecTotalOrder ℓ₃ ℓ₄ _ →
                    DecTotalOrder _ _ _
  ×-decTotalOrder t₁ t₂ = record
    { isDecTotalOrder = ×-isDecTotalOrder
        (isDecTotalOrder t₁) (isDecTotalOrder t₂)
    } where open DecTotalOrder

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

-- Version 0.15

_×-isPartialOrder_  = ×-isPartialOrder
{-# WARNING_ON_USAGE _×-isPartialOrder_
"Warning: _×-isPartialOrder_ was deprecated in v0.15.
Please use ×-isPartialOrder instead."
#-}
_×-isDecTotalOrder_ = ×-isDecTotalOrder
{-# WARNING_ON_USAGE _×-isDecTotalOrder_
"Warning: _×-isDecTotalOrder_ was deprecated in v0.15.
Please use ×-isDecTotalOrder instead."
#-}
_×-poset_           = ×-poset
{-# WARNING_ON_USAGE _×-poset_
"Warning: _×-poset_ was deprecated in v0.15.
Please use ×-poset instead."
#-}
_×-totalOrder_      = ×-totalOrder
{-# WARNING_ON_USAGE _×-totalOrder_
"Warning: _×-totalOrder_ was deprecated in v0.15.
Please use ×-totalOrder instead."
#-}
_×-decTotalOrder_   = ×-decTotalOrder
{-# WARNING_ON_USAGE _×-decTotalOrder_
"Warning: _×-decTotalOrder_ was deprecated in v0.15.
Please use ×-decTotalOrder instead."
#-}
×-≈-respects₂       = ×-respects₂
{-# WARNING_ON_USAGE ×-≈-respects₂
"Warning: ×-≈-respects₂ was deprecated in v0.15.
Please use ×-respects₂ instead."
#-}


module Formalization.ClassicalPropositionalLogic.NaturalDeduction where

open import Data.Either as Either using (Left ; Right)
open import Formalization.ClassicalPropositionalLogic.Syntax
open import Functional
import      Lvl
import      Logic.Propositional as Meta
open import Logic
open import Relator.Equals
open import Relator.Equals.Proofs.Equiv
open import Sets.PredicateSet using (PredSet ; _∈_ ; _∉_ ; _∪_ ; _∪•_ ; _∖_ ; _⊆_ ; _⊇_ ; ∅ ; [≡]-to-[⊆] ; [≡]-to-[⊇]) renaming (•_ to singleton ; _≡_ to _≡ₛ_)
open import Type

private variable ℓₚ ℓ ℓ₁ ℓ₂ : Lvl.Level

data _⊢_ {ℓ ℓₚ} {P : Type{ℓₚ}} : Formulas(P){ℓ} → Formula(P) → Stmt{Lvl.𝐒(ℓₚ Lvl.⊔ ℓ)} where
  direct : ∀{Γ} → (Γ ⊆ (Γ ⊢_))

  [⊤]-intro : ∀{Γ} → (Γ ⊢ ⊤)

  [⊥]-intro : ∀{Γ}{φ} → (Γ ⊢ φ) → (Γ ⊢ (¬ φ)) → (Γ ⊢ ⊥)
  [⊥]-elim  : ∀{Γ}{φ} → (Γ ⊢ ⊥) → (Γ ⊢ φ)

  [¬]-intro : ∀{Γ}{φ} → ((Γ ∪ singleton(φ)) ⊢ ⊥) → (Γ ⊢ (¬ φ))
  [¬]-elim  : ∀{Γ}{φ} → ((Γ ∪ singleton(¬ φ)) ⊢ ⊥) → (Γ ⊢ φ)

  [∧]-intro : ∀{Γ}{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ ψ) → (Γ ⊢ (φ ∧ ψ))
  [∧]-elimₗ : ∀{Γ}{φ ψ} → (Γ ⊢ (φ ∧ ψ)) → (Γ ⊢ φ)
  [∧]-elimᵣ : ∀{Γ}{φ ψ} → (Γ ⊢ (φ ∧ ψ)) → (Γ ⊢ ψ)

  [∨]-introₗ : ∀{Γ}{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ (φ ∨ ψ))
  [∨]-introᵣ : ∀{Γ}{φ ψ} → (Γ ⊢ ψ) → (Γ ⊢ (φ ∨ ψ))
  [∨]-elim   : ∀{Γ}{φ ψ χ} → ((Γ ∪ singleton(φ)) ⊢ χ) → ((Γ ∪ singleton(ψ)) ⊢ χ) → (Γ ⊢ (φ ∨ ψ)) → (Γ ⊢ χ)

  [⟶]-intro : ∀{Γ}{φ ψ} → ((Γ ∪ singleton(φ)) ⊢ ψ) → (Γ ⊢ (φ ⟶ ψ))
  [⟶]-elim  : ∀{Γ}{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ (φ ⟶ ψ)) → (Γ ⊢ ψ)

  [⟷]-intro : ∀{Γ}{φ ψ} → ((Γ ∪ singleton(ψ)) ⊢ φ) → ((Γ ∪ singleton(φ)) ⊢ ψ) → (Γ ⊢ (φ ⟷ ψ))
  [⟷]-elimₗ : ∀{Γ}{φ ψ} → (Γ ⊢ ψ) → (Γ ⊢ (φ ⟷ ψ)) → (Γ ⊢ φ)
  [⟷]-elimᵣ : ∀{Γ}{φ ψ} → (Γ ⊢ φ) → (Γ ⊢ (φ ⟷ ψ)) → (Γ ⊢ ψ)

module _ where
  private variable P : Type{ℓₚ}
  private variable Γ Γ₁ Γ₂ : Formulas(P){ℓ}
  private variable φ ψ : Formula(P)

  _⊬_ : Formulas(P){ℓ} → Formula(P) → Stmt
  _⊬_ = Meta.¬_ ∘₂ (_⊢_)

  weaken-union-singleton : (Γ₁ ⊆ Γ₂) → (((Γ₁ ∪ singleton(φ)) ⊢_) ⊆ ((Γ₂ ∪ singleton(φ)) ⊢_))

  weaken : (Γ₁ ⊆ Γ₂) → ((Γ₁ ⊢_) ⊆ (Γ₂ ⊢_))
  weaken Γ₁Γ₂ {φ}        (direct p)         = direct (Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {.⊤}       [⊤]-intro          = [⊤]-intro
  weaken Γ₁Γ₂ {.⊥}       ([⊥]-intro  p q)   = [⊥]-intro  (weaken Γ₁Γ₂ p) (weaken Γ₁Γ₂ q)
  weaken Γ₁Γ₂ {φ}        ([⊥]-elim   p)     = [⊥]-elim   (weaken Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {.(¬ _)}   ([¬]-intro  p)     = [¬]-intro  (weaken-union-singleton Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {φ}        ([¬]-elim   p)     = [¬]-elim   (weaken-union-singleton Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {.(_ ∧ _)} ([∧]-intro  p q)   = [∧]-intro  (weaken Γ₁Γ₂ p) (weaken Γ₁Γ₂ q)
  weaken Γ₁Γ₂ {φ}        ([∧]-elimₗ  p)     = [∧]-elimₗ  (weaken Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {φ}        ([∧]-elimᵣ  p)     = [∧]-elimᵣ  (weaken Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {.(_ ∨ _)} ([∨]-introₗ p)     = [∨]-introₗ (weaken Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {.(_ ∨ _)} ([∨]-introᵣ p)     = [∨]-introᵣ (weaken Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {φ}        ([∨]-elim   p q r) = [∨]-elim   (weaken-union-singleton Γ₁Γ₂ p) (weaken-union-singleton Γ₁Γ₂ q) (weaken Γ₁Γ₂ r)
  weaken Γ₁Γ₂ {.(_ ⟶ _)} ([⟶]-intro  p)     = [⟶]-intro  (weaken-union-singleton Γ₁Γ₂ p)
  weaken Γ₁Γ₂ {φ}        ([⟶]-elim   p q)   = [⟶]-elim   (weaken Γ₁Γ₂ p) (weaken Γ₁Γ₂ q)
  weaken Γ₁Γ₂ {.(_ ⟷ _)} ([⟷]-intro  p q)   = [⟷]-intro  (weaken-union-singleton Γ₁Γ₂ p) (weaken-union-singleton Γ₁Γ₂ q)
  weaken Γ₁Γ₂ {φ}        ([⟷]-elimₗ  p q)   = [⟷]-elimₗ  (weaken Γ₁Γ₂ p) (weaken Γ₁Γ₂ q)
  weaken Γ₁Γ₂ {φ}        ([⟷]-elimᵣ  p q)   = [⟷]-elimᵣ  (weaken Γ₁Γ₂ p) (weaken Γ₁Γ₂ q)

  weaken-union-singleton Γ₁Γ₂ p = weaken (Either.mapLeft Γ₁Γ₂) p

  weaken-union : (Γ₁ ⊢_) ⊆ ((Γ₁ ∪ Γ₂) ⊢_)
  weaken-union = weaken Either.Left

  [⟵]-intro : ((Γ ∪ singleton(φ)) ⊢ ψ) → (Γ ⊢ (ψ ⟵ φ))
  [⟵]-intro = [⟶]-intro

  [⟵]-elim : (Γ ⊢ φ) → (Γ ⊢ (ψ ⟵ φ)) → (Γ ⊢ ψ)
  [⟵]-elim = [⟶]-elim

  [¬¬]-elim : (Γ ⊢ ¬(¬ φ)) → (Γ ⊢ φ)
  [¬¬]-elim nnφ =
    ([¬]-elim
      ([⊥]-intro
        (direct(Right [≡]-intro))
        (weaken-union nnφ)
      )
    )

  [¬¬]-intro : (Γ ⊢ φ) → (Γ ⊢ ¬(¬ φ))
  [¬¬]-intro Γφ =
    ([¬]-intro
      ([⊥]-intro
        (weaken-union Γφ)
        (direct (Right [≡]-intro))
      )
    )


{-
  This file contains cospans, cones, pullbacks and maps of cones in precategories.
-}
{-# OPTIONS --cubical --no-import-sorts --safe #-}

module Cubical.Categories.Pullback where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Sigma
open import Cubical.Categories.Category

private
  variable
    ℓ ℓ' : Level

record Cospan (𝒞 : Precategory ℓ ℓ') : Type (ℓ-max ℓ ℓ') where
  constructor cospan
  field
    S₁ S₂ vertex : Precategory.ob 𝒞
    s₁ : hom 𝒞 S₁ vertex
    s₂ : hom 𝒞 S₂ vertex

record Cone {𝒞 : Precategory ℓ ℓ'} (cspn : Cospan 𝒞) (c : ob 𝒞) : Type (ℓ-max ℓ ℓ') where
  constructor cone
  field
    p₁ : hom 𝒞 c (Cospan.S₁ cspn)
    p₂ : hom 𝒞 c (Cospan.S₂ cspn)
    sq : seq 𝒞 p₁ (Cospan.s₁ cspn) ≡ seq 𝒞 p₂ (Cospan.s₂ cspn)

record Pullback {𝒞 : Precategory ℓ ℓ'} (cspn : Cospan 𝒞) : Type (ℓ-max ℓ ℓ') where
  constructor pullback
  field
    c : ob 𝒞
    cn : Cone cspn c
    universal : {c' : ob 𝒞} (cn' : Cone cspn c') → ∃![ f ∈ 𝒞 .hom c' c ] Σ[ q ∈ Cone.p₁ cn' ≡ 𝒞 .seq f (Cone.p₁ cn) ] (Cone.p₂ cn' ≡ 𝒞 .seq f (Cone.p₂ cn))

-- whisker the parallel morphisms g and g' with f
lPrecatWhisker : {𝒞 : Precategory ℓ ℓ'} {x y z : 𝒞 .ob} (f : 𝒞 .hom x y) (g g' : 𝒞 .hom y z) (p : g ≡ g') → 𝒞 .seq f g ≡ 𝒞 .seq f g'
lPrecatWhisker {𝒞 = 𝒞} f _ _ p = cong (𝒞 .seq f) p

-- extend a cone on c by a morphism c'→c using precomposition
coneMap : {𝒞 : Precategory ℓ ℓ'} {cspn : Cospan 𝒞} {c c' : ob 𝒞} (cn : Cone cspn c) (f : hom 𝒞 c' c) → Cone cspn c'
coneMap {𝒞 = 𝒞} {cospan _ _ _ s₁ s₂} (cone p₁ p₂ sq) f =
  cone (𝒞 .seq f p₁)  (𝒞 .seq f p₂) ((𝒞 .seq-α f p₁ s₁) ∙∙ lPrecatWhisker {𝒞 = 𝒞} f (𝒞 .seq p₁ s₁) (𝒞 .seq p₂ s₂) sq ∙∙ sym (𝒞 .seq-α f p₂ s₂))


------------------------------------------------------------------------
-- Raw terms
------------------------------------------------------------------------

import Level
open import Data.Universe

module README.DependentlyTyped.Raw-term
  (Uni₀ : Universe Level.zero Level.zero)
  where

open import Data.Nat
import README.DependentlyTyped.Term as Term; open Term Uni₀
open import Relation.Binary.PropositionalEquality as P using (_≡_)

mutual

  infixl 9 _·_
  infix  5 _∶_

  -- Raw types.

  data Raw-ty : Set where
    ⋆  : Raw-ty
    el : (t : Raw) → Raw-ty
    π  : (t₁ t₂ : Raw-ty) → Raw-ty

  -- Raw terms.
  --
  -- One could distinguish between "checkable" and "inferrable" terms,
  -- but without a corresponding split for the well-typed terms this
  -- seems awkward.

  data Raw : Set where
    var : (x : ℕ) → Raw
    ƛ   : (t : Raw) → Raw
    _·_ : (t₁ t₂ : Raw) → Raw

    -- Type annotation.
    _∶_ : (t : Raw) (σ : Raw-ty) → Raw

-- The context position corresponding to a variable.

position : ∀ {Γ σ} → Γ ∋ σ → ℕ
position zero    = zero
position (suc x) = suc (position x)

-- Well-typed terms can be turned into raw ones.

⌊_⌋ : ∀ {Γ σ} → Γ ⊢ σ → Raw
⌊ var x   ⌋ = var (position x)
⌊ ƛ t     ⌋ = ƛ ⌊ t ⌋
⌊ t₁ · t₂ ⌋ = ⌊ t₁ ⌋ · ⌊ t₂ ⌋

-- The same applies to syntactic types.

⌊_⌋ty : ∀ {Γ σ} → Γ ⊢ σ type → Raw-ty
⌊ ⋆       ⌋ty = ⋆
⌊ el t    ⌋ty = el ⌊ t ⌋
⌊ π σ′ τ′ ⌋ty = π ⌊ σ′ ⌋ty ⌊ τ′ ⌋ty

mutual

  -- The following functions remove type-annotations.

  ⌊_⌋raw-ty : Raw-ty → Raw-ty
  ⌊ ⋆       ⌋raw-ty = ⋆
  ⌊ el t    ⌋raw-ty = el ⌊ t ⌋raw
  ⌊ π t₁ t₂ ⌋raw-ty = π ⌊ t₁ ⌋raw-ty ⌊ t₂ ⌋raw-ty

  ⌊_⌋raw : Raw → Raw
  ⌊ var x   ⌋raw = var x
  ⌊ ƛ t     ⌋raw = ƛ ⌊ t ⌋raw
  ⌊ t₁ · t₂ ⌋raw = ⌊ t₁ ⌋raw · ⌊ t₂ ⌋raw
  ⌊ t ∶ σ   ⌋raw = ⌊ t ⌋raw

-- Some congruence lemmas.

position-cong : ∀ {Γ₁ σ₁} {x₁ : Γ₁ ∋ σ₁}
                  {Γ₂ σ₂} {x₂ : Γ₂ ∋ σ₂} →
                x₁ ≅-∋ x₂ → position x₁ ≡ position x₂
position-cong P.refl = P.refl

⌊⌋-cong : ∀ {Γ₁ σ₁} {t₁ : Γ₁ ⊢ σ₁}
            {Γ₂ σ₂} {t₂ : Γ₂ ⊢ σ₂} →
          t₁ ≅-⊢ t₂ → ⌊ t₁ ⌋ ≡ ⌊ t₂ ⌋
⌊⌋-cong P.refl = P.refl

⌊⌋ty-cong : ∀ {Γ₁ σ₁} {σ′₁ : Γ₁ ⊢ σ₁ type}
              {Γ₂ σ₂} {σ′₂ : Γ₂ ⊢ σ₂ type} →
            σ′₁ ≅-type σ′₂ → ⌊ σ′₁ ⌋ty ≡ ⌊ σ′₂ ⌋ty
⌊⌋ty-cong P.refl = P.refl


open import guarded-recursion.prelude

module guarded-recursion.model where

-- ℂʷᵒᵖ (ℂ^{ω}^{op})
-- Notation:
--   For the category ℂ we use superscript 'c' to disambiguate (e.g. _→ᶜ_)
--   We use ᵖ for the presheaf category.
module Presheaf
  {o m}
  (Objᶜ : Type_ o)
  (_→ᶜ_ : Objᶜ → Objᶜ → Type_ m)
  (idᶜ  : {A : Objᶜ} → A →ᶜ A)
  (_∘ᶜ_ : {A B C : Objᶜ} → (B →ᶜ C) → (A →ᶜ B) → (A →ᶜ C))
  (∘-idᶜ : {A B : Objᶜ} {f : A →ᶜ B} → f ∘ᶜ idᶜ ≡ f)
  (id-∘ᶜ : {A B : Objᶜ} {f : A →ᶜ B} → idᶜ ∘ᶜ f ≡ f)
  (∘-assocᶜ : {A B C D : Objᶜ}
              {f : C →ᶜ D} {g : B →ᶜ C} {h : A →ᶜ B}
              → (f ∘ᶜ g) ∘ᶜ h ≡ f ∘ᶜ (g ∘ᶜ h))
  (𝟙ᶜ : Objᶜ)
  (!ᶜ : {A : Objᶜ} → A →ᶜ 𝟙ᶜ)
  (!-uniqᶜ : {A : Objᶜ} {f : A →ᶜ 𝟙ᶜ} → f ≡ !ᶜ)
  where

  -- Index preserving maps
  _→ᶜ°_ : (A B : ℕ → Objᶜ) → Type_ _
  A →ᶜ° B = (n : ℕ) → A n →ᶜ B n

  Endoᶜ° : (ℕ → Objᶜ) → Type_ _
  Endoᶜ° A = A →ᶜ° A

  -- Restriction maps
  Rmap : (ℕ → Objᶜ) → Type_ _
  Rmap A = (A ∘ S) →ᶜ° A

  Objᵖ : Type_ _
  Objᵖ = Σ (ℕ → Objᶜ) Rmap

  _→ᵖ_ : Objᵖ → Objᵖ → Type_ _
  (A , rᴬ) →ᵖ (B , rᴮ) = Σ (A →ᶜ° B) λ f →
                           (n : ℕ) → f n ∘ᶜ rᴬ n ≡ rᴮ n ∘ᶜ f (1 + n)

  [_]ᵖ : Objᶜ → Objᵖ
  [ A ]ᵖ = (λ _ → A) , (λ _ → idᶜ)

  ⟨_⟩ᵖ : {A B : Objᶜ} → A →ᶜ B → [ A ]ᵖ →ᵖ [ B ]ᵖ
  ⟨ f ⟩ᵖ = (λ _ → f) , (λ n → ∘-idᶜ ∙ ! id-∘ᶜ)

  idᵖ : {A : Objᵖ} → A →ᵖ A
  idᵖ = (λ n → idᶜ) , (λ n → id-∘ᶜ ∙ ! ∘-idᶜ)

  {-
      B ---f--> D
      ^         ^
      |         |
      g         h
      |         |
      A ---i--> C
  -}
  module _ {A B C D}
           (f : B →ᶜ D) (g : A →ᶜ B)
           (h : C →ᶜ D) (i : A →ᶜ C) where
    Squareᶜ = f ∘ᶜ g ≡ h ∘ᶜ i

  module _ {A B C} {f f' : B →ᶜ C} {g g'  : A →ᶜ B} where
    ap-∘ᶜ  : f ≡ f' → g ≡ g' → f ∘ᶜ g ≡ f' ∘ᶜ g'
    ap-∘ᶜ p q = ap (λ x → x ∘ᶜ g) p ∙ ap (_∘ᶜ_ f') q

  !-irrᶜ : {A : Objᶜ} {f g : A →ᶜ 𝟙ᶜ} → f ≡ g
  !-irrᶜ = !-uniqᶜ ∙ ! !-uniqᶜ

  ∘-!ᶜ : {A : Objᶜ} {f : 𝟙ᶜ →ᶜ A} { !g : 𝟙ᶜ →ᶜ 𝟙ᶜ} → f ∘ᶜ !g ≡ f
  ∘-!ᶜ = ap-∘ᶜ idp !-irrᶜ ∙ ∘-idᶜ

  with-∘-assocᶜ : {A B C C' D : Objᶜ}
              {f  : C  →ᶜ D} {g  : B →ᶜ C}
              {f' : C' →ᶜ D} {g' : B →ᶜ C'}
              {h : A →ᶜ B}
              → f ∘ᶜ g ≡ f' ∘ᶜ g'
              → f ∘ᶜ (g ∘ᶜ h) ≡ f' ∘ᶜ (g' ∘ᶜ h)
  with-∘-assocᶜ p = ! ∘-assocᶜ ∙ ap-∘ᶜ p idp ∙ ∘-assocᶜ

  with-!∘-assocᶜ : {A B B' C D : Objᶜ}
              {f  : C  →ᶜ D}
              {g  : B  →ᶜ C} {h : A →ᶜ B}
              {g' : B' →ᶜ C} {h' : A →ᶜ B'}
              → g ∘ᶜ h ≡ g' ∘ᶜ h'
              → (f ∘ᶜ g) ∘ᶜ h ≡ (f ∘ᶜ g') ∘ᶜ h'
  with-!∘-assocᶜ p = ∘-assocᶜ ∙ ap-∘ᶜ idp p ∙ ! ∘-assocᶜ

  {-
      B ---f--> D ---e--> F
      ^         ^         ^
      |         |         |
      g    L    h    R    j
      |         |         |
      A ---i--> C ---k--> E
  -}
  module _
           {A B C D E F}
           {f : B →ᶜ D} {g : A →ᶜ B}
           {h : C →ᶜ D} {i : A →ᶜ C}
           {e : D →ᶜ F}
           {j : E →ᶜ F} {k : C →ᶜ E}
           (L : Squareᶜ f g h i)
           (R : Squareᶜ e h j k) where

      private
        efg-ehi : (e ∘ᶜ f) ∘ᶜ g ≡ e ∘ᶜ (h ∘ᶜ i)
        efg-ehi = ∘-assocᶜ ∙ ap-∘ᶜ idp L

        ehi-jki : e ∘ᶜ (h ∘ᶜ i) ≡ j ∘ᶜ (k ∘ᶜ i)
        ehi-jki = ! ∘-assocᶜ ∙ ap-∘ᶜ R idp ∙ ∘-assocᶜ

      LR : Squareᶜ (e ∘ᶜ f) g j (k ∘ᶜ i)
      LR = efg-ehi ∙ ehi-jki

  {-
                    X
                  / . \
                /   .   \
              /     .     \
           f/       .u!     \g
          /         .         \
        v           v           v
      A <---fst--- A×B ---snd---> B
  -}
  module _
     {A B A×B X : Objᶜ}
     {fstᶜ   : A×B →ᶜ A}
     {sndᶜ   : A×B →ᶜ B}
     {f      : X →ᶜ A}
     {g      : X →ᶜ B}
     (u!     : X →ᶜ A×B)
     (fst-u! : fstᶜ ∘ᶜ u! ≡ f)
     (snd-u! : sndᶜ ∘ᶜ u! ≡ g)
     where
     1-ProductDiagram = fst-u! , snd-u!

  module ProductDiagram
     {A B A×B : Objᶜ}
     {fstᶜ : A×B →ᶜ A}
     {sndᶜ : A×B →ᶜ B}
     {<_,_>ᶜ : ∀ {X} → X →ᶜ A → X →ᶜ B → X →ᶜ A×B}
     (fst-<,> : ∀ {X} {f : X →ᶜ A} {g : X →ᶜ B}
                → fstᶜ ∘ᶜ < f , g >ᶜ ≡ f)
     (snd-<,> : ∀ {X} {f : X →ᶜ A} {g : X →ᶜ B}
                → sndᶜ ∘ᶜ < f , g >ᶜ ≡ g)
     (<,>-uniq! : ∀ {A B X : Objᶜ} {f : X →ᶜ A×B}
                  → < fstᶜ ∘ᶜ f , sndᶜ ∘ᶜ f >ᶜ ≡ f)
     where

     module _ {X} {f : X →ᶜ A} {g : X →ᶜ B} where
        1-productDiagram = 1-ProductDiagram < f , g >ᶜ fst-<,> snd-<,>

  infixr 9 _∘ᵖ_
  _∘ᵖ_ : {A B C : Objᵖ} → (B →ᵖ C) → (A →ᵖ B) → (A →ᵖ C)
  (f , ☐f) ∘ᵖ (g , ☐g) = (λ n → f n ∘ᶜ g n)
                       , (λ n → LR (☐g n) (☐f n))

  -- TODO: the real thing™
  _≡ᵖ_ : {A B : Objᵖ} (f g : A →ᵖ B) → Type_ _
  (f , _) ≡ᵖ (g , _) = ∀ n → f n ≡ g n
  infix 2 _≡ᵖ_

  ∘-idᵖ : {A B : Objᵖ} {f : A →ᵖ B} → f ∘ᵖ idᵖ ≡ᵖ f
  ∘-idᵖ _ = ∘-idᶜ

  id-∘ᵖ : {A B : Objᵖ} {f : A →ᵖ B} → idᵖ ∘ᵖ f ≡ᵖ f
  id-∘ᵖ _ = id-∘ᶜ

  ∘-assocᵖ : {A B C D : Objᵖ}
             {f : C →ᵖ D} {g : B →ᵖ C} {h : A →ᵖ B}
             → (f ∘ᵖ g) ∘ᵖ h ≡ᵖ f ∘ᵖ (g ∘ᵖ h)
  ∘-assocᵖ _ = ∘-assocᶜ

  𝟙ᵖ : Objᵖ
  𝟙ᵖ = (λ _ → 𝟙ᶜ) , (λ _ → idᶜ)
  !ᵖ : {A : Objᵖ} → A →ᵖ 𝟙ᵖ
  !ᵖ = (λ _ → !ᶜ) , (λ _ → !-irrᶜ)
  !-uniqᵖ : {A : Objᵖ} {f : A →ᵖ 𝟙ᵖ} → f ≡ᵖ !ᵖ
  !-uniqᵖ _ = !-uniqᶜ

  ▸ : Objᵖ → Objᵖ
  ▸ (A , rᴬ) = ▸A , ▸rᴬ
     module Later where
       ▸A : ℕ → Objᶜ
       ▸A 0     = 𝟙ᶜ
       ▸A (S n) = A n
       ▸rᴬ : (n : ℕ) → ▸A (1 + n) →ᶜ ▸A n
       ▸rᴬ 0     = !ᶜ
       ▸rᴬ (S n) = rᴬ n

  -- ▸ functor action on morphisms
  ▸[_] : {A B : Objᵖ} → (A →ᵖ B) → ▸ A →ᵖ ▸ B
  ▸[_] {A , rᴬ} {B , rᴮ} (f , ☐) = ▸f , ▸☐
     module Later[] where
       open Later A rᴬ
       open Later B rᴮ renaming (▸A to ▸B; ▸rᴬ to ▸rᴮ)
       ▸f : ▸A →ᶜ° ▸B
       ▸f 0     = !ᶜ
       ▸f (S n) = f n
       ▸☐ : (n : ℕ) → ▸f n ∘ᶜ ▸rᴬ n ≡ ▸rᴮ n ∘ᶜ ▸f (1 + n)
       ▸☐ 0     = !-irrᶜ
       ▸☐ (S n) = ☐ n

  ▸-id : {A : Objᵖ}
       → ▸[ idᵖ {A} ] ≡ᵖ idᵖ
  ▸-id 0     = !-irrᶜ
  ▸-id (S n) = idp

  ▸-∘ : {A B C : Objᵖ} {f : B →ᵖ C} {g : A →ᵖ B}
      → ▸[ f ∘ᵖ g ] ≡ᵖ ▸[ f ] ∘ᵖ ▸[ g ]
  ▸-∘ 0     = !-irrᶜ
  ▸-∘ (S n) = idp

  {-
  ▸-[]ᵖ : {A : Objᶜ} → ▸ [ A ]ᵖ →ᵖ [ A ]ᵖ
  ▸-[]ᵖ {A} = ▸f , ▸☐
    where
      open Later (λ _ → A) (λ _ → idᶜ)
      ▸f : ▸A →ᶜ° (λ _ → A)
      ▸f O     = {!!}
      ▸f (S n) = idᶜ
      ▸☐ : (n : ℕ) → ▸f n ∘ᶜ ▸rᴬ n ≡ idᶜ ∘ᶜ ▸f (1 + n)
      ▸☐ O     = {!!}
      ▸☐ (S n) = idp
  -}

  next : {A : Objᵖ} → A →ᵖ ▸ A
  next {A , rᴬ} = f , ☐
     module Next where
       open Later A rᴬ
       f : (n : ℕ) → A n →ᶜ ▸A n
       f 0     = !ᶜ
       f (S n) = rᴬ n

       ☐ : (n : ℕ) → f n ∘ᶜ rᴬ n ≡ ▸rᴬ n ∘ᶜ f (1 + n)
       ☐ 0     = idp
       ☐ (S n) = idp

  next-nat : {A B : Objᵖ} {f : A →ᵖ B} → ▸[ f ] ∘ᵖ next ≡ᵖ next ∘ᵖ f
  next-nat             0     = !-irrᶜ
  next-nat {f = f , ☐} (S n) = ☐ n

  module _ {A : Objᵖ} (f : A →ᵖ A) where
    Contractive = Σ (▸ A →ᵖ A) λ g → f ≡ᵖ g ∘ᵖ next

  module _ {A B : Objᶜ} {i : B →ᶜ B} {f : A →ᶜ B} where
    id-∘ᶜ' : i ≡ idᶜ → i ∘ᶜ f ≡ f
    id-∘ᶜ' p = ap-∘ᶜ p idp ∙ id-∘ᶜ
  module _ {A B : Objᶜ} {i : A →ᶜ A} {f : A →ᶜ B} where
    ∘-idᶜ' : i ≡ idᶜ → f ∘ᶜ i ≡ f
    ∘-idᶜ' p = ap-∘ᶜ idp p ∙ ∘-idᶜ

  fix1 : {A : Objᵖ} (f : ▸ A →ᵖ A) → 𝟙ᵖ →ᵖ A
  fix1 {A , rᴬ} (f , ☐) = fixf , λ n → ∘-idᶜ ∙ fix☐ n
    module Fix1 where
      open Later A rᴬ

      fixf : (n : ℕ) → 𝟙ᶜ →ᶜ A n
      fixf 0     = f 0
      fixf (S n) = f (S n) ∘ᶜ fixf n

      fix☐ : (n : ℕ) → fixf n ≡ rᴬ n ∘ᶜ fixf (1 + n)
      fix☐ 0     = ! ∘-!ᶜ             ∙ with-∘-assocᶜ (☐ 0)
      fix☐ (S n) = ap-∘ᶜ idp (fix☐ n) ∙ with-∘-assocᶜ (☐ (S n))

  module WithProducts
    (_×ᶜ_ : Objᶜ → Objᶜ → Objᶜ)
    -- projections
    (fstᶜ : ∀ {A B} → (A ×ᶜ B) →ᶜ A)
    (sndᶜ : ∀ {A B} → (A ×ᶜ B) →ᶜ B)
    -- injection (pair creation)
    (<_,_>ᶜ : ∀ {A B C} → A →ᶜ B → A →ᶜ C → A →ᶜ (B ×ᶜ C))
    -- computation rules
    (fst-<,> : ∀ {A B C} {f : A →ᶜ B} {g : A →ᶜ C}
               → fstᶜ ∘ᶜ < f , g >ᶜ ≡ f)
    (snd-<,> : ∀ {A B C} {f : A →ᶜ B} {g : A →ᶜ C}
               → sndᶜ ∘ᶜ < f , g >ᶜ ≡ g)
    -- universal property
    (<,>-uniq! : ∀ {A B X : Objᶜ} {f : X →ᶜ (A ×ᶜ B)}
                 → < fstᶜ ∘ᶜ f , sndᶜ ∘ᶜ f >ᶜ ≡ f)
    where

    firstᶜ : ∀ {A B C} → A →ᶜ B → (A ×ᶜ C) →ᶜ (B ×ᶜ C)
    firstᶜ f = < f ∘ᶜ fstᶜ , sndᶜ >ᶜ

    secondᶜ : ∀ {A B C} → B →ᶜ C → (A ×ᶜ B) →ᶜ (A ×ᶜ C)
    secondᶜ f = < fstᶜ , f ∘ᶜ sndᶜ >ᶜ

    <_×_>ᶜ : ∀ {A B C D} (f : A →ᶜ C) (g : B →ᶜ D) → (A ×ᶜ B) →ᶜ (C ×ᶜ D)
    < f × g >ᶜ = < f ∘ᶜ fstᶜ , g ∘ᶜ sndᶜ >ᶜ

    module _ {A B C} {f f' : A →ᶜ B} {g g' : A →ᶜ C} where
        ≡<_,_> : f ≡ f' → g ≡ g' → < f , g >ᶜ ≡ < f' , g' >ᶜ
        ≡< p , q > = ap (λ f → < f , g >ᶜ) p ∙ ap (λ g → < f' , g >ᶜ) q

    module _ {A B C X} {f₀ : A →ᶜ B} {f₁ : A →ᶜ C} {g : X →ᶜ A} where
        <,>-∘ : < f₀ , f₁ >ᶜ ∘ᶜ g ≡ < f₀ ∘ᶜ g , f₁ ∘ᶜ g >ᶜ
        <,>-∘ = ! <,>-uniq! ∙ ≡< ! ∘-assocᶜ ∙ ap-∘ᶜ fst-<,> idp
                               , ! ∘-assocᶜ ∙ ap-∘ᶜ snd-<,> idp >

    module _ {A B} where
        -- η-rule
        <fst,snd> : < fstᶜ , sndᶜ >ᶜ ≡ idᶜ {A ×ᶜ B}
        <fst,snd> = ≡< ! ∘-idᶜ , ! ∘-idᶜ > ∙ <,>-uniq!

        <fst,id∘snd> : < fstᶜ , idᶜ ∘ᶜ sndᶜ >ᶜ ≡ idᶜ {A ×ᶜ B}
        <fst,id∘snd> = ≡< idp , id-∘ᶜ > ∙ <fst,snd>

    _×ᶜ°_ : (A B : ℕ → Objᶜ) → ℕ → Objᶜ
    (A ×ᶜ° B) n = A n ×ᶜ B n

    _×ʳ_ : {A B : ℕ → Objᶜ} → Rmap A → Rmap B → Rmap (A ×ᶜ° B)
    (rᴬ ×ʳ rᴮ) n = < rᴬ n × rᴮ n >ᶜ

    _×ᵖ_ : Objᵖ → Objᵖ → Objᵖ
    (A , rᴬ) ×ᵖ (B , rᴮ) = (A ×ᶜ° B) , (rᴬ ×ʳ rᴮ)

    fstᵖ : ∀ {A B} → (A ×ᵖ B) →ᵖ A
    fstᵖ = (λ _ → fstᶜ) , (λ _ → fst-<,>)

    sndᵖ : ∀ {A B} → (A ×ᵖ B) →ᵖ B
    sndᵖ = (λ _ → sndᶜ) , (λ _ → snd-<,>)

    <_,_>ᵖ : ∀ {A B C} → A →ᵖ B → A →ᵖ C → A →ᵖ (B ×ᵖ C)
    <_,_>ᵖ {A , rᴬ} {B , rᴮ} {C , rᶜ} (f , f☐) (g , g☐) =
      (λ n → < f n , g n >ᶜ) ,
      (λ n → <,>-∘
           ∙ ≡< f☐ n ∙ ap-∘ᶜ idp (! fst-<,>) ∙ ! ∘-assocᶜ
              , g☐ n ∙ ap-∘ᶜ idp (! snd-<,>) ∙ ! ∘-assocᶜ >
           ∙ ! <,>-∘)

    firstᵖ : ∀ {A B C} → A →ᵖ B → (A ×ᵖ C) →ᵖ (B ×ᵖ C)
    firstᵖ f = < f ∘ᵖ fstᵖ , sndᵖ >ᵖ

    ▸-× : {A B : Objᵖ} → ▸(A ×ᵖ B) →ᵖ (▸ A ×ᵖ ▸ B)
    ▸-× {A , rᴬ} {B , rᴮ} = ▸f , ▸☐
      module LaterProduct where
          open Later A rᴬ
          open Later B rᴮ                 renaming (▸A to ▸B; ▸rᴬ to ▸rᴮ)
          open Later (A ×ᶜ° B) (rᴬ ×ʳ rᴮ) renaming (▸A to ▸AB; ▸rᴬ to ▸rᴬᴮ)
          ▸f : ▸AB →ᶜ° (▸A ×ᶜ° ▸B)
          ▸f 0     = < !ᶜ , !ᶜ >ᶜ
          ▸f (S n) = idᶜ
          ▸☐ : (n : ℕ) → ▸f n ∘ᶜ ▸rᴬᴮ n ≡ (▸rᴬ ×ʳ ▸rᴮ) n ∘ᶜ ▸f (1 + n)
          ▸☐ 0     = <,>-∘ ∙ ≡< !-irrᶜ , !-irrᶜ > ∙ ! ∘-idᶜ
          ▸☐ (S n) = id-∘ᶜ ∙ ! ∘-idᶜ

    _^_  : Objᶜ → ℕ → Objᶜ
    A ^ 0    = 𝟙ᶜ
    A ^(S n) = A ×ᶜ (A ^ n)

    fix : {A B : Objᵖ} (f : (B ×ᵖ ▸ A) →ᵖ A) → B →ᵖ A
    fix {A , rᴬ} {B , rᴮ} (f , ☐) = fixf , fix☐
     module Fix where
      open Later A rᴬ
      open Later B rᴮ renaming (▸A to ▸B; ▸rᴬ to ▸rᴮ)

      fixf : (n : ℕ) → B n →ᶜ A n
      fixf 0     = f 0 ∘ᶜ < idᶜ , !ᶜ >ᶜ
      fixf (S n) = f (S n) ∘ᶜ < idᶜ , fixf n ∘ᶜ rᴮ n >ᶜ

      fix☐ : (n : ℕ) → fixf n ∘ᶜ rᴮ n ≡ rᴬ n ∘ᶜ fixf (1 + n)
      fix☐ 0     = ∘-assocᶜ ∙ ap-∘ᶜ idp (<,>-∘ ∙ ≡< id-∘ᶜ ∙ ! (∘-idᶜ' fst-<,>) ∙ ! ∘-assocᶜ , !-irrᶜ > ∙ ! <,>-∘) ∙ with-∘-assocᶜ (☐ 0)
      fix☐ (S n) = ∘-assocᶜ ∙ ap-∘ᶜ idp (<,>-∘ ∙ ≡< id-∘ᶜ ∙ ! (∘-idᶜ' fst-<,>) ∙ ! ∘-assocᶜ , h      > ∙ ! <,>-∘) ∙ with-∘-assocᶜ (☐ (S n))
        where h = ap-∘ᶜ (fix☐ n) idp ∙ with-!∘-assocᶜ (! snd-<,>)

    module _ {A} where
        initᶜ : ∀ n → (A ^(1 + n)) →ᶜ (A ^ n)
        initᶜ 0     = !ᶜ
        initᶜ (S n) = secondᶜ (initᶜ n)

    module Str1
      (A    : Objᶜ)
      where

      Aᵖ = [ A ]ᵖ

      Str : ℕ → Objᶜ
      Str n = A ^(1 + n)

      rStr : (n : ℕ) → Str (1 + n) →ᶜ Str n
      rStr = λ n → initᶜ (1 + n)

      Strᵖ : Objᵖ
      Strᵖ = Str , rStr

      open Later Str (snd Strᵖ) renaming (▸A to ▸Str; ▸rᴬ to ▸rStr)

      roll : (n : ℕ) → (A ^ n) →ᶜ ▸Str n
      roll 0     = !ᶜ -- or idᶜ {𝟙ᶜ}
      roll (S n) = idᶜ
      unroll : (n : ℕ) → ▸Str n →ᶜ (A ^ n)
      unroll 0     = !ᶜ -- or idᶜ {𝟙ᶜ}
      unroll (S n) = idᶜ

      hdᵖ : Strᵖ →ᵖ Aᵖ
      hdᵖ = (λ _ → fstᶜ) , (λ n → fst-<,> ∙ ! id-∘ᶜ)

      tlᵖ : Strᵖ →ᵖ ▸ Strᵖ
      tlᵖ = f , ☐
        where
          f : (n : ℕ) → Str n →ᶜ ▸Str n
          f n = roll n ∘ᶜ sndᶜ
          ☐ : (n : ℕ) → f n ∘ᶜ rStr n ≡ ▸rStr n ∘ᶜ f (1 + n)
          ☐ 0     = !-irrᶜ
          ☐ (S n) = ∘-assocᶜ ∙ id-∘ᶜ ∙ snd-<,> ∙ ap-∘ᶜ idp (! id-∘ᶜ)

      ∷ᵖ : (Aᵖ ×ᵖ ▸ Strᵖ) →ᵖ Strᵖ
      ∷ᵖ = f , ☐
        where
          f : (n : ℕ) → (A ×ᶜ ▸Str n) →ᶜ Str n
          f n = secondᶜ (unroll n)
          ☐ : (n : ℕ) → f n ∘ᶜ snd (Aᵖ ×ᵖ ▸ Strᵖ) n ≡ rStr n ∘ᶜ f (1 + n)
          ☐ 0     = <,>-∘ ∙ ≡< fst-<,> ∙ id-∘ᶜ , !-irrᶜ > ∙ !(∘-idᶜ' <fst,id∘snd>)
          ☐ (S n) = id-∘ᶜ' <fst,id∘snd> ∙ ≡< id-∘ᶜ , idp > ∙ !(∘-idᶜ' <fst,id∘snd>)

      repeatᵖ : (𝟙ᵖ →ᵖ Aᵖ) → 𝟙ᵖ →ᵖ Strᵖ
      repeatᵖ f = fix1 it
        where it : ▸ Strᵖ →ᵖ Strᵖ
              it = ∷ᵖ ∘ᵖ < f ∘ᵖ !ᵖ , idᵖ >ᵖ

      {-
      iterate f x = x ∷ iterate f (f x)
      or
      iterate f = (∷) id (iterate f ∘ f)
      -}
      {-
      iterateᵖ : (Aᵖ →ᵖ Aᵖ) → Aᵖ →ᵖ Strᵖ
      iterateᵖ f = fix it -- sndᵖ ∘ᵖ fix it ∘ᵖ !ᵖ
        where
           it : Aᵖ ×ᵖ ▸ Strᵖ →ᵖ Strᵖ
           it = < fstᵖ , ∷ᵖ >ᵖ ∘ᵖ firstᵖ (f ∘ᵖ {!!}) ∘ᵖ ▸-×
      -}

{-
      mapᵖ : ([ A ]ᵖ →ᵖ [ A ]ᵖ) → Strᵖ →ᵖ Strᵖ
      mapᵖ f = {!fix!}
-}

  Objᶜ↑ : ℕ → Endo (ℕ → Objᶜ)
  Objᶜ↑ k A n = A (n + k)

  ↑ᶜ° : ∀ k {A B} → A →ᶜ° B → Objᶜ↑ k A →ᶜ° Objᶜ↑ k B
  ↑ᶜ° k f n = f (n + k)

  ↑r : ∀ k {A} → Rmap A → Rmap (Objᶜ↑ k A)
  ↑r k rᴬ n = rᴬ (n + k)

  Objᵖ↑ : ℕ → Endo Objᵖ
  Objᵖ↑ k (A , rᴬ) = Objᶜ↑ k A , ↑r k rᴬ

  r* : {A : ℕ → Objᶜ}
       (rᴬ : Rmap A)
       (n : ℕ) → A (1 + n) →ᶜ A 0
  r* rᴬ 0     = rᴬ 0
  r* rᴬ (S n) = r* rᴬ n ∘ᶜ rᴬ (S n)

  module _
    {A B : ℕ → Objᶜ}
    {rᴬ  : (n : ℕ) → A (1 + n) →ᶜ A n}
    {rᴮ  : (n : ℕ) → B (1 + n) →ᶜ B n}
    (f   : (n : ℕ) → A n →ᶜ B n)
    (☐f  : (n : ℕ) → f n ∘ᶜ rᴬ n ≡ rᴮ n ∘ᶜ f (1 + n))
    where

    ☐* : (n : ℕ) → f 0 ∘ᶜ r* rᴬ n ≡ r* rᴮ n ∘ᶜ f (1 + n)
    ☐* 0     = ☐f 0
    ☐* (S n) = ! ∘-assocᶜ
             ∙ ap-∘ᶜ (☐* n) idp
             ∙ ∘-assocᶜ
             ∙ ap-∘ᶜ idp (☐f (S n))
             ∙ ! ∘-assocᶜ

  module _
    {A B : ℕ → Objᶜ}
    {rᴬ  : (n : ℕ) → A (1 + n) →ᶜ A n}
    {rᴮ  : (n : ℕ) → B (1 + n) →ᶜ B n}
    (f   : (n : ℕ) → A n →ᶜ B n)
    (☐f  : (n : ℕ) → f n ∘ᶜ rᴬ n ≡ rᴮ n ∘ᶜ f (1 + n))
    (k : ℕ)
    where

    --☐*' : (n : ℕ) → f k ∘ᶜ r* (↑r k rᴬ) n ≡ r* (↑r k rᴮ) n ∘ᶜ f ((1 + n) + k)
    --☐*' n = ☐* (↑ᶜ° k f) {!!} n

  module WithMore -- TODO upto topos
    -- Initial object
    (𝟘ᶜ : Objᶜ)
    (¡ᶜ : {A : Objᶜ} → 𝟘ᶜ →ᶜ A)
    (¡-uniqᶜ : {A : Objᶜ} {f : 𝟘ᶜ →ᶜ A} → ¡ᶜ ≡ f)

    -- Coproducts
    (_+ᶜ_ : Objᶜ → Objᶜ → Objᶜ)
    -- injections
    (inlᶜ : ∀ {A B} → A →ᶜ (A +ᶜ B))
    (inrᶜ : ∀ {A B} → B →ᶜ (A +ᶜ B))
    -- projection (coproduct elimination)
    ([_,_]ᶜ : ∀ {A B C} → B →ᶜ A → C →ᶜ A → (B +ᶜ C) →ᶜ A)
    -- computation rules
    ([,]-inl : ∀ {A B C} {f : B →ᶜ A} {g : C →ᶜ A}
               → [ f , g ]ᶜ ∘ᶜ inlᶜ ≡ f)
    ([,]-inr : ∀ {A B C} {f : B →ᶜ A} {g : C →ᶜ A}
               → [ f , g ]ᶜ ∘ᶜ inrᶜ ≡ g)
    -- universal property
    ([,]-uniq! : ∀ {A B X : Objᶜ} {f : (A +ᶜ B) →ᶜ X}
                 → [ f ∘ᶜ inlᶜ , f ∘ᶜ inrᶜ ]ᶜ ≡ f)
    where
-- -}
-- -}
-- -}
-- -}


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

-- The universe of all types
module hott.core.universe where

open import Agda.Primitive public using (Level; lzero; lsuc; _⊔_)

-- We give an new name for Set
Type : (ℓ : Level) → Set (lsuc ℓ)
Type ℓ = Set ℓ

lone   : Level; lone   = lsuc lzero
ltwo   : Level; ltwo   = lsuc lone
lthree : Level; lthree = lsuc ltwo
lfour  : Level; lfour  = lsuc lthree
lfive  : Level; lfive  = lsuc lfour
lsix   : Level; lsix   = lsuc lfive
lseven : Level; lseven = lsuc lsix
leight : Level; leight = lsuc lseven
lnine  : Level; lnine  = lsuc leight

Type₀ = Type lzero
Type₁ = Type lone
Type₂ = Type ltwo
Type₃ = Type lthree
Type₄ = Type lfour
Type₅ = Type lfive
Type₆ = Type lsix
Type₇ = Type lseven
Type₈ = Type leight
Type₉ = Type lnine


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Group.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.SIP
open import Cubical.Data.Sigma
open import Cubical.Data.Int renaming (_+_ to _+Int_ ; _-_ to _-Int_)
open import Cubical.Data.Unit

open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Semigroup
open import Cubical.Foundations.HLevels

private
  variable
    ℓ : Level

record IsGroup {G : Type ℓ}
               (0g : G) (_+_ : G → G → G) (-_ : G → G) : Type ℓ where

  constructor isgroup

  field
    isMonoid  : IsMonoid 0g _+_
    inverse   : (x : G) → (x + (- x) ≡ 0g) × ((- x) + x ≡ 0g)

  open IsMonoid isMonoid public

  infixl 6 _-_

  _-_ : G → G → G
  x - y = x + (- y)

  invl : (x : G) → (- x) + x ≡ 0g
  invl x = inverse x .snd

  invr : (x : G) → x + (- x) ≡ 0g
  invr x = inverse x .fst

record GroupStr (G : Type ℓ) : Type (ℓ-suc ℓ) where

  constructor groupstr

  field
    0g      : G
    _+_     : G → G → G
    -_      : G → G
    isGroup : IsGroup 0g _+_ -_

  infix  8 -_
  infixr 7 _+_

  open IsGroup isGroup public

Group : Type (ℓ-suc ℓ)
Group = TypeWithStr _ GroupStr

Group₀ : Type₁
Group₀ = Group {ℓ-zero}

group : (G : Type ℓ) (0g : G) (_+_ : G → G → G) (-_ : G → G) (h : IsGroup 0g _+_ -_) → Group
group G 0g _+_ -_ h = G , groupstr 0g _+_ -_ h

isSetGroup : (G : Group {ℓ}) → isSet ⟨ G ⟩
isSetGroup G = GroupStr.isGroup (snd G) .IsGroup.isMonoid .IsMonoid.isSemigroup .IsSemigroup.is-set

makeIsGroup : {G : Type ℓ} {0g : G} {_+_ : G → G → G} { -_ : G → G}
              (is-setG : isSet G)
              (assoc : (x y z : G) → x + (y + z) ≡ (x + y) + z)
              (rid : (x : G) → x + 0g ≡ x)
              (lid : (x : G) → 0g + x ≡ x)
              (rinv : (x : G) → x + (- x) ≡ 0g)
              (linv : (x : G) → (- x) + x ≡ 0g)
            → IsGroup 0g _+_ -_
IsGroup.isMonoid (makeIsGroup is-setG assoc rid lid rinv linv) = makeIsMonoid is-setG assoc rid lid
IsGroup.inverse (makeIsGroup is-setG assoc rid lid rinv linv) = λ x → rinv x , linv x

makeGroup : {G : Type ℓ} (0g : G) (_+_ : G → G → G) (-_ : G → G)
            (is-setG : isSet G)
            (assoc : (x y z : G) → x + (y + z) ≡ (x + y) + z)
            (rid : (x : G) → x + 0g ≡ x)
            (lid : (x : G) → 0g + x ≡ x)
            (rinv : (x : G) → x + (- x) ≡ 0g)
            (linv : (x : G) → (- x) + x ≡ 0g)
          → Group
makeGroup 0g _+_ -_ is-setG assoc rid lid rinv linv = _ , helper
  where
  helper : GroupStr _
  GroupStr.0g helper = 0g
  GroupStr._+_ helper = _+_
  GroupStr.- helper = -_
  GroupStr.isGroup helper = makeIsGroup is-setG assoc rid lid rinv linv

makeGroup-right : ∀ {ℓ} {A : Type ℓ}
  → (id : A)
  → (comp : A → A → A)
  → (inv : A → A)
  → (set : isSet A)
  → (assoc : ∀ a b c → comp a (comp b c) ≡ comp (comp a b) c)
  → (rUnit : ∀ a → comp a id ≡ a)
  → (rCancel : ∀ a → comp a (inv a) ≡ id)
  → Group
makeGroup-right id comp inv set assoc rUnit rCancel =
  makeGroup id comp inv set assoc rUnit lUnit rCancel lCancel
  where
    _⨀_ = comp
    abstract
      lCancel : ∀ a → comp (inv a) a ≡ id
      lCancel a =
        inv a ⨀ a
          ≡⟨ sym (rUnit (comp (inv a) a))  ⟩
        (inv a ⨀ a) ⨀ id
          ≡⟨ cong (comp (comp (inv a) a)) (sym (rCancel (inv a))) ⟩
        (inv a ⨀ a) ⨀ (inv a ⨀ (inv (inv a)))
          ≡⟨ assoc _ _ _ ⟩
        ((inv a ⨀ a) ⨀ (inv a)) ⨀ (inv (inv a))
          ≡⟨ cong (λ □ → □ ⨀ _) (sym (assoc _ _ _)) ⟩
        (inv a ⨀ (a ⨀ inv a)) ⨀ (inv (inv a))
          ≡⟨ cong (λ □ → (inv a ⨀ □) ⨀ (inv (inv a))) (rCancel a) ⟩
        (inv a ⨀ id) ⨀ (inv (inv a))
          ≡⟨ cong (λ □ → □ ⨀ (inv (inv a))) (rUnit (inv a)) ⟩
        inv a ⨀ (inv (inv a))
          ≡⟨ rCancel (inv a) ⟩
        id
          ∎

      lUnit : ∀ a → comp id a ≡ a
      lUnit a =
        id ⨀ a
          ≡⟨ cong (λ b → comp b a) (sym (rCancel a)) ⟩
        (a ⨀ inv a) ⨀ a
          ≡⟨ sym (assoc _ _ _) ⟩
        a ⨀ (inv a ⨀ a)
          ≡⟨ cong (comp a) (lCancel a) ⟩
        a ⨀ id
          ≡⟨ rUnit a ⟩
        a
          ∎

makeGroup-left : ∀ {ℓ} {A : Type ℓ}
  → (id : A)
  → (comp : A → A → A)
  → (inv : A → A)
  → (set : isSet A)
  → (assoc : ∀ a b c → comp a (comp b c) ≡ comp (comp a b) c)
  → (lUnit : ∀ a → comp id a ≡ a)
  → (lCancel : ∀ a → comp (inv a) a ≡ id)
  → Group
makeGroup-left id comp inv set assoc lUnit lCancel =
  makeGroup id comp inv set assoc rUnit lUnit rCancel lCancel
  where
    abstract
      rCancel : ∀ a → comp a (inv a) ≡ id
      rCancel a =
        comp a (inv a)
          ≡⟨ sym (lUnit (comp a (inv a)))  ⟩
        comp id (comp a (inv a))
          ≡⟨ cong (λ b → comp b (comp a (inv a))) (sym (lCancel (inv a))) ⟩
        comp (comp (inv (inv a)) (inv a)) (comp a (inv a))
          ≡⟨ sym (assoc (inv (inv a)) (inv a) (comp a (inv a))) ⟩
        comp (inv (inv a)) (comp (inv a) (comp a (inv a)))
          ≡⟨ cong (comp (inv (inv a))) (assoc (inv a) a (inv a)) ⟩
        comp (inv (inv a)) (comp (comp (inv a) a) (inv a))
          ≡⟨ cong (λ b → comp (inv (inv a)) (comp b (inv a))) (lCancel a) ⟩
        comp (inv (inv a)) (comp id (inv a))
          ≡⟨ cong (comp (inv (inv a))) (lUnit (inv a)) ⟩
        comp (inv (inv a)) (inv a)
          ≡⟨ lCancel (inv a) ⟩
        id
          ∎

      rUnit : ∀ a → comp a id ≡ a
      rUnit a =
        comp a id
          ≡⟨ cong (comp a) (sym (lCancel a)) ⟩
        comp a (comp (inv a) a)
          ≡⟨ assoc a (inv a) a ⟩
        comp (comp a (inv a)) a
          ≡⟨ cong (λ b → comp b a) (rCancel a) ⟩
        comp id a
          ≡⟨ lUnit a ⟩
        a
          ∎

open GroupStr hiding (0g ; _+_ ; -_)

isSetCarrier : ∀ {ℓ} → (G : Group {ℓ}) → isSet ⟨ G ⟩
isSetCarrier G = IsSemigroup.is-set (IsMonoid.isSemigroup (GroupStr.isMonoid (snd G)))

open GroupStr

dirProd : ∀ {ℓ ℓ'} → Group {ℓ} → Group {ℓ'} → Group
dirProd (GC , G) (HC , H) =
  makeGroup (0g G , 0g H)
            (λ { (x1 , x2) (y1 , y2) → _+_ G x1 y1 , _+_ H x2 y2 })
            (λ { (x1 , x2) → -_ G x1 , -_ H x2 })
            (isSet× (isSetCarrier (GC , G)) (isSetCarrier (HC , H)))
            (λ { (x1 , x2) (y1 , y2) (z1 , z2) i →
               assoc G x1 y1 z1 i , assoc H x2 y2 z2 i })
            (λ { (x1 , x2) i → GroupStr.rid G x1 i , GroupStr.rid H x2 i })
            (λ { (x1 , x2) i → GroupStr.lid G x1 i , GroupStr.lid H x2 i })
            (λ { (x1 , x2) i → GroupStr.invr G x1 i , GroupStr.invr H x2 i })
            (λ { (x1 , x2) i → GroupStr.invl G x1 i , GroupStr.invl H x2 i })

trivialGroup : Group₀
trivialGroup = Unit , groupstr tt (λ _ _ → tt) (λ _ → tt)
                      (makeIsGroup isSetUnit (λ _ _ _ → refl) (λ _ → refl) (λ _ → refl)
                                   (λ _ → refl) (λ _ → refl))

intGroup : Group₀
intGroup = Int , groupstr 0 _+Int_ (0 -Int_)
                 (makeIsGroup isSetInt +-assoc (λ x → refl) (λ x → +-comm 0 x)
                              (λ x → +-comm x (pos 0 -Int x) ∙ minusPlus x 0) (λ x → minusPlus x 0))


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

--------------------------------------------------------------------------------
-- Simple implementation of sets of ℕ.
--------------------------------------------------------------------------------

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

module Data.Nat.Table where

open import Data.Nat as ℕ using (ℕ; suc; zero)
open import Data.List as List using (List; _∷_; [])
open import Function

Table : Set
Table = List ℕ

para : ∀ {a b} {A : Set a} {B : Set b} → (A → List A → B → B) → B → List A → B
para f b [] = b
para f b (x ∷ xs) = f x xs (para f b xs)

insert : ℕ → Table → Table
insert x xs = para (go zero) (_∷ []) xs x
  where
  go : ℕ → ℕ → Table → (ℕ → Table) → ℕ → Table
  go k zero    xs g zero    = k ∷ xs
  go k zero    xs g (suc i) = k ∷ g i
  go k (suc x) xs g zero    = k ∷ x ∷ xs
  go k (suc x) xs g (suc i) = go (suc k) x xs g i

open import Data.Maybe as Maybe using (Maybe; just; nothing)

member : ℕ → Table → Maybe ℕ
member x xs = List.foldr go (const (const nothing)) xs x 0
  where
  go : ℕ → (ℕ → ℕ → Maybe ℕ) → ℕ → ℕ → Maybe ℕ
  go zero    ys zero    k = just k
  go zero    ys (suc x) k = ys x (suc k)
  go (suc y) ys zero    _ = nothing
  go (suc y) ys (suc x) k = go y ys x k

fromList : List ℕ → Table
fromList = List.foldr insert []

toList : Table → List ℕ
toList = tail′ ∘ List.map ℕ.pred ∘ List.scanl (λ x y → suc (y ℕ.+ x)) 0
  where
  tail′ : List ℕ → List ℕ
  tail′ [] = []
  tail′ (_ ∷ xs) = xs

private
  open import Relation.Binary.PropositionalEquality
  example₁ : fromList (4 ∷ 3 ∷ 1 ∷ 0 ∷ 2 ∷ []) ≡ (0 ∷ 0 ∷ 0 ∷ 0 ∷ 0 ∷ [])
  example₁ = refl

  example₂ : member 3 (fromList (4 ∷ 3 ∷ 1 ∷ 0 ∷ 2 ∷ [])) ≡ just 3
  example₂ = refl

  example₃ : toList (fromList (4 ∷ 3 ∷ 1 ∷ 0 ∷ 2 ∷ [])) ≡ (0 ∷ 1 ∷ 2 ∷ 3 ∷ 4 ∷ [])
  example₃ = refl


module UnquoteDef where

open import Common.Reflection
open import Common.Prelude

module Target where
  mutual
    even : Nat → Bool
    even zero    = true
    even (suc n) = odd n

    odd : Nat → Bool
    odd zero    = false
    odd (suc n) = even n

pattern `false = con (quote false) []
pattern `true  = con (quote true) []
pattern `zero  = con (quote zero) []
pattern `suc n = con (quote suc) (vArg n ∷ [])

pattern _`→_ a b = pi (vArg a) (abs "A" b)
pattern `Nat = def (quote Nat) []
pattern `Bool = def (quote Bool) []

`idType = `Nat `→ `Nat

-- Simple non-mutual case
`id : QName → FunDef
`id f = funDef `idType
        ( clause (vArg `zero            ∷ []) `zero
        ∷ clause (vArg (`suc (var "n")) ∷ []) (`suc (def f (vArg (var 0 []) ∷ [])))
        ∷ [])

`idDef : QName → List Clause
`idDef f =
   clause (vArg `zero            ∷ []) `zero
 ∷ clause (vArg (`suc (var "n")) ∷ []) (`suc (def f (vArg (var 0 []) ∷ [])))
 ∷ []

-- id : Nat → Nat
-- unquoteDef id = `idDef id

-- Now for the mutal ones
`evenOdd : Term → QName → List Clause
`evenOdd base step =
    clause (vArg `zero            ∷ []) base
  ∷ clause (vArg (`suc (var "n")) ∷ []) (def step (vArg (var 0 []) ∷ []))
  ∷ []

_>>_ : TC ⊤ → TC ⊤ → TC ⊤
m >> m₁ = bindTC m λ _ → m₁

even odd : Nat → Bool
unquoteDef even odd =
  defineFun even (`evenOdd `true  odd) >>
  defineFun odd  (`evenOdd `false even)


{-# OPTIONS --no-universe-polymorphism #-}
open import Induction.WellFounded as WF
open import Data.Product
open import Relation.Binary.Core
open import Relation.Unary as U using (Decidable)
open import Relation.Nullary
import Level as L using (zero)

module DivideEtImpera where
{-- This module implements a quite general version of Divide and Conquer as desribed by Douglas R. Smith in his paper
   "The Design of Divide and Conquer Algorithms" 1984. the function makeD&C requires a well founded relation, a decidable proposition
   of whether a given input is primitive, a composition and a decomposition function, a special function g and a function dirSolve.
   All of these functions have to fullfill certain conditions and lastly you also need an "induction principle" that describes that these
   functions work well together. After putting in all this work, you can finally reap your reward: an algorithm that does what you want:
   takes inputs that satisfy the input condition and returns results that satisfy the output condition.
 --}

{-- these 2 functions should be somewhere in the agda standard libraries but i couln't find them so i just wrote them again --}

foldAcc : {A : Set} → {_<_ : Rel A L.zero} → {R : A → Set} →
          (step : (x : A) → ((y : A) → _<_ y x → R y) → R x) →
          (z : A) → Acc _<_ z → R z
foldAcc step z (acc a) = step z (λ y y<z → foldAcc step y (a y y<z))

wfInd : {A : Set} {_<_ : Rel A _} → (WellFounded _<_) →
        {R : A → Set} →
        (step : (x : A) → ((y : A) → _<_ y x → R y) → R x) →
        (z : A) → R z
wfInd wf step z = foldAcc step z (wf z)

{- the algorithm itself as one huge function:
 instead of requiring all the functions and then five axioms describing their behaiviour, i use
 dependent types to embedd four of the axions in therespective function they describe and
 only the last one (called "lemma" here) as an extra. See the paper for details -}

makeD&C1 : {D₁ R₁ D₂ R₂ : Set} {I₁ : D₁ → Set} {O₁ : D₁ → R₁ → Set} {I₂ : D₂ → Set}
           {O₂ : D₂ → R₂ → Set} {O₃ : D₁ → (D₂ × D₁) → Set} {O₄ : (R₂ × R₁) → R₁ → Set}
           {_<_ : Rel D₁ _} → (wf : Well-founded _<_) → {Prim : D₁ → Set} → (pDec : U.Decidable Prim) →
           (decomp : (x : D₁) → I₁ x → ¬ Prim x → Σ (D₂ × D₁)
                   λ y → I₂ (proj₁ y) × (I₁ (proj₂ y)) × (_<_ (proj₂ y) x) × (O₃ x y) ) →
           (comp : (u : (R₂ × R₁)) → Σ R₁ (O₄ u)) →
           (g : (x : D₂) → I₂ x → Σ R₂ (O₂ x)) →
           (dirSolve : (x : D₁) → I₁ x → Prim x → Σ R₁ (O₁ x)) →
           (lemma : ∀{x₀ x₁ x₂ z₀ z₁ z₂} → O₃ x₀ (x₁ , x₂) → O₂ x₁ z₁ → O₁ x₂ z₂ → O₄ (z₁ , z₂) z₀ → O₁ x₀ z₀) →
           (x : D₁) → I₁ x → Σ R₁ (O₁ x)

makeD&C1 {D₁ = D₁} {R₁ = R₁} {I₁ = I₁} {O₁ = O₁} {_<_ = _<_} wf {Prim} pDec decomp comp g dirSolve lemma x = wfInd wf step x where
                                                step : (x : D₁) → ((y : D₁) → _<_ y x → I₁ y → Σ R₁ (O₁ y)) → I₁ x → Σ R₁ (O₁ x)
                                                step x rec ix with (pDec x)
                                                step x rec ix | (yes pfY) = dirSolve x ix pfY
                                                step x rec ix | (no  pfN) = let dec = decomp x ix pfN
                                                                                decV  = proj₁ dec
                                                                                decV1 = proj₁ decV
                                                                                decV2 = proj₂ decV
                                                                                decP  = proj₂ dec
                                                                                decP1 = proj₁ decP
                                                                                decP2 = proj₁ (proj₂ decP)
                                                                                decP3 = proj₁ (proj₂ (proj₂ decP))
                                                                                decP4 = proj₂ (proj₂ (proj₂ decP))
                                                                                gRes  = g decV1 decP1
                                                                                gV    = proj₁ gRes
                                                                                gP    = proj₂ gRes
                                                                                fRes  = rec decV2 decP3 decP2
                                                                                fV    = proj₁ fRes
                                                                                fP    = proj₂ fRes
                                                                                com   = comp (gV , fV)
                                                                                comV  = proj₁ com
                                                                                comP  = proj₂ com
                                                                                proof = lemma decP4 gP fP comP
                                                                             in (comV , proof)

{- it occurred to me, that a proper Divide and conquer algorithm should make (at least) two recursive calls,
   the algorithm describen inthe pape, however, only makes one! So slightly modified it to fit in that extra
   recursive calls but left the basic principle exactly the same in this second version -}

makeD&C2 : {D₁ R₁ D₂ R₂ : Set} {I₁ : D₁ → Set} {O₁ : D₁ → R₁ → Set} {I₂ : D₂ → Set}
                 {O₂ : D₂ → R₂ → Set} {O₃ : D₁ → (D₂ × (D₁ × D₁)) → Set} {O₄ : (R₂ × (R₁ × R₁)) → R₁ → Set}
                 {_<_ : Rel D₁ _} → (wf : Well-founded _<_) → {Prim : D₁ → Set} → (pDec : U.Decidable Prim) →
                 (decomp : (x : D₁) → I₁ x → ¬ Prim x → Σ (D₂ × (D₁ × D₁))
                           λ y → (I₂ (proj₁ y)) × (I₁ (proj₁ (proj₂ y))) × (I₁ (proj₂ (proj₂ y))) ×
                                 (_<_ (proj₁ (proj₂ y)) x) × (_<_ (proj₂ (proj₂ y)) x) × (O₃ x y) ) →
                 (comp : (u : (R₂ × (R₁ × R₁)) ) → Σ R₁ (O₄ u)) →
                 (g : (x : D₂) → I₂ x → Σ R₂ (O₂ x)) →
                 (dirSolve : (x : D₁) → I₁ x → Prim x → Σ R₁ (O₁ x)) →
                 (lemma : ∀{x₀ x₁ x₂ x₃ z₀ z₁ z₂ z₃} → O₃ x₀ (x₁ , (x₂ , x₃ )) → O₂ x₁ z₁ → O₁ x₂ z₂
                             → O₁ x₃ z₃ → O₄ (z₁ , (z₂ , z₃)) z₀ → O₁ x₀ z₀) →
                 (x : D₁) → I₁ x → Σ R₁ (O₁ x)

makeD&C2 {D₁ = D₁} {R₁ = R₁} {I₁ = I₁} {O₁ = O₁} {_<_ = _<_} wf {Prim} pDec decomp comp g dirSolve lemma x = wfInd wf step x where
                                                step : (x : D₁) → ((y : D₁) → _<_ y x → I₁ y → Σ R₁ (O₁ y)) → I₁ x → Σ R₁ (O₁ x)
                                                step x rec ix with (pDec x)
                                                step x rec ix | (yes pfY) = dirSolve x ix pfY
                                                step x rec ix | (no  pfN) = let dec = decomp x ix pfN
                                                                                decV = proj₁ dec
                                                                                decV1 = proj₁ decV
                                                                                decV2 = proj₁ (proj₂ decV)
                                                                                decV3 = proj₂ (proj₂ decV)
                                                                                decP  = proj₂ dec
                                                                                decP1 = proj₁ decP
                                                                                decP2 = proj₁ (proj₂ decP)
                                                                                decP3 = proj₁ (proj₂ (proj₂ decP))
                                                                                decP4 = proj₁ (proj₂ (proj₂ (proj₂ decP)))
                                                                                decP5 = proj₁ (proj₂ (proj₂ (proj₂ (proj₂ decP))))
                                                                                decP6 = proj₂ (proj₂ (proj₂ (proj₂ (proj₂ decP))))
                                                                                gRes  = g decV1 decP1
                                                                                gV    = proj₁ gRes
                                                                                gP    = proj₂ gRes
                                                                                f1Res = rec decV2 decP4 decP2
                                                                                f1V   = proj₁ f1Res
                                                                                f1P   = proj₂ f1Res
                                                                                f2Res = rec decV3 decP5 decP3
                                                                                f2V   = proj₁ f2Res
                                                                                f2P   = proj₂ f2Res
                                                                                com   = comp (gV , (f1V , f2V))
                                                                                comV  = proj₁ com
                                                                                comP  = proj₂ com
                                                                                proof = lemma decP6 gP f1P f2P comP
                                                                            in (comV , proof)


module FunClause where

f
  : {A : Set}
  → A
  → A
f x
  = y
  where
    y = x
    z = x

data List
  (A : Set)
  : Set
  where

  nil
    : List A

  cons
    : A
    → List A
    → List A

snoc
  : {A : Set}
  → List A
  → A
  → List A
snoc nil y
  = cons y nil
snoc (cons x xs) y
  = cons x (snoc xs y)


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

open import lib.Basics -- hiding (_=⟨_⟩_ ; _∎)
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Unit
open import lib.types.Nat
open import lib.types.TLevel
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.NType2
open import lib.PathGroupoid

open import nicolai.pseudotruncations.Preliminary-definitions

module nicolai.pseudotruncations.Liblemmas where

-- transport along constant family
transport-const-fam : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A}
                → (p : a₁ == a₂) → (b : B) → transport (λ _ → B) p b == b
transport-const-fam idp b = idp

-- interaction of transport and ap
trans-ap : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A}
           → (f g : A → B) → (p : a₁ == a₂) → (q : f a₁ == g a₁)
           → transport (λ x → f x == g x) p q == ! (ap f p) ∙ q ∙ (ap g p)
trans-ap f g idp q = ! (∙-unit-r q)

-- special interaction of transport and ap, where the second map is constant at a point
trans-ap₁ : ∀ {i j} {A : Type i} {B : Type j} (f : A → B) {a₁ a₂ : A} (b : B)
            (p : a₁ == a₂) (q : f a₁ == b)
            → transport (λ a → f a == b) p q == ! (ap f p) ∙ q
trans-ap₁ f b idp q = idp

-- first map is constant at a point
trans-ap₂ : ∀ {i j} {A : Type i} {B : Type j} (g : A → B) {a₁ a₂ : A} (b : B)
            (p : a₁ == a₂) (q : b == g a₁)
            → transport (λ a → b == g a) p q == q ∙ ap g p
trans-ap₂ g b idp q = !( ∙-unit-r _)

-- if f is weakly constant, then so is ap f
ap-const : ∀ {i j} {A : Type i} {B : Type j} (f : A → B)
           → wconst f → {a₁ a₂ : A} → wconst (ap f {x = a₁} {y = a₂})
ap-const {A = A} f wc p q = calc-ap p ∙ ! (calc-ap q) where
  calc-ap : {a₁ a₂ : A} → (p : a₁ == a₂) → ap f p == wc a₁ a₂ ∙ ! (wc a₂ a₂)
  calc-ap idp = ! (!-inv-r (wc _ _))

-- in particular, if f is weakly constant, then ap f maps loops to 'refl'
ap-const₁ : ∀ {i j} {A : Type i} {B : Type j} (f : A → B)
           → wconst f → {a₁ : A} → (p : a₁ == a₁) → ap f p == idp
ap-const₁ f wc p = ap-const f wc p idp

-- if f is constant at a point, it maps every path to 'refl'
ap-const-at-point : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A}
                    (b : B) (p : a₁ == a₂) → ap (λ _ → b) p == idp
ap-const-at-point b idp = idp

{- this lemma is ad-hoc; it could be proved as a concatenation of
   many library lemmas, but it would be much more tedious to do -}
adhoc-lemma : ∀ {i} {A : Type i} {x y z : A}
                (p : x == y)
                (q : z == y)
                (r : z == x)
                → p ∙ ! q ∙ r == idp
                → p == ! r ∙ q
adhoc-lemma p idp idp e = ! (∙-unit-r p) ∙ e

{- If f is weakly constant, then so is ap f. This is a lemma from our
   old Hedberg article. -}
ap-wconst : ∀ {i j} {A : Type i} {B : Type j} (f : A → B) (w : wconst f)
            → {a₁ a₂ : A} → wconst (ap f {a₁} {a₂})
ap-wconst f w p q = lemma p ∙ ! (lemma q) where
  lemma : ∀ {x y} (p : x == y) → ap f {x} {y} p == ! (w x x) ∙ (w x y)
  lemma {x} idp = ! (!-inv-l (w x x))

-- Silly little lemma (is it in the library?)
ap-fst : ∀ {i j} {A : Type i} {B : Type j} {a₁ a₂ : A} {b₁ b₂ : B}
           (p : a₁ == a₂) (q : b₁ == b₂)
           → ap fst (pair×= p q) == p
ap-fst idp idp = idp

{- An ad-hoc lemma. Whenever this appears, one could (should, to be honest)
   use library lemmas, but it's just so much more convenient to formulate it
   and pattern match... -}
adhoc-=-eqv : ∀ {i} {A : Type i} {x y : A} (p : y == x) (q : y == x)
                → (! p ∙ q == idp) ≃ (p == q)
adhoc-=-eqv idp  q = !-equiv

{- Another ad-hoc equality; it could be proved easily with many nested
   library lemmas -}
multi-cancelling : ∀ {i} {A : Type i} {x y z w : A} (p : y == x) (q : y == z) (r : x == w)
                   → (! p) ∙ q ∙ (! q) ∙ p ∙ r == r
multi-cancelling idp idp r = idp


module Avionics.SafetyEnvelopes.ExtInterface where

open import Data.Bool using (Bool)
open import Data.Float using (Float)
open import Data.List using (List; map)
open import Data.Maybe using (Maybe; just; nothing)
open import Data.Product using (_×_; _,_)

open import Avionics.Probability using (Dist; NormalDist; ND)
open import Avionics.Real renaming (fromFloat to ff; toFloat to tf)
open import Avionics.SafetyEnvelopes using (z-predictable'; sample-z-predictable)

open import ExtInterface.Data.Maybe using (just; nothing) renaming (Maybe to ExtMaybe)
open import ExtInterface.Data.Product as Ext using (⟨_,_⟩)

fromFloats-z-predictable : List (Float Ext.× Float) → Float → Float → ExtMaybe (Float Ext.× Bool)
fromFloats-z-predictable means×stds z x =
    let
      ndists = map (λ{⟨ mean , std ⟩ → ND (ff mean) (ff std)}) means×stds
      (m , b) = z-predictable' ndists (ff z) (ff x)
    in
      just ⟨ tf m , b ⟩
{-# COMPILE GHC fromFloats-z-predictable as zPredictable #-}

fromFloats-sample-z-predictable :
    List (Float Ext.× Float)
    → Float → Float → List Float → ExtMaybe (Float Ext.× Float Ext.× Bool)
fromFloats-sample-z-predictable means×stds zμ zσ xs =
    let
      ndists = map (λ{⟨ mean , std ⟩ → ND (ff mean) (ff std)}) means×stds
    in
      return (sample-z-predictable ndists (ff zμ) (ff zσ) (map ff xs))
  where
    return : Maybe (ℝ × ℝ × Bool) → ExtMaybe (Float Ext.× Float Ext.× Bool)
    return nothing = nothing
    return (just (m' , v' , b)) = just ⟨ tf m' , ⟨ tf v' , b ⟩ ⟩
{-# COMPILE GHC fromFloats-sample-z-predictable as sampleZPredictable #-}


module Oscar.Property.Equivalence where

open import Oscar.Function
open import Oscar.Level
open import Oscar.Property.Reflexivity
open import Oscar.Property.Symmetry
open import Oscar.Property.Transitivity

record Equivalence {𝔬} {⋆ : Set 𝔬} {𝔮} (_≋_ : ⋆ → ⋆ → Set 𝔮) : Set (𝔬 ⊔ 𝔮) where
  field
    ⦃ ′reflexivity ⦄ : Reflexivity _≋_
    ⦃ ′symmetry ⦄ : Symmetry _≋_
    ⦃ ′transitivity ⦄ : Transitivity _≋_

open Equivalence ⦃ … ⦄ public

instance
  Equivalence⋆ : ∀
    {𝔬} {⋆ : Set 𝔬} {𝔮} {_≋_ : ⋆ → ⋆ → Set 𝔮}
    ⦃ _ : Reflexivity _≋_ ⦄
    ⦃ _ : Symmetry _≋_ ⦄
    ⦃ _ : Transitivity _≋_ ⦄
    → Equivalence _≋_
  Equivalence.′reflexivity Equivalence⋆ = it
  Equivalence.′symmetry Equivalence⋆ = it
  Equivalence.′transitivity Equivalence⋆ = it


------------------------------------------------------------------------
-- The "terminates" relation
------------------------------------------------------------------------

open import Atom

module Termination (atoms : χ-atoms) where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude

open import Chi           atoms
open import Deterministic atoms
open import Propositional atoms

-- Terminates e means that e terminates with a value.

Terminates : Exp → Type
Terminates p = ∃ λ v → p ⇓ v

-- This relation is propositional.

Terminates-propositional :
  ∀ {e} →
  Is-proposition (Terminates e)
Terminates-propositional (_ , e₁) (_ , e₂) =
  Σ-≡,≡→≡ (⇓-deterministic e₁ e₂)
          (⇓-propositional _ _)


------------------------------------------------------------------------
-- Various definitions of "true infinitely often", and some proofs
-- showing that this property commutes with binary sums (in the
-- double-negation monad, and sometimes with extra assumptions)
------------------------------------------------------------------------

module InfinitelyOften where

open import Algebra
open import Axiom.ExcludedMiddle
open import Category.Monad
open import Codata.Musical.Notation
open import Data.Empty
open import Data.Nat
import Data.Nat.Properties as NatProp
open import Data.Product as Prod hiding (map)
open import Data.Sum hiding (map)
open import Data.Unit using (tt)
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence
  using (_⇔_; equivalence; module Equivalence)
import Function.Related as Related
open import Level using (Lift; lift; lower)
open import Relation.Binary hiding (_⇒_; _⇔_)
open import Relation.Nullary
open import Relation.Nullary.Negation
import Relation.Nullary.Universe as Univ
open import Relation.Unary hiding (_⇒_)
private
  open module M {f} = RawMonad {f = f} ¬¬-Monad
  module NatOrder   = DecTotalOrder NatProp.≤-decTotalOrder
  module NatLattice = DistributiveLattice
                        NatProp.⊓-⊔-distributiveLattice

------------------------------------------------------------------------
-- Above

module Above where

  -- P Above i holds if P holds somewhere above i (perhaps at i
  -- itself).

  infix 4 _Above_

  _Above_ : (ℕ → Set) → (ℕ → Set)
  P Above i = ∃ λ j → i ≤ j × P j

  -- Conversion lemma.

  move-suc : ∀ {P i} → P Above suc i ⇔ (P ∘ suc) Above i
  move-suc {P} {i} = equivalence ⇒ ⇐
    where
    ⇒ : P Above suc i → (P ∘ suc) Above i
    ⇒ (.(1 + j) , s≤s {n = j} i≤j , P[1+j]) = (j , i≤j , P[1+j])

    ⇐ : (P ∘ suc) Above i → P Above suc i
    ⇐ (j , i≤j , P[1+j]) = (1 + j , s≤s i≤j , P[1+j])

  -- A closure lemma.

  stable-up :
    ∀ {P} →
    (∀ i → Stable (P Above i)) → (∀ i → Stable ((P ∘ suc) Above i))
  stable-up stable i ¬¬P∘suc⇑i =
    Equivalence.to move-suc ⟨$⟩
      stable (1 + i) (_⟨$⟩_ (Equivalence.from move-suc) <$> ¬¬P∘suc⇑i)

------------------------------------------------------------------------
-- Has upper bound

module Has-upper-bound where

  open Above using (_Above_)

  -- P Has-upper-bound i means that P does not hold for any j ≥ i.

  infix 4 _Has-upper-bound_

  _Has-upper-bound_ : (ℕ → Set) → (ℕ → Set)
  P Has-upper-bound i = ∀ j → i ≤ j → ¬ P j

  -- A closure lemma.

  up : ∀ {P i} → P Has-upper-bound i → (P ∘ suc) Has-upper-bound i
  up ¬p = λ j i≤j → ¬p (suc j) (NatProp.≤-step i≤j)

  -- A conversion lemma.

  move-suc : ∀ {P i} →
             P Has-upper-bound suc i ⇔ (P ∘ suc) Has-upper-bound i
  move-suc {P} {i} = equivalence ⇒ ⇐
    where
    ⇒ : P Has-upper-bound suc i → (P ∘ suc) Has-upper-bound i
    ⇒ P↯[1+i] j i≤j P[1+j] = P↯[1+i] (1 + j) (s≤s i≤j) P[1+j]

    ⇐ : (P ∘ suc) Has-upper-bound i → P Has-upper-bound suc i
    ⇐ P∘suc↯i .(1 + j) (s≤s {n = j} i≤j) Pj = P∘suc↯i j i≤j Pj

  -- _Has-upper-bound_ and _Above_ are mutually inconsistent.

  mutually-inconsistent :
    ∀ {P i} → P Has-upper-bound i ⇔ (¬ (P Above i))
  mutually-inconsistent {P} {i} = equivalence ⇒ ⇐
    where
    ⇒ : P Has-upper-bound i → ¬ (P Above i)
    ⇒ P↯i (j , i≤j , Pj) = P↯i j i≤j Pj

    ⇐ : ¬ (P Above i) → P Has-upper-bound i
    ⇐ ¬P⇑i j i≤j Pj = ¬P⇑i (j , i≤j , Pj)

------------------------------------------------------------------------
-- Below

module Below where

  open Above using (_Above_)

  -- P Below i holds if P holds everywhere below i (including at i).

  infix 4 _Below_

  _Below_ : (ℕ → Set) → (ℕ → Set)
  P Below i = ∀ j → j ≤ i → P j

  -- _Below_ P has a comonadic structure (at least if morphism
  -- equality is trivial).

  map : ∀ {P Q} → P ⊆ Q → _Below_ P ⊆ _Below_ Q
  map P⊆Q P⇓i j j≤i = P⊆Q (P⇓i j j≤i)

  counit : ∀ {P} → _Below_ P ⊆ P
  counit P⇓i = P⇓i _ NatOrder.refl

  cojoin : ∀ {P} → _Below_ P ⊆ _Below_ (_Below_ P)
  cojoin P⇓i = λ j j≤i k k≤j → P⇓i _ (NatOrder.trans k≤j j≤i)

  -- _Above_ (_Below_ P) is pointwise equivalent to _Below_ P.

  ⇑⇓⇔⇓ : ∀ {P} i → (_Below_ P) Above i ⇔ P Below i
  ⇑⇓⇔⇓ {P} i = equivalence ⇒ ⇐
    where
    ⇒ : (_Below_ P) Above i → P Below i
    ⇒ (j , i≤j , P⇓j) k k≤i = P⇓j k (NatOrder.trans k≤i i≤j)

    ⇐ : P Below i → (_Below_ P) Above i
    ⇐ P⇓i = (i , NatOrder.refl , P⇓i)

------------------------------------------------------------------------
-- Mixed inductive/coinductive definition of "true infinitely often"

module Mixed where

  open Above using (_Above_)

  -- Inf P means that P is true for infinitely many natural numbers.

  data Inf (P : ℕ → Set) : Set where
    now  : (p : P 0) (inf : ∞ (Inf (P ∘ suc))) → Inf P
    skip :           (inf :    Inf (P ∘ suc) ) → Inf P

  -- Inf commutes with binary sums in the double-negation monad if one
  -- of the predicates satisfies a certain stability condition.

  up : ∀ {P} → Inf P → Inf (P ∘ suc)
  up (now p inf) = ♭ inf
  up (skip  inf) = inf

  filter₁ : ∀ {P Q} → Inf (P ∪ Q) → ¬ ∃ Q → Inf P
  filter₁ (now (inj₁ p) inf) ¬q = now p (♯ filter₁ (♭ inf) (¬q ∘ Prod.map suc id))
  filter₁ (now (inj₂ q) inf) ¬q = ⊥-elim (¬q (0 , q))
  filter₁ (skip         inf) ¬q = skip (filter₁ inf (¬q ∘ Prod.map suc id))

  filter₂ : ∀ {P Q} → (∀ i → Stable (Q Above i)) →
            Inf (P ∪ Q) → ¬ Inf P → Inf Q
  filter₂ {P} {Q} stable p∪q ¬p = helper witness stable p∪q ¬p
    where
    open Related.EquationalReasoning

    witness : ∃ Q
    witness = Prod.map id proj₂ $ stable 0 (
      ¬ (Q Above 0)  ∼⟨ contraposition (Prod.map id (_,_ z≤n)) ⟩
      ¬ ∃ Q          ∼⟨ filter₁ p∪q ⟩
      Inf P          ∼⟨ ¬p ⟩
      ⊥              ∎)

    helper : ∀ {P Q} →
             ∃ Q → (∀ i → Stable (Q Above i)) →
             Inf (P ∪ Q) → ¬ Inf P → Inf Q
    helper (zero  , q) stable p∪q ¬p = now q (♯ filter₂     (Above.stable-up stable) (up p∪q) (¬p ∘ skip))
    helper (suc i , q) stable p∪q ¬p = skip (helper (i , q) (Above.stable-up stable) (up p∪q) (¬p ∘ skip))

  commutes : ∀ {P Q} → (∀ i → Stable (Q Above i)) →
             Inf (P ∪ Q) → ¬ ¬ (Inf P ⊎ Inf Q)
  commutes stable p∪q =
    call/cc λ ¬[p⊎q] →
    return $ inj₂ $ filter₂ stable p∪q (¬[p⊎q] ∘ inj₁)

------------------------------------------------------------------------
-- Alternative inductive/coinductive definition of "true infinitely
-- often"

module Alternative where

  open Mixed using (now; skip)

  -- Always P means that P holds for every natural number.

  data Always (P : ℕ → Set) : Set where
    now : (p : P 0) (next : ∞ (Always (P ∘ suc))) → Always P

  -- Eventually P means that P holds for some natural number.

  data Eventually (P : ℕ → Set) : Set where
    now   : (p : P 0) → Eventually P
    later : (p : Eventually (P ∘ suc)) → Eventually P

  -- Inf P means that P is true for infinitely many natural numbers.

  Inf : (ℕ → Set) → Set
  Inf P = Always (λ n → Eventually (P ∘ _+_ n))

  -- This definition is equivalent to the previous one.

  up : ∀ P → Inf P → Inf (P ∘ suc)
  up _ (now _ inf) = ♭ inf

  equivalent : ∀ {P} → Inf P ⇔ Mixed.Inf P
  equivalent = equivalence ⇒ ⇐
    where
    ⇒ : ∀ {P} → Inf P → Mixed.Inf P
    ⇒ (now p inf) = ⇒′ p (♭ inf)
      where
      ⇒′ : ∀ {P} → Eventually P → Inf (P ∘ suc) → Mixed.Inf P
      ⇒′     (now   p) inf = now p (♯ ⇒ inf)
      ⇒′ {P} (later p) inf = skip (⇒′ p (up (P ∘ suc) inf))

    ⇐ : ∀ {P} → Mixed.Inf P → Inf P
    ⇐ inf = now (eventually inf) (♯ ⇐ (Mixed.up inf))
      where
      eventually : ∀ {P} → Mixed.Inf P → Eventually P
      eventually (now p _)  = now p
      eventually (skip inf) = later (eventually inf)

------------------------------------------------------------------------
-- Functional/inductive definition of "true infinitely often"

module Functional where

  open Mixed using (now; skip)
  open Above using (_Above_)

  -- Inf P means that P is true for infinitely many natural numbers.

  Inf : (ℕ → Set) → Set
  Inf P = ∀ i → P Above i

  -- This definition is equivalent to the ones above.

  up : ∀ {P} → Inf P → Inf (P ∘ suc)
  up ∀iP⇑i i = Equivalence.to Above.move-suc ⟨$⟩ ∀iP⇑i (suc i)

  equivalent : ∀ {P} → Inf P ⇔ Mixed.Inf P
  equivalent = equivalence ⇒ ⇐
    where
    ⇒ : ∀ {P} → Inf P → Mixed.Inf P
    ⇒ {P} inf with inf 0
    ... | (j , _ , p) = helper inf j p
      where
      helper : ∀ {P} → Inf P → ∀ j → P j → Mixed.Inf P
      helper inf zero    p = now p (♯ ⇒ (up inf))
      helper inf (suc n) p = skip (helper (up inf) n p)

    ⇐ : ∀ {P} → Mixed.Inf P → Inf P
    ⇐ (now p inf) zero    = (0 , z≤n , p)
    ⇐ (skip  inf) zero    = Prod.map suc (Prod.map (const z≤n) id) $ ⇐ inf zero
    ⇐ inf         (suc i) = Prod.map suc (Prod.map s≤s         id) $
                              ⇐ (Mixed.up inf) i

  -- Inf is a functor (at least if morphism equality is trivial).

  map : ∀ {P₁ P₂} → P₁ ⊆ P₂ → Inf P₁ → Inf P₂
  map P₁⊆P₂ inf = λ i → Prod.map id (Prod.map id P₁⊆P₂) (inf i)

------------------------------------------------------------------------
-- Definition of "only true finitely often"

module Fin where

  open Mixed using (now; skip)
  open Has-upper-bound using (_Has-upper-bound_)

  -- Fin P means that P is only true for finitely many natural
  -- numbers.

  Fin : (ℕ → Set) → Set
  Fin P = ∃ λ i → P Has-upper-bound i

  -- Fin implies the negation of Mixed.Inf.

  ⇐ : ∀ {P} → Fin P → ¬ Mixed.Inf P
  ⇐ = uncurry ⇐′
    where
    ⇐′ : ∀ {P} i → P Has-upper-bound i → ¬ Mixed.Inf P
    ⇐′ zero    fin (now p inf) = fin 0 z≤n p
    ⇐′ zero    fin (skip  inf) = ⇐′ zero (λ j i≤j → fin (suc j) z≤n) inf
    ⇐′ (suc i) fin inf         =
      ⇐′ i (Equivalence.to Has-upper-bound.move-suc ⟨$⟩ fin)
         (Mixed.up inf)

  -- The other direction (with a double-negated conclusion) implies
  -- that Mixed.Inf commutes with binary sums (in the double-negation
  -- monad).

  filter : ∀ {P Q} → Mixed.Inf (P ∪ Q) → Fin P → Mixed.Inf Q
  filter inf (i , fin) = filter′ inf i fin
    where
    filter′ : ∀ {P Q} →
              Mixed.Inf (P ∪ Q) →
              ∀ i → P Has-upper-bound i → Mixed.Inf Q
    filter′ (now (inj₁ p) inf) 0       fin = ⊥-elim (fin 0 z≤n p)
    filter′ (now (inj₁ p) inf) (suc i) fin = skip (filter′ (♭ inf) i (Equivalence.to Has-upper-bound.move-suc ⟨$⟩ fin))
    filter′ (now (inj₂ q) inf) i       fin = now q (♯ filter′ (♭ inf) i (Has-upper-bound.up fin))
    filter′ (skip         inf) i       fin = skip (filter′ inf i (Has-upper-bound.up fin))

  commutes : ∀ {P Q} → (¬ Mixed.Inf P → ¬ ¬ Fin P) →
             Mixed.Inf (P ∪ Q) → ¬ ¬ (Mixed.Inf P ⊎ Mixed.Inf Q)
  commutes ⇒ p∪q =
    call/cc               λ ¬[p⊎q] →
    ⇒ (¬[p⊎q] ∘ inj₁) >>= λ fin →
    return $ inj₂ (filter p∪q fin)

  -- Fin is preserved by binary sums.

  ∪-preserves : ∀ {P Q} → Fin P → Fin Q → Fin (P ∪ Q)
  ∪-preserves {P} {Q} (i , ¬p) (j , ¬q) = (i ⊔ j , helper)
    where
    open NatProp.≤-Reasoning

    helper : ∀ k → i ⊔ j ≤ k → ¬ (P ∪ Q) k
    helper k i⊔j≤k (inj₁ p) = ¬p k (begin
      i      ≤⟨ NatProp.m≤m⊔n i j ⟩
      i ⊔ j  ≤⟨ i⊔j≤k ⟩
      k      ∎) p
    helper k i⊔j≤k (inj₂ q) = ¬q k (begin
      j      ≤⟨ NatProp.m≤m⊔n j i ⟩
      j ⊔ i  ≡⟨ NatLattice.∧-comm j i ⟩
      i ⊔ j  ≤⟨ i⊔j≤k ⟩
      k      ∎) q

------------------------------------------------------------------------
-- Double-negation shift lemmas

module Double-negation-shift where

  open Below using (_Below_)

  -- General double-negation shift property.

  DNS : (ℕ → Set) → Set
  DNS P = (∀ i → ¬ ¬ P i) → ¬ ¬ (∀ i → P i)

  -- DNS holds for stable predicates.

  Stable⇒DNS : ∀ {P} → (∀ i → Stable (P i)) → DNS P
  Stable⇒DNS stable ∀¬¬P = λ ¬∀P → ¬∀P (λ i → stable i (∀¬¬P i))

  -- DNS follows from excluded middle.

  EM⇒DNS : ∀ {P} → ExcludedMiddle Level.zero → DNS P
  EM⇒DNS {P} em hyp = return hyp′
    where
    hyp′ : ∀ i → P i
    hyp′ i = decidable-stable em (hyp i)

  -- DNS follows from the double-negation of excluded middle.

  ¬¬EM⇒DNS : ∀ {P} → ¬ ¬ ExcludedMiddle Level.zero → DNS P
  ¬¬EM⇒DNS em hyp =
    ¬¬-map lower (em >>= λ em → ¬¬-map lift (EM⇒DNS em hyp))

  -- DNS respects predicate equivalence.

  respects : ∀ {P₁ P₂} → (∀ i → P₁ i ⇔ P₂ i) → DNS P₁ → DNS P₂
  respects P₁⇔P₂ dns ∀i¬¬P₂i ¬∀iP₂i =
    dns (λ i ¬P₁i → ∀i¬¬P₂i i (λ P₂i →
                      ¬P₁i (Equivalence.from (P₁⇔P₂ i) ⟨$⟩ P₂i)))
        (λ ∀iP₁i → ¬∀iP₂i (λ i → Equivalence.to (P₁⇔P₂ i) ⟨$⟩ ∀iP₁i i))

  -- Double-negation shift property restricted to predicates which are
  -- downwards closed.

  DNS⇓ : (ℕ → Set) → Set
  DNS⇓ P =
    (∀ {i j} → i ≥ j → P i → P j) →
    (∀ i → ¬ ¬ P i) → ¬ ¬ (∀ i → P i)

  -- Certain instances of DNS imply other instances of DNS⇓, and vice
  -- versa.

  DNS⇒DNS⇓ : ∀ {P} → DNS (_Below_ P) → DNS⇓ P
  DNS⇒DNS⇓ {P} shift downwards-closed ∀i¬¬Pi =
    _∘_ Below.counit <$> shift (λ i → unit <$> ∀i¬¬Pi i)
    where
    unit : P ⊆ _Below_ P
    unit Pi j j≤i = downwards-closed j≤i Pi

  -- The following lemma is due to Thierry Coquand (but the proof,
  -- including any inelegance, is due to me).

  DNS⇓⇒DNS : ∀ {P} → DNS⇓ (_Below_ P) → DNS P
  DNS⇓⇒DNS {P} shift ∀¬¬P = _∘_ Below.counit <$> ¬¬∀P⇓
    where
    P⇓-downwards-closed : ∀ {i j} → i ≥ j → P Below i → P Below j
    P⇓-downwards-closed i≥j P⇓i = λ j′ j′≤j →
      P⇓i j′ (NatOrder.trans j′≤j i≥j)

    Q : ℕ → Set
    Q i = ∀ {j} → j ≤′ i → P j

    q-zero : P 0 → Q 0
    q-zero P0 ≤′-refl = P0

    q-suc : ∀ {i} → P (suc i) → Q i → Q (suc i)
    q-suc P1+i Qi ≤′-refl       = P1+i
    q-suc P1+i Qi (≤′-step j≤i) = Qi j≤i

    ∀¬¬Q : ∀ i → ¬ ¬ Q i
    ∀¬¬Q zero    = q-zero <$> ∀¬¬P zero
    ∀¬¬Q (suc i) = q-suc  <$> ∀¬¬P (suc i) ⊛ ∀¬¬Q i

    ∀¬¬P⇓ : ∀ i → ¬ ¬ (P Below i)
    ∀¬¬P⇓ i = (λ Qi j j≤i → Qi (NatProp.≤⇒≤′ j≤i)) <$> ∀¬¬Q i

    ¬¬∀P⇓ : ¬ ¬ (∀ i → P Below i)
    ¬¬∀P⇓ = shift P⇓-downwards-closed ∀¬¬P⇓

------------------------------------------------------------------------
-- "Non-constructive" definition of "true infinitely often"

module NonConstructive where

  open Fin using (Fin)
  open Above using (_Above_)
  open Below using (_Below_)
  open Has-upper-bound using (_Has-upper-bound_)
  open Double-negation-shift using (DNS; DNS⇓)

  -- Inf P means that P is true for infinitely many natural numbers.

  Inf : (ℕ → Set) → Set
  Inf P = ¬ Fin P

  -- Inf commutes with binary sums (in the double-negation monad).

  commutes : ∀ {P Q} → Inf (P ∪ Q) → ¬ ¬ (Inf P ⊎ Inf Q)
  commutes p∪q =
    call/cc λ ¬[p⊎q] →
    (λ ¬p ¬q → ⊥-elim (p∪q $ Fin.∪-preserves ¬p ¬q))
      <$> ¬[p⊎q] ∘ inj₁ ⊛ ¬[p⊎q] ∘ inj₂

  -- Inf is a functor (at least if morphism equality is trivial).

  map : ∀ {P₁ P₂} → P₁ ⊆ P₂ → Inf P₁ → Inf P₂
  map P₁⊆P₂ ¬fin = λ fin →
    ¬fin (Prod.map id (λ never j i≤j P₁j → never j i≤j (P₁⊆P₂ P₁j)) fin)

  -- If we have a constructive proof of "true infinitely often", then
  -- we get a "non-constructive" proof as well.

  ⇒ : ∀ {P} → Functional.Inf P → Inf P
  ⇒ inf (i , fin) with inf i
  ... | (j , i≤j , p) = fin j i≤j p

  -- The other direction can be proved iff we have a double-negation
  -- shift lemma.

  Other-direction : (ℕ → Set) → Set
  Other-direction P = Inf P → ¬ ¬ Functional.Inf P

  equivalent₁ : ∀ {P} → Other-direction P ⇔ DNS (_Above_ P)
  equivalent₁ = equivalence ⇒shift shift⇒
    where
    shift⇒ : ∀ {P} → DNS (_Above_ P) → Other-direction P
    shift⇒ shift ¬fin =
      shift (λ i ¬p →
        ¬fin (i , Equivalence.from
                    Has-upper-bound.mutually-inconsistent ⟨$⟩ ¬p))

    ⇒shift : ∀ {P} → Other-direction P → DNS (_Above_ P)
    ⇒shift hyp p =
      hyp (uncurry (λ i fin →
        p i (Equivalence.to
               Has-upper-bound.mutually-inconsistent ⟨$⟩ fin)))

  equivalent₂ : ∀ {P} → Other-direction (_Below_ P) ⇔ DNS P
  equivalent₂ {P} = equivalence ⇒shift shift⇒
    where
    shift⇒ : DNS P → Other-direction (_Below_ P)
    shift⇒ shift inf ¬inf =
      shift (λ i ¬Pi →
               inf (i , λ j i≤j ∀k≤j[Pk] → ¬Pi (∀k≤j[Pk] i i≤j)))
            (λ ∀iPi → ¬inf (λ i → i , NatOrder.refl , λ j j≤i → ∀iPi j))

    ⇒shift : ∀ {P} → Other-direction (_Below_ P) → DNS P
    ⇒shift {P} =
      Other-direction (_Below_ P)                   ∼⟨ (λ other₁ →
        Inf (_Below_ (_Below_ P))                         ∼⟨ map Below.counit ⟩
        Inf (_Below_ P)                                   ∼⟨ other₁ ⟩
        ¬ ¬ Functional.Inf (_Below_ P)                    ∼⟨ _<$>_ (Functional.map Below.cojoin) ⟩
        (¬ ¬ Functional.Inf (_Below_ (_Below_ P)))        ∎) ⟩
      Other-direction (_Below_ (_Below_ P))         ∼⟨ _⟨$⟩_ (Equivalence.to equivalent₁) ⟩
      DNS (_Above_ (_Below_ (_Below_ P)))           ∼⟨ Double-negation-shift.respects Below.⇑⇓⇔⇓ ⟩
      DNS (_Below_ (_Below_ P))                     ∼⟨ Double-negation-shift.DNS⇒DNS⇓ ⟩
      DNS⇓ (_Below_ P)                              ∼⟨ Double-negation-shift.DNS⇓⇒DNS ⟩
      DNS P                                         ∎
      where open Related.EquationalReasoning

  equivalent : (∀ P → Other-direction P) ⇔ (∀ P → DNS P)
  equivalent =
    equivalence (λ other P → _⟨$⟩_ (Equivalence.to   equivalent₂)
                                   (other (_Below_ P)))
                (λ shift P → _⟨$⟩_ (Equivalence.from equivalent₁)
                                   (shift (_Above_ P)))

  -- Some lemmas used below.

  up : ∀ {P} → Inf P → Inf (P ∘ suc)
  up =
    contraposition
      (Prod.map suc (_⟨$⟩_ (Equivalence.from Has-upper-bound.move-suc)))

  witness : ∀ {P} → Inf P → ¬ ¬ ∃ P
  witness ¬fin ¬p = ¬fin (0 , λ i _ Pi → ¬p (i , Pi))

------------------------------------------------------------------------
-- Definition of "true infinitely often" which uses double-negation

module DoubleNegated where

  open Fin using (Fin)
  open Has-upper-bound using (_Has-upper-bound_)

  infixl 4 _⟪$⟫_

  mutual

    -- Inf P means that P is true for infinitely many natural numbers.

    data Inf (P : ℕ → Set) : Set₁ where
      now  : (p : P 0) (inf : ∞ (¬¬Inf (P ∘ suc))) → Inf P
      skip :           (inf :      Inf (P ∘ suc) ) → Inf P

    data ¬¬Inf (P : ℕ → Set) : Set₁ where
      _⟪$⟫_ : {A : Set} (f : A → Inf P) (m : ¬ ¬ A) → ¬¬Inf P

  -- ¬¬Inf is equivalent to the non-constructive definition given
  -- above.

  expand : ∀ {P} → ¬¬Inf P → ¬ ¬ Inf P
  expand (f ⟪$⟫ m) = λ ¬inf → m (¬inf ∘ f)

  ¬¬equivalent : ∀ {P} → NonConstructive.Inf P ⇔ ¬¬Inf P
  ¬¬equivalent = equivalence ⇒ ⇐
    where
    ⇒ : ∀ {P} → NonConstructive.Inf P → ¬¬Inf P
    ⇒ ¬fin = helper ¬fin ⟪$⟫ NonConstructive.witness ¬fin
      where
      helper : ∀ {P} → NonConstructive.Inf P → ∃ P → Inf P
      helper ¬fin (zero  , p) = now p (♯ ⇒ (NonConstructive.up ¬fin))
      helper ¬fin (suc i , p) =
        skip (helper (NonConstructive.up ¬fin) (i , p))

    ⇐ : ∀ {P} → ¬¬Inf P → NonConstructive.Inf P
    ⇐ ¬¬inf (i , fin) = ⇐′ ¬¬inf i fin
      where
      mutual
        ⇐′ : ∀ {P} → ¬¬Inf P → ∀ i → ¬ P Has-upper-bound i
        ⇐′ ¬¬inf i fin = ¬¬-map (helper i fin) (expand ¬¬inf) id

        helper : ∀ {P} → ∀ i → P Has-upper-bound i → ¬ Inf P
        helper i       ¬p (skip  inf)   = helper i (Has-upper-bound.up ¬p) inf
        helper zero    ¬p (now p inf)   = ¬p 0 z≤n p
        helper (suc i) ¬p (now p ¬¬inf) =
          ⇐′ (♭ ¬¬inf) i (λ j i≤j → ¬p (suc j) (s≤s i≤j))

  -- Inf is equivalent to the non-constructive definition given above
  -- (in the double-negation monad).

  ⇐ : ∀ {P} → Inf P → NonConstructive.Inf P
  ⇐ {P} =
    Inf P                  ∼⟨ (λ inf → const inf ⟪$⟫ return tt) ⟩
    ¬¬Inf P                ∼⟨ _⟨$⟩_ (Equivalence.from ¬¬equivalent) ⟩
    NonConstructive.Inf P  ∎
    where open Related.EquationalReasoning

  ⇒ : ∀ {P} → NonConstructive.Inf P → ¬ ¬ Inf P
  ⇒ {P} =
    NonConstructive.Inf P  ∼⟨ _⟨$⟩_ (Equivalence.to ¬¬equivalent) ⟩
    ¬¬Inf P                ∼⟨ expand ⟩
    (¬ ¬ Inf P)            ∎
    where open Related.EquationalReasoning

  equivalent : ∀ {P} → ¬ ¬ (NonConstructive.Inf P ⇔ Inf P)
  equivalent {P} =
    (λ ⇒′ → equivalence (⇒′ ∘ lift) ⇐) <$>
      Univ.¬¬-pull (Univ._⇒_ _ Univ.Id) (λ inf → ⇒ (lower inf))

  -- Inf commutes with binary sums (in the double-negation monad).

  commutes : ∀ {P Q} → Inf (P ∪ Q) → ¬ ¬ (Inf P ⊎ Inf Q)
  commutes {P} {Q} p∪q =
    negated-stable $ ¬¬-map helper $ NonConstructive.commutes (⇐ p∪q)
    where
    helper : NonConstructive.Inf P ⊎ NonConstructive.Inf Q →
             ¬ ¬ (Inf P ⊎ Inf Q)
    helper (inj₁ p) = λ ¬p∪q → ⇒ p (¬p∪q ∘ inj₁)
    helper (inj₂ q) = λ ¬p∪q → ⇒ q (¬p∪q ∘ inj₂)

-- You may wonder why double-negation is introduced in a roundabout
-- way in ¬¬Inf above. The reason is that the more direct definition,
-- used in DoubleNegated₂ below, is not strictly positive. Furthermore
-- DoubleNegated₂.equivalent is not accepted by the termination
-- checker.

{-
module DoubleNegated₂ where

  open DoubleNegated using (now; skip; _⟪$⟫_)

  data Inf (P : ℕ → Set) : Set where
    now  : (p : P 0) (inf : ∞ (¬ ¬ Inf (P ∘ suc))) → Inf P
    skip :           (inf :        Inf (P ∘ suc) ) → Inf P

  equivalent : ∀ {P} → DoubleNegated.Inf P ⇔ Inf P
  equivalent = equivalence ⇒ ⇐
    where
    ⇐ : ∀ {P} → Inf P → DoubleNegated.Inf P
    ⇐ (now p inf) = now p (♯ (⇐ ⟪$⟫ ♭ inf))
    ⇐ (skip  inf) = skip (⇐ inf)

    ⇒ : ∀ {P} → DoubleNegated.Inf P → Inf P
    ⇒ (now p inf) with ♭ inf
    ... | f ⟪$⟫ m = now p (♯ λ ¬inf → m (λ x → ¬inf (⇒ (f x))))
    ⇒ (skip  inf) = skip (⇒ inf)
-}