FunctionSMCMC
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description Methods to implement Markov Chain Monte Carlo Simulation (MCMC)
library(title='FunctionSMCMC')

import RicardoSantos/FunctionProbabilityDistributionSampling/1 as dist

// @function a basic implementation of the markov chain algorithm
// @param weights float array, weights of the Markov Chain.
// @param actions float array, actions of the Markov Chain.
// @param target_path float array, target path array.
// @param position int, index of the path.
// @param last_value float, base value to increment.
// @returns void, updates target array
export markov_chain (float[] weights, float[] actions, float[] target_path, int position, float last_value) =>  //{
    int _size_t = array.size(target_path)
    int _size_w = array.size(weights)
    int _size_a = array.size(actions)
    //
    switch
        (_size_t < 1) => runtime.error('FunctionSMCMC -> markov_chain(): "target_path" has the wrong size.')
        (_size_w < 1) => runtime.error('FunctionSMCMC -> markov_chain(): "weights" has the wrong size.')
        (_size_w != _size_a) => runtime.error('FunctionSMCMC -> markov_chain(): "weights" and "actions" size must match.')
    //
    if _size_w > 1
        array.set(target_path, position, last_value + array.get(actions, dist.sample(weights)))
    else
        array.set(target_path, position, last_value + array.get(actions, 0))
//}

// @function uses a monte carlo algorithm to simulate a markov chain at each step.
// @param weights float array, weights of the Markov Chain.
// @param actions float array, actions of the Markov Chain.
// @param start_value float, base value to start simulation.
// @param n_iterations integer, number of iterations to run.
// @returns float array with path.
export mcmc (float[] weights, float[] actions, float start_value, int n_iterations) =>  //{
    int _size_w = array.size(weights)
    int _size_a = array.size(actions)
    float _sum_w = array.sum(weights)
    //
    switch
        na(start_value) => runtime.error('FunctionSMCMC -> markov_chain(): "start_value" is undefined.')
        (n_iterations < 1) => runtime.error('FunctionSMCMC -> markov_chain(): "n_iterations" must be a positive integer > 0.')
        (_size_w < 1) => runtime.error('FunctionSMCMC -> markov_chain(): "weights" has the wrong size.')
        (_size_w != _size_a) => runtime.error('FunctionSMCMC -> markov_chain(): "weights" and "actions" size must match.')
        (_sum_w != 1.0) => runtime.error(str.format('FunctionSMCMC -> markov_chain(): "weights" must sum 1, found: {0}.', _sum_w))
    //
    float[] _T = array.new_float(n_iterations, 0.0)
    array.set(_T, 0, start_value)
    for _i = 1 to n_iterations - 1 by 1
        float _current = array.get(_T, _i - 1)
        markov_chain(weights, actions, _T, _i, _current)
    _T
// @references:
//      https://stephens999.github.io/fiveMinuteStats/MH-examples1.html
//      https://ml-cheatsheet.readthedocs.io/en/latest/activation_functions.html
//}

i_iterations = input(100)
i_start = input(500.)
weights = array.from(0.3, 0.3, 0.4)
actions = array.from(1.0, -1.0, 0.0)
var float[] M = array.new_float(0)
if barstate.isfirst
    M := mcmc(weights, actions, i_start, i_iterations)
    M

float output = na
if bar_index < i_iterations
    output := array.get(M, bar_index)
    output
plot(output)

