void LALInferenceDumptemplateFreqDomain(LALInferenceVariables *currentParams,
                                        LALInferenceModel *model,
                                        const char *filename)
/* de-bugging function writing (frequency-domain) template to a CSV file */
/* File contains real & imaginary parts of plus & cross components.      */
/* Template amplitude is scaled to 1Mpc distance.                        */
{
  FILE *outfile=NULL; 
  REAL8 f;
  UINT4 i;

  REAL8 deltaF = model->deltaF;

  LALInferenceCopyVariables(currentParams, model->params);
  model->templt(model);
  if (model->domain == LAL_SIM_DOMAIN_TIME)
    LALInferenceExecuteFT(model);

  outfile = fopen(filename, "w");
  /*fprintf(outfile, "f PSD dataRe dataIm signalPlusRe signalPlusIm signalCrossRe signalCrossIm\n");*/
  fprintf(outfile, "\"f\",\"signalPlusRe\",\"signalPlusIm\",\"signalCrossRe\",\"signalCrossIm\"\n");
  for (i=0; i<model->freqhPlus->data->length; ++i){
    f = ((double) i) * deltaF;
    fprintf(outfile, "%f,%e,%e,%e,%e\n",
            f,
            creal(model->freqhPlus->data->data[i]),
            cimag(model->freqhPlus->data->data[i]),
            creal(model->freqhCross->data->data[i]),
            cimag(model->freqhCross->data->data[i]));
  }
  fclose(outfile);
  fprintf(stdout, " wrote (frequency-domain) template to CSV file \"%s\".\n", filename);
}
void LALInferenceDumptemplateTimeDomain(LALInferenceVariables *currentParams,
                                        LALInferenceModel *model,
					                    const char *filename)
/* de-bugging function writing (frequency-domain) template to a CSV file */
/* File contains real & imaginary parts of plus & cross components.      */
/* Template amplitude is scaled to 1Mpc distance.                        */
{
  FILE *outfile=NULL; 
  REAL8 deltaT, t, epoch; // deltaF - set but not used
  UINT4 i;

  LALInferenceCopyVariables(currentParams, model->params);
  model->templt(model);
  if (model->domain == LAL_SIM_DOMAIN_FREQUENCY)
    LALInferenceExecuteInvFT(model);

  outfile = fopen(filename, "w");
  fprintf(outfile, "\"t\",\"signalPlus\",\"signalCross\"\n");
  deltaT = model->deltaT;
  epoch = XLALGPSGetREAL8(&model->timehPlus->epoch);
  for (i=0; i<model->timehPlus->data->length; ++i){
    t =  epoch + ((double) i) * deltaT;
    fprintf(outfile, "%f,%e,%e\n",
            t,
            model->timehPlus->data->data[i],
            model->timehCross->data->data[i]);
  }
  fclose(outfile);
  fprintf(stdout, " wrote (time-domain) template to CSV file \"%s\".\n", filename);
}
/**
 * \brief The log likelihood function
 *
 * This function calculates natural logarithm of the likelihood of a signal model (specified by a given set of
 * parameters) given the data from a set of detectors.
 *
 * The likelihood is the joint likelihood of chunks of data over which the noise is assumed stationary and Gaussian. For
 * each chunk a Gaussian likelihood for the noise and data has been marginalised over the unknown noise standard
 * deviation using a Jeffreys prior on the standard deviation. Given the data consisting of independent real and
 * imaginary parts this gives a Students-t distribution for each chunk (of length \f$m\f$) with \f$m/2\f$ degrees of
 * freedom:
 * \f[
 * p(\mathbf{\theta}|\mathbf{B}) = \prod_{j=1}^M \frac{(m_j-1)!}{2\pi^{m_j}}
 * \left( \sum_{k=k_0}^{k_0+(m_j-1)} |B_k - y(\mathbf{\theta})_k|^2
 * \right)^{-m_j},
 * \f]
 * where \f$\mathbf{B}\f$ is a vector of the complex data, \f$y(\mathbf{\theta})\f$ is the model for a set of parameters
 * \f$\mathbf{\theta}\f$, \f$M\f$ is the total number of independent data chunks with lengths \f$m_j\f$ and \f$k_0 =
 * \sum_{i=1}^j 1 + m_{i-1}\f$ (with \f$m_0 = 0\f$) is the index of the first data point in each chunk. The product of
 * this for each detector will give the full joint likelihood. In the calculation here the unnecessary proportionality
 * factors are left out (this would effect the actual value of the marginal likelihood/evidence, but since we are only
 * interested in evidence ratios/Bayes factors these factors would cancel out anyway. See \cite DupuisWoan2005 for a
 * more detailed description.
 *
 * In this function data in chunks smaller than a certain minimum length \c chunkMin are ignored.
 *
 * \param vars [in] The parameter values
 * \param data [in] The detector data and initial signal phase template
 * \param get_model [in] The signal template/model function
 *
 * \return The natural logarithm of the likelihood function
 */
REAL8 pulsar_log_likelihood( LALInferenceVariables *vars, LALInferenceIFOData *data,
                             LALInferenceTemplateFunction get_model){
  REAL8 loglike = 0.; /* the log likelihood */
  UINT4 i = 0;
  REAL8Vector *freqFactors = *(REAL8Vector **)LALInferenceGetVariable( data->dataParams, "freqfactors" );

  LALInferenceIFOData *datatemp1 = data, *datatemp2 = data, *datatemp3 = data;

  /* copy model parameters to data parameters */
  while( datatemp1 ){
    LALInferenceCopyVariables( vars, datatemp1->modelParams );
    datatemp1 = datatemp1->next;
  }

  /* get pulsar model */
  while( datatemp2 ){
    get_model( datatemp2 );

    for( i = 0; i < freqFactors->length; i++ ) { datatemp2 = datatemp2->next; }
  }

  while ( datatemp3 ){
    UINT4 j = 0, count = 0, cl = 0;
    UINT4 length = 0, chunkMin;
    REAL8 chunkLength = 0.;
    REAL8 logliketmp = 0.;

    REAL8 sumModel = 0., sumDataModel = 0.;
    REAL8 chiSquare = 0.;
    COMPLEX16 B, M;

    REAL8Vector *sumDat = NULL;
    UINT4Vector *chunkLengths = NULL;

    sumDat = *(REAL8Vector **)LALInferenceGetVariable( datatemp3->dataParams, "sumData" );
    chunkLengths = *(UINT4Vector **)LALInferenceGetVariable( datatemp3->dataParams, "chunkLength" );
    chunkMin = *(INT4*)LALInferenceGetVariable( datatemp3->dataParams, "chunkMin" );

    length = datatemp3->compTimeData->data->length;

    for( i = 0 ; i < length ; i += chunkLength ){
      chunkLength = (REAL8)chunkLengths->data[count];

      /* skip section of data if its length is less than the minimum allowed chunk length */
      if( chunkLength < chunkMin ){
        count++;
        continue;
      }

      sumModel = 0.;
      sumDataModel = 0.;

      cl = i + (INT4)chunkLength;

      for( j = i ; j < cl ; j++ ){
        B = datatemp3->compTimeData->data->data[j];

        M = datatemp3->compModelData->data->data[j];

        /* sum over the model */
        sumModel += creal(M)*creal(M) + cimag(M)*cimag(M);

        /* sum over that data and model */
        sumDataModel += creal(B)*creal(M) + cimag(B)*cimag(M);
      }

      chiSquare = sumDat->data[count];
      chiSquare -= 2.*sumDataModel;
      chiSquare += sumModel;

      logliketmp -= chunkLength*log(chiSquare) + LAL_LN2 * (chunkLength-1.) + gsl_sf_lnfact(chunkLength);

      count++;
    }
    loglike += logliketmp;
    datatemp3 = datatemp3->next;
  }
  return loglike;
}