packages feed

permutation 0.1 → 0.2

raw patch · 18 files changed

+1775/−493 lines, 18 filesdep +ghc-primdep −containerssetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: ghc-prim

Dependencies removed: containers

API changes (from Hackage documentation)

- Data.Permutation: apply :: Permutation -> Int -> Int
- Data.Permutation: applyWith :: (Monad m) => (Int -> Int -> m ()) -> Permutation -> m ()
- Data.Permutation: data Permutation
- Data.Permutation: fromForeignPtr :: Int -> ForeignPtr Int -> Int -> Permutation
- Data.Permutation: fromList :: [Int] -> Permutation
- Data.Permutation: identity :: Int -> Permutation
- Data.Permutation: instance Eq Permutation
- Data.Permutation: instance Show Permutation
- Data.Permutation: inverse :: Permutation -> Permutation
- Data.Permutation: invertWith :: (Monad m) => (Int -> Int -> m ()) -> Permutation -> m ()
- Data.Permutation: permutation :: Int -> [Int] -> Permutation
- Data.Permutation: size :: Permutation -> Int
- Data.Permutation: toForeignPtr :: Permutation -> (Int, ForeignPtr Int, Int)
- Data.Permutation: toList :: Permutation -> [Int]
- Data.Permutation: unsafeApply :: Permutation -> Int -> Int
- Data.Permutation: unsafePermutation :: Int -> [Int] -> Permutation
- Data.Permutation: withPermutationPtr :: Permutation -> (Ptr Int -> IO a) -> IO a
+ Data.Permute: apply :: Permute -> Int -> Int
+ Data.Permute: data Permute
+ Data.Permute: elems :: Permute -> [Int]
+ Data.Permute: invSwaps :: Permute -> [(Int, Int)]
+ Data.Permute: inverse :: Permute -> Permute
+ Data.Permute: listPermute :: Int -> [Int] -> Permute
+ Data.Permute: next :: Permute -> Maybe Permute
+ Data.Permute: order :: (Ord a) => Int -> [a] -> Permute
+ Data.Permute: orderBy :: (a -> a -> Ordering) -> Int -> [a] -> Permute
+ Data.Permute: permute :: Int -> Permute
+ Data.Permute: prev :: Permute -> Maybe Permute
+ Data.Permute: rank :: (Ord a) => Int -> [a] -> Permute
+ Data.Permute: rankBy :: (a -> a -> Ordering) -> Int -> [a] -> Permute
+ Data.Permute: size :: Permute -> Int
+ Data.Permute: sort :: (Ord a) => Int -> [a] -> ([a], Permute)
+ Data.Permute: sortBy :: (a -> a -> Ordering) -> Int -> [a] -> ([a], Permute)
+ Data.Permute: swaps :: Permute -> [(Int, Int)]
+ Data.Permute: swapsPermute :: Int -> [(Int, Int)] -> Permute
+ Data.Permute: unsafeApply :: Permute -> Int -> Int
+ Data.Permute.IO: data IOPermute
+ Data.Permute.MPermute: class (Monad m) => MPermute p m | p -> m, m -> p
+ Data.Permute.MPermute: copyInverse :: (MPermute p m) => p -> p -> m ()
+ Data.Permute.MPermute: copyPermute :: (MPermute p m) => p -> p -> m ()
+ Data.Permute.MPermute: freeze :: (MPermute p m) => p -> m Permute
+ Data.Permute.MPermute: getElem :: (MPermute p m) => p -> Int -> m Int
+ Data.Permute.MPermute: getElems :: (MPermute p m) => p -> m [Int]
+ Data.Permute.MPermute: getInvSwaps :: (MPermute p m) => p -> m [(Int, Int)]
+ Data.Permute.MPermute: getInverse :: (MPermute p m) => p -> m p
+ Data.Permute.MPermute: getOrder :: (Ord a, MPermute p m) => Int -> [a] -> m p
+ Data.Permute.MPermute: getOrderBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m p
+ Data.Permute.MPermute: getRank :: (Ord a, MPermute p m) => Int -> [a] -> m p
+ Data.Permute.MPermute: getRankBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m p
+ Data.Permute.MPermute: getSize :: (MPermute p m) => p -> m Int
+ Data.Permute.MPermute: getSort :: (Ord a, MPermute p m) => Int -> [a] -> m ([a], p)
+ Data.Permute.MPermute: getSortBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m ([a], p)
+ Data.Permute.MPermute: getSwaps :: (MPermute p m) => p -> m [(Int, Int)]
+ Data.Permute.MPermute: instance MPermute (STPermute s) (ST s)
+ Data.Permute.MPermute: instance MPermute IOPermute IO
+ Data.Permute.MPermute: isValid :: (MPermute p m) => p -> m Bool
+ Data.Permute.MPermute: newCopyPermute :: (MPermute p m) => p -> m p
+ Data.Permute.MPermute: newListPermute :: (MPermute p m) => Int -> [Int] -> m p
+ Data.Permute.MPermute: newPermute :: (MPermute p m) => Int -> m p
+ Data.Permute.MPermute: newPermute_ :: (MPermute p m) => Int -> m p
+ Data.Permute.MPermute: newSwapsPermute :: (MPermute p m) => Int -> [(Int, Int)] -> m p
+ Data.Permute.MPermute: setElem :: (MPermute p m) => p -> Int -> Int -> m ()
+ Data.Permute.MPermute: setElems :: (MPermute p m) => p -> [Int] -> m ()
+ Data.Permute.MPermute: setIdentity :: (MPermute p m) => p -> m ()
+ Data.Permute.MPermute: setNext :: (MPermute p m) => p -> m Bool
+ Data.Permute.MPermute: setPrev :: (MPermute p m) => p -> m Bool
+ Data.Permute.MPermute: swapElems :: (MPermute p m) => p -> Int -> Int -> m ()
+ Data.Permute.MPermute: thaw :: (MPermute p m) => Permute -> m p
+ Data.Permute.MPermute: unsafeFreeze :: (MPermute p m) => p -> m Permute
+ Data.Permute.MPermute: unsafeGetElem :: (MPermute p m) => p -> Int -> m Int
+ Data.Permute.MPermute: unsafeNewListPermute :: (MPermute p m) => Int -> [Int] -> m p
+ Data.Permute.MPermute: unsafeNewSwapsPermute :: (MPermute p m) => Int -> [(Int, Int)] -> m p
+ Data.Permute.MPermute: unsafeSetElem :: (MPermute p m) => p -> Int -> Int -> m ()
+ Data.Permute.MPermute: unsafeSwapElems :: (MPermute p m) => p -> Int -> Int -> m ()
+ Data.Permute.MPermute: unsafeThaw :: (MPermute p m) => Permute -> m p
+ Data.Permute.ST: data STPermute s
+ Data.Permute.ST: runSTPermute :: (forall s. ST s (STPermute s)) -> Permute

Files

− Data/Permutation.hs
@@ -1,265 +0,0 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fglasgow-exts #-}--------------------------------------------------------------------------------- |--- Module     : Data.Permutation--- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>--- License    : BSD3--- Maintainer : Patrick Perry <patperry@stanford.edu>--- Stability  : experimental-----module Data.Permutation (-    -- * The Permutation type-    Permutation,--    -- * Creating permutations-    permutation,-    identity,-    inverse,-    -    -- * Permutation properties-    size,-    apply,--    -- * Applying permutations-    applyWith,-    invertWith,--    -- * Converstion to/from other types-    -- ** @ForeignPtr@s-    fromForeignPtr,-    toForeignPtr,-    -    -- ** Lists-    toList,-    fromList,--    -- * Unsafe operations-    withPermutationPtr,-    unsafePermutation,-    unsafeApply,-    -    ) where-        -import Control.Monad         ( foldM, liftM )-import Data.IntSet           ( IntSet )-import qualified Data.IntSet as IntSet-import Foreign               ( Ptr, ForeignPtr, mallocForeignPtrArray, -                               withForeignPtr, pokeArray, peekArray, -                               advancePtr, peek, peekElemOff, pokeElemOff ) -import System.IO.Unsafe      ( unsafePerformIO )--#if defined(__GLASGOW_HASKELL__)-import GHC.Base                 ( realWorld# )-import GHC.IOBase               ( IO(IO) )-#endif--inlinePerformIO :: IO a -> a-#if defined(__GLASGOW_HASKELL__)-inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r-#else-inlinePerformIO = unsafePerformIO-#endif-{-# INLINE inlinePerformIO #-}----- | Represents a permutation of the integers @[0..n)@.        -data Permutation =-        Perm {-# UNPACK #-} !Int             -             {-# UNPACK #-} !(ForeignPtr Int)-             {-# UNPACK #-} !Int---- | Get the size, raw data array and offset-toForeignPtr :: Permutation -> (Int, ForeignPtr Int, Int)-toForeignPtr (Perm n f o) = (n, f, o)---- | Convert size and raw array to a permutation.  No validation is--- performed on the arguments.-fromForeignPtr :: Int -> ForeignPtr Int -> Int -> Permutation-fromForeignPtr = Perm---- | Get the size of the permutation.-size :: Permutation -> Int-size (Perm n _ _) = n-{-# INLINE size #-}---- | Perform an operation, given a pointer to the start of the--- permutation data-withPermutationPtr :: Permutation -> (Ptr Int -> IO a) -> IO a-withPermutationPtr (Perm _ fptr off) f =-    withForeignPtr fptr $ \ptr ->-        f (ptr `advancePtr` off)---- | Apply a permutation to an integer.  The integer must be in the range---   @[0..n)@.-apply :: Permutation -> Int -> Int-apply p@(Perm n _ _) i -    | i < 0 || i >= n =-        error $ -            "applyPerm: Tried to apply permutation of size `" ++ show n ++-            "' to the value `" ++ show i ++ "'."-    | otherwise =-        unsafeApply p i-{-# INLINE apply #-}---- | Same as 'apply' but does not range-check the argument.-unsafeApply :: Permutation -> Int -> Int-unsafeApply p i =-    inlinePerformIO $ do-        withPermutationPtr p $ flip peekElemOff i-{-# INLINE unsafeApply #-}---- | Create a permutation from a list of values.  The list must be of length---   @n@ and contain each integer in @{0, 1, ..., (n-1) }@ exactly once.---   The permutation that is returned will send the integer @i@ to its index---   in the list.-permutation :: Int -> [Int] -> Permutation-permutation n is = -    let p = unsafePermutation n is-    in case isValid p of-           False -> error $ "Not a valid permutation."-           True  -> p---- | Same as 'permutation', but does not check that the inputs are valid.-unsafePermutation :: Int -> [Int] -> Permutation-unsafePermutation n is = -    unsafePerformIO $ do-        fptr <- mallocForeignPtrArray n-        withForeignPtr fptr $ \ptr -> pokeArray ptr is-        return $ fromForeignPtr n fptr 0-{-# NOINLINE unsafePermutation #-}---fromList :: [Int] -> Permutation-fromList is = permutation (length is) is--toList :: Permutation -> [Int]-toList p = unsafePerformIO $ -               withPermutationPtr p $ peekArray (size p)---- | Create an identity permutation of the given size.-identity :: Int -> Permutation-identity n =-    unsafePerformIO $ do-        fptr <- mallocForeignPtrArray n-        withForeignPtr fptr $ \ptr -> pokeArray ptr [0..(n-1)]-        return $ fromForeignPtr n fptr 0----- | Get the inverse of a permutation.-inverse :: Permutation -> Permutation-inverse p =-    let n = size p-    in-        unsafePerformIO $ do-            fptr <- mallocForeignPtrArray n-            withForeignPtr fptr $ \ptr -> do-                pokeArray ptr [0..(n-1)]-                invertWith (swap ptr) p-            return $ fromForeignPtr n fptr 0-    where-        swap :: Ptr Int -> Int -> Int -> IO ()-        swap ptr i j = do-            x <- peekElemOff ptr i-            y <- peekElemOff ptr j-            pokeElemOff ptr i y-            pokeElemOff ptr j x-{-# NOINLINE inverse #-}-----isValid :: Permutation -> Bool-isValid p@(Perm n _ _) =-    unsafePerformIO $-        withPermutationPtr p $ \ptr -> do-            liftM and $-                mapM (\i -> peekElemOff ptr i -                            >>= \x -> isValidI ptr x i)-                     [0..(n-1)]-             -    where-        isValidI :: Ptr Int -> Int -> Int -> IO Bool-        isValidI ptr x i =-            liftM and $ -                sequence [ inRange x, isUnique x ptr i ]-    -        inRange :: Int -> IO Bool-        inRange x =-            return $ x >= 0 && x < n-        -        isUnique :: Int -> Ptr Int -> Int -> IO Bool-        isUnique x ptr' n'-            | n' == 0 =-                return True-            | otherwise = do-                x' <- peek ptr'-                if x' == x -                    then return False-                    else isUnique x (ptr' `advancePtr` 1) (n'-1)-    --- | @applyWith swap perm@ applies the permutation as a sequence of swaps.  After --- this function is applied, @OUT[i] = IN[P[i]]@-applyWith :: (Monad m) => (Int -> Int -> m ()) -> Permutation -> m ()-applyWith swap p =-    let n = size p-    in foldM (flip $ doCycle swap) IntSet.empty [0..(n-1)] >> return ()-    -    where-        doCycle :: (Monad m) => -            (Int -> Int -> m ()) -> Int -> IntSet -> m (IntSet)-        doCycle swp i visited = -            if i `IntSet.member` visited -                then return visited-                else let visited' = IntSet.insert i visited-                         next     = unsafeApply p i-                     in doCycle' swp i i next visited'-            -        doCycle' :: (Monad m) => -            (Int -> Int -> m ()) -> Int -> Int -> Int -> IntSet -> m (IntSet)-        doCycle' swp start cur next visited-            | next == start =-                return visited -            | otherwise = -                let visited' = IntSet.insert next visited-                    next'    = unsafeApply p next-                in do-                    swp cur next-                    doCycle' swp start next next' visited'---- | @invertWith swap p@ applies the inverse of the permutation as a --- sequence of swaps.  After this function is applied, @OUT[P[i]] = IN[i]@-invertWith :: (Monad m) => (Int -> Int -> m ()) -> Permutation -> m ()-invertWith swap p =-    let n = size p-    in foldM (flip $ doCycle swap) IntSet.empty [0..(n-1)] >> return ()-    -    where-        doCycle :: Monad m => -            (Int -> Int -> m ()) -> Int -> IntSet -> m (IntSet)-        doCycle swp i visited = -            if i `IntSet.member` visited -                then return visited-                else let visited' = IntSet.insert i visited-                         cur      = unsafeApply p i-                     in doCycle' swp i cur visited'-            -        doCycle' :: Monad m => (Int -> Int -> m ()) -> Int -> Int -> IntSet -> m (IntSet)-        doCycle' swp start cur visited-            | cur == start =-                return visited -            | otherwise = -                let visited' = IntSet.insert cur visited-                    cur'     = unsafeApply p cur-                in do-                    swp start cur-                    doCycle' swp start cur' visited'---instance Show Permutation where-    show p = "permutation "     ++ show (size p) ++ " " ++ show (toList p)-    -instance Eq Permutation where-    (==) p q = (size p == size q) && (toList p == toList q)-    
Setup.lhs view
@@ -10,5 +10,5 @@ >         then ioError $ userError $ "failed" >         else return () >-> main = defaultMainWithHooks defaultUserHooks+> main = defaultMainWithHooks simpleUserHooks >        {runTests=testing}
+ examples/Enumerate.hs view
@@ -0,0 +1,81 @@+module Main where+    +import Control.Monad+import Control.Monad.ST+import Data.List( permutations )+import Data.STRef+import System.Environment++import Data.Permute+import Data.Permute.MPermute+    +-- | Execute an action on every permutation of a given order.  This function+-- is unsafe, because it only allocates space for a single permutation.  The+-- action @f@ should not retain any references to the passed-in @Permute@+-- object, otherwise bad things will happen.  For instance, running+-- >+-- >    forAllPermutes 2 id+-- >+-- in ghci yields @[listPermute 2 [1,0],listPermute 2 [1,0]]@.+--+forAllPermutes :: Int -> (Permute -> a) -> [a]+forAllPermutes n f = runST $ do+    -- Allocate a mutable permutation initialized to the identity+    p  <- newPermute n+    +    -- Run the action on all successors of p+    runOnSuccessors p+    +  where+    runOnSuccessors p = do+        -- Cast the mutable permutation to an immutable one+        -- and the action on the immutable+        a <- liftM f (unsafeFreeze p)++        -- Set the permutation to be equal to its successor+        hasNext <- setNext p+        +        -- If a successor exists, recurse, otherwise stop+        as <- unsafeInterleaveST $+            if hasNext then runOnSuccessors p +                       else return []+        return (a:as)+++forAllPermutesM_ :: (MPermute p m) => Int -> (Permute -> m a) -> m () +forAllPermutesM_ n f = sequence_ $ forAllPermutes n f+{-# INLINE forAllPermutesM_ #-}++-- | Count the number of permutations of a given order+countAllPermutes :: Int -> Int+countAllPermutes n = length $ forAllPermutes n id++-- | Another version of the same function.  This one is slightly slower.+countAllPermutes2 :: Int -> Int+countAllPermutes2 n = runST $ do+    count <- newSTRef 0+    forAllPermutesM_ n $ (const $ modifySTRef' count (+1))+    readSTRef count+  where+    modifySTRef' var f = do+        old <- readSTRef var+        writeSTRef var $! f old++-- | Yet another version, this time using 'permutations' from Data.List.+-- This version is faster but uses more memory.+countAllPermutes3 :: Int -> Int+countAllPermutes3 n = length $ permutations [0 .. n-1]+++-- | Print all permutations of a given order.+printAllPermutes :: Int -> IO ()+printAllPermutes n =+    forAllPermutesM_ n (putStrLn . show . elems)+++main = do+    n  <- fmap (read . head) getArgs+    let count = countAllPermutes n+    putStrLn $ +        "There are " ++ show count ++ " permutations of order " ++ show n ++ "."+    
+ lib/Data/IntArray.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE MagicHash, UnboxedTuples #-}+{-# OPTIONS_GHC -funbox-strict-fields #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.IntArray+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--++module Data.IntArray (+    IntArray,+    STIntArray,++    numElements,+    unsafeAt,+    elems,++    newArray_,+    +    numElementsSTIntArray,+    getNumElements,+    unsafeRead,+    unsafeWrite,+    unsafeSwap,+    +    readElems,+    writeElems,+    +    unsafeFreeze,+    unsafeThaw,+    +    ) where++import GHC.Base( Int(..) )+import GHC.Prim+import GHC.ST+import Foreign( sizeOf )++-----------------------------  Immutable arrays -----------------------------++data IntArray = IntArray !Int (ByteArray#)++{-# INLINE numElements #-}+numElements :: IntArray -> Int+numElements (IntArray n _) = n++{-# INLINE unsafeAt #-}+unsafeAt :: IntArray -> Int -> Int+unsafeAt (IntArray _ arr#) (I# i#) =+    case indexIntArray# arr# i# of { e# ->+    I# e# }++{-# INLINE elems #-}+elems :: IntArray -> [Int]+elems arr@(IntArray n _) =+    [ unsafeAt arr i | i <- [0 .. n-1]]+++------------------------------  Mutable arrays ------------------------------++data STIntArray s = STIntArray !Int (MutableByteArray# s)++{-# INLINE newArray_ #-}+newArray_ :: Int -> ST s (STIntArray s)+newArray_ n@(I# n#) =+    ST $ \s1# ->+        case newByteArray# (n# *# sizeOfInt) s1# of { (# s2#, marr# #) ->+        (# s2#, STIntArray n marr# #) }+  where+    sizeOfInt = case sizeOf (0 :: Int) of (I# s#) -> s#+    +{-# INLINE numElementsSTIntArray #-}+numElementsSTIntArray :: STIntArray s -> Int+numElementsSTIntArray (STIntArray n _) = n++{-# INLINE getNumElements #-}+getNumElements :: STIntArray s -> ST s Int+getNumElements arr = return $! numElementsSTIntArray arr++{-# INLINE unsafeRead #-}+unsafeRead :: STIntArray s -> Int -> ST s Int+unsafeRead (STIntArray _ marr#) (I# i#) =+    ST $ \s1# -> +        case readIntArray# marr# i# s1# of { (# s2#, e# #) ->+        let e = I# e# in+        (# s2#, e #) }++{-# INLINE unsafeWrite #-}+unsafeWrite :: STIntArray s -> Int -> Int -> ST s ()+unsafeWrite (STIntArray _ marr#) (I# i#) (I# e#) =+    ST $ \s1# -> +        case writeIntArray# marr# i# e# s1# of { s2# ->+        (# s2#, () #) }++{-# INLINE unsafeSwap #-}+unsafeSwap :: STIntArray s -> Int -> Int -> ST s ()+unsafeSwap (STIntArray _ marr#) (I# i#) (I# j#) =+    ST $ \s1# ->+        let doSwap =+                case readIntArray#  marr# i# s1# of { (# s2#, e# #) ->+                case readIntArray#  marr# j# s2# of { (# s3#, f# #) ->+                case writeIntArray# marr# i# f# s3# of { s4# ->+                     writeIntArray# marr# j# e# s4# }}} in+        if i# ==# j# then (# s1#, () #)+                     else case doSwap of { s2# ->+                          (# s2#, () #) }++{-# INLINE readElems #-}+readElems :: STIntArray s -> ST s [Int]+readElems (STIntArray (I# n#) marr#) =+    ST $ \s1# ->+        let inlineReadList i# | i# ==# n# = []+                              | otherwise = +                case readIntArray# marr# i# s1# of { (# _, e# #) ->+                let e  = I# e#+                    es = inlineReadList (i# +# 1#) in+                (e:es) } in+        case inlineReadList 0# of { es ->+        (# s1#, es #)}+        +{-# INLINE writeElems #-}+writeElems :: STIntArray s -> [Int] -> ST s ()+writeElems (STIntArray (I# n#) marr#) es =+    ST $ \s1# ->+        let fillFromList i# xs s2# | i# ==# n# = s2#+                                   | otherwise = case xs of+                []         -> s2#+                (I# y#):ys -> case writeIntArray# marr# i# y# s2# of { s3# ->+                              fillFromList (i# +# 1#) ys s3# } in+        case fillFromList 0# es s1# of { s2# ->+        (# s2#, () #) }++{-# INLINE unsafeFreeze #-}+unsafeFreeze :: STIntArray s -> ST s IntArray+unsafeFreeze (STIntArray n marr#) =+    ST $ \s1# ->+        case unsafeFreezeByteArray# marr# s1# of { (# s2#, arr# #) ->+        let arr = IntArray n arr# in+        (# s2#, arr #)}++{-# INLINE unsafeThaw #-}+unsafeThaw :: IntArray -> ST s (STIntArray s)+unsafeThaw (IntArray n arr#) =+    ST $ \s1# ->+        let coerceArray :: State# s -> MutableByteArray# s+            coerceArray _ = unsafeCoerce# arr#+            marr# = coerceArray s1#+            marr  = STIntArray n marr# in+        (# s1#, marr #)
+ lib/Data/Permute.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE Rank2Types #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--+-- Immutable permutations.++module Data.Permute (+    -- * Permutations+    Permute,+    +    -- * Creating permutations+    permute,+    listPermute,+    swapsPermute,++    -- * Accessing permutation elements+    apply,+    unsafeApply,++    -- * Permutation properties+    size,+    elems,+    +    -- * Permutation functions+    inverse,+    next,+    prev,+    +    -- * Applying permutations+    swaps,+    invSwaps,+    +    -- * Sorting+    sort,+    sortBy,+    order,+    orderBy,+    rank,+    rankBy,+    +    ) where++import Control.Monad+import Control.Monad.ST+import Data.Permute.Base+import Data.Permute.ST++-- | Construct an identity permutation of the given size.+permute :: Int -> Permute+permute n = runST $+    unsafeFreeze =<< newPermute n++-- | Construct a permutation from a list of elements.  +-- @listPermute n is@ creates a permuation of size @n@ with+-- the @i@th element equal to @is !! i@.  For the permutation to be valid,+-- the list @is@ must have length @n@ and contain the indices @0..(n-1)@ +-- exactly once each.+listPermute :: Int -> [Int] -> Permute+listPermute n is = runST $+    unsafeFreeze =<< newListPermute n is++-- | Construct a permutation from a list of swaps.+-- @swapsPermute n ss@ creats a permuation of size @n@ given by a+-- sequence of swaps.+-- If @ss@ is @[(i0,j0), (i1,j1), ..., (ik,jk)]@, the+-- sequence of swaps is+-- @i0 \<-> j0@, then +-- @i1 \<-> j1@, and so on until+-- @ik \<-> jk@.+swapsPermute :: Int -> [(Int,Int)] -> Permute+swapsPermute n ss = runST $+    unsafeFreeze =<< newSwapsPermute n ss++-- | @apply p i@ gets the value of the @i@th element of the permutation+-- @p@.  The index @i@ must be in the range @0..(n-1)@, where @n@ is the+-- size of the permutation.+apply :: Permute -> Int -> Int+apply p i+    | i >= 0 && i < size p = +        unsafeApply p i+    | otherwise =+        error "Invalid index"+{-# INLINE apply #-}++-- | Get the inverse of a permutation+inverse :: Permute -> Permute+inverse p = runST $ +    unsafeFreeze =<< getInverse =<< unsafeThaw p++-- | Return the next permutation in lexicographic order, or @Nothing@ if+-- there are no further permutations.  Starting with the identity permutation+-- and repeatedly calling this function will iterate through all permutations+-- of a given order.+next :: Permute -> Maybe Permute+next = nextPrevHelp setNext++-- | Return the previous permutation in lexicographic order, or @Nothing@+-- if there is no such permutation.+prev :: Permute -> Maybe Permute+prev = nextPrevHelp setPrev++nextPrevHelp :: (forall s. STPermute s -> ST s Bool) +             -> Permute -> Maybe Permute+nextPrevHelp set p = runST $ do+    p' <- thaw p+    set p' >>= \valid ->+        if valid+            then liftM Just $ unsafeFreeze p'+            else return Nothing++-- | Get a list of swaps equivalent to the permutation.  A result of+-- @[ (i0,j0), (i1,j1), ..., (ik,jk) ]@ means swap @i0 \<-> j0@, +-- then @i1 \<-> j1@, and so on until @ik \<-> jk@.+swaps :: Permute -> [(Int,Int)]+swaps p = runST $+    getSwaps =<< unsafeThaw p++-- | Get a list of swaps equivalent to the inverse of permutation.+invSwaps :: Permute -> [(Int,Int)]+invSwaps p = runST $+    getInvSwaps =<< unsafeThaw p+++-- | @sort n xs@ sorts the first @n@ elements of @xs@ and returns a +-- permutation which transforms @xs@ into sorted order.  The results are+-- undefined if @n@ is greater than the length of @xs@.  This is a special +-- case of 'sortBy'.+sort :: (Ord a) => Int -> [a] -> ([a], Permute)+sort n xs = runST $ do+    (xs',mp) <- getSort n xs+    p <- unsafeFreeze mp+    return (xs',p)+    +sortBy :: (a -> a -> Ordering) -> Int -> [a] -> ([a], Permute)+sortBy cmp n xs = runST $ do+    (xs',mp) <- getSortBy cmp n xs+    p <- unsafeFreeze mp+    return (xs',p)+++-- | @order n xs@ returns a permutation which rearranges the first @n@+-- elements of @xs@ into ascending order. The results are undefined if @n@ is +-- greater than the length of @xs@.  This is a special case of 'orderBy'.+order :: (Ord a) => Int -> [a] -> Permute+order n xs = runST $ +    unsafeFreeze =<< getOrder n xs++orderBy :: (a -> a -> Ordering) -> Int -> [a] -> Permute+orderBy cmp n xs = runST $+    unsafeFreeze =<< getOrderBy cmp n xs++-- | @rank n xs@ eturns a permutation, the inverse of which rearranges the +-- first @n@ elements of @xs@ into ascending order. The returned permutation, +-- @p@, has the property that @p[i]@ is the rank of the @i@th element of @xs@. +-- The results are undefined if @n@ is greater than the length of @xs@.+-- This is a special case of 'rankBy'.  +rank :: (Ord a) => Int -> [a] -> Permute+rank n xs = runST $+    unsafeFreeze =<< getRank n xs++rankBy :: (a -> a -> Ordering) -> Int -> [a] -> Permute+rankBy cmp n xs = runST $+    unsafeFreeze =<< getRankBy cmp n xs
+ lib/Data/Permute/Base.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE Rank2Types #-}+{-# OPTIONS_GHC -XMagicHash -XUnboxedTuples #-}+{-# OPTIONS_HADDOCK hide #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute.Base+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--++module Data.Permute.Base +    where++import Control.Monad+import Control.Monad.ST+import Foreign++import Data.IntArray ( IntArray, STIntArray )+import qualified Data.IntArray as Arr+import qualified Data.IntArray as ArrST+++--------------------------------- Permute ---------------------------------++-- | The immutable permutation data type.+-- Internally, a permutation of size @n@ is stored as an+-- @0@-based array of @n@ 'Int's.  The permutation represents a reordering of+-- the integers @0, ..., (n-1)@.  The @i@th element of the array stores+-- the value @p[i]@. +newtype Permute = Permute IntArray++unsafeApply :: Permute -> Int -> Int+unsafeApply (Permute p) i = Arr.unsafeAt p i+{-# INLINE unsafeApply #-}++-- | Get the size of the permutation.+size :: Permute -> Int+size (Permute p) = Arr.numElements p+{-# INLINE size #-}++-- | Get a list of the permutation elements.+elems :: Permute -> [Int]+elems (Permute p) = Arr.elems p+{-# INLINE elems #-}++instance Show Permute where+    show p = "listPermute " ++ show (size p) ++ " " ++ show (elems p)+    +instance Eq Permute where+    (==) p q = (size p == size q) && (elems p == elems q)+++--------------------------------- STPermute --------------------------------++-- | A mutable permutation that can be manipulated in the 'ST' monad. The+-- type argument @s@ is the state variable argument for the 'ST' type.+newtype STPermute s = STPermute (STIntArray s)++getSizeSTPermute :: STPermute s -> ST s Int+getSizeSTPermute (STPermute marr) = ArrST.getNumElements marr+{-# INLINE getSizeSTPermute #-}++sizeSTPermute :: STPermute s -> Int+sizeSTPermute (STPermute marr) = ArrST.numElementsSTIntArray marr+{-# INLINE sizeSTPermute #-}++newSTPermute :: Int -> ST s (STPermute s)+newSTPermute n = do+    p@(STPermute marr) <- newSTPermute_ n+    ArrST.writeElems marr [0 .. n-1]+    return $! p+{-# INLINE newSTPermute #-}++newSTPermute_ :: Int -> ST s (STPermute s)+newSTPermute_ n = do+    when (n < 0) $ fail "invalid size"+    liftM STPermute $ ArrST.newArray_ n+{-# INLINE newSTPermute_ #-}++unsafeGetElemSTPermute :: STPermute s -> Int -> ST s Int+unsafeGetElemSTPermute (STPermute marr) i = ArrST.unsafeRead marr i+{-# INLINE unsafeGetElemSTPermute #-}++unsafeSetElemSTPermute :: STPermute s -> Int -> Int -> ST s ()+unsafeSetElemSTPermute (STPermute marr) i x = ArrST.unsafeWrite marr i x+{-# INLINE unsafeSetElemSTPermute #-}++unsafeSwapElemsSTPermute :: STPermute s -> Int -> Int -> ST s ()+unsafeSwapElemsSTPermute (STPermute marr) i j = ArrST.unsafeSwap marr i j+{-# INLINE unsafeSwapElemsSTPermute #-}++getElemsSTPermute :: STPermute s -> ST s [Int]+getElemsSTPermute (STPermute marr) = ArrST.readElems marr+{-# INLINE getElemsSTPermute #-}++setElemsSTPermute :: STPermute s -> [Int] -> ST s ()+setElemsSTPermute (STPermute marr) is = ArrST.writeElems marr is+{-# INLINE setElemsSTPermute #-}++unsafeFreezeSTPermute :: STPermute s -> ST s Permute+unsafeFreezeSTPermute (STPermute marr) = +    (liftM Permute . ArrST.unsafeFreeze) marr+{-# INLINE unsafeFreezeSTPermute #-}++unsafeThawSTPermute :: Permute -> ST s (STPermute s)+unsafeThawSTPermute (Permute arr) =+    (liftM STPermute . ArrST.unsafeThaw) arr+{-# INLINE unsafeThawSTPermute #-}
+ lib/Data/Permute/IO.hs view
@@ -0,0 +1,20 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute.IO+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--+-- Mutable permutations in the 'IO' monad.++module Data.Permute.IO (+    -- * Permutations+    IOPermute,+    +    -- * Overloaded mutable permutation interface+    module Data.Permute.MPermute+    ) where++import Data.Permute.IOBase( IOPermute )+import Data.Permute.MPermute
+ lib/Data/Permute/IOBase.hs view
@@ -0,0 +1,64 @@+{-# OPTIONS_HADDOCK hide #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute.IOBase+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--++module Data.Permute.IOBase+    where++import Control.Monad+import Control.Monad.ST+import Data.Permute.Base++-- | A mutable permutation that can be manipulated in the 'IO' monad. The+-- type argument @s@ is the state variable argument for the 'IO' type.+newtype IOPermute = IOPermute (STPermute RealWorld)++newIOPermute :: Int -> IO (IOPermute)+newIOPermute n = liftM IOPermute $ stToIO (newSTPermute n)+{-# INLINE newIOPermute #-}++newIOPermute_ :: Int -> IO (IOPermute)+newIOPermute_ n = liftM IOPermute $ stToIO (newSTPermute_ n)+{-# INLINE newIOPermute_ #-}++getSizeIOPermute :: IOPermute -> IO Int+getSizeIOPermute (IOPermute p) = stToIO $ getSizeSTPermute p+{-# INLINE getSizeIOPermute #-}++sizeIOPermute :: IOPermute -> Int+sizeIOPermute (IOPermute p) = sizeSTPermute p+{-# INLINE sizeIOPermute #-}+        +unsafeGetElemIOPermute :: IOPermute -> Int -> IO Int+unsafeGetElemIOPermute (IOPermute p) i = stToIO $ unsafeGetElemSTPermute p i+{-# INLINE unsafeGetElemIOPermute #-}++unsafeSetElemIOPermute :: IOPermute -> Int -> Int -> IO ()+unsafeSetElemIOPermute (IOPermute p) i x = stToIO $ unsafeSetElemSTPermute p i x+{-# INLINE unsafeSetElemIOPermute #-}++unsafeSwapElemsIOPermute :: IOPermute -> Int -> Int -> IO ()+unsafeSwapElemsIOPermute (IOPermute p) i j = stToIO $ unsafeSwapElemsSTPermute p i j+{-# INLINE unsafeSwapElemsIOPermute #-}++getElemsIOPermute :: IOPermute -> IO [Int]+getElemsIOPermute (IOPermute p) = stToIO $ getElemsSTPermute p+{-# INLINE getElemsIOPermute #-}++setElemsIOPermute :: IOPermute -> [Int] -> IO ()+setElemsIOPermute (IOPermute p) is = stToIO $ setElemsSTPermute p is+{-# INLINE setElemsIOPermute #-}++unsafeFreezeIOPermute :: IOPermute -> IO Permute+unsafeFreezeIOPermute (IOPermute p) = stToIO $ unsafeFreezeSTPermute p+{-# INLINE unsafeFreezeIOPermute #-}++unsafeThawIOPermute :: Permute -> IO (IOPermute)+unsafeThawIOPermute p = liftM IOPermute $ stToIO (unsafeThawSTPermute p)+{-# INLINE unsafeThawIOPermute #-}
+ lib/Data/Permute/MPermute.hs view
@@ -0,0 +1,474 @@+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, +        FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute.MPermute+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--+-- An overloaded interface to mutable permutations. For permutation types which +-- can be used with this interface, see "Data.Permute.IO" and "Data.Permute.ST".+--++module Data.Permute.MPermute (+    -- * Class of mutable permutation types+    MPermute, ++    -- * Constructing mutable permutations+    newPermute,+    newPermute_,+    newListPermute,+    newSwapsPermute,+    newCopyPermute,+    copyPermute,+    setIdentity,+    +    -- * Accessing permutation elements+    getElem,+    setElem,+    swapElems,+    +    -- * Permutation properties+    getSize,+    getElems,+    setElems,+    isValid,++    -- * Permutation functions+    getInverse,+    copyInverse,+    setNext,+    setPrev,+    +    -- * Applying permutations+    getSwaps,+    getInvSwaps,+    +    -- * Sorting+    getSort,+    getSortBy,+    getOrder,+    getOrderBy,+    getRank,+    getRankBy,+    +    -- * Converstions between mutable and immutable permutations+    freeze,+    unsafeFreeze,+    thaw,+    unsafeThaw,+    +    -- * Unsafe operations+    unsafeNewListPermute,+    unsafeNewSwapsPermute,+    unsafeGetElem,+    unsafeSetElem,+    unsafeSwapElems,++    ) where++import Control.Monad+import Control.Monad.ST+import Data.Function( on )+import qualified Data.List as List+import System.IO.Unsafe( unsafeInterleaveIO )++import Data.Permute.Base+import Data.Permute.IOBase+++--------------------------------- MPermute --------------------------------++-- | Class for representing a mutable permutation.  The type is parameterized+-- over the type of the monad, @m@, in which the mutable permutation will be+-- manipulated.+class (Monad m) => MPermute p m | p -> m, m -> p where+    -- | Get the size of a permutation.+    getSize :: p -> m Int++    -- | Create a new permutation initialized to be the identity.+    newPermute :: Int -> m p++    -- | Allocate a new permutation but do not initialize it.+    newPermute_ :: Int -> m p++    unsafeGetElem :: p -> Int -> m Int+    unsafeSetElem :: p -> Int -> Int -> m ()+    unsafeSwapElems :: p -> Int -> Int -> m ()    ++    -- | Get a lazy list of the permutation elements.  The laziness makes this+    -- function slightly dangerous if you are modifying the permutation.+    getElems :: p -> m [Int]++    -- | Set all the values of a permutation from a list of elements.+    setElems :: p -> [Int] -> m ()++    unsafeFreeze :: p -> m Permute+    unsafeThaw :: Permute -> m p+    +    unsafeInterleaveM :: m a -> m a+    +        +-- | Construct a permutation from a list of elements.  +-- @newListPermute n is@ creates a permuation of size @n@ with+-- the @i@th element equal to @is !! i@.  For the permutation to be valid,+-- the list @is@ must have length @n@ and contain the indices @0..(n-1)@ +-- exactly once each.+newListPermute :: (MPermute p m) => Int -> [Int] -> m p+newListPermute n is = do+    p <- unsafeNewListPermute n is+    valid <- isValid p+    when (not valid) $ fail "invalid permutation"+    return $! p+{-# INLINE newListPermute #-}++unsafeNewListPermute :: (MPermute p m) => Int -> [Int] -> m p+unsafeNewListPermute n is = do+    p <- newPermute_ n+    setElems p is+    return $! p+{-# INLINE unsafeNewListPermute #-}++-- | Construct a permutation from a list of swaps.+-- @newSwapsPermute n ss@ creates a permuation of size @n@ given a+-- sequence of swaps.+-- If @ss@ is @[(i0,j0), (i1,j1), ..., (ik,jk)]@, the+-- sequence of swaps is+-- @i0 \<-> j0@, then +-- @i1 \<-> j1@, and so on until+-- @ik \<-> jk@.+newSwapsPermute :: (MPermute p m) => Int -> [(Int,Int)] -> m p+newSwapsPermute = newSwapsPermuteHelp swapElems+{-# INLINE newSwapsPermute #-}++unsafeNewSwapsPermute :: (MPermute p m) => Int -> [(Int,Int)] -> m p+unsafeNewSwapsPermute = newSwapsPermuteHelp unsafeSwapElems+{-# INLINE unsafeNewSwapsPermute #-}++newSwapsPermuteHelp :: (MPermute p m) => (p -> Int -> Int -> m ())+                       -> Int -> [(Int,Int)] -> m p+newSwapsPermuteHelp swap n ss = do+    p <- newPermute n+    mapM_ (uncurry $ swap p) ss+    return $! p+{-# INLINE newSwapsPermuteHelp #-}++-- | Construct a new permutation by copying another.+newCopyPermute :: (MPermute p m) => p -> m p+newCopyPermute p = do+    n  <- getSize p+    p' <- newPermute_ n+    copyPermute p' p+    return $! p'+{-# INLINE newCopyPermute #-}++-- | @copyPermute dst src@ copies the elements of the permutation @src@+-- into the permtuation @dst@.  The two permutations must have the same+-- size.+copyPermute :: (MPermute p m) => p -> p -> m ()+copyPermute dst src =+    getElems src >>= setElems dst+{-# INLINE copyPermute #-}++-- | Set a permutation to the identity.+setIdentity :: (MPermute p m) => p -> m ()+setIdentity p = do+    n <- getSize p+    setElems p [0 .. n-1]+{-# INLINE setIdentity #-}++-- | @getElem p i@ gets the value of the @i@th element of the permutation+-- @p@.  The index @i@ must be in the range @0..(n-1)@, where @n@ is the+-- size of the permutation.+getElem :: (MPermute p m) => p -> Int -> m Int+getElem p i = do+    n <- getSize p+    when (i < 0 || i >= n) $ fail "getElem: invalid index"+    unsafeGetElem p i+{-# INLINE getElem #-}++-- | @setElem p i x@ sets the value of the @i@th element of the permutation+-- @p@.  The index @i@ must be in the range @0..(n-1)@, where @n@ is the+-- size of the permutation.+setElem :: (MPermute p m) => p -> Int -> Int -> m ()+setElem p i x = do+    n <- getSize p+    when (i < 0 || i >= n) $ fail "getElem: invalid index"+    unsafeSetElem p i x+{-# INLINE setElem #-}++-- | @swapElems p i j@ exchanges the @i@th and @j@th elements of the +-- permutation @p@.+swapElems :: (MPermute p m) => p -> Int -> Int -> m ()+swapElems p i j = do+    n <- getSize p+    when (i < 0 || i >= n || j < 0 || j >= n) $ fail "swapElems: invalid index"+    unsafeSwapElems p i j+{-# INLINE swapElems #-}++-- | Returns whether or not the permutation is valid.  For it to be valid,+-- the numbers @0,...,(n-1)@ must all appear exactly once in the stored+-- values @p[0],...,p[n-1]@.+isValid :: (MPermute p m) => p -> m Bool+isValid p = do+    n <- getSize p+    liftM and $ validIndices n+  where+    j `existsIn` i = do+        seen <- liftM (take i) $ getElems p+        return $ (any (==j)) seen+        +    isValidIndex n i = do+        i' <- unsafeGetElem p i+        valid  <- return $ i' >= 0 && i' < n+        unique <- liftM not (i' `existsIn` i)+        return $ valid && unique++    validIndices n = validIndicesHelp n 0++    validIndicesHelp n i+        | i == n = return []+        | otherwise = do+            a  <- isValidIndex n i+            as <- unsafeInterleaveM $ validIndicesHelp n (i+1)+            return (a:as)+{-# INLINE isValid #-}++-- | Compute the inverse of a permutation.  +getInverse :: (MPermute p m) => p -> m p+getInverse p = do+    n <- getSize p+    q <- newPermute_ n+    copyInverse q p+    return $! q+{-# INLINE getInverse #-}++-- | Set one permutation to be the inverse of another.  +-- @copyInverse inv p@ computes the inverse of @p@ and stores it in @inv@.+-- The two permutations must have the same size.+copyInverse :: (MPermute p m) => p -> p -> m ()+copyInverse dst src = do+    n  <- getSize src+    n' <- getSize dst+    when (n /= n') $ fail "permutation size mismatch"+    forM_ [0 .. n-1] $ \i -> do+        i' <- unsafeGetElem src i+        unsafeSetElem dst i' i+{-# INLINE copyInverse #-}+    +-- | Advance a permutation to the next permutation in lexicogrphic order and+-- return @True@.  If no further permutaitons are available, return @False@ and+-- leave the permutation unmodified.  Starting with the idendity permutation +-- and repeatedly calling @setNext@ will iterate through all permutations of a +-- given size.+setNext :: (MPermute p m) => p -> m Bool+setNext = setNextBy compare+{-# INLINE setNext #-}++-- | Step backwards to the previous permutation in lexicographic order and+-- return @True@.  If there is no previous permutation, return @False@ and+-- leave the permutation unmodified.+setPrev :: (MPermute p m) => p -> m Bool+setPrev = setNextBy (flip compare)+{-# INLINE setPrev #-}++setNextBy :: (MPermute p m) => (Int -> Int -> Ordering) -> p -> m Bool+setNextBy cmp p = do+    n <- getSize p+    if n > 1+        then do+            findLastAscent (n-2) >>=+                maybe (return False) (\i -> do+                    i'     <- unsafeGetElem p i+                    i1'    <- unsafeGetElem p (i+1)+                    (k,k') <- findSmallestLargerThan n i' (i+2) (i+1) i1'+                    +                    -- swap i and k+                    unsafeSetElem p i k'+                    unsafeSetElem p k i'+                    +                    reverseElems (i+1) (n-1)+                    +                    return True+                )+        else +            return False+        +  where+    i `lt` j = cmp i j == LT+    i `gt` j = cmp i j == GT+    +    findLastAscent i = do+        ascent <- isAscent i+        if ascent then return (Just i) else recurse+      where+        recurse = if i /= 0 then findLastAscent (i-1) else return Nothing +    +    findSmallestLargerThan n i' j k k'+        | j < n = do+            j' <- unsafeGetElem p j+            if j' `gt` i' && j' `lt` k'+                then findSmallestLargerThan n i' (j+1) j j'+                else findSmallestLargerThan n i' (j+1) k k'+        | otherwise =+            return (k,k')+            +    isAscent i = liftM2 lt (unsafeGetElem p i) (unsafeGetElem p (i+1))+    +    reverseElems i j+        | i >= j = return ()+        | otherwise = do+            unsafeSwapElems p i j+            reverseElems (i+1) (j-1)+{-# INLINE setNextBy #-}  +++-- | Get a lazy list of swaps equivalent to the permutation.  A result of+-- @[ (i0,j0), (i1,j1), ..., (ik,jk) ]@ means swap @i0 \<-> j0@, +-- then @i1 \<-> j1@, and so on until @ik \<-> jk@.  The laziness makes this+-- function slightly dangerous if you are modifying the permutation.+getSwaps :: (MPermute p m) => p -> m [(Int,Int)]+getSwaps = getSwapsHelp False+{-# INLINE getSwaps #-}++-- | Get a lazy list of swaps equivalent to the inverse of a permutation.+getInvSwaps :: (MPermute p m) => p -> m [(Int,Int)]+getInvSwaps = getSwapsHelp True+{-# INLINE getInvSwaps #-}++getSwapsHelp :: (MPermute p m) => Bool -> p -> m [(Int,Int)]+getSwapsHelp inv p = do+    n <- getSize p+    liftM concat $+        forM [0..(n-1)] $ \i -> do+            k <- unsafeGetElem p i+            least <- isLeast i k+            if least +                then do+                    i' <- unsafeGetElem p i+                    unsafeInterleaveM $ doCycle i i i'+                else+                    return []+  where+    isLeast i k +        | k > i = do+            k' <- unsafeGetElem p k+            isLeast i k'+        | k < i     = return False+        | otherwise = return True+        +    doCycle start i i'+        | i' == start = return []+        | otherwise = do+            i'' <- unsafeGetElem p i'+            let s = if inv then (start,i') else (i,i')+            ss <- unsafeInterleaveM $ doCycle start i' i''+            return (s:ss)+{-# INLINE getSwapsHelp #-}++-- | Convert a mutable permutation to an immutable one.+freeze :: (MPermute p m) => p -> m Permute+freeze p = unsafeFreeze =<< newCopyPermute p+{-# INLINE freeze #-}++-- | Convert an immutable permutation to a mutable one.+thaw :: (MPermute p m) => Permute -> m p+thaw p = newCopyPermute =<< unsafeThaw p+{-# INLINE thaw #-}++-- | @getSort n xs@ sorts the first @n@ elements of @xs@ and returns a +-- permutation which transforms @xs@ into sorted order.  The results are+-- undefined if @n@ is greater than the length of @xs@.  This is a special +-- case of 'getSortBy'.+getSort :: (Ord a, MPermute p m) => Int -> [a] -> m ([a], p)+getSort = getSortBy compare+{-# INLINE getSort #-}+    +getSortBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m ([a], p)+getSortBy cmp n xs =+    let ys       = take n xs+        (is,ys') = (unzip . List.sortBy (cmp `on` snd) . zip [0..]) ys+    in liftM ((,) ys') $ unsafeNewListPermute n is+{-# INLINE getSortBy #-}++-- | @getOrder n xs@ returns a permutation which rearranges the first @n@+-- elements of @xs@ into ascending order. The results are undefined if @n@ is +-- greater than the length of @xs@.  This is a special case of 'getOrderBy'.+getOrder :: (Ord a, MPermute p m) => Int -> [a] -> m p+getOrder = getOrderBy compare+{-# INLINE getOrder #-}++getOrderBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m p+getOrderBy cmp n xs =+    liftM snd $ getSortBy cmp n xs+{-# INLINE getOrderBy #-}++-- | @getRank n xs@ eturns a permutation, the inverse of which rearranges the +-- first @n@ elements of @xs@ into ascending order. The returned permutation, +-- @p@, has the property that @p[i]@ is the rank of the @i@th element of @xs@. +-- The results are undefined if @n@ is greater than the length of @xs@.+-- This is a special case of 'getRankBy'.  +getRank :: (Ord a, MPermute p m) => Int -> [a] -> m p+getRank = getRankBy compare+{-# INLINE getRank #-}++getRankBy :: (MPermute p m) => (a -> a -> Ordering) -> Int -> [a] -> m p+getRankBy cmp n xs = do+    p <- getOrderBy cmp n xs+    getInverse p+{-# INLINE getRankBy #-}+++--------------------------------- Instances ---------------------------------++instance MPermute (STPermute s) (ST s) where+    getSize = getSizeSTPermute+    {-# INLINE getSize #-}+    newPermute = newSTPermute+    {-# INLINE newPermute #-}+    newPermute_ = newSTPermute_+    {-# INLINE newPermute_ #-}+    unsafeGetElem = unsafeGetElemSTPermute+    {-# INLINE unsafeGetElem #-}+    unsafeSetElem = unsafeSetElemSTPermute+    {-# INLINE unsafeSetElem #-}+    unsafeSwapElems = unsafeSwapElemsSTPermute+    {-# INLINE unsafeSwapElems #-}+    getElems = getElemsSTPermute+    {-# INLINE getElems #-}+    setElems = setElemsSTPermute+    {-# INLINE setElems #-}+    unsafeFreeze = unsafeFreezeSTPermute+    {-# INLINE unsafeFreeze #-}+    unsafeThaw = unsafeThawSTPermute+    {-# INLINE unsafeThaw #-}+    unsafeInterleaveM = unsafeInterleaveST+    {-# INLINE unsafeInterleaveM #-}+++instance MPermute IOPermute IO where+    getSize = getSizeIOPermute+    {-# INLINE getSize #-}+    newPermute = newIOPermute+    {-# INLINE newPermute #-}+    newPermute_ = newIOPermute_+    {-# INLINE newPermute_ #-}+    unsafeGetElem = unsafeGetElemIOPermute+    {-# INLINE unsafeGetElem #-}+    unsafeSetElem = unsafeSetElemIOPermute+    {-# INLINE unsafeSetElem #-}+    unsafeSwapElems = unsafeSwapElemsIOPermute+    {-# INLINE unsafeSwapElems #-}+    getElems = getElemsIOPermute+    {-# INLINE getElems #-}+    setElems = setElemsIOPermute+    {-# INLINE setElems #-}+    unsafeFreeze = unsafeFreezeIOPermute+    {-# INLINE unsafeFreeze #-}+    unsafeThaw = unsafeThawIOPermute+    {-# INLINE unsafeThaw #-}+    unsafeInterleaveM = unsafeInterleaveIO+    {-# INLINE unsafeInterleaveM #-}+
+ lib/Data/Permute/ST.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE Rank2Types #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Data.Permute.ST+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--+-- Mutable permutations in the 'ST' monad.++module Data.Permute.ST (+    -- * Permutations+    STPermute,+    runSTPermute,+    +    -- * Overloaded mutable permutation interface+    module Data.Permute.MPermute+    ) where++import Control.Monad.ST++import Data.Permute.Base( Permute, STPermute, unsafeFreezeSTPermute )+import Data.Permute.MPermute++-- | A safe way to create and work with a mutable permutation before returning +-- an immutable permutation for later perusal. This function avoids copying the +-- permutation before returning it - it uses unsafeFreeze internally, but this +-- wrapper is a safe interface to that function. +runSTPermute :: (forall s. ST s (STPermute s)) -> Permute+runSTPermute p = runST (p >>= unsafeFreezeSTPermute)+{-# INLINE runSTPermute #-}
permutation.cabal view
@@ -1,11 +1,19 @@ name:            permutation-version:         0.1+version:         0.2 homepage:        http://stat.stanford.edu/~patperry/code/permutation-synopsis:        Permutations and combinations+synopsis:        A library for representing and applying permutations. description:-    A library for representing and applying permutations.+    This library includes data types for storing permutations.  It+    implements pure and impure types, the latter which can be modified+    in-place.  The main utility of the library is converting between+    the linear representation of a permutation to a sequence of swaps.+    This allows, for instance, applying a permutation or its inverse+    to an array with O(1) memory use.     .-category:        Math+    Much of the interface for the library is based on the permutation +    functions in the GNU Scientific Library (GSL).+    .+category:        Data Structures, Math license:         BSD3 license-file:    LICENSE copyright:       (c) 2008. Patrick Perry <patperry@stanford.edu>@@ -13,14 +21,32 @@ maintainer:      Patrick Perry <patperry@stanford.edu> cabal-version: >= 1.2.0 build-type:      Custom-tested-with:     GHC ==6.8.2+tested-with:     GHC ==6.8.2, GHC ==6.10.1 -extra-source-files:  tests/Unit.hs+extra-source-files:  examples/Enumerate.hs+                     tests/Test/Permute.hs+                     tests/Driver.hs+                     tests/Main.hs+                     tests/Pure.hs+                     tests/ST.hs                      tests/Makefile  library-    exposed-modules: Data.Permutation+    hs-source-dirs:  lib+    exposed-modules: Data.Permute+                     Data.Permute.MPermute+                     Data.Permute.IO+                     Data.Permute.ST+                     +    other-modules:   Data.IntArray+                     Data.Permute.Base+                     Data.Permute.IOBase -    build-depends:   base, containers+    build-depends:   base+    extensions:      MultiParamTypeClasses, FunctionalDependencies, +                     FlexibleContexts, Rank2Types, MagicHash, UnboxedTuples      ghc-options:     -Wall++    if impl(ghc >= 6.9)+      build-depends: ghc-prim
+ tests/Driver.hs view
@@ -0,0 +1,176 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Driver+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--++module Driver (+    Natural(..),+    Index(..),+    ListPermute(..),+    SwapsPermute(..),+    Sort(..),+    SortBy(..),+    Swap(..),+    +    mytest,+    mycheck,+    mytests,+    done,++    ) where++import Control.Monad+import Data.List+import Data.Ord+import System.IO+import System.Random+import Test.QuickCheck+import Text.Printf+import Text.Show.Functions+++newtype Natural = Nat Int deriving (Eq,Show)+instance Arbitrary Natural where+    arbitrary = do+        n <- arbitrary+        return $ Nat (abs n)+    +    coarbitrary = undefined++data Index = Index Int Int deriving (Eq,Show)+instance Arbitrary Index where+    arbitrary = do+        (Nat n) <- arbitrary+        i <- choose (0, n)+        return $ Index (n + 1) i++    coarbitrary = undefined++data ListPermute = ListPermute Int [Int] deriving (Eq,Show)+instance Arbitrary ListPermute where+    arbitrary = do+        (Nat n) <- arbitrary+        xs <- vector n :: Gen [Int]+        return . ListPermute n $ +            (snd . unzip) $ sortBy (comparing fst) $ zip xs [0..]++    coarbitrary = undefined++data SwapsPermute = SwapsPermute Int [(Int,Int)] deriving (Eq,Show)+instance Arbitrary SwapsPermute where+    arbitrary = do+        (Nat n) <- arbitrary+        let n' = n + 1+        (Nat k) <- arbitrary+        ss <- replicateM k (swap n')+        return $ SwapsPermute n' ss++    coarbitrary = undefined++swap n = do+    i <- choose (0,n-1)+    j <- choose (0,n-1)+    return (i,j)+++data Swap = Swap Int Int Int deriving (Eq,Show)+instance Arbitrary Swap where+    arbitrary = do+        (Index n i) <- arbitrary+        j <- choose (0,n-1)+        return $ Swap n i j++    coarbitrary = undefined++instance Arbitrary Ordering where+    arbitrary   = elements [ LT, GT, EQ ]+    coarbitrary = coarbitrary . fromEnum++data Sort = Sort Int [Int] deriving (Eq,Show)+instance Arbitrary Sort where+    arbitrary = do+        (Index n i) <- arbitrary+        xs <- vector n+        return $ Sort i xs+        +    coarbitrary = undefined++data SortBy = SortBy (Int -> Int -> Ordering) Int [Int] deriving (Show)+instance Arbitrary SortBy where+    arbitrary = do+        cmp <- arbitrary+        (Sort n xs) <- arbitrary+        return $ SortBy cmp n xs+    +    coarbitrary = undefined+++------------------------------------------------------------------------+--+-- QC driver ( taken from xmonad-0.6 )+--++debug = False++mytest :: Testable a => a -> Int -> IO (Bool, Int)+mytest a n = mycheck defaultConfig+    { configMaxTest=n+    , configEvery   = \n args -> let s = show n in s ++ [ '\b' | _ <- s ] } a+ -- , configEvery= \n args -> if debug then show n ++ ":\n" ++ unlines args else [] } a++mycheck :: Testable a => Config -> a -> IO (Bool, Int)+mycheck config a = do+    rnd <- newStdGen+    mytests config (evaluate a) rnd 0 0 []++mytests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO (Bool, Int)+mytests config gen rnd0 ntest nfail stamps+    | ntest == configMaxTest config = done "OK," ntest stamps >> return (True, ntest)+    | nfail == configMaxFail config = done "Arguments exhausted after" ntest stamps >> return (True, ntest)+    | otherwise               =+      do putStr (configEvery config ntest (arguments result)) >> hFlush stdout+         case ok result of+           Nothing    ->+             mytests config gen rnd1 ntest (nfail+1) stamps+           Just True  ->+             mytests config gen rnd1 (ntest+1) nfail (stamp result:stamps)+           Just False ->+             putStr ( "Falsifiable after "+                   ++ show ntest+                   ++ " tests:\n"+                   ++ unlines (arguments result)+                    ) >> hFlush stdout >> return (False, ntest)+     where+      result      = generate (configSize config ntest) rnd2 gen+      (rnd1,rnd2) = split rnd0++done :: String -> Int -> [[String]] -> IO ()+done mesg ntest stamps = putStr ( mesg ++ " " ++ show ntest ++ " tests" ++ table )+  where+    table = display+            . map entry+            . reverse+            . sort+            . map pairLength+            . group+            . sort+            . filter (not . null)+            $ stamps++    display []  = ".\n"+    display [x] = " (" ++ x ++ ").\n"+    display xs  = ".\n" ++ unlines (map (++ ".") xs)++    pairLength xss@(xs:_) = (length xss, xs)+    entry (n, xs)         = percentage n ntest+                       ++ " "+                       ++ concat (intersperse ", " xs)++    percentage n m        = show ((100 * n) `div` m) ++ "%"++------------------------------------------------------------------------+
+ tests/Main.hs view
@@ -0,0 +1,31 @@++import Control.Monad+import System.Environment+import Text.Printf++import Driver+import Pure+import ST++main :: IO ()+main = do+    args <- getArgs+    let n = if null args then 100 else read (head args)+    +    (results, passed) <- liftM unzip $+        foldM ( \prev (name,subtests) -> do+                     printf "\n%s\n" name+                     printf "%s\n" $ replicate (length name) '-'+                     cur <- mapM (\(s,a) -> printf "%-30s: " s >> a n) subtests+                     return (prev ++ cur)+              )+              []+              tests+               +    printf "\nPassed %d tests!\n\n" (sum passed)+    when (not . and $ results) $ fail "\nNot all tests passed!"+ where++    tests = [ ("Permute"   , tests_Permute)+            , ("STPermute" , tests_STPermute)+            ]
tests/Makefile view
@@ -1,8 +1,16 @@ all:-	ghc -O -i. -i.. Unit.hs --make-	./Unit+	ghc -O -i. -i../lib Main.hs --make -o test-permute+	./test-permute +hpc:+	ghc -fforce-recomp -i. -i../lib -fhpc --make Main.hs -o test-permute+	rm -f test-permute.tix+	./test-permute+	hpc markup test-permute+ clean:-	rm -f *~ Unit *.hi *.o \-		../Data/Permutation.hi ../Data/Permutation.o \-		../System/Random/Permutation.o+	find ../lib . -name '*.hi' | xargs rm -f+	find ../lib . -name '*.o'  | xargs rm -f+	find . -name '*.html' | xargs rm -f+	rm -f test-permute test-permute.tix+	
+ tests/Pure.hs view
@@ -0,0 +1,173 @@+{-# OPTIONS_GHC -fglasgow-exts #-}+module Pure (+    tests_Permute+    ) where+    +import Control.Monad.ST+import Data.Array.ST+import Data.List( foldl' )+import qualified Data.List as List+import Data.Maybe( fromJust )++import Data.Permute++import Driver+import Test.QuickCheck+    +import Test.Permute()+import qualified Test.Permute as Test+++prop_size_permute (Nat n) =+    size (permute n) == n+prop_elems_permute (Nat n) =+    elems (permute n) == [0..(n-1)]++prop_size_listPermute (ListPermute n is) =+    size (listPermute n is) == n+prop_elems_listPermute (ListPermute n is) =+    elems (listPermute n is) == is++prop_size_swapsPermute (SwapsPermute n ss) =+    size (swapsPermute n ss) == n+prop_elems_swapsPermute (SwapsPermute n ss) =+    elems (swapsPermute n ss) == map at [0..(n-1)]+  where+    at i = foldl' doSwap i $ reverse ss+    doSwap k (i,j) | k == i    = j+                   | k == j    = i+                   | otherwise = k++prop_apply       = prop_apply_help apply+prop_unsafeApply = prop_apply_help unsafeApply+prop_apply_help a =+    forAll arbitrary $ \(Index n i) ->+    forAll (Test.permute n) $ \p ->+        a p i == (elems p) !! i++prop_size_inverse (p :: Permute) =+    size (inverse p) == size p+prop_elems_inverse (p :: Permute) =+    all (\i -> is' !! (apply p i) == i) [0..(n-1)]+  where+    n   = size p+    is' = elems (inverse p)++prop_swaps (Nat n) =+    forAll (Test.permute n) $ \p ->+    forAll (vector n) $ \xs ->+        let xs' = applySwaps (swaps p) xs+        in all (\i -> xs' !! i == xs !! (apply p i)) [0..(n-1)]++prop_invSwaps (Nat n) =+    forAll (Test.permute n) $ \p ->+    forAll (vector n) $ \xs ->+        let xs' = applySwaps (invSwaps p) xs+        in all (\i -> xs' !! (apply p i) == xs !! i) [0..(n-1)]++prop_swaps_inverse (Nat n) =+    forAll (Test.permute n) $ \p ->+    forAll (vector n) $ \xs ->+        applySwaps (swaps $ inverse p) xs == (applySwaps (invSwaps p) xs)+    +prop_invSwaps_inverse (Nat n) =+    forAll (Test.permute n) $ \p ->+    forAll (vector n) $ \xs ->+        applySwaps (invSwaps $ inverse p) xs == (applySwaps (swaps p) xs)++prop_prev_permute (Nat n) =+    prev (permute n) == Nothing+prop_next_last (Nat n) =+    next (listPermute n $ reverse [0..(n-1)]) == Nothing++prop_next_prev (p :: Permute) =+    case prev p of+        Just p' -> p == (fromJust $ next p')+        Nothing -> p == permute n+  where+    n = size p+    +prop_prev_next (p :: Permute) =+    case next p of+        Just p' -> p == (fromJust $ prev p')+        Nothing -> p == (listPermute n $ reverse [0..(n-1)])+  where+    n = size p++prop_fst_sort (Sort n xs) = let+    ys = take n xs+    in (fst . sort n) xs == (List.sort ys)+prop_snd_sort (Sort n xs) = let+    ys = take n xs+    in applySwaps (swaps $ snd $ sort n xs) ys == (List.sort ys)++prop_fst_sortBy (SortBy cmp n xs) = let +    ys = take n xs+    in (fst . sortBy cmp n) xs == (List.sortBy cmp ys)+prop_snd_sortBy (SortBy cmp n xs) = let +    ys = take n xs+    in applySwaps (swaps $ snd $ sortBy cmp n xs) ys == (List.sortBy cmp ys)++prop_order (Sort n xs) = let +    ys = take n xs+    in applySwaps (swaps $ order n xs) ys == (List.sort ys)++prop_orderBy (SortBy cmp n xs) = let +    ys = take n xs+    in applySwaps (swaps $ orderBy cmp n xs) ys == (List.sortBy cmp ys)++prop_rank (Sort n xs) = let+    ys = take n xs+    in applySwaps (invSwaps $ rank n xs) ys == (List.sort ys)++prop_rankBy (SortBy cmp n xs) = let+    ys = take n xs+    in applySwaps (invSwaps $ rankBy cmp n xs) ys == (List.sortBy cmp ys)++prop_swapsPermute_swaps (p :: Permute) =+    swapsPermute (size p) (swaps p) == p+++tests_Permute = +    [ ("size . permute"          , mytest prop_size_permute)+    , ("elems . permute"         , mytest prop_elems_permute)+    , ("size . listPermute"      , mytest prop_size_listPermute)+    , ("elems . listPermute"     , mytest prop_elems_listPermute)+    , ("size . swapsPermute"     , mytest prop_size_swapsPermute)+    , ("elems . swapsPermute"    , mytest prop_elems_swapsPermute)+    , ("apply"                   , mytest prop_apply)+    , ("unsafeApply"             , mytest prop_unsafeApply)+    , ("size . inverse"          , mytest prop_size_inverse)+    , ("elems . inverse"         , mytest prop_elems_inverse)+    , ("swaps"                   , mytest prop_swaps)+    , ("invSwaps"                , mytest prop_invSwaps)+    , ("swaps . inverse"         , mytest prop_swaps_inverse)+    , ("invSwaps . inverse"      , mytest prop_invSwaps_inverse)+    , ("prev . permute"          , mytest prop_prev_permute)+    , ("next (last permutation)" , mytest prop_next_last)+    , ("next . prev"             , mytest prop_next_prev)+    , ("prev . next"             , mytest prop_prev_next)+    , ("fst . sort"              , mytest prop_fst_sort)+    , ("snd . sort"              , mytest prop_snd_sort)+    , ("fst . sortBy"            , mytest prop_fst_sortBy)+    , ("snd . sortBy"            , mytest prop_snd_sortBy)+    , ("order"                   , mytest prop_order)+    , ("orderBy"                 , mytest prop_orderBy)+    , ("rank"                    , mytest prop_rank)+    , ("rankBy"                  , mytest prop_rankBy)+    , ("swapsPermute . swaps"    , mytest prop_swapsPermute_swaps)+    ]+++applySwaps :: [(Int,Int)] -> [Int] -> [Int]+applySwaps ss xs = runST $ do+    arr <- newListArray (0,length xs - 1) xs :: ST s (STUArray s Int Int) +    mapM_ (swap arr) ss+    getElems arr+  where+    swap arr (i,j) = do+        i' <- readArray arr i+        j' <- readArray arr j+        writeArray arr j i'+        writeArray arr i j'+
+ tests/ST.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE Rank2Types #-}+module ST (+    tests_STPermute+    ) where+    +import Control.Monad+import Control.Monad.ST++import Data.Permute+import Data.Permute.ST+++import Driver+import Debug.Trace+import Test.QuickCheck+import Text.Printf+    +import Test.Permute()+import qualified Test.Permute as Test++newPermute_S n = permute n+prop_NewPermute (Nat n) = +    newPermute n `equivalent` newPermute_S n++newListPermute_S n is = listPermute n is+prop_NewListPermute (ListPermute n is) =+    newListPermute n is `equivalent` newListPermute_S n is++newSwapsPermute_S n ss = swapsPermute n ss+prop_NewSwapsPermute (SwapsPermute n ss) =+    newSwapsPermute n ss `equivalent` newSwapsPermute_S n ss+prop_UnsafeNewSwapsPermute (SwapsPermute n ss) =+    unsafeNewSwapsPermute n ss `equivalent` newSwapsPermute_S n ss++++newCopyPermute_S p = (p, p)+prop_NewCopyPermute =+    implements+        (\p -> newCopyPermute p >>= unsafeFreeze)+        (\p -> newCopyPermute_S p)++copyPermute_S p q = ((), q, q)+prop_CopyPermute =+    copyPermute `implements2` copyPermute_S++setIdentity_S p = ((), permute (size p))+prop_SetIdentity =+    setIdentity `implements` setIdentity_S ++getElem_S p i = ((elems p) !! i, p)+prop_GetElem (Index n i) =+    implementsFor n+        (\p -> getElem   p i)+        (\p -> getElem_S p i)++prop_UnsafeGetElem (Index n i) =+    implementsFor n+        (\p -> unsafeGetElem p i)+        (\p -> getElem_S p i)++swapElems_S p i j = ((), p')+  where+    (n,is) = (size p, elems p)+      +    at k | k == i    = is !! j+         | k == j    = is !! i+         | otherwise = is !! k+    p'   = listPermute n $ map at [0..(n-1)]++prop_SwapElems (Swap n i j) =+    implementsFor n+        (\p -> swapElems p i j) +        (\p -> swapElems_S p i j)++prop_UnsafeSwapElems (Swap n i j) =+    implementsFor n+        (\p -> unsafeSwapElems p i j) +        (\p -> swapElems_S p i j)+++getSize_S p = (length (elems p), p)+prop_GetSize = getSize `implements` getSize_S+      +getElems_S p = (elems p, p)+prop_GetElems = getElems `implements` getElems_S+++++tests_STPermute = +    [ ("newPermute"               , mytest prop_NewPermute)+    , ("newListPermute"           , mytest prop_NewListPermute)+    , ("newSwapsPermute"          , mytest prop_NewSwapsPermute)+    , ("unsafeNewSwapsPermute"    , mytest prop_UnsafeNewSwapsPermute)+    , ("newCopyPermute"           , mytest prop_NewCopyPermute)+    , ("copyPermute"              , mytest prop_CopyPermute)+    , ("setIdentity"              , mytest prop_SetIdentity)+    , ("getElem"                  , mytest prop_GetElem)+    , ("unsafeGetElem"            , mytest prop_UnsafeGetElem)+    , ("swapElems"                , mytest prop_SwapElems)+    , ("unsafeSwapElems"          , mytest prop_UnsafeSwapElems)+    , ("getSize"                  , mytest prop_GetSize)+    , ("getElems"                 , mytest prop_GetElems)+    ]+++------------------------------------------------------------------------+-- +-- The specification language+--+    +abstract :: STPermute s -> ST s Permute+abstract = freeze++commutes :: (Eq a, Show a) =>+    STPermute s -> (STPermute s -> ST s a) ->+        (Permute -> (a,Permute)) -> ST s Bool+commutes p a f = do+    old <- abstract p+    r   <- a p+    new <- abstract p+    let s      = f old+        s'     = (r,new)+        passed = s == s'+        +    when (not passed) $+        trace (printf ("expected `%s' but got `%s'") (show s) (show s'))+              return ()+              +    return passed++equivalent :: (forall s . ST s (STPermute s)) -> Permute -> Bool+equivalent p s = runST $ do+    p' <- (p >>= abstract)+    when (not $ p' == s) $+        trace (printf ("expected `%s' but got `%s'") (show s) (show p'))+            return ()+    return (p' == s)+    +implements :: (Eq a, Show a) =>+    (forall s . STPermute s -> ST s a) ->+    (Permute -> (a,Permute)) -> +        Property+a `implements` f =+    forAll arbitrary $ \(Nat n) ->+        implementsFor n a f++implementsFor :: (Eq a, Show a) =>+    Int ->+    (forall s . STPermute s -> ST s a) ->+    (Permute -> (a,Permute)) -> +        Property+implementsFor n a f =+    forAll (Test.permute n) $ \p ->+        runST $ do+            p' <- unsafeThaw p+            commutes p' a f++implementsIf :: (Eq a, Show a) =>+    (forall s . STPermute s -> ST s Bool) ->+    (forall s . STPermute s -> ST s a) ->+    (Permute -> (a, Permute)) -> +        Property+implementsIf pre a f =+    forAll arbitrary $ \p ->+        runST ( do+            p' <- thaw p+            pre p') ==>+        runST ( do+            p' <- unsafeThaw p+            commutes p' a f )+++commutes2 :: (Eq a, Show a) =>+    STPermute s -> STPermute s -> (STPermute s -> STPermute s -> ST s a) ->+        (Permute -> Permute -> (a,Permute,Permute)) -> ST s Bool+commutes2 p q a f = do+    oldp <- abstract p+    oldq <- abstract q+    r   <- a p q+    newp <- abstract p+    newq <- abstract q+    let s      = f oldp oldq+        s'     = (r,newp,newq)+        passed = s == s'+        +    when (not passed) $+        trace (printf ("expected `%s' but got `%s'") (show s) (show s'))+              return ()+              +    return passed+++implements2 :: (Eq a, Show a) =>+    (forall s . STPermute s -> STPermute s -> ST s a) ->+    (Permute -> Permute -> (a,Permute,Permute)) -> +        Property+implements2 a f =+    forAll arbitrary $ \(Nat n) ->+    forAll (Test.permute n) $ \p ->+    forAll (Test.permute n) $ \q ->+        runST $ do+            p' <- unsafeThaw p+            q' <- unsafeThaw q+            commutes2 p' q' a f+
+ tests/Test/Permute.hs view
@@ -0,0 +1,40 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+-----------------------------------------------------------------------------+-- |+-- Module     : Test.Permute+-- Copyright  : Copyright (c) , Patrick Perry <patperry@stanford.edu>+-- License    : BSD3+-- Maintainer : Patrick Perry <patperry@stanford.edu>+-- Stability  : experimental+--+-- An 'Arbitrary' instance and functions for generating random permutations.+--++module Test.Permute (+    permute+    ) where++import Data.List ( sortBy )+import Data.Ord ( comparing )++import Test.QuickCheck++import Data.Permute ( Permute )+import qualified Data.Permute as P++-- | Generate a random permutation of the given size.+permute :: Int -> Gen Permute+permute n = do+    xs <- vector n :: Gen [Int]+    let is = (snd . unzip) $ sortBy (comparing fst) $ zip xs [0..]+    return $ P.listPermute n is+++instance Arbitrary Permute where+    arbitrary = do+        n <- arbitrary >>= return . abs+        p <- permute n+        return $ p+        +    coarbitrary p =+        coarbitrary $ P.elems p
− tests/Unit.hs
@@ -1,214 +0,0 @@--import Control.Monad ( forM_ )-import Data.List ( sortBy )-import Data.Ord  ( comparing )-import Foreign-import System.Random-import Test.HUnit--import Data.Permutation----swapElems :: Storable a => Ptr a -> Int -> Int -> IO ()-swapElems ptr i j =  do-    x <- peekElemOff ptr i-    y <- peekElemOff ptr j-    pokeElemOff ptr i y-    pokeElemOff ptr j x--newRandomArray :: (Random a, Storable a) => Int -> IO (Ptr a)-newRandomArray n = do-    xs <- sequence $ replicate n randomIO-    newArray xs--newArrayCopy :: Storable a => Int -> Ptr a -> IO (Ptr a)-newArrayCopy n x = do-    y <- mallocArray n-    copyArray y x n-    return y--newRandomPerm :: Int -> IO (Permutation)-newRandomPerm n = do-    xs <- sequence $ replicate n randomIO :: IO [Int]-    let ixs  = zip [0..] xs-        ixs' = sortBy (comparing snd) ixs-        is   = (fst . unzip) ixs'-        p    = permutation n is-    return p-    --testIdentity :: Test-testIdentity = TestCase $ do-    let n = 30-        p = identity n-    -    x <- newRandomArray n :: IO (Ptr Double)-    y <- newArrayCopy n x-    applyWith (swapElems y) p-    -    xs <- peekArray n x-    ys <- peekArray n y-    -    assertEqual ""-        xs-        ys-        -        -testSmallCycle :: Test-testSmallCycle = TestCase $ do-    let n = 3-        p = permutation 3  [ 2, 0, 1 ]--    x <- newArray [ 6, 7, 8 ] :: IO (Ptr Int)-    applyWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ 8, 6, 7 ]        -        xs--testSmallCycleInv :: Test-testSmallCycleInv = TestCase $ do-    let n = 3-        p = permutation 3  [ 2, 0, 1 ]--    x <- newArray [ 6, 7, 8 ] :: IO (Ptr Int)-    invertWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ 7, 8, 6 ]-        xs---testSingleCycleInv :: Test-testSingleCycleInv = TestCase $ do-    let n = 5-        p = permutation n [ 3, 0, 1, 4, 2 ]-        -    x <- newArray [ 0.237,  0.382,  0.818, -0.413,  0.037 ] :: IO (Ptr Double)-    invertWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ 0.382, 0.818, 0.037, 0.237, -0.413 ]-        xs--testSingleCycle :: Test-testSingleCycle = TestCase $ do-    let n = 5-        p = permutation n [ 3, 0, 1, 4, 2 ]--    x <- newArray [ 0.237,  0.382,  0.818, -0.413,  0.037 ] :: IO (Ptr Double)-    applyWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ -0.413, 0.237, 0.382, 0.037, 0.818 ]-        xs-        -testMultiCycleInv :: Test-testMultiCycleInv = TestCase $ do-    let n = 5-        p = permutation n [ 2, 3, 0, 4, 1 ]-        -    x <- newArray [ 0.237,  0.382,  0.818, -0.413,  0.037 ] :: IO (Ptr Double)-    invertWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ 0.818, 0.037, 0.237, 0.382, -0.413 ]-        xs--testMultiCycle :: Test-testMultiCycle = TestCase $ do-    let n = 5-        p = permutation n [ 2, 3, 0, 4, 1 ]-    x <- newArray [ 0.237,  0.382,  0.818, -0.413,  0.037 ] :: IO (Ptr Double)-    applyWith (swapElems x) p-    -    xs <- peekArray n x-    assertEqual ""-        [ 0.818, -0.413, 0.237, 0.037, 0.382 ]-        xs--testApply :: Int -> Test-testApply n = TestCase $ do-    x <- newRandomArray n :: IO (Ptr Double)-    y <- newArrayCopy n x-    p <- newRandomPerm n-    applyWith  (swapElems y) p-    -    xs <- peekArray n x-    ys <- peekArray n y--    forM_ [0..(n-1)] $ \i ->-        assertEqual ("position " ++ show i) (xs !! (apply p i)) (ys !! i)--testInvert :: Int -> Test-testInvert n = TestCase $ do-    x <- newRandomArray n :: IO (Ptr Double)-    y <- newArrayCopy n x-    p <- newRandomPerm n-    invertWith  (swapElems y) p-    -    xs <- peekArray n x-    ys <- peekArray n y--    forM_ [0..(n-1)] $ \i ->-        assertEqual ("position " ++ show i) (xs !! i) (ys !! (apply p i))---testApplyThenInv :: Int -> Test-testApplyThenInv n = TestCase $ do-    x <- newRandomArray n :: IO (Ptr Double)-    y <- newArrayCopy n x-    p <- newRandomPerm n-    applyWith  (swapElems y) p-    invertWith (swapElems y) p-    -    xs <- peekArray n x-    ys <- peekArray n y-    -    assertEqual "" xs ys--testInvThenApply :: Int -> Test-testInvThenApply n = TestCase $ do-    x <- newRandomArray n :: IO (Ptr Double)-    y <- newArrayCopy n x-    p <- newRandomPerm n-    invertWith (swapElems y) p-    applyWith  (swapElems y) p-    -    xs <- peekArray n x-    ys <- peekArray n y-    -    assertEqual "" xs ys---- -smallTests = [ TestLabel "identity"             testIdentity-             , TestLabel "small cycle"          testSmallCycle-             , TestLabel "small cycle inverse"  testSmallCycleInv-             , TestLabel "single cycle"         testSingleCycle-             , TestLabel "single cycle inverse" testSingleCycleInv-             , TestLabel "multi cycle"          testMultiCycle-             , TestLabel "multi cycle inverse"  testMultiCycleInv-             ]-- -main =-    let ns      = [ 0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 ]-        aTests  = map (\n -> TestLabel ("apply (" ++ show n ++ ")") -                                       (testApply n)) ns-        iTests  = map (\n -> TestLabel ("apply (" ++ show n ++ ")") -                                       (testInvert n)) ns-        aiTests = map (\n -> TestLabel ("apply then inverse (" ++ show n ++ ")") -                                       (testApplyThenInv n)) ns-        iaTests = map (\n -> TestLabel ("inverse then apply (" ++ show n ++ ")") -                                       (testInvThenApply n)) ns-        tests   = TestList $ smallTests ++ aTests ++ aiTests ++ iaTests-    in runTestTT tests-