packages feed

random-bytestring 0.1.1 → 0.1.2

raw patch · 12 files changed

+375/−114 lines, 12 filesdep +cryptonitedep +pcg-randombinary-added

Dependencies added: cryptonite, pcg-random

Files

CHANGELOG.md view
@@ -1,3 +1,17 @@+0.1.2+=====++*   Add support for PCG as PRNG in addition to MWC.++    The module `Data.ByteString.Random.PCG` uses PCG. The module+    `Data.ByteString.Random.MWC` uses MWC. The module `Data.ByteString.Random`+    uses MWC for backward compatibility.++*   Support usage of custom PRNGs through the type class `RandomWords` and the+    function `generate` in the module `Data.ByteString.Random.Internal`.++*   Add some benchmark results to package description+ 0.1.1 ===== 
benchmark/Benchmarks.hs view
@@ -14,6 +14,7 @@ ( Impl(..) , benchmarks , largeBenchmarks+, compareBenchmarks ) where  import Criterion@@ -42,7 +43,7 @@         [ singleThreaded impls 1024         , singleThreaded impls (1024 * 1024)         , singleThreaded impls (1024 * 1024 * 10)-        , singleThreaded impls (1024 * 1024 * 100)+        -- , singleThreaded impls (1024 * 1024 * 100)         ]     , bgroup "multi-threaded"         [ concurrent impls 10 1024@@ -52,14 +53,21 @@         ]     ] +compareBenchmarks ∷ [Impl] →  [Benchmark]+compareBenchmarks impls =+    [ singleThreaded impls (1024 * 1024)+    , singleThreaded impls (1024 * 1024 * 10)+    -- , singleThreaded impls (1024 * 1024 * 100)+    ]+ concurrent ∷ [Impl] → Natural → Natural → Benchmark-concurrent impls c n = bgroup (show c <> "-" <> show n)+concurrent impls c n = bgroup (show c <> "-" <> show n <> "-bytes")     [ cgo label f | (Impl label f) ←  impls ]   where     cgo s f = bench s $ nfIO $         forConcurrently [0..(c-1)] (const $ f n)  singleThreaded ∷ [Impl] → Natural → Benchmark-singleThreaded impls n = bgroup (show n)+singleThreaded impls n = bgroup (show n <> "-bytes")     [ bench label $ nfIO $ f n | (Impl label f) ←  impls ] 
benchmark/Implementations.hs view
@@ -19,6 +19,8 @@  , random , entropy+, cryptoSystem+, cryptoChaCha , mwcUnfoldr , mwcUnfoldrIO @@ -26,11 +28,16 @@ , mwcMalloc8 , mwcMalloc32 , mwcMalloc64++--+, pcgMalloc64 ) where  import Control.Exception (bracketOnError) import Control.Monad.Primitive +import Crypto.Random (getRandomBytes, drgNew, getSystemDRG, withDRG)+ import Data.ByteString (ByteString, pack, unfoldrN) import Data.ByteString.Unsafe (unsafePackAddressLen) import Data.Word (Word8, Word32, Word64)@@ -44,7 +51,8 @@  import System.Entropy (getEntropy) import System.Random (randoms, getStdGen)-import System.Random.MWC (uniform, create, Gen)+import qualified System.Random.MWC as MWC (uniform, create, Gen)+import qualified System.Random.PCG as PCG (uniform, create, Gen)  import Benchmarks @@ -55,11 +63,14 @@ allImplementations =     [ Impl "random" random     , Impl "entropy" entropy+    , Impl "cryptonite-system" cryptoSystem+    , Impl "cryptonite-chacha" cryptoChaCha     , Impl "mwc-unfoldr" mwcUnfoldr     , Impl "mwc-unfoldr-io" mwcUnfoldrIO     , Impl "mwc-malloc-8" mwcMalloc8     , Impl "mwc-malloc-32" mwcMalloc32     , Impl "mwc-malloc-64" mwcMalloc64+    , Impl "pcg-malloc-64" pcgMalloc64     ]  -- -------------------------------------------------------------------------- --@@ -75,11 +86,24 @@ entropy n = getEntropy (fromIntegral n)  -- -------------------------------------------------------------------------- --+-- cryptonite getRandomBytes++cryptoSystem ∷ Natural → IO ByteString+cryptoSystem n = do+    gen ← getSystemDRG+    return $ fst (withDRG gen $ getRandomBytes (fromIntegral n))++cryptoChaCha ∷ Natural → IO ByteString+cryptoChaCha n = do+    gen ← drgNew+    return $ fst (withDRG gen $ getRandomBytes (fromIntegral n))++-- -------------------------------------------------------------------------- -- -- Malloc8  mwcMalloc8 ∷ Natural → IO ByteString mwcMalloc8 n = do-    !gen ← create+    !gen ← MWC.create     bracketOnError (mallocBytes len) free $ \ptr@(Ptr !addr) → do         go gen ptr         unsafePackAddressLen len addr@@ -93,7 +117,7 @@         loop !i             | i == pred len = return ()             | otherwise = do-                !b ← uniform gen ∷ IO Word8+                !b ← MWC.uniform gen ∷ IO Word8                 poke (ptr `plusPtr` i) b                 loop (succ i) @@ -102,7 +126,7 @@  mwcMalloc32 ∷ Natural → IO ByteString mwcMalloc32 n = do-    !gen ← create+    !gen ← MWC.create     bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do         go gen ptr         unsafePackAddressLen len8 addr@@ -116,7 +140,7 @@         !fin32Ptr = startPtr `plusPtr` (len32 * 4)         loop32 !curPtr             | curPtr < fin32Ptr = do-                !b ← uniform gen ∷ IO Word32+                !b ← MWC.uniform gen ∷ IO Word32                 poke curPtr b                 loop32 $ curPtr `plusPtr` 4             | otherwise = loop8 $ castPtr curPtr@@ -124,7 +148,7 @@         !fin8Ptr = startPtr `plusPtr` len8         loop8 !curPtr             | curPtr < fin8Ptr = do-                !b ← uniform gen ∷ IO Word8+                !b ← MWC.uniform gen ∷ IO Word8                 poke curPtr b                 loop8 $ curPtr `plusPtr` 1             | otherwise = return ()@@ -135,7 +159,7 @@  mwcMalloc64 ∷ Natural → IO ByteString mwcMalloc64 n = do-    !gen ← create+    !gen ← MWC.create     bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do         go gen ptr         unsafePackAddressLen len8 addr@@ -144,7 +168,7 @@     !len8 = fromIntegral n     !len64 = len8 `div` 8 -    go ∷ Gen (PrimState IO) → Ptr Word64 → IO ()+    go ∷ MWC.Gen (PrimState IO) → Ptr Word64 → IO ()     go !gen !startPtr = loop64 startPtr       where         fin64Ptr ∷ Ptr Word64@@ -153,7 +177,7 @@         loop64 ∷ Ptr Word64 → IO ()         loop64 !curPtr             | curPtr < fin64Ptr = do-                !b ← uniform gen ∷ IO Word64+                !b ← MWC.uniform gen ∷ IO Word64                 poke curPtr b                 loop64 $ curPtr `plusPtr` 8             | otherwise = loop8 $ castPtr curPtr@@ -164,7 +188,7 @@         loop8 ∷ Ptr Word8 → IO ()         loop8 !curPtr             | curPtr < fin8Ptr = do-                !b ← uniform gen ∷ IO Word8+                !b ← MWC.uniform gen ∷ IO Word8                 poke curPtr b                 loop8 $ curPtr `plusPtr` 1             | otherwise = return ()@@ -176,11 +200,11 @@  mwcUnfoldr ∷ ∀ m . (PrimMonad m, PrimBase m) ⇒ Natural → m ByteString mwcUnfoldr n = do-    !gen ← create+    !gen ← MWC.create     primitive $ go gen   where     go-        ∷ Gen (PrimState m)+        ∷ MWC.Gen (PrimState m)         → State# (PrimState m)         → (# State# (PrimState m), ByteString #)     go !gen !st =@@ -188,11 +212,11 @@         in (# st', b #)      step-        ∷ Gen (PrimState m)+        ∷ MWC.Gen (PrimState m)         → Box m         → Maybe (Word8, Box m)     step !gen (Box !s) =-        let !(# !s', !b #) = internal (uniform gen ∷ m Word8) s+        let !(# !s', !b #) = internal (MWC.uniform gen ∷ m Word8) s         in Just (b, Box s')  -- -------------------------------------------------------------------------- --@@ -200,11 +224,11 @@  mwcUnfoldrIO ∷ Natural → IO ByteString mwcUnfoldrIO n = do-    !gen ← create+    !gen ← MWC.create     primitive $ go gen   where     go-        ∷ Gen (PrimState IO)+        ∷ MWC.Gen (PrimState IO)         → State# (PrimState IO)         → (# State# (PrimState IO), ByteString #)     go !gen !st =@@ -212,10 +236,49 @@         in (# st', b #)      step-        ∷ Gen (PrimState IO)+        ∷ MWC.Gen (PrimState IO)         → Box IO         → Maybe (Word8, Box IO)     step !gen (Box !s) =-        let !(# !s', !b #) = internal (uniform gen ∷ IO Word8) s+        let !(# !s', !b #) = internal (MWC.uniform gen ∷ IO Word8) s         in Just (b, Box s')++-- -------------------------------------------------------------------------- --+-- PCG Malloc64++pcgMalloc64 ∷ Natural → IO ByteString+pcgMalloc64 n = do+    !gen ← PCG.create+    bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do+        go gen ptr+        unsafePackAddressLen len8 addr+  where+    len8, len64 ∷ Int+    !len8 = fromIntegral n+    !len64 = len8 `div` 8++    go ∷ PCG.Gen (PrimState IO) → Ptr Word64 → IO ()+    go !gen !startPtr = loop64 startPtr+      where+        fin64Ptr ∷ Ptr Word64+        !fin64Ptr = startPtr `plusPtr` (len64 * 8)++        loop64 ∷ Ptr Word64 → IO ()+        loop64 !curPtr+            | curPtr < fin64Ptr = do+                !b ← PCG.uniform gen ∷ IO Word64+                poke curPtr b+                loop64 $ curPtr `plusPtr` 8+            | otherwise = loop8 $ castPtr curPtr++        fin8Ptr ∷ Ptr Word8+        !fin8Ptr = startPtr `plusPtr` len8++        loop8 ∷ Ptr Word8 → IO ()+        loop8 !curPtr+            | curPtr < fin8Ptr = do+                !b ← PCG.uniform gen ∷ IO Word8+                poke curPtr b+                loop8 $ curPtr `plusPtr` 1+            | otherwise = return () 
benchmark/benchmark-compare.hs view
@@ -19,5 +19,5 @@ import Implementations
 
 main ∷ IO ()
-main = defaultMain $ benchmarks allImplementations
+main = defaultMain $ compareBenchmarks allImplementations
 
benchmark/benchmark.hs view
@@ -16,8 +16,12 @@ 
 import Benchmarks
 import Criterion.Main
-import Data.ByteString.Random
+import qualified Data.ByteString.Random.MWC as MWC
+import qualified Data.ByteString.Random.PCG as PCG
 
 main ∷ IO ()
-main = defaultMain $ largeBenchmarks [Impl "Data.ByteString.Random" random]
+main = defaultMain $ compareBenchmarks
+    [ Impl "random-bytestring+mwc" MWC.random
+    , Impl "random-bytestring+pcg" PCG.random
+    ]
 
+ benchmarks-details.png view

binary file changed (absent → 33474 bytes)

+ benchmarks.png view

binary file changed (absent → 47057 bytes)

random-bytestring.cabal view
@@ -1,15 +1,26 @@ Name: random-bytestring-Version: 0.1.1+Version: 0.1.2 Synopsis: Efficient generation of random bytestrings Description:     Efficient generation of random bytestrings. The implementation     populates uninitialized memory with uniformily distributed random-    64 bit words. The words are generated using the PRNG from the-    mwc-random package.-+    64 bit words (and 8 bit words for remaining bytes at the end of+    the bytestring).+    .+    Random words are generated using the PRNG from+    the [mwc-random](https://hackage.haskell.org/package/mwc-random) package+    or the [pcg-random](https://hackage.haskell.org/package/pcg-random)+    package. It is also possible to use a custom PRNG by providing+    an instance for the 'RandomWords' type class and using the function+    'generate' from the module "Data.ByteString.Random.Internal".+    .     The generated byte strings are suitable for statistical     applications. They are /not/ suitable for cryptographic     applications.+    .+    ![benchmarks](https://hackage.haskell.org/package/random-bytestring-0.1.2/src/benchmarks.png)+    .+    ![detailed benchmarks](https://hackage.haskell.org/package/random-bytestring-0.1.2/src/benchmarks-details.png)  Homepage: https://www.github.com/larskuhtz/random-bytestring License: MIT@@ -25,6 +36,8 @@ extra-doc-files:     README.md,     CHANGELOG.md+    benchmarks.png+    benchmarks-details.png  source-repository head     type: git@@ -36,17 +49,21 @@      exposed-modules:         Data.ByteString.Random+        Data.ByteString.Random.Internal+        Data.ByteString.Random.PCG+        Data.ByteString.Random.MWC      build-depends:         base >= 4.7 && < 4.11,         bytestring >= 0.10,-        mwc-random >= 0.13+        mwc-random >= 0.13,+        pcg-random >= 0.1      if impl(ghc < 7.10)         build-depends:             nats >= 1.1 -    ghc-options: -Wall+    ghc-options: -Wall -O2  Benchmark benchmark     default-language: Haskell2010@@ -59,7 +76,14 @@         async >= 2.1,         base >= 4.7 && < 4.11,         bytestring >= 0.10,-        criterion >= 1.1+        criterion >= 1.1,+        entropy >= 0.3,+        ghc-prim >= 0.3,+        mwc-random >= 0.13,+        pcg-random >= 0.1,+        primitive >= 0.6,+        random >= 1.1,+        cryptonite >= 0.24      if impl(ghc < 7.10)         build-depends:@@ -70,6 +94,7 @@      other-modules:         Benchmarks+        Implementations  Benchmark benchmark-compare     default-language: Haskell2010@@ -86,8 +111,10 @@         entropy >= 0.3,         ghc-prim >= 0.3,         mwc-random >= 0.13,+        pcg-random >= 0.1,         primitive >= 0.6,-        random >= 1.1+        random >= 1.1,+        cryptonite >= 0.24      if impl(ghc < 7.10)         build-depends:@@ -115,8 +142,10 @@         entropy >= 0.3,         ghc-prim >= 0.3,         mwc-random >= 0.13,+        pcg-random >= 0.1,         primitive >= 0.6,-        random >= 1.1+        random >= 1.1,+        cryptonite >= 0.24      if impl(ghc < 7.10)         build-depends:
src/Data/ByteString/Random.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE UnicodeSyntax #-}-{-# LANGUAGE BangPatterns #-}  -- -------------------------------------------------------------------------- -- -- |@@ -8,88 +7,10 @@ -- License: MIT -- Maintainer: lakuhtz@gmail.com -- Stability: experimental--- -------------------------------------------------------------------------- --  module Data.ByteString.Random-( random-, randomGen+( module Data.ByteString.Random.MWC ) where -import Control.Exception (bracketOnError)--import Data.ByteString (ByteString)-import Data.ByteString.Unsafe (unsafePackAddressLen)-import Data.Word (Word8, Word64)--import Foreign (mallocBytes, poke, plusPtr, free, castPtr)--import GHC.Ptr (Ptr(..))--import Numeric.Natural (Natural)--import System.Random.MWC (uniform, GenIO, withSystemRandom)---- $setup--- >>> import qualified Data.ByteString as B--- >>> import Test.QuickCheck---- | Generate a random bytestring of length n. The PRNG is seeded--- from the system randomness source.------ prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n--- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n----random-    ∷ Natural-        -- ^ Length of the result bytestring in bytes-    → IO ByteString-random n = withSystemRandom $ \gen → randomGen gen n---- | Generate a random bytestring of length n using the given--- PRNG.----randomGen-    ∷ GenIO-        -- ^ A PRNG that is used to generate random bytes.-    → Natural-        -- ^ Length of the result bytestring in bytes-    → IO ByteString-randomGen gen n =-    bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do-        {-# SCC "go" #-} go ptr-        {-# SCC "pack" #-} unsafePackAddressLen len8 addr-  where-    len8, len64 ∷ Int-    !len8 = fromIntegral n-    !len64 = len8 `div` 8--    go ∷ Ptr Word64 → IO ()-    go !startPtr = loop64 startPtr-      where-        -- Would it help to add more manual unrolling levels?-        -- How smart is the compiler about unrolling loops?--        -- Generate 64bit values-        fin64Ptr ∷ Ptr Word64-        !fin64Ptr = startPtr `plusPtr` (len64 * 8)--        loop64 ∷ Ptr Word64 → IO ()-        loop64 !curPtr-            | curPtr < fin64Ptr = {-# SCC "loop64" #-} do-                !b ← uniform gen ∷ IO Word64-                {-# SCC "poke64" #-} poke curPtr b-                loop64 $ {-# SCC "ptr_inc" #-} curPtr `plusPtr` 8-            | otherwise = loop8 $ castPtr curPtr--        -- Generate 8bit values-        fin8Ptr ∷ Ptr Word8-        !fin8Ptr = startPtr `plusPtr` len8--        loop8 ∷ Ptr Word8 → IO ()-        loop8 !curPtr-            | curPtr < fin8Ptr = {-# SCC "loop8" #-} do-                !b ← uniform gen ∷ IO Word8-                poke curPtr b-                loop8 $ curPtr `plusPtr` 1-            | otherwise = return ()+import Data.ByteString.Random.MWC 
+ src/Data/ByteString/Random/Internal.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE UnicodeSyntax #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.Internal+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.Internal+( generate+, RandomWords(..)+) where++import Control.Exception (bracketOnError)++import Data.ByteString (ByteString)+import Data.ByteString.Unsafe (unsafePackAddressLen)+import Data.Word (Word8, Word64)++import Foreign (mallocBytes, poke, plusPtr, free, castPtr)++import GHC.Ptr (Ptr(..))++import Numeric.Natural (Natural)++-- -------------------------------------------------------------------------- --++class RandomWords g where+    uniformW8 ∷ g → IO Word8+        -- ^ function that generates uniformily distributed random 8 bit words+    uniformW64 ∷ g → IO Word64+        -- ^ function that generates uniformily distributed random 64 bit words++-- The reason why a type class is used instead of passing the IO functions+-- directoy to generate, is that we can force GHC to inline these methods by+-- using a SPECIALIZE pragma.++-- -------------------------------------------------------------------------- --++-- | Generates uniformily distributed random bytestrings of length n using the+-- given PRNG.+--+generate+    ∷ RandomWords g+    ⇒ g+        -- ^ PRNG+    → Natural+        -- ^ Length of the result bytestring in bytes+    → IO ByteString+generate g n =+    bracketOnError (mallocBytes len8) free $ \ptr@(Ptr !addr) → do+        {-# SCC "go" #-} go ptr+        {-# SCC "pack" #-} unsafePackAddressLen len8 addr+  where+    len8, len64 ∷ Int+    !len8 = fromIntegral n+    !len64 = len8 `div` 8++    go ∷ Ptr Word64 → IO ()+    go !startPtr = loop64 startPtr+      where+        -- Would it help to add more manual unrolling levels?+        -- How smart is the compiler about unrolling loops?++        -- Generate 64bit values+        fin64Ptr ∷ Ptr Word64+        !fin64Ptr = startPtr `plusPtr` (len64 * 8)++        loop64 ∷ Ptr Word64 → IO ()+        loop64 !curPtr+            | curPtr < fin64Ptr = {-# SCC "loop64" #-} do+                !b ← uniformW64 g+                {-# SCC "poke64" #-} poke curPtr b+                loop64 $ {-# SCC "ptr_inc" #-} curPtr `plusPtr` 8+            | otherwise = loop8 $ castPtr curPtr++        -- Generate 8bit values+        fin8Ptr ∷ Ptr Word8+        !fin8Ptr = startPtr `plusPtr` len8++        loop8 ∷ Ptr Word8 → IO ()+        loop8 !curPtr+            | curPtr < fin8Ptr = {-# SCC "loop8" #-} do+                !b ← uniformW8 g+                poke curPtr b+                loop8 $ curPtr `plusPtr` 1+            | otherwise = return ()+{-# INLINEABLE generate #-}+
+ src/Data/ByteString/Random/MWC.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnicodeSyntax #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.MWC+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.MWC+( random+, randomGen+) where++import Data.ByteString (ByteString)++import Numeric.Natural (Natural)++import System.Random.MWC (uniform, GenIO, withSystemRandom)++-- internal imports++import Data.ByteString.Random.Internal++-- -------------------------------------------------------------------------- --++instance (g ~ GenIO) ⇒ RandomWords g where+    uniformW8 = uniform+    {-# INLINE uniformW8 #-}+    uniformW64 = uniform+    {-# INLINE uniformW64 #-}++{-# SPECIALIZE generate ∷ GenIO → Natural → IO ByteString #-}++-- -------------------------------------------------------------------------- --++randomGen+    ∷ GenIO+        -- ^ PRNG+    → Natural+        -- ^ Length of the result bytestring in bytes+    → IO ByteString+randomGen = generate+{-# INLINE randomGen #-}++-- $setup+-- >>> import qualified Data.ByteString as B+-- >>> import Test.QuickCheck++-- | Generate a random bytestring of length n. The PRNG is seeded+-- from the system randomness source.+--+-- prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n+-- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n+--+random+    ∷ Natural+        -- ^ Length of the result bytestring in bytes+    → IO ByteString+random = withSystemRandom . flip randomGen+
+ src/Data/ByteString/Random/PCG.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnicodeSyntax #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- -------------------------------------------------------------------------- --+-- |+-- Module: Data.ByteString.Random.PCG+-- Copyright: (c) Lars Kuhtz <lakuhtz@gmail.com> 2017+-- License: MIT+-- Maintainer: lakuhtz@gmail.com+-- Stability: experimental++module Data.ByteString.Random.PCG+( random+, randomGen+) where++import Data.ByteString (ByteString)++import Numeric.Natural (Natural)++import System.Random.PCG (uniform, GenIO, withSystemRandom)++-- internal imports++import Data.ByteString.Random.Internal++-- -------------------------------------------------------------------------- --++instance (g ~ GenIO) ⇒ RandomWords g where+    uniformW8 = uniform+    {-# INLINE uniformW8 #-}+    uniformW64 = uniform+    {-# INLINE uniformW64 #-}++{-# SPECIALIZE generate ∷ GenIO → Natural → IO ByteString #-}++-- -------------------------------------------------------------------------- --++randomGen+    ∷ GenIO+        -- ^ PRNG+    → Natural+        -- ^ Length of the result bytestring in bytes+    → IO ByteString+randomGen = generate+{-# INLINE randomGen #-}++-- $setup+-- >>> import qualified Data.ByteString as B+-- >>> import Test.QuickCheck++-- | Generate a random bytestring of length n. The PRNG is seeded+-- from the system randomness source.+--+-- prop> ioProperty $ ((fromIntegral n ===) . B.length) <$> random n+-- prop> n > 4 ==> ioProperty $ (/=) <$> random n <*> random n+--+random+    ∷ Natural+        -- ^ Length of the result bytestring in bytes+    → IO ByteString+random n = withSystemRandom $ flip randomGen n+