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

/* nrnscfg.c
 *
 * ER, Wed Oct  3 18:36:17 CDT 2001  [St. Louis]
 * 
 * dynamic programming (cyk and inside) with the NRN (R Dowell) rna nussinov
 *
 * calculates:
 *                       P(seqX,seqY \pi^* | rnamodel)  [CYK; \pi^* = best path ]
 *              \sum_\pi P(seqX,seqY \pi   | rnamodel)  [fInside 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


/* Function: AllocNRNModel()
 * Date:     ER,  Tue Jun  8 14:15:46 CDT 1999 [St. Louis]
 *
 * Purpose:  Allocates memory for the transition and emission probs of the rnamodel
 *
 * Args:     rnamodel - rnamodel structure
 *
 * Returns:  void
 *           allocates rna->t[], rna->ROB, rna->ROJ, rna->ROE, 
 *                     rna->vx, rna->wx, rna->wbx, rna->is1, rna->is2, 
 *           (which must be free'd by caller).
 */
struct nrn_s *
AllocNRNModel(int L)
{
  struct nrn_s *nrn;   /* rnamodel structure           */
  int           j;

  nrn     = (struct nrn_s  *)  MallocOrDie (sizeof(struct nrn_s ));
  nrn->S  = (struct nrnS_s *)  MallocOrDie (sizeof(struct nrnS_s));
  nrn->L  = (struct nrnL_s *)  MallocOrDie (sizeof(struct nrnL_s));
  nrn->R  = (struct nrnR_s *)  MallocOrDie (sizeof(struct nrnR_s));

  nrn->S->pp    = (double **) MallocOrDie(sizeof(double *) * L);
  nrn->L->pp    = (double **) MallocOrDie(sizeof(double *) * L);
  nrn->S->pp[0] = (double  *) MallocOrDie(sizeof(double  ) * L * (L+1) / 2);
  nrn->L->pp[0] = (double  *) MallocOrDie(sizeof(double  ) * L * (L+1) / 2);

  nrn->S->ps = (double *) MallocOrDie(sizeof(double) * L);
  nrn->L->ps = (double *) MallocOrDie(sizeof(double) * L);
  nrn->R->ps = (double *) MallocOrDie(sizeof(double) * L);

  for (j = 1; j < L; j++) {
    nrn->S->pp[j] = nrn->S->pp[0] + j*(j+1)/2;
    nrn->L->pp[j] = nrn->L->pp[0] + j*(j+1)/2;
  }

  /* Initialize */
  PatternVec(L, nrn->S->ps);
  PatternVec(L, nrn->L->ps);
  PatternVec(L, nrn->R->ps);

  PatternMtx(L, nrn->S->pp);
  PatternMtx(L, nrn->L->pp);

  return nrn;
}

/* Function: AllocScfgNRN()
 *
 * Date:     ER, Wed Oct  3 18:31:32 CDT 2001   [St. Louis]
 *
 * Purpose:  allocate dp matrices for the NRN RNA model
 *
 * Args:     L   --  length of the sequences
 *           sx  --  dp matrix (L x L)
 *           lx  --  dp matrix (L x L)
 *           rx  --  dp matrix (L x L)
 *
 * Purpose:  Allocates memory for the dp matrices of the RNA model
 *
 * Returns:  sx, lx, rx are allocated.
 *
 */
struct nrnscfg_s *
AllocScfgNRN(int L)
{
  struct nrnscfg_s *mx;       /* structure with dp matrices   */
  int               j;

  mx = (struct nrnscfg_s *) MallocOrDie (sizeof(struct nrnscfg_s));

  mx->sx = (double **) MallocOrDie(sizeof(double *) * L);
  mx->lx = (double **) MallocOrDie(sizeof(double *) * L);
  mx->rx = (double **) MallocOrDie(sizeof(double *) * L);

  mx->sx[0] = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mx->lx[0] = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mx->rx[0] = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);

  for (j = 1; j < L; j++) {
    mx->sx[j] = mx->sx[0] + j*(j+1)/2;
    mx->lx[j] = mx->lx[0] + j*(j+1)/2;
    mx->rx[j] = mx->rx[0] + j*(j+1)/2;
  }

  /* Initialize */
  PatternMtx(L, mx->sx);
  PatternMtx(L, mx->lx);
  PatternMtx(L, mx->rx);

  return mx;
}

/* Function: Best_nrnS()
 * Date:     ER, Mon Aug 27 14:01:29 CDT 2001   [STL]  
 * 
 * Purpose:  fill S from nrn grammar using CYK algorit hm
 *  
 * Args:     L    -- 
 *           vx   --  
 *           wx   -- 
 *           wbx  -- 
 *           j,d   - coordinates of the matrix eleme nt 
 * 
 * Returns:  void   
 *           matrix vx is filled at position [j][d]. 
 * 
 */ 
void 
Best_nrnS(int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, 
	  struct nrnscfg_s *mtx, struct end_s *rnaends)
{
  int     off;
  int     jh;
  int     mid;
  double  sc, bestsc;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
    
  jh = j + off;

  bestsc = -BIGFLOAT;

  /* Initialize diagonal 
   */
  if (d == 0) { mtx->sx[jh][d] = nrn->S->tsr + nrn->R->tre + nrn->R->ps[j]; return; } /* goes thru R a if only one position */

  /* Main recursion
   */

                                /* S -> (xi,yi) S(V) (xj,yj)  */
  if (d == 1 && (sc = nrn->S->tse + nrn->S->pp[j][d]) > bestsc) bestsc = sc;
  if (d > 1  && (sc = nrn->S->tss + mtx->sx[jh-1][d-2] + nrn->S->pp[j][d]) > bestsc) bestsc = sc;
  
				/* S ->  (xi,yi) L(W) */
  if ((sc = nrn->S->tsl + mtx->lx[jh][d-1] + nrn->S->ps[j-d]) > bestsc) bestsc = sc;
  
				/* S ->  R(WB) (xj,yj) */
  if ((sc = nrn->S->tsr + mtx->rx[jh-1][d-1] + nrn->S->ps[j]) > bestsc) bestsc = sc;
 
				/* bifurcations S -> LS */
  for (mid = 0; mid < d; mid++)
    if ((sc = nrn->S->tsb + mtx->lx[jh-mid-1][d-mid-1] + mtx->sx[jh][mid]) > bestsc) bestsc = sc;
  
				/* summation */
  mtx->sx[jh][d] = bestsc;
}

/* Function: Best_nrnL()
 * Date:     ER,  Mon Aug 27 14:11:11 CDT 2001  [STL]
 *
 * Purpose:  fill L from nrn grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix vx is filled at position [j][d].
 *
 */
void
Best_nrnL(int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, 
	  struct nrnscfg_s *mtx, struct end_s *rnaends)
{
  int     off;
  int     jh;
  double  sc, bestsc;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
    
  jh = j + off;

  bestsc = -BIGFLOAT;

  /* Initialize diagonal 
   */
  if (d == 0) { mtx->lx[jh][d] = -BIGFLOAT; return; }

  /* Main recursion
   */

                                /* L(W) -> (xi,yi) S(V) (xj,yj)  */
  if (d == 1 && (sc = nrn->L->tle + nrn->L->pp[j][d]) > bestsc) bestsc = sc;
  if (d > 1  && (sc = nrn->L->tls + mtx->sx[jh-1][d-2] + nrn->L->pp[j][d]) > bestsc) bestsc = sc;
  
				/* L(W) ->  (xi,yi) L(W) */
  if ((sc = nrn->L->tll + mtx->lx[jh][d-1] + nrn->L->ps[j-d]) > bestsc) bestsc = sc;

  mtx->lx[jh][d] = bestsc;
}


/* Function: Best_nrnR()
 * Date:     ER,    [STL]
 *
 * Purpose:  fill R from nrn grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][d].
 *
 */
void
Best_nrnR(int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, 
	  struct nrnscfg_s *mtx, struct end_s *rnaends)
{
  int     off;
  int     jh;
  double  sc, bestsc;

  bestsc = -BIGFLOAT;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
    
  jh = j + off;
  

  /* Initialize diagonal 
   */
  if (d == 0) { mtx->rx[jh][0] = nrn->R->trr + nrn->R->tre + nrn->R->ps[j]; return; }

  /* Main recursion
   */
                                /* R(W) -> R(WB) (xj,yj)  */
  if ((sc = nrn->R->trr + mtx->rx[jh-1][d-1] + nrn->R->ps[j]) > bestsc) bestsc = sc;
  

  mtx->rx[jh][d] = bestsc;
  
}

/* Function: ConstructNRNModel()
 * Date:     ER,  Thu Oct  4 10:10:05 CDT 2001 [St. Louis]
 *
 * Purpose:  Build the NR-NUSSINOV RNA model 
 *
 * Args:     
 *
 * Returns:  void
 *           fills in nrnmodel
 */
void
ConstructNRNModel(int L, struct nrn_s **ret_nrn, double **pp, double *ps)
{
  int           j, d;
  double        sum;
  struct nrn_s *nrn;

  nrn = AllocNRNModel (L);

  /* Emissions. 
   *       These are weird, they are posterior probabilities, and they are position dependent
   */
  for (j = 0; j < L; j++) {
      nrn->S->ps[j] = ps[j];
      nrn->L->ps[j] = ps[j];
      nrn->R->ps[j] = ps[j];
      
    for (d = 0; d <= j; d++) {
      nrn->S->pp[j][d] = pp[j][d];
      nrn->L->pp[j][d] = pp[j][d];
    }
  }

  /* S state Transitions
   */
  sum = 0.0;
  nrn->S->tss = 0.05;
  nrn->S->tsl = 0.05;
  nrn->S->tsr = 0.84;
  nrn->S->tsb = 0.05;
  nrn->S->tse = 0.01;

  sum = nrn->S->tss + nrn->S->tsl + nrn->S->tsr + nrn->S->tsb + nrn->S->tse;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->S transitions don't add up to one (sum_S = %f)\n", sum);

  /* log2-probs */
  nrn->S->tss = LOG2(nrn->S->tss);
  nrn->S->tsl = LOG2(nrn->S->tsl);
  nrn->S->tsr = LOG2(nrn->S->tsr);
  nrn->S->tsb = LOG2(nrn->S->tsb);
  nrn->S->tse = LOG2(nrn->S->tse);

 /* L state Transitions
  */
  sum = 0.0;
  nrn->L->tls = 0.24;
  nrn->L->tll = 0.75;
  nrn->L->tle = 0.01;
  
  sum = nrn->L->tls + nrn->L->tll +  nrn->L->tle;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->L transitions don't add up to one (sum_L = %f)\n", sum);

 /* log2-probs */
  nrn->L->tls = LOG2(nrn->L->tls);
  nrn->L->tll = LOG2(nrn->L->tll);
  nrn->L->tle = LOG2(nrn->L->tle);

 /* R state Transitions
  */
  sum = 0.0;
  nrn->R->trr = 0.99;
  nrn->R->tre = 0.01;

  sum = nrn->R->trr + nrn->R->tre;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->R transitions don't add up to one (sum_R = %f)\n", sum);

 /* log2-probs */
  nrn->R->trr = LOG2(nrn->R->trr);
  nrn->R->tre = LOG2(nrn->R->tre);

  *ret_nrn = nrn;
}

void
ConstructNRNModel2(int L, struct nrn_s **ret_nrn, double **pp, double *ps)
{
  int           j, d;
  double        sum;
  struct nrn_s *nrn;
  double       *psingle;

  nrn = AllocNRNModel(L);

  psingle = (double *) MallocOrDie(sizeof(double) * L);
  for (j = 0; j < L; j++) 
    psingle[j] = 0.0;
  
  /* Emissions. 
   *       These are weird, they are posterior probabilities, and they are position dependent
   */
  /* First calculate the posterior prob that j is paired, pp's come in log2 version
   */
  for (j = 0; j < L; j++) 
    {
      for (d = 0; d <= j;  d++) { psingle[j] += EXP2(pp[j][d]);   }
      for (d = 1; d < L-j; d++) { psingle[j] += EXP2(pp[j+d][d]); }
    }
  
  /* the prob of being single stranded is that of not being paired to any other position
   */
  for (j = 0; j < L; j++) 
    psingle[j] = LOG2(1.0 - psingle[j]);

  for (j = 0; j < L; j++) {
    nrn->S->ps[j] = psingle[j];
    nrn->L->ps[j] = nrn->S->ps[j];
    nrn->R->ps[j] = nrn->S->ps[j];
    
    for (d = 0; d <= j; d++) {
      nrn->S->pp[j][d] = pp[j][d];
      nrn->L->pp[j][d] = nrn->S->pp[j][d];
    }
  }

  /* S state Transitions
   */
  sum = 0.0;
  nrn->S->tss = 0.6596;
  nrn->S->tsl = 0.0295;
  nrn->S->tsr = 0.1517;
  nrn->S->tsb = 0.1582;
  nrn->S->tse = 0.001;

  sum = nrn->S->tss + nrn->S->tsl + nrn->S->tsr + nrn->S->tsb + nrn->S->tse;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->S transitions don't add up to one (sum_S = %f)\n", sum);

  /* log2-probs */
  nrn->S->tss = LOG2(nrn->S->tss);
  nrn->S->tsl = LOG2(nrn->S->tsl);
  nrn->S->tsr = LOG2(nrn->S->tsr);
  nrn->S->tsb = LOG2(nrn->S->tsb);
  nrn->S->tse = LOG2(nrn->S->tse);

 /* L state Transitions
  */
  sum = 0.0;
  nrn->L->tls = 0.2363;
  nrn->L->tll = 0.7627;
  nrn->L->tle = 0.001;
  
  sum = nrn->L->tls + nrn->L->tll +  nrn->L->tle;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->L transitions don't add up to one (sum_L = %f)\n", sum);

 /* log2-probs */
  nrn->L->tls = LOG2(nrn->L->tls);
  nrn->L->tll = LOG2(nrn->L->tll);
  nrn->L->tle = LOG2(nrn->L->tle);

 /* R state Transitions
  */
  sum = 0.0;
  nrn->R->trr = 0.7622;
  nrn->R->tre = 0.2378;

  sum = nrn->R->trr + nrn->R->tre;
  if (sum > 2.0-accuracy || sum < accuracy) 
    Die ("nrn->R transitions don't add up to one (sum_R = %f)\n", sum);

 /* log2-probs */
  nrn->R->trr = LOG2(nrn->R->trr);
  nrn->R->tre = LOG2(nrn->R->tre);

  free (psingle);

  *ret_nrn = nrn;
}


/* Function: Cyk_NRN()
 * Date:     ER, Mon Aug 27 13:45:52 CDT 2001  [STL]
 *
 * Purpose:  CYK algorithm for NR nussinov grammar (R.Dowell)
 *
 *           S -> aSb | aL | Ra | LS
 *           L -> aSb | aL
 *           R -> Ra  | e
 *
 */
void 
Cyk_NRN(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,  
	struct nrn_s *nrn, struct nrnscfg_s *mtx, struct end_s *rnaends) 
{ 
  int     len;
  int     j, d; 

  PatternMtx(L, mtx->sx);
  PatternMtx(L, mtx->lx);
  PatternMtx(L, mtx->rx);

  len = abs(rnaends->rend[0] - rnaends->lend[0]) + 1;

  for (j = 0; j < len; j++) 
    for (d = 0; d <= j; d++) 
      { 
	Best_nrnS (sX, sY, nrn, start, L, j, d, mtx, rnaends); 
	Best_nrnL (sX, sY, nrn, start, L, j, d, mtx, rnaends); 
	Best_nrnR (sX, sY, nrn, start, L, j, d, mtx, rnaends); 
      } 

} 

void
FreeNRNModel(struct nrn_s *nrn)
{
  free(nrn->S->pp[0]);
  free(nrn->L->pp[0]);

  free(nrn->S->pp);
  free(nrn->L->pp);

  free(nrn->S->ps);
  free(nrn->L->ps);
  free(nrn->R->ps);


  free(nrn->S);
  free(nrn->L);
  free(nrn->R);

  free(nrn);
}

void
FreeScfgNRN(struct nrnscfg_s *mx)
{
  free(mx->sx[0]);
  free(mx->lx[0]);
  free(mx->rx[0]);

  free(mx->sx);
  free(mx->lx);
  free(mx->rx);

  
  free(mx);
}

void
PrintNRNModel (FILE *ofp, struct nrn_s *nrn, int L)
{ 
  int i, d;

  fprintf(ofp, "State S transitions\n");
  fprintf(ofp, "S -> a S b %f\n", EXP2(nrn->S->tss));
  fprintf(ofp, "S -> a L   %f\n", EXP2(nrn->S->tsl));
  fprintf(ofp, "S -> R a   %f\n", EXP2(nrn->S->tsr));
  fprintf(ofp, "S -> L S   %f\n", EXP2(nrn->S->tsb));
  fprintf(ofp, "S -> a e a %f\n", EXP2(nrn->S->tse));

  fprintf(ofp, "State L transitions\n");
  fprintf(ofp, "L -> a S b %f\n", EXP2(nrn->L->tls));
  fprintf(ofp, "L -> a L   %f\n", EXP2(nrn->L->tll));
  fprintf(ofp, "L -> a e b %f\n", EXP2(nrn->L->tle));

  fprintf(ofp, "State R transitions\n");
  fprintf(ofp, "R -> R a %f\n", EXP2(nrn->R->trr));
  fprintf(ofp, "R -> ae  %f\n", EXP2(nrn->R->tre));


  fprintf(ofp, "Posterior Prob of Column Unpaired (logodds)\n");
  for (i = 0; i < L; i++) 
     fprintf(ofp, "%d %f\n", i, nrn->S->ps[i]);

  fprintf(ofp, "Posterior Prob of two Paired Columns (logodds)\n");
  for (i = 0; i < L; i++)    
    for (d = 0; d < L-i; d++) 
      fprintf(ofp, "%d %d %f\n", i, i+d, nrn->S->pp[i+d][d]);
}

/* Function: TR_nrnS()
 * Date:     ER, Mon Aug 27 15:18:47 CDT 2001   [STL]
 *
 * Purpose:  traceback S from nrn grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_nrnS(FILE *ofp, int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, 
	struct nrnscfg_s *mtx, 
	int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  double curr_sc;
  double sc;
  int    off;
  int    i;
  int    mid;
  int    ih, jh;
  int    iabs, jabs;
  int    xi, yi, xj, yj;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
  
  i = j - d;

  ih = i + off;
  jh = j + off;

  iabs = ih + start;
  jabs = jh + start;
  
  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
    
  curr_sc = mtx->sx[jh][d];

  if (curr_sc == -BIGFLOAT) { *flag = TRUE; return; }
  if (curr_sc < -BIGFLOAT || curr_sc >=  BIGFLOAT*100.) { Die ("in Tr_nrnS(). Wallace Shawn says: 'Inconceivable score' %f", curr_sc); }


  /* S -> a e b
   */
  if (!*flag && d == 1) {
    
    sc = nrn->S->tse + nrn->S->pp[j][d]; 
    if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
      {
	if (traceback) 
	  fprintf(ofp," S -> a e b X=(%d %d) Y=(%d %d) %f\n", sX[iabs], sX[jabs], sY[iabs], sY[jabs], nrn->S->pp[j][d]);
	
	curr_tr->type = dpcES;
	
	*flag = TRUE;
	return; 
      } 
  }

  /* S -> a S b
   */
  if (!*flag && d > 1) 
    {
      sc = nrn->S->tss + mtx->sx[jh-1][d-2] + nrn->S->pp[j][d];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  
	  if (traceback) {
	    fprintf(ofp," S -> a S b  X=(%d %d) Y=(%d %d) %f\n", sX[iabs], sX[jabs], sY[iabs], sY[jabs], nrn->S->pp[j][d]);
	    fprintf(ofp," trace S [%d %d] %f\n", ih+1, jh-1, mtx->sx[jh-1][d-2]);
	  }
	  
	  curr_tr->type = dpcPS;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-1,
						 i+1+(int)((d-2)/2),
						 i+1+(int)((d-2)/2)+1,
						 dpcP, dpcP));     
	  *flag = TRUE; 
	  return; 
	}     
    }
  /* S -> a L
   */
  if (!*flag && d > 0) 
    {
      sc = nrn->S->tsl + mtx->lx[jh][d-1] + nrn->S->ps[i];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  if (traceback) {
	    fprintf(ofp," S -> a L X=(%d) Y=(%d) %f\n", xi, yi, nrn->S->ps[i]);
	    fprintf(ofp," trace L [%d %d] %f\n", ih+1, jh, mtx->lx[jh][d-1]);
	  }
	  
	  curr_tr->type = dpcLS;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j,
						 i+1+(int)((d-1)/2),
						 i+1+(int)((d-1)/2)+1,
						 dpcL, dpcP));
	  *flag = TRUE;
	  return; 
	}
    }
  /* S -> R a -> e
   */
  if (!*flag && d == 0) 
    {
      sc = nrn->S->tsr + nrn->R->tre + nrn->R->ps[j];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  if (traceback) {
	    fprintf(ofp," S -> R a -> e X=(%d) Y=(%d) %f\n", sX[jabs], sY[jabs], nrn->S->ps[j]);
	  }
	  
	  curr_tr->type = dpcRS;
	  *flag = TRUE;
	  return; 
	}
    }
  /* S -> R a
   */
  if (!*flag && d > 0) 
    {
      sc = nrn->S->tsr + mtx->rx[jh-1][d-1] + nrn->S->ps[j];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  if (traceback) {
	    fprintf(ofp," S -> R a X=(%d) Y=(%d) %f\n", xj, yj, nrn->S->ps[j]);
	    fprintf(ofp," trace R [%d %d] %f\n", ih, jh-1, mtx->rx[jh-1][d-1]);
	  }
	  
	  curr_tr->type = dpcRS;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
						 i+(int)((d-1)/2),
						 i+(int)((d-1)/2)+1,
						 dpcR, dpcP));
	  *flag = TRUE;
	  return; 
	}
    }
  /* S -> L S
   */
  for (mid = 0; mid < d; mid++) {
    sc = nrn->S->tsb + mtx->sx[jh][mid] + mtx->lx[jh-mid-1][d-mid-1];

    if (!*flag && curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
      {
	if (traceback) {
	  fprintf(ofp," S -> L S \n");
	  fprintf(ofp," trace L [%d %d] %f\n", ih, jh-mid-1, mtx->lx[jh-mid-1][d-mid-1]);
	  fprintf(ofp," trace S [%d %d] %f\n", jh-mid, jh, mtx->sx[jh][mid]);
	}
	
	curr_tr->type = dpcBS;
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-mid-1,
					       i+(int)((d-mid-1)/2),
					       i+(int)((d-mid-1)/2)+1,
					       dpcL, dpcP));
	PushTraceknstack(dolist, AttachTracekn(curr_tr, j-mid, j,
					       j-mid+(int)((mid)/2),
					       j-mid+(int)((mid)/2)+1,
					       dpcP, dpcP));
	*flag = TRUE;
	return; 
	
      }  
  }  
}

/* Function: TR_nrnL()
 * Date:     ER, Mon Aug 27 15:50:00 CDT 2001   [STL]
 *
 * Purpose:  traceback L from nrn grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_nrnL(FILE *ofp, int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, struct nrnscfg_s *mtx, 
            int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  double curr_sc;
  double sc;
  int    off;
  int    i;
  int    xi, yi;
  int    ih, jh;
  int    iabs, jabs;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
    
  i = j - d;
  
  ih = i + off;
  jh = j + off;

  iabs = ih + start;
  jabs = jh + start;
  
  xi = sX[iabs];
  yi = sY[iabs];
  
  curr_sc = mtx->lx[jh][d];

  if (curr_sc == -BIGFLOAT) { *flag = TRUE; return; }
  if (curr_sc < -BIGFLOAT || curr_sc >=  BIGFLOAT*100.) { Die ("in Tr_nrnL(). Wallace Shawn says: 'Inconceivable score' %f", curr_sc); }

  /* L -> a e b
   */
  if (!*flag && d == 1) 
    {
      sc = nrn->L->tle + nrn->L->pp[j][d];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  
	  if (traceback) {
	    fprintf(ofp," L -> a e b   X=(%d %d) Y=(%d %d) %f\n", sX[iabs], sX[jabs], sY[iabs], sY[jabs], nrn->L->pp[j][d]);
	  }
	  curr_tr->type = dpcEL;
	  *flag = TRUE;
	  return; 
	}
    }
  /* L -> a S b
   */
  if (!*flag) 
    {
      sc = nrn->L->tls + mtx->sx[jh-1][d-2] + nrn->L->pp[j][d];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  if (traceback) {
	    fprintf(ofp," L -> a S b   X=(%d %d) Y=(%d %d) %f\n", sX[iabs], sX[jabs], sY[iabs], sY[jabs], nrn->L->pp[j][d]);
	    fprintf(ofp," trace S [%d %d] %f\n", ih+1, jh-1, mtx->sx[jh-1][d-2]);
	  }
	  
	  curr_tr->type = dpcPL;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-1,
						 i+1+(int)((d-2)/2),
						 i+1+(int)((d-2)/2)+1,
						 dpcP, dpcP));     
	  *flag = TRUE;
	  return; 
	}
    }
  /* L -> a L
   */
  if (!*flag) 
    {
      sc = nrn->L->tll + mtx->lx[jh][d-1] + nrn->L->ps[i];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  
	  if (traceback) {
	    fprintf(ofp," L -> a L X=(%d)  Y=(%d) %f\n", xi, yi, nrn->L->ps[i]);
	    fprintf(ofp," trace L [%d %d] %f\n", ih+1, jh, mtx->lx[jh][d-1]);
	  }
	  
	  curr_tr->type = dpcLL;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j,
						 i+1+(int)((d-1)/2),
						 i+1+(int)((d-1)/2)+1,
						 dpcL, dpcP));
	  *flag = TRUE;
	  return; 
	}
    } 
}

/* Function: TR_nrnR()
 * Date:     ER,  Mon Aug 27 15:50:00 CDT 2001  [STL]
 *
 * Purpose:  traceback R from nrn grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_nrnR(FILE *ofp, int *sX, int *sY, struct nrn_s *nrn, int start, int L, int j, int d, struct nrnscfg_s *mtx, 
            int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  double curr_sc;
  double sc;
  int    off;
  int    i;
  int    xj, yj;
  int    ih, jh;
  int    iabs, jabs;

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
    
  i = j - d;

  ih = i + off;
  jh = j + off;

  iabs = ih + start;
  jabs = jh + start;
  
  xj = sX[jabs];
  yj = sY[jabs];
  
  curr_sc = mtx->rx[jh][d];

  if (curr_sc == -BIGFLOAT) { *flag = TRUE; return; }
  if (curr_sc < -BIGFLOAT || curr_sc >=  BIGFLOAT*100.) { Die ("in Tr_nrnR(). Wallace Shawn says: 'Inconceivable score' %f", curr_sc); }

  /* R -> Ra -> e
   */
  if (!*flag && d == 0) 
    {
      sc = nrn->R->trr + nrn->R->tre + nrn->R->ps[j];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{
	  
	  if (traceback) {
	    fprintf(ofp," R -> R a -> e X=(%d) Y=(%d) %f \n", xj, yj, nrn->R->ps[j]);
	  }
	  curr_tr->type = dpcER;
	  *flag = TRUE;
	  return;
	} 
    }
  /* R -> R a
   */
  if (!*flag && d > 0) 
    {
      sc = nrn->R->trr + mtx->rx[jh-1][d-1] + nrn->R->ps[j];
      if (curr_sc < sc+MARGIN && curr_sc > sc-MARGIN) 
	{

	  if (traceback) {
	    fprintf(ofp," R -> R a X=(%d) Y=(%d) %f \n", xj, yj, nrn->R->ps[j]);
	    fprintf(ofp," trace R [%d %d] %f\n", ih, jh-1, mtx->rx[jh-1][d-1]);
	  }
	  
	  curr_tr->type = dpcRR;
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
					     i+(int)((d-1)/2),
						 i+(int)((d-1)/2)+1,
						 dpcR, dpcP));
	  *flag = TRUE;
	  return; 
	}
    }
}

/* Function: Trk_NRN()
 * Date:     ER, Mon Aug 27 14:51:04 CDT 2001    [STL]
 *
 * Purpose:  traceback for NR nussinov grammar (R.Dowell)
 *
 *           S -> aSb | aL | Ra | LS | aeb
 *           L -> aSb | aL | aeb
 *           R -> Ra  | e
 *
 */
void 
Trk_NRN(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,  int j, int d,
	struct nrn_s *nrn, struct nrnscfg_s *mtx, struct tracekn_s **ret_trace, int traceback, struct end_s *rnaends)
{ 
  struct tracekn_s      *tr;           /* the traceback tree under construction  */
  struct tracekn_s      *curr_tr;      /* ptr to node of tr we're working on     */
  struct traceknstack_s *dolist;       /* pushdown stack of active tr nodes      */
  int                    i,k,l;        /* coords in mtx's                        */
  int                    d1, d2;       /* coords in mtx's                        */
  int                    fl, *flag;    /* flags for the traceback                */
  int                    ih, jh;
  int                    off;
  
  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
  
  /* Initialize.
   * Start at j, d, d1 = (int)(d/2), d2 = d - (int)(d/2) - 1.
   */
  tr     = InitTracekn();       /* start a trace tree */
  dolist = InitTraceknstack();  /* start a stack for traversing the trace tree */
  
  if (d < 0) Die("check your traceback assignments");
  
  i = j - d;

  k = i + (int)(d/2);
  l = k + 1;

  ih = i + off;
  jh = j + off;

  
  fl   = FALSE;
  flag =   &fl;
  if (traceback) fprintf(ofp,"---------------------------------------------------\n");
  
  /* assigned state to (i,j):  dpcP = traceback S (V)
   *                           dpcL = traceback L (W)
   *                           dpcR = traceback R (WB)
   */
  
  curr_tr = AttachTracekn(tr, i, j, k, l, dpcP, dpcP);
  PushTraceknstack(dolist, curr_tr);
  
  /* Recursion. While there's active nodes in the stack, trace from them.
   *
   * This is cribbed from FillMtx_nested(); it's almost the exact reverse.
   * We know the best score, we just have to figure out where it came from.
   */
  while ((curr_tr = PopTraceknstack(dolist)) != NULL)
    {
      /* get some useful numbers, mostly for clarity */
      i = curr_tr->emiti;
      j = curr_tr->emitj;
      k = curr_tr->emitk;
      l = curr_tr->emitl;
      
      d  = j - i;
      
      d1 = (int)(d/2);
      d2 = d - d1 - 1;
      
      k = i + d1;
      l = j - d2;
      
      jh = j + off;
      ih = i + off;

      *flag = FALSE;

      if (traceback) fprintf(ofp,"---------------------------------------------------\n");
      if (traceback) fprintf(ofp,"%d %d  \n", ih, jh);
      
      /* Determine the matrix we have to trace down */
      
      if (curr_tr->type == dpcP)
        curr_tr->node = V;
      
      else if (curr_tr->type == dpcL)
        curr_tr->node = W;
      
      else if (curr_tr->type == dpcR)
        curr_tr->node = WB;
      
      switch (curr_tr->node){
	
	/*************************************
	 * TRACE S state
	 *************************************/
      case V:
        if (traceback) fprintf(ofp,"tracing S  %f   \n", mtx->sx[jh][d]);
	
        Tr_nrnS(ofp, sX, sY, nrn, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of S");
        break;         
	
	/*************************************
	 * TRACE L state
	 *************************************/
      case W:
        if (traceback) fprintf(ofp,"tracing L  %f   \n", mtx->lx[jh][d]);
	
        Tr_nrnL(ofp, sX, sY, nrn, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of L");
        break;         
	
	
	/*************************************
	 * TRACE R state
	 *************************************/
      case WB:
        if (traceback) fprintf(ofp,"tracing R  %f  \n", mtx->rx[jh][d]);
	
        Tr_nrnR(ofp, sX, sY, nrn, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of R");
        break;         
	
      default:
        Die("invalid traceback matrix assignement");
      }
    } /* while something is in the trace stack */
  
  FreeTracekn(curr_tr);
  FreeTraceknstack(dolist);
  *ret_trace = tr; 
}

