void direct_method_complex_xp
(
        unsigned int NParticles,
        _FLOAT_ particles[][2], 
	_FLOAT_ charges[][2],
	_FLOAT_ dipoleMoments[][2],
        unsigned int NTargets,
	_FLOAT_ targets[][2],
	_FLOAT_ pot[][2],
	_FLOAT_ grad[][2],
        double beta, 
        double *time,
        char **errorMessage
)
{
	int i;
        if (pot&&grad) {
                if (dipoleMoments) {
			for (i=0; i<NTargets; i++) {
        			exactDipoleGradient(NParticles, particles, charges, dipoleMoments, targets, i, pot[i], grad[i]); 
			}
                }
                else if(charges) {
			for (i=0; i<NTargets; i++) {
        			 exactGradient(NParticles, particles, charges, targets, i, pot[i], grad[i]); 
			}
                }
                else {
                        *errorMessage = "charges and dipoleMoments must not both be NULL.";
                        return;
                }
        }
        else if (pot) {
                if (dipoleMoments) {
			for (i=0; i<NTargets; i++) {
        			exactDipolePotential(NParticles, particles, charges, dipoleMoments, targets, i, pot[i]); 
			}
                }
                else if(charges) {
			for (i=0; i<NTargets; i++) {
        			exactPotential(NParticles, particles, charges, targets, i, pot[i]); 
			}
                }
                else {
                        *errorMessage = "charges and dipoleMoments must not both be NULL.";
                        return;
                }
        }
        else {
                *errorMessage = "pot must not be NULL.";
                return;
        }
	*errorMessage = 0;
}		
int main ( int argc, char* argv[] )
{
	const bool verbose = false;
	const int procRank = 0;
	const int numProcs = 1;
	const OutputMessage::priority loggingLevel = OutputMessage::debugPriority;
	
	Logger* exeLog = Logger::Instance(loggingLevel, procRank, numProcs);
	
	const double maxR = 1.0;
	
	double nInv[7];
	double interpMaxErr[7];
	double interpAvgErr[7];
	double gradMaxErr[7];
	double gradAvgErr[7];
	double lapMaxErr[7];
	double lapAvgErr[7];
	
	
	const int scalarDim = 1;
	const int vectorDim = 2;
	
	std::stringstream ss;
	std::string s;
	
	for ( int iNest = 1; iNest < 8; ++iNest )
	{
		//
		// construct a mesh to use for testing
		//
		PolyMesh2d pMesh( iNest, PolyMesh2d::triHexSeed, maxR, procRank, numProcs);
		std::cout << "iNest = " << iNest << ", nParticles = " << pMesh.nParticles() << ", nFaces = " << pMesh.nFaces() << std::endl;
		
		if ( verbose )
		{
			LongMessage meshStatus("planar mesh built", OutputMessage::tracePriority, "main", pMesh.getInfo() );
			exeLog->logMessage(meshStatus);
		}
		
		nInv[iNest-1] = 1.0/pMesh.nFaces();
		
		//
		// construct scalar field to use for testing
		//
		Field gaussScalar("Gaussian", "n/a", scalarDim, pMesh.nParticles() );
		Field exactGradient( "grad Gaussian", "n/a", vectorDim, pMesh.nParticles() );
		Field exactLaplacian( "laplacian Gaussian", "n/a", scalarDim, pMesh.nParticles() );
		Particles* pp = pMesh.getParticles();
		const double b = 8.0;
		for (int i = 0; i < pMesh.nParticles(); ++i)
		{
			double x = pp->x[i];
			double y = pp->y[i];
			double expPart = exp(- b*b * ( x*x + y*y ) );
			gaussScalar.insertScalarToField( i, expPart );
			
			exactGradient.insertVectorToField( i, -2.0 * b * b * x * expPart, - 2.0 * b * b * y * expPart );
			
			exactLaplacian.insertScalarToField(i, 4.0 * b * b * expPart * (-1.0 + b*b * ( x*x + y*y )) );
		}
		
		std::cout << "exact Fields defined... " << std::endl;
		
		ss.str(s);
		ss << "gaussScalar_nest_" << iNest << ".m";
		gaussScalar.outputForMatlab( ss.str(), *pp );
		
		std::cout << "source data output complete... " << std::endl;
		
		//
		// initialize interpolation
		//
		TriCubicHermite gaussScalarInterp( &pMesh, &gaussScalar);
		//
		// compute gradient estimates for Hermite interpolation, set interpolation coefficients
		//
		const double w1 = 2.0;
		const double w2 = 1.0;
		Field gaussGrad = gaussScalarInterp.estimateScalarGradientAtVertices(w1, w2);
		gaussScalarInterp.findAllCoefficientsScalar( gaussGrad );
		
		std::cout << "interpolation coefficients set... " << std::endl;
		//
		// interpolate
		//
		Field interpolatedScalar("GaussInterp", "n/a", scalarDim, pMesh.nParticles() );
		Field interpolatedGradient("GaussInterpGrad", "n/a", vectorDim, pMesh.nParticles() );
		Field interpolatedLaplacian("GaussInterpLaplacian", "n/a", scalarDim, pMesh.nParticles() );
		
		for ( int i = 0; i < pMesh.nParticles(); ++i)
		{
			xyzVector loc( pp->x[i], pp->y[i] );
			
			interpolatedScalar.insertScalarToField( i, gaussScalarInterp.interpolateScalar( loc ) );			
			
			interpolatedGradient.insertVectorToField(i, gaussScalarInterp.scalarGradient( loc ) );
			
			interpolatedLaplacian.insertScalarToField( i, gaussScalarInterp.scalarLaplacian( loc ) );
		}
		std::cout << "interpolation complete... calculating error ..." << std::endl;
		
		Field interpError("interpolation error", "n/a", scalarDim, pMesh.nParticles() );
		Field gradError( "gradient estimation error", "n/a", vectorDim, pMesh.nParticles() );
		Field lapError("laplacian error", "n/a", scalarDim, pMesh.nParticles() );
		
		interpMaxErr[iNest-1] = 0.0;
		interpAvgErr[iNest-1] = 0.0;
		gradMaxErr[iNest-1] = 0.0;
		gradAvgErr[iNest-1] = 0.0;
		lapMaxErr[iNest-1] = 0.0;
		lapAvgErr[iNest-1] = 0.0;
		double interpDenom = 0.0;
		double gradDenom = 0.0;
		double lapDenom = 0.0;
		double sMax = 0.0;
		double gMax = 0.0;
		double lMax = 0.0;
		for ( int i = 0; i < pMesh.nParticles(); ++i)
		{
			interpError.scalar[i] = interpolatedScalar.scalar[i] - gaussScalar.scalar[i]; 
			double errI = std::abs(interpError.scalar[i]);
			interpAvgErr[iNest-1] += errI * errI * pp->area[i];
			interpDenom += gaussScalar.scalar[i] * gaussScalar.scalar[i]  * pp->area[i];
			if ( gaussScalar.scalar[i] > sMax )
				sMax = gaussScalar.scalar[i];
			if (  errI > interpMaxErr[iNest-1] )
				interpMaxErr[iNest-1] = errI;
			
			gradError.xComp[i] = interpolatedGradient.xComp[i] - exactGradient.xComp[i];
			gradError.yComp[i] = interpolatedGradient.yComp[i] - exactGradient.yComp[i];
			double errG = std::sqrt( gradError.xComp[i]*gradError.xComp[i] + gradError.yComp[i]*gradError.yComp[i] );
			gradAvgErr[iNest-1] += errG * errG * pp->area[i];
			gradDenom += ( exactGradient.xComp[i]*exactGradient.xComp[i] + exactGradient.yComp[i]*exactGradient.yComp[i]) * pp->area[i];
			if ( std::sqrt( exactGradient.xComp[i]*exactGradient.xComp[i] + exactGradient.yComp[i]*exactGradient.yComp[i]) > gMax )
				gMax = std::sqrt( exactGradient.xComp[i]*exactGradient.xComp[i] + exactGradient.yComp[i]*exactGradient.yComp[i]);
			if ( errG > gradMaxErr[iNest-1] )
				gradMaxErr[iNest-1] = errG;
			
			lapError.scalar[i] = interpolatedLaplacian.scalar[i] - exactLaplacian.scalar[i];
			double errL = std::abs( lapError.scalar[i] );
			lapAvgErr[iNest-1] += errL * errL * pp->area[i];
			lapDenom += exactLaplacian.scalar[i] * exactLaplacian.scalar[i] * pp->area[i];
			if ( exactLaplacian.scalar[i] > lMax )
				lMax = exactLaplacian.scalar[i];
			if ( errL > lapMaxErr[iNest-1] )
				lapMaxErr[iNest-1] = errL;
		}
		interpMaxErr[iNest-1] /= sMax;
		gradMaxErr[iNest-1] /= gMax;
		lapMaxErr[iNest-1] /= lMax;
		interpAvgErr[iNest-1] /= interpDenom;
		gradAvgErr[iNest-1] /= gradDenom;
		lapAvgErr[iNest-1] /= lapDenom;
		
		interpAvgErr[iNest-1] = std::sqrt( interpAvgErr[iNest-1] );
		gradAvgErr[iNest-1] = std::sqrt( gradAvgErr[iNest-1] );
		lapAvgErr[iNest-1] = std::sqrt( lapAvgErr[iNest-1] );
		
		std::cout << "error complete ..." << std::endl;
		
		
		
		
	}
	
	std::cout << "--- 1/N --- " << " --- interp max err ---- " << " --- interp avg err ---- " 
								<< " --- grad max err --- " << " --- grad avg err --- "
								<< " --- lap max err --- " << " --- lap avg err --- " << std::endl;
	for ( int i = 0; i < 7; ++i )
		std::cout << nInv[i] << " , "
				  << interpMaxErr[i] << " , "
				  << interpAvgErr[i] << " , "
				  << gradMaxErr[i] << " , "
				  << gradAvgErr[i] << " , "
				  << lapMaxErr[i] << " , "
				  << lapAvgErr[i] << std::endl;


return 0;
};