-- |
-- Module:      Data.Bit.PdepPext
-- Copyright:   (c) 2020 Andrew Lelechenko
-- Licence:     BSD3
--
-- | Parallel bit deposit and extract instructions.
-- https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets#Parallel_bit_deposit_and_extract

{-# LANGUAGE CPP          #-}
{-# LANGUAGE MagicHash    #-}

module Data.Bit.PdepPext
  ( pdep
  , pext
  ) where

#if MIN_VERSION_base(4,11,0)

import GHC.Exts

pdep :: Word -> Word -> Word
pdep (W# src#) (W# mask#) = W# (pdep# src# mask#)

pext :: Word -> Word -> Word
pext (W# src#) (W# mask#) = W# (pext# src# mask#)

#else

import Data.Bits

pdep :: Word -> Word -> Word
pdep = go 0
  where
    go :: Word -> Word -> Word -> Word
    go result _ 0 = result
    go result src mask = go newResult newSrc newMask
      where
        lowest    = 1 `shiftL` countTrailingZeros mask
        newResult = if src .&. 1 == 0 then result else result .|. lowest
        newSrc    = src `shiftR` 1
        newMask   = mask .&. complement lowest

pext :: Word -> Word -> Word
pext src mask = loop 0 0 0
  where
    loop i count acc
      | i >= finiteBitSize (0 :: Word)
      = acc
      | testBit mask i
      = loop (i + 1) (count + 1) (if testBit src i then setBit acc count else acc)
      | otherwise
      = loop (i + 1) count acc

#endif