/* segSiteHMM v. 0.1 !!!
/
/
/ Andrew Kern 3/14/07
*/

#include "../hmm/hmm.h"
#include "../hmm/adkGSL.h"
#include "../hmm/popGenTools.h"
#include "segSites.h"
#include "segSiteHMM.h"
#include <math.h>
#include <assert.h>
#include <string.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>

#define MAXSNPS 100000000

char modeFlag, *outfileName, smoothFlag;
int snpNumber, maxSampleSize, states, verboseFlag = 0, fixedEmisFlag = 0, sampleSize;
gsl_vector *sampleSizes, *piStart, *piEnd;
struct snp data[MAXSNPS];
gsl_matrix *m, *sfsCounts, *emisMatrix;
double gamma1, theta1;
gsl_vector *gammas;
double priorCons = 0;
double omega = 0;
int outputLevel = 0;

int main(int argc, char *argv[]){
  gsl_matrix *alphas, *betas, *posts, *logPosts;
	gsl_vector  *vits, *locs, *include, *include2;
	gsl_matrix  *emisLogs;
	double score1, sum, pFor;
	HMM *h;
	FILE *outfile;
	int i, j, **elements, elementCount, transitionPowerFlag;

	printf("segSiteHMM v0.3 -- thank you for playing thermonuclear war\n");
	printf("getting parameters/data\n");
	getParameters(argc,argv);

	//find max sampleSize
	maxSampleSize = 0;
	for(j = 0; j < snpNumber; j++){
	  if (data[j].n > maxSampleSize){
	    maxSampleSize = data[j].n;
	  }
	}
	//transition gaps?
	transitionPowerFlag = 0;

	//make sampleSize vector and summarize sfs
	sampleSizes = sampleSizeVector(data,snpNumber,maxSampleSize);
	sfsCounts = summarizeSFSBool(maxSampleSize, data, snpNumber);

	//was smoothing used?
	if(smoothFlag == 'f'){
	  //init with equal starting probs
	  sum = 1.0 / states;
	  for (i = 0; i < states; i++){
	    gsl_vector_set(piStart, i, sum);
	    gsl_vector_set(piEnd, i, sum);
	  }
	}
	else if(smoothFlag == 's'){
	  //set start and end probs based on smoothing
	  gsl_vector_set(piStart, 1, priorCons);
	  gsl_vector_set(piStart, 0, 1.0 - gsl_vector_get(piStart, 1));
	  gsl_vector_set(piEnd, 0, gsl_vector_get(piStart,0));
	  gsl_vector_set(piEnd, 1, gsl_vector_get(piStart,1));

	  //define transition probs based on omega and gamma
	  gsl_matrix_set(m, 1, 0, 1.0 / omega);
	  gsl_matrix_set(m, 1, 1, 1.0 - gsl_matrix_get(m, 1, 0));
	  gsl_matrix_set(m, 0, 1, (gsl_matrix_get(m, 1, 0) / gsl_vector_get(piStart, 0)) \
			 -  gsl_matrix_get(m, 1,0));
	  gsl_matrix_set(m, 0, 0, 1.0 - gsl_matrix_get(m, 0, 1));
	}

	printf("path is %d SNPs long\n",snpNumber);
	locs = gsl_vector_alloc(snpNumber);
	//fill up locs vector for use in calculating transition powers
	for(i = 0; i < snpNumber; i++){
	  gsl_vector_set(locs, i, data[i].pos);
	}

	//initialize HMM
	printf("initializing model\n");
	h = newHMM(m, piStart,piEnd,locs);
	initHMM(h);
	printHMM(h);

	switch(modeFlag){
	case 't' :
	  printf("training mode\nEM iterations follow:\n");
	  fflush(stdout);
	  emAlg(h, gammas,snpNumber, data, transitionPowerFlag);
	  //output selection coefficient
	  strcat(outfileName,".alphas");
	  outfile = fopen(outfileName, "w");
	  gsl_vector_fprintf(outfile,gammas,"%f");
	  fclose(outfile);
	  //output transition matrix
	  strcat(outfileName,".trainedTransMat");
	  printTransitions(h, outfileName);
	  break;
	case 'b' :
	  printf("train and decode mode\nEM iterations follow:\n");
	  fflush(stdout);
	  emAlg(h, gammas,snpNumber, data, transitionPowerFlag);
	  	if(outputLevel>0){
		//output selection coefficient
		  strcat(outfileName,".alphas");
		  outfile = fopen(outfileName, "w");
		  fprintf(outfile,"%f",gsl_vector_get(gammas,1));
		  fclose(outfile);
		  //output transition matrix
		  strcat(outfileName,".trainedTransMat");
		  printTransitions(h, outfileName);
		}
	  strcat(outfileName,".decoding");
	  printf("Viterbi decoding mode\nelement predicition will be stored in a file called \"%s\"\nPosterior Probs and Viterbi calls follow:\n",outfileName);
	  vits = gsl_vector_alloc(snpNumber);
          if(!fixedEmisFlag)
          {
	    emisLogs = gsl_matrix_alloc(h->nstates, snpNumber);
	    calculateEmissions(h, emisLogs, gammas, theta1, snpNumber, data);
          }
	  alphas = gsl_matrix_alloc(h->nstates,snpNumber);
	  betas = gsl_matrix_alloc(h->nstates,snpNumber);
	  posts = gsl_matrix_alloc(h->nstates, snpNumber);
	  logPosts =  gsl_matrix_alloc(h->nstates, snpNumber);
	  pFor = forwardAlg(h, alphas, emisLogs, snpNumber, transitionPowerFlag);
	  backwardAlg(h, betas, emisLogs, snpNumber, transitionPowerFlag);
	  posteriorProbsReduced(h, posts, pFor, alphas, betas, emisLogs, snpNumber, transitionPowerFlag);
	  viterbi(h, vits, emisLogs, snpNumber, transitionPowerFlag);

	  //print out likelihood
	  printf("Likelihood = %lf\n",pFor);

	if (outputLevel > 0){
		//output posteriors and viterbi calls
		outfile = fopen(outfileName, "w");
		for(i = 0; i < emisLogs->size2; i++){
			fprintf(outfile,"%d",(int)gsl_vector_get(h->otherData,i));
			for(j = 0; j < h->nstates; j++){
				fprintf(outfile,"\t%lf",gsl_matrix_get(posts, j, i));
			}
			fprintf(outfile,"\t%d\n",(int)gsl_vector_get(vits, i));
		}
		fclose(outfile);
  	}
	  //score elements
	  strcat(outfileName,".elements");
	  scoreElements(h, emisLogs, vits, outfileName, transitionPowerFlag);
	  break;
	case 'v' :
	  printf("Viterbi decoding mode\nelement predicition will be stored in a file called \"%s\"\nPosterior Probs and Viterbi calls follow:\n",outfileName);
	  vits = gsl_vector_alloc(snpNumber);
	  emisLogs = gsl_matrix_alloc(h->nstates, snpNumber);
          if (fixedEmisFlag)
          {
              initializeEmissions(emisLogs, emisMatrix, snpNumber, data);
          }
          else
          {
	      calculateEmissions(h, emisLogs, gammas, theta1, snpNumber, data);
          }
	  alphas = gsl_matrix_alloc(h->nstates,snpNumber);
	  betas = gsl_matrix_alloc(h->nstates,snpNumber);
	  posts = gsl_matrix_alloc(h->nstates, snpNumber);
	  logPosts =  gsl_matrix_alloc(h->nstates, snpNumber);

	  pFor = forwardAlg(h, alphas, emisLogs, snpNumber, transitionPowerFlag);
	  backwardAlg(h, betas, emisLogs, snpNumber, transitionPowerFlag);
	  posteriorProbsReduced(h, posts, pFor, alphas, betas, emisLogs, snpNumber, transitionPowerFlag);
	  viterbi(h, vits, emisLogs, snpNumber, transitionPowerFlag);

	  //print out likelihood
	  printf("Likelihood = %lf\n",pFor);

	if(outputLevel > 0){
	//output posteriors and viterbi calls
		outfile = fopen("snpPosts", "w");
		for(i = 0; i < emisLogs->size2; i++){
			fprintf(outfile,"%d",(int)gsl_vector_get(h->otherData,i));
			for(j = 0; j < h->nstates; j++){
				fprintf(outfile,"\t%lf",gsl_matrix_get(posts, j, i));
			}
			fprintf(outfile,"\t%d\n",(int)gsl_vector_get(vits, i));
		}
		fclose(outfile);
		scoreElements(h, emisLogs, vits, outfileName, transitionPowerFlag);
		break;
		}
	}

	freeHMM(h);
	return 0;
}

void usage(){
    fprintf(stderr,"usage: pgHMM - population genetic HMMs\nrun modes:\n\t-t train mode -- EM training for model\n\t-v viterbi decoding mode\n\t-b both train and decode\n\ninput control\n\t-S stateNumber\n\t-d dataFile\n\t-p transitionMatrix\n\t-s startProbs\n\t-e endProbs\n\t-g prior probability of being in selected state\n\t-E emissionMatrixFile sampleSize -- specifies a file containing an SFS for each state (including frequencies of monomorphic sites); entires must sum to 1, and data must have constant sampleSize\n\t-w omega smoothing parameter (note either transitionMatrix or gamma and omega need to be defined)\n\t-a beta1 beta2 -- value for selected classes\n\t-u 4Nu initial value\n\t-o outfile name (trained matrix or elements)\n\t-V verbose\n\t-O output level (default= 0)\n");
    exit(1);
}

/* sets the parameters/run mode and then opens data */
void getParameters(int argc, char *argv[]){
  FILE *infile;
  int  i, j, n, args;
  long int pos;

  if (argc < 2){
    usage();
  }
  else{
    args = 1;
    while(args < argc){
      switch(argv[args][1]){
      case 't' :
	/* train mode? */
	modeFlag = 't';
	smoothFlag = 'f';
	break;
      case 'v' :
	/* viterbi decode */
	modeFlag = 'v';
	smoothFlag = 'f';
	break;
      case 'b' :
	/*both train and decode */
	modeFlag = 'b';
	smoothFlag = 'f';
	break;
      case 'a' :
	/*set alphas for selected class */
	for(j = 1; j < states; j++){
	  gsl_vector_set(gammas,j,atof(argv[++args]));
	}
	break;
      case 'u' :
	//set theta
	theta1 = atof(argv[++args]);
	break;
	case 'O' :
	//set outputLevel
	outputLevel = atoi(argv[++args]);
	break;
      case 'S' :
	//set state number
	states = atoi(argv[++args]);
	m = gsl_matrix_alloc(states,states);
	gsl_matrix_set_zero(m);
	//initialize selection params and allocate stuff
	gammas = gsl_vector_alloc(states);
	piStart = gsl_vector_alloc(states);
	piEnd = gsl_vector_alloc(states);
	break;
      case 'd' :
      	/* open data file, errors? */
	infile = fopen(argv[++args], "r");
	printf("%s is datafile\n",argv[args]);
	if (infile == NULL){
		fprintf(stderr,"Error opening infile! ARRRRR!!!!\n");
		exit(1);
	}
	/* go through infile and get snp info; expect pos, i, n */
	snpNumber = 0;
	maxSampleSize = 0;
	while (fscanf(infile, "%ld\t%d\t%d", &pos, &i, &n) != EOF){
		data[snpNumber].pos = pos;
		data[snpNumber].i = i;
		data[snpNumber].n = n;
		snpNumber++;
		if(n > maxSampleSize){
		  maxSampleSize = n;
		}
	}
	fclose(infile);
	break;
	//either whole transition matrix needs to be defined or gamma and omega
      case 'p':
	//get transition matrix
	infile = fopen(argv[++args], "r");
	if (infile == NULL){
	  fprintf(stderr,"Error opening matrixfile! ARRRRR!!!!\n");
	  exit(1);
	}
	printf("%s is matrixfile\n",argv[args]);
	gsl_matrix_fscanf(infile, m);
	//gsl_matrix_fprintf(stdout,m,"%f");
	fclose(infile);
	break;
      case's':
	//get start vector
		infile = fopen(argv[++args], "r");
	if (infile == NULL){
	  fprintf(stderr,"Error opening start prob. file! ARRRRR!!!!\n");
	  exit(1);
	}
	printf("%s is start prob. file\n",argv[args]);
	gsl_vector_fscanf(infile, piStart);
	fclose(infile);
	smoothFlag = 'piSet';
	break;
      case'e':
	//get end vector
		infile = fopen(argv[++args], "r");
	if (infile == NULL){
	  fprintf(stderr,"Error opening end prob. file! ARRRRR!!!!\n");
	  exit(1);
	}
	printf("%s is end prob. file\n",argv[args]);
	gsl_vector_fscanf(infile, piEnd);
	smoothFlag = 'piSet';
	fclose(infile);
	break;
      case 'E':
        //get emission matrix
        infile = fopen(argv[++args],"r");
        sampleSize = atoi(argv[++args]);
        if (infile == NULL){
          fprintf(stderr,"Error opening end emission prob. file! ARRRRR!!!!\n");
          exit(1);
        }
        printf("%s is emission prob. file; sampleSize=%d\n",argv[args-1],sampleSize);
	emisMatrix = gsl_matrix_alloc(states,sampleSize);
        gsl_matrix_fscanf(infile, emisMatrix);
        printf("finished reading emission prob. matrix\n");
        for (i=0;i<states;i++)
        {
            for (j=0;j<sampleSize;j++)
            {
                printf("%f ",gsl_matrix_get(emisMatrix,i,j));
            }
            printf("\n");
        }
        fixedEmisFlag = 1;
        fclose(infile);
        break;

      case 'g':
	//get gamma for transitions
	priorCons = atof(argv[++args]);
	printf("prior probability of conservation defined\n");
	break;
      case 'w':
	//get omega for transitions
	omega =  atof(argv[++args]);
	smoothFlag = 's';
	break;
      case 'o':
	//get outfile name
	outfileName = argv[++args];
	break;
      case 'V':
	//verbose training
	verboseFlag = 1;
	break;
      }
      args++;
    }
  }
  if (omega == 0 && gsl_matrix_get(m, 0, 0) == 0){
    usage();
    exit(1);
  }
}

/* calculateEmissions- this fills up a matrix of Nstates * L observations where the elements are
   the computed allele frequency probabilities drawn from the stationary distribution of Wright's genic
   selection model. The population is assumed to be at equilibrium and all snps are considered to be
    independent draws from the same distributions */
void calculateEmissions(HMM *hmm, gsl_matrix *emisLogs, gsl_vector *gammas, double theta,\
			int snpNumber, struct snp theData[MAXSNPS]){
  int i, j;
  gsl_matrix *probs;
  gsl_vector *pVector;

  //error check
  assert(emisLogs->size1 == hmm->nstates);
  assert(emisLogs->size2 == snpNumber);
  //alloc pVector
  pVector = gsl_vector_alloc(hmm->nstates);
  probs = gsl_matrix_alloc(maxSampleSize + 1, maxSampleSize);
  //calculate emissions and fill up matrix
  for(i = 0; i < emisLogs->size1; i++){
    gsl_vector_set(pVector,0,gsl_vector_get(gammas,i));
    gsl_vector_set(pVector,1,theta);
    //make prob matrix (note this is in logs)
    siteProbMatrix(probs,pVector,maxSampleSize, sampleSizes);
    //go through snps, calculate probs
    for(j=0; j < emisLogs->size2; j++){
      gsl_matrix_set(emisLogs, i, j, gsl_matrix_get(probs, data[j].n, data[j].i));
    }
  }
  gsl_vector_free(pVector);
  gsl_matrix_free(probs);
}

void initializeEmissions(gsl_matrix *emisLogs, gsl_matrix *emisMatrix, int snpNumber, struct snp theData[MAXSNPS]){
  int i, j;
  int freq;
  double logProb;

  //calculate emissions and fill up matrix
  for(i = 0; i < emisLogs->size1; i++){
    for(j=0; j < emisLogs->size2; j++){
      //need to get the allele frequency here, and then extract the probs from the corresponding emisMatrix elements
      freq = data[j].i;
      logProb = log(gsl_matrix_get(emisMatrix, i, freq));
      //if (emisLogs->size2 - j <= 10)
      //{
      //    fprintf(stderr,"emission probability for state %d, position %d: %f; log=%f\n",i,j,gsl_matrix_get(emisMatrix, i, freq),logProb);
      //}
      gsl_matrix_set(emisLogs, i, j, logProb);
    }
  }
}

/* emAlg- this is mean to train the emissions (the parameter gamma) using the EM algorithm.
 it returns the trained up gamma value  */
double emAlg(HMM *hmm, gsl_vector *startGammas, int snpNumber, struct snp theData[MAXSNPS], int transitionPowerFlag){
  gsl_matrix *alphas, *betas, *posts, *logPosts, *emisLogs, *tempTrans, *powTrans, **jpMatArray;
  gsl_vector *rowSums, *estimateVector;
  double  lastLik, newLik, delta, dummy, val, pFor, pBack;
  gsl_function likFunc;
   struct my_jointLik_params paramSet;
  int i, j,  k, count;

  //initialize and allocate
  emisLogs = gsl_matrix_alloc(hmm->nstates,snpNumber);
  if(fixedEmisFlag)
  {
      initializeEmissions(emisLogs, emisMatrix, snpNumber, theData);
  }
  else
  {
      calculateEmissions(hmm, emisLogs, startGammas, theta1, snpNumber, theData);
  }
  posts = gsl_matrix_alloc(hmm->nstates,snpNumber);
  logPosts =  gsl_matrix_alloc(hmm->nstates,snpNumber);
  alphas = gsl_matrix_alloc(hmm->nstates,snpNumber);
  betas = gsl_matrix_alloc(hmm->nstates,snpNumber);
  rowSums = gsl_vector_alloc(hmm->nstates);
  delta = 1e-4;
  newLik = 1 ;
  lastLik = 0;
  tempTrans = gsl_matrix_alloc(hmm->nstates,hmm->nstates);

  //make jpMatArray
  jpMatArray = malloc(sizeof(gsl_matrix) * snpNumber);
  for(i = 0; i < snpNumber; i++){
    //each is nstate x nstate transition matrix
    jpMatArray[i] = gsl_matrix_alloc(hmm->nstates,hmm->nstates);
    gsl_matrix_set_all(jpMatArray[i],0.0);
  }

				     //setup parameter estimate vector
  estimateVector = gsl_vector_alloc(hmm->nstates); // 1 mutation parameter, n-1 selection parameters
  gsl_vector_set(estimateVector,0,theta1);
  for(i = 1; i < hmm->nstates; i++){
    gsl_vector_set(estimateVector,i,gsl_vector_get(startGammas,i));
  }

  //initializing joint lik params
  paramSet.data = theData;
  paramSet.snpNumber = snpNumber;
  paramSet.nstates = hmm->nstates;
  paramSet.maxSampleSize = maxSampleSize;
  paramSet.sampleSizeVector = sampleSizes;
  paramSet.sfsBools = sfsCounts;
  paramSet.startingPoint = estimateVector;

  //main loop
  count = 0;
  while ( fabs(newLik - lastLik) >  delta){
    transitionPowerFlag = 0;

    //E step
    //set lastLik to newLik
    dummy = newLik;
    lastLik = dummy;

    //recalculate emissions
    if (!fixedEmisFlag)
    {
        calculateEmissions(hmm, emisLogs, startGammas, theta1, snpNumber, theData);
    }

    //run forwards/backwards and calculate posterior probs
    pFor = forwardAlg(hmm, alphas, emisLogs, snpNumber, transitionPowerFlag);
    pBack = backwardAlg(hmm, betas, emisLogs, snpNumber,transitionPowerFlag);
    //print current status
    printf("%d\tlik= %f\t\ttheta_hat= %f", count,pFor,theta1);
    for(i = 1; i < hmm->nstates; i++){
      printf("\tgamma_hat= %f",gsl_vector_get(startGammas, i));
    }
    printf("\n");
    fflush(stdout);
    //check probs; calculate posterior probs; set "weights"
    assert(fabs(pFor - pBack) < 0.1);
    posteriorProbsReduced(hmm, posts, pFor, alphas, betas, emisLogs, snpNumber,transitionPowerFlag);
    posteriorProbsReducedLog(hmm, logPosts, pFor, alphas, betas, emisLogs, snpNumber,transitionPowerFlag);

    //reestimate the new shiny way
    reestimateTransitions(hmm, posts, jpMatArray,  pFor, alphas, betas, emisLogs, snpNumber,transitionPowerFlag);

    //update start and end vectors based on posteriors
    for(j = 0; j < hmm->nstates; j++){
      gsl_vector_set(hmm->piStart,j,gsl_matrix_get(posts,j,0));
      gsl_vector_set(hmm->piEnd,j,gsl_matrix_get(posts,j,snpNumber - 1));
      gsl_vector_set(hmm->piStart_scores, j, log(gsl_vector_get(hmm->piStart,j)));
      gsl_vector_set(hmm->piEnd_scores, j, log(gsl_vector_get(hmm->piEnd,j)));
    }

    // M step for other params
    if (!fixedEmisFlag)
    {
        paramSet.posts = posts;
        paramSet.startingPoint = estimateVector;
        likFunc.params = &paramSet;
        jointMLEstNState(estimateVector, &newLik, &paramSet);

        //maximization
        theta1 = gsl_vector_get(estimateVector,0);
        for(j = 1; j < hmm->nstates; j++){
          gsl_vector_set(startGammas,j, gsl_vector_get(estimateVector, j));
        }
    }
    newLik = pFor;
    count += 1;
    if (verboseFlag) printHMM(hmm);
  }

 //free jpMatArray
  for(i = 0; i < snpNumber; i++){
    //each is nstate x nstate transition matrix
    gsl_matrix_free(jpMatArray[i]);
  }
  //free(jpMatArray);

  gsl_matrix_free(emisLogs);
  gsl_matrix_free(posts);
  gsl_matrix_free(alphas);
  gsl_matrix_free(betas);
  gsl_vector_free(rowSums);
  gsl_vector_free(estimateVector);
  printHMM(hmm);
  return(gsl_vector_get(startGammas,1));
}

/*This returns an array of arrays of ints which indicate the Viterbi decoded elements from the model.
  The arrays returned are in the format startIndex, stopIndex */
int **elementArray(gsl_vector *vits, double state, int *pCount){
  int i, **array, elementCount, size, last;

  //allocate storage for element arrays
  array = malloc(vits->size * sizeof(int *));
  for(i = 0; i < vits->size; i++){
    array[i] = malloc(3 * sizeof(int));
  }
  //set last to initial obs
  elementCount = 0;
  size = state; //this is the first one we're about to find!
  //find first occurence of state
  for(last = 0; last < vits->size; last++){
    if (gsl_vector_get(vits,last) == state){
      break;
    }
  }

  //iterate through Viterbi's, find runs
  for(i = last+1; i < vits->size; i++){
    if (gsl_vector_get(vits,last) == gsl_vector_get(vits,i)){
      if (size == 0){ //new one?
	last = i;
      }
      size++;
      if ( i == vits->size - 1 && size > 1){
	array[elementCount][0] = last;
	array[elementCount][1] = i;
	elementCount++;
      }
    }
    else{
      if (size > 1){
	array[elementCount][0] = last;
	array[elementCount][1] = i - 1;
	array[elementCount][2] = size;
	elementCount++;
	size = 0;
	last = i - 1 ;
      }
      else{
	size = 0;
      }
    }
  }
  *pCount = elementCount;
  return(array);
}

void scoreElements(HMM *h, gsl_matrix *emisLogs, gsl_matrix *vits, char *outfileName, int transitionPowerFlag){
  gsl_vector *include, *include2;
  FILE *outfile;
  int elementCount, **elements, i, j;
  double score1;

  //open file to print to
  outfile = fopen(outfileName, "w");
  if (outfile == NULL){
    fprintf(stderr,"Error opening outfile! ARRRRR!!!!\n");
    exit(1);
  }
  //print header
  fprintf(outfile,"state\tstart\tstop\tlog_odds_score\tlength\n");

  //alloc state inclusion vectors
  include = gsl_vector_alloc(h->nstates);
  include2 = gsl_vector_alloc(h->nstates);

  //iterate across states
  for(j = 0; j < h->nstates; j++){
    //get state elements from Viterbi
    elementCount = 0;
    elements = elementArray(vits, j, &elementCount);
    //set up state sets for element scoring
    gsl_vector_set_all(include,0);
    gsl_vector_set(include, j, 1);
    gsl_vector_set_all(include2,1);
    gsl_vector_set(include2, j, 0);
    //score elements
    for(i=0; i < elementCount; i++){
      score1 = hmm_subpath_log_odds(h, emisLogs, include,include2, elements[i][0], \
				    elements[i][1] - elements[i][0] + 1 , transitionPowerFlag);
      fprintf(outfile,"%d\t%d\t%d\t%f\t%d\n",j,(int) gsl_vector_get(h->otherData,elements[i][0]), \
	      (int) gsl_vector_get(h->otherData,elements[i][1]),score1, (int) elements[i][2]);
      free(elements[i]);
    }
    free(elements);
  }
  fclose(outfile);
}


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <alloca.h>

#include <gsl/gsl_cdf.h>

#include "limits.h"
#include "stattest.h"
#include "cat.h"
#include "fisher.h"
#include "min2.h"
#include "bvr.h"

typedef unsigned int count_t;
typedef const count_t COUNT_T;
typedef double       prob_t;

/**
  * This class is stateful in the interest of efficiency
  * because some methods share preliminary computations:
  * 1) statistics require expectation, and
  * 2) expectation requires marginals
  * (which may also be used incidentally for other purposes), so this
  * variable captures the progress towards complete calculations
  * to enable implicit just-in-time calculation.
  */
enum State {
	Nothing = 0,
	Marginals,
	Expectation
};

struct CatCovars {

	/**
	 * Buffers are sized to accomodate these dimensions at construction
	 * and are not changeable thereafter.
	 */
	unsigned int ROW_CAPACITY, COL_CAPACITY;

	/**
	  * "decl(ared)_" because the bounds says nothing about the row/col
	  * content--empty rows/columns may exist! I'm just driving this point
	  * home in these members' names because it has already burned me once!
	  */
	unsigned int decl_rows, decl_cols, decl_cells;

	/**
	  * Count of samples pushed since last cat_clear.
	  */
	count_t sample_count;

	// Calculation state ///////////////////////////////////////////////////

	prob_t minimumExpected;

	/**
	 * This is used strictly by badCellExists
	 */
	count_t REQUIRED_CELL_MINIMUM;

	enum State calculated;

	// Buffers /////////////////////////////////////////////////////////////

	size_t SIZEOF_PROB_BUF;

	prob_t  *expect;

	size_t SIZEOF_COUNT_BUF;

	/**
	  * The counts matrix is stored packed against the head of this
	  * (heap-allocated) buffer. In other words, though this buffer
	  * is -expected- to be larger than required (since the class
	  * is designed to be iteratively re-used), only the first
	  * (decl_rows*decl_cols) of the buffer are used for any given
	  * matrix.
	  */
	count_t *counts;

	/**
	  * Following are derivative data...computed from counts.
	  */
	count_t *rmarg;
	count_t *cmarg;
};

static void _coordsOfOffset(
	struct CatCovars *co, unsigned int off, unsigned int *r, unsigned int *c ) {
	*r = off / co->decl_cols;
	*c = off % co->decl_cols;
}

static count_t _count( struct CatCovars *co, coord_t r, coord_t c ) {
	const int C = co->decl_cols;
	assert( r < co->decl_rows && c < C );
	return co->counts[ r*C + c ];
}

static prob_t _expected( struct CatCovars *co, coord_t r, coord_t c ) {
	const int C = co->decl_cols;
	assert( r < co->decl_rows && c < C );
	return co->expect[ r*C + c ];
}

static void _recalcSampleCount( struct CatCovars *co ) {
	int n = co->decl_cells;
	co->sample_count = 0;
	while( n-- > 0 ) co->sample_count += co->counts[n];
}

/**
  * This starts searching at the given coordinates and continues linearly
  * through the matrix until it finds a violating cell at which point it
  * turns the linear index into 2D coordinates.
  */
static bool _badCellExists( struct CatCovars *co,
		unsigned int *offset ) {

	unsigned int i = *offset;
	while( i < co->decl_cells ) {
		if( co->counts[i] < co->REQUIRED_CELL_MINIMUM ) {
			*offset = i;
			return true;
		}
		i++;
	}
	return false;
}

/**
  * If after accumulation there are fewer than 2 rows or fewer than 2
  * columns, the table is degenerate. Rows or columns with zero marginal
  * sum are OK! For a contingency table that's bad but not -degenerate-.
  */
static bool _immediatelyDegenerate( struct CatCovars *co ) {
	return ! ( co->decl_rows >= 2 && co->decl_cols >= 2 );
}

#if 0
static bool _eventuallyDegenerate( struct CatCovars *co ) {
	unsigned nonemprows = 0;
	unsigned nonempcols = 0;
	unsigned i;
	for(i = 0; i < co->decl_rows; i++ ) if( co->rmarg[i] > 0 ) nonemprows++;
	for(i = 0; i < co->decl_cols; i++ ) if( co->cmarg[i] > 0 ) nonempcols++;
	return  !  ( nonemprows >= 2 && nonempcols >= 2 );
}
#endif

static void _cullRow( struct CatCovars *co, unsigned int r ) {

	// An over-writing copy suffices since matrix is
	// stored row-major. mergeCols is more intricate.

	if( r+1 < co->decl_rows ) { // ...so 0 < rows-U-1
		memmove(
			co->counts + (r  )*co->decl_cols,
			co->counts + (r+1)*co->decl_cols,
			(co->decl_rows - r - 1)*co->decl_cols*sizeof(count_t) );
	} else {
		// ...it's the last row, and there's nothing to move.
	}
	co->decl_rows  -= 1;
	co->decl_cells -= co->decl_cols;

	co->sample_count = 0;
	co->calculated = Nothing; // ...to trigger recomputation.
}

static void _cullCol( struct CatCovars *co, unsigned int c ) {

	const unsigned int N = co->decl_cells;
	// Shrink. Because matrices are stored row-major and calculations
	// throughout this class assume the matrix is packed at the head of
	// the buffer, there is actually a fairly complex shifting of all
	// but the first c entries that has to occurr...
	// Basically, traverse the matrix linearly (storage order) shifting
	// "left" all entries that are NOT in the c'th column...
	for(unsigned int src = 0, dst = 0 ; src < N; src++ ) {
		if( src % co->decl_cols != c ) {
			co->counts[dst++] = co->counts[src];
		}
	}
	co->decl_cols -= 1;
	co->decl_cells -= co->decl_rows;

	co->sample_count = 0;
	co->calculated = Nothing; // ...to trigger recomputation.
}

/**
  * Following two MUST be called in order before any of
  * the statistics are computed. In other words,
  *    !!!! THESE ARE NOT CALLED IMPLICITLY! !!!!
  * (Statistical methods which -ought- to be const can't be unless
  *  'expect', '[rc]marg' and friends are made mutable->)
  */
static void _calc_marginals( struct CatCovars *co ) {

	const int R = co->decl_rows;
	const int C = co->decl_cols;
	co->sample_count = 0;

	memset( co->rmarg, 0, R*sizeof(count_t) );
	memset( co->cmarg, 0, C*sizeof(count_t) );

	// Calculate the marginals...

	for(unsigned int i = 0; i < R; i++ ) {
		for(unsigned int j = 0; j < C; j++ ) {
			const count_t C = _count( co, i,j);
			co->rmarg[i] += C;
			co->cmarg[j] += C;
			co->sample_count    += C;
		}
	}

	co->calculated = Marginals;
}

/**
 * Calculates the expected probabilities of a contingency table assuming
 * independence of the two variables.
 * "Since the Chi-square and G^2 statistics rest on the normal approximation
 *  to the hypergeometric distribution, these approximations get strained
 *  when there are zero frequency cells or when the expected counts are
 *  'very small'." ...from Yaramakala's Masters Thesis, p.43
 *
 * "...for an independence test to be reliable, all cells have non-zero
 *  expected values and at least 80% of the cells should have expected
 *  values greater than 5." [Everitt 1977, Upton 1978]
 */
static void _calc_expectation( struct CatCovars *co ) {

	const int R = co->decl_rows;
	const int C = co->decl_cols;

	if( co->calculated < Marginals )
		_calc_marginals( co );

	if( co->sample_count > 0 ) {
		co->minimumExpected
			= (prob_t)(co->rmarg[0] * co->cmarg[0])
			/ (prob_t)co->sample_count;
		for(unsigned int i = 0; i < R; i++ ) {
			for(unsigned int j = 0; j < C; j++ ) {
				const prob_t E
					= (prob_t)(co->rmarg[i] * co->cmarg[j])
					/ (prob_t)co->sample_count;
				co->expect[i*co->decl_cols + j] = E;
				if( co->minimumExpected > E )
					co->minimumExpected = E;
			}
		}
	}

	co->calculated = Expectation;
}

/**
 * This function cannot fail. Valid results always exist.
 * Returns the indices of the minimal row and columns marginals.
 */
#if 0
static void _minimalMarginals( struct CatCovars *co, unsigned int *rm, unsigned int *cm ) {

	unsigned int MIN_DIM
		= co->decl_rows < co->decl_cols ? co->decl_rows : co->decl_cols;
	unsigned int i, ri = 0, ci = 0;
	count_t mr, mc;

	if( co->calculated < Marginals )
		_calc_marginals( co );

	mr = co->rmarg[ri];
	mc = co->cmarg[ci];

	// Iterate over marginals at indices [0,min(rows,cols) )

	for(i = 0; i < MIN_DIM; i++ ) {
		if( mr > co->rmarg[i] ) {
			mr = co->rmarg[i];
			ri = i;
		}
		if( mc > co->cmarg[i] ) {
			mc = co->cmarg[i];
			ci = i;
		}
	}

	// ...continue iteration over [ min(rows,cols), max(rows,cols) ).

	if( co->decl_cols > co->decl_rows )
		for(; i < co->decl_cols; i++ ) {
			if( mc > co->cmarg[i] ) {
				mc = co->cmarg[i];
				ci = i;
			}
		}
	else
	if( co->decl_rows > co->decl_cols )
		for(; i < co->decl_rows; i++ ) {
			if( mr > co->rmarg[i] ) {
				mr = co->rmarg[i];
				ri = i;
			}
		}

	*rm = ri;
	*cm = ci;
}
#endif

/**
 * A pretty printer...primarily for debugging.
 */
#ifdef _DEBUG
static void dbg_dump( struct CatCovars *co, FILE *fp, const char *prefix ) {

	if( prefix==NULL ) prefix="";

	fprintf( fp, "%s %d(r) x %d(c) (capacity = %dx%d)\n",
		prefix,
		co->decl_rows,
		co->decl_cols,
		co->ROW_CAPACITY,
		co->COL_CAPACITY );

	fprintf( fp, "%s cell counts...\n", prefix );
	for(unsigned int r = 0; r < co->decl_rows; r++ ) {
		fputs( prefix, fp );
		for(unsigned int c = 0; c < co->decl_cols; c++ ) {
			fprintf( fp, "\t%d", _count(co,r,c) );
		}
		fputc( '\n', fp );
	}

	if( co->calculated >= Expectation ) {
		fprintf( fp, "%s %d total samples, expectation...\n", prefix, co->sample_count );
		for(unsigned int r = 0; r < co->decl_rows; r++ ) {
			fputs( prefix, fp );
			for(unsigned int c = 0; c < co->decl_cols; c++ ) {
				fprintf( fp, "\t%.3e", _expected( co, r, c) );
			}
			fputc( '\n', fp );
		}
	}
}
#endif

/***************************************************************************
  * Publics
  */

void cat_destroy( void *pv ) {

	if( pv ) {
		struct CatCovars *co = (struct CatCovars *)pv;
		if( co->expect )
			free( co->expect );
		free( pv );
	}
}

/**
  * Pre-allocate a set of working buffers large enough for all anticipated
  * calculations (max feature length) and a struct to wrap them.
  */
void *cat_create( unsigned int rcap, unsigned int ccap ) {

	struct CatCovars *co
		= calloc( 1, sizeof(struct CatCovars) );

	if( co ) {
		co->ROW_CAPACITY = rcap;
		co->COL_CAPACITY = ccap;
		co->SIZEOF_COUNT_BUF
			= (rcap*ccap+rcap+ccap)*sizeof(count_t);
		co->SIZEOF_PROB_BUF
			= rcap*ccap*sizeof(prob_t);

		// Allocate one large buffer and partition it up.
		co->expect
			= calloc( co->SIZEOF_PROB_BUF + co->SIZEOF_COUNT_BUF, sizeof(char) );
		co->counts
			= (count_t*)( co->expect + rcap*ccap );
		co->rmarg
			= co->counts + rcap*ccap;
		co->cmarg
			= co->rmarg + rcap;

		co->REQUIRED_CELL_MINIMUM = 5;

		// If -anything- failed clean up any successes.
		if( (NULL == co->counts) ||
			(NULL == co->expect) ) {
			cat_destroy( co );
			return NULL;
		}
		return co;
	}
	return NULL;
}

void cat_clear( void *pv, coord_t nr, coord_t nc ) {

	struct CatCovars *co = (struct CatCovars *)pv;

	assert( nr <= co->ROW_CAPACITY && nc <= co->COL_CAPACITY );

	// Set dimensions and cell count.

	co->decl_rows  = nr;
	co->decl_cols  = nc;
	co->decl_cells = nr*nc;

	co->sample_count    = 0;
	co->minimumExpected = 0.0;
	co->calculated = Marginals;

	// OPTIMIZATION: Only clearing the part of capacity intended for use!

	memset( co->counts, 0, co->decl_cells*sizeof(count_t) );
	memset( co->rmarg,  0, co->decl_rows*sizeof(count_t) );
	memset( co->cmarg,  0, co->decl_cols*sizeof(count_t) );
	memset( co->expect, 0, co->decl_cells*sizeof(prob_t) );
}

void cat_setMinCellCount( void *pv, unsigned n ) {
	struct CatCovars *co = (struct CatCovars *)pv;
	co->REQUIRED_CELL_MINIMUM = n;
}

/**
 * Only this one is public in order that we may count the samples
 * as they're added...
 */
void cat_push( void *pv, coord_t r, coord_t c ) {

	struct CatCovars *co = (struct CatCovars *)pv;

	assert( r < co->decl_rows );
	assert( c < co->decl_cols );

	co->counts[ r*co->decl_cols + c ] += 1;

	// size and marginals are kept up to date...
	co->sample_count++;
	co->rmarg[r] += 1;
	co->cmarg[c] += 1;
}

size_t cat_size( void *pv ) {
	struct CatCovars *co = (struct CatCovars *)pv;
	return co->sample_count;
}

bool cat_complete( void *pv ) {
	struct CatCovars *co = (struct CatCovars *)pv;
	return ! _immediatelyDegenerate( co );
}

bool cat_is2x2( void *pv ) {
	struct CatCovars *co = (struct CatCovars *)pv;
	return (2 == co->decl_rows) && (2 == co->decl_cols);
}

/**
  * <bits> will ALWAYS be non-zero on entry, but it may only contain
  * a single set bit.
  */
static int _offsetOfLeastLoss( unsigned bits, COUNT_T *loss, count_t *leastLoss ) {
	int v = __builtin_ctz( bits );
	int l = loss[v];
	bits ^= (1<<v);
	while( bits ) {
		const int i = __builtin_ctz( bits );
		bits ^= (1<<i);
		if( l > loss[i] ) {
			l = loss[i];
			v = i;
		}
	}
	*leastLoss = l;
	return v;
}

/**
 * This removes all rows and any columns containing cells with
 * counts LESS THAN OR EQUAL TO REQUIRED_CELL_MINIMUM.
 *
 * This method assumes we start with a table larger in at least
 * one dimention than 2x2 and halts when/if we reach a 2x2.
 */
unsigned int cat_cullBadCells( void *pv, char *log, int buflen ) {

	unsigned int culled = 0;
	struct CatCovars *co = (struct CatCovars *)pv;
	const char * const EOL = log + buflen - 2; // leave room for "+\0".
	// BEGIN log maintenance...
	const int MIN_LOG_RECORD_LEN = 3; // "R99"
	// ...since tables will never exceed 99 rows or columns.
	char *pc = log;
	// ...END log maintenance.
	unsigned int victim;
	unsigned int raw_mem_offset = 0; // linear offsets of "bad" cell.

#ifdef _DEBUG
	const bool SHOW_CULLING = getenv("SHOW_CULLING") != NULL;
	if( SHOW_CULLING ) {
		fprintf( stderr, "CULL log: original matrix\n" );
		dbg_dump( co, stderr, "CULL" );
	}
#endif

	assert( ! _immediatelyDegenerate( co ) ); // so BOTH dims >= 2

	while( (co->decl_rows > 2 || co->decl_cols > 2) && _badCellExists( co, &raw_mem_offset ) ) {

		unsigned int r, c;
		char cullty = '?';

		/**
		  * It is always necessary to cull a row or column.
		  * If |columns| > 3, calculate "best" column to cull.
		  * If |rows| > 3, calculate "best" row to cull.
		  * "Best" victim is row OR column among the candidates for
		  * culling with minimal marginal.
		  */

		unsigned row_bitfield = 0;
		unsigned col_bitfield = 0;
		count_t minRowCost, minColCost;

		assert( sizeof(row_bitfield)*8 >= MAX_CATEGORY_COUNT );

		// Find ALL rows and columns that are candidates for culling by
		// virtue of containing a bad cell.

		do {
			_coordsOfOffset( co, raw_mem_offset, &r, &c );
			row_bitfield |= (1<<r);
			col_bitfield |= (1<<c);
			++ raw_mem_offset;
			// Add the row and column to the list of candidate victims.
		} while( _badCellExists( co, &raw_mem_offset ) );

		if( co->calculated < Marginals )
			_calc_marginals( co );

		if( co->decl_rows > 2 ) {

			r = _offsetOfLeastLoss( row_bitfield, co->rmarg, &minRowCost );

			if( co->decl_cols > 2 ) { // EITHER a col OR row may be culled

				c = _offsetOfLeastLoss( col_bitfield, co->cmarg, &minColCost );
				cullty = minRowCost < minColCost ? 'R' : 'C';

			} else                    // ONLY a row may be culled
				cullty = 'R';

		} else {                      // ONLY a column may be culled

			c = _offsetOfLeastLoss( col_bitfield, co->cmarg, &minColCost );
			cullty = 'C';
		}

		if( cullty == 'R' )
			_cullRow( co, (victim = r) );
		else {
			assert( cullty == 'C' );
			_cullCol( co, (victim = c) );
		}

		// Log it, if possible...

		if( pc ) {

			pc += sprintf( pc, "%c%d", cullty, victim );

			// Following is a bit of overkill to make sure we can
			// give some indication that we ran out of log space.

			if( EOL - pc < MIN_LOG_RECORD_LEN ) {
				if( EOL - pc > 0 ) strcpy( pc, "+");
				pc = NULL; // no more logging.
				// Really this should never be executed if log buffer
				// is always sufficient.
			}
		}

		// Unlike merging, decimating CAN leave a 0 cell at a lower
		// linear index, so reset is required...
		culled++;
		raw_mem_offset = 0;

#ifdef _DEBUG
		if( SHOW_CULLING ) {
			fprintf( stderr,
				"CULL After %d selected %c%d (from R:%08X,C:%08X) costing %d samples\n",
				culled, cullty, victim,
				row_bitfield, col_bitfield,
			   	cullty=='R' ? minRowCost : minColCost );
			dbg_dump( co, stderr, "CULL" );
		}
#endif
	}

	if( culled > 0 )
		_recalcSampleCount( co );

	return culled;
}

#ifdef HAVE_G_TEST
int cat_g( void *pv, struct Statistic *result ) {

	struct CatCovars *co = (struct CatCovars *)pv;
	const int R = co->decl_rows;
	const int C = co->decl_cols;
	double g = 0.0;

	abort(); // unfinished.

	if( co->calculated < Expectation )
		_calc_expectation( co );

	for(unsigned int i = 0; i < R; i++ ) {
		for(unsigned int j = 0; j < C; j++ ) {
			const double O = _count( co, i, j );
			const double E = _expected( co, i, j );
			if( E > 0 && O > 0 )
				g += ( O * log( O / E ) );
		}
	}

	g *= 2.0; // TODO: verify!

	result->name
		= "G";
	result->sample_count
		= co->sample_count;
	result->probability
		= gsl_cdf_chisq_Q( g, (R-1)*(C-1) );

	return 0;
}
#endif

int cat_chi_square( void *pv, struct Statistic *result ) {

	struct CatCovars *co = (struct CatCovars *)pv;
	const int R = co->decl_rows;
	const int C = co->decl_cols;

	unsigned int n_empty = 0;
	double chi = 0.0;

	if( co->calculated < Expectation )
		_calc_expectation( co );

	for(unsigned int i = 0; i < R; i++ ) {
		for(unsigned int j = 0; j < C; j++ ) {
			const double O = _count( co, i, j );
			const double E = _expected( co, i, j );
			if( E > 0 && O > 0 )
				chi += ((O-E)*(O-E))/E;
			else
			if( O == 0 )
				n_empty++;
		}
	}

	result->name
		= "Chi-square";
	result->sample_count
		= co->sample_count;
	result->probability
		= gsl_cdf_chisq_Q( chi, (R-1)*(C-1) );

	result->extra_value[0] = R;
	result->extra_value[1] = C;
	result->extra_value[2] = co->minimumExpected;
	result->extra_value[3] = n_empty;

	return 0;
}

int cat_fisher_exact( void *pv, struct Statistic *result ) {

	struct CatCovars *co = (struct CatCovars *)pv;

	// OPTIMIZE: does not make sense to call fisher exact without
	// NULL stest_t argument. Keeping this signature for consistency
	// with all other stats, but it might make sense to change the
	// signature of just this function to return P-value (unless I
	// ever added log-likelihood ratio calculation which is what
	// ought to be returned as the function value).

	/**
	  * Given the table:
	  *    a | b
	  *    --+--
	  *    c | d
	  *
	  * ...we need to pass fexact_prob( x, m, n, k )
	  * m == a+c
	  * n == b+d
	  * k == a+b
	  * x == a
	  */
	result->name
		= "Fisher_Exact";
	result->sample_count
		= co->sample_count;
	result->probability
		= fexact_prob(
			_count(co,0,0),
			_count(co,0,0) + _count(co,1,0),   // a+c
			_count(co,0,1) + _count(co,1,1),   // b+d
			_count(co,0,0) + _count(co,0,1) ); // a+b

	// TODO: Don't need these now.
	result->extra_value[0] = co->decl_rows;
	result->extra_value[1] = co->decl_cols;

	return 0;
}

/**
  */
double cat_spearman_rho( void *pv ) {

	struct CatCovars *co = (struct CatCovars *)pv;
	double rho = nan("nan");
	const unsigned r0 = _count(co,0,0) + _count(co,0,1);
	const unsigned r1 = _count(co,1,0) + _count(co,1,1);
	const unsigned c0 = _count(co,0,0) + _count(co,1,0);
	const unsigned c1 = _count(co,0,1) + _count(co,1,1);

	// Continuing is pointless if any of these are 0.

	if( r0 > 0 && r1 > 0 && c0 > 0 && c1 > 0 ) {

		const float MU    = (co->sample_count+1.0)/2.0;
		const float r0_mu = (r0+1.0)/2.0;
		const float r1_mu = MEAN_RANK_OF_TIES( r0, co->sample_count );
		const float c0_mu = (c0+1.0)/2.0;
		const float c1_mu = MEAN_RANK_OF_TIES( c0, co->sample_count );

		/**
		  * Following is a heavily compacted form of the Pearson rho,
		  * compacted because thanks to all the ties there are only two
		  * "versions" of (x_i-x_mu) and (y_i-x_mu), and the general
		  * sums in the Pearson quotient just become multiples of these.
		  */
		const double DENOM
			= sqrt(
			  (r0*(r0_mu-MU)*(r0_mu-MU) + r1*(r1_mu-MU)*(r1_mu-MU))
			* (c0*(c0_mu-MU)*(c0_mu-MU) + c1*(c1_mu-MU)*(c1_mu-MU)) );

		if( isnormal(DENOM) ) {
			rho =
				( _count(co,0,0)*(r0_mu-MU)*(c0_mu-MU)
				+ _count(co,0,1)*(r0_mu-MU)*(c1_mu-MU)
				+ _count(co,1,0)*(r1_mu-MU)*(c0_mu-MU)
				+ _count(co,1,1)*(r1_mu-MU)*(c1_mu-MU) )
				/
				DENOM;
		}

	}
	return rho;
}

#ifdef _UNITTEST_CAT_

#include <err.h>

int main( int argc, char *argv[] ) {

	if( argc >= 3 ) {

		const int MERGE_LOG_LEN = 128;
		char merge_log[ MERGE_LOG_LEN ];

		struct Statistic result;

		const unsigned int MAXROW
			= atoi(argv[1]);
		const unsigned int MAXCOL
			= atoi(argv[2]);

		FILE *fp
			= argc > 3
			? fopen( argv[3], "r" )
			: stdin;

		int err = 0;
		char *line = NULL;
		size_t n = 0;

		void *accum
			= cat_create( MAXROW, MAXCOL);

		cat_setMinCellCount( accum, 5 );
		cat_clear( accum, MAXROW, MAXCOL );   // ...as per command line.

		while( getline( &line, &n, fp ) > 0 ) {
			int cat[2];
			if( line[0] == '#' ) {
				fputs( line, stdout );
				continue;
			}
			if( 2 == sscanf( line, "%d\t%d\n", cat+0, cat+1 ) ) {
				if( 0 <= cat[0] && 0 <= cat[1] ) {
					cat_push( accum, cat[0], cat[1] );
				}
			} else {
				fprintf( stderr, "Failure parsing line: %s", line );
			}
		}
		free( line );
		fclose( fp );

		puts( "Original..." );
		dbg_dump( accum, stdout, ""  );

		if( cat_cullBadCells( accum, merge_log, MERGE_LOG_LEN ) > 0 ) {
			fprintf( stdout, "Culled %s, leaving...\n", merge_log );
			dbg_dump( accum, stdout, "" );
		}

		memset( &result, 0, sizeof(struct Statistic) );
		err = cat_is2x2( accum )
			? cat_fisher_exact( accum, &result )
			: cat_chi_square( accum, &result );

		if( ! err ) {
			printf( "p-value=%f", result.probability );
			if( cat_is2x2( accum ) )
				printf( ", spearman=%f\n", cat_spearman_rho( accum ) );
			else
				fputc( '\n', stdout );
		} else
			printf( "error\n" );

		cat_destroy( accum );

	} else
		err( -1, "%s <max row> <max col> [ <2-column file> ]", argv[0] );
	return 0;
}
#endif


/**
 * \author Sylvain Marsat, University of Maryland - NASA GSFC
 *
 * \brief C header for functions windowing and computing FFT/IFFT of time/frequency series.
 *
 */

#ifndef __COMPUTELISASNR_H__
#define __COMPUTELISASNR_H__ 1

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_bspline.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_complex.h>

#include "constants.h"
#include "struct.h"
#include "waveform.h"
#include "fft.h"
#include "LISAgeometry.h"
#include "LISAFDresponse.h"
#include "LISAutils.h"

/********************************** Structures ******************************************/

/* Parameters for the generation of a LISA waveform (in the form of a list of modes) */
typedef struct tagComputeLISASNRparams {
  double tRef;               /* reference time (s) - GPS time at the frequency representing coalescence */
  double phiRef;             /* reference phase (rad) - phase at the frequency representing coalescence (or at fRef if specified) */
  double fRef;               /* reference frequency at which phiRef is set (Hz, default 0 which is interpreted as Mf=0.14) */
  double m1;                 /* mass of companion 1 (solar masses, default 2e6) */
  double m2;                 /* mass of companion 2 (solar masses, default 1e6) */
  double distance;           /* distance of source (Mpc, default 1e3) */
  double inclination;        /* inclination of source (rad, default pi/3) */
  double lambda;             /* first angle for the position in the sky (rad, default 0) */
  double beta;               /* second angle for the position in the sky (rad, default 0) */
  double polarization;       /* polarization angle (rad, default 0) */

  int nbmode;                /* number of modes to generate (starting with 22) - defaults to 5 (all modes) */
  double deltatobs;          /* Observation duration (years, default=2) */
  double minf;               /* Minimal frequency (Hz, default=0) - when set to 0, use the lowest frequency where the detector noise model is trusted __LISASimFD_Noise_fLow (set somewhat arbitrarily)*/
  double maxf;               /* Maximal frequency (Hz, default=0) - when set to 0, use the highest frequency where the detector noise model is trusted __LISASimFD_Noise_fHigh (set somewhat arbitrarily)*/
  int tagextpn;              /* Tag to allow PN extension of the waveform at low frequencies */
  double Mfmatch;            /* When PN extension allowed, geometric matching frequency: will use ROM above this value. If <=0, use ROM down to the lowest covered frequency */
  int tagtdi;                /* Tag selecting the desired TDI observables */
  int tagint;                /* Tag choosing the integrator: 0 for Fresnel (default), 1 for linear integration */
  int nbptsoverlap;          /* Number of points to use in loglinear overlaps (default 32768) */
  LISAconstellation *variant;  /* A structure defining the LISA constellation features */
  int frozenLISA;            /* Freeze the orbital configuration to the time of peak of the injection (default 0) */
  ResponseApproxtag responseapprox;    /* Approximation in the GAB and orb response - choices are full (full response, default), lowfL (keep orbital delay frequency-dependence but simplify constellation response) and lowf (simplify constellation and orbital response) - WARNING : at the moment noises are not consistent, and TDI combinations from the GAB are unchanged */
  int fromtditdfile;         /* Option for loading time series for TDI observables and FFTing */
  int nlinesinfile;          /* Number of lines of input file */
  char indir[256];           /* Input directory */
  char infile[256];          /* Input file */
  int loadparamsfile;        /* Option to load physical parameters from file and to output result to file (default 0) */
  int nlinesparams;          /* Number of lines in params file */
  char paramsdir[256];       /* Directory for the input/output file */
  char paramsfile[256];      /* Input file with the parameters */
  char outputfile[256];      /* Output file */
} ComputeLISASNRparams;

#if 0
{ /* so that editors will match succeeding brace */
#elif defined(__cplusplus)
}
#endif

#endif /* _COMPUTELISASNR_H */


#include "np.h"
#include "main.h"
#include "argv.h"
#include "memory.h"
#include "utf8.h"

#include "module_categorical.h"
#include "module_lde.h"

#include <stdlib.h>
#include <string.h>

#include <math.h>
#include <float.h>

#include <gsl/gsl_sf.h>
#include "gslsupp.h"

#include "sort.h"

#ifndef TEST_DEACTIVATE

#   include "test.h"
#   include "test_ll.h"
#   include "test_np.h"
#   include "test_sort.h"
#   include "test_utf8.h"

static bool test_main(struct log *log)
{
    const struct test_group group_arr[] = {
        {
            NULL,
            sizeof(struct test_ll_a),
            CLII((test_generator_callback[]) {
                test_ll_generator_a,
            }),
            CLII((test_callback[]) {
                test_ll_a_1,
                test_ll_a_2,
                test_ll_a_3
            })
        },
        {
            NULL,
            sizeof(struct test_ll_a),
            CLII((test_generator_callback[]) {
                test_ll_generator_b,
            }),
            CLII((test_callback[]) {
                test_ll_b,
            })
        },
        {
            test_np_disposer_a,
            sizeof(struct test_np_a),
            CLII((test_generator_callback[]) {
                test_np_generator_a,
            }),
            CLII((test_callback[]) {
                test_np_a,
            })
        },
        {
            test_sort_disposer_a,
            sizeof(struct test_sort_a),
            CLII((test_generator_callback[]) {
                test_sort_generator_a_1,
                test_sort_generator_a_2,
                test_sort_generator_a_3
            }),
            CLII((test_callback[]) {
                test_sort_a,
            })
        },
        {
            test_sort_disposer_b,
            sizeof(struct test_sort_b),
            CLII((test_generator_callback[]) {
                test_sort_generator_b_1
            }),
            CLII((test_callback[]) {
                test_sort_b_1,
                test_sort_b_2
            })
        },
        {
            test_sort_disposer_c,
            sizeof(struct test_sort_c),
            CLII((test_generator_callback[]) {
                test_sort_generator_c_1
            }),
            CLII((test_callback[]) {
                test_sort_c_1,
                test_sort_c_2
            })
        },
        {
            NULL,
            sizeof(struct test_utf8),
            CLII((test_generator_callback[]) {
                test_utf8_generator,
            }),
            CLII((test_callback[]) {
                test_utf8_len,
                test_utf8_encode,
                test_utf8_decode,
                test_utf16_encode,
                test_utf16_decode
            })
        }
    };
    log_message_generic(log, CODE_METRIC, MESSAGE_NOTE, "Test mode triggered!\n");
    return test(group_arr, countof(group_arr), log);
}

#else

static bool test_main(struct log *log)
{
    log_message_generic(log, CODE_METRIC, MESSAGE_NOTE, "Test mode deactivated!\n");
    return 1;
}

#endif

// Main routine arguments management
struct main_args main_args_default()
{
    struct main_args res = { .thread_cnt = get_processor_count() };
    uint8_bit_set(res.bits, MAIN_ARGS_BIT_POS_THREAD_CNT);
    return res;
}

struct main_args main_args_override(struct main_args args_hi, struct main_args args_lo)
{
    struct main_args res = { .log_path = args_hi.log_path ? args_hi.log_path : args_lo.log_path };
    if (res.log_path && !strcmp(res.log_path, "stderr")) res.log_path = NULL;
    memcpy(res.bits, args_hi.bits, UINT8_CNT(MAIN_ARGS_BIT_CNT));
    if (uint8_bit_test(args_hi.bits, MAIN_ARGS_BIT_POS_THREAD_CNT)) res.thread_cnt = args_hi.thread_cnt;
    else
    {
        res.thread_cnt = args_lo.thread_cnt;
        uint8_bit_set(res.bits, MAIN_ARGS_BIT_POS_THREAD_CNT);
    }
    return res;
}

// Main routine (see below for actual entry point)
static int Main(int argc, char **argv)
{
    /*const char *strings[] = {
        __FUNCTION__,
        "NOTE (%s): No input data specified.\n",
        "INFO (%s): Help mode triggered.\n",
        "WARNING (%s): Unable to compile XML document \"%s\"!\n",
        "WARNING (%s): Unable to execute one or more branches of the XML document \"%s\"!\n",
        "Overall program execution"
    };

    enum
    {
        STR_FN = 0,
        STR_FR_ND,
        STR_FR_IH,
        STR_FR_WC,
        STR_FR_WE,
        STR_M_EXE
    };
    */

    // All names should be sorted according to 'strncmp'!!!
    struct argv_par_sch argv_par_sch =
    {
        CLII((struct tag[]) { { STRI("help"), 0 }, { STRI("log"), 1 }, { STRI("test"), 2 }, { STRI("threads"), 3 }}),
        CLII((struct tag[]) { { STRI("C"), 4 }, { STRI("L"), 5 }, { STRI("T"), 2 }, { STRI("h"), 0 }, { STRI("l"), 1 }, { STRI("t"), 3 } }),
        CLII((struct par_sch[])
        {
            { 0, &(struct handler_context) { offsetof(struct main_args, bits), MAIN_ARGS_BIT_POS_HELP }, empty_handler, 1 },
            { offsetof(struct main_args, log_path), NULL, p_str_handler, 0 },
            { 0, &(struct handler_context) { offsetof(struct main_args, bits), MAIN_ARGS_BIT_POS_TEST }, empty_handler, 1 },
            { offsetof(struct main_args, thread_cnt), &(struct handler_context) { offsetof(struct main_args, bits), MAIN_ARGS_BIT_POS_THREAD_CNT }, size_handler, 0 },
            { 0, &(struct handler_context) { offsetof(struct main_args, bits), MAIN_ARGS_BIT_POS_CAT }, empty_handler, 1 },
            { 0, &(struct handler_context) { offsetof(struct main_args, bits), MAIN_ARGS_BIT_POS_LDE }, empty_handler, 1 },
        })
    };

    /*

    // All names on every sub-level should be sorted according to 'strncmp'!!!
    struct { xmlNode *node; size_t cnt; } dscsch = CLII((xmlNode[])
    {
        {
            .name = STRI("Density"),
            .sz = sizeof(densityContext),
            .prologue = (prologueCallback) densityPrologue,
            .epilogue = (epilogueCallback) densityEpilogue,
            .dispose = (disposeCallback) densityContextDispose,
            CLII((struct att[])
            {
                { STRI("pos"), 0, &(handlerContext) { offsetof(densityContext, bits), DENSITYCONTEXT_BIT_POS_POS }, (readHandlerCallback) boolHandler },
                { STRI("radius"), offsetof(densityContext, radius), &(handlerContext) { offsetof(densityContext, bits) - offsetof(densityContext, radius), DENSITYCONTEXT_BIT_POS_RADIUS }, (readHandlerCallback) uint32Handler },
                { STRI("type"), offsetof(densityContext, type), NULL, (readHandlerCallback) densityTypeHandler }
            }),
            CLII((xmlNode[])
            {
                {
                    .name = STRI("Fold"),
                    .sz = sizeof(densityFoldContext),
                    .prologue = (prologueCallback) densityFoldPrologue,
                    .epilogue = (epilogueCallback) densityFoldEpilogue,
                    .dispose = (disposeCallback) densityFoldContextDispose,
                    CLII((struct att[])
                    {
                        { STRI("group"), offsetof(densityFoldContext, group), NULL, (readHandlerCallback) strHandler },
                        { STRI("type"), offsetof(densityFoldContext, type), NULL, (readHandlerCallback) densityFoldHandler }
                    }),
                    CLII((xmlNode[])
                    {
                        {
                            .name = STRI("Report"),
                            .sz = sizeof(dfReportContext),
                            .prologue = (prologueCallback) dfReportPrologue,
                            .epilogue = (epilogueCallback) dfReportEpilogue,
                            .dispose = (disposeCallback) dfReportContextDispose,
                            CLII((struct att[])
                            {
                                { STRI("header"), 0, &(handlerContext) { offsetof(dfReportContext, bits), DFREPORTCONTEXT_BIT_POS_HEADER }, (readHandlerCallback) boolHandler },
                                { STRI("limit"), offsetof(dfReportContext, limit), &(handlerContext) { offsetof(dfReportContext, bits) - offsetof(dfReportContext, limit), DFREPORTCONTEXT_BIT_POS_LIMIT }, (readHandlerCallback) uint32Handler },
                                { STRI("path"), offsetof(dfReportContext, path), NULL, (readHandlerCallback) strHandler },
                                { STRI("semicolon"), 0, &(handlerContext) { offsetof(dfReportContext, bits), DFREPORTCONTEXT_BIT_POS_SEMICOLON }, (readHandlerCallback) boolHandler },
                                { STRI("threshold"), offsetof(dfReportContext, threshold), &(handlerContext) { offsetof(dfReportContext, bits) - offsetof(dfReportContext, threshold), DFREPORTCONTEXT_BIT_POS_THRESHOLD }, (readHandlerCallback) float64Handler },
                                { STRI("type"), offsetof(dfReportContext, type), NULL, (readHandlerCallback) dfReportHandler }
                            })
                        }
                    })
                },
                {
                    .name = STRI("Regions"),
                    .sz = sizeof(regionsContext),
                    .prologue = (prologueCallback) regionsPrologue,
                    .epilogue = (epilogueCallback) regionsEpilogue,
                    .dispose = (disposeCallback) regionsContextDispose,
                    CLII((struct att[])
                    {
                        { STRI("decay"), offsetof(regionsContext, decay), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, decay), REGIONSCONTEXT_BIT_POS_DECAY }, (readHandlerCallback) uint32Handler },
                        { STRI("depth"), offsetof(regionsContext, depth), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, depth), REGIONSCONTEXT_BIT_POS_DEPTH }, (readHandlerCallback) uint32Handler },
                        { STRI("length"), offsetof(regionsContext, length), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, length), REGIONSCONTEXT_BIT_POS_LENGTH }, (readHandlerCallback) uint32Handler },
                        { STRI("threshold"), offsetof(regionsContext, threshold), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, threshold), REGIONSCONTEXT_BIT_POS_THRESHOLD }, (readHandlerCallback) float64Handler },
                        { STRI("tolerance"), offsetof(regionsContext, tolerance), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, tolerance), REGIONSCONTEXT_BIT_POS_TOLERANCE }, (readHandlerCallback) float64Handler },
                        { STRI("slope"), offsetof(regionsContext, slope), &(handlerContext) { offsetof(regionsContext, bits) - offsetof(regionsContext, slope), REGIONSCONTEXT_BIT_POS_SLOPE }, (readHandlerCallback) float64Handler }
                    })
                },
                {
                    .name = STRI("Report"),
                    .sz = sizeof(dReportContext),
                    .prologue = (prologueCallback) dReportPrologue,
                    .epilogue = (epilogueCallback) dReportEpilogue,
                    .dispose = (disposeCallback) dReportContextDispose,
                    CLII((struct att[])
                    {
                        { STRI("header"), 0, &(handlerContext) { offsetof(dReportContext, bits), DREPORTCONTEXT_BIT_POS_HEADER }, (readHandlerCallback) boolHandler },
                        { STRI("limit"), offsetof(dReportContext, limit), &(handlerContext) { offsetof(dReportContext, bits) - offsetof(dReportContext, limit), DREPORTCONTEXT_BIT_POS_LIMIT }, (readHandlerCallback) uint32Handler },
                        { STRI("path"), offsetof(dReportContext, path), NULL, (readHandlerCallback) strHandler },
                        { STRI("semicolon"), 0, &(handlerContext) { offsetof(dReportContext, bits), DREPORTCONTEXT_BIT_POS_SEMICOLON }, (readHandlerCallback) boolHandler },
                        { STRI("threshold"), offsetof(dReportContext, threshold), &(handlerContext) { offsetof(dReportContext, bits) - offsetof(dReportContext, threshold), DREPORTCONTEXT_BIT_POS_THRESHOLD }, (readHandlerCallback) float64Handler }
                    })
                }
            })
        },
        {
            .name = STRI("MySQL.Dispatch"),
            .sz = sizeof(mysqlDispatchContext),
            .prologue = (prologueCallback) mysqlDispatchPrologue,
            .epilogue = (epilogueCallback) mysqlDispatchEpilogue,
            .dispose = (disposeCallback) mysqlDispatchContextDispose,
            CLII((struct att[])
            {
                { STRI("host"), offsetof(mysqlDispatchContext, host), NULL, (readHandlerCallback) strHandler },
                { STRI("login"), offsetof(mysqlDispatchContext, login), NULL, (readHandlerCallback) strHandler },
                { STRI("password"), offsetof(mysqlDispatchContext, password), NULL, (readHandlerCallback) strHandler },
                { STRI("port"), offsetof(mysqlDispatchContext, port), NULL, (readHandlerCallback) uint32Handler },
                { STRI("schema"), offsetof(mysqlDispatchContext, schema), NULL, (readHandlerCallback) strHandler },
                { STRI("temp.prefix"), offsetof(mysqlDispatchContext, temppr), NULL, (readHandlerCallback) strHandler },
            })
        }
    });

    // All names on every sub-level should be sorted according to 'strncmp'!!!
    xmlNode xmlsch =
    {
        .name = STRI("RegionsMT"),
        .sz = sizeof(frameworkContext),
        .prologue = (prologueCallback) frameworkPrologue,
        .epilogue = (epilogueCallback) frameworkEpilogue,
        .dispose = (disposeCallback) frameworkContextDispose,
        CLII((struct att[])
        {
            { STRI("log"), offsetof(frameworkContext, logFile), NULL, (readHandlerCallback) strHandler },
            { STRI("threads"), offsetof(frameworkContext, threadCount), &(handlerContext) { offsetof(frameworkContext, bits) - offsetof(frameworkContext, threadCount), FRAMEWORKCONTEXT_BIT_POS_THREADCOUNT }, (readHandlerCallback) uint32Handler }
        }),
        CLII((xmlNode[])
        {
            {
                .name = STRI("Data.Load"),
                .sz = sizeof(loadDataContext),
                .prologue = (prologueCallback) loadDataPrologue,
                .epilogue = (epilogueCallback) loadDataEpilogue,
                .dispose = (disposeCallback) loadDataContextDispose,
                CLII((struct att[])
                {
                    { STRI("header"), 0, &(handlerContext) { offsetof(loadDataContext, bits), LOADDATACONTEXT_BIT_POS_HEADER }, (readHandlerCallback) boolHandler },
                    { STRI("logarithm"), 0, &(handlerContext) { offsetof(loadDataContext, bits), LOADDATACONTEXT_BIT_POS_LOGARITHM }, (readHandlerCallback) boolHandler },
                    { STRI("no.ranks"), 0, &(handlerContext) { offsetof(loadDataContext, bits), LOADDATACONTEXT_BIT_POS_NORANKS }, (readHandlerCallback) boolHandler },
                    { STRI("path.chr"), offsetof(loadDataContext, pathchr), NULL, (readHandlerCallback) strHandler },
                    { STRI("path.row"), offsetof(loadDataContext, pathrow), NULL, (readHandlerCallback) strHandler },
                    { STRI("path.test"), offsetof(loadDataContext, pathtest), NULL, (readHandlerCallback) strHandler },
                    { STRI("path.val"), offsetof(loadDataContext, pathval), NULL, (readHandlerCallback) strHandler },
                }),
                { dscsch.node, dscsch.cnt }
            },
            {
                .name = STRI("Genotypes"),
                .sz = sizeof(genotypesContext),
                .prologue = (prologueCallback) genotypesPrologue,
                .epilogue = (epilogueCallback) genotypesEpilogue,
                .dispose = (disposeCallback) genotypesContextDispose,
                CLII((struct att[])
                {
                    { STRI("option0"), 0, &(handlerContext) { offsetof(genotypesContext, bits), GENOTYPESCONTEXT_BIT_POS_OPTION0 }, (readHandlerCallback) boolHandler },
                    { STRI("option1"), 0, &(handlerContext) { offsetof(genotypesContext, bits), GENOTYPESCONTEXT_BIT_POS_OPTION1 }, (readHandlerCallback) boolHandler },
                    { STRI("option2"), 0, &(handlerContext) { offsetof(genotypesContext, bits), GENOTYPESCONTEXT_BIT_POS_OPTION2 }, (readHandlerCallback) boolHandler },
                    { STRI("option3"), 0, &(handlerContext) { offsetof(genotypesContext, bits), GENOTYPESCONTEXT_BIT_POS_OPTION3 }, (readHandlerCallback) boolHandler },
                    { STRI("path.bed"), offsetof(genotypesContext, path_bed), NULL, (readHandlerCallback) strHandler },
                    { STRI("path.bim"), offsetof(genotypesContext, path_bim), NULL, (readHandlerCallback) strHandler },
                    { STRI("path.fam"), offsetof(genotypesContext, path_fam), NULL, (readHandlerCallback) strHandler }
                }),
                { dscsch.node, dscsch.cnt }
            },
            {
                .name = STRI("MySQL.Fetch"),
                .sz = sizeof(mysqlFetchContext),
                .prologue = (prologueCallback) mysqlFetchPrologue,
                .epilogue = (epilogueCallback) mysqlFetchEpilogue,
                .dispose = (disposeCallback) mysqlFetchContextDispose,
                CLII((struct att[])
                {
                    { STRI("host"), offsetof(mysqlFetchContext, host), NULL, (readHandlerCallback) strHandler },
                    { STRI("login"), offsetof(mysqlFetchContext, login), NULL, (readHandlerCallback) strHandler },
                    { STRI("no.ranks"), 0, &(handlerContext) { offsetof(mysqlFetchContext, bits), MYSQLFETCHCONTEXT_BIT_POS_NORANKS }, (readHandlerCallback) boolHandler },
                    { STRI("password"), offsetof(mysqlFetchContext, password), NULL, (readHandlerCallback) strHandler },
                    { STRI("port"), offsetof(mysqlFetchContext, port), NULL, (readHandlerCallback) uint32Handler },
                    { STRI("schema"), offsetof(mysqlFetchContext, schema), NULL, (readHandlerCallback) strHandler }
                }),
                { dscsch.node, dscsch.cnt }
            }
        })
    };

    */

    //FILE *f = stderr; // fopen("f:\\test\\h", "w");
    //fputs("\xef\xbb\xbf", f);
    //for (size_t i = 0; i < (size_t) argc; i++) fprintf(stderr, "%s\n", argv[i]);
    //fclose(f);

    struct style style = {
        .ttl = { INIT_ENV_COL(FG_GREEN), INIT_ENV_COL(FG_RED), INIT_ENV_COL(FG_YELLOW), INIT_ENV_COL(FG_MAGENTA), INIT_ENV_COL(FG_CYAN) },
        .ts = INIT_ENV_COL(FG_BR_BLACK),
        .src = INIT_ENV_COL(FG_BR_BLACK),
        .str = INIT_ENV_COL(FG_BR_MAGENTA),
        .num = INIT_ENV_COL(FG_BR_CYAN),
        .time = INIT_ENV_COL(FG_BR_YELLOW)
    };

    struct log log;
    if (log_init(&log, NULL, 1, 0, style, NULL))
    {
        size_t pos_cnt;
        char **pos_arr;
        struct main_args main_args = { 0 };
        if (argv_parse(argv_par_selector, &argv_par_sch, &main_args, argv, argc, &pos_arr, &pos_cnt, &log))
        {
            main_args = main_args_override(main_args, main_args_default());
            if (uint8_bit_test(main_args.bits, MAIN_ARGS_BIT_POS_HELP))
            {
                // Help mode
                log_message_generic(&log, CODE_METRIC, MESSAGE_INFO, "Help mode triggered!\n");
            }
            else if (uint8_bit_test(main_args.bits, MAIN_ARGS_BIT_POS_TEST))
            {
                test_main(&log);
            }
            else if (uint8_bit_test(main_args.bits, MAIN_ARGS_BIT_POS_CAT))
            {
                if (pos_cnt >= 6)
                {
                    size_t rpl = (size_t) strtoull(pos_arr[4], NULL, 10);
                    uint64_t seed = (uint64_t) strtoull(pos_arr[5], NULL, 10);
                    categorical_run(pos_arr[0], pos_arr[1], pos_arr[2], pos_arr[3], rpl, seed, &log);
                }
            }
            else if (uint8_bit_test(main_args.bits, MAIN_ARGS_BIT_POS_LDE))
            {
                if (pos_cnt >= 2) lde_run(pos_arr[0], pos_arr[1], &log);
            }
            else
            {
                if (!pos_cnt) log_message_generic(&log, CODE_METRIC, MESSAGE_NOTE, "No input data specified.\n");
                else
                {

                }
            }
            free(pos_arr);
        }
        log_close(&log);
    }

    /*
    if (bitTest(args.bits, CMDARGS_BIT_POS_HELP))
    {
        logMsg(&loginfo, strings[STR_FR_IH], strings[STR_FN]);

        // Help message should be here
    }
    else
    {
        if (!inputcnt) logMsg(&loginfo, strings[STR_FR_ND], strings[STR_FN]);

        for (size_t i = 0; i < inputcnt; i++) // Processing input files
        {
            programObject *obj = programObjectFromXML(&xmlsch, input[i], &loginfo);

            if (obj)
            {
                if (!programObjectExecute(obj, &args.in)) logMsg(&loginfo, strings[STR_FR_WE], strings[STR_FN], input[i]);
                programObjectDispose(obj);
            }
            else logMsg(&loginfo, strings[STR_FR_WC], strings[STR_FN], input[i]);
        }
    }
    */

    return EXIT_SUCCESS;
}

#if _WIN32
#   include <windows.h>

int wmain(int argc, wchar_t **wargv)
{
    // Memory leaks will be reported at the program exit
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);

    // Making console output UTF-8 friendly
    if (!SetConsoleOutputCP(CP_UTF8)) return EXIT_FAILURE;

    int main_res = EXIT_FAILURE;

    // Translating UTF-16 command-line parameters to UTF-8
    char **argv;
    if (array_init(&argv, NULL, argc, sizeof(*argv), 0, ARRAY_STRICT))
    {
        size_t base_cnt = 0, i;
        for (i = 0; i < (size_t) argc; i++) // Determining total length and performing error checking
        {
            wchar_t *word = wargv[i];
            uint32_t val;
            uint8_t context = 0;
            for (; *word; word++)
            {
                if (!utf16_decode((uint16_t) *word, &val, NULL, NULL, &context)) break;
                if (!context) base_cnt += utf8_len(val);
            }
            if (*word) break;
            else base_cnt++;
        }
        if (i == (size_t) argc)
        {
            char *base;
            if (array_init(&base, NULL, base_cnt, sizeof(*base), 0, ARRAY_STRICT))
            {
                char *byte = base;
                for (i = 0; i < (size_t) argc; i++) // Performing translation
                {
                    argv[i] = byte;
                    wchar_t *word = wargv[i];
                    uint32_t val;
                    uint8_t context = 0;
                    for (; *word; word++)
                    {
                        if (!utf16_decode((uint16_t) *word, &val, NULL, NULL, &context)) break;
                        if (!context)
                        {
                            uint8_t len;
                            utf8_encode(val, (uint8_t *) byte, &len);
                            byte += len;
                        }
                    }
                    if (*word) break;
                    else *(byte++) = '\0';
                }
                if (i == (size_t) argc) main_res = Main(argc, argv);
                free(base);
            }
        }
        free(argv);
    }
    return main_res;
}

#else

int main(int argc, char **argv)
{
    return Main(argc, argv);
}

#endif


#ifndef CTETRA_SUM_H
#define CTETRA_SUM_H

#include <stdlib.h>
#include <stdbool.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

#include "input.h"
#include "ecache.h"
#include "evcache.h"
#include "submesh.h"
#include "fermi.h"
#include "weights.h"

double SumEnergy(double *E_Fermi, InputFn Efn, int na, int nb, int nc, int num_bands, double num_electrons, gsl_matrix *R, bool use_cache);

double SumEnergyFixedFermi(double E_Fermi, InputFn Efn, int na, int nb, int nc, int num_bands, gsl_matrix *R, bool use_cache);

double with_cache_SumEnergyFixedFermi(double E_Fermi, EnergyCache *Ecache);

double** partial_num_states(UEInputFn UEfn, int na, int nb, int nc, int num_bands, double num_total_electrons, gsl_matrix *R, double **Es, int num_E, double *E_Fermi, double **num_states_Fermi);

double one_band_n(double E_Fermi, int band_index, EvecCache *evCache);

#endif // CTETRA_SUM_H


#include <gsl/gsl_math.h>
#include "gsl_cblas.h"
#include "cblas.h"

void
cblas_drotg (double *a, double *b, double *c, double *s)
{
#define BASE double
#include "source_rotg.h"
#undef BASE
}


/*! \file rho.h
    \brief 電子密度ρ(r)を求めるクラスの宣言

    Copyright © 2015 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "data.h"
#include "property.h"
#include <memory>           // for std::unique_ptr
#include <vector>           // for std::vector
#include <gsl/gsl_spline.h> // for gsl_interp_accel, gsl_interp_accel_free, gsl_spline, gsl_spline_free

namespace schrac {
    //! A class.
    /*!
        電子密度ρ(r)を求めるクラス
    */
    class Rho final {
        // #region コンストラクタ・デストラクタ

    public:
        //! A constructor.
        /*!
            唯一のコンストラクタ
            \param pdiffdata 微分方程式のデータオブジェクト
        */
        Rho(std::shared_ptr<DiffData> const & pdiffdata);

        //! A destructor.
        /*!
            デフォルトデストラクタ
        */
        ~Rho() = default;

        // #region メンバ関数

        //!  A public member function.
        /*!
            現在の電子密度ρ(r)で初期化する
        */
        void init();

        //!  A public member function (const).
        /*!
            電子密度ρ(r)を返す
            \param r rの値
            \return ρ(r)の値
        */
        double operator()(double r) const;

        //!  A public member function (const).
        /*!
            新しい電子密度ρnew(r)と、電子密度ρ(r)を一次混合する
            \param rhonew 新しい電子密度ρnew(r)
        */
        void rhomix(dvector const & rhonew);

        // #endregion メンバ関数

        // #region プロパティ

    public:
        //! A property.
        /*!
            電子密度が格納された可変長配列へのプロパティ
        */
        Property< std::vector<double> > const PRho;

        // #endregion プロパティ

        // #region メンバ変数

    private:
        //! A private member variable.
        /*!
            gsl_interp_accelへのスマートポインタ
        */
        std::unique_ptr<gsl_interp_accel, decltype(&gsl_interp_accel_free)> const acc_;

        //!  A private member variable.
        /*!
            データオブジェクト
        */
        std::shared_ptr<DiffData> pdiffdata_;

        //!  A private member variable.
        /*!
            密度ρ(r)
        */
        std::vector<double> rho_;

        //! A private member variable.
        /*!
            gsl_splineへのスマートポインタ
        */
        std::unique_ptr<gsl_spline, decltype(&gsl_spline_free)> const spline_;

        // #endregion メンバ変数

        // #region 禁止されたコンストラクタ・メンバ関数

        //! A private constructor (deleted).
        /*!
            デフォルトコンストラクタ（禁止）
        */
        Rho() = delete;

        //! A private copy constructor (deleted).
        /*!
            コピーコンストラクタ（禁止）
        */
        Rho(Rho const &) = delete;

        //! A private member function (deleted).
        /*!
            operator=()の宣言（禁止）
            \param コピー元のオブジェクト（未使用）
            \return コピー元のオブジェクト
        */
        Rho & operator=(Rho const &) = delete;

        // #endregion 禁止されたコンストラクタ・メンバ関数
    };
}


/**
 *
 * @file qwrapper_zgemm.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Hatem Ltaief
 * @author Mathieu Faverge
 * @author Jakub Kurzak
 * @date 2010-11-15
 * @precisions normal z -> c d s
 *
 **/
#include <cblas.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm(Quark *quark, Quark_Task_Flags *task_flags,
                      PLASMA_enum transA, int transB,
                      int m, int n, int k, int nb,
                      PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                                                const PLASMA_Complex64_t *B, int ldb,
                      PLASMA_Complex64_t beta, PLASMA_Complex64_t *C, int ldc)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    C,                 INOUT,
        sizeof(int),                        &ldc,       VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm2( Quark *quark, Quark_Task_Flags *task_flags,
                        PLASMA_enum transA, int transB,
                        int m, int n, int k, int nb,
                        PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                        const PLASMA_Complex64_t *B, int ldb,
                        PLASMA_Complex64_t beta, PLASMA_Complex64_t *C, int ldc)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    C,                 INOUT | LOCALITY | GATHERV,
        sizeof(int),                        &ldc,       VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemm_quark = PCORE_zgemm_quark
#define CORE_zgemm_quark PCORE_zgemm_quark
#endif
void CORE_zgemm_quark(Quark *quark)
{
    PLASMA_enum transA;
    PLASMA_enum transB;
    int m;
    int n;
    int k;
    PLASMA_Complex64_t alpha;
    PLASMA_Complex64_t *A;
    int lda;
    PLASMA_Complex64_t *B;
    int ldb;
    PLASMA_Complex64_t beta;
    PLASMA_Complex64_t *C;
    int ldc;

    quark_unpack_args_13(quark, transA, transB, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
    cblas_zgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        m, n, k,
        CBLAS_SADDR(alpha), A, lda,
        B, ldb,
        CBLAS_SADDR(beta), C, ldc);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm_f2(Quark *quark, Quark_Task_Flags *task_flags,
                         PLASMA_enum transA, int transB,
                         int m, int n, int k, int nb,
                         PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                                                   const PLASMA_Complex64_t *B, int ldb,
                         PLASMA_Complex64_t beta, PLASMA_Complex64_t *C, int ldc,
                         PLASMA_Complex64_t *fake1, int szefake1, int flag1,
                         PLASMA_Complex64_t *fake2, int szefake2, int flag2)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_f2_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t)*nb*nb,    C,                 INOUT | LOCALITY,
        sizeof(int),                        &ldc,       VALUE,
        sizeof(PLASMA_Complex64_t)*szefake1, fake1,             flag1,
        sizeof(PLASMA_Complex64_t)*szefake2, fake2,             flag2,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemm_f2_quark = PCORE_zgemm_f2_quark
#define CORE_zgemm_f2_quark PCORE_zgemm_f2_quark
#endif
void CORE_zgemm_f2_quark(Quark* quark)
{
    PLASMA_enum transA;
    PLASMA_enum transB;
    int M;
    int N;
    int K;
    PLASMA_Complex64_t alpha;
    PLASMA_Complex64_t *A;
    int LDA;
    PLASMA_Complex64_t *B;
    int LDB;
    PLASMA_Complex64_t beta;
    PLASMA_Complex64_t *C;
    int LDC;
    void *fake1, *fake2;

    quark_unpack_args_15(quark, transA, transB, M, N, K, alpha,
                         A, LDA, B, LDB, beta, C, LDC, fake1, fake2);
    cblas_zgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        M, N, K,
        CBLAS_SADDR(alpha), A, LDA,
        B, LDB,
        CBLAS_SADDR(beta), C, LDC);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm_p2(Quark *quark, Quark_Task_Flags *task_flags,
                         PLASMA_enum transA, int transB,
                         int m, int n, int k, int nb,
                         PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                         const PLASMA_Complex64_t **B, int ldb,
                         PLASMA_Complex64_t beta, PLASMA_Complex64_t *C, int ldc)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_p2_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*lda*nb,   A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t*),         B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t)*ldc*nb,    C,                 INOUT | LOCALITY,
        sizeof(int),                        &ldc,       VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemm_p2_quark = PCORE_zgemm_p2_quark
#define CORE_zgemm_p2_quark PCORE_zgemm_p2_quark
#endif
void CORE_zgemm_p2_quark(Quark* quark)
{
    PLASMA_enum transA;
    PLASMA_enum transB;
    int M;
    int N;
    int K;
    PLASMA_Complex64_t alpha;
    PLASMA_Complex64_t *A;
    int LDA;
    PLASMA_Complex64_t **B;
    int LDB;
    PLASMA_Complex64_t beta;
    PLASMA_Complex64_t *C;
    int LDC;

    quark_unpack_args_13(quark, transA, transB, M, N, K, alpha,
                         A, LDA, B, LDB, beta, C, LDC);
    cblas_zgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        M, N, K,
        CBLAS_SADDR(alpha), A, LDA,
        *B, LDB,
        CBLAS_SADDR(beta), C, LDC);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm_p3(Quark *quark, Quark_Task_Flags *task_flags,
                           PLASMA_enum transA, int transB,
                           int m, int n, int k, int nb,
                           PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                           const PLASMA_Complex64_t *B, int ldb,
                           PLASMA_Complex64_t beta, PLASMA_Complex64_t **C, int ldc)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_p3_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*lda*nb,   A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t)*ldb*nb,   B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t*),         C,                 INOUT | LOCALITY,
        sizeof(int),                        &ldc,       VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemm_p3_quark = PCORE_zgemm_p3_quark
#define CORE_zgemm_p3_quark PCORE_zgemm_p3_quark
#endif
void CORE_zgemm_p3_quark(Quark* quark)
{
    PLASMA_enum transA;
    PLASMA_enum transB;
    int M;
    int N;
    int K;
    PLASMA_Complex64_t alpha;
    PLASMA_Complex64_t *A;
    int LDA;
    PLASMA_Complex64_t *B;
    int LDB;
    PLASMA_Complex64_t beta;
    PLASMA_Complex64_t **C;
    int LDC;

    quark_unpack_args_13(quark, transA, transB, M, N, K, alpha,
                         A, LDA, B, LDB, beta, C, LDC);
    cblas_zgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        M, N, K,
        CBLAS_SADDR(alpha), A, LDA,
        B, LDB,
        CBLAS_SADDR(beta), *C, LDC);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemm_p2f1(Quark *quark, Quark_Task_Flags *task_flags,
                           PLASMA_enum transA, int transB,
                           int m, int n, int k, int nb,
                           PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                           const PLASMA_Complex64_t **B, int ldb,
                           PLASMA_Complex64_t beta, PLASMA_Complex64_t *C, int ldc,
                           PLASMA_Complex64_t *fake1, int szefake1, int flag1)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_zgemm_p2f1_quark, task_flags,
        sizeof(PLASMA_enum),                &transA,    VALUE,
        sizeof(PLASMA_enum),                &transB,    VALUE,
        sizeof(int),                        &m,         VALUE,
        sizeof(int),                        &n,         VALUE,
        sizeof(int),                        &k,         VALUE,
        sizeof(PLASMA_Complex64_t),         &alpha,     VALUE,
        sizeof(PLASMA_Complex64_t)*lda*nb,   A,                 INPUT,
        sizeof(int),                        &lda,       VALUE,
        sizeof(PLASMA_Complex64_t*),         B,                 INPUT,
        sizeof(int),                        &ldb,       VALUE,
        sizeof(PLASMA_Complex64_t),         &beta,      VALUE,
        sizeof(PLASMA_Complex64_t)*ldc*nb,    C,                 INOUT | LOCALITY,
        sizeof(int),                        &ldc,       VALUE,
        sizeof(PLASMA_Complex64_t)*szefake1, fake1,             flag1,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemm_p2f1_quark = PCORE_zgemm_p2f1_quark
#define CORE_zgemm_p2f1_quark PCORE_zgemm_p2f1_quark
#endif
void CORE_zgemm_p2f1_quark(Quark* quark)
{
    PLASMA_enum transA;
    PLASMA_enum transB;
    int M;
    int N;
    int K;
    PLASMA_Complex64_t alpha;
    PLASMA_Complex64_t *A;
    int LDA;
    PLASMA_Complex64_t **B;
    int LDB;
    PLASMA_Complex64_t beta;
    PLASMA_Complex64_t *C;
    int LDC;
    void *fake1;

    quark_unpack_args_14(quark, transA, transB, M, N, K, alpha,
                         A, LDA, B, LDB, beta, C, LDC, fake1);
    cblas_zgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        M, N, K,
        CBLAS_SADDR(alpha), A, LDA,
        *B, LDB,
        CBLAS_SADDR(beta), C, LDC);
}