{-# LANGUAGE ParallelListComp #-}
module Math.Gamma.Stirling
( lnGammaStirling
, cs
, s
, abs_s
, terms) where
import qualified Data.Vector as V
{-# INLINE lnGammaStirling #-}
lnGammaStirling :: Floating a => [a] -> a -> a
lnGammaStirling ks z
= (z - 0.5) * log z
- z
+ 0.5 * log (2*pi)
+ sum (zipWith (/) ks (risingPowers (z+1)))
{-# INLINE risingPowers #-}
risingPowers :: Num a => a -> [a]
risingPowers = scanl1 (*) . iterate (1+)
cs :: (Fractional a, Ord a) => [a]
cs = map c [1..]
c :: (Fractional a, Ord a) => Int -> a
c n = 0.5 * recip n' * sum [k' * fromInteger (abs_s n k) / ((k' + 1) * (k' + 2)) | k <- [1..n], let k' = fromIntegral k]
where n' = fromIntegral n
s :: Int -> Int -> Integer
s n k
| n < 0 = error "s n k: n < 0"
| k < 0 = error "s n k: k < 0"
| k > n = error "s n k: k > n"
| otherwise = s' n k
where
table = [V.generate (i+1) $ s' i | i <- [0..]]
s' 0 0 = 1
s' _ 0 = 0
s' n' k'
| n' == k' = 1
| otherwise = s'' (n'-1) (k'-1) - (toInteger n'-1) * s'' (n'-1) k'
where
s'' n'' k'' = table !! n'' V.! k''
abs_s :: Int -> Int -> Integer
abs_s n k
| n < 0 = error "abs_s n k: n < 0"
| k < 0 = error "abs_s n k: k < 0"
| k > n = error "abs_s n k: k > n"
| otherwise = abs_s' n k
where
table = [V.generate (n''+1) $ \k'' -> abs_s' n'' k'' | n'' <- [0..]]
abs_s' 0 0 = 1
abs_s' _ 0 = 0
abs_s' n' k'
| n' == k' = 1
| otherwise = abs_s'' (n'-1) (k'-1) + (toInteger n'-1) * abs_s'' (n'-1) k'
where
abs_s'' n'' k'' = table !! n'' V.! k''
terms :: (Num t, Floating a, Ord a) => a -> a -> t
terms prec z = stepsNeeded (eps z) (f z)
where
cs' = cs
f x = scanl1 (+) (zipWith (/) cs' (risingPowers (x+1)))
eps x = prec * abs ((x - 0.5) * log x - x + 0.5 * log (2*pi))
stepsNeeded e xs = go 1 xs
where
go n (x:y:zs)
| abs(x-y)<=e = n
| otherwise = go (n+1) (y:zs)
go n _ = n