{- Taken from Doug McIlroy's paper
"Power series, power serious"
JFP 9(3) May 1999
-}
module Main where
import IO
import Ratio
infixl 7 .*
infixr 5 :+:
default (Integer, Rational, Double)
main = do { putStrLn (show (extract 50 (sinx - sqrt (1-cosx^2)))) ;
putStrLn (show (extract 50 (sinx/cosx - revert (integral (1/(1+x^2)))))) ;
putStrLn (show (extract 50 ts)) ;
putStrLn (show (extract 50 tree))
}
-- From Section 6
tree = 0 :+: forest
forest = compose list tree
list = 1 :+: list
ts = 1 :+: ts^2
-- The main implementation follows
data Ps a = Pz | a :+: Ps a
extract :: Int -> Ps a -> [a]
extract 0 ps = []
extract n Pz = []
extract n (x :+: ps) = x : extract (n-1) ps
deriv:: Num a => Ps a -> Ps a
integral:: Fractional a => Ps a -> Ps a
compose:: Num a => Ps a -> Ps a -> Ps a
revert:: Fractional a => Ps a -> Ps a
toList:: Num a => Ps a -> [a]
takePs:: Num a => Int -> Ps a -> [a]
(.*):: Num a => a -> Ps a -> Ps a
x:: Num a => Ps a
expx, sinx, cosx:: Fractional a => Ps a
c .* Pz = Pz
c .* (f :+: fs) = c*f :+: c.*fs
x = 0 :+: 1 :+: Pz
toList Pz = [] --(0)
toList (f :+: fs) = f : (toList fs)
takePs n fs = take n (toList fs)
instance Num a => Eq (Ps a) where --(1)
Pz == Pz = True
Pz == (f :+: fs) = f==0 && Pz==fs
fs == Pz = Pz==fs
(f :+: fs) == (g :+: gs) = f==g && fs==gs
instance Num a => Show (Ps a) where --(2)
showsPrec p Pz = showsPrec p [0]
showsPrec p fs = showsPrec p (toList fs)
instance Num a => Num (Ps a) where
negate Pz = Pz
negate (f :+: fs) = -f :+: -fs
Pz + gs = gs
fs + Pz = fs
(f :+: fs) + (g :+: gs) = f+g :+: fs+gs
Pz * _ = Pz
_ * Pz = Pz
(f :+: fs) * (g :+: gs) =
f*g :+: f.*gs + g.*fs + x*fs*gs --(3)
fromInteger 0 = Pz
fromInteger c = fromInteger c :+: Pz
instance Fractional a => Fractional (Ps a) where
recip fs = 1/fs
Pz/Pz = error "power series 0/0"
Pz / (0 :+: gs) = Pz / gs
Pz / _ = Pz
(0 :+: fs) / (0 :+: gs) = fs / gs
(f :+: fs) / (g :+: gs) = let q = f/g in
q :+: (fs - q.*gs)/(g :+: gs)
compose Pz _ = Pz
compose (f :+: _) Pz = f :+: Pz
compose (f :+: fs) (0 :+: gs) = f :+: gs*(compose fs (0 :+: gs))
compose (f :+: fs) gs = (f :+: Pz) + gs*(compose fs gs) --(4)
revert (0 :+: fs) = rs where
rs = 0 :+: 1/(compose fs rs)
revert (f0 :+: f1 :+: Pz) = -1/f1 :+: 1/f1 :+: Pz --(5)
deriv Pz = Pz
deriv (_ :+: fs) = deriv1 fs 1 where
deriv1 Pz _ = Pz
deriv1 (f :+: fs) n = n*f :+: (deriv1 fs (n+1))
integral fs = 0 :+: (int1 fs 1) where --(6)
int1 Pz _ = Pz
int1 (f :+: fs) n = f/n :+: (int1 fs (n+1))
instance Fractional a => Floating (Ps a) where
sqrt Pz = Pz
sqrt (0 :+: 0 :+: fs) = 0 :+: (sqrt fs)
sqrt (1 :+: fs) = qs where
qs = 1 + integral((deriv (1:+:fs))/(2.*qs))
expx = 1 + (integral expx)
sinx = integral cosx
cosx = 1 - (integral sinx)
--(0) Convert power series to a list; used in printing.
--(1) Equality works on polynomials; diverges otherwise.
--(2) Specifies how to print the new data type.
--(3) x*fs*gs replaces 0:fs*gs to avoid extra zero
-- at end of product of polynomials; it works
-- because x is a finite series.
--(4) This extra production works for the composition
-- of polynomials with non-zero constant term,
-- but not for infinite series.
--(5) Special case for reverting a linear function.
--(6) There is no special case for (integral Pz)
-- because this would defeat the property that
-- (integral) emits one term before evaluating
-- its operand--a property used in solving
-- differential equations.