{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
--------------------------------------------------------------------
-- |
-- Copyright :  (c) Edward Kmett 2013
-- License   :  BSD3
-- Maintainer:  Edward Kmett <ekmett@gmail.com>
-- Stability :  experimental
-- Portability: non-portable
--
-- These functions provide wildly inaccurate but very fast
-- approximations to common transcendental functions.
--
-- The algorithms here are based on Martin Ankerl's optimized 'pow',
-- <http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/>
-- which is in turn based on
-- <http://nic.schraudolph.org/pubs/Schraudolph99.pdf>
--------------------------------------------------------------------
module Data.Approximate.Numerics
  ( Fast(..)
  , blog
  ) where

class Floating a => Fast a where
  -- | Calculate an approximate log.
  flog    :: a -> a
  flog_lb :: a -> a
  flog_ub :: a -> a
  -- | Calculate an approximate exp.
  fexp    :: a -> a
  fexp_lb :: a -> a
  fexp_ub :: a -> a
  -- | Calculate an approximate pow.
  fpow    :: a -> a -> a
  fpow_lb :: a -> a -> a
  fpow_ub :: a -> a -> a

instance Fast Float where
  flog :: Float -> Float
flog     = Float -> Float
logf_fast
  flog_lb :: Float -> Float
flog_lb  = Float -> Float
logf_fast_lb
  flog_ub :: Float -> Float
flog_ub  = Float -> Float
logf_fast_ub
  fexp :: Float -> Float
fexp     = Float -> Float
better_expf_fast
  fexp_lb :: Float -> Float
fexp_lb  = Float -> Float
expf_fast_lb
  fexp_ub :: Float -> Float
fexp_ub  = Float -> Float
expf_fast_ub
  fpow :: Float -> Float -> Float
fpow     = Float -> Float -> Float
better_powf_fast_precise
  fpow_lb :: Float -> Float -> Float
fpow_lb  = Float -> Float -> Float
powf_fast_lb
  fpow_ub :: Float -> Float -> Float
fpow_ub  = Float -> Float -> Float
powf_fast_ub

instance Fast Double where
  flog :: Double -> Double
flog     = Double -> Double
log_fast
  flog_lb :: Double -> Double
flog_lb  = Double -> Double
log_fast_lb
  flog_ub :: Double -> Double
flog_ub  = Double -> Double
log_fast_ub
  fexp :: Double -> Double
fexp     = Double -> Double
better_exp_fast
  fexp_lb :: Double -> Double
fexp_lb  = Double -> Double
exp_fast_lb
  fexp_ub :: Double -> Double
fexp_ub  = Double -> Double
exp_fast_ub
  fpow :: Double -> Double -> Double
fpow     = Double -> Double -> Double
better_pow_fast_precise
  fpow_lb :: Double -> Double -> Double
fpow_lb  = Double -> Double -> Double
pow_fast_lb
  fpow_ub :: Double -> Double -> Double
fpow_ub  = Double -> Double -> Double
pow_fast_ub

-- | Borchardt’s Algorithm from “Dead Reckoning: Calculating without instruments”.
--
-- This is a remarkably bad approximate logarithm.
--
-- 'flog' had better outperform it! It is provided merely for comparison.
blog :: Floating a => a -> a
blog :: a -> a
blog a
x = a
6 a -> a -> a
forall a. Num a => a -> a -> a
* (a
x a -> a -> a
forall a. Num a => a -> a -> a
- a
1) a -> a -> a
forall a. Fractional a => a -> a -> a
/ (a
x a -> a -> a
forall a. Num a => a -> a -> a
+ a
1 a -> a -> a
forall a. Num a => a -> a -> a
+ a
4 a -> a -> a
forall a. Num a => a -> a -> a
* a -> a
forall a. Floating a => a -> a
sqrt a
x)
      -- 6 / (x + 1 + 4 * sqrt x) / (x-1) -- herbie's suggested replacement.
#ifdef HERBIE
{-# ANN blog "NoHerbie" #-}
#endif

foreign import ccall unsafe pow_fast_lb       :: Double -> Double -> Double
foreign import ccall unsafe pow_fast_ub       :: Double -> Double -> Double
foreign import ccall unsafe better_pow_fast_precise  :: Double -> Double -> Double
foreign import ccall unsafe powf_fast_lb      :: Float -> Float -> Float
foreign import ccall unsafe powf_fast_ub      :: Float -> Float -> Float
foreign import ccall unsafe better_powf_fast_precise :: Float -> Float -> Float

foreign import ccall unsafe better_exp_fast   :: Double -> Double
foreign import ccall unsafe exp_fast_lb       :: Double -> Double
foreign import ccall unsafe exp_fast_ub       :: Double -> Double
foreign import ccall unsafe better_expf_fast  :: Float -> Float
foreign import ccall unsafe expf_fast_lb      :: Float -> Float
foreign import ccall unsafe expf_fast_ub      :: Float -> Float

foreign import ccall unsafe log_fast        :: Double -> Double
foreign import ccall unsafe log_fast_lb     :: Double -> Double
foreign import ccall unsafe log_fast_ub     :: Double -> Double
foreign import ccall unsafe logf_fast       :: Float -> Float
foreign import ccall unsafe logf_fast_lb    :: Float -> Float
foreign import ccall unsafe logf_fast_ub    :: Float -> Float