
#include <stdio.h>
#include <vector>
#include <sirocco.hpp>
#include <list.hpp>


using namespace std;

// unsigned int *_Polynomial::combinatorial;



/******************************************
// f(z1,z2) = z1^3+2*z1*z2-3*z2-z1;
// COEFS = {{1,0,0,0}, {0,2,0}, {-1,3}, {0}}
// z1 = x + iy
// z2 = t(1+0.01i)
******************************************/

#ifndef __clang__
#pragma STDC FENV_ACCESS ON
#endif

// PERFORM A MINIMUM OF 5 ITERATIONS OF NEWTON METHOD TO CORRECT THE APPROXIMATION OF THE ROOT (SUPPOSED TO BE GOOD ENOUGH)
template <>
void correctRoot<Complex> (const Polynomial<Complex> f, const Complex &x0, Complex &y0) {
	Complex increment;
	int count = 0;
	double last_increment, new_increment;			// sizes of corrections
	new_increment = 9999.0;
	do {
		last_increment = new_increment;
		increment = f(x0,y0) / (f.diffY(x0,y0));
		new_increment = abs (increment);
		y0 = y0 - increment;
	  	++count;
	} while ((fabs(last_increment - new_increment)  > 1.0e-14 || count < 6 ) && count < 50);
	return;
}
template <>
void correctRoot<MPComplex> (const Polynomial<MPComplex> f, const MPComplex &x0, MPComplex &y0) {
	MPComplex increment;
	int count = 0;
	mpfr_t last_increment, new_increment, tol;			// sizes of corrections and tolerance
	mpfr_inits (last_increment, new_increment, tol, NULL);

	mpfr_set_d (tol, mpfr_get_default_prec(), MPFR_RNDN);
	mpfr_sub_si (tol, tol, 5, MPFR_RNDN);
	mpfr_neg (tol, tol, MPFR_RNDN);
	mpfr_exp2 (tol, tol, MPFR_RNDN);
	// std::cout << "tol = ";
	// mpfr_out_str (stdout, 10, 20, tol, MPFR_RNDN);
	// std::cout << std::endl;

	mpfr_set_d (new_increment, 9999.0, MPFR_RNDN);
	do {
		mpfr_set (last_increment, new_increment, MPFR_RNDN);
		increment = f(x0,y0) / (f.diffY(x0,y0));
		abs (new_increment, increment);
		y0 = y0 - increment;
	  	++count;

	  	// compute difference of increments and store in "last_increment"
	  	mpfr_sub (last_increment, last_increment, new_increment, MPFR_RNDN);
	  	mpfr_abs (last_increment, last_increment, MPFR_RNDN);
	} while ((mpfr_cmp (last_increment, tol) > 0 || count < 6 ) && count < 50);

	mpfr_clears (last_increment, new_increment, tol, NULL);
	return;
}

double * homotopyPath_comps (int degree, double *_coef, double _y0R, double _y0I, int nothercomps, int *degreescomps, double *_coefscomps) {

    int tdegree = degree;


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

        tdegree = max(tdegree, degreescomps[i]);
    }

    _Polynomial::startCombinatorialNumbers (tdegree);

    // 	/************************************************/
    // 	/* VARIABLE DECLARATION AND DATA INITIALIZATION */
    // 	/************************************************/
    Complex aux;								// Complex aux variable.

    double stepsize = 0.0001;					// step size
    double eps;									// radius of the box


    int nCoef = ((degree+1)*(degree+2)) / 2;	// number of monomials
    Complex coef[nCoef];						// coefficient list for floating point representation of
    // the polynomial.
    IComplex Icoef[nCoef];	 					// coefficient list for Interval representation of
    // the polynomial.

    std::vector<Cell<double> > outputList;



    // INITIALIZE COEFICIENT DATA FROM ARGUMENT INTO APPROPIATE DATA STRUCTURES
    for (int i=0; i<nCoef; ++i) {
        Icoef[i] = IComplex (_coef[4*i] ,_coef[4*i+1] ,_coef[4*i+2] ,_coef[4*i+3]);
        coef[i] = Complex ((_coef[4*i]  + _coef[4*i+1])/2.0 ,
                           (_coef[4*i+2] + _coef[4*i+3])/2.0);
    }


    Polynomial<Complex> f (degree, coef);		// complex polynomials (with fp arithmetic)
    // to be used in Newton method correction
    Polynomial<IComplex> If (degree, Icoef);	// complex polynomials (with Interval arithmetic)
    Polynomial<IComplex> Ig (degree);			// to be used in validated homotopy
    Polynomial<IComplex> Ih (degree);


    Polynomial<IComplex> othercomponents[nothercomps];
    Polynomial<IComplex> Igs[nothercomps];
    Polynomial<IComplex> Ihs[nothercomps];

    int acumcoefscomps = 0;

    for (int i=0; i< nothercomps; ++i) {
        int cdegree = degreescomps[i];
        int nCoefcomp = ((cdegree+1)*(cdegree+2)) / 2;
        IComplex Icoefcomp[nCoefcomp];
        for (int j=0; j<nCoefcomp; ++j) {
            Icoefcomp[j] = IComplex (_coefscomps[4*acumcoefscomps] ,_coefscomps[4*acumcoefscomps+1] ,_coefscomps[4*acumcoefscomps+2] ,_coefscomps[acumcoefscomps*4+3]);
            acumcoefscomps++;
        }
        Polynomial<IComplex> Ifcomp (cdegree, Icoefcomp);
        othercomponents[i] = Ifcomp;
        Polynomial<IComplex > Igcomp (cdegree);
        Polynomial<IComplex > Ihcomp (cdegree);
        Igs[i] = Igcomp;
        Ihs[i] = Ihcomp;
    }




    // INITIALIZE FLOATING POINT COMPLEX APPROXIMATION OF THE ROOT
    Complex x0 (0.0);							// x0 of the root (x0.imag = 0)
    Complex y0 (_y0R, _y0I);					// approximated y0 of the root
    IComplex Ix0;								// Interval representation for x0
    IComplex Iy0;								// Interval representation for y0
    IComplex IY0;								// Interval enclosure for y0 for Newton Method
    IComplex Iy1, IY1;							// Interval representation of zeros for rotated polynomial
    IComplex Ix1;

    Complex a;									// y'(x0) as Complex
    IComplex Ia;								// y'(x0) as IComplex


    #ifdef PROFILING
    int nsteps = 0;								// number of steps performed
    int nrejectedEps = 0;						// number of rejected validations of boxes
    int nrejectedSteps = 0;						// number of rejected validations of tubes
    #endif



    outputList.push_back (Cell<double> (0.0, y0.real (), y0.imag ()));

    /*******************/
    /* CORRECT ROOT	   */
    /*******************/


    correctRoot<Complex> (f, x0, y0);


    /*******************/
    /** MAIN LOOP	   */
    /*******************/
    while (x0.real () < 1.0) {

        #ifdef PROFILING
        nsteps++; // used for profiling only
        #endif

        /***********************************************************/
        /*** BOX SIZE ESTIMATION. F RESTRICTED TO X0 IS INJECTIVE. */
        /***********************************************************/
        aux = f.diffYY (x0, y0);
        if (abs (aux) < 1.0e-10) {
            eps = 0.5;
        } else {
            eps = (abs (f.diffY(x0,y0) / aux)) / 8.0;
        }

        eps /= 2.0;
        // cout << "estima eps = " << eps << endl;

        /**************************/
        /*** VALIDATE y0 AS POINT.*/
        /**************************/
        Ix0 = x0;
        Iy0 = y0;
        IY0 = y0 + IComplex (-eps, eps, -eps, eps);


        while (!validate<IComplex> (If, Ix0, Iy0, IY0, nothercomps, othercomponents)) {
            #ifdef PROFILING
            nrejectedEps++; // used for profiling
            #endif
            if (eps < 1.0e-13) {
                printf ("Singularity detected. Abort Sirocco\n");
                /*******************/
                /* CLEAN VARIABLES */
                /*******************/

                _Polynomial::releaseCombinatorialNumbers ();
                return NULL;
            }
            eps *= 0.5;
            IY0 = Iy0;
            IY0 = y0 + IComplex (-eps, eps, -eps, eps);
        }
        // cout << "acepta eps = " << eps << endl;

        // COMPUTE a = -fx(x0,y0) / fy(x0.y0)
        // both fp and Interval
        a = getA<Complex> (f, x0, y0);
        Ia = a;


        /************************/
        /* ESTIMATE STEP SIZE ***/
        /************************/
        // we have: aux = fyy
        // store all in aux2

        aux = a*a*aux + 2.0*a*f.diffXY(x0,y0) + f.diffXX (x0, y0);

        // DETECT INFLEXION POINT
        if (abs(aux) < 1.0e-10)
            stepsize=1.0;
        else
            stepsize = sqrt(abs((eps * f.diffY (x0, y0) )/ aux));
        if (stepsize + x0.real () > 1.0) stepsize = 1.000001 - x0.real ();


        /*******************/
        /** VALIDATE STEP  */
        /*******************/
        Ix1 = Interval(0,stepsize);


        // PERFORM TRANSLATION AND ROTATION OF THE POLYNOMIAL
        Ig = If.IlinearVarChange2 (Ix0);
        Ih = Ig.IlinearVarChange (Ia);

        for (int jj = 0; jj<nothercomps; ++jj) {

            Igs[jj] = othercomponents[jj].IlinearVarChange2(Ix0);
            Ihs[jj] = Igs[jj].IlinearVarChange (Ia);
        }


        while (!validate<IComplex> (Ih, Ix1, Iy0, IY0, nothercomps, Ihs)) {
            #ifdef PROFILING
            nrejectedSteps++; // used for profiling
            #endif
            stepsize *= 0.5;
            eps*=0.95;
            IY0 = y0 + IComplex (-eps, eps, -eps, eps);
            Ix1 = Interval (0, stepsize);

            if (stepsize < 1.0e-13) {
                /*******************/
                /* CLEAN VARIABLES */
                /*******************/
                _Polynomial::releaseCombinatorialNumbers ();
                return NULL;
            }

        }
        #ifdef DEVELOPER
        std::cout << "x0 = " << x0.real () << " step = " << stepsize << std::endl;
        #endif


        // 		/**************************/
        // 		/** VALIDATE FINAL STEP   */
        // 		/**************************/
        if (x0.real () + stepsize > 1.0) {
            stepsize = 1.0 - x0.real ();
            Ix1.r.b = stepsize;

            #ifdef DEVELOPER
            std::cout << "final step = " << stepsize << std::endl;
            #endif

            while (!validate<IComplex> (Ih, Ix1, Iy0, IY0, nothercomps, Ihs));
        }


        // 		/********************/
        // 		/* UPDATE VARIABLES */
        // 		/********************/
        x0 = Complex (Ix0.r.a + stepsize, 0.0);
        y0 = Complex (y0.real () + Ia.r.a * stepsize,
                      y0.imag () + Ia.i.a * stepsize);




        // 		/*******************/
        // 		/* CORRECT ROOT	   */
        // 		/*******************/
        correctRoot<Complex> (f, x0, y0);

        /**********************************************/
        /* CHECK WE HAVE NOT JUMPED TO ANOTHER ROOT   */
        /**********************************************/
        if (!(IY0+Ia*stepsize).contains (y0)) {
            printf ("error! Jumped to other thread!\n");
            _Polynomial::releaseCombinatorialNumbers ();
            return NULL;
        }


        outputList.push_back (Cell<double> (x0.real (), y0.real (), y0.imag ()));

    }





    // 	/***********************************************/
    /* PREPARE OUTPUT AS A BINARY ARRAY OF DOUBLES */
    /***********************************************/

    double *rop = new double [((3 * outputList.size () + 1))];
    rop[0] = outputList.size ();
    for (int i=0; i<outputList.size (); i++) {
        rop[3*i+1] = outputList[i].x[0];
        rop[3*i+2] = outputList[i].x[1];
        rop[3*i+3] = outputList[i].x[2];
    }
    _Polynomial::releaseCombinatorialNumbers ();
    return rop;

}


double * homotopyPath (int degree, double *_coef, double _y0R, double _y0I) {

	_Polynomial::startCombinatorialNumbers (degree);

// 	/************************************************/
// 	/* VARIABLE DECLARATION AND DATA INITIALIZATION */
// 	/************************************************/
	Complex aux;								// Complex aux variable.

	double stepsize = 0.0001;					// step size
	double eps;									// radius of the box


	int nCoef = ((degree+1)*(degree+2)) / 2;	// number of monomials
	Complex coef[nCoef];						// coefficient list for floating point representation of
												// the polynomial.
	IComplex Icoef[nCoef];	 					// coefficient list for Interval representation of
												// the polynomial.

	std::vector<Cell<double> > outputList;



 	// INITIALIZE COEFICIENT DATA FROM ARGUMENT INTO APPROPIATE DATA STRUCTURES
	for (int i=0; i<nCoef; ++i) {
		Icoef[i] = IComplex (_coef[4*i] ,_coef[4*i+1] ,_coef[4*i+2] ,_coef[4*i+3]);
		coef[i] = Complex ((_coef[4*i]  + _coef[4*i+1])/2.0 ,
						(_coef[4*i+2] + _coef[4*i+3])/2.0);
	}


	Polynomial<Complex> f (degree, coef);		// complex polynomials (with fp arithmetic)
												// to be used in Newton method correction
	Polynomial<IComplex> If (degree, Icoef);	// complex polynomials (with Interval arithmetic)
	Polynomial<IComplex> Ig (degree);			// to be used in validated homotopy
	Polynomial<IComplex> Ih (degree);



 	// INITIALIZE FLOATING POINT COMPLEX APPROXIMATION OF THE ROOT
	Complex x0 (0.0);							// x0 of the root (x0.imag = 0)
	Complex y0 (_y0R, _y0I);					// approximated y0 of the root
	IComplex Ix0;								// Interval representation for x0
	IComplex Iy0;								// Interval representation for y0
	IComplex IY0;								// Interval enclosure for y0 for Newton Method
	IComplex Iy1, IY1;							// Interval representation of zeros for rotated polynomial
	IComplex Ix1;

	Complex a;									// y'(x0) as Complex
	IComplex Ia;								// y'(x0) as IComplex


#ifdef PROFILING
	int nsteps = 0;								// number of steps performed
	int nrejectedEps = 0;						// number of rejected validations of boxes
	int nrejectedSteps = 0;						// number of rejected validations of tubes
#endif



	outputList.push_back (Cell<double> (0.0, y0.real (), y0.imag ()));

	/*******************/
	/* CORRECT ROOT	   */
	/*******************/


	correctRoot<Complex> (f, x0, y0);


	/*******************/
	/** MAIN LOOP	   */
	/*******************/
	while (x0.real () < 1.0) {

#ifdef PROFILING
	nsteps++; // used for profiling only
#endif

		/***********************************************************/
		/*** BOX SIZE ESTIMATION. F RESTRICTED TO X0 IS INJECTIVE. */
		/***********************************************************/
		aux = f.diffYY (x0, y0);
		if (abs (aux) < 1.0e-10) {
			eps = 0.5;
		} else {
			eps = (abs (f.diffY(x0,y0) / aux)) / 8.0;
		}

		eps /= 2.0;
		// cout << "estima eps = " << eps << endl;

		/**************************/
		/*** VALIDATE y0 AS POINT.*/
		/**************************/
		Ix0 = x0;
		Iy0 = y0;
		IY0 = y0 + IComplex (-eps, eps, -eps, eps);


		while (!validate<IComplex> (If, Ix0, Iy0, IY0)) {
#ifdef PROFILING
	nrejectedEps++; // used for profiling
#endif
			if (eps < 1.0e-13) {
				printf ("Singularity detected. Abort Sirocco\n");
				/*******************/
				/* CLEAN VARIABLES */
				/*******************/

				_Polynomial::releaseCombinatorialNumbers ();
				return NULL;
            }
			eps *= 0.5;
			IY0 = Iy0;
			IY0 = y0 + IComplex (-eps, eps, -eps, eps);
		}
		// cout << "acepta eps = " << eps << endl;

		// COMPUTE a = -fx(x0,y0) / fy(x0.y0)
		// both fp and Interval
		a = getA<Complex> (f, x0, y0);
		Ia = a;


		/************************/
		/* ESTIMATE STEP SIZE ***/
		/************************/
		// we have: aux = fyy
		// store all in aux2

		aux = a*a*aux + 2.0*a*f.diffXY(x0,y0) + f.diffXX (x0, y0);

		// DETECT INFLEXION POINT
		if (abs(aux) < 1.0e-10)
			stepsize=1.0;
		else
			stepsize = sqrt(abs((eps * f.diffY (x0, y0) )/ aux));
		if (stepsize + x0.real () > 1.0) stepsize = 1.000001 - x0.real ();


		/*******************/
		/** VALIDATE STEP  */
		/*******************/
		Ix1 = Interval(0,stepsize);


		// PERFORM TRANSLATION AND ROTATION OF THE POLYNOMIAL
		Ig = If.IlinearVarChange2 (Ix0);
		Ih = Ig.IlinearVarChange (Ia);


		while (!validate<IComplex> (Ih, Ix1, Iy0, IY0)) {
#ifdef PROFILING
	nrejectedSteps++; // used for profiling
#endif
			stepsize *= 0.5;
			eps*=0.95;
			IY0 = y0 + IComplex (-eps, eps, -eps, eps);
			Ix1 = Interval (0, stepsize);

			if (stepsize < 1.0e-13) {
				/*******************/
				/* CLEAN VARIABLES */
				/*******************/
				_Polynomial::releaseCombinatorialNumbers ();
				return NULL;
            }

		}
#ifdef DEVELOPER
	std::cout << "x0 = " << x0.real () << " step = " << stepsize << std::endl;
#endif


// 		/**************************/
// 		/** VALIDATE FINAL STEP   */
// 		/**************************/
		if (x0.real () + stepsize > 1.0) {
			stepsize = 1.0 - x0.real ();
			Ix1.r.b = stepsize;

#ifdef DEVELOPER
	std::cout << "final step = " << stepsize << std::endl;
#endif

			while (!validate<IComplex> (Ih, Ix1, Iy0, IY0));
		}


// 		/********************/
// 		/* UPDATE VARIABLES */
// 		/********************/
		x0 = Complex (Ix0.r.a + stepsize, 0.0);
		y0 = Complex (y0.real () + Ia.r.a * stepsize,
					y0.imag () + Ia.i.a * stepsize);




// 		/*******************/
// 		/* CORRECT ROOT	   */
// 		/*******************/
		correctRoot<Complex> (f, x0, y0);

		/**********************************************/
		/* CHECK WE HAVE NOT JUMPED TO ANOTHER ROOT   */
		/**********************************************/
		if (!(IY0+Ia*stepsize).contains (y0)) {
			printf ("error! Jumped to other thread!\n");
			_Polynomial::releaseCombinatorialNumbers ();
			return NULL;
		}


		outputList.push_back (Cell<double> (x0.real (), y0.real (), y0.imag ()));

	}





	// 	/***********************************************/
	/* PREPARE OUTPUT AS A BINARY ARRAY OF DOUBLES */
	/***********************************************/

	double *rop = new double [((3 * outputList.size () + 1))];
	rop[0] = outputList.size ();
	for (int i=0; i<outputList.size (); i++) {
		rop[3*i+1] = outputList[i].x[0];
		rop[3*i+2] = outputList[i].x[1];
		rop[3*i+3] = outputList[i].x[2];
	}
	_Polynomial::releaseCombinatorialNumbers ();
	return rop;
}


mpfr_t * homotopyPath_mp (int degree, mpfr_t *_coef, mpfr_t _y0R, mpfr_t _y0I, int prec) {
    mpfr_prec_t oldprec = mpfr_get_default_prec();
    mpfr_set_default_prec(prec);

	_Polynomial::startCombinatorialNumbers (degree);

// // 	/************************************************/
// // 	/* VARIABLE DECLARATION AND DATA INITIALIZATION */
// // 	/************************************************/
	MPComplex aux;								// Complex aux variable.

	mpfr_t stepsize , eps, calc, tol;
	mpfr_inits (stepsize, eps, calc, tol, NULL); // step size and radius of the box
	mpfr_set_d (stepsize, 0.0001, MPFR_RNDN);
	mpfr_set_d (tol, mpfr_get_default_prec(), MPFR_RNDN);
	mpfr_sub_si (tol, tol, 10, MPFR_RNDN);
	mpfr_neg (tol, tol, MPFR_RNDN);
	mpfr_exp2 (tol, tol, MPFR_RNDN);


	int nCoef = ((degree+1)*(degree+2)) / 2;	// number of monomials
	MPComplex coef[nCoef];						// coefficient list for floating point representation of
												// the polynomial.
	MPIComplex Icoef[nCoef];	 					// coefficient list for Interval representation of
												// the polynomial.

	std::vector<Cell<mpfr_t> > outputList;



 // 	INITIALIZE COEFICIENT DATA FROM ARGUMENT INTO APPROPRIATE DATA STRUCTURES
	for (int i=0; i<nCoef; ++i) {
		Icoef[i] = MPIComplex (_coef[4*i] ,_coef[4*i+1] ,_coef[4*i+2] ,_coef[4*i+3]);
		coef[i] = 0.5*(MPComplex (_coef[4*i] , _coef[4*i+2]) +
							MPComplex (_coef[4*i+1] , _coef[4*i+3]));
	}


	Polynomial<MPComplex> f (degree, coef);		// complex polynomials (with fp arithmetic)
												// to be used in Newton method correction
	Polynomial<MPIComplex> If (degree, Icoef);	// complex polynomials (with Interval arithmetic)
	Polynomial<MPIComplex> Ig (degree);			// to be used in validated homotopy
	Polynomial<MPIComplex> Ih (degree);



//  	// INITIALIZE FLOATING POINT COMPLEX APPROXIMATION OF THE ROOT
	MPComplex x0 (0.0);							// x0 of the root (x0.imag = 0)
	MPComplex y0 (_y0R, _y0I);					// approximated y0 of the root
	MPIComplex Ix0;								// Interval representation for x0
	MPIComplex Iy0;								// Interval representation for y0
	MPIComplex IY0;								// Interval enclosure for y0 for Newton Method
	MPIComplex Iy1, IY1;							// Interval representation of zeros for rotated polynomial
	MPIComplex Ix1;

	MPComplex a;									// y'(x0) as Complex
	MPIComplex Ia;								// y'(x0) as MPIComplex


#ifdef PROFILING
	int nsteps = 0;								// number of steps performed
	int nrejectedEps = 0;						// number of rejected validations of boxes
	int nrejectedSteps = 0;						// number of rejected validations of tubes
#endif


	outputList.push_back (Cell<mpfr_t> (x0.r, y0.r, y0.i));

// 	/*******************/
// 	/* CORRECT ROOT	   */
// 	/*******************/


	correctRoot<MPComplex> (f, x0, y0);


// 	/*******************/
// 	/** MAIN LOOP	   */
// 	/*******************/
	while (mpfr_cmp_si(x0.r, 1.0) < 0) {

#ifdef PROFILING
	nsteps++; // used for profiling only
#endif

		/***********************************************************/
		/*** BOX SIZE ESTIMATION. F RESTRICTED TO X0 IS INJECTIVE. */
		/***********************************************************/
		aux = f.diffYY (x0, y0);
		abs (calc, aux);
		if (mpfr_cmp_d (calc, 1.0e-10) < 0) {
			mpfr_set_d (eps, 0.5, MPFR_RNDN);
		} else {
			abs (eps, (f.diffY(x0,y0) / aux));
			mpfr_div_si (eps, eps, 8, MPFR_RNDN);
		}

		/**************************/
		/*** VALIDATE y0 AS POINT.*/
		/**************************/
		Ix0 = x0;
		Iy0 = y0;
		IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);


		while (!validate<MPIComplex> (If, Ix0, Iy0, IY0)) {
#ifdef PROFILING
	nrejectedEps++; // used for profiling
#endif
			if (mpfr_cmp (eps ,tol) < 0) {
				printf ("Singularity detected. Abort Sirocco\n");
				/*******************/
				/* CLEAN VARIABLES */
				/*******************/
				mpfr_clears (stepsize, eps, calc, tol, NULL);

                mpfr_set_default_prec(oldprec);

				_Polynomial::releaseCombinatorialNumbers ();
				return NULL;
            }
			mpfr_div_si (eps, eps, 2, MPFR_RNDN);
			IY0 = Iy0;
			IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);
		}

		// COMPUTE a = -fx(x0,y0) / fy(x0.y0)
		// both fp and Interval
		a = getA<MPComplex> (f, x0, y0);
		Ia = a;


		/************************/
		/* ESTIMATE STEP SIZE ***/
		/************************/
		// we have: aux = fyy
		// store all in aux2

		aux = a*a*aux + 2.0*a*f.diffXY(x0,y0) + f.diffXX (x0, y0);

		// DETECT INFLEXION POINT
		abs (calc, aux);
		if (mpfr_cmp_d(calc, 1.0e-10) < 0) {
			mpfr_set_si (stepsize, 1, MPFR_RNDN);
		} else {
			abs (calc, (eps * f.diffY (x0, y0) )/ aux);
			mpfr_sqrt (stepsize, calc, MPFR_RNDN);
		}

		// if (stepsize + x0.real () > 1.0) stepsize = 1.000001 - x0.real ();
		mpfr_add (calc, x0.r, stepsize, MPFR_RNDU);
		if (mpfr_cmp_si (calc, 1) > 0) {
			mpfr_si_sub (stepsize, 1, x0.r, MPFR_RNDU);
		}


		/*******************/
		/** VALIDATE STEP  */
		/*******************/
		mpfr_set_si (calc, 0, MPFR_RNDN);
		Ix1 = MPInterval(calc,stepsize);


		// PERFORM TRANSLATION AND ROTATION OF THE POLYNOMIAL
		Ig = If.IlinearVarChange2 (Ix0);
		Ih = Ig.IlinearVarChange (Ia);


		while (!validate<MPIComplex> (Ih, Ix1, Iy0, IY0)) {
#ifdef PROFILING
	nrejectedSteps++; // used for profiling
#endif
			mpfr_div_si (stepsize, stepsize, 2, MPFR_RNDN);
			mpfr_mul_d (eps, eps, 0.95, MPFR_RNDN);

			IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);
			Ix1 = MPInterval (calc, stepsize);

			if (mpfr_cmp_d (stepsize, 1.0e-13) < 0) {
				/*******************/
				/* CLEAN VARIABLES */
				/*******************/
				mpfr_clears (stepsize, eps, calc, tol, NULL);

                mpfr_set_default_prec(oldprec);
				_Polynomial::releaseCombinatorialNumbers ();
				return NULL;
            }

		}
#ifdef DEVELOPER
	std::cout << "x0 = " << x0.r << " step = " << stepsize << std::endl;
#endif

// // 		/********************/
// // 		/* UPDATE VARIABLES */
// // 		/********************/
		x0 = MPComplex (Ix0.r.a) + MPComplex (stepsize);
		y0 = y0 + stepsize*a;




// 		/*******************/
// 		/* CORRECT ROOT	   */
// 		/*******************/
		correctRoot<MPComplex> (f, x0, y0);

		/**********************************************/
		/* CHECK WE HAVE NOT JUMPED TO ANOTHER ROOT   */
		/**********************************************/
		if (!(MPIComplex(y0).subset (IY0+Ia*stepsize))) {
			mpfr_clears (stepsize, eps, calc, tol, NULL);
			printf ("error! Jumped to other thread!\n");
            mpfr_set_default_prec(oldprec);
			_Polynomial::releaseCombinatorialNumbers ();
			return NULL;
		}


		outputList.push_back (Cell<mpfr_t> (x0.r, y0.r, y0.i));

	}


	// 	/***********************************************/
	/* PREPARE OUTPUT AS A BINARY ARRAY OF DOUBLES */
	/***********************************************/

	mpfr_t *rop = new mpfr_t [((3 * outputList.size () + 1))];
    mpfr_init(rop[0]);
	mpfr_set_si(rop[0], outputList.size(), MPFR_RNDN);
	for (int i=0; i<outputList.size (); i++) {
        mpfr_init(rop[3*i+1]);
        mpfr_init(rop[3*i+2]);
        mpfr_init(rop[3*i+3]);
		mpfr_set(rop[3*i+1], outputList[i].x[0], MPFR_RNDN);
		mpfr_set(rop[3*i+2], outputList[i].x[1], MPFR_RNDN);
		mpfr_set(rop[3*i+3], outputList[i].x[2], MPFR_RNDN);
	}


mpfr_clears (stepsize, eps, calc, tol, NULL);

mpfr_set_default_prec(oldprec);
_Polynomial::releaseCombinatorialNumbers ();
return rop;
}


mpfr_t * homotopyPath_mp_comps (int degree, mpfr_t *_coef, mpfr_t _y0R, mpfr_t _y0I, int prec, int nothercomps, int *degreescomps, mpfr_t *_coefscomps) {
    mpfr_prec_t oldprec = mpfr_get_default_prec();
    mpfr_set_default_prec(prec);


    int tdegree = degree;
    for (int i=0; i< nothercomps; ++i) {

        tdegree = max(tdegree, degreescomps[i]);
    }

    _Polynomial::startCombinatorialNumbers (tdegree);


    // // 	/************************************************/
    // // 	/* VARIABLE DECLARATION AND DATA INITIALIZATION */
    // // 	/************************************************/
    MPComplex aux;								// Complex aux variable.

    mpfr_t stepsize , eps, calc, tol;
    mpfr_inits (stepsize, eps, calc, tol, NULL); // step size and radius of the box
    mpfr_set_d (stepsize, 0.0001, MPFR_RNDN);
    mpfr_set_d (tol, mpfr_get_default_prec(), MPFR_RNDN);
    mpfr_sub_si (tol, tol, 10, MPFR_RNDN);
    mpfr_neg (tol, tol, MPFR_RNDN);
    mpfr_exp2 (tol, tol, MPFR_RNDN);


    int nCoef = ((degree+1)*(degree+2)) / 2;	// number of monomials
    MPComplex coef[nCoef];						// coefficient list for floating point representation of
    // the polynomial.
    MPIComplex Icoef[nCoef];	 					// coefficient list for Interval representation of
    // the polynomial.

    std::vector<Cell<mpfr_t> > outputList;



    // 	INITIALIZE COEFICIENT DATA FROM ARGUMENT INTO APPROPRIATE DATA STRUCTURES
    for (int i=0; i<nCoef; ++i) {
        Icoef[i] = MPIComplex (_coef[4*i] ,_coef[4*i+1] ,_coef[4*i+2] ,_coef[4*i+3]);
        coef[i] = 0.5*(MPComplex (_coef[4*i] , _coef[4*i+2]) +
        MPComplex (_coef[4*i+1] , _coef[4*i+3]));
    }


    Polynomial<MPComplex> f (degree, coef);		// complex polynomials (with fp arithmetic)
    // to be used in Newton method correction
    Polynomial<MPIComplex> If (degree, Icoef);	// complex polynomials (with Interval arithmetic)
    Polynomial<MPIComplex> Ig (degree);			// to be used in validated homotopy
    Polynomial<MPIComplex> Ih (degree);

    Polynomial<MPIComplex> othercomponents[nothercomps];
    Polynomial<MPIComplex> Igs[nothercomps];
    Polynomial<MPIComplex> Ihs[nothercomps];

    int acumcoefscomps = 0;

    for (int i=0; i< nothercomps; ++i) {
        int cdegree = degreescomps[i];
        int nCoefcomp = ((cdegree+1)*(cdegree+2)) / 2;
        MPIComplex MPIcoefcomp[nCoefcomp];
        for (int j=0; j<nCoefcomp; ++j) {
            MPIcoefcomp[j] = MPIComplex (_coefscomps[4*acumcoefscomps] ,_coefscomps[4*acumcoefscomps+1] ,_coefscomps[4*acumcoefscomps+2] ,_coefscomps[acumcoefscomps*4+3]);
            acumcoefscomps++;
        }
        Polynomial<MPIComplex> Ifcomp (cdegree, MPIcoefcomp);
        othercomponents[i] = Ifcomp;
        Polynomial<MPIComplex > Igcomp (cdegree);
        Polynomial<MPIComplex > Ihcomp (cdegree);
        Igs[i] = Igcomp;
        Ihs[i] = Ihcomp;
    }



    //  	// INITIALIZE FLOATING POINT COMPLEX APPROXIMATION OF THE ROOT
    MPComplex x0 (0.0);							// x0 of the root (x0.imag = 0)
    MPComplex y0 (_y0R, _y0I);					// approximated y0 of the root
    MPIComplex Ix0;								// Interval representation for x0
    MPIComplex Iy0;								// Interval representation for y0
    MPIComplex IY0;								// Interval enclosure for y0 for Newton Method
    MPIComplex Iy1, IY1;							// Interval representation of zeros for rotated polynomial
    MPIComplex Ix1;

    MPComplex a;									// y'(x0) as Complex
    MPIComplex Ia;								// y'(x0) as MPIComplex


    #ifdef PROFILING
    int nsteps = 0;								// number of steps performed
    int nrejectedEps = 0;						// number of rejected validations of boxes
    int nrejectedSteps = 0;						// number of rejected validations of tubes
    #endif


    outputList.push_back (Cell<mpfr_t> (x0.r, y0.r, y0.i));

    // 	/*******************/
    // 	/* CORRECT ROOT	   */
    // 	/*******************/


    correctRoot<MPComplex> (f, x0, y0);


    // 	/*******************/
    // 	/** MAIN LOOP	   */
    // 	/*******************/
    while (mpfr_cmp_si(x0.r, 1.0) < 0) {

        #ifdef PROFILING
        nsteps++; // used for profiling only
        #endif

        /***********************************************************/
        /*** BOX SIZE ESTIMATION. F RESTRICTED TO X0 IS INJECTIVE. */
        /***********************************************************/
        aux = f.diffYY (x0, y0);
        abs (calc, aux);
        if (mpfr_cmp_d (calc, 1.0e-10) < 0) {
            mpfr_set_d (eps, 0.5, MPFR_RNDN);
        } else {
            abs (eps, (f.diffY(x0,y0) / aux));
            mpfr_div_si (eps, eps, 8, MPFR_RNDN);
        }

        /**************************/
        /*** VALIDATE y0 AS POINT.*/
        /**************************/
        Ix0 = x0;
        Iy0 = y0;
        IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);


        while (!validate<MPIComplex> (If, Ix0, Iy0, IY0, nothercomps, othercomponents)) {
            #ifdef PROFILING
            nrejectedEps++; // used for profiling
            #endif
            if (mpfr_cmp (eps ,tol) < 0) {
                printf ("Singularity detected. Abort Sirocco\n");
                /*******************/
                /* CLEAN VARIABLES */
                /*******************/
                mpfr_clears (stepsize, eps, calc, tol, NULL);

                mpfr_set_default_prec(oldprec);

                _Polynomial::releaseCombinatorialNumbers ();
                return NULL;
            }
            mpfr_div_si (eps, eps, 2, MPFR_RNDN);
            IY0 = Iy0;
            IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);
        }

        // COMPUTE a = -fx(x0,y0) / fy(x0.y0)
        // both fp and Interval
        a = getA<MPComplex> (f, x0, y0);
        Ia = a;


        /************************/
        /* ESTIMATE STEP SIZE ***/
        /************************/
        // we have: aux = fyy
        // store all in aux2

        aux = a*a*aux + 2.0*a*f.diffXY(x0,y0) + f.diffXX (x0, y0);

        // DETECT INFLEXION POINT
        abs (calc, aux);
        if (mpfr_cmp_d(calc, 1.0e-10) < 0) {
            mpfr_set_si (stepsize, 1, MPFR_RNDN);
        } else {
            abs (calc, (eps * f.diffY (x0, y0) )/ aux);
            mpfr_sqrt (stepsize, calc, MPFR_RNDN);
        }

        // if (stepsize + x0.real () > 1.0) stepsize = 1.000001 - x0.real ();
        mpfr_add (calc, x0.r, stepsize, MPFR_RNDU);
        if (mpfr_cmp_si (calc, 1) > 0) {
            mpfr_si_sub (stepsize, 1, x0.r, MPFR_RNDU);
        }


        /*******************/
        /** VALIDATE STEP  */
        /*******************/
        mpfr_set_si (calc, 0, MPFR_RNDN);
        Ix1 = MPInterval(calc,stepsize);


        // PERFORM TRANSLATION AND ROTATION OF THE POLYNOMIAL
        Ig = If.IlinearVarChange2 (Ix0);
        Ih = Ig.IlinearVarChange (Ia);


        for (int jj = 0; jj<nothercomps; ++jj) {

            Igs[jj] = othercomponents[jj].IlinearVarChange2(Ix0);
            Ihs[jj] = Igs[jj].IlinearVarChange (Ia);
        }


        while (!validate<MPIComplex> (Ih, Ix1, Iy0, IY0, nothercomps, Ihs)) {
            #ifdef PROFILING
            nrejectedSteps++; // used for profiling
            #endif
            mpfr_div_si (stepsize, stepsize, 2, MPFR_RNDN);
            mpfr_mul_d (eps, eps, 0.95, MPFR_RNDN);

            IY0 = MPIComplex(y0) + eps*MPIComplex (-1.0, 1.0, -1.0, 1.0);
            Ix1 = MPInterval (calc, stepsize);

            if (mpfr_cmp_d (stepsize, 1.0e-13) < 0) {
                /*******************/
                /* CLEAN VARIABLES */
                /*******************/
                mpfr_clears (stepsize, eps, calc, tol, NULL);

                mpfr_set_default_prec(oldprec);
                _Polynomial::releaseCombinatorialNumbers ();
                return NULL;
            }

        }
        #ifdef DEVELOPER
        std::cout << "x0 = " << x0.r << " step = " << stepsize << std::endl;
        #endif

        // // 		/********************/
        // // 		/* UPDATE VARIABLES */
        // // 		/********************/
        x0 = MPComplex (Ix0.r.a) + MPComplex (stepsize);
        y0 = y0 + stepsize*a;




        // 		/*******************/
        // 		/* CORRECT ROOT	   */
        // 		/*******************/
        correctRoot<MPComplex> (f, x0, y0);

        /**********************************************/
        /* CHECK WE HAVE NOT JUMPED TO ANOTHER ROOT   */
        /**********************************************/
        if (!(MPIComplex(y0).subset (IY0+Ia*stepsize))) {
            mpfr_clears (stepsize, eps, calc, tol, NULL);
            printf ("error! Jumped to other thread!\n");
            mpfr_set_default_prec(oldprec);
            _Polynomial::releaseCombinatorialNumbers ();
            return NULL;
        }


        outputList.push_back (Cell<mpfr_t> (x0.r, y0.r, y0.i));

    }


    // 	/***********************************************/
    /* PREPARE OUTPUT AS A BINARY ARRAY OF DOUBLES */
    /***********************************************/

    mpfr_t *rop = new mpfr_t [((3 * outputList.size () + 1))];
    mpfr_init(rop[0]);
    mpfr_set_si(rop[0], outputList.size(), MPFR_RNDN);
    for (int i=0; i<outputList.size (); i++) {
        mpfr_init(rop[3*i+1]);
        mpfr_init(rop[3*i+2]);
        mpfr_init(rop[3*i+3]);
        mpfr_set(rop[3*i+1], outputList[i].x[0], MPFR_RNDN);
        mpfr_set(rop[3*i+2], outputList[i].x[1], MPFR_RNDN);
        mpfr_set(rop[3*i+3], outputList[i].x[2], MPFR_RNDN);
    }


    mpfr_clears (stepsize, eps, calc, tol, NULL);

    mpfr_set_default_prec(oldprec);
    _Polynomial::releaseCombinatorialNumbers ();
    return rop;
}
