{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 800
{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
#endif
module Data.Bit.Select1
( select1
) where
#include "MachDeps.h"
import Data.Bits
#if MIN_VERSION_base(4,11,0) && defined(BMI2_ENABLED)
import Data.Bits.Pdep
import Data.Int
#endif
import Data.Word
infixl 8 .>.
(.>.) :: Bits a => a -> Int -> a
(.>.) = shiftR
infixl 8 .<.
(.<.) :: Bits a => a -> Int -> a
(.<.) = shiftL
#if MIN_VERSION_base(4,11,0) && defined(BMI2_ENABLED)
select1Word64Bmi2Base0 :: Word64 -> Word64 -> Word64
select1Word64Bmi2Base0 w r = fromIntegral (countTrailingZeros (pdep (1 .<. fromIntegral r) w))
{-# INLINE select1Word64Bmi2Base0 #-}
select1Word64Bmi2 :: Word64 -> Word64 -> Word64
select1Word64Bmi2 w r =
let zeros = countTrailingZeros (pdep (1 .<. fromIntegral (r - 1)) w) :: Int
mask = fromIntegral ((fromIntegral (zeros .<. 57) :: Int64) `shiftR` 63) :: Word64
in (fromIntegral zeros .|. mask) + 1
{-# INLINE select1Word64Bmi2 #-}
select1Word32Bmi2 :: Word32 -> Word64 -> Word64
select1Word32Bmi2 w r =
let zeros = countTrailingZeros (pdep (1 .<. fromIntegral (r - 1)) w) :: Int
mask = fromIntegral ((fromIntegral (zeros .<. 58) :: Int64) `shiftR` 63) :: Word64
in (fromIntegral zeros .|. mask) + 1
{-# INLINE select1Word32Bmi2 #-}
#endif
select1Word64Broadword :: Word64 -> Word64 -> Word64
select1Word64Broadword _ 0 = 0
select1Word64Broadword v rn =
let a = (v .&. 0x5555555555555555) + ((v .>. 1) .&. 0x5555555555555555) in
let b = (a .&. 0x3333333333333333) + ((a .>. 2) .&. 0x3333333333333333) in
let c = (b .&. 0x0f0f0f0f0f0f0f0f) + ((b .>. 4) .&. 0x0f0f0f0f0f0f0f0f) in
let d = (c .&. 0x00ff00ff00ff00ff) + ((c .>. 8) .&. 0x00ff00ff00ff00ff) in
let e = (d .&. 0x0000ffff0000ffff) + ((d .>. 16) .&. 0x0000ffff0000ffff) in
let f = (e .&. 0x00000000ffffffff) + ((e .>. 32) .&. 0x00000000ffffffff) in
let r0 = f + 1 - fromIntegral rn in
let s0 = 64 :: Word64 in
let t0 = (d .>. 32) + (d .>. 48) in
let s1 = s0 - ((t0 - r0) .&. 256) .>. 3 in
let r1 = r0 - (t0 .&. ((t0 - r0) .>. 8)) in
let t1 = (d .>. fromIntegral (s1 - 16)) .&. 0xff in
let s2 = s1 - ((t1 - r1) .&. 256) .>. 4 in
let r2 = r1 - (t1 .&. ((t1 - r1) .>. 8)) in
let t2 = (c .>. fromIntegral (s2 - 8)) .&. 0xf in
let s3 = s2 - ((t2 - r2) .&. 256) .>. 5 in
let r3 = r2 - (t2 .&. ((t2 - r2) .>. 8)) in
let t3 = (b .>. fromIntegral (s3 - 4)) .&. 0x7 in
let s4 = s3 - ((t3 - r3) .&. 256) .>. 6 in
let r4 = r3 - (t3 .&. ((t3 - r3) .>. 8)) in
let t4 = (a .>. fromIntegral (s4 - 2)) .&. 0x3 in
let s5 = s4 - ((t4 - r4) .&. 256) .>. 7 in
let r5 = r4 - (t4 .&. ((t4 - r4) .>. 8)) in
let t5 = (v .>. fromIntegral (s5 - 1)) .&. 0x1 in
let s6 = s5 - ((t5 - r5) .&. 256) .>. 8 in
fromIntegral s6
{-# INLINE select1Word64Broadword #-}
select1Word32Broadword :: Word32 -> Word64 -> Word64
select1Word32Broadword _ 0 = 0
select1Word32Broadword v rn =
let a = (v .&. 0x55555555) + ((v .>. 1) .&. 0x55555555) in
let b = (a .&. 0x33333333) + ((a .>. 2) .&. 0x33333333) in
let c = (b .&. 0x0f0f0f0f) + ((b .>. 4) .&. 0x0f0f0f0f) in
let d = (c .&. 0x00ff00ff) + ((c .>. 8) .&. 0x00ff00ff) in
let e = (d .&. 0x000000ff) + ((d .>. 16) .&. 0x000000ff) in
let r0 = e + 1 - fromIntegral rn in
let s0 = 64 :: Word32 in
let t0 = (d .>. 32) + (d .>. 48) in
let s1 = s0 - ((t0 - r0) .&. 256) .>. 3 in
let r1 = r0 - (t0 .&. ((t0 - r0) .>. 8)) in
let t1 = (d .>. fromIntegral (s1 - 16)) .&. 0xff in
let s2 = s1 - ((t1 - r1) .&. 256) .>. 4 in
let r2 = r1 - (t1 .&. ((t1 - r1) .>. 8)) in
let t2 = (c .>. fromIntegral (s2 - 8)) .&. 0xf in
let s3 = s2 - ((t2 - r2) .&. 256) .>. 5 in
let r3 = r2 - (t2 .&. ((t2 - r2) .>. 8)) in
let t3 = (b .>. fromIntegral (s3 - 4)) .&. 0x7 in
let s4 = s3 - ((t3 - r3) .&. 256) .>. 6 in
let r4 = r3 - (t3 .&. ((t3 - r3) .>. 8)) in
let t4 = (a .>. fromIntegral (s4 - 2)) .&. 0x3 in
let s5 = s4 - ((t4 - r4) .&. 256) .>. 7 in
let r5 = r4 - (t4 .&. ((t4 - r4) .>. 8)) in
let t5 = (v .>. fromIntegral (s5 - 1)) .&. 0x1 in
let s6 = s5 - ((t5 - r5) .&. 256) .>. 8 in
fromIntegral s6
{-# INLINE select1Word32Broadword #-}
select1Word64 :: Word64 -> Word64 -> Word64
#if MIN_VERSION_base(4,11,0) && defined(BMI2_ENABLED)
select1Word64 = select1Word64Bmi2
#else
select1Word64 = select1Word64Broadword
#endif
{-# INLINE select1Word64 #-}
select1Word32 :: Word32 -> Word64 -> Word64
#if MIN_VERSION_base(4,11,0) && defined(BMI2_ENABLED)
select1Word32 = select1Word32Bmi2
#else
select1Word32 = select1Word32Broadword
#endif
{-# INLINE select1Word32 #-}
select1 :: Word -> Int -> Int
#if WORD_SIZE_IN_BITS == 64
select1 w i = fromIntegral $ select1Word64 (fromIntegral w) (fromIntegral i)
#elif WORD_SIZE_IN_BITS == 32
select1 w i = fromIntegral $ select1Word32 (fromIntegral w) (fromIntegral i)
#else
#error unsupported WORD_SIZE_IN_BITS config
#endif
{-# INLINE select1 #-}