packages feed

perfect-vector-shuffle 0.1.1.1 → 0.1.1.2

raw patch · 5 files changed

+224/−63 lines, 5 filesdep ~MonadRandomdep ~basedep ~primitivePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: MonadRandom, base, primitive, vector

API changes (from Hackage documentation)

- Immutable.Shuffle: derangement :: forall a g. (Eq a, RandomGen g) => Vector a -> g -> (Vector a, g)
+ Immutable.Shuffle: derangement :: (Eq a, RandomGen g, Vector v a) => v a -> g -> (v a, g)
- Immutable.Shuffle: derangementM :: forall m a. (Eq a, MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)
+ Immutable.Shuffle: derangementM :: forall m a v. (Eq a, MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a)
- Immutable.Shuffle: maximalCycle :: forall a g. RandomGen g => Vector a -> g -> (Vector a, g)
+ Immutable.Shuffle: maximalCycle :: forall a g v. (RandomGen g, Vector v a) => v a -> g -> (v a, g)
- Immutable.Shuffle: maximalCycleM :: forall m a. (MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)
+ Immutable.Shuffle: maximalCycleM :: forall m a v. (MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a)
- Immutable.Shuffle: sampleWithoutReplacement :: forall m a. (MonadRandom m, PrimMonad m) => Int -> Vector a -> m (Vector a)
+ Immutable.Shuffle: sampleWithoutReplacement :: forall m a v. (MonadRandom m, PrimMonad m, Vector v a) => Int -> v a -> m (v a)
- Immutable.Shuffle: shuffle :: forall a g. RandomGen g => Vector a -> g -> (Vector a, g)
+ Immutable.Shuffle: shuffle :: forall a g v. (RandomGen g, Vector v a) => v a -> g -> (v a, g)
- Immutable.Shuffle: shuffleK :: forall m a. (MonadRandom m, PrimMonad m) => Int -> Vector a -> m (Vector a)
+ Immutable.Shuffle: shuffleK :: forall m a v. (MonadRandom m, PrimMonad m, Vector v a) => Int -> v a -> m (v a)
- Immutable.Shuffle: shuffleM :: forall m a. (MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)
+ Immutable.Shuffle: shuffleM :: forall m a v. (MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a)
- Mutable.Shuffle: derangement :: forall m a g. (PrimMonad m, RandomGen g, Eq a) => MVector (PrimState m) a -> g -> m g
+ Mutable.Shuffle: derangement :: forall m a g v. (PrimMonad m, RandomGen g, Eq a, MVector v a) => v (PrimState m) a -> g -> m g
- Mutable.Shuffle: derangementM :: forall m a. (PrimMonad m, MonadRandom m, Eq a) => MVector (PrimState m) a -> m ()
+ Mutable.Shuffle: derangementM :: (PrimMonad m, MonadRandom m, Eq a, MVector v a) => v (PrimState m) a -> m ()
- Mutable.Shuffle: maximalCycle :: forall m a g. (PrimMonad m, RandomGen g) => MVector (PrimState m) a -> g -> m g
+ Mutable.Shuffle: maximalCycle :: forall m a g v. (PrimMonad m, RandomGen g, MVector v a) => v (PrimState m) a -> g -> m g
- Mutable.Shuffle: maximalCycleM :: forall m a. (PrimMonad m, MonadRandom m) => MVector (PrimState m) a -> m ()
+ Mutable.Shuffle: maximalCycleM :: forall m a v. (PrimMonad m, MonadRandom m, MVector v a) => v (PrimState m) a -> m ()
- Mutable.Shuffle: shuffle :: forall m a g. (PrimMonad m, RandomGen g) => MVector (PrimState m) a -> g -> m g
+ Mutable.Shuffle: shuffle :: forall m a g v. (PrimMonad m, RandomGen g, MVector v a) => v (PrimState m) a -> g -> m g
- Mutable.Shuffle: shuffleK :: forall m a. (PrimMonad m, MonadRandom m) => Int -> MVector (PrimState m) a -> m ()
+ Mutable.Shuffle: shuffleK :: forall m a v. (PrimMonad m, MonadRandom m, MVector v a) => Int -> v (PrimState m) a -> m ()
- Mutable.Shuffle: shuffleM :: forall m a. (PrimMonad m, MonadRandom m) => MVector (PrimState m) a -> m ()
+ Mutable.Shuffle: shuffleM :: forall m a v. (PrimMonad m, MonadRandom m, MVector v a) => v (PrimState m) a -> m ()

Files

perfect-vector-shuffle.cabal view
@@ -4,7 +4,7 @@  name:               perfect-vector-shuffle synopsis:           Library for performing vector shuffles-version:            0.1.1.1+version:            0.1.1.2  author:             Callan McGill maintainer:         callan.mcgill@gmail.com@@ -61,7 +61,7 @@       [4,8,5,2,7,3,9,6,10,1]     @ -extra-source-files: CHANGELOG.md+extra-doc-files: CHANGELOG.md   source-repository head@@ -77,11 +77,11 @@                     -fexpose-all-unfoldings                     -fspecialise-aggressively -  build-depends:    base        >= 4.9      && < 4.14-                  , MonadRandom >= 0.5.1.1  && < 0.6-                  , primitive   ^>= 0.7-                  , random      ^>= 1.1-                  , vector      >= 0.12.0   && < 0.13+  build-depends:    base        >= 4.9      && < 4.20+                  , MonadRandom >= 0.5.1.1  && < 0.7+                  , primitive   >= 0.6.4    && < 0.9+                  , random      >= 1.1      && < 1.3+                  , vector      >= 0.12.0   && < 0.14    exposed-modules:  Immutable.Shuffle                     Mutable.Shuffle@@ -104,17 +104,18 @@                     -fspecialise-aggressively                     -Wincomplete-patterns -  build-depends:    perfect-vector-shuffle ^>= 0.1.1.1-                  , base        >= 4.9      && < 4.14-                  , QuickCheck             ^>= 2.13.2-                  , random                 ^>= 1.1-                  , tasty                  >= 1.2    && < 1.3-                  , tasty-quickcheck       ^>= 0.10-                  , vector                 >= 0.12.0 && < 0.13-                  , quickcheck-instances   >= 0.3.19 && < 0.4+  build-depends:    perfect-vector-shuffle+                  , base                   +                  , QuickCheck             +                  , random                 +                  , tasty                  +                  , tasty-quickcheck       +                  , vector                 +                  , quickcheck-instances        other-modules:    Immutable.Test+                    Mutable.Test    default-language: Haskell2010 @@ -130,15 +131,15 @@                     -fspecialize-aggressively                     -Wincomplete-patterns -  build-depends:    perfect-vector-shuffle ^>= 0.1.1.1-                  , base        >= 4.9      && < 4.14-                  , MonadRandom >= 0.5.1.1  && < 0.6-                  , primitive   ^>= 0.7-                  , random      ^>= 1.1-                  , vector      >= 0.12.0   && < 0.13+  build-depends:    perfect-vector-shuffle+                  , base+                  , MonadRandom+                  , primitive+                  , random+                  , vector    other-modules: Immutable.Shuffle                  Mutable.Shuffle-  +    default-language: Haskell2010
src/Immutable/Shuffle.hs view
@@ -9,7 +9,7 @@ import           Control.Monad.Primitive import           Control.Monad.Random    (MonadRandom (..)) import           Control.Monad.ST        (runST)-import           Data.Vector+import           Data.Vector.Generic import qualified Mutable.Shuffle         as MS import           Prelude                 hiding (length, take) import           System.Random           (RandomGen (..))@@ -19,7 +19,7 @@ -- Perform a shuffle on an immutable vector with a given random generator returning a shuffled vector and a new generator. -- -- This uses the Fisher--Yates--Knuth algorithm.-shuffle :: forall a g. RandomGen g => Vector a -> g -> (Vector a, g)+shuffle :: forall a g v. (RandomGen g, Vector v a) => v a -> g -> (v a, g) shuffle v g   | length v <= 1 = (v, g)   | otherwise     =@@ -35,7 +35,7 @@ -- Perform a shuffle on an input immutable vector in a monad which has a source of randomness. -- -- This uses the Fisher--Yates--Knuth algorithm.-shuffleM :: forall m a . (MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)+shuffleM :: forall m a v . (MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a) shuffleM v   | length v <= 1 = pure v   | otherwise =@@ -49,7 +49,7 @@ -- Perform a shuffle on the first k elements of a vector in a monad which has a -- source of randomness. ---shuffleK :: forall m a . (MonadRandom m, PrimMonad m) => Int -> Vector a -> m (Vector a)+shuffleK :: forall m a v. (MonadRandom m, PrimMonad m, Vector v a) => Int -> v a -> m (v a) shuffleK k v   | length v <= 1 = pure v   | otherwise =@@ -61,7 +61,7 @@  -- | -- Get a random sample of k elements without replacement from a vector.-sampleWithoutReplacement :: forall m a . (MonadRandom m, PrimMonad m) => Int -> Vector a -> m (Vector a)+sampleWithoutReplacement :: forall m a v . (MonadRandom m, PrimMonad m, Vector v a) => Int -> v a -> m (v a) {-# INLINEABLE sampleWithoutReplacement #-} sampleWithoutReplacement k v = take k <$> shuffleK k v @@ -71,7 +71,7 @@ -- indices form a maximal cycle. -- -- This uses the Sattolo algorithm.-maximalCycle :: forall a g. RandomGen g => Vector a -> g -> (Vector a, g)+maximalCycle :: forall a g v. (RandomGen g, Vector v a) => v a -> g -> (v a, g) maximalCycle v g   | length v <= 1 = (v, g)   | otherwise     =@@ -87,7 +87,7 @@ -- indices form a maximal cycle in a monad with a source of randomness. -- -- This uses the Sattolo algorithm.-maximalCycleM :: forall m a . (MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)+maximalCycleM :: forall m a v . (MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a) maximalCycleM v   | length v <= 1 = pure v   | otherwise =@@ -105,7 +105,7 @@ -- __Note:__ It is assumed the input vector consists of distinct values. -- -- This uses the "early refusal" algorithm.-derangement :: forall a g. (Eq a, RandomGen g) => Vector a -> g -> (Vector a, g)+derangement :: forall a g v . (Eq a, RandomGen g, Vector v a) => v a -> g -> (v a, g) derangement v g   | length v <= 1 = (v, g)   | otherwise     =@@ -124,7 +124,7 @@ -- __Note:__ It is assumed the input vector consists of distinct values. -- -- This uses the "early refusal" algorithm.-derangementM :: forall m a . (Eq a, MonadRandom m, PrimMonad m) => Vector a -> m (Vector a)+derangementM :: forall m a v . (Eq a, MonadRandom m, PrimMonad m, Vector v a) => v a -> m (v a) derangementM v   | length v <= 1 = pure v   | otherwise =
src/Mutable/Shuffle.hs view
@@ -8,11 +8,11 @@ module Mutable.Shuffle where  import           Control.Monad.Primitive-import           Control.Monad.Random    (MonadRandom (..))-import           Data.Vector.Mutable-import           Prelude                 hiding (length, read, tail)-import           System.Random           (RandomGen)-import qualified System.Random           as SR+import           Control.Monad.Random        (MonadRandom (..))+import           Data.Vector.Generic.Mutable+import           Prelude                     hiding (length, read, tail)+import           System.Random               (RandomGen)+import qualified System.Random               as SR   -- |@@ -20,17 +20,19 @@ -- -- This uses the Fisher--Yates--Knuth algorithm shuffle-  :: forall m a g+  :: forall m a g v   . ( PrimMonad m     , RandomGen g+    , MVector v a     )-  => MVector (PrimState m) a -> g -> m g+  => v (PrimState m) a -> g -> m g {-# INLINABLE shuffle #-} shuffle mutV gen = go mutV gen (length mutV - 1)   where-    go :: MVector (PrimState m) a -> g -> Int -> m g+    go :: v (PrimState m) a -> g -> Int -> m g     {-# INLINE go #-}-    go _ g 0   =  pure g+    go _ g (- 1)  = pure g+    go _ g 0      =  pure g     go v g maxInd =       do         let (ind, newGen) :: (Int, g) = SR.randomR (0, maxInd) g@@ -44,38 +46,41 @@ -- -- This uses the Fisher--Yates--Knuth algorithm shuffleM-  :: forall m a+  :: forall m a v   . ( PrimMonad m     , MonadRandom m+    , MVector v a     )-  => MVector (PrimState m) a  -> m ()+  => v (PrimState m) a  -> m () {-# INLINABLE shuffleM #-} shuffleM mutV = go mutV (length mutV - 1)   where-    go :: MVector (PrimState m) a -> Int -> m ()+    go :: v (PrimState m) a -> Int -> m ()     {-# INLINE go #-}-    go _ 0   =  pure ()+    go _ (- 1)  = pure ()+    go _ 0      = pure ()     go v maxInd =       do         ind <-  getRandomR (0, maxInd)         swap v 0 ind         go (tail v) (maxInd - 1) -{-# SPECIALISE shuffleM :: MVector RealWorld a -> IO () #-}+-- {-# SPECIALISE shuffleM :: MVector RealWorld a -> IO () #-}  -- | -- Shuffle the first k elements of a vector. -- shuffleK-  :: forall m a+  :: forall m a v   . ( PrimMonad m     , MonadRandom m+    , MVector v a     )-  => Int -> MVector (PrimState m) a  -> m ()+  => Int -> v (PrimState m) a  -> m () {-# INLINABLE shuffleK #-} shuffleK numberOfShuffles mutV = go mutV (numberOfShuffles - 1)   where-    go :: MVector (PrimState m) a -> Int -> m ()+    go :: v (PrimState m) a -> Int -> m ()     {-# INLINE go #-}     go _ k | k < 0       = error "Cannot pass negative value to ShuffleK"@@ -95,16 +100,18 @@ -- -- This uses the Sattolo algorithm. maximalCycle-  :: forall m a g+  :: forall m a g v   . ( PrimMonad m     , RandomGen g+    , MVector v a     )-  => MVector (PrimState m) a -> g -> m g+  => v (PrimState m) a -> g -> m g {-# INLINABLE maximalCycle #-} maximalCycle mutV gen = go mutV gen (length mutV - 1)   where-    go :: MVector (PrimState m) a -> g -> Int -> m g+    go :: v (PrimState m) a -> g -> Int -> m g     {-# INLINE go #-}+    go _ g (- 1)  =  pure g     go _ g 0      =  pure g     go v g maxInd =       do@@ -119,24 +126,26 @@ -- -- This uses the Sattolo algorithm. maximalCycleM-  :: forall m a+  :: forall m a v   . ( PrimMonad m     , MonadRandom m+    , MVector v a     )-  => MVector (PrimState m) a  -> m ()+  => v (PrimState m) a  -> m () {-# INLINABLE maximalCycleM #-} maximalCycleM mutV = go mutV (length mutV - 1)   where-    go :: MVector (PrimState m) a -> Int -> m ()+    go :: v (PrimState m) a -> Int -> m ()     {-# INLINE go #-}-    go _ 0   =  pure ()+    go _ (- 1)  =  pure ()+    go _ 0      =  pure ()     go v maxInd =       do         ind <-  getRandomR (1, maxInd)         swap v 0 ind         go (tail v) (maxInd - 1) -{-# SPECIALISE maximalCycleM :: MVector RealWorld a -> IO () #-}+-- {-# SPECIALISE maximalCycleM :: MVector RealWorld a -> IO () #-}   @@ -147,19 +156,21 @@ -- -- This uses the "early refusal" algorithm. derangement-  :: forall m a g+  :: forall m a g v   . ( PrimMonad m     , RandomGen g     , Eq a+    , MVector v a     )-  => MVector (PrimState m) a -> g -> m g+  => v (PrimState m) a -> g -> m g {-# INLINABLE derangement #-} derangement mutV gen = do   mutV_copy <- clone mutV   go mutV_copy mutV gen 0 (length mutV - 1)   where-    go :: MVector (PrimState m) a -> MVector (PrimState m) a -> g -> Int -> Int -> m g+    go :: v (PrimState m) a -> v (PrimState m) a -> g -> Int -> Int -> m g     {-# INLINE go #-}+    go _ _ g _ (- 1)   = pure g     go c v g lastInd 0 =       do         v_last_old <- read c lastInd@@ -192,19 +203,21 @@ -- -- This uses the "early refusal" algorithm derangementM-  :: forall m a+  :: forall m a v   . ( PrimMonad m     , MonadRandom m     , Eq a+    , MVector v a     )-  => MVector (PrimState m) a -> m ()+  => v (PrimState m) a -> m () {-# INLINABLE derangementM #-} derangementM mutV = do   mutV_copy <- clone mutV   go mutV_copy mutV 0 (length mutV - 1)   where-    go :: MVector (PrimState m) a -> MVector (PrimState m) a -> Int -> Int -> m ()+    go :: v (PrimState m) a -> v (PrimState m) a -> Int -> Int -> m ()     {-# INLINE go #-}+    go _ _ _  (- 1)  = pure ()     go c v lastInd 0 =       do         v_last_old <- read c lastInd
+ test/Mutable/Test.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}+module Mutable.Test+  ( testSuite+  ) where++import           Data.List                        (sort)+import           Data.Vector                      (Vector, freeze, thaw, (!))+import qualified Data.Vector                      as V++import           Mutable.Shuffle+import           System.Random+import           Test.QuickCheck.Instances.Vector ()+import           Test.QuickCheck.Monadic+import           Test.Tasty+import           Test.Tasty.QuickCheck            as QC hiding (shuffle)+++testSuite :: TestTree+testSuite = testGroup ""+  [ localOption (QuickCheckTests 10000) shuffleTestSuite+  , localOption (QuickCheckTests 10000) maximalCycleTestSuite+  , localOption (QuickCheckTests 10000) derangementTestSuite+  ]++shuffleTestSuite :: TestTree+shuffleTestSuite = testGroup "shuffleM"+  [ QC.testProperty+      "shuffleM: Shuffling preserves length and elements"+       (monadicIO . isPermutationM @Int)+  , QC.testProperty+      "shuffle: Shuffling preserves length and elements"+       (monadicIO . isPermutation  @Int)+  ]+++maximalCycleTestSuite :: TestTree+maximalCycleTestSuite = testGroup "maximalCycleM"+  [ QC.testProperty+      "maximalCycleM: maximal cycle does indeed produce a maximal cycle on [0..n]"+      (monadicIO . isMaximalCycleM)+  , QC.testProperty+      "maximalCycle: maximal cycle does indeed produce a maximal cycle on [0..n]"+      (monadicIO . isMaximalCycle)+  ]+++derangementTestSuite :: TestTree+derangementTestSuite = testGroup "derangementM"+  [ QC.testProperty+      "derangementM: derangement does indeed produce a derangment on [0..n]."+      (monadicIO . isDerangementM)+  , QC.testProperty+      "derangement: derangement does indeed produce a derangment on [0..n]."+      (monadicIO . isDerangement)+  ]+++isPermutationM :: forall a . (Ord a , Show a, Arbitrary a) => Vector a -> PropertyM IO Property+isPermutationM v =+  do+    mv  <- run $ thaw v+    run $ shuffleM mv+    v'  <- run $ freeze mv+    let ls  = V.toList v+    let ls' = V.toList v'+    pure $ sort ls === sort ls'+++isPermutation+  :: forall a . (Ord a , Show a, Arbitrary a)+  => Vector a -> PropertyM IO Property+isPermutation v =+  do+    mv <- run $ thaw v+    g  <- run $ getStdGen+    _  <- run $ shuffle mv g+    v' <- run $ freeze mv+    let ls  = V.toList v+    let ls' = V.toList v'+    pure $ sort ls === sort ls'+++isMaximalCycleM :: Positive Int -> PropertyM IO Property+isMaximalCycleM (Positive n) =+  do+    let v = V.fromList [0..n]+    mv <- run $ thaw v+    run $ maximalCycleM mv+    v' <- run $ freeze mv+    pure $ cycleLength v' === (n + 1)++   where+     cycleLength :: Vector Int -> Int+     cycleLength v = go (V.head v) v++     go :: Int -> Vector Int -> Int+     go k xs = if k == 0+                       then 1+                     else+                       1 + go (xs ! k) xs++isMaximalCycle :: Positive Int -> PropertyM IO Property+isMaximalCycle (Positive n) =+  do+    let v = V.fromList [0..n]+    g  <- run getStdGen+    mv <- run $ thaw v+    _  <- run $ maximalCycle mv g+    v' <- run $ freeze mv+    pure $ cycleLength v' === (n + 1)++   where+     cycleLength :: Vector Int -> Int+     cycleLength v = go (V.head v) v++     go :: Int -> Vector Int -> Int+     go k xs = if k == 0+                       then 1+                     else+                       1 + go (xs ! k) xs+++isDerangementM :: Positive Int -> PropertyM IO Property+isDerangementM (Positive n) =+  do+    let v = V.fromList [0..n]+    mv <- run $ thaw v+    run $ derangementM mv+    v' <- run $ freeze mv+    let perm    = V.indexed v'+    let unmoved = V.filter (uncurry (==)) perm+    pure $ null unmoved === True+++isDerangement :: Positive Int -> PropertyM IO Property+isDerangement (Positive n) =+  do+    let v = V.fromList [0..n]+    g  <- run getStdGen+    mv <- run $ thaw v+    _  <- run $ derangement mv g+    v' <- run $ freeze mv+    let perm    = V.indexed v'+    let unmoved = V.filter (uncurry (==)) perm+    pure $ null unmoved === True
test/Test.hs view
@@ -1,6 +1,7 @@ module Main where  import qualified Immutable.Test as Immutable+import qualified Mutable.Test   as Mutable import           Test.Tasty  @@ -11,5 +12,5 @@ testSuite :: TestTree testSuite = testGroup "Perfect Vector Sort"   [ Immutable.testSuite+  , Mutable.testSuite   ]-