{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExistentialQuantification #-}
module Numeric.Series (
Sequence(..)
, enumSequenceFrom
, enumSequenceFromStep
, scanSequence
, sumSeries
, sumPowerSeries
, sequenceToList
, evalContFractionB
) where
import Control.Applicative
import Data.List (unfoldr)
import Numeric.MathFunctions.Constants (m_epsilon)
data Sequence a = forall s. Sequence s (s -> (a,s))
instance Functor Sequence where
fmap f (Sequence s0 step) = Sequence s0 (\s -> let (a,s') = step s in (f a, s'))
{-# INLINE fmap #-}
instance Applicative Sequence where
pure a = Sequence () (\() -> (a,()))
Sequence sA fA <*> Sequence sB fB = Sequence (sA,sB) $ \(!sa,!sb) ->
let (a,sa') = fA sa
(b,sb') = fB sb
in (a b, (sa',sb'))
{-# INLINE pure #-}
{-# INLINE (<*>) #-}
instance Num a => Num (Sequence a) where
(+) = liftA2 (+)
(*) = liftA2 (*)
(-) = liftA2 (-)
{-# INLINE (+) #-}
{-# INLINE (*) #-}
{-# INLINE (-) #-}
abs = fmap abs
signum = fmap signum
fromInteger = pure . fromInteger
{-# INLINE abs #-}
{-# INLINE signum #-}
{-# INLINE fromInteger #-}
instance Fractional a => Fractional (Sequence a) where
(/) = liftA2 (/)
recip = fmap recip
fromRational = pure . fromRational
{-# INLINE (/) #-}
{-# INLINE recip #-}
{-# INLINE fromRational #-}
enumSequenceFrom :: Num a => a -> Sequence a
enumSequenceFrom i = Sequence i (\n -> (n,n+1))
{-# INLINE enumSequenceFrom #-}
enumSequenceFromStep :: Num a => a -> a -> Sequence a
enumSequenceFromStep n d = Sequence n (\i -> (i,i+d))
{-# INLINE enumSequenceFromStep #-}
scanSequence :: (b -> a -> b) -> b -> Sequence a -> Sequence b
{-# INLINE scanSequence #-}
scanSequence f b0 (Sequence s0 step) = Sequence (b0,s0) $ \(b,s) ->
let (a,s') = step s
b' = f b a
in (b,(b',s'))
sumSeries :: Sequence Double -> Double
{-# INLINE sumSeries #-}
sumSeries (Sequence sInit step)
= go x0 s0
where
(x0,s0) = step sInit
go x s | abs (d/x) >= m_epsilon = go x' s'
| otherwise = x'
where (d,s') = step s
x' = x + d
sumPowerSeries :: Double -> Sequence Double -> Double
sumPowerSeries x ser = sumSeries $ liftA2 (*) (scanSequence (*) 1 (pure x)) ser
{-# INLINE sumPowerSeries #-}
sequenceToList :: Sequence a -> [a]
sequenceToList (Sequence s f) = unfoldr (Just . f) s
evalContFractionB :: Sequence (Double,Double) -> Double
{-# INLINE evalContFractionB #-}
evalContFractionB (Sequence sInit step)
= let ((_,b0),s0) = step sInit
f0 = maskZero b0
in go f0 f0 0 s0
where
tiny = 1e-60
maskZero 0 = tiny
maskZero x = x
go f c d s
| abs (delta - 1) >= m_epsilon = go f' c' d' s'
| otherwise = f'
where
((a,b),s') = step s
d' = recip $ maskZero $ b + a*d
c' = maskZero $ b + a/c
delta = c'*d'
f' = f*delta