-- Define the integers as a HIT by identifying +0 and -0
{-# OPTIONS --safe #-}
module Cubical.Data.Int.MoreInts.QuoInt.Base where

open import Cubical.Core.Everything

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Relation.Nullary

open import Cubical.Data.Int using ()
  renaming ( to Int ; discreteℤ to discreteInt ; isSetℤ to isSetInt ; 0≢1-ℤ to 0≢1-Int)
open import Cubical.Data.Nat as  using (; zero; suc)
open import Cubical.Data.Bool as Bool using (Bool; not; notnot)

variable
  l : Level


Sign : Type₀
Sign = Bool

pattern spos = Bool.false
pattern sneg = Bool.true

_·S_ : Sign  Sign  Sign
_·S_ = Bool._⊕_


data  : Type₀ where
  signed : (s : Sign) (n : )  
  posneg : signed spos 0  signed sneg 0

pattern pos n = signed spos n
pattern neg n = signed sneg n


sign :   Sign
sign (signed _ zero) = spos
sign (signed s (suc _)) = s
sign (posneg i) = spos

sign-pos :  n  sign (pos n)  spos
sign-pos zero = refl
sign-pos (suc n) = refl

abs :   
abs (signed _ n) = n
abs (posneg i) = zero

signed-inv :  n  signed (sign n) (abs n)  n
signed-inv (pos zero) = refl
signed-inv (neg zero) = posneg
signed-inv (signed s (suc n)) = refl
signed-inv (posneg i) j = posneg (i  j)

signed-zero :  s₁ s₂  signed s₁ zero  signed s₂ zero
signed-zero spos spos = refl
signed-zero sneg sneg = refl
signed-zero spos sneg = posneg
signed-zero sneg spos = sym posneg


rec :  {A : Type l}  (pos' neg' :   A)  pos' 0  neg' 0    A
rec pos' neg' eq (pos m)    = pos' m
rec pos' neg' eq (neg m)    = neg' m
rec pos' neg' eq (posneg i) = eq i

elim :  (P :   Type l)
        (pos' :  n  P (pos n))
        (negsuc' :  n  P (neg (suc n)))
         z  P z
elim P pos' negsuc' (pos n) = pos' n
elim P pos' negsuc' (neg zero) = subst P posneg (pos' zero)
elim P pos' negsuc' (neg (suc n)) = negsuc' n
elim P pos' negsuc' (posneg i) = subst-filler P posneg (pos' zero) i


Int→ℤ : Int  
Int→ℤ (Int.pos n) = pos n
Int→ℤ (Int.negsuc n) = neg (suc n)

ℤ→Int :   Int
ℤ→Int (pos n) = Int.pos n
ℤ→Int (neg zero) = Int.pos 0
ℤ→Int (neg (suc n)) = Int.negsuc n
ℤ→Int (posneg _) = Int.pos 0

ℤ→Int→ℤ :  (n : )  Int→ℤ (ℤ→Int n)  n
ℤ→Int→ℤ (pos n) _       = pos n
ℤ→Int→ℤ (neg zero) i    = posneg i
ℤ→Int→ℤ (neg (suc n)) _ = neg (suc n)
ℤ→Int→ℤ (posneg j) i    = posneg (j  i)

Int→ℤ→Int :  (n : Int)  ℤ→Int (Int→ℤ n)  n
Int→ℤ→Int (Int.pos n) _ = Int.pos n
Int→ℤ→Int (Int.negsuc n) _ = Int.negsuc n

isoIntℤ : Iso Int 
isoIntℤ = iso Int→ℤ ℤ→Int ℤ→Int→ℤ Int→ℤ→Int

Int≡ℤ : Int  
Int≡ℤ = isoToPath isoIntℤ

discreteℤ : Discrete 
discreteℤ = subst Discrete Int≡ℤ discreteInt

isSetℤ : isSet 
isSetℤ = subst isSet Int≡ℤ isSetInt


-_ :   
- signed s n = signed (not s) n
- posneg i   = posneg (~ i)

negate-invol :  n  - - n  n
negate-invol (signed s n) i = signed (notnot s i) n
negate-invol (posneg i)   _ = posneg i

negateEquiv :   
negateEquiv = isoToEquiv (iso -_ -_ negate-invol negate-invol)

negateEq :   
negateEq = ua negateEquiv


infixl 6 _+_
infixl 7 _·_

sucℤ :   
sucℤ (pos n)       = pos (suc n)
sucℤ (neg zero)    = pos 1
sucℤ (neg (suc n)) = neg n
sucℤ (posneg _)    = pos 1

predℤ :   
predℤ = subst  Z  (Z  Z)) negateEq sucℤ
  -- definitionally equal to λ n → - (sucℤ (- n))
  -- strictly more useful than the direct pattern matching version,
  --  see negateSuc and negatePred

sucPredℤ :  n  sucℤ (predℤ n)  n
sucPredℤ (pos zero)    = sym posneg
sucPredℤ (pos (suc _)) = refl
sucPredℤ (neg _)       = refl
sucPredℤ (posneg i) j  = posneg (i  ~ j)

predSucℤ :  n  predℤ (sucℤ n)  n
predSucℤ (pos _)       = refl
predSucℤ (neg zero)    = posneg
predSucℤ (neg (suc _)) = refl
predSucℤ (posneg i) j  = posneg (i  j)

_+_ :     
(signed _ zero) + n = n
(posneg _)      + n = n
(pos (suc m))   + n = sucℤ  (pos m + n)
(neg (suc m))   + n = predℤ (neg m + n)


sucPathℤ :   
sucPathℤ  = isoToPath (iso sucℤ predℤ sucPredℤ predSucℤ)

-- We do the same trick as in Cubical.Data.Int to prove that addition
-- is an equivalence
addEqℤ :     
addEqℤ zero    = refl
addEqℤ (suc n) = addEqℤ n  sucPathℤ

predPathℤ :   
predPathℤ = isoToPath (iso predℤ sucℤ predSucℤ sucPredℤ)

subEqℤ :     
subEqℤ zero    = refl
subEqℤ (suc n) = subEqℤ n  predPathℤ

addℤ :     
addℤ (pos m) n    = transport (addEqℤ m) n
addℤ (neg m) n    = transport (subEqℤ m) n
addℤ (posneg _) n = n

isEquivAddℤ : (m : )  isEquiv (addℤ m)
isEquivAddℤ (pos n)    = isEquivTransport (addEqℤ n)
isEquivAddℤ (neg n)    = isEquivTransport (subEqℤ n)
isEquivAddℤ (posneg _) = isEquivTransport refl

addℤ≡+ℤ : addℤ  _+_
addℤ≡+ℤ i  (pos (suc m)) n = sucℤ   (addℤ≡+ℤ i (pos m) n)
addℤ≡+ℤ i  (neg (suc m)) n = predℤ  (addℤ≡+ℤ i (neg m) n)
addℤ≡+ℤ i  (pos zero) n    = n
addℤ≡+ℤ i  (neg zero) n    = n
addℤ≡+ℤ _  (posneg _) n    = n

isEquiv+ℤ : (m : )  isEquiv (m +_)
isEquiv+ℤ = subst  _+_  (m : )  isEquiv (m +_)) addℤ≡+ℤ isEquivAddℤ


_·_ :     
m · n = signed (sign m ·S sign n) (abs m ℕ.· abs n)

private
  ·-abs :  m n  abs (m · n)  abs m ℕ.· abs n
  ·-abs m n = refl


-- Natural number and negative integer literals for ℤ

open import Cubical.Data.Nat.Literals public

instance
  fromNatℤ : HasFromNat 
  fromNatℤ = record { Constraint = λ _  Unit ; fromNat = λ n  pos n }

instance
  fromNegℤ : HasFromNeg 
  fromNegℤ = record { Constraint = λ _  Unit ; fromNeg = λ n  neg n }


-- ℤ is non-trivial

0≢1-ℤ : ¬ 0  1
0≢1-ℤ p = 0≢1-Int (cong ℤ→Int p)