//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

**/
//==================================================================================================
#ifndef BOOST_SIMD_EXPONENTIAL_HPP_INCLUDED
#define BOOST_SIMD_EXPONENTIAL_HPP_INCLUDED

namespace boost { namespace simd
{
  /*!
    @ingroup group-functions
    @defgroup group-exponential Exponential functions

    Those functions provide algorithms for computing exponentials
    and logarithms.

      <center>
        |                      |                  |                  |                 |
        |:--------------------:|:----------------:|:----------------:|:---------------:|
        | @ref cbrt            | @ref exp10       | @ref exp2        | @ref exp        |
        | @ref expm1           | @ref exprecneg   | @ref exprecnegc  | @ref expx2      |
        | @ref log10           | @ref log2        | @ref log         | @ref log1p      |
        | @ref nthroot         | @ref pow2        | @ref pow_abs     | @ref pow        |
        | @ref significants    |                  |                  |                 |
       </center>
  **/

} }

#include <boost/simd/function/cbrt.hpp>
#include <boost/simd/function/exp10.hpp>
#include <boost/simd/function/exp2.hpp>
#include <boost/simd/function/exp.hpp>
#include <boost/simd/function/expm1.hpp>
#include <boost/simd/function/exprecnegc.hpp>
#include <boost/simd/function/exprecneg.hpp>
#include <boost/simd/function/expx2.hpp>
#include <boost/simd/function/log10.hpp>
#include <boost/simd/function/log2.hpp>
#include <boost/simd/function/log.hpp>
#include <boost/simd/function/log1p.hpp>
#include <boost/simd/function/nthroot.hpp>
#include <boost/simd/function/pow2.hpp>
#include <boost/simd/function/pow_abs.hpp>
#include <boost/simd/function/pow.hpp>
#include <boost/simd/function/significants.hpp>

#endif


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_CARTESIAN_UNIFORM_POINT_DISTRIBUTION_TRIANGLE_HPP
#define BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_CARTESIAN_UNIFORM_POINT_DISTRIBUTION_TRIANGLE_HPP

#include <random>
#include <cmath>

#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/point_type.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace uniform_point_distribution {

//The following strategy is suitable for rings or polygons of three points.
template
<
    typename Point,
    typename DomainGeometry
>
struct uniform_2d_cartesian_triangle
{
private:
    typedef typename point_type<DomainGeometry>::type domain_point_type;
public:
    uniform_2d_cartesian_triangle(DomainGeometry const& g) {}
    bool equals(DomainGeometry const& l_domain,
                DomainGeometry const& r_domain,
                uniform_2d_cartesian_triangle const& r_strategy) const
    {
        return boost::geometry::equals(l_domain.domain(), r_domain.domain());
    }

    template<typename sample_type>
    static Point map(domain_point_type const& p1,
                     domain_point_type const& p2,
                     domain_point_type const& p3,
                     sample_type const& s1,
                     sample_type const& s2)
    {
        Point out;
        sample_type r1 = std::sqrt(s1);
        set<0>(out, (1 - r1) * get<0>(p1)
                  + ( r1 * (1 - s2) ) * get<0>(p2)
                  + ( s2 * r1 * get<0>(p3)));
        set<1>(out, (1 - r1) * get<1>(p1)
                  + ( r1 * (1 - s2) ) * get<1>(p2)
                  + ( s2 * r1 * get<1>(p3)));
        return out;
    }

    template<typename Gen>
    Point apply(Gen& g, DomainGeometry const& d)
    {
        typedef typename select_most_precise
            <
                typename coordinate_type<DomainGeometry>::type,
                double
            >::type sample_type;
        std::uniform_real_distribution<sample_type> real_dist(0, 1);
        sample_type s1 = real_dist(g),
                    s2 = real_dist(g);
        return map(*d.begin(), *(d.begin() + 1), *(d.begin() + 2), s1, s2);
    }
    void reset(DomainGeometry const&) {};
};

}} // namespace strategy::uniform_point_distribution

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_CARTESIAN_UNIFORM_POINT_DISTRIBUTION_TRIANGLE_HPP


/*
 * common.hpp
 *
 *  Created on: Apr 19, 2019
 *      Author: Gregory Kramida
 *   Copyright: 2019 Gregory Kramida
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#pragma once

//libraries
#include <Eigen/Dense>

namespace tsdf{

constexpr float near_clipping_distance = 0.05; //m

template<typename Scalar>
inline Scalar compute_TSDF_value(Scalar signed_distance, Scalar narrow_band_half_width){
	if (signed_distance < -narrow_band_half_width) {
		return (Scalar)-1.0;
	} else if (signed_distance > narrow_band_half_width) {
		return (Scalar)1.0;
	} else {
		return signed_distance / narrow_band_half_width;
	}
}

template<typename Scalar>
inline bool is_voxel_out_of_bounds(const Eigen::Matrix<Scalar,2,1>& voxel_image,
		const Eigen::Matrix<unsigned short, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& depth_image,
		int margin = 3){
	if (voxel_image(0) < -margin || voxel_image(0) >= depth_image.cols() + margin ||
			voxel_image(1) < -margin || voxel_image(1) >= depth_image.rows() + margin){
		return true;
	}
	return false;
}

}  // namespace tsdf


#include <burst/integer/intpow.hpp>
#include <burst/iterator/subsequence_iterator.hpp>
#include <utility/io/vector.hpp>

#include <doctest/doctest.h>

#include <boost/range/iterator_range.hpp>

#include <iterator>
#include <list>
#include <vector>

TEST_SUITE("subsequence_iterator")
{
    TEST_CASE("Конец итератора подпоследовательностей создаётся из его начала с помощью "
        "специальной метки-индикатора")
    {
        const auto sequence = {'a', 'b', 'c'};

        const auto subsequences_begin = burst::make_subsequence_iterator(sequence);
        const auto subsequences_end =
            burst::make_subsequence_iterator(burst::iterator::end_tag, subsequences_begin);

        const auto expected_subsequences =
            std::vector<std::vector<char>>
            {
                {'a'}, {'b'}, {'c'},
                {'a', 'b'}, {'a', 'c'}, {'b', 'c'},
                {'a', 'b', 'c'}
            };
        const auto subsequences = boost::make_iterator_range(subsequences_begin, subsequences_end);
        CHECK(subsequences == expected_subsequences);
    }

    TEST_CASE("Пустая подпоследовательность не содержит подпоследовательностей")
    {
        const auto sequence = std::vector<int>{};

        const auto subsequences_begin = burst::make_subsequence_iterator(sequence);
        const auto subsequences_end =
            burst::make_subsequence_iterator(burst::iterator::end_tag, subsequences_begin);

        CHECK(subsequences_begin == subsequences_end);
    }

    TEST_CASE("Одноэлементная последовательность содержит ровно одну подпоследовательность, равную "
        "всей последовательности")
    {
        const auto sequence = std::list<int>{3};

        const auto subsequence = burst::make_subsequence_iterator(sequence);
        const auto subsequences_end =
            burst::make_subsequence_iterator(burst::iterator::end_tag, subsequence);

        const auto expected_subsequence = std::vector<int>{3};
        CHECK(*subsequence == expected_subsequence);
        CHECK(std::distance(subsequence, subsequences_end) == 1);
    }

    TEST_CASE("Количество подпоследовательностей равно 2 ^ N - 1, где N — длина последовательности")
    // Пустая подпоследовательность считается концом, а потому не входит в число "действительных"
    // подпоследовательностей.
    {
        const auto sequence = {1, 1, 1, 1, 1, 1};

        const auto subsequences_begin = burst::make_subsequence_iterator(sequence);
        const auto subsequences_end =
            burst::make_subsequence_iterator(burst::iterator::end_tag, subsequences_begin);

        const auto expected_subsequence_count = burst::intpow(2, sequence.size()) - 1;
        CHECK(std::distance(subsequences_begin, subsequences_end) == expected_subsequence_count);
    }
}


// unit test file sinhc.hpp for the special functions test suite

//  (C) Copyright Hubert Holin 2003. Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

#include <functional>
#include <iomanip>
#include <iostream>
#include <complex>

#include <boost/math/special_functions/sinhc.hpp>

#include <boost/test/unit_test.hpp>

template<typename T>
void    sinhc_pi_test(const char * more_blurb)
{
    using    ::std::abs;

    using    ::std::numeric_limits;

    using    ::boost::math::sinhc_pi;

    BOOST_MESSAGE("Testing sinhc_pi in the real domain for "
        << more_blurb << ".");

    BOOST_CHECK_PREDICATE(::std::less_equal<T>(), 2,
        (
            abs(sinhc_pi<T>(static_cast<T>(0))-static_cast<T>(1)),
            numeric_limits<T>::epsilon()
        ));
}

template<typename T>
void    sinhc_pi_complex_test(const char * more_blurb)
{
    using    ::std::abs;
    using    ::std::sin;

    using    ::std::numeric_limits;

    using    ::boost::math::sinhc_pi;

    BOOST_MESSAGE("Testing sinhc_pi in the complex domain for "
        << more_blurb << ".");

    BOOST_CHECK_PREDICATE(::std::less_equal<T>(), 2,
        (
            abs(sinhc_pi<T>(::std::complex<T>(0, 1))-
                ::std::complex<T>(sin(static_cast<T>(1)))),
            numeric_limits<T>::epsilon()
        ));
}

void    sinhc_pi_manual_check()
{
    using    ::boost::math::sinhc_pi;

    BOOST_MESSAGE("sinc_pi");

    for    (int i = 0; i <= 100; i++)
    {
        BOOST_MESSAGE( ::std::setw(15)
                    << sinhc_pi<float>(static_cast<float>(i-50)/
                                                static_cast<float>(50))
                    << ::std::setw(15)
                    << sinhc_pi<double>(static_cast<double>(i-50)/
                                                static_cast<double>(50))
                    << ::std::setw(15)
                    << sinhc_pi<long double>(static_cast<long double>(i-50)/
                                                static_cast<long double>(50)));
    }

    BOOST_MESSAGE(" ");
}


#include <iostream>
#include <fstream>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include "headers/util.h"
#include "headers/rmat.h"
#include "headers/matutil.h"
#include "headers/matrix_wrapper.h"
#include "headers/timer.h"

using namespace std;
using namespace boost::numeric::ublas;

// constexpr size_t N = 20000;
// constexpr size_t N_EDGES = 4000000;
constexpr size_t N = 1000;
constexpr size_t N_EDGES = 25500;

template <class T>
inline void saveToFile(const string &name, T *mat, timer *t) {
  string fileName = "data/benchmark/" + name + ".csv";
  ofstream ofs;
  ofs.open(fileName, ios::trunc);
  t->start();
  will::matutil::writeAdjMatrix(*mat, ofs);
  t->stop();
}

template <class T>
inline void runWrappedBenchmark(const RmatConfig &cfg, const string &shortname,
                                bool parallel = false, bool useMutex = false) {
  string name = (parallel ? "par_" : "seq_") + shortname;
  cout << "Running " << name << " benchmark" << endl;
  timer t1, t2, t3;
  t1.start();
  T *w = new T(N, N, 0, useMutex);
  t1.stop();
  t2.start();
  if (parallel) rmat<T>(w, N_EDGES, cfg);
  else
    rmatSeq<T>(w, N_EDGES, cfg);
  t2.stop();
  saveToFile<T>(name, w, &t3);
  cout << "Took " << t1.get_total() << " to allocate" << endl;
  cout << "Took " << t2.get_total() << " to run RMAT" << endl;
  cout << "Took " << t3.get_total() << " to save to file" << endl;
}

inline void runListRmat(const RmatConfig &cfg, const string &shortname,
                                bool parallel = true) {
  if (parallel != true)
    cout << "WARNING: no sequential implementation yet" << endl;
  string name = (parallel ? "par_" : "seq_") + shortname;
  cout << "Running " << name << " benchmark" << endl;
  timer t1, t2, t3;
  t1.start();
  std::list<Edge> l;
  t1.stop();
  t2.start();
  l = listRmat(N, N_EDGES, cfg);
  t2.stop();
  EdgeList el(&l, N);
  saveToFile<EdgeList>(name, &el, &t3);
  cout << "Took " << t1.get_total() << " to allocate" << endl;
  cout << "Took " << t2.get_total() << " to run RMAT" << endl;
  cout << "Took " << t3.get_total() << " to save to file" << endl;
}

int main() {
  const RmatConfig cfg(0.57, 0.19, 0.19);

  cout << "===== Running sequential benchmarks =====" << endl;
  runWrappedBenchmark<MatrixWrapper<AdjMatrix>>(cfg, "boost-mat", false);
  // runWrappedBenchmark<MatrixWrapper<CompAdjMat>>(cfg, "compressed-mat", false);
  runWrappedBenchmark<MatrixWrapper<SparseAdjMat>>(cfg, "sparse-mat", false);
  runWrappedBenchmark<CustomMatrix>(cfg, "custom-mat", false);

  cout << "===== Running parallel benchmarks =====" << endl;
  runWrappedBenchmark<MatrixWrapper<AdjMatrix>>(cfg, "boost-mat", true);
  // runWrappedBenchmark<MatrixWrapper<CompAdjMat>>(cfg, "compressed-mat-mut",
                                                //  true, true);
  runWrappedBenchmark<MatrixWrapper<SparseAdjMat>>(cfg, "sparse-mat-mut",
                                                 true, true);
  runWrappedBenchmark<CustomMatrix>(cfg, "custom-mat", true);
  runListRmat(cfg, "custom-list");

  cout << "Done!" << endl;
  // {
  //   timer t;
  //   MatrixWrapper<AdjMatrix> *w = new MatrixWrapper<AdjMatrix>(50000, 50000);
  //   RmatConfig cfg(0.57, 0.19, 0.19);
  //   t.start();
  //   rmatSeq<AdjMatrix>(w, 505571000, cfg);
  //   t.stop();
  //   cout << w->getMat() << endl;
  //   cout << "Took: " << t.get_total() << endl;
  // }

  // {
  //   timer t;
  //   RmatConfig cfg(0.57, 0.19, 0.19);
  //   t.start();
  //   std::list<Edge> res = listRmat(30000, 50557100, cfg);
  //   t.stop();
  //   cout << res.size() << endl;
  //   cout << "Took: " << t.get_total() << endl;
  // }

  // will::matutil::writeAdjMatrix(*w, std::cout);
  // std::cout << m << std::endl;
  // MatrixWrapper<CompAdjMat> w(10, 10);
  // w.insert(3, 3, 9);
  // w.set(1, 1, 9);
  // cout << static_cast<int>(w.get(3, 3)) << endl;
  // m = w.getMat();
  // will::matutil::writeAdjMatrix(*m, std::cout);
}


// -----------------------------------------------------------------------
// RTToolbox - DKFZ radiotherapy quantitative evaluation library
//
// Copyright (c) German Cancer Research Center (DKFZ),
// Software development for Integrated Diagnostics and Therapy (SIDT).
// ALL RIGHTS RESERVED.
// See rttbCopyright.txt or
// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the above copyright notices for more information.
//
//------------------------------------------------------------------------

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include "litCheckMacros.h"

#include "rttbBaseType.h"
#include "rttbBaseTypeModels.h"
#include "rttbBioModel.h"
#include "rttbDVH.h"
#include "rttbTCPLQModel.h"
#include "rttbNTCPLKBModel.h"
#include "rttbNTCPRSModel.h"
#include "rttbBaseTypeModels.h"
#include "rttbBioModelCurve.h"
#include "rttbInvalidParameterException.h"
#include "rttbBioModelScatterPlots.h"

namespace rttb
{
	namespace testing
	{

		typedef core::DVH::DataDifferentialType DataDifferentialType;

		/*! @brief RTBioModelTest. TCP calculated using a DVH PTV and LQ Model. NTCP tested using 3 Normal Tissue DVHs and LKB/RS Model.
		TCPLQ:
		1) test constructors (values as expected?)
		2) test init (calcTCPxxx)
		3) test set/get<Values>

		NTCP (LKB):
		1) test constructors (values as expected?)
		2) test init (calcxxx)
		3) test set/get<Values>

		NTCP (RS):
		1) test constructors (values as expected?)
		2) test init (calcxxx)
		3) test set/get<Values>
		*/
		int BioModelTest(int argc, char* argv[])
		{
			PREPARE_DEFAULT_TEST_REPORTING;

			//generate artificial DVH and corresponding statistical values
			DoseTypeGy binSize = DoseTypeGy(0.1);
			DoseVoxelVolumeType voxelVolume = 8;

			DataDifferentialType aDataDifferential;

			DoseCalcType value = 0;
			DVHVoxelNumber numberOfVoxels = 0;

			// creat default values
			for (int i = 0; i < 98; i++)
			{
				value = 0;
				numberOfVoxels += value;
				aDataDifferential.push_back(value);
			}

			aDataDifferential.push_back(10);
			aDataDifferential.push_back(20);

			const IDType structureID = "myStructure";
			const IDType doseID = "myDose";
			const IDType voxelizationID = "myVoxelization";

			core::DVH::Pointer dvhPtr = boost::make_shared<core::DVH>(aDataDifferential, binSize,
			                               voxelVolume, structureID,
			                               doseID, voxelizationID);

			//test TCP LQ Model
			models::BioModelParamType alpha = 0.35;
			models::BioModelParamType beta = 0.023333333333333;
			models::BioModelParamType roh = 10000000;
			int numFractions = 8;

			DoseTypeGy normalizationDose = 50;

			//1) test constructors (values as expected?)
			rttb::models::TCPLQModel tcplq = rttb::models::TCPLQModel();
			CHECK_EQUAL(0, tcplq.getAlphaMean());
			CHECK_EQUAL(0, tcplq.getAlphaBeta());
			CHECK_EQUAL(0, tcplq.getRho());
			CHECK_EQUAL(0, tcplq.getValue());

			tcplq = rttb::models::TCPLQModel(dvhPtr, roh, numFractions, alpha / beta, alpha, 0.08);
			CHECK_EQUAL(alpha, tcplq.getAlphaMean());
			CHECK_EQUAL(alpha / beta, tcplq.getAlphaBeta());
			CHECK_EQUAL(roh, tcplq.getRho());
			CHECK_EQUAL(0, tcplq.getValue());

			tcplq = rttb::models::TCPLQModel();
			CHECK_EQUAL(0, tcplq.getAlphaMean());
			CHECK_EQUAL(0, tcplq.getAlphaBeta());
			CHECK_EQUAL(0, tcplq.getRho());
			CHECK_EQUAL(0, tcplq.getValue());

			tcplq = rttb::models::TCPLQModel(dvhPtr, alpha, beta, roh, numFractions);
			CHECK_EQUAL(alpha, tcplq.getAlphaMean());
			CHECK_EQUAL(alpha / beta, tcplq.getAlphaBeta());
			CHECK_EQUAL(roh, tcplq.getRho());
			CHECK_EQUAL(0, tcplq.getValue());

			//2) test init (calcTCPxxx)
			CHECK_NO_THROW(tcplq.init(1));

			//3) test set/get<Values>
			CHECK_EQUAL(0, tcplq.getValue());
			CHECK_NO_THROW(tcplq.setParameters(alpha, 10, roh, 0.08));
			CHECK_EQUAL(10, tcplq.getAlphaBeta());
			CHECK_EQUAL(0.08, tcplq.getAlphaVariance());
			CHECK_EQUAL(alpha, tcplq.getAlphaMean());
			CHECK_EQUAL(roh, tcplq.getRho());

			CHECK_NO_THROW(models::getCurveDoseVSBioModel(tcplq, normalizationDose));

			std::vector<models::BioModelParamType> aParameterVector;
			aParameterVector.push_back(alpha + 0.02);
			CHECK_THROW_EXPLICIT(tcplq.setParameterVector(aParameterVector), core::InvalidParameterException);
			aParameterVector.push_back(0.06);
			aParameterVector.push_back(8);
			aParameterVector.push_back(roh / 10);
			CHECK_NO_THROW(tcplq.setParameterVector(aParameterVector));
			CHECK_EQUAL(8, tcplq.getAlphaBeta());
			CHECK_EQUAL(0.06, tcplq.getAlphaVariance());
			CHECK_EQUAL(alpha + 0.02, tcplq.getAlphaMean());
			CHECK_EQUAL(roh / 10, tcplq.getRho());

			for (int i = 0; i < 4; i++)
			{
				CHECK_NO_THROW(tcplq.setParameterByID(i, models::BioModelParamType(i)));
			}

			CHECK_THROW_EXPLICIT(tcplq.setParameterByID(4, 4.0), core::InvalidParameterException);

			CHECK_EQUAL(0, tcplq.getParameterID("alphaMean"));
			CHECK_EQUAL(0, tcplq.getAlphaMean());
			CHECK_EQUAL(1, tcplq.getParameterID("alphaVariance"));
			CHECK_EQUAL(1, tcplq.getAlphaVariance());
			CHECK_EQUAL(2, tcplq.getParameterID("alpha_beta"));
			CHECK_EQUAL(2, tcplq.getAlphaBeta());
			CHECK_EQUAL(3, tcplq.getParameterID("rho"));
			CHECK_EQUAL(3, tcplq.getRho());

			//test NTCPLKBModel
			//1) test constructors (values as expected?)
			models::BioModelParamType aVal = 10;
			models::BioModelParamType mVal = 0.16;
			models::BioModelParamType d50Val = 35;
			CHECK_NO_THROW(rttb::models::NTCPLKBModel());
			rttb::models::NTCPLKBModel lkb = rttb::models::NTCPLKBModel();
			CHECK_EQUAL(0, lkb.getA());
			CHECK_EQUAL(0, lkb.getM());
			CHECK_EQUAL(0, lkb.getD50());
			CHECK_EQUAL(0, lkb.getValue());
			CHECK_NO_THROW(rttb::models::NTCPLKBModel(dvhPtr, d50Val, mVal, aVal));
			lkb = rttb::models::NTCPLKBModel(dvhPtr, d50Val, mVal, aVal);
			CHECK_EQUAL(0, lkb.getValue());
			CHECK_EQUAL(dvhPtr, lkb.getDVH());
			CHECK_EQUAL(aVal, lkb.getA());
			CHECK_EQUAL(mVal, lkb.getM());
			CHECK_EQUAL(d50Val, lkb.getD50());

			//2) test init (calcxxx)
			CHECK_NO_THROW(lkb.init(1));
			lkb.getValue();

			//3) test set/get<Values>
			lkb = rttb::models::NTCPLKBModel();
			CHECK_EQUAL(0, lkb.getA());
			CHECK_EQUAL(0, lkb.getM());
			CHECK_EQUAL(0, lkb.getD50());
			lkb.setDVH(dvhPtr);
			CHECK_EQUAL(dvhPtr, lkb.getDVH());
			lkb.setA(aVal);
			CHECK_EQUAL(aVal, lkb.getA());
			lkb.setM(mVal);
			CHECK_EQUAL(mVal, lkb.getM());
			lkb.setD50(d50Val);
			CHECK_EQUAL(d50Val, lkb.getD50());

			CHECK_NO_THROW(models::getCurveEUDVSBioModel(lkb));

			aParameterVector.clear();
			aParameterVector.push_back(d50Val + 5);
			CHECK_THROW_EXPLICIT(lkb.setParameterVector(aParameterVector), core::InvalidParameterException);
			aParameterVector.push_back(mVal + 0.2);
			aParameterVector.push_back(aVal + 0.5);
			CHECK_NO_THROW(lkb.setParameterVector(aParameterVector));
			CHECK_EQUAL(aVal + 0.5, lkb.getA());
			CHECK_EQUAL(mVal + 0.2, lkb.getM());
			CHECK_EQUAL(d50Val + 5, lkb.getD50());

			for (int i = 0; i < 3; i++)
			{
				CHECK_NO_THROW(lkb.setParameterByID(i, models::BioModelParamType(i)));
			}

			CHECK_THROW_EXPLICIT(lkb.setParameterByID(3, 4.0), core::InvalidParameterException);

			CHECK_EQUAL(0, lkb.getParameterID("d50"));
			CHECK_EQUAL(0, lkb.getD50());
			CHECK_EQUAL(1, lkb.getParameterID("m"));
			CHECK_EQUAL(1, lkb.getM());
			CHECK_EQUAL(2, lkb.getParameterID("a"));
			CHECK_EQUAL(2, lkb.getA());

			//test NTCPRSModel
			//1) test constructors (values as expected?)
			CHECK_NO_THROW(rttb::models::NTCPRSModel());
			models::BioModelParamType gammaVal = 1.7;
			models::BioModelParamType sVal = 1;
			CHECK_NO_THROW(rttb::models::NTCPRSModel(dvhPtr, d50Val, gammaVal, sVal));
			rttb::models::NTCPRSModel rs = rttb::models::NTCPRSModel(dvhPtr, d50Val, gammaVal, sVal);
			CHECK_EQUAL(dvhPtr, rs.getDVH());
			CHECK_EQUAL(d50Val, rs.getD50());
			CHECK_EQUAL(gammaVal, rs.getGamma());
			CHECK_EQUAL(sVal, rs.getS());

			rs = rttb::models::NTCPRSModel();
			CHECK_EQUAL(0, rs.getGamma());
			CHECK_EQUAL(0, rs.getS());
			CHECK_EQUAL(0, rs.getD50());

			//3) test set/get<Values>
			rs.setDVH(dvhPtr);
			CHECK_EQUAL(dvhPtr, rs.getDVH());
			rs.setD50(d50Val);
			CHECK_EQUAL(d50Val, rs.getD50());
			rs.setGamma(gammaVal);
			CHECK_EQUAL(gammaVal, rs.getGamma());
			rs.setS(sVal);
			CHECK_EQUAL(sVal, rs.getS());

			//2) test init (calcxxx)
			CHECK_NO_THROW(rs.init(1));

			//3) test set/get<Values> continued
			aParameterVector.clear();
			aParameterVector.push_back(d50Val + 5);
			CHECK_THROW_EXPLICIT(rs.setParameterVector(aParameterVector), core::InvalidParameterException);
			aParameterVector.push_back(gammaVal + 0.2);
			aParameterVector.push_back(sVal + 0.5);
			CHECK_NO_THROW(rs.setParameterVector(aParameterVector));
			CHECK_EQUAL(gammaVal + 0.2, rs.getGamma());
			CHECK_EQUAL(sVal + 0.5, rs.getS());
			CHECK_EQUAL(d50Val + 5, rs.getD50());

			for (int i = 0; i < 3; i++)
			{
				CHECK_NO_THROW(rs.setParameterByID(i, models::BioModelParamType(i)));
			}

			CHECK_THROW_EXPLICIT(rs.setParameterByID(3, 4.0), core::InvalidParameterException);

			CHECK_EQUAL(0, rs.getParameterID("d50"));
			CHECK_EQUAL(0, rs.getD50());
			CHECK_EQUAL(1, rs.getParameterID("gamma"));
			CHECK_EQUAL(1, rs.getGamma());
			CHECK_EQUAL(2, rs.getParameterID("s"));
			CHECK_EQUAL(2, rs.getS());

			//Scatter plot tests
			CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, 0,
			               normalizationDose)); //variance=0, will be set to 1e-30
			CHECK_THROW_EXPLICIT(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, alpha * 0.1, 0),
			                     core::InvalidParameterException);//normalisationdose=0
			CHECK_THROW_EXPLICIT(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, alpha * 0.1,
			                     normalizationDose, 10000, 0, 0),
			                     core::InvalidParameterException);//maxDose-minDose=0

			RETURN_AND_REPORT_TEST_SUCCESS;

		}

	}//testing
}//rttb


//  (C) Copyright Nick Thompson 2020.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <boost/math/tools/luroth_expansion.hpp>
#include <boost/math/constants/constants.hpp>

#ifndef BOOST_MATH_STANDALONE
#include <boost/multiprecision/mpfr.hpp>
using boost::multiprecision::mpfr_float;
#endif // BOOST_MATH_STANDALONE

using boost::math::constants::pi;
using boost::math::tools::luroth_expansion;

int main() {
    #ifndef BOOST_MATH_STANDALONE
    using Real = mpfr_float;
    mpfr_float::default_precision(1024);
    #else
    using Real = long double;
    #endif

    auto luroth = luroth_expansion(pi<Real>());
    std::cout << luroth << "\n";
}


//
// Created by krab1k on 31/10/18.
//

#include <functional>
#include <vector>
#include <cmath>
#include <Eigen/LU>

#include "eqeq.h"
#include "../parameters.h"
#include "../geometry.h"

CHARGEFW2_METHOD(EQeq)

Eigen::VectorXd EQeq::EE_system(const std::vector<const Atom *> &atoms, double total_charge) const {

    size_t n = atoms.size();

    const double lambda = 1.2;
    const double k = 14.4;
    double H_electron_affinity = -2.0; // Exception for hydrogen mentioned in the article

    Eigen::MatrixXd A = Eigen::MatrixXd::Zero(n + 1, n + 1);
    Eigen::VectorXd b = Eigen::VectorXd::Zero(n + 1);
    Eigen::VectorXd J = Eigen::VectorXd::Zero(n);
    Eigen::VectorXd X = Eigen::VectorXd::Zero(n);

    for (size_t i = 0; i < n; i++) {
        const auto &atom_i = *atoms[i];
        if (atom_i.element().symbol() == "H") {
            X(i) = (atom_i.element().ionization_potential() + H_electron_affinity) / 2;
            J(i) = atom_i.element().ionization_potential() - H_electron_affinity;
        } else {
            X(i) = (atom_i.element().ionization_potential() + atom_i.element().electron_affinity()) / 2;
            J(i) = atom_i.element().ionization_potential() - atom_i.element().electron_affinity();
        }
    }

    for (size_t i = 0; i < n; i++) {
        const auto &atom_i = *atoms[i];
        A(i, i) = J(i);
        b(i) = -X(i);
        for (size_t j = i + 1; j < n; j++) {
            const auto &atom_j = *atoms[j];
            double a = std::sqrt(J(i) * J(j)) / k;
            double Rij = distance(atom_i, atom_j);
            double overlap = std::exp(-a * a * Rij * Rij) * (2 * a - a * a * Rij - 1 / Rij);
            auto x = lambda * k / 2 * (1 / Rij + overlap);
            A(i, j) = x;
            A(j, i) = x;
        }
    }

    A.row(n) = Eigen::VectorXd::Constant(n + 1, 1);
    A.col(n) = Eigen::VectorXd::Constant(n + 1, 1);
    A(n, n) = 0;
    b(n) = total_charge;

    return A.partialPivLu().solve(b).head(n);
}

std::vector<double> EQeq::calculate_charges(const Molecule &molecule) const {
    auto f = [this](const std::vector<const Atom *> &atoms, double total_charge) -> Eigen::VectorXd {
        return EE_system(atoms, total_charge);
    };

    Eigen::VectorXd q = solve_EE(molecule, f);

    return std::vector<double>(q.data(), q.data() + q.size());
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common/pixel_benchmark.h"
#include "common/pixel_fast_rng.h"

#include <opencv2/opencv.hpp>
#include <opencv2/core/simd_intrinsics.hpp>

//#define USE_MIPP
#ifdef USE_MIPP
#include "mipp.h"
#endif

#include <Eigen/Dense>
#include "xsimd/xsimd.hpp"

#ifdef _MSC_VER
#define USE_SSE 1
#endif

#ifdef USE_SSE
#include <xmmintrin.h> // SSE
#endif

#include "pixel_simd.h"

float dotproduct1(size_t len, float* va, float* vb)
{
    float sum = 0;
    for (size_t i=0; i<len; i++) {
        sum += va[i] * vb[i];
    }
    return sum;
}

// OpenCV universal intrinsics based implementation
float dotproduct2(size_t len, float* va, float* vb)
{
    size_t step = sizeof(cv::v_float32)/sizeof(float);
    cv::v_float32 v_sum = cv::vx_setzero_f32();
    size_t vec_size = len - len % step;
    for (size_t i=0; i< vec_size; i+=step)
    {
        cv::v_float32 v1 = cv::vx_load(va+i);
        cv::v_float32 v2 = cv::vx_load(vb+i);
        v_sum += v1 * v2;
    }

    float sum = cv::v_reduce_sum(v_sum);

    for (size_t i= vec_size; i<len; i++) {
        sum += va[i] * vb[i];
    }

    return sum;
}

#ifdef __ARM_NEON
float dotproduct3(size_t len, float* va, float* vb)
{
    const size_t step = 4; // 128 / sizeof(float)
    size_t vec_size = len - len % step;
    float32x4_t vres = vdupq_n_f32(0);
    float32x4_t v1;
    float32x4_t v2;
    float32x4_t vtmp;
    for (size_t i = 0; i < vec_size; i+=step) {
        v1 = vld1q_f32(va + i);
        v2 = vld1q_f32(vb + i);
        vtmp = vmulq_f32(v1, v2);
        vres = vaddq_f32(vtmp, vres);
    }

    float sum_lst[4];
    vst1q_f32(sum_lst, vres);

    float sum = 0;
    for (size_t i = 0; i < step; i++) {
        sum += sum_lst[i];
    }
    for (size_t i = vec_size; i < len; i++) {
        sum += va[i] * vb[i];
    }
    return sum;
}

float dotproduct3_cache(size_t len, float* va, float* vb)
{
    int segments = len / 4;
    int remain = len - (segments/4*4)*4;

    float32x4_t partSum = vdupq_n_f32(0);
    float32x4_t sum1 = vdupq_n_f32(0);
    float32x4_t sum2 = vdupq_n_f32(0);
    float32x4_t sum3 = vdupq_n_f32(0);
    float32x4_t sum4 = vdupq_n_f32(0);
    for (int i=0; i+3<segments; i+=4)
    {
        float32x4_t v11 = vld1q_f32(va);
        float32x4_t v12 = vld1q_f32(vb);

        float32x4_t v21 = vld1q_f32(va+4);
        float32x4_t v22 = vld1q_f32(vb+4);

        float32x4_t v31 = vld1q_f32(va+8);
        float32x4_t v32 = vld1q_f32(vb+8);

        float32x4_t v41 = vld1q_f32(va+12);
        float32x4_t v42 = vld1q_f32(vb+12);

        sum1 = vmlaq_f32(sum1, v11, v12);
        sum2 = vmlaq_f32(sum2, v21, v22);
        sum3 = vmlaq_f32(sum3, v31, v32);
        sum4 = vmlaq_f32(sum4, v41, v42);

        va += 16;
        vb += 16;
    }
    partSum = sum1 + sum2 + sum3 + sum4;

    for (int i=0; i<remain; i++) {
        float32x4_t vector1Neon = vld1q_f32(va);
        float32x4_t vector2Neon = vld1q_f32(vb);
        partSum = vmlaq_f32(partSum, vector1Neon, vector2Neon);

        va += 4;
        vb += 4;
    }

    float sum = partSum[0] + partSum[1] + partSum[2] + partSum[3];

    return sum;
}
#endif

#ifdef USE_MIPP
// MIPP based implementation
float dotproduct4(size_t len, float* va, float* vb)
{
    constexpr int step = mipp::N<float>();
    //printf("--- mipp::N<float>() is %d\n", mipp::N<float>());
    size_t vec_size = len - len % step;
    mipp::Reg<float> rc;
    mipp::Reg<float> ra, rb;
    rc.set0();
    for (size_t i=0; i< vec_size; i+=step) {
        ra.load(va + i);
        rb.load(vb + i);
        rc += ra * rb;
    }

    float sum = rc.sum();

    for (size_t i= vec_size; i<len; i++) {
        sum += va[i] * vb[i];
    }

    return sum;
}
#endif

// Eigen based implementation
float dotproduct5(size_t len, float* va, float* vb)
{
    Eigen::Map<Eigen::Matrix<float, 1, Eigen::Dynamic, Eigen::RowMajor>> vva(va, len);
    Eigen::Map<Eigen::Matrix<float, 1, Eigen::Dynamic, Eigen::RowMajor>> vvb(vb, len);
    float res = vva.dot(vvb);
    return res;
}

// xsimd based implementation
float dotproduct6(size_t len, float* va, float* vb)
{
    constexpr size_t simd_size = xsimd::simd_type<float>::size;
    size_t vec_size = len - len % simd_size;

    //xsimd::batch<float, simd_size> vres;
    //for (size_t i = 0; i < simd_size; i++) {
    //    vres[i] = 0;
    //}
    xsimd::batch<float, simd_size> vres(0.f);
    for (size_t i=0; i<vec_size; i+=simd_size)
    {
        auto vva = xsimd::load_unaligned(va + i);
        auto vvb = xsimd::load_unaligned(vb + i);
        vres += vva * vvb;
    }
    float sum = 0;
    for (size_t i = 0; i < simd_size; i++) {
        sum += vres[i];
    }
    for (size_t i = vec_size; i < len; i++) {
        sum += va[i] * vb[i];
    }
    return sum;
}

#ifdef USE_SSE
// SSE based implementation
float dotproduct7(size_t len, float* va, float* vb)
{
    const size_t step = 4; // 128 / sizeof(float)
    size_t vec_size = len - len % step;
    __m128 vacc = _mm_setzero_ps();
    __m128 v1;
    __m128 v2;
    for (size_t i=0; i<vec_size; i+=step) {
        v1 = _mm_loadu_ps(va + i);
        v2 = _mm_loadu_ps(vb + i);
        vacc = _mm_add_ps(_mm_mul_ps(v1, v2), vacc);
    }

    float sum_lst[step];
    _mm_store_ps(sum_lst, vacc);

    float sum = 0;
    for (size_t i = 0; i < step; i++) {
        sum += sum_lst[i];
    }

    for (size_t i = vec_size; i < len; i++) {
        sum += va[i] * vb[i];
    }
    return sum;
}
#endif

// pixel_simd based impl, for NEON/SSE/NONE-SIMD
float dotproduct8(size_t len, float* va, float* vb)
{
    const size_t step = 4; // 128 / sizeof(float)
    size_t vec_size = len - len % step;
    v_float32x4 v1, v2;
    v_float32x4 vres = vq_setzero_f32();
    for (size_t i = 0; i < vec_size; i += step) {
        v1 = vq_load_f32(va + i);
        v2 = vq_load_f32(vb + i);
        //vres = vq_add_f32(v_mul_f32(v1, v2), vres);
        vres = vq_fmadd_f32(v1, v2, vres);
    }
    float sum_lst[step];
    vq_store_f32(sum_lst, vres);
    float sum = 0;
    for (size_t i = 0; i < step; i++) {
        sum += sum_lst[i];
    }

    for (size_t i = vec_size; i < len; i++) {
        sum += va[i] * vb[i];
    }
    return sum;
}

static void opencv_simd_test()
{
    using namespace cv;
#ifdef CV_SIMD
    printf("CV_SIMD is : " CVAUX_STR(CV_SIMD) "\n");
    printf("CV_SIMD_WIDTH is : " CVAUX_STR(CV_SIMD_WIDTH) "\n");
    printf("CV_SIMD128 is : " CVAUX_STR(CV_SIMD128) "\n");
    printf("CV_SIMD256 is : " CVAUX_STR(CV_SIMD256) "\n");
    printf("CV_SIMD512 is : " CVAUX_STR(CV_SIMD512) "\n");
#else
    printf("CV_SIMD is NOT defined\n");
#endif

#ifdef CV_SIMD
    printf("sizeof(v_uint8) = %d\n", (int)sizeof(v_uint8));
    printf("sizeof(v_int32) = %d\n", (int)sizeof(v_int32));
    printf("sizeof(v_float32) = %d\n", (int)sizeof(v_float32));
#endif
}

int main2() {

    float c1[4] = {12.0f,12.0f,12.0f,12.0f};
    float c2[4] = {13.0f,12.0f,9.0f,12.0f};
    float32x4_t t1,t2;
    uint32x4_t rq;
    t1 = vld1q_f32(c1);
    t2 = vld1q_f32(c2);
    rq = vceqq_f32(t1,t2);
    printf("start\n");
    for( int i = 0;i < 4; i++){
        printf("%u\n",rq[i]);
    }
    printf("end\n");

    return 0;
}

int main() {
    //opencv_simd_test();

    size_t len = 200000000; //200M
    //size_t len = 9;
    float* va = (float*)malloc(sizeof(float)*len);
    float* vb = (float*)malloc(sizeof(float)*len);

    g_state.a = 7767517;
    double r_start = pixel_get_current_time();
    for (size_t i=0; i<len; i++) {
        va[i] = pixel_fast_random_float(-1.2, 1.2);
        vb[i] = pixel_fast_random_float(-1.2, 1.2);
    }
    double r_cost = pixel_get_current_time() - r_start;
    printf("time cost for random assign is %.6lf ms\n", r_cost);

    double t_start, t_cost;
    float res;

    t_start = pixel_get_current_time();
    res = dotproduct1(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl1(Naive),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);

    t_start = pixel_get_current_time();
    res = dotproduct2(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl2(OpenCV),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);

#ifdef __ARM_NEON
    t_start = pixel_get_current_time();
    res = dotproduct3(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl3(neon),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);
#endif

#ifdef USE_MIPP
    t_start = pixel_get_current_time();
    res = dotproduct4(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl4(MIPP),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);
#endif

    t_start = pixel_get_current_time();
    res = dotproduct5(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl5(Eigen),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);

    //t_start = pixel_get_current_time();
    //res = dotproduct6(len, va, vb);
    //t_cost = pixel_get_current_time() - t_start;
    //printf("impl6(xsimd),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);

#ifdef USE_SSE
    t_start = pixel_get_current_time();
    res = dotproduct7(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl7(sse),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);
#endif

    t_start = pixel_get_current_time();
    res = dotproduct8(len, va, vb);
    t_cost = pixel_get_current_time() - t_start;
    printf("impl8(pixel),\tresult is %.6f, time cost is %.6lf ms\n", res, t_cost);

#if defined(PIXEL_NEON)
    printf("defined PIXEL_NEON\n");
#else
    printf("not defined PIXEL_NEON\n");
#endif

    free(va);
    free(vb);

    return 0;
}


#include <cassert>
#include <boost/contract.hpp>

int
factorial (int n)
{
  int result;

  boost::contract::check c (
      boost::contract::function ()
          .precondition (
              [&]
              {
                BOOST_CONTRACT_ASSERT (n >= 0);  // Non-negative natural number.
                BOOST_CONTRACT_ASSERT (n <= 12); // Max function input.
              })
          .postcondition (
              [&]
              {
                BOOST_CONTRACT_ASSERT (result >= 1);
                if (n < 2)
                { // Select assertion.
                  BOOST_CONTRACT_ASSERT (result == 1);
                }
                else
                {
                  // Assertions automatically disabled in other assertions.
                  // Therefore, this postcondition can recursively call the
                  // function without causing infinite recursion.
                  BOOST_CONTRACT_ASSERT_AUDIT (n * factorial (n - 1) != 0);
                }
              }));

  return n < 2 ? (result = 1) : (result = n * factorial (n - 1));
}

int
main ()
{
  return factorial (4) == 24 ? 0 : 1;
}


//main.cpp

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <Eigen/Core>

matrixXd weightInitialisation(double maxWeight, int width, int height){

  matrixXd weights(width, height);

  for (unsigned i=1; i <= width; ++i){
    for (unsigned j=1; j <= height; ++j){
      double f = (double)rand() / RAND_MAX;
      weights(i, j) = f * (2 * maxWeight) - maxWeight;
    }
  }

  return weights;
}

void feedForward(MatrixXd inputMatrix, MatrixXd weightMatrix,
                VectorXd biasMatrix, MatrixXd &outputMatrix,
                MatrixXd &netMatrix){

  MatrixXd concatenatedInput(inputMatrix.rows(), inputMatrix.cols() + 1);
  concatenatedInput << inputMatrix, biasMatrix;

  //net = mul(weights, horcat(inputs, bias))
  //output = activate(net)

  netMatrix = weightMatrix * concatenatedInput;
  outputMatrix = activate(netMatrix);

}

void networkError(MatrixXd inputMatrix, MatrixXd weightMatrix,
                VectorXd biasMatrix, MatrixXd targetOutputMatrix,
                VectorXi targetClassVector, double error, double classError){

  feedForward(inputMatrix weightMatrix, biasMatrix,
    MatrixXd outputMatrix, Matrix netMatrix);

  error = sum((targetOutputMatrix - outputMatrix)^2.0)
    / (sample_count * output_count);

  outputToClass(int n, outputMatrix, &classVector);

  c = sum_all_components(classVector != targetClassVector)/sample_count;

}

matrixXd backPropogration(MatrixXd inputMatrix, MatrixXd weightMatrix,
  double eta, VectorXd biasMatrix){

  //currently dummy function
  return weightMatrix;

}

void trainingFunction(){

}

double activationFunction(double x){
  return (tanh(x) + 1.0)/2.0;
}

double activationFunctionDerivative(double x){
  return (1.0 - (tanh(x)^2.0))/2.0;
}

void checkColumns(MatrixXi &Matrix, VectorXi &Vector){
  if (Matrix.cols() /= Vector.rows()){
      std::cout << "Mismatch between output matrix columns and class vector rows" << std::endl;
      exit (EXIT_FAILURE);
  }
}

void outputToClass(int n, MatrixXi outputMatrix, VectorXi &classVector){

  checkColumns(outputMatrix, classVector);

  for (unsigned i=1; i <= n; ++i){
    for (unsigned j=1; j <= 3; ++j){
      if (outputMatrix(i, j) == 1){
        classVector(i) = j;
      }
    }
  }
}

void outputToMatrix(int n, MatrixXi &outputMatrix, VectorXi classVector){

  checkColumns(outputMatrix, classVector);

  for (unsigned i=1; i <= n; ++i){
    for (unsigned j=1; j <= 3; ++j){
      if (classVector(i) == j){
        outputMatrix(i, j) = 1;
      }
      else {
        outputMatrix(i, j) = 0;
      }
    }
  }
}

int main (){

  return 0;
}


#include <iostream>
#include <Eigen/Dense>
#include "Stack.h"
#include <ctime>

using namespace Eigen;
using namespace std;
/*int main() {
    MatrixXd m = MatrixXd::Random(3,3);
    m = (m + MatrixXd::Constant(3,3,1.2)) * 50;
    cout << "m =" << endl << m << endl;
    VectorXd v(3);
    v << 1, 2, 3;
    cout << "m * v =" << endl << m * v << endl;
    std::cout << "Hello, World!" << std::endl;
    return 0;
}*/
/*
 int main(){
     for (int ix = 0; ix<10;++ix) {
         cout << ix << '\n';
     }
     cout<<"change"<<'\n';
     for (int iy = 0; iy<10;iy++) {
         cout << iy << '\n';
     }
     return 0;
 }*/

/*testing the Eigen template
int main (){
    Matrix<double, 3,3> A;
    A<<1,2,3,
       4,5,6,
       7,8,9;
    cout << A << '\n'<< endl;
    A<<A,A;
    cout<< A <<endl;
return 0;
};
*/
MatrixXd myproduct (MatrixXd b,MatrixXd c,int m1,int m2) {
    MatrixXd a(c.rows(),c.cols());
    int temp_index1 = 0;
    int temp_index2 = 0;
    for (int index1_1 = 1; index1_1 <= m1; ++index1_1) {
        for (int index1_2 = 1; index1_2 <= b.rows(); ++index1_2) {
            for (int index1_3 = 1; index1_3 <= m2; ++index1_3) {
                for (int index1_4 = 1; index1_4 <= b.rows(); ++index1_4) {
                    temp_index1 = (index1_1 - 1) * b.rows() * m2 + (index1_2 - 1) * m2 + index1_3 - 1;
                    temp_index2 = (index1_1 - 1) * b.rows() * m2 + (index1_4 - 1) * m2 + index1_3 - 1;
                    //cout<<temp_index1<<endl;
                    //cout<<temp_index2<<endl;
                    a(temp_index1, 0) = a(temp_index1, 0) + b(index1_2 - 1, index1_4 - 1) * c(temp_index2, 0);
                    //cout<<index1_4-1;
                }
            }
        }
    }
    return a;
}
int main(){
    //MatrixXd A(27,1);
    MatrixXd B = MatrixXd::Random(10,10);
    MatrixXd C = MatrixXd::Random(400,1);
    //MatrixXd D(9,1);
    int _size_m1 = 4;
    int _size_m2 = 10;
    /*cout<<C(1,0);
    int temp = 1;
    C(1,0)=temp;
    cout<<temp;
    cout<<C(temp,0);*/
    MatrixXd E;
    clock_t t1 = clock();
    for (int i = 0; i<=100000;++i){
        E = myproduct(B,C,_size_m1,_size_m2);
    }
    clock_t t2 = clock();
    //cout<<E<<endl;
    cout<<(double)(t2-t1)/CLOCKS_PER_SEC<<endl;
    return 0;
}


#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/numeric/conversion/cast.hpp>

//typedef boost::multiprecision::cpp_dec_float_50 rub_float;
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<64> > rub_float;
typedef boost::multiprecision::cpp_int rub_int;
typedef boost::multiprecision::uint128_t rub_uint_128;

const rub_float RUB_UINT = rub_float(1000000000000.0);
const rub_uint_128 RUB_INT_MAX = rub_uint_128((uint64_t)10000000000000000000ull);

inline double rub_int_to_double(rub_int amount) {
        rub_uint_128 amount_128 = amount.convert_to<rub_uint_128>();
        //std::cout<<"uint128_t amount:" << amount_128 << std::endl;

	if(amount_128 < RUB_INT_MAX)
	{
		uint64_t int_amount = amount_128.convert_to<uint64_t>();
		//std::cout<< "amount < RUB_DEFAULT_DECIMAL :" << int_amount << std::endl;
		double ret = int_amount / 1000000000000.0;
		//std::cout<< "****** return value:" << ret << std::endl;
		return ret;
	}
	//std::setprecision(std::numeric_limits<rub_float>::max_digits10);
	//std::cout<<"# RUB int to double ==> rub_int:" << amount << std::endl;
	//rub_float amount_float = amount_128.convert_to<rub_float>();
	rub_float amount_float = rub_float(amount_128);
	//std::cout<<"# RUB int to double ==> rub_float:" << amount_float << std::endl;
	rub_float amount_rub = amount_float / RUB_UINT;
	//std::cout<<"#RUB int to double ==> amount_rub:" << amount_rub << std::endl;
	double ret = amount_rub.convert_to<double>();
	//std::cout<<"#RUB int to double ==> result:" << ret << std::endl;
	return ret;
}


//////////////////////////////////////////////////////////////////////////////
// random::poisson_ex::poisson_devroye::q_function::standard.hpp        	//
//                                                                          //
//                                                                          //
//  (C) Copyright 2010 Erwann Rogard                                        //
//  Use, modification and distribution are subject to the                   //
//  Boost Software License, Version 1.0. (See accompanying file             //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)        //
//////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_RANDOM_POISSON_EXT_DEVROYE_Q_FUNCTION_STANDARD_HPP_ER_2010
#define BOOST_RANDOM_POISSON_EXT_DEVROYE_Q_FUNCTION_STANDARD_HPP_ER_2010
#include <cmath>
#include <stdexcept>
#include <string>
#include <boost/format.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/random/poisson_ext/devroye/detail/math.hpp>

namespace boost{
namespace random{
namespace poisson{
namespace devroye{
namespace detail{

	// The q-function is needed in either version of step4 of the algorithm.
	// See equation (4), p.199.
    //
    // TODO make static
    template<typename Int,typename T,typename P>
    struct q
    {

		typedef devroye::detail::math<Int,T,P> ma_;

        // TODO perhaps make this a template or runtime choice
        // For now overwrite manually. Checking may impedede speed a bit.
        typedef boost::mpl::bool_<true> do_check_;

        typedef std::string str_;
        typedef boost::format f_;
		static const str_ name(){ return "devroye::detail::q"; }

		public:

        q(){}

        typedef T float_;
		typedef Int int_;

        float_ q_fun(const int_& mean,const int_& y)const{
        	BOOST_ASSERT(y>=(-mean));
        	float_ result;

			if(y<0){
            	result = this->impl1(mean,y);
            }else{
            	if(y==0){
					result = this->impl2(mean,y);
                }else{
                	if(y>0){
                		result = this->impl3(mean,y);
                    }else{
                    	throw std::runtime_error(name() + "q");
                    }
                }
            }
            if(do_check_::value){
				this->check_against_slow_version(mean,y,result);
				this->check_lemma1(mean,y,result);
				this->check_lemma2(mean,y,result);
            }

            return result;
		}

    private:

        float_ slow_version(const int_& mean,const int_& y)const
        {
            return y * ma_::log(y)
                - ma_::log ( ma_::factorial(mean+y,P())/ma_::factorial(mean,P()) );
        }

        void check_against_slow_version(
            const int_& mean,
            const int_& y,
            const float_& result
        )const
        {
            static const str_ str = name()
                + "::check_bound_against_slow_version, q(%1%) = %2% != %3%";
            float_ alt = this->slow_version(mean,y);
            std::cout << "alt=" << alt << ' ';
            std::cout << "res=" << result << std::endl;
            if((result> alt + ma_::eps()) || (alt> result + ma_::eps()) )
            {
                throw std::runtime_error(
                    ( boost::format(str) % y % result % alt ).str()
                );
            }

        }

		// Lemma 1, p.199
		// q(y) < upper_bound(y) if y >= -mean
		float_ upper_bound(const int_& mean,const int_& y)const{
        	float_ num = ( - ma_::to_float(y) * ma_::to_float(1+y) );
            float_ den = ma_::to_float(2 * mean);
            den += ma_::to_float((y<0)?0:y);
            return num / den;
        }

		void check_lemma1(
        	const int_& mean,
            const int_& y,
            const float_& result
        )const{
            if( y >= mean ){
            	float_ b = this->upper_bound(mean,y);
                if( result>b ){
                	static const str_ str
                    	= name() + "::check_bound, %1% = q(%2%) > %3%";
                    throw std::runtime_error(
						( boost::format(str) % result % y % b ).str()
                    );
                }
            }
        }

		// Lemma 2, p. 199
		void check_lemma2(
            const int_& mean,
            const int_& y,
            const float_& q_val
        )const{
            static const str_ str1
            	= "q::check_lemma2(%1%,%2%,%3%) : failed condition(s) :";

        	int_ yp1 = y + 1;
        	int_ yp1pm1 = yp1 + mean;
            int_ y2p1 = 2 * y + 1;
            int_ ysq = y * y;
            int_ yp1sq = yp1 * yp1;
            int_ m2 = 2 * mean;
            int_ msq = mean * mean;
            int_ mcu = msq * mean;

            float_ lhs
            	= q_val + ma_::to_float( y * yp1 ) / ma_::to_float( m2 );

			str_ str2;
            bool fail = false;
            if(y>=0)
            {
                if(!(lhs >= 0)){
                	str2 += (f_("1: lhs = %1% >= 0")%lhs).str();
                    fail = true;
                }
            }else{
                if(!(lhs <= 0)){
                	str2 += (f_("1: lhs = %1% <= 0")%lhs).str();
                    fail = true;
                }
			}

            float_ rhs
            	=  ma_::to_float( y * yp1 * y2p1 ) / ma_::to_float(12 * msq);
            if(!(lhs <= rhs)){
				f_ f2(" 2: lhs = %1% <= rhs = %2%");
                str2 += (f2%lhs%rhs).str();
                fail = true;
            }

            float_ num = ma_::to_float( ysq * yp1sq );
            if(y>=0){
                rhs -= ( num / ma_::to_float(12 * mcu) );
            }else{
				rhs -= ( num / ma_::to_float(12 * msq * yp1pm1) );
            }
            if(!(lhs >= rhs)){
                f_ f3(" 3: lhs = %1% >= rhs = %2%");
                str2 += (f_(f3)%lhs%rhs).str();
                fail = true;
            }
			if(fail){
            	str2 = (f_(str1)%mean%y%q_val).str() + str2;
                throw std::runtime_error(str2);
			}
        }

		// TODO consider using
        // #include <boost/math/tools/series.hpp>

		float_ impl1(const int_& mean,const int_& y)const{
        	BOOST_ASSERT(y<0);
            float_ im1 = ma_::to_float(1) / ma_::to_float(mean);
        	float_ result = ma_::to_float(0);
            int_ n = -(y+1) + 1;
			for(int_ i = 0; i < n; i++){
            	result += ma_::log1p(-ma_::to_float(i)*im1,P());
            }
            return result;
        }

		float_ impl2(const int_& mean,const int_& y)const{
        	BOOST_ASSERT(y==0);
        	return ma_::to_float(0);
		}
		float_ impl3(const int_& mean,const int_& y)const{
        	BOOST_ASSERT(y>0);
            float_ im1 = ma_::to_float(1) / ma_::to_float(mean);
        	float_ result = ma_::to_float(0);
            int n = y+1;
			for(int_ i = 1; i < n; i++){
            	result -= ma_::log1p(ma_::to_float(i)*im1,P());
            }
            return result;
        }

	};

}// q_function
}// devroye
}// poisson
}// random
}// boost

#endif


#include <fstream>
#include <iostream>
#include <vector>

#include <boost/timer.hpp>
#include <opencv2/opencv.hpp>

#include "include/depth_predictor.h"
#include "include/util/image_processor.h"
#include "include/util/plotter.h"
#include "include/util/reader.h"

using namespace util;
using namespace dense_mapper;

/**********************************************
* 本程序演示了单目相机在已知轨迹下的稠密深度估计
* 使用极线搜索 + NCC 匹配的方式，与书本的 12.2 节对应
* 请注意本程序并不完美，你完全可以改进它——我其实在故意暴露一些问题(这是借口)。
***********************************************/

// ------------------------------------------------------------------
// parameters
const int border = 20;     // 边缘宽度
const int width = 640;     // 图像宽度
const int height = 480;    // 图像高度
const double fx = 481.2;   // 相机内参
const double fy = -480.0;  // WHY NEGATIVE?
const double cx = 319.5;
const double cy = 239.5;
const int ncc_window_size = 3;  // NCC 取的窗口半宽度
const double min_cov = 0.1;     // 收敛判定：最小方差
const double max_cov = 10.;     // 发散判定：最大方差

int main(int argc, char **argv) {
  if (argc != 2) {
    std::cout << "Usage: dense_mapping path_to_test_dataset" << std::endl;
    return -1;
  }

  // 从数据集读取数据
  std::vector<std::string> color_image_files;
  std::vector<Sophus::SE3d> poses_Twc;
  cv::Mat ref_depth;
  Reader::ReadRemode(width, height, argv[1], &color_image_files, &poses_Twc,
                     &ref_depth);
  std::cout << "read total " << color_image_files.size() << " files."
            << std::endl;

  DepthPredictor predictor(border, width, height, fx, fy, cx, cy,
                           ncc_window_size, min_cov, max_cov);

  // 第一张图
  const cv::Mat ref = cv::imread(color_image_files[0], 0);  // gray-scale image
  const Sophus::SE3d pose_ref_Twc = poses_Twc[0];           // reference pose
  const double init_depth = 3.0;                            // 深度初始值
  const double init_cov2 = 3.0;                             // 方差初始值
  cv::Mat depth(height, width, CV_64F, init_depth);         // 深度图
  cv::Mat depth_cov2(height, width, CV_64F, init_cov2);     // 深度图方差

  for (size_t index = 1; index < color_image_files.size(); ++index) {
    std::cout << "*** loop " << index << " ***" << std::endl;
    cv::Mat curr = cv::imread(color_image_files[index], 0);
    if (curr.data == nullptr) {
      continue;
    }
    Sophus::SE3d pose_curr_Twc = poses_Twc[index];

    // 坐标转换关系： T_cw * T_wr = T_cr
    Sophus::SE3d pose_T_cr = pose_curr_Twc.inverse() * pose_ref_Twc;
    predictor.Update(ref, curr, pose_T_cr, &depth, &depth_cov2);
    predictor.EvaludateDepth(ref_depth, depth);
    Plotter::PlotDepth(ref_depth, depth);
    cv::imshow("image", curr);
    cv::waitKey(1);
  }

  std::cout << "estimation returns, saving depth map ..." << std::endl;
  cv::imwrite("depth.png", depth);
  std::cout << "done." << std::endl;

  return 0;
}


#pragma once

#include <Eigen/Core>
#include <Eigen/Dense>

namespace FieldMath::detail
{
    // Scalar typedef
    typedef double scalar;

    // Dynamic Eigen typedefs
    typedef Eigen::Matrix<scalar, -1,  1> VectorX;
    typedef Eigen::Matrix<scalar,  1, -1> RowVectorX;
    typedef Eigen::Matrix<scalar, -1, -1> MatrixX;

    // 3D Eigen typedefs
    typedef Eigen::Matrix<scalar, 3, 1> Vector3;
    typedef Eigen::Matrix<scalar, 1, 3> RowVector3;
    typedef Eigen::Matrix<scalar, 3, 3> Matrix3;
}


#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <boost/math/special_functions/beta.hpp>
#include<boost/math/distributions.hpp>
#include <cmath>
#include "helpers.hpp"

using namespace std;
using namespace boost::math;

void computeIndices(const int n, const double beta, vector<vector<double> >& indices)
{
  #pragma omp parallel for
  for(int nn = n-1; nn >= 2; --nn)
  {
    for(int a = 1; a <= nn-1; ++a)
    {
      double p = computeAGI(beta, a, nn);
      indices[a-1][nn-a-1] = p;
    }
  }
}

void computeGittinsIndices(const int n, const double step, const double beta, vector<vector<double> >& indices)
{
  vector<vector<double> > tmpindices(n-1, vector<double>(n-1, 0));
  for(int a = 1; a < n; ++a)
  {
    tmpindices[a-1][n-a-1] = ((double)a/(double)n);
  }
  for(double p=(step/2.0); p <= 1.0; p+=step)
  {
    const double safe = p/(1-beta);
    for(int nn = n-1; nn >= 2; --nn)
    {
      for(int a = 1; a <= nn-1; ++a)
      {
        const double r = ((double)a/(double)nn);
        const double risky =  r*(1 + beta*tmpindices[a][nn-a-1]) + (1-r)*beta*tmpindices[a-1][nn-a];
        if(indices[a-1][nn-a-1] == 0 && safe > risky)
        {
          indices[a-1][nn-a-1] = p - step/2.0;
        }
        tmpindices[a-1][nn-a-1] = max(safe,risky);
      }
    }
  }
  /*for(int nn = n-1; nn >= 2; --nn)
  {
    for(int a = 1; a <= nn-1; ++a)
    {
      if(a > ceil(nn/2.0))
      {
        indices[a-1][nn-a-1] = 1 - step/2.0;
      }
    }
  }*/
}

void experiment(int n, double beta, double step)
{
  vector<vector<double> > gindices(n-1, vector<double>(n-1, 0));
  const static int maxnum = 500;
  /*cout << "computing gittins indices...." << endl;
  computeGittinsIndices(n,step,beta,gindices);*/
  cout << "loading gittins indices..." << endl;
  loadFromCSV("gixs_95.csv", gindices, maxnum, maxnum);

  cout << "checking values..." << endl;
  for(int a = 0; a < maxnum; ++a)
  {
    for(int b = 0; b < maxnum; ++b)
    {
      const double agi1 = computeAGI(beta, a + 1, a + b + 2);
      const double agi2 = computeAGI2(beta, a + 1, b + 1);
      const double agi2a = computeGeneralAGI(beta, a + 1, b + 1, 2);
      const double agi3 = computeGeneralAGI(beta, a + 1, b + 1, 3);
      const double agi4 = computeGeneralAGI(beta, a + 1, b + 1, 4);
      double eps = 1e-6;
      if (abs(agi2a - agi2) > eps)
      {
        cerr << "difference found in agi1 comp for a: " << a << ". b: " << b << ". agi2: " << agi2 << ". agi2a: " << agi2a << endl;
      }
      if (!(agi1 > agi2 - eps && agi2 > agi3 - eps && agi3 > agi4 - eps))
      {
        cerr << "unexpected order of indices. a = " << a << ". b = " << b << ". agi1: " << agi1 << ". agi2: " << agi2 << ". agi3:" << agi3 << ". agi4:" << agi4 << endl;
        //exit(1);
        //
      }

      if (agi1 < gindices[a][b] -eps)
      {
        cerr << "unexpected. a = " << a << ". b = " << b << ". agi1: " << agi1 << ". gidx: " << gindices[a][b] << endl;
      }
      if (agi2 < gindices[a][b] -eps)
      {
        cerr << "unexpected. a = " << a << ". b = " << b << ". agi2: " << agi2 << ". gidx: " << gindices[a][b] << endl;
      }
      if (agi3 < gindices[a][b] -eps)
      {
        cerr << "unexpected. a = " << a << ". b = " << b << ". agi3: " << agi3 << ". gidx: " << gindices[a][b] << endl;
      }
      if (agi4 < gindices[a][b] -eps)
      {
        cerr << "unexpected. a = " << a << ". b = " << b << ". agi4: " << agi4 << ". gidx: " << gindices[a][b] << endl;
      }
    }
  }

  cout << "done..." << endl;
}

void calculator()
{
  while(1)
  {
    double gamma, a, b;
    cin >> gamma;
    cin >> a;
    cin >> b;
    cout << std::setprecision(20) << computeAGI(gamma,a,a+b) << endl;
    cout << std::setprecision(20) << computeAGI2(gamma,a,b) << endl;
    cout << std::setprecision(20) << computeGeneralAGI(gamma,a,b,3) << endl;
  }
}

int main(int argc, const char** args)
{
  //this is bad lol
  //calculator();
  //cout << ::computeAGI2(0.99,3,3);
  if(argc != 3)
  {
    cerr << "need 2 arguments [n] [beta]" << endl;
    exit(1);
  }
  int n;
  string strnum = args[1];
  stringstream ss(strnum);
  ss >> n;

  double beta;
  string betastr = args[2];
  stringstream ssbeta(betastr);
  ssbeta >> beta;

  double step = 0.0001;
  experiment(n, beta, step);

  exit(0);
  vector<vector<double> > indices(n-1, vector<double>(n-1, 0));
  computeIndices(n, beta, indices);

  for(int i = 0; i < n-1; ++i)
  {
    for(int j=0; j < n-2; ++j)
    {
      cout << indices[i][j] << ", ";
    }
    cout << indices[i][n-2] << endl;
  }
}


#include <vector>
#include <iostream>
#include <string>
#include <armadillo>
#include <tgmath.h>
#include <limits>
#include <omp.h>

#include "global.h"
#include "utils.h"
#include "distr.h"

#include "ESS_Sampler.h"
#include "HESS_Chain.h"
#include "SSUR_Chain.h"
#include "dSUR_Chain.h"

extern omp_lock_t RNGlock; //defined in global.h
extern std::vector<std::mt19937_64> rng;

int drive_SSUR( arma::mat& Y , arma::mat& X , unsigned int& nChains , unsigned int& nIter ,
				std::string& inFile , std::string& outFilePath , std::string& gammaSampler , bool gPrior )
{

	// ****************************************
	// **********  INIT THE CHAIN *************
	// ****************************************
	std::cout << "Initialising the MCMC Chain " << std::endl;

	ESS_Sampler<SSUR_Chain> sampler( Y , X , nChains );// this is thus also some sort of default
		// although note that you won't pass the input phase with a different method string

	// *****************************
	// need to use getX because I need the intercept
	arma::mat Q,R; arma::qr(Q,R, *sampler[0]->getX() );
	arma::mat betaInit = arma::solve(R,arma::trans(Q) * Y );
	arma::umat gammaInit = betaInit > 0.5*arma::stddev(arma::vectorise(betaInit));
	gammaInit.shed_row(0);

	sampler[0] -> gammaInit( gammaInit );
    sampler[0] -> updateQuantities();
    sampler[0] -> logLikelihood();
	sampler[0] -> stepSigmaRhoAndBeta();

	// *****************************

	// set when the JT move should start
	unsigned int jtStartIteration = nIter/10;
	for( unsigned int i=0; i<nChains; ++i)
		sampler[i]->setJTStartIteration( jtStartIteration );

	// *****************************

	if( gPrior )
		for(unsigned int m=0; m<nChains; ++m)
			sampler[m] -> gPriorInit();

	for(unsigned int m=0; m<nChains; ++m)
		sampler[m] -> setGammaSamplerType(gammaSampler);

	// ****************************************

	// INIT THE FILE OUTPUT

	// clear the content of previous files
	std::ofstream logPOutFile; logPOutFile.open(outFilePath+inFile+"logP_out.txt", std::ios::out | std::ios::trunc); logPOutFile.close();
	// openlogP file in append mode
	logPOutFile.open( outFilePath+inFile+"logP_out.txt" , std::ios_base::app); // note we don't close!
	// open avg files in trunc mode to cut previous content
	std::ofstream gammaOutFile; gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc); gammaOutFile.close();
	std::ofstream gOutFile; gOutFile.open( outFilePath+inFile+"G_out.txt" , std::ios_base::trunc); gOutFile.close();
	std::ofstream piOutFile; piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc); piOutFile.close();
	std::ofstream htpOutFile; htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc); htpOutFile.close();

	// Output to file the current state
	arma::umat gamma_out = sampler[0] -> getGamma(); // out var for the gammas

	arma::umat g_out = arma::umat( sampler[0] -> getGAdjMat() ); // out var for G
	arma::mat beta_out = sampler[0] -> getBeta(); // out var for the betas
	arma::mat sigmaRho_out  = sampler[0] -> getSigmaRho(); // out var for the sigmas and rhos

	arma::vec tmpVec = sampler[0] -> getPi();
	arma::vec pi_out = tmpVec;
	tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
	arma::vec hotspot_tail_prob_out = tmpVec;

	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out)) << std::flush;
	gammaOutFile.close();

	gOutFile.open( outFilePath+inFile+"G_out.txt" , std::ios_base::trunc);
	gOutFile << ( arma::conv_to<arma::mat>::from(g_out) ) << std::flush;   // this might be quite long...
	gOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPTau() << " ";
	logPOutFile << 	sampler[0] -> getLogPEta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPJT() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;

	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out << std::flush;
	htpOutFile.close();

	// ########
	// ########
	// ######## Start
	// ########
	// ########

	std::cout << "Starting "<< nChains <<" (parallel) chain(s) for " << nIter << " iterations:" << std::endl << std::flush;

	unsigned int tick = 1000; // how may iter for each print?

	for(unsigned int i=1; i < nIter ; ++i)
	{

		sampler.step();

		// #################### END LOCAL MOVES

		// ## Global moves
		// *** end Global move's section

		// UPDATE OUTPUT STATE
		gamma_out += sampler[0] -> getGamma(); // the result of the whole procedure is now my new mcmc point, so add that up
		g_out += arma::umat( sampler[0] -> getGAdjMat() );

		beta_out += sampler[0] -> getBeta();
		sigmaRho_out += sampler[0] -> getSigmaRho();

		tmpVec = sampler[0] -> getPi();
		pi_out += tmpVec;
		tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
		hotspot_tail_prob_out += tmpVec;

		// Print something on how the chain is going
		if( (i+1) % tick == 0 )
		{

			std::cout << " Running iteration " << i+1 << " ... local Acc Rate: ~ gamma: " << Utils::round( sampler[0] -> getGammaAccRate() , 3 );
			std::cout << " -- JT: " << Utils::round( sampler[0] -> getJTAccRate() , 3 ) ;

			if( nChains > 1)
				std::cout << " -- Global: " << Utils::round( sampler.getGlobalAccRate() , 3 ) << std::endl;
			else
				std::cout << std::endl;

			// Output to files every now and then
			if( (i+1) % (tick*10) == 0 )
			{

				gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
				gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)i+1.0) << std::flush;
				gammaOutFile.close();

				gOutFile.open( outFilePath+inFile+"G_out.txt" , std::ios_base::trunc);
				gOutFile << ( arma::conv_to<arma::mat>::from(g_out) )/((double)(i-jtStartIteration)+1.0) << std::flush;   // this might be quite long...
				gOutFile.close();

				logPOutFile << 	sampler[0] -> getLogPTau() << " ";
				logPOutFile << 	sampler[0] -> getLogPEta() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPJT() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
				logPOutFile << 	sampler[0] -> getLogLikelihood();
				logPOutFile << 	std::endl << std::flush;

				piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
				piOutFile << pi_out/((double)i+1.0) << std::flush;
				piOutFile.close();

				htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
				htpOutFile << hotspot_tail_prob_out/((double)i+1.0) << std::flush;
				htpOutFile.close();
			}

		}

	} // end MCMC

	// Print the end
	std::cout << " MCMC ends. " /* << " Final temperature ratio ~ " << temperatureRatio  */<< "  --- Saving results and exiting" << std::endl;

	// ### Collect results and save them
	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)nIter+1.0) << std::flush;
	gammaOutFile.close();

	gOutFile.open( outFilePath+inFile+"G_out.txt" , std::ios_base::trunc);
	gOutFile << ( arma::conv_to<arma::mat>::from(g_out) )/((double)(nIter-jtStartIteration)+1.0) << std::flush;   // this might be quite long...
	gOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPTau() << " ";
	logPOutFile << 	sampler[0] -> getLogPEta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPJT() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;
	logPOutFile.close();

	// ----
	beta_out = beta_out/((double)nIter);
	beta_out.save(outFilePath+inFile+"beta_out.txt",arma::raw_ascii);

	sigmaRho_out = sigmaRho_out/((double)nIter);
	sigmaRho_out.save(outFilePath+inFile+"sigmaRho_out.txt",arma::raw_ascii);
	// -----

	// -----
	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out/((double)nIter) << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out/((double)nIter) << std::flush;
	htpOutFile.close();
	// -----

	std::cout << "Saved to :   "+outFilePath+inFile+"****_out.txt" << std::endl;
	std::cout << "Final w : " << sampler[0] -> getW() <<  std::endl;
	std::cout << "Final tau : " << sampler[0] -> getTau() << "    w/ proposal variance: " << sampler[0] -> getVarTauProposal() << std::endl;
	std::cout << "Final eta : " << sampler[0] -> getEta() <<  std::endl;
	// std::cout << "Final o : " << sampler[0] -> getO().t() << "       w/ proposal variance: " << sampler[0] -> getVarOProposal() << std::endl;
	// std::cout << "Final pi : " << sampler[0] -> getPi().t() << "       w/ proposal variance: " << sampler[0] -> getVarPiProposal() << std::endl;
	std::cout << "  -- Average Omega : " << arma::accu( sampler[0] -> getO() * sampler[0] -> getPi().t() )/((double)(sampler[0]->getP()*sampler[0]->getS())) <<  std::endl;
	if( nChains > 1 )
		std::cout << "Final temperature ratio : " << sampler[1]->getTemperature() <<  std::endl << std::endl ;

	// Exit

	std::cout << "DONE, exiting! " << std::endl << std::endl ;
	return 0;
}

int drive_dSUR( arma::mat& Y , arma::mat& X , unsigned int& nChains , unsigned int& nIter ,
				std::string& inFile , std::string& outFilePath , std::string& gammaSampler , bool gPrior )
{

	// ****************************************
	// **********  INIT THE CHAIN *************
	// ****************************************
	std::cout << "Initialising the MCMC Chain " << std::endl;

	ESS_Sampler<dSUR_Chain> sampler( Y , X , nChains );// this is thus also some sort of default
		// although note that you won't pass the input phase with a different method string

	// *****************************
	// need to use getX because I need the intercept
	arma::mat Q,R; arma::qr(Q,R, *sampler[0]->getX() );
	arma::mat betaInit = arma::solve(R,arma::trans(Q) * Y );
	arma::umat gammaInit = betaInit > 0.5*arma::stddev(arma::vectorise(betaInit));
	gammaInit.shed_row(0);

	sampler[0] -> gammaInit( gammaInit );
    sampler[0] -> updateQuantities();
    sampler[0] -> logLikelihood();
	sampler[0] -> stepSigmaRhoAndBeta();

	// *****************************

	if( gPrior )
		for(unsigned int m=0; m<nChains; ++m)
			sampler[m] -> gPriorInit();

	for(unsigned int m=0; m<nChains; ++m)
		sampler[m] -> setGammaSamplerType(gammaSampler);

	// *****************************

	// INIT THE FILE OUTPUT

	// clear the content of previous files
	std::ofstream logPOutFile; logPOutFile.open(outFilePath+inFile+"logP_out.txt", std::ios::out | std::ios::trunc); logPOutFile.close();
	// openlogP file in append mode
	logPOutFile.open( outFilePath+inFile+"logP_out.txt" , std::ios_base::app); // note we don't close!
	// open avg files in trunc mode to cut previous content
	std::ofstream gammaOutFile; gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc); gammaOutFile.close();
	std::ofstream gOutFile; gOutFile.open( outFilePath+inFile+"G_out.txt" , std::ios_base::trunc); gOutFile.close();
	std::ofstream piOutFile; piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc); piOutFile.close();
	std::ofstream htpOutFile; htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc); htpOutFile.close();

	// Output to file the current state
	arma::umat gamma_out = sampler[0] -> getGamma(); // out var for the gammas

	arma::mat beta_out = sampler[0] -> getBeta(); // out var for the betas
	arma::mat sigmaRho_out  = sampler[0] -> getSigmaRho(); // out var for the sigmas and rhos

	arma::vec tmpVec = sampler[0] -> getPi();
	arma::vec pi_out = tmpVec;
	tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
	arma::vec hotspot_tail_prob_out = tmpVec;

	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out)) << std::flush;
	gammaOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPTau() << " ";
	logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;

	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out << std::flush;
	htpOutFile.close();

	// ########
	// ########
	// ######## Start
	// ########
	// ########

	std::cout << "Starting "<< nChains <<" (parallel) chain(s) for " << nIter << " iterations:" << std::endl << std::flush;

	unsigned int tick = 1000; // how may iter for each print?

	for(unsigned int i=1; i < nIter ; ++i)
	{

		sampler.step();

		// #################### END LOCAL MOVES

		// ## Global moves
		// *** end Global move's section

		// UPDATE OUTPUT STATE
		gamma_out += sampler[0] -> getGamma(); // the result of the whole procedure is now my new mcmc point, so add that up

		beta_out += sampler[0] -> getBeta();
		sigmaRho_out += sampler[0] -> getSigmaRho();

		tmpVec = sampler[0] -> getPi();
		pi_out += tmpVec;
		tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
		hotspot_tail_prob_out += tmpVec;

		// Print something on how the chain is going
		if( (i+1) % tick == 0 )
		{

			std::cout << " Running iteration " << i+1 << " ... local Acc Rate: ~ gamma: " << Utils::round( sampler[0] -> getGammaAccRate() , 3 );

			if( nChains > 1)
				std::cout << " -- Global: " << Utils::round( sampler.getGlobalAccRate() , 3 ) << std::endl;
			else
				std::cout << std::endl;

			// Output to files every now and then
			if( (i+1) % (tick*10) == 0 )
			{

				gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
				gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)i+1.0) << std::flush;
				gammaOutFile.close();

				logPOutFile << 	sampler[0] -> getLogPTau() << " ";
				logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
				logPOutFile << 	sampler[0] -> getLogLikelihood();
				logPOutFile << 	std::endl << std::flush;

				piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
				piOutFile << pi_out/((double)i+1.0) << std::flush;
				piOutFile.close();

				htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
				htpOutFile << hotspot_tail_prob_out/((double)i+1.0) << std::flush;
				htpOutFile.close();
			}

		}

	} // end MCMC

	// Print the end
	std::cout << " MCMC ends. " /* << " Final temperature ratio ~ " << temperatureRatio  */<< "  --- Saving results and exiting" << std::endl;

	// ### Collect results and save them
	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)nIter+1.0) << std::flush;
	gammaOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPTau() << " ";
	logPOutFile << 	sampler[0] -> getLogPSigmaRho() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPBeta() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;
	logPOutFile.close();

	// ----
	beta_out = beta_out/((double)nIter);
	beta_out.save(outFilePath+inFile+"beta_out.txt",arma::raw_ascii);

	sigmaRho_out = sigmaRho_out/((double)nIter);
	sigmaRho_out.save(outFilePath+inFile+"sigmaRho_out.txt",arma::raw_ascii);
	// -----

	// -----
	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out/((double)nIter) << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out/((double)nIter) << std::flush;
	htpOutFile.close();
	// -----

	std::cout << "Saved to :   "+outFilePath+inFile+"****_out.txt" << std::endl;
	std::cout << "Final w : " << sampler[0] -> getW() <<  std::endl;
	std::cout << "Final tau : " << sampler[0] -> getTau() << "    w/ proposal variance: " << sampler[0] -> getVarTauProposal() << std::endl;
	// std::cout << "Final o : " << sampler[0] -> getO().t() << "       w/ proposal variance: " << sampler[0] -> getVarOProposal() << std::endl;
	// std::cout << "Final pi : " << sampler[0] -> getPi().t() << "       w/ proposal variance: " << sampler[0] -> getVarPiProposal() << std::endl;
	std::cout << "  -- Average Omega : " << arma::accu( sampler[0] -> getO() * sampler[0] -> getPi().t() )/((double)(sampler[0]->getP()*sampler[0]->getS())) <<  std::endl;
	if( nChains > 1 )
		std::cout << "Final temperature ratio : " << sampler[1]->getTemperature() <<  std::endl << std::endl ;

	// Exit

	std::cout << "DONE, exiting! " << std::endl << std::endl ;
	return 0;
}

int drive_HESS( arma::mat& Y , arma::mat& X , unsigned int& nChains , unsigned int& nIter ,
				std::string& inFile , std::string& outFilePath , std::string& gammaSampler , bool gPrior )
{

	// ****************************************
	// **********  INIT THE CHAIN *************
	// ****************************************
	std::cout << "Initialising the MCMC Chain " << std::endl;

	ESS_Sampler<HESS_Chain> sampler( Y , X , nChains );// this is thus also some sort of default
		// although note that you won't pass the input phase with a differetn method string

	// *****************************
	arma::mat Q,R; arma::qr(Q,R, *sampler[0]->getX() );
	arma::mat betaInit = arma::solve(R,arma::trans(Q) * Y );
	arma::umat gammaInit = betaInit > 0.5*arma::stddev(arma::vectorise(betaInit));
	gammaInit.shed_row(0);

	sampler[0] -> gammaInit( gammaInit );
    sampler[0] -> updateGammaMask();
    sampler[0] -> logLikelihood();

	// *****************************

	if( gPrior )
		for(unsigned int m=0; m<nChains; ++m)
			sampler[m] -> gPriorInit();

	for(unsigned int m=0; m<nChains; ++m)
		sampler[m] -> setGammaSamplerType(gammaSampler);

	// ****************************************

	// INIT THE FILE OUTPUT
	// clear the content of previous files

	std::ofstream logPOutFile; logPOutFile.open(outFilePath+inFile+"logP_out.txt", std::ios::out | std::ios::trunc); logPOutFile.close();
	// openlogP file in append mode
	logPOutFile.open( outFilePath+inFile+"logP_out.txt" , std::ios_base::app); // note we don't close!
	// open avg files in trunc mode to cut previous content
	std::ofstream gammaOutFile; gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc); gammaOutFile.close();
	std::ofstream piOutFile; piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc); piOutFile.close();
	std::ofstream htpOutFile; htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc); htpOutFile.close();

	// Output to file the current state
	arma::umat gamma_out = sampler[0] -> getGamma(); // out var for the gammas

	arma::vec tmpVec = sampler[0] -> getPi();
	arma::vec pi_out = tmpVec;
	tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
	arma::vec hotspot_tail_prob_out = tmpVec;

	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out)) << std::flush;
	gammaOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;

	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out << std::flush;
	htpOutFile.close();

	// ########
	// ########
	// ######## Start
	// ########
	// ########

	std::cout << "Starting "<< nChains <<" (parallel) chain(s) for " << nIter << " iterations:" << std::endl << std::flush;

	unsigned int tick = 1000; // how may iter for each print?

	for(unsigned int i=1; i < nIter ; ++i)
	{

		sampler.step();

		// #################### END LOCAL MOVES

		// ## Global moves
		// *** end Global move's section

		// UPDATE OUTPUT STATE
		gamma_out += sampler[0] -> getGamma(); // the result of the whole procedure is now my new mcmc point, so add that up

		tmpVec = sampler[0] -> getPi();
		pi_out += tmpVec;
		tmpVec.for_each( [](arma::vec::elem_type& val) { if(val>1.0) val = 1.0; else val=0.0; } );
		hotspot_tail_prob_out += tmpVec;

		// Print something on how the chain is going
		if( (i+1) % tick == 0 )
		{

			std::cout << " Running iteration " << i+1 << " ... local Acc Rate: ~ gamma: " << Utils::round( sampler[0] -> getGammaAccRate() , 3 );

			if( nChains > 1)
				std::cout << " -- Global: " << Utils::round( sampler.getGlobalAccRate() , 3 ) << std::endl;
			else
				std::cout << std::endl;

			// Output to files every now and then
			if( (i+1) % (tick*10) == 0 )
			{

				gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
				gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)i+1.0) << std::flush;
				gammaOutFile.close();

				logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
				logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
				logPOutFile << 	sampler[0] -> getLogLikelihood();
				logPOutFile << 	std::endl << std::flush;

				piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
				piOutFile << pi_out/((double)i+1.0) << std::flush;
				piOutFile.close();

				htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
				htpOutFile << hotspot_tail_prob_out/((double)i+1.0) << std::flush;
				htpOutFile.close();
			}

		}

	} // end MCMC

	// Print the end
	std::cout << " MCMC ends. " /* << " Final temperature ratio ~ " << temperatureRatio  */<< "  --- Saving results and exiting" << std::endl;

	// ### Collect results and save them
	gammaOutFile.open( outFilePath+inFile+"gamma_out.txt" , std::ios_base::trunc);
	gammaOutFile << (arma::conv_to<arma::mat>::from(gamma_out))/((double)nIter+1.0) << std::flush;
	gammaOutFile.close();

	logPOutFile << 	sampler[0] -> getLogPO() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPPi() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPGamma() <<  " ";
	logPOutFile << 	sampler[0] -> getLogPW() <<  " ";
	logPOutFile << 	sampler[0] -> getLogLikelihood();
	logPOutFile << 	std::endl << std::flush;
	logPOutFile.close();

	// -----
	piOutFile.open( outFilePath+inFile+"pi_out.txt" , std::ios_base::trunc);
	piOutFile << pi_out/((double)nIter) << std::flush;
	piOutFile.close();

	htpOutFile.open( outFilePath+inFile+"hotspot_tail_p_out.txt" , std::ios_base::trunc);
	htpOutFile << hotspot_tail_prob_out/((double)nIter) << std::flush;
	htpOutFile.close();
	// -----

	std::cout << "Saved to :   "+outFilePath+inFile+"****_out.txt" << std::endl;
	std::cout << "Final w : " << sampler[0] -> getW() << "       w/ proposal variance: " << sampler[0] -> getVarWProposal() << std::endl;
	// std::cout << "Final o : " << sampler[0] -> getO().t() << "       w/ proposal variance: " << sampler[0] -> getVarOProposal() << std::endl;
	// std::cout << "Final pi : " << sampler[0] -> getPi().t() << "       w/ proposal variance: " << sampler[0] -> getVarPiProposal() << std::endl;
	std::cout << "  -- Average Omega : " << arma::accu( sampler[0] -> getO() * sampler[0] -> getPi().t() )/((double)(sampler[0]->getP()*sampler[0]->getS())) <<  std::endl;
	if( nChains > 1 )
		std::cout << "Final temperature ratio : " << sampler[1]->getTemperature() <<  std::endl << std::endl ;

	// Exit

	std::cout << "DONE, exiting! " << std::endl << std::endl ;
	return 0;

}

int main(int argc, char *  argv[])
{
	omp_init_lock(&RNGlock);  // RNG lock for the parallel part

	unsigned int nIter = 10; // default number of iterations
	unsigned int s=1,p=1;      // might read them from a meta-data file, but for the moment is easier like this..
	unsigned int nChains = 1;

	std::string inFile = "data.txt";
	std::string outFilePath = "";
	std::string omegaInitPath = "";

	std::string method = "";
	std::string gammaSampler = "Bandit";
	bool gPrior = false;

    // ### Read and interpret command line (to put in a separate file / function?)
    int na = 1;
    while(na < argc)
    {
		if ( 0 == strcmp(argv[na],"--method") )
		{
			method = std::string(argv[++na]); // use the next

			if( method != "SSUR" && method != "HESS" && method != "dSUR")
			{
				std::cout << "Unknown method: only SSUR, dSUR or HESS are available" << std::endl;
			    return(1); //this is exit if I'm in a function elsewhere
			}

			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--gammaSampler") )
		{
			gammaSampler = std::string(argv[++na]); // use the next

			if( gammaSampler != "MC3" && gammaSampler != "mc3" && gammaSampler != "Bandit" && gammaSampler != "bandit")
			{
				std::cout << "Unknown gammaSampler method: only Bandit or MC3 are available" << std::endl;
			    return(1); //this is exit if I'm in a function elsewhere
			}

			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--gPrior") )
		{
			gPrior = true;

			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--nIter") )
		{
			nIter = std::stoi(argv[++na]);
			if (na+1==argc) break;
			++na;
		}
		// else if ( 0 == strcmp(argv[na],"--jtMethod") )
		// {
		// 	jtMethod = std::stoi(argv[++na]); // 0 for single, 1 for multiple
		// 	if (na+1==argc) break;
		// 	++na;
		// }
		else if ( 0 == strcmp(argv[na],"--nOutcomes") )
		{
			s = std::stoi(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--nPredictors") )
		{
			p = std::stoi(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--nChains") )
		{
			nChains = std::stoi(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--inFile") )
		{
			inFile = ""+std::string(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--outFilePath") )
		{
			outFilePath = std::string(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else if ( 0 == strcmp(argv[na],"--omegaInitPath") )
		{
			omegaInitPath = ""+std::string(argv[++na]); // use the next
			if (na+1==argc) break; // in case it's last, break
			++na; // otherwise augment counter
		}
		else
    {
	    std::cout << "Unknown option: " << argv[na] << std::endl;
	    return(1); //this is exit if I'm in a function elsewhere
    }
    }//end reading from command line

	std::cout << "Init RNG engine .. " << std::endl;

	// ############# Init the RNG generator/engine
	std::random_device r;
	unsigned int nThreads = omp_get_max_threads();

	rng.reserve(nThreads);  // reserve the correct space for the vector of rng engines
	std::seed_seq seedSeq;	// and declare the seedSequence
	std::vector<unsigned int> seedInit(8);
	long long int seed = std::chrono::system_clock::now().time_since_epoch().count();

	// seed all the engines
	for(unsigned int i=0; i<nThreads; ++i)
	{
		rng[i] = std::mt19937_64(seed + i*(1000*(p*s*3+s*s)*nIter) );
	}

	// ############

	// ### Read the data
	unsigned int n;
	arma::mat Y, X;
	std::cout << "Trying to read data ...  " << std::flush;

	if( Utils::readData(inFile, s, p, n, Y, X) ){
		std::cout << "Reading successfull!" << std::endl;
	}else{
		std::cout << "OUCH! EXITING --- " << std::endl;
		return 1;
	}

	// The intercept columnto X will be inserted when initialising the chain

	std::cout << "Clearing and initialising output files " << std::endl;
	// Re-define inFile so that I can use it in the output
	std::size_t slash = inFile.find("/");  // remove the path from inFile
	while( slash != std::string::npos )
	{
		inFile.erase(inFile.begin(),inFile.begin()+slash+1);
		slash = inFile.find("/");
	}
	inFile.erase(inFile.end()-4,inFile.end());  // remomve the .txt from inFile !

	// Update the "outFilePath" (inFile variable) with the method's name
	inFile += "_"+method+"_";

	int status;

	// TODO, I hate this, but I can't initialise/instanciate templated classes
	// at runtime so this seems fair (given that the 2 drive functions have their differences in output and stuff...)
	// still if there's a more elegant solution I'd like to find it

	if( method == "SSUR" )
		status = drive_SSUR(Y,X,nChains,nIter,inFile,outFilePath,gammaSampler,gPrior);
	else if( method == "dSUR" )
		status = drive_dSUR(Y,X,nChains,nIter,inFile,outFilePath,gammaSampler,gPrior);
	else if( method == "HESS" )
		status = drive_HESS(Y,X,nChains,nIter,inFile,outFilePath,gammaSampler,gPrior);
	else
		status = drive_SSUR(Y,X,nChains,nIter,inFile,outFilePath,gammaSampler,gPrior); // this makes a default, but
			// you shound't reach here if method is wrongly specified

	return status;
}