#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <builtin_types.h>
#include "intT.h"
#include <fenv.h>
#include <signal.h>

  //electron mass squared
  double m2 = 1.0;
  //electron charge
  double e = 0.30282212;
  //double m2=1.0;
  //double e=1.0;

double EV(double T, void * p, int* WLlist);

double Exact(double T, double B, int fermion)
//Return the exact integrand for constant fields
{
  double TB = e*T*B;
  double Exact;
  if( B < 1.0e-40)
	Exact = 0.0;
  else
    if(fermion == 1)
	Exact = exp(-m2*T)/(T*T*T)*(TB/tanh(TB)-1.0-1.0/3.0*TB*TB);
    else
	if (TB < 50.0)
		Exact = exp(-m2*T)/(T*T*T)*(TB/sinh(TB)-1.0+1.0/6.0*TB*TB);
	else
		Exact = exp(-m2*T)/(T*T*T)*(1.0/6.0*TB*TB-1.0);
  //printf("%f %e\n",T, exp(-T));
  return Exact;
}

double bumpd(double x)
//The bump function
{
  if (abs(x) < 1.0)
	return exp(-1.0/(1.0-x*x));
  else
	return 0.0;
}

double fixfluxB(double rho2, double lambda2)
//Magnetic field strength for the fixflux profile
{
  float l = sqrt(lambda2);
  float lmlmin = (l - lmin)/(tubedist - lmin);
  if(sqrt(rho2) > tubedist/2.0) printf("Warning: inappropriate magnetic field being used. rho=%f\n", sqrt(rho2));
  return  2.0/(lambda2*q2)*(1.0-0.75*lmlmin)*bumpd(2.0*sqrt(rho2/lambda2))
	+ 3.0/(tubedist*tubedist)*lmlmin;
}

double periodicB(double rho2, double lambda2)
//magnetic field for the periodic flux tube profile
{
  double expr, rho, lambda, flraf, f, ceilraf;
  double expral, exprapl;
  rho = sqrt(rho2);
  lambda = sqrt(lambda2);
  expr = exp(-rho/lambda);
  flraf = floor(rho/tubedist);
  ceilraf = ceil(rho/tubedist);
  if(rho < tubedist)
  {
	expral = 0.0;
	if((tubedist-rho)/lambda < 10.0)
		expral = exp(-(rho-tubedist)/lambda);
	f = expr/(tubedist*(1.0 + expr)*(1.0 + expr));
	if(rho > 0.0)
		f += expral/(rho*(1.0+expral)*(1.0+expral));
  }
  else
  {
        if(abs(fmod(rho,tubedist)) < 1.0e-6)
 	{
		f = flraf/(4.0*rho);
	}
	else
	{
	  expral = exp(-(rho-flraf*tubedist)/lambda);
	  exprapl = exp(-(rho-ceilraf*tubedist)/lambda);
          f = flraf*expral/(rho*(1.0+expral)*(1.0+expral)) +
		ceilraf*exprapl/(rho*(1.0+exprapl)*(1.0+exprapl));
        }
  }
  //f=1.0f/lambda2*exp(-1.0f*rho2/lambda2);
  //f=lambda2/8.0f;
  f=tubedist/(2.0*log(2.0)*lambda2)*f;
  //f=1.0;
  return f;
}

double MagField(double rho2, void* p)
//Compute the magnetic field.
{
  double B;
  double sinrho;
  struct Wparams *params = (struct Wparams *) p;
  switch(profile){
	case step:
	  if(rho2<params->l2)
		B=1.0/params->l2;
	  else
		B=0.0;
	  break;
	case smooth:
	  B=params->l2/((params->l2+rho2)*(params->l2+rho2));
	  break;
	case quadratic:
	  if(rho2<params->l2)
		B=2.0/params->l2*(1.0-rho2/params->l2);
	  else
		B=0.0;
	  break;
	case gaussian:
	  if(rho2<100.0*params->l2)
	  	B=1.0/(params->l2*exp(rho2/params->l2));
	  else
		B=0.0;
	  break;
	case periodic:
	  B=periodicB(rho2,params->l2);
	  break;
	case spline:
	  sinrho=sin(pi*sqrt(rho2)/tubedist);
	  B=1.0/(params->l2*exp(sinrho*sinrho/params->l2));
	  break;
	case fixflux:
	  B = fixfluxB(rho2,params->l2);
	  break;
  }
  B=2.0*params->F/e*B;

  return B;
}

double smTscal(double B, double T)
//Effective action for T<<1.0 for scalar QED
{
	double B2=e*e*B*B;
	double B4=B2*B2;
	double B6=B2*B4;
	double T2=T*T;
	double T3=T2*T;
	double T4=T3*T;
	double result;
	result = 7.0/360.0*B4*T*(1.0-m2*T)+(147.0*B4*m2*m2-31.0*B6)/15120.0*T3
		+(31.0*B6*m2-49.0*B4*m2*m2*m2)/15120.0*T4;
	return result;
}

double smTferm(double B, double T)
//Effective action for T<<1.0 for fermionic QED
{
	double B2=e*e*B*B;
	double B4=B2*B2;
	double B6=B2*B4;
	double T2=T*T;
	double T3=T2*T;
	double T4=T3*T;
	double result;
	result = 1.0/45.0*B4*T*(m2*T-1.0) + (2.0*B6/945.0 - B4*m2*m2/90.0)*T3
		+(7.0*B4*m2*m2*m2-4.0*B6*m2)/1890.0*T4;
	//result = 1.0/45.0*B4*T*(T-1.0) + (2.0*B6/945.0 - B4/90.0)*T3
	//	+(7.0*B4-4.0*B6)/1890.0*T4;
	return result;
}

double meanigrand(double* igrand, int ngroups, double* igranderr)
//compute the mean and std. err. for igrand
{
  double meanig=0.0;
  int i;
  *igranderr=0.0;
  for(i=0;i<ngroups;i++)
  {
	meanig+=igrand[i];
  }
  meanig/=ngroups;
  for(i=0; i < ngroups; i++)
  {
	*igranderr+=(igrand[i]-meanig)*(igrand[i]-meanig);
  }
  *igranderr = sqrt(*igranderr);
  *igranderr /= ngroups;
  return meanig;
}

double* Tfunc(double T, void* p, int* order)
//The integrand of the proper time, T, integral
{
  struct Wparams *params = (struct Wparams *) p;
  double rho2 = params->xcm.x*params->xcm.x + params->xcm.y*params->xcm.y;
  double smallT;
  double B;
  double TB;
  double* igrand = malloc(params->ng*sizeof(*igrand));
  int groupsize = 128; //loops per group
  int* WLlist = malloc(groupsize*sizeof(*WLlist));
  int i, j;
  double rho = params->xcm.x;
  double l2 = params->l2;
  double sin2rho = sin(2.0*pi*rho/tubedist);
  double denominator;
  double igmean; double igerr;
  if(!igrand || !WLlist) printf("error Tfunc():  malloc failed\n");

  //printf("func: T=%f\n",T);
  B=MagField(rho2,p);
  TB=e*T*B;
  //printf("T=%f f'/f''=%f\n",T,(params->l2+params->xcm.x*params->xcm.x));
  //determine a suitable definition for small T
  switch(profile)
  {
	case step:
	  smallT=abs(rho*rho-l2);
          break;
        case smooth:
	  smallT= 0.5*(l2+rho*rho);
          break;
        case quadratic:
	  smallT= 0.5*(l2+rho*rho);
          break;
        case gaussian:
	  smallT=l2;
	  break;
        case periodic:
	  smallT=0.5*(l2+rho*rho);
          break;
	case spline:
	  if(rho/tubedist>1.0e-8)
	  	smallT=tubedist*tubedist*l2/(pi*pi*sin2rho*sin2rho);
	  else
		smallT=1.0e12;
	  break;
	case fixflux:
	  //small if T << l2, or if the loop is far from any bump functions.
	  if(rho*rho<l2/4.0)
		smallT = 0.25*l2;
	  else
		smallT = 0.25*l2+(rho-sqrt(l2)/2.0)*(rho-sqrt(l2)/2.0);
  }
  for(i = 0; i < params->ng; i++)
  {
	//printf("func igrand[%d]=%f\n",i,igrand[i]);
	if(T < 0.1 && T < 0.01*smallT)
	//if(T<0.7)
  	{
		if(verbosity >= 2)
		  printf("using small T expression: %f %f %f\n", rho2, T, smallT);
		if(params->fermion == 1)
			igrand[i] = smTferm(B, T);
		else
			igrand[i] = smTscal(B, T);
  	}
        else if(T < 0.01*smallT)
	//use the exact expression
	{
		if(verbosity >= 2)
		  printf("using const. field expression %f %f %f\n", rho2, T, smallT);
		if(TB > 1.0e-6)
			igrand[i] = Exact(T, B, params->fermion);
		else
			igrand[i] = 0.0;
	}
	else if(T > 49.0)
	//for large T, the contribution is exponentially supressed.
	{
		if(verbosity >= 2)
                  printf("using large T expression %f %f %f\n", rho2, T);
		if(params->fermion==1)
		{
			igrand[i]=-1.0*exp(-m2*T)/(3.0*T)*e*e*B*B;
			//if(B>1e-6)
			//	igrand[i]=exp(-m2*T)/(T*T*T)*(TB/tanh(TB)-1.0-1.0/3.0*TB*TB);
			//else
			//	igrand[i]=0.0;
			//printf("igrand(%f)=%f\n",T,igrand[i]);
		}
		else
			igrand[i]=exp(-m2*T)/(6.0*T)*e*e*B*B;
	}
  	else
  	{
		for(j=0;j<groupsize;j++)
        	{
			//printf("func j=%d\n",j);
			WLlist[j]=order[j + i*groupsize];
        	}
		if(verbosity >= 3)
		  printf("EV= %e Exact = %e\n",EV(T,p, WLlist),T/sinh(T));
		if(params->fermion==1)
			igrand[i]= exp(-m2*T)/(T*T*T)*(EV(T, p, WLlist)-1.0-1.0/3.0*TB*TB);
		else
			igrand[i]= exp(-m2*T)/(T*T*T)*(EV(T, p, WLlist)-1.0+1.0/6.0*TB*TB);
		//igrand[i]=Exact(T,1.0);
		//return Exact(T,1.0);

  	}

  }
  if(T > 0.0f && verbosity >= 2){
	igmean = meanigrand(igrand, params->ng, &igerr);
	printf("WLvconstExpression: ");
  		printf("%e %e %e %e %e %e\n",
  		  rho2, T, Exact(T, B, params->fermion), smTscal(B, T), igmean, igerr);
  }
  free(WLlist);
  return igrand;
}

void signal_handler(int sig)
//Floating point signal handler
{
  printf( "Error: Floating point signal detected.\n");
  exit(1);
}

void EVMean(double *EV, float4 *Wsscal_h, float4 *Wsferm_h, int n, int *WL, double T, int fermion)
//Compute the expectation value and error of the Wilson loops
//WL is a list representing a group of wordline indices
//to compute the expectation value of
{
  int i, WLi;
  double EVWlNb2, EVWlN, EVWlNb4;
  double SEWlNb2, SEWlN, SEWlNb4;
  double *EVpart1;
  double *EVpart2;
  double *EVpart4;
  double Nby4part;

  //Turn on floating point exceptions:
  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);

  // Set the signal handler:
  signal(SIGFPE, signal_handler);

  *EV = 0.0;
  EVWlNb2 = 0.0; EVWlN = 0.0; EVWlNb4 = 0.0;
  //allocate arrays to store the wilson loop of each worldline
  EVpart1 = (double *) malloc(n*sizeof(*EVpart1));
  EVpart2 = (double *) malloc(n*sizeof(*EVpart2));
  EVpart4 = (double *) malloc(n*sizeof(*EVpart4));
  if(!EVpart1 || !EVpart2 || !EVpart4) printf("EVonly(): malloc failed\n");
  //determine the sum of the contributions
  for(i = 0; i < n; i++){
	WLi=WL[i];
	//Compute the scalar part
	Nby4part = Wsscal_h[WLi].y + Wsscal_h[WLi].z;
	EVpart1[i] = cos(0.5*Wsscal_h[WLi].x+0.25*(Nby4part));
	EVpart2[i] = cos(Wsscal_h[WLi].x);
	EVpart4[i] = cos(0.5*(Nby4part));
	if(fermion == 1)
	{
		if(isinf(Wsferm_h[WLi].x) !=0 ||isinf(Wsferm_h[WLi].y) !=0 ||isinf(Wsferm_h[WLi].z) !=0
		  || isnan(Wsferm_h[WLi].x) !=0 || isnan(Wsferm_h[WLi].y) !=0 ||isnan(Wsferm_h[WLi].z) !=0 )
		{
			printf("Warning: WSferm is infinite. Worldline=%d\n",WLi);
		}
		//Compute the fermion part
		//printf("Wsferm_h[%d].x=%f\n",WLi,Wsferm_h[WLi].x);
		//printf("Wsferm_h[%d].y=%f\n",WLi,Wsferm_h[WLi].y);
		//printf("Wsferm_h[%d].z=%f\n",WLi,Wsferm_h[WLi].z);
		Nby4part = Wsferm_h[WLi].y+Wsferm_h[WLi].z;
		if(abs(Nby4part) > 600.0 | abs(Wsferm_h[WLi].x) > 600.0)
		{
			printf("Warning: Large cosh argument.  T=%f, WLi=%d\n",T,WLi);
			EVpart1[i] *= 1.0e10;
			EVpart2[i] *= 1.0e10;
			EVpart4[i] *= 1.0e10;
		}
                else
		{
			EVpart1[i] *= cosh(0.5*(double)Wsferm_h[WLi].x+0.25*((double)Nby4part));
			EVpart2[i] *= cosh((double)Wsferm_h[WLi].x);
			EVpart4[i] *= cosh(0.5*((double)Nby4part));
			//EVpart1[i]*=cosh(T);
			//EVpart2[i]*=cosh(T);
			//EVpart4[i]*=cosh(T);
		}
		if(isnan(EVpart1[i]) != 0 || isnan(EVpart2[i]) != 0 || isnan(EVpart4[i]) != 0
			|| isinf(EVpart1[i]) != 0 || isinf(EVpart2[i]) != 0 || isinf(EVpart4[i]) != 0)
			printf("Warning: Infinity detected: WL# %d\n",WLi);
	}
	EVWlNb4 += EVpart4[i];
	EVWlNb2 += EVpart2[i];
	EVWlN   += EVpart1[i];
        //printf("EVpart1[%d]=%f\n",i,EVpart1[i]);
  }
  EVWlNb4 /=n;
  EVWlNb2 /=n;
  EVWlN  /=n;
  //Extrapolate to N=infinity
  *EV=8.0/3.0*EVWlN - 2.0*EVWlNb2 + 1.0/3.0*EVWlNb4;

  free(EVpart1);
  free(EVpart2);
  free(EVpart4);

}


#include <math.h>
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include "matrix_operations.c"
#include "ellipsoid/parametric_function_roots.c"
#include "polynomial.c"

void characteristic_ellipsoid_matrix(double *X, double *R, double phi, double *rotax, double exponent)
{

    double w, x, y, z;
    w = cos(phi / 2.0);
    x = sin(phi / 2.0) * rotax[0];
    y = sin(phi / 2.0) * rotax[1];
    z = sin(phi / 2.0) * rotax[2];

    // Rotation matrix
    double Q[3][3] = {{ 0 }};

    Q[0][0] = 1.0 - 2.0 * (pow(y, 2) + pow(z, 2));
    Q[0][1] = 2.0 * (x * y + w * z);
    Q[0][2] = 2.0 * (x * z - w * y);

    Q[1][0] = 2.0 * (x * y - w * z);
    Q[1][1] = 1.0 - 2.0 * (pow(x, 2) + pow(z, 2));
    Q[1][2] = 2.0 * (y * z + w * x);

    Q[2][0] = 2.0 * (x * z + w * y);
    Q[2][1] = 2.0 * (y * z - w * x);
    Q[2][2] = 1.0 - 2.0 * (pow(x, 2) + pow(y, 2));

    //Rotation matrix transpose
    double Qt[3][3] = {{ 0 }};
    matrix_transpose(&Qt[0][0], &Q[0][0], 3, 3);

    // radii matrix
    double O[3][3] = {{ 0 }};
    double diag_vals[3];
    diag_vals[0] = pow(R[0], exponent * -2.0);
    diag_vals[1] = pow(R[1], exponent * -2.0);
    diag_vals[2] = pow(R[2], exponent * -2.0);
    set_diagonal(&O[0][0], diag_vals, 3, 3);

    // characteristic ellipse matrix
    double X_temp[3][3] = {{ 0 }};
    matrix_multiply(&X_temp[0][0], &O[0][0], &Q[0][0], 3, 3, 3);
    matrix_multiply(X, &Qt[0][0], &X_temp[0][0], 3, 3, 3);

}

double ellipsoid_overlap(double *rA, double *radiiA, double phiA, double *rotaxA,
    double *rB, double *radiiB, double phiB, double *rotaxB)
{

    // find XA^(-1) and XB^(1/2)
    double XA[3][3] = {{ 0 }};
    double XB[3][3] = {{ 0 }};
    characteristic_ellipsoid_matrix(&XA[0][0], &radiiA[0], phiA, &rotaxA[0], -1.0);
    characteristic_ellipsoid_matrix(&XB[0][0], &radiiB[0], phiB, &rotaxB[0], 0.5);

    // find A_AB
    double A_AB[3][3] = {{ 0 }};
    double A_temp[3][3] = {{ 0 }};
    matrix_multiply(&A_temp[0][0], &XA[0][0], &XB[0][0], 3, 3, 3);
    matrix_multiply(&A_AB[0][0], &XB[0][0], &A_temp[0][0], 3, 3, 3);

    // find r_AB
    double rAB[3];
    rAB[0] = rB[0] - rA[0];
    rAB[1] = rB[1] - rA[1];
    rAB[2] = rB[2] - rA[2];

    // find a_AB
    double a_AB[3];
    matrix_multiply(&a_AB[0], &XB[0][0], &rAB[0], 3, 3, 1);

    // extract elements of the matrix A_AB and a_AB
    double a11, a12, a13, a21, a22, a23, a31, a32, a33;
    double b1, b2, b3;
    a11 = A_AB[0][0];
    a12 = A_AB[0][1];
    a13 = A_AB[0][2];
    a21 = A_AB[1][0];
    a22 = A_AB[1][1];
    a23 = A_AB[1][2];
    a31 = A_AB[2][0];
    a32 = A_AB[2][1];
    a33 = A_AB[2][2];
    b1 = a_AB[0];
    b2 = a_AB[1];
    b3 = a_AB[2];

    // find coefficients for the parametric polynomial derivative used to find max
    double h[7];
    double z[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    size_t n = 7;
    h[0] = h0(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[1] = h1(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[2] = h2(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[3] = h3(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[4] = h4(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[5] = h5(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);
    h[6] = h6(a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);

    // find roots
    size_t m;
    m = find_roots(&z[0], &h[0], n);

    double F;
    int i;

    if (m > 1)
    {
        if (f(0, a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3) > f(1, a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3))
        {

           F = f(0, a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);

        }
        else
        {

           F = f(1, a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);

        }

        for(i=0; i<=m-2; i++)
        {

           if (( z[2 * i + 1] == 0 ) & (z[2 * i] > 0) & (z[2 * i] < 1))
           {

              F = f(z[2 * i], a11, a12, a13, a21, a22, a23, a31, a32, a33, b1, b2, b3);

           }

        }
    }
    else
    {
        F = 0.;
    }

    return F;

}

double container_cube_overlap_potential(double *rA, double *radiiA, double phiA, double *rotaxA)
{

    double rB[3];
    double radiiB[3];
    double phiB;
    double rotaxB[3];

    double top, bottom, left, right, front, back;

    // top
    rB[0] = 0.5;
    rB[1] = 0.5;
    rB[2] = 2;

    radiiB[0] = INFINITY;
    radiiB[1] = INFINITY;
    radiiB[2] = 1;

    rotaxB[0] = 1;
    rotaxB[1] = 0;
    rotaxB[2] = 0;

    phiB = 0.0;

    top = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    // bottom
    rB[0] = 0.5;
    rB[1] = 0.5;
    rB[2] = -1;

    radiiB[0] = INFINITY;
    radiiB[1] = INFINITY;
    radiiB[2] = 1.0;

    rotaxB[0] = 1;
    rotaxB[1] = 0;
    rotaxB[2] = 0;

    phiB = 0;

    bottom = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    // left
    rB[0] = 0.5;
    rB[1] = -1.0;
    rB[2] = 0.5;

    radiiB[0] = INFINITY;
    radiiB[1] = 1;
    radiiB[2] = INFINITY;

    rotaxB[0] = 1;
    rotaxB[1] = 0;
    rotaxB[2] = 0;

    phiB = 0;

    left = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    // right
    rB[0] = 0.5;
    rB[1] = 2;
    rB[2] = 0.5;

    radiiB[0] = INFINITY;
    radiiB[1] = 1;
    radiiB[2] = INFINITY;

    rotaxB[0] = 1;
    rotaxB[1] = 0;
    rotaxB[2] = 0;

    phiB = 0;

    right = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    // front
    rB[0] = -1.0;
    rB[1] = 0.5;
    rB[2] = 0.5;

    radiiB[0] = 1.0;
    radiiB[1] = INFINITY;
    radiiB[2] = INFINITY;

    rotaxB[0] = 1.0;
    rotaxB[1] = 0.0;
    rotaxB[2] = 0.0;

    phiB = 0.0;

    front = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    // back
    rB[0] = 2.0;
    rB[1] = 0.5;
    rB[2] = 0.5;

    radiiB[0] = 1;
    radiiB[1] = INFINITY;
    radiiB[2] = INFINITY;

    rotaxB[0] = 1.0;
    rotaxB[1] = 0.0;
    rotaxB[2] = 0.0;

    phiB = 0;

    back = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0], &rB[0], &radiiB[0], phiB, &rotaxB[0]);

    return fminf(top, fminf(bottom, fminf(left, fminf(right, fminf(front, back)))));

}

void random_point_on_sphere(double *rotax, double theta, double z)
{

    double x, y;

    x = sqrt(1 - pow(z, 2)) * cos(theta);
    y = sqrt(1 - pow(z, 2)) * sin(theta);

    rotax[0] = x;
    rotax[1] = y;
    rotax[2] = z;

}

size_t rsa_align_cube(double *x, double *y, double *z,
    size_t npoints, double *radius, double *rotax, double phi,
    int step_limit, unsigned long randSeed)
{

    // Setup GSL random number generator
    const gsl_rng_type * T;
    gsl_rng * r;
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    // Set the seed
    // srand ( time(NULL) );
    // unsigned long randSeed = rand();
    gsl_rng_set(r, randSeed);

    double rA[3];
    double radiiA[3];
    double phiA;
    double rotaxA[3];

    double rB[3];
    double radiiB[3];
    double phiB;
    double rotaxB[3];

    size_t valid_pts;
    double F, G;
    int k, flag, step;

    step = 0;
    valid_pts = 1;

    // Get new ellipsoid position and orientation
    G = 0;
    while (G < 1)
    {

        rB[0] = gsl_rng_uniform (r);
        rB[1] = gsl_rng_uniform (r);
        rB[2] = gsl_rng_uniform (r);
        radiiB[0] = radius[0];
        radiiB[1] = radius[1];
        radiiB[2] = radius[2];
        phiB = phi;
        rotaxB[0] = rotax[0];
        rotaxB[1] = rotax[1];
        rotaxB[2] = rotax[2];

        G = container_cube_overlap_potential(&rB[0], &radiiB[0], phiB, &rotaxB[0]);

    }

    // store ellipsoid position
    x[0] = rB[0];
    y[0] = rB[1];
    z[0] = rB[2];

    // Generate ellipsoid positions
    while ((valid_pts < npoints) & (step < step_limit))
    {

        // Get new ellipsoid position and orientation
        G = 0;
        while (G < 1)
        {

            rB[0] = gsl_rng_uniform (r);
            rB[1] = gsl_rng_uniform (r);
            rB[2] = gsl_rng_uniform (r);
            radiiB[0] = radius[0];
            radiiB[1] = radius[1];
            radiiB[2] = radius[2];
            phiB = phi;
            rotaxB[0] = rotax[0];
            rotaxB[1] = rotax[1];
            rotaxB[2] = rotax[2];

            G = container_cube_overlap_potential(&rB[0], &radiiB[0], phiB, &rotaxB[0]);

        }

        flag = 1;
        for (k = 0; k < valid_pts; k++)
        {

            rA[0] = x[k];
            rA[1] = y[k];
            rA[2] = z[k];
            radiiA[0] = radius[0];
            radiiA[1] = radius[1];
            radiiA[2] = radius[2];
            phiA = phi;
            rotaxA[0] = rotax[0];
            rotaxA[1] = rotax[1];
            rotaxA[2] = rotax[2];

            F = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0],
                &rB[0], &radiiB[0], phiB, &rotaxB[0]);

            if (F < 1.0)
            {

                flag = 0;
                break;

            }
        }
        if (flag == 1)
        {

           x[valid_pts] = rB[0];
           y[valid_pts] = rB[1];
           z[valid_pts] = rB[2];
           valid_pts += 1;

        }

        step += 1;

    }

    gsl_rng_free (r);

    return valid_pts;

}

size_t rsa_cube(double *x, double *y, double *z,
    size_t npoints, double *radius,
    double *rtx, double *rty, double *rtz, double *phi,
    int step_limit, unsigned long randSeed)
{

    // Setup GSL random number generator
    const gsl_rng_type * T;
    gsl_rng * r;
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    // Set the seed
    // srand ( time(NULL) );
    // unsigned long randSeed = rand();
    gsl_rng_set(r, randSeed);

    double rA[3];
    double radiiA[3];
    double phiA;
    double rotaxA[3];

    double rB[3];
    double radiiB[3];
    double phiB;
    double rotaxB[3];

    double theta, sz;

    size_t valid_pts;
    double F, G;
    int k, flag, step;

    step = 0;
    valid_pts = 1;

    // Get new ellipsoid position and orientation
    G = 0;
    while (G < 1)
    {

        rB[0] = gsl_rng_uniform (r);
        rB[1] = gsl_rng_uniform (r);
        rB[2] = gsl_rng_uniform (r);
        radiiB[0] = radius[0];
        radiiB[1] = radius[1];
        radiiB[2] = radius[2];
        phiB = 2 * M_PI * gsl_rng_uniform (r);

        // random rotation axis
        theta = 2 * M_PI * gsl_rng_uniform (r);
        sz = 2 * gsl_rng_uniform (r) - 1;
        random_point_on_sphere(&rotaxB[0], theta, sz);

        G = container_cube_overlap_potential(&rB[0], &radiiB[0], phiB, &rotaxB[0]);

    }

    // store ellipsoid position
    x[0] = rB[0];
    y[0] = rB[1];
    z[0] = rB[2];
    rtx[0] = rotaxB[0];
    rty[0] = rotaxB[1];
    rtz[0] = rotaxB[2];
    phi[0] = phiB;

    // Generate ellipsoid positions
    while ((valid_pts < npoints) & (step < step_limit))
    {

        // Get new ellipsoid position and orientation
        G = 0;
        while (G < 1)
        {

            rB[0] = gsl_rng_uniform (r);
            rB[1] = gsl_rng_uniform (r);
            rB[2] = gsl_rng_uniform (r);
            radiiB[0] = radius[0];
            radiiB[1] = radius[1];
            radiiB[2] = radius[2];
            phiB = 2 * M_PI * gsl_rng_uniform (r);

            // random rotation axis
            theta = 2 * M_PI * gsl_rng_uniform (r);
            sz = 2 * gsl_rng_uniform (r) - 1;
            random_point_on_sphere(&rotaxB[0], theta, sz);

            G = container_cube_overlap_potential(&rB[0], &radiiB[0], phiB, &rotaxB[0]);

        }

        flag = 1;
        for (k = 0; k < valid_pts; k++)
        {

            rA[0] = x[k];
            rA[1] = y[k];
            rA[2] = z[k];
            radiiA[0] = radius[0];
            radiiA[1] = radius[1];
            radiiA[2] = radius[2];
            phiA = phi[k];
            rotaxA[0] = rtx[k];
            rotaxA[1] = rty[k];
            rotaxA[2] = rtz[k];

            F = ellipsoid_overlap(&rA[0], &radiiA[0], phiA, &rotaxA[0],
                &rB[0], &radiiB[0], phiB, &rotaxB[0]);

            if (F < 1.0)
            {

                flag = 0;
                break;

            }
        }
        if (flag == 1)
        {

            x[valid_pts] = rB[0];
            y[valid_pts] = rB[1];
            z[valid_pts] = rB[2];
            rtx[valid_pts] = rotaxB[0];
            rty[valid_pts] = rotaxB[1];
            rtz[valid_pts] = rotaxB[2];
            phi[valid_pts] = phiB;

            valid_pts += 1;

        }

        step += 1;

    }

    gsl_rng_free (r);

    return valid_pts;

}


/* specfunc/gsl_sf_log.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/* Author:  G. Jungman */

#ifndef __GSL_SF_LOG_H__
#define __GSL_SF_LOG_H__

#include <gsl/gsl_sf_result.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

/* Provide a logarithm function with GSL semantics.
 *
 * exceptions: GSL_EDOM
 */
int gsl_sf_log_e(const double x, gsl_sf_result * result);
double gsl_sf_log(const double x);

/* Log(|x|)
 *
 * exceptions: GSL_EDOM
 */
int gsl_sf_log_abs_e(const double x, gsl_sf_result * result);
double gsl_sf_log_abs(const double x);

/* Complex Logarithm
 *   exp(lnr + I theta) = zr + I zi
 * Returns argument in [-pi,pi].
 *
 * exceptions: GSL_EDOM
 */
int gsl_sf_complex_log_e(const double zr, const double zi, gsl_sf_result * lnr, gsl_sf_result * theta);

/* Log(1 + x)
 *
 * exceptions: GSL_EDOM
 */
int gsl_sf_log_1plusx_e(const double x, gsl_sf_result * result);
double gsl_sf_log_1plusx(const double x);

/* Log(1 + x) - x
 *
 * exceptions: GSL_EDOM
 */
int gsl_sf_log_1plusx_mx_e(const double x, gsl_sf_result * result);
double gsl_sf_log_1plusx_mx(const double x);

#ifdef HAVE_INLINE
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>

extern inline
int
gsl_sf_log_e(const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(x <= 0.0) {
    result->val = GSL_NAN;
    result->err = GSL_NAN;
    GSL_ERROR ("domain error", GSL_EDOM);
  }
  else {
    result->val = log(x);
    result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return GSL_SUCCESS;
  }
}
extern inline
int
gsl_sf_log_abs_e(const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(x == 0.0) {
    result->val = GSL_NAN;
    result->err = GSL_NAN;
    GSL_ERROR ("domain error", GSL_EDOM);
  }
  else {
    result->val = log(fabs(x));
    result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return GSL_SUCCESS;
  }
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_SF_LOG_H__ */


/***********************************************************************
* mexeigPartial.c : C mex file for the routine DSYEVX in LAPACK
*
* mex -O -largeArrayDims -lmwlapack -lmwblas mexeigPartial.c
*
*  [V,D,nv] = mexeigPartial(A, vl, vu, options);
*
* options.jobz = 1 (default), compute both eigenvectors and eigenvalues;
*              = 2, compute only eigenvalues.
* options.range = 1 (default), compute all;
*               = 2, compute  eigenvalues between [vl, vu].
*               = 3, compute  vl-th to vu-th eigenvalues.
* options.abstol = tolerance (default = 1e-8)
***********************************************************************/

#include <math.h>
#include <mex.h>
#include <matrix.h>
#include <blas.h>
#include <lapack.h>
#include <string.h> /* needed for memcpy() */

#if !defined(_WIN32)
#define dsyevd dsyevd_
#define dsyevx dsyevx_
#define dgesdd dgesdd_
#endif

/**********************************************************
*
***********************************************************/
void mexFunction(
      int nlhs,   mxArray  *plhs[],
      int nrhs,   const mxArray  *prhs[] )

{    double   *A, *AA, *nV, *V, *D, VL, VU, abstol, *work, *work2, *tmp;

     ptrdiff_t   m, n, IL, IU, lwork, *iwork, *ifail, info, options;
     char     *jobz="V";
     char     *uplo="U";
     char     *range="A";

/* CHECK FOR PROPER NUMBER OF ARGUMENTS */

   if (nrhs > 4){
      mexErrMsgTxt("mexeigPartial: requires at most 4 input arguments."); }
   if (nlhs > 3){
      mexErrMsgTxt("mexeigPartial: requires at most 3 output argument."); }

/* CHECK THE DIMENSIONS */

    m = mxGetM(prhs[0]);
    n = mxGetN(prhs[0]);
    if (m != n) {
       mexErrMsgTxt("mexeigPartial: matrix must be square."); }
    if (mxIsSparse(prhs[0])) {
       mexErrMsgTxt("mexeigPartial: sparse matrix not allowed."); }
    A = mxGetPr(prhs[0]);

    options = 1;
    abstol = 1e-8;
    /*Get fields from options */
    tmp = mxGetPr(mxGetField(prhs[3], 0, "abstol"));  abstol  = *tmp;
    tmp = mxGetPr(mxGetField(prhs[3], 0, "jobz"));    options = (ptrdiff_t)*tmp;
    tmp = mxGetPr(mxGetField(prhs[3], 0, "range"));   options = (ptrdiff_t)*tmp;
    if (options==1) {
       range="A";  }
    else if (options==2) {
       range="V"; VL = *mxGetPr(prhs[1]); VU = *mxGetPr(prhs[2]);  }
    else if (options == 3) {
       range="I";
       IL = (ptrdiff_t)*mxGetPr(prhs[1]);
       IU = (ptrdiff_t)*mxGetPr(prhs[2]);
    }
    /***** create return argument *****/
    plhs[0] = mxCreateDoubleMatrix(n,n,mxREAL);
    V = mxGetPr(plhs[0]);
    plhs[1] = mxCreateDoubleMatrix(n,1,mxREAL);
    D = mxGetPr(plhs[1]);
    plhs[2] = mxCreateDoubleMatrix(1,1,mxREAL);
    nV = mxGetPr(plhs[2]);

    /***** Do the computations in a subroutine *****/
    lwork  = 8*n;
    work   =  mxCalloc(lwork,sizeof(double));
    iwork  =  mxCalloc(5*n, sizeof(ptrdiff_t));
    ifail  =  mxCalloc(n, sizeof(ptrdiff_t));

    AA =  mxCalloc(n*n,sizeof(double));
    memcpy(AA,mxGetPr(prhs[0]),(n*n)*sizeof(double));

    dsyevx(jobz, range, uplo, &n, AA, &n, &VL, &VU, &IL, &IU,
           &abstol, &m, D, V, &n, work, &lwork, iwork, ifail, &info);

    *nV = m;
    return;
 }
/**********************************************************/


#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_cblas.h>

#include "tests.h"

void
test_rot (void) {
const double flteps = 1e-4, dbleps = 1e-6;
  {
   int N = 1;
   float c = 0.0f;
   float s = 0.0f;
   float X[] = { -0.314f };
   int incX = 1;
   float Y[] = { -0.406f };
   int incY = -1;
   float x_expected[] = { 0.0f };
   float y_expected[] = { 0.0f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 558)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 559)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.866025403784f;
   float s = 0.5f;
   float X[] = { -0.314f };
   int incX = 1;
   float Y[] = { -0.406f };
   int incY = -1;
   float x_expected[] = { -0.474932f };
   float y_expected[] = { -0.194606f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 560)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 561)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.0f;
   float s = -1.0f;
   float X[] = { -0.314f };
   int incX = 1;
   float Y[] = { -0.406f };
   int incY = -1;
   float x_expected[] = { 0.406f };
   float y_expected[] = { -0.314f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 562)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 563)");
     }
   };
  };

  {
   int N = 1;
   float c = -1.0f;
   float s = 0.0f;
   float X[] = { -0.314f };
   int incX = 1;
   float Y[] = { -0.406f };
   int incY = -1;
   float x_expected[] = { 0.314f };
   float y_expected[] = { 0.406f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 564)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 565)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = 0;
   double X[] = { -0.493 };
   int incX = 1;
   double Y[] = { -0.014 };
   int incY = -1;
   double x_expected[] = { 0.0 };
   double y_expected[] = { 0.0 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 566)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 567)");
     }
   };
  };

  {
   int N = 1;
   double c = 0.866025403784;
   double s = 0.5;
   double X[] = { -0.493 };
   int incX = 1;
   double Y[] = { -0.014 };
   int incY = -1;
   double x_expected[] = { -0.433950524066 };
   double y_expected[] = { 0.234375644347 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 568)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 569)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = -1;
   double X[] = { -0.493 };
   int incX = 1;
   double Y[] = { -0.014 };
   int incY = -1;
   double x_expected[] = { 0.014 };
   double y_expected[] = { -0.493 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 570)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 571)");
     }
   };
  };

  {
   int N = 1;
   double c = -1;
   double s = 0;
   double X[] = { -0.493 };
   int incX = 1;
   double Y[] = { -0.014 };
   int incY = -1;
   double x_expected[] = { 0.493 };
   double y_expected[] = { 0.014 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 572)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 573)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.0f;
   float s = 0.0f;
   float X[] = { -0.808f };
   int incX = -1;
   float Y[] = { -0.511f };
   int incY = 1;
   float x_expected[] = { 0.0f };
   float y_expected[] = { 0.0f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 574)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 575)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.866025403784f;
   float s = 0.5f;
   float X[] = { -0.808f };
   int incX = -1;
   float Y[] = { -0.511f };
   int incY = 1;
   float x_expected[] = { -0.955249f };
   float y_expected[] = { -0.038539f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 576)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 577)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.0f;
   float s = -1.0f;
   float X[] = { -0.808f };
   int incX = -1;
   float Y[] = { -0.511f };
   int incY = 1;
   float x_expected[] = { 0.511f };
   float y_expected[] = { -0.808f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 578)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 579)");
     }
   };
  };

  {
   int N = 1;
   float c = -1.0f;
   float s = 0.0f;
   float X[] = { -0.808f };
   int incX = -1;
   float Y[] = { -0.511f };
   int incY = 1;
   float x_expected[] = { 0.808f };
   float y_expected[] = { 0.511f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 580)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 581)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = 0;
   double X[] = { -0.176 };
   int incX = -1;
   double Y[] = { -0.165 };
   int incY = 1;
   double x_expected[] = { 0.0 };
   double y_expected[] = { 0.0 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 582)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 583)");
     }
   };
  };

  {
   int N = 1;
   double c = 0.866025403784;
   double s = 0.5;
   double X[] = { -0.176 };
   int incX = -1;
   double Y[] = { -0.165 };
   int incY = 1;
   double x_expected[] = { -0.234920471066 };
   double y_expected[] = { -0.0548941916244 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 584)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 585)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = -1;
   double X[] = { -0.176 };
   int incX = -1;
   double Y[] = { -0.165 };
   int incY = 1;
   double x_expected[] = { 0.165 };
   double y_expected[] = { -0.176 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 586)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 587)");
     }
   };
  };

  {
   int N = 1;
   double c = -1;
   double s = 0;
   double X[] = { -0.176 };
   int incX = -1;
   double Y[] = { -0.165 };
   int incY = 1;
   double x_expected[] = { 0.176 };
   double y_expected[] = { 0.165 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 588)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 589)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.0f;
   float s = 0.0f;
   float X[] = { -0.201f };
   int incX = -1;
   float Y[] = { 0.087f };
   int incY = -1;
   float x_expected[] = { 0.0f };
   float y_expected[] = { 0.0f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 590)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 591)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.866025403784f;
   float s = 0.5f;
   float X[] = { -0.201f };
   int incX = -1;
   float Y[] = { 0.087f };
   int incY = -1;
   float x_expected[] = { -0.130571f };
   float y_expected[] = { 0.175844f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 592)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 593)");
     }
   };
  };

  {
   int N = 1;
   float c = 0.0f;
   float s = -1.0f;
   float X[] = { -0.201f };
   int incX = -1;
   float Y[] = { 0.087f };
   int incY = -1;
   float x_expected[] = { -0.087f };
   float y_expected[] = { -0.201f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 594)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 595)");
     }
   };
  };

  {
   int N = 1;
   float c = -1.0f;
   float s = 0.0f;
   float X[] = { -0.201f };
   int incX = -1;
   float Y[] = { 0.087f };
   int incY = -1;
   float x_expected[] = { 0.201f };
   float y_expected[] = { -0.087f };
   cblas_srot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], flteps, "srot(case 596)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], flteps, "srot(case 597)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = 0;
   double X[] = { -0.464 };
   int incX = -1;
   double Y[] = { 0.7 };
   int incY = -1;
   double x_expected[] = { 0.0 };
   double y_expected[] = { 0.0 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 598)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 599)");
     }
   };
  };

  {
   int N = 1;
   double c = 0.866025403784;
   double s = 0.5;
   double X[] = { -0.464 };
   int incX = -1;
   double Y[] = { 0.7 };
   int incY = -1;
   double x_expected[] = { -0.051835787356 };
   double y_expected[] = { 0.838217782649 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 600)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 601)");
     }
   };
  };

  {
   int N = 1;
   double c = 0;
   double s = -1;
   double X[] = { -0.464 };
   int incX = -1;
   double Y[] = { 0.7 };
   int incY = -1;
   double x_expected[] = { -0.7 };
   double y_expected[] = { -0.464 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 602)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 603)");
     }
   };
  };

  {
   int N = 1;
   double c = -1;
   double s = 0;
   double X[] = { -0.464 };
   int incX = -1;
   double Y[] = { 0.7 };
   int incY = -1;
   double x_expected[] = { 0.464 };
   double y_expected[] = { -0.7 };
   cblas_drot(N, X, incX, Y, incY, c, s);
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(X[i], x_expected[i], dbleps, "drot(case 604)");
     }
   };
   {
     int i;
     for (i = 0; i < 1; i++) {
       gsl_test_rel(Y[i], y_expected[i], dbleps, "drot(case 605)");
     }
   };
  };

}


#pragma once

#include <memory>
#include <random>
#include <gsl/gsl_rng.h>

namespace EERAModel {
/**
 * @brief Namespace containing objects related to random number generation
 *
 * This namespace contains a wrapper interace to existing random number
 * generation libraries in order for them to be used within all
 * model functions.
 */
namespace Random {

/**
 * @class RNGInterface
 * @brief Abstract interface to a random number generator
 */
class RNGInterface {
public:
    using Sptr = std::shared_ptr<RNGInterface>;

    virtual ~RNGInterface() = default;

    /**
     * @brief Flat distribution
     *
     * Return a random number from a flat distribution
     *
     * @param a Lower limit of distribution interval
     * @param b Upper limit of distribution interval
     *
     * @return Random number
     */
    virtual double Flat(double a, double b) = 0;

    /**
     * @brief Multinomial distribution
     *
     * Return a random number from a multinomial distribution
     */
    virtual void Multinomial(size_t K, unsigned int N, const double p[], unsigned int n[]) = 0;

    /**
     * @brief Poisson distribution
     *
     * Return a random number from a Poisson distribution
     *
     * @param mu Mean of the distribution
     *
     * @return Random number
     */
    virtual double Poisson(double mu) = 0;

    /**
     * @brief Gamma distribution
     *
     * Return a random number from a Gamma distribution
     *
     * @param a Gamma a parameter
     * @param b Gamma b parameter
     *
     * @return Random number
     */
    virtual double Gamma(double a, double b) = 0;

    /**
     * @brief Beta distribution
     *
     * Return a random number from a Beta distribution
     *
     * @param a Beta a parameter
     * @param b Beta b parameter
     *
     * @return Random number
     */
    virtual double Beta(double a, double b) = 0;

   /**
    * @brief Binomial distribution
    *
    * Return a random number from a Binomial distribution
    *
    * @param p Binomial p parameter: probability
    * @param n Binomial n parameter: the population to sample
    *
    * @return Random number
    */
   virtual unsigned int Binomial(double p, unsigned int n) = 0;

    /**
     * @brief MT19937 generator
     *
     * Returns a reference to a std::mt19937 object
     *
     * @return mt19937 reference
     */
    virtual std::mt19937& MT19937() = 0;
};

/**
 * @class RNG
 * @brief Wrapper for the GNU Scientific Library random number generator
 */
class RNG : public RNGInterface
{
public:
    using Sptr = std::shared_ptr<RNG>;

    /**
     * @brief Contructor
     *
     * Sets up a handle for the underlying GSL and STL random number generator. Seeds the randomiser
     * with the supplied seed
     *
     * @param seed Seed value
     */
    RNG(unsigned long int seed);

    /** @brief Flat distribution override */
    virtual double Flat(double a, double b) override;

    /** @brief Multinomial distribution override */
    virtual void Multinomial(size_t K, unsigned int N, const double p[], unsigned int n[]) override;

    /** @brief Poisson distribution override */
    virtual double Poisson(double mu) override;

    /** @brief Gamma distribution override */
    virtual double Gamma(double a, double b) override;

    /** @brief Beta distribution override */
    virtual double Beta(double a, double b) override;

    /** @brief Binomial distribution override */
    virtual unsigned int Binomial(double p, unsigned int n) override;

    /** @brief MT19937 override */
    virtual std::mt19937& MT19937() override;

private:
    /**
     * @private
     * @brief Handle for the GSL random number generator
     */
    gsl_rng* r_;

    /**
     * @private
     * @brief Mersenne twister generator
     */
    std::mt19937 gen_;
};

} // namespace Random
} // namespace EERAModel


#include <stdio.h>
#include <gsl/gsl_multiset.h>

int
main (void)
{
  gsl_multiset * c;
  size_t i;

  printf ("All multisets of {0,1,2,3} by size:\n") ;
  for (i = 0; i <= 4; i++)
    {
      c = gsl_multiset_calloc (4, i);
      do
        {
          printf ("{");
          gsl_multiset_fprintf (stdout, c, " %u");
          printf (" }\n");
        }
      while (gsl_multiset_next (c) == GSL_SUCCESS);
      gsl_multiset_free (c);
    }

  return 0;
}


/* multimin/vector_bfgs.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Fabrice Rossi
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* vector_bfgs.c -- Limited memory Broyden-Fletcher-Goldfarb-Shanno
   conjugate gradient method */

/* Modified by Brian Gough to use single iteration structure */

#include <config.h>
#include <gsl/gsl_multimin.h>
#include <gsl/gsl_blas.h>

#include "directional_minimize.c"

typedef struct
{
  int iter;
  double step;
  double max_step;
  double tol;
  gsl_vector *x1;
  gsl_vector *dx1;
  gsl_vector *x2;
  double g0norm;
  double pnorm;
  gsl_vector *p;
  gsl_vector *x0;
  gsl_vector *g0;
  gsl_vector *dx0;
  gsl_vector *dg0;
}
vector_bfgs_state_t;

static int
vector_bfgs_alloc (void *vstate, size_t n)
{
  vector_bfgs_state_t *state = (vector_bfgs_state_t *) vstate;

  state->x1 = gsl_vector_calloc (n);

  if (state->x1 == 0)
    {
      GSL_ERROR ("failed to allocate space for x1", GSL_ENOMEM);
    }

  state->dx1 = gsl_vector_calloc (n);

  if (state->dx1 == 0)
    {
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for dx1", GSL_ENOMEM);
    }

  state->x2 = gsl_vector_calloc (n);

  if (state->x2 == 0)
    {
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for x2", GSL_ENOMEM);
    }

  state->p = gsl_vector_calloc (n);

  if (state->p == 0)
    {
      gsl_vector_free (state->x2);
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for p", GSL_ENOMEM);
    }

  state->x0 = gsl_vector_calloc (n);

  if (state->x0 == 0)
    {
      gsl_vector_free (state->p);
      gsl_vector_free (state->x2);
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
    }

  state->g0 = gsl_vector_calloc (n);

  if (state->g0 == 0)
    {
      gsl_vector_free (state->x0);
      gsl_vector_free (state->p);
      gsl_vector_free (state->x2);
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
    }

  state->dx0 = gsl_vector_calloc (n);

  if (state->dx0 == 0)
    {
      gsl_vector_free (state->g0);
      gsl_vector_free (state->x0);
      gsl_vector_free (state->p);
      gsl_vector_free (state->x2);
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
    }

  state->dg0 = gsl_vector_calloc (n);

  if (state->dg0 == 0)
    {
      gsl_vector_free (state->dx0);
      gsl_vector_free (state->g0);
      gsl_vector_free (state->x0);
      gsl_vector_free (state->p);
      gsl_vector_free (state->x2);
      gsl_vector_free (state->dx1);
      gsl_vector_free (state->x1);
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
    }

  return GSL_SUCCESS;
}

static int
vector_bfgs_set (void *vstate, gsl_multimin_function_fdf * fdf,
		 const gsl_vector * x, double *f, gsl_vector * gradient,
		 double step_size, double tol)
{
  vector_bfgs_state_t *state = (vector_bfgs_state_t *) vstate;

  state->iter = 0;
  state->step = step_size;
  state->max_step = step_size;
  state->tol = tol;

  GSL_MULTIMIN_FN_EVAL_F_DF (fdf, x, f, gradient);

  /* Use the gradient as the initial direction */

  gsl_vector_memcpy (state->x0, x);
  gsl_vector_memcpy (state->p, gradient);
  gsl_vector_memcpy (state->g0, gradient);

  {
    double gnorm = gsl_blas_dnrm2 (gradient);
    state->pnorm = gnorm;
    state->g0norm = gnorm;
  }

  return GSL_SUCCESS;
}

static void
vector_bfgs_free (void *vstate)
{
  vector_bfgs_state_t *state = (vector_bfgs_state_t *) vstate;

  gsl_vector_free (state->dg0);
  gsl_vector_free (state->dx0);
  gsl_vector_free (state->g0);
  gsl_vector_free (state->x0);
  gsl_vector_free (state->p);
  gsl_vector_free (state->x2);
  gsl_vector_free (state->dx1);
  gsl_vector_free (state->x1);
}

static int
vector_bfgs_restart (void *vstate)
{
  vector_bfgs_state_t *state = (vector_bfgs_state_t *) vstate;

  state->iter = 0;
  return GSL_SUCCESS;
}

static int
vector_bfgs_iterate (void *vstate, gsl_multimin_function_fdf * fdf,
		     gsl_vector * x, double *f,
		     gsl_vector * gradient, gsl_vector * dx)
{
  vector_bfgs_state_t *state = (vector_bfgs_state_t *) vstate;

  gsl_vector *x1 = state->x1;
  gsl_vector *dx1 = state->dx1;
  gsl_vector *x2 = state->x2;
  gsl_vector *p = state->p;
  gsl_vector *g0 = state->g0;
  gsl_vector *x0 = state->x0;

  double pnorm = state->pnorm;
  double g0norm = state->g0norm;

  double fa = *f, fb, fc;
  double dir;
  double stepa = 0.0, stepb, stepc = state->step, tol = state->tol;

  double g1norm;
  double pg;

  if (pnorm == 0.0 || g0norm == 0.0)
    {
      gsl_vector_set_zero (dx);
      return GSL_ENOPROG;
    }

  /* Determine which direction is downhill, +p or -p */

  gsl_blas_ddot (p, gradient, &pg);

  dir = (pg >= 0.0) ? +1.0 : -1.0;

  /* Compute new trial point at x_c= x - step * p, where p is the
     current direction */

  take_step (x, p, stepc, dir / pnorm, x1, dx);

  /* Evaluate function and gradient at new point xc */

  fc = GSL_MULTIMIN_FN_EVAL_F (fdf, x1);

  if (fc < fa)
    {
      /* Success, reduced the function value */
      state->step = stepc * 2.0;
      *f = fc;
      gsl_vector_memcpy (x, x1);
      GSL_MULTIMIN_FN_EVAL_DF (fdf, x1, gradient);
      state->g0norm = gsl_blas_dnrm2 (gradient);
      return GSL_SUCCESS;
    }

#ifdef DEBUG
  printf ("got stepc = %g fc = %g\n", stepc, fc);
#endif

  /* Do a line minimisation in the region (xa,fa) (xc,fc) to find an
     intermediate (xb,fb) satisifying fa > fb < fc.  Choose an initial
     xb based on parabolic interpolation */

  intermediate_point (fdf, x, p, dir / pnorm, pg,
		      stepa, stepc, fa, fc, x1, dx1, gradient, &stepb, &fb);

  minimize (fdf, x, p, dir / pnorm,
	    stepa, stepb, stepc, fa, fb, fc, tol,
	    x1, dx1, x2, dx, gradient, &(state->step), f, &g1norm);

  gsl_vector_memcpy (x, x2);

  /* Choose a new conjugate direction for the next step */

  state->iter = (state->iter + 1) % x->size;

  if (state->iter == 0)
    {
      gsl_vector_memcpy (p, gradient);
      state->pnorm = g1norm;
    }
  else
    {
      /* This is the BFGS update: */
      /* p' = g1 - A dx - B dg */
      /* A = - (1+ dg.dg/dx.dg) B + dg.g/dx.dg */
      /* B = dx.g/dx.dg */

      gsl_vector *dx0 = state->dx0;
      gsl_vector *dg0 = state->dg0;

      double dxg, dgg, dxdg, dgnorm, A, B;

      /* dx0 = x - x0 */
      gsl_vector_memcpy (dx0, x);
      gsl_blas_daxpy (-1.0, x0, dx0);

      /* dg0 = g - g0 */
      gsl_vector_memcpy (dg0, gradient);
      gsl_blas_daxpy (-1.0, g0, dg0);

      gsl_blas_ddot (dx0, gradient, &dxg);
      gsl_blas_ddot (dg0, gradient, &dgg);
      gsl_blas_ddot (dx0, dg0, &dxdg);

      dgnorm = gsl_blas_dnrm2 (dg0);

      B = dxg / dxdg;
      A = -(1.0 + dgnorm * dgnorm / dxdg) * B + dgg / dxdg;

      gsl_vector_memcpy (p, gradient);
      gsl_blas_daxpy (-A, dx0, p);
      gsl_blas_daxpy (-B, dg0, p);

      state->pnorm = gsl_blas_dnrm2 (p);
    }

  gsl_vector_memcpy (g0, gradient);
  gsl_vector_memcpy (x0, x);
  state->g0norm = gsl_blas_dnrm2 (g0);

#ifdef DEBUG
  printf ("updated conjugate directions\n");
  printf ("p: ");
  gsl_vector_fprintf (stdout, p, "%g");
  printf ("g: ");
  gsl_vector_fprintf (stdout, gradient, "%g");
#endif

  return GSL_SUCCESS;
}

static const gsl_multimin_fdfminimizer_type vector_bfgs_type = {
  "vector_bfgs",		/* name */
  sizeof (vector_bfgs_state_t),
  &vector_bfgs_alloc,
  &vector_bfgs_set,
  &vector_bfgs_iterate,
  &vector_bfgs_restart,
  &vector_bfgs_free
};

const gsl_multimin_fdfminimizer_type
  * gsl_multimin_fdfminimizer_vector_bfgs = &vector_bfgs_type;