/********************************************************************************************************
 * QRNA - Comparative analysis of biological sequences 
 *         with pair hidden Markov models, pair stochastic context-free
 *        grammars, and probabilistic evolutionary  models.
 *       
 * Version 2.0.0 (JUN 2003)
 *
 * Copyright (C) 2000-2003 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.
 ***********************************************************************************************************/

/* codcorealign.c
 *
 * ER, Wed Jun  2 09:46:02 CDT 1999[St. Louis]
 * 
 * score of a given alignment with the codingmodel
 * 
 * calculates:
 *             P(seqX, seqY, \pi^{given} | codingmodel) 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

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

#ifdef MEMDEBUG
#include "dbmalloc.h"
#endif

static double cod_score_single_frame(int *sX, int *sY, int L, struct codmodel_s *cod);

static double score_Ce(int iabs, int i, int frame, int *seqX, int *seqY, struct codmodel_s *cod, 
		       struct nullmodel_s *null, struct coddp_s *coddp, int logodds);

static double score_with_COD_single_frame(FILE *ofp, int *seqX, int *seqY, int start, int L, int frame, 
					  struct  codmodel_s *cod, struct nullmodel_s *null, 
					  struct coddp_s *coddp, int logodds, int traceback);
static void tracebackCODscore(FILE *ofp, int *seqX, int *seqY, int start, int L, int frame, double score, 
			      struct codmodel_s *cod, struct nullmodel_s *null, struct coddp_s *coddp, int logodds);
static void trace_Ce_score(int iabs, int i,int  frame,  double cur_sc, int *seqX, int *seqY, struct codmodel_s *cod, 
			   struct nullmodel_s *null, struct coddp_s *coddp, int logodds, int *ret_st, int *ret_i);


/* Function: ScoreWithCOD_ungapped()
 * Date:     ER, Sat Jun  5 08:12:12 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a ungapped sequence alignment with coding model.
 *           Assumes equal prior over six frames, and sums.
 *
 * Args:     sX, sY  -- equal length sequences, ACGT- /only/
 *           L       -- lengths of s1,s2 
 *           cod     -- structure that contains the cod model (logodds form)
 *
 * Returns:  logodds likelihood, 
 *           log [P(sX,sY,alignment|codmodel)/P(sX,sY,alignment|nullmodel)]
 */
double
ScoreWithCOD_ungapped(int *seqX, int *seqY, int L, struct codmodel_s *cod, int traceback)
{
  double sc;
  
  sc = ScoreWithPAM(seqX, seqY, L, cod);
  
  return sc;
  
}

/* Function: ScoreWithCOD()
 * Date:     ER, Wed Jul 28 13:01:20 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with COD model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- starting position in s1,s2 
 *           L            -- lengths of s1,s2 from start
 *           codmodel     -- cod_model structure
 *           traceback    -- printf the traceback if asked
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | CODmodel)
 */
void
ScoreWithCOD(FILE *ofp, int *isX, int *isY, int start, int L, struct codmodel_s *cod, 
	     struct nullmodel_s *null, struct coddp_s  *coddp, struct sc2_s *codsc, int logodds, int ones, int traceback)
{
  int          *irX, *irY;     /* reverse complements */
  int           i;		 /* frame counter */
  double        sc[3];

			/* frames 0..2   [frame = i%3] */
  for (i = 0; i < 3; i++) 
    sc[i] = score_with_COD_single_frame(ofp, isX, isY, 0, L, i%3, cod, null, coddp, logodds, traceback);

  codsc->pl = DLog2Sum(sc, i);

  if (!ones) {
    
    /* revcomp */
    irX = MallocOrDie(sizeof(int) * (L+1));
    irY = MallocOrDie(sizeof(int) * (L+1));
    RevComp(irX, isX, L);
    RevComp(irY, isY, L);
    
				/* frames 3..5  [frame = i%3] */
    for (i = 0; i < 3; i++) 
      sc[i] = score_with_COD_single_frame(ofp, irX, irY, 0, L, i%3, cod, null, coddp, logodds, traceback);
    
    codsc->mn = DLog2Sum(sc, i);
    
    free(irX);
    free(irY);
  }
  else codsc->mn = 0.0;
  
}


/* Function: ScoreWithPAM()
 * Date:     ER, Fri Jun 11 16:42:31 CDT 1999 [St. Louis]
 *
 * Purpose:  Score an ungapped sequence alignment with cod model.
 *           Assumes equal prior over six frames, and sums.
 *
 * Args:     s1, s2    -- equal length sequences, ACGT /only/
 *           L         -- lengths of s1,s2 
 *           pammodel  -- [64][64] codon substitution matrix
 *
 * Returns:  log likelihood, log P(s1,s2 | pammodel)
 */
double
ScoreWithPAM(int *isX, int *isY, int L, struct codmodel_s *cod)
{
  double  sc[6];
  int    *irX, *irY;                /* reverse complements */
  double  totsc;
  int     x0,x1,y0,y1;
  int     i;			/* frame counter */

  x0 = isX[0];
  y0 = isY[0];
  x1 = isX[1];
  y1 = isY[1];
  
				/* frames 0..2 */
  i = 0;
  if (L >= 3) { sc[i] = cod_score_single_frame(isX,   isY,   L,   cod); i++; }

  if (L >= 4) { sc[i] = cod->pmut[idx(x0,y0)] + 
		        cod_score_single_frame(isX+1, isY+1, L-1, cod); i++; }

  if (L >= 5) { sc[i] = cod->pmut[idx(x0,y0)] + 
		        cod->pmut[idx(x1,y1)] + 
		        cod_score_single_frame(isX+2, isY+2, L-2, cod); i++;}

			/* revcomp */
  irX = MallocOrDie(sizeof(int) * (L+1));
  irY = MallocOrDie(sizeof(int) * (L+1));
  RevComp(irX, isX, L);
  RevComp(irY, isY, L);
  
  x0 = irX[0];
  y0 = irY[0];
  x1 = irX[1];
  y1 = irY[1];
				/* frames 3..5 */
  if (L >= 3) { sc[i] = cod_score_single_frame(irX,   irY,   L,   cod); i++; }

  if (L >= 4) { sc[i] = cod->pmut[idx(x0,y0)] +
		        cod_score_single_frame(irX+1, irY+1, L-1, cod); i++; }

  if (L >= 5) { sc[i] = cod->pmut[idx(x0,y0)] + 
		        cod->pmut[idx(x1,y1)] + 
		        cod_score_single_frame(irX+2, irY+2, L-2, cod); i++; }

  totsc = DLog2Sum(sc, i);
  totsc -= LOG2(6.0);	/* prior */

  free(irX);
  free(irY);

  return totsc;
}


/* Function: average()
 * Date:     ER, Thu Aug 19 21:56:22 CDT 1999 [Panticosa]
 *
 * Purpose:  Score a codon of the form: x1 x2 x3 
 *                                      y1 -- y3  (the gap can be placed in any of the three positions)
 *           
 * Strategy: marginalize the missing position.
 *           P(x1x2x3,y1-y3 | codmodel) = \sum_x pcodom[x1,x2,x3][y1,x,y3]
 *
 * Args:     gappos  -- position where the gap ocurrs, in this case, gappos = 2
 *           x1      -- first position of the gapped codon, y1
 *           x2      -- second position of the gapped codon, y3
 *           codon   -- COD(x1,x2,x3)
 *           cod     -- structure that contains the cod model 
 *           null    -- structure that contains the null model 
 *           logodds -- true if in logodds mode
 *
 * Returns:  prob, the logodds score of the gapped codon. 
 *           log [P(x1x2x3,y1-y3 | codmodel)/P(x1x2x3,y1-y3 | nullmodel)]
 */
double 
average(int gappos, int x1, int x2, int codon, struct codmodel_s *cod, struct nullmodel_s *null,
	int logodds)
{
  int    x;
  double sc[4];
  double prob;

  if (codon/16 > 3 || (codon%16)/4 > 3 || codon%4 > 3 || x1 > 3 || x2 > 3) 
    Die ("bad codon labeling in average() [codon = %d, x1 = %d x2 = %d] \n", codon, x1, x2);

  for (x = 0; x < 4; x++) {
    if      (gappos == 1) sc[x] = cod->pcodon[CODON(x,x1,x2)][codon];
    else if (gappos == 2) sc[x] = cod->pcodon[CODON(x1,x,x2)][codon];
    else if (gappos == 3) sc[x] = cod->pcodon[CODON(x1,x2,x)][codon];
    else Die ("not gap allowed in codon position %d", gappos);

    if (logodds) sc[x] += null->xem[x];
  }

  prob = DLog2Sum(sc, 4);
  
  return prob;
}

/* Function: average_codon()
 * Date:     ER, Thu Aug 19 22:07:57 CDT 1999 [Panticosa]
 *
 * Purpose: calculate a single codon probability
 *           
 * Strategy: marginalize the other codon.
 *           P(x1x2x3 | codmodel) = \sum_{codon} pcodom[x1,x2,x3][codon]
 *
 * Args:     codon   -- COD(x1,x2,x3)
 *           cod     -- structure that contains the cod model 
 *           null    -- structure that contains the null model 
 *           logodds -- true if in logodds mode
 *
 * Returns:  prob, the logodds score of the gapped codon. 
 *           log [P(codon | codmodel)/P(codon | nullmodel)]
 */
double 
average_codon(int codon, struct codmodel_s *cod, struct nullmodel_s *null, int logodds)
{
  int    x;
  double sc[64];
  double prob;

  if (codon/16 > 3 || (codon%16)/4 > 3 || codon%4 > 3) Die ("bad codon labeling in average_codon()");

  for (x = 0; x < 64; x++) {
    sc[x] = cod->pcodon[x][codon];
    if (logodds) sc[x] += null->xem[x/16] + null->xem[(x%16)/4] + null->xem[x%4]; 
  }
  
  prob = DLog2Sum(sc, 64);
  
  return prob;
}

/* Function: cod_score_single_frame()
 * Date:     ER, Fri Jun 11 10:39:03 CDT 1999 [St. Louis]
 *
 * Purpose:  called by ScoreWithPam().
 *           Scores a single frame, starting with first position
 *
 * Args:     s1, s2    -- equal length sequences, ACGT /only/
 *           L         -- lengths of s1,s2 
 *           cod       -- sructure for COD model
 *
 * Returns:  log likelihood, log P(s1,s2 | pammodel,frame)
 */
double
cod_score_single_frame(int *sX, int *sY, int L, struct codmodel_s *cod)
{
  int x,y;
  int codonX, codonY;		/* lookups for two codons  */
  double sc;

  sc = 0.0;
  for (x = 0; x < L-2; x += 3)	/* skip by triplets */
    {
      codonX = CODON(sX[x], sX[x+1], sX[x+2]); 
      codonY = CODON(sY[x], sY[x+1], sY[x+2]);
      sc += cod->pcodon[codonX][codonY];
    }

 for (y = x; y < L; y++) 
   sc += cod->pmut[idx(sX[y],sY[y])];

  return sc;
}

/* Function: fourcodonX()
 * Date:     ER, Thu Aug 19 06:00:14 CDT 1999 [Panticosa]
 *
 * Purpose:  Score a codon of the form: x1 x2 x3 
 *                                      y1 -- y3  (the gap can be placed in any of the three positions)
 *           by adding one extra position.
 *           that is, by looking at     x1 x2 x3 x4
 *                                      y1 -- y3 y4
 * Strategy: if x4 and y4 aren't gaps      --> C43 = pcodon[x1,x3,x4][y1,y3,y4] + null(xleft)
 *           if x4 is gap and y4 isnot gap --> C33 = pcodon[x1,x2,x3][y1,y3,y4] 
 *           if x4 isnot gap and y4 is gap --> C42 = pcodon[x1,x2,x3][y1,--,y3] + null(x4)
 *                                              or   pcodon[x1,x3,x4][y1,y3,--] + null(xleft)
 *           if x4 and y4 are gap          --> C32 = pcodon[x1,x2,x3][y1,--,y4] (average gap pos)
 *
 * Args:     xleft   -- nucleotide in seqX at the gap, in this case xleft = x2
 *           gapposs -- position where the gap ocurrs, in this case, gappos = 2
 *           codX    -- COD(x1,x3,x4) 
 *           codY    -- COD(y1,y3,y4)
 *           cod     -- structure that contains the cod model 
 *           null    -- structure that contains the null model 
 *           tr1     -- name of the codon transition (TCbCxx) we are at (depends on the nature of x4, y4)
 *           tr2     -- TCxxCe
 *           logodds -- if true we are doing logodds, otherwise just logs.
 *
 * Returns:  sc, the logodds score of the four-position codon. 
 *           log [P(x1x2x3x4,y1y3y4 | codmodel)/P(x1x2x3x4,y1y3y4 | nullmodel)]
 */
double
fourcodonX(int xleft, int gappos, int cod5X, int cod5Y, struct  codmodel_s *cod, 
	   struct nullmodel_s *null, int logodds, int *ret_tr1, int *ret_tr2)
{
  int    x1,x2,x3,x4;
  int    y1,y2,y3,y4;
  int    tr1, tr2;
  int    codX, codY;
  double sc1, sc2, sc;

  x4 = cod5X%5;
  y4 = cod5Y%5;
  


    if (gappos == 1) {
    x1 = xleft;
    y1 = 4;
    x2 = cod5X/25;
    y2 = cod5Y/25;
    x3 = (cod5X%25)/5;
    y3 = (cod5Y%25)/5;
    codX = CODON(x2,x3,x4);
    codY = CODON(y2,y3,y4);
  }
  else if (gappos == 2) {
    x1 = cod5X/25;
    y1 = cod5Y/25;
    x2 = xleft;
    y2 = 4;
    x3 = (cod5X%25)/5;
    y3 = (cod5Y%25)/5;
    codX = CODON(x1,x3,x4);
    codY = CODON(y1,y3,y4);
  }
  else if (gappos == 3) {
    x1 = cod5X/25;
    y1 = cod5Y/25;
    x2 = (cod5X%25)/5;
    y2 = (cod5Y%25)/5;
    x3 = xleft;
    y3 = 4;
    codX = CODON(x1,x2,x4);
    codY = CODON(y1,y2,y4);
   }
  
  if (!isitagap(x4) && !isitagap(y4)) {
    /* Cb --> C43 --> Ce 
     */
    tr1 = TCbC43; tr2 = TC43Ce; 
    sc = cod->pcodon[codX][codY] + ((logodds)?0.:null->xem[xleft]);
  }
  else if (isitagap(x4)  && !isitagap(y4)) {
    /* Cb --> C33 --> Ce 
     */
    tr1 = TCbC33; tr2 = TC33Ce; 
    sc = cod->pcodon[CODON(x1,x2,x3)][codY];
  }
  else if (!isitagap(x4) && isitagap(y4)) {
    /* Cb --> C42 --> Ce 
     */
    tr1 = TCbC42; tr2 = TC42Ce; 
    sc1 = average(3, cod5Y/25, (cod5Y%25)/5, codX, cod, null, logodds) + 
      ((logodds)?0.:null->xem[xleft]);
    sc2 = average(gappos, cod5Y/25, (cod5Y%25)/5, CODON(x1,x2,x3), cod, null, logodds) + 
      ((logodds)?0.:null->xem[x4]);
    sc = sc1 - 1. + LOG2(1. + EXP2(sc2-sc1));
  }
  else if (isitagap(x4)  && isitagap(y4)) {
    /* Cb --> C32 --> Ce 
     */
    tr1 = TCbC32; tr2 = TC32Ce; 
    sc = average(gappos, cod5Y/25, (cod5Y%25)/5, CODON(x1,x2,x3), cod, null, logodds);
  }

  sc += cod->t[tr1] + cod->t[tr2];

  *ret_tr1 = tr1;
  *ret_tr2 = tr2;
  return sc;
}

double
fourcodonY(int yleft, int gappos, int cod5X, int cod5Y, struct  codmodel_s *cod,  
	   struct nullmodel_s *null, int logodds, int *ret_tr1, int *ret_tr2)
{
  int    x1,x2,x3,x4;
  int    y1,y2,y3,y4;
  int    tr1, tr2;
  int    codX, codY;
  double sc1, sc2, sc;

  x4 = cod5X%5;
  y4 = cod5Y%5;

  if (gappos == 1) {
    x1 = 4;
    y1 = yleft;
    x2 = cod5X/25;
    y2 = cod5Y/25;
    x3 = (cod5X%25)/5;
    y3 = (cod5Y%25)/5;
    codX = CODON(x2,x3,x4);
    codY = CODON(y2,y3,y4);
  }
  else if (gappos == 2) {
    x1 = cod5X/25;
    y1 = cod5Y/25;
    x2 = 4;
    y2 = yleft;
    x3 = (cod5X%25)/5;
    y3 = (cod5Y%25)/5;
    codX = CODON(x1,x3,x4);
    codY = CODON(y1,y3,y4);
  }
  else if (gappos == 3) {
    x1 = cod5X/25;
    y1 = cod5Y/25;
    x2 = (cod5X%25)/5;
    y2 = (cod5Y%25)/5;
    x3 = 4;
    y3 = yleft;
    codX = CODON(x1,x2,x4);
    codY = CODON(y1,y2,y4);
   }
  
  if (!isitagap(x4) && !isitagap(y4)) {
    /* Cb --> C34 --> Ce 
     */
    tr1 = TCbC34; tr2 = TC34Ce; 
    sc = cod->pcodon[codX][codY] + ((logodds)?0.:null->yem[yleft]);
  }
  else if (isitagap(x4)  && !isitagap(y4)) {
    /* Cb --> C24 --> Ce 
     */
    tr1 = TCbC24; tr2 = TC24Ce; 
    sc1 = average(3, cod5X/25, (cod5X%25)/5, codY, cod, null, logodds) + 
      ((logodds)?0.:null->yem[yleft]);
    sc2 = average(gappos, cod5X/25, (cod5X%25)/5, CODON(y1,y2,y3), cod, null, logodds) + 
      ((logodds)?0.:null->yem[y4]);
    sc = sc1 - 1. + LOG2(1. + EXP2(sc2-sc1));
  }
  else if (!isitagap(x4) && isitagap(y4)) {
    /* Cb --> C33 --> Ce 
     */
    tr1 = TCbC33; tr2 = TC33Ce; 
    sc =  cod->pcodon[codX][CODON(y1,y2,y3)];
  }
  else if (isitagap(x4)  && isitagap(y4)) {
    /* Cb --> C23 --> Ce 
     */
    tr1 = TCbC23; tr2 = TC23Ce; 
    sc =  average(gappos, cod5X/25, (cod5X%25)/5, CODON(y1,y2,y3), cod, null, logodds);
  }

  sc += cod->t[tr1] + cod->t[tr2];

  *ret_tr1 = tr1;
  *ret_tr2 = tr2;
  return sc;
}

int 
iscomplete (int cod5)
{
  int x1, x2, x3;

  x1 = cod5/25;
  x2 = (cod5%25)/5;
  x3 = cod5%5;
  
  if (x1<4 && x2<4 && x3<4) return 1;
  else return 0;
}

int 
isgap1(int cod5)
{
  int x1, x2, x3;

  x1 = cod5/25;
  x2 = (cod5%25)/5;
  x3 = cod5%5;
  
  if (x1 == 4 && x2 < 4 && x3 < 4) return 1;
  else return 0;
}

int 
isgap2(int cod5)
{
  int x1, x2, x3;

  x1 = cod5/25;
  x2 = (cod5%25)/5;
  x3 = cod5%5;
  
  if (x1 < 4 && x2 == 4 && x3 < 4) return 1;
  else return 0;
}

int 
isgap3(int cod5)
{
  int x1, x2, x3;
  
  x1 = cod5/25;
  x2 = (cod5%25)/5;
  x3 = cod5%5;

  if (x1 < 4 && x2 < 4 && x3 == 4) return 1;
  else return 0;
}

int 
isitagap(int x)
{
  if      (x == 4) return 1;
  else if (x <  4) return 0;
  else { Die("this is not a valid nucleotide %d", x); return -1; }
}

int 
isnocod(int cod5)
{
  if (cod5 == 124) return 1;
  else return 0;
}

int 
miss1(int cod5)
{
  int x1;
  
  x1 = cod5/25;

  if (x1 == 4) return 1;
  else return 0;
}

int 
miss2(int cod5)
{
  int x2;
  
  x2 = (cod5%25)/5;

  if (x2 == 4) return 1;
  else return 0;
}

int 
miss3(int cod5)
{
  int x3;
  
  x3 = cod5%5;

  if (x3 == 4) return 1;
  else return 0;
}

double 
score_Ce(int iabs, int i, int  frame, int *seqX, int *seqY, struct codmodel_s *cod, 
	 struct nullmodel_s *null, struct coddp_s *coddp, int logodds)
{
  int    x1, x2, x3, x4;
  int    y1, y2, y3, y4;
  int    xx1, xx2, xx3;
  int    yy1, yy2, yy3;
  int    tr1, tr2;
  int    codX, cod5X;
  int    codY, cod5Y;
  int    start;
  int    flag;
  double add;
  double sc, bestsc;
  double hexa, extra_hexamer;
  
  start = iabs - i;

  /* Cb[i] --> Ce[i] (taken into account in cojmx)
   */
  bestsc = -BIGFLOAT;
  
  /* TRIPLETS */
  if (i > 1 + frame) 
    { 
      x1 = seqX[iabs-2];
      x2 = seqX[iabs-1];
      x3 = seqX[iabs];
      
      y1 = seqY[iabs-2];
      y2 = seqY[iabs-1];
      y3 = seqY[iabs];
      
      codX  = CODON( x1, x2, x3); 
      codY  = CODON( y1, y2, y3); 
      cod5X = CODON5(x1, x2, x3); 
      cod5Y = CODON5(y1, y2, y3);

      codX  = CODON( x1, x2, x3); 
      codY  = CODON( y1, y2, y3); 
      cod5X = CODON5(x1, x2, x3); 
      cod5Y = CODON5(y1, y2, y3);
      
      xx1 = (iabs > 4)? seqX[iabs-5] : 4;
      xx2 = (iabs > 3)? seqX[iabs-4] : 4;
      xx3 = (iabs > 2)? seqX[iabs-3] : 4;
      
      yy1 = (iabs > 4)? seqY[iabs-5] : 4;
      yy2 = (iabs > 3)? seqY[iabs-4] : 4;
      yy3 = (iabs > 2)? seqY[iabs-3] : 4;

      hexa = 0.0;
      flag = 0;

      if ( !isitagap(x1)  && !isitagap(x2)  && !isitagap(x3)  &&
	   !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) ) 
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag++;
	}
      if ( !isitagap(y1)  && !isitagap(y2)  && !isitagap(y3)  &&
	   !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	{
	  hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][codY]);
	  flag++;
	}
      
      switch (flag) 
	{
	case 0: extra_hexamer = 0.0;               break;
	case 1: extra_hexamer =        LOG2(hexa); break;
	case 2: extra_hexamer = -1.0 + LOG2(hexa); break;
	}
      
      if (i == 2+frame) 
	add = cod->t[TCOBCb] + ScoreWithOTH(stdout, seqX, seqY, start, frame, cod->COB, FALSE);
      else 
	add = coddp->cbmx[i-3];
      
      add += extra_hexamer;
      
      if (iscomplete(cod5X)) 
	{
	  if (iscomplete(cod5Y)) {
   	    
	    /* Cb --> C33 --> Ce 
	     */
	    tr1 = TCbC33; tr2 = TC33Ce; 
	    if (( sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] )
		> bestsc) bestsc = sc;
 	  }
	  
	  else if (isgap1(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce;  
	    if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(1, y2, y3, codX, cod, null, logodds)) 
		> bestsc) bestsc = sc;
	  }
	  
	  else if (isgap2(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce;  
	    if ((sc  = add + cod->t[tr1] + cod->t[tr2] + 
	      average(2, y1, y3, codX, cod, null, logodds))
		> bestsc) bestsc = sc;
	  }
	  
	  else if (isgap3(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce;  
	    if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(3, y1, y2, codX, cod, null, logodds))
		> bestsc) bestsc = sc;
	  }
	  
	  else if (isnocod(cod5Y)) { 
	    /* Cb --> C30 --> Ce 
	     */
	    tr1 = TCbC30; tr2 = TC30Ce;  
	    if ((sc  = add + cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codX, cod, null, logodds))
		> bestsc) bestsc = sc;
	  }

	}
      else if (iscomplete(cod5Y)) 
	{
	  if (isgap1(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	    tr1 = TCbC23; tr2 = TC23Ce;  
	    if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(1, x2, x3, codY, cod, null, logodds)) 
		> bestsc) bestsc = sc;
	  }
	  
	  else if (isgap2(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	    tr1 = TCbC23; tr2 = TC23Ce;  
	    if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(2, x1, x3, codY, cod, null, logodds)) 
		> bestsc) bestsc = sc;
	  }
	  
	  else if (isgap3(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	      tr1 = TCbC23; tr2 = TC23Ce;  
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
		average(3, x1, x2, codY, cod, null, logodds)) 
		> bestsc) bestsc = sc;
	    }

	  else if (isnocod(cod5X)) {
	    /* Cb --> C03 --> Ce 
	     */
	    tr1 = TCbC03; tr2 = TC03Ce;  
	    if ((sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codY, cod, null, logodds)) 
		> bestsc) bestsc = sc;
  	  }
	}
     }
  
  /* QUADRUPLETS */
  if (i > 2+frame) 
    {
      x1 = seqX[iabs-3];
      x2 = seqX[iabs-2];
      x3 = seqX[iabs-1];
      x4 = seqX[iabs]; /* for codons of four */
      
      y1 = seqY[iabs-3];
      y2 = seqY[iabs-2];
      y3 = seqY[iabs-1];
      y4 = seqY[iabs]; /* for codons of four */

      codX  = CODON( x1, x2, x3); 
      codY  = CODON( y1, y2, y3); 
      cod5X = CODON5(x1, x2, x3); 
      cod5Y = CODON5(y1, y2, y3);
      xx1 = (iabs > 5)? seqX[iabs-6] : 4;
      xx2 = (iabs > 4)? seqX[iabs-5] : 4;
      xx3 = (iabs > 3)? seqX[iabs-4] : 4;
      
      yy1 = (iabs > 5)? seqY[iabs-6] : 4;
      yy2 = (iabs > 4)? seqY[iabs-5] : 4;
      yy3 = (iabs > 3)? seqY[iabs-4] : 4;

      hexa = 0.0;
      flag = 0;
      
      if ( !isitagap(x1)  && !isitagap(x2)  && !isitagap(x3)  &&
	   !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) ) 
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag++;
	}
      if ( !isitagap(y1)  && !isitagap(y2)  && !isitagap(y3)  &&
	   !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	{
	  hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][codY]);
	  flag++;
	}

      switch (flag) 
	{
	case 0: extra_hexamer = 0.0;               break;
	case 1: extra_hexamer =        LOG2(hexa); break;
	case 2: extra_hexamer = -1.0 + LOG2(hexa); break;
	}
      
      if (i == 3+frame) 
	add =  cod->t[TCOBCb] + ScoreWithOTH(stdout, seqX, seqY, start, frame, cod->COB, FALSE);
      else 
	add = coddp->cbmx[i-4];

      add += extra_hexamer;

       if (iscomplete(cod5X)) 
	{
	  if      (iscomplete(cod5Y)) {
	    if (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C44 --> Ce 
	       */
	      tr1 = TCbC44; tr2 = TC44Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->xem[x4]+null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if(!isitagap(x4) && isitagap(y4)) {
	      /* Cb --> C43 --> Ce 
	       */
	      tr1 = TCbC43; tr2 = TC43Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->xem[x4]))
		  > bestsc) bestsc = sc;
	    }
	    else if(isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C34 --> Ce 
	       */
	      tr1 = TCbC34; tr2 = TC34Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if(isitagap(x4) && isitagap(y4)) {
	      /* Cb --> C33 --> Ce 
	       */
	      tr1 = TCbC33; tr2 = TC33Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY])
		  > bestsc) bestsc = sc;
	    }
	  }
	  else if (isgap1(cod5Y)) 
	    {
	    if ((sc = add +
		 fourcodonX(x1, 1, CODON5(x2, x3, x4), CODON5(y2, y3, y4), cod, null, 
			    logodds, &tr1, &tr2)) 
		> bestsc) bestsc = sc;
	  }
	  else if (isgap2(cod5Y)) {
	    if ((sc = add +
	      fourcodonX(x2, 2, CODON5(x1, x3, x4), CODON5(y1, y3, y4), cod, null, 
			 logodds, &tr1, &tr2)) 
		> bestsc) bestsc = sc;
	  }
	  else if (isgap3(cod5Y)) {
	    if ((sc = add +
	      fourcodonX(x3, 3, CODON5(x1, x2, x4), CODON5(y1, y2, y4), cod, null, 
			 logodds, &tr1, &tr2)) 
		> bestsc) bestsc = sc;
	  }

	  else if (isnocod(cod5Y)) {
	    if      (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C40 --> Ce 
	       */
	      tr1 = TCbC40; tr2 = TC40Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4]+null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if (!isitagap(x4) &&  isitagap(y4)) {
	      /* Cb --> C40 --> Ce 
	       */
	      tr1 = TCbC40; tr2 = TC40Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?0:null->xem[x4]))
		  > bestsc) bestsc = sc;
	    }
	    else if ( isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C30 --> Ce 
	       */
	      tr1 = TCbC30; tr2 = TC30Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if ( isitagap(x4) &&  isitagap(y4)){
	      /* Cb --> C30 --> Ce 
	       */
	      tr1 = TCbC30; tr2 = TC30Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds))
		  > bestsc) bestsc = sc;
	    }
	  }

	  else if (!isitagap(x4) && !isitagap(y4)) {
	    if      (miss1(cod5Y) && miss2(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(1, y3, y4, CODON(x2,x3,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x1])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(2, y3, y4, CODON(x1,x3,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x2])) 
		> bestsc) bestsc = sc;
	    }
	    else if (miss1(cod5Y) && miss3(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(1, y2, y4, CODON(x2,x3,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x1])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(3, y2, y4, CODON(x1,x2,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x3])) 
		> bestsc) bestsc = sc;
	    }
	    else if (miss2(cod5Y) && miss3(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(2, y1, y4, CODON(x1,x3,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x2])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(3, y1, y4, CODON(x1,x2,x4), cod, null, logodds) + ((logodds)?0.:null->xem[x3])) 
		> bestsc) bestsc = sc;
	    }
	    else Die("wrong number of gaps in this codon \n");
	  }
	}

      else if (iscomplete(cod5Y))
	{
	  if      (isgap1(cod5X)) {
	    if ((sc = add + 
	      fourcodonY(y1, 1, CODON5(x2, x3, x4), CODON5(y2, y3, y4), cod, null, logodds, &tr1, &tr2))
		> bestsc) bestsc = sc;
	  }
	  else if (isgap2(cod5X)) {
	    if ((sc = add +
	      fourcodonY(y2, 2, CODON5(x1, x3, x4), CODON5(y1, y3, y4), cod, null, logodds, &tr1, &tr2)) 
		> bestsc) bestsc = sc;
	  }
	  else if (isgap3(cod5X)) {
	   if (( sc = add +
	      fourcodonY(y3, 3, CODON5(x1, x2, x4), CODON5(y1, y2, y4), cod, null, logodds, &tr1, &tr2)) 
		> bestsc) bestsc = sc;
	  }

	  else if (isnocod(cod5X)) {
	    if      (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C04 --> Ce 
	       */
	      tr1 = TCbC04; tr2 = TC04Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4]+null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if (!isitagap(x4) &&  isitagap(y4)) {
	      /* Cb --> C03 --> Ce 
	       */
	      tr1 = TCbC03; tr2 = TC03Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4]))
		  > bestsc) bestsc = sc;
	    }
	    else if ( isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C04 --> Ce 
	       */
	      tr1 = TCbC04; tr2 = TC04Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?0:null->yem[y4]))
		  > bestsc) bestsc = sc;
	    }
	    else if ( isitagap(x4) &&  isitagap(y4)){
	      /* Cb --> C03 --> Ce 
	       */
	      tr1 = TCbC03; tr2 = TC03Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds))
		  > bestsc) bestsc = sc;
	    }
	  }

	  else if (!isitagap(x4) && !isitagap(y4)) {
	    if      (miss1(cod5X) && miss2(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(1, x3, x4, CODON(y2,y3,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y1])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(2, x3, x4, CODON(y1,y3,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y2])) 
		> bestsc) bestsc = sc;
	    }
	    else if (miss1(cod5X) && miss3(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(1, x2, x4, CODON(y2,y3,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y1])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(3, x2, x4, CODON(y1,y2,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y3])) 
		> bestsc) bestsc = sc;
	    }
	    else if (miss2(cod5X) && miss3(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce; 
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(2, x1, x4, CODON(y1,y3,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y2])) 
		> bestsc) bestsc = sc;
	      if ((sc = add + cod->t[tr1] + cod->t[tr2] +
		average(3, x1, x4, CODON(y1,y2,y4), cod, null, logodds) + ((logodds)?0.:null->yem[y3])) 
		> bestsc) bestsc = sc;
	    }
	    else Die("wrong number of gaps in this codon");
	  }
	}
    }

  /* Ce[i-1] --> coj[i] --> Cb[i] --> Ce[i] (if nothing else worked)
   * this shifts the frame. I don't want to allow this in a globlal scoring model
   *
  if ((sc = coddp->cemx[i-1] + cod->t[TCeCOJ] + cod->t[TCOJCb] + cod->t[TCbCe] + 
       ScoreWithOTH(stdout, seqX, seqY, iabs, 1, cod->COJ, FALSE)) > bestsc) 
    bestsc = sc;
   */
  

  return bestsc;
}



/* Function: score_with_COD_single_frame()
 *
 * Date:     ER, Wed Feb 23 10:54:56 CST 2000 [St. Louis]
 *
 * Purpose:  called by ScoreWithCOD().
 *           Scores a single frame from the "start" position.
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           L            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY | pammodel,frame). prints traceback
 */
double
score_with_COD_single_frame(FILE *ofp, int *seqX, int *seqY, int start, int L, int frame, 
			    struct codmodel_s *cod, struct nullmodel_s *null, 
			    struct coddp_s  *coddp, int logodds, int traceback)
{
  int     i;		          /* relative positions in seqX, seqY      */
  int     d;                      /* nucleotides left at the end of frame  */
  int     iabs;		          /* absolute positions in seqX, seqY      */
  double  score;                  /* dp->cod->coemx[L-1]                   */

  
    if (L == 0) 
    return cod->t[TCOBCOE] + ScoreWithOTH(ofp, seqX, seqY, 0, 0, cod->COB, FALSE) +
      ScoreWithOTH(ofp, seqX, seqY, 0, 0, cod->COE, FALSE);
  
  else if (L < 3+frame) 
    return ScoreWithOTH(ofp, seqX, seqY, 0, L, cod->COB, FALSE) 
      + cod->t[TCOBCb]
      + cod->t[TCbCe] 
      + cod->t[TCeCOE] +
      ScoreWithOTH(ofp, seqX, seqY, L-1, 0, cod->COE, FALSE);
  
  for (i = 0; i < L; i++) { 
    iabs = i + start;

    /* state COB
     */
    if (i <  frame) 
      coddp->cobmx[i] = ScoreWithOTH(ofp, seqX, seqY, iabs, i+1, cod->COB, FALSE);
    else 
      coddp->cobmx[i] = -BIGFLOAT;

    /* state Ce
     */
    coddp->cemx[i] = score_Ce(iabs, i, frame, seqX, seqY, cod, null, coddp, logodds);

    /* state COJ (just pass by this model)
     */
    coddp->cojmx[i] = coddp->cemx[i] + cod->t[TCeCOJ] + 
      ScoreWithOTH(ofp, seqX, seqY, iabs, 0, cod->COJ, FALSE);
   
    /* state Cb
     */
    if (i < frame) 
      coddp->cbmx[i] = cod->t[TCOBCb] + coddp->cobmx[i];
    else 
      coddp->cbmx[i] = cod->t[TCOJCb] + coddp->cojmx[i];
    
  } /* while i */
  
  /* state COE[L-1]
   */
  d = (L-frame) % 3; /* nucleotides extra at the end of frame */
  score = ScoreWithOTH(ofp, seqX, seqY, start+L-d, d, cod->COE, FALSE) + 
    cod->t[TCeCOE]  + coddp->cemx[L-1-d];
  
  if (score > BIGFLOAT) Die ("something when wrong scoring the COD model global (score = %f)", score);

  /* calculate and print the traceback
   */
  if (traceback)
    tracebackCODscore(ofp, seqX, seqY, start, L, frame, score, cod, null, coddp, logodds);
 
  return score;
}

/* Function: tracebackCODscore()
 * Date:     ER, Wed Feb 23 12:43:38 CST 2000 [St. Louis]
 *
 * Purpose:  Traceback of best align with score algorith for COD model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  void. prints the traceback for the score algorithm.
 */
void
tracebackCODscore(FILE *ofp, int *seqX, int *seqY, int start, int L, int frame, double score, 
		  struct codmodel_s *cod, struct nullmodel_s *null, struct coddp_s *coddp, int logodds)
{
  struct tracer_s      *tr;      /* the traceback tree under construction  */
  struct tracer_s      *cur_tr;  /* ptr to node of tr we're working on     */
  struct tracerstack_s *dolist;  /* pushdown stack of active tr nodes      */
  int    i, iabs, prv_i;         /* position in seqX, seqY                 */
  int    end;                    /* position in seqX, seqY                 */
  int    d;                      /* nucleotides left at the end of frame   */
  int    cur_x, cur_y;           /* nucleotides at those positions         */
  int    cur_st, prv_st;
  float  sc, cur_sc, prv_sc;     /* do the comparisons as floats (for precision reasons) */

  /* Initialize
   * Start at end = start + L - 1
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */

  end = start + L - 1;

  fprintf(ofp, "\nScoreWithCOD traceback. frame: %d\n", frame);
  cur_tr = AttachTracer(tr, end, stCOE); 
  PushTracerstack(dolist, cur_tr);
  prv_sc = score;
  
  if (score <= -BIGFLOAT) {
    fprintf (ofp, "It did not score in this frame.\n");
    return;
  }

  while ((cur_tr = PopTracerstack(dolist)) != NULL)
   {
     iabs = cur_tr->emit;
     i    = iabs - start;

     if (i > -1) {
       cur_x = seqX[iabs];
       cur_y = seqY[iabs];
     }
     else {
       cur_x = -1;
       cur_y = -1;
     }

     cur_sc = prv_sc;
     cur_st = cur_tr->type;
     
     fprintf(ofp,"tracing %s (%d) [%d %d] %f \n", cstNAME[cur_st], i, cur_x, cur_y, cur_sc);
     
     switch (cur_st){
     case stCOB: 
       if (i < frame &&
	   cur_sc == (sc = ScoreWithOTH(stdout, seqX, seqY, start, i+1, cod->COB, FALSE))) {
	 prv_st = -1;
	 break;
       }
       else Die ("invalid traceback in state %s in ScoreWithCOD() %f %f", cstNAME[cur_st], sc, cur_sc);
       
     case stCOJ: 
       if (cur_sc == (sc = coddp->cemx[i] + cod->t[TCeCOJ] + 
		      ScoreWithOTH(ofp, seqX, seqY, iabs, 0, cod->COJ, FALSE))) {
	 prv_i  = iabs; 
	 prv_st = stCe;
	 prv_sc = coddp->cemx[i];
	 break; 
       }
       else Die ("invalid traceback in state %s in ScoreWithCOD()", cstNAME[cur_st]);
       break;
       
     case stCOE: 
       d = (L-frame) % 3;
       if (cur_sc == (sc = cod->t[TCeCOE]  + coddp->cemx[i-d] +
		      ScoreWithOTH(stdout, seqX, seqY, iabs+1-d, d, cod->COE, FALSE))) {
	 prv_i = iabs-d;
	 prv_st = stCe;
	 prv_sc = coddp->cemx[i-d];
	 break;
       }
       else Die ("invalid traceback in state %s in ScoreWithCOD()", cstNAME[cur_st]);
       break;
       
     case stCb: 
       if (i < frame) {
	 if (cur_sc == (sc = cod->t[TCOBCb] + 
			ScoreWithOTH(stdout, seqX, seqY, start, i+1, cod->COB, FALSE))) {
	   prv_i = iabs;
	   prv_st = stCOB;
	   prv_sc = ScoreWithOTH(stdout, seqX, seqY, start, i+1, cod->COB, FALSE);
	   break;
	 }
	 else 
	   Die("invalid traceback in state %s in ScoreWithCOD()", cstNAME[cur_st]);
       }
       else {
	 if (cur_sc == (sc = cod->t[TCOJCb] + coddp->cojmx[i])) {
	   prv_i = iabs;
	   prv_st = stCOJ;
	   prv_sc = coddp->cojmx[i];
	   break;
	 }
	 else 
	   Die("invalid traceback in state %s in ScoreWithCOD()", cstNAME[cur_st]);
       }
       break;

     case stCe: 
       trace_Ce_score(iabs, i, frame, cur_sc, seqX, seqY, cod, null, coddp, logodds, &prv_st, &prv_i);
       fprintf(ofp," %s->%s->%s, %f\n", cstNAME[stCb], cstNAME[prv_st], cstNAME[cur_st], 
	       cur_sc - coddp->cbmx[prv_i-start]); 
       prv_st = stCb;

      if (prv_i == frame-1)
	prv_sc = cod->t[TCOBCb] + 
	  ScoreWithOTH(stdout, seqX, seqY, start, frame, cod->COB, FALSE);
      else 
	prv_sc = coddp->cbmx[prv_i-start];
       break;
       
     default:
       Die("invalid state in traceback of ScoreWithCOD()");
     }
     
     if (prv_st != -1) {
       if (cur_st != stCe) 
	 fprintf(ofp," %s->%s, %f\n", cstNAME[prv_st], cstNAME[cur_st], cur_sc - prv_sc);  
       PushTracerstack(dolist, AttachTracer(cur_tr, prv_i, prv_st));
     }
   }
  FreeTracer(cur_tr);
  FreeTracerstack(dolist);
}




void 
trace_Ce_score(int iabs, int i, int  frame, double cur_sc, int *seqX, int *seqY, struct codmodel_s *cod, 
	       struct nullmodel_s *null, struct coddp_s *coddp, int logodds, int *ret_st, int *ret_i)
{
  int    x1, x2, x3, x4;
  int    y1, y2, y3, y4;
  int    xx1, xx2, xx3;
  int    yy1, yy2, yy3;
  int    tr1, tr2;
  int    codX, cod5X;
  int    codY, cod5Y;
  int    prv_i, prv_st;
  int    start;
  int    flag;
  double add;
  double hexa, extra_hexamer;
  float  sc;

  start = iabs - i;
  prv_st = -1;
  
  /* TRIPLETS */
  if (i > frame + 1) 
    { 
      x1 = seqX[iabs-2];
      x2 = seqX[iabs-1];
      x3 = seqX[iabs];
      
      y1 = seqY[iabs-2];
      y2 = seqY[iabs-1];
      y3 = seqY[iabs];
      
      codX  = CODON( x1, x2, x3); 
      codY  = CODON( y1, y2, y3); 
      cod5X = CODON5(x1, x2, x3); 
      cod5Y = CODON5(y1, y2, y3);

      xx1 = (iabs > 4)? seqX[iabs-5] : 4;
      xx2 = (iabs > 3)? seqX[iabs-4] : 4;
      xx3 = (iabs > 2)? seqX[iabs-3] : 4;
      
      yy1 = (iabs > 4)? seqY[iabs-5] : 4;
      yy2 = (iabs > 3)? seqY[iabs-4] : 4;
      yy3 = (iabs > 2)? seqY[iabs-3] : 4;

      hexa = 0.0;
      flag =  0;
      if ( !isitagap(x1)  && !isitagap(x2)  && !isitagap(x3)  &&
	   !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) )
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag ++;
	}
      if ( !isitagap(y1)  && !isitagap(y2)  && !isitagap(y3)  &&
	   !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	{
	  hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][codY]);
	  flag ++;
	}

      switch (flag) 
	{
	case 0: extra_hexamer = 0.0;               break;
	case 1: extra_hexamer =        LOG2(hexa); break;
	case 2: extra_hexamer = -1.0 + LOG2(hexa); break;
	}
      
      if (i == frame + 2) 
	add = cod->t[TCOBCb] + ScoreWithOTH(stdout, seqX, seqY, start, frame, cod->COB, FALSE);
      else 
	add = coddp->cbmx[i-3];

      add += extra_hexamer;

      if (iscomplete(cod5X)) 
	{
	  if (iscomplete(cod5Y)) {
	    /* Cb --> C33 --> Ce 
	     */
	    tr1 = TCbC33; tr2 = TC33Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY])) 
	      {
		prv_st = stC33;
		prv_i = iabs-3;
	      }
	    
	  }
	  
	  else if (isgap1(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
			   average(1, y2, y3, codX, cod, null, logodds))) 
	      {
		prv_st = stC32;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isgap2(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce; 
	    if (cur_sc == (sc  = add + cod->t[tr1] + cod->t[tr2] + 
			   average(2, y1, y3, codX, cod, null, logodds))) 
	      {
		prv_st = stC32;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isgap3(cod5Y)) {
	    /* Cb --> C32 --> Ce 
	     */
	    tr1 = TCbC32; tr2 = TC32Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(3, y1, y2, codX, cod, null, logodds))) 
	      {
		prv_st = stC32;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isnocod(cod5Y)) { 
	    /* Cb --> C30 --> Ce 
	     */
	    tr1 = TCbC30; tr2 = TC30Ce; 
	    if (cur_sc == (sc  = add + cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codX, cod, null, logodds))) 
	      {
		prv_st = stC30;
		prv_i = iabs-3;
	      }
	  }

	}
      else if (iscomplete(cod5Y)) 
	{
	  if (isgap1(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	    tr1 = TCbC23; tr2 = TC23Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(1, x2, x3, codY, cod, null, logodds))) 
	      {
		prv_st = stC23;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isgap2(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	    tr1 = TCbC23; tr2 = TC23Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average(2, x1, x3, codY, cod, null, logodds))) 
	      {
		prv_st = stC23;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isgap3(cod5X)) {
	    /* Cb --> C23 --> Ce 
	     */
	    tr1 = TCbC23; tr2 = TC23Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
			   average(3, x1, x2, codY, cod, null, logodds))) 
	      {
		prv_st = stC23;
		prv_i = iabs-3;
	      }
	  }
	  
	  else if (isnocod(cod5X)) {
	    /* Cb --> C03 --> Ce 
	     */
	    tr1 = TCbC03; tr2 = TC03Ce; 
	    if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codY, cod, null, logodds))) 
	      {
		prv_st = stC03;
		prv_i = iabs-3;
	      }
	  }
	}
    }

  /* QUADRUPLETS */
  if (i > frame + 2) 
    {
      x1 = seqX[iabs-3];
      x2 = seqX[iabs-2];
      x3 = seqX[iabs-1];
      x4 = seqX[iabs]; /* for codons of four */
      
      y1 = seqY[iabs-3];
      y2 = seqY[iabs-2];
      y3 = seqY[iabs-1];
      y4 = seqY[iabs]; /* for codons of four */

      codX  = CODON( x1, x2, x3); 
      codY  = CODON( y1, y2, y3); 
      cod5X = CODON5(x1, x2, x3); 
      cod5Y = CODON5(y1, y2, y3);
      
      xx1 = (iabs > 5)? seqX[iabs-6] : 4;
      xx2 = (iabs > 4)? seqX[iabs-5] : 4;
      xx3 = (iabs > 3)? seqX[iabs-4] : 4;
      
      yy1 = (iabs > 5)? seqY[iabs-6] : 4;
      yy2 = (iabs > 4)? seqY[iabs-5] : 4;
      yy3 = (iabs > 3)? seqY[iabs-4] : 4;

      hexa = 0.0;
      flag =  0;
      if ( !isitagap(x1)  && !isitagap(x2)  && !isitagap(x3)  &&
	   !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) )
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag ++;
	}
      if ( !isitagap(y1)  && !isitagap(y2)  && !isitagap(y3)  &&
	   !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	{
	  hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][codY]);
	  flag ++;
	}

      switch (flag) 
	{
	case 0: extra_hexamer = 0.0;               break;
	case 1: extra_hexamer =        LOG2(hexa); break;
	case 2: extra_hexamer = -1.0 + LOG2(hexa); break;
	}
      
      if (i == frame + 3) 
	add = cod->t[TCOBCb] + ScoreWithOTH(stdout, seqX, seqY, start, frame, cod->COB, FALSE);
      else 
	add = coddp->cbmx[i-4];

      add += extra_hexamer;

      if (iscomplete(cod5X)) 
	{
	  if      (iscomplete(cod5Y)) {
	    if (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C44 --> Ce 
	       */
	      tr1 = TCbC44; tr2 = TC44Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->xem[x4]+null->yem[y4])))
		{
		  prv_st = stC44;
		  prv_i = iabs-4;
		}
	    }
	    else if(!isitagap(x4) && isitagap(y4)) {
	      /* Cb --> C43 --> Ce 
	       */
	      tr1 = TCbC43; tr2 = TC43Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->xem[x4])))
		{
		  prv_st = stC43;
		  prv_i = iabs-4;
		}
	    }
	    else if(isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C34 --> Ce 
	       */
	      tr1 = TCbC34; tr2 = TC34Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY] + 
		   ((logodds)?0:null->yem[y4])))
		{
		  prv_st = stC34;
		  prv_i = iabs-4;
		}
	    }
	    else if(isitagap(x4) && isitagap(y4)) {
	      /* Cb --> C33 --> Ce 
	       */
	      tr1 = TCbC33; tr2 = TC33Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + cod->pcodon[codX][codY]))
		{
		  prv_st = stC33;
		  prv_i = iabs-4;
		}
	    }
	  }
	  else if (isgap1(cod5Y)) {
	    if (cur_sc == 
		(sc = add + 
		 fourcodonX(x1, 1, CODON5(x2, x3, x4), CODON5(y2, y3, y4), cod, null, logodds, 
			    &tr1, &tr2)))
	      {
		prv_st = tr1; /*this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      
	  }
	  else if (isgap2(cod5Y)) {

	    if (cur_sc == 
		(sc = add + 
		 fourcodonX(x2, 2, CODON5(x1, x3, x4), CODON5(y1, y3, y4), cod, null, logodds, 
			    &tr1, &tr2))) 
	      {
		prv_st = tr1; /*this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      
	  }
	  else if (isgap3(cod5Y)) {
	    if (cur_sc == 
		(sc = add + 
		 fourcodonX(x3, 3, CODON5(x1, x2, x4), CODON5(y1, y2, y4), cod, null, logodds, 
			    &tr1, &tr2))) 
	      {
		prv_st = tr1; /*this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      
	  }
	  
	  else if (isnocod(cod5Y)) {
	    if      (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C40 --> Ce 
	       */
	      tr1 = TCbC40; tr2 = TC40Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4]+null->yem[y4])))
		{
		  prv_st = stC40;
		  prv_i = iabs-4;
		}
	    }
	    else if (!isitagap(x4) &&  isitagap(y4)) {
	      /* Cb --> C40 --> Ce 
	       */
	      tr1 = TCbC40; tr2 = TC40Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?0:null->xem[x4])))
		{
		  prv_st = stC40;
		  prv_i = iabs-4;
		}
	    }
	    else if ( isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C30 --> Ce 
	       */
	      tr1 = TCbC30; tr2 = TC30Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->yem[y4])))
		{
		  prv_st = stC30;
		  prv_i = iabs-4;
		}
	    }
	    else if ( isitagap(x4) &&  isitagap(y4)){
	      /* Cb --> C30 --> Ce 
	       */
	      tr1 = TCbC30; tr2 = TC30Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codX, cod, null, logodds))) 
		{
		  prv_st = stC30;
		  prv_i = iabs-4;
		}
	    }
	  }
	  
	  else if (!isitagap(x4) && !isitagap(y4)) {
	    if      (miss1(cod5Y) && miss2(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce;
	      if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x1]) +
			     average(1, y3, y4, CODON(x2,x3,x4), cod, null, logodds)))
		{
		  prv_st = stC42;
		  prv_i = iabs-4;
		}      
	      else if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x2]) +
				  average(2, y3, y4, CODON(x1,x3,x4), cod, null, logodds)))
		{
		  prv_st = stC42;
		  prv_i = iabs-4;
		}  
	    }    
	    else if (miss1(cod5Y) && miss3(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce;
		if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x1]) +
			       average(1, y2, y4, CODON(x2,x3,x4), cod, null, logodds))) 
		  {
		    prv_st = stC42;
		    prv_i = iabs-4;
		  }      
		else if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x3]) +
				    average(3, y2, y4, CODON(x1,x2,x4), cod, null, logodds)))
		  {
		    prv_st = stC42;
		    prv_i = iabs-4;
		  }      
	    }
	    else if (miss2(cod5Y) && miss3(cod5Y)) {
	      /* Cb --> C42 --> Ce 
	       */
	      tr1 = TCbC42; tr2 = TC42Ce;
	      if (cur_sc == (sc =  add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x2]) +
			     average(2, y1, y4, CODON(x1,x3,x4), cod, null, logodds)))
		{
		  prv_st = stC42;
		  prv_i = iabs-4;
		}      
	      else if (cur_sc == (sc =  add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->xem[x3]) +
				  average(3, y1, y4, CODON(x1,x2,x4), cod, null, logodds)))
		{
		  prv_st = stC42;
		  prv_i = iabs-4;
		}      
	    }
	    else Die("wrong number of gaps in this codon \n");
	  }
	}
      
      
      else if (iscomplete(cod5Y))
	{
	  if      (isgap1(cod5X)) {
	    if (cur_sc == 
		(sc = add + 
		 fourcodonY(y1, 1, CODON5(x2, x3, x4), CODON5(y2, y3, y4), cod, null, logodds, 
			    &tr1, &tr2))) 
	      {
		prv_st = tr1; /* this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      
	  }
	  else if (isgap2(cod5X)) {
	    if (cur_sc == 
		(sc = add + 
		 fourcodonY(y2, 2, CODON5(x1, x3, x4), CODON5(y1, y3, y4), cod, null, logodds, 
			    &tr1, &tr2))) 
	      {
		prv_st =  tr1; /* this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      	   
	  }
	  else if (isgap3(cod5X)) {
	    if (cur_sc == 
		(sc = add +  
		 fourcodonY(y3, 3, CODON5(x1, x2, x4), CODON5(y1, y2, y4), cod, null, logodds, 
			    &tr1, &tr2))) 
	      {
		prv_st = tr1; /* this is not extrictly correct but it works */
		prv_i = iabs-4;
	      }      
	  }
	  
	  else if (isnocod(cod5X)) {
	    if      (!isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C04 --> Ce 
	       */
	      tr1 = TCbC04; tr2 = TC04Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4]) + ((logodds)?0:null->yem[y4])))
		{
		  prv_st = stC04;
		  prv_i = iabs-4;
		}
	    }
	    else if (!isitagap(x4) &&  isitagap(y4)) {
	      /* Cb --> C03 --> Ce 
	       */
	      tr1 = TCbC03; tr2 = TC03Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?-null->meta:null->xem[x4])))
		{
		  prv_st = stC03;
		  prv_i = iabs-4;
		}
	    }
	    else if ( isitagap(x4) && !isitagap(y4)) {
	      /* Cb --> C04 --> Ce 
	       */
	      tr1 = TCbC04; tr2 = TC04Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds) + 
		   ((logodds)?0:null->yem[y4])))
		{
		  prv_st = stC04;
		  prv_i = iabs-4;
		}
	    }
	    else if ( isitagap(x4) &&  isitagap(y4)){
	      /* Cb --> C03 --> Ce 
	       */
	      tr1 = TCbC03; tr2 = TC03Ce;
	      if (cur_sc == 
		  (sc = add + cod->t[tr1] + cod->t[tr2] + average_codon(codY, cod, null, logodds))) 
		{
		  prv_st = stC03;
		  prv_i = iabs-4;
		}
	    }
	  }
	  
	  else if (!isitagap(x4) && !isitagap(y4)) {
	    if      (miss1(cod5X) && miss2(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce;
	      if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y1]) +
			     average(1, x3, x4, CODON(y2,y3,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	      else if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y2]) +
				  average(2, x3, x4, CODON(y1,y3,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	    }
	    else if (miss1(cod5X) && miss3(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce;
	      if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y1]) +
			     average(1, x2, x4, CODON(y2,y3,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	      else if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y3]) +
				  average(3, x2, x4, CODON(y1,y2,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	    }
	    else if (miss2(cod5X) && miss3(cod5X)) {
	      /* Cb --> C24 --> Ce 
	       */
	      tr1 = TCbC24; tr2 = TC24Ce;
	      if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y2]) +
			     average(2, x1, x4, CODON(y1,y3,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	      else if (cur_sc == (sc = add + cod->t[tr1] + cod->t[tr2] + ((logodds)?0.:null->yem[y3]) +
				  average(3, x1, x4, CODON(y1,y2,y4), cod, null, logodds)))
		{
		  prv_st = stC24;
		  prv_i = iabs-4;
		}      
	    }
	    else Die("wrong number of gaps in this codon");
	  }
	}
    }

  /* Ce[i-1] --> coj[i] --> Cb[i] --> Ce[i] (if nothing else worked)
   * this shifts the frame. I don't want to allow this in a globlal scoring model
   *
  if (cur_sc == (sc = coddp->cemx[i-1] + cod->t[TCeCOJ] + cod->t[TCOJCb] + cod->t[TCbCe] + 
      ScoreWithOTH(stdout, seqX, seqY, iabs, 1, cod->COJ, FALSE)))
      {
	prv_st = stCe;
	prv_i = iabs-1;
      }
   */
  
  if (prv_st == -1) Die ("Could not find the traceback in Ce\n");
  *ret_i = prv_i;
  *ret_st = prv_st;
}



