gauge-0.2.1: math-functions/Numeric/SpecFunctions/Internal.hs
{-# LANGUAGE CPP, BangPatterns, ScopedTypeVariables, ForeignFunctionInterface #-}
-- |
-- Module : Numeric.SpecFunctions.Internal
-- Copyright : (c) 2009, 2011, 2012 Bryan O'Sullivan
-- License : BSD3
--
-- Maintainer : bos@serpentine.com
-- Stability : experimental
-- Portability : portable
--
-- Internal module with implementation of special functions.
module Numeric.SpecFunctions.Internal
( erf
, erfc
, invErf
, invErfc
, log2
) where
import Data.Bits ((.&.), (.|.), shiftR)
import Data.Word (Word64)
import qualified Data.Vector.Unboxed as U
import Numeric.MathFunctions.Constants
----------------------------------------------------------------
-- Error function
----------------------------------------------------------------
-- | Error function.
--
-- \[
-- \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}} \int_{0}^{x} \exp(-t^2) dt
-- \]
--
-- Function limits are:
--
-- \[
-- \begin{aligned}
-- &\operatorname{erf}(-\infty) &=& -1 \\
-- &\operatorname{erf}(0) &=& \phantom{-}\,0 \\
-- &\operatorname{erf}(+\infty) &=& \phantom{-}\,1 \\
-- \end{aligned}
-- \]
erf :: Double -> Double
{-# INLINE erf #-}
erf = c_erf
-- | Complementary error function.
--
-- \[
-- \operatorname{erfc}(x) = 1 - \operatorname{erf}(x)
-- \]
--
-- Function limits are:
--
-- \[
-- \begin{aligned}
-- &\operatorname{erf}(-\infty) &=&\, 2 \\
-- &\operatorname{erf}(0) &=&\, 1 \\
-- &\operatorname{erf}(+\infty) &=&\, 0 \\
-- \end{aligned}
-- \]
erfc :: Double -> Double
{-# INLINE erfc #-}
erfc = c_erfc
foreign import ccall "erf" c_erf :: Double -> Double
foreign import ccall "erfc" c_erfc :: Double -> Double
-- | Inverse of 'erf'.
invErf :: Double -- ^ /p/ ∈ [-1,1]
-> Double
invErf p = invErfc (1 - p)
-- | Inverse of 'erfc'.
invErfc :: Double -- ^ /p/ ∈ [0,2]
-> Double
invErfc p
| p == 2 = m_neg_inf
| p == 0 = m_pos_inf
| p >0 && p < 2 = if p <= 1 then r else -r
| otherwise = modErr $ "invErfc: p must be in [0,2] got " ++ show p
where
pp = if p <= 1 then p else 2 - p
t = sqrt $ -2 * log( 0.5 * pp)
-- Initial guess
x0 = -0.70711 * ((2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t)
r = loop 0 x0
--
loop :: Int -> Double -> Double
loop !j !x
| j >= 2 = x
| otherwise = let err = erfc x - pp
x' = x + err / (1.12837916709551257 * exp(-x * x) - x * err) -- // Halley
in loop (j+1) x'
-- | /O(log n)/ Compute the logarithm in base 2 of the given value.
log2 :: Int -> Int
log2 v0
| v0 <= 0 = modErr $ "log2: nonpositive input, got " ++ show v0
| otherwise = go 5 0 v0
where
go !i !r !v | i == -1 = r
| v .&. b i /= 0 = let si = U.unsafeIndex sv i
in go (i-1) (r .|. si) (v `shiftR` si)
| otherwise = go (i-1) r v
b = U.unsafeIndex bv
!bv = U.fromList [ 0x02, 0x0c, 0xf0, 0xff00
, fromIntegral (0xffff0000 :: Word64)
, fromIntegral (0xffffffff00000000 :: Word64)]
!sv = U.fromList [1,2,4,8,16,32]
modErr :: String -> a
modErr msg = error $ "Numeric.SpecFunctions." ++ msg