{-# OPTIONS --cubical #-}

module Nat where

open import Cubical.Foundations.Prelude
open import Cubical.Relation.Nullary
open import Cubical.Data.Empty as 
open import Cubical.Data.Sum
open import Cubical.Data.Sigma
open import Cubical.Data.Nat public

+1-suc : (n : )  n + 1  suc n
+1-suc zero = refl
+1-suc (suc n) = cong suc (+1-suc n)

data is-even :   Type₀
data is-odd  :   Type₀

data is-even where
  even-zero : is-even zero
  even-suc  : {n : }  is-odd n  is-even (suc n)

data is-odd where
  odd-suc : {n : }  is-even n  is-odd (suc n)

is-even-isProp : (n : )  isProp (is-even n)
is-odd-isProp  : (n : )  isProp (is-odd  n)
is-even-isProp zero even-zero even-zero = refl
is-even-isProp (suc n) (even-suc o) (even-suc o') = cong even-suc (is-odd-isProp n o o')
is-odd-isProp zero ()
is-odd-isProp (suc n) (odd-suc e) (odd-suc e') = cong odd-suc (is-even-isProp n e e')

¬odd-zero : ¬ (is-odd zero)
¬odd-zero ()

odd-pred : {n : }  is-even (suc n)  is-odd n
odd-pred (even-suc o) = o

even-pred : {n : }  is-odd (suc n)  is-even n
even-pred (odd-suc e) = e

parity : (n : )  is-even n  is-odd n
parity zero = inl even-zero
parity (suc n) with parity n
... | inl p = inr (odd-suc p)
... | inr p = inl (even-suc p)

¬even-odd : (n : )  is-even n  is-odd n  
¬even-odd (suc n) even odd = ¬even-odd n (even-pred odd) (odd-pred even)

open import Cubical.Data.Nat.Order
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Induction.WellFounded
open import Cubical.HITs.PropositionalTruncation as ∥∥

-- if there exists a natural number, we can find it!
-- now in Cubical.Data.Nat.Order.Recursive
find : { : Level} (P :   Type )  ((n : )  isProp (P n))  ((n : )  Dec (P n))   Σ  P ∥₁  Σ  P
find P prop dec ex = fst first , fst (snd first)
  where
  isFirst :   Type _
  isFirst n = P n × ((m : )  P m  n  m)
  isFirst-unique : {m n : }  isFirst m  isFirst n  m  n
  isFirst-unique fm fn = ≤-antisym (snd fm _ (fst fn)) (snd fn _ (fst fm))
  isFirst-isProp : (n : )  isProp (isFirst n)
  isFirst-isProp n = isProp× (prop n) (isPropΠ  m  isPropΠ λ _  isProp≤))
  isPropΣ' : { ℓ' : Level} {A : Type } {B : A  Type ℓ'}  ((x : A)  isProp (B x))  ((x y : A)  B x  B y  x  y)  isProp (Σ A B)
  isPropΣ' p e (a , b) (a' , b') = ΣPathP ((e _ _ b b') , (toPathP (p _ _ _)))
  first-isProp : isProp (Σ  isFirst)
  first-isProp = isPropΣ' isFirst-isProp  _ _  isFirst-unique)
  firstUntil : (n : )  Type _
  firstUntil n = Σ  isFirst  ((m : )  m < n  ¬ (P m))
  search-until : (n : )  firstUntil n
  search-until zero = inr λ m m<0 _  ¬-<-zero m<0
  search-until (suc n) with search-until n
  ... | inl f = inl f
  ... | inr ¬P with dec n
  ... | yes Pn = inl (n , Pn , λ m Pm  case n  m return  _  n  m) of λ { (lt n<m)  ≤-trans (≤-suc ≤-refl) n<m ; (eq m≡n)  subst  n  n  m) (sym m≡n) ≤-refl ; (gt n>m)  ⊥.rec (¬P m n>m Pm) })
  ... | no ¬Pn = inr  m m<sn Pm  case <-split m<sn of λ { (inl m<n)  ¬P _ m<n Pm ; (inr m≡n)  ¬Pn (subst P m≡n Pm) })
  search : (n : )  P n  Σ  isFirst
  search n Pn with search-until (suc n)
  ... | inl first = first
  ... | inr ¬P = ⊥.rec (¬P n ≤-refl Pn)
  first : Σ  isFirst
  first = ∥∥.rec first-isProp  { (n , Pn)  search n Pn }) ex

-- decidability of bounded universal quantification
DecΠ≤ : { : Level} (P :   Type )  ((n : )  Dec (P n))  (n : )  Dec ((k : )  k < n  P k)
DecΠ≤ P D zero = yes λ { k k<0  ⊥.rec (¬-<-zero k<0) }
DecΠ≤ P D (suc n) with DecΠ≤ P D n
... | no ¬p = no λ f  ¬p λ k k<n  f k (<-trans k<n ≤-refl)
... | yes p with D n
... | yes pn = yes λ k k<sn  case <-split k<sn of λ { (inl k<n)  p k k<n ; (inr k≡n)  subst P (sym k≡n) pn }
... | no ¬pn = no λ f  ¬pn (f n ≤-refl)