/********************************************************************************************************
 * QRNA - Comparative analysis of biological sequences 
 *         with pair hidden Markov models, pair stochastic context-free
 *        grammars, and probabilistic evolutionary  models.
 *       
 * Version 2.0.3 (MAY 2004)
 *
 * Copyright (C) 2000-2004 Howard Hughes Medical Institute/Washington University School of Medicine
 * All Rights Reserved
 * 
 *     This source code is distributed under the terms of the
 *     GNU General Public License. See the files COPYING and LICENSE
 *     for details.
 ***********************************************************************************************************/

/* evolcodmodel.c
 * 
 *
 * ER, Mon May 17 10:03:12 CDT 2004 [STL, at work with Coro]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <time.h>

#include "funcs.h"
#include "evolfuncs.h"
#include "globals.h"
#include "squid.h"
#include "structs.h"


/* Function: EvolChangeCODProbsNaive()
 *
 * Date:     ER, Sun Aug 22 17:56:19 CDT 2004  [St. Louis at home with Coro ]
 *
 * Purpose:  Modifies the codon probs P(xyz) to match a given background frequencies.
 *
 *          Two methods, 
 *
 *               one is exact but assumes independence
 *               the other is approximate.
 *
 *
 *
 * Args:    
 *
 * Returns:  (void)
 */
void
EvolChangeCODProbsNaive(FILE *ofp, double *targetfreq, double **ret_pnewcod, struct psubs_s *codprob, int isindep, int verbose)
{
  double *pnewcod;
  double *pcond1;
  double *pcond2;
  double *pcond3;
  double *pcond12;
  double *pcond21;
  double *p12;
  double *p23;
  double *p31;
  double *ps1;
  double *ps2;
  double *ps3;
  double *ps1_old;
  double *ps2_old;
  double *ps3_old;
  double  lods12 = 0.0;
  double  lods23 = 0.0;
  double  lods31 = 0.0;
  double  ave_1_error = 0.0;
  double  ave_2_error = 0.0;
  double  ave_3_error = 0.0;
  int     L, LSQ, LC;
  int     xyz;
  int     xy;
  int     x, y, z;

  LC  = codprob->L;
  L   = EXP2(1.0/3.0*LOG2(LC));
  LSQ = L*L;

  /* allocate memoery */
  pnewcod = (double *) MallocOrDie (sizeof(double) * LC);

  if (isindep) {
    for (xyz = 0; xyz < LC; xyz++)
      pnewcod[xyz] = targetfreq[xyz/LSQ] * targetfreq[(xyz%LSQ)/L] * targetfreq[xyz%L];
    
    if (verbose) {
      fprintf(stdout, "Target Codon-marginals probabilities\n");
      PrintVectorProbs(stdout, pnewcod, LC);
    }
    CheckSingleProb(pnewcod, LC); /* paranoia */
    
    *ret_pnewcod = pnewcod;
    return;
  }

  if (verbose) {
    fprintf(stdout, "Target background freqs\n");
    PrintVectorProbs(stdout, targetfreq, L);
  }

  /* allocate memory */
  pcond1  = (double *) MallocOrDie (sizeof(double) * LSQ);
  pcond2  = (double *) MallocOrDie (sizeof(double) * LSQ);
  pcond3  = (double *) MallocOrDie (sizeof(double) * LSQ);
  pcond12 = (double *) MallocOrDie (sizeof(double) * LSQ);
  pcond21 = (double *) MallocOrDie (sizeof(double) * LSQ);
  p12     = (double *) MallocOrDie (sizeof(double) * LSQ);
  p23     = (double *) MallocOrDie (sizeof(double) * LSQ);
  p31     = (double *) MallocOrDie (sizeof(double) * LSQ);
  ps1     = (double *) MallocOrDie (sizeof(double) * L);
  ps2     = (double *) MallocOrDie (sizeof(double) * L);
  ps3     = (double *) MallocOrDie (sizeof(double) * L);
  ps1_old = (double *) MallocOrDie (sizeof(double) * L);
  ps2_old = (double *) MallocOrDie (sizeof(double) * L);
  ps3_old = (double *) MallocOrDie (sizeof(double) * L);
  
  /* Initialize */
  for (xy = 0; xy < LSQ; xy++) {
    pcond1[xy] = 0.0;
    pcond2[xy] = 0.0;
    pcond3[xy] = 0.0;

    pcond12[xy] = 0.0;
    pcond21[xy] = 0.0;

    p12[xy] = 0.0;
    p23[xy] = 0.0;
    p31[xy] = 0.0;
  }
  for (x = 0; x < L; x++) {
    ps1[x] = 0.0;
    ps2[x] = 0.0;
    ps3[x] = 0.0;

    ps1_old[x] = 0.0;
    ps2_old[x] = 0.0;
    ps3_old[x] = 0.0;
  }

  /* calculate marginals*/
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;

    p12[x*L+y] += codprob->pm[xyz];
    p23[y*L+z] += codprob->pm[xyz];
    p31[z*L+x] += codprob->pm[xyz];

    ps1_old[x] += codprob->pm[xyz];
    ps2_old[y] += codprob->pm[xyz];
    ps3_old[z] += codprob->pm[xyz];
  }
  if (verbose) {
    fprintf(stdout, "OLD background freqs\n");
    PrintVectorProbs(stdout, ps1_old, L);
    PrintVectorProbs(stdout, ps2_old, L);
    PrintVectorProbs(stdout, ps3_old, L);
  }
  CheckSingleProb(p12, LSQ);
  CheckSingleProb(p23, LSQ);
  CheckSingleProb(p31, LSQ);

  CheckSingleProb(ps1_old, L);
  CheckSingleProb(ps2_old, L);
  CheckSingleProb(ps3_old, L);

   /* calculate conditionals */
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;
 
    pcond1[y*L+z] = codprob->pm[xyz] / ps1_old[x];
    pcond2[z*L+x] = codprob->pm[xyz] / ps2_old[y];
    pcond3[x*L+y] = codprob->pm[xyz] / ps3_old[z];
  }
  CheckSingleProb(pcond1, LSQ);
  CheckSingleProb(pcond2, LSQ);
  CheckSingleProb(pcond3, LSQ);
    
  for (xy = 0; xy < LSQ; xy++) {
    x = xy/L;
    y = xy%L;

    pcond12[x*L+y] = p12[xy] / ps1_old[x];
    pcond21[y*L+x] = p12[xy] / ps2_old[y];
  }
  for (x = 0; x < L; x ++) {
    CheckSingleProb(pcond12 + x*L, L);
    CheckSingleProb(pcond21 + x*L, L);
  }
  if (verbose) {
    fprintf(stdout, "conditionals\n");
    PrintProbs(stdout, pcond1, LSQ);
    PrintProbs(stdout, pcond2, LSQ);
    PrintProbs(stdout, pcond3, LSQ);
  }

  /* Is P(xyz) \approx p(xy) * p(z) ?
   *
   */
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;

    lods12 += ( LOG2(codprob->pm[xyz]) - LOG2(p12[x*L+y]) - LOG2(ps3_old[z]) ) * codprob->pm[xyz];
    lods23 += ( LOG2(codprob->pm[xyz]) - LOG2(p23[y*L+z]) - LOG2(ps1_old[x]) ) * codprob->pm[xyz];
    lods31 += ( LOG2(codprob->pm[xyz]) - LOG2(p31[z*L+x]) - LOG2(ps2_old[y]) ) * codprob->pm[xyz];
  }

  if (verbose) {
    fprintf(stdout, "Test of independence\n");
    fprintf(stdout, "1 pos independence %f\n", lods23);
    fprintf(stdout, "2 pos independence %f\n", lods31);
    fprintf(stdout, "3 pos independence %f\n", lods12);
  }

  /* Now apply the naive approximate method of changing the marginals but not the conditionals
   */
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;
    pnewcod[xyz] = targetfreq[z] * (pcond12[x*L+y] * targetfreq[x] + pcond21[y*L+x] * targetfreq[y]) / 2.0;
      
  }
  if (verbose) {
    fprintf(stdout, "Target Codon-marginals probabilities\n");
    PrintVectorProbs(stdout, pnewcod, LC);
  }
  CheckSingleProb(pnewcod, LC); /* paranoia */
  
  /* calculate the new marginals*/
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;

    ps1[x] += pnewcod[xyz];
    ps2[y] += pnewcod[xyz];
    ps3[z] += pnewcod[xyz];
  }
  CheckSingleProb(ps1, L);
  CheckSingleProb(ps2, L);
  CheckSingleProb(ps3, L);

  if (verbose) {
    fprintf(stdout, "NEW background freqs\n");
    PrintVectorProbs(stdout, ps1, L);
    PrintVectorProbs(stdout, ps2, L);
    PrintVectorProbs(stdout, ps3, L);    
  }

  if (verbose) {
    fprintf(stdout, "AVERAGE error in COD\n");
    
    for (x = 0; x < L; x ++) {
      ave_1_error += fabs(targetfreq[x] - ps1[x]) * targetfreq[x];
      ave_2_error += fabs(targetfreq[x] - ps2[x]) * targetfreq[x];
      ave_3_error += fabs(targetfreq[x] - ps3[x]) * targetfreq[x];
    }

    fprintf(stdout, "pos 1: %f \n", ave_1_error*100.0);
    fprintf(stdout, "pos 2: %f \n", ave_2_error*100.0);
    fprintf(stdout, "pos 3: %f \n", ave_3_error*100.0);

  }

  free(pcond1);
  free(pcond2);
  free(pcond3);
  free(pcond12);
  free(pcond21);
  free(p12);
  free(p23);
  free(p31);
  free(ps1);
  free(ps2);
  free(ps3);
  free(ps1_old);
  free(ps2_old);
  free(ps3_old);
  
  *ret_pnewcod = pnewcod;
}

/* Function: EvolChangeCODProbsYuAltchul()
 *
 * Date:     ER, Fri Jan 21 09:54:28 CST 2005  [St. Louis]
 *
 * Purpose:  Modifies the codon probs P(xyz) to match a given background frequencies,
 *           using the Yu-ALtchul method PNAS 100 (2003) 15688-15693, 
 *
 *         see details in function Cal_YuAltschul_Modif3DProbs() in evolve.c
 *
 *
 *
 * Args:    
 *
 * Returns:  (void)
 */
void
EvolChangeCODProbsYuAltchul(FILE *ofp, double *targetfreq, double **ret_pnewcod, struct psubs_s *codprob, int verbose)
{
  double *pnewcod;
  double *ps1;
  double *ps2;
  double *ps3;
  double *ps1_old;
  double *ps2_old;
  double *ps3_old;
  double  ave_1_error = 0.0;
  double  ave_2_error = 0.0;
  double  ave_3_error = 0.0;
  int     L, LSQ, LC;
  int     xyz;
  int     x, y, z;

  LC  = codprob->L;
  L   = EXP2(1.0/3.0*LOG2(LC));
  LSQ = L*L;

  if (verbose) {
    fprintf(stdout, "Target background freqs\n");
    PrintVectorProbs(stdout, targetfreq, L);
  }

  /* allocate memoery */
  pnewcod = (double *) MallocOrDie (sizeof(double) * LC);
  ps1     = (double *) MallocOrDie (sizeof(double) * L);
  ps2     = (double *) MallocOrDie (sizeof(double) * L);
  ps3     = (double *) MallocOrDie (sizeof(double) * L);
  ps1_old = (double *) MallocOrDie (sizeof(double) * L);
  ps2_old = (double *) MallocOrDie (sizeof(double) * L);
  ps3_old = (double *) MallocOrDie (sizeof(double) * L);

  /* Initialize */
  for (x = 0; x < L; x++) {
    ps1[x] = 0.0;
    ps2[x] = 0.0;
    ps3[x] = 0.0;

    ps1_old[x] = 0.0;
    ps2_old[x] = 0.0;
    ps3_old[x] = 0.0;
  }

  /* calculate marginals*/
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;

    ps1_old[x] += codprob->pm[xyz];
    ps2_old[y] += codprob->pm[xyz];
    ps3_old[z] += codprob->pm[xyz];
  }
  if (verbose) {
    fprintf(stdout, "OLD background freqs\n");
    PrintVectorProbs(stdout, ps1_old, L);
    PrintVectorProbs(stdout, ps2_old, L);
    PrintVectorProbs(stdout, ps3_old, L);
  }

  CheckSingleProb(ps1_old, L);
  CheckSingleProb(ps2_old, L);
  CheckSingleProb(ps3_old, L);

  /* use the YuAltchul method to modify the probabilities to that they have the background freqs
   */
  CopyVector(pnewcod, codprob->pm, LC);
  Cal_YuAltschul_Modif3DProbs(ofp, pnewcod, targetfreq, targetfreq, targetfreq, L, L, L, verbose);
  
  if (verbose) {
    fprintf(stdout, "Target Codon-marginals probabilities\n");
    PrintVectorProbs(stdout, pnewcod, LC);
  }
  CheckSingleProb(pnewcod, LC); /* paranoia */
  
  /* calculate the new marginals*/
  for (xyz = 0; xyz < LC; xyz++) {
    x = xyz/LSQ;
    y = (xyz%LSQ)/L;
    z = xyz%L;

    ps1[x] += pnewcod[xyz];
    ps2[y] += pnewcod[xyz];
    ps3[z] += pnewcod[xyz];
  }
  CheckSingleProb(ps1, L);
  CheckSingleProb(ps2, L);
  CheckSingleProb(ps3, L);

  if (verbose) {
    fprintf(stdout, "NEW background freqs\n");
    PrintVectorProbs(stdout, ps1, L);
    PrintVectorProbs(stdout, ps2, L);
    PrintVectorProbs(stdout, ps3, L);    
  }

  if (verbose) {
    fprintf(stdout, "AVERAGE error in COD\n");
    
    for (x = 0; x < L; x ++) {
      ave_1_error += fabs(targetfreq[x] - ps1[x]) * targetfreq[x];
      ave_2_error += fabs(targetfreq[x] - ps2[x]) * targetfreq[x];
      ave_3_error += fabs(targetfreq[x] - ps3[x]) * targetfreq[x];
    }

    fprintf(stdout, "pos 1: %f \n", ave_1_error*100.0);
    fprintf(stdout, "pos 2: %f \n", ave_2_error*100.0);
    fprintf(stdout, "pos 3: %f \n", ave_3_error*100.0);

  }

  free(ps1);
  free(ps2);
  free(ps3);
  free(ps1_old);
  free(ps2_old);
  free(ps3_old);

 *ret_pnewcod = pnewcod;
}

/* Function: ConstructCODEmitProbs()
 * Date:     ER,  [St. Louis]
 *
 * Purpose:  construct codon probs
 *
 * Args:     pammodel  - 64x64 AAA..UUUxAAA..UUU joint prob matrix (prealloc)
 *           pm        - 4x4 A..UxA..U joint prob matrix (prealloc)           
 *
 * Returns:  (void)
 *           Fills in other->mem. (already allocated)
 */
void
EvolConstructCODEmitProbs(double **pammodel, double *pmut, double **pcodon, int verbose)
{
  double *p64;
  double *p64_marg;
  double *p4;
  int     x, y;
  int     x1, x2, x3;
  int     y1, y2, y3;

  /* allocate memory */
  p64      = (double *) MallocOrDie (sizeof(double) * 64);
  p64_marg = (double *) MallocOrDie (sizeof(double) * 4);
  p4       = (double *) MallocOrDie (sizeof(double) * 4);
  
  for (x = 0; x < 64; x++) 
    p64[x] = 0.0;
  for (x1 = 0; x1 < 4; x1++) {
    p64_marg[x1] = 0.0;
    p4[x1]       = 0.0;
  }

  for (x1 = 0; x1 < 4; x1++)
    for (y1 = 0; y1 < 4; y1++)
      pmut[idx(x1,y1)] = 0.0;

  /* Marginalize and average over three positions
   */
  for (x1 = 0; x1 < 4; x1++)
    for (x2 = 0; x2 < 4; x2++)
      for (x3 = 0; x3 < 4; x3++)
	for (y1 = 0; y1 < 4; y1++)
	  for (y2 = 0; y2 < 4; y2++)
	    for (y3 = 0; y3 < 4; y3++)
	      {
		pmut[idx(x1,y1)] += pammodel[CODON(x1,x2,x3)][CODON(y1,y2,y3)] / 3.0;
		pmut[idx(x2,y2)] += pammodel[CODON(x1,x2,x3)][CODON(y1,y2,y3)] / 3.0;
		pmut[idx(x3,y3)] += pammodel[CODON(x1,x2,x3)][CODON(y1,y2,y3)] / 3.0;

		/* the p64 marginals */
		p64[CODON(x1,x2,x3)] += pammodel[CODON(x1,x2,x3)][CODON(y1,y2,y3)] / 2.0;
		p64[CODON(y1,y2,y3)] += pammodel[CODON(x1,x2,x3)][CODON(y1,y2,y3)] / 2.0;

	      }
  CheckSingleProb(pmut, 16);
  CheckSingleProb(p64,  64);

  /* the p4 marginals */
  for (x1 = 0; x1 < 4; x1++) 
    for (y1 = 0; y1 < 4; y1++) {
      p4[x1] += pmut[idx(x1,y1)] / 2.0;
      p4[y1] += pmut[idx(x1,y1)] / 2.0;
    }
  if (verbose) {
    fprintf(stdout, "p4 probabilities\n");
    PrintVectorProbs(stdout, p4, 4);
  }
  CheckSingleProb(p4, 4);

  /* the p64_marg marginals */
  for (x1 = 0; x1 < 4; x1++)
    for (x2 = 0; x2 < 4; x2++)
      for (x3 = 0; x3 < 4; x3++) {
	p64_marg[x1] += p64[CODON(x1,x2,x3)] / 3.0;
	p64_marg[x2] += p64[CODON(x1,x2,x3)] / 3.0;
	p64_marg[x3] += p64[CODON(x1,x2,x3)] / 3.0;
      }
  if (verbose) {
    fprintf(stdout, "p64_marg probabilities\n");
    PrintVectorProbs(stdout, p64_marg, 4);
  }
  CheckSingleProb(p64_marg, 4);

  for (x = 0; x < 64; x++)
    for (y = 0; y < 64; y++) 
      pcodon[x][y] = pammodel[x][y];

  CheckDoubleProb(pcodon, 64, 64);

  free(p64);
  free(p64_marg);
  free(p4);
}

/* Function: EvolConstructCODModel()
 * Date:     ER, Mon May 17 12:57:07 CDT 2004  [St. Louis, at work with Coro]
 *
 * Purpose:  Constructs a codingmodel_s
 *
 * Args:     codingparam - the list of parameters that define a codingmodel           
 *
 * Returns:  (void)
 *           fills all prob's for othermodel, log2 form 
 *           (allc'ed here, freed by caller)
 */
void
EvolConstructCODModel(struct psubs_s *pam_star, struct psubs_s *codprob_star, struct psubs_s *mutpxy_star,
		      double *codon_joint, int add_codon, double **hexa, int win, double tfactor, 
		      double *targetfreq, int changefreq, struct codmodel_s *cod, int add_hexamer, int pedantic, int verbose)
{
  struct psubs_s  *codprob;
  struct psubs_s  *mutpxy_cod;
  double         **pammodel;                  /* for historical reasons comes as a double pointer */
  double             ave_len_COB;             /* expected length of the other models */
  double             ave_len_COE;
  double             ave_len_COJ;
  double             ave_len_COB_zero;             /* expected length of the other models */
  double             ave_len_COE_zero;
  double             ave_len_COJ_zero;
  double             ave_len_COB_infty;             /* expected length of the other models */
  double             ave_len_COE_infty;
  double             ave_len_COJ_infty;
  int                L;
  int                i, j;

  if (verbose) printf("Construct COD Model at time = %.4f\n", tfactor);

  /* need to adjust the background frequencies
   */
  /* Here we select the background freq of the rest of the models 
   * according to those of the cod model
  */
  EvolCODProbs(pam_star, codprob_star, tfactor, &codprob, codon_joint, add_codon, targetfreq, TRUE, pedantic, verbose);  
  EvolMutProbs(mutpxy_star, tfactor, &mutpxy_cod, targetfreq, changefreq, pedantic, verbose);

  if (verbose) {
    fprintf(stdout, "mutpxy_cod(t=%f)\n", mutpxy_cod->time);
    PrintProbs(stdout, mutpxy_cod->P, mutpxy_cod->L);
    fprintf(stdout, "mutpxy_cod-marginals(t=%f)\n", mutpxy_cod->time);
    PrintVectorProbs(stdout, mutpxy_cod->pm, mutpxy_cod->L);
    fprintf(stdout, "targetfreq(changefreq=%d)\n", changefreq);
    PrintVectorProbs(stdout, targetfreq, mutpxy_cod->L);
  }

  L = codprob->L;
  pammodel    = (double **) MallocOrDie (sizeof(double *) * L);
  pammodel[0] = (double  *) MallocOrDie (sizeof(double  ) * L * L);
  for (i = 1; i < L; i++) pammodel[i] = pammodel[0] + i*L;

  for (i = 0; i < L; i++)
    for (j = 0; j < L; j++)
      pammodel[i][j] = codprob->P[i*L+j];

  /* the oth model has a paramente tau that set the invers of the average length generated by those models
   * This is how I set them as a few percentages of the length under analysis. This parameter does not evolve
   */
  ave_len_COB_zero = EXP2(0.70*LOG2(win));
  ave_len_COE_zero = EXP2(0.70*LOG2(win));
  ave_len_COJ_zero = EXP2(0.70*LOG2(win));
  
  ave_len_COB       = EXP2(1.00*LOG2(ave_len_COB_zero));
  ave_len_COE       = EXP2(1.00*LOG2(ave_len_COE_zero));
  ave_len_COJ       = EXP2(1.00*LOG2(ave_len_COJ_zero));
  
  ave_len_COB_infty = EXP2(1.00*LOG2(ave_len_COB_zero));
  ave_len_COE_infty = EXP2(1.00*LOG2(ave_len_COE_zero));
  ave_len_COJ_infty = EXP2(1.00*LOG2(ave_len_COJ_zero));
  
  if (verbose) printf("\nConstruct COB model at time = %.4f\n", tfactor);
  EvolConstructOTHModel(mutpxy_star, COBparam, COBparam_zero, COBparam_infty, ave_len_COB, ave_len_COB_zero, ave_len_COB_infty, 
			tfactor, targetfreq, changefreq, cod->COB, pedantic, verbose);
  if (verbose) printf("\nConstruct COJ model at time = %.4f\n", tfactor);
  EvolConstructOTHModel(mutpxy_star, COJparam, COJparam_zero, COJparam_infty, ave_len_COJ, ave_len_COJ_zero, ave_len_COJ_infty, 
			tfactor, targetfreq, changefreq, cod->COJ, pedantic, verbose);
  if (verbose) printf("\nConstruct COE model at time = %.4f\n", tfactor);
  EvolConstructOTHModel(mutpxy_star, COEparam, COEparam_zero, COEparam_infty, ave_len_COE, ave_len_COE_zero, ave_len_COE_infty, 
			tfactor, targetfreq, changefreq, cod->COE, pedantic, verbose);
  
  /* 24 JUL 02
   *
   * The evolutionary COD model is as follows: T = T^A X T^B
   *
   *       - A 2x2 matrix for transitions T^A              Cb      COE                          
   *                                               COB  |  psi    1-psi |
   *                                     T^A =          |               |
   *                                                Ce  | 1-eta    eta  |
   * 
   *
   *               at t=0                               |  1        0   |
   *                                   T^A_0 =          |               |
   *                                                    |  0        1   |
   *
   *
   *      - A 17x17 matrix for transitions T^B                 xi_1    xi_2  ...  xi_16   Ce
   *                                                     Cb | T_b1      ...       T_b6     0  |
   *                                                   xi_1 |  0        ...         0      1  |
   *                                           T^B =     .  |  0        ...         0      1  |
   *                                                     .  |  0        ...         0      1  |
   *                                                     .  |  0        ...         0      1  |
   *                                                 xi_16  |  0        ...         0      1  |
   *
   *                 at t=0                           
   *                                                     Cb | T0_b1     ...       T0_b6    0  |
   *                                                   xi_1 |  0        ...         0      1  |
   *                                         T^B_0 =     .  |  0        ...         0      1  |
   *                                                     .  |  0        ...         0      1  |
   *                                                     .  |  0        ...         0      1  |
   *                                                 xi_16  |  0        ...         0      1  |
   *
   *
   *
   *
   *
   *              parametric family:      T = T_0 exp [tA] with T_0 = T^A_0 X T^B_0
   *
   */
  CODmodelTransitionProbabilities(win, tfactor, cod, pedantic, verbose);  
 
  /* Calculate emission prob's: model.mem[], model.xem[], model.yem[]
   */
  EvolConstructCODEmitProbs(pammodel, cod->pmut, cod->pcodon, verbose);

   /* check prob's add up to one
   */
  CheckCODProbs(cod);
  
  /* convert to Log2 form
   */
  CODToLog2(cod);

  ConstructHexamerProbs(hexa, cod->phexa, add_hexamer);

  /* Print Transition Probabilities if asked for it
   */
  if (verbose) PrintCODTrProbs(cod);
  if (verbose) PrintCODModel(cod);

  /* free memory */
  FreePAMModel(pammodel);
  FreeSubsProbs(codprob);
  FreeSubsProbs(mutpxy_cod);

}

/* Function: MarginalizeCODProbs()
 * Date:     ER, Tue Aug 17 13:56:59 CDT 2004[St. Louis at work, Coro with Maribel]
 *
 * Purpose:  Given a pcod(x,y,x) probability distrubution, 
 *           marginalize to calculate p[x].
 *
 * Args:   
 *
 * Returns:  (void)
 *           Fills in sp (already allocated)
 */
void
MarginalizeCODProbs(double *pcod, double *pmar, int verbose)
{
  int LC = 64;
  int L;
  int L2;
  int x;
  int xyz;

  L = EXP2(LOG2(LC)/3.0);
  L2 = L*L;

  /* initialize
   */
  for (x = 0; x < L; x++)
    pmar[x] = 0.0;

  /* Marginalize and average over Y positions
   */
  for (xyz = 0; xyz < LC; xyz++) {
    pmar[xyz/L2]     += pcod[xyz] / 3.0;
    pmar[(xyz%L2)/L] += pcod[xyz] / 3.0;
    pmar[xyz%L]      += pcod[xyz] / 3.0;
  }

  if (verbose) {
    fprintf(stdout, "COD marginal probabilities\n");
    PrintVectorProbs(stdout, pmar, L);
  }

  CheckSingleProb(pmar, L);
}
