{-# LANGUAGE BangPatterns              #-}
{-# LANGUAGE DeriveAnyClass            #-}
{-# LANGUAGE DeriveGeneric             #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts          #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE GADTs                     #-}
{-# LANGUAGE RankNTypes                #-}
{-# LANGUAGE TemplateHaskell           #-}

module Experimenter.Result.Type where

import           Experimenter.Availability
import           Experimenter.Experiment
import           Experimenter.Input
import           Experimenter.Measure
import           Experimenter.Models
import           Experimenter.Parameter
import           Experimenter.Setting

import           Control.DeepSeq
import           Control.Lens
import qualified Data.Text                 as T
import           Data.Time
import           System.Random.MWC


phaseFromResultDataKey :: ResultDataKey -> Phase
phaseFromResultDataKey :: ResultDataKey -> Phase
phaseFromResultDataKey ResultDataPrep{}   = Phase
PreparationPhase
phaseFromResultDataKey ResultDataWarmUp{} = Phase
WarmUpPhase
phaseFromResultDataKey ResultDataRep{}    = Phase
EvaluationPhase

data ResultDataKey
  = ResultDataPrep !(Key PrepResultData)
  | ResultDataWarmUp !(Key WarmUpResultData)
  | ResultDataRep !(Key RepResultData)
  deriving (ResultDataKey -> ResultDataKey -> Bool
(ResultDataKey -> ResultDataKey -> Bool)
-> (ResultDataKey -> ResultDataKey -> Bool) -> Eq ResultDataKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ResultDataKey -> ResultDataKey -> Bool
$c/= :: ResultDataKey -> ResultDataKey -> Bool
== :: ResultDataKey -> ResultDataKey -> Bool
$c== :: ResultDataKey -> ResultDataKey -> Bool
Eq, Eq ResultDataKey
Eq ResultDataKey
-> (ResultDataKey -> ResultDataKey -> Ordering)
-> (ResultDataKey -> ResultDataKey -> Bool)
-> (ResultDataKey -> ResultDataKey -> Bool)
-> (ResultDataKey -> ResultDataKey -> Bool)
-> (ResultDataKey -> ResultDataKey -> Bool)
-> (ResultDataKey -> ResultDataKey -> ResultDataKey)
-> (ResultDataKey -> ResultDataKey -> ResultDataKey)
-> Ord ResultDataKey
ResultDataKey -> ResultDataKey -> Bool
ResultDataKey -> ResultDataKey -> Ordering
ResultDataKey -> ResultDataKey -> ResultDataKey
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: ResultDataKey -> ResultDataKey -> ResultDataKey
$cmin :: ResultDataKey -> ResultDataKey -> ResultDataKey
max :: ResultDataKey -> ResultDataKey -> ResultDataKey
$cmax :: ResultDataKey -> ResultDataKey -> ResultDataKey
>= :: ResultDataKey -> ResultDataKey -> Bool
$c>= :: ResultDataKey -> ResultDataKey -> Bool
> :: ResultDataKey -> ResultDataKey -> Bool
$c> :: ResultDataKey -> ResultDataKey -> Bool
<= :: ResultDataKey -> ResultDataKey -> Bool
$c<= :: ResultDataKey -> ResultDataKey -> Bool
< :: ResultDataKey -> ResultDataKey -> Bool
$c< :: ResultDataKey -> ResultDataKey -> Bool
compare :: ResultDataKey -> ResultDataKey -> Ordering
$ccompare :: ResultDataKey -> ResultDataKey -> Ordering
$cp1Ord :: Eq ResultDataKey
Ord, Int -> ResultDataKey -> ShowS
[ResultDataKey] -> ShowS
ResultDataKey -> String
(Int -> ResultDataKey -> ShowS)
-> (ResultDataKey -> String)
-> ([ResultDataKey] -> ShowS)
-> Show ResultDataKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ResultDataKey] -> ShowS
$cshowList :: [ResultDataKey] -> ShowS
show :: ResultDataKey -> String
$cshow :: ResultDataKey -> String
showsPrec :: Int -> ResultDataKey -> ShowS
$cshowsPrec :: Int -> ResultDataKey -> ShowS
Show)

instance NFData ResultDataKey where
  rnf :: ResultDataKey -> ()
rnf (ResultDataPrep !Key PrepResultData
_)   = ()
  rnf (ResultDataWarmUp !Key WarmUpResultData
_) = ()
  rnf (ResultDataRep !Key RepResultData
_)    = ()


data ResultData a = ResultData
  { ResultData a -> ResultDataKey
_resultDataKey   :: !ResultDataKey
  , ResultData a -> UTCTime
_startTime       :: !UTCTime
  , ResultData a -> Maybe UTCTime
_endTime         :: !(Maybe UTCTime)
  , ResultData a -> GenIO
_startRandGen    :: !GenIO
  , ResultData a -> Maybe GenIO
_endRandGen      :: !(Maybe GenIO)
  , ResultData a -> AvailabilityList (ExpM a) (Input a)
_inputValues     :: !(AvailabilityList (ExpM a) (Input a))
  , ResultData a -> AvailabilityList (ExpM a) Measure
_results         :: !(AvailabilityList (ExpM a) Measure)
  , ResultData a -> Availability (ExpM a) a
_startState      :: !(Availability (ExpM a) a)
  , ResultData a -> Availability (ExpM a) (Maybe a)
_endState        :: !(Availability (ExpM a) (Maybe a))    -- ^ state at end of run
  , ResultData a -> InputState a
_startInputState :: !(InputState a)
  , ResultData a -> Maybe (InputState a)
_endInputState   :: !(Maybe (InputState a))
  }
makeLenses ''ResultData

instance (ExperimentDef a) => NFData (ResultData a) where
  rnf :: ResultData a -> ()
rnf (ResultData !ResultDataKey
k UTCTime
st Maybe UTCTime
end !GenIO
_ !Maybe GenIO
_ !AvailabilityList (ExpM a) (Input a)
inpVal !AvailabilityList (ExpM a) Measure
res Availability (ExpM a) a
_ Availability (ExpM a) (Maybe a)
endSt !InputState a
_ !Maybe (InputState a)
_) = ResultDataKey -> ()
forall a. NFData a => a -> ()
rnf ResultDataKey
k () -> () -> ()
`seq` UTCTime -> ()
forall a. NFData a => a -> ()
rnf UTCTime
st () -> () -> ()
`seq` Maybe UTCTime -> ()
forall a. NFData a => a -> ()
rnf Maybe UTCTime
end () -> () -> ()
`seq` AvailabilityList (ExpM a) (Input a) -> ()
forall a. NFData a => a -> ()
rnf AvailabilityList (ExpM a) (Input a)
inpVal () -> () -> ()
`seq` AvailabilityList (ExpM a) Measure -> ()
forall a. NFData a => a -> ()
rnf AvailabilityList (ExpM a) Measure
res () -> () -> ()
`seq` Availability (ExpM a) (Maybe a) -> ()
forall a. NFData a => a -> ()
rnf Availability (ExpM a) (Maybe a)
endSt -- `seq` rnf stSt

data ReplicationResult a = ReplicationResult
  { ReplicationResult a -> Key RepResult
_replicationResultKey :: !(Key RepResult)
  , ReplicationResult a -> Int
_replicationNumber    :: !Int
  , ReplicationResult a -> Maybe (ResultData a)
_warmUpResults        :: !(Maybe (ResultData a))
  , ReplicationResult a -> Maybe (ResultData a)
_evalResults          :: !(Maybe (ResultData a))
  }
makeLenses ''ReplicationResult

instance (ExperimentDef a) => NFData (ReplicationResult a) where
  rnf :: ReplicationResult a -> ()
rnf (ReplicationResult !Key RepResult
_ Int
nr Maybe (ResultData a)
wm Maybe (ResultData a)
ev) = Int -> ()
forall a. NFData a => a -> ()
rnf Int
nr () -> () -> ()
`seq` Maybe (ResultData a) -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 Maybe (ResultData a)
wm () -> () -> ()
`seq` Maybe (ResultData a) -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 Maybe (ResultData a)
ev

data ExperimentResult a = ExperimentResult
  { ExperimentResult a -> Key ExpResult
_experimentResultKey :: !(Key ExpResult)
  , ExperimentResult a -> Int
_repetitionNumber    :: !Int
  , ExperimentResult a -> Maybe (ResultData a)
_preparationResults  :: !(Maybe (ResultData a))
  , ExperimentResult a -> [ReplicationResult a]
_evaluationResults   :: ![ReplicationResult a]
  }
makeLenses ''ExperimentResult

instance (ExperimentDef a) => NFData (ExperimentResult a) where
  rnf :: ExperimentResult a -> ()
rnf (ExperimentResult !Key ExpResult
_ Int
nr Maybe (ResultData a)
prep [ReplicationResult a]
ev) = Int -> ()
forall a. NFData a => a -> ()
rnf Int
nr () -> () -> ()
`seq` Maybe (ResultData a) -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 Maybe (ResultData a)
prep () -> () -> ()
`seq` [ReplicationResult a] -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 [ReplicationResult a]
ev

data Experiment a = Experiment
  { Experiment a -> Key Exp
_experimentKey       :: !(Key Exp)
  , Experiment a -> Int
_experimentNumber    :: !Int
  , Experiment a -> UTCTime
_experimentStartTime :: !UTCTime
  , Experiment a -> Maybe UTCTime
_experimentEndTime   :: !(Maybe UTCTime)
  , Experiment a -> [ParameterSetting a]
_parameterSetup      :: ![ParameterSetting a]
  , Experiment a -> [ExperimentResult a]
_experimentResults   :: ![ExperimentResult a]
  }
makeLenses ''Experiment

instance (ExperimentDef a) => NFData (Experiment a) where
  rnf :: Experiment a -> ()
rnf (Experiment !Key Exp
_ Int
nr UTCTime
stT Maybe UTCTime
endT [ParameterSetting a]
setup [ExperimentResult a]
res) = Int -> ()
forall a. NFData a => a -> ()
rnf Int
nr () -> () -> ()
`seq` UTCTime -> ()
forall a. NFData a => a -> ()
rnf UTCTime
stT () -> () -> ()
`seq` Maybe UTCTime -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 Maybe UTCTime
endT () -> () -> ()
`seq` [ParameterSetting a] -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 [ParameterSetting a]
setup () -> () -> ()
`seq` [ExperimentResult a] -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 [ExperimentResult a]
res


data Experiments a = Experiments
  { Experiments a -> Key Exps
_experimentsKey               :: !(Key Exps)
  , Experiments a -> Text
_experimentsName              :: !T.Text
  , Experiments a -> UTCTime
_experimentsStartTime         :: !UTCTime
  , Experiments a -> Maybe UTCTime
_experimentsEndTime           :: !(Maybe UTCTime)
  , Experiments a -> ExpsSetup
_experimentsSetup             :: !ExpsSetup
  , Experiments a -> [ParameterSetup a]
_experimentsParameters        :: ![ParameterSetup a]
  , Experiments a -> [ExperimentInfoParameter]
_experimentsInfoParameters    :: ![ExperimentInfoParameter]
  , Experiments a -> a
_experimentsInitialState      :: !a -- ^ state at period 0
  , Experiments a -> InputState a
_experimentsInitialInputState :: !(InputState a)
  , Experiments a -> [Experiment a]
_experiments                  :: ![Experiment a]
  }
makeLenses ''Experiments

instance (ExperimentDef a) => NFData (Experiments a) where
  rnf :: Experiments a -> ()
rnf (Experiments !Key Exps
_ Text
name ~UTCTime
stT !Maybe UTCTime
endT !ExpsSetup
_ ![ParameterSetup a]
param ![ExperimentInfoParameter]
infoParams !a
initSt !InputState a
initInp [Experiment a]
exps) =
    Text -> ()
forall a. NFData a => a -> ()
rnf Text
name () -> () -> ()
`seq` UTCTime -> ()
forall a. NFData a => a -> ()
rnf UTCTime
stT () -> () -> ()
`seq` Maybe UTCTime -> ()
forall a. NFData a => a -> ()
rnf Maybe UTCTime
endT () -> () -> ()
`seq` (ParameterSetup a -> ()) -> [ParameterSetup a] -> [()]
forall a b. (a -> b) -> [a] -> [b]
map ParameterSetup a -> ()
forall a. a -> ()
rwhnf [ParameterSetup a]
param [()] -> () -> ()
`seq` [ExperimentInfoParameter] -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 [ExperimentInfoParameter]
infoParams () -> () -> ()
`seq` a -> ()
forall a. NFData a => a -> ()
rnf a
initSt () -> () -> ()
`seq` InputState a -> ()
forall a. NFData a => a -> ()
rnf InputState a
initInp () -> () -> ()
`seq` [Experiment a] -> ()
forall (f :: * -> *) a. (NFData1 f, NFData a) => f a -> ()
rnf1 [Experiment a]
exps

-- instance Serialize GenIO where
--   put g = put (show g)
--   get = do
--     gTxt <- get
--     return (read gTxt)