-- | See McHale, Vanessa [\"Hypergeometric Functions for Statistical Computing\"](http://vmchale.com/static/serve/hypergeometric.pdf) and especially Shaw, Ernest [\"Hypergeometric Functions and CDFs in J\"](https://www.jsoftware.com/papers/jhyper.pdf) module Math.Hypergeometric ( hypergeometric , erf , ncdf ) where import Data.Functor ((<$>)) risingFactorial :: Num a => a -> Int -> a risingFactorial _ 0 = 1 risingFactorial a n = (a + fromIntegral n - 1) * risingFactorial a (n-1) factorial :: Num a => Int -> a factorial n = product (fromIntegral <$> [1..n]) {-# SPECIALIZE ncdf :: Double -> Double #-} {-# SPECIALIZE ncdf :: Float -> Float #-} -- | CDF of the standard normal \( N(0,1) \) ncdf :: (Eq a, Floating a) => a -> a ncdf z = (1/2) * (1 + erf (z / sqrt 2)) {-# SPECIALIZE erf :: Double -> Double #-} {-# SPECIALIZE erf :: Float -> Float #-} -- | [erf](https://mathworld.wolfram.com/Erf.html) erf :: (Eq a, Floating a) => a -> a erf z = (2 * z * exp (-z^(2::Int)) / sqrt pi) * hypergeometric [1] [3/2] (z^(2::Int)) {-# SPECIALIZE hypergeometric :: [Double] -> [Double] -> Double -> Double #-} {-# SPECIALIZE hypergeometric :: [Float] -> [Float] -> Float -> Float #-} -- | \( _pF_q(a_1,\ldots,a_p;b_1,\ldots,b_q;z) = \displaystyle\sum_{n=0}^\infty\frac{(a_1)_n\cdots(a_p)_n}{(b_1)_b\cdots(b_q)_n}\frac{z^n}{n!} \) -- -- This iterates until the result stabilizes. hypergeometric :: (Eq a, Fractional a) => [a] -- ^ \( a_1,\ldots,a_p \) -> [a] -- ^ \( b_1,\ldots,b_q \) -> a -- ^ \( z \) -> a hypergeometric as bs z = sumUntilEq [ (product (fmap (`risingFactorial` n) as) / product (fmap (`risingFactorial` n) bs)) * (z ^ n) / factorial n | n <- [0..] ] sumUntilEq :: (Eq a, Num a) => [a] -> a sumUntilEq = sumUntilEqLoop 0 sumUntilEqLoop :: (Eq a, Num a) => a -> [a] -> a sumUntilEqLoop acc (x:y:xs) = if step0 == step1 then step0 else sumUntilEqLoop step1 xs where step0 = acc + x step1 = acc + x + y