-- | Bit manipulation

module Feldspar.Core.Functions.Bits where

import qualified Data.Bits as B
import Data.Int
import Data.Word

import Feldspar.Range
import Feldspar.Core.Types
import Feldspar.Core.Representation
import Feldspar.Core.Constructs

infixl 5 <<,>>
infixl 4 

-- | Redefinition of the standard 'B.Bits' class for Feldspar
class (B.Bits a, Type a, FullProp (Size a)) => Bits a
  where
  -- Logical operations
  (.&.)         :: Data a -> Data a -> Data a
  (.&.)         =  optAnd fullProp
  (.|.)         :: Data a -> Data a -> Data a
  (.|.)         =  optOr fullProp
  xor           :: Data a -> Data a -> Data a
  xor           =  optXor fullProp
  complement    :: Data a -> Data a
  complement    =  function1 "complement" fullProp B.complement

  -- Operations on individual bits
  bit           :: Data Index -> Data a
  bit           =  function1 "bit" fullProp (B.bit . fromIntegral)
  setBit        :: Data a -> Data Index -> Data a
  setBit        =  function2 "setBit" fullProp (liftIntWord B.setBit)
  clearBit      :: Data a -> Data Index -> Data a
  clearBit      =  function2 "clearBit" fullProp (liftIntWord B.clearBit)
  complementBit :: Data a -> Data Index -> Data a
  complementBit =  function2 "complementBit" fullProp (liftIntWord B.complementBit)
  testBit       :: Data a -> Data Index -> Data Bool
  testBit       =  function2 "testBit" fullProp (liftIntWord B.testBit)

  -- Moving bits around
  shiftLU       :: Data a -> Data Index -> Data a
  shiftLU       =  optZero $ function2 "shiftL" fullProp (liftIntWord B.shiftL)
  shiftRU       :: Data a -> Data Index -> Data a
  shiftRU       =  optZero $ function2 "shiftR" fullProp cShiftRU
  shiftL        :: Data a -> Data DefaultInt -> Data a
  shiftL        =  optZero $ function2 "shiftL" fullProp (liftInt B.shiftL)
  shiftR        :: Data a -> Data DefaultInt -> Data a
  shiftR        =  optZero $ function2 "shiftR" fullProp (liftInt B.shiftR)
  rotateLU      :: Data a -> Data Index -> Data a
  rotateLU      =  optZero $ function2 "rotateL" fullProp (liftIntWord B.rotateL )
  rotateRU      :: Data a -> Data Index -> Data a
  rotateRU      =  optZero $ function2 "rotateR" fullProp (liftIntWord B.rotateR )
  rotateL       :: Data a -> Data DefaultInt -> Data a
  rotateL       =  optZero $ function2 "rotateL" fullProp (liftInt B.rotateL)
  rotateR       :: Data a -> Data DefaultInt -> Data a
  rotateR       =  optZero $ function2 "rotateR" fullProp (liftInt B.rotateR)
  reverseBits   :: Data a -> Data a
  reverseBits   =  function1 "reverseBits" fullProp evalReverseBits

  -- Bulk bit operations
  -- | Returns the number of leading zeroes for unsigned types.
  -- For signed types it returns the number of unnecessary sign bits
  bitScan       :: Data a -> Data Index
  bitScan       =  function1 "bitScan" fullProp (fromIntegral . evalBitScan)
  bitCount      :: Data a -> Data Index
  bitCount      =  function1 "bitCount" fullProp (fromIntegral . evalBitCount)

  -- Queries about the type
  bitSize       :: Data a -> Data Index
  bitSize       =  function1 "bitSize" (\_ -> naturalRange) (fromIntegral . B.bitSize)
  isSigned      :: Data a -> Data Bool
  isSigned      =  function1 "isSigned" fullProp B.isSigned

-- TODO Some range propagation could be improved.
--      bitSize could have (range 0 32) instead of naturalRange.

liftIntWord :: (a -> Int -> b) -> (a -> DefaultWord -> b)
liftIntWord f x = f x . fromIntegral

liftInt :: (a -> Int -> b) -> (a -> DefaultInt -> b)
liftInt f x = f x . fromIntegral

()  :: Bits a => Data a -> Data a -> Data a
()  =  xor
(<<) :: Bits a => Data a -> Data Index -> Data a
(<<) =  shiftLU
(>>) :: Bits a => Data a -> Data Index -> Data a
(>>) =  shiftRU

optAnd :: (Bits a) =>
          (Size a -> Size a -> Size a)
       -> Data a -> Data a -> Data a
optAnd rangeProp x y =
    case (viewLiteral x, viewLiteral y) of
      (Just 0, _) -> value 0
      (_, Just 0) -> value 0
      (Just x, _) | isAllOnes x -> y
      (_, Just y) | isAllOnes y -> x
      _ -> function2 "(.&.)" rangeProp (B..&.) x y

optOr :: (Bits a) =>
         (Size a -> Size a -> Size a)
      -> Data a -> Data a -> Data a
optOr rangeProp x y =
    case (viewLiteral x, viewLiteral y) of
      (Just 0, _) -> y
      (_, Just 0) -> x
      (Just x, _) | isAllOnes x -> value allOnes
      (_, Just y) | isAllOnes y -> value allOnes
      _ -> function2 "(.|.)" rangeProp (B..|.) x y

optXor :: (Bits a) =>
          (Size a -> Size a -> Size a)
       -> Data a -> Data a -> Data a
optXor rangeProp x y =
    case (viewLiteral x, viewLiteral y) of
      (Just 0, _) -> y
      (_, Just 0) -> x
      (Just x, _) | isAllOnes x -> complement y
      (_, Just y) | isAllOnes y -> complement x
      _ -> function2 "xor" rangeProp B.xor x y

isAllOnes :: B.Bits a => a -> Bool
isAllOnes x = x Prelude.== B.complement 0

allOnes :: B.Bits a => a
allOnes = B.complement 0

optZero :: (Type n, Num n) => (a -> Data n -> a) -> a -> Data n -> a
optZero f x y = case viewLiteral y of
                  Just 0 -> x
                  _      -> f x y

evalBitScan :: B.Bits b => b -> Word
evalBitScan b =
   if B.isSigned b
   then scanLoop b (B.testBit b (B.bitSize b - 1)) (B.bitSize b - 2) 0
         else scanLoop b False (B.bitSize b - 1) 0
  where
    scanLoop b bit i n | i Prelude.< 0                = n
    scanLoop b bit i n | B.testBit b i Prelude./= bit = n
    scanLoop b bit i n | otherwise                    = scanLoop b bit (i-1) (n+1)

evalBitCount :: B.Bits b => b -> Word
evalBitCount b = loop b (B.bitSize b - 1) 0
  where
    loop b i n | i Prelude.< 0 = n
    loop b i n | B.testBit b i = loop b (i-1) (n+1)
    loop b i n | otherwise     = loop b (i-1) n

evalReverseBits :: B.Bits b => b -> b
evalReverseBits b = revLoop b 0 (0 `asTypeOf` b)
  where
    bitSize = B.bitSize b
    revLoop b i n | i Prelude.>= bitSize  = n
    revLoop b i n | B.testBit b i = revLoop b (i+1) (B.setBit n (bitSize - i - 1))
    revLoop b i n | otherwise     = revLoop b (i+1) n

{- TODO
   This is a hack until we have proper size information for DefaultWord.
   We do not want the Range module to have to know about DefaultWord so
   we patch the types for this range propagation function here.
-}
propRangeShiftLU r1 r2
    = rangeShiftLU r1 (mapMonotonic (\ (DefaultWord a) -> a) r2)
propRangeShiftRU r1 r2
    = rangeShiftRU r1 (mapMonotonic (\ (DefaultWord a) -> a) r2)
cShiftRU v (DefaultWord i) = correctShiftRU v i

{- TODO
   The reason we have to provide the range propagation functions
   in these instances is that if we would try to do it in the default
   methods of the class we would get a superclass constraint 'Size a ~ Range a'.
   GHC 6.12 doesn't support this.
-}
instance Bits Word8 where
  xor     = optXor rangeXor
  shiftLU = optZero $ function2 "shiftL" propRangeShiftLU (liftIntWord B.shiftL)
  shiftRU = optZero $ function2 "shiftR" propRangeShiftRU cShiftRU
instance Bits Int8 where
  xor = optXor rangeXor
instance Bits Word16 where
  xor = optXor rangeXor
  shiftLU = optZero $ function2 "shiftL" propRangeShiftLU (liftIntWord B.shiftL)
  shiftRU = optZero $ function2 "shiftR" propRangeShiftRU cShiftRU
instance Bits Int16 where
  xor = optXor rangeXor
instance Bits Word32 where
  xor = optXor rangeXor
  shiftLU = optZero $ function2 "shiftL" propRangeShiftLU (liftIntWord B.shiftL)
  shiftRU = optZero $ function2 "shiftR" propRangeShiftRU cShiftRU
instance Bits Int32 where
  xor = optXor rangeXor
instance Bits DefaultWord where
  xor = optXor rangeXor
  shiftLU = optZero $ function2 "shiftL" propRangeShiftLU (liftIntWord B.shiftL)
  shiftRU = optZero $ function2 "shiftR" propRangeShiftRU cShiftRU
instance Bits DefaultInt where
  xor = optXor rangeXor