{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, BangPatterns #-}
module QuantLib.Methods.MonteCarlo
        ( module QuantLib.Methods.MonteCarlo
        ) where

import Control.Monad()
import Control.Parallel.Strategies
import QuantLib.Stochastic.Process
import QuantLib.Stochastic.Random

-- | Summary type class aggregates all priced values of paths
class PathPricer p => Summary m p | m->p where
        -- | Updates summary with given priced pathes
        sSummarize      :: m->[p]->m
        -- | Defines a metric, i.e. calculate distance between 2 summaries
        sNorm           :: m->m->Double

-- | Path generator is a stochastic path generator
class PathGenerator m where
        pgMkNew         :: m->IO m
        pgGenerate      :: m->IO Path

-- | Path pricer provides a price for given path
class PathPricer m where
        ppPrice         :: m->Path->m


-- | Monte Carlo engine function
monteCarlo :: (Summary s p, PathPricer p, PathGenerator g) => PathMonteCarlo s p g->Int->IO s
monteCarlo (PathMonteCarlo s p g) size = do
        priced <- mapM (\_ -> pricing) [1..size]
        return $ sSummarize s priced
        where   pricing = do
                        !path <- pgGenerate g
                        return $! ppPrice p path

-- | Monte Carlo engine function. Parallelized version
monteCarloParallel :: (Summary s p, PathPricer p, PathGenerator g) => PathMonteCarlo s p g->Int->IO s
monteCarloParallel (PathMonteCarlo s p g) size = do
        priced <- mapM (\_ -> pricing) [1..size] `using` rpar
        return $ sSummarize s priced
        where   pricing = do
                        !path <- pgGenerate g
                        return $! ppPrice p path

-- | Path-dependant Monte Carlo engine
data PathMonteCarlo s p g =
        PathMonteCarlo {
                pmcSummary      :: s,
                pmcPricer       :: p,
                pmcGenerator    :: g
        }

-- | This pricer gets the last point of path
data LastPointPricer = LastPointPricer Dot

instance PathPricer LastPointPricer where
        ppPrice _ path = LastPointPricer (last path) 

-- | Stochastic process generator
data ProcessGenerator sp b d = 
        ProcessGenerator {
                pgStart         :: Dot,
                pgLength        :: Int,
                pgProcess       :: sp,
                pgGenerator     :: b,
                pgDiscretize    :: d
        }

instance (StochasticProcess sp, NormalGenerator b, Discretize d) => PathGenerator (ProcessGenerator sp b d) where
        pgMkNew (ProcessGenerator start len process rnd d)       = do
                newRnd <- ngMkNew rnd
                return $! ProcessGenerator start len process newRnd d
        pgGenerate (ProcessGenerator start len sp b d) = generatePath b d sp len start