{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module HaskellWorks.Data.RankSelect.BitSeq
( BitSeq(..)
, mempty
, size
, fromWord64s
, fromPartialWord64s
, toPartialWord64s
, fromBools
, toBools
, BS.splitAt
, take
, drop
, (<|), (><), (|>)
, select1
) where
import Data.Coerce
import Data.Foldable
import Data.Monoid
import Data.Word
import HaskellWorks.Data.Bits.BitWise
import HaskellWorks.Data.FingerTree (ViewL (..), ViewR (..), (<|), (><), (|>))
import HaskellWorks.Data.Positioning
import HaskellWorks.Data.RankSelect.Base.Select1 (select1)
import HaskellWorks.Data.RankSelect.Internal.BitSeq (BitSeq (BitSeq), BitSeqFt, Elem (Elem))
import Prelude hiding (drop, max, min, splitAt, take)
import qualified Data.List as L
import qualified HaskellWorks.Data.FingerTree as FT
import qualified HaskellWorks.Data.RankSelect.Internal.BitSeq as BS
import qualified HaskellWorks.Data.RankSelect.Internal.Word as W
empty :: BitSeq
empty = BitSeq FT.empty
size :: BitSeq -> Count
size (BitSeq parens) = BS.measureBitCount (FT.measure parens)
fromWord64s :: Traversable f => f Word64 -> BitSeq
fromWord64s = foldl go empty
where go :: BitSeq -> Word64 -> BitSeq
go ps w = BitSeq (BS.parens ps |> Elem w 64)
fromPartialWord64s :: Traversable f => f (Word64, Count) -> BitSeq
fromPartialWord64s = foldl go empty
where go :: BitSeq -> (Word64, Count) -> BitSeq
go ps (w, n) = BitSeq (BS.parens ps |> Elem w n)
toPartialWord64s :: BitSeq -> [(Word64, Count)]
toPartialWord64s = L.unfoldr go . coerce
where go :: BitSeqFt -> Maybe ((Word64, Count), BitSeqFt)
go ft = case FT.viewl ft of
BS.Elem w n :< rt -> Just ((w, coerce n), rt)
FT.EmptyL -> Nothing
fromBools :: [Bool] -> BitSeq
fromBools = go empty
where go :: BitSeq -> [Bool] -> BitSeq
go (BitSeq ps) (b:bs) = case FT.viewr ps of
FT.EmptyR -> go (BitSeq (FT.singleton (Elem b' 1))) bs
lt :> Elem w n ->
let newPs = if n >= 64
then ps |> Elem b' 1
else lt |> Elem (w .|. (b' .<. fromIntegral n)) (n + 1)
in go (BitSeq newPs) bs
where b' = if b then 1 else 0 :: Word64
go ps [] = ps
toBools :: BitSeq -> [Bool]
toBools ps = toBoolsDiff ps []
toBoolsDiff :: BitSeq -> [Bool] -> [Bool]
toBoolsDiff ps = mconcat (fmap go (toPartialWord64s ps))
where go :: (Word64, Count) -> [Bool] -> [Bool]
go (w, n) = W.partialToBoolsDiff (fromIntegral n) w
drop :: Count -> BitSeq -> BitSeq
drop n ps = snd (BS.splitAt n ps)
take :: Count -> BitSeq -> BitSeq
take n ps = fst (BS.splitAt n ps)