{-# LANGUAGE RankNTypes #-}

-- |
-- Module      :  Mcmc.Monitor.Parameter
-- Description :  Monitor parameters
-- Copyright   :  2021 Dominik Schrempf
-- License     :  GPL-3.0-or-later
--
-- Maintainer  :  dominik.schrempf@gmail.com
-- Stability   :  unstable
-- Portability :  portable
--
-- Creation date: Fri May 29 11:11:49 2020.
module Mcmc.Monitor.Parameter
  ( -- * Parameter monitors
    MonitorParameter (..),
    (>$<),
    monitorInt,
    monitorDouble,
    monitorDoubleF,
    monitorDoubleS,
  )
where

import qualified Data.ByteString.Builder as BB
import Data.Functor.Contravariant

-- XXX: 'MonitorParameter' has a drawback. Extracting and monitoring multiple
-- parameters in one go is impossible.

-- | Instruction about a parameter to monitor.
--
-- Convert a parameter monitor from one data type to another with '(>$<)'.
--
-- For example, monitor a 'Double' value being the first entry of a tuple:
--
-- @
-- mon = fst >$< monitorDouble
-- @
data MonitorParameter a = MonitorParameter
  { -- | Name of parameter.
    forall a. MonitorParameter a -> String
mpName :: String,
    -- | Instruction about how to extract the parameter from the state.
    forall a. MonitorParameter a -> a -> Builder
mpFunc :: a -> BB.Builder
  }

instance Contravariant MonitorParameter where
  contramap :: forall a' a. (a' -> a) -> MonitorParameter a -> MonitorParameter a'
contramap a' -> a
f (MonitorParameter String
n a -> Builder
m) = forall a. String -> (a -> Builder) -> MonitorParameter a
MonitorParameter String
n (a -> Builder
m forall b c a. (b -> c) -> (a -> b) -> a -> c
. a' -> a
f)

-- | Monitor 'Int'.
monitorInt ::
  -- | Name.
  String ->
  MonitorParameter Int
monitorInt :: String -> MonitorParameter Int
monitorInt String
n = forall a. String -> (a -> Builder) -> MonitorParameter a
MonitorParameter String
n Int -> Builder
BB.intDec

-- | Monitor 'Double' with eight decimal places (half precision).
monitorDouble ::
  -- | Name.
  String ->
  MonitorParameter Double
monitorDouble :: String -> MonitorParameter Double
monitorDouble String
n = forall a. String -> (a -> Builder) -> MonitorParameter a
MonitorParameter String
n (FloatFormat -> Double -> Builder
BB.formatDouble forall a b. (a -> b) -> a -> b
$ Int -> FloatFormat
BB.standard Int
8)

-- | Monitor 'Double' with full precision.
monitorDoubleF ::
  -- | Name.
  String ->
  MonitorParameter Double
monitorDoubleF :: String -> MonitorParameter Double
monitorDoubleF String
n = forall a. String -> (a -> Builder) -> MonitorParameter a
MonitorParameter String
n Double -> Builder
BB.doubleDec

-- | Monitor 'Double' in scientific format.
monitorDoubleS ::
  -- | Name.
  String ->
  MonitorParameter Double
monitorDoubleS :: String -> MonitorParameter Double
monitorDoubleS String
n = forall a. String -> (a -> Builder) -> MonitorParameter a
MonitorParameter String
n (FloatFormat -> Double -> Builder
BB.formatDouble FloatFormat
BB.scientific)