-----------------------------------------------------------------------------
-- Unsigned Integers
-- Suitable for use with Hugs 98 on 32 bit systems.
-----------------------------------------------------------------------------
module Hugs.Word
	( Word
	, Word8
	, Word16
	, Word32
	, Word64
	) where

import Hugs.Prelude ( Word, Word8, Word16, Word32, Word64,
                      boundedSucc, boundedPred,
		      boundedEnumFrom, boundedEnumFromTo,
		      boundedEnumFromThen, boundedEnumFromThenTo )
import Hugs.Prelude ( Ix(..) )
import Hugs.Prelude ( (%) )
import Hugs.Prelude ( readDec )
import Hugs.Prelude ( Num(fromInt), Integral(toInt) )
import Hugs.Numeric ( showInt )
import Data.Bits
import Data.Int

-----------------------------------------------------------------------------
-- Word
-----------------------------------------------------------------------------

instance Eq  Word     where (==)    = primEqWord
instance Ord Word     where compare = primCmpWord

instance Num Word where
    (+)           = primPlusWord
    (-)           = primMinusWord
    negate        = primNegateWord
    (*)           = primMulWord
    abs           = absReal
    signum        = signumReal
    fromInteger   = primIntegerToWord
    fromInt       = primIntToWord

instance Bounded Word where
    minBound = 0
    maxBound = primMaxWord

instance Real Word where
    toRational x = toInteger x % 1

instance Integral Word where
    div       = primDivWord
    quot      = primQuotWord
    rem       = primRemWord
    mod       = primModWord
    quotRem   = primQrmWord
    divMod    = quotRem
    toInteger = primWordToInteger
    toInt     = primWordToInt

instance Ix Word where
    range (m,n)          = [m..n]
    index b@(m,n) i
	   | inRange b i = toInt (i - m)
	   | otherwise   = error "index: Index out of range"
    inRange (m,n) i      = m <= i && i <= n

instance Enum Word where
    succ           = boundedSucc
    pred           = boundedPred
    toEnum         = primIntToWord
    fromEnum       = primWordToInt

    enumFrom       = boundedEnumFrom
    enumFromTo     = boundedEnumFromTo
    enumFromThen   = boundedEnumFromThen
    enumFromThenTo = boundedEnumFromThenTo

instance Read Word where
    readsPrec p   = readDec

instance Show Word where
    showsPrec p   = showInt  -- a particularily counterintuitive name!

instance Bits Word where
    (.&.)         = primAndWord
    (.|.)         = primOrWord
    xor           = primXorWord
    complement    = primComplementWord
    shift         = primShiftWord
    rotate        = primRotateWord (bitSize (0::Word))
    bit           = primBitWord
    setBit x i    = x .|. bit i
    clearBit x i  = x .&. complement (bit i)
    complementBit x i = x `xor` bit i
    testBit       = primTestWord
    bitSize  _    = bitSize (0::Int)
    isSigned _    = False

-----------------------------------------------------------------------------
-- Word8
-----------------------------------------------------------------------------

instance Eq  Word8     where (==)    = binop (==)
instance Ord Word8     where compare = binop compare

instance Num Word8 where
    x + y         = to (binop (+) x y)
    x - y         = to (binop (-) x y)
    negate        = to . negate . from
    x * y         = to (binop (*) x y)
    abs           = absReal
    signum        = signumReal
    fromInteger   = to . primIntegerToWord
    fromInt       = to . primIntToWord

instance Bounded Word8 where
    minBound = 0
    maxBound = 0xff

instance Real Word8 where
    toRational x = toInteger x % 1

instance Integral Word8 where
    x `div` y     = to  (binop div x y)
    x `quot` y    = to  (binop quot x y)
    x `rem` y     = to  (binop rem x y)
    x `mod` y     = to  (binop mod x y)
    x `quotRem` y = to2 (binop quotRem x y)
    divMod        = quotRem
    toInteger     = toInteger . from
    toInt         = toInt . from

instance Ix Word8 where
    range (m,n)          = [m..n]
    index b@(m,n) i
	   | inRange b i = toInt (i - m)
	   | otherwise   = error "index: Index out of range"
    inRange (m,n) i      = m <= i && i <= n

instance Enum Word8 where
    succ          = boundedSucc
    pred          = boundedPred
    toEnum        = fromInt
    fromEnum      = toInt
    enumFrom      = boundedEnumFrom
    enumFromThen  = boundedEnumFromThen

instance Read Word8 where
    readsPrec p   = readDec

instance Show Word8 where
    showsPrec p   = showInt  -- a particularily counterintuitive name!

instance Bits Word8 where
    x .&. y       = to (binop (.&.) x y)
    x .|. y       = to (binop (.|.) x y)
    x `xor` y     = to (binop xor x y)
    complement    = to . complement . from
    x `shift` i   = to (from x `shift` i)
    x `rotate` i  = to (from x `rot` i)
      where rot = primRotateWord 8
    bit           = to . bit
    setBit x i    = to (setBit (from x) i)
    clearBit x i  = to (clearBit (from x) i)
    complementBit x i = to (complementBit (from x) i)
    testBit x i   = testBit (from x) i
    bitSize  _    = 8
    isSigned _    = False

-----------------------------------------------------------------------------
-- Word16
-----------------------------------------------------------------------------

instance Eq  Word16     where (==)    = binop (==)
instance Ord Word16     where compare = binop compare

instance Num Word16 where
    x + y         = to (binop (+) x y)
    x - y         = to (binop (-) x y)
    negate        = to . negate . from
    x * y         = to (binop (*) x y)
    abs           = absReal
    signum        = signumReal
    fromInteger   = to . primIntegerToWord
    fromInt       = to . primIntToWord

instance Bounded Word16 where
    minBound = 0
    maxBound = 0xffff

instance Real Word16 where
    toRational x  = toInteger x % 1

instance Integral Word16 where
    x `div` y     = to  (binop div x y)
    x `quot` y    = to  (binop quot x y)
    x `rem` y     = to  (binop rem x y)
    x `mod` y     = to  (binop mod x y)
    x `quotRem` y = to2 (binop quotRem x y)
    divMod        = quotRem
    toInteger     = toInteger . from
    toInt         = toInt . from

instance Ix Word16 where
    range (m,n)          = [m..n]
    index b@(m,n) i
	   | inRange b i = toInt (i - m)
	   | otherwise   = error "index: Index out of range"
    inRange (m,n) i      = m <= i && i <= n

instance Enum Word16 where
    succ          = boundedSucc
    pred          = boundedPred
    toEnum        = fromInt
    fromEnum      = toInt
    enumFrom      = boundedEnumFrom
    enumFromThen  = boundedEnumFromThen

instance Read Word16 where
    readsPrec p   = readDec

instance Show Word16 where
    showsPrec p   = showInt  -- a particularily counterintuitive name!

instance Bits Word16 where
    x .&. y       = to (binop (.&.) x y)
    x .|. y       = to (binop (.|.) x y)
    x `xor` y     = to (binop xor x y)
    complement    = to . complement . from
    x `shift` i   = to (from x `shift` i)
    x `rotate` i  = to (from x `rot` i)
      where rot = primRotateWord 16
    bit           = to . bit
    setBit x i    = to (setBit (from x) i)
    clearBit x i  = to (clearBit (from x) i)
    complementBit x i = to (complementBit (from x) i)
    testBit x i   = testBit (from x) i
    bitSize  _    = 16
    isSigned _    = False

-----------------------------------------------------------------------------
-- Word32
-----------------------------------------------------------------------------

instance Eq  Word32     where (==)    = binop (==)
instance Ord Word32     where compare = binop compare

instance Num Word32 where
    x + y         = to (binop (+) x y)
    x - y         = to (binop (-) x y)
    negate        = to . negate . from
    x * y         = to (binop (*) x y)
    abs           = absReal
    signum        = signumReal
    fromInteger   = to . primIntegerToWord
    fromInt       = to . primIntToWord

instance Bounded Word32 where
    minBound = 0
    maxBound = 0xffffffff

instance Real Word32 where
    toRational x  = toInteger x % 1

instance Integral Word32 where
    x `div` y     = to  (binop div x y)
    x `quot` y    = to  (binop quot x y)
    x `rem` y     = to  (binop rem x y)
    x `mod` y     = to  (binop mod x y)
    x `quotRem` y = to2 (binop quotRem x y)
    divMod        = quotRem
    toInteger     = toInteger . from
    toInt         = toInt . from

instance Ix Word32 where
    range (m,n)          = [m..n]
    index b@(m,n) i
	   | inRange b i = toInt (i - m)
	   | otherwise   = error "index: Index out of range"
    inRange (m,n) i      = m <= i && i <= n

instance Enum Word32 where
    succ          = boundedSucc
    pred          = boundedPred
    toEnum        = fromInt
    fromEnum      = toInt
    enumFrom      = boundedEnumFrom
    enumFromThen  = boundedEnumFromThen
    enumFromTo    = boundedEnumFromTo
    enumFromThenTo = boundedEnumFromThenTo

instance Read Word32 where
    readsPrec p   = readDec

instance Show Word32 where
    showsPrec p   = showInt  -- a particularily counterintuitive name!

instance Bits Word32 where
    x .&. y       = to (binop (.&.) x y)
    x .|. y       = to (binop (.|.) x y)
    x `xor` y     = to (binop xor x y)
    complement    = to . complement . from
    x `shift` i   = to (from x `shift` i)
    x `rotate` i  = to (from x `rot` i)
      where rot = primRotateWord 32
    bit           = to . bit
    setBit x i    = to (setBit (from x) i)
    clearBit x i  = to (clearBit (from x) i)
    complementBit x i = to (complementBit (from x) i)
    testBit x i   = testBit (from x) i
    bitSize  _    = 32
    isSigned _    = False

-----------------------------------------------------------------------------
-- Word64
-----------------------------------------------------------------------------

primitive word64ToWord32 "primWord64ToWord32" :: Word64 -> (Word32,Word32)
primitive word32ToWord64 "primWord32ToWord64" :: Word32 -> Word32 -> Word64

integerToW64 :: Integer -> Word64
integerToW64 x = case x `divMod` 0x100000000 of
	(hi,lo) -> word32ToWord64 (fromInteger hi) (fromInteger lo)

w64ToInteger :: Word64 -> Integer
w64ToInteger x = case word64ToWord32 x of
	(hi,lo) -> toInteger hi * 0x100000000 + toInteger lo

instance Eq Word64 where
    x == y = word64ToWord32 x == word64ToWord32 y

instance Ord Word64 where
    compare x y = compare (word64ToWord32 x) (word64ToWord32 y)

instance Bounded Word64 where
    minBound = word32ToWord64 minBound minBound
    maxBound = word32ToWord64 maxBound maxBound

instance Show Word64 where
    showsPrec p = showInt . toInteger

instance Read Word64 where
    readsPrec p s = [ (fromInteger x,r) | (x,r) <- readDec s ]

instance Num Word64 where
    x + y         = fromInteger (toInteger x + toInteger y)
    x - y         = fromInteger (toInteger x - toInteger y)
    x * y         = fromInteger (toInteger x * toInteger y)
    abs           = absReal
    signum        = signumReal
    fromInteger   = integerToW64

instance Real Word64 where
    toRational x = toInteger x % 1

instance Ix Word64 where
    range (m,n)          = [m..n]
    index b@(m,n) i
	   | inRange b i = toInt (i - m)
	   | otherwise   = error "index: Index out of range"
    inRange (m,n) i      = m <= i && i <= n

instance Enum Word64 where
    succ             = boundedSucc
    pred             = boundedPred
    toEnum           = fromInt
    fromEnum         = toInt

    enumFrom x       = enumFromTo x maxBound
    enumFromTo x y   = map fromInteger [toInteger x .. toInteger y]
    enumFromThen     = boundedEnumFromThen
    enumFromThenTo x y z =
                       map fromInteger [toInteger x, toInteger y .. toInteger z]

instance Integral Word64 where
    x `quotRem` y = (fromInteger q, fromInteger r)
	where (q,r) = toInteger x `quotRem` toInteger y
    toInteger     = w64ToInteger

instance Bits Word64 where
    x .&. y       = liftBinary (.&.) x y
    x .|. y       = liftBinary (.|.) x y
    x `xor` y     = liftBinary xor x y
    complement    = liftUnary complement
    x `shift` i   = fromInteger ((toInteger x `shift` i) `mod`
				 0x10000000000000000)
    x `rotate` i  | i<0  = (x `shift` i) .|. (x `shift` (i+bitSize x))
		  | i==0 = x
		  | i>0  = (x `shift` i) .|. (x `shift` (i-bitSize x))
    bit i | i `mod` 64 < 32 = word32ToWord64 0 (bit i)
          | otherwise       = word32ToWord64 (bit i) 0
    bitSize  _    = 64
    isSigned _    = False

liftBinary :: (Word32 -> Word32 -> Word32) -> Word64 -> Word64 -> Word64
liftBinary op x y = word32ToWord64 (op xhi yhi) (op xlo ylo)
	where	(xhi,xlo) = word64ToWord32 x
		(yhi,ylo) = word64ToWord32 y

liftUnary :: (Word32 -> Word32) -> Word64 -> Word64
liftUnary op x = word32ToWord64 (op xhi) (op xlo)
	where	(xhi,xlo) = word64ToWord32 x

-----------------------------------------------------------------------------
-- End of exported definitions
--
-- The remainder of this file consists of definitions which are only
-- used in the implementation.
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
-- Coercions - used to make the instance declarations more uniform
-----------------------------------------------------------------------------

primitive word8ToWord "primWord8ToWord" :: Word8  -> Word
primitive wordToWord8 "primWordToWord8" :: Word -> Word8

primitive word16ToWord "primWord16ToWord" :: Word16 -> Word
primitive wordToWord16 "primWordToWord16" :: Word -> Word16

primitive word32ToWord "primWord32ToWord" :: Word32 -> Word
primitive wordToWord32 "primWordToWord32" :: Word -> Word32

class Coerce a where
    to   :: Word -> a
    from :: a -> Word

instance Coerce Word8 where
    from = word8ToWord
    to   = wordToWord8

instance Coerce Word16 where
    from = word16ToWord
    to   = wordToWord16

instance Coerce Word32 where
    from = word32ToWord
    to   = wordToWord32

binop :: Coerce word => (Word -> Word -> a) -> (word -> word -> a)
binop op x y = from x `op` from y

to2 :: Coerce word => (Word, Word) -> (word, word)
to2 (x,y) = (to x, to y)

-----------------------------------------------------------------------------
-- primitives
-----------------------------------------------------------------------------

primitive primEqWord        :: Word -> Word -> Bool
primitive primCmpWord       :: Word -> Word -> Ordering
primitive primPlusWord,
	  primMinusWord,
	  primMulWord	    :: Word -> Word -> Word
primitive primNegateWord    :: Word -> Word
primitive primIntToWord     :: Int -> Word
primitive primIntegerToWord :: Integer -> Word
primitive primMaxWord       :: Word
primitive primDivWord,
	  primQuotWord,
	  primRemWord,
	  primModWord       :: Word -> Word -> Word
primitive primQrmWord       :: Word -> Word -> (Word,Word)
primitive primWordToInt     :: Word -> Int
primitive primWordToInteger :: Word -> Integer
primitive primAndWord       :: Word -> Word -> Word
primitive primOrWord        :: Word -> Word -> Word
primitive primXorWord       :: Word -> Word -> Word
primitive primComplementWord:: Word -> Word
primitive primShiftWord     :: Word -> Int -> Word
primitive primRotateWord    :: Int -> Word -> Int -> Word
primitive primBitWord       :: Int -> Word
primitive primTestWord      :: Word -> Int -> Bool

-----------------------------------------------------------------------------
-- Code copied from the Prelude
-----------------------------------------------------------------------------

absReal x    | x >= 0    = x
	     | otherwise = -x

signumReal x | x == 0    =  0
	     | x > 0     =  1
	     | otherwise = -1

-----------------------------------------------------------------------------
-- End
-----------------------------------------------------------------------------
