\chapter{Implementation of @RealReal@'s} \begin{verbatim} $Log: RealReals.lhs,v $ Revision 1.1 2004/08/05 11:12:53 malcolm Add a regression testsuite for the nhc98 compiler. It isn't very good, but it is better than nothing. I've been using it for about four years on nightly builds, so it's about time it entered the repository! It includes a slightly altered version of the nofib suite. Instructions are in the README. Revision 1.3 1999/11/02 16:10:42 simonpj Haskell 98 changes Revision 1.2 1996/07/25 21:30:49 partain Bulk of final changes for 2.01 Revision 1.1 1996/01/08 20:05:19 partain Initial revision \end{verbatim} > module RealReals (RealReal) where > import Transcendentals > import Ratio > import List( genericLength ) > import Numeric( readSigned ) > data RealReal = RealInt Integer | > RealRat QRational | > RealCF ContinuedFraction \section{@RealReal@ is an instance of @Eq@} It should be remembered that computable real numbers do not permit computable comparisons, but never mind! > instance Eq RealReal where > (==) = realRealComp (==) > (/=) = realRealComp (/=) The actual comparison is carried out by subtracting @y@ from @x@ and testing the result at our current accuracy. > realRealComp :: (QRational -> QRational -> Bool) -> -- Operator > RealReal -> -- First Argument > RealReal -> -- Second Argument > Bool -- Result > realRealComp op x y = op (makeRational (x-y)) 0 \section{@RealReal@ is an instance of @Ord@} Once more, these operations are not computable. > instance Ord RealReal where > (<=) = realRealComp (<=) > (<) = realRealComp (<) > (>=) = realRealComp (>=) > (>) = realRealComp (>) \section{@RealReal@ is an instance of @Enum@} We make @RealReal@ an instance of @Enum@. > instance Enum RealReal where > enumFrom = iterate (+1) > enumFromThen n m = iterate (+(m-n)) n \section{@RealReal@ is an instance of @Num@} In this section we come to operations that {\em are} computable. > instance Num RealReal where > x + y = realRealAdd x y > negate x = realRealNegate x > x * y = realRealMultiply x y > abs x = realRealAbs x > signum x = realRealSignum x > fromInteger x = RealInt x \subsection{Addition} > realRealAdd :: RealReal -> RealReal -> RealReal > realRealAdd (RealInt x) (RealInt y) = RealInt (x+y) > realRealAdd (RealInt x) (RealRat y) = wrapRat ((x%%1)+y) > realRealAdd (RealInt x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where h = ([1,x],[0,1]) > realRealAdd (RealRat x) (RealInt y) = wrapRat (x+(y%%1)) > realRealAdd (RealRat x) (RealRat y) = wrapRat (x+y) > realRealAdd (RealRat x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where nx = qNumerator x > dx = qDenominator x > h = ([dx,nx],[0,dx]) > realRealAdd (RealCF x) (RealInt y) = RealCF (algebraicAlgorithm h x) > where h = ([1,y],[0,1]) > realRealAdd (RealCF x) (RealRat y) = RealCF (algebraicAlgorithm h x) > where ny = qNumerator y > dy = qDenominator y > h = ([dy,ny],[0,dy]) > realRealAdd (RealCF x) (RealCF y) = RealCF (quadraticAlgorithm h x y) > where h = ([0,1,1,0],[0,0,0,1]) The @wrapRat@ function ensures that a rational number that is in fact an integer, is so treated. > wrapRat :: QRational -> RealReal > wrapRat x = if qDenominator x == 1 then RealInt (qNumerator x) else > RealRat x \subsection{Negation} Here we are using the symmetry of the continued fraction representation, so that we can simply map @negate@ over the continued fraction. > realRealNegate :: RealReal -> RealReal > realRealNegate (RealInt x) = RealInt (negate x) > realRealNegate (RealRat x) = RealRat (negate x) > realRealNegate (RealCF x) = RealCF (map negate x) \subsection{Multiplication} > realRealMultiply :: RealReal -> RealReal -> RealReal > realRealMultiply (RealInt x) (RealInt y) = RealInt (x*y) > realRealMultiply (RealInt x) (RealRat y) = wrapRat ((x%%1)*y) > realRealMultiply (RealInt x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where h = ([x,0],[0,1]) > realRealMultiply (RealRat x) (RealInt y) = wrapRat (x*(y%%1)) > realRealMultiply (RealRat x) (RealRat y) = wrapRat (x*y) > realRealMultiply (RealRat x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where nx = qNumerator x > dx = qDenominator x > h = ([nx,0],[0,dx]) > realRealMultiply (RealCF x) (RealInt y) = RealCF (algebraicAlgorithm h x) > where h = ([y,0],[0,1]) > realRealMultiply (RealCF x) (RealRat y) = RealCF (algebraicAlgorithm h x) > where ny = qNumerator y > dy = qDenominator y > h = ([ny,0],[0,dy]) > realRealMultiply (RealCF x) (RealCF y) = RealCF (quadraticAlgorithm h x y) > where h = ([1,0,0,0],[0,0,0,1]) \subsection{Abs} We have a problem here. If @signum x == -1@ then we negate the continued fraction; otherwise we leave the continued fraction alone. > realRealAbs :: RealReal -> RealReal > realRealAbs x = if realRealSignum x < 0 then realRealNegate x else x \subsection{Signum} This isn't computable either, so again {\it caveat emptor}. > realRealSignum :: RealReal -> RealReal > realRealSignum x = wrapRat (signum (makeRational x)) \section{@RealReal@ is an instance of @Real@} This is yet another non-computable operation that is fudged. > instance Real RealReal where > toRational rx = qNumerator x % qDenominator x > where x = makeRational rx \section{@RealReal@ is an instance of @Fractional@} Fortunately these operations are computable. > instance Fractional RealReal where > x / y = realRealDivide x y > fromRational x = wrapRat (numerator x %% denominator x) > realRealDivide :: RealReal -> RealReal -> RealReal > realRealDivide (RealInt x) (RealInt y) = wrapRat (x%%y) > realRealDivide (RealInt x) (RealRat y) = wrapRat ((x%%1)/y) > realRealDivide (RealInt x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where h = ([0,x],[1,0]) > realRealDivide (RealRat x) (RealInt y) = wrapRat (x/(y%%1)) > realRealDivide (RealRat x) (RealRat y) = wrapRat (x/y) > realRealDivide (RealRat x) (RealCF y) = RealCF (algebraicAlgorithm h y) > where nx = qNumerator x > dx = qDenominator x > h = ([0,nx],[dx,0]) > realRealDivide (RealCF x) (RealInt y) = RealCF (algebraicAlgorithm h x) > where h = ([1,0],[0,y]) > realRealDivide (RealCF x) (RealRat y) = RealCF (algebraicAlgorithm h x) > where ny = qNumerator y > dy = qDenominator y > h = ([dy,0],[0,ny]) > realRealDivide (RealCF x) (RealCF y) = RealCF (quadraticAlgorithm h x y) > where h = ([0,0,1,0],[0,1,0,0]) \section{@RealReal@ is an instance of @Floating@} > instance Floating RealReal where > pi = realRealPi > exp = realRealExp > log = realRealLog > sqrt = realRealSqrt > sin = realRealSin > cos = realRealCos > tan = realRealTan > asin = realRealAsin > acos = realRealAcos > atan = realRealAtan > sinh = realRealSinh > cosh = realRealCosh > tanh = realRealTanh > asinh = realRealAsinh > acosh = realRealAcosh > atanh = realRealAtanh Here we notice that $\pi$ is a computable real number. > realRealPi :: RealReal > realRealPi = 4 * (RealCF atan1) \subsection{logarithms and exponentials} > realRealExp :: RealReal -> RealReal > realRealExp (RealInt x) = RealCF exp1 ^^ x > realRealExp (RealRat x) = RealCF exp1 ^^ i * RealCF (expQ (x-(i%%1))) > where i = qRound x > realRealExp (RealCF x) = RealCF exp1 ^^ i * RealCF (expR x') > where (i,x') = integerFraction x > realRealLog :: RealReal -> RealReal > realRealLog (RealInt x) > = if x==1 then RealInt 0 else RealCF log10 * RealInt i + RealCF (logQ x') > where i = genericLength (show x):: Integer > x' = x %% (10^i) > realRealLog (RealRat x) > = RealCF log10 * RealInt i + RealCF (logQ x') > where i = if x > 1 then f x else negate (f (1/x)) > f :: QRational -> Integer > f x = genericLength (show (qRound x + 1)) > x' = x / ((10%%1)^^i) > realRealLog (RealCF x) > = RealCF log10 * RealInt i + RealCF (logR x') > where i = if head x /= 0 then f x else negate (f (tail x)) > f :: ContinuedFraction -> Integer > f x = genericLength (show (i + 1)) where (i,_) = integerFraction x > m = (1%%10)^^i > x' = algebraicAlgorithm ([qNumerator m,0],[0,qDenominator m]) x > realRealSqrt :: RealReal -> RealReal > realRealSqrt (RealInt x) = RealCF (cfRat2CFSqrt (x%%1)) > realRealSqrt (RealRat x) = RealCF (cfRat2CFSqrt x) > realRealSqrt rx@(RealCF _) = exp (log rx / 2) \subsection{Trigonometry} The basic idea for @sin@ and @cos@ is to use the following formul\ae . \[\sin(x) = \frac{2 \tan(x/2)}{1+\tan^2(x/2)} \mbox{ and } \cos(x) = \frac{1-\tan^2(x/2)}{1+\tan^2(x/2)}\] Unfortunately, we observe that there is a problem at the points $(2k+1)\pi / 2$ since the @tan@ function switches from $+\infty$ to $-\infty$. To cure this we observe the following identities that are based on the value of the cotangent function: \[\sin(x) = \frac{2 \cot(x/2)}{1+\cot^2(x/2)} \mbox{ and } \cos(x) = \frac{\cot^2(x/2)-1}{\cot^2(x/2)+1}\] > realRealSin :: RealReal -> RealReal > realRealSin rx@(RealCF _) > = 2*c/(1+c*c) > where (RealCF x2) = rx/2 > cotx2 = cotR x2 > tanx2 = tanR x2 > cf = if head cotx2 == 0 then cotx2 else tanx2 > c = RealCF cf > realRealSin x = 2*t / (1+t*t) > where t = tan (x/2) > realRealCos :: RealReal -> RealReal > realRealCos rx@(RealCF _) > = if head cotx2 == 0 then (c2-1)/(c2+1) else (1-c2)/(1+c2) > where (RealCF x2) = rx/2 > cotx2 = cotR x2 > tanx2 = tanR x2 > cf = if head cotx2 == 0 then cotx2 else tanx2 > c = RealCF cf > c2 = c*c > realRealCos x = (1-t2) / (1+t2) > where t = tan (x/2) > t2 = t*t > realRealTan :: RealReal -> RealReal > realRealTan (RealInt x) = if x==0 then RealInt 0 else RealCF (tanQ (x%%1)) > realRealTan (RealRat x) = RealCF (tanQ x) > realRealTan rx@(RealCF _) > = RealCF (tanR x) > where (RealCF x') = rx / pi > (i,_) = integerFraction x' > (RealCF x) = rx - (RealInt i * pi) \subsection{Inverse Trig} > realRealAsin :: RealReal -> RealReal > realRealAsin x = atan (x / sqrt (1-x*x)) > realRealAcos :: RealReal -> RealReal > realRealAcos x = atan (sqrt (1-x*x) / x) > realRealAtan :: RealReal -> RealReal > realRealAtan (RealInt x) = if x==0 then RealInt 0 else RealCF (atanQ (x%%1)) > realRealAtan (RealRat x) = RealCF (atanQ x) > realRealAtan (RealCF x) = RealCF (atanR x) \subsection{Hyperbolic Trig Functions} > realRealSinh :: RealReal -> RealReal > realRealSinh x = (ex - (1 / ex)) / 2 > where ex = exp x > realRealCosh :: RealReal -> RealReal > realRealCosh x = (ex + (1 / ex)) / 2 > where ex = exp x > realRealTanh :: RealReal -> RealReal > realRealTanh x = (ex - ex') / (ex + ex') > where ex = exp x > ex' = 1/ex \subsection{Inverse Hyperbolic Trig Functions} > realRealAsinh :: RealReal -> RealReal > realRealAsinh x = log (x + sqrt (x*x+1)) > realRealAcosh :: RealReal -> RealReal > realRealAcosh x = log (x + sqrt (x*x-1)) This is not quite true as the root needs to be real. > realRealAtanh :: RealReal -> RealReal > realRealAtanh x = log ((1+x)/(1-x)) / 2 \section{@RealReal@ is an instance of @Text@} > instance Read RealReal where > readsPrec p = readSigned readRealReal > instance Show RealReal where > showsPrec p rx = showString (showRat p (makeRational rx)) > makeRational :: RealReal -> QRational > makeRational (RealInt x) = (x%%1) > makeRational (RealRat x) = x > makeRational (RealCF x) = cf2Rat x \subsection{reading @RealReal@'s} We can't guarantee that the invariant @read(show x) = x@ once we consider computable real numbers -- so we don't! > readRealReal :: ReadS RealReal > readRealReal r = [(RealInt x, s) | (x,s) <- reads r] \subsection{printing @RealReal@'s} There's an irritating feature in the Haskell standard prelude that requires us to deal with the minus sign here rather than leaving it to the standard mechanism. > showRat :: Int -> QRational -> String > showRat p x = if s == "-" && p > 6 then "(" ++ res ++ ")" else res > where z = qRound (x/accuracy) > s = if z < 0 then "-" else "" > zs = show (abs z) > l = length zs > zs' = pad ++ zs > pad = if (l <= decimals) > then take (decimals-l+1) zeros else "" > zeros = repeat '0' > (i,f) = splitAt (length zs' - decimals) zs' > res = s ++ i ++ "." ++ f