diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -26,8 +26,7 @@
 	* Added functions 'merge' & 'mergeBy' to module "ToolShed.ListPlus" & exported a new type-synonym 'ToolShed.ListPlus.ChunkLength'.
 	* Uploaded to <http://hackage.haskell.org/packages/hackage.html>.
 0.12.0.0
-	* Added module "ToolShed.TimeAction" to measure the CPU-seconds required for an IO-action.
-	* Added module "Pair".
+	* Added modules "ToolShed.TimeAction" to measure the CPU-seconds required for an IO-action, & "Pair".
 0.12.0.1
 	* Removed unnecessary type-context 'Ord' from 'ToolShed.ListPus.mergeBy' & reimplemented 'ToolShed.ListPus.merge' in terms of 'ToolShed.ListPus.mergeBy'.
 	* Added 'ToolShed.ListPlus.runLengthEncode' & 'ToolShed.ListPlus.runLengthDecode'.
@@ -44,8 +43,7 @@
 	* Added instances of 'ToolShed.SelfValidate.SelfValidator' for 'Data.Maybe.Maybe' '(,)', 'Data.Set.Set', 'Data.Map.Map' & 'Data.Array.IArray.Array'.
 	* Added instance of 'ToolShed.Defaultable.Defaultable' for '(,)'.
 	* Added functions 'ToolShed.Data.List.showListWith', 'ToolShed.Data.List.permutations', 'ToolShed.Data.List.gatherBy', 'ToolShed.Data.List.findConvergenceBy', 'ToolShed.Data.List.findConvergence', 'ToolShed.Data.List.gather'.
-	* Added module "ToolShed.System.File".
-	* Added module "ToolShed.System.Random".
+	* Added modules "ToolShed.System.File" & "ToolShed.System.Random".
 	* Added method 'ToolShed.SelfValidate.getErrors', & supporting functions 'ToolShed.SelfValidate.getFirstErrors' & 'ToolShed.SelfValidate.extractErrors'.
 	* Create module "ToolShed.Arbitrary.*".
 	* Added "Main", "ToolShed.Test.QuickChecks" & "ToolShed.Test.Data.List" to facilitate testing.
@@ -56,12 +54,22 @@
 	* Defined package's name using program's name, in "Main.hs".
 	* Checked package "Base" exports 'Data.Tuple.swap', in "ToolShed.Arbitrary.Array".
 	* Relocated "ToolShed.Arbitrary" to "ToolShed.Test.QuickCheck.Arbitrary".
-	* Added "ToolShed.Test.ReversibleBoundedEnum", "ToolShed.Test.ReversibleEnum" "ToolShed.Test.ReversibleIO", & "ToolShed.Test.SelfValidate".
+	* Added "ToolShed.Test.ReversibleBoundedEnum", "ToolShed.Test.ReversibleEnum", "ToolShed.Test.ReversibleIO", & "ToolShed.Test.SelfValidate".
 	* Amended 'ToolShed.System.File.locate' to deal with absolute file-paths.
 	* Replaced legacy calls to 'Data.Map.fold' & 'Data.Set.fold', with more expicit calls to 'Data.Map.foldr' & 'Data.Set.foldr' from a more recent version of the "containers" package.
 	* Added instance of 'ToolShed.SelfValidate.SelfValidator' for '(,,)'.
 	* Trapped null list in 'ToolShed.System.Random.select'.
-	* Added "ToolShed.Data.List.nub'".
-	* Added 'ToolShed.Data.List.hasDuplicates'.
+	* Added functions "ToolShed.Data.List.nub'" & 'ToolShed.Data.List.hasDuplicates'.
 	* Migrated functions 'gather', 'gatherBy', 'hasDuplicates' which merely operate on 'Data.Foldable.Foldable' types from module "ToolShed.Data.List", into new module "ToolShed.Data.Foldable".
-
+0.15.0.0
+	* Refactored 'ToolShed.Test.QuickChecks'.
+	* Added type 'ToolShed.Data.List.Matches' & function 'ToolShed.Data.List.permutationsBy'.
+	* Trapped the case of a null list supplied to 'ToolShed.Data.List.permutationsBy'.
+	* Re-implemented 'ToolShed.System.Random.shuffle', using the Fisher-Yates algorithm; which also improved the efficiency.
+	* Trapped special cases in 'ToolShed.System.Random.generateSelection'.
+	* Added modules "ToolShed.Test.System.Random", "ToolShed.Data.Triple", "ToolShed.Data.Quadruple", "ToolShed.Test.Data.Triple", & "ToolShed.Test.Data.Quadruple".
+	* Explicitly closed standard-input in the executable.
+	* Replaced calls to 'error' from inside the IO-monad, with 'Control.Monad.fail'.
+	* Added an instance for '(,,)', to "ToolShed.Defaultable.Defaultable".
+	* Corrected version-string in "Main" used in '--version' option.
+	* Corrected the output of 'Main.main.optDescrList.printVersion'.
diff --git a/copyright b/copyright
--- a/copyright
+++ b/copyright
@@ -2,7 +2,7 @@
 	Dr. Alistair Ward <toolshed at functionalley dot eu>.
 
 Copyright:
-	Copyright (C) 2010-2011 Dr. Alistair Ward. All Rights Reserved.
+	Copyright (C) 2010-2013 Dr. Alistair Ward. All Rights Reserved.
 
 Home-page:
 	http://functionalley.eu
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -24,13 +24,7 @@
 	* Facilitates testing.
 -}
 
-module Main(
--- * Types
--- ** Type-synonyms
---	CommandLineAction,
--- * Functions
-	main
-) where
+module Main(main) where
 
 import qualified	Data.List
 import qualified	Data.Version
@@ -52,6 +46,8 @@
 -- | Parses the command-line arguments, to determine 'Test.CommandOptions.CommandOptions'.
 main :: IO ()
 main	= do
+	System.IO.hClose System.IO.stdin	--Nothing is read from standard input.
+
 	progName	<- System.Environment.getProgName
 
 	let
@@ -67,7 +63,7 @@
 			G.Option "q"	["runQuickChecks"]	(G.NoArg $ const runQuickChecks)	"Run Quick-checks using arbitrary data & then exit."
 		 ] where
 			printVersion, printUsage, runQuickChecks :: IO ()
-			printVersion	= System.IO.hPutStrLn System.IO.stderr (Distribution.Text.display packageIdentifier ++ "\n\nCopyright (C) 2011 " ++ author ++ ".\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions.\n\nWritten by " ++ author ++ ".")	>> System.Exit.exitWith System.Exit.ExitSuccess	where
+			printVersion	= System.IO.hPutStrLn System.IO.stderr (Distribution.Text.display packageIdentifier ++ "\n\nCopyright (C) 2010-2013 " ++ author ++ ".\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions.\n\nWritten by " ++ author ++ ".")	>> System.Exit.exitWith System.Exit.ExitSuccess	where
 				packageIdentifier :: Distribution.Package.PackageIdentifier
 				packageIdentifier	= Distribution.Package.PackageIdentifier {
 					Distribution.Package.pkgName	= Distribution.Package.PackageName progName,	--CAVEAT: coincidentally.
diff --git a/src/ToolShed/Data/Foldable.hs b/src/ToolShed/Data/Foldable.hs
--- a/src/ToolShed/Data/Foldable.hs
+++ b/src/ToolShed/Data/Foldable.hs
@@ -47,3 +47,4 @@
 -- | Whether the specified collection contains any equal items.
 hasDuplicates :: (Data.Foldable.Foldable f, Ord a) => f a -> Bool
 hasDuplicates	= any ((> 1) . length) . gather
+
diff --git a/src/ToolShed/Data/List.hs b/src/ToolShed/Data/List.hs
--- a/src/ToolShed/Data/List.hs
+++ b/src/ToolShed/Data/List.hs
@@ -24,6 +24,7 @@
 -- * Types
 -- ** Type-synonyms
 	ChunkLength,
+	Matches,
 -- * Functions
 	chunk,
 	excise,
@@ -36,6 +37,7 @@
 	nub',
 --	nubWithInt,
 	permutations,
+	permutationsBy,
 	takeUntil,
 	showListWith,
 --	showListWithChar,
@@ -43,6 +45,7 @@
 ) where
 
 import qualified	Control.Arrow
+import			Control.Arrow((&&&))
 import qualified	Data.IntSet
 import qualified	Data.List
 import qualified	Data.Set
@@ -51,17 +54,17 @@
 type ChunkLength	= Int
 
 {- |
-	* Splits a list into length-@size@ pieces, where @(size >= 0)@.
+	* Splits a list into chunks of the specified length.
 
-	* The last chunk will be shorter, if @n@ isn't an aliquot part of the input list-length.
+	* The last chunk will be shorter, if the chunk-length isn't an aliquot part of the input list-length.
 
-	* If @(size == 0)@, the resulting list will be an infinite sequence of null lists.
+	* If the chunk-length is zero, the resulting list will be an infinite sequence of null lists.
 
-	* CAVEAT: a similar function is available in the module /Data.List.Split/, though this one checks for @(size < 0)@.
+	* CAVEAT: a similar function is available in the module /Data.List.Split/, though this one checks for @(chunkLength < 0)@.
 -}
 chunk
 	:: ChunkLength
-	-> [a]		-- ^ The polymorphic input list to be chunked.
+	-> [a]	-- ^ The polymorphic input list to be chunked.
 	-> [[a]]
 chunk size list
 	| size < 0	= error $ "ToolShed.Data.List.chunk:\tnegative chunk-size=" ++ show size
@@ -79,24 +82,60 @@
 excise 0	= tail	--Just for efficiency.
 excise i	= uncurry (++) . Control.Arrow.second tail . splitAt i
 
--- | A convenient way to compose the function-parameter required by 'Data.List.groupBy' or 'Data.List.nubBy'.
-equalityBy :: Eq b => (a -> b) -> a -> a -> Bool
+-- | The type of function required by 'findConvergenceBy', 'permutationsBy'.
+type Matches a	= a -> a -> Bool
+
+-- | A convenient way to compose the 'Matches'-function required by 'findConvergenceBy' & 'permutationsBy'.
+equalityBy :: Eq b => (a -> b) -> Matches a
 equalityBy f x y	= f x == f y
 
--- | Take the first element from the list, which compares equal according to the specified predicate, with the subsequent element.
-findConvergenceBy :: (a -> a -> Bool) -> [a] -> a
-findConvergenceBy predicate l	= fst . head . dropWhile (not . uncurry predicate) . zip l $ tail l
+-- | Take the first element from the (potentially infinite) list, which matches the subsequent element, according to the specified function.
+findConvergenceBy :: Matches a -> [a] -> a
+findConvergenceBy _ []	=  error "ToolShed.Data.List.findConvergenceBy:\ta null list is too short for convergence to exist"
+findConvergenceBy _ [_]	=  error "ToolShed.Data.List.findConvergenceBy:\ta singleton list is too short for convergence to exist"
+findConvergenceBy matches l
+	| null l'	= error "ToolShed.Data.List.findConvergenceBy:\tno convergence found"
+	| otherwise	= fst $ head l'
+	where
+		l'	= dropWhile (not . uncurry matches) . uncurry zip $ (init &&& tail) l
 
 -- | A specific instance of 'findConvergenceBy'.
 findConvergence :: Eq a => [a] -> a
 findConvergence	= findConvergenceBy (==)
 
+{- |
+	* The list of all permutations, generated by selecting any one datum from each sub-list in turn, from the specified list of lists.
+
+	* As each item is selected, the remaining lists are filtered according to the specified 'Matches'-function.
+
+	* Thus '/=' could be used to select a different item from each list.
+-}
+permutationsBy :: Matches a -> [[a]] -> [[a]]
+permutationsBy matches lists
+	| any null lists	= []	--Required for efficiency, to catch the case [bigList1, bigList2 ... null]
+	| otherwise		= slave lists
+	where
+		slave (xs : xss)	= [x : xs' | x <- xs, xs' <- slave $ map (filter $ matches x) xss]
+		slave []		= [[]]
+
+{- |
+	* The list of all permutations, generated by selecting any one datum from each sub-list in turn, from the specified list of lists.
+
+	* A specific instance of 'permutationsBy', in which no filtering of subsequent lists is performed after each item is selected.
+
+	* NB: differs from 'Data.List.permutations', which selects items from a single input list.
+-}
+permutations :: [[a]] -> [[a]]
+permutations	= permutationsBy (\_ _ -> True)
+
 {-# NOINLINE nub' #-}
 {-# RULES "nub'/Int" nub' = nubWithInt #-}
 
 {- |
-	* A version of 'Data.List.nub' with better time-complexity.
+	* A strict version of 'Data.List.nub' with better time-complexity.
 
+	* CAVEAT: the specified list must be finite, since the entire set is constructed before streaming to a list.
+
 	* CAVEAT: it sorts the output as a side-effect, & consequently it requires a type which implements 'Ord'.
 -}
 nub' :: Ord a => [a] -> [a]
@@ -129,15 +168,6 @@
 -- | A specific instance of 'mergeBy'.
 merge :: Ord a => [a] -> [a] -> [a]
 merge	= mergeBy compare
-
-{- |
-	* The list of all permutations, generated by selecting any one datum from each sub-list in turn, from the specified list of lists.
-
-	* Taken from <http://www.haskell.org/pipermail/haskell-cafe/2006-June/015975.html>.
--}
-permutations :: [[a]] -> [[a]]
-permutations (xs : xss)	= [ x : xs' | x <- xs, xs' <- permutations xss ]
-permutations []		= [[]]
 
 {- |
 	* Take until the specified predicate is satisfied; /including/ the item which satisfied it.
diff --git a/src/ToolShed/Data/List/Runlength.hs b/src/ToolShed/Data/List/Runlength.hs
--- a/src/ToolShed/Data/List/Runlength.hs
+++ b/src/ToolShed/Data/List/Runlength.hs
@@ -53,3 +53,4 @@
 -- | Performs /run-length/ decoding to retrieve the original unencoded list.
 decode :: [Code a] -> [a]
 decode	= concatMap (uncurry replicate)
+
diff --git a/src/ToolShed/Data/Quadruple.hs b/src/ToolShed/Data/Quadruple.hs
new file mode 100644
--- /dev/null
+++ b/src/ToolShed/Data/Quadruple.hs
@@ -0,0 +1,59 @@
+{-
+	Copyright (C) 2013 Dr. Alistair Ward
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-}
+{- |
+ [@AUTHOR@]	Dr. Alistair Ward
+
+ [@DESCRIPTION@]	Miscellaneous operations on quadruples.
+
+ [@CAVEAT@]	Import fully qualified, since some identifiers clash with 'ToolShed.Data.Triple'.
+-}
+
+module ToolShed.Data.Quadruple(
+-- * Functions
+	curry4,
+	uncurry4,
+-- ** Accessors
+	getFirst,
+	getSecond,
+	getThird,
+	getFourth
+) where
+
+-- | Extends the concept of 'Data.Tuple.curry'.
+curry4 :: ((a, b, c, d) -> result) -> a -> b -> c -> d -> result
+curry4 f a b c d	= f (a, b, c, d)
+
+-- | Extends the concept of 'Data.Tuple.uncurry'.
+uncurry4 :: (a -> b -> c -> d -> result) -> (a, b, c, d) -> result
+uncurry4 f (a, b, c, d)	= f a b c d
+
+-- | Access the first datum from the specified quadruple.
+getFirst :: (a, b, c, d) -> a
+getFirst (a, _, _, _)	= a
+
+-- | Access the second datum from the specified quadruple.
+getSecond :: (a, b, c, d) -> b
+getSecond (_, b, _, _)	= b
+
+-- | Access the third datum from the specified quadruple.
+getThird :: (a, b, c, d) -> c
+getThird (_, _, c, _)	= c
+
+-- | Access the fourth datum from the specified quadruple.
+getFourth :: (a, b, c, d) -> d
+getFourth (_, _, _, d)	= d
+
diff --git a/src/ToolShed/Data/Triple.hs b/src/ToolShed/Data/Triple.hs
new file mode 100644
--- /dev/null
+++ b/src/ToolShed/Data/Triple.hs
@@ -0,0 +1,54 @@
+{-
+	Copyright (C) 2013 Dr. Alistair Ward
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-}
+{- |
+ [@AUTHOR@]	Dr. Alistair Ward
+
+ [@DESCRIPTION@]	Miscellaneous operations on triples.
+
+ [@CAVEAT@]	Import fully qualified, since some identifiers clash with 'ToolShed.Data.Quadruple'.
+-}
+
+module ToolShed.Data.Triple(
+-- * Functions
+	curry3,
+	uncurry3,
+-- ** Accessors
+	getFirst,
+	getSecond,
+	getThird
+) where
+
+-- | Extends the concept of 'Data.Tuple.curry'.
+curry3 :: ((a, b, c) -> result) -> a -> b -> c -> result
+curry3 f a b c	= f (a, b, c)
+
+-- | Extends the concept of 'Data.Tuple.uncurry'.
+uncurry3 :: (a -> b -> c -> result) -> (a, b, c) -> result
+uncurry3 f (a, b, c)	= f a b c
+
+-- | Access the first datum from the specified triple.
+getFirst :: (a, b, c) -> a
+getFirst (a, _, _)	= a
+
+-- | Access the second datum from the specified triple.
+getSecond :: (a, b, c) -> b
+getSecond (_, b, _)	= b
+
+-- | Access the third datum from the specified triple.
+getThird :: (a, b, c) -> c
+getThird (_, _, c)	= c
+
diff --git a/src/ToolShed/Defaultable.hs b/src/ToolShed/Defaultable.hs
--- a/src/ToolShed/Defaultable.hs
+++ b/src/ToolShed/Defaultable.hs
@@ -31,3 +31,6 @@
 
 instance (Defaultable a, Defaultable b) => Defaultable (a, b)	where
 	defaultValue	= (defaultValue, defaultValue)
+
+instance (Defaultable a, Defaultable b, Defaultable c) => Defaultable (a, b, c)	where
+	defaultValue	= (defaultValue, defaultValue, defaultValue)
diff --git a/src/ToolShed/System/File.hs b/src/ToolShed/System/File.hs
--- a/src/ToolShed/System/File.hs
+++ b/src/ToolShed/System/File.hs
@@ -103,5 +103,5 @@
 
 	case reads fileContents of
 		[(x, _)]	-> return {-to IO-monad-} (filePath, x)	--CAVEAT: discards any unconsumed text.
-		_		-> error $ "ToolShed.System.File.fromFile:\tfailed to parse file=" ++ show filePath
+		_		-> fail $ "ToolShed.System.File.fromFile:\tfailed to parse file=" ++ show filePath
 
diff --git a/src/ToolShed/System/Random.hs b/src/ToolShed/System/Random.hs
--- a/src/ToolShed/System/Random.hs
+++ b/src/ToolShed/System/Random.hs
@@ -31,8 +31,8 @@
 ) where
 
 import qualified	Control.Arrow
-import qualified	Data.List
-import qualified	Data.Ord
+import qualified	Data.Sequence
+import			Data.Sequence((><), ViewL((:<)))
 import qualified	System.Random
 
 -- | Constructs an infinite list of independent random-generators.
@@ -40,37 +40,41 @@
 randomGens	= uncurry (:) . Control.Arrow.second randomGens {-recurse-} . System.Random.split
 
 {- |
-	* Shuffles the specified finite list.
+	* Shuffles the specified finite list, using the /Fisher-Yates/ algorithm; <http://en.wikipedia.org/wiki/Fisher-Yates_shuffle>.
 
 	* The resulting list has the same length and constituents as the original; only the order has changed.
 
-	* CAVEAT: the implementation /zips/ a list of integers, with the specified polymorphic list, then sorts it,
-	but when identical random integers are generated, the sort-algorithm being /stable/ always return the corresponding items in their original order.
-	The shuffle is therefore imperfect,
-	but on a /64-bit/ machine, it would need such a large list of items, for the probability of randomly generating two identical integers, to be significant,
-	that /sort/ probably wouldn't return in a reasonable time anyway.
-	Ideally, it would be amended to use an /unstable/ sort-algorithm.
+	* The input list is traversed, but the items aren't evaluated.
 -}
 shuffle :: System.Random.RandomGen randomGen => randomGen -> [a] -> [a]
-shuffle randomGen	= map snd . Data.List.sortBy (Data.Ord.comparing fst) . zip (System.Random.randoms randomGen :: [Int])
+shuffle _ []		= []		--Not strictly necessary.
+shuffle _ singleton@[_]	= singleton	--Not strictly necessary.
+shuffle randomGen l	= slave randomGen s (pred $ Data.Sequence.length s)	where
+	s	= Data.Sequence.fromList l
 
+	slave randomGen' s' maxIndex
+		| maxIndex < 0	= []
+		| otherwise	= selection : slave randomGen'' (first >< remainder) (pred maxIndex) {-recurse-}
+		where
+			(randomIndex, randomGen'')	= System.Random.randomR (0, maxIndex) randomGen'
+			(first, selection :< remainder)	= Control.Arrow.second Data.Sequence.viewl $ Data.Sequence.splitAt randomIndex s'
+
 {- |
-	* Generate an infinite list of items, each randomly selected from the specified finite list.
+	* Generate an infinite list of items, each independently randomly selected from the specified finite list.
 
 	* CAVEAT: because the selections are made non-destructively, duplicates may be returned; cf. 'shuffle'.
 -}
 generateSelection :: System.Random.RandomGen randomGen => randomGen -> [a] -> [a]
+generateSelection _ []		= error "ToolShed.System.Random.generateSelection:\tnull list"
+generateSelection _ [x]		= repeat x	--Not strictly necessary, but more efficient.
 generateSelection randomGen l	= map (l !!) $ System.Random.randomRs (0, pred $ length l) randomGen
 
--- | Return a random element from the specified list.
+-- | Return a randomly selected element from the specified list.
 select :: System.Random.RandomGen randomGen => randomGen -> [a] -> a
-select _ []		= error "ToolShed.System.Random.generateSelection:\tnull list"
-select randomGen l	= head $ generateSelection randomGen l
+select randomGen	= head . generateSelection randomGen
 
 {- |
-	* Generate an infinite list of items, each randomly selected, from the specified finite list of /bounded/ items.
-
-	* Because the selections are made non-destructively, duplicates may be returned.
+	* Generate an infinite list of data, each independently selected random instances of the specified /bounded/ type.
 
 	* E.g. @ (generateSelectionFromBounded `fmap` System.Random.getStdGen) :: IO [Bool] @.
 -}
diff --git a/src/ToolShed/Test/Data/List.hs b/src/ToolShed/Test/Data/List.hs
--- a/src/ToolShed/Test/Data/List.hs
+++ b/src/ToolShed/Test/Data/List.hs
@@ -36,9 +36,9 @@
 quickChecks :: IO ()
 quickChecks =
 	Test.QuickCheck.quickCheck prop_chunk
-	>> Test.QuickCheck.quickCheck prop_findConvergence
+	>> Test.QuickCheck.quickCheck `mapM` [prop_findConvergence, prop_permutationsBy']
 	>> Test.QuickCheck.quickCheck prop_nub
-	>> Test.QuickCheck.quickCheck prop_permutations
+	>> Test.QuickCheck.quickCheck `mapM` [prop_permutations, prop_permutations', prop_permutationsBy]
 	>> Test.QuickCheck.quickCheck prop_linearise
 	>> Test.QuickCheck.quickCheck prop_merge
 	where
@@ -51,9 +51,21 @@
 		prop_nub :: [Int] -> Test.QuickCheck.Property
 		prop_nub x	= Test.QuickCheck.label "prop_nub" $ ToolShed.Data.List.nub' x == Data.List.sort (Data.List.nub x)
 
-		prop_permutations :: [[Int]] -> Test.QuickCheck.Property
+		prop_permutations, prop_permutations', prop_permutationsBy :: [[Int]] -> Test.QuickCheck.Property
 		prop_permutations l	= not (null l')	==> Test.QuickCheck.label "prop_permutations" $ length (ToolShed.Data.List.permutations l') == product (map length l')	where
 			l'	= take 6 $ map (take 6) l	--Limit the task.
+
+		prop_permutations' l	= not (null l') && all (not . null) l'	==> Test.QuickCheck.label "prop_permutations'" . (== 1) . length . Data.List.nub . map length $ ToolShed.Data.List.permutations l'	where
+			l'	= take 7 $ map (take 5) l	--Limit the task.
+
+		prop_permutationsBy l	= and [not $ null l', all (not . null) l', not $ null permutations]	==> Test.QuickCheck.label "prop_permutationsBy" . (== 1) . length . Data.List.nub $ map (length . Data.List.nub) permutations	where
+			l'		= take 8 $ map (take 4) l	--Limit the task.
+			permutations	= ToolShed.Data.List.permutationsBy (/=) l'
+
+		prop_permutationsBy' :: Int -> Test.QuickCheck.Property
+		prop_permutationsBy' i	= Test.QuickCheck.label "prop_permutationsBy'" . all ((== range) . Data.List.sort) . ToolShed.Data.List.permutationsBy (/=) $ replicate (succ i') range	where
+			i'	= succ $ mod i 7
+			range	= [0 .. i']
 
 		prop_linearise :: [(Int, Int)] -> Test.QuickCheck.Property
 		prop_linearise l	= Test.QuickCheck.label "prop_linearise" $ map (\[x, y] -> (x, y)) (ToolShed.Data.List.chunk 2 $ ToolShed.Data.List.linearise l) == l
diff --git a/src/ToolShed/Test/Data/Quadruple.hs b/src/ToolShed/Test/Data/Quadruple.hs
new file mode 100644
--- /dev/null
+++ b/src/ToolShed/Test/Data/Quadruple.hs
@@ -0,0 +1,36 @@
+{-
+	Copyright (C) 2013 Dr. Alistair Ward
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-}
+{- |
+ [@AUTHOR@]	Dr. Alistair Ward
+
+ [@DESCRIPTION@]	Defines /QuickCheck/-properties "ToolShed.Data.Quadruple".
+-}
+
+module ToolShed.Test.Data.Quadruple(
+-- * Functions
+	quickChecks
+) where
+
+import qualified	Test.QuickCheck
+import qualified	ToolShed.Data.Quadruple
+
+-- | Defines invariant properties.
+quickChecks :: IO ()
+quickChecks = Test.QuickCheck.quickCheck prop_accessors	where
+	prop_accessors :: (Int, Char, Bool, Float) -> Test.QuickCheck.Property
+	prop_accessors quadruple	= Test.QuickCheck.label "prop_accessors" $ (f ToolShed.Data.Quadruple.getFirst quadruple, f ToolShed.Data.Quadruple.getSecond quadruple, f ToolShed.Data.Quadruple.getThird quadruple, ToolShed.Data.Quadruple.getFourth quadruple) == quadruple	where
+		f	= ToolShed.Data.Quadruple.uncurry4 . ToolShed.Data.Quadruple.curry4
diff --git a/src/ToolShed/Test/Data/Triple.hs b/src/ToolShed/Test/Data/Triple.hs
new file mode 100644
--- /dev/null
+++ b/src/ToolShed/Test/Data/Triple.hs
@@ -0,0 +1,37 @@
+{-
+	Copyright (C) 2013 Dr. Alistair Ward
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-}
+{- |
+ [@AUTHOR@]	Dr. Alistair Ward
+
+ [@DESCRIPTION@]	Defines /QuickCheck/-properties "ToolShed.Data.Triple".
+-}
+
+module ToolShed.Test.Data.Triple(
+-- * Functions
+	quickChecks
+) where
+
+import qualified	Test.QuickCheck
+import qualified	ToolShed.Data.Triple
+
+-- | Defines invariant properties.
+quickChecks :: IO ()
+quickChecks = Test.QuickCheck.quickCheck prop_accessors	where
+	prop_accessors :: (Int, Char, Bool) -> Test.QuickCheck.Property
+	prop_accessors triple	= Test.QuickCheck.label "prop_accessors" $ (f ToolShed.Data.Triple.getFirst triple, f ToolShed.Data.Triple.getSecond triple, f ToolShed.Data.Triple.getThird triple) == triple	where
+		f	= ToolShed.Data.Triple.uncurry3 . ToolShed.Data.Triple.curry3
+
diff --git a/src/ToolShed/Test/QuickChecks.hs b/src/ToolShed/Test/QuickChecks.hs
--- a/src/ToolShed/Test/QuickChecks.hs
+++ b/src/ToolShed/Test/QuickChecks.hs
@@ -25,17 +25,27 @@
 	run
 ) where
 
+import qualified	Control.Arrow
 import qualified	ToolShed.Test.Data.Foldable
 import qualified	ToolShed.Test.Data.List
 import qualified	ToolShed.Test.Data.List.Runlength
-import qualified	ToolShed.Test.SelfValidate
 import qualified	ToolShed.Test.Data.List.Splits
+import qualified	ToolShed.Test.Data.Quadruple
+import qualified	ToolShed.Test.Data.Triple
+import qualified	ToolShed.Test.SelfValidate
+import qualified	ToolShed.Test.System.Random
 
 -- | Run the /quickChecks/-functions for modules supporting this feature.
 run :: IO ()
-run	= putStrLn "Data.Foldable"		>> ToolShed.Test.Data.Foldable.quickChecks
-	>> putStrLn "Data.List"			>> ToolShed.Test.Data.List.quickChecks
-	>> putStrLn "Data.List.Runlength"	>> ToolShed.Test.Data.List.Runlength.quickChecks
-	>> putStrLn "Data.List.Splits"		>> ToolShed.Test.Data.List.Splits.quickChecks
-	>> putStrLn "SelfValidate"		>> ToolShed.Test.SelfValidate.quickChecks
-
+run	= mapM_ (
+	uncurry (>>) . Control.Arrow.first putStrLn
+ ) [
+	("Data.Foldable",	ToolShed.Test.Data.Foldable.quickChecks),
+	("Data.List",		ToolShed.Test.Data.List.quickChecks),
+	("Data.List.Runlength",	ToolShed.Test.Data.List.Runlength.quickChecks),
+	("Data.List.Splits",	ToolShed.Test.Data.List.Splits.quickChecks),
+	("Data.Quadruple",	ToolShed.Test.Data.Quadruple.quickChecks),
+	("Data.Triple",		ToolShed.Test.Data.Triple.quickChecks),
+	("SelfValidate",	ToolShed.Test.SelfValidate.quickChecks),
+	("System.Random",	ToolShed.Test.System.Random.quickChecks)
+ ]
diff --git a/src/ToolShed/Test/System/Random.hs b/src/ToolShed/Test/System/Random.hs
new file mode 100644
--- /dev/null
+++ b/src/ToolShed/Test/System/Random.hs
@@ -0,0 +1,82 @@
+{-
+	Copyright (C) 2013 Dr. Alistair Ward
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-}
+{- |
+ [@AUTHOR@]	Dr. Alistair Ward
+
+ [@DESCRIPTION@]	Defines /QuickCheck/-properties "ToolShed.System.Random".
+-}
+
+module ToolShed.Test.System.Random(
+-- * Functions
+--	getMean,
+--	getStandardDeviation
+	quickChecks
+) where
+
+import qualified	Control.Arrow
+import			Control.Arrow((&&&),(***))
+import qualified	Data.Foldable
+import qualified	Data.List
+import qualified	Data.Map
+import qualified	System.Random
+import qualified	Test.QuickCheck
+import qualified	ToolShed.System.Random
+
+-- | Determines the /mean/ of the specified numbers
+getMean :: (Data.Foldable.Foldable f, Real r, Fractional result) => f r -> result
+getMean	= uncurry (/) . (realToFrac *** fromIntegral) . Data.Foldable.foldr (\i -> (+ i) *** succ) (0, 0 :: Int)
+
+-- | Find the standard-deviation of the specified list.
+getStandardDeviation :: (Data.Foldable.Foldable f, Functor f, Real r) => f r -> Double
+getStandardDeviation x	= sqrt . getMean $ fmap ((^ (2 :: Int)) . (+ negate (getMean x :: Rational)) . toRational) x
+
+-- | Defines invariant properties.
+quickChecks :: IO ()
+quickChecks
+	= Test.QuickCheck.quickCheck prop_shuffle
+	>> Test.QuickCheck.quickCheck prop_shuffleDistribution
+	where
+		prop_shuffle :: Int -> Int -> Test.QuickCheck.Property
+		prop_shuffle seed l	= Test.QuickCheck.label "prop_shuffle" . (== testList) . Data.List.sort $ ToolShed.System.Random.shuffle (System.Random.mkStdGen seed) testList	where
+			testList	= [0 .. l `mod` 10000]
+
+		prop_shuffleDistribution :: Int -> Test.QuickCheck.Property
+		prop_shuffleDistribution	= Test.QuickCheck.label "prop_shuffleDistribution" . all (
+			uncurry (&&) . (
+				(
+					< recip 20 {-empirically-}	--The ideal value for a uniform distribution, would be zero.
+				) . (
+					/ getStandardDeviation (populationSize : replicate (pred $ length testList) 0)	--Normalise wrt the worst-case; which occurs when this column contains the same letter, for each test-case in the population.
+				) . getStandardDeviation &&& (
+					== populationSize	--Confirm the size of the test-data.
+				) . Data.Foldable.sum
+			)
+		 ) . map (
+			foldr (
+				uncurry $ Data.Map.insertWith (+)
+			) (
+				Data.Map.fromList . zip testList $ repeat (0 :: Int)	--Initial value.
+			) . (`zip` repeat 1)	--Count the instances of each letter in this column, which for a uniform distribution, should be approximately the same.
+		 ) . Data.List.transpose {-examine the columns-} . take populationSize . map fst {-shuffled digits-} . tail {-drop initial value-} . iterate (
+			Control.Arrow.first (`ToolShed.System.Random.shuffle` testList) . System.Random.split . snd
+		 ) . (,) undefined . System.Random.mkStdGen	where
+			testList :: String
+			testList	= ['a' .. 'z']
+
+			populationSize :: Int
+			populationSize	= 1000
+
diff --git a/toolshed.cabal b/toolshed.cabal
--- a/toolshed.cabal
+++ b/toolshed.cabal
@@ -1,8 +1,8 @@
 --Package-properties
 Name:			toolshed
-Version:		0.14.0.0
+Version:		0.15.0.0
 Cabal-Version:		>= 1.6
-Copyright:		(C) 2010 Dr. Alistair Ward
+Copyright:		(C) 2010-2013 Dr. Alistair Ward
 License:		GPL
 License-file:		LICENSE
 Author:			Dr. Alistair Ward
@@ -11,7 +11,7 @@
 Build-Type:		Simple
 Description:		An ill-defined collection of simple unrelated utilities used by other packages from <http://functionalley.eu>
 Category:		Utils
-Tested-With:		GHC == 6.10, GHC == 6.12, GHC == 7.0, GHC == 7.4
+Tested-With:		GHC == 7.4
 Homepage:		http://functionalley.eu
 Maintainer:		toolshed <at> functionalley <dot> eu
 Bug-reports:		toolshed <at> functionalley <dot> eu
@@ -35,6 +35,8 @@
         ToolShed.Data.List.Runlength
         ToolShed.Data.List.Splits
         ToolShed.Data.Pair
+        ToolShed.Data.Quadruple
+        ToolShed.Data.Triple
         ToolShed.Defaultable
         ToolShed.Options
         ToolShed.SelfValidate
@@ -42,14 +44,13 @@
         ToolShed.System.Random
         ToolShed.System.TimeAction
         ToolShed.System.TimePure
-        ToolShed.Test.ReversibleBoundedEnum
-        ToolShed.Test.ReversibleEnum
-        ToolShed.Test.ReversibleIO
-        ToolShed.Test.SelfValidate
         ToolShed.Test.QuickCheck.Arbitrary.Array
         ToolShed.Test.QuickCheck.Arbitrary.ArrayElem
         ToolShed.Test.QuickCheck.Arbitrary.Map
         ToolShed.Test.QuickCheck.Arbitrary.Set
+        ToolShed.Test.ReversibleBoundedEnum
+        ToolShed.Test.ReversibleEnum
+        ToolShed.Test.ReversibleIO
 
     Build-depends:
         array,
@@ -80,11 +81,15 @@
     Main-Is:		Main.hs
 
     Other-modules:
-        ToolShed.Test.QuickChecks
         ToolShed.Test.Data.Foldable
         ToolShed.Test.Data.List
         ToolShed.Test.Data.List.Runlength
         ToolShed.Test.Data.List.Splits
+        ToolShed.Test.Data.Quadruple
+        ToolShed.Test.Data.Triple
+        ToolShed.Test.QuickChecks
+        ToolShed.Test.SelfValidate
+        ToolShed.Test.System.Random
 
     Build-depends:
         Cabal >= 1.6 && < 2
