packages feed

fishfood 0.0.1.4 → 0.0.1.5

raw patch · 25 files changed

+913/−811 lines, 25 filesdep +fishfooddep ~Cabaldep ~toolshed

Dependencies added: fishfood

Dependency ranges changed: Cabal, toolshed

Files

+ README.markdown view
@@ -0,0 +1,30 @@+# **FishFood**.++This is **FishFood**, an application which determines the distribution in sizes of the specified files.++## Installation++It can be built and installed using [Cabal](https://www.haskell.org/cabal/users-guide/installing-packages.html).++## Documentation++The directory **man/** contains the documentation.++## License++For information on copying and distributing this package, see the file **LICENSE** in this directory.++## Bug-reporting++Bug-reports should be emailed to <fishfood *at* functionalley *dot* eu>.++## Testing++The test-suite can be run using:+	cabal configure --enable-tests+	cabal build+	cabal test --show-details=always++## Author++This application is written and maintained by Dr. Alistair Ward.
− changelog
@@ -1,23 +0,0 @@-2013-07-16 Dr. Alistair Ward <fishfood at functionalley dot eu>--0.0.0.1-	* First version of the package.-0.0.0.2-	* Added 'man/man1/fishfood.1' & 'fishfood.spec' to the distribution.-0.0.1.0-	* Migrated all but the I/O, from "Main" to a new module "FishFood.Profiler".-	* Added module "FishFood.Test", containing "Test.QuickCheck"-implementation.-	* Added Command-line options; '--deriveProbabilityMassFunction', '--binSizeRatio', '--runQuickChecks'.-0.0.1.1-	* Added files to build .deb to 'fishfood.cabal'.-0.0.1.2-	* Added '--runQuickChecks' to 'man/man1/fishfood.1' & corrected typo.-	* Improved the syntax of 'verbosity' in the usage-message.-	* Tested with 'haskell-platform-2013.2.0.0'.-0.0.1.3-	* In module "FishFood.Data.File", replaced 'Control.Exception.throw' with 'Control.Exception.throwIO'.-	* Either replaced instances of '(<$>)' with 'fmap' to avoid ambiguity between "Control.Applicative" & "Prelude" which (from 'base-4.8') also exports this symbol, or hid the symbol when importing the "Prelude"..-	* In module "Main", redirected version-message to stderr.-0.0.1.4-	* Added the compiler to the output returned for the command-line option "version".-	* In module "FishFood.Data.File.hs", replaced the use of the package "unix" with the package "directory", for compatibility with Windows.
+ changelog.markdown view
@@ -0,0 +1,31 @@+# 2013-07-16 Dr. Alistair Ward <fishfood at functionalley dot eu>++## 0.0.0.1+	* First version of the package.+## 0.0.0.2+	* Added **man/man1/fishfood.1** & *.spec*-file to the distribution.+## 0.0.1.0+	* Migrated all but the I/O, from **Main** to a new module **FishFood.Profiler**.+	* Added module **FishFood.Test**, containing **Test.QuickCheck**-implementation.+	* Added Command-line options; **--deriveProbabilityMassFunction**, **--binSizeRatio**, **--runQuickChecks**.+## 0.0.1.1+	* Added files to build *.deb* to the *.cabal*-file.+## 0.0.1.2+	* Added **--runQuickChecks** to **man/man1/fishfood.1** & corrected typo.+	* Improved the syntax of **verbosity** in the usage-message.+	* Tested with **haskell-platform-2013.2.0.0**.+## 0.0.1.3+	* In module **FishFood.Data.File**, replaced `Control.Exception.throw` with `Control.Exception.throwIO`.+	* Either replaced instances of `(<$>)` with `fmap` to avoid ambiguity between **Control.Applicative** & **Prelude** which (from **base-4.8**) also exports this symbol, or hid the symbol when importing the **Prelude**..+	* In module **Main**, redirected version-message to stderr.+## 0.0.1.4+	* Added the compiler to the output returned for the command-line option **version**.+	* In module **FishFood.Data.File.hs**, replaced the use of the package **unix** with the package **directory**, for compatibility with Windows.+## 0.0.1.5+	* Added **Default-language**-specification to the *.cabal*-file.+	* Added file **README.markdown**.+	* Converted this file to markdown-format.+	* Replaced `System.Exit.exitWith System.Exit.ExitSuccess` with `System.Exit.exitSuccess`.+	* Moved the entry-point to the test-suite from **Main.hs** to **Test.hs**, both to integrate with **cabal** & to minimise the dependencies of the executable.+	* Partitioned the source-files into **src-lib**, **src-exe**, & **src-test** directories, & referenced them individually from the *.cabal*-file to avoid repeated compilation.+	* Used **CPP** to control the import of symbols from **Control.Applicative**.
debian/DEBIAN/control view
@@ -1,11 +1,10 @@ Package:	fishfood-Version:	0.0.1.4-1+Version:	0.0.1.5-1 Section:	utils Priority:	optional-#Architecture:	i386 Architecture:	amd64 Depends:	libc6-Installed-size:	2252+Installed-size:	2082 Maintainer:	Dr. Alistair Ward <fishfood at functionalley dot eu> Provides: Homepage:	http://functionalley.eu
fishfood.cabal view
@@ -1,54 +1,92 @@--- Package-properties-Name:			fishfood-Version:		0.0.1.4-Cabal-Version:		>= 1.6-Copyright:		(C) 2013-2015 Dr. Alistair Ward-License:		GPL-License-file:		LICENSE-Author:			Dr. Alistair Ward-Stability:		Unstable interface, incomplete features.-Synopsis:		Calculates file-size frequency-distribution.-Build-Type:		Simple-Description:		Counts the number of files in a set of bins, each of which holds only those files which fall within a specific size-interval.-Category:		Utils-Tested-With:		GHC == 7.4, GHC == 7.6, GHC == 7.10-Homepage:		http://functionalley.eu-Maintainer:		fishfood <at> functionalley <dot> eu-Bug-reports:		fishfood <at> functionalley <dot> eu-Data-files:-Extra-Source-Files:	changelog changelog.Debian copyright debian/DEBIAN/control fishfood.spec man/man1/fishfood.1+-- This file is part of FishFood.+--+-- FishFood 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.+--+-- FishFood 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 FishFood.  If not, see <http://www.gnu.org/licenses/>. --- Turn on using: 'runhaskell ./Setup.hs configure -f llvm'.+Name:		fishfood+Version:	0.0.1.5+Cabal-version:	>= 1.10+Copyright:	(C) 2013-2015 Dr. Alistair Ward+License:	GPL+License-file:	LICENSE+Author:		Dr. Alistair Ward+Stability:	stable+Synopsis:	Calculates file-size frequency-distribution.+Build-type:	Simple+Description:	Counts the number of files in a set of bins, each of which holds only those files which fall within a specific size-interval.+Category:	Utils+Tested-with:	GHC == 7.4, GHC == 7.6, GHC == 7.8, GHC == 7.10+Homepage:	http://functionalley.eu+Maintainer:	mailto <colon> fishfood <at> functionalley <dot> eu+Bug-reports:	mailto <colon> fishfood <at> functionalley <dot> eu++-- None of these files are needed at run-time.+Extra-source-files:+    changelog.markdown+    changelog.Debian+    copyright+    debian/DEBIAN/control+    fishfood.spec+    man/man1/fishfood.1+    README.markdown++-- Enable using: 'cabal configure -f llvm'. flag llvm     Description:	Whether the 'llvm' compiler-backend has been installed and is required for code-generation.-    manual:		True-    default:		False+    Manual:		True+    Default:		False -Executable fishfood-    hs-source-dirs:	src-    Main-Is:		Main.hs-    Other-modules:-        FishFood.Data.CommandOptions,-        FishFood.Data.File,+Library+    Default-language:	Haskell2010+    GHC-options:	-Wall -fno-warn-tabs+    Hs-source-dirs:	src-lib++    Exposed-modules:+        FishFood.Data.CommandOptions+        FishFood.Data.File         FishFood.Data.Verbosity         FishFood.Profiler-        FishFood.Test.Data.CommandOptions-        FishFood.Test.Data.Verbosity-        FishFood.Test.Profiler-        FishFood.Test.QuickChecks      Build-depends:         base == 4.*,-        Cabal >= 1.6 && < 2,+        Cabal >= 1.10,         containers,         directory,         factory >= 0.2.0.5,         mtl,-        QuickCheck,-        toolshed >= 0.13+        toolshed >= 0.16 +    if impl(ghc >= 7.4.1)+        GHC-prof-options:	-prof -fprof-auto -fprof-cafs+    else+        GHC-prof-options:	-prof -auto-all -caf-all++    if impl(ghc >= 7.0) && flag(llvm)+        GHC-options:	-fllvm++Executable fishfood+    Default-language:	Haskell2010     GHC-options:	-Wall -fno-warn-tabs+    Hs-source-dirs:	src-exe+    Main-is:		Main.hs +    Build-depends:+        base == 4.*,+        Cabal >= 1.10,+        fishfood,+        mtl,+        toolshed >= 0.16+     if impl(ghc >= 7.4.1)         GHC-prof-options:	-prof -fprof-auto -fprof-cafs     else@@ -59,4 +97,26 @@          if flag(llvm)             GHC-options:	-fllvm++Test-Suite quickCheck+    Default-language:	Haskell2010+    GHC-options:	-Wall -fno-warn-tabs+    Hs-source-dirs:	src-test+    Main-is:		Main.hs+    Type:		exitcode-stdio-1.0++-- Required for 'cabal sdist'.+    Other-modules:+        FishFood.Test.Data.CommandOptions+        FishFood.Test.Data.Verbosity+        FishFood.Test.Profiler++    Build-depends:+        base == 4.*,+        Cabal >= 1.10,+        containers,+        fishfood,+        mtl,+        QuickCheck,+        toolshed >= 0.16 
fishfood.spec view
@@ -1,4 +1,4 @@-# Copyright (C) 2013-2014 Dr. Alistair Ward+# Copyright (C) 2013-2015 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@@ -15,51 +15,53 @@  %define package		%name-%version %define tarBall		%package.tar.gz-%define _bindir		%prefix/bin %define _sharedir	%prefix/share-%define _docdir		%_sharedir/doc/%package+%define _docdir		%_sharedir/doc/%name %define _mandir		%_sharedir/man  Summary:	Calculates file-size frequency-distribution. Name:		fishfood-Version:	0.0.1.4+Version:	0.0.1.5 Release:	1 License:	GPLv3-#From '/usr/share/doc/packages/rpm/GROUPS'.+# From '/usr/share/doc/packages/rpm/GROUPS'. Group:		Applications/File Source0:	http://functionalley.eu/Downloads/sdist/%tarBall URL:		http://functionalley.eu Prefix:		/usr-BuildRequires:	ghc+BuildRequires:	haskell-platform  %description Counts the number of files in a set of bins, each of which holds only those files which fall within a specific size-interval.  %prep-#NB: we've changed directory to %_builddir-(cd $OLDPWD && runhaskell Setup sdist) && tar -zxf $OLDPWD/dist/%tarBall	#Make a source-distribution & unpack it into the build-directory.-cd '%package/' && runhaskell Setup configure --user --prefix=%prefix		#Configure to use the user's personal package-database & to generate an appropriate "Paths" module.+# NB: CWD has changed to %_builddir+echo 'package="%package", prefix="%prefix", _builddir="%_builddir", buildroot="%buildroot"'+(cd $OLDPWD && cabal sdist) && tar -zxf $OLDPWD/dist/%tarBall	# Make a source-distribution & unpack it into the build-directory.+cd '%package/' && cabal configure --user --prefix='%prefix' --docdir='%_docdir'	# Tell cabal to use the user's personal package-database, to generate an appropriate "Paths" module, & where to place the documentation.  %build-cd '%package/' && runhaskell Setup build	#Descend into the source-distribution and build according to the previously established configuration.+cd '%package/' && cabal build	# Descend into the source-distribution and build according to the previously established configuration.  %install-cd '%package/'					#Descend into the build-directory.-runhaskell Setup copy --destdir=%buildroot	#Install the package in the target-directory.-mkdir -p -- '%buildroot%_docdir' && mv changelog copyright LICENSE '%buildroot%_docdir/'+cd '%package/'	# Descend into the build-directory.+cabal copy --destdir='%buildroot'	# Install the built package in the target-directory.+mkdir -p -- '%buildroot%_docdir' && mv 'changelog.markdown' 'copyright' 'README.markdown' '%buildroot%_docdir/'	# 'LICENSE' has already been copied by cabal. mkdir -p -- '%buildroot%_mandir' && mv 'man/man1' '%buildroot%_mandir/'+rm -rf -- '%buildroot%prefix/lib/'	# The library isn't a deliverable.  %clean-make --directory='%package' clobber-rm -rf -- '%_builddir/%package/' '%buildroot'+rm -rf -- '%_builddir/%package/' '%buildroot/'	# Only the '.rpm' is required.  %files-%attr(0755, root, root)		%_bindir/%name-%attr(0644, root, root)	%doc	%_docdir/changelog+%attr(0755, root, root)		%prefix/bin/%name+%attr(0644, root, root)	%doc	%_docdir/changelog.markdown %attr(0644, root, root)	%doc	%_docdir/copyright %attr(0644, root, root)	%doc	%_docdir/LICENSE+%attr(0644, root, root)	%doc	%_docdir/README.markdown %attr(0644, root, root)	%doc	%_mandir/man1/%name.1.gz  %changelog * Tue Jul 16 2013	Alistair Ward	<fishfood at functionalley dot eu>	0.0.0.2-1 First cut.+
man/man1/fishfood.1 view
@@ -32,10 +32,6 @@ .TP \fB-?\fR, \fB--help\fR Displays help & then exits.-.SS Test-.TP-\fB-q\fR, \fB--runQuickChecks\fR-Tests the implementation, by validating some invariant properties using arbitrary data; & then exits. .SS File-paths .TP If \fIFile-path\fR is a single hyphen-minus (\fB-\fR), then the list of file-paths will be read from standard-input. Only plain files are acceptable; no directories, symlinks, sockets, ...@@ -140,7 +136,7 @@ Written by Dr. Alistair Ward. .SH BUGS .SS "REPORTING BUGS"-Report bugs to \fBfishfood\fR \fIat\fR \fBfunctionalley\fR \fIdot\fR \fBeu\fR+Report bugs to <\fBfishfood\fR \fIat\fR \fBfunctionalley\fR \fIdot\fR \fBeu\fR>. .SH COPYRIGHT Copyright \(co 2013-2015 Dr. Alistair Ward .PP
+ src-exe/Main.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2013-2015 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@]++	* Contains the entry-point of the application.++	* Processes the command-line arguments.++	* Delegates the task to "Profiler".+-}++module Main(main) where++import qualified	Control.Monad+import qualified	Control.Monad.Writer+import qualified	Data.List+import qualified	Data.Maybe+import qualified	Data.Version+import qualified	Distribution.Package+import qualified	Distribution.Text+import qualified	Distribution.Version+import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions+import qualified	FishFood.Data.File		as Data.File+import qualified	FishFood.Data.Verbosity		as Data.Verbosity+import qualified	FishFood.Profiler		as Profiler+import qualified	Paths_fishfood			as Paths	-- Either local stub, or package-instance autogenerated by 'Setup.hs build'.+import qualified	System.Console.GetOpt		as G+import qualified	System.Environment+import qualified	System.Exit+import qualified	System.Info+import qualified	System.IO+import qualified	System.IO.Error+import qualified	ToolShed.Data.List+import qualified	ToolShed.Defaultable+import qualified	ToolShed.SelfValidate++#if !MIN_VERSION_base(4,8,0)+import	Control.Applicative((<$>), (<*>))+#endif++-- | Define the concrete type of command-options.+type CommandOptions	= Data.CommandOptions.CommandOptions Double++-- | Used to thread user-defined command-line options, though the list of functions which implement them.+type CommandLineAction	= CommandOptions -> IO CommandOptions	-- Supplied as the type-argument to 'G.OptDescr'.++-- | On failure to parse the specified string, returns an explanatory error.+read' :: Read a => String -> String -> a+read' errorMessage s	= case reads s of+	[(x, "")]	-> x+	_		-> error $ errorMessage ++ show s++-- | On failure to parse a command-line argument, returns an explanatory error.+readCommandArg :: Read a => String -> a+readCommandArg	= read' "failed to parse command-line argument "++-- | Reads a bounded integral from the command-line, guarding against overflow.+readBoundedIntegral :: Integral i => String -> i+readBoundedIntegral s+	| fromIntegral bounded /= unbounded	= error $ "integral value exceeds permissible bounds; " ++ show unbounded ++ "."+	| otherwise				= bounded+	where+		unbounded	= readCommandArg s+		bounded		= fromInteger unbounded++{- |+	* Parses the command-line options, which over-ride default values.++	* Any arguments which follow known options, are interpreted as file-names.++	* If the specified file-name is /-/, then the actual file-names are read from /standard-input/, to augment any other non-options specified.+-}+main :: IO ()+main	= do+	progName	<- System.Environment.getProgName++	let+		defaultCommandOptions :: CommandOptions+		defaultCommandOptions	= ToolShed.Defaultable.defaultValue++		optDescrList :: [G.OptDescr CommandLineAction]+		optDescrList	= [+--				 String	[String]				(G.ArgDescr CommandLineAction)				String+			G.Option "?"	["help"]				(G.NoArg $ const printUsage)				"Display this help, & then exit.",+			G.Option ""	["verbosity"]				(+				setVerbosity `G.ReqArg` ToolShed.Data.List.showListWith listDelimiters Data.Verbosity.range ""++			)														("Define the log-level; default '" ++ show (Data.CommandOptions.getVerbosity defaultCommandOptions) ++ "'."+			),+			G.Option "v"	["version"]				(G.NoArg $ const printVersion)				"Print version-information, & then exit.",+			G.Option "s"	["binSizeIncrement"]			(setBinSizeIncrement `G.ReqArg` "<Int>")		"The constant size-increase in the arithmetic sequence of bins into which the byte-sizes of files are categorised; defaulting to one standard-deviation.",+			G.Option "r"	["binSizeRatio"]			(setBinSizeRatio `G.ReqArg` "<Float>")			"The constant size-ratio in the geometric sequence of bins into which the byte-sizes of files are categorised; an alternative to 'binSizeIncrement'.",+			G.Option "p"	["deriveProbabilityMassFunction"]	(setDeriveProbabilityMassFunction `G.OptArg` "<Bool>")	("Whether to derive the \"Probability mass function\" rather than the \"Frequency-distribution\"; default '" ++ show (Data.CommandOptions.getDeriveProbabilityMassFunction defaultCommandOptions) ++ "'."),+			G.Option "d"	["nDecimalDigits"]			(setNDecimalDigits `G.ReqArg` "<Int>")			("The precision to which fractional auxiliary data is displayed; default " ++ show (Data.CommandOptions.getNDecimalDigits defaultCommandOptions) ++ ".")+		 ] where+			listDelimiters	= ('(', '|', ')')++			setBinSizeIncrement, setBinSizeRatio, setNDecimalDigits, setVerbosity :: String -> CommandLineAction+			setBinSizeIncrement arg			= return {-to IO-monad-} . Data.CommandOptions.setBinSizeIncrement (readCommandArg arg)+			setBinSizeRatio arg			= return {-to IO-monad-} . Data.CommandOptions.setBinSizeRatio (readCommandArg arg)+			setNDecimalDigits arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getNDecimalDigits = readBoundedIntegral arg }+			setVerbosity arg commandOptions		= return {-to IO-monad-} commandOptions { Data.CommandOptions.getVerbosity = readCommandArg arg }++			setDeriveProbabilityMassFunction :: Maybe String -> CommandLineAction+			setDeriveProbabilityMassFunction arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction = Data.Maybe.maybe True readCommandArg arg }++			printVersion, printUsage :: IO CommandOptions+			printVersion	= System.IO.hPutStrLn System.IO.stderr (+				Distribution.Text.display packageIdentifier ++ "\n\nCompiled by " ++ show compiler ++ ".\n\nCopyright (C) 2013-2015 " ++ 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.exitSuccess	where+				packageIdentifier :: Distribution.Package.PackageIdentifier+				packageIdentifier	= Distribution.Package.PackageIdentifier {+					Distribution.Package.pkgName	= Distribution.Package.PackageName progName,	-- CAVEAT: coincidentally.+					Distribution.Package.pkgVersion	= Distribution.Version.Version (Data.Version.versionBranch Paths.version) []+				}++				author, compiler :: String+				author		= "Dr. Alistair Ward"+				compiler	= System.Info.compilerName ++ "-" ++ Data.List.intercalate "." (map show $ Data.Version.versionBranch System.Info.compilerVersion)++			printUsage	= System.IO.hPutStrLn System.IO.stderr ("Usage:\t" ++ G.usageInfo progName optDescrList ++ "  [<File-path> ...]") >> System.Exit.exitSuccess++	args	<- System.Environment.getArgs++	case G.getOpt G.RequireOrder optDescrList args of+		(commandLineActions, nonOptions, [{-errors-}])	-> do+			commandOptions	<- Data.List.foldl' (>>=) (return {-to IO-monad-} ToolShed.Defaultable.defaultValue) commandLineActions++			if not $ ToolShed.SelfValidate.isValid commandOptions+				then fail $ ToolShed.SelfValidate.getFirstError commandOptions+				else if null nonOptions+					then fail "zero file-paths specified"+					else let+						standardInputProxy	= "-"+					in do+						filePaths	<- if standardInputProxy `elem` nonOptions+							then let+								getFilePaths :: IO [String]+								getFilePaths	= do+									eof	<- System.IO.isEOF++									if eof+										then return {-to IO-monad-} []+										else {-more to read-} (:) <$> getLine <*> getFilePaths {-recurse-}+							in do+								filePaths	<- (filter (/= standardInputProxy) nonOptions ++) <$> getFilePaths++								if null filePaths+									then fail "zero file-paths"+									else return filePaths+							else {-real fileNames-} return {-to IO-monad-} nonOptions++						Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions == maxBound) $ System.IO.hPrint System.IO.stderr filePaths	-- CAVEAT: potentially very long.++						(fileSizeDistribution, statistics)	<- Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions <$> mapM Data.File.findSize (Data.List.nub filePaths)++						Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions > ToolShed.Defaultable.defaultValue) $ mapM_ (System.IO.hPutStrLn System.IO.stderr) statistics++						putStrLn $ Profiler.formatFileSizeDistribution commandOptions fileSizeDistribution+		(_, _, errors)	-> System.IO.Error.ioError . System.IO.Error.userError $ concatMap init {-chop-} errors+
+ src-lib/FishFood/Data/CommandOptions.hs view
@@ -0,0 +1,113 @@+{-+	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 options for program-operation.++	* Defines an appropriate default value, which is expected to be over-ridden on the command-line.++	* Self-validates.+-}++module FishFood.Data.CommandOptions(+-- * Types+-- ** Type-synonyms+	BinSizeDelta,+-- ** Data-types+	CommandOptions(+--		MkCommandOptions,+		getBinSizeDelta,+		getDeriveProbabilityMassFunction,+		getNDecimalDigits,+		getVerbosity+	),+-- * Functions+-- ** Constructors+	mkCommandOptions,+-- ** Mutators+	setBinSizeIncrement,+	setBinSizeRatio+) where++import qualified	Data.Maybe+import qualified	Distribution.Verbosity+import qualified	FishFood.Data.File	as Data.File+import qualified	ToolShed.Defaultable+import qualified	ToolShed.SelfValidate++-- | Either an arithmetic size-increase for which there's a default, or a geometric size-ratio.+type BinSizeDelta ratio	= Either (Maybe Data.File.FileSize) ratio++-- | Declares a record to contain command-line options.+data CommandOptions ratio	= MkCommandOptions {+	getBinSizeDelta				:: BinSizeDelta ratio,			-- ^ Either the arithmetic size-increase (defaulting to one standard-deviation), or the geometric size-ratio, of the sequence of bins into which files are categorized.+	getDeriveProbabilityMassFunction	:: Bool,				-- ^ Whether to derive the "Probability mass function" rather than the "Frequency-distribution".+	getNDecimalDigits			:: Int,					-- ^ The precision to which fractional data is displayed.+	getVerbosity				:: Distribution.Verbosity.Verbosity	-- ^ The threshold for ancillary information-output.+} deriving Show++instance ToolShed.Defaultable.Defaultable (CommandOptions ratio)	where+	defaultValue	= MkCommandOptions {+		getBinSizeDelta				= Left Nothing,	-- Interpreted as one standard-deviation.+		getDeriveProbabilityMassFunction	= False,+		getNDecimalDigits			= 3,+		getVerbosity				= Distribution.Verbosity.normal+	}++instance (Num ratio, Ord ratio, Show ratio) => ToolShed.SelfValidate.SelfValidator (CommandOptions ratio)	where+	getErrors commandOptions@MkCommandOptions {+		getBinSizeDelta				= binSizeDelta,+		getDeriveProbabilityMassFunction	= deriveProbabilityMassFunction,+		getNDecimalDigits			= nDecimalDigits+	} = map snd $ filter fst [+		(+			either (Data.Maybe.maybe False (<= 0)) (<= 1) binSizeDelta,+			"either the bin-size's arithmetic increase must exceed zero, or it's geometric ratio must exceed one; " ++ show commandOptions ++ "."+		), (+			deriveProbabilityMassFunction && nDecimalDigits < 1,+			"the number of decimal digits must exceed zero to adequately represent probabilities; " ++ show commandOptions ++ "."+		),+		let+			maxNDecimalDigits	= floor $ fromIntegral (+				floatDigits (+					undefined :: Double	-- CAVEAT: the actual type could be merely 'Float', but that's currently unknown.+				)+			 ) * (logBase 10 2 :: Double)+		in (+			nDecimalDigits > maxNDecimalDigits,+			"the number of decimal digits shouldn't exceed " ++ show maxNDecimalDigits ++ "; " ++ show commandOptions ++ "."+		)+	 ]++-- | Smart constructor.+mkCommandOptions :: (Num ratio, Ord ratio, Show ratio) => BinSizeDelta ratio -> Bool -> Int -> Distribution.Verbosity.Verbosity -> CommandOptions ratio+mkCommandOptions binSizeDelta deriveProbabilityMassFunction nDecimalDigits verbosity+	| ToolShed.SelfValidate.isValid commandOptions	= commandOptions+	| otherwise					= error $ "FishFood.Data.CommandOptions.mkCommandOptions:\t" ++ ToolShed.SelfValidate.getFirstError commandOptions+	where+		commandOptions	= MkCommandOptions binSizeDelta deriveProbabilityMassFunction nDecimalDigits verbosity++-- | Mutator.+setBinSizeIncrement :: Data.File.FileSize -> CommandOptions ratio -> CommandOptions ratio+setBinSizeIncrement fileSize commandOptions	= commandOptions { getBinSizeDelta = Left $ Just fileSize }++-- | Mutator.+setBinSizeRatio :: ratio -> CommandOptions ratio -> CommandOptions ratio+setBinSizeRatio ratio commandOptions	= commandOptions { getBinSizeDelta = Right ratio }
+ src-lib/FishFood/Data/File.hs view
@@ -0,0 +1,60 @@+{-+	Copyright (C) 2013-2014 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@]	Allows one to find the size of a file, & to gather statistics on a population.+-}++module FishFood.Data.File(+-- * Types+-- ** Type-synonyms+	FileSize,+-- * Functions+	findSize,+	getFileSizeStatistics+) where++import qualified	Control.Exception+import qualified	Factory.Math.Statistics+import qualified	System.Directory+import qualified	System.IO+import qualified	System.IO.Error++-- | A type-synonym specifically to hold file-sizes (in bytes).+type FileSize	= Integer	-- Matches the return-type of 'IO.hFileSize'.++-- | Get the size of the specified file.+findSize :: System.IO.FilePath -> IO FileSize+findSize f	= do+	fileExists	<- System.Directory.doesFileExist f++	if fileExists+		then System.IO.withFile f System.IO.ReadMode System.IO.hFileSize+		else {-not a file-} Control.Exception.throwIO $ System.IO.Error.mkIOError System.IO.Error.illegalOperationErrorType ("file=" ++ show f ++ " either doesn't exist or has an unexpected type") Nothing (Just f)++-- | Acquire statistics related to a list of file-sizes.+getFileSizeStatistics+	:: (Fractional mean, Floating standardDeviation)+	=> [FileSize]+	-> (Int, mean, standardDeviation)	-- ^ (The population-size, Mean size, Standard-deviation).+getFileSizeStatistics fileSizes	= (+	length fileSizes,+	Factory.Math.Statistics.getMean fileSizes,+	Factory.Math.Statistics.getStandardDeviation fileSizes+ )+
+ src-lib/FishFood/Data/Verbosity.hs view
@@ -0,0 +1,37 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+	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@]	Instance-definitions for 'Distribution.Verbosity.Verbosity'.+-}++module FishFood.Data.Verbosity(+-- * Constants+	range+) where++import qualified	Distribution.Verbosity+import qualified	ToolShed.Defaultable++instance ToolShed.Defaultable.Defaultable Distribution.Verbosity.Verbosity	where+	defaultValue	= Distribution.Verbosity.normal++-- | The constant complete range of values.+range :: [Distribution.Verbosity.Verbosity]+range	= [minBound .. maxBound]
+ src-lib/FishFood/Profiler.hs view
@@ -0,0 +1,148 @@+{-+	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@]	Profiles lists of file-sizes.+-}++module FishFood.Profiler(+-- * Types+-- ** Type-synonyms+--	Probability,+--	Result,+--	FileSizeDistribution,+-- * Functions+	calculateFileSizeDistribution,+	formatFileSizeDistribution,+-- ** Accessors+	getFileSize,+	getValue+) where++import			Control.Arrow((&&&),(***))+import qualified	Control.Monad.Writer+import qualified	Data.List+import qualified	Data.Map+import qualified	Data.Maybe+import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions+import qualified	FishFood.Data.File		as Data.File+import			FishFood.Data.Verbosity()+import qualified	Text.Printf+import qualified	ToolShed.Defaultable++-- | Define a type to represent the fractional closed unit-interval.+type Probability	= Double++-- | Defines either the number of files or the probability that a files has a specific size.+type Result	= (Data.File.FileSize, Either Int {-file-count-} Probability)++-- | Accessor.+getFileSize :: Result -> Data.File.FileSize+getFileSize	= fst++-- | Accessor.+getValue :: Result -> Either Int {-file-count-} Probability+getValue	= snd++-- | Defines either a /Probability Mass Function/ or /Frequency-distribution/.+type FileSizeDistribution	= [Result]++-- | Calculates either the /Probability Mass Function/ or /Frequency-distribution/ for the specified files.+calculateFileSizeDistribution :: (Floating ratio, RealFrac ratio) => Data.CommandOptions.CommandOptions ratio -> [Data.File.FileSize] -> Control.Monad.Writer.Writer [String] FileSizeDistribution+calculateFileSizeDistribution commandOptions fileSizes	= let+	binSizeDelta			= Data.CommandOptions.getBinSizeDelta commandOptions+	deriveProbabilityMassFunction	= Data.CommandOptions.getDeriveProbabilityMassFunction commandOptions+	nDecimalDigits			= Data.CommandOptions.getNDecimalDigits commandOptions++	mean, standardDeviation :: Double+	(nFiles, mean, standardDeviation)	= Data.File.getFileSizeStatistics fileSizes+ in do+	Control.Monad.Writer.tell [Text.Printf.printf "Files=%d, mean=%.*f, standard-deviation=%.*f" nFiles nDecimalDigits mean nDecimalDigits standardDeviation]++	return {-to Writer-monad-} $ if standardDeviation == 0+		then return {-to List-monad-} . (,) (head fileSizes) $ if deriveProbabilityMassFunction+			then Right 1		-- i.e. certainty.+			else Left nFiles	-- i.e. all.+		else let+			getDefaultedBinSizeIncrement :: Maybe Data.File.FileSize -> Data.File.FileSize+			getDefaultedBinSizeIncrement	= Data.Maybe.fromMaybe $ round standardDeviation `max` 1 {-minimum increment-}	-- CAVEAT: guard against subsequent division by zero or infinite iteration.++			calculatedBinSizes :: [Data.File.FileSize]+			calculatedBinSizes	= map (+				\fileSize	-> either (+					div {-round down-} fileSize . getDefaultedBinSizeIncrement {-non-zero-}+				) (+					floor {-round down-} . (`logBase` fromIntegral fileSize)	-- CAVEAT: converts file-size 0, to bin-size -infinity.+				) binSizeDelta+			 ) fileSizes	-- Each bin spans the semi-closed integral interval [size, succ size), so round down fractional values to match the lower bin.++			initialFrequencyDistribution :: Data.Map.Map Data.File.FileSize Int+			initialFrequencyDistribution	= Data.Map.fromAscList . (+				`zip` repeat 0	-- The initial file-count.+			 ) . takeWhile (+				<= maximum calculatedBinSizes+			 ) . dropWhile (+				< minimum calculatedBinSizes+			 ) $ either (+				\maybeBinSizeIncrement	-> iterate (+ getDefaultedBinSizeIncrement {-non-zero-} maybeBinSizeIncrement) 0+			 ) (+				\binRatio		-> map round {-file-sizes are integral-} $ iterate (* binRatio) 1	-- The sequence could be started at fractional values in the open unit-interval, but the only value less than 1 which may be required is 0 (which isn't a sequence-member), which will be created later on demand.+			 ) binSizeDelta++			mapBinSizeToFileSize :: Data.Map.Map Data.File.FileSize value -> Data.Map.Map Data.File.FileSize value+			mapBinSizeToFileSize	= Data.Map.mapKeys $ \binSize -> either (+				(* binSize) . getDefaultedBinSizeIncrement+			 ) (+				ceiling {-round up-} . (^^ binSize)	-- Converts binSize -infinity, back to file-size 0.+			 ) binSizeDelta	-- Represent each bin by the minimum file-size it can accept.+		in Data.Map.toList . (+			if deriveProbabilityMassFunction+				then Data.Map.map Right . mapBinSizeToFileSize . Data.Map.map ((/ fromIntegral nFiles {-non-zero-}) . fromIntegral)+				else Data.Map.map Left . mapBinSizeToFileSize+		) $ foldr (+			Data.Map.insertWith (+) `flip` 1	-- Count the files allocated to each bin.+		) initialFrequencyDistribution calculatedBinSizes++-- | Formats a file-size distribution.+formatFileSizeDistribution :: Data.CommandOptions.CommandOptions ratio -> FileSizeDistribution -> String+formatFileSizeDistribution commandOptions	= Data.List.intercalate "\n" . map (+	\(fileSize, value)	-> fileSize ++ " " ++ value+ ) . (+	if Data.CommandOptions.getVerbosity commandOptions > ToolShed.Defaultable.defaultValue+		then (+			[+				(+					($ (fileSizeWidth, fileSizeHeader)) &&& ($ (valueWidth, valueHeader))+				) . uncurry $ Text.Printf.printf "%*s",	-- Column-headers.+				(`replicate` '=') *** (`replicate` '=') $ columnWidths	-- Separator-bar.+			] +++		) -- Section.+		else id+ ) . map (+	Text.Printf.printf "%*d" fileSizeWidth *** either (+		Text.Printf.printf "%*d" valueWidth+	) (+		Text.Printf.printf "%.*f" $ Data.CommandOptions.getNDecimalDigits commandOptions+	)+ ) where+	fileSizeHeader, valueHeader :: String+	headers@(fileSizeHeader, valueHeader)	= (,) "Bin-size" $ if Data.CommandOptions.getDeriveProbabilityMassFunction commandOptions then "Probability" else "Frequency"++	fileSizeWidth, valueWidth :: Int+	columnWidths@(fileSizeWidth, valueWidth)	= (`max` 10) . length *** length $ headers	-- CAVEAT: the data-length may exceed the header-length, so define a minimum.+
+ src-test/FishFood/Test/Data/CommandOptions.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+	Copyright (C) 2013-2015 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@]	Implements 'Test.QuickCheck.Arbitrary' for 'Data.CommandOptions.CommandOptions'.+-}++module FishFood.Test.Data.CommandOptions(+-- * Types+-- ** Type-synonyms+	CommandOptions+) where++import qualified	Test.QuickCheck+import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions+import			FishFood.Test.Data.Verbosity()++#if !MIN_VERSION_base(4,8,0)+import	Control.Applicative((<$>), (<*>))+#endif++instance (+	Enum				ratio,+	Fractional			ratio,+	Ord				ratio,+	Show				ratio,+	Test.QuickCheck.Arbitrary	ratio+ ) => Test.QuickCheck.Arbitrary (Data.CommandOptions.CommandOptions ratio)	where+	arbitrary	= do+		deriveProbabilityMassFunction	<- Test.QuickCheck.arbitrary++		Data.CommandOptions.mkCommandOptions <$> (+			either (+				Left . (succ . abs <$> {-into Maybe-monad-})+			) (+				Right . (+ recip 1000000) . succ . abs+			) <$> Test.QuickCheck.arbitrary {-binSizeDelta-}+		 ) <*> return {-to Gen-monad-} deriveProbabilityMassFunction <*> (+			max (+				if deriveProbabilityMassFunction then 1 else 0+			) . (+				`mod` 15 {-maximum permissible-}+			) <$> Test.QuickCheck.arbitrary {-nDecimalDigits-}+		 ) <*> Test.QuickCheck.arbitrary {-verbosity-}++-- | Defines a concrete type for testing.+type CommandOptions	= Data.CommandOptions.CommandOptions Double+
+ src-test/FishFood/Test/Data/Verbosity.hs view
@@ -0,0 +1,31 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+	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@]	Implements 'Test.QuickCheck.Arbitrary' for 'Distribution.Verbosity.Verbosity'.+-}++module FishFood.Test.Data.Verbosity() where++import qualified	Distribution.Verbosity+import qualified	FishFood.Data.Verbosity	as Data.Verbosity+import qualified	Test.QuickCheck++instance Test.QuickCheck.Arbitrary Distribution.Verbosity.Verbosity	where+	arbitrary	= Test.QuickCheck.elements Data.Verbosity.range
+ src-test/FishFood/Test/Profiler.hs view
@@ -0,0 +1,58 @@+{-+	Copyright (C) 2013-2015 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 for 'Profiler'.+-}++module FishFood.Test.Profiler(+-- * Constants+	results+) where++import qualified	Control.Monad.Writer+import qualified	Data.Either+import qualified	Data.Set+import qualified	FishFood.Data.CommandOptions		as Data.CommandOptions+import qualified	FishFood.Data.File			as Data.File+import qualified	FishFood.Profiler			as Profiler+import qualified	FishFood.Test.Data.CommandOptions	as Test.Data.CommandOptions+import qualified	Test.QuickCheck+import			Test.QuickCheck((==>))+import qualified	ToolShed.SelfValidate++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results	= mapM Test.QuickCheck.quickCheckResult [prop_calculateProbabilityMassFunction, prop_calculateFileSizeFrequencyDistribution, prop_attendance]	where+	prop_calculateProbabilityMassFunction, prop_calculateFileSizeFrequencyDistribution, prop_attendance :: Test.Data.CommandOptions.CommandOptions -> [Data.File.FileSize] -> Test.QuickCheck.Property+	prop_calculateProbabilityMassFunction commandOptions fileSizes	= not (null fileSizes) && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_calculateProbabilityMassFunction" . (<= recip 1000000) . (+ negate 1) . sum . Data.Either.rights {-probabilities-} . map Profiler.getValue . fst {-distribution-} . Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions' $ map abs fileSizes	where+		commandOptions'	= commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction	= True }++	prop_calculateFileSizeFrequencyDistribution commandOptions fileSizes	= not (null fileSizes) && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_calculateFileSizeFrequencyDistribution" . (== length fileSizes) . sum . Data.Either.lefts {-frequency-} . map Profiler.getValue . fst {-distribution-} . Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions' $ map abs fileSizes	where+		commandOptions'	= commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction	= False }++	prop_attendance commandOptions fileSizes	= not (null fileSizes') && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_attendance" . (+		== Data.Set.fromList fileSizes'+	 ) . Data.Set.fromList . map Profiler.getFileSize . filter (+		(/= 0) . either fromIntegral id . Profiler.getValue	-- Remove file-sizes which match zero actual files.+	 ) . fst {-distribution-} . Control.Monad.Writer.runWriter $ Profiler.calculateFileSizeDistribution commandOptions' fileSizes'	where+		fileSizes'	= map abs fileSizes++		commandOptions' :: Test.Data.CommandOptions.CommandOptions+		commandOptions'	= Data.CommandOptions.setBinSizeIncrement 1 commandOptions+
+ src-test/Main.hs view
@@ -0,0 +1,44 @@+{-+	Copyright (C) 2015 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@]++	* The entry-point to the application's test-suite.+-}++module Main(main) where++import			Control.Arrow((***))+import qualified	Control.Monad+import qualified	FishFood.Test.Profiler	as Test.Profiler+import qualified	System.Exit+import qualified	ToolShed.Test.QuickCheck.Result++-- | Entry-point.+main :: IO ()+main	= mapM_ (+	snd {-exit-status-} . (+		putStrLn . (++ ":") *** (+			>>= (`Control.Monad.unless` System.Exit.exitFailure) . all ToolShed.Test.QuickCheck.Result.isSuccessful+		)+	)+ ) [+	("Profiler",	Test.Profiler.results)+ ]+
− src/FishFood/Data/CommandOptions.hs
@@ -1,113 +0,0 @@-{--	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 options for program-operation.--	* Defines an appropriate default value, which is expected to be over-ridden on the command-line.--	* Self-validates.--}--module FishFood.Data.CommandOptions(--- * Types--- ** Type-synonyms-	BinSizeDelta,--- ** Data-types-	CommandOptions(---		MkCommandOptions,-		getBinSizeDelta,-		getDeriveProbabilityMassFunction,-		getNDecimalDigits,-		getVerbosity-	),--- * Functions--- ** Constructors-	mkCommandOptions,--- ** Mutators-	setBinSizeIncrement,-	setBinSizeRatio-) where--import qualified	Data.Maybe-import qualified	Distribution.Verbosity-import qualified	FishFood.Data.File	as Data.File-import qualified	ToolShed.Defaultable-import qualified	ToolShed.SelfValidate---- | Either an arithmetic size-increase for which there's a default, or a geometric size-ratio.-type BinSizeDelta ratio	= Either (Maybe Data.File.FileSize) ratio---- | Declares a record to contain command-line options.-data CommandOptions ratio	= MkCommandOptions {-	getBinSizeDelta				:: BinSizeDelta ratio,			-- ^ Either the arithmetic size-increase (defaulting to one standard-deviation), or the geometric size-ratio, of the sequence of bins into which files are categorized.-	getDeriveProbabilityMassFunction	:: Bool,				-- ^ Whether to derive the "Probability mass function" rather than the "Frequency-distribution".-	getNDecimalDigits			:: Int,					-- ^ The precision to which fractional data is displayed.-	getVerbosity				:: Distribution.Verbosity.Verbosity	-- ^ The threshold for ancillary information-output.-} deriving Show--instance ToolShed.Defaultable.Defaultable (CommandOptions ratio)	where-	defaultValue	= MkCommandOptions {-		getBinSizeDelta				= Left Nothing,	-- Interpreted as one standard-deviation.-		getDeriveProbabilityMassFunction	= False,-		getNDecimalDigits			= 3,-		getVerbosity				= Distribution.Verbosity.normal-	}--instance (Num ratio, Ord ratio, Show ratio) => ToolShed.SelfValidate.SelfValidator (CommandOptions ratio)	where-	getErrors commandOptions@MkCommandOptions {-		getBinSizeDelta				= binSizeDelta,-		getDeriveProbabilityMassFunction	= deriveProbabilityMassFunction,-		getNDecimalDigits			= nDecimalDigits-	} = map snd $ filter fst [-		(-			either (Data.Maybe.maybe False (<= 0)) (<= 1) binSizeDelta,-			"either the bin-size's arithmetic increase must exceed zero, or it's geometric ratio must exceed one; " ++ show commandOptions ++ "."-		), (-			deriveProbabilityMassFunction && nDecimalDigits < 1,-			"the number of decimal digits must exceed zero to adequately represent probabilities; " ++ show commandOptions ++ "."-		),-		let-			maxNDecimalDigits	= floor $ fromIntegral (-				floatDigits (-					undefined :: Double	-- CAVEAT: the actual type could be merely 'Float', but that's currently unknown.-				)-			 ) * (logBase 10 2 :: Double)-		in (-			nDecimalDigits > maxNDecimalDigits,-			"the number of decimal digits shouldn't exceed " ++ show maxNDecimalDigits ++ "; " ++ show commandOptions ++ "."-		)-	 ]---- | Smart constructor.-mkCommandOptions :: (Num ratio, Ord ratio, Show ratio) => BinSizeDelta ratio -> Bool -> Int -> Distribution.Verbosity.Verbosity -> CommandOptions ratio-mkCommandOptions binSizeDelta deriveProbabilityMassFunction nDecimalDigits verbosity-	| ToolShed.SelfValidate.isValid commandOptions	= commandOptions-	| otherwise					= error $ "FishFood.Data.CommandOptions.mkCommandOptions:\t" ++ ToolShed.SelfValidate.getFirstError commandOptions-	where-		commandOptions	= MkCommandOptions binSizeDelta deriveProbabilityMassFunction nDecimalDigits verbosity---- | Mutator.-setBinSizeIncrement :: Data.File.FileSize -> CommandOptions ratio -> CommandOptions ratio-setBinSizeIncrement fileSize commandOptions	= commandOptions { getBinSizeDelta = Left $ Just fileSize }---- | Mutator.-setBinSizeRatio :: ratio -> CommandOptions ratio -> CommandOptions ratio-setBinSizeRatio ratio commandOptions	= commandOptions { getBinSizeDelta = Right ratio }
− src/FishFood/Data/File.hs
@@ -1,60 +0,0 @@-{--	Copyright (C) 2013-2014 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@]	Allows one to find the size of a file, & to gather statistics on a population.--}--module FishFood.Data.File(--- * Types--- ** Type-synonyms-	FileSize,--- * Functions-	findSize,-	getFileSizeStatistics-) where--import qualified	Control.Exception-import qualified	Factory.Math.Statistics-import qualified	System.Directory-import qualified	System.IO-import qualified	System.IO.Error---- | A type-synonym specifically to hold file-sizes (in bytes).-type FileSize	= Integer	-- Matches the return-type of 'IO.hFileSize'.---- | Get the size of the specified file.-findSize :: System.IO.FilePath -> IO FileSize-findSize f	= do-	fileExists	<- System.Directory.doesFileExist f--	if fileExists-		then System.IO.withFile f System.IO.ReadMode System.IO.hFileSize-		else {-not a file-} Control.Exception.throwIO $ System.IO.Error.mkIOError System.IO.Error.illegalOperationErrorType ("file=" ++ show f ++ " either doesn't exist or has an unexpected type") Nothing (Just f)---- | Acquire statistics related to a list of file-sizes.-getFileSizeStatistics-	:: (Fractional mean, Floating standardDeviation)-	=> [FileSize]-	-> (Int, mean, standardDeviation)	-- ^ (The population-size, Mean size, Standard-deviation).-getFileSizeStatistics fileSizes	= (-	length fileSizes,-	Factory.Math.Statistics.getMean fileSizes,-	Factory.Math.Statistics.getStandardDeviation fileSizes- )-
− src/FishFood/Data/Verbosity.hs
@@ -1,37 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{--	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@]	Instance-definitions for 'Distribution.Verbosity.Verbosity'.--}--module FishFood.Data.Verbosity(--- * Constants-	range-) where--import qualified	Distribution.Verbosity-import qualified	ToolShed.Defaultable--instance ToolShed.Defaultable.Defaultable Distribution.Verbosity.Verbosity	where-	defaultValue	= Distribution.Verbosity.normal---- | The constant complete range of values.-range :: [Distribution.Verbosity.Verbosity]-range	= [minBound .. maxBound]
− src/FishFood/Profiler.hs
@@ -1,148 +0,0 @@-{--	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@]	Profiles lists of file-sizes.--}--module FishFood.Profiler(--- * Types--- ** Type-synonyms---	Probability,---	Result,---	FileSizeDistribution,--- * Functions-	calculateFileSizeDistribution,-	formatFileSizeDistribution,--- ** Accessors-	getFileSize,-	getValue-) where--import			Control.Arrow((&&&),(***))-import qualified	Control.Monad.Writer-import qualified	Data.List-import qualified	Data.Map-import qualified	Data.Maybe-import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions-import qualified	FishFood.Data.File		as Data.File-import			FishFood.Data.Verbosity()-import qualified	Text.Printf-import qualified	ToolShed.Defaultable---- | Define a type to represent the fractional closed unit-interval.-type Probability	= Double---- | Defines either the number of files or the probability that a files has a specific size.-type Result	= (Data.File.FileSize, Either Int {-file-count-} Probability)---- | Accessor.-getFileSize :: Result -> Data.File.FileSize-getFileSize	= fst---- | Accessor.-getValue :: Result -> Either Int {-file-count-} Probability-getValue	= snd---- | Defines either a /Probability Mass Function/ or /Frequency-distribution/.-type FileSizeDistribution	= [Result]---- | Calculates either the /Probability Mass Function/ or /Frequency-distribution/ for the specified files.-calculateFileSizeDistribution :: (Floating ratio, RealFrac ratio) => Data.CommandOptions.CommandOptions ratio -> [Data.File.FileSize] -> Control.Monad.Writer.Writer [String] FileSizeDistribution-calculateFileSizeDistribution commandOptions fileSizes	= let-	binSizeDelta			= Data.CommandOptions.getBinSizeDelta commandOptions-	deriveProbabilityMassFunction	= Data.CommandOptions.getDeriveProbabilityMassFunction commandOptions-	nDecimalDigits			= Data.CommandOptions.getNDecimalDigits commandOptions--	mean, standardDeviation :: Double-	(nFiles, mean, standardDeviation)	= Data.File.getFileSizeStatistics fileSizes- in do-	Control.Monad.Writer.tell [Text.Printf.printf "Files=%d, mean=%.*f, standard-deviation=%.*f" nFiles nDecimalDigits mean nDecimalDigits standardDeviation]--	return {-to Writer-monad-} $ if standardDeviation == 0-		then return {-to List-monad-} . (,) (head fileSizes) $ if deriveProbabilityMassFunction-			then Right 1		-- i.e. certainty.-			else Left nFiles	-- i.e. all.-		else let-			getDefaultedBinSizeIncrement :: Maybe Data.File.FileSize -> Data.File.FileSize-			getDefaultedBinSizeIncrement	= Data.Maybe.fromMaybe $ round standardDeviation `max` 1 {-minimum increment-}	-- CAVEAT: guard against subsequent division by zero or infinite iteration.--			calculatedBinSizes :: [Data.File.FileSize]-			calculatedBinSizes	= map (-				\fileSize	-> either (-					div {-round down-} fileSize . getDefaultedBinSizeIncrement {-non-zero-}-				) (-					floor {-round down-} . (`logBase` fromIntegral fileSize)	-- CAVEAT: converts file-size 0, to bin-size -infinity.-				) binSizeDelta-			 ) fileSizes	-- Each bin spans the semi-closed integral interval [size, succ size), so round down fractional values to match the lower bin.--			initialFrequencyDistribution :: Data.Map.Map Data.File.FileSize Int-			initialFrequencyDistribution	= Data.Map.fromAscList . (-				`zip` repeat 0	-- The initial file-count.-			 ) . takeWhile (-				<= maximum calculatedBinSizes-			 ) . dropWhile (-				< minimum calculatedBinSizes-			 ) $ either (-				\maybeBinSizeIncrement	-> iterate (+ getDefaultedBinSizeIncrement {-non-zero-} maybeBinSizeIncrement) 0-			 ) (-				\binRatio		-> map round {-file-sizes are integral-} $ iterate (* binRatio) 1	-- The sequence could be started at fractional values in the open unit-interval, but the only value less than 1 which may be required is 0 (which isn't a sequence-member), which will be created later on demand.-			 ) binSizeDelta--			mapBinSizeToFileSize :: Data.Map.Map Data.File.FileSize value -> Data.Map.Map Data.File.FileSize value-			mapBinSizeToFileSize	= Data.Map.mapKeys $ \binSize -> either (-				(* binSize) . getDefaultedBinSizeIncrement-			 ) (-				ceiling {-round up-} . (^^ binSize)	-- Converts binSize -infinity, back to file-size 0.-			 ) binSizeDelta	-- Represent each bin by the minimum file-size it can accept.-		in Data.Map.toList . (-			if deriveProbabilityMassFunction-				then Data.Map.map Right . mapBinSizeToFileSize . Data.Map.map ((/ fromIntegral nFiles {-non-zero-}) . fromIntegral)-				else Data.Map.map Left . mapBinSizeToFileSize-		) $ foldr (-			Data.Map.insertWith (+) `flip` 1	-- Count the files allocated to each bin.-		) initialFrequencyDistribution calculatedBinSizes---- | Formats a file-size distribution.-formatFileSizeDistribution :: Data.CommandOptions.CommandOptions ratio -> FileSizeDistribution -> String-formatFileSizeDistribution commandOptions	= Data.List.intercalate "\n" . map (-	\(fileSize, value)	-> fileSize ++ " " ++ value- ) . (-	if Data.CommandOptions.getVerbosity commandOptions > ToolShed.Defaultable.defaultValue-		then (-			[-				(-					($ (fileSizeWidth, fileSizeHeader)) &&& ($ (valueWidth, valueHeader))-				) . uncurry $ Text.Printf.printf "%*s",	-- Column-headers.-				(`replicate` '=') *** (`replicate` '=') $ columnWidths	-- Separator-bar.-			] ++-		) -- Section.-		else id- ) . map (-	Text.Printf.printf "%*d" fileSizeWidth *** either (-		Text.Printf.printf "%*d" valueWidth-	) (-		Text.Printf.printf "%.*f" $ Data.CommandOptions.getNDecimalDigits commandOptions-	)- ) where-	fileSizeHeader, valueHeader :: String-	headers@(fileSizeHeader, valueHeader)	= (,) "Bin-size" $ if Data.CommandOptions.getDeriveProbabilityMassFunction commandOptions then "Probability" else "Frequency"--	fileSizeWidth, valueWidth :: Int-	columnWidths@(fileSizeWidth, valueWidth)	= (`max` 10) . length *** length $ headers	-- CAVEAT: the data-length may exceed the header-length, so define a minimum.-
− src/FishFood/Test/Data/CommandOptions.hs
@@ -1,62 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{--	Copyright (C) 2013-2015 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@]	Implements 'Test.QuickCheck.Arbitrary' for 'Data.CommandOptions.CommandOptions'.--}--module FishFood.Test.Data.CommandOptions(--- * Types--- ** Type-synonyms-	CommandOptions-) where--import Prelude hiding ((<$>), (<*>))	-- The "Prelude" from 'base-4.8' exports this symbol.-import			Control.Applicative((<$>), (<*>))-import qualified	Test.QuickCheck-import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions-import			FishFood.Test.Data.Verbosity()--instance (-	Enum				ratio,-	Fractional			ratio,-	Ord				ratio,-	Show				ratio,-	Test.QuickCheck.Arbitrary	ratio- ) => Test.QuickCheck.Arbitrary (Data.CommandOptions.CommandOptions ratio)	where-	arbitrary	= do-		deriveProbabilityMassFunction	<- Test.QuickCheck.arbitrary--		Data.CommandOptions.mkCommandOptions <$> (-			either (-				Left . (succ . abs <$> {-into Maybe-monad-})-			) (-				Right . (+ recip 1000000) . succ . abs-			) <$> Test.QuickCheck.arbitrary {-binSizeDelta-}-		 ) <*> return {-to Gen-monad-} deriveProbabilityMassFunction <*> (-			max (-				if deriveProbabilityMassFunction then 1 else 0-			) . (-				`mod` 15 {-maximum permissible-}-			) <$> Test.QuickCheck.arbitrary {-nDecimalDigits-}-		 ) <*> Test.QuickCheck.arbitrary {-verbosity-}---- | Defines a concrete type for testing.-type CommandOptions	= Data.CommandOptions.CommandOptions Double-
− src/FishFood/Test/Data/Verbosity.hs
@@ -1,31 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{--	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@]	Implements 'Test.QuickCheck.Arbitrary' for 'Distribution.Verbosity.Verbosity'.--}--module FishFood.Test.Data.Verbosity() where--import qualified	Distribution.Verbosity-import qualified	FishFood.Data.Verbosity	as Data.Verbosity-import qualified	Test.QuickCheck--instance Test.QuickCheck.Arbitrary Distribution.Verbosity.Verbosity	where-	arbitrary	= Test.QuickCheck.elements Data.Verbosity.range
− src/FishFood/Test/Profiler.hs
@@ -1,58 +0,0 @@-{--	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@]	Implements 'Test.QuickCheck.Arbitrary' & defines /QuickCheck/-properties for 'Profiler'.--}--module FishFood.Test.Profiler(--- * Functions-	quickChecks-) where--import qualified	Control.Monad.Writer-import qualified	Data.Either-import qualified	Data.Set-import qualified	FishFood.Data.CommandOptions		as Data.CommandOptions-import qualified	FishFood.Data.File			as Data.File-import qualified	FishFood.Profiler			as Profiler-import qualified	FishFood.Test.Data.CommandOptions	as Test.Data.CommandOptions-import qualified	Test.QuickCheck-import			Test.QuickCheck((==>))-import qualified	ToolShed.SelfValidate---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks	= Test.QuickCheck.quickCheck `mapM_` [prop_calculateProbabilityMassFunction, prop_calculateFileSizeFrequencyDistribution, prop_attendance]	where-	prop_calculateProbabilityMassFunction, prop_calculateFileSizeFrequencyDistribution, prop_attendance :: Test.Data.CommandOptions.CommandOptions -> [Data.File.FileSize] -> Test.QuickCheck.Property-	prop_calculateProbabilityMassFunction commandOptions fileSizes	= not (null fileSizes) && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_calculateProbabilityMassFunction" . (<= recip 1000000) . (+ negate 1) . sum . Data.Either.rights {-probabilities-} . map Profiler.getValue . fst {-distribution-} . Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions' $ map abs fileSizes	where-		commandOptions'	= commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction	= True }--	prop_calculateFileSizeFrequencyDistribution commandOptions fileSizes	= not (null fileSizes) && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_calculateFileSizeFrequencyDistribution" . (== length fileSizes) . sum . Data.Either.lefts {-frequency-} . map Profiler.getValue . fst {-distribution-} . Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions' $ map abs fileSizes	where-		commandOptions'	= commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction	= False }--	prop_attendance commandOptions fileSizes	= not (null fileSizes') && ToolShed.SelfValidate.isValid commandOptions' ==> Test.QuickCheck.label "prop_attendance" . (-		== Data.Set.fromList fileSizes'-	 ) . Data.Set.fromList . map Profiler.getFileSize . filter (-		(/= 0) . either fromIntegral id . Profiler.getValue	-- Remove file-sizes which match zero actual files.-	 ) . fst {-distribution-} . Control.Monad.Writer.runWriter $ Profiler.calculateFileSizeDistribution commandOptions' fileSizes'	where-		fileSizes'	= map abs fileSizes--		commandOptions' :: Test.Data.CommandOptions.CommandOptions-		commandOptions'	= Data.CommandOptions.setBinSizeIncrement 1 commandOptions-
− src/FishFood/Test/QuickChecks.hs
@@ -1,38 +0,0 @@-{--	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@]	Calls the /quickChecks/-functions for modules supporting this feature.--}--module FishFood.Test.QuickChecks(--- * Functions-	run-) where--import qualified	Control.Arrow-import qualified	FishFood.Test.Profiler	as Test.Profiler---- | Runs checks on invariant properties.-run :: IO ()-run	= mapM_ (-	uncurry (>>) . Control.Arrow.first putStrLn- ) [-	("Profiler",	Test.Profiler.quickChecks)- ]-
− src/Main.hs
@@ -1,181 +0,0 @@-{--	Copyright (C) 2013-2015 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@]--	* Contains the entry-point of the application.--	* Processes the command-line arguments.--	* Delegates the task to "Profiler".--}--module Main(main) where--import Prelude hiding ((<$>), (<*>))	-- The "Prelude" from 'base-4.8' exports this symbol.-import			Control.Applicative((<$>),(<*>))-import qualified	Control.Monad-import qualified	Control.Monad.Writer-import qualified	Data.List-import qualified	Data.Maybe-import qualified	Data.Version-import qualified	Distribution.Package-import qualified	Distribution.Text-import qualified	Distribution.Version-import qualified	FishFood.Data.CommandOptions	as Data.CommandOptions-import qualified	FishFood.Data.File		as Data.File-import qualified	FishFood.Data.Verbosity		as Data.Verbosity-import qualified	FishFood.Profiler		as Profiler-import qualified	FishFood.Test.QuickChecks	as Test.QuickChecks-import qualified	Paths_fishfood			as Paths	-- Either local stub, or package-instance autogenerated by 'Setup.hs build'.-import qualified	System.Console.GetOpt		as G-import qualified	System.Environment-import qualified	System.Exit-import qualified	System.Info-import qualified	System.IO-import qualified	System.IO.Error-import qualified	ToolShed.Data.List-import qualified	ToolShed.Defaultable-import qualified	ToolShed.SelfValidate---- | Define the concrete type of command-options.-type CommandOptions	= Data.CommandOptions.CommandOptions Double---- | Used to thread user-defined command-line options, though the list of functions which implement them.-type CommandLineAction	= CommandOptions -> IO CommandOptions	-- Supplied as the type-argument to 'G.OptDescr'.---- | On failure to parse the specified string, returns an explanatory error.-read' :: Read a => String -> String -> a-read' errorMessage s	= case reads s of-	[(x, "")]	-> x-	_		-> error $ errorMessage ++ show s---- | On failure to parse a command-line argument, returns an explanatory error.-readCommandArg :: Read a => String -> a-readCommandArg	= read' "failed to parse command-line argument "---- | Reads a bounded integral from the command-line, guarding against overflow.-readBoundedIntegral :: Integral i => String -> i-readBoundedIntegral s-	| fromIntegral bounded /= unbounded	= error $ "integral value exceeds permissible bounds; " ++ show unbounded ++ "."-	| otherwise				= bounded-	where-		unbounded	= readCommandArg s-		bounded		= fromInteger unbounded--{- |-	* Parses the command-line options, which over-ride default values.--	* Any arguments which follow known options, are interpreted as file-names.--	* If the specified file-name is /-/, then the actual file-names are read from /standard-input/, to augment any other non-options specified.--}-main :: IO ()-main	= do-	progName	<- System.Environment.getProgName--	let-		defaultCommandOptions :: CommandOptions-		defaultCommandOptions	= ToolShed.Defaultable.defaultValue--		optDescrList :: [G.OptDescr CommandLineAction]-		optDescrList	= [---				 String	[String]				(G.ArgDescr CommandLineAction)				String-			G.Option "?"	["help"]				(G.NoArg $ const printUsage)				"Display this help, & then exit.",-			G.Option ""	["verbosity"]				(-				setVerbosity `G.ReqArg` ToolShed.Data.List.showListWith listDelimiters Data.Verbosity.range ""--			)														("Define the log-level; default '" ++ show (Data.CommandOptions.getVerbosity defaultCommandOptions) ++ "'."-			),-			G.Option "v"	["version"]				(G.NoArg $ const printVersion)				"Print version-information, & then exit.",-			G.Option "s"	["binSizeIncrement"]			(setBinSizeIncrement `G.ReqArg` "<Int>")		"The constant size-increase in the arithmetic sequence of bins into which the byte-sizes of files are categorised; defaulting to one standard-deviation.",-			G.Option "r"	["binSizeRatio"]			(setBinSizeRatio `G.ReqArg` "<Float>")			"The constant size-ratio in the geometric sequence of bins into which the byte-sizes of files are categorised; an alternative to 'binSizeIncrement'.",-			G.Option "p"	["deriveProbabilityMassFunction"]	(setDeriveProbabilityMassFunction `G.OptArg` "<Bool>")	("Whether to derive the \"Probability mass function\" rather than the \"Frequency-distribution\"; default '" ++ show (Data.CommandOptions.getDeriveProbabilityMassFunction defaultCommandOptions) ++ "'."),-			G.Option "d"	["nDecimalDigits"]			(setNDecimalDigits `G.ReqArg` "<Int>")			("The precision to which fractional auxiliary data is displayed; default " ++ show (Data.CommandOptions.getNDecimalDigits defaultCommandOptions) ++ "."),-			G.Option "q"	["runQuickChecks"]			(G.NoArg runQuickChecks)				"Test the implementation, by validating some invariant properties using arbitrary data; & then exit."-		 ] where-			listDelimiters	= ('(', '|', ')')--			setBinSizeIncrement, setBinSizeRatio, setNDecimalDigits, setVerbosity :: String -> CommandLineAction-			setBinSizeIncrement arg			= return {-to IO-monad-} . Data.CommandOptions.setBinSizeIncrement (readCommandArg arg)-			setBinSizeRatio arg			= return {-to IO-monad-} . Data.CommandOptions.setBinSizeRatio (readCommandArg arg)-			setNDecimalDigits arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getNDecimalDigits = readBoundedIntegral arg }-			setVerbosity arg commandOptions		= return {-to IO-monad-} commandOptions { Data.CommandOptions.getVerbosity = readCommandArg arg }--			setDeriveProbabilityMassFunction :: Maybe String -> CommandLineAction-			setDeriveProbabilityMassFunction arg commandOptions	= return {-to IO-monad-} commandOptions { Data.CommandOptions.getDeriveProbabilityMassFunction = Data.Maybe.maybe True readCommandArg arg }--			printVersion, printUsage :: IO CommandOptions-			printVersion	= System.IO.hPutStrLn System.IO.stderr (-				Distribution.Text.display packageIdentifier ++ "\n\nCompiled by " ++ show compiler ++ ".\n\nCopyright (C) 2013-2015 " ++ 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.-					Distribution.Package.pkgVersion	= Distribution.Version.Version (Data.Version.versionBranch Paths.version) []-				}--				author, compiler :: String-				author		= "Dr. Alistair Ward"-				compiler	= System.Info.compilerName ++ "-" ++ Data.List.intercalate "." (map show $ Data.Version.versionBranch System.Info.compilerVersion)--			printUsage	= System.IO.hPutStrLn System.IO.stderr ("Usage:\t" ++ G.usageInfo progName optDescrList ++ "  [<File-path> ...]") >> System.Exit.exitWith System.Exit.ExitSuccess--			runQuickChecks :: CommandOptions -> IO CommandOptions-			runQuickChecks _	= Test.QuickChecks.run >> System.Exit.exitWith System.Exit.ExitSuccess--	args	<- System.Environment.getArgs--	case G.getOpt G.RequireOrder optDescrList args of-		(commandLineActions, nonOptions, [{-errors-}])	-> do-			commandOptions	<- Data.List.foldl' (>>=) (return {-to IO-monad-} ToolShed.Defaultable.defaultValue) commandLineActions--			if not $ ToolShed.SelfValidate.isValid commandOptions-				then fail $ ToolShed.SelfValidate.getFirstError commandOptions-				else if null nonOptions-					then fail "zero file-paths specified"-					else let-						standardInputProxy	= "-"-					in do-						filePaths	<- if standardInputProxy `elem` nonOptions-							then let-								getFilePaths :: IO [String]-								getFilePaths	= do-									eof	<- System.IO.isEOF--									if eof-										then return {-to IO-monad-} []-										else {-more to read-} (:) <$> getLine <*> getFilePaths {-recurse-}-							in do-								filePaths	<- (filter (/= standardInputProxy) nonOptions ++) <$> getFilePaths--								if null filePaths-									then fail "zero file-paths"-									else return filePaths-							else {-real fileNames-} return {-to IO-monad-} nonOptions--						Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions == maxBound) $ System.IO.hPrint System.IO.stderr filePaths	-- CAVEAT: potentially very long.--						(fileSizeDistribution, statistics)	<- Control.Monad.Writer.runWriter . Profiler.calculateFileSizeDistribution commandOptions <$> mapM Data.File.findSize (Data.List.nub filePaths)--						Control.Monad.when (Data.CommandOptions.getVerbosity commandOptions > ToolShed.Defaultable.defaultValue) $ mapM_ (System.IO.hPutStrLn System.IO.stderr) statistics--						System.IO.putStrLn $ Profiler.formatFileSizeDistribution commandOptions fileSizeDistribution-		(_, _, errors)	-> System.IO.Error.ioError . System.IO.Error.userError $ concatMap init {-chop-} errors-