packages feed

hw-rankselect-base 0.2.0.2 → 0.3.0.0

raw patch · 12 files changed

+461/−201 lines, 12 filesdep +bits-extradep +hedgehogdep +hw-hedgehogPVP ok

version bump matches the API change (PVP)

Dependencies added: bits-extra, hedgehog, hw-hedgehog, hw-hspec-hedgehog

API changes (from Hackage documentation)

+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word32 :: Word32 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word32Bmi2 :: Word32 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word32Broadword :: Word32 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word64 :: Word64 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word64Bmi2 :: Word64 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word64Bmi2Base0 :: Word64 -> Word64 -> Word64
+ HaskellWorks.Data.RankSelect.Base.Internal: select1Word64Broadword :: Word64 -> Word64 -> Word64

Files

README.md view
@@ -1,4 +1,112 @@ # hw-rankselect-base-[![0.0-branch](https://circleci.com/gh/haskell-works/hw-rankselect-base/tree/0.0-branch.svg?style=svg)](https://circleci.com/gh/haskell-works/hw-rankselect-base/tree/0.0-branch)+[![CircleCI](https://circleci.com/gh/haskell-works/hw-rankselect-base.svg?style=svg)](https://circleci.com/gh/haskell-works/hw-rankselect-base)  Rank and select operations.++This library will use support for some BMI2 CPU instructions on some x86 based+CPUs if compiled with the appropriate flags on `ghc-8.4.1` or later.++## Rank and select++This library provides the following functions on various types:++* `rank1`+* `rank0`+* `select1`+* `select0`++Type class instances are provided for the following primitive types:++* `Bool`+* `Word8`+* `Word16`+* `Word32`+* `Word64`++Moreover additional type class instances are provided for `[]`, `Vector`+from both `Data.Vector`, and `Data.Vector.Storable` of these primitive+types.++The collection-based type classes instances are not intended to be used+in high-performance code because they have poor performance for most+purposes and are provided purely as reference implementations.++Bit-vectors larger than 64-bits need indexing to achieve higher performance.+A indexed bit-vector implementation can found in the+[hw-rankselect](https://hackage.haskell.org/package/hw-rankselect) package.++## Notes++This library follows standard 1-based counting conventions typically found in+Computer Science literature where `select1 10 2 = 4` as illustrated here:++```text+  8 7 6 5  [4]3 2 1+  0 0 0 0   1 0 1 0+```++The standard convention for the `bmi2` implementation, comes at a small cost.++An internal function `select1Word64Bmi2Base0` demonstrates 0-based counting+that is slightly faster when implemented with the `bmi2` instruction set where+`select1 10 1 = 3` as illustrated here:++```text+  7 6 5 4  [3]2 1 0+  0 0 0 0   1 0 1 0+```++## Compilation++It is sufficient to build, test and benchmark the library as follows+for emulated behaviour:++```text+stack build+stack test+stack bench+```++To target the BMI2 instruction set, add the `bmi2` flag:++```text+stack build --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2+stack test  --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2+stack bench --flag bits-extra:bmi2 --flag hw-rankselect-base:bmi2+```++## Benchmark results++The following benchmark shows the kinds of performance gain that can+be expected from enabling the BMI2 instruction set for CPU targets+that support them:++```text+benchmarking 64-bit/Once: Select1 Broadword+time                 14.75 ns   (14.63 ns .. 14.90 ns)+                     0.996 R²   (0.987 R² .. 0.999 R²)+mean                 15.35 ns   (14.92 ns .. 16.70 ns)+std dev              2.355 ns   (607.2 ps .. 4.849 ns)+variance introduced by outliers: 96% (severely inflated)++benchmarking 64-bit/Once: Select1 Bmi2+time                 6.026 ns   (5.933 ns .. 6.134 ns)+                     0.999 R²   (0.998 R² .. 0.999 R²)+mean                 6.024 ns   (5.966 ns .. 6.096 ns)+std dev              224.4 ps   (176.9 ps .. 318.6 ps)+variance introduced by outliers: 62% (severely inflated)++benchmarking 32-bit/Once: Select1 Broadword+time                 26.09 ns   (25.84 ns .. 26.40 ns)+                     0.999 R²   (0.998 R² .. 0.999 R²)+mean                 26.67 ns   (26.37 ns .. 27.01 ns)+std dev              1.017 ns   (848.4 ps .. 1.291 ns)+variance introduced by outliers: 61% (severely inflated)++benchmarking 32-bit/Once: Select1 Bmi2+time                 8.613 ns   (8.543 ns .. 8.687 ns)+                     0.999 R²   (0.999 R² .. 1.000 R²)+mean                 8.592 ns   (8.515 ns .. 8.671 ns)+std dev              248.3 ps   (216.2 ps .. 294.8 ps)+variance introduced by outliers: 48% (moderately inflated)+```
bench/Main.hs view
@@ -2,11 +2,14 @@  module Main where -import           Criterion.Main-import qualified Data.Vector.Storable                                   as DVS-import           Data.Word-import           HaskellWorks.Data.RankSelect.Base+import Criterion.Main+import Data.Bits.Pdep+import Data.Word+import HaskellWorks.Data.RankSelect.Base+import HaskellWorks.Data.RankSelect.Base.Internal +import qualified Data.Vector.Storable as DVS+ setupEnvVector :: Int -> IO (DVS.Vector Word64) setupEnvVector n = return $ DVS.fromList (take n (cycle [maxBound, 0])) @@ -33,7 +36,24 @@     , bench "Select - Once" (whnf (select1  bv) 1)     , bench "Rank - Many"   (nf   (map (rank1 bv)) [0, 1000..10000000])     ]+  , env (return ()) $ \_ -> bgroup "64-bit"+    [ bench "Once: Rank1"               (whnf (rank1                  (0x5555555555555555 :: Word64)) 64)+    , bench "Once: Select1 Class"       (whnf (select1                (0x5555555555555555 :: Word64)) 64)+    , bench "Once: Select1 Function"    (whnf (select1Word64          (0x5555555555555555 :: Word64)) 64)+    , bench "Once: Select1 Broadword"   (whnf (select1Word64Broadword (0x5555555555555555 :: Word64)) 64)+    , bench "Once: Select1 Bmi2"        (whnf (select1Word64Bmi2      (0x5555555555555555 :: Word64)) 64)+    , bench "Once: Select1 Bmi2Base0"   (whnf (select1Word64Bmi2Base0 (0x5555555555555555 :: Word64)) 64)+    ]+  , env (return ()) $ \_ -> bgroup "32-bit"+    [ bench "Once: Rank1"               (whnf (rank1                  (0x55555555 :: Word32)) 32)+    , bench "Once: Select1 Class"       (whnf (select1                (0x55555555 :: Word32)) 32)+    , bench "Once: Select1 Function"    (whnf (select1Word32          (0x55555555 :: Word32)) 32)+    , bench "Once: Select1 Broadword"   (whnf (select1Word32Broadword (0x55555555 :: Word32)) 32)+    , bench "Once: Select1 Bmi2"        (whnf (select1Word32Bmi2      (0x55555555 :: Word32)) 32)+    ]   ]  main :: IO ()-main = defaultMain benchRankSelect+main = do+  putStrLn $ "Fast pdep enabled: " <> show fastPdepEnabled+  defaultMain benchRankSelect
hw-rankselect-base.cabal view
@@ -1,70 +1,112 @@-name:                   hw-rankselect-base-version:                0.2.0.2-synopsis:               Rank-select base-description:            Please see README.md-homepage:               http://github.com/haskell-works/hw-rankselect-base#readme-license:                BSD3-license-file:           LICENSE-author:                 John Ky-maintainer:             newhoggy@gmail.com-copyright:              2016 John Ky-category:               Data-build-type:             Simple-extra-source-files:     README.md-cabal-version:          >= 1.22--library-  hs-source-dirs:       src-  exposed-modules:      HaskellWorks.Data.RankSelect.Base-                      , HaskellWorks.Data.RankSelect.Base.Rank-                      , HaskellWorks.Data.RankSelect.Base.Rank0-                      , HaskellWorks.Data.RankSelect.Base.Rank1-                      , HaskellWorks.Data.RankSelect.Base.Select-                      , HaskellWorks.Data.RankSelect.Base.Select0-                      , HaskellWorks.Data.RankSelect.Base.Select1-  build-depends:        base                          >= 4          && < 5-                      , hw-bits                       >= 0.4.0.0-                      , hw-int                        >= 0.0.0.1-                      , hw-prim                       >= 0.4.0.0-                      , hw-string-parse               >= 0.0.0.2-                      , safe-                      , vector--  default-language:     Haskell2010-  ghc-options:          -Wall -O2 -msse4.2+-- This file has been generated from package.yaml by hpack version 0.20.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: e621f9ce4411c3740727c5167f0f6488c0a25514832ad5d6359e86474477c5ff -test-suite hw-rankselect-base-test-  type:                 exitcode-stdio-1.0-  hs-source-dirs:       test-  main-is:              Spec.hs-  other-modules:        HaskellWorks.Data.RankSelect.Base.Rank0Spec-                      , HaskellWorks.Data.RankSelect.Base.Rank1Spec-                      , HaskellWorks.Data.RankSelect.Base.Select0Spec-                      , HaskellWorks.Data.RankSelect.Base.Select1Spec+name:           hw-rankselect-base+version:        0.3.0.0+synopsis:       Rank-select base+description:    Please see README.md+category:       Data+homepage:       http://github.com/haskell-works/hw-rankselect-base#readme+bug-reports:    https://github.com/haskell-works/hw-rankselect-base/issues+author:         John Ky+maintainer:     newhoggy@gmail.com+copyright:      2016 John Ky+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10 -  build-depends:        base                          >= 4          && < 5-                      , hspec-                      , hw-bits                       >= 0.4.0.0-                      , hw-prim                       >= 0.4.0.0-                      , hw-rankselect-base-                      , QuickCheck-                      , vector-  ghc-options:          -threaded -rtsopts -with-rtsopts=-N -Wall-  default-language:     Haskell2010+extra-source-files:+    README.md  source-repository head-  type:     git+  type: git   location: https://github.com/haskell-works/hw-rankselect-base +flag bmi2+  description: Enable bmi2 instruction set+  manual: False+  default: False++library+  hs-source-dirs:+      src+  ghc-options: -Wall -O2 -msse4.2+  build-depends:+      base >=4 && <5+    , bits-extra+    , hw-bits >=0.4.0.0+    , hw-int >=0.0.0.1+    , hw-prim >=0.4.0.0+    , hw-string-parse >=0.0.0.2+    , safe+    , vector+  if (flag(bmi2)) && (impl(ghc >=8.4.1))+    ghc-options: -mbmi2 -msse4.2+    cpp-options: -DBMI2_ENABLED+  exposed-modules:+      HaskellWorks.Data.RankSelect.Base+      HaskellWorks.Data.RankSelect.Base.Internal+      HaskellWorks.Data.RankSelect.Base.Rank+      HaskellWorks.Data.RankSelect.Base.Rank0+      HaskellWorks.Data.RankSelect.Base.Rank1+      HaskellWorks.Data.RankSelect.Base.Select+      HaskellWorks.Data.RankSelect.Base.Select0+      HaskellWorks.Data.RankSelect.Base.Select1+  other-modules:+      Paths_hw_rankselect_base+  default-language: Haskell2010++test-suite hw-rankselect-base-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  hs-source-dirs:+      test+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      QuickCheck+    , base >=4 && <5+    , bits-extra+    , hedgehog+    , hspec+    , hw-bits >=0.4.0.0+    , hw-hedgehog+    , hw-hspec-hedgehog+    , hw-prim >=0.4.0.0+    , hw-rankselect-base+    , vector+  if (flag(bmi2)) && (impl(ghc >=8.4.1))+    ghc-options: -mbmi2 -msse4.2+    cpp-options: -DBMI2_ENABLED+  other-modules:+      HaskellWorks.Data.RankSelect.Base.InternalSpec+      HaskellWorks.Data.RankSelect.Base.Rank0Spec+      HaskellWorks.Data.RankSelect.Base.Rank1Spec+      HaskellWorks.Data.RankSelect.Base.Select0Spec+      HaskellWorks.Data.RankSelect.Base.Select1Spec+      Paths_hw_rankselect_base+  default-language: Haskell2010+ benchmark bench-    Type: exitcode-stdio-1.0-    HS-Source-Dirs: bench-    Main-Is: Main.hs-    GHC-Options: -O2 -Wall -msse4.2-    Default-Language: Haskell2010-    Build-Depends:      base                          >= 4          && < 5-                      , criterion-                      , hw-bits                       >= 0.4.0.0-                      , hw-prim                       >= 0.4.0.0-                      , hw-rankselect-base-                      , vector+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs:+      bench+  ghc-options: -Wall -O2 -msse4.2+  build-depends:+      base >=4 && <5+    , bits-extra+    , criterion+    , hw-bits >=0.4.0.0+    , hw-prim >=0.4.0.0+    , hw-rankselect-base+    , vector+  if (flag(bmi2)) && (impl(ghc >=8.4.1))+    ghc-options: -mbmi2 -msse4.2+    cpp-options: -DBMI2_ENABLED+  other-modules:+      Paths_hw_rankselect_base+  default-language: Haskell2010
+ src/HaskellWorks/Data/RankSelect/Base/Internal.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE CPP #-}++module HaskellWorks.Data.RankSelect.Base.Internal+    ( select1Word64Broadword+    , select1Word64Bmi2+    , select1Word64Bmi2Base0+    , select1Word64+    , select1Word32Broadword+    , select1Word32Bmi2+    , select1Word32+    ) where++import Data.Bits                      (countTrailingZeros, shiftR)+import Data.Bits.Pdep+import Data.Int+import Data.Word+import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Int.Narrow++select1Word64Bmi2Base0 :: Word64 -> Word64 -> Word64+select1Word64Bmi2Base0 w r = fromIntegral (countTrailingZeros (pdep (1 .<. r) w))+{-# INLINE select1Word64Bmi2Base0 #-}++select1Word64Bmi2 :: Word64 -> Word64 -> Word64+select1Word64Bmi2 w r =+  let zeros = countTrailingZeros (pdep (1 .<. (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 .<. (r - 1)) w) :: Int+      mask  = fromIntegral ((fromIntegral (zeros .<. 58) :: Int64) `shiftR` 63) :: Word64+  in (fromIntegral zeros .|. mask) + 1+{-# INLINE select1Word32Bmi2 #-}++select1Word64Broadword :: Word64 -> Word64 -> Word64+select1Word64Broadword _ 0 = 0+select1Word64Broadword v rn =+  -- Do a normal parallel bit count for a 64-bit integer,+  -- but store all intermediate steps.+  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+  -- Now do branchless select!+  let r0 = f + 1 - narrow64 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 =+  -- Do a normal parallel bit count for a 64-bit integer,+  -- but store all intermediate steps.+  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+  -- Now do branchless select!+  let r0 = e + 1 - narrow32 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 #-}
src/HaskellWorks/Data/RankSelect/Base/Rank.hs view
@@ -2,13 +2,13 @@ {-# LANGUAGE MultiParamTypeClasses #-}  module HaskellWorks.Data.RankSelect.Base.Rank-    ( -- * Rank & Select-      Rank(..)-    ) where+  ( -- * Rank & Select+    Rank(..)+  ) where -import           HaskellWorks.Data.Positioning-import           HaskellWorks.Data.RankSelect.Base.Rank0-import           HaskellWorks.Data.RankSelect.Base.Rank1+import HaskellWorks.Data.Positioning+import HaskellWorks.Data.RankSelect.Base.Rank0+import HaskellWorks.Data.RankSelect.Base.Rank1  class Eq a => Rank v a where   rank :: a -> v -> Count -> Count
src/HaskellWorks/Data/RankSelect/Base/Rank0.hs view
@@ -6,17 +6,18 @@     ( Rank0(..)     ) where -import qualified Data.Vector                                as DV-import qualified Data.Vector.Storable                       as DVS-import           Data.Word-import           HaskellWorks.Data.AtIndex-import           HaskellWorks.Data.Bits.BitShown-import           HaskellWorks.Data.Bits.ElemFixedBitSize-import           HaskellWorks.Data.Bits.PopCount.PopCount0-import           HaskellWorks.Data.Positioning-import           HaskellWorks.Data.RankSelect.Base.Rank1    as X-import           Prelude                                    as P+import Data.Word+import HaskellWorks.Data.AtIndex+import HaskellWorks.Data.Bits.BitShown+import HaskellWorks.Data.Bits.ElemFixedBitSize+import HaskellWorks.Data.Bits.PopCount.PopCount0+import HaskellWorks.Data.Positioning+import HaskellWorks.Data.RankSelect.Base.Rank1   as X+import Prelude                                   as P +import qualified Data.Vector          as DV+import qualified Data.Vector.Storable as DVS+ {-# ANN module ("HLint: Ignore Reduce duplication"  :: String) #-}  class Rank0 v where@@ -42,7 +43,7 @@  instance Rank0 [Bool] where   rank0 = go 0-    where go r _ 0 = r+    where go r _ 0          = r           go r (False:bs) p = go (r + 1) bs (p - 1)           go r (True:bs) p  = go  r      bs (p - 1)           go _ [] _         = error "Out of range"
src/HaskellWorks/Data/RankSelect/Base/Rank1.hs view
@@ -13,7 +13,6 @@ import HaskellWorks.Data.Bits.ElemFixedBitSize import HaskellWorks.Data.Bits.PopCount.PopCount1 import HaskellWorks.Data.Positioning-import Prelude                                   as P  import qualified Data.Vector          as DV import qualified Data.Vector.Storable as DVS
src/HaskellWorks/Data/RankSelect/Base/Select.hs view
@@ -6,9 +6,9 @@       Select(..)     ) where -import           HaskellWorks.Data.Positioning-import           HaskellWorks.Data.RankSelect.Base.Select0-import           HaskellWorks.Data.RankSelect.Base.Select1+import HaskellWorks.Data.Positioning+import HaskellWorks.Data.RankSelect.Base.Select0+import HaskellWorks.Data.RankSelect.Base.Select1  class Eq a => Select v a where   select :: a -> v -> Count -> Count
src/HaskellWorks/Data/RankSelect/Base/Select0.hs view
@@ -6,17 +6,18 @@     ( Select0(..)     ) where -import qualified Data.Vector                                as DV-import qualified Data.Vector.Storable                       as DVS-import           Data.Word-import           HaskellWorks.Data.AtIndex-import           HaskellWorks.Data.Bits.BitShown-import           HaskellWorks.Data.Bits.BitWise-import           HaskellWorks.Data.Bits.ElemFixedBitSize-import           HaskellWorks.Data.Bits.PopCount.PopCount0-import           HaskellWorks.Data.Positioning-import           HaskellWorks.Data.RankSelect.Base.Select1+import Data.Word+import HaskellWorks.Data.AtIndex+import HaskellWorks.Data.Bits.BitShown+import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Bits.ElemFixedBitSize+import HaskellWorks.Data.Bits.PopCount.PopCount0+import HaskellWorks.Data.Positioning+import HaskellWorks.Data.RankSelect.Base.Select1 +import qualified Data.Vector          as DV+import qualified Data.Vector.Storable as DVS+ {-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}  class Select0 v where@@ -43,7 +44,7 @@  instance Select0 [Bool] where   select0 = go 0-    where go r _ 0 = r+    where go r _ 0          = r           go r (False:bs) c = go (r + 1) bs (c - 1)           go r (True:bs)  c = go (r + 1) bs  c           go _ []         _ = error "Out of range"@@ -56,7 +57,7 @@           go u d acc = let w = head u in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINABLE select0 #-}  instance Select0 [Word16] where@@ -66,7 +67,7 @@           go u d acc = let w = head u in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINABLE select0 #-}  instance Select0 [Word32] where@@ -76,7 +77,7 @@           go u d acc = let w = head u in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINABLE select0 #-}  instance Select0 [Word64] where@@ -86,7 +87,7 @@           go u d acc = let w = head u in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINABLE select0 #-}  instance Select0 (DV.Vector Word8) where@@ -95,7 +96,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DV.Vector Word16) where@@ -104,7 +105,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DV.Vector Word32) where@@ -113,7 +114,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DV.Vector Word64) where@@ -122,7 +123,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DVS.Vector Word8) where@@ -131,7 +132,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DVS.Vector Word16) where@@ -140,7 +141,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DVS.Vector Word32) where@@ -149,7 +150,7 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}  instance Select0 (DVS.Vector Word64) where@@ -158,5 +159,5 @@           go n d acc = let w = (v !!! n) in             case popCount0 w of               pc | d <= pc  -> select0 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINABLE select0 #-}
src/HaskellWorks/Data/RankSelect/Base/Select1.hs view
@@ -6,18 +6,20 @@     ( Select1(..)     ) where -import qualified Data.Vector                               as DV-import qualified Data.Vector.Storable                      as DVS-import           Data.Word-import           HaskellWorks.Data.AtIndex-import           HaskellWorks.Data.Bits.BitShown-import           HaskellWorks.Data.Bits.BitWise-import           HaskellWorks.Data.Bits.ElemFixedBitSize-import           HaskellWorks.Data.Bits.PopCount.PopCount1-import           HaskellWorks.Data.Int.Narrow-import           HaskellWorks.Data.Positioning-import           Prelude                                   as P+import Data.Word+import HaskellWorks.Data.AtIndex+import HaskellWorks.Data.Bits.BitShown+import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Bits.ElemFixedBitSize+import HaskellWorks.Data.Bits.PopCount.PopCount1+import HaskellWorks.Data.Int.Narrow+import HaskellWorks.Data.Positioning+import HaskellWorks.Data.RankSelect.Base.Internal+import Prelude +import qualified Data.Vector          as DV+import qualified Data.Vector.Storable as DVS+ class Select1 v where   select1 :: v -> Count -> Count @@ -62,77 +64,17 @@     fromIntegral s6   {-# INLINE select1 #-} --- TODO: Remove redundant code to optimise instance Select1 Word32 where-  select1 _ 0 = 0-  select1 v rn =-    -- Do a normal parallel bit count for a 64-bit integer,-    -- but store all intermediate steps.-    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-    -- Now do branchless select!-    let r0 = e + 1 - narrow32 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+  select1 = select1Word32   {-# INLINE select1 #-}  instance Select1 Word64 where-  select1 _ 0 = 0-  select1 v rn =-    -- Do a normal parallel bit count for a 64-bit integer,-    -- but store all intermediate steps.-    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-    -- Now do branchless select!-    let r0 = f + 1 - narrow64 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+  select1 = select1Word64   {-# INLINE select1 #-}  instance Select1 [Bool] where   select1 = go 0-    where go r _ 0 = r+    where go r _ 0          = r           go r (True :bs) c = go (r + 1) bs (c - 1)           go r (False:bs) c = go (r + 1) bs  c           go _ []         _ = error "Out of range"@@ -145,7 +87,7 @@           go u d acc = let w = head u in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINE select1 #-}  instance Select1 [Word16] where@@ -155,7 +97,7 @@           go u d acc = let w = head u in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINE select1 #-}  instance Select1 [Word32] where@@ -165,7 +107,7 @@           go u d acc = let w = head u in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINE select1 #-}  instance Select1 [Word64] where@@ -175,7 +117,7 @@           go u d acc = let w = head u in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (tail u) (d - pc) (acc + elemFixedBitSize u)+              pc -> go (tail u) (d - pc) (acc + elemFixedBitSize u)   {-# INLINE select1 #-}  instance Select1 (DVS.Vector Word8) where@@ -184,7 +126,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DVS.Vector Word16) where@@ -193,7 +135,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DVS.Vector Word32) where@@ -202,7 +144,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DVS.Vector Word64) where@@ -211,7 +153,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DV.Vector Word8) where@@ -220,7 +162,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DV.Vector Word16) where@@ -229,7 +171,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DV.Vector Word32) where@@ -238,7 +180,7 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}  instance Select1 (DV.Vector Word64) where@@ -247,5 +189,5 @@           go n d acc = let w = (v !!! n) in             case popCount1 w of               pc | d <= pc  -> select1 w d + acc-              pc            -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)+              pc -> go (n + 1) (d - pc) (acc + elemFixedBitSize v)   {-# INLINE select1 #-}
+ test/HaskellWorks/Data/RankSelect/Base/InternalSpec.hs view
@@ -0,0 +1,28 @@+module HaskellWorks.Data.RankSelect.Base.InternalSpec+  ( spec+  ) where++import Control.Monad                              (mfilter)+import Data.Bits                                  (popCount)+import HaskellWorks.Data.RankSelect.Base.Internal+import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified Hedgehog.Gen   as G+import qualified Hedgehog.Range as R++{-# ANN module ("HLint: ignore Redundant do" :: String) #-}+{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.RankSelect.Base.InternalSpec" $ do+  describe "Bmi2 and broadword implementations match" $ do+    it "64-bit" $ require $ property $ do+      s <- forAll $ mfilter (/= 0) (G.word64 R.constantBounded)+      r <- forAll $ G.word64 (R.linear 0 (fromIntegral (popCount s)))+      select1Word64Broadword s r === select1Word64Bmi2 s r+    it "32-bit" $ require $ property $ do+      s <- forAll $ mfilter (/= 0) (G.word32 R.constantBounded)+      r <- forAll $ G.word64 (R.linear 0 (fromIntegral (popCount s)))+      select1Word32Broadword s r === select1Word32Bmi2 s r
test/HaskellWorks/Data/RankSelect/Base/Select1Spec.hs view
@@ -10,8 +10,8 @@  import           Data.Maybe import           Data.Typeable-import qualified Data.Vector                                                as DV-import qualified Data.Vector.Storable                                       as DVS+import qualified Data.Vector                               as DV+import qualified Data.Vector.Storable                      as DVS import           Data.Word import           HaskellWorks.Data.Bits.BitRead import           HaskellWorks.Data.Bits.BitWise