/**
 * \file
 * \copyright
 * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

#include "BHE_1P.h"

#include <boost/math/constants/constants.hpp>

#include "FlowAndTemperatureControl.h"
#include "Physics.h"
#include "ThermoMechanicalFlowProperties.h"

namespace ProcessLib
{
namespace HeatTransportBHE
{
namespace BHE
{
BHE_1P::BHE_1P(BoreholeGeometry const& borehole,
               RefrigerantProperties const& refrigerant,
               GroutParameters const& grout,
               FlowAndTemperatureControl const& flowAndTemperatureControl,
               PipeConfiguration1PType const& pipes,
               bool const use_python_bcs)
    : BHECommon{borehole, refrigerant, grout, flowAndTemperatureControl,
                use_python_bcs},
      _pipe(pipes)
{
    _thermal_resistances.fill(std::numeric_limits<double>::quiet_NaN());

    // Initialize thermal resistances.
    auto values = visit(
        [&](auto const& control) {
            return control(refrigerant.reference_temperature,
                           0. /* initial time */);
        },
        flowAndTemperatureControl);
    updateHeatTransferCoefficients(values.flow_rate);
}

std::array<double, BHE_1P::number_of_unknowns> BHE_1P::pipeHeatCapacities()
    const
{
    double const rho_r = refrigerant.density;
    double const specific_heat_capacity = refrigerant.specific_heat_capacity;
    double const rho_g = grout.rho_g;
    double const porosity_g = grout.porosity_g;
    double const heat_cap_g = grout.heat_cap_g;

    return {{
        /*pipe*/ rho_r * specific_heat_capacity,
        /*grout*/ (1.0 - porosity_g) * rho_g * heat_cap_g,
    }};
}

std::array<double, BHE_1P::number_of_unknowns> BHE_1P::pipeHeatConductions()
    const
{
    double const lambda_r = refrigerant.thermal_conductivity;
    double const rho_r = refrigerant.density;
    double const Cp_r = refrigerant.specific_heat_capacity;
    double const alpha_L = _pipe.longitudinal_dispersion_length;
    double const porosity_g = grout.porosity_g;
    double const lambda_g = grout.lambda_g;

    // Here we calculate the laplace coefficients in the governing
    // equations of the BHE.
    return {{
        // pipe, Eq. 19
        (lambda_r + rho_r * Cp_r * alpha_L * _flow_velocity),
        // grout, Eq. 21
        (1.0 - porosity_g) * lambda_g,
    }};
}

std::array<Eigen::Vector3d, BHE_1P::number_of_unknowns>
BHE_1P::pipeAdvectionVectors(Eigen::Vector3d const& elem_direction) const
{
    double const& rho_r = refrigerant.density;
    double const& Cp_r = refrigerant.specific_heat_capacity;
    Eigen::Vector3d adv_vector = rho_r * Cp_r * _flow_velocity * elem_direction;

    return {// pipe, Eq. 19
            adv_vector,
            // grout, Eq. 21
            {0, 0, 0}};
}

double BHE_1P::compute_R_gs(double const chi, double const R_g)
{
    return (1 - chi) * R_g;
}

void BHE_1P::updateHeatTransferCoefficients(double const flow_rate)

{
    auto const tm_flow_properties = calculateThermoMechanicalFlowPropertiesPipe(
        _pipe.single_pipe, borehole_geometry.length, refrigerant, flow_rate);

    _flow_velocity = tm_flow_properties.velocity;
    _thermal_resistances =
        calcThermalResistances(tm_flow_properties.nusselt_number);
}

// Nu is the Nusselt number.
std::array<double, BHE_1P::number_of_unknowns> BHE_1P::calcThermalResistances(
    double const Nu)
{
    constexpr double pi = boost::math::constants::pi<double>();

    double const lambda_r = refrigerant.thermal_conductivity;
    double const lambda_g = grout.lambda_g;
    double const lambda_p = _pipe.single_pipe.wall_thermal_conductivity;

    // thermal resistances due to advective flow of refrigerant in the pipe
    double const R_adv_i1 = 1.0 / (Nu * lambda_r * pi);

    // thermal resistance due to thermal conductivity of the pipe wall material
    double const R_con_a = std::log(_pipe.single_pipe.outsideDiameter() /
                                    _pipe.single_pipe.diameter) /
                           (2.0 * pi * lambda_p);

    // thermal resistances of the grout
    double const D = borehole_geometry.diameter;
    double const pipe_outside_diameter = _pipe.single_pipe.outsideDiameter();

    double const chi = std::log(std::sqrt(D * D + pipe_outside_diameter *
                                                      pipe_outside_diameter) /
                                std::sqrt(2) / pipe_outside_diameter) /
                       std::log(D / pipe_outside_diameter);
    double const R_g =
        std::log(D / pipe_outside_diameter) / 2 / (pi * lambda_g);

    double const R_con_b = chi * R_g;

    // thermal resistances due to grout-soil exchange
    double const R_gs = compute_R_gs(chi, R_g);

    // Eq. 29 and 30
    double const R_fg = R_adv_i1 + R_con_a + R_con_b;

    return {{R_fg, R_gs}};
}

std::array<std::pair<std::size_t /*node_id*/, int /*component*/>, 2>
BHE_1P::getBHEInflowDirichletBCNodesAndComponents(
    std::size_t const top_node_id,
    std::size_t const bottom_node_id,
    int const in_component_id)
{
    return {std::make_pair(top_node_id, in_component_id),
            std::make_pair(bottom_node_id, in_component_id)};
}

std::optional<
    std::array<std::pair<std::size_t /*node_id*/, int /*component*/>, 2>>
BHE_1P::getBHEBottomDirichletBCNodesAndComponents(
    std::size_t const /*bottom_node_id*/,
    int const /*in_component_id*/,
    int const /*out_component_id*/)
{
    return {};
}

std::array<double, BHE_1P::number_of_unknowns> BHE_1P::crossSectionAreas() const
{
    return {{_pipe.single_pipe.area(),
             borehole_geometry.area() - _pipe.single_pipe.outsideArea()}};
}

double BHE_1P::updateFlowRateAndTemperature(double const T_out,
                                            double const current_time)
{
    auto values =
        visit([&](auto const& control) { return control(T_out, current_time); },
              flowAndTemperatureControl);
    updateHeatTransferCoefficients(values.flow_rate);
    return values.temperature;
}
}  // namespace BHE
}  // namespace HeatTransportBHE
}  // namespace ProcessLib


#include <algorithm>
#include <boost/optional.hpp>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define REP(i, n) for(int i = 0, i##_MACRO = (n); i < i##_MACRO; i++)
#define RANGE(i, a, b) for(int i = (a), i##_MACRO = (b); i < i##_MACRO; i++)
#define EACH(e, a) for(auto&& e : a)
#define ALL(a) std::begin(a), std::end(a)
#define RALL(a) std::rbegin(a), std::rend(a)
#define FILL(a, n) memset((a), n, sizeof(a))
#define FILLZ(a) FILL(a, 0)
#define INT(x) (static_cast<int>(x))
#define PRECISION(x) std::fixed << std::setprecision(x)

using namespace std;

using ll = long long;
using VI = std::vector<int>;
using VI2D = std::vector<vector<int>>;
using VLL = std::vector<long long>;
using VLL2D = std::vector<vector<long long>>;

constexpr int INF = 2e9;
constexpr double EPS = 1e-10;
constexpr double PI = acos(-1.0);

constexpr int dx[] = {-1, 0, 1, 0};
constexpr int dy[] = {0, -1, 0, 1};

template <typename T, std::size_t N>
struct make_vector_type {
	using type =
		typename std::vector<typename make_vector_type<T, (N - 1)>::type>;
};

template <typename T>
struct make_vector_type<T, 0> {
	using type = typename std::vector<T>;
};

template <typename T, size_t N>
auto make_vector_impl(const std::vector<std::size_t>& ls, T init_value) {
	if constexpr(N == 0) {
		return std::vector<T>(ls[N], init_value);
	} else {
		return typename make_vector_type<T, N>::type(
			ls[N], make_vector_impl<T, (N - 1)>(ls, init_value));
	}
}

template <typename T, std::size_t N>
auto make_vector(const std::size_t (&ls)[N], T init_value) {
	std::vector<std::size_t> dimensions(N);
	for(int i = 0; i < N; i++) {
		dimensions[N - i - 1] = ls[i];
	}
	return make_vector_impl<T, N - 1>(dimensions, init_value);
}

template <typename T>
std::vector<T> make_vector(std::size_t size, T init_value) {
	return std::vector<T>(size, init_value);
}

template <typename T>
constexpr int sign(T x) {
	return x < 0 ? -1 : x > 0 ? 1 : 0;
}

template <>
constexpr int sign(double x) {
	return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

template <typename T, typename U>
constexpr void chmax(T& m, U x) {
	m = max<T>(m, x);
}

template <typename T, typename U>
constexpr void chmin(T& m, U x) {
	m = min<T>(m, x);
}

template <typename T>
constexpr T square(T x) {
	return x * x;
}

template <long long MOD = 1000000007>
class ModInt {
	public:
	long long n;

	constexpr ModInt() : n(0) {}
	constexpr ModInt(long long n) : n(n < 0 ? n + MOD : n % MOD) {}

	constexpr long long get() const { return this->n; }
	constexpr long long get_mod() const { return MOD; }

	constexpr ModInt inv() const { return pow<ModInt<MOD>>(*this, MOD - 2); }

	constexpr ModInt& operator=(const long long rhs) {
		return *this = ModInt(rhs);
	}
	constexpr ModInt& operator+=(const ModInt rhs) {
		return *this = ModInt(this->n + rhs.n);
	}
	constexpr ModInt& operator-=(const ModInt rhs) {
		return *this = ModInt(this->n - rhs.n);
	}
	constexpr ModInt& operator*=(const ModInt rhs) {
		return *this = ModInt(this->n * rhs.n);
	}
	constexpr ModInt& operator/=(const ModInt rhs) {
		return *this *= rhs.inv();
	}
	constexpr bool operator==(const ModInt rhs) const {
		return this->n == rhs.n;
	}
};

template <long long MOD>
constexpr ModInt<MOD> operator+(const ModInt<MOD>& lhs,
								const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) += rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator+(const ModInt<MOD>& lhs, const long long& rhs) {
	return ModInt<MOD>(lhs) += rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator+(const long long& lhs, const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) += rhs;
}

template <long long MOD>
constexpr ModInt<MOD> operator-(const ModInt<MOD>& lhs,
								const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) -= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator-(const ModInt<MOD>& lhs, const long long& rhs) {
	return ModInt<MOD>(lhs) -= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator-(const long long& lhs, const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) -= rhs;
}

template <long long MOD>
constexpr ModInt<MOD> operator*(const ModInt<MOD>& lhs,
								const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) *= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator*(const ModInt<MOD>& lhs, const long long& rhs) {
	return ModInt<MOD>(lhs) *= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator*(const long long& lhs, const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) *= rhs;
}

template <long long MOD>
constexpr ModInt<MOD> operator/(const ModInt<MOD>& lhs,
								const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) /= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator/(const ModInt<MOD>& lhs, const long long& rhs) {
	return ModInt<MOD>(lhs) /= rhs;
}
template <long long MOD>
constexpr ModInt<MOD> operator/(const long long& lhs, const ModInt<MOD>& rhs) {
	return ModInt<MOD>(lhs) /= rhs;
}

template <long long MOD>
std::ostream& operator<<(std::ostream& os, const ModInt<MOD>& x) {
	return os << x.n;
}

template <long long MOD>
std::istream& operator>>(std::istream& is, const ModInt<MOD>& x) {
	return is >> x.n;
}

int roundup_pow2(int n) {
	if(!(n & (n - 1))) {
		return n;
	}

	int ret = 1;
	while(n > ret) {
		ret <<= 1;
	}
	return ret;
}

template <typename T, typename U>
class LazySegmentTree {
	using F = std::function<T(T, T)>;
	using G = std::function<T(T, U)>;
	using H = std::function<U(U, U)>;

	std::vector<T> tree;
	std::vector<U> lazy;
	F merge;
	G mapping;
	H composition;
	T id1;
	U id2;
	std::size_t size;

	public:
	LazySegmentTree(const vector<T>& a,
					const F f,
					const T id1,
					const G g,
					const H h,
					const U id2)
		: tree(roundup_pow2(a.size()) * 2 - 1, id1),
		  lazy(roundup_pow2(a.size()) * 2 - 1, id2), merge(f), id1(id1),
		  mapping(g), composition(h), id2(id2), size(roundup_pow2(a.size())) {
		int offset = this->size - 1;
		for(int i = 0; i < a.size(); i++) {
			this->tree[i + offset] = a[i];
		}
		for(int i = offset - 1; i >= 0; i--) {
			this->tree[i] =
				this->merge(this->tree[i * 2 + 1], this->tree[i * 2 + 2]);
		}
	}

	void debug_print() {
		cout << "tree: ";
		REP(i, this->size * 2 - 1) {
			if(i == this->size - 1) {
				cout << "| ";
			}
			cout << "(" << this->tree[i].first << "," << this->tree[i].second
				 << ") ";
		}
		cout << "\nlazy: ";
		REP(i, this->size * 2 - 1) {
			if(i == this->size - 1) {
				cout << "| ";
			}
			cout << this->lazy[i] << " ";
		}
		cout << endl;
	}

	void
	update(const std::size_t left, const std::size_t right, const U value) {
		this->update_impl(left, right, 0, 0, this->size, value);
	}

	T find(const std::size_t left, const std::size_t right) {
		return this->find_impl(left, right, 0, 0, this->size);
	}

	private:
	void update_impl(std::size_t query_left,
					 std::size_t query_right,
					 std::size_t node,
					 std::size_t node_left,
					 std::size_t node_right,
					 U value) {
		this->force(node);
		if(node_right <= query_left || query_right <= node_left) {
			return;
		}
		if(query_left <= node_left && node_right <= query_right) {
			this->lazy[node] = this->composition(this->lazy[node], value);
			this->force(node);
			return;
		}
		this->update_impl(query_left,
						  query_right,
						  node * 2 + 1,
						  node_left,
						  node_left + (node_right - node_left) / 2,
						  value);
		this->update_impl(query_left,
						  query_right,
						  node * 2 + 2,
						  node_left + (node_right - node_left) / 2,
						  node_right,
						  value);
		this->tree[node] =
			this->merge(this->tree[node * 2 + 1], this->tree[node * 2 + 2]);
	}

	T find_impl(size_t query_left,
				size_t query_right,
				size_t node,
				size_t node_left,
				size_t node_right) {
		this->force(node);
		if(node_right <= query_left || query_right <= node_left) {
			return this->id1;
		}
		if(query_left <= node_left && node_right <= query_right) {
			return this->tree[node];
		}

		return this->merge(find_impl(query_left,
									 query_right,
									 node * 2 + 1,
									 node_left,
									 node_left + (node_right - node_left) / 2),
						   find_impl(query_left,
									 query_right,
									 node * 2 + 2,
									 node_left + (node_right - node_left) / 2,
									 node_right));
	}

	void force(std::size_t node) {
		if(this->lazy[node] == this->id2) {
			return;
		}
		if(node * 2 + 1 < this->size * 2 - 1) {
			this->lazy[node * 2 + 1] =
				this->composition(lazy[node * 2 + 1], lazy[node]);
			this->lazy[node * 2 + 2] =
				this->composition(lazy[node * 2 + 2], lazy[node]);
		}
		this->tree[node] = this->mapping(this->tree[node], lazy[node]);
		this->lazy[node] = this->id2;
	}
};

using mint = ModInt<998244353>;

int main() {
	int n, q;
	cin >> n >> q;
	VI l(q), r(q);
	VI d(q);
	REP(i, q) {
		cin >> l[i] >> r[i] >> d[i];
		l[i] = n - l[i] + 1;
		r[i] = n - r[i];
		swap(l[i], r[i]);
	}

	vector<mint> keta(n + 1, 0);
	keta[0] = 1;
	REP(i, n) { keta[i + 1] = keta[i] * 10; }

	vector<mint> repunit(n + 1, 0);
	repunit[0] = 1;
	REP(i, n) { repunit[i + 1] = repunit[i] * 10 + 1; }

	vector<pair<mint, int>> s(n, {1, 1});

	LazySegmentTree<pair<mint, int>, int> lst(
		s,
		[&](pair<mint, int> a, pair<mint, int> b) {
			return make_pair(a.first + b.first * keta[a.second],
							 a.second + b.second);
		},
		{0, 1},
		[&](pair<mint, int> a, int b) {
			return make_pair(b ? b * repunit[a.second - 1] : a.first, a.second);
		},
		[](int a, int b) { return b ? b : a; },
		0);
	// lst.debug_print();
	REP(i, q) {
		lst.update(l[i], r[i], d[i]);
		cout << lst.find(0, n).first << endl;
		// lst.debug_print();
	}
	return 0;
}


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

#ifndef BOOST_MATH_SPECIAL_DAUBECHIES_WAVELET_HPP
#define BOOST_MATH_SPECIAL_DAUBECHIES_WAVELET_HPP
#include <vector>
#include <array>
#include <cmath>
#include <thread>
#include <future>
#include <iostream>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/detail/daubechies_scaling_integer_grid.hpp>
#include <boost/math/special_functions/daubechies_scaling.hpp>
#include <boost/math/filters/daubechies.hpp>
#include <boost/math/interpolators/detail/cubic_hermite_detail.hpp>
#include <boost/math/interpolators/detail/quintic_hermite_detail.hpp>
#include <boost/math/interpolators/detail/septic_hermite_detail.hpp>

namespace boost::math {

   template<class Real, int p, int order>
   std::vector<Real> daubechies_wavelet_dyadic_grid(int64_t j_max)
   {
      if (j_max == 0)
      {
         throw std::domain_error("The wavelet dyadic grid is refined from the scaling integer grid, so its minimum amount of data is half integer widths.");
      }
      auto phijk = daubechies_scaling_dyadic_grid<Real, p, order>(j_max - 1);
      //psi_j[l] = psi(-p+1 + l/2^j) = \sum_{k=0}^{2p-1} (-1)^k c_k \phi(1-2p+k + l/2^{j-1})
      //For derivatives just map c_k -> 2^order c_k.
      auto d = boost::math::filters::daubechies_scaling_filter<Real, p>();
      Real scale = boost::math::constants::root_two<Real>() * (1 << order);
      for (size_t i = 0; i < d.size(); ++i)
      {
         d[i] *= scale;
         if (!(i & 1))
         {
            d[i] = -d[i];
         }
      }

      std::vector<Real> v(2 * p + (2 * p - 1) * ((int64_t(1) << j_max) - 1), std::numeric_limits<Real>::quiet_NaN());
      v[0] = 0;
      v[v.size() - 1] = 0;

      for (int64_t l = 1; l < static_cast<int64_t>(v.size() - 1); ++l)
      {
         Real term = 0;
         for (int64_t k = 0; k < static_cast<int64_t>(d.size()); ++k)
         {
            int64_t idx = (int64_t(1) << (j_max - 1)) * (1 - 2 * p + k) + l;
            if (idx < 0 || idx >= static_cast<int64_t>(phijk.size()))
            {
               continue;
            }
            term += d[k] * phijk[idx];
         }
         v[l] = term;
      }

      return v;
   }

   template<class Real, int p>
   class daubechies_wavelet {
      //
      // Some type manipulation so we know the type of the interpolator, and the vector type it requires:
      //
      typedef std::vector < std::array < Real, p < 6 ? 2 : p < 10 ? 3 : 4>> vector_type;
      //
      // List our interpolators:
      //
      typedef std::tuple<
         detail::null_interpolator, detail::matched_holder_aos<vector_type>, detail::linear_interpolation_aos<vector_type>,
         interpolators::detail::cardinal_cubic_hermite_detail_aos<vector_type>, interpolators::detail::cardinal_quintic_hermite_detail_aos<vector_type>,
         interpolators::detail::cardinal_septic_hermite_detail_aos<vector_type> > interpolator_list;
      //
      // Select the one we need:
      //
      typedef std::tuple_element_t<
         p == 1 ? 0 :
         p == 2 ? 1 :
         p == 3 ? 2 :
         p <= 5 ? 3 :
         p <= 9 ? 4 : 5, interpolator_list> interpolator_type;
   public:
      daubechies_wavelet(int grid_refinements = -1)
      {
         static_assert(p < 20, "Daubechies wavelets are only implemented for p < 20.");
         static_assert(p > 0, "Daubechies wavelets must have at least 1 vanishing moment.");
         if (grid_refinements == 0)
         {
            throw std::domain_error("The wavelet requires at least 1 grid refinement.");
         }
         if constexpr (p == 1)
         {
            return;
         }
         else
         {
            if (grid_refinements < 0)
            {
               if (std::is_same_v<Real, float>)
               {
                  if (grid_refinements == -2)
                  {
                     // Control absolute error:
                     //                          p= 2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
                     std::array<int, 20> r{ -1, -1, 18, 19, 16, 11,  8,  7,  7,  7,  5,  5,  4,  4,  4,  4,  3,  3,  3,  3 };
                     grid_refinements = r[p];
                  }
                  else
                  {
                     // Control relative error:
                     //                          p= 2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
                     std::array<int, 20> r{ -1, -1, 21, 21, 21, 17, 16, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11 };
                     grid_refinements = r[p];
                  }
               }
               else if (std::is_same_v<Real, double>)
               {
                  //                          p= 2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
                  std::array<int, 20> r{ -1, -1, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 19, 18, 18, 18, 18, 18, 18, 18 };
                  grid_refinements = r[p];
               }
               else
               {
                  grid_refinements = 21;
               }
            }

            // Compute the refined grid:
            // In fact for float precision I know the grid must be computed in double precision and then cast back down, or else parts of the support are systematically inaccurate.
            std::future<std::vector<Real>> t0 = std::async(std::launch::async, [&grid_refinements]() {
               // Computing in higher precision and downcasting is essential for 1ULP evaluation in float precision:
               auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 0>(grid_refinements);
               return detail::daubechies_eval_type<Real>::vector_cast(v);
               });
            // Compute the derivative of the refined grid:
            std::future<std::vector<Real>> t1 = std::async(std::launch::async, [&grid_refinements]() {
               auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 1>(grid_refinements);
               return detail::daubechies_eval_type<Real>::vector_cast(v);
               });

            // if necessary, compute the second and third derivative:
            std::vector<Real> d2ydx2;
            std::vector<Real> d3ydx3;
            if constexpr (p >= 6) {
               std::future<std::vector<Real>> t3 = std::async(std::launch::async, [&grid_refinements]() {
                  auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 2>(grid_refinements);
                  return detail::daubechies_eval_type<Real>::vector_cast(v);
                  });

               if constexpr (p >= 10) {
                  std::future<std::vector<Real>> t4 = std::async(std::launch::async, [&grid_refinements]() {
                     auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 3>(grid_refinements);
                     return detail::daubechies_eval_type<Real>::vector_cast(v);
                     });
                  d3ydx3 = t4.get();
               }
               d2ydx2 = t3.get();
            }

            auto y = t0.get();
            auto dydx = t1.get();

            if constexpr (p >= 2)
            {
               vector_type data(y.size());
               for (size_t i = 0; i < y.size(); ++i)
               {
                  data[i][0] = y[i];
                  data[i][1] = dydx[i];
                  if constexpr (p >= 6)
                     data[i][2] = d2ydx2[i];
                  if constexpr (p >= 10)
                     data[i][3] = d3ydx3[i];
               }
               if constexpr (p <= 3)
                  m_interpolator = std::make_shared<interpolator_type>(std::move(data), grid_refinements, Real(-p + 1));
               else
                  m_interpolator = std::make_shared<interpolator_type>(std::move(data), Real(-p + 1), Real(1) / (1 << grid_refinements));
            }
            else
               m_interpolator = std::make_shared<detail::null_interpolator>();
         }
      }

      inline Real operator()(Real x) const
      {
         if (x <= -p + 1 || x >= p)
         {
            return 0;
         }
         if constexpr (p == 1)
         {
            if (x < Real(1) / Real(2))
            {
               return 1;
            }
            else if (x == Real(1) / Real(2))
            {
               return 0;
            }
            return -1;
         }
         return (*m_interpolator)(x);
      }

      inline Real prime(Real x) const
      {
         static_assert(p > 2, "The 3-vanishing moment Daubechies wavelet is the first which is continuously differentiable.");
         if (x <= -p + 1 || x >= p)
         {
            return 0;
         }
         return m_interpolator->prime(x);
      }

      inline Real double_prime(Real x) const
      {
         static_assert(p >= 6, "Second derivatives of Daubechies wavelets require at least 6 vanishing moments.");
         if (x <= -p + 1 || x >= p)
         {
            return Real(0);
         }
         return m_interpolator->double_prime(x);
      }

      std::pair<Real, Real> support() const
      {
         return { Real(-p + 1), Real(p) };
      }

      int64_t bytes() const
      {
         return m_interpolator->bytes() + sizeof(*this);
      }

   private:
      std::shared_ptr<interpolator_type> m_interpolator;
   };

}
#endif


// ====================================================================
// This file is part of FlexibleSUSY.
//
// FlexibleSUSY is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// FlexibleSUSY is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FlexibleSUSY.  If not, see
// <http://www.gnu.org/licenses/>.
// ====================================================================

// File generated at Sat 26 May 2018 14:35:26

/**
 * @file ScalarSingletDM_Z2_two_scale_ewsb_solver.hpp
 *
 * @brief contains class for solving EWSB when two-scale algorithm is used
 *
 * This file was generated at Sat 26 May 2018 14:35:26 with FlexibleSUSY
 * 2.0.1 (git commit: unknown) and SARAH 4.12.2 .
 */

#ifndef ScalarSingletDM_Z2_TWO_SCALE_EWSB_SOLVER_H
#define ScalarSingletDM_Z2_TWO_SCALE_EWSB_SOLVER_H

#include "ScalarSingletDM_Z2_ewsb_solver.hpp"
#include "ScalarSingletDM_Z2_ewsb_solver_interface.hpp"
#include "error.hpp"

#include <Eigen/Core>

namespace flexiblesusy {

class EWSB_solver;
class Two_scale;

class ScalarSingletDM_Z2_mass_eigenstates;

template<>
class ScalarSingletDM_Z2_ewsb_solver<Two_scale> : public ScalarSingletDM_Z2_ewsb_solver_interface {
public:
   ScalarSingletDM_Z2_ewsb_solver() = default;
   ScalarSingletDM_Z2_ewsb_solver(const ScalarSingletDM_Z2_ewsb_solver&) = default;
   ScalarSingletDM_Z2_ewsb_solver(ScalarSingletDM_Z2_ewsb_solver&&) = default;
   virtual ~ScalarSingletDM_Z2_ewsb_solver() {}
   ScalarSingletDM_Z2_ewsb_solver& operator=(const ScalarSingletDM_Z2_ewsb_solver&) = default;
   ScalarSingletDM_Z2_ewsb_solver& operator=(ScalarSingletDM_Z2_ewsb_solver&&) = default;

   virtual void set_loop_order(int l) override { loop_order = l; }
   virtual void set_number_of_iterations(int n) override { number_of_iterations = n; }
   virtual void set_precision(double p) override { precision = p; }

   virtual int get_loop_order() const override { return loop_order; }
   virtual int get_number_of_iterations() const override { return number_of_iterations; }
   virtual double get_precision() const override { return precision; }

   virtual int solve(ScalarSingletDM_Z2_mass_eigenstates&) override;
private:
   static const int number_of_ewsb_equations = 1;
   using EWSB_vector_t = Eigen::Matrix<double,number_of_ewsb_equations,1>;

   class EEWSBStepFailed : public Error {
   public:
      virtual ~EEWSBStepFailed() {}
      virtual std::string what() const { return "Could not perform EWSB step."; }
   };

   int number_of_iterations{100}; ///< maximum number of iterations
   int loop_order{2};             ///< loop order to solve EWSB at
   double precision{1.e-5};       ///< precision goal

   void set_ewsb_solution(ScalarSingletDM_Z2_mass_eigenstates&, const EWSB_solver*);
   template <typename It> void set_best_ewsb_solution(ScalarSingletDM_Z2_mass_eigenstates&, It, It);

   int solve_tree_level(ScalarSingletDM_Z2_mass_eigenstates&);
   int solve_iteratively(ScalarSingletDM_Z2_mass_eigenstates&);
   int solve_iteratively_at(ScalarSingletDM_Z2_mass_eigenstates&, int);
   int solve_iteratively_with(ScalarSingletDM_Z2_mass_eigenstates&, EWSB_solver*, const EWSB_vector_t&);

   EWSB_vector_t initial_guess(const ScalarSingletDM_Z2_mass_eigenstates&) const;
   EWSB_vector_t tadpole_equations(const ScalarSingletDM_Z2_mass_eigenstates&) const;
   EWSB_vector_t ewsb_step(const ScalarSingletDM_Z2_mass_eigenstates&) const;
};

} // namespace flexiblesusy

#endif


// Copyright (c) 2012 Andre Martins
// All Rights Reserved.
//
// This file is part of AD3 2.0.
//
// AD3 2.0 is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// AD3 2.0 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with AD3 2.0.  If not, see <http://www.gnu.org/licenses/>.

#ifndef _AD3_QP_HPP_
#define _AD3_QP_HPP_

#include <Eigen/Eigenvalues>
#include <math.h>
#include <limits>
#include "dd_grlab.hpp"

#define NEARLY_ZERO_TOL(a,tol) (((a)<=(tol)) && ((a)>=(-(tol))))
#define NEARLY_EQ_TOL(a,b,tol) (((a)-(b))*((a)-(b))<=(tol))
#define num_max_iterations_QP_ 10

struct admm_vertex_program_general:public admm_vertex_program {

void Maximize(vertex_type& vertex, vec additional_log_potentials, vec variable_log_potentials,
                Configuration &configuration,
                double *value) {

    vector <Configuration> states(vertex.data().nvars,-1);
    int best = -1;
    *value = -1e12;
    for (int index = 0;
         index < additional_log_potentials.size();
         ++index) {
      //cout<<"enter loop 1 in  Maximize .."<<endl;
      double score = additional_log_potentials[index];
      //cout<<"score in Maximize .."<<endl;
      get_configuration_states(vertex,index, &states);
      //cout<<"get config state Maximize"<<endl;
      int offset = 0;
      for (int i = 0; i < vertex.data().nvars; ++i) {
      //cout<<"enter loop 2 in  Maximize .."<<endl;
        score += variable_log_potentials[offset+states[i]];
        offset = vertex.data().cards[i];

      }

      //cout<<"exit loop 2 in  Maximize .."<<endl;
      if (configuration < 0 || score > *value) {
        configuration = index;
        *value = score;
      }
    }
    //cout<<"exit loop 1 in  Maximize .."<<endl;
    assert(configuration >= 0);

  }

//Configuration CreateConfiguration()
//{ Configuration *configuration = new Configuration;
//  return *Configuration;
 //}

 void DeleteConfiguration(Configuration &configuration) {
    configuration = -1;
  }

 bool InvertAfterInsertion(vertex_type& vertex, vector <double> & inverse_A_,
        const vector<Configuration> &active_set, const Configuration &inserted_element) {

  vector<double> inverse_A = inverse_A_;
  int size_A = active_set.size() + 1;
  vector<double> r(size_A);

  r[0] = 1.0;
  for (int i = 0; i < active_set.size(); ++i) {
    // Count how many variable values the new assignment
    // have in common with the i-th assignment.
    int num_common_values = CountCommonValues(vertex, active_set[i], inserted_element);
    r[i+1] = static_cast<double>(num_common_values);
  }

  double r0 = static_cast<double>(CountCommonValues(vertex,
      inserted_element, inserted_element));
  double s = r0;
  for (int i = 0; i < size_A; ++i) {
    if (r[i] == 0.0) continue;
    s -= r[i] * r[i] * inverse_A[i * size_A + i];
    for (int j = i+1; j < size_A; ++j) {
      if (r[j] == 0.0) continue;
      s -= 2 * r[i] * r[j] * inverse_A[i * size_A + j];
    }
  }

    if (NEARLY_ZERO_TOL(s, 1e-9)) {
         if (opts.verbose> 2) {
      cout << "Warning: updated matrix will become singular after insertion."
           << endl;
    }
    return false;
  }

  double invs = 1.0 / s;
  vector<double> d(size_A, 0.0);
  for (int i = 0; i < size_A; ++i) {
    if (r[i] == 0.0) continue;
    for (int j = 0; j < size_A; ++j) {
      d[j] += inverse_A[i * size_A + j] * r[i];
    }
  }

  int size_A_after = size_A + 1;
  inverse_A_.resize(size_A_after * size_A_after);
  for (int i = 0; i < size_A; ++i) {
    for (int j = 0; j < size_A; ++j) {
      inverse_A_[i * size_A_after + j] = inverse_A[i * size_A + j] +
          invs * d[i] * d[j];
    }
    inverse_A_[i * size_A_after + size_A] = -invs * d[i];
    inverse_A_[size_A * size_A_after + i] = -invs * d[i];
  }
  inverse_A_[size_A * size_A_after + size_A] = invs;

  return true;
}

void InvertAfterRemoval(vector <double> &inverse_A_,const vector<Configuration> &active_set,
                                       int removed_index) {
  vector<double> inverse_A = inverse_A_;
  int size_A = active_set.size() + 1;
  vector<double> r(size_A);

  ++removed_index; // Index in A has an offset of 1.
  double invs = inverse_A[removed_index * size_A + removed_index];
  assert(!NEARLY_ZERO_TOL(invs, 1e-12));
  double s = 1.0 / invs;
  vector<double> d(size_A - 1, 0.0);
  int k = 0;
  for (int i = 0; i < size_A; ++i) {
    if (i == removed_index) continue;
    d[k] = -s * inverse_A[removed_index * size_A + i];
    ++k;
  }

  int size_A_after = size_A - 1;
  inverse_A_.resize(size_A_after * size_A_after);
  k = 0;
  for (int i = 0; i < size_A; ++i) {
    if (i == removed_index) continue;
    int l = 0;
    for (int j = 0; j < size_A; ++j) {
      if (j == removed_index) continue;
      inverse_A_[k * size_A_after + l] = inverse_A[i * size_A + j] -
          invs * d[k] * d[l];
      ++l;
    }
    ++k;
  }
}

// Compute Mnz'*Mnz
void ComputeActiveSetSimilarities(vertex_type& vertex,
    const vector<Configuration> &active_set,
    vector<double> *similarities) {
  int size = active_set.size();

  // Compute similarity matrix.
  similarities->resize(size * size);
  (*similarities)[0] = 0.0;
  for (int i = 0; i < active_set.size(); ++i) {
    (*similarities)[i*size + i] = static_cast<double>(
        CountCommonValues(vertex,active_set[i], active_set[i]) );
    for (int j = i+1; j < active_set.size(); ++j) {
      // Count how many variable values the i-th and j-th
      // assignments have in common.
      int num_common_values = CountCommonValues(vertex,active_set[i], active_set[j]);
      (*similarities)[i*size + j] = num_common_values;
      (*similarities)[j*size + i] = num_common_values;
    }
  }
}

void ComputeMarginalsFromSparseDistribution( vertex_type& vertex,
    const vector<Configuration> &active_set,
    const vector<double> &distribution,
    vec  &variable_posteriors,
    vec &additional_posteriors) {
    //cout<<"enter cmfsp ..."<<endl;
    variable_posteriors.setZero();
    additional_posteriors.setZero();
    for (int i = 0; i < active_set.size(); ++i) {
    UpdateMarginalsFromConfiguration(vertex,active_set[i],
                                       distribution[i],
                                       variable_posteriors,
                                       additional_posteriors);
    }
  }

   // Given a configuration with a probability (weight),
  // increment the vectors of variable and additional posteriors.
  void UpdateMarginalsFromConfiguration(vertex_type& vertex,
    const Configuration &configuration,
    double weight,
    vec &variable_posteriors,
    vec &additional_posteriors) {

     vector <Configuration> states(vertex.data().nvars, -1);
     get_configuration_states(vertex, configuration, &states);

            int offset = 0;

            for (int k = 0; k < vertex.data().nvars; ++k)
            {   //cout<<"loop enter update marginals.."<<offset<<" "<<states[k]<<" "<<variable_posteriors.size()<<endl;
                variable_posteriors[offset + states[k]] += weight;
                //cout<<"till here ...";
                offset += vertex.data().cards[k];
                //cout<<"loop exit update marginals.."<<endl;
            }
    additional_posteriors[configuration] += weight;

  }
  // Count how many common values two configurations have.
  int CountCommonValues(vertex_type& vertex,Configuration configuration1,
                        Configuration configuration2) {

    //assert(states1->size() == states2->size());
    int count = 0;
    vector <Configuration> states1(vertex.data().nvars, -1);
    vector <Configuration> states2(vertex.data().nvars, -1);
    get_configuration_states(vertex, configuration1, &states1);
    get_configuration_states(vertex, configuration2, &states2);
    for(int i = 0; i< vertex.data().nvars; i++)
    { //cout<<"enter loop  in get count common values..."<<endl;
    if (states1[i] == states2[i])
      { count++;} }
    return count;
  }

void Evaluate(vertex_type& vertex, vec additional_log_potentials, vec variable_log_potentials,
                const Configuration configuration,
                double *value) {

    vector<Configuration> states(vertex.data().nvars, -1);
    get_configuration_states(vertex, configuration, &states);
    *value = 0.0;
    int offset = 0;
    for (int i = 0;i<vertex.data().nvars; ++i) {
     //cout<<"enter loop in eval .."<<" "<<offset<<" "<<states[i]<<" "<<variable_log_potentials.size()<<endl;
      *value += variable_log_potentials[offset + states[i]];
      offset = vertex.data().cards[i];
    }
    *value += additional_log_potentials[configuration];
  }

  void EigenDecompose(vector<double> *similarities,
                            vector<double> *eigenvalues) {

  int size = sqrt(similarities->size());

  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es;
  Eigen::MatrixXd sim(size, size);
  int t = 0;
  for (int i = 0; i < size; ++i) {
    for (int j = 0; j < size; ++j) {
      sim(i, j) = (*similarities)[t];
      ++t;
    }
  }
  es.compute(sim);
  const Eigen::VectorXd &eigvals = es.eigenvalues();
  eigenvalues->resize(size);
  for (int i = 0; i < size; ++i) {
    (*eigenvalues)[i] = eigvals[i];
  }
  const Eigen::MatrixXd &eigvectors = es.eigenvectors().transpose();
  t = 0;
  for (int i = 0; i < size; ++i) {
    for (int j = 0; j < size; ++j) {
      (*similarities)[t] = eigvectors(i, j);
      ++t;
    }
  }

}

void SolveQP(vertex_type& vertex,const gather_type& total,
                            vec& beliefs, vec& variable_posteriors,
                            vec& additional_posteriors) {
  vertex_data& vdata = vertex.data();
  beliefs = vdata.potentials;
  int num_configurations = vdata.potentials.size();
     for (int index_configuration = 0;
         index_configuration < num_configurations;
         ++index_configuration) {
         vector<int> states(vdata.nvars, -1);
         // This could be made more efficient by defining an iterator over factor
         // configurations.
        get_configuration_states(vertex, index_configuration, &states);
        int offset = 0;
        for (int k = 0; k < vdata.nvars; ++k) {
        //cout<<index_configuration<<" "<<offset<<" "<<states[k]<<endl;
        //cout<<beliefs.size()<<" "<<total.messages.size()<<endl;
        beliefs[index_configuration] += total.messages[offset + states[k]];
        offset += vdata.cards[k];

             }

          }
   //cout<<"loop 1 in solveQP .."<<endl;
   vec additional_log_potentials = beliefs;
   vec variable_log_potentials = total.neighbor_distribution;
   vector <Configuration> active_set_;
   vector<double> distribution_;
   vector<double> inverse_A_;
  // Initialize the active set.

  if (active_set_.size() == 0) {
    variable_posteriors.resize(variable_log_potentials.size());
    additional_posteriors.resize(additional_log_potentials.size());
    distribution_.clear();
    // Initialize by solving the LP, discarding the quadratic
    // term.
    Configuration configuration = -1;
    double value;
    //cout<<"Before Maximize .."<<endl;
    Maximize(vertex, additional_log_potentials, variable_log_potentials,
             configuration,
             &value);
    //cout<<"Maximize in solveQP .."<<endl;
    active_set_.push_back(configuration);
    distribution_.push_back(1.0);

    // Initialize inv(A) as [-M,1;1,0].
    inverse_A_.resize(4);
    inverse_A_[0] = static_cast<double>(
        -CountCommonValues(vertex,configuration, configuration));
    //cout<<"count common vals in solve QP ..."<<endl;
    inverse_A_[1] = 1;
    inverse_A_[2] = 1;
    inverse_A_[3] = 0;
  }

  bool changed_active_set = true;
  vector<double> z;
  int num_max_iterations = num_max_iterations_QP_;
  double tau = 0;
  for (int iter = 0; iter < num_max_iterations; ++iter) {
    bool same_as_before = true;
    bool unbounded = false;
    if (changed_active_set) {
      // Recompute vector b.
      vector<double> b(active_set_.size() + 1, 0.0);
      b[0] = 1.0;
      for (int i = 0; i < active_set_.size(); ++i) {
        const Configuration &configuration = active_set_[i];
        double score;
     //   cout<<"Before eval in solveQP ..."<<endl;
        Evaluate(vertex, additional_log_potentials, variable_log_potentials,
                 configuration,
                 &score);
        b[i+1] = score;
      }
   //cout<<"Eval in solveQP .."<<endl;
      // Solve the system Az = b.
      z.resize(active_set_.size());
      int size_A = active_set_.size() + 1;
      for (int i = 0; i < active_set_.size(); ++i) {
        z[i] = 0.0;
        for (int j = 0; j < size_A; ++j) {
          z[i] += inverse_A_[(i+1) * size_A + j] * b[j];
        }
      }
      tau = 0.0;
      for (int j = 0; j < size_A; ++j) {
        tau += inverse_A_[j] * b[j];
      }

      same_as_before = false;
    }

    if (same_as_before) {
      // Compute the variable marginals from the full distribution
      // stored in z.
      ComputeMarginalsFromSparseDistribution(vertex, active_set_,
                                             z,
                                             variable_posteriors,
                                             additional_posteriors);
    //cout<<"ComputeMarginals 1 in solveQP .."<<endl;
      // Get the most violated constraint
      // (by calling the black box that computes the MAP).
      vec scores = variable_log_potentials;
      for (int i = 0; i < scores.size(); ++i) {
        scores[i] -= variable_posteriors[i];
      }
      Configuration configuration = -1;
      double value = 0.0;

      Maximize(vertex,
                additional_log_potentials, scores,
               configuration,
               &value);
     //cout<<"Maximize 2 in SolveQP"<<endl;
      double very_small_threshold = 1e-9;
      if (value <= tau + very_small_threshold) { // value <= tau.
        // We have found the solution;
        // the distribution, active set, and inv(A) are cached for the next round.
        DeleteConfiguration(configuration);
        return;
      } else {
        for (int k = 0; k < active_set_.size(); ++k) {
          // This is expensive and should just be a sanity check.
          // However, in practice, numerical issues force an already existing
          // configuration to try to be added. Therefore, we always check
          // if a configuration already exists before inserting it.
          // If it does, that means the active set method converged to a
          // solution (but numerical issues had prevented us to see it.)
          if (active_set_[k] == configuration) {                         ////////
            if (opts.verbose > 2) {
              cout << "Warning: value - tau = "
                   << value - tau << " " << value << " " << tau
                   << endl;
            }
            // We have found the solution;
            // the distribution, active set, and inv(A)
            // are cached for the next round.
            DeleteConfiguration(configuration);

            // Just in case, clean the cache.
            // This may prevent eventual numerical problems in the future.
            for (int j = 0; j < active_set_.size(); ++j) {
              if (j == k) continue; // This configuration was deleted already.
              DeleteConfiguration(active_set_[j]);
            }
            active_set_.clear();
            inverse_A_.clear();
            distribution_.clear();

            // Return.
            return;
          }
        }
        z.push_back(0.0);
        distribution_ = z;

        // Update inv(A).
        bool singular = !InvertAfterInsertion(vertex, inverse_A_, active_set_, configuration);
        //cout<<"Invertafterinsertion in solveQP .."<<endl;
        if (singular) {
          // If adding a new configuration causes the matrix to be singular,
          // don't just add it. Instead, look for a configuration in the null
          // space and remove it before inserting the new one.
          // Right now, if more than one such configuration exists, we just
          // remove the first one we find. There's a chance this could cause
          // some cyclic behaviour. If that is the case, we should randomize
          // this choice.
          // Note: This step is expensive and requires an eigendecomposition.
          // TODO: I think there is a graph interpretation for this problem.
          // Maybe some specialized graph algorithm is cheaper than doing
          // the eigendecomposition.
          vector<double> similarities(active_set_.size() * active_set_.size());
          ComputeActiveSetSimilarities(vertex, active_set_, &similarities);

          //cout<<"compute active similarities in solveQP .."<<endl;
          vector<double> padded_similarities((active_set_.size()+2) *
                                             (active_set_.size()+2), 1.0);
          for (int i = 0; i < active_set_.size(); ++i) {
            for (int j = 0; j < active_set_.size(); ++j) {
              padded_similarities[(i+1)*(active_set_.size()+2) + (j+1)] =
                  similarities[i*active_set_.size() + j];
            }
          }
          padded_similarities[0] = 0.0;
          for (int i = 0; i < active_set_.size(); ++i) {
            double value = static_cast<double>(
                CountCommonValues(vertex, configuration, active_set_[i]));
            padded_similarities[(i+1)*(active_set_.size()+2) +
                                (active_set_.size()+1)] = value;
            padded_similarities[(active_set_.size()+1)*(active_set_.size()+2) +
                                (i+1)] = value;
          }
          double value = static_cast<double>(
              CountCommonValues(vertex, configuration, configuration));
          padded_similarities[(active_set_.size()+1)*(active_set_.size()+2) +
                              (active_set_.size()+1)] = value;

          vector<double> eigenvalues(active_set_.size()+2);
          EigenDecompose(&padded_similarities, &eigenvalues);
          int zero_eigenvalue = -1;
          for (int i = 0; i < active_set_.size()+2; ++i) {
            if (NEARLY_EQ_TOL(eigenvalues[i], 0.0, 1e-9)) {
              if (zero_eigenvalue >= 0) {
                // If this happens, something failed. Maybe a numerical problem
                // may cause this. In that case, just give up, clean the cache
                // and return. Hopefully the next iteration will fix it.
                cout << "Multiple zero eigenvalues: "
                     << eigenvalues[zero_eigenvalue] << " and "
                     << eigenvalues[i] << endl;
                cout << "Warning: Giving up." << endl;
                // Clean the cache.
                for (int j = 0; j < active_set_.size(); ++j) {
                  DeleteConfiguration(active_set_[j]);
                }
                active_set_.clear();
                inverse_A_.clear();
                distribution_.clear();
                return;
              }
              zero_eigenvalue = i;
            }
          }
          assert(zero_eigenvalue >= 0);
          vector<int> configurations_to_remove;
          for (int j = 1; j < active_set_.size()+1; ++j) {
            double value = padded_similarities[zero_eigenvalue*(active_set_.size()+2) + j];
            if (!NEARLY_EQ_TOL(value, 0.0, 1e-9)) {
              configurations_to_remove.push_back(j-1);
            }
          }
          if (opts.verbose > 2) {
            cout << "Pick a configuration to remove (" << configurations_to_remove.size()
                 << " out of " << active_set_.size() << ")." << endl;
          }

          assert(configurations_to_remove.size() >= 1);
          int j = configurations_to_remove[0];

          // Update inv(A).
          InvertAfterRemoval(inverse_A_, active_set_, j);

          // Remove blocking constraint from the active set.
          DeleteConfiguration(active_set_[j]); // Delete configutation.
          active_set_.erase(active_set_.begin() + j);

          singular = !InvertAfterInsertion(vertex, inverse_A_, active_set_, configuration);
          assert(!singular);
        }

        // Insert configuration to active set.
        if (opts.verbose > 2) {
          cout << "Inserted one element to the active set (iteration "
               << iter << ")." << endl;
        }
        active_set_.push_back(configuration);
        changed_active_set = true;
      }
    } else {
      // Solution has changed from the previous iteration.
      // Look for blocking constraints.
      int blocking = -1;
      bool exist_blocking = false;
      double alpha = 1.0;
      for (int i = 0; i < active_set_.size(); ++i) {
        assert(distribution_[i] >= -1e-12);
        if (z[i] >= distribution_[i]) continue;
        if (z[i] < 0) exist_blocking = true;
        double tmp = distribution_[i] / (distribution_[i] - z[i]);
        if (blocking < 0 || tmp < alpha) {
          alpha = tmp;
          blocking = i;
        }
      }

      if (!exist_blocking) {
        // No blocking constraints.
        assert(!unbounded);
        distribution_ = z;
        alpha = 1.0;
        changed_active_set = false;
      } else {
        if (alpha > 1.0 && !unbounded) alpha = 1.0;
        // Interpolate between factor_posteriors_[i] and z.
        if (alpha == 1.0) {
          distribution_ = z;
        } else {
          for (int i = 0; i < active_set_.size(); ++i) {
            z[i] = (1 - alpha) * distribution_[i] + alpha * z[i];
            distribution_[i] = z[i];
          }
        }

        // Update inv(A).
        InvertAfterRemoval(inverse_A_, active_set_, blocking);

        // Remove blocking constraint from the active set.
        if (opts.verbose > 2) {
          cout << "Removed one element to the active set (iteration "
               << iter << ")." << endl;
        }

        DeleteConfiguration(active_set_[blocking]); // Delete configutation.
        active_set_.erase(active_set_.begin() + blocking);

        z.erase(z.begin() + blocking);
        distribution_.erase(distribution_.begin() + blocking);
        changed_active_set = true;
        for (int i = 0; i < distribution_.size(); ++i) {
          assert(distribution_[i] > -1e-16);
        }
      }
    }
  }

  // Maximum number of iterations reached.
  // Return the best existing solution by computing the variable marginals
  // from the full distribution stored in z.
  //assert(false);
  ComputeMarginalsFromSparseDistribution(vertex, active_set_,
                                         z,
                                         variable_posteriors,
                                         additional_posteriors);
  };

};
#endif


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

// Copyright (c) 2011-2015 Barend Gehrels, Amsterdam, the Netherlands.

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

// Contributed and/or modified by Menelaos Karavelas, 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_ALGORITHMS_DETAIL_PARTITION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP

#include <cstddef>
#include <vector>
#include <boost/range.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/algorithms/assign.hpp>

namespace boost { namespace geometry
{

namespace detail { namespace partition
{

template <int Dimension, typename Box>
inline void divide_box(Box const& box, Box& lower_box, Box& upper_box)
{
    typedef typename coordinate_type<Box>::type ctype;

    // Divide input box into two parts, e.g. left/right
    ctype two = 2;
    ctype mid = (geometry::get<min_corner, Dimension>(box)
            + geometry::get<max_corner, Dimension>(box)) / two;

    lower_box = box;
    upper_box = box;
    geometry::set<max_corner, Dimension>(lower_box, mid);
    geometry::set<min_corner, Dimension>(upper_box, mid);
}

// Divide forward_range into three subsets: lower, upper and oversized
// (not-fitting)
// (lower == left or bottom, upper == right or top)
template <typename OverlapsPolicy, typename Box, typename IteratorVector>
inline void divide_into_subsets(Box const& lower_box,
        Box const& upper_box,
        IteratorVector const& input,
        IteratorVector& lower,
        IteratorVector& upper,
        IteratorVector& exceeding)
{
    typedef typename boost::range_iterator
        <
            IteratorVector const
        >::type it_type;

    for(it_type it = boost::begin(input); it != boost::end(input); ++it)
    {
        bool const lower_overlapping = OverlapsPolicy::apply(lower_box, **it);
        bool const upper_overlapping = OverlapsPolicy::apply(upper_box, **it);

        if (lower_overlapping && upper_overlapping)
        {
            exceeding.push_back(*it);
        }
        else if (lower_overlapping)
        {
            lower.push_back(*it);
        }
        else if (upper_overlapping)
        {
            upper.push_back(*it);
        }
        else
        {
            // Is nowhere. That is (since 1.58) possible, it might be
            // skipped by the OverlapsPolicy to enhance performance
        }
    }
}

template
<
    typename ExpandPolicy,
    typename Box,
    typename IteratorVector
>
inline void expand_with_elements(Box& total, IteratorVector const& input)
{
    typedef typename boost::range_iterator<IteratorVector const>::type it_type;
    for(it_type it = boost::begin(input); it != boost::end(input); ++it)
    {
        ExpandPolicy::apply(total, **it);
    }
}

// Match forward_range with itself
template <typename Policy, typename IteratorVector>
inline void handle_one(IteratorVector const& input, Policy& policy)
{
    if (boost::size(input) == 0)
    {
        return;
    }

    typedef typename boost::range_iterator<IteratorVector const>::type it_type;

    // Quadratic behaviour at lowest level (lowest quad, or all exceeding)
    for (it_type it1 = boost::begin(input); it1 != boost::end(input); ++it1)
    {
        it_type it2 = it1;
        for (++it2; it2 != boost::end(input); ++it2)
        {
            policy.apply(**it1, **it2);
        }
    }
}

// Match forward range 1 with forward range 2
template
<
    typename Policy,
    typename IteratorVector1,
    typename IteratorVector2
>
inline void handle_two(IteratorVector1 const& input1,
        IteratorVector2 const& input2,
        Policy& policy)
{
    typedef typename boost::range_iterator
        <
            IteratorVector1 const
        >::type iterator_type1;

    typedef typename boost::range_iterator
        <
            IteratorVector2 const
        >::type iterator_type2;

    if (boost::size(input1) == 0 || boost::size(input2) == 0)
    {
        return;
    }

    for(iterator_type1 it1 = boost::begin(input1);
        it1 != boost::end(input1);
        ++it1)
    {
        for(iterator_type2 it2 = boost::begin(input2);
            it2 != boost::end(input2);
            ++it2)
        {
            policy.apply(**it1, **it2);
        }
    }
}

template <typename IteratorVector>
inline bool recurse_ok(IteratorVector const& input,
                std::size_t min_elements, std::size_t level)
{
    return boost::size(input) >= min_elements
        && level < 100;
}

template <typename IteratorVector1, typename IteratorVector2>
inline bool recurse_ok(IteratorVector1 const& input1,
                IteratorVector2 const& input2,
                std::size_t min_elements, std::size_t level)
{
    return boost::size(input1) >= min_elements
        && recurse_ok(input2, min_elements, level);
}

template
<
    typename IteratorVector1,
    typename IteratorVector2,
    typename IteratorVector3
>
inline bool recurse_ok(IteratorVector1 const& input1,
                IteratorVector2 const& input2,
                IteratorVector3 const& input3,
                std::size_t min_elements, std::size_t level)
{
    return boost::size(input1) >= min_elements
        && recurse_ok(input2, input3, min_elements, level);
}

template
<
    int Dimension,
    typename Box,
    typename OverlapsPolicy1,
    typename OverlapsPolicy2,
    typename ExpandPolicy1,
    typename ExpandPolicy2,
    typename VisitBoxPolicy
>
class partition_two_ranges;

template
<
    int Dimension,
    typename Box,
    typename OverlapsPolicy,
    typename ExpandPolicy,
    typename VisitBoxPolicy
>
class partition_one_range
{
    template <typename IteratorVector>
    static inline Box get_new_box(IteratorVector const& input)
    {
        Box box;
        geometry::assign_inverse(box);
        expand_with_elements<ExpandPolicy>(box, input);
        return box;
    }

    template <typename Policy, typename IteratorVector>
    static inline void next_level(Box const& box,
            IteratorVector const& input,
            std::size_t level, std::size_t min_elements,
            Policy& policy, VisitBoxPolicy& box_policy)
    {
        if (recurse_ok(input, min_elements, level))
        {
            partition_one_range
            <
                1 - Dimension,
                Box,
                OverlapsPolicy,
                ExpandPolicy,
                VisitBoxPolicy
            >::apply(box, input, level + 1, min_elements, policy, box_policy);
        }
        else
        {
            handle_one(input, policy);
        }
    }

    // Function to switch to two forward ranges if there are
    // geometries exceeding the separation line
    template <typename Policy, typename IteratorVector>
    static inline void next_level2(Box const& box,
            IteratorVector const& input1,
            IteratorVector const& input2,
            std::size_t level, std::size_t min_elements,
            Policy& policy, VisitBoxPolicy& box_policy)
    {
        if (recurse_ok(input1, input2, min_elements, level))
        {
            partition_two_ranges
            <
                1 - Dimension,
                Box,
                OverlapsPolicy, OverlapsPolicy,
                ExpandPolicy, ExpandPolicy,
                VisitBoxPolicy
            >::apply(box, input1, input2, level + 1, min_elements,
                policy, box_policy);
        }
        else
        {
            handle_two(input1, input2, policy);
        }
    }

public :
    template <typename Policy, typename IteratorVector>
    static inline void apply(Box const& box,
            IteratorVector const& input,
            std::size_t level,
            std::size_t min_elements,
            Policy& policy, VisitBoxPolicy& box_policy)
    {
        box_policy.apply(box, level);

        Box lower_box, upper_box;
        divide_box<Dimension>(box, lower_box, upper_box);

        IteratorVector lower, upper, exceeding;
        divide_into_subsets<OverlapsPolicy>(lower_box, upper_box,
                    input, lower, upper, exceeding);

        if (boost::size(exceeding) > 0)
        {
            // Get the box of exceeding-only
            Box exceeding_box = get_new_box(exceeding);

            // Recursively do exceeding elements only, in next dimension they
            // will probably be less exceeding within the new box
            next_level(exceeding_box, exceeding, level, min_elements,
                policy, box_policy);

            // Switch to two forward ranges, combine exceeding with
            // lower resp upper, but not lower/lower, upper/upper
            next_level2(exceeding_box, exceeding, lower, level, min_elements,
                policy, box_policy);
            next_level2(exceeding_box, exceeding, upper, level, min_elements,
                policy, box_policy);
        }

        // Recursively call operation both parts
        next_level(lower_box, lower, level, min_elements, policy, box_policy);
        next_level(upper_box, upper, level, min_elements, policy, box_policy);
    }
};

template
<
    int Dimension,
    typename Box,
    typename OverlapsPolicy1,
    typename OverlapsPolicy2,
    typename ExpandPolicy1,
    typename ExpandPolicy2,
    typename VisitBoxPolicy
>
class partition_two_ranges
{
    template
    <
        typename Policy,
        typename IteratorVector1,
        typename IteratorVector2
    >
    static inline void next_level(Box const& box,
            IteratorVector1 const& input1,
            IteratorVector2 const& input2,
            std::size_t level, std::size_t min_elements,
            Policy& policy, VisitBoxPolicy& box_policy)
    {
        partition_two_ranges
        <
            1 - Dimension,
            Box,
            OverlapsPolicy1,
            OverlapsPolicy2,
            ExpandPolicy1,
            ExpandPolicy2,
            VisitBoxPolicy
        >::apply(box, input1, input2, level + 1, min_elements,
                 policy, box_policy);
    }

    template <typename ExpandPolicy, typename IteratorVector>
    static inline Box get_new_box(IteratorVector const& input)
    {
        Box box;
        geometry::assign_inverse(box);
        expand_with_elements<ExpandPolicy>(box, input);
        return box;
    }

    template <typename IteratorVector1, typename IteratorVector2>
    static inline Box get_new_box(IteratorVector1 const& input1,
                    IteratorVector2 const& input2)
    {
        Box box = get_new_box<ExpandPolicy1>(input1);
        expand_with_elements<ExpandPolicy2>(box, input2);
        return box;
    }

public :
    template
    <
        typename Policy,
        typename IteratorVector1,
        typename IteratorVector2
    >
    static inline void apply(Box const& box,
            IteratorVector1 const& input1,
            IteratorVector2 const& input2,
            std::size_t level,
            std::size_t min_elements,
            Policy& policy, VisitBoxPolicy& box_policy)
    {
        box_policy.apply(box, level);

        Box lower_box, upper_box;
        divide_box<Dimension>(box, lower_box, upper_box);

        IteratorVector1 lower1, upper1, exceeding1;
        IteratorVector2 lower2, upper2, exceeding2;
        divide_into_subsets<OverlapsPolicy1>(lower_box, upper_box,
                    input1, lower1, upper1, exceeding1);
        divide_into_subsets<OverlapsPolicy2>(lower_box, upper_box,
                    input2, lower2, upper2, exceeding2);

        if (boost::size(exceeding1) > 0)
        {
            // All exceeding from 1 with 2:

            if (recurse_ok(exceeding1, exceeding2, min_elements, level))
            {
                Box exceeding_box = get_new_box(exceeding1, exceeding2);
                next_level(exceeding_box, exceeding1, exceeding2, level,
                           min_elements, policy, box_policy);
            }
            else
            {
                handle_two(exceeding1, exceeding2, policy);
            }

            // All exceeding from 1 with lower and upper of 2:

            // (Check sizes of all three forward ranges to avoid recurse into
            // the same combinations again and again)
            if (recurse_ok(lower2, upper2, exceeding1, min_elements, level))
            {
                Box exceeding_box = get_new_box<ExpandPolicy1>(exceeding1);
                next_level(exceeding_box, exceeding1, lower2, level,
                           min_elements, policy, box_policy);
                next_level(exceeding_box, exceeding1, upper2, level,
                           min_elements, policy, box_policy);
            }
            else
            {
                handle_two(exceeding1, lower2, policy);
                handle_two(exceeding1, upper2, policy);
            }
        }

        if (boost::size(exceeding2) > 0)
        {
            // All exceeding from 2 with lower and upper of 1:
            if (recurse_ok(lower1, upper1, exceeding2, min_elements, level))
            {
                Box exceeding_box = get_new_box<ExpandPolicy2>(exceeding2);
                next_level(exceeding_box, lower1, exceeding2, level,
                    min_elements, policy, box_policy);
                next_level(exceeding_box, upper1, exceeding2, level,
                    min_elements, policy, box_policy);
            }
            else
            {
                handle_two(lower1, exceeding2, policy);
                handle_two(upper1, exceeding2, policy);
            }
        }

        if (recurse_ok(lower1, lower2, min_elements, level))
        {
            next_level(lower_box, lower1, lower2, level,
                       min_elements, policy, box_policy);
        }
        else
        {
            handle_two(lower1, lower2, policy);
        }
        if (recurse_ok(upper1, upper2, min_elements, level))
        {
            next_level(upper_box, upper1, upper2, level,
                       min_elements, policy, box_policy);
        }
        else
        {
            handle_two(upper1, upper2, policy);
        }
    }
};

struct visit_no_policy
{
    template <typename Box>
    static inline void apply(Box const&, std::size_t )
    {}
};

struct include_all_policy
{
    template <typename Item>
    static inline bool apply(Item const&)
    {
        return true;
    }
};

}} // namespace detail::partition

template
<
    typename Box,
    typename ExpandPolicy1,
    typename OverlapsPolicy1,
    typename ExpandPolicy2 = ExpandPolicy1,
    typename OverlapsPolicy2 = OverlapsPolicy1,
    typename IncludePolicy1 = detail::partition::include_all_policy,
    typename IncludePolicy2 = detail::partition::include_all_policy,
    typename VisitBoxPolicy = detail::partition::visit_no_policy
>
class partition
{
    template
    <
        typename ExpandPolicy,
        typename IncludePolicy,
        typename ForwardRange,
        typename IteratorVector
    >
    static inline void expand_to_range(ForwardRange const& forward_range,
                Box& total, IteratorVector& iterator_vector)
    {
        for(typename boost::range_iterator<ForwardRange const>::type it
            = boost::begin(forward_range);
            it != boost::end(forward_range);
            ++it)
        {
            if (IncludePolicy::apply(*it))
            {
                ExpandPolicy::apply(total, *it);
                iterator_vector.push_back(it);
            }
        }
    }

public :
    template <typename ForwardRange, typename VisitPolicy>
    static inline void apply(ForwardRange const& forward_range,
            VisitPolicy& visitor,
            std::size_t min_elements = 16,
            VisitBoxPolicy box_visitor = detail::partition::visit_no_policy()
            )
    {
        typedef typename boost::range_iterator
            <
                ForwardRange const
            >::type iterator_type;

        if (std::size_t(boost::size(forward_range)) > min_elements)
        {
            std::vector<iterator_type> iterator_vector;
            Box total;
            assign_inverse(total);
            expand_to_range<ExpandPolicy1, IncludePolicy1>(forward_range,
                    total, iterator_vector);

            detail::partition::partition_one_range
                <
                    0, Box,
                    OverlapsPolicy1,
                    ExpandPolicy1,
                    VisitBoxPolicy
                >::apply(total, iterator_vector, 0, min_elements,
                         visitor, box_visitor);
        }
        else
        {
            for(iterator_type it1 = boost::begin(forward_range);
                it1 != boost::end(forward_range);
                ++it1)
            {
                iterator_type it2 = it1;
                for(++it2; it2 != boost::end(forward_range); ++it2)
                {
                    visitor.apply(*it1, *it2);
                }
            }
        }
    }

    template
    <
        typename ForwardRange1,
        typename ForwardRange2,
        typename VisitPolicy
    >
    static inline void apply(ForwardRange1 const& forward_range1,
                ForwardRange2 const& forward_range2,
                VisitPolicy& visitor,
                std::size_t min_elements = 16,
                VisitBoxPolicy box_visitor
                    = detail::partition::visit_no_policy()
                )
    {
        typedef typename boost::range_iterator
            <
                ForwardRange1 const
            >::type iterator_type1;

        typedef typename boost::range_iterator
            <
                ForwardRange2 const
            >::type iterator_type2;

        if (std::size_t(boost::size(forward_range1)) > min_elements
            && std::size_t(boost::size(forward_range2)) > min_elements)
        {
            std::vector<iterator_type1> iterator_vector1;
            std::vector<iterator_type2> iterator_vector2;
            Box total;
            assign_inverse(total);
            expand_to_range<ExpandPolicy1, IncludePolicy1>(forward_range1,
                    total, iterator_vector1);
            expand_to_range<ExpandPolicy2, IncludePolicy2>(forward_range2,
                    total, iterator_vector2);

            detail::partition::partition_two_ranges
                <
                    0, Box, OverlapsPolicy1, OverlapsPolicy2,
                    ExpandPolicy1, ExpandPolicy2, VisitBoxPolicy
                >::apply(total, iterator_vector1, iterator_vector2,
                         0, min_elements, visitor, box_visitor);
        }
        else
        {
            for(iterator_type1 it1 = boost::begin(forward_range1);
                it1 != boost::end(forward_range1);
                ++it1)
            {
                for(iterator_type2 it2 = boost::begin(forward_range2);
                    it2 != boost::end(forward_range2);
                    ++it2)
                {
                    visitor.apply(*it1, *it2);
                }
            }
        }
    }
};

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP


/*
 * Copyright (c) 2020, The Regents of the University of California (Regents).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *    3. Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Please contact the author(s) of this library if you have any questions.
 * Authors: David Fridovich-Keil   ( dfk@eecs.berkeley.edu )
 */

///////////////////////////////////////////////////////////////////////////////
//
// Core open-loop LQ game solver based on Basar and Olsder, Chapter 6. All
// notation matches the text, though we shall assume that `c` (additive drift in
// dynamics) is always `0`, which holds because these dynamics are for delta x,
// delta us. Also, we have modified terms slightly to account for linear terms
// in the stage cost for control, i.e.
//       control penalty i = 0.5 \sum_j du_j^T R_ij (du_j + 2 r_ij)
//
// Solve a time-varying, finite horizon LQ game (finds open-loop Nash
// feedback strategies for both players).
//
// Assumes that dynamics are given by
//           ``` dx_{k+1} = A_k dx_k + \sum_i Bs[i]_k du[i]_k ```
//
// Returns strategies Ps, alphas. Here, all the Ps are zero (by default), and
// only the alphas are nonzero.
//
// Notation is based on derivation which may be found in the PDF included in
// this repository named "open_loop_lq_derivation.pdf".
//
///////////////////////////////////////////////////////////////////////////////

#include <ilqgames/dynamics/multi_player_integrable_system.h>
#include <ilqgames/solver/lq_feedback_solver.h>
#include <ilqgames/solver/lq_open_loop_solver.h>
#include <ilqgames/utils/linear_dynamics_approximation.h>
#include <ilqgames/utils/quadratic_cost_approximation.h>
#include <ilqgames/utils/strategy.h>

#include <glog/logging.h>
#include <Eigen/Core>
#include <vector>

namespace ilqgames {

std::vector<Strategy> LQOpenLoopSolver::Solve(
    const std::vector<LinearDynamicsApproximation>& linearization,
    const std::vector<std::vector<QuadraticCostApproximation>>&
        quadraticization,
    const VectorXf& x0, std::vector<VectorXf>* delta_xs,
    std::vector<std::vector<VectorXf>>* costates) {
  CHECK_EQ(linearization.size(), num_time_steps_);
  CHECK_EQ(quadraticization.size(), num_time_steps_);

  // Make sure delta_xs and costates are the right size.
  if (delta_xs) CHECK_NOTNULL(costates);
  if (costates) CHECK_NOTNULL(delta_xs);
  if (delta_xs) {
    delta_xs->resize(num_time_steps_);
    costates->resize(num_time_steps_);
    for (size_t kk = 0; kk < num_time_steps_; kk++) {
      (*delta_xs)[kk].resize(dynamics_->XDim());
      (*costates)[kk].resize(dynamics_->NumPlayers());
      for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++)
        (*costates)[kk][ii].resize(dynamics_->XDim());
    }
  }

  // List of player-indexed strategies (each of which is a time-indexed
  // affine state error-feedback controller). Since this is an open-loop
  // strategy, we will not change the default zero value of the P matrix.
  std::vector<Strategy> strategies;
  for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++)
    strategies.emplace_back(num_time_steps_, dynamics_->XDim(),
                            dynamics_->UDim(ii));

  // Initialize m^i and M^i and index first by time and then by player.
  for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++) {
    ms_.back()[ii] = quadraticization.back()[ii].state.grad;
    Ms_.back()[ii] = quadraticization.back()[ii].state.hess;
  }

  // (1) Work backward in time and cache "special" terms.
  // NOTE: time starts from the second-to-last entry since we'll treat the
  // final entry as a terminal cost as in Basar and Olsder, ch. 6.
  for (int kk = num_time_steps_ - 2; kk >= 0; kk--) {
    // Unpack linearization and quadraticization at this time step.
    const auto& lin = linearization[kk];
    const auto& quad = quadraticization[kk];

    // Campute capital lambdas.
    capital_lambdas_[kk].setIdentity();
    for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++) {
      const auto control_iter = quad[ii].control.find(ii);
      CHECK(control_iter != quad[ii].control.end());

      chol_Rs_[kk][ii].compute(control_iter->second.hess);
      warped_Bs_[kk][ii] = chol_Rs_[kk][ii].solve(lin.Bs[ii].transpose());
      warped_rs_[kk][ii] = chol_Rs_[kk][ii].solve(control_iter->second.grad);
      capital_lambdas_[kk] += lin.Bs[ii] * warped_Bs_[kk][ii] * Ms_[kk + 1][ii];
    }

    // Compute inv(capital lambda).
    qr_capital_lambdas_[kk].compute(capital_lambdas_[kk]);

    // Compute Ms and ms.
    intermediate_terms_[kk].setZero();
    for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++) {
      intermediate_terms_[kk] -=
          lin.Bs[ii] *
          (warped_Bs_[kk][ii] * ms_[kk + 1][ii] + warped_rs_[kk][ii]);
    }

    for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++) {
      Ms_[kk][ii] =
          quad[ii].state.hess + lin.A.transpose() * Ms_[kk + 1][ii] *
                                    qr_capital_lambdas_[kk].solve(lin.A);
      ms_[kk][ii] =
          quad[ii].state.grad +
          lin.A.transpose() * (ms_[kk + 1][ii] +
                               Ms_[kk + 1][ii] * qr_capital_lambdas_[kk].solve(
                                                     intermediate_terms_[kk]));
    }
  }

  // (2) Now compute optimal state and control trajectory forward in time.
  VectorXf x_star = x0;
  VectorXf last_x_star;
  for (size_t kk = 0; kk < num_time_steps_ - 1; kk++) {
    // Maybe set delta_x.
    if (delta_xs) (*delta_xs)[kk] = x_star;

    // Unpack linearization at this time step.
    const auto& lin = linearization[kk];

    // Compute optimal x.
    last_x_star = x_star;
    x_star = qr_capital_lambdas_[kk].solve(lin.A * last_x_star +
                                           intermediate_terms_[kk]);

    // Compute optimal u and store (sign flipped) in alpha.
    // Also maybe compute costates.
    for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++) {
      const VectorXf intermediate_term =
          Ms_[kk + 1][ii] * x_star + ms_[kk + 1][ii];
      strategies[ii].alphas[kk] =
          warped_Bs_[kk][ii] * intermediate_term + warped_rs_[kk][ii];

      if (costates) (*costates)[kk][ii] = lin.A.transpose() * intermediate_term;
    }

    // Check dynamic feasibility.
    // VectorXf check_x = lin.A * last_x_star;
    // for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++)
    //   check_x -= lin.Bs[ii] * strategies[ii].alphas[kk];

    // CHECK_LE((x_star - check_x).cwiseAbs().maxCoeff(), 1e-1);
  }

  // Set delta_x and costate for last time step.
  if (delta_xs) {
    delta_xs->back() = x_star;
    for (PlayerIndex ii = 0; ii < dynamics_->NumPlayers(); ii++)
      costates->back()[ii].setZero();
  }

  return strategies;
}

}  // namespace ilqgames


/*
 * Copyright (c) 2021 Abit More, and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <graphene/chain/market_object.hpp>

#include <boost/multiprecision/cpp_int.hpp>

#include <functional>

#include <fc/io/raw.hpp>

using namespace graphene::chain;

/*
target_CR = max( target_CR, MCR )

target_CR = new_collateral / ( new_debt / feed_price )
          = ( collateral - max_amount_to_sell ) * feed_price
            / ( debt - amount_to_get )
          = ( collateral - max_amount_to_sell ) * feed_price
            / ( debt - round_down(max_amount_to_sell * match_price ) )
          = ( collateral - max_amount_to_sell ) * feed_price
            / ( debt - (max_amount_to_sell * match_price - x) )

Note: x is the fraction, 0 <= x < 1

=>

max_amount_to_sell = ( (debt + x) * target_CR - collateral * feed_price )
                     / (target_CR * match_price - feed_price)
                   = ( (debt + x) * tCR / DENOM - collateral * fp_debt_amt / fp_coll_amt )
                     / ( (tCR / DENOM) * (mp_debt_amt / mp_coll_amt) - fp_debt_amt / fp_coll_amt )
                   = ( (debt + x) * tCR * fp_coll_amt * mp_coll_amt - collateral * fp_debt_amt * DENOM * mp_coll_amt)
                     / ( tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt )

max_debt_to_cover = max_amount_to_sell * match_price
                  = max_amount_to_sell * mp_debt_amt / mp_coll_amt
                  = ( (debt + x) * tCR * fp_coll_amt * mp_debt_amt - collateral * fp_debt_amt * DENOM * mp_debt_amt)
                    / (tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt)
*/
share_type call_order_object::get_max_debt_to_cover( price match_price,
                                                     price feed_price,
                                                     const uint16_t maintenance_collateral_ratio,
                                                     const optional<price>& maintenance_collateralization )const
{ try {
   // be defensive here, make sure feed_price is in collateral / debt format
   if( feed_price.base.asset_id != call_price.base.asset_id )
      feed_price = ~feed_price;

   FC_ASSERT( feed_price.base.asset_id == call_price.base.asset_id
              && feed_price.quote.asset_id == call_price.quote.asset_id );

   bool after_core_hardfork_1270 = maintenance_collateralization.valid();

   // be defensive here, make sure maintenance_collateralization is in collateral / debt format
   if( after_core_hardfork_1270 )
   {
      FC_ASSERT( maintenance_collateralization->base.asset_id == call_price.base.asset_id
                 && maintenance_collateralization->quote.asset_id == call_price.quote.asset_id );
   }

   // According to the feed protection rule (https://github.com/cryptonomex/graphene/issues/436),
   // a call order should only be called when its collateral ratio is not higher than required maintenance collateral ratio.
   // Although this should be guaranteed by the caller of this function, we still check here to be defensive.
   // Theoretically this check can be skipped for better performance.
   //
   // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
   if( ( !after_core_hardfork_1270 && call_price > feed_price )
       || ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) )
      return 0;

   if( !target_collateral_ratio.valid() ) // target cr is not set
      return debt;

   uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small

   price target_collateralization = ( after_core_hardfork_1270 ?
                                      feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) :
                                      price() );

   // be defensive here, make sure match_price is in collateral / debt format
   if( match_price.base.asset_id != call_price.base.asset_id )
      match_price = ~match_price;

   FC_ASSERT( match_price.base.asset_id == call_price.base.asset_id
              && match_price.quote.asset_id == call_price.quote.asset_id );

   typedef boost::multiprecision::int256_t i256;
   i256 mp_debt_amt = match_price.quote.amount.value;
   i256 mp_coll_amt = match_price.base.amount.value;
   i256 fp_debt_amt = feed_price.quote.amount.value;
   i256 fp_coll_amt = feed_price.base.amount.value;

   // firstly we calculate without the fraction (x), the result could be a bit too small
   i256 numerator = fp_coll_amt * mp_debt_amt * debt.value * tcr
                  - fp_debt_amt * mp_debt_amt * collateral.value * GRAPHENE_COLLATERAL_RATIO_DENOM;
   if( numerator < 0 ) // feed protected, actually should not be true here, just check to be safe
      return 0;

   i256 denominator = fp_coll_amt * mp_debt_amt * tcr - fp_debt_amt * mp_coll_amt * GRAPHENE_COLLATERAL_RATIO_DENOM;
   if( denominator <= 0 ) // black swan
      return debt;

   // note: if add 1 here, will result in 1.5x imperfection rate;
   //       however, due to rounding, the result could still be a bit too big, thus imperfect.
   i256 to_cover_i256 = ( numerator / denominator );
   if( to_cover_i256 >= debt.value ) // avoid possible overflow
      return debt;
   share_type to_cover_amt = static_cast< int64_t >( to_cover_i256 );

   // stabilize
   // note: rounding up-down results in 3x imperfection rate in comparison to down-down-up
   asset to_pay = asset( to_cover_amt, debt_type() ) * match_price;
   asset to_cover = to_pay * match_price;
   to_pay = to_cover.multiply_and_round_up( match_price );

   if( to_cover.amount >= debt || to_pay.amount >= collateral ) // to be safe
      return debt;
   FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );

   // Check whether the collateral ratio after filled is high enough
   // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
   std::function<bool()> result_is_good = after_core_hardfork_1270 ?
      std::function<bool()>( [this,&to_cover,&to_pay,target_collateralization]() -> bool
      {
         price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover );
         return ( new_collateralization > target_collateralization );
      }) :
      std::function<bool()>( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool
      {
         price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
         return ( new_call_price > feed_price );
      });

   // if the result is good, we return.
   if( result_is_good() )
      return to_cover.amount;

   // be here, to_cover is too small due to rounding. deal with the fraction
   numerator += fp_coll_amt * mp_debt_amt * tcr; // plus the fraction
   to_cover_i256 = ( numerator / denominator ) + 1;
   if( to_cover_i256 >= debt.value ) // avoid possible overflow
      to_cover_i256 = debt.value;
   to_cover_amt = static_cast< int64_t >( to_cover_i256 );

   asset max_to_pay = ( ( to_cover_amt == debt.value ) ? get_collateral()
                        : asset( to_cover_amt, debt_type() ).multiply_and_round_up( match_price ) );
   if( max_to_pay.amount > collateral )
      max_to_pay.amount = collateral;

   asset max_to_cover = ( ( max_to_pay.amount == collateral ) ? get_debt() : ( max_to_pay * match_price ) );
   if( max_to_cover.amount >= debt ) // to be safe
   {
      max_to_pay.amount = collateral;
      max_to_cover.amount = debt;
   }

   if( max_to_pay <= to_pay || max_to_cover <= to_cover ) // strange data. should skip binary search and go on, but doesn't help much
      return debt;
   FC_ASSERT( max_to_pay > to_pay && max_to_cover > to_cover );

   asset min_to_pay = to_pay;
   asset min_to_cover = to_cover;

   // try with binary search to find a good value
   // note: actually binary search can not always provide perfect result here,
   //       due to rounding, collateral ratio is not always increasing while to_pay or to_cover is increasing
   bool max_is_ok = false;
   while( true )
   {
      // get the mean
      if( match_price.base.amount < match_price.quote.amount ) // step of collateral is smaller
      {
         to_pay.amount = ( min_to_pay.amount + max_to_pay.amount + 1 ) / 2; // should not overflow. round up here
         if( to_pay.amount == max_to_pay.amount )
            to_cover.amount = max_to_cover.amount;
         else
         {
            to_cover = to_pay * match_price;
            if( to_cover.amount >= max_to_cover.amount ) // can be true when max_is_ok is false
            {
               to_pay.amount = max_to_pay.amount;
               to_cover.amount = max_to_cover.amount;
            }
            else
            {
               to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization, no change or become smaller
               FC_ASSERT( to_pay.amount < max_to_pay.amount );
            }
         }
      }
      else // step of debt is smaller or equal
      {
         to_cover.amount = ( min_to_cover.amount + max_to_cover.amount ) / 2; // should not overflow. round down here
         if( to_cover.amount == max_to_cover.amount )
            to_pay.amount = max_to_pay.amount;
         else
         {
            to_pay = to_cover.multiply_and_round_up( match_price );
            if( to_pay.amount >= max_to_pay.amount ) // can be true when max_is_ok is false
            {
               to_pay.amount = max_to_pay.amount;
               to_cover.amount = max_to_cover.amount;
            }
            else
            {
               to_cover = to_pay * match_price; // stabilization, to_cover should have increased
               if( to_cover.amount >= max_to_cover.amount ) // to be safe
               {
                  to_pay.amount = max_to_pay.amount;
                  to_cover.amount = max_to_cover.amount;
               }
            }
         }
      }

      // check again to see if we've moved away from the minimums, if not, use the maximums directly
      if( to_pay.amount <= min_to_pay.amount || to_cover.amount <= min_to_cover.amount
            || to_pay.amount > max_to_pay.amount || to_cover.amount > max_to_cover.amount )
      {
         to_pay.amount = max_to_pay.amount;
         to_cover.amount = max_to_cover.amount;
      }

      // check the mean
      if( to_pay.amount == max_to_pay.amount && ( max_is_ok || to_pay.amount == collateral ) )
         return to_cover.amount;
      FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );

      // Check whether the result is good
      if( result_is_good() ) // good
      {
         if( to_pay.amount == max_to_pay.amount )
            return to_cover.amount;
         max_to_pay.amount = to_pay.amount;
         max_to_cover.amount = to_cover.amount;
         max_is_ok = true;
      }
      else // not good
      {
         if( to_pay.amount == max_to_pay.amount )
            break;
         min_to_pay.amount = to_pay.amount;
         min_to_cover.amount = to_cover.amount;
      }
   }

   // be here, max_to_cover is too small due to rounding. search forward
   for( uint64_t d1 = 0, d2 = 1, d3 = 1; ; d1 = d2, d2 = d3, d3 = d1 + d2 ) // 1,1,2,3,5,8,...
   {
      if( match_price.base.amount > match_price.quote.amount ) // step of debt is smaller
      {
         to_pay.amount += d2;
         if( to_pay.amount >= collateral )
            return debt;
         to_cover = to_pay * match_price;
         if( to_cover.amount >= debt )
            return debt;
         to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization
         if( to_pay.amount >= collateral )
            return debt;
      }
      else // step of collateral is smaller or equal
      {
         to_cover.amount += d2;
         if( to_cover.amount >= debt )
            return debt;
         to_pay = to_cover.multiply_and_round_up( match_price );
         if( to_pay.amount >= collateral )
            return debt;
         to_cover = to_pay * match_price; // stabilization
         if( to_cover.amount >= debt )
            return debt;
      }

      // defensive check
      FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );

      // Check whether the result is good
      if( result_is_good() ) // good
         return to_cover.amount;
   }

} FC_CAPTURE_AND_RETHROW( (*this)(feed_price)(match_price)(maintenance_collateral_ratio) ) }

FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::limit_order_object,
                    (graphene::db::object),
                    (expiration)(seller)(for_sale)(sell_price)(deferred_fee)(deferred_paid_fee)
                  )

FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::call_order_object, (graphene::db::object),
                    (borrower)(collateral)(debt)(call_price)(target_collateral_ratio) )

FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::force_settlement_object,
                    (graphene::db::object),
                    (owner)(balance)(settlement_date)
                  )

FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::collateral_bid_object, (graphene::db::object),
                    (bidder)(inv_swan_price) )

GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::limit_order_object )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::call_order_object )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::force_settlement_object )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::collateral_bid_object )


#ifndef HW4_RENDER_HPP
#define HW4_RENDER_HPP

#include <algorithm>
#include <functional>
#include <memory>

#include <boost/filesystem.hpp>
#include <glm/glm.hpp>

#include "scene.hpp"

namespace hw4 {
    class Image {
        struct pixel {
            unsigned char r;
            unsigned char g;
            unsigned char b;
        };

        glm::ivec2 m_size;
        std::unique_ptr<pixel[]> m_data;
    public:
        Image() : m_size(glm::ivec2(0)), m_data(nullptr) {}
        Image(glm::ivec2 size)
            : m_size(size), m_data(std::make_unique<pixel[]>(size.x * size.y)) {}

        glm::ivec2 size() const { return this->m_size; }

        Image& fill(glm::vec3 value) {
            std::fill(
                &this->m_data[0],
                &this->m_data[this->m_size.x * this->m_size.y],
                pixel {
                    static_cast<unsigned char>(std::round(glm::clamp(value.r, 0.0f, 1.0f) * 255.0f)),
                    static_cast<unsigned char>(std::round(glm::clamp(value.g, 0.0f, 1.0f) * 255.0f)),
                    static_cast<unsigned char>(std::round(glm::clamp(value.b, 0.0f, 1.0f) * 255.0f))
                }
            );

            return *this;
        }

        Image& set_pixel(glm::ivec2 pos, glm::vec3 value) {
            assert(pos.x >= 0 && pos.x < this->m_size.x);
            assert(pos.y >= 0 && pos.y < this->m_size.y);

            this->m_data[static_cast<size_t>(pos.x + pos.y * this->m_size.x)] = pixel {
                static_cast<unsigned char>(std::round(glm::clamp(value.r, 0.0f, 1.0f) * 255.0f)),
                static_cast<unsigned char>(std::round(glm::clamp(value.g, 0.0f, 1.0f) * 255.0f)),
                static_cast<unsigned char>(std::round(glm::clamp(value.b, 0.0f, 1.0f) * 255.0f))
            };

            return *this;
        }

        glm::vec3 get_pixel(glm::ivec2 pos) const {
            assert(pos.x >= 0 && pos.x < this->m_size.x);
            assert(pos.y >= 0 && pos.y < this->m_size.y);

            pixel p = this->m_data[static_cast<size_t>(pos.x + pos.y * this->m_size.x)];

            return glm::vec3(p.r / 255.0f, p.g / 255.0f, p.b / 255.0f);
        }

        Image& copy_data(const Image& img, glm::ivec2 pos) {
            assert(pos.x >= 0 && pos.x + img.m_size.x <= this->m_size.x);
            assert(pos.y >= 0 && pos.y + img.m_size.y <= this->m_size.y);

            // Since image rows are stored separately, each row needs to be copied individually.
            for (int y = 0; y < img.m_size.y; y++) {
                std::copy(
                    &img.m_data[y * img.m_size.x],
                    &img.m_data[(y + 1) * img.m_size.x],
                    &this->m_data[pos.x + (y + pos.y) * this->m_size.x]
                );
            }

            return *this;
        }

        void save_as_ppm(const boost::filesystem::path& path) const;
    };

    class RayTraceRenderer {
        glm::ivec2 m_size;
        int m_max_recursion;
        int m_supersample_level;
        float m_bias;
        Camera m_camera;

        float m_img_plane_distance;
        float m_sample_spacing;
        float m_sample_mult;

        void update_params();
    public:
        RayTraceRenderer(
            glm::ivec2 size,
            int max_recursion,
            int supersample_level,
            float bias,
            Camera camera
        )
            : m_size(size), m_max_recursion(max_recursion), m_supersample_level(supersample_level),
              m_bias(bias), m_camera(camera) {
            this->update_params();
        }

        glm::ivec2 size() const { return this->m_size; }
        int max_recursion() const { return this->m_max_recursion; }
        int supersample_level() const { return this->m_supersample_level; }
        const Camera& camera() const { return this->m_camera; }

        Image render(
            const Scene& scene,
            std::function<void (float)> progress_callback
        ) const;
        Image render_patch(
            const Scene& scene,
            const glm::mat4& inv_view_matrix,
            glm::ivec2 start,
            glm::ivec2 size
        ) const;
        glm::vec3 render_pixel(
            const Scene& scene,
            const glm::mat4& inv_view_matrix,
            glm::ivec2 pos
        ) const;
        glm::vec3 render_ray(
            const Scene& scene,
            const Ray& ray
        ) const {
            return this->render_ray(scene, ray, 0);
        }
    private:
        glm::vec3 render_ray(
            const Scene& scene,
            const Ray& ray,
            int recursion
        ) const;
        glm::vec3 render_point_light(
            const Scene& scene,
            const Ray& ray,
            const Intersection& intersection,
            const PointMaterial& material,
            const PointLight& point_light
        ) const;
        float get_visibility(const Scene& scene, glm::vec3 from, glm::vec3 to) const;
    };
}

#endif