module HaskellWorks.Data.Bits.PopCount.PopCount1
    ( PopCount1(..)
    ) where
import qualified Data.Bits                              as DB
import qualified Data.Vector                            as DV
import qualified Data.Vector.Storable                   as DVS
import           Data.Word
import           HaskellWorks.Data.Bits.BitWise
import           HaskellWorks.Data.Bits.Types.Broadword
import           HaskellWorks.Data.Bits.Types.Builtin
import           HaskellWorks.Data.Int.Widen.Widen64
import           HaskellWorks.Data.Positioning
import           Prelude                                as P
type FastWord a = Builtin a
fastWord :: a -> FastWord a
fastWord = Builtin
class PopCount1 v where
  popCount1 :: v -> Count
instance PopCount1 Bool where
  popCount1 True  = 1
  popCount1 False = 0
  
instance PopCount1 (Broadword Word8) where
  popCount1 (Broadword x0) = widen64 x3
    where
      x1 = x0  ((x0 .&. 0xaa) .>. 1)
      x2 = (x1 .&. 0x33) + ((x1 .>. 2) .&. 0x33)
      x3 = (x2 + (x2 .>. 4)) .&. 0x0f
  
instance PopCount1 (Broadword Word16) where
  popCount1 (Broadword x0) = widen64 ((x3 * 0x0101) .>. 8)
    where
      x1 = x0  ((x0 .&. 0xaaaa) .>. 1)
      x2 = (x1 .&. 0x3333) + ((x1 .>. 2) .&. 0x3333)
      x3 = (x2 + (x2 .>. 4)) .&. 0x0f0f
  
instance PopCount1 (Broadword Word32) where
  popCount1 (Broadword x0) = widen64 ((x3 * 0x01010101) .>. 24)
    where
      x1 = x0  ((x0 .&. 0xaaaaaaaa) .>. 1)
      x2 = (x1 .&. 0x33333333) + ((x1 .>. 2) .&. 0x33333333)
      x3 = (x2 + (x2 .>. 4)) .&. 0x0f0f0f0f
  
instance PopCount1 (Broadword Word64) where
  popCount1 (Broadword x0) = widen64 (x3 * 0x0101010101010101) .>. 56
    where
      x1 = x0  ((x0 .&. 0xaaaaaaaaaaaaaaaa) .>. 1)
      x2 = (x1 .&. 0x3333333333333333) + ((x1 .>. 2) .&. 0x3333333333333333)
      x3 = (x2 + (x2 .>. 4)) .&. 0x0f0f0f0f0f0f0f0f
  
instance PopCount1 (Builtin Word8) where
  popCount1 (Builtin x0) = fromIntegral (DB.popCount x0)
  
instance PopCount1 (Builtin Word16) where
  popCount1 (Builtin x0) = fromIntegral (DB.popCount x0)
  
instance PopCount1 (Builtin Word32) where
  popCount1 (Builtin x0) = fromIntegral (DB.popCount x0)
  
instance PopCount1 (Builtin Word64) where
  popCount1 (Builtin x0) = fromIntegral (DB.popCount x0)
  
instance PopCount1 Word8 where
  popCount1 = fromIntegral . popCount1 . fastWord
  
instance PopCount1 Word16 where
  popCount1 = fromIntegral . popCount1 . fastWord
  
instance PopCount1 Word32 where
  popCount1 = fromIntegral . popCount1 . fastWord
  
instance PopCount1 Word64 where
  popCount1 = fromIntegral . popCount1 . fastWord
  
instance PopCount1 a => PopCount1 [a] where
  popCount1 = P.sum . fmap popCount1
  
instance PopCount1 a => PopCount1 (DV.Vector a) where
  popCount1 = DV.foldl (\c -> (c +) . popCount1) 0
  
instance (PopCount1 a, DVS.Storable a) => PopCount1 (DVS.Vector a) where
  popCount1 = DVS.foldl (\c -> (c +) . popCount1) 0