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

/* evolrnadp.c
 *
 * ER, Mon Aug 16 13:12:40 CDT 2004 [St. Louis at work, Coro with Maribel]
 * 
 * dynamic programming (viterbi and forward) with the rnamodel (the HMM part of the model)
 *
 * 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"


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


static double forwRNASTdiag(int iabs, int i, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
			    struct dpd_s *dp, struct rnamtx_s *mtx, struct dpsc3_s *sc);
static double forwROJdiag(int iabs, int i, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
			  struct dpd_s *dp, struct dpsc3_s *sc);
static double forwROEdiag(int start, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
			  struct dpd_s *dp, struct dpsc3_s *sc);
static double forwardRNAdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss,
				     int start, int L, struct emodel_s *emodel, 
				     struct dpd_s *dp, struct rnamtx_s *mtx, double *sc, double *vp,
				     struct dpsc3_s *sc3, int *ret_scfg_status, int cyk, int fastintloop, int logodds, int do_nus, int parse);
static double viterbiRNAdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss, 
				     int start, int L, struct emodel_s *emodel, 
				     struct dpd_s *dp, struct rnamtx_s *mtx, double *sc, double *vp,
				     struct ali_s *ali, int *ret_scfg_status, int alignment, int cyk, int fastintloop, int logodds, int do_nus, 
				     int parse, int traceback, int doends, struct end3_s *ends);
static void   tracebackRNAdiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
			       int start, int L, double score, struct rnamodel_s *rna, 
			       struct dpd_s *dp, struct rnamtx_s *mtx, struct ali_s *ali, int traceback, struct end3_s *ends);


/* Function: EvolForwardRNADiag()
 * Date:     ER, Mon Aug 16 13:16:21 CDT 2004 [St. Louis  at work, Coro with Maribel]
 *
 * Purpose:  Calculates \sum_{align} P(X,Y, align | RNA 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 
 *           rnamodel     -- rna_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | RNAmodel)
 */
void
EvolForwardRNADiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss, int start, int L, struct emodel_s *emodel, 
		   struct dpd_s *dp, struct rnascfg_s *mx, struct sc2_s *rnasc, struct dpsc3_s *sc3, 
		   int *ret_scfg_status, int *ret_scfgrv_status, int cyk, int fastintloop, int logodds, int do_nus, int ones, int parse)
{
  int *reqX, *reqY;                /* reverse complements */

  PatternRNAMtx(L, mx->in);
  PatternRNAMtx(L, mx->inrv);

  rnasc->pl = forwardRNAdiag_onepass(ofp, sqinfoX, seqX, sqinfoY, seqY, gss, start, L, emodel, dp, 
				     mx->in, mx->sc, mx->vp, sc3, ret_scfg_status, cyk, fastintloop, logodds, do_nus, parse);
  
  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);
    
    if (sqinfoX.flags & SQINFO_SS) gss = RevSS(gss+start, L);
    
    rnasc->mn = forwardRNAdiag_onepass(ofp, sqinfoX, reqX, sqinfoY, reqY, gss, 0, L, emodel, dp, 
				       mx->inrv, mx->sc, mx->vp, sc3, ret_scfgrv_status, cyk, fastintloop, logodds, do_nus, parse);
    
    free(reqX);
    free(reqY);
  }
  else  rnasc->mn = 0.0;
  
}

/* Function: forwardRNAfiag_onepass()
 * Date:     ER, Wed Sep 15 17:15:38 CDT 1999 [St. Louis at work, Coro with Maribel]
 *
 * Purpose:  Calculates \sum_{align} P(X,Y, align | RNA 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 
 *           rnamodel     -- rna_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | RNAmodel)
 */
double
forwardRNAdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss, int start, int L, struct emodel_s *emodel, 
		       struct dpd_s *dp, struct rnamtx_s *mtx, double *sc, double *vp,
		       struct dpsc3_s *sc3, int *ret_scfg_status, int cyk, int fastintloop, int logodds, int do_nus, int parse)
{
  int     i, d;		            /* relative positions in seqX, seqY   */
  int     iabs;		            /* absolute positions in seqX, seqY   */
  int     cur_x, cur_y;             /* nucleotides at those positions     */
  int     scfg_status;
  int    *ct;
  double  score;                    /* dp->rna->roemx[L-1]                */
  double  rob, roe;

  scfg_status = *ret_scfg_status;
  
  if (parse) KHS2ct (gss+start, L, FALSE, &ct);
  
  if (!scfg_status) {
    if (!cyk) 
      if (!parse)
	scfg_status = InsideRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna, emodel->null,
				dp->oth, mtx, sc, vp, do_nus, fastintloop, logodds);
      else
	scfg_status = InsideParseRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, ct, start, L, emodel->rna, emodel->null,
				     dp->oth, mtx, sc, vp, do_nus, logodds);
    else 
      if (!parse)
	scfg_status = CYKRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna, emodel->null,
			     dp->oth, mtx, vp, logodds, do_nus, FALSE);
      else
	scfg_status = CYKParseRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, ct, start, L, emodel->rna, emodel->null,
				  dp->oth, mtx, vp, logodds, do_nus, FALSE);
  }
  
  rob = ForwardOTHDiag_L(ofp, seqX, seqY, start,   0, emodel->rna->ROB, dp->oth, sc3->oth);
  roe = ForwardOTHDiag_L(ofp, seqX, seqY, start+L, 0, emodel->rna->ROE, dp->oth, sc3->oth);

  if (L == 0) return emodel->rna->t[TROBROE] + rob + roe;
  
  if (0)
    ForwardOTHDiagMatrix(ofp, dp->rna->ROJ, seqX, seqY, start, L, emodel->rna->ROJ, dp->oth, sc3->oth);
  else {
    for (i = 0; i < L; i++) {
      iabs = i + start;
      for (d = 1; d <= i+1; d++) 
	if (i == 0 || d == 1) 
	  dp->rna->ROJ[i][d] = ScoreWithOTH(ofp, seqX, seqY, iabs, d, emodel->rna->ROJ, FALSE);
      else 
	dp->rna->ROJ[i][d] = dp->rna->ROJ[i-1][d-1] + ScoreWithOTHMatrix(seqX, seqY, iabs, emodel->rna->ROJ); 
      }
    }
    
  /* state ROB
   */
  ForwardOTHDiagVector(ofp, dp->rna->rbmx, seqX, seqY, start, L, emodel->rna->ROB, dp->oth, sc3->oth);

  for (i = 0; i < L; i++) {
    
    iabs = i + start;
    
    cur_x = seqX[iabs];
    cur_y = seqY[iabs];
    
    /* state RNA
     */
    dp->rna->rrmx[i] = forwRNASTdiag(iabs, i, L, seqX, seqY, emodel->rna, dp, mtx, sc3);

    /* state ROJ
     */
    dp->rna->rjmx[i] = forwROJdiag(iabs, i, L, seqX, seqY, emodel->rna, dp, sc3);
  }

  /* state ROE[i=L-1]
   */
  score = forwROEdiag(start, L, seqX, seqY, emodel->rna, dp, sc3);

  *ret_scfg_status = scfg_status;

  if (parse) free (ct);
  return score;
}

/* Function: EvolViterbiRNADiag()
 * Date:     ER,  Mon Aug 16 13:18:10 CDT 2004[St. Louis at work, Coro with Maribel]
 *
 * Purpose:  Calculates P(X,Y, best_align | RNA 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 
 *           rnamodel     -- rna_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | RNAmodel)
 */
void
EvolViterbiRNADiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss, int start, int L, 
		   struct emodel_s *emodel, struct dpd_s *dp, struct rnascfg_s *mx, struct sc2_s *rnasc, struct ali_s *ali, 
		   int *ret_scfg_status, int *ret_scfgrv_status, 
		   int alignment, int cyk, int fastintloop, int logodds, int do_nus, int ones, int parse, int traceback, 
		   int doends, struct windowends_s *windowends)
{
  int *reqX, *reqY;   /* reverse complements */

   rnasc->pl = viterbiRNAdiag_onepass(ofp, sqinfoX, seqX, sqinfoY, seqY, gss, start, L, 
				     emodel, dp, mx->in, mx->sc, mx->vp, ali, ret_scfg_status, 
				     alignment, cyk, fastintloop, logodds, do_nus, parse, 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);
    
    if (sqinfoX.flags & SQINFO_SS) gss = RevSS(gss+start, L);
    
    rnasc->mn = viterbiRNAdiag_onepass(ofp, sqinfoX, reqX, sqinfoY, reqY, gss, 0, L, 
				       emodel, dp, mx->inrv, mx->sc, mx->vp, ali, ret_scfgrv_status, 
				       alignment, cyk, fastintloop, logodds, do_nus, parse, traceback, doends, windowends->rev);
    
    free(reqX);
    free(reqY);
  }
  else 
    rnasc->mn = 0.0;
  
}

/* Function: viterbiRNAdiag_onepass()
 * Date:     ER, Mon Aug 16 13:17:32 CDT 2004 [St. Louis at work, Coro with Maribel]
 *
 * Purpose:  Calculates P(X,Y, best_align | RNA 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 
 *           rnamodel     -- rna_model structure
 *
 * Returns:  log likelihood, log P(seqX,seqY,\pi_{blast} | RNAmodel)
 */
double
viterbiRNAdiag_onepass(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, char *gss, int start, int L, 
		       struct emodel_s *emodel, struct dpd_s *dp, struct rnamtx_s *mtx, double *sc, double *vp,
		       struct ali_s *ali, int *ret_scfg_status, int alignment, int cyk, int fastintloop, int logodds, int do_nus, 
		       int parse, int traceback, int doends, struct end3_s *ends)
{
  int     i;		           /* relative positions in seqX, seqY   */
  int     d;
  int     iabs;		           /* absolute positions in seqX, seqY   */
  int     cur_x, cur_y;             /* nucleotides at those positions     */
  int     scfg_status;
  int    *ct;
  double  score, bestsc;
  double  roe;

  scfg_status = *ret_scfg_status;

  if (parse) KHS2ct (gss+start, L, FALSE, &ct);
  
  if (!scfg_status) {
    if (!cyk) 
      if (!parse)
	scfg_status = InsideRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna, emodel->null,
				dp->oth, mtx, sc, vp, do_nus, fastintloop, logodds);
      else
	scfg_status = InsideParseRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, ct, start, L, emodel->rna, emodel->null,
				     dp->oth, mtx, sc, vp, do_nus, logodds);
    else 
      if (!parse)
	scfg_status = CYKRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna, emodel->null,
			     dp->oth, mtx, vp, logodds, do_nus, traceback);
      else
	scfg_status = CYKParseRNA(ofp, sqinfoX, seqX, sqinfoY, seqY, ct, start, L, emodel->rna, emodel->null,
				  dp->oth, mtx, vp, logodds, do_nus, traceback);
  }
  
  if (L == 0) return emodel->rna->t[TROBROE] +
		ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, 
				 emodel->rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth) +
		ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, 
				 emodel->rna->ROE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth); 
  
  if (FALSE) 
    ViterbiOTHDiagMatrix(ofp, dp->rna->ROJ, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna->ROJ, 
			 dp->oth);
  else {
    for (i = 0; i < L; i++) {
      iabs = i + start;
      
      dp->rna->ROJ[i][0] = ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, iabs, 0, emodel->rna->ROJ, 
					    dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
      for (d = 1; d <= i+1; d++) {
	if (i == 0 || d == 1) 
	  dp->rna->ROJ[i][d] = ScoreWithOTH(ofp, seqX, seqY, iabs, d, emodel->rna->ROJ, FALSE);
      else 
	dp->rna->ROJ[i][d] = dp->rna->ROJ[i-1][d-1] + ScoreWithOTHMatrix(seqX, seqY, iabs, emodel->rna->ROJ);
      }
    }
  }
  
  /* state ROB
   */
  ViterbiOTHDiagVector(ofp, dp->rna->rbmx, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna->ROB, dp->oth); 
  
  for (i = 0; i < L; i++) {
    iabs = i + start;
    cur_x = seqX[iabs];
    cur_y = seqY[iabs];

    /* state RNA
    */
    bestsc = emodel->rna->t[TROBRNA] + mtx->wx[iabs][i] + 
      ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, emodel->rna->ROB, dp->oth, ali, 
		       FALSE, FALSE, FALSE, FALSE, ends->oth);  
    
    for (d = 1; d <= i; d++) {
      if ((score = emodel->rna->t[TROBRNA] + dp->rna->rbmx[i-d] + mtx->wx[iabs][d-1]) > bestsc) bestsc = score;
      if ((score = emodel->rna->t[TROJRNA] + dp->rna->rjmx[i-d] + mtx->wx[iabs][d-1]) > bestsc) bestsc = score;
   }
    dp->rna->rrmx[i] = bestsc;

    /* state ROJ
     */
    bestsc = -BIGFLOAT;
    for (d = 0; d <= i; d++) {
      if ((score = emodel->rna->t[TRNAROJ] + dp->rna->rrmx[i-d] + dp->rna->ROJ[i][d]) > bestsc) bestsc = score;
    }
     dp->rna->rjmx[i] = bestsc;

  } /* while i */
  
  /* state ROE[i=L-1]
   */
  bestsc = emodel->rna->t[TROBROE] +
    ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, emodel->rna->ROB, dp->oth, ali, 
		     FALSE, FALSE, FALSE, FALSE, ends->oth) +
    ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, emodel->rna->ROE, dp->oth, ali, 
		     FALSE, FALSE, FALSE, FALSE, ends->oth);

  for (d = 0; d < L; d++) {
    roe = ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start+L-d, d, emodel->rna->ROE, 
			   dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
    if((score = emodel->rna->t[TRNAROE] + dp->rna->rrmx[L-1-d] + roe) > bestsc) bestsc = score;
    if((score = emodel->rna->t[TROBROE] + dp->rna->rbmx[L-1-d] + roe) > bestsc) bestsc = score;
  }
  
  /* calculate and print the traceback
   */
  if (doends || traceback)
    tracebackRNAdiag(ofp, sqinfoX, seqX, sqinfoY, seqY, start, L, bestsc, 
		     emodel->rna, dp, mtx, ali, traceback, ends);
  
  *ret_scfg_status = scfg_status;

  if (parse) free (ct);

  return bestsc;
}

double 
forwRNASTdiag(int iabs, int i, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
	      struct dpd_s *dp, struct rnamtx_s *mtx, struct dpsc3_s *sc)
{
  int    d;
  int    idx = 0;
  double score;

  sc->rna[idx++] = ForwardOTHDiag_L(stdout, seqX, seqY, iabs-i, 0, rna->ROB, dp->oth, sc->oth) + 
    rna->t[TROBRNA] + mtx->wx[iabs][i];
  
  for (d = 1; d <= i; d++) {
    sc->rna[idx++] = rna->t[TROBRNA] + dp->rna->rbmx[i-d] + mtx->wx[iabs][d-1];
    sc->rna[idx++] = rna->t[TROJRNA] + dp->rna->rjmx[i-d] + mtx->wx[iabs][d-1];
  }

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

double 
forwROJdiag(int iabs, int i, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
	    struct dpd_s *dp, struct dpsc3_s *sc)
{
  int    d;
  int    idx = 0;
  double score;

  for (d = 0; d <= i; d++) 
    sc->rna[idx++] = rna->t[TRNAROJ] + dp->rna->rrmx[i-d] + dp->rna->ROJ[i][d];
  
  score = DLog2Sum(sc->rna, idx);
  return score;
}

double 
forwROEdiag(int start, int L, int *seqX, int *seqY, struct rnamodel_s *rna, 
	    struct dpd_s *dp, struct dpsc3_s *sc)
{
  int    i;
  int    idx = 0;
  double score;
  
  sc->rna[idx++] = rna->t[TROBROE] + 
    ForwardOTHDiag_L(stdout, seqX, seqY, start, 0, rna->ROB, dp->oth, sc->oth) + 
    ForwardOTHDiag_L(stdout, seqX, seqY, start, L, rna->ROE, dp->oth, sc->oth);
  
  for (i = 0; i < L; i++) 
    sc->rna[idx++] = rna->t[TRNAROE] + dp->rna->rrmx[L-1-i] +
      ForwardOTHDiag_L(stdout, seqX, seqY, start+L-i, i, rna->ROE, dp->oth, sc->oth);
 
  score = DLog2Sum(sc->rna, idx);
  return score;
}


/* Function: tracebackRNAdiag()
 * Date:     ER, Wed Sep 29 09:49:55 CDT 1999 [St. Louis]
 *
 * Purpose:  Traceback of best align with viterbi algorith for RNA model.
 *
 * Args:     seqX, seqY   -- equal length sequences, ACGT and gaps (-) 
 *           L            -- lengths of sX,sY 
 *           rna          -- rna_model structure (the parameters of the model)
 *           rnadp        -- rna_dp    structure (the matrices for dp of the HMM part)
 *           mx           -- rnascfg   structure (the matrices for the SCFg part of the model)
 *
 * Returns:  void. prints the traceback for the vitebi algorithm.
 */
void   
tracebackRNAdiag(FILE *ofp, SQINFO sqinfoX, int *seqX, SQINFO sqinfoY, int *seqY, 
		 int start, int L, double score,  struct rnamodel_s *rna, struct dpd_s *dp, 
		 struct rnamtx_s *mtx, struct ali_s *ali, 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) */
  int    flag;
  int    lc = 0;                 /* index for the counting of local RNA regions */
  int    x;

  /* 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, "\nRNA traceback (diagonal viterbi) [start= %d, end = %d]\n", start, end);

  cur_tr = AttachTracer(tr, end, stROE); 
  PushTracerstack(dolist, cur_tr);
  prv_sc = score;
  
  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", rstNAME[cur_st], i, cur_x, cur_y, cur_sc);

     switch (cur_st){
     case stROB: 
       sc = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, start, i+1, 
			     rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_st = -1;
	   break;
	 }
       else Die ("invalid traceback %s in ViterbiRNADiag() pos: %d (%f, %f)", rstNAME[cur_st], iabs,
		 cur_sc, sc);
       break;

     case stRNA: 
       flag = FALSE;
       sc = rna->t[TROBRNA] + mtx->wx[i][i] +
	 ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, iabs-i, 0, 
			  rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_i  = -1; 
	   prv_st = stROB;
	   prv_sc = ViterbiOTHDiag_L(stdout, sqinfoX, seqX, sqinfoY, seqY, iabs-i, 0, 
				     rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	   
	   ends->rna->lend[lc] = start;
	   ends->rna->rend[lc] = i;
	   lc ++;
	   
	   break;
	 }
       
       for (d = 1; d <= i; d++) {
	 sc = rna->t[TROBRNA] + dp->rna->rbmx[i-d] + mtx->wx[iabs][d-1];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stROB;
	     prv_sc = dp->rna->rbmx[i-d];
	     flag   = TRUE;
	
	     ends->rna->lend[lc] = prv_i+1;
	     ends->rna->rend[lc] = i;
	     lc ++;
	     
	     break;
	   }
	 sc = rna->t[TROJRNA] + dp->rna->rjmx[i-d] + mtx->wx[iabs][d-1];
	 if (!flag &&
	     cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stROJ;
	     prv_sc = dp->rna->rjmx[i-d];
	     flag   = TRUE;

	     ends->rna->lend[lc] = prv_i+1;
	     ends->rna->rend[lc] = i;
	     lc ++;
	     
	     break;
	   }
       }
       if (!flag) Die ("invalid traceback %s in ViterbiRNADiag() pos: %d", rstNAME[cur_st], iabs);
       break;
       
       
     case stROJ: 
       flag = FALSE;
       for (d = 0; d <= i; d++) {
	 sc = rna->t[TRNAROJ] + dp->rna->rrmx[i-d] + dp->rna->ROJ[i][d];
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stRNA;
	     prv_sc = dp->rna->rrmx[i-d];
	     flag   = TRUE;
	     break;
	   }
       }
       if (!flag) Die ("invalid traceback %s in ViterbiRNADiag() pos: %d", rstNAME[cur_st], iabs);
       break;
       
     case stROE: 
       flag = FALSE;
       sc = rna->t[TROBROE] + 
	 ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, 
			  rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth) +
	 ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, iabs-i, i+1, 
			  rna->ROE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
       if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	 {
	   prv_i  = -1; 
	   prv_st = stROB;
	   prv_sc = ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, start, 0, 
				     rna->ROB, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	   
	   break;
	 }
       
       for (d = 0; d <= i; d++) {
	 sc =  rna->t[TRNAROE] + dp->rna->rrmx[i-d] +
	   ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, iabs-d+1, d, 
			    rna->ROE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	 if (cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stRNA;
	     prv_sc = dp->rna->rrmx[i-d];
	     flag   = TRUE;
	     break;
	   }
	 sc = rna->t[TROBROE] + dp->rna->rbmx[i-d] +
	   ViterbiOTHDiag_L(ofp, sqinfoX, seqX, sqinfoY, seqY, iabs-d+1, d, 
			    rna->ROE, dp->oth, ali, FALSE, FALSE, FALSE, FALSE, ends->oth);
	 if (!flag &&
	     cur_sc < sc+MARGIN && cur_sc > sc-MARGIN) 
	   {
	     prv_i  = iabs-d; 
	     prv_st = stROB;
	     prv_sc = dp->rna->rbmx[i-d];
	     flag   = TRUE;
	     break;
	   }
       }
       
       if (!flag) Die ("invalid traceback %s in ViterbiRNADiag() pos: %d", rstNAME[cur_st], iabs);
       break;
       
     default:
       Die("invalid state in ViterbiRNADiag()");
     }
     
     if (prv_st != -1) {
       if (traceback) fprintf(ofp," %s->%s, %f\n", rstNAME[prv_st], rstNAME[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 RNA traceback. Increase parameter MAX_NUM_ENDS");

   }

  if (traceback) {
    printf("RNA ends [%d %d]\n", cur_st, end);
    for (x = 0; x < MAX_NUM_ENDS; x++)
      if (ends->rna->lend[x] > -1)
	printf("(%d..%d) ", ends->rna->lend[x], ends->rna->rend[x]);
    printf("\n");
  }

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











