diff --git a/Sym/Internal/CLongArray.hs b/Sym/Internal/CLongArray.hs
--- a/Sym/Internal/CLongArray.hs
+++ b/Sym/Internal/CLongArray.hs
@@ -1,7 +1,5 @@
-{-# LANGUAGE MagicHash, UnboxedTuples #-}
-
 -- |
--- Copyright   : Anders Claesson 2013
+-- Copyright   : Anders Claesson 2013-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 -- Convenience functions for dealing with arrays of 'CLong's.
@@ -14,8 +12,7 @@
     -- * Conversions
     , fromList
     , toList
-    , slice
-    , unsafeSlice
+    , slices
 
     -- * Accessors
     , size
@@ -33,31 +30,20 @@
     ) where
 
 import Data.Ord
+import qualified Data.Vector.Storable as V
+import qualified Data.Vector.Storable.Mutable as MV
 import Sym.Internal.Size
 import Foreign
 import Foreign.C.Types
-import GHC.Base
 
 infixl 9 `at`
 infixl 9 `unsafeAt`
 
-inlinePerformIO :: IO a -> a
-inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
-{-# INLINE inlinePerformIO #-}
-
-
 -- Data type
 -- ---------
 
 -- | An array of 'CLong's
-data CLongArray = CArr {-# UNPACK #-} !(ForeignPtr CLong) -- elements
-                       {-# UNPACK #-} !Int                -- size
-
-instance Show CLongArray where
-    show w = "fromList " ++ show (toList w)
-
-instance Eq CLongArray where
-    u == v = toList u == toList v
+newtype CLongArray = CArr (V.Vector CLong) deriving (Show, Eq)
 
 instance Ord CLongArray where
     compare u v =
@@ -66,7 +52,7 @@
           x  -> x
 
 instance Size CLongArray where
-    size (CArr _ n) = n
+    size (CArr w) = V.length w
     {-# INLINE size #-}
 
 
@@ -75,34 +61,18 @@
 
 -- | Construct an array from a list of elements.
 fromList :: [Int] -> CLongArray
-fromList xs = CArr p (length xs)
-  where
-    p = inlinePerformIO $ newForeignPtr finalizerFree =<< newArray (map fromIntegral xs)
-{-# INLINE fromList #-}
+fromList = CArr . V.fromList . map fromIntegral
 
 -- | The list of elements.
 toList :: CLongArray -> [Int]
-toList w = map fromIntegral . inlinePerformIO . unsafeWith w $ peekArray (size w)
-{-# INLINE toList #-}
+toList (CArr w) = map fromIntegral $ V.toList w
 
 -- | Slice a 'CLongArray' into contiguous segments of the given
 -- sizes. Each segment size must be positive and they must sum to the
 -- size of the array.
-slice :: [Int] -> CLongArray -> [CLongArray]
-slice ks w
-    | any (<=0) ks     = error "Sym.Internal.CLongArray.slice: zero or negative parts"
-    | sum ks /= size w = error "Sym.Internal.CLongArray.slice: parts doesn't sum to size of array"
-    | otherwise        = unsafeSlice ks w
-
--- | Like 'slice' but without range checking.
-unsafeSlice :: [Int] -> CLongArray -> [CLongArray]
-unsafeSlice parts w = inlinePerformIO . unsafeWith w $ go parts
-  where
-    go []     _ = return []
-    go (k:ks) p = do
-      vs <- go ks (advancePtr p k)
-      v  <- unsafeNew k $ \q -> copyArray q p k
-      return (v:vs)
+slices :: [Int] -> CLongArray -> [CLongArray]
+slices [] _ = []
+slices (k:ks) (CArr w) = let (u,v) = V.splitAt k w in CArr u : slices ks (CArr v)
 
 
 -- Accessors
@@ -110,29 +80,19 @@
 
 -- | @w \`at\` i@ is the value of @w@ at @i@, where @i@ is in @[0..size w-1]@.
 at :: CLongArray -> Int -> Int
-at w i =
-    let n = size w
-    in if i < 0 || i >= n
-       then error $ "Sym.Internal.CLongArray.at: " ++ show i ++ " not in [0.." ++ show (n-1) ++ "]"
-       else unsafeAt w i
-{-# INLINE at #-}
+at (CArr w) i =
+    case (V.!?) w i of
+      Nothing -> error "Sym.Internal.CLongArray.at: out of range"
+      Just j  -> fromIntegral j
 
 -- | Like 'at' but without range checking.
 unsafeAt :: CLongArray -> Int -> Int
-unsafeAt w = fromIntegral . inlinePerformIO . unsafeWith w . flip peekElemOff
-{-# INLINE unsafeAt #-}
+unsafeAt (CArr w) = fromIntegral . (V.!) w
 
 -- | The indices of all elements equal to the query element, in
 -- ascending order.
-elemIndices :: CLong -> CLongArray -> [Int]
-elemIndices x w = inlinePerformIO $ unsafeWith w (go 0)
-  where
-    n = size w
-    go i p
-      | i >= n = return []
-      | otherwise = do
-          y <- peek p
-          ([ i | y == x ] ++) `fmap` go (i+1) (advancePtr p 1)
+elemIndices :: CLong -> CLongArray -> V.Vector Int
+elemIndices x (CArr w) = V.elemIndices x w
 
 
 -- Map and Zip
@@ -140,29 +100,11 @@
 
 -- | Apply a function to every element of an array and its index.
 imap :: (Int -> CLong -> CLong) -> CLongArray -> CLongArray
-imap f w = inlinePerformIO . unsafeWith w $ \p -> unsafeNew n (go 0 p)
-  where
-    n = size w
-    go i p q
-      | i >= n = return ()
-      | otherwise = do
-          x <- peek p
-          poke q (f i x)
-          go (i+1) (advancePtr p 1) (advancePtr q 1)
+imap f (CArr w) =  CArr (V.imap f w)
 
 -- | Apply a function to corresponding pairs of elements and their (shared) index.
 izipWith :: (Int -> CLong -> CLong -> CLong) -> CLongArray -> CLongArray -> CLongArray
-izipWith f u v =
-    inlinePerformIO . unsafeWith u $ \p -> unsafeWith v $ \q -> unsafeNew n (go 0 p q)
-  where
-    n = min (size u) (size v)
-    go i p q r
-      | i >= n = return ()
-      | otherwise = do
-          x <- peek p
-          y <- peek q
-          poke r (f i x y)
-          go (i+1) (advancePtr p 1) (advancePtr q 1) (advancePtr r 1)
+izipWith f (CArr u) (CArr v) = CArr (V.izipWith f u v)
 
 -- Low level functions
 -- -------------------
@@ -171,13 +113,12 @@
 -- an IO action.
 unsafeNew :: Int -> (Ptr CLong -> IO ()) -> IO CLongArray
 unsafeNew n act = do
-  q <- newForeignPtr finalizerFree =<< mallocArray n
-  withForeignPtr q act
-  return $ CArr q n
-{-# INLINE unsafeNew #-}
+    v <- V.unsafeFreeze =<< MV.unsafeNew n
+    let (ptr, _) = V.unsafeToForeignPtr0 v
+    withForeignPtr ptr act
+    return $ CArr (V.unsafeFromForeignPtr0 ptr n)
 
 -- | Pass a pointer to the array to an IO action; the array may not be
 -- modified through the pointer.
 unsafeWith :: CLongArray -> (Ptr CLong -> IO a) -> IO a
-unsafeWith (CArr p _) = withForeignPtr p
-{-# INLINE unsafeWith #-}
+unsafeWith (CArr w) = V.unsafeWith w
diff --git a/Sym/Internal/SubSeq.hs b/Sym/Internal/SubSeq.hs
--- a/Sym/Internal/SubSeq.hs
+++ b/Sym/Internal/SubSeq.hs
@@ -70,7 +70,7 @@
 
 -- | @onesCLong m@ gives the indices whose bits are set in @m@.
 onesCLong :: CLong -> CLongArray
-onesCLong m = unsafeDupablePerformIO . unsafeNew (popCount m) $ flip c_ones m
+onesCLong m = unsafePerformIO . unsafeNew (popCount m) $ flip c_ones m
 
 -- | Lexicographically, the next integral number with the same Hamming weight.
 nextIntegral :: (Integral a, Bits a) => a -> a
diff --git a/Sym/Perm.hs b/Sym/Perm.hs
--- a/Sym/Perm.hs
+++ b/Sym/Perm.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ForeignFunctionInterface, TypeSynonymInstances #-}
 
 -- |
--- Copyright   : Anders Claesson 2013
+-- Copyright   : Anders Claesson 2013-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 -- Generating permutations: rank and unrank
@@ -40,7 +40,7 @@
 
 -- | The identity permutation.
 idperm :: Int -> Perm
-idperm n = fromList [0..n-1]
+idperm n = fromList [0 .. n-1]
 
 -- | The reverse of the identity permutation.
 ebb :: Int -> Perm
@@ -52,7 +52,7 @@
 -- E.g., @mkPerm \"baxa\" == fromList [2,0,3,1]@.
 mkPerm :: Ord a => [a] -> Perm
 mkPerm xs =
-    let sti ys = map snd . sort $ zip ys [ 0::Int .. ]
+    let sti ys = map snd . sort $ zip ys [0::Int ..]
     in fromList $ (sti . sti) xs
 
 foreign import ccall unsafe "rank.h rank" c_rank
@@ -64,7 +64,7 @@
 rank :: Perm -> Integer
 rank w =
     let n = fromIntegral (size w)
-    in truncate . unsafeDupablePerformIO . unsafeWith w $ flip c_rank n
+    in truncate . unsafePerformIO . unsafeWith w $ flip c_rank n
 {-# INLINE rank #-}
 
 foreign import ccall unsafe "rank.h unrank" c_unrank
@@ -76,7 +76,7 @@
 -- (2001) 281-284].
 unrank :: Int -> Integer -> Perm
 unrank n r =
-    unsafeDupablePerformIO . unsafeNew n $ \ptr ->
+    unsafePerformIO . unsafeNew n $ \ptr ->
         c_unrank ptr (fromIntegral n) (fromIntegral r)
 {-# INLINE unrank #-}
 
diff --git a/Sym/Perm/Bijection.hs b/Sym/Perm/Bijection.hs
--- a/Sym/Perm/Bijection.hs
+++ b/Sym/Perm/Bijection.hs
@@ -24,7 +24,7 @@
 
 marshal :: (Ptr CLong -> Ptr CLong -> CLong -> IO ()) -> Perm -> Perm
 marshal bij w =
-    unsafeDupablePerformIO . unsafeWith w $ \p -> do
+    unsafePerformIO . unsafeWith w $ \p -> do
       let n = size w
       unsafeNew n $ \q -> bij q p (fromIntegral n)
 {-# INLINE marshal #-}
diff --git a/Sym/Perm/Component.hs b/Sym/Perm/Component.hs
--- a/Sym/Perm/Component.hs
+++ b/Sym/Perm/Component.hs
@@ -1,5 +1,5 @@
 -- |
--- Copyright   : Anders Claesson 2013
+-- Copyright   : Anders Claesson 2013-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 -- Components of permutations.
@@ -23,7 +23,7 @@
 -- Positions /i/ such that /max{ w[j] : j <= i } = i/. These positions
 -- mark the boundaries of components.
 comps :: Perm -> [Int]
-comps w = unsafeDupablePerformIO . unsafeWith w $ go [] 0 0
+comps w = unsafePerformIO . unsafeWith w $ go [] 0 0
     where
       n = size w
       go ks m i p
@@ -41,7 +41,7 @@
 components w =
     let ds = 0 : map (+1) (comps w)
         ks = zipWith (-) (tail ds) ds
-        ws = unsafeSlice ks w
+        ws = slices ks w
     in zipWith (\d v -> imap (\_ x -> x - fromIntegral d) v) ds ws
 
 -- | The list of skew components, also called minus components.
@@ -58,19 +58,19 @@
 -- | For each position, left-to-right, records the largest value seen
 -- thus far.
 leftMaxima :: Perm -> [Int]
-leftMaxima w = reverse $ records (<) (toList w)
+leftMaxima w = map fromIntegral . reverse $ records (<) (toList w)
 
 -- | For each position, left-to-right, records the smallest value seen
 -- thus far.
 leftMinima :: Perm -> [Int]
-leftMinima w = reverse $ records (>) (toList w)
+leftMinima w = map fromIntegral . reverse $ records (>) (toList w)
 
 -- | For each position, /right-to-left/, records the largest value seen
 -- thus far.
 rightMaxima :: Perm -> [Int]
-rightMaxima w = records (<) (reverse (toList w))
+rightMaxima w = map fromIntegral $ records (<) (reverse (toList w))
 
 -- | For each position, /right-to-left/, records the smallest value seen
 -- thus far.
 rightMinima :: Perm -> [Int]
-rightMinima w = records (>) (reverse (toList w))
+rightMinima w = map fromIntegral $ records (>) (reverse (toList w))
diff --git a/Sym/Perm/Constructions.hs b/Sym/Perm/Constructions.hs
--- a/Sym/Perm/Constructions.hs
+++ b/Sym/Perm/Constructions.hs
@@ -30,7 +30,7 @@
    let k  = size u
        l  = size v
        v' = imap (\_ x -> x + fromIntegral k) v
-   in unsafeDupablePerformIO . unsafeNew (k+l) $ \p ->
+   in unsafePerformIO . unsafeNew (k+l) $ \p ->
        let q = advancePtr p k
        in unsafeWith u  $ \uPtr ->
           unsafeWith v' $ \vPtr -> do
diff --git a/Sym/Perm/D8.hs b/Sym/Perm/D8.hs
--- a/Sym/Perm/D8.hs
+++ b/Sym/Perm/D8.hs
@@ -119,7 +119,7 @@
 
 marshal :: (Ptr CLong -> Ptr CLong -> CLong -> IO ()) -> Perm -> Perm
 marshal op w =
-    unsafeDupablePerformIO . unsafeWith w $ \p -> do
+    unsafePerformIO . unsafeWith w $ \p -> do
       let n = size w
       unsafeNew n $ \q -> op q p (fromIntegral n)
 {-# INLINE marshal #-}
diff --git a/Sym/Perm/Group.hs b/Sym/Perm/Group.hs
--- a/Sym/Perm/Group.hs
+++ b/Sym/Perm/Group.hs
@@ -19,7 +19,7 @@
 marshal :: (Ptr CLong -> Ptr CLong -> CLong -> Ptr CLong -> CLong -> IO ())
         -> Perm -> Perm -> Perm
 marshal op u v =
-    unsafeDupablePerformIO $
+    unsafePerformIO $
     unsafeWith u $ \u' ->
     unsafeWith v $ \v' -> do
       let k = size u
diff --git a/Sym/Perm/MeshPattern.hs b/Sym/Perm/MeshPattern.hs
--- a/Sym/Perm/MeshPattern.hs
+++ b/Sym/Perm/MeshPattern.hs
@@ -1,5 +1,7 @@
+{-# LANGUAGE DeriveGeneric #-}
+
 -- |
--- Copyright   : Anders Claesson 2014
+-- Copyright   : Anders Claesson 2014-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 
diff --git a/Sym/Perm/Pattern.hs b/Sym/Perm/Pattern.hs
--- a/Sym/Perm/Pattern.hs
+++ b/Sym/Perm/Pattern.hs
@@ -39,7 +39,7 @@
 ordiso :: Pattern -> Perm -> SubSeq -> Bool
 ordiso u v m =
     let k = fromIntegral (size u)
-    in unsafeDupablePerformIO $
+    in unsafePerformIO $
        unsafeWith u $ \u' ->
        unsafeWith v $ \v' ->
        unsafeWith m $ \m' ->
diff --git a/Sym/Perm/SSYT.hs b/Sym/Perm/SSYT.hs
--- a/Sym/Perm/SSYT.hs
+++ b/Sym/Perm/SSYT.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
 
 -- |
--- Copyright   : Anders Claesson 2013
+-- Copyright   : Anders Claesson 2013-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 -- Data types for Semistandard Young Tableaux (SSYT) and functions for
@@ -142,7 +142,7 @@
 
 -- | The Robinson-Schensted algorithm.
 fromPerm :: Perm -> SSYTPair
-fromPerm = fromGeneralizedPerm . zip [0..] . toList
+fromPerm = fromGeneralizedPerm . zip [0..] . map fromIntegral . toList
 
 -- | The inverse of the Robinson-Schensted-Knuth algorithm.
 toGeneralizedPerm :: SSYTPair -> GeneralizedPerm
@@ -153,4 +153,4 @@
 
 -- | The inverse of the Robinson-Schensted algorithm.
 toPerm :: SSYTPair -> Perm
-toPerm = fromList . map snd . toGeneralizedPerm
+toPerm = fromList . map (fromIntegral . snd) . toGeneralizedPerm
diff --git a/Sym/Perm/Simple.hs b/Sym/Perm/Simple.hs
--- a/Sym/Perm/Simple.hs
+++ b/Sym/Perm/Simple.hs
@@ -20,6 +20,6 @@
 
 -- | Is the permutation simple?
 simple :: Perm -> Bool
-simple w = toBool . unsafeDupablePerformIO $
+simple w = toBool . unsafePerformIO $
     let n = fromIntegral (size w)
     in unsafeWith w $ \ptr -> return $ c_simple ptr n
diff --git a/Sym/Perm/Sort.hs b/Sym/Perm/Sort.hs
--- a/Sym/Perm/Sort.hs
+++ b/Sym/Perm/Sort.hs
@@ -24,7 +24,7 @@
 
 marshal :: (Ptr CLong -> Ptr CLong -> CLong -> IO ()) -> Perm -> Perm
 marshal op w =
-    unsafeDupablePerformIO . unsafeWith w $ \p -> do
+    unsafePerformIO . unsafeWith w $ \p -> do
       let n = size w
       unsafeNew n $ \q -> op q p (fromIntegral n)
 {-# INLINE marshal #-}
diff --git a/Sym/Perm/Stat.hs b/Sym/Perm/Stat.hs
--- a/Sym/Perm/Stat.hs
+++ b/Sym/Perm/Stat.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
 
 -- |
--- Copyright   : Anders Claesson 2013
+-- Copyright   : Anders Claesson 2013-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 --
 -- Common permutation statistics. To avoid name clashes this module is
@@ -56,7 +56,7 @@
 
 marshal :: (Ptr CLong -> CLong -> CLong) -> Perm -> Int
 marshal f w =
-    fromIntegral . unsafeDupablePerformIO . unsafeWith w $ \p ->
+    fromIntegral . unsafePerformIO . unsafeWith w $ \p ->
         return $ f p (fromIntegral (size w))
 {-# INLINE marshal #-}
 
@@ -210,13 +210,13 @@
 -- | The first (left-most) element in the standardization. E.g.,
 -- @head \"231\" = head (fromList [1,2,0]) = 1@.
 head :: Perm -> Int
-head w | size w > 0 = w `unsafeAt` 0
+head w | size w > 0 = fromIntegral (w `unsafeAt` 0)
        | otherwise  = 0
 
 -- | The last (right-most) element in the standardization. E.g.,
 -- @last \"231\" = last (fromList [1,2,0]) = 0@.
 last :: Perm -> Int
-last w | size w > 0 = w `unsafeAt` (size w - 1)
+last w | size w > 0 = fromIntegral (w `unsafeAt` (size w - 1))
        | otherwise  = 0
 
 -- | Length of the left-most increasing run: largest @i@ such that
diff --git a/sym.cabal b/sym.cabal
--- a/sym.cabal
+++ b/sym.cabal
@@ -1,5 +1,5 @@
 name:                sym
-version:             0.11.1
+version:             0.12.0
 synopsis:            Permutations, patterns, and statistics
 description:
   Definitions for permutations with an emphasis on permutation
@@ -40,14 +40,13 @@
                        Sym.Internal.Size
                        Sym.Internal.Util
 
-  build-depends:       base >= 3 && <= 5,
+  build-depends:       base >=4.7 && <= 5,
+                       vector >=0.11,
                        array >=0.4,
                        hashable >=1.1,
-                       containers,
-                       QuickCheck >=2.5
+                       containers
 
-  ghc-prof-options:    -auto-all
-  ghc-options:         -Wall
+  ghc-options:         -Wall -O2
   cc-options:          -Wall
 
   c-sources:           cbits/rank.c
@@ -59,3 +58,12 @@
                        cbits/bit.c
                        cbits/simple.c
                        cbits/sortop.c
+
+Test-Suite Props
+  type:                exitcode-stdio-1.0
+  Main-is:             Properties.hs
+  hs-source-dirs:      tests
+  Build-depends:       base >=4.7 && <= 5,
+                       sym,
+                       hashable >=1.1,
+                       QuickCheck >=2.5
diff --git a/tests/Properties.hs b/tests/Properties.hs
new file mode 100644
--- /dev/null
+++ b/tests/Properties.hs
@@ -0,0 +1,504 @@
+{-# LANGUAGE TypeSynonymInstances #-}
+
+-- |
+-- Copyright   : Anders Claesson 2013-2016
+-- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
+--
+
+import qualified ListStat as LS
+import Sym.Perm.Meta hiding (choose)
+import qualified Sym.Perm.SSYT as Y
+import qualified Sym.Perm.Pattern as P
+import qualified Sym.Perm.Stat as S
+import qualified Sym.Perm.D8 as D8
+import Sym.Permgram (Permgram)
+import qualified Sym.Permgram as PG
+import Data.List
+import Data.Hashable
+import Control.Monad
+import Test.QuickCheck
+
+checkN :: Testable prop => Int -> prop -> IO Result
+checkN n = quickCheckWithResult stdArgs {maxSuccess = n}
+
+check :: Testable prop => prop -> IO ()
+check p = checkN 1000 p >> return ()
+
+--------------------------------------------------------------------------------
+-- Generators
+--------------------------------------------------------------------------------
+
+lenRank :: Gen (Int, Integer)
+lenRank = sized $ \m -> do
+            n <- choose (0, m) -- length
+            r <- choose (0, product [1..fromIntegral n] - 1) -- rank
+            return (n, r)
+
+instance Arbitrary Perm where
+    arbitrary = uncurry unrank `liftM` lenRank
+
+instance Arbitrary a => Arbitrary (Permgram a) where
+    arbitrary = do
+      (n, r) <- lenRank
+      xs <- vector n
+      return $ PG.permgram (unrank n r) xs
+
+newtype GPerm = GPerm [(Int, Int)] deriving (Eq, Show)
+
+instance Arbitrary GPerm where
+    arbitrary = sized $ \m -> do
+                  n  <- choose (0, m)
+                  xs <- vector n
+                  ys <- vector n
+                  return . GPerm . sort $ zip (map abs xs) (map abs ys)
+
+newtype PFun = PFun (Int -> Permgram Int)
+
+instance Show PFun where
+    show (PFun f) = "[" ++ images  ++ ", ..]"
+        where
+          images = intercalate ", " $ map (show . f) [0..2]
+
+instance Arbitrary PFun where
+    arbitrary = sized $ \m -> do
+      salt <- arbitrary
+      ws <- listOf1 arbitrary
+      return . PFun $ \x -> cycle ws !! (hashWithSalt salt x `mod` (m+1))
+
+--------------------------------------------------------------------------------
+-- Properties for Data.Perm
+--------------------------------------------------------------------------------
+
+prop_length xs = size (fromList xs) == length (xs :: [Int])
+
+prop_unrankRankId =
+    forAll (resize 18 arbitrary) $ \w ->
+        unrank (size w) (rank w) == w
+
+prop_rankUnrankId =
+    forAll (resize 18 lenRank) $ \(n,r) ->
+        rank (unrank n r) == r
+
+st :: Ord a => [a] -> [Int]
+st = sti . sti where sti w = map snd . sort $ zip w [0::Int ..]
+
+prop_mkPermToListSt xs = toList (mkPerm xs) == st (xs :: [Int])
+
+prop_unsafeAt w = map (w `unsafeAt`) [0 .. size w - 1] == toList w
+
+prop_perms = and [ sort (permutations [0..n-1]) == sort (map toList (perms n))
+                   | n <- [0..7]
+                 ]
+
+prop_coeff_des =
+    forAll (resize 7 arbitrary) $ \w ->
+        let n = size w
+        in n > 0 ==> fromEnum (w `at` 0 > w `at` (n-1))*(-1)^n == P.coeff S.des w
+
+prop_coeff_lmax =
+    forAll (resize 7 arbitrary) $ \w ->
+        let n = size w
+        in n > 0 ==> fromEnum (w `at` (n-1) == 0) * (-1)^(n-1) == P.coeff S.lmax w
+
+-- prop_monoid_mempty1 w = mempty <> w == (w :: StPerm)
+-- prop_monoid_mempty2 w = w <> mempty == (w :: StPerm)
+-- prop_monoid_associative u v w = u <> (v <> w) == (u <> v) <> (w :: StPerm)
+
+-- newtype S = S {unS :: StPerm} deriving (Eq, Show)
+
+-- instance Arbitrary S where
+--     arbitrary = liftM S arbitrary
+
+-- instance Monoid S where
+--     mempty = S $ Sym.fromVector SV.empty
+--     mappend u v = S $ (Sym.\-\) (unS u) (unS v)
+
+-- prop_monoid_mempty1_S w = mempty <> w == (w :: S)
+-- prop_monoid_mempty2_S w = w <> mempty == (w :: S)
+-- prop_monoid_associative_S u v w = u <> (v <> w) == (u <> v) <> (w :: S)
+
+testsDataPerm =
+    [ ("Data.Perm/length",                check prop_length)
+    , ("Data.Perm/unrankRankId",          check prop_unrankRankId)
+    , ("Data.Perm/rankUnrankId",          check prop_rankUnrankId)
+    , ("Data.Perm/mkPermToListSt",        check prop_mkPermToListSt)
+    , ("Data.Perm/unsafeAt",              check prop_unsafeAt)
+    , ("Data.Perm/perms",                 check prop_perms)
+    , ("Data.Perm/coeff/des",             check prop_coeff_des)
+    , ("Data.Perm/coeff/lmax",            check prop_coeff_lmax)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Data.Permgram
+--------------------------------------------------------------------------------
+
+prop_monadLeftId (PFun f) x = (return x >>= f) == f x
+
+prop_monadRightId w = (w >>= return) == (w :: Permgram Int)
+
+prop_monadAssociativity =
+    forAll (resize 7 arbitrary) $ \(PFun f) ->
+    forAll (resize 7 arbitrary) $ \(PFun g) ->
+    forAll (resize 9 arbitrary) $ \w ->
+        ((w >>= f) >>= g) == (w >>= (\x -> f x >>= g))
+
+testsDataPermgram =
+    [ ("Data.Permgram/Monad/LeftId",         check prop_monadLeftId)
+    , ("Data.Permgram/Monad/RightId",        check prop_monadRightId)
+    , ("Data.Permgram/Monad/Associativity",  check prop_monadAssociativity)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Data.SSYT
+--------------------------------------------------------------------------------
+
+swap (x,y) = (y,x)
+
+inverseGeneralizedPerm = sort . map swap
+
+prop_RSK (GPerm w) = w == Y.toGeneralizedPerm (Y.fromGeneralizedPerm w)
+
+prop_RSKInverse (GPerm w) = (p, q) == (s, r)
+    where
+      Y.SSYTPair p q = Y.fromGeneralizedPerm w
+      Y.SSYTPair r s = Y.fromGeneralizedPerm $ inverseGeneralizedPerm w
+
+prop_RS w = w == Y.toPerm (Y.fromPerm w)
+
+prop_RSInverse w = (p, q) == (s, r)
+    where
+      Y.SSYTPair p q = Y.fromPerm w
+      Y.SSYTPair r s = Y.fromPerm $ D8.inverse w
+
+prop_RSfp w = S.fp v == length (filter odd (Y.shape (transpose q)))
+    where
+      Y.SSYTPair p _ = Y.fromPerm w
+      v = Y.toPerm $ Y.SSYTPair p p -- Kludge to get an involution
+      Y.SSYTPair _ q = Y.fromPerm v
+
+testsDataSSYT =
+    [ ("Data.SSYT/RSK/Bijection",    check prop_RSK)
+    , ("Data.SSYT/RSK/Inverse",      check prop_RSKInverse)
+    , ("Data.SSYT/RS/Bijection",     check prop_RS)
+    , ("Data.SSYT/RS/Inverse",       check prop_RSInverse)
+    , ("Data.SSYT/RS/fp",            check prop_RSfp)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm
+--------------------------------------------------------------------------------
+
+segments :: [a] -> [[a]]
+segments [] = [[]]
+segments (x:xs) = segments xs ++ map (x:) (inits xs)
+
+nonEmptySegments :: [a] -> [[a]]
+nonEmptySegments = drop 1 . segments
+
+properSegments :: [a] -> [[a]]
+properSegments xs = [ ys | ys@(_:_:_) <- init $ segments xs ]
+
+properIntervals :: Ord a => [a] -> [[a]]
+properIntervals xs = [ ys | ys <- yss, sort ys `elem` zss ]
+    where
+      yss = properSegments xs
+      zss = properSegments $ sort xs
+
+isSimple :: Ord a => [a] -> Bool
+isSimple = null . properIntervals
+
+prop_simple =
+    forAll (resize 20 arbitrary) $ \w ->
+        simple w == isSimple (toList w)
+
+testsMathPerm =
+    [ ("Math.Perm/simple",    check prop_simple)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.Pattern
+--------------------------------------------------------------------------------
+
+prop_copiesSelf v = P.copiesOf v v == [fromList [0 .. size v - 1]]
+
+prop_avoidersAvoid =
+    forAll (resize 20 arbitrary) $ \ws ->
+    forAll (resize  6 arbitrary) $ \ps ->
+        all (`P.avoidsAll` ps) $ P.avoiders ps ws
+
+prop_avoidersIdempotent =
+    forAll (resize 18 arbitrary) $ \vs ->
+    forAll (resize  5 arbitrary) $ \ps ->
+        let ws = P.avoiders ps vs in ws == P.avoiders ps ws
+
+prop_avoidersSpectrum =
+    forAll (resize 3 arbitrary) $ \p ->
+        let spec = [ length $ P.avoiders [p] (perms n) | n<-[0..6] ]
+        in case size p of
+             0 -> spec == [0,0,0,0,0,0,0]
+             1 -> spec == [1,0,0,0,0,0,0]
+             2 -> spec == [1,1,1,1,1,1,1]
+             3 -> spec == [1,1,2,5,14,42,132]
+             _ -> False
+
+prop_minimaSmallest =
+    forAll (resize 14 arbitrary) $ \ws ->
+        let vs = P.minima ws
+        in and [ not (w `P.avoidsAll` vs) | w <- ws ]
+
+prop_minimaAntichain =
+    forAll (resize 14 arbitrary) $ \ws ->
+        let vs = P.minima ws in and [ v `P.avoidsAll` (vs \\ [v]) | v <- vs ]
+
+prop_maximaAntichain =
+    forAll (resize 12 arbitrary) $ \ws ->
+        let vs = P.maxima ws
+        in and [ v `P.avoidsAll` (vs \\ [v]) | v <- vs ]
+
+testsMathPermPattern =
+    [ ("Math.Perm.Pattern/copies/self",           check prop_copiesSelf)
+    , ("Math.Perm.Pattern/avoiders/avoid",        check prop_avoidersAvoid)
+    , ("Math.Perm.Pattern/avoiders/idempotent",   check prop_avoidersIdempotent)
+    , ("Math.Perm.Pattern/avoiders/spectrum",     check prop_avoidersSpectrum)
+    , ("Math.Perm.Pattern/minima/smallest",       check prop_minimaSmallest)
+    , ("Math.Perm.Pattern/minima/antichain",      check prop_minimaAntichain)
+    , ("Math.Perm.Pattern/maxima/antichain",      check prop_maximaAntichain)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.D8
+--------------------------------------------------------------------------------
+
+prop_reverseDef u =
+    let v = D8.reverse u
+        n = size u
+    in n > 0 ==> forAll (choose (0,n-1)) $ \i -> v `at` i == u `at` (n-1-i)
+
+prop_reverseInvolutive w = D8.reverse (D8.reverse w) == w
+
+prop_complementDef u =
+    let v = D8.complement u
+        n = size u
+    in n > 0 ==> forAll (choose (0,n-1)) $ \i -> v `at` i == n - 1 - u `at` i
+
+prop_complementInvolutive w = D8.complement (D8.complement w) == w
+
+prop_inverseDef u =
+    let v = D8.inverse u
+        n = size u
+    in n > 0 ==> forAll (choose (0,n-1)) $ \i -> v `at` (u `at` i) == i
+
+prop_inverseInvolutive w = D8.inverse (D8.inverse w) == w
+
+testsMathPermD8 =
+    [ ("Math.Perm.D8/reverse/def",           check prop_reverseDef)
+    , ("Math.Perm.D8/reverse/involutive",    check prop_reverseInvolutive)
+    , ("Math.Perm.D8/complement/def",        check prop_complementDef)
+    , ("Math.Perm.D8/complement/involutive", check prop_complementInvolutive)
+    , ("Math.Perm.D8/inverse/def",           check prop_inverseDef)
+    , ("Math.Perm.D8/inverse/involutive",    check prop_inverseInvolutive)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.Group
+--------------------------------------------------------------------------------
+
+prop_act_id v =
+    idperm (size v) `act` v == v
+
+prop_act_associative u v w =
+    (u `compose` v) `act` w == u `act` (v `act` w)
+
+prop_compose_id_left v =
+    idperm (size v) `compose` v == v
+
+prop_compose_id_right v =
+    v `compose` idperm (size v) == v
+
+prop_compose_associative u v w =
+    (u `compose` v) `compose` w == u `compose` (v `compose` w)
+
+testsMathPermGroup =
+    [ ("Math.Perm.Group/act/id",               check prop_act_id)
+    , ("Math.Perm.Group/act/associative",      check prop_act_associative)
+    , ("Math.Perm.Group/compose/id/left",      check prop_compose_id_left)
+    , ("Math.Perm.Group/compose/id/right",     check prop_compose_id_right)
+    , ("Math.Perm.Group/compose/associative",  check prop_compose_associative)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.Bijection
+--------------------------------------------------------------------------------
+
+p123 = fromList [0,1,2]
+p132 = fromList [0,2,1]
+
+prop_simionSchmidt_avoid =
+    forAll (resize 12 arbitrary) $ \w ->
+        w `P.avoids` p123 ==> simionSchmidt w `P.avoids` p132
+
+prop_simionSchmidt_avoid' =
+    forAll (resize 12 arbitrary) $ \w ->
+        w `P.avoids` p132 ==> simionSchmidt' w `P.avoids` p123
+
+prop_simionSchmidt_id =
+    forAll (resize 12 arbitrary) $ \w ->
+        w `P.avoids` p123 ==> simionSchmidt' (simionSchmidt w) == w
+
+prop_simionSchmidt_id' =
+    forAll (resize 12 arbitrary) $ \w ->
+        w `P.avoids` p132 ==> simionSchmidt (simionSchmidt' w) == w
+
+testsMathPermBijection =
+    [ ("Math.Perm.Bijection/simionSchmidt/avoid",  check prop_simionSchmidt_avoid)
+    , ("Math.Perm.Bijection/simionSchmidt'/avoid", check prop_simionSchmidt_avoid')
+    , ("Math.Perm.Bijection/simionSchmidt/id",     check prop_simionSchmidt_id)
+    , ("Math.Perm.Bijection/simionSchmidt'/id",    check prop_simionSchmidt_id')
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.Sort
+--------------------------------------------------------------------------------
+
+-- The stack-sort operator
+stack [] = []
+stack xs = stack left ++ stack right ++ [n]
+    where
+      (left, n:right) = span ( < maximum xs) xs
+
+-- The bubble-sort operator; i.e. one pass of the classical bubble
+-- sort algorithm
+bubble :: Ord a => [a] -> [a]
+bubble = bub []
+    where
+      bub xs []       = reverse xs
+      bub [] (y:ys)   = bub [y] ys
+      bub (x:xs) (y:ys)
+          | x < y     = bub (y:x:xs) ys
+          | otherwise = bub (x:y:xs) ys
+
+prop_stackDef  w = stack  (toList w) == toList (stackSort  w)
+prop_bubbleDef w = bubble (toList w) == toList (bubbleSort w)
+
+prop_stackAv w =
+    let basis = [fromList [1,2,0]]
+    in (stackSort w == idperm (size w)) == w `P.avoidsAll` basis
+
+prop_bubbleAv w =
+    let basis = [fromList [1,2,0], fromList [2,1,0]]
+    in (bubbleSort w == idperm (size w)) == w `P.avoidsAll` basis
+
+testsMathPermSort =
+    [ ("Math.Perm.Sort/stack/def",    check prop_stackDef)
+    , ("Math.Perm.Sort/bubble/def",   check prop_bubbleDef)
+    , ("Math.Perm.Sort/stack/Av",     check prop_stackAv)
+    , ("Math.Perm.Sort/bubble/Av",    check prop_bubbleAv)
+    ]
+
+--------------------------------------------------------------------------------
+-- Properties for Math.Perm.Stat
+--------------------------------------------------------------------------------
+
+statEq f g w = f w == g (toList w)
+
+prop_asc    = statEq S.asc   LS.asc
+prop_des    = statEq S.des   LS.des
+prop_exc    = statEq S.exc   LS.exc
+prop_fp     = statEq S.fp    LS.fp
+prop_cyc    = statEq S.cyc   LS.cyc
+prop_inv    = statEq S.inv   LS.inv
+prop_maj    = statEq S.maj   LS.maj
+prop_comaj  = statEq S.comaj LS.comaj
+prop_lmin   = statEq S.lmin  LS.lmin
+prop_lmax   = statEq S.lmax  LS.lmax
+prop_rmin   = statEq S.rmin  LS.rmin
+prop_rmax   = statEq S.rmax  LS.rmax
+prop_head w = size w > 0 ==> statEq S.head head w
+prop_last w = size w > 0 ==> statEq S.last last w
+prop_peak   = statEq S.peak  LS.peak
+prop_vall   = statEq S.vall  LS.vall
+prop_dasc   = statEq S.dasc  LS.dasc
+prop_ddes   = statEq S.ddes  LS.ddes
+prop_ep     = statEq S.ep    LS.ep
+prop_lir    = statEq S.lir   LS.lir
+prop_ldr    = statEq S.ldr   LS.ldr
+prop_rir    = statEq S.rir   LS.rir
+prop_rdr    = statEq S.rdr   LS.rdr
+prop_comp   = statEq S.comp  LS.comp
+prop_scomp  = statEq S.scomp LS.scomp
+prop_dim    = statEq S.dim   LS.dim
+prop_asc0   = statEq S.asc0  LS.asc0
+prop_des0   = statEq S.des0  LS.des0
+prop_inv_21 = forAll (resize 17 arbitrary) $ \w ->
+                 let stat21 = length . P.copiesOf (fromList [1,0])
+                 in S.inv w == stat21 w
+prop_lis_r w = S.lis w == S.lds (D8.reverse w)
+prop_lis_c w = S.lis w == S.lds (D8.complement w)
+prop_lis_i w = S.lis w == S.lis (D8.inverse w)
+prop_lds_i w = S.lds w == S.lds (D8.inverse w)
+prop_ErdosSzekeres w = n > 0 ==> S.lis w > m || S.lds w > m
+    where
+      n = size w
+      m = floor . sqrt $ fromIntegral (n-1)
+
+-- prop_shad   = forAllPermEq  (shad  . ints)  S.shad
+
+
+testsMathPermStat =
+    [ ("Math.Perm.Stat/asc",        check prop_asc)
+    , ("Math.Perm.Stat/des",        check prop_des)
+    , ("Math.Perm.Stat/exc",        check prop_exc)
+    , ("Math.Perm.Stat/fp",         check prop_fp)
+    , ("Math.Perm.Stat/cyc",        check prop_cyc)
+    , ("Math.Perm.Stat/inv",        check prop_inv)
+    , ("Math.Perm.Stat/maj",        check prop_maj)
+    , ("Math.Perm.Stat/comaj",      check prop_comaj)
+    , ("Math.Perm.Stat/lmin",       check prop_lmin)
+    , ("Math.Perm.Stat/lmax",       check prop_lmax)
+    , ("Math.Perm.Stat/rmin",       check prop_rmin)
+    , ("Math.Perm.Stat/rmax",       check prop_rmax)
+    , ("Math.Perm.Stat/head",       check prop_head)
+    , ("Math.Perm.Stat/last",       check prop_last)
+    , ("Math.Perm.Stat/peak",       check prop_peak)
+    , ("Math.Perm.Stat/vall",       check prop_vall)
+    , ("Math.Perm.Stat/dasc",       check prop_dasc)
+    , ("Math.Perm.Stat/ddes",       check prop_ddes)
+    , ("Math.Perm.Stat/ep",         check prop_ep)
+    , ("Math.Perm.Stat/lir",        check prop_lir)
+    , ("Math.Perm.Stat/ldr",        check prop_ldr)
+    , ("Math.Perm.Stat/rir",        check prop_rir)
+    , ("Math.Perm.Stat/rdr",        check prop_rdr)
+    , ("Math.Perm.Stat/comp",       check prop_comp)
+    , ("Math.Perm.Stat/scomp",      check prop_scomp)
+    , ("Math.Perm.Stat/dim",        check prop_dim)
+    , ("Math.Perm.Stat/asc0",       check prop_asc0)
+    , ("Math.Perm.Stat/des0",       check prop_des0)
+    , ("Math.Perm.Stat/inv/21",     check prop_inv_21)
+    , ("Math.Perm.Stat/lis/r",      check prop_lis_r)
+    , ("Math.Perm.Stat/lis/c",      check prop_lis_c)
+    , ("Math.Perm.Stat/lis/i",      check prop_lis_i)
+    , ("Math.Perm.Stat/lds/i",      check prop_lds_i)
+    , ("Math.Perm.Stat/Erdös-Szekeres", check prop_ErdosSzekeres)
+--     , ("shad",       check prop_shad)
+    ]
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+tests = concat [
+          testsDataPerm
+        , testsDataPermgram
+        , testsDataSSYT
+        , testsMathPerm
+        , testsMathPermPattern
+        , testsMathPermD8
+        , testsMathPermGroup
+        , testsMathPermBijection
+        , testsMathPermSort
+        , testsMathPermStat
+        ]
+
+runTests = mapM_ (\(name, t) -> putStr (name ++ ":\t") >> t)
+
+main = runTests tests
