{- 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 . -} {- | [@AUTHOR@] Dr. Alistair Ward [@DESCRIPTION@] Defines the classes of /Pi/-algorithm which have been implemented. -} module Factory.Math.Pi( -- * Type-classes Algorithm(..), -- * Types -- ** Data-types Category(..) ) where import qualified Data.Ratio import qualified Factory.Math.Precision as Math.Precision import qualified ToolShed.Defaultable as 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 Algorithm algorithm where openR :: algorithm -> Math.Precision.DecimalDigits -> Data.Ratio.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) $ decimalDigits - 1 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 (decimalDigits + 1) $ 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 -- ^ . | Borwein borwein -- ^ . | Ramanujan ramanujan -- ^ . | Spigot spigot -- ^ Algorithms from which the digits of /Pi/ slowly drip, one by one. deriving (Eq, Read, Show) instance ( Defaultable.Defaultable agm, Defaultable.Defaultable bbp, Defaultable.Defaultable borwein, Defaultable.Defaultable ramanujan, Defaultable.Defaultable spigot ) => Defaultable.Defaultable (Category agm bbp borwein ramanujan spigot) where defaultValue = BBP Defaultable.defaultValue instance ( Algorithm agm, Algorithm bbp, Algorithm borwein, Algorithm ramanujan, Algorithm spigot ) => Algorithm (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 (decimalDigits - 1) (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) $ decimalDigits - 1