-- | The implementations of all functions except for rem, quot, div, mod are 
--   supposed to be as non-strict as possible.
module Data.Number.Int ( Int(..), pos ) where


import Prelude hiding ( Int )
import Data.Number.Nat
import Data.Number.NatO
import Data.Ratio ( (%) )


-- | Integers
data Int 
  -- | A negative natural number
  = Neg Nat 
  -- | A positive natural number or zero
  | NatO NatO
  deriving Eq


pos :: Nat -> Int
pos = NatO . Nat


instance Show Int where
  show (Neg n)  = "-" ++ show n
  show (NatO n) = show n


instance Read Int where
  readsPrec n = map (\(x,str) -> (toEnum x,str)) . readsPrec n  


instance Ord Int where
  compare (Neg _)  (NatO _) = LT
  compare (NatO _) (Neg _)  = GT
  compare (NatO m) (NatO n) = compare m n
  compare (Neg m)  (Neg n)  = compare n m

  x <  y = cmpIntLT y x == GT
  x >  y = cmpIntLT x y == GT
  x <= y = cmpIntLT x y == LT
  x >= y = cmpIntLT y x == LT

cmpIntLT :: Int -> Int -> Ordering
cmpIntLT (Neg _)  (NatO _) = LT
cmpIntLT (NatO _) (Neg _)  = GT
cmpIntLT (NatO m) (NatO n) = cmpNatOLT m n
cmpIntLT (Neg m)  (Neg n)  = invOrd (cmpNatLT m n)


instance Enum Int where
  succ (NatO n)  = NatO (succ n)
  succ (Neg IHi) = NatO Zero
  succ (Neg n)   = Neg (succ n)

  pred (Neg n)     = Neg (pred n)
  pred (NatO Zero) = Neg IHi
  pred (NatO n)    = NatO (pred n)

  toEnum = toInt

  fromEnum = fromInt


instance Num Int where
  n            + NatO Zero    = n
  Neg m        + NatO (Nat n) = n -^ m
  NatO Zero    + n            = n
  NatO (Nat m) + Neg n        = m -^ n
  NatO m       + NatO n       = NatO (m+n)
  Neg m        + Neg n        = Neg (m+n)

  m - Neg n        = m + pos n
  m - NatO Zero    = m
  m - NatO (Nat n) = m + Neg n

  _            * NatO Zero    = NatO Zero
  NatO Zero    * _            = NatO Zero
  Neg m        * NatO (Nat n) = Neg (m*n)
  NatO (Nat m) * Neg n        = Neg (m*n)
  Neg m        * Neg n        = pos (m*n)
  NatO m       * NatO n       = NatO (m*n) 

  negate   (Neg n)        = pos n
  negate n@(NatO Zero)    = n
  negate   (NatO (Nat n)) = Neg n

  signum   (Neg _)     = Neg IHi
  signum n@(NatO Zero) = n
  signum   (NatO _)    = pos IHi

  abs (Neg n) = pos n
  abs n       = n

  fromInteger = toInt

-- this implementation is least strict
-- for example: I _|_ -^ O IHi = NatO (Nat _|_)
-- while the standard implementation yields _|_ in this case
-- another solution would be to use another datatype ???
-- data Int = Neg NatO | Pos Nat
(-^) :: Nat -> Nat -> Int
x -^ y = 
  case minus x y of
    NatO n  -> NatO n
    Neg IHi -> NatO Zero
    Neg n   -> Neg (pred n)

-- minus x y yields x - y - 1 if x - y <= 0
--                  x - y     otherwise
minus :: Nat -> Nat -> Int
minus IHi     y     = Neg y
minus x@(O _) IHi   = pos (pred x)
minus (I x)   IHi   = pos (O x)
minus (O x)   (O y) = incNeg (mult2 (minus x y))
minus (I x)   (I y) = incNeg (mult2 (minus x y))
minus (O x)   (I y) = decNeg (mult2 (minus x y))
minus (I x)   (O y) = decNeg (mult2 (negate (minus y x)))

mult2 :: Int -> Int
mult2 (Neg n)  = Neg (O n)
mult2 (NatO n) = 
  NatO (case n of
             Zero  -> Zero
             Nat m -> Nat (O m))

incNeg :: Int -> Int
incNeg (Neg n)  = Neg (pred n)
incNeg (NatO n) = NatO n

decNeg :: Int -> Int
decNeg (Neg  n) = Neg n
decNeg (NatO n) = NatO (pred n)


instance Integral Int where
  quotRem (Neg m) (Neg n) = (NatO q,neg r)
   where
    (q,r) = quotRem (Nat m) (Nat n)
  quotRem (Neg m) (NatO n) = (neg q,neg r)
   where
    (q,r) = quotRem (Nat m) n
  quotRem (NatO m) (Neg n) = (neg q,NatO r)
   where
    (q,r) = quotRem m (Nat n)
  quotRem (NatO m) (NatO n) = (NatO q,NatO r)
   where
    (q,r) = quotRem m n

  toInteger = fromInt


fromInt :: Num n => Int -> n
fromInt (Neg n)  = -fromNat n
fromInt (NatO n) = fromNatO n

toInt :: (Integral n,Num n) => n -> Int
toInt n 
  | n<0       = Neg (toNat (-n))
  | otherwise = NatO (toNatO n)

neg :: NatO -> Int 
neg Zero    = NatO Zero
neg (Nat n) = Neg n


instance Real Int where
  toRational n = toInteger n % 1