packages feed

factory-0.2.1.1: src/Main.hs

{-
	Copyright (C) 2011-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@]

	* Contains the entry-point to the program.

	* Facilitates testing.
-}

module Main(main) where

import qualified	Data.Map
import qualified	Data.List
import qualified	Data.Version
import qualified	Distribution.Package
import qualified	Distribution.Text
import qualified	Distribution.Version
import qualified	Factory.Math.Hyperoperation			as Math.Hyperoperation
import qualified	Factory.Math.Implementations.Factorial		as Math.Implementations.Factorial
import qualified	Factory.Math.Implementations.Primality		as Math.Implementations.Primality
import qualified	Factory.Math.Implementations.PrimeFactorisation	as Math.Implementations.PrimeFactorisation
import qualified	Factory.Math.Implementations.Primes.Algorithm	as Math.Implementations.Primes.Algorithm
import qualified	Factory.Math.Implementations.SquareRoot		as Math.Implementations.SquareRoot
import qualified	Factory.Math.Probability			as Math.Probability
import qualified	Factory.Test.CommandOptions			as Test.CommandOptions
import qualified	Factory.Test.Performance.Factorial		as Test.Performance.Factorial
import qualified	Factory.Test.Performance.Hyperoperation		as Test.Performance.Hyperoperation
import qualified	Factory.Test.Performance.Pi			as Test.Performance.Pi
import qualified	Factory.Test.Performance.Primality		as Test.Performance.Primality
import qualified	Factory.Test.Performance.PrimeFactorisation	as Test.Performance.PrimeFactorisation
import qualified	Factory.Test.Performance.Primes			as Test.Performance.Primes
import qualified	Factory.Test.Performance.SquareRoot		as Test.Performance.SquareRoot
import qualified	Factory.Test.Performance.Statistics		as Test.Performance.Statistics
import qualified	Factory.Test.QuickCheck.QuickChecks		as Test.QuickCheck.QuickChecks
import qualified	Paths_factory					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.IO
import qualified	System.IO.Error
import qualified	System.Random
import qualified	ToolShed.Defaultable

-- Local convenience definitions.
type PrimalityAlgorithm		= Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm
type PiCategory			= Test.Performance.Pi.Category Math.Implementations.SquareRoot.Algorithm Math.Implementations.Factorial.Algorithm

-- | Used to thread user-defined command-line options, though the list of functions which implement them.
type CommandLineAction	= Test.CommandOptions.CommandOptions -> IO Test.CommandOptions.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 "

-- | Parses the command-line arguments, to determine 'Test.CommandOptions.CommandOptions'.
main :: IO ()
main	= do
	System.IO.hClose System.IO.stdin	-- Nothing is read from standard input.

	progName	<- System.Environment.getProgName

	let
		usageMessage :: String
		usageMessage	= "Usage:\t" ++ G.usageInfo progName optDescrList

		optDescrList :: [G.OptDescr CommandLineAction]
		optDescrList	= [
--				 String	[String]					(G.ArgDescr CommandLineAction)												String
			G.Option "?"	["help"]					(G.NoArg $ const printUsage)												"Display this help-text & then exit.",
			G.Option ""	["verbose"]					(G.NoArg $ return {-to IO-monad-} . Test.CommandOptions.setVerbose)							("Provide additional information where available; default '" ++ show (Test.CommandOptions.verbose ToolShed.Defaultable.defaultValue) ++ "'."),
			G.Option ""	["version"]					(G.NoArg $ const printVersion)												"Print version-information & then exit.",
			G.Option "q"	["runQuickChecks"]				(G.NoArg $ const runQuickChecks)											"Run Quick-checks using arbitrary data & then exit.",
			G.Option ""	["carmichaelNumbersPerformance"]		(carmichaelNumbersPerformance `G.ReqArg` "(Math.Implementations.Primality.Algorithm, Int)")				"Test the performance of 'Math.Primality.carmichaelNumbers'.",
			G.Option ""	["factorialPerformance"]			(factorialPerformance `G.ReqArg` "(Math.Implementations.Factorial.Algorithm, Integer)")					"Test the performance of 'Math.Factorial.factorial'.",
			G.Option ""	["factorialPerformanceGraph"]			(factorialPerformanceGraph `G.ReqArg` "Math.Implementations.Factorial.Algorithm")					"Test the performance of 'Math.Factorial.factorial', with an exponentially increasing operand.",
			G.Option ""	["factorialPerformanceGraphControl"]		(G.NoArg factorialPerformanceGraphControl)										"Test the performance of a naive factorial-implementation, with an exponentially increasing operand.",
			G.Option ""	["hyperoperationPerformance"]			(hyperoperationPerformance `G.ReqArg` "(Integer, Math.Hyperoperation.Base, Math.Hyperoperation.HyperExponent)")		"Test the performance of 'Math.Hyperoperation.hyperoperation', against the specified rank, base and hyper-exponent.",
			G.Option ""	["hyperoperationPerformanceGraphRank"]		(hyperoperationPerformanceGraphRank `G.ReqArg` "(Math.Hyperoperation.Base, Math.Hyperoperation.HyperExponent)")		"Test the performance of 'Math.Hyperoperation.hyperoperation', for the specified base and hyper-exponent, and a linearly increasing rank.",
			G.Option ""	["hyperoperationPerformanceGraphExponent"]	(hyperoperationPerformanceGraphExponent `G.ReqArg` "(Integer, Math.Hyperoperation.Base)")				"Test the performance of 'Math.Hyperoperation.hyperoperation', for the specified rank and base, and a linearly increasing hyper-exponent.",
			G.Option ""	["isPrimePerformance"]				(isPrimePerformance `G.ReqArg` "(Math.Implementations.Primality.Algorithm, Integer)")					"Test the performance of 'Math.Primality.isPrime'.",
			G.Option ""	["isPrimePerformanceGraph"]			(isPrimePerformanceGraph `G.ReqArg` "Math.Implementations.Primality.Algorithm")						"Test the performance of 'Math.Primality.isPrime', against the prime-indexed Fibonacci-numbers.",
			G.Option ""	["mersenneNumbersPerformance"]			(mersenneNumbersPerformance `G.ReqArg` "(Math.Implementations.Primes.Algorithm.Algorithm, Int)")			"Test the performance of 'Math.Primes.mersenneNumbers'.",
			G.Option ""	["factorialPerformance"]			(factorialPerformance `G.ReqArg` "(Math.Implementations.Factorial.Algorithm, Integer)")					"Test the performance of 'Math.Factorial.factorial'.",
			G.Option ""	["nCrPerformance"]				(nCrPerformance `G.ReqArg` "(Math.Implementations.Factorial.Algorithm, Integer, Integer)")				"Test the performance of 'Math.Factorial.factorial'.",
			G.Option ""	["piPerformance"]				(piPerformance `G.ReqArg` "(Math.Pi.Category, Math.Precision.DecimalDigits)")						"Test the performance of 'Math.Pi.openI'.",
			G.Option ""	["piPerformanceGraph"]				(piPerformanceGraph `G.ReqArg` "(Math.Pi.Category, Double, Math.Precision.DecimalDigits)")				"Test the performance of 'Math.Pi.openI', with an exponential precision-requirement (of the specified exponent), up to the specified limit.",
			G.Option ""	["plotDiscreteDistribution"]			(plotDiscreteDistribution `G.ReqArg` "(Int, Math.Probability.DiscreteDistribution)")					"Plot the Probability Mass function for the specified discrete distribution.",
			G.Option ""	["primeFactorsPerformance"]			(primeFactorsPerformance `G.ReqArg` "(Math.Implementations.PrimeFactorisation.Algorithm, Integer)")			"Test the performance of 'Math.PrimeFactorisation.primeFactors'.",
			G.Option ""	["primeFactorsPerformanceGraph"]		(primeFactorsPerformanceGraph `G.ReqArg` "(Math.Implementations.PrimeFactorisation.Algorithm, Int)")			"Test the performance of 'Math.PrimeFactorisation.primeFactors', on the specified number of odd integers from the Fibonacci-sequence.",
			G.Option ""	["primesPerformance"]				(primesPerformance `G.ReqArg` "(Math.Implementations.Primes.Algorithm.Algorithm, Int)")					"Test the performance of 'Math.Primes.primes'.",
			G.Option ""	["squareRootPerformance"]			(squareRootPerformance `G.ReqArg` "(Math.Implementations.SquareRoot.Algorithm, Rational, DecimalDigits)")	"Test the performance of 'Math.SquareRoot.squareRoot'.",
			G.Option ""	["squareRootPerformanceGraph"]			(squareRootPerformanceGraph `G.ReqArg` "(Math.Implementations.SquareRoot.Algorithm, Rational)")		"Test the performance of 'Math.SquareRoot.squareRoot', with an exponentially increasing precision-requirement."
		 ] where
			printVersion, printUsage, runQuickChecks :: IO Test.CommandOptions.CommandOptions
			printVersion	= System.IO.hPutStrLn System.IO.stderr (Distribution.Text.display packageIdentifier ++ "\n\nCopyright (C) 2011-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 :: String
				author	= "Dr. Alistair Ward"

			printUsage	= System.IO.hPutStrLn System.IO.stderr usageMessage	>> System.Exit.exitWith System.Exit.ExitSuccess

			runQuickChecks	= Test.QuickCheck.QuickChecks.run			>> System.Exit.exitWith System.Exit.ExitSuccess

			factorialPerformanceGraphControl :: Test.CommandOptions.CommandOptions -> IO Test.CommandOptions.CommandOptions
			factorialPerformanceGraphControl commandOptions	= Test.Performance.Factorial.factorialPerformanceGraphControl (Test.CommandOptions.verbose commandOptions)	>> System.Exit.exitWith (System.Exit.ExitFailure 1)

			carmichaelNumbersPerformance, factorialPerformance, factorialPerformanceGraph, hyperoperationPerformance, hyperoperationPerformanceGraphRank, hyperoperationPerformanceGraphExponent, isPrimePerformance, isPrimePerformanceGraph, mersenneNumbersPerformance, piPerformance, piPerformanceGraph, plotDiscreteDistribution, primeFactorsPerformance, primesPerformance, squareRootPerformance, squareRootPerformanceGraph :: String -> CommandLineAction

			carmichaelNumbersPerformance arg _	= Test.Performance.Primality.carmichaelNumbersPerformance algorithm i >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm :: PrimalityAlgorithm
				(algorithm, i)	= readCommandArg arg

			factorialPerformance arg _	= Test.Performance.Factorial.factorialPerformance algorithm i >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm	:: Math.Implementations.Factorial.Algorithm
				i		:: Integer
				(algorithm, i)	= readCommandArg arg

			factorialPerformanceGraph arg commandOptions	= Test.Performance.Factorial.factorialPerformanceGraph (Test.CommandOptions.verbose commandOptions) (readCommandArg arg :: Math.Implementations.Factorial.Algorithm)	>> System.Exit.exitWith (System.Exit.ExitFailure 1)

			hyperoperationPerformance arg _	= Test.Performance.Hyperoperation.hyperoperationPerformance rank base hyperExponent >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				rank		:: Integer
				base		:: Math.Hyperoperation.Base
				hyperExponent	:: Math.Hyperoperation.HyperExponent
				(rank, base, hyperExponent)	= readCommandArg arg

			hyperoperationPerformanceGraphRank arg commandOptions	= Test.Performance.Hyperoperation.hyperoperationPerformanceGraphRank (Test.CommandOptions.verbose commandOptions) base hyperExponent >> System.Exit.exitWith (System.Exit.ExitFailure 1)	where
				base		:: Math.Hyperoperation.Base
				hyperExponent	:: Math.Hyperoperation.HyperExponent
				(base, hyperExponent)	= readCommandArg arg

			hyperoperationPerformanceGraphExponent arg commandOptions	= Test.Performance.Hyperoperation.hyperoperationPerformanceGraphExponent (Test.CommandOptions.verbose commandOptions) rank base >> System.Exit.exitWith (System.Exit.ExitFailure 1)	where
				rank	:: Integer
				base	:: Math.Hyperoperation.Base
				(rank, base)	= readCommandArg arg

			isPrimePerformance arg _	= Test.Performance.Primality.isPrimePerformance algorithm i >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm	:: PrimalityAlgorithm
				i		:: Integer
				(algorithm, i)	= readCommandArg arg

			isPrimePerformanceGraph arg _	= Test.Performance.Primality.isPrimePerformanceGraph (readCommandArg arg :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm) >> System.Exit.exitWith (System.Exit.ExitFailure 1)

			mersenneNumbersPerformance arg _	= Test.Performance.Primes.mersenneNumbersPerformance algorithm i >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm :: Math.Implementations.Primes.Algorithm.Algorithm
				(algorithm, i)	= readCommandArg arg

			nCrPerformance arg _	= Test.Performance.Statistics.nCrPerformance algorithm n r >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm	:: Math.Implementations.Factorial.Algorithm
				n, r		:: Integer
				(algorithm, n, r)	= readCommandArg arg

			piPerformance arg _	= Test.Performance.Pi.piPerformance category decimalDigits >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				category :: PiCategory
				(category, decimalDigits)	= readCommandArg arg

			piPerformanceGraph arg commandOptions	= Test.Performance.Pi.piPerformanceGraph category factor maxDecimalDigits (Test.CommandOptions.verbose commandOptions) >> System.Exit.exitWith (System.Exit.ExitFailure 1)	where
				category	:: PiCategory
				factor		:: Double
				(category, factor, maxDecimalDigits)	= readCommandArg arg

			plotDiscreteDistribution arg _	= let
				distribution :: Math.Probability.DiscreteDistribution Double
				(n, distribution)	= readCommandArg arg
			 in do
				System.Random.getStdGen >>= print . Data.Map.toList . Data.Map.map ((/ (fromIntegral n :: Double)) . fromInteger) . Data.Map.fromListWith (+) . (`zip` repeat 1) . (take n :: [Integer] -> [Integer]) . Math.Probability.generateDiscretePopulation distribution

				System.Exit.exitWith System.Exit.ExitSuccess

			primeFactorsPerformance arg _	= Test.Performance.PrimeFactorisation.primeFactorsPerformance algorithm i >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm :: Math.Implementations.PrimeFactorisation.Algorithm
				(algorithm, i)	= readCommandArg arg

			primeFactorsPerformanceGraph arg _	= Test.Performance.PrimeFactorisation.primeFactorsPerformanceGraph algorithm index >> System.Exit.exitWith (System.Exit.ExitFailure 1)	where
				algorithm :: Math.Implementations.PrimeFactorisation.Algorithm
				(algorithm, index)	= readCommandArg arg

			primesPerformance arg _	= (
				(
{-
	Hard-code specific algorithms, so the simplifier triggers rewrite-rules in "Math.Implementations.Primes",
	ready for run-time definitions of 'algorithm' to exploit as appropriate.
	CAVEAT: fragile.
-}
					case algorithm of
						Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize	-> Test.Performance.Primes.primesPerformance $ Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize
						Math.Implementations.Primes.Algorithm.SieveOfAtkin maxPrime		-> Test.Performance.Primes.primesPerformance $ Math.Implementations.Primes.Algorithm.SieveOfAtkin maxPrime
						_									-> Test.Performance.Primes.primesPerformance algorithm
				) index :: IO (
					Double,
--					Integer
					Int	-- Exploits rewrite-rules in "Math.Implementations.Primes.*".
				)
			 ) >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm :: Math.Implementations.Primes.Algorithm.Algorithm
				(algorithm, index)	= readCommandArg arg

			squareRootPerformance arg _	= Test.Performance.SquareRoot.squareRootPerformance algorithm operand decimalDigits >>= print >> System.Exit.exitWith System.Exit.ExitSuccess	where
				algorithm	:: Math.Implementations.SquareRoot.Algorithm
				operand		:: Rational
				(algorithm, operand, decimalDigits)	= readCommandArg arg

			squareRootPerformanceGraph arg _	= Test.Performance.SquareRoot.squareRootPerformanceGraph algorithm operand >> System.Exit.exitWith (System.Exit.ExitFailure 1)	where
				algorithm	:: Math.Implementations.SquareRoot.Algorithm
				operand		:: Rational
				(algorithm, operand)	= readCommandArg arg

	args	<- System.Environment.getArgs

--	G.getOpt :: G.ArgOrder CommandLineAction -> [G.OptDescr Action] -> [String] -> ([Action], [String], [String])
	case G.getOpt G.RequireOrder optDescrList args of
		(commandLineActions, _, [])	-> Data.List.foldl' (>>=) (return {-to IO-monad-} ToolShed.Defaultable.defaultValue) commandLineActions	>> System.Exit.exitWith System.Exit.ExitSuccess
		(_, _, errors)			-> System.IO.Error.ioError . System.IO.Error.userError $ concat errors ++ usageMessage	-- Throw.