/********************************************************************************************************
 * 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.
 ***********************************************************************************************************/

/* coddpscan.c
 *
 * ER, Sun Dec  9 16:30:34 CST 2001 [St. Louis]
 * 
 * dynamic programming (viterbi and forward) with the CODmodel scanning version
 *
 * calculates:
 *                       P(seqX,seqY \pi^* | CODmodel)  [viterbi algorithm; \pi^* = best path ]
 *              \sum_\pi P(seqX,seqY \pi   | CODmodel)  [forward algorithm ]
 * 
 */

#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"

static double bestC33diagscanfast(int win, int i, int imod, int l, 
				  SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				  struct coddpxfast_s *dp, 
				  struct codmodel_s   *cod);
static double bestCediagscanfast(int leg, int win, int i, int imod, int l, 
				 SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				 struct coddpscanfast_s *dp, 
				 struct codmodel_s *cod, 
				 struct nullmodel_s *null, 
				 struct ali_s *ali, 
				 int logodds, 
				 struct endscan_s *othends);
static void tracebackCODdiagscanfast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				     int leg, int win, int st, int stmod, int l, int lmax, 
				     double score,  
				     struct codmodel_s *cod, 
				     struct coddpscanfast_s *dp,
				     struct nullmodel_s *null, 
				     struct ali_s *ali, 
				     int alignment, 
				     int logodds,
				     int revstrand,
				     int traceback, 
				     struct end3scan_s *ends);
static void traceCediagscanfast(int leg, int win, int i, int imod, int l, float cur_sc,
				SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				struct coddpscanfast_s *dp, 
				struct codmodel_s *cod, 
				struct nullmodel_s *null, 
				struct ali_s *ali,
				int logodds, 
				int *ret_st, 
				int *ret_i, 
				struct end3scan_s *ends);
     
/* Function: AllocDpDiagScanCOD()
 * Date:     ER, Mon Dec 10 10:10:59 CST 2001 [St. Louis]
 *
 * Purpose:  Allocates memory for the dp matrices of the COD model - scanning diagonal DP
 *
 * Returns:  coddp are allocated
 */
struct coddpscanfast_s *
AllocDpDiagScanFastCOD(int L)
{
  struct coddpscanfast_s *coddpscan;       /* structure with dp matrices   */
  int                 i, d;

  coddpscan = (struct coddpscanfast_s *) MallocOrDie (sizeof(struct coddpscanfast_s));

  coddpscan->cob = AllocDpDiagScanFastOTH(L);
  coddpscan->coj = AllocDpDiagScanFastOTH(L);
  coddpscan->coe = AllocDpDiagScanFastOTH(L);

  coddpscan->cod = (struct coddpxfast_s *) MallocOrDie (sizeof(struct coddpxfast_s));

  coddpscan->cod->COJ    = (double **) MallocOrDie (sizeof(double *) * L);
  coddpscan->cod->COJ[0] = (double  *) MallocOrDie (sizeof(double  ) * L * (L+1));

  for (i = 1; i < L; i++) 
    coddpscan->cod->COJ[i] = coddpscan->cod->COJ[0] + i*(L+1);
  
  for (i = 0; i < L; i++)  
    for (d = 0; d <= L; d++) 
      coddpscan->cod->COJ[i][d] = -BIGFLOAT;

  coddpscan->cod->cbmx  = (double *) MallocOrDie (sizeof(double) * L);
  coddpscan->cod->c33mx = (double *) MallocOrDie (sizeof(double) * L);
  coddpscan->cod->cemx  = (double *) MallocOrDie (sizeof(double) * L);
  coddpscan->cod->cobmx = (double *) MallocOrDie (sizeof(double) * L);
  coddpscan->cod->cojmx = (double *) MallocOrDie (sizeof(double) * L);
  coddpscan->cod->coemx = (double *) MallocOrDie (sizeof(double) * L);

  for (i = 0; i < L; i++) {
    coddpscan->cod->cbmx[i]  = -BIGFLOAT;
    coddpscan->cod->c33mx[i] = -BIGFLOAT;
    coddpscan->cod->cemx[i]  = -BIGFLOAT;
    coddpscan->cod->cobmx[i] = -BIGFLOAT;
    coddpscan->cod->cojmx[i] = -BIGFLOAT;
    coddpscan->cod->coemx[i] = -BIGFLOAT;
  }
  
  return coddpscan;
}

/* Function: AllocDpDiagScan2COD()
 * Date:     ER, Thu Dec 27 2001 [Zaragoza]
 *
 * Purpose:  Allocates memory for the dp matrices of the COD model - scanning diagonal DP
 *           both strands
 *
 * Returns:  coddpscan2 are allocated
 */
struct coddpscanfast2_s *
AllocDpDiagScanFast2COD(int L)
{
  struct coddpscanfast2_s *coddpscan2;       /* structure with dp matrices   */

  coddpscan2 = (struct coddpscanfast2_s *) MallocOrDie (sizeof(struct coddpscanfast2_s));

  coddpscan2->codscan = AllocDpDiagScanFastCOD(L);
  coddpscan2->docscan = AllocDpDiagScanFastCOD(L);

  return coddpscan2;
}

/* Function: FreeDpXFastCOD()
 * Date:     ER, Thu Jan 17 15:36:40 CST 2002   [St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the COD model
 *
 * Returns:  coddp are freed
 */
void
FreeDpXFastCOD(struct coddpxfast_s *dp)
{
  free(dp->COJ[0]);
  free(dp->COJ);

  free(dp->cbmx);
  free(dp->c33mx);
  free(dp->cemx);
  free(dp->cobmx);
  free(dp->cojmx);
  free(dp->coemx);

  free(dp);
}
 

/* Function: FreeDpScanCOD()
 * Date:     ER,  Mon Dec 10 10:25:26 CST 2001[St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the COD model
 *
 * Returns:  coddp are freed
 */
void
FreeDpScanFastCOD(struct coddpscanfast_s *coddpscan)
{
  FreeDpDiagScanFastOTH(coddpscan->cob);
  FreeDpDiagScanFastOTH(coddpscan->coj);
  FreeDpDiagScanFastOTH(coddpscan->coe);

  FreeDpXFastCOD(coddpscan->cod);

  free(coddpscan);
}

/* Function: FreeDpScan2COD()
 * Date:     ER,  Mon Dec 10 10:25:26 CST 2001[St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the COD model
 *           both strands
 *
 * Returns:  coddpscan2 are freed
 */
void
FreeDpScanFast2COD(struct coddpscanfast2_s *coddpscan2)
{
  FreeDpScanFastCOD(coddpscan2->codscan);
  FreeDpScanFastCOD(coddpscan2->docscan);

  free(coddpscan2);
}


/* Function: ViterbiOTHDiagScanFast() 
 * Date:     ER,  Sun Dec  9 16:31:47 CST 2001[St. Louis] 
 * 
 * Purpose:  Calculates, i \in [start, start+L-1] --- l \in [1, i+1]
 *           mt[i][l] = P(X,Y, begin = start+i-l+1, end = start+i, best align | OTH model) 
 * 
 * Args:     ofp          -- output file 
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-)  
 *           start        -- start position 
 *           L            -- length of sX,sY  
 *           oth          -- oth_model structure 
 *           dp           -- matrices for dynamic programming
 *           ali          -- array with alignment
 *           traceback    -- if true due traceback
 *           alignment    -- if true print alignment
 * 
 * Returns:  void.
 */
double
ViterbiCODDiagScanFast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		       int leg, int win, int st, int stmod, int l, int lmax, struct codmodel_s *cod, 
		       struct coddpscanfast_s *dp,struct nullmodel_s *null, struct ali_s *ali, 
		       int alignment, int logodds, int revstrand, int traceback, int doends, struct end3scan_s *ends)
{
  int     d;
  int     i, imod;            /* relative positions in seqX, seqY    */
  int     imodmin;
  int     iback, ibackmod;
  int     end, endmod;
  int     kmod;
  double  olend, orend;
  double  sc, bestsc; 
  double  score;

  olend = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, revstrand, FALSE, FALSE, ends->oth);
  orend = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COE, dp->coe, revstrand, FALSE, FALSE, ends->oth);

 if (l == 0 || lmax == 0) 
   return olend + cod->t[TCOBCOE] + orend;
 
  i    = st + l - 1;
  imod = (stmod+l-1 > win-1)? stmod+l-1-win : stmod+l-1;
 
  if (imod != i%win) Die("Bad coor transformation in ViterbiRNADiagScan()");  /* paranoia */

  imodmin = (imod-1 < 0)? imod-1+win : imod-1;

  /* state COB
   */
   dp->cod->cobmx[imod] = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, l, l, cod->COB, dp->cob, 
						 revstrand, FALSE, FALSE, ends->oth);
 
  /* state C33
   */
  dp->cod->c33mx[imod] = bestC33diagscanfast(win, i, imod, l, sqinfoX, seqX, sqinfoY, seqY, dp->cod, cod);
  
  /* state Ce
   */
  bestsc = -BIGFLOAT;
  if ((sc = bestCediagscanfast(leg, win, i, imod, l, sqinfoX, seqX, sqinfoY, seqY, dp, cod, null, ali, logodds, ends->oth)) 
      > bestsc) bestsc = sc;
  dp->cod->cemx[imod] = bestsc;

  /* state COJ
   */
  bestsc = -BIGFLOAT;
  for (d = 0; d < l; d++) 
    {
      if ((sc = dp->cod->cemx[(imod-d<0)?imod-d+win:imod-d] + cod->t[TCeCOJ] + dp->cod->COJ[imod][d]) 
	  > bestsc) bestsc = sc;
      
      if (d > 0 && 
	  (sc = dp->cod->cbmx[(imod-d<0)?imod-d+win:imod-d] + cod->t[TCbCe] + cod->t[TCeCOJ] + dp->cod->COJ[imod][d]) 
	  > bestsc) bestsc = sc;
    }
  dp->cod->cojmx[imod] = bestsc;
  
  /* state Cb
   */
  bestsc = -BIGFLOAT;
  if ((sc = cod->t[TCOBCb] + dp->cod->cobmx[imod]) > bestsc) bestsc = sc;
  if ((sc = cod->t[TCOJCb] + dp->cod->cojmx[imod]) > bestsc) bestsc = sc;
  dp->cod->cbmx[imod] = bestsc;
  
  /* state COE [Viterbi Backwards for the COE othermodel]
   *
   *  from iback = st+lmax-l to end of full window. assign to ibackmod
   */
  iback    = st + lmax - l;
  ibackmod = iback%win;

  end    = st + lmax - 1;
  endmod = end%win;

  dp->cod->coemx[ibackmod] = ViterbiBackwardsOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, win, end, endmod, l, l, cod->COE, dp->coe);

  /* state END
   */
  score = -BIGFLOAT;
  if (l == lmax) {
    
    /* special cases the involve no emissions by the flanking OTH models */
    if ((sc = cod->t[TCOBCOE] + dp->cod->coemx[stmod] + olend) > score) score = sc;
    if ((sc = cod->t[TCOBCOE] + dp->cod->cobmx[imod]  + orend) > score) score = sc;
    if ((sc = cod->t[TCeCOE]  + dp->cod->cemx[imod]   + orend) > score) score = sc;
    
    for (d = 0; d < l-1; d++) {     
      
      kmod = (stmod+d>win-1)? stmod+d-win:stmod+d;
      
      if ((sc = dp->cod->cemx[kmod]  + cod->t[TCeCOE]  + dp->cod->coemx[(kmod+1>win-1)? kmod+1-win:kmod+1]) > score) score = sc;
      if ((sc = dp->cod->cobmx[kmod] + cod->t[TCOBCOE] + dp->cod->coemx[(kmod+1>win-1)? kmod+1-win:kmod+1]) > score) score = sc;
      
    }
   
    if (doends || traceback) 
      tracebackCODdiagscanfast (ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, l, lmax, score,
				cod, dp, null, ali, alignment, logodds, revstrand, traceback, ends);

  }
  
  return score;
}


double 
bestC33diagscanfast(int win, int i, int imod, int l, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		    struct coddpxfast_s *dp, struct codmodel_s *cod)
{
  int    x1,  x2,  x3;
  int    xx1, xx2, xx3;
  int    y1,  y2,  y3;
  int    yy1, yy2, yy3;
  int    codX;
  int    codY;
  int    stmod;
  int    imod3;
  int    k, kmod;
  double sc, bestsc;
  double hexa, extra_hexamer;

  bestsc = -BIGFLOAT;

  stmod = (imod-l+1 < 0)? imod-l+1+win : imod-l+1;
  imod3 = (imod-3   < 0)? imod-3+win   : imod-3;

  /* TRIPLETS */
  if (l > 2) 
    { 
      x1 = seqX[i-2];
      x2 = seqX[i-1];
      x3 = seqX[i];
      
      y1 = seqY[i-2];
      y2 = seqY[i-1];
      y3 = seqY[i];

      codX = CODON( x1, x2, x3); 
      codY = CODON( y1, y2, y3); 
      
      if ( isitagap(x1) || isitagap(x2) || isitagap(x3) ||
	   isitagap(y1) || isitagap(y2) || isitagap(y3)   )
	{ 
	  return bestsc;
	}
    
      if (l > 3 &&
	  (sc = cod->t[TCbC33] + cod->pcodon[codX][codY] + dp->cbmx[imod3])
	  > bestsc) bestsc = sc;
      
      for (k = 2; k < l-2; k++) {
	
	kmod = (stmod+k > win-1)? stmod+k-win : stmod+k;

	xx1 = seqX[k-2];
	xx2 = seqX[k-1];
	xx3 = seqX[k];
	
	yy1 = seqY[k-2];
	yy2 = seqY[k-1];
	yy3 = seqY[k];
	
	hexa = 0.0;
	if ( !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) )
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	if ( !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	  hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][codY]);
	
	extra_hexamer = (hexa > 0.0)? -1.0 + LOG2(hexa) : 0.0;
	
	if ((sc = 
	     dp->COJ[imod3][l-3-k]   + 
	     cod->t[TC33Ce]          +  
	     cod->t[TCeCOJ]          +  
	     cod->t[TCOJCb]          +  
	     cod->t[TCbC33]          + 
	     cod->pcodon[codX][codY] + 
	     extra_hexamer           +
	     dp->c33mx[kmod]          ) > bestsc) bestsc = sc;
      }
    }
  return bestsc;
}

double 
bestCediagscanfast(int leg, int win, int i, int imod, int l, 
		   SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		   struct coddpscanfast_s *dp, 
		   struct codmodel_s  *cod, 
		   struct nullmodel_s *null, 
		   struct ali_s       *ali, 
		   int logodds, 
		   struct endscan_s *othends)
{
  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    st;
  int    flag;
  int    stmod;
  int    imod3, imod4;
  double add;
  double sc, bestsc;
  double hexa, extra_hexamer;
  
  st    = i - l + 1;
  stmod = (imod-l+1 < 0)? imod-l+1+win : imod-l+1;
  imod3 = (imod-3   < 0)? imod-3+win   : imod-3;
  imod4 = (imod-4   < 0)? imod-4+win   : imod-4;

  /* Cb --> Ce (not considered here but in COJ)
   */
  bestsc = -BIGFLOAT;

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

      xx1 = (l > 5)? seqX[i-5] : 4;
      xx2 = (l > 4)? seqX[i-4] : 4;
      xx3 = (l > 3)? seqX[i-3] : 4;
      
      yy1 = (l > 5)? seqY[i-5] : 4;
      yy2 = (l > 4)? seqY[i-4] : 4;
      yy3 = (l > 3)? seqY[i-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 (l == 3) 
	add = cod->t[TCOBCb] 
	  + ViterbiOTHDiagScanFast(stdout, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, FALSE,  FALSE, FALSE, othends);
      else 
	add = dp->cod->cbmx[imod3];

      add += extra_hexamer;
 
      if (iscomplete(cod5X)) 
	{
	  if (iscomplete(cod5Y)) {
	    /* Cb --> C33 --> Ce 
	     */
	    tr1 = TCbC33; tr2 = TC33Ce; 
	    if ((sc = 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 = 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  = 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 = 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  = 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 = 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 = 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 = 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 = cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codY, cod, null, logodds)) 
		> bestsc) bestsc = sc;
	  }
	}
      bestsc += add;
    }

  /* QUADRUPLETS */
  if (l > 3) 
    {
      x1 = seqX[i-3];
      x2 = seqX[i-2];
      x3 = seqX[i-1];
      x4 = seqX[i]; /* for codons of four */
      
      y1 = seqY[i-3];
      y2 = seqY[i-2];
      y3 = seqY[i-1];
      y4 = seqY[i]; /* 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 = (l > 6)? seqX[i-6] : 4;
      xx2 = (l > 5)? seqX[i-5] : 4;
      xx3 = (l > 4)? seqX[i-4] : 4;
      
      yy1 = (l > 6)? seqY[i-6] : 4;
      yy2 = (l > 5)? seqY[i-5] : 4;
      yy3 = (l > 4)? seqY[i-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 (l == 4) 
	 add =  cod->t[TCOBCb] 
	   + ViterbiOTHDiagScanFast(stdout, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, FALSE, FALSE, FALSE, othends);
      else 
	add = dp->cod->cbmx[imod4];

       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]) + ((logodds)?0: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] + 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");
	  }
	}
    }
  return bestsc;
}

/* Function: tracebackCODdiagscanfast()
 * Date:     ER, Tue Nov 26 13:48:13 CST 2002 [St. Louis]
 *
 * Purpose:  Traceback of best align with viterbi 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 vitebi algorithm.
 */
void
tracebackCODdiagscanfast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
			 int leg, int win, int st, int stmod, int l, int lmax, 
			 double score,  
			 struct codmodel_s *cod, 
			 struct coddpscanfast_s *dp,
			 struct nullmodel_s *null, 
			 struct ali_s *ali, 
			 int alignment, 
			 int logodds, 
			 int revstrand, 
			 int traceback, 
			 struct end3scan_s *ends)

{
  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, imod;                /* position in seqX, seqY                 */
  int    kmod;                   /* position in seqX, seqY                 */
  int    prv_i;                  /* position in seqX, seqY                 */
  int    d, end;                 /* position in seqX, seqY                 */
  int    len;
  int    cur_x, cur_y;           /* nucleotides at those positions         */
  int    cur_st, prv_st;
  double sc, cur_sc, prv_sc;    /* do the comparisons */
  double olend, orend;
  int    flag;
  int    lc = 0;                 /* index for the counting of local COD regions */
  int    verbose = FALSE;
  int    x;
  

  if (score <= -BIGFLOAT*BIGFLOAT || score >= BIGFLOAT*BIGFLOAT) 
    Die ("in tracebackCODdiagscanfast(). Wallace Shawn says: 'Inconceivable score' %f", score);

  olend = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, FALSE, FALSE, FALSE, ends->oth);  
  orend = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COE, dp->coe, FALSE, FALSE, FALSE, ends->oth);

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

  end = st + l - 1;

  if (traceback) {
    if (revstrand)  fprintf(ofp, "\nCOD traceback [REVSTRAND] (diagonal viterbi) [start= %d, end = %d]\n", leg-1-end, leg-1-st);
    else            fprintf(ofp, "\nCOD traceback (diagonal viterbi) [start= %d, end = %d]\n", st, end);
  }

  cur_tr = AttachTracer(tr, end, stCOE); 
  PushTracerstack(dolist, cur_tr);
  prv_sc = score;
  
  while ((cur_tr = PopTracerstack(dolist)) != NULL)
   {
     i    = cur_tr->emit;
     imod = i%win;

     len = i - st + 1;

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

     cur_sc = prv_sc;
     cur_st = cur_tr->type;

     if (traceback) fprintf(ofp,"tracing %s (%d) [%d %d] %f \n", cstNAME[cur_st], i, cur_x, cur_y, cur_sc);
     
     switch (cur_st){

     case stCOB: 
       sc = ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, len, len, cod->COB, dp->cob, 
				   FALSE, FALSE, FALSE, ends->oth);

       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = -1;

	   ends->cod->lend[lc] = i + 1;

	   if (traceback) fprintf(ofp,"END COD traceback\n\n");  
	   break;
	 }
       else Die ("invalid traceback %s in ViterbiCODdiagscanfast() pos: %d (%f, %f)", cstNAME[cur_st], i, cur_sc, sc);
       break;
      
     case stCOJ: 
       flag = FALSE;
       for (d = 0; d < len; d++) {
	 sc = cod->t[TCeCOJ] + dp->cod->cemx[(imod-d<0)?imod-d+win:imod-d] + dp->cod->COJ[imod][d];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = i-d; 
	     prv_st = stCe;
	     prv_sc = dp->cod->cemx[(imod-d<0)?imod-d+win:imod-d];

	     if (d > 0) {
	       ends->cod->lend[lc++] = i;
	       ends->cod->rend[lc]   = prv_i-1;
	     }

	     flag   = TRUE;
	     break;
	   }

	 sc = dp->cod->cbmx[(imod-d<0)?imod-d+win:imod-d] + cod->t[TCbCe] + cod->t[TCeCOJ] + dp->cod->COJ[imod][d];
	 if (!flag &&
	     d > 0 && 
	     cur_sc < sc+MARGIN && cur_sc > sc-MARGIN)
	   {
	     prv_i  = i-d; 
	     prv_st = stCb;
	     prv_sc = dp->cod->cbmx[(imod-d<0)?imod-d+win:imod-d];

	     flag   = TRUE;
	     break; 
	   }
       }
       if (!flag) Die ("invalid traceback %s in ViterbiCODdiagscanfast() pos: %d", cstNAME[cur_st], i);
       break;
      
     case stCOE: 
       flag = FALSE;
       
       sc = cod->t[TCOBCOE] + dp->cod->coemx[stmod] + olend;
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_i  = -1; 
	   prv_st = stCOB;
	   prv_sc = olend;
	   flag   = TRUE;
	   break;
	 }

       sc = cod->t[TCOBCOE] + dp->cod->cobmx[imod]  + orend;
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_i  = i; 
	   prv_st = stCOB;
	   prv_sc = dp->cod->cobmx[imod];
	   flag   = TRUE;
	   break;
	 }

       sc = cod->t[TCeCOE]  + dp->cod->cemx[imod]   + orend;  
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
 	 {
	   prv_i  = i; 
	   prv_st = stCe;
	   prv_sc = dp->cod->cemx[imod];
	   
	   ends->cod->rend[lc] = i-1;
	   
	   flag   = TRUE;
	   break;
	 }
     
       for (d = 0; d < len-1; d++) {
	 
	 kmod = (stmod+d>win-1)? stmod+d-win:stmod+d;
	 
	 sc = dp->cod->cemx[kmod]  + cod->t[TCeCOE]  + dp->cod->coemx[(kmod+1>win-1)? kmod+1-win:kmod+1];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i = st+d;
	     prv_st = stCe;
	     prv_sc = dp->cod->cemx[kmod];

	     ends->cod->rend[lc] = prv_i;
	     
	     flag   = TRUE;
	     break;
	   }
	 
	 sc = dp->cod->cobmx[kmod] + cod->t[TCOBCOE] + dp->cod->coemx[(kmod+1>win-1)? kmod+1-win:kmod+1];
	 if (!flag &&
	     cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i = st+d;
	     prv_st = stCOB;
	     prv_sc = dp->cod->cobmx[kmod];

	     flag   = TRUE;
	     break;
	   }
       }
       if (!flag) Die ("invalid traceback %s in ViterbiCODdiagscanfast() pos: %d", cstNAME[cur_st], i);
       break;
       
     case stCb: 
       flag = FALSE;
       
       prv_i = i;

       if (i < st) {
	 sc  = cod->t[TCOBCb] + olend;
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_st = stCOB;
	     prv_sc = olend;
	     
	     flag   = TRUE;
	     break;
	   }
       }
       
       sc = cod->t[TCOBCb] + dp->cod->cobmx[imod];
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = stCOB;
	   prv_sc = dp->cod->cobmx[imod];
	   
	   flag   = TRUE;
	   break;
	 }

       sc = cod->t[TCOJCb] + dp->cod->cojmx[imod];
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = stCOJ;
	   prv_sc = dp->cod->cojmx[imod];
	   flag   = TRUE;

	   break;
	 }
       if (!flag) Die ("invalid traceback %s in ViterbiCODdiagscanfast() pos: %d", cstNAME[cur_st], i);
       break;

     case stCe: 
       traceCediagscanfast(leg, win, i, imod, len, cur_sc, sqinfoX, seqX, sqinfoX, seqY, dp, cod, null, ali,
			   logodds, &prv_st, &prv_i, ends);

       if (prv_i < st)
	 prv_sc = cod->t[TCOBCb] + olend;
       else 
	 prv_sc = dp->cod->cbmx[prv_i%win];
       
       if (traceback) fprintf(ofp," %s->%s->%s, %f\n", cstNAME[stCb], cstNAME[prv_st], cstNAME[cur_st], 
			      cur_sc - prv_sc); 
       prv_st = stCb;
       break;
       
     default:
       Die("invalid state in ViterbiCODdiagscanfast()");
     }
     
     if (prv_st != -1) {
       if (cur_st != stCe) 
	 if (traceback) 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));
     }

     if (lc >= MAX_NUM_ENDS) Die(" Too many ends in COD traceback. Increase parameter MAX_NUM_ENDS");

   }

  if (verbose || traceback) {
    printf("COD ends [%d %d]\n", st, end);
    for (x = 0; x < MAX_NUM_ENDS; x++)
      if (ends->cod->lend[x] > -1)
	printf("(%d..%d) ", ends->cod->lend[x], ends->cod->rend[x]);
    printf("\n");
  }

  FreeTracer(tr);
  FreeTracer(cur_tr);
  FreeTracerstack(dolist);
}




void 
traceCediagscanfast(int leg, int win, int i, int imod, int l, float cur_sc, 
		    SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		    struct coddpscanfast_s *dp, 
		    struct codmodel_s *cod, 
		    struct nullmodel_s *null, 
		    struct ali_s *ali,
		    int logodds, 
		    int *ret_st, 
		    int *ret_i, 
		    struct end3scan_s *ends)
{
  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    flag;
  int    st, stmod;
  int    imod3, imod4;
  double add;
  double hexa, extra_hexamer;
  float  sc;

  prv_st = -5; /* set to some boggus value */

  st    = i - l + 1;
  stmod = (imod-l+1 < 0)? imod-l+1+win : imod-l+1;
  imod3 = (imod-3   < 0)? imod-3+win   : imod-3;
  imod4 = (imod-4   < 0)? imod-4+win   : imod-4;

  /* Cb --> Ce 
   */
  if (cur_sc == (sc = cod->t[TCbCe] + dp->cod->cbmx[imod])) {
    prv_i = i;
    prv_st = stCb;
  }

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

      xx1 = (i > 4)? seqX[i-5] : 4;
      xx2 = (i > 3)? seqX[i-4] : 4;
      xx3 = (i > 2)? seqX[i-3] : 4;
      
      yy1 = (i > 4)? seqY[i-5] : 4;
      yy2 = (i > 3)? seqY[i-4] : 4;
      yy3 = (i > 2)? seqY[i-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 (l == 3) 
	add = cod->t[TCOBCb] 
	  + ViterbiOTHDiagScanFast(stdout, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, 
				   FALSE, FALSE, FALSE, ends->oth);
      else 
	add = dp->cod->cbmx[imod3];
     
      add += extra_hexamer;

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

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

  /* QUADRUPLETS */
  if (l > 3) 
    {
      x1 = seqX[i-3];
      x2 = seqX[i-2];
      x3 = seqX[i-1];
      x4 = seqX[i]; /* for codons of four */
      
      y1 = seqY[i-3];
      y2 = seqY[i-2];
      y3 = seqY[i-1];
      y4 = seqY[i]; /* 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 = (i > 5)? seqX[i-6] : 4;
      xx2 = (i > 4)? seqX[i-5] : 4;
      xx3 = (i > 3)? seqX[i-4] : 4;
      
      yy1 = (i > 5)? seqY[i-6] : 4;
      yy2 = (i > 4)? seqY[i-5] : 4;
      yy3 = (i > 3)? seqY[i-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 (l == 4) 
	 add =  cod->t[TCOBCb] 
	   + ViterbiOTHDiagScanFast(stdout, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, 0, 0, cod->COB, dp->cob, 
				    FALSE, FALSE, FALSE, ends->oth);
      else 
	add = dp->cod->cbmx[imod4];

      add += extra_hexamer;

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

  *ret_i  = prv_i;
  *ret_st = prv_st;
}

