module Numeric.GSL.Distribution.Continuous (
                                 ZeroParamDist(..), OneParamDist(..)
                                , TwoParamDist(..), ThreeParamDist(..)
                                , MultiParamDist(..)
                                , BivariateDist(..)
                                , DistFunc(..)
                                , random_0p,random_0p_s,random_0p_v,density_0p
                                , random_1p,random_1p_s,random_1p_v,density_1p
                                , random_2p,random_2p_s,random_2p_v,density_2p
                                , random_3p,random_3p_s,random_3p_v,density_3p
                                , random_mp,random_mp_s,density_mp
                                , random_biv,random_biv_s
                                , random_biv_v,density_biv
                                , spherical_vector 
                             ) where
import Numeric.LinearAlgebra.Data
import Numeric.LinearAlgebra.Devel
import Foreign hiding(shift)
import Foreign.C.Types(CInt(..))
import Numeric.GSL.Distribution.Common
import Numeric.GSL.Distribution.Internal
import System.IO.Unsafe(unsafePerformIO)
infixl 1 #
a # b = applyRaw a b
data ZeroParamDist = Landau
                   deriving Enum
data OneParamDist = Gaussian    
                  | Exponential 
                  | Laplace     
                  | Cauchy      
                  | Rayleigh    
                  | ChiSq       
                  | TDist       
                  | Logistic    
                    deriving Enum
data TwoParamDist = GaussianTail 
                  | ExpPower     
                  | RayleighTail 
                  | Levy         
                  | Gamma        
                  | Uniform      
                  | Lognormal    
                  | FDist        
                  | Beta         
                  | Pareto       
                  | Weibull      
                  | GumbellI     
                  | GumbellII    
                    deriving Enum
data ThreeParamDist = LevySkew    
                    deriving Enum
data MultiParamDist = Dirichlet   
                    deriving Enum
data BivariateDist = BiGaussian  
                    deriving Enum
fromei x = fromIntegral (fromEnum x) :: CInt
random_0p :: ZeroParamDist    
          -> Int             
          -> Double          
random_0p d s = unsafePerformIO $ distribution_random_zero_param (fromIntegral s) (fromei d)            
random_0p_s :: RNG             
            -> ZeroParamDist   
            -> IO Double          
random_0p_s (RNG rng) d = withForeignPtr rng $ \r -> distribution_random_zero_param_s r (fromei d)            
random_0p_v :: ZeroParamDist    
            -> Int             
            -> Int             
            -> Vector Double   
random_0p_v  d s l = unsafePerformIO $ do
   r <- createVector l
   (distribution_random_zero_param_v (fromIntegral s) (fromei d)) # r #| "random_0p_v"
   return r
density_0p :: ZeroParamDist   
                -> DistFunc       
                -> Double         
                -> Double         
density_0p d f x = unsafePerformIO $ do
                                     case d of
                                            Landau -> density_only f d x
    where density_only f' d' x' = if f' /= Density
                                     then error "distribution has no CDF"
                                     else distribution_dist_zero_param (fromei f') (fromei d') x'
foreign import ccall "distribution-aux.h random0" distribution_random_zero_param :: CInt -> CInt -> IO Double
foreign import ccall "distribution-aux.h random0_s" distribution_random_zero_param_s :: RNGHandle -> CInt -> IO Double
foreign import ccall "distribution-aux.h random0_v" distribution_random_zero_param_v :: CInt -> CInt -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random0_dist" distribution_dist_zero_param :: CInt -> CInt -> Double -> IO Double
random_1p :: OneParamDist    
          -> Int             
          -> Double          
          -> Double          
random_1p d s p = unsafePerformIO $
                  alloca $ \r -> do
                      check "random1p" $ distribution_random_one_param (fromIntegral s) (fromei d) p r
                      r' <- peek r
                      return r'
random_1p_s :: RNG             
            -> OneParamDist    
            -> Double          
            -> IO Double       
random_1p_s (RNG rng) d p = alloca $ \r -> do
  check "random_1p_s" $ withForeignPtr rng $ \rg -> distribution_random_one_param_s rg (fromei d) p r
  r' <- peek r
  return r'
random_1p_v :: OneParamDist    
            -> Int             
            -> Double          
            -> Int             
            -> Vector Double   
random_1p_v d s p l = unsafePerformIO $ do
   r <- createVector l
   (distribution_random_one_param_v (fromIntegral s) (fromei d) p) # r #| "random_1p_v"
   return r
density_1p :: OneParamDist   
                -> DistFunc       
                -> Double         
                -> Double         
                -> Double  
density_1p d f p x = unsafePerformIO $ distribution_dist_one_param (fromei f) (fromei d) x p
foreign import ccall "distribution-aux.h random1" distribution_random_one_param :: CInt -> CInt -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random1_s" distribution_random_one_param_s :: RNGHandle -> CInt -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random1_v" distribution_random_one_param_v :: CInt -> CInt -> Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random1_dist" distribution_dist_one_param :: CInt -> CInt -> Double -> Double -> IO Double
random_2p :: TwoParamDist    
          -> Int             
          -> Double          
          -> Double          
          -> Double          
random_2p d s p1 p2  = unsafePerformIO $
                       alloca $ \r -> do
                           check "random2p" $ distribution_random_two_param (fromIntegral s) (fromei d) p1 p2 r
                           r' <- peek r
                           return r'
random_2p_s :: RNG             
            -> TwoParamDist    
            -> Double          
            -> Double          
            -> IO Double       
random_2p_s (RNG rng) d p1 p2 = alloca $ \r -> do
  check "random_2p_s" $ withForeignPtr rng $ \rg -> distribution_random_two_param_s rg (fromei d) p1 p2 r
  r' <- peek r
  return r'
random_2p_v :: TwoParamDist    
            -> Int             
            -> Double          
            -> Double          
            -> Int             
            -> Vector Double   
random_2p_v d s p1 p2 l = unsafePerformIO $ do
   r <- createVector l
   (distribution_random_two_param_v (fromIntegral s) (fromei d) p1 p2) # r #| "random_2p_v"
   return r
density_2p :: TwoParamDist   
                -> DistFunc       
                -> Double         
                -> Double         
                -> Double         
                -> Double         
density_2p d f p1 p2 x = unsafePerformIO $ do
                          case d of
                                 GaussianTail -> density_only f d p1 p2 x
                                 ExpPower     -> no_inverse f d p1 p2 x
                                 RayleighTail -> density_only f d p1 p2 x
                                 Levy         -> error "no PDF or CDF for Levy"
                                 _            -> distribution_dist_two_param (fromei f) (fromei d) x p1 p2
    where density_only f' d' p1' p2' x' = if f' /= Density
                                            then error "distribution has no CDF"
                                            else distribution_dist_two_param (fromei f') (fromei d') x' p1' p2'
          no_inverse f' d' p1' p2' x' = if (f' == LowInv || f' == UppInv)
                                          then error "distribution has no inverse CDF"
                                          else distribution_dist_two_param (fromei f') (fromei d') x' p1' p2'
foreign import ccall "distribution-aux.h random2" distribution_random_two_param :: CInt -> CInt -> Double -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random2_s" distribution_random_two_param_s :: RNGHandle -> CInt -> Double -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random2_v" distribution_random_two_param_v :: CInt -> CInt -> Double -> Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random2_dist" distribution_dist_two_param :: CInt -> CInt -> Double -> Double -> Double -> IO Double
random_3p :: ThreeParamDist  
          -> Int             
          -> Double          
          -> Double          
          -> Double          
          -> Double          
random_3p d s p1 p2 p3 = unsafePerformIO $
                         alloca $ \r -> do
                                 check "random_3p" $ distribution_random_three_param (fromIntegral s) (fromei d) p1 p2 p3 r
                                 r' <- peek r
                                 return r'
random_3p_s :: RNG             
            -> ThreeParamDist  
            -> Double          
            -> Double          
            -> Double          
            -> IO Double       
random_3p_s (RNG rng) d p1 p2 p3 = alloca $ \r -> do
  check "random_3p_s" $ withForeignPtr rng $ \rg -> distribution_random_three_param_s rg (fromei d) p1 p2 p3 r           
  r' <- peek r
  return r'
random_3p_v :: ThreeParamDist  
            -> Int             
            -> Double          
            -> Double          
            -> Double          
            -> Int             
            -> Vector Double   
random_3p_v d s p1 p2 p3 l = unsafePerformIO $ do
   r <- createVector l
   (distribution_random_three_param_v (fromIntegral s) (fromei d) p1 p2 p3) # r #| "random_3p_v"
   return r
density_3p :: ThreeParamDist   
                -> DistFunc       
                -> Double         
                -> Double         
                -> Double         
                -> Double         
                -> Double         
density_3p d _ _ _ _ _ = unsafePerformIO $ do
                            case d of
                                 LevySkew -> error "Levy skew has no PDF or CDF"
                                 _        -> error "unknown 3 parameter distribution"
foreign import ccall "distribution-aux.h random3" distribution_random_three_param :: CInt -> CInt -> Double -> Double -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random3_s" distribution_random_three_param_s :: RNGHandle -> CInt -> Double -> Double -> Double -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random3_v" distribution_random_three_param_v :: CInt -> CInt -> Double -> Double -> Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random3_dist" distribution_dist_three_param :: CInt -> CInt -> Double -> Double -> Double -> Double -> IO Double
random_mp :: MultiParamDist  
          -> Int             
          -> Vector Double   
          -> Vector Double   
random_mp d s p = unsafePerformIO $ do
                  r <- createVector $ size p
                  (distribution_random_multi_param (fromIntegral s) (fromei d)) # p # r #| "random_mp"
                  return r
random_mp_s :: RNG                
            -> MultiParamDist     
            -> Vector Double      
            -> IO (Vector Double) 
random_mp_s (RNG rng) d p = do
  let l = size p
  r <- createVector l
  withForeignPtr rng $ \rg -> (distribution_random_multi_param_s rg (fromei d)) # p # r #| "random_mp_s"
  return r
  
density_mp :: MultiParamDist   
                -> DistFunc       
                -> Vector Double  
                -> Vector Double  
                -> Double         
density_mp d f p q = unsafePerformIO $ do
                            case d of
                                 Dirichlet -> density_only f d p q
    where density_only f' d' p' q' = if f' /= Density
                                              then error "distribution has no CDF"
                                              else alloca $ \r -> do
                                                                  (distribution_dist_multi_param (fromei f') (fromei d') r) # p' # q' #| "density_mp"
                                                                  r' <- peek r
                                                                  return r'
foreign import ccall "distribution-aux.h random_mp" distribution_random_multi_param :: CInt -> CInt -> CInt -> Ptr Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random_mp_s" distribution_random_multi_param_s :: RNGHandle -> CInt -> CInt -> Ptr Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random_mp_dist" distribution_dist_multi_param :: CInt -> CInt -> Ptr Double -> CInt -> Ptr Double -> CInt -> Ptr Double -> IO CInt
random_biv :: BivariateDist  
          -> Int             
          -> Double          
          -> Double          
          -> Double          
          -> (Double,Double) 
random_biv d s p1 p2 p3 = unsafePerformIO $
                         alloca $ \r1 ->
                             alloca $ \r2 -> do
                                 distribution_random_bivariate (fromIntegral s) (fromei d) p1 p2 p3 r1 r2
                                 r1' <- peek r1
                                 r2' <- peek r2
                                 return (r1',r2')
random_biv_s :: RNG                
            -> BivariateDist      
            -> Double          
            -> Double          
            -> Double          
            -> IO (Double,Double) 
random_biv_s (RNG rng) d p1 p2 p3 = do
  alloca $ \r1 ->
    alloca $ \r2 -> do
      withForeignPtr rng $ \r -> distribution_random_bivariate_s r (fromei d) p1 p2 p3 r1 r2
      r1' <- peek r1
      r2' <- peek r2
      return (r1',r2')
  
random_biv_v :: BivariateDist  
             -> Int             
             -> Double          
             -> Double          
             -> Double          
             -> Int             
             -> (Vector Double,Vector Double) 
random_biv_v d s p1 p2 p3 l = unsafePerformIO $ do
   r1 <- createVector l
   r2 <- createVector l
   (distribution_random_bivariate_v (fromIntegral s) (fromei d) p1 p2 p3) # r1 # r2 #| "random_biv_v"
   return (r1,r2)
density_biv :: BivariateDist      
                -> DistFunc        
                -> Double          
                -> Double          
                -> Double          
                -> (Double,Double) 
                -> Double          
density_biv d f p1 p2 p3 (x,y) = unsafePerformIO $ do
                            case d of
                                 BiGaussian -> density_only f d p1 p2 p3 x y
    where density_only f' d' p1' p2' p3' x' y' = if f' /= Density
                                                 then error "distribution has no CDF"
                                                 else distribution_dist_bivariate (fromei f') (fromei d') x' y' p1' p2' p3'
foreign import ccall "distribution-aux.h random_biv" distribution_random_bivariate :: CInt -> CInt -> Double -> Double -> Double -> Ptr Double -> Ptr Double -> IO ()
foreign import ccall "distribution-aux.h random_biv_s" distribution_random_bivariate_s :: RNGHandle -> CInt -> Double -> Double -> Double -> Ptr Double -> Ptr Double -> IO ()
foreign import ccall "distribution-aux.h random_biv_v" distribution_random_bivariate_v :: CInt -> CInt -> Double -> Double -> Double -> CInt -> Ptr Double -> CInt -> Ptr Double -> IO CInt
foreign import ccall "distribution-aux.h random_biv_dist" distribution_dist_bivariate :: CInt -> CInt -> Double -> Double -> Double -> Double -> Double -> IO Double
spherical_vector :: Int           
                 -> Int           
                 -> Vector Double 
spherical_vector s vs = unsafePerformIO $ do
                        r <- createVector vs
                        (distribution_spherical_vector (fromIntegral s)) # r #| "spherical_vector"
                        return r
foreign import ccall "distribution-aux.h spherical_vector" distribution_spherical_vector :: CInt -> CInt -> Ptr Double -> IO CInt