#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <set>
#include <boost/graph/adjacency_list.hpp>
#include <boost/pending/disjoint_sets.hpp>
#include <boost/graph/incremental_components.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/filtered_graph.hpp>

using namespace std;

/* CROCO - Complete Redundant Orthology Clusters */
namespace boost {
	enum vertex_component_t { vertex_component = 111 };
	BOOST_INSTALL_PROPERTY(vertex, component);
}
using namespace boost;

typedef map<string, int> GeneID;
typedef map<string, int> stringcounts;
typedef map<int, string> invGeneID;
typedef set<string> setstring;
typedef set<int> setint;
template <typename ComponentMap>
struct vertexComponent {

	vertexComponent() {}

	vertexComponent(ComponentMap component, int f_component) : m_component(component), m_f_component(f_component) {}

	template <typename Vertex>
	bool operator()(const Vertex& v) const {
		return (get(m_component, v) == m_f_component);
	}

	ComponentMap m_component;
	int m_f_component;
};

/* MAIN */
int main(int argc, char* argv[]) {
	/* USAGE */
	if (argc != 2) {
		std::cerr << "Usage: " << argv[0] << " genelinks (results to STDOUT)" << std::endl;
		exit(1);
	}

	/* DECLARATIONS */
	char      line[200];
	char      scanL[20];
	char      scanR[20];

	int lico = 0;
	GeneID ids;
	invGeneID name;
	int runningid = 0;

	typedef adjacency_list<vecS, vecS, undirectedS, property<vertex_component_t, int> > Graph;

	typedef property_map<Graph, vertex_component_t>::type ComponentMap;
	typedef filtered_graph<Graph, keep_all,	vertexComponent<ComponentMap> > FilteredGraph;

	graph_traits < Graph >::vertex_iterator vi, vi_end;
	graph_traits < Graph >::out_edge_iterator oei, oei_end;
	graph_traits < FilteredGraph >::vertex_iterator fvi, fvi_end;
	graph_traits < FilteredGraph >::out_edge_iterator foei, foei_end;

	Graph * g;
	FilteredGraph * fg;

	g = new Graph;

	// READ LINK FILE and add edges
	std::ifstream linkFile(argv[1]);
	if (linkFile.fail()) {
		std::cerr << "Error: could not read from file " << argv[1] << std::endl;
		exit(1);
	}
	std::cerr << "Reading gene links " << argv[1] << std::endl;
	while (!linkFile.eof()) {
		linkFile.getline(line, 200);
		if (linkFile.eof()) break;

		if (++lico % 1000 == 0) std::cerr << "\r" << lico;
		sscanf(line, "%s%s", scanL, scanR);
		string scanLstring = string(scanL);
		string scanRstring = string(scanR);

		if (ids.find(scanLstring) == ids.end()) {
			ids.insert(pair<string,int>(scanLstring,++runningid));
			name.insert(pair<int,string>(runningid,scanLstring));
		}
		if (ids.find(scanRstring) == ids.end()) {
			ids.insert(pair<string,int>(scanRstring,++runningid));
			name.insert(pair<int,string>(runningid,scanRstring));
		}
		// add edge
		add_edge(ids.find(scanLstring)->second, ids.find(scanRstring)->second, *g);
	}
	linkFile.close();
	cerr << "\r" << lico << " gene pairs read" << std::endl;

	//// SHOW GRAPH
	/*	std::cerr<<std::endl<<"Graph out-edges:"<<std::endl;
	 for (tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi) {
	 std::cerr<<name[*vi] << " " << *vi <<" outedges - ";
	 for(tie(oei, oei_end)=out_edges(*vi, *g); oei != oei_end; ++oei) {
	 std::cerr<<name[source(*oei, *g)]<<"<->"<<name[target(*oei, *g)]<<"  ";
	 }
	 std::cerr<<std::endl;
	 }
	 std::cerr<<std::endl;
	 */	//// SHOW GRAPH

	int numComponents = connected_components(*g, get(vertex_component, *g));

	std::cerr<<"Graph has "<<numComponents<<" components."<<std::endl;

	string familyprefix = "GF";

	int connex = 0;
	int compo = numComponents;
	int clusterNumber = 0;
	for(int i=0; i<numComponents; i++) {
		if (--compo % 100 == 0) std::cerr << "\r" << compo << " ";
		keep_all efilter;
		vertexComponent<ComponentMap> vfilter(get(vertex_component, *g), i);
		fg = new FilteredGraph(*g, efilter, vfilter);

		//std::cerr<<"Filtered graph (component "<<i<<") ";

		setstring Genes;
		int elements = 0;
		for (tie(fvi, fvi_end) = vertices(*fg); fvi != fvi_end; ++fvi) {
			Genes.insert(name[*fvi]);
			++elements;
		}
		// printing
		++connex;
		if (elements > 1 && elements == Genes.size()) {
			++clusterNumber;
			for (setstring::const_iterator it = Genes.begin(); it != Genes.end(); ++it) {
				std::cout << familyprefix << clusterNumber << "\t" << *it << std::endl;
			}
		}
		else std::cerr << "D'oh!" << endl;

		delete fg;
		fg = NULL;
	}

	std::cerr << "\rFound " << clusterNumber << " (" << connex << ") connected components (gene families)."<<std::endl;

	delete g;
	g = NULL;

	return EXIT_SUCCESS;

} // END OF MAIN


//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright Contributors to the OpenEXR Project.
//

// clang-format off

#include <Python.h>
#include <boost/python.hpp>
#include <boost/python/make_constructor.hpp>
#include <boost/format.hpp>
#include <ImathVec.h>
#include "PyImath.h"
#include "PyImathMathExc.h"
#include "PyImathEuler.h"
#include "PyImathDecorators.h"
#include "PyImathExport.h"
#include "PyImathOperators.h"

// XXX incomplete array wrapping, docstrings missing

namespace PyImath {
template<> const char *PyImath::EulerfArray::name() { return "EulerfArray"; }
template<> const char *PyImath::EulerdArray::name() { return "EulerdArray"; }
}

namespace PyImath {
using namespace boost::python;
using namespace IMATH_NAMESPACE;

template <class T> struct EulerName { static const char *value; };
template<> const char *EulerName<float>::value  = "Eulerf";
template<> const char *EulerName<double>::value = "Eulerd";

template <class T>
static std::string nameOfOrder(typename IMATH_NAMESPACE::Euler<T>::Order order)
{
    switch(order)
    {
        case IMATH_NAMESPACE::Euler<T>::XYZ:
            return "EULER_XYZ";
        case IMATH_NAMESPACE::Euler<T>::XZY:
            return "EULER_XZY";
        case IMATH_NAMESPACE::Euler<T>::YZX:
            return "EULER_YZX";
        case IMATH_NAMESPACE::Euler<T>::YXZ:
            return "EULER_YXZ";
        case IMATH_NAMESPACE::Euler<T>::ZXY:
            return "EULER_ZXY";
        case IMATH_NAMESPACE::Euler<T>::ZYX:
            return "EULER_ZYX";
        case IMATH_NAMESPACE::Euler<T>::XZX:
            return "EULER_XZX";
        case IMATH_NAMESPACE::Euler<T>::XYX:
            return "EULER_XYX";
        case IMATH_NAMESPACE::Euler<T>::YXY:
            return "EULER_YXY";
        case IMATH_NAMESPACE::Euler<T>::YZY:
            return "EULER_YZY";
        case IMATH_NAMESPACE::Euler<T>::ZYZ:
            return "EULER_ZYZ";
        case IMATH_NAMESPACE::Euler<T>::ZXZ:
            return "EULER_ZXZ";
        case IMATH_NAMESPACE::Euler<T>::XYZr:
            return "EULER_XYZr";
        case IMATH_NAMESPACE::Euler<T>::XZYr:
            return "EULER_XZYr";
        case IMATH_NAMESPACE::Euler<T>::YZXr:
            return "EULER_YZXr";
        case IMATH_NAMESPACE::Euler<T>::YXZr:
            return "EULER_YXZr";
        case IMATH_NAMESPACE::Euler<T>::ZXYr:
            return "EULER_ZXYr";
        case IMATH_NAMESPACE::Euler<T>::ZYXr:
            return "EULER_ZYXr";
        case IMATH_NAMESPACE::Euler<T>::XZXr:
            return "EULER_XZXr";
        case IMATH_NAMESPACE::Euler<T>::XYXr:
            return "EULER_XYXr";
        case IMATH_NAMESPACE::Euler<T>::YXYr:
            return "EULER_YXYr";
        case IMATH_NAMESPACE::Euler<T>::YZYr:
            return "EULER_YZYr";
        case IMATH_NAMESPACE::Euler<T>::ZYZr:
            return "EULER_ZYZr";
        case IMATH_NAMESPACE::Euler<T>::ZXZr:
            return "EULER_ZXZr";
        default:
            break;
    }

    return "";
}

template <class T>
static std::string Euler_str(const Euler<T> &e)
{
    std::stringstream stream;
    stream << EulerName<T>::value << "(" << e.x << ", " << e.y << ", " << e.z << ", "
           << nameOfOrder<T> (e.order()) << ")";
    return stream.str();
}

// Non-specialized repr is same as str
template <class T>
static std::string Euler_repr(const Euler<T> &e)
{
    return Euler_str(e);
}

// Specialization for float to full precision
template <>
std::string Euler_repr(const Euler<float> &e)
{
    return (boost::format("%s(%.9g, %.9g, %.9g, %s)")
                        % EulerName<float>::value
                        % e.x % e.y % e.z
                        % nameOfOrder<float>(e.order()).c_str()).str();
}

// Specialization for double to full precision
template <>
std::string Euler_repr(const Euler<double> &e)
{
    return (boost::format("%s(%.17g, %.17g, %.17g, %s)")
                        % EulerName<double>::value
                        % e.x % e.y % e.z
                        % nameOfOrder<double>(e.order()).c_str()).str();
}

template <class T>
static bool
equal(const Euler<T> &e0, const Euler<T> &e1)
{
    if(e0.x == e1.x && e0.y == e1.y && e0.z == e1.z && (e0.order())==(e1.order()))
        return true;
    else
        return false;
}

template <class T>
static bool
notequal(const Euler<T> &e0, const Euler<T> &e1)
{
    if(e0.x != e1.x || e0.y != e1.y || e0.z != e1.z || (e0.order()) != (e1.order()))
    {
        return true;
    }
    else
        return false;
}

template <class T>
static IMATH_NAMESPACE::Vec3 <int> getAngleOrder(Euler <T> &euler)
{
    int i, j, k;
    euler.angleOrder(i, j, k);
    return IMATH_NAMESPACE::Vec3 <int> (i, j, k);
}

template <class T>
static void
setXYZTuple(Euler<T> &euler, const tuple &t)
{
    MATH_EXC_ON;
    Vec3<T> v;
    if(t.attr("__len__")() == 3)
    {
        v.x = extract<T>(t[0]);
        v.y = extract<T>(t[1]);
        v.z = extract<T>(t[2]);

        euler.setXYZVector(v);
    }
    else
        throw std::invalid_argument ("Color3 expects tuple of length 3");
}

// needed to convert Eulerf::Order to Euler<T>::Order
template <class T>
static typename Euler<T>::Order interpretOrder(typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = Euler<T>::XYZ;
    switch(order)
    {
        case IMATH_NAMESPACE::Eulerf::XYZ:
        {
            o = Euler<T>::XYZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZY:
        {
            o = Euler<T>::XZY;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZX:
        {
            o = Euler<T>::YZX;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXZ:
        {
            o = Euler<T>::YXZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXY:
        {
            o = Euler<T>::ZXY;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYX:
        {
            o = Euler<T>::ZYX;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZX:
        {
            o = Euler<T>::XZX;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYX:
        {
            o = Euler<T>::XYX;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXY:
        {
            o = Euler<T>::YXY;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZY:
        {
            o = Euler<T>::YZY;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYZ:
        {
            o = Euler<T>::ZYZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXZ:
        {
            o = Euler<T>::ZXZ;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYZr:
        {
            o = Euler<T>::XYZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZYr:
        {
            o = Euler<T>::XZYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZXr:
        {
            o = Euler<T>::YZXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXZr:
        {
            o = Euler<T>::YXZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXYr:
        {
            o = Euler<T>::ZXYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYXr:
        {
            o = Euler<T>::ZYXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XZXr:
        {
            o = Euler<T>::XZXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::XYXr:
        {
            o = Euler<T>::XYXr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YXYr:
        {
            o = Euler<T>::YXYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::YZYr:
        {
            o = Euler<T>::YZYr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZYZr:
        {
            o = Euler<T>::ZYZr;
        }break;
        case IMATH_NAMESPACE::Eulerf::ZXZr:
        {
            o = Euler<T>::ZXZr;
        }break;
        default:
            break;
    }

    return o;
}

// needed to convert Eulerf::Axis to Euler<T>::Axis
template <class T>
static typename Euler<T>::Axis interpretAxis(typename IMATH_NAMESPACE::Eulerf::Axis axis)
{
    if (axis == IMATH_NAMESPACE::Eulerf::X)
        return Euler<T>::X;
    else if (axis == IMATH_NAMESPACE::Eulerf::Y)
        return Euler<T>::Y;
    else
        return Euler<T>::Z;
}

template <class T>
static Euler<T> *
eulerConstructor1(const Vec3<T> &v, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(v, o);
}

template <class T>
static Euler<T> *
eulerConstructor1a(const Vec3<T> &v)
{
    return eulerConstructor1 (v, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor1b(const Vec3<T> &v, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(v, o);
}

template <class T>
static Euler<T> *
eulerConstructor2(T i, T j, T k, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(i, j, k, o);
}

template <class T>
static Euler<T> *
eulerConstructor2a(T i, T j, T k)
{
    return eulerConstructor2 (i, j, k, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor2b(T i, T j, T k, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(i, j, k, o);
}

template <class T>
static Euler<T> *
eulerConstructor3(const Matrix33<T> &mat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor3a(const Matrix33<T> &mat)
{
    return eulerConstructor3 (mat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor3b(const Matrix33<T> &mat, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor4(const Matrix44<T> &mat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor4a(const Matrix44<T> &mat)
{
    return eulerConstructor4 (mat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor4b(const Matrix44<T> &mat, int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(mat, o);
}

template <class T>
static Euler<T> *
eulerConstructor5(typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor5a()
{
    typename Euler<T>::Order o = interpretOrder<T>(IMATH_NAMESPACE::Eulerf::Default);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor5b(int iorder)
{
    typename Euler<T>::Order o = typename Euler<T>::Order (iorder);
    return new Euler<T>(o);
}

template <class T>
static Euler<T> *
eulerConstructor6(T x, T y, T z)
{
    return new Euler<T>(Vec3<T>(x,y,z));
}

template <class T>
static Euler<T> *
eulerConstructor7(const Quat<T> &quat, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    Euler<T> *e = eulerConstructor5<T>(order);
    e->extract(quat);
    return e;
}

template <class T>
static Euler<T> *
eulerConstructor7a(const Quat<T> &quat)
{
    return eulerConstructor7(quat, IMATH_NAMESPACE::Eulerf::Default);
}

template <class T>
static Euler<T> *
eulerConstructor7b(const Quat<T> &quat, int iorder)
{
    Euler<T> *e = eulerConstructor5b<T>(iorder);
    e->extract(quat);
    return e;
}

template <class T, class S>
static Euler<T> *
eulerConversionConstructor(const Euler<S> &euler)
{
    MATH_EXC_ON;
    Euler<T> *e = new Euler<T>;
    *e = euler;
    return e;
}

template <class T>
static void
eulerMakeNear(Euler<T> &euler, Euler<T> &target)
{
    MATH_EXC_ON;
    euler.makeNear (target);
}

template <class T>
static void
eulerSetOrder(Euler<T> &euler, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    typename Euler<T>::Order o = interpretOrder<T>(order);
    euler.setOrder (o);
}

template <class T>
static void
eulerSet(Euler<T> &euler, IMATH_NAMESPACE::Eulerf::Axis axis, int relative, int parityEven, int firstRepeats)
{
    MATH_EXC_ON;
    typename Euler<T>::Axis a = interpretAxis<T>(axis);
    euler.set (a, relative, parityEven, firstRepeats);
}

template <class T>
static void
extract1(Euler<T> &euler, const Matrix33<T> &m)
{
    MATH_EXC_ON;
    euler.extract(m);
}

template <class T>
static void
extract2(Euler<T> &euler, const Matrix44<T> &m)
{
    MATH_EXC_ON;
    euler.extract(m);
}

template <class T>
static void
extract3(Euler<T> &euler, const Quat<T> &q)
{
    MATH_EXC_ON;
    euler.extract(q);
}

template <class T>
static Matrix33<T>
toMatrix33(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toMatrix33();
}

template <class T>
static Matrix44<T>
toMatrix44(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toMatrix44();
}

template <class T>
static Quat<T>
toQuat(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toQuat();
}

template <class T>
static Vec3<T>
toXYZVector(Euler<T> &euler)
{
    MATH_EXC_ON;
    return euler.toXYZVector();
}

template <class T>
class_<Euler<T>,bases<IMATH_NAMESPACE::Vec3<T> > >
register_Euler()
{
    class_<Euler<T>,bases<Vec3<T> > > euler_class(EulerName<T>::value,EulerName<T>::value,init<Euler<T> >("copy construction"));
    euler_class
        .def(init<>("imath Euler default construction"))
        .def("__init__", make_constructor(eulerConstructor1<T>))
        .def("__init__", make_constructor(eulerConstructor1a<T>))
        .def("__init__", make_constructor(eulerConstructor1b<T>))
        .def("__init__", make_constructor(eulerConstructor2<T>))
        .def("__init__", make_constructor(eulerConstructor2a<T>))
        .def("__init__", make_constructor(eulerConstructor2b<T>))
        .def("__init__", make_constructor(eulerConstructor3<T>),
             "Euler-from-matrix construction assumes, but does\n"
             "not verify, that the matrix includes no shear or\n"
             "non-uniform scaling.  If necessary, you can fix\n"
             "the matrix by calling the removeScalingAndShear()\n"
             "function.\n")
        .def("__init__", make_constructor(eulerConstructor3a<T>))
        .def("__init__", make_constructor(eulerConstructor3b<T>))
        .def("__init__", make_constructor(eulerConstructor4<T>))
        .def("__init__", make_constructor(eulerConstructor4a<T>))
        .def("__init__", make_constructor(eulerConstructor4b<T>))
        .def("__init__", make_constructor(eulerConstructor5<T>))
        .def("__init__", make_constructor(eulerConstructor5a<T>))
        .def("__init__", make_constructor(eulerConstructor5b<T>))
        .def("__init__", make_constructor(eulerConstructor6<T>))
        .def("__init__", make_constructor(eulerConstructor7<T>))
        .def("__init__", make_constructor(eulerConstructor7a<T>))
        .def("__init__", make_constructor(eulerConstructor7b<T>))
        .def("__init__", make_constructor(eulerConversionConstructor<T, float>))
        .def("__init__", make_constructor(eulerConversionConstructor<T, double>))

        .def("angleOrder", &getAngleOrder<T>, "angleOrder() set the angle order")

        .def("frameStatic", &Euler<T>::frameStatic,
             "e.frameStatic() -- returns true if the angles of e\n"
             "are measured relative to a set of fixed axes,\n"
             "or false if the angles of e are measured relative to\n"
             "each other\n")

        .def("initialAxis", &Euler<T>::initialAxis,
             "e.initialAxis() -- returns the initial rotation\n"
             "axis of e (EULER_X_AXIS, EULER_Y_AXIS, EULER_Z_AXIS)")

        .def("initialRepeated", &Euler<T>::initialRepeated,
             "e.initialRepeated() -- returns 1 if the initial\n"
             "rotation axis of e is repeated (for example,\n"
             "e.order() == EULER_XYX); returns 0 if the initial\n"
             "rotation axis is not repeated.\n")

        .def("makeNear", &eulerMakeNear<T>,
             "e.makeNear(t) -- adjusts Euler e so that it\n"
             "represents the same rotation as before, but the\n"
             "individual angles of e differ from the angles of\n"
             "t by as little as possible.\n"
             "This method might not make sense if e.order()\n"
             "and t.order() are different\n")

        .def("order", &Euler<T>::order,
             "e.order() -- returns the rotation order in e\n"
             "(EULER_XYZ, EULER_XZY, ...)")

        .def("parityEven", &Euler<T>::parityEven,
             "e.parityEven() -- returns the parity of the\n"
             "axis permutation of e\n")

        .def("set", &eulerSet<T>,
             "e.set(i,r,p,f) -- sets the rotation order in e\n"
             "according to the following flags:\n"
             "\n"
             "   i   initial axis (EULER_X_AXIS,\n"
             "       EULER_Y_AXIS or EULER_Z_AXIS)\n"
             "\n"
             "   r   rotation angles are measured relative\n"
             "       to each other (r == 1), or relative to a\n"
             "       set of fixed axes (r == 0)\n"
             "\n"
             "   p   parity of axis permutation is even (r == 1)\n"
             "       or odd (r == 0)\n"
             "\n"
             "   f   first rotation axis is repeated (f == 1)\n"
             "	or not repeated (f == 0)\n")

        .def("setOrder", &eulerSetOrder<T>,
             "e.setOrder(o) -- sets the rotation order in e\n"
             "to o (EULER_XYZ, EULER_XZY, ...)")

        .def("setXYZVector", &Euler<T>::setXYZVector,
             "e.setXYZVector(v) -- sets the three rotation\n"
             "angles in e to v[0], v[1], v[2]")
        .def("setXYZVector", &setXYZTuple<T>)

        .def("extract", &extract1<T>,
             "e.extract(m) -- extracts the rotation component\n"
             "from 3x3 matrix m and stores the result in e.\n"
             "Assumes that m does not contain shear or non-\n"
             "uniform scaling.  If necessary, you can fix m\n"
             "by calling m.removeScalingAndShear().")

        .def("extract", &extract2<T>,
             "e.extract(m) -- extracts the rotation component\n"
             "from 4x4 matrix m and stores the result in e.\n"
             "Assumes that m does not contain shear or non-\n"
             "uniform scaling.  If necessary, you can fix m\n"
             "by calling m.removeScalingAndShear().")

        .def("extract", &extract3<T>,
             "e.extract(q) -- extracts the rotation component\n"
             "from quaternion q and stores the result in e")

        .def("toMatrix33", &toMatrix33<T>, "e.toMatrix33() -- converts e into a 3x3 matrix\n")

        .def("toMatrix44", &toMatrix44<T>, "e.toMatrix44() -- converts e into a 4x4 matrix\n")

        .def("toQuat", &toQuat<T>, "e.toQuat() -- converts e into a quaternion\n")

        .def("toXYZVector", &toXYZVector<T>,
             "e.toXYZVector() -- converts e into an XYZ\n"
             "rotation vector")
        .def("__str__", &Euler_str<T>)
        .def("__repr__", &Euler_repr<T>)

        .def("__eq__", &equal<T>)
        .def("__ne__", &notequal<T>)
        ;

    // fill in the Euler scope
    {
        scope euler_scope(euler_class);
        enum_<typename Euler<T>::Order> euler_order("Order");
        euler_order
            .value("XYZ",Euler<T>::XYZ)
            .value("XZY",Euler<T>::XZY)
            .value("YZX",Euler<T>::YZX)
            .value("YXZ",Euler<T>::YXZ)
            .value("ZXY",Euler<T>::ZXY)
            .value("ZYX",Euler<T>::ZYX)
            .value("XZX",Euler<T>::XZX)
            .value("XYX",Euler<T>::XYX)
            .value("YXY",Euler<T>::YXY)
            .value("YZY",Euler<T>::YZY)
            .value("ZYZ",Euler<T>::ZYZ)
            .value("ZXZ",Euler<T>::ZXZ)
            .value("XYZr",Euler<T>::XYZr)
            .value("XZYr",Euler<T>::XZYr)
            .value("YZXr",Euler<T>::YZXr)
            .value("YXZr",Euler<T>::YXZr)
            .value("ZXYr",Euler<T>::ZXYr)
            .value("ZYXr",Euler<T>::ZYXr)
            .value("XZXr",Euler<T>::XZXr)
            .value("XYXr",Euler<T>::XYXr)
            .value("YXYr",Euler<T>::YXYr)
            .value("YZYr",Euler<T>::YZYr)
            .value("ZYZr",Euler<T>::ZYZr)
            .value("ZXZr",Euler<T>::ZXZr)

            // don't export these, they're not really part of the public interface
            //.value("Legal",Euler<T>::Legal)
            //.value("Min",Euler<T>::Min)
            //.value("Max",Euler<T>::Max)

            // handle Default seperately since boost sets up a 1-1 mapping for enum values
            //.value("Default",Euler<T>::Default)
            .export_values()
            ;
        // just set it to the XYZ value manually
        euler_scope.attr("Default") = euler_scope.attr("XYZ");

        enum_<typename Euler<T>::Axis>("Axis")
            .value("X",Euler<T>::X)
            .value("Y",Euler<T>::Y)
            .value("Z",Euler<T>::Z)
            .export_values()
            ;

        enum_<typename Euler<T>::InputLayout>("InputLayout")
            .value("XYZLayout",Euler<T>::XYZLayout)
            .value("IJKLayout",Euler<T>::IJKLayout)
            .export_values()
            ;
    }

    decoratecopy(euler_class);

    return euler_class;
}

// XXX fixme - template this
// really this should get generated automatically...

/*
template <class T,int index>
static FixedArray<T>
EulerArray_get(FixedArray<IMATH_NAMESPACE::Euler<T> > &qa)
{
    return FixedArray<T>( &(qa[0].r)+index, qa.len(), 4*qa.stride());
}
*/

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor7a(const FixedArray<IMATH_NAMESPACE::Quat<T> > &q)
{
    MATH_EXC_ON;
    size_t len = q.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);
    for (size_t i = 0; i < len; ++i) {
        (*result)[i].extract(q[i]);
    }
    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor8a(const FixedArray<IMATH_NAMESPACE::Vec3<T> >& v)
{
    MATH_EXC_ON;
    size_t len = v.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);

    for (size_t i = 0; i < len; ++i)
        (*result)[i] = Euler<T>(v[i]);

    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Euler<T> > *
EulerArray_eulerConstructor9a(const FixedArray<IMATH_NAMESPACE::Vec3<T> >& v, typename IMATH_NAMESPACE::Eulerf::Order order)
{
    MATH_EXC_ON;
    size_t len = v.len();
    FixedArray<IMATH_NAMESPACE::Euler<T> >* result = new FixedArray<IMATH_NAMESPACE::Euler<T> >(len);

    typename Euler<T>::Order o = interpretOrder<T>(order);
    for (size_t i = 0; i < len; ++i)
        (*result)[i] = Euler<T>(v[i], o);

    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Vec3<T> >
EulerArray_toXYZVector(const FixedArray<IMATH_NAMESPACE::Euler<T> >& e)
{
    MATH_EXC_ON;
    size_t len = e.len();
    FixedArray<IMATH_NAMESPACE::Vec3<T> > result(len, UNINITIALIZED);
    for (size_t i = 0; i < len; ++i)
        result[i] = e[i].toXYZVector();
    return result;
}

template <class T>
static FixedArray<IMATH_NAMESPACE::Quat<T> >
EulerArray_toQuat(const FixedArray<IMATH_NAMESPACE::Euler<T> >& e)
{
    MATH_EXC_ON;
    size_t len = e.len();
    FixedArray<IMATH_NAMESPACE::Quat<T> > result(len, UNINITIALIZED);
    for (size_t i = 0; i < len; ++i)
        result[i] = e[i].toQuat();
    return result;
}

template <class T>
class_<FixedArray<IMATH_NAMESPACE::Euler<T> > >
register_EulerArray()
{
    class_<FixedArray<IMATH_NAMESPACE::Euler<T> > > eulerArray_class = FixedArray<IMATH_NAMESPACE::Euler<T> >::register_("Fixed length array of IMATH_NAMESPACE::Euler");
    eulerArray_class
        //.add_property("x",&EulerArray_get<T,1>)
        //.add_property("y",&EulerArray_get<T,2>)
        //.add_property("z",&EulerArray_get<T,3>)
        .def("__init__", make_constructor(EulerArray_eulerConstructor7a<T>))
        .def("__init__", make_constructor(EulerArray_eulerConstructor8a<T>))
        .def("__init__", make_constructor(EulerArray_eulerConstructor9a<T>))
        .def("toXYZVector", EulerArray_toXYZVector<T>)
        .def("toQuat", EulerArray_toQuat<T>)
        ;

    add_comparison_functions(eulerArray_class);
    PyImath::add_explicit_construction_from_type<IMATH_NAMESPACE::Matrix33<T> >(eulerArray_class);
    PyImath::add_explicit_construction_from_type<IMATH_NAMESPACE::Matrix44<T> >(eulerArray_class);
    return eulerArray_class;
}

template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Euler<float>,bases<IMATH_NAMESPACE::Vec3<float> > > register_Euler<float>();
template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Euler<double>,bases<IMATH_NAMESPACE::Vec3<double> > > register_Euler<double>();

template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Euler<float> > > register_EulerArray<float>();
template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Euler<double> > > register_EulerArray<double>();

template<> PYIMATH_EXPORT IMATH_NAMESPACE::Euler<float> FixedArrayDefaultValue<IMATH_NAMESPACE::Euler<float> >::value() { return IMATH_NAMESPACE::Euler<float>(); }
template<> PYIMATH_EXPORT IMATH_NAMESPACE::Euler<double> FixedArrayDefaultValue<IMATH_NAMESPACE::Euler<double> >::value() { return IMATH_NAMESPACE::Euler<double>(); }

} // namespace PyImath


#include <tesseract_common/macros.h>
TESSERACT_COMMON_IGNORE_WARNINGS_PUSH
#include <gtest/gtest.h>
#include <Eigen/Geometry>
TESSERACT_COMMON_IGNORE_WARNINGS_POP

#include <tesseract_urdf/inertial.h>
#include "tesseract_urdf_common_unit.h"

TEST(TesseractURDFUnit, parse_inertial)  // NOLINT
{
  {
    std::string str = R"(<inertial extra="0 0 0">
                           <origin xyz="0 0 0" rpy="0 0 0"/>
                           <mass value="1.0" extra="0 0 0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0" extra="0 0 0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_TRUE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
    EXPECT_NEAR(elem->mass, 1, 1e-8);
    EXPECT_NEAR(elem->ixx, 1, 1e-8);
    EXPECT_NEAR(elem->ixy, 2, 1e-8);
    EXPECT_NEAR(elem->ixz, 3, 1e-8);
    EXPECT_NEAR(elem->iyy, 4, 1e-8);
    EXPECT_NEAR(elem->iyz, 5, 1e-8);
    EXPECT_NEAR(elem->izz, 6, 1e-8);
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_TRUE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
    EXPECT_NEAR(elem->mass, 1, 1e-8);
    EXPECT_NEAR(elem->ixx, 1, 1e-8);
    EXPECT_NEAR(elem->ixy, 2, 1e-8);
    EXPECT_NEAR(elem->ixz, 3, 1e-8);
    EXPECT_NEAR(elem->iyy, 4, 1e-8);
    EXPECT_NEAR(elem->iyz, 5, 1e-8);
    EXPECT_NEAR(elem->izz, 6, 1e-8);
  }

  {
    std::string str = R"(<inertial extra="0 0 0">
                           <origin xyz="0 0 0 4" rpy="0 0 0"/>
                           <mass value="1.0" extra="0 0 0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0" extra="0 0 0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="a"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass />
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="a" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="a" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="a" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="a" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="a" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="a"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixz="3.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" iyy="4.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyz="5.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" izz="6.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia ixx="1.0" ixy="2.0" ixz="3.0" iyy="4.0" iyz="5.0"/>
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }

  {
    std::string str = R"(<inertial>
                           <mass value="1.0"/>
                           <inertia />
                         </inertial>)";
    tesseract_scene_graph::Inertial::Ptr elem;
    EXPECT_FALSE(
        runTest<tesseract_scene_graph::Inertial::Ptr>(elem, &tesseract_urdf::parseInertial, str, "inertial", 2));
  }
}

TEST(TesseractURDFUnit, write_inertial)  // NOLINT
{
  {
    tesseract_scene_graph::Inertial::Ptr inertial = std::make_shared<tesseract_scene_graph::Inertial>();
    inertial->origin = Eigen::Isometry3d::Identity();
    inertial->ixx = 1.0;
    inertial->ixy = 2.0;
    inertial->iyy = 3.0;
    inertial->iyz = 4.0;
    inertial->izz = 5.0;
    inertial->ixz = 6.0;
    std::string text;
    EXPECT_EQ(0, writeTest<tesseract_scene_graph::Inertial::Ptr>(inertial, &tesseract_urdf::writeInertial, text));
    EXPECT_NE(text, "");
  }

  {
    tesseract_scene_graph::Inertial::Ptr inertial = nullptr;
    std::string text;
    EXPECT_EQ(1, writeTest<tesseract_scene_graph::Inertial::Ptr>(inertial, &tesseract_urdf::writeInertial, text));
    EXPECT_EQ(text, "");
  }
}


#include "boxsampler.hpp"
#include "gibbssampler.hpp"
#include "heliumsampler.hpp"
#include "importancesampler.hpp"
#include "metropolissampler.hpp"
#include "sampler.hpp"

#include <Eigen/Dense>
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;

void init_sampler(py::module& main)
{
    auto m  = main.def_submodule("samplers");
    m.doc() = R"doc(
Samplers
--------

The sampling classes are the foundation of the Monte Carlo machinery that VMC
depends on. They provide a way of sampling system configurations (i.e.
positions of all particles) from a possibly unnormalized probability amplitude
(i.e. wavefunctions), which in turn allows us to efficiently evaluate
multidimensional integrals.
)doc";

    py::class_<Sampler>(m, "Sampler", R"doc(
The :class:`Sampler` class provides a unified abstraction for the generation of
successive system instances drawn from some probability distribution (i.e. a
wavefunction).

All other code expecting a sampler instance will take a :class:`Sampler` reference.

The subclasses of :class:`Sampler` will differ only in how they generate new
samples, according to their respective algorithms. As such they have different
parameters that can be set at initialization.
)doc")
        .def("next_configuration", &Sampler::next_configuration, R"doc(
Return a newly sampled system configuration.

There is no guarantee that this will always differ between successive calls,
but with a sufficient number of calls the distribution of outputs,
:math:`P(\mathbf{X})` should approximate

.. math::

    P(\mathbf{X}) \simeq  |\Psi(\mathbf{X})|^2

)doc")

        .def("thermalize", &Sampler::thermalize, R"doc(
Generate a given number of samples, discarding all.

This is useful to ensure that the sampler has reached a stable point where
samples are representative of the underlying distribution.
)doc")

        .def_property_readonly("acceptance_rate", &Sampler::get_acceptance_rate, R"doc(
Rate at which newly proposed samples where accepted by the algorithm.
)doc")

        .def_property_readonly("current_system", &Sampler::get_current_system);

    py::class_<MetropolisSampler, Sampler>(m, "MetropolisSampler", R"doc(
Implementation of the standard Metropolis-Hastings algorithm, producing a
Markov Chain of system configurations. [1]_

Each configuration is a _random_ perturbation of its predecessor if accepted, or
a copy if rejected. The perturbation is defined as a :math:`D` dimensional
vector of uniform random numbers in :math:`\frac{1}{2}[-s, s]` for some scale
parameter :math:`s`.

Notes
-----
This implementation only changes the coordinates of one particle at a time. This
means that two successive calls to `next_configuration()` will at most differ
in one row of the output. This can be trusted to always be the case, and
potential optimizations can be made based on this knowledge. For instance, if
caching of relative distances is employed, only the distances corresponding to
the changed particle would have to be recalculated.

References
----------
.. [1] W. K. Hastings; Monte Carlo sampling methods using Markov chains and
       their applications, Biometrika, Volume 57, Issue 1, 1 April 1970, Pages 97–109,
       https://doi.org/10.1093/biomet/57.1.97

Examples
--------

    >>> import numpy as np
    >>> from qflow.samplers import MetropolisSampler
    >>> from qflow.wavefunctions import SimpleGaussian
    >>> psi = SimpleGaussian()
    >>> sampler = MetropolisSampler(np.zeros((2, 3)), psi, step_size=1)

We can now get samples of the desired size on demand:

    >>> sampler.next_configuration()
    array([[-0.61370758,  0.08936709,  0.15872668],
           [ 0.05973557,  0.07445129, -0.29230947]])

Thermalize the sampler (equivalent to running `next_configuration` the given number of times, only faster):

    >>> sampler.thermalize(100)

Inspect the acceptance rate:

    >>> sampler.acceptance_rate
    0.7326732673267327
)doc")
        .def(py::init<const System&, Wavefunction&, Real>(),
             py::arg("system"),
             py::arg("wavefunction"),
             py::arg("step_size") = 1,
             R"doc(
Construct a sampler that uses the standard Metropolis algorithm. The
`step_size` determines how different successive configurations will be, and
should be tuned such that the acceptance rate remains high at all times.

Arguments
---------
 )doc");

    py::class_<ImportanceSampler, Sampler>(m, "ImportanceSampler", R"doc(
Modified version of the Metropolis-Hastings algorithm, which employs a smarter
way of generating new samples. [2]_ The variance of integrals computed using
this algorithm tends to be significantly lower compared to
:class:`MetropolisSampler`, at the expense of higher run-time cost.

.. warning::
    Importance sampling may only be used with wavefunctions that implements the
    :class:`Wavefunction.drift_force` method. If this is not fulfilled, the
    program will halt immediately without any way of catching an exception from
    Python.

Notes
-----
This algorithm differs from Metropolis-Hastings in the perturbations made, with
a corresponding change in the acceptance probability. In our case,

.. math::

    X_k^{(i+1)} = X_k^{(i)} + \sqrt{t}\mathcal{n} + t \frac{1}{\Psi}\nabla_k \Psi,

where :math:`X_k^{(i)}` is the coordinates of particle :math:`k` at time step
:math:`i`, :math:`\mathcal{n}` is a random number drawn from the standard normal
distribution and :math:`t` is the step size parameter used to tune how
different successive samples are.

Similarly to :class:`MetropolisSampler`, only one particle is perturbed at a time.

References
----------
.. [2] Reiher, W. (1966), Hammersley, J. M., D. C. Handscomb: Monte Carlo
       Methods. Methuen & Co., London, and John Wiley & Sons, New York, 1964. VII +
       178 S., Preis: 25 s. Biom. J., 8: 209-209. doi:10.1002/bimj.19660080314

Examples
--------
This is used exactly like :class:`MetropolisSampler`, with the only exception being the
meaning of the `step_size`.

    >>> import numpy as np
    >>> from qflow.samplers import ImportanceSampler
    >>> from qflow.wavefunctions import SimpleGaussian
    >>> psi = SimpleGaussian()
    >>> sampler = ImportanceSampler(np.zeros((2, 3)), psi, step_size=0.1)
    >>> sampler.next_configuration()
    array([[ 0.56938477,  0.25037102, -0.50411809],
           [-0.8038079 ,  0.15799471, -0.06645576]])
    >>> sampler.thermalize(100)
    >>> sampler.acceptance_rate
    0.9603960396039604

)doc")
        .def(py::init<const System&, Wavefunction&, Real>(),
             py::arg("system"),
             py::arg("wavefunction"),
             py::arg("step_size") = 0.1);

    py::class_<GibbsSampler, Sampler>(m, "GibbsSampler")
        .def(py::init<const System&, RBMWavefunction&>(),
             py::arg("system"),
             py::arg("rbm_wavefunction"));

    py::class_<BoxMetropolisSampler, MetropolisSampler>(m, "BoxMetropolisSampler")
        .def(py::init<const System&, Wavefunction&, Real, Real>(),
             py::arg("system"),
             py::arg("wavefunction"),
             py::arg("box_size"),
             py::arg("step_size") = 1)
        .def("initialize_from_system", &BoxMetropolisSampler::initialize_from_system);

    py::class_<BoxImportanceSampler, ImportanceSampler>(m, "BoxImportanceSampler")
        .def(py::init<const System&, Wavefunction&, Real, Real>(),
             py::arg("system"),
             py::arg("wavefunction"),
             py::arg("box_size"),
             py::arg("step_size") = 0.1);

    py::class_<HeliumSampler, Sampler>(m, "HeliumSampler")
        .def(py::init<const System&, Wavefunction&, Real, Real>(),
             py::arg("system"),
             py::arg("wavefunction"),
             py::arg("step_size"),
             py::arg("box_size"));
}


#include <boost/test/unit_test.hpp>
#include <cstdint>
#include <cstring>
#include <gmp.h>

#include "runtime/alloc.h"
#include "runtime/header.h"

#define KCHAR char
extern "C" {
uint32_t getTagForSymbolName(const char *s) {
  return 0;
}

uint64_t tag_big_endian();
uint64_t tag_unsigned();

mpz_ptr
hook_BYTES_bytes2int(string *b, uint64_t endianness, uint64_t signedness);
string *hook_BYTES_int2bytes(mpz_t len, mpz_t i, uint64_t endianness);
string *hook_BYTES_bytes2string(string *b);
string *hook_BYTES_string2bytes(string *s);
string *hook_BYTES_substr(string *b, mpz_t start, mpz_t end);
string *hook_BYTES_replaceAt(string *b, mpz_t start, string *b2);
string *hook_BYTES_update(string *b, mpz_t off, mpz_t val);
mpz_ptr hook_BYTES_get(string *b, mpz_t off);
mpz_ptr hook_BYTES_length(string *b);
string *hook_BYTES_padRight(string *b, mpz_t len, mpz_t v);
string *hook_BYTES_padLeft(string *b, mpz_t len, mpz_t v);
string *hook_BYTES_reverse(string *b);
string *hook_BYTES_concat(string *b1, string *);
string *makeString(const KCHAR *, int64_t len = -1);
}

BOOST_AUTO_TEST_SUITE(BytesTest)

BOOST_AUTO_TEST_CASE(bytes2int) {
  auto empty = makeString("");
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(
             hook_BYTES_bytes2int(empty, tag_big_endian(), tag_unsigned()), 0));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(empty, 2, tag_unsigned()), 0));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(empty, tag_big_endian(), 2), 0));
  BOOST_CHECK_EQUAL(0, mpz_cmp_si(hook_BYTES_bytes2int(empty, 2, 2), 0));

  auto ff = makeString("\xff");
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(
             hook_BYTES_bytes2int(ff, tag_big_endian(), tag_unsigned()), 255));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(ff, tag_big_endian(), 2), -1));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(ff, 2, tag_unsigned()), 255));
  BOOST_CHECK_EQUAL(0, mpz_cmp_si(hook_BYTES_bytes2int(ff, 2, 2), -1));

  auto _00ff = makeString("\x00\xff", 2);
  BOOST_CHECK_EQUAL(
      0,
      mpz_cmp_si(
          hook_BYTES_bytes2int(_00ff, tag_big_endian(), tag_unsigned()), 255));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(_00ff, tag_big_endian(), 2), 255));

  auto ff00 = makeString("\xff\x00", 2);
  BOOST_CHECK_EQUAL(0, mpz_cmp_si(hook_BYTES_bytes2int(ff00, 2, 2), 255));
  BOOST_CHECK_EQUAL(
      0, mpz_cmp_si(hook_BYTES_bytes2int(ff00, 2, tag_unsigned()), 255));
}

BOOST_AUTO_TEST_CASE(int2bytes) {
  mpz_t _0;
  mpz_init_set_ui(_0, 0);

  auto res = hook_BYTES_int2bytes(_0, _0, tag_big_endian());
  BOOST_CHECK_EQUAL(0, len(res));
  res = hook_BYTES_int2bytes(_0, _0, 2);
  BOOST_CHECK_EQUAL(0, len(res));

  mpz_t _4;
  mpz_init_set_ui(_4, 4);

  res = hook_BYTES_int2bytes(_4, _0, tag_big_endian());
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x00\x00\x00\x00", 4));
  res = hook_BYTES_int2bytes(_4, _0, 2);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x00\x00\x00\x00", 4));

  mpz_t _1, neg128, neg2;
  mpz_init_set_ui(_1, 1);
  mpz_init_set_si(neg128, -128);
  mpz_init_set_si(neg2, -2);

  res = hook_BYTES_int2bytes(_1, neg128, tag_big_endian());
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80", 1));
  res = hook_BYTES_int2bytes(_1, neg128, 2);
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80", 1));

  res = hook_BYTES_int2bytes(_1, neg2, tag_big_endian());
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\xfe", 1));
  res = hook_BYTES_int2bytes(_1, neg2, 2);
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\xfe", 1));

  mpz_t _128;
  mpz_init_set_ui(_128, 128);

  res = hook_BYTES_int2bytes(_1, _128, tag_big_endian());
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80", 1));
  res = hook_BYTES_int2bytes(_1, _128, 2);
  BOOST_CHECK_EQUAL(1, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80", 1));

  mpz_t _2;
  mpz_init_set_ui(_2, 2);

  res = hook_BYTES_int2bytes(_2, _128, tag_big_endian());
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x00\x80", 2));
  res = hook_BYTES_int2bytes(_2, _128, 2);
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80\x00", 2));

  res = hook_BYTES_int2bytes(_2, neg128, tag_big_endian());
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\xff\x80", 2));
  res = hook_BYTES_int2bytes(_2, neg128, 2);
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\x80\xff", 2));

  res = hook_BYTES_int2bytes(_2, neg2, tag_big_endian());
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\xff\xfe", 2));
  res = hook_BYTES_int2bytes(_2, neg2, 2);
  BOOST_CHECK_EQUAL(2, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "\xfe\xff", 2));

  mpz_t num;
  mpz_init_set_str(num, "-340240827991474256463460986840063997951", 10);
  mpz_t _17;
  mpz_init_set_ui(_17, 17);

  res = hook_BYTES_int2bytes(_17, num, tag_big_endian());
  BOOST_CHECK_EQUAL(17, len(res));
  BOOST_CHECK_EQUAL(
      0, memcmp(
             res->data,
             "\xff\x00\x08\x00\x06\xFF\xFE\xFF\xF0\x00\x08\xFF\xFF\xFF\xFF\x08"
             "\x01",
             17));
}

BOOST_AUTO_TEST_CASE(bytes2string) {
  auto empty = makeString("");
  auto res = hook_BYTES_bytes2string(empty);
  BOOST_CHECK(res != empty);
  BOOST_CHECK_EQUAL(len(empty), 0);

  auto _1234 = makeString("1234");
  res = hook_BYTES_bytes2string(_1234);
  BOOST_CHECK(res != _1234);
  BOOST_CHECK_EQUAL(len(_1234), 4);
  BOOST_CHECK_EQUAL(0, memcmp(_1234->data, "1234", 4));
}

BOOST_AUTO_TEST_CASE(string2bytes) {
  auto empty = makeString("");
  auto res = hook_BYTES_string2bytes(empty);
  BOOST_CHECK(res != empty);
  BOOST_CHECK_EQUAL(len(empty), 0);

  auto _1234 = makeString("1234");
  res = hook_BYTES_string2bytes(_1234);
  BOOST_CHECK(res != _1234);
  BOOST_CHECK_EQUAL(len(_1234), 4);
  BOOST_CHECK_EQUAL(0, memcmp(_1234->data, "1234", 4));
}

BOOST_AUTO_TEST_CASE(substr) {
  auto catAll = makeString("hellohehf");

  mpz_t _2, _9, _6, _0, _4, _7, _40, _8, _10, _1024, _4096;
  mpz_init_set_si(_2, 2);
  mpz_init_set_si(_9, 9);
  mpz_init_set_si(_6, 6);
  mpz_init_set_si(_0, 0);
  mpz_init_set_si(_4, 4);
  mpz_init_set_si(_7, 7);
  mpz_init_set_si(_40, 40);
  mpz_init_set_si(_8, 8);
  mpz_init_set_si(_10, 10);
  mpz_init_set_si(_1024, 1024);
  mpz_init_set_si(_4096, 4096);
  BOOST_CHECK_EQUAL(
      memcmp(hook_BYTES_substr(catAll, _2, _9)->data, "llohehf", 7), 0);
  BOOST_CHECK_EQUAL(
      memcmp(hook_BYTES_substr(catAll, _2, _6)->data, "lloh", 4), 0);
  BOOST_CHECK_EQUAL(
      memcmp(hook_BYTES_substr(catAll, _0, _4)->data, "hell", 4), 0);
  BOOST_CHECK_EQUAL(
      memcmp(hook_BYTES_substr(catAll, _6, _9)->data, "ehf", 3), 0);
  BOOST_CHECK_THROW(hook_BYTES_substr(catAll, _7, _40), std::invalid_argument);
  BOOST_CHECK_THROW(hook_BYTES_substr(catAll, _8, _40), std::invalid_argument);
  BOOST_CHECK_EQUAL(memcmp(hook_BYTES_substr(catAll, _8, _9)->data, "f", 1), 0);
  BOOST_CHECK_EQUAL(len(hook_BYTES_substr(catAll, _9, _9)), 0);
  BOOST_CHECK_THROW(hook_BYTES_substr(catAll, _8, _7), std::invalid_argument);
  BOOST_CHECK_THROW(hook_BYTES_substr(catAll, _7, _10), std::invalid_argument);
  BOOST_CHECK_THROW(
      hook_BYTES_substr(catAll, _1024, _4096), std::invalid_argument);
}

BOOST_AUTO_TEST_CASE(update) {
  auto _1234 = makeString("1234");
  mpz_t _0, _2;
  mpz_init_set_ui(_0, '0');
  mpz_init_set_ui(_2, 2);

  auto res = hook_BYTES_update(_1234, _2, _0);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "1204", 4));
}

BOOST_AUTO_TEST_CASE(get) {
  auto _1234 = makeString("1234");
  mpz_t _0;
  mpz_init_set_ui(_0, 0);

  auto res = hook_BYTES_get(_1234, _0);
  BOOST_CHECK_EQUAL(0, mpz_cmp_ui(res, '1'));
}

BOOST_AUTO_TEST_CASE(replaceAt) {
  auto _1234 = makeString("1234");
  auto _2 = makeString("2");
  mpz_t _0;
  mpz_init_set_ui(_0, 0);

  auto res = hook_BYTES_replaceAt(_1234, _0, _2);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "2234", 4));

  auto _23 = makeString("23");
  _1234 = makeString("1234");

  res = hook_BYTES_replaceAt(_1234, _0, _23);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "2334", 4));

  auto empty = makeString("");
  _1234 = makeString("1234");

  res = hook_BYTES_replaceAt(_1234, _0, empty);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "1234", 4));

  mpz_t _1;
  mpz_init_set_ui(_1, 1);
  auto _12 = makeString("12");

  res = hook_BYTES_replaceAt(_1234, _1, _12);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "1124", 4));
}

BOOST_AUTO_TEST_CASE(length) {
  auto empty = makeString("");
  BOOST_CHECK_EQUAL(0, mpz_get_ui(hook_BYTES_length(empty)));

  auto _1234 = makeString("1234");
  BOOST_CHECK_EQUAL(4, mpz_get_ui(hook_BYTES_length(_1234)));
}

BOOST_AUTO_TEST_CASE(padRight) {
  auto empty = makeString("");
  mpz_t _0;
  mpz_init_set_ui(_0, 0);

  auto res = hook_BYTES_padRight(empty, _0, _0);
  BOOST_CHECK_EQUAL(empty, res);
  BOOST_CHECK_EQUAL(0, len(empty));

  mpz_t _4, _97;
  mpz_init_set_ui(_4, 4);
  mpz_init_set_ui(_97, 97);

  res = hook_BYTES_padRight(empty, _4, _97);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "aaaa", 4));

  auto _1234 = makeString("1234");
  mpz_t _8;
  mpz_init_set_ui(_8, 8);

  res = hook_BYTES_padRight(_1234, _8, _97);
  BOOST_CHECK_EQUAL(8, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "1234aaaa", 8));
}

BOOST_AUTO_TEST_CASE(padLeft) {
  auto empty = makeString("");
  mpz_t _0;
  mpz_init_set_ui(_0, 0);

  auto res = hook_BYTES_padLeft(empty, _0, _0);
  BOOST_CHECK_EQUAL(empty, res);
  BOOST_CHECK_EQUAL(0, len(empty));

  mpz_t _4, _97;
  mpz_init_set_ui(_4, 4);
  mpz_init_set_ui(_97, 97);

  res = hook_BYTES_padLeft(empty, _4, _97);
  BOOST_CHECK_EQUAL(4, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "aaaa", 4));

  auto _1234 = makeString("1234");
  mpz_t _8;
  mpz_init_set_ui(_8, 8);

  res = hook_BYTES_padLeft(_1234, _8, _97);
  BOOST_CHECK_EQUAL(8, len(res));
  BOOST_CHECK_EQUAL(0, memcmp(res->data, "aaaa1234", 8));
}

BOOST_AUTO_TEST_CASE(reverse) {
  auto empty = makeString("");
  auto res = hook_BYTES_reverse(empty);
  BOOST_CHECK_EQUAL(empty, res);
  BOOST_CHECK_EQUAL(len(empty), 0);

  auto _1234 = makeString("1234");
  res = hook_BYTES_reverse(_1234);
  BOOST_CHECK_EQUAL(_1234, res);
  BOOST_CHECK_EQUAL(len(_1234), 4);
  BOOST_CHECK_EQUAL(0, memcmp(_1234->data, "4321", 4));
}

BOOST_AUTO_TEST_CASE(concat) {
  auto a = makeString("hello");
  auto b = makeString("he");
  auto c = makeString("hf");
  auto d = makeString("");

  auto emptyCatR = hook_BYTES_concat(a, d);
  BOOST_CHECK_EQUAL(0, memcmp(emptyCatR->data, a->data, len(emptyCatR)));
  BOOST_CHECK_EQUAL(len(emptyCatR), len(a));

  auto emptyCatL = hook_BYTES_concat(d, a);
  BOOST_CHECK_EQUAL(0, memcmp(emptyCatL->data, a->data, len(emptyCatL)));
  BOOST_CHECK_EQUAL(len(emptyCatL), len(a));

  auto catAll = hook_BYTES_concat(hook_BYTES_concat(a, b), c);
  auto expected = makeString("hellohehf");
  BOOST_CHECK_EQUAL(0, memcmp(catAll->data, expected->data, len(catAll)));
  BOOST_CHECK_EQUAL(len(catAll), len(expected));
}

BOOST_AUTO_TEST_SUITE_END()


#include "modules/bio_base/seqset.h"
#include "modules/mapred/sort_task.h"
#include "modules/bio_mapred/sort_expand.h"
#include "modules/bio_base/corrected_read.h"
#include "modules/io/parallel.h"

#include <boost/bind.hpp>
#include <boost/dynamic_bitset.hpp>
#include <parallel/algorithm>

REGISTER_TASK(sort_expand_task);
REGISTER_TASK(sort_expand_part_task);

static dna_slice decode_flyweight(const dna_sequence& repo, uint32_t read_size, uint32_t f) {
	uint32_t read = ((f/2) / read_size);
	uint32_t size = read_size - ((f/2) % read_size);
	auto start = ((f%2==0) ?
			repo.begin() + (read * read_size + (read_size - size)) :
			(repo.begin() + (read * read_size + size - 1)).rev_comp());
	return dna_slice(start, size_t(size));
}

void sort_expand_part_task::run()
{
	progress_handler_t my_prog = boost::bind(&sort_expand_part_task::void_progress, this, _1);
	subprogress sp1(my_prog, 0.0, 0.4); // Pull in actual reads
	subprogress sp2(my_prog, 0.7, 1.0); // Write out

	std::string key;
	corrected_reads value;
	size_t num_records = input.num_records;

	SPLOG("Reading initial read");
	auto source = input.build();
	if (!source->read_msgpack(key, value)) {
		throw io_exception("Trying to make empty PBWT");
	}
	size_t read_size = value[0].corrected.size();
	size_t num_seqs = num_records * read_size * 2;
	size_t c = 0;
	SPLOG("Read size = %zu, number of reads = %zu, reading them in", read_size, num_records);

	dna_sequence repo(read_size * num_records);
	std::vector<uint32_t> flyweights;
	flyweights.reserve(num_seqs);
	do {
		for(size_t i = 0; i < read_size; i++) {
			repo[c*read_size + i] = value[0].corrected[i];
			flyweights.push_back(2*(c*read_size + i));
			flyweights.push_back(2*(c*read_size + i) + 1);
		}
		c++;
		sp1(double(c) / double(num_records));
	} while(source->read_msgpack(key, value));

	SPLOG("Doing the (parallel?) sort");

        auto sort_future = std::async(std::launch::async, [&] {
            parallel_sort_in_place(
			flyweights.begin(), flyweights.end(), [&](const uint32_t &a, const uint32_t &b) {
			dna_slice as = decode_flyweight(repo, read_size, a);
			dna_slice bs = decode_flyweight(repo, read_size, b);
			return as < bs;
		});
        });
	future_watchdog(sort_future, my_prog, .5);

	SPLOG("Doing the (parallel?) compare");
	uint32_t *buf = new uint32_t[(num_seqs + 31) / 32];
	memset(buf, 0, sizeof(uint32_t) * ((num_seqs + 31) / 32));

        auto compare_future = std::async(std::launch::async, [&] {
		#ifdef _OPENMP
		#pragma omp parallel for
		#endif
		for(size_t i = 0; i < num_seqs - 1; i++) {
			dna_slice as = decode_flyweight(repo, read_size, flyweights[i]);
			dna_slice bs = decode_flyweight(repo, read_size, flyweights[i+1]);
			if (bs.size() < as.size() || bs.subseq(0, as.size()) != as) {
				__sync_fetch_and_or(&buf[i / 32], 1 << (i % 32));
			}
		}
		buf[(num_seqs -1) / 32] |= (1 << ((num_seqs - 1) % 32));
	});
	future_watchdog(compare_future, my_prog, .6);

	SPLOG("Writing data");
	manifest mout;
	auto out = output.build(get_root(), "exps",  mout);
	size_t tot = 0;
	for(size_t i = 0; i < num_seqs; i++) {
		sp2(double(i) / double(num_seqs));
		if (buf[i / 32] & (1 << (i % 32)))
		{
			tot++;
			dna_slice as = decode_flyweight(repo, read_size, flyweights[i]);
			dna_sequence seq(as.begin(), as.end());  // TODO: Remove needless copy
			//SPLOG("Kept: %s", seq.as_string().c_str());
			out->write_msgpack(seq, 0);
		} else {
			//dna_slice as = decode_flyweight(repo, read_size, flyweights[i]);
			//SPLOG("Dropped: %s", as.as_string().c_str());
		}
	}
	SPLOG("Kept %zu sequences", tot);

	delete[] buf;
	out->close();
	set_output(mout);
}

void sort_expand_task::run() {
	if (m_state == 0) {
		split_progress(0.01, 0.25);
		std::vector<input_stream_params> inputs;
		input.split_by_goal_size(inputs, split_size);

		for (size_t i = 0; i < inputs.size(); i++) {
			std::unique_ptr<sort_expand_part_task> t = make_unique<sort_expand_part_task>();
			t->input = inputs[i];
			t->output.presorted = true;
			t->output.sort = "prefix";
			t->output.encoding = "null";
			t->output.goal_size = 256*1024*1024;
			m_subtasks.push_back(add_subtask(std::move(t)));
			update_progress(double(i) / double(inputs.size()));
		}
		m_state = 1;
	} else if (m_state == 1) {
		split_progress(0.01, 0.01);
		manifest out("prefix");

		for (size_t i = 0; i < m_subtasks.size(); i++) {
			manifest subout;
			get_output(subout, m_subtasks[i]);
			out.add(subout);
			update_progress(double(i) / double(m_subtasks.size()));
		}
		m_subtasks.clear();
		std::unique_ptr<sort_task> t = make_unique<sort_task>();
		t->input = out;
		t->is_summary = true;
		t->reduce = "prefix";
		m_subtasks.push_back(add_subtask(std::move(t)));
		m_state = 2;
	} else if (m_state == 2) {
		manifest out;
		get_output(out, m_subtasks[0]);
		// Update sort to prevent further prefix merging
		out.set_sort("dna");
		set_output(out);
	}
}


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// MavlinkMoCap.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <chrono>
#include <thread>
#include "NPTrackingTools.h"
#include "MavLinkConnection.hpp"
#include "MavLinkVehicle.hpp"
#include "MavLinkMessages.hpp"
#include <boost/algorithm/string.hpp>

using namespace mavlinkcom;

void CheckResult(NPRESULT result)
{
    if (result != NPRESULT_SUCCESS)
    {
        // Treat all errors as failure conditions.
        printf("Error: %s\n", TT_GetResultString(result));
        exit(1);
    }
}

void mavlink_euler_to_quaternion(float roll, float pitch, float yaw, float quaternion[4])
{
    float cosPhi_2 = cosf(roll / 2);
    float sinPhi_2 = sinf(roll / 2);
    float cosTheta_2 = cosf(pitch / 2);
    float sinTheta_2 = sinf(pitch / 2);
    float cosPsi_2 = cosf(yaw / 2);
    float sinPsi_2 = sinf(yaw / 2);
    quaternion[0] = (cosPhi_2 * cosTheta_2 * cosPsi_2 +
        sinPhi_2 * sinTheta_2 * sinPsi_2);
    quaternion[1] = (sinPhi_2 * cosTheta_2 * cosPsi_2 -
        cosPhi_2 * sinTheta_2 * sinPsi_2);
    quaternion[2] = (cosPhi_2 * sinTheta_2 * cosPsi_2 +
        sinPhi_2 * cosTheta_2 * sinPsi_2);
    quaternion[3] = (cosPhi_2 * cosTheta_2 * sinPsi_2 -
        sinPhi_2 * sinTheta_2 * cosPsi_2);
}

class PortAddress
{
public:
    std::string addr;
    int port;
};

PortAddress endPoint = { "127.0.0.1", 14590 };
std::string bodyName = "Quadrocopter"; // expected name.
std::string project;

bool ParseCommandLine(int argc, char* argv[]) {

    // parse command line
    for (int i = 1; i < argc; i++)
    {
        const char* arg = argv[i];
        if (arg[0] == '-' || arg[0] == '/')
        {
            std::string option(arg + 1);
            std::vector<std::string> parts;
            boost::algorithm::split(parts, option, boost::is_any_of(":,"));
            std::string lower = boost::algorithm::to_lower_copy(parts[0]);
            if (lower == "server")
            {
                if (parts.size() > 1)
                {
                    endPoint.addr = parts[1];
                    if (parts.size() > 2)
                    {
                        endPoint.port = atoi(parts[2].c_str());
                    }
                }
            }
            else if (lower == "body")
            {
                if (parts.size() > 1)
                {
                    bodyName = parts[1];
                }
            }
            else if (lower == "project")
            {
                if (parts.size() > 1)
                {
                    project = parts[1];
                    if (parts.size() > 2)
                    {
                        project += ":";
                        project += parts[2];
                    }
                }
            }
            else if (lower == "?" || lower == "h" || lower == "help")
            {
                return false;
            }
            else
            {
                printf("### Error: unexpected argument: %s\n", arg);
                return false;
            }
        }
    }
    return true;
}

void PrintUsage() {
    printf("Usage: MavLinkMoCap options\n");
    printf("Connects PX4 to ATT_POS_MOCAP messages from OptiTrack system.\n");
    printf("Using a project you created already using OptiTrack Motive\n");
    printf("Options: \n");
    printf("    -project:d:/path/to/motive/project.ttp\n");
    printf("    -server:ipaddr[:port]]     - connect to drone via this udp address (default 127.0.0.1:14590)\n");
    printf("    -body:name                 - specify name of rigid body to track (default 'Quadrocopter')\n");
}

int main(int argc, char* argv[])
{
    int rc = 0;

    if (!ParseCommandLine(argc, argv)) {
        PrintUsage();
        return 1;
    }
    if (project == "") {
        printf("error: please specify the Motive project to load.\n");
        PrintUsage();
        return 1;
    }

    // motive gives a weird error if the project is not found, so we look for it.
    FILE* ptr = fopen(project.c_str(), "rb");
    if (ptr == nullptr) {
        int rc = errno;
        printf("error: cannot open project file '%s', rc=%d\n", project.c_str(), rc);
        PrintUsage();
        return 1;
    }
    fclose(ptr);

    printf("Initializing NaturalPoint Devices\n");
    TT_Initialize();

    // Do an update to pick up any recently-arrived cameras.
    TT_Update();

    printf("Loading Project: %s\n", project.c_str());
    CheckResult(TT_LoadProject(project.c_str()));

    // List all detected cameras.
    printf("Cameras:\n");
    for (int i = 0; i < TT_CameraCount(); i++)
    {
        printf("\t%s\n", TT_CameraName(i));
    }
    printf("\n");

    // List all defined rigid bodies.
    printf("Rigid Bodies:\n");
    for (int i = 0; i < TT_RigidBodyCount(); i++)
    {
        printf("\t%s\n", TT_RigidBodyName(i));
    }
    printf("\n");

    printf("Starting MOCAP server at %s:%d\n", endPoint.addr.c_str(), endPoint.port);

    std::shared_ptr<MavLinkConnection> proxyConnection = MavLinkConnection::connectLocalUdp("optitrack", endPoint.addr, endPoint.port);

    auto start = std::chrono::system_clock::now();

    int body = -1;
    printf("Looking for Motive RigidBody named '%s'\n", bodyName.c_str());

    while (true) {

        if (TT_Update() == NPRESULT_SUCCESS)
        {
            float   yaw, pitch, roll;
            float   x, y, z;
            float   qx, qy, qz, qw;

            if (body == -1)
            {
                for (int i = 0; i < TT_RigidBodyCount(); i++)
                {
                    const char* found = TT_RigidBodyName(i);
                    if (strcmp(found, bodyName.c_str()) == 0) {
                        printf("Found '%s'\n", bodyName.c_str());
                        printf("Waiting for body tracking data...\n");
                        body = i;
                        break;
                    }
                }
            }
            if (body >= 0)
            {
                TT_RigidBodyLocation(body, &x, &y, &z, &qx, &qy, &qz, &qw, &yaw, &pitch, &roll);

                if (TT_IsRigidBodyTracked(body))
                {
                    auto now = std::chrono::system_clock::now();
                    auto duration = now - start;
                    // throttle to 50 messages per second.
                    if (std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() > 20)
                    {
                        if (x < -10 || x > 10) {
                            printf("X out of range: %f\n", x);
                        }
                        else if (y < -10 || y > 10)
                        {
                            printf("Y out of range: %f\n", y);
                        }
                        else if (z < -10 || z > 10)
                        {
                            printf("Z out of range: %f\n", z);
                        }
                        else
                        {
                            printf("Pos (%.3f, %.3f, %.3f) Orient (%.1f, %.1f, %.1f)\n",
                                x, y, z, yaw, pitch, roll);

                            MavLinkAttPosMocap pos;
                            // OptiTrack uses 'y' axis for vertical.
                            pos.x = x;
                            pos.y = z;
                            pos.z = -y; // convert to NED coordinates.
                            pos.time_usec = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
                            pos.compid = 1;
                            pos.sysid = 166;
                            mavlink_euler_to_quaternion(roll, pitch, yaw, pos.q);
                            proxyConnection->sendMessage(pos);
                            start = now;
                        }
                    }
                }
            }
        }

    }

    return 0;
}


//
// Created by Sandra Alfaro on 31/05/18.
//

#include <gr/algorithms/matchBase.h>
#include <gr/algorithms/match3pcs.h>
#include <gr/algorithms/PointPairFilter.h>
#include "gr/io/io.h"
#include "gr/utils/geometry.h"

#include <Eigen/Dense>
#include <Eigen/Geometry>

#include <array>
#include <fstream>
#include <iostream>
#include <string>

#include <boost/filesystem.hpp>

#include "testing.h"

#define WRITE_OUTPUT_FILES

#define sqr(x) ((x) * (x))

using namespace std;
using namespace gr;

using Scalar = Point3D<float>::Scalar;
enum {Dim = 3};
typedef Eigen::Transform<Scalar, Dim, Eigen::Affine> Transform;

const int nbSet = 2;

struct TrVisitorType {
    template <typename Derived>
    inline void operator() (
            float fraction,
            float best_LCP,
            const Eigen::MatrixBase<Derived>& /*transformation*/) {
      if (fraction >= 0)
        {
          printf("done: %d%c best: %f                  \r",
                 static_cast<int>(fraction * 100), '%', best_LCP);
          fflush(stdout);
        }
    }
    constexpr bool needsGlobalTransformation() const { return false; }
};

constexpr Utils::LogLevel loglvl = Utils::Verbose;
Utils::Logger logger(loglvl);

/*!
  Datasets and associated parameters
  In the current state we use a conservative configuration, better timing
  could be achieved by tuning a bit more the parameters.
  */

std::array<std::string, nbSet> confFiles = {
    "./datasets/bunny/data/bun.conf",
    "./datasets/armadillo/ArmadilloSide.conf"
};

std::array<Scalar, nbSet> deltas  = {
    0.004,
    0.003,
};

std::array<Scalar, nbSet> overlaps = {
    0.9,
    0.8,
};

std::array<bool, nbSet> swapPQ {
    true,
    false,
};

std::array<Scalar, nbSet> n_points = {
    200,
    200,
};

// Maximum allowed computation time.
int max_time_seconds = 1e9;

/*!
  Read a configuration file from Standford 3D shape repository and
  output a set of filename and eigen transformations
  */
inline void
extractFilesAndTrFromStandfordConfFile(
        const std::string &confFilePath,
        std::vector<Transform>& transforms,
        std::vector<string>& files
){
    using namespace boost;
    using namespace std;

    VERIFY (filesystem::exists(confFilePath) && filesystem::is_regular_file(confFilePath));

    // extract the working directory for the configuration path
    const string workingDir = filesystem::path(confFilePath).parent_path().native();
    VERIFY (filesystem::exists(workingDir));

    // read the configuration file and call the matching process
    string line;
    ifstream confFile;
    confFile.open(confFilePath);
    VERIFY (confFile.is_open());

    while ( getline (confFile,line) )
    {
        istringstream iss (line);
        vector<string> tokens{istream_iterator<string>{iss},
                              istream_iterator<string>{}};

        // here we know that the tokens are:
        // [0]: keyword, must be bmesh
        // [1]: 3D object filename
        // [2-4]: target translation with previous object
        // [5-8]: target quaternion with previous object

        if (tokens.size() == 9){
            if (tokens[0].compare("bmesh") == 0){

                string inputfile = filesystem::path(confFilePath).parent_path().string()+string("/")+tokens[1];
                VERIFY(filesystem::exists(inputfile) && filesystem::is_regular_file(inputfile));

                // build the Eigen rotation matrix from the rotation and translation stored in the files
                Eigen::Matrix<Scalar, Dim, 1> tr (
                        std::atof(tokens[2].c_str()),
                        std::atof(tokens[3].c_str()),
                        std::atof(tokens[4].c_str()));

                Eigen::Quaternion<Scalar> quat(
                        std::atof(tokens[8].c_str()), // eigen starts by w
                        std::atof(tokens[5].c_str()),
                        std::atof(tokens[6].c_str()),
                        std::atof(tokens[7].c_str()));

                quat.normalize();

                Transform transform (Transform::Identity());
                transform.rotate(quat);
                transform.translate(-tr);

                transforms.push_back(transform);
                files.push_back(inputfile);

            }
        }
    }
    confFile.close();
}

void test_model(const vector<Transform> &transforms,
                const vector<string> &files,
                vector<Point3D<float> > &mergedset,
                int i,
                int param_i){
    using namespace gr;

    const string input1 = files.at(i-1);
    const string input2 = files.at(i);

    cout << "Matching " << input2.c_str() << endl;

    vector<Point3D<float> > set1, set2;
    vector<Eigen::Matrix2f> tex_coords1, tex_coords2;
    vector<typename Point3D<float>::VectorType> normals1, normals2;
    vector<tripple> tris1, tris2;
    vector<std::string> mtls1, mtls2;

    IOManager iomanager;
    VERIFY(iomanager.ReadObject((char *)input1.c_str(), set1, tex_coords1, normals1, tris1, mtls1));
    VERIFY(iomanager.ReadObject((char *)input2.c_str(), set2, tex_coords2, normals2, tris2, mtls2));

    using MatrixType = Eigen::Matrix<typename Point3D<float>::Scalar, 4, 4>;
    // clean only when we have pset to avoid wrong face to point indexation
    if (tris1.size() == 0)
        Utils::CleanInvalidNormals(set1, normals1);
    if (tris2.size() == 0)
        Utils::CleanInvalidNormals(set2, normals2);

    // first transform the first mesh to its gt coordinates:
    // we compare only pairwise matching, so we don't want to
    // accumulate error during the matching process
    // Transforms Q by the new transformation.
    {
        MatrixType transformation = transforms[i-1].inverse().matrix();
        for (int j = 0; j < set1.size(); ++j) {
            set1[j].pos() = (transformation * set1[j].pos().homogeneous()).head<3>();

//            cv::Mat first(4, 1, CV_64F), transformed;
//            first.at<double>(0, 0) = set1[j].x();
//            first.at<double>(1, 0) = set1[j].y();
//            first.at<double>(2, 0) = set1[j].z();
//            first.at<double>(3, 0) = 1;
//            transformed = transformation * first;
//            set1[j].x() = transformed.at<double>(0, 0);
//            set1[j].y() = transformed.at<double>(1, 0);
//            set1[j].z() = transformed.at<double>(2, 0);
        }
    }

    mergedset.insert(mergedset.end(), set1.begin(), set1.end());

    // Our matcher.
    using MatcherType = gr::Match3pcs<gr::Point3D<float>, TrVisitorType, gr::AdaptivePointFilter, gr::AdaptivePointFilter::Options>;
    using OptionType  = typename MatcherType::OptionsType;
    using SamplerType   = gr::UniformDistSampler<gr::Point3D<float> >;

    OptionType options;
    TrVisitorType visitor;
    SamplerType sampler;

    // Set parameters.
    MatrixType mat (MatrixType::Identity());
    VERIFY(options.configureOverlap(overlaps[param_i]));
    options.sample_size = n_points[param_i];
    options.max_time_seconds = max_time_seconds;
    options.delta = deltas[param_i];

    Scalar score = 0.;

    MatcherType matcher(options, logger);

    cout << "./Super4PCS -i "
         << input1.c_str() << " "
         << input2.c_str()
         << " -o " << options.getOverlapEstimation()
         << " -d " << options.delta
         << " -n " << options.sample_size
         << " -a " << options.max_normal_difference
         << " -c " << options.max_color_distance
         << " -t " << options.max_time_seconds
         << endl;
    score = matcher.ComputeTransformation(mergedset, set2, mat, sampler, visitor);

#ifdef WRITE_OUTPUT_FILES
    stringstream iss;
    iss << input2;
    iss << "_aligned.ply";
    Utils::TransformPointCloud(set2, mat);
    iomanager.WriteObject(iss.str().c_str(),
                          set2,
                          tex_coords2,
                          normals2,
                          tris2,
                          mtls2);
#endif

    Transform transformEst (mat);

    cout << "Reference: " << endl << transforms[i].matrix() << endl;
    cout << "Estimation: " << endl << transformEst.matrix() << endl;

    Eigen::Quaternion<Scalar>
            q    (transformEst.rotation()),
            qref (transforms[i].rotation());
    cout << " " << q.x()
         << " " << q.y()
         << " " << q.z()
         << " " << q.w()  << endl;
    cout << " " << qref.x()
         << " " << qref.y()
         << " " << qref.z()
         << " " << qref.w()  << endl;

    Scalar rotDiff = std::abs( ( q.vec().array().abs() - ( qref.vec().array().abs() ) ).abs().sum()) + std::abs(std::abs(q.w()) - std::abs(qref.w()));
    Scalar trDiff  = std::abs(transformEst.translation().dot(transforms[i].translation()));

    cout << "rotDiff = " << rotDiff << endl;
    cout << "trDiff = " << trDiff << endl;

    // use different tests, just to get more verbose output
    VERIFY(rotDiff <= 0.3);
    VERIFY(trDiff <= 0.1);
    VERIFY(rotDiff + trDiff <= 0.3);

#ifdef WRITE_OUTPUT_FILES
    stringstream iss2;
    iss2 << input1;
    iss2 << "_merged.ply";
    iomanager.WriteObject(iss2.str().c_str(),
                          mergedset,
                          vector<Eigen::Matrix2f>(),
                          vector<typename Point3D<float>::VectorType>(),
                          vector<tripple>(),
                          vector<std::string>());
#endif
}

int main(int argc, const char **argv) {
    using std::string;

    const char* custom_argv [1] = {"matching"};

    if(!Testing::init_testing(1, custom_argv))
    {
        return EXIT_FAILURE;
    }

    if(argc < 2) // we accept only 1 argument: the test id
    {
        return EXIT_FAILURE;
    }

    int i = std::atoi(argv[1]);
    if (i > confFiles.size()) return EXIT_FAILURE;

    cout << "Starting to process test " << i << endl;

    vector<Transform> transforms;
    vector<string> files;
    extractFilesAndTrFromStandfordConfFile(confFiles.at(i), transforms, files);

    VERIFY(transforms.size() == files.size());
    const int nbTests = 2;

    // In this test we match each frame to the union of the previously matched
    // frames.
    // In practice we can't use the output of Super4PCS directly, it would require
    // a local ICP to avoid error accumulation. So we sum-up the GT transformations,
    // and check our Super4PCS is working well by comparing the estimated transformation
    // matrix and the GT.
    vector<Point3D<float> > mergedset;

    for (int j = 1; j <= nbTests; ++j){
        CALL_SUBTEST(( test_model(transforms, files, mergedset, j, i) ));
    }

    return EXIT_SUCCESS;
}


#include <boost/graph/distributed/crauser_et_al_shortest_paths.hpp>


#pragma once
#ifndef IDOCK_RESULT_HPP
#define IDOCK_RESULT_HPP

#include <boost/ptr_container/ptr_vector.hpp>
#include "vec3.hpp"
#include "conformation.hpp"

using boost::ptr_vector;

/// Represents a result found by BFGS local optimization for later clustering.
class result
{
public:
	conformation conf; ///< Conformation.
	fl e; ///< Free energy.
	fl f; ///< Inter-molecular free energy.
	vector<vec3> heavy_atoms; ///< Heavy atom coordinates.
	vector<vec3> hydrogens; ///< Hydrogen atom coordinates.

	/// Constructs a result from free energy e, force f, heavy atom coordinates and hydrogen atom coordinates.
	explicit result(const conformation& conf, const fl e, const fl f, vector<vec3>&& heavy_atoms_, vector<vec3>&& hydrogens_) : conf(conf), e(e), f(f), heavy_atoms(static_cast<vector<vec3>&&>(heavy_atoms_)), hydrogens(static_cast<vector<vec3>&&>(hydrogens_)) {}

	result(const result&) = default;
	result(result&&) = default;
	result& operator=(const result&) = default;
	result& operator=(result&&) = default;

	/// For sorting ptr_vector<result>.
	bool operator<(const result& r) const
	{
		return e < r.e;
	}
};

// TODO: Do not inline large functions.
// TODO: Consider using double linked list std::list<> to store results because of frequent insertions and deletions.
/// Clusters a result into an existing result set with a minimum RMSD requirement.
inline void add_to_result_container(ptr_vector<result>& results, result&& r, const fl required_square_error)
{
	// If this is the first result, simply save it.
	if (results.empty())
	{
		results.push_back(new result(static_cast<result&&>(r)));
		return;
	}

	// If the container is not empty, find in a coordinate that is closest to the given newly found r.coordinate.
	size_t index = 0;
	fl best_square_error = distance_sqr(r.heavy_atoms, results.front().heavy_atoms);
	for (size_t i = 1; i < results.size(); ++i)
	{
		const fl this_square_error = distance_sqr(r.heavy_atoms, results[i].heavy_atoms);
		if (this_square_error < best_square_error)
		{
			index = i;
			best_square_error = this_square_error;
		}
	}

	if (best_square_error < required_square_error) // The result r is very close to results[index].
	{
		if (r.e < results[index].e) // r is better than results[index], so substitute r for results[index].
		{
			results.replace(index, new result(static_cast<result&&>(r)));
		}
	}
	else // Cannot find in results a result that is similar to r.
	{
		if (results.size() < results.capacity())
		{
			results.push_back(new result(static_cast<result&&>(r)));
		}
		else // Now the container is full.
		{
			if (r.e < results.back().e) // If r is better than the worst one, then replace it.
			{
				results.replace(results.size() - 1, new result(static_cast<result&&>(r)));
			}
		}
	}
	results.sort();
}

#endif


// Copyright © 2016-2021 Thomas Nagler and Thibault Vatter
//
// This file is part of the vinecopulib library and licensed under the terms of
// the MIT license. For a copy, see the LICENSE file in the root directory of
// vinecopulib or https://vinecopulib.github.io/vinecopulib/.

#pragma once

#include <Eigen/Dense>
#include <vinecopulib/vinecop/fit_controls.hpp>
#include <vinecopulib/vinecop/rvine_structure.hpp>

namespace vinecopulib {

// forward declarations
class Bicop;
namespace tools_select {
class VinecopSelector;
}

//! @brief A class for vine copula models.
//!
//! A vine copula model is characterized by its structure (see
//! `RVineStructure` objects) and the pair-copulas.
class Vinecop
{
public:
  // default constructors
  Vinecop() {}

  explicit Vinecop(size_t d);

  // Constructors without data
  explicit Vinecop(const RVineStructure& structure,
                   const std::vector<std::vector<Bicop>>& pair_copulas = {},
                   const std::vector<std::string>& var_types = {});

  explicit Vinecop(
    const Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic>& matrix,
    const std::vector<std::vector<Bicop>>& pair_copulas = {},
    const std::vector<std::string>& var_types = {});

  // Constructors from data
  explicit Vinecop(const Eigen::MatrixXd& data,
                   const RVineStructure& structure = RVineStructure(),
                   const std::vector<std::string>& var_types = {},
                   const FitControlsVinecop& controls = FitControlsVinecop());

  explicit Vinecop(
    const Eigen::MatrixXd& data,
    const Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic>& matrix =
      Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic>(),
    const std::vector<std::string>& var_types = {},
    const FitControlsVinecop& controls = FitControlsVinecop());

  // Constructors from files/serialized objects
  explicit Vinecop(const std::string& filename, const bool check = true);
  explicit Vinecop(const nlohmann::json& input, const bool check = true);

  // Serialize
  nlohmann::json to_json() const;
  void to_file(const std::string& filename) const;

  // Methods modifying structure and/or families and parameters
  void select(const Eigen::MatrixXd& data,
              const FitControlsVinecop& controls = FitControlsVinecop());

  DEPRECATED void select_all(
    const Eigen::MatrixXd& data,
    const FitControlsVinecop& controls = FitControlsVinecop());

  DEPRECATED void select_families(
    const Eigen::MatrixXd& data,
    const FitControlsVinecop& controls = FitControlsVinecop());

  // Getters for a single pair copula
  Bicop get_pair_copula(size_t tree, size_t edge) const;

  BicopFamily get_family(size_t tree, size_t edge) const;

  int get_rotation(size_t tree, size_t edge) const;

  Eigen::MatrixXd get_parameters(size_t tree, size_t edge) const;

  double get_tau(size_t tree, size_t edge) const;

  size_t get_trunc_lvl() const;

  // Getters for all pair copulas
  std::vector<std::vector<Bicop>> get_all_pair_copulas() const;

  std::vector<std::vector<BicopFamily>> get_all_families() const;

  std::vector<std::vector<int>> get_all_rotations() const;

  std::vector<std::vector<Eigen::MatrixXd>> get_all_parameters() const;

  std::vector<std::vector<double>> get_all_taus() const;

  // Getters for the structure
  size_t get_dim() const;

  std::vector<size_t> get_order() const;

  RVineStructure get_rvine_structure() const;

  Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic> get_matrix() const;

  TriangularArray<size_t> get_struct_array(bool natural_order = false) const;

  // getters for fit statistics
  double get_threshold() const;
  double get_loglik() const;
  size_t get_nobs() const;
  double get_aic() const;
  double get_bic() const;
  double get_mbicv(const double psi0 = 0.9) const;

  // Stats methods
  Eigen::VectorXd pdf(Eigen::MatrixXd u, const size_t num_threads = 1) const;

  Eigen::VectorXd cdf(const Eigen::MatrixXd& u,
                      const size_t N = 1e4,
                      const size_t num_threads = 1,
                      std::vector<int> seeds = std::vector<int>()) const;

  Eigen::MatrixXd simulate(
    const size_t n,
    const bool qrng = false,
    const size_t num_threads = 1,
    const std::vector<int>& seeds = std::vector<int>()) const;

  Eigen::MatrixXd rosenblatt(const Eigen::MatrixXd& u,
                             const size_t num_threads = 1) const;
  Eigen::MatrixXd inverse_rosenblatt(const Eigen::MatrixXd& u,
                                     const size_t num_threads = 1) const;

  void set_all_pair_copulas(
    const std::vector<std::vector<Bicop>>& pair_copulas);
  void set_var_types(const std::vector<std::string>& var_types);

  std::vector<std::string> get_var_types() const;

  // Fit statistics
  double get_npars() const;

  double loglik(const Eigen::MatrixXd& u = Eigen::MatrixXd(),
                const size_t num_threads = 1) const;

  double aic(const Eigen::MatrixXd& u = Eigen::MatrixXd(),
             const size_t num_threads = 1) const;

  double bic(const Eigen::MatrixXd& u = Eigen::MatrixXd(),
             const size_t num_threads = 1) const;

  double mbicv(const Eigen::MatrixXd& u = Eigen::MatrixXd(),
               const double psi0 = 0.9,
               const size_t num_threads = 1) const;

  // Misc methods
  static std::vector<std::vector<Bicop>> make_pair_copula_store(
    const size_t d,
    const size_t trunc_lvl = std::numeric_limits<size_t>::max());
  void truncate(size_t trunc_lvl);

  std::string str() const;

protected:
  size_t d_;
  RVineStructure rvine_structure_;
  mutable std::vector<std::vector<Bicop>> pair_copulas_;
  double threshold_{ 0.0 };
  double loglik_{ NAN };
  size_t nobs_{ 0 };
  mutable std::vector<std::string> var_types_;

  void check_data_dim(const Eigen::MatrixXd& data) const;
  void check_data(const Eigen::MatrixXd& data) const;
  void check_pair_copulas_rvine_structure(
    const std::vector<std::vector<Bicop>>& pair_copulas) const;
  double calculate_mbicv_penalty(const size_t nobs, const double psi0) const;
  void finalize_fit(const tools_select::VinecopSelector& selector);
  void check_weights_size(const Eigen::VectorXd& weights,
                          const Eigen::MatrixXd& data) const;
  void check_enough_data(const Eigen::MatrixXd& data) const;
  void check_fitted() const;
  void check_indices(const size_t tree, const size_t edge) const;
  void check_var_types(const std::vector<std::string>& var_types) const;
  void set_continuous_var_types() const;
  void set_var_types_internal(const std::vector<std::string>& var_types) const;
  int get_n_discrete() const;
  Eigen::MatrixXd collapse_data(const Eigen::MatrixXd& u) const;
};
}

#include <vinecopulib/vinecop/implementation/class.ipp>


//==================================================================================================
/*!
  @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_ARCH_COMMON_SIMD_FUNCTION_INEARBYINT_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_INEARBYINT_HPP_INCLUDED
#include <boost/simd/detail/overload.hpp>

#include <boost/simd/meta/hierarchy/simd.hpp>
#include <boost/simd/constant/valmax.hpp>
#include <boost/simd/constant/valmin.hpp>
#include <boost/simd/function/if_else.hpp>
#include <boost/simd/function/is_greater.hpp>
#include <boost/simd/function/is_less.hpp>
#include <boost/simd/function/splat.hpp>
#include <boost/simd/detail/dispatch/meta/scalar_of.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>

#ifndef BOOST_SIMD_NO_NANS
#include <boost/simd/function/is_nan.hpp>
#include <boost/simd/function/if_zero_else.hpp>
#endif

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;
   BOOST_DISPATCH_OVERLOAD(inearbyint_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bs::pack_<bd::single_<A0>, X>
                          )
   {
      using result = bd::as_integer_t<A0>;
      BOOST_FORCEINLINE result operator()(pedantic_tag const &
                                         , const A0& a0) const BOOST_NOEXCEPT
      {
        using sr_t = bd::scalar_of_t<result>;
        static const A0 Vax =  splat<A0>(bs::Valmax<sr_t>());
        static const A0 Vix =  splat<A0>(bs::Valmin<sr_t>());
      #ifndef BOOST_SIMD_NO_NANS
        A0 aa0 = if_zero_else(is_nan(a0), a0);
        return if_else(bs::is_less(aa0, Vix), Valmin<result>(),
                       if_else(bs::is_greater(aa0, Vax), Valmax<result>(),
                               inearbyint(aa0)
                              )
                      );
      #else
        return if_else(bs::is_less(a0, Vix), Valmin<result>(),
                       if_else(bs::is_greater(a0, Vax), Valmax<result>(),
                               inearbyint(a0)
                              )
                      );
      #endif
      }
   };

  BOOST_DISPATCH_OVERLOAD ( inearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bd::generic_<bd::integer_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() (pedantic_tag const &
                                    , A0 const& a0) const BOOST_NOEXCEPT
    {
      return a0;
    }
  };
  BOOST_DISPATCH_OVERLOAD ( inearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bd::generic_<bd::double_<A0> >
                          )
  {
    BOOST_FORCEINLINE bd::as_integer_t<A0> operator() (pedantic_tag const &
                                         , A0 const& a0) const BOOST_NOEXCEPT
    {
      return saturated_(toint)(nearbyint(a0));
    }
  };
  BOOST_DISPATCH_OVERLOAD ( inearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bd::generic_<bd::integer_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() (A0 const& a0 ) const BOOST_NOEXCEPT
    {
      return a0;
    }
  };
  BOOST_DISPATCH_OVERLOAD ( inearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bd::generic_<bd::floating_<A0> >
                          )
  {
    BOOST_FORCEINLINE bd::as_integer_t<A0> operator() ( A0 const& a0
                                                      ) const BOOST_NOEXCEPT
    {
      return toint(nearbyint(a0));
    }
  };
} } }

#endif