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

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

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

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

open import Cubical.Induction.WellFounded

open import Cubical.Relation.Nullary

infix 4 _≤_ _<_

_≤_ :     Type₀
zero  _ = Unit
suc m  zero = 
suc m  suc n = m  n

_<_ :     Type₀
m < n = suc m  n

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

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

private
  variable
     : Level
    R : Type 
    P :   Type 
    k l m n : 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

open Minimal using (Decidable→Collapsible) public