#define DEBUG 1
/**
 * File    : C.cpp
 * Author  : Kazune Takahashi
 * Created : 2020/7/3 5:48:49
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/integer/common_factor_rt.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/rational.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::integer::gcd; // for C++14 or for cpp_int
using boost::integer::lcm; // for C++14 or for cpp_int
using boost::multiprecision::cpp_int;
using ll = long long;
using ld = long double;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
constexpr ll MOD{1'000'000'007LL};
// constexpr ll MOD{998'244'353LL}; // be careful
constexpr ll MAX_SIZE{3'000'010LL};
// constexpr ll MAX_SIZE{30'000'010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
bool ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
    return true;
  }
  return false;
}
template <typename T>
bool ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
    return true;
  }
  return false;
}
// ----- Mint -----
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(Mint const &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(Mint const &a) { return *this += -a; }
  Mint &operator++() { return *this += 1; }
  Mint operator++(int)
  {
    Mint tmp{*this};
    ++*this;
    return tmp;
  }
  Mint &operator--() { return *this -= 1; }
  Mint operator--(int)
  {
    Mint tmp{*this};
    --*this;
    return tmp;
  }
  Mint &operator*=(Mint const &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(Mint const &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(Mint const &a) const { return Mint(*this) += a; }
  Mint operator-(Mint const &a) const { return Mint(*this) -= a; }
  Mint operator*(Mint const &a) const { return Mint(*this) *= a; }
  Mint operator/(Mint const &a) const { return Mint(*this) /= a; }
  bool operator<(Mint const &a) const { return x < a.x; }
  bool operator<=(Mint const &a) const { return x <= a.x; }
  bool operator>(Mint const &a) const { return x > a.x; }
  bool operator>=(Mint const &a) const { return x >= a.x; }
  bool operator==(Mint const &a) const { return x == a.x; }
  bool operator!=(Mint const &a) const { return !(*this == a); }
  Mint power(ll N) const
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, Mint<MOD> const &rhs) { return rhs + lhs; }
template <ll MOD>
Mint<MOD> operator-(ll lhs, Mint<MOD> const &rhs) { return -rhs + lhs; }
template <ll MOD>
Mint<MOD> operator*(ll lhs, Mint<MOD> const &rhs) { return rhs * lhs; }
template <ll MOD>
Mint<MOD> operator/(ll lhs, Mint<MOD> const &rhs) { return Mint<MOD>{lhs} / rhs; }
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a) { return stream >> a.x; }
template <ll MOD>
ostream &operator<<(ostream &stream, Mint<MOD> const &a) { return stream << a.x; }
// ----- Combination -----
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i{2LL}; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i{1LL}; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// ----- for C++14 -----
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
// ----- for C++17 -----
template <typename T, typename enable_if<is_integral<T>::value>::type * = nullptr>
size_t popcount(T x) { return bitset<64>(x).count(); }
size_t popcount(string const &S) { return bitset<200010>{S}.count(); }
// ----- Infty -----
template <typename T>
constexpr T Infty() { return numeric_limits<T>::max(); }
template <typename T>
constexpr T mInfty() { return numeric_limits<T>::min(); }
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1'000'000'000'000'010LL}; // or
// constexpr int infty{1'000'000'010};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}

// -----Geometry Library-- ---
// Referring to the great source codes:
//   - Maehara-san's algorithm library: http://www.prefield.com/algorithm/index.html
// Many thanks.

// ----- Basic Classes -----

constexpr ld EPSILON{1e-12};

// ----- Point -----

using Point = complex<ld>;
bool operator<(Point const &p, Point const &q)
{
  return real(p) != real(q) ? real(p) < real(q) : imag(p) < imag(q);
}
istream &operator>>(istream &is, Point &p)
{
  ld x, y;
  is >> x >> y;
  p = Point{x, y};
  return is;
}

ld OuterProduct(Point const &p, Point const &q)
{
  return imag(conj(p) * q);
}
ld InnerProduct(Point const &p, Point const &q)
{
  return real(conj(p) * q);
}

Point Normalize(Point const &p)
{
  return p / abs(p);
}

// ---- ccw -----

int ccw(Point a, Point b, Point c)
{
  b -= a;
  c -= a;
  auto tmp{OuterProduct(b, c)};
  if (tmp > 0)
  {
    return +1; // counter clockwise
  }
  if (tmp < 0)
  {
    return -1; // clockwise
  }
  if (InnerProduct(b, c) < 0)
  {
    return +2; // c--a--b on line
  }
  if (norm(b) < norm(c))
  {
    return -2; // a--b--c on line
  }
  return 0;
}

// ----- Geom -----

using Geom = vector<Point>;

Geom &operator+=(Geom &g, Point const &p)
{
  for (auto &q : g)
  {
    q += p;
  }
  return g;
}
Geom operator+(Geom const &g, Point const &p)
{
  Geom h{g};
  return h += p;
}
Geom &operator-=(Geom &g, Point const &p)
{
  return g += (-p);
}
Geom operator-(Geom const &g, Point const &p)
{
  return g + (-p);
}

// ----- Line -----

struct Segment;

struct Line : public Geom
{
  Line() {}
  Line(Point const &p, Point const &q)
  {
    push_back(p);
    push_back(q);
  }
};

// ----- Segment -----

struct Segment : public Line
{
  Segment() {}
  Segment(Point const &p, Point const &q)
  {
    push_back(p);
    push_back(q);
  }
};

// ----- Circle -----

struct Circle
{
  Point p;
  ld r;

  Circle() {}
  Circle(Point const &p, ld r) : p(p), r(r) {}
};

// ----- Functions -----

// ----- Rotate -----

Point Rotate(Point const &p, ld radian = M_PI / 2)
{
  return p * Point{cos(radian), sin(radian)};
}
Geom Rotate(Geom g, ld radian = M_PI / 2)
{
  for (auto &p : g)
  {
    p = Rotate(p, radian);
  }
  return g;
}

// ----- Projection and Reflection (Point and Line) -----

Point Projection(Line const &l, Point const &p)
{
  ld t{InnerProduct(p - l[0], l[0] - l[1]) / norm(l[0] - l[1])};
  return l[0] + t * (l[0] - l[1]);
}

Point Reflection(Line const &l, Point const &p)
{
  return p + ld{2} * (Projection(l, p) - p);
}

// ----- Intersect -----

bool Intersect(Line const &l, Line const &m)
{
  return abs(OuterProduct(l[1] - l[0], m[1] - m[0])) > EPSILON || // non-parallel
         abs(OuterProduct(l[1] - l[0], m[0] - l[0])) < EPSILON;   // same line
}
bool Intersect(Line const &l, Segment const &s)
{
  return OuterProduct(l[1] - l[0], s[0] - l[0]) * OuterProduct(l[1] - l[0], s[1] - l[0]) < EPSILON;
}
bool Intersect(Segment const &s, Line const &l)
{
  return Intersect(l, s);
}
bool Intersect(Line const &l, Point const &p)
{
  return abs(OuterProduct(l[1] - p, l[0] - p)) < EPSILON;
}
bool Intersect(Point const &p, Line const &l)
{
  return Intersect(l, p);
}
bool Intersect(Segment const &s, Segment const &t)
{
  return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
         ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
bool Intersect(Segment const &s, Point const &p)
{
  return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < EPSILON; // triangle inequality
}
bool Intersect(Point const &p, Segment const &s)
{
  return Intersect(s, p);
}
bool Intersect(Circle const &a, Circle const &b)
{
  return a.r + b.r + EPSILON < abs(a.p - b.p);
}

// ----- Dist -----

ld Dist(Point const &p, Point const &q)
{
  return abs(p - q);
}
ld Dist(Line const &l, Point const &p)
{
  return abs(p - Projection(l, p));
}
ld Dist(Point const &p, Line const &l)
{
  return Dist(l, p);
}
ld Dist(Line const &l, Line const &m)
{
  return Intersect(l, m) ? 0 : Dist(l, m[0]);
}
ld Dist(Line const &l, Segment const &s)
{
  if (Intersect(l, s))
  {
    return 0;
  }
  return min(Dist(l, s[0]), Dist(l, s[1]));
}
ld Dist(Segment const &s, Line const &l)
{
  return Dist(l, s);
}
ld Dist(Segment const &s, Point const &p)
{
  auto r{Projection(static_cast<Line>(s), p)};
  if (Intersect(s, r))
  {
    return abs(r - p);
  }
  return min(abs(s[0] - p), abs(s[1] - p));
}
ld Dist(Point const &p, Segment const &s)
{
  return Dist(s, p);
}
ld Dist(Segment const &s, Segment const &t)
{
  if (Intersect(s, t))
  {
    return 0;
  }
  return min({Dist(s, t[0]), Dist(s, t[1]), Dist(t, s[0]), Dist(t, s[1])});
}

// ----- IntersectionPoints ------

vector<Point> IntersectionPoints(Circle const &a, Circle const &b)
{
  auto d{Dist(a.p, b.p)};
  auto l{(a.r * a.r - b.r * b.r + d * d) / (2 * d)};
  auto tmp{a.r * a.r - l * l};
  if (tmp <= 0)
  {
    return {};
  }
  auto h{sqrt(tmp)};
  vector<Point> res;
  auto v{Normalize(b.p - a.p)};
  auto w{Rotate(v)};
  res.push_back(a.p + v * l + w * h);
  res.push_back(a.p + v * l - w * h);
  return res;
}

vector<Point> IntersectionPoints(Line const &l, Line const &m)
{
  auto A{OuterProduct(l[1] - l[0], m[1] - m[0])};
  auto B{OuterProduct(l[1] - l[0], l[1] - m[0])};
  if (abs(A) < EPSILON && abs(B) < EPSILON)
  {
    return {m[0], m[1], l[0], l[1]}; // same line
  }
  if (abs(A) < EPSILON)
  {
    assert(false); // Precondition is not satisfied.
  }
  return {m[0] + B / A * (m[1] - m[0])};
}

// ----- Contains -----

enum class ContainState
{
  OUT,
  ON,
  IN
};

ContainState Contains(Geom const &g, Point const &p)
{
  bool in{false};
  for (auto i{size_t{0}}; i < g.size(); ++i)
  {
    auto a{g[i] - p};
    auto b{g[(i + 1) % g.size()] - p};
    if (imag(a) > imag(b))
    {
      swap(a, b);
    }
    if (imag(a) <= 0 && 0 < imag(b) && OuterProduct(a, b) < 0)
    {
      in = !in;
    }
    if (abs(OuterProduct(a, b)) < EPSILON && InnerProduct(a, b) < EPSILON)
    {
      return ContainState::ON;
    }
  }
  return in ? ContainState::IN : ContainState::OUT;
}

ContainState Contains(Circle const &c, Point const &p)
{
  auto d{Dist(c.p, p)};
  if (abs(d - c.r) < EPSILON)
  {
    return ContainState::ON;
  }
  if (d > c.r)
  {
    return ContainState::OUT;
  }
  return ContainState::IN;
}

bool ContainStateToBool(ContainState s)
{
  return s == ContainState::IN || s == ContainState::ON;
}

template <typename T, typename U>
bool DoesContain(T const &a, U const &b)
{
  return ContainStateToBool(Contains(a, b));
}

// ----- Solve -----

class Solve
{

public:
  Solve()
  {
  }

  void flush()
  {
  }

private:
};

// ----- main() -----

/*
int main()
{
  Solve solve;
  solve.flush();
}
*/

int main()
{
  ld A, B, H, M;
  cin >> A >> B >> H >> M;
  Point p{polar(A, 2 * M_PI * H / 12 + 2 * M_PI * M / (12 * 60))};
  Point q{polar(B, 2 * M_PI * M / 60)};
  cout << fixed << setprecision(12) << abs(p - q) << endl;
}


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2014.
// Modifications copyright (c) 2014 Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// 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_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP
#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP

#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/core/srs.hpp>

#include <boost/geometry/algorithms/detail/flattening.hpp>

#include <boost/geometry/strategies/distance.hpp>

#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/promote_floating_point.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace distance
{

/*!
\brief Point-point distance approximation taking flattening into account
\ingroup distance
\tparam Spheroid The reference spheroid model
\tparam CalculationType \tparam_calculation
\author After Andoyer, 19xx, republished 1950, republished by Meeus, 1999
\note Although not so well-known, the approximation is very good: in all cases the results
are about the same as Vincenty. In my (Barend's) testcases the results didn't differ more than 6 m
\see http://nacc.upc.es/tierra/node16.html
\see http://sci.tech-archive.net/Archive/sci.geo.satellite-nav/2004-12/2724.html
\see http://home.att.net/~srschmitt/great_circle_route.html (implementation)
\see http://www.codeguru.com/Cpp/Cpp/algorithms/article.php/c5115 (implementation)
\see http://futureboy.homeip.net/frinksamp/navigation.frink (implementation)
\see http://www.voidware.com/earthdist.htm (implementation)
*/
template
<
    typename Spheroid,
    typename CalculationType = void
>
class andoyer
{
public :
    template <typename Point1, typename Point2>
    struct calculation_type
        : promote_floating_point
          <
              typename select_calculation_type
                  <
                      Point1,
                      Point2,
                      CalculationType
                  >::type
          >
    {};

    typedef Spheroid model_type;

    inline andoyer()
        : m_spheroid()
    {}

    explicit inline andoyer(Spheroid const& spheroid)
        : m_spheroid(spheroid)
    {}

    template <typename Point1, typename Point2>
    inline typename calculation_type<Point1, Point2>::type
    apply(Point1 const& point1, Point2 const& point2) const
    {
        return calc<typename calculation_type<Point1, Point2>::type>
            (
                get_as_radian<0>(point1), get_as_radian<1>(point1),
                get_as_radian<0>(point2), get_as_radian<1>(point2)
            );
    }

    inline Spheroid const& model() const
    {
        return m_spheroid;
    }

private :
    template <typename CT, typename T>
    inline CT calc(T const& lon1,
                T const& lat1,
                T const& lon2,
                T const& lat2) const
    {
        CT const G = (lat1 - lat2) / 2.0;
        CT const lambda = (lon1 - lon2) / 2.0;

        if (geometry::math::equals(lambda, 0.0)
            && geometry::math::equals(G, 0.0))
        {
            return 0.0;
        }

        CT const F = (lat1 + lat2) / 2.0;

        CT const sinG2 = math::sqr(sin(G));
        CT const cosG2 = math::sqr(cos(G));
        CT const sinF2 = math::sqr(sin(F));
        CT const cosF2 = math::sqr(cos(F));
        CT const sinL2 = math::sqr(sin(lambda));
        CT const cosL2 = math::sqr(cos(lambda));

        CT const S = sinG2 * cosL2 + cosF2 * sinL2;
        CT const C = cosG2 * cosL2 + sinF2 * sinL2;

        CT const c0 = 0;
        CT const c1 = 1;
        CT const c2 = 2;
        CT const c3 = 3;

        if (geometry::math::equals(S, c0) || geometry::math::equals(C, c0))
        {
            return c0;
        }

        CT const radius_a = CT(get_radius<0>(m_spheroid));
        CT const flattening = geometry::detail::flattening<CT>(m_spheroid);

        CT const omega = atan(math::sqrt(S / C));
        CT const r3 = c3 * math::sqrt(S * C) / omega; // not sure if this is r or greek nu
        CT const D = c2 * omega * radius_a;
        CT const H1 = (r3 - c1) / (c2 * C);
        CT const H2 = (r3 + c1) / (c2 * S);

        return D * (c1 + flattening * (H1 * sinF2 * cosG2 - H2 * cosF2 * sinG2) );
    }

    Spheroid m_spheroid;
};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{

template <typename Spheroid, typename CalculationType>
struct tag<andoyer<Spheroid, CalculationType> >
{
    typedef strategy_tag_distance_point_point type;
};

template <typename Spheroid, typename CalculationType, typename P1, typename P2>
struct return_type<andoyer<Spheroid, CalculationType>, P1, P2>
    : andoyer<Spheroid, CalculationType>::template calculation_type<P1, P2>
{};

template <typename Spheroid, typename CalculationType>
struct comparable_type<andoyer<Spheroid, CalculationType> >
{
    typedef andoyer<Spheroid, CalculationType> type;
};

template <typename Spheroid, typename CalculationType>
struct get_comparable<andoyer<Spheroid, CalculationType> >
{
    static inline andoyer<Spheroid, CalculationType> apply(andoyer<Spheroid, CalculationType> const& input)
    {
        return input;
    }
};

template <typename Spheroid, typename CalculationType, typename P1, typename P2>
struct result_from_distance<andoyer<Spheroid, CalculationType>, P1, P2>
{
    template <typename T>
    static inline typename return_type<andoyer<Spheroid, CalculationType>, P1, P2>::type
        apply(andoyer<Spheroid, CalculationType> const& , T const& value)
    {
        return value;
    }
};

template <typename Point1, typename Point2>
struct default_strategy<point_tag, point_tag, Point1, Point2, geographic_tag, geographic_tag>
{
    typedef strategy::distance::andoyer
                <
                    srs::spheroid
                        <
                            typename select_coordinate_type<Point1, Point2>::type
                        >
                > type;
};

} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

}} // namespace strategy::distance

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP


/* -*-C++-*- */
/*
   (c) Copyright 1996-2005, Hewlett-Packard Development Company, LP

   See the file named COPYING for license details
*/

/** @file
    \brief mathematical functions not found in the standard math library.
*/

#ifndef LINTEL_MATHSPECIALFUNCTIONS_HPP
#define LINTEL_MATHSPECIALFUNCTIONS_HPP

#include <math.h>

#include <boost/version.hpp>
#include <boost/config.hpp>
/* TODO: when we decide to stop supporting older boost versions, this
   should be removed. Also, note the equivalent checks later in the file
   and in the .cpp
*/
#if BOOST_VERSION >= 103500
#include <boost/math/special_functions/erf.hpp>
#else
   // no erf() on windows
#  if defined(BOOST_MSVC)
#     error need boost version >= 1.35
#  endif
#endif

//////////////////////////////////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////////////////////////////////

// The inverse of the Erf function, defined over the domain -1 < y < 1.
// TODO: should probably be deprecated in favor of boost::math::erfc() ?
double inverseErf(double y);

// The cumulative distribution function of the unit Normal RV U;
// unitNormalCDF(x) = Prob[U < x] = (1+erf(x/sqrt(2)))/2
inline double unitNormalCDF(double x)
{
#if BOOST_VERSION >= 103500
    return 0.5*(1.0+ boost::math::erf<double>(0.70710678118654752440084436210485*x));
#else
    return 0.5*(1.0+ erf(0.70710678118654752440084436210485*x));
#endif
}

// The probability that the absolute value of a measurement of a
// unit normal-distributed quantity is above X.  This is the same
// as the two-side folded cumulative distribution function of the
// unit normal RV U:
inline double probAbsNormal(double x)
{
#if BOOST_VERSION >= 103500
    return 1. - boost::math::erf<double>(0.70710678118654752440084436210485*fabs(x));
#else
    return 1. - erf(0.70710678118654752440084436210485*fabs(x));
#endif
}

// The probability density function of the unit Normal RV U;
// unitNormalPDF(x) = unitNormalCDF'(x) = exp(-x*x/2)/sqrt(2 Pi)
inline double unitNormalPDF(double x)
  {return 0.39894228040143267793994605993438 * exp(-0.5*x*x) ;};

// The inverse of the cumulative distribution function of the unit Normal
// RV U, also known as inversePhi.
// inverseUnitNormalCDF(x) == y  <=>  x == unitNormalCDF(y)
// inverseUnitNormalCDF(x) = sqrt(2) * inverseErf(2*x -1)
inline double inverseUnitNormalCDF(double x)
  {return 1.4142135623730950488016887242097*(inverseErf(2.0*x - 1.0));};

//////////////////////////////////////////////////////////////////////////////
// Data type conversion utilities
//////////////////////////////////////////////////////////////////////////////

// Verify that the double argument can be converted (without rounding error)
// to an integer data type.  You get to specify the size of the integer
// data type; only the size of a long or a long long is supported.
// "Without rounding error" means that the double is really close to
// being an actual integer.
extern bool isDoubleIntegral(double d, size_t target_size);

// Convert the double to an integer, of type long or long long
extern long convertDoubleLong(double d);
extern long long convertDoubleLongLong(double d);

#endif


/*
 * 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


//  (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());
}


//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;
}


//////////////////////////////////////////////////////////////////////////////
// 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 <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;
}


#include <bits/stdc++.h>
#include <boost/optional.hpp>

using namespace std;

boost::optional<long long> dijkstra(
    const vector<vector<tuple<long long, long long>>> &graph,
    long long from_i,
    long long to_i) {
  const long long INF = 1e18;

  vector<long long> dist(graph.size(), INF);
  dist[from_i] = 0;

  priority_queue<tuple<long long, long long>, vector<tuple<long long, long long>>, greater<>> que;
  que.push(make_tuple(0, from_i));
  while (!que.empty()) {
    long long d, v;
    tie(d, v) = que.top();
    que.pop();
    if (d > dist[v]) continue;
    if (v == to_i) return d;

    for (const auto &edge : graph[v]) {
      long long w, u;
      tie(u, w) = edge;
      if (d + w < dist[u]) {
        dist[u] = d + w;
        que.push(make_tuple(d + w, u));
      }
    }
  }

  if (dist[to_i] < INF) {
    return dist[to_i];
  } else {
    return boost::none;
  }
}

vector<long long> topological_sort(const vector<vector<long long>> &graph) {
  long long size = graph.size();

  // 入次数
  vector<long long> ins(size, 0);

  for (auto &&vs : graph) {
    for (auto &&v : vs) {
      ins[v]++;
    }
  }

  // 入次数がゼロのやつを集める
  vector<long long> zeros;
  for (long long v = 0; v < size; ++v) {
    if (ins[v] == 0) {
      zeros.push_back(v);
    }
  }

  // ゼロのやつから追加してく
  vector<long long> ret;
  while (!zeros.empty()) {
    long long v = zeros.back();
    zeros.pop_back();
    ret.push_back(v);
    for (auto &&u:  graph[v]) {
      ins[u]--;
      if (ins[u] == 0) {
        zeros.push_back(u);
      }
    }
  }
  // 閉路があると入次数が絶対ゼロにならない
  if (ret.size() != size) {
    throw invalid_argument("閉路があります");
  }

  return ret;
}


#include "CEGO/CEGO.hpp"
#include "CEGO/minimizers.hpp"
#include "CEGO/utilities.hpp"
#include <Eigen/Dense>

// autodiff include
#include <autodiff/forward.hpp>
#include <autodiff/forward/eigen.hpp>

#if defined(PYBIND11)
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <pybind11/eigen.h>
namespace py = pybind11;
#endif

std::atomic_size_t Ncalls(0);

template <typename T> using EArray = Eigen::Array<T, Eigen::Dynamic, 1>;

class Bumps {
public:
    std::size_t Nbumps;
    Eigen::ArrayXd c0, xp, yp, zp;
    double gamma = 10;
    const std::vector<CEGO::Bound> m_bounds;

    Bumps(std::size_t Nbumps, std::size_t Npoints, const std::vector<CEGO::Bound> &bounds) : Nbumps(Nbumps), m_bounds(bounds)
    {
        // Initialize the random number generator
        std::random_device rd;  // Will be used to obtain a seed for the random number engine
        std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()

        // Calculate the initial set of coefficients for the bump characteristics
        c0.resize(Nbumps * 6);
        for (auto i = 0; i < bounds.size(); ++i) {
            double dbl; int integer;
            bounds[i].gen_uniform(gen, dbl, integer);
            if (bounds[i].m_lower.type == bounds[i].m_lower.DOUBLE){
                c0[i] = dbl;
            }
            else {
                c0[i] = static_cast<double>(integer);
            }
        }

        // Generate some random points in the domain [0.1,1.0] for both variables
        xp = (1-0.1)*(Eigen::ArrayXd::Random(Npoints)+1)/2 + 0.1;
        yp = (1-0.1)*(Eigen::ArrayXd::Random(Npoints)+1)/2 + 0.1;
        zp = f_givenxy(c0, xp, yp);

        double checkval = objective(to_scaled(c0));
        std::cout << "c0: " << c0 << std::endl;

        assert(std::abs(checkval) < 1e-16);
        if (std::abs(checkval) > 1e-16) {
            throw std::invalid_argument("Did not start out with zero objective function!");
        }
    }
    /**
     * @brief Calculate the functional value for a set of vectors of points
     * @brief xb The x coordinate of the center of the bump
     * @brief x The x coordinate of the points to be evaluated
     * @brief y The y coordinate of the points to be evaluated
     */
    template <typename T>
    EArray<T> f_givenxy(const EArray<T> &c, const EArray<T> &x, const EArray<T> &y)
    {
        Ncalls++;
        EArray<T> s = EArray<T>::Zero(x.size());
        auto chunksize = 6;
        assert(c.size()%chunksize==0);
        for (long i = 0; i < c.size(); i += chunksize) {
            s += x.pow(c[i+0])*y.pow(c[i+1])*(c[i+2]*(x-c[i+3]).square() +c[i+4]*(y-c[i+5]).square()).exp();
        }
        return s.eval();
    }
    double objective(const CEGO::AbstractIndividual *pind) {
        const EArray<CEGO::numberish> &c = static_cast<const CEGO::NumericalIndividual<CEGO::numberish>*>(pind)->get_coefficients();
        Eigen::ArrayXd cc(c.size());
        for (auto i = 0; i < cc.size(); ++i) {
            cc[i] = c[i];
        }
        return objective(cc);
    }
    double objective(const EArray<double>& cscaled) {
        return (f_givenxy<double>(to_realworld<double>(cscaled), xp, yp) - zp).square().sum();
    }
    std::complex<double> objective(const EArray<std::complex<double>>& cscaled) {
        return (f_givenxy<std::complex<double>>(to_realworld<std::complex<double>>(cscaled), xp, yp) - zp).square().sum();
    }

    autodiff::dual objective(const EArray<autodiff::dual>& cscaled) {
        EArray<autodiff::dual> creal = to_realworld(cscaled);
        EArray<autodiff::dual> zmodel = f_givenxy<autodiff::dual>(creal, xp, yp);
        EArray<autodiff::dual> err = zmodel - zp.cast<autodiff::dual>();
        return err.square().sum();
    }

    // Inspired by scipy, keep all variables scaled in 0,1
    template <typename T>
    EArray<T> to_realworld(const EArray<T>&x){
        EArray<T> o(x.size());
        for (auto i = 0; i < o.size(); ++i){
            if constexpr (std::is_same<T, std::complex<double>>::value) {
                // If complex<double> type, first cast the numberish bounds to double, then to complex
                // Otherwise compiler gets confused
                T lower = static_cast<T>(static_cast<double>(m_bounds[i].m_lower));
                T upper = static_cast<T>(static_cast<double>(m_bounds[i].m_upper));
                o[i] = lower * (static_cast<T>(1.0) - x[i]) + upper * x[i];
            }
            else {
                T lower = static_cast<T>(m_bounds[i].m_lower);
                T upper = static_cast<T>(m_bounds[i].m_upper);
                o[i] = lower * (static_cast<T>(1.0) - x[i]) + upper * x[i];
            }
        }
        return o.eval();
    }
    Eigen::ArrayXd to_scaled(const Eigen::ArrayXd &x) {
        Eigen::ArrayXd o(x.size());
        for (auto i = 0; i < o.size(); ++i) {
            double lower = static_cast<double>(m_bounds[i].m_lower);
            double upper = static_cast<double>(m_bounds[i].m_upper);
            o[i] = (x[i]-lower)/(upper-lower);
        }
        return o;
    }

    void plot_surface() {
        #if defined(PYBIND11)
        using namespace pybind11::literals;
        py::module plt = py::module::import("matplotlib.pyplot"); // Import matplotlib
        std::size_t Nx = 100, Ny = 100;
        Eigen::MatrixXd X = Eigen::RowVectorXd::LinSpaced(Nx, 0.1, 1).replicate(Ny, 1);
        Eigen::MatrixXd Y = Eigen::VectorXd::LinSpaced(Ny, 0.1, 1).replicate(Nx, 1);
        X.resize(Nx*Ny,1); Y.resize(Nx*Ny,1);
        Eigen::MatrixXd Z = f_givenxy(c0, X.array(), Y.array()).matrix();
        X.resize(Nx, Ny); Y.resize(Nx, Ny); Z.resize(Nx, Ny);

        Eigen::ArrayXd levels = Eigen::ArrayXd::LinSpaced(300, Z.minCoeff(), Z.maxCoeff());
        try{
            plt.attr("contourf")(X, Y, Z, levels);
        }
        catch (std::exception &e) {
            std::cout << e.what()  << std::endl;
        }
        plt.attr("colorbar")();
        plt.attr("scatter")(xp, yp);
        plt.attr("show")();
        #else
        std::cout << "No support for pybind11, so no plots\n";
        #endif
    }
    void plot_trace(const std::vector<double> &best_costs) {
        #if defined(PYBIND11)
        py::module plt = py::module::import("matplotlib.pyplot"); // Import matplotlib
        plt.attr("plot")(best_costs);
        plt.attr("show")();
        #else
        std::cout << "No support for pybind11, so no plots\n";
        #endif
    }
};

struct BumpsInputs{
    std::string root = "";
    std::size_t parallel_threads = 1;
    std::size_t Nbumps = 4;
    std::vector<std::size_t> Nlayersvec = { 1 };
    std::size_t i = 0;
    std::size_t gradmin_mod = 5;
    std::size_t Nmax_gradient = 5;
};

inline void to_json(nlohmann::json& j, const BumpsInputs& f) {
    j = nlohmann::json{ { "root", f.root },{ "parallel_threads", f.parallel_threads },{ "Nbumps", f.Nbumps },{"i",f.i},{"gradmin_mod",f.gradmin_mod},{"Nmax_gradient",f.Nmax_gradient} };
}

inline void from_json(const nlohmann::json& j, BumpsInputs& f) {
    f.root = j.at("root").get<std::string>();
    f.parallel_threads = j.at("parallel_threads").get<int>();
    f.Nbumps = j.at("Nbumps").get<int>();
    f.i = j.at("i").get < std::size_t > ();
    f.gradmin_mod = j.at("gradmin_mod").get < std::size_t >();
    f.Nmax_gradient = j.at("Nmax_gradient").get < std::size_t >();
}

bool do_one(BumpsInputs &inputs)
{
    std::srand((unsigned int)time(0));

    // Construct the bounds
    std::size_t Npoints = inputs.Nbumps*6*10;
    std::vector<CEGO::Bound> bounds;
    for (auto i = 0; i < inputs.Nbumps; ++i) {
        bounds.push_back(CEGO::Bound(std::pair<double, double>(0.1, 1))); // ex
        bounds.push_back(CEGO::Bound(std::pair<double, double>(1, 3))); // ey
        bounds.push_back(CEGO::Bound(std::pair<double, double>(-50, -10))); // gx
        bounds.push_back(CEGO::Bound(std::pair<double, double>(0.1, 1))); // xb
        bounds.push_back(CEGO::Bound(std::pair<double, double>(-50, -10))); // gy
        bounds.push_back(CEGO::Bound(std::pair<double, double>(0.1, 1))); // yb
    }

    // Normalized bounds in [0,1]
    std::vector<CEGO::Bound> nbounds;
    for (auto i = 0; i < inputs.Nbumps; ++i) {
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // ex
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // ey
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // gx
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // xb
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // gy
        nbounds.push_back(CEGO::Bound(std::pair<double, double>(0, 1))); // yb
    }

    Bumps bumps(inputs.Nbumps, Npoints, bounds);
    //bumps.plot_surface();

    for (auto Nlayers : inputs.Nlayersvec){
        Ncalls = 0;
        CEGO::CostFunction<CEGO::numberish> cost_wrapper = std::bind((double (Bumps::*)(const CEGO::AbstractIndividual *)) &Bumps::objective, bumps, std::placeholders::_1);
        auto Npop_size = 15*bounds.size();
        auto layers = CEGO::Layers<CEGO::numberish>(cost_wrapper, bounds.size(), Npop_size, Nlayers, 5);
        layers.parallel = (inputs.parallel_threads > 1);
        layers.parallel_threads = inputs.parallel_threads;
        layers.set_builtin_evolver(CEGO::BuiltinEvolvers::differential_evolution);
        layers.set_bounds(nbounds);
        auto f = [&bumps](const CEGO::EArray<double>& c)->double { return bumps.objective(c); };
        auto f2 = [&bumps](const CEGO::EArray<std::complex<double>>& c)->std::complex<double> { return bumps.objective(c); };
        layers.add_gradient(f, f2);

        auto flags = layers.get_evolver_flags();
        flags["Nelite"] = 1;
        flags["Fmin"] = 0.1;
        flags["Fmax"] = 1.0;
        flags["CR"] = 1;
        layers.set_evolver_flags(flags);

        std::vector<double> best_costs;
        std::vector<std::vector<double> > objs;
        double VTR = 1e-16, best_cost = 999999.0;
        auto startTime = std::chrono::system_clock::now();
        for (auto counter = 0; counter < 50000; ++counter) {
            layers.do_generation();

            if (counter % inputs.gradmin_mod == 0 && counter > 0) {
                layers.gradient_minimizer();
            }

            // Store the best objective function in each layer
            std::vector<double> oo;
            for (auto &&cost_coefficients : layers.get_best_per_layer()) {
                oo.push_back(std::get<0>(cost_coefficients));
            }
            objs.push_back(oo);
            auto stats = layers.cost_stats_each_layer();

            // For the overall best result, print it, and write JSON to file
            auto [best_cost, best_coeffs] = layers.get_best();
            if (counter % 50 == 0) {
                std::cout << counter << ": best: " << best_cost << std::endl;
                //std::cout << bumps.to_realworld(best_coeffs//)-bumps.c0 << "\n ";// << CEGO::vec2string(bumps.c0) << "\n";
            }
            if (best_cost < VTR){ return true; }
        }
        auto endTime = std::chrono::system_clock::now();
        double elap = std::chrono::duration<double>(endTime - startTime).count();
        std::cout << "run:" << elap << " s" << std::endl;

        //bumps.plot_trace(best_costs);
        std::string fname = inputs.root + "Nbumps"+std::to_string(inputs.Nbumps)+"-Nlayers"+std::to_string(Nlayers) + "-run" + std::to_string(inputs.i) + ".txt";
        FILE* fp = fopen(fname.c_str(), "w");
        for (auto j = 0; j < best_costs.size(); ++j){
            fprintf(fp, "%12.8e", best_costs[j]);
            if (j < best_costs.size() - 1) {
                fprintf(fp, ", ");
            }
        }
        fclose(fp);
        /*std::cout << bumps.xb0 << std::endl;
        std::cout << bumps.yb0 << std::endl;*/
        std::cout << "NFE:" << Ncalls << std::endl;
    }
    return 0;
}

int main() {
    #if defined(PYBIND11)
    py::scoped_interpreter interp{};
    #endif
    BumpsInputs in;
    in.root = "shaped-";
    in.Nlayersvec = {3};
    using CEGO::get_env_int;
    auto Nrepeats = get_env_int("NREPEATS", 10);
    in.Nbumps = get_env_int("NBUMPS", 1);
    in.gradmin_mod = get_env_int("GRADMOD", 100);
    in.parallel_threads = get_env_int("NTHREADS", 6);
    in.Nmax_gradient = get_env_int("NMAX_gradient", 5);
    nlohmann::json j = in;
    std::cout << j << std::endl;
    int good_counter = 0;
    for (in.i = 0; in.i < Nrepeats; ++in.i) {
        good_counter += do_one(in);
    }
    std::cout << "Success: " << good_counter << "/" << Nrepeats << std::endl;
}


//
//  Rational.hpp
//
//
//  https://gist.github.com/sklaw/10473569
//

#ifndef RATIONAL_NUM
#define RATIONAL_NUM

#include <iostream>
#include <Eigen/Dense>
#include "../basic.h"

class RationalNum {
    friend RationalNum operator+(const RationalNum& left, const RationalNum& right);
    friend RationalNum operator-(const RationalNum& left, const RationalNum& right);
    friend RationalNum operator*(const RationalNum& left, const RationalNum& right);
    friend RationalNum operator/(const RationalNum& left, const RationalNum& right);
    friend bool operator==(const RationalNum& left, const RationalNum& right);
    friend bool operator!=(const RationalNum& left, const RationalNum& right);
    friend bool operator<(const RationalNum& left, const RationalNum& right);
    friend bool operator>(const RationalNum& left, const RationalNum& right);
    friend bool operator<=(const RationalNum& left, const RationalNum& right);
    friend bool operator>=(const RationalNum& left, const RationalNum& right);
    friend std::ostream& operator<<(std::ostream& out, const RationalNum& obj);
    friend std::istream& operator>>(std::istream& in, RationalNum& obj);

public:
    RationalNum(): numerator(0), denominator(1) {}
    RationalNum(rational_t x): numerator(x.p), denominator(x.q) {}
    RationalNum(int_t numerator_, int_t denominator_ = 1): numerator(numerator_), denominator(denominator_) {}

    RationalNum& operator=(const RationalNum& obj);
    RationalNum& operator+=(const RationalNum& obj);
    RationalNum& operator-=(const RationalNum& obj);
    RationalNum& operator*=(const RationalNum& obj);
    RationalNum& operator/=(const RationalNum& obj);
    RationalNum& operator++();
    RationalNum operator++(int);
    RationalNum& operator--();
    RationalNum operator--(int);
    RationalNum operator+() const;
    RationalNum operator-() const;

    explicit operator int_t() const;

    int_t getNumerator() const { return numerator; }
    int_t getDenominator() const { return denominator; }

private:
    int_t numerator;
    int_t denominator;
    RationalNum& simplify();
};

namespace Eigen {
template<> struct NumTraits<RationalNum>
 : NumTraits<int_t> // permits to get the epsilon, dummy_precision, lowest, highest functions
{
  typedef RationalNum Real;
  typedef RationalNum NonInteger;
  typedef RationalNum Nested;
  enum {
    IsComplex = 0,
    IsInteger = 0,
    IsSigned = 1,
    RequireInitialization = 1,
    ReadCost = 1,
    AddCost = 3,
    MulCost = 3
  };
    static inline Real epsilon() { return 0; }
    static inline Real dummy_precision() { return 0; }
//    static inline int digits10() { return 0; }

};
}

inline const RationalNum& conj(const RationalNum& x)  { return x; }
inline const RationalNum& real(const RationalNum& x)  { return x; }
inline RationalNum imag(const RationalNum&)    { return RationalNum(); }
inline RationalNum abs(const RationalNum&  x)  { return RationalNum(absInt(x.getNumerator()), absInt(x.getDenominator())); }
inline RationalNum abs2(const RationalNum& x)  { return x*x; }

inline rational_t to_rational_t(const RationalNum& x) { return {x.getNumerator(), x.getDenominator()}; }

#endif


#ifndef STAN_MATH_PRIM_FUN_CHOOSE_HPP
#define STAN_MATH_PRIM_FUN_CHOOSE_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/functor/apply_scalar_binary.hpp>
#include <boost/math/special_functions/binomial.hpp>
#include <cmath>
#include <limits>

namespace stan {
namespace math {

/**
 * Return the binomial coefficient for the specified integer
 * arguments.
 *
 * The binomial coefficient, \f${n \choose k}\f$, read "n choose k", is
 * defined for \f$0 \leq k \leq n\f$ (otherwise return 0) by
 *
 * \f${n \choose k} = \frac{n!}{k! (n-k)!}\f$.
 *
 * @param n total number of objects
 * @param k number of objects chosen
 * @return n choose k or 0 iff k > n
 * @throw std::domain_error if either argument is negative or the
 * result will not fit in an int type
 */
inline int choose(int n, int k) {
  check_nonnegative("choose", "n", n);
  check_nonnegative("choose", "k", k);
  if (k > n) {
    return 0;
  }
  const double choices = boost::math::binomial_coefficient<double>(n, k);
  check_less_or_equal("choose", "n choose k", choices,
                      std::numeric_limits<int>::max());
  return static_cast<int>(std::round(choices));
}

/**
 * Enables the vectorised application of the binomial coefficient function,
 * when the first and/or second arguments are containers.
 *
 * @tparam T1 type of first input
 * @tparam T2 type of second input
 * @param a First input
 * @param b Second input
 * @return Binomial coefficient function applied to the two inputs.
 */
template <typename T1, typename T2, require_any_container_t<T1, T2>* = nullptr>
inline auto choose(const T1& a, const T2& b) {
  return apply_scalar_binary(
      a, b, [&](const auto& c, const auto& d) { return choose(c, d); });
}

}  // namespace math
}  // namespace stan
#endif


#include "QMathUtil.h"
#include <cmath>
//#include <Eigen/Dense>
#include <iostream>
namespace GCL {

QVector3D QMathUtil::getRayPlaneIntersect(const QVector3D &rayPos, const QVector3D &rayDir,
                              const QVector3D &planePoint, const QVector3D &planeNormal, bool *has_intersection)
{
    float t0 = QVector3D::dotProduct(rayDir,planeNormal);
    float t1 = QVector3D::dotProduct(rayPos - planePoint , planeNormal);
    if(fabs(t0) < 1e-7)
    {
        if(has_intersection)
        {
            *has_intersection = false;
        }
        return QVector3D(0,0,0);
    }

    float k = -t1 / t0;
    QVector3D intersect = rayPos + k * rayDir;
    if(has_intersection)
    {
        *has_intersection = true;
    }
    return intersect;
}
float clamp(float x, float l, float r)
{
    if(x < l) return l;
    if(x > r) return r;
    return x;
}
QVector3D QMathUtil::fromMatrixToEuler(const QMatrix4x4 &m, const QString &order)
{
    QMatrix3x3 tm = m.normalMatrix();

   float m11 = tm(0,0), m12 = tm(1,0), m13 = tm(2,0);
   float m21 = tm(0,1), m22 = tm(1,1), m23 = tm(2,1);
   float m31 = tm(0,2), m32 = tm(1,2), m33 = tm(2,2);
    float _x,_y,_z;
    if(order == "XYZ")
    {
        _y = asin(clamp(m13,-1,1));
        if(fabs(m13) < 0.99999)
        {
            _x = atan2(-m23,m33);
            _z = atan2(-m12,m11);
        }else
        {
            _x = atan2(m32,m22);
            _z = 0;
        }
    }else if ( order == "YXZ" ) {

        _x = asin( - clamp( m23, - 1, 1 ) );

        if ( fabs( m23 ) < 0.99999 ) {

            _y = atan2( m13, m33 );
            _z = atan2( m21, m22 );

        } else {

            _y = atan2( - m31, m11 );
            _z = 0;

        }

    } else if ( order == "ZXY" ) {

        _x = asin( clamp( m32, - 1, 1 ) );

        if ( fabs( m32 ) < 0.99999 ) {

            _y = atan2( - m31, m33 );
            _z = atan2( - m12, m22 );

        } else {

            _y = 0;
            _z = atan2( m21, m11 );

        }

    } else if ( order == "ZYX" ) {

        _y = asin( - clamp( m31, - 1, 1 ) );

        if ( fabs( m31 ) < 0.99999 ) {

            _x = atan2( m32, m33 );
            _z = atan2( m21, m11 );

        } else {

            _x = 0;
            _z = atan2( - m12, m22 );

        }

    } else if ( order == "YZX" ) {

        _z = asin( clamp( m21, - 1, 1 ) );

        if ( fabs( m21 ) < 0.99999 ) {

            _x = atan2( - m23, m22 );
            _y = atan2( - m31, m11 );

        } else {

            _x = 0;
            _y = atan2( m13, m33 );

        }

    } else if ( order == "XZY" ) {

        _z = asin( - clamp( m12, - 1, 1 ) );

        if ( fabs( m12 ) < 0.99999 ) {

            _x = atan2( m32, m22 );
            _y = atan2( m13, m11 );

        } else {

            _x = atan2( - m23, m33 );
            _y = 0;

        }

    } else {

         qDebug()<<( "THREE.Euler: .setFromRotationMatrix() given unsupported order: " + order );
    }

    QVector3D v(_x,_y,_z);
    v = v / 3.1415926535898 * 180;
    return -v;

}

QVector3D QMathUtil::mulEuler(const QVector3D &x, const QVector3D &y)
{
    QMatrix4x4 mat;
    mat.rotate(x[0],QVector3D(1,0,0));
    mat.rotate(x[1],QVector3D(0,1,0));
    mat.rotate(x[2],QVector3D(0,0,1));

    mat.rotate(y[0],QVector3D(1,0,0));
    mat.rotate(y[1],QVector3D(0,1,0));
    mat.rotate(y[2],QVector3D(0,0,1));

    return QMathUtil::fromMatrixToEuler(mat);

}

float QMathUtil::getDistanceSumToPlane(const QList<QVector3D> &vlist, const QVector3D &point, const QVector3D &normal)
{
    float sum = 0;
    for(const auto & v : vlist)
    {
        sum += fabs(QVector3D::dotProduct(v-point,normal));
    }
    return sum;
}

float QMathUtil::getDistanceSquareSumToPlane(const QList<QVector3D> &vlist, const QVector3D &point, const QVector3D &normal)
{
    float sum = 0;
    for(const auto & v : vlist)
    {
        float val = fabs(QVector3D::dotProduct(v-point,normal));
        sum += val * val;
    }
    return sum;
}

void QMathUtil::computePCA(const QList<QVector3D> &vlist, QVector3D &axis_0, QVector3D &axis_1, QVector3D &axis_2)
{
//    QVector3D center;
//    for(const auto &v : vlist)
//    {
//        center += v;
//    }
//    center/= vlist.size();
//    Eigen::Matrix3d mat;
//    for(const auto &v : vlist)
//    {
//        Eigen::Vector3d ev;
//        QVector3D tv = v - center;
//        for(int j=0; j < 3; j++)
//        {
//            ev(j) = tv[j];
//        }
//        Eigen::Matrix3d tm = ev * ev.transpose();
//        mat += tm;
//    }
//    mat /= vlist.size();
//    Eigen::EigenSolver<Eigen::Matrix3d> solver;
//    solver.compute(mat);
//    auto eigen_v = solver.eigenvectors();
//    for(int j=0; j < 3; j++)
//    {
//        axis_0[j] = eigen_v.coeff(j,0).real();
//        axis_1[j] = eigen_v.coeff(j,1).real();
//        axis_2[j] = eigen_v.coeff(j,2).real();
//    }

}

QVector3D QMathUtil::getRayTriangleIntesect(const QVector3D &rayPos, const QVector3D &rayDir, const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, bool *has_intersection)
{
    Vec3 vray(rayPos.x(),rayPos.y(),rayPos.z());
    Vec3 vdir(rayDir.x(),rayDir.y(),rayDir.z());
    Vec3 vv0(v0.x(),v0.y(),v0.z());
    Vec3 vv1(v1.x(),v1.y(),v1.z());
    Vec3 vv2(v2.x(),v2.y(),v2.z());
    Vec3 ans;
    bool t = Vec3::getIntersectionRayToTriangle(vray,vdir,vv0,vv1,vv2,ans);

    if(has_intersection)
    {
        *has_intersection = t;
    }
    return QVector3D(ans[0],ans[1],ans[2]);
}

QVector3D QMathUtil::fromVectorTransformToEuler(const QVector3D &v0, const QVector3D &v1,const QString &order)
{
    Vec3 vv0(v0.x(),v0.y(),v0.z());
    Vec3 vv1(v1.x(),v1.y(),v1.z());

    Quat quat =  Quat::quatFromVectorTransform(vv0,vv1);
    HomoMatrix4 hmat =  quat.convertToMatrix();

    QMatrix4x4 qmat(hmat.data(),4,4);

    return fromMatrixToEuler(qmat,order);

}

}