/** @file monte_carlo.hpp
 *
 *  @author Cory Alexander Balaton (coryab)
 *  @author Janita Ovidie Sandtrøen Willumsen (janitaws)
 *
 *  @version 1.0
 *
 *  @brief Functions for monte carlo simulations.
 *
 *  @bug No known bugs
 * */
#ifndef __MONTE_CARLO__
#define __MONTE_CARLO__

#include "IsingModel.hpp"
#include "data_type.hpp"
#include "utils.hpp"

#include <functional>
#include <string>

#define BURN_IN_TIME 1000

#define EPS_2 (-2 * std::sinh(8.)) / (std::cosh(8.) + 3)

#define MAG_2 (std::exp(8.) + 1) / (2 * cosh(8.) + 3)

#define CV_2                                                                   \
    16                                                                         \
        * (3 * std::cosh(8.) + std::cosh(8.) * std::cosh(8.)                   \
           - std::sinh(8.) * std::sinh(8.))                                    \
        / ((std::cosh(8.) + 3) * (std::cosh(8.) + 3))

#define X_2                                                                    \
    (3 * std::exp(8.) + std::exp(-8.) + 3)                                     \
        / ((std::cosh(8.) + 3) * (std::cosh(8.) + 3))

/** @brief Test numerical data with analytical data.
 *
 *  @param tol The tolerance between the analytical and numerical solution.
 *  @param max_cycles The max number of Monte Carlo cycles.
 *
 *  return uint
 * */
uint test_2x2_lattice(double tol, uint max_cycles);

/** @brief Write the expected values for each Monte Carlo cycles to file.
 *
 *  @param T Temperature
 *  @param L The size of the lattice
 *  @param cycles The amount of Monte Carlo cycles to do
 *  @param filename The file to write to
 * */
void monte_carlo_progression(double T, uint L, uint cycles,
                             const std::string filename);

/** @brief Estimate the probability distribution for the energy.
 *
 *  @param T The temperature of the Ising model
 *  @param L The size of the lattice
 *  @param cycles The amount of Monte Carlo cycles to do
 *  @param filename The file to write to
 * */
void pd_estimate(double T, uint L, uint cycles, const std::string filename);

/** @brief Execute the Metropolis algorithm for a certain amount of Monte 
 *  Carlo cycles.
 *
 *  @param data The data to store the results
 *  @param L The size of the lattice
 *  @param T The Temperature for the Ising model
 *  @param cycles The amount of Monte Carlo cycles to do*/
void monte_carlo_serial(data_t &data, uint L, double T, uint cycles);

/** @brief Execute the Metropolis algorithm for a certain amount of Monte 
 *  Carlo cycles in parallel.
 *
 *  @param data The data to store the results
 *  @param L The size of the lattice
 *  @param T The Temperature for the Ising model
 *  @param cycles The amount of Monte Carlo cycles to do
 * */
void monte_carlo_parallel(data_t &data, uint L, double T, uint cycles);

/** @brief Perform the MCMC algorithm using a range of temperatures.
 *
 *  @param L The size of the lattice
 *  @param start_T The start temperature
 *  @param end_T The end temperature
 *  @param point_T The amount of point to measure
 *  @param monte_carlo Which Monte Carlo implementation to use
 *  @param outfile The file to write the data to
 * */
void phase_transition(
    uint L, double start_T, double end_T, uint points_T,
    std::function<void(data_t &, uint, double, uint)> monte_carlo,
    std::string outfile);

#endif
