 /* 
 * spatial.cpp - Radial distribution function implentation
 * Copyright (C) 2018, D Haley
 * 
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "spatial.h"

#include "../filterCommon.h"

#include <gsl/gsl_sf_gamma.h>

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_multifit.h>

#include <qhull/qhull_a.h>

using std::vector;

#ifdef _OPENMP
#include <omp.h>
#endif


const unsigned int CALLBACK_REDUCE=5000;



//Fit data, using a sequential X
void polyFitVector(vector<float> &data, unsigned int maxDegree, vector<float> &coeffs, float &residual)
{

	ASSERT(data.size());
	//solve Xa = b, where X is the polynomial we want to fit, and 
	gsl_matrix *xMat = gsl_matrix_alloc(data.size(),maxDegree+1);
	gsl_vector *b = gsl_vector_alloc(data.size());

	for(size_t ui=0;ui<data.size();ui++)
	{
		for(size_t uj=0;uj<=maxDegree;uj++)
		{
			gsl_matrix_set(xMat,ui,uj,pow(uj,ui));
		}
		gsl_vector_set(b,ui,data[ui]);
	}

	gsl_vector *coeffVec = gsl_vector_alloc(maxDegree+1);
	gsl_matrix *covariance= gsl_matrix_alloc(maxDegree+1,maxDegree+1);
	double chiSq;

	gsl_multifit_linear_workspace *w = gsl_multifit_linear_alloc(data.size(),maxDegree+1);
	gsl_multifit_linear(xMat, b,coeffVec,covariance,&chiSq,w);

	residual=chiSq;

	ASSERT(residual > 0);


	gsl_multifit_linear_free(w);
	gsl_matrix_free(xMat);
	gsl_vector_free(b);
	gsl_vector_free(coeffVec);
	gsl_matrix_free(covariance);

}

//A bigger MAX_NN_DISTS is better because you will attempt to grab more ram
//however there is a chance that memory allocation can fail, which currently I do not grab safely
const unsigned int MAX_NN_DISTS = 0x8000000; //96 MB samples at a time 




//!Generate an NN histogram using NN-max cutoffs 
unsigned int generateNNHist( const vector<Point3D> &pointList, 
			const K3DTree &tree,unsigned int nnMax, unsigned int numBins,
		       	vector<vector<size_t> > &histogram, float *binWidth , unsigned int *progressPtr,
			ATOMIC_BOOL &wantAbort)
{
	if(pointList.size() <=nnMax)
		return RDF_ERR_INSUFFICIENT_INPUT_POINTS;
	
	//Disallow exact matching for NNs
	float deadDistSqr;
	deadDistSqr= std::numeric_limits<float>::epsilon();
	
	//calclate NNs
	BoundCube cube;
	cube.setBounds(pointList);

	//Allocate and assign the initial max distances
	float *maxSqrDist= new float[nnMax];
	for(unsigned int ui=0; ui<nnMax; ui++)
		maxSqrDist[ui] =0.0f; 
	


	int callbackReduce=CALLBACK_REDUCE;
#ifdef _OPENMP
	size_t numAnalysed=0;
	bool spin=false;
#endif

	//We need to do two passes. 
	// One, where we update the maximum distance
	//   to allow for correct sizing of the histogram and
	// Two, where we fill out the histogram

	//do NN search
#pragma omp parallel for shared(spin,numAnalysed) firstprivate(callbackReduce)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
#ifdef _OPENMP
		if(spin)
			continue;
#endif
		vector<const Point3D *> nnPoints;	
		tree.findKNearest(pointList[ui],cube,
					nnMax,nnPoints,deadDistSqr);


		
		for(unsigned int uj=0; uj<nnPoints.size(); uj++)
		{
			float temp;
			temp = nnPoints[uj]->sqrDist(pointList[ui]);	
			if(temp > maxSqrDist[uj])
				maxSqrDist[uj] = temp;
		}
			

		//Callbacks to perform UI updates as needed
		if(!(callbackReduce--))
		{
#ifdef _OPENMP 
			#pragma omp critical
			{
				numAnalysed+=CALLBACK_REDUCE;
				*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
				if(wantAbort)
					spin=true;
			}			
#else
			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*50.0f);
			if(wantAbort)
			{
				delete[] maxSqrDist;
				return RDF_ABORT_FAIL;
			}
#endif
			callbackReduce=CALLBACK_REDUCE;
		}

	}
#ifdef _OPENMP
	if(spin)
	{
		delete[] maxSqrDist;
		return RDF_ABORT_FAIL;
	}
#endif


	float maxOfMaxDists=0;
	float *maxDist=new float[nnMax];
	for(unsigned int ui=0; ui<nnMax; ui++)
	{
		if(maxOfMaxDists < maxSqrDist[ui])
			maxOfMaxDists = maxSqrDist[ui];

		//convert maxima from sqrDistance 
		//to normal =distance	
		maxDist[ui] =sqrtf(maxSqrDist[ui]);
	}	

	maxOfMaxDists=sqrtf(maxOfMaxDists);
	maxDist[nnMax-1] = maxOfMaxDists;	

	//Calculate the bin widths required to accommodate this
	//distribution
	for(unsigned int ui=0; ui<nnMax; ui++)
		binWidth[ui]= maxDist[ui]/(float)numBins;
	delete[] maxDist;
	
	//Generate histogram for what we have already
	//zero out memory
	histogram.resize(nnMax);
	for(unsigned int ui=0;ui<nnMax;ui++)
		histogram[ui].resize(numBins,0);

	//we know the bin that things will fall into now, so we can scan 
	//remaining points and place into the histogram on the fly now
	
#ifdef _OPENMP
	spin=false;
	numAnalysed=0;
#endif
	
	callbackReduce=CALLBACK_REDUCE;
#pragma omp parallel for firstprivate(callbackReduce)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
		vector<const Point3D *> nnPoints;
#ifdef _OPENMP
		if(spin)
			continue;
#endif

		tree.findKNearest(pointList[ui],cube,
					nnMax, nnPoints);

		for(unsigned int uj=0; uj<nnPoints.size(); uj++)
		{
			unsigned int offsetTemp;
			float temp;

			temp=sqrtf(nnPoints[uj]->sqrDist(pointList[ui]));
			offsetTemp = (unsigned int)(temp/binWidth[uj]);
			
			//Prevent overflow due to temp/binWidth exceeding array dimension 
			//as (temp is <= binwidth, not < binWidth)
			if(offsetTemp == numBins)
				offsetTemp--;
			ASSERT(offsetTemp < nnMax*numBins);

			(histogram[uj])[offsetTemp]++;
		}
	

		//Callbacks to perform UI updates as needed
#ifdef _OPENMP 
		if(!(callbackReduce--))
		{
		#pragma omp critical
		{
			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*50.0f + 50.0f);
			if(wantAbort)
				spin=true;
			numAnalysed+=CALLBACK_REDUCE;
		}			
			callbackReduce=CALLBACK_REDUCE;
		}	
#else
		if(!(callbackReduce--))
		{
			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*50.0f + 50.0f);
			if(wantAbort)
			{
				delete[] maxSqrDist;
				return RDF_ABORT_FAIL;
			}
			callbackReduce=CALLBACK_REDUCE;
		}
#endif
	}
	delete[] maxSqrDist;	

#ifdef _OPENMP
	if(spin)
		return RDF_ABORT_FAIL;
#endif

	return 0;
}


unsigned int generate1DAxialDistHist(const vector<Point3D> &pointList, const K3DTreeMk2 &tree,
		const Point3D &axisDir, unsigned int *histogram, float distMax, unsigned int numBins,
		unsigned int *progressPtr, ATOMIC_BOOL &wantAbort)
{
	//Check that the axis is normalised
	ASSERT(EQ_TOL(axisDir.sqrMag(),1.0f));
#ifdef DEBUG
	//Ensure that histogram is initially zeroed
	for(unsigned int ui=0;ui<numBins;ui++)
	{
		ASSERT(!histogram[ui]);
	}
#endif

	if(pointList.empty())
		return 0;


#ifdef _OPENMP
	bool spin=false;
#endif
	
	//Main r-max searching routine
	const unsigned int CALLBACK_REDUCE_VAL=CALLBACK_REDUCE/100;
	unsigned int callbackReduce=CALLBACK_REDUCE_VAL;
	unsigned int numAnalysed=0;
#pragma omp parallel for firstprivate(callbackReduce) shared(numAnalysed)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
#ifdef _OPENMP
		if(spin)
			continue;
#endif

		//Go through each point and grab all neighbours within distMax
		// - this does not alter the tree itself
		vector<size_t> nnPts;
		tree.ptsInSphere(pointList[ui],distMax,nnPts);

		for(unsigned int uj=0;uj<nnPts.size();uj++)
		{
			Point3D deltaVec;
			deltaVec=tree.getPtRef(nnPts[uj])-pointList[ui];
			//Disallow self (or overlapped) matching
			if(deltaVec.sqrMag() < std::numeric_limits<float>::epsilon())
				continue;
			//Compute the projection of the point onto the axis of the
			// primary analysis direction
			float distance;
			distance=deltaVec.dotProd(axisDir);
		
			//update the histogram with the new position.
			// centre of the distribution function lies at the analysis point,
			// and can be either negative or positive. 
			// Shift the zero to the center of the histogram
			int offset=(int)(((0.5f*distance)/distMax+0.5f)*(float)numBins);
			if(offset < (int)numBins && offset >=0)
			{
#pragma omp critical
				histogram[offset]++;
			}

		}
		
		
		//Run callbacks as needed
		if(!(callbackReduce--))
		{
#pragma omp critical
			{
			numAnalysed+=CALLBACK_REDUCE_VAL;
			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
			if(wantAbort)
			{
#ifdef _OPENMP
				spin=true;
#else
				return RDF_ABORT_FAIL;
#endif
			}
			}
			callbackReduce=CALLBACK_REDUCE_VAL;
		}

	}
#ifdef _OPENMP
	if(spin)
		return RDF_ABORT_FAIL;
#endif
	*progressPtr=100;
	return 0;
}


unsigned int generate1DAxialNNHist(const vector<Point3D> &pointList, const K3DTreeMk2 &tree,
		const Point3D &axisDir, unsigned int *histogram, float &binWidth, unsigned int nnMax, unsigned int numBins,
		unsigned int *progressPtr, ATOMIC_BOOL &wantAbort)
{
#ifdef DEBUG
	for(unsigned int ui=0;ui<numBins;ui++)
	{
		ASSERT(!histogram[ui]);
	}
#endif

	//axis needs to be normalised, or calculation is out
	ASSERT(EQ_TOL(axisDir.sqrMag(),1.0f));

	if(pointList.size() <=nnMax)
		return RDF_ERR_INSUFFICIENT_INPUT_POINTS;
	
	//calculate NNs
	BoundCube cube;
	cube.setBounds(pointList);

	//Allocate and assign the initial max distance
	float maxAxialDist=0.0f;
	

	int callbackReduce=CALLBACK_REDUCE;
#ifdef _OPENMP
	size_t numAnalysed=0;
	bool spin=false;
#endif
	//do NN search - first pass we are only looking for the maximum distance
	// for the distribution. We do not update the histogram
	//------
#pragma omp parallel for shared(spin,numAnalysed) firstprivate(callbackReduce)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
#ifdef _OPENMP
		if(spin)
			continue;
#endif

		//Loop through this point's nns
		set<size_t> nnPoints;
		size_t nnGood;
		nnGood=0;
		while(nnGood < nnMax)
		{	
			size_t nn;
			nn=tree.findNearestWithSkip(pointList[ui],cube,nnPoints);

			if(nn == (size_t)-1)
				continue;
			
			//remember this point so we don't re-match it
			nnPoints.insert(nn);

			//compute upper bound for plot output distance
			float temp;
			temp=fabs((tree.getPtRef(nn)-pointList[ui]).dotProd(axisDir));

			if(temp < std::numeric_limits<float>::epsilon())
				continue;
			
			//update max distance
			maxAxialDist=std::max(maxAxialDist,temp);


			nnGood++;
		}
			

		//Callbacks to perform UI updates as needed
		if(!(callbackReduce--))
		{
#ifdef _OPENMP 
			#pragma omp critical
			{
				numAnalysed+=CALLBACK_REDUCE;
				*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
				if(wantAbort)
					spin=true;
			}			
#else
			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
			if(wantAbort)
				return RDF_ABORT_FAIL;
#endif
			callbackReduce=CALLBACK_REDUCE;
		}

	}
#ifdef _OPENMP
	if(spin)
		return RDF_ABORT_FAIL;
#endif



	//Calculate the bin widths required to accommodate this
	//distribution
	for(unsigned int ui=0; ui<nnMax; ui++)
		binWidth= maxAxialDist/(float)numBins;
	//------
	

	//we know the bin that things will fall into now, so we can scan 
	//points for their distance values
	// and place into the histogram 
	//----------------------	
#ifdef _OPENMP
	spin=false;
	numAnalysed=0;
#endif
	
	callbackReduce=CALLBACK_REDUCE;
#pragma omp parallel for firstprivate(callbackReduce)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
#ifdef _OPENMP
		if(spin)
			continue;
#endif

		

		set<size_t> nns;
		size_t nnGood;
		nnGood=0;
		while(nnGood < nnMax)
		{
		
			size_t nn;
			nn = tree.findNearestWithSkip(pointList[ui],cube,nns);
			if(nn == (size_t)-1)
				continue;
			
			//remember point so we don't re-match	
			nns.insert(nn);

			Point3D deltaVec;
			deltaVec=tree.getPtRef(nn)-pointList[ui];

			//Disallow self (or overlapped) matching
			if(deltaVec.sqrMag() < std::numeric_limits<float>::epsilon())
				continue;

			//Find the axial distance
			float temp;
			temp=deltaVec.dotProd(axisDir);
			//Convert to position in array
			int offset=(int)(((0.5f*temp)/maxAxialDist+0.5f)*numBins);

			if(offset < numBins && offset >=0)	
			{
				//TODO: OpenMP could use multiple histograms
				// rather than locking
				//update histogram
#pragma omp critical 
				histogram[offset]++;
			}

			nnGood++;	
		}
	

		//Callbacks to check for abort as needed
		if(!(callbackReduce--))
		{
#ifdef _OPENMP 
			#pragma omp critical
			{
			*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
			if(wantAbort)
				spin=true;
			numAnalysed+=CALLBACK_REDUCE;
			}			
#else
			*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
			if(wantAbort)
				return RDF_ABORT_FAIL;
#endif
			callbackReduce=CALLBACK_REDUCE;
		}
	}

#ifdef _OPENMP
	if(spin)
		return RDF_ABORT_FAIL;
#endif

	*progressPtr=100;

	return 0;
}


//!Generate an NN histogram using distance max cutoffs. Input histogram must be zeroed,
unsigned int generateDistHist(const vector<Point3D> &pointList, const K3DTree &tree,
			unsigned int *histogram, float distMax,
			unsigned int numBins, unsigned int &warnBiasCount,
			unsigned int *progressPtr,ATOMIC_BOOL &wantAbort)
{


#ifdef DEBUG
	for(unsigned int ui=0;ui<numBins;ui++)
	{
		ASSERT(!histogram[ui]);
	}
#endif

	if(pointList.empty())
		return 0;

	BoundCube cube;
	cube.setBounds(pointList);
	//We dont know how much ram we will need
	//one could estimate an upper bound by 
	//tree.numverticies*pointlist.size()
	//but i dont have  a tree.numvertcies
	float maxSqrDist = distMax*distMax;

	warnBiasCount=0;
	
	//Main r-max searching routine
	unsigned int callbackReduce=CALLBACK_REDUCE;
#ifdef _OPENMP
	bool spin=false;
	size_t numAnalysed=0;
	size_t numAnalysedThread=0;

	unsigned int threadHist[numBins][omp_get_max_threads()];
	for(size_t ui=0; ui<omp_get_max_threads(); ui++)
	{
		for (size_t uj = 0; uj < numBins; uj++)
			threadHist[uj][ui] = 0;
	}
#endif

#pragma omp parallel for shared(spin,histogram,numAnalysed,threadHist) firstprivate(callbackReduce,numAnalysedThread) default(shared)
	for(unsigned int ui=0; ui<pointList.size(); ui++)
	{
#ifdef _OPENMP
		if(spin)
			continue;
#endif

		float sqrDist,deadDistSqr;
		Point3D sourcePoint;
		const Point3D *nearPt=0;
		//Go through each point and grab up to the maximum distance
		//that we need
		
		//Loop from this ion, up to its max	
		//disable exact matching, by requiring d^2 > epsilon
		deadDistSqr=std::numeric_limits<float>::epsilon();
		sqrDist=0;
		sourcePoint=pointList[ui];
		while(deadDistSqr < maxSqrDist)
		{

			//Grab the nearest point
			nearPt = tree.findNearest(sourcePoint, cube,
							 deadDistSqr);

			if(nearPt)
			{
				//Cacluate the sq of the distance to the point
				sqrDist = nearPt->sqrDist(sourcePoint);
				
				//if sqrDist is = maxSqrdist then this will cause
				//the histogram indexing to trash alternate memory
				//- this is bad - prevent this please.	
				if(sqrDist < maxSqrDist)
				{
					//Add the point to the histogram
#ifdef _OPENMP
					threadHist[(size_t) ((sqrtf(sqrDist/maxSqrDist)*(float)numBins))] [omp_get_thread_num()]++;
#else
					histogram[(size_t)((sqrtf(sqrDist/maxSqrDist)*(float)numBins))]++;
#endif
				}

				//increase the dead distance to the last distance
				deadDistSqr = sqrDist+std::numeric_limits<float>::epsilon();
			}
			else
			{		
				//Oh no, we had a problem, somehow we couldn't find enough
#pragma omp critical
				warnBiasCount++;
				break;
			}


			if(!(callbackReduce--))
			{
#ifdef _OPENMP 
				#pragma omp critical
				{
					numAnalysed+=numAnalysedThread;
					*progressPtr= (unsigned int)((float)(numAnalysed)/((float)pointList.size())*100.0f);
					if(wantAbort)
						spin=true;
				}
				if(spin)
					break;
				numAnalysedThread=0;
#else
				*progressPtr= (unsigned int)((float)(ui)/((float)pointList.size())*100.0f);
				if(wantAbort)
					return RDF_ABORT_FAIL;
#endif
				callbackReduce=CALLBACK_REDUCE;
			}
		}
#ifdef _OPENMP
		numAnalysedThread++;
#endif
	}

#ifdef _OPENMP
	if(spin)
		return RDF_ABORT_FAIL;
    
	for (size_t i = 0; i < numBins; i++)
	{
		for (size_t j = 0; j < omp_get_max_threads(); j++)
			histogram[i] += threadHist[i][j];
	}
#endif

	//Calculations complete!
	*progressPtr=100;

	return 0;
}



void generateKnnTheoreticalDist(const std::vector<float> &radii, float density, unsigned int nn,
					std::vector<float> &nnDist)
{
	ASSERT(density >=0);
	ASSERT(nn>0);
	//Reference :Stephenson, 2009, simplified using D=3

	//Distribution requires evaluation of :
	//- the gamma function gamma(5/2)
	const float GAMMA_2PT5 = 1.32934038817914;
	// - power of pi : pi^(D/2)
	const float PI_POW_D_2 = pow(M_PI,3.0/2.0);
	
	const float LAMBDA_FACTOR =  PI_POW_D_2/GAMMA_2PT5;

	double pBase,lambda;

	//radius independent component
	lambda = density*LAMBDA_FACTOR;
	pBase = 3.0/gsl_sf_fact(nn-1);
	pBase*= pow(lambda,nn);
	

	nnDist.resize(radii.size());
	for(size_t ui=0;ui<radii.size();ui++)
	{
		double r,p;
		p=pBase;
		r =radii[ui];	
		p*= pow(r,3*nn-1);
		p*=exp(-lambda*r*r*r);
		nnDist[ui]=p;
	}

}

unsigned int generate1DAxialDistHistSweep(const std::vector<Point3D> &pointList, K3DTreeMk2 &tree,
		float distMax, unsigned int &prog, ATOMIC_BOOL &wantAbort,
		std::vector<std::vector<std::vector<unsigned int> > > &histogram, float thetaStart,
		float thetaStop,float phiStart,float phiStop)
{
	//The histogram should be pre-sized
	ASSERT(histogram.size());

	//zero out histogram
	for(size_t ui=0;ui<histogram.size(); ui++)
	{
		for(size_t uj=0;uj<histogram[ui].size();uj++)
		{
			std::fill(histogram[ui][uj].begin(),histogram[ui][uj].end(),0);
		}
	}

	if(pointList.empty())
		return 0;

	BoundCube cube;
	cube.setBounds(pointList);

	float maxSqrDist = distMax*distMax;

	const size_t NUM_THETA=histogram.size();
	const size_t NUM_PHI=histogram[0].size();
	const size_t DIST_BINS=histogram[0][0].size();


	const float dTheta= (thetaStop-thetaStart)/NUM_THETA;
	const float dPhi= (phiStop-phiStart)/NUM_PHI;

	Point3D **axisDir= new Point3D*[NUM_THETA];
	for(size_t ui=0;ui<NUM_THETA;ui++)
		axisDir[ui] = new Point3D[NUM_PHI];
	//Construct a ball of unit vectors, facing outwards
	for(size_t ui=0; ui<NUM_THETA; ui++)
	{
		for(size_t uj=0; uj<NUM_PHI; uj++)
		{
			float tmpPhi,tmpTheta;
			tmpTheta= dTheta*ui+thetaStart;
			tmpPhi = dPhi*uj+phiStart;
			ASSERT(tmpTheta>=0.0f && tmpTheta<=1.05f*M_PI);
			ASSERT(tmpPhi>=0&& tmpPhi<=2.1f*M_PI);
			axisDir[ui][uj].setISOSpherical(tmpTheta,tmpPhi,1.0f);

		}
	}

	
	//Main r-max searching routine

	//Abort variable
	bool spin=false;
//construct worker threads
	#pragma omp parallel shared(histogram)
	for(unsigned int uSrcPt=0; uSrcPt<pointList.size(); uSrcPt++)
	{
		if(spin)
			continue;

		Point3D sourcePoint;
		//Go through each point and grab up to the maximum distance
		//that we need

		vector<size_t> inSphereIdx;

		sourcePoint=pointList[uSrcPt];

		//Grab the nearest point. Will tag on completion
		tree.ptsInSphere(sourcePoint, distMax, inSphereIdx);



		//Loop through the points within the search radius and
		// update the tag radius
		#pragma omp for 
		for(size_t uPt=0; uPt<inSphereIdx.size(); uPt++)
		{
			float sqrDist;
			ASSERT(inSphereIdx[uPt] < tree.size());
			const Point3D &nearPt=tree.getPtRef(inSphereIdx[uPt]);

			//Calculate the sq of the distance to the point
			sqrDist = nearPt.sqrDist(sourcePoint);

			if(sqrDist == 0.0f)
				continue;

			//if sqrDist is = maxSqrdist then this will cause
			//the histogram indexing to trash alternate memory
			//- this is bad - prevent this please.
			if(sqrDist < maxSqrDist)
			{
				Point3D deltaPt;
				deltaPt=nearPt-sourcePoint;
				//Compute the SDM fit for all angular space
				for(size_t ui=0; ui<NUM_THETA; ui++)
				{
					for(size_t uj=0; uj<NUM_PHI; uj++)
					{

						//Compute the projection of
						// the point onto the axis of the
						// primary analysis direction
						float distance;
						distance=deltaPt.dotProd(axisDir[ui][uj]);
						int offset;

						offset=(int)(((0.5f*distance)/distMax+0.5f)*(float)DIST_BINS);
						if(offset >=0 && offset < (int)DIST_BINS)
						{
							//FIXME: We don't really want a critical
							// section here. Accumulate histograms
							// to avoid doing this
							#pragma omp critical
							histogram[ui][uj][offset]++;
						}
					}
				}
			}
		}
		prog = ((float)uSrcPt/(float)pointList.size()*100.0f);

		if(wantAbort)
			spin=true;
	}
	
	for(size_t ui=0;ui<NUM_THETA;ui++)
		delete[] axisDir[ui]; 

	delete[] axisDir;

	if(spin)
		return DF_ABORT_FAIL;
	return 0;
}

void normaliseVector(const vector<unsigned int > &f, vector<float> &r, float &normFactor)
{
	r.resize(f.size());
	//TODO: Use running mean algorithm
	double sum=0;
	for(size_t ui=0;ui<f.size();ui++)
		sum+=f[ui];

	if(sum < std::numeric_limits<float>::epsilon())
	{
		std::fill(r.begin(),r.end(),0);
		return;
	}
	for(size_t ui=0;ui<f.size();ui++)
		r[ui] = f[ui]/sum;

	normFactor=sum;
}

void errorFitAxialDistArray(const std::vector<std::vector<std::vector<unsigned int> > > &histogram,
		Array2D<float> &errorArray)
{

	if(!histogram.size())
		return;

	errorArray.resize(histogram.size(),histogram[0].size());
	
	//These two matrices are computed, but not used outside this
	// function
	//FIXME: There is a problem with Array2D. This doesn't work as it should
	Array2D<float> normFactorMatrix;
	Array2D<vector<float> > normMatrix;
	normMatrix.resize(histogram.size(),histogram[0].size());
	normFactorMatrix.resize(histogram.size(),histogram[0].size());
	
	//Go through each dataset, normalise it, and fit the DF
	for(unsigned int ui=0;ui<histogram.size();ui++)
	{
		for(unsigned int uj=0;uj<histogram[ui].size();uj++)
		{
			normaliseVector(histogram[ui][uj],
				normMatrix.getRef(ui,uj),normFactorMatrix.getRef(ui,uj));
		
			vector<float> polyCoeffs;
			float fitError;
			polyFitVector(normMatrix.getRef(ui,uj),2,polyCoeffs,fitError);
			errorArray.set(ui,uj,fitError);
		}
	}

}

bool qhullTest()
{
#if defined(__WIN64) && !defined(HAVE_SWEEP_HULL)
	//If using a cross-compile (at least)
	// qhull under win64 must use long long, or we get random crashes
	// The definition is set in qhull/mem.h
	COMPILE_ASSERT(sizeof(ptr_intT) == sizeof(long long))
#endif

	return true;
}

