#define __STDCPP_WANT_MATH_SPEC_FUNCS__ 1

#include <iostream>
#include <vector>
#include <map>
#include <cmath>
#include <limits>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <boost/math/special_functions/beta.hpp>
#include "readdata.h"
#include "classes.h"

#define STOP 1.0e-8
#define TINY 1.0e-30

//stolen incomplete beta function cuz im too lazy to implement
double incbeta(double a, double b, double x) {
    if (x < 0.0 || x > 1.0) return 1.0/0.0;

    /*The continued fraction converges nicely for x < (a+1)/(a+b+2)*/
    if (x > (a+1.0)/(a+b+2.0)) {
        return (1.0-incbeta(b,a,1.0-x)); /*Use the fact that beta is symmetrical.*/
    }

    /*Find the first part before the continued fraction.*/
    const double lbeta_ab = lgamma(a)+lgamma(b)-lgamma(a+b);
    const double front = exp(log(x)*a+log(1.0-x)*b-lbeta_ab) / a;

    /*Use Lentz's algorithm to evaluate the continued fraction.*/
    double f = 1.0, c = 1.0, d = 0.0;

    int i, m;
    for (i = 0; i <= 200; ++i) {
        m = i/2;

        double numerator;
        if (i == 0) {
            numerator = 1.0; /*First numerator is 1.0.*/
        } else if (i % 2 == 0) {
            numerator = (m*(b-m)*x)/((a+2.0*m-1.0)*(a+2.0*m)); /*Even term.*/
        } else {
            numerator = -((a+m)*(a+b+m)*x)/((a+2.0*m)*(a+2.0*m+1)); /*Odd term.*/
        }

        /*Do an iteration of Lentz's algorithm.*/
        d = 1.0 + numerator * d;
        if (fabs(d) < TINY) d = TINY;
        d = 1.0 / d;

        c = 1.0 + numerator / c;
        if (fabs(c) < TINY) c = TINY;

        const double cd = c*d;
        f *= cd;

        /*Check for stop.*/
        if (fabs(1.0-cd) < STOP) {
            return front * (f-1.0);
        }
    }

    return 1.0/0.0; /*Needed more loops, did not converge.*/
}

double commulDistribution(double v, double t)
{
	double incbetaVal = (v/(pow(t,2)+v));
	double incbetaAns = incbeta((v/2),0.5,incbetaVal);
	/*
	std::cout << "func value small thingy yeah: "  << (v/(pow(t,2)+v)) << std::endl;
	std::cout << "inc beta func: " << incbetaAns << std::endl;
	std::cout << "real beta func: "  << std::beta((v/2),0.5) << std::endl;
	double regularizedIncBeta = incbetaAns/std::beta((v/2),0.5);*/

	double commulDistr = 1-incbetaAns/2;

	return commulDistr;
}

double invCommulDistribution(double v, double t)
{
	double signVal = (t-0.5) < 0 ? -1: 1;
	double pVal = t<0.5 ? (2*t) : (2*(1-t));
	double val2Divis = boost::math::ibeta_inv((v/2),0.5,pVal);
	double val2 = v*(1/val2Divis-1);
	double val2Sqrt = sqrt(val2);
	/*std::cout << "(signVal: "  << signVal << ")" << std::endl;
	std::cout << "(pVal: "  << pVal << ")" << std::endl;
	std::cout << "(val2Divis: "  << val2Divis << ")" << std::endl;
	std::cout << "(val2: "  << val2 << ")" << std::endl;*/
	double invCommulDistr = signVal * val2Sqrt;

	return invCommulDistr;
}

void windowFunc(int width, int height, std::vector<DataValues> data)
{
	sf::RenderWindow window(sf::VideoMode(width,height),"Graph view");

	Date firstDate = data[0].date;
	Date lastDate = data[data.size()-1].date;
	int dateSpanDays = lastDate.ToDays()-firstDate.ToDays();
	float lowestVar = std::numeric_limits<float>::infinity();
	float highestVar = -std::numeric_limits<float>::infinity();
	for(DataValues datai : data)
	{
		if(lowestVar>datai.value)
		{
			lowestVar=datai.value;
		}
		if(highestVar<datai.value)
		{
			highestVar=datai.value;
		}
	}

	float highlowDiff = highestVar-lowestVar;

	float lineWidth = 2.5;
	float textScale = 0.5;
	float statsPointRadius = 4;
	int floatPrecision = 2;

	//drawing bla bla bla
	sf::Font font;
	sf::FloatRect bounds;
	#ifdef _WIN32
	font.loadFromFile("c:/windows/fonts/arial.ttf");
	#endif
	#ifdef linux
	font.loadFromFile("/usr/share/fonts/truetype/freefont/FreeSans.ttf");
	#endif
	std::vector<sf::RectangleShape> shapes;
	std::vector<sf::Text> texts;

	sf::RectangleShape lineHor(sf::Vector2f(width-(width/8), lineWidth));
	lineHor.setOrigin((width-(width/8))/2,lineWidth/2);
	lineHor.setPosition(width/1.85,height-(height/6));

	shapes.push_back(lineHor);

	sf::RectangleShape lineVer(sf::Vector2f(height-(height/5.5), lineWidth));
	lineVer.setOrigin((height-(height/5.5))/2,lineWidth/2);
	lineVer.setPosition((width/1.85)-(width/2)+(width/8)/2,(height-(height/6))-((height-(height/5.5))/2)+lineWidth/2);
	lineVer.setRotation(90);

	shapes.push_back(lineVer);

	std::ostringstream outstr;
	outstr.precision(floatPrecision);
	outstr << std::fixed << (lowestVar-highlowDiff/2);
	sf::Text lowNumText(outstr.str(),font);
	bounds = lowNumText.getLocalBounds();
	lowNumText.setOrigin(bounds.width,bounds.height);
	lowNumText.setScale(textScale, textScale);
	lowNumText.setPosition((width/1.85)-(width/2)+(width/8)/2-textScale*30,height-(height/6)-textScale*10);
	outstr.str("");

	texts.push_back(lowNumText);

	outstr << std::fixed << (highestVar+highlowDiff/2);
	sf::Text highNumText(outstr.str(),font);
	bounds = highNumText.getLocalBounds();
	highNumText.setOrigin(bounds.width,0);
	highNumText.setScale(textScale, textScale);
	highNumText.setPosition((width/1.85)-(width/2)+(width/8)/2-bounds.height/2,height-(height-(height/5.5)+(height/6))+textScale*10);

	texts.push_back(highNumText);

	std::string dateStr = std::to_string(firstDate.year)+"/"+std::to_string(firstDate.month)+"/"+std::to_string(firstDate.day);
	sf::Text lowDateText(dateStr,font);
	bounds = lowDateText.getLocalBounds();
	lowDateText.setOrigin(0,0);
	lowDateText.setScale(textScale, textScale);
	lowDateText.setPosition((width/1.85)-(width/2)+(width/8)/2,height-(height/6)+textScale*10);

	texts.push_back(lowDateText);

	dateStr = std::to_string(lastDate.year)+"/"+std::to_string(lastDate.month)+"/"+std::to_string(lastDate.day);
	sf::Text highDateText(dateStr,font);
	bounds = highDateText.getLocalBounds();
	highDateText.setOrigin(bounds.width,0);
	highDateText.setScale(textScale, textScale);
	highDateText.setPosition(width/1.85+(width-(width/8))/2,height-(height/6)+textScale*10);

	texts.push_back(highDateText);

	for(int i = 0; i < texts.size(); i++)
	{
		texts[i].setFillColor(sf::Color(100, 150, 255));
	}
	//drawing over

	//drawing the graph now

	std::vector<sf::CircleShape> circles;

	float topStart = height-(height-(height/5.5)+(height/6));
	float verticalHeight = height-(height/5.5);
	float pixelPerOne = verticalHeight/((highestVar+highlowDiff/2)-(lowestVar-highlowDiff/2));
	float leftStart = (width/1.85)-(width/2)+(width/8)/2;
	float horizontalHeight = width-(width/8);
	float pixelPerOneDate = horizontalHeight/dateSpanDays;

	float lastX = 0;
	float lastY = 0;

	for(DataValues datapoint : data)
	{
		sf::CircleShape circle;
		circle.setRadius(statsPointRadius);
		circle.setOrigin(statsPointRadius,statsPointRadius);
		float x = leftStart+(dateSpanDays-(lastDate.ToDays()-datapoint.date.ToDays()))*pixelPerOneDate;
		float y = topStart+((highestVar+highlowDiff/2)-datapoint.value)*pixelPerOne;
		circle.setPosition(x,y);
		circles.push_back(circle);
		if(lastX!=0 || lastY!=0)
		{
			float triangleWidth = x-lastX;
			float triangleHeight = y-lastY;
			float triangleBigSide = std::sqrt(std::pow(triangleWidth,2)+std::pow(triangleHeight,2));
			float angle = std::atan2(triangleWidth,-triangleHeight)-M_PI/2;

			sf::RectangleShape graphLine(sf::Vector2f(triangleBigSide, lineWidth));
			graphLine.setOrigin(lineWidth/2,lineWidth/2);
			graphLine.setPosition(lastX,lastY);
			graphLine.setRotation(angle*(180/M_PI));

			shapes.push_back(graphLine);
		}
		lastX = x;
		lastY = y;
	}

	for(int i = 0; i < circles.size(); i++)
	{
		circles[i].setFillColor(sf::Color(100, 150, 255));
	}

	for(int i = 0; i < shapes.size(); i++)
	{
		shapes[i].setFillColor(sf::Color(50, 100, 250));
	}

	//drawing the graph ends here

    while(window.isOpen())
    {
        sf::Event event;
        while(window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        window.clear();
		for(sf::CircleShape circlei : circles)
		{
			window.draw(circlei);
		}
        for(sf::RectangleShape rectanglei : shapes)
		{
			window.draw(rectanglei);
		}
		for(sf::Text texti : texts)
		{
			window.draw(texti);
		}
        window.display();
    }
}

void calculateStats(std::string datapath, std::string searchstr)
{
	std::vector<DataValues> data;

	readfile(datapath, data);

	std::cout << std::endl << std::endl;

	std::vector<DataValues> varVec;
	double averageVariable = 0;

	for(int i = 0; i < data.size(); i++)
	{
		std::cout << data[i].name << " (" << data[i].date.day << "): " << data[i].value << std::endl;
		if(data[i].name.find(searchstr)!=std::string::npos)
		{
			averageVariable+=data[i].value;
			varVec.push_back(data[i]);
		}
	}

	if(varVec.size()==0)
	{
		return;
	}

	averageVariable/=varVec.size();

	double sStandardDev = 0;
	for(int i = 0; i < varVec.size(); i++)
	{
		sStandardDev+=pow(averageVariable-varVec[i].value,2);
	}
	sStandardDev = sqrt(sStandardDev/(varVec.size()-1));

	double sdStandardDev = sStandardDev/(sqrt(varVec.size()));

	double tvalue = invCommulDistribution(varVec.size()-1,0.975);
	double marginOfError = tvalue*sdStandardDev;

	std::cout << std::endl;
	std::cout << "Average variable: " << averageVariable << std::endl;
	std::cout << "Sample standard deviation: " << sStandardDev << std::endl;
	std::cout << "Sampling distribution standard deviation: " << sdStandardDev << std::endl;
	std::cout << "T Value for 95%, " << varVec.size()-1 << " df: " << tvalue << std::endl;
	std::cout << "Margin of error: " << marginOfError << std::endl;

	std::cout << "\nReal value between " << averageVariable-marginOfError << " and " << averageVariable+marginOfError << std::endl;

	windowFunc(640,480,varVec);
}

int main()
{
	std::string datapath;
	std::string searchstr;

	std::cout << "path to data: " << std::endl;
	getline(std::cin, datapath);

	std::cout << "variable name: " << std::endl;
	getline(std::cin, searchstr);
	while(true)
	{
		calculateStats(datapath, searchstr);

		std::string userAction;

		std::cout << "\n\nPress enter to quit" << "\nType path to change path" << "\nType var to change the variable" << "\nType update to run with same settings\n";
		getline(std::cin, userAction);
		if(userAction=="path")
		{
			std::cout << "\nnew path to data: " << std::endl;
			getline(std::cin, datapath);
		}
		else if(userAction=="var")
		{
			std::cout << "\nnew variable name: " << std::endl;
			getline(std::cin, searchstr);
		}
		else if(userAction=="update")
		{
		} else
		{
			break;
		}
	}
	return 0;
}


#include <iostream>
#include <NTL/ZZ.h>
#include <fstream>

uint64_t msb(uint64_t n) {
    if (n == 0)
        return 0;

    uint64_t tmp;
    tmp = n >> (uint) 1;

    uint64_t mask = 1;
    while (tmp != 0) {
        tmp >>= (uint) 1;
        mask <<= (uint) 1;
    }

    return mask;
}

NTL::ZZ fibonacci(uint64_t n) {
    NTL::ZZ V_l;
    V_l = 2;
    NTL::ZZ V_h;
    V_h = 1;

    NTL::ZZ Q_l;
    Q_l = 1;
    NTL::ZZ Q_h;
    Q_h = 1;

    uint64_t mask = msb(n);
    while (mask != 0) {
        Q_l = Q_l * Q_h;
        if (n & mask) {
            Q_h = -Q_l;
            V_l = V_h * V_l - Q_l;
            V_h = V_h * V_h - 2 * Q_h;
        }
        else {
            Q_h = Q_l;
            V_h = V_h * V_l - Q_l;
            V_l = V_l * V_l - 2 * Q_h;
        }

        mask >>= (uint) 1;
    }

    return (2 * V_h - V_l) / 5;
}

int main() {
    std::ofstream outfile("case2_output.txt", std::ios::app);
    std::ifstream infile("case2_input.txt");

    long k;
    while (infile >> k) {
        std::cout << k << std::endl;

        NTL::ZZ x;
        x = NTL::power2_ZZ(k-1)-1;
        NTL::ZZ y;
        y = fibonacci(k+1);
        NTL::ZZ res;
        res = NTL::GCD(x, y);

        outfile << k << " " << res << std::endl;

        std::cout << res << std::endl;
        std::cout << std::endl;
    }

    infile.close();
    outfile.close();

    return 0;
}


#pragma once
#include <Eigen/Core>
#include "nn.hpp"
#include <algorithm>
#include <random>

using Eigen::Matrix;

constexpr int agent_num_states = 6;
constexpr int agent_num_inputs = 11 + agent_num_states;

enum class AgentAction {Right, Down, Left, Up};

class AgentController: public SmallNN<agent_num_inputs, 20, 4+agent_num_states> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  AgentAction calc(const Matrix<float, agent_num_inputs, 1> &inputs, float *states, std::default_random_engine &rng) {
    Matrix<float, 4+agent_num_states, 1> o = predict(inputs);
    using std::max;
    using std::exp;

    // First 4 outputs represent 4 actions.
    // Select a random action according to softmax probabilities.
    //
    // def softmax(x):
    //     e_x = np.exp(x - np.max(x))
    //     return e_x / e_x.sum(axis=0)
    //
    float m = max(o(0), max(o(1), max(o(2), o(3))));
    float c0 = exp(o(0)-m);
    float c1 = exp(o(1)-m);
    float c2 = exp(o(2)-m);
    float c3 = exp(o(3)-m);
    float sum = c0+c1+c2+c3;
    std::uniform_real_distribution<float> dist(0, sum);
    float roll = dist(rng);
    AgentAction action;
    if (roll < c0) action = AgentAction::Right;
    else if (roll < c0+c1) action = AgentAction::Down;
    else if (roll < c0+c1+c2) action = AgentAction::Left;
    else action = AgentAction::Up;

    constexpr float state_decay = 0.01;
    for (int i=0; i<agent_num_states; i++) {
      states[i] = (1.0-state_decay)*states[i] + state_decay*o(i+4);
    }

    return action;
  }
};


//
// OpenTissue, A toolbox for physical based simulation and animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <OpenTissue/configuration.h>

#include <OpenTissue/core/math/math_basic_types.h>
#include <OpenTissue/collision/gjk/gjk_reduce_tetrahedron.h>

#define BOOST_AUTO_TEST_MAIN
#include <OpenTissue/utility/utility_push_boost_filter.h>
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>
#include <OpenTissue/utility/utility_pop_boost_filter.h>

#include <cmath>

using namespace OpenTissue;

BOOST_AUTO_TEST_SUITE(opentissue_collision_gjk_reduce_tetrahedron);

BOOST_AUTO_TEST_CASE(case_by_case_test_not_touching)
{
  typedef OpenTissue::math::BasicMathTypes<double, size_t> math_types;
  typedef math_types::vector3_type                         vector3_type;
  typedef math_types::real_type                            real_type;

  typedef OpenTissue::gjk::Simplex<vector3_type>           simplex_type;

  // First we create a simplex that represents a tetrahedron
  vector3_type const a = vector3_type( 0.0,  0.0, 0.0);
  vector3_type const b = vector3_type( 1.0,  0.0, 0.0);
  vector3_type const c = vector3_type( 0.0,  1.0, 0.0);
  vector3_type const d = vector3_type( 0.0,  0.0, 1.0);

  // Inside tetrahedron region new simplex should be ABC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a + b + c + d)*0.25;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 4u );

    BOOST_CHECK(S.m_bitmask == 15u);
    BOOST_CHECK(S.m_v[0] == a);
    BOOST_CHECK(S.m_a[0] == a);
    BOOST_CHECK(S.m_b[0] == a);

    BOOST_CHECK(S.m_v[1] == b);
    BOOST_CHECK(S.m_a[1] == b);
    BOOST_CHECK(S.m_b[1] == b);

    BOOST_CHECK(S.m_v[2] == c);
    BOOST_CHECK(S.m_a[2] == c);
    BOOST_CHECK(S.m_b[2] == c);

    BOOST_CHECK(S.m_v[3] == d);
    BOOST_CHECK(S.m_a[3] == d);
    BOOST_CHECK(S.m_b[3] == d);

    BOOST_CHECK_CLOSE(S.m_w[0], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[1], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[2], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[3], 0.25, 0.01);
  }

  // Inside ABC face region new simplex should be ABC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b+c)/3.0 - vector3_type(0,0,1);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == c);
    BOOST_CHECK(S.m_a[idx_C] == c);
    BOOST_CHECK(S.m_b[idx_C] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside BCD face region new simplex should be BCD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+c+d)/3.0 + vector3_type(1,1,1);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside ABD face region new simplex should be ABD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b+d)/3.0 - vector3_type(0,1,0);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;

    int bit_B    = 0;
    size_t idx_B = 0;

    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside ACD face region new simplex should be ACD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+c+d)/3.0 - vector3_type(1,0,0);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside AB edge region new simplex should be AB
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b)/2.0 - vector3_type(0,1,1);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside AC edge region new simplex should be AC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+c)/2.0 - vector3_type(1,0,1);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside AD edge region new simplex should be AD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+d)/2.0 - vector3_type(1,1,0);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside BC edge region new simplex should be BC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+c)/2.0 + vector3_type(0,0,-1) + unit( vector3_type(1,1,1) );

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside BD edge region new simplex should be BD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+d)/2.0 + vector3_type(0.0,-1.0,0.0) + unit(vector3_type(1.0,1.0,1.0));

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside CD edge region new simplex should be CD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (c+d)/2.0 + vector3_type(-1.0,0.0,0.0) + unit( vector3_type(1.0,1.0,1.0) );

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == c);
    BOOST_CHECK(S.m_a[idx_A] == c);
    BOOST_CHECK(S.m_b[idx_A] == c);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside A vertex region new simplex should be A
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = a + vector3_type(-1.0,-1.0,-1.0);

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside B vertex region new simplex should be B
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = b + vector3_type(0.0,-1.0,0.0)+ vector3_type(0.0,0.0,-1.0) + unit( vector3_type(1,1,1) );

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside C vertex region new simplex should be C
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = c + vector3_type(-1.0,0.0,0.0) + vector3_type(0,0,-1)  + unit( vector3_type(1,1,1) );

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == c);
    BOOST_CHECK(S.m_a[idx_A] == c);
    BOOST_CHECK(S.m_b[idx_A] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside D vertex region new simplex should be D
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = d + vector3_type(-1.0,0.0,0.0) + vector3_type(0.0,-1.0,0.0) + unit( vector3_type(1,1,1) );

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == d);
    BOOST_CHECK(S.m_a[idx_A] == d);
    BOOST_CHECK(S.m_b[idx_A] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

}

BOOST_AUTO_TEST_CASE(case_by_case_test_touching)
{
  typedef OpenTissue::math::BasicMathTypes<double, size_t> math_types;
  typedef math_types::vector3_type                         vector3_type;
  typedef math_types::real_type                            real_type;

  typedef OpenTissue::gjk::Simplex<vector3_type>           simplex_type;

  // First we create a simplex that represents a tetrahedron
  vector3_type const a = vector3_type( 0.0,  0.0, 0.0);
  vector3_type const b = vector3_type( 1.0,  0.0, 0.0);
  vector3_type const c = vector3_type( 0.0,  1.0, 0.0);
  vector3_type const d = vector3_type( 0.0,  0.0, 1.0);

  // Inside tetrahedron region new simplex should be ABCD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a + b + c + d)*0.25;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 4u );

    BOOST_CHECK(S.m_bitmask == 15u);
    BOOST_CHECK(S.m_v[0] == a);
    BOOST_CHECK(S.m_a[0] == a);
    BOOST_CHECK(S.m_b[0] == a);

    BOOST_CHECK(S.m_v[1] == b);
    BOOST_CHECK(S.m_a[1] == b);
    BOOST_CHECK(S.m_b[1] == b);

    BOOST_CHECK(S.m_v[2] == c);
    BOOST_CHECK(S.m_a[2] == c);
    BOOST_CHECK(S.m_b[2] == c);

    BOOST_CHECK(S.m_v[3] == d);
    BOOST_CHECK(S.m_a[3] == d);
    BOOST_CHECK(S.m_b[3] == d);

    BOOST_CHECK_CLOSE(S.m_w[0], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[1], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[2], 0.25, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[3], 0.25, 0.01);
  }

  // Inside ABC face region new simplex should be ABC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b+c)/3.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == c);
    BOOST_CHECK(S.m_a[idx_C] == c);
    BOOST_CHECK(S.m_b[idx_C] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside BCD face region new simplex should be BCD
  {

    // This case fails, problem is with numerical precision, the
    // triangle face plane distance is computed to something like -5e-17, very
    // small but negative. This means the test point is seen as being inside
    // the tetrahedron and we get the full simplex back!

    // It means that all the test cases for this case fails!

    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+c+d)/3.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    //BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    //int bit_A    = 0;
    //size_t idx_A = 0;
    //int bit_B    = 0;
    //size_t idx_B = 0;
    //int bit_C    = 0;
    //size_t idx_C = 0;
    //OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    //BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    //BOOST_CHECK(S.m_v[idx_A] == b);
    //BOOST_CHECK(S.m_a[idx_A] == b);
    //BOOST_CHECK(S.m_b[idx_A] == b);
    //
    //BOOST_CHECK(S.m_v[idx_B] == c);
    //BOOST_CHECK(S.m_a[idx_B] == c);
    //BOOST_CHECK(S.m_b[idx_B] == c);

    //BOOST_CHECK(S.m_v[idx_C] == d);
    //BOOST_CHECK(S.m_a[idx_C] == d);
    //BOOST_CHECK(S.m_b[idx_C] == d);

    //BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    //BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    //BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside ABD face region new simplex should be ABD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b+d)/3.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;

    int bit_B    = 0;
    size_t idx_B = 0;

    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside ACD face region new simplex should be ACD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+c+d)/3.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3333333333333333, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.3333333333333333, 0.01);
  }

  // Inside AB edge region new simplex should be AB
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+b)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside AC edge region new simplex should be AC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+c)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside AD edge region new simplex should be AD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (a+d)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside BC edge region new simplex should be BC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+c)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside BD edge region new simplex should be BD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (b+d)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside CD edge region new simplex should be CD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = (c+d)/2.0;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == c);
    BOOST_CHECK(S.m_a[idx_A] == c);
    BOOST_CHECK(S.m_b[idx_A] == c);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.5, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.5, 0.01);
  }

  // Inside A vertex region new simplex should be A
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = a;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside B vertex region new simplex should be B
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = b;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside C vertex region new simplex should be C
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = c;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == c);
    BOOST_CHECK(S.m_a[idx_A] == c);
    BOOST_CHECK(S.m_b[idx_A] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

  // Inside D vertex region new simplex should be D
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 1u );

    int bit_A    = 0;
    size_t idx_A = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A );

    BOOST_CHECK(S.m_bitmask == bit_A );
    BOOST_CHECK(S.m_v[idx_A] == d);
    BOOST_CHECK(S.m_a[idx_A] == d);
    BOOST_CHECK(S.m_b[idx_A] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 1.0, 0.01);
  }

}

BOOST_AUTO_TEST_CASE(case_by_case_test_assymmetric)
{
  typedef OpenTissue::math::BasicMathTypes<double, size_t> math_types;
  typedef math_types::vector3_type                         vector3_type;
  typedef math_types::real_type                            real_type;

  typedef OpenTissue::gjk::Simplex<vector3_type>           simplex_type;

  // First we create a simplex that represents a tetrahedron
  vector3_type const a = vector3_type( 0.0,  0.0, 0.0);
  vector3_type const b = vector3_type( 1.0,  0.0, 0.0);
  vector3_type const c = vector3_type( 0.0,  1.0, 0.0);
  vector3_type const d = vector3_type( 0.0,  0.0, 1.0);

  // New simplex should be ABCD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.1*a + 0.2*b + 0.3*c + 0.4*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 4u );

    BOOST_CHECK(S.m_bitmask == 15u);
    BOOST_CHECK(S.m_v[0] == a);
    BOOST_CHECK(S.m_a[0] == a);
    BOOST_CHECK(S.m_b[0] == a);

    BOOST_CHECK(S.m_v[1] == b);
    BOOST_CHECK(S.m_a[1] == b);
    BOOST_CHECK(S.m_b[1] == b);

    BOOST_CHECK(S.m_v[2] == c);
    BOOST_CHECK(S.m_a[2] == c);
    BOOST_CHECK(S.m_b[2] == c);

    BOOST_CHECK(S.m_v[3] == d);
    BOOST_CHECK(S.m_a[3] == d);
    BOOST_CHECK(S.m_b[3] == d);

    BOOST_CHECK_CLOSE(S.m_w[0], 0.1, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[1], 0.2, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[2], 0.3, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[3], 0.4, 0.01);
  }

  // New simplex should be ABC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.2*a+0.3*b+0.5*c;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == c);
    BOOST_CHECK(S.m_a[idx_C] == c);
    BOOST_CHECK(S.m_b[idx_C] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.2, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.5, 0.01);
  }

  // New simplex should be BCD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.2*b+0.3*c+0.5*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.2, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.5, 0.01);
  }

  // New simplex should be ABD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.2*a+0.3*b+0.5*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;

    int bit_B    = 0;
    size_t idx_B = 0;

    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.2, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.5, 0.01);
  }

  // New simplex should be ACD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.2*a + 0.3*c + 0.5*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 3u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    int bit_C    = 0;
    size_t idx_C = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B , idx_C, bit_C );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B | bit_C));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK(S.m_v[idx_C] == d);
    BOOST_CHECK(S.m_a[idx_C] == d);
    BOOST_CHECK(S.m_b[idx_C] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.2, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.3, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_C], 0.5, 0.01);
  }

  // New simplex should be AB
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*a+0.6*b;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == b);
    BOOST_CHECK(S.m_a[idx_B] == b);
    BOOST_CHECK(S.m_b[idx_B] == b);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }

  // New simplex should be AC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*a+0.6*c;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }

  // New simplex should be AD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*a+0.6*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == a);
    BOOST_CHECK(S.m_a[idx_A] == a);
    BOOST_CHECK(S.m_b[idx_A] == a);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }

  // New simplex should be BC
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*b+0.6*c;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == c);
    BOOST_CHECK(S.m_a[idx_B] == c);
    BOOST_CHECK(S.m_b[idx_B] == c);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }

  // New simplex should be BD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*b+0.6*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == b);
    BOOST_CHECK(S.m_a[idx_A] == b);
    BOOST_CHECK(S.m_b[idx_A] == b);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }

  // New simplex should be CD
  {
    simplex_type S;

    OpenTissue::gjk::add_point_to_simplex( a, a, a, S);
    OpenTissue::gjk::add_point_to_simplex( b, b, b, S);
    OpenTissue::gjk::add_point_to_simplex( c, c, c, S);
    OpenTissue::gjk::add_point_to_simplex( d, d, d, S);

    vector3_type const p = 0.4*c+0.6*d;

    OpenTissue::gjk::detail::reduce_tetrahedron( p, S );

    BOOST_CHECK( OpenTissue::gjk::dimension( S ) == 2u );

    int bit_A    = 0;
    size_t idx_A = 0;
    int bit_B    = 0;
    size_t idx_B = 0;
    OpenTissue::gjk::get_used_indices( S.m_bitmask, idx_A, bit_A, idx_B, bit_B );

    BOOST_CHECK(S.m_bitmask == (bit_A | bit_B ));
    BOOST_CHECK(S.m_v[idx_A] == c);
    BOOST_CHECK(S.m_a[idx_A] == c);
    BOOST_CHECK(S.m_b[idx_A] == c);

    BOOST_CHECK(S.m_v[idx_B] == d);
    BOOST_CHECK(S.m_a[idx_B] == d);
    BOOST_CHECK(S.m_b[idx_B] == d);

    BOOST_CHECK_CLOSE(S.m_w[idx_A], 0.4, 0.01);
    BOOST_CHECK_CLOSE(S.m_w[idx_B], 0.6, 0.01);
  }
}

BOOST_AUTO_TEST_SUITE_END();


/*
 *  Distributed under the MIT License (See accompanying file /LICENSE )
 */
#include <doctest/doctest.h>  // for ResultBuilder

#include <boost/multiprecision/cpp_int.hpp>  // for cpp_int
#include <ostream>                           // for operator<<
#include <tuple>                             // for tuple
#include <type_traits>                       // for move

#include "projgeom/common_concepts.h"  // for Value_type
#include "projgeom/euclid_plane.hpp"   // for Ar
#include "projgeom/fractions.hpp"      // for operator*
#include "projgeom/persp_plane.hpp"    // for persp_eucl...
#include "projgeom/pg_common.hpp"      // for sq
#include "projgeom/pg_line.hpp"        // for pg_line
#include "projgeom/pg_object.hpp"      // for operator*
#include "projgeom/pg_point.hpp"       // for pg_point
#include "projgeom/proj_plane.hpp"     // for coincident
// #include <iostream>

using namespace fun;

static const auto Zero = doctest::Approx(0).epsilon(0.01);

/**
 * @brief
 *
 * @tparam PG
 * @param[in] myck
 */
template <typename PG> void chk_degenerate(const PG& myck) {
    using Point = typename PG::point_t;
    // using Line = typename PG::line_t;
    using K = Value_type<Point>;

    auto a1 = Point{-1, 0, 3};
    auto a2 = Point{4, -2, 1};
    auto a3 = Point{3, -1, 1};

    const auto m12 = myck.midpoint(a1, a2);
    const auto m23 = myck.midpoint(a2, a3);
    const auto m13 = myck.midpoint(a1, a3);
    const auto t1 = a1 * m23;
    const auto t2 = a2 * m13;
    const auto t3 = a3 * m12;

    auto triangle = std::tuple{std::move(a1), std::move(a2), std::move(a3)};
    const auto trilateral = tri_dual(triangle);

    // const auto& [a1, a2, a3] = triangle;
    const auto& [l1, l2, l3] = trilateral;

    const auto [q1, q2, q3] = myck.tri_quadrance(triangle);
    const auto [s1, s2, s3] = myck.tri_spread(trilateral);

    const auto tqf = sq(q1 + q2 + q3) - 2 * (q1 * q1 + q2 * q2 + q3 * q3);
    const auto tsf = sq(s1 + s2 + s3) - 2 * (s1 * s1 + s2 * s2 + s3 * s3) - 4 * s1 * s2 * s3;

    if constexpr (Integral<K>) {
        CHECK(!myck.is_parallel(l1, l2));
        CHECK(!myck.is_parallel(l2, l3));
        CHECK(coincident(t1 * t2, t3));
        CHECK(tqf == Ar(q1, q2, q3));
        CHECK(tsf == K(0));
    } else {
        CHECK(myck.l_infty().dot(l1 * l2) != Zero);
        CHECK(myck.l_infty().dot(l2 * l3) != Zero);
        CHECK(t1.dot(t2 * t3) == Zero);
        CHECK(tqf - Ar(q1, q2, q3) == Zero);
        CHECK(tsf == Zero);
    }
}

/**
 * @brief
 *
 * @tparam PG
 * @param[in] myck
 */
template <typename PG> void chk_degenerate2(const PG& myck) {
    using Point = typename PG::point_t;
    // using Line = typename PG::line_t;
    using K = Value_type<Point>;

    auto a1 = Point{-1, 0, 3};
    auto a2 = Point{4, -2, 1};
    auto a4 = plucker(3, a1, 4, a2);

    const auto tri2 = std::tuple{std::move(a1), std::move(a2), std::move(a4)};
    const auto [qq1, qq2, qq3] = myck.tri_quadrance(tri2);
    const auto tqf2 = Ar(qq1, qq2, qq3);  // get 0

    if constexpr (Integral<K>) {
        CHECK(tqf2 == 0);
    } else {
        CHECK(tqf2 == Zero);
    }
}

TEST_CASE("Perspective Euclid plane (cpp_int)") {
    using boost::multiprecision::cpp_int;

    auto Ire = pg_point<cpp_int>(0, 1, 1);
    auto Iim = pg_point<cpp_int>(1, 0, 0);
    auto l_inf = pg_line<cpp_int>(0, -1, 1);

    const auto P = persp_euclid_plane{std::move(Ire), std::move(Iim), std::move(l_inf)};
    chk_degenerate(P);
    chk_degenerate2(P);
}

TEST_CASE("Perspective Euclid plane (floating point)") {
    auto Ire = pg_point{0., 1., 1.};
    auto Iim = pg_point{1., 0., 0.};
    auto l_inf = pg_line{0., -1., 1.};

    const auto P = persp_euclid_plane{std::move(Ire), std::move(Iim), std::move(l_inf)};
    chk_degenerate(P);
    chk_degenerate2(P);
}


#include <stan/math/prim/scal.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <gtest/gtest.h>

TEST(MathFunctions, inv_Phi) {
  using stan::math::inv_Phi;
  using stan::math::Phi;
  EXPECT_FLOAT_EQ(0.0, inv_Phi(0.5));
  double p = 0.123456789;
  EXPECT_FLOAT_EQ(p, Phi(inv_Phi(p)));
  p = 8e-311;
  EXPECT_FLOAT_EQ(p, Phi(inv_Phi(p)));
  p = 0.99;
  EXPECT_FLOAT_EQ(p, Phi(inv_Phi(p)));

  // breakpoints
  p = 0.02425;
  EXPECT_FLOAT_EQ(p, Phi(inv_Phi(p)));
  p = 0.97575;
  EXPECT_FLOAT_EQ(p, Phi(inv_Phi(p)));
}
TEST(MathFunctions, inv_Phi_inf) {
  using stan::math::inv_Phi;
  double p = 7e-311;
  const double inf = std::numeric_limits<double>::infinity();
  EXPECT_EQ(inv_Phi(p),-inf);
  p = 1.0;
  EXPECT_EQ(inv_Phi(p),inf);
}
TEST(MathFunctions, inv_Phi_nan) {
  using stan::math::inv_Phi;
  double nan = std::numeric_limits<double>::quiet_NaN();
  EXPECT_THROW(inv_Phi(nan), std::domain_error);
  EXPECT_THROW(inv_Phi(-2.0), std::domain_error);
  EXPECT_THROW(inv_Phi(2.0), std::domain_error);
}


// This file is part of PolyMPC, a lightweight C++ template library
// for real-time nonlinear optimization and optimal control.
//
// Copyright (C) 2020 Listov Petr <petr.listov@epfl.ch>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef BFGS_HPP
#define BFGS_HPP

#include <Eigen/Dense>
#include <limits>

/** Damped BFGS update
 * Implements "Procedure 18.2 Damped BFGS updating for SQP" form Numerical Optimization by Nocedal.
 *
 * @param[in,out]   B hessian matrix, is updated by this function
 * @param[in]       s step vector (x - x_prev)
 * @param[in]       y gradient change (grad - grad_prev)
 */
template <typename Mat, typename Vec>
void BFGS_update(Eigen::MatrixBase<Mat>& B, const Eigen::MatrixBase<Vec>& s, const Eigen::MatrixBase<Vec>& y)
{
    using Scalar = typename Mat::Scalar;
    Scalar sy, sr, sBs;
    typename Vec::PlainObject Bs, r;

    Bs.noalias() = B * s;
    sBs = s.dot(Bs);
    sy = s.dot(y);

    if (sy < 0.2 * sBs) {
        // damped update to enforce positive definite B
        Scalar theta;
        theta = 0.8 * sBs / (sBs - sy);
        r.noalias() = theta * y + (1 - theta) * Bs;
        sr = theta * sy + (1 - theta) * sBs;
    } else {
        // unmodified BFGS
        r = y;
        sr = sy;
    }

    if (sr < std::numeric_limits<Scalar>::epsilon()) {
        return;
    }

    B.noalias() += -Bs * Bs.transpose() / sBs;
    B.noalias() += r * r.transpose() / sr;
}

#endif /* BFGS_HPP */


/* =========================================================================
   Copyright (c) 2010-2012, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the PDF manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

//
// *** System
//
#include <iostream>

//
// *** Boost
//
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/vector.hpp>

//
// *** ViennaCL
//
#define VIENNACL_WITH_UBLAS 1

//#define VIENNACL_DEBUG_ALL
//#define VIENNACL_DEBUG_BUILD
#include "viennacl/vector.hpp"
#include "viennacl/matrix.hpp"

#include "viennacl/linalg/prod.hpp"
#include "viennacl/linalg/reduce.hpp"
#include "viennacl/device_specific/code_generator.hpp"
#include "viennacl/scheduler/io.hpp"

#define CHECK_RESULT(cpu,gpu, op) \
    if ( double delta = fabs ( diff ( cpu, gpu) ) > epsilon ) {\
        std::cout << "# Error at operation: " #op << std::endl;\
        std::cout << "  diff: " << delta << std::endl;\
        retval = EXIT_FAILURE;\
    }\

using namespace boost::numeric;
using namespace viennacl;

template<typename ScalarType, typename VCLMatrixType>
ScalarType diff(ublas::matrix<ScalarType> & mat1, VCLMatrixType & mat2)
{
   ublas::matrix<ScalarType> mat2_cpu(mat2.size1(), mat2.size2());
   viennacl::backend::finish();  //workaround for a bug in APP SDK 2.7 on Trinity APUs (with Catalyst 12.8)
   viennacl::copy(mat2, mat2_cpu);
   double ret = 0;
   double act = 0;

    for (unsigned int i = 0; i < mat2_cpu.size1(); ++i)
    {
      for (unsigned int j = 0; j < mat2_cpu.size2(); ++j)
      {
         act = std::fabs(mat2_cpu(i,j) - mat1(i,j)) / std::max<ScalarType>( std::fabs(mat2_cpu(i, j)), std::fabs(mat1(i,j)) );
         if (act > ret)
           ret = act;
      }
    }
   //std::cout << ret << std::endl;
   return ret;
}

template<typename ScalarType, unsigned int Alignment>
ScalarType diff ( ublas::vector<ScalarType> & v1, viennacl::vector<ScalarType,Alignment> & v2 ) {
    ublas::vector<ScalarType> v2_cpu ( v2.size() );
    viennacl::copy( v2.begin(), v2.end(), v2_cpu.begin() );
    for ( unsigned int i=0; i<v1.size(); ++i ) {
        if ( std::max ( std::fabs ( v2_cpu[i] ), std::fabs ( v1[i] ) ) > 0 )
            v2_cpu[i] = std::fabs ( v2_cpu[i] - v1[i] ) / std::max<ScalarType>( std::fabs ( v2_cpu[i] ), std::fabs ( v1[i] ) );
        else
            v2_cpu[i] = 0.0;
    }
    return norm_inf ( v2_cpu );
}

template< typename NumericT, class Layout, typename Epsilon >
int test( Epsilon const& epsilon) {
    int retval = EXIT_SUCCESS;

    ublas::vector<NumericT> cx;
    ublas::vector<NumericT> cy;

    ublas::matrix<NumericT> cA;
    ublas::matrix<NumericT> cB;
    ublas::matrix<NumericT> cC;
    ublas::matrix<NumericT> cD;

    unsigned int size1 = 762;
    unsigned int size2 = 663;

    cA.resize(size1,size2);
    cx.resize(size2);
    cy.resize(size1);

    srand(0);

    for (unsigned int i=0; i<size1; ++i){
        for (unsigned int j=0; j<size2; ++j){
            cA(i,j)=j;
        }
    }

    for (unsigned int i=0; i<size2; ++i){
        cx(i) = i;
    }

    for (unsigned int i=0; i<size1; ++i){
        cy(i) = i;
    }

//    std::cout << "Running tests for matrix of size " << cA.size1() << "," << cA.size2() << std::endl;

    viennacl::matrix<NumericT,Layout> A (size1, size2);
    viennacl::matrix<NumericT,Layout> B (size1, size2);
    viennacl::matrix<NumericT,Layout> C (size1, size2);
    viennacl::matrix<NumericT,Layout> D (size1, size2);

    viennacl::vector<NumericT> x(size2);
    viennacl::vector<NumericT> y(size1);

    cB = cA;
    cC = cA;
    cD = cA;
    viennacl::copy(cA,A);
    viennacl::copy(cB,B);
    viennacl::copy(cC,C);
    viennacl::copy(cD,D);

    viennacl::copy(cx,x);
    viennacl::copy(cy,y);

    // --------------------------------------------------------------------------
    {
        std::cout << "y = A*x..." << std::endl;
        cy     =  ublas::prod(cA,cx);
        viennacl::scheduler::statement statement(y, viennacl::op_assign(), viennacl::linalg::prod(A,x));
        //std::cout << statement << std::endl;
        device_specific::generate_enqueue_statement(statement, statement.array()[0]);
        viennacl::backend::finish();
        CHECK_RESULT(cy,y,y=A*x)
    }

    {
        std::cout << "x = trans(A)*y..." << std::endl;
        cx     =  ublas::prod(trans(cA),cy);
        viennacl::scheduler::statement statement(x, viennacl::op_assign(), viennacl::linalg::prod(trans(A),y));
        device_specific::generate_enqueue_statement(statement, statement.array()[0]);
        viennacl::backend::finish();
        CHECK_RESULT(cx,x,x=trans(A)*y)
    }

    {
        std::cout << "y = reduce_rows<add>(A)..." << std::endl;
        for (unsigned int i = 0; i < size1; ++i){
            NumericT acc = cA(i,0);
            for (unsigned int j = 1; j < size2; ++j){
                acc += cA(i,j);
            }
            cy(i) = acc;
        }
        viennacl::scheduler::statement statement(y, viennacl::op_assign(), viennacl::linalg::reduce_rows<viennacl::op_add>(A));
        //std::cout << statement << std::endl;

        device_specific::generate_enqueue_statement(statement, statement.array()[0]);
        viennacl::backend::finish();
        CHECK_RESULT(cy,y,y = reduce_rows<max>(A))
    }

    {
        std::cout << "x = reduce_columns<add>(A)..." << std::endl;
        for (unsigned int j = 0; j < size2; ++j){
            NumericT acc = cA(0,j);
            for (unsigned int i = 1; i < size1; ++i){
                acc += cA(i,j);
            }
            cx(j) = acc;
        }
        viennacl::scheduler::statement statement(x, viennacl::op_assign(), viennacl::linalg::reduce_columns<viennacl::op_add>(A));
        device_specific::generate_enqueue_statement(statement, statement.array()[0]);
        viennacl::backend::finish();
        CHECK_RESULT(cx,x,x = reduce_columns<max>(A))
    }

    return retval;
}

int main() {
    std::cout << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "## Test :: Generated BLAS2" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << std::endl;

    int retval = EXIT_SUCCESS;

    std::cout << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << std::endl;
    {
        double epsilon = 1.0E-4;
        std::cout << "# Testing setup:" << std::endl;
        std::cout << "  numeric: float" << std::endl;
        std::cout << "  --------------" << std::endl;
        std::cout << "  Row-Major"      << std::endl;
        std::cout << "  --------------" << std::endl;
        retval = test<float, viennacl::row_major> (epsilon);
        std::cout << "  --------------" << std::endl;
        std::cout << "  Column-Major"   << std::endl;
        std::cout << "  --------------" << std::endl;
        retval &= test<float, viennacl::column_major> (epsilon);

        if ( retval == EXIT_SUCCESS )
            std::cout << "# Test passed" << std::endl;
        else
            return retval;
    }

//    std::cout << std::endl;
//    std::cout << "----------------------------------------------" << std::endl;
//    std::cout << std::endl;
//#ifdef VIENNACL_WITH_OPENCL
//   if ( viennacl::ocl::current_device().double_support() )
//#endif
//    {
//        double epsilon = 1.0E-4;
//        std::cout << "# Testing setup:" << std::endl;
//        std::cout << "  numeric: double" << std::endl;
//        std::cout << "  --------------" << std::endl;
//        std::cout << "  Row-Major"      << std::endl;
//        std::cout << "  --------------" << std::endl;
//        retval = test<double, viennacl::row_major> (epsilon);
//        std::cout << "  --------------" << std::endl;
//        std::cout << "  Column-Major"   << std::endl;
//        std::cout << "  --------------" << std::endl;
//        retval &= test<double, viennacl::column_major> (epsilon);

//        if ( retval == EXIT_SUCCESS )
//            std::cout << "# Test passed" << std::endl;
//        else
//            return retval;
//    }
}


/********************************************************************************
*  This file is part of CinoLib                                                 *
*  Copyright(C) 2016: Marco Livesu                                              *
*                                                                               *
*  The MIT License                                                              *
*                                                                               *
*  Permission is hereby granted, free of charge, to any person obtaining a      *
*  copy of this software and associated documentation files (the "Software"),   *
*  to deal in the Software without restriction, including without limitation    *
*  the rights to use, copy, modify, merge, publish, distribute, sublicense,     *
*  and/or sell copies of the Software, and to permit persons to whom the        *
*  Software is furnished to do so, subject to the following conditions:         *
*                                                                               *
*  The above copyright notice and this permission notice shall be included in   *
*  all copies or substantial portions of the Software.                          *
*                                                                               *
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   *
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *
*  FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE *
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       *
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      *
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS *
*  IN THE SOFTWARE.                                                             *
*                                                                               *
*  Author(s):                                                                   *
*                                                                               *
*     Marco Livesu (marco.livesu@gmail.com)                                     *
*     http://pers.ge.imati.cnr.it/livesu/                                       *
*                                                                               *
*     Italian National Research Council (CNR)                                   *
*     Institute for Applied Mathematics and Information Technologies (IMATI)    *
*     Via de Marini, 6                                                          *
*     16149 Genoa,                                                              *
*     Italy                                                                     *
*********************************************************************************/
#include <cinolib/matrix.h>
#include <Eigen/Eigenvalues>

namespace cinolib
{

// http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
//
CINO_INLINE
void eigen_decomposition_2x2(const double   a00,
                             const double   a01,
                             const double   a10,
                             const double   a11,
                                   vec2d  & v_min, // eigenvectors
                                   vec2d  & v_max,
                                   double & min,   // eigenvalues
                                   double & max)
{
    eigenvalues_2x2(a00,a01,a10,a11,min,max);

    if(std::fabs(a10)>1e-5)
    {
        v_max = vec2d(max-a11,a10);
        v_min = vec2d(min-a11,a10);
    }
    else if(std::fabs(a01)>1e-5)
    {
        v_max = vec2d(a01,max-a00);
        v_min = vec2d(a01,min-a00);
    }
    else
    {
        v_max = (a00>=a11) ? vec2d(1,0) : vec2d(0,1);
        v_min = (a00>=a11) ? vec2d(0,1) : vec2d(1,0);
    }

    v_max.normalize();
    v_min.normalize();
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

// http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
//
CINO_INLINE
void eigenvalues_2x2(const double   a00,
                     const double   a01,
                     const double   a10,
                     const double   a11,
                           double & min,
                           double & max)
{
    double T = a00 + a11; // trace
    double D = determinant_2x2(a00,a01,a10,a11);

    min = T/2.0 - sqrt(T*T/4.0-D);
    max = T/2.0 + sqrt(T*T/4.0-D);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void eigenvectors_2x2(const double   a00,
                      const double   a01,
                      const double   a10,
                      const double   a11,
                            vec2d  & v_min,
                            vec2d  & v_max)
{
    double min, max;
    eigen_decomposition_2x2(a00, a01, a10, a11, v_min, v_max, min, max);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
double determinant_2x2(const double a00, const double a01, const double a10, const double a11)
{
    return ((a00*a11) - (a10*a01));
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
double determinant_2x2(const vec2d a0, const vec2d a1)
{
    return determinant_2x2(a0[0], a0[1], a1[0], a1[1]);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void eigen_decomposition_3x3(const double   a[3][3],
                                   vec3d  & v_min, // eigenvectors
                                   vec3d  & v_mid,
                                   vec3d  & v_max,
                                   double & min,   // eigenvalues
                                   double & mid,
                                   double & max)
{
    eigen_decomposition_3x3(a[0][0], a[0][1], a[0][2],
                            a[1][0], a[1][1], a[1][2],
                            a[2][0], a[2][1], a[2][2],
                            v_min, v_mid, v_max,
                            min, mid, max);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void eigen_decomposition_3x3(const double   a00,
                             const double   a01,
                             const double   a02,
                             const double   a10,
                             const double   a11,
                             const double   a12,
                             const double   a20,
                             const double   a21,
                             const double   a22,
                                   vec3d  & v_min, // eigenvectors
                                   vec3d  & v_mid,
                                   vec3d  & v_max,
                                   double & min,   // eigenvalues
                                   double & mid,
                                   double & max)
{
    Eigen::Matrix3d m;
    m << a00, a01, a02,
         a10, a11, a12,
         a20, a21, a22;

    bool symmetric = (a10==a01) && (a20==a02) && (a21==a12);

    if(symmetric)
    {
        // eigen decomposition for self-adjoint matrices
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(m);
        assert(eig.info() == Eigen::Success);

        v_min = vec3d(eig.eigenvectors()(0,0), eig.eigenvectors()(1,0), eig.eigenvectors()(2,0));
        v_mid = vec3d(eig.eigenvectors()(0,1), eig.eigenvectors()(1,1), eig.eigenvectors()(2,1));
        v_max = vec3d(eig.eigenvectors()(0,2), eig.eigenvectors()(1,2), eig.eigenvectors()(2,2));

        min = eig.eigenvalues()[0];
        mid = eig.eigenvalues()[1];
        max = eig.eigenvalues()[2];
    }
    else
    {
        // eigen decomposition for general matrices
        Eigen::EigenSolver<Eigen::Matrix3d> eig(m);
        assert(eig.info() == Eigen::Success);

        // WARNING: I am taking only the real part!
        v_min = vec3d(eig.eigenvectors()(0,0).real(), eig.eigenvectors()(1,0).real(), eig.eigenvectors()(2,0).real());
        v_mid = vec3d(eig.eigenvectors()(0,1).real(), eig.eigenvectors()(1,1).real(), eig.eigenvectors()(2,1).real());
        v_max = vec3d(eig.eigenvectors()(0,2).real(), eig.eigenvectors()(1,2).real(), eig.eigenvectors()(2,2).real());

        // WARNING: I am taking only the real part!
        min = eig.eigenvalues()[0].real();
        mid = eig.eigenvalues()[1].real();
        max = eig.eigenvalues()[2].real();
    }
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void eigenvalues_3x3(const double   a00,
                     const double   a01,
                     const double   a02,
                     const double   a10,
                     const double   a11,
                     const double   a12,
                     const double   a20,
                     const double   a21,
                     const double   a22,
                           double & min,
                           double & mid,
                           double & max)
{
    vec3d v_min, v_mid, v_max;
    eigen_decomposition_3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22, v_min, v_mid, v_max, min, mid, max);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void eigenvectors_3x3(const double   a00,
                      const double   a01,
                      const double   a02,
                      const double   a10,
                      const double   a11,
                      const double   a12,
                      const double   a20,
                      const double   a21,
                      const double   a22,
                            vec3d  & v_min,
                            vec3d  & v_mid,
                            vec3d  & v_max)
{
    double min, mid, max;
    eigen_decomposition_3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22, v_min, v_mid, v_max, min, mid, max);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
double determinant_3x3(const double a00, const double a01, const double a02,
                       const double a10, const double a11, const double a12,
                       const double a20, const double a21, const double a22)
{
    return a00 * determinant_2x2(a11, a12, a21, a22) -
           a01 * determinant_2x2(a10, a12, a20, a22) +
           a02 * determinant_2x2(a10, a11, a20, a21);
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void from_std_3x3_to_Eigen_3x3(const double stdM[3][3], Eigen::Matrix3d & eigenM)
{
    eigenM.coeffRef(0,0) = stdM[0][0];  eigenM.coeffRef(0,1) = stdM[0][1];  eigenM.coeffRef(0,2) = stdM[0][2];
    eigenM.coeffRef(1,0) = stdM[1][0];  eigenM.coeffRef(1,1) = stdM[1][1];  eigenM.coeffRef(1,2) = stdM[1][2];
    eigenM.coeffRef(2,0) = stdM[2][0];  eigenM.coeffRef(2,1) = stdM[2][1];  eigenM.coeffRef(2,2) = stdM[2][2];
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CINO_INLINE
void from_eigen_3x3_to_std_3x3(const Eigen::Matrix3d & eigenM, double stdM[3][3])
{
    stdM[0][0] = eigenM.coeffRef(0,0);  stdM[0][1] = eigenM.coeffRef(0,1);  stdM[0][2] = eigenM.coeffRef(0,2);
    stdM[1][0] = eigenM.coeffRef(1,0);  stdM[1][1] = eigenM.coeffRef(1,1);  stdM[1][2] = eigenM.coeffRef(1,2);
    stdM[2][0] = eigenM.coeffRef(2,0);  stdM[2][1] = eigenM.coeffRef(2,1);  stdM[2][2] = eigenM.coeffRef(2,2);
}

}


//  Copyright (C) Eric Niebler 2005.
//  Copyright (C) Pieter Bastiaan Ober 2014.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/rolling_moment.hpp>

using namespace boost;
using namespace unit_test;
using namespace accumulators;

template<typename T>
void assert_is_double(T const &)
{
    BOOST_MPL_ASSERT((is_same<T, double>));
}

///////////////////////////////////////////////////////////////////////////////
// test_rolling_moment
//

void test_rolling_second_moment()
{
    accumulator_set<int, stats<tag::rolling_moment<2> > > acc(tag::rolling_moment<2>::window_size = 3);

    acc(2);

    BOOST_CHECK_CLOSE(rolling_moment<2>(acc), 4.0/1 ,1e-5);

    acc(4);

    BOOST_CHECK_CLOSE(rolling_moment<2>(acc), (4.0 + 16.0)/2, 1e-5);

    acc(5);

    BOOST_CHECK_CLOSE(rolling_moment<2>(acc), (4.0 + 16.0 + 25.0)/3, 1e-5);

    acc(6);

    BOOST_CHECK_CLOSE(rolling_moment<2>(acc), (16.0 + 25.0 + 36.0)/3, 1e-5);

    assert_is_double(rolling_moment<2>(acc));
}

void test_rolling_fifth_moment()
{
    accumulator_set<int, stats<tag::rolling_moment<5> > > acc(tag::rolling_moment<2>::window_size = 3);

    acc(2);

    BOOST_CHECK_CLOSE(rolling_moment<5>(acc), 32.0/1, 1e-5);

    acc(3);

    BOOST_CHECK_CLOSE(rolling_moment<5>(acc), (32.0 + 243.0)/2, 1e-5);

    acc(4);

    BOOST_CHECK_CLOSE(rolling_moment<5>(acc), (32.0 + 243.0 + 1024.0)/3, 1e-5);

    acc(5);

    BOOST_CHECK_CLOSE(rolling_moment<5>(acc), (243.0 + 1024.0 + 3125.0)/3, 1e-5);

    assert_is_double(rolling_moment<5>(acc));
}

///////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("rolling moment test");

    test->add(BOOST_TEST_CASE(&test_rolling_second_moment));
    test->add(BOOST_TEST_CASE(&test_rolling_fifth_moment));

    return test;
}


/*!
 * @file
 * @brief
 * @copyright alphya 2019-2021
 * 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 NYARUGA_UTIL_PARTIAL_DIFF_HPP
#define NYARUGA_UTIL_PARTIAL_DIFF_HPP

#pragma once

#include <concepts>
#include <type_traits>
#include <nyaruga_util/diff.hpp>
#include <nyaruga_util/bind_select_arg_replace.hpp>
#include <boost/hana/functional/arg.hpp>

namespace nyaruga {

namespace util {

namespace hana = boost::hana;

// Partial differentiation
template <std::size_t count, typename NumType = num_t, typename F>
constexpr auto partial_diff(F && f) noexcept
{
   return [f](auto&& ... args) noexcept -> NumType
   {
      return diff<NumType>(bind_select_arg_replace<count>(std::forward<decltype(f)>(f), args...))
         (hana::arg<count>(std::forward<decltype(args)>(args)...));
   };
}

} // namespace nyaruga::util

/* usage
#include <iostream>
#include <nyaruga_util/partial_diff.hpp>

int main()
{
   auto lambda = [](auto ... a) { return static_cast<nyaruga::util::num_t>((a * ... )); };

   std::cout << std::setprecision(18) << nyaruga::util::partial_diff<1>(lambda)(nyaruga::util::num_t(2), 5., 6.);
}
*/

#endif // #ifndef NYARUGA_UTIL_PARTIAL_DIFF_HPP


/*
 * Copyright (c) 2019 Nobuyuki Umetani
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <cmath>
#include <iostream>
#include <vector>
#include <chrono>
#include <Eigen/Core>
#if defined(_WIN32) // windows
#  define NOMINMAX   // to remove min,max macro
#  include <windows.h>  // should put before glfw3.h
#endif
#define GL_SILENCE_DEPRECATION
#include <GLFW/glfw3.h>

#include "delfem2/mshuni.h"
#include "delfem2/dtri2_v2dtri.h"
#include "delfem2/dtri.h"
#include "delfem2/eigen/ls_dense.h"
#include "delfem2/eigen/ls_sparse.h"
#include "delfem2/eigen/ls_ilu_sparse.h"
#include "delfem2/lsitrsol.h"
#include "delfem2/femsolidlinear.h"
#include "delfem2/glfw/viewer3.h"
#include "delfem2/glfw/util.h"
#include "delfem2/opengl/old/mshuni.h"

namespace dfm2 = delfem2;

void MakeMesh(
    std::vector<double>& aXY1,
    std::vector<unsigned int>& aTri1,
    std::vector<int>& aBCFlag,
    unsigned int ndim)
{
  std::vector< std::vector<double> > aaXY;
  const double len = 1.0;
  {
    aaXY.resize(1);
    aaXY[0].push_back(-len); aaXY[0].push_back(-len);
    aaXY[0].push_back(-len); aaXY[0].push_back(+len);
    aaXY[0].push_back(+len); aaXY[0].push_back(+len);
    aaXY[0].push_back(+len); aaXY[0].push_back(-len);
  }
  std::vector<delfem2::CDynPntSur> aPo2D;
  std::vector<delfem2::CDynTri> aETri;
  std::vector<delfem2::CVec2d> aVec2;
  delfem2::GenMesh(aPo2D,aETri,aVec2,
                   aaXY,0.05,0.05);
  MeshTri2D_Export(
      aXY1,aTri1,
      aVec2,aETri);
  const unsigned int np = aXY1.size()/2;
  aBCFlag.assign(np*ndim, 0);
  for(unsigned int ip=0;ip<np;++ip){
//    const double px = aXY1[ip*2+0];
    const double py = aXY1[ip*2+1];
    if( fabs(py-len) > 0.0001 ){ continue; }
    for(unsigned int idim=0;idim<ndim;++idim) {
      aBCFlag[ip * 2 + idim] = 1;
    }
  }
  std::cout<<"  ntri;"<<aTri1.size()/3<<"  nXY:"<<aXY1.size()/2<<std::endl;
}

void Solve1(
    std::vector<double>& aVal,
    const std::vector<double>& aXY1,
    const std::vector<unsigned int>& aTri1,
    const std::vector<int>& aBCFlag)
{
  const unsigned int np = aXY1.size()/2;
  const unsigned int nDoF = np*2;
  // -----------
  std::vector<unsigned int> psup_ind0, psup0;
  dfm2::JArray_PSuP_MeshElem(
      psup_ind0, psup0,
      aTri1.data(), aTri1.size()/3, 3,
      aXY1.size()/2);
  // -------------
  delfem2::CMatrixSparseBlock<Eigen::Matrix2d,Eigen::aligned_allocator<Eigen::Matrix2d>> mA;
  mA.Initialize(np);
  mA.SetPattern(psup_ind0.data(), psup_ind0.size(), psup0.data(), psup0.size());
  // ----------------------
  double myu = 10.0;
  double lambda = 10.0;
  double rho = 1.0;
  double g_x = 0.0;
  double g_y = -3.0;
  mA.setZero();
  Eigen::VectorXd vec_b(nDoF);
  vec_b.setZero();
  dfm2::MergeLinSys_SolidLinear_Static_MeshTri2D(
      mA,vec_b.data(),
      myu,lambda,rho,g_x,g_y,
      aXY1.data(), aXY1.size()/2,
      aTri1.data(), aTri1.size()/3,
      aVal.data());
  SetFixedBC_Dia(mA, aBCFlag.data(), 1.f);
  SetFixedBC_Col(mA, aBCFlag.data());
  SetFixedBC_Row(mA, aBCFlag.data());
  delfem2::setZero_Flag(vec_b, aBCFlag,0);
  // ---------------
  Eigen::VectorXd vec_x(vec_b.size());
  {
    double conv_ratio = 1.0e-6;
    int iteration = 1000;
    const std::size_t n = vec_b.size();
    Eigen::VectorXd tmp0(n), tmp1(n);
    std::vector<double> aConv = delfem2::Solve_CG(
        vec_b, vec_x, tmp0, tmp1,
        conv_ratio, iteration, mA);
    std::cout << aConv.size() << std::endl;
  }
//  SolveLinSys_PCG(mat_A,vec_b,vec_x,ilu_A, conv_ratio,iteration);
  // --------------
  {
    delfem2::CILU_SparseBlock<Eigen::Matrix2d,Eigen::aligned_allocator<Eigen::Matrix2d>> ilu;
    delfem2::ILU_SetPattern0(ilu,mA);
    delfem2::ILU_CopyValue(ilu,mA);
    delfem2::ILU_Decompose(ilu);
    Eigen::VectorXd vecX1(vec_b.size());
  }
  // --------------
  delfem2::XPlusAY(aVal,
      aBCFlag,
      1.0,vec_x);
}

int main()
{
  std::vector<unsigned int> aTri1;
  std::vector<double> aXY1;
  std::vector<int> aBCFlag; // master slave flag
  MakeMesh(
      aXY1, aTri1, aBCFlag,
      2);
  // ---
  std::vector<double> aVal;
  {
    const unsigned int np = aXY1.size()/2;
    aVal.assign(np * 2, 0.0);
    Solve1(aVal,aXY1,aTri1,aBCFlag);
  }
  // --------
  dfm2::glfw::CViewer3 viewer(1.5);
  dfm2::glfw::InitGLOld();
  viewer.OpenWindow();
  // ---------
  while(!::glfwWindowShouldClose(viewer.window)){
    viewer.DrawBegin_oldGL();
    delfem2::opengl::DrawMeshTri2D_FaceDisp2D(
        aXY1.data(), aXY1.size()/2,
        aTri1.data(), aTri1.size()/3,
        aVal.data(), 2);
    viewer.SwapBuffers();
    glfwPollEvents();
    viewer.ExitIfClosed();
  }
}


#include <iostream>
#include <algorithm>
#include <cmath>        // abs() for float, and fabs()
#include <math.h>       // pow()
#include <random>
#include <climits>

#include <Eigen/Dense>

#define print(var) \
  std::cout<<#var" = "<<(var)<<std::endl;
#define printstr(str) \
  std::cout<<str<<std::endl;
#define printLine() \
  std::cout<<"============================"<<std::endl;


#include <iostream>
#include <string>
#include <fstream>
#include "partitionCounting.h"
#include "counting.h"
#include "utils.h"

#include <Eigen/Dense>

using namespace Eigen;
using namespace std;

#include <iostream>
#include <iomanip>
#include <sstream>
#include <chrono>
typedef std::chrono::high_resolution_clock Clock;

// define the format you want, you only need one instance of this...
const static IOFormat CSVFormat(StreamPrecision, DontAlignCols, ", ", "\n");

static void show_usage(std::string name)
{
    std::cerr << "Usage: " << name << " <option(s)>"
              << "Options:\n"
              << "\t-h,--help\t\tShow this help message\n"
              << "\t-n, NUM\tSpecify the number n from which the partitions are generated. Default: 20."
              << std::endl;
}

// To write the results in a CSV file
void writeToCSVfile(const string &name, const MatrixXUL &matrix) {
    std::ofstream file(name.c_str());
    file << matrix.format(CSVFormat);
}

int main(int argc, char *argv[]) {
  // n is the maximum sum of the elements of the compositions
  int n=20;
  for (int i = 1; i < argc; ++i) {
    std::string arg = argv[i];
    if ((arg == "-h") || (arg == "--help")) {
        show_usage(argv[0]);
        return 0;
    } else if ((arg == "-n")) {
        if (i + 1 < argc) { // Make sure we aren't at the end of argv!
            n = atoi(argv[++i]); // Increment 'i' so we don't get the argument as the next argv[i].
        } else { // Uh-oh, there was no argument to the destination option.
            std::cerr << "The -n option requires one argument." << std::endl;
            return 1;
        }
    } else {
      show_usage(argv[0]);
      return 1;
    }
  }

  // Definition of the alpha parameter, choose a value in (0,2)
  double alpha = 20.0;
  // This object will be called for counting the partitions
  Counter ct(n);
  // Partition generation
  cout << "[INF] Generating partitions of n=" << n << std::endl;
  // Enumerate all the partitions of n. They will come in ascending lexicographical order
  std::list<std::vector<unsigned int> >partitionsOfN;
  ascPartition(n,partitionsOfN);

  // Add the trivial one because the algorithm does not give it as an output
  std::vector<unsigned int> trivialPartition; trivialPartition.push_back(n);
  partitionsOfN.push_back(trivialPartition);

  // Number of partitions
  int dim = partitionsOfN.size();
  cout << "[INF] Number of partitions:" << dim << std::endl;

  // Esta es la parte que nos interesa, vamos a estudiar una cadena de Markov con valores en las composiciones
  // (lo que llamo composiciones es nuestra manera de representar las particiones con el número
  // de bloques de cada tamaño, en plan (2,1,1...) )

  cout << "[INF] Filling Rmatrix" << endl;
  // Definition of the state matrix, the rows Rmatrix[i] are compositions
  // Rmatrix= np.zeros((dim,n), dtype=int)
  MatrixXi Rmatrix(dim,n);
  int i=0;
  // Enumerate all the generated partitions
  for (auto partition: partitionsOfN) {
    // Take the partition one by one
    // Count how many elements in the partition have the value j+1.
    for (int j=0; j<n; j++) {
      Rmatrix(i,j) = countElements(partition,j+1);
    }
    i++;
  }
  cout << Rmatrix << endl;

  // Precompute all the sum(R[i])
  cout << "[INF] Computing rowwise sums" << endl;
  MatrixXi S = Rmatrix.rowwise().sum();

  // Constructs all the compositions
  std::cout << "[INF] Building the list of partition descriptors (compositions)" << std::endl;
  std::vector<partitionDescriptor> P;
  for (auto partition: partitionsOfN) {
    P.push_back(partitionDescriptor(partition,n));
  }
  auto t1 = Clock::now();

  // Precompute all the Cbr or read them from file
  std::cout << "[INF] Computing counts" << std::endl;
  MatrixXUL Combin(dim,dim);
  bool computeCombin = true;
  if (computeCombin)
    for (int j=0; j<dim; j++) {
      printBar((float)j/dim);
      ct.resetValues();
      for (int i=0; i<j; i++) {
        Combin(i,j)=ct.recursiveCount_DescBreak(P[i],P[j]);
      }
    }
  auto t2 = Clock::now();
  std::cout << std::endl;
  std::cout << "[INF] Took: " << std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count() << " seconds" << std::endl;

  Counter::printCalls();

  std::string fileName;
  std::stringstream ss(fileName);
  ss << "Combin-" << setw(3) << setfill('0') << n << ".csv";
  writeToCSVfile(ss.str(),Combin);

  return 0;

//      df = pd.DataFrame(data=Combin.astype(float))
//      df.to_csv('outfile' + str(n) + '.csv', sep=' ', header=False, float_format='%.10f', index=False)
//  else
//  //    try:
//          df  =pd.read_csv('outfile' + str(n) + '.csv', delim_whitespace=True, header=None)
//          Combin=df.astype(float).values
//          computeCombin = False
//      except IOError as e:
//          print("Error in reading file")
//          print(e)
//  printCalls()
//  print(Combin)
//  print(Combin.min())
//  print(Combin.max())

}