{-# LANGUAGE MagicHash           #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}

module HaskellWorks.Data.Dsv.Internal.Vector
  ( empty64
  , constructNS
  , ltWord
  , indexCsvChunk
  , oddsMask
  ) where

import Control.Monad.ST
import Data.Bits.Pdep
import Data.Word
import Foreign.Storable                          (Storable)
import GHC.Int
import GHC.Prim
import GHC.Word                                  hiding (ltWord)
import HaskellWorks.Data.Bits.BitWise
import HaskellWorks.Data.Bits.PopCount.PopCount1
import HaskellWorks.Data.Positioning

import qualified Data.Vector.Storable         as DVS
import qualified Data.Vector.Storable.Mutable as DVSM

empty64 :: DVS.Vector Word64
empty64 = DVS.replicate 64 0
{-# NOINLINE empty64 #-}

constructNS :: forall a s. Storable a => Int -> s -> (s -> DVS.Vector a -> (s, a)) -> (s, DVS.Vector a)
constructNS n s f = DVS.createT (go 0 s)
  where go :: forall q. Int -> s -> ST q (s, DVS.MVector q a)
        go n1 s1 = do
          mv :: DVS.MVector q a <- DVSM.unsafeNew n
          u <- DVS.unsafeFreeze mv
          let (s2, w) = f s1 (DVS.take n1 u)
          DVSM.unsafeWrite mv n1 w
          return (s2, mv)
{-# INLINE constructNS #-}

ltWord :: Word64 -> Word64 -> Word64
ltWord (W64# a#) (W64# b#) = fromIntegral (I64# (ltWord# a# b#))
{-# INLINE ltWord #-}

indexCsvChunk ::
     Count
  -> Word64
  -> DVS.Vector Word64
  -> DVS.Vector Word64
  -> DVS.Vector Word64
  -> (DVS.Vector Word64, DVS.Vector Word64, Word64, Word64)
indexCsvChunk qqCount qqCarry mks nls qqs = runST $ do
  tmks <- DVSM.unsafeNew len
  tnls <- DVSM.unsafeNew len
  (newCount, newCarry) <- go 0 qqCount qqCarry tmks tnls
  rmks <- DVS.unsafeFreeze tmks
  rnls <- DVS.unsafeFreeze tnls
  return (rmks, rnls, newCount, newCarry)
  where len = DVS.length mks
        go :: Int -> Word64 -> Word64 -> DVSM.MVector z Word64 -> DVSM.MVector z Word64 -> ST z (Count, Word64)
        go i pc carry tmks tnls | i < len = do
          let qq = DVS.unsafeIndex qqs i
          let mk = DVS.unsafeIndex mks i
          let nl = DVS.unsafeIndex nls i

          let enters = pdep (oddsMask .<. (0x1 .&.      pc)) qq
          let leaves = pdep (oddsMask .<. (0x1 .&. comp pc)) qq

          let compLeaves    = comp leaves
          let preQuoteMask  = enters + compLeaves
          let quoteMask     = preQuoteMask + carry
          let newCarry      = quoteMask `ltWord` (enters .|. compLeaves .|. carry)

          DVSM.unsafeWrite tmks i ((nl .|. mk) .&. quoteMask)
          DVSM.unsafeWrite tnls i ( nl         .&. quoteMask)

          go (i + 1) (popCount1 qq + pc) newCarry tmks tnls
        go _ pc carry _ _ = return (pc, carry)
{-# INLINE indexCsvChunk #-}

oddsMask :: Word64
oddsMask = 0x5555555555555555
{-# INLINE oddsMask #-}