factory 0.2.1.1 → 0.2.1.2
raw patch · 176 files changed
+8316/−8232 lines, 176 filesdep +factorydep ~Cabaldep ~toolshedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: factory
Dependency ranges changed: Cabal, toolshed
API changes (from Hackage documentation)
- Factory.Data.MonicPolynomial: instance (Eq c, Eq e) => Eq (MonicPolynomial c e)
- Factory.Data.MonicPolynomial: instance (Eq c, Num c, Num e, Ord e, Show c, Show e) => QuotientRing (MonicPolynomial c e)
- Factory.Data.MonicPolynomial: instance (Eq c, Num c, Num e, Ord e, Show c, Show e) => Ring (MonicPolynomial c e)
- Factory.Data.MonicPolynomial: instance (Show c, Show e) => Show (MonicPolynomial c e)
- Factory.Data.Polynomial: instance (Eq c, Fractional c, Num e, Ord e) => QuotientRing (Polynomial c e)
- Factory.Data.Polynomial: instance (Eq c, Num c, Num e, Ord e) => Ring (Polynomial c e)
- Factory.Data.Polynomial: instance (Eq coefficient, Eq exponent) => Eq (Polynomial coefficient exponent)
- Factory.Data.Polynomial: instance (Show coefficient, Show exponent) => Show (Polynomial coefficient exponent)
- Factory.Data.PrimeWheel: instance Show i => Show (PrimeWheel i)
- Factory.Data.Ring: instance Read p => Read (Product p)
- Factory.Data.Ring: instance Read s => Read (Sum s)
- Factory.Data.Ring: instance Ring r => Monoid (Product r)
- Factory.Data.Ring: instance Ring r => Monoid (Sum r)
- Factory.Data.Ring: instance Show p => Show (Product p)
- Factory.Data.Ring: instance Show s => Show (Sum s)
- Factory.Math.Implementations.Factorial: instance Algorithmic Algorithm
- Factory.Math.Implementations.Factorial: instance Defaultable Algorithm
- Factory.Math.Implementations.Factorial: instance Eq Algorithm
- Factory.Math.Implementations.Factorial: instance Read Algorithm
- Factory.Math.Implementations.Factorial: instance Show Algorithm
- Factory.Math.Implementations.Pi.AGM.Algorithm: instance Algorithmic squareRootAlgorithm => Algorithmic (Algorithm squareRootAlgorithm)
- Factory.Math.Implementations.Pi.AGM.Algorithm: instance Defaultable squareRootAlgorithm => Defaultable (Algorithm squareRootAlgorithm)
- Factory.Math.Implementations.Pi.AGM.Algorithm: instance Eq squareRootAlgorithm => Eq (Algorithm squareRootAlgorithm)
- Factory.Math.Implementations.Pi.AGM.Algorithm: instance Read squareRootAlgorithm => Read (Algorithm squareRootAlgorithm)
- Factory.Math.Implementations.Pi.AGM.Algorithm: instance Show squareRootAlgorithm => Show (Algorithm squareRootAlgorithm)
- Factory.Math.Implementations.Pi.BBP.Algorithm: instance Algorithmic Algorithm
- Factory.Math.Implementations.Pi.BBP.Algorithm: instance Defaultable Algorithm
- Factory.Math.Implementations.Pi.BBP.Algorithm: instance Eq Algorithm
- Factory.Math.Implementations.Pi.BBP.Algorithm: instance Read Algorithm
- Factory.Math.Implementations.Pi.BBP.Algorithm: instance Show Algorithm
- Factory.Math.Implementations.Pi.BBP.Series: base :: Series -> Integer
- Factory.Math.Implementations.Pi.BBP.Series: getDenominators :: Series -> Int -> [Integer]
- Factory.Math.Implementations.Pi.BBP.Series: numerators :: Series -> [Integer]
- Factory.Math.Implementations.Pi.BBP.Series: seriesScalingFactor :: Series -> Rational
- Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Algorithmic squareRootAlgorithm, Algorithmic factorialAlgorithm) => Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Defaultable squareRootAlgorithm, Defaultable factorialAlgorithm) => Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Eq squareRootAlgorithm, Eq factorialAlgorithm) => Eq (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Read squareRootAlgorithm, Read factorialAlgorithm) => Read (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Show squareRootAlgorithm, Show factorialAlgorithm) => Show (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Borwein.Series: convergenceRate :: Series squareRootAlgorithm factorialAlgorithm -> ConvergenceRate
- Factory.Math.Implementations.Pi.Borwein.Series: terms :: Series squareRootAlgorithm factorialAlgorithm -> squareRootAlgorithm -> factorialAlgorithm -> DecimalDigits -> (Rational, [Rational])
- Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Algorithmic squareRootAlgorithm, Algorithmic factorialAlgorithm) => Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Defaultable squareRootAlgorithm, Defaultable factorialAlgorithm) => Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Eq squareRootAlgorithm, Eq factorialAlgorithm) => Eq (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Read squareRootAlgorithm, Read factorialAlgorithm) => Read (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Show squareRootAlgorithm, Show factorialAlgorithm) => Show (Algorithm squareRootAlgorithm factorialAlgorithm)
- Factory.Math.Implementations.Pi.Ramanujan.Series: convergenceRate :: Series squareRootAlgorithm factorialAlgorithm -> ConvergenceRate
- Factory.Math.Implementations.Pi.Ramanujan.Series: getSeriesScalingFactor :: Series squareRootAlgorithm factorialAlgorithm -> squareRootAlgorithm -> DecimalDigits -> Rational
- Factory.Math.Implementations.Pi.Ramanujan.Series: terms :: Series squareRootAlgorithm factorialAlgorithm -> factorialAlgorithm -> [Rational]
- Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Algorithmic Algorithm
- Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Defaultable Algorithm
- Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Eq Algorithm
- Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Read Algorithm
- Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Show Algorithm
- Factory.Math.Implementations.Pi.Spigot.Series: baseDenominators :: Series i -> [i]
- Factory.Math.Implementations.Pi.Spigot.Series: baseNumerators :: Series i -> [i]
- Factory.Math.Implementations.Pi.Spigot.Series: coefficients :: Series i -> [i]
- Factory.Math.Implementations.Pi.Spigot.Series: nTerms :: Series i -> DecimalDigits -> Int
- Factory.Math.Implementations.Primality: instance Algorithmic factorisationAlgorithm => Algorithmic (Algorithm factorisationAlgorithm)
- Factory.Math.Implementations.Primality: instance Defaultable (Algorithm factorisationAlgorithm)
- Factory.Math.Implementations.Primality: instance Eq factorisationAlgorithm => Eq (Algorithm factorisationAlgorithm)
- Factory.Math.Implementations.Primality: instance Read factorisationAlgorithm => Read (Algorithm factorisationAlgorithm)
- Factory.Math.Implementations.Primality: instance Show factorisationAlgorithm => Show (Algorithm factorisationAlgorithm)
- Factory.Math.Implementations.PrimeFactorisation: instance Algorithmic Algorithm
- Factory.Math.Implementations.PrimeFactorisation: instance Defaultable Algorithm
- Factory.Math.Implementations.PrimeFactorisation: instance Eq Algorithm
- Factory.Math.Implementations.PrimeFactorisation: instance Read Algorithm
- Factory.Math.Implementations.PrimeFactorisation: instance Show Algorithm
- Factory.Math.Implementations.Primes.Algorithm: instance Algorithmic Algorithm
- Factory.Math.Implementations.Primes.Algorithm: instance Defaultable Algorithm
- Factory.Math.Implementations.Primes.Algorithm: instance Eq Algorithm
- Factory.Math.Implementations.Primes.Algorithm: instance Read Algorithm
- Factory.Math.Implementations.Primes.Algorithm: instance Show Algorithm
- Factory.Math.Implementations.Primes.SieveOfAtkin: instance Eq PolynomialType
- Factory.Math.Implementations.SquareRoot: instance Algorithmic Algorithm
- Factory.Math.Implementations.SquareRoot: instance Defaultable Algorithm
- Factory.Math.Implementations.SquareRoot: instance Eq Algorithm
- Factory.Math.Implementations.SquareRoot: instance Iterator Algorithm
- Factory.Math.Implementations.SquareRoot: instance Read Algorithm
- Factory.Math.Implementations.SquareRoot: instance Show Algorithm
- Factory.Math.Pi: instance (Algorithmic agm, Algorithmic bbp, Algorithmic borwein, Algorithmic ramanujan, Algorithmic spigot) => Algorithmic (Category agm bbp borwein ramanujan spigot)
- Factory.Math.Pi: instance (Defaultable agm, Defaultable bbp, Defaultable borwein, Defaultable ramanujan, Defaultable spigot) => Defaultable (Category agm bbp borwein ramanujan spigot)
- Factory.Math.Pi: instance (Eq agm, Eq bbp, Eq borwein, Eq ramanujan, Eq spigot) => Eq (Category agm bbp borwein ramanujan spigot)
- Factory.Math.Pi: instance (Read agm, Read bbp, Read borwein, Read ramanujan, Read spigot) => Read (Category agm bbp borwein ramanujan spigot)
- Factory.Math.Pi: instance (Show agm, Show bbp, Show borwein, Show ramanujan, Show spigot) => Show (Category agm bbp borwein ramanujan spigot)
- Factory.Math.Probability: instance (Floating parameter, Ord parameter, Show parameter) => SelfValidator (ContinuousDistribution parameter)
- Factory.Math.Probability: instance (Num parameter, Ord parameter, Show parameter) => SelfValidator (DiscreteDistribution parameter)
- Factory.Math.Probability: instance (RealFloat parameter, Show parameter, Random parameter) => Distribution (ContinuousDistribution parameter)
- Factory.Math.Probability: instance (RealFloat parameter, Show parameter, Random parameter) => Distribution (DiscreteDistribution parameter)
- Factory.Math.Probability: instance Eq parameter => Eq (ContinuousDistribution parameter)
- Factory.Math.Probability: instance Eq parameter => Eq (DiscreteDistribution parameter)
- Factory.Math.Probability: instance Read parameter => Read (ContinuousDistribution parameter)
- Factory.Math.Probability: instance Read parameter => Read (DiscreteDistribution parameter)
- Factory.Math.Probability: instance Show parameter => Show (ContinuousDistribution parameter)
- Factory.Math.Probability: instance Show parameter => Show (DiscreteDistribution parameter)
+ Factory.Data.MonicPolynomial: instance (GHC.Classes.Eq c, GHC.Classes.Eq e) => GHC.Classes.Eq (Factory.Data.MonicPolynomial.MonicPolynomial c e)
+ Factory.Data.MonicPolynomial: instance (GHC.Classes.Eq c, GHC.Num.Num c, GHC.Num.Num e, GHC.Classes.Ord e, GHC.Show.Show c, GHC.Show.Show e) => Factory.Data.QuotientRing.QuotientRing (Factory.Data.MonicPolynomial.MonicPolynomial c e)
+ Factory.Data.MonicPolynomial: instance (GHC.Classes.Eq c, GHC.Num.Num c, GHC.Num.Num e, GHC.Classes.Ord e, GHC.Show.Show c, GHC.Show.Show e) => Factory.Data.Ring.Ring (Factory.Data.MonicPolynomial.MonicPolynomial c e)
+ Factory.Data.MonicPolynomial: instance (GHC.Show.Show c, GHC.Show.Show e) => GHC.Show.Show (Factory.Data.MonicPolynomial.MonicPolynomial c e)
+ Factory.Data.Polynomial: instance (GHC.Classes.Eq c, GHC.Num.Num c, GHC.Num.Num e, GHC.Classes.Ord e) => Factory.Data.Ring.Ring (Factory.Data.Polynomial.Polynomial c e)
+ Factory.Data.Polynomial: instance (GHC.Classes.Eq c, GHC.Real.Fractional c, GHC.Num.Num e, GHC.Classes.Ord e) => Factory.Data.QuotientRing.QuotientRing (Factory.Data.Polynomial.Polynomial c e)
+ Factory.Data.Polynomial: instance (GHC.Classes.Eq coefficient, GHC.Classes.Eq exponent) => GHC.Classes.Eq (Factory.Data.Polynomial.Polynomial coefficient exponent)
+ Factory.Data.Polynomial: instance (GHC.Show.Show coefficient, GHC.Show.Show exponent) => GHC.Show.Show (Factory.Data.Polynomial.Polynomial coefficient exponent)
+ Factory.Data.PrimeWheel: instance GHC.Show.Show i => GHC.Show.Show (Factory.Data.PrimeWheel.PrimeWheel i)
+ Factory.Data.Ring: instance Factory.Data.Ring.Ring r => GHC.Base.Monoid (Factory.Data.Ring.Product r)
+ Factory.Data.Ring: instance Factory.Data.Ring.Ring r => GHC.Base.Monoid (Factory.Data.Ring.Sum r)
+ Factory.Data.Ring: instance GHC.Read.Read p => GHC.Read.Read (Factory.Data.Ring.Product p)
+ Factory.Data.Ring: instance GHC.Read.Read s => GHC.Read.Read (Factory.Data.Ring.Sum s)
+ Factory.Data.Ring: instance GHC.Show.Show p => GHC.Show.Show (Factory.Data.Ring.Product p)
+ Factory.Data.Ring: instance GHC.Show.Show s => GHC.Show.Show (Factory.Data.Ring.Sum s)
+ Factory.Math.Implementations.Factorial: instance Factory.Math.Factorial.Algorithmic Factory.Math.Implementations.Factorial.Algorithm
+ Factory.Math.Implementations.Factorial: instance GHC.Classes.Eq Factory.Math.Implementations.Factorial.Algorithm
+ Factory.Math.Implementations.Factorial: instance GHC.Read.Read Factory.Math.Implementations.Factorial.Algorithm
+ Factory.Math.Implementations.Factorial: instance GHC.Show.Show Factory.Math.Implementations.Factorial.Algorithm
+ Factory.Math.Implementations.Factorial: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.Factorial.Algorithm
+ Factory.Math.Implementations.Pi.AGM.Algorithm: instance Factory.Math.SquareRoot.Algorithmic squareRootAlgorithm => Factory.Math.Pi.Algorithmic (Factory.Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm)
+ Factory.Math.Implementations.Pi.AGM.Algorithm: instance GHC.Classes.Eq squareRootAlgorithm => GHC.Classes.Eq (Factory.Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm)
+ Factory.Math.Implementations.Pi.AGM.Algorithm: instance GHC.Read.Read squareRootAlgorithm => GHC.Read.Read (Factory.Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm)
+ Factory.Math.Implementations.Pi.AGM.Algorithm: instance GHC.Show.Show squareRootAlgorithm => GHC.Show.Show (Factory.Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm)
+ Factory.Math.Implementations.Pi.AGM.Algorithm: instance ToolShed.Defaultable.Defaultable squareRootAlgorithm => ToolShed.Defaultable.Defaultable (Factory.Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm)
+ Factory.Math.Implementations.Pi.BBP.Algorithm: instance Factory.Math.Pi.Algorithmic Factory.Math.Implementations.Pi.BBP.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.BBP.Algorithm: instance GHC.Classes.Eq Factory.Math.Implementations.Pi.BBP.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.BBP.Algorithm: instance GHC.Read.Read Factory.Math.Implementations.Pi.BBP.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.BBP.Algorithm: instance GHC.Show.Show Factory.Math.Implementations.Pi.BBP.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.BBP.Algorithm: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.Pi.BBP.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.BBP.Series: [base] :: Series -> Integer
+ Factory.Math.Implementations.Pi.BBP.Series: [getDenominators] :: Series -> Int -> [Integer]
+ Factory.Math.Implementations.Pi.BBP.Series: [numerators] :: Series -> [Integer]
+ Factory.Math.Implementations.Pi.BBP.Series: [seriesScalingFactor] :: Series -> Rational
+ Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (Factory.Math.SquareRoot.Algorithmic squareRootAlgorithm, Factory.Math.Factorial.Algorithmic factorialAlgorithm) => Factory.Math.Pi.Algorithmic (Factory.Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (GHC.Classes.Eq squareRootAlgorithm, GHC.Classes.Eq factorialAlgorithm) => GHC.Classes.Eq (Factory.Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (GHC.Read.Read squareRootAlgorithm, GHC.Read.Read factorialAlgorithm) => GHC.Read.Read (Factory.Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (GHC.Show.Show squareRootAlgorithm, GHC.Show.Show factorialAlgorithm) => GHC.Show.Show (Factory.Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Borwein.Algorithm: instance (ToolShed.Defaultable.Defaultable squareRootAlgorithm, ToolShed.Defaultable.Defaultable factorialAlgorithm) => ToolShed.Defaultable.Defaultable (Factory.Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Borwein.Series: [convergenceRate] :: Series squareRootAlgorithm factorialAlgorithm -> ConvergenceRate
+ Factory.Math.Implementations.Pi.Borwein.Series: [terms] :: Series squareRootAlgorithm factorialAlgorithm -> squareRootAlgorithm -> factorialAlgorithm -> DecimalDigits -> (Rational, [Rational])
+ Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (Factory.Math.SquareRoot.Algorithmic squareRootAlgorithm, Factory.Math.Factorial.Algorithmic factorialAlgorithm) => Factory.Math.Pi.Algorithmic (Factory.Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (GHC.Classes.Eq squareRootAlgorithm, GHC.Classes.Eq factorialAlgorithm) => GHC.Classes.Eq (Factory.Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (GHC.Read.Read squareRootAlgorithm, GHC.Read.Read factorialAlgorithm) => GHC.Read.Read (Factory.Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (GHC.Show.Show squareRootAlgorithm, GHC.Show.Show factorialAlgorithm) => GHC.Show.Show (Factory.Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Ramanujan.Algorithm: instance (ToolShed.Defaultable.Defaultable squareRootAlgorithm, ToolShed.Defaultable.Defaultable factorialAlgorithm) => ToolShed.Defaultable.Defaultable (Factory.Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm)
+ Factory.Math.Implementations.Pi.Ramanujan.Series: [convergenceRate] :: Series squareRootAlgorithm factorialAlgorithm -> ConvergenceRate
+ Factory.Math.Implementations.Pi.Ramanujan.Series: [getSeriesScalingFactor] :: Series squareRootAlgorithm factorialAlgorithm -> squareRootAlgorithm -> DecimalDigits -> Rational
+ Factory.Math.Implementations.Pi.Ramanujan.Series: [terms] :: Series squareRootAlgorithm factorialAlgorithm -> factorialAlgorithm -> [Rational]
+ Factory.Math.Implementations.Pi.Spigot.Algorithm: instance Factory.Math.Pi.Algorithmic Factory.Math.Implementations.Pi.Spigot.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.Spigot.Algorithm: instance GHC.Classes.Eq Factory.Math.Implementations.Pi.Spigot.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.Spigot.Algorithm: instance GHC.Read.Read Factory.Math.Implementations.Pi.Spigot.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.Spigot.Algorithm: instance GHC.Show.Show Factory.Math.Implementations.Pi.Spigot.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.Spigot.Algorithm: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.Pi.Spigot.Algorithm.Algorithm
+ Factory.Math.Implementations.Pi.Spigot.Series: [baseDenominators] :: Series i -> [i]
+ Factory.Math.Implementations.Pi.Spigot.Series: [baseNumerators] :: Series i -> [i]
+ Factory.Math.Implementations.Pi.Spigot.Series: [coefficients] :: Series i -> [i]
+ Factory.Math.Implementations.Pi.Spigot.Series: [nTerms] :: Series i -> DecimalDigits -> Int
+ Factory.Math.Implementations.Primality: instance Factory.Math.PrimeFactorisation.Algorithmic factorisationAlgorithm => Factory.Math.Primality.Algorithmic (Factory.Math.Implementations.Primality.Algorithm factorisationAlgorithm)
+ Factory.Math.Implementations.Primality: instance GHC.Classes.Eq factorisationAlgorithm => GHC.Classes.Eq (Factory.Math.Implementations.Primality.Algorithm factorisationAlgorithm)
+ Factory.Math.Implementations.Primality: instance GHC.Read.Read factorisationAlgorithm => GHC.Read.Read (Factory.Math.Implementations.Primality.Algorithm factorisationAlgorithm)
+ Factory.Math.Implementations.Primality: instance GHC.Show.Show factorisationAlgorithm => GHC.Show.Show (Factory.Math.Implementations.Primality.Algorithm factorisationAlgorithm)
+ Factory.Math.Implementations.Primality: instance ToolShed.Defaultable.Defaultable (Factory.Math.Implementations.Primality.Algorithm factorisationAlgorithm)
+ Factory.Math.Implementations.PrimeFactorisation: instance Factory.Math.PrimeFactorisation.Algorithmic Factory.Math.Implementations.PrimeFactorisation.Algorithm
+ Factory.Math.Implementations.PrimeFactorisation: instance GHC.Classes.Eq Factory.Math.Implementations.PrimeFactorisation.Algorithm
+ Factory.Math.Implementations.PrimeFactorisation: instance GHC.Read.Read Factory.Math.Implementations.PrimeFactorisation.Algorithm
+ Factory.Math.Implementations.PrimeFactorisation: instance GHC.Show.Show Factory.Math.Implementations.PrimeFactorisation.Algorithm
+ Factory.Math.Implementations.PrimeFactorisation: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.PrimeFactorisation.Algorithm
+ Factory.Math.Implementations.Primes.Algorithm: instance Factory.Math.Primes.Algorithmic Factory.Math.Implementations.Primes.Algorithm.Algorithm
+ Factory.Math.Implementations.Primes.Algorithm: instance GHC.Classes.Eq Factory.Math.Implementations.Primes.Algorithm.Algorithm
+ Factory.Math.Implementations.Primes.Algorithm: instance GHC.Read.Read Factory.Math.Implementations.Primes.Algorithm.Algorithm
+ Factory.Math.Implementations.Primes.Algorithm: instance GHC.Show.Show Factory.Math.Implementations.Primes.Algorithm.Algorithm
+ Factory.Math.Implementations.Primes.Algorithm: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.Primes.Algorithm.Algorithm
+ Factory.Math.Implementations.Primes.SieveOfAtkin: instance GHC.Classes.Eq Factory.Math.Implementations.Primes.SieveOfAtkin.PolynomialType
+ Factory.Math.Implementations.SquareRoot: instance Factory.Math.SquareRoot.Algorithmic Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Implementations.SquareRoot: instance Factory.Math.SquareRoot.Iterator Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Implementations.SquareRoot: instance GHC.Classes.Eq Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Implementations.SquareRoot: instance GHC.Read.Read Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Implementations.SquareRoot: instance GHC.Show.Show Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Implementations.SquareRoot: instance ToolShed.Defaultable.Defaultable Factory.Math.Implementations.SquareRoot.Algorithm
+ Factory.Math.Pi: instance (Factory.Math.Pi.Algorithmic agm, Factory.Math.Pi.Algorithmic bbp, Factory.Math.Pi.Algorithmic borwein, Factory.Math.Pi.Algorithmic ramanujan, Factory.Math.Pi.Algorithmic spigot) => Factory.Math.Pi.Algorithmic (Factory.Math.Pi.Category agm bbp borwein ramanujan spigot)
+ Factory.Math.Pi: instance (GHC.Classes.Eq agm, GHC.Classes.Eq bbp, GHC.Classes.Eq borwein, GHC.Classes.Eq ramanujan, GHC.Classes.Eq spigot) => GHC.Classes.Eq (Factory.Math.Pi.Category agm bbp borwein ramanujan spigot)
+ Factory.Math.Pi: instance (GHC.Read.Read agm, GHC.Read.Read bbp, GHC.Read.Read borwein, GHC.Read.Read ramanujan, GHC.Read.Read spigot) => GHC.Read.Read (Factory.Math.Pi.Category agm bbp borwein ramanujan spigot)
+ Factory.Math.Pi: instance (GHC.Show.Show agm, GHC.Show.Show bbp, GHC.Show.Show borwein, GHC.Show.Show ramanujan, GHC.Show.Show spigot) => GHC.Show.Show (Factory.Math.Pi.Category agm bbp borwein ramanujan spigot)
+ Factory.Math.Pi: instance (ToolShed.Defaultable.Defaultable agm, ToolShed.Defaultable.Defaultable bbp, ToolShed.Defaultable.Defaultable borwein, ToolShed.Defaultable.Defaultable ramanujan, ToolShed.Defaultable.Defaultable spigot) => ToolShed.Defaultable.Defaultable (Factory.Math.Pi.Category agm bbp borwein ramanujan spigot)
+ Factory.Math.Probability: instance (GHC.Float.Floating parameter, GHC.Classes.Ord parameter, GHC.Show.Show parameter) => ToolShed.SelfValidate.SelfValidator (Factory.Math.Probability.ContinuousDistribution parameter)
+ Factory.Math.Probability: instance (GHC.Float.RealFloat parameter, GHC.Show.Show parameter, System.Random.Random parameter) => Factory.Math.Probability.Distribution (Factory.Math.Probability.ContinuousDistribution parameter)
+ Factory.Math.Probability: instance (GHC.Float.RealFloat parameter, GHC.Show.Show parameter, System.Random.Random parameter) => Factory.Math.Probability.Distribution (Factory.Math.Probability.DiscreteDistribution parameter)
+ Factory.Math.Probability: instance (GHC.Num.Num parameter, GHC.Classes.Ord parameter, GHC.Show.Show parameter) => ToolShed.SelfValidate.SelfValidator (Factory.Math.Probability.DiscreteDistribution parameter)
+ Factory.Math.Probability: instance GHC.Classes.Eq parameter => GHC.Classes.Eq (Factory.Math.Probability.ContinuousDistribution parameter)
+ Factory.Math.Probability: instance GHC.Classes.Eq parameter => GHC.Classes.Eq (Factory.Math.Probability.DiscreteDistribution parameter)
+ Factory.Math.Probability: instance GHC.Read.Read parameter => GHC.Read.Read (Factory.Math.Probability.ContinuousDistribution parameter)
+ Factory.Math.Probability: instance GHC.Read.Read parameter => GHC.Read.Read (Factory.Math.Probability.DiscreteDistribution parameter)
+ Factory.Math.Probability: instance GHC.Show.Show parameter => GHC.Show.Show (Factory.Math.Probability.ContinuousDistribution parameter)
+ Factory.Math.Probability: instance GHC.Show.Show parameter => GHC.Show.Show (Factory.Math.Probability.DiscreteDistribution parameter)
Files
- README.markdown +23/−0
- changelog +0/−88
- changelog.markdown +98/−0
- copyright +1/−1
- factory.cabal +90/−45
- makefile +0/−57
- src-exe/Factory/Test/CommandOptions.hs +48/−0
- src-exe/Factory/Test/Performance/Factorial.hs +73/−0
- src-exe/Factory/Test/Performance/Hyperoperation.hs +71/−0
- src-exe/Factory/Test/Performance/Pi.hs +81/−0
- src-exe/Factory/Test/Performance/Primality.hs +54/−0
- src-exe/Factory/Test/Performance/PrimeFactorisation.hs +50/−0
- src-exe/Factory/Test/Performance/Primes.hs +47/−0
- src-exe/Factory/Test/Performance/SquareRoot.hs +59/−0
- src-exe/Factory/Test/Performance/Statistics.hs +45/−0
- src-exe/Main.hs +241/−0
- src-lib/Factory/Data/Exponential.hs +89/−0
- src-lib/Factory/Data/Interval.hs +201/−0
- src-lib/Factory/Data/MonicPolynomial.hs +98/−0
- src-lib/Factory/Data/Monomial.hs +152/−0
- src-lib/Factory/Data/Polynomial.hs +379/−0
- src-lib/Factory/Data/PrimeFactors.hs +143/−0
- src-lib/Factory/Data/PrimeWheel.hs +198/−0
- src-lib/Factory/Data/QuotientRing.hs +79/−0
- src-lib/Factory/Data/Ring.hs +118/−0
- src-lib/Factory/Math/ArithmeticGeometricMean.hs +91/−0
- src-lib/Factory/Math/DivideAndConquer.hs +122/−0
- src-lib/Factory/Math/Factorial.hs +37/−0
- src-lib/Factory/Math/Fibonacci.hs +42/−0
- src-lib/Factory/Math/Hyperoperation.hs +113/−0
- src-lib/Factory/Math/Implementations/Factorial.hs +138/−0
- src-lib/Factory/Math/Implementations/Pi/AGM/Algorithm.hs +42/−0
- src-lib/Factory/Math/Implementations/Pi/AGM/BrentSalamin.hs +64/−0
- src-lib/Factory/Math/Implementations/Pi/BBP/Algorithm.hs +47/−0
- src-lib/Factory/Math/Implementations/Pi/BBP/Base65536.hs +38/−0
- src-lib/Factory/Math/Implementations/Pi/BBP/Bellard.hs +41/−0
- src-lib/Factory/Math/Implementations/Pi/BBP/Implementation.hs +57/−0
- src-lib/Factory/Math/Implementations/Pi/BBP/Series.hs +36/−0
- src-lib/Factory/Math/Implementations/Pi/Borwein/Algorithm.hs +56/−0
- src-lib/Factory/Math/Implementations/Pi/Borwein/Borwein1993.hs +73/−0
- src-lib/Factory/Math/Implementations/Pi/Borwein/Implementation.hs +50/−0
- src-lib/Factory/Math/Implementations/Pi/Borwein/Series.hs +43/−0
- src-lib/Factory/Math/Implementations/Pi/Ramanujan/Algorithm.hs +55/−0
- src-lib/Factory/Math/Implementations/Pi/Ramanujan/Chudnovsky.hs +63/−0
- src-lib/Factory/Math/Implementations/Pi/Ramanujan/Classic.hs +60/−0
- src-lib/Factory/Math/Implementations/Pi/Ramanujan/Implementation.hs +52/−0
- src-lib/Factory/Math/Implementations/Pi/Ramanujan/Series.hs +37/−0
- src-lib/Factory/Math/Implementations/Pi/Spigot/Algorithm.hs +50/−0
- src-lib/Factory/Math/Implementations/Pi/Spigot/Gosper.hs +39/−0
- src-lib/Factory/Math/Implementations/Pi/Spigot/RabinowitzWagon.hs +40/−0
- src-lib/Factory/Math/Implementations/Pi/Spigot/Series.hs +53/−0
- src-lib/Factory/Math/Implementations/Pi/Spigot/Spigot.hs +153/−0
- src-lib/Factory/Math/Implementations/Primality.hs +217/−0
- src-lib/Factory/Math/Implementations/PrimeFactorisation.hs +145/−0
- src-lib/Factory/Math/Implementations/Primes/Algorithm.hs +63/−0
- src-lib/Factory/Math/Implementations/Primes/SieveOfAtkin.hs +242/−0
- src-lib/Factory/Math/Implementations/Primes/SieveOfEratosthenes.hs +162/−0
- src-lib/Factory/Math/Implementations/Primes/TrialDivision.hs +59/−0
- src-lib/Factory/Math/Implementations/Primes/TurnersSieve.hs +48/−0
- src-lib/Factory/Math/Implementations/SquareRoot.hs +192/−0
- src-lib/Factory/Math/MultiplicativeOrder.hs +66/−0
- src-lib/Factory/Math/PerfectPower.hs +100/−0
- src-lib/Factory/Math/Pi.hs +100/−0
- src-lib/Factory/Math/Power.hs +84/−0
- src-lib/Factory/Math/Precision.hs +125/−0
- src-lib/Factory/Math/Primality.hs +102/−0
- src-lib/Factory/Math/PrimeFactorisation.hs +151/−0
- src-lib/Factory/Math/Primes.hs +64/−0
- src-lib/Factory/Math/Probability.hs +255/−0
- src-lib/Factory/Math/Radix.hs +139/−0
- src-lib/Factory/Math/SquareRoot.hs +120/−0
- src-lib/Factory/Math/Statistics.hs +181/−0
- src-lib/Factory/Math/Summation.hs +91/−0
- src-test/Factory/Test/QuickCheck/ArithmeticGeometricMean.hs +57/−0
- src-test/Factory/Test/QuickCheck/Factorial.hs +75/−0
- src-test/Factory/Test/QuickCheck/Hyperoperation.hs +79/−0
- src-test/Factory/Test/QuickCheck/Interval.hs +43/−0
- src-test/Factory/Test/QuickCheck/MonicPolynomial.hs +77/−0
- src-test/Factory/Test/QuickCheck/PerfectPower.hs +55/−0
- src-test/Factory/Test/QuickCheck/Pi.hs +117/−0
- src-test/Factory/Test/QuickCheck/Polynomial.hs +122/−0
- src-test/Factory/Test/QuickCheck/Power.hs +47/−0
- src-test/Factory/Test/QuickCheck/Primality.hs +72/−0
- src-test/Factory/Test/QuickCheck/PrimeFactorisation.hs +100/−0
- src-test/Factory/Test/QuickCheck/Primes.hs +101/−0
- src-test/Factory/Test/QuickCheck/Probability.hs +161/−0
- src-test/Factory/Test/QuickCheck/Radix.hs +46/−0
- src-test/Factory/Test/QuickCheck/SquareRoot.hs +85/−0
- src-test/Factory/Test/QuickCheck/Statistics.hs +125/−0
- src-test/Factory/Test/QuickCheck/Summation.hs +42/−0
- src-test/Main.hs +76/−0
- src/Factory/Data/Exponential.hs +0/−89
- src/Factory/Data/Interval.hs +0/−201
- src/Factory/Data/MonicPolynomial.hs +0/−98
- src/Factory/Data/Monomial.hs +0/−148
- src/Factory/Data/Polynomial.hs +0/−375
- src/Factory/Data/PrimeFactors.hs +0/−143
- src/Factory/Data/PrimeWheel.hs +0/−198
- src/Factory/Data/QuotientRing.hs +0/−79
- src/Factory/Data/Ring.hs +0/−118
- src/Factory/Math/ArithmeticGeometricMean.hs +0/−91
- src/Factory/Math/DivideAndConquer.hs +0/−122
- src/Factory/Math/Factorial.hs +0/−37
- src/Factory/Math/Fibonacci.hs +0/−42
- src/Factory/Math/Hyperoperation.hs +0/−113
- src/Factory/Math/Implementations/Factorial.hs +0/−138
- src/Factory/Math/Implementations/Pi/AGM/Algorithm.hs +0/−42
- src/Factory/Math/Implementations/Pi/AGM/BrentSalamin.hs +0/−64
- src/Factory/Math/Implementations/Pi/BBP/Algorithm.hs +0/−47
- src/Factory/Math/Implementations/Pi/BBP/Base65536.hs +0/−38
- src/Factory/Math/Implementations/Pi/BBP/Bellard.hs +0/−41
- src/Factory/Math/Implementations/Pi/BBP/Implementation.hs +0/−57
- src/Factory/Math/Implementations/Pi/BBP/Series.hs +0/−36
- src/Factory/Math/Implementations/Pi/Borwein/Algorithm.hs +0/−56
- src/Factory/Math/Implementations/Pi/Borwein/Borwein1993.hs +0/−73
- src/Factory/Math/Implementations/Pi/Borwein/Implementation.hs +0/−50
- src/Factory/Math/Implementations/Pi/Borwein/Series.hs +0/−43
- src/Factory/Math/Implementations/Pi/Ramanujan/Algorithm.hs +0/−55
- src/Factory/Math/Implementations/Pi/Ramanujan/Chudnovsky.hs +0/−63
- src/Factory/Math/Implementations/Pi/Ramanujan/Classic.hs +0/−60
- src/Factory/Math/Implementations/Pi/Ramanujan/Implementation.hs +0/−52
- src/Factory/Math/Implementations/Pi/Ramanujan/Series.hs +0/−37
- src/Factory/Math/Implementations/Pi/Spigot/Algorithm.hs +0/−50
- src/Factory/Math/Implementations/Pi/Spigot/Gosper.hs +0/−39
- src/Factory/Math/Implementations/Pi/Spigot/RabinowitzWagon.hs +0/−40
- src/Factory/Math/Implementations/Pi/Spigot/Series.hs +0/−53
- src/Factory/Math/Implementations/Pi/Spigot/Spigot.hs +0/−153
- src/Factory/Math/Implementations/Primality.hs +0/−217
- src/Factory/Math/Implementations/PrimeFactorisation.hs +0/−145
- src/Factory/Math/Implementations/Primes/Algorithm.hs +0/−63
- src/Factory/Math/Implementations/Primes/SieveOfAtkin.hs +0/−242
- src/Factory/Math/Implementations/Primes/SieveOfEratosthenes.hs +0/−162
- src/Factory/Math/Implementations/Primes/TrialDivision.hs +0/−59
- src/Factory/Math/Implementations/Primes/TurnersSieve.hs +0/−48
- src/Factory/Math/Implementations/SquareRoot.hs +0/−192
- src/Factory/Math/MultiplicativeOrder.hs +0/−66
- src/Factory/Math/PerfectPower.hs +0/−100
- src/Factory/Math/Pi.hs +0/−100
- src/Factory/Math/Power.hs +0/−84
- src/Factory/Math/Precision.hs +0/−125
- src/Factory/Math/Primality.hs +0/−102
- src/Factory/Math/PrimeFactorisation.hs +0/−151
- src/Factory/Math/Primes.hs +0/−64
- src/Factory/Math/Probability.hs +0/−255
- src/Factory/Math/Radix.hs +0/−139
- src/Factory/Math/SquareRoot.hs +0/−120
- src/Factory/Math/Statistics.hs +0/−181
- src/Factory/Math/Summation.hs +0/−91
- src/Factory/Test/CommandOptions.hs +0/−48
- src/Factory/Test/Performance/Factorial.hs +0/−73
- src/Factory/Test/Performance/Hyperoperation.hs +0/−71
- src/Factory/Test/Performance/Pi.hs +0/−81
- src/Factory/Test/Performance/Primality.hs +0/−54
- src/Factory/Test/Performance/PrimeFactorisation.hs +0/−50
- src/Factory/Test/Performance/Primes.hs +0/−47
- src/Factory/Test/Performance/SquareRoot.hs +0/−59
- src/Factory/Test/Performance/Statistics.hs +0/−45
- src/Factory/Test/QuickCheck/ArithmeticGeometricMean.hs +0/−57
- src/Factory/Test/QuickCheck/Factorial.hs +0/−68
- src/Factory/Test/QuickCheck/Hyperoperation.hs +0/−75
- src/Factory/Test/QuickCheck/Interval.hs +0/−43
- src/Factory/Test/QuickCheck/MonicPolynomial.hs +0/−72
- src/Factory/Test/QuickCheck/PerfectPower.hs +0/−54
- src/Factory/Test/QuickCheck/Pi.hs +0/−114
- src/Factory/Test/QuickCheck/Polynomial.hs +0/−116
- src/Factory/Test/QuickCheck/Power.hs +0/−45
- src/Factory/Test/QuickCheck/Primality.hs +0/−72
- src/Factory/Test/QuickCheck/PrimeFactorisation.hs +0/−94
- src/Factory/Test/QuickCheck/Primes.hs +0/−99
- src/Factory/Test/QuickCheck/Probability.hs +0/−160
- src/Factory/Test/QuickCheck/QuickChecks.hs +0/−70
- src/Factory/Test/QuickCheck/Radix.hs +0/−46
- src/Factory/Test/QuickCheck/SquareRoot.hs +0/−86
- src/Factory/Test/QuickCheck/Statistics.hs +0/−112
- src/Factory/Test/QuickCheck/Summation.hs +0/−42
- src/Main.hs +0/−241
+ README.markdown view
@@ -0,0 +1,23 @@+# **Factory**.++This is **Factory**, a library of number-theory functions.++## Installation++It can be built and installed using [Cabal](https://www.haskell.org/cabal/users-guide/installing-packages.html).++## Documentation++More information about this library can be found at [Factory](http://functionalley.eu/Factory/factory.html).++## License++For information on copying and distributing this package, see the file **LICENSE** in this directory.++## Bug-reporting++Bug-reports should be emailed to <factory *at* functionalley *dot* eu>.++## Author++This library is written and maintained by Dr. Alistair Ward.
− changelog
@@ -1,88 +0,0 @@-2011-03-01 Dr. Alistair Ward <factory at functionalley dot eu>--0.0.0.1- * First version of the package.-0.0.0.2- * Created the modules; "Factory.Test.QuickCheck.Bounds", "Factory.Math.Implementations.Pi.Borwein.*" and "Factory.Test.Performance.Statistics".- * Created a new module "Factory.Data.PrimeFactors", and migrated definitions from both "Factory.Math.PrimeFactorisation" and "Factory.Math.Implementations.PrimeFactorisation".- * Created the class 'Factory.Math.Factorial.Factorial' and a new module "Factory.Math.Implementations.Factorial".- Moved existing implementation (Bisection) into the new module, with a new implementation (PrimeFactorisation).- * Added the function 'Factory.Math.Summation.sumR'.- * Added a parameter to the functions 'Factory.Math.DivideAndConquer.divideAndConquer' and 'Factory.Data.Bounds.divideAndConquer', to permit asymmetric bisection.- * Added methods to class "Factory.Math.Pi.Algorithm" to permit the retrieval of /Pi/ as a 'Rational' or a 'String'.- * Renamed the function 'Factory.Math.Precision.capPrecision' to 'Factory.Math.Precision.simplify'.- * Removed the module "Factory.Test.Performance.Exponential".- * Removed the function 'Factory.Math.Power.raise', which was no more efficient than ghc's implementation of '(^)'.- * Uploaded to <http://hackage.haskell.org/packages/hackage.html>.-0.1.0.0- * Amended 'factory.cabal' to more correctly specify the dependency on package 'toolshed'.- * Added the module "Factory.Math.Probability".- * Renamed the module "Factory.Data.Bounds" to "Factory.Data.Interval",- and added the functions; 'Factory.Data.Interval.precisely', 'Factory.Data.Interval.shift', 'Factory.Data.Interval.closedUnitInterval'.- * Guarded 'eager-blackholing' flag in /cabal/ file.-0.1.0.1- * Renamed classes "Factory.Math.[Primality, Pi, Factorial, SquareRoot, PrimeFactorisation].Algorithm" to "Factory.Math.[Primality, Pi, Factorial, SquareRoot, PrimeFactorisation].Algorithmic", to distinguish them from the data-types which implement them.- * Added the modules "Factory.Math.Hyperoperation", "Factory.Test.QuickCheck.Hyperoperation" and "Factory.Test.Performance.Hyperoperation".- * Added the modules "Factory.Math.Primes", "Factory.Math.Implementation.Primes", "Factory.Test.Performance.Primes", "Factory.Test.QuickCheck.Primes" and "Factory.Data.PrimeWheel".- * Added the function 'Factory.Math.PrimeFactorisation.squareFree'.- * Added rewrite-rules to specialise 'Factory.Math.Power.isPerfectPower' for type-parameter='Int'.- * Recoded "Factory.Math.Radix" to the interface "Data.Array.IArray.IArray", rather than the data-type "Data.Array.Array".-0.1.0.2- * Added 'Factory.Math.Primes.primorial'.- * Altered 'Factory.Math.Implementations.Primes.trialDivision' to take an integer defining the size of a 'Factory.Data.PrimeWheel', from which candidates are extracted.- * Removed the command-line option 'primesPerformanceGraph', which appears to memoise data from previous tests.- * Uploaded to <http://hackage.haskell.org/packages/hackage.html>.-0.1.0.3- * Qualified 'Factory.Math.Implementations.Primes.trialDivision' with /NOINLINE/ pragma, to block optimization which conflicts with rewrite-rule for 'Factory.Math.Implementations.Primes.sieveOfEratosthenes' !- * Re-coded 'Factory.Data.PrimeWheel.coprimes' and 'Factory.Math.Implementations.Primes.sieveOfEratosthenes', to use a map of lists, rather than a map of lists of lists.-0.2.0.0- * Separately coded the special-case of a 'Factory.Data.PrimeWheel' of size zero, in 'Factory.Math.Implementations.Primes.trialDivision', to achieve better space-complexity.- * Added 'Factory.Data.PrimeWheel.estimateOptimalSize'.- * Split "Factory.Math.Implementations.Primes" into; "Factory.Math.Implementations.Primes.SieveOfEratosthenes", "Factory.Math.Implementations.Primes.TurnersSieve", "Factory.Math.Implementations.Primes.TrialDivision", and added a new module "Factory.Math.Implementations.Primes.SieveOfAtkin". This makes the rewrite-rules less fragile.- * Coded 'Factory.Math.Radix.digitalRoot' more concisely.- * Split "Factory.Math.Power" into an additional module "Factory.Math.PerfectPower".- * Replaced '(+ 1)' and '(- 1)' with the faster calls 'succ' and 'pred'.- * Used 'Paths_factory.version' in 'Main', rather than hard-coding it.-0.2.0.1- * Changed by Lennart Augustsson, to replace "System" with "System.Environment" and "System.Exit", and to remove dependency on "haskell98".-0.2.0.2- * Reacted to new module-hierarchy and addition of method 'ToolShed.SelfValidate.getErrors', in 'toolshed-0.13.0.0'.- * Made 'Factory.Data.Interval.getLength' private.- * Added 'Factory.Data.Interval.mkBounded'.- * Generalised "Factory.Math.Statistics" to accept any 'Data.Foldable.Foldable' 'Functor', rather than merely lists.-0.2.0.3- * Added class 'Show' to some contexts in "Factory.Math.Radix", for migration to 'ghc-7.4'.-0.2.0.4- * Added classes 'Eq' and 'Show' to many contexts, for migration to 'ghc-7.4'.- * Minor re-formatting.-0.2.0.5- * Minor clarification of 'Factory.Math.Implementations.Primality.witnessesCompositeness'.- * Added details to any failure to parse the command-line arguments.- * Defined package's name using program's name, in "Main.hs".- * Added 'Factory.Math.Primes.mersenneNumbers'.- * Replaced use of 'mod' on positive integers, with the faster 'rem', in 'Factory.Math.Implementations.Pi.Spigot.Spigot.processColumns', 'Factory.Math.Implementations.Primality.witnessesCompositeness', 'Factory.Math.Implementations.Primes.TrialDivision.isIndivisibleBy', 'Factory.Math.Implementations.Primes.SieveOfAtkin.polynomialTypeLookup', 'Factory.Math.Implementations.Primes.SieveOfAtkin.findPolynomialSolutions', 'Factory.Math.Implementations.Primes.TurnersSieve.turnersSieve', 'Factory.Math.PerfectPower.maybeSquareNumber'.- * Replaced calls to 'realToFrac' with 'toRational' in; "Factory.Math.Implementations.SquareRoot", 'Factory.Math.Statistics.getDispersionFromMean', 'Factory.Math.SquareRoot.getDiscrepancy', 'Factory.Math.SquareRoot.getAccuracy', to more clearly represent the required operation.-0.2.1.0- * Refactored 'Factory.Test.QuickCheck.QuickChecks'.- * Remove redundant import of 'Data.Ratio' from many modules.- * Refactored 'Factory.Math.Radix.encodes' to make use of 'Data.List.genericLength', & removed empty 'where'.- * Explicitly closed standard-input in the executable.- * Replaced calls to 'error' from inside the IO-monad, with 'Control.Monad.fail'.- * Added function 'Factory.Math.Precision.roundTo'.- * Trapped command-line arguments to which garbage has been appended.- * Corrected the output of 'Main.main.optDescrList.printVersion'.- * Removed the integral population-size parameter from 'Factory.Math.Probability.generateContinuousPopulation' & 'Factory.Math.Probability.generateDiscretePopulation', making the result conceptually infinite.- * Created class 'Factory.Math.Probability.Distribution', to which data-types 'Factory.Math.Probability.ContinuousDistribution' & 'Factory.Math.Probability.DiscreteDistribution' conform.- * Added data-constructors 'Factory.Math.Probability.ExponentialDistribution', 'Factory.Math.Probability.ShiftedGeometricDistribution' & 'Factory.Math.Probability.LogNormal'.- * Added command-line option '--plotDiscreteDistribution' to "Main".- * Removed Preprocessor-check on the version of package 'toolshed', in "Factory/Math/Summation" & "Factory/Data/PrimeFactors".-0.2.1.1- * Added 'Factory.Test.QuickCheck.Probability.prop_logNormalDistributionEqual'.- * Removed /INLINE/ pragma from 'Factory.Math.Implementations.Primes.TrialDivision.isIndivisibleBy', since to be effective it must be called with fully applied parameters (which it isn't).- * Un eta-reduced 'Factory.Math.Power.square', since we want it to be inlined when called with one argument.- * Tested with 'haskell-platform-2013.2.0.0'.- * Replaced preprocessor-directives with 'build-depends' constraints in 'factory.cabal'.- * Added function 'Factory.Math.Statistics.getWeightedMean' & corresponding tests in module "Factory.Test.QuickCheck.Statistics".- * Since '(<$>)' is exported from the Prelude from 'base-4.8', imported "Prelude" hiding '(<*>)' into module "Factory.Data.Monomial", since this symbol is defined locally for other purposes.- * 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"..-
+ changelog.markdown view
@@ -0,0 +1,98 @@+# 2011-03-01 Dr. Alistair Ward <factory at functionalley dot eu>++## 0.0.0.1+ * First version of the package.+## 0.0.0.2+ * Created the modules; **Factory.Test.QuickCheck.Bounds**, **Factory.Math.Implementations.Pi.Borwein** & **Factory.Test.Performance.Statistics** .+ * Created a new module **Factory.Data.PrimeFactors**, and migrated definitions from both **Factory.Math.PrimeFactorisation** & **Factory.Math.Implementations.PrimeFactorisation**.+ * Created the class `Factory.Math.Factorial.Factorial` and a new module **Factory.Math.Implementations.Factorial**.+ Moved existing implementation (`Bisection`) into the new module, with a new implementation (`PrimeFactorisation`).+ * Added the function `Factory.Math.Summation.sumR`.+ * Added a parameter to the functions `Factory.Math.DivideAndConquer.divideAndConquer` and `Factory.Data.Bounds.divideAndConquer`, to permit asymmetric bisection.+ * Added methods to class `Factory.Math.Pi.Algorithm` to permit the retrieval of *Pi* as a `Rational` or a `String`.+ * Renamed the function `Factory.Math.Precision.capPrecision` to `Factory.Math.Precision.simplify`.+ * Removed the module **Factory.Test.Performance.Exponential**.+ * Removed the function `Factory.Math.Power.raise`, which was no more efficient than ghc's implementation of `(^)`.+ * Uploaded to [Hackage](http://hackage.haskell.org/packages/hackage.html).+## 0.1.0.0+ * Amended the *.cabal*-file to more correctly specify the dependency on package **toolshed**.+ * Added the module **Factory.Math.Probability**.+ * Renamed the module **Factory.Data.Bounds** to **Factory.Data.Interval**,+ and added the functions; `Factory.Data.Interval.precisely`, `Factory.Data.Interval.shift`, `Factory.Data.Interval.closedUnitInterval`.+ * Guarded **eager-blackholing** flag in the *cabal*-file.+## 0.1.0.1+ * Renamed classes *Factory.Math.[Primality, Pi, Factorial, SquareRoot, PrimeFactorisation].Algorithm* to *Factory.Math.[Primality, Pi, Factorial, SquareRoot, PrimeFactorisation].Algorithmic*, to distinguish them from the data-types which implement them.+ * Added the modules **Factory.Math.Hyperoperation**, **Factory.Test.QuickCheck.Hyperoperation** and **Factory.Test.Performance.Hyperoperation**.+ * Added the modules **Factory.Math.Primes**, **Factory.Math.Implementation.Primes**, **Factory.Test.Performance.Primes**, **Factory.Test.QuickCheck.Primes** and **Factory.Data.PrimeWheel**.+ * Added the function `Factory.Math.PrimeFactorisation.squareFree`.+ * Added rewrite-rules to specialise `Factory.Math.Power.isPerfectPower` for type-parameter=`Int`.+ * Recoded **Factory.Math.Radix** to the interface `Data.Array.IArray.IArray`, rather than the data-type `Data.Array.Array`.+## 0.1.0.2+ * Added `Factory.Math.Primes.primorial`.+ * Altered `Factory.Math.Implementations.Primes.trialDivision` to take an integer defining the size of a `Factory.Data.PrimeWheel`, from which candidates are extracted.+ * Removed the command-line option `primesPerformanceGraph`, which appears to memoise data from previous tests.+ * Uploaded to [Hackage](http://hackage.haskell.org/packages/hackage.html).+## 0.1.0.3+ * Qualified `Factory.Math.Implementations.Primes.trialDivision` with **NOINLINE**-pragma, to block optimization which conflicts with rewrite-rule for `Factory.Math.Implementations.Primes.sieveOfEratosthenes` !+ * Re-coded `Factory.Data.PrimeWheel.coprimes` and `Factory.Math.Implementations.Primes.sieveOfEratosthenes`, to use a map of lists, rather than a map of lists of lists.+## 0.2.0.0+ * Separately coded the special-case of a **Factory.Data.PrimeWheel** of size zero, in `Factory.Math.Implementations.Primes.trialDivision`, to achieve better space-complexity.+ * Added `Factory.Data.PrimeWheel.estimateOptimalSize`.+ * Split **Factory.Math.Implementations.Primes** into; **Factory.Math.Implementations.Primes.SieveOfEratosthenes**, **Factory.Math.Implementations.Primes.TurnersSieve**, **Factory.Math.Implementations.Primes.TrialDivision**, and added a new module **Factory.Math.Implementations.Primes.SieveOfAtkin**. This makes the rewrite-rules less fragile.+ * Coded `Factory.Math.Radix.digitalRoot` more concisely.+ * Split **Factory.Math.Power** into an additional module **Factory.Math.PerfectPower**.+ * Replaced `(+ 1)` and `(- 1)` with the faster calls `succ` and `pred`.+ * Used `Paths_factory.version` in **Main**, rather than hard-coding it.+## 0.2.0.1+ * Changed by Lennart Augustsson, to replace `System` with `System.Environment` and `System.Exit`, and to remove dependency on **haskell98**.+## 0.2.0.2+ * Reacted to new module-hierarchy and addition of method `ToolShed.SelfValidate.getErrors`, in **toolshed-0.13.0.0**.+ * Made `Factory.Data.Interval.getLength` private.+ * Added `Factory.Data.Interval.mkBounded`.+ * Generalised **Factory.Math.Statistics** to accept any `Data.Foldable.Foldable` *Functor*, rather than merely lists.+## 0.2.0.3+ * Added class `Show` to some contexts in **Factory.Math.Radix**, for migration to **ghc-7.4**.+## 0.2.0.4+ * Added classes `Eq` and `Show` to many contexts, for migration to **ghc-7.4**.+ * Minor re-formatting.+## 0.2.0.5+ * Minor clarification of `Factory.Math.Implementations.Primality.witnessesCompositeness`.+ * Added details to any failure to parse the command-line arguments.+ * Defined package's name using program's name, in **Main.hs**.+ * Added `Factory.Math.Primes.mersenneNumbers`.+ * Replaced use of `mod` on positive integers, with the faster `rem`, in `Factory.Math.Implementations.Pi.Spigot.Spigot.processColumns`, `Factory.Math.Implementations.Primality.witnessesCompositeness`, `Factory.Math.Implementations.Primes.TrialDivision.isIndivisibleBy`, `Factory.Math.Implementations.Primes.SieveOfAtkin.polynomialTypeLookup`, `Factory.Math.Implementations.Primes.SieveOfAtkin.findPolynomialSolutions`, `Factory.Math.Implementations.Primes.TurnersSieve.turnersSieve`, `Factory.Math.PerfectPower.maybeSquareNumber`.+ * Replaced calls to `realToFrac` with `toRational` in; **Factory.Math.Implementations.SquareRoot**, `Factory.Math.Statistics.getDispersionFromMean`, `Factory.Math.SquareRoot.getDiscrepancy`, `Factory.Math.SquareRoot.getAccuracy`, to more clearly represent the required operation.+## 0.2.1.0+ * Refactored **Factory.Test.QuickCheck.QuickChecks**.+ * Remove redundant import of `Data.Ratio` from many modules.+ * Refactored `Factory.Math.Radix.encodes` to make use of `Data.List.genericLength`, & removed empty `where`.+ * Explicitly closed standard-input in the executable.+ * Replaced calls to `error` from inside the IO-monad, with `Control.Monad.fail`.+ * Added function `Factory.Math.Precision.roundTo`.+ * Trapped command-line arguments to which garbage has been appended.+ * Corrected the output of `Main.main.optDescrList.printVersion`.+ * Removed the integral population-size parameter from `Factory.Math.Probability.generateContinuousPopulation` & `Factory.Math.Probability.generateDiscretePopulation`, making the result conceptually infinite.+ * Created class `Factory.Math.Probability.Distribution`, to which data-types `Factory.Math.Probability.ContinuousDistribution` & `Factory.Math.Probability.DiscreteDistribution` conform.+ * Added data-constructors `Factory.Math.Probability.ExponentialDistribution`, `Factory.Math.Probability.ShiftedGeometricDistribution` & `Factory.Math.Probability.LogNormal`.+ * Added command-line option **--plotDiscreteDistribution** to **Main**.+ * Removed Preprocessor-check on the version of package **toolshed**, in **Factory/Math/Summation** & **Factory/Data/PrimeFactors**.+## 0.2.1.1+ * Added `Factory.Test.QuickCheck.Probability.prop_logNormalDistributionEqual`.+ * Removed /INLINE/ pragma from `Factory.Math.Implementations.Primes.TrialDivision.isIndivisibleBy`, since to be effective it must be called with fully applied parameters (which it isn't).+ * Un eta-reduced `Factory.Math.Power.square`, since we want it to be inlined when called with one argument.+ * Tested with **haskell-platform-2013.2.0.0**.+ * Replaced preprocessor-directives with **build-depends** constraints in the *.cabal*-file.+ * Added function `Factory.Math.Statistics.getWeightedMean` & corresponding tests in module **Factory.Test.QuickCheck.Statistics**.+ * Since `(<$>)` is exported from the Prelude from **base-4.8**, imported **Prelude** hiding `(<*>)` into module **Factory.Data.Monomial**, since this symbol is defined locally for other purposes.+ * 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**..+## 0.2.1.2+ * Hid `(<$>)` when importing the **Prelude** into module **src/Factory/Test/QuickCheck/Pi**.+ * Added the compiler to the output returned for the command-line option **version**.+ * Changed flag **threaded** in the *.cabal*-file to **manual**.+ * Added **Default-language**-specification to the *.cabal*-file.+ * Added file **README.markdown**.+ * Converted this file to markdown-format.+ * Replaced `System.Exit.exitWith (System.Exit.ExitFailure 1)` with `System.Exit.exitFailure` & `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**.
copyright view
@@ -5,7 +5,7 @@ Copyright (C) 2011-2013 Dr. Alistair Ward. All Rights Reserved. Home-page:- http://functionalley.eu+ http://functionalley.eu/Factory/factory.html License: GNU GENERAL PUBLIC LICENSE Version 3; see '/usr/share/common-licenses/GPL-3' or '/usr/share/doc/licenses/gpl-3.0.txt' where available, or the local packaged file 'LICENSE'.
factory.cabal view
@@ -1,34 +1,56 @@--- Package-properties-Name: factory-Version: 0.2.1.1-Cabal-Version: >= 1.6-Copyright: (C) 2011-2013 Dr. Alistair Ward-License: GPL-License-file: LICENSE-Author: Dr. Alistair Ward-Stability: Unstable interface, incomplete features.-Synopsis: Rational arithmetic in an irrational world.-Build-Type: Simple-Description: A library of number-theory functions, for; factorials, square-roots, Pi and primes.-Category: Math, Number Theory-Tested-With: GHC == 7.4, GHC == 7.6, GHC == 7.10-Homepage: http://functionalley.eu-Maintainer: factory <at> functionalley <dot> eu-Bug-reports: factory <at> functionalley <dot> eu-Extra-Source-Files: changelog, copyright, makefile+-- This file is part of Factory.+--+-- Factory 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.+--+-- Factory 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 Factory. If not, see <http://www.gnu.org/licenses/>. --- Turn on using: 'runhaskell ./Setup.hs configure -f llvm'.+Name: factory+Version: 0.2.1.2+Cabal-version: >= 1.10+Copyright: (C) 2011-2015 Dr. Alistair Ward+License: GPL+License-file: LICENSE+Author: Dr. Alistair Ward+Stability: stable+Synopsis: Rational arithmetic in an irrational world.+Build-type: Simple+Description: A library of number-theory functions, for; factorials, square-roots, Pi and primes.+Category: Math, Number Theory+Tested-with: GHC == 7.4, GHC == 7.6, GHC == 7.8, GHC == 7.10+Homepage: http://functionalley.eu/Factory/factory.html+Maintainer: mailto <colon> factory <at> functionalley <dot> eu+Bug-reports: mailto <colon> factory <at> functionalley <dot> eu++-- None of these files are needed at run-time.+Extra-source-files:+ changelog.markdown+ copyright+ 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 flag threaded Description: Enable parallelized code.- default: True+ Manual: True+ Default: True Library- hs-source-dirs: src+ Default-language: Haskell2010+ GHC-options: -Wall -O2 -fno-warn-tabs+ Hs-source-dirs: src-lib Exposed-modules: Factory.Data.Exponential@@ -97,9 +119,7 @@ parallel >= 3.0, primes >= 0.1, random,- toolshed >= 0.13-- GHC-options: -Wall -O2 -fno-warn-tabs+ toolshed >= 0.16 if impl(ghc >= 7.4.1) GHC-prof-options: -prof -fprof-auto -fprof-cafs@@ -110,10 +130,13 @@ GHC-options: -fllvm Executable factory- hs-source-dirs: src-- Main-Is: Main.hs+ Default-language: Haskell2010+ GHC-options: -Wall -O2 -fno-warn-tabs+ Hs-source-dirs: src-exe+ Main-is: Main.hs+ GHC-prof-options: -prof -auto-all -caf-all +-- Unexposed modules must be referenced for 'cabal sdist'. Other-modules: Factory.Test.CommandOptions Factory.Test.Performance.Factorial@@ -124,6 +147,35 @@ Factory.Test.Performance.Primes Factory.Test.Performance.SquareRoot Factory.Test.Performance.Statistics++ Build-depends:+ array,+ base >= 4.3 && < 5,+ Cabal >= 1.10,+ containers,+ deepseq >= 1.1,+ factory,+ random,+ toolshed >= 0.16++ if flag(threaded)+ GHC-options: -threaded++ if impl(ghc >= 7.0)+ GHC-options: -rtsopts++ 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: Factory.Test.QuickCheck.ArithmeticGeometricMean Factory.Test.QuickCheck.Factorial Factory.Test.QuickCheck.Hyperoperation@@ -137,25 +189,18 @@ Factory.Test.QuickCheck.PrimeFactorisation Factory.Test.QuickCheck.Primes Factory.Test.QuickCheck.Probability- Factory.Test.QuickCheck.QuickChecks Factory.Test.QuickCheck.Radix Factory.Test.QuickCheck.SquareRoot Factory.Test.QuickCheck.Statistics Factory.Test.QuickCheck.Summation Build-depends:- Cabal >= 1.6 && < 2,- QuickCheck >= 2.2-- GHC-options: -Wall -O2 -fno-warn-tabs- GHC-prof-options: -prof -auto-all -caf-all-- if flag(threaded)- GHC-options: -threaded-- if impl(ghc >= 7.0)- GHC-options: -rtsopts-- if flag(llvm)- GHC-options: -fllvm-+ array,+ base >= 4.3 && < 5,+ containers,+ deepseq >= 1.1,+ factory,+ primes >= 0.1,+ QuickCheck >= 2.2,+ random,+ toolshed >= 0.16
− makefile
@@ -1,57 +0,0 @@-# Copyright (C) 2011-2104 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/>.--.PHONY: all build check clean configure copy haddock help hlint install prof sdist--all: install--install: build haddock- @[ -z "$$CABAL_INSTALL_OPTIONS" ] || echo "INFO: CABAL_INSTALL_OPTIONS='$$CABAL_INSTALL_OPTIONS'"- runhaskell Setup $@ $$CABAL_INSTALL_OPTIONS--prof:- CABAL_CONFIGURE_OPTIONS="--enable-library-profiling --enable-executable-profiling $$CABAL_CONFIGURE_OPTIONS" make install--copy: build- @[ -z "$$CABAL_COPY_OPTIONS" ] || echo "INFO: CABAL_COPY_OPTIONS='$$CABAL_COPY_OPTIONS'"- runhaskell Setup $@ $$CABAL_COPY_OPTIONS--build: configure- @[ -z "$$CABAL_BUILD_OPTIONS" ] || echo "INFO: CABAL_BUILD_OPTIONS='$$CABAL_BUILD_OPTIONS'"- runhaskell Setup $@ $$CABAL_BUILD_OPTIONS--configure: factory.cabal Setup.hs- @[ -z "$$CABAL_CONFIGURE_OPTIONS" ] || echo "INFO: CABAL_CONFIGURE_OPTIONS='$$CABAL_CONFIGURE_OPTIONS'"- runhaskell Setup $@ $$CABAL_CONFIGURE_OPTIONS #--user--haddock: configure- PATH=~/.cabal/bin:$$PATH runhaskell Setup $@ --hyperlink-source #Amend path to find 'HsColour', as required for 'hyperlink-source'.--hlint:- @$@ -i 'Use &&' -i 'Reduce duplication' -i 'Redundant bracket' src/ +RTS -N--sdist:- TAR_OPTIONS='--format=ustar' runhaskell Setup $@--check: sdist- cabal upload --check --verbose=3 dist/*.tar.gz;--clean:- runhaskell Setup $@- find src -type f \( -name '*.hc' -o -name '*.hcr' -o -name '*.hi' -o -name '*.o' \) -delete--help:- @grep '^[a-zA-Z].*:' makefile | sed -e 's/:.*//'-
+ src-exe/Factory/Test/CommandOptions.hs view
@@ -0,0 +1,48 @@+{-+ Copyright (C) 2011 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 the available set of command-line options; of which there's currently only one.+-}++module Factory.Test.CommandOptions(+-- * Types+-- ** Data-types+ CommandOptions(..),+-- * Functions+-- ** Mutators+ setVerbose+) where++import qualified ToolShed.Defaultable++-- | Declare a record used to contain command-line options.+data CommandOptions = MkCommandOptions {+ verbose :: Bool -- ^ Whether additional informative output should be generated, where applicable.+}++instance ToolShed.Defaultable.Defaultable CommandOptions where+ defaultValue = MkCommandOptions { verbose = False }++-- | Mutator.+setVerbose :: CommandOptions -> CommandOptions+setVerbose commandOptions = commandOptions {+ verbose = True+}++
+ src-exe/Factory/Test/Performance/Factorial.hs view
@@ -0,0 +1,73 @@+{-+ Copyright (C) 2011 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@] Times the methods exported from module "Math.Factorial".+-}++module Factory.Test.Performance.Factorial(+-- * Functions+ factorialPerformance,+ factorialPerformanceControl,+ factorialPerformanceGraph,+ factorialPerformanceGraphControl+) where++import qualified Control.DeepSeq+import qualified Data.List+import qualified Factory.Math.Factorial as Math.Factorial+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required by 'Math.Factorial.factorial'.+factorialPerformance :: (+ Control.DeepSeq.NFData i,+ Integral i,+ Math.Factorial.Algorithmic algorithm,+ Show i+ ) => algorithm -> i -> IO (Double, i)+factorialPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . Math.Factorial.factorial algorithm++-- | Measures the CPU-time required by a naive implementation.+factorialPerformanceControl :: (Control.DeepSeq.NFData i, Integral i) => i -> IO (Double, i)+-- factorialPerformanceControl i = ToolShed.System.TimePure.getCPUSeconds $ product [1 .. i] -- CAVEAT: too lazy.+factorialPerformanceControl i = ToolShed.System.TimePure.getCPUSeconds $ Data.List.foldl' (*) 1 [2 .. i]++{- |+ * Measure the CPU-time required by 'Math.Factorial.factorial', against an exponentially increasing operand.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+factorialPerformanceGraph :: Math.Factorial.Algorithmic algorithm => Bool -> algorithm -> IO ()+factorialPerformanceGraph verbose algorithm = mapM_ (+ \operand -> factorialPerformance algorithm operand >>= putStrLn . shows operand . showChar '\t' . (+ if verbose+ then (`shows` "")+ else (`shows` "") . fst+ )+ ) $ iterate (* 2) (1 :: Integer)++-- | Graphs the CPU-time required by a naive implementation, against an exponentially increasing operand.+factorialPerformanceGraphControl :: Bool -> IO ()+factorialPerformanceGraphControl verbose = mapM_ (+ \operand -> factorialPerformanceControl operand >>= putStrLn . shows operand . showChar '\t' . (+ if verbose+ then (`shows` "")+ else (`shows` "") . fst+ )+ ) $ iterate (* 2) (1 :: Integer)+
+ src-exe/Factory/Test/Performance/Hyperoperation.hs view
@@ -0,0 +1,71 @@+{-+ Copyright (C) 2011 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@] Times functions exported from module "Math.Hyperoperation".+-}++module Factory.Test.Performance.Hyperoperation(+-- * Functions+ hyperoperationPerformance,+ hyperoperationPerformanceGraphRank,+ hyperoperationPerformanceGraphExponent+) where++import qualified Factory.Math.Hyperoperation as Math.Hyperoperation+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required by 'Math.Hyperoperation.hyperoperation'.+hyperoperationPerformance :: (Integral rank, Show rank) => rank -> Math.Hyperoperation.Base -> Math.Hyperoperation.HyperExponent -> IO (Double, Integer)+hyperoperationPerformance rank base = ToolShed.System.TimePure.getCPUSeconds . Math.Hyperoperation.hyperoperation rank base++{- |+ * Measure the CPU-time required by 'Math.Hyperoperation.hyperoperation', against a linearly increasing /rank/.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+hyperoperationPerformanceGraphRank+ :: Bool -- ^ Verbose.+ -> Math.Hyperoperation.Base+ -> Math.Hyperoperation.HyperExponent+ -> IO ()+hyperoperationPerformanceGraphRank verbose base hyperExponent = mapM_ (+ \rank -> hyperoperationPerformance rank base hyperExponent >>= putStrLn . shows rank . showChar '\t' . (+ if verbose+ then (`shows` "")+ else (`shows` "") . fst+ )+ ) [0 :: Int ..]++{- |+ * Measure the CPU-time required by 'Math.Hyperoperation.hyperoperation', against a linearly increasing /hyper-exponent/.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+hyperoperationPerformanceGraphExponent :: (Integral rank, Show rank)+ => Bool -- ^ Verbose.+ -> rank+ -> Math.Hyperoperation.Base+ -> IO ()+hyperoperationPerformanceGraphExponent verbose rank base = mapM_ (+ \hyperExponent -> hyperoperationPerformance rank base hyperExponent >>= putStrLn . shows hyperExponent . showChar '\t' . (+ if verbose+ then (`shows` "")+ else (`shows` "") . fst+ )+ ) [0 ..]
+ src-exe/Factory/Test/Performance/Pi.hs view
@@ -0,0 +1,81 @@+{-+ Copyright (C) 2011 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@] Times the methods exported from module "Math.Pi".+-}++module Factory.Test.Performance.Pi(+-- * Types+-- ** Type-synonyms+ Category,+-- * Functions+ piPerformance,+ piPerformanceGraph+) where++import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Pi.AGM.Algorithm as Math.Implementations.Pi.AGM.Algorithm+import qualified Factory.Math.Implementations.Pi.BBP.Algorithm as Math.Implementations.Pi.BBP.Algorithm+import qualified Factory.Math.Implementations.Pi.Borwein.Algorithm as Math.Implementations.Pi.Borwein.Algorithm+import qualified Factory.Math.Implementations.Pi.Ramanujan.Algorithm as Math.Implementations.Pi.Ramanujan.Algorithm+import qualified Factory.Math.Implementations.Pi.Spigot.Algorithm as Math.Implementations.Pi.Spigot.Algorithm+import qualified Factory.Math.Pi as Math.Pi+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified ToolShed.System.TimePure++-- | The type of a /Pi/-algorithm, including where required, the algorithm for /square-root/s and /factorial/s.+type Category squareRootAlgorithm factorialAlgorithm = Math.Pi.Category (+ Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm+ ) Math.Implementations.Pi.BBP.Algorithm.Algorithm (+ Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm+ ) (+ Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm+ ) Math.Implementations.Pi.Spigot.Algorithm.Algorithm++-- | Measures the CPU-time required to find Pi to the required precision.+piPerformance :: (+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Category squareRootAlgorithm factorialAlgorithm -> Math.Precision.DecimalDigits -> IO (Double, String)+piPerformance category = ToolShed.System.TimePure.getCPUSeconds . Math.Pi.openS category++{- |+ * Measures the CPU-time required to determine /Pi/ to an exponentially increasing precision-requirement.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+piPerformanceGraph :: (+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Show squareRootAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm,+ Show factorialAlgorithm+ ) => RealFrac i+ => Category squareRootAlgorithm factorialAlgorithm -- ^ The algorithm.+ -> i -- ^ The factor by which the precision is increased on each iteration.+ -> Math.Precision.DecimalDigits -- ^ The maximum precision required.+ -> Bool -- ^ Whether to return the digits of /Pi/.+ -> IO ()+piPerformanceGraph category factor maxDecimalDigits verbose = mapM_ (+ \decimalDigits -> piPerformance category decimalDigits >>= putStrLn . shows decimalDigits . showChar '\t' . (+ if verbose+ then (`shows` "")+ else (`shows` "") . fst+ )+ ) . takeWhile (<= maxDecimalDigits) . map round $ iterate (* factor) 1
+ src-exe/Factory/Test/Performance/Primality.hs view
@@ -0,0 +1,54 @@+{-+ Copyright (C) 2011 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@] Times functions exported from module "Math.Primality".+-}++module Factory.Test.Performance.Primality(+-- * Functions+ carmichaelNumbersPerformance,+ isPrimePerformance,+ isPrimePerformanceGraph+) where++import qualified Control.DeepSeq+import qualified Factory.Math.Fibonacci as Math.Fibonacci+import qualified Factory.Math.Primality as Math.Primality+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required to find the specified number of /Carmichael/-numbers, which is returned together with the requested list.+carmichaelNumbersPerformance :: Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> Int -> IO (Double, [Integer])+carmichaelNumbersPerformance primalityAlgorithm i+ | i < 0 = fail $ "Factory.Test.Performance.Primality.carmichaelNumbersPerformance:\tnegative number; " ++ show i+ | otherwise = ToolShed.System.TimePure.getCPUSeconds . take i $ Math.Primality.carmichaelNumbers primalityAlgorithm++-- | Measures the CPU-time required to determine whether the specified integer is prime, which is returned together with the Boolean result.+isPrimePerformance :: (Control.DeepSeq.NFData i, Integral i, Show i) => Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> i -> IO (Double, Bool)+isPrimePerformance primalityAlgorithm = ToolShed.System.TimePure.getCPUSeconds . Math.Primality.isPrime primalityAlgorithm++{- |+ * Measures the CPU-time required to determine whether /prime-indexed Fibonacci-numbers/ are actually /prime/.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+isPrimePerformanceGraph :: Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> IO ()+isPrimePerformanceGraph primalityAlgorithm = mapM_ (+ \operand -> isPrimePerformance primalityAlgorithm operand >>= putStrLn . shows operand . showChar '\t' . (`shows` "")+ ) (Math.Fibonacci.primeIndexedFibonacci :: [Integer])+
+ src-exe/Factory/Test/Performance/PrimeFactorisation.hs view
@@ -0,0 +1,50 @@+{-+ Copyright (C) 2011 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@] Times the methods exported by module "Math.PrimeFactorisation".+-}++module Factory.Test.Performance.PrimeFactorisation(+-- * Functions+ primeFactorsPerformance,+ primeFactorsPerformanceGraph+) where++import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Fibonacci as Math.Fibonacci+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required to prime-factorise the specified integer, which is returned together with the resulting list of factors.+primeFactorsPerformance :: Math.PrimeFactorisation.Algorithmic algorithm => algorithm -> Integer -> IO (Double, Data.PrimeFactors.Factors Integer Int)+primeFactorsPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . Math.PrimeFactorisation.primeFactors algorithm++{- |+ * Measure the CPU-time required by 'Math.PrimeFactorisation.primeFactors',+ arbitrarily against the /Fibonacci/-numbers (which seemed to fit the requirements).++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+primeFactorsPerformanceGraph :: Math.PrimeFactorisation.Algorithmic algorithm => algorithm -> Int -> IO ()+primeFactorsPerformanceGraph algorithm tests+ | tests < 0 = fail $ "Factory.Test.Performance.PrimeFactorisation.primeFactorsPerformanceGraph:\tnegative number; " ++ show tests+ | otherwise = mapM_ (+ \operand -> primeFactorsPerformance algorithm operand >>= putStrLn . shows operand . showChar '\t' . (`shows` "")+ ) . take tests . dropWhile (< 2) $ Math.Fibonacci.fibonacci+
+ src-exe/Factory/Test/Performance/Primes.hs view
@@ -0,0 +1,47 @@+{-+ Copyright (C) 2011 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@] Measures the CPU-time required by "Math.Primes.primes".+-}++module Factory.Test.Performance.Primes(+-- * Functions+ primesPerformance,+ mersenneNumbersPerformance+) where++import qualified Control.DeepSeq+import qualified Data.Array.IArray+import qualified Factory.Math.Primes as Math.Primes+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required by 'Math.Primes.primes', to find the specified prime.+primesPerformance :: (+ Control.DeepSeq.NFData i,+ Data.Array.IArray.Ix i,+ Math.Primes.Algorithmic algorithm,+ Integral i+ ) => algorithm -> Int -> IO (Double, i)+primesPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . (Math.Primes.primes algorithm !!)++-- | Measures the CPU-time required to find the specified number of /Mersenne/-numbers, which is returned together with the requested list.+mersenneNumbersPerformance :: Math.Primes.Algorithmic algorithm => algorithm -> Int -> IO (Double, [Integer])+mersenneNumbersPerformance primalityAlgorithm i+ | i < 0 = fail $ "Factory.Test.Performance.Primes.mersenneNumbersPerformance:\tnegative number; " ++ show i+ | otherwise = ToolShed.System.TimePure.getCPUSeconds . take i $ Math.Primes.mersenneNumbers primalityAlgorithm
+ src-exe/Factory/Test/Performance/SquareRoot.hs view
@@ -0,0 +1,59 @@+{-+ Copyright (C) 2011 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@] Measures the CPU-time required by the methods exported from module "Math.SquareRoot".+-}++module Factory.Test.Performance.SquareRoot(+-- * Functions+ squareRootPerformance,+ squareRootPerformanceGraph+) where++import qualified Control.Arrow+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required by 'Math.SquareRoot.squareRootFrom', which is returned together with the approximate rational result.+squareRootPerformance :: (+ Math.SquareRoot.Algorithmic algorithm,+ Real operand,+ Show operand+ ) => algorithm -> operand -> Math.Precision.DecimalDigits -> IO (Double, Math.SquareRoot.Result)+squareRootPerformance algorithm operand requiredDecimalDigits = ToolShed.System.TimePure.getCPUSeconds $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand++{- |+ * Measures the CPU-time required by 'Math.SquareRoot.squareRootFrom', and the resulting accuracy,+ using the specified algorithm, to an exponentially increasing precision-requirement.++ * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.+-}+squareRootPerformanceGraph :: (+ Math.SquareRoot.Algorithmic algorithm,+ Math.SquareRoot.Iterator algorithm,+ Real operand,+ Show algorithm,+ Show operand+ ) => algorithm -> operand -> IO ()+squareRootPerformanceGraph algorithm operand = mapM_ (+ \requiredDecimalDigits -> putStrLn . (+ \(cpuSeconds, actualDecimalDigits) -> shows algorithm . showChar '\t' . shows requiredDecimalDigits . showChar '\t' . shows actualDecimalDigits . showChar '\t' $ shows cpuSeconds ""+ ) . Control.Arrow.second (Math.SquareRoot.getAccuracy operand) =<< squareRootPerformance algorithm operand requiredDecimalDigits+ ) $ iterate (* max 2 (Math.SquareRoot.convergenceOrder algorithm)) 16
+ src-exe/Factory/Test/Performance/Statistics.hs view
@@ -0,0 +1,45 @@+{-+ Copyright (C) 2011 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@] Times the functions exported from module "Math.Statistics".+-}++module Factory.Test.Performance.Statistics(+-- * Functions+ nCrPerformance+) where++import qualified Control.DeepSeq+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Statistics as Math.Statistics+import qualified ToolShed.System.TimePure++-- | Measures the CPU-time required by 'Math.Statistics.nCr'.+nCrPerformance :: (+ Control.DeepSeq.NFData i,+ Integral i,+ Math.Factorial.Algorithmic factorialAlgorithm,+ Show i+ )+ => factorialAlgorithm+ -> i -- ^ The total number from which to select.+ -> i -- ^ The number of items in a sample.+ -> IO (Double, i)+nCrPerformance factorialAlgorithm n r = ToolShed.System.TimePure.getCPUSeconds $ Math.Statistics.nCr factorialAlgorithm n r+
+ src-exe/Main.hs view
@@ -0,0 +1,241 @@+{-+ 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 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.Info+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 "" ["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 :: IO Test.CommandOptions.CommandOptions+ printVersion = System.IO.hPutStrLn System.IO.stderr (+ Distribution.Text.display packageIdentifier ++ "\n\nCompiled by " ++ show compiler ++ ".\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.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 usageMessage >> System.Exit.exitSuccess++ factorialPerformanceGraphControl :: Test.CommandOptions.CommandOptions -> IO Test.CommandOptions.CommandOptions+ factorialPerformanceGraphControl commandOptions = Test.Performance.Factorial.factorialPerformanceGraphControl (Test.CommandOptions.verbose commandOptions) >> System.Exit.exitFailure++ 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.exitSuccess where+ algorithm :: PrimalityAlgorithm+ (algorithm, i) = readCommandArg arg++ factorialPerformance arg _ = Test.Performance.Factorial.factorialPerformance algorithm i >>= print >> 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.exitFailure++ hyperoperationPerformance arg _ = Test.Performance.Hyperoperation.hyperoperationPerformance rank base hyperExponent >>= print >> 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.exitFailure 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.exitFailure where+ rank :: Integer+ base :: Math.Hyperoperation.Base+ (rank, base) = readCommandArg arg++ isPrimePerformance arg _ = Test.Performance.Primality.isPrimePerformance algorithm i >>= print >> 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.exitFailure++ mersenneNumbersPerformance arg _ = Test.Performance.Primes.mersenneNumbersPerformance algorithm i >>= print >> 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.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.exitSuccess where+ category :: PiCategory+ (category, decimalDigits) = readCommandArg arg++ piPerformanceGraph arg commandOptions = Test.Performance.Pi.piPerformanceGraph category factor maxDecimalDigits (Test.CommandOptions.verbose commandOptions) >> System.Exit.exitFailure 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.exitSuccess++ primeFactorsPerformance arg _ = Test.Performance.PrimeFactorisation.primeFactorsPerformance algorithm i >>= print >> System.Exit.exitSuccess where+ algorithm :: Math.Implementations.PrimeFactorisation.Algorithm+ (algorithm, i) = readCommandArg arg++ primeFactorsPerformanceGraph arg _ = Test.Performance.PrimeFactorisation.primeFactorsPerformanceGraph algorithm index >> System.Exit.exitFailure 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.exitSuccess where+ algorithm :: Math.Implementations.Primes.Algorithm.Algorithm+ (algorithm, index) = readCommandArg arg++ squareRootPerformance arg _ = Test.Performance.SquareRoot.squareRootPerformance algorithm operand decimalDigits >>= print >> 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.exitFailure 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.exitSuccess+ (_, _, errors) -> System.IO.Error.ioError . System.IO.Error.userError $ concat errors ++ usageMessage -- Throw.+
+ src-lib/Factory/Data/Exponential.hs view
@@ -0,0 +1,89 @@+{-+ Copyright (C) 2011 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@]++ * Describes a simple numeric type, designed to contain an /exponential/ number.++ * <http://en.wikipedia.org/wiki/Exponentiation>.+-}++module Factory.Data.Exponential(+-- * Types+-- ** Type-synonyms+ Exponential,+-- * Functions+ evaluate,+ invert,+-- ** Accessors+ getBase,+ getExponent,+-- ** Constructors+ rightIdentity,+-- ** Operators+ (<^),+ (=~)+) where++import qualified Control.Arrow++infix 4 =~ -- Same as (==).+infixr 8 <^ -- Same as (^).++-- | Describes an /exponential/, in terms of its /base/ and /exponent/.+type Exponential base exponent = (base, exponent)++-- | Accessor.+{-# INLINE getBase #-}+getBase :: Exponential base exponent -> base+getBase = fst++-- | Accessor.+{-# INLINE getExponent #-}+getExponent :: Exponential base exponent -> exponent+getExponent = snd++{- |+ * Construct an 'Exponential' merely raised to the 1st power.++ * The value of the resulting exponential is the same as specified 'base'; <http://en.wikipedia.org/wiki/Identity_element>.+-}+rightIdentity :: Num exponent => base -> Exponential base exponent+rightIdentity x = (x, 1)++-- | Evaluate the specified 'Exponential', returning the resulting number.+{-# INLINE evaluate #-}+evaluate :: (Num base, Integral exponent) => Exponential base exponent -> base+evaluate = uncurry (^) -- CAVEAT: in this eta-reduced form, it'll only be inlined when called without arguments.++-- | True if the /bases/ are equal.+(=~) :: Eq base => Exponential base exponent -> Exponential base exponent -> Bool+(l, _) =~ (r, _) = l == r++-- | Raise the specified 'Exponential' to a power.+(<^) :: Num exponent+ => Exponential base exponent -- ^ The operand.+ -> exponent -- ^ The power to which the exponential is to be raised.+ -> Exponential base exponent -- ^ The result.+(b, e) <^ power = (b, e * power)++-- | Invert the value, by negating the exponent.+invert :: Num exponent => Exponential base exponent -> Exponential base exponent+invert = Control.Arrow.second negate+
+ src-lib/Factory/Data/Interval.hs view
@@ -0,0 +1,201 @@+{-+ Copyright (C) 2011 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@]++ * Describes a bounded set of, typically integral, quantities.++ * Operations have been defined, on the list of /consecutive/ quantities delimited by these endpoints.++ * The point is that if the list is composed from /consecutive/ quantities, the intermediate values can be inferred, rather than physically represented.++ [@CAVEATS@]++ * The API was driven top-down by its caller's requirements, rather than a bottom-up attempt to provide a complete interface.+ consequently there may be omissions from the view point of future callers.++ * Thought similar to the mathematical concept of an /interval/, the latter technically relates to /real/ numbers; <http://en.wikipedia.org/wiki/Interval_%28mathematics%29>.++ * No account has been made for /semi-closed/ or /open/ intervals.+-}++module Factory.Data.Interval(+-- * Types+-- ** Type-synonyms+ Interval,+-- * Constants+ closedUnitInterval,+ mkBounded,+-- * Functions+-- divideAndConquer,+ elem',+-- getLength,+ normalise,+ product',+ shift,+ splitAt',+ toList,+-- ** Accessors+ getMinBound,+ getMaxBound,+-- ** Constructors+ precisely,+-- ** Predicates+ isReversed+) where++import Control.Arrow((***), (&&&))+import qualified Control.Parallel.Strategies+import qualified Data.Monoid+import qualified Data.Ratio+import qualified Data.Tuple+import qualified ToolShed.Data.Pair++-- | Defines a closed (inclusive) interval of consecutive values.+type Interval endPoint = (endPoint, endPoint)++-- | Accessor.+{-# INLINE getMinBound #-}+getMinBound :: Interval endPoint -> endPoint+getMinBound = fst++-- | Accessor.+{-# INLINE getMaxBound #-}+getMaxBound :: Interval endPoint -> endPoint+getMaxBound = snd++-- | Construct the /unsigned closed unit-interval/; <http://en.wikipedia.org/wiki/Unit_interval>.+closedUnitInterval :: Num n => Interval n+closedUnitInterval = (0, 1)++-- | Construct an /interval/ from a bounded type.+mkBounded :: Bounded endPoint => Interval endPoint+mkBounded = (minBound, maxBound)++-- | Construct an /interval/ from a single value.+precisely :: endPoint -> Interval endPoint+precisely = id &&& id++-- | Shift of both /end-points/ of the /interval/ by the specified amount.+shift :: Num endPoint+ => endPoint -- ^ The magnitude of the require shift.+ -> Interval endPoint -- ^ The interval to be shifted.+ -> Interval endPoint+shift i = ToolShed.Data.Pair.mirror (+ i)++-- | True if the specified value is within the inclusive bounds of the /interval/.+elem' :: Ord endPoint => endPoint -> Interval endPoint -> Bool+elem' x = uncurry (&&) . ((<= x) *** (x <=))++-- | True if 'getMinBound' exceeds 'getMaxBound' extent.+isReversed :: Ord endPoint => Interval endPoint -> Bool+isReversed = uncurry (>)++-- | Swap the /end-points/ where they were originally reversed, but otherwise do nothing.+normalise :: Ord endPoint => Interval endPoint -> Interval endPoint+normalise b+ | isReversed b = Data.Tuple.swap b+ | otherwise = b++-- | Bisect the /interval/ at the specified /end-point/; which should be between the two existing /end-points/.+splitAt' :: (+ Enum endPoint,+ Num endPoint,+ Ord endPoint,+ Show endPoint+ ) => endPoint -> Interval endPoint -> (Interval endPoint, Interval endPoint)+splitAt' i interval@(l, r)+ | any ($ i) [(< l), (>= r)] = error $ "Factory.Data.Interval.splitAt':\tunsuitable index=" ++ show i ++ " for interval=" ++ show interval ++ "."+ | otherwise = ((l, i), (succ i, r))++{- |+ * The distance between the endpoints,+ which for 'Integral' quantities is the same as the number of items in closed interval; though the latter concept would return type 'Int'.++ * CAVEAT: the implementation accounts for the potential fence-post error, for closed intervals of integers,+ but this results in the opposite error when used with /Fractional/ quantities.+ So, though most of the module merely requires 'Enum', this function is further restricted to 'Integral'.+-}+{-# INLINE getLength #-}+getLength :: Integral endPoint => Interval endPoint -> endPoint+getLength (l, r) = succ r - l++{- |+ * Converts 'Interval' to a list by enumerating the values.++ * CAVEAT: produces rather odd results for 'Fractional' types, but no stranger than considering such types Enumerable in the first place.+-}+{-# INLINE toList #-}+toList :: Enum endPoint => Interval endPoint -> [endPoint]+toList = uncurry enumFromTo -- CAVEAT: in this eta-reduced form, it'll only be inlined when called without arguments.++{- |+ * Reduces 'Interval' to a single integral value encapsulated in a 'Data.Monoid.Monoid',+ using a /divide-and-conquer/ strategy,+ bisecting the /interval/ and recursively evaluating each part; <http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm>.++ * By choosing a 'ratio' other than @(1 % 2)@, the bisection can be made asymmetrical.+ The specified ratio represents the length of the left-hand portion over the original list-length;+ eg. @(1 % 3)@ results in the first part, half the length of the second.++ * This process of recursive bisection, is terminated beneath the specified minimum length,+ after which the 'Interval' are expanded into the corresponding list, and the /monoid/'s binary operator is directly /folded/ over it.++ * One can view this as a <http://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>,+ in which 'Interval' is exploded into a binary tree-structure+ (each leaf of which contains a list of up to 'minLength' integers, and each node of which contains an associative binary operator),+ and then collapsed to a scalar, by application of the operators.+-}+divideAndConquer :: (Data.Monoid.Monoid monoid, Integral i, Show i)+ => (i -> monoid) -- ^ The monoid's constructor.+ -> Data.Ratio.Ratio i -- ^ The ratio of the original span, at which to bisect the 'Interval'.+ -> i -- ^ For efficiency, the /interval/ will not be bisected, when it's length has been reduced to this value.+ -> Interval i+ -> monoid -- ^ The resulting scalar.+divideAndConquer monoidConstructor ratio minLength+ | any ($ ratio) [+ (< 0),+ (>= 1)+ ] = error $ "Factory.Data.Interval.divideAndConquer:\tunsuitable ratio='" ++ show ratio ++ "'."+ | minLength < 1 = error $ "Factory.Data.Interval.divideAndConquer:\tunsuitable minLength=" ++ show minLength ++ "."+ | otherwise = slave+ where+ slave interval@(l, r)+ | getLength interval <= minLength = Data.Monoid.mconcat . map monoidConstructor $ toList interval -- Fold the monoid's binary operator over the delimited list.+ | otherwise = uncurry Data.Monoid.mappend . Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rseq Control.Parallel.Strategies.rseq+ ) . ToolShed.Data.Pair.mirror slave $ splitAt' (+ l + (r - l) * Data.Ratio.numerator ratio `div` Data.Ratio.denominator ratio -- Use the ratio to generate the split-index.+ ) interval -- Apply the monoid's binary operator to the two operands resulting from bisection.++{- |+ * Multiplies the consecutive sequence of integers within 'Interval'.++ * Since the result can be large, 'divideAndConquer' is used to form operands of a similar order of magnitude,+ thus improving the efficiency of the big-number multiplication.+-}+product' :: (Integral i, Show i)+ => Data.Ratio.Ratio i -- ^ The ratio at which to bisect the 'Interval'.+ -> i -- ^ For efficiency, the /interval/ will not be bisected, when it's length has been reduced to this value.+ -> Interval i+ -> i -- ^ The resulting product.+product' ratio minLength interval+ | elem' 0 interval = 0+ | otherwise = Data.Monoid.getProduct $ divideAndConquer Data.Monoid.Product ratio minLength interval+
+ src-lib/Factory/Data/MonicPolynomial.hs view
@@ -0,0 +1,98 @@+{-+ Copyright (C) 2011 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@]++ * Describes a /monic polynomial; <http://en.wikipedia.org/wiki/Monic_polynomial#Classifications>;+ ie. in which the /coefficient/ of the /leading term/ is one.+-}++module Factory.Data.MonicPolynomial(+-- * Types+-- ** Data-types,+ MonicPolynomial(getPolynomial), -- Hide the data-constructor.+-- * Functions+-- ** Constructors+ mkMonicPolynomial+) where++import qualified Control.Arrow+import qualified Factory.Data.Monomial as Data.Monomial+import Factory.Data.Polynomial((*=))+import qualified Factory.Data.Polynomial as Data.Polynomial+import qualified Factory.Data.QuotientRing as Data.QuotientRing+import Factory.Data.Ring((=*=), (=+=), (=-=))+import qualified Factory.Data.Ring as Data.Ring+import qualified ToolShed.Data.Pair++-- | A type of 'Data.Polynomial.Polynomial', in which the /leading term/ is required to have a /coefficient/ of one.+newtype MonicPolynomial c e = MkMonicPolynomial {+ getPolynomial :: Data.Polynomial.Polynomial c e+} deriving (Eq, Show)++-- | Smart constructor. Constructs an arbitrary /monic polynomial/.+mkMonicPolynomial :: (+ Eq c,+ Num c,+ Ord e,+ Show c,+ Show e+ ) => Data.Polynomial.Polynomial c e -> MonicPolynomial c e+mkMonicPolynomial polynomial+ | not $ Data.Polynomial.isMonic polynomial = error $ "Factory.Data.MonicPolynomial.mkMonicPolynomial:\tnot monic; " ++ show polynomial+ | otherwise = MkMonicPolynomial polynomial++{-+ * This instance-declaration merely delegates to the 'Data.Polynomial.Polynomial' payload.++ * CAVEAT: it's not strictly an instance of this class, since the result of some methods isn't /monic/.+-}+instance (+ Eq c,+ Num c,+ Num e,+ Ord e,+ Show c,+ Show e+ ) => Data.Ring.Ring (MonicPolynomial c e) where+ MkMonicPolynomial l =*= MkMonicPolynomial r = MkMonicPolynomial $ l =*= r+ MkMonicPolynomial l =+= MkMonicPolynomial r = mkMonicPolynomial $ l =+= r -- CAVEAT: potentially non-monic.+-- additiveInverse (MkMonicPolynomial p) = MkMonicPolynomial $ Data.Ring.additiveInverse p -- CAVEAT: not monic !+ additiveInverse _ = error "Factory.Data.MonicPolynomial.additiveInverse:\tresult isn't monic"+ multiplicativeIdentity = MkMonicPolynomial Data.Ring.multiplicativeIdentity+ additiveIdentity = MkMonicPolynomial Data.Ring.additiveIdentity -- CAVEAT: not monic !++-- Since the /leading term/ of the /denominator/ is one, the /coefficient/ isn't required to implement 'Fractional'.+instance (+ Eq c,+ Num c,+ Num e,+ Ord e,+ Show c,+ Show e+ ) => Data.QuotientRing.QuotientRing (MonicPolynomial c e) where+ MkMonicPolynomial polynomialN `quotRem'` MkMonicPolynomial polynomialD = ToolShed.Data.Pair.mirror MkMonicPolynomial $ longDivide polynomialN where+-- longDivide :: (Num c, Num e, Ord e) => Polynomial c e -> (Polynomial c e, Polynomial c e)+ longDivide numerator+ | Data.Polynomial.isZero numerator || Data.Monomial.getExponent quotient < 0 = (Data.Polynomial.zero, numerator)+ | otherwise = Control.Arrow.first (Data.Polynomial.lift (quotient :)) $ longDivide (numerator =-= polynomialD *= quotient)+ where+-- quotient :: Num e => Data.Monomial.Monomial c e+ quotient = Data.Polynomial.getLeadingTerm numerator `Data.Monomial.shiftExponent` negate (Data.Monomial.getExponent $ Data.Polynomial.getLeadingTerm polynomialD)+
+ src-lib/Factory/Data/Monomial.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE CPP #-}+{-+ Copyright (C) 2011-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@]++ * Describes a <http://en.wikipedia.org/wiki/Monomial> and operations on it.++ * A /monomial/ is merely a /polynomial/ with a single non-zero term; cf. /Binomial/.+-}++module Factory.Data.Monomial(+-- * Types+-- ** Type-synonyms+ Monomial,+-- * Functions+ double,+ mod',+ negateCoefficient,+ realCoefficientToFrac,+ shiftCoefficient,+ shiftExponent,+ square,+-- ** Accessors+ getExponent,+ getCoefficient,+-- ** Operators+ (<=>),+ (</>),+ (<*>), -- CAVEAT: this clashes with the Prelude from 'base-4.8'.+ (=~),+-- ** Predicates+ isMonomial+) where++import qualified Control.Arrow++#if MIN_VERSION_base(4,8,0)+import Prelude hiding ((<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.+#endif++infix 4 <=> -- Same as (==).+infix 4 =~ -- Same as (==).+infixl 7 </> -- Same as (/).+infixl 7 <*> -- Same as (*).++{- |+ * The type of an arbitrary monomial.++ * CAVEAT: though a /monomial/ has an integral power, this contraint is only imposed at the function-level.+-}+type Monomial coefficient exponent = (coefficient, exponent)++-- | Accessor.+{-# INLINE getCoefficient #-}+getCoefficient :: Monomial c e -> c+getCoefficient = fst++-- | Accessor.+{-# INLINE getExponent #-}+getExponent :: Monomial c e -> e+getExponent = snd++{- |+ * 'True' if the /exponent/ is both integral and non-/negative/.++ * CAVEAT: one can't even call this function unless the /exponent/ is integral.+-}+isMonomial :: Integral e => Monomial c e -> Bool+isMonomial = (>= 0) . getExponent++-- | Compares the /exponents/ of the specified 'Monomial's.+{-# INLINE (<=>) #-}+(<=>) :: Ord e => Monomial c e -> Monomial c e -> Ordering+(_, l) <=> (_, r) = l `compare` r++-- | True if the /exponents/ are equal.+(=~) :: Eq e => Monomial c e -> Monomial c e -> Bool+(_, l) =~ (_, r) = l == r++-- | Multiply the two specified 'Monomial's.+{-# INLINE (<*>) #-}+(<*>) :: (Num c, Num e) => Monomial c e -> Monomial c e -> Monomial c e+(cL, eL) <*> (cR, eR) = (cL * cR, eL + eR)++-- | Divide the two specified 'Monomial's.+(</>) :: (Eq c, Fractional c, Num e)+ => Monomial c e -- ^ Numerator.+ -> Monomial c e -- ^ Denominator.+ -> Monomial c e+(cN, eN) </> (1, eD) = (cN, eN - eD)+(cN, eN) </> (cD, eD) = (cN / cD, eN - eD)++-- | Square the specified 'Monomial'.+square :: (Num c, Num e) => Monomial c e -> Monomial c e+square (c, e) = (c ^ (2 :: Int), 2 * e)++-- | Double the specified 'Monomial'.+{-# INLINE double #-}+double :: Num c => Monomial c e -> Monomial c e+double (c, e) = (2 * c, e)++-- | Shift the /coefficient/, by the specified amount.+{-# INLINE shiftCoefficient #-}+shiftCoefficient :: Num c+ => Monomial c e+ -> c -- ^ The magnitude of the shift.+ -> Monomial c e+-- m `shiftCoefficient` i = Control.Arrow.first (+ i) m -- CAVEAT: Too slow.+(c, e) `shiftCoefficient` i = (c + i, e)++-- | Shift the /exponent/, by the specified amount.+{-# INLINE shiftExponent #-}+shiftExponent :: Num e+ => Monomial c e+ -> e -- ^ The magnitude of the shift.+ -> Monomial c e+-- m `shiftExponent` i = Control.Arrow.second (+ i) m -- CAVEAT: Too slow.+(c, e) `shiftExponent` i = (c, e + i)++-- | Negate the coefficient.+negateCoefficient :: Num c => Monomial c e -> Monomial c e+negateCoefficient = Control.Arrow.first negate++-- | Reduce the coefficient using /modular/ arithmetic.+{-# INLINE mod' #-}+mod' :: Integral c+ => Monomial c e+ -> c -- ^ Modulus.+ -> Monomial c e+monomial `mod'` modulus = Control.Arrow.first (`mod` modulus) monomial++-- | Convert the type of the /coefficient/.+realCoefficientToFrac :: (Real r, Fractional f) => Monomial r e -> Monomial f e+realCoefficientToFrac = Control.Arrow.first realToFrac+
+ src-lib/Factory/Data/Polynomial.hs view
@@ -0,0 +1,379 @@+{-# LANGUAGE CPP #-}+{-+ Copyright (C) 2011-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@]++ * Describes a <http://en.wikipedia.org/wiki/Univariate> polynomial and operations on it.++ * <http://en.wikipedia.org/wiki/Polynomial>.++ * <http://mathworld.wolfram.com/Polynomial.html>.+-}++module Factory.Data.Polynomial(+-- * Types+-- ** Type-synonyms+-- MonomialList,+-- ** Data-types,+ Polynomial,+-- * Constants+ zero,+ one,+-- * Functions+ evaluate,+ getDegree,+ getLeadingTerm,+ lift,+ mod',+ normalise,+-- pruneCoefficients,+ raiseModulo,+ realCoefficientsToFrac,+ terms,+-- ** Constructors+ mkConstant,+ mkLinear,+ mkPolynomial,+-- ** Operators+ (*=),+-- ** Predicates+ areCongruentModulo,+ inAscendingOrder,+ inDescendingOrder,+-- inOrder,+ isMonic,+ isMonomial,+ isNormalised,+ isPolynomial,+-- isReduced,+ isZero+) where++import Control.Arrow((&&&))+import qualified Control.Arrow+import qualified Data.List+import Factory.Data.Monomial((<*>), (</>), (<=>), (=~))+import qualified Factory.Data.Monomial as Data.Monomial+import qualified Factory.Data.QuotientRing as Data.QuotientRing+import Factory.Data.Ring((=*=), (=+=), (=-=))+import qualified Factory.Data.Ring as Data.Ring++#if MIN_VERSION_base(4,8,0)+import Prelude hiding ((<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.+#endif++infixl 7 *= -- Same as (*).++-- | The guts of a 'Polynomial'.+type MonomialList coefficient exponent = [Data.Monomial.Monomial coefficient exponent]++{- |+ * The type of an arbitrary /univariate/ polynomial;+ actually it's more general, since it permits negative powers (<http://en.wikipedia.org/wiki/Laurent_polynomial>s).+ It can't describe /multivariate/ polynomials, which would require a list of /exponents/.+ Rather than requiring the /exponent/ to implement the /type-class/ 'Integral', this is implemented at the function-level, as required.++ * The structure permits gaps between /exponents/,+ in which /coefficients/ are inferred to be zero, thus enabling efficient representation of sparse polynomials.++ * CAVEAT: the 'MonomialList' is required to;+ be ordered by /descending/ exponent (ie. reverse <http://en.wikipedia.org/wiki/Monomial_order>);+ have had zero coefficients removed;+ and to have had /like/ terms merged;+ so the raw data-constructor isn't exported.+-}+newtype {- Integral exponent => -} Polynomial coefficient exponent = MkPolynomial {+ getMonomialList :: MonomialList coefficient exponent -- ^ Accessor.+} deriving (Eq, Show)++-- | Makes /Polynomial/ a 'Data.Ring.Ring', over the /field/ composed from all possible /coefficients/; <http://en.wikipedia.org/wiki/Polynomial_ring>.+instance (+ Eq c,+ Num c,+ Num e,+ Ord e+ ) => Data.Ring.Ring (Polynomial c e) where+ MkPolynomial [] =*= _ = zero+ _ =*= MkPolynomial [] = zero+ polynomialL =*= polynomialR+-- | polynomialL == one = polynomialR -- Counterproductive.+-- | polynomialR == one = polynomialL -- Counterproductive.+ | terms polynomialL > terms polynomialR = polynomialL `times` polynomialR+ | otherwise = polynomialR `times` polynomialL+ where+ l `times` r = {-# SCC "times" #-} Data.Ring.sum' (recip 2) {-TODO-} 10 {-empirical-} . map (l *=) $ getMonomialList r++ MkPolynomial [] =+= p = p+ p =+= MkPolynomial [] = p+ MkPolynomial listL =+= MkPolynomial listR = {-# SCC "merge" #-} MkPolynomial $ merge listL listR where+ merge [] r = r+ merge l [] = l+ merge l@(lh : ls) r@(rh : rs) = case lh <=> rh of+ GT -> lh : merge ls r+ LT -> rh : merge l rs+ _ -> case lh `Data.Monomial.shiftCoefficient` Data.Monomial.getCoefficient rh of+ (0, _) -> merge ls rs+ monomial -> monomial : merge ls rs++ additiveInverse = lift (Data.Monomial.negateCoefficient `map`)+ multiplicativeIdentity = one+ additiveIdentity = zero++{-+ Override the default implementation,+ in order to take advantage of the symmetry under reflection about the main diagonal,+ in the square matrix of products formed from the multiplication of each term by each term.+ Eg:+ (ax^3 + bx^2 + cx + d)^2 = [+ (a^2x^6 + abx^5 + acx^4 + adx^3) ++ (bax^5 + b^2x^4 + bcx^3 + bdx^2) ++ (cax^4 + cbx^3 + c^2x^2 + cdx) ++ (dax^3 + dbx^2 + dcx + d^2)+ ]++ = (a^2x^6 + b^2x^4 + c^2x^2 + d^2) + 2 * [ax^3 * (bx^2 + cx + d) + bx^2 * (cx + d) + cx * (d)]+-}+ square (MkPolynomial []) = zero+ square p = Data.Ring.sum' (recip 2) {-TODO-} 10 {-empirical-} $ diagonal : corners where+ diagonal = {-# SCC "diagonal" #-} map Data.Monomial.square `lift` p+ corners = {-# SCC "corners" #-} uncurry (+ zipWith (*=)+ ) $ map MkPolynomial . init {-remove terminal null-} . Data.List.tails . tail &&& map Data.Monomial.double $ getMonomialList p++-- | Defines the ability to divide /polynomials/.+instance (+ Eq c,+ Fractional c,+ Num e,+ Ord e+ ) => Data.QuotientRing.QuotientRing (Polynomial c e) where+{-+ Uses /Euclidian division/.+ <http://en.wikipedia.org/wiki/Polynomial_long_division>.+ <http://demonstrations.wolfram.com/PolynomialLongDivision/>.+-}+ _ `quotRem'` MkPolynomial [] = error "Factory.Data.Polynomial.quotRem':\tzero denominator."+ polynomialN `quotRem'` polynomialD = longDivide polynomialN where+-- longDivide :: (Fractional c, Num e, Ord e) => Polynomial c e -> (Polynomial c e, Polynomial c e)+ longDivide (MkPolynomial []) = (zero, zero) -- Exactly divides.+ longDivide numerator+ | Data.Monomial.getExponent quotient < 0 = (zero, numerator) -- Indivisible remainder.+ | otherwise = Control.Arrow.first (lift (quotient :)) $ longDivide (numerator =-= polynomialD *= quotient )+ where+-- quotient :: (Fractional c, Num e) => Data.Monomial.Monomial c e+ quotient = getLeadingTerm numerator </> getLeadingTerm polynomialD++{- |+ * Transforms the data behind the constructor.++ * CAVEAT: similar to 'Data.Functor.fmap', but 'Polynomial' isn't an instance of 'Data.Functor.Functor' since we may want to operate on both /type-parameters/.++ * CAVEAT: the caller is required to re-'normalise' the resulting polynomial depending on the nature of the transformation of the data.+-}+lift :: (MonomialList c1 e1 -> MonomialList c2 e2) -> Polynomial c1 e1 -> Polynomial c2 e2+lift transform = MkPolynomial . transform . getMonomialList++-- | Returns the number of non-zero terms in the polynomial.+terms :: Polynomial c e -> Int+terms (MkPolynomial l) = length l++-- | Return the highest-degree monomial.+getLeadingTerm :: Polynomial c e -> Data.Monomial.Monomial c e+getLeadingTerm (MkPolynomial []) = error "Factory.Data.Polynomial.getLeadingTerm:\tzero polynomial has no leading term."+getLeadingTerm (MkPolynomial (m : _)) = m++-- | Removes terms with a /coefficient/ of zero.+pruneCoefficients :: (Eq c, Num c) => Polynomial c e -> Polynomial c e+pruneCoefficients (MkPolynomial []) = zero+pruneCoefficients p = filter ((/= 0) . Data.Monomial.getCoefficient) `lift` p++-- | Sorts into /descending order/ of exponents, groups /like/ exponents, and calls 'pruneCoefficients'.+normalise :: (Eq c, Num c, Ord e) => Polynomial c e -> Polynomial c e+normalise = pruneCoefficients . lift (+ map (+ foldr ((+) . Data.Monomial.getCoefficient) 0 &&& Data.Monomial.getExponent . head+ ) . Data.List.groupBy (=~) . Data.List.sortBy (flip (<=>))+ )++-- | Constructs an arbitrary /zeroeth-degree polynomial/, ie. independent of the /indeterminate/.+mkConstant :: (Eq c, Num c, Num e) => c -> Polynomial c e+mkConstant 0 = zero+mkConstant c = MkPolynomial [(c, 0)]++-- | Constructs an arbitrary /first-degree polynomial/.+mkLinear :: (Eq c, Num c, Num e)+ => c -- ^ Gradient.+ -> c -- ^ Constant.+ -> Polynomial c e+mkLinear m c = pruneCoefficients $ MkPolynomial [(m, 1), (c, 0)]++-- | Smart constructor. Constructs an arbitrary /polynomial/.+mkPolynomial :: (Eq c, Num c, Ord e) => MonomialList c e -> Polynomial c e+mkPolynomial [] = zero+mkPolynomial l = normalise $ MkPolynomial l++-- | Constructs a /polynomial/ with zero terms.+zero :: Polynomial c e+zero = MkPolynomial []++-- | Constructs a constant /monomial/, independent of the /indeterminate/.+one :: (Eq c, Num c, Num e) => Polynomial c e+one = mkConstant 1++-- | True if all /exponents/ are in the order defined by the specified comparator.+inOrder :: (e -> e -> Bool) -> Polynomial c e -> Bool+inOrder comparator p+ | any ($ p) [isZero, isMonomial] = True+ | otherwise = and . uncurry (zipWith comparator) . (init &&& tail) . map Data.Monomial.getExponent $ getMonomialList p++-- | True if the /exponents/ of successive terms are in /ascending/ order.+inAscendingOrder :: Ord e => Polynomial c e -> Bool+inAscendingOrder = inOrder (<=)++-- | True if the /exponents/ of successive terms are in /descending/ order.+inDescendingOrder :: Ord e => Polynomial c e -> Bool+inDescendingOrder = inOrder (>=)++-- | True if no term has a /coefficient/ of zero.+isReduced :: (Eq c, Num c) => Polynomial c e -> Bool+isReduced = all ((/= 0) . Data.Monomial.getCoefficient) . getMonomialList++-- | True if no term has a /coefficient/ of zero and the /exponents/ of successive terms are in /descending/ order.+isNormalised :: (Eq c, Num c, Ord e) => Polynomial c e -> Bool+isNormalised polynomial = all ($ polynomial) [isReduced, inDescendingOrder]++{- |+ * 'True' if the /leading coefficient/ is one.++ * <http://en.wikipedia.org/wiki/Monic_polynomial#Classifications>.+-}+isMonic :: (Eq c, Num c) => Polynomial c e -> Bool+isMonic (MkPolynomial []) = False -- All coefficients are zero, and have therefore been removed.+isMonic p = (== 1) . Data.Monomial.getCoefficient $ getLeadingTerm p++-- | True if there are zero terms.+isZero :: Polynomial c e -> Bool+isZero (MkPolynomial []) = True+isZero _ = False++-- | True if there's exactly one term.+isMonomial :: Polynomial c e -> Bool+isMonomial (MkPolynomial []) = True+isMonomial _ = False++-- | True if all /exponents/ are /positive/ integers as required.+isPolynomial :: Integral e => Polynomial c e -> Bool+isPolynomial = all Data.Monomial.isMonomial . getMonomialList++{- |+ * 'True' if the two specified /polynomials/ are /congruent/ in /modulo/-arithmetic.++ * <http://planetmath.org/encyclopedia/PolynomialCongruence.html>.+-}+areCongruentModulo :: (Integral c, Num e, Ord e)+ => Polynomial c e -- ^ LHS.+ -> Polynomial c e -- ^ RHS.+ -> c -- ^ Modulus.+ -> Bool+areCongruentModulo _ _ 0 = error "Factory.Data.Polynomial.areCongruentModulo:\tzero modulus."+areCongruentModulo _ _ 1 = True+areCongruentModulo l r modulus+ | l == r = True+ | otherwise = all ((== 0) . (`mod` modulus) . Data.Monomial.getCoefficient) . getMonomialList $ l =-= r++{- |+ * Return the /degree/ (AKA /order/) of the /polynomial/.++ * <http://en.wikipedia.org/wiki/Degree_of_a_polynomial>.++ * <http://mathworld.wolfram.com/PolynomialDegree.html>.+-}+getDegree :: Num e => Polynomial c e -> e+getDegree (MkPolynomial []) = -1 -- CAVEAT: debatable, but makes some operations more robust and consistent.+getDegree p = Data.Monomial.getExponent $ getLeadingTerm p++{- |+ * Scale-up the specified /polynomial/ by a constant /monomial/ factor.++ * <http://en.wikipedia.org/wiki/Scalar_multiplication>.+-}+(*=) :: (Eq c, Num c, Num e) => Polynomial c e -> Data.Monomial.Monomial c e -> Polynomial c e+polynomial *= monomial+ | Data.Monomial.getCoefficient monomial == 1 = map (`Data.Monomial.shiftExponent` Data.Monomial.getExponent monomial) `lift` polynomial+ | otherwise = map (monomial <*>) `lift` polynomial++{- |+ * Raise a /polynomial/ to the specified positive integral power, but using /modulo/-arithmetic.++ * Whilst one could naively implement this as @(x Data.Ring.=^ n) `mod` m@, this will result in arithmetic operatons on unnecessarily big integers.+-}+raiseModulo :: (Integral c, Integral power, Num e, Ord e, Show power)+ => Polynomial c e -- ^ The base.+ -> power -- ^ The exponent to which the base should be raised.+ -> c -- ^ The modulus.+ -> Polynomial c e -- ^ The result.+raiseModulo _ _ 0 = error "Factory.Data.Polynomial.raiseModulo:\tzero modulus."+raiseModulo _ _ 1 = zero+raiseModulo _ 0 modulus = mkConstant $ 1 `mod` modulus+raiseModulo polynomial power modulus+ | power < 0 = error $ "Factory.Data.Polynomial.raiseModulo:\tthe result isn't guaranteed to be a polynomial, for power=" ++ show power+ | first `elem` [zero, one] = first -- Eg 'raiseModulo (mkPolynomial [(3,1)]) 100 3' or 'raiseModulo (mkPolynomial [(3,1),(1,0)]) 100 3'.+ | otherwise = slave power+ where+-- first :: Integral c => Polynomial c e+ first = polynomial `mod'` modulus++-- slave :: (Integral c, Integral power, Num e, Ord e) => power -> Polynomial c e+ slave 1 = first+ slave n = (`mod'` modulus) . (if r == 0 {-even-} then id else (polynomial =*=)) . Data.Ring.square $ slave q {-recurse-} where+ (q, r) = n `quotRem` 2++-- | Reduces all the coefficients using /modular/ arithmetic.+mod' :: Integral c+ => Polynomial c e+ -> c -- ^ Modulus.+ -> Polynomial c e+mod' p modulus = pruneCoefficients $ map (`Data.Monomial.mod'` modulus) `lift` p++{- |+ * Evaluate the /polynomial/ at a specific /indeterminate/.++ * CAVEAT: requires positive exponents; but it wouldn't really be a /polynomial/ otherwise.++ * If the /polynomial/ is very sparse, this may be inefficient,+ since it /memoizes/ the complete sequence of powers up to the polynomial's /degree/.+-}+evaluate :: (Num n, Integral e, Show e)+ => n -- ^ The /indeterminate/.+ -> Polynomial n e+ -> n -- ^ The Result.+evaluate x = foldr ((+) . raise) 0 . getMonomialList where+ powers = iterate (* x) 1++ raise monomial+ | exponent' < 0 = error $ "Factory.Data.Polynomial.evaluate.raise:\tnegative exponent; " ++ show exponent'+ | otherwise = Data.Monomial.getCoefficient monomial * Data.List.genericIndex powers exponent'+ where+ exponent' = Data.Monomial.getExponent monomial++-- | Convert the type of the /coefficient/s.+realCoefficientsToFrac :: (Real r, Fractional f) => Polynomial r e -> Polynomial f e+realCoefficientsToFrac = lift (Data.Monomial.realCoefficientToFrac `map`)+
+ src-lib/Factory/Data/PrimeFactors.hs view
@@ -0,0 +1,143 @@+{-+ Copyright (C) 2011 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@]++ * Describes a list of /prime factors/.++ * The product of this list of prime-factors represents the /composite/ integer from which they were originally extracted.+-}++module Factory.Data.PrimeFactors(+-- * Types+-- ** Type-synonyms+ Factors,+-- * Functions+ insert',+-- invert,+ product',+ reduce,+-- reduceSorted,+-- sumExponents,+-- ** Operators+ (>*<),+ (>/<),+ (>^)+) where++import qualified Control.Arrow+import Control.Arrow((&&&))+import qualified Data.List+import qualified Data.Ord+import qualified Factory.Math.DivideAndConquer as Math.DivideAndConquer+import qualified Factory.Data.Exponential as Data.Exponential+import Factory.Data.Exponential((<^), (=~))+import qualified ToolShed.Data.List++infixl 7 >/<, >*< -- Same as (/).+infixr 8 >^ -- Same as (^).++{- |+ * Each element of this list represents one /prime-factor/, expressed as an /exponential/ with a /prime/ base, of the original integer.++ * Whilst it only makes sense for both the /base/ and /exponent/ to be integral, these constrains are applied at the function-level as required.+-}+type Factors base exponent = [Data.Exponential.Exponential base exponent]++{- |+ * Sorts a list representing a product of /prime factors/ by increasing /base/.++ * Multiplies 'Data.Exponential.Exponential's of similar /base/.+-}+reduce :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent+reduce = reduceSorted . Data.List.sort {-primarily by base-}++-- | Multiplies 'Data.Exponential.Exponential's of similar /base/.+reduceSorted :: (Eq base, Num exponent) => Factors base exponent -> Factors base exponent+-- reduceSorted = map (Data.Exponential.getBase . head &&& sumExponents) . Data.List.groupBy (=~) -- Slow+reduceSorted [] = []+reduceSorted (x : xs)+ | null matched = x : reduceSorted remainder+ | otherwise = Control.Arrow.second (+ sumExponents matched) x : reduceSorted remainder+ where+ (matched, remainder) = span (=~ x) xs++{- |+ * Insert a 'Data.Exponential.Exponential', into a list representing a product of /prime factors/, multiplying with any incumbent of like /base/.++ * The list should be sorted by increasing /base/.++ * Preserves the sort-order.++ * CAVEAT: this is tolerably efficient for sporadic insertion; to insert a list, use '>*<'.+-}+insert' :: (Ord base, Num exponent) => Data.Exponential.Exponential base exponent -> Factors base exponent -> Factors base exponent+insert' e [] = [e]+insert' e l@(x : xs) = case Data.Ord.comparing Data.Exponential.getBase e x of+ LT -> e : l+ GT -> x : insert' e xs -- Recurse.+ _ -> Control.Arrow.second (+ Data.Exponential.getExponent e) x : xs -- Multiply by adding exponents.++{- |+ * Multiplies two lists each representing a product of /prime factors/, and sorted by increasing /base/.++ * Preserves the sort-order.+-}+(>*<) :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent -> Factors base exponent+l >*< r = reduceSorted $ ToolShed.Data.List.merge l r++-- | Invert the product of a list /prime factors/, by negating each of the /exponents/.+invert :: Num exponent => Factors base exponent -> Factors base exponent+invert = map Data.Exponential.invert++{- |+ * Divides two lists, each representing a product of /prime factors/, and sorted by increasing /base/.++ * Preserves the sort-order.+-}+(>/<) :: (Integral base, Integral exponent)+ => Factors base exponent -- ^ The list of /prime factors/ in the /numerator/.+ -> Factors base exponent -- ^ The list of /prime factors/ in the /denominator/.+ -> (Factors base exponent, Factors base exponent) -- ^ The ratio of /numerator/ and /denominator/, after like /prime factors/ are cancelled.+numerator >/< denominator = filter (+ (> 0) . Data.Exponential.getExponent+ ) &&& invert . filter (+ (< 0) . Data.Exponential.getExponent+ ) $ numerator >*< invert denominator++{- |+ * Raise the product of a list /prime factors/ to the specified power.++ * CAVEAT: this merely involves raising each element to the specified power; cf. raising a /polynomial/ to a power.+-}+(>^) :: Num exponent => Factors base exponent -> exponent -> Factors base exponent+factors >^ power = map (<^ power) factors++-- | Sum the /exponents/ of the specified list; as required to multiply exponentials with identical /base/.+sumExponents :: Num exponent => Factors base exponent -> exponent+sumExponents = foldr ((+) . Data.Exponential.getExponent) 0++-- | Multiply a list of /prime factors/.+product' :: (Num base, Integral exponent)+ => Math.DivideAndConquer.BisectionRatio+ -> Math.DivideAndConquer.MinLength+ -> Factors base exponent -- ^ The list on which to operate.+ -> base -- ^ The result.+product' bisectionRatio minLength = Math.DivideAndConquer.product' bisectionRatio minLength . map Data.Exponential.evaluate+
+ src-lib/Factory/Data/PrimeWheel.hs view
@@ -0,0 +1,198 @@+{-+ Copyright (C) 2011 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 a /prime-wheel/, for use in prime-number generation; <http://en.wikipedia.org/wiki/Wheel_factorization>.+-}++module Factory.Data.PrimeWheel(+-- * Types+-- ** Type-synonyms+ Distance,+ NPrimes,+ PrimeMultiples,+-- Repository,+-- ** Data-types+ PrimeWheel(getPrimeComponents, getSpokeGaps),+-- * Functions+ estimateOptimalSize,+-- findCoprimes,+ generateMultiples,+ roll,+ rotate,+-- ** Constructors+ mkPrimeWheel,+-- ** Query+ getCircumference,+ getSpokeCount+) where++import Control.Arrow((&&&), (***))+import qualified Data.IntMap+import qualified Data.List++{- |+ * A conceptual /wheel/, with irregularly spaced spokes; <http://www.haskell.org/haskellwiki/Prime_numbers_miscellaneous#Prime_Wheels>.++ * On being rolled, the trace of the spokes, identifies candidates which are /coprime/ to those primes from which the /wheel/ was composed.++ * One can alternatively view this as a set of vertical nested rings, each with a /prime circumference/, and touching at its lowest point.+ Each has a single mark on its /circumference/, which when rolled identifies multiples of that /circumference/.+ When the complete set is rolled, from the state where all marks are coincident, all multiples of the set of primes, are traced.++ * CAVEAT: The distance required to return to this state (the wheel's /circumference/), grows rapidly with the number of primes:++> zip [0 ..] . scanl (*) 1 $ [2,3,5,7,11,13,17,19,23,29,31]+> [(0,1),(1,2),(2,6),(3,30),(4,210),(5,2310),(6,30030),(7,510510),(8,9699690),(9,223092870),(10,6469693230),(11,200560490130)]++ * The number of spokes also grows rapidly with the number of primes:++> zip [0 ..] . scanl (*) 1 . map pred $ [2,3,5,7,11,13,17,19,23,29,31]+> [(0,1),(1,1),(2,2),(3,8),(4,48),(5,480),(6,5760),(7,92160),(8,1658880),(9,36495360),(10,1021870080),(11,30656102400)]+-}+data PrimeWheel i = MkPrimeWheel {+ getPrimeComponents :: [i], -- ^ Accessor: the ordered sequence of initial primes, from which the /wheel/ was composed.+ getSpokeGaps :: [i] -- ^ Accessor: the sequence of spoke-gaps, the sum of which equals its /circumference/.+} deriving Show++-- | The /circumference/ of the specified 'PrimeWheel'.+getCircumference :: Integral i => PrimeWheel i -> i+getCircumference = product . getPrimeComponents++-- | The number of spokes in the specified 'PrimeWheel'.+getSpokeCount :: Integral i => PrimeWheel i -> i+getSpokeCount = foldr ((*) . pred) 1 . getPrimeComponents++-- | An infinite increasing sequence, of the multiples of a specific prime.+type PrimeMultiples i = [i]++-- | Defines a container for the 'PrimeMultiples'.+type Repository = Data.IntMap.IntMap (PrimeMultiples Int)++-- | The size of the /wheel/, measured by the number of primes from which it is composed.+type NPrimes = Int++{- |+ * Uses a /Sieve of Eratosthenes/ (<http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>), to generate an initial sequence of primes.++ * Also generates an infinite sequence of candidate primes, each of which is /coprime/ to the primes just found, e.g.:+ @filter ((== 1) . (gcd (2 * 3 * 5 * 7))) [11 ..] = [11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121 ..]@; NB /121/ isn't prime.++ * CAVEAT: the use, for efficiency, of "Data.IntMap", limits the maximum bound of this sequence, though not to a significant extent.+-}+findCoprimes :: NPrimes -> ([Int], [Int])+findCoprimes 0 = ([], [])+findCoprimes required+ | required < 0 = error $ "Factory.Data.PrimeWheel.findCoprimes: invalid number of coprimes; " ++ show required+ | otherwise = splitAt required $ 2 : sieve 3 0 Data.IntMap.empty+ where+ sieve :: Int -> NPrimes -> Repository -> [Int]+ sieve candidate found repository = case Data.IntMap.lookup candidate repository of+ Just primeMultiples -> sieve' found . insertUniq primeMultiples $ Data.IntMap.delete candidate repository -- Re-insert subsequent multiples.+ Nothing {-prime-} -> let+ found' = succ found+ (key : values) = iterate (+ gap * candidate) $ candidate ^ (2 :: Int) -- Generate a sequence of prime-multiples, starting from its square.+ in candidate : sieve' found' (+ if found' >= required+ then repository+ else Data.IntMap.insert key values repository+ )+ where+ gap :: Int+ gap = 2 -- For efficiency, only sieve odd integers.++ sieve' :: NPrimes -> Repository -> [Int]+ sieve' = sieve $ candidate + gap -- Tail-recurse.++ insertUniq :: PrimeMultiples Int -> Repository -> Repository+ insertUniq l m = insert $ dropWhile (`Data.IntMap.member` m) l where+ insert :: PrimeMultiples Int -> Repository+ insert [] = error "Factory.Data.PrimeWheel.findCoprimes.sieve.insertUniq.insert:\tnull list"+ insert (key : values) = Data.IntMap.insert key values m+{- |+ * The optimal number of low primes from which to build the /wheel/, grows with the number of primes required;+ the /circumference/ should be approximately the /square-root/ of the number of integers it will be required to sieve.++ * CAVEAT: one greater than this is returned, which empirically seems better.+-}+estimateOptimalSize :: Integral i => i -> NPrimes+estimateOptimalSize maxPrime = succ . length . takeWhile (<= optimalCircumference) . scanl1 (*) {-circumference-} . map fromIntegral {-prevent overflow-} . fst {-primes-} $ findCoprimes 10 {-arbitrary maximum bound-} where+ optimalCircumference :: Integer+ optimalCircumference = round (sqrt $ fromIntegral maxPrime :: Double)++{- |+ Smart constructor for a /wheel/ from the specified number of low primes.++ * The optimal number of low primes from which to build the /wheel/, grows with the number of primes required;+ the /circumference/ should be approximately the /square-root/ of the number of integers it will be required to sieve.++ * The sequence of gaps between spokes on the /wheel/ is /symmetrical under reflection/;+ though two values lie /on/ the axis, that aren't part of this symmetry. Eg:++> nPrimes Gaps+> ====== ====+> 0 [1]+> 1 [2] -- The terminal gap for all subsequent wheels is '2'; [(succ circumference `mod` circumference) - (pred circumference `mod` circumference)].+> 2 [4,2] -- Both points are on the axis, so the symmetry isn't yet clear.+> 3 [6,4,2,4,2,4,6,2]+> 4 [10,2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2]++ Exploitation of this property has proved counter-productive, probably because it requires /strict evaluation/,+ exposing the user to the full cost of inadvertently choosing a /wheel/, which in practice, is rotated less than once.+-}+mkPrimeWheel :: Integral i => NPrimes -> PrimeWheel i+mkPrimeWheel 0 = MkPrimeWheel [] [1]+mkPrimeWheel nPrimes+ | nPrimes < 0 = error $ "Factory.Data.PrimeWheel.mkPrimeWheel: unable to construct from " ++ show nPrimes ++ " primes"+ | otherwise = primeWheel+ where+ (primeComponents, coprimeCandidates) = (map fromIntegral *** map fromIntegral . Data.List.genericTake (getSpokeCount primeWheel)) $ findCoprimes nPrimes+ primeWheel = MkPrimeWheel primeComponents $ zipWith (-) coprimeCandidates $ 1 : coprimeCandidates -- Measure the gaps between candidate primes.++-- | Couples a candidate prime with a /rolling wheel/, to define the distance rolled.+type Distance i = (i, [i])++-- | Generates a new candidate prime, from a /rolling wheel/, and the current candidate.+rotate :: Integral i => Distance i -> Distance i+rotate (candidate, rollingWheel) = (candidate +) . head &&& tail $ rollingWheel++{-# INLINE rotate #-}++-- | Generate an infinite, increasing sequence of candidate primes, from the specified /wheel/.+roll :: Integral i => PrimeWheel i -> [Distance i]+roll primeWheel = tail $ iterate rotate (1, cycle $ getSpokeGaps primeWheel)++{- |+ * Generates multiples of the specified prime, starting from its /square/,+ skipping those multiples of the low primes from which the specified 'PrimeWheel' was composed,+ and which therefore, the /wheel/ won't generate as candidates. Eg:++> Prime Rotating PrimeWheel 3 Output+> ===== ===================== ======+> 7 [4,2,4,2,4,6,2,6] [49,77,91,119,133,161,203,217,259 ..]+> 11 [2,4,2,4,6,2,6,4] [121,143,187,209,253,319,341,407 ..]+> 13 [4,2,4,6,2,6,4,2] [169,221,247,299,377,403,481,533,559 ..]+-}+generateMultiples :: Integral i+ => i -- ^ The number to square and multiply+ -> [i] -- ^ A /rolling wheel/, the track of which, delimits the gaps between /coprime/ candidates.+ -> [i]+generateMultiples i = scanl (\accumulator -> (+ accumulator) . (* i)) (i ^ (2 :: Int))++{-# INLINE generateMultiples #-}+
+ src-lib/Factory/Data/QuotientRing.hs view
@@ -0,0 +1,79 @@+{-+ Copyright (C) 2011 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@]++ * Describes a /Quotient Ring/; <http://en.wikipedia.org/wiki/Quotient_ring>.++ * This is a /ring/ composed from a residue-class resulting from /modular/ division.+-}++module Factory.Data.QuotientRing(+-- * Type-classes+ QuotientRing(..),+-- * Functions+ quot',+ rem',+-- ** Predicates+ areCongruentModulo,+ isDivisibleBy+) where++import Factory.Data.Ring((=-=))+import qualified Factory.Data.Ring as Data.Ring++-- | Defines a sub-class of 'Data.Ring.Ring', in which division is implemented.+class Data.Ring.Ring q => QuotientRing q where+ quotRem' :: q -> q -> (q, q) -- ^ Divides the first operand by the second, to yield a pair composed from the /quotient/ and the /remainder/.++-- | Returns the /quotient/, after division of the two specified 'QuotientRing's.+quot' :: QuotientRing q+ => q -- ^ Numerator.+ -> q -- ^ Denominator.+ -> q+quot' numerator = fst . quotRem' numerator++-- | Returns the /remainder/, after division of the two specified 'QuotientRing's.+rem' :: QuotientRing q+ => q -- ^ Numerator.+ -> q -- ^ Denominator.+ -> q+rem' numerator = snd . quotRem' numerator++{- |+ * 'True' if the two specified 'QuotientRing's are /congruent/ in /modulo/-arithmetic, where the /modulus/ is a third 'QuotientRing'.++ * <http://www.usna.edu/Users/math/wdj/book/node74.html>.+-}+areCongruentModulo :: (Eq q, QuotientRing q)+ => q -- ^ LHS.+ -> q -- ^ RHS.+ -> q -- ^ Modulus.+ -> Bool+areCongruentModulo l r modulus+ | l == r = True -- Only required for efficiency.+ | otherwise = (l =-= r) `isDivisibleBy` modulus++-- | True if the second operand /divides/ the first.+isDivisibleBy :: (Eq q, QuotientRing q)+ => q -- ^ Numerator.+ -> q -- ^ Denominator.+ -> Bool+numerator `isDivisibleBy` denominator = rem' numerator denominator == Data.Ring.additiveIdentity+
+ src-lib/Factory/Data/Ring.hs view
@@ -0,0 +1,118 @@+{-+ Copyright (C) 2011 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@]++ * Describes a /ring/ and operations on its members.++ * <http://en.wikipedia.org/wiki/Ring_%28mathematics%29>.++ * <http://www.numericana.com/answer/rings.htm>.+-}++module Factory.Data.Ring(+-- * Type-classes+ Ring(..),+-- * Types+-- ** Data.types+-- Product,+-- Sum,+-- * Functions+ product',+ sum',+-- ** Operators+ (=^)+) where++import qualified Data.Monoid+import qualified Factory.Math.DivideAndConquer as Math.DivideAndConquer++infixl 6 =+= -- Same as (+).+infixl 6 =-= -- Same as (-).+infixl 7 =*= -- Same as (*).+infixr 8 =^ -- Same as (^).++{- |+ * Define both the operations applicable to all members of the /ring/, and its mandatory members.++ * Minimal definition; '=+=', '=*=', 'additiveInverse', 'multiplicativeIdentity', 'additiveIdentity'.+-}+class Ring r where+ (=+=) :: r -> r -> r -- ^ Addition of two members; required to be /commutative/; <http://en.wikipedia.org/wiki/Commutativity>.+ (=*=) :: r -> r -> r -- ^ Multiplication of two members.+ additiveInverse :: r -> r -- ^ The operand required to yield /zero/ under addition; <http://en.wikipedia.org/wiki/Additive_inverse>.+ multiplicativeIdentity :: r -- ^ The /identity/-member under multiplication; <http://mathworld.wolfram.com/MultiplicativeIdentity.html>.+ additiveIdentity :: r -- ^ The /identity/-member under addition (AKA /zero/); <http://en.wikipedia.org/wiki/Additive_identity>.++ (=-=) :: r -> r -> r -- ^ Subtract the two specified /ring/-members.+ l =-= r = l =+= additiveInverse r -- Default implementation.++ square :: r -> r -- ^ Square the ring.+ square r = r =*= r -- Default implementation; there may be a more efficient one.++{- |+ * Raise a /ring/-member to the specified positive integral power.++ * Exponentiation is implemented as a sequence of either squares of, or multiplications by, the /ring/-member;+ <http://en.wikipedia.org/wiki/Exponentiation_by_squaring>.+-}+(=^) :: (+ Eq r,+ Integral power,+ Ring r,+ Show power+ ) => r -> power -> r+_ =^ 0 = multiplicativeIdentity+ring =^ power+ | power < 0 = error $ "Factory.Data.Ring.(=^):\tthe result isn't guaranteed to be a ring-member, for power=" ++ show power+ | ring `elem` [additiveIdentity, multiplicativeIdentity] = ring+ | otherwise = slave power+ where+ slave 1 = ring+ slave n = (if r == 0 {-even-} then id else (=*= ring)) . square $ slave q where+ (q, r) = n `quotRem` 2++-- | Does for 'Ring', what 'Data.Monoid.Product' does for type 'Num', in that it makes it an instance of 'Data.Monoid.Monoid' under multiplication.+newtype Product p = MkProduct {+ getProduct :: p -- ^ Access the polymorphic payload.+} deriving (Read, Show)++instance Ring r => Data.Monoid.Monoid (Product r) where+ mempty = MkProduct multiplicativeIdentity+ MkProduct x `mappend` MkProduct y = MkProduct $ x =*= y++-- | Returns the /product/ of the list of /ring/-members.+product' :: Ring r => Math.DivideAndConquer.BisectionRatio -> Math.DivideAndConquer.MinLength -> [r] -> r+-- product' _ _ = getProduct . Data.Monoid.mconcat . map MkProduct+product' ratio minLength = getProduct . Math.DivideAndConquer.divideAndConquer ratio minLength . map MkProduct++-- | Does for 'Ring', what 'Data.Monoid.Sum' does for type 'Num', in that it makes it an instance of 'Data.Monoid.Monoid' under addition.+newtype Sum s = MkSum {+ getSum :: s -- ^ Access the polymorphic payload.+} deriving (Read, Show)++instance Ring r => Data.Monoid.Monoid (Sum r) where+ mempty = MkSum additiveIdentity+ MkSum x `mappend` MkSum y = MkSum $ x =+= y++-- | Returns the /sum/ of the list of /ring/-members.+sum' :: Ring r => Math.DivideAndConquer.BisectionRatio -> Math.DivideAndConquer.MinLength -> [r] -> r+-- sum' _ _ = getSum . Data.Monoid.mconcat . map MkSum+sum' ratio minLength = getSum . Math.DivideAndConquer.divideAndConquer ratio minLength . map MkSum+
+ src-lib/Factory/Math/ArithmeticGeometricMean.hs view
@@ -0,0 +1,91 @@+{-+ Copyright (C) 2011 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@] Determines the /Arithmetic-geometric mean/; <http://en.wikipedia.org/wiki/Arithmetic-geometric_mean>.+-}++module Factory.Math.ArithmeticGeometricMean(+-- * Types+-- ** Type-synonyms+ ArithmeticMean,+ GeometricMean,+ AGM,+-- * Functions+ convergeToAGM,+ spread,+-- ** Accessors+ getArithmeticMean,+ getGeometricMean,+-- ** Predicates+ isValid+) where++import Control.Arrow((&&&))+import qualified Control.Parallel.Strategies+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot++-- | The type of the /arithmetic mean/; <http://en.wikipedia.org/wiki/Arithmetic_mean>.+type ArithmeticMean = Rational++-- | The type of the /geometric mean/; <http://en.wikipedia.org/wiki/Geometric_mean>.+type GeometricMean = Rational++-- | Encapsulates both /arithmetic/ and /geometric/ means.+type AGM = (ArithmeticMean, GeometricMean)++-- | Accessor.+{-# INLINE getArithmeticMean #-}+getArithmeticMean :: AGM -> ArithmeticMean+getArithmeticMean = fst++-- | Accessor.+{-# INLINE getGeometricMean #-}+getGeometricMean :: AGM -> GeometricMean+getGeometricMean = snd++-- | Returns an infinite list which converges on the /Arithmetic-geometric mean/.+convergeToAGM :: Math.SquareRoot.Algorithmic squareRootAlgorithm => squareRootAlgorithm -> Math.Precision.DecimalDigits -> AGM -> [AGM]+convergeToAGM squareRootAlgorithm decimalDigits agm+ | decimalDigits <= 0 = error $ "Factory.Math.ArithmeticGeometricMean.convergeToAGM:\tinvalid number of decimal digits; " ++ show decimalDigits+ | not $ isValid agm = error $ "Factory.Math.ArithmeticGeometricMean.convergeToAGM:\tboth means must be positive for a real geometric mean; " ++ show agm+ | spread agm == 0 = repeat agm+ | otherwise = let+ simplify :: Rational -> Rational+ simplify = Math.Precision.simplify (pred decimalDigits {-ignore single integral digit-}) -- This makes a gigantic difference to performance.++ findArithmeticMean :: AGM -> ArithmeticMean+ findArithmeticMean = (/ 2) . uncurry (+)++ findGeometricMean :: AGM -> GeometricMean+ findGeometricMean = Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits . uncurry (*)+ in iterate (+ Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq+ ) . (simplify . findArithmeticMean &&& simplify . findGeometricMean)+ ) agm++-- | Returns the bounds within which the 'AGM' has been constrained.+spread :: AGM -> Rational+spread = uncurry (-)++-- | Checks that both /means/ are positive, as required for the /geometric mean/ to be consistently /real/.+isValid :: AGM -> Bool+isValid (a, g) = all (>= 0) [a, g]+
+ src-lib/Factory/Math/DivideAndConquer.hs view
@@ -0,0 +1,122 @@+{-+ Copyright (C) 2011 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@]++ * Provides a polymorphic algorithm, to /unfold/ a list into a tree, to which an /associative binary operator/ is then applied to re-/fold/ the tree to a /scalar/.++ * Implementations of this strategy have been provided for /addition/ and /multiplication/,+ though other associative binary operators, like 'gcd' or 'lcm' could also be used.++ * Where the contents of the list are consecutive, a more efficient implementation is available in /Factory.Data.Interval/.+-}++module Factory.Math.DivideAndConquer(+-- * Types+-- ** Type-synonyms+ BisectionRatio,+ MinLength,+-- * Functions+ divideAndConquer,+ product',+ sum'+) where++import Control.Arrow((***))+import qualified Control.Parallel.Strategies+import qualified Data.Monoid+import qualified Data.Ratio++{- |+ * The ratio of the original list-length at which to bisect.++ * CAVEAT: the value can overflow.+-}+type BisectionRatio = Data.Ratio.Ratio Int++-- | The list-length beneath which to terminate bisection.+type MinLength = Int++{- |+ * Reduces a list to a single scalar encapsulated in a 'Data.Monoid.Monoid',+ using a /divide-and-conquer/ strategy,+ bisecting the list and recursively evaluating each part; <http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm>.++ * By choosing a 'bisectionRatio' other than @(1 % 2)@, the bisection can be made asymmetrical.+ The specified ratio represents the length of the left-hand portion, over the original list-length;+ eg. @(1 % 3)@ results in the first part, half the length of the second.++ * This process of recursive bisection, is terminated beneath the specified minimum list-length,+ after which the /monoid/'s binary operator is directly /folded/ over the list.++ * One can view this as a <http://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>,+ in which the list is exploded into a binary tree-structure+ (each leaf of which contains a list of up to 'minLength' integers, and each node of which contains an associative binary operator),+ and then collapsed to a scalar, by application of the operators.+-}+divideAndConquer :: Data.Monoid.Monoid monoid+ => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.+ -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.+ -> [monoid] -- ^ The list on which to operate.+ -> monoid -- ^ The resulting scalar.+divideAndConquer bisectionRatio minLength l+ | any ($ apportion minLength) [+ (< 1), -- The left-hand list may be null.+ (> pred minLength) -- The right-hand list may be null.+ ] = error $ "Factory.Math.DivideAndConquer.divideAndConquer:\tbisectionRatio='" ++ show bisectionRatio ++ "' is incompatible with minLength=" ++ show minLength ++ "."+ | otherwise = slave (length l) l+ where+ apportion :: Int -> Int+ apportion list = (list * Data.Ratio.numerator bisectionRatio) `div` Data.Ratio.denominator bisectionRatio++ slave len list+ | len <= minLength = Data.Monoid.mconcat list -- Fold the monoid's binary operator over the list.+ | otherwise = uncurry Data.Monoid.mappend . Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rseq Control.Parallel.Strategies.rseq+ ) . (slave cut *** slave (len - cut)) $ splitAt cut list where -- Apply the monoid's binary operator to the two operands resulting from bisection.+ cut = apportion len++{- |+ * Multiplies the specified list of numbers.++ * Since the result can be large, 'divideAndConquer' is used in an attempt to form operands of a similar order of magnitude,+ which creates scope for the use of more efficient multiplication-algorithms.+-}+product' :: Num n+ => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.+ -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.+ -> [n] -- ^ The numbers whose product is required.+ -> n -- ^ The resulting product.+product' bisectionRatio minLength = Data.Monoid.getProduct . divideAndConquer bisectionRatio minLength . map Data.Monoid.Product++{- |+ * Sums the specified list of numbers.++ * Since the result can be large, 'divideAndConquer' is used in an attempt to form operands of a similar order of magnitude,+ which creates scope for the use of more efficient multiplication-algorithms.+ /Multiplication/ is required for the /addition/ of 'Rational' numbers by cross-multiplication;+ this function is unlikely to be useful for other numbers.+-}+sum' :: Num n+ => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.+ -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.+ -> [n] -- ^ The numbers whose sum is required.+ -> n -- ^ The resulting sum.+sum' bisectionRatio minLength = Data.Monoid.getSum . divideAndConquer bisectionRatio minLength . map Data.Monoid.Sum+
+ src-lib/Factory/Math/Factorial.hs view
@@ -0,0 +1,37 @@+{-+ Copyright (C) 2011 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@]++ * Whilst this particular function is the subject of many introductory examples to Haskell,+ the simple algorithms appropriate for that forum, leave a large margin for performance-improvement.+ This module provides the interface for alternative algorithms.++ * <http://mathworld.wolfram.com/Factorial.html>.+-}++module Factory.Math.Factorial(+-- * Type-classes+ Algorithmic(..)+) where++-- | Defines the methods expected of a /factorial/-algorithm.+class Algorithmic algorithm where+ factorial :: (Integral i, Show i) => algorithm -> i -> i+
+ src-lib/Factory/Math/Fibonacci.hs view
@@ -0,0 +1,42 @@+{-+ Copyright (C) 2011 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@] <http://en.wikipedia.org/wiki/Fibonacci_number>.+-}++module Factory.Math.Fibonacci(+-- * Constants+ fibonacci,+ primeIndexedFibonacci+) where++import qualified Data.Numbers.Primes++-- | A constant ordered list of the /Fibonacci/-numbers.+fibonacci :: Integral i => [i]+fibonacci = 0 : scanl (+) 1 fibonacci++{- |+ * The subset of 'fibonacci', /indexed/ by a /prime/-number.++ * <http://primes.utm.edu/glossary/page.php?sort=FibonacciPrime>.+-}+primeIndexedFibonacci :: Integral i => [i]+primeIndexedFibonacci = map (fibonacci !!) Data.Numbers.Primes.primes+
+ src-lib/Factory/Math/Hyperoperation.hs view
@@ -0,0 +1,113 @@+{-+ Copyright (C) 2011 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@] Provides various /hyperoperations/; <http://en.wikipedia.org/wiki/Hyperoperation>.+-}++module Factory.Math.Hyperoperation(+-- * Types+-- ** Type-synonyms+ Base,+ HyperExponent,+-- * Constants+ succession,+ addition,+ multiplication,+ exponentiation,+ tetration,+ pentation,+ hexation,+-- * Functions+ hyperoperation,+ ackermannPeter,+ powerTower,+-- ** Predicates+ areCoincidental+) where++import qualified Data.List++{- |+ * Merely to enhance self-documentation.++ * CAVEAT: whilst it may appear that 'Base' could be non-'Integral', the recursive definition for /hyper-exponents/ above 'tetration', prevents this.+-}+type Base = Integer++{- |+ * Merely to enhance self-documentation.++ * CAVEAT: whilst 'Base' and 'HyperExponent' can be independent types for both 'exponentiation' and 'tetration', they interact for other /hyper-exponents/.+-}+type HyperExponent = Base++succession, addition, multiplication, exponentiation, tetration, pentation, hexation :: Int -- Arbitrarily.+(succession : addition : multiplication : exponentiation : tetration : pentation : hexation : _) = [0 ..]++{- |+ * Returns the /power-tower/ of the specified /base/; <http://mathworld.wolfram.com/PowerTower.html>.++ * A synonym for /tetration/;+ <http://en.wikipedia.org/wiki/Tetration>,+ <http://www.tetration.org/Fractals/Atlas/index.html>.+-}+powerTower :: (Integral base, Integral hyperExponent, Show base) => base -> hyperExponent -> base+powerTower 0 hyperExponent+ | even hyperExponent = 1+ | otherwise = 0+powerTower _ (-1) = 0 -- The only negative hyper-exponent for which there's a consistent result.+powerTower base hyperExponent+ | base < 0 && hyperExponent > 1 = error $ "Factory.Math.Hyperoperation.powerTower:\tundefined for negative base; " ++ show base+ | otherwise = Data.List.genericIndex (iterate (base ^) 1) hyperExponent++-- | The /hyperoperation/-sequence; <http://en.wikipedia.org/wiki/Hyperoperation>.+hyperoperation :: (Integral rank, Show rank) => rank -> Base -> HyperExponent -> Base+hyperoperation rank base hyperExponent+ | rank < fromIntegral succession = error $ "Factory.Math.Hyperoperation.hyperoperation:\tundefined for rank; " ++ show rank+ | hyperExponent < 0 = error $ "Factory.Math.Hyperoperation.hyperoperation:\tundefined for hyper-exponent; " ++ show hyperExponent+ | otherwise = rank ^# hyperExponent+ where+ (^#) :: Integral rank => rank -> HyperExponent -> Base+ r ^# 0 = case r of+ 1 {-addition-} -> base+ 2 {-multiplication-} -> 0+ _ -> 1+ r ^# e = case r of+ 0 {-succession-} -> succ {-fromIntegral-} e+ 1 {-addition-} -> base + {-fromIntegral-} e+ 2 {-multiplication-} -> base * {-fromIntegral-} e+ 3 {-exponentiation-} -> base ^ e+ 4 {-tetration-} -> base `powerTower` e+ _+ | e' == e -> tetration ^# e' -- To which it would otherwise be reduced by laborious recursion.+ | otherwise -> pred r ^# e'+ where+ e' = {-fromIntegral $-} r ^# pred e++-- | The /Ackermann-Peter/-function; <http://en.wikipedia.org/wiki/Ackermann_function#Ackermann_numbers>.+ackermannPeter :: (Integral rank, Show rank) => rank -> HyperExponent -> Base+ackermannPeter rank = (+ negate 3) . hyperoperation rank 2 {-base-} . (+ 3)++-- | True if @hyperoperation base hyperExponent@ has the same value for each specified 'rank'.+areCoincidental :: (Integral rank, Show rank) => Base -> HyperExponent -> [rank] -> Bool+areCoincidental _ _ [] = True+areCoincidental _ _ [_] = True+areCoincidental base hyperExponent ranks = all (== h) hs where+ (h : hs) = map (\rank -> hyperoperation rank base hyperExponent) ranks+
+ src-lib/Factory/Math/Implementations/Factorial.hs view
@@ -0,0 +1,138 @@+{-+ Copyright (C) 2011 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@]++ * Provides implementations of the class 'Math.Factorial.Algorithmic'.++ * Provides additional functions related to /factorials/, but which depends on a specific implementation,+ and which therefore can't be accessed throught the class-interface.++ * <http://en.wikipedia.org/wiki/Factorial>.++ * <http://mathworld.wolfram.com/Factorial.html>.++ * <http://www.luschny.de/math/factorial/FastFactorialFunctions.htm>.+-}++module Factory.Math.Implementations.Factorial(+-- * Types+-- ** Data-types+ Algorithm(..),+-- * Functions+ primeFactors,+-- primeMultiplicity,+ risingFactorial,+ fallingFactorial,+-- ** Operators+ (!/!)+) where++import qualified Data.Numbers.Primes+import qualified Factory.Data.Interval as Data.Interval+import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Factorial as Math.Factorial+import qualified ToolShed.Defaultable++infixl 7 !/! -- Same as (/).++-- | The algorithms by which /factorial/ has been implemented.+data Algorithm =+ Bisection -- ^ The integers from which the /factorial/ is composed, are multiplied using @Data.Interval.product'@.+ | PrimeFactorisation -- ^ The /prime factors/ of the /factorial/ are extracted, then raised to the appropriate power, before multiplication.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = Bisection++instance Math.Factorial.Algorithmic Algorithm where+ factorial algorithm n+ | n < 2 = 1+ | otherwise = case algorithm of+ Bisection -> risingFactorial 2 $ pred n+ PrimeFactorisation -> Data.PrimeFactors.product' (recip 5) {-empirical-} 10 {-empirical-} $ primeFactors n++{- |+ * Returns the /prime factors/, of the /factorial/ of the specifed integer.++ * Precisely all the primes less than or equal to the specified integer /n/, are included in /n!/;+ only the multiplicity of each of these known prime components need be determined.++ * <http://en.wikipedia.org/wiki/Factorial#Number_theory>.++ * CAVEAT: currently a hotspot.+-}+primeFactors :: Integral base+ => base -- ^ The number, whose /factorial/ is to be factorised.+ -> Data.PrimeFactors.Factors base base -- ^ The /base/ and /exponent/ of each /prime factor/ in the /factorial/, ordered by increasing /base/ (and decreasing /exponent/).+primeFactors n = takeWhile ((> 0) . snd) $ map (\prime -> (prime, primeMultiplicity prime n)) Data.Numbers.Primes.primes++{- |+ * The number of times a specific /prime/, can be factored from the /factorial/ of the specified integer.++ * General purpose /prime-factorisation/ has /exponential time-complexity/,+ so use /Legendre's Theorem/, which relates only to the /prime factors/ of /factorials/.++ * <http://www.proofwiki.org/wiki/Multiplicity_of_Prime_Factor_in_Factorial>.+-}+primeMultiplicity :: Integral i+ => i -- ^ A prime number.+ -> i -- ^ The integer, the factorial of which the prime is a factor.+ -> i -- ^ The number of times the prime occurs in the factorial.+primeMultiplicity prime = sum . takeWhile (> 0) . tail . iterate (`div` prime)++-- | Returns the /rising factorial/; <http://mathworld.wolfram.com/RisingFactorial.html>+risingFactorial :: (Integral i, Show i)+ => i -- ^ The lower bound of the integer-range, whose product is returned.+ -> i -- ^ The number of integers in the range above.+ -> i -- ^ The result.+risingFactorial _ 0 = 1+risingFactorial 0 _ = 0+risingFactorial x n = Data.Interval.product' (recip 2) 64 $ Data.Interval.normalise (x, pred $ x + n)++-- | Returns the /falling factorial/; <http://mathworld.wolfram.com/FallingFactorial.html>+fallingFactorial :: (Integral i, Show i)+ => i -- ^ The upper bound of the integer-range, whose product is returned.+ -> i -- ^ The number of integers in the range beneath.+ -> i -- ^ The result.+fallingFactorial _ 0 = 1+fallingFactorial 0 _ = 0+fallingFactorial x n = Data.Interval.product' (recip 2) 64 $ Data.Interval.normalise (x, succ $ x - n)++{- |+ * Returns the ratio of two factorials.++ * It is more efficient than evaluating both factorials, and then dividing.++ * For more complex combinations of factorials, such as in the /Binomial coefficient/,+ extract the /prime factors/ using 'primeFactors'+ then manipulate them using the module "Data.PrimeFactors",+ and evaluate it using by /Data.PrimeFactors.product'/.+-}+(!/!) :: (Integral i, Fractional f, Show i)+ => i -- ^ The /numerator/.+ -> i -- ^ The /denominator/.+ -> f -- ^ The resulting fraction.+numerator !/! denominator+ | numerator <= 1 = recip . fromIntegral $ Math.Factorial.factorial (ToolShed.Defaultable.defaultValue :: Algorithm) denominator+ | denominator <= 1 = fromIntegral $ Math.Factorial.factorial (ToolShed.Defaultable.defaultValue :: Algorithm) numerator+ | numerator == denominator = 1+ | numerator < denominator = recip $ denominator !/! numerator -- Recurse.+ | otherwise = fromIntegral $ Data.Interval.product' (recip 2) 64 (succ denominator, numerator)+
+ src-lib/Factory/Math/Implementations/Pi/AGM/Algorithm.hs view
@@ -0,0 +1,42 @@+{-+ Copyright (C) 2011 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 the set of /Arithmetic-geometric Mean/-type /Pi/-algorithms which have been implemented; currently just one.+-}++module Factory.Math.Implementations.Pi.AGM.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import qualified Factory.Math.Implementations.Pi.AGM.BrentSalamin as Math.Implementations.Pi.AGM.BrentSalamin+import qualified Factory.Math.Pi as Math.Pi+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified ToolShed.Defaultable++-- | Defines the available algorithms.+data Algorithm squareRootAlgorithm = BrentSalamin squareRootAlgorithm deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable squareRootAlgorithm => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm) where+ defaultValue = BrentSalamin ToolShed.Defaultable.defaultValue++instance Math.SquareRoot.Algorithmic squareRootAlgorithm => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm) where+ openR (BrentSalamin squareRootAlgorithm) = Math.Implementations.Pi.AGM.BrentSalamin.openR squareRootAlgorithm+
+ src-lib/Factory/Math/Implementations/Pi/AGM/BrentSalamin.hs view
@@ -0,0 +1,64 @@+{-+ Copyright (C) 2011 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 the /Brent-Salamin/ (AKA /Gauss-Legendre/) algorithm;+ <http://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_algorithm>,+ <http://mathworld.wolfram.com/Brent-SalaminFormula.html>,+ <http://www.pi314.net/eng/salamin.php>.++ * The precision of the result approximately doubles for each iteration.++ [@CAVEAT@] Assumptions on the convergence-rate result in rounding-errors, when only a small number of digits are requested.+-}++module Factory.Math.Implementations.Pi.AGM.BrentSalamin(+-- * Functions+ openR+) where++import Control.Arrow((&&&))+import qualified Factory.Math.ArithmeticGeometricMean as Math.ArithmeticGeometricMean+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot++{- |+ * Returns /Pi/, accurate to the specified number of decimal digits.++ * This algorithm is based on the /arithmetic-geometric/ mean of @1@ and @(1 / sqrt 2)@,+ but there are many confusingly similar formulations.+ The algorithm I've used here, where @a@ is the /arithmetic mean/ and @g@ is the /geometric mean/, is equivalent to other common formulations:++> pi = (a[N-1] + g[N-1])^2 / (1 - sum [2^n * (a[n] - g[n])^2]) where n = [0 .. N-1]+> => 4*a[N]^2 / (1 - sum [2^n * (a[n]^2 - 2*a[n]*g[n] + g[n]^2)])+> => 4*a[N]^2 / (1 - sum [2^n * (a[n]^2 + 2*a[n]*g[n] + g[n]^2 - 4*a[n]*g[n])])+> => 4*a[N]^2 / (1 - sum [2^n * ((a[n] + g[n])^2 - 4*a[n]*g[n])])+> => 4*a[N]^2 / (1 - sum [2^(n-1) * 4 * (a[n-1]^2 - g[n-1]^2)]) where n = [1 .. N]+> => 4*a[N]^2 / (1 - sum [2^(n+1) * (a[n-1]^2 - g[n-1]^2)])++-}+openR :: Math.SquareRoot.Algorithmic squareRootAlgorithm => squareRootAlgorithm -> Math.Precision.DecimalDigits -> Rational+openR squareRootAlgorithm decimalDigits = uncurry (/) . (+ Math.Power.square . uncurry (+) . last &&& negate . pred . sum . zipWith (*) (iterate (* 2) 1) . map (Math.Power.square . Math.ArithmeticGeometricMean.spread)+ ) . take (+ Math.Precision.getIterationsRequired Math.Precision.quadraticConvergence 1 decimalDigits+ ) $ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits (1, Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (recip 2 :: Rational))+
+ src-lib/Factory/Math/Implementations/Pi/BBP/Algorithm.hs view
@@ -0,0 +1,47 @@+{-+ Copyright (C) 2011 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 the set of /Bailey-Borwein-Plouffe/-type formulae which have been implemented.+-}++module Factory.Math.Implementations.Pi.BBP.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import qualified Factory.Math.Implementations.Pi.BBP.Base65536 as Math.Implementations.Pi.BBP.Base65536+import qualified Factory.Math.Implementations.Pi.BBP.Bellard as Math.Implementations.Pi.BBP.Bellard+import qualified Factory.Math.Implementations.Pi.BBP.Implementation as Math.Implementations.Pi.BBP.Implementation+import qualified Factory.Math.Pi as Math.Pi+import qualified ToolShed.Defaultable++-- | Defines those /BBP/-type series which have been implemented.+data Algorithm =+ Base65536 -- ^ A /base/-@2^16@ version of the formula.+ | Bellard -- ^ A /nega-base/ @2^10@ version of the formula.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = Base65536++instance Math.Pi.Algorithmic Algorithm where+ openR Base65536 = Math.Implementations.Pi.BBP.Implementation.openR Math.Implementations.Pi.BBP.Base65536.series+ openR Bellard = Math.Implementations.Pi.BBP.Implementation.openR Math.Implementations.Pi.BBP.Bellard.series+
+ src-lib/Factory/Math/Implementations/Pi/BBP/Base65536.hs view
@@ -0,0 +1,38 @@+{-+ Copyright (C) 2011 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 a specific base-@2^16@ /BBP/-formula; <http://mathworld.wolfram.com/PiFormulas.html>++-}++module Factory.Math.Implementations.Pi.BBP.Base65536(+-- * Constants+ series+) where++import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series++-- | Defines the parameters of this specific series.+series :: Math.Implementations.Pi.BBP.Series.Series+series = Math.Implementations.Pi.BBP.Series.MkSeries {+ Math.Implementations.Pi.BBP.Series.numerators = zipWith ($) (cycle [id, id, id, negate]) $ map (2 ^) [15 :: Integer, 14, 14, 12, 11, 10, 10, 8, 7, 6, 6, 4, 3, 2, 2, 0],+ Math.Implementations.Pi.BBP.Series.getDenominators = \i -> map (32 * fromIntegral i +) [2, 3, 4, 7, 10, 11, 12, 15, 18, 19, 20, 23, 26, 27, 28, 31],+ Math.Implementations.Pi.BBP.Series.seriesScalingFactor = recip $ 2 ^ (13 :: Int),+ Math.Implementations.Pi.BBP.Series.base = 2 ^ (16 :: Int)+}
+ src-lib/Factory/Math/Implementations/Pi/BBP/Bellard.hs view
@@ -0,0 +1,41 @@+{-+ Copyright (C) 2011 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 /Bellard/'s nega-base-@2^10@ /BBP/-formula; <http://en.wikipedia.org/wiki/Bellard%27s_formula>+-}++module Factory.Math.Implementations.Pi.BBP.Bellard(+-- * Constants+ series+) where++import Control.Arrow((&&&))+import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series++-- | Defines the parameters of this specific series.+series :: Math.Implementations.Pi.BBP.Series.Series+series = Math.Implementations.Pi.BBP.Series.MkSeries {+ Math.Implementations.Pi.BBP.Series.numerators = zipWith ($) [negate, negate, id, negate, negate, negate, id] $ map (2 ^) [5 :: Integer, 0, 8, 6, 2, 2, 0],+ Math.Implementations.Pi.BBP.Series.getDenominators = \i -> let+ f, t :: Integer+ (f, t) = (4 *) &&& (10 *) $ fromIntegral i+ in [f + 1, f + 3, t + 1, t + 3, t + 5, t + 7, t + 9],+ Math.Implementations.Pi.BBP.Series.seriesScalingFactor = recip $ 2 ^ (6 :: Int),+ Math.Implementations.Pi.BBP.Series.base = negate $ 2 ^ (10 :: Int)+}
+ src-lib/Factory/Math/Implementations/Pi/BBP/Implementation.hs view
@@ -0,0 +1,57 @@+{-+ Copyright (C) 2011 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 a /Bailey-Borwein-Plouffe/ formula; <http://mathworld.wolfram.com/PiFormulas.html>++ * Surprisingly, because of the huge size of the 'Rational' quantities,+ it is a /single/ call to @Factory.Math.Summation.sum'@, rather than the calculation of the many terms in the series, which is the performance-bottleneck.+-}++module Factory.Math.Implementations.Pi.BBP.Implementation(+-- * Functions+ openR+) where++import Data.Ratio((%))+import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.Summation as Math.Summation++-- | Returns /Pi/, accurate to the specified number of decimal digits.+openR+ :: Math.Implementations.Pi.BBP.Series.Series -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.+ -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.+ -> Rational+openR Math.Implementations.Pi.BBP.Series.MkSeries {+ Math.Implementations.Pi.BBP.Series.numerators = numerators,+ Math.Implementations.Pi.BBP.Series.getDenominators = getDenominators,+ Math.Implementations.Pi.BBP.Series.seriesScalingFactor = seriesScalingFactor,+ Math.Implementations.Pi.BBP.Series.base = base+} decimalDigits = (seriesScalingFactor *) . Math.Summation.sum' 8 . take (+ Math.Precision.getTermsRequired (+ recip . fromIntegral $ abs {-potentially negative-} base -- The convergence-rate.+ ) decimalDigits+ ) . zipWith (*) (+ iterate (/ fromIntegral base) 1 -- Generate the scaling-ratio, between successive terms.+ ) $ map (+ sum . zipWith (%) numerators . getDenominators+ ) [0 ..]+
+ src-lib/Factory/Math/Implementations/Pi/BBP/Series.hs view
@@ -0,0 +1,36 @@+{-+ Copyright (C) 2011 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 a /Bailey-Borwein-Plouffe/ formula; <http://mathworld.wolfram.com/PiFormulas.html>+-}++module Factory.Math.Implementations.Pi.BBP.Series(+-- * Types+-- ** Data-types+ Series(..)+) where++-- | Defines a series corresponding to a specific /BBP/-formula.+data Series = MkSeries {+ numerators :: [Integer], -- ^ The constant numerators from which each term in the series is composed.+ getDenominators :: Int -> [Integer], -- ^ Generates the term-dependent denominators from which each term in the series is composed.+ seriesScalingFactor :: Rational, -- ^ The ratio by which the sum to infinity of the series, must be scaled to result in /Pi/.+ base :: Integer -- ^ The geometric ratio, by which successive terms are scaled.+}+
+ src-lib/Factory/Math/Implementations/Pi/Borwein/Algorithm.hs view
@@ -0,0 +1,56 @@+{-+ Copyright (C) 2011 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 the set of /Borwein/-type algorithms (currently only one) which have been implemented; <http://www.pi314.net/eng/borwein.php>.+-}++module Factory.Math.Implementations.Pi.Borwein.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Pi.Borwein.Borwein1993 as Math.Implementations.Pi.Borwein.Borwein1993+import qualified Factory.Math.Implementations.Pi.Borwein.Implementation as Math.Implementations.Pi.Borwein.Implementation+import qualified Factory.Math.Pi as Math.Pi+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified ToolShed.Defaultable++{- |+ * Define those /Borwein/-series which have been implemented.++ * Though currently there's only one, provision has been made for the addition of more.+-}+data Algorithm squareRootAlgorithm factorialAlgorithm =+ Borwein1993 squareRootAlgorithm factorialAlgorithm -- ^ <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>.+ deriving (Eq, Read, Show)++instance (+ ToolShed.Defaultable.Defaultable squareRootAlgorithm,+ ToolShed.Defaultable.Defaultable factorialAlgorithm+ ) => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm) where+ defaultValue = Borwein1993 ToolShed.Defaultable.defaultValue ToolShed.Defaultable.defaultValue++instance (+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm) where+ openR (Borwein1993 squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Borwein.Implementation.openR Math.Implementations.Pi.Borwein.Borwein1993.series squareRootAlgorithm factorialAlgorithm+
+ src-lib/Factory/Math/Implementations/Pi/Borwein/Borwein1993.hs view
@@ -0,0 +1,73 @@+{-+ Copyright (C) 2011 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 the /Borwein/ series for /Pi/; <http://en.wikipedia.org/wiki/Borwein%27s_algorithm#Jonathan_Borwein_and_Peter_Borwein.27s_Version_.281993.29>+-}++module Factory.Math.Implementations.Pi.Borwein.Borwein1993(+-- * Constants+ series+) where++-- import Control.Arrow((***))+import Data.Ratio((%))+-- import Factory.Data.PrimeFactors((>*<), (>/<), (>^))+-- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Implementations.Pi.Borwein.Series as Math.Implementations.Pi.Borwein.Series+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot++-- | Defines the parameters of the /Borwein/ series.+series :: (Math.SquareRoot.Algorithmic squareRootAlgorithm, Math.Factorial.Algorithmic factorialAlgorithm) => Math.Implementations.Pi.Borwein.Series.Series squareRootAlgorithm factorialAlgorithm+series = Math.Implementations.Pi.Borwein.Series.MkSeries {+ Math.Implementations.Pi.Borwein.Series.terms = \squareRootAlgorithm factorialAlgorithm decimalDigits -> let+ simplify, squareRoot :: Rational -> Rational+ simplify = Math.Precision.simplify $ pred decimalDigits {-ignore single integral digit-} -- This makes a gigantic difference to performance.+ squareRoot = simplify . Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits++ sqrt5, a, b, c3 :: Rational+ sqrt5 = squareRoot 5++ a = 63365028312971999585426220 + sqrt5 * (28337702140800842046825600 + 384 * squareRoot (10891728551171178200467436212395209160385656017 + 4870929086578810225077338534541688721351255040 * sqrt5))+ b = 7849910453496627210289749000 + 3510586678260932028965606400 * sqrt5 + 2515968 * squareRoot (3110 * (6260208323789001636993322654444020882161 + 2799650273060444296577206890718825190235 * sqrt5))+ c3 = simplify . Math.Power.cube $ negate 214772995063512240 - sqrt5 * (96049403338648032 + 1296 * squareRoot (10985234579463550323713318473 + 4912746253692362754607395912 * sqrt5))+ in (+ squareRoot $ negate c3, -- The factor into which the series must be divided, to yield Pi.+ zipWith (+{-+ \n power -> let+ product' = Data.PrimeFactors.product' (recip 2) 10+ in uncurry (/) . (+ (* (a + b * fromIntegral n)) . fromIntegral . product' *** (* power) . fromIntegral . product'+ ) $ Math.Implementations.Factorial.primeFactors (6 * n) >/< (+ Math.Implementations.Factorial.primeFactors (3 * n) >*< Math.Implementations.Factorial.primeFactors n >^ 3+ )+-}+ \n power -> (+ Math.Implementations.Factorial.risingFactorial (succ $ 3 * n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)+ ) * (+ (a + b * fromIntegral n) / power+ )+ ) [0 :: Integer ..] $ iterate (* c3) 1+ ),+ Math.Implementations.Pi.Borwein.Series.convergenceRate = 10 ** negate 50 -- Empirical.+}
+ src-lib/Factory/Math/Implementations/Pi/Borwein/Implementation.hs view
@@ -0,0 +1,50 @@+{-+ Copyright (C) 2011 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 /Borwein/ series for /Pi/; <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>+-}++module Factory.Math.Implementations.Pi.Borwein.Implementation(+-- * Functions+ openR+) where++import qualified Control.Arrow+import qualified Control.Parallel.Strategies+import qualified Factory.Math.Implementations.Pi.Borwein.Series as Math.Implementations.Pi.Borwein.Series+import qualified Factory.Math.Precision as Math.Precision++-- | Returns /Pi/, accurate to the specified number of decimal digits.+openR+ :: Math.Implementations.Pi.Borwein.Series.Series squareRootAlgorithm factorialAlgorithm -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.+ -> squareRootAlgorithm -- ^ The specific /square-root/ algorithm to apply to the above series.+ -> factorialAlgorithm -- ^ The specific /factorial/-algorithm to apply to the above series.+ -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.+ -> Rational+openR Math.Implementations.Pi.Borwein.Series.MkSeries {+ Math.Implementations.Pi.Borwein.Series.terms = terms,+ Math.Implementations.Pi.Borwein.Series.convergenceRate = convergenceRate+} squareRootAlgorithm factorialAlgorithm decimalDigits = uncurry (/) . Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq+ ) . Control.Arrow.second (+ sum . take (+ Math.Precision.getTermsRequired convergenceRate decimalDigits+ )+ ) $ terms squareRootAlgorithm factorialAlgorithm decimalDigits+
+ src-lib/Factory/Math/Implementations/Pi/Borwein/Series.hs view
@@ -0,0 +1,43 @@+{-+ Copyright (C) 2011 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 a <http://en.wikipedia.org/wiki/Srinivasa_Borwein>-type series for /Pi/.+-}++module Factory.Math.Implementations.Pi.Borwein.Series(+-- * Types+-- ** Data-types+ Series(..)+) where++import qualified Factory.Math.Precision as Math.Precision++-- | Defines a series corresponding to a specific /Borwein/-formula.+data Series squareRootAlgorithm factorialAlgorithm = MkSeries {+ terms+ :: squareRootAlgorithm+ -> factorialAlgorithm+ -> Math.Precision.DecimalDigits+ -> (+ Rational, -- The factor into which the sum to infinity of the sequence, must be divided to result in /Pi/+ [Rational] -- The sequence of terms, the sum to infinity of which defines the series.+ ),+ convergenceRate :: Math.Precision.ConvergenceRate -- ^ The expected number of digits of /Pi/, per term in the series.+}+
+ src-lib/Factory/Math/Implementations/Pi/Ramanujan/Algorithm.hs view
@@ -0,0 +1,55 @@+{-+ Copyright (C) 2011 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 the set of /Ramanujan/-type algorithms which have been implemented; <http://en.wikipedia.org/wiki/Pi>.+-}++module Factory.Math.Implementations.Pi.Ramanujan.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Pi.Ramanujan.Chudnovsky as Math.Implementations.Pi.Ramanujan.Chudnovsky+import qualified Factory.Math.Implementations.Pi.Ramanujan.Classic as Math.Implementations.Pi.Ramanujan.Classic+import qualified Factory.Math.Implementations.Pi.Ramanujan.Implementation as Math.Implementations.Pi.Ramanujan.Implementation+import qualified Factory.Math.Pi as Math.Pi+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified ToolShed.Defaultable++-- | Define those /Ramanujan/-series which have been implemented.+data Algorithm squareRootAlgorithm factorialAlgorithm =+ Classic squareRootAlgorithm factorialAlgorithm -- ^ The original version.+ | Chudnovsky squareRootAlgorithm factorialAlgorithm -- ^ A variant found by the /Chudnovsky brothers/.+ deriving (Eq, Read, Show)++instance (+ ToolShed.Defaultable.Defaultable squareRootAlgorithm,+ ToolShed.Defaultable.Defaultable factorialAlgorithm+ ) => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm) where+ defaultValue = Chudnovsky ToolShed.Defaultable.defaultValue ToolShed.Defaultable.defaultValue++instance (+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm) where+ openR (Classic squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Ramanujan.Implementation.openR Math.Implementations.Pi.Ramanujan.Classic.series squareRootAlgorithm factorialAlgorithm+ openR (Chudnovsky squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Ramanujan.Implementation.openR Math.Implementations.Pi.Ramanujan.Chudnovsky.series squareRootAlgorithm factorialAlgorithm+
+ src-lib/Factory/Math/Implementations/Pi/Ramanujan/Chudnovsky.hs view
@@ -0,0 +1,63 @@+{-+ Copyright (C) 2011 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 the /Chudnovsky/ series for /Pi/; <http://en.wikipedia.org/wiki/Pi>.+-}++module Factory.Math.Implementations.Pi.Ramanujan.Chudnovsky(+-- * Constants+ series+) where++-- import Control.Arrow((***))+import Data.Ratio((%))+-- import Factory.Data.PrimeFactors((>/<), (>*<), (>^))+-- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.SquareRoot as Math.SquareRoot++-- | Defines the parameters of the /Chudnovsky/ series.+series :: (+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm+series = Math.Implementations.Pi.Ramanujan.Series.MkSeries {+ Math.Implementations.Pi.Ramanujan.Series.terms = \factorialAlgorithm -> zipWith (+{-+ \n power -> let+ product' = Data.PrimeFactors.product' (recip 2) 10+ in uncurry (%) . (+ (* (13591409 + 545140134 * n)) . product' *** (* power) . product'+ ) $ Math.Implementations.Factorial.primeFactors (6 * n) >/< (+ Math.Implementations.Factorial.primeFactors (3 * n) >*< Math.Implementations.Factorial.primeFactors n >^ 3+ )+-}+ \n power -> (+ Math.Implementations.Factorial.risingFactorial (succ $ 3 * n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)+ ) * (+ (13591409 + 545140134 * n) % power+ ) -- CAVEAT: the order in which these terms are evaluated radically affects performance.+ ) [0 ..] $ iterate (* (Math.Power.cube $ negate 640320 :: Integer)) 1,+ Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = \squareRootAlgorithm decimalDigits -> 426880 * Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (10005 :: Integer),+ Math.Implementations.Pi.Ramanujan.Series.convergenceRate = 10 ** negate 14.0 -- Empirical.+}+
+ src-lib/Factory/Math/Implementations/Pi/Ramanujan/Classic.hs view
@@ -0,0 +1,60 @@+{-+ Copyright (C) 2011 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 the /Ramanujan/ series for /Pi/; <http://planetmath.org/encyclopedia/RamanujansFormulaForPi.html>.+-}++module Factory.Math.Implementations.Pi.Ramanujan.Classic(+-- * Constants+ series+) where++-- import Control.Arrow((***))+import Data.Ratio((%))+-- import Factory.Data.PrimeFactors((>/<), (>^))+-- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.SquareRoot as Math.SquareRoot++-- | Defines the parameters of the /Ramanujan/ series.+series :: (Math.SquareRoot.Algorithmic squareRootAlgorithm, Math.Factorial.Algorithmic factorialAlgorithm) => Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm+series = Math.Implementations.Pi.Ramanujan.Series.MkSeries {+ Math.Implementations.Pi.Ramanujan.Series.terms = \factorialAlgorithm -> let+ toFourthPower = (^ (4 :: Int))+ in zipWith (+{-+ \n power -> let+ product' = Data.PrimeFactors.product' (recip 2) 10+ in uncurry (%) . (+ (* (1103 + 26390 * n)) . product' *** (* power) . product'+ ) $ Math.Implementations.Factorial.primeFactors (4 * n) >/< Math.Implementations.Factorial.primeFactors n >^ 4+-}+ \n power -> (+ Math.Implementations.Factorial.risingFactorial (succ n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)+ ) * (+ (1103 + 26390 * n) % power+ ) -- CAVEAT: the order in which these terms are evaluated radically affects performance.+ ) [0 ..] $ iterate (* toFourthPower 396) 1,+ Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = \squareRootAlgorithm decimalDigits -> 9801 / Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (8 :: Integer),+ Math.Implementations.Pi.Ramanujan.Series.convergenceRate = 10 ** negate 7.9 -- Empirical.+}+
+ src-lib/Factory/Math/Implementations/Pi/Ramanujan/Implementation.hs view
@@ -0,0 +1,52 @@+{-+ Copyright (C) 2011 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 a /Ramanujan/-type series for /Pi/; <http://en.wikipedia.org/wiki/Srinivasa_Ramanujan>.+-}++module Factory.Math.Implementations.Pi.Ramanujan.Implementation(+-- * Functions+ openR+) where++import qualified Control.Parallel.Strategies+import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.Summation as Math.Summation++-- | Returns /Pi/, accurate to the specified number of decimal digits.+openR+ :: Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.+ -> squareRootAlgorithm -- ^ The specific /square-root/ algorithm to apply to the above series.+ -> factorialAlgorithm -- ^ The specific /factorial/-algorithm to apply to the above series.+ -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.+ -> Rational+openR Math.Implementations.Pi.Ramanujan.Series.MkSeries {+ Math.Implementations.Pi.Ramanujan.Series.terms = terms,+ Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = getSeriesScalingFactor,+ Math.Implementations.Pi.Ramanujan.Series.convergenceRate = convergenceRate+} squareRootAlgorithm factorialAlgorithm decimalDigits = uncurry (/) $ Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq+ ) (+ getSeriesScalingFactor squareRootAlgorithm decimalDigits,+ Math.Summation.sumR 64 . take (+ Math.Precision.getTermsRequired convergenceRate decimalDigits+ ) $ terms factorialAlgorithm+ ) -- Pair.+
+ src-lib/Factory/Math/Implementations/Pi/Ramanujan/Series.hs view
@@ -0,0 +1,37 @@+{-+ Copyright (C) 2011 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 a <http://en.wikipedia.org/wiki/Srinivasa_Ramanujan>-type series for /Pi/.+-}++module Factory.Math.Implementations.Pi.Ramanujan.Series(+-- * Types+-- ** Data-types+ Series(..)+) where++import qualified Factory.Math.Precision as Math.Precision++-- | Defines a series corresponding to a specific /Ramanujan/-formula.+data Series squareRootAlgorithm factorialAlgorithm = MkSeries {+ terms :: factorialAlgorithm -> [Rational], -- ^ The sequence of terms, the sum to infinity of which defines the series.+ getSeriesScalingFactor :: squareRootAlgorithm -> Math.Precision.DecimalDigits -> Rational, -- ^ The ratio by which the sum to infinity of the sequence, must be scaled to result in /Pi/.+ convergenceRate :: Math.Precision.ConvergenceRate -- ^ The expected number of digits of /Pi/, per term in the series.+}+
+ src-lib/Factory/Math/Implementations/Pi/Spigot/Algorithm.hs view
@@ -0,0 +1,50 @@+{-+ Copyright (C) 2011 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 the set of /Spigot/-algorithms which have been implemented.+-}++module Factory.Math.Implementations.Pi.Spigot.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import Data.Ratio((%))+import qualified Factory.Math.Implementations.Pi.Spigot.Gosper as Math.Implementations.Pi.Spigot.Gosper+import qualified Factory.Math.Implementations.Pi.Spigot.RabinowitzWagon as Math.Implementations.Pi.Spigot.RabinowitzWagon+import qualified Factory.Math.Implementations.Pi.Spigot.Spigot as Math.Implementations.Pi.Spigot.Spigot+import qualified Factory.Math.Pi as Math.Pi+import qualified ToolShed.Defaultable++-- | Define those /Spigot/-algorithms which have been implemented.+data Algorithm =+ Gosper -- ^ A /continued fraction/ discovered by /Gosper/.+ | RabinowitzWagon -- ^ A /continued fraction/ discovered by /Rabinowitz/ and /Wagon/.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = Gosper++instance Math.Pi.Algorithmic Algorithm where+ openI Gosper = Math.Implementations.Pi.Spigot.Spigot.openI Math.Implementations.Pi.Spigot.Gosper.series+ openI RabinowitzWagon = Math.Implementations.Pi.Spigot.Spigot.openI Math.Implementations.Pi.Spigot.RabinowitzWagon.series++ openR algorithm decimalDigits = Math.Pi.openI algorithm decimalDigits % (10 ^ pred decimalDigits)+
+ src-lib/Factory/Math/Implementations/Pi/Spigot/Gosper.hs view
@@ -0,0 +1,39 @@+{-+ Copyright (C) 2011 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 the /Gosper/ series; <http://www.pi314.net/eng/goutte.php>+-}++module Factory.Math.Implementations.Pi.Spigot.Gosper(+-- * Constants+ series+) where++import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series+import qualified Factory.Math.Precision as Math.Precision++-- | Defines a series which converges to /Pi/.+series :: Integral i => Math.Implementations.Pi.Spigot.Series.Series i+series = Math.Implementations.Pi.Spigot.Series.MkSeries {+ Math.Implementations.Pi.Spigot.Series.baseNumerators = map (\i -> i * pred (2 * i)) [1 ..],+ Math.Implementations.Pi.Spigot.Series.baseDenominators = map ((* 3) . (\i -> succ i * (i + 2))) [3, 6 ..],+ Math.Implementations.Pi.Spigot.Series.coefficients = [3, 8 ..], -- 5n - 2+ Math.Implementations.Pi.Spigot.Series.nTerms = Math.Precision.getTermsRequired $ 1 / 13 {-empirical convergence-rate-}+}+
+ src-lib/Factory/Math/Implementations/Pi/Spigot/RabinowitzWagon.hs view
@@ -0,0 +1,40 @@+{-+ Copyright (C) 2011 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 the /Rabinowitz-Wagon/ series;+ <http://web.comlab.ox.ac.uk/oucl/work/jeremy.gibbons/publications/spigot.pdf>+ <http://www.mathpropress.com/stan/bibliography/spigot.pdf>.+-}++module Factory.Math.Implementations.Pi.Spigot.RabinowitzWagon(+-- * Constants+ series+) where++import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series+import qualified Factory.Math.Precision as Math.Precision++-- | Defines a series which converges to /Pi/.+series :: Integral i => Math.Implementations.Pi.Spigot.Series.Series i+series = Math.Implementations.Pi.Spigot.Series.MkSeries {+ Math.Implementations.Pi.Spigot.Series.baseNumerators = [1 ..],+ Math.Implementations.Pi.Spigot.Series.baseDenominators = [3, 5 ..],+ Math.Implementations.Pi.Spigot.Series.coefficients = repeat 2,+ Math.Implementations.Pi.Spigot.Series.nTerms = Math.Precision.getTermsRequired $ 10 ** negate (3 / 10) {-convergence-rate-}+}
+ src-lib/Factory/Math/Implementations/Pi/Spigot/Series.hs view
@@ -0,0 +1,53 @@+{-+ Copyright (C) 2011 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 the parameters of a series used in a /Spigot/-table to generate /Pi/.+-}++module Factory.Math.Implementations.Pi.Spigot.Series(+-- * Types+-- ** Data-types+ Series(..),+-- * Functions+ bases+) where++import Data.Ratio((%))+import qualified Data.Ratio+import qualified Factory.Math.Precision as Math.Precision++{- |+ * Defines a series composed from a sum of terms, each one of which is the product of a coefficient and a base.++ * The coefficents and bases of the series are described in /Horner form/; @Pi = c1 + (b1 * (c2 + b2 * (c3 + b3 * (...))))@.+-}+data Series i = MkSeries {+ coefficients :: [i],+ baseNumerators :: [i],+ baseDenominators :: [i],+ nTerms :: Math.Precision.DecimalDigits -> Int -- ^ The width of the spigot-table, required to accurately generate the requested number of digits.+}++-- | Combines 'baseNumerators' and 'baseDenominators', and as a side-effect, expresses the ratio in lowest terms.+bases :: Integral i => Series i -> [Data.Ratio.Ratio i]+bases MkSeries {+ baseNumerators = n,+ baseDenominators = d+} = zipWith (%) n d+
+ src-lib/Factory/Math/Implementations/Pi/Spigot/Spigot.hs view
@@ -0,0 +1,153 @@+{-+ Copyright (C) 2011 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 a /spigot/-algorithm; <http://en.wikipedia.org/wiki/Spigot_algorithm>.++ * Uses the traditional algorithm, rather than the /unbounded/ algorithm described by <http://www.comlab.ox.ac.uk/jeremy.gibbons/publications/spigot.pdf>.+-}++module Factory.Math.Implementations.Pi.Spigot.Spigot(+-- * Types+-- ** Type-synonyms+-- Base,+-- Coefficients,+-- I,+-- Pi,+-- PreDigits,+-- QuotRem,+-- * Constants+ decimal,+-- * Functions+-- carryAndDivide,+-- processColumns,+ openI,+-- ** Accessors+-- getQuotient,+-- getRemainder,+-- ** Constructors+-- mkRow+) where++import qualified Control.Arrow+import qualified Data.Char+import qualified Data.Ratio+import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series+import qualified Factory.Math.Precision as Math.Precision++{- |+ * The type in which all arithmetic is performed.++ * A small dynamic range, 32 bits or more, is typically adequate.+-}+type I = Int++-- | The constant base in which we want the resulting value of /Pi/ to be expressed.+decimal :: I+decimal = 10++-- | Coerce the polymorphic type 'Data.Ratio.Ratio' to suit the base used in our series.+type Base = Data.Ratio.Ratio I++-- | Coerce the polymorphic type returned by 'quotRem' to our specific requirements.+type QuotRem = (I, I)++-- Accessors.+getQuotient, getRemainder :: QuotRem -> I+getQuotient = fst+getRemainder = snd++type PreDigits = [I]+type Pi = [I]+type Coefficients = [I]++{- |+ * For a digit on one row of the spigot-table, add any numerator carried from the similar calculation one column to the right.++ * Divide the result of this summation, by the denominator of the base, to get the quotient and remainder.++ * Determine the quantity to carry to the similar calculation one column to the left, by multiplying the quotient by the numerator of the base.+-}+carryAndDivide :: (Base, I) -> QuotRem -> QuotRem+carryAndDivide (base, lhs) rhs+ | n < d = (0, n) -- In some degenerate cases, the result of the subsequent calculation can be more simply determined.+ | otherwise = Control.Arrow.first (* Data.Ratio.numerator base) $ n `quotRem` d+ where+ d, n :: I+ d = Data.Ratio.denominator base+ n = lhs + getQuotient rhs -- Carry numerator from the column to the right and add it to the current digit.++{- |+ * Fold 'carryAndDivide', from right to left, over the columns of a row in the spigot-table, continuously checking for overflow.++ * Release any previously withheld result-digits, after any adjustment for overflow in the current result-digit.++ * Withhold the current result-digit until the risk of overflow in subsequent result-digits has been assessed.++ * Call 'mkRow'.+-}+processColumns+ :: Math.Implementations.Pi.Spigot.Series.Series I+ -> PreDigits+ -> [(Base, I)] -- ^ Data-row.+ -> Pi+processColumns series preDigits l+ | overflowMargin > 1 = preDigits ++ nextRow [digit] -- There's neither overflow, nor risk of impact from subsequent overflow.+ | overflowMargin == 1 = nextRow $ preDigits ++ [digit] -- There's no overflow, but risk of impact from subsequent overflow.+ | otherwise = map ((`rem` decimal) . succ) preDigits ++ nextRow [0] -- Overflow => propagate the excess to previously withheld preDigits.+ where+ results :: [QuotRem]+ results = init $ scanr carryAndDivide (0, undefined) l++ digit :: I+ digit = getQuotient $ head results++ overflowMargin :: I+ overflowMargin = decimal - digit++ nextRow :: [I] -> [I]+ nextRow preDigits' = mkRow series preDigits' $ map getRemainder results++{- |+ * Multiply the remainders from the previous row.++ * Zip them with the constant bases, with an addition one stuck on the front to perform the conversion to decimal, to create a new row of the spigot-table.++ * Call 'processColumns'.+-}+mkRow :: Math.Implementations.Pi.Spigot.Series.Series I -> PreDigits -> Coefficients -> Pi+mkRow series preDigits = processColumns series preDigits . zip (recip (fromIntegral decimal) : Math.Implementations.Pi.Spigot.Series.bases series) . map (* decimal)++{- |+ * Initialises a /spigot/-table with the row of 'Math.Implementations.Pi.Spigot.Series.coefficients'.++ * Ensures that the row has suffient terms to accurately generate the required number of digits.++ * Extracts only those digits which are guaranteed to be accurate.++ * CAVEAT: the result is returned as an 'Integer', i.e. without any decimal point.+-}+openI :: Math.Implementations.Pi.Spigot.Series.Series I -> Math.Precision.DecimalDigits -> Integer+openI series decimalDigits = read . map (+ Data.Char.intToDigit . fromIntegral+ ) . take decimalDigits . mkRow series [] . take (+ Math.Implementations.Pi.Spigot.Series.nTerms series decimalDigits+ ) $ Math.Implementations.Pi.Spigot.Series.coefficients series+
+ src-lib/Factory/Math/Implementations/Primality.hs view
@@ -0,0 +1,217 @@+{-+ Copyright (C) 2011 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@]++ * Determines whether an integer is prime.++ * <http://en.wikipedia.org/wiki/Primality_test>.++ * <http://primes.utm.edu/index.html>++ * CAVEAT: it doesn't determine the prime-factors of composite numbers, just that they exist.+-}++module Factory.Math.Implementations.Primality(+-- * Types+-- ** Data-types+ Algorithm(..)+-- * Functions+-- ** Predicates+-- isPrimeByAKS,+-- isPrimeByMillerRabin,+-- witnessesCompositeness+) where++import Control.Arrow((&&&))+import qualified Control.DeepSeq+import qualified Control.Parallel.Strategies+import qualified Data.Numbers.Primes+import qualified Factory.Data.MonicPolynomial as Data.MonicPolynomial+import qualified Factory.Data.Polynomial as Data.Polynomial+import qualified Factory.Data.QuotientRing as Data.QuotientRing+import qualified Factory.Math.MultiplicativeOrder as Math.MultiplicativeOrder+import qualified Factory.Math.PerfectPower as Math.PerfectPower+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Primality as Math.Primality+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation+import qualified ToolShed.Defaultable++-- | The algorithms by which /primality/-testing has been implemented.+data Algorithm factorisationAlgorithm =+ AKS factorisationAlgorithm -- ^ <http://en.wikipedia.org/wiki/AKS_primality_test>.+ | MillerRabin -- ^ <http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test>.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable (Algorithm factorisationAlgorithm) where+ defaultValue = MillerRabin++instance Math.PrimeFactorisation.Algorithmic factorisationAlgorithm => Math.Primality.Algorithmic (Algorithm factorisationAlgorithm) where+ isPrime _ 2 = True -- The only even prime.+ isPrime algorithm candidate+ | candidate < 2 || (+ any (+ (== 0) . (candidate `rem`) -- The candidate has a small prime-factor, and is therefore composite.+ ) . filter (+ (candidate >=) . (* 2) -- The candidate must be at least double the small prime, for it to be a potential factor.+ ) . take 5 {-arbitrarily-} $ Data.Numbers.Primes.primes -- Excludes even numbers, provided at least the 1st prime is tested.+ ) = False+ | otherwise = (+ case algorithm of+ AKS factorisationAlgorithm -> isPrimeByAKS factorisationAlgorithm+ MillerRabin -> isPrimeByMillerRabin+ ) candidate++{- |+ * An implementation of the /Agrawal-Kayal-Saxena/ primality-test; <http://en.wikipedia.org/wiki/AKS_primality_test>,+ using the /Lenstra/ and /Pomerance/ algorithm.++ * CAVEAT: this deterministic algorithm has a theoretical time-complexity of @O(log^6)@,+ and therefore can't compete with the performance of probabilistic ones.++ * The /formal polynomials/ used in this algorithm, are conceptually different from /polynomial functions/;+ the /indeterminate/ and its powers, are merely used to name a sequence of pigeon-holes in which /coefficients/ are stored,+ and is never substituted for a specific value.+ This mind-shift, allows one to introduce concepts like /modular/ arithmetic on polynomials,+ which merely represent an operation on their coefficients and the pigeon-hole in which they're placed.++ [@Manindra Agrawal, Neeraj Kayal and Nitin Saxena@] <http://www.cse.iitk.ac.in/users/manindra/algebra/primality_v6.pdf>.++ [@H. W. Lenstra, Jr. and Carl Pomerance@] <http://www.math.dartmouth.edu/~carlp/PDF/complexity12.pdf>.++ [@Salembier and Southerington@] <http://ece.gmu.edu/courses/ECE746/project/F06_Project_resources/Salembier_Southerington_AKS.pdf>,++ [@R. Crandall and J. Papadopoulos@] <http://images.apple.com/acg/pdf/aks3.pdf>,++ [@Andreas Klappenecker@] <http://faculty.cs.tamu.edu/klappi/629/aks.ps>,++ [@Vibhor Bhatt and G. K. Patra@] <http://www.cmmacs.ernet.in/cmmacs/Publications/resch_rep/rrcm0307.pdf>,+-}+isPrimeByAKS :: (+ Control.DeepSeq.NFData i,+ Integral i,+ Math.PrimeFactorisation.Algorithmic factorisationAlgorithm,+ Show i+ ) => factorisationAlgorithm -> i -> Bool+isPrimeByAKS factorisationAlgorithm n = and [+ not $ Math.PerfectPower.isPerfectPower n, -- Step 1.+ Math.Primality.areCoprime n `all` filter (/= n) [2 .. r], -- Step 3.+ and $ Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq {-Benefits from '+RTS -H100M', which reduces garbage-collections-} (+ \a -> let+-- lhs, rhs :: Data.Polynomial.Polynomial i i+ lhs = Data.Polynomial.raiseModulo (Data.Polynomial.mkLinear 1 a) n {-power-} n {-modulus-}+ rhs = Data.Polynomial.mod' (Data.Polynomial.mkPolynomial [(1, n), (a, 0)]) n+ in Data.QuotientRing.areCongruentModulo (+ Data.MonicPolynomial.mkMonicPolynomial lhs+ ) (+ Data.MonicPolynomial.mkMonicPolynomial rhs+ ) (+ Data.MonicPolynomial.mkMonicPolynomial modulus+ ) -- Because all these polynomials are /monic/, one can establish /congruence/ using /integer/-division.+ ) [+ 1 .. floor . (* lg) . sqrt $ fromIntegral r+ ] -- Step 4; (x + a)^n ~ x^n + a mod (x^r - 1, n).+ ] where+ lg :: Double+ lg = logBase 2 $ fromIntegral n++-- r :: i+ r = fst . head . dropWhile (+ (<= floor (Math.Power.square lg)) . snd+ ) . map (+ id &&& Math.MultiplicativeOrder.multiplicativeOrder factorisationAlgorithm n+ ) $ Math.Primality.areCoprime n `filter` [2 ..] -- Step 2.++-- modulus :: Data.Polynomial.Polynomial i i+ modulus = Data.Polynomial.mkPolynomial [(1, r), (negate 1, 0)]++{- |+ * Uses the specified 'base' in an attempt to prove the /compositeness/ of an integer.++ * This is the opposite of the /Miller Test/; <http://mathworld.wolfram.com/MillersPrimalityTest.html>.++ * If the result is 'True', then the candidate is /composite/; regrettably the converse isn't true.+ Amongst the set of possible bases, over three-quarters are /witnesses/ to the compositeness of a /composite/ candidate,+ the remainder belong to the subset of /liars/.+ In consequence, many false results must be accumulated for different bases, to convincingly identify a prime.+-}+witnessesCompositeness :: (Integral i, Show i)+ => i -- ^ Candidate integer.+ -> i+ -> Int+ -> i -- ^ Base.+ -> Bool+witnessesCompositeness candidate oddRemainder nPowersOfTwo base = all (+ $ ((`rem` candidate) . Math.Power.square) `iterate` Math.Power.raiseModulo base oddRemainder candidate -- Repeatedly modulo-square.+ ) [+ (/= 1) . head, -- Check whether the zeroeth modulo-power is incongruent to one.+ notElem (pred candidate) . take nPowersOfTwo -- Check whether any modulo-power is incongruent to -1.+ ]++{- |+ * Repeatedly calls 'witnessesCompositeness', to progressively increase the probability of detecting a /composite/ number,+ until ultimately the candidate integer is proven to be prime.++ * Should all bases be tested, then the test is deterministic, but at an efficiency /lower/ than performing prime-factorisation.++ * The test becomes deterministic, for any candidate integer, when the number of tests reaches the limit defined by /Eric Bach/.++ * A testing of smaller set of bases, is sufficient for candidates smaller than various thresholds; <http://primes.utm.edu/prove/prove2_3.html>.++ * <http://en.wikipedia.org/wiki/Miller-Rabin_primality_test>.++ * <http://mathworld.wolfram.com/Rabin-MillerStrongPseudoprimeTest.html>++ * <http://mathworld.wolfram.com/StrongPseudoprime.html>.++ * <http://oeis.org/A014233>, <http://oeis.org/A006945>.+-}+isPrimeByMillerRabin :: (Integral i, Show i) => i -> Bool+isPrimeByMillerRabin primeCandidate = not $ witnessesCompositeness primeCandidate (+ fst $ last binaryFactors -- Odd-remainder.+ ) (+ length binaryFactors -- The number of times that 'two' can be factored-out from 'predecessor'.+ ) `any` testBases where+ predecessor = pred primeCandidate+ binaryFactors = takeWhile ((== 0) . snd) . tail {-drop the original-} $ iterate ((`quotRem` 2) . fst) (predecessor, 0) -- Factor-out powers of two.+ testBases+ | null fewestPrimeBases = let+ millersTestSet = floor . (* 2 {-Eric Bach-}) . Math.Power.square . toRational {-avoid premature rounding-} $ log (fromIntegral primeCandidate :: Double {-overflows at 10^851-})+ in [2 .. predecessor `min` millersTestSet]+ | otherwise = head fewestPrimeBases `take` Data.Numbers.Primes.primes+ where+ fewestPrimeBases = map fst $ dropWhile ((primeCandidate >=) . snd) [+ (0, 9), -- All odd integers less this, are prime, and require no further verification.+ (1, 2047),+ (2, 1373653),+ (3, 25326001),+ (4, 3215031751),+ (5, 2152302898747), -- Jaeschke ...+ (6, 3474749660383),+ (8, 341550071728321),+ (11, 3825123056546413051), -- Zhang ...+ (12, 318665857834031151167461),+ (13, 3317044064679887385961981),+ (14, 6003094289670105800312596501),+ (15, 59276361075595573263446330101),+ (17, 564132928021909221014087501701),+ (19, 1543267864443420616877677640751301),+ (20, 10 ^ (36 :: Int)) -- At least.+ ]+
+ src-lib/Factory/Math/Implementations/PrimeFactorisation.hs view
@@ -0,0 +1,145 @@+{-+ Copyright (C) 2011 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 several different prime-factorisation algorithms.++ * <http://www.tug.org/texinfohtml/coreutils.html#factor-invocation>.+-}++module Factory.Math.Implementations.PrimeFactorisation(+-- * Types+-- ** Data-types+ Algorithm(+-- DixonsMethod,+ FermatsMethod,+ TrialDivision+ )+-- * Functions+-- factoriseByDixonsMethod+-- factoriseByFermatsMethod+-- factoriseByTrialDivision+) where++import Control.Arrow((&&&))+import qualified Control.Arrow+import qualified Control.DeepSeq+import qualified Control.Parallel.Strategies+import qualified Data.Maybe+import qualified Data.Numbers.Primes+import qualified Factory.Data.Exponential as Data.Exponential+import Factory.Data.Exponential((<^))+import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.PerfectPower as Math.PerfectPower+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation+import qualified ToolShed.Data.Pair+import qualified ToolShed.Defaultable++-- | The algorithms by which prime-factorisation has been implemented.+data Algorithm+ = DixonsMethod -- ^ <http://en.wikipedia.org/wiki/Dixon%27s_factorization_method>.+ | FermatsMethod -- ^ <http://en.wikipedia.org/wiki/Fermat%27s_factorization_method>.+ | TrialDivision -- ^ <http://en.wikipedia.org/wiki/Trial_division>.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = TrialDivision++instance Math.PrimeFactorisation.Algorithmic Algorithm where+ primeFactors algorithm = case algorithm of+ DixonsMethod -> factoriseByDixonsMethod+ FermatsMethod -> Data.PrimeFactors.reduce . factoriseByFermatsMethod+ TrialDivision -> factoriseByTrialDivision++-- | <http://en.wikipedia.org/wiki/Dixon%27s_factorization_method>.+factoriseByDixonsMethod :: Integral base => base -> Data.PrimeFactors.Factors base exponent+factoriseByDixonsMethod = undefined++{- |+ * <http://en.wikipedia.org/wiki/Fermat%27s_factorization_method>.++ * <http://mathworld.wolfram.com/FermatsFactorizationMethod.html>.++ * <http://en.wikipedia.org/wiki/Congruence_of_squares>.++ * @i = f1 * f2@ Assume a non-trivial factorisation, ie. one in which both factors exceed one.+ => @i = (larger + smaller) * (larger - smaller)@ Represent the co-factors as a sum and difference.+ => @i = larger^2 - smaller^2@ Which has an integral solution if @i@ is neither /even/ nor a /perfect square/.+ => @sqrt (larger^2 - i) = smaller@ Search for /larger/, which results in an integral value for /smaller/.++ * Given that the smaller factor /f2/, can't be less than 3 (/i/ isn't /even/), then the larger /f1/, can't be greater than @(i `div` 3)@.+ So: @(f2 >= 3) && (f1 <= i `div` 3)@ Two equations which can be used to solve for /larger/.+ => @(larger - smaller >= 3) && (larger + smaller <= i `div` 3)@ Add these to eliminate /smaller/.+ => @larger <= (i + 9) `div` 6@ The upper bound of the search-space.++ * This algorithm works best when there's a factor close to the /square-root/.+-}+factoriseByFermatsMethod :: (+ Control.DeepSeq.NFData base,+ Control.DeepSeq.NFData exponent,+ Integral base,+ Num exponent+ ) => base -> Data.PrimeFactors.Factors base exponent+factoriseByFermatsMethod i+ | i <= 3 = [Data.Exponential.rightIdentity i]+ | even i = Data.Exponential.rightIdentity 2 : factoriseByFermatsMethod (i `div` 2) {-recurse-}+ | Data.Maybe.isJust maybeSquareNumber = (<^ 2) `map` factoriseByFermatsMethod (Data.Maybe.fromJust maybeSquareNumber) {-recurse-}+ | null factors = [Data.Exponential.rightIdentity i] -- Prime.+ | otherwise = uncurry (++) . Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq -- CAVEAT: unproductive on the size of integers tested so far.+ ) . ToolShed.Data.Pair.mirror factoriseByFermatsMethod $ head factors+ where+-- maybeSquareNumber :: Integral i => Maybe i+ maybeSquareNumber = Math.PerfectPower.maybeSquareNumber i++-- factors :: Integral i => [i]+ factors = map (+ (+ uncurry (+) &&& uncurry (-) -- Construct the co-factors as the sum and difference of /larger/ and /smaller/.+ ) . Control.Arrow.second Data.Maybe.fromJust+ ) . filter (+ Data.Maybe.isJust . snd -- Search for a perfect square.+ ) . map (+ Control.Arrow.second $ Math.PerfectPower.maybeSquareNumber {-hotspot-} . (+ negate i) -- Associate the corresponding value of /smaller/.+ ) . takeWhile (+ (<= (i + 9) `div` 6) . fst -- Terminate the search at the maximum value of /larger/.+ ) . Math.Power.squaresFrom {-hotspot-} . ceiling $ sqrt (fromIntegral i :: Double) -- Start the search at the minimum value of /larger/.++{- |+ * Decomposes the specified integer, into a product of /prime/-factors,+ using <http://mathworld.wolfram.com/DirectSearchFactorization.html>, AKA <http://en.wikipedia.org/wiki/Trial_division>.++ * This works best when the factors are small.+-}+factoriseByTrialDivision :: (Integral base, Num exponent) => base -> Data.PrimeFactors.Factors base exponent+factoriseByTrialDivision = slave Data.Numbers.Primes.primes where+ slave primes i+ | null primeCandidates = [Data.Exponential.rightIdentity i]+ | otherwise = Data.Exponential.rightIdentity lowestPrimeFactor `Data.PrimeFactors.insert'` slave primeCandidates (i `quot` lowestPrimeFactor)+ where+ primeCandidates = dropWhile (+ (/= 0) . (i `rem`)+ ) $ takeWhile (+ <= Math.PrimeFactorisation.maxBoundPrimeFactor i+ ) primes++ lowestPrimeFactor = head primeCandidates+
+ src-lib/Factory/Math/Implementations/Primes/Algorithm.hs view
@@ -0,0 +1,63 @@+{-+ Copyright (C) 2011 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@]++ * Generates the constant list of /prime-numbers/, by a variety of different algorithms.++ * <http://www.haskell.org/haskellwiki/Prime_numbers>.++ * <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.31.3936&rep=rep1&type=pdf>.++ * <http://larc.unt.edu/ian/pubs/sieve.pdf>.+-}++module Factory.Math.Implementations.Primes.Algorithm(+-- * Types+-- ** Data-types+ Algorithm(..)+) where++import qualified Data.Numbers.Primes+import qualified Factory.Data.PrimeWheel as Data.PrimeWheel+import qualified Factory.Math.Implementations.Primes.SieveOfAtkin as Math.Implementations.Primes.SieveOfAtkin+import qualified Factory.Math.Implementations.Primes.SieveOfEratosthenes as Math.Implementations.Primes.SieveOfEratosthenes+import qualified Factory.Math.Implementations.Primes.TrialDivision as Math.Implementations.Primes.TrialDivision+import qualified Factory.Math.Implementations.Primes.TurnersSieve as Math.Implementations.Primes.TurnersSieve+import qualified Factory.Math.Primes as Math.Primes+import qualified ToolShed.Defaultable++-- | The implemented methods by which the primes may be generated.+data Algorithm+ = SieveOfAtkin Integer -- ^ The /Sieve of Atkin/, optimised using a 'Data.PrimeWheel.PrimeWheel' of optimal size, for primes up to the specified maximum bound; <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.+ | SieveOfEratosthenes Data.PrimeWheel.NPrimes -- ^ The /Sieve of Eratosthenes/ (<http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>), optimised using a 'Data.PrimeWheel.PrimeWheel'.+ | TrialDivision Data.PrimeWheel.NPrimes -- ^ For each candidate, confirm indivisibility, by all /primes/ smaller than its /square-root/, optimised using a 'Data.PrimeWheel.PrimeWheel'.+ | TurnersSieve -- ^ For each /prime/, the infinite list of candidates greater than its /square/, is filtered for indivisibility; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.+ | WheelSieve Int -- ^ 'Data.Numbers.Primes.wheelSieve'.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = SieveOfEratosthenes 7 -- Resulting in a wheel of circumference 510510.++instance Math.Primes.Algorithmic Algorithm where+ primes (SieveOfAtkin maxPrime) = Math.Implementations.Primes.SieveOfAtkin.sieveOfAtkin (Data.PrimeWheel.estimateOptimalSize maxPrime) $ fromIntegral maxPrime+ primes (SieveOfEratosthenes wheelSize) = Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenes wheelSize+ primes (TrialDivision wheelSize) = Math.Implementations.Primes.TrialDivision.trialDivision wheelSize+ primes TurnersSieve = Math.Implementations.Primes.TurnersSieve.turnersSieve+ primes (WheelSieve wheelSize) = Data.Numbers.Primes.wheelSieve wheelSize -- Has better space-complexity than 'SieveOfEratosthenes'.
+ src-lib/Factory/Math/Implementations/Primes/SieveOfAtkin.hs view
@@ -0,0 +1,242 @@+{-+ Copyright (C) 2011 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@]++ * Generates the constant /bounded/ list of /prime-numbers/, using the /Sieve of Atkin/; <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.++ * <cr.yp.to/papers/primesieves-19990826.pdf>.++ * The implementation;+ has been optimised using a /wheel/ of static, but parameterised, size;+ has been parallelized;+ is polymorphic, but with a specialisation for type 'Int'.++ [@CAVEAT@] The 'Int'-specialisation is implemented by a /rewrite-rule/, which is /very/ fragile.+-}++module Factory.Math.Implementations.Primes.SieveOfAtkin(+-- * Types+-- ** Data-types+-- PolynomialType,+-- * Constants+-- atkinsModulus,+-- inherentPrimes,+-- nInherentPrimes,+-- squares,+-- * Functions+-- polynomialTypeLookupPeriod,+-- polynomialTypeLookup,+-- findPolynomialSolutions,+-- filterOddRepetitions,+-- generateMultiplesOfSquareTo,+-- getPrefactoredPrimes,+ sieveOfAtkin,+-- sieveOfAtkinInt+) where++import qualified Control.DeepSeq+import qualified Control.Parallel.Strategies+import qualified Data.Array.IArray+import Data.Array.IArray((!))+import qualified Data.IntSet+import qualified Data.List+import qualified Data.Set+import qualified Factory.Data.PrimeWheel as Data.PrimeWheel+import qualified Factory.Math.Power as Math.Power+import qualified ToolShed.Data.List++-- | Defines the types of /quadratic/, available to test the potential primality of a candidate integer.+data PolynomialType+ = ModFour -- ^ Suitable for primality-testing numbers meeting @(n `mod` 4 == 1)@.+ | ModSix -- ^ Suitable for primality-testing numbers meeting @(n `mod` 6 == 1)@.+ | ModTwelve -- ^ Suitable for primality-testing numbers meeting @(n `mod` 12 == 11)@.+ | None -- ^ There's no polynomial which can assess primality, because the candidate is composite.+ deriving Eq++-- | The constant modulus used to select the appropriate quadratic for a prime candidate.+atkinsModulus :: Integral i => i+atkinsModulus = foldr1 lcm [4, 6, 12] -- Sure, this is always '12', but this is the reason why.++-- | The constant list of primes factored-out by the unoptimised algorithm.+inherentPrimes :: Integral i => [i]+inherentPrimes = [2, 3]++-- | The constant number of primes factored-out by the unoptimised algorithm.+nInherentPrimes :: Int+nInherentPrimes = length (inherentPrimes :: [Int])++-- | Typically the set of primes which have been built into the specified /wheel/, but never fewer than 'inherentPrimes'.+getPrefactoredPrimes :: Integral i => Data.PrimeWheel.PrimeWheel i -> [i]+getPrefactoredPrimes = max inherentPrimes . Data.PrimeWheel.getPrimeComponents++-- | The period over which the data returned by 'polynomialTypeLookup' repeats.+polynomialTypeLookupPeriod :: Integral i => Data.PrimeWheel.PrimeWheel i -> i+polynomialTypeLookupPeriod = lcm atkinsModulus . Data.PrimeWheel.getCircumference++{- |+ * Defines which, if any, of the three /quadratics/ is appropriate for the primality-test for each candidate.++ * Since this algorithm uses /modular arithmetic/, the /range/ of results repeat after a short /domain/ related to the /modulus/.+ Thus one need calculate at most one period of this cycle, but fewer if the maximum prime required falls within the first cycle of results.++ * Because the results are /bounded/, they're returned in a zero-indexed /array/, to provide efficient random access;+ the first few elements should never be required, but it makes query clearer.++ * <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.+-}+polynomialTypeLookup :: (Data.Array.IArray.Ix i, Integral i)+ => Data.PrimeWheel.PrimeWheel i+ -> i -- ^ The maximum prime required.+ -> Data.Array.IArray.Array i PolynomialType+polynomialTypeLookup primeWheel maxPrime = Data.Array.IArray.listArray (0, pred (polynomialTypeLookupPeriod primeWheel) `min` maxPrime) $ map select [0 ..] where+-- select :: Integral i => i -> PolynomialType+ select n+ | any (+ (== 0) . (n `rem`) -- Though this is merely /Trial Division/, it's only performed over a short bounded interval of numerators.+ ) primeComponents = None+ | r `elem` [1, 5] = ModFour -- We actually require @(n `mod` 4 == 1)@, but this is the equivalent modulo 12, with @(r == 9)@ removed because they're all divisible by /3/.+ | r == 7 = ModSix -- We actually require @(n `mod` 6 == 1)@, but this is the equivalent modulo 12, where @(r == 1)@ has been accounted for above.+ | r == 11 = ModTwelve -- We require @(n `mod` 12 == 11)@.+ | otherwise = None+ where+ r = n `rem` atkinsModulus+ primeComponents = drop nInherentPrimes $ Data.PrimeWheel.getPrimeComponents primeWheel++-- | The constant, infinite list of the /squares/, of integers increasing from /1/.+squares :: Integral i => [i]+squares = map snd $ Math.Power.squaresFrom 1++{- |+ * Returns the /ordered/ list of those values with an /odd/ number of occurrences in the specified /unordered/ list.++ * CAVEAT: this is expensive in both execution-time and space.+ The typical imperative-style implementation accumulates polynomial-solutions in a /mutable array/ indexed by the candidate integer.+ This doesn't translate seamlessly to the /pure functional/ domain where /arrays/ naturally immutable,+ so we /sort/ a /list/ of polynomial-solutions, then measure the length of the solution-spans, corresponding to viable candidates.+ Regrettably, 'Data.List.sort' (implemented in /GHC/ by /mergesort/) has a time-complexity /O(n*log n)/+ which is greater than the theoretical /O(n)/ of the whole /Sieve of Atkin/;+ /GHC/'s old /qsort/-implementation is even slower :(+-}+filterOddRepetitions :: Ord a => [a] -> [a]+-- filterOddRepetitions = map head . filter (foldr (const not) False) . Data.List.group . Data.List.sort -- Too slow.+filterOddRepetitions = slave True . Data.List.sort where+ slave isOdd (one : remainder@(two : _))+ | one == two = slave (not isOdd) remainder+ | isOdd = one : beginSpan+ | otherwise = beginSpan+ where+ beginSpan = slave True remainder+ slave True [singleton] = [singleton]+ slave _ _ = []++{- |+ * Returns the ordered list of solutions aggregated from each of three /bivariate quadratics/; @z = f(x, y)@.++ * For a candidate integer to be prime, it is necessary but insufficient, that there are an /odd/ number of solutions of value /candidate/.++ * At most one of these three polynomials is suitable for the validation of any specific candidate /z/, depending on 'lookupPolynomialType'.+ so the three sets of solutions are mutually exclusive.+ One coordinate @(x, y)@, can have solutions in more than one of the three polynomials.++ * This algorithm exhaustively traverses the domain @(x, y)@, for resulting /z/ of the required modulus.+ Whilst it tightly constrains the bounds of the search-space, it searches the domain methodically rather than intelligently.+-}+findPolynomialSolutions :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i)+ => Data.PrimeWheel.PrimeWheel i+ -> i -- ^ The maximum prime-number required.+ -> [i]+findPolynomialSolutions primeWheel maxPrime = foldr1 ToolShed.Data.List.merge {-The lists were previously sorted, as a side-effect, by 'filterOddRepetitions'-} $ Control.Parallel.Strategies.withStrategy (+ Control.Parallel.Strategies.parList Control.Parallel.Strategies.rdeepseq+ ) [+ {-# SCC "4x^2+y^2" #-} filterOddRepetitions [+ z |+ x' <- takeWhile (<= pred maxPrime) $ map (* 4) squares,+ z <- takeWhile (<= maxPrime) $ map (+ x') oddSquares,+ lookupPolynomialType z == ModFour+ ], -- List-comprehension. Twice the length of the other two lists.+ {-# SCC "3x^2+y^2" #-} filterOddRepetitions [+ z |+ x' <- takeWhile (<= pred maxPrime) $ map (* 3) squares,+ z <- takeWhile (<= maxPrime) . map (+ x') $ if even x' then oddSelection else evenSelection,+ lookupPolynomialType z == ModSix+ ], -- List-comprehension.+ {-# SCC "3x^2-y^2" #-} filterOddRepetitions [+ z |+ x2 <- takeWhile (<= maxPrime `div` 2) squares,+ z <- dropWhile (> maxPrime) . map (3 * x2 -) . takeWhile (< x2) $ if even x2 then oddSelection else evenSelection,+ lookupPolynomialType z == ModTwelve+ ] -- List-comprehension.+ ] where+ (evenSquares, oddSquares) = Data.List.partition even squares++-- evenSelection, oddSelection :: Integral i => [i]+ evenSelection = selection110 evenSquares where+ selection110 (x0 : x1 : _ : xs) = x0 : x1 : selection110 xs -- Effectively, those for meeting ((== 4) . (`mod` 6)).+ selection110 xs = xs+ oddSelection = selection101 oddSquares where+ selection101 (x0 : _ : x2 : xs) = x0 : x2 : selection101 xs -- Effectively, those for meeting ((== 1) . (`mod` 6)).+ selection101 xs = xs++-- lookupPolynomialType :: (Data.Array.IArray.Ix i, Integral i) => i -> PolynomialType+ lookupPolynomialType = (polynomialTypeLookup primeWheel maxPrime !) . (`rem` polynomialTypeLookupPeriod primeWheel)++-- | Generates the /bounded/ list of multiples, of the /square/ of the specified prime, skipping those which aren't required.+generateMultiplesOfSquareTo :: Integral i+ => Data.PrimeWheel.PrimeWheel i -- ^ Used to generate the gaps between prime multiples of the square.+ -> i -- ^ The /prime/.+ -> i -- ^ The maximum bound.+ -> [i]+generateMultiplesOfSquareTo primeWheel prime max' = takeWhile (<= max') . scanl (\accumulator -> (+ accumulator) . (* prime2)) prime2 . cycle $ Data.PrimeWheel.getSpokeGaps primeWheel where+ prime2 = Math.Power.square prime++{- |+ * Generates the constant /bounded/ list of /prime-numbers/.++ * <http://cr.yp.to/papers/primesieves-19990826.pdf>+-}+sieveOfAtkin :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i)+ => Data.PrimeWheel.NPrimes -- ^ Other implementations effectively use a hard-coded value either /2/ or /3/, but /6/ seems better.+ -> i -- ^ The maximum prime required.+ -> [i] -- ^ The /bounded/ list of primes.+sieveOfAtkin wheelSize maxPrime = (prefactoredPrimes ++) . filterSquareFree Data.Set.empty . dropWhile (<= maximum prefactoredPrimes) $ findPolynomialSolutions primeWheel maxPrime where+ primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize+ prefactoredPrimes = getPrefactoredPrimes primeWheel++-- filterSquareFree :: Integral i => Data.Set.Set i -> [i] -> [i]+ filterSquareFree _ [] = []+ filterSquareFree primeMultiples (candidate : candidates)+ | Data.Set.member candidate primeMultiples = {-# SCC "delete" #-} filterSquareFree (Data.Set.delete candidate primeMultiples) candidates -- Tail-recurse.+ | otherwise = {-# SCC "insert" #-} candidate : filterSquareFree (Data.Set.union primeMultiples . Data.Set.fromDistinctAscList $ generateMultiplesOfSquareTo primeWheel candidate maxPrime) candidates++{-# NOINLINE sieveOfAtkin #-}+{-# RULES "sieveOfAtkin/Int" sieveOfAtkin = sieveOfAtkinInt #-} -- CAVEAT: doesn't fire when built with profiling enabled.++-- | A specialisation of 'sieveOfAtkin', which reduces both the execution-time and the space required.+sieveOfAtkinInt :: Data.PrimeWheel.NPrimes -> Int -> [Int]+sieveOfAtkinInt wheelSize maxPrime = (prefactoredPrimes ++) . filterSquareFree Data.IntSet.empty . dropWhile (<= maximum prefactoredPrimes) $ findPolynomialSolutions primeWheel maxPrime where+ primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize+ prefactoredPrimes = getPrefactoredPrimes primeWheel++ filterSquareFree :: Data.IntSet.IntSet -> [Int] -> [Int]+ filterSquareFree _ [] = []+ filterSquareFree primeMultiples (candidate : candidates)+ | Data.IntSet.member candidate primeMultiples = filterSquareFree (Data.IntSet.delete candidate primeMultiples) candidates+ | otherwise = candidate : filterSquareFree (Data.IntSet.union primeMultiples . Data.IntSet.fromDistinctAscList $ generateMultiplesOfSquareTo primeWheel candidate maxPrime) candidates+
+ src-lib/Factory/Math/Implementations/Primes/SieveOfEratosthenes.hs view
@@ -0,0 +1,162 @@+{-+ Copyright (C) 2011 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@]++ * Generates the constant, conceptually infinite, list of /prime-numbers/, using the /Sieve of Eratosthenes/; <http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>.++ * Based on <http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf>.++ * The implementation;+ has been optimised using a /wheel/ of static, but parameterised, size;+ is polymorphic, but with a specialisation for type 'Int'.++ [@CAVEAT@] The 'Int'-specialisation is implemented by a /rewrite-rule/, which is /very/ fragile.+-}++module Factory.Math.Implementations.Primes.SieveOfEratosthenes(+-- * Types+-- ** Type-synonyms+-- PrimeMultiplesQueue,+-- PrimeMultiplesMap,+-- Repository,+-- PrimeMultiplesMapInt,+-- RepositoryInt,+-- * Functions+-- head',+-- tail',+ sieveOfEratosthenes,+-- sieveOfEratosthenesInt+) where++import Control.Arrow((&&&), (***))+import qualified Control.Arrow+import qualified Data.IntMap+import qualified Data.Map+import Data.Sequence((|>))+import qualified Data.Sequence+import qualified Factory.Data.PrimeWheel as Data.PrimeWheel++-- | The 'Data.Sequence.Seq' counterpart to 'Data.List.head'.+head' :: Data.Sequence.Seq [a] -> [a]+head' = (`Data.Sequence.index` 0)++{- |+ * The 'Data.Sequence.Seq' counterpart to 'Data.List.tail'.++ * CAVEAT: because @ Data.List.tail [] @ returns an error, whereas @ tail' Data.Sequence.empty @ returns 'Data.Sequence.empty',+ this function is for internal use only.+-}+tail' :: Data.Sequence.Seq [a] -> Data.Sequence.Seq [a]+tail' = Data.Sequence.drop 1++-- | An ordered queue of the multiples of primes.+type PrimeMultiplesQueue i = Data.Sequence.Seq (Data.PrimeWheel.PrimeMultiples i)++-- | A map of the multiples of primes.+type PrimeMultiplesMap i = Data.Map.Map i (Data.PrimeWheel.PrimeMultiples i)++-- | Combine a /queue/, with a /map/, to form a repository to hold prime-multiples.+type Repository i = (PrimeMultiplesQueue i, PrimeMultiplesMap i)++{- |+ * A refinement of the /Sieve Of Eratosthenes/, which pre-sieves candidates, selecting only those /coprime/ to the specified short sequence of low prime-numbers.++ * The short sequence of initial primes are represented by a 'Data.PrimeWheel.PrimeWheel',+ of parameterised, but static, size; <http://en.wikipedia.org/wiki/Wheel_factorization>.++ * The algorithm requires one to record multiples of previously discovered primes, allowing /composite/ candidates to be eliminated by comparison.++ * Because each /list/ of multiples, starts with the /square/ of the prime from which it was generated,+ the vast majority will be larger than the maximum prime ultimately demanded, and the effort of constructing and storing this list, is consequently wasted.+ Many implementations solve this, by requiring specification of the maximum prime required,+ thus allowing the construction of redundant lists of multiples to be avoided.++ * This implementation doesn't impose that constraint, leaving a requirement for /rapid/ storage,+ which is supported by /appending/ the /list/ of prime-multiples, to a /queue/.+ If a large enough candidate is ever generated, to match the /head/ of the /list/ of prime-multiples,+ at the /head/ of this /queue/, then the whole /list/ of prime-multiples is dropped from the /queue/,+ but the /tail/ of this /list/ of prime-multiples, for which there is now a high likelyhood of a subsequent match, must now be re-recorded.+ A /queue/ doesn't support efficient random /insertion/, so a 'Data.Map.Map' is used for these subsequent multiples.+ This solution is faster than just using a "Data.PQueue.Min".++ * CAVEAT: has linear /O(n)/ space-complexity.+-}+sieveOfEratosthenes :: Integral i+ => Data.PrimeWheel.NPrimes+ -> [i]+sieveOfEratosthenes = uncurry (++) . (Data.PrimeWheel.getPrimeComponents &&& start . Data.PrimeWheel.roll) . Data.PrimeWheel.mkPrimeWheel where+ start :: Integral i => [Data.PrimeWheel.Distance i] -> [i]+ start ~((candidate, rollingWheel) : distances) = candidate : sieve (head distances) (Data.Sequence.singleton $ Data.PrimeWheel.generateMultiples candidate rollingWheel, Data.Map.empty)++ sieve :: Integral i => Data.PrimeWheel.Distance i -> Repository i -> [i]+ sieve distance@(candidate, rollingWheel) repository@(primeSquares, squareFreePrimeMultiples) = case Data.Map.lookup candidate squareFreePrimeMultiples of+ Just primeMultiples -> sieve' $ Control.Arrow.second (insertUniq primeMultiples . Data.Map.delete candidate) repository -- Re-insert subsequent multiples.+ Nothing -- Not a square-free composite.+ | candidate == smallestPrimeSquare -> sieve' $ (tail' *** insertUniq subsequentPrimeMultiples) repository -- Migrate subsequent prime-multiples, from 'primeSquares' to 'squareFreePrimeMultiples'.+ | otherwise {-prime-} -> candidate : sieve' (Control.Arrow.first (|> Data.PrimeWheel.generateMultiples candidate rollingWheel) repository)+ where+ (smallestPrimeSquare : subsequentPrimeMultiples) = head' primeSquares+ where+-- sieve' :: Repository i -> [i]+ sieve' = sieve $ Data.PrimeWheel.rotate distance -- Tail-recurse.++ insertUniq :: Ord i => Data.PrimeWheel.PrimeMultiples i -> PrimeMultiplesMap i -> PrimeMultiplesMap i+ insertUniq l m = insert $ dropWhile (`Data.Map.member` m) l where+-- insert :: Ord i => Data.PrimeWheel.PrimeMultiples i -> PrimeMultiplesMap i+ insert [] = error "Factory.Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenes.sieve.insertUniq.insert:\tnull list"+ insert (key : values) = Data.Map.insert key values m++{-# NOINLINE sieveOfEratosthenes #-}+{-# RULES "sieveOfEratosthenes/Int" sieveOfEratosthenes = sieveOfEratosthenesInt #-} -- CAVEAT: doesn't fire when built with profiling enabled.++-- | A specialisation of 'PrimeMultiplesMap'.+type PrimeMultiplesMapInt = Data.IntMap.IntMap (Data.PrimeWheel.PrimeMultiples Int)++-- | A specialisation of 'Repository'.+type RepositoryInt = (PrimeMultiplesQueue Int, PrimeMultiplesMapInt)++{- |+ * A specialisation of 'sieveOfEratosthenes', which approximately /doubles/ the speed and reduces the space required.++ * CAVEAT: because the algorithm involves /squares/ of primes,+ this implementation will overflow when finding primes greater than @2^16@ on a /32-bit/ machine.+-}+sieveOfEratosthenesInt :: Data.PrimeWheel.NPrimes -> [Int]+sieveOfEratosthenesInt = uncurry (++) . (Data.PrimeWheel.getPrimeComponents &&& start . Data.PrimeWheel.roll) . Data.PrimeWheel.mkPrimeWheel where+ start :: [Data.PrimeWheel.Distance Int] -> [Int]+ start ~((candidate, rollingWheel) : distances) = candidate : sieve (head distances) (Data.Sequence.singleton $ Data.PrimeWheel.generateMultiples candidate rollingWheel, Data.IntMap.empty)++ sieve :: Data.PrimeWheel.Distance Int -> RepositoryInt -> [Int]+ sieve distance@(candidate, rollingWheel) repository@(primeSquares, squareFreePrimeMultiples) = case Data.IntMap.lookup candidate squareFreePrimeMultiples of+ Just primeMultiples -> sieve' $ Control.Arrow.second (insertUniq primeMultiples . Data.IntMap.delete candidate) repository+ Nothing+ | candidate == smallestPrimeSquare -> sieve' $ (tail' *** insertUniq subsequentPrimeMultiples) repository+ | otherwise -> candidate : sieve' (Control.Arrow.first (|> Data.PrimeWheel.generateMultiples candidate rollingWheel) repository)+ where+ (smallestPrimeSquare : subsequentPrimeMultiples) = head' primeSquares+ where+ sieve' :: RepositoryInt -> [Int]+ sieve' = sieve $ Data.PrimeWheel.rotate distance++ insertUniq :: Data.PrimeWheel.PrimeMultiples Int -> PrimeMultiplesMapInt -> PrimeMultiplesMapInt+ insertUniq l m = insert $ dropWhile (`Data.IntMap.member` m) l where+ insert :: Data.PrimeWheel.PrimeMultiples Int -> PrimeMultiplesMapInt+ insert [] = error "Factory.Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenesInt.sieve.insertUniq.insert:\tnull list"+ insert (key : values) = Data.IntMap.insert key values m
+ src-lib/Factory/Math/Implementations/Primes/TrialDivision.hs view
@@ -0,0 +1,59 @@+{-+ Copyright (C) 2011 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@] Generates the constant, conceptually infinite, list of /prime-numbers/, using /Trial Division/.+-}++module Factory.Math.Implementations.Primes.TrialDivision(+-- * Functions+ trialDivision+-- ** Predicates+-- isIndivisibleBy+) where++import qualified Control.Arrow+import qualified Data.List+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation+import qualified Factory.Data.PrimeWheel as Data.PrimeWheel++-- | Uses /Trial Division/, to determine whether the specified candidate is indivisible by all potential denominators from the specified list.+isIndivisibleBy :: Integral i+ => i -- ^ The numerator.+ -> [i] -- ^ The denominators of which it must not be a multiple.+ -> Bool+isIndivisibleBy numerator = all ((/= 0) . (numerator `rem`)) . takeWhile (<= Math.PrimeFactorisation.maxBoundPrimeFactor numerator)++{- |+ * For each candidate, confirm indivisibility, by all /primes/ smaller than its /square-root/.++ * The candidates to sieve, are generated by a 'Data.PrimeWheel.PrimeWheel',+ of parameterised, but static, size; <http://en.wikipedia.org/wiki/Wheel_factorization>.+-}+trialDivision :: Integral prime => Data.PrimeWheel.NPrimes -> [prime]+trialDivision 0 = [2, 3] ++ filter (`isIndivisibleBy` trialDivision 0 {-recurse-}) [5 ..] -- No faster than using 'Data.PrimeWheel.mkPrimeWheel 0', but apparently better space-complexity ?!+trialDivision wheelSize = Data.PrimeWheel.getPrimeComponents primeWheel ++ indivisible where+ primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize+ candidates = map fst $ Data.PrimeWheel.roll primeWheel+ indivisible = uncurry (++) . Control.Arrow.second (+ filter (`isIndivisibleBy` indivisible {-recurse-})+ ) $ Data.List.span (+ < Math.Power.square (head candidates) -- The first composite candidate, is the square of the next prime after the wheel's constituent ones.+ ) candidates+
+ src-lib/Factory/Math/Implementations/Primes/TurnersSieve.hs view
@@ -0,0 +1,48 @@+{-+ Copyright (C) 2011 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@] Generates the constant, conceptally infinite, list of /prime-numbers/, using /Turner's Sieve/; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.+-}++module Factory.Math.Implementations.Primes.TurnersSieve(+-- * Functions+ turnersSieve+) where++import qualified Factory.Math.Power as Math.Power++{- |+ * For each /prime/, the infinite list of candidates greater than its /square/,+ is filtered for indivisibility; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.++ * CAVEAT: though one can easily add a 'Data.PrimeWheel.PrimeWheel', it proved counterproductive.+-}+turnersSieve :: Integral prime => [prime]+turnersSieve = 2 : sieve [3, 5 ..] where+ sieve :: Integral i => [i] -> [i]+ sieve [] = []+ sieve (prime : candidates) = prime : sieve (+ filter (+ \candidate -> any ($ candidate) [+ (< Math.Power.square prime), -- Unconditionally admit any candidate smaller than the square of the last prime.+ (/= 0) . (`rem` prime) -- Ensure indivisibility, of all subsequent candidates, by the last prime discovered.+ ]+ ) candidates+ )+
+ src-lib/Factory/Math/Implementations/SquareRoot.hs view
@@ -0,0 +1,192 @@+{-+ Copyright (C) 2011 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 'Math.SquareRoot.Algorithmic' by a variety of methods.++ [@CAVEAT@]++ Caller may benefit from application of 'Math.Precision.simplify' before operating on the result;+ which though of the required accuracy, may not be the most concise rational number satisfying that criterion.+-}+module Factory.Math.Implementations.SquareRoot(+-- * Types+-- ** Type-synonyms+-- ProblemSpecification,+ Terms,+-- ** Data-types+ Algorithm(..)+-- * Functions+-- squareRootByContinuedFraction,+-- squareRootByIteration,+-- squareRootByTaylorSeries,+-- taylorSeriesCoefficients+) where++import Control.Arrow((***))+import Factory.Data.PrimeFactors((>/<), (>^))+import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified Factory.Math.Summation as Math.Summation+import qualified ToolShed.Defaultable++-- | The number of terms in a series.+type Terms = Int++-- | The algorithms by which the /square-root/ has been implemented.+data Algorithm+ = BakhshaliApproximation -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Bakhshali_approximation>+ | ContinuedFraction -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion>.+ | HalleysMethod -- ^ <http://en.wikipedia.org/wiki/Halley%27s_method>.+ | NewtonRaphsonIteration -- ^ <http://en.wikipedia.org/wiki/Newton%27s_method>.+ | TaylorSeries Terms -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.+ deriving (Eq, Read, Show)++instance ToolShed.Defaultable.Defaultable Algorithm where+ defaultValue = NewtonRaphsonIteration++-- | Returns an improved estimate for the /square-root/ of the specified value, to the required precision, using the supplied initial estimate..+type ProblemSpecification operand+ = Math.SquareRoot.Estimate+ -> Math.Precision.DecimalDigits -- ^ The required precision.+ -> operand -- ^ The value for which to find the /square-root/.+ -> Math.SquareRoot.Result++instance Math.SquareRoot.Algorithmic Algorithm where+ squareRootFrom _ _ _ 0 = 0+ squareRootFrom _ _ _ 1 = 1+ squareRootFrom algorithm estimate@(x, decimalDigits) requiredDecimalDigits y+ | decimalDigits >= requiredDecimalDigits = x+ | requiredDecimalDigits <= 0 = error $ "Factory.Math.Implementations.SquareRoot.squareRootFrom:\tinvalid number of required decimal digits; " ++ show requiredDecimalDigits+ | y < 0 = error $ "Factory.Math.Implementations.SquareRoot.squareRootFrom:\tthere's no real square-root of " ++ show y+ | otherwise = (+ case algorithm of+ ContinuedFraction -> squareRootByContinuedFraction+ _ -> squareRootByIteration algorithm+ ) estimate requiredDecimalDigits y++instance Math.SquareRoot.Iterator Algorithm where+ step BakhshaliApproximation y x+ | dy == 0 = x -- The estimate was precise.+ | otherwise = x' - dx' -- Correct the estimate.+ where+ dy, dydx, dx, x', dydx', dx' :: Math.SquareRoot.Result+ dy = Math.SquareRoot.getDiscrepancy y x+ dydx = 2 * x+ dx = dy / dydx+ x' = x + dx -- Identical to Newton-Raphson iteration.+ dydx' = 2 * x'+ dx' = Math.Power.square dx / dydx'++{-+ * /Halley's/ method; <http://mathworld.wolfram.com/HalleysMethod.html>++> X(n+1) = Xn - f(Xn) / [f'(Xn) - f''(Xn) * f(Xn) / 2 * f'(Xn)]+> => Xn - (Xn^2 - Y) / [2Xn - 2 * (Xn^2 - Y) / 2 * 2Xn] where Y = X^2, f(X) = X^2 - Y, f'(X) = 2X, f''(X) = 2+> => Xn - 1 / [2Xn / (Xn^2 - Y) - 1 / 2Xn]+-}+ step HalleysMethod y x+ | dy == 0 = x -- The estimate was precise.+ | otherwise = x - dx -- Correct the estimate.+ where+ dy, dydx, dx :: Math.SquareRoot.Result+ dy = negate $ Math.SquareRoot.getDiscrepancy y x -- Use the estimate to determine the error in 'y'.+ dydx = 2 * x -- The gradient, at the estimated value 'x'.+ dx = recip $ dydx / dy - recip dydx++-- step NewtonRaphsonIteration y x = (x + toRational y / x) / 2 -- This is identical to the /Babylonian Method/.+-- step NewtonRaphsonIteration y x = x / 2 + toRational y / (2 * x) -- Faster.+ step NewtonRaphsonIteration y x = x / 2 + (toRational y / 2) / x -- Faster still.++ step (TaylorSeries terms) y x = squareRootByTaylorSeries terms y x++ step algorithm _ _ = error $ "Factory.Math.Implementations.SquareRoot.step:\tinappropriate algorithm; " ++ show algorithm++ convergenceOrder BakhshaliApproximation = Math.Precision.quarticConvergence+ convergenceOrder ContinuedFraction = Math.Precision.linearConvergence+ convergenceOrder HalleysMethod = Math.Precision.cubicConvergence+ convergenceOrder NewtonRaphsonIteration = Math.Precision.quadraticConvergence+ convergenceOrder (TaylorSeries terms) = terms -- The order of convergence, per iteration, equals the number of terms in the series on each iteration.++{- |+ * Uses /continued-fractions/, to iterate towards the principal /square-root/ of the specified positive integer;+ <http://en.wikipedia.org/wiki/Solving_quadratic_equations_with_continued_fractions>,+ <http://en.wikipedia.org/wiki/Generalized_continued_fraction#Roots_of_positive_numbers>,+ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion>.+ <http://www.myreckonings.com/Dead_Reckoning/Online/Materials/General%20Method%20for%20Extracting%20Roots.pdf>++ * The convergence <http://en.wikipedia.org/wiki/Rate_of_convergence> of the /continued-fraction/ is merely /1st order/ (linear).+-}+squareRootByContinuedFraction :: Real operand => ProblemSpecification operand+squareRootByContinuedFraction (initialEstimate, initialDecimalDigits) requiredDecimalDigits y = initialEstimate + (convergents initialEstimate !! Math.Precision.getTermsRequired (10 ^^ negate initialDecimalDigits) requiredDecimalDigits) where+ convergents :: Math.SquareRoot.Result -> [Math.SquareRoot.Result]+ convergents x = iterate ((Math.SquareRoot.getDiscrepancy y x /) . ((2 * x) +)) 0++{- |+ * The constant coefficients of the /Taylor-series/ for a /square-root/; <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.++ * @ ((-1)^n * factorial(2*n)) / ((1 - 2*n) * 4^n * factorial(n^2)) @.+-}+taylorSeriesCoefficients :: Fractional f => [f]+taylorSeriesCoefficients = zipWith (+ \powers n -> let+ doubleN = 2 * n+ product' = Data.PrimeFactors.product' (recip 2) {-arbitrary-} 10 {-arbitrary-}+ in uncurry (/) . (+ fromIntegral . product' *** fromIntegral . (* ((1 - doubleN) * powers)) . product'+ ) $ Math.Implementations.Factorial.primeFactors doubleN >/< Math.Implementations.Factorial.primeFactors n >^ 2+ ) (+ iterate (* negate 4) 1 -- (-4)^n+ ) [0 :: Integer ..] -- n++{- |+ * Returns the /Taylor-series/ for the /square-root/ of the specified value, to any requested number of terms.++ * <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.++ * The convergence of the series is merely /linear/,+ in that each term increases the precision, by a constant number of decimal places, equal to the those in the original estimate.++ * By feeding-back the improved estimate, to form a new series, the order of convergence, on each successive iteration,+ becomes proportional to the number of terms;++> Terms Convergence+> ===== ===========+> 2 terms /quadratic/+> 3 terms /cubic/+-}+squareRootByTaylorSeries :: Real operand+ => Terms -- ^ The number of terms of the infinite series, to evaluate.+ -> operand -- ^ The value for which the /square-root/ is required.+ -> Math.SquareRoot.Result -- ^ An initial estimate.+ -> Math.SquareRoot.Result+squareRootByTaylorSeries _ _ 0 = error "Factory.Math.Implementations.SquareRoot.squareRootByTaylorSeries:\talgorithm can't cope with estimated value of zero."+squareRootByTaylorSeries terms y x+ | terms < 2 = error $ "Factory.Math.Implementations.SquareRoot.squareRootByTaylorSeries:\tinvalid number of terms; " ++ show terms+ | otherwise = Math.Summation.sumR' . take terms . zipWith (*) taylorSeriesCoefficients $ iterate (* relativeError) x+ where+ relativeError :: Math.SquareRoot.Result+ relativeError = pred $ toRational y / Math.Power.square x -- Pedantically, this is the error in y, which is twice the magnitude of the error in x.++-- | Iterates from the estimated value, towards the /square-root/, a sufficient number of times to achieve the required accuracy.+squareRootByIteration :: Real operand => Algorithm -> ProblemSpecification operand+squareRootByIteration algorithm (initialEstimate, initialDecimalDigits) requiredDecimalDigits y = iterate (Math.SquareRoot.step algorithm y) initialEstimate !! Math.Precision.getIterationsRequired (Math.SquareRoot.convergenceOrder algorithm) initialDecimalDigits requiredDecimalDigits+
+ src-lib/Factory/Math/MultiplicativeOrder.hs view
@@ -0,0 +1,66 @@+{-+ Copyright (C) 2011 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@] Exports the /Multiplicative Order/ of an integer, in a specific /modular/ arithmetic.++-}++module Factory.Math.MultiplicativeOrder(+-- * Functions+ multiplicativeOrder+) where++import qualified Control.DeepSeq+import qualified Factory.Data.Exponential as Data.Exponential+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Primality as Math.Primality+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation++{- |+ * The smallest positive integral power to which the specified integral base must be raised,+ to be congruent with one, in the specified /modular/ arithmetic.++ * Based on <http://rosettacode.org/wiki/Multiplicative_order#Haskell>.++ * <http://en.wikipedia.org/wiki/Multiplicative_order>.++ * <http://mathworld.wolfram.com/MultiplicativeOrder.html>.+-}+multiplicativeOrder :: (Math.PrimeFactorisation.Algorithmic primeFactorisationAlgorithm, Control.DeepSeq.NFData i, Integral i, Show i)+ => primeFactorisationAlgorithm+ -> i -- ^ Base.+ -> i -- ^ Modulus.+ -> i -- ^ Result.+multiplicativeOrder primeFactorisationAlgorithm base modulus+ | modulus < 2 = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\tinvalid modulus; " ++ show modulus+ | not $ Math.Primality.areCoprime base modulus = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\targuments aren't coprime; " ++ show (base, modulus)+ | otherwise = foldr (lcm . multiplicativeOrder') 1 $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm modulus -- Combine the /multiplicative order/ of the constituent /prime-factors/.+ where+-- multiplicativeOrder' :: (Control.DeepSeq.NFData i, Integral i) => Data.Exponential.Exponential i -> i+ multiplicativeOrder' e = product . map (+ \e' -> let+ d :: Int+ d = length . takeWhile (/= 1) . iterate (+ \y -> Math.Power.raiseModulo y (Data.Exponential.getBase e') pk+ ) $ Math.Power.raiseModulo base (totient `div` Data.Exponential.evaluate e') pk+ in Data.Exponential.getBase e' ^ d+ ) $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm totient where+ pk = Data.Exponential.evaluate e+ totient = Math.PrimeFactorisation.primePowerTotient e+
+ src-lib/Factory/Math/PerfectPower.hs view
@@ -0,0 +1,100 @@+{-+ Copyright (C) 2011 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@] Exports functions related to /perfect powers/.+-}++module Factory.Math.PerfectPower(+-- * Functions+ maybeSquareNumber,+-- ** Predicates+ isPerfectPower+-- isPerfectPowerInt+) where++import qualified Data.IntSet+import qualified Data.Set+import qualified Factory.Math.Power as Math.Power++{- |+ * Returns @(Just . sqrt)@ if the specified integer is a /square number/ (AKA /perfect square/).++ * <http://en.wikipedia.org/wiki/Square_number>.++ * <http://mathworld.wolfram.com/SquareNumber.html>.++ * @(Math.Power.square . sqrt)@ is expensive, so the modulus of the operand is tested first, in an attempt to prove it isn't a /perfect square/.+ The set of tests, and the valid moduli within each test, are ordered to maximize the rate of failure-detection.+-}+maybeSquareNumber :: Integral i => i -> Maybe i+maybeSquareNumber i+-- | i < 0 = Nothing -- This function is performance-sensitive, but this test is neither strictly nor frequently required.+ | all (\(modulus, valid) -> rem i modulus `elem` valid) [+-- -- Distribution of moduli amongst perfect squares Cumulative failure-detection.+ (16, [0,1,4,9]), -- All moduli are equally likely. 75%+ (9, [0,1,4,7]), -- Zero occurs 33%, the others only 22%. 88%+ (17, [1,2,4,8,9,13,15,16,0]), -- Zero only occurs 5.8%, the others 11.8%. 94%+-- These additional tests, aren't always cost-effective.+ (13, [1,3,4,9,10,12,0]), -- Zero only occurs 7.7%, the others 15.4%. 97%+ (7, [1,2,4,0]), -- Zero only occurs 14.3%, the others 28.6%. 98%+ (5, [1,4,0]) -- Zero only occurs 20%, the others 40%. 99%++-- ] && fromIntegral iSqrt == sqrt' = Just iSqrt -- CAVEAT: erroneously True for 187598574531033120 (187598574531033121 is square).+ ] && Math.Power.square iSqrt == i = Just iSqrt+ | otherwise = Nothing+ where+ sqrt' :: Double+ sqrt' = sqrt $ fromIntegral i++ iSqrt = round sqrt'++{- |+ * An integer @(> 1)@ which can be expressed as an integral power @(> 1)@ of a smaller /natural/ number.++ * CAVEAT: /zero/ and /one/ are normally excluded from this set.++ * <http://en.wikipedia.org/wiki/Perfect_power>.++ * <http://mathworld.wolfram.com/PerfectPower.html>.++ * A generalisation of the concept of /perfect squares/, in which only the exponent '2' is significant.+-}+isPerfectPower :: Integral i => i -> Bool+isPerfectPower i+ | i < Math.Power.square 2 = False+ | otherwise = i `Data.Set.member` foldr (+ \n set -> if n `Data.Set.member` set+ then set+-- else Data.Set.union set . Data.Set.fromDistinctAscList . takeWhile (<= i) . iterate (* n) $ Math.Power.square n+ else foldr Data.Set.insert set . takeWhile (<= i) . iterate (* n) $ Math.Power.square n -- Faster.+ ) Data.Set.empty [2 .. round $ sqrt (fromIntegral i :: Double)]++{-# NOINLINE isPerfectPower #-}+{-# RULES "isPerfectPower/Int" isPerfectPower = isPerfectPowerInt #-}++-- | A specialisation of 'isPerfectPower'.+isPerfectPowerInt :: Int -> Bool+isPerfectPowerInt i+ | i < Math.Power.square 2 = False+ | otherwise = i `Data.IntSet.member` foldr (+ \n set -> if n `Data.IntSet.member` set+ then set+ else foldr Data.IntSet.insert set . takeWhile (<= i) . iterate (* n) $ Math.Power.square n+ ) Data.IntSet.empty [2 .. round $ sqrt (fromIntegral i :: Double)]+
+ src-lib/Factory/Math/Pi.hs view
@@ -0,0 +1,100 @@+{-+ Copyright (C) 2011 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 the classes of /Pi/-algorithm which have been implemented.+-}++module Factory.Math.Pi(+-- * Type-classes+ Algorithmic(..),+-- * Types+-- ** Data-types+ Category(..)+) where++import qualified Factory.Math.Precision as Math.Precision+import qualified ToolShed.Defaultable++{- |+ * Defines the methods expected of a /Pi/-algorithm.++ * Most of the implementations naturally return a 'Rational', but the spigot-algorithms naturally produce a @[Int]@;+ though representing /Pi/ as a big integer with the decimal point removed is clearly incorrect.++ * Since representing /Pi/ as either a 'Rational' or promoted to an 'Integer', is inconvenient, an alternative decimal 'String'-representation is provided.+-}+class Algorithmic algorithm where+ openR :: algorithm -> Math.Precision.DecimalDigits -> Rational -- ^ Returns the value of /Pi/ as a 'Rational'.++ openI :: algorithm -> Math.Precision.DecimalDigits -> Integer -- ^ Returns the value of /Pi/, promoted by the required precision to form an integer.+ openI _ 1 = 3+ openI algorithm decimalDigits+ | decimalDigits <= 0 = error $ "Factory.Math.Pi.openI:\tinsufficient decimalDigits=" ++ show decimalDigits+ | otherwise = round . Math.Precision.promote (openR algorithm decimalDigits) $ pred decimalDigits++ openS :: algorithm -> Math.Precision.DecimalDigits -> String -- ^ Returns the value of /Pi/ as a decimal 'String'.+ openS _ 1 = "3"+ openS algorithm decimalDigits+ | decimalDigits <= 0 = ""+ | decimalDigits <= 16 = take (succ decimalDigits) $ show (pi :: Double)+ | otherwise = "3." ++ tail (show $ openI algorithm decimalDigits) -- Insert a decimal point.++-- | Categorises the various algorithms.+data Category agm bbp borwein ramanujan spigot+ = AGM agm -- ^ Algorithms based on the /Arithmetic-geometric Mean/.+ | BBP bbp -- ^ <http://en.wikipedia.org/wiki/Bailey%E2%80%93Borwein%E2%80%93Plouffe_formula>.+ | Borwein borwein -- ^ <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>.+ | Ramanujan ramanujan -- ^ <http://www.pi314.net/eng/ramanujan.php>.+ | Spigot spigot -- ^ Algorithms from which the digits of /Pi/ slowly drip, one by one.+ deriving (Eq, Read, Show)++instance (+ ToolShed.Defaultable.Defaultable agm,+ ToolShed.Defaultable.Defaultable bbp,+ ToolShed.Defaultable.Defaultable borwein,+ ToolShed.Defaultable.Defaultable ramanujan,+ ToolShed.Defaultable.Defaultable spigot+ ) => ToolShed.Defaultable.Defaultable (Category agm bbp borwein ramanujan spigot) where+ defaultValue = BBP ToolShed.Defaultable.defaultValue++instance (+ Algorithmic agm,+ Algorithmic bbp,+ Algorithmic borwein,+ Algorithmic ramanujan,+ Algorithmic spigot+ ) => Algorithmic (Category agm bbp borwein ramanujan spigot) where+ openR algorithm decimalDigits+ | decimalDigits <= 0 = error $ "Factory.Math.Pi.openR:\tinsufficient decimalDigits=" ++ show decimalDigits+ | decimalDigits <= 16 = Math.Precision.simplify (pred decimalDigits) (pi :: Double)+ | otherwise = (+ case algorithm of+ AGM agm -> openR agm+ BBP bbp -> openR bbp+ Borwein borwein -> openR borwein+ Ramanujan ramanujan -> openR ramanujan+ Spigot spigot -> openR spigot+ ) decimalDigits++ openI _ 1 = 3+ openI (Spigot spigot) decimalDigits = openI spigot decimalDigits+ openI algorithm decimalDigits+ | decimalDigits <= 0 = error $ "Factory.Math.Pi.openI:\tinsufficient decimalDigits=" ++ show decimalDigits+ | otherwise = round . Math.Precision.promote (openR algorithm decimalDigits) $ pred decimalDigits+
+ src-lib/Factory/Math/Power.hs view
@@ -0,0 +1,84 @@+{-+ Copyright (C) 2011 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@] Exports functions involving integral powers.+-}++module Factory.Math.Power(+-- * Functions+ square,+ squaresFrom,+ cube,+ cubeRoot,+ raiseModulo+) where++-- | Mainly for convenience.+square :: Num n => n -> n+square x = x ^ (2 :: Int) -- CAVEAT: this could be eta-reduced, but it won't then inline when called with a single argument.++{-# INLINE square #-}++-- | Just for convenience.+cube :: Num n => n -> n+cube = (^ (3 :: Int))++{- |+ * Iteratively generate sequential /squares/, from the specified initial value,+ based on the fact that @(x + 1)^2 = x^2 + 2 * x + 1@.++ * The initial value doesn't need to be either positive or integral.+-}+squaresFrom :: (Enum n, Num n)+ => n -- ^ Lower bound.+ -> [(n, n)] -- ^ @ [(n, n^2)] @.+squaresFrom from = iterate (\(x, y) -> (succ x, succ $ y + 2 * x)) (from, square from)++-- | Just for convenience.+cubeRoot :: Double -> Double+cubeRoot = (** recip 3)++{- |+ * Raise an arbitrary number to the specified positive integral power, using /modular/ arithmetic.++ * Implements exponentiation as a sequence of either /squares/ or multiplications by the base;+ <http://en.wikipedia.org/wiki/Exponentiation_by_squaring>.++ * <http://en.wikipedia.org/wiki/Modular_exponentiation>.+-}+raiseModulo :: (Integral i, Integral power, Show power)+ => i -- ^ Base.+ -> power+ -> i -- ^ Modulus.+ -> i -- ^ Result.+raiseModulo _ _ 0 = error "Factory.Math.Power.raiseModulo:\tzero modulus."+raiseModulo _ _ 1 = 0+raiseModulo _ 0 modulus = 1 `mod` modulus+raiseModulo base power modulus+ | base < 0 = (`mod` modulus) . (if even power then id else negate) $ raiseModulo (negate base) power modulus -- Recurse.+ | power < 0 = error $ "Factory.Math.Power.raiseModulo:\tnegative power; " ++ show power+ | first `elem` [0, 1] = first+ | otherwise = slave power+ where+ first = base `mod` modulus++ slave 1 = first+ slave e = (`mod` modulus) . (if r == 0 {-even-} then id else (* base)) . square $ slave q {-recurse-} where+ (q, r) = e `quotRem` 2+
+ src-lib/Factory/Math/Precision.hs view
@@ -0,0 +1,125 @@+{-+ Copyright (C) 2011 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 the unit with which precision is measured, and operations on it.+-}+module Factory.Math.Precision(+-- * Types+-- ** Type-synonyms+ ConvergenceOrder,+ ConvergenceRate,+ DecimalDigits,+-- * Constants+ linearConvergence,+ quadraticConvergence,+ cubicConvergence,+ quarticConvergence,+-- * Functions+ getIterationsRequired,+ getTermsRequired,+ roundTo,+ promote,+ simplify+) where++import qualified Data.Ratio++-- | The /order of convergence/; <http://en.wikipedia.org/wiki/Rate_of_convergence>.+type ConvergenceOrder = Int++-- | The /rate of convergence/; <http://en.wikipedia.org/wiki/Rate_of_convergence>.+type ConvergenceRate = Double++-- | A number of decimal digits; presumably positive.+type DecimalDigits = Int++-- | /Linear/ convergence-rate; which may be qualified by the /rate of convergence/.+linearConvergence :: ConvergenceOrder+linearConvergence = 1++-- | /Quadratic/ convergence-rate.+quadraticConvergence :: ConvergenceOrder+quadraticConvergence = 2++-- | /Cubic/ convergence-rate.+cubicConvergence :: ConvergenceOrder+cubicConvergence = 3++-- | /Quartic/ convergence-rate.+quarticConvergence :: ConvergenceOrder+quarticConvergence = 4++-- | The predicted number of iterations, required to achieve a specific accuracy, at a given /order of convergence/.+getIterationsRequired :: Integral i+ => ConvergenceOrder+ -> DecimalDigits -- ^ The precision of the initial estimate.+ -> DecimalDigits -- ^ The required precision.+ -> i+getIterationsRequired convergenceOrder initialDecimalDigits requiredDecimalDigits+ | initialDecimalDigits <= 0 = error $ "Factory.Math.Precision.getIterationsRequired:\tinsufficient 'initialDecimalDigits'; " ++ show initialDecimalDigits+ | precisionRatio <= 1 = 0+ | otherwise = ceiling $ fromIntegral convergenceOrder `logBase` precisionRatio+ where+ precisionRatio :: Double+ precisionRatio = fromIntegral requiredDecimalDigits / fromIntegral initialDecimalDigits++{- |+ * The predicted number of terms which must be extracted from a series,+ if it is to converge to the required accuracy,+ at the specified linear /convergence-rate/.++ * The /convergence-rate/ of a series, is the error in the series after summation of @(n+1)th@ terms,+ divided by the error after only @n@ terms, as the latter tends to infinity.+ As such, for a /convergent/ series (in which the error get smaller with successive terms), it's value lies in the range @0 .. 1@.++ * <http://en.wikipedia.org/wiki/Rate_of_convergence>.+-}+getTermsRequired :: Integral i+ => ConvergenceRate+ -> DecimalDigits -- ^ The additional number of correct decimal digits.+ -> i+getTermsRequired _ 0 = 0+getTermsRequired convergenceRate requiredDecimalDigits+ | convergenceRate <= 0 || convergenceRate >= 1 = error $ "Factory.Math.Precision.getTermsRequired:\t(0 < convergence-rate < 1); " ++ show convergenceRate+ | requiredDecimalDigits < 0 = error $ "Factory.Math.Precision.getTermsRequired:\t'requiredDecimalDigits' must be positive; " ++ show requiredDecimalDigits+ | otherwise = ceiling $ fromIntegral requiredDecimalDigits / negate (logBase 10 convergenceRate)++-- | Rounds the specified number, to a positive number of 'DecimalDigits'.+roundTo :: (RealFrac a, Fractional f) => DecimalDigits -> a -> f+roundTo decimals = (/ fromInteger promotionFactor) . fromInteger . round . (* fromInteger promotionFactor) where+ promotionFactor :: Integer+ promotionFactor = 10 ^ decimals++-- | Promotes the specified number, by a positive number of 'DecimalDigits'.+promote :: Num n => n -> DecimalDigits -> n+promote x = (* x) . (10 ^)++{- |+ * Reduces a 'Rational' to the minimal form required for the specified number of /fractional/ decimal places;+ irrespective of the number of integral decimal places.++ * A 'Rational' approximation to an irrational number, may be very long, and provide an unknown excess precision.+ Whilst this doesn't sound harmful, it costs in performance and memory-requirement, and being unpredictable isn't actually useful.+-}+simplify :: RealFrac operand+ => DecimalDigits -- ^ The number of places after the decimal point, which are required.+ -> operand+ -> Rational+simplify decimalDigits operand = Data.Ratio.approxRational operand . recip $ 4 * 10 ^ succ decimalDigits -- Tolerate any error less than half the least significant digit required.+
+ src-lib/Factory/Math/Primality.hs view
@@ -0,0 +1,102 @@+{-+ Copyright (C) 2011 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@]++ * Exports a common interface for primality-implementations.++ * Provides utilities for these implementations.+-}++module Factory.Math.Primality(+-- * Type-classes+ Algorithmic(..),+-- * Functions+ carmichaelNumbers,+-- ** Predicates+ areCoprime,+ isFermatWitness,+ isCarmichaelNumber+) where++import qualified Control.DeepSeq+import qualified Factory.Math.Power as Math.Power++-- | Defines the methods expected of a primality-testing algorithm.+class Algorithmic algorithm where+ isPrime :: (Control.DeepSeq.NFData i, Integral i, Show i) => algorithm -> i -> Bool++{- |+ 'True' if the two specified integers are /relatively prime/,+ i.e. if they share no common positive factors except one.++ * @1@ and @-1@ are the only numbers which are /coprime/ to themself.++ * <http://en.wikipedia.org/wiki/Coprime>.++ * <http://mathworld.wolfram.com/RelativelyPrime.html>.+-}+areCoprime :: Integral i => i -> i -> Bool+areCoprime i = (== 1) . gcd i++{- |+ * Tests /Fermat's Little Theorem/ for all applicable values, as a probabilistic primality-test.++ * <http://en.wikipedia.org/wiki/Fermat%27s_little_theorem>.++ * <http://en.wikipedia.org/wiki/Fermat_primality_test>.++ * <http://en.wikipedia.org/wiki/Fermat_pseudoprime>.++ * CAVEAT: this primality-test fails for the /Carmichael numbers/.++ * TODO: confirm that all values must be tested.+-}+isFermatWitness :: (Integral i, Show i) => i -> Bool+isFermatWitness i = not . all isFermatPseudoPrime $ filter (areCoprime i) [2 .. pred i] where+ isFermatPseudoPrime base = Math.Power.raiseModulo base (pred i) i == 1 -- CAVEAT: a /Fermat Pseudo-prime/ must also be a /composite/ number.++{- |+ * A /Carmichael number/ is an /odd/ /composite/ number which satisfies /Fermat's little theorem/.++ * <http://en.wikipedia.org/wiki/Carmichael_number>.++ * <http://mathworld.wolfram.com/CarmichaelNumber.html>.+-}+isCarmichaelNumber :: (+ Algorithmic algorithm,+ Control.DeepSeq.NFData i,+ Integral i,+ Show i+ ) => algorithm -> i -> Bool+isCarmichaelNumber algorithm i = not $ or [+ i <= 2,+ even i,+ isFermatWitness i,+ isPrime algorithm i+ ]++-- | An ordered list of the /Carmichael/ numbers; <http://en.wikipedia.org/wiki/Carmichael_number>.+carmichaelNumbers :: (+ Algorithmic algorithm,+ Control.DeepSeq.NFData i,+ Integral i,+ Show i+ ) => algorithm -> [i]+carmichaelNumbers algorithm = isCarmichaelNumber algorithm `filter` [3, 5 ..]
+ src-lib/Factory/Math/PrimeFactorisation.hs view
@@ -0,0 +1,151 @@+{-+ Copyright (C) 2011 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@]++ * <http://en.wikipedia.org/wiki/Integer_factorization>.++ * Exports a common interface to permit decomposition of positive integers,+ into the unique combination of /prime/-factors known to exist according to the /Fundamental Theorem of Arithmetic/; <http://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic>.++ * Leveraging this abstract capability, it derives the /smoothness/, /power-smoothness/, /omega/-numbers and /square-free/ integers.++ * Filters the list of /regular-numbers/ from the list of /smoothness/.++ * CAVEAT: to avoid wasting time, it may be advantageous to check /Factory.Math.Primality.isPrime/ first.+-}++module Factory.Math.PrimeFactorisation(+-- * Type-classes+ Algorithmic(..),+-- * Functions+ maxBoundPrimeFactor,+ smoothness,+ powerSmoothness,+ regularNumbers,+ primePowerTotient,+ eulersTotient,+ omega,+ squareFree+) where++import qualified Control.DeepSeq+import qualified Data.List+import qualified Factory.Data.Exponential as Data.Exponential+import qualified Factory.Data.PrimeFactors as Data.PrimeFactors++-- | Defines the methods expected of a /factorisation/-algorithm.+class Algorithmic algorithm where+ primeFactors :: (Control.DeepSeq.NFData base, Integral base)+ => algorithm+ -> base -- ^ The operand+ -> Data.PrimeFactors.Factors base Int {-arbitrarily-}++{- |+ * The upper limit for a prime to be considered as a candidate factor of the specified number.++ * One might naively think that this limit is @(x `div` 2)@ for an even number,+ but though a prime-factor /greater/ than the /square-root/ of the number can exist,+ its smaller /cofactor/ decomposes to a prime which must be less than the /square-root/.++ * NB: rather then using @(primeFactor <= sqrt numerator)@ to filter the candidate prime-factors of a given numerator,+ one can alternatively use @(numerator >= primeFactor ^ 2)@ to filter what can potentially be factored by a given prime-factor.++ * CAVEAT: suffers from rounding-errors, though no consequence has been witnessed.+-}+maxBoundPrimeFactor :: Integral i => i -> i+maxBoundPrimeFactor = floor . (sqrt :: Double -> Double) . fromIntegral++{- |+ * A constant, zero-indexed, conceptually infinite, list, of the /smooth/ness of all positive integers.++ * <http://en.wikipedia.org/wiki/Smooth_number>.++ * <http://mathworld.wolfram.com/SmoothNumber.html>.+-}+smoothness :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]+smoothness algorithm = 0 : map (Data.Exponential.getBase . last . primeFactors algorithm) [1 ..]++{- |+ * A constant, zero-indexed, conceptually infinite, list of the /power-smooth/ness of all positive integers.++ * <http://en.wikipedia.org/wiki/Smooth_number#Powersmooth_numbers>.+-}+powerSmoothness :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]+powerSmoothness algorithm = 0 : map (maximum . map Data.Exponential.evaluate . primeFactors algorithm) [1 ..]++{- |+ * Filters 'smoothness', to derive the constant list of /Hamming-numbers/.++ * <http://en.wikipedia.org/wiki/Regular_number>.+-}+regularNumbers :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]+regularNumbers algorithm = map fst . filter ((<= (5 :: Integer)) . snd) . zip [1 ..] . tail $ smoothness algorithm++{- |+ * /Euler's Totient/ for a /power/ of a /prime/-number.++ * By /Olofsson/; @(phi(n^k) = n^(k - 1) * phi(n))@+ and since @(phi(prime) = prime - 1)@++ * CAVEAT: checks neither the primality nor the bounds of the specified value; therefore for internal use only.+-}+primePowerTotient :: (Integral base, Integral exponent) => Data.Exponential.Exponential base exponent -> base+primePowerTotient (base, exponent') = pred base * base ^ pred exponent'++{- |+ * The number of /coprimes/ less than or equal to the specified positive integer.++ * <http://en.wikipedia.org/wiki/Euler%27s_totient_function>.++ * <http://mathworld.wolfram.com/TotientFunction.html>.++ * AKA /EulerPhi/.+-}+eulersTotient :: (+ Algorithmic algorithm,+ Control.DeepSeq.NFData i,+ Integral i,+ Show i+ ) => algorithm -> i -> i+eulersTotient _ 1 = 1+eulersTotient algorithm i+ | i <= 0 = error $ "Factory.Math.PrimeFactorisation.eulersTotient:\tundefined for; " ++ show i+ | otherwise = product . map primePowerTotient $ primeFactors algorithm i++{- |+ * A constant, zero-indexed, conceptually infinite, list of the /small omega/ numbers (i.e. the number of /distinct/ prime factors); cf. /big omega/.++ * <http://oeis.org/wiki/Omega%28n%29,_number_of_distinct_primes_dividing_n>.++ * <http://mathworld.wolfram.com/DistinctPrimeFactors.html>++ * <http://planetmath.org/encyclopedia/NumberOfDistinctPrimeFactorsFunction.html>.+-}+omega :: (Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i) => algorithm -> [i]+omega algorithm = map (Data.List.genericLength . primeFactors algorithm) [0 :: Integer ..]++{- |+ * A constant, conceptually infinite, list of the /square-free/ numbers, i.e. those which aren't divisible by any /perfect square/.++ * <http://en.wikipedia.org/wiki/Square-free_integer>.+-}+squareFree :: (Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i) => algorithm -> [i]+squareFree algorithm = filter (all (== 1) . map Data.Exponential.getExponent . primeFactors algorithm) [1 ..]+
+ src-lib/Factory/Math/Primes.hs view
@@ -0,0 +1,64 @@+{-+ Copyright (C) 2011 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@] Exports a common interface for implementations of /prime-number/ generators.+-}++module Factory.Math.Primes(+-- * Types-classes+ Algorithmic(..),+-- * Functions+ primorial,+ mersenneNumbers+) where++import qualified Control.DeepSeq+import qualified Data.Array.IArray++-- | Defines the methods expected of a /prime-number/ generator.+class Algorithmic algorithm where+ primes :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i) => algorithm -> [i] -- ^ Returns the constant, infinite, list of primes.++{- |+ * Returns the constant list, defining the /Primorial/.++ * <http://en.wikipedia.org/wiki/Primorial>.++ * <http://mathworld.wolfram.com/Primorial.html>.+-}+primorial :: (+ Algorithmic algorithm,+ Control.DeepSeq.NFData i,+ Data.Array.IArray.Ix i,+ Integral i+ ) => algorithm -> [i]+primorial = scanl (*) 1 . primes++{- |+ * Returns the constant ordered infinite list of /Mersenne numbers/.++ * Only the subset composed from a prime exponent is returned; which is a strict superset of the /Mersenne Primes/.++ * <http://en.wikipedia.org/wiki/Mersenne_prime>.++ * <http://mathworld.wolfram.com/MersenneNumber.html>+-}+mersenneNumbers :: (Algorithmic algorithm, Integral i) => algorithm -> [i]+mersenneNumbers algorithm = map (pred . (2 ^)) (primes algorithm :: [Int]) -- Whilst the exponentiation could be parallelised, not all values are known to be required.+
+ src-lib/Factory/Math/Probability.hs view
@@ -0,0 +1,255 @@+{-+ 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@] Functions for probability-distributions.++ [@CAVEAT@] Because data-constructors are exposed, 'ToolShed.SelfValidate.isValid' need not be called.+-}++module Factory.Math.Probability(+-- * Type-classes+ Distribution(..),+-- * Types+-- ** Data-types+ ContinuousDistribution(..),+ DiscreteDistribution(..),+-- * Functions+ maxPreciseInteger,+-- minPositiveFloat,+ boxMullerTransform,+-- reProfile,+ generateStandardizedNormalDistribution,+ generateContinuousPopulation,+-- generatePoissonDistribution,+ generateDiscretePopulation+) where++import qualified Control.Arrow+import Control.Arrow((***), (&&&))+import qualified Factory.Data.Interval as Data.Interval+import qualified Factory.Math.Power as Math.Power+import qualified System.Random+import qualified ToolShed.Data.List+import qualified ToolShed.Data.Pair+import qualified ToolShed.SelfValidate++-- | The maximum integer which can be accurately represented as a Double.+maxPreciseInteger :: RealFloat a => a -> Integer+maxPreciseInteger = (2 ^) . floatDigits++{- |+ * Determines the minimum positive floating-point number, which can be represented by using the parameter's type.++ * Only the type of the parameter is relevant, not its value.+-}+minPositiveFloat :: RealFloat a => a -> a+minPositiveFloat = encodeFloat 1 . uncurry (-) . (fst . floatRange &&& floatDigits)++-- | Describes /continuous probability-distributions/; <http://en.wikipedia.org/wiki/List_of_probability_distributions#Continuous_distributions>.+data ContinuousDistribution parameter+ = ExponentialDistribution parameter {-lambda-} -- ^ Defines an /Exponential/-distribution with a particular /lambda/; <http://en.wikipedia.org/wiki/Exponential_distribution>.+ | LogNormalDistribution parameter {-location-} parameter {-scale2-} -- ^ Defines a distribution whose logarithm is normally distributed with a particular /mean/ & /variance/; <http://en.wikipedia.org/wiki/Lognormal>.+ | NormalDistribution parameter {-mean-} parameter {-variance-} -- ^ Defines a /Normal/-distribution with a particular /mean/ & /variance/; <http://en.wikipedia.org/wiki/Normal_distribution>.+ | UniformDistribution (Data.Interval.Interval parameter) -- ^ Defines a /Uniform/-distribution within a /closed interval/; <http://en.wikipedia.org/wiki/Uniform_distribution>.+ deriving (Eq, Read, Show)++instance (Floating parameter, Ord parameter, Show parameter) => ToolShed.SelfValidate.SelfValidator (ContinuousDistribution parameter) where+ getErrors probabilityDistribution = ToolShed.SelfValidate.extractErrors $ case probabilityDistribution of+ ExponentialDistribution lambda -> [(lambda <= 0, "'lambda' must exceed zero; " ++ show probabilityDistribution ++ ".")]+ LogNormalDistribution location scale2 -> let+ maxParameter = log . fromInteger $ maxPreciseInteger (undefined :: Double)+ in [+ (scale2 <= 0, "'scale' must exceed zero; " ++ show probabilityDistribution ++ "."),+ (location > maxParameter || scale2 > maxParameter, "loss of precision will result from either 'location' or 'scale^2' exceeding '" ++ show maxParameter ++ "'; " ++ show probabilityDistribution ++ ".")+ ]+ NormalDistribution _ variance -> [(variance <= 0, "variance must exceed zero; " ++ show probabilityDistribution ++ ".")]+ UniformDistribution interval -> [(Data.Interval.isReversed interval, "reversed interval='" ++ show probabilityDistribution ++ "'.")]++-- | Describes /discrete probability-distributions/; <http://en.wikipedia.org/wiki/List_of_probability_distributions#Discrete_distributions>.+data DiscreteDistribution parameter+ = PoissonDistribution parameter {-lambda-} -- ^ Defines an /Poisson/-distribution with a particular /lambda/; <http://en.wikipedia.org/wiki/Poisson_distribution>.+ | ShiftedGeometricDistribution parameter {-probability-} -- ^ Defines an /Geometric/-distribution with a particular probability of success; <http://en.wikipedia.org/wiki/Geometric_distribution>.+ deriving (Eq, Read, Show)++instance (Num parameter, Ord parameter, Show parameter) => ToolShed.SelfValidate.SelfValidator (DiscreteDistribution parameter) where+ getErrors probabilityDistribution = ToolShed.SelfValidate.extractErrors $ case probabilityDistribution of+ PoissonDistribution lambda -> [(lambda <= 0, "'lambda' must exceed zero; " ++ show probabilityDistribution ++ ".")]+ ShiftedGeometricDistribution probability -> [(any ($ probability) [(<= 0), (> 1)], "probability must be in the semi-closed unit-interval (0, 1]; " ++ show probabilityDistribution ++ ".")]++-- | Defines a common interface for probability-distributions.+class Distribution probabilityDistribution where+ generatePopulation+ :: (Fractional sample, System.Random.RandomGen randomGen)+ => probabilityDistribution+ -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.+ -> [sample] -- ^ CAVEAT: the integers generated for discrete distributions are represented by a fractional type; use 'generateDiscretePopulation' if this is a problem.++ getMean :: Fractional mean => probabilityDistribution -> mean -- ^ The theoretical mean.++ getStandardDeviation :: Floating standardDeviation => probabilityDistribution -> standardDeviation-- ^ The theoretical standard-deviation.+ getStandardDeviation = sqrt . getVariance -- Default implementation.++ getVariance :: Floating variance => probabilityDistribution -> variance -- ^ The theoretical variance.+ getVariance = Math.Power.square . getStandardDeviation -- Default implementation.++instance (RealFloat parameter, Show parameter, System.Random.Random parameter) => Distribution (ContinuousDistribution parameter) where+ generatePopulation probabilityDistribution = map realToFrac {-parameter -> sample-} . generateContinuousPopulation probabilityDistribution++ getMean (ExponentialDistribution lambda) = realToFrac $ recip lambda+ getMean (LogNormalDistribution location scale2) = realToFrac . exp . (+ location) $ scale2 / 2+ getMean (NormalDistribution mean _) = realToFrac mean+ getMean (UniformDistribution (minParameter, maxParameter)) = realToFrac $ (minParameter + maxParameter) / 2++ getVariance (ExponentialDistribution lambda) = realToFrac . recip $ Math.Power.square lambda+ getVariance (LogNormalDistribution location scale2) = realToFrac $ (exp scale2 - 1) * exp (2 * location + scale2) -- NB: for standard-deviation == mean, use scale^2 == ln 2.+ getVariance (NormalDistribution _ variance) = realToFrac variance+ getVariance (UniformDistribution (minParameter, maxParameter)) = realToFrac $ Math.Power.square (maxParameter - minParameter) / 12++instance (RealFloat parameter, Show parameter, System.Random.Random parameter) => Distribution (DiscreteDistribution parameter) where+ generatePopulation probabilityDistribution = map fromInteger . generateDiscretePopulation probabilityDistribution++ getMean (PoissonDistribution lambda) = realToFrac lambda+ getMean (ShiftedGeometricDistribution probability) = realToFrac $ recip probability++ getVariance (PoissonDistribution lambda) = realToFrac lambda+ getVariance (ShiftedGeometricDistribution probability) = realToFrac $ (1 - probability) / Math.Power.square probability++{- |+ * Converts a pair of independent /uniformly distributed/ random numbers, within the /semi-closed unit interval/ /(0,1]/,+ to a pair of independent /normally distributed/ random numbers, of standardized /mean/=0, and /variance/=1.++ * <http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>.+-}+boxMullerTransform :: (+ Floating f,+ Ord f,+ Show f+ )+ => (f, f) -- ^ Independent, /uniformly distributed/ random numbers, which must be within the /semi-closed unit interval/, /(0,1]/.+ -> (f, f) -- ^ Independent, /normally distributed/ random numbers, with standardized /mean/=0 and /variance/=1.+boxMullerTransform cartesian+ | not . uncurry (&&) $ ToolShed.Data.Pair.mirror inSemiClosedUnitInterval cartesian = error $ "Factory.Math.Probability.boxMullerTransform:\tspecified Cartesian coordinates, must be within semi-closed unit-interval (0, 1]; " ++ show cartesian+ | otherwise = polarToCartesianTransform $ (sqrt . negate . (* 2) . log *** (* 2) . (* pi)) cartesian+ where+ inSemiClosedUnitInterval :: (Num n, Ord n) => n -> Bool+ inSemiClosedUnitInterval = uncurry (&&) . ((> 0) &&& (<= 1))++ polarToCartesianTransform :: Floating f => (f, f) -> (f, f)+ polarToCartesianTransform = uncurry (*) . Control.Arrow.second cos &&& uncurry (*) . Control.Arrow.second sin++{- |+ * Uses the supplied random-number generator,+ to generate a conceptually infinite list, of /normally distributed/ random numbers, with standardized /mean/=0, and /variance/=1.++ * <http://en.wikipedia.org/wiki/Normal_distribution>, <http://mathworld.wolfram.com/NormalDistribution.html>.+-}+generateStandardizedNormalDistribution :: (+ RealFloat f,+ Show f,+ System.Random.Random f,+ System.Random.RandomGen randomGen+ ) => randomGen -> [f]+generateStandardizedNormalDistribution = ToolShed.Data.List.linearise . uncurry (zipWith $ curry boxMullerTransform) . ToolShed.Data.Pair.mirror (+ System.Random.randomRs (minPositiveFloat undefined, 1)+ ) . System.Random.split++-- | Stretches and shifts a /distribution/ to achieve the required /mean/ and /standard-deviation/.+reProfile :: (Distribution distribution, Floating n) => distribution -> [n] -> [n]+reProfile distribution = map ((+ getMean distribution) . (* getStandardDeviation distribution))++-- | Uses the supplied random-number generator, to generate a conceptually infinite population, with the specified continuous probability-distribution.+generateContinuousPopulation :: (+ RealFloat f,+ Show f,+ System.Random.Random f,+ System.Random.RandomGen randomGen+ )+ => ContinuousDistribution f+ -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.+ -> [f]+generateContinuousPopulation probabilityDistribution randomGen+ | not $ ToolShed.SelfValidate.isValid probabilityDistribution = error $ "Factory.Math.Probability.generateContinuousPopulation:\t" ++ ToolShed.SelfValidate.getFirstError probabilityDistribution+ | otherwise = (+ case probabilityDistribution of+ ExponentialDistribution lambda -> let+ quantile = (/ lambda) . negate . log . (1 -) -- <http://en.wikipedia.org/wiki/Quantile_function>.+ in map quantile . System.Random.randomRs (0, 1)+ LogNormalDistribution location scale2 -> map (+ exp . (+ location) . (* sqrt scale2) -- Stretch the standard-deviation & re-locate the mean to that specified for the log-space, then return to the original coordinates.+ ) . generateStandardizedNormalDistribution+ NormalDistribution _ _ -> reProfile probabilityDistribution . generateStandardizedNormalDistribution+ UniformDistribution interval -> System.Random.randomRs interval+ ) randomGen++{- |+ * Uses the supplied random-number generator,+ to generate a conceptually infinite population, of random integers conforming to the /Poisson distribution/; <http://en.wikipedia.org/wiki/Poisson_distribution>.++ * CAVEAT:+ uses an algorithm by Knuth, which having a /linear time-complexity/ in /lambda/, can be intolerably slow;+ also, the term @exp $ negate lambda@, underflows for large /lambda/;+ so for large /lambda/, this implementation returns the appropriate 'NormalDistribution'.+-}+generatePoissonDistribution :: (+ Integral sample,+ RealFloat lambda,+ Show lambda,+ System.Random.Random lambda,+ System.Random.RandomGen randomGen+ )+ => lambda -- ^ Defines the required approximate value of both /mean/ and /variance/.+ -> randomGen+ -> [sample]+generatePoissonDistribution lambda+ | lambda <= 0 = error $ "Factory.Math.Probability.generatePoissonDistribution:\tlambda must exceed zero " ++ show lambda+ | lambda > (+ negate . log $ minPositiveFloat lambda -- Guard against underflow, in the user-defined type for lambda.+ ) = filter (>= 0) . map round . (reProfile (PoissonDistribution lambda) :: [Double] -> [Double]) . generateStandardizedNormalDistribution+ | otherwise = generator+ where+ generator = uncurry (:) . (+ fst . head . dropWhile (+ (> exp (negate lambda)) . snd -- CAVEAT: underflows if lambda > (103 :: Float, 745 :: Double).+ ) . scanl (+ \accumulator random -> succ *** (* random) $ accumulator+ ) (negate 1, 1) . System.Random.randomRs (0, 1) *** generator {-recurse-}+ ) . System.Random.split++-- | Uses the supplied random-number generator, to generate a conceptually infinite population, with the specified discrete probability-distribution.+generateDiscretePopulation :: (+ Integral sample,+ Ord parameter,+ RealFloat parameter,+ Show parameter,+ System.Random.Random parameter,+ System.Random.RandomGen randomGen+ )+ => DiscreteDistribution parameter+ -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.+ -> [sample]+generateDiscretePopulation probabilityDistribution randomGen+ | not $ ToolShed.SelfValidate.isValid probabilityDistribution = error $ "Factory.Math.Probability.generateDiscretePopulation:\t" ++ ToolShed.SelfValidate.getFirstError probabilityDistribution+ | otherwise = (+ case probabilityDistribution of+ PoissonDistribution lambda -> generatePoissonDistribution lambda+ ShiftedGeometricDistribution probability+ | probability == 1 -> const $ repeat 1 -- The first Bernoulli Trial is guaranteed to succeed.+ | otherwise -> map ceiling {-minimum 1-} . (\x -> x :: [Rational]) . generatePopulation (ExponentialDistribution . negate $ log (1 - probability)) -- The geometric distribution is a discrete version of the exponential distribution.+ ) randomGen+
+ src-lib/Factory/Math/Radix.hs view
@@ -0,0 +1,139 @@+{-+ Copyright (C) 2011-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@] Facilitates representation of 'Integral' values in alternative 'Integral' bases.+-}++module Factory.Math.Radix(+-- * Constants+-- decodes,+-- digits,+-- encodes,+-- * Functions+ digitSum,+ digitalRoot,+ fromBase,+ toBase+) where++import Data.Array.IArray((!))+import qualified Data.Array.IArray+import qualified Data.Char+import qualified Data.List+import qualified Data.Maybe++-- | Characters used to represent the digits of numbers in @(-36 <= base <= 36)@.+digits :: String+digits = ['0' .. '9'] ++ ['a' .. 'z']++-- | Constant random-access lookup for 'digits'.+encodes :: (Data.Array.IArray.Ix index, Integral index) => Data.Array.IArray.Array index Char+encodes = Data.Array.IArray.listArray (0, pred $ Data.List.genericLength digits) digits++-- | Constant reverse-lookup for 'digits'.+decodes :: Integral i => [(Char, i)]+decodes = zip digits [0 ..]++{- |+ * Convert the specified integral quantity, to an alternative base, and represent the result as a 'String'.++ * Both negative integers and negative bases are permissible.++ * The conversion to 'Char' can only succeed where printable and intelligible characters exist to represent all digits in the chosen base;+ which in practice means @(-36 <= base <= 36)@.+-}+toBase :: (+ Data.Array.IArray.Ix decimal,+ Integral base,+ Integral decimal,+ Show base,+ Show decimal+ ) => base -> decimal -> String+toBase 10 decimal = show decimal -- Base unchanged.+toBase _ 0 = "0" -- Zero has the same representation in any base.+toBase base decimal+ | abs base < 2 = error $ "Factory.Math.Radix.toBase:\tan arbitrary integer can't be represented in base " ++ show base+ | abs base > Data.List.genericLength digits = error $ "Factory.Math.Radix.toBase:\tunable to clearly represent the complete set of digits in base " ++ show base+ | base > 0 && decimal < 0 = '-' : map toDigit (fromDecimal (negate decimal) [])+ | otherwise = toDigit `map` fromDecimal decimal []+ where+ fromDecimal 0 = id+ fromDecimal n+ | remainder < 0 = fromDecimal (succ quotient) . ((remainder - fromIntegral base) :) -- This can only occur when base is negative; cf. 'divMod'.+ | otherwise = fromDecimal quotient . (remainder :)+ where+ (quotient, remainder) = n `quotRem` fromIntegral base++ toDigit :: (Data.Array.IArray.Ix i, Integral i, Show i) => i -> Char+ toDigit n+ | n >&< encodes = encodes ! n+ | otherwise = error $ "Factory.Math.Radix.toBase.toDigit:\tno suitable character-representation for integer " ++ show n+ where+ (>&<) :: (Data.Array.IArray.Ix i, Integral i) => i -> Data.Array.IArray.Array i Char -> Bool+ index >&< array = ($ index) `all` [(>= lower), (<= upper)] where+ (lower, upper) = Data.Array.IArray.bounds array++{- |+ * Convert the 'String'-representation of a number in the specified base, to an integer.++ * Both negative numbers and negative bases are permissible.+-}+fromBase :: (+ Integral base,+ Integral decimal,+ Read decimal,+ Show base+ ) => base -> String -> decimal+fromBase 10 s = read s -- Base unchanged.+fromBase _ "0" = 0 -- Zero has the same representation in any base.+fromBase base s+ | abs base < 2 = error $ "Factory.Math.Radix.fromBase:\tan arbitrary integer can't be represented in base " ++ show base+ | abs base > Data.List.genericLength digits = error $ "Factory.Math.Radix.fromBase:\tunable to clearly represent the complete set of digits in base " ++ show base+ | base > 0 && head s == '-' = negate . fromBase base $ tail s -- Recurse.+ | otherwise = Data.List.foldl' (\l -> ((l * fromIntegral base) +) . fromDigit) 0 s where+ fromDigit :: Integral i => Char -> i+ fromDigit c = case c `lookup` decodes of+ Just i+ | i >= abs (fromIntegral base) -> error $ "Factory.Math.Radix.fromBase.fromDigit:\tillegal char " ++ show c ++ ", for base " ++ show base+ | otherwise -> i+ _ -> error $ "Factory.Math.Radix.fromBase.fromDigit:\tunrecognised char " ++ show c++{- |+ * <http://mathworld.wolfram.com/DigitSum.html>.++ * <http://en.wikipedia.org/wiki/Digit_sum>.+-}+digitSum :: (+ Data.Array.IArray.Ix decimal,+ Integral base,+ Integral decimal,+ Show base,+ Show decimal+ ) => base -> decimal -> decimal+digitSum 10 = fromIntegral . foldr ((+) . Data.Char.digitToInt) 0 . show+digitSum base = sum . Data.Maybe.mapMaybe (`lookup` decodes) . toBase base++-- | <http://en.wikipedia.org/wiki/Digital_root>.+digitalRoot :: (+ Data.Array.IArray.Ix decimal,+ Integral decimal,+ Show decimal+ ) => decimal -> decimal+digitalRoot = until (<= 9) (digitSum (10 :: Int))+
+ src-lib/Factory/Math/SquareRoot.hs view
@@ -0,0 +1,120 @@+{-+ Copyright (C) 2011 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@]++ * Exports a common interface for /square-root/ implementations.++ * Provides utilities for these implementations.+-}++module Factory.Math.SquareRoot(+-- * Type-classes+ Algorithmic(..),+ Iterator(..),+-- * Types+-- ** Type-synonyms+ Result,+ Estimate,+-- * Functions+ getAccuracy,+ getDiscrepancy,+ getEstimate,+-- rSqrt,+-- ** Predicates+ isPrecise+) where++import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Precision as Math.Precision++-- | The result-type; actually, only the concrete return-type of 'Math.Precision.simplify', stops it being a polymorphic instance of 'Fractional'.+type Result = Rational++-- | Contains an estimate for the /square-root/ of a value, and its accuracy.+type Estimate = (Result, Math.Precision.DecimalDigits)++-- | Defines the methods expected of a /square-root/ algorithm.+class Algorithmic algorithm where+ squareRootFrom :: (Real operand, Show operand)+ => algorithm+ -> Estimate -- ^ An initial estimate from which to start.+ -> Math.Precision.DecimalDigits -- ^ The required precision.+ -> operand -- ^ The value for which to find the /square-root/.+ -> Result -- ^ Returns an improved estimate of the /square-root/, found using the specified algorithm, accurate to at least the required number of decimal digits.++ squareRoot :: (Real operand, Show operand)+ => algorithm+ -> Math.Precision.DecimalDigits -- ^ The required precision.+ -> operand -- ^ The value for which to find the /square-root/.+ -> Result -- ^ Returns an estimate of the /square-root/, found using the specified algorithm, accurate to at least the required number of decimal digits.+ squareRoot algorithm decimalDigits operand = squareRootFrom algorithm (getEstimate operand) decimalDigits operand -- Default implementation++-- | The interface required to iterate, from an estimate of the required value, to the next approximation.+class Iterator algorithm where+ step :: Real operand+ => algorithm+ -> operand -- ^ The value for which the /square-root/ is required; @y@.+ -> Result -- ^ The current estimate; @x(n)@.+ -> Result -- ^ An improved estimate; @x(n+1)@.++ convergenceOrder :: algorithm -> Math.Precision.ConvergenceOrder -- ^ The ultimate ratio of successive terms as the iteration converges.++-- | Generalise 'sqrt' to operate on any 'Real' operand.+rSqrt :: Real operand => operand -> Double+rSqrt = sqrt . realToFrac++-- | Uses 'Double'-precision floating-point arithmetic, to obtain an initial estimate for the /square-root/, and its accuracy.+getEstimate :: (Real operand, Show operand) => operand -> Estimate+getEstimate y+ | y < 0 = error $ "Factory.Math.SquareRoot.getEstimate:\tthere's no real square-root of " ++ show y+ | otherwise = (Math.Precision.simplify decimalDigits {-doubles performance by roughly length of the Rational representation-} . toRational $ rSqrt y, decimalDigits)+ where+ decimalDigits :: Math.Precision.DecimalDigits+ decimalDigits = 16 -- <http://en.wikipedia.org/wiki/IEEE_floating_point>.++{- |+ * The signed difference between the /square/ of an estimate for the /square-root/ of a value, and that value.++ * Positive when the estimate is too low.++ * CAVEAT: the magnitude is twice the error in the /square-root/.+-}+getDiscrepancy :: Real operand => operand -> Result -> Result+getDiscrepancy y x = toRational y - Math.Power.square x++-- | True if the specified estimate for the /square-root/, is precise.+isPrecise :: Real operand => operand -> Result -> Bool+isPrecise y x = getDiscrepancy y x == 0++{- |+ * For a given value and an estimate of its /square-root/,+ returns the number of decimals digits to which the /square-root/ is accurate; including the integral digits.++ * CAVEAT: the result returned for an exact match has been bodged.+-}+getAccuracy :: Real operand => operand -> Result -> Math.Precision.DecimalDigits+getAccuracy y x+ | absoluteError == 0 = maxBound -- Bodge.+-- | otherwise = length . takeWhile (< 1) $ iterate (* 10) relativeError -- CAVEAT: too slow.+ | otherwise = length $ show (round $ toRational y / absoluteError :: Integer)+ where+ absoluteError :: Result+ absoluteError = abs (getDiscrepancy y x) / 2 -- NB: the magnitude of the error in 'y', is twice the error in its square-root, 'x'.+
+ src-lib/Factory/Math/Statistics.hs view
@@ -0,0 +1,181 @@+{-+ Copyright (C) 2011 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Miscellaneous statistics functions.+-}++module Factory.Math.Statistics(+-- * Functions+ getMean,+ getWeightedMean,+-- getDispersionFromMean,+ getVariance,+ getStandardDeviation,+ getAverageAbsoluteDeviation,+ getCoefficientOfVariance,+ nCr,+ nPr+) where++import Control.Arrow((***))+import Control.Parallel(par, pseq)+import qualified Data.Foldable+import qualified Data.List+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Power as Math.Power++{- |+ * Determines the /mean/ of the specified numbers; <http://en.wikipedia.org/wiki/Mean>.++ * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.+-}+getMean :: (+ Data.Foldable.Foldable foldable,+ Fractional result,+ Real value+ )+ => foldable value+ -> result+getMean foldable+ | denominator == 0 = error "Factory.Math.Statistics.getMean:\tno data => undefined result."+ | otherwise = realToFrac numerator / fromIntegral denominator+ where+ (numerator, denominator) = Data.Foldable.foldr (\s -> (+ s) *** succ) (0, 0 :: Int) foldable++{- |+ * Determines the /weighted mean/ of the specified numbers; <http://en.wikipedia.org/wiki/Weighted_arithmetic_mean>.++ * The specified value is only evaluated if the corresponding weight is non-zero.++ * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.+-}+getWeightedMean :: (+ Data.Foldable.Foldable foldable,+ Fractional result,+ Real value,+ Real weight+ )+ => foldable (value, weight) -- ^ Each pair consists of a value & the corresponding weight.+ -> result+getWeightedMean foldable+ | denominator == 0 = error "Factory.Math.Statistics.getWeightedMean:\tzero weight => undefined result."+ | otherwise = numerator / realToFrac denominator+ where+ (numerator, denominator) = Data.Foldable.foldr (+ \(value, weight) -> if weight == 0+ then id --Avoid unnecessarily evaluation.+ else (+ realToFrac value * realToFrac weight) *** (+ weight)+ ) (0, 0) foldable++{- |+ * Measures the /dispersion/ of a /population/ of results from the /mean/ value; <http://en.wikipedia.org/wiki/Statistical_dispersion>.++ * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.+-}+getDispersionFromMean :: (+ Data.Foldable.Foldable foldable,+ Fractional result,+ Functor foldable,+ Real value+ ) => (Rational -> Rational) -> foldable value -> result+getDispersionFromMean weight foldable = getMean $ fmap (weight . (+ negate mean) . toRational) foldable where+ mean :: Rational+ mean = getMean foldable++{- |+ * Determines the exact /variance/ of the specified numbers; <http://en.wikipedia.org/wiki/Variance>.++ * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.+-}+getVariance :: (+ Data.Foldable.Foldable foldable,+ Fractional variance,+ Functor foldable,+ Real value+ ) => foldable value -> variance+getVariance = getDispersionFromMean Math.Power.square++-- | Determines the /standard-deviation/ of the specified numbers; <http://en.wikipedia.org/wiki/Standard_deviation>.+getStandardDeviation :: (+ Data.Foldable.Foldable foldable,+ Floating result,+ Functor foldable,+ Real value+ ) => foldable value -> result+getStandardDeviation = sqrt . getVariance++{- |+ * Determines the /average absolute deviation/ of the specified numbers; <http://en.wikipedia.org/wiki/Absolute_deviation#Average_absolute_deviation>.++ * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.+-}+getAverageAbsoluteDeviation :: (+ Data.Foldable.Foldable foldable,+ Fractional result,+ Functor foldable,+ Real value+ ) => foldable value -> result+getAverageAbsoluteDeviation = getDispersionFromMean abs++-- | Determines the /coefficient-of-variance/ of the specified numbers; <http://en.wikipedia.org/wiki/Coefficient_of_variation>.+getCoefficientOfVariance :: (+ Data.Foldable.Foldable foldable,+ Eq result,+ Floating result,+ Functor foldable,+ Real value+ ) => foldable value -> result+getCoefficientOfVariance l+ | mean == 0 = error "Factory.Math.Statistics.getCoefficientOfVariance:\tundefined if mean is zero."+ | otherwise = getStandardDeviation l / abs mean+ where+ mean = getMean l++-- | The number of unordered /combinations/ of /r/ objects taken from /n/; <http://en.wikipedia.org/wiki/Combination>.+nCr :: (Math.Factorial.Algorithmic factorialAlgorithm, Integral i, Show i)+ => factorialAlgorithm+ -> i -- ^ The total number of items from which to select.+ -> i -- ^ The number of items in a sample.+ -> i -- ^ The number of combinations.+nCr _ 0 _ = 1+nCr _ _ 0 = 1+nCr factorialAlgorithm n r+ | n < 0 = error $ "Factory.Math.Statistics.nCr:\tinvalid n; " ++ show n+ | r < 0 = error $ "Factory.Math.Statistics.nCr:\tinvalid r; " ++ show r+ | n < r = 0+ | otherwise = numerator `par` (denominator `pseq` numerator `div` denominator)+ where+ [smaller, bigger] = Data.List.sort [r, n - r]+ numerator = Math.Implementations.Factorial.risingFactorial (succ bigger) (n - bigger)+ denominator = Math.Factorial.factorial factorialAlgorithm smaller++-- | The number of /permutations/ of /r/ objects taken from /n/; <http://en.wikipedia.org/wiki/Permutations>.+nPr :: (Integral i, Show i)+ => i -- ^ The total number of items from which to select.+ -> i -- ^ The number of items in a sample.+ -> i -- ^ The number of permutations.+nPr 0 _ = 1+nPr _ 0 = 1+nPr n r+ | n < 0 = error $ "Factory.Math.Statistics.nPr:\tinvalid n; " ++ show n+ | r < 0 = error $ "Factory.Math.Statistics.nPr:\tinvalid r; " ++ show r+ | n < r = 0+ | otherwise = Math.Implementations.Factorial.fallingFactorial n r+
+ src-lib/Factory/Math/Summation.hs view
@@ -0,0 +1,91 @@+{-+ Copyright (C) 2011 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@] Provides an alternative algorithm for the summation of /rational/ numbers.+-}++module Factory.Math.Summation(+-- * Functions+ sum',+ sumR',+ sumR+) where++import qualified Control.DeepSeq+import qualified Control.Parallel.Strategies+import qualified Data.List+import qualified Data.Ratio+import Data.Ratio((%))+import qualified ToolShed.Data.List++{- |+ * Sums a list of numbers of arbitrary type.++ * Sparks the summation of @(list-length / chunk-size)@ chunks from the list, each of the specified size (thought the last chunk may be smaller),+ then recursively sums the list of results from each spark.++ * CAVEAT: unless the numbers are large, 'Rational' (requiring /cross-multiplication/), or the list long,+ 'sum' is too light-weight for sparking to be productive,+ therefore it is more likely to be the parallelised deep /evaluation/ of list-elements which saves time.+-}+sum' :: (Num n, Control.DeepSeq.NFData n)+ => ToolShed.Data.List.ChunkLength+ -> [n]+ -> n+sum' chunkLength+ | chunkLength <= 1 = error $ "Factory.Math.Summation.sum':\tinvalid chunk-size; " ++ show chunkLength+ | otherwise = slave+ where+ slave :: (Num n, Control.DeepSeq.NFData n) => [n] -> n+ slave [] = 0+ slave [x] = x+ slave l = slave {-recurse-} . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq sum $ ToolShed.Data.List.chunk chunkLength l++{- |+ * Sums a list of /rational/ type numbers.++ * CAVEAT: though faster than 'Data.List.sum', this algorithm has poor space-complexity, making it unsuitable for unrestricted use.+-}+{-# INLINE sumR' #-} -- This makes a staggering difference.+sumR' :: Integral i => [Data.Ratio.Ratio i] -> Data.Ratio.Ratio i+sumR' l = foldr (\ratio -> ((Data.Ratio.numerator ratio * (commonDenominator `div` Data.Ratio.denominator ratio)) +)) 0 l % commonDenominator where+-- commonDenominator = foldr (lcm . Data.Ratio.denominator) 1 l+ commonDenominator = Data.List.foldl' (\multiple -> lcm multiple . Data.Ratio.denominator) 1 l -- Slightly faster.++{- |+ * Sums a list of /rational/ numbers.++ * Sparks the summation of @(list-length / chunk-length)@ chunks from the list, each of the specified size (thought the last chunk may be smaller),+ then recursively sums the list of results from each spark.++ * CAVEAT: memory-use is proportional to chunk-size.+-}+{-# INLINE sumR #-} -- This makes a staggering difference to calls from other modules.+sumR :: (Integral i, Control.DeepSeq.NFData i)+ => ToolShed.Data.List.ChunkLength+ -> [Data.Ratio.Ratio i]+ -> Data.Ratio.Ratio i+sumR chunkLength+ | chunkLength <= 1 = error $ "Factory.Math.Summation.sumR:\tinvalid chunk-size; " ++ show chunkLength+ | otherwise = slave+ where+ slave :: (Integral i, Control.DeepSeq.NFData i) => [Data.Ratio.Ratio i] -> Data.Ratio.Ratio i+ slave l+ | length l <= chunkLength = sumR' l+ | otherwise = slave {-recurse-} . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq sumR' $ ToolShed.Data.List.chunk chunkLength l
+ src-test/Factory/Test/QuickCheck/ArithmeticGeometricMean.hs view
@@ -0,0 +1,57 @@+{-+ Copyright (C) 2011-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 "Math.ArithmeticGeometricMean".+-}++module Factory.Test.QuickCheck.ArithmeticGeometricMean(+-- * Constants+ results,+-- * Types+-- ** Type-synonyms+-- Testable+) where++import qualified Data.Tuple+import qualified Factory.Math.ArithmeticGeometricMean as Math.ArithmeticGeometricMean+import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot+import qualified Factory.Math.Precision as Math.Precision+import Factory.Test.QuickCheck.SquareRoot()+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++type Testable = Math.Implementations.SquareRoot.Algorithm -> Math.Precision.DecimalDigits -> Math.ArithmeticGeometricMean.AGM -> Int -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [prop_symmetrical, prop_bounds] where+ prop_symmetrical, prop_bounds :: Testable+ prop_symmetrical squareRootAlgorithm decimalDigits agm index = Math.ArithmeticGeometricMean.isValid agm ==> Test.QuickCheck.label "prop_symmetrical" . and . tail . take index' $ zipWith (==) (+ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' agm+ ) (+ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' $ Data.Tuple.swap agm+ ) where+ decimalDigits' = succ $ decimalDigits `mod` 64+ index' = succ $ index `mod` 8++ prop_bounds squareRootAlgorithm decimalDigits agm index = all ($ agm) [Math.ArithmeticGeometricMean.isValid, uncurry (/=)] ==> Test.QuickCheck.label "prop_bounds" . all (uncurry (>=)) . tail . take index' $ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' agm+ where+ decimalDigits' = 33 {-test is sensitive to rounding-errors-} + (decimalDigits `mod` 96)+ index' = succ $ index `mod` 5+
+ src-test/Factory/Test/QuickCheck/Factorial.hs view
@@ -0,0 +1,75 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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 "Math.Implementations.Factorial".+-}++module Factory.Test.QuickCheck.Factorial(+-- * Constants+ results,+-- * Types+-- ** Type-synonyms+-- Testable+) where++import Data.Ratio((%))+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import Factory.Math.Implementations.Factorial((!/!))+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++instance Test.QuickCheck.Arbitrary Math.Implementations.Factorial.Algorithm where+ arbitrary = Test.QuickCheck.elements [Math.Implementations.Factorial.Bisection, Math.Implementations.Factorial.PrimeFactorisation]++type Testable = Integer -> Integer -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_equivalence,+ Test.QuickCheck.quickCheckResult prop_symmetry,+ Test.QuickCheck.quickCheckResult prop_x0,+ Test.QuickCheck.quickCheckResult prop_0n,+ Test.QuickCheck.quickCheckResult prop_ratio,+ Test.QuickCheck.quickCheckResult prop_consistency+ ] where+ prop_equivalence, prop_symmetry, prop_x0, prop_0n :: Testable+ prop_equivalence x n = Test.QuickCheck.label "prop_equivalence" $ Math.Implementations.Factorial.risingFactorial x n == sign * Math.Implementations.Factorial.fallingFactorial (negate x) n && Math.Implementations.Factorial.fallingFactorial x n == sign * Math.Implementations.Factorial.risingFactorial (negate x) n where+ sign :: Integer+ sign+ | even n = 1+ | otherwise = negate 1++ prop_symmetry x n = Test.QuickCheck.label "prop_symmetry" $ Math.Implementations.Factorial.risingFactorial x n == Math.Implementations.Factorial.fallingFactorial (pred $ x + n) n++ prop_x0 x _ = Test.QuickCheck.label "prop_x0" $ all (== 1) $ map ($ 0) [Math.Implementations.Factorial.risingFactorial x, Math.Implementations.Factorial.fallingFactorial x]++ prop_0n _ n = Test.QuickCheck.label "prop_0n" $ all (== if n == 0 then 1 else 0) $ map ($ n) [Math.Implementations.Factorial.risingFactorial 0, Math.Implementations.Factorial.fallingFactorial 0]++ prop_ratio :: Math.Implementations.Factorial.Algorithm -> Integer -> Integer -> Test.QuickCheck.Property+ prop_ratio algorithm i j = Test.QuickCheck.label "prop_ratio" $ n !/! d == Math.Factorial.factorial algorithm n % Math.Factorial.factorial algorithm d where+ n = pred $ i `mod` 100000+ d = pred $ j `mod` 100000++ prop_consistency :: Math.Implementations.Factorial.Algorithm -> Math.Implementations.Factorial.Algorithm -> Integer -> Test.QuickCheck.Property+ prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Factorial.factorial l n == Math.Factorial.factorial r n where+ n = pred $ i `mod` 100000+
+ src-test/Factory/Test/QuickCheck/Hyperoperation.hs view
@@ -0,0 +1,79 @@+{-+ Copyright (C) 2011-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 "Math.Hyperoperation".+-}++module Factory.Test.QuickCheck.Hyperoperation(+-- * Constants+ results+) where++import qualified Factory.Math.Hyperoperation as Math.Hyperoperation+import qualified Test.QuickCheck++type Rank = Int++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_rankCoincides,+ Test.QuickCheck.quickCheckResult prop_baseCoincides,+ Test.QuickCheck.quickCheckResult prop_hyperExponentCoincides,+ Test.QuickCheck.quickCheckResult prop_succ,+ Test.QuickCheck.quickCheckResult prop_addition,+ Test.QuickCheck.quickCheckResult prop_multiplication,+ Test.QuickCheck.quickCheckResult prop_exponentiation+ ] where+ prop_rankCoincides :: Rank -> Test.QuickCheck.Property+ prop_rankCoincides rank = Test.QuickCheck.label "prop_rankCoincides" $ Math.Hyperoperation.hyperoperation rank' 2 2 == 4 where+ rank' :: Rank+ rank' = succ $ rank `mod` 1000++ prop_baseCoincides :: Rank -> Integer -> Test.QuickCheck.Property+ prop_baseCoincides rank base = Test.QuickCheck.label "prop_baseCoincides" $ Math.Hyperoperation.hyperoperation rank' base 1 == base where+ rank' :: Rank+ rank' = 2 + (rank `mod` 1000)++ prop_hyperExponentCoincides :: Rank -> Integer -> Test.QuickCheck.Property+ prop_hyperExponentCoincides rank hyperExponent = Test.QuickCheck.label "prop_hyperExponentCoincides" $ Math.Hyperoperation.hyperoperation rank' 1 hyperExponent' == 1 where+ rank' :: Rank+ rank' = 3 + (rank `mod` 1000)++ hyperExponent' :: Math.Hyperoperation.HyperExponent+ hyperExponent' = abs hyperExponent++ prop_succ, prop_addition, prop_multiplication, prop_exponentiation :: Integer -> Integer -> Test.QuickCheck.Property+ prop_succ base hyperExponent = Test.QuickCheck.label "prop_succ" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.succession base hyperExponent' == succ (fromIntegral hyperExponent') where+ hyperExponent' :: Math.Hyperoperation.HyperExponent+ hyperExponent' = abs hyperExponent++ prop_addition base hyperExponent = Test.QuickCheck.label "prop_addition" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.addition base hyperExponent' == base + fromIntegral hyperExponent' where+ hyperExponent' :: Math.Hyperoperation.HyperExponent+ hyperExponent' = abs hyperExponent++ prop_multiplication base hyperExponent = Test.QuickCheck.label "prop_multiplication" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.multiplication base hyperExponent' == base * fromIntegral hyperExponent' where+ hyperExponent' :: Math.Hyperoperation.HyperExponent+ hyperExponent' = abs hyperExponent++ prop_exponentiation base hyperExponent = Test.QuickCheck.label "prop_exponentiation" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.exponentiation base hyperExponent' == base ^ hyperExponent' where+ hyperExponent' :: Math.Hyperoperation.HyperExponent+ hyperExponent' = abs hyperExponent++
+ src-test/Factory/Test/QuickCheck/Interval.hs view
@@ -0,0 +1,43 @@+{-+ Copyright (C) 2011-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 "Data.Interval".+-}++module Factory.Test.QuickCheck.Interval(+-- * Constants+ results+) where++import qualified Data.Ratio+import qualified Factory.Data.Interval as Data.Interval+import qualified Test.QuickCheck++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 1000 } `mapM` [prop_product] where+ prop_product :: Data.Ratio.Ratio Integer -> Integer -> Data.Interval.Interval Integer -> Test.QuickCheck.Property+ prop_product ratio minLength interval = Test.QuickCheck.label "prop_product" $ Data.Interval.product' ratio' minLength' interval' == product (Data.Interval.toList interval') where+ interval' = Data.Interval.normalise interval+ minLength' = succ $ minLength `mod` 1000+ ratio'+ | r > 1 = recip r+ | otherwise = r+ where+ r = abs ratio
+ src-test/Factory/Test/QuickCheck/MonicPolynomial.hs view
@@ -0,0 +1,77 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Data.MonicPolynomial".+-}++module Factory.Test.QuickCheck.MonicPolynomial(+-- * Constants+ results,+-- * Types+-- ** Type-synonyms+-- P+) where++import Factory.Data.Ring((=*=), (=+=), (=^))+import Factory.Test.QuickCheck.Polynomial()+import qualified Factory.Data.MonicPolynomial as Data.MonicPolynomial+import qualified Factory.Data.Polynomial as Data.Polynomial+import qualified Factory.Data.QuotientRing as Data.QuotientRing+import qualified Factory.Data.Ring as Data.Ring+import qualified Test.QuickCheck++instance (+ Integral c,+ Integral e,+ Test.QuickCheck.Arbitrary c,+ Test.QuickCheck.Arbitrary e,+ Show c,+ Show e+ ) => Test.QuickCheck.Arbitrary (Data.MonicPolynomial.MonicPolynomial c e) where+ arbitrary = do+ polynomial <- Test.QuickCheck.arbitrary++ return {-to Gen-monad-} . Data.MonicPolynomial.mkMonicPolynomial $ ((1, succ $ Data.Polynomial.getDegree polynomial) :) `Data.Polynomial.lift` polynomial++type P = Data.MonicPolynomial.MonicPolynomial Integer Integer++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_quotRem,+ Test.QuickCheck.quickCheckResult prop_quotientRingNormalised,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_perfectPower,+ Test.QuickCheck.quickCheckResult prop_isDivisibleBy+ ] where+ prop_quotRem, prop_quotientRingNormalised :: P -> P -> Test.QuickCheck.Property+ prop_quotRem numerator denominator = Test.QuickCheck.label "prop_quotRem" $ numerator == denominator =*= quotient =+= remainder where+ (quotient, remainder) = numerator `Data.QuotientRing.quotRem'` denominator++ prop_quotientRingNormalised numerator denominator = Test.QuickCheck.label "prop_quotientRingNormalised" $ all (Data.Polynomial.isNormalised . Data.MonicPolynomial.getPolynomial) [numerator `Data.QuotientRing.quot'` denominator, numerator `Data.QuotientRing.rem'` denominator]++ prop_perfectPower :: P -> Int -> Test.QuickCheck.Property+ prop_perfectPower polynomial power = Test.QuickCheck.label "prop_perfectPower" $ iterate (`Data.QuotientRing.quot'` polynomial) (polynomial =^ power') !! pred power' == polynomial where+ power' :: Int+ power' = succ $ power `mod` 100++ prop_isDivisibleBy :: [P] -> Test.QuickCheck.Property+ prop_isDivisibleBy monicPolynomials = Test.QuickCheck.label "prop_isDivisibleBy" $ all (Data.QuotientRing.isDivisibleBy (Data.Ring.product' (recip 2) {-TODO-} 10 monicPolynomials)) monicPolynomials++
+ src-test/Factory/Test/QuickCheck/PerfectPower.hs view
@@ -0,0 +1,55 @@+{-+ Copyright (C) 2011-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 "Math.PerfectPower".+-}++module Factory.Test.QuickCheck.PerfectPower(+-- * Constants+ results+) where++import qualified Data.Maybe+import qualified Factory.Math.PerfectPower as Math.PerfectPower+import qualified Factory.Math.Power as Math.Power+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_maybeSquareNumber,+ Test.QuickCheck.quickCheckResult prop_rewriteRule,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 10000 } prop_notSquare,+ Test.QuickCheck.quickCheckResult prop_isPerfectPower+ ] where+ prop_maybeSquareNumber, prop_notSquare, prop_rewriteRule :: Integer -> Test.QuickCheck.Property+ prop_maybeSquareNumber i = Test.QuickCheck.label "prop_maybeSquareNumber" $ Math.PerfectPower.maybeSquareNumber (Math.Power.square i) == Just (abs i)++ prop_notSquare i = abs i > 0 ==> Test.QuickCheck.label "prop_notSquare" . Data.Maybe.isNothing $ Math.PerfectPower.maybeSquareNumber (succ $ i ^ (10 {-promote rounding-error using big number-} :: Int))++ prop_rewriteRule i = Test.QuickCheck.label "prop_rewriteRule" $ Math.PerfectPower.isPerfectPower i' == Math.PerfectPower.isPerfectPower (fromIntegral i' :: Int) where+ i' = abs i++ prop_isPerfectPower :: Integer -> Integer -> Test.QuickCheck.Property+ prop_isPerfectPower b e = Test.QuickCheck.label "prop_isPerfectPower" . Math.PerfectPower.isPerfectPower $ b' ^ e' where+ b' = 2 + (b `mod` 10)+ e' = 2 + (e `mod` 8)++
+ src-test/Factory/Test/QuickCheck/Pi.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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 "Math.Pi".+-}++module Factory.Test.QuickCheck.Pi(+-- * Constants+ results,+-- * Types+-- ** Type-synonyms+-- Testable+) where++import Factory.Test.QuickCheck.Factorial()+import Factory.Test.QuickCheck.SquareRoot()+import qualified Factory.Math.Factorial as Math.Factorial+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Implementations.Pi.AGM.Algorithm as Math.Implementations.Pi.AGM.Algorithm+import qualified Factory.Math.Implementations.Pi.BBP.Algorithm as Math.Implementations.Pi.BBP.Algorithm+import qualified Factory.Math.Implementations.Pi.Borwein.Algorithm as Math.Implementations.Pi.Borwein.Algorithm+import qualified Factory.Math.Implementations.Pi.Ramanujan.Algorithm as Math.Implementations.Pi.Ramanujan.Algorithm+import qualified Factory.Math.Implementations.Pi.Spigot.Algorithm as Math.Implementations.Pi.Spigot.Algorithm+import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot+import qualified Factory.Math.Pi as Math.Pi+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative((<$>), (<*>))+#endif++instance (+ Test.QuickCheck.Arbitrary squareRootAlgorithm,+ Math.SquareRoot.Algorithmic squareRootAlgorithm+ ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm) where+ arbitrary = Math.Implementations.Pi.AGM.Algorithm.BrentSalamin <$> Test.QuickCheck.arbitrary++instance Test.QuickCheck.Arbitrary Math.Implementations.Pi.BBP.Algorithm.Algorithm where+ arbitrary = Test.QuickCheck.elements [Math.Implementations.Pi.BBP.Algorithm.Bellard, Math.Implementations.Pi.BBP.Algorithm.Base65536]++instance (+ Test.QuickCheck.Arbitrary squareRootAlgorithm,+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Test.QuickCheck.Arbitrary factorialAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm) where+ arbitrary = Test.QuickCheck.oneof [+ Math.Implementations.Pi.Borwein.Algorithm.Borwein1993 <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary+ ]++instance (+ Test.QuickCheck.Arbitrary squareRootAlgorithm,+ Math.SquareRoot.Algorithmic squareRootAlgorithm,+ Test.QuickCheck.Arbitrary factorialAlgorithm,+ Math.Factorial.Algorithmic factorialAlgorithm+ ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm) where+ arbitrary = Test.QuickCheck.oneof [+ Math.Implementations.Pi.Ramanujan.Algorithm.Classic <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary,+ Math.Implementations.Pi.Ramanujan.Algorithm.Chudnovsky <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary+ ]++instance Test.QuickCheck.Arbitrary Math.Implementations.Pi.Spigot.Algorithm.Algorithm where+ arbitrary = Test.QuickCheck.elements [Math.Implementations.Pi.Spigot.Algorithm.RabinowitzWagon, Math.Implementations.Pi.Spigot.Algorithm.Gosper]++instance (+ Test.QuickCheck.Arbitrary agm,+ Test.QuickCheck.Arbitrary bbp,+ Test.QuickCheck.Arbitrary borwein,+ Test.QuickCheck.Arbitrary ramanujan,+ Test.QuickCheck.Arbitrary spigot+ ) => Test.QuickCheck.Arbitrary (Math.Pi.Category agm bbp borwein ramanujan spigot) where+ arbitrary = Test.QuickCheck.oneof [+ Math.Pi.AGM <$> Test.QuickCheck.arbitrary,+ Math.Pi.BBP <$> Test.QuickCheck.arbitrary,+ Math.Pi.Borwein <$> Test.QuickCheck.arbitrary,+ Math.Pi.Ramanujan <$> Test.QuickCheck.arbitrary,+ Math.Pi.Spigot <$> Test.QuickCheck.arbitrary+ ]++type Category = Math.Pi.Category (+ Math.Implementations.Pi.AGM.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm+ ) Math.Implementations.Pi.BBP.Algorithm.Algorithm (+ Math.Implementations.Pi.Borwein.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm Math.Implementations.Factorial.Algorithm+ ) (+ Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm Math.Implementations.Factorial.Algorithm+ ) Math.Implementations.Pi.Spigot.Algorithm.Algorithm++type Testable = Category -> Category -> Math.Precision.DecimalDigits -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [prop_consistency] where+ prop_consistency :: Testable+ prop_consistency l r decimalDigits = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Pi.openI l decimalDigits' - Math.Pi.openI r decimalDigits' <= 1 {-rounding error-} where+ decimalDigits' = succ $ decimalDigits `mod` 250+
+ src-test/Factory/Test/QuickCheck/Polynomial.hs view
@@ -0,0 +1,122 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Data.Polynomial".+-}++module Factory.Test.QuickCheck.Polynomial(+-- * Constants+ results+) where++import Control.Arrow((***))+import Factory.Data.Ring((=*=), (=+=), (=-=), (=^))+import qualified Data.Numbers.Primes+import qualified Factory.Data.Polynomial as Data.Polynomial+import qualified Factory.Data.QuotientRing as Data.QuotientRing+import qualified Factory.Data.Ring as Data.Ring+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++instance (+ Test.QuickCheck.Arbitrary c,+ Integral c,+ Test.QuickCheck.Arbitrary e,+ Integral e+ ) => Test.QuickCheck.Arbitrary (Data.Polynomial.Polynomial c e) where+ arbitrary = (Data.Polynomial.mkPolynomial . map ((+ negate 4) . (`mod` 8) *** (`mod` 8))) `fmap` Test.QuickCheck.arbitrary++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_congruence,+ Test.QuickCheck.quickCheckResult prop_quotRem,+ Test.QuickCheck.quickCheckResult prop_degree,+ Test.QuickCheck.quickCheckResult prop_ringNormalised,+ Test.QuickCheck.quickCheckResult prop_quotientRingNormalised,+ Test.QuickCheck.quickCheckResult prop_power,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_perfectPower,+ Test.QuickCheck.quickCheckResult prop_normalised,+ Test.QuickCheck.quickCheckResult prop_raiseModuloNormalised,+ Test.QuickCheck.quickCheckResult prop_integralDomain,+ Test.QuickCheck.quickCheckResult prop_isDivisibleBy+ ] where+ prop_congruence :: Int -> Test.QuickCheck.Property+ prop_congruence i = Test.QuickCheck.label "prop_congruence" $ Data.Polynomial.areCongruentModulo (Data.Polynomial.mkLinear 1 (negate 1) =^ prime) (Data.Polynomial.mkPolynomial [(1, prime), (negate 1, 0)]) prime where+ prime :: Integer+ prime = Data.Numbers.Primes.primes !! mod i 100++ prop_quotRem, prop_degree, prop_ringNormalised, prop_quotientRingNormalised :: Data.Polynomial.Polynomial Integer Integer -> Data.Polynomial.Polynomial Integer Integer -> Test.QuickCheck.Property+ prop_quotRem numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_quotRem" $ numerator' == denominator' =*= quotient =+= remainder where+ numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer+ numerator' = Data.Polynomial.realCoefficientsToFrac numerator+ denominator' = Data.Polynomial.realCoefficientsToFrac denominator++ (quotient, remainder) = numerator' `Data.QuotientRing.quotRem'` denominator'++ prop_degree numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_degree" $ remainder == Data.Polynomial.zero || Data.Polynomial.getDegree remainder < Data.Polynomial.getDegree denominator' where+ numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer+ numerator' = Data.Polynomial.realCoefficientsToFrac numerator+ denominator' = Data.Polynomial.realCoefficientsToFrac denominator++ remainder = numerator' `Data.QuotientRing.rem'` denominator'++ prop_ringNormalised l r = Test.QuickCheck.label "prop_ringNormalised" $ all Data.Polynomial.isNormalised [l =*= r, l =+= r, l =-= r]++ prop_quotientRingNormalised numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_quotientRingNormalised" $ all Data.Polynomial.isNormalised [numerator' `Data.QuotientRing.quot'` denominator', numerator' `Data.QuotientRing.rem'` denominator'] where+ numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer+ numerator' = Data.Polynomial.realCoefficientsToFrac numerator+ denominator' = Data.Polynomial.realCoefficientsToFrac denominator++ prop_power, prop_perfectPower, prop_normalised :: Data.Polynomial.Polynomial Integer Integer -> Int -> Test.QuickCheck.Property+ prop_power polynomial power = Test.QuickCheck.label "prop_power" $ polynomial =^ power' == iterate (=*= polynomial) polynomial !! pred power' where+ power' :: Int+ power' = succ $ power `mod` 100++ prop_perfectPower polynomial power = polynomial' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_perfectPower" $ iterate (`Data.QuotientRing.quot'` polynomial') (polynomial' =^ power') !! pred power' == polynomial' where+ polynomial' :: Data.Polynomial.Polynomial Rational Integer+ polynomial' = Data.Polynomial.realCoefficientsToFrac polynomial++ power' :: Int+ power' = succ $ power `mod` 100++ prop_normalised polynomial i = Test.QuickCheck.label "prop_normalised" $ all Data.Polynomial.isNormalised [+ polynomial =^ power',+ polynomial `Data.Polynomial.mod'` modulus'+ ] where+ power' :: Int+ power' = succ $ i `mod` 100++ modulus' :: Integer+ modulus' = succ $ fromIntegral i `mod` 100++ prop_raiseModuloNormalised :: Data.Polynomial.Polynomial Integer Integer -> Integer -> Integer -> Test.QuickCheck.Property+ prop_raiseModuloNormalised polynomial power modulus = Test.QuickCheck.label "prop_raiseModuloNormalised" . Data.Polynomial.isNormalised $ Data.Polynomial.raiseModulo polynomial power' modulus' where+ power', modulus' :: Integer+ power' = succ $ power `mod` 100+ modulus' = succ $ modulus `mod` 100++ prop_integralDomain, prop_isDivisibleBy :: [Data.Polynomial.Polynomial Integer Integer] -> Test.QuickCheck.Property+ prop_integralDomain polynomials = Data.Polynomial.zero `notElem` polynomials ==> Test.QuickCheck.label "prop_integralDomain" $ Data.Ring.product' (recip 2) {-TODO-} 10 polynomials /= Data.Polynomial.zero++ prop_isDivisibleBy polynomials = Test.QuickCheck.label "prop_isDivisibleBy" . all (Data.QuotientRing.isDivisibleBy (Data.Ring.product' (recip 2) {-TODO-} 10 polynomials')) $ filter (/= Data.Polynomial.zero) polynomials' where+ polynomials' :: [Data.Polynomial.Polynomial Rational Integer]+ polynomials' = map Data.Polynomial.realCoefficientsToFrac polynomials+
+ src-test/Factory/Test/QuickCheck/Power.hs view
@@ -0,0 +1,47 @@+{-+ Copyright (C) 2011-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 "Math.Power".+-}++module Factory.Test.QuickCheck.Power(+-- * Constants+ results+) where++import qualified Data.List+import qualified Factory.Math.Power as Math.Power+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_squaresFrom,+ Test.QuickCheck.quickCheckResult prop_raiseModulo+ ] where+ prop_squaresFrom :: Integer -> Integer -> Test.QuickCheck.Property+ prop_squaresFrom from l = Test.QuickCheck.label "prop_squaresFrom" . (\(x, y) -> y == Math.Power.square x) . Data.List.genericIndex (Math.Power.squaresFrom from) $ abs l++ prop_raiseModulo :: Integer -> Integer -> Integer -> Test.QuickCheck.Property+ prop_raiseModulo b e m = m /= 0 ==> Test.QuickCheck.label "prop_raiseModulo" $ Math.Power.raiseModulo b e' m == (b ^ e') `mod` m where+ e' :: Integer+ e' = abs e++
+ src-test/Factory/Test/QuickCheck/Primality.hs view
@@ -0,0 +1,72 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.Primality".+-}++module Factory.Test.QuickCheck.Primality(+-- * Constants+ results+) where++import Factory.Test.QuickCheck.PrimeFactorisation()+import qualified Data.List+import qualified Data.Numbers.Primes+import qualified Factory.Math.Implementations.Primality as Math.Implementations.Primality+import qualified Factory.Math.Implementations.PrimeFactorisation as Math.Implementations.PrimeFactorisation+import qualified Factory.Math.Primality as Math.Primality+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++instance Test.QuickCheck.Arbitrary factorisationAlgorithm => Test.QuickCheck.Arbitrary (Math.Implementations.Primality.Algorithm factorisationAlgorithm) where+ arbitrary = Test.QuickCheck.oneof [+ Math.Implementations.Primality.AKS `fmap` Test.QuickCheck.arbitrary,+ return {-to Gen-monad-} Math.Implementations.Primality.MillerRabin+ ]++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_prime,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_composite,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_consistency+ ] where+ prop_prime :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property+ prop_prime primalityAlgorithm i = Test.QuickCheck.label "prop_prime" $ Math.Primality.isPrime primalityAlgorithm prime where+ normalise n+ | primalityAlgorithm == Math.Implementations.Primality.MillerRabin = n `mod` 1000000 -- Limited by the efficiency of 'Data.Numbers.Primes.primes'.+ | otherwise = n `mod` 59++ prime :: Integer+ prime = Data.List.genericIndex Data.Numbers.Primes.primes $ normalise i++ prop_composite :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> [Integer] -> Test.QuickCheck.Property+ prop_composite primalityAlgorithm l = length l > 1 ==> Test.QuickCheck.label "prop_composite" . not $ Math.Primality.isPrime primalityAlgorithm composite where+ normalise n+ | primalityAlgorithm == Math.Implementations.Primality.MillerRabin = n `mod` 1000000+ | otherwise = n `mod` 10++ composite :: Integer+ composite = product . map (Data.List.genericIndex Data.Numbers.Primes.primes . normalise) $ take 8 l++ prop_consistency :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property+ prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Primality.isPrime l i' == Math.Primality.isPrime r i' where+ i' = i `mod` 512+
+ src-test/Factory/Test/QuickCheck/PrimeFactorisation.hs view
@@ -0,0 +1,100 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.PrimeFactorisation".+-}++module Factory.Test.QuickCheck.PrimeFactorisation(+-- * Constants+ results+) where++import qualified Data.List+import qualified Data.Numbers.Primes+import qualified Factory.Data.PrimeFactors as Data.PrimeFactors+import qualified Factory.Data.Exponential as Data.Exponential+import qualified Factory.Math.Implementations.PrimeFactorisation as Math.Implementations.PrimeFactorisation+import qualified Factory.Math.MultiplicativeOrder as Math.MultiplicativeOrder+import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++instance Test.QuickCheck.Arbitrary Math.Implementations.PrimeFactorisation.Algorithm where+ arbitrary = Test.QuickCheck.oneof [+ Test.QuickCheck.elements [+ Math.Implementations.PrimeFactorisation.TrialDivision,+ Math.Implementations.PrimeFactorisation.FermatsMethod+ ]+ ]++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_consistency,+ Test.QuickCheck.quickCheckResult prop_primeFactors,+ Test.QuickCheck.quickCheckResult prop_smoothness,+ Test.QuickCheck.quickCheckResult prop_eulersTotientP,+ Test.QuickCheck.quickCheckResult prop_eulersTotientInequality,+ Test.QuickCheck.quickCheckResult prop_eulersTotient,+ Test.QuickCheck.quickCheckResult prop_lagrange,+ Test.QuickCheck.quickCheckResult prop_multiplicativeOrder,+ Test.QuickCheck.quickCheckResult prop_perfectPower+ ] where+ prop_consistency :: Integer -> Test.QuickCheck.Property+ prop_consistency i = Test.QuickCheck.label "prop_consistency" $ (Math.PrimeFactorisation.primeFactors Math.Implementations.PrimeFactorisation.TrialDivision i' :: Data.PrimeFactors.Factors Integer Int) == Math.PrimeFactorisation.primeFactors Math.Implementations.PrimeFactorisation.FermatsMethod i' where+ i' :: Integer+ i' = succ $ i `mod` 1000000++ prop_primeFactors, prop_smoothness, prop_eulersTotientP, prop_eulersTotientInequality :: Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property+ prop_primeFactors algorithm i = Test.QuickCheck.label "prop_primeFactors" $ Data.PrimeFactors.product' (recip 2) {-TODO-} 10 (Math.PrimeFactorisation.primeFactors algorithm i') == i' where+ i' :: Integer+ i' = succ $ i `mod` 1000000++ prop_smoothness algorithm i = Test.QuickCheck.label "prop_smoothness" $ (Math.PrimeFactorisation.smoothness algorithm !! (2 ^ i')) <= (2 :: Integer) where+ i' :: Integer+ i' = i `mod` 20++ prop_eulersTotientP algorithm i = Test.QuickCheck.label "prop_eulersTotientP" $ Math.PrimeFactorisation.eulersTotient algorithm prime == pred prime where+ prime :: Integer+ prime = Data.List.genericIndex Data.Numbers.Primes.primes (i `mod` 10000)++ prop_eulersTotientInequality algorithm i = i `notElem` [2, 6] ==> Test.QuickCheck.label "prop_eulersTotientInequality" $ Math.PrimeFactorisation.eulersTotient algorithm i' >= floor (sqrt $ fromIntegral i' :: Double) where+ i' = succ $ i `mod` 100000++ prop_eulersTotient, prop_lagrange, prop_multiplicativeOrder, prop_perfectPower :: Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Integer -> Test.QuickCheck.Property+ prop_eulersTotient algorithm i power = Test.QuickCheck.label "prop_eulersTotient" $ Math.PrimeFactorisation.eulersTotient algorithm (base ^ power') == (base ^ pred power') * pred base where+ base :: Integer+ base = Data.List.genericIndex Data.Numbers.Primes.primes (i `mod` 8)++ power' = succ $ power `mod` 5++ prop_lagrange algorithm base modulus = gcd base modulus' == 1 ==> Test.QuickCheck.label "prop_lagrange" $ (Math.PrimeFactorisation.eulersTotient algorithm modulus' `rem` Math.MultiplicativeOrder.multiplicativeOrder algorithm base modulus') == 0 where+ modulus' :: Integer+ modulus' = 2 + abs modulus++ prop_multiplicativeOrder algorithm base modulus = gcd base modulus' == 1 ==> Test.QuickCheck.label "prop_multiplicativeOrder" $ (+ base ^ Math.MultiplicativeOrder.multiplicativeOrder algorithm base modulus'+ ) `mod` modulus' == 1 where+ modulus' :: Integer+ modulus' = 2 + abs modulus++ prop_perfectPower algorithm b e = Test.QuickCheck.label "prop_perfectPower" $ foldr1 gcd (+ map Data.Exponential.getExponent . Math.PrimeFactorisation.primeFactors algorithm $ (2 + b `mod` 10 :: Integer) ^ (2 + e `mod` 5)+ ) > 1
+ src-test/Factory/Test/QuickCheck/Primes.hs view
@@ -0,0 +1,101 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.Primes".+-}++module Factory.Test.QuickCheck.Primes(+-- * Constants+-- defaultAlgorithm,+ results,+-- * Functions+-- isPrime,+ upperBound+) where++import qualified Control.DeepSeq+import qualified Data.Set+import qualified Factory.Data.PrimeWheel as Data.PrimeWheel+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.Primality as Math.Primality+import qualified Factory.Math.Primes as Math.Primes+import qualified Test.QuickCheck+import Test.QuickCheck((==>))+import qualified ToolShed.Defaultable++instance Test.QuickCheck.Arbitrary Math.Implementations.Primes.Algorithm.Algorithm where+ arbitrary = Test.QuickCheck.oneof [+ return {-to Gen-monad-} Math.Implementations.Primes.Algorithm.TurnersSieve,+ (Math.Implementations.Primes.Algorithm.TrialDivision . (`mod` 10)) `fmap` Test.QuickCheck.arbitrary,+ (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes . (`mod` 10)) `fmap` Test.QuickCheck.arbitrary+ ]++isPrime :: (Control.DeepSeq.NFData i, Integral i, Show i) => i -> Bool+isPrime = Math.Primality.isPrime primalityAlgorithm where+ primalityAlgorithm :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm+ primalityAlgorithm = ToolShed.Defaultable.defaultValue++upperBound :: Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Int+upperBound algorithm i = mod i $ if algorithm == Math.Implementations.Primes.Algorithm.TurnersSieve+ then 8192+ else 65536++defaultAlgorithm :: Math.Implementations.Primes.Algorithm.Algorithm+defaultAlgorithm = ToolShed.Defaultable.defaultValue++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_isPrime,+ Test.QuickCheck.quickCheckResult prop_isComposite,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } prop_consistency,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 25 } prop_rewriteRule,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 25 } prop_sieveOfAtkin,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 25 } prop_sieveOfAtkinRewrite+ ] where+ prop_isPrime, prop_isComposite :: Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Test.QuickCheck.Property+ prop_isPrime algorithm i = Test.QuickCheck.label "prop_isPrime" . all isPrime . takeWhile (<= upperBound algorithm i) $ (Math.Primes.primes algorithm :: [Int])+ prop_isComposite algorithm i = Test.QuickCheck.label "prop_isComposite" . not . any isPrime . Data.Set.toList . Data.Set.difference (+ Data.Set.fromList [2 .. upperBound algorithm i]+ ) . Data.Set.fromList . takeWhile (<= upperBound algorithm i) $ Math.Primes.primes algorithm++ prop_consistency :: Math.Implementations.Primes.Algorithm.Algorithm -> Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Test.QuickCheck.Property+ prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" . and . take (i `mod` 4096) $ zipWith (==) (Math.Primes.primes l) (Math.Primes.primes r :: [Int])++ prop_rewriteRule :: Data.PrimeWheel.NPrimes -> Int -> Test.QuickCheck.Property+ prop_rewriteRule wheelSize i = Test.QuickCheck.label "prop_rewriteRule" $ toInteger (Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize') !! index :: Int) == (Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize') !! index :: Integer) where+ wheelSize' = wheelSize `mod` 8+ index = i `mod` 131072++ prop_sieveOfAtkin, prop_sieveOfAtkinRewrite :: Int -> Test.QuickCheck.Property+ prop_sieveOfAtkin i = Test.QuickCheck.label "prop_sieveOfAtkin" $ Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfAtkin prime) !! index == prime where+ index = i `mod` 131072++ prime :: Integer+ prime = Math.Primes.primes defaultAlgorithm !! index++ prop_sieveOfAtkinRewrite i = Test.QuickCheck.label "prop_sieveOfAtkinRewrite" $ Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfAtkin $ fromIntegral prime) !! index == prime where+ index = i `mod` 131072++ prime :: Int+ prime = Math.Primes.primes defaultAlgorithm !! index+
+ src-test/Factory/Test/QuickCheck/Probability.hs view
@@ -0,0 +1,161 @@+{-+ Copyright (C) 2011-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 "Math.Probability".+-}++module Factory.Test.QuickCheck.Probability(+-- * Constants+ results,+-- * Functions+-- normalise+) where++import Control.Arrow((&&&))+import qualified Data.List+import qualified Factory.Math.Probability as Math.Probability+import qualified Factory.Math.Statistics as Math.Statistics+import Factory.Test.QuickCheck.Factorial()+import qualified System.Random+import qualified Test.QuickCheck+import Test.QuickCheck((==>))+import qualified ToolShed.Data.Pair++-- | Re-profile a distribution to achieve a standard mean & variance.+normalise :: (+ Eq f,+ Floating f,+ Math.Probability.Distribution distribution+ ) => distribution -> [f] -> [f]+normalise distribution+ | variance == 0 = error "Factory.Test.Quick.Probability.normalise:\tzero variance => can't stretch to one."+ | otherwise = map $ (/ sqrt variance) . (+ negate mean)+ where+ (mean, variance) = Math.Probability.getMean &&& Math.Probability.getVariance $ distribution++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = let+ isWithinTolerance :: Double -> Double -> Bool+ isWithinTolerance i = (< recip i) . abs++ prop_logNormalDistribution, prop_logNormalDistribution', prop_normalDistribution, prop_uniformDistribution :: System.Random.RandomGen randomGen => randomGen -> Double -> Double -> Test.QuickCheck.Property+ prop_logNormalDistribution randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 1) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where+ maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)+ location'+ | location >= 0 = maxParameter `min` location+ | otherwise = negate maxParameter `max` location++ distribution = Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2++ prop_logNormalDistribution' randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution'" . all (+ >= (0 :: Double)+ ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2) randomGen where+ maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)++ location'+ | location >= 0 = maxParameter `min` location+ | otherwise = negate maxParameter `max` location++-- The mean & standard-deviation are equal when scale^2 == ln 2, but this seems to break-down when the mean is close to zero.+ prop_logNormalDistributionEqual :: System.Random.RandomGen randomGen => randomGen -> Double -> Test.QuickCheck.Property+ prop_logNormalDistributionEqual randomGen location = location' > 16 {-any lower & it seems to fail-} ==> Test.QuickCheck.label "prop_logNormalDistributionEqual" . (+ < (recip 1000000 :: Double)+ ) . pred . abs . uncurry (/) . (+ Math.Statistics.getMean &&& Math.Statistics.getStandardDeviation+ ) $ take 10000 (+ Math.Probability.generatePopulation (Math.Probability.LogNormalDistribution location' $ log 2) randomGen :: [Double]+ ) where+ maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)++ location'+ | location >= 0 = maxParameter `min` location+ | otherwise = negate maxParameter `max` location++ prop_normalDistribution randomGen mean variance = variance /= 0 ==> Test.QuickCheck.label "prop_normalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where+ distribution = Math.Probability.NormalDistribution mean $ abs variance++ prop_uniformDistribution randomGen min' max' = min' /= max' ==> Test.QuickCheck.label "prop_uniformDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where+ [min'', max''] = Data.List.sort [min', max']+ distribution = Math.Probability.UniformDistribution (min'', max'')++ prop_exponentialDistribution, prop_exponentialDistribution', prop_poissonDistribution, prop_poissonDistribution', prop_shiftedGeometricDistribution, prop_shiftedGeometricDistribution' :: System.Random.RandomGen randomGen => randomGen -> Double -> Test.QuickCheck.Property+ prop_exponentialDistribution randomGen lambda = Test.QuickCheck.label "prop_exponentialDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where+ distribution = Math.Probability.ExponentialDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}++ prop_exponentialDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_exponentialDistribution'" . all (+ >= (0 :: Double)+ ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ExponentialDistribution $ abs lambda) randomGen++ prop_poissonDistribution randomGen lambda = Test.QuickCheck.label "prop_poissonDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where+ distribution = Math.Probability.PoissonDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}++ prop_poissonDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_poissonDistribution'" . all (+ >= (0 :: Double)+ ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.PoissonDistribution $ abs lambda) randomGen++ prop_shiftedGeometricDistribution randomGen probability = probability' /= 1 ==> Test.QuickCheck.label "prop_shiftedGeometricDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (+ Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.+ ) . (+ normalise distribution :: [Double] -> [Double]+ ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where+ probability' = recip . succ $ abs probability -- Semi-closed unit-interval (0, 1].+ distribution = Math.Probability.ShiftedGeometricDistribution probability'++ prop_shiftedGeometricDistribution' randomGen probability = Test.QuickCheck.label "prop_shiftedGeometricDistribution'" . all (+ >= (1 :: Double)+ ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ShiftedGeometricDistribution probability') randomGen where+ probability' = recip . succ $ abs probability -- Semi-closed unit-interval (0, 1].+ in do+ randomGen <- System.Random.getStdGen++ sequence [+ Test.QuickCheck.quickCheckResult $ prop_logNormalDistributionEqual randomGen, -- CAVEAT: known to fail occasionally.+ Test.QuickCheck.quickCheckResult $ prop_logNormalDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_logNormalDistribution' randomGen,+ Test.QuickCheck.quickCheckResult $ prop_normalDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_uniformDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_exponentialDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_exponentialDistribution' randomGen,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 25 } $ prop_poissonDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_poissonDistribution' randomGen,+ Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 50 } $ prop_shiftedGeometricDistribution randomGen,+ Test.QuickCheck.quickCheckResult $ prop_shiftedGeometricDistribution' randomGen+ ]+
+ src-test/Factory/Test/QuickCheck/Radix.hs view
@@ -0,0 +1,46 @@+{-+ Copyright (C) 2011-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 "Math.Radix".+-}++module Factory.Test.QuickCheck.Radix(+-- * Constants+ results,+-- * Types+-- ** Type-synonyms+-- Testable+) where++import qualified Factory.Math.Radix as Math.Radix+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++type Testable = (Int, Integer) -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [prop_reversable, prop_digitalRoot] where+ prop_reversable, prop_digitalRoot :: Testable+ prop_reversable (b, n) = abs base > 1 ==> Test.QuickCheck.label "prop_reversable" $ Math.Radix.fromBase base (Math.Radix.toBase base n) == n where+ base = (b `mod` 73) - 36++ prop_digitalRoot (_, n) = Test.QuickCheck.label "prop_digitalRoot" $ Math.Radix.digitalRoot n' == 9 where+ n' = 9 * succ (abs n)+
+ src-test/Factory/Test/QuickCheck/SquareRoot.hs view
@@ -0,0 +1,85 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.SquareRoot".+-}++module Factory.Test.QuickCheck.SquareRoot(+-- * Constants+ results+) where++import Data.Ratio((%))+import qualified Data.Ratio+import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Precision as Math.Precision+import qualified Factory.Math.SquareRoot as Math.SquareRoot+import qualified Test.QuickCheck++instance Test.QuickCheck.Arbitrary (Math.Implementations.SquareRoot.Algorithm) where+ arbitrary = Test.QuickCheck.oneof [+ Test.QuickCheck.elements [+ Math.Implementations.SquareRoot.BakhshaliApproximation,+ Math.Implementations.SquareRoot.ContinuedFraction,+ Math.Implementations.SquareRoot.HalleysMethod,+ Math.Implementations.SquareRoot.NewtonRaphsonIteration+ ],+ Math.Implementations.SquareRoot.TaylorSeries `fmap` Test.QuickCheck.elements [2 .. 32]+ ]++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [+ prop_accuracy,+ prop_factorable+-- prop_perfectSquare -- This occasionally fails.+ ] where+ prop_accuracy, prop_factorable, prop_perfectSquare :: (Math.Implementations.SquareRoot.Algorithm, Math.Precision.DecimalDigits, Rational) -> Test.QuickCheck.Property+ prop_accuracy (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_accuracy" . (>= requiredDecimalDigits) . Math.SquareRoot.getAccuracy operand' $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand' where+ requiredDecimalDigits :: Math.Precision.DecimalDigits+ requiredDecimalDigits = succ $ decimalDigits `mod` 1024++ operand' :: Rational+ operand' = abs operand++ prop_factorable (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_factorable" . (<= 5) . (+ * 10 ^ requiredDecimalDigits -- Promote the relative error.+ ) . abs $ 1 - (+ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits (+ toRational $ Data.Ratio.numerator operand'+ ) / Math.SquareRoot.squareRoot algorithm requiredDecimalDigits (+ toRational $ Data.Ratio.denominator operand'+ )+ ) / Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand' where+ requiredDecimalDigits :: Math.Precision.DecimalDigits+ requiredDecimalDigits = succ $ decimalDigits `mod` 1024++ operand' :: Rational+ operand' = succ $ abs operand++ prop_perfectSquare (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_perfectSquare" . Math.SquareRoot.isPrecise perfectSquare $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits perfectSquare where+ requiredDecimalDigits :: Math.Precision.DecimalDigits+ requiredDecimalDigits = succ $ decimalDigits `mod` 32768++ operand', perfectSquare :: Rational+ operand' = (abs (Data.Ratio.numerator operand) `min` (2 ^ (32 :: Int))) % (abs (Data.Ratio.denominator operand) `min` (2 ^ (32 :: Int))) -- Avoid floating-point rounding-errors in 'Math.SquareRoot.rSqrt'.+ perfectSquare = Math.Power.square operand'+
+ src-test/Factory/Test/QuickCheck/Statistics.hs view
@@ -0,0 +1,125 @@+{-+ Copyright (C) 2011-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 "Math.Statistics".+-}++module Factory.Test.QuickCheck.Statistics(+-- * Constants+ results+) where++import qualified Data.Array+import qualified Data.List+import qualified Data.Map+import qualified Data.Numbers.Primes+import qualified Data.Set+import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial+import qualified Factory.Math.Power as Math.Power+import qualified Factory.Math.Statistics as Math.Statistics+import Factory.Test.QuickCheck.Factorial()+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_nC0,+ Test.QuickCheck.quickCheckResult prop_nC1,+ Test.QuickCheck.quickCheckResult prop_sum,+ Test.QuickCheck.quickCheckResult prop_symmetry,+ Test.QuickCheck.quickCheckResult prop_prime,+ Test.QuickCheck.quickCheckResult prop_nP0,+ Test.QuickCheck.quickCheckResult prop_nP1,+ Test.QuickCheck.quickCheckResult prop_zeroVariance,+ Test.QuickCheck.quickCheckResult prop_zeroAverageAbsoluteDeviation,+ Test.QuickCheck.quickCheckResult prop_balance,+ Test.QuickCheck.quickCheckResult prop_varianceRelocated,+ Test.QuickCheck.quickCheckResult prop_varianceScaled,+ Test.QuickCheck.quickCheckResult prop_varianceOrder,+ Test.QuickCheck.quickCheckResult prop_equivalence,+ Test.QuickCheck.quickCheckResult prop_varianceOfArray,+ Test.QuickCheck.quickCheckResult prop_varianceOfMap,+ Test.QuickCheck.quickCheckResult prop_meanOfSet,+ Test.QuickCheck.quickCheckResult prop_weightedMeanRational,+ Test.QuickCheck.quickCheckResult prop_weightedMeanInteger,+ Test.QuickCheck.quickCheckResult prop_weightedMeanUniformDenominator+ ] where+ prop_nC0, prop_nC1, prop_sum :: Math.Implementations.Factorial.Algorithm -> Integer -> Test.QuickCheck.Property+ prop_nC0 algorithm n = Test.QuickCheck.label "prop_nC0" $ Math.Statistics.nCr algorithm (abs n) 0 == 1++ prop_nC1 algorithm i = Test.QuickCheck.label "prop_nC1" $ Math.Statistics.nCr algorithm n 1 == n where+ n = succ $ abs i++ prop_sum algorithm i = Test.QuickCheck.label "prop_sum" $ sum (Math.Statistics.nCr algorithm n `map` [0 .. n]) == 2 ^ n where+ n = succ $ abs i++ prop_symmetry, prop_prime :: Math.Implementations.Factorial.Algorithm -> (Integer, Integer) -> Test.QuickCheck.Property+ prop_symmetry algorithm (i, j) = Test.QuickCheck.label "prop_symmetry" $ Math.Statistics.nCr algorithm n r == Math.Statistics.nCr algorithm n (n - r) where+ [r, n] = Data.List.sort $ map abs [i, j]++ prop_prime algorithm (i, j) = r `notElem` [0, n] ==> Test.QuickCheck.label "prop_prime" $ (Math.Statistics.nCr algorithm n r `mod` n) == 0 where+ n = Data.Numbers.Primes.primes !! fromIntegral (i `mod` 500000)+ r = j `mod` n -- Ensure r is smaller than n.++ prop_nP0, prop_nP1 :: Integer -> Test.QuickCheck.Property+ prop_nP0 n = Test.QuickCheck.label "prop_nP0" $ Math.Statistics.nPr (abs n) 0 == 1++ prop_nP1 i = Test.QuickCheck.label "prop_nP1" $ Math.Statistics.nPr n 1 == n where+ n = succ $ abs i++ prop_zeroVariance, prop_zeroAverageAbsoluteDeviation :: Rational -> Test.QuickCheck.Property+ prop_zeroVariance x = Test.QuickCheck.label "prop_zeroVariance" $ Math.Statistics.getVariance (replicate 32 x) == (0 :: Rational)+ prop_zeroAverageAbsoluteDeviation x = Test.QuickCheck.label "zeroAverageAbsoluteDeviation" $ Math.Statistics.getAverageAbsoluteDeviation (replicate 32 x) == (0 :: Rational)++ prop_balance, prop_varianceRelocated, prop_varianceScaled, prop_varianceOrder, prop_equivalence, prop_varianceOfMap, prop_meanOfSet, prop_varianceOfArray :: [Integer] -> Test.QuickCheck.Property+ prop_balance l = not (null l) ==> Test.QuickCheck.label "prop_balance" . (== 0) . abs . sum $ map (\i -> toRational i - Math.Statistics.getMean l) l+ prop_varianceRelocated l = not (null l) ==> Test.QuickCheck.label "prop_varianceRelocated" $ (Math.Statistics.getVariance l :: Rational) == Math.Statistics.getVariance (map succ l)+ prop_varianceScaled l = not (null l) ==> Test.QuickCheck.label "prop_varianceScaled" $ (4 * Math.Statistics.getVariance l :: Rational) == Math.Statistics.getVariance (map (* 2) l)+ prop_varianceOrder l = not (null l) ==> Test.QuickCheck.label "prop_varianceOrder" $ Math.Statistics.getVariance l == (Math.Statistics.getVariance (reverse l) :: Rational)+ prop_equivalence l = not (null l) ==> Test.QuickCheck.label "prop_equivalence" $ Math.Statistics.getVariance l == Math.Statistics.getMean (map Math.Power.square l) - Math.Power.square (Math.Statistics.getMean l :: Rational)+ prop_varianceOfArray l = not (null l) ==> Test.QuickCheck.label "prop_varianceOfArray" $ Math.Statistics.getVariance (Data.Array.array (1, length l) $ zip [1 ..] l) == (Math.Statistics.getVariance l :: Rational)+ prop_varianceOfMap l = not (null l) ==> Test.QuickCheck.label "prop_varianceOfMap" $ Math.Statistics.getVariance (Data.Map.fromList $ zip [0 :: Int ..] l) == (Math.Statistics.getVariance l :: Rational)+ prop_meanOfSet l = not (null l') ==> Test.QuickCheck.label "prop_meanOfSet" $ Math.Statistics.getMean (Data.Set.fromList l') == (Math.Statistics.getMean l' :: Rational) where+ l' = Data.List.nub l++ prop_weightedMeanRational :: [(Rational, Rational)] -> Test.QuickCheck.Property+ prop_weightedMeanRational assoc = (denominator /= 0) ==> Test.QuickCheck.label "prop_weightedMeanRational" $ Math.Statistics.getWeightedMean assoc == (+ sum (map (uncurry (*)) assoc) / denominator+ ) where+ denominator = sum $ map snd assoc+++ prop_weightedMeanInteger :: [(Integer, Integer)] -> Test.QuickCheck.Property+ prop_weightedMeanInteger assoc = (denominator /= 0) ==> Test.QuickCheck.label "prop_weightedMeanInteger" $ Math.Statistics.getWeightedMean assoc == (+ toRational (+ sum $ map (+ uncurry (*)+ ) assoc+ ) / toRational denominator+ ) where+ denominator = sum $ map snd assoc++ prop_weightedMeanUniformDenominator :: [Rational] -> Integer -> Test.QuickCheck.Property+ prop_weightedMeanUniformDenominator numerators i = (not (null numerators) && i /= 0) ==> Test.QuickCheck.label "prop_weightedMeanUniformDenominator" $ Math.Statistics.getWeightedMean (+ zip numerators $ repeat i+ ) == (+ Math.Statistics.getMean numerators :: Rational+ )+
+ src-test/Factory/Test/QuickCheck/Summation.hs view
@@ -0,0 +1,42 @@+{-+ Copyright (C) 2011-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 "Math.Summation".+-}++module Factory.Test.QuickCheck.Summation(+-- * Constants+ results+) where++import qualified Factory.Math.Summation as Math.Summation+import qualified Test.QuickCheck+import Test.QuickCheck((==>))++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [prop_sum, prop_sumR] where+ prop_sum, prop_sumR :: Int -> [Rational] -> Test.QuickCheck.Property+ prop_sum chunkSize l = not (null l) ==> Test.QuickCheck.label "prop_sum" $ Math.Summation.sum' chunkSize' l == sum l where+ chunkSize' = 2 + (chunkSize `mod` length l)++ prop_sumR chunkSize l = not (null l) ==> Test.QuickCheck.label "prop_sumR" $ Math.Summation.sumR chunkSize' l == sum l where+ chunkSize' = 2 + (chunkSize `mod` length l)++
+ src-test/Main.hs view
@@ -0,0 +1,76 @@+{-+ 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 Factory.Test.QuickCheck.ArithmeticGeometricMean as Test.QuickCheck.ArithmeticGeometricMean+import qualified Factory.Test.QuickCheck.Factorial as Test.QuickCheck.Factorial+import qualified Factory.Test.QuickCheck.Hyperoperation as Test.QuickCheck.Hyperoperation+import qualified Factory.Test.QuickCheck.Interval as Test.QuickCheck.Interval+import qualified Factory.Test.QuickCheck.MonicPolynomial as Test.QuickCheck.MonicPolynomial+import qualified Factory.Test.QuickCheck.PerfectPower as Test.QuickCheck.PerfectPower+import qualified Factory.Test.QuickCheck.Pi as Test.QuickCheck.Pi+import qualified Factory.Test.QuickCheck.Polynomial as Test.QuickCheck.Polynomial+import qualified Factory.Test.QuickCheck.Power as Test.QuickCheck.Power+import qualified Factory.Test.QuickCheck.Primality as Test.QuickCheck.Primality+import qualified Factory.Test.QuickCheck.PrimeFactorisation as Test.QuickCheck.PrimeFactorisation+import qualified Factory.Test.QuickCheck.Primes as Test.QuickCheck.Primes+import qualified Factory.Test.QuickCheck.Probability as Test.QuickCheck.Probability+import qualified Factory.Test.QuickCheck.Radix as Test.QuickCheck.Radix+import qualified Factory.Test.QuickCheck.SquareRoot as Test.QuickCheck.SquareRoot+import qualified Factory.Test.QuickCheck.Statistics as Test.QuickCheck.Statistics+import qualified Factory.Test.QuickCheck.Summation as Test.QuickCheck.Summation+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+ )+ )+ ) [+ ("ArithmeticGeometricMean", Test.QuickCheck.ArithmeticGeometricMean.results),+ ("Factorial", Test.QuickCheck.Factorial.results),+ ("Hyperoperation", Test.QuickCheck.Hyperoperation.results),+ ("Interval", Test.QuickCheck.Interval.results),+ ("MonicPolynomial", Test.QuickCheck.MonicPolynomial.results),+ ("PerfectPower", Test.QuickCheck.PerfectPower.results),+ ("Pi", Test.QuickCheck.Pi.results),+ ("Polynomial", Test.QuickCheck.Polynomial.results),+ ("Power", Test.QuickCheck.Power.results),+ ("Primality", Test.QuickCheck.Primality.results),+ ("PrimeFactorisation", Test.QuickCheck.PrimeFactorisation.results),+ ("Primes", Test.QuickCheck.Primes.results),+ ("Probability", Test.QuickCheck.Probability.results),+ ("Radix", Test.QuickCheck.Radix.results),+ ("SquareRoot", Test.QuickCheck.SquareRoot.results),+ ("Statistics", Test.QuickCheck.Statistics.results),+ ("Summation", Test.QuickCheck.Summation.results)+ ]+
− src/Factory/Data/Exponential.hs
@@ -1,89 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a simple numeric type, designed to contain an /exponential/ number.-- * <http://en.wikipedia.org/wiki/Exponentiation>.--}--module Factory.Data.Exponential(--- * Types--- ** Type-synonyms- Exponential,--- * Functions- evaluate,- invert,--- ** Accessors- getBase,- getExponent,--- ** Constructors- rightIdentity,--- ** Operators- (<^),- (=~)-) where--import qualified Control.Arrow--infix 4 =~ -- Same as (==).-infixr 8 <^ -- Same as (^).---- | Describes an /exponential/, in terms of its /base/ and /exponent/.-type Exponential base exponent = (base, exponent)---- | Accessor.-{-# INLINE getBase #-}-getBase :: Exponential base exponent -> base-getBase = fst---- | Accessor.-{-# INLINE getExponent #-}-getExponent :: Exponential base exponent -> exponent-getExponent = snd--{- |- * Construct an 'Exponential' merely raised to the 1st power.-- * The value of the resulting exponential is the same as specified 'base'; <http://en.wikipedia.org/wiki/Identity_element>.--}-rightIdentity :: Num exponent => base -> Exponential base exponent-rightIdentity x = (x, 1)---- | Evaluate the specified 'Exponential', returning the resulting number.-{-# INLINE evaluate #-}-evaluate :: (Num base, Integral exponent) => Exponential base exponent -> base-evaluate = uncurry (^) -- CAVEAT: in this eta-reduced form, it'll only be inlined when called without arguments.---- | True if the /bases/ are equal.-(=~) :: Eq base => Exponential base exponent -> Exponential base exponent -> Bool-(l, _) =~ (r, _) = l == r---- | Raise the specified 'Exponential' to a power.-(<^) :: Num exponent- => Exponential base exponent -- ^ The operand.- -> exponent -- ^ The power to which the exponential is to be raised.- -> Exponential base exponent -- ^ The result.-(b, e) <^ power = (b, e * power)---- | Invert the value, by negating the exponent.-invert :: Num exponent => Exponential base exponent -> Exponential base exponent-invert = Control.Arrow.second negate-
− src/Factory/Data/Interval.hs
@@ -1,201 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a bounded set of, typically integral, quantities.-- * Operations have been defined, on the list of /consecutive/ quantities delimited by these endpoints.-- * The point is that if the list is composed from /consecutive/ quantities, the intermediate values can be inferred, rather than physically represented.-- [@CAVEATS@]-- * The API was driven top-down by its caller's requirements, rather than a bottom-up attempt to provide a complete interface.- consequently there may be omissions from the view point of future callers.-- * Thought similar to the mathematical concept of an /interval/, the latter technically relates to /real/ numbers; <http://en.wikipedia.org/wiki/Interval_%28mathematics%29>.-- * No account has been made for /semi-closed/ or /open/ intervals.--}--module Factory.Data.Interval(--- * Types--- ** Type-synonyms- Interval,--- * Constants- closedUnitInterval,- mkBounded,--- * Functions--- divideAndConquer,- elem',--- getLength,- normalise,- product',- shift,- splitAt',- toList,--- ** Accessors- getMinBound,- getMaxBound,--- ** Constructors- precisely,--- ** Predicates- isReversed-) where--import Control.Arrow((***), (&&&))-import qualified Control.Parallel.Strategies-import qualified Data.Monoid-import qualified Data.Ratio-import qualified Data.Tuple-import qualified ToolShed.Data.Pair---- | Defines a closed (inclusive) interval of consecutive values.-type Interval endPoint = (endPoint, endPoint)---- | Accessor.-{-# INLINE getMinBound #-}-getMinBound :: Interval endPoint -> endPoint-getMinBound = fst---- | Accessor.-{-# INLINE getMaxBound #-}-getMaxBound :: Interval endPoint -> endPoint-getMaxBound = snd---- | Construct the /unsigned closed unit-interval/; <http://en.wikipedia.org/wiki/Unit_interval>.-closedUnitInterval :: Num n => Interval n-closedUnitInterval = (0, 1)---- | Construct an /interval/ from a bounded type.-mkBounded :: Bounded endPoint => Interval endPoint-mkBounded = (minBound, maxBound)---- | Construct an /interval/ from a single value.-precisely :: endPoint -> Interval endPoint-precisely = id &&& id---- | Shift of both /end-points/ of the /interval/ by the specified amount.-shift :: Num endPoint- => endPoint -- ^ The magnitude of the require shift.- -> Interval endPoint -- ^ The interval to be shifted.- -> Interval endPoint-shift i = ToolShed.Data.Pair.mirror (+ i)---- | True if the specified value is within the inclusive bounds of the /interval/.-elem' :: Ord endPoint => endPoint -> Interval endPoint -> Bool-elem' x = uncurry (&&) . ((<= x) *** (x <=))---- | True if 'getMinBound' exceeds 'getMaxBound' extent.-isReversed :: Ord endPoint => Interval endPoint -> Bool-isReversed = uncurry (>)---- | Swap the /end-points/ where they were originally reversed, but otherwise do nothing.-normalise :: Ord endPoint => Interval endPoint -> Interval endPoint-normalise b- | isReversed b = Data.Tuple.swap b- | otherwise = b---- | Bisect the /interval/ at the specified /end-point/; which should be between the two existing /end-points/.-splitAt' :: (- Enum endPoint,- Num endPoint,- Ord endPoint,- Show endPoint- ) => endPoint -> Interval endPoint -> (Interval endPoint, Interval endPoint)-splitAt' i interval@(l, r)- | any ($ i) [(< l), (>= r)] = error $ "Factory.Data.Interval.splitAt':\tunsuitable index=" ++ show i ++ " for interval=" ++ show interval ++ "."- | otherwise = ((l, i), (succ i, r))--{- |- * The distance between the endpoints,- which for 'Integral' quantities is the same as the number of items in closed interval; though the latter concept would return type 'Int'.-- * CAVEAT: the implementation accounts for the potential fence-post error, for closed intervals of integers,- but this results in the opposite error when used with /Fractional/ quantities.- So, though most of the module merely requires 'Enum', this function is further restricted to 'Integral'.--}-{-# INLINE getLength #-}-getLength :: Integral endPoint => Interval endPoint -> endPoint-getLength (l, r) = succ r - l--{- |- * Converts 'Interval' to a list by enumerating the values.-- * CAVEAT: produces rather odd results for 'Fractional' types, but no stranger than considering such types Enumerable in the first place.--}-{-# INLINE toList #-}-toList :: Enum endPoint => Interval endPoint -> [endPoint]-toList = uncurry enumFromTo -- CAVEAT: in this eta-reduced form, it'll only be inlined when called without arguments.--{- |- * Reduces 'Interval' to a single integral value encapsulated in a 'Data.Monoid.Monoid',- using a /divide-and-conquer/ strategy,- bisecting the /interval/ and recursively evaluating each part; <http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm>.-- * By choosing a 'ratio' other than @(1 % 2)@, the bisection can be made asymmetrical.- The specified ratio represents the length of the left-hand portion over the original list-length;- eg. @(1 % 3)@ results in the first part, half the length of the second.-- * This process of recursive bisection, is terminated beneath the specified minimum length,- after which the 'Interval' are expanded into the corresponding list, and the /monoid/'s binary operator is directly /folded/ over it.-- * One can view this as a <http://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>,- in which 'Interval' is exploded into a binary tree-structure- (each leaf of which contains a list of up to 'minLength' integers, and each node of which contains an associative binary operator),- and then collapsed to a scalar, by application of the operators.--}-divideAndConquer :: (Data.Monoid.Monoid monoid, Integral i, Show i)- => (i -> monoid) -- ^ The monoid's constructor.- -> Data.Ratio.Ratio i -- ^ The ratio of the original span, at which to bisect the 'Interval'.- -> i -- ^ For efficiency, the /interval/ will not be bisected, when it's length has been reduced to this value.- -> Interval i- -> monoid -- ^ The resulting scalar.-divideAndConquer monoidConstructor ratio minLength- | any ($ ratio) [- (< 0),- (>= 1)- ] = error $ "Factory.Data.Interval.divideAndConquer:\tunsuitable ratio='" ++ show ratio ++ "'."- | minLength < 1 = error $ "Factory.Data.Interval.divideAndConquer:\tunsuitable minLength=" ++ show minLength ++ "."- | otherwise = slave- where- slave interval@(l, r)- | getLength interval <= minLength = Data.Monoid.mconcat . map monoidConstructor $ toList interval -- Fold the monoid's binary operator over the delimited list.- | otherwise = uncurry Data.Monoid.mappend . Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rseq Control.Parallel.Strategies.rseq- ) . ToolShed.Data.Pair.mirror slave $ splitAt' (- l + (r - l) * Data.Ratio.numerator ratio `div` Data.Ratio.denominator ratio -- Use the ratio to generate the split-index.- ) interval -- Apply the monoid's binary operator to the two operands resulting from bisection.--{- |- * Multiplies the consecutive sequence of integers within 'Interval'.-- * Since the result can be large, 'divideAndConquer' is used to form operands of a similar order of magnitude,- thus improving the efficiency of the big-number multiplication.--}-product' :: (Integral i, Show i)- => Data.Ratio.Ratio i -- ^ The ratio at which to bisect the 'Interval'.- -> i -- ^ For efficiency, the /interval/ will not be bisected, when it's length has been reduced to this value.- -> Interval i- -> i -- ^ The resulting product.-product' ratio minLength interval- | elem' 0 interval = 0- | otherwise = Data.Monoid.getProduct $ divideAndConquer Data.Monoid.Product ratio minLength interval-
− src/Factory/Data/MonicPolynomial.hs
@@ -1,98 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a /monic polynomial; <http://en.wikipedia.org/wiki/Monic_polynomial#Classifications>;- ie. in which the /coefficient/ of the /leading term/ is one.--}--module Factory.Data.MonicPolynomial(--- * Types--- ** Data-types,- MonicPolynomial(getPolynomial), -- Hide the data-constructor.--- * Functions--- ** Constructors- mkMonicPolynomial-) where--import qualified Control.Arrow-import qualified Factory.Data.Monomial as Data.Monomial-import Factory.Data.Polynomial((*=))-import qualified Factory.Data.Polynomial as Data.Polynomial-import qualified Factory.Data.QuotientRing as Data.QuotientRing-import Factory.Data.Ring((=*=), (=+=), (=-=))-import qualified Factory.Data.Ring as Data.Ring-import qualified ToolShed.Data.Pair---- | A type of 'Data.Polynomial.Polynomial', in which the /leading term/ is required to have a /coefficient/ of one.-newtype MonicPolynomial c e = MkMonicPolynomial {- getPolynomial :: Data.Polynomial.Polynomial c e-} deriving (Eq, Show)---- | Smart constructor. Constructs an arbitrary /monic polynomial/.-mkMonicPolynomial :: (- Eq c,- Num c,- Ord e,- Show c,- Show e- ) => Data.Polynomial.Polynomial c e -> MonicPolynomial c e-mkMonicPolynomial polynomial- | not $ Data.Polynomial.isMonic polynomial = error $ "Factory.Data.MonicPolynomial.mkMonicPolynomial:\tnot monic; " ++ show polynomial- | otherwise = MkMonicPolynomial polynomial--{-- * This instance-declaration merely delegates to the 'Data.Polynomial.Polynomial' payload.-- * CAVEAT: it's not strictly an instance of this class, since the result of some methods isn't /monic/.--}-instance (- Eq c,- Num c,- Num e,- Ord e,- Show c,- Show e- ) => Data.Ring.Ring (MonicPolynomial c e) where- MkMonicPolynomial l =*= MkMonicPolynomial r = MkMonicPolynomial $ l =*= r- MkMonicPolynomial l =+= MkMonicPolynomial r = mkMonicPolynomial $ l =+= r -- CAVEAT: potentially non-monic.--- additiveInverse (MkMonicPolynomial p) = MkMonicPolynomial $ Data.Ring.additiveInverse p -- CAVEAT: not monic !- additiveInverse _ = error "Factory.Data.MonicPolynomial.additiveInverse:\tresult isn't monic"- multiplicativeIdentity = MkMonicPolynomial Data.Ring.multiplicativeIdentity- additiveIdentity = MkMonicPolynomial Data.Ring.additiveIdentity -- CAVEAT: not monic !---- Since the /leading term/ of the /denominator/ is one, the /coefficient/ isn't required to implement 'Fractional'.-instance (- Eq c,- Num c,- Num e,- Ord e,- Show c,- Show e- ) => Data.QuotientRing.QuotientRing (MonicPolynomial c e) where- MkMonicPolynomial polynomialN `quotRem'` MkMonicPolynomial polynomialD = ToolShed.Data.Pair.mirror MkMonicPolynomial $ longDivide polynomialN where--- longDivide :: (Num c, Num e, Ord e) => Polynomial c e -> (Polynomial c e, Polynomial c e)- longDivide numerator- | Data.Polynomial.isZero numerator || Data.Monomial.getExponent quotient < 0 = (Data.Polynomial.zero, numerator)- | otherwise = Control.Arrow.first (Data.Polynomial.lift (quotient :)) $ longDivide (numerator =-= polynomialD *= quotient)- where--- quotient :: Num e => Data.Monomial.Monomial c e- quotient = Data.Polynomial.getLeadingTerm numerator `Data.Monomial.shiftExponent` negate (Data.Monomial.getExponent $ Data.Polynomial.getLeadingTerm polynomialD)-
− src/Factory/Data/Monomial.hs
@@ -1,148 +0,0 @@-{-- Copyright (C) 2011-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@]-- * Describes a <http://en.wikipedia.org/wiki/Monomial> and operations on it.-- * A /monomial/ is merely a /polynomial/ with a single non-zero term; cf. /Binomial/.--}--module Factory.Data.Monomial(--- * Types--- ** Type-synonyms- Monomial,--- * Functions- double,- mod',- negateCoefficient,- realCoefficientToFrac,- shiftCoefficient,- shiftExponent,- square,--- ** Accessors- getExponent,- getCoefficient,--- ** Operators- (<=>),- (</>),- (<*>),- (=~),--- ** Predicates- isMonomial-) where--import Prelude hiding ((<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import qualified Control.Arrow--infix 4 <=> -- Same as (==).-infix 4 =~ -- Same as (==).-infixl 7 </> -- Same as (/).-infixl 7 <*> -- Same as (*).--{- |- * The type of an arbitrary monomial.-- * CAVEAT: though a /monomial/ has an integral power, this contraint is only imposed at the function-level.--}-type Monomial coefficient exponent = (coefficient, exponent)---- | Accessor.-{-# INLINE getCoefficient #-}-getCoefficient :: Monomial c e -> c-getCoefficient = fst---- | Accessor.-{-# INLINE getExponent #-}-getExponent :: Monomial c e -> e-getExponent = snd--{- |- * 'True' if the /exponent/ is both integral and non-/negative/.-- * CAVEAT: one can't even call this function unless the /exponent/ is integral.--}-isMonomial :: Integral e => Monomial c e -> Bool-isMonomial = (>= 0) . getExponent---- | Compares the /exponents/ of the specified 'Monomial's.-{-# INLINE (<=>) #-}-(<=>) :: Ord e => Monomial c e -> Monomial c e -> Ordering-(_, l) <=> (_, r) = l `compare` r---- | True if the /exponents/ are equal.-(=~) :: Eq e => Monomial c e -> Monomial c e -> Bool-(_, l) =~ (_, r) = l == r---- | Multiply the two specified 'Monomial's.-{-# INLINE (<*>) #-}-(<*>) :: (Num c, Num e) => Monomial c e -> Monomial c e -> Monomial c e-(cL, eL) <*> (cR, eR) = (cL * cR, eL + eR)---- | Divide the two specified 'Monomial's.-(</>) :: (Eq c, Fractional c, Num e)- => Monomial c e -- ^ Numerator.- -> Monomial c e -- ^ Denominator.- -> Monomial c e-(cN, eN) </> (1, eD) = (cN, eN - eD)-(cN, eN) </> (cD, eD) = (cN / cD, eN - eD)---- | Square the specified 'Monomial'.-square :: (Num c, Num e) => Monomial c e -> Monomial c e-square (c, e) = (c ^ (2 :: Int), 2 * e)---- | Double the specified 'Monomial'.-{-# INLINE double #-}-double :: Num c => Monomial c e -> Monomial c e-double (c, e) = (2 * c, e)---- | Shift the /coefficient/, by the specified amount.-{-# INLINE shiftCoefficient #-}-shiftCoefficient :: Num c- => Monomial c e- -> c -- ^ The magnitude of the shift.- -> Monomial c e--- m `shiftCoefficient` i = Control.Arrow.first (+ i) m -- CAVEAT: Too slow.-(c, e) `shiftCoefficient` i = (c + i, e)---- | Shift the /exponent/, by the specified amount.-{-# INLINE shiftExponent #-}-shiftExponent :: Num e- => Monomial c e- -> e -- ^ The magnitude of the shift.- -> Monomial c e--- m `shiftExponent` i = Control.Arrow.second (+ i) m -- CAVEAT: Too slow.-(c, e) `shiftExponent` i = (c, e + i)---- | Negate the coefficient.-negateCoefficient :: Num c => Monomial c e -> Monomial c e-negateCoefficient = Control.Arrow.first negate---- | Reduce the coefficient using /modular/ arithmetic.-{-# INLINE mod' #-}-mod' :: Integral c- => Monomial c e- -> c -- ^ Modulus.- -> Monomial c e-monomial `mod'` modulus = Control.Arrow.first (`mod` modulus) monomial---- | Convert the type of the /coefficient/.-realCoefficientToFrac :: (Real r, Fractional f) => Monomial r e -> Monomial f e-realCoefficientToFrac = Control.Arrow.first realToFrac-
− src/Factory/Data/Polynomial.hs
@@ -1,375 +0,0 @@-{-- Copyright (C) 2011-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@]-- * Describes a <http://en.wikipedia.org/wiki/Univariate> polynomial and operations on it.-- * <http://en.wikipedia.org/wiki/Polynomial>.-- * <http://mathworld.wolfram.com/Polynomial.html>.--}--module Factory.Data.Polynomial(--- * Types--- ** Type-synonyms--- MonomialList,--- ** Data-types,- Polynomial,--- * Constants- zero,- one,--- * Functions- evaluate,- getDegree,- getLeadingTerm,- lift,- mod',- normalise,--- pruneCoefficients,- raiseModulo,- realCoefficientsToFrac,- terms,--- ** Constructors- mkConstant,- mkLinear,- mkPolynomial,--- ** Operators- (*=),--- ** Predicates- areCongruentModulo,- inAscendingOrder,- inDescendingOrder,--- inOrder,- isMonic,- isMonomial,- isNormalised,- isPolynomial,--- isReduced,- isZero-) where--import Prelude hiding ((<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import Control.Arrow((&&&))-import qualified Control.Arrow-import qualified Data.List-import Factory.Data.Monomial((<*>), (</>), (<=>), (=~))-import qualified Factory.Data.Monomial as Data.Monomial-import qualified Factory.Data.QuotientRing as Data.QuotientRing-import Factory.Data.Ring((=*=), (=+=), (=-=))-import qualified Factory.Data.Ring as Data.Ring--infixl 7 *= -- Same as (*).---- | The guts of a 'Polynomial'.-type MonomialList coefficient exponent = [Data.Monomial.Monomial coefficient exponent]--{- |- * The type of an arbitrary /univariate/ polynomial;- actually it's more general, since it permits negative powers (<http://en.wikipedia.org/wiki/Laurent_polynomial>s).- It can't describe /multivariate/ polynomials, which would require a list of /exponents/.- Rather than requiring the /exponent/ to implement the /type-class/ 'Integral', this is implemented at the function-level, as required.-- * The structure permits gaps between /exponents/,- in which /coefficients/ are inferred to be zero, thus enabling efficient representation of sparse polynomials.-- * CAVEAT: the 'MonomialList' is required to;- be ordered by /descending/ exponent (ie. reverse <http://en.wikipedia.org/wiki/Monomial_order>);- have had zero coefficients removed;- and to have had /like/ terms merged;- so the raw data-constructor isn't exported.--}-newtype {- Integral exponent => -} Polynomial coefficient exponent = MkPolynomial {- getMonomialList :: MonomialList coefficient exponent -- ^ Accessor.-} deriving (Eq, Show)---- | Makes /Polynomial/ a 'Data.Ring.Ring', over the /field/ composed from all possible /coefficients/; <http://en.wikipedia.org/wiki/Polynomial_ring>.-instance (- Eq c,- Num c,- Num e,- Ord e- ) => Data.Ring.Ring (Polynomial c e) where- MkPolynomial [] =*= _ = zero- _ =*= MkPolynomial [] = zero- polynomialL =*= polynomialR--- | polynomialL == one = polynomialR -- Counterproductive.--- | polynomialR == one = polynomialL -- Counterproductive.- | terms polynomialL > terms polynomialR = polynomialL `times` polynomialR- | otherwise = polynomialR `times` polynomialL- where- l `times` r = {-# SCC "times" #-} Data.Ring.sum' (recip 2) {-TODO-} 10 {-empirical-} . map (l *=) $ getMonomialList r-- MkPolynomial [] =+= p = p- p =+= MkPolynomial [] = p- MkPolynomial listL =+= MkPolynomial listR = {-# SCC "merge" #-} MkPolynomial $ merge listL listR where- merge [] r = r- merge l [] = l- merge l@(lh : ls) r@(rh : rs) = case lh <=> rh of- GT -> lh : merge ls r- LT -> rh : merge l rs- _ -> case lh `Data.Monomial.shiftCoefficient` Data.Monomial.getCoefficient rh of- (0, _) -> merge ls rs- monomial -> monomial : merge ls rs-- additiveInverse = lift (Data.Monomial.negateCoefficient `map`)- multiplicativeIdentity = one- additiveIdentity = zero--{-- Override the default implementation,- in order to take advantage of the symmetry under reflection about the main diagonal,- in the square matrix of products formed from the multiplication of each term by each term.- Eg:- (ax^3 + bx^2 + cx + d)^2 = [- (a^2x^6 + abx^5 + acx^4 + adx^3) +- (bax^5 + b^2x^4 + bcx^3 + bdx^2) +- (cax^4 + cbx^3 + c^2x^2 + cdx) +- (dax^3 + dbx^2 + dcx + d^2)- ]-- = (a^2x^6 + b^2x^4 + c^2x^2 + d^2) + 2 * [ax^3 * (bx^2 + cx + d) + bx^2 * (cx + d) + cx * (d)]--}- square (MkPolynomial []) = zero- square p = Data.Ring.sum' (recip 2) {-TODO-} 10 {-empirical-} $ diagonal : corners where- diagonal = {-# SCC "diagonal" #-} map Data.Monomial.square `lift` p- corners = {-# SCC "corners" #-} uncurry (- zipWith (*=)- ) $ map MkPolynomial . init {-remove terminal null-} . Data.List.tails . tail &&& map Data.Monomial.double $ getMonomialList p---- | Defines the ability to divide /polynomials/.-instance (- Eq c,- Fractional c,- Num e,- Ord e- ) => Data.QuotientRing.QuotientRing (Polynomial c e) where-{-- Uses /Euclidian division/.- <http://en.wikipedia.org/wiki/Polynomial_long_division>.- <http://demonstrations.wolfram.com/PolynomialLongDivision/>.--}- _ `quotRem'` MkPolynomial [] = error "Factory.Data.Polynomial.quotRem':\tzero denominator."- polynomialN `quotRem'` polynomialD = longDivide polynomialN where--- longDivide :: (Fractional c, Num e, Ord e) => Polynomial c e -> (Polynomial c e, Polynomial c e)- longDivide (MkPolynomial []) = (zero, zero) -- Exactly divides.- longDivide numerator- | Data.Monomial.getExponent quotient < 0 = (zero, numerator) -- Indivisible remainder.- | otherwise = Control.Arrow.first (lift (quotient :)) $ longDivide (numerator =-= polynomialD *= quotient )- where--- quotient :: (Fractional c, Num e) => Data.Monomial.Monomial c e- quotient = getLeadingTerm numerator </> getLeadingTerm polynomialD--{- |- * Transforms the data behind the constructor.-- * CAVEAT: similar to 'Data.Functor.fmap', but 'Polynomial' isn't an instance of 'Data.Functor.Functor' since we may want to operate on both /type-parameters/.-- * CAVEAT: the caller is required to re-'normalise' the resulting polynomial depending on the nature of the transformation of the data.--}-lift :: (MonomialList c1 e1 -> MonomialList c2 e2) -> Polynomial c1 e1 -> Polynomial c2 e2-lift transform = MkPolynomial . transform . getMonomialList---- | Returns the number of non-zero terms in the polynomial.-terms :: Polynomial c e -> Int-terms (MkPolynomial l) = length l---- | Return the highest-degree monomial.-getLeadingTerm :: Polynomial c e -> Data.Monomial.Monomial c e-getLeadingTerm (MkPolynomial []) = error "Factory.Data.Polynomial.getLeadingTerm:\tzero polynomial has no leading term."-getLeadingTerm (MkPolynomial (m : _)) = m---- | Removes terms with a /coefficient/ of zero.-pruneCoefficients :: (Eq c, Num c) => Polynomial c e -> Polynomial c e-pruneCoefficients (MkPolynomial []) = zero-pruneCoefficients p = filter ((/= 0) . Data.Monomial.getCoefficient) `lift` p---- | Sorts into /descending order/ of exponents, groups /like/ exponents, and calls 'pruneCoefficients'.-normalise :: (Eq c, Num c, Ord e) => Polynomial c e -> Polynomial c e-normalise = pruneCoefficients . lift (- map (- foldr ((+) . Data.Monomial.getCoefficient) 0 &&& Data.Monomial.getExponent . head- ) . Data.List.groupBy (=~) . Data.List.sortBy (flip (<=>))- )---- | Constructs an arbitrary /zeroeth-degree polynomial/, ie. independent of the /indeterminate/.-mkConstant :: (Eq c, Num c, Num e) => c -> Polynomial c e-mkConstant 0 = zero-mkConstant c = MkPolynomial [(c, 0)]---- | Constructs an arbitrary /first-degree polynomial/.-mkLinear :: (Eq c, Num c, Num e)- => c -- ^ Gradient.- -> c -- ^ Constant.- -> Polynomial c e-mkLinear m c = pruneCoefficients $ MkPolynomial [(m, 1), (c, 0)]---- | Smart constructor. Constructs an arbitrary /polynomial/.-mkPolynomial :: (Eq c, Num c, Ord e) => MonomialList c e -> Polynomial c e-mkPolynomial [] = zero-mkPolynomial l = normalise $ MkPolynomial l---- | Constructs a /polynomial/ with zero terms.-zero :: Polynomial c e-zero = MkPolynomial []---- | Constructs a constant /monomial/, independent of the /indeterminate/.-one :: (Eq c, Num c, Num e) => Polynomial c e-one = mkConstant 1---- | True if all /exponents/ are in the order defined by the specified comparator.-inOrder :: (e -> e -> Bool) -> Polynomial c e -> Bool-inOrder comparator p- | any ($ p) [isZero, isMonomial] = True- | otherwise = and . uncurry (zipWith comparator) . (init &&& tail) . map Data.Monomial.getExponent $ getMonomialList p---- | True if the /exponents/ of successive terms are in /ascending/ order.-inAscendingOrder :: Ord e => Polynomial c e -> Bool-inAscendingOrder = inOrder (<=)---- | True if the /exponents/ of successive terms are in /descending/ order.-inDescendingOrder :: Ord e => Polynomial c e -> Bool-inDescendingOrder = inOrder (>=)---- | True if no term has a /coefficient/ of zero.-isReduced :: (Eq c, Num c) => Polynomial c e -> Bool-isReduced = all ((/= 0) . Data.Monomial.getCoefficient) . getMonomialList---- | True if no term has a /coefficient/ of zero and the /exponents/ of successive terms are in /descending/ order.-isNormalised :: (Eq c, Num c, Ord e) => Polynomial c e -> Bool-isNormalised polynomial = all ($ polynomial) [isReduced, inDescendingOrder]--{- |- * 'True' if the /leading coefficient/ is one.-- * <http://en.wikipedia.org/wiki/Monic_polynomial#Classifications>.--}-isMonic :: (Eq c, Num c) => Polynomial c e -> Bool-isMonic (MkPolynomial []) = False -- All coefficients are zero, and have therefore been removed.-isMonic p = (== 1) . Data.Monomial.getCoefficient $ getLeadingTerm p---- | True if there are zero terms.-isZero :: Polynomial c e -> Bool-isZero (MkPolynomial []) = True-isZero _ = False---- | True if there's exactly one term.-isMonomial :: Polynomial c e -> Bool-isMonomial (MkPolynomial []) = True-isMonomial _ = False---- | True if all /exponents/ are /positive/ integers as required.-isPolynomial :: Integral e => Polynomial c e -> Bool-isPolynomial = all Data.Monomial.isMonomial . getMonomialList--{- |- * 'True' if the two specified /polynomials/ are /congruent/ in /modulo/-arithmetic.-- * <http://planetmath.org/encyclopedia/PolynomialCongruence.html>.--}-areCongruentModulo :: (Integral c, Num e, Ord e)- => Polynomial c e -- ^ LHS.- -> Polynomial c e -- ^ RHS.- -> c -- ^ Modulus.- -> Bool-areCongruentModulo _ _ 0 = error "Factory.Data.Polynomial.areCongruentModulo:\tzero modulus."-areCongruentModulo _ _ 1 = True-areCongruentModulo l r modulus- | l == r = True- | otherwise = all ((== 0) . (`mod` modulus) . Data.Monomial.getCoefficient) . getMonomialList $ l =-= r--{- |- * Return the /degree/ (AKA /order/) of the /polynomial/.-- * <http://en.wikipedia.org/wiki/Degree_of_a_polynomial>.-- * <http://mathworld.wolfram.com/PolynomialDegree.html>.--}-getDegree :: Num e => Polynomial c e -> e-getDegree (MkPolynomial []) = -1 -- CAVEAT: debatable, but makes some operations more robust and consistent.-getDegree p = Data.Monomial.getExponent $ getLeadingTerm p--{- |- * Scale-up the specified /polynomial/ by a constant /monomial/ factor.-- * <http://en.wikipedia.org/wiki/Scalar_multiplication>.--}-(*=) :: (Eq c, Num c, Num e) => Polynomial c e -> Data.Monomial.Monomial c e -> Polynomial c e-polynomial *= monomial- | Data.Monomial.getCoefficient monomial == 1 = map (`Data.Monomial.shiftExponent` Data.Monomial.getExponent monomial) `lift` polynomial- | otherwise = map (monomial <*>) `lift` polynomial--{- |- * Raise a /polynomial/ to the specified positive integral power, but using /modulo/-arithmetic.-- * Whilst one could naively implement this as @(x Data.Ring.=^ n) `mod` m@, this will result in arithmetic operatons on unnecessarily big integers.--}-raiseModulo :: (Integral c, Integral power, Num e, Ord e, Show power)- => Polynomial c e -- ^ The base.- -> power -- ^ The exponent to which the base should be raised.- -> c -- ^ The modulus.- -> Polynomial c e -- ^ The result.-raiseModulo _ _ 0 = error "Factory.Data.Polynomial.raiseModulo:\tzero modulus."-raiseModulo _ _ 1 = zero-raiseModulo _ 0 modulus = mkConstant $ 1 `mod` modulus-raiseModulo polynomial power modulus- | power < 0 = error $ "Factory.Data.Polynomial.raiseModulo:\tthe result isn't guaranteed to be a polynomial, for power=" ++ show power- | first `elem` [zero, one] = first -- Eg 'raiseModulo (mkPolynomial [(3,1)]) 100 3' or 'raiseModulo (mkPolynomial [(3,1),(1,0)]) 100 3'.- | otherwise = slave power- where--- first :: Integral c => Polynomial c e- first = polynomial `mod'` modulus---- slave :: (Integral c, Integral power, Num e, Ord e) => power -> Polynomial c e- slave 1 = first- slave n = (`mod'` modulus) . (if r == 0 {-even-} then id else (polynomial =*=)) . Data.Ring.square $ slave q {-recurse-} where- (q, r) = n `quotRem` 2---- | Reduces all the coefficients using /modular/ arithmetic.-mod' :: Integral c- => Polynomial c e- -> c -- ^ Modulus.- -> Polynomial c e-mod' p modulus = pruneCoefficients $ map (`Data.Monomial.mod'` modulus) `lift` p--{- |- * Evaluate the /polynomial/ at a specific /indeterminate/.-- * CAVEAT: requires positive exponents; but it wouldn't really be a /polynomial/ otherwise.-- * If the /polynomial/ is very sparse, this may be inefficient,- since it /memoizes/ the complete sequence of powers up to the polynomial's /degree/.--}-evaluate :: (Num n, Integral e, Show e)- => n -- ^ The /indeterminate/.- -> Polynomial n e- -> n -- ^ The Result.-evaluate x = foldr ((+) . raise) 0 . getMonomialList where- powers = iterate (* x) 1-- raise monomial- | exponent' < 0 = error $ "Factory.Data.Polynomial.evaluate.raise:\tnegative exponent; " ++ show exponent'- | otherwise = Data.Monomial.getCoefficient monomial * Data.List.genericIndex powers exponent'- where- exponent' = Data.Monomial.getExponent monomial---- | Convert the type of the /coefficient/s.-realCoefficientsToFrac :: (Real r, Fractional f) => Polynomial r e -> Polynomial f e-realCoefficientsToFrac = lift (Data.Monomial.realCoefficientToFrac `map`)-
− src/Factory/Data/PrimeFactors.hs
@@ -1,143 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a list of /prime factors/.-- * The product of this list of prime-factors represents the /composite/ integer from which they were originally extracted.--}--module Factory.Data.PrimeFactors(--- * Types--- ** Type-synonyms- Factors,--- * Functions- insert',--- invert,- product',- reduce,--- reduceSorted,--- sumExponents,--- ** Operators- (>*<),- (>/<),- (>^)-) where--import qualified Control.Arrow-import Control.Arrow((&&&))-import qualified Data.List-import qualified Data.Ord-import qualified Factory.Math.DivideAndConquer as Math.DivideAndConquer-import qualified Factory.Data.Exponential as Data.Exponential-import Factory.Data.Exponential((<^), (=~))-import qualified ToolShed.Data.List--infixl 7 >/<, >*< -- Same as (/).-infixr 8 >^ -- Same as (^).--{- |- * Each element of this list represents one /prime-factor/, expressed as an /exponential/ with a /prime/ base, of the original integer.-- * Whilst it only makes sense for both the /base/ and /exponent/ to be integral, these constrains are applied at the function-level as required.--}-type Factors base exponent = [Data.Exponential.Exponential base exponent]--{- |- * Sorts a list representing a product of /prime factors/ by increasing /base/.-- * Multiplies 'Data.Exponential.Exponential's of similar /base/.--}-reduce :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent-reduce = reduceSorted . Data.List.sort {-primarily by base-}---- | Multiplies 'Data.Exponential.Exponential's of similar /base/.-reduceSorted :: (Eq base, Num exponent) => Factors base exponent -> Factors base exponent--- reduceSorted = map (Data.Exponential.getBase . head &&& sumExponents) . Data.List.groupBy (=~) -- Slow-reduceSorted [] = []-reduceSorted (x : xs)- | null matched = x : reduceSorted remainder- | otherwise = Control.Arrow.second (+ sumExponents matched) x : reduceSorted remainder- where- (matched, remainder) = span (=~ x) xs--{- |- * Insert a 'Data.Exponential.Exponential', into a list representing a product of /prime factors/, multiplying with any incumbent of like /base/.-- * The list should be sorted by increasing /base/.-- * Preserves the sort-order.-- * CAVEAT: this is tolerably efficient for sporadic insertion; to insert a list, use '>*<'.--}-insert' :: (Ord base, Num exponent) => Data.Exponential.Exponential base exponent -> Factors base exponent -> Factors base exponent-insert' e [] = [e]-insert' e l@(x : xs) = case Data.Ord.comparing Data.Exponential.getBase e x of- LT -> e : l- GT -> x : insert' e xs -- Recurse.- _ -> Control.Arrow.second (+ Data.Exponential.getExponent e) x : xs -- Multiply by adding exponents.--{- |- * Multiplies two lists each representing a product of /prime factors/, and sorted by increasing /base/.-- * Preserves the sort-order.--}-(>*<) :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent -> Factors base exponent-l >*< r = reduceSorted $ ToolShed.Data.List.merge l r---- | Invert the product of a list /prime factors/, by negating each of the /exponents/.-invert :: Num exponent => Factors base exponent -> Factors base exponent-invert = map Data.Exponential.invert--{- |- * Divides two lists, each representing a product of /prime factors/, and sorted by increasing /base/.-- * Preserves the sort-order.--}-(>/<) :: (Integral base, Integral exponent)- => Factors base exponent -- ^ The list of /prime factors/ in the /numerator/.- -> Factors base exponent -- ^ The list of /prime factors/ in the /denominator/.- -> (Factors base exponent, Factors base exponent) -- ^ The ratio of /numerator/ and /denominator/, after like /prime factors/ are cancelled.-numerator >/< denominator = filter (- (> 0) . Data.Exponential.getExponent- ) &&& invert . filter (- (< 0) . Data.Exponential.getExponent- ) $ numerator >*< invert denominator--{- |- * Raise the product of a list /prime factors/ to the specified power.-- * CAVEAT: this merely involves raising each element to the specified power; cf. raising a /polynomial/ to a power.--}-(>^) :: Num exponent => Factors base exponent -> exponent -> Factors base exponent-factors >^ power = map (<^ power) factors---- | Sum the /exponents/ of the specified list; as required to multiply exponentials with identical /base/.-sumExponents :: Num exponent => Factors base exponent -> exponent-sumExponents = foldr ((+) . Data.Exponential.getExponent) 0---- | Multiply a list of /prime factors/.-product' :: (Num base, Integral exponent)- => Math.DivideAndConquer.BisectionRatio- -> Math.DivideAndConquer.MinLength- -> Factors base exponent -- ^ The list on which to operate.- -> base -- ^ The result.-product' bisectionRatio minLength = Math.DivideAndConquer.product' bisectionRatio minLength . map Data.Exponential.evaluate-
− src/Factory/Data/PrimeWheel.hs
@@ -1,198 +0,0 @@-{-- Copyright (C) 2011 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 a /prime-wheel/, for use in prime-number generation; <http://en.wikipedia.org/wiki/Wheel_factorization>.--}--module Factory.Data.PrimeWheel(--- * Types--- ** Type-synonyms- Distance,- NPrimes,- PrimeMultiples,--- Repository,--- ** Data-types- PrimeWheel(getPrimeComponents, getSpokeGaps),--- * Functions- estimateOptimalSize,--- findCoprimes,- generateMultiples,- roll,- rotate,--- ** Constructors- mkPrimeWheel,--- ** Query- getCircumference,- getSpokeCount-) where--import Control.Arrow((&&&), (***))-import qualified Data.IntMap-import qualified Data.List--{- |- * A conceptual /wheel/, with irregularly spaced spokes; <http://www.haskell.org/haskellwiki/Prime_numbers_miscellaneous#Prime_Wheels>.-- * On being rolled, the trace of the spokes, identifies candidates which are /coprime/ to those primes from which the /wheel/ was composed.-- * One can alternatively view this as a set of vertical nested rings, each with a /prime circumference/, and touching at its lowest point.- Each has a single mark on its /circumference/, which when rolled identifies multiples of that /circumference/.- When the complete set is rolled, from the state where all marks are coincident, all multiples of the set of primes, are traced.-- * CAVEAT: The distance required to return to this state (the wheel's /circumference/), grows rapidly with the number of primes:--> zip [0 ..] . scanl (*) 1 $ [2,3,5,7,11,13,17,19,23,29,31]-> [(0,1),(1,2),(2,6),(3,30),(4,210),(5,2310),(6,30030),(7,510510),(8,9699690),(9,223092870),(10,6469693230),(11,200560490130)]-- * The number of spokes also grows rapidly with the number of primes:--> zip [0 ..] . scanl (*) 1 . map pred $ [2,3,5,7,11,13,17,19,23,29,31]-> [(0,1),(1,1),(2,2),(3,8),(4,48),(5,480),(6,5760),(7,92160),(8,1658880),(9,36495360),(10,1021870080),(11,30656102400)]--}-data PrimeWheel i = MkPrimeWheel {- getPrimeComponents :: [i], -- ^ Accessor: the ordered sequence of initial primes, from which the /wheel/ was composed.- getSpokeGaps :: [i] -- ^ Accessor: the sequence of spoke-gaps, the sum of which equals its /circumference/.-} deriving Show---- | The /circumference/ of the specified 'PrimeWheel'.-getCircumference :: Integral i => PrimeWheel i -> i-getCircumference = product . getPrimeComponents---- | The number of spokes in the specified 'PrimeWheel'.-getSpokeCount :: Integral i => PrimeWheel i -> i-getSpokeCount = foldr ((*) . pred) 1 . getPrimeComponents---- | An infinite increasing sequence, of the multiples of a specific prime.-type PrimeMultiples i = [i]---- | Defines a container for the 'PrimeMultiples'.-type Repository = Data.IntMap.IntMap (PrimeMultiples Int)---- | The size of the /wheel/, measured by the number of primes from which it is composed.-type NPrimes = Int--{- |- * Uses a /Sieve of Eratosthenes/ (<http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>), to generate an initial sequence of primes.-- * Also generates an infinite sequence of candidate primes, each of which is /coprime/ to the primes just found, e.g.:- @filter ((== 1) . (gcd (2 * 3 * 5 * 7))) [11 ..] = [11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121 ..]@; NB /121/ isn't prime.-- * CAVEAT: the use, for efficiency, of "Data.IntMap", limits the maximum bound of this sequence, though not to a significant extent.--}-findCoprimes :: NPrimes -> ([Int], [Int])-findCoprimes 0 = ([], [])-findCoprimes required- | required < 0 = error $ "Factory.Data.PrimeWheel.findCoprimes: invalid number of coprimes; " ++ show required- | otherwise = splitAt required $ 2 : sieve 3 0 Data.IntMap.empty- where- sieve :: Int -> NPrimes -> Repository -> [Int]- sieve candidate found repository = case Data.IntMap.lookup candidate repository of- Just primeMultiples -> sieve' found . insertUniq primeMultiples $ Data.IntMap.delete candidate repository -- Re-insert subsequent multiples.- Nothing {-prime-} -> let- found' = succ found- (key : values) = iterate (+ gap * candidate) $ candidate ^ (2 :: Int) -- Generate a sequence of prime-multiples, starting from its square.- in candidate : sieve' found' (- if found' >= required- then repository- else Data.IntMap.insert key values repository- )- where- gap :: Int- gap = 2 -- For efficiency, only sieve odd integers.-- sieve' :: NPrimes -> Repository -> [Int]- sieve' = sieve $ candidate + gap -- Tail-recurse.-- insertUniq :: PrimeMultiples Int -> Repository -> Repository- insertUniq l m = insert $ dropWhile (`Data.IntMap.member` m) l where- insert :: PrimeMultiples Int -> Repository- insert [] = error "Factory.Data.PrimeWheel.findCoprimes.sieve.insertUniq.insert:\tnull list"- insert (key : values) = Data.IntMap.insert key values m-{- |- * The optimal number of low primes from which to build the /wheel/, grows with the number of primes required;- the /circumference/ should be approximately the /square-root/ of the number of integers it will be required to sieve.-- * CAVEAT: one greater than this is returned, which empirically seems better.--}-estimateOptimalSize :: Integral i => i -> NPrimes-estimateOptimalSize maxPrime = succ . length . takeWhile (<= optimalCircumference) . scanl1 (*) {-circumference-} . map fromIntegral {-prevent overflow-} . fst {-primes-} $ findCoprimes 10 {-arbitrary maximum bound-} where- optimalCircumference :: Integer- optimalCircumference = round (sqrt $ fromIntegral maxPrime :: Double)--{- |- Smart constructor for a /wheel/ from the specified number of low primes.-- * The optimal number of low primes from which to build the /wheel/, grows with the number of primes required;- the /circumference/ should be approximately the /square-root/ of the number of integers it will be required to sieve.-- * The sequence of gaps between spokes on the /wheel/ is /symmetrical under reflection/;- though two values lie /on/ the axis, that aren't part of this symmetry. Eg:--> nPrimes Gaps-> ====== ====-> 0 [1]-> 1 [2] -- The terminal gap for all subsequent wheels is '2'; [(succ circumference `mod` circumference) - (pred circumference `mod` circumference)].-> 2 [4,2] -- Both points are on the axis, so the symmetry isn't yet clear.-> 3 [6,4,2,4,2,4,6,2]-> 4 [10,2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2]-- Exploitation of this property has proved counter-productive, probably because it requires /strict evaluation/,- exposing the user to the full cost of inadvertently choosing a /wheel/, which in practice, is rotated less than once.--}-mkPrimeWheel :: Integral i => NPrimes -> PrimeWheel i-mkPrimeWheel 0 = MkPrimeWheel [] [1]-mkPrimeWheel nPrimes- | nPrimes < 0 = error $ "Factory.Data.PrimeWheel.mkPrimeWheel: unable to construct from " ++ show nPrimes ++ " primes"- | otherwise = primeWheel- where- (primeComponents, coprimeCandidates) = (map fromIntegral *** map fromIntegral . Data.List.genericTake (getSpokeCount primeWheel)) $ findCoprimes nPrimes- primeWheel = MkPrimeWheel primeComponents $ zipWith (-) coprimeCandidates $ 1 : coprimeCandidates -- Measure the gaps between candidate primes.---- | Couples a candidate prime with a /rolling wheel/, to define the distance rolled.-type Distance i = (i, [i])---- | Generates a new candidate prime, from a /rolling wheel/, and the current candidate.-rotate :: Integral i => Distance i -> Distance i-rotate (candidate, rollingWheel) = (candidate +) . head &&& tail $ rollingWheel--{-# INLINE rotate #-}---- | Generate an infinite, increasing sequence of candidate primes, from the specified /wheel/.-roll :: Integral i => PrimeWheel i -> [Distance i]-roll primeWheel = tail $ iterate rotate (1, cycle $ getSpokeGaps primeWheel)--{- |- * Generates multiples of the specified prime, starting from its /square/,- skipping those multiples of the low primes from which the specified 'PrimeWheel' was composed,- and which therefore, the /wheel/ won't generate as candidates. Eg:--> Prime Rotating PrimeWheel 3 Output-> ===== ===================== ======-> 7 [4,2,4,2,4,6,2,6] [49,77,91,119,133,161,203,217,259 ..]-> 11 [2,4,2,4,6,2,6,4] [121,143,187,209,253,319,341,407 ..]-> 13 [4,2,4,6,2,6,4,2] [169,221,247,299,377,403,481,533,559 ..]--}-generateMultiples :: Integral i- => i -- ^ The number to square and multiply- -> [i] -- ^ A /rolling wheel/, the track of which, delimits the gaps between /coprime/ candidates.- -> [i]-generateMultiples i = scanl (\accumulator -> (+ accumulator) . (* i)) (i ^ (2 :: Int))--{-# INLINE generateMultiples #-}-
− src/Factory/Data/QuotientRing.hs
@@ -1,79 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a /Quotient Ring/; <http://en.wikipedia.org/wiki/Quotient_ring>.-- * This is a /ring/ composed from a residue-class resulting from /modular/ division.--}--module Factory.Data.QuotientRing(--- * Type-classes- QuotientRing(..),--- * Functions- quot',- rem',--- ** Predicates- areCongruentModulo,- isDivisibleBy-) where--import Factory.Data.Ring((=-=))-import qualified Factory.Data.Ring as Data.Ring---- | Defines a sub-class of 'Data.Ring.Ring', in which division is implemented.-class Data.Ring.Ring q => QuotientRing q where- quotRem' :: q -> q -> (q, q) -- ^ Divides the first operand by the second, to yield a pair composed from the /quotient/ and the /remainder/.---- | Returns the /quotient/, after division of the two specified 'QuotientRing's.-quot' :: QuotientRing q- => q -- ^ Numerator.- -> q -- ^ Denominator.- -> q-quot' numerator = fst . quotRem' numerator---- | Returns the /remainder/, after division of the two specified 'QuotientRing's.-rem' :: QuotientRing q- => q -- ^ Numerator.- -> q -- ^ Denominator.- -> q-rem' numerator = snd . quotRem' numerator--{- |- * 'True' if the two specified 'QuotientRing's are /congruent/ in /modulo/-arithmetic, where the /modulus/ is a third 'QuotientRing'.-- * <http://www.usna.edu/Users/math/wdj/book/node74.html>.--}-areCongruentModulo :: (Eq q, QuotientRing q)- => q -- ^ LHS.- -> q -- ^ RHS.- -> q -- ^ Modulus.- -> Bool-areCongruentModulo l r modulus- | l == r = True -- Only required for efficiency.- | otherwise = (l =-= r) `isDivisibleBy` modulus---- | True if the second operand /divides/ the first.-isDivisibleBy :: (Eq q, QuotientRing q)- => q -- ^ Numerator.- -> q -- ^ Denominator.- -> Bool-numerator `isDivisibleBy` denominator = rem' numerator denominator == Data.Ring.additiveIdentity-
− src/Factory/Data/Ring.hs
@@ -1,118 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Describes a /ring/ and operations on its members.-- * <http://en.wikipedia.org/wiki/Ring_%28mathematics%29>.-- * <http://www.numericana.com/answer/rings.htm>.--}--module Factory.Data.Ring(--- * Type-classes- Ring(..),--- * Types--- ** Data.types--- Product,--- Sum,--- * Functions- product',- sum',--- ** Operators- (=^)-) where--import qualified Data.Monoid-import qualified Factory.Math.DivideAndConquer as Math.DivideAndConquer--infixl 6 =+= -- Same as (+).-infixl 6 =-= -- Same as (-).-infixl 7 =*= -- Same as (*).-infixr 8 =^ -- Same as (^).--{- |- * Define both the operations applicable to all members of the /ring/, and its mandatory members.-- * Minimal definition; '=+=', '=*=', 'additiveInverse', 'multiplicativeIdentity', 'additiveIdentity'.--}-class Ring r where- (=+=) :: r -> r -> r -- ^ Addition of two members; required to be /commutative/; <http://en.wikipedia.org/wiki/Commutativity>.- (=*=) :: r -> r -> r -- ^ Multiplication of two members.- additiveInverse :: r -> r -- ^ The operand required to yield /zero/ under addition; <http://en.wikipedia.org/wiki/Additive_inverse>.- multiplicativeIdentity :: r -- ^ The /identity/-member under multiplication; <http://mathworld.wolfram.com/MultiplicativeIdentity.html>.- additiveIdentity :: r -- ^ The /identity/-member under addition (AKA /zero/); <http://en.wikipedia.org/wiki/Additive_identity>.-- (=-=) :: r -> r -> r -- ^ Subtract the two specified /ring/-members.- l =-= r = l =+= additiveInverse r -- Default implementation.-- square :: r -> r -- ^ Square the ring.- square r = r =*= r -- Default implementation; there may be a more efficient one.--{- |- * Raise a /ring/-member to the specified positive integral power.-- * Exponentiation is implemented as a sequence of either squares of, or multiplications by, the /ring/-member;- <http://en.wikipedia.org/wiki/Exponentiation_by_squaring>.--}-(=^) :: (- Eq r,- Integral power,- Ring r,- Show power- ) => r -> power -> r-_ =^ 0 = multiplicativeIdentity-ring =^ power- | power < 0 = error $ "Factory.Data.Ring.(=^):\tthe result isn't guaranteed to be a ring-member, for power=" ++ show power- | ring `elem` [additiveIdentity, multiplicativeIdentity] = ring- | otherwise = slave power- where- slave 1 = ring- slave n = (if r == 0 {-even-} then id else (=*= ring)) . square $ slave q where- (q, r) = n `quotRem` 2---- | Does for 'Ring', what 'Data.Monoid.Product' does for type 'Num', in that it makes it an instance of 'Data.Monoid.Monoid' under multiplication.-newtype Product p = MkProduct {- getProduct :: p -- ^ Access the polymorphic payload.-} deriving (Read, Show)--instance Ring r => Data.Monoid.Monoid (Product r) where- mempty = MkProduct multiplicativeIdentity- MkProduct x `mappend` MkProduct y = MkProduct $ x =*= y---- | Returns the /product/ of the list of /ring/-members.-product' :: Ring r => Math.DivideAndConquer.BisectionRatio -> Math.DivideAndConquer.MinLength -> [r] -> r--- product' _ _ = getProduct . Data.Monoid.mconcat . map MkProduct-product' ratio minLength = getProduct . Math.DivideAndConquer.divideAndConquer ratio minLength . map MkProduct---- | Does for 'Ring', what 'Data.Monoid.Sum' does for type 'Num', in that it makes it an instance of 'Data.Monoid.Monoid' under addition.-newtype Sum s = MkSum {- getSum :: s -- ^ Access the polymorphic payload.-} deriving (Read, Show)--instance Ring r => Data.Monoid.Monoid (Sum r) where- mempty = MkSum additiveIdentity- MkSum x `mappend` MkSum y = MkSum $ x =+= y---- | Returns the /sum/ of the list of /ring/-members.-sum' :: Ring r => Math.DivideAndConquer.BisectionRatio -> Math.DivideAndConquer.MinLength -> [r] -> r--- sum' _ _ = getSum . Data.Monoid.mconcat . map MkSum-sum' ratio minLength = getSum . Math.DivideAndConquer.divideAndConquer ratio minLength . map MkSum-
− src/Factory/Math/ArithmeticGeometricMean.hs
@@ -1,91 +0,0 @@-{-- Copyright (C) 2011 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@] Determines the /Arithmetic-geometric mean/; <http://en.wikipedia.org/wiki/Arithmetic-geometric_mean>.--}--module Factory.Math.ArithmeticGeometricMean(--- * Types--- ** Type-synonyms- ArithmeticMean,- GeometricMean,- AGM,--- * Functions- convergeToAGM,- spread,--- ** Accessors- getArithmeticMean,- getGeometricMean,--- ** Predicates- isValid-) where--import Control.Arrow((&&&))-import qualified Control.Parallel.Strategies-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot---- | The type of the /arithmetic mean/; <http://en.wikipedia.org/wiki/Arithmetic_mean>.-type ArithmeticMean = Rational---- | The type of the /geometric mean/; <http://en.wikipedia.org/wiki/Geometric_mean>.-type GeometricMean = Rational---- | Encapsulates both /arithmetic/ and /geometric/ means.-type AGM = (ArithmeticMean, GeometricMean)---- | Accessor.-{-# INLINE getArithmeticMean #-}-getArithmeticMean :: AGM -> ArithmeticMean-getArithmeticMean = fst---- | Accessor.-{-# INLINE getGeometricMean #-}-getGeometricMean :: AGM -> GeometricMean-getGeometricMean = snd---- | Returns an infinite list which converges on the /Arithmetic-geometric mean/.-convergeToAGM :: Math.SquareRoot.Algorithmic squareRootAlgorithm => squareRootAlgorithm -> Math.Precision.DecimalDigits -> AGM -> [AGM]-convergeToAGM squareRootAlgorithm decimalDigits agm- | decimalDigits <= 0 = error $ "Factory.Math.ArithmeticGeometricMean.convergeToAGM:\tinvalid number of decimal digits; " ++ show decimalDigits- | not $ isValid agm = error $ "Factory.Math.ArithmeticGeometricMean.convergeToAGM:\tboth means must be positive for a real geometric mean; " ++ show agm- | spread agm == 0 = repeat agm- | otherwise = let- simplify :: Rational -> Rational- simplify = Math.Precision.simplify (pred decimalDigits {-ignore single integral digit-}) -- This makes a gigantic difference to performance.-- findArithmeticMean :: AGM -> ArithmeticMean- findArithmeticMean = (/ 2) . uncurry (+)-- findGeometricMean :: AGM -> GeometricMean- findGeometricMean = Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits . uncurry (*)- in iterate (- Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq- ) . (simplify . findArithmeticMean &&& simplify . findGeometricMean)- ) agm---- | Returns the bounds within which the 'AGM' has been constrained.-spread :: AGM -> Rational-spread = uncurry (-)---- | Checks that both /means/ are positive, as required for the /geometric mean/ to be consistently /real/.-isValid :: AGM -> Bool-isValid (a, g) = all (>= 0) [a, g]-
− src/Factory/Math/DivideAndConquer.hs
@@ -1,122 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Provides a polymorphic algorithm, to /unfold/ a list into a tree, to which an /associative binary operator/ is then applied to re-/fold/ the tree to a /scalar/.-- * Implementations of this strategy have been provided for /addition/ and /multiplication/,- though other associative binary operators, like 'gcd' or 'lcm' could also be used.-- * Where the contents of the list are consecutive, a more efficient implementation is available in /Factory.Data.Interval/.--}--module Factory.Math.DivideAndConquer(--- * Types--- ** Type-synonyms- BisectionRatio,- MinLength,--- * Functions- divideAndConquer,- product',- sum'-) where--import Control.Arrow((***))-import qualified Control.Parallel.Strategies-import qualified Data.Monoid-import qualified Data.Ratio--{- |- * The ratio of the original list-length at which to bisect.-- * CAVEAT: the value can overflow.--}-type BisectionRatio = Data.Ratio.Ratio Int---- | The list-length beneath which to terminate bisection.-type MinLength = Int--{- |- * Reduces a list to a single scalar encapsulated in a 'Data.Monoid.Monoid',- using a /divide-and-conquer/ strategy,- bisecting the list and recursively evaluating each part; <http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm>.-- * By choosing a 'bisectionRatio' other than @(1 % 2)@, the bisection can be made asymmetrical.- The specified ratio represents the length of the left-hand portion, over the original list-length;- eg. @(1 % 3)@ results in the first part, half the length of the second.-- * This process of recursive bisection, is terminated beneath the specified minimum list-length,- after which the /monoid/'s binary operator is directly /folded/ over the list.-- * One can view this as a <http://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>,- in which the list is exploded into a binary tree-structure- (each leaf of which contains a list of up to 'minLength' integers, and each node of which contains an associative binary operator),- and then collapsed to a scalar, by application of the operators.--}-divideAndConquer :: Data.Monoid.Monoid monoid- => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.- -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.- -> [monoid] -- ^ The list on which to operate.- -> monoid -- ^ The resulting scalar.-divideAndConquer bisectionRatio minLength l- | any ($ apportion minLength) [- (< 1), -- The left-hand list may be null.- (> pred minLength) -- The right-hand list may be null.- ] = error $ "Factory.Math.DivideAndConquer.divideAndConquer:\tbisectionRatio='" ++ show bisectionRatio ++ "' is incompatible with minLength=" ++ show minLength ++ "."- | otherwise = slave (length l) l- where- apportion :: Int -> Int- apportion list = (list * Data.Ratio.numerator bisectionRatio) `div` Data.Ratio.denominator bisectionRatio-- slave len list- | len <= minLength = Data.Monoid.mconcat list -- Fold the monoid's binary operator over the list.- | otherwise = uncurry Data.Monoid.mappend . Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rseq Control.Parallel.Strategies.rseq- ) . (slave cut *** slave (len - cut)) $ splitAt cut list where -- Apply the monoid's binary operator to the two operands resulting from bisection.- cut = apportion len--{- |- * Multiplies the specified list of numbers.-- * Since the result can be large, 'divideAndConquer' is used in an attempt to form operands of a similar order of magnitude,- which creates scope for the use of more efficient multiplication-algorithms.--}-product' :: Num n- => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.- -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.- -> [n] -- ^ The numbers whose product is required.- -> n -- ^ The resulting product.-product' bisectionRatio minLength = Data.Monoid.getProduct . divideAndConquer bisectionRatio minLength . map Data.Monoid.Product--{- |- * Sums the specified list of numbers.-- * Since the result can be large, 'divideAndConquer' is used in an attempt to form operands of a similar order of magnitude,- which creates scope for the use of more efficient multiplication-algorithms.- /Multiplication/ is required for the /addition/ of 'Rational' numbers by cross-multiplication;- this function is unlikely to be useful for other numbers.--}-sum' :: Num n- => BisectionRatio -- ^ The ratio of the original list-length at which to bisect.- -> MinLength -- ^ For efficiency, the list will not be bisected, when it's length has been reduced to this value.- -> [n] -- ^ The numbers whose sum is required.- -> n -- ^ The resulting sum.-sum' bisectionRatio minLength = Data.Monoid.getSum . divideAndConquer bisectionRatio minLength . map Data.Monoid.Sum-
− src/Factory/Math/Factorial.hs
@@ -1,37 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Whilst this particular function is the subject of many introductory examples to Haskell,- the simple algorithms appropriate for that forum, leave a large margin for performance-improvement.- This module provides the interface for alternative algorithms.-- * <http://mathworld.wolfram.com/Factorial.html>.--}--module Factory.Math.Factorial(--- * Type-classes- Algorithmic(..)-) where---- | Defines the methods expected of a /factorial/-algorithm.-class Algorithmic algorithm where- factorial :: (Integral i, Show i) => algorithm -> i -> i-
− src/Factory/Math/Fibonacci.hs
@@ -1,42 +0,0 @@-{-- Copyright (C) 2011 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@] <http://en.wikipedia.org/wiki/Fibonacci_number>.--}--module Factory.Math.Fibonacci(--- * Constants- fibonacci,- primeIndexedFibonacci-) where--import qualified Data.Numbers.Primes---- | A constant ordered list of the /Fibonacci/-numbers.-fibonacci :: Integral i => [i]-fibonacci = 0 : scanl (+) 1 fibonacci--{- |- * The subset of 'fibonacci', /indexed/ by a /prime/-number.-- * <http://primes.utm.edu/glossary/page.php?sort=FibonacciPrime>.--}-primeIndexedFibonacci :: Integral i => [i]-primeIndexedFibonacci = map (fibonacci !!) Data.Numbers.Primes.primes-
− src/Factory/Math/Hyperoperation.hs
@@ -1,113 +0,0 @@-{-- Copyright (C) 2011 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@] Provides various /hyperoperations/; <http://en.wikipedia.org/wiki/Hyperoperation>.--}--module Factory.Math.Hyperoperation(--- * Types--- ** Type-synonyms- Base,- HyperExponent,--- * Constants- succession,- addition,- multiplication,- exponentiation,- tetration,- pentation,- hexation,--- * Functions- hyperoperation,- ackermannPeter,- powerTower,--- ** Predicates- areCoincidental-) where--import qualified Data.List--{- |- * Merely to enhance self-documentation.-- * CAVEAT: whilst it may appear that 'Base' could be non-'Integral', the recursive definition for /hyper-exponents/ above 'tetration', prevents this.--}-type Base = Integer--{- |- * Merely to enhance self-documentation.-- * CAVEAT: whilst 'Base' and 'HyperExponent' can be independent types for both 'exponentiation' and 'tetration', they interact for other /hyper-exponents/.--}-type HyperExponent = Base--succession, addition, multiplication, exponentiation, tetration, pentation, hexation :: Int -- Arbitrarily.-(succession : addition : multiplication : exponentiation : tetration : pentation : hexation : _) = [0 ..]--{- |- * Returns the /power-tower/ of the specified /base/; <http://mathworld.wolfram.com/PowerTower.html>.-- * A synonym for /tetration/;- <http://en.wikipedia.org/wiki/Tetration>,- <http://www.tetration.org/Fractals/Atlas/index.html>.--}-powerTower :: (Integral base, Integral hyperExponent, Show base) => base -> hyperExponent -> base-powerTower 0 hyperExponent- | even hyperExponent = 1- | otherwise = 0-powerTower _ (-1) = 0 -- The only negative hyper-exponent for which there's a consistent result.-powerTower base hyperExponent- | base < 0 && hyperExponent > 1 = error $ "Factory.Math.Hyperoperation.powerTower:\tundefined for negative base; " ++ show base- | otherwise = Data.List.genericIndex (iterate (base ^) 1) hyperExponent---- | The /hyperoperation/-sequence; <http://en.wikipedia.org/wiki/Hyperoperation>.-hyperoperation :: (Integral rank, Show rank) => rank -> Base -> HyperExponent -> Base-hyperoperation rank base hyperExponent- | rank < fromIntegral succession = error $ "Factory.Math.Hyperoperation.hyperoperation:\tundefined for rank; " ++ show rank- | hyperExponent < 0 = error $ "Factory.Math.Hyperoperation.hyperoperation:\tundefined for hyper-exponent; " ++ show hyperExponent- | otherwise = rank ^# hyperExponent- where- (^#) :: Integral rank => rank -> HyperExponent -> Base- r ^# 0 = case r of- 1 {-addition-} -> base- 2 {-multiplication-} -> 0- _ -> 1- r ^# e = case r of- 0 {-succession-} -> succ {-fromIntegral-} e- 1 {-addition-} -> base + {-fromIntegral-} e- 2 {-multiplication-} -> base * {-fromIntegral-} e- 3 {-exponentiation-} -> base ^ e- 4 {-tetration-} -> base `powerTower` e- _- | e' == e -> tetration ^# e' -- To which it would otherwise be reduced by laborious recursion.- | otherwise -> pred r ^# e'- where- e' = {-fromIntegral $-} r ^# pred e---- | The /Ackermann-Peter/-function; <http://en.wikipedia.org/wiki/Ackermann_function#Ackermann_numbers>.-ackermannPeter :: (Integral rank, Show rank) => rank -> HyperExponent -> Base-ackermannPeter rank = (+ negate 3) . hyperoperation rank 2 {-base-} . (+ 3)---- | True if @hyperoperation base hyperExponent@ has the same value for each specified 'rank'.-areCoincidental :: (Integral rank, Show rank) => Base -> HyperExponent -> [rank] -> Bool-areCoincidental _ _ [] = True-areCoincidental _ _ [_] = True-areCoincidental base hyperExponent ranks = all (== h) hs where- (h : hs) = map (\rank -> hyperoperation rank base hyperExponent) ranks-
− src/Factory/Math/Implementations/Factorial.hs
@@ -1,138 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Provides implementations of the class 'Math.Factorial.Algorithmic'.-- * Provides additional functions related to /factorials/, but which depends on a specific implementation,- and which therefore can't be accessed throught the class-interface.-- * <http://en.wikipedia.org/wiki/Factorial>.-- * <http://mathworld.wolfram.com/Factorial.html>.-- * <http://www.luschny.de/math/factorial/FastFactorialFunctions.htm>.--}--module Factory.Math.Implementations.Factorial(--- * Types--- ** Data-types- Algorithm(..),--- * Functions- primeFactors,--- primeMultiplicity,- risingFactorial,- fallingFactorial,--- ** Operators- (!/!)-) where--import qualified Data.Numbers.Primes-import qualified Factory.Data.Interval as Data.Interval-import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Factorial as Math.Factorial-import qualified ToolShed.Defaultable--infixl 7 !/! -- Same as (/).---- | The algorithms by which /factorial/ has been implemented.-data Algorithm =- Bisection -- ^ The integers from which the /factorial/ is composed, are multiplied using @Data.Interval.product'@.- | PrimeFactorisation -- ^ The /prime factors/ of the /factorial/ are extracted, then raised to the appropriate power, before multiplication.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = Bisection--instance Math.Factorial.Algorithmic Algorithm where- factorial algorithm n- | n < 2 = 1- | otherwise = case algorithm of- Bisection -> risingFactorial 2 $ pred n- PrimeFactorisation -> Data.PrimeFactors.product' (recip 5) {-empirical-} 10 {-empirical-} $ primeFactors n--{- |- * Returns the /prime factors/, of the /factorial/ of the specifed integer.-- * Precisely all the primes less than or equal to the specified integer /n/, are included in /n!/;- only the multiplicity of each of these known prime components need be determined.-- * <http://en.wikipedia.org/wiki/Factorial#Number_theory>.-- * CAVEAT: currently a hotspot.--}-primeFactors :: Integral base- => base -- ^ The number, whose /factorial/ is to be factorised.- -> Data.PrimeFactors.Factors base base -- ^ The /base/ and /exponent/ of each /prime factor/ in the /factorial/, ordered by increasing /base/ (and decreasing /exponent/).-primeFactors n = takeWhile ((> 0) . snd) $ map (\prime -> (prime, primeMultiplicity prime n)) Data.Numbers.Primes.primes--{- |- * The number of times a specific /prime/, can be factored from the /factorial/ of the specified integer.-- * General purpose /prime-factorisation/ has /exponential time-complexity/,- so use /Legendre's Theorem/, which relates only to the /prime factors/ of /factorials/.-- * <http://www.proofwiki.org/wiki/Multiplicity_of_Prime_Factor_in_Factorial>.--}-primeMultiplicity :: Integral i- => i -- ^ A prime number.- -> i -- ^ The integer, the factorial of which the prime is a factor.- -> i -- ^ The number of times the prime occurs in the factorial.-primeMultiplicity prime = sum . takeWhile (> 0) . tail . iterate (`div` prime)---- | Returns the /rising factorial/; <http://mathworld.wolfram.com/RisingFactorial.html>-risingFactorial :: (Integral i, Show i)- => i -- ^ The lower bound of the integer-range, whose product is returned.- -> i -- ^ The number of integers in the range above.- -> i -- ^ The result.-risingFactorial _ 0 = 1-risingFactorial 0 _ = 0-risingFactorial x n = Data.Interval.product' (recip 2) 64 $ Data.Interval.normalise (x, pred $ x + n)---- | Returns the /falling factorial/; <http://mathworld.wolfram.com/FallingFactorial.html>-fallingFactorial :: (Integral i, Show i)- => i -- ^ The upper bound of the integer-range, whose product is returned.- -> i -- ^ The number of integers in the range beneath.- -> i -- ^ The result.-fallingFactorial _ 0 = 1-fallingFactorial 0 _ = 0-fallingFactorial x n = Data.Interval.product' (recip 2) 64 $ Data.Interval.normalise (x, succ $ x - n)--{- |- * Returns the ratio of two factorials.-- * It is more efficient than evaluating both factorials, and then dividing.-- * For more complex combinations of factorials, such as in the /Binomial coefficient/,- extract the /prime factors/ using 'primeFactors'- then manipulate them using the module "Data.PrimeFactors",- and evaluate it using by /Data.PrimeFactors.product'/.--}-(!/!) :: (Integral i, Fractional f, Show i)- => i -- ^ The /numerator/.- -> i -- ^ The /denominator/.- -> f -- ^ The resulting fraction.-numerator !/! denominator- | numerator <= 1 = recip . fromIntegral $ Math.Factorial.factorial (ToolShed.Defaultable.defaultValue :: Algorithm) denominator- | denominator <= 1 = fromIntegral $ Math.Factorial.factorial (ToolShed.Defaultable.defaultValue :: Algorithm) numerator- | numerator == denominator = 1- | numerator < denominator = recip $ denominator !/! numerator -- Recurse.- | otherwise = fromIntegral $ Data.Interval.product' (recip 2) 64 (succ denominator, numerator)-
− src/Factory/Math/Implementations/Pi/AGM/Algorithm.hs
@@ -1,42 +0,0 @@-{-- Copyright (C) 2011 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 the set of /Arithmetic-geometric Mean/-type /Pi/-algorithms which have been implemented; currently just one.--}--module Factory.Math.Implementations.Pi.AGM.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import qualified Factory.Math.Implementations.Pi.AGM.BrentSalamin as Math.Implementations.Pi.AGM.BrentSalamin-import qualified Factory.Math.Pi as Math.Pi-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified ToolShed.Defaultable---- | Defines the available algorithms.-data Algorithm squareRootAlgorithm = BrentSalamin squareRootAlgorithm deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable squareRootAlgorithm => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm) where- defaultValue = BrentSalamin ToolShed.Defaultable.defaultValue--instance Math.SquareRoot.Algorithmic squareRootAlgorithm => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm) where- openR (BrentSalamin squareRootAlgorithm) = Math.Implementations.Pi.AGM.BrentSalamin.openR squareRootAlgorithm-
− src/Factory/Math/Implementations/Pi/AGM/BrentSalamin.hs
@@ -1,64 +0,0 @@-{-- Copyright (C) 2011 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 the /Brent-Salamin/ (AKA /Gauss-Legendre/) algorithm;- <http://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_algorithm>,- <http://mathworld.wolfram.com/Brent-SalaminFormula.html>,- <http://www.pi314.net/eng/salamin.php>.-- * The precision of the result approximately doubles for each iteration.-- [@CAVEAT@] Assumptions on the convergence-rate result in rounding-errors, when only a small number of digits are requested.--}--module Factory.Math.Implementations.Pi.AGM.BrentSalamin(--- * Functions- openR-) where--import Control.Arrow((&&&))-import qualified Factory.Math.ArithmeticGeometricMean as Math.ArithmeticGeometricMean-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot--{- |- * Returns /Pi/, accurate to the specified number of decimal digits.-- * This algorithm is based on the /arithmetic-geometric/ mean of @1@ and @(1 / sqrt 2)@,- but there are many confusingly similar formulations.- The algorithm I've used here, where @a@ is the /arithmetic mean/ and @g@ is the /geometric mean/, is equivalent to other common formulations:--> pi = (a[N-1] + g[N-1])^2 / (1 - sum [2^n * (a[n] - g[n])^2]) where n = [0 .. N-1]-> => 4*a[N]^2 / (1 - sum [2^n * (a[n]^2 - 2*a[n]*g[n] + g[n]^2)])-> => 4*a[N]^2 / (1 - sum [2^n * (a[n]^2 + 2*a[n]*g[n] + g[n]^2 - 4*a[n]*g[n])])-> => 4*a[N]^2 / (1 - sum [2^n * ((a[n] + g[n])^2 - 4*a[n]*g[n])])-> => 4*a[N]^2 / (1 - sum [2^(n-1) * 4 * (a[n-1]^2 - g[n-1]^2)]) where n = [1 .. N]-> => 4*a[N]^2 / (1 - sum [2^(n+1) * (a[n-1]^2 - g[n-1]^2)])---}-openR :: Math.SquareRoot.Algorithmic squareRootAlgorithm => squareRootAlgorithm -> Math.Precision.DecimalDigits -> Rational-openR squareRootAlgorithm decimalDigits = uncurry (/) . (- Math.Power.square . uncurry (+) . last &&& negate . pred . sum . zipWith (*) (iterate (* 2) 1) . map (Math.Power.square . Math.ArithmeticGeometricMean.spread)- ) . take (- Math.Precision.getIterationsRequired Math.Precision.quadraticConvergence 1 decimalDigits- ) $ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits (1, Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (recip 2 :: Rational))-
− src/Factory/Math/Implementations/Pi/BBP/Algorithm.hs
@@ -1,47 +0,0 @@-{-- Copyright (C) 2011 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 the set of /Bailey-Borwein-Plouffe/-type formulae which have been implemented.--}--module Factory.Math.Implementations.Pi.BBP.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import qualified Factory.Math.Implementations.Pi.BBP.Base65536 as Math.Implementations.Pi.BBP.Base65536-import qualified Factory.Math.Implementations.Pi.BBP.Bellard as Math.Implementations.Pi.BBP.Bellard-import qualified Factory.Math.Implementations.Pi.BBP.Implementation as Math.Implementations.Pi.BBP.Implementation-import qualified Factory.Math.Pi as Math.Pi-import qualified ToolShed.Defaultable---- | Defines those /BBP/-type series which have been implemented.-data Algorithm =- Base65536 -- ^ A /base/-@2^16@ version of the formula.- | Bellard -- ^ A /nega-base/ @2^10@ version of the formula.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = Base65536--instance Math.Pi.Algorithmic Algorithm where- openR Base65536 = Math.Implementations.Pi.BBP.Implementation.openR Math.Implementations.Pi.BBP.Base65536.series- openR Bellard = Math.Implementations.Pi.BBP.Implementation.openR Math.Implementations.Pi.BBP.Bellard.series-
− src/Factory/Math/Implementations/Pi/BBP/Base65536.hs
@@ -1,38 +0,0 @@-{-- Copyright (C) 2011 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 a specific base-@2^16@ /BBP/-formula; <http://mathworld.wolfram.com/PiFormulas.html>---}--module Factory.Math.Implementations.Pi.BBP.Base65536(--- * Constants- series-) where--import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series---- | Defines the parameters of this specific series.-series :: Math.Implementations.Pi.BBP.Series.Series-series = Math.Implementations.Pi.BBP.Series.MkSeries {- Math.Implementations.Pi.BBP.Series.numerators = zipWith ($) (cycle [id, id, id, negate]) $ map (2 ^) [15 :: Integer, 14, 14, 12, 11, 10, 10, 8, 7, 6, 6, 4, 3, 2, 2, 0],- Math.Implementations.Pi.BBP.Series.getDenominators = \i -> map (32 * fromIntegral i +) [2, 3, 4, 7, 10, 11, 12, 15, 18, 19, 20, 23, 26, 27, 28, 31],- Math.Implementations.Pi.BBP.Series.seriesScalingFactor = recip $ 2 ^ (13 :: Int),- Math.Implementations.Pi.BBP.Series.base = 2 ^ (16 :: Int)-}
− src/Factory/Math/Implementations/Pi/BBP/Bellard.hs
@@ -1,41 +0,0 @@-{-- Copyright (C) 2011 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 /Bellard/'s nega-base-@2^10@ /BBP/-formula; <http://en.wikipedia.org/wiki/Bellard%27s_formula>--}--module Factory.Math.Implementations.Pi.BBP.Bellard(--- * Constants- series-) where--import Control.Arrow((&&&))-import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series---- | Defines the parameters of this specific series.-series :: Math.Implementations.Pi.BBP.Series.Series-series = Math.Implementations.Pi.BBP.Series.MkSeries {- Math.Implementations.Pi.BBP.Series.numerators = zipWith ($) [negate, negate, id, negate, negate, negate, id] $ map (2 ^) [5 :: Integer, 0, 8, 6, 2, 2, 0],- Math.Implementations.Pi.BBP.Series.getDenominators = \i -> let- f, t :: Integer- (f, t) = (4 *) &&& (10 *) $ fromIntegral i- in [f + 1, f + 3, t + 1, t + 3, t + 5, t + 7, t + 9],- Math.Implementations.Pi.BBP.Series.seriesScalingFactor = recip $ 2 ^ (6 :: Int),- Math.Implementations.Pi.BBP.Series.base = negate $ 2 ^ (10 :: Int)-}
− src/Factory/Math/Implementations/Pi/BBP/Implementation.hs
@@ -1,57 +0,0 @@-{-- Copyright (C) 2011 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 a /Bailey-Borwein-Plouffe/ formula; <http://mathworld.wolfram.com/PiFormulas.html>-- * Surprisingly, because of the huge size of the 'Rational' quantities,- it is a /single/ call to @Factory.Math.Summation.sum'@, rather than the calculation of the many terms in the series, which is the performance-bottleneck.--}--module Factory.Math.Implementations.Pi.BBP.Implementation(--- * Functions- openR-) where--import Data.Ratio((%))-import qualified Factory.Math.Implementations.Pi.BBP.Series as Math.Implementations.Pi.BBP.Series-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.Summation as Math.Summation---- | Returns /Pi/, accurate to the specified number of decimal digits.-openR- :: Math.Implementations.Pi.BBP.Series.Series -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.- -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.- -> Rational-openR Math.Implementations.Pi.BBP.Series.MkSeries {- Math.Implementations.Pi.BBP.Series.numerators = numerators,- Math.Implementations.Pi.BBP.Series.getDenominators = getDenominators,- Math.Implementations.Pi.BBP.Series.seriesScalingFactor = seriesScalingFactor,- Math.Implementations.Pi.BBP.Series.base = base-} decimalDigits = (seriesScalingFactor *) . Math.Summation.sum' 8 . take (- Math.Precision.getTermsRequired (- recip . fromIntegral $ abs {-potentially negative-} base -- The convergence-rate.- ) decimalDigits- ) . zipWith (*) (- iterate (/ fromIntegral base) 1 -- Generate the scaling-ratio, between successive terms.- ) $ map (- sum . zipWith (%) numerators . getDenominators- ) [0 ..]-
− src/Factory/Math/Implementations/Pi/BBP/Series.hs
@@ -1,36 +0,0 @@-{-- Copyright (C) 2011 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 a /Bailey-Borwein-Plouffe/ formula; <http://mathworld.wolfram.com/PiFormulas.html>--}--module Factory.Math.Implementations.Pi.BBP.Series(--- * Types--- ** Data-types- Series(..)-) where---- | Defines a series corresponding to a specific /BBP/-formula.-data Series = MkSeries {- numerators :: [Integer], -- ^ The constant numerators from which each term in the series is composed.- getDenominators :: Int -> [Integer], -- ^ Generates the term-dependent denominators from which each term in the series is composed.- seriesScalingFactor :: Rational, -- ^ The ratio by which the sum to infinity of the series, must be scaled to result in /Pi/.- base :: Integer -- ^ The geometric ratio, by which successive terms are scaled.-}-
− src/Factory/Math/Implementations/Pi/Borwein/Algorithm.hs
@@ -1,56 +0,0 @@-{-- Copyright (C) 2011 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 the set of /Borwein/-type algorithms (currently only one) which have been implemented; <http://www.pi314.net/eng/borwein.php>.--}--module Factory.Math.Implementations.Pi.Borwein.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Pi.Borwein.Borwein1993 as Math.Implementations.Pi.Borwein.Borwein1993-import qualified Factory.Math.Implementations.Pi.Borwein.Implementation as Math.Implementations.Pi.Borwein.Implementation-import qualified Factory.Math.Pi as Math.Pi-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified ToolShed.Defaultable--{- |- * Define those /Borwein/-series which have been implemented.-- * Though currently there's only one, provision has been made for the addition of more.--}-data Algorithm squareRootAlgorithm factorialAlgorithm =- Borwein1993 squareRootAlgorithm factorialAlgorithm -- ^ <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>.- deriving (Eq, Read, Show)--instance (- ToolShed.Defaultable.Defaultable squareRootAlgorithm,- ToolShed.Defaultable.Defaultable factorialAlgorithm- ) => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm) where- defaultValue = Borwein1993 ToolShed.Defaultable.defaultValue ToolShed.Defaultable.defaultValue--instance (- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm) where- openR (Borwein1993 squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Borwein.Implementation.openR Math.Implementations.Pi.Borwein.Borwein1993.series squareRootAlgorithm factorialAlgorithm-
− src/Factory/Math/Implementations/Pi/Borwein/Borwein1993.hs
@@ -1,73 +0,0 @@-{-- Copyright (C) 2011 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 the /Borwein/ series for /Pi/; <http://en.wikipedia.org/wiki/Borwein%27s_algorithm#Jonathan_Borwein_and_Peter_Borwein.27s_Version_.281993.29>--}--module Factory.Math.Implementations.Pi.Borwein.Borwein1993(--- * Constants- series-) where---- import Control.Arrow((***))-import Data.Ratio((%))--- import Factory.Data.PrimeFactors((>*<), (>/<), (>^))--- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Implementations.Pi.Borwein.Series as Math.Implementations.Pi.Borwein.Series-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot---- | Defines the parameters of the /Borwein/ series.-series :: (Math.SquareRoot.Algorithmic squareRootAlgorithm, Math.Factorial.Algorithmic factorialAlgorithm) => Math.Implementations.Pi.Borwein.Series.Series squareRootAlgorithm factorialAlgorithm-series = Math.Implementations.Pi.Borwein.Series.MkSeries {- Math.Implementations.Pi.Borwein.Series.terms = \squareRootAlgorithm factorialAlgorithm decimalDigits -> let- simplify, squareRoot :: Rational -> Rational- simplify = Math.Precision.simplify $ pred decimalDigits {-ignore single integral digit-} -- This makes a gigantic difference to performance.- squareRoot = simplify . Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits-- sqrt5, a, b, c3 :: Rational- sqrt5 = squareRoot 5-- a = 63365028312971999585426220 + sqrt5 * (28337702140800842046825600 + 384 * squareRoot (10891728551171178200467436212395209160385656017 + 4870929086578810225077338534541688721351255040 * sqrt5))- b = 7849910453496627210289749000 + 3510586678260932028965606400 * sqrt5 + 2515968 * squareRoot (3110 * (6260208323789001636993322654444020882161 + 2799650273060444296577206890718825190235 * sqrt5))- c3 = simplify . Math.Power.cube $ negate 214772995063512240 - sqrt5 * (96049403338648032 + 1296 * squareRoot (10985234579463550323713318473 + 4912746253692362754607395912 * sqrt5))- in (- squareRoot $ negate c3, -- The factor into which the series must be divided, to yield Pi.- zipWith (-{-- \n power -> let- product' = Data.PrimeFactors.product' (recip 2) 10- in uncurry (/) . (- (* (a + b * fromIntegral n)) . fromIntegral . product' *** (* power) . fromIntegral . product'- ) $ Math.Implementations.Factorial.primeFactors (6 * n) >/< (- Math.Implementations.Factorial.primeFactors (3 * n) >*< Math.Implementations.Factorial.primeFactors n >^ 3- )--}- \n power -> (- Math.Implementations.Factorial.risingFactorial (succ $ 3 * n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)- ) * (- (a + b * fromIntegral n) / power- )- ) [0 :: Integer ..] $ iterate (* c3) 1- ),- Math.Implementations.Pi.Borwein.Series.convergenceRate = 10 ** negate 50 -- Empirical.-}
− src/Factory/Math/Implementations/Pi/Borwein/Implementation.hs
@@ -1,50 +0,0 @@-{-- Copyright (C) 2011 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 /Borwein/ series for /Pi/; <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>--}--module Factory.Math.Implementations.Pi.Borwein.Implementation(--- * Functions- openR-) where--import qualified Control.Arrow-import qualified Control.Parallel.Strategies-import qualified Factory.Math.Implementations.Pi.Borwein.Series as Math.Implementations.Pi.Borwein.Series-import qualified Factory.Math.Precision as Math.Precision---- | Returns /Pi/, accurate to the specified number of decimal digits.-openR- :: Math.Implementations.Pi.Borwein.Series.Series squareRootAlgorithm factorialAlgorithm -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.- -> squareRootAlgorithm -- ^ The specific /square-root/ algorithm to apply to the above series.- -> factorialAlgorithm -- ^ The specific /factorial/-algorithm to apply to the above series.- -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.- -> Rational-openR Math.Implementations.Pi.Borwein.Series.MkSeries {- Math.Implementations.Pi.Borwein.Series.terms = terms,- Math.Implementations.Pi.Borwein.Series.convergenceRate = convergenceRate-} squareRootAlgorithm factorialAlgorithm decimalDigits = uncurry (/) . Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq- ) . Control.Arrow.second (- sum . take (- Math.Precision.getTermsRequired convergenceRate decimalDigits- )- ) $ terms squareRootAlgorithm factorialAlgorithm decimalDigits-
− src/Factory/Math/Implementations/Pi/Borwein/Series.hs
@@ -1,43 +0,0 @@-{-- Copyright (C) 2011 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 a <http://en.wikipedia.org/wiki/Srinivasa_Borwein>-type series for /Pi/.--}--module Factory.Math.Implementations.Pi.Borwein.Series(--- * Types--- ** Data-types- Series(..)-) where--import qualified Factory.Math.Precision as Math.Precision---- | Defines a series corresponding to a specific /Borwein/-formula.-data Series squareRootAlgorithm factorialAlgorithm = MkSeries {- terms- :: squareRootAlgorithm- -> factorialAlgorithm- -> Math.Precision.DecimalDigits- -> (- Rational, -- The factor into which the sum to infinity of the sequence, must be divided to result in /Pi/- [Rational] -- The sequence of terms, the sum to infinity of which defines the series.- ),- convergenceRate :: Math.Precision.ConvergenceRate -- ^ The expected number of digits of /Pi/, per term in the series.-}-
− src/Factory/Math/Implementations/Pi/Ramanujan/Algorithm.hs
@@ -1,55 +0,0 @@-{-- Copyright (C) 2011 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 the set of /Ramanujan/-type algorithms which have been implemented; <http://en.wikipedia.org/wiki/Pi>.--}--module Factory.Math.Implementations.Pi.Ramanujan.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Pi.Ramanujan.Chudnovsky as Math.Implementations.Pi.Ramanujan.Chudnovsky-import qualified Factory.Math.Implementations.Pi.Ramanujan.Classic as Math.Implementations.Pi.Ramanujan.Classic-import qualified Factory.Math.Implementations.Pi.Ramanujan.Implementation as Math.Implementations.Pi.Ramanujan.Implementation-import qualified Factory.Math.Pi as Math.Pi-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified ToolShed.Defaultable---- | Define those /Ramanujan/-series which have been implemented.-data Algorithm squareRootAlgorithm factorialAlgorithm =- Classic squareRootAlgorithm factorialAlgorithm -- ^ The original version.- | Chudnovsky squareRootAlgorithm factorialAlgorithm -- ^ A variant found by the /Chudnovsky brothers/.- deriving (Eq, Read, Show)--instance (- ToolShed.Defaultable.Defaultable squareRootAlgorithm,- ToolShed.Defaultable.Defaultable factorialAlgorithm- ) => ToolShed.Defaultable.Defaultable (Algorithm squareRootAlgorithm factorialAlgorithm) where- defaultValue = Chudnovsky ToolShed.Defaultable.defaultValue ToolShed.Defaultable.defaultValue--instance (- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Math.Pi.Algorithmic (Algorithm squareRootAlgorithm factorialAlgorithm) where- openR (Classic squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Ramanujan.Implementation.openR Math.Implementations.Pi.Ramanujan.Classic.series squareRootAlgorithm factorialAlgorithm- openR (Chudnovsky squareRootAlgorithm factorialAlgorithm) = Math.Implementations.Pi.Ramanujan.Implementation.openR Math.Implementations.Pi.Ramanujan.Chudnovsky.series squareRootAlgorithm factorialAlgorithm-
− src/Factory/Math/Implementations/Pi/Ramanujan/Chudnovsky.hs
@@ -1,63 +0,0 @@-{-- Copyright (C) 2011 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 the /Chudnovsky/ series for /Pi/; <http://en.wikipedia.org/wiki/Pi>.--}--module Factory.Math.Implementations.Pi.Ramanujan.Chudnovsky(--- * Constants- series-) where---- import Control.Arrow((***))-import Data.Ratio((%))--- import Factory.Data.PrimeFactors((>/<), (>*<), (>^))--- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.SquareRoot as Math.SquareRoot---- | Defines the parameters of the /Chudnovsky/ series.-series :: (- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm-series = Math.Implementations.Pi.Ramanujan.Series.MkSeries {- Math.Implementations.Pi.Ramanujan.Series.terms = \factorialAlgorithm -> zipWith (-{-- \n power -> let- product' = Data.PrimeFactors.product' (recip 2) 10- in uncurry (%) . (- (* (13591409 + 545140134 * n)) . product' *** (* power) . product'- ) $ Math.Implementations.Factorial.primeFactors (6 * n) >/< (- Math.Implementations.Factorial.primeFactors (3 * n) >*< Math.Implementations.Factorial.primeFactors n >^ 3- )--}- \n power -> (- Math.Implementations.Factorial.risingFactorial (succ $ 3 * n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)- ) * (- (13591409 + 545140134 * n) % power- ) -- CAVEAT: the order in which these terms are evaluated radically affects performance.- ) [0 ..] $ iterate (* (Math.Power.cube $ negate 640320 :: Integer)) 1,- Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = \squareRootAlgorithm decimalDigits -> 426880 * Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (10005 :: Integer),- Math.Implementations.Pi.Ramanujan.Series.convergenceRate = 10 ** negate 14.0 -- Empirical.-}-
− src/Factory/Math/Implementations/Pi/Ramanujan/Classic.hs
@@ -1,60 +0,0 @@-{-- Copyright (C) 2011 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 the /Ramanujan/ series for /Pi/; <http://planetmath.org/encyclopedia/RamanujansFormulaForPi.html>.--}--module Factory.Math.Implementations.Pi.Ramanujan.Classic(--- * Constants- series-) where---- import Control.Arrow((***))-import Data.Ratio((%))--- import Factory.Data.PrimeFactors((>/<), (>^))--- import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.SquareRoot as Math.SquareRoot---- | Defines the parameters of the /Ramanujan/ series.-series :: (Math.SquareRoot.Algorithmic squareRootAlgorithm, Math.Factorial.Algorithmic factorialAlgorithm) => Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm-series = Math.Implementations.Pi.Ramanujan.Series.MkSeries {- Math.Implementations.Pi.Ramanujan.Series.terms = \factorialAlgorithm -> let- toFourthPower = (^ (4 :: Int))- in zipWith (-{-- \n power -> let- product' = Data.PrimeFactors.product' (recip 2) 10- in uncurry (%) . (- (* (1103 + 26390 * n)) . product' *** (* power) . product'- ) $ Math.Implementations.Factorial.primeFactors (4 * n) >/< Math.Implementations.Factorial.primeFactors n >^ 4--}- \n power -> (- Math.Implementations.Factorial.risingFactorial (succ n) (3 * n) % Math.Power.cube (Math.Factorial.factorial factorialAlgorithm n)- ) * (- (1103 + 26390 * n) % power- ) -- CAVEAT: the order in which these terms are evaluated radically affects performance.- ) [0 ..] $ iterate (* toFourthPower 396) 1,- Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = \squareRootAlgorithm decimalDigits -> 9801 / Math.SquareRoot.squareRoot squareRootAlgorithm decimalDigits (8 :: Integer),- Math.Implementations.Pi.Ramanujan.Series.convergenceRate = 10 ** negate 7.9 -- Empirical.-}-
− src/Factory/Math/Implementations/Pi/Ramanujan/Implementation.hs
@@ -1,52 +0,0 @@-{-- Copyright (C) 2011 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 a /Ramanujan/-type series for /Pi/; <http://en.wikipedia.org/wiki/Srinivasa_Ramanujan>.--}--module Factory.Math.Implementations.Pi.Ramanujan.Implementation(--- * Functions- openR-) where--import qualified Control.Parallel.Strategies-import qualified Factory.Math.Implementations.Pi.Ramanujan.Series as Math.Implementations.Pi.Ramanujan.Series-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.Summation as Math.Summation---- | Returns /Pi/, accurate to the specified number of decimal digits.-openR- :: Math.Implementations.Pi.Ramanujan.Series.Series squareRootAlgorithm factorialAlgorithm -- ^ This /Pi/-algorithm is parameterised by the type of other algorithms to use.- -> squareRootAlgorithm -- ^ The specific /square-root/ algorithm to apply to the above series.- -> factorialAlgorithm -- ^ The specific /factorial/-algorithm to apply to the above series.- -> Math.Precision.DecimalDigits -- ^ The number of decimal digits required.- -> Rational-openR Math.Implementations.Pi.Ramanujan.Series.MkSeries {- Math.Implementations.Pi.Ramanujan.Series.terms = terms,- Math.Implementations.Pi.Ramanujan.Series.getSeriesScalingFactor = getSeriesScalingFactor,- Math.Implementations.Pi.Ramanujan.Series.convergenceRate = convergenceRate-} squareRootAlgorithm factorialAlgorithm decimalDigits = uncurry (/) $ Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq- ) (- getSeriesScalingFactor squareRootAlgorithm decimalDigits,- Math.Summation.sumR 64 . take (- Math.Precision.getTermsRequired convergenceRate decimalDigits- ) $ terms factorialAlgorithm- ) -- Pair.-
− src/Factory/Math/Implementations/Pi/Ramanujan/Series.hs
@@ -1,37 +0,0 @@-{-- Copyright (C) 2011 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 a <http://en.wikipedia.org/wiki/Srinivasa_Ramanujan>-type series for /Pi/.--}--module Factory.Math.Implementations.Pi.Ramanujan.Series(--- * Types--- ** Data-types- Series(..)-) where--import qualified Factory.Math.Precision as Math.Precision---- | Defines a series corresponding to a specific /Ramanujan/-formula.-data Series squareRootAlgorithm factorialAlgorithm = MkSeries {- terms :: factorialAlgorithm -> [Rational], -- ^ The sequence of terms, the sum to infinity of which defines the series.- getSeriesScalingFactor :: squareRootAlgorithm -> Math.Precision.DecimalDigits -> Rational, -- ^ The ratio by which the sum to infinity of the sequence, must be scaled to result in /Pi/.- convergenceRate :: Math.Precision.ConvergenceRate -- ^ The expected number of digits of /Pi/, per term in the series.-}-
− src/Factory/Math/Implementations/Pi/Spigot/Algorithm.hs
@@ -1,50 +0,0 @@-{-- Copyright (C) 2011 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 the set of /Spigot/-algorithms which have been implemented.--}--module Factory.Math.Implementations.Pi.Spigot.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import Data.Ratio((%))-import qualified Factory.Math.Implementations.Pi.Spigot.Gosper as Math.Implementations.Pi.Spigot.Gosper-import qualified Factory.Math.Implementations.Pi.Spigot.RabinowitzWagon as Math.Implementations.Pi.Spigot.RabinowitzWagon-import qualified Factory.Math.Implementations.Pi.Spigot.Spigot as Math.Implementations.Pi.Spigot.Spigot-import qualified Factory.Math.Pi as Math.Pi-import qualified ToolShed.Defaultable---- | Define those /Spigot/-algorithms which have been implemented.-data Algorithm =- Gosper -- ^ A /continued fraction/ discovered by /Gosper/.- | RabinowitzWagon -- ^ A /continued fraction/ discovered by /Rabinowitz/ and /Wagon/.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = Gosper--instance Math.Pi.Algorithmic Algorithm where- openI Gosper = Math.Implementations.Pi.Spigot.Spigot.openI Math.Implementations.Pi.Spigot.Gosper.series- openI RabinowitzWagon = Math.Implementations.Pi.Spigot.Spigot.openI Math.Implementations.Pi.Spigot.RabinowitzWagon.series-- openR algorithm decimalDigits = Math.Pi.openI algorithm decimalDigits % (10 ^ pred decimalDigits)-
− src/Factory/Math/Implementations/Pi/Spigot/Gosper.hs
@@ -1,39 +0,0 @@-{-- Copyright (C) 2011 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 the /Gosper/ series; <http://www.pi314.net/eng/goutte.php>--}--module Factory.Math.Implementations.Pi.Spigot.Gosper(--- * Constants- series-) where--import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series-import qualified Factory.Math.Precision as Math.Precision---- | Defines a series which converges to /Pi/.-series :: Integral i => Math.Implementations.Pi.Spigot.Series.Series i-series = Math.Implementations.Pi.Spigot.Series.MkSeries {- Math.Implementations.Pi.Spigot.Series.baseNumerators = map (\i -> i * pred (2 * i)) [1 ..],- Math.Implementations.Pi.Spigot.Series.baseDenominators = map ((* 3) . (\i -> succ i * (i + 2))) [3, 6 ..],- Math.Implementations.Pi.Spigot.Series.coefficients = [3, 8 ..], -- 5n - 2- Math.Implementations.Pi.Spigot.Series.nTerms = Math.Precision.getTermsRequired $ 1 / 13 {-empirical convergence-rate-}-}-
− src/Factory/Math/Implementations/Pi/Spigot/RabinowitzWagon.hs
@@ -1,40 +0,0 @@-{-- Copyright (C) 2011 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 the /Rabinowitz-Wagon/ series;- <http://web.comlab.ox.ac.uk/oucl/work/jeremy.gibbons/publications/spigot.pdf>- <http://www.mathpropress.com/stan/bibliography/spigot.pdf>.--}--module Factory.Math.Implementations.Pi.Spigot.RabinowitzWagon(--- * Constants- series-) where--import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series-import qualified Factory.Math.Precision as Math.Precision---- | Defines a series which converges to /Pi/.-series :: Integral i => Math.Implementations.Pi.Spigot.Series.Series i-series = Math.Implementations.Pi.Spigot.Series.MkSeries {- Math.Implementations.Pi.Spigot.Series.baseNumerators = [1 ..],- Math.Implementations.Pi.Spigot.Series.baseDenominators = [3, 5 ..],- Math.Implementations.Pi.Spigot.Series.coefficients = repeat 2,- Math.Implementations.Pi.Spigot.Series.nTerms = Math.Precision.getTermsRequired $ 10 ** negate (3 / 10) {-convergence-rate-}-}
− src/Factory/Math/Implementations/Pi/Spigot/Series.hs
@@ -1,53 +0,0 @@-{-- Copyright (C) 2011 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 the parameters of a series used in a /Spigot/-table to generate /Pi/.--}--module Factory.Math.Implementations.Pi.Spigot.Series(--- * Types--- ** Data-types- Series(..),--- * Functions- bases-) where--import Data.Ratio((%))-import qualified Data.Ratio-import qualified Factory.Math.Precision as Math.Precision--{- |- * Defines a series composed from a sum of terms, each one of which is the product of a coefficient and a base.-- * The coefficents and bases of the series are described in /Horner form/; @Pi = c1 + (b1 * (c2 + b2 * (c3 + b3 * (...))))@.--}-data Series i = MkSeries {- coefficients :: [i],- baseNumerators :: [i],- baseDenominators :: [i],- nTerms :: Math.Precision.DecimalDigits -> Int -- ^ The width of the spigot-table, required to accurately generate the requested number of digits.-}---- | Combines 'baseNumerators' and 'baseDenominators', and as a side-effect, expresses the ratio in lowest terms.-bases :: Integral i => Series i -> [Data.Ratio.Ratio i]-bases MkSeries {- baseNumerators = n,- baseDenominators = d-} = zipWith (%) n d-
− src/Factory/Math/Implementations/Pi/Spigot/Spigot.hs
@@ -1,153 +0,0 @@-{-- Copyright (C) 2011 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 a /spigot/-algorithm; <http://en.wikipedia.org/wiki/Spigot_algorithm>.-- * Uses the traditional algorithm, rather than the /unbounded/ algorithm described by <http://www.comlab.ox.ac.uk/jeremy.gibbons/publications/spigot.pdf>.--}--module Factory.Math.Implementations.Pi.Spigot.Spigot(--- * Types--- ** Type-synonyms--- Base,--- Coefficients,--- I,--- Pi,--- PreDigits,--- QuotRem,--- * Constants- decimal,--- * Functions--- carryAndDivide,--- processColumns,- openI,--- ** Accessors--- getQuotient,--- getRemainder,--- ** Constructors--- mkRow-) where--import qualified Control.Arrow-import qualified Data.Char-import qualified Data.Ratio-import qualified Factory.Math.Implementations.Pi.Spigot.Series as Math.Implementations.Pi.Spigot.Series-import qualified Factory.Math.Precision as Math.Precision--{- |- * The type in which all arithmetic is performed.-- * A small dynamic range, 32 bits or more, is typically adequate.--}-type I = Int---- | The constant base in which we want the resulting value of /Pi/ to be expressed.-decimal :: I-decimal = 10---- | Coerce the polymorphic type 'Data.Ratio.Ratio' to suit the base used in our series.-type Base = Data.Ratio.Ratio I---- | Coerce the polymorphic type returned by 'quotRem' to our specific requirements.-type QuotRem = (I, I)---- Accessors.-getQuotient, getRemainder :: QuotRem -> I-getQuotient = fst-getRemainder = snd--type PreDigits = [I]-type Pi = [I]-type Coefficients = [I]--{- |- * For a digit on one row of the spigot-table, add any numerator carried from the similar calculation one column to the right.-- * Divide the result of this summation, by the denominator of the base, to get the quotient and remainder.-- * Determine the quantity to carry to the similar calculation one column to the left, by multiplying the quotient by the numerator of the base.--}-carryAndDivide :: (Base, I) -> QuotRem -> QuotRem-carryAndDivide (base, lhs) rhs- | n < d = (0, n) -- In some degenerate cases, the result of the subsequent calculation can be more simply determined.- | otherwise = Control.Arrow.first (* Data.Ratio.numerator base) $ n `quotRem` d- where- d, n :: I- d = Data.Ratio.denominator base- n = lhs + getQuotient rhs -- Carry numerator from the column to the right and add it to the current digit.--{- |- * Fold 'carryAndDivide', from right to left, over the columns of a row in the spigot-table, continuously checking for overflow.-- * Release any previously withheld result-digits, after any adjustment for overflow in the current result-digit.-- * Withhold the current result-digit until the risk of overflow in subsequent result-digits has been assessed.-- * Call 'mkRow'.--}-processColumns- :: Math.Implementations.Pi.Spigot.Series.Series I- -> PreDigits- -> [(Base, I)] -- ^ Data-row.- -> Pi-processColumns series preDigits l- | overflowMargin > 1 = preDigits ++ nextRow [digit] -- There's neither overflow, nor risk of impact from subsequent overflow.- | overflowMargin == 1 = nextRow $ preDigits ++ [digit] -- There's no overflow, but risk of impact from subsequent overflow.- | otherwise = map ((`rem` decimal) . succ) preDigits ++ nextRow [0] -- Overflow => propagate the excess to previously withheld preDigits.- where- results :: [QuotRem]- results = init $ scanr carryAndDivide (0, undefined) l-- digit :: I- digit = getQuotient $ head results-- overflowMargin :: I- overflowMargin = decimal - digit-- nextRow :: [I] -> [I]- nextRow preDigits' = mkRow series preDigits' $ map getRemainder results--{- |- * Multiply the remainders from the previous row.-- * Zip them with the constant bases, with an addition one stuck on the front to perform the conversion to decimal, to create a new row of the spigot-table.-- * Call 'processColumns'.--}-mkRow :: Math.Implementations.Pi.Spigot.Series.Series I -> PreDigits -> Coefficients -> Pi-mkRow series preDigits = processColumns series preDigits . zip (recip (fromIntegral decimal) : Math.Implementations.Pi.Spigot.Series.bases series) . map (* decimal)--{- |- * Initialises a /spigot/-table with the row of 'Math.Implementations.Pi.Spigot.Series.coefficients'.-- * Ensures that the row has suffient terms to accurately generate the required number of digits.-- * Extracts only those digits which are guaranteed to be accurate.-- * CAVEAT: the result is returned as an 'Integer', i.e. without any decimal point.--}-openI :: Math.Implementations.Pi.Spigot.Series.Series I -> Math.Precision.DecimalDigits -> Integer-openI series decimalDigits = read . map (- Data.Char.intToDigit . fromIntegral- ) . take decimalDigits . mkRow series [] . take (- Math.Implementations.Pi.Spigot.Series.nTerms series decimalDigits- ) $ Math.Implementations.Pi.Spigot.Series.coefficients series-
− src/Factory/Math/Implementations/Primality.hs
@@ -1,217 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Determines whether an integer is prime.-- * <http://en.wikipedia.org/wiki/Primality_test>.-- * <http://primes.utm.edu/index.html>-- * CAVEAT: it doesn't determine the prime-factors of composite numbers, just that they exist.--}--module Factory.Math.Implementations.Primality(--- * Types--- ** Data-types- Algorithm(..)--- * Functions--- ** Predicates--- isPrimeByAKS,--- isPrimeByMillerRabin,--- witnessesCompositeness-) where--import Control.Arrow((&&&))-import qualified Control.DeepSeq-import qualified Control.Parallel.Strategies-import qualified Data.Numbers.Primes-import qualified Factory.Data.MonicPolynomial as Data.MonicPolynomial-import qualified Factory.Data.Polynomial as Data.Polynomial-import qualified Factory.Data.QuotientRing as Data.QuotientRing-import qualified Factory.Math.MultiplicativeOrder as Math.MultiplicativeOrder-import qualified Factory.Math.PerfectPower as Math.PerfectPower-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Primality as Math.Primality-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation-import qualified ToolShed.Defaultable---- | The algorithms by which /primality/-testing has been implemented.-data Algorithm factorisationAlgorithm =- AKS factorisationAlgorithm -- ^ <http://en.wikipedia.org/wiki/AKS_primality_test>.- | MillerRabin -- ^ <http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test>.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable (Algorithm factorisationAlgorithm) where- defaultValue = MillerRabin--instance Math.PrimeFactorisation.Algorithmic factorisationAlgorithm => Math.Primality.Algorithmic (Algorithm factorisationAlgorithm) where- isPrime _ 2 = True -- The only even prime.- isPrime algorithm candidate- | candidate < 2 || (- any (- (== 0) . (candidate `rem`) -- The candidate has a small prime-factor, and is therefore composite.- ) . filter (- (candidate >=) . (* 2) -- The candidate must be at least double the small prime, for it to be a potential factor.- ) . take 5 {-arbitrarily-} $ Data.Numbers.Primes.primes -- Excludes even numbers, provided at least the 1st prime is tested.- ) = False- | otherwise = (- case algorithm of- AKS factorisationAlgorithm -> isPrimeByAKS factorisationAlgorithm- MillerRabin -> isPrimeByMillerRabin- ) candidate--{- |- * An implementation of the /Agrawal-Kayal-Saxena/ primality-test; <http://en.wikipedia.org/wiki/AKS_primality_test>,- using the /Lenstra/ and /Pomerance/ algorithm.-- * CAVEAT: this deterministic algorithm has a theoretical time-complexity of @O(log^6)@,- and therefore can't compete with the performance of probabilistic ones.-- * The /formal polynomials/ used in this algorithm, are conceptually different from /polynomial functions/;- the /indeterminate/ and its powers, are merely used to name a sequence of pigeon-holes in which /coefficients/ are stored,- and is never substituted for a specific value.- This mind-shift, allows one to introduce concepts like /modular/ arithmetic on polynomials,- which merely represent an operation on their coefficients and the pigeon-hole in which they're placed.-- [@Manindra Agrawal, Neeraj Kayal and Nitin Saxena@] <http://www.cse.iitk.ac.in/users/manindra/algebra/primality_v6.pdf>.-- [@H. W. Lenstra, Jr. and Carl Pomerance@] <http://www.math.dartmouth.edu/~carlp/PDF/complexity12.pdf>.-- [@Salembier and Southerington@] <http://ece.gmu.edu/courses/ECE746/project/F06_Project_resources/Salembier_Southerington_AKS.pdf>,-- [@R. Crandall and J. Papadopoulos@] <http://images.apple.com/acg/pdf/aks3.pdf>,-- [@Andreas Klappenecker@] <http://faculty.cs.tamu.edu/klappi/629/aks.ps>,-- [@Vibhor Bhatt and G. K. Patra@] <http://www.cmmacs.ernet.in/cmmacs/Publications/resch_rep/rrcm0307.pdf>,--}-isPrimeByAKS :: (- Control.DeepSeq.NFData i,- Integral i,- Math.PrimeFactorisation.Algorithmic factorisationAlgorithm,- Show i- ) => factorisationAlgorithm -> i -> Bool-isPrimeByAKS factorisationAlgorithm n = and [- not $ Math.PerfectPower.isPerfectPower n, -- Step 1.- Math.Primality.areCoprime n `all` filter (/= n) [2 .. r], -- Step 3.- and $ Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq {-Benefits from '+RTS -H100M', which reduces garbage-collections-} (- \a -> let--- lhs, rhs :: Data.Polynomial.Polynomial i i- lhs = Data.Polynomial.raiseModulo (Data.Polynomial.mkLinear 1 a) n {-power-} n {-modulus-}- rhs = Data.Polynomial.mod' (Data.Polynomial.mkPolynomial [(1, n), (a, 0)]) n- in Data.QuotientRing.areCongruentModulo (- Data.MonicPolynomial.mkMonicPolynomial lhs- ) (- Data.MonicPolynomial.mkMonicPolynomial rhs- ) (- Data.MonicPolynomial.mkMonicPolynomial modulus- ) -- Because all these polynomials are /monic/, one can establish /congruence/ using /integer/-division.- ) [- 1 .. floor . (* lg) . sqrt $ fromIntegral r- ] -- Step 4; (x + a)^n ~ x^n + a mod (x^r - 1, n).- ] where- lg :: Double- lg = logBase 2 $ fromIntegral n---- r :: i- r = fst . head . dropWhile (- (<= floor (Math.Power.square lg)) . snd- ) . map (- id &&& Math.MultiplicativeOrder.multiplicativeOrder factorisationAlgorithm n- ) $ Math.Primality.areCoprime n `filter` [2 ..] -- Step 2.---- modulus :: Data.Polynomial.Polynomial i i- modulus = Data.Polynomial.mkPolynomial [(1, r), (negate 1, 0)]--{- |- * Uses the specified 'base' in an attempt to prove the /compositeness/ of an integer.-- * This is the opposite of the /Miller Test/; <http://mathworld.wolfram.com/MillersPrimalityTest.html>.-- * If the result is 'True', then the candidate is /composite/; regrettably the converse isn't true.- Amongst the set of possible bases, over three-quarters are /witnesses/ to the compositeness of a /composite/ candidate,- the remainder belong to the subset of /liars/.- In consequence, many false results must be accumulated for different bases, to convincingly identify a prime.--}-witnessesCompositeness :: (Integral i, Show i)- => i -- ^ Candidate integer.- -> i- -> Int- -> i -- ^ Base.- -> Bool-witnessesCompositeness candidate oddRemainder nPowersOfTwo base = all (- $ ((`rem` candidate) . Math.Power.square) `iterate` Math.Power.raiseModulo base oddRemainder candidate -- Repeatedly modulo-square.- ) [- (/= 1) . head, -- Check whether the zeroeth modulo-power is incongruent to one.- notElem (pred candidate) . take nPowersOfTwo -- Check whether any modulo-power is incongruent to -1.- ]--{- |- * Repeatedly calls 'witnessesCompositeness', to progressively increase the probability of detecting a /composite/ number,- until ultimately the candidate integer is proven to be prime.-- * Should all bases be tested, then the test is deterministic, but at an efficiency /lower/ than performing prime-factorisation.-- * The test becomes deterministic, for any candidate integer, when the number of tests reaches the limit defined by /Eric Bach/.-- * A testing of smaller set of bases, is sufficient for candidates smaller than various thresholds; <http://primes.utm.edu/prove/prove2_3.html>.-- * <http://en.wikipedia.org/wiki/Miller-Rabin_primality_test>.-- * <http://mathworld.wolfram.com/Rabin-MillerStrongPseudoprimeTest.html>-- * <http://mathworld.wolfram.com/StrongPseudoprime.html>.-- * <http://oeis.org/A014233>, <http://oeis.org/A006945>.--}-isPrimeByMillerRabin :: (Integral i, Show i) => i -> Bool-isPrimeByMillerRabin primeCandidate = not $ witnessesCompositeness primeCandidate (- fst $ last binaryFactors -- Odd-remainder.- ) (- length binaryFactors -- The number of times that 'two' can be factored-out from 'predecessor'.- ) `any` testBases where- predecessor = pred primeCandidate- binaryFactors = takeWhile ((== 0) . snd) . tail {-drop the original-} $ iterate ((`quotRem` 2) . fst) (predecessor, 0) -- Factor-out powers of two.- testBases- | null fewestPrimeBases = let- millersTestSet = floor . (* 2 {-Eric Bach-}) . Math.Power.square . toRational {-avoid premature rounding-} $ log (fromIntegral primeCandidate :: Double {-overflows at 10^851-})- in [2 .. predecessor `min` millersTestSet]- | otherwise = head fewestPrimeBases `take` Data.Numbers.Primes.primes- where- fewestPrimeBases = map fst $ dropWhile ((primeCandidate >=) . snd) [- (0, 9), -- All odd integers less this, are prime, and require no further verification.- (1, 2047),- (2, 1373653),- (3, 25326001),- (4, 3215031751),- (5, 2152302898747), -- Jaeschke ...- (6, 3474749660383),- (8, 341550071728321),- (11, 3825123056546413051), -- Zhang ...- (12, 318665857834031151167461),- (13, 3317044064679887385961981),- (14, 6003094289670105800312596501),- (15, 59276361075595573263446330101),- (17, 564132928021909221014087501701),- (19, 1543267864443420616877677640751301),- (20, 10 ^ (36 :: Int)) -- At least.- ]-
− src/Factory/Math/Implementations/PrimeFactorisation.hs
@@ -1,145 +0,0 @@-{-- Copyright (C) 2011 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 several different prime-factorisation algorithms.-- * <http://www.tug.org/texinfohtml/coreutils.html#factor-invocation>.--}--module Factory.Math.Implementations.PrimeFactorisation(--- * Types--- ** Data-types- Algorithm(--- DixonsMethod,- FermatsMethod,- TrialDivision- )--- * Functions--- factoriseByDixonsMethod--- factoriseByFermatsMethod--- factoriseByTrialDivision-) where--import Control.Arrow((&&&))-import qualified Control.Arrow-import qualified Control.DeepSeq-import qualified Control.Parallel.Strategies-import qualified Data.Maybe-import qualified Data.Numbers.Primes-import qualified Factory.Data.Exponential as Data.Exponential-import Factory.Data.Exponential((<^))-import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.PerfectPower as Math.PerfectPower-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation-import qualified ToolShed.Data.Pair-import qualified ToolShed.Defaultable---- | The algorithms by which prime-factorisation has been implemented.-data Algorithm- = DixonsMethod -- ^ <http://en.wikipedia.org/wiki/Dixon%27s_factorization_method>.- | FermatsMethod -- ^ <http://en.wikipedia.org/wiki/Fermat%27s_factorization_method>.- | TrialDivision -- ^ <http://en.wikipedia.org/wiki/Trial_division>.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = TrialDivision--instance Math.PrimeFactorisation.Algorithmic Algorithm where- primeFactors algorithm = case algorithm of- DixonsMethod -> factoriseByDixonsMethod- FermatsMethod -> Data.PrimeFactors.reduce . factoriseByFermatsMethod- TrialDivision -> factoriseByTrialDivision---- | <http://en.wikipedia.org/wiki/Dixon%27s_factorization_method>.-factoriseByDixonsMethod :: Integral base => base -> Data.PrimeFactors.Factors base exponent-factoriseByDixonsMethod = undefined--{- |- * <http://en.wikipedia.org/wiki/Fermat%27s_factorization_method>.-- * <http://mathworld.wolfram.com/FermatsFactorizationMethod.html>.-- * <http://en.wikipedia.org/wiki/Congruence_of_squares>.-- * @i = f1 * f2@ Assume a non-trivial factorisation, ie. one in which both factors exceed one.- => @i = (larger + smaller) * (larger - smaller)@ Represent the co-factors as a sum and difference.- => @i = larger^2 - smaller^2@ Which has an integral solution if @i@ is neither /even/ nor a /perfect square/.- => @sqrt (larger^2 - i) = smaller@ Search for /larger/, which results in an integral value for /smaller/.-- * Given that the smaller factor /f2/, can't be less than 3 (/i/ isn't /even/), then the larger /f1/, can't be greater than @(i `div` 3)@.- So: @(f2 >= 3) && (f1 <= i `div` 3)@ Two equations which can be used to solve for /larger/.- => @(larger - smaller >= 3) && (larger + smaller <= i `div` 3)@ Add these to eliminate /smaller/.- => @larger <= (i + 9) `div` 6@ The upper bound of the search-space.-- * This algorithm works best when there's a factor close to the /square-root/.--}-factoriseByFermatsMethod :: (- Control.DeepSeq.NFData base,- Control.DeepSeq.NFData exponent,- Integral base,- Num exponent- ) => base -> Data.PrimeFactors.Factors base exponent-factoriseByFermatsMethod i- | i <= 3 = [Data.Exponential.rightIdentity i]- | even i = Data.Exponential.rightIdentity 2 : factoriseByFermatsMethod (i `div` 2) {-recurse-}- | Data.Maybe.isJust maybeSquareNumber = (<^ 2) `map` factoriseByFermatsMethod (Data.Maybe.fromJust maybeSquareNumber) {-recurse-}- | null factors = [Data.Exponential.rightIdentity i] -- Prime.- | otherwise = uncurry (++) . Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parTuple2 Control.Parallel.Strategies.rdeepseq Control.Parallel.Strategies.rdeepseq -- CAVEAT: unproductive on the size of integers tested so far.- ) . ToolShed.Data.Pair.mirror factoriseByFermatsMethod $ head factors- where--- maybeSquareNumber :: Integral i => Maybe i- maybeSquareNumber = Math.PerfectPower.maybeSquareNumber i---- factors :: Integral i => [i]- factors = map (- (- uncurry (+) &&& uncurry (-) -- Construct the co-factors as the sum and difference of /larger/ and /smaller/.- ) . Control.Arrow.second Data.Maybe.fromJust- ) . filter (- Data.Maybe.isJust . snd -- Search for a perfect square.- ) . map (- Control.Arrow.second $ Math.PerfectPower.maybeSquareNumber {-hotspot-} . (+ negate i) -- Associate the corresponding value of /smaller/.- ) . takeWhile (- (<= (i + 9) `div` 6) . fst -- Terminate the search at the maximum value of /larger/.- ) . Math.Power.squaresFrom {-hotspot-} . ceiling $ sqrt (fromIntegral i :: Double) -- Start the search at the minimum value of /larger/.--{- |- * Decomposes the specified integer, into a product of /prime/-factors,- using <http://mathworld.wolfram.com/DirectSearchFactorization.html>, AKA <http://en.wikipedia.org/wiki/Trial_division>.-- * This works best when the factors are small.--}-factoriseByTrialDivision :: (Integral base, Num exponent) => base -> Data.PrimeFactors.Factors base exponent-factoriseByTrialDivision = slave Data.Numbers.Primes.primes where- slave primes i- | null primeCandidates = [Data.Exponential.rightIdentity i]- | otherwise = Data.Exponential.rightIdentity lowestPrimeFactor `Data.PrimeFactors.insert'` slave primeCandidates (i `quot` lowestPrimeFactor)- where- primeCandidates = dropWhile (- (/= 0) . (i `rem`)- ) $ takeWhile (- <= Math.PrimeFactorisation.maxBoundPrimeFactor i- ) primes-- lowestPrimeFactor = head primeCandidates-
− src/Factory/Math/Implementations/Primes/Algorithm.hs
@@ -1,63 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Generates the constant list of /prime-numbers/, by a variety of different algorithms.-- * <http://www.haskell.org/haskellwiki/Prime_numbers>.-- * <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.31.3936&rep=rep1&type=pdf>.-- * <http://larc.unt.edu/ian/pubs/sieve.pdf>.--}--module Factory.Math.Implementations.Primes.Algorithm(--- * Types--- ** Data-types- Algorithm(..)-) where--import qualified Data.Numbers.Primes-import qualified Factory.Data.PrimeWheel as Data.PrimeWheel-import qualified Factory.Math.Implementations.Primes.SieveOfAtkin as Math.Implementations.Primes.SieveOfAtkin-import qualified Factory.Math.Implementations.Primes.SieveOfEratosthenes as Math.Implementations.Primes.SieveOfEratosthenes-import qualified Factory.Math.Implementations.Primes.TrialDivision as Math.Implementations.Primes.TrialDivision-import qualified Factory.Math.Implementations.Primes.TurnersSieve as Math.Implementations.Primes.TurnersSieve-import qualified Factory.Math.Primes as Math.Primes-import qualified ToolShed.Defaultable---- | The implemented methods by which the primes may be generated.-data Algorithm- = SieveOfAtkin Integer -- ^ The /Sieve of Atkin/, optimised using a 'Data.PrimeWheel.PrimeWheel' of optimal size, for primes up to the specified maximum bound; <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.- | SieveOfEratosthenes Data.PrimeWheel.NPrimes -- ^ The /Sieve of Eratosthenes/ (<http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>), optimised using a 'Data.PrimeWheel.PrimeWheel'.- | TrialDivision Data.PrimeWheel.NPrimes -- ^ For each candidate, confirm indivisibility, by all /primes/ smaller than its /square-root/, optimised using a 'Data.PrimeWheel.PrimeWheel'.- | TurnersSieve -- ^ For each /prime/, the infinite list of candidates greater than its /square/, is filtered for indivisibility; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.- | WheelSieve Int -- ^ 'Data.Numbers.Primes.wheelSieve'.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = SieveOfEratosthenes 7 -- Resulting in a wheel of circumference 510510.--instance Math.Primes.Algorithmic Algorithm where- primes (SieveOfAtkin maxPrime) = Math.Implementations.Primes.SieveOfAtkin.sieveOfAtkin (Data.PrimeWheel.estimateOptimalSize maxPrime) $ fromIntegral maxPrime- primes (SieveOfEratosthenes wheelSize) = Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenes wheelSize- primes (TrialDivision wheelSize) = Math.Implementations.Primes.TrialDivision.trialDivision wheelSize- primes TurnersSieve = Math.Implementations.Primes.TurnersSieve.turnersSieve- primes (WheelSieve wheelSize) = Data.Numbers.Primes.wheelSieve wheelSize -- Has better space-complexity than 'SieveOfEratosthenes'.
− src/Factory/Math/Implementations/Primes/SieveOfAtkin.hs
@@ -1,242 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Generates the constant /bounded/ list of /prime-numbers/, using the /Sieve of Atkin/; <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.-- * <cr.yp.to/papers/primesieves-19990826.pdf>.-- * The implementation;- has been optimised using a /wheel/ of static, but parameterised, size;- has been parallelized;- is polymorphic, but with a specialisation for type 'Int'.-- [@CAVEAT@] The 'Int'-specialisation is implemented by a /rewrite-rule/, which is /very/ fragile.--}--module Factory.Math.Implementations.Primes.SieveOfAtkin(--- * Types--- ** Data-types--- PolynomialType,--- * Constants--- atkinsModulus,--- inherentPrimes,--- nInherentPrimes,--- squares,--- * Functions--- polynomialTypeLookupPeriod,--- polynomialTypeLookup,--- findPolynomialSolutions,--- filterOddRepetitions,--- generateMultiplesOfSquareTo,--- getPrefactoredPrimes,- sieveOfAtkin,--- sieveOfAtkinInt-) where--import qualified Control.DeepSeq-import qualified Control.Parallel.Strategies-import qualified Data.Array.IArray-import Data.Array.IArray((!))-import qualified Data.IntSet-import qualified Data.List-import qualified Data.Set-import qualified Factory.Data.PrimeWheel as Data.PrimeWheel-import qualified Factory.Math.Power as Math.Power-import qualified ToolShed.Data.List---- | Defines the types of /quadratic/, available to test the potential primality of a candidate integer.-data PolynomialType- = ModFour -- ^ Suitable for primality-testing numbers meeting @(n `mod` 4 == 1)@.- | ModSix -- ^ Suitable for primality-testing numbers meeting @(n `mod` 6 == 1)@.- | ModTwelve -- ^ Suitable for primality-testing numbers meeting @(n `mod` 12 == 11)@.- | None -- ^ There's no polynomial which can assess primality, because the candidate is composite.- deriving Eq---- | The constant modulus used to select the appropriate quadratic for a prime candidate.-atkinsModulus :: Integral i => i-atkinsModulus = foldr1 lcm [4, 6, 12] -- Sure, this is always '12', but this is the reason why.---- | The constant list of primes factored-out by the unoptimised algorithm.-inherentPrimes :: Integral i => [i]-inherentPrimes = [2, 3]---- | The constant number of primes factored-out by the unoptimised algorithm.-nInherentPrimes :: Int-nInherentPrimes = length (inherentPrimes :: [Int])---- | Typically the set of primes which have been built into the specified /wheel/, but never fewer than 'inherentPrimes'.-getPrefactoredPrimes :: Integral i => Data.PrimeWheel.PrimeWheel i -> [i]-getPrefactoredPrimes = max inherentPrimes . Data.PrimeWheel.getPrimeComponents---- | The period over which the data returned by 'polynomialTypeLookup' repeats.-polynomialTypeLookupPeriod :: Integral i => Data.PrimeWheel.PrimeWheel i -> i-polynomialTypeLookupPeriod = lcm atkinsModulus . Data.PrimeWheel.getCircumference--{- |- * Defines which, if any, of the three /quadratics/ is appropriate for the primality-test for each candidate.-- * Since this algorithm uses /modular arithmetic/, the /range/ of results repeat after a short /domain/ related to the /modulus/.- Thus one need calculate at most one period of this cycle, but fewer if the maximum prime required falls within the first cycle of results.-- * Because the results are /bounded/, they're returned in a zero-indexed /array/, to provide efficient random access;- the first few elements should never be required, but it makes query clearer.-- * <http://en.wikipedia.org/wiki/Sieve_of_Atkin>.--}-polynomialTypeLookup :: (Data.Array.IArray.Ix i, Integral i)- => Data.PrimeWheel.PrimeWheel i- -> i -- ^ The maximum prime required.- -> Data.Array.IArray.Array i PolynomialType-polynomialTypeLookup primeWheel maxPrime = Data.Array.IArray.listArray (0, pred (polynomialTypeLookupPeriod primeWheel) `min` maxPrime) $ map select [0 ..] where--- select :: Integral i => i -> PolynomialType- select n- | any (- (== 0) . (n `rem`) -- Though this is merely /Trial Division/, it's only performed over a short bounded interval of numerators.- ) primeComponents = None- | r `elem` [1, 5] = ModFour -- We actually require @(n `mod` 4 == 1)@, but this is the equivalent modulo 12, with @(r == 9)@ removed because they're all divisible by /3/.- | r == 7 = ModSix -- We actually require @(n `mod` 6 == 1)@, but this is the equivalent modulo 12, where @(r == 1)@ has been accounted for above.- | r == 11 = ModTwelve -- We require @(n `mod` 12 == 11)@.- | otherwise = None- where- r = n `rem` atkinsModulus- primeComponents = drop nInherentPrimes $ Data.PrimeWheel.getPrimeComponents primeWheel---- | The constant, infinite list of the /squares/, of integers increasing from /1/.-squares :: Integral i => [i]-squares = map snd $ Math.Power.squaresFrom 1--{- |- * Returns the /ordered/ list of those values with an /odd/ number of occurrences in the specified /unordered/ list.-- * CAVEAT: this is expensive in both execution-time and space.- The typical imperative-style implementation accumulates polynomial-solutions in a /mutable array/ indexed by the candidate integer.- This doesn't translate seamlessly to the /pure functional/ domain where /arrays/ naturally immutable,- so we /sort/ a /list/ of polynomial-solutions, then measure the length of the solution-spans, corresponding to viable candidates.- Regrettably, 'Data.List.sort' (implemented in /GHC/ by /mergesort/) has a time-complexity /O(n*log n)/- which is greater than the theoretical /O(n)/ of the whole /Sieve of Atkin/;- /GHC/'s old /qsort/-implementation is even slower :(--}-filterOddRepetitions :: Ord a => [a] -> [a]--- filterOddRepetitions = map head . filter (foldr (const not) False) . Data.List.group . Data.List.sort -- Too slow.-filterOddRepetitions = slave True . Data.List.sort where- slave isOdd (one : remainder@(two : _))- | one == two = slave (not isOdd) remainder- | isOdd = one : beginSpan- | otherwise = beginSpan- where- beginSpan = slave True remainder- slave True [singleton] = [singleton]- slave _ _ = []--{- |- * Returns the ordered list of solutions aggregated from each of three /bivariate quadratics/; @z = f(x, y)@.-- * For a candidate integer to be prime, it is necessary but insufficient, that there are an /odd/ number of solutions of value /candidate/.-- * At most one of these three polynomials is suitable for the validation of any specific candidate /z/, depending on 'lookupPolynomialType'.- so the three sets of solutions are mutually exclusive.- One coordinate @(x, y)@, can have solutions in more than one of the three polynomials.-- * This algorithm exhaustively traverses the domain @(x, y)@, for resulting /z/ of the required modulus.- Whilst it tightly constrains the bounds of the search-space, it searches the domain methodically rather than intelligently.--}-findPolynomialSolutions :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i)- => Data.PrimeWheel.PrimeWheel i- -> i -- ^ The maximum prime-number required.- -> [i]-findPolynomialSolutions primeWheel maxPrime = foldr1 ToolShed.Data.List.merge {-The lists were previously sorted, as a side-effect, by 'filterOddRepetitions'-} $ Control.Parallel.Strategies.withStrategy (- Control.Parallel.Strategies.parList Control.Parallel.Strategies.rdeepseq- ) [- {-# SCC "4x^2+y^2" #-} filterOddRepetitions [- z |- x' <- takeWhile (<= pred maxPrime) $ map (* 4) squares,- z <- takeWhile (<= maxPrime) $ map (+ x') oddSquares,- lookupPolynomialType z == ModFour- ], -- List-comprehension. Twice the length of the other two lists.- {-# SCC "3x^2+y^2" #-} filterOddRepetitions [- z |- x' <- takeWhile (<= pred maxPrime) $ map (* 3) squares,- z <- takeWhile (<= maxPrime) . map (+ x') $ if even x' then oddSelection else evenSelection,- lookupPolynomialType z == ModSix- ], -- List-comprehension.- {-# SCC "3x^2-y^2" #-} filterOddRepetitions [- z |- x2 <- takeWhile (<= maxPrime `div` 2) squares,- z <- dropWhile (> maxPrime) . map (3 * x2 -) . takeWhile (< x2) $ if even x2 then oddSelection else evenSelection,- lookupPolynomialType z == ModTwelve- ] -- List-comprehension.- ] where- (evenSquares, oddSquares) = Data.List.partition even squares---- evenSelection, oddSelection :: Integral i => [i]- evenSelection = selection110 evenSquares where- selection110 (x0 : x1 : _ : xs) = x0 : x1 : selection110 xs -- Effectively, those for meeting ((== 4) . (`mod` 6)).- selection110 xs = xs- oddSelection = selection101 oddSquares where- selection101 (x0 : _ : x2 : xs) = x0 : x2 : selection101 xs -- Effectively, those for meeting ((== 1) . (`mod` 6)).- selection101 xs = xs---- lookupPolynomialType :: (Data.Array.IArray.Ix i, Integral i) => i -> PolynomialType- lookupPolynomialType = (polynomialTypeLookup primeWheel maxPrime !) . (`rem` polynomialTypeLookupPeriod primeWheel)---- | Generates the /bounded/ list of multiples, of the /square/ of the specified prime, skipping those which aren't required.-generateMultiplesOfSquareTo :: Integral i- => Data.PrimeWheel.PrimeWheel i -- ^ Used to generate the gaps between prime multiples of the square.- -> i -- ^ The /prime/.- -> i -- ^ The maximum bound.- -> [i]-generateMultiplesOfSquareTo primeWheel prime max' = takeWhile (<= max') . scanl (\accumulator -> (+ accumulator) . (* prime2)) prime2 . cycle $ Data.PrimeWheel.getSpokeGaps primeWheel where- prime2 = Math.Power.square prime--{- |- * Generates the constant /bounded/ list of /prime-numbers/.-- * <http://cr.yp.to/papers/primesieves-19990826.pdf>--}-sieveOfAtkin :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i)- => Data.PrimeWheel.NPrimes -- ^ Other implementations effectively use a hard-coded value either /2/ or /3/, but /6/ seems better.- -> i -- ^ The maximum prime required.- -> [i] -- ^ The /bounded/ list of primes.-sieveOfAtkin wheelSize maxPrime = (prefactoredPrimes ++) . filterSquareFree Data.Set.empty . dropWhile (<= maximum prefactoredPrimes) $ findPolynomialSolutions primeWheel maxPrime where- primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize- prefactoredPrimes = getPrefactoredPrimes primeWheel---- filterSquareFree :: Integral i => Data.Set.Set i -> [i] -> [i]- filterSquareFree _ [] = []- filterSquareFree primeMultiples (candidate : candidates)- | Data.Set.member candidate primeMultiples = {-# SCC "delete" #-} filterSquareFree (Data.Set.delete candidate primeMultiples) candidates -- Tail-recurse.- | otherwise = {-# SCC "insert" #-} candidate : filterSquareFree (Data.Set.union primeMultiples . Data.Set.fromDistinctAscList $ generateMultiplesOfSquareTo primeWheel candidate maxPrime) candidates--{-# NOINLINE sieveOfAtkin #-}-{-# RULES "sieveOfAtkin/Int" sieveOfAtkin = sieveOfAtkinInt #-} -- CAVEAT: doesn't fire when built with profiling enabled.---- | A specialisation of 'sieveOfAtkin', which reduces both the execution-time and the space required.-sieveOfAtkinInt :: Data.PrimeWheel.NPrimes -> Int -> [Int]-sieveOfAtkinInt wheelSize maxPrime = (prefactoredPrimes ++) . filterSquareFree Data.IntSet.empty . dropWhile (<= maximum prefactoredPrimes) $ findPolynomialSolutions primeWheel maxPrime where- primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize- prefactoredPrimes = getPrefactoredPrimes primeWheel-- filterSquareFree :: Data.IntSet.IntSet -> [Int] -> [Int]- filterSquareFree _ [] = []- filterSquareFree primeMultiples (candidate : candidates)- | Data.IntSet.member candidate primeMultiples = filterSquareFree (Data.IntSet.delete candidate primeMultiples) candidates- | otherwise = candidate : filterSquareFree (Data.IntSet.union primeMultiples . Data.IntSet.fromDistinctAscList $ generateMultiplesOfSquareTo primeWheel candidate maxPrime) candidates-
− src/Factory/Math/Implementations/Primes/SieveOfEratosthenes.hs
@@ -1,162 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Generates the constant, conceptually infinite, list of /prime-numbers/, using the /Sieve of Eratosthenes/; <http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes>.-- * Based on <http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf>.-- * The implementation;- has been optimised using a /wheel/ of static, but parameterised, size;- is polymorphic, but with a specialisation for type 'Int'.-- [@CAVEAT@] The 'Int'-specialisation is implemented by a /rewrite-rule/, which is /very/ fragile.--}--module Factory.Math.Implementations.Primes.SieveOfEratosthenes(--- * Types--- ** Type-synonyms--- PrimeMultiplesQueue,--- PrimeMultiplesMap,--- Repository,--- PrimeMultiplesMapInt,--- RepositoryInt,--- * Functions--- head',--- tail',- sieveOfEratosthenes,--- sieveOfEratosthenesInt-) where--import Control.Arrow((&&&), (***))-import qualified Control.Arrow-import qualified Data.IntMap-import qualified Data.Map-import Data.Sequence((|>))-import qualified Data.Sequence-import qualified Factory.Data.PrimeWheel as Data.PrimeWheel---- | The 'Data.Sequence.Seq' counterpart to 'Data.List.head'.-head' :: Data.Sequence.Seq [a] -> [a]-head' = (`Data.Sequence.index` 0)--{- |- * The 'Data.Sequence.Seq' counterpart to 'Data.List.tail'.-- * CAVEAT: because @ Data.List.tail [] @ returns an error, whereas @ tail' Data.Sequence.empty @ returns 'Data.Sequence.empty',- this function is for internal use only.--}-tail' :: Data.Sequence.Seq [a] -> Data.Sequence.Seq [a]-tail' = Data.Sequence.drop 1---- | An ordered queue of the multiples of primes.-type PrimeMultiplesQueue i = Data.Sequence.Seq (Data.PrimeWheel.PrimeMultiples i)---- | A map of the multiples of primes.-type PrimeMultiplesMap i = Data.Map.Map i (Data.PrimeWheel.PrimeMultiples i)---- | Combine a /queue/, with a /map/, to form a repository to hold prime-multiples.-type Repository i = (PrimeMultiplesQueue i, PrimeMultiplesMap i)--{- |- * A refinement of the /Sieve Of Eratosthenes/, which pre-sieves candidates, selecting only those /coprime/ to the specified short sequence of low prime-numbers.-- * The short sequence of initial primes are represented by a 'Data.PrimeWheel.PrimeWheel',- of parameterised, but static, size; <http://en.wikipedia.org/wiki/Wheel_factorization>.-- * The algorithm requires one to record multiples of previously discovered primes, allowing /composite/ candidates to be eliminated by comparison.-- * Because each /list/ of multiples, starts with the /square/ of the prime from which it was generated,- the vast majority will be larger than the maximum prime ultimately demanded, and the effort of constructing and storing this list, is consequently wasted.- Many implementations solve this, by requiring specification of the maximum prime required,- thus allowing the construction of redundant lists of multiples to be avoided.-- * This implementation doesn't impose that constraint, leaving a requirement for /rapid/ storage,- which is supported by /appending/ the /list/ of prime-multiples, to a /queue/.- If a large enough candidate is ever generated, to match the /head/ of the /list/ of prime-multiples,- at the /head/ of this /queue/, then the whole /list/ of prime-multiples is dropped from the /queue/,- but the /tail/ of this /list/ of prime-multiples, for which there is now a high likelyhood of a subsequent match, must now be re-recorded.- A /queue/ doesn't support efficient random /insertion/, so a 'Data.Map.Map' is used for these subsequent multiples.- This solution is faster than just using a "Data.PQueue.Min".-- * CAVEAT: has linear /O(n)/ space-complexity.--}-sieveOfEratosthenes :: Integral i- => Data.PrimeWheel.NPrimes- -> [i]-sieveOfEratosthenes = uncurry (++) . (Data.PrimeWheel.getPrimeComponents &&& start . Data.PrimeWheel.roll) . Data.PrimeWheel.mkPrimeWheel where- start :: Integral i => [Data.PrimeWheel.Distance i] -> [i]- start ~((candidate, rollingWheel) : distances) = candidate : sieve (head distances) (Data.Sequence.singleton $ Data.PrimeWheel.generateMultiples candidate rollingWheel, Data.Map.empty)-- sieve :: Integral i => Data.PrimeWheel.Distance i -> Repository i -> [i]- sieve distance@(candidate, rollingWheel) repository@(primeSquares, squareFreePrimeMultiples) = case Data.Map.lookup candidate squareFreePrimeMultiples of- Just primeMultiples -> sieve' $ Control.Arrow.second (insertUniq primeMultiples . Data.Map.delete candidate) repository -- Re-insert subsequent multiples.- Nothing -- Not a square-free composite.- | candidate == smallestPrimeSquare -> sieve' $ (tail' *** insertUniq subsequentPrimeMultiples) repository -- Migrate subsequent prime-multiples, from 'primeSquares' to 'squareFreePrimeMultiples'.- | otherwise {-prime-} -> candidate : sieve' (Control.Arrow.first (|> Data.PrimeWheel.generateMultiples candidate rollingWheel) repository)- where- (smallestPrimeSquare : subsequentPrimeMultiples) = head' primeSquares- where--- sieve' :: Repository i -> [i]- sieve' = sieve $ Data.PrimeWheel.rotate distance -- Tail-recurse.-- insertUniq :: Ord i => Data.PrimeWheel.PrimeMultiples i -> PrimeMultiplesMap i -> PrimeMultiplesMap i- insertUniq l m = insert $ dropWhile (`Data.Map.member` m) l where--- insert :: Ord i => Data.PrimeWheel.PrimeMultiples i -> PrimeMultiplesMap i- insert [] = error "Factory.Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenes.sieve.insertUniq.insert:\tnull list"- insert (key : values) = Data.Map.insert key values m--{-# NOINLINE sieveOfEratosthenes #-}-{-# RULES "sieveOfEratosthenes/Int" sieveOfEratosthenes = sieveOfEratosthenesInt #-} -- CAVEAT: doesn't fire when built with profiling enabled.---- | A specialisation of 'PrimeMultiplesMap'.-type PrimeMultiplesMapInt = Data.IntMap.IntMap (Data.PrimeWheel.PrimeMultiples Int)---- | A specialisation of 'Repository'.-type RepositoryInt = (PrimeMultiplesQueue Int, PrimeMultiplesMapInt)--{- |- * A specialisation of 'sieveOfEratosthenes', which approximately /doubles/ the speed and reduces the space required.-- * CAVEAT: because the algorithm involves /squares/ of primes,- this implementation will overflow when finding primes greater than @2^16@ on a /32-bit/ machine.--}-sieveOfEratosthenesInt :: Data.PrimeWheel.NPrimes -> [Int]-sieveOfEratosthenesInt = uncurry (++) . (Data.PrimeWheel.getPrimeComponents &&& start . Data.PrimeWheel.roll) . Data.PrimeWheel.mkPrimeWheel where- start :: [Data.PrimeWheel.Distance Int] -> [Int]- start ~((candidate, rollingWheel) : distances) = candidate : sieve (head distances) (Data.Sequence.singleton $ Data.PrimeWheel.generateMultiples candidate rollingWheel, Data.IntMap.empty)-- sieve :: Data.PrimeWheel.Distance Int -> RepositoryInt -> [Int]- sieve distance@(candidate, rollingWheel) repository@(primeSquares, squareFreePrimeMultiples) = case Data.IntMap.lookup candidate squareFreePrimeMultiples of- Just primeMultiples -> sieve' $ Control.Arrow.second (insertUniq primeMultiples . Data.IntMap.delete candidate) repository- Nothing- | candidate == smallestPrimeSquare -> sieve' $ (tail' *** insertUniq subsequentPrimeMultiples) repository- | otherwise -> candidate : sieve' (Control.Arrow.first (|> Data.PrimeWheel.generateMultiples candidate rollingWheel) repository)- where- (smallestPrimeSquare : subsequentPrimeMultiples) = head' primeSquares- where- sieve' :: RepositoryInt -> [Int]- sieve' = sieve $ Data.PrimeWheel.rotate distance-- insertUniq :: Data.PrimeWheel.PrimeMultiples Int -> PrimeMultiplesMapInt -> PrimeMultiplesMapInt- insertUniq l m = insert $ dropWhile (`Data.IntMap.member` m) l where- insert :: Data.PrimeWheel.PrimeMultiples Int -> PrimeMultiplesMapInt- insert [] = error "Factory.Math.Implementations.Primes.SieveOfEratosthenes.sieveOfEratosthenesInt.sieve.insertUniq.insert:\tnull list"- insert (key : values) = Data.IntMap.insert key values m
− src/Factory/Math/Implementations/Primes/TrialDivision.hs
@@ -1,59 +0,0 @@-{-- Copyright (C) 2011 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@] Generates the constant, conceptually infinite, list of /prime-numbers/, using /Trial Division/.--}--module Factory.Math.Implementations.Primes.TrialDivision(--- * Functions- trialDivision--- ** Predicates--- isIndivisibleBy-) where--import qualified Control.Arrow-import qualified Data.List-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation-import qualified Factory.Data.PrimeWheel as Data.PrimeWheel---- | Uses /Trial Division/, to determine whether the specified candidate is indivisible by all potential denominators from the specified list.-isIndivisibleBy :: Integral i- => i -- ^ The numerator.- -> [i] -- ^ The denominators of which it must not be a multiple.- -> Bool-isIndivisibleBy numerator = all ((/= 0) . (numerator `rem`)) . takeWhile (<= Math.PrimeFactorisation.maxBoundPrimeFactor numerator)--{- |- * For each candidate, confirm indivisibility, by all /primes/ smaller than its /square-root/.-- * The candidates to sieve, are generated by a 'Data.PrimeWheel.PrimeWheel',- of parameterised, but static, size; <http://en.wikipedia.org/wiki/Wheel_factorization>.--}-trialDivision :: Integral prime => Data.PrimeWheel.NPrimes -> [prime]-trialDivision 0 = [2, 3] ++ filter (`isIndivisibleBy` trialDivision 0 {-recurse-}) [5 ..] -- No faster than using 'Data.PrimeWheel.mkPrimeWheel 0', but apparently better space-complexity ?!-trialDivision wheelSize = Data.PrimeWheel.getPrimeComponents primeWheel ++ indivisible where- primeWheel = Data.PrimeWheel.mkPrimeWheel wheelSize- candidates = map fst $ Data.PrimeWheel.roll primeWheel- indivisible = uncurry (++) . Control.Arrow.second (- filter (`isIndivisibleBy` indivisible {-recurse-})- ) $ Data.List.span (- < Math.Power.square (head candidates) -- The first composite candidate, is the square of the next prime after the wheel's constituent ones.- ) candidates-
− src/Factory/Math/Implementations/Primes/TurnersSieve.hs
@@ -1,48 +0,0 @@-{-- Copyright (C) 2011 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@] Generates the constant, conceptally infinite, list of /prime-numbers/, using /Turner's Sieve/; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.--}--module Factory.Math.Implementations.Primes.TurnersSieve(--- * Functions- turnersSieve-) where--import qualified Factory.Math.Power as Math.Power--{- |- * For each /prime/, the infinite list of candidates greater than its /square/,- is filtered for indivisibility; <http://www.haskell.org/haskellwiki/Prime_numbers#Turner.27s_sieve_-_Trial_division>.-- * CAVEAT: though one can easily add a 'Data.PrimeWheel.PrimeWheel', it proved counterproductive.--}-turnersSieve :: Integral prime => [prime]-turnersSieve = 2 : sieve [3, 5 ..] where- sieve :: Integral i => [i] -> [i]- sieve [] = []- sieve (prime : candidates) = prime : sieve (- filter (- \candidate -> any ($ candidate) [- (< Math.Power.square prime), -- Unconditionally admit any candidate smaller than the square of the last prime.- (/= 0) . (`rem` prime) -- Ensure indivisibility, of all subsequent candidates, by the last prime discovered.- ]- ) candidates- )-
− src/Factory/Math/Implementations/SquareRoot.hs
@@ -1,192 +0,0 @@-{-- Copyright (C) 2011 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 'Math.SquareRoot.Algorithmic' by a variety of methods.-- [@CAVEAT@]-- Caller may benefit from application of 'Math.Precision.simplify' before operating on the result;- which though of the required accuracy, may not be the most concise rational number satisfying that criterion.--}-module Factory.Math.Implementations.SquareRoot(--- * Types--- ** Type-synonyms--- ProblemSpecification,- Terms,--- ** Data-types- Algorithm(..)--- * Functions--- squareRootByContinuedFraction,--- squareRootByIteration,--- squareRootByTaylorSeries,--- taylorSeriesCoefficients-) where--import Control.Arrow((***))-import Factory.Data.PrimeFactors((>/<), (>^))-import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified Factory.Math.Summation as Math.Summation-import qualified ToolShed.Defaultable---- | The number of terms in a series.-type Terms = Int---- | The algorithms by which the /square-root/ has been implemented.-data Algorithm- = BakhshaliApproximation -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Bakhshali_approximation>- | ContinuedFraction -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion>.- | HalleysMethod -- ^ <http://en.wikipedia.org/wiki/Halley%27s_method>.- | NewtonRaphsonIteration -- ^ <http://en.wikipedia.org/wiki/Newton%27s_method>.- | TaylorSeries Terms -- ^ <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.- deriving (Eq, Read, Show)--instance ToolShed.Defaultable.Defaultable Algorithm where- defaultValue = NewtonRaphsonIteration---- | Returns an improved estimate for the /square-root/ of the specified value, to the required precision, using the supplied initial estimate..-type ProblemSpecification operand- = Math.SquareRoot.Estimate- -> Math.Precision.DecimalDigits -- ^ The required precision.- -> operand -- ^ The value for which to find the /square-root/.- -> Math.SquareRoot.Result--instance Math.SquareRoot.Algorithmic Algorithm where- squareRootFrom _ _ _ 0 = 0- squareRootFrom _ _ _ 1 = 1- squareRootFrom algorithm estimate@(x, decimalDigits) requiredDecimalDigits y- | decimalDigits >= requiredDecimalDigits = x- | requiredDecimalDigits <= 0 = error $ "Factory.Math.Implementations.SquareRoot.squareRootFrom:\tinvalid number of required decimal digits; " ++ show requiredDecimalDigits- | y < 0 = error $ "Factory.Math.Implementations.SquareRoot.squareRootFrom:\tthere's no real square-root of " ++ show y- | otherwise = (- case algorithm of- ContinuedFraction -> squareRootByContinuedFraction- _ -> squareRootByIteration algorithm- ) estimate requiredDecimalDigits y--instance Math.SquareRoot.Iterator Algorithm where- step BakhshaliApproximation y x- | dy == 0 = x -- The estimate was precise.- | otherwise = x' - dx' -- Correct the estimate.- where- dy, dydx, dx, x', dydx', dx' :: Math.SquareRoot.Result- dy = Math.SquareRoot.getDiscrepancy y x- dydx = 2 * x- dx = dy / dydx- x' = x + dx -- Identical to Newton-Raphson iteration.- dydx' = 2 * x'- dx' = Math.Power.square dx / dydx'--{-- * /Halley's/ method; <http://mathworld.wolfram.com/HalleysMethod.html>--> X(n+1) = Xn - f(Xn) / [f'(Xn) - f''(Xn) * f(Xn) / 2 * f'(Xn)]-> => Xn - (Xn^2 - Y) / [2Xn - 2 * (Xn^2 - Y) / 2 * 2Xn] where Y = X^2, f(X) = X^2 - Y, f'(X) = 2X, f''(X) = 2-> => Xn - 1 / [2Xn / (Xn^2 - Y) - 1 / 2Xn]--}- step HalleysMethod y x- | dy == 0 = x -- The estimate was precise.- | otherwise = x - dx -- Correct the estimate.- where- dy, dydx, dx :: Math.SquareRoot.Result- dy = negate $ Math.SquareRoot.getDiscrepancy y x -- Use the estimate to determine the error in 'y'.- dydx = 2 * x -- The gradient, at the estimated value 'x'.- dx = recip $ dydx / dy - recip dydx---- step NewtonRaphsonIteration y x = (x + toRational y / x) / 2 -- This is identical to the /Babylonian Method/.--- step NewtonRaphsonIteration y x = x / 2 + toRational y / (2 * x) -- Faster.- step NewtonRaphsonIteration y x = x / 2 + (toRational y / 2) / x -- Faster still.-- step (TaylorSeries terms) y x = squareRootByTaylorSeries terms y x-- step algorithm _ _ = error $ "Factory.Math.Implementations.SquareRoot.step:\tinappropriate algorithm; " ++ show algorithm-- convergenceOrder BakhshaliApproximation = Math.Precision.quarticConvergence- convergenceOrder ContinuedFraction = Math.Precision.linearConvergence- convergenceOrder HalleysMethod = Math.Precision.cubicConvergence- convergenceOrder NewtonRaphsonIteration = Math.Precision.quadraticConvergence- convergenceOrder (TaylorSeries terms) = terms -- The order of convergence, per iteration, equals the number of terms in the series on each iteration.--{- |- * Uses /continued-fractions/, to iterate towards the principal /square-root/ of the specified positive integer;- <http://en.wikipedia.org/wiki/Solving_quadratic_equations_with_continued_fractions>,- <http://en.wikipedia.org/wiki/Generalized_continued_fraction#Roots_of_positive_numbers>,- <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion>.- <http://www.myreckonings.com/Dead_Reckoning/Online/Materials/General%20Method%20for%20Extracting%20Roots.pdf>-- * The convergence <http://en.wikipedia.org/wiki/Rate_of_convergence> of the /continued-fraction/ is merely /1st order/ (linear).--}-squareRootByContinuedFraction :: Real operand => ProblemSpecification operand-squareRootByContinuedFraction (initialEstimate, initialDecimalDigits) requiredDecimalDigits y = initialEstimate + (convergents initialEstimate !! Math.Precision.getTermsRequired (10 ^^ negate initialDecimalDigits) requiredDecimalDigits) where- convergents :: Math.SquareRoot.Result -> [Math.SquareRoot.Result]- convergents x = iterate ((Math.SquareRoot.getDiscrepancy y x /) . ((2 * x) +)) 0--{- |- * The constant coefficients of the /Taylor-series/ for a /square-root/; <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.-- * @ ((-1)^n * factorial(2*n)) / ((1 - 2*n) * 4^n * factorial(n^2)) @.--}-taylorSeriesCoefficients :: Fractional f => [f]-taylorSeriesCoefficients = zipWith (- \powers n -> let- doubleN = 2 * n- product' = Data.PrimeFactors.product' (recip 2) {-arbitrary-} 10 {-arbitrary-}- in uncurry (/) . (- fromIntegral . product' *** fromIntegral . (* ((1 - doubleN) * powers)) . product'- ) $ Math.Implementations.Factorial.primeFactors doubleN >/< Math.Implementations.Factorial.primeFactors n >^ 2- ) (- iterate (* negate 4) 1 -- (-4)^n- ) [0 :: Integer ..] -- n--{- |- * Returns the /Taylor-series/ for the /square-root/ of the specified value, to any requested number of terms.-- * <http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Taylor_series>.-- * The convergence of the series is merely /linear/,- in that each term increases the precision, by a constant number of decimal places, equal to the those in the original estimate.-- * By feeding-back the improved estimate, to form a new series, the order of convergence, on each successive iteration,- becomes proportional to the number of terms;--> Terms Convergence-> ===== ===========-> 2 terms /quadratic/-> 3 terms /cubic/--}-squareRootByTaylorSeries :: Real operand- => Terms -- ^ The number of terms of the infinite series, to evaluate.- -> operand -- ^ The value for which the /square-root/ is required.- -> Math.SquareRoot.Result -- ^ An initial estimate.- -> Math.SquareRoot.Result-squareRootByTaylorSeries _ _ 0 = error "Factory.Math.Implementations.SquareRoot.squareRootByTaylorSeries:\talgorithm can't cope with estimated value of zero."-squareRootByTaylorSeries terms y x- | terms < 2 = error $ "Factory.Math.Implementations.SquareRoot.squareRootByTaylorSeries:\tinvalid number of terms; " ++ show terms- | otherwise = Math.Summation.sumR' . take terms . zipWith (*) taylorSeriesCoefficients $ iterate (* relativeError) x- where- relativeError :: Math.SquareRoot.Result- relativeError = pred $ toRational y / Math.Power.square x -- Pedantically, this is the error in y, which is twice the magnitude of the error in x.---- | Iterates from the estimated value, towards the /square-root/, a sufficient number of times to achieve the required accuracy.-squareRootByIteration :: Real operand => Algorithm -> ProblemSpecification operand-squareRootByIteration algorithm (initialEstimate, initialDecimalDigits) requiredDecimalDigits y = iterate (Math.SquareRoot.step algorithm y) initialEstimate !! Math.Precision.getIterationsRequired (Math.SquareRoot.convergenceOrder algorithm) initialDecimalDigits requiredDecimalDigits-
− src/Factory/Math/MultiplicativeOrder.hs
@@ -1,66 +0,0 @@-{-- Copyright (C) 2011 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@] Exports the /Multiplicative Order/ of an integer, in a specific /modular/ arithmetic.---}--module Factory.Math.MultiplicativeOrder(--- * Functions- multiplicativeOrder-) where--import qualified Control.DeepSeq-import qualified Factory.Data.Exponential as Data.Exponential-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Primality as Math.Primality-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation--{- |- * The smallest positive integral power to which the specified integral base must be raised,- to be congruent with one, in the specified /modular/ arithmetic.-- * Based on <http://rosettacode.org/wiki/Multiplicative_order#Haskell>.-- * <http://en.wikipedia.org/wiki/Multiplicative_order>.-- * <http://mathworld.wolfram.com/MultiplicativeOrder.html>.--}-multiplicativeOrder :: (Math.PrimeFactorisation.Algorithmic primeFactorisationAlgorithm, Control.DeepSeq.NFData i, Integral i, Show i)- => primeFactorisationAlgorithm- -> i -- ^ Base.- -> i -- ^ Modulus.- -> i -- ^ Result.-multiplicativeOrder primeFactorisationAlgorithm base modulus- | modulus < 2 = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\tinvalid modulus; " ++ show modulus- | not $ Math.Primality.areCoprime base modulus = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\targuments aren't coprime; " ++ show (base, modulus)- | otherwise = foldr (lcm . multiplicativeOrder') 1 $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm modulus -- Combine the /multiplicative order/ of the constituent /prime-factors/.- where--- multiplicativeOrder' :: (Control.DeepSeq.NFData i, Integral i) => Data.Exponential.Exponential i -> i- multiplicativeOrder' e = product . map (- \e' -> let- d :: Int- d = length . takeWhile (/= 1) . iterate (- \y -> Math.Power.raiseModulo y (Data.Exponential.getBase e') pk- ) $ Math.Power.raiseModulo base (totient `div` Data.Exponential.evaluate e') pk- in Data.Exponential.getBase e' ^ d- ) $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm totient where- pk = Data.Exponential.evaluate e- totient = Math.PrimeFactorisation.primePowerTotient e-
− src/Factory/Math/PerfectPower.hs
@@ -1,100 +0,0 @@-{-- Copyright (C) 2011 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@] Exports functions related to /perfect powers/.--}--module Factory.Math.PerfectPower(--- * Functions- maybeSquareNumber,--- ** Predicates- isPerfectPower--- isPerfectPowerInt-) where--import qualified Data.IntSet-import qualified Data.Set-import qualified Factory.Math.Power as Math.Power--{- |- * Returns @(Just . sqrt)@ if the specified integer is a /square number/ (AKA /perfect square/).-- * <http://en.wikipedia.org/wiki/Square_number>.-- * <http://mathworld.wolfram.com/SquareNumber.html>.-- * @(Math.Power.square . sqrt)@ is expensive, so the modulus of the operand is tested first, in an attempt to prove it isn't a /perfect square/.- The set of tests, and the valid moduli within each test, are ordered to maximize the rate of failure-detection.--}-maybeSquareNumber :: Integral i => i -> Maybe i-maybeSquareNumber i--- | i < 0 = Nothing -- This function is performance-sensitive, but this test is neither strictly nor frequently required.- | all (\(modulus, valid) -> rem i modulus `elem` valid) [--- -- Distribution of moduli amongst perfect squares Cumulative failure-detection.- (16, [0,1,4,9]), -- All moduli are equally likely. 75%- (9, [0,1,4,7]), -- Zero occurs 33%, the others only 22%. 88%- (17, [1,2,4,8,9,13,15,16,0]), -- Zero only occurs 5.8%, the others 11.8%. 94%--- These additional tests, aren't always cost-effective.- (13, [1,3,4,9,10,12,0]), -- Zero only occurs 7.7%, the others 15.4%. 97%- (7, [1,2,4,0]), -- Zero only occurs 14.3%, the others 28.6%. 98%- (5, [1,4,0]) -- Zero only occurs 20%, the others 40%. 99%---- ] && fromIntegral iSqrt == sqrt' = Just iSqrt -- CAVEAT: erroneously True for 187598574531033120 (187598574531033121 is square).- ] && Math.Power.square iSqrt == i = Just iSqrt- | otherwise = Nothing- where- sqrt' :: Double- sqrt' = sqrt $ fromIntegral i-- iSqrt = round sqrt'--{- |- * An integer @(> 1)@ which can be expressed as an integral power @(> 1)@ of a smaller /natural/ number.-- * CAVEAT: /zero/ and /one/ are normally excluded from this set.-- * <http://en.wikipedia.org/wiki/Perfect_power>.-- * <http://mathworld.wolfram.com/PerfectPower.html>.-- * A generalisation of the concept of /perfect squares/, in which only the exponent '2' is significant.--}-isPerfectPower :: Integral i => i -> Bool-isPerfectPower i- | i < Math.Power.square 2 = False- | otherwise = i `Data.Set.member` foldr (- \n set -> if n `Data.Set.member` set- then set--- else Data.Set.union set . Data.Set.fromDistinctAscList . takeWhile (<= i) . iterate (* n) $ Math.Power.square n- else foldr Data.Set.insert set . takeWhile (<= i) . iterate (* n) $ Math.Power.square n -- Faster.- ) Data.Set.empty [2 .. round $ sqrt (fromIntegral i :: Double)]--{-# NOINLINE isPerfectPower #-}-{-# RULES "isPerfectPower/Int" isPerfectPower = isPerfectPowerInt #-}---- | A specialisation of 'isPerfectPower'.-isPerfectPowerInt :: Int -> Bool-isPerfectPowerInt i- | i < Math.Power.square 2 = False- | otherwise = i `Data.IntSet.member` foldr (- \n set -> if n `Data.IntSet.member` set- then set- else foldr Data.IntSet.insert set . takeWhile (<= i) . iterate (* n) $ Math.Power.square n- ) Data.IntSet.empty [2 .. round $ sqrt (fromIntegral i :: Double)]-
− src/Factory/Math/Pi.hs
@@ -1,100 +0,0 @@-{-- Copyright (C) 2011 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 the classes of /Pi/-algorithm which have been implemented.--}--module Factory.Math.Pi(--- * Type-classes- Algorithmic(..),--- * Types--- ** Data-types- Category(..)-) where--import qualified Factory.Math.Precision as Math.Precision-import qualified ToolShed.Defaultable--{- |- * Defines the methods expected of a /Pi/-algorithm.-- * Most of the implementations naturally return a 'Rational', but the spigot-algorithms naturally produce a @[Int]@;- though representing /Pi/ as a big integer with the decimal point removed is clearly incorrect.-- * Since representing /Pi/ as either a 'Rational' or promoted to an 'Integer', is inconvenient, an alternative decimal 'String'-representation is provided.--}-class Algorithmic algorithm where- openR :: algorithm -> Math.Precision.DecimalDigits -> Rational -- ^ Returns the value of /Pi/ as a 'Rational'.-- openI :: algorithm -> Math.Precision.DecimalDigits -> Integer -- ^ Returns the value of /Pi/, promoted by the required precision to form an integer.- openI _ 1 = 3- openI algorithm decimalDigits- | decimalDigits <= 0 = error $ "Factory.Math.Pi.openI:\tinsufficient decimalDigits=" ++ show decimalDigits- | otherwise = round . Math.Precision.promote (openR algorithm decimalDigits) $ pred decimalDigits-- openS :: algorithm -> Math.Precision.DecimalDigits -> String -- ^ Returns the value of /Pi/ as a decimal 'String'.- openS _ 1 = "3"- openS algorithm decimalDigits- | decimalDigits <= 0 = ""- | decimalDigits <= 16 = take (succ decimalDigits) $ show (pi :: Double)- | otherwise = "3." ++ tail (show $ openI algorithm decimalDigits) -- Insert a decimal point.---- | Categorises the various algorithms.-data Category agm bbp borwein ramanujan spigot- = AGM agm -- ^ Algorithms based on the /Arithmetic-geometric Mean/.- | BBP bbp -- ^ <http://en.wikipedia.org/wiki/Bailey%E2%80%93Borwein%E2%80%93Plouffe_formula>.- | Borwein borwein -- ^ <http://en.wikipedia.org/wiki/Borwein%27s_algorithm>.- | Ramanujan ramanujan -- ^ <http://www.pi314.net/eng/ramanujan.php>.- | Spigot spigot -- ^ Algorithms from which the digits of /Pi/ slowly drip, one by one.- deriving (Eq, Read, Show)--instance (- ToolShed.Defaultable.Defaultable agm,- ToolShed.Defaultable.Defaultable bbp,- ToolShed.Defaultable.Defaultable borwein,- ToolShed.Defaultable.Defaultable ramanujan,- ToolShed.Defaultable.Defaultable spigot- ) => ToolShed.Defaultable.Defaultable (Category agm bbp borwein ramanujan spigot) where- defaultValue = BBP ToolShed.Defaultable.defaultValue--instance (- Algorithmic agm,- Algorithmic bbp,- Algorithmic borwein,- Algorithmic ramanujan,- Algorithmic spigot- ) => Algorithmic (Category agm bbp borwein ramanujan spigot) where- openR algorithm decimalDigits- | decimalDigits <= 0 = error $ "Factory.Math.Pi.openR:\tinsufficient decimalDigits=" ++ show decimalDigits- | decimalDigits <= 16 = Math.Precision.simplify (pred decimalDigits) (pi :: Double)- | otherwise = (- case algorithm of- AGM agm -> openR agm- BBP bbp -> openR bbp- Borwein borwein -> openR borwein- Ramanujan ramanujan -> openR ramanujan- Spigot spigot -> openR spigot- ) decimalDigits-- openI _ 1 = 3- openI (Spigot spigot) decimalDigits = openI spigot decimalDigits- openI algorithm decimalDigits- | decimalDigits <= 0 = error $ "Factory.Math.Pi.openI:\tinsufficient decimalDigits=" ++ show decimalDigits- | otherwise = round . Math.Precision.promote (openR algorithm decimalDigits) $ pred decimalDigits-
− src/Factory/Math/Power.hs
@@ -1,84 +0,0 @@-{-- Copyright (C) 2011 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@] Exports functions involving integral powers.--}--module Factory.Math.Power(--- * Functions- square,- squaresFrom,- cube,- cubeRoot,- raiseModulo-) where---- | Mainly for convenience.-square :: Num n => n -> n-square x = x ^ (2 :: Int) -- CAVEAT: this could be eta-reduced, but it won't then inline when called with a single argument.--{-# INLINE square #-}---- | Just for convenience.-cube :: Num n => n -> n-cube = (^ (3 :: Int))--{- |- * Iteratively generate sequential /squares/, from the specified initial value,- based on the fact that @(x + 1)^2 = x^2 + 2 * x + 1@.-- * The initial value doesn't need to be either positive or integral.--}-squaresFrom :: (Enum n, Num n)- => n -- ^ Lower bound.- -> [(n, n)] -- ^ @ [(n, n^2)] @.-squaresFrom from = iterate (\(x, y) -> (succ x, succ $ y + 2 * x)) (from, square from)---- | Just for convenience.-cubeRoot :: Double -> Double-cubeRoot = (** recip 3)--{- |- * Raise an arbitrary number to the specified positive integral power, using /modular/ arithmetic.-- * Implements exponentiation as a sequence of either /squares/ or multiplications by the base;- <http://en.wikipedia.org/wiki/Exponentiation_by_squaring>.-- * <http://en.wikipedia.org/wiki/Modular_exponentiation>.--}-raiseModulo :: (Integral i, Integral power, Show power)- => i -- ^ Base.- -> power- -> i -- ^ Modulus.- -> i -- ^ Result.-raiseModulo _ _ 0 = error "Factory.Math.Power.raiseModulo:\tzero modulus."-raiseModulo _ _ 1 = 0-raiseModulo _ 0 modulus = 1 `mod` modulus-raiseModulo base power modulus- | base < 0 = (`mod` modulus) . (if even power then id else negate) $ raiseModulo (negate base) power modulus -- Recurse.- | power < 0 = error $ "Factory.Math.Power.raiseModulo:\tnegative power; " ++ show power- | first `elem` [0, 1] = first- | otherwise = slave power- where- first = base `mod` modulus-- slave 1 = first- slave e = (`mod` modulus) . (if r == 0 {-even-} then id else (* base)) . square $ slave q {-recurse-} where- (q, r) = e `quotRem` 2-
− src/Factory/Math/Precision.hs
@@ -1,125 +0,0 @@-{-- Copyright (C) 2011 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 the unit with which precision is measured, and operations on it.--}-module Factory.Math.Precision(--- * Types--- ** Type-synonyms- ConvergenceOrder,- ConvergenceRate,- DecimalDigits,--- * Constants- linearConvergence,- quadraticConvergence,- cubicConvergence,- quarticConvergence,--- * Functions- getIterationsRequired,- getTermsRequired,- roundTo,- promote,- simplify-) where--import qualified Data.Ratio---- | The /order of convergence/; <http://en.wikipedia.org/wiki/Rate_of_convergence>.-type ConvergenceOrder = Int---- | The /rate of convergence/; <http://en.wikipedia.org/wiki/Rate_of_convergence>.-type ConvergenceRate = Double---- | A number of decimal digits; presumably positive.-type DecimalDigits = Int---- | /Linear/ convergence-rate; which may be qualified by the /rate of convergence/.-linearConvergence :: ConvergenceOrder-linearConvergence = 1---- | /Quadratic/ convergence-rate.-quadraticConvergence :: ConvergenceOrder-quadraticConvergence = 2---- | /Cubic/ convergence-rate.-cubicConvergence :: ConvergenceOrder-cubicConvergence = 3---- | /Quartic/ convergence-rate.-quarticConvergence :: ConvergenceOrder-quarticConvergence = 4---- | The predicted number of iterations, required to achieve a specific accuracy, at a given /order of convergence/.-getIterationsRequired :: Integral i- => ConvergenceOrder- -> DecimalDigits -- ^ The precision of the initial estimate.- -> DecimalDigits -- ^ The required precision.- -> i-getIterationsRequired convergenceOrder initialDecimalDigits requiredDecimalDigits- | initialDecimalDigits <= 0 = error $ "Factory.Math.Precision.getIterationsRequired:\tinsufficient 'initialDecimalDigits'; " ++ show initialDecimalDigits- | precisionRatio <= 1 = 0- | otherwise = ceiling $ fromIntegral convergenceOrder `logBase` precisionRatio- where- precisionRatio :: Double- precisionRatio = fromIntegral requiredDecimalDigits / fromIntegral initialDecimalDigits--{- |- * The predicted number of terms which must be extracted from a series,- if it is to converge to the required accuracy,- at the specified linear /convergence-rate/.-- * The /convergence-rate/ of a series, is the error in the series after summation of @(n+1)th@ terms,- divided by the error after only @n@ terms, as the latter tends to infinity.- As such, for a /convergent/ series (in which the error get smaller with successive terms), it's value lies in the range @0 .. 1@.-- * <http://en.wikipedia.org/wiki/Rate_of_convergence>.--}-getTermsRequired :: Integral i- => ConvergenceRate- -> DecimalDigits -- ^ The additional number of correct decimal digits.- -> i-getTermsRequired _ 0 = 0-getTermsRequired convergenceRate requiredDecimalDigits- | convergenceRate <= 0 || convergenceRate >= 1 = error $ "Factory.Math.Precision.getTermsRequired:\t(0 < convergence-rate < 1); " ++ show convergenceRate- | requiredDecimalDigits < 0 = error $ "Factory.Math.Precision.getTermsRequired:\t'requiredDecimalDigits' must be positive; " ++ show requiredDecimalDigits- | otherwise = ceiling $ fromIntegral requiredDecimalDigits / negate (logBase 10 convergenceRate)---- | Rounds the specified number, to a positive number of 'DecimalDigits'.-roundTo :: (RealFrac a, Fractional f) => DecimalDigits -> a -> f-roundTo decimals = (/ fromInteger promotionFactor) . fromInteger . round . (* fromInteger promotionFactor) where- promotionFactor :: Integer- promotionFactor = 10 ^ decimals---- | Promotes the specified number, by a positive number of 'DecimalDigits'.-promote :: Num n => n -> DecimalDigits -> n-promote x = (* x) . (10 ^)--{- |- * Reduces a 'Rational' to the minimal form required for the specified number of /fractional/ decimal places;- irrespective of the number of integral decimal places.-- * A 'Rational' approximation to an irrational number, may be very long, and provide an unknown excess precision.- Whilst this doesn't sound harmful, it costs in performance and memory-requirement, and being unpredictable isn't actually useful.--}-simplify :: RealFrac operand- => DecimalDigits -- ^ The number of places after the decimal point, which are required.- -> operand- -> Rational-simplify decimalDigits operand = Data.Ratio.approxRational operand . recip $ 4 * 10 ^ succ decimalDigits -- Tolerate any error less than half the least significant digit required.-
− src/Factory/Math/Primality.hs
@@ -1,102 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Exports a common interface for primality-implementations.-- * Provides utilities for these implementations.--}--module Factory.Math.Primality(--- * Type-classes- Algorithmic(..),--- * Functions- carmichaelNumbers,--- ** Predicates- areCoprime,- isFermatWitness,- isCarmichaelNumber-) where--import qualified Control.DeepSeq-import qualified Factory.Math.Power as Math.Power---- | Defines the methods expected of a primality-testing algorithm.-class Algorithmic algorithm where- isPrime :: (Control.DeepSeq.NFData i, Integral i, Show i) => algorithm -> i -> Bool--{- |- 'True' if the two specified integers are /relatively prime/,- i.e. if they share no common positive factors except one.-- * @1@ and @-1@ are the only numbers which are /coprime/ to themself.-- * <http://en.wikipedia.org/wiki/Coprime>.-- * <http://mathworld.wolfram.com/RelativelyPrime.html>.--}-areCoprime :: Integral i => i -> i -> Bool-areCoprime i = (== 1) . gcd i--{- |- * Tests /Fermat's Little Theorem/ for all applicable values, as a probabilistic primality-test.-- * <http://en.wikipedia.org/wiki/Fermat%27s_little_theorem>.-- * <http://en.wikipedia.org/wiki/Fermat_primality_test>.-- * <http://en.wikipedia.org/wiki/Fermat_pseudoprime>.-- * CAVEAT: this primality-test fails for the /Carmichael numbers/.-- * TODO: confirm that all values must be tested.--}-isFermatWitness :: (Integral i, Show i) => i -> Bool-isFermatWitness i = not . all isFermatPseudoPrime $ filter (areCoprime i) [2 .. pred i] where- isFermatPseudoPrime base = Math.Power.raiseModulo base (pred i) i == 1 -- CAVEAT: a /Fermat Pseudo-prime/ must also be a /composite/ number.--{- |- * A /Carmichael number/ is an /odd/ /composite/ number which satisfies /Fermat's little theorem/.-- * <http://en.wikipedia.org/wiki/Carmichael_number>.-- * <http://mathworld.wolfram.com/CarmichaelNumber.html>.--}-isCarmichaelNumber :: (- Algorithmic algorithm,- Control.DeepSeq.NFData i,- Integral i,- Show i- ) => algorithm -> i -> Bool-isCarmichaelNumber algorithm i = not $ or [- i <= 2,- even i,- isFermatWitness i,- isPrime algorithm i- ]---- | An ordered list of the /Carmichael/ numbers; <http://en.wikipedia.org/wiki/Carmichael_number>.-carmichaelNumbers :: (- Algorithmic algorithm,- Control.DeepSeq.NFData i,- Integral i,- Show i- ) => algorithm -> [i]-carmichaelNumbers algorithm = isCarmichaelNumber algorithm `filter` [3, 5 ..]
− src/Factory/Math/PrimeFactorisation.hs
@@ -1,151 +0,0 @@-{-- Copyright (C) 2011 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@]-- * <http://en.wikipedia.org/wiki/Integer_factorization>.-- * Exports a common interface to permit decomposition of positive integers,- into the unique combination of /prime/-factors known to exist according to the /Fundamental Theorem of Arithmetic/; <http://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic>.-- * Leveraging this abstract capability, it derives the /smoothness/, /power-smoothness/, /omega/-numbers and /square-free/ integers.-- * Filters the list of /regular-numbers/ from the list of /smoothness/.-- * CAVEAT: to avoid wasting time, it may be advantageous to check /Factory.Math.Primality.isPrime/ first.--}--module Factory.Math.PrimeFactorisation(--- * Type-classes- Algorithmic(..),--- * Functions- maxBoundPrimeFactor,- smoothness,- powerSmoothness,- regularNumbers,- primePowerTotient,- eulersTotient,- omega,- squareFree-) where--import qualified Control.DeepSeq-import qualified Data.List-import qualified Factory.Data.Exponential as Data.Exponential-import qualified Factory.Data.PrimeFactors as Data.PrimeFactors---- | Defines the methods expected of a /factorisation/-algorithm.-class Algorithmic algorithm where- primeFactors :: (Control.DeepSeq.NFData base, Integral base)- => algorithm- -> base -- ^ The operand- -> Data.PrimeFactors.Factors base Int {-arbitrarily-}--{- |- * The upper limit for a prime to be considered as a candidate factor of the specified number.-- * One might naively think that this limit is @(x `div` 2)@ for an even number,- but though a prime-factor /greater/ than the /square-root/ of the number can exist,- its smaller /cofactor/ decomposes to a prime which must be less than the /square-root/.-- * NB: rather then using @(primeFactor <= sqrt numerator)@ to filter the candidate prime-factors of a given numerator,- one can alternatively use @(numerator >= primeFactor ^ 2)@ to filter what can potentially be factored by a given prime-factor.-- * CAVEAT: suffers from rounding-errors, though no consequence has been witnessed.--}-maxBoundPrimeFactor :: Integral i => i -> i-maxBoundPrimeFactor = floor . (sqrt :: Double -> Double) . fromIntegral--{- |- * A constant, zero-indexed, conceptually infinite, list, of the /smooth/ness of all positive integers.-- * <http://en.wikipedia.org/wiki/Smooth_number>.-- * <http://mathworld.wolfram.com/SmoothNumber.html>.--}-smoothness :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]-smoothness algorithm = 0 : map (Data.Exponential.getBase . last . primeFactors algorithm) [1 ..]--{- |- * A constant, zero-indexed, conceptually infinite, list of the /power-smooth/ness of all positive integers.-- * <http://en.wikipedia.org/wiki/Smooth_number#Powersmooth_numbers>.--}-powerSmoothness :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]-powerSmoothness algorithm = 0 : map (maximum . map Data.Exponential.evaluate . primeFactors algorithm) [1 ..]--{- |- * Filters 'smoothness', to derive the constant list of /Hamming-numbers/.-- * <http://en.wikipedia.org/wiki/Regular_number>.--}-regularNumbers :: (Algorithmic algorithm, Control.DeepSeq.NFData base, Integral base) => algorithm -> [base]-regularNumbers algorithm = map fst . filter ((<= (5 :: Integer)) . snd) . zip [1 ..] . tail $ smoothness algorithm--{- |- * /Euler's Totient/ for a /power/ of a /prime/-number.-- * By /Olofsson/; @(phi(n^k) = n^(k - 1) * phi(n))@- and since @(phi(prime) = prime - 1)@-- * CAVEAT: checks neither the primality nor the bounds of the specified value; therefore for internal use only.--}-primePowerTotient :: (Integral base, Integral exponent) => Data.Exponential.Exponential base exponent -> base-primePowerTotient (base, exponent') = pred base * base ^ pred exponent'--{- |- * The number of /coprimes/ less than or equal to the specified positive integer.-- * <http://en.wikipedia.org/wiki/Euler%27s_totient_function>.-- * <http://mathworld.wolfram.com/TotientFunction.html>.-- * AKA /EulerPhi/.--}-eulersTotient :: (- Algorithmic algorithm,- Control.DeepSeq.NFData i,- Integral i,- Show i- ) => algorithm -> i -> i-eulersTotient _ 1 = 1-eulersTotient algorithm i- | i <= 0 = error $ "Factory.Math.PrimeFactorisation.eulersTotient:\tundefined for; " ++ show i- | otherwise = product . map primePowerTotient $ primeFactors algorithm i--{- |- * A constant, zero-indexed, conceptually infinite, list of the /small omega/ numbers (i.e. the number of /distinct/ prime factors); cf. /big omega/.-- * <http://oeis.org/wiki/Omega%28n%29,_number_of_distinct_primes_dividing_n>.-- * <http://mathworld.wolfram.com/DistinctPrimeFactors.html>-- * <http://planetmath.org/encyclopedia/NumberOfDistinctPrimeFactorsFunction.html>.--}-omega :: (Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i) => algorithm -> [i]-omega algorithm = map (Data.List.genericLength . primeFactors algorithm) [0 :: Integer ..]--{- |- * A constant, conceptually infinite, list of the /square-free/ numbers, i.e. those which aren't divisible by any /perfect square/.-- * <http://en.wikipedia.org/wiki/Square-free_integer>.--}-squareFree :: (Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i) => algorithm -> [i]-squareFree algorithm = filter (all (== 1) . map Data.Exponential.getExponent . primeFactors algorithm) [1 ..]-
− src/Factory/Math/Primes.hs
@@ -1,64 +0,0 @@-{-- Copyright (C) 2011 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@] Exports a common interface for implementations of /prime-number/ generators.--}--module Factory.Math.Primes(--- * Types-classes- Algorithmic(..),--- * Functions- primorial,- mersenneNumbers-) where--import qualified Control.DeepSeq-import qualified Data.Array.IArray---- | Defines the methods expected of a /prime-number/ generator.-class Algorithmic algorithm where- primes :: (Control.DeepSeq.NFData i, Data.Array.IArray.Ix i, Integral i) => algorithm -> [i] -- ^ Returns the constant, infinite, list of primes.--{- |- * Returns the constant list, defining the /Primorial/.-- * <http://en.wikipedia.org/wiki/Primorial>.-- * <http://mathworld.wolfram.com/Primorial.html>.--}-primorial :: (- Algorithmic algorithm,- Control.DeepSeq.NFData i,- Data.Array.IArray.Ix i,- Integral i- ) => algorithm -> [i]-primorial = scanl (*) 1 . primes--{- |- * Returns the constant ordered infinite list of /Mersenne numbers/.-- * Only the subset composed from a prime exponent is returned; which is a strict superset of the /Mersenne Primes/.-- * <http://en.wikipedia.org/wiki/Mersenne_prime>.-- * <http://mathworld.wolfram.com/MersenneNumber.html>--}-mersenneNumbers :: (Algorithmic algorithm, Integral i) => algorithm -> [i]-mersenneNumbers algorithm = map (pred . (2 ^)) (primes algorithm :: [Int]) -- Whilst the exponentiation could be parallelised, not all values are known to be required.-
− src/Factory/Math/Probability.hs
@@ -1,255 +0,0 @@-{-- 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@] Functions for probability-distributions.-- [@CAVEAT@] Because data-constructors are exposed, 'ToolShed.SelfValidate.isValid' need not be called.--}--module Factory.Math.Probability(--- * Type-classes- Distribution(..),--- * Types--- ** Data-types- ContinuousDistribution(..),- DiscreteDistribution(..),--- * Functions- maxPreciseInteger,--- minPositiveFloat,- boxMullerTransform,--- reProfile,- generateStandardizedNormalDistribution,- generateContinuousPopulation,--- generatePoissonDistribution,- generateDiscretePopulation-) where--import qualified Control.Arrow-import Control.Arrow((***), (&&&))-import qualified Factory.Data.Interval as Data.Interval-import qualified Factory.Math.Power as Math.Power-import qualified System.Random-import qualified ToolShed.Data.List-import qualified ToolShed.Data.Pair-import qualified ToolShed.SelfValidate---- | The maximum integer which can be accurately represented as a Double.-maxPreciseInteger :: RealFloat a => a -> Integer-maxPreciseInteger = (2 ^) . floatDigits--{- |- * Determines the minimum positive floating-point number, which can be represented by using the parameter's type.-- * Only the type of the parameter is relevant, not its value.--}-minPositiveFloat :: RealFloat a => a -> a-minPositiveFloat = encodeFloat 1 . uncurry (-) . (fst . floatRange &&& floatDigits)---- | Describes /continuous probability-distributions/; <http://en.wikipedia.org/wiki/List_of_probability_distributions#Continuous_distributions>.-data ContinuousDistribution parameter- = ExponentialDistribution parameter {-lambda-} -- ^ Defines an /Exponential/-distribution with a particular /lambda/; <http://en.wikipedia.org/wiki/Exponential_distribution>.- | LogNormalDistribution parameter {-location-} parameter {-scale2-} -- ^ Defines a distribution whose logarithm is normally distributed with a particular /mean/ & /variance/; <http://en.wikipedia.org/wiki/Lognormal>.- | NormalDistribution parameter {-mean-} parameter {-variance-} -- ^ Defines a /Normal/-distribution with a particular /mean/ & /variance/; <http://en.wikipedia.org/wiki/Normal_distribution>.- | UniformDistribution (Data.Interval.Interval parameter) -- ^ Defines a /Uniform/-distribution within a /closed interval/; <http://en.wikipedia.org/wiki/Uniform_distribution>.- deriving (Eq, Read, Show)--instance (Floating parameter, Ord parameter, Show parameter) => ToolShed.SelfValidate.SelfValidator (ContinuousDistribution parameter) where- getErrors probabilityDistribution = ToolShed.SelfValidate.extractErrors $ case probabilityDistribution of- ExponentialDistribution lambda -> [(lambda <= 0, "'lambda' must exceed zero; " ++ show probabilityDistribution ++ ".")]- LogNormalDistribution location scale2 -> let- maxParameter = log . fromInteger $ maxPreciseInteger (undefined :: Double)- in [- (scale2 <= 0, "'scale' must exceed zero; " ++ show probabilityDistribution ++ "."),- (location > maxParameter || scale2 > maxParameter, "loss of precision will result from either 'location' or 'scale^2' exceeding '" ++ show maxParameter ++ "'; " ++ show probabilityDistribution ++ ".")- ]- NormalDistribution _ variance -> [(variance <= 0, "variance must exceed zero; " ++ show probabilityDistribution ++ ".")]- UniformDistribution interval -> [(Data.Interval.isReversed interval, "reversed interval='" ++ show probabilityDistribution ++ "'.")]---- | Describes /discrete probability-distributions/; <http://en.wikipedia.org/wiki/List_of_probability_distributions#Discrete_distributions>.-data DiscreteDistribution parameter- = PoissonDistribution parameter {-lambda-} -- ^ Defines an /Poisson/-distribution with a particular /lambda/; <http://en.wikipedia.org/wiki/Poisson_distribution>.- | ShiftedGeometricDistribution parameter {-probability-} -- ^ Defines an /Geometric/-distribution with a particular probability of success; <http://en.wikipedia.org/wiki/Geometric_distribution>.- deriving (Eq, Read, Show)--instance (Num parameter, Ord parameter, Show parameter) => ToolShed.SelfValidate.SelfValidator (DiscreteDistribution parameter) where- getErrors probabilityDistribution = ToolShed.SelfValidate.extractErrors $ case probabilityDistribution of- PoissonDistribution lambda -> [(lambda <= 0, "'lambda' must exceed zero; " ++ show probabilityDistribution ++ ".")]- ShiftedGeometricDistribution probability -> [(any ($ probability) [(<= 0), (> 1)], "probability must be in the semi-closed unit-interval (0, 1]; " ++ show probabilityDistribution ++ ".")]---- | Defines a common interface for probability-distributions.-class Distribution probabilityDistribution where- generatePopulation- :: (Fractional sample, System.Random.RandomGen randomGen)- => probabilityDistribution- -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.- -> [sample] -- ^ CAVEAT: the integers generated for discrete distributions are represented by a fractional type; use 'generateDiscretePopulation' if this is a problem.-- getMean :: Fractional mean => probabilityDistribution -> mean -- ^ The theoretical mean.-- getStandardDeviation :: Floating standardDeviation => probabilityDistribution -> standardDeviation-- ^ The theoretical standard-deviation.- getStandardDeviation = sqrt . getVariance -- Default implementation.-- getVariance :: Floating variance => probabilityDistribution -> variance -- ^ The theoretical variance.- getVariance = Math.Power.square . getStandardDeviation -- Default implementation.--instance (RealFloat parameter, Show parameter, System.Random.Random parameter) => Distribution (ContinuousDistribution parameter) where- generatePopulation probabilityDistribution = map realToFrac {-parameter -> sample-} . generateContinuousPopulation probabilityDistribution-- getMean (ExponentialDistribution lambda) = realToFrac $ recip lambda- getMean (LogNormalDistribution location scale2) = realToFrac . exp . (+ location) $ scale2 / 2- getMean (NormalDistribution mean _) = realToFrac mean- getMean (UniformDistribution (minParameter, maxParameter)) = realToFrac $ (minParameter + maxParameter) / 2-- getVariance (ExponentialDistribution lambda) = realToFrac . recip $ Math.Power.square lambda- getVariance (LogNormalDistribution location scale2) = realToFrac $ (exp scale2 - 1) * exp (2 * location + scale2) -- NB: for standard-deviation == mean, use scale^2 == ln 2.- getVariance (NormalDistribution _ variance) = realToFrac variance- getVariance (UniformDistribution (minParameter, maxParameter)) = realToFrac $ Math.Power.square (maxParameter - minParameter) / 12--instance (RealFloat parameter, Show parameter, System.Random.Random parameter) => Distribution (DiscreteDistribution parameter) where- generatePopulation probabilityDistribution = map fromInteger . generateDiscretePopulation probabilityDistribution-- getMean (PoissonDistribution lambda) = realToFrac lambda- getMean (ShiftedGeometricDistribution probability) = realToFrac $ recip probability-- getVariance (PoissonDistribution lambda) = realToFrac lambda- getVariance (ShiftedGeometricDistribution probability) = realToFrac $ (1 - probability) / Math.Power.square probability--{- |- * Converts a pair of independent /uniformly distributed/ random numbers, within the /semi-closed unit interval/ /(0,1]/,- to a pair of independent /normally distributed/ random numbers, of standardized /mean/=0, and /variance/=1.-- * <http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>.--}-boxMullerTransform :: (- Floating f,- Ord f,- Show f- )- => (f, f) -- ^ Independent, /uniformly distributed/ random numbers, which must be within the /semi-closed unit interval/, /(0,1]/.- -> (f, f) -- ^ Independent, /normally distributed/ random numbers, with standardized /mean/=0 and /variance/=1.-boxMullerTransform cartesian- | not . uncurry (&&) $ ToolShed.Data.Pair.mirror inSemiClosedUnitInterval cartesian = error $ "Factory.Math.Probability.boxMullerTransform:\tspecified Cartesian coordinates, must be within semi-closed unit-interval (0, 1]; " ++ show cartesian- | otherwise = polarToCartesianTransform $ (sqrt . negate . (* 2) . log *** (* 2) . (* pi)) cartesian- where- inSemiClosedUnitInterval :: (Num n, Ord n) => n -> Bool- inSemiClosedUnitInterval = uncurry (&&) . ((> 0) &&& (<= 1))-- polarToCartesianTransform :: Floating f => (f, f) -> (f, f)- polarToCartesianTransform = uncurry (*) . Control.Arrow.second cos &&& uncurry (*) . Control.Arrow.second sin--{- |- * Uses the supplied random-number generator,- to generate a conceptually infinite list, of /normally distributed/ random numbers, with standardized /mean/=0, and /variance/=1.-- * <http://en.wikipedia.org/wiki/Normal_distribution>, <http://mathworld.wolfram.com/NormalDistribution.html>.--}-generateStandardizedNormalDistribution :: (- RealFloat f,- Show f,- System.Random.Random f,- System.Random.RandomGen randomGen- ) => randomGen -> [f]-generateStandardizedNormalDistribution = ToolShed.Data.List.linearise . uncurry (zipWith $ curry boxMullerTransform) . ToolShed.Data.Pair.mirror (- System.Random.randomRs (minPositiveFloat undefined, 1)- ) . System.Random.split---- | Stretches and shifts a /distribution/ to achieve the required /mean/ and /standard-deviation/.-reProfile :: (Distribution distribution, Floating n) => distribution -> [n] -> [n]-reProfile distribution = map ((+ getMean distribution) . (* getStandardDeviation distribution))---- | Uses the supplied random-number generator, to generate a conceptually infinite population, with the specified continuous probability-distribution.-generateContinuousPopulation :: (- RealFloat f,- Show f,- System.Random.Random f,- System.Random.RandomGen randomGen- )- => ContinuousDistribution f- -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.- -> [f]-generateContinuousPopulation probabilityDistribution randomGen- | not $ ToolShed.SelfValidate.isValid probabilityDistribution = error $ "Factory.Math.Probability.generateContinuousPopulation:\t" ++ ToolShed.SelfValidate.getFirstError probabilityDistribution- | otherwise = (- case probabilityDistribution of- ExponentialDistribution lambda -> let- quantile = (/ lambda) . negate . log . (1 -) -- <http://en.wikipedia.org/wiki/Quantile_function>.- in map quantile . System.Random.randomRs (0, 1)- LogNormalDistribution location scale2 -> map (- exp . (+ location) . (* sqrt scale2) -- Stretch the standard-deviation & re-locate the mean to that specified for the log-space, then return to the original coordinates.- ) . generateStandardizedNormalDistribution- NormalDistribution _ _ -> reProfile probabilityDistribution . generateStandardizedNormalDistribution- UniformDistribution interval -> System.Random.randomRs interval- ) randomGen--{- |- * Uses the supplied random-number generator,- to generate a conceptually infinite population, of random integers conforming to the /Poisson distribution/; <http://en.wikipedia.org/wiki/Poisson_distribution>.-- * CAVEAT:- uses an algorithm by Knuth, which having a /linear time-complexity/ in /lambda/, can be intolerably slow;- also, the term @exp $ negate lambda@, underflows for large /lambda/;- so for large /lambda/, this implementation returns the appropriate 'NormalDistribution'.--}-generatePoissonDistribution :: (- Integral sample,- RealFloat lambda,- Show lambda,- System.Random.Random lambda,- System.Random.RandomGen randomGen- )- => lambda -- ^ Defines the required approximate value of both /mean/ and /variance/.- -> randomGen- -> [sample]-generatePoissonDistribution lambda- | lambda <= 0 = error $ "Factory.Math.Probability.generatePoissonDistribution:\tlambda must exceed zero " ++ show lambda- | lambda > (- negate . log $ minPositiveFloat lambda -- Guard against underflow, in the user-defined type for lambda.- ) = filter (>= 0) . map round . (reProfile (PoissonDistribution lambda) :: [Double] -> [Double]) . generateStandardizedNormalDistribution- | otherwise = generator- where- generator = uncurry (:) . (- fst . head . dropWhile (- (> exp (negate lambda)) . snd -- CAVEAT: underflows if lambda > (103 :: Float, 745 :: Double).- ) . scanl (- \accumulator random -> succ *** (* random) $ accumulator- ) (negate 1, 1) . System.Random.randomRs (0, 1) *** generator {-recurse-}- ) . System.Random.split---- | Uses the supplied random-number generator, to generate a conceptually infinite population, with the specified discrete probability-distribution.-generateDiscretePopulation :: (- Integral sample,- Ord parameter,- RealFloat parameter,- Show parameter,- System.Random.Random parameter,- System.Random.RandomGen randomGen- )- => DiscreteDistribution parameter- -> randomGen -- ^ A generator of /uniformly distributed/ random numbers.- -> [sample]-generateDiscretePopulation probabilityDistribution randomGen- | not $ ToolShed.SelfValidate.isValid probabilityDistribution = error $ "Factory.Math.Probability.generateDiscretePopulation:\t" ++ ToolShed.SelfValidate.getFirstError probabilityDistribution- | otherwise = (- case probabilityDistribution of- PoissonDistribution lambda -> generatePoissonDistribution lambda- ShiftedGeometricDistribution probability- | probability == 1 -> const $ repeat 1 -- The first Bernoulli Trial is guaranteed to succeed.- | otherwise -> map ceiling {-minimum 1-} . (\x -> x :: [Rational]) . generatePopulation (ExponentialDistribution . negate $ log (1 - probability)) -- The geometric distribution is a discrete version of the exponential distribution.- ) randomGen-
− src/Factory/Math/Radix.hs
@@ -1,139 +0,0 @@-{-- Copyright (C) 2011 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@] Facilitates representation of 'Integral' values in alternative 'Integral' bases.--}--module Factory.Math.Radix(--- * Constants--- decodes,--- digits,--- encodes,--- * Functions- digitSum,- digitalRoot,- fromBase,- toBase-) where--import Data.Array.IArray((!))-import qualified Data.Array.IArray-import qualified Data.Char-import qualified Data.List-import qualified Data.Maybe---- | Characters used to represent the digits of numbers in @(-36 <= base <= 36)@.-digits :: String-digits = ['0' .. '9'] ++ ['a' .. 'z']---- | Constant random-access lookup for 'digits'.-encodes :: (Data.Array.IArray.Ix index, Integral index) => Data.Array.IArray.Array index Char-encodes = Data.Array.IArray.listArray (0, pred $ Data.List.genericLength digits) digits---- | Constant reverse-lookup for 'digits'.-decodes :: Integral i => [(Char, i)]-decodes = zip digits [0 ..]--{- |- * Convert the specified integral decimal quantity, to an alternative base, and represent the result as a 'String'.-- * Both negative decimals and negative bases are permissible.-- * The conversion to 'Char' can only succeed where printable and intelligible characters exist to represent all digits in the chosen base;- which in practice means @(-36 <= base <= 36)@.--}-toBase :: (- Data.Array.IArray.Ix decimal,- Integral base,- Integral decimal,- Show base,- Show decimal- ) => base -> decimal -> String-toBase 10 decimal = show decimal -- Base unchanged.-toBase _ 0 = "0" -- Zero has the same representation in any base.-toBase base decimal- | abs base < 2 = error $ "Factory.Math.Radix.toBase:\tan arbitrary integer can't be represented in base " ++ show base- | abs base > Data.List.genericLength digits = error $ "Factory.Math.Radix.toBase:\tunable to clearly represent the complete set of digits in base " ++ show base- | base > 0 && decimal < 0 = '-' : map toDigit (fromDecimal (negate decimal) [])- | otherwise = toDigit `map` fromDecimal decimal []- where- fromDecimal 0 = id- fromDecimal n- | remainder < 0 = fromDecimal (succ quotient) . ((remainder - fromIntegral base) :) -- This can only occur when base is negative; cf. 'divMod'.- | otherwise = fromDecimal quotient . (remainder :)- where- (quotient, remainder) = n `quotRem` fromIntegral base-- toDigit :: (Data.Array.IArray.Ix i, Integral i, Show i) => i -> Char- toDigit n- | n >&< encodes = encodes ! n- | otherwise = error $ "Factory.Math.Radix.toBase.toDigit:\tno suitable character-representation for integer " ++ show n- where- (>&<) :: (Data.Array.IArray.Ix i, Integral i) => i -> Data.Array.IArray.Array i Char -> Bool- index >&< array = ($ index) `all` [(>= lower), (<= upper)] where- (lower, upper) = Data.Array.IArray.bounds array--{- |- * Convert the 'String'-representation of a number in the specified base, to a decimal integer.-- * Both negative numbers and negative bases are permissible.--}-fromBase :: (- Integral base,- Integral decimal,- Read decimal,- Show base- ) => base -> String -> decimal-fromBase 10 s = read s -- Base unchanged.-fromBase _ "0" = 0 -- Zero has the same representation in any base.-fromBase base s- | abs base < 2 = error $ "Factory.Math.Radix.fromBase:\tan arbitrary integer can't be represented in base " ++ show base- | abs base > Data.List.genericLength digits = error $ "Factory.Math.Radix.fromBase:\tunable to clearly represent the complete set of digits in base " ++ show base- | base > 0 && head s == '-' = negate . fromBase base $ tail s -- Recurse.- | otherwise = Data.List.foldl' (\l -> ((l * fromIntegral base) +) . fromDigit) 0 s where- fromDigit :: Integral i => Char -> i- fromDigit c = case c `lookup` decodes of- Just i- | i >= abs (fromIntegral base) -> error $ "Factory.Math.Radix.fromBase.fromDigit:\tillegal char " ++ show c ++ ", for base " ++ show base- | otherwise -> i- _ -> error $ "Factory.Math.Radix.fromBase.fromDigit:\tunrecognised char " ++ show c--{- |- * <http://mathworld.wolfram.com/DigitSum.html>.-- * <http://en.wikipedia.org/wiki/Digit_sum>.--}-digitSum :: (- Data.Array.IArray.Ix decimal,- Integral base,- Integral decimal,- Show base,- Show decimal- ) => base -> decimal -> decimal-digitSum 10 = fromIntegral . foldr ((+) . Data.Char.digitToInt) 0 . show-digitSum base = sum . Data.Maybe.mapMaybe (`lookup` decodes) . toBase base---- | <http://en.wikipedia.org/wiki/Digital_root>.-digitalRoot :: (- Data.Array.IArray.Ix decimal,- Integral decimal,- Show decimal- ) => decimal -> decimal-digitalRoot = until (<= 9) (digitSum (10 :: Int))-
− src/Factory/Math/SquareRoot.hs
@@ -1,120 +0,0 @@-{-- Copyright (C) 2011 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@]-- * Exports a common interface for /square-root/ implementations.-- * Provides utilities for these implementations.--}--module Factory.Math.SquareRoot(--- * Type-classes- Algorithmic(..),- Iterator(..),--- * Types--- ** Type-synonyms- Result,- Estimate,--- * Functions- getAccuracy,- getDiscrepancy,- getEstimate,--- rSqrt,--- ** Predicates- isPrecise-) where--import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Precision as Math.Precision---- | The result-type; actually, only the concrete return-type of 'Math.Precision.simplify', stops it being a polymorphic instance of 'Fractional'.-type Result = Rational---- | Contains an estimate for the /square-root/ of a value, and its accuracy.-type Estimate = (Result, Math.Precision.DecimalDigits)---- | Defines the methods expected of a /square-root/ algorithm.-class Algorithmic algorithm where- squareRootFrom :: (Real operand, Show operand)- => algorithm- -> Estimate -- ^ An initial estimate from which to start.- -> Math.Precision.DecimalDigits -- ^ The required precision.- -> operand -- ^ The value for which to find the /square-root/.- -> Result -- ^ Returns an improved estimate of the /square-root/, found using the specified algorithm, accurate to at least the required number of decimal digits.-- squareRoot :: (Real operand, Show operand)- => algorithm- -> Math.Precision.DecimalDigits -- ^ The required precision.- -> operand -- ^ The value for which to find the /square-root/.- -> Result -- ^ Returns an estimate of the /square-root/, found using the specified algorithm, accurate to at least the required number of decimal digits.- squareRoot algorithm decimalDigits operand = squareRootFrom algorithm (getEstimate operand) decimalDigits operand -- Default implementation---- | The interface required to iterate, from an estimate of the required value, to the next approximation.-class Iterator algorithm where- step :: Real operand- => algorithm- -> operand -- ^ The value for which the /square-root/ is required; @y@.- -> Result -- ^ The current estimate; @x(n)@.- -> Result -- ^ An improved estimate; @x(n+1)@.-- convergenceOrder :: algorithm -> Math.Precision.ConvergenceOrder -- ^ The ultimate ratio of successive terms as the iteration converges.---- | Generalise 'sqrt' to operate on any 'Real' operand.-rSqrt :: Real operand => operand -> Double-rSqrt = sqrt . realToFrac---- | Uses 'Double'-precision floating-point arithmetic, to obtain an initial estimate for the /square-root/, and its accuracy.-getEstimate :: (Real operand, Show operand) => operand -> Estimate-getEstimate y- | y < 0 = error $ "Factory.Math.SquareRoot.getEstimate:\tthere's no real square-root of " ++ show y- | otherwise = (Math.Precision.simplify decimalDigits {-doubles performance by roughly length of the Rational representation-} . toRational $ rSqrt y, decimalDigits)- where- decimalDigits :: Math.Precision.DecimalDigits- decimalDigits = 16 -- <http://en.wikipedia.org/wiki/IEEE_floating_point>.--{- |- * The signed difference between the /square/ of an estimate for the /square-root/ of a value, and that value.-- * Positive when the estimate is too low.-- * CAVEAT: the magnitude is twice the error in the /square-root/.--}-getDiscrepancy :: Real operand => operand -> Result -> Result-getDiscrepancy y x = toRational y - Math.Power.square x---- | True if the specified estimate for the /square-root/, is precise.-isPrecise :: Real operand => operand -> Result -> Bool-isPrecise y x = getDiscrepancy y x == 0--{- |- * For a given value and an estimate of its /square-root/,- returns the number of decimals digits to which the /square-root/ is accurate; including the integral digits.-- * CAVEAT: the result returned for an exact match has been bodged.--}-getAccuracy :: Real operand => operand -> Result -> Math.Precision.DecimalDigits-getAccuracy y x- | absoluteError == 0 = maxBound -- Bodge.--- | otherwise = length . takeWhile (< 1) $ iterate (* 10) relativeError -- CAVEAT: too slow.- | otherwise = length $ show (round $ toRational y / absoluteError :: Integer)- where- absoluteError :: Result- absoluteError = abs (getDiscrepancy y x) / 2 -- NB: the magnitude of the error in 'y', is twice the error in its square-root, 'x'.-
− src/Factory/Math/Statistics.hs
@@ -1,181 +0,0 @@-{-- Copyright (C) 2011 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Miscellaneous statistics functions.--}--module Factory.Math.Statistics(--- * Functions- getMean,- getWeightedMean,--- getDispersionFromMean,- getVariance,- getStandardDeviation,- getAverageAbsoluteDeviation,- getCoefficientOfVariance,- nCr,- nPr-) where--import Control.Arrow((***))-import Control.Parallel(par, pseq)-import qualified Data.Foldable-import qualified Data.List-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Power as Math.Power--{- |- * Determines the /mean/ of the specified numbers; <http://en.wikipedia.org/wiki/Mean>.-- * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.--}-getMean :: (- Data.Foldable.Foldable foldable,- Fractional result,- Real value- )- => foldable value- -> result-getMean foldable- | denominator == 0 = error "Factory.Math.Statistics.getMean:\tno data => undefined result."- | otherwise = realToFrac numerator / fromIntegral denominator- where- (numerator, denominator) = Data.Foldable.foldr (\s -> (+ s) *** succ) (0, 0 :: Int) foldable--{- |- * Determines the /weighted mean/ of the specified numbers; <http://en.wikipedia.org/wiki/Weighted_arithmetic_mean>.-- * The specified value is only evaluated if the corresponding weight is non-zero.-- * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.--}-getWeightedMean :: (- Data.Foldable.Foldable foldable,- Fractional result,- Real value,- Real weight- )- => foldable (value, weight) -- ^ Each pair consists of a value & the corresponding weight.- -> result-getWeightedMean foldable- | denominator == 0 = error "Factory.Math.Statistics.getWeightedMean:\tzero weight => undefined result."- | otherwise = numerator / realToFrac denominator- where- (numerator, denominator) = Data.Foldable.foldr (- \(value, weight) -> if weight == 0- then id --Avoid unnecessarily evaluation.- else (+ realToFrac value * realToFrac weight) *** (+ weight)- ) (0, 0) foldable--{- |- * Measures the /dispersion/ of a /population/ of results from the /mean/ value; <http://en.wikipedia.org/wiki/Statistical_dispersion>.-- * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.--}-getDispersionFromMean :: (- Data.Foldable.Foldable foldable,- Fractional result,- Functor foldable,- Real value- ) => (Rational -> Rational) -> foldable value -> result-getDispersionFromMean weight foldable = getMean $ fmap (weight . (+ negate mean) . toRational) foldable where- mean :: Rational- mean = getMean foldable--{- |- * Determines the exact /variance/ of the specified numbers; <http://en.wikipedia.org/wiki/Variance>.-- * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.--}-getVariance :: (- Data.Foldable.Foldable foldable,- Fractional variance,- Functor foldable,- Real value- ) => foldable value -> variance-getVariance = getDispersionFromMean Math.Power.square---- | Determines the /standard-deviation/ of the specified numbers; <http://en.wikipedia.org/wiki/Standard_deviation>.-getStandardDeviation :: (- Data.Foldable.Foldable foldable,- Floating result,- Functor foldable,- Real value- ) => foldable value -> result-getStandardDeviation = sqrt . getVariance--{- |- * Determines the /average absolute deviation/ of the specified numbers; <http://en.wikipedia.org/wiki/Absolute_deviation#Average_absolute_deviation>.-- * Should the caller define the result-type as 'Rational', then it will be free from rounding-errors.--}-getAverageAbsoluteDeviation :: (- Data.Foldable.Foldable foldable,- Fractional result,- Functor foldable,- Real value- ) => foldable value -> result-getAverageAbsoluteDeviation = getDispersionFromMean abs---- | Determines the /coefficient-of-variance/ of the specified numbers; <http://en.wikipedia.org/wiki/Coefficient_of_variation>.-getCoefficientOfVariance :: (- Data.Foldable.Foldable foldable,- Eq result,- Floating result,- Functor foldable,- Real value- ) => foldable value -> result-getCoefficientOfVariance l- | mean == 0 = error "Factory.Math.Statistics.getCoefficientOfVariance:\tundefined if mean is zero."- | otherwise = getStandardDeviation l / abs mean- where- mean = getMean l---- | The number of unordered /combinations/ of /r/ objects taken from /n/; <http://en.wikipedia.org/wiki/Combination>.-nCr :: (Math.Factorial.Algorithmic factorialAlgorithm, Integral i, Show i)- => factorialAlgorithm- -> i -- ^ The total number of items from which to select.- -> i -- ^ The number of items in a sample.- -> i -- ^ The number of combinations.-nCr _ 0 _ = 1-nCr _ _ 0 = 1-nCr factorialAlgorithm n r- | n < 0 = error $ "Factory.Math.Statistics.nCr:\tinvalid n; " ++ show n- | r < 0 = error $ "Factory.Math.Statistics.nCr:\tinvalid r; " ++ show r- | n < r = 0- | otherwise = numerator `par` (denominator `pseq` numerator `div` denominator)- where- [smaller, bigger] = Data.List.sort [r, n - r]- numerator = Math.Implementations.Factorial.risingFactorial (succ bigger) (n - bigger)- denominator = Math.Factorial.factorial factorialAlgorithm smaller---- | The number of /permutations/ of /r/ objects taken from /n/; <http://en.wikipedia.org/wiki/Permutations>.-nPr :: (Integral i, Show i)- => i -- ^ The total number of items from which to select.- -> i -- ^ The number of items in a sample.- -> i -- ^ The number of permutations.-nPr 0 _ = 1-nPr _ 0 = 1-nPr n r- | n < 0 = error $ "Factory.Math.Statistics.nPr:\tinvalid n; " ++ show n- | r < 0 = error $ "Factory.Math.Statistics.nPr:\tinvalid r; " ++ show r- | n < r = 0- | otherwise = Math.Implementations.Factorial.fallingFactorial n r-
− src/Factory/Math/Summation.hs
@@ -1,91 +0,0 @@-{-- Copyright (C) 2011 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@] Provides an alternative algorithm for the summation of /rational/ numbers.--}--module Factory.Math.Summation(--- * Functions- sum',- sumR',- sumR-) where--import qualified Control.DeepSeq-import qualified Control.Parallel.Strategies-import qualified Data.List-import qualified Data.Ratio-import Data.Ratio((%))-import qualified ToolShed.Data.List--{- |- * Sums a list of numbers of arbitrary type.-- * Sparks the summation of @(list-length / chunk-size)@ chunks from the list, each of the specified size (thought the last chunk may be smaller),- then recursively sums the list of results from each spark.-- * CAVEAT: unless the numbers are large, 'Rational' (requiring /cross-multiplication/), or the list long,- 'sum' is too light-weight for sparking to be productive,- therefore it is more likely to be the parallelised deep /evaluation/ of list-elements which saves time.--}-sum' :: (Num n, Control.DeepSeq.NFData n)- => ToolShed.Data.List.ChunkLength- -> [n]- -> n-sum' chunkLength- | chunkLength <= 1 = error $ "Factory.Math.Summation.sum':\tinvalid chunk-size; " ++ show chunkLength- | otherwise = slave- where- slave :: (Num n, Control.DeepSeq.NFData n) => [n] -> n- slave [] = 0- slave [x] = x- slave l = slave {-recurse-} . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq sum $ ToolShed.Data.List.chunk chunkLength l--{- |- * Sums a list of /rational/ type numbers.-- * CAVEAT: though faster than 'Data.List.sum', this algorithm has poor space-complexity, making it unsuitable for unrestricted use.--}-{-# INLINE sumR' #-} -- This makes a staggering difference.-sumR' :: Integral i => [Data.Ratio.Ratio i] -> Data.Ratio.Ratio i-sumR' l = foldr (\ratio -> ((Data.Ratio.numerator ratio * (commonDenominator `div` Data.Ratio.denominator ratio)) +)) 0 l % commonDenominator where--- commonDenominator = foldr (lcm . Data.Ratio.denominator) 1 l- commonDenominator = Data.List.foldl' (\multiple -> lcm multiple . Data.Ratio.denominator) 1 l -- Slightly faster.--{- |- * Sums a list of /rational/ numbers.-- * Sparks the summation of @(list-length / chunk-length)@ chunks from the list, each of the specified size (thought the last chunk may be smaller),- then recursively sums the list of results from each spark.-- * CAVEAT: memory-use is proportional to chunk-size.--}-{-# INLINE sumR #-} -- This makes a staggering difference to calls from other modules.-sumR :: (Integral i, Control.DeepSeq.NFData i)- => ToolShed.Data.List.ChunkLength- -> [Data.Ratio.Ratio i]- -> Data.Ratio.Ratio i-sumR chunkLength- | chunkLength <= 1 = error $ "Factory.Math.Summation.sumR:\tinvalid chunk-size; " ++ show chunkLength- | otherwise = slave- where- slave :: (Integral i, Control.DeepSeq.NFData i) => [Data.Ratio.Ratio i] -> Data.Ratio.Ratio i- slave l- | length l <= chunkLength = sumR' l- | otherwise = slave {-recurse-} . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq sumR' $ ToolShed.Data.List.chunk chunkLength l
− src/Factory/Test/CommandOptions.hs
@@ -1,48 +0,0 @@-{-- Copyright (C) 2011 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 the available set of command-line options; of which there's currently only one.--}--module Factory.Test.CommandOptions(--- * Types--- ** Data-types- CommandOptions(..),--- * Functions--- ** Mutators- setVerbose-) where--import qualified ToolShed.Defaultable---- | Declare a record used to contain command-line options.-data CommandOptions = MkCommandOptions {- verbose :: Bool -- ^ Whether additional informative output should be generated, where applicable.-}--instance ToolShed.Defaultable.Defaultable CommandOptions where- defaultValue = MkCommandOptions { verbose = False }---- | Mutator.-setVerbose :: CommandOptions -> CommandOptions-setVerbose commandOptions = commandOptions {- verbose = True-}--
− src/Factory/Test/Performance/Factorial.hs
@@ -1,73 +0,0 @@-{-- Copyright (C) 2011 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@] Times the methods exported from module "Math.Factorial".--}--module Factory.Test.Performance.Factorial(--- * Functions- factorialPerformance,- factorialPerformanceControl,- factorialPerformanceGraph,- factorialPerformanceGraphControl-) where--import qualified Control.DeepSeq-import qualified Data.List-import qualified Factory.Math.Factorial as Math.Factorial-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required by 'Math.Factorial.factorial'.-factorialPerformance :: (- Control.DeepSeq.NFData i,- Integral i,- Math.Factorial.Algorithmic algorithm,- Show i- ) => algorithm -> i -> IO (Double, i)-factorialPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . Math.Factorial.factorial algorithm---- | Measures the CPU-time required by a naive implementation.-factorialPerformanceControl :: (Control.DeepSeq.NFData i, Integral i) => i -> IO (Double, i)--- factorialPerformanceControl i = ToolShed.System.TimePure.getCPUSeconds $ product [1 .. i] -- CAVEAT: too lazy.-factorialPerformanceControl i = ToolShed.System.TimePure.getCPUSeconds $ Data.List.foldl' (*) 1 [2 .. i]--{- |- * Measure the CPU-time required by 'Math.Factorial.factorial', against an exponentially increasing operand.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-factorialPerformanceGraph :: Math.Factorial.Algorithmic algorithm => Bool -> algorithm -> IO ()-factorialPerformanceGraph verbose algorithm = mapM_ (- \operand -> factorialPerformance algorithm operand >>= putStrLn . shows operand . showChar '\t' . (- if verbose- then (`shows` "")- else (`shows` "") . fst- )- ) $ iterate (* 2) (1 :: Integer)---- | Graphs the CPU-time required by a naive implementation, against an exponentially increasing operand.-factorialPerformanceGraphControl :: Bool -> IO ()-factorialPerformanceGraphControl verbose = mapM_ (- \operand -> factorialPerformanceControl operand >>= putStrLn . shows operand . showChar '\t' . (- if verbose- then (`shows` "")- else (`shows` "") . fst- )- ) $ iterate (* 2) (1 :: Integer)-
− src/Factory/Test/Performance/Hyperoperation.hs
@@ -1,71 +0,0 @@-{-- Copyright (C) 2011 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@] Times functions exported from module "Math.Hyperoperation".--}--module Factory.Test.Performance.Hyperoperation(--- * Functions- hyperoperationPerformance,- hyperoperationPerformanceGraphRank,- hyperoperationPerformanceGraphExponent-) where--import qualified Factory.Math.Hyperoperation as Math.Hyperoperation-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required by 'Math.Hyperoperation.hyperoperation'.-hyperoperationPerformance :: (Integral rank, Show rank) => rank -> Math.Hyperoperation.Base -> Math.Hyperoperation.HyperExponent -> IO (Double, Integer)-hyperoperationPerformance rank base = ToolShed.System.TimePure.getCPUSeconds . Math.Hyperoperation.hyperoperation rank base--{- |- * Measure the CPU-time required by 'Math.Hyperoperation.hyperoperation', against a linearly increasing /rank/.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-hyperoperationPerformanceGraphRank- :: Bool -- ^ Verbose.- -> Math.Hyperoperation.Base- -> Math.Hyperoperation.HyperExponent- -> IO ()-hyperoperationPerformanceGraphRank verbose base hyperExponent = mapM_ (- \rank -> hyperoperationPerformance rank base hyperExponent >>= putStrLn . shows rank . showChar '\t' . (- if verbose- then (`shows` "")- else (`shows` "") . fst- )- ) [0 :: Int ..]--{- |- * Measure the CPU-time required by 'Math.Hyperoperation.hyperoperation', against a linearly increasing /hyper-exponent/.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-hyperoperationPerformanceGraphExponent :: (Integral rank, Show rank)- => Bool -- ^ Verbose.- -> rank- -> Math.Hyperoperation.Base- -> IO ()-hyperoperationPerformanceGraphExponent verbose rank base = mapM_ (- \hyperExponent -> hyperoperationPerformance rank base hyperExponent >>= putStrLn . shows hyperExponent . showChar '\t' . (- if verbose- then (`shows` "")- else (`shows` "") . fst- )- ) [0 ..]
− src/Factory/Test/Performance/Pi.hs
@@ -1,81 +0,0 @@-{-- Copyright (C) 2011 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@] Times the methods exported from module "Math.Pi".--}--module Factory.Test.Performance.Pi(--- * Types--- ** Type-synonyms- Category,--- * Functions- piPerformance,- piPerformanceGraph-) where--import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Pi.AGM.Algorithm as Math.Implementations.Pi.AGM.Algorithm-import qualified Factory.Math.Implementations.Pi.BBP.Algorithm as Math.Implementations.Pi.BBP.Algorithm-import qualified Factory.Math.Implementations.Pi.Borwein.Algorithm as Math.Implementations.Pi.Borwein.Algorithm-import qualified Factory.Math.Implementations.Pi.Ramanujan.Algorithm as Math.Implementations.Pi.Ramanujan.Algorithm-import qualified Factory.Math.Implementations.Pi.Spigot.Algorithm as Math.Implementations.Pi.Spigot.Algorithm-import qualified Factory.Math.Pi as Math.Pi-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified ToolShed.System.TimePure---- | The type of a /Pi/-algorithm, including where required, the algorithm for /square-root/s and /factorial/s.-type Category squareRootAlgorithm factorialAlgorithm = Math.Pi.Category (- Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm- ) Math.Implementations.Pi.BBP.Algorithm.Algorithm (- Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm- ) (- Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm- ) Math.Implementations.Pi.Spigot.Algorithm.Algorithm---- | Measures the CPU-time required to find Pi to the required precision.-piPerformance :: (- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Category squareRootAlgorithm factorialAlgorithm -> Math.Precision.DecimalDigits -> IO (Double, String)-piPerformance category = ToolShed.System.TimePure.getCPUSeconds . Math.Pi.openS category--{- |- * Measures the CPU-time required to determine /Pi/ to an exponentially increasing precision-requirement.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-piPerformanceGraph :: (- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Show squareRootAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm,- Show factorialAlgorithm- ) => RealFrac i- => Category squareRootAlgorithm factorialAlgorithm -- ^ The algorithm.- -> i -- ^ The factor by which the precision is increased on each iteration.- -> Math.Precision.DecimalDigits -- ^ The maximum precision required.- -> Bool -- ^ Whether to return the digits of /Pi/.- -> IO ()-piPerformanceGraph category factor maxDecimalDigits verbose = mapM_ (- \decimalDigits -> piPerformance category decimalDigits >>= putStrLn . shows decimalDigits . showChar '\t' . (- if verbose- then (`shows` "")- else (`shows` "") . fst- )- ) . takeWhile (<= maxDecimalDigits) . map round $ iterate (* factor) 1
− src/Factory/Test/Performance/Primality.hs
@@ -1,54 +0,0 @@-{-- Copyright (C) 2011 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@] Times functions exported from module "Math.Primality".--}--module Factory.Test.Performance.Primality(--- * Functions- carmichaelNumbersPerformance,- isPrimePerformance,- isPrimePerformanceGraph-) where--import qualified Control.DeepSeq-import qualified Factory.Math.Fibonacci as Math.Fibonacci-import qualified Factory.Math.Primality as Math.Primality-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required to find the specified number of /Carmichael/-numbers, which is returned together with the requested list.-carmichaelNumbersPerformance :: Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> Int -> IO (Double, [Integer])-carmichaelNumbersPerformance primalityAlgorithm i- | i < 0 = fail $ "Factory.Test.Performance.Primality.carmichaelNumbersPerformance:\tnegative number; " ++ show i- | otherwise = ToolShed.System.TimePure.getCPUSeconds . take i $ Math.Primality.carmichaelNumbers primalityAlgorithm---- | Measures the CPU-time required to determine whether the specified integer is prime, which is returned together with the Boolean result.-isPrimePerformance :: (Control.DeepSeq.NFData i, Integral i, Show i) => Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> i -> IO (Double, Bool)-isPrimePerformance primalityAlgorithm = ToolShed.System.TimePure.getCPUSeconds . Math.Primality.isPrime primalityAlgorithm--{- |- * Measures the CPU-time required to determine whether /prime-indexed Fibonacci-numbers/ are actually /prime/.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-isPrimePerformanceGraph :: Math.Primality.Algorithmic primalityAlgorithm => primalityAlgorithm -> IO ()-isPrimePerformanceGraph primalityAlgorithm = mapM_ (- \operand -> isPrimePerformance primalityAlgorithm operand >>= putStrLn . shows operand . showChar '\t' . (`shows` "")- ) (Math.Fibonacci.primeIndexedFibonacci :: [Integer])-
− src/Factory/Test/Performance/PrimeFactorisation.hs
@@ -1,50 +0,0 @@-{-- Copyright (C) 2011 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@] Times the methods exported by module "Math.PrimeFactorisation".--}--module Factory.Test.Performance.PrimeFactorisation(--- * Functions- primeFactorsPerformance,- primeFactorsPerformanceGraph-) where--import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Math.Fibonacci as Math.Fibonacci-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required to prime-factorise the specified integer, which is returned together with the resulting list of factors.-primeFactorsPerformance :: Math.PrimeFactorisation.Algorithmic algorithm => algorithm -> Integer -> IO (Double, Data.PrimeFactors.Factors Integer Int)-primeFactorsPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . Math.PrimeFactorisation.primeFactors algorithm--{- |- * Measure the CPU-time required by 'Math.PrimeFactorisation.primeFactors',- arbitrarily against the /Fibonacci/-numbers (which seemed to fit the requirements).-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-primeFactorsPerformanceGraph :: Math.PrimeFactorisation.Algorithmic algorithm => algorithm -> Int -> IO ()-primeFactorsPerformanceGraph algorithm tests- | tests < 0 = fail $ "Factory.Test.Performance.PrimeFactorisation.primeFactorsPerformanceGraph:\tnegative number; " ++ show tests- | otherwise = mapM_ (- \operand -> primeFactorsPerformance algorithm operand >>= putStrLn . shows operand . showChar '\t' . (`shows` "")- ) . take tests . dropWhile (< 2) $ Math.Fibonacci.fibonacci-
− src/Factory/Test/Performance/Primes.hs
@@ -1,47 +0,0 @@-{-- Copyright (C) 2011 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@] Measures the CPU-time required by "Math.Primes.primes".--}--module Factory.Test.Performance.Primes(--- * Functions- primesPerformance,- mersenneNumbersPerformance-) where--import qualified Control.DeepSeq-import qualified Data.Array.IArray-import qualified Factory.Math.Primes as Math.Primes-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required by 'Math.Primes.primes', to find the specified prime.-primesPerformance :: (- Control.DeepSeq.NFData i,- Data.Array.IArray.Ix i,- Math.Primes.Algorithmic algorithm,- Integral i- ) => algorithm -> Int -> IO (Double, i)-primesPerformance algorithm = ToolShed.System.TimePure.getCPUSeconds . (Math.Primes.primes algorithm !!)---- | Measures the CPU-time required to find the specified number of /Mersenne/-numbers, which is returned together with the requested list.-mersenneNumbersPerformance :: Math.Primes.Algorithmic algorithm => algorithm -> Int -> IO (Double, [Integer])-mersenneNumbersPerformance primalityAlgorithm i- | i < 0 = fail $ "Factory.Test.Performance.Primes.mersenneNumbersPerformance:\tnegative number; " ++ show i- | otherwise = ToolShed.System.TimePure.getCPUSeconds . take i $ Math.Primes.mersenneNumbers primalityAlgorithm
− src/Factory/Test/Performance/SquareRoot.hs
@@ -1,59 +0,0 @@-{-- Copyright (C) 2011 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@] Measures the CPU-time required by the methods exported from module "Math.SquareRoot".--}--module Factory.Test.Performance.SquareRoot(--- * Functions- squareRootPerformance,- squareRootPerformanceGraph-) where--import qualified Control.Arrow-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required by 'Math.SquareRoot.squareRootFrom', which is returned together with the approximate rational result.-squareRootPerformance :: (- Math.SquareRoot.Algorithmic algorithm,- Real operand,- Show operand- ) => algorithm -> operand -> Math.Precision.DecimalDigits -> IO (Double, Math.SquareRoot.Result)-squareRootPerformance algorithm operand requiredDecimalDigits = ToolShed.System.TimePure.getCPUSeconds $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand--{- |- * Measures the CPU-time required by 'Math.SquareRoot.squareRootFrom', and the resulting accuracy,- using the specified algorithm, to an exponentially increasing precision-requirement.-- * CAVEAT: nothing is returned, since the result is printed ... and it never terminates.--}-squareRootPerformanceGraph :: (- Math.SquareRoot.Algorithmic algorithm,- Math.SquareRoot.Iterator algorithm,- Real operand,- Show algorithm,- Show operand- ) => algorithm -> operand -> IO ()-squareRootPerformanceGraph algorithm operand = mapM_ (- \requiredDecimalDigits -> putStrLn . (- \(cpuSeconds, actualDecimalDigits) -> shows algorithm . showChar '\t' . shows requiredDecimalDigits . showChar '\t' . shows actualDecimalDigits . showChar '\t' $ shows cpuSeconds ""- ) . Control.Arrow.second (Math.SquareRoot.getAccuracy operand) =<< squareRootPerformance algorithm operand requiredDecimalDigits- ) $ iterate (* max 2 (Math.SquareRoot.convergenceOrder algorithm)) 16
− src/Factory/Test/Performance/Statistics.hs
@@ -1,45 +0,0 @@-{-- Copyright (C) 2011 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@] Times the functions exported from module "Math.Statistics".--}--module Factory.Test.Performance.Statistics(--- * Functions- nCrPerformance-) where--import qualified Control.DeepSeq-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Statistics as Math.Statistics-import qualified ToolShed.System.TimePure---- | Measures the CPU-time required by 'Math.Statistics.nCr'.-nCrPerformance :: (- Control.DeepSeq.NFData i,- Integral i,- Math.Factorial.Algorithmic factorialAlgorithm,- Show i- )- => factorialAlgorithm- -> i -- ^ The total number from which to select.- -> i -- ^ The number of items in a sample.- -> IO (Double, i)-nCrPerformance factorialAlgorithm n r = ToolShed.System.TimePure.getCPUSeconds $ Math.Statistics.nCr factorialAlgorithm n r-
− src/Factory/Test/QuickCheck/ArithmeticGeometricMean.hs
@@ -1,57 +0,0 @@-{-- Copyright (C) 2011 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 "Math.ArithmeticGeometricMean".--}--module Factory.Test.QuickCheck.ArithmeticGeometricMean(--- * Types--- ** Type-synonyms--- Testable,--- * Functions- quickChecks-) where--import qualified Data.Tuple-import qualified Factory.Math.ArithmeticGeometricMean as Math.ArithmeticGeometricMean-import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot-import qualified Factory.Math.Precision as Math.Precision-import Factory.Test.QuickCheck.SquareRoot()-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--type Testable = Math.Implementations.SquareRoot.Algorithm -> Math.Precision.DecimalDigits -> Math.ArithmeticGeometricMean.AGM -> Int -> Test.QuickCheck.Property---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_symmetrical, prop_bounds] where- prop_symmetrical, prop_bounds :: Testable- prop_symmetrical squareRootAlgorithm decimalDigits agm index = Math.ArithmeticGeometricMean.isValid agm ==> Test.QuickCheck.label "prop_symmetrical" . and . tail . take index' $ zipWith (==) (- Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' agm- ) (- Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' $ Data.Tuple.swap agm- ) where- decimalDigits' = succ $ decimalDigits `mod` 64- index' = succ $ index `mod` 8-- prop_bounds squareRootAlgorithm decimalDigits agm index = all ($ agm) [Math.ArithmeticGeometricMean.isValid, uncurry (/=)] ==> Test.QuickCheck.label "prop_bounds" . all (uncurry (>=)) . tail . take index' $ Math.ArithmeticGeometricMean.convergeToAGM squareRootAlgorithm decimalDigits' agm- where- decimalDigits' = 33 {-test is sensitive to rounding-errors-} + (decimalDigits `mod` 96)- index' = succ $ index `mod` 5-
− src/Factory/Test/QuickCheck/Factorial.hs
@@ -1,68 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011 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 "Math.Implementations.Factorial".--}--module Factory.Test.QuickCheck.Factorial(--- * Types--- ** Type-synonyms--- Testable,--- * Functions- quickChecks-) where--import Data.Ratio((%))-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import Factory.Math.Implementations.Factorial((!/!))-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--instance Test.QuickCheck.Arbitrary Math.Implementations.Factorial.Algorithm where- arbitrary = Test.QuickCheck.elements [Math.Implementations.Factorial.Bisection, Math.Implementations.Factorial.PrimeFactorisation]--type Testable = Integer -> Integer -> Test.QuickCheck.Property---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_equivalence, prop_symmetry, prop_x0, prop_0n] >> Test.QuickCheck.quickCheck prop_ratio >> Test.QuickCheck.quickCheck prop_consistency where- prop_equivalence, prop_symmetry, prop_x0, prop_0n :: Testable- prop_equivalence x n = Test.QuickCheck.label "prop_equivalence" $ Math.Implementations.Factorial.risingFactorial x n == sign * Math.Implementations.Factorial.fallingFactorial (negate x) n && Math.Implementations.Factorial.fallingFactorial x n == sign * Math.Implementations.Factorial.risingFactorial (negate x) n where- sign :: Integer- sign- | even n = 1- | otherwise = negate 1-- prop_symmetry x n = Test.QuickCheck.label "prop_symmetry" $ Math.Implementations.Factorial.risingFactorial x n == Math.Implementations.Factorial.fallingFactorial (pred $ x + n) n-- prop_x0 x _ = Test.QuickCheck.label "prop_x0" $ all (== 1) $ map ($ 0) [Math.Implementations.Factorial.risingFactorial x, Math.Implementations.Factorial.fallingFactorial x]-- prop_0n _ n = Test.QuickCheck.label "prop_0n" $ all (== if n == 0 then 1 else 0) $ map ($ n) [Math.Implementations.Factorial.risingFactorial 0, Math.Implementations.Factorial.fallingFactorial 0]-- prop_ratio :: Math.Implementations.Factorial.Algorithm -> Integer -> Integer -> Test.QuickCheck.Property- prop_ratio algorithm i j = Test.QuickCheck.label "prop_ratio" $ n !/! d == Math.Factorial.factorial algorithm n % Math.Factorial.factorial algorithm d where- n = pred $ i `mod` 100000- d = pred $ j `mod` 100000-- prop_consistency :: Math.Implementations.Factorial.Algorithm -> Math.Implementations.Factorial.Algorithm -> Integer -> Test.QuickCheck.Property- prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Factorial.factorial l n == Math.Factorial.factorial r n where- n = pred $ i `mod` 100000-
− src/Factory/Test/QuickCheck/Hyperoperation.hs
@@ -1,75 +0,0 @@-{-- Copyright (C) 2011 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 "Math.Hyperoperation".--}--module Factory.Test.QuickCheck.Hyperoperation(--- * Functions- quickChecks-) where--import qualified Factory.Math.Hyperoperation as Math.Hyperoperation-import qualified Test.QuickCheck--type Rank = Int---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks =- Test.QuickCheck.quickCheck prop_rankCoincides- >> Test.QuickCheck.quickCheck prop_baseCoincides- >> Test.QuickCheck.quickCheck prop_hyperExponentCoincides- >> Test.QuickCheck.quickCheck `mapM_` [prop_succ, prop_addition, prop_multiplication, prop_exponentiation] where- prop_rankCoincides :: Rank -> Test.QuickCheck.Property- prop_rankCoincides rank = Test.QuickCheck.label "prop_rankCoincides" $ Math.Hyperoperation.hyperoperation rank' 2 2 == 4 where- rank' :: Rank- rank' = succ $ rank `mod` 1000-- prop_baseCoincides :: Rank -> Integer -> Test.QuickCheck.Property- prop_baseCoincides rank base = Test.QuickCheck.label "prop_baseCoincides" $ Math.Hyperoperation.hyperoperation rank' base 1 == base where- rank' :: Rank- rank' = 2 + (rank `mod` 1000)-- prop_hyperExponentCoincides :: Rank -> Integer -> Test.QuickCheck.Property- prop_hyperExponentCoincides rank hyperExponent = Test.QuickCheck.label "prop_hyperExponentCoincides" $ Math.Hyperoperation.hyperoperation rank' 1 hyperExponent' == 1 where- rank' :: Rank- rank' = 3 + (rank `mod` 1000)-- hyperExponent' :: Math.Hyperoperation.HyperExponent- hyperExponent' = abs hyperExponent-- prop_succ, prop_addition, prop_multiplication, prop_exponentiation :: Integer -> Integer -> Test.QuickCheck.Property- prop_succ base hyperExponent = Test.QuickCheck.label "prop_succ" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.succession base hyperExponent' == succ (fromIntegral hyperExponent') where- hyperExponent' :: Math.Hyperoperation.HyperExponent- hyperExponent' = abs hyperExponent-- prop_addition base hyperExponent = Test.QuickCheck.label "prop_addition" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.addition base hyperExponent' == base + fromIntegral hyperExponent' where- hyperExponent' :: Math.Hyperoperation.HyperExponent- hyperExponent' = abs hyperExponent-- prop_multiplication base hyperExponent = Test.QuickCheck.label "prop_multiplication" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.multiplication base hyperExponent' == base * fromIntegral hyperExponent' where- hyperExponent' :: Math.Hyperoperation.HyperExponent- hyperExponent' = abs hyperExponent-- prop_exponentiation base hyperExponent = Test.QuickCheck.label "prop_exponentiation" $ Math.Hyperoperation.hyperoperation Math.Hyperoperation.exponentiation base hyperExponent' == base ^ hyperExponent' where- hyperExponent' :: Math.Hyperoperation.HyperExponent- hyperExponent' = abs hyperExponent--
− src/Factory/Test/QuickCheck/Interval.hs
@@ -1,43 +0,0 @@-{-- Copyright (C) 2011 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 "Data.Interval".--}--module Factory.Test.QuickCheck.Interval(--- * Functions- quickChecks-) where--import qualified Data.Ratio-import qualified Factory.Data.Interval as Data.Interval-import qualified Test.QuickCheck---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheckWith Test.QuickCheck.stdArgs {Test.QuickCheck.maxSuccess = 1000} prop_product where- prop_product :: Data.Ratio.Ratio Integer -> Integer -> Data.Interval.Interval Integer -> Test.QuickCheck.Property- prop_product ratio minLength interval = Test.QuickCheck.label "prop_product" $ Data.Interval.product' ratio' minLength' interval' == product (Data.Interval.toList interval') where- interval' = Data.Interval.normalise interval- minLength' = succ $ minLength `mod` 1000- ratio'- | r > 1 = recip r- | otherwise = r- where- r = abs ratio
− src/Factory/Test/QuickCheck/MonicPolynomial.hs
@@ -1,72 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011 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' and defines /QuickCheck/-properties for "Data.MonicPolynomial".--}--module Factory.Test.QuickCheck.MonicPolynomial(--- * Types--- ** Type-synonyms--- P--- * Functions- quickChecks-) where--import Factory.Data.Ring((=*=), (=+=), (=^))-import Factory.Test.QuickCheck.Polynomial()-import qualified Factory.Data.MonicPolynomial as Data.MonicPolynomial-import qualified Factory.Data.Polynomial as Data.Polynomial-import qualified Factory.Data.QuotientRing as Data.QuotientRing-import qualified Factory.Data.Ring as Data.Ring-import qualified Test.QuickCheck--instance (- Integral c,- Integral e,- Test.QuickCheck.Arbitrary c,- Test.QuickCheck.Arbitrary e,- Show c,- Show e- ) => Test.QuickCheck.Arbitrary (Data.MonicPolynomial.MonicPolynomial c e) where- arbitrary = do- polynomial <- Test.QuickCheck.arbitrary-- return {-to Gen-monad-} . Data.MonicPolynomial.mkMonicPolynomial $ ((1, succ $ Data.Polynomial.getDegree polynomial) :) `Data.Polynomial.lift` polynomial--type P = Data.MonicPolynomial.MonicPolynomial Integer Integer---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_quotRem, prop_quotientRingNormalised] >> Test.QuickCheck.quickCheck prop_perfectPower >> Test.QuickCheck.quickCheck prop_isDivisibleBy where- prop_quotRem, prop_quotientRingNormalised :: P -> P -> Test.QuickCheck.Property- prop_quotRem numerator denominator = Test.QuickCheck.label "prop_quotRem" $ numerator == denominator =*= quotient =+= remainder where- (quotient, remainder) = numerator `Data.QuotientRing.quotRem'` denominator-- prop_quotientRingNormalised numerator denominator = Test.QuickCheck.label "prop_quotientRingNormalised" $ all (Data.Polynomial.isNormalised . Data.MonicPolynomial.getPolynomial) [numerator `Data.QuotientRing.quot'` denominator, numerator `Data.QuotientRing.rem'` denominator]-- prop_perfectPower :: P -> Int -> Test.QuickCheck.Property- prop_perfectPower polynomial power = Test.QuickCheck.label "prop_perfectPower" $ iterate (`Data.QuotientRing.quot'` polynomial) (polynomial =^ power') !! pred power' == polynomial where- power' :: Int- power' = succ $ power `mod` 100-- prop_isDivisibleBy :: [P] -> Test.QuickCheck.Property- prop_isDivisibleBy monicPolynomials = Test.QuickCheck.label "prop_isDivisibleBy" $ all (Data.QuotientRing.isDivisibleBy (Data.Ring.product' (recip 2) {-TODO-} 10 monicPolynomials)) monicPolynomials--
− src/Factory/Test/QuickCheck/PerfectPower.hs
@@ -1,54 +0,0 @@-{-- Copyright (C) 2011 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 "Math.PerfectPower".--}--module Factory.Test.QuickCheck.PerfectPower(--- * Functions- quickChecks-) where--import qualified Data.Maybe-import qualified Factory.Math.PerfectPower as Math.PerfectPower-import qualified Factory.Math.Power as Math.Power-import qualified Test.QuickCheck-import Test.QuickCheck((==>))---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks =- Test.QuickCheck.quickCheck `mapM_` [prop_maybeSquareNumber, prop_rewriteRule]- >> Test.QuickCheck.quickCheckWith Test.QuickCheck.stdArgs {Test.QuickCheck.maxSuccess = 10000} prop_notSquare- >> Test.QuickCheck.quickCheck prop_isPerfectPower- where- prop_maybeSquareNumber, prop_notSquare, prop_rewriteRule :: Integer -> Test.QuickCheck.Property- prop_maybeSquareNumber i = Test.QuickCheck.label "prop_maybeSquareNumber" $ Math.PerfectPower.maybeSquareNumber (Math.Power.square i) == Just (abs i)-- prop_notSquare i = abs i > 0 ==> Test.QuickCheck.label "prop_notSquare" . Data.Maybe.isNothing $ Math.PerfectPower.maybeSquareNumber (succ $ i ^ (10 {-promote rounding-error using big number-} :: Int))-- prop_rewriteRule i = Test.QuickCheck.label "prop_rewriteRule" $ Math.PerfectPower.isPerfectPower i' == Math.PerfectPower.isPerfectPower (fromIntegral i' :: Int) where- i' = abs i-- prop_isPerfectPower :: Integer -> Integer -> Test.QuickCheck.Property- prop_isPerfectPower b e = Test.QuickCheck.label "prop_isPerfectPower" . Math.PerfectPower.isPerfectPower $ b' ^ e' where- b' = 2 + (b `mod` 10)- e' = 2 + (e `mod` 8)--
− src/Factory/Test/QuickCheck/Pi.hs
@@ -1,114 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011-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 "Math.Pi".--}--module Factory.Test.QuickCheck.Pi(--- * Types--- ** Type-synonyms--- Testable,--- * Functions- quickChecks-) where--import Prelude hiding ((<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import Control.Applicative((<$>), (<*>))-import Factory.Test.QuickCheck.Factorial()-import Factory.Test.QuickCheck.SquareRoot()-import qualified Factory.Math.Factorial as Math.Factorial-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Implementations.Pi.AGM.Algorithm as Math.Implementations.Pi.AGM.Algorithm-import qualified Factory.Math.Implementations.Pi.BBP.Algorithm as Math.Implementations.Pi.BBP.Algorithm-import qualified Factory.Math.Implementations.Pi.Borwein.Algorithm as Math.Implementations.Pi.Borwein.Algorithm-import qualified Factory.Math.Implementations.Pi.Ramanujan.Algorithm as Math.Implementations.Pi.Ramanujan.Algorithm-import qualified Factory.Math.Implementations.Pi.Spigot.Algorithm as Math.Implementations.Pi.Spigot.Algorithm-import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot-import qualified Factory.Math.Pi as Math.Pi-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--instance (- Test.QuickCheck.Arbitrary squareRootAlgorithm,- Math.SquareRoot.Algorithmic squareRootAlgorithm- ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.AGM.Algorithm.Algorithm squareRootAlgorithm) where- arbitrary = Math.Implementations.Pi.AGM.Algorithm.BrentSalamin <$> Test.QuickCheck.arbitrary--instance Test.QuickCheck.Arbitrary Math.Implementations.Pi.BBP.Algorithm.Algorithm where- arbitrary = Test.QuickCheck.elements [Math.Implementations.Pi.BBP.Algorithm.Bellard, Math.Implementations.Pi.BBP.Algorithm.Base65536]--instance (- Test.QuickCheck.Arbitrary squareRootAlgorithm,- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Test.QuickCheck.Arbitrary factorialAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.Borwein.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm) where- arbitrary = Test.QuickCheck.oneof [- Math.Implementations.Pi.Borwein.Algorithm.Borwein1993 <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary- ]--instance (- Test.QuickCheck.Arbitrary squareRootAlgorithm,- Math.SquareRoot.Algorithmic squareRootAlgorithm,- Test.QuickCheck.Arbitrary factorialAlgorithm,- Math.Factorial.Algorithmic factorialAlgorithm- ) => Test.QuickCheck.Arbitrary (Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm squareRootAlgorithm factorialAlgorithm) where- arbitrary = Test.QuickCheck.oneof [- Math.Implementations.Pi.Ramanujan.Algorithm.Classic <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary,- Math.Implementations.Pi.Ramanujan.Algorithm.Chudnovsky <$> Test.QuickCheck.arbitrary <*> Test.QuickCheck.arbitrary- ]--instance Test.QuickCheck.Arbitrary Math.Implementations.Pi.Spigot.Algorithm.Algorithm where- arbitrary = Test.QuickCheck.elements [Math.Implementations.Pi.Spigot.Algorithm.RabinowitzWagon, Math.Implementations.Pi.Spigot.Algorithm.Gosper]--instance (- Test.QuickCheck.Arbitrary agm,- Test.QuickCheck.Arbitrary bbp,- Test.QuickCheck.Arbitrary borwein,- Test.QuickCheck.Arbitrary ramanujan,- Test.QuickCheck.Arbitrary spigot- ) => Test.QuickCheck.Arbitrary (Math.Pi.Category agm bbp borwein ramanujan spigot) where- arbitrary = Test.QuickCheck.oneof [- Math.Pi.AGM <$> Test.QuickCheck.arbitrary,- Math.Pi.BBP <$> Test.QuickCheck.arbitrary,- Math.Pi.Borwein <$> Test.QuickCheck.arbitrary,- Math.Pi.Ramanujan <$> Test.QuickCheck.arbitrary,- Math.Pi.Spigot <$> Test.QuickCheck.arbitrary- ]--type Category = Math.Pi.Category (- Math.Implementations.Pi.AGM.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm- ) Math.Implementations.Pi.BBP.Algorithm.Algorithm (- Math.Implementations.Pi.Borwein.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm Math.Implementations.Factorial.Algorithm- ) (- Math.Implementations.Pi.Ramanujan.Algorithm.Algorithm Math.Implementations.SquareRoot.Algorithm Math.Implementations.Factorial.Algorithm- ) Math.Implementations.Pi.Spigot.Algorithm.Algorithm--type Testable = Category -> Category -> Math.Precision.DecimalDigits -> Test.QuickCheck.Property---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck prop_consistency where- prop_consistency :: Testable- prop_consistency l r decimalDigits = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Pi.openI l decimalDigits' - Math.Pi.openI r decimalDigits' <= 1 {-rounding error-} where- decimalDigits' = succ $ decimalDigits `mod` 250-
− src/Factory/Test/QuickCheck/Polynomial.hs
@@ -1,116 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Data.Polynomial".--}--module Factory.Test.QuickCheck.Polynomial(--- * Functions- quickChecks-) where--import Control.Arrow((***))-import Factory.Data.Ring((=*=), (=+=), (=-=), (=^))-import qualified Data.Numbers.Primes-import qualified Factory.Data.Polynomial as Data.Polynomial-import qualified Factory.Data.QuotientRing as Data.QuotientRing-import qualified Factory.Data.Ring as Data.Ring-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--instance (- Test.QuickCheck.Arbitrary c,- Integral c,- Test.QuickCheck.Arbitrary e,- Integral e- ) => Test.QuickCheck.Arbitrary (Data.Polynomial.Polynomial c e) where- arbitrary = (Data.Polynomial.mkPolynomial . map ((+ negate 4) . (`mod` 8) *** (`mod` 8))) `fmap` Test.QuickCheck.arbitrary---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks- = Test.QuickCheck.quickCheck prop_congruence- >> Test.QuickCheck.quickCheck `mapM_` [prop_quotRem, prop_degree, prop_ringNormalised, prop_quotientRingNormalised]- >> Test.QuickCheck.quickCheck `mapM_` [prop_power, prop_perfectPower, prop_normalised]- >> Test.QuickCheck.quickCheck prop_raiseModuloNormalised- >> Test.QuickCheck.quickCheck `mapM_` [prop_integralDomain, prop_isDivisibleBy]- where- prop_congruence :: Int -> Test.QuickCheck.Property- prop_congruence i = Test.QuickCheck.label "prop_congruence" $ Data.Polynomial.areCongruentModulo (Data.Polynomial.mkLinear 1 (negate 1) =^ prime) (Data.Polynomial.mkPolynomial [(1, prime), (negate 1, 0)]) prime where- prime :: Integer- prime = Data.Numbers.Primes.primes !! mod i 100-- prop_quotRem, prop_degree, prop_ringNormalised, prop_quotientRingNormalised :: Data.Polynomial.Polynomial Integer Integer -> Data.Polynomial.Polynomial Integer Integer -> Test.QuickCheck.Property- prop_quotRem numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_quotRem" $ numerator' == denominator' =*= quotient =+= remainder where- numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer- numerator' = Data.Polynomial.realCoefficientsToFrac numerator- denominator' = Data.Polynomial.realCoefficientsToFrac denominator-- (quotient, remainder) = numerator' `Data.QuotientRing.quotRem'` denominator'-- prop_degree numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_degree" $ remainder == Data.Polynomial.zero || Data.Polynomial.getDegree remainder < Data.Polynomial.getDegree denominator' where- numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer- numerator' = Data.Polynomial.realCoefficientsToFrac numerator- denominator' = Data.Polynomial.realCoefficientsToFrac denominator-- remainder = numerator' `Data.QuotientRing.rem'` denominator'-- prop_ringNormalised l r = Test.QuickCheck.label "prop_ringNormalised" $ all Data.Polynomial.isNormalised [l =*= r, l =+= r, l =-= r]-- prop_quotientRingNormalised numerator denominator = denominator' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_quotientRingNormalised" $ all Data.Polynomial.isNormalised [numerator' `Data.QuotientRing.quot'` denominator', numerator' `Data.QuotientRing.rem'` denominator'] where- numerator', denominator' :: Data.Polynomial.Polynomial Rational Integer- numerator' = Data.Polynomial.realCoefficientsToFrac numerator- denominator' = Data.Polynomial.realCoefficientsToFrac denominator-- prop_power, prop_perfectPower, prop_normalised :: Data.Polynomial.Polynomial Integer Integer -> Int -> Test.QuickCheck.Property- prop_power polynomial power = Test.QuickCheck.label "prop_power" $ polynomial =^ power' == iterate (=*= polynomial) polynomial !! pred power' where- power' :: Int- power' = succ $ power `mod` 100-- prop_perfectPower polynomial power = polynomial' /= Data.Polynomial.zero ==> Test.QuickCheck.label "prop_perfectPower" $ iterate (`Data.QuotientRing.quot'` polynomial') (polynomial' =^ power') !! pred power' == polynomial' where- polynomial' :: Data.Polynomial.Polynomial Rational Integer- polynomial' = Data.Polynomial.realCoefficientsToFrac polynomial-- power' :: Int- power' = succ $ power `mod` 100-- prop_normalised polynomial i = Test.QuickCheck.label "prop_normalised" $ all Data.Polynomial.isNormalised [- polynomial =^ power',- polynomial `Data.Polynomial.mod'` modulus'- ] where- power' :: Int- power' = succ $ i `mod` 100-- modulus' :: Integer- modulus' = succ $ fromIntegral i `mod` 100-- prop_raiseModuloNormalised :: Data.Polynomial.Polynomial Integer Integer -> Integer -> Integer -> Test.QuickCheck.Property- prop_raiseModuloNormalised polynomial power modulus = Test.QuickCheck.label "prop_raiseModuloNormalised" . Data.Polynomial.isNormalised $ Data.Polynomial.raiseModulo polynomial power' modulus' where- power', modulus' :: Integer- power' = succ $ power `mod` 100- modulus' = succ $ modulus `mod` 100-- prop_integralDomain, prop_isDivisibleBy :: [Data.Polynomial.Polynomial Integer Integer] -> Test.QuickCheck.Property- prop_integralDomain polynomials = Data.Polynomial.zero `notElem` polynomials ==> Test.QuickCheck.label "prop_integralDomain" $ Data.Ring.product' (recip 2) {-TODO-} 10 polynomials /= Data.Polynomial.zero-- prop_isDivisibleBy polynomials = Test.QuickCheck.label "prop_isDivisibleBy" . all (Data.QuotientRing.isDivisibleBy (Data.Ring.product' (recip 2) {-TODO-} 10 polynomials')) $ filter (/= Data.Polynomial.zero) polynomials' where- polynomials' :: [Data.Polynomial.Polynomial Rational Integer]- polynomials' = map Data.Polynomial.realCoefficientsToFrac polynomials-
− src/Factory/Test/QuickCheck/Power.hs
@@ -1,45 +0,0 @@-{-- Copyright (C) 2011 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 "Math.Power".--}--module Factory.Test.QuickCheck.Power(--- * Functions- quickChecks-) where--import qualified Data.List-import qualified Factory.Math.Power as Math.Power-import qualified Test.QuickCheck-import Test.QuickCheck((==>))---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck prop_squaresFrom >> Test.QuickCheck.quickCheck prop_raiseModulo- where- prop_squaresFrom :: Integer -> Integer -> Test.QuickCheck.Property- prop_squaresFrom from l = Test.QuickCheck.label "prop_squaresFrom" . (\(x, y) -> y == Math.Power.square x) . Data.List.genericIndex (Math.Power.squaresFrom from) $ abs l-- prop_raiseModulo :: Integer -> Integer -> Integer -> Test.QuickCheck.Property- prop_raiseModulo b e m = m /= 0 ==> Test.QuickCheck.label "prop_raiseModulo" $ Math.Power.raiseModulo b e' m == (b ^ e') `mod` m where- e' :: Integer- e' = abs e--
− src/Factory/Test/QuickCheck/Primality.hs
@@ -1,72 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.Primality".--}--module Factory.Test.QuickCheck.Primality(--- * Functions- quickChecks-) where--import Factory.Test.QuickCheck.PrimeFactorisation()-import qualified Data.List-import qualified Data.Numbers.Primes-import qualified Factory.Math.Implementations.Primality as Math.Implementations.Primality-import qualified Factory.Math.Implementations.PrimeFactorisation as Math.Implementations.PrimeFactorisation-import qualified Factory.Math.Primality as Math.Primality-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--instance Test.QuickCheck.Arbitrary factorisationAlgorithm => Test.QuickCheck.Arbitrary (Math.Implementations.Primality.Algorithm factorisationAlgorithm) where- arbitrary = Test.QuickCheck.oneof [- Math.Implementations.Primality.AKS `fmap` Test.QuickCheck.arbitrary,- return {-to Gen-monad-} Math.Implementations.Primality.MillerRabin- ]---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks- = Test.QuickCheck.quickCheck prop_prime- >> Test.QuickCheck.quickCheck prop_composite- >> Test.QuickCheck.quickCheck prop_consistency- where- prop_prime :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property- prop_prime primalityAlgorithm i = Test.QuickCheck.label "prop_prime" $ Math.Primality.isPrime primalityAlgorithm prime where- normalise n- | primalityAlgorithm == Math.Implementations.Primality.MillerRabin = n `mod` 1000000 -- Limited by the efficiency of 'Data.Numbers.Primes.primes'.- | otherwise = n `mod` 59-- prime :: Integer- prime = Data.List.genericIndex Data.Numbers.Primes.primes $ normalise i-- prop_composite :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> [Integer] -> Test.QuickCheck.Property- prop_composite primalityAlgorithm l = length l > 1 ==> Test.QuickCheck.label "prop_composite" . not $ Math.Primality.isPrime primalityAlgorithm composite where- normalise n- | primalityAlgorithm == Math.Implementations.Primality.MillerRabin = n `mod` 1000000- | otherwise = n `mod` 10-- composite :: Integer- composite = product . map (Data.List.genericIndex Data.Numbers.Primes.primes . normalise) $ take 8 l-- prop_consistency :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property- prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" $ Math.Primality.isPrime l i' == Math.Primality.isPrime r i' where- i' = i `mod` 512-
− src/Factory/Test/QuickCheck/PrimeFactorisation.hs
@@ -1,94 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011 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' and defines /QuickCheck/-properties for "Math.PrimeFactorisation".--}--module Factory.Test.QuickCheck.PrimeFactorisation(--- * Functions- quickChecks-) where--import qualified Data.List-import qualified Data.Numbers.Primes-import qualified Factory.Data.PrimeFactors as Data.PrimeFactors-import qualified Factory.Data.Exponential as Data.Exponential-import qualified Factory.Math.Implementations.PrimeFactorisation as Math.Implementations.PrimeFactorisation-import qualified Factory.Math.MultiplicativeOrder as Math.MultiplicativeOrder-import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--instance Test.QuickCheck.Arbitrary Math.Implementations.PrimeFactorisation.Algorithm where- arbitrary = Test.QuickCheck.oneof [- Test.QuickCheck.elements [- Math.Implementations.PrimeFactorisation.TrialDivision,- Math.Implementations.PrimeFactorisation.FermatsMethod- ]- ]---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks =- Test.QuickCheck.quickCheck prop_consistency- >> Test.QuickCheck.quickCheck `mapM_` [prop_primeFactors, prop_smoothness, prop_eulersTotientP, prop_eulersTotientInequality]- >> Test.QuickCheck.quickCheck `mapM_` [prop_eulersTotient, prop_lagrange, prop_multiplicativeOrder, prop_perfectPower]- where- prop_consistency :: Integer -> Test.QuickCheck.Property- prop_consistency i = Test.QuickCheck.label "prop_consistency" $ (Math.PrimeFactorisation.primeFactors Math.Implementations.PrimeFactorisation.TrialDivision i' :: Data.PrimeFactors.Factors Integer Int) == Math.PrimeFactorisation.primeFactors Math.Implementations.PrimeFactorisation.FermatsMethod i' where- i' :: Integer- i' = succ $ i `mod` 1000000-- prop_primeFactors, prop_smoothness, prop_eulersTotientP, prop_eulersTotientInequality :: Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Test.QuickCheck.Property- prop_primeFactors algorithm i = Test.QuickCheck.label "prop_primeFactors" $ Data.PrimeFactors.product' (recip 2) {-TODO-} 10 (Math.PrimeFactorisation.primeFactors algorithm i') == i' where- i' :: Integer- i' = succ $ i `mod` 1000000-- prop_smoothness algorithm i = Test.QuickCheck.label "prop_smoothness" $ (Math.PrimeFactorisation.smoothness algorithm !! (2 ^ i')) <= (2 :: Integer) where- i' :: Integer- i' = i `mod` 20-- prop_eulersTotientP algorithm i = Test.QuickCheck.label "prop_eulersTotientP" $ Math.PrimeFactorisation.eulersTotient algorithm prime == pred prime where- prime :: Integer- prime = Data.List.genericIndex Data.Numbers.Primes.primes (i `mod` 10000)-- prop_eulersTotientInequality algorithm i = i `notElem` [2, 6] ==> Test.QuickCheck.label "prop_eulersTotientInequality" $ Math.PrimeFactorisation.eulersTotient algorithm i' >= floor (sqrt $ fromIntegral i' :: Double) where- i' = succ $ i `mod` 100000-- prop_eulersTotient, prop_lagrange, prop_multiplicativeOrder, prop_perfectPower :: Math.Implementations.PrimeFactorisation.Algorithm -> Integer -> Integer -> Test.QuickCheck.Property- prop_eulersTotient algorithm i power = Test.QuickCheck.label "prop_eulersTotient" $ Math.PrimeFactorisation.eulersTotient algorithm (base ^ power') == (base ^ pred power') * pred base where- base :: Integer- base = Data.List.genericIndex Data.Numbers.Primes.primes (i `mod` 8)-- power' = succ $ power `mod` 5-- prop_lagrange algorithm base modulus = gcd base modulus' == 1 ==> Test.QuickCheck.label "prop_lagrange" $ (Math.PrimeFactorisation.eulersTotient algorithm modulus' `rem` Math.MultiplicativeOrder.multiplicativeOrder algorithm base modulus') == 0 where- modulus' :: Integer- modulus' = 2 + abs modulus-- prop_multiplicativeOrder algorithm base modulus = gcd base modulus' == 1 ==> Test.QuickCheck.label "prop_multiplicativeOrder" $ (- base ^ Math.MultiplicativeOrder.multiplicativeOrder algorithm base modulus'- ) `mod` modulus' == 1 where- modulus' :: Integer- modulus' = 2 + abs modulus-- prop_perfectPower algorithm b e = Test.QuickCheck.label "prop_perfectPower" $ foldr1 gcd (- map Data.Exponential.getExponent . Math.PrimeFactorisation.primeFactors algorithm $ (2 + b `mod` 10 :: Integer) ^ (2 + e `mod` 5)- ) > 1
− src/Factory/Test/QuickCheck/Primes.hs
@@ -1,99 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.Primes".--}--module Factory.Test.QuickCheck.Primes(--- * Constants--- defaultAlgorithm,--- * Functions- quickChecks,--- isPrime,- upperBound-) where--import qualified Control.DeepSeq-import qualified Data.Set-import qualified Factory.Data.PrimeWheel as Data.PrimeWheel-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.Primality as Math.Primality-import qualified Factory.Math.Primes as Math.Primes-import qualified Test.QuickCheck-import Test.QuickCheck((==>))-import qualified ToolShed.Defaultable--instance Test.QuickCheck.Arbitrary Math.Implementations.Primes.Algorithm.Algorithm where- arbitrary = Test.QuickCheck.oneof [- return {-to Gen-monad-} Math.Implementations.Primes.Algorithm.TurnersSieve,- (Math.Implementations.Primes.Algorithm.TrialDivision . (`mod` 10)) `fmap` Test.QuickCheck.arbitrary,- (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes . (`mod` 10)) `fmap` Test.QuickCheck.arbitrary- ]--isPrime :: (Control.DeepSeq.NFData i, Integral i, Show i) => i -> Bool-isPrime = Math.Primality.isPrime primalityAlgorithm where- primalityAlgorithm :: Math.Implementations.Primality.Algorithm Math.Implementations.PrimeFactorisation.Algorithm- primalityAlgorithm = ToolShed.Defaultable.defaultValue--upperBound :: Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Int-upperBound algorithm i = mod i $ if algorithm == Math.Implementations.Primes.Algorithm.TurnersSieve- then 8192- else 65536--defaultAlgorithm :: Math.Implementations.Primes.Algorithm.Algorithm-defaultAlgorithm = ToolShed.Defaultable.defaultValue---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks =- Test.QuickCheck.quickCheck `mapM_` [prop_isPrime, prop_isComposite]- >> Test.QuickCheck.quickCheck prop_consistency- >> Test.QuickCheck.quickCheck prop_rewriteRule- >> Test.QuickCheck.quickCheck `mapM_` [prop_sieveOfAtkin, prop_sieveOfAtkinRewrite]- where- prop_isPrime, prop_isComposite :: Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Test.QuickCheck.Property- prop_isPrime algorithm i = Test.QuickCheck.label "prop_isPrime" . all isPrime . takeWhile (<= upperBound algorithm i) $ (Math.Primes.primes algorithm :: [Int])- prop_isComposite algorithm i = Test.QuickCheck.label "prop_isComposite" . not . any isPrime . Data.Set.toList . Data.Set.difference (- Data.Set.fromList [2 .. upperBound algorithm i]- ) . Data.Set.fromList . takeWhile (<= upperBound algorithm i) $ Math.Primes.primes algorithm-- prop_consistency :: Math.Implementations.Primes.Algorithm.Algorithm -> Math.Implementations.Primes.Algorithm.Algorithm -> Int -> Test.QuickCheck.Property- prop_consistency l r i = l /= r ==> Test.QuickCheck.label "prop_consistency" . and . take (i `mod` 4096) $ zipWith (==) (Math.Primes.primes l) (Math.Primes.primes r :: [Int])-- prop_rewriteRule :: Data.PrimeWheel.NPrimes -> Int -> Test.QuickCheck.Property- prop_rewriteRule wheelSize i = Test.QuickCheck.label "prop_rewriteRule" $ toInteger (Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize') !! index :: Int) == (Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfEratosthenes wheelSize') !! index :: Integer) where- wheelSize' = wheelSize `mod` 8- index = i `mod` 131072-- prop_sieveOfAtkin, prop_sieveOfAtkinRewrite :: Int -> Test.QuickCheck.Property- prop_sieveOfAtkin i = Test.QuickCheck.label "prop_sieveOfAtkin" $ Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfAtkin prime) !! index == prime where- index = i `mod` 131072-- prime :: Integer- prime = Math.Primes.primes defaultAlgorithm !! index-- prop_sieveOfAtkinRewrite i = Test.QuickCheck.label "prop_sieveOfAtkin'" $ Math.Primes.primes (Math.Implementations.Primes.Algorithm.SieveOfAtkin $ fromIntegral prime) !! index == prime where- index = i `mod` 131072-- prime :: Int- prime = Math.Primes.primes defaultAlgorithm !! index-
− src/Factory/Test/QuickCheck/Probability.hs
@@ -1,160 +0,0 @@-{-- 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@] Defines /QuickCheck/-properties for "Math.Probability".--}--module Factory.Test.QuickCheck.Probability(--- * Functions--- normalise,- quickChecks-) where--import Control.Arrow((&&&))-import qualified Data.List-import qualified Factory.Math.Probability as Math.Probability-import qualified Factory.Math.Statistics as Math.Statistics-import Factory.Test.QuickCheck.Factorial()-import qualified System.Random-import qualified Test.QuickCheck-import Test.QuickCheck((==>))-import qualified ToolShed.Data.Pair---- | Re-profile a distribution to achieve a standard mean & variance.-normalise :: (- Eq f,- Floating f,- Math.Probability.Distribution distribution- ) => distribution -> [f] -> [f]-normalise distribution- | variance == 0 = error "Factory.Test.Quick.Probability.normalise:\tzero variance => can't stretch to one."- | otherwise = map $ (/ sqrt variance) . (+ negate mean)- where- (mean, variance) = Math.Probability.getMean &&& Math.Probability.getVariance $ distribution---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = do- randomGen <- System.Random.getStdGen-- Test.QuickCheck.quickCheck (prop_logNormalDistributionEqual randomGen) >> (Test.QuickCheck.quickCheck . ($ randomGen)) `mapM_` [- prop_logNormalDistribution,- prop_logNormalDistribution',- prop_normalDistribution,- prop_uniformDistribution- ] >> (Test.QuickCheck.quickCheck . ($ randomGen)) `mapM_` [- prop_exponentialDistribution,- prop_exponentialDistribution',- prop_poissonDistribution,- prop_poissonDistribution',- prop_shiftedGeometricDistribution,- prop_shiftedGeometricDistribution'- ]- where- isWithinTolerance :: Double -> Double -> Bool- isWithinTolerance i = (< recip i) . abs-- prop_logNormalDistribution, prop_logNormalDistribution', prop_normalDistribution, prop_uniformDistribution :: System.Random.RandomGen randomGen => randomGen -> Double -> Double -> Test.QuickCheck.Property- prop_logNormalDistribution randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 1) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where- maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)- location'- | location >= 0 = maxParameter `min` location- | otherwise = negate maxParameter `max` location-- distribution = Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2-- prop_logNormalDistribution' randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution'" . all (- >= (0 :: Double)- ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2) randomGen where- maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)-- location'- | location >= 0 = maxParameter `min` location- | otherwise = negate maxParameter `max` location---- The mean & standard-deviation are equal when scale^2 == ln 2, but this seems to break-down when the mean is close to zero.- prop_logNormalDistributionEqual :: System.Random.RandomGen randomGen => randomGen -> Double -> Test.QuickCheck.Property- prop_logNormalDistributionEqual randomGen location = location' > 1 ==> Test.QuickCheck.label "prop_logNormalDistributionEqual" . (- < (recip 1000000 :: Double)- ) . pred . abs . uncurry (/) . (- Math.Statistics.getMean &&& Math.Statistics.getStandardDeviation- ) $ take 10000 (- Math.Probability.generatePopulation (Math.Probability.LogNormalDistribution location' $ log 2) randomGen :: [Double]- ) where- maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)-- location'- | location >= 0 = maxParameter `min` location- | otherwise = negate maxParameter `max` location-- prop_normalDistribution randomGen mean variance = variance /= 0 ==> Test.QuickCheck.label "prop_normalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where- distribution = Math.Probability.NormalDistribution mean $ abs variance-- prop_uniformDistribution randomGen min' max' = min' /= max' ==> Test.QuickCheck.label "prop_uniformDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where- [min'', max''] = Data.List.sort [min', max']- distribution = Math.Probability.UniformDistribution (min'', max'')-- prop_exponentialDistribution, prop_exponentialDistribution', prop_poissonDistribution, prop_poissonDistribution', prop_shiftedGeometricDistribution, prop_shiftedGeometricDistribution' :: System.Random.RandomGen randomGen => randomGen -> Double -> Test.QuickCheck.Property- prop_exponentialDistribution randomGen lambda = Test.QuickCheck.label "prop_exponentialDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where- distribution = Math.Probability.ExponentialDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}-- prop_exponentialDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_exponentialDistribution'" . all (- >= (0 :: Double)- ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ExponentialDistribution $ abs lambda) randomGen-- prop_poissonDistribution randomGen lambda = Test.QuickCheck.label "prop_poissonDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where- distribution = Math.Probability.PoissonDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}-- prop_poissonDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_poissonDistribution'" . all (- >= (0 :: Double)- ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.PoissonDistribution $ abs lambda) randomGen-- prop_shiftedGeometricDistribution randomGen probability = probability' /= 1 ==> Test.QuickCheck.label "prop_shiftedGeometricDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (- Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation -- Both of which, having been normalised, should be zero.- ) . (- normalise distribution :: [Double] -> [Double]- ) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where- probability' = recip . succ $ abs probability -- Semi-closed unit-interval (0, 1].- distribution = Math.Probability.ShiftedGeometricDistribution probability'-- prop_shiftedGeometricDistribution' randomGen probability = Test.QuickCheck.label "prop_shiftedGeometricDistribution'" . all (- >= (1 :: Double)- ) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ShiftedGeometricDistribution probability') randomGen where- probability' = recip . succ $ abs probability -- Semi-closed unit-interval (0, 1].-
− src/Factory/Test/QuickCheck/QuickChecks.hs
@@ -1,70 +0,0 @@-{-- Copyright (C) 2011 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 Factory.Test.QuickCheck.QuickChecks(--- * Functions- run-) where--import qualified Control.Arrow-import qualified Factory.Test.QuickCheck.ArithmeticGeometricMean-import qualified Factory.Test.QuickCheck.Factorial-import qualified Factory.Test.QuickCheck.Hyperoperation-import qualified Factory.Test.QuickCheck.Interval-import qualified Factory.Test.QuickCheck.MonicPolynomial-import qualified Factory.Test.QuickCheck.PerfectPower-import qualified Factory.Test.QuickCheck.Pi-import qualified Factory.Test.QuickCheck.Polynomial-import qualified Factory.Test.QuickCheck.Power-import qualified Factory.Test.QuickCheck.Primality-import qualified Factory.Test.QuickCheck.PrimeFactorisation-import qualified Factory.Test.QuickCheck.Primes-import qualified Factory.Test.QuickCheck.Probability-import qualified Factory.Test.QuickCheck.Radix-import qualified Factory.Test.QuickCheck.SquareRoot-import qualified Factory.Test.QuickCheck.Statistics-import qualified Factory.Test.QuickCheck.Summation---- | Run the /quickChecks/-functions for modules supporting this feature.-run :: IO ()-run = mapM_ (- uncurry (>>) . Control.Arrow.first putStrLn- ) [- ("ArithmeticGeometricMean", Factory.Test.QuickCheck.ArithmeticGeometricMean.quickChecks),- ("Factorial", Factory.Test.QuickCheck.Factorial.quickChecks),- ("Hyperoperation", Factory.Test.QuickCheck.Hyperoperation.quickChecks),- ("Interval", Factory.Test.QuickCheck.Interval.quickChecks),- ("MonicPolynomial", Factory.Test.QuickCheck.MonicPolynomial.quickChecks),- ("PerfectPower", Factory.Test.QuickCheck.PerfectPower.quickChecks),- ("Pi", Factory.Test.QuickCheck.Pi.quickChecks),- ("Polynomial", Factory.Test.QuickCheck.Polynomial.quickChecks),- ("Power", Factory.Test.QuickCheck.Power.quickChecks),- ("Primality", Factory.Test.QuickCheck.Primality.quickChecks),- ("PrimeFactorisation", Factory.Test.QuickCheck.PrimeFactorisation.quickChecks),- ("Primes", Factory.Test.QuickCheck.Primes.quickChecks),- ("Probability", Factory.Test.QuickCheck.Probability.quickChecks),- ("Radix", Factory.Test.QuickCheck.Radix.quickChecks),- ("SquareRoot", Factory.Test.QuickCheck.SquareRoot.quickChecks),- ("Statistics", Factory.Test.QuickCheck.Statistics.quickChecks),- ("Summation", Factory.Test.QuickCheck.Summation.quickChecks)- ]-
− src/Factory/Test/QuickCheck/Radix.hs
@@ -1,46 +0,0 @@-{-- Copyright (C) 2011 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 "Math.Radix".--}--module Factory.Test.QuickCheck.Radix(--- * Types--- ** Type-synonyms--- Testable,--- * Functions- quickChecks-) where--import qualified Factory.Math.Radix as Math.Radix-import qualified Test.QuickCheck-import Test.QuickCheck((==>))--type Testable = (Int, Integer) -> Test.QuickCheck.Property---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_reversable, prop_digitalRoot] where- prop_reversable, prop_digitalRoot :: Testable- prop_reversable (b, n) = abs base > 1 ==> Test.QuickCheck.label "prop_reversable" $ Math.Radix.fromBase base (Math.Radix.toBase base n) == n where- base = (b `mod` 73) - 36-- prop_digitalRoot (_, n) = Test.QuickCheck.label "prop_digitalRoot" $ Math.Radix.digitalRoot n' == 9 where- n' = 9 * succ (abs n)-
− src/Factory/Test/QuickCheck/SquareRoot.hs
@@ -1,86 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2011-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' and defines /QuickCheck/-properties for "Math.Implementations.SquareRoot".--}--module Factory.Test.QuickCheck.SquareRoot(--- * Types--- ** Type-synonyms--- Testable,--- * Functions- quickChecks-) where--import Data.Ratio((%))-import qualified Data.Ratio-import qualified Factory.Math.Implementations.SquareRoot as Math.Implementations.SquareRoot-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Precision as Math.Precision-import qualified Factory.Math.SquareRoot as Math.SquareRoot-import qualified Test.QuickCheck--instance Test.QuickCheck.Arbitrary (Math.Implementations.SquareRoot.Algorithm) where- arbitrary = Test.QuickCheck.oneof [- Test.QuickCheck.elements [- Math.Implementations.SquareRoot.BakhshaliApproximation,- Math.Implementations.SquareRoot.ContinuedFraction,- Math.Implementations.SquareRoot.HalleysMethod,- Math.Implementations.SquareRoot.NewtonRaphsonIteration- ],- Math.Implementations.SquareRoot.TaylorSeries `fmap` Test.QuickCheck.elements [2 .. 32]- ]--type Testable = (Math.Implementations.SquareRoot.Algorithm, Math.Precision.DecimalDigits, Rational) -> Test.QuickCheck.Property---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_accuracy, prop_factorable, prop_perfectSquare] where- prop_accuracy, prop_factorable, prop_perfectSquare :: Testable- prop_accuracy (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_accuracy" . (>= requiredDecimalDigits) . Math.SquareRoot.getAccuracy operand' $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand' where- requiredDecimalDigits :: Math.Precision.DecimalDigits- requiredDecimalDigits = succ $ decimalDigits `mod` 1024-- operand' :: Rational- operand' = abs operand-- prop_factorable (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_factorable" . (<= 5) . (- * 10 ^ requiredDecimalDigits -- Promote the relative error.- ) . abs $ 1 - (- Math.SquareRoot.squareRoot algorithm requiredDecimalDigits (- toRational $ Data.Ratio.numerator operand'- ) / Math.SquareRoot.squareRoot algorithm requiredDecimalDigits (- toRational $ Data.Ratio.denominator operand'- )- ) / Math.SquareRoot.squareRoot algorithm requiredDecimalDigits operand' where- requiredDecimalDigits :: Math.Precision.DecimalDigits- requiredDecimalDigits = succ $ decimalDigits `mod` 1024-- operand' :: Rational- operand' = succ $ abs operand-- prop_perfectSquare (algorithm, decimalDigits, operand) = Test.QuickCheck.label "prop_perfectSquare" . Math.SquareRoot.isPrecise perfectSquare $ Math.SquareRoot.squareRoot algorithm requiredDecimalDigits perfectSquare where- requiredDecimalDigits :: Math.Precision.DecimalDigits- requiredDecimalDigits = succ $ decimalDigits `mod` 32768-- operand', perfectSquare :: Rational- operand' = (abs (Data.Ratio.numerator operand) `min` (2 ^ (32 :: Int))) % (abs (Data.Ratio.denominator operand) `min` (2 ^ (32 :: Int))) -- Avoid floating-point rounding-errors in 'Math.SquareRoot.rSqrt'.- perfectSquare = Math.Power.square operand'-
− src/Factory/Test/QuickCheck/Statistics.hs
@@ -1,112 +0,0 @@-{-- Copyright (C) 2011-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@] Defines /QuickCheck/-properties for "Math.Statistics".--}--module Factory.Test.QuickCheck.Statistics(--- * Functions- quickChecks-) where--import qualified Data.Array-import qualified Data.List-import qualified Data.Map-import qualified Data.Numbers.Primes-import qualified Data.Set-import qualified Factory.Math.Implementations.Factorial as Math.Implementations.Factorial-import qualified Factory.Math.Power as Math.Power-import qualified Factory.Math.Statistics as Math.Statistics-import Factory.Test.QuickCheck.Factorial()-import qualified Test.QuickCheck-import Test.QuickCheck((==>))---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_nC0, prop_nC1, prop_sum]- >> Test.QuickCheck.quickCheck `mapM_` [prop_symmetry, prop_prime]- >> Test.QuickCheck.quickCheck `mapM_` [prop_nP0, prop_nP1]- >> Test.QuickCheck.quickCheck `mapM_` [prop_zeroVariance, prop_zeroAverageAbsoluteDeviation]- >> Test.QuickCheck.quickCheck `mapM_` [prop_balance, prop_varianceRelocated, prop_varianceScaled, prop_varianceOrder, prop_equivalence, prop_varianceOfArray, prop_varianceOfMap, prop_meanOfSet]- >> Test.QuickCheck.quickCheck prop_weightedMeanRational- >> Test.QuickCheck.quickCheck prop_weightedMeanInteger- >> Test.QuickCheck.quickCheck prop_weightedMeanUniformDenominator- where- prop_nC0, prop_nC1, prop_sum :: Math.Implementations.Factorial.Algorithm -> Integer -> Test.QuickCheck.Property- prop_nC0 algorithm n = Test.QuickCheck.label "prop_nC0" $ Math.Statistics.nCr algorithm (abs n) 0 == 1-- prop_nC1 algorithm i = Test.QuickCheck.label "prop_nC1" $ Math.Statistics.nCr algorithm n 1 == n where- n = succ $ abs i-- prop_sum algorithm i = Test.QuickCheck.label "prop_sum" $ sum (Math.Statistics.nCr algorithm n `map` [0 .. n]) == 2 ^ n where- n = succ $ abs i-- prop_symmetry, prop_prime :: Math.Implementations.Factorial.Algorithm -> (Integer, Integer) -> Test.QuickCheck.Property- prop_symmetry algorithm (i, j) = Test.QuickCheck.label "prop_symmetry" $ Math.Statistics.nCr algorithm n r == Math.Statistics.nCr algorithm n (n - r) where- [r, n] = Data.List.sort $ map abs [i, j]-- prop_prime algorithm (i, j) = r `notElem` [0, n] ==> Test.QuickCheck.label "prop_prime" $ (Math.Statistics.nCr algorithm n r `mod` n) == 0 where- n = Data.Numbers.Primes.primes !! fromIntegral (i `mod` 500000)- r = j `mod` n -- Ensure r is smaller than n.-- prop_nP0, prop_nP1 :: Integer -> Test.QuickCheck.Property- prop_nP0 n = Test.QuickCheck.label "prop_nP0" $ Math.Statistics.nPr (abs n) 0 == 1-- prop_nP1 i = Test.QuickCheck.label "prop_nP1" $ Math.Statistics.nPr n 1 == n where- n = succ $ abs i-- prop_zeroVariance, prop_zeroAverageAbsoluteDeviation :: Rational -> Test.QuickCheck.Property- prop_zeroVariance x = Test.QuickCheck.label "prop_zeroVariance" $ Math.Statistics.getVariance (replicate 32 x) == (0 :: Rational)- prop_zeroAverageAbsoluteDeviation x = Test.QuickCheck.label "zeroAverageAbsoluteDeviation" $ Math.Statistics.getAverageAbsoluteDeviation (replicate 32 x) == (0 :: Rational)-- prop_balance, prop_varianceRelocated, prop_varianceScaled, prop_varianceOrder, prop_equivalence, prop_varianceOfMap, prop_meanOfSet, prop_varianceOfArray :: [Integer] -> Test.QuickCheck.Property- prop_balance l = not (null l) ==> Test.QuickCheck.label "prop_balance" . (== 0) . abs . sum $ map (\i -> toRational i - Math.Statistics.getMean l) l- prop_varianceRelocated l = not (null l) ==> Test.QuickCheck.label "prop_varianceRelocated" $ (Math.Statistics.getVariance l :: Rational) == Math.Statistics.getVariance (map succ l)- prop_varianceScaled l = not (null l) ==> Test.QuickCheck.label "prop_varianceScaled" $ (4 * Math.Statistics.getVariance l :: Rational) == Math.Statistics.getVariance (map (* 2) l)- prop_varianceOrder l = not (null l) ==> Test.QuickCheck.label "prop_varianceOrder" $ Math.Statistics.getVariance l == (Math.Statistics.getVariance (reverse l) :: Rational)- prop_equivalence l = not (null l) ==> Test.QuickCheck.label "prop_equivalence" $ Math.Statistics.getVariance l == Math.Statistics.getMean (map Math.Power.square l) - Math.Power.square (Math.Statistics.getMean l :: Rational)- prop_varianceOfArray l = not (null l) ==> Test.QuickCheck.label "prop_varianceOfArray" $ Math.Statistics.getVariance (Data.Array.array (1, length l) $ zip [1 ..] l) == (Math.Statistics.getVariance l :: Rational)- prop_varianceOfMap l = not (null l) ==> Test.QuickCheck.label "prop_varianceOfMap" $ Math.Statistics.getVariance (Data.Map.fromList $ zip [0 :: Int ..] l) == (Math.Statistics.getVariance l :: Rational)- prop_meanOfSet l = not (null l') ==> Test.QuickCheck.label "prop_meanOfSet" $ Math.Statistics.getMean (Data.Set.fromList l') == (Math.Statistics.getMean l' :: Rational) where- l' = Data.List.nub l-- prop_weightedMeanRational :: [(Rational, Rational)] -> Test.QuickCheck.Property- prop_weightedMeanRational assoc = (denominator /= 0) ==> Test.QuickCheck.label "prop_weightedMeanRational" $ Math.Statistics.getWeightedMean assoc == (- sum (map (uncurry (*)) assoc) / denominator- ) where- denominator = sum $ map snd assoc--- prop_weightedMeanInteger :: [(Integer, Integer)] -> Test.QuickCheck.Property- prop_weightedMeanInteger assoc = (denominator /= 0) ==> Test.QuickCheck.label "prop_weightedMeanInteger" $ Math.Statistics.getWeightedMean assoc == (- toRational (- sum $ map (- uncurry (*)- ) assoc- ) / toRational denominator- ) where- denominator = sum $ map snd assoc-- prop_weightedMeanUniformDenominator :: [Rational] -> Integer -> Test.QuickCheck.Property- prop_weightedMeanUniformDenominator numerators i = (not (null numerators) && i /= 0) ==> Test.QuickCheck.label "prop_weightedMeanUniformDenominator" $ Math.Statistics.getWeightedMean (- zip numerators $ repeat i- ) == (- Math.Statistics.getMean numerators :: Rational- )-
− src/Factory/Test/QuickCheck/Summation.hs
@@ -1,42 +0,0 @@-{-- Copyright (C) 2011 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 "Math.Summation".--}--module Factory.Test.QuickCheck.Summation(--- * Functions- quickChecks-) where--import qualified Factory.Math.Summation as Math.Summation-import qualified Test.QuickCheck-import Test.QuickCheck((==>))---- | Defines invariant properties.-quickChecks :: IO ()-quickChecks = Test.QuickCheck.quickCheck `mapM_` [prop_sum, prop_sumR] where- prop_sum, prop_sumR :: Int -> [Rational] -> Test.QuickCheck.Property- prop_sum chunkSize l = not (null l) ==> Test.QuickCheck.label "prop_sum" $ Math.Summation.sum' chunkSize' l == sum l where- chunkSize' = 2 + (chunkSize `mod` length l)-- prop_sumR chunkSize l = not (null l) ==> Test.QuickCheck.label "prop_sumR" $ Math.Summation.sumR chunkSize' l == sum l where- chunkSize' = 2 + (chunkSize `mod` length l)--
− src/Main.hs
@@ -1,241 +0,0 @@-{-- 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.-