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

/* othdpscan.c
 *
 * ER, Mon Dec  3 14:13:25 CST 2001 [St. Louis]
 * 
 * dynamic programming (viterbi and forward) with the othermodel
 *
 * calculates:
 *                       P(seqX,seqY \pi^* | othemodel)  [viterbi algorithm; \pi^* = best path ]
 *              \sum_\pi P(seqX,seqY \pi   | othemodel)  [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 void tracebackOTHdiagscanfast(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 othmodel_s *oth, 
				     struct othdpscanfast_s *othdp, 
				     int revstrand, int traceback, 
				     struct endscan_s *othends);


/* Function: AllocDpDiagScanFastOTH()
 * Date:     ER, Thu Jan 17 14:13:15 CST 2002  [St. Louis]
 *
 * Purpose:  Allocates memory for the dp matrices of the OTH model- diagonal DP
 *
 * Returns:  othdp_s are allocated
 */
struct othdpscanfast_s *
AllocDpDiagScanFastOTH(int L)
{
  struct othdpscanfast_s *othdp;       /* structure with dp matrices   */
  int i, d;

  othdp = (struct othdpscanfast_s *) MallocOrDie (sizeof(struct othdpscanfast_s));

  othdp->flmx    = (double **) MallocOrDie (sizeof(double *) *  L           );
  othdp->frmx    = (double **) MallocOrDie (sizeof(double *) *  L           );
  othdp->fjmx    = (double **) MallocOrDie (sizeof(double *) *  L           );
  othdp->flmx[0] = (double *)  MallocOrDie (sizeof(double)   *  L    * (L+1));
  othdp->frmx[0] = (double *)  MallocOrDie (sizeof(double)   *  L    * (L+1));
  othdp->fjmx[0] = (double *)  MallocOrDie (sizeof(double)   *  L    * (L+1));

  othdp->bmx = (double *) MallocOrDie (sizeof(double) * L);
  othdp->mmx = (double *) MallocOrDie (sizeof(double) * L);
  othdp->xmx = (double *) MallocOrDie (sizeof(double) * L);
  othdp->ymx = (double *) MallocOrDie (sizeof(double) * L);
  othdp->emx = (double *) MallocOrDie (sizeof(double) * L);
  
  for (i = 0; i < L; i++) {
    othdp->flmx[i] = othdp->flmx[0] + i*(L+1);
    othdp->frmx[i] = othdp->frmx[0] + i*(L+1);
    othdp->fjmx[i] = othdp->fjmx[0] + i*(L+1);
  }

  for (i = 0; i < L; i++) {
   
    for (d = 0; d <= L; d++) {
      othdp->flmx[i][d] = -BIGFLOAT;
      othdp->frmx[i][d] = -BIGFLOAT;
      othdp->fjmx[i][d] = -BIGFLOAT;
    }
    
    othdp->bmx[i] = -BIGFLOAT;
    othdp->mmx[i] = -BIGFLOAT;
    othdp->xmx[i] = -BIGFLOAT;
    othdp->ymx[i] = -BIGFLOAT;
    othdp->emx[i] = -BIGFLOAT;
  }
  
  return othdp;
}

/* Function: AllocDpDiagScanFast2OTH()
 * Date:     ER,  Thu Jan 17 15:23:42 CST 2002 [STL]
 *
 * Purpose:  Allocates memory for the dp matrices of the OTH model - scanning diagonal DP
 *           both strands
 *
 * Returns:  coddpscan2 are allocated
 */
struct othdpscanfast2_s *
AllocDpDiagScanFast2OTH(int L)
{
  struct othdpscanfast2_s *othdpscan2;       /* structure with dp matrices   */

  othdpscan2 = (struct othdpscanfast2_s *) MallocOrDie (sizeof(struct othdpscanfast2_s));

  othdpscan2->othscan = AllocDpDiagScanFastOTH(L);
  othdpscan2->htoscan = AllocDpDiagScanFastOTH(L);

  return othdpscan2;
}

/* Function: FillJOTHScan() 
 * Date:     ER, Fri Dec 14 11:28:40 CST 2001 [St. Louis] 
 * 
 * 
 * Returns:  othj[jmod][l]  is filled
 */
void
FillOTHMtxScanFast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		   int leg, int win, int j, int jmod, int l, 
		   struct othmodel_s *othmodel, struct nullmodel_s *null, struct othdpscanfast_s *othdp, double  **othj, 
		   int exact, int logodds, struct endscan_s *othends)
{
 if (exact)
    othj[jmod][l] = 
      ViterbiOTHDiagScanFast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, j-l+1, (jmod-l+1<0)?jmod-l+1+win:jmod-l+1, l, l, othmodel, othdp, 
			     FALSE, FALSE, FALSE, othends);
  else 
    {
      if (l == 0)  
	othj[jmod][l] = ScoreWithOTH(ofp, seqX, seqY, j, l, othmodel, FALSE);
      
      else if (l == 1) 
	othj[jmod][l] = ScoreWithOTH(ofp, seqX, seqY, j, l, othmodel, FALSE) 
	  + ((logodds)? 2.0*null->meta : 0.0);
	
      else
	othj[jmod][l] = othj[(jmod-1<0)?jmod-1+win:jmod-1][l-1] 
	  + ScoreWithOTHMatrix(seqX, seqY, j, othmodel) 
	  + ((logodds)? 2.0*null->meta : 0.0);
      
    }
}

/* Function: FreeDpScanOTH()
 * Date:     ER, Thu Jan 17 15:34:06 CST 2002  [St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the OTH model
 *
 * Returns:  dpmatrixoth are allocated
 */
void
FreeDpDiagScanFastOTH(struct othdpscanfast_s *dp)
{
  free(dp->flmx[0]);
  free(dp->frmx[0]);
  free(dp->fjmx[0]);

  free(dp->flmx);
  free(dp->frmx);
  free(dp->fjmx);

  free(dp->bmx);
  free(dp->mmx);
  free(dp->xmx);
  free(dp->ymx);
  free(dp->emx);

  free(dp);
}

/* Function: FreeDpScan2OTH
 * Date:     ER,  Mon Dec 10 10:25:26 CST 2001[St. Louis]
 *
 * Purpose:  frees memory for the dp matrices of the OTH model
 *           both strands
 *
 * Returns:  coddpscan2 are freed
 */
void
FreeDpDiagScanFast2OTH(struct othdpscanfast2_s *othdpscan2)
{
  FreeDpDiagScanFastOTH(othdpscan2->othscan);
  FreeDpDiagScanFastOTH(othdpscan2->htoscan);

  free(othdpscan2);
}

void
PatternDpDiagScanFastOTH(int L, struct othdpscanfast_s *dp)
{
  int i, d;

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

    for (d = 0; d <= L; d++) {
      dp->flmx[i][d] = -BIGFLOAT;
      dp->frmx[i][d] = -BIGFLOAT;
      dp->fjmx[i][d] = -BIGFLOAT;
    }
    
    dp->bmx[i] = -BIGFLOAT;
    dp->mmx[i] = -BIGFLOAT;
    dp->xmx[i] = -BIGFLOAT;
    dp->ymx[i] = -BIGFLOAT;
    dp->emx[i] = -BIGFLOAT;
  }
}

/* Function: ViterbiOTHDiagScanFast() 
 * Date:     ER, Tue Oct 15 14:01:16 CDT 2002 [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
ViterbiOTHDiagScanFast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		       int leg, int win, int st, int stmod, int l, int lmax, 
		       struct othmodel_s *oth, struct othdpscanfast_s *othdp, 
		       int revstrand, int traceback, int doends, struct endscan_s *othends)
{
  int     i, imod;            /* relative positions in seqX, seqY    */
  int     imin,  imodmin;
  int     d; 
  int     curr_x, curr_y;       /* nucleotides at those positions      */
  int     k, kmod;
  double  flend, frend;
  double  sc, bestsc; 
  double  score;
 
  flend = othdp->flmx[stmod][0];   /* pass by FL flanking model without emitting anything */
  frend = othdp->frmx[stmod][0];   /* pass by FR flanking model without emitting anything */

  if (l == 0 || lmax == 0) 
    return flend + oth->t[TFLFR] + frend;                    
 
  i    = st + l-1;
  imod = i%win;
  
  imin    = i-1;
  imodmin = imin%win;
   
  curr_x = seqX[i];
  curr_y = seqY[i];
  
  /* state B(i) 
   */
  bestsc = oth->t[TFLB] + othdp->flmx[imod][l];
  for (d = 1; d < l; d++) 
    if ((sc = othdp->emx[(stmod+d-1>win-1)?stmod+d-1-win:stmod+d-1] + oth->t[TEFJ] + oth->t[TFJB] + 
	 othdp->fjmx[imod][l-d]) > bestsc) bestsc = sc;
  othdp->bmx[imod] = bestsc;
 
  /* state M(i)
   */
  bestsc = -BIGFLOAT;
  if (curr_x < 4 && curr_y < 4) {
    if (i == st) 
      bestsc = oth->t[TBM] + oth->t[TFLB] + flend;
    else {
      if ((sc = oth->t[TBM] + othdp->bmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMM] + othdp->mmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXM] + othdp->xmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYM] + othdp->ymx[imodmin]) > bestsc) bestsc = sc;
    }
    bestsc += oth->mem[idx(curr_x,curr_y)];
  }
  othdp->mmx[imod] = bestsc;
  
  /* state X(i)
   */
  bestsc = -BIGFLOAT;
  if (curr_x < 4 && curr_y == 4) {
    if (i == st) 
      bestsc = oth->t[TBX] + oth->t[TFLB] + flend;
    else {
      if ((sc = oth->t[TBX] + othdp->bmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMX] + othdp->mmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXX] + othdp->xmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYX] + othdp->ymx[imodmin]) > bestsc) bestsc = sc;
    }
    bestsc += oth->xem[curr_x];
  }
  othdp->xmx[imod] = bestsc;
 
  /* state Y(i)
   */
  bestsc = -BIGFLOAT;
  if (curr_x == 4 && curr_y < 4) { 
    if (i == st) 
      bestsc = oth->t[TBY] + oth->t[TFLB] + flend;
    else {
      if ((sc = oth->t[TBY] + othdp->bmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMY] + othdp->mmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXY] + othdp->xmx[imodmin]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYY] + othdp->ymx[imodmin]) > bestsc) bestsc = sc;
    }
    bestsc += oth->yem[curr_y];
  }
  othdp->ymx[imod] = bestsc;
  
  /* state E(i)
   */
  bestsc = -BIGFLOAT;
  if ((sc = oth->t[TME] + othdp->mmx[imod]) > bestsc) bestsc = sc;
  if ((sc = oth->t[TXE] + othdp->xmx[imod]) > bestsc) bestsc = sc;
  if ((sc = oth->t[TYE] + othdp->ymx[imod]) > bestsc) bestsc = sc;
  othdp->emx[imod] = bestsc;

  /* start END
   *
   * mt[i][l] = \max_{d=st}^{d=i} E(d)*FRN(d+1 to i)
   *
   */
  score = -BIGFLOAT;
  if (l==lmax) {
    
    /* special case
     */
    score = flend + oth->t[TFLFR] + othdp->frmx[imod][l];
    
    for (d = 1; d <= l; d++) {
      k    = st + d;
      kmod = k%win;
      
      if ((sc = othdp->emx[(kmod-1<0)? kmod-1+win:kmod-1]     + oth->t[TEFR]  + othdp->frmx[imod][l-d]) > score) score = sc;
      if ((sc = othdp->flmx[(kmod-1<0)? kmod-1+win:kmod-1][d] + oth->t[TFLFR] + othdp->frmx[imod][l-d]) > score) score = sc; 
    }
    
    if (doends || traceback)
      tracebackOTHdiagscanfast(ofp, sqinfoX, seqX, sqinfoY, seqY, leg, win, st, stmod, l, lmax, score, oth, othdp, 
			       revstrand, traceback, othends);
    
  }
  
  return score;
}

/* Function: ViterbiBackwardsOTHDiagScanFast() 
 * Date:     ER,Thu Oct 17 17:27:03 CDT 2002  [St. Louis] 
 * 
 * Purpose:  
 * 
 * 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
ViterbiBackwardsOTHDiagScanFast(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
				int win, int end, int endmod, int l, int lmax, 
				struct othmodel_s *oth, struct othdpscanfast_s *othdp)
{
  int     i, imod;
  int     iplus, imodplus;
  int     k, kmod;
  int     d; 
  int     curr_x, curr_y;       /* nucleotides at those positions      */
  double  flend, frend;
  double  sc, bestsc; 
  
  flend = othdp->flmx[endmod][0];  /* pass by FL flanking model without emitting anything */
  frend = othdp->frmx[endmod][0];  /* pass by FR flanking model without emitting anything */
  
  if (l == 0 || lmax == 0) 
    return flend + oth->t[TFLFR] + frend;

  i    = end - l + 1;
  imod = i%win;
  
  iplus    = i+1;
  imodplus = iplus%win;
   
  curr_x = seqX[i];
  curr_y = seqY[i];
  
  /* state M(i)
   */
  bestsc = -BIGFLOAT; 
  if (curr_x < 4 && curr_y < 4) {
    if (l == 1)   
      bestsc =  oth->t[TME] + oth->t[TEFR] + frend;
    else {
      if ((sc = oth->t[TME] + othdp->emx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMM] + othdp->mmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMX] + othdp->xmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TMY] + othdp->ymx[imodplus]) > bestsc) bestsc = sc;
    }
    bestsc += oth->mem[idx(curr_x,curr_y)];
 }
  othdp->mmx[imod] = bestsc; 
  
  /* state X(i)
   */
  bestsc = -BIGFLOAT;
  if (curr_x < 4 && curr_y == 4) {
    if (l == 1) 
      bestsc =  oth->t[TXE] + oth->t[TEFR] + frend;
    else {
      if ((sc = oth->t[TXE] + othdp->emx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXM] + othdp->mmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXX] + othdp->xmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TXY] + othdp->ymx[imodplus]) > bestsc) bestsc = sc;
    }
    bestsc += oth->xem[curr_x];
  }
  othdp->xmx[imod] = bestsc;
  
  /* state Y(i)
   */
  bestsc = -BIGFLOAT;
  if (curr_x == 4 && curr_y < 4) {
    if (l == 1) 
      bestsc =  oth->t[TYE] + oth->t[TEFR] + frend;
    else {
      if ((sc = oth->t[TYE] + othdp->emx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYM] + othdp->mmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYX] + othdp->xmx[imodplus]) > bestsc) bestsc = sc;
      if ((sc = oth->t[TYY] + othdp->ymx[imodplus]) > bestsc) bestsc = sc;
    }
    bestsc += oth->yem[curr_y];
  }
  othdp->ymx[imod] = bestsc;
  
  /* state B(i) [has to be filled after M(i) X(i) and Y(i) are filled]
   */
  bestsc = -BIGFLOAT;
  if ((sc = oth->t[TBM] + othdp->mmx[imod]) > bestsc) bestsc = sc;
  if ((sc = oth->t[TBX] + othdp->xmx[imod]) > bestsc) bestsc = sc;
  if ((sc = oth->t[TBY] + othdp->ymx[imod]) > bestsc) bestsc = sc;
  othdp->bmx[imod] = bestsc;
  
  /* state E(i)  [ which has to be filled after B(i) ]
   */
  bestsc = oth->t[TEFR] + othdp->frmx[endmod][l];
  for (d = 0; d < l; d++) {
    k    = i + d;
    kmod = k%win;
    if ((sc = oth->t[TEFJ] + othdp->fjmx[(kmod-1<0)?kmod-1+win:kmod-1][d] + oth->t[TFJB] + othdp->bmx[kmod]) > bestsc) bestsc = sc;
  }
  othdp->emx[imod] = bestsc;
  
  /* 
   *  State START
   */
  bestsc = -BIGFLOAT;
  if (l==lmax) {

    /* special case
     */
    bestsc = othdp->flmx[endmod][l] + oth->t[TFLFR] + frend;
    
    for (d = 0; d < l; d++) {
      
      k    = i + d;
      kmod = k%win;
      
      if ((sc = othdp->flmx[(kmod-1<0)?kmod-1+win:kmod-1][d] + oth->t[TFLB]  + othdp->bmx[kmod]        ) > bestsc) bestsc = sc;
      if ((sc = othdp->flmx[(kmod-1<0)?kmod-1+win:kmod-1][d] + oth->t[TFLFR] + othdp->frmx[endmod][l-d]) > bestsc) bestsc = sc; 

    }
  }

   return bestsc;
}

/* Function: tracebackOTHdiagscanfast()
 * Date:     ER, Tue Nov 26 13:59:06 CST 2002 [St. Louis]
 *
 * Purpose:  Traceback of best align with viterbi algorith for OTH model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           othmodel     -- oth_model structure
 *
 * Returns:  void. prints the traceback for the vitebi algorithm.
 */
void
tracebackOTHdiagscanfast(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 othmodel_s *oth, 
			 struct othdpscanfast_s *othdp, 
			 int revstrand, int traceback, 
			 struct endscan_s *othends)
{
  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    len;
  int    prv_i, prv_i_mod;       /* position in seqX, seqY                 */
  int    k, kmod;                /* position in seqX, seqY                 */
  int    cur_x, cur_y;           /* nucleotides at those positions         */
  int    cur_st, prv_st;
  double  sc, cur_sc, prv_sc;    /* to do the comparisons                  */
  double flend, frend;
  int    d;
  int    flag = FALSE; 
  int    lc = 0;
  int    verbose = FALSE;
  int    x;

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

  /* initialize ends
   */
  PatternEndScanFast(othends);

  i    = st + l - 1;
  imod = i%win;

  flend = othdp->flmx[stmod][0];   /* pass by FL flanking model without emitting anything */
  frend = othdp->frmx[stmod][0];   /* pass by FR flanking model without emitting anything */

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

  if (traceback) fprintf(ofp,"tracing T (%d) [%d %d] %f \n", i, seqX[i], seqY[i], score);
  
  flag = FALSE;

  if (l == 0 || lmax == 0) 
    {
      cur_st = stFR;
      sc = flend + oth->t[TFLFR] + frend; 
      if (score < sc+MARGIN && score > sc-MARGIN)
	{
	  prv_st = stFL;
	  if (traceback) fprintf(ofp,"tracing %s (%d) %f \n", ostNAME[cur_st], i, cur_sc);
	  if (traceback) fprintf(ofp," %s->%s, %f\n", ostNAME[prv_st], ostNAME[cur_st], cur_sc);  
	  return;
	}
      else Die("ViterbiTraceOTHDiagscanfast() wrong traceback in stE(l=0)");
    }
  
  sc = flend + oth->t[TFLFR] + othdp->frmx[imod][l];
  if (score < sc+MARGIN && score > sc-MARGIN)
    {
      flag = TRUE; 
      prv_st = -1;  
      if (traceback) fprintf(ofp," FL[%d]->FR, %f\n", st, score);
      othends->lend[lc] = st; 
      othends->rend[lc] = st; 
    }  
  
  for (d = 1; d <= l; d++) {
    k    = st + d;
    kmod = k%win;
    
    sc = othdp->emx[(kmod-1<0)? kmod-1+win:kmod-1]     + oth->t[TEFR]  + othdp->frmx[imod][l-d];
    if (score < sc+MARGIN && score > sc-MARGIN)
      {
	i = k - 1; 
	prv_sc = othdp->emx[(kmod-1<0)? kmod-1+win:kmod-1];
	if (traceback) fprintf(ofp," E->FR, %f\n", prv_sc);
	
	othends->rend[lc] = k; 
	flag = TRUE;
	break;
      }      
    
    sc = othdp->flmx[(kmod-1<0)? kmod-1+win:kmod-1][d] + oth->t[TFLFR] + othdp->frmx[imod][l-d]; 
    if (score < sc+MARGIN && score > sc-MARGIN)
      {
	i = -1; 
	if (traceback) fprintf(ofp," FL[%d]->FR, %f\n", k, score);
	prv_st = st;
	othends->lend[lc] = k; 
	othends->rend[lc] = k; 
	flag = TRUE;
	break;
      }     
  }
  
  /* check that it grabed at least one position
   */
  if (!flag) Die("ViterbiTraceOTHDiagscanfast() wrong traceback in FR");
  flag = FALSE;
  
  /* Initialize
   * Start at pos "end" with state "stE"
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */
  
  if (i > st) {
    cur_tr = AttachTracer(tr, i, stE); 
    PushTracerstack(dolist, cur_tr);
  }

  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 (cur_st == stE) prv_i = i;
      else               prv_i = i-1;

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

	/* traceback B
	 */
      case stB:
	flag = FALSE;
	
	sc = oth->t[TFLB] + flend;
	if (i < st && 
	    cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	  {
	    prv_st = -1;  
	    othends->lend[lc] = st;
	    
	    break;	   
	  }
	else {
	  sc = oth->t[TFLB] + othdp->flmx[imod][len];
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = -1;
	      
	      flag = TRUE;	  
	      othends->lend[lc] = i+1;
	      lc ++;
	      
	      break;
	    }
	}
	for (d = 1; d < len; d++) {
	  sc = othdp->emx[(stmod+d-1>win-1)?stmod+d-1-win:stmod+d-1] + oth->t[TEFJ] + oth->t[TFJB] + othdp->fjmx[imod][len-d];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stE;
	      prv_sc = othdp->emx[(stmod+d-1>win-1)?stmod+d-1-win:stmod+d-1];
	      prv_i  = st+d-1;
	      
	      flag = TRUE;
	      othends->lend[lc] = i+1;
	      lc ++;
	      
	      break;
	    }
	}
	
	if (!flag) Die("ViterbiOTHDiagscanfast() wrong traceback in %s (%d)", ostNAME[cur_st], i);
	break;
	
 	/* traceback M
	 */
     case stM: 
	flag = FALSE;

	if (i == st) {
	  sc = oth->mem[idx(cur_x,cur_y)] + oth->t[TBM] + oth->t[TFLB] + flend;
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = oth->t[TFLB] + flend;
	      flag   = TRUE;
	      break;
	    }
	  else Die("ViterbiTraceOTHDiagscanfast() wrong traceback in %s(i==st)", ostNAME[cur_st]);
	}
	else {
	  sc = oth->t[TMM] + oth->mem[idx(cur_x,cur_y)] + othdp->mmx[prv_i_mod]; 
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stM;
	      prv_sc = othdp->mmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TXM] + oth->mem[idx(cur_x,cur_y)] + othdp->xmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stX;
	      prv_sc = othdp->xmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TYM] + oth->mem[idx(cur_x,cur_y)] + othdp->ymx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stY;
	      prv_sc = othdp->ymx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TBM] + oth->mem[idx(cur_x,cur_y)] + othdp->bmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = othdp->bmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  if (!flag) Die("ViterbiOTHDiagscanfast() wrong traceback in %s (%d)", ostNAME[cur_st], i);
	}
	
	/* traceback X
	 */
      case stX: 
	flag = FALSE;
	
	if (i == st) {
	  sc = oth->t[TBX] + oth->t[TFLB] + oth->xem[cur_x] + flend;
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = oth->t[TFLB] + flend;
	      flag   = TRUE;
	      break;
	    }
	  else Die("ViterbiTraceOTHDiagscanfast() wrong traceback in %s(i==st)", ostNAME[cur_st]);
	}
	else {
	  sc = oth->t[TMX] + oth->xem[cur_x] + othdp->mmx[prv_i_mod];
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stM;
	      prv_sc = othdp->mmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TXX] + oth->xem[cur_x] + othdp->xmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stX;
	      prv_sc = othdp->xmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TYX] + oth->xem[cur_x] + othdp->ymx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stY;
	      prv_sc = othdp->ymx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TBX] + oth->xem[cur_x] + othdp->bmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = othdp->bmx[prv_i_mod];
	      flag   = TRUE;
	    break;
	    }
	  if (!flag) Die("ViterbiOTHDiagscanfast() wrong traceback in %s (%d)", ostNAME[cur_st], i);
	}
	
	/* traceback Y
	 */
      case stY: 
	flag = FALSE;
	
	if (i == st) {
	  sc = oth->t[TBY] + oth->t[TFLB] + oth->yem[cur_y] + flend;
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = oth->t[TFLB] + flend;
	      flag   = TRUE;
	      break;
	    }
	  else Die("ViterbiTraceOTHDiagscanfast() wrong traceback in %s(i==st)", ostNAME[cur_st]);
	}
	else {
	  sc = oth->t[TMY] + oth->yem[cur_y] + othdp->mmx[prv_i_mod];
	  if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stM;
	      prv_sc = othdp->mmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TXY] + oth->yem[cur_y] + othdp->xmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stX;
	      prv_sc = othdp->xmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TYY] + oth->yem[cur_y] + othdp->ymx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stY;
	      prv_sc = othdp->ymx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  sc = oth->t[TBY] + oth->yem[cur_y] + othdp->bmx[prv_i_mod];
	  if (!flag &&
	      cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	    {
	      prv_st = stB;
	      prv_sc = othdp->bmx[prv_i_mod];
	      flag   = TRUE;
	      break;
	    }
	  if (!flag) Die("ViterbiOTHDiagscanfast() wrong traceback in %s (%d)", ostNAME[cur_st], i);
	}
	
	/* traceback E
	 */
      case stE:  
	flag = FALSE;
	sc = oth->t[TME] + othdp->mmx[prv_i_mod];
	if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	  {
	    prv_st = stM; 
	    prv_sc = othdp->mmx[prv_i_mod];

	    othends->rend[lc] = i;

	    flag   = TRUE;
	    break;
	  }
	sc = oth->t[TXE] + othdp->xmx[prv_i_mod];
	if (!flag &&
	    cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	  {
	    prv_st = stX;
	    prv_sc = othdp->xmx[prv_i_mod];

	    othends->rend[lc] = i;

	    flag   = TRUE;
	    break;
	  }
	sc = oth->t[TYE] + othdp->ymx[prv_i_mod];
	if (!flag &&
	    cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	  {
	    prv_st = stY;
	    prv_sc = othdp->ymx[prv_i_mod];
	
	    othends->rend[lc] = i;

	    flag   = TRUE;
	    break;
	  }
	if (!flag) Die("ViterbiOTHDiagscanfast() wrong traceback in %s (%d)", ostNAME[cur_st], i);
	
      default:
	Die("invalid state in ViterbiOTHDiagscanfast()");
      }
      
      if (prv_st != -1) {
	if (traceback) 
	  fprintf(ofp," %s->%s, %f\n", ostNAME[prv_st], ostNAME[cur_st], cur_sc - prv_sc);
	PushTracerstack(dolist, AttachTracer(cur_tr, prv_i, prv_st));
      }
      else 
	if (traceback) 
 	  fprintf(ofp," %s->%s %f\nEND OTH traceback\n\n", ostNAME[0],  ostNAME[cur_st], cur_sc);
     

      if (lc >= MAX_NUM_ENDS) Die(" Too many ends in OTH traceback. Increase parameter MAX_NUM_ENDS");
    }
 
  if (verbose || traceback) {
    printf("OTH ends [%d %d]\n", st, st+l-1);
    for (x = 0; x < MAX_NUM_ENDS; x++)
      if (othends->lend[x] > -1)
	printf("(%d..%d) ", othends->lend[x], othends->rend[x]);
    printf("\n");
  }
  
  FreeTracer(tr);
  FreeTracer(cur_tr);
  FreeTracerstack(dolist);
}


