{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies  #-}

module HaskellWorks.Data.PackedVector.PackedVector64
  ( PackedVector64(..)
  , empty
  , fromList
  , fromListN
  , toList
  ) where

import Control.DeepSeq
import Data.Int
import Data.Word
import GHC.Generics
import HaskellWorks.Data.AtIndex
import HaskellWorks.Data.Bits.BitWise
import HaskellWorks.Data.Bits.LoBitsSized
import HaskellWorks.Data.PackedVector.Internal
import HaskellWorks.Data.Positioning
import HaskellWorks.Data.Unsign
import Prelude                                 hiding (length)

import qualified Data.Vector.Storable as DVS

data PackedVector64 = PackedVector64
  { swBuffer    :: !(DVS.Vector Word64)
  , swBitSize   :: !Word
  , swBufferLen :: !Int
  } deriving (Eq, Show, Generic)

instance NFData PackedVector64

empty :: PackedVector64
empty =
  PackedVector64
  { swBuffer    = DVS.empty
  , swBufferLen = 0
  , swBitSize   = 1
  }

instance Container PackedVector64 where
  type Elem PackedVector64 = Word64

instance Length PackedVector64 where
  length = fromIntegral . swBufferLen
  {-# INLINE length #-}

instance AtIndex PackedVector64 where
  atIndex v i =
    let bitSize     = fromIntegral (swBitSize v) :: Count
        bitIndex    = fromIntegral (swBitSize v) * i
        (q, r)      = bitIndex `quotRem` 64
        vv          = swBuffer v
    in if r <= 64 - fromIntegral bitSize
      then -- Not crossing boundary
        ((vv !!! q) .>. unsign r) .&. loBitsSized bitSize
      else -- Crossing boundary
        let loBitsSize  = 64 - toCount r
            hiBitsSize  = bitSize - loBitsSize
            loBits      = ((vv !!! q) .>. unsign r) .&. loBitsSized loBitsSize
            hiBits      = (vv !!! (q + 1)) .&. loBitsSized hiBitsSize
        in  loBits .|. (hiBits .<. loBitsSize)
  (!!!)       = atIndex
  {-# INLINE (!!!)   #-}
  {-# INLINE atIndex #-}

fromList :: Count -> [Word64] -> PackedVector64
fromList wordLength ws = PackedVector64
  { swBuffer    = DVS.fromList (packBits wordLength ws)
  , swBufferLen = fromIntegral (length ws)
  , swBitSize   = fromIntegral wordLength
  }

fromListN :: Count -> Count -> [Word64] -> PackedVector64
fromListN vectorSize wordLength ws = PackedVector64
  { swBuffer    = DVS.fromListN (fromIntegral vectorSize) (packBits wordLength ws)
  , swBufferLen = fromIntegral (length ws)
  , swBitSize   = fromIntegral wordLength
  }

toList :: PackedVector64 -> [Word64]
toList v = unpackBits (swBufferLen v) (fromIntegral (swBitSize v)) (DVS.toList (swBuffer v))