module Factory.Math.ArithmeticGeometricMean(
ArithmeticMean,
GeometricMean,
AGM,
convergeToAGM,
spread,
getArithmeticMean,
getGeometricMean,
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
type ArithmeticMean = Rational
type GeometricMean = Rational
type AGM = (ArithmeticMean, GeometricMean)
getArithmeticMean :: AGM -> ArithmeticMean
getArithmeticMean = fst
getGeometricMean :: AGM -> GeometricMean
getGeometricMean = snd
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 )
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
spread :: AGM -> Rational
spread = uncurry ()
isValid :: AGM -> Bool
isValid (a, g) = all (>= 0) [a, g]