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.