{-# OPTIONS --cubical --no-import-sorts --safe --guardedness #-}
module Cubical.Codata.Conat.Bounded where

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Univalence

open import Cubical.Codata.Conat.Base
  renaming (zero to czero; suc to csuc)
open import Cubical.Codata.Conat.Properties

open import Cubical.Data.Empty as Empty
open import Cubical.Data.Sigma
open import Cubical.Data.Sum hiding (rec)
open import Cubical.Data.Unit

open import Cubical.Relation.Nullary

open import Cubical.Data.Nat as Nat
import Cubical.Data.Fin.Recursive as Fin

private variable  : Level

_≺_ :   Conat  Type _
_≺′_ :   Conat′  Type _

n  c = n ≺′ force c
_ ≺′ czero = 
zero  ≺′ csuc _ = Unit
suc n ≺′ csuc c = n  c

isProp≺ :  n c  isProp (n  c)
isProp≺′ :  n c  isProp (n ≺′ c)
isProp≺ n c = isProp≺′ n (force c)
isProp≺′ n           czero = isProp⊥
isProp≺′ zero    (csuc  _) = isPropUnit
isProp≺′ (suc n) (csuc c') = isProp≺ n c'

isPropDep≺ :  c  isPropDep (_≺ c)
isPropDep≺ c = isOfHLevel→isOfHLevelDep 1  n  isProp≺ n c) {_} {_}

isPropDep≺′ :  c  isPropDep (_≺′ c)
isPropDep≺′ c = isOfHLevel→isOfHLevelDep 1  n  isProp≺′ n c) {_} {_}

private
  apart :     Type
  apart zero zero = 
  apart (suc m) (suc n) = apart m n
  apart _ _ = Unit

  ≢→apart : (i j : )  ¬ i  j  apart i j
  ≢→apart zero zero ¬p = ¬p refl
  ≢→apart (suc i) (suc j) ¬p = ≢→apart i j (¬p  cong suc)
  ≢→apart zero (suc j) _ = _
  ≢→apart (suc i) zero _ = _

  apart→≢ : (i j : )  apart i j  ¬ i  j
  apart→≢ (suc i) zero _ = snotz
  apart→≢ zero (suc j) _ = znots
  apart→≢ (suc i) (suc j) i#j = apart→≢ i j i#j  cong predℕ

  isPropApart :  m n  isProp (apart m n)
  isPropApart 0 0 = isProp⊥
  isPropApart (suc m) (suc n) = isPropApart m n
  isPropApart (suc _) 0 = isPropUnit
  isPropApart 0 (suc _) = isPropUnit

_#_ : ∀{P :   Type }  (l r : Σ  P)  Type
(m , _) # (n , _) = apart m n

#→≢ : ∀{P :   Type }  (l r : Σ  P)  l # r  ¬ l  r
#→≢ (i , _) (j , _) d = apart→≢ i j d  cong fst

isProp# : ∀{P :   Type } (l r : Σ  P)  isProp (l # r)
isProp# (m , _) (n , _) = isPropApart m n

isProp#Depᵣ : ∀{P :   Type } (r : Σ  P)  isPropDep (_# r)
isProp#Depᵣ r = isOfHLevel→isOfHLevelDep 1  l  isProp# l r) {_} {_}

Bounded : Conat  Type
Bounded m = Σ[ n   ] n  m

Bounded′ : Conat′  Type
Bounded′ m = Σ[ n   ] n ≺′ m

discreteB′ :  m  (i j : Bounded′ m)  (i  j)  (i # j)
discreteB′ m (i , i≺m) (j , j≺m) with discreteℕ i j
... | yes p = inl λ i  p i , isPropDep≺′ m i≺m j≺m p i
... | no ¬p = inr (≢→apart i j ¬p)

≺∞ :  n  n  
≺∞ zero = _
≺∞ (suc n) = ≺∞ n

Σ≺∞≃ℕ : Bounded   
Σ≺∞≃ℕ = isoToEquiv λ where
    .fun  fst
    .inv n  n , ≺∞ n
    .rightInv _  refl
    .leftInv (n , p) i  λ where
      .fst  n
      .snd  isProp≺ n  (≺∞ n) p i
  where open Iso

Σ≺∞≡ℕ : Bounded   
Σ≺∞≡ℕ = ua Σ≺∞≃ℕ

_≺?_ :  n c  Dec (n  c)
n ≺? c with force c
_     ≺? c |  czero = no (idfun )
zero  ≺? c | csuc d = yes _
suc n ≺? c | csuc d = n ≺? d

≺-pred :  n c  suc n  c  n  c
≺-pred n c sn≺c with force c
≺-pred    zero c sn≺c | csuc d = _
≺-pred (suc n) c sn≺c | csuc d = ≺-pred n d sn≺c

≺?-yes :  n c  (p : n  c)  n ≺? c  yes p
≺?-yes n c p with force c
≺?-yes   zero  c p | csuc c' = refl
≺?-yes (suc n) c p | csuc c' = ≺?-yes n c' p

∀≺-same :  m n  (∀ k  (k  m)  (k  n))  m  n
∀≺-same m n ∀≺ i .force with force m | force n
... |  czero |  czero = czero
... | csuc o | csuc p = csuc (∀≺-same o p (∀≺  suc) i)
... | csuc o |  czero
  = Empty.rec {A = csuc o  czero} (transport (∀≺ 0) _) i
... |  czero | csuc p
  = Empty.rec {A = czero  csuc p} (transport⁻ (∀≺ 0) _) i

Bounded→Fin :  m  Bounded (embed m)  Fin.Fin m
Bounded→Fin (suc m) (0 , 0≺m) = Fin.zero
Bounded→Fin (suc m) (suc n , n≺m) = Fin.suc (Bounded→Fin m (n , n≺m))

module Untangle
    {m n}
    (f : Bounded′ (csuc m)  Bounded′ (csuc n))
    (g : Bounded′ (csuc n)  Bounded′ (csuc m))
    (rinv : section f g)
    (linv : retract f g)
  where
  bzro : ∀{k}  Bounded′ (csuc k)
  bzro = (zero , _)

  bsuc : ∀{k}  Bounded k  Bounded′ (csuc k)
  bsuc (l , l≺k) = (suc l , l≺k)

  #-f :  v u  v # u  f v # f u
  #-f v u v#u with discreteB′ (csuc n) (f v) (f u)
  ... | inr fv#fu = fv#fu
  ... | inl fv≡fu
    = rec (#→≢ v u v#u (sym (linv v) ∙∙ cong g (fv≡fu) ∙∙ linv u))

  #-g :  v u  v # u  g v # g u
  #-g v u v#u with discreteB′ (csuc m) (g v) (g u)
  ... | inr gv#gu = gv#gu
  ... | inl gv≡gu
    = rec (#→≢ v u v#u (sym (rinv v) ∙∙ cong f (gv≡gu) ∙∙ rinv u))

  #-fg :  v u  v # u  f (g v) # f (g u)
  #-fg v u = #-f (g v) (g u)  #-g v u

  #-gf :  v u  v # u  g (f v) # g (f u)
  #-gf v u = #-g (f v) (f u)  #-f v u

  default : ∀{k}  (v d : Bounded′ (csuc k))  v # d  Bounded k
  default (suc l , l≺n) d _ = (l , l≺n)
  default (0 , _) (suc l , l≺n) _ = (l , l≺n)

  f- : Bounded m  Bounded n
  f- v = default (f (bsuc v)) (f bzro) (#-f (bsuc v) bzro _)

  g- : Bounded n  Bounded m
  g- v = default (g (bsuc v)) (g bzro) (#-g (bsuc v) bzro _)

  g-f-z :  v u  g bzro  bsuc v  g (bsuc u)  bzro  g- u  v
  g-f-z (l , l≺m) u p q with g (bsuc u) | g bzro | #-g (bsuc u) bzro _
  ... | zero , _ | suc k , k≺m | #gf = λ where
    i .fst  predℕ (p i .fst)
    i .snd  isPropDep≺ m k≺m l≺m (cong (predℕ  fst) p) i
  ... | w@(suc k , k≺m) | dg | #gf = rec (snotz (cong fst q))

  g-f-s :  v u  g (bsuc u)  bsuc v  g- u  v
  g-f-s (l , l≺m) u p with g (bsuc u) | #-g (bsuc u) bzro _
  ... | suc k , k≺m | #gf = λ where
    i .fst  predℕ (p i .fst)
    i .snd  isPropDep≺ m k≺m l≺m (cong (predℕ  fst) p) i
  ... |  zero , k≺m | #gf = rec (znots (cong fst p))

  g-f- :  v  g- (f- v)  v
  g-f- v@(i , i≺m)
    with f (bsuc v) | linv (bsuc v) | #-f (bsuc v) bzro _
  ... | suc j , j≺m | p | #f = g-f-s v (j , j≺m) p
  ... | zero , _ | p | #f with f bzro | linv bzro
  ... | suc k , k≺n | q = g-f-z v (k , k≺n) p q

  f-g-z :  v u  f bzro  bsuc v  f (bsuc u)  bzro  f- u  v
  f-g-z (l , l≺n) u p q with f (bsuc u) | f bzro | #-f (bsuc u) bzro _
  ... | zero , _ | suc k , k≺n | #fg = λ where
    i .fst  predℕ (p i .fst)
    i .snd  isPropDep≺ n k≺n l≺n (cong (predℕ  fst) p) i
  ... | w@(suc k , k≺m) | df | #fg = rec (snotz (cong fst q))

  f-g-s :  v u  f (bsuc u)  bsuc v  f- u  v
  f-g-s (l , l≺n) u p with f (bsuc u) | #-f (bsuc u) bzro _
  ... | suc k , k≺n | _ = λ where
    i .fst  predℕ (p i .fst)
    i .snd  isPropDep≺ n k≺n l≺n (cong (predℕ  fst) p) i
  ... |  zero , k≺m | _ = rec (znots (cong fst p))

  f-g- :  v  f- (g- v)  v
  f-g- v@(i , i≺n)
    with g (bsuc v) | rinv (bsuc v) | #-g (bsuc v) bzro _
  ... | suc j , j≺m | p | #g = f-g-s v (j , j≺m) p
  ... | zero , _ | p | #g with g bzro | rinv bzro
  ... | suc k , k≺m | q = f-g-z v (k , k≺m) p q

  open Iso
  iso- : Iso (Bounded m) (Bounded n)
  iso- .fun = f-
  iso- .inv = g-
  iso- .rightInv = f-g-
  iso- .leftInv = g-f-

untangled
  : ∀{m n}
   Iso (Bounded′ (csuc m)) (Bounded′ (csuc n))
   Iso (Bounded m) (Bounded n)
untangled isom = Untangle.iso- fun inv rightInv leftInv
  where open Iso isom

Bounded-inj-iso :  m n  Iso (Bounded m) (Bounded n)  m  n
Bounded-inj-iso m n theIso i .force with force m | force n
... | czero | czero = czero
... | csuc l | csuc r
  = csuc (Bounded-inj-iso l r (untangled theIso) i)
... | czero | csuc r
  = rec {A = czero  csuc r} (Iso.inv theIso (zero , _) .snd) i
... | csuc l | czero
  = rec {A = csuc l  czero} (Iso.fun theIso (zero , _) .snd) i

Bounded-inj :  m n  Bounded m  Bounded n  m  n
Bounded-inj m n = Bounded-inj-iso m n  pathToIso