{-# OPTIONS --cubical --allow-unsolved-metas #-}

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Data.Empty hiding (elim ; rec)
open import Cubical.Data.Unit renaming (Unit to )
open import Cubical.Data.Nat as  using ()
open import Cubical.Data.Nat.Order as  using ()
open import Cubical.Data.Sigma
open import Cubical.Relation.Nullary
-- TODO: use addition, negation and subtraction from the standard library
open import Cubical.Data.Int.MoreInts.BiInvInt renaming (BiInvℤ to ) hiding (_+_ ; +-assoc ; +-comm ; +-zero ; +-suc ; +-pred ; neg ; _-_) public
open import Misc

one : 
one = suc zero

-one : 
-one = pred zero

elim :  {} (A :   Type )
       (z : A zero)
       (s : (n : )  A n  A (suc n))
       (p : (n : )  A n  A (predl n))
       (p' : (n : )  A n  A (predr n))
       (ps : (n : )  (x : A n)  PathP  i  A (predl-suc n i)) (p (suc n) (s n x)) x)
       (sp' : (n : )  (x : A n)  PathP  i  A (suc-predr n i)) (s (predr n) (p' n x)) x)
       (n : )  A n
elim A z s p p' ps sp' zero = z
elim A z s p p' ps sp' (suc n) = s n (elim A z s p p' ps sp' n)
elim A z s p p' ps sp' (predl n) = p n (elim A z s p p' ps sp' n)
elim A z s p p' ps sp' (predr n) = p' n (elim A z s p p' ps sp' n)
elim A z s p p' ps sp' (predl-suc n i) = ps n (elim A z s p p' ps sp' n) i
elim A z s p p' ps sp' (suc-predr n i) = sp' n (elim A z s p p' ps sp' n) i

-- same as above but with same function for the two predecessors (which is what
-- we use in practice)
elim' :  {} (A :   Type )
        (z : A zero)
        (s : (n : )  A n  A (suc n))
        (p : (n : )  A n  A (predl n))
        (ps : (n : )  (x : A n)  PathP  i  A (predl-suc n i)) (p (suc n) (s n x)) x)
        (sp' : (n : )  (x : A n)  PathP  i  A (suc-predr n i)) (s (predr n) (subst A (predl≡predr n) (p n x))) x)
        (n : )  A n
elim' A z s p ps sp' n = elim A z s p  n An  subst A (predl≡predr n) (p n An)) ps sp' n

rec :  {} {A : Type }
       (z : A)
       (s :   A  A)
       (p :   A  A)
       (p' :   A  A)
       (ps : (n : )  (x : A)  (p (suc n) (s n x))  x)
       (sp' : (n : )  (x : A)  (s (predr n) (p' n x))  x) 
         A
rec {A = A} z s p p' ps sp' n = elim  _  A) z s p p' ps sp' n

-- elim : ∀ {ℓ} (A : ℤ → Type ℓ)
      -- (z : A zero)
      -- (s : {n : ℤ} → A n → A (suc n))
      -- (p : {n : ℤ} → A n → A (predl n))
      -- (ps : {n : ℤ} → (x : A n) → PathP (λ i → A (predl-suc n i)) (p (s x)) x)
      -- (sp : {n : ℤ} → (x : A n) → PathP (λ i → A (suc-predl n i)) (s (p x)) x)
      -- (n : ℤ) → A n
-- elim A z s p ps sp n = elim' A z (λ n x → s x) (λ n x → p x) (λ n x → subst A (predl≡predr n) (p x)) (λ n x → ps x) (λ n x → {!transport (sym ?!}) n

-- simpl : ℤ → ℤ
-- simpl zero = zero
-- simpl (suc n) with simpl n
-- ... | zero = suc zero
-- ... | suc m = suc (suc m)
-- ... | predl m = m
-- ... | predr m = m
-- ... | predl-suc zero i = suc zero
-- ... | predl-suc (suc m) i = suc (suc m)
-- ... | predl-suc (predl m) i = suc-predl m i
-- ... | predl-suc (predr m) i = suc-predr m i
-- ... | predl-suc (predl-suc m i) j = {!!}
-- ... | predl-suc (suc-predr m i) j = {!!}
-- ... | suc-predr zero i = cong suc (suc-predr zero) i
-- ... | suc-predr (suc m) i = cong suc (suc-predr (suc m)) i
-- ... | suc-predr (predl m) i = ((cong suc (suc-predr (predl m))) ∙ (suc-predl m)) i
-- ... | suc-predr (predr m) i = ((cong suc (suc-predr (predr m))) ∙ (suc-predr m)) i
-- ... | suc-predr (predl-suc m i) j = {!!}
-- ... | suc-predr (suc-predr m i) j = {!!}
-- simpl (predl n) with simpl n
-- ... | zero = predl zero
-- ... | suc m = m
-- ... | predl m = predl (predl m)
-- ... | predr m = predl (predl m)
-- ... | predl-suc m i = {!!}
-- ... | suc-predr m i = {!!}
-- simpl (predr n) with simpl n
-- ... | zero = predl zero
-- ... | suc m = n
-- ... | predl m = predl (predl n)
-- ... | predr m = predl (predl n)
-- ... | predl-suc m i = {!!}
-- ... | suc-predr m i = {!!}
-- simpl (predl-suc n i) = {!!}
-- simpl (suc-predr n i) = {!!}

postulate discrete : Discrete 
-- discreteℤ zero zero = yes refl
-- discreteℤ zero (suc n) = {!!}
-- discreteℤ zero (predl n) = {!!}
-- discreteℤ zero (predr n) = {!!}
-- discreteℤ zero (predl-suc n i) = {!!}
-- discreteℤ zero (suc-predr n i) = {!!}
-- discreteℤ (suc m) n = {!!}
-- discreteℤ (predl m) zero = {!!}
-- discreteℤ (predl m) (suc n) = {!!}
-- discreteℤ (predl m) (predl n) with discreteℤ m n
-- ... | yes p = yes (cong predl p)
-- ... | no ¬p = no λ m-≡n- → ¬p (sym (suc-predl m) ∙ cong suc m-≡n- ∙ suc-predl n)
-- discreteℤ (predl m) (predr n) = {!!}
-- discreteℤ (predl m) (predl-suc n i) = {!!}
-- discreteℤ (predl m) (suc-predr n i) = {!!}
-- discreteℤ (predr m) n = {!!}
-- discreteℤ (predl-suc m i) n = {!!}
-- discreteℤ (suc-predr m i) n = {!!}

ℤ-isSet : isSet 
ℤ-isSet = Discrete→isSet discrete

postulate
  -- We would need discreteℤ to compute in order to be able to show this one
  -- with subst
  1≢0 : ¬ (suc zero  zero)

_+_ :     
zero + n = n
suc m + n = suc (m + n)
predl m + n = predl (m + n)
predr m + n = predr (m + n)
predl-suc m i + n = predl-suc (m + n) i
suc-predr m i + n = suc-predr (m + n) i

postulate
  +-assoc : (m n o : )  m + (n + o)  (m + n) + o
  +-comm : (m n : )  m + n  n + m
  zero-+ : (n : )  zero + n  n
  +-zero : (n : )  n + zero  n
  +-suc : (m n : )  m + suc n  suc (m + n)
  +-pred : (m n : )  m + pred n  pred (m + n)
  +-predr : (m n : )  m + predr n  predr (m + n)
  +1-suc : (n : )  n + one  suc n
  -1+suc : (n : )  -one + suc n  n
  suc+-1 : (n : )  suc n + -one  n

cong-+-r : (m : ) {n n' : }  n  n'  m + n  m + n'
cong-+-r m p = cong  n  m + n) p

-- opposite of an integer
neg :   
neg zero = zero
neg (suc n) = predl (neg n)
neg (predl n) = suc (neg n)
neg (predr n) = suc (neg n)
neg (predl-suc n i) = suc-predl (neg n) i
neg (suc-predr n i) = predl-suc (neg n) i

postulate
  neg-neg : (n : )  neg (neg n)  n
  neg+≡0 : (n : )  (neg n) + n  zero
  +neg≡0 : (n : )  n + (neg n)  zero
  neg-+ : (m n : )  neg (m + n)  neg m + neg n

_-_ :     
m - n = m + neg n

postulate
  n-n≡0 : (n : )  n - n  zero
  -1-pred : (n : )  n - one  pred n

-- op-op : (n : ℤ) → op (op n) ≡ n
-- op-op zero = refl
-- op-op (suc n) = cong suc (op-op n)
-- op-op (predl n) = cong predl (op-op n)
-- op-op (predr n) = cong predl (op-op n) ∙ predl≡predr n
-- op-op (predl-suc n i) = {!!} -- op (op (predl-suc n i)) ≡ predl-suc n i
-- op-op (suc-predr n i) = {!!} -- op (op (suc-predr n i)) ≡ suc-predr n i

fromℕ :   
fromℕ ℕ.zero = zero
fromℕ (ℕ.suc n) = suc (fromℕ n)

postulate
  fromℕ-+ : (m n : )  fromℕ (m ℕ.+ n)  fromℕ m + fromℕ n

---
--- Comparison with non-quotiented definition
---

open import Cubical.Data.Int as Int using (pos ; negsuc ; predSuc ; sucPred) renaming ( to ℤ' ; sucℤ to sucℤ' ; predℤ to predℤ')

toInt :   ℤ'
toInt zero = Int.pos ℕ.zero
toInt (suc n) = Int.sucℤ (toInt n)
toInt (predl n) = Int.predℤ (toInt n)
toInt (predr n) = Int.predℤ (toInt n)
toInt (predl-suc n i) = predSuc (toInt n) i
toInt (suc-predr n i) = sucPred (toInt n) i

fromInt : ℤ'  
fromInt (pos n) = fromℕ n
fromInt (negsuc n) = neg (fromℕ (ℕ.suc n))

open import Cubical.Core.Everything
open import Cubical.Foundations.Isomorphism

postulate
  ℤ≃Int : ℤ'  
  -- ℤ≃Int = isoToEquiv (iso toInt fromInt {!!} {!!})

---
--- Even / odd predicates
---

-- is-even : ℤ → Type₀
-- is-odd : ℤ → Type₀

-- is-even zero = Unit
-- is-even (suc n) = is-odd n
-- is-even (predl n) = is-odd n
-- is-even (predr n) = is-odd n
-- is-even (predl-suc n i) = is-even n
-- is-even (suc-predr n i) = is-even n

-- is-odd zero = ⊥
-- is-odd (suc n) = is-even n
-- is-odd (predl n) = is-even n
-- is-odd (predr n) = is-even n
-- is-odd (predl-suc n i) = is-odd n
-- is-odd (suc-predr n i) = is-odd 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)
  even-predl : {n : }  is-odd n  is-even (predl n)
  even-predr : {n : }  is-odd n  is-even (predr n)

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

-- is-even-zero : (e : is-even zero) → e ≡ even-zero
-- is-even-zero e = {!!}

-- TODO
postulate is-even-isProp : {n : }  isProp (is-even n)
postulate is-odd-isProp  : {n : }  isProp (is-odd n)
-- is-even-isProp {zero} e e' = {!!}
-- is-even-isProp {suc n} e e' = {!!}
-- is-even-isProp {predl n} e e' = {!!}
-- is-even-isProp {predr n} e e' = {!!}
-- is-even-isProp {predl-suc n i} e e' = {!!}
-- is-even-isProp {suc-predr n i} e e' = {!!}
-- is-odd-isProp o o' = {!!}

postulate even-or-odd : (n : )  ¬ (is-even n × is-odd n)
-- even-or-odd zero (e , o) = {!!}
-- even-or-odd (suc n) (e , o) = {!!}
-- even-or-odd (predl n) (e , o) = {!!}
-- even-or-odd (predr n) (e , o) = {!!}
-- even-or-odd (predl-suc n i) (e , o) = {!!}
-- even-or-odd (suc-predr n i) (e , o) = {!!}

postulate
  even-suc-inv : (n : )  is-even (suc n)  is-odd n

open import Cubical.Data.Sum

dec-parity : (n : )  Type₀
dec-parity n = is-even n  is-odd n

parity : (n : )  dec-parity n
parity zero = inl even-zero
parity (suc n) with parity n
... | inl e = inr (odd-suc e)
... | inr o = inl (even-suc o)
parity (predl n) with parity n
... | inl e = inr (odd-predl e)
... | inr o = inl (even-predl o)
parity (predr n) with parity n
... | inl e = inr (odd-predr e)
... | inr o = inl (even-predr o)
parity (predl-suc n i) = lem n i
  where
  --- NB: this obvious idea does not work because it does not terminate...
  -- lem : (n : ℤ) → PathP (λ i → dec-parity (predl-suc n i)) (parity (predl (suc n))) (parity n)
  -- lem n = cong parity (predl-suc n)
  -- TODO
  postulate lem : (n : )  PathP  i  dec-parity (predl-suc n i)) (parity (predl (suc n))) (parity n)
  -- lem zero = {!!}
  -- lem (suc n) = {!!}
  -- lem (predl n) = {!!}
  -- lem (predr n) = {!!}
  -- lem (predl-suc n i) = {!!}
  -- lem (suc-predr n i) = {!!}
  -- lem : (n : ℤ) →  transport (cong dec-parity (predl-suc n)) (parity (predl (suc n))) ≡ (parity n)
  -- lem zero = {!refl!}
  -- lem (suc n) = {!!}
  -- lem (predl n) = {!!}
  -- lem (predr n) = {!!}
  -- lem (predl-suc n i) = {!!}
  -- lem (suc-predr n i) = {!!}
parity (suc-predr n i) = lem n i
  where
  postulate lem : (n : )  PathP  i  dec-parity (suc-predr n i)) (parity (suc (predr n))) (parity n)

-- parity-rec : ∀ {ℓ} (A : ℤ → Type ℓ) → ({n : ℤ} → is-even n → A n) → ({n : ℤ} → is-odd n → A n) → (n : ℤ) → A n
-- parity-rec A even odd n with parity n
-- ... | inl e = even e
-- ... | inr o = odd o

open import Nat as  using (suc ; even-zero ; even-suc ; odd-suc)

is-evenℕ : {n : }  ℕ.is-even n  is-even (fromℕ n)
is-oddℕ  : {n : }  ℕ.is-odd n  is-odd (fromℕ n)
is-evenℕ even-zero    = even-zero
is-evenℕ (even-suc p) = even-suc (is-oddℕ p)
is-oddℕ  (odd-suc p)  = odd-suc  (is-evenℕ p)

postulate
  zero-abs : {n : }  abs n  0  n  zero

-- order

_≤_ :     Type₀
m  n = Σ  λ k  fromℕ k + m  n

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

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

0<1 : zero < one
0<1 = 0 , refl

≤-refl : {n : }  n  n
≤-refl {n} = 0 , refl

≤-≡ : {m n : }  m  n  m  n
≤-≡ {m} {n} p = subst  n  m  n) p ≤-refl

≤-path : {m n : }  m  n  m  n
≤-path {m} {n} p = subst  n  m  n) p ≤-refl

≤-trans : {m n o : }  m  n  n  o  m  o
≤-trans {m} {n} {o} m≤n n≤o = fst m≤n ℕ.+ fst n≤o ,
  (fromℕ (fst m≤n ℕ.+ fst n≤o) + m          ≡⟨ cong  n  n + m) (fromℕ-+ (fst m≤n) (fst n≤o)) 
  (fromℕ (fst m≤n) + fromℕ (fst n≤o)) + m   ≡⟨ cong  n  n + m) (+-comm (fromℕ (fst m≤n)) _) 
  (fromℕ (fst n≤o) + fromℕ (fst m≤n)) + m   ≡⟨ sym (+-assoc (fromℕ (fst n≤o)) _ _) 
  (fromℕ (fst n≤o) + (fromℕ (fst m≤n) + m)) ≡⟨ cong  n  fromℕ (fst n≤o) + n) (snd m≤n) 
  fromℕ (fst n≤o) + n                       ≡⟨ snd n≤o 
  o                                         )

≤-suc : {m n : }  m  n  m  suc n
≤-suc (k , p) = suc k , cong suc p

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

postulate
  ≤-neg : {m n : }  neg n  neg m  m  n
  pos-fromℕ : {n : }  zero  n  Σ   n'  n  fromℕ 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

postulate
  _≟_ : (m n : )  Trichotomy m n

-- even/odd folding of ℤ into ℕ
ℤ≃ℕ :   
ℤ≃ℕ = isoToEquiv i
  where
  f :   
  f n with toInt n
  ... | pos k = 2 ℕ.· k
  ... | negsuc k = suc (2 ℕ.· k)
  g :   
  g n = aux (ℕ.parity n)
    where
    aux : {n : }  ℕ.is-even n  ℕ.is-odd n  
    aux (inl even-zero) = ℤ.zero
    aux (inl (even-suc (odd-suc p))) = suc (aux (inl p))
    aux (inr (odd-suc even-zero)) = neg one
    aux (inr (odd-suc (even-suc p))) = pred (aux (inr p))
  i : Iso  
  Iso.fun i = f
  Iso.inv i = g
  Iso.rightInv i n = {!!}
  Iso.leftInv i n = {!!}

-- a well founded order on ℤ
_≺_ :     Type₀
m  n = equivFun ℤ≃ℕ m ℕ.< equivFun ℤ≃ℕ n

open import Cubical.Foundations.Univalence

postulate
  ≺≡< : transport (cong  N  N  N  Type₀) (ua ℤ≃ℕ)) _≺_  ℕ._<_

open import Cubical.Induction.WellFounded

postulate
  ≺-wellfounded : WellFounded _≺_
  -- -- from ℕ.<-wellfounded
  -- ≺-wellfounded = subst WellFounded {!sym ≺≡<!} {!ℕ.<-wellfounded!} -- 

-- postulate
  -- -- follows from ℕ.find and ℤ≃ℕ by transport
  -- find : {ℓ : Level} (P : ℤ → Type ℓ) → ((n : ℤ) → isProp (P n)) → ((n : ℤ) → Dec (P n)) → ∥ Σ ℤ P ∥₁ → Σ ℤ P

-- ¬∀⇒∃¬ : {ℓ : Level} (P : ℤ → Type ℓ) → ((n : ℤ) → Dec (P n))  → ¬ ((n : ℤ) → P n) → Σ ℤ (λ n → ¬ (P n))
-- ¬∀⇒∃¬ P D N = find _ (λ n → isProp¬ _) (λ n → Dec¬ (D n)) {!!}