PreludeInteger

module PreludeInteger(Integer,integerFromInt,intFromInteger,doubleFromInteger,readInteger) where
import PreludeNatural
import Ratio(intToRat)
import Numeric(readSigned,readDec,readHex,readOct,showSigned,showInt)
--import Debug.Trace

instance Eq Integer where
  Pos a==Pos b = a==b
  Neg a==Neg b = a==b
  a    ==b     = isZero a && isZero b

isZero (Pos a) = isZeroN a
isZero (Neg a) = isZeroN a

zeroI = Pos zeroN
oneI = Pos oneN

instance Ord Integer where
  compare (Pos a) (Pos b) = compare a b
  compare (Neg a) (Neg b) = compare b a
  compare (Neg a) (Pos b) = if isZeroN a && isZeroN b
                            then EQ
                            else LT
  compare (Pos a) (Neg b) = if isZeroN a && isZeroN b
                            then EQ
                            else GT

integerFromInt n = if n<zero
                   then if n==minBound
                        then Neg (succ (natFromInt maxBound))
                        else Neg (natFromInt (-n))
                   else Pos (natFromInt n)

doubleFromInteger (Pos n) = doubleFromNatural n
doubleFromInteger (Neg n) = -doubleFromNatural n

intFromInteger (Pos n) = intFromNatural n
intFromInteger (Neg n) = -intFromNatural n

instance Show Integer where
  showsPrec p (Pos n) = showsPrec p n
  showsPrec p (Neg n) = showParen (p>six) (showChar '-' . shows n)
--show (Pos n) = '+':show n
--show (Neg n) = '-':show n

instance Enum Integer where
  toEnum = integerFromInt
  fromEnum = intFromInteger

  succ n = n+oneI
  pred n = n-oneI

  enumFrom = iterate succ
  enumFromTo = genericEnumFromTo
  enumFromThen a b = iterate (+(b-a)) a
  enumFromThenTo x y z = enumFromStepTo x (y-x) z
  
instance Num Integer where
  fromInteger = id
--fromInt = integerFromInt

  Pos a + Pos b = Pos (a+b)
  Neg a + Neg b = Neg (a+b)
  Pos a + Neg b = case compare a b of
                    LT -> Neg (b-a)
                    EQ -> zeroI
                    GT -> Pos (a-b)
  Neg a + Pos b = case compare a b of
                    LT -> Pos (b-a)
                    EQ -> zeroI
                    GT -> Neg (a-b)

  negate (Pos a) = Neg a
  negate (Neg a) = Pos a

  Pos a * Pos b = Pos (a*b)
  Neg a * Neg b = Pos (a*b)
  Pos a * Neg b = Neg (a*b)
  Neg a * Pos b = Neg (a*b)
  
  abs (Neg a) = Pos a
  abs a = a

  signum (Pos a) = if isZeroN a then zeroI else Pos oneN
  signum (Neg a) = if isZeroN a then zeroI else Neg oneN
  
instance Integral Integer where
  toInteger = id
  quotRem  = qRemInteger

instance Real Integer where
  toRational = intToRat

instance Read Integer where
  readsPrec d = readSigned (\s->case s of
                                  '0':'x':r -> readHex r
                                  '0':'o':r -> readOct r
                                  _ -> readDec s)

readInteger ('-':s) = Neg (readDigits s)
readInteger s       = Pos (readDigits s)

qRemInteger :: Integer -> Integer -> (Integer,Integer)
--qRemInteger _ 0 = error "quotRem{Integer}: divide by zero"
--qRemInteger 0 _ = (zeroI,zeroI)
qRemInteger (Pos ds) (Pos es) = apPair Pos Pos (quotRem ds es)
qRemInteger (Neg ds) (Pos es) = apPair Neg Neg (quotRem ds es)
qRemInteger (Neg ds) (Neg es) = apPair Pos Neg (quotRem ds es)
qRemInteger (Pos ds) (Neg es) = apPair Neg Pos (quotRem ds es)

apPair f g (x,y) = (f x,g y)