diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -67,9 +67,18 @@
 	* Added command-line flag 'includeEmpty', to allow one to include empty files & directories in any solution.
 1.0.3.0
 	* Changed "Main" to replace calls to 'error' from inside the IO-monad, with 'Control.Monad.fail'.
-	* Changed "Main"; by adding a command-line option to define the seed for the random-generator used for performance-testing; to permitting 'includeEmpty' take an optional Boolean argument, rather than a mandatory one; to read 'Squeeze.Data.CommandOptions.getMinimumUsageRatio' as a 'Double', rather than a 'Rational'.
+	* Changed "Main"; by adding a command-line option to define the seed for the random-generator used for performance-testing; to permit 'includeEmpty' to take an optional Boolean argument, rather than a mandatory one; to read 'Squeeze.Data.CommandOptions.getMinimumUsageRatio' as a 'Double', rather than a 'Rational'.
 	* Renamed the accessors in "Squeeze.Data.CommandOptions", & removed 'bisectionRatio' & associated code.
 	* Added "Squeeze.Control.Concurrent.DivideAndConquer", called from "Main" to utilise multiple CPU-cores where available.
 	* Trapped command-line arguments to which garbage has been appended.
 	* Changed "Data.CommandOptions" to use 'Distribution.Verbosity.Verbosity'.
+1.0.4.0
+	* Corrected the Copyright-statement in 'Main.main.optDescrList.printVersion'.
+	* Renamed "Squeeze.Test.QC" to "Squeeze.Test.QuickChecks"; migrating its tests to a new module "Squeeze.Test.Squeeze", & added new tests 'prop_monotonicFileSize' & 'prop_uniqueFileNames', requiring package 'toolshed-0.14.0.0'; added module "Squeeze.Test.Data.FileCombinations".
+	* Amended the log-messages printed by "Main".
+	* Removed the command-line option 'graphPerformance', which printed only CPU-seconds rather than elapsed time, & therefore didn't correctly represent the multi-threaded implementation.
+	* Removed the printing of CPU-seconds when not testing performance; this is better left to '/usr/bin/time'.
+	* Compensated for the potential rejection of some virtual files after generation of a test-population.
+	* Used new class 'Factory.Math.Probability.Distribution' & bumped required version to 'factory-0.2.1.0'.
+	* Replaced the command-line option '--testPerformance', with '--testPerformanceContinuous' & '--testPerformanceDiscrete'.
 
diff --git a/copyright b/copyright
--- a/copyright
+++ b/copyright
@@ -2,7 +2,7 @@
 	Dr. Alistair Ward <squeeze 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/debian/DEBIAN/control b/debian/DEBIAN/control
--- a/debian/DEBIAN/control
+++ b/debian/DEBIAN/control
@@ -1,5 +1,5 @@
 Package:	squeeze
-Version:	1.0.3.0-1
+Version:	1.0.4.0-1
 Section:	utils
 Priority:	optional
 Architecture:	i386
diff --git a/man/man1/squeeze.1 b/man/man1/squeeze.1
--- a/man/man1/squeeze.1
+++ b/man/man1/squeeze.1
@@ -5,7 +5,8 @@
 \fBsqueeze\fR [\fIOPTIONS\fR] [\fIFile-path\fR ...]
 .SH DESCRIPTION
 .PP
-Finds the subset of the specified \fIFile-path\fRs, which fits into the specified space with least room to spare, whilst also meeting the minimum usage-requirements.
+Finds the subset of the specified \fIFile-path\fRs, which fits into the specified space with least room to spare, whilst also meeting the minimum usage-requirements;
+i.e. an instance of the "0-1 Knapsac problem".
 .PP
 Any directories amongst the specified \fIFile-path\fRs are treated as atomic units, & therefore only solutions which involve either all or none of the files contained, are returned.
 .PP
@@ -14,13 +15,13 @@
 .SH OPTIONS
 .SS "Selection"
 .TP
-\fB-M\fR, \fB--maximumBytes=\fR\fIBytes\fR
+\fB-M\fR \fIBytes\fR, \fB--maximumBytes=\fR\fIBytes\fR
 Defines the maximum available space, in bytes; defaulting to the space available on a DVD, i.e. \fB4700000000\fR bytes.
 .TP
-\fB-m\fR, \fB--minimumUsageRatio='\fR\fIRational\fR\fB'\fR
+\fB-m\fR \fIFloat\fR, \fB--minimumUsageRatio='\fR\fIFloat\fR\fB'\fR
 Defines the minimum acceptable usage-ratio (in the closed unit-interval [0,1]); defaulting to '\fB0.99\fR', i.e. 99% of \fBmaximumBytes\fR.
 .TP
-\fB-z\fR\fIBool\fR, \fB--includeEmpty\fR[\fB=\fR\fIBool\fR]
+\fB-z\fR[\fIBool\fR], \fB--includeEmpty\fR[\fB=\fR\fIBool\fR]
 When "\fBTrue\fR", any empty file or directory, can be a member of all solutions.
 .br
 The default value, in the absence of this option, is "\fBFalse\fR",
@@ -30,38 +31,35 @@
 .SS Test
 .TP
 \fB-q\fR, \fB--runQuickChecks\fR
-Validates some invariant properties, using arbitrary data ... & then exits.
+Tests the implementation, by validating some invariant properties using arbitrary data ... & then exits.
 .TP
-\fB-r\fR\fIInt\fR, \fB--randomSeed\fR[\fB=\fR\fIInt\fR]
+\fB-r\fR[\fIInt\fR], \fB--randomSeed\fR[\fB=\fR\fIInt\fR]
 This option takes an optional integral argument with which to seed the unique random-number generator used for all random operations.
 .br
 In the absence of this option, the random-number generator will be seeded unpredictably from the operating-system,
 but in the absence of only the integral argument, "\fB0\fR" will be inferred.
 .br
-CAVEAT: it must be specified before either "\fBtestPerformance\fR" or "\fBgraphPerformance\fR".
+CAVEAT: to be effective, this option must precede either "\fBtestPerformanceContinuous\fR" or "\fBtestPerformanceDiscrete\fR".
 .TP
-\fB--testPerformance='(\fR\fIInteger\fR\fB,\fR \fBPoissonDistribution\fR \fIlambda\fR\fB)'\fR
+\fB--testPerformanceContinuous='(\fR\fIInteger\fR\fB,\fR \fBLogNormalDistribution\fR \fIlocation\fR \fIscale^2\fR\fB)'\fR
 Measures the CPU-time required to find the best fit,
 for the specified number of randomly generated virtual files,
-the size of which conform to the specified probability-distribution,
-& the names of which reflect their size ... & then exits.
+the size of which conform to the specified probability-distribution, & then exits.
 .br
-NB: this test is performed on a single thread.
+"\fIA Large-Scale Study of File-System Contents by John R. Douceur and William J. Bolosky\fR" concludes that for arbitrary file-types,
+the frequency of file-sizes matches this distribution.
 .TP
-\fB--graphPerformance='\fR\fBPoissonDistribution\fR \fIlambda\fR\fB'\fR
+\fB--testPerformanceDiscrete='(\fR\fIInteger\fR\fB,\fR \fBPoissonDistribution\fR \fIlambda\fR\fB)'\fR
 Measures the CPU-time required to find the best fit,
-for a linearly increasing number of randomly generated virtual files,
-the size of which conform to the specified probability-distribution,
-& the names of which reflect their size.
-.br
-NB: this test is performed on a single thread.
+for the specified number of randomly generated virtual files,
+the size of which conform to the specified probability-distribution, & then exits.
 .br
-Doesn't normally terminate.
+CAVEAT: the standard-deviation for this distribution is narrower than observed.
 .TP
 \fB--verbosity=\fR\fBSilent\fR|\fBNormal\fR|\fBVerbose\fR|\fBDeafening\fR
 Produces additional explanatory output where appropriate.
 .br
-This option, if required, may need to precede other options.
+CAVEAT: to be effective, this option must precede other options.
 .SS "Generic Program-information"
 .TP
 \fB-v\fR, \fB--version\fR
@@ -107,9 +105,9 @@
 We can improve on that result if we're prepared to split some of the artist-specific directories into individual albums.
 With the expection of more solutions from which to select, we also raise the bar for what is acceptible.
 .br
-NB: from version \fB1.0.3.0\fR, the runtime can be passed a \fB-N\fR flag to request parallel execution on multiple cores.
-Selecting this option will not only result in reduced execution-time (given appropriate hardware),
-but as a side-effect will alter the selection of sub-optimal solutions to return.
+NB: from version \fB1.0.3.0\fR, the runtime can be passed a \fB-N\fR flag to request parallel execution on multiple cores,
+which as a side-effect, alters the selection of sub-optimal solutions to return.
+Whether this option actually results in reduced execution-time, depends on the file-size frequency-distribution & on the available hardware.
 .IP
 .B squeeze -M 700000000 -m 0.99999 ArabStrap BobDylan/* JeffBuckley JohnMartyn JoniMitchell ReservoirDogsOST RichardThompson SethLakeman SusheelaRaman TeddyThompson Vangelis +RTS -N
 .nf
@@ -137,7 +135,7 @@
 .SS "REPORTING BUGS"
 Report bugs to \fBsqueeze\fR \fIat\fR \fBfunctionalley\fR \fIdot\fR \fBeu\fR
 .SH COPYRIGHT
-Copyright \(co 2010 Dr. Alistair Ward
+Copyright \(co 2010-2013 Dr. Alistair Ward
 .PP
 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.
 .PP
@@ -150,4 +148,8 @@
 .IP \(bu
 Source-documentation is generated by \fBHaddock\fR, & is available in the distribution.
 .IP \(bu
-.I http://www.haskell.org/haddock/
+.B http://www.haskell.org/haddock/
+.IP \(bu
+.B http://en.wikipedia.org/wiki/Log-normal_distribution
+.IP \(bu
+.B http://en.wikipedia.org/wiki/Poisson_distribution
diff --git a/squeeze.cabal b/squeeze.cabal
--- a/squeeze.cabal
+++ b/squeeze.cabal
@@ -1,8 +1,8 @@
 --Package-properties
 Name:			squeeze
-Version:		1.0.3.0
+Version:		1.0.4.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
@@ -36,19 +36,21 @@
         Squeeze.Data.File
         Squeeze.Data.FileCombination
         Squeeze.Squeeze
+        Squeeze.Test.Data.FileCombinations
         Squeeze.Test.Performance
-        Squeeze.Test.QC
+        Squeeze.Test.QuickChecks
+        Squeeze.Test.Squeeze
 
     Build-depends:
         base == 4.*,
         Cabal >= 1.6 && < 2,
         directory,
-        factory >= 0.1,
+        factory >= 0.2.1.0,
         filepath,
         mtl,
         QuickCheck >= 2.4,
         random,
-        toolshed >= 0.13,
+        toolshed >= 0.14,
         unix
 
     GHC-options:	-Wall -O2
diff --git a/squeeze.spec b/squeeze.spec
--- a/squeeze.spec
+++ b/squeeze.spec
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Dr. Alistair Ward
+# Copyright (C) 2012-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
@@ -22,7 +22,7 @@
 
 Summary:	Finds the optimal subset of the specified files, to fit into a limited space, with minimal wastage
 Name:		squeeze
-Version:	1.0.3.0
+Version:	1.0.4.0
 Release:	1
 License:	GPLv3
 #From '/usr/share/doc/packages/rpm/GROUPS'.
@@ -61,6 +61,6 @@
 %attr(0644, root, root)	%doc	%_mandir/man1/%name.1.gz
 
 %changelog
-* Wed May 09 2012	Alistair Ward	<regexchar at functionalley dot eu>	1.0.2.4-1
+* Wed May 09 2012	Alistair Ward	<squeeze at functionalley dot eu>	1.0.2.4-1
 Removed "rm -rf -- '%buildroot'" from build, for security.
 Corrected "Paths" module, even though it's unused.
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,6 +1,5 @@
-{-# LANGUAGE CPP #-}
 {-
-	Copyright (C) 2010 Dr. Alistair Ward
+	Copyright (C) 2010-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
@@ -50,14 +49,13 @@
 import qualified	Squeeze.Data.FileCombination			as Data.FileCombination
 import qualified	Squeeze.Squeeze					as Squeeze
 import qualified	Squeeze.Test.Performance			as Test.Performance
-import qualified	Squeeze.Test.QC					as Test.QC
+import qualified	Squeeze.Test.QuickChecks			as Test.QuickChecks
 import qualified	System.Console.GetOpt				as G
 import qualified	System.Environment
 import qualified	System.Exit
 import qualified	System.FilePath
 import qualified	System.IO
 import qualified	System.IO.Error
-import qualified	Test.QuickCheck
 import qualified	Text.Printf
 import qualified	ToolShed.Defaultable
 import qualified	ToolShed.SelfValidate
@@ -89,9 +87,11 @@
 		bounded		= fromInteger unbounded
 
 {- |
-	* Recursively bisects the task, distributing the parts to 'Squeeze.findBestFit', to utilise the available CPU-cores.
+	* Recursively bisects the task, distributing the sub-tasks to 'Squeeze.findBestFit', to utilise the available CPU-cores.
 
-	* Recombines the part solutions to finds the single monotonically increasing list of file-combinations matching the specified criteria.
+	* Recombines the part solutions to finds the single monotonically increasing list of file-combinations matching the original criteria.
+
+	* CAVEAT: whilst the ultimate solution is similar, regardless of the specified number of CPU-cores available, the path leading to it typically differs.
 -}
 distribute
 	:: RealFrac ratio
@@ -110,22 +110,20 @@
 
 distribute numCapabilities commandOptions fileSizeAndPathList	= let
 	distribute'						= distribute $ numCapabilities `div` 2	--Partially apply.
-	(selectedFileSizeAndPath, remainingFileSizeAndPaths)	= Data.List.minimumBy (Data.Ord.comparing Data.File.getSize) &&& filter (/= selectedFileSizeAndPath) $ fileSizeAndPathList	--Balance the load on the two threads, by selecting the smallest file rather than the largest; this makes a big difference.
+	(selectedFileSizeAndPath, remainingFileSizeAndPaths)	= Data.List.minimumBy (Data.Ord.comparing Data.File.getSize) &&& filter (/= selectedFileSizeAndPath) $ fileSizeAndPathList	--Balance the load on the two threads, by selecting the smallest file rather than the largest; this makes a big difference !
  in do
-	Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions == maxBound) . System.IO.hPutStrLn System.IO.stderr $ "Splitting task into those including & those excluding, " ++ show selectedFileSizeAndPath
+	Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions == maxBound) . System.IO.hPutStrLn System.IO.stderr $ show numCapabilities ++ " CPU-cores => bisecting task into those, with & without, " ++ show selectedFileSizeAndPath
 
-	fileCombinationsExcludingSelected	<- distribute' commandOptions remainingFileSizeAndPaths	--Recurse.
+	fileCombinationsExcludingSelected	<- distribute' commandOptions remainingFileSizeAndPaths	--Recurse using all the original space, but a reduced list of files.
 	fileCombinationsIncludingSelected	<- map (
 		Data.FileCombination.prepend selectedFileSizeAndPath	--Prepend to all file-combinations, the file we previously removed.
 	 ) <$> distribute' (
 		commandOptions {
 			Data.CommandOptions.getMaximumBytes	= Data.CommandOptions.getMaximumBytes commandOptions - Data.File.getSize selectedFileSizeAndPath
-		} --Reduce the maximum size; there's no need to account for the minimum, since this s defined as a ratio of the maximum.
+		} --Reduce the maximum size; there's no need to account for the minimum, since this is defined as a ratio of the maximum.
 	 ) remainingFileSizeAndPaths	--Recurse.
 
-	Control.Concurrent.DivideAndConquer.divideAndConquer (
-		Data.FileCombination.risingMerge $ Data.Ord.comparing Data.FileCombination.getAggregateFileSize
-	 ) fileCombinationsExcludingSelected fileCombinationsIncludingSelected	--Merge the part-solutions.
+	Control.Concurrent.DivideAndConquer.divideAndConquer Data.FileCombination.risingMergeByAggregateFileSize fileCombinationsExcludingSelected fileCombinationsIncludingSelected	--Merge the part-solutions.
 
 {- |
 	* Parses the command-line arguments, to determine 'Data.CommandOptions.CommandOptions', which over-ride the default value.
@@ -137,8 +135,6 @@
 
 	* Delegates the donkey-work to 'distribute'.
 	Because this may take a long time, it prints the results in real time, rather than batching until the optimum has been determined.
-
-	* If /verbose/ has been specified, prints the CPU-time used.
 -}
 main :: IO ()
 main	= do
@@ -153,50 +149,51 @@
 
 		optDescrList :: [G.OptDescr CommandLineAction]
 		optDescrList	= [
---				 String	[String]		(G.ArgDescr CommandLineAction)				String
-			G.Option "?"	["help"]		(G.NoArg $ const printUsage)				"Display this help, & then exit.",
+--				 String	[String]		(G.ArgDescr CommandLineAction)			String
+			G.Option "?"	["help"]		(G.NoArg $ const printUsage)			"Display this help, & then exit.",
 			G.Option ""	["verbosity"]		(
 				setVerbosity `G.ReqArg` show [minBound :: Distribution.Verbosity.Verbosity .. maxBound]
-			)												("define the log-level; default '" ++ show (Data.CommandOptions.getVerbosity defaultCommandOptions) ++ "'. CAVEAT: to be effective, it must precede other options."
+			)											("Define the log-level; default '" ++ show (Data.CommandOptions.getVerbosity defaultCommandOptions) ++ "'. CAVEAT: to be effective, it must precede other options."
 			),
-			G.Option "v"	["version"]		(G.NoArg $ const printVersion)				"Print version-information, & then exit.",
-			G.Option "z"	["includeEmpty"]	(setIncludeEmpty `G.OptArg` "<Bool>")			("Whether empty files & directories may be included in any solution; default '" ++ show (Data.CommandOptions.getIncludeEmpty defaultCommandOptions) ++ "'."),
-			G.Option "M"	["maximumBytes"]	(setMaximumBytes `G.ReqArg` "<Int>")			("The maximum bytes of available space; default '" ++ show (Data.CommandOptions.getMaximumBytes defaultCommandOptions) ++ "'."),
-			G.Option "m"	["minimumUsageRatio"]	(setMinimumUsageRatio `G.ReqArg` "<Float>")		("The minimum acceptable space usage-ratio; default '" ++ show (realToFrac $ Data.CommandOptions.getMinimumUsageRatio defaultCommandOptions :: Double) ++ "'."),
-			G.Option "q"	["runQuickChecks"]	(G.NoArg runQuickChecks)				"Run Quick-checks using arbitrary data, & then exit.",
-			G.Option "r"	["randomSeed"]		(G.OptArg setRandomSeed "<Int>")			("Seed the random number-generator with the specified integer, to produce a repeatable pseudo-random sequence as required for performance-testing. If this option is unspecified then the seed is unpredictable, but if only its argument is unspecified then the seed defaults to '" ++ show defaultRandomSeed ++ "'. CAVEAT: to be effective, it must precede either 'testPerformance' or 'graphPerformance'."),
-			G.Option ""	["testPerformance"]	(
-				testPerformance `G.ReqArg` "(<Int>, <Probability-distribution>)"
-			)												"Test the single-threaded performance, using the specified number of randomly generated virtual files, the size of which conform to the specified probability-distribution, & then exit",
-			G.Option ""	["graphPerformance"]	(
-				graphPerformance `G.ReqArg` "<Probability-distribution>"
-			)												"Graph the single-threaded performance, against a linearly increasing number of randomly generated virtual files, the size of which conform to the specified probability-distribution. Doesn't normally terminate."
+			G.Option "v"	["version"]		(G.NoArg $ const printVersion)			"Print version-information, & then exit.",
+			G.Option "z"	["includeEmpty"]	(setIncludeEmpty `G.OptArg` "<Bool>")		("Whether empty files & directories may be included in any solution; default '" ++ show (Data.CommandOptions.getIncludeEmpty defaultCommandOptions) ++ "'."),
+			G.Option "M"	["maximumBytes"]	(setMaximumBytes `G.ReqArg` "<Int>")		("The maximum bytes of available space; default '" ++ show (Data.CommandOptions.getMaximumBytes defaultCommandOptions) ++ "'."),
+			G.Option "m"	["minimumUsageRatio"]	(setMinimumUsageRatio `G.ReqArg` "<Float>")	("The minimum acceptable space usage-ratio; default '" ++ show (realToFrac $ Data.CommandOptions.getMinimumUsageRatio defaultCommandOptions :: Double) ++ "'."),
+			G.Option "q"	["runQuickChecks"]	(G.NoArg runQuickChecks)			"Run Quick-checks using arbitrary data, & then exit.",
+			G.Option "r"	["randomSeed"]		(G.OptArg setRandomSeed "<Int>")		("Seed the random number-generator with the specified integer, to produce a repeatable pseudo-random sequence as required for performance-testing. If this option is unspecified then the seed is unpredictable, but if only its argument is unspecified then the seed defaults to '" ++ show defaultRandomSeed ++ "'. CAVEAT: to be effective, it must precede either 'testPerformanceContinuous' or 'testPerformanceDiscrete'."),
+			G.Option ""	["testPerformanceContinuous"]	(
+				testPerformanceContinuous `G.ReqArg` "(<Int>, <Continuous Distribution>)"
+			)											"Measure the CPU-seconds required for the specified number of randomly generated virtual files, the size of which conform to the specified continuous probability-distribution; & then exit.",
+			G.Option ""	["testPerformanceDiscrete"]	(
+				testPerformanceDiscrete `G.ReqArg` "(<Int>, <Discrete Distribution>)"
+			)											"Measure the CPU-seconds required for the specified number of randomly generated virtual files, the size of which conform to the specified discrete probability-distribution; & then exit."
 		 ] where
-			setMaximumBytes, setMinimumUsageRatio, setVerbosity, testPerformance, graphPerformance :: String -> CommandLineAction
+			setMaximumBytes, setMinimumUsageRatio, setVerbosity, testPerformanceContinuous, testPerformanceDiscrete :: String -> CommandLineAction
 			setMaximumBytes arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getMaximumBytes = readCommandArg arg }
 			setMinimumUsageRatio arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getMinimumUsageRatio = realToFrac (readCommandArg arg :: Double) }
 			setVerbosity arg commandOptions		= return {-to IO-monad-} commandOptions { Data.CommandOptions.getVerbosity = readCommandArg arg }
 
-			testPerformance arg commandOptions
+			testPerformanceContinuous arg commandOptions
 				| not $ ToolShed.SelfValidate.isValid commandOptions	= fail $ ToolShed.SelfValidate.getFirstError commandOptions
 				| otherwise						= do
-					ToolShed.System.TimeAction.printCPUSeconds $ Test.Performance.run commandOptions fileCount probabilityDistribution >>= mapM_ print
+					ToolShed.System.TimeAction.printCPUSeconds $ Test.Performance.run commandOptions fileCount probabilityDistribution >>= mapM_ print {-force evaluation-}
 
 					System.Exit.exitWith System.Exit.ExitSuccess
 				where
 					fileCount		:: Int
-					probabilityDistribution	:: Factory.Math.Probability.DiscreteDistribution Double
+					probabilityDistribution	:: Factory.Math.Probability.ContinuousDistribution Double
 					(fileCount, probabilityDistribution)	= readCommandArg arg
 
-			graphPerformance arg commandOptions
+			testPerformanceDiscrete arg commandOptions
 				| not $ ToolShed.SelfValidate.isValid commandOptions	= fail $ ToolShed.SelfValidate.getFirstError commandOptions
 				| otherwise						= do
-					mapM_ (\fileCount -> ToolShed.System.TimeAction.printCPUSeconds $ Test.Performance.run commandOptions fileCount probabilityDistribution >>= mapM_ print) [1 ..]
+					ToolShed.System.TimeAction.printCPUSeconds $ Test.Performance.run commandOptions fileCount probabilityDistribution >>= mapM_ print {-force evaluation-}
 
-					System.Exit.exitWith $ System.Exit.ExitFailure 1
+					System.Exit.exitWith System.Exit.ExitSuccess
 				where
+					fileCount		:: Int
 					probabilityDistribution	:: Factory.Math.Probability.DiscreteDistribution Double
-					probabilityDistribution	= readCommandArg arg
+					(fileCount, probabilityDistribution)	= readCommandArg arg
 
 			setIncludeEmpty, setRandomSeed :: Maybe String -> CommandLineAction
 			setIncludeEmpty arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getIncludeEmpty = Data.Maybe.maybe True readCommandArg arg }
@@ -205,20 +202,10 @@
 			runQuickChecks :: (Num f, Ord f, Show f) => Data.CommandOptions.CommandOptions f -> IO (Data.CommandOptions.CommandOptions f)
 			runQuickChecks commandOptions
 				| not $ ToolShed.SelfValidate.isValid commandOptions	= fail $ ToolShed.SelfValidate.getFirstError commandOptions
-				| otherwise						= do
-					Test.QC.quickChecks $ if Data.CommandOptions.getVerbosity commandOptions > Distribution.Verbosity.normal
-						then
-#if MIN_VERSION_QuickCheck(2,4,0)
-							Test.QuickCheck.verboseCheck
-#else
-							fail "'Test.QuickCheck.verboseCheck' is only available as of 'QuickCheck-2.4'."
-#endif
-						else Test.QuickCheck.quickCheck
-
-					System.Exit.exitWith System.Exit.ExitSuccess
+				| otherwise						= Test.QuickChecks.run >> System.Exit.exitWith System.Exit.ExitSuccess
 
 			printVersion, printUsage :: IO (Data.CommandOptions.CommandOptions f)
-			printVersion	= Text.Printf.printf "%s\n\n%s %s.\n%s.\n%s.\n%s %s.\n" packageName "Copyright (C) 2010" author "This program comes with ABSOLUTELY NO WARRANTY" "This is free software, & you are welcome to redistribute it under certain conditions" "Written by" author >> System.Exit.exitWith System.Exit.ExitSuccess	where
+			printVersion	= Text.Printf.printf "%s\n\n%s %s.\n%s.\n%s.\n%s %s.\n" packageName "Copyright (C) 2010-2013" author "This program comes with ABSOLUTELY NO WARRANTY" "This is free software, & you are welcome to redistribute it under certain conditions" "Written 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.
@@ -229,14 +216,16 @@
 				packageName	= Distribution.Text.display packageIdentifier
 				author		= "Dr. Alistair Ward"
 
-			printUsage	= Text.Printf.hPrintf System.IO.stderr "Usage:\t%s %s\n\nEBNF argument-format:\n\t%-23s = %s;\n\t%-23s = %s;\n\t%-23s = %s;\n\t%-23s = %s;\n\t%-23s = %s;\n\nE.g.\n\t%s\n\t%s\n" (
+			printUsage	= Text.Printf.hPrintf System.IO.stderr "Usage:\t%s  %s\n\nEBNF argument-format:\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\t%-22s = %s;\n\nE.g.\n\t%s\n\t%s\n\t%s\n" (
 				G.usageInfo progName optDescrList
-			 ) "[<File-path> ...]" "Bool" "\"True\" | \"False\"\t(* Case-sensitive *)" "Int" "[0-9]+" "File-path" (
+			 ) "[<File-path> ...]" "Bool" "\"True\" | \"False\"\t(* case-sensitive *)" "ContinuousDistribution" "LogNormalDistribution location scale^2" "DiscreteDistribution" "PoissonDistribution lambda" "File-path" (
 				"File-name ('" ++ [System.FilePath.pathSeparator] ++ "' File-name)*"
-			 ) "ProbabilityDistribution" "PoissonDistribution Int\t(* Defines both mean & variance *)" "Float" "Int ('.' Int)?" (
-				progName ++ " -M 700000000 *.ogg +RTS -N\t#Find the best-fit for the globbed file-names, into the space available on a CD, using multiple CPU-cores where available."
+			 ) "Float" "Int ('.' Int)?" "Int" "[0-9]+" "lambda" "Float\t(* the mean & variance of the distribution *)" "location" "Float\t(* the mean of the log of the distribution *)" "scale^2" "Float\t(* the variance of the log of the distribution *)" (
+				progName ++ " --verbosity=Verbose -M 700000000 *.ogg +RTS -N\t#Find the best-fit for the globbed file-names, into the space available on a CD, using multiple CPU-cores where available."
 			 ) (
-				progName ++ " --verbosity=Verbose -r --testPerformance='(100, PoissonDistribution 1000000000)'\t#Test performance."
+				progName ++ " -r --testPerformanceContinuous='(100, LogNormalDistribution 19.4 0.6)'\t#Test performance."
+			 ) (
+				progName ++ " -r --testPerformanceDiscrete='(100, PoissonDistribution 4e8)'\t#Test performance."
 			 ) >> System.Exit.exitWith System.Exit.ExitSuccess
 
 	args	<- System.Environment.getArgs
@@ -251,13 +240,11 @@
 			if not $ ToolShed.SelfValidate.isValid commandOptions
 				then fail $ ToolShed.SelfValidate.getFirstError commandOptions
 				else if null nonOptions
-					then fail "No file-paths specified"
-					else (
-						if Data.CommandOptions.getVerbosity commandOptions > Distribution.Verbosity.normal
-							then ToolShed.System.TimeAction.printCPUSeconds
-							else id
-					 ) $ do
-						filePaths	<- if "-" `elem` nonOptions
+					then fail "zero file-paths specified"
+					else let
+						standardInputProxy	= "-"
+					in do
+						filePaths	<- if standardInputProxy `elem` nonOptions
 							then let
 								getFilePaths :: IO Data.File.FilePathList
 								getFilePaths	= do
@@ -267,10 +254,10 @@
 										then return {-to IO-monad-} []
 										else {-more to read-} (:) <$> getLine <*> getFilePaths {-recurse-}
 							in do
-								filePaths	<- (filter (/= "-") nonOptions ++) <$> getFilePaths
+								filePaths	<- (filter (/= standardInputProxy) nonOptions ++) <$> getFilePaths
 
 								if null filePaths
-									then fail "No file-paths"
+									then fail "zero file-paths"
 									else return filePaths
 							else {-real fileNames-} return {-to IO-monad-} nonOptions
 
@@ -280,17 +267,14 @@
 						Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions > minBound) . System.IO.hPutStrLn System.IO.stderr $ Data.List.intercalate "\n" logFile
 
 						if null acceptedFileSizeAndPathList
-							then fail "There are zero suitable files"
+							then fail "there are zero suitable files"
 							else let
 								aggregateSize	= Data.File.aggregateSize acceptedFileSizeAndPathList
 								minimumBytes	= Data.CommandOptions.deriveMinimumBytes commandOptions
 							in if aggregateSize < minimumBytes
-								then fail $ "The aggregate size of all suitable files, is insufficient; " ++ show (aggregateSize, minimumBytes)
-								else do
-									numCapabilities	<- Control.Concurrent.getNumCapabilities	--Find how many CPU-cores are available.
-
-									Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions == maxBound) . System.IO.hPutStrLn System.IO.stderr $ show numCapabilities ++ " available CPU-cores"
-
-									mapM_ print {-lazy evaluation-} =<< distribute numCapabilities commandOptions acceptedFileSizeAndPathList
+								then fail $ "the aggregate size of all suitable files, is insufficient; " ++ show (aggregateSize, minimumBytes)
+								else Control.Concurrent.getNumCapabilities >>= (
+									\numCapabilities -> distribute numCapabilities commandOptions acceptedFileSizeAndPathList
+								) >>= mapM_ print {-lazy evaluation-}
 		(_, _, errors)	-> System.IO.Error.ioError . System.IO.Error.userError $ concatMap init {-chop-} errors
 
diff --git a/src/Squeeze/Control/Concurrent/DivideAndConquer.hs b/src/Squeeze/Control/Concurrent/DivideAndConquer.hs
--- a/src/Squeeze/Control/Concurrent/DivideAndConquer.hs
+++ b/src/Squeeze/Control/Concurrent/DivideAndConquer.hs
@@ -53,15 +53,15 @@
 	* Any attempt to read beyond the sentinel, will block forever since the writer is out of data.
 -}
 readListFromChan :: TerminatedChannel a -> IO [a]
-readListFromChan chan	= System.IO.Unsafe.unsafeInterleaveIO {-read on demand; i.e. lazy-} $ Control.Concurrent.Chan.readChan chan >>= Data.Maybe.maybe (
+readListFromChan chan	= System.IO.Unsafe.unsafeInterleaveIO {-read lazily, on demand-} $ Control.Concurrent.Chan.readChan chan >>= Data.Maybe.maybe (
 	return {-to IO-monad-} []
  ) (
 	\x	-> (x :) <$> readListFromChan chan {-recurse-}
  )
 
--- | Writes the two unevaluated lists to separate concurrent channels, reads them both & merges the results into a single list, evaluating in parallel, lazily on demand.
+-- | Writes the two unevaluated lists to separate concurrent channels, lazily reads them both (resulting in parallel evaluation) & merges them into a single results-list.
 divideAndConquer
-	:: ([a] -> [a] -> [a])	-- ^ Merge-function, which reads from the two channels, to produce a results-list.
+	:: ([a] -> [a] -> [a])	-- ^ Merge-function, which reads from two channels, to produce a third.
 	-> [a]			-- ^ Data for first channel.
 	-> [a]			-- ^ Data for second channel.
 	-> IO [a]
@@ -70,7 +70,6 @@
 	chan1	<- Control.Concurrent.Chan.newChan
 
 	Control.Monad.void . Control.Concurrent.forkIO $ writeListToChan chan0 l
-
 	Control.Monad.void . Control.Concurrent.forkIO $ writeListToChan chan1 r
 
 	merge <$> readListFromChan chan0 <*> readListFromChan chan1
diff --git a/src/Squeeze/Data/FileCombination.hs b/src/Squeeze/Data/FileCombination.hs
--- a/src/Squeeze/Data/FileCombination.hs
+++ b/src/Squeeze/Data/FileCombination.hs
@@ -25,23 +25,29 @@
 -- ** Data-types
 	FileCombination(
 --		MkFileCombination,
-		getAggregateFileSize
---		getFilePathList
+		getAggregateFileSize,
+		getFilePathList
 	),
 -- * Constants
 	nullFileCombination,
 -- * Functions
+	comparingAggregateFileSize,
 	prepend,
 	risingFilter,
-	risingMerge,
+--	risingMerge,
+	risingMergeByAggregateFileSize,
 -- ** Constructors
+	mkFileCombination,
 	singleton,
 -- ** Predicates
 	hasSizeBy
 ) where
 
 import qualified	Data.List
+import qualified	Data.Ord
 import qualified	Squeeze.Data.File	as Data.File
+import qualified	ToolShed.Data.Foldable
+import qualified	ToolShed.SelfValidate
 
 -- | Declare a list of files qualified by its aggregate size.
 data FileCombination	= MkFileCombination {
@@ -52,18 +58,42 @@
 instance Show FileCombination where
 	showsPrec _ fileCombination	= shows (getAggregateFileSize fileCombination) . showChar '\t' . shows (Data.List.sort $ getFilePathList fileCombination)
 
+instance ToolShed.SelfValidate.SelfValidator FileCombination	where
+	getErrors fileCombination	= ToolShed.SelfValidate.extractErrors [
+		(
+			getAggregateFileSize fileCombination < 0,
+			"aggregate size must be positive; " ++ show fileCombination
+		), (
+			getAggregateFileSize fileCombination /= 0 && null (getFilePathList fileCombination),
+			"when zero files are specified, the aggregate size must also be zero; " ++ show fileCombination
+		), let
+			duplicateFilePaths	= map head . filter ((> 1) . length) . ToolShed.Data.Foldable.gather $ getFilePathList fileCombination
+		in (
+			not $ null duplicateFilePaths,
+			"duplicate file-paths have been specified; " ++ show duplicateFilePaths
+		)
+	 ]
+
 -- | A constant empty instance.
 nullFileCombination :: FileCombination
 nullFileCombination	= MkFileCombination 0 []
 
+-- | Smart constructor.
+mkFileCombination :: Data.File.FileSize -> Data.File.FilePathList -> FileCombination
+mkFileCombination fileSize filePathList
+	| ToolShed.SelfValidate.isValid fileCombination	= fileCombination
+	| otherwise					= error $ "Squeeze.Data.FileCombination.mkFileCombination:\t" ++ ToolShed.SelfValidate.getFirstError fileCombination
+	where
+		fileCombination	= MkFileCombination fileSize filePathList
+
 -- | Construct a 'FileCombination' from a single 'Data.File.FileSizeAndPath'.
 singleton ::  Data.File.FileSizeAndPath -> FileCombination
-singleton (fileSize, filePath)	= MkFileCombination fileSize [filePath]
+singleton (fileSize, filePath)	= mkFileCombination fileSize [filePath]
 
 {- |
 	* Prepend a 'Data.File.FileSizeAndPath' to an existing 'FileCombination'.
 
-	* CAVEAT: performance hot-spot.
+	* CAVEAT: performance hot-spot, so it by-passes the checks made by 'mkFileCombination'.
 -}
 {-# INLINE prepend #-}
 prepend
@@ -99,7 +129,7 @@
 	* Merges two lists of monotonically increasing values, into a single monotonically increasing list, by dropping values which compare less than results already found.
 
 	* CAVEAT: both lists must produce an element, in order to determine which is selected.
-	As a result, if one list produces a value, it can't be returned until the other does (which make take a long time), even if ultimately the first is then selected.
+	As a result, though one list produces a value, it can't be returned until the other does (which make take a long time), even if ultimately the first is then selected.
 -}
 risingMerge
 	:: (FileCombination -> FileCombination -> Ordering)	-- ^ Comparator used to select the best file-combination from the heads of the two list supplied.
@@ -107,8 +137,10 @@
 	-> [FileCombination]					-- ^ A list of monotonically increasing file-combinations.
 	-> [FileCombination]
 risingMerge cmp	= slave nullFileCombination	where
-	slave bar [] r	= dropWhile ((== LT) . (`cmp` bar)) r
-	slave bar l []	= dropWhile ((== LT) . (`cmp` bar)) l
+	lessThan bar	= (== LT) . (`cmp` bar)
+
+	slave bar [] r	= dropWhile (lessThan bar) r
+	slave bar l []	= dropWhile (lessThan bar) l
 	slave _ (x : xs) (y : ys)
 		| o == GT	= x : slave x xs ys
 		| o == LT	= y : slave y xs ys
@@ -116,3 +148,17 @@
 		where
 			o	= x `cmp` y
 
+-- Compares two /file-combination/s by their aggregate file-size.
+comparingAggregateFileSize :: FileCombination -> FileCombination -> Ordering
+comparingAggregateFileSize	= Data.Ord.comparing getAggregateFileSize
+
+{- |
+	Merges two lists of monotonically increasing lists of file-combinations,
+	into a single monotonically increasing list,
+	by dropping values which have a smaller aggregate size than results already found.
+-}
+risingMergeByAggregateFileSize
+	:: [FileCombination]	-- ^ A list of monotonically increasing file-combinations.
+	-> [FileCombination]	-- ^ A list of monotonically increasing file-combinations.
+	-> [FileCombination]
+risingMergeByAggregateFileSize	= risingMerge comparingAggregateFileSize
diff --git a/src/Squeeze/Test/Data/FileCombinations.hs b/src/Squeeze/Test/Data/FileCombinations.hs
new file mode 100644
--- /dev/null
+++ b/src/Squeeze/Test/Data/FileCombinations.hs
@@ -0,0 +1,49 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-
+	Copyright (C) 2010 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 tests to dynamically verify the module "Squeeze.Data.FileCombinations" against arbitrary data.
+-}
+
+module Squeeze.Test.Data.FileCombinations(
+-- * Functions
+	quickChecks
+) where
+
+import			Control.Applicative((<$>))
+import			Control.Arrow((&&&))
+import qualified	Data.List
+import qualified	Squeeze.Data.FileCombination	as Data.FileCombination
+import qualified	Test.QuickCheck
+
+instance Test.QuickCheck.Arbitrary Data.FileCombination.FileCombination	where
+	arbitrary	= do
+		filePathList		<- Data.List.nub <$> (Test.QuickCheck.elements [0 .. 99] >>= Test.QuickCheck.vector)
+		aggregateFileSize	<- Test.QuickCheck.elements [0 .. Data.List.genericLength filePathList]	--Assume files of size either zero or one byte.
+
+		return $ Data.FileCombination.mkFileCombination aggregateFileSize filePathList
+
+-- | Defines invariant properties.
+quickChecks :: IO ()
+quickChecks	= Test.QuickCheck.quickCheck `mapM_` [prop_risingFilter, prop_risingFilter', prop_risingMergeByAggregateFileSize]	where
+	prop_risingFilter, prop_risingFilter', prop_risingMergeByAggregateFileSize :: [Data.FileCombination.FileCombination] -> Test.QuickCheck.Property
+	prop_risingFilter	= Test.QuickCheck.label "prop_risingFilter" . uncurry (==) . (id &&& Data.List.sort) . map Data.FileCombination.getAggregateFileSize . Data.FileCombination.risingFilter 0
+	prop_risingFilter'	= Test.QuickCheck.label "prop_risingFilter'" . uncurry (==) . (id &&& Data.FileCombination.risingFilter 0) . Data.List.sortBy Data.FileCombination.comparingAggregateFileSize
+
+	prop_risingMergeByAggregateFileSize	= Test.QuickCheck.label "prop_risingMergeByAggregateFileSize" . uncurry (==) . (id &&& map head . Data.List.group . uncurry Data.FileCombination.risingMergeByAggregateFileSize . (id &&& id)) . Data.FileCombination.risingFilter 0
diff --git a/src/Squeeze/Test/Performance.hs b/src/Squeeze/Test/Performance.hs
--- a/src/Squeeze/Test/Performance.hs
+++ b/src/Squeeze/Test/Performance.hs
@@ -25,7 +25,6 @@
 	run
 ) where
 
-import			Control.Arrow((&&&))
 import qualified	Control.Monad
 import qualified	Control.Monad.Writer
 import qualified	Data.List
@@ -39,23 +38,34 @@
 import qualified	System.Random
 
 {- |
-	* Find the best-fit, into the available space, using a set composed from the specified number of /virtual/ files.
+	* Find the best-fit, into the available space, using a set composed from the specified number of virtual files.
 
-	* The files have a random size, conforming to the requested distribution.
+	* The files have a random size, conforming to the specified distribution.
 
-	* The generated file-name, arbitrarily reflects its size.
+	* The generated file-name, is merely a counter.
+
+	* CAVEAT: fewer files than requested may be used, because depending on the requested distribution, some may overflow the maximum permissible size.
 -}
-run :: (RealFrac ratio, RealFloat f, Show f, System.Random.Random f)
+run :: (Factory.Math.Probability.Distribution distribution, RealFrac ratio)
 	=> Data.CommandOptions.CommandOptions ratio
-	-> Int	-- ^ The number of /virtual/ files to randomly generate.
-	-> Factory.Math.Probability.DiscreteDistribution f
+	-> Int	-- ^ The number of virtual files to randomly generate.
+	-> distribution
 	-> IO [Data.FileCombination.FileCombination]
 run commandOptions fileCount probabilityDistribution	= do
 	randomGen	<- Data.Maybe.maybe System.Random.getStdGen {-use the global random-number generator-} (
 		return {-to IO-monad-} . System.Random.mkStdGen	--Seed the random-number generator as specified.
 	 ) $ Data.CommandOptions.getMaybeRandomSeed commandOptions	--Select a random-number generator.
 
-	let (acceptedFileSizeAndPathList, logFile)	= Control.Monad.Writer.runWriter . Data.File.selectSuitableFileSizes (Data.CommandOptions.isWithinSizeBounds commandOptions) . Data.List.nub . map (id &&& show) $ Factory.Math.Probability.generateDiscretePopulation fileCount probabilityDistribution randomGen
+	let
+		(acceptedFileSizeAndPathList, logFile)	= Control.Monad.Writer.runWriter . Data.File.selectSuitableFileSizes (
+			Data.CommandOptions.isWithinSizeBounds commandOptions	--This may reduce the requested number of files, but compensating distorts the requested distribution.
+		 ) . (
+			`zip` map show [0 :: Int ..]	--Construct a Data.File.FileSizeAndPath, by using a counter as the file-name.
+		 ) . take fileCount . filter (
+			/= 0	--Though a valid file-size, it doesn't work well when finding valid combinations.
+		 ) . map (
+			ceiling :: Double -> Integer	--File-sizes are integral.
+		 ) $ Factory.Math.Probability.generatePopulation probabilityDistribution randomGen
 
 	Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions > minBound) . System.IO.hPutStrLn System.IO.stderr $ Data.List.intercalate "\n" logFile
 
diff --git a/src/Squeeze/Test/QC.hs b/src/Squeeze/Test/QC.hs
deleted file mode 100644
--- a/src/Squeeze/Test/QC.hs
+++ /dev/null
@@ -1,70 +0,0 @@
-{-
-	Copyright (C) 2010 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 tests to dynamically verify the module "Squeeze.Squeeze" against arbitrary data.
--}
-
-module Squeeze.Test.QC(
--- * Types
--- ** Type-synonyms
---	Testable,
--- * Functions
-	quickChecks
-) where
-
-import qualified	Control.Arrow
-import			Control.Arrow((&&&))
-import qualified	Data.List
-import qualified	Factory.Data.Interval
-import qualified	Squeeze.Data.File		as Data.File
-import qualified	Squeeze.Data.FileCombination	as Data.FileCombination
-import qualified	Squeeze.Squeeze			as Squeeze
-import qualified	Test.QuickCheck
-
-type Testable	= [Data.File.FileSizeAndPath] -> Test.QuickCheck.Property
-
--- | Defines invariant properties, which must hold for any 'FileSizeAndPath'.
-quickChecks :: (Testable -> IO ()) -> IO ()
-quickChecks	= (`mapM_` [prop_totalCombinations, prop_bounds, prop_all, prop_monotonic])	where
-	translate :: [Data.File.FileSizeAndPath] -> [Data.File.FileSizeAndPath]
-	translate	= Data.List.nub . map (Control.Arrow.first abs)
-
-	prop_totalCombinations, prop_bounds, prop_all :: Testable
-	prop_totalCombinations fileSizeAndPathList	=  Test.QuickCheck.label "prop_totalCombinations" $ (length . Data.List.nub . Squeeze.findCombinations (0, fromIntegral (maxBound :: Int)) $ Data.File.orderBySize l) == 2 ^ length l where
-		l	= take 10 {-arbitrarily-} $ translate fileSizeAndPathList	--CAVEAT: may be shorter than requested.
-
-	prop_bounds fileSizeAndPathList	=  Test.QuickCheck.label "prop_bounds" . all ((`Factory.Data.Interval.elem'` fileSizeBounds) . Data.FileCombination.getAggregateFileSize) . Squeeze.findCombinations fileSizeBounds $ Data.File.orderBySize l	where
-		l	= take 16 {-arbitrarily-} $ translate fileSizeAndPathList	--CAVEAT: may be shorter than requested.
-
-		fileSizeBounds :: Factory.Data.Interval.Interval Data.File.FileSize
-		fileSizeBounds	= (minimumBytes, maximumBytes)	where
-			maximumBytes, minimumBytes :: Data.File.FileSize
-			maximumBytes	= round $ fromIntegral (Data.File.aggregateSize l) / (4.0 :: Double)	--Arbitrarily.
-			minimumBytes	= maximumBytes `div` 2	--Arbitrarily.
-
-	prop_all fileSizeAndPathList	= Test.QuickCheck.label "prop_all" . (== bytes) . last . map Data.FileCombination.getAggregateFileSize $ Squeeze.findBestFit (Factory.Data.Interval.precisely bytes) l	where
-		l	= take 16 {-arbitrarily-} $ translate fileSizeAndPathList	--CAVEAT: may be shorter than requested.
-
-		bytes :: Data.File.FileSize
-		bytes	= Data.File.aggregateSize l
-
-	prop_monotonic :: Testable
-	prop_monotonic fileSizeAndPathList	= Test.QuickCheck.label "prop_monotonic" . uncurry (==) . (id &&& Data.List.sort) . map Data.FileCombination.getAggregateFileSize $ Squeeze.findBestFit (0, fromIntegral (maxBound :: Int)) l	where
-		l	= take 16 {-arbitrarily-} $ translate fileSizeAndPathList	--CAVEAT: may be shorter than requested.
-
diff --git a/src/Squeeze/Test/QuickChecks.hs b/src/Squeeze/Test/QuickChecks.hs
new file mode 100644
--- /dev/null
+++ b/src/Squeeze/Test/QuickChecks.hs
@@ -0,0 +1,39 @@
+{-
+	Copyright (C) 2010 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 tests to dynamically verify the application against arbitrary data.
+-}
+
+module Squeeze.Test.QuickChecks(
+-- * Functions
+	run
+) where
+
+import qualified	Control.Arrow
+import qualified	Squeeze.Test.Data.FileCombinations	as Test.Data.FileCombinations
+import qualified	Squeeze.Test.Squeeze			as Test.Squeeze
+
+-- | Runs checks on invariant properties.
+run :: IO ()
+run = mapM_ (
+	uncurry (>>) . Control.Arrow.first putStrLn
+ ) [
+	("Data.FileCombinations",	Test.Data.FileCombinations.quickChecks),
+	("Squeeze",			Test.Squeeze.quickChecks)
+ ]
diff --git a/src/Squeeze/Test/Squeeze.hs b/src/Squeeze/Test/Squeeze.hs
new file mode 100644
--- /dev/null
+++ b/src/Squeeze/Test/Squeeze.hs
@@ -0,0 +1,70 @@
+{-
+	Copyright (C) 2010 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 tests to dynamically verify the module "Squeeze.Squeeze" against arbitrary data.
+-}
+
+module Squeeze.Test.Squeeze(
+-- * Functions
+	quickChecks
+) where
+
+import			Control.Arrow((&&&))
+import qualified	Data.List
+import qualified	Factory.Data.Interval
+import qualified	Squeeze.Data.File		as Data.File
+import qualified	Squeeze.Data.FileCombination	as Data.FileCombination
+import qualified	Squeeze.Squeeze			as Squeeze
+import qualified	Test.QuickCheck
+import qualified	ToolShed.Data.Foldable
+import qualified	ToolShed.Data.List
+
+-- | Defines invariant properties.
+quickChecks :: IO ()
+quickChecks	= Test.QuickCheck.quickCheck `mapM_` [prop_totalCombinations, prop_fileSizeBounds, prop_allFiles, prop_monotonicFileSize, prop_uniqueFileNames]	where
+	mkFileSizeAndPathList :: (Integral i, Show i) => [i] -> [Data.File.FileSizeAndPath]
+	mkFileSizeAndPathList	= take 12 . Data.List.nubBy (ToolShed.Data.List.equalityBy snd) . map (toInteger . abs &&& show)	--CAVEAT: may be shorter than requested.
+
+	prop_totalCombinations, prop_fileSizeBounds, prop_allFiles, prop_monotonicFileSize, prop_uniqueFileNames :: [Integer] -> Test.QuickCheck.Property
+	prop_totalCombinations integers =  Test.QuickCheck.label "prop_totalCombinations" $ (length . Data.List.nub . Squeeze.findCombinations (0, fromIntegral (maxBound :: Int)) $ Data.File.orderBySize fileSizeAndPathList) == 2 ^ length fileSizeAndPathList where
+		fileSizeAndPathList	= mkFileSizeAndPathList integers
+
+	prop_fileSizeBounds integers	=  Test.QuickCheck.label "prop_fileSizeBounds" . all ((`Factory.Data.Interval.elem'` fileSizeBounds) . Data.FileCombination.getAggregateFileSize) . Squeeze.findCombinations fileSizeBounds $ Data.File.orderBySize fileSizeAndPathList	where
+		fileSizeAndPathList	= mkFileSizeAndPathList integers
+
+		fileSizeBounds :: Factory.Data.Interval.Interval Data.File.FileSize
+		fileSizeBounds	= (minimumBytes, maximumBytes)	where
+			maximumBytes, minimumBytes :: Data.File.FileSize
+			maximumBytes	= round $ fromIntegral (Data.File.aggregateSize fileSizeAndPathList) / (4.0 :: Double)	--Arbitrarily.
+			minimumBytes	= maximumBytes `div` 2	--Arbitrarily.
+
+	prop_allFiles integers	= Test.QuickCheck.label "prop_allFiles" . (== aggregateSize) . last . map Data.FileCombination.getAggregateFileSize $ Squeeze.findBestFit (Factory.Data.Interval.precisely aggregateSize) fileSizeAndPathList	where
+		fileSizeAndPathList	= mkFileSizeAndPathList integers
+
+		aggregateSize :: Data.File.FileSize
+		aggregateSize	= Data.File.aggregateSize fileSizeAndPathList
+
+	prop_monotonicFileSize integers	= Test.QuickCheck.label "prop_monotonicFileSize" . uncurry (==) . (id &&& Data.List.sort) . map Data.FileCombination.getAggregateFileSize $ Squeeze.findBestFit (0, fromIntegral (maxBound :: Int)) fileSizeAndPathList	where
+		fileSizeAndPathList	= mkFileSizeAndPathList integers
+
+	prop_uniqueFileNames integers	= Test.QuickCheck.label "prop_uniqueFileNames" . all (
+		all ((== 1) . length) . ToolShed.Data.Foldable.gather . Data.FileCombination.getFilePathList
+	 ) $ Squeeze.findBestFit (0, fromIntegral (maxBound :: Int)) fileSizeAndPathList	where
+		fileSizeAndPathList	= mkFileSizeAndPathList integers
+
