content: module Util.Word12(Word12) where
  
  import Data.Word(Word32(..))
  import Data.Bits
  import Data.Ix
  import Data.Ratio
  
  newtype Word12 = Word12 {unWord12 :: Word32}
    deriving (Eq,Ord)
  
  
  -- for other bit lengths, just change these definitions
  type T = Word12
  bits :: Int  
  bits = 12
  wrap :: Word32 -> Word12
  wrap = Word12
  unwrap :: Word12 -> Word32
  unwrap = unWord12
  maxVal :: Word32
  maxVal = (1 `shiftL` bits) - 1
  narrow :: Word32 -> Word32
  narrow w = w .&. maxVal
  
  instance Show Word12 where
    showsPrec p x = showsPrec p (unwrap x)
  
  instance Num Word12 where
    x + y = wrap (narrow (unwrap x + unwrap y))
    x - y = wrap (narrow (unwrap x - unwrap y))
    x * y = wrap (narrow (unwrap x * unwrap y))
    negate x = wrap (narrow (negate (unwrap x)))
    abs x = x
    signum x | unwrap x == 0  = 0
    signum _                  = 1
    fromInteger i = wrap (narrow(fromInteger i))
  
  instance Bounded Word12 where
    minBound = 0
    maxBound = wrap maxVal
  
  instance Real Word12 where
    toRational x = toInteger (unwrap x) % 1
  
  {- following Enum support stuff stolen from GHC libraries -}
  
  {-# NOINLINE toEnumError #-}
  toEnumError :: (Show a) => String -> Int -> (a,a) -> b
  toEnumError inst_ty i bnds =
      error $ "Enum.toEnum{" ++ inst_ty ++ "}: tag (" ++
              show i ++
              ") is outside of bounds " ++
              show bnds
  
  {-# NOINLINE succError #-}
  succError :: String -> a
  succError inst_ty =
      error $ "Enum.succ{" ++ inst_ty ++ "}: tried to take `succ' of maxBound"
  
  {-# NOINLINE predError #-}
  predError :: String -> a
  predError inst_ty =
      error $ "Enum.pred{" ++ inst_ty ++ "}: tried to take `pred' of minBound"
  
  integralEnumFrom :: (Integral a, Bounded a) => a -> [a]
  integralEnumFrom n = map fromInteger [toInteger n .. toInteger (maxBound `asTypeOf` n)]
  
  integralEnumFromThen :: (Integral a, Bounded a) => a -> a -> [a]
  integralEnumFromThen n1 n2
    | i_n2 >= i_n1  = map fromInteger [i_n1, i_n2 .. toInteger (maxBound `asTypeOf` n1)]
    | otherwise     = map fromInteger [i_n1, i_n2 .. toInteger (minBound `asTypeOf` n1)]
    where
      i_n1 = toInteger n1
      i_n2 = toInteger n2
  
  integralEnumFromTo :: Integral a => a -> a -> [a]
  integralEnumFromTo n m = map fromInteger [toInteger n .. toInteger m]
  
  integralEnumFromThenTo :: Integral a => a -> a -> a -> [a]
  integralEnumFromThenTo n1 n2 m
    = map fromInteger [toInteger n1, toInteger n2 .. toInteger m]
  
  instance Enum Word12 where
    succ x
        | x /= maxBound = x + 1
        | otherwise     = succError ("Word" ++ show bits)
    pred x
        | x /= minBound = x - 1
        | otherwise     = predError ("Word" ++ show bits)
    toEnum i
        | i >= 0 && i <= fromIntegral (maxBound::T)
                        = wrap (toEnum i)
        | otherwise     = toEnumError ("Word" ++ show bits) i (0,maxBound::T)
    fromEnum x = fromEnum (unwrap x) 
    enumFrom            = integralEnumFrom
    enumFromThen        = integralEnumFromThen
    enumFromTo          = integralEnumFromTo
    enumFromThenTo      = integralEnumFromThenTo
  
  
  {-# RULES
  "fromIntegral/Word12->Word32" fromIntegral = fromIntegral . unwrap
  "fromIntegral/Word12->Int"    fromIntegral = fromIntegral . unwrap
  "fromIntegral/Word12->Word12" fromIntegral = id :: Word12 -> Word12
    #-}
  
  instance Ix Word12 where
    range (m,n)                    = [m..n]
    index b@(m,_) i  | inRange b i = fromIntegral (i - m)
                     | otherwise   = error "Error in array index"
    inRange (m,n) i                = m <= i && i <= n
  
  instance Read Word12 where
    readsPrec p s = [(fromInteger x, r) | (x, r) <- readsPrec p s] 
  
  instance Integral Word12 where
    x `quot` y = wrap(unwrap x `quot` unwrap y)
    x `rem` y = wrap(unwrap x `rem` unwrap y)
    x `div` y = wrap(unwrap x `div` unwrap y)
    x `mod` y = wrap(unwrap x `mod` unwrap y)
    quotRem x y = (wrap q,wrap r) where (q,r) = quotRem (unwrap x) (unwrap y)
    divMod x y = (wrap d,wrap m) where (d,m) = divMod (unwrap x) (unwrap y)
    toInteger x = toInteger (unwrap x)
  
  instance Bits Word12 where
    x .&. y = wrap (unwrap x .&. unwrap y)
    x .|. y = wrap (unwrap x .|. unwrap y)
    x `xor` y = wrap (unwrap x `xor`  unwrap y)
    complement x = wrap (unwrap x `xor` maxVal)
    x `shift` i = wrap (narrow (unwrap x `shift` i))
    x `rotate` i 
       | i == 0    = x
       | otherwise = wrap (narrow ((x' `shiftL` i') .|. (x' `shiftR` (bits - i'))))
       where x' = unwrap x
             i' = i `mod` bits -- always positive
    bitSize  _               = bits
    isSigned _               = False
    
  
  