{-# LANGUAGE FlexibleInstances #-}

module HaskellWorks.Data.EliasFano64
  ( EliasFano64(..)
  , FromEliasFano64(..)
  , ToEliasFano64(..)
  ) where

import qualified Data.Vector.Storable                   as DVS
import           Data.Word
import           HaskellWorks.Data.AtIndex
import           HaskellWorks.Data.Bits.BitWise
import           HaskellWorks.Data.Bits.Log2
import           HaskellWorks.Data.EliasFano64.Internal
import           HaskellWorks.Data.PackedVector         as PV
import           HaskellWorks.Data.Positioning
import           HaskellWorks.Data.Take
import           Safe
import           Prelude hiding (length, take)

data EliasFano64 = EliasFano64
  { hi      :: DVS.Vector Word64
  , lo      :: PackedVector64
  , loBits  :: Int
  , count   :: Count
  } deriving (Eq, Show)

class FromEliasFano64 a where
  fromEliasFano64 :: EliasFano64 -> a

class ToEliasFano64 a where
  toEliasFano64 :: a -> EliasFano64

instance ToEliasFano64 [Word64] where
  toEliasFano64 ws = case lastMay ws of
    Just end' -> EliasFano64
      { hi      = DVS.fromList (packToWord64 (packToWord32 (packToWord16 (packToWord8 (mkHiBits loBits' ws)))))
      , lo      = PV.fromList loBits' ws
      , loBits  = fromIntegral loBits'
      , count   = length'
      }
      where length' = length ws
            loBits' = fromIntegral (log2 (end' `div` length')) :: Count
    Nothing -> EliasFano64
      { hi      = DVS.empty
      , lo      = PV.empty
      , loBits  = 0
      , count   = 0
      }

instance FromEliasFano64 [Word64] where
  fromEliasFano64 ef = gen `fmap` take (count ef) [0 ..]
    where gen :: Int -> Word64
          gen i = let pos             = (loBits ef * i)                                         in
                  let (index, offset) = pos `quotRem` 64                                        in
                  let loValue         = (lo ef !!! fromIntegral index) .>. fromIntegral offset  in
                  let hiValue         = (lo ef !!! fromIntegral index) .>. fromIntegral offset  in
                  loValue + hiValue

-- instance AtIndex EliasFano64 where
--   (!!!)   v i = v !! fromIntegral i
--   atIndex v i = v !! fromIntegral i
--   {-# INLINE (!!!)   #-}
--   {-# INLINE atIndex #-}