void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    char cmd[64];
    metricTreeCPP *theTree;
    
    if(nrhs>4) {
        mexErrMsgTxt("Too many inputs.");   
    }
    
    //Get the command string that is passed.
    mxGetString(prhs[0], cmd, sizeof(cmd));
    
    //prhs[0] is assumed to be the string telling
    if(!strcmp("metricTreeCPP", cmd)){
        size_t k, N;
        mxArray *retPtr;
        
        k=getSizeTFromMatlab(prhs[1]);
        N=getSizeTFromMatlab(prhs[2]);
        
        theTree =  new metricTreeCPP(k,N);
        
        //Convert the pointer to a Matlab matrix to return.
        retPtr=ptr2Matlab<metricTreeCPP*>(theTree);
        
        //Lock this mex file so that it can not be cleared until the object
        //has been deleted (This avoids a memory leak).
        mexLock();
        //Return the pointer to the tree
        plhs[0]=retPtr;
    } else if(!strcmp("buildTreeFromBatch",cmd)) {
        double *dataBatch;
        
        //Get the pointer back from Matlab.
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);   
        
        checkRealDoubleArray(prhs[2]);
        dataBatch=reinterpret_cast<double*>(mxGetData(prhs[2]));
        
        theTree->buildTreeFromBatch(dataBatch);
    } else if(!strcmp("searchRadius",cmd)) {
        size_t numPoints;
        ClusterSetCPP<size_t> pointClust;
        ClusterSetCPP<double> distClust;
        double *point;
        double *radius;
        mxArray *clustParams[3];
        
        //Get the inputs
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);
        checkRealDoubleArray(prhs[2]);
        checkRealDoubleArray(prhs[3]);
        point=reinterpret_cast<double*>(mxGetData(prhs[2]));
        radius=reinterpret_cast<double*>(mxGetData(prhs[3]));
        
        numPoints=mxGetN(prhs[2]);
        if(mxGetM(prhs[2])!=theTree->k){
            mexErrMsgTxt("Invalid point size passed.");
        }
        
        //Run the search; rangeCluster now contains the results.
        theTree->searchRadius(pointClust,distClust,point,radius, numPoints);
        
        //Put the results into an instance of the ClusterSet container 
        //class in Matlab.
        clustParams[0]=unsignedSizeMat2Matlab(pointClust.clusterEls,pointClust.totalNumEl,1);
        clustParams[1]=unsignedSizeMat2Matlab(pointClust.clusterSizes,pointClust.numClust,1);
        clustParams[2]=unsignedSizeMat2Matlab(pointClust.offsetArray,pointClust.numClust,1);
        
        //Return a ClusterSet containing the appropriate data.
        mexCallMATLAB(1, &(plhs[0]), 3,  clustParams, "ClusterSet");
        
        //If the distances should also be returned.
        if(nlhs>1) {
            clustParams[0]=doubleMat2Matlab(distClust.clusterEls,distClust.totalNumEl,1);
            clustParams[1]=unsignedSizeMat2Matlab(distClust.clusterSizes,distClust.numClust,1);
            clustParams[2]=unsignedSizeMat2Matlab(distClust.offsetArray,distClust.numClust,1);
        
            //Return a ClusterSet containing the appropriate data.
            mexCallMATLAB(1, &(plhs[1]), 3,  clustParams, "ClusterSet");
        }
    } else if(!strcmp("~metricTreeCPP", cmd)){
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);

        delete theTree;
        //Unlock the mex file allowing it to be cleared.
        mexUnlock();
    } else if(!strcmp("getAllData", cmd)){
        size_t N;
        size_t k;
        
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);
        N=theTree->N;
        k=theTree->k;
        
        plhs[0]=unsignedSizeMat2Matlab(theTree->DATAIDX,N, 1);
        if(nlhs>1) {
            plhs[1]=signedSizeMat2Matlab(theTree->innerChild,N, 1);
            if(nlhs>2) {
                plhs[2]=signedSizeMat2Matlab(theTree->outerChild,N, 1);
                if(nlhs>3) {
                    plhs[3]=doubleMat2Matlab(theTree->innerRadii,N, 1);
                    if(nlhs>4) {
                        plhs[4]=doubleMat2Matlab(theTree->outerRadii,N, 1);
                        if(nlhs>5) {
                            plhs[5]=doubleMat2Matlab(theTree->data,k, N);
                        }
                    }
                }
            }   
        }
    } else if(!strcmp("getN", cmd)) {
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);
        plhs[0]=unsignedSizeMat2Matlab(&(theTree->N),1,1);
    } else if(!strcmp("getk", cmd)) {
        theTree=Matlab2Ptr<metricTreeCPP*>(prhs[1]);
        plhs[0]=unsignedSizeMat2Matlab(&(theTree->k),1,1);
    }else {
        mexErrMsgTxt("Invalid string passed to metricTreeCPPInt.");
    }
}
void mexFunction(const int nlhs, mxArray *plhs[], const int nrhs, const mxArray *prhs[]) {
    double u, scalFactor;
    ClusterSetCPP<double> HBar;
    ClusterSetCPP<double> dHBardu;//The first derivatives
    ClusterSetCPP<double> d2HBardu2;//The second derivatives
    size_t M, numH, i;
    mxArray *CSRetVal;
    mxArray *clusterElsMATLAB,*clusterSizesMATLAB, *offsetArrayMATLAB;
    
    if(nrhs!=3){
        mexErrMsgTxt("Incorrect number of inputs.");
        return;
    }

    u=getDoubleFromMatlab(prhs[0]);
    M=getSizeTFromMatlab(prhs[1]);
    scalFactor=getDoubleFromMatlab(prhs[2]);
    
    if(M<3) {
       mexErrMsgTxt("The maximum order should be at least 3.");
       return; 
    }
    
    numH=(M+1)*(M+2)/2;
    
    //Allocate space for the results.
    clusterElsMATLAB=mxCreateDoubleMatrix(numH,1,mxREAL);
    clusterSizesMATLAB=allocUnsignedSizeMatInMatlab(M+1,1);
    offsetArrayMATLAB=allocUnsignedSizeMatInMatlab(M+1,1);
    
    HBar.numClust=M+1;
    HBar.totalNumEl=numH;
    HBar.clusterEls=reinterpret_cast<double*>(mxGetData(clusterElsMATLAB));
    HBar.offsetArray=reinterpret_cast<size_t*>(mxGetData(offsetArrayMATLAB));
    HBar.clusterSizes=reinterpret_cast<size_t*>(mxGetData(clusterSizesMATLAB));
    
    //Initialize the offset array and cluster sizes.
    HBar.offsetArray[0]=0;
    HBar.clusterSizes[0]=1;
    for(i=1;i<=M;i++){
        HBar.clusterSizes[i]=i+1;
        HBar.offsetArray[i]=HBar.offsetArray[i-1]+HBar.clusterSizes[i-1];
    }
    
    normHelmHoltzCPP(HBar,u,scalFactor);
    
    //Set the first return value
    mexCallMATLAB(1,&CSRetVal,0, 0, "ClusterSet");
    mxSetProperty(CSRetVal,0,"clusterEls",clusterElsMATLAB);
    mxSetProperty(CSRetVal,0,"clusterSizes",clusterSizesMATLAB);
    mxSetProperty(CSRetVal,0,"offsetArray",offsetArrayMATLAB);
    
    plhs[0]=CSRetVal;
    
    if(nlhs>1) {//Compute the first derivatives, if they are desired.
        mxArray *clusterEls1stDerivMATLAB=mxCreateDoubleMatrix(numH,1,mxREAL);
        
        dHBardu.numClust=M+1;
        dHBardu.totalNumEl=numH;
        dHBardu.clusterEls=reinterpret_cast<double*>(mxGetData(clusterEls1stDerivMATLAB));
        dHBardu.offsetArray=reinterpret_cast<size_t*>(mxGetData(offsetArrayMATLAB));
        dHBardu.clusterSizes=reinterpret_cast<size_t*>(mxGetData(clusterSizesMATLAB));
        
        normHelmHoltzDerivCPP(dHBardu,HBar);
        //Set the second return value
        mexCallMATLAB(1,&CSRetVal,0, 0, "ClusterSet");
        mxSetProperty(CSRetVal,0,"clusterEls",clusterEls1stDerivMATLAB);
        mxSetProperty(CSRetVal,0,"clusterSizes",clusterSizesMATLAB);
        mxSetProperty(CSRetVal,0,"offsetArray",offsetArrayMATLAB);

        plhs[1]=CSRetVal;
        mxDestroyArray(clusterEls1stDerivMATLAB);
    }
    
    if(nlhs>2) {//Compute the second derivatives if they are desired.
        mxArray *clusterEls2ndDerivMATLAB=mxCreateDoubleMatrix(numH,1,mxREAL);
        
        d2HBardu2.numClust=M+1;
        d2HBardu2.totalNumEl=numH;
        d2HBardu2.clusterEls=reinterpret_cast<double*>(mxGetData(clusterEls2ndDerivMATLAB));
        d2HBardu2.offsetArray=reinterpret_cast<size_t*>(mxGetData(offsetArrayMATLAB));
        d2HBardu2.clusterSizes=reinterpret_cast<size_t*>(mxGetData(clusterSizesMATLAB));
        
        normHelmHoltzDeriv2CPP(d2HBardu2,HBar);
        
        //Set the third return value
        mexCallMATLAB(1,&CSRetVal,0, 0, "ClusterSet");
        mxSetProperty(CSRetVal,0,"clusterEls",clusterEls2ndDerivMATLAB);
        mxSetProperty(CSRetVal,0,"clusterSizes",clusterSizesMATLAB);
        mxSetProperty(CSRetVal,0,"offsetArray",offsetArrayMATLAB);

        plhs[2]=CSRetVal;
        mxDestroyArray(clusterEls2ndDerivMATLAB);
    }
    
    //Free the buffers. The mxSetProperty command copied the data.
    mxDestroyArray(clusterElsMATLAB);
    mxDestroyArray(clusterSizesMATLAB);
    mxDestroyArray(offsetArrayMATLAB);
}
void mexFunction(const int nlhs, mxArray *plhs[], const int nrhs, const mxArray *prhs[]) {
    size_t n,j;
    size_t nCard;
    bool isLast, lastPassed;
    mxArray *codeArray;
    
    if(nrhs<1){
        mexErrMsgTxt("Not enough inputs.");
    }
    
    if(nrhs>2){
        mexErrMsgTxt("Too many inputs.");
    }
    
    if(nlhs>4) {
        mexErrMsgTxt("Too many outputs.");
    }
    
    //If an empty code matrix is passed, then the second argument is
    //required, and we will return the first gray code in the sequence.
    if(mxIsEmpty(prhs[0])) {
        mwSize dims[2];
        if(nrhs<2) {
            mexErrMsgTxt("The second argument is required when an empty code matrix is passed.");
        }
        
        dims[0]=getSizeTFromMatlab(prhs[1]);
        dims[1]=1;
        //Allocate the array; this also initializes all of the elements to
        //0.
        codeArray=mxCreateLogicalArray(2, dims);
        
        //This is the code
        plhs[0]=codeArray;
        
        if(nlhs>1) {
            //This is nCard
            plhs[1]=mxCreateDoubleScalar(0.0);
            
            if(nlhs>2) {
                //This is isLast
                plhs[2]=mxCreateLogicalScalar(false);
                
                if(nlhs>3) {
                    //This is j.
                    plhs[3]=mxCreateDoubleMatrix(0, 0, mxREAL);
                }
            }
        }
        return;
    }
    
    //The code array cannot be complex.
    if(mxIsComplex(prhs[0])!=false) {
        mexErrMsgTxt("The code array cannot be complex.");
    }
    
    //Copy the code value so that the input matrix is not modified on
    //return.
    {
        size_t n1,n2;
        n1=mxGetM(prhs[0]);
        n2=mxGetN(prhs[0]);

        if((n1==1&&n2>=1)||(n2==1&&n1>=1)) {
            n=std::max(n1,n2);
            
            codeArray=mxDuplicateArray(prhs[0]);
        } else {
            mexErrMsgTxt("The code vector has the wrong dimensionality.");
        }
    }

    if(nrhs>1) {
        nCard=getSizeTFromMatlab(prhs[1]);
    } else {
        mxArray *lhs[1];
        //Sum up the ones in codeArray to get nCard if it is not provided.
        mexCallMATLAB(1,lhs,1, &codeArray, "sum");
        nCard=getSizeTFromMatlab(lhs[0]);
        mxDestroyArray(lhs[0]);
    }
    
    //The type of the data in the code array is whatever the user passed to
    //the function. The function has to be called with the correct template 
    //value for the type of the code.
    lastPassed=false;
    switch(mxGetClassID(codeArray)){
        case mxCHAR_CLASS:
        {
            mxChar *code=(mxChar*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxLOGICAL_CLASS:
        {
            mxLogical* code=(mxLogical*)mxGetData(codeArray); 
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxDOUBLE_CLASS:
        {
            double* code=(double*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxSINGLE_CLASS:
        {
            float* code=(float*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxINT8_CLASS:
        {
            int8_T* code=(int8_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxUINT8_CLASS:
        {
            uint8_T* code=(uint8_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxINT16_CLASS:
        {
            int16_T* code=(int16_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxUINT16_CLASS:
        {
            uint16_T* code=(uint16_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxINT32_CLASS:
        {
            int32_T* code=(int32_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxUINT32_CLASS:
        {
            uint32_T* code=(uint32_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxINT64_CLASS:
        {
            int64_T* code=(int64_T*)mxGetData(codeArray);
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        case mxUINT64_CLASS:
        {
            uint64_T* code=(uint64_T*)mxGetData(codeArray);   
            
            if(nCard==(size_t)code[n-1]&&nCard!=0) {
                lastPassed=true;
            } else{
                isLast=getNextGrayCodeCPP(n, code, nCard, j);
            }
            break;
        }
        default:
            mexErrMsgTxt("The code vector is of an unknown data type.");
    }
    
    //If the final gray code was passed, then just return empty matrices
    //and put n in nCard. That way, if called again, the function will
    //start from the beginning.
    if(lastPassed==true) {
        mxDestroyArray(codeArray);
        plhs[0]=mxCreateDoubleMatrix(0, 0, mxREAL);
        if(nlhs>1) {
            mxArray *nCardMat=allocUnsignedSizeMatInMatlab(1, 1);
            *(size_t*)mxGetData(nCardMat)=n;

            plhs[1]=nCardMat;
            if(nlhs>2) {
                //This is isLast
                plhs[2]=mxCreateDoubleMatrix(0, 0, mxREAL);;

                if(nlhs>3) {
                    //This is j
                    plhs[3]=mxCreateDoubleMatrix(0, 0, mxREAL);;
                }
            }
        }
        
        return;   
    }
    
    //Set the return values for the case when the last code was not passed.
    plhs[0]=codeArray;
    if(nlhs>1) {
        mxArray *nCardMat=allocUnsignedSizeMatInMatlab(1, 1);
        *(size_t*)mxGetData(nCardMat)=nCard;
        
        plhs[1]=nCardMat;
        if(nlhs>2) {
            //This is isLast
            plhs[2]=mxCreateLogicalScalar(isLast);

            if(nlhs>3) {
                mxArray *jMat=allocUnsignedSizeMatInMatlab(1, 1);
                //Increment j to be an index for Matlab.
                j++;
                *(size_t*)mxGetData(jMat)=j;
                
                plhs[3]=jMat;
            }
        }
    }
}