/**
 * @file stability.cpp
 *
 */

#include "stability.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "metutil.h"
#include "plugin_factory.h"
#include <algorithm>  // for std::transform
#include <boost/thread.hpp>
#include <functional>  // for std::plus

#include "fetcher.h"
#include "hitool.h"

using namespace std;
using namespace himan;
using namespace himan::plugin;

vector<double> Shear(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, const param& wantedParam,
                     double lowerHeight, double upperHeight);

#ifdef DEBUG
void DumpVector(const vector<double>& vec);
#endif

// Required source and target parameters and levels

const param TParam("T-K");
const param TDParam("TD-K");
const param HParam("Z-M2S2");
const params PParam({param("P-HPA"), param("P-PA")});
const param KIParam("KINDEX-N", 80, 0, 7, 2);
const param VTIParam("VTI-N", 4754);
const param CTIParam("CTI-N", 4751);
const param TTIParam("TTI-N", 4755, 0, 7, 4);
const param SIParam("SI-N", 4750, 0, 7, 13);
const param LIParam("LI-N", 4751, 0, 7, 192);
const param BS01Param("WSH-1-KT", 4771);  // knots!
const param BS06Param("WSH-KT", 4770);    // knots!
const param SRH01Param("HLCY-1-M2S2", 4773);
const param SRH03Param("HLCY-M2S2", 4772, 0, 7, 8);

const level P850Level(himan::kPressure, 850, "PRESSURE");
const level P700Level(himan::kPressure, 700, "PRESSURE");
const level P500Level(himan::kPressure, 500, "PRESSURE");
level groundLevel(himan::kHeight, 0, "HEIGHT");

void T500mSearch(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, vector<double>& result);
void TD500mSearch(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, vector<double>& result);

stability::stability() : itsLICalculation(false), itsBSCalculation(false), itsSRHCalculation(false)
{
	itsLogger = logger("stability");
}

void stability::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	vector<param> theParams;

	// Kindex
	theParams.push_back(KIParam);

	// Cross totals index
	theParams.push_back(CTIParam);

	// Vertical Totals index
	theParams.push_back(VTIParam);

	// Total Totals index
	theParams.push_back(TTIParam);

	if (itsConfiguration->Exists("li") && itsConfiguration->GetValue("li") == "true")
	{
		// Lifted index

		itsLICalculation = true;
		theParams.push_back(LIParam);

		// Showalter Index
		theParams.push_back(SIParam);
	}

	if (itsConfiguration->Exists("bs") && itsConfiguration->GetValue("bs") == "true")
	{
		itsBSCalculation = true;

		// Bulk shear 0 .. 1 km
		theParams.push_back(BS01Param);

		// Bulk shear 0 .. 6 km

		theParams.push_back(BS06Param);
	}

	if (itsConfiguration->Exists("srh") && itsConfiguration->GetValue("srh") == "true")
	{
		// Storm relative helicity 0 .. 1 km
		theParams.push_back(SRH01Param);

		// Storm relative helicity 0 .. 3 km
		theParams.push_back(SRH03Param);
	}

	SetParams(theParams);

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void stability::Calculate(shared_ptr<info> myTargetInfo, unsigned short theThreadIndex)
{
	vector<double> T500mVector, TD500mVector, P500mVector, U01Vector, V01Vector, U06Vector, V06Vector, UidVector,
	    VidVector;

	auto myThreadedLogger = logger("stabilityThread #" + to_string(theThreadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();

	info_t T850Info, T700Info, T500Info, TD850Info, TD700Info;

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                      static_cast<string>(forecastLevel));

	bool LICalculation = itsLICalculation;
	bool BSCalculation = itsBSCalculation;
	bool SRHCalculation = itsSRHCalculation;

	if (!GetSourceData(T850Info, T700Info, T500Info, TD850Info, TD700Info, myTargetInfo,
	                   itsConfiguration->UseCudaForPacking()))
	{
		myThreadedLogger.Warning("Skipping step " + to_string(forecastTime.Step()) + ", level " +
		                         static_cast<string>(forecastLevel));
		return;
	}

	if (LICalculation)
	{
		if (!GetLISourceData(myTargetInfo, T500mVector, TD500mVector, P500mVector))
		{
			myThreadedLogger.Warning("Source data not found for param LI");
			LICalculation = false;
		}
	}

	if (BSCalculation)
	{
		if (!GetWindShearSourceData(myTargetInfo, U01Vector, V01Vector, U06Vector, V06Vector))
		{
			myThreadedLogger.Warning("Source data not found for param BulkShear");
			BSCalculation = false;
		}
	}

	if (SRHCalculation)
	{
		if (!GetSRHSourceData(myTargetInfo, UidVector, VidVector))
		{
			myThreadedLogger.Warning("Source data not found for param SRH");
			SRHCalculation = false;
		}
	}

	string deviceType = "CPU";

#ifdef HAVE_CUDA

	if (itsConfiguration->UseCuda())
	{
		deviceType = "GPU";

		unique_ptr<stability_cuda::options> opts(new stability_cuda::options);

		opts->t500 = T500Info->ToSimple();
		opts->t700 = T700Info->ToSimple();
		opts->t850 = T850Info->ToSimple();
		opts->td700 = TD700Info->ToSimple();
		opts->td850 = TD850Info->ToSimple();

		myTargetInfo->Param(param("KINDEX-N"));
		opts->ki = myTargetInfo->ToSimple();

		myTargetInfo->Param(param("VTI-N"));
		opts->vti = myTargetInfo->ToSimple();

		myTargetInfo->Param(param("CTI-N"));
		opts->cti = myTargetInfo->ToSimple();

		myTargetInfo->Param(param("TTI-N"));
		opts->tti = myTargetInfo->ToSimple();

		if (LICalculation)
		{
			opts->t500m = &T500mVector[0];
			opts->td500m = &TD500mVector[0];
			opts->p500m = &P500mVector[0];

			myTargetInfo->Param(param("LI-N"));
			opts->li = myTargetInfo->ToSimple();

			myTargetInfo->Param(param("SI-N"));
			opts->si = myTargetInfo->ToSimple();
		}

		if (BSCalculation)
		{
			opts->u01 = &U01Vector[0];
			opts->v01 = &V01Vector[0];
			opts->u06 = &U06Vector[0];
			opts->v06 = &V06Vector[0];

			myTargetInfo->Param(BS01Param);
			opts->bs01 = myTargetInfo->ToSimple();
			myTargetInfo->Param(BS06Param);
			opts->bs06 = myTargetInfo->ToSimple();
		}

		opts->N = opts->t500->size_x * opts->t500->size_y;

		stability_cuda::Process(*opts);
	}
	else
#endif
	{
		LOCKSTEP(myTargetInfo, T850Info, T700Info, T500Info, TD850Info, TD700Info)
		{
			double T850 = T850Info->Value();
			double T700 = T700Info->Value();
			double T500 = T500Info->Value();
			double TD850 = TD850Info->Value();
			double TD700 = TD700Info->Value();

			assert(T850 > 0);
			assert(T700 > 0);
			assert(T500 > 0);
			assert(TD850 > 0);
			assert(TD700 > 0);

			double value = kFloatMissing;

			if (T850 == kFloatMissing || T700 == kFloatMissing || T500 == kFloatMissing || TD850 == kFloatMissing ||
			    TD700 == kFloatMissing)
			{
				continue;
			}

			value = metutil::KI_(T850, T700, T500, TD850, TD700);
			myTargetInfo->Param(KIParam);
			myTargetInfo->Value(value);

			value = metutil::CTI_(T500, TD850);
			myTargetInfo->Param(CTIParam);
			myTargetInfo->Value(value);

			value = metutil::VTI_(T850, T500);
			myTargetInfo->Param(VTIParam);
			myTargetInfo->Value(value);

			value = metutil::TTI_(T850, T500, TD850);
			myTargetInfo->Param(TTIParam);
			myTargetInfo->Value(value);

			if (LICalculation)
			{
				size_t locationIndex = myTargetInfo->LocationIndex();

				double T500m = T500mVector[locationIndex];
				double TD500m = TD500mVector[locationIndex];
				double P500m = P500mVector[locationIndex];

				assert(T500m != kFloatMissing);
				assert(TD500m != kFloatMissing);
				assert(P500m != kFloatMissing);

				if (T500m != kFloatMissing && TD500m != kFloatMissing && P500m != kFloatMissing)
				{
					value = metutil::LI_(T500, T500m, TD500m, P500m);

					myTargetInfo->Param(LIParam);
					myTargetInfo->Value(value);
				}

				value = metutil::SI_(T850, T500, TD850);
				myTargetInfo->Param(SIParam);
				myTargetInfo->Value(value);
			}

			if (BSCalculation)
			{
				size_t locationIndex = myTargetInfo->LocationIndex();

				double U01 = U01Vector[locationIndex];
				double V01 = V01Vector[locationIndex];
				double U06 = U06Vector[locationIndex];
				double V06 = V06Vector[locationIndex];

				assert(U01 != kFloatMissing);
				assert(V01 != kFloatMissing);

				assert(U06 != kFloatMissing);
				assert(V06 != kFloatMissing);

				if (U01 != kFloatMissing && V01 != kFloatMissing)
				{
					value = metutil::BulkShear_(U01, V01);

					myTargetInfo->Param(BS01Param);
					myTargetInfo->Value(value);
				}

				if (U06 != kFloatMissing && V01 != kFloatMissing)
				{
					value = metutil::BulkShear_(U06, V06);

					myTargetInfo->Param(BS06Param);
					myTargetInfo->Value(value);
				}
			}

			if (SRHCalculation)
			{
				size_t locationIndex = myTargetInfo->LocationIndex();

				double Uid = UidVector[locationIndex];
				double Vid = VidVector[locationIndex];

				assert(Uid != kFloatMissing);
				assert(Vid != kFloatMissing);

				if (Uid != kFloatMissing && Vid != kFloatMissing)
				{
				}
			}
		}
	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}

void T500mSearch(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, vector<double>& result)
{
	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	h->Configuration(conf);
	h->Time(ftime);

	result = h->VerticalAverage(param("T-K"), 0, 500);

#ifdef DEBUG
	for (size_t i = 0; i < result.size(); i++)
	{
		assert(result[i] != kFloatMissing);
	}
#endif
}

void TD500mSearch(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, vector<double>& result)
{
	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	h->Configuration(conf);
	h->Time(ftime);

	result = h->VerticalAverage(param("TD-K"), 0, 500);
}
#if 0
inline
double stability::StormRelativeHelicity(double UID, double VID, double U_lower, double U_higher, double V_lower, double V_higher)
{
	return ((UID - U_lower) * (V_lower - V_higher)) - ((VID - V_lower) * (U_lower - U_higher));
}
#endif

bool stability::GetSourceData(shared_ptr<info>& T850Info, shared_ptr<info>& T700Info, shared_ptr<info>& T500Info,
                              shared_ptr<info>& TD850Info, shared_ptr<info>& TD700Info,
                              const shared_ptr<info>& myTargetInfo, bool useCudaInThisThread)
{
	bool ret = true;

	if (!T850Info)
	{
		T850Info = Fetch(myTargetInfo->Time(), P850Level, TParam, myTargetInfo->ForecastType(), useCudaInThisThread);
	}

	if (!T700Info)
	{
		T700Info = Fetch(myTargetInfo->Time(), P700Level, TParam, myTargetInfo->ForecastType(), useCudaInThisThread);
	}

	if (!T500Info)
	{
		T500Info = Fetch(myTargetInfo->Time(), P500Level, TParam, myTargetInfo->ForecastType(), useCudaInThisThread);
	}

	if (!TD850Info)
	{
		TD850Info = Fetch(myTargetInfo->Time(), P850Level, TDParam, myTargetInfo->ForecastType(), useCudaInThisThread);
	}

	if (!TD700Info)
	{
		TD700Info = Fetch(myTargetInfo->Time(), P700Level, TDParam, myTargetInfo->ForecastType(), useCudaInThisThread);
	}

	if (!T850Info || !T700Info || !T500Info || !TD850Info || !TD700Info)
	{
		ret = false;
	}

	return ret;
}

bool stability::GetLISourceData(const shared_ptr<info>& myTargetInfo, vector<double>& T500mVector,
                                vector<double>& TD500mVector, vector<double>& P500mVector)
{
	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());

// Fetch Z uncompressed since it is not transferred to cuda
#if 0
	auto HInfo = Fetch(myTargetInfo->Time(), groundLevel, HParam, false);

	if (!HInfo)
	{
		return false;
	}

	vector<double> H0mVector = HInfo->Grid()->Data().Values();
	vector<double> H500mVector(HInfo->SizeLocations());

	for (size_t i = 0; i < H500mVector.size(); i++)
	{
		// H0mVector contains the height of ground (compared to MSL). Height can be negative
		// (maybe even in real life (Netherlands?)), but in our case we use 0 as smallest height.
		// TODO: check how it is in smarttools

		if (H0mVector[i] == kFloatMissing)
		{
			continue;
		}

		H0mVector[i] *= constants::kIg;
		H0mVector[i] = fmax(0, H0mVector[i]);

		H500mVector[i] = H0mVector[i] + 500.;
	}

#endif

	// Fetch average values of T, TD and P over vertical height range 0 ... 500m OVER GROUND

	boost::thread t1(&T500mSearch, itsConfiguration, myTargetInfo->Time(), boost::ref(T500mVector));
	boost::thread t2(&TD500mSearch, itsConfiguration, myTargetInfo->Time(), boost::ref(TD500mVector));

	P500mVector = h->VerticalAverage(PParam, 0., 500.);

	assert(P500mVector[0] != kFloatMissing);

	if (P500mVector[0] < 1500)
	{
		transform(P500mVector.begin(), P500mVector.end(), P500mVector.begin(),
		          bind1st(multiplies<double>(), 100));  // hPa to Pa
	}

	t1.join();
	t2.join();

	return true;
}

bool stability::GetWindShearSourceData(const shared_ptr<info>& myTargetInfo, vector<double>& U01Vector,
                                       vector<double>& V01Vector, vector<double>& U06Vector, vector<double>& V06Vector)
{
	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());

	// BS 0-6
	U06Vector = Shear(itsConfiguration, myTargetInfo->Time(), param("U-MS"), 0, 6000);
	V06Vector = Shear(itsConfiguration, myTargetInfo->Time(), param("V-MS"), 0, 6000);

#ifdef DEBUG
	DumpVector(U06Vector);
	DumpVector(V06Vector);
#endif

	// BS 0-1

	U01Vector = Shear(itsConfiguration, myTargetInfo->Time(), param("U-MS"), 0, 1000);
	V01Vector = Shear(itsConfiguration, myTargetInfo->Time(), param("V-MS"), 0, 1000);

#ifdef DEBUG
	DumpVector(U01Vector);
	DumpVector(V01Vector);
#endif

	return true;
}

vector<double> Shear(shared_ptr<const plugin_configuration> conf, const forecast_time& ftime, const param& wantedParam,
                     double lowerHeight, double upperHeight)
{
	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	auto lowerValues = h->VerticalValue(wantedParam, lowerHeight);
	auto upperValues = h->VerticalValue(wantedParam, upperHeight);

	vector<double> ret(lowerValues.size(), kFloatMissing);

#ifdef YES_WE_HAVE_GCC_WHICH_SUPPORTS_LAMBDAS
	transform(lowerValues.begin(), lowerValues.end(), upperValues.begin(), back_inserter(U),
	          [](double l, double u) { return (u == kFloatMissing || l == kFloatMissing) ? kFloatMissing : u - l; });
	transform(lowerValues.begin(), lowerValues.end(), upperValues.begin(), back_inserter(V),
	          [](double l, double u) { return (u == kFloatMissing || l == kFloatMissing) ? kFloatMissing : u - l; });
#else

	for (size_t i = 0; i < lowerValues.size(); i++)
	{
		double l = lowerValues[i];
		double u = upperValues[i];

		if (u == kFloatMissing || l == kFloatMissing)
		{
			continue;
		}

		ret[i] = u - l;
	}
#endif

	return ret;
}

bool stability::GetSRHSourceData(const shared_ptr<info>& myTargetInfo, vector<double>& Uid, vector<double>& Vid)
{
	// NOTES COPIED FROM SMARTTOOLS-LIBRARY

	/* // **********  SRH calculation help from Pieter Groenemeijer ******************

	Some tips here on how tyo calculate storm-relative helciity

	How to calculate storm-relative helicity

	Integrate the following from p = p_surface to p = p_top (or in case of height coordinates from h_surface to h_top):

	storm_rel_helicity -= ((u_ID-u[p])*(v[p]-v[p+1]))-((v_ID - v[p])*(u[p]-u[p+1]));

	Here, u_ID and v_ID are the forecast storm motion vectors calculated with the so-called ID-method. These can be calculated as follows:

	where

	/average wind
	u0_6 = average 0_6 kilometer u-wind component
	v0_6 = average 0_6 kilometer v-wind component
	(you should use a pressure-weighted average in case you work with height coordinates)

	/shear
	shr_0_6_u = u_6km - u_surface;
	shr_0_6_v = v_6km - v_surface;

	/ shear unit vector
	shr_0_6_u_n = shr_0_6_u / ((shr_0_6_u^2 + shr_0_6_v^2)**0.5);
	shr_0_6_v_n = shr_0_6_v / ((shr_0_6_u^2 + shr_0_6_v^2)** 0.5);

	/id-vector components
	u_ID = u0_6 + shr_0_6_v_n * 7.5;
	v_ID = v0_6 - shr_0_6_u_n * 7.5;

	(7.5 are meters per second... watch out when you work with knots instead)

	*/  // **********  SRH calculation help from Pieter Groenemeijer ******************

	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());

	// average wind
	auto Uavg = h->VerticalAverage(param("U-MS"), 0, 6000);
	auto Vavg = h->VerticalAverage(param("V-MS"), 0, 6000);

	// shear
	auto Ushear = Shear(itsConfiguration, myTargetInfo->Time(), param("U-MS"), 0, 6000);
	auto Vshear = Shear(itsConfiguration, myTargetInfo->Time(), param("V-MS"), 0, 6000);

	// shear unit vectors
	Uid.resize(Ushear.size(), kFloatMissing);
	Vid.resize(Vshear.size(), kFloatMissing);

	assert(Uid.size() == Vid.size());
	assert(Uid.size() == Uavg.size());

	for (size_t i = 0; i < Ushear.size(); i++)
	{
		double u = Ushear[i];
		double v = Vshear[i];

		if (u == kFloatMissing || v == kFloatMissing)
		{
			continue;
		}

		double Uunit = u / sqrt(u * u + v * v);
		double Vunit = v / sqrt(u * u + v * v);

		Uid[i] = Uavg[i] - Vunit * 7.5;
		Vid[i] = Vavg[i] - Uunit * 7.5;
	}

	return true;
}

#ifdef DEBUG
void DumpVector(const vector<double>& vec)
{
	double min = 1e38, max = -1e38, sum = 0;
	size_t count = 0, missing = 0;

	for (double val : vec)
	{
		if (val == kFloatMissing)
		{
			missing++;
			continue;
		}

		min = (val < min) ? val : min;
		max = (val > max) ? val : max;
		count++;
		sum += val;
	}

	double mean = numeric_limits<double>::quiet_NaN();

	if (count > 0)
	{
		mean = sum / static_cast<double>(count);
	}

	cout << "min " << min << " max " << max << " mean " << mean << " count " << count << " missing " << missing << endl;
}

#endif


// Copyright 2004 The Trustees of Indiana University.

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

//  Authors: Douglas Gregor
//           Andrew Lumsdaine
#ifndef BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP
#define BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP

#include <boost/config/no_tr1/cmath.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/simple_point.hpp>
#include <vector>
#include <list>
#include <algorithm> // for std::min and std::max

namespace boost {

struct square_distance_attractive_force {
  template<typename Graph, typename T>
  T
  operator()(typename graph_traits<Graph>::edge_descriptor,
             T k,
             T d,
             const Graph&) const
  {
    return d * d / k;
  }
};

struct square_distance_repulsive_force {
  template<typename Graph, typename T>
  T
  operator()(typename graph_traits<Graph>::vertex_descriptor,
             typename graph_traits<Graph>::vertex_descriptor,
             T k,
             T d,
             const Graph&) const
  {
    return k * k / d;
  }
};

template<typename T>
struct linear_cooling {
  typedef T result_type;

  linear_cooling(std::size_t iterations)
    : temp(T(iterations) / T(10)), step(0.1) { }

  linear_cooling(std::size_t iterations, T temp)
    : temp(temp), step(temp / T(iterations)) { }

  T operator()()
  {
    T old_temp = temp;
    temp -= step;
    if (temp < T(0)) temp = T(0);
    return old_temp;
  }

 private:
  T temp;
  T step;
};

struct all_force_pairs
{
  template<typename Graph, typename ApplyForce >
  void operator()(const Graph& g, ApplyForce apply_force)
  {
    typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
    vertex_iterator v, end;
    for (tie(v, end) = vertices(g); v != end; ++v) {
      vertex_iterator u = v;
      for (++u; u != end; ++u) {
        apply_force(*u, *v);
        apply_force(*v, *u);
      }
    }
  }
};

template<typename Dim, typename PositionMap>
struct grid_force_pairs
{
  template<typename Graph>
  explicit
  grid_force_pairs(Dim width, Dim height, PositionMap position, const Graph& g)
    : width(width), height(height), position(position)
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::sqrt;
#endif // BOOST_NO_STDC_NAMESPACE
    two_k = Dim(2) * sqrt(width*height / num_vertices(g));
  }

  template<typename Graph, typename ApplyForce >
  void operator()(const Graph& g, ApplyForce apply_force)
  {
    typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
    typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
    typedef std::list<vertex_descriptor> bucket_t;
    typedef std::vector<bucket_t> buckets_t;

    std::size_t columns = std::size_t(width / two_k + Dim(1));
    std::size_t rows = std::size_t(height / two_k + Dim(1));
    buckets_t buckets(rows * columns);
    vertex_iterator v, v_end;
    for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
      std::size_t column = std::size_t((position[*v].x + width  / 2) / two_k);
      std::size_t row    = std::size_t((position[*v].y + height / 2) / two_k);

      if (column >= columns) column = columns - 1;
      if (row >= rows) row = rows - 1;
      buckets[row * columns + column].push_back(*v);
    }

    for (std::size_t row = 0; row < rows; ++row)
      for (std::size_t column = 0; column < columns; ++column) {
        bucket_t& bucket = buckets[row * columns + column];
        typedef typename bucket_t::iterator bucket_iterator;
        for (bucket_iterator u = bucket.begin(); u != bucket.end(); ++u) {
          // Repulse vertices in this bucket
          bucket_iterator v = u;
          for (++v; v != bucket.end(); ++v) {
            apply_force(*u, *v);
            apply_force(*v, *u);
          }

          std::size_t adj_start_row = row == 0? 0 : row - 1;
          std::size_t adj_end_row = row == rows - 1? row : row + 1;
          std::size_t adj_start_column = column == 0? 0 : column - 1;
          std::size_t adj_end_column = column == columns - 1? column : column + 1;
          for (std::size_t other_row = adj_start_row; other_row <= adj_end_row;
               ++other_row)
            for (std::size_t other_column = adj_start_column;
                 other_column <= adj_end_column; ++other_column)
              if (other_row != row || other_column != column) {
                // Repulse vertices in this bucket
                bucket_t& other_bucket
                  = buckets[other_row * columns + other_column];
                for (v = other_bucket.begin(); v != other_bucket.end(); ++v)
                  apply_force(*u, *v);
              }
        }
      }
  }

 private:
  Dim width;
  Dim height;
  PositionMap position;
  Dim two_k;
};

template<typename Dim, typename PositionMap, typename Graph>
inline grid_force_pairs<Dim, PositionMap>
make_grid_force_pairs(Dim width, Dim height, const PositionMap& position,
                      const Graph& g)
{ return grid_force_pairs<Dim, PositionMap>(width, height, position, g); }

template<typename Graph, typename PositionMap, typename Dim>
void
scale_graph(const Graph& g, PositionMap position,
            Dim left, Dim top, Dim right, Dim bottom)
{
  if (num_vertices(g) == 0) return;

  if (bottom > top) {
    using std::swap;
    swap(bottom, top);
  }

  typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;

  // Find min/max ranges
  Dim minX = position[*vertices(g).first].x, maxX = minX;
  Dim minY = position[*vertices(g).first].y, maxY = minY;
  vertex_iterator vi, vi_end;
  for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
    BOOST_USING_STD_MIN();
    BOOST_USING_STD_MAX();
    minX = min BOOST_PREVENT_MACRO_SUBSTITUTION (minX, position[*vi].x);
    maxX = max BOOST_PREVENT_MACRO_SUBSTITUTION (maxX, position[*vi].x);
    minY = min BOOST_PREVENT_MACRO_SUBSTITUTION (minY, position[*vi].y);
    maxY = max BOOST_PREVENT_MACRO_SUBSTITUTION (maxY, position[*vi].y);
  }

  // Scale to bounding box provided
  for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
    position[*vi].x = ((position[*vi].x - minX) / (maxX - minX))
                    * (right - left) + left;
    position[*vi].y = ((position[*vi].y - minY) / (maxY - minY))
                    * (top - bottom) + bottom;
  }
}

namespace detail {
  template<typename PositionMap, typename DisplacementMap,
           typename RepulsiveForce, typename Dim, typename Graph>
  struct fr_apply_force
  {
    typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;

    fr_apply_force(const PositionMap& position,
                   const DisplacementMap& displacement,
                   RepulsiveForce repulsive_force, Dim k, const Graph& g)
      : position(position), displacement(displacement),
        repulsive_force(repulsive_force), k(k), g(g)
    { }

    void operator()(vertex_descriptor u, vertex_descriptor v)
    {
#ifndef BOOST_NO_STDC_NAMESPACE
      using std::sqrt;
#endif // BOOST_NO_STDC_NAMESPACE
      if (u != v) {
        Dim delta_x = position[v].x - position[u].x;
        Dim delta_y = position[v].y - position[u].y;
        Dim dist = sqrt(delta_x * delta_x + delta_y * delta_y);
        Dim fr = repulsive_force(u, v, k, dist, g);
        displacement[v].x += delta_x / dist * fr;
        displacement[v].y += delta_y / dist * fr;
      }
    }

  private:
    PositionMap position;
    DisplacementMap displacement;
    RepulsiveForce repulsive_force;
    Dim k;
    const Graph& g;
  };

} // end namespace detail

template<typename Graph, typename PositionMap, typename Dim,
         typename AttractiveForce, typename RepulsiveForce,
         typename ForcePairs, typename Cooling, typename DisplacementMap>
void
fruchterman_reingold_force_directed_layout
 (const Graph&    g,
  PositionMap     position,
  Dim             width,
  Dim             height,
  AttractiveForce attractive_force,
  RepulsiveForce  repulsive_force,
  ForcePairs      force_pairs,
  Cooling         cool,
  DisplacementMap displacement)
{
  typedef typename graph_traits<Graph>::vertex_iterator   vertex_iterator;
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
  typedef typename graph_traits<Graph>::edge_iterator     edge_iterator;

#ifndef BOOST_NO_STDC_NAMESPACE
  using std::sqrt;
#endif // BOOST_NO_STDC_NAMESPACE

  Dim area = width * height;
  // assume positions are initialized randomly
  Dim k = sqrt(area / num_vertices(g));

  detail::fr_apply_force<PositionMap, DisplacementMap,
                         RepulsiveForce, Dim, Graph>
    apply_force(position, displacement, repulsive_force, k, g);

  Dim temp = cool();
  if (temp) do {
    // Calculate repulsive forces
    vertex_iterator v, v_end;
    for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
      displacement[*v].x = 0;
      displacement[*v].y = 0;
    }
    force_pairs(g, apply_force);

    // Calculate attractive forces
    edge_iterator e, e_end;
    for (tie(e, e_end) = edges(g); e != e_end; ++e) {
      vertex_descriptor v = source(*e, g);
      vertex_descriptor u = target(*e, g);
      Dim delta_x = position[v].x - position[u].x;
      Dim delta_y = position[v].y - position[u].y;
      Dim dist = sqrt(delta_x * delta_x + delta_y * delta_y);
      Dim fa = attractive_force(*e, k, dist, g);

      displacement[v].x -= delta_x / dist * fa;
      displacement[v].y -= delta_y / dist * fa;
      displacement[u].x += delta_x / dist * fa;
      displacement[u].y += delta_y / dist * fa;
    }

    // Update positions
    for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
      BOOST_USING_STD_MIN();
      BOOST_USING_STD_MAX();
      Dim disp_size = sqrt(displacement[*v].x * displacement[*v].x
                           + displacement[*v].y * displacement[*v].y);
      position[*v].x += displacement[*v].x / disp_size
                     * min BOOST_PREVENT_MACRO_SUBSTITUTION (disp_size, temp);
      position[*v].y += displacement[*v].y / disp_size
                     * min BOOST_PREVENT_MACRO_SUBSTITUTION (disp_size, temp);
      position[*v].x = min BOOST_PREVENT_MACRO_SUBSTITUTION
                         (width / 2,
                          max BOOST_PREVENT_MACRO_SUBSTITUTION(-width / 2,
                                                               position[*v].x));
      position[*v].y = min BOOST_PREVENT_MACRO_SUBSTITUTION
                         (height / 2,
                          max BOOST_PREVENT_MACRO_SUBSTITUTION(-height / 2,
                                                               position[*v].y));
    }
  } while (temp = cool());
}

namespace detail {
  template<typename DisplacementMap>
  struct fr_force_directed_layout
  {
    template<typename Graph, typename PositionMap, typename Dim,
             typename AttractiveForce, typename RepulsiveForce,
             typename ForcePairs, typename Cooling,
             typename Param, typename Tag, typename Rest>
    static void
    run(const Graph&    g,
        PositionMap     position,
        Dim             width,
        Dim             height,
        AttractiveForce attractive_force,
        RepulsiveForce  repulsive_force,
        ForcePairs      force_pairs,
        Cooling         cool,
        DisplacementMap displacement,
        const bgl_named_params<Param, Tag, Rest>&)
    {
      fruchterman_reingold_force_directed_layout
        (g, position, width, height, attractive_force, repulsive_force,
         force_pairs, cool, displacement);
    }
  };

  template<>
  struct fr_force_directed_layout<error_property_not_found>
  {
    template<typename Graph, typename PositionMap, typename Dim,
             typename AttractiveForce, typename RepulsiveForce,
             typename ForcePairs, typename Cooling,
             typename Param, typename Tag, typename Rest>
    static void
    run(const Graph&    g,
        PositionMap     position,
        Dim             width,
        Dim             height,
        AttractiveForce attractive_force,
        RepulsiveForce  repulsive_force,
        ForcePairs      force_pairs,
        Cooling         cool,
        error_property_not_found,
        const bgl_named_params<Param, Tag, Rest>& params)
    {
      std::vector<simple_point<Dim> > displacements(num_vertices(g));
      fruchterman_reingold_force_directed_layout
        (g, position, width, height, attractive_force, repulsive_force,
         force_pairs, cool,
         make_iterator_property_map
         (displacements.begin(),
          choose_const_pmap(get_param(params, vertex_index), g,
                            vertex_index),
          simple_point<Dim>()));
    }
  };

} // end namespace detail

template<typename Graph, typename PositionMap, typename Dim, typename Param,
         typename Tag, typename Rest>
void
fruchterman_reingold_force_directed_layout
  (const Graph&    g,
   PositionMap     position,
   Dim             width,
   Dim             height,
   const bgl_named_params<Param, Tag, Rest>& params)
{
  typedef typename property_value<bgl_named_params<Param,Tag,Rest>,
                                  vertex_displacement_t>::type D;

  detail::fr_force_directed_layout<D>::run
    (g, position, width, height,
     choose_param(get_param(params, attractive_force_t()),
                  square_distance_attractive_force()),
     choose_param(get_param(params, repulsive_force_t()),
                  square_distance_repulsive_force()),
     choose_param(get_param(params, force_pairs_t()),
                  make_grid_force_pairs(width, height, position, g)),
     choose_param(get_param(params, cooling_t()),
                  linear_cooling<Dim>(100)),
     get_param(params, vertex_displacement_t()),
     params);
}

template<typename Graph, typename PositionMap, typename Dim>
void
fruchterman_reingold_force_directed_layout(const Graph&    g,
                                           PositionMap     position,
                                           Dim             width,
                                           Dim             height)
{
  fruchterman_reingold_force_directed_layout
    (g, position, width, height,
     attractive_force(square_distance_attractive_force()));
}

} // end namespace boost

#endif // BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP


/* Copyright (c) 2019, NVIDIA CORPORATION. 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 NVIDIA CORPORATION 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 ``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 OWNER 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.
 */

#include <boost/multiprecision/cpp_int.hpp>

#include "util/numeric.hpp"

using namespace boost::multiprecision;

//------------------------------------
//              Factors
//------------------------------------

unsigned long Factors::ISqrt_(unsigned long x)
{
  register unsigned long op, res, one;

  op = x;
  res = 0;

  one = 1 << 30;
  while (one > op) one >>= 2;

  while (one != 0)
  {
    if (op >= res + one)
    {
      op -= res + one;
      res += one << 1;
    }
    res >>= 1;
    one >>= 2;
  }
  return res;
}

void Factors::CalculateAllFactors_()
{
  all_factors_.clear();
  for (unsigned long i = 1; i <= ISqrt_(n_); i++)
  {
    if (n_ % i == 0)
    {
      all_factors_.push_back(i);
      if (i * i != n_)
      {
        all_factors_.push_back(n_ / i);
      }
    }
  }
}

// Return a vector of all order-way cofactor sets of n.
std::vector<std::vector<unsigned long>>
Factors::MultiplicativeSplitRecursive_(unsigned long n, int order)
{
  if (order == 0)
  {
    return {{}};
  }
  else if (order == 1)
  {
    return {{n}};
  }
  else
  {
    std::vector<std::vector<unsigned long>> retval;
    for (auto factor = all_factors_.begin(); factor != all_factors_.end(); factor++)
    {
      // This factor is only acceptable if the residue is divisible by it.
      if (n % (*factor) == 0)
      {
        // Recursive call.
        std::vector<std::vector<unsigned long>> subproblem =
          MultiplicativeSplitRecursive_(n / (*factor), order - 1);

        // Append this factor to the end of each vector returned by the
        // recursive call.
        for (auto vector = subproblem.begin(); vector != subproblem.end(); vector++)
        {
          vector->push_back(*factor);
        }

        // Add all these appended vectors to my growing vector of vectors.
        retval.insert(retval.end(), subproblem.begin(), subproblem.end());
      }
      else
      {
        // Discard this factor.
      }
    }
    return retval;
  }
}

Factors::Factors() :
    n_(0)
{
}

Factors::Factors(const unsigned long n, const int order) :
    n_(n), cofactors_()
{
  CalculateAllFactors_();
  cofactors_ = MultiplicativeSplitRecursive_(n, order);
}

Factors::Factors(const unsigned long n, const int order, std::map<unsigned, unsigned long> given) :
    n_(n), cofactors_()
{
  assert(given.size() <= std::size_t(order));

  // If any of the given factors is not a factor of n, forcibly reset that to
  // be a free variable, otherwise accumulate them into a partial product.
  unsigned long partial_product = 1;
  for (auto f = given.begin(); f != given.end(); f++)
  {
    auto factor = f->second;
    if (n % (factor * partial_product) == 0)
    {
      partial_product *= factor;
    }
    else
    {
      std::cerr << "WARNING: cannot accept " << factor << " as a factor of " << n
                << " with current partial product " << partial_product;
#define SET_NONFACTOR_TO_FREE_VARIABLE
#ifdef SET_NONFACTOR_TO_FREE_VARIABLE
      // Ignore mapping constraint and set the factor to a free variable.
      std::cerr << ", ignoring mapping constraint and setting to a free variable."
                << std::endl;
      f = given.erase(f);
#else
      // Try to find the next *lower* integer that is a factor of n.
      // FIXME: there are multiple exceptions that can cause us to be here:
      // (a) factor doesn't divide into n.
      // (b) factor does divide into n but causes partial product to exceed n.
      // (c) factor does divide into n but causes partial product to not
      //     divide into n.
      // The following code only solves case (a).
      std::cerr << "FIXME: please fix this code." << std::endl;
      assert(false);

      for (; factor >= 1 && (n % factor != 0); factor--);
      std::cerr << ", setting this to " << factor << " instead." << std::endl;
      f->second = factor;
      partial_product *= factor;
#endif
    }
    assert(n % partial_product == 0);
  }

  CalculateAllFactors_();

  cofactors_ = MultiplicativeSplitRecursive_(n / partial_product, order - given.size());

  // Insert the given factors at the specified indices of each of the solutions.
  for (auto& cofactors : cofactors_)
  {
    for (auto& given_factor : given)
    {
      // Insert the given factor, pushing all existing factors back.
      auto index = given_factor.first;
      auto value = given_factor.second;
      assert(index <= cofactors.size());
      cofactors.insert(cofactors.begin() + index, value);
    }
  }
}

void Factors::PruneMax(std::map<unsigned, unsigned long>& max)
{
  // Prune the vector of cofactor sets by removing those sets that have factors
  // outside user-specified min/max range. We should really have done this during
  // MultiplicativeSplitRecursive. However, the "given" map complicates things
  // because given factors may be scattered, and we'll need a map table to
  // find the original rank from the "compressed" rank seen by
  // MultiplicativeSplitRecursive. Doing it now is slower but cleaner and less
  // bug-prone.

  auto cofactors_it = cofactors_.begin();
  while (cofactors_it != cofactors_.end())
  {
    bool illegal = false;
    for (auto& max_factor : max)
    {
      auto index = max_factor.first;
      auto max = max_factor.second;
      assert(index <= cofactors_it->size());
      auto value = cofactors_it->at(index);
      if (value > max)
      {
        illegal = true;
        break;
      }
    }

    if (illegal)
      cofactors_it = cofactors_.erase(cofactors_it);
    else
      cofactors_it++;
  }
}

std::vector<unsigned long>& Factors::operator[](int index)
{
  return cofactors_[index];
}

std::size_t Factors::size()
{
  return cofactors_.size();
}

void Factors::Print()
{
  PrintAllFactors();
  PrintCoFactors();
}

void Factors::PrintAllFactors()
{
  std::cout << "All factors of " << n_ << ": ";
  bool first = true;
  for (auto f = all_factors_.begin(); f != all_factors_.end(); f++)
  {
    if (first)
    {
      first = false;
    }
    else
    {
      std::cout << ", ";
    }
    std::cout << (*f);
  }
  std::cout << std::endl;
}

void Factors::PrintCoFactors()
{
  std::cout << *this;
}

std::ostream& operator<<(std::ostream& out, const Factors& f)
{
  out << "Co-factors of " << f.n_ << " are: " << std::endl;
  for (auto cset = f.cofactors_.begin(); cset != f.cofactors_.end(); cset++)
  {
    out << "    " << f.n_ << " = ";
    bool first = true;
    for (auto i = cset->begin(); i != cset->end(); i++)
    {
      if (first)
      {
        first = false;
      }
      else
      {
        out << " * ";
      }
      out << (*i);
    }
    out << std::endl;
  }
  return out;
}
//------------------------------------
//              ResidualFactors
//------------------------------------

unsigned long ResidualFactors::ISqrt_(unsigned long x)
{
  unsigned long op, res, one;

  op = x;
  res = 0;

  one = 1 << 30;
  while (one > op) one >>= 2;

  while (one != 0)
  {
    if (op >= res + one)
    {
      op -= res + one;
      res += one << 1;
    }
    res >>= 1;
    one >>= 2;
  }
  return res;
}

void ResidualFactors::ClearAllFactors_()
{
  all_factors_.clear();
}
void ResidualFactors::CalculateAllFactors_()
{
  for (unsigned long i = 1; i <= ISqrt_(n_); i++)
  {
    if (n_ % i == 0)
    {
      all_factors_.insert(i);
      if (i * i != n_)
      {
        all_factors_.insert(n_ / i);
      }
    }
  }
}

// Generate all additional potential factors given the remainder bounds
void ResidualFactors::CalculateAdditionalFactors_()
{
  std::vector<unsigned long> reaminder_possible;
  for (auto& n : remainder_bounds_){
    for(unsigned long i = 1; i <= n; i++)
      reaminder_possible.push_back(i);
  }

  for (auto& n : reaminder_possible){
    unsigned long g = n * n_ * ceil((double)n_/(double)n);
    for (unsigned long i = 1; i <= n_; i++)
    {
      if (g % i == 0)
      {
        if (i < n_)
        all_factors_.insert(i);
        if (i * i != g)
        {
          if ((g/i) < n_)
          all_factors_.insert(g / i);
        }
      }
    }
  }
}

std::vector<std::vector<unsigned long>> ResidualFactors::CartProduct_ (const std::vector<std::vector<unsigned long>> v) {
  std::vector<std::vector<unsigned long>> s = {{}};
  for (const auto u : v) {
      std::vector<std::vector<unsigned long>> r;
      for (const auto x : s) {
          for (const auto y : u) {
              r.push_back(x);
              r.back().push_back(y);
          }
      }
      s = r;
  }
  return s;
}

// Replicate all factors possibilities accross each level n, disregarding sets with that include more than dimension size n * sqrt(n)
void ResidualFactors::GenerateFactorProduct_(const unsigned long n, const int order)
{
  for(auto rec = 0; rec < order; rec++){
    std::vector<std::vector<unsigned long>> inter_factors;
    std::vector<std::vector<unsigned long>> product_factors;
    for (auto i = 0; i < order; i++)
    {
      std::vector<unsigned long> v2;
      for(auto a : all_factors_){
        if (i == 0 && rec == 0){
          v2.push_back(a);
        }else if(a <= ((unsigned int)(pow((double)n_, 1.0/(2.0)) + 1.5)) && i > 0){
          v2.push_back(a);
        }else if (rec > 0 && i == 0 && a >= ((unsigned int)(pow((double)n_, 1.0/(2.0)) + 1.5))){
          v2.push_back(a);
        }
      }
      inter_factors.push_back(v2);
    }

    std::swap(inter_factors[0], inter_factors[rec]);

    product_factors = CartProduct_(inter_factors);
    replicated_factors_.reserve(replicated_factors_.size() + product_factors.size());
    replicated_factors_.insert(replicated_factors_.end(), product_factors.begin(), product_factors.end());
  }

  for(auto t : replicated_factors_){

      unsigned long product = 1;
      for(auto p : t){
        if (p != 1){
          product *= (p-1);
        }
      }
      if (product <= n){
          pruned_product_factors_.push_back(t);
      }
  }

}

// Replicate all possible residual combiniations, and remove possibilities that sum up to size greater than n
void ResidualFactors::GenerateResidual_(const unsigned long n, const int order)
{
  std::vector<std::vector<unsigned long>> residuals;

  for (auto i : remainder_bounds_){
    std::vector<unsigned long> r;
    for (unsigned j = 1; j <= i; j++){
      r.push_back(j);
    }
    residuals.push_back(r);
  }

  residuals = CartProduct_(residuals);

  for(auto t : residuals){
    unsigned long sum = 0;
    for(auto p : t){
      sum += p;
    }
    if(sum <= n+(unsigned)order){
      pruned_residuals_.push_back(t);
    }
  }

}

// Replicate all possible residual combiniations, and remove possibilities that sum up to size greater than n
void ResidualFactors::ValidityChecker_(const unsigned long n, std::map<unsigned, unsigned long> given)
{

  for (unsigned i = pruned_product_factors_.size(); i > 0 ; i--)
  {

    for (auto it = given.begin(); it != given.end(); it++)
    {
      // Insert the given factor, pushing all existing factors back.
      auto index = it->first;
      auto value = it->second;
      pruned_product_factors_[i-1].insert(pruned_product_factors_[i-1].begin() + index, value);
    }

  }

  for(auto f : pruned_product_factors_){
    for(auto r : pruned_residuals_){
      std::vector<unsigned long> valid_residuals;
      int s_i = 0;
      bool valid = true;
      for(unsigned long i = 0; i < (f.size()); i++){
        if(std::count(remainder_ix_.begin(),remainder_ix_.end(), i) > 0){
          valid_residuals.push_back(r.at(s_i));

          if(f.at(i) > remainder_bounds_[s_i]){
            valid = false;
          }
          s_i++;
        }else{
          valid_residuals.push_back(f.at(i));
        }
      }

      //Solve for generic is L_{n} = L{n+1}*P{n} + R_{n} - 1
      unsigned long equation_answer = 0;
      for(unsigned j = (f.size()); j > 0; j--){
          equation_answer = f.at(j-1)*equation_answer + (valid_residuals.at(j-1) - 1);

          if (f.at(j-1) < valid_residuals.at(j-1))
            valid = false;
          if(equation_answer == 0 && valid_residuals.at(j-1) != f.at(j-1))
            valid = false;
        }

        if ((equation_answer + 1 == n) and valid){

          cofactors_.push_back(f);
          rfactors_.push_back(valid_residuals);

      }
    }
  }

}

void ResidualFactors::PruneMax()
{
  // Prune the vector of cofactor sets by removing those sets that have factors
  // outside user-specified min/max range. We should really have done this during
  // MultiplicativeSplitRecursive. However, the "given" map complicates things
  // because given factors may be scattered, and we'll need a map table to
  // find the original rank from the "compressed" rank seen by
  // MultiplicativeSplitRecursive. Doing it now is slower but cleaner and less
  // bug-prone.

}

ResidualFactors::ResidualFactors() : n_(0) {}

/***
First, we cacluate all the factors same as Uber
Next, we add in additional factors based on user defined loop bounds and take the cross product of these (eliminating impossible mappings)
Then, we calculate all the valid mapspace points that fit the expanded formula L_{n} = L{n+1}*P{n} + R_{n} - 1
***/
ResidualFactors::ResidualFactors(const unsigned long n, const int order, std::vector<unsigned long> remainder_bounds,
    std::vector<unsigned long> remainder_ix) : n_(n), remainder_bounds_(remainder_bounds), remainder_ix_(remainder_ix)
{
  ClearAllFactors_();
  CalculateAllFactors_();
  CalculateAdditionalFactors_();
  GenerateFactorProduct_(n, order);
  GenerateResidual_(n, order);
  std::map<unsigned, unsigned long> given = {{}};
  ValidityChecker_(n, given);

  for (unsigned i = 0; i < cofactors_.size(); i++)
  {
    std::reverse(cofactors_[i].begin(), cofactors_[i].end());
    std::reverse(rfactors_[i].begin(), rfactors_[i].end());
  }
}

ResidualFactors::ResidualFactors(const unsigned long n, const int order, std::vector<unsigned long> remainder_bounds,
    std::vector<unsigned long> remainder_ix, std::map<unsigned, unsigned long> given)
    : n_(n), remainder_bounds_(remainder_bounds), remainder_ix_(remainder_ix)
{

  const unsigned int given_size = given.size();

  assert(given_size <= std::size_t(order));
  // If any of the given factors is not a factor of n, forcibly reset that to
  // be a free variable, otherwise accumulate them into a partial product.
  unsigned long partial_product = 1;
  for (auto f = given.begin(); f != given.end(); f++)
  {
    auto factor = f->second;
    if (n % (factor * partial_product) == 0)
    {
      partial_product *= factor;
    }
    else
    {
      std::cerr << "WARNING: cannot accept " << factor << " as a factor of " << n
                << " with current partial product " << partial_product;
#define SET_NONFACTOR_TO_FREE_VARIABLE
#ifdef SET_NONFACTOR_TO_FREE_VARIABLE
      // Ignore mapping constraint and set the factor to a free variable.
      std::cerr << ", ignoring mapping constraint and setting to a free variable."
                << std::endl;
      f = given.erase(f);
#else
      // Try to find the next *lower* integer that is a factor of n.
      // FIXME: there are multiple exceptions that can cause us to be here:
      // (a) factor doesn't divide into n.
      // (b) factor does divide into n but causes partial product to exceed n.
      // (c) factor does divide into n but causes partial product to not
      //     divide into n.
      // The following code only solves case (a).
      std::cerr << "FIXME: please fix this code." << std::endl;
      assert(false);

      for (; factor >= 1 && (n % factor != 0); factor--);
      std::cerr << ", setting this to " << factor << " instead." << std::endl;
      f->second = factor;
      partial_product *= factor;
#endif
    }
    assert(n % partial_product == 0);
  }

  ClearAllFactors_();
  CalculateAllFactors_();
  CalculateAdditionalFactors_();

  GenerateFactorProduct_(n / partial_product, order - given.size());

  GenerateResidual_(n / partial_product, order - given.size());

  // Insert the given factors at the specified indices of each of the solutions.

  ValidityChecker_(n, given);

  remainder_bounds_.resize(0);
  remainder_ix_.resize(0);
  pruned_product_factors_.resize(0);
  pruned_residuals_.resize(0);
  replicated_factors_.resize(0);

}

std::vector<std::vector<unsigned long>> ResidualFactors::operator[](int index)
{
  std::vector<std::vector<unsigned long>> ret;
  std::vector<unsigned long> cfm = cofactors_.at(index);
  std::vector<unsigned long> rfm = rfactors_.at(index);

  ret.push_back(cfm);
  ret.push_back(rfm);
  return ret;
}

std::size_t ResidualFactors::size() { return cofactors_.size(); }

void ResidualFactors::Print()
{
  PrintAllFactors();
  PrintCoFactors();
}

void ResidualFactors::PrintAllFactors()
{
  std::cout << "All factors of " << n_ << ": ";
  bool first = true;
  for (auto f = all_factors_.begin(); f != all_factors_.end(); f++) {
    if (first) {
      first = false;
    } else {
      std::cout << ", ";
    }
    std::cout << (*f);
  }
  std::cout << std::endl;
}

void ResidualFactors::PrintCoFactors() { std::cout << *this; }

std::ostream& operator<<(std::ostream& out, const ResidualFactors& f) {
  out << "Co-factors of " << f.n_ << " are: " << std::endl;
  for (auto cset = f.cofactors_.begin(); cset != f.cofactors_.end(); cset++) {
    out << "    " << f.n_ << " = ";
    bool first = true;
    for (auto i = cset->begin(); i != cset->end(); i++) {
      if (first) {
        first = false;
      } else {
        out << " * ";
      }
      out << (*i);
    }
    out << std::endl;
  }
  return out;
}

//------------------------------------
//        PatternGenerator128
//------------------------------------

PatternGenerator128::PatternGenerator128(uint128_t bound) :
    bound_(bound)
{
}

SequenceGenerator128::SequenceGenerator128(uint128_t bound, bool autoloop) :
    PatternGenerator128(bound),
    autoloop_(autoloop),
    cur_(0)
{
}

uint128_t SequenceGenerator128::Next()
{
  auto retval = cur_;
  if (cur_ == bound_-1)
  {
    assert(autoloop_);
    cur_ = 0;
  }
  else
  {
    cur_++;
  }
  return retval;
}

RandomGenerator128::RandomGenerator128(uint128_t bound) :
    PatternGenerator128(bound),
    use_two_generators_(bound > uint128_t(uint64_max_)),
    low_gen_(0, use_two_generators_ ? uint64_max_ : (std::uint64_t)(bound - 1)),
    high_gen_(0, (std::uint64_t)(bound/uint64_max_ - 1))
{
}

uint128_t RandomGenerator128::Next()
{
  std::uint64_t low = low_gen_(engine_);
  std::uint64_t high = 0;

  if (use_two_generators_)
  {
    high = high_gen_(engine_);
  }

  uint128_t rand = low + ((uint128_t)high * uint64_max_);
  assert(rand < bound_);

  return rand;
}

//------------------------------------
//           Miscellaneous
//------------------------------------

// Returns the smallest factor of an integer and the quotient after
// division with the smallest factor.
void SmallestFactor(uint64_t n, uint64_t& factor, uint64_t& residue)
{
  for (uint64_t i = 2; i < n; i++)
  {
    if (n % i == 0)
    {
      factor = i;
      residue = n / i;
      return;
    }
  }
  factor = n;
  residue = 1;
}

// Helper function to get close-to-square layouts of arrays
// containing a given number of nodes.
void GetTiling(uint64_t num_elems, uint64_t& height, uint64_t& width)
{
  std::vector<uint64_t> factors;
  uint64_t residue = num_elems;
  uint64_t cur_factor;
  while (residue > 1)
  {
    SmallestFactor(residue, cur_factor, residue);
    factors.push_back(cur_factor);
  }

  height = 1;
  width = 1;
  for (uint64_t i = 0; i < factors.size(); i++)
  {
    if (i % 2 == 0)
      height *= factors[i];
    else
      width *= factors[i];
  }

  if (height > width)
  {
    uint64_t temp = height;
    height = width;
    width = temp;
  }
}

double LinearInterpolate(double x,
                         double x0, double x1,
                         double q0, double q1)
{
  double slope = (x0 == x1) ? 0 : (q1 - q0) / double(x1 - x0);
  return q0 + slope * (x - x0);
}

double BilinearInterpolate(double x, double y,
                           double x0, double x1,
                           double y0, double y1,
                           double q00, double q01, double q10, double q11)
{
  // Linear interpolate along x dimension.
  double qx0 = LinearInterpolate(x, x0, x1, q00, q10);
  double qx1 = LinearInterpolate(x, x0, x1, q01, q11);

  // Linear interpolate along y dimension.
  return LinearInterpolate(y, y0, y1, qx0, qx1);
}


/*******************************************************************
Author: David Ge (dge893@gmail.com, aka Wei Ge)
Last modified: 11/16/2020
Allrights reserved by David Ge

field source
********************************************************************/
#include "FieldSourceTss.h"
#include "TimeTssBase.h"

#include <boost/multiprecision/cpp_dec_float.hpp>

#define ih(i, h) ((h)*eMax+(i))

FieldSourceTss::FieldSourceTss()
{
	f0 = g0 = w0 = u0 = NULL;
	_initialized = false;
}

FieldSourceTss::~FieldSourceTss()
{
	cleanup();
}

void FieldSourceTss::cleanup()
{
	if (f0 != NULL)
	{
		free(f0); f0 = NULL;
	}
	if (g0 != NULL)
	{
		free(g0); g0 = NULL;
	}
	if (w0 != NULL)
	{
		free(w0); w0 = NULL;
	}
	if (u0 != NULL)
	{
		free(u0); u0 = NULL;
	}
}

int FieldSourceTss::initialize(SimStruct *params)
{
	int ret = ERR_OK;
	//size_t emMax2;
	size_t srcDim2;
	cleanup();
	pams = params;
	nx1 = pams->nx + 1; ny1 = pams->ny + 1; nz1 = pams->nz + 1;
	emMax = 2 * pams->kmax + 3;
	//emMax2 = emMax * emMax;
	srcDim = 2 * pams->kmax + 1;
	srcDim2 = srcDim * srcDim;
	f0 = (double *)malloc(srcDim2*sizeof(double));
	g0 = (double *)malloc(srcDim2*sizeof(double));
	w0 = (double *)malloc(srcDim2*sizeof(double));
	u0 = (double *)malloc(srcDim2*sizeof(double));
	if (f0 == NULL || g0 == NULL || w0 == NULL || u0 == NULL)
	{
		ret = ERR_OUTOFMEMORY;
	}
	else
	{
		for (unsigned int k = 0; k < srcDim2; k++)
		{
			f0[k] = g0[k] = w0[k] = u0[k] = 0.0;
		}

	}
	_initialized = true;
	return ret;
}

/*
	calculate field fource coefficients
*/
int FieldSourceTss::onInitialized(void *e0, void *m0)
{
	using namespace boost::multiprecision;
	int ret = ERR_OK;
	size_t srcDim2 = srcDim * srcDim;
	cpp_dec_float_100 *f  = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *g  = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *w  = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *u  = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *f2 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *g2 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *w2 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *u2 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *f1 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *g1 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *w1 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	cpp_dec_float_100 *u1 = (cpp_dec_float_100 *)malloc(srcDim2*sizeof(cpp_dec_float_100));
	//
	cpp_dec_float_100 *sr = (cpp_dec_float_100 *)malloc((emMax + 2)*sizeof(cpp_dec_float_100));
	if (sr == NULL || f == NULL || g == NULL || w == NULL || u == NULL
		|| f1 == NULL || g1 == NULL || w1 == NULL || u1 == NULL
		|| f2 == NULL || g2 == NULL || w2 == NULL || u2 == NULL)
		ret = ERR_OUTOFMEMORY;
	else
	{
		cpp_dec_float_100 *e = (cpp_dec_float_100 *)e0;
		cpp_dec_float_100 *m = (cpp_dec_float_100 *)m0;
		for (unsigned int k = 0; k < srcDim2; k++)
		{
			f[k]  = 0.0; g[k]  = 0.0; w[k]  = 0.0; u[k]  = 0.0;
		}
		cpp_dec_float_100 XN = 1.0;
		cpp_dec_float_100 X = 0.0;
		cpp_dec_float_100 dtN = 1.0;
		//sr[k] = (∆_t^k)/ k!
		for (unsigned int k = 0; k < emMax + 2; k++)
		{
			sr[k] = dtN / XN;
			X += 1.0;
			XN = XN * X;
			dtN = dtN * pams->dt;
		}
		/*
		S_m (2k+1)=∑_(h=0)^k▒〖∇^{2(k-h)} ×∑_(i=0)^2h▒〖m_i^{2(k-h)}   (d^(2h-i) J_m)/(dt^(2h-i) )〗〗
			      +∑_(h=0)^(k-1)▒〖∇^{2(k-h)-1} ×∑_(i=0)^(2h+1)▒〖e_i^{2(k-h)-1}   (d^(2h-i+1) J_e)/(dt^(2h-i+1) )〗〗
		S_e (2k+1)=∑_(h=0)^k▒〖∇^{2(k-h)} ×∑_(i=0)^2h▒〖e_i^{2(k-h)}   (d^(2h-i) J_e)/(dt^(2h-i) )〗〗
			      +∑_(h=0)^(k-1)▒〖∇^{2(k-h)-1} ×∑_(i=0)^(2h+1)▒〖m_i^{2(k-h)-1}   (d^(2h-i+1) J_m)/(dt^(2h-i+1) )〗〗
		S_m (2k)  =∑_(h=0)^(k-1)▒(∇^{2(k-1-h)} ×∑_(i=0)^(2h+1)▒〖m_i^{2(k-1-h)}   (d^(2h+1-i) J_m)/(dt^(2h+1-i) )〗
				  +∇^{2(k-h)-1} ×∑_(i=0)^2h▒〖e_i^{2(k-h)-1}   (d^(2h-i) J_e)/(dt^(2h-i) )〗)
		S_e (2k)  =∑_(h=0)^(k-1)▒(∇^{2(k-1-h)} ×∑_(i=0)^(2h+1)▒〖e_i^{2(k-1-h)}   (d^(2h+1-i) J_e)/(dt^(2h+1-i) )〗
				  +∇^{2(k-h)-1} ×∑_(i=0)^2h▒〖m_i^{2(k-h)-1}   (d^(2h-i) J_m)/(dt^(2h-i) )〗)

		S^h (k,∆_t)=(∆_t^2k)/(2k)! S_m (2k) + (∆_t^(2k+1))/(2k+1)! S_m (2k+1)
		S^e (k,∆_t)=(∆_t^2k)/(2k)! S_e (2k) + (∆_t^(2k+1))/(2k+1)! S_e (2k+1)

		SH=∑_(k=0)^(k_max)▒〖S^h (k,∆_t ) 〗-> get f and g -> C.F.dJm + C.G.dJe
		SE=∑_(k=0)^(k_max)▒〖S^e (k,∆_t ) 〗-> get u and w -> C.U.dJe + C.W.dJm

		S_m (2k)   -> f2, g2;  S_m (2k+1) -> f1, g1;
		S_e (2k)   -> u2, w2;  S_e (2k+1) -> u1, w1
		f = sr[2k]*f2 + sr[2k+1]*f1; g = sr[2k]*g2 + sr[2k+1]*g1
		u = sr[2k]*u2 + sr[2k+1]*u1; w = sr[2k]*w2 + sr[2k+1]*w1
		*/
		size_t t,t1=0;
		unsigned int kmax = pams->kmax;
		t = 0;
		for (unsigned int k = 0; k <= kmax; k++)
		{
			//temporary summation holders
			for (t = 0; t < srcDim2; t++)
			{
				f1[t] = 0.0; g1[t] = 0.0; w1[t] = 0.0; u1[t] = 0.0;
				f2[t] = 0.0; g2[t] = 0.0; w2[t] = 0.0; u2[t] = 0.0;
			}
			//H = C.F.dJm + C.G.dJe + ...
			//E = C.U.dJe + C.W.dJm + ...
			for (unsigned int h = 0; h <= k; h++)
			{
				//first part of S_m (2k+1)->f1:Jm, S_e (2k+1)->u1:Je
				//∇^{2(k-h)} -> row number 0...2kmax
				for (unsigned int i = 0; i <= 2 * h; i++)
				{
					//d^(2h-i), column number 2 * h - i
					t = Sr_ih(2 * h - i, 2 * (k - h));  //0...2kmax, 0...,2kmax
					t1 = I_ih(i, 2 * (k - h)); //m_i^{2(k-h)}, e_i^{2(k-h)}
					f1[t] += m[t1];
					u1[t] += e[t1];
				}
			}
			for (unsigned int h = 0; h < k; h++)
			{
				for (unsigned int i = 0; i <= 2 * h + 1; i++)
				{
					//second part of S_m (2k+1)->g1:Je, S_e (2k+1)->w1:Jm
					//∇^{2(k-h)-1} ->row number
					//column number 2h-i+1
					t = Sr_ih(2 * h - i + 1, 2 * (k - h) - 1);
					t1 = I_ih(i, 2 * (k - h) - 1); //e_i^{2(k-h)-1}, m_i^{2(k-h)-1}
					g1[t] += e[t1];
					w1[t] += m[t1];
					//first part of S_m (2k)->f2:Jm, S_e (2k)->u2:Je
					//∇^{2(k-1-h)} -> row number; column number d^(2h+1-i)
					t = Sr_ih(2 * h + 1 - i, 2 * (k - 1 - h));
					t1 = I_ih(i, 2 * (k - 1 - h)); //m_i^{2(k-1-h)}, e_i^{2(k-1-h)}
					f2[t] += m[t1];
					u2[t] += e[t1];
				}
				//second part of  S_m (2k)->g2:Je, S_e (2k)->w2:Jm
				//∇^{2(k-h)-1} -> row number; column number d^(2h-i)
				for (unsigned int i = 0; i <= 2 * h; i++)
				{
					t = Sr_ih(2 * h - i, 2 * (k - h) - 1);
					t1 = I_ih(i, 2 * (k - h) - 1); //e_i^{2(k-h)-1}, m_i^{2(k-h)-1}
					g2[t] += e[t1];
					w2[t] += m[t1];
				}
			}
			//add to the results
			for (t = 0; t < srcDim2; t++)
			{
				f[t] += f2[t] * sr[2 * k] + f1[t] * sr[2 * k + 1];
				g[t] += g2[t] * sr[2 * k] + g1[t] * sr[2 * k + 1];
				u[t] += u2[t] * sr[2 * k] + u1[t] * sr[2 * k + 1];
				w[t] += w2[t] * sr[2 * k] + w1[t] * sr[2 * k + 1];
			}
		}
		//convert to double
		for (t = 0; t < srcDim2; t++)
		{
			f0[t] = f[t].convert_to<double>();
			g0[t] = g[t].convert_to<double>();
			w0[t] = w[t].convert_to<double>();
			u0[t] = u[t].convert_to<double>();
		}
	}
	if(f != NULL) free(f);
	if(g != NULL) free(g);
	if(w != NULL) free(w);
	if(u != NULL) free(u);
	//
	if (f1 != NULL) free(f1);
	if (g1 != NULL) free(g1);
	if (w1 != NULL) free(w1);
	if (u1 != NULL) free(u1);
	//
	if (f2 != NULL) free(f2);
	if (g2 != NULL) free(g2);
	if (w2 != NULL) free(w2);
	if (u2 != NULL) free(u2);
	//
	if(sr != NULL) free(sr);
	return ret;
}

int FieldSourceTss::applySourceToFields(TimeTssBase *timeModule)
{
	if (timeModule->FieldType() == Field_type_3D)
	{
		return applySources(timeModule->GetTimeValue(), timeModule->GetTimeIndex(), timeModule->GetFieldE(), timeModule->GetFieldH());
	}
	else if (timeModule->FieldType() == Field_type_z_rotateSymmetry)
	{
		return applyToZrotateSymmetry(timeModule->GetTimeValue(), timeModule->GetTimeIndex(), timeModule->GetFieldZrotateSymmetryE(), timeModule->GetFieldZrotateSymmetryH());
	}
	else
	{
		return ERR_SOURCE_NOT_SUPPORT;
	}
}


#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>

#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/set_algorithm.hpp>

template<class D, class S>
template<class Functor, class Iterator>
void
dionysus::Rips<D,S>::
generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
{
    auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; };

    // current      = empty
    // candidates   = everything
    VertexContainer current;
    VertexContainer candidates(bg, end);
    bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f);
}

template<class D, class S>
template<class Functor, class Iterator>
void
dionysus::Rips<D,S>::
vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
{
    auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; };

    // current      = [v]
    // candidates   = everything - [v]
    VertexContainer current; current.push_back(v);
    VertexContainer candidates;
    for (Iterator cur = bg; cur != end; ++cur)
        if (*cur != v && neighbor(v, *cur))
            candidates.push_back(*cur);

    bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f);
}

template<class D, class S>
template<class Functor, class Iterator>
void
dionysus::Rips<D,S>::
edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
{
    auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; };

    // current      = [u,v]
    // candidates   = everything - [u,v]
    VertexContainer current; current.push_back(u); current.push_back(v);

    VertexContainer candidates;
    for (Iterator cur = bg; cur != end; ++cur)
        if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur))
            candidates.push_back(*cur);

    bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f);
}

template<class D, class S>
template<class Functor, class Iterator>
void
dionysus::Rips<D,S>::
cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const
{
    namespace ba = boost::adaptors;

    auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; };

    // current      = s
    VertexContainer current(s.begin(), s.end());

    // candidates   = everything - s     that is a neighbor of every vertex in the simplex
    VertexContainer candidates;
    boost::set_difference(std::make_pair(bg, end) |
                                ba::filtered([this,&s,&neighbor](Vertex cur)
                                             { for (auto& v : s)
                                                   if (!neighbor(v, cur))
                                                       return false;
                                             }),
                          s,
                          std::back_inserter(candidates));

    bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false);
}

template<class D, class S>
template<class Functor, class NeighborTest>
void
dionysus::Rips<D,S>::
bron_kerbosch(VertexContainer&                          current,
              const VertexContainer&                    candidates,
              typename VertexContainer::const_iterator  excluded,
              Dimension                                 max_dim,
              const NeighborTest&                       neighbor,
              const Functor&                            functor,
              bool                                      check_initial)
{
    if (check_initial && !current.empty())
        functor(Simplex(current));

    if (current.size() == static_cast<size_t>(max_dim) + 1)
        return;

    for (auto cur = std::next(excluded); cur != candidates.end(); ++cur)
    {
        current.push_back(*cur);

        VertexContainer new_candidates;
        for (auto ccur = candidates.begin(); ccur != cur; ++ccur)
            if (neighbor(*ccur, *cur))
                new_candidates.push_back(*ccur);
        size_t ex = new_candidates.size();
        for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur)
            if (neighbor(*ccur, *cur))
                new_candidates.push_back(*ccur);
        excluded  = new_candidates.begin() + (ex - 1);

        bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor);
        current.pop_back();
    }
}

template<class Distances_, class Simplex_>
typename dionysus::Rips<Distances_, Simplex_>::DistanceType
dionysus::Rips<Distances_, Simplex_>::
distance(const Simplex& s1, const Simplex& s2) const
{
    DistanceType mx = 0;
    for (auto a : s1)
        for (auto b : s2)
            mx = std::max(mx, distances_(a,b));
    return mx;
}

template<class Distances_, class Simplex_>
typename dionysus::Rips<Distances_, Simplex_>::DistanceType
dionysus::Rips<Distances_, Simplex_>::
max_distance() const
{
    DistanceType mx = 0;
    for (IndexType a = distances_.begin(); a != distances_.end(); ++a)
        for (IndexType b = std::next(a); b != distances_.end(); ++b)
            mx = std::max(mx, distances_(a,b));
    return mx;
}

template<class Distances_, class Simplex_>
typename dionysus::Rips<Distances_, Simplex_>::DistanceType
dionysus::Rips<Distances_, Simplex_>::Evaluator::
operator()(const Simplex& s) const
{
    DistanceType mx = 0;
    for (auto a = s.begin(); a != s.end(); ++a)
        for (auto b = std::next(a); b != s.end(); ++b)
            mx = std::max(mx, distances_(*a,*b));
    return mx;
}


/**
 * @file solver.hpp
 * @author Paul Kirth
 * @date 3/20/15
 */

#ifndef _SOLVER_HPP_
#define _SOLVER_HPP_

#include <string>
#include <fstream>
#include <algorithm>
#include <boost/dynamic_bitset.hpp>

struct node {
  size_t count;
  size_t id;
  bool val;
};

struct clause {
  int a;
  int b;
};

struct solution {
  boost::dynamic_bitset<> ans;
  int value;
  solution(size_t size = 0, int val = 0) : ans(size) { value = val; };
  solution(boost::dynamic_bitset<> bs, int val = 0) : ans(bs), value(val) {}
};

/**
 * Finds solutions for MAX 2 SAT instances
 * has exact solution, and approximate solutions
 *
 */
class solver {
public:
  solver(std::string file);

  solution exact();
  solution approx();
  void reset()
  {
    x.reset();
  }

private:
  boost::dynamic_bitset<> x;
  std::vector<clause> clauses;
  std::vector<node> nodes;

  inline bool check(const clause &c) noexcept {
    bool a;
    bool b;
    int ida = abs(c.a), idb = abs(c.b);
    a = (c.a > 0) ? x[ida] : !x[ida];
    b = (c.b > 0) ? x[idb] : !x[idb];
    return a || b;
  }

  void increment(boost::dynamic_bitset<> &bitset) {
    auto len = std::max(bitset.size(), (size_t)1);
    // printf("count:%d\nlen:  %d\n", bitset.count(), len);
    for (int loop = 0; loop < len; ++loop) {
      if ((bitset[loop] ^= 0x1) == 0x1) {
        break;
      }
    }
  }

}; // end class solver

#endif // end solver.hpp


//
//  common.hpp
//  Elasticity
//
//  Created by Wim van Rees on 2/15/16.
//  Copyright © 2016 Wim van Rees. All rights reserved.
//

#ifndef common_hpp
#define common_hpp

#include <iostream>
#include <cassert>
#include <random>
#include <limits>
#include <utility>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <map>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <array>
#include <set>
#include <memory>

#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <Eigen/StdVector>

//#include "ArgumentParser.h"

typedef double Real;
typedef unsigned long long tUint;
typedef std::vector<Eigen::Matrix2d, Eigen::aligned_allocator<Eigen::Matrix2d>> tVecMat2d;
typedef std::vector<Eigen::Matrix3d, Eigen::aligned_allocator<Eigen::Matrix3d>> tVecMat3d;
enum MeshLayer {single, bottom, top};

//#define USETANTHETA

// little helper to avoid unused warnings when something is called in release mode and variable only checked in debug
#define _unused(x) ((void)(x))

namespace rnd
{
    /*! global random number generator */
    static std::mt19937 gen;
}

namespace helpers
{

    /*! convert an integer to a string with a specified number of zeros */
    inline std::string ToString(int value,int digitsCount)
    {
        std::ostringstream os;
        os<<std::setfill('0')<<std::setw(digitsCount)<<value;
        return os.str();
    }

    /*! check if two numbers are close (within some accuracy) */
    inline bool isclose(const Real val1, const Real val2, const bool printOnly=false, const Real rtol=1e-5, const Real atol=1e-8)
    {
        const Real diff = std::abs(val1-val2) ;
        bool retval = true;
        if(diff > (atol + rtol * std::abs(val2)))
            retval = false;
        if(diff > (atol + rtol * std::abs(val1)))
            retval = false;

        if(printOnly && not retval)
        {
            std::cout << "PROBLEM : " << val1 << "\t" << val2 << "\t" << diff << std::endl;
            retval = true;
        }

        return retval;
    }

    inline bool isPointInProjectedTriangle(const Real x, const Real y, const Eigen::Vector3d & v0, const Eigen::Vector3d & v1, const Eigen::Vector3d & v2)
    {
        // dont use the third dimension
        const Real xp[3] = {v0(0),v1(0),v2(0)};
        const Real yp[3] = {v0(1),v1(1),v2(1)};

        const int npoly = 3; // triangle

        int i,j,c=0;
        for (i = 0, j = npoly-1; i < npoly; j = i++) {
            if ((((yp[i] <= y) && (y < yp[j])) ||
                 ((yp[j] <= y) && (y < yp[i]))) &&
                (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
                c = !c;
        }
        return c;
    }

    inline Eigen::Vector3d getBaryCentricWeights2D(const Eigen::Vector3d & v0, const Eigen::Vector3d & v1, const Eigen::Vector3d & v2, const Real x, const Real y)
    {
        assert(isPointInProjectedTriangle(x, y, v0, v1, v2));

        Eigen::Vector3d me;
        me << x, y, 0;
        // compute my point in barycentric coordinates of undeformed configuration
        // Compute barycentric coordinates (u, v, w) for
        // point p with respect to triangle (a, b, c)
        // http://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates
        const Eigen::Vector3d tmp0 = v1 - v0;
        const Eigen::Vector3d tmp1 = v2 - v0;
        const Eigen::Vector3d tmp2 = me - v0;
        const Real d00 = tmp0.dot(tmp0);
        const Real d01 = tmp0.dot(tmp1);
        const Real d11 = tmp1.dot(tmp1);
        const Real d20 = tmp2.dot(tmp0);
        const Real d21 = tmp2.dot(tmp1);
        const Real denom = d00 * d11 - d01 * d01;
        const Real lambda1 = (d11 * d20 - d01 * d21) / denom;
        const Real lambda2 = (d00 * d21 - d01 * d20) / denom;
        const Real lambda0 = 1.0 - lambda1 - lambda2;
        // if me == v0 --> lambda0 = 1
        // if me == v1 --> lambda1 = 1
        // if me == v2 --> lambda2 = 1

        Eigen::Vector3d retval;
        retval << lambda0, lambda1, lambda2;
        return retval;
    }

    template<int component>
    inline Real getDisplacementOfPointInRestTriangle(const Eigen::Vector3d & rv0, const Eigen::Vector3d & rv1, const Eigen::Vector3d & rv2, const Eigen::Vector3d & v0, const Eigen::Vector3d & v1, const Eigen::Vector3d & v2, const Real x, const Real y)
    {

        const Eigen::Vector3d lambdas = getBaryCentricWeights2D(rv0, rv1, rv2, x, y);

        const Real displ_0 = (v0 - rv0)(component);
        const Real displ_1 = (v1 - rv1)(component);
        const Real displ_2 = (v2 - rv2)(component);

        // get my displacement
        return -(lambdas(0)*displ_0 + lambdas(1)*displ_1 + lambdas(2)*displ_2);
    }

    inline Real interpolateOverTriangle(const Eigen::Vector3d & v0, const Eigen::Vector3d & v1, const Eigen::Vector3d & v2, const Real x, const Real y)
    {
        // linear interpolation over the triangle
        const Real A = v0(1)*(v1(2)-v2(2)) + v1(1)*(v2(2)-v0(2)) + v2(1)*(v0(2)-v1(2));
        const Real B = v0(2)*(v1(0)-v2(0)) + v1(2)*(v2(0)-v0(0)) + v2(2)*(v0(0)-v1(0));
        const Real C = v0(0)*(v1(1)-v2(1)) + v1(0)*(v2(1)-v0(1)) + v2(0)*(v0(1)-v1(1));
        const Real D = v0(0)*(v1(1)*v2(2)-v1(2)*v2(1)) + v1(0)*(v2(1)*v0(2)-v2(2)*v0(1)) + v2(0)*(v0(1)*v1(2)-v0(2)*v1(1));

        const Real ptZ = (D-A*x-B*y)/C;

        return ptZ;
    }

    inline Real computeDistanceToLineSegment(const Eigen::Vector2d & l1, const Eigen::Vector2d & l2, const Eigen::Vector2d & pt)
    {
        //http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
        const Real Lsq = (l1-l2).squaredNorm();
        if(Lsq < std::numeric_limits<Real>::epsilon())
            return (l1-pt).norm();

        const Real t =  std::max(0.0, std::min(1.0, (pt-l1).dot(l2-l1) / Lsq));
        const Eigen::Vector2d projection = l1 + t * (l2 - l1);
        return (projection - pt).norm();
    }

    inline int getIntersectionPointBetweenVectors(const Eigen::Vector2d & pt1, const Eigen::Vector2d & n1, const Eigen::Vector2d & pt2, const Eigen::Vector2d & n2, Eigen::Vector2d & result)
    {
        // return 0 if intersection is found
        // return 1 if an intersection is found, but beyond the extent of pt1+n1 and pt2+n2
        // return 2 if no intersection is found (parallel vectors)

        const Eigen::Vector2d n1_hat = n1.normalized();
        const Eigen::Vector2d n2_hat = n2.normalized();

        const Real pt1_vec1 = pt1.dot(n1_hat);
        const Real pt1_vec2 = pt1.dot(n2_hat);
        const Real pt2_vec1 = pt2.dot(n1_hat);
        const Real pt2_vec2 = pt2.dot(n2_hat);
        const Real vec1_vec2 = n1_hat.dot(n2_hat);

        // deal with orthogonal
        if(std::abs(vec1_vec2 - 1) < std::numeric_limits<Real>::epsilon())
        {
            if((pt1-pt2).norm()  < std::numeric_limits<Real>::epsilon())
            {
                result = pt1;
                return 0;
            }
            else
            {
                return 2;
            }
        }

        const Real beta1 = (pt2_vec1 + (pt1_vec2 - pt2_vec2) * vec1_vec2 - pt1_vec1) / (1.0 - std::pow(vec1_vec2,2));
        const Real beta2 = (pt1_vec2 + (pt2_vec1 - pt1_vec1) * vec1_vec2 - pt2_vec2) / (1.0 - std::pow(vec1_vec2,2));

        //const Eigen::Vector2d retval1 = pt1 + beta1*n1_hat;
        const Eigen::Vector2d retval2 = pt2 + beta2*n2_hat;

        // set the intersection point
        result = retval2;

        const bool inRange_1 = (beta1 >= 0 && beta1 <= n1.norm());
        const bool inRange_2 = (beta2 >= 0 && beta2 <= n2.norm());
        return (inRange_1 && inRange_2) ? 0 : 1;
    }

    inline std::string removeExtension(const std::string input, const std::string extension)
    {
        // remove extension from input. Assume extension is given as (eg) .vtp (including the period)
        // if not found, return input unaltered
        const std::size_t found_ext = input.rfind(extension);
        const std::string output = (found_ext != std::string::npos ? input.substr(0, found_ext) : input);
        return output;
    }

    inline void catastrophe(std::string s, const char* file, const int line, const bool isFatal=true)    // write ``error: s and exit program
    {
        std::cerr << "Something went wrong, error: " << s << std::endl;
        std::cerr << "Called from file : " << file << " at line number " << line << std::endl;
        if(isFatal)
        {
            std::cout << "Exiting..." << std::endl;
            std::exit(1);
        }
    }

    template<class Matrix>
    inline void write_matrix_binary(const std::string & filename, const Matrix& matrix)
    {
        // from https://stackoverflow.com/a/25389481
        std::ofstream out(filename, std::ios::out | std::ios::binary | std::ios::trunc);
        typename Matrix::Index rows=matrix.rows(), cols=matrix.cols();
        out.write((char*) (&rows), sizeof(typename Matrix::Index));
        out.write((char*) (&cols), sizeof(typename Matrix::Index));
        out.write((char*) matrix.data(), rows*cols*sizeof(typename Matrix::Scalar) );
        out.close();
    }

    template<class Matrix>
    inline void read_matrix_binary(const std::string & filename, Matrix& matrix)
    {
        // from https://stackoverflow.com/a/25389481
        std::ifstream in(filename, std::ios::in | std::ios::binary);
        typename Matrix::Index rows=0, cols=0;
        in.read((char*) (&rows),sizeof(typename Matrix::Index));
        in.read((char*) (&cols),sizeof(typename Matrix::Index));
        matrix.resize(rows, cols);
        in.read( (char *) matrix.data() , rows*cols*sizeof(typename Matrix::Scalar) );
        in.close();
    }

    template<class Matrix>
    inline void write_vecmat_binary(const std::string & filename, const std::vector<Matrix, Eigen::aligned_allocator<Matrix>> & vecmat)
    {
        // from https://stackoverflow.com/a/25389481
        std::ofstream out(filename, std::ios::out | std::ios::binary | std::ios::trunc);
        const size_t vecsize = vecmat.size();
        if(vecsize==0) return;
        typename Matrix::Index rows=vecmat[0].rows(), cols=vecmat[0].cols();
        out.write((char*) &vecsize, sizeof(vecsize));
        out.write((char*) (&rows), sizeof(typename Matrix::Index));
        out.write((char*) (&cols), sizeof(typename Matrix::Index));
        out.write((char*)&vecmat[0], vecsize * rows*cols*sizeof(typename Matrix::Scalar));
        out.close();
    }

    template<class Matrix>
    inline void read_vecmat_binary(const std::string & filename, std::vector<Matrix, Eigen::aligned_allocator<Matrix>> & vecmat)
    {
        std::ifstream in(filename, std::ios::in | std::ios::binary);
        size_t vecsize = 0;
        typename Matrix::Index rows=0, cols=0;

        in.read((char*) (&vecsize),sizeof(vecsize));
        assert(vecsize > 0);

        in.read((char*) (&rows),sizeof(typename Matrix::Index));
        in.read((char*) (&cols),sizeof(typename Matrix::Index));

        assert(rows == Matrix::RowsAtCompileTime);
        assert(cols == Matrix::ColsAtCompileTime);

        vecmat.resize(vecsize);
        in.read( (char *) &vecmat[0] , vecsize * rows*cols*sizeof(typename Matrix::Scalar) );
        in.close();
    }
}

namespace Eigen
{
    /*! typedef vector of booleans */
    typedef Matrix<bool, 3, 1> Vector3b;

    /*! typedef vector of booleans */
    typedef Matrix<bool, Dynamic, 1> VectorXb;
    typedef Matrix<bool, Dynamic, Dynamic> MatrixXb;
}

#endif /* common_hpp */


///////////////////////////////////////////////////////////////////////////////
// weighted_tail_variate_means.hpp
//
//  Copyright 2006 Daniel Egloff, Olivier Gygi. 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_ACCUMULATORS_STATISTICS_WEIGHTED_TAIL_VARIATE_MEANS_HPP_DE_01_01_2006
#define BOOST_ACCUMULATORS_STATISTICS_WEIGHTED_TAIL_VARIATE_MEANS_HPP_DE_01_01_2006

#include <numeric>
#include <vector>
#include <limits>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <boost/throw_exception.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/accumulators/numeric/functional.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/statistics_fwd.hpp>
#include <boost/accumulators/statistics/tail.hpp>
#include <boost/accumulators/statistics/tail_variate.hpp>
#include <boost/accumulators/statistics/tail_variate_means.hpp>
#include <boost/accumulators/statistics/weighted_tail_mean.hpp>
#include <boost/accumulators/statistics/parameters/quantile_probability.hpp>

#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
#endif

namespace boost
{
    // for _BinaryOperatrion2 in std::inner_product below
    // multiplies two values and promotes the result to double
    namespace numeric { namespace functional
    {
        ///////////////////////////////////////////////////////////////////////////////
        // numeric::functional::multiply_and_promote_to_double
        template<typename T, typename U>
        struct multiply_and_promote_to_double
          : multiplies<T, double const>
        {
        };
    }}
}

namespace boost { namespace accumulators
{

namespace impl
{
    /**
        @brief Estimation of the absolute and relative weighted tail variate means (for both left and right tails)

        For all \f$j\f$-th variates associated to the

        \f[
            \lambda = \inf\left\{ l \left| \frac{1}{\bar{w}_n}\sum_{i=1}^{l} w_i \geq \alpha \right. \right\}
        \f]

        smallest samples (left tail) or the weighted mean of the

        \f[
            n + 1 - \rho = n + 1 - \sup\left\{ r \left| \frac{1}{\bar{w}_n}\sum_{i=r}^{n} w_i \geq (1 - \alpha) \right. \right\}
        \f]

        largest samples (right tail), the absolute weighted tail means \f$\widehat{ATM}_{n,\alpha}(X, j)\f$
        are computed and returned as an iterator range. Alternatively, the relative weighted tail means
        \f$\widehat{RTM}_{n,\alpha}(X, j)\f$ are returned, which are the absolute weighted tail means
        normalized with the weighted (non-coherent) sample tail mean \f$\widehat{NCTM}_{n,\alpha}(X)\f$.

        \f[
            \widehat{ATM}_{n,\alpha}^{\mathrm{right}}(X, j) =
                \frac{1}{\sum_{i=\rho}^n w_i}
                \sum_{i=\rho}^n w_i \xi_{j,i}
        \f]

        \f[
            \widehat{ATM}_{n,\alpha}^{\mathrm{left}}(X, j) =
                \frac{1}{\sum_{i=1}^{\lambda}}
                \sum_{i=1}^{\lambda} w_i \xi_{j,i}
        \f]

        \f[
            \widehat{RTM}_{n,\alpha}^{\mathrm{right}}(X, j) =
                \frac{\sum_{i=\rho}^n w_i \xi_{j,i}}
            {\sum_{i=\rho}^n w_i \widehat{NCTM}_{n,\alpha}^{\mathrm{right}}(X)}
        \f]

        \f[
            \widehat{RTM}_{n,\alpha}^{\mathrm{left}}(X, j) =
                \frac{\sum_{i=1}^{\lambda} w_i \xi_{j,i}}
            {\sum_{i=1}^{\lambda} w_i \widehat{NCTM}_{n,\alpha}^{\mathrm{left}}(X)}
        \f]
    */

    ///////////////////////////////////////////////////////////////////////////////
    // weighted_tail_variate_means_impl
    //  by default: absolute weighted_tail_variate_means
    template<typename Sample, typename Weight, typename Impl, typename LeftRight, typename VariateType>
    struct weighted_tail_variate_means_impl
      : accumulator_base
    {
        typedef typename numeric::functional::average<Weight, Weight>::result_type float_type;
        typedef typename numeric::functional::average<typename numeric::functional::multiplies<VariateType, Weight>::result_type, Weight>::result_type array_type;
        // for boost::result_of
        typedef iterator_range<typename array_type::iterator> result_type;

        weighted_tail_variate_means_impl(dont_care) {}

        template<typename Args>
        result_type result(Args const &args) const
        {
            float_type threshold = sum_of_weights(args)
                             * ( ( is_same<LeftRight, left>::value ) ? args[quantile_probability] : 1. - args[quantile_probability] );

            std::size_t n = 0;
            Weight sum = Weight(0);

            while (sum < threshold)
            {
                if (n < static_cast<std::size_t>(tail_weights(args).size()))
                {
                    sum += *(tail_weights(args).begin() + n);
                    n++;
                }
                else
                {
                    if (std::numeric_limits<float_type>::has_quiet_NaN)
                    {
                        std::fill(
                            this->tail_means_.begin()
                          , this->tail_means_.end()
                          , std::numeric_limits<float_type>::quiet_NaN()
                        );
                    }
                    else
                    {
                        std::ostringstream msg;
                        msg << "index n = " << n << " is not in valid range [0, " << tail(args).size() << ")";
                        boost::throw_exception(std::runtime_error(msg.str()));
                    }
                }
            }

            std::size_t num_variates = tail_variate(args).begin()->size();

            this->tail_means_.clear();
            this->tail_means_.resize(num_variates, Sample(0));

            this->tail_means_ = std::inner_product(
                tail_variate(args).begin()
              , tail_variate(args).begin() + n
              , tail_weights(args).begin()
              , this->tail_means_
              , numeric::functional::plus<array_type const, array_type const>()
              , numeric::functional::multiply_and_promote_to_double<VariateType const, Weight const>()
            );

            float_type factor = sum * ( (is_same<Impl, relative>::value) ? non_coherent_weighted_tail_mean(args) : 1. );

            std::transform(
                this->tail_means_.begin()
              , this->tail_means_.end()
              , this->tail_means_.begin()
              , std::bind2nd(numeric::functional::divides<typename array_type::value_type const, float_type const>(), factor)
            );

            return make_iterator_range(this->tail_means_);
        }

    private:

        mutable array_type tail_means_;

    };

} // namespace impl

///////////////////////////////////////////////////////////////////////////////
// tag::absolute_weighted_tail_variate_means
// tag::relative_weighted_tail_variate_means
//
namespace tag
{
    template<typename LeftRight, typename VariateType, typename VariateTag>
    struct absolute_weighted_tail_variate_means
      : depends_on<non_coherent_weighted_tail_mean<LeftRight>, tail_variate<VariateType, VariateTag, LeftRight>, tail_weights<LeftRight> >
    {
        typedef accumulators::impl::weighted_tail_variate_means_impl<mpl::_1, mpl::_2, absolute, LeftRight, VariateType> impl;
    };
    template<typename LeftRight, typename VariateType, typename VariateTag>
    struct relative_weighted_tail_variate_means
      : depends_on<non_coherent_weighted_tail_mean<LeftRight>, tail_variate<VariateType, VariateTag, LeftRight>, tail_weights<LeftRight> >
    {
        typedef accumulators::impl::weighted_tail_variate_means_impl<mpl::_1, mpl::_2, relative, LeftRight, VariateType> impl;
    };
}

///////////////////////////////////////////////////////////////////////////////
// extract::weighted_tail_variate_means
// extract::relative_weighted_tail_variate_means
//
namespace extract
{
    extractor<tag::abstract_absolute_tail_variate_means> const weighted_tail_variate_means = {};
    extractor<tag::abstract_relative_tail_variate_means> const relative_weighted_tail_variate_means = {};

    BOOST_ACCUMULATORS_IGNORE_GLOBAL(weighted_tail_variate_means)
    BOOST_ACCUMULATORS_IGNORE_GLOBAL(relative_weighted_tail_variate_means)
}

using extract::weighted_tail_variate_means;
using extract::relative_weighted_tail_variate_means;

// weighted_tail_variate_means<LeftRight, VariateType, VariateTag>(absolute) -> absolute_weighted_tail_variate_means<LeftRight, VariateType, VariateTag>
template<typename LeftRight, typename VariateType, typename VariateTag>
struct as_feature<tag::weighted_tail_variate_means<LeftRight, VariateType, VariateTag>(absolute)>
{
    typedef tag::absolute_weighted_tail_variate_means<LeftRight, VariateType, VariateTag> type;
};

// weighted_tail_variate_means<LeftRight, VariateType, VariateTag>(relative) -> relative_weighted_tail_variate_means<LeftRight, VariateType, VariateTag>
template<typename LeftRight, typename VariateType, typename VariateTag>
struct as_feature<tag::weighted_tail_variate_means<LeftRight, VariateType, VariateTag>(relative)>
{
    typedef tag::relative_weighted_tail_variate_means<LeftRight, VariateType, VariateTag> type;
};

}} // namespace boost::accumulators

#ifdef _MSC_VER
# pragma warning(pop)
#endif

#endif