/*
 * Website:
 *      https://github.com/wo3kie/dojo
 *
 * Author:
 *      Lukasz Czerwinski
 *
 * Training set:
 *      http://www.dt.fee.unicamp.br/~tiago/smsspamcollection/smsspamcollection.zip
 *
 * Compilation:
 *      g++ --std=c++11 bayes.cpp -o bayes
 *
 * Usage:
 *      $ ./bayes
 *      URGENT! You have won a 1 week FREE membership in our £100,000 Prize Jackpot!
 *      ...
 *      {{ham,-118.253},{spam,-89.9372}}
 */

#include <cmath>
#include <iostream>
#include <fstream>
#include <map>
#include <string>

#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>

#include "./output.hpp"

struct ItemStats
{
    unsigned counter_ = 0;
    double probability_ = 0;
};

struct ClassStats
{
    unsigned itemCounter_ = 0;
    double minProbability_ = 1;
    std::map< std::string, ItemStats > itemStats_;
};

class NaiveBayes
{
public:
    void learn( std::string const className, std::vector< std::string > const & items ){
        itemCounter_ += 1;

        ClassStats & classStats = stats_[ className ];
        classStats.itemCounter_ += 1;

        for( auto const & item : items ){
            classStats.itemStats_[ item ].counter_ += 1;
        }
    }

    void recalculateStats(){
        for( auto & clazz : stats_ ){
            recalculateClass( clazz );
        }
    }

    std::map< std::string, double > classify( std::vector< std::string > const & items ) const {
        std::map< std::string, double > classes;

        for( auto const & pair : stats_ ){
            std::string const & name = pair.first;
            ClassStats const & stat = pair.second;

            double const classProbability = 1.0 * stat.itemCounter_ / itemCounter_;
            classes[ name ] = std::log( classProbability ) + classifyClass( pair, items );

            std::cout
                << "class '" << name << "'"
                << " has probability: " << classProbability
                << " (" << std::log( classProbability ) << ")"
                << " and a final result: " << classes[ name ]
                << std::endl;
        }

        return classes;
    }

private:
    static double classifyClass(
        std::pair< std::string const, ClassStats > const & clazz,
        std::vector< std::string > const & items
    ){
        std::string const & name = clazz.first;
        ClassStats const & stats = clazz.second;

        double probability = 0;

        for( auto const & item : items ){
            auto const & itemIterator = stats.itemStats_.find( item );

            if( itemIterator == stats.itemStats_.end() ){
                probability += std::log( stats.minProbability_ );

                std::cout
                    << "class '" << name << "'"
                    << " item '" << item << "'"
                    << " not found: " << stats.minProbability_
                    << " (" << std::log( stats.minProbability_ ) << ")"
                    << std::endl;
            }
            else{
                probability += std::log( itemIterator->second.probability_ );

                std::cout
                    << "class '" << name << "'"
                    << " item '" << item  << "'"
                    << " found: " << itemIterator->second.probability_
                    << " (" << std::log( itemIterator->second.probability_ ) << ")"
                    << std::endl;
            }
        }

        return probability;
    }

    static void recalculateClass( std::pair< std::string const, ClassStats > & clazz ){
        unsigned allItemsCounter = 0;
        ClassStats & classStats = clazz.second;

        for( auto const & pair : classStats.itemStats_ ){
            ItemStats const & itemStats = pair.second;

            allItemsCounter += itemStats.counter_;
        }

        for( auto & pair : classStats.itemStats_ ){
            ItemStats & itemStats = pair.second;

            itemStats.probability_ = 1.0 * itemStats.counter_ / allItemsCounter;
        }

        double minProbability = 1;

        for( auto const & pair : clazz.second.itemStats_ ){
            ItemStats const & itemStats = pair.second;

            if( minProbability > itemStats.probability_ ){
                minProbability = itemStats.probability_;
            }
        }

        classStats.minProbability_ = minProbability;

    }

private:
    unsigned itemCounter_ = 0;
    std::map< std::string, ClassStats > stats_;
};

int main(){
    NaiveBayes bayes;

    std::ifstream spamFile( "bayes.spam.txt" );

    if( ! spamFile ){
        std::cerr << "ERROR: Can not open a file 'bayes.spam.txt'" << std::endl;
        return 1;
    }

    std::string line;

    while( std::getline( spamFile, line ) ){
        boost::tokenizer<> tokenizer( line );

        std::vector< std::string > items;

        for( std::string const & token : tokenizer ){
            items.push_back( boost::algorithm::to_lower_copy( token ) );
        }

        bayes.learn( "spam", items );
    }

    std::ifstream hamFile( "bayes.ham.txt" );

    if( ! hamFile ){
        std::cerr << "ERROR: Can not open a file 'bayes.ham.txt'" << std::endl;
        return 2;
    }

    while( std::getline( hamFile, line ) ){
        boost::tokenizer<> tokenizer( line );

        std::vector< std::string > items;

        for( std::string const & token : tokenizer ){
            items.push_back( boost::algorithm::to_lower_copy( token ) );
        }

        bayes.learn( "ham", items );
    }

    bayes.recalculateStats();

    while( std::getline( std::cin, line ) ){
        std::vector< std::string > items;
        boost::tokenizer<> tokenizer( line );

        for( std::string const & token : tokenizer ){
            items.push_back( boost::algorithm::to_lower_copy( token ) );
        }

        std::map< std::string, double > const & classesProbability
            = bayes.classify( items );

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

        return 0;
    }
}


#include "cryptonote_config.h"
#include "common/exp2.h"
#include "int-util.h"
#include <vector>
#include <boost/lexical_cast.hpp>

#include "service_node_rules.h"

namespace service_nodes {

	uint64_t get_staking_requirement(cryptonote::network_type m_nettype, uint64_t height)
	{
		if (m_nettype != cryptonote::MAINNET)
			return COIN * 100;

		uint64_t hardfork_height = m_nettype == cryptonote::MAINNET ? 106950 : 581 /* stagenet */;
		if (height < hardfork_height) height = hardfork_height;

		uint64_t height_adjusted = height - hardfork_height;
		uint64_t base = 0, variable = 0;
		if (height >= 352846)
		{
			base = 70000 * COIN;
			variable = (20000.0 * COIN) / triton::exp2(height_adjusted / 356446.0);
		}
		else
		{
			base = 10000 * COIN;
			variable = (30000.0 * COIN) / triton::exp2(height_adjusted / 129600.0);
		}

		uint64_t result = base + variable;
		return result;
	}

	uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement)
	{
		uint64_t hi, lo, resulthi, resultlo;
		lo = mul128(staking_requirement, portions, &hi);
		div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo);
		return resultlo;
	}

	bool check_service_node_portions(const std::vector<uint64_t>& portions, const uint64_t min_portions)
	{
		uint64_t portions_left = STAKING_PORTIONS;

		for (const auto portion : portions) {
			const uint64_t min_portions = std::min(portions_left, min_portions);
			if (portion < min_portions || portion > portions_left) return false;
			portions_left -= portion;
		}

		return true;
	}

	uint64_t get_portions_to_make_amount(uint64_t staking_requirement, uint64_t amount)
	{
		uint64_t lo, hi, resulthi, resultlo;
		lo = mul128(amount, STAKING_PORTIONS, &hi);
		if (lo > UINT64_MAX - (staking_requirement - 1))
			hi++;
		lo += staking_requirement-1;
		div128_64(hi, lo, staking_requirement, &resulthi, &resultlo);
		return resultlo;
	}

	static bool get_portions_from_percent(double cur_percent, uint64_t& portions) {

		if(cur_percent < 0.0 || cur_percent > 100.0) return false;

		// Fix for truncation issue when operator cut = 100 for a pool Service Node.
		if (cur_percent == 100.0)
		{
			portions = STAKING_PORTIONS;
		}
		else
		{
			portions = (cur_percent / 100.0) * STAKING_PORTIONS;
		}

		return true;
	}

	bool get_portions_from_percent_str(std::string cut_str, uint64_t& portions) {

		if(!cut_str.empty() && cut_str.back() == '%')
		{
		cut_str.pop_back();
		}

		double cut_percent;
		try
		{
		cut_percent = boost::lexical_cast<double>(cut_str);
		}
		catch(...)
		{
		return false;
		}

		return get_portions_from_percent(cut_percent, portions);
	}

}


#include "stdafx.h"

#include <vector>

#include <Eigen/Dense>
#include <Eigen/SVD>
#include <Eigen/Cholesky>

#include <Eigen/Sparse>
#include <Eigen/SparseCholesky>

#include <iostream>

#include <math/func.h>
#include <math/matrix.h>
#include <math/SparseBlockSquareMatrix.h>

#include <import.h>
#include <api/cl_wrapper.h>

#include <time.h>

Eigen::VectorXf pcg( const SparseBlockSquareMatrix &A, const Eigen::VectorXf &b, int maxIters, float threshold = 1e-4 ) {
	Eigen::VectorXf M = A.diagonal();
	for ( int i = 0; i < M.rows(); i++ ) {
		if ( fabs( M[i] ) > threshold ) {
			M[i] = 1.0f / M[i];
		} else {
			M[i] = 1.0f;
		}
	}

	Eigen::VectorXf x = Eigen::VectorXf::Zero(A.size());
	Eigen::VectorXf p = Eigen::VectorXf::Zero(A.size());
	Eigen::VectorXf r = b; // b - A * x

	int m_numIters = 0;
	clock_t begin = clock();
	for ( int i = 0; i < maxIters; i++ ) {
		m_numIters++;

		Eigen::VectorXf Mr = M.array() * r.array();
		float rMr = r.dot( Mr );
		if ( threshold <= 0 && rMr < 1e-6 ) {
			rMr = 0.0f;
		} else {
			rMr = 1.0f / rMr;
		}
		p += Mr * rMr;

		Eigen::VectorXf Ap = A * p;
		float pAp = p.dot( Ap );
		if ( threshold <= 0 && pAp < 1e-6 ) {
			pAp = 0.0f;
		} else {
			pAp = 1.0f / pAp;
		}
		x +=  p * pAp;
		r -= Ap * pAp;

		float rme = sqrt( r.dot( r ) / A.size() );
		if ( rme < 1e-6 ) {
			break;
		}
	}
	clock_t end = clock();
	double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
	printf( "pcg cpu (ms) : %.3f : %.3f : %d\n", time_spent * 1000, time_spent * 1000 / m_numIters, m_numIters );

	return x;
}

void cl_reduction(
	cl::CommandQueue &cq,
	cl::Kernel &kn_reduction,
	cl::Mem &mem,
	int elem_num,
	int global_work_size, int local_work_size
) {
	int group_num = ( global_work_size + local_work_size - 1 ) / local_work_size;
	global_work_size = local_work_size * group_num;
	while ( elem_num > 1 ) { //
		int activeGroupNum = std::min( group_num, ( elem_num + local_work_size - 1 ) / local_work_size );
		cq.Kernel1D( ( kn_reduction << (int)elem_num, (int)activeGroupNum, cl::Arg( sizeof( int ) * local_work_size ), mem ), local_work_size * activeGroupNum, local_work_size );
		elem_num = activeGroupNum;
	}
}

float cl_dot_product(
	cl::CommandQueue &cq,
	cl::Kernel &kn_mul_v_v, cl::Kernel &kn_reduction,
	cl::Mem &mem_src0, cl::Mem &mem_src1, cl::Mem &mem_dst,
	int elem_num,
	int global_work_size, int local_work_size
) {
	// rMr = r * Mr;
	cq.Kernel1D( (kn_mul_v_v << elem_num, mem_src0, mem_src1, mem_dst ), global_work_size, local_work_size );

	// rMr' = r dot rMr;
	cl_reduction( cq, kn_reduction, mem_dst, elem_num, global_work_size, local_work_size );

	float dot = 0.0f;
	cq.ReadBuffer( mem_dst, CL_TRUE, 0, sizeof( float ), &dot );
	return dot;
}

class cl_block_pcg {
public :
	int m_size, m_blockSize, m_gridSize;

	int m_numIters;

	// mem
	// static matrix
	cl::Mem mem_M;
	cl::Mem mem_A_blocks;
	cl::Mem mem_A_blockInfos;
	cl::Mem mem_A_rowScan;

	// updating vector
	cl::Mem mem_x;
	cl::Mem mem_p;
	cl::Mem mem_r;

	// temp vector
	cl::Mem mem_Mv;
	cl::Mem mem_dot;

	// kernel
	cl::Kernel kn_reduction;
	cl::Kernel kn_zero_v;
	cl::Kernel kn_mul_v_v;
	cl::Kernel kn_mad_v_s;
	cl::Kernel kn_mul_m_v;

	void genTopology( cl::Context &context, SparseBlockSquareMatrix &A ) {
		m_size = A.size();
		m_blockSize = A.blockSize();
		m_gridSize = A.gridSize();

		size_t bufferSize = sizeof( float ) * m_size;

		// static topology
		mem_A_blockInfos.CreateBuffer( context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, A.m_blockInfos );
		mem_A_rowScan.CreateBuffer( context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, A.m_rowScan );

		// matrix
		mem_A_blocks.CreateBuffer( context, CL_MEM_READ_WRITE, A.m_blocks.size() * sizeof( float ) );
		mem_M.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize ); // A diagonal

		// updating vector
		mem_x.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize );
		mem_p.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize );
		mem_r.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize );

		// temp vector
		mem_Mv.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize ); // Mr, Ap
		mem_dot.CreateBuffer( context, CL_MEM_READ_WRITE, bufferSize ); // rMr, pAp
	}

	void initMatrix( cl::CommandQueue &cq, SparseBlockSquareMatrix &A ) {
		size_t bufferSize = sizeof( float ) * m_size;
		int local_work_size = 256;
		int global_work_size = ceil( m_size, local_work_size );

		Eigen::VectorXf M = A.diagonal();
		for ( int i = 0; i < M.rows(); i++ ) {
			if ( fabs( M[i] ) > 1e-6f ) {
				M[i] = 1.0f / M[i];
			} else {
				M[i] = 1.0f;
			}
		}

		cq.WriteBuffer( mem_A_blocks, CL_TRUE, A.m_blocks );
		cq.WriteBuffer( mem_M, CL_TRUE, 0, bufferSize, M.data() );
	}

	void initVector( cl::CommandQueue &cq, Eigen::VectorXf &b ) {
		size_t bufferSize = sizeof( float ) * m_size;
		int local_work_size = 256;
		int global_work_size = ceil( m_size, local_work_size );

		// x = 0;
		cq.Kernel1D( (kn_zero_v << m_size, mem_x), global_work_size, local_work_size );
		//cq.WriteBuffer( mem_x, CL_TRUE, 0, bufferSize, x.data() );

		// p = 0;
		cq.Kernel1D( (kn_zero_v << m_size, mem_p), global_work_size, local_work_size );

		// r = b = b - A * x
		cq.WriteBuffer( mem_r, CL_TRUE, 0, bufferSize, b.data() );
	}

	void getResult( cl::CommandQueue &cq, Eigen::VectorXf &x ) {
		x = Eigen::VectorXf::Zero( m_size );
		size_t bufferSize = sizeof( float ) * m_size;
		cq.ReadBuffer( mem_x, CL_TRUE, 0, bufferSize, x.data() );
	}

	void compute(
		cl::CommandQueue &cq,
		int maxIters,
		float threshold,
		int local_work_size,
		int local_work_size_A
	) {
		int global_work_size = ceil( m_size, local_work_size );

		m_numIters = 0;
		clock_t begin = clock();
		for ( int i = 0; i < maxIters; i++ ) {
			m_numIters++;

			// Mr = M * r;
			cq.Kernel1D( (kn_mul_v_v << m_size, mem_M, mem_r, mem_Mv ), global_work_size, local_work_size );

			// rMr = r dot Mr;
			float rMr = cl_dot_product( cq, kn_mul_v_v, kn_reduction, mem_r, mem_Mv, mem_dot, m_size, global_work_size, local_work_size );
			if ( rMr < threshold * m_size ) {
				break;
			}
			if ( threshold <= 0 && rMr < 1e-6 ) {
				rMr = 0.0f;
			} else {
				rMr = 1.0f / rMr;
			}

			// p += Mr / rMr'
			cq.Kernel1D( (kn_mad_v_s << m_size, mem_Mv, rMr, mem_p ), global_work_size, local_work_size );

			// Ap = A * p
			cq.Kernel1D( (kn_zero_v << m_size, mem_Mv), global_work_size, local_work_size );
			//cq.Kernel1D( (kn_mul_m_v << m_gridSize, mem_A_rowScan, mem_A_blockInfos, mem_A_blocks, mem_p, mem_Mv ), ceil( m_gridSize, 256 ), 256 );
			cq.Kernel1D( (kn_mul_m_v << m_gridSize, m_blockSize, mem_A_rowScan, mem_A_blockInfos, mem_A_blocks, mem_p, mem_Mv, cl::Arg( m_blockSize * local_work_size_A * sizeof( float ) ) ), m_gridSize *local_work_size_A, local_work_size_A );

			// pAp = p dot Ap
			float pAp = cl_dot_product( cq, kn_mul_v_v, kn_reduction, mem_p, mem_Mv, mem_dot, m_size, global_work_size, local_work_size );
			if ( threshold <= 0 && pAp < 1e-6 ) {
				pAp = 0.0f;
			} else {
				pAp = 1.0f / pAp;
			}

			// r -= Ap / pAp;
			cq.Kernel1D( (kn_mad_v_s << m_size, mem_Mv, -pAp, mem_r), global_work_size, local_work_size );

			// x +=  p / pAp;
			cq.Kernel1D( (kn_mad_v_s << m_size, mem_p , pAp, mem_x), global_work_size, local_work_size );

#if 0
			float rme =  cl_dot_product( cq, kn_mul_v_v, kn_reduction, mem_r, mem_r, mem_dot, m_size, global_work_size, local_work_size );
			rme = sqrt( rme / m_size );
			printf( "PCG : %d : %.3f\n", i, rme );
			if ( rme < 1e-6 ) {
				break;
			}
#endif
		}
		cq.Finish();

		clock_t end = clock();
		double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

		printf( "pcg gpu (ms) : %.3f : %.3f : %d\n", time_spent * 1000, time_spent * 1000 / m_numIters, m_numIters );
	}

	void compileProgram( cl::Context &context ) {
		// read source
		{
			std::vector<char> source;
			read_text_file( "../../src/cl-shader/reduction.txt", source );
			const char *sources[] = {
				"#define T float\n",
				source.data(),
				0
			};

			// program
			const char *flags = "-cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros";
			cl::Program program;
			program.Create( context, 2, sources, 0, flags );

			// kernal
			kn_reduction.Create( program, "reduction" );
		}

		{
			std::vector<char> source;
			read_text_file( "../../src/cl-shader/vector.txt", source );
			const char *sources[] = {
				source.data(),
				0
			};

			// program
			const char *flags = "-cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros";
			cl::Program program;
			program.Create( context, 1, sources, 0, flags );

			// kernal
			kn_zero_v.Create( program, "zero_v" );
			kn_mul_v_v.Create( program, "mul_v_v" );
			kn_mad_v_s.Create( program, "mad_v_s" );

			kn_mul_m_v.Create( program, "mul_m_v" );
		}
	}
};

void TestPCG( void ) {
	int dim = 12 * 100;

	// A : positive definite matrix
	Eigen::MatrixXf A = Eigen::MatrixXf::Random( dim, dim );
	A = A * A.transpose();
	A += Eigen::MatrixXf::Identity( dim, dim ) * dim;
	printf( "Random A\n" );
	//std::cout << A << std::endl;

	// x
	Eigen::VectorXf x = Eigen::VectorXf::Random( dim );

	// b
	Eigen::VectorXf b = A * x;

#if 0
	{
		clock_t begin = clock();
		Eigen::VectorXf xx = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
		clock_t end = clock();
		double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
		printf( "cpu eigen time %fms\n", time_spent * 1000 );

		std::cout << ( xx - x ).dot( xx - x ) / xx.rows() << std::endl << std::endl;
	}
#endif

	std::cout << std::endl << "=============== Eigen ===============" << std::endl;
	{
		// fill A and b
		Eigen::ConjugateGradient<Eigen::MatrixXf, Eigen::Lower|Eigen::Upper> cg;
		clock_t begin = clock();

		cg.compute(A);
		x = cg.solve(b);

		clock_t end = clock();
		double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
		printf( "cpu eigen (ms) : %.3f : %.3f : %d\n", time_spent * 1000, time_spent * 1000 / cg.iterations(), cg.iterations() );
		std::cout << cg.error() << std::endl;
	}

	SparseBlockSquareMatrix B;
	B.createFromDenseMatrix( A, 12 );

	float threshold = 0.0f;

	std::cout << std::endl << "=============== CPU ===============" << std::endl;
	{
		Eigen::VectorXf xx = pcg( B, b, 100, threshold );
		std::cout << ( xx - x ).dot( xx - x ) / xx.rows() << std::endl << std::endl;
	}

	std::cout << std::endl << "=============== GPU ===============" << std::endl;
	{
		// context
		cl::Context context;
		cl::System::CreateContext( context );
		cl::CommandQueue cq( context );

		cl_block_pcg pcg;
		pcg.compileProgram( context );

		pcg.genTopology( context, B );

		pcg.initMatrix( cq, B );
		pcg.initVector( cq, b );

		pcg.compute( cq, 100, threshold, 256, 256 );

		Eigen::VectorXf xx;
		pcg.getResult( cq, xx );
		std::cout << ( xx - x ).dot( xx - x ) / xx.rows() << std::endl << std::endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	TestPCG();

	return 0;
}


#ifndef __PICSAR_MULTIPHYSICS_SPECIAL_FUNCTIONS__
#define __PICSAR_MULTIPHYSICS_SPECIAL_FUNCTIONS__

//This .hpp file is an extremely thin wrapper around special functions
//(Bessel functions for now) defined either in the STL (if C++17 is available)
//or in Boost library as a fallback.

//TODO: should we foresee a flag FORCE_USE_BOOST ?

//Set build option for the Bessel functions.
// 1) from STL (if C++ version > 14)
// 2) from Boost library

#include <limits>

#if __cplusplus > 201402L
    #define PXRMP_SPECFUNC_WITH_CXX17
    #include <cmath>
#else
    #define PXRMP_SPECFUNC_WITH_BOOST
    #include <boost/math/special_functions/bessel.hpp>
#endif

//Should be included by all the src files of the library
#include "qed_commons.h"

//############################################### Declaration

namespace picsar{
    namespace multi_physics{

        //For the moment we need just modified Bessel functions of the
        //second kind.
        //Different combinations of argument types can be accepted
        //(e.g. double + double or double + float).
    #ifdef PXRMP_SPECFUNC_WITH_CXX17
        template<typename _REAL_ARG1, typename _REAL_ARG2>
        auto k_v(_REAL_ARG1 v, _REAL_ARG2 x);
    #elif defined(PXRMP_SPECFUNC_WITH_BOOST)
        template<typename _REAL_ARG1, typename _REAL_ARG2>
        constexpr auto k_v(_REAL_ARG1 v, _REAL_ARG2 x)
        -> decltype(boost::math::cyl_bessel_k(v, x));
    #endif

    }
}

//############################################### Implementation

#ifdef PXRMP_SPECFUNC_WITH_CXX17

template<typename _REAL_ARG1, typename _REAL_ARG2>
auto picsar::multi_physics::k_v(_REAL_ARG1 v, _REAL_ARG2 x)
{
    return std::cyl_bessel_k(v, x);
}

#elif defined(PXRMP_SPECFUNC_WITH_BOOST)

template<typename _REAL_ARG1, typename _REAL_ARG2>
constexpr auto picsar::multi_physics::k_v(_REAL_ARG1 v, _REAL_ARG2 x)
->decltype(boost::math::cyl_bessel_k(v, x))
{
    return boost::math::cyl_bessel_k(v, x);;
}

#endif

#endif //__PICSAR_MULTIPHYSICS_SPECIAL_FUNCTIONS__


// HEADER
#include <path_follower/controller/robotcontroller_kinematic_SLP.h>

// THIRD PARTY
#include <visualization_msgs/MarkerArray.h>

// PROJECT
#include <path_follower/parameters/path_follower_parameters.h>
#include <path_follower/utils/cubic_spline_interpolation.h>
#include <cslibs_navigation_utilities/MathHelper.h>
#include <path_follower/utils/pose_tracker.h>
#include <path_follower/utils/visualizer.h>

// SYSTEM
#include <cmath>
#include <deque>
#include <boost/algorithm/clamp.hpp>

#include <path_follower/factory/controller_factory.h>

REGISTER_ROBOT_CONTROLLER(RobotController_Kinematic_SLP, kinematic_SLP, default_collision_avoider);

using namespace Eigen;

RobotController_Kinematic_SLP::RobotController_Kinematic_SLP():
    RobotController(),
    cmd_(this),
    vn_(0),
    delta_(0),
    Ts_(0.02),
    ind_(0),
    xe_(0),
    ye_(0)
{

}

void RobotController_Kinematic_SLP::stopMotion()
{

    cmd_.speed = 0;
    cmd_.direction_angle = 0;
    cmd_.rotation = 0;

    MoveCommand mcmd = cmd_;
    publishMoveCommand(mcmd);
}

void RobotController_Kinematic_SLP::initialize()
{
    RobotController::initialize();

    //reset the index of the current point on the path
    ind_ = 0;

    // desired velocity
    vn_ = std::min(PathFollowerParameters::getInstance()->max_velocity(), velocity_);
    ROS_DEBUG_STREAM("velocity_: " << velocity_ << ", vn: " << vn_);
}

void RobotController_Kinematic_SLP::start()
{

}

void RobotController_Kinematic_SLP::reset()
{
    RobotController::reset();
}

void RobotController_Kinematic_SLP::setPath(Path::Ptr path)
{
    RobotController::setPath(path);
}

RobotController::MoveCommandStatus RobotController_Kinematic_SLP::computeMoveCommand(MoveCommand *cmd)
{
    // omni drive can rotate.
    *cmd = MoveCommand(true);

    if(path_interpol.n() < 2) {
        ROS_ERROR("[Line] path is too short (N = %d)", (int) path_interpol.n());

        stopMotion();
        return MoveCommandStatus::REACHED_GOAL;
    }

    /// get the pose as pose(0) = x, pose(1) = y, pose(2) = theta
    Eigen::Vector3d current_pose = pose_tracker_->getRobotPose();

    double x_meas = current_pose[0];
    double y_meas = current_pose[1];
    double theta_meas = current_pose[2];
    ///***///

    RobotController::findOrthogonalProjection();

    if(RobotController::isGoalReached(cmd)){
       return RobotController::MoveCommandStatus::REACHED_GOAL;
    }

    ///Compute the control for the current point on the path

    //robot direction angle in path coordinates
    double theta_e = MathHelper::AngleDelta(path_interpol.theta_p(ind_), theta_meas);

    //robot position vector module
    double r = hypot(x_meas - path_interpol.p(ind_), y_meas - path_interpol.q(ind_));

    //robot position vector angle in world coordinates
    double theta_r = atan2(y_meas - path_interpol.q(ind_), x_meas - path_interpol.p(ind_));

    //robot position vector angle in path coordinates
    double delta_theta = MathHelper::AngleDelta(path_interpol.theta_p(ind_), theta_r);

    //current robot position in path coordinates
    xe_ = r * cos(delta_theta);
    ye_ = r * sin(delta_theta);

    ///***///

    ///Check the driving direction, and set the complementary angle in path coordinates, if driving backwards

    if (getDirSign() < 0.0) {
        theta_e = MathHelper::NormalizeAngle(M_PI + theta_e);
        ROS_WARN_THROTTLE(1, "Driving backwards...");
    }

    ///***///

    ///Compute the delta_ and its derivative

    double delta_old = delta_;

    delta_ = MathHelper::AngleClamp(-getDirSign()*opt_.theta_a()*tanh(ye_));

    double delta_prim = (delta_ - delta_old)/Ts_;
    ///***///

    ///Lyapunov-curvature speed control

    //Lyapunov function as a measure of the path following error
    double v = vn_;
    double V1 = 0.5*(std::pow(xe_,2) + std::pow(ye_,2)) + (0.5/opt_.gamma())*std::pow((theta_e - delta_),2);

    //use v/2 as the minimum speed, and allow larger values when the error is small
    if (V1 >= opt_.epsilon()) v = 0.5*v;

    else if (V1 < opt_.epsilon()) v = v/(1 + opt_.b()*std::abs(path_interpol.curvature(ind_)));

    ///***///

    ///Compute the next point on the path

    double s_old = path_interpol.s_new();

    //calculate the speed of the "virtual vehicle"
    double s_prim_tmp = v * cos(theta_e) + opt_.k1() * xe_;
    path_interpol.set_s_prim(s_prim_tmp > 0 ? s_prim_tmp : 0);

    //approximate the first derivative and calculate the next point
    double s_temp = Ts_*path_interpol.s_prim() + s_old;
    path_interpol.set_s_new(s_temp > 0 ? s_temp : 0);

    ///***///

    ///Direction control

    cmd_.direction_angle = 0;

    //omega_m = theta_e_prim + curv*s_prim
    double omega_m = delta_prim - opt_.gamma()*ye_*v*(sin(theta_e) - sin(delta_))
            /(theta_e - delta_) - opt_.k2()*(theta_e - delta_) + path_interpol.curvature(ind_)*path_interpol.s_prim();

    omega_m = boost::algorithm::clamp(omega_m, -opt_.max_angular_velocity(), opt_.max_angular_velocity());
    cmd_.rotation = omega_m;

    ///***///

    ///Speed control

    double exp_factor = RobotController::exponentialSpeedControl();
    v = v * exp_factor;

    cmd_.speed = getDirSign()*std::max((double)PathFollowerParameters::getInstance()->min_velocity(), fabs(v));

    ///***///

    ///plot the moving reference frame together with position vector and error components

    if (visualizer_->MarrayhasSubscriber()) {
        visualizer_->drawFrenetSerretFrame(getFixedFrame(), 0, current_pose, xe_, ye_, path_interpol.p(ind_),
                                           path_interpol.q(ind_), path_interpol.theta_p(ind_));
    }

    ///***///

    ///Compute the index of the new point

    double s_diff = std::numeric_limits<double>::max();
    uint old_ind = ind_;

    for (unsigned int i = old_ind; i < path_interpol.n(); i++){

        double s_diff_curr = std::abs(path_interpol.s_new() - path_interpol.s(i));

        if(s_diff_curr < s_diff){

            s_diff = s_diff_curr;
            ind_ = i;

        }

    }

    if(old_ind != ind_) {
        path_->fireNextWaypointCallback();
    }

    ///***///

    if (visualizer_->hasSubscriber()) {
        visualizer_->drawSteeringArrow(pose_tracker_->getFixedFrameId(), 1, pose_tracker_->getRobotPoseMsg(), cmd_.direction_angle, 0.2, 1.0, 0.2);
    }

    ///***///

    *cmd = cmd_;

    return MoveCommandStatus::OKAY;
}

void RobotController_Kinematic_SLP::publishMoveCommand(const MoveCommand &cmd) const
{
    geometry_msgs::Twist msg;
    msg.linear.x  = cmd.getVelocity();
    msg.linear.y  = 0;
    msg.angular.z = cmd.getRotationalVelocity();

    cmd_pub_.publish(msg);
}


#include <crave/ConstrainedRandom.hpp>
#include <vector>
#include <boost/timer.hpp>

using crave::rand_obj;
using crave::randv;
using crave::inside;

class sudoku : public rand_obj {
public:
  randv<short> field[9][9];

	sudoku(rand_obj* parent = 0) : rand_obj(parent) {
    short numbers[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		for (int i = 0; i < 9; i++)
			for (int j = 0; j < 9; j++)
        constraint( inside(field[i][j](), numbers) );

		for (int i = 0; i < 9; i++)
			for (int j = 0; j < 9; j++)
				for (int k = j + 1; k < 9; k++)
         	constraint( field[i][j]() != field[i][k]() );

		for (int j = 0; j < 9; j++)
			for (int i = 0; i < 9; i++)
				for (int k = i + 1; k < 9; k++)
         	constraint( field[i][j]() != field[k][j]() );

		for (int i = 0; i < 9; i++)
			for (int j = 0; j < 9; j++)
				constraint
          ( field[i][j]() != field[(i + 1) % 3 + i - (i % 3)][ j                       ]() )
          ( field[i][j]() != field[(i + 2) % 3 + i - (i % 3)][ j                       ]() )

          ( field[i][j]() != field[ i                       ][(j + 1) % 3 + j - (j % 3)]() )
          ( field[i][j]() != field[(i + 1) % 3 + i - (i % 3)][(j + 1) % 3 + j - (j % 3)]() )
          ( field[i][j]() != field[(i + 2) % 3 + i - (i % 3)][(j + 1) % 3 + j - (j % 3)]() )

          ( field[i][j]() != field[ i                       ][(j + 2) % 3 + j - (j % 3)]() )
          ( field[i][j]() != field[(i + 1) % 3 + i - (i % 3)][(j + 2) % 3 + j - (j % 3)]() )
          ( field[i][j]() != field[(i + 2) % 3 + i - (i % 3)][(j + 2) % 3 + j - (j % 3)]() );
	}
};

template <unsigned N, bool ToBeFailed>
class sudoku_container : public rand_obj {
 public:
	sudoku_container(rand_obj* parent = 0) : rand_obj(parent) {
    for (unsigned i = 0; i < N; i++) {
      list.push_back(new sudoku(this));
      if (ToBeFailed)
        constraint(x[i]() != x[i]());
      else
        constraint(x[i]() == x[i]());
    }
	}

 private:
  std::vector<sudoku*> list;
  randv<unsigned> x[N];
};

int main (int argc, char *argv[]) {
  boost::timer timer;
  crave::init("crave.cfg");
	sudoku_container<9, true> sc;
  sc.constraint.enable_multithreading();
	for (int i = 0; i < 20; i++) {
    std::cout << (sc.next() ? "solved" : "failed") << std::endl;
	}
  std::cout << "complete: " << timer.elapsed() << std::endl;
	return 0;
}


/*
Headers for file "anfis.cpp".

Copyright (c) 2021 Gabriele Gilardi
*/

#ifndef __ANFIS_H_
#define __ANFIS_H_

#include <Eigen/Dense>

using namespace Eigen;

/* Helper functions */
ArrayXd build_class_table(ArrayXd Y, double tol=1.e-5);
ArrayXi get_classes(ArrayXd Y, ArrayXd table, double tol=1.e-5);

/* ANFIS class */
class AnfisType {

    public:

        // Data
        int n_pf;                   // Number of premise MFs
        int n_cf;                   // Number of consequent MFs
        int n_var;                  // Number of variables
        ArrayXd mu;                 // Premise parameters mu
        ArrayXd s;                  // Premise parameters s
        ArrayXd c;                  // Premise parameters c
        ArrayXXd A;                 // Consequent parameters A

        // Functions
        AnfisType();
        void init(ArrayXi MFs, int n_outputs);
        double create_model(ArrayXd theta, ArrayXXd X, ArrayXXd Y);
        double create_model(ArrayXd theta, ArrayXXd X, ArrayXi Yc);
        ArrayXXd eval_data(ArrayXXd Xp);
        ArrayXXd eval_data(ArrayXXd Xp, ArrayXd table);
        void info();
        ~AnfisType();

    private:

        // Data
        int n_outputs;              // Number of labels/classes
        int n_inputs;               // Number of features/inputs
        ArrayXi MFs;                   // Number of MFs in each feature/input
        ArrayXXi combs;                // Combinations of premise MFs

        // Functions
        void build_combinations();
        void build_param(ArrayXd theta);
        ArrayXXd forward_steps(ArrayXXd X);
        ArrayXXd f_activation(ArrayXXd z);
        ArrayXXd logsig(ArrayXXd z);
};

#endif


#include "Clausen.h"
#include <boost/math/special_functions/zeta.hpp>
namespace ucd {

double Clausen( double theta )
{
    if ( theta == 0 )
        return 0;
    while ( theta < 0 )
        theta += 2*M_PI;
    while ( theta > 2*M_PI )
        theta -= 2*M_PI;
    if ( theta > M_PI )
    {
        theta -= M_PI;
        double diff = theta - M_PI/2;
        theta -= 2*diff;
        return -Clausen( theta );
    }

    // precompute coefficients for the partial sum part of the Clausen
    // function
    static std::vector< double > coeff;
    if ( coeff.size( ) == 0 )
    {
        std::vector< double > num, den, frac;
        for ( int i = 1; i < 20; ++i )
        {
            num.push_back( boost::math::zeta( 2*i ) );
            den.push_back( i*(2*i + 1) );
            frac.push_back( num.back( ) / den.back( ) );
        }

        coeff.push_back( 0 );
        for ( int i = 0; i < frac.size( ); ++i )
        {
            coeff.push_back( 0 );
            coeff.push_back( frac[i] );
        }
    }

    double res = 1 - log( theta );

    double hornerSum = 0.0;
    double term = theta / (2 * M_PI);
    for ( int i = coeff.size( ) - 1; i >= 0; --i )
    {
        if ( i == coeff.size( ) - 1 )
            hornerSum = coeff[i];
        else
            hornerSum = coeff[i] + hornerSum*term;
    }
    res += hornerSum;

    return theta*res;
}

} // namespace ucd


// Copyright (C) 2012  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include <dlib/clustering.h>

#include "tester.h"

namespace
{
    using namespace test;
    using namespace dlib;
    using namespace std;

    logger dlog("test.clustering");

// ----------------------------------------------------------------------------------------

    void make_test_graph(
        dlib::rand& rnd,
        std::vector<sample_pair>& edges,
        std::vector<unsigned long>& labels,
        const int groups,
        const int group_size,
        const int noise_level,
        const double missed_edges
    )
    {
        labels.resize(groups*group_size);

        for (unsigned long i = 0; i < labels.size(); ++i)
        {
            labels[i] = i/group_size;
        }

        edges.clear();
        for (int i = 0; i < groups; ++i)
        {
            for (int j = 0; j < group_size; ++j)
            {
                for (int k = 0; k < group_size; ++k)
                {
                    if (j == k)
                        continue;

                    if (rnd.get_random_double() < missed_edges)
                        continue;

                    edges.push_back(sample_pair(j+group_size*i, k+group_size*i, 1));
                }
            }
        }

        for (int k = 0; k < groups*noise_level; ++k)
        {
            const int i = rnd.get_random_32bit_number()%labels.size();
            const int j = rnd.get_random_32bit_number()%labels.size();
            edges.push_back(sample_pair(i,j,1));
        }

    }

// ----------------------------------------------------------------------------------------

    void make_modularity_matrices (
        const std::vector<sample_pair>& edges,
        matrix<double>& A,
        matrix<double>& P,
        double& m
    )
    {
        const unsigned long num_nodes = max_index_plus_one(edges);
        A.set_size(num_nodes, num_nodes);
        P.set_size(num_nodes, num_nodes);
        A = 0;
        P = 0;
        std::vector<double> k(num_nodes,0);

        for (unsigned long i = 0; i < edges.size(); ++i)
        {
            const unsigned long n1 = edges[i].index1();
            const unsigned long n2 = edges[i].index2();
            k[n1] += edges[i].distance();
            if (n1 != n2)
            {
                k[n2] += edges[i].distance();
                A(n2,n1) += edges[i].distance();
            }

            A(n1,n2) += edges[i].distance();
        }

        m = sum(A)/2;

        for (long r = 0; r < P.nr(); ++r)
        {
            for (long c = 0; c < P.nc(); ++c)
            {
                P(r,c) = k[r]*k[c]/(2*m);
            }
        }

    }

    double compute_modularity_simple (
        const std::vector<sample_pair>& edges,
        std::vector<unsigned long> labels
    )
    {
        double m;
        matrix<double> A,P;
        make_modularity_matrices(edges, A, P, m);
        matrix<double> B = A - P;

        double Q = 0;
        for (long r = 0; r < B.nr(); ++r)
        {
            for (long c = 0; c < B.nc(); ++c)
            {
                if (labels[r] == labels[c])
                {
                    Q += B(r,c);
                }
            }
        }
        return 1.0/(2*m) * Q;
    }

// ----------------------------------------------------------------------------------------

    void test_modularity(dlib::rand& rnd)
    {
        print_spinner();
        std::vector<sample_pair> edges;
        std::vector<ordered_sample_pair> oedges;
        std::vector<unsigned long> labels;

        make_test_graph(rnd, edges, labels, 10, 30, 3, 0.10);
        if (rnd.get_random_double() < 0.5)
            remove_duplicate_edges(edges);
        convert_unordered_to_ordered(edges, oedges);

        const double m1 = modularity(edges, labels);
        const double m2 = compute_modularity_simple(edges, labels);
        const double m3 = modularity(oedges, labels);

        DLIB_TEST(std::abs(m1-m2) < 1e-12);
        DLIB_TEST(std::abs(m2-m3) < 1e-12);
        DLIB_TEST(std::abs(m3-m1) < 1e-12);
    }

    void test_newman_clustering(dlib::rand& rnd)
    {
        print_spinner();
        std::vector<sample_pair> edges;
        std::vector<unsigned long> labels;

        make_test_graph(rnd, edges, labels, 5, 30, 3, 0.10);
        if (rnd.get_random_double() < 0.5)
            remove_duplicate_edges(edges);

        std::vector<unsigned long> labels2;

        unsigned long num_clusters = newman_cluster(edges, labels2);
        DLIB_TEST(labels.size() == labels2.size());
        DLIB_TEST(num_clusters == 5);

        for (unsigned long i = 0; i < labels.size(); ++i)
        {
            for (unsigned long j = 0; j < labels.size(); ++j)
            {
                if (labels[i] == labels[j])
                {
                    DLIB_TEST(labels2[i] == labels2[j]);
                }
                else
                {
                    DLIB_TEST(labels2[i] != labels2[j]);
                }
            }
        }
    }

    void test_chinese_whispers(dlib::rand& rnd)
    {
        print_spinner();
        std::vector<sample_pair> edges;
        std::vector<unsigned long> labels;

        make_test_graph(rnd, edges, labels, 5, 30, 3, 0.10);
        if (rnd.get_random_double() < 0.5)
            remove_duplicate_edges(edges);

        std::vector<unsigned long> labels2;

        unsigned long num_clusters;
        if (rnd.get_random_double() < 0.5)
            num_clusters = chinese_whispers(edges, labels2, 200, rnd);
        else
            num_clusters = chinese_whispers(edges, labels2);

        DLIB_TEST(labels.size() == labels2.size());
        DLIB_TEST(num_clusters == 5);

        for (unsigned long i = 0; i < labels.size(); ++i)
        {
            for (unsigned long j = 0; j < labels.size(); ++j)
            {
                if (labels[i] == labels[j])
                {
                    DLIB_TEST(labels2[i] == labels2[j]);
                }
                else
                {
                    DLIB_TEST(labels2[i] != labels2[j]);
                }
            }
        }
    }

    void test_bottom_up_clustering()
    {
        std::vector<dpoint> pts;
        pts.push_back(dpoint(0.0,0.0));
        pts.push_back(dpoint(0.5,0.0));
        pts.push_back(dpoint(0.5,0.5));
        pts.push_back(dpoint(0.0,0.5));

        pts.push_back(dpoint(3.0,3.0));
        pts.push_back(dpoint(3.5,3.0));
        pts.push_back(dpoint(3.5,3.5));
        pts.push_back(dpoint(3.0,3.5));

        pts.push_back(dpoint(7.0,7.0));
        pts.push_back(dpoint(7.5,7.0));
        pts.push_back(dpoint(7.5,7.5));
        pts.push_back(dpoint(7.0,7.5));

        matrix<double> dists(pts.size(), pts.size());
        for (long r = 0; r < dists.nr(); ++r)
            for (long c = 0; c < dists.nc(); ++c)
                dists(r,c) = length(pts[r]-pts[c]);

        matrix<unsigned long,0,1> truth(12);
        truth = 0, 0, 0, 0,
                1, 1, 1, 1,
                2, 2, 2, 2;

        std::vector<unsigned long> labels;
        DLIB_TEST(bottom_up_cluster(dists, labels, 3) == 3);
        DLIB_TEST(mat(labels) == truth);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1, 4.0) == 3);
        DLIB_TEST(mat(labels) == truth);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1, 4.95) == 2);
        truth = 0, 0, 0, 0,
                0, 0, 0, 0,
                1, 1, 1, 1;
        DLIB_TEST(mat(labels) == truth);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1) == 1);
        truth = 0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0;
        DLIB_TEST(mat(labels) == truth);

        dists.set_size(0,0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 3) == 0);
        DLIB_TEST(labels.size() == 0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1) == 0);
        DLIB_TEST(labels.size() == 0);

        dists.set_size(1,1);
        dists = 1;
        DLIB_TEST(bottom_up_cluster(dists, labels, 3) == 1);
        DLIB_TEST(labels.size() == 1);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1) == 1);
        DLIB_TEST(labels.size() == 1);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1, 0) == 1);
        DLIB_TEST(labels.size() == 1);
        DLIB_TEST(labels[0] == 0);

        dists.set_size(2,2);
        dists = 1;
        DLIB_TEST(bottom_up_cluster(dists, labels, 3) == 2);
        DLIB_TEST(labels.size() == 2);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(labels[1] == 1);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1) == 1);
        DLIB_TEST(labels.size() == 2);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(labels[1] == 0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1, 1) == 1);
        DLIB_TEST(labels.size() == 2);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(labels[1] == 0);
        DLIB_TEST(bottom_up_cluster(dists, labels, 1, 0.999) == 2);
        DLIB_TEST(labels.size() == 2);
        DLIB_TEST(labels[0] == 0);
        DLIB_TEST(labels[1] == 1);
    }

    void test_segment_number_line()
    {
        dlib::rand rnd;

        std::vector<double> x;
        for (int i = 0; i < 5000; ++i)
        {
            x.push_back(rnd.get_double_in_range(-1.5, -1.01));
            x.push_back(rnd.get_double_in_range(-0.99, -0.01));
            x.push_back(rnd.get_double_in_range(0.01, 1));
        }

        auto r = segment_number_line(x,1);
        std::sort(r.begin(), r.end());
        DLIB_TEST(r.size() == 3);
        DLIB_TEST(-1.5 <= r[0].lower && r[0].lower < r[0].upper && r[0].upper <= -1.01);
        DLIB_TEST(-0.99 <= r[1].lower && r[1].lower < r[1].upper && r[1].upper <= -0.01);
        DLIB_TEST(0.01 <= r[2].lower && r[2].lower < r[2].upper && r[2].upper <= 1);

        x.clear();
        for (int i = 0; i < 5000; ++i)
        {
            x.push_back(rnd.get_double_in_range(-2, 1));
            x.push_back(rnd.get_double_in_range(-2, 1));
            x.push_back(rnd.get_double_in_range(-2, 1));
        }

        r = segment_number_line(x,1);
        DLIB_TEST(r.size() == 3);
        r = segment_number_line(x,1.5);
        DLIB_TEST(r.size() == 2);
        r = segment_number_line(x,10.5);
        DLIB_TEST(r.size() == 1);
        DLIB_TEST(-2 <= r[0].lower && r[0].lower < r[0].upper && r[0].upper <= 1);
    }

    class test_clustering : public tester
    {
    public:
        test_clustering (
        ) :
            tester ("test_clustering",
                    "Runs tests on the clustering routines.")
        {}

        void perform_test (
        )
        {
            test_bottom_up_clustering();
            test_segment_number_line();

            dlib::rand rnd;

            std::vector<sample_pair> edges;
            std::vector<unsigned long> labels;
            DLIB_TEST(newman_cluster(edges, labels) == 0);
            DLIB_TEST(chinese_whispers(edges, labels) == 0);

            edges.push_back(sample_pair(0,1,1));
            DLIB_TEST(newman_cluster(edges, labels) == 1);
            DLIB_TEST(labels.size() == 2);
            DLIB_TEST(chinese_whispers(edges, labels) == 1);
            DLIB_TEST(labels.size() == 2);

            edges.clear();
            edges.push_back(sample_pair(0,0,1));
            DLIB_TEST(newman_cluster(edges, labels) == 1);
            DLIB_TEST(labels.size() == 1);
            DLIB_TEST(chinese_whispers(edges, labels) == 1);
            DLIB_TEST(labels.size() == 1);

            edges.clear();
            edges.push_back(sample_pair(1,1,1));
            DLIB_TEST(newman_cluster(edges, labels) == 1);
            DLIB_TEST(labels.size() == 2);
            DLIB_TEST(chinese_whispers(edges, labels) == 2);
            DLIB_TEST(labels.size() == 2);

            edges.push_back(sample_pair(0,0,1));
            DLIB_TEST(newman_cluster(edges, labels) == 2);
            DLIB_TEST(labels.size() == 2);
            DLIB_TEST(chinese_whispers(edges, labels) == 2);
            DLIB_TEST(labels.size() == 2);

            for (int i = 0; i < 10; ++i)
                test_modularity(rnd);

            for (int i = 0; i < 10; ++i)
                test_newman_clustering(rnd);

            for (int i = 0; i < 10; ++i)
                test_chinese_whispers(rnd);

        }
    } a;

}


/**
 * @file
 * @brief Investigates when parametrization from reference element breaks down
 * @author Anian Ruoss
 * @date   2019-02-04 13:36:17
 * @copyright MIT License
 */

#include <lf/geometry/geometry.h>
#include <lf/quad/quad.h>
#include <lf/refinement/refinement.h>

#include <Eigen/Eigen>
#include <filesystem>
#include <fstream>
#include <string>

/**
 * @brief Stores an Eigen::MatrixXd to .csv file
 * @param file_path path to .csv file
 * @param matrix matrix to be saved
 */
void writeMatrixToCSV(const std::string& file_path,
                      const Eigen::MatrixXd& matrix) {
  const static Eigen::IOFormat CSVFormat(Eigen::StreamPrecision,
                                         Eigen::DontAlignCols, ", ", "\n");
  std::ofstream file(file_path.c_str());
  file << matrix.format(CSVFormat);
  file.close();
}

/**
 * @brief Saves the global vertex/midpoint coordinates
 * @param file_path path to .csv file
 * @param geom second-order geometry object
 */
void storeSecondOrderCoords(const std::string& file_path,
                            const lf::geometry::Geometry& geom) {
  const Eigen::MatrixXd& local_vertex_coords = geom.RefEl().NodeCoords();
  const long num_vertices = local_vertex_coords.cols();

  Eigen::MatrixXd all_local_coords(local_vertex_coords.rows(),
                                   2 * num_vertices);

  // compute local vertex and midpoint coordinates from reference element
  for (auto col_idx = 0; col_idx < num_vertices; ++col_idx) {
    all_local_coords.col(col_idx) = local_vertex_coords.col(col_idx);
    all_local_coords.col(col_idx + num_vertices) =
        (local_vertex_coords.col(col_idx) +
         local_vertex_coords.col((col_idx + 1) % num_vertices)) /
        2.;
  }

  writeMatrixToCSV(file_path, geom.Global(all_local_coords));
}

/**
 * @brief Evaluates parametrization and corresponding jacobian determinant
 * @param base_file_path base path for .csv files
 * @param geom geometry object
 * @param qr_order order of quadrature rule to sample points from
 */
void storeParametrizationEvals(const std::string& base_file_path,
                               const lf::geometry::Geometry& geom,
                               const size_t& qr_order) {
  // use quadrature to sample random points on the reference element
  auto qr = lf::quad::make_QuadRule(geom.RefEl(), qr_order);

  const auto& points = qr.Points();
  const Eigen::MatrixXd& jacobians = geom.Jacobian(points);
  Eigen::VectorXd determinants(points.cols());

  for (Eigen::Index point_idx = 0; point_idx < points.cols(); ++point_idx) {
    determinants(point_idx) = jacobians
                                  .block(0, point_idx * geom.DimLocal(),
                                         geom.DimGlobal(), geom.DimLocal())
                                  .determinant();
  }

  writeMatrixToCSV(base_file_path + "_refpoints.csv", points);
  writeMatrixToCSV(base_file_path + "_points.csv", geom.Global(points));
  writeMatrixToCSV(base_file_path + "_jacdets.csv", determinants);
}

/**
 * @brief Computes volume of geometry object by means of overkill quadrature
 * @param geom geometry object
 * @return geometry volume
 */
double computeGeometryVolume(const lf::geometry::Geometry& geom) {
  const auto qr = lf::quad::make_QuadRule(geom.RefEl(), 23);

  const auto& points = qr.Points();
  const auto& weights = qr.Weights();
  const auto& integrationElements = geom.IntegrationElement(points);

  double vol = 0.;

  for (size_t j = 0; j < points.cols(); ++j) {
    vol += weights(j) * integrationElements(j);
  }

  return vol;
}

int main() {
  // create a directory to store results
  const std::string results_dir = "results/";
  std::filesystem::create_directory(results_dir);

  // define second-order geometry elements
  lf::geometry::TriaO2 tria(
      (Eigen::MatrixXd(2, 6) << 1, 6, 3, 3.7, 4.2, 2.3, 1, 3, 8, 1.2, 5.2, 4.5)
          .finished());
  lf::geometry::TriaO2 tria_degenerate(
      (Eigen::MatrixXd(2, 6) << 1, 6, 3, 5, 4.5, 1.75, 1, 3, 8, 4, 9, 6.5)
          .finished());
  lf::geometry::QuadO2 quad((Eigen::MatrixXd(2, 8) << 3, 7, 4, 1, 5, 5.4, 2.5,
                             1.8, 1, 3, 7, 8, 2.5, 5.9, 6.9, 4.1)
                                .finished());
  lf::geometry::QuadO2 quad_degenerate(
      (Eigen::MatrixXd(2, 8) << 3, 7, 4, 1, 6, 5, 2, 2, 1, 3, 7, 8, 0, 6, 5, 2)
          .finished());

  // store vertex/midpoint coordinates, random point evaluations and
  // corresponding jacobian determinants for every geometry object
  for (const auto& geom_element :
       {std::pair<std::string, lf::geometry::Geometry*>{"tria", &tria},
        std::pair<std::string, lf::geometry::Geometry*>{"tria_degenerate",
                                                        &tria_degenerate},
        std::pair<std::string, lf::geometry::Geometry*>{"quad", &quad},
        std::pair<std::string, lf::geometry::Geometry*>{"quad_degenerate",
                                                        &quad_degenerate}}) {
    storeSecondOrderCoords(results_dir + geom_element.first + "_coords.csv",
                           *geom_element.second);
    storeParametrizationEvals(results_dir + geom_element.first,
                              *geom_element.second, 50);

    const double volume = computeGeometryVolume(*geom_element.second);
    double refined_volume = 0;

    // compute child geometries by means of regular refinement
    auto children = geom_element.second->ChildGeometry(
        lf::refinement::Hybrid2DRefinementPattern(geom_element.second->RefEl(),
                                                  lf::refinement::rp_regular),
        0);

    // store vertex/midpoint coordinates, random point evaluations and
    // corresponding jacobian determinants for every child geometry object
    for (std::size_t child_idx = 0; child_idx < children.size(); ++child_idx) {
      storeSecondOrderCoords(results_dir + geom_element.first + "_child_" +
                                 std::to_string(child_idx) + "_coords.csv",
                             *children[child_idx]);
      storeParametrizationEvals(results_dir + geom_element.first + "_child_" +
                                    std::to_string(child_idx),
                                *children[child_idx], 20);

      refined_volume += computeGeometryVolume(*children[child_idx]);
    }

    // save volumes
    writeMatrixToCSV(results_dir + geom_element.first + "_volumes.csv",
                     (Eigen::VectorXd(2) << volume, refined_volume).finished());
  }

  return 0;
}


// Testing the do_equal function

#include <CGAL/config.h>
#ifndef CGAL_USE_CORE
#include <iostream>
int main()
{
  std::cout << "Sorry, this example needs CORE ..." << std::endl;
  return 0;
}

#else

#include <vector>
#include <list>

#include <boost/type_traits/is_same.hpp>

#include <CGAL/Cartesian.h>
#include <CGAL/Quotient.h>
#include <CGAL/MP_Float.h>
#include <CGAL/CORE_algebraic_number_traits.h>
#include <CGAL/Arr_polyline_traits_2.h>
#include <CGAL/Arr_conic_traits_2.h>
#include <CGAL/tags.h>
#include <CGAL/Arr_tags.h>

////////////////////
//conic traits
////////////////////
typedef CGAL::CORE_algebraic_number_traits            Nt_traits;
typedef Nt_traits::Rational                           Rational;
typedef Nt_traits::Algebraic                          Algebraic;
typedef CGAL::Cartesian<Rational>                     Rat_kernel;
typedef Rat_kernel::Point_2                           Rat_point_2;
typedef Rat_kernel::Segment_2                         Rat_segment_2;
typedef Rat_kernel::Circle_2                          Rat_circle_2;
typedef CGAL::Cartesian<Algebraic>                    Alg_kernel;
typedef CGAL::Arr_conic_traits_2<Rat_kernel, Alg_kernel, Nt_traits>
                                                      Conic_traits_2;
typedef Conic_traits_2::Point_2                       Conic_point_2;
typedef Conic_traits_2::Curve_2                       Conic_curve_2;
typedef Conic_traits_2::X_monotone_curve_2            Conic_x_monotone_curve_2;
typedef CGAL::Arr_polyline_traits_2<Conic_traits_2>   Polycurve_conic_traits_2;
typedef Polycurve_conic_traits_2::X_monotone_curve_2  Pc_x_monotone_curve_2;
typedef Polycurve_conic_traits_2::Point_2             Pc_point_2;

// typedef CGAL::Arr_polyline_traits_2<
//                CGAL::Arr_conic_traits_2<CGAL::Cartesian<BigRat>,
//                CGAL::Cartesian<Expr>,
//                CGAL::CORE_algebraic_number_traits>
//              >::Point_2   test_point_2;

void check_equal()
{
  bool are_equal;

  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Equal_2 equal = traits.equal_2_object();
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();

  //create some curves
  Conic_point_2 ps1(Rational(1,4), 4);
  Conic_point_2 pt1(2, Rational(1,2));
  Conic_curve_2 c1(0, 0, 1, 0, 0, -1, CGAL::COUNTERCLOCKWISE, ps1, pt1);

  Conic_point_2 ps2(Rational(1,4), 4);
  Conic_point_2 pt2(2, Rational(1,2));
  Conic_curve_2 c2(0, 0, 1, 0, 0, -1, CGAL::COUNTERCLOCKWISE, ps2, pt2);

  Rat_point_2 ps3(Rational(1,4), 4);
  Rat_point_2 pmid3(Rational(3,2), 2);
  Rat_point_2 pt3(2, Rational(1,3));
  Conic_curve_2 c3(ps3, pmid3, pt3);

  Rat_point_2 ps4(1, 5);
  Rat_point_2 pmid4(Rational(3,2), 3);
  Rat_point_2 pt4(3, Rational(1,3));
  Conic_curve_2 c4(ps4, pmid4, pt4);

  // //make x_monotone
  Polycurve_conic_traits_2::X_monotone_curve_2 xmc1 =
    construct_x_monotone_curve_2(c1);
  Polycurve_conic_traits_2::X_monotone_curve_2 xmc2 =
    construct_x_monotone_curve_2(c2);
  Polycurve_conic_traits_2::X_monotone_curve_2 xmc3 =
    construct_x_monotone_curve_2(c3);
  Polycurve_conic_traits_2::X_monotone_curve_2 xmc4 =
    construct_x_monotone_curve_2(c4);

  are_equal = equal(xmc1, xmc2);
  std::cout << "Two equal conic arcs are computed as:  "
            << ((are_equal) ? "equal" : "Not equal") << std::endl;

  are_equal = equal(xmc3, xmc2);
  std::cout << "Two un-equal conic arcs are computed as:  "
            << ((are_equal) ? "equal" : "Not equal") << std::endl;

  are_equal = equal(xmc3, xmc4);
  std::cout << "Two un-equal conic arcs are computed as:  "
            << ((are_equal) ? "equal" : "Not equal") << std::endl;
 }

 template <typename Traits>
 void check_intersect(typename Traits::X_monotone_curve_2& xcv1,
                      typename Traits::X_monotone_curve_2& xcv2,
                      const Traits& traits)
 {
   typedef typename Traits::Multiplicity                Multiplicity;
   typedef typename Traits::Point_2                     Point_2;
   typedef typename Traits::X_monotone_curve_2          X_monotone_curve_2;
   typedef std::pair<Multiplicity, Point_2>             Intersection_point;
   typedef boost::variant<Intersection_point, X_monotone_curve_2>
     Intersection_result;

   std::vector<Intersection_result> intersection_points;
   traits.intersect_2_object()(xcv1, xcv2,
                               std::back_inserter(intersection_points));
   std::cout<< "Number of intersection Points: " << intersection_points.size()
            << std::endl;

   //dynamic cast the cgal_objects
   // std::vector< std::pair<Polycurve_conic_traits_2::Point_2,
   //                        Polycurve_conic_traits_2::Multiplicity> > pm_vector;
   // for(int i=0; i<intersection_points.size(); i++)
   // {
   //   std::pair<Polycurve_conic_traits_2::Point_2,
   //             Polycurve_conic_traits_2::Multiplicity> pm =
   //   CGAL::object_cast<std::pair<Polycurve_conic_traits_2::Point_2,
   //                               Polycurve_conic_traits_2::Multiplicity> >
   //                              (&(intersection_points[i]));
   //   pm_vector.push_back(pm);
   // }
 }

void check_compare_end_points_xy_2()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Compare_endpoints_xy_2 compare_endpoints_xy_2 =
    traits.compare_endpoints_xy_2_object();

  //create some curves
  Conic_point_2 ps1(Rational(1,4), 4);
  Conic_point_2 pt1(2, Rational(1,2));
  Conic_curve_2 c1(0, 0, 1, 0, 0, -1, CGAL::COUNTERCLOCKWISE, ps1, pt1);

  // Insert a parabolic arc that is supported by a parabola y = -x^2
  // (or: x^2 + y = 0) and whose endpoints are (-sqrt(3), -3) ~ (-1.73, -3)
  // and (sqrt(2), -2) ~ (1.41, -2). Notice that since the x-coordinates
  // of the endpoints cannot be accurately represented, we specify them
  // as the intersections of the parabola with the lines y = -3 and y = -2.
  // Note that the arc is clockwise oriented.
  Conic_curve_2
    c2 = Conic_curve_2(1, 0, 0, 0, 1, 0,         // The parabola.
                       CGAL::CLOCKWISE,
                       Conic_point_2(-1.73, -3), // Approximation of the source.
                       0, 0, 0, 0, 1, 3,         // The line: y = -3.
                       Conic_point_2(1.41, -2),  // Approximation of the target.
                       0, 0, 0, 0, 1, 2);        // The line: y = -2.
  assert(c2.is_valid());

  //make polyline x-monotone curves
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
    construct_x_monotone_curve_2(c1);
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc2 =
    construct_x_monotone_curve_2(c2);

  CGAL::Comparison_result res = compare_endpoints_xy_2(polyline_xmc1);
  std::cout << "compare_end_points_xy_2 for counterclockwise curve: "
            << (res == CGAL::SMALLER ? "SMALLER":
               (res == CGAL::LARGER ? "LARGER" : "EQUAL")) << std::endl;

  res = compare_endpoints_xy_2(polyline_xmc2);
  std::cout<< "compare_end_points_xy_2 for clockwise curve: "
           << (res == CGAL::SMALLER ? "SMALLER":
               (res == CGAL::LARGER ? "LARGER" : "EQUAL")) << std::endl;
}

template <typename Curve_type>
void check_split(Curve_type &xcv1, Curve_type &xcv2)
{
  Polycurve_conic_traits_2 traits;

  //split x poly-curves

  Conic_curve_2 c6(1,1,0,6,-26,162,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-7), Algebraic(13)),
                   Conic_point_2(Algebraic(-3), Algebraic(9)));
  Conic_curve_2 c7(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-3), Algebraic(9)),
                   Conic_point_2(Algebraic(0), Algebraic(0)));
  Conic_curve_2 c8(0,1,0,-1,0,0, CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(0), Algebraic(0)),
                   Conic_point_2(Algebraic(4), Algebraic(-2)));

  Conic_x_monotone_curve_2 xc6(c6);
  Conic_x_monotone_curve_2 xc7(c7);
  Conic_x_monotone_curve_2 xc8(c8);
  std::vector<Conic_x_monotone_curve_2> xmono_conic_curves_2;

  xmono_conic_curves_2.push_back(xc6);
  xmono_conic_curves_2.push_back(xc7);
  Pc_x_monotone_curve_2 split_expected_1 =
    traits.construct_x_monotone_curve_2_object()(xmono_conic_curves_2.begin(),
                                                 xmono_conic_curves_2.end());

  xmono_conic_curves_2.clear();
  xmono_conic_curves_2.push_back(xc8);
  Pc_x_monotone_curve_2 split_expected_2 =
    traits.construct_x_monotone_curve_2_object()(xmono_conic_curves_2.begin(),
                                                 xmono_conic_curves_2.end());

  Polycurve_conic_traits_2::X_monotone_curve_2 split_curve_1, split_curve_2;
  Polycurve_conic_traits_2::Point_2
    point_of_split = Polycurve_conic_traits_2::Point_2(0,0);

  //Split functor
  traits.split_2_object()(xcv2, point_of_split, split_curve_1, split_curve_2);

  bool split_1_chk = traits.equal_2_object()(split_curve_1, split_expected_1);
  bool split_2_chk = traits.equal_2_object()(split_curve_2, split_expected_2);

  if(split_1_chk && split_2_chk)
    std::cout << "Split is working fine" << std::endl;
  else
    std::cout << "Something is wrong with split" << std::endl;
}

void check_is_vertical()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Is_vertical_2 is_vertical =
    traits.is_vertical_2_object();

   //create a curve
  Rat_point_2 ps1(1, 10);
  Rat_point_2 pmid1(5, 4);
  Rat_point_2 pt1(10, 1);
  Conic_curve_2 c1(ps1, pmid1, pt1);

  //make x-monotone curve
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
    construct_x_monotone_curve_2(c1);

  bool result = is_vertical(polyline_xmc1);
  std::cout << "Is_verticle:: Expected first result is not vertivle: Computed: "
            << ((result)? "vertical" : "not vertical") << std::endl;
}

/*! */
template <typename stream>
bool read_orientation(stream& is, CGAL::Orientation& orient)
{
  int i_orient;
  is >> i_orient;
  orient = (i_orient > 0) ? CGAL::COUNTERCLOCKWISE :
    (i_orient < 0) ? CGAL::CLOCKWISE : CGAL::COLLINEAR;
  return true;
}

/*! */
template <typename stream>
bool read_app_point(stream& is, Conic_point_2& p)
{
  //waqar: original
  double x, y;
  is >> x >> y;
  p = Conic_point_2(Algebraic(x), Algebraic(y));
  return true;

  //waqar: my modification
  // long int rat_x_num, rat_x_den, rat_y_num, rat_y_den;
  // is >> rat_x_num >> rat_x_den >> rat_y_num >> rat_y_den;

  // Basic_number_type x(rat_x), y(rat_y);
  // p = Conic_point_2(Rational(rat_x_num, rat_x_den),
  //                   Rational(rat_y_num, rat_y_den));
  // return true;
}

/*! */
template <typename stream>
bool read_orientation_and_end_points(stream& is, CGAL::Orientation& orient,
                                     Conic_point_2& source,
                                     Conic_point_2& target)
{
  // Read the orientation.
  if (!read_orientation(is, orient)) return false;

  // Read the end points of the arc and create it.
  if (!read_app_point(is, source)) return false;
  if (!read_app_point(is, target)) return false;
  return true;
}

/*! */
template <typename stream, typename Curve>
bool read_general_arc(stream& is, Curve& cv)
{
  // Read a general conic, given by its coefficients <r,s,t,u,v,w>.
  Rational r, s, t, u, v, w;                // The conic coefficients.
  is >> r >> s >> t >> u >> v >> w;
    // Read the orientation.
  int i_orient = 0;
  is >> i_orient;
  CGAL::Orientation orient = (i_orient > 0) ? CGAL::COUNTERCLOCKWISE :
    (i_orient < 0) ? CGAL::CLOCKWISE : CGAL::COLLINEAR;

  // Read the approximated source, along with a general conic
  // <r_1,s_1,t_1,u_1,v_1,w_1> whose intersection with <r,s,t,u,v,w>
  // defines the source.
  Conic_point_2 app_source;
  if (!read_app_point(is, app_source)) return false;
  Rational r1, s1, t1, u1, v1, w1;
  is >> r1 >> s1 >> t1 >> u1 >> v1 >> w1;

  // Read the approximated target, along with a general conic
  // <r_2,s_2,t_2,u_2,v_2,w_2> whose intersection with <r,s,t,u,v,w>
  // defines the target.
  Conic_point_2 app_target;
  if (!read_app_point(is, app_target)) return false;

  Rational r2, s2, t2, u2, v2, w2;
  is >> r2 >> s2 >> t2 >> u2 >> v2 >> w2;

  std::cout << "line is: " << r << s << t << u << v << w << i_orient
            << r1 << s1 << t1 << u1 << v1 << w1
            << r2 << s2 << t2 << u2 << v2 << w2 << std::endl;

  // Create the conic arc.
  cv = Curve(r, s, t, u, v, w, orient,
             app_source, r1, s1, t1, u1, v1, w1,
             app_target, r2, s2, t2, u2, v2, w2);
  return true;
}

/*! */
template <typename stream, typename Curve>
bool read_general_conic(stream& is, Curve& cv)
{
  // Read a general conic, given by its coefficients <r,s,t,u,v,w>.
  Rational r, s, t, u, v, w;
  is >> r >> s >> t >> u >> v >> w;
  // Create a full conic (should work only for ellipses).
  cv = Curve(r, s, t, u, v, w);
  return true;
}

// /*! */
template <typename stream, typename Curve>
bool read_general_curve(stream& is, Curve& cv)
{
  Rational r, s, t, u, v, w;                // The conic coefficients.
  // Read a general conic, given by its coefficients <r,s,t,u,v,w>.
  is >> r >> s >> t >> u >> v >> w;
  CGAL::Orientation orient;
  Conic_point_2 source, target;
  if (!read_orientation_and_end_points(is, orient, source, target))
    return false;

  // Create the conic (or circular) arc.
  // std::cout << "arc coefficients: " << r << " " << s << " " << t << " "
  //           << u << " " << v << " " << w << std::endl;
  // std::cout << "Read Points : " << source.x() << " " << source.y() << " "
  //           << target.x() << " " << target.y() << std::endl;
  cv = Curve(r, s, t, u, v, w, orient, source, target);
  return true;
}
std::istream& skip_comments(std::istream& is, std::string& line)
{
  while (std::getline(is, line))
    if (!line.empty() && (line[0] != '#')) break;
  return is;
}

bool check_compare_y_at_x_2()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Compare_y_at_x_2 cmp_y_at_x_2 =
    traits.compare_y_at_x_2_object();
  //polycurve constructors
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_mono_polycurve = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Construct_curve_2  construct_polycurve =
    traits.construct_curve_2_object();

   //create a curve
  Rat_point_2 ps1(1, 10);
  Rat_point_2 pmid1(5, 4);
  Rat_point_2 pt1(10, 1);
  Conic_curve_2 c1(ps1, pmid1, pt1);

   //create a curve
  Rat_point_2 ps2(10, 1);
  Rat_point_2 pmid2(15, 5);
  Rat_point_2 pt2(20, 10);
  Conic_curve_2 c2(ps2, pmid2, pt2);

  Conic_curve_2 c3(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(0), Algebraic(0)),
                   Conic_point_2(Algebraic(3), Algebraic(9)));
  Conic_curve_2 c4(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(3), Algebraic(9)),
                   Conic_point_2(Algebraic(5), Algebraic(25)));

  std::vector<Conic_curve_2> conic_curves, conic_curves_2, Conic_curves_3;
  conic_curves.push_back(c1);
  conic_curves.push_back(c2);

  //conic_curves_2.push_back(c3);
  //conic_curves_2.push_back(c4);

  Conic_x_monotone_curve_2 xc1(c1);
  Conic_x_monotone_curve_2 xc2(c2);
  Conic_x_monotone_curve_2 xc3(c3);
  Conic_x_monotone_curve_2 xc4(c4);

  std::vector<Conic_x_monotone_curve_2> xmono_conic_curves, xmono_conic_curves_2;
  /* VERY IMPORTANT
   * For efficiency reasons, we recommend users not to construct x-monotone
   * conic arc directly, but rather use the Make_x_monotone_2 functor supplied
   * by the conic-arc traits class to convert conic curves to x-monotone curves.
   */
  xmono_conic_curves.push_back(xc1);
  xmono_conic_curves.push_back(xc2);
  xmono_conic_curves_2.push_back(xc3);
  xmono_conic_curves_2.push_back(xc4);

  //construct x-monotone poly-curve
  Polycurve_conic_traits_2::X_monotone_curve_2 conic_x_mono_polycurve =
    construct_x_mono_polycurve(xmono_conic_curves.begin(),
                               xmono_conic_curves.end());
  Polycurve_conic_traits_2::X_monotone_curve_2 conic_x_mono_polycurve_2 =
    construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
                               xmono_conic_curves_2.end());

  //construct poly-curve
  Polycurve_conic_traits_2::Curve_2 conic_polycurve =
    construct_polycurve(conic_curves.begin(), conic_curves.end());
  //Polycurve_conic_traits_2::Curve_2 conic_polycurve_2 =
  //  construct_polycurve(conic_curves_2.begin(), conic_curves_2.end());

  //make x-monotone curve
  //Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
  //  construct_x_monotone_curve_2(c1);

  //create points
  Polycurve_conic_traits_2::Point_2
    point_above_line = Polycurve_conic_traits_2::Point_2(2,10),
    point_below_line = Polycurve_conic_traits_2::Point_2(4,7),
    point_on_line = Polycurve_conic_traits_2::Point_2(2,4);

  CGAL::Comparison_result result;

  result =  cmp_y_at_x_2(point_above_line, conic_x_mono_polycurve_2);
  std::cout << "Compare_y_at_x_2:: for point above the curve computed Answer is:  "
            << (result == CGAL::SMALLER ? "Below":
               (result == CGAL::LARGER ? "Above" : "On-line")) << std::endl;

  result =  cmp_y_at_x_2(point_below_line, conic_x_mono_polycurve_2);
  std::cout << "Compare_y_at_x_2:: for point below the curve computed Answer is:  "
            << (result == CGAL::SMALLER ? "Below":
               (result == CGAL::LARGER ? "Above" : "On-line")) << std::endl;

  result =  cmp_y_at_x_2(point_on_line, conic_x_mono_polycurve_2);
  std::cout << "Compare_y_at_x_2:: for point on the curve computed Answer is:  "
            << (result == CGAL::SMALLER ? "Below":
               (result == CGAL::LARGER ? "Above" : "On-line")) << std::endl;

  return true;
}

void check_are_mergable()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Are_mergeable_2  are_mergeable_2 =
    traits.are_mergeable_2_object();

   //create a curve
  Rat_point_2 ps1(1, 10);
  Rat_point_2 pmid1(5, 4);
  Rat_point_2 pt1(10, 1);
  Conic_curve_2 c1(ps1, pmid1, pt1);

  Rat_point_2 ps2(10, 1);
  Rat_point_2 pmid2(15, 14);
  Rat_point_2 pt2(20, 20);
  Conic_curve_2 c2(ps2, pmid2, pt2);

  Rat_point_2 ps3(Rational(1,4), 4);
  Rat_point_2 pmid3(Rational(3,2), 2);
  Rat_point_2 pt3(2, Rational(1,3));
  Conic_curve_2 c3(ps3, pmid3, pt3);

  //construct x-monotone curve(compatible with polyline class)
   Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
     construct_x_monotone_curve_2(c1);
   Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc2 =
     construct_x_monotone_curve_2(c2);
   Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc3 =
     construct_x_monotone_curve_2(c3);

  bool result = are_mergeable_2(polyline_xmc1, polyline_xmc2);
  std::cout << "Are_mergable:: Mergable x-monotone polycurves are Computed as: "
            << ((result)? "Mergable" : "Not-Mergable") << std::endl;

  result = are_mergeable_2(polyline_xmc1, polyline_xmc3);
  std::cout << "Are_mergable:: Non-Mergable x-monotone polycurves are Computed as: "
            << ((result)? "Mergable" : "Not-Mergable") << std::endl;
}

void check_merge_2()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Merge_2  merge_2 = traits.merge_2_object();

  //create a curve
  Rat_point_2 ps1(1, 10);
  Rat_point_2 pmid1(5, 4);
  Rat_point_2 pt1(10, 1);
  Conic_curve_2 c1(ps1, pmid1, pt1);

  Rat_point_2 ps2(10, 1);
  Rat_point_2 pmid2(15, 14);
  Rat_point_2 pt2(20, 20);
  Conic_curve_2 c2(ps2, pmid2, pt2);

//construct x-monotone curve (compatible with polyline class)
 Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
   construct_x_monotone_curve_2(c1);
 Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc2 =
   construct_x_monotone_curve_2(c2);

 Polycurve_conic_traits_2::X_monotone_curve_2 merged_xmc;

 merge_2(polyline_xmc1, polyline_xmc2, merged_xmc);
 std::cout<< "Merge_2:: Mergable x-monotone curves merged successfully"
          << std:: endl;
}

void check_construct_opposite()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Construct_opposite_2 construct_opposite_2 =
    traits.construct_opposite_2_object();

  //create a curve
  Rat_point_2 ps1(1, 10);
  Rat_point_2 pmid1(5, 4);
  Rat_point_2 pt1(10, 1);
  Conic_curve_2     c1(ps1, pmid1, pt1);

  //construct x-monotone curve (compatible with polyline class)
 Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
   construct_x_monotone_curve_2(c1);
 Polycurve_conic_traits_2::X_monotone_curve_2 polyline_opposite_curve =
   construct_opposite_2(polyline_xmc1);

 std::cout<< "Construct_opposite_2:: Opposite curve created";
}

void check_compare_y_at_x_right()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Compare_y_at_x_right_2 cmp_y_at_x_right_2 =
    traits.compare_y_at_x_right_2_object();

  //create constructing curves
  Rat_point_2 ps2(1, 10);
  Rat_point_2 pmid2(5, 4);
  Rat_point_2 pt2(10, 1);
  Conic_curve_2 c1(ps2, pmid2, pt2);

  Rat_point_2 ps3(10, 1);
  Rat_point_2 pmid3(5, 4);
  Rat_point_2 pt3(1, 10);
  Conic_curve_2 c2(ps3, pmid3, pt3);

  //construct x-monotone curve (compatible with polyline class)
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
    construct_x_monotone_curve_2(c1);
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc2 =
    construct_x_monotone_curve_2(c2);
  Polycurve_conic_traits_2::Point_2 intersection_point =
    Polycurve_conic_traits_2::Point_2(5,4);

  CGAL::Comparison_result result;
  result = cmp_y_at_x_right_2(polyline_xmc1, polyline_xmc2, intersection_point);
  std::cout << "Compare_y_at_x_right:: Expected Answer: equal, Computed answer:  "
            << (result == CGAL::SMALLER ? "smaller":
               (result == CGAL::LARGER ? "Larger" : "equal")) << std::endl;
}

void check_compare_y_at_x_left()
{
  Polycurve_conic_traits_2 traits;
  Polycurve_conic_traits_2::Construct_x_monotone_curve_2
    construct_x_monotone_curve_2 = traits.construct_x_monotone_curve_2_object();
  Polycurve_conic_traits_2::Compare_y_at_x_left_2 cmp_y_at_x_left_2 =
    traits.compare_y_at_x_left_2_object();

  //create constructing curves
  Rat_point_2 ps2(1, 10);
  Rat_point_2 pmid2(5, 4);
  Rat_point_2 pt2(10, 1);
  Conic_curve_2 c1(ps2, pmid2, pt2);

  Rat_point_2 ps3(10, 1);
  Rat_point_2 pmid3(5, 4);
  Rat_point_2 pt3(1, 10);
  Conic_curve_2 c2(ps3, pmid3, pt3);

  //construct x-monotone curve(compatible with polyline class)
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc1 =
    construct_x_monotone_curve_2(c1);
  Polycurve_conic_traits_2::X_monotone_curve_2 polyline_xmc2 =
    construct_x_monotone_curve_2(c2);
  Polycurve_conic_traits_2::Point_2 intersection_point =
    Polycurve_conic_traits_2::Point_2(5,4);

  CGAL::Comparison_result result;

  result = cmp_y_at_x_left_2(polyline_xmc1, polyline_xmc2, intersection_point);
  std::cout << "Compare_y_at_x_left:: Expected Answer: equal, Computed answer:  "
            << (result == CGAL::SMALLER ? "smaller":
               (result == CGAL::LARGER ? "Larger" : "equal")) << std::endl;
}

template <typename GeometryTraits>
void check_make_x_monotne_curve(const typename GeometryTraits::Curve_2& c1)
{
  typename GeometryTraits::Point_2                      Point_2;
  typename GeometryTraits::X_monotone_curve_2           X_monotone_curve_2;
  typedef boost::variant<Point_2, X_monotone_curve_2>   Make_x_monotone_result;
  Polycurve_conic_traits_2 traits;
  std::vector<Make_x_monotone_result> objs;
  traits.make_x_monotone_2_object()(c1, std::back_inserter(objs));
  std::cout << "The polycurve is: " << c1 << std::endl;
  std::cout<< "The poly curve have been split into " << objs.size()
           << " polycurves" << std::endl;

  //const Pc_x_monotone_curve_2 *split_curve_1 =
  //  CGAL::object_cast<Pc_x_monotone_curve_2> (&(objs[0]));
  //const Pc_x_monotone_curve_2 *split_curve_2 =
  //  CGAL::object_cast<Pc_x_monotone_curve_2> (&(objs[1]));

  //std::cout << "The split curve 1 is: " << *split_curve_1 << std::endl;
  //std::cout << "The split curve 2 is: " << *split_curve_2 << std::endl;
}

template<typename Curve, typename Segment>
void check_push_front(Curve base_curve, Segment curve_tobe_pushed)
{
  Polycurve_conic_traits_2 traits;

  std::cout << "Base curve: " << base_curve << std::endl;
  std::cout << "push curve: " << curve_tobe_pushed << std::endl;

  traits.push_front_2_object()(base_curve, curve_tobe_pushed);
  std::cout << "result curve: " << base_curve << std::endl;
}

template<typename Curve, typename Segment>
void check_push_back(Curve& base_curve, Segment curve_tobe_pushed)
{
  Polycurve_conic_traits_2 traits;

  std::cout << "Base curve: " << base_curve << std::endl;
  std::cout << "push curve: " << curve_tobe_pushed << std::endl;

  traits.push_back_2_object()(base_curve, curve_tobe_pushed);

  std::cout << "result curve: " << base_curve << std::endl;
}

template<typename Segment>
void check_compare_x_2(const Segment& seg1, const Segment& seg2)
{
  Polycurve_conic_traits_2 traits;
  CGAL::Comparison_result result;

  result = traits.compare_x_2_object()(seg1, CGAL::ARR_MIN_END, seg2,
                                       CGAL::ARR_MIN_END);
  std::cout << "Compare_x_2:: Expected Answer: Larger, Computed answer:  "
            << (result == CGAL::SMALLER ? "smaller":
               (result == CGAL::LARGER ? "Larger" : "equal")) << std::endl;

  result = traits.compare_x_2_object()(seg1, CGAL::ARR_MIN_END, seg2,
                                       CGAL::ARR_MAX_END);
  std::cout << "Compare_x_2:: Expected Answer: Equal, Computed answer:  "
            << (result == CGAL::SMALLER ? "smaller":
               (result == CGAL::LARGER ? "Larger" : "equal")) << std::endl;
}

template<typename Curve>
void check_compare_points(Curve& cv)
{
  Polycurve_conic_traits_2 traits;
  CGAL::Arr_parameter_space result =
    traits.parameter_space_in_x_2_object()(cv, CGAL::ARR_MAX_END);
}

template <typename curve>
void check_trim(curve& xcv, int sx, int sy, int tx, int ty)
{
  Polycurve_conic_traits_2 traits;

  // Conic_point_2 source(Algebraic(-16), Algebraic(-4));
  // Conic_point_2 target(Algebraic(4), Algebraic(16));
  Conic_point_2 source(sx, sy);
  Conic_point_2 target(tx, ty);

  Polycurve_conic_traits_2::Trim_2  trim_polycurve = traits.trim_2_object();
  Pc_x_monotone_curve_2 trimmed_curve = trim_polycurve(xcv, source, target);

  std::cout << "polycurvecurve: " << xcv << std::endl<<std::endl;
  std::cout << "Trimmed curve: " << trimmed_curve << std::endl;

}

int main(int argc, char* argv[])
{
  Polycurve_conic_traits_2 traits;
    //polycurve constructors
  auto construct_x_mono_polycurve = traits.construct_x_monotone_curve_2_object();
  auto construct_polycurve = traits.construct_curve_2_object();

   //create a curve

  Conic_curve_2 c3(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(0), Algebraic(0)),
                   Conic_point_2(Algebraic(3), Algebraic(9)));
  Conic_curve_2 c4(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(3), Algebraic(9)),
                   Conic_point_2(Algebraic(5), Algebraic(25)));
  Conic_curve_2 c5(0,1,0,1,0,0, CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-25), Algebraic(-5)),
                   Conic_point_2(Algebraic(0), Algebraic(0)));

  Conic_curve_2 c6(1,1,0,6,-26,162,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-7), Algebraic(13)),
                   Conic_point_2(Algebraic(-3), Algebraic(9)));
  Conic_curve_2 c7(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-3), Algebraic(9)),
                   Conic_point_2(Algebraic(0), Algebraic(0)));
  Conic_curve_2 c8(0,1,0,-1,0,0, CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(0), Algebraic(0)),
                   Conic_point_2(Algebraic(4), Algebraic(-2)));

  Conic_curve_2 c9(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                   Conic_point_2(Algebraic(-5), Algebraic(25)),
                   Conic_point_2(Algebraic(5), Algebraic(25)));
  Conic_curve_2 c10(58, 72, -48, 0, 0, -360);

  //This vector is used to store curves that will be used to create polycurve
  std::vector<Conic_curve_2> conic_curves;
  conic_curves.push_back(c9);

  //construct poly-curve
  Polycurve_conic_traits_2::Curve_2 conic_polycurve =
    construct_polycurve(conic_curves.begin(), conic_curves.end());

  Conic_curve_2 c11(0,1,0,-1,0,0,CGAL::COUNTERCLOCKWISE,
                     Conic_point_2(Algebraic(25), Algebraic(-5)),
                     Conic_point_2(Algebraic(0), Algebraic(0)));
  Conic_curve_2 c12(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
                     Conic_point_2(Algebraic(0), Algebraic(0)),
                     Conic_point_2(Algebraic(5), Algebraic(25)));
  conic_curves.clear();
  conic_curves.push_back(c11);
  conic_curves.push_back(c12);

  //construct poly-curve
  Polycurve_conic_traits_2::Curve_2 conic_polycurve_2 =
    construct_polycurve(conic_curves.begin(), conic_curves.end());

  /* VERY IMPORTANT
   * For efficiency reasons, we recommend users not to construct
   * x-monotone conic arc directly, but rather use the Make_x_monotone_2
   * functor supplied by the conic-arc traits class to convert conic curves
   * to x-monotone curves.
   */
  Conic_x_monotone_curve_2 xc3(c3);
  Conic_x_monotone_curve_2 xc4(c4);
  Conic_x_monotone_curve_2 xc5(c5);
  Conic_x_monotone_curve_2 xc6(c6);
  Conic_x_monotone_curve_2 xc7(c7);
  Conic_x_monotone_curve_2 xc8(c8);

  //This vector is used to store curves that will be used to create
  //X-monotone-polycurve
  std::vector<Conic_x_monotone_curve_2> xmono_conic_curves_2;
  xmono_conic_curves_2.push_back(xc5);
  xmono_conic_curves_2.push_back(xc3);
  xmono_conic_curves_2.push_back(xc4);

  //construct x-monotone poly-curve
  Pc_x_monotone_curve_2 conic_x_mono_polycurve_1 =
    construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
                               xmono_conic_curves_2.end());

  xmono_conic_curves_2.clear();
  xmono_conic_curves_2.push_back(xc6);
  xmono_conic_curves_2.push_back(xc7);
  xmono_conic_curves_2.push_back(xc8);
  //construct x-monotone poly-curve
  Pc_x_monotone_curve_2 conic_x_mono_polycurve_2 =
    construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
                               xmono_conic_curves_2.end());

  xmono_conic_curves_2.clear();
  xmono_conic_curves_2.push_back(xc5);

  Pc_x_monotone_curve_2 x_polycurve_push =
    construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
                               xmono_conic_curves_2.end());
  Polycurve_conic_traits_2::X_monotone_subcurve_2 xcurve_push =
    Polycurve_conic_traits_2::X_monotone_subcurve_2(c5);
  //traits.construct_x_monotone_curve_2_object()(c5);

  xmono_conic_curves_2.clear();
  xmono_conic_curves_2.push_back(xc3);
  xmono_conic_curves_2.push_back(xc4);
  Pc_x_monotone_curve_2 base_curve =
    construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
                               xmono_conic_curves_2.end());

  //curves for push_back
  Conic_curve_2 c13(1,1,0,-50,12,660,CGAL::COUNTERCLOCKWISE,
                    Conic_point_2(Algebraic(25), Algebraic(-7)),
                    Conic_point_2(Algebraic(25), Algebraic(-5)));
  Conic_curve_2 c14(0,1,0,-1,0,0,CGAL::COUNTERCLOCKWISE,
                    Conic_point_2(Algebraic(25), Algebraic(-5)),
                    Conic_point_2(Algebraic(0), Algebraic(0)));
  Conic_curve_2 c15(-1,0,0,0,1,0,CGAL::COUNTERCLOCKWISE,
                    Conic_point_2(Algebraic(0), Algebraic(0)),
                    Conic_point_2(Algebraic(5), Algebraic(25)));
  conic_curves.clear();
  conic_curves.push_back(c13);
  conic_curves.push_back(c14);
  Polycurve_conic_traits_2::Curve_2 base_curve_push_back =
    construct_polycurve(conic_curves.begin(), conic_curves.end());

  conic_curves.push_back(c15);
  Polycurve_conic_traits_2::Curve_2 Expected_push_back_result =
    construct_polycurve(conic_curves.begin(), conic_curves.end());

  // //checking the orientattion consistency
  // Conic_curve_2 c21(0,1,0,1,0,0,CGAL::CLOCKWISE,
  //                  Conic_point_2(Algebraic(9), Algebraic(-3)),
  //                  Conic_point_2(Algebraic(0), Algebraic(0)));
  // Conic_curve_2 c20(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
  //                   Conic_point_2(Algebraic(0), Algebraic(0)),
  //                   Conic_point_2(Algebraic(3), Algebraic(9)));
  //  Conic_x_monotone_curve_2 xc20(c20);
  //  Conic_x_monotone_curve_2 xc21(c21);
  //  xmono_conic_curves_2.clear();
  //  xmono_conic_curves_2.push_back(xc20);
  // xmono_conic_curves_2.push_back(xc21);
  // Pc_x_monotone_curve_2 eric_polycurve =
  //   construct_x_mono_polycurve(xmono_conic_curves_2.begin(),
  //                              xmono_conic_curves_2.end());
  // std::cout << "the polycurve is: " << eric_polycurve << std::endl;

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

  //check_compare_x_2(xc3, xc5);

  // check_equal();
  // std::cout<< std::endl;

   //check_intersect(conic_x_mono_polycurve_1, conic_x_mono_polycurve_2);
   //std::cout<< std::endl;

  // check_compare_end_points_xy_2();
  // std::cout<< std::endl;

  //check_split(conic_x_mono_polycurve_1, conic_x_mono_polycurve_2);
  // std::cout<< std::endl;

  //check_make_x_monotne_curve<Point_2, Curve_2>(conic_polycurve_2);
   //std::cout<< std::endl;

  // check_is_vertical();
  // std::cout<< std::endl;

  //check_compare_y_at_x_2();
  //std::cout<< std::endl;

  //adds the segment to the right.
  //check_push_back(base_curve_push_back, c15);
  //std::cout<< std::endl;

  //adds the segment to the left.
  //check_push_front(base_curve, xcurve_push);
  //std::cout<< std::endl;

  // check_are_mergable();
  // std::cout<< std::endl;

  // check_merge_2();
  // std::cout<< std::endl;

  // check_construct_opposite();
  // std::cout<< std::endl;

  // check_compare_y_at_x_right();
  // std::cout<< std::endl;

  // check_compare_y_at_x_left();
  // std::cout<< std::endl;
  //check_compare_points(conic_x_mono_polycurve_1);

  //number of segments
  //std::cout << "Number of segments: "
  //          << traits.number_of_points_2_object()(base_curve_push_back)
  //          << std::endl;

  check_trim(conic_x_mono_polycurve_1, atoi(argv[1]), atoi(argv[2]),
             atoi(argv[3]), atoi(argv[4]));
  std::cout << std::endl;

  //std::cout << (atoi(argv[1]) + atoi(argv[2])) << std::endl;
  // Conic_traits_2 con_traits;
  // Conic_curve_2 cc3(1,0,0,0,-1,0,CGAL::COUNTERCLOCKWISE,
  //                   Conic_point_2(Algebraic(0), Algebraic(0)),
  //                   Conic_point_2(Algebraic(3), Algebraic(9)));
  // Conic_x_monotone_curve_2 xcc3(cc3);
  // Conic_point_2       ps2(0, 0);
  // Conic_point_2       pt2(3, 9);
  // std::cout << "conic curve is : " << xcc3 << std::endl;
  // Conic_x_monotone_curve_2 trimmed_curve =
  //   con_traits.trim_2_object()(xc3, ps2, pt2);
  // std::cout << "trimmed conic curve is : " << trimmed_curve << std::endl;

  return 0;
}

#endif


/*
Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.

NVIDIA CORPORATION and its licensors retain all intellectual property
and proprietary rights in and to this software, related documentation
and any modifications thereto. Any use, reproduction, disclosure or
distribution of this software and related documentation without an express
license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
#pragma once

#include <Eigen/Eigen>

namespace isaac {

template <typename K, int N, int M>
using Matrix = Eigen::Matrix<K, N, M>;
template <typename K, int N>
using Vector = Eigen::Matrix<K, N, 1>;

template <typename K, int N>
using RowVector = Eigen::Matrix<K, 1, N>;
template <typename K, int N>
using ColVector = Eigen::Matrix<K, N, 1>;

template <typename K>
using VectorX = Vector<K, Eigen::Dynamic>;
using VectorXd = VectorX<double>;
using VectorXf = VectorX<float>;
using VectorXi = VectorX<int>;
using VectorXub = VectorX<uint8_t>;

using VectorXcf = Eigen::VectorXcf;

template <typename K>
using RowVectorX = RowVector<K, Eigen::Dynamic>;
using RowVectorXf = RowVectorX<float>;

template <typename K>
using ColVectorX = ColVector<K, Eigen::Dynamic>;
using ColVectorXf = ColVectorX<float>;

template <typename K>
using MatrixX = Matrix<K, Eigen::Dynamic, Eigen::Dynamic>;
using MatrixXd = MatrixX<double>;
using MatrixXf = MatrixX<float>;
using MatrixXi = MatrixX<int>;

using MatrixXcf = Eigen::MatrixXcf;

#define DEFINE_MATRIX_TYPES(N)            \
  template <typename K>                   \
  using Matrix##N = Matrix<K, N, N>;      \
  using Matrix##N##d = Matrix##N<double>; \
  using Matrix##N##f = Matrix##N<float>;  \
  using Matrix##N##i = Matrix##N<int>;

DEFINE_MATRIX_TYPES(2)
DEFINE_MATRIX_TYPES(3)
DEFINE_MATRIX_TYPES(4)
DEFINE_MATRIX_TYPES(5)
DEFINE_MATRIX_TYPES(6)
DEFINE_MATRIX_TYPES(7)
DEFINE_MATRIX_TYPES(8)

#undef DEFINE_MATRIX_TYPES

// Matrix types with fixed number of rows but dynamic number of columns.
// Useful to store and efficiently manipulate sets of geometric entities, e.g. points and planes.
// 2xN: 2D points in Euclidean coordinates
// 3xN: 2D points in homogeneous coordinates or 3D points in Euclidean coordinates
// 4xN: planes or 3D points in homogeneous coordinates
#define DEFINE_FIXED_ROWS_MATRIX_TYPES(N) \
  template <typename K> using Matrix##N##X = Matrix<K, N, Eigen::Dynamic>; \
  using Matrix##N##Xd = Matrix##N##X<double>; \
  using Matrix##N##Xf = Matrix##N##X<float>; \
  using Matrix##N##Xi = Matrix##N##X<int>; \

DEFINE_FIXED_ROWS_MATRIX_TYPES(2)
DEFINE_FIXED_ROWS_MATRIX_TYPES(3)
DEFINE_FIXED_ROWS_MATRIX_TYPES(4)
DEFINE_FIXED_ROWS_MATRIX_TYPES(5)
DEFINE_FIXED_ROWS_MATRIX_TYPES(6)
DEFINE_FIXED_ROWS_MATRIX_TYPES(7)
DEFINE_FIXED_ROWS_MATRIX_TYPES(8)

#undef DEFINE_FIXED_ROWS_MATRIX_TYPES

template <typename K>
using Matrix34 = Matrix<K, 3, 4>;
using Matrix34d = Matrix34<double>;
using Matrix34f = Matrix34<float>;
using Matrix34i = Matrix34<int>;

template <typename K>
using Matrix43 = Matrix<K, 4, 3>;
using Matrix43d = Matrix43<double>;
using Matrix43f = Matrix43<float>;
using Matrix43i = Matrix43<int>;

#define DEFINE_VECTOR_TYPES(N)            \
  template <typename K>                   \
  using Vector##N = Vector<K, N>;         \
  using Vector##N##d = Vector##N<double>; \
  using Vector##N##f = Vector##N<float>;  \
  using Vector##N##i = Vector##N<int>;    \
  using Vector##N##ub = Vector##N<uint8_t>;

DEFINE_VECTOR_TYPES(2)
DEFINE_VECTOR_TYPES(3)
DEFINE_VECTOR_TYPES(4)
DEFINE_VECTOR_TYPES(5)
DEFINE_VECTOR_TYPES(6)
DEFINE_VECTOR_TYPES(7)
DEFINE_VECTOR_TYPES(8)

#undef DEFINE_VECTOR_TYPES

template <typename K>
using Quaternion = Eigen::Quaternion<K>;
using Quaterniond = Quaternion<double>;
using Quaternionf = Quaternion<float>;

// Helper function to compute the sum of two quaternions
template <typename K>
Quaternion<K> operator+(const Quaternion<K>& lhs, const Quaternion<K>& rhs) {
  return Quaternion<K>(lhs.coeffs() + rhs.coeffs());
}
// Helper function to compute the difference of two quaternions
template <typename K>
Quaternion<K> operator-(const Quaternion<K>& lhs, const Quaternion<K>& rhs) {
  return Quaternion<K>(lhs.coeffs() - rhs.coeffs());
}
// Unary - operator for a quaternion
template <typename K>
Quaternion<K> operator-(const Quaternion<K>& q) {
  return Quaternion<K>(-q.coeffs());
}

template <typename K>
using EigenImage = Eigen::Array<K, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
template <typename K>
using EigenImageMap = Eigen::Map<EigenImage<K>>;
template <typename K>
using EigenImageConstMap = Eigen::Map<const EigenImage<K>>;

// A base helper for constructing Views of data as eigen matrix maps.
template <typename K, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic, int MatrixOptions = 0,
          int MapOptions = 0, int OuterStride = 0, int InnerStride = 0>
using EigenMatrixView = Eigen::Map<Eigen::Matrix<K, Rows, Cols, MatrixOptions>, MapOptions,
                                   Eigen::Stride<OuterStride, InnerStride>>;

// A base helper for constructing const Views of data as eigen matrix maps.
template <typename K, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic, int MatrixOptions = 0,
          int MapOptions = 0, int OuterStride = 0, int InnerStride = 0>
using EigenMatrixConstView = Eigen::Map<const Eigen::Matrix<K, Rows, Cols, MatrixOptions>,
                                        MapOptions, Eigen::Stride<OuterStride, InnerStride>>;

// A base helper for constructing Views of data as eigen vector maps.
template <typename K, int Rows = Eigen::Dynamic, int MapOptions = 0,
          int OuterStride = 0, int InnerStride = 0>
using EigenVectorView = Eigen::Map<Eigen::Matrix<K, Rows, 1>, MapOptions,
                                   Eigen::Stride<OuterStride, InnerStride>>;

// A base helper for constructing const Views of data as eigen vector maps.
template <typename K, int Rows = Eigen::Dynamic,  int MapOptions = 0,
          int OuterStride = 0, int InnerStride = 0>
using EigenVectorConstView = Eigen::Map<const Eigen::Matrix<K, Rows, 1>, MapOptions,
                                        Eigen::Stride<OuterStride, InnerStride>>;

// A base helper for constructing Views of data as eigen row vector maps.
template <typename K, int Cols = Eigen::Dynamic, int MapOptions = 0,
          int OuterStride = 0, int InnerStride = 0>
using EigenRowVectorView = Eigen::Map<Eigen::Matrix<K, 1, Cols, Eigen::RowMajor>, MapOptions,
                                      Eigen::Stride<OuterStride, InnerStride>>;

// A base helper for constructing const Views of data as eigen row vector maps.
template <typename K, int Cols = Eigen::Dynamic, int MapOptions = 0,
          int OuterStride = 0, int InnerStride = 0>
using EigenRowVectorConstView = Eigen::Map<const Eigen::Matrix<K, 1, Cols, Eigen::RowMajor>,
                                           MapOptions, Eigen::Stride<OuterStride, InnerStride>>;

// Creates a Vector from an initializer list
template <typename K, size_t N>
Vector<K, N> MakeVector(const K (&elements)[N]) {
  Vector<K, N> result;
  for (size_t i = 0; i < N; i++) {
    result[i] = elements[i];
  }
  return result;
}

}  // namespace isaac


/*
 * This benchmark times the performance of B += sqr(A), where A and B
 * are complex<double> arrays.
 *
 * Note: need to use -mv8 for SPARC v8.
 */

#include <blitz/array.h>
#include <blitz/timer.h>

BZ_USING_NAMESPACE(blitz)

#include <blitz/vector.h>

typedef Array<complex<double>,1> CArray;

void setup(Array<complex<double>,1>& A, Array<complex<double>,1>& B)
{
    int n = A.extent(firstDim);

    for (int i=0; i < n; ++i)
    {
        double x=-10.+20./(n-1.0)*i;
        A(i)=sin(x);
        B(i)=sin(x);
    }
}

void version1(CArray& A, CArray& B, int nIters)
{
    Timer timer;

    // Array notation
    setup(A, B);
    timer.start();
    for (int i=0; i < nIters; ++i)
    {
        B += A*A;
    }
    timer.stop();
    cout << "Time using array notation b += a*a: " << timer.elapsedSeconds()
         << endl;
}

void version2(CArray& A, CArray& B, int nIters)
{
    // Array notation, using sqr(a)
    Timer timer;

    setup(A,B);
    timer.start();
    for (int i=0; i < nIters; ++i)
    {
        B += sqr(A);
    }
    timer.stop();
    cout << "Time using array notation b += sqr(a): " << timer.elapsedSeconds()
         << endl;
}

void version2c(CArray& A, CArray& B)
{
    B += sqr(A);
}

void version2b(CArray& A, CArray& B, int nIters)
{
    // Array notation, using sqr(a)
    Timer timer;

    setup(A,B);
    timer.start();
    for (int i=0; i < nIters; ++i)
    {
        version2c(A,B);
    }
    timer.stop();
    cout << "Time using array notation b += sqr(a): " << timer.elapsedSeconds()
         << endl;
}

void version3(CArray& A, CArray& B, int nIters)
{
    Timer timer;

    int N = A.extent(firstDim);

    // Low-level implementation
    setup(A,B);
    timer.start();
    for (int i=0; i < nIters; ++i)
    {
        for (int j=0; j < N; ++j)
            B(j) += A(j) * A(j);
    }
    timer.stop();
    cout << "Time using low-level version: " << timer.elapsedSeconds()
         << endl;
}

void version4(CArray& A, CArray& B, int nIters)
{
    Timer timer;

    struct cmplx {
        double re, im;
    };
    cmplx* a = (cmplx*)A.data();
    cmplx* b = (cmplx*)B.data();
    setup(A,B);
    int N = A.extent(firstDim);

    timer.start();
    for (int i=0; i < nIters; ++i)
    {
        for (int j=0; j < N; ++j)
        {
            double ar = a[j].re;
            double ai = a[j].im;
            b[j].re += ar*ar - ai*ai;
            b[j].im += 2 * ar * ai;
        }
    }
    timer.stop();
    cout << "Time using really low-level version: " << timer.elapsedSeconds()
         << endl;
}

int run(int N, int nIters)
{
    Array<complex<double>,1> A(N), B(N);

    version1(A,B,nIters);
    version2(A,B,nIters);
    version2b(A,B,nIters);
    version3(A,B,nIters);
    version4(A,B,nIters);

    return 0;
}

int main()
{
    cout << "In-cache:" << endl;
    run(256,39063);

    cout << endl << "Out-of-cache:" << endl;
    run(1000000,10);
}