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

/* coddp.c
 *
 * ER, Tue Jun  1 08:59:10  CDT 1999 [St. Louis]
 * 
 * dynamic programming (Viterbi and Forward) with the codingmodel
 *
 * calculates:
 *                       P(seqX,seqY \pi^* | codingmodel)  [Viterbi algorithm; \pi^* = best path ]
 *              \sum_\pi P(seqX,seqY \pi   | codingmodel)  [Forward algorithm; sum to all paths  ]
 * 
 */

#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 forwardCODdiag_onepass(FILE *ofp, int *seqX, int *seqY, int start, int L, 
				     struct codmodel_s *cod, struct dpd_s *dp, 
				     struct nullmodel_s *null, struct dpsc3_s *sc, int logodds);

static double viterbiCODdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, 
				     int *seqY, int start, int L, struct codmodel_s *cod, 
				     struct dpd_s *dp, struct nullmodel_s *null, struct ali_s *ali, 
				     int alignment, int logodds, int traceback, int doends, struct end3_s *end3);
static double viterbiCODfull_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				     int startX, int startY, int Lx, int Ly, int *ret_leg, int Lw,
				     struct codmodel_s *cod, struct dpf_s *dp, struct nullmodel_s *null, 
				     struct ali_s *ali, int alignment, int logodds, int traceback, int doends, struct end3_s *end3);
     
static double bestC33diag(int iabs, int i, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, struct dpd_s *dp, 
			  struct codmodel_s *cod);
static double bestCediag(int iabs, int i, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
			 struct dpd_s *dp, struct codmodel_s *cod, 
			 struct nullmodel_s *null, struct ali_s *ali, int logodds, struct end_s *othends);
static double forwCbdiag(int i, struct coddp_s *dp, struct codmodel_s *cod, double *sc);

static double bestCe_L2(int iabs, int i, int jabs, int j, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
			int Lx, int Ly, int *leg, int Lw,
			struct dpf_s *dp, struct codmodel_s *cod, 
			struct nullmodel_s *null, struct ali_s *ali, int logodds, struct end3_s *ends);
static double forwCediag(int iabs, int i, int *seqX, int *seqY, struct dpd_s *dp, struct codmodel_s *cod, 
			 struct nullmodel_s *null, int logodds, struct dpsc3_s *sc);

static double forwCOJdiag(int i, int *seqX, int *seqY, struct dpd_s *dp, struct codmodel_s *cod, 
			  struct dpsc3_s *sc);
static double forwCOEdiag(int start, int L, int *seqX, int *seqY, struct dpd_s *dp, 
			  struct codmodel_s *cod, struct dpsc3_s *sc);

static void   tracebackCODdiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
			       int start, int L, double score, struct dpd_s *dp, 
			       struct codmodel_s *cod, struct nullmodel_s *null, 
			       struct ali_s *ali, int alignment, int logodds, int traceback, struct end3_s *ends);
static void   traceCediag(int iabs, int i, float cur_sc, SQINFO sqinfoX, int *seqX, 
			  SQINFO sqinfoY, int *seqY, struct dpd_s *dp, 
			  struct codmodel_s *cod, struct nullmodel_s *null, struct ali_s *ali, 
			  int logodds, int *ret_st, int *ret_i, struct end3_s *ends);

/* Function: AllocDpDiagCOD()
 * Date:     ER, Thu Sep 16 11:15:52 CDT 1999 [St. Louis]
 *
 * Purpose:  Allocates memory for the dp matrices of the COD model - diagonal DP
 *
 * Returns:  coddp are allocated
 */
struct coddp_s *
AllocDpDiagCOD(int L)
{
  struct coddp_s *coddp;       /* structure with dp matrices   */
  int i;

  coddp = (struct coddp_s *) MallocOrDie (sizeof(struct coddp_s));

  coddp->COJ = AllocMatrixDiag(L);

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

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

/* Function: AllocDpFullCOD()
 * Date:     ER, Sun Nov 28 18:01:59 CST 1999 [St. Louis]
 *
 * Purpose:  Allocates memory for the dp matrices of the COD model - full DP
 *
 * Returns:  coddp are allocated
 */
struct coddp_s *
AllocDpFullCOD(int Lx, int Ly)
{
  struct coddp_s *coddp;       /* structure with dp matrices   */
  int             i, j, ij;

  coddp = (struct coddp_s *) MallocOrDie (sizeof(struct coddp_s));

  coddp->COJ = AllocMatrixFull(Lx, Ly);
 
  coddp->cbmx  = (double *) MallocOrDie (sizeof(double) * (Lx+1) * (Ly+1));
  coddp->c33mx = (double *) MallocOrDie (sizeof(double) * (Lx+1) * (Ly+1));
  coddp->cemx  = (double *) MallocOrDie (sizeof(double) * (Lx+1) * (Ly+1));
  coddp->cobmx = (double *) MallocOrDie (sizeof(double) * (Lx+1) * (Ly+1));
  coddp->cojmx = (double *) MallocOrDie (sizeof(double) * (Lx+1) * (Ly+1));

  for (i = 0; i <= Lx; i++) 
    for (j = 0; j <= Ly; j++) {
      ij = i*(Ly+1) + j;
      coddp->cbmx[ij]  = -BIGFLOAT;
      coddp->c33mx[ij] = -BIGFLOAT;
      coddp->cemx[ij]  = -BIGFLOAT;
      coddp->cobmx[ij] = -BIGFLOAT;
      coddp->cojmx[ij] = -BIGFLOAT;
    }
  
  
  return coddp;
}

/* Function: ForwardCODDiag()
 * Date:     ER, Tue Sep 28 10:33:42 CDT 1999 [St. Louis]
 *
 * Purpose:  Calculates \sum_{align} P(X,Y, align | COD model)
 *
 * Strategy: The algorith is O(L) in time because we respect the  gaps
 *               so there is only one "length".
 *           The algorithm is O(L) in memory, but it can be implemented keeping 
 *               only the previous value.
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | CODmodel)
 */
void
ForwardCODDiag(FILE *ofp, int *seqX, int *seqY, int start, int L, struct codmodel_s *cod, 
	       struct dpd_s *dp, struct nullmodel_s *null, struct sc2_s *codsc, int logodds, struct dpsc3_s *sc, int ones)
{
  int *reqX, *reqY;                /* reverse complements */

  codsc->pl = forwardCODdiag_onepass(ofp, seqX, seqY, start, L, cod, dp, null, sc, logodds);

  if (!ones) {
    /* revcomp */
    reqX = (int *) MallocOrDie(sizeof(int) * L);
    reqY = (int *) MallocOrDie(sizeof(int) * L);
    RevComp(reqX, seqX+start, L);
    RevComp(reqY, seqY+start, L);
    
    codsc->mn = forwardCODdiag_onepass(ofp, reqX, reqY, 0, L, cod, dp, null, sc, logodds);
    
    free(reqX);
    free(reqY);
  }
  else
    codsc->mn = 0.0;
  
}

/* Function: forwardCODdiag_onepass()
 * Date:     ER, Thu Sep 16 15:11:53 CDT 1999 [St. Louis]
 *
 * Purpose:  Calculates \sum_{align} P(X,Y, align | COD model)
 *
 * Strategy: The algorith is O(L) in time because we respect the  gaps
 *               so there is only one "length".
 *           The algorithm is O(L) in memory, but it can be implemented keeping 
 *               only the previous value.
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | CODmodel)
 */
double
forwardCODdiag_onepass(FILE *ofp, int *seqX, int *seqY, int start, int L, struct codmodel_s *cod, 
		       struct dpd_s *dp, struct nullmodel_s *null, struct dpsc3_s *sc, int logodds)
{
  int    i, d;		           /* relative positions in seqX, seqY   */
  int    iabs;		           /* absolute positions in seqX, seqY   */
  double score;                    /* dp->cod->coemx[L-1]                */
  double cob, coe;
  int     exact;

  exact = FALSE;  /* if TRUE due the right thing for the O models. It is toooooo slow! */


  cob = ForwardOTHDiag_L(ofp, seqX, seqY, start,   0, cod->COB, dp->oth, sc->oth);
  coe = ForwardOTHDiag_L(ofp, seqX, seqY, start+L, 0, cod->COE, dp->oth, sc->oth);

  if (L == 0) return  cod->t[TCOBCOE] + cob + coe; 

  if (exact) 
    ForwardOTHDiagMatrix(ofp, dp->cod->COJ, seqX, seqY, start, L, cod->COJ, dp->oth, sc->oth);
  else {
    for (i = 0; i < L; i++) {
      iabs = i + start;
      
      dp->cod->COJ[i][0] = ForwardOTHDiag_L(ofp, seqX, seqY, iabs, 0, cod->COJ, dp->oth, sc->oth);
      
    for (d = 1; d <= i+1; d++) 
      if (i == 0 || d == 1) 
	dp->cod->COJ[i][d] = ScoreWithOTH(ofp, seqX, seqY, iabs, d, cod->COJ, FALSE);
      else 
	dp->cod->COJ[i][d] = dp->cod->COJ[i-1][d-1] + ScoreWithOTHMatrix(seqX, seqY, iabs, cod->COJ);
    }
  }
    
  /* state COB
   */
  ForwardOTHDiagVector(ofp, dp->cod->cobmx, seqX, seqY, start, L, cod->COB, dp->oth, sc->oth);
  
  for (i = 0; i < L; i++) {
    iabs = i + start;

    /* state Ce
     */
    dp->cod->cemx[i] = forwCediag(iabs, i, seqX, seqY, dp, cod, null, logodds, sc);
    
    /* state COJ
     */
    dp->cod->cojmx[i] = forwCOJdiag(i, seqX, seqY, dp, cod, sc);

    /* state Cb
     */
    dp->cod->cbmx[i] = forwCbdiag(i, dp->cod, cod, sc->cod);
  }
 
  /* state COE[i=L-1]
   */
  score = forwCOEdiag(start, L, seqX, seqY, dp, cod, sc);
  return score; 
}

/* Function: FreeDpCOD()
 * Date:     ER, Thu Sep 16 11:18:54 CDT 1999 [St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the COD model
 *
 * Returns:  coddp are freed
 */
void
FreeDpCOD(struct coddp_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);
}

void
PatternDpDiagCOD(int L, struct coddp_s *coddp)
{
  int i, d;

  for (i = 0; i < L; i++) {
    coddp->cbmx[i]  = -BIGFLOAT;
    coddp->c33mx[i] = -BIGFLOAT;
    coddp->cemx[i]  = -BIGFLOAT;
    coddp->cobmx[i] = -BIGFLOAT;
    coddp->cojmx[i] = -BIGFLOAT;

    for (d = 0; d <= i; d++) 
      coddp->COJ[i][d] = -BIGFLOAT;
  }
}

/* Function: ViterbiCODDiag()
 * Date:     ER, Tue Sep 28 10:34:55 CDT 1999 [St. Louis]
 *
 * Purpose:  Calculates P(X,Y, best align | COD model) and traceback
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,best align | OTHmodel). prints traceback
 */
void
ViterbiCODDiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, int start, 
	       int L, struct codmodel_s *cod, struct dpd_s *dp, struct nullmodel_s *null, struct sc2_s *codsc,
	       struct ali_s *ali, int alignment, int logodds, int ones, int traceback, 
	       int doends, struct windowends_s *windowends)
{
  int *reqX, *reqY;                /* reverse complements */
  
  codsc->pl = viterbiCODdiag_onepass(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, cod, dp, 
				     null, ali, alignment, logodds, traceback, doends, windowends->fwd);

  if (!ones) {
                                           /* revcomp */
  reqX = (int *) MallocOrDie(sizeof(int) * L);
  reqY = (int *) MallocOrDie(sizeof(int) * L);
  RevComp(reqX, seqX+start, L);
  RevComp(reqY, seqY+start, L);
  
  codsc->mn = viterbiCODdiag_onepass(ofp, sqinfoX, reqX, sqinfoY, reqY, 0, L, cod, dp, 
				     null, ali, alignment, logodds, traceback, doends, windowends->rev);

  free(reqX);
  free(reqY);

  } 
  else  
    codsc->mn = 0.0;
  
}

/* Function: viterbiCODdiag_onepass()
 * Date:     ER, Mon Sep 20 11:01:56 CDT 1999 [St. Louis]
 *
 * Purpose:  Calculates P(X,Y, best align | COD model) and traceback
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,best align | OTHmodel). prints traceback
 */
double
viterbiCODdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		       int start, int L, struct codmodel_s *cod, struct dpd_s *dp, 
		       struct nullmodel_s *null, struct ali_s *ali,
		       int alignment, int logodds, int traceback, int doends, struct end3_s *ends)
{
  int     i, d;		      /* relative positions in seqX, seqY   */
  int     iabs;		      /* absolute positions in seqX, seqY   */
  double  foroth;
  double  sc, bestsc;
  double  score;              /* dp->cod->coemx[L-1]                */
  int     exact;

  exact = FALSE;  /* if TRUE due the right thing for the O models. It is toooooo slow! */
  
  PatternDpDiagCOD(L, dp->cod);

  if (L == 0) 
    return cod->t[TCOBCOE] +
      ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, cod->COB, dp->oth, ali, 
		       FALSE, FALSE, FALSE, FALSE, ends->oth) +
      ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COE, dp->oth, ali, 
		       FALSE, FALSE, FALSE, FALSE, ends->oth); 
  
  if (exact) 
    ViterbiOTHDiagMatrix(ofp, dp->cod->COJ, sqinfoX, seqX, sqinfoY, seqY, start, L, cod->COJ, 
			 dp->oth);
  else {
    for (i = 0; i < L; i++) { 
      iabs = i + start;
      
      dp->cod->COJ[i][0] = ScoreWithOTH(ofp, seqX, seqY, iabs, 0, cod->COJ, FALSE);
      
      for (d = 1; d <= i+1; d++) {
	if (i == 0 || d == 1) 
	  dp->cod->COJ[i][d] = ScoreWithOTH(ofp, seqX, seqY, iabs, d, cod->COJ, FALSE);
	else 
	  dp->cod->COJ[i][d] = dp->cod->COJ[i-1][d-1] + ScoreWithOTHMatrix(seqX, seqY, iabs, cod->COJ); 
      }
    }
  }
  
  /* state COB
   */
  ViterbiOTHDiagVector(ofp, dp->cod->cobmx, sqinfoX, seqX, sqinfoY, seqY, start, L, cod->COB, dp->oth); 

  for (i = 0; i < L; i++) { 
    iabs = i + start;

    /* state C33
     */
    dp->cod->c33mx[i] = bestC33diag(iabs, i, sqinfoX, seqX, sqinfoY, seqY, dp, cod);

    /* state Ce
     */
    bestsc = -BIGFLOAT;
    if ((sc = bestCediag(iabs, i, sqinfoX, seqX, sqinfoY, seqY, dp, cod, null, ali, logodds, ends->oth)) > bestsc) 
      bestsc = sc;
    dp->cod->cemx[i] = bestsc;

    /* state COJ
     */
    bestsc = -BIGFLOAT;
    for (d = 0; d <= i; d++) 
      {
	if ((sc = dp->cod->cemx[i-d] + cod->t[TCeCOJ] + dp->cod->COJ[i][d]) 
	    > bestsc) bestsc = sc;

	if (d > 0 && 
	    (sc = dp->cod->cbmx[i-d] + cod->t[TCbCe] + cod->t[TCeCOJ] + dp->cod->COJ[i][d]) 
	    > bestsc) bestsc = sc;
      }
    dp->cod->cojmx[i] = bestsc;

    /* state Cb
     */
    bestsc = -BIGFLOAT;
    if ((sc = cod->t[TCOBCb] + dp->cod->cobmx[i]) > bestsc) bestsc = sc;
    if ((sc = cod->t[TCOJCb] + dp->cod->cojmx[i]) > bestsc) bestsc = sc;
    dp->cod->cbmx[i] = bestsc;

  } /* while i */
  
  /* state COE[L-1]
   */
  bestsc = cod->t[TCOBCOE] +
    ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, dp->oth, ali, 
		     FALSE, FALSE, FALSE, FALSE, ends->oth) +
    ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, cod->COE, dp->oth, ali, 
		     FALSE, FALSE, FALSE, FALSE, ends->oth);

  for (d = 0; d < L; d++) {
    foroth = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start+L-d, d, cod->COE, 
			      dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);

    if ((sc = foroth + cod->t[TCeCOE]  + dp->cod->cemx[L-1-d])  > bestsc) bestsc = sc;
    if ((sc = foroth + cod->t[TCOBCOE] + dp->cod->cobmx[L-1-d]) > bestsc) bestsc = sc;
  }
  score = bestsc;
  
  /* calculate and print the traceback
   */
  if (doends || traceback)
    tracebackCODdiag(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, score, 
		     dp, cod, null, ali, alignment, logodds, traceback, ends);
  
  return score;
}

/* Function: ViterbiCODFull()
 * Date:     ER, Sat Dec  4 17:43:48 CST 1999 [St. Louis]
 *
 * Purpose:  Calculates P(X,Y, best align | COD model) and traceback
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,best align | OTHmodel). prints traceback
 */
void
ViterbiCODFull(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY,  
	       int startX, int startY, int Lx, int Ly, int *ret_leg, int Lw,
	       struct codmodel_s *cod, struct dpf_s *dp, struct nullmodel_s *null, struct sc2_s *codsc,
	       struct ali_s *ali, int alignment, int logodds, int ones, int traceback, 
	       int doends, struct windowends_s *windowends)
{
  int *reqX, *reqY;                /* reverse complements */
  int  leg;

  codsc->pl = viterbiCODfull_onepass(ofp, sqinfoX, seqX, sqinfoY, seqY, startX, startY, Lx, Ly, &leg, Lw,
				 cod, dp, null, ali, alignment, logodds, traceback, doends, windowends->fwd);

  if (!ones) {
    /* revcomp */
    reqX = (int *) MallocOrDie(sizeof(int) * Lx);
    reqY = (int *) MallocOrDie(sizeof(int) * Ly);
    RevComp(reqX, seqX+startX, Lx);
    RevComp(reqY, seqY+startY, Ly);
    
    codsc->mn = viterbiCODfull_onepass(ofp, sqinfoX, reqX, sqinfoY, reqY, startX, startY, Lx, Ly, &leg, Lw,
				       cod, dp, null, ali, alignment, logodds, traceback, doends, windowends->rev);
    
    free(reqX);
    free(reqY);
  }
  else 
    codsc->mn = 0.0;

  *ret_leg = leg;
}

/* Function: viterbiCODfull_onepass()
 * Date:     ER, Mon Sep 20 11:01:56 CDT 1999 [St. Louis]
 *
 * Purpose:  Calculates P(X,Y, best align | COD model) and traceback
 *
 * Args:     ofp          -- output file
 *           seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           start        -- start position
 *           d            -- length of sX,sY 
 *           codmodel     -- cod_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,best align | OTHmodel). prints traceback
 */
double
viterbiCODfull_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		       int startX, int startY, int Lx, int Ly, int *ret_leg, int Lw,
		       struct codmodel_s *cod, struct dpf_s *dp, struct nullmodel_s *null, 
		       struct ali_s *ali, int alignment, int logodds, int traceback, 
		       int doends, struct end3_s *ends)
{
  int     i, dx, j, dy;		      /* relative positions in seqX, seqY   */
  int     iabs, jabs;		      /* absolute positions in seqX, seqY   */
  int     ij;
  int     dimY;
  int     leg;                        /* length of alignment created        */
  int     pos, dxy;
  double  foroth;
  double  sc, bestsc;
  double  score;              /* dp->cod->coemx[L-1]                */
  double  coj;
  int     exact;

  leg = *ret_leg;

  exact = FALSE;  /* if TRUE due the right thing for the COJ model. It is toooooo slow! */
  
  dimY = Ly + 1;
  
  if (Lx == 0 || Ly == 0) 
    return cod->t[TCOBCOE] +
      ViterbiOTH_L2(ofp, sqinfoX, seqX, sqinfoY, seqY, startX, startY, Lx, Ly, &leg, Lw,
		    cod->COB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth) +
      ViterbiOTH_L2(ofp, sqinfoX, seqX, sqinfoY, seqY, startX+Lx-1, startY+Ly-1, 0, 0, &leg, Lw, 
		    cod->COE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth); 

  if (exact)
    ViterbiOTHFullMatrix(ofp, dp->cod->COJ, sqinfoX, seqX, sqinfoY, seqY, startX, startY, Lx, Ly, Lw,
			 cod->COJ, dp->oth);  
  
  /* state COB
   */
  ViterbiOTHFullVector(ofp, dp->cod->cobmx, sqinfoX, seqX, sqinfoY, seqY, startX, startY, Lx, Ly, cod->COB, dp->oth); 


  for (i = 0; i < Lx; i++) { 
    iabs = i + startX;

    for (j = 0; j < Ly; j++) { 
      jabs = j + startY;

      ij = i*dimY + j;
      
      /* state Ce
       */
      dp->cod->cemx[ij] = bestCe_L2(iabs, i, jabs, j, sqinfoX, seqX, sqinfoY, seqY, Lx, Ly, &leg, Lw,
				    dp, cod, null, ali, logodds, ends);
      /* state COJ
       */
      bestsc = -BIGFLOAT;
      for (dx = 0; dx <= i; dx++) 
	for (dy = 0; dy <= j; dy++) 
	  {
	    if (dx == 0 && dy == 0) {
	      pos = Lx*dimY + Ly;
	      dxy = 0;
	    }
	    else if (dx == 0) {
	      pos = Lx*dimY + j;
	      dxy = dy - 1;
	    }
	    else if (dy == 0) {
	      pos = i*dimY + Ly;
	      dxy = dx - 1;
	    }
	    else {
	      pos = ij;
	      dxy = (dx-1)*(j+1) + dy-1;
	    }
	    
	    if (exact) {
	      if ((sc = dp->cod->cemx[(i-dx)*dimY + j-dy] + cod->t[TCeCOJ]
		   + dp->cod->COJ[pos][dxy]) > bestsc) bestsc = sc;
	      if ((sc = dp->cod->cbmx[(i-dx)*dimY + j-dy] + cod->t[TCbCe] + cod->t[TCeCOJ] 
		   + dp->cod->COJ[pos][dxy]) > bestsc) bestsc = sc;
	    }
	    else {
	      coj = NullOJ(iabs, jabs, dx, dy, seqX, seqY, cod->COJ);
	      
	      if ((sc = dp->cod->cemx[(i-dx)*dimY + j-dy] + cod->t[TCeCOJ]
		   + coj) > bestsc) bestsc = sc;
	      if ((sc = dp->cod->cbmx[(i-dx)*dimY + j-dy] + cod->t[TCbCe] + cod->t[TCeCOJ] 
		   + coj) > bestsc) bestsc = sc;
	    }

	  }
      dp->cod->cojmx[ij] = bestsc;
      
     /* state Cb
       */
      bestsc = -BIGFLOAT;
      if ((sc = cod->t[TCOBCb] + dp->cod->cobmx[ij]) > bestsc) bestsc = sc;
      if ((sc = cod->t[TCOJCb] + dp->cod->cojmx[ij]) > bestsc) bestsc = sc;
      dp->cod->cbmx[ij] = bestsc;
      
   } /* while j */
  } /* while i */
  
  /* state COE[L-1]
   */
  bestsc = -BIGFLOAT;
  for (dx = 0; dx < Lx; dx++) 
    for (dy = 0; dy < Ly; dy++) {

      foroth = ViterbiOTH_L2(stdout, sqinfoX, seqX, sqinfoY, seqY, startX+Lx-dx, startY+Ly-dy, dx, dy, 
			     &leg, Lw, cod->COE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
      if ((sc = foroth + cod->t[TCeCOE]  + dp->cod->cemx [(Lx-1-dx)*dimY+Ly-1-dy]) > bestsc) bestsc = sc;
      if ((sc = foroth + cod->t[TCOBCOE] + dp->cod->cobmx[(Lx-1-dx)*dimY+Ly-1-dy]) > bestsc) bestsc = sc;
    }
  score = bestsc;
  
  /* calculate and print the traceback -- not implemented just yet
  if (traceback)
    tracebackCOD_L2(ofp, sqinfoX, seqX, sqinfoY, seqY, startX, Lx, startY, Ly, score, 
		    dp, cod, null, ali, logodds, alignment);
   */
  
  *ret_leg = leg;
  return score;
}


double 
forwCbdiag(int i, struct coddp_s *dp, struct codmodel_s *cod, double *sc)
{
  int    idx = 0;
  double score;
  
  sc[idx++] = cod->t[TCOBCb] + dp->cobmx[i];
  sc[idx++] = cod->t[TCOJCb] + dp->cojmx[i];

  score = DLog2Sum(sc, idx);
  return score;
}


double 
forwCOJdiag(int i, int *seqX, int *seqY, struct dpd_s *dp, struct codmodel_s *cod, 
	    struct dpsc3_s *sc)
{
  int    d;
  int    idx = 0;
  double ce2coj;
  double score;

  for (d = 0; d <= i; d++) {
    ce2coj = cod->t[TCeCOJ] + dp->cod->COJ[i][d];
    sc->cod[idx++] = ce2coj + dp->cod->cemx[i-d];

    if (d > 0) 
      sc->cod[idx++] = ce2coj + dp->cod->cbmx[i-d] + cod->t[TCbCe];  /* case Cb[i-k]->Ce[i-k]->COJ[i] */
  }
  
  score = DLog2Sum(sc->cod, idx);
  return score;
}

double 
forwCOEdiag(int start, int L, int *seqX, int *seqY, struct dpd_s *dp, struct codmodel_s *cod, 
	    struct dpsc3_s *sc)
{
  int    i;
  int    idx = 0;
  double score;

  sc->cod[idx++] = cod->t[TCOBCOE] 
    + ForwardOTHDiag_L(stdout, seqX, seqY, start, 0, cod->COB, dp->oth, sc->oth)  
    + ForwardOTHDiag_L(stdout, seqX, seqY, start, L, cod->COE, dp->oth, sc->oth);
  
  for (i = 0; i < L; i++) 
    sc->cod[idx++] = cod->t[TCeCOE]  + dp->cod->cemx[L-1-i]  
      + ForwardOTHDiag_L(stdout, seqX, seqY, start+L-i, i, cod->COE, dp->oth, sc->oth); 
  
  score = DLog2Sum(sc->cod, idx);
  return score;
}


double 
bestC33diag(int iabs, int i, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, struct dpd_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    start;
  int    k, kabs;
  double sc, bestsc;
  double hexa, extra_hexamer;

  start = iabs - i;

  bestsc = -BIGFLOAT;

  /* TRIPLETS */
  if (i > 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); 
      
      if ( isitagap(x1) || isitagap(x2) || isitagap(x3) ||
	   isitagap(y1) || isitagap(y2) || isitagap(y3)   )
	{ 
	  return bestsc;
	}
      
    
      if (i > 2 &&
	  (sc = cod->t[TCbC33] + cod->pcodon[codX][codY] + dp->cod->cbmx[i-3])
	  > bestsc) bestsc = sc;
      
      for (k = 2; k < i-2; k++) {
	
	kabs = k + start;

	xx1 = seqX[kabs-2];
	xx2 = seqX[kabs-1];
	xx3 = seqX[kabs];
	
	yy1 = seqY[kabs-2];
	yy2 = seqY[kabs-1];
	yy3 = seqY[kabs];
	
	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->cod->COJ[i-3][i-3-k] + 
	     cod->t[TC33Ce]           +  
	     cod->t[TCeCOJ]           +  
	     cod->t[TCOJCb]           +  
	     cod->t[TCbC33]           + 
	     cod->pcodon[codX][codY]  + 
	     extra_hexamer            +
	     dp->cod->c33mx[k]          ) > bestsc) bestsc = sc;
      }
    }
  return bestsc;
}


double 
bestCediag(int iabs, int i, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, struct dpd_s *dp, 
	   struct codmodel_s *cod, struct nullmodel_s *null, struct ali_s *ali, int logodds, struct end_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    start;
  int    flag;
  double add;
  double sc, bestsc;
  double hexa, extra_hexamer;
  
  start = iabs - i;
  /* Cb --> Ce (not considered here but in COJ)
   */
  bestsc = -BIGFLOAT;

  /* TRIPLETS */
  if (i > 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 == 2) 
	add = cod->t[TCOBCb] +
	  ViterbiOTHDiag_L(stdout, sqinfoX,  seqX, sqinfoY, seqY, start, 0, cod->COB, 
			   dp->oth, ali, FALSE, FALSE, FALSE, FALSE, othends);
      else 
	add = dp->cod->cbmx[i-3];

      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 (i > 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 == 3) 
	add =  cod->t[TCOBCb] +
	  ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, 
			   dp->oth, ali, FALSE, FALSE, FALSE, FALSE, othends);
      else 
	add = dp->cod->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]) + ((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;
}

double 
bestCe_L2(int iabs, int i, int jabs, int j, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
	  int Lx, int Ly, int *leg, int Lw,
	  struct dpf_s *dp, struct codmodel_s *cod, struct nullmodel_s *null, struct ali_s *ali, int logodds, struct end3_s *ends)
{
  int    dimY;
  int    x1, x2, x3, x4;
  int    y1, y2, y3, y4;
  int    xx1, xx2, xx3;
  int    yy1, yy2, yy3;
  int    tr1, tr2;
  int    codX;
  int    codY;
  int    startX, startY;
  int    flag;
  int    prv_ij;
  double sc, bestsc;
  double hexa, extra_hexamer;
  
  dimY = Ly + 1;

  startX = iabs - i;
  startY = jabs - j;

  /* Cb --> Ce (not considered here but in COJ)
   */
  bestsc = -BIGFLOAT;
  
  /* TRIPLETS */
  if (i > 1 && j > 1) 
    { 
      x1 = seqX[iabs-2];
      x2 = seqX[iabs-1];
      x3 = seqX[iabs];
      
      y1 = seqY[jabs-2];
      y2 = seqY[jabs-1];
      y3 = seqY[jabs];
      
      codX  = CODON( x1, x2, x3); 
      codY  = CODON( 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 = (jabs > 4)? seqY[jabs-5] : 4;
      yy2 = (jabs > 3)? seqY[jabs-4] : 4;
      yy3 = (jabs > 2)? seqY[jabs-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 || j == 2) 
	{
	  tr1 = TCbC33; tr2 = TC33Ce; 
	  bestsc = cod->t[TCOBCb] 
	    + cod->t[tr1] + cod->t[tr2] 
	    + cod->pcodon[codX][codY]
	    + ViterbiOTH_L2(stdout, sqinfoX, seqX, sqinfoY, seqY, 
			    startX, startY, i-2, j-2, leg, Lw, 
			    cod->COB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	  return bestsc;
	}
      
      /* Cb --> C33 --> Ce 
       */
      tr1 = TCbC33; tr2 = TC33Ce; 
      prv_ij = (i-3)*dimY + j-3;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[codX][codY]
	   + extra_hexamer)
	  > bestsc) bestsc = sc;
      
      /* Cb --> C32 --> Ce 
       */
      tr1 = TCbC32; tr2 = TC32Ce; 
      prv_ij = (i-3)*dimY + j-2;
      if ((sc =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]  
	   + average(1, y2, y3, codX, cod, null, logodds)) 
	  > bestsc) bestsc = sc;
      
      /* Cb --> C32 --> Ce 
       */
      tr1 = TCbC32; tr2 = TC32Ce; 
      prv_ij = (i-3)*dimY + j-2;
      if ((sc  =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] +
	   + average(2, y2, y3, codX, cod, null, logodds))
	  > bestsc) bestsc = sc;
    
      /* Cb --> C32 --> Ce 
       */
      tr1 = TCbC32; tr2 = TC32Ce; 
      prv_ij = (i-3)*dimY + j-2;
      if ((sc =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]  
	   + average(3, y2, y3, codX, cod, null, logodds))
	  > bestsc) bestsc = sc;
      
      /* Cb --> C30 --> Ce 
       */
      tr1 = TCbC30; tr2 = TC30Ce; 
      prv_ij = (i-3)*dimY + j;
      if ((sc  =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]  
	   + average_codon(codX, cod, null, logodds))
	  > bestsc) bestsc = sc;
 
      /* Cb --> C23 --> Ce 
       */
      tr1 = TCbC23; tr2 = TC23Ce; 
      prv_ij = (i-2)*dimY + j-3;
      if ((sc =  dp->cod->cbmx[prv_ij]
	   + cod->t[tr1] + cod->t[tr2]  
	   + average(1, x2, x3, codY, cod, null, logodds)) 
	  > bestsc) bestsc = sc;
      
      /* Cb --> C23 --> Ce 
       */
      tr1 = TCbC23; tr2 = TC23Ce; 
      prv_ij = (i-2)*dimY + j-3;
      if ((sc =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]  
	   + average(2, x2, x3, codY, cod, null, logodds)) 
	  > bestsc) bestsc = sc;

      /* Cb --> C23 --> Ce 
       */
      tr1 = TCbC23; tr2 = TC23Ce; 
      prv_ij = (i-2)*dimY + j-3;
      if ((sc =  dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]  
	   + average(3, x2, x3, codY, cod, null, logodds)) 
	  > bestsc) bestsc = sc;

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

      codX  = CODON( x1, x2, x3); 
      codY  = CODON( 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 = (jabs > 5)? seqY[jabs-6] : 4;
      yy2 = (jabs > 4)? seqY[jabs-5] : 4;
      yy3 = (jabs > 3)? seqY[jabs-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 || j == 3) 
	{
	  tr1 = TCbC44; tr2 = TC44Ce;
 	  if ((sc = cod->t[TCOBCb] 
	       + cod->t[tr1] + cod->t[tr2] 
	       + cod->pcodon[codX][codY] + 
	       + ((logodds)?0:null->xem[x4]) 
	       + ((logodds)?0:null->yem[y4])
	       + ViterbiOTH_L2(stdout, sqinfoX, seqX, sqinfoY, seqY, 
			       startX, startY, i-3, j-3, leg, Lw, 
			       cod->COB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth))
	      > bestsc) bestsc = sc;
	  return bestsc;
	}     
      
      /* Cb --> C44 --> Ce 
       */
      tr1 = TCbC44; tr2 = TC44Ce;
      prv_ij = (i-4)*dimY + j-4;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[codX][codY] + 
	   + ((logodds)?0.:null->xem[x4]) 
	   + ((logodds)?0.:null->yem[y4])
	   + extra_hexamer)
	  > bestsc) bestsc = sc;
      
      /* Cb --> C43 --> Ce 
       */
      tr1 = TCbC43; tr2 = TC43Ce;
      prv_ij = (i-4)*dimY + j-3;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x1,x2,x3)][CODON(y2,y3,y4)]  
	   + ((logodds)?0.:null->xem[x4]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x1,x2,x4)][CODON(y2,y3,y4)]
	   + ((logodds)?0.:null->xem[x3]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x1,x3,x4)][CODON(y2,y3,y4)] 
	   + ((logodds)?0.:null->xem[x2]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x2,x3,x4)][CODON(y2,y3,y4)]
	   + ((logodds)?0.:null->xem[x1]))
	  > bestsc) bestsc = sc;
      
      /* Cb --> C34 --> Ce 
       */
      tr1 = TCbC34; tr2 = TC34Ce;
      prv_ij = (i-3)*dimY + j-4;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x2,x3,x4)][CODON(y1,y2,y3)]  
	   + ((logodds)?0.:null->yem[y4]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x2,x3,x4)][CODON(y1,y2,y4)]  
	   + ((logodds)?0.:null->yem[y3]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x2,x3,x4)][CODON(y1,y3,y4)]  
	   + ((logodds)?0.:null->yem[y2]))
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij]  
	   + cod->t[tr1] + cod->t[tr2] 
	   + cod->pcodon[CODON(x2,x3,x4)][CODON(y2,y3,y4)]  
	   + ((logodds)?0.:null->yem[y1]))
	  > bestsc) bestsc = sc;

      
      /* Cb --> C40 --> Ce 
       */
      tr1 = TCbC40; tr2 = TC40Ce;
      prv_ij = (i-4)*dimY + j;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1]  + cod->t[tr2] 
	   + average_codon(codX, cod, null, logodds)
	   + ((logodds)?0.:null->xem[x4]))
	  > bestsc) bestsc = sc;
      
      /* Cb --> C04 --> Ce 
       */
      tr1 = TCbC04; tr2 = TC04Ce;
      prv_ij = (i)*dimY + j-4;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + average_codon(codY, cod, null, logodds)  
	   + ((logodds)?0.:null->yem[y4]))
	  > bestsc) bestsc = sc;
      
      /* Cb --> C42 --> Ce 
       */
      tr1 = TCbC42; tr2 = TC42Ce;
      prv_ij = (i-4)*dimY + j-2;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + 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 = dp->cod->cbmx[prv_ij] 
	   + 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;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]
	   + average(2, y3, y4, CODON(x1,x2,x4), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[x3])) 
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2]
	   + average(2, y3, y4, CODON(x1,x2,x3), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[x4])) 
	  > bestsc) bestsc = sc;
      
      /* Cb --> C24 --> Ce 
       */
      tr1 = TCbC24; tr2 = TC24Ce;
      prv_ij = (i-2)*dimY + j-4;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + average(1, x3, x4, CODON(y2,y3,y4), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[y1])) 
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + average(1, x3, x4, CODON(y1,y3,y4), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[y2])) 
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + average(1, x3, x4, CODON(y1,y2,y4), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[y3])) 
	  > bestsc) bestsc = sc;
      if ((sc = dp->cod->cbmx[prv_ij] 
	   + cod->t[tr1] + cod->t[tr2] 
	   + average(1, x3, x4, CODON(y1,y2,y3), cod, null, logodds) 
	   + ((logodds)?0.:null->xem[y4])) 
	  > bestsc) bestsc = sc;
    }
  
  return bestsc;
}

double 
forwCediag(int iabs, int i, int *seqX, int *seqY, struct dpd_s *dp, struct codmodel_s *cod, 
	   struct nullmodel_s *null, int logodds, struct dpsc3_s *sc)
{
  int    idx = 0;
  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 score;
  double hexa, extra_hexamer;
  
  start = iabs - i;

  /* Cb --> Ce (not considered here but in COJ)
   */

  /* TRIPLETS */
  if (i > 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(xx1) && !isitagap(xx2) && !isitagap(xx3) ) 
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag++;
	}
      if ( !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) 
	add = ForwardOTHDiag_L(stdout, seqX, seqY, start, 0, cod->COB, dp->oth, sc->oth) + 
	  cod->t[TCOBCb];
      else 
	add = dp->cod->cbmx[i-3];

      add += extra_hexamer;

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

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

	  else if (isnocod(cod5X)) {
	    /* Cb --> C03 --> Ce 
	     */
	    tr1 = TCbC03; tr2 = TC03Ce; 
	    sc->cod[idx++] = add + cod->t[tr1] + cod->t[tr2] + 
	      average_codon(codY, cod, null, logodds);
	  }
	}
    }

  /* QUADRUPLETS */
  if (i > 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);
      
      if (i == 3) 
	add = ForwardOTHDiag_L(stdout, seqX, seqY, start, 0, cod->COB, dp->oth, sc->oth) + 
	  cod->t[TCOBCb];
      else 
	add = dp->cod->cbmx[i-4];

      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(xx1) && !isitagap(xx2) && !isitagap(xx3) ) 
	{
	  hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][codX]);
	  flag++;
	}
      if ( !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;
	}
	      
      add += extra_hexamer;

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

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

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

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

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

  score = DLog2Sum(sc->cod, idx);
  return score;
}



/* Function: tracebackCODdiag()
 * Date:     ER, Mon Sep 20 11:39:25 CDT 1999 [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
tracebackCODdiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		 int start, int L, double score, struct dpd_s *dp, struct codmodel_s *cod, 
		 struct nullmodel_s *null, struct ali_s *ali, int alignment, int logodds, 
		 int traceback, struct end3_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, iabs, prv_i;         /* position in seqX, seqY                 */
  int    d, end;                 /* position in seqX, seqY                 */
  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) */
  double foroth;
  int    lc = 0;                  /* index for the counting of local COD regions */
  int    x;

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

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

  if (traceback) fprintf(ofp, "\nCOD traceback\n");
  cur_tr = AttachTracer(tr, end, stCOE); 
  PushTracerstack(dolist, cur_tr);
  prv_sc = score;
  prv_st =  cur_tr->type;
  
  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;

     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 = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, i+1, 
			     cod->COB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = -1;
	   ends->cod->lend[lc] = i + 1;

	   break;
	 }
       else Die ("invalid traceback in state %s in ViterbiCODDiag() (%f %f)", cstNAME[cur_st], sc, cur_sc);
       
     case stCOJ: 
       for (d = 0; d <= i; d++) {
	 sc = dp->cod->cemx[i-d] + cod->t[TCeCOJ] + dp->cod->COJ[i][d];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stCe;
	     prv_sc = dp->cod->cemx[i-d];

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

	 sc = dp->cod->cbmx[i-d] + cod->t[TCbCe] + cod->t[TCeCOJ] + dp->cod->COJ[i][d];
	 if (d > 0 && 
	     cur_sc < sc+MARGIN && cur_sc > sc-MARGIN)
	   {
	     prv_i  = iabs-d; 
	     prv_st = stCb;
	     prv_sc = dp->cod->cbmx[i-d];
	     break; 
	   }
       }
       break;
      
     case stCOE: 
       sc = ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, dp->oth, ali, 
			     FALSE, FALSE, FALSE, FALSE, ends->oth) + cod->t[TCOBCOE] + 
	 ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, L, 
			  cod->COE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);

      if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_i = -1;
	   prv_st = stCOB;
	   prv_sc = ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, dp->oth, ali, 
				     FALSE, FALSE, FALSE, FALSE, ends->oth);
	   ends->cod->rend[lc] = prv_i;
	   break;
	 }
       
       for (d = 0; d <= i; d++) {
	 foroth = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, iabs-d+1, d, 
				   cod->COE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);

	 sc = foroth + cod->t[TCeCOE]  + dp->cod->cemx[i-d];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i = iabs-d;
	     prv_st = stCe;
	     prv_sc = dp->cod->cemx[i-d];
	     ends->cod->rend[lc] = prv_i;
	     break;
	   }
	 sc = foroth + cod->t[TCOBCOE] + dp->cod->cobmx[i-d];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i = iabs-d;
	     prv_st = stCOB;
	     prv_sc = dp->cod->cobmx[i-d];
	     ends->cod->rend[lc] = prv_i;
	     break;
	   }
       }
       if (prv_st != stCOB && prv_st != stCe)
	 Die("ViterbiCODDiag(): you should not be here. Your only allowed prv states are Ce or COB. cur_state (%s) prv_state (%s)", 
	     cstNAME[cur_st], cstNAME[prv_st]);
       break;
       
     case stCb: 
       prv_i = iabs;
       if (i == -1) {
	 sc  = cod->t[TCOBCb] + 
	   ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoX, seqY, start, 0, cod->COB, dp->oth, 
			    ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_st = stCOB;
	     prv_sc = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoX, seqY, start, 0, cod->COB, dp->oth, 
				       ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	     
	     ends->cod->lend[lc] = prv_i+1;
	     break;
	   }
       }
       sc = cod->t[TCOBCb] + dp->cod->cobmx[i];
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = stCOB;
	   prv_sc = dp->cod->cobmx[i];
	   
	   ends->cod->lend[lc] = prv_i+1;
	   break;
	 }
       sc = cod->t[TCOJCb] + dp->cod->cojmx[i];
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = stCOJ;
	   prv_sc = dp->cod->cojmx[i];
	   break;
	 }
       break;

     case stCe: 
       traceCediag(iabs, i, cur_sc, sqinfoX, seqX, sqinfoX, seqY, dp, cod, null, ali,
		   logodds, &prv_st, &prv_i, ends);
       
       if (prv_i == -1)
	 prv_sc = cod->t[TCOBCb] + 
	   ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, dp->oth, 
			    ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
       else 
	 prv_sc = dp->cod->cbmx[prv_i-start];
       
       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 ViterbiCODDiag(): state (%d)(%s)", cur_st, cstNAME[cur_st]);
     }
     
     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 (traceback) {
    printf("COD ends [%d %d]\n", cur_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 
traceCediag(int iabs, int i, float cur_sc, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
	    struct dpd_s *dp, struct codmodel_s *cod, struct nullmodel_s *null, struct ali_s *ali,
	    int logodds, int *ret_st, int *ret_i, struct end3_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    start;
  int    flag;
  double add;
  double hexa, extra_hexamer;
  float  sc;

  prv_st = -5; /* set to some boggus value */
  start = iabs - i;
  
  /* Cb --> Ce 
   */
  if (cur_sc == (sc = cod->t[TCbCe] + dp->cod->cbmx[i])) {
    prv_i = iabs;
    prv_st = stCb;
  }

  if (i <2) {
    prv_i = iabs; prv_st = stCb;
  }

  /* TRIPLETS */
  if (i > 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 == 2) 
	add =  cod->t[TCOBCb]  +
	  ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoX, seqY, start, 0, cod->COB, dp->oth, 
			   ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
      else 
	add = dp->cod->cbmx[i-3];
      
      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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-3;
	      }
	  }
	}
    }

  /* QUADRUPLETS */
  if (i > 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 == 3) 
	add = cod->t[TCOBCb] + 
	  ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, 0, cod->COB, dp->oth, 
			   ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
      else 
	add = dp->cod->cbmx[i-4];

      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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-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 = iabs-4;
		}      
	    }
	    else Die("wrong number of gaps in this codon");
	  }
	}
    }
  
  if (prv_st == -5) Die("wrong traceback in traceCediag()");
  *ret_i = prv_i;
  *ret_st = prv_st;
}

