#pragma once

#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/rational.hpp>

#include <cryptopp/osrng.h>
#include <blake2/blake2.h>

using Rational = boost::rational<boost::multiprecision::int256_t>;
using Reward   = std::pair<boost::multiprecision::uint128_t, Rational>;

namespace logos
{
// Random pool used by Logos.
// This must be thread_local as long as the AutoSeededRandomPool implementation requires it
extern thread_local CryptoPP::AutoSeededRandomPool random_pool;
using uint128_t = boost::multiprecision::uint128_t;
using uint256_t = boost::multiprecision::uint256_t;
using uint512_t = boost::multiprecision::uint512_t;
// SI dividers
logos::uint128_t const Glgs_ratio = logos::uint128_t ("1000000000000000000000000000000000"); // 10^33
logos::uint128_t const Mlgs_ratio = logos::uint128_t ("1000000000000000000000000000000"); // 10^30
logos::uint128_t const klgs_ratio = logos::uint128_t ("1000000000000000000000000000"); // 10^27
logos::uint128_t const lgs_ratio = logos::uint128_t ("1000000000000000000000000"); // 10^24
logos::uint128_t const mlgs_ratio = logos::uint128_t ("1000000000000000000000"); // 10^21
logos::uint128_t const ulgs_ratio = logos::uint128_t ("1000000000000000000"); // 10^18

// We operate on streams of uint8_t by convention
using stream = std::basic_streambuf<uint8_t>;

class mdb_val;

union uint128_union
{
public:
    static_assert (sizeof(uint128_t)==16, "sizeof(uint128_t) != 16");

    uint128_union ()
    {
        memset(bytes.data(), 0, 16);
    }
    uint128_union (std::string const &);
    uint128_union (uint64_t);
    uint128_union (logos::uint128_union const &) = default;
    uint128_union (logos::uint128_t const &);
    bool Deserialize(logos::stream & stream);
    uint32_t Serialize(logos::stream & stream) const;
    uint128_union operator+ (logos::uint128_union const &) const;
    uint128_union operator- (logos::uint128_union const &) const;
    uint128_union operator* (logos::uint128_union const &) const;
    bool operator== (logos::uint128_union const &) const;
    bool operator!= (logos::uint128_union const &) const;
    bool operator< (logos::uint128_union const &) const;
    bool operator> (logos::uint128_union const &) const;
    bool operator<= (logos::uint128_union const &) const;
    bool operator>= (logos::uint128_union const &) const;
    uint128_union & operator+=(const uint128_union & other);
    uint128_union & operator-=(const uint128_union & other);
    void encode_hex (std::string &) const;
    bool decode_hex (std::string const &);
    void encode_dec (std::string &) const;
    bool decode_dec (std::string const &);
    std::string format_balance (logos::uint128_t scale, int precision, bool group_digits);
    std::string format_balance (logos::uint128_t scale, int precision, bool group_digits, const std::locale & locale);
    logos::uint128_t number () const;
    void clear ();
    bool is_zero () const;
    std::string to_string () const;
    std::string to_string_dec () const;
    logos::mdb_val to_mdb_val(std::vector<uint8_t>& buf) const;
    std::array<uint8_t, 16> bytes;
    std::array<char, 16> chars;
    std::array<uint32_t, 4> dwords;
    std::array<uint64_t, 2> qwords;

    const uint8_t * data() const
    {
        return bytes.data();
    }
    void Hash(blake2b_state & hash) const
    {
        blake2b_update(&hash, bytes.data(), 16);
    }

    operator std::array<uint8_t, 16>() { return bytes; }
};
// Balances are 128 bit.
using amount = uint128_union;
class raw_key;
union uint256_union
{
    uint256_union ()
    {
        memset(bytes.data(), 0, 32);
    }
    uint256_union (std::string const &);
    uint256_union (uint64_t);
    uint256_union (logos::uint256_t const &);
    uint256_union (std::array<uint8_t, 32> const &);
    void encrypt (logos::raw_key const &, logos::raw_key const &, uint128_union const &);
    uint256_union & operator^= (logos::uint256_union const &);
    uint256_union operator^ (logos::uint256_union const &) const;
    bool operator== (logos::uint256_union const &) const;
    bool operator!= (logos::uint256_union const &) const;
    bool operator< (logos::uint256_union const &) const;
    uint256_union operator+ (logos::uint256_union const &) const;
    uint256_union operator- (logos::uint256_union const &) const;
    void encode_hex (std::string &) const;
    bool decode_hex (std::string const &);
    void encode_dec (std::string &) const;
    bool decode_dec (std::string const &);
    void encode_account (std::string &) const;
    std::string to_account () const;
    std::string to_account_split () const;
    bool decode_account (std::string const &);
    std::array<uint8_t, 32> bytes;
    std::array<char, 32> chars;
    std::array<uint32_t, 8> dwords;
    std::array<uint64_t, 4> qwords;
    std::array<uint128_union, 2> owords;
    void clear ();
    bool is_zero () const;
    std::string to_string () const;
    logos::uint256_t number () const;

    uint8_t * data()
    {
        return bytes.data();
    }

    const uint8_t * data() const
    {
        return bytes.data();
    }

    void Hash(blake2b_state & hash) const
    {
        blake2b_update(&hash, bytes.data(), 32);
    }
    uint256_union(void * buf, size_t buf_len)
    {
        assert(buf_len == 32);
        memcpy(data(), buf, 32);
    }

    operator std::array<uint8_t, 32>() { return bytes; }
};
// All keys and hashes are 256 bit.
using block_hash = uint256_union;
using account = uint256_union;
using public_key = uint256_union;
using private_key = uint256_union;
using secret_key = uint256_union;
using checksum = uint256_union;
class raw_key
{
public:
    raw_key () = default;
    ~raw_key ();
    void decrypt (logos::uint256_union const &, logos::raw_key const &, uint128_union const &);
    raw_key (logos::raw_key const &) = delete;
    raw_key (logos::raw_key const &&) = delete;
    logos::raw_key & operator= (logos::raw_key const &) = delete;
    bool operator== (logos::raw_key const &) const;
    bool operator!= (logos::raw_key const &) const;
    logos::uint256_union data;
};
union uint512_union
{
    uint512_union (uint8_t t = 0)
    {
        memset(bytes.data(), t, 64);
    }

    uint512_union (logos::uint512_t const &);
    bool operator== (logos::uint512_union const &) const;
    bool operator!= (logos::uint512_union const &) const;
    logos::uint512_union & operator^= (logos::uint512_union const &);
    void encode_hex (std::string &) const;
    bool decode_hex (std::string const &);
    std::array<uint8_t, 64> bytes;
    std::array<uint32_t, 16> dwords;
    std::array<uint64_t, 8> qwords;
    std::array<uint256_union, 2> uint256s;
    void clear ();
    logos::uint512_t number () const;
    std::string to_string () const;

    uint8_t * data()
    {
        return bytes.data();
    }

    const uint8_t * data() const
    {
        return bytes.data();
    }

    void Hash(blake2b_state & hash) const
    {
        blake2b_update(&hash, bytes.data(), 64);
    }

    operator std::array<uint8_t, 64>() { return bytes; }
};
// Only signatures are 512 bit.
using signature = uint512_union;

logos::uint512_union sign_message (logos::raw_key const &, logos::public_key const &, logos::uint256_union const &);
bool validate_message (logos::public_key const &, logos::uint256_union const &, logos::uint512_union const &);
void deterministic_key (logos::uint256_union const &, uint32_t, logos::uint256_union &);
}

namespace std
{
template <>
struct hash<logos::uint256_union>
{
    size_t operator() (logos::uint256_union const & data_a) const
    {
        return *reinterpret_cast<size_t const *> (data_a.bytes.data ());
    }
};
template <>
struct hash<logos::uint256_t>
{
    size_t operator() (logos::uint256_t const & number_a) const
    {
        return number_a.convert_to<size_t> ();
    }
};
}

namespace logos
{

inline
size_t hash_value(const logos::block_hash & hash)
{
    return std::hash<logos::block_hash>()(hash);
}

}


#pragma once

#include <array>
#include <random>

#include <Eigen/Geometry>
#include <unsupported/Eigen/EulerAngles>

namespace affx {

class Affine {
  using Vector6d = Eigen::Matrix<double, 6, 1>;
  using Vector7d = Eigen::Matrix<double, 7, 1>;

  using Euler = Eigen::EulerAngles<double, Eigen::EulerSystemZYX>;
  typedef Eigen::Affine3d Type;

public:
  Type data;

  explicit Affine() {
    this->data = Type::Identity();
  }

  explicit Affine(const Type& data) {
    this->data = data;
  }

  explicit Affine(double x, double y, double z, double a = 0.0, double b = 0.0, double c = 0.0) {
    data.translation() = Eigen::Vector3d(x, y, z);
    data.linear() = Euler(a, b, c).toRotationMatrix();
  }

  explicit Affine(double x, double y, double z, double q_w, double q_x, double q_y, double q_z) {
    data.translation() = Eigen::Vector3d(x, y, z);
    data.linear() = Eigen::Quaterniond(q_w, q_x, q_y, q_z).toRotationMatrix();
  }

  explicit Affine(const std::array<double, 6>& v): Affine(v[0], v[1], v[2], v[3], v[4], v[5]) { }

  explicit Affine(const std::array<double, 7>& v): Affine(v[0], v[1], v[2], v[3], v[4], v[5]) { }

  explicit Affine(const Vector6d& v): Affine(v[0], v[1], v[2], v[3], v[4], v[5]) { }

  explicit Affine(const Vector7d& v): Affine(v[0], v[1], v[2], v[3], v[4], v[5]) { }

  explicit Affine(const std::array<double, 16>& array) {
    Type affine(Eigen::Matrix4d::Map(array.data()));
    data = affine;
  }

  Affine operator *(const Affine &a) const {
    Type result;
    result = data * a.data;
    return Affine(result);
  }

  Affine inverse() const {
    return Affine(data.inverse());
  }

  bool isApprox(const Affine &a) const {
    return data.isApprox(a.data);
  }

  Eigen::Ref<Affine::Type::MatrixType> matrix() {
    return data.matrix();
  }

  std::array<double, 16> array() const {
    std::array<double, 16> array;
    std::copy(data.data(), data.data() + array.size(), array.begin());
    return array;
  }

  Vector6d vector() const {
    Vector6d result;
    result << data.translation(), angles();
    return result;
  }

  Vector7d vector_with_elbow(double elbow) const {
    Vector7d result;
    result << data.translation(), angles(), elbow;
    return result;
  }

  Eigen::Vector3d translation() const {
    Eigen::Vector3d v;
    v << data.translation();
    return v;
  }

  Eigen::Vector3d angles() const {
    Eigen::Vector3d angles = Euler::FromRotation<false, false, false>(data.rotation()).angles();
    Eigen::Vector3d angles_equal;
    angles_equal << angles[0] - M_PI, M_PI - angles[1], angles[2] - M_PI;

    if (angles_equal[1] > M_PI) {
      angles_equal[1] -= 2 * M_PI;
    }
    if (angles_equal[2] < -M_PI) {
      angles_equal[2] += 2 * M_PI;
    }

    if (angles.norm() < angles_equal.norm()) {
      return angles;
    }
    return angles_equal;
  }

  Type::LinearMatrixType rotation() const {
    Type::LinearMatrixType result;
    result << data.rotation();
    return result;
  }

  Eigen::Quaterniond quaternion() const {
    Eigen::Quaterniond q(data.rotation());
    return q;
  }

  std::array<double, 4> py_quaternion() const {
    auto q = quaternion();
    return {q.w(), q.x(), q.y(), q.z()};
  }

  double x() const {
    return data.translation().x();
  }

  double y() const {
    return data.translation().y();
  }

  double z() const {
    return data.translation().z();
  }

  double a() const {
    return angles()(0);
  }

  double b() const {
    return angles()(1);
  }

  double c() const {
    return angles()(2);
  }

  double qW() const {
    return quaternion().w();
  }

  double qX() const {
    return quaternion().x();
  }

  double qY() const {
    return quaternion().y();
  }

  double qZ() const {
    return quaternion().z();
  }

  void translate(const Eigen::Vector3d &v) {
    data.translate(v);
  }

  void pretranslate(const Eigen::Vector3d &v) {
    data.pretranslate(v);
  }

  void rotate(const Type::LinearMatrixType &r) {
    data.rotate(r);
  }

  void prerotate(const Type::LinearMatrixType &r) {
    data.prerotate(r);
  }

  void setQuaternion(double w, double x, double y, double z) {
    data.linear() = Eigen::Quaterniond(w, x, y, z).toRotationMatrix();
  }

  void setX(double x) {
    data.translation().x() = x;
  }

  void setY(double y) {
    data.translation().y() = y;
  }

  void setZ(double z) {
    data.translation().z() = z;
  }

  void setA(double a) {
    Eigen::Vector3d euler = angles();
    data.linear() = Euler(a, euler(1), euler(2)).toRotationMatrix();
  }

  void setB(double b) {
    Eigen::Vector3d euler = angles();
    data.linear() = Euler(euler(0), b, euler(2)).toRotationMatrix();
  }

  void setC(double c) {
    Eigen::Vector3d euler = angles();
    data.linear() = Euler(euler(0), euler(1), c).toRotationMatrix();
  }

  Affine slerp(const Affine& affine, double t) const {
    Type result;
    Eigen::Quaterniond q_start(data.rotation());
    Eigen::Quaterniond q_end(affine.rotation());
    result.translation() = data.translation() + t * (affine.translation() - data.translation());
    result.linear() = q_start.slerp(t, q_end).toRotationMatrix();
    return Affine(result);
  }

  Affine getInnerRandom() const {
    std::random_device r;
    std::default_random_engine engine(r());

    Eigen::Matrix<double, 6, 1> max, random;
    max << data.translation(), angles();

    for (int i = 0; i < 6; i++) {
      std::uniform_real_distribution<double> distribution(-max(i), max(i));
      random(i) = distribution(engine);
    }

    return Affine(random(0), random(1), random(2), random(3), random(4), random(5));
  }

  std::string toString() const {
    Eigen::Matrix<double, 6, 1> v;
    v << data.translation(), angles();

    return "[" + std::to_string(v(0)) + ", " + std::to_string(v(1)) + ", " + std::to_string(v(2))
      + ", " + std::to_string(v(3)) + ", " + std::to_string(v(4)) + ", " + std::to_string(v(5)) + "]";
  }
};

} // namespace affx


// -----------------------------------------------------------------------
//
// Copyright (C) 2020  - David Fernández Castellanos
//
// This file is part of the MEPLS software. You can use it, redistribute
// it, and/or modify it under the terms of the Creative Commons Attribution
// 4.0 International Public License. The full text of the license can be
// found in the file LICENSE at the top level of the MEPLS distribution.
//
// -----------------------------------------------------------------------

#include <deal.II/base/logstream.h>
#include <deal.II/base/path_search.h>

#include "espci.h"

#if defined(OPENMP)
#include <omp.h>

#endif

#include <cmdparser.hpp>
#include <random>

namespace espci
{

using namespace mepls;

void run(const parameters::Parameters &p, dealii::ConditionalOStream & cout)
{
	// TODO here, dim=2 at least for how we set the average pressure

	/////////////////////////////
	//////  setup system ///////
	///////////////////////////

	constexpr unsigned int dim = 2;

	utils::ContinueSimulation continue_simulation;

	auto timer = utils::TimerSingleton::getInstance();
	timer->enter_subsection("Setting up");

	std::mt19937 generator(p.sim.seed);

	std::vector<espci::element::Anisotropic<dim> *> elements_espci = create_elements<dim>(p,
																						  generator);

	mepls::element::Vector<dim> elements;
	for(auto &element : elements_espci)
		elements.push_back(element);

	snapshot::Check<dim> snapshot_check(p.out.snapshots_min, p.out.snapshots_max,
										p.out.snapshots_interval, p.out.snapshots_sensitivity);
	std::vector<snapshot::Threshold<dim> > threshold_snapshots;
	std::vector<snapshot::Stress<dim> > stress_snapshots;
	std::vector<snapshot::DefGrad<dim> > def_grad_snapshots;
	std::vector<patches::PatchPropertiesSnapshot<dim> > patch_prop_snapshots;
	std::vector<GlobalPropertiesSnapshot<dim>> global_properties_snapshots;
	auto patch_to_element_map = patches::make_patch_to_element_map(elements, p.sim.N_patch_list,
																   p.sim.Nx, p.sim.Ny);
	bool parent_liquid = p.sim.parent_liquid;
	bool thermal_relaxation = p.sim.thermal_relaxation;
	bool reload = p.sim.reload;
	bool het_elasticity = p.sim.het_elasticity;
	bool precalculate = not het_elasticity; // precalculate stress factors when doing patch tests;
	// only valid for homogeneous elasticity
	bool do_ee = p.sim.do_ee; // relax from oi state to ee when doing patch tests

	std::vector<double> theta_list;
	if(p.sim.n_theta == 4)
	{
		theta_list.push_back(0 * M_PI / 180.);
		theta_list.push_back(90 * M_PI / 180.);
		theta_list.push_back(50 * M_PI / 180.);
		theta_list.push_back(140 * M_PI / 180.);
	}
	else if(p.sim.n_theta == 2)
	{
		theta_list.push_back(0 * M_PI / 180.);
		theta_list.push_back(90 * M_PI / 180.);
	}
	else
	{
		for(double z = 0.; z < M_PI; z += M_PI / double(p.sim.n_theta))
			theta_list.push_back(z);
	}

	elasticity_solver::LeesEdwards<dim> solver(p.sim.Nx, p.sim.Ny);
	M_Assert(elements.size() == solver.get_n_elements(), "Numbers of mesoscale and finite "
													  "elements do not match");

	// initial (default) assembly using the quench elastic properties.
	for(auto &element : elements)
		solver.set_elastic_properties(element->number(), element->C());
	solver.setup_and_assembly();

	if(het_elasticity)
		mepls::element::calculate_local_stress_coefficients(elements, solver);
	else
		mepls::element::calculate_local_stress_coefficients_central(elements, solver);

	mepls::element::calculate_ext_stress_coefficients(elements, solver);

	system::Standard<dim> system(elements, solver, generator);
	system::MacroState<dim> &macrostate = system.macrostate;

	if(p.mat.init_eigenstrain)
		apply_initial_eigenstrain<dim>(system, p);

	timer->leave_subsection("Setting up");

	///////////////////////////////////////////
	///////  simulate parent liquid //////////
	/////////////////////////////////////////

	timer->enter_subsection("Simulate parent liquid");

	history::History<dim> liquid_history("parent_liquid");
	system.set_history(liquid_history);
	if(parent_liquid)
	{

		for(auto &element : elements_espci)
		{
			auto conf = element->config();
			conf.temperature = p.mat.temperature_liquid;
			element->config(conf);
		}

		simulate_parent_liquid_KMC(system, liquid_history, p, continue_simulation);
//		simulate_parent_liquid_MH(system, liquid_history, p, continue_simulation);
	}

	// apply instantaneous quench
	for(auto &element : elements_espci)
	{
		auto conf = element->config();
		conf.temperature = 0;
		element->config(conf);
	}

	dynamics::relaxation(system, continue_simulation, 1e10);
	if(not continue_simulation())
	{
		cout << continue_simulation << std::endl;
		abort();
	}

	liquid_history.add_macro(system);
	system.macrostate.clear();

	timer->leave_subsection("Simulate parent liquid");

	////////////////////////////
	//////      AQS     ///////
	//////////////////////////

	history::History<dim> aqs_history("AQS");
	system.set_history(aqs_history);

	/* ----- snapshots ----- */
	timer->enter_subsection("Taking snapshots");

	if(p.out.snapshots.find("slip_thresholds") != std::string::npos)
		threshold_snapshots.push_back(
			snapshot::Threshold(system, p.sim.monitor_name, 0.,
								macrostate[p.sim.monitor_name]));
	if(p.out.snapshots.find("stress") != std::string::npos)
		stress_snapshots.push_back(
			snapshot::Stress(system, p.sim.monitor_name, 0.,
							 macrostate[p.sim.monitor_name]));
	if(p.out.snapshots.find("def_grad") != std::string::npos)
		def_grad_snapshots.push_back(
			snapshot::DefGrad(system, p.sim.monitor_name, 0.,
							  macrostate[p.sim.monitor_name]));

	if(p.out.snapshots.find("patches") != std::string::npos)
		for(auto n_patch : p.sim.N_patch_list)
		{
			cout << ">>> " << n_patch << std::endl;
			patch_prop_snapshots.push_back(
				patches::PatchPropertiesSnapshot<dim>(system,
													  p.sim.monitor_name, 0.,
													  macrostate[p.sim.monitor_name],
													  n_patch, theta_list, precalculate,
													  do_ee));
		}

	if(p.out.snapshots.find("global_properties") != std::string::npos)
		global_properties_snapshots.push_back(
			GlobalPropertiesSnapshot<dim>(system, solver, aqs_history.index(), p.sim.monitor_name,
										  0., macrostate[p.sim.monitor_name]));
	timer->leave_subsection("Taking snapshots");

	/* ----- save struct. properties ----- */
	std::vector<event::RenewSlip<dim>> renewal_vector;
	for(auto &element : elements)
		element->record_structural_properties(renewal_vector);
	aqs_history.add(renewal_vector);

	/* ---- simulation loop ----- */
	timer->enter_subsection("Running AQS");

	continue_simulation(system.macrostate[p.sim.monitor_name] < p.sim.monitor_limit,
						p.sim.monitor_name + " limit reached");

	// adding an (empty) driving event will record the average prestress
	// (which can be slightly non-zero due to numerical accuray) as the
	// first event in the history
	event::Driving<dim> prestress_event;
	prestress_event.activation_protocol = mepls::dynamics::Protocol::prestress;
	system.add(prestress_event);

	aqs_history.add_macro(system);

	assert( macrostate["av_vm_plastic_strain"]==0. );

	double G_old = 0.;
	double G_stat = p.mat.G;
	while(continue_simulation())
	{
		// reassemble the elastic properties if the change in the shear
		// modulus is big enough and if it's different enough from the stationary
		// value
		dealii::SymmetricTensor<4,dim> av_C;
		for(auto &element : elements)
			av_C += element->C();
		av_C /= double(elements.size());

		double G_new = av_C[0][1][0][1];

		if(std::abs(G_new/G_old-1)>0.001 and std::abs(G_old/G_stat-1)>0.005)
		{
			timer->leave_subsection("Running AQS");
			timer->enter_subsection("Reassembling with new stiffness");

			G_old = G_new;

			solver.reassemble_with_new_stiffness(av_C);

			// we call add which will inform the macrostate and the elements
			// about the change in external stress due to the change in global
			// stiffness and will also record that change in the history
			const std::vector<event::Plastic<dim>> added_yielding;
			event::Driving<dim> driving_event_variation_stiffness;
			driving_event_variation_stiffness.activation_protocol = mepls::dynamics::Protocol::variation_stiffness;
			system.add(driving_event_variation_stiffness);

			auto state = solver.get_state();
			mepls::element::calculate_local_stress_coefficients_central(elements, solver);
			mepls::element::calculate_ext_stress_coefficients(elements, solver);
			solver.set_state(state);

			// relax to ensure there are no unstable elements after the change
			// in the elastic properties (if the shear modulus rises with strain,
			// that will lead to stress rises that can unstabilise elements)
			dynamics::relaxation(system, continue_simulation);

			timer->leave_subsection("Reassembling with new stiffness");
			timer->enter_subsection("Running AQS");
		}

		cout << aqs_history.index() << " | " << std::fixed << macrostate["total_strain"]
				  << " " << macrostate["ext_stress"] << std::endl;

		if(snapshot_check(macrostate[p.sim.monitor_name]))
		{
			timer->leave_subsection("Running AQS");
			timer->enter_subsection("Taking snapshots");

			if(p.out.snapshots.find("slip_thresholds") != std::string::npos)
				threshold_snapshots.push_back(
					snapshot::Threshold(system, p.sim.monitor_name,
										snapshot_check.desired_value,
										macrostate[p.sim.monitor_name]));
			if(p.out.snapshots.find("stress") != std::string::npos)
				stress_snapshots.push_back(
					snapshot::Stress(system, p.sim.monitor_name,
									 snapshot_check.desired_value, macrostate[p.sim.monitor_name]));
			if(p.out.snapshots.find("def_grad") != std::string::npos)
				def_grad_snapshots.push_back(
					snapshot::DefGrad(system, p.sim.monitor_name,
									  snapshot_check.desired_value,
									  macrostate[p.sim.monitor_name]));
			if(p.out.snapshots.find("patches") != std::string::npos)
				for(auto n_patch : p.sim.N_patch_list)
				{
					cout << ">>> " << n_patch << std::endl;
					patch_prop_snapshots.push_back(
						patches::PatchPropertiesSnapshot<dim>(system,
															  p.sim.monitor_name,
															  snapshot_check.desired_value,
															  macrostate[p.sim.monitor_name],
															  n_patch,
															  theta_list, precalculate, do_ee));
				}

			timer->leave_subsection("Taking snapshots");
			timer->enter_subsection("Running AQS");
		}

		/* ---- dyanmics ----- */
		dynamics::finite_extremal_dynamics_step(1e-4 * 0.5, system);
		aqs_history.add_macro(system);

		dynamics::relaxation(system, continue_simulation);
		aqs_history.add_macro(system);

		continue_simulation(system.macrostate[p.sim.monitor_name] < p.sim.monitor_limit,
							p.sim.monitor_name + " limit reached");
	}

	auto solver_state_end_AQS = solver.get_state();
	auto macrostate_end_AQS = system.macrostate;

	cout << continue_simulation << std::endl;

	timer->leave_subsection("Running AQS");

	//////////////////////////////////
	//////  thermal relaxation //////
	////////////////////////////////

	timer->enter_subsection("Thermal relaxation");

	history::History<dim> thermal_relaxation_hist("thermal_relaxation");

	if(thermal_relaxation)
	{
		mepls::element::Vector<dim> elements_replica;
		for(auto &element : elements_espci)
		{
			auto element_copy = element->make_copy();

			auto conf = element_copy->config();
			conf.temperature = p.mat.temperature_relaxation;
			element_copy->config(conf);

			elements_replica.push_back( element_copy );
		}

		// the solver state is copied, so it contains the eigenstrain and the load
		// in this case the elements elastic field need not be converted into a
		// prestress before
		solver.set_state(solver_state_end_AQS);

		auto system_replica = system.get_new_instance(elements_replica, solver,
													  system.generator);
		system_replica->macrostate = macrostate_end_AQS;
		system_replica->set_history(thermal_relaxation_hist);

		// initiate dynamics using copied system
		mepls::dynamics::KMC<dim> kmc;
		mepls::utils::ContinueSimulation continue_relaxing;
		auto &macrostate = system_replica->macrostate;
		thermal_relaxation_hist.add_macro( *system_replica );

		while(continue_relaxing())
		{

			cout << thermal_relaxation_hist.index() << " | " << std::fixed
					  << macrostate["total_strain"]
					  << " " << macrostate["ext_stress"]
					  << " " << macrostate["pressure"]
					  << " " << macrostate["time"]
					  << std::endl;

			kmc(*system_replica);
			thermal_relaxation_hist.add_macro( *system_replica );

			mepls::dynamics::relaxation(*system_replica, continue_relaxing);
			thermal_relaxation_hist.add_macro( *system_replica );

			continue_relaxing(macrostate["ext_stress"] > 0, "System relaxed");
		}

		delete system_replica;

		cout << continue_relaxing << std::endl;
		cout << "Relaxing finished" << std::endl;
	}

	timer->leave_subsection("Thermal relaxation");

	///////////////////////////////
	//////  reloading step ///////
	/////////////////////////////

	history::History<dim> aqs_unloading("AQS_unloading");
	history::History<dim> aqs_reload_forward("AQS_reload_forward");
	history::History<dim> aqs_reload_backward("AQS_reload_backward");

	if(reload)
	{
		timer->enter_subsection("Reloading");

		// the solver state is copied, so it contains the eigenstrain and the load
		// in this case the elements elastic field need not be converted into a
		// prestress before
		solver.set_state(solver_state_end_AQS);

		system.macrostate = macrostate_end_AQS;

		system.set_history(aqs_unloading);

		utils::ContinueSimulation continue_unloading;
		auto &macrosate = system.macrostate;
		aqs_unloading.add_macro(system);

		while(continue_unloading())
		{
			cout << aqs_unloading.index() << " | " << std::fixed
					  << macrostate["total_strain"] << " " << macrostate["ext_stress"] << " "
					  << macrostate["pressure"] << std::endl;

			dynamics::finite_extremal_dynamics_step(1e-4 * 0.5, system, false);
			aqs_unloading.add_macro(system);

			dynamics::relaxation(system, continue_unloading);
			aqs_unloading.add_macro(system);

			continue_unloading(macrostate["ext_stress"] > 0, "System unloaded");
		}

		cout << continue_unloading << std::endl;

		perform_reloading(system, aqs_reload_forward, true, p);
		perform_reloading(system, aqs_reload_backward, false, p);

		timer->leave_subsection("Reloading");
	}

	/////////////////////////////////////
	//////  post-simultaion step ///////
	///////////////////////////////////

	std::string filename = p.out.filename + "_" + write::make_filename(p) + ".h5";
	H5::H5File file(p.out.path + "/" + filename, H5F_ACC_TRUNC);
	write::file_attrs(file, p);

	if(parent_liquid)
		write::evolution_history(file, liquid_history);
	if(thermal_relaxation)
		write::evolution_history(file, thermal_relaxation_hist);
	write::evolution_history(file, aqs_history);
	if(reload)
	{
		write::evolution_history(file, aqs_unloading);
		write::evolution_history(file, aqs_reload_forward);
		write::evolution_history(file, aqs_reload_backward);
	}

	write::patch_info<dim>(file, "/patch_info", patch_to_element_map);
	write::element_info(file, "/element_info", elements);
	write::snapshots(file, "/snapshots", threshold_snapshots, stress_snapshots, def_grad_snapshots,
					 patch_prop_snapshots, global_properties_snapshots);
	file.close();

	/* ---- delete dynamically-allocated objects ----- */
	for(auto &element : elements)
		delete element;

	if(p.out.verbosity and omp_get_thread_num() == 0)
		timer->print_summary();
} // run

} // espci

int main(int argc, char *argv[])
{
	cli::Parser parser(argc, argv);
	parser.set_optional<std::string>("f", "file", "./default.cfg",
									 "Name of the input configuration file");
	parser.run_and_exit_if_error();

	dealii::deallog.depth_console(0);

	espci::parameters::Parameters p;

	try
	{
		p.load_file(parser.get<std::string>("f"));
	}
	catch(dealii::PathSearch::ExcFileNotFound &)
	{
		p.generate_file(parser.get<std::string>("f"));
		std::cout << "Configuration file " << parser.get<std::string>("f") << " created" << std::endl;
	}

	unsigned int n_rep = p.sim.n_rep;
	if(n_rep < omp_get_max_threads())
		n_rep = omp_get_max_threads();

	// initialize the master engine with the master seed
	std::srand(p.sim.seed);

	#pragma omp parallel
	{
		unsigned int n_threads = omp_get_max_threads();
		unsigned int id = omp_get_thread_num();
		unsigned int rep_per_thread = int( n_rep / n_threads );

		dealii::ConditionalOStream cout(std::cout, id==0 and p.out.verbosity);

		for(unsigned int n = 0; n < rep_per_thread; ++n)
		{
			espci::parameters::Parameters p_thread = p;
			#pragma critical
			{
				// generate a seed for a simulation run
				p_thread.sim.seed = std::rand();
			};

			espci::run(p_thread, cout);
		}

	}

}


#include <iostream>
#include <mutex>
#include <boost/thread/thread.hpp>
#include <boost/thread/tss.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <list>
#include <tuple>

#define LOOP 100000
#define BENCH_RUNS 5

using namespace std;
using namespace boost;

template <typename V=int> struct node{
  V val;
  list< std::tuple<int,int,int> > vinfo; //first,last,remove
  node * left;
  node * right;
};

template <typename T=int> class orSet{
private:

  //global state
  int gvid;
  node<T> *root;

  //thread-local state
  boost::thread_specific_ptr<int> lvid;
  boost::thread_specific_ptr< list< pair< T, node<T>*> > > localAdd;
  boost::thread_specific_ptr< list< pair< node<T>*, int> > > localRemove;

  //class auxiliars
  boost::detail::spinlock m;

void insert(T val, node<T> *leaf, T vid){
  if(val < leaf->val){
    if(leaf->left != NULL) insert(val, leaf->left,vid);

    else {
      leaf->left = new node<T>;
      leaf->left->val = val;
      leaf->left->left = NULL;
      leaf->left->right = NULL;

      (leaf->left->vinfo).push_front(make_tuple(vid,vid,0));

    }

  }

  else{
    if(leaf->right != NULL) insert(val, leaf->right,vid);

    else{
      leaf->right=new node<T>;
      leaf->right->val=val;
      leaf->right->left=NULL;
      leaf->right->right=NULL;

      (leaf->right->vinfo).push_front(make_tuple(vid,vid,0));
    }
  }
}

node<T>* searchNode(node<T> * leaf, T val){
  if(leaf!=NULL){
    if(val == leaf->val) return leaf;
    if(val < leaf->val) return searchNode(leaf->left, val);
    else return searchNode(leaf->right, val);
  }
  else return NULL;
}

node<T>* findParentNode(node<T> * leaf, T val, node<T> * parent){
  if(leaf!=NULL){
    if(val == leaf->val) return parent;
    if(val < leaf->val) return findParentNode(leaf->left, val, leaf);
    else return findParentNode(leaf->right, val, leaf);
  }
  else return parent;
}

void removeFromLocalAdded(T val){
  typename std::list< pair<T,node<T>*> >::iterator itr = (*localAdd).begin();
  while (itr != (*localAdd).end()){
    if((*itr).first == val) {
      itr = (*localAdd).erase(itr);
    }
    else ++itr;
  }
}

void weakAddHelper(T val, node<T>* possibleParent, int vid){
    //start seaching starting from the possible parent
    node<T>* n = searchNode(possibleParent, val);
    if(n==NULL){
        if (possibleParent != NULL) insert(val, possibleParent, vid);
        else insertAtRoot(val, vid); //if possibleParent null, tree is empty
    }
    else{
        //node to insert already exists, just update vinfo
        if(get<2>((n->vinfo).front()) > 0){
            (n->vinfo).push_front(make_tuple(vid,vid,0)); //if removed insert new vinfo at head
        }
        else{
            get<1>((n->vinfo).front())= vid; //if not update last of the head
        }
    }
}

void strongAddHelper(T val, int vid){
    //search for a node, if doesn't exist insert it. Otherwise just update
    node<T>* n = searchNode(root, val);
    if(n==NULL){
        //node to insert doesn't exist so we just need to insert it
        if (root != NULL) insert(val, root, vid);
        else insertAtRoot(val, vid);
    }
    else{
        //node to insert already exists, just update vinfo
        if(get<2>((n->vinfo).front()) > 0){
            (n->vinfo).push_front(make_tuple(vid,vid,0)); //if removed insert new vinfo at head
        }
        else{
            get<1>((n->vinfo).front())= vid; //if not update last of the head
        }
    }
}

void weakRemoveHelper(node<T>* node, int observedLast, int vid){
  //check if was not removed by other threads meanwhile
  bool notConcurrentlyRemoved = get<2>((node->vinfo).front()) == 0;

  //check if was not updated by other threads meanwhile(add-wins)
  bool notConcurrentlyUpdated = get<1>((node->vinfo).front()) == observedLast;

  if(notConcurrentlyRemoved && notConcurrentlyUpdated){
      get<2>((node->vinfo).front()) = vid;
  }
}

void strongRemoveHelper(T val, int vid){
    //just need to check if node is != NULL and if removed is == 0
    node<T>* n = searchNode(root, val);
    if(n != NULL){
      if(get<2>((n->vinfo).front()) == 0){
        get<2>((n->vinfo).front()) = vid;
      }
    }
}

void readSet(node<T> * leaf){
    if(leaf == NULL) return;
    if(leaf!=NULL) {
      m.lock();
      cout << leaf->val << "\n";
      m.unlock();
    }

    readSet(leaf->left);
    readSet(leaf->right);
  }

  int globalCount(node<T>* node){
    if(node == NULL) return 0;
    else return globalCount(node->left) + globalCount(node->right) + 1;
  }

  void deleteTree(node<T>* leaf) {
    if(leaf==NULL) return;

    deleteTree(leaf->left);
    deleteTree(leaf->right);

    delete leaf;
  }

public:
  orSet(){
    root=NULL;
    gvid = 0;
  }

  void init(){
    localAdd.reset(new list<pair<T,node<T>*>>);
    localRemove.reset(new list<pair<node<T>*,int>>);
    lvid.reset(new int(gvid));
  }

  bool weakLookup(T val){
    for(auto pair: *localAdd){
      if(pair.first == val) return true;
    }

    m.lock();
    node<T>* n = searchNode(root, val);
    if(n == NULL) {
        m.unlock();
        return false;
    }
    list<std::tuple<int,int,int>> vinfo (n->vinfo);
    m.unlock();

    for(auto v : vinfo){
      if(get<2>(v) == 0 && get<0>(v) <= *lvid) return true;
      else if(get<0>(v) <= *lvid && *lvid < get<2>(v)) return true; //node could be valid in local version
    }

    return false;
  }

  bool strongLookup(T val){
    for(auto pair: *localAdd){
      if(pair.first == val) return true;
    }

    m.lock();
    node<T>* n = searchNode(root, val);
    if(n == NULL) {
        m.unlock();
        return false;
    }
    list<std::tuple<int,int,int>> vinfo (n->vinfo);
    m.unlock();

    for(auto v : vinfo){
      if(get<2>(v) == 0) return true;
    }

    return false;
  }

void weakAdd(T val){
    //finding an estimated position for the insertion
    m.lock();
    node<T>* parent = findParentNode(root, val, root);
    m.unlock();
    (*localAdd).push_front(make_pair(val, parent));
  }

  void strongAdd(T val){
      m.lock();
      strongAddHelper(val, gvid);
      m.unlock();
  }

  void weakRemove(T val){
    m.lock();
    node<T>* n = searchNode(root, val);

    if(n != NULL && (get<2>((n->vinfo).front())==0)){ //only remove if we can observe it
      int last = get<1>((n->vinfo).front());
      (*localRemove).push_front(make_pair(n,last));
    }
    m.unlock();

    removeFromLocalAdded(val);
  }

    void strongRemove(T val){
      m.lock();
      strongRemoveHelper(val, gvid);
      m.unlock();
  }

  void merge(){
    m.lock();
    int newVid = gvid+1;

    //merge weak adds
    for(auto pair: *localAdd){
        weakAddHelper(pair.first, pair.second, newVid);
    }
    (*localAdd).clear();

    //merge weak removes
    for(auto pair: *localRemove){
        weakRemoveHelper(pair.first, pair.second, newVid);
    }
    (*localRemove).clear();

    gvid = newVid;
    *lvid = gvid;
    m.unlock();
  }

////////////////////////////FOR DEBUGGING/BENCHMARK PURPOSES
  void readSet(){
    readSet(root);
  }

  int globalCountMdt(){
    return globalCount(root);
  }

  void deleteTree(){
    deleteTree(root);
  }

  void insertAtRoot(T val, T vid){
    if(root!=NULL) insert(val, root, vid);
    else{
      root=new node<T>;
      root->val=val;
      root->left=NULL;
      root->right=NULL;

      (root->vinfo).push_front(make_tuple(vid,vid,0));
    }
  }

  void reset(){
    root=NULL;
    gvid = 0;
    deleteTree();
  }
};

orSet<int> mdt;
vector<int> NTHREADS;
int SYNCFREQ [8] = {1,8,64,128,256,512,4096,32768};
std::atomic<int> threadCount;
void work(int syncFreqIndex, int operationsPerThread){
  threadCount++;
  int localCount = threadCount;
  int startNumber = operationsPerThread * (localCount-1);
  int endNumber = startNumber + operationsPerThread;
  mdt.init();
  mdt.merge();

  for (int i=startNumber; i < endNumber; i++){
    if(i%SYNCFREQ[syncFreqIndex] == 1){
      mdt.merge();
    }

    if(i%10==2){
      mdt.weakRemove(i-1);
    }

    mdt.weakAdd(i);

    if(i%5000 == 0){
      mdt.strongLookup(LOOP/2);
    }

    if(i%SYNCFREQ[syncFreqIndex] == 0){
      mdt.merge();
    }
  }

  mdt.merge();
}

void benchmarkPerFreq(int syncFreqIndex){
    using namespace std::chrono;
    for(int k = 0; k < NTHREADS.size(); k++){
      std::list<double> times;
      std::list<double> elementCount;
      std::list<double> throughs;

      for(int i= 0; i< BENCH_RUNS; i++){

        //ensure balancing on the tree
        for(int i=0; i < LOOP; i+=500){
            int right = LOOP/2 + i;
            int left = LOOP/2 - i;

            if(right < LOOP) {
               mdt.insertAtRoot(right, 0);
            }
            if(left > 0 && left!=LOOP/2) {
              mdt.insertAtRoot(left, 0);
            }
        }

        steady_clock::time_point t1 = steady_clock::now();

        boost::thread_group threads;
        int operationsPerThread = LOOP/NTHREADS[k];
        for (int a=0; a < NTHREADS[k]; a++){
          threads.create_thread(boost::bind(work, boost::cref(syncFreqIndex), boost::cref(operationsPerThread)));
        }

        threads.join_all();

        steady_clock::time_point t2 = steady_clock::now();
        threadCount=0;

        duration<double> ti = duration_cast<duration<double>>(t2 - t1);

        times.push_back(ti.count());
        // cout << "TIME: " << ti.count() << endl;

        int globalCount = mdt.globalCountMdt() * NTHREADS[k];

        elementCount.push_back(mdt.globalCountMdt());
        throughs.push_back(LOOP/ti.count());

        mdt.reset();
      }

      double sumTimes = 0;
      for(double t: times){
        sumTimes += t;
      }

      double finalTime = sumTimes/times.size();
      cout << fixed;

      double sumCounters = 0;
      for(double c: elementCount){
        sumCounters += c;
      }
      double finalElemCount = (int)(sumCounters/elementCount.size());

      double sumThroughs = 0;
      for(double th: throughs){
        sumThroughs += th;
      }
      double finalThroughs = sumThroughs/throughs.size();

      // cout << (int)finalElemCount << "," << (int)finalThroughs << "," << NTHREADS[k] << endl;
      cout << NTHREADS[k] << "," << (int)finalThroughs << endl;
    }
}

int main(int argc, char** argv){
    for(int i=1; i<argc; i++){
      NTHREADS.push_back(atoi(argv[i]));
    }

    for(int i=0; i<8;i++){
        cout << "***************SYNCFREQ: " << SYNCFREQ[i] << " ***************" << endl;
        benchmarkPerFreq(i);
        cout << endl;
    }

    return 0;
}


#include <algorithm>
#include <bitset>
// #include <boost/algorithm/string/replace.hpp>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>

using namespace std;

#define FOR(i, j, n) for (int i = j; i < n; i++)
#define OUT(s) cout << s << endl;
#define IN(a) cin >> a;
#define PACK(a, n)                                                             \
  for (int i = 0; i < n; i++)                                                  \
    cin >> a[i];
#define LIST(a, n)                                                             \
  for (int i = 0; i < n; i++)                                                  \
    cout << i << ": " << a[i] << endl;
#define PP(label, obj) cout << "[" << label << "] " << obj << endl;

int main() {
  int A, B;
  IN(A >> B);

  OUT(max(max(A + B, A - B), A * B));
}


#include <stan/math/prim.hpp>
#include <test/unit/math/prim/prob/vector_rng_test_helper.hpp>
#include <test/unit/math/prim/prob/util.hpp>
#include <boost/math/distributions.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <gtest/gtest.h>
#include <limits>
#include <vector>

class SkewNormalTestRig : public VectorRealRNGTestRig {
 public:
  SkewNormalTestRig()
      : VectorRealRNGTestRig(
            10000, 10, {-2.5, -1.7, -0.1, 0.0, 2.0}, {-3, -2, -1, 0, 2, 6}, {},
            {}, {0.1, 1.0, 2.5, 4.0}, {1, 2, 3, 4}, {-2.7, -1.5, -0.5, 0.0},
            {-3, -2, -1, 0}, {-2.0, -1.0, -0.5, 0.0, 0.7, 0.5},
            {-2, -1, 0, 1, 2}, {}, {}) {}

  template <typename T1, typename T2, typename T3, typename T_rng>
  auto generate_samples(const T1& mu, const T2& sigma, const T3& alpha,
                        T_rng& rng) const {
    return stan::math::skew_normal_rng(mu, sigma, alpha, rng);
  }

  std::vector<double> generate_quantiles(double mu, double sigma,
                                         double alpha) const {
    std::vector<double> quantiles;
    double K = stan::math::round(2 * std::pow(N_, 0.4));
    boost::math::skew_normal_distribution<> dist(mu, sigma, alpha);

    for (int i = 1; i < K; ++i) {
      double frac = i / K;
      quantiles.push_back(quantile(dist, frac));
    }
    quantiles.push_back(std::numeric_limits<double>::max());

    return quantiles;
  }
};

TEST(ProbDistributionsSkewNormal, errorCheck) {
  check_dist_throws_all_types(SkewNormalTestRig());
}

TEST(ProbDistributionsSkewNormal, distributionTest) {
  check_quantiles_real_real_real(SkewNormalTestRig());
}

TEST(ProbDistributionsSkewNormal, error_check) {
  boost::random::mt19937 rng;
  EXPECT_NO_THROW(stan::math::skew_normal_rng(10.0, 2.0, 1.0, rng));

  EXPECT_THROW(stan::math::skew_normal_rng(10.0, -2.0, 1.0, rng),
               std::domain_error);
  EXPECT_THROW(stan::math::skew_normal_rng(stan::math::positive_infinity(), 2.0,
                                           1.0, rng),
               std::domain_error);
  EXPECT_THROW(
      stan::math::skew_normal_rng(10, 2, stan::math::positive_infinity(), rng),
      std::domain_error);
}

TEST(ProbDistributionsSkewNormal, chiSquareGoodnessFitTest) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  std::vector<double> samples;
  for (int i = 0; i < N; ++i) {
    samples.push_back(stan::math::skew_normal_rng(3.0, 2.0, 1.0, rng));
  }

  // Generate quantiles from boost's skew normal distribution
  boost::math::skew_normal_distribution<> dist(3.0, 2.0, 1.0);
  std::vector<double> quantiles;
  for (int i = 1; i < K; ++i) {
    double frac = static_cast<double>(i) / K;
    quantiles.push_back(quantile(dist, frac));
  }
  quantiles.push_back(std::numeric_limits<double>::max());

  // Assert that they match
  assert_matches_quantiles(samples, quantiles, 1e-6);
}


#include <stan/math/prim/meta.hpp>
#include <gtest/gtest.h>
#include <Eigen/Dense>
#include <vector>

TEST(MetaTraitsPrimScal, size_of) {
  using stan::size_of;
  double x1 = 2;
  EXPECT_EQ(1U, size_of(x1));
}

TEST(MetaTraitsPrimArr, size_of) {
  using stan::size_of;

  std::vector<double> x2(3);
  EXPECT_EQ(3U, size_of(x2));

  std::vector<std::vector<double>> x3(6);
  EXPECT_EQ(6U, size_of(x3));

  std::vector<Eigen::MatrixXd> x4(8);
  EXPECT_EQ(8U, size_of(x4));
}


#include <catch.hpp>

#include <arlib/multi_predecessor_map.hpp>
#include <test_types.hpp>
#include <utils.hpp>

#include <boost/graph/properties.hpp>

#include <arlib/graph_types.hpp>
#include <arlib/graph_utils.hpp>

TEST_CASE("multi_predecessor_map interface") {
  using namespace boost;
  using namespace arlib::test;

  auto G = arlib::read_graph_from_string<Graph>(std::string(graph_gr_esx));
  auto pmap = arlib::multi_predecessor_map<Vertex>{};

  BOOST_CONCEPT_ASSERT((ReadablePropertyMapConcept<decltype(pmap), Vertex>));
  for (auto [first, last] = vertices(G); first != last; ++first) {
    REQUIRE(get(pmap, *first).empty());
  }

  auto v = *vertices(G).first;
  auto &pred = get(pmap, v);
  for (auto [first, last] = in_edges(v, G); first != last; ++first) {
    auto u = source(*first, G);
    auto k_th = std::distance(in_edges(v, G).first, first);
    pred.insert({k_th, u});
  }

  REQUIRE(pred.size() == in_degree(v, G));
  graph_traits<Graph>::in_edge_iterator first, last;
  for (std::tie(first, last) = in_edges(v, G); first != last; ++first) {
    REQUIRE(
        std::find_if(pred.begin(), pred.end(), [&first, &G](auto const &entry) {
          return entry.second == source(*first, G);
        }) != pred.end());
  }
}

void fill_kth_predecessors(
    int kth, arlib::test::Graph const &G,
    arlib::multi_predecessor_map<arlib::test::Vertex> &predecessors,
    std::vector<arlib::test::Edge> const &edges) {
  using boost::get;
  for (auto const &e : edges) {
    auto u = boost::source(e, G);
    auto v = boost::target(e, G);
    get(predecessors, v).insert({kth, u});
  }
}

TEST_CASE("to_paths builds correct vector<Path> from multi_predecessor_map") {
  using namespace boost;
  using namespace arlib::test;

  std::pair<int, int> edge_list[] = {{0, 1}, {1, 3}, {0, 2}, {2, 3},
                                     {3, 4}, {4, 6}, {3, 5}, {5, 6}};
  int weights[] = {1, 1, 3, 3, 1, 1, 3, 3};
  auto G = Graph{edge_list,
                 edge_list + sizeof(edge_list) / sizeof(std::pair<int, int>),
                 weights, 7};

  auto pmap = arlib::multi_predecessor_map<Vertex>{};
  fill_kth_predecessors(1, G, pmap,
                        {edge(0, 1, G).first, edge(1, 3, G).first,
                         edge(3, 4, G).first, edge(4, 6, G).first});
  fill_kth_predecessors(2, G, pmap,
                        {edge(0, 2, G).first, edge(2, 3, G).first,
                         edge(3, 5, G).first, edge(5, 6, G).first});

  auto paths = arlib::to_paths(G, pmap, 0lu, 6lu);

  REQUIRE(paths.size() == 2);

  auto lengths = std::vector<Length>{4, 12};
  for (auto l : lengths) {
    REQUIRE(std::any_of(paths.begin(), paths.end(),
                        [l](auto const &p) { return p.length() == l; }));
  }
}


/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include "poses-precomp.h"	// Precompiled headers
//
#include <mrpt/math/ops_containers.h>  // maximum()
#include <mrpt/poses/CPoint3D.h>
#include <mrpt/poses/CPointPDFParticles.h>
#include <mrpt/poses/CPose3D.h>
#include <mrpt/serialization/CArchive.h>
#include <mrpt/system/os.h>

#include <Eigen/Dense>

using namespace mrpt;
using namespace mrpt::poses;
using namespace mrpt::math;
using namespace mrpt::system;

IMPLEMENTS_SERIALIZABLE(CPointPDFParticles, CPointPDF, mrpt::poses)

CPointPDFParticles::CPointPDFParticles(size_t numParticles)
{
	setSize(numParticles);
}

/** Clear all the particles (free memory) */
void CPointPDFParticles::clear() { setSize(0); }
/*---------------------------------------------------------------
		setSize
  ---------------------------------------------------------------*/
void CPointPDFParticles::setSize(
	size_t numberParticles, const mrpt::math::TPoint3Df& defaultValue)
{
	// Free old particles: automatic via smart ptr
	m_particles.resize(numberParticles);
	for (auto& it : m_particles)
	{
		it.log_w = 0;
		it.d.reset(new TPoint3Df(defaultValue));
	}
}

/*---------------------------------------------------------------
						getMean
  Returns an estimate of the pose, (the mean, or mathematical expectation of the
 PDF)
 ---------------------------------------------------------------*/
void CPointPDFParticles::getMean(CPoint3D& p) const
{
	MRPT_START
	if (m_particles.empty())
		THROW_EXCEPTION("Cannot compute mean since there are zero particles.");

	CParticleList::const_iterator it;
	double sumW = 0;
	double x = 0, y = 0, z = 0;
	for (it = m_particles.begin(); it != m_particles.end(); it++)
	{
		const double w = exp(it->log_w);
		x += it->d->x * w;
		y += it->d->y * w;
		z += it->d->z * w;
		sumW += w;
	}

	ASSERT_(sumW != 0);

	sumW = 1.0 / sumW;

	p.x(x * sumW);
	p.y(y * sumW);
	p.z(z * sumW);

	MRPT_END
}

std::tuple<CMatrixDouble33, CPoint3D> CPointPDFParticles::getCovarianceAndMean()
	const
{
	MRPT_START

	CPoint3D mean;
	CMatrixDouble33 cov;

	getMean(mean);
	cov.setZero();

	size_t i, n = m_particles.size();
	double var_x = 0, var_y = 0, var_p = 0, var_xy = 0, var_xp = 0, var_yp = 0;

	double lin_w_sum = 0;

	for (i = 0; i < n; i++)
		lin_w_sum += exp(m_particles[i].log_w);
	if (lin_w_sum == 0) lin_w_sum = 1;

	for (i = 0; i < n; i++)
	{
		double w = exp(m_particles[i].log_w) / lin_w_sum;

		double err_x = m_particles[i].d->x - mean.x();
		double err_y = m_particles[i].d->y - mean.y();
		double err_phi = m_particles[i].d->z - mean.z();

		var_x += square(err_x) * w;
		var_y += square(err_y) * w;
		var_p += square(err_phi) * w;
		var_xy += err_x * err_y * w;
		var_xp += err_x * err_phi * w;
		var_yp += err_y * err_phi * w;
	}

	if (n >= 2)
	{
		// Unbiased estimation of variance:
		cov(0, 0) = var_x;
		cov(1, 1) = var_y;
		cov(2, 2) = var_p;

		cov(1, 0) = cov(0, 1) = var_xy;
		cov(2, 0) = cov(0, 2) = var_xp;
		cov(1, 2) = cov(2, 1) = var_yp;
	}

	return {cov, mean};
	MRPT_END
}

uint8_t CPointPDFParticles::serializeGetVersion() const { return 0; }
void CPointPDFParticles::serializeTo(mrpt::serialization::CArchive& out) const
{
	uint32_t N = size();
	out << N;

	for (const auto& m_particle : m_particles)
		out << m_particle.log_w << m_particle.d->x << m_particle.d->y
			<< m_particle.d->z;
}

void CPointPDFParticles::serializeFrom(
	mrpt::serialization::CArchive& in, uint8_t version)
{
	switch (version)
	{
		case 0:
		{
			uint32_t N;
			in >> N;
			setSize(N);

			for (auto& m_particle : m_particles)
				in >> m_particle.log_w >> m_particle.d->x >> m_particle.d->y >>
					m_particle.d->z;
		}
		break;
		default: MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(version);
	};
}

void CPointPDFParticles::copyFrom(const CPointPDF& o)
{
	if (this == &o) return;	 // It may be used sometimes

	// Convert to samples:
	THROW_EXCEPTION("NO");
}

/*---------------------------------------------------------------

  ---------------------------------------------------------------*/
bool CPointPDFParticles::saveToTextFile(const std::string& file) const
{
	MRPT_START

	FILE* f = os::fopen(file.c_str(), "wt");
	if (!f) return false;

	size_t i, N = m_particles.size();
	for (i = 0; i < N; i++)
		os::fprintf(
			f, "%f %f %f %e\n", m_particles[i].d->x, m_particles[i].d->y,
			m_particles[i].d->z, m_particles[i].log_w);

	os::fclose(f);
	return true;
	MRPT_END
}

/*---------------------------------------------------------------
						changeCoordinatesReference
 ---------------------------------------------------------------*/
void CPointPDFParticles::changeCoordinatesReference(
	const CPose3D& newReferenceBase)
{
	TPoint3D pt;
	for (auto& m_particle : m_particles)
	{
		newReferenceBase.composePoint(
			m_particle.d->x, m_particle.d->y, m_particle.d->z,	// In
			pt.x, pt.y, pt.z  // Out
		);
		m_particle.d->x = d2f(pt.x);
		m_particle.d->y = d2f(pt.y);
		m_particle.d->z = d2f(pt.z);
	}
}

/*---------------------------------------------------------------
						computeKurtosis
 ---------------------------------------------------------------*/
double CPointPDFParticles::computeKurtosis()
{
	MRPT_START

	// kurtosis = \mu^4 / (\sigma^2) -3
	Eigen::Vector3d kurts, mu4, m, var;
	kurts.fill(0);
	mu4.fill(0);
	m.fill(0);
	var.fill(0);

	// Means:
	for (auto& m_particle : m_particles)
	{
		m[0] += m_particle.d->x;
		m[1] += m_particle.d->y;
		m[2] += m_particle.d->z;
	}
	m *= 1.0 / m_particles.size();

	// variances:
	for (auto& m_particle : m_particles)
	{
		var[0] += square(m_particle.d->x - m[0]);
		var[1] += square(m_particle.d->y - m[1]);
		var[2] += square(m_particle.d->z - m[2]);
	}
	var *= 1.0 / m_particles.size();
	var[0] = square(var[0]);
	var[1] = square(var[1]);
	var[2] = square(var[2]);

	// Moment:
	for (auto& m_particle : m_particles)
	{
		mu4[0] += pow(m_particle.d->x - m[0], 4.0);
		mu4[1] += pow(m_particle.d->y - m[1], 4.0);
		mu4[2] += pow(m_particle.d->z - m[2], 4.0);
	}
	mu4 *= 1.0 / m_particles.size();

	// Kurtosis's
	kurts.array() = mu4.array() / var.array();

	return math::maximum(kurts);

	MRPT_END
}

/*---------------------------------------------------------------
					drawSingleSample
  ---------------------------------------------------------------*/
void CPointPDFParticles::drawSingleSample([
	[maybe_unused]] CPoint3D& outSample) const
{
	THROW_EXCEPTION("TO DO!");
}

/*---------------------------------------------------------------
					bayesianFusion
 ---------------------------------------------------------------*/
void CPointPDFParticles::bayesianFusion(
	[[maybe_unused]] const CPointPDF& p1_,
	[[maybe_unused]] const CPointPDF& p2_,
	[[maybe_unused]] const double minMahalanobisDistToDrop)
{
	MRPT_START

	THROW_EXCEPTION("TODO!!!");

	MRPT_END
}


// Copyright (C) 2013 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:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of The Regents or University of California 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 HOLDERS 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 of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

#include "theia/sfm/transformation/gdls_similarity_transform.h"

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

#include "theia/alignment/alignment.h"
#include "theia/sfm/pose/dls_impl.h"
#include "theia/util/random.h"

namespace theia {

using dls_impl::CreateMacaulayMatrix;
using dls_impl::ExtractJacobianCoefficients;
using dls_impl::LeftMultiplyMatrix;
using Eigen::Matrix;
using Eigen::Matrix3d;
using Eigen::Matrix4d;
using Eigen::MatrixXd;
using Eigen::Quaterniond;
using Eigen::Vector3d;

// This implementation is based off of the DLS PnP implementation. The general
// approach is to first rewrite the reprojection constraint (i.e., cost
// function) such that all unknowns appear linearly in terms of the rotation
// parameters (which are 3 parameters in the Cayley-Gibss-Rodriguez
// formulation). Then we create a system of equations from the jacobian of the
// cost function, and solve these equations via a Macaulay matrix to obtain the
// roots (i.e., the 3 parameters of rotation). The translation and scale can
// then be obtained through back-substitution.
void GdlsSimilarityTransform(const std::vector<Vector3d>& ray_origin,
                             const std::vector<Vector3d>& ray_direction,
                             const std::vector<Vector3d>& world_point,
                             std::vector<Quaterniond>* solution_rotation,
                             std::vector<Vector3d>* solution_translation,
                             std::vector<double>* solution_scale) {
  CHECK_GE(ray_direction.size(), 4);

  const int num_correspondences = ray_direction.size();
  // The bottom-right symmetric block matrix of inverse(A^T * A). This is the
  // generalized version of Matrix H from Eq. 17 in the Appendix of the gDLS
  // paper (note that term appears exactly in the bottom right 3x3 of this
  // matrix).
  Matrix4d h_inverse = Matrix4d::Zero();
  for (int i = 0; i < num_correspondences; i++) {
    h_inverse(0, 0) = h_inverse(0, 0) + ray_origin[i].squaredNorm() -
                      ray_origin[i].dot(ray_direction[i]) *
                          ray_origin[i].dot(ray_direction[i]);
    Vector3d temp_term =
        -ray_origin[i] + ray_origin[i].dot(ray_direction[i]) * ray_direction[i];
    h_inverse.block<3, 1>(1, 0) = h_inverse.block<3, 1>(1, 0) + temp_term;
    h_inverse.block<1, 3>(0, 1) =
        h_inverse.block<1, 3>(0, 1) + temp_term.transpose();
    h_inverse.block<3, 3>(1, 1) =
        h_inverse.block<3, 3>(1, 1) + Matrix3d::Identity() -
        (ray_direction[i] * ray_direction[i].transpose());
  }
  const Matrix4d h_matrix = h_inverse.inverse();

  // This is the translation and scale parameterized by the 9 entries of the
  // rotation matrix. The first row is the scale and rows 2 - 4 are the
  // translation.
  Matrix<double, 4, 9> sv_helper = Matrix<double, 4, 9>::Zero();
  for (int i = 0; i < num_correspondences; i++) {
    // Scale factor.
    sv_helper.row(0) =
        sv_helper.row(0) +
        (ray_origin[i].transpose() -
         ray_origin[i].dot(ray_direction[i]) * ray_direction[i].transpose()) *
            LeftMultiplyMatrix(world_point[i]);

    // Translation factor.
    sv_helper.block<3, 9>(1, 0) =
        sv_helper.block<3, 9>(1, 0) +
        (ray_direction[i] * ray_direction[i].transpose() -
         Matrix3d::Identity()) *
            LeftMultiplyMatrix(world_point[i]);
  }

  sv_helper = h_matrix * sv_helper;
  const Matrix<double, 1, 9>& scale_factor = sv_helper.row(0);
  const Matrix<double, 3, 9>& translation_factor = sv_helper.block<3, 9>(1, 0);

  // Compute the cost function C' of Eq. 15 in gDLS paper. This is a factorized
  // version where the rotation matrix parameters have been pulled out. The
  // entries to this equation are the coefficients to the cost function which is
  // a quartic in the rotation parameters.
  Matrix<double, 9, 9> ls_cost_coefficients = Matrix<double, 9, 9>::Zero();
  for (int i = 0; i < num_correspondences; i++) {
    const Matrix<double, 3, 9> cost_coeff_term =
        (ray_direction[i] * ray_direction[i].transpose() -
         Matrix3d::Identity()) *
        (LeftMultiplyMatrix(world_point[i]) - ray_origin[i] * scale_factor +
         translation_factor);
    ls_cost_coefficients =
        ls_cost_coefficients + cost_coeff_term.transpose() * cost_coeff_term;
  }

  // Extract the coefficients of the jacobian (Eq. 16) from the
  // ls_cost_coefficients matrix. The jacobian represent 3 monomials in the
  // rotation parameters. Each entry of the jacobian will be 0 at the roots of
  // the polynomial, so we can arrange a system of polynomials from these
  // equations.
  double f1_coeff[20];
  double f2_coeff[20];
  double f3_coeff[20];
  ExtractJacobianCoefficients(
      ls_cost_coefficients, f1_coeff, f2_coeff, f3_coeff);

  // We create one equation with random terms that is generally non-zero at the
  // roots of our system.
  const Eigen::Vector4d rand_vec = 100.0 * Eigen::Vector4d::Random();
  const double macaulay_term[4] = {
      rand_vec(0), rand_vec(1), rand_vec(2), rand_vec(3)};

  // Create Macaulay matrix that will be used to solve our polynonomial system.
  const MatrixXd& macaulay_matrix =
      CreateMacaulayMatrix(f1_coeff, f2_coeff, f3_coeff, macaulay_term);

  // Via the Schur complement trick, the top-left of the Macaulay matrix
  // contains a multiplication matrix whose eigenvectors correspond to solutions
  // to our system of equations.
  const MatrixXd solution_polynomial =
      macaulay_matrix.block<27, 27>(0, 0) -
      (macaulay_matrix.block<27, 93>(0, 27) *
       macaulay_matrix.block<93, 93>(27, 27).partialPivLu().solve(
           macaulay_matrix.block<93, 27>(27, 0)));

  // Extract eigenvectors of the solution polynomial to obtain the roots which
  // are contained in the entries of the eigenvectors.
  const Eigen::EigenSolver<MatrixXd> eigen_solver(solution_polynomial);

  // Many of the eigenvectors will contain complex solutions so we must filter
  // them to find the real solutions.
  const auto eigen_vectors = eigen_solver.eigenvectors();
  for (int i = 0; i < 27; i++) {
    // The first entry of the eigenvector should equal 1 according to our
    // polynomial, so we must divide each solution by the first entry.
    std::complex<double> s1 = eigen_vectors(9, i) / eigen_vectors(0, i);
    std::complex<double> s2 = eigen_vectors(3, i) / eigen_vectors(0, i);
    std::complex<double> s3 = eigen_vectors(1, i) / eigen_vectors(0, i);

    // If the rotation solutions are real, treat this as a valid candidate
    // rotation.
    const double kEpsilon = 1e-6;
    if (fabs(s1.imag()) < kEpsilon && fabs(s2.imag()) < kEpsilon &&
        fabs(s3.imag()) < kEpsilon) {
      // Compute the rotation (which is the transpose rotation of our solution)
      // and translation.
      Quaterniond soln_rotation(1.0, s1.real(), s2.real(), s3.real());
      soln_rotation = soln_rotation.inverse().normalized();

      const Matrix3d rot_mat = soln_rotation.inverse().toRotationMatrix();
      const Eigen::Map<const Matrix<double, 9, 1> > rot_vec(rot_mat.data());
      const Vector3d soln_translation = translation_factor * rot_vec;
      const double soln_scale = scale_factor * rot_vec;

      // TODO(cmsweeney): evaluate cost function and return it as an output
      // variable.

      // Check that all points are in front of the camera. Discard the solution
      // if this is not the case.
      bool all_points_in_front_of_camera = true;

      for (int j = 0; j < num_correspondences; j++) {
        const Vector3d transformed_point = soln_rotation * world_point[j] +
                                           soln_translation -
                                           soln_scale * ray_origin[j];

        // Find the rotation that puts the image ray at [0, 0, 1] i.e. looking
        // straightforward from the camera.
        const Quaterniond unrot =
            Quaterniond::FromTwoVectors(ray_direction[j], Vector3d(0, 0, 1));

        // Rotate the transformed point and check if the z coordinate is
        // negative. This will indicate if the point is projected behind the
        // camera.
        const Vector3d rotated_projection = unrot * transformed_point;
        if (rotated_projection.z() < 0) {
          all_points_in_front_of_camera = false;
          break;
        }
      }

      if (all_points_in_front_of_camera) {
        solution_rotation->push_back(soln_rotation);
        solution_translation->push_back(soln_translation);
        solution_scale->push_back(soln_scale);
      }
    }
  }
}

}  // namespace theia


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

#ifndef ALLIUM_LA_LOCAL_VECTOR_HPP
#define ALLIUM_LA_LOCAL_VECTOR_HPP

#include <Eigen/Core>
#include "vector_trait.hpp"
#include <allium/util/extern.hpp>

namespace allium {

  /**
   @brief Implements a (mathematical) vector in the mathematical sense, which is
   stored locally, i.e., not distributed across different processors.
  */
  template <typename N>
  class LocalVector
    : public VectorTrait<LocalVector<N>, N>
  {
    public:
      using Number = N;
      using Real = real_part_t<Number>;
      using EigenVector = Eigen::Matrix<N, Eigen::Dynamic, 1>;

      explicit LocalVector(size_t rows)
        : m_storage(rows)
      {}

      explicit LocalVector(std::initializer_list<N> entries)
        : m_storage(entries.size())
      {
        size_t i_entry = 0;
        for (auto entry : entries) {
          m_storage(i_entry) = entry;
          ++i_entry;
        }
      }

      explicit LocalVector(const EigenVector& v)
        : m_storage(v)
      {}

      /**
        Return a vector whose entries are all equal.

        @param [in] x The value of the entries.
      */
      static LocalVector constant(size_t rows, Number x) {
        LocalVector v(rows);
        for (size_t i=0; i < rows; ++i) {
          v[i] = x;
        }
        return v;
      }

      LocalVector& operator+= (const LocalVector& rhs);
      LocalVector& operator*= (Number rhs);

      Number dot(const LocalVector& rhs) const {
        return rhs.m_storage.dot(m_storage);
      }

      Number& operator[] (size_t i_element) {
        return m_storage(i_element);
      }
      Number operator[] (size_t i_element) const {
        return (*const_cast<LocalVector*>(this))[i_element];
      }

      /**
        The length of the vector.
      */
      size_t rows() const { return m_storage.rows(); }

      [[deprecated ("Use the 'rows' method instead.")]]
        size_t nrows() const { return rows(); }

      const EigenVector& native() const { return m_storage; }
    private:
      EigenVector m_storage;
  };

  template <typename N>
  LocalVector<N>& LocalVector<N>::operator+= (const LocalVector& rhs)
  {
    m_storage += rhs.m_storage;
    return *this;
  }

  template <typename N>
  LocalVector<N>& LocalVector<N>::operator*= (Number rhs)
  {
    m_storage *= rhs;
    return *this;
  }

  template <typename N>
  LocalVector<N> operator* (N s, const LocalVector<N>& v) {
    LocalVector<N> w = v;
    w *= s;
    return w;
  }

  template <typename N>
  std::ostream& operator<< (std::ostream& os, const LocalVector<N>& v)
  {
    bool first = true;
    for (size_t i = 0; i < v.rows(); ++i) {
      if (first) first = false;
      else os << " ";

      os << v[i];
    }

    return os;
  }

  #define ALLIUM_LOCAL_VECTOR_DECL(extern, N) \
    extern template class LocalVector<N>;
  ALLIUM_EXTERN_N(ALLIUM_LOCAL_VECTOR_DECL)
}

#endif


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
#include <Expected.hpp>
using armadilloJava::Expected;

#include <iostream>
using std::cout;
using std::endl;

#include <algorithm>
using std::min;

#include <utility>
using std::pair;

#include <armadillo>
using arma::Mat;

#include <InputClass.hpp>
using armadilloJava::InputClass;

#include <Input.hpp>
using armadilloJava::Input;

namespace armadilloJava {
  class ExpectedInPlaceGenMatGenMat : public Expected {
    public:
      ExpectedInPlaceGenMatGenMat() {
        cout << "Compute ExpectedInPlaceGenMatGenMat(): " << endl;

          vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
            InputClass::GenMat,
            InputClass::GenMat
          });

          for (vector<pair<string, void*>> input : inputs) {
            _fileSuffix = "";

            int n = 0;
            for (pair<string, void*> value : input) {
              switch (n) {
                case 0:
                  _fileSuffix += value.first;
                  _genMatA = *static_cast<Mat<double>*>(value.second);
                  break;
                case 1:
                  _fileSuffix += "," + value.first;
                  _genMatB = *static_cast<Mat<double>*>(value.second);
                  break;
              }
              ++n;
            }

            cout << "Using input: " << _fileSuffix << endl;

            _copyOfGenMatA = _genMatA;
            _copyOfGenMatB = _genMatB;

            expectedMatInPlaceEqual();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatInPlacePlus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatInPlaceMinus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatInPlaceTimes();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatInPlaceElemTimes();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatInPlaceElemDivide();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatSwap();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_colEqual();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_colPlus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_colMinus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_colElemTimes();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_colElemDivide();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_rowEqual();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_rowPlus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_rowMinus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_rowElemTimes();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatEach_rowElemDivide();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatDiagEqual();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatDiagPlus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatDiagMinus();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatDiagElemTimes();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatDiagElemDivide();

            _genMatA = _copyOfGenMatA;
            _genMatB = _copyOfGenMatB;
            expectedMatCopy_size();
          }

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

    protected:
      Mat<double> _genMatA;
      Mat<double> _copyOfGenMatA;

      Mat<double> _genMatB;
      Mat<double> _copyOfGenMatB;

      void expectedMatInPlaceEqual() {
        cout << "- Compute expectedMatInPlaceEqual() ... ";

        _genMatA = _genMatB;
        save<double>("Mat.inPlaceEqual", _genMatA);

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

      void expectedMatInPlacePlus() {
        if(_genMatA.n_rows != _genMatB.n_rows || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatInPlace() ... ";

        _genMatA += _genMatB;
        save<double>("Mat.inPlacePlus", _genMatA);

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

      void expectedMatInPlaceMinus() {
        if(_genMatA.n_rows != _genMatB.n_rows || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatInPlaceMinus() ... ";

        _genMatA -= _genMatB;
        save<double>("Mat.inPlaceMinus", _genMatA);

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

      void expectedMatInPlaceTimes() {
        if(_genMatA.n_cols != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatInPlaceTimes() ... ";

        _genMatA *= _genMatB;
        save<double>("Mat.inPlaceTimes", _genMatA);

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

      void expectedMatInPlaceElemTimes() {
        if(_genMatA.n_rows != _genMatB.n_rows || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatInPlaceElemTimes() ... ";

        _genMatA %= _genMatB;
        save<double>("Mat.inPlaceElemTimes", _genMatA);

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

      void expectedMatInPlaceElemDivide() {
        if(_genMatA.n_rows != _genMatB.n_rows || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatInPlaceElemDivide() ... ";

        _genMatA /= _genMatB;
        save<double>("Mat.inPlaceElemDivide", _genMatA);

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

      void expectedMatSwap() {
        cout << "- Compute expectedMatSwap() ... ";

        _genMatA.swap(_genMatB);
        save<double>("Mat.swap", _genMatA);

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

      void expectedMatEach_colEqual() {
        if(!_genMatB.is_colvec() || _genMatA.n_rows != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatEach_colEqual() ... ";

        _genMatA.each_col() = _genMatB;
        save<double>("Mat.each_colEqual", _genMatA);

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

      void expectedMatEach_colPlus() {
        if(!_genMatB.is_colvec() || _genMatA.n_rows != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatEach_colPlus() ... ";

        _genMatA.each_col() += _genMatB;
        save<double>("Mat.each_colPlus", _genMatA);

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

      void expectedMatEach_colMinus() {
        if(!_genMatB.is_colvec() || _genMatA.n_rows != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatEach_colMinus() ... ";

        _genMatA.each_col() -= _genMatB;
        save<double>("Mat.each_colMinus", _genMatA);

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

      void expectedMatEach_colElemTimes() {
        if(!_genMatB.is_colvec() || _genMatA.n_rows != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatEach_colElemTimes() ... ";

        _genMatA.each_col() %= _genMatB;
        save<double>("Mat.each_colElemTimes", _genMatA);

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

      void expectedMatEach_colElemDivide() {
        if(!_genMatB.is_colvec() || _genMatA.n_rows != _genMatB.n_rows) {
          return;
        }

        cout << "- Compute expectedMatEach_colElemDivide() ... ";

        _genMatA.each_col() /= _genMatB;
        save<double>("Mat.each_colElemDivide", _genMatA);

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

      void expectedMatEach_rowEqual() {
        if(!_genMatB.is_rowvec() || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatEach_rowEqual() ... ";

        _genMatA.each_row() = _genMatB;
        save<double>("Mat.each_rowEqual", _genMatA);

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

      void expectedMatEach_rowPlus() {
        if(!_genMatB.is_rowvec() || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatEach_rowPlus() ... ";

        _genMatA.each_row() += _genMatB;
        save<double>("Mat.each_rowPlus", _genMatA);

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

      void expectedMatEach_rowMinus() {
        if(!_genMatB.is_rowvec() || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatEach_rowMinus() ... ";

        _genMatA.each_row() -= _genMatB;
        save<double>("Mat.each_rowMinus", _genMatA);

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

      void expectedMatEach_rowElemTimes() {
        if(!_genMatB.is_rowvec() || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatEach_rowElemTimes() ... ";

        _genMatA.each_row() %= _genMatB;
        save<double>("Mat.each_rowElemTimes", _genMatA);

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

      void expectedMatEach_rowElemDivide() {
        if(!_genMatB.is_rowvec() || _genMatA.n_cols != _genMatB.n_cols) {
          return;
        }

        cout << "- Compute expectedMatEach_rowElemDivide() ... ";

        _genMatA.each_row() /= _genMatB;
        save<double>("Mat.each_rowElemDivide", _genMatA);

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

      void expectedMatDiagEqual() {
        if(!_genMatB.is_vec() || _genMatB.n_elem != min(_genMatA.n_rows, _genMatA.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagEqual() ... ";

        _genMatA.diag() = _genMatB;
        save<double>("Mat.diagEqual", _genMatA);

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

      void expectedMatDiagPlus() {
        if(!_genMatB.is_vec() || _genMatB.n_elem != min(_genMatA.n_rows, _genMatA.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagPlus() ... ";

        _genMatA.diag() += _genMatB;
        save<double>("Mat.diagPlus", _genMatA);

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

      void expectedMatDiagMinus() {
        if(!_genMatB.is_vec() || _genMatB.n_elem != min(_genMatA.n_rows, _genMatA.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagMinus() ... ";

        _genMatA.diag() -= _genMatB;
        save<double>("Mat.diagMinus", _genMatA);

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

      void expectedMatDiagElemTimes() {
        if(!_genMatB.is_vec() || _genMatB.n_elem != min(_genMatA.n_rows, _genMatA.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagElemTimes() ... ";

        _genMatA.diag() %= _genMatB;
        save<double>("Mat.diagElemTimes", _genMatA);

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

      void expectedMatDiagElemDivide() {
        if(!_genMatB.is_vec() || _genMatB.n_elem != min(_genMatA.n_rows, _genMatA.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagElemDivide() ... ";

        _genMatA.diag() /= _genMatB;
        save<double>("Mat.diagElemDivide", _genMatA);

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

      void expectedMatCopy_size() {
        cout << "- Compute expectedMatCopy_size() ... ";

        _genMatA.copy_size(_genMatB);
        save<double>("Mat.copy_size", _genMatA);

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


// Copyright 2015-2018 Hans Dembinski
//
// Distributed under 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_HISTOGRAM_ACCUMULATORS_MEAN_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP

#include <boost/histogram/fwd.hpp>
#include <cstddef>
#include <type_traits>

namespace boost {
namespace histogram {
namespace accumulators {

/** Calculates mean and variance of sample.

  Uses Welfords's incremental algorithm to improve the numerical
  stability of mean and variance computation.
*/
template <class RealType>
class mean {
public:
  mean() = default;
  mean(const std::size_t n, const RealType& mean, const RealType& variance)
      : sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (sum_ - 1)) {}

  void operator()(const RealType& x) {
    sum_ += 1;
    const auto delta = x - mean_;
    mean_ += delta / sum_;
    sum_of_deltas_squared_ += delta * (x - mean_);
  }

  template <class T>
  mean& operator+=(const mean<T>& rhs) {
    const auto tmp = mean_ * sum_ + static_cast<RealType>(rhs.mean_ * rhs.sum_);
    sum_ += rhs.sum_;
    mean_ = tmp / sum_;
    sum_of_deltas_squared_ += static_cast<RealType>(rhs.sum_of_deltas_squared_);
    return *this;
  }

  mean& operator*=(const RealType& s) {
    mean_ *= s;
    sum_of_deltas_squared_ *= s * s;
    return *this;
  }

  template <class T>
  bool operator==(const mean<T>& rhs) const noexcept {
    return sum_ == rhs.sum_ && mean_ == rhs.mean_ &&
           sum_of_deltas_squared_ == rhs.sum_of_deltas_squared_;
  }

  template <class T>
  bool operator!=(const mean<T>& rhs) const noexcept {
    return !operator==(rhs);
  }

  std::size_t count() const noexcept { return sum_; }
  const RealType& value() const noexcept { return mean_; }
  RealType variance() const { return sum_of_deltas_squared_ / (sum_ - 1); }

  template <class Archive>
  void serialize(Archive&, unsigned /* version */);

private:
  std::size_t sum_ = 0;
  RealType mean_ = 0, sum_of_deltas_squared_ = 0;
};

} // namespace accumulators
} // namespace histogram
} // namespace boost

#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
/// Specialization for boost::histogram::accumulators::mean.
struct common_type<boost::histogram::accumulators::mean<T>,
                   boost::histogram::accumulators::mean<U>> {
  using type = boost::histogram::accumulators::mean<common_type_t<T, U>>;
};
} // namespace std
#endif

#endif


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

#include <boost/hana/assert.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/ext/std/integral_constant.hpp>
#include <boost/hana/functional/on.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>

#include <type_traits>
using namespace boost::hana;

namespace ns1 {
//! [comparable]
struct T;
struct U;
BOOST_HANA_CONSTANT_CHECK(type<T> == type<T>);
BOOST_HANA_CONSTANT_CHECK(type<T> != type<U>);
//! [comparable]
}

namespace ns2 {
//! [largest]
template <typename ...Types>
constexpr auto largest(Types ...types) {
    return maximum_by(less ^on^ sizeof_, make<Tuple>(types...));
}

template <int size>
struct storage { char s[size]; };

static_assert(
    largest(type<storage<1>>, type<storage<2>>, type<storage<3>>) == type<storage<3>>
, "");
//! [largest]
}

namespace ns22 {
//! [largest2]
// Note: tuple_t<T...> is equivalent to make<Tuple>(type<T>...)
template <typename ...T>
struct largest
    : decltype(maximum_by(less ^on^ sizeof_, tuple_t<T...>))
{ };

template <int size>
struct storage { char s[size]; };

static_assert(std::is_same<
    largest<storage<1>, storage<2>, storage<3>>::type,
    storage<3>
>{}, "");
//! [largest2]
}

namespace ns3 {
//! [decltype]
struct X { };
BOOST_HANA_CONSTANT_CHECK(type<X> == decltype_(X{}));
BOOST_HANA_CONSTANT_CHECK(type<int> == decltype_(1));
//! [decltype]
}

namespace ns4 {
//! [sizeof]
struct X { };
static_assert(sizeof_(type<X>) == sizeof(X), "");
static_assert(sizeof_(type<int>) == sizeof(int), "");
//! [sizeof]
}

namespace ns5 {
//! [template]
template <typename ...> struct f;
struct x;
struct y;

BOOST_HANA_CONSTANT_CHECK(template_<f>() == type<f<>>);
BOOST_HANA_CONSTANT_CHECK(template_<f>(type<x>) == type<f<x>>);
BOOST_HANA_CONSTANT_CHECK(template_<f>(type<x>, type<y>) == type<f<x, y>>);

static_assert(std::is_same<
    decltype(template_<f>)::apply<x, y>::type,
    f<x, y>
>::value, "");
//! [template]
}

namespace ns6 {
//! [metafunction]
template <typename ...> struct f { struct type; };
struct x;
struct y;

BOOST_HANA_CONSTANT_CHECK(metafunction<f>() == type<f<>::type>);
BOOST_HANA_CONSTANT_CHECK(metafunction<f>(type<x>) == type<f<x>::type>);
BOOST_HANA_CONSTANT_CHECK(metafunction<f>(type<x>, type<y>) == type<f<x, y>::type>);

static_assert(std::is_same<
    decltype(metafunction<f>)::apply<x, y>::type,
    f<x, y>::type
>::value, "");
//! [metafunction]
}

namespace ns7 {
//! [liftable_trait]
BOOST_HANA_CONSTANT_CHECK(trait<std::is_integral>(type<int>));
BOOST_HANA_CONSTANT_CHECK(not_(trait<std::is_integral>(type<float>)));
//! [liftable_trait]
}

namespace ns8 {
//! [non_liftable_trait]
BOOST_HANA_CONSTEXPR_LAMBDA auto extent = [](auto t, auto n) {
    return std::extent<typename decltype(t)::type, value(n)>{};
};
BOOST_HANA_CONSTANT_CHECK(extent(type<char>, int_<1>) == size_t<0>);
BOOST_HANA_CONSTANT_CHECK(extent(type<char[1][2]>, int_<1>) == size_t<2>);
//! [non_liftable_trait]
}

namespace ns9 {
//! [trait_]
BOOST_HANA_CONSTANT_CHECK(trait_<std::is_integral>(2));
BOOST_HANA_CONSTANT_CHECK(trait_<std::is_integral>(2ll));
BOOST_HANA_CONSTANT_CHECK(not_(trait_<std::is_integral>(2.2)));
//! [trait_]
}

int main() { }


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

  @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-trigonometric
    Function object implementing inrad capabilities

    convert degree to radian.

    @par Semantic:

    For every parameter of floating type

    @code
    auto r = inrad(x);
    @endcode

    is similar to:

    @code
    auto r = Deginrad*x;
    @endcode

    @see  indeg, Radindeg, Deginrad

  **/
  Value inrad(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/inrad.hpp>
#include <boost/simd/function/simd/inrad.hpp>

#endif


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/rec.hpp>
#include <boost/simd/function/fast.hpp>
#include <simd_test.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>

STF_CASE_TPL(" rec",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::rec;

  // return type conformity test
  STF_EXPR_IS( rec(T()) , T );

  // specific values tests
  STF_EQUAL(rec(bs::Inf<T>()), bs::Zero<T>());
  STF_EQUAL(rec(bs::Minf<T>()), bs::Zero<T>());
  STF_EQUAL(rec(bs::Mone<T>()), bs::Mone<T>());
  STF_EQUAL(rec(bs::Mzero<T>()), bs::Minf<T>());
  STF_IEEE_EQUAL(rec(bs::Nan<T>()), bs::Nan<T>());
  STF_EQUAL(rec(bs::One<T>()), bs::One<T>());
  STF_EQUAL(rec(bs::Zero<T>()), bs::Inf<T>());
} // end of test for floating_

STF_CASE_TPL(" rec",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::rec;
  using bs::fast_;

  // return type conformity test
  STF_EXPR_IS( bs::fast_(rec)(T()) , T );
  //For scalar is it a call redibs::fast_(rec)ted to bs::fast_(rec)
  // specific values tests
  STF_EQUAL(bs::fast_(rec)(bs::Inf<T>()), bs::Zero<T>());
  STF_EQUAL(bs::fast_(rec)(bs::Minf<T>()), bs::Zero<T>());
  STF_EQUAL(bs::fast_(rec)(bs::Mone<T>()), bs::Mone<T>());
  STF_EQUAL(bs::fast_(rec)(bs::Mzero<T>()), bs::Minf<T>());
  STF_IEEE_EQUAL(bs::fast_(rec)(bs::Nan<T>()), bs::Nan<T>());
  STF_EQUAL(bs::fast_(rec)(bs::One<T>()), bs::One<T>());
  STF_EQUAL(bs::fast_(rec)(bs::Zero<T>()), bs::Inf<T>());
} // end of test for floating_


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_ULP_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_ULP_HPP_INCLUDED
#include <boost/simd/ieee/functions/ulp.hpp>

#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/functions/simd/abs.hpp>
#include <boost/simd/ieee/functions/simd/common/details/ulp.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION(boost::simd::tag::ulp_, tag::cpu_,
                             (A0)(X),
                             ((simd_<arithmetic_<A0>,X>))
                             )
  {
    typedef A0 result_type;
    inline result_type operator()(const A0&)const
      {
        return One<A0>();
      }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::ulp_, tag::cpu_
                              , (A0)(X)
                              , ((simd_<floating_<A0>,X>))
                              )
  {
    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      const A0 x = boost::simd::abs(a0);
      return details::ulp(x);
    }
  };

} } }

#endif