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


/* rnascfg.c
 *
 * ER, Tue Jun  1 09:43:11  CDT 1999 [St. Louis]
 * 
 * dynamic programming (cyk and inside) with the rnamodel
 *
 * 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"

static void bestvp(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx);
static void bestparsevp(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx);
static void checkloops(int L, struct rnamodel_s *rna);
static int  complete_pairs(int *ct, int jabs, int d);
static void cyk_nus(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L, 
		    struct nusmodel_s *nus, struct rnamtx_s *mtx, int traceback);
static void cyk_parse_ss(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L, 
			 struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp, int traceback);
static void cyk_pi2_from_posteriors(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L, 
				    struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp,
				    int traceback);
static void cyk_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L, 
		    struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp,
		    int traceback);
static void fillvp(int *sX, int *sY, struct pi2model_s *rna, int start, int L, int j, int d, 
		   double *vp, double **vx);
static void fillvp_ss(struct pi2model_s *rna, int j, int d, double *vp, double **vx);
static void fillparsevp(int *sX, int *sY, int *ct, struct pi2model_s *rna, int start, int L, int j, int d, 
			double *vp, double **vx);
static void fillvpo(int *sX, int *sY, struct pi2model_s *rna, int L, int jabs, int j, int d, 
		    double *vp, double **vox);
static int  how_many_paired(int *ct, int jabs, int d);
static int  how_many_unpaired(int *ct, int jabs, int d);
static void inside_nus(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
		       struct nusmodel_s *nus, struct rnamtx_s *mtx, double *sc);
static void inside_pi1(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
		       struct nusmodel_s *nus, struct othdpd_s *othdp, struct rnascfg_s *mx);
static void inside_parse_ss(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L,
			    struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, 
			    double *sc, double *vp);
static void inside_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
		       struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, 
		       double *sc, double *vp, int fastintloop);
static void inside_pi2_from_posteriors(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
				       struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *sc, double *vp);
static int  is_single_stranded(int *ct, int jabs, int d);
static void outside_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
			struct pi2model_s *pi2, struct rnamtx_s *in, struct rnamtx_ou_s *ou, 
			double *vp, double *sc,struct end_s *rnaends);

/* Function: AllocRNAMtx()
 *
 * Date:     ER, Fri Sep 28 10:59:45 CDT 2001  [St. Louis]
 *
 * Purpose:  allocate dp matrices for the RNA model
 * Args:     L    --  length of the sequences
 *           vx   --  dp matrix (L x L)
 *           wx   --  dp matrix (L x L)
 *           wbx  --  dp matrix (L x L)
 *
 * Purpose:  Allocates memory for the dp matrices of the RNA model
 *
 * Returns:  vx, wx, wbx are allocated.
 *
 */
struct rnamtx_s *
AllocRNAMtx(int L)
{
  struct rnamtx_s *mtx;       /* structure with dp matrices   */
  int              j;

  mtx = (struct rnamtx_s *) MallocOrDie (sizeof(struct rnamtx_s));

  mtx->rnaj = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->vx   = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->wx   = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->wbx  = (double **) MallocOrDie(sizeof(double *) * L);

  mtx->rnaj[0] = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->vx[0]   = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->wx[0]   = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->wbx[0]  = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);

  for (j = 1; j < L; j++) {
    mtx->rnaj[j] = mtx->rnaj[0] + j*(j+1)/2;
    mtx->vx[j]   = mtx->vx[0]   + j*(j+1)/2;
    mtx->wx[j]   = mtx->wx[0]   + j*(j+1)/2;
    mtx->wbx[j]  = mtx->wbx[0]  + j*(j+1)/2;
  }

  /* Initialize */
  PatternRNAMtx(L, mtx);

  return mtx;
}

struct rnamtx_ou_s *
AllocRNAMtxOutside(int L)
{
  struct rnamtx_ou_s *mtx;       /* structure with dp matrices   */
  int              j;

  mtx = (struct rnamtx_ou_s *) MallocOrDie (sizeof(struct rnamtx_ou_s));

  mtx->rnaj = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->vx   = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->wx   = (double **) MallocOrDie(sizeof(double *) * L);
  mtx->wbx  = (double **) MallocOrDie(sizeof(double *) * L);

  mtx->rnaj[0] = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->vx[0]   = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->wx[0]   = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);
  mtx->wbx[0]  = (double *) MallocOrDie(sizeof(double) * L * (L+1) / 2);

  for (j = 1; j < L; j++) {
    mtx->rnaj[j] = mtx->rnaj[0] + j*(j+1)/2;
    mtx->vx[j]   = mtx->vx[0]   + j*(j+1)/2;
    mtx->wx[j]   = mtx->wx[0]   + j*(j+1)/2;
    mtx->wbx[j]  = mtx->wbx[0]  + j*(j+1)/2;
  }

  /* Initialize */
  PatternRNAMtxOutside(L, mtx);

  return mtx;
}



/* Function: AllocScfgRNA()
 *
 * Date:     ER, Tue Sep 14 13:03:25 CDT 1999 [St. Louis]
 *
 * Purpose:  allocate matrices for the RNA model
 *
 * Args:     L    --  length of the sequences
 *           sc   --  dp matrix (4L+2)

 *           vp   --  dp matrix (L)
 *           vx   --  dp matrix (L x L)
 *           wx   --  dp matrix (L x L)
 *           wbx  --  dp matrix (L x L)
 *           rnaj --  dp matrix (L x L)
 * Purpose:  Allocates memory for the dp matrices of the RNA model
 *
 * Returns:  sc, vp, vx, wx, wbx, rnaj are allocated.
 *
 */
struct rnascfg_s *
AllocScfgRNA(int L, int fastintloop, int rnass)
{
  struct rnascfg_s *mx;       /* structure with dp matrices   */
  int               dim;
  int               j;

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

  /* Allocate a 0..L-1 square matrix.
   */
  mx->in   = AllocRNAMtx(L);
  mx->inrv = AllocRNAMtx(L);

  if (rnass) { mx->ou  = AllocRNAMtxOutside(L); mx->nrn = AllocScfgNRN(L);}

  if (fastintloop == TRUE) dim = 4*L+2;
  else                     dim = L*L + 3*L + 2;

  mx->sc      = (double  *) MallocOrDie(sizeof(double  ) * dim);
  mx->vp      = (double  *) MallocOrDie(sizeof(double  ) * L);
 
  /* initialize  matrices */
  for (j = 0; j < dim; j++) 
    mx->sc[j]  = 0.;

  for (j = 0; j < L; j++) 
    mx->vp[j]  = -BIGFLOAT;
 
  return mx;
}

/* Function: BestV()
 * Date:     ER, Sat Jan  1 10:06:54 CST 2000 [Panticosa]
 *
 * Purpose:  fill matrix V using the 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
BestV(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, 
      struct rnamtx_s *mtx, double *vp, int fastversion)
{
  int     x,y;
  int     iabs, jabs;
  int     xi,xj,yi,yj;
  int     xk,xl,yk,yl;
  int     i,mid;
  int     k,l;
  int     mid1,mid2;
  int     nt;
  double  scnt;
  double  sc, bestsc;

  bestsc = -BIGFLOAT;

  i = j - d;

  jabs = j + start;
  iabs = i + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */

  /* hairpin loop
   */
  /* this is the way to calculate IS1s using the pi2->is1->tn[d]
     
   */
    if (d < MAXRNALOOP) {
     scnt = 0.;  
     for (nt = 1; nt < d; nt++) {
       x = sX[i+nt];
       y = sY[i+nt];
       scnt += pi2->is1->ps[idx5(x,y)];
     } 
     if ((sc = scnt + pi2->v->t1 + pi2->is1->tn[d]) > bestsc) bestsc = sc;
    }  

    /* this is the way to calculate IS1s using an OTH Model
    if (d < MAXRNALOOP)  
    if ((sc = pi2->v->t1 
	 + mtx->rnaj[j-1][d-2]) > bestsc)  
      bestsc = sc;      
    */

  /* stem loops 
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
  /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1)  
    if ((sc = mtx->vx[j-1][d-2] 
	 + pi2->v->t2s 
	 + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
  
  /* this is the way to calculate IS2s using an OTH Model
   *
   * if (d > 1) if ((sc = vx[j-1][d-2] + pi2->v->t2s +
   *               pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
   */
  
  /* bulges L */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+mid];
      yk = sY[iabs+mid];
      xl = sX[jabs-1];
      yl = sY[jabs-1];
      
      /* this is the way to calculate IS2s using the pi2->is2->tn[d]
       */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[iabs+nt];
	y = sY[iabs+nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      if ((sc = mtx->vx[j-1][d-mid-1] 
	   + scnt 
	   + pi2->v->t2b - 1.0 
	   + pi2->is2b->tn[mid+1]
	   + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
      
       /* this is the way to calculate IS2s using an OTH Model
       *
       * if ((sc = vx[j-1][d-mid-1] + pi2->v->t2b + 
       *   rnaj[i+mid-1][mid-2] +
       *   pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
       */
    }
  
  /* bulges R */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+1];
      yk = sY[iabs+1];
      xl = sX[jabs-mid];
      yl = sY[jabs-mid];
      
      /* this is the way to calculate IS2s using the pi2->is2->tn[d]
       */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[jabs-nt];
	y = sY[jabs-nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
      if ((sc = mtx->vx[j-mid][d-mid-1] 
	   + scnt 
	   + pi2->v->t2b - 1.0
	   + pi2->is2b->tn[mid+1]
	   + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
      
      /* this is the way to calculate IS2s using an OTH Model
       *
       * if ((sc = vx[j-mid][d-mid-1] + pi2->v->t2b +
       *   rnaj[j-1][mid-2] +
       *   pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
       */     
    }
  
  /* internal loops */

  if (fastversion) {
    /* L^3 calculation of internal loops */
    bestvp(sX, sY, pi2, start, L, j, d, vp, mtx->vx);
    
    if (d > 3 && d < MAXRNALOOP) 
      for (mid = 0; mid <= (d-4); mid++) {
	if ((sc = vp[mid] + pi2->is2i->tn[d-mid] - LOG2(d-mid-3)) > bestsc) bestsc = sc;
      }  
    
  }
  else { /* L^4  calculation of internal loops */
    
    for (k = i+2; k < j-1; k++)  
      for (l = k; l < j-1; l++) { 
	
	mid1 = k - i; 
	mid2 = j - l; 
	
	
	if ((mid1+mid2+d) < MAXRNALOOP) { 
	  scnt = 0.; 
	  for (nt = 1; nt < mid1; nt++) { 
	    x = sX[iabs+nt]; 
	    y = sY[iabs+nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[jabs-nt]; 
	    y = sY[jabs-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  
	  if ((sc = mtx->vx[l][d-mid1-mid2] 
	       + pi2->v->t2i 
	       + scnt + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
	       + pi2->v->pp[idx5(sX[k],sY[k])][idx5(sX[l],sY[l])]) > bestsc) bestsc = sc;     
	  
	} 
      } 
  }

  
  /* multiloops */
  for (mid = 3; mid < d-3; mid++) {
    if ((sc = pi2->v->t3 
	 + mtx->wbx[j-d+1+mid][mid] 
	 + mtx->wbx[j-1][d-mid-3]) > bestsc) bestsc = sc;
  }   


   				/* summation */
  mtx->vx[j][d] = bestsc;
}

/* Function: BestParseV()
 * Date:     ER, Wed Apr  3 19:15:41 CST 2002 [STL]
 *
 * Purpose:  fill matrix V using the 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
BestParseV(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, 
	   struct rnamtx_s *mtx, double *vp)
{
  int     x,y;
  int     iabs, jabs;
  int     xi,xj,yi,yj;
  int     xk,xl,yk,yl;
  int     i,mid;
  int     k,l;
  int     mid1,mid2;
  int     nt;
  double  scnt;
  double  sc, bestsc;

  bestsc = -BIGFLOAT;

  i = j - d;

  jabs = j + start;
  iabs = i + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  bestparsevp(sX, sY, ct, pi2, start, L, j, d, vp, mtx->vx);
  
  /* Initialize diagonal 
   */
  if (ct[iabs] != jabs) { mtx->vx[j][d] = -BIGFLOAT; return; }
  
  /* Main recursion
   */
  
  /* hairpin loop
   */
  /* this is the way to calculate IS1s using the pi2->is1->tn[d]
   */   
  if (d < MAXRNALOOP &&
      is_single_stranded(ct, jabs-1, d-2)) {
    scnt = 0.;
    for (nt = 1; nt < d; nt++) {
      x = sX[i+nt];
      y = sY[i+nt];
      scnt += pi2->is1->ps[idx5(x,y)];
    }
    if ((sc = scnt + pi2->v->t1 + pi2->is1->tn[d]) > bestsc) bestsc = sc;
  }
  
  /* this is the way to calculate IS1s using an OTH Model 
     if (d < MAXRNALOOP &&
     is_single_stranded(ct, jabs-1, d-2))  
    if ((sc = pi2->v->t1 
	 + mtx->rnaj[j-1][d-2]) > bestsc)  
      bestsc = sc; 
  */
  /* stem loops 
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
  /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1)  
    if (ct[iabs+1] == jabs-1 && ct[jabs-1] == iabs+1 &&
	(sc = mtx->vx[j-1][d-2] 
	 + pi2->v->t2s 
	 + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
  
  /* bulges L */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP   &&
	ct[jabs-1] == iabs+mid &&
	how_many_paired (ct, iabs+mid-1, mid-2) == 0) 
      {
	xk = sX[iabs+mid];
	yk = sY[iabs+mid];
	xl = sX[jabs-1];
	yl = sY[jabs-1];
	
	/* this is the way to calculate IS2s using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[iabs+nt];
	  y = sY[iabs+nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	if ((sc = mtx->vx[j-1][d-mid-1] 
	     + scnt 
	     + pi2->v->t2b - 1.0
	     + pi2->is2b->tn[mid+1]
	     + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
	
	/* this is the way to calculate IS2s using an OTH Model
	 *
	 * if ((sc = vx[j-1][d-mid-1] + pi2->v->t2b + 
	 *   rnaj[i+mid-1][mid-2] +
	 *   pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
	 */
      }
  
  /* bulges R */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP &&
	ct[iabs+1] == jabs-mid &&
	how_many_paired (ct, jabs-1, mid-2) == 0) 
      {
	xk = sX[iabs+1];
	yk = sY[iabs+1];
	xl = sX[jabs-mid];
	yl = sY[jabs-mid];
	
	/* this is the way to calculate IS2s using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[jabs-nt];
	  y = sY[jabs-nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	
	if ((sc = mtx->vx[j-mid][d-mid-1] 
	     + scnt 
	     + pi2->v->t2b - 1.0
	     + pi2->is2b->tn[mid+1]
	     + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]) > bestsc) bestsc = sc;
     }
  
  /* internal loops */
  
#ifdef L4CYKIL
  /* L^4  calculation of internal loops */
  for (k = i+2; k < j-1; k++)  
    for (l = k; l < j-1; l++) { 
      
      mid1 = k - i; 
      mid2 = j - l; 
      
      if (ct[iabs+mid1] == jabs - mid2 &&
	  ct[jabs-mid2] == iabs + mid1 &&
	  is_single_stranded(ct, iabs+mid-1, mid1-2) &&
	  is_single_stranded(ct, jabs-1,     mid2-2) &&
	  (mid1+mid2+d) < MAXRNALOOP) 
	{ 
	  scnt = 0.; 
	  for (nt = 1; nt < mid1; nt++) { 
	    x = sX[iabs+nt]; 
	    y = sY[iabs+nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[jabs-nt]; 
	    y = sY[jabs-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  
	  if ((sc = mtx->vx[l][d-mid1-mid2] 
	       + pi2->v->t2i 
	       + scnt + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
	       + pi2->v->pp[idx5(sX[k],sY[k])][idx5(sX[l],sY[l])]) > bestsc) bestsc = sc; 
	} 
    } 
#else
  /* L^3 calculation of internal loops */
  if (d > 3 && d < MAXRNALOOP) 
    for (mid = 0; mid <= (d-4); mid++) {
     if ((sc = vp[mid] + pi2->is2i->tn[d-mid] - LOG2(d-mid-3)) > bestsc) bestsc = sc;
    }
#endif

  
  /* multiloops */ 
  for (mid = 3; mid < d-3; mid++) {
    if (complete_pairs(ct, jabs-1,     d-mid-3)   &&
	complete_pairs(ct, iabs+mid+1, mid)       &&
	(sc = pi2->v->t3 
	 + mtx->wbx[j-d+1+mid][mid] 
	 + mtx->wbx[j-1][d-mid-3]) > bestsc) bestsc = sc;
  }


   				/* summation */
  mtx->vx[j][d] = bestsc;
}

/* Function: BestW()
 * Date:     ER, Sat Jan  1 09:59:44 CST 2000 [Zaragoza]
 *
 * Purpose:  fill matrix W using the CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wx is filled at position [j][d].
 *
 */
void
BestW(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx)
{
  int     xi,xj,yi,yj;
  int     i, mid;
  int     iabs, jabs;
  double  sc, bestsc;

  i = j - d;

  iabs = i + start;
  jabs = j + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  bestsc = -BIGFLOAT;

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

  /* Main recursion
   */
  
                                /* i left; connect to i+1, j; emit xi,yi */
  if ((sc = mtx->wx[j][d-1] + pi2->w->tl + pi2->w->pl[idx5(xi,yi)]) > bestsc) bestsc = sc;
  
				/* j right; connect to i, j-1; emit xj,yj */
  if ((sc = mtx->wx[j-1][d-1] + pi2->w->tr + pi2->w->pr[idx5(xj,yj)]) > bestsc) bestsc = sc;
  
				/* i,j pair; connect to i+1,j-1; emit xy */
  if ((sc = mtx->vx[j][d] + pi2->w->tv + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)]) > bestsc) bestsc = sc;
  
				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if ((sc = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + pi2->w->tw) > bestsc) bestsc = sc;
  
				/* summation */
  mtx->wx[j][d] = bestsc;
}

/* Function: BestParseW()
 * Date:     ER,  Wed Apr  3 19:15:41 CST 2002 [STL]
 *
 * Purpose:  fill matrix W using the CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wx is filled at position [j][d].
 *
 */
void
BestParseW(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx)
{
  int     xi,xj,yi,yj;
  int     i, mid;
  int     iabs, jabs;
  double  sc, bestsc;

  i = j - d;

  iabs = i + start;
  jabs = j + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  bestsc = -BIGFLOAT;

  /* Initialize diagonal 
   */
 /* wx has to have structure inside
   */
  if (d == 0) { mtx->wx[j][d] = -BIGFLOAT; return; }

  /* Main recursion
   */

                                /* i left; connect to i+1, j; emit xi,yi */
  if (ct[iabs] == iabs &&
      (sc = mtx->wx[j][d-1] + pi2->w->tl + pi2->w->pl[idx5(xi,yi)]) > bestsc) bestsc = sc;
  
				/* j right; connect to i, j-1; emit xj,yj */
  if (ct[jabs] == jabs &&
      (sc = mtx->wx[j-1][d-1] + pi2->w->tr + pi2->w->pr[idx5(xj,yj)]) > bestsc) bestsc = sc;
  
				/* i,j pair; connect to i+1,j-1; emit xy */
  if (ct[iabs] == jabs && 
      ct[jabs] == iabs &&
      (sc = mtx->vx[j][d] + pi2->w->tv + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)]) > bestsc) bestsc = sc;
  
				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if (complete_pairs(ct, jabs-mid-1, d-mid-1) &&
	complete_pairs(ct, jabs,       mid)     &&
	(sc = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + pi2->w->tw) > bestsc) bestsc = sc;
  
				/* summation */
  mtx->wx[j][d] = bestsc;
}

/* Function: BestWB()
 * Date:     ER, Sat Jan  1 09:59:44 CST 2000 [Zaragoza]
 *
 * Purpose:  fill matrix WB using the CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][b]
 *
 */
void
BestWB(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx)
{
  int     xi,xj,yi,yj;
  int     i, mid;
  int     iabs, jabs;
  double  sc, bestsc;

  i = j - d;

  iabs = i + start;
  jabs = j + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  bestsc = -BIGFLOAT;

  /* wbx has to have structure inside
   */
  if (d == 0) { mtx->wbx[j][0] = -BIGFLOAT; return; }

  /* Main recursion
   */
                                /* i left; connect to i+1, j; emit xi,yi */
  if ((sc = mtx->wbx[j][d-1] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)]) > bestsc) bestsc = sc;

				/* j right; connect to i, j-1; emit xj,yj */
  if ((sc = mtx->wbx[j-1][d-1] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)]) > bestsc) bestsc = sc;

				/* i,j pair; connect to i+1,j-1; emit xy */
  if ((sc = mtx->vx[j][d] + pi2->wb->tv + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)]) > bestsc) bestsc = sc;

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if ((sc = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1] + pi2->wb->tw) > bestsc) bestsc = sc;

				/* summation */
  mtx->wbx[j][d] = bestsc;
}


/* Function: BestParseWB()
 * Date:     ER, Wed Apr  3 19:17:27 CST 2002 [STL]
 *
 * Purpose:  fill matrix WB using the CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][b]
 *
 */
void
BestParseWB(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx)
{
  int     xi,xj,yi,yj;
  int     i, mid;
  int     iabs, jabs;
  double  sc, bestsc;

  i = j - d;

  iabs = i + start;
  jabs = j + start;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  bestsc = -BIGFLOAT;

  /* wbx has to have structure inside
   */
  if (d == 0) { mtx->wbx[j][0] = -BIGFLOAT; return; }

  /* Main recursion
   */
                                /* i left; connect to i+1, j; emit xi,yi */
  if (ct[iabs] == iabs &&
      (sc = mtx->wbx[j][d-1] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)]) > bestsc) bestsc = sc;

				/* j right; connect to i, j-1; emit xj,yj */
  if (ct[jabs] == jabs &&
      (sc = mtx->wbx[j-1][d-1] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)]) > bestsc) bestsc = sc;

				/* i,j pair; connect to i+1,j-1; emit xy */
  if (ct[iabs] == jabs && 
      ct[jabs] == iabs &&
      (sc = mtx->vx[j][d] + pi2->wb->tv + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)]) > bestsc) bestsc = sc;

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if (complete_pairs(ct, jabs-mid-1, d-mid-1) &&
	complete_pairs(ct, jabs,       mid)     &&
	(sc = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1] + pi2->wb->tw) > bestsc) bestsc = sc;

				/* summation */
  mtx->wbx[j][d] = bestsc;
}


/* Function: CalPairs()
 * Date:     ER, Sun Aug 19 18:00:38 CDT 2001   [St. Louis]
 *
 * Purpose: Using Inside and Outside algorithm calculates the probability that
 *          2 positions pair.
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           
 *
 */
void
CalPairs (int *sX, int *sY, int start, int L, struct pi2model_s *pi2, struct rnamtx_s *in, struct rnamtx_ou_s *ou,
	  double ***ret_pp, double **ret_ps, struct end_s *rnaends)
{
  int      off, len;
  int      jabs, iabs;
  int      ih,jh;
  int      i, j, d;
  int      k, l;
  int      x, y;
  int      s;
  int      mid1, mid2;
  int      nt;
  int      xi, xj, yi, yj;
  double **pp;
  double  *ps;
  double  *pl, *pr;
  double  *pb, *pi;
  double   prob_pp, prob_pb, prob_pi, prob_pl, prob_pr;
  double   probtot;
  double   scnt;
  double   sc;

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

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
  
    /* Allocate P[][] */
  pp    = (double **) MallocOrDie(sizeof(double *) * len);
  pp[0] = (double *)  MallocOrDie(sizeof(double)   * len * (len+1) / 2);
  ps    = (double *)  MallocOrDie(sizeof(double)   * len);
  pl    = (double *)  MallocOrDie(sizeof(double)   * len);
  pr    = (double *)  MallocOrDie(sizeof(double)   * len);
  pb    = (double *)  MallocOrDie(sizeof(double)   * len);
  pi    = (double *)  MallocOrDie(sizeof(double)   * len);

  for (j = 1; j < len; j++) 
    pp[j]   = pp[0] + j*(j+1)/2;

  /* Initialize P[][] */
  for (j = 0; j < len; j++) {
    pl[j] =  -BIGFLOAT;
    pr[j] =  -BIGFLOAT;
    pb[j] =  -BIGFLOAT;
    pi[j] =  -BIGFLOAT;

    for (d = 0; d <= j; d++) 
      pp[j][d] = -BIGFLOAT;
  }
  
  /* log_2 P(align | RNAmodel) */
  probtot = in->wx[off+len-1][len-1];

 /* pl[i]
   */
  for (i = 0; i < len; i++) {
    
    ih   = i  + off;
    iabs = ih + start;
    
    xi = sX[iabs];
    yi = sY[iabs];
    
    prob_pl = 0.0;
    
    prob_pl += EXP2(ou->wx[i][0]  + pi2->w->tl  + pi2->w->pl[idx5(xi,yi)]  - probtot);
    prob_pl += EXP2(ou->wbx[i][0] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)] - probtot);

    for (d = 1; i+d < len; d++) {
      j  = i + d;
      jh = j + off;
      
      prob_pl += EXP2(ou->wx[j][d]  + in->wx[jh][d-1]  + pi2->w->tl  + pi2->w->pl[idx5(xi,yi)]  - probtot);
      prob_pl += EXP2(ou->wbx[j][d] + in->wbx[jh][d-1] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)] - probtot);

    }
    
    if (prob_pl > 1.0+MARGIN || prob_pl < 0.0-MARGIN) 
      Die ("CalPairs(): pl Posterior probabilities are getting out of range! i=%d %f\n", i, prob_pl);

    /* convert to logs */
    pl[i] = LOG2(prob_pl);
  }
  
  /* pr[j]
   */
  for (j = 0; j < len; j++) {
    
     jh   = j  + off;
     jabs = jh + start;
    
    xj = sX[jabs];
    yj = sY[jabs];
    
    prob_pr = 0.0;
    
    prob_pr += EXP2(ou->wx[j][0]  + pi2->w->tr  + pi2->w->pr[idx5(xj,yj)]  - probtot);
    prob_pr += EXP2(ou->wbx[j][0] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)] - probtot);
 
    for (d = 1; d <= j; d++) {
      prob_pr += EXP2(ou->wx[j][d]  + in->wx[jh-1][d-1]  + pi2->w->tr  + pi2->w->pr[idx5(xj,yj)]  - probtot);
      prob_pr += EXP2(ou->wbx[j][d] + in->wbx[jh-1][d-1] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)] - probtot);
    }

    if (prob_pr > 1.0+MARGIN || prob_pr < 0.0-MARGIN) 
      Die ("CalPairs(): pr Posterior probabilities are getting out of range! i=%d %f\n", j, prob_pr);

    /* convert to logs */
    pr[j] = LOG2(prob_pr);
  }  
  

  /* Pb[j]
   *
   * Posterior probability o being in a bulge
   */
  for (j = 0; j < len; j++) {
    
     jh   = j  + off;
     jabs = jh + start;
    
    xj = sX[jabs];
    yj = sY[jabs];
    
    prob_pb = 0.0;

  }

  /* Pi[j]
   *
   * Posterior probability o being in an interior loop
   */
  if (0) {
    for (s = 0; s < len; s++) {
      
      prob_pi = 0.0;
      
      /* in the left arm */
      for (j = 0; j < len; j++) {
	
	jh   = j  + off;
	jabs = jh + start;
	
	for (d = 0; d <= j-s; d++) {
	  
	  i    = j - d;
	  iabs = jabs - d;
	  
	  xi = sX[iabs];
	  yi = sY[iabs];
	  xj = sX[jabs];
	  yj = sY[jabs];
	  
	  for (k = 0; k < s-1; k++)
	    for (l = len-1; l > j+1; l--) {
	    
	      mid1 = i - k;
	      mid2 = l - j;
	      
	      scnt = 0.;
	      if ((mid1+mid2+d) < MAXRNALOOP) {
		for (nt = 1; nt < mid1; nt++) {
		  x = sX[iabs-nt];
		  y = sY[iabs-nt];
		  scnt += pi2->is2i->ps[idx5(x,y)];
		}
		for (nt = 1; nt < mid2; nt++) {
		  x = sX[jabs+nt];
		  y = sY[jabs+nt];
		  scnt += pi2->is2i->ps[idx5(x,y)];
		}
		
		prob_pi += 
		  EXP2(ou->vx[l][l-k] + in->vx[jh][d] + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)] 
		       + pi2->v->t2i + scnt + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3) - probtot);
	      }
	    }
	}
      }
    }
    
  }
  
  /* Add left and right and bulge and internal loop single bases together into ps */
  for (j = 0; j < len; j++) 
    ps[j] = LOG2(EXP2(pl[j]) + EXP2(pr[j]) + EXP2(pb[j]) + EXP2(pi[j])); 

  for (j = 0; j < len; j++) {
    
    jh   = j  + off;
    jabs = jh + start;
    
    for (d = 0; d <= j; d++) {
      
      i    = j - d;
      iabs = jabs - d;
      
      prob_pp = 0.0;
      
      xi = sX[iabs];
      yi = sY[iabs];
      xj = sX[jabs];
      yj = sY[jabs];
      
      
      /* external pair
       */
      if ( (sc = EXP2(ou->wx[j][d]  + in->vx[jh][d] + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)]  + pi2->w->tv  - probtot)) > 1.0+MARGIN)
	Die ("CalPairs(): external pair probabilities are getting out of range! i=%d j=%d jh=%d %.6f\n", j-d, j, jh, sc);  
      else
	prob_pp += sc;
      
      /* multiloop start pair
       */
      if( (sc = EXP2(ou->wbx[j][d] + in->vx[jh][d] + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)] + pi2->wb->tv - probtot)) > 1.0+MARGIN)
	Die ("CalPairs(): multiloop start pair probabilities are getting out of range! i=%d j=%d jh=%d %.6f\n", j-d, j, jh, sc);  
      else
	prob_pp += sc;
     
      /* internal pair
       */
      /* Stem Loop */
      mid1 = 1;
      mid2 = 1;
      k = i - mid1;
      l = j + mid2;

      if (k >= 0 && l < len) 
	{
	  if ((sc = EXP2(ou->vx[l][l-k] + in->vx[jh][d] + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)] 
			 + pi2->v->t2s  - probtot)) > 1.0+MARGIN)
	    Die ("CalPairs(): stem loop probabilities are getting out of range! i=%d j=%d jh=%d %.6f\n", j-d, j, jh, sc);  
	    
	  else
	    prob_pp += sc;  
      }

     /* Bulge L */
      mid2 = 1;
      l = j + mid2;
      if (l < len) 
	{
	  for (k = 0; k < i-1; k++) {
	    mid1 = i - k;
	    
	    if ((mid1+mid2) < MAXRNALOOP) {
	      scnt = 0.;
	      for (nt = 1; nt < mid1; nt++) {
		x = sX[iabs-nt];
		y = sY[iabs-nt];
		scnt += pi2->is2b->ps[idx5(x,y)];
	      }
	      
	      if( (sc = EXP2(ou->vx[l][l-k] + in->vx[jh][d] + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)] 
			     + pi2->v->t2b -1.0 + scnt + pi2->is2b->tn[mid1+mid2] - probtot)) > 1.0+MARGIN)
		Die ("CalPairs(): Bulge L pair probabilities are getting out of range! i=%d j=%d jh=%d %.6f\n", j-d, j, jh, sc);  
	      else
		prob_pp += sc;
	    }
	    
	  }
	}
      
  /* Bulge R */
      mid1 = 1;
      k = i - mid1;
      if (k >= 0) 
	{
	  for (l = len-1; l > j+1; l--) {
	    mid2 = l - j;
	    
	    if ((mid1+mid2) < MAXRNALOOP) {
	      scnt = 0.;
	      for (nt = 1; nt < mid2; nt++) {
		x = sX[jabs+nt];
		y = sY[jabs+nt];
		scnt += pi2->is2b->ps[idx5(x,y)];
	      }
	      
	      if( (sc = EXP2(ou->vx[l][l-k] + in->vx[jh][d] + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)] 
			     + pi2->v->t2b - 1.0 + scnt + pi2->is2b->tn[mid1+mid2] - probtot)) > 1.0+MARGIN) 
		Die ("CalPairs(): Bulge R pair probabilities are getting out of range! i=%d j=%d jh=%d %.6f\n", j-d, j, jh, sc); 
	      else
		prob_pp += sc;
	    }
	  }
	}
      
      /* Internal Loops */
      for (k = 0; k < i-1; k++)
	for (l = len-1; l > j+1; l--) {
	  
	  mid1 = i - k;
	  mid2 = l - j;
	  
	  if ((mid1+mid2) < MAXRNALOOP) {
	    scnt = 0.;
	    for (nt = 1; nt < mid1; nt++) {
	      x = sX[iabs-nt];
	      y = sY[iabs-nt];
	      scnt += pi2->is2i->ps[idx5(x,y)];
	    }
	    
	    for (nt = 1; nt < mid2; nt++) {
	      x = sX[jabs+nt];
	      y = sY[jabs+nt];
	      scnt += pi2->is2i->ps[idx5(x,y)];
	    }
	    
	    if( (sc = EXP2(ou->vx[l][l-k] + in->vx[jh][d] + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)] 
			   + pi2->v->t2i + scnt + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3) - probtot)) > 1.0+MARGIN)
	      Die ("CalPairs(): IntLoop pair probabilities are getting out of range! i=%d j=%d jh=%d %f\n", j-d, j, jh, sc);  
	    else
		prob_pp += sc;
	  }
	}
      
      if (prob_pp > 1.0+MARGIN || prob_pp < 0.0-MARGIN) 
	Die ("CalPairs(): Pair Posterior probabilities are getting out of range! i=%d j=%d jh=%d %f\n", j-d, j, jh, prob_pp);
      
      /* convert to logs */
      pp[j][d] = LOG2(prob_pp);
      
    }
  }
  
  *ret_ps = ps;
  *ret_pp = pp;
  
  free(pl); free(pr);
  free(pb); free(pi);
  
}

/* Function: CYKParseASecondaryStructure()
 * Date:     ER, Wed Apr  3 19:07:29 CST 2002 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           The structure is given, calculate the best-scoring parse
 *
 */
int
CYKParseRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L,
	    struct rnamodel_s *rna, struct nullmodel_s *null, struct othdpd_s *othdp, 
	    struct rnamtx_s *mtx, double *vp, int logodds, int do_nus, int traceback)
{
  int j, d;
  int jabs;

  PatternVec(L, vp);
  PatternRNAMtx(L, mtx);

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {
	jabs = j + start;
	
	/* 
	 * mx->rnaj[j][d] = ViterbiOTHDiag_L(ofp, sX, sY, iabs, d+1, rna->pi2->Rloop, othdp, mx->sc);
	 *
	 * This is the correct way to calculate rnaj, but it takes too long.
	 * What we  use instead assumes that there ara no bases emited by
	 * the flanking models. 
	 *
	 */
	if (j == 0 || d == 0) 
	  mtx->rnaj[j][d] = ScoreWithOTH(ofp, sX, sY, jabs, d+1, rna->pi2->Rloop, FALSE);
	else 
	  mtx->rnaj[j][d] = mtx->rnaj[j-1][d-1] + ScoreWithOTHMatrix(sX, sY, jabs, rna->pi2->Rloop);
      }
  
  if (logodds) {
    for (j = 0; j < L; j++)
      for (d = 0; d <= j; d++)
	mtx->rnaj[j][d] += (d+1) * 2. * null->meta;
  }

  cyk_parse_ss(ofp, sqinfoX, sX, sqinfoY, sY, ct, 0, L, rna->pi2, othdp, mtx, vp, traceback);

  return 1;
}
 
/* Function: CYKRNA()
 * Date:     ER, Mon Sep 27 15:14:45 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures.
 *
 */
int
CYKRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
       struct rnamodel_s *rna, struct nullmodel_s *null, struct othdpd_s *othdp, 
       struct rnamtx_s *mtx, double *vp, int logodds, int do_nus, int traceback)
{
  int j, d;
  int jabs;

  PatternVec(L, vp);
  PatternRNAMtx(L, mtx);

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {
	jabs = j + start;
	
	/* 
	 * mx->rnaj[j][d] = ViterbiOTHDiag_L(ofp, sX, sY, iabs, d+1, rna->pi2->Rloop, othdp, mx->sc);
	 *
	 * This is the correct way to calculate rnaj, but it takes too long.
	 * What we  use instead assumes that there ara no bases emited by
	 * the flanking models. 
	 *
	 */
	if (j == 0 || d == 0) 
	  mtx->rnaj[j][d] = ScoreWithOTH(ofp, sX, sY, jabs, d+1, rna->pi2->Rloop, FALSE);
	else 
	  mtx->rnaj[j][d] = mtx->rnaj[j-1][d-1] + ScoreWithOTHMatrix(sX, sY, jabs, rna->pi2->Rloop);
      }
  
  if (logodds) {
    for (j = 0; j < L; j++)
      for (d = 0; d <= j; d++) {
	mtx->rnaj[j][d] += (d+1) * 2. * null->meta;
      }
  }

  if (do_nus) 
    cyk_nus(ofp, sqinfoX, sX, sqinfoY, sY, 0, L, rna->nus, mtx, traceback);
  else 
    cyk_pi2(ofp, sqinfoX, sX, sqinfoY, sY, 0, L, rna->pi2, othdp, mtx, vp, traceback);
    /*cyk_pi2_from_posteriors(ofp, sqinfoX, sX, sqinfoY, sY, 0, L, rna->pi2, othdp, mtx, vp, TRUE);*/

  return 1;
}


/* Function: CYKTracebackRNA()
 * Date:     ER, Mon Oct  8 11:39:16 CDT 2001  [St. Louis]
 *
 * Purpose:  
 *          
 *
 */
void
CYKTracebackRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, char *gss, int start, int L,
		struct pi2model_s *pi2, struct rnascfg_s *mx, 
		int do_nus, int logodds, int traceback, struct end_s *rnaends, int isrnarev)
{
  struct rnamtx_s    *inside;
  struct tracekn_s   *tr;                  /* traceback of a predicted RNA structure        */
  char               *cc;                  /* secondary structure with integers             */
  char               *ss;                  /* secondary structure with > <                  */
  int                *reqX, *reqY;         /* reverse complements                           */
  int                 off, len;

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

 /* reverse sequence */
  if (isrnarev) {
    reqX = (int *) MallocOrDie(sizeof(int) * L);
    reqY = (int *) MallocOrDie(sizeof(int) * L);
    RevComp(reqX, sX+start, L);
    RevComp(reqY, sY+start, L);

    if (sqinfoX.flags & SQINFO_SS) gss = RevSS(gss+start, L);

    sX = reqX;
    sY = reqY;
    start = 0;

    inside = mx->inrv;
  }
  else 
    inside = mx->in;

  off = rnaends->lend[0] - start;
  
  /*  Use RNA grammar and CYK algorithm to traceback.
   */
  Trk_RNA(ofp, sqinfoX, sX, sqinfoY, sY, start, L, len-1, len-1, pi2, inside, mx->vp, &tr, traceback, rnaends);

  /*
   * print output -- alignment + structure.
   */
  TraceRNA(tr, L, off, FALSE, &ss, &cc);
  PrintSSseqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, ss, cc);

  /* If this was a kSquid or Selex file with a "given" secondary structure,
   * compare the two structures
   */
  if (sqinfoX.flags & SQINFO_SS) {
    
    PrintCtSeqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, gss);
    CompareRNAStructures(ofp, start, L, gss, ss);
  }

  if (isrnarev) { free(reqX); free(reqY); }
  free(cc);                                                                                                       
  free(ss);                                                                                                                                         
  FreeTracekn(tr);
}

/* Function: CYKParseTracebackRNA()
 * Date:     ER,  Fri Apr  5 15:06:42 CST 2002 [St. Louis]
 *
 * Purpose:  
 *          
 *
 */
void
CYKParseTracebackRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, char *gss, int start, int L,
		     struct pi2model_s *pi2, struct rnascfg_s *mx, 
		     int do_nus, int logodds, int traceback, struct end_s *rnaends, int isrnarev)
{
  struct rnamtx_s    *inside;
  struct tracekn_s   *tr;                  /* traceback of a predicted RNA structure        */
  char               *cc;                  /* secondary structure with integers             */
  char               *ss;                  /* secondary structure with > <                  */
  int               *ct;                   /* integer form for given structure gss          */
  int                *reqX, *reqY;         /* reverse complements                           */
  int                 off, len;

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

 /* reverse sequence */
  if (isrnarev) {
    reqX = (int *) MallocOrDie(sizeof(int) * L);
    reqY = (int *) MallocOrDie(sizeof(int) * L);
    RevComp(reqX, sX+start, L);
    RevComp(reqY, sY+start, L);

    gss = RevSS(gss+start, L);
    KHS2ct(gss, L, FALSE, &ct);
    
    sX = reqX;
    sY = reqY;
    start = 0;
    
    inside = mx->inrv;
  }
  else {
    inside = mx->in;
    KHS2ct(gss+start, L, FALSE, &ct);
 }
  
  off = rnaends->lend[0] - start;

  /*  Use RNA grammar and CYK algorithm to traceback.
   */
  Trk_Parse_RNA(ofp, sqinfoX, sX, sqinfoY, sY, ct, start, L, len-1, len-1, pi2, inside, mx->vp, &tr, traceback, rnaends);

  /*
   * print output -- alignment + structure.
   */
  TraceRNA(tr, L, off, FALSE, &ss, &cc);
  PrintSSseqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, ss, cc);

  /* compare with the given structure
   */
  CompareRNAParses(ofp, start, L, gss, ss);

  

  if (isrnarev) { free(reqX); free(reqY); }
  free (ct);
  free (ss);
  free (cc);
  FreeTracekn(tr);
}

/* Function: FillV()
 * Date:     ER, Mon Jun 14 11:26:44 CDT 1999 [St. Louis]
 *
 * Purpose:  fill matrix V using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix vx is filled at position [j][d].
 *
 */
void
FillV(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, 
      struct rnamtx_s *mtx, double *vp, double *sc, int fastversion)
{
  int     idx = 0;
  int     x,y;
  int     xi,xj,yi,yj;
  int     xk,xl,yk,yl;
  int     iabs, jabs, mid;
  int     i,k,l;
  int     mid1,mid2;
  int     nt;
  double  scnt;
  double  sum = 0.0;
  int     verbose = FALSE;

  jabs = j + start;

  i    = j    - d;
  iabs = jabs - d;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */

  /* hairpin loop
   */
  /* this is the way to calculate IS1s using the pi2->is1->tn[d]
   */
  if (d < MAXRNALOOP) {
    scnt = 0.;  
    for (nt = 1; nt < d; nt++) { 
      x = sX[iabs+nt]; 
      y = sY[iabs+nt]; 
      scnt += pi2->is1->ps[idx5(x,y)]; 
    } 
    sc[idx++] = scnt + pi2->v->t1 + pi2->is1->tn[d];
  } 

  /* this is the way to calculate IS1s using an OTH Model
  if (d < MAXRNALOOP) 
    sc[idx++] = pi2->v->t1  
      + mtx->rnaj[j-1][d-2];
    */

  /* stem loops 
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
  /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1)  
    sc[idx++] = mtx->vx[j-1][d-2] 
      + pi2->v->t2s 
      + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
  
  /* bulges L */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+mid];
      yk = sY[iabs+mid];
      xl = sX[jabs-1];
      yl = sY[jabs-1];
      
      /* this is the way to calculate IS2b using the pi2->is2->tn[d]
      */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[iabs+nt];
	y = sY[iabs+nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      sc[idx++] = mtx->vx[j-1][d-mid-1] 
	+ scnt 
	+ pi2->v->t2b - 1.0
	+ pi2->is2b->tn[mid+1]
	+ pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
    }
  
  /* bulges R */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+1];
      yk = sY[iabs+1];
      xl = sX[jabs-mid];
      yl = sY[jabs-mid];
      
      /* this is the way to calculate IS2b using the pi2->is2->tn[d]
       */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[jabs-nt];
	y = sY[jabs-nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
      sc[idx++] = mtx->vx[j-mid][d-mid-1] 
	+ scnt 
	+ pi2->v->t2b - 1.0
	+ pi2->is2b->tn[mid+1]
	+ pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
    }

  /* internal loops */
  if (fastversion) {  /*L^3 calculation of internal loops */
    
    fillvp(sX, sY, pi2, start, L, j, d, vp, mtx->vx);
    
    if (d > 3) 
      for (mid = 0; mid <= (d-4); mid++) {
	if ((d-mid) < MAXRNALOOP) {
	  
	  sc[idx++] = vp[mid] + pi2->is2i->tn[d-mid] - LOG2(d-mid-3); 
	  
	  if (verbose) {
	    
	    sum = 0.0;
	    
	    for (k = i+2; k < j-1; k++) 
	      for (l = k; l < j-1; l++) { 	
		
		mid1 = k - i;
		mid2 = j - l;	
		
		if ((mid1+mid2) < MAXRNALOOP && (d-mid1-mid2) == mid) {
		  scnt = 0.;
		  for (nt = 1; nt < mid1; nt++) {
		    x = sX[i+nt];
		    y = sY[i+nt];
		  scnt += pi2->is2i->ps[idx5(x,y)];
		  }
		  for (nt = 1; nt < mid2; nt++) { 
		    x = sX[j-nt]; 
		    y = sY[j-nt]; 
		    scnt += pi2->is2i->ps[idx5(x,y)]; 
		  }  
		  
		  sum += EXP2(mtx->vx[l][d-mid1-mid2] 
			      + pi2->v->t2i 
			      + scnt 
			      + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
			      + pi2->v->pp[idx5(sX[k],sY[k])][idx5(sX[l],sY[l])]);
		} 
		
	      }
	  sum = LOG2(sum);
	  
	  if (sum > -50.0 && (sum > sc[idx-1]+MARGIN || sum < sc[idx-1]-MARGIN)) 
	    Die ("bad Internal loop j=%d d=%d mid=%d L^3=%.10f L^4=%.10f\n", j, d, mid, sc[idx-1], sum);
	  }
	}
      } /* mid loop */
  }
  else {  /* L^4 calculation of internal loops */
    
    for (k = i+2; k < j-1; k++) 
      for (l = k; l < j-1; l++) { 	
	
	mid1 = k - i;
	mid2 = j - l;	
 	
	if ((mid1+mid2) < MAXRNALOOP) {
	  scnt = 0.;
	  for (nt = 1; nt < mid1; nt++) {
	    x = sX[i+nt];
	    y = sY[i+nt];
	    scnt += pi2->is2i->ps[idx5(x,y)];
	  }
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[j-nt]; 
	    y = sY[j-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  }  
	  
	  sc[idx++] = mtx->vx[l][d-mid1-mid2] 
	    + pi2->v->t2i 
	    + scnt 
	    + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
	    + pi2->v->pp[idx5(sX[k],sY[k])][idx5(sX[l],sY[l])];
	  
	  } 
	} 
      }
  
  /* multiloops */
  for (mid = 0; mid < d-3; mid++) {
    sc[idx++] = pi2->v->t3 
      + mtx->wbx[j-d+1+mid][mid] 
      + mtx->wbx[j-1][d-mid-3];  
    
  }
  
  /* summation */
  mtx->vx[j][d] = DLog2Sum(sc, idx);

}

/* Function: FillVSS()
 * Date:     ER, Mon Jun 14 11:26:44 CDT 1999 [St. Louis]
 *
 * Purpose:  fill matrix V using the inside algorithm -- only structure, no sequence
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix vx is filled at position [j][d].
 *
 */
void
FillVSS(struct pi2model_s *pi2, int j, int d, struct rnamtx_s *mtx, double *vp, double *sc)
{
  int     idx = 0;
  int     mid;
  int     k,l;
  int     mid1,mid2;

  /* Main recursion
   */

  /* hairpin loop
   */
  if (d < MAXRNALOOP) 
    sc[idx++] = pi2->v->t1 + pi2->is1->tn[d];

  /* stem loops 
   */
  
  /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1)  
    sc[idx++] = mtx->vx[j-1][d-2] 
      + pi2->v->t2s;
  
  /* bulges L */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      
      /* this is the way to calculate IS2b using the pi2->is2->tn[d]
      */
      sc[idx++] = mtx->vx[j-1][d-mid-1] 
	+ pi2->v->t2b - 1.0
	+ pi2->is2b->tn[mid+1];
    }
  
  /* bulges R */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP) {
      
      /* this is the way to calculate IS2b using the pi2->is2->tn[d]
       */
      sc[idx++] = mtx->vx[j-mid][d-mid-1] 
	+ pi2->v->t2b - 1.0
	+ pi2->is2b->tn[mid+1];
    }

  /* internal loops */
  /* L^3 calculation of internal loops */
  fillvp_ss(pi2, j, d, vp, mtx->vx);

  if (d > 3) 
    for (mid = 0; mid <= (d-4); mid++) 
      if ((d-mid) < MAXRNALOOP) sc[idx++] = vp[mid] + pi2->is2i->tn[d-mid] - LOG2(d-mid-3); 
  
  /* multiloops */
  for (mid = 0; mid < d-3; mid++) 
    sc[idx++] = pi2->v->t3 
      + mtx->wbx[j-d+1+mid][mid] 
      + mtx->wbx[j-1][d-mid-3];       

   				/* summation */
  mtx->vx[j][d] = DLog2Sum(sc, idx);

}

/* Function: FillParseV()
 * Date:     ER, Fri Apr  5 18:07:24 CST 2002 [St. Louis]
 *
 * Purpose:  fill matrix V using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix vx is filled at position [j][d].
 *
 */
void
FillParseV(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, 
	   struct rnamtx_s *mtx, double *vp, double *sc)
{
  int     idx = 0;
  int     x,y;
  int     xi,xj,yi,yj;
  int     xk,xl,yk,yl;
  int     iabs, jabs, mid;
  int     i,k,l;
  int     mid1,mid2;
  int     nt;
  double  scnt;

  fillparsevp(sX, sY, ct, pi2, start, L, j, d, vp, mtx->vx);

  jabs = j + start;

  i    = j    -d ;
  iabs = jabs - d;

  xi = sX[iabs];

  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Initialize diagonal 
   */
  if (ct[iabs] != jabs) { mtx->vx[j][d] = -BIGFLOAT; return; }

  /* Main recursion
   */

  /* hairpin loop
   */
  /* this is the way to calculate IS1s using the pi2->is1->tn[d]
   */
  if (d < MAXRNALOOP &&
      is_single_stranded(ct, jabs-1, d-2)) {
    scnt = 0.; 
    for (nt = 1; nt < d; nt++) { 
      x = sX[iabs+nt]; 
      y = sY[iabs+nt]; 
      scnt += pi2->is1->ps[idx5(x,y)]; 
    } 
    sc[idx++] = scnt + pi2->v->t1 + pi2->is1->tn[d]; 
  } 

  /* this is the way to calculate IS1s using an OTH Model
  if (d < MAXRNALOOP &&
      is_single_stranded(ct, jabs-1, d-2)) 
    sc[idx++] = pi2->v->t1 
      + mtx->rnaj[j-1][d-2];
   */

  /* stem loops 
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
 /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1 &&
      ct[iabs+1] == jabs-1 && ct[jabs-1] == iabs+1)  
    sc[idx++] = mtx->vx[j-1][d-2] 
      + pi2->v->t2s 
      + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
  
  /* bulges L */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP   &&
	ct[jabs-1] == iabs+mid &&
	how_many_paired (ct, iabs-1+mid, mid-2) == 0) 
      {
	xk = sX[iabs+mid];
	yk = sY[iabs+mid];
	xl = sX[jabs-1];
	yl = sY[jabs-1];
	
	/* this is the way to calculate IS2b using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[iabs+nt];
	  y = sY[iabs+nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	sc[idx++] = mtx->vx[j-1][d-mid-1] 
	  + scnt 
	  + pi2->v->t2b - 1.0
	  + pi2->is2b->tn[mid+1]
	  + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
      }
  
  /* bulges R */
  for (mid = 2; mid < d; mid++)    
    if ((mid+1) < MAXRNALOOP &&
	ct[iabs+1] == jabs-mid &&
	how_many_paired (ct, jabs-1, mid-2) == 0) 
      {
	xk = sX[iabs+1];
	yk = sY[iabs+1];
	xl = sX[jabs-mid];
	yl = sY[jabs-mid];
	
	/* this is the way to calculate IS2b using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[jabs-nt];
	  y = sY[jabs-nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	
	sc[idx++] = mtx->vx[j-mid][d-mid-1] 
	  + scnt 
	  + pi2->v->t2b - 1.0
	  + pi2->is2b->tn[mid+1]
	  + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)];
      }
  
  /* internal loops */

#ifdef L4INSIDEIL
  /* L^4 calculation of internal loops */
  for (k = i+2; k < j-1; k++) 
    for (l = k; l < j-1; l++) { 
      
      mid1 = k - i;
      mid2 = j - l;
      
      
      if (ct[iabs+mid1] == jabs - mid2 &&
	  ct[jabs-mid2] == iabs + mid1 &&
	  is_single_stranded(ct, iabs+mid-1, mid1-2) &&
	  is_single_stranded(ct, jabs-1,     mid2-2) &&
	  (mid1+mid2) < MAXRNALOOP) 
	{
	  scnt = 0.;
	  for (nt = 1; nt < mid1; nt++) {
	    x = sX[i+nt];
	    y = sY[i+nt];
	    scnt += pi2->is2i->ps[idx5(x,y)];
	  }
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[j-nt]; 
	    y = sY[j-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  }  
	  
	  sc[idx++] = mtx->vx[l][d-mid1-mid2] 
	    + pi2->v->t2i 
	    + scnt 
	    + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
	    + pi2->v->pp[idx5(sX[k],sY[k])][idx5(sX[l],sY[l])]; 
	}
    } 
#else 
  /* L^3 calculation of internal loops */
  if (d > 3) 
    for (mid = 0; mid <= (d-4); mid++) 
      if((d-mid) < MAXRNALOOP) sc[idx++] = vp[mid] + pi2->is2i->tn[d-mid] - LOG2(d-mid-3); 
#endif
  
  /* multiloops */
  for (mid = 0; mid < d-3; mid++) 
    if (complete_pairs(ct, iabs+1+mid, mid)  &&
	complete_pairs(ct, jabs-1,     d-mid-3)) 
      sc[idx++] = pi2->v->t3 
	+ mtx->wbx[j-d+1+mid][mid] 
	+ mtx->wbx[j-1][d-mid-3];  

     				/* summation */
  mtx->vx[j][d] = DLog2Sum(sc, idx);
}

/* Function: FillVO()
 * Date:     ER, Thu Aug  9 14:05:42 CDT 2001  [St. Louis]
 *
 * Purpose:  fill matrix VO using the outside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix vox is filled at position [j][d]. Outside algorithm
 *
 */
void
FillVO(int *sX, int *sY, struct pi2model_s *pi2, double score, int start, int L, int j, int d, 
       double *vp, double **wox, double **wbox, double **vox, double **vx, double *sc, struct end_s *rnaends)
{
  int     idx = 0;
  int     off, len;
  int     iabs, jabs;
  int     x,y;
  int     xi,xj,yi,yj;
  int     i,k,l;
  int     ih,jh;
  int     dd;
  int     mid1, mid2;
  int     nt;
  double  scnt;

  len = abs(rnaends->rend[0] - rnaends->lend[0]) + 1;
  
  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;
 
  /* Initialize
   */
  /* do not need to initialize for d=len-1 */
  if (d < 1) { vox[j][d] = -BIGFLOAT; return; }

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */
  /* W -> aVb
   */
  sc[idx++] = wox[j][d]  + pi2->w->tv  + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)];
  
  /* WB -> aVb
   */
  sc[idx++] = wbox[j][d] + pi2->wb->tv + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)];

  /* IS2s
   */
  /* stem loops   */

  k = i - 1;
  l = j + 1;

  mid1 = i - k;
  mid2 = l - j;
  
  if (k >= 0 && l < len) {
    sc[idx++] = vox[l][l-k] + pi2->v->t2s 
      + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)];
  }
  
 /* bulges L */
  l = j + 1;

  for (k = 0; k < i-1; k++) {
      
    mid1 = i - k;
    mid2 = l - j;
    
    /* this is the way to calculate IS2b using the pi2->is2->tn[d]
     */
    if (l < len && (mid1+mid2) < MAXRNALOOP) {
      scnt = 0.;
      for (nt = 1; nt < mid1; nt++) {
	x = sX[iabs-nt];
	y = sY[iabs-nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
      if (k >= 0 && l < len) {
	sc[idx++] = vox[l][l-k] + pi2->v->t2b - 1.0 + scnt + pi2->is2b->tn[mid1+mid2] 
	  + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)];
      }
    }
  }
  
  /* bulges R */
  k = i - 1;
  
  for (l = len-1; l > j+1; l--) {
    
    mid1 = i - k;
    mid2 = l - j;
    /* this is the way to calculate IS2b using the pi2->is2->tn[d]
     */
    if ((mid1+mid2) < MAXRNALOOP) {
      scnt = 0.;
      for (nt = 1; nt < mid2; nt++) {
	x = sX[jabs+nt];
	y = sY[jabs+nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
      if (k >= 0 && l < len) {
	sc[idx++] = vox[l][l-k] + pi2->v->t2b - 1.0 + scnt + pi2->is2b->tn[mid1+mid2] 
	  + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)];
      }
    }
  }
  
  /* internal loops */

  /* the slow L^4 way */
#ifdef L4OUTSIDEI 
  for (k = 0; k < i-1; k++)  
    for (l = len-1; l > j+1; l--) { 
      
	mid1 = i - k;
	mid2 = l - j;
	
	if ((mid1+mid2) < MAXRNALOOP) {

	  scnt = 0.;  
	  for (nt = 1; nt < mid1; nt++) { 
	    x = sX[iabs-nt]; 
	    y = sY[iabs-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[jabs+nt]; 
	    y = sY[jabs+nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  sc[idx++] = vox[l][l-k] + pi2->v->t2i + scnt + 
	    pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
	    + pi2->v->pp[idx5(xi,yi)][idx5(xj,yj)]; 
	}  
    }  
#else 
  
  /* the smart L^3 way for internal loops */
  fillvpo(sX, sY, pi2, len, jabs, j, d, vp, vox); 
  for (dd = 4+d; dd < len; dd++) {
    if ((dd-d) < MAXRNALOOP && d < len-4) sc[idx++] = vp[dd] + pi2->is2i->tn[dd-d] - LOG2(dd-d-3); 
  }
#endif
  
  				/* summation */
  vox[j][d] = DLog2Sum(sc, idx);

if (vox[j][d]+vx[jh][d] > score+MARGIN) 
    Die("FillVO() error at j=%d jh=%d d=%d vox = %.4f vx = %.4f sum = %.4f (score = %.4f)\n", 
	j, jh, d, vox[j][d], vx[jh][d], vox[j][d]+vx[jh][d], score);
}

/* Function: FillW()
 * Date:     ER, Mon Jun 14 11:26:44 CDT 1999 [St. Louis]
 *
 * Purpose:  fill matrix W using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wx is filled at position [j][d].
 *
 */
void
FillW(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, 
      struct rnamtx_s *mtx, double *sc)
{
  int     idx = 0;
  int     xi,xj,yi,yj;
  int     iabs, jabs, mid;

  /* Initialize diagonal 
   */
 /* wx has to have structure inside
   */
  if (d == 0) { mtx->wx[j][d] = -BIGFLOAT; return; }

  jabs = j + start;
  iabs = jabs - d;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */
  
                                /* i left; connect to i+1, j; emit xi,yi  */
  sc[idx++] = mtx->wx[j][d-1]   + pi2->w->tl + pi2->w->pl[idx5(xi,yi)]; 

				/* j right; connect to i, j-1; emit xj,yj */
  sc[idx++] = mtx->wx[j-1][d-1] + pi2->w->tr + pi2->w->pr[idx5(xj,yj)]; 
  
				/* i,j pair; connect to i+1,j-1; emit xy   */
  sc[idx++] = mtx->vx[j][d] + pi2->w->tv + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)];

				/* bifurcations */
  for (mid = 0; mid < d; mid++) {
    sc[idx++] = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + pi2->w->tw;  
  }


				/* summation */
  mtx->wx[j][d] = DLog2Sum(sc, idx);

}

/* Function: FillWSS()
 * Date:     ER, Mon Mar 10 09:48:42 CST 2003 [St. Louis]
 *
 * Purpose:  fill matrix W using the inside algorithm -- no emission
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wx is filled at position [j][d].
 *
 */
void
FillWSS(struct pi2model_s *pi2, int j, int d, struct rnamtx_s *mtx, double *sc)
{
  int idx = 0;
  int mid;
 
  /* Initialize diagonal 
   */
 /* wx has to have structure inside
   */
  if (d == 0) { mtx->wx[j][d] = -BIGFLOAT; return; }

 /* Main recursion
   */
  
                                /* i left; connect to i+1, j;  */
  sc[idx++] = mtx->wx[j][d-1]   + pi2->w->tl; 

				/* j right; connect to i, j-1;  */
  sc[idx++] = mtx->wx[j-1][d-1] + pi2->w->tr; 
  
				/* i,j pair; connect to i+1,j-1;   */
  sc[idx++] = mtx->vx[j][d] + pi2->w->tv;

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    sc[idx++] = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + pi2->w->tw;  

				/* summation */
  mtx->wx[j][d] = DLog2Sum(sc, idx);
}

/* Function: FillParseW()
 * Date:     ER, Fri Apr  5 18:08:09 CST 2002 [St. Louis]
 *
 * Purpose:  fill matrix W using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wx is filled at position [j][d].
 *
 */
void
FillParseW(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, 
	   struct rnamtx_s *mtx, double *sc)
{
  int     idx = 0;
  int     xi,xj,yi,yj;
  int     iabs, jabs, mid;

  /* Initialize diagonal 
   */
 /* wx has to have structure inside
   */
  if (d == 0) { mtx->wx[j][d] = -BIGFLOAT; return; }

  jabs = j + start;
  iabs = jabs - d;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */
  
                                /* i left; connect to i+1, j; emit xi,yi  */
  if (ct[iabs] == iabs) 
    sc[idx++] = mtx->wx[j][d-1]   + pi2->w->tl + pi2->w->pl[idx5(xi,yi)]; 

				/* j right; connect to i, j-1; emit xj,yj */
  if (ct[jabs] == jabs) 
    sc[idx++] = mtx->wx[j-1][d-1] + pi2->w->tr + pi2->w->pr[idx5(xj,yj)]; 
  
				/* i,j pair; connect to i+1,j-1; emit xy   */
  if (ct[iabs] == jabs && ct[jabs] == iabs) 
    sc[idx++] = mtx->vx[j][d] + pi2->w->tv + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)];

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if (complete_pairs(ct, jabs,       mid) &&
	complete_pairs(ct, jabs-mid-1, d-mid-1)) 
      sc[idx++] = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + pi2->w->tw;  

				/* summation */
  mtx->wx[j][d] = DLog2Sum(sc, idx);
}

/* Function: FillWO()
 * Date:     ER, Thu Aug  9 11:13:50 CDT 2001   [St. Louis]
 *
 * Purpose:  fill matrix WO using the outside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wox is filled at position [j][d].
 *
 */
void
FillWO(int *sX, int *sY, struct pi2model_s *pi2, double score, int start, int L, int j, int d, double **wox,
       double **wx, double *sc, struct end_s *rnaends)
{
  int     idx = 0;
  int     len;
  int     off;
  int     iabs, jabs;
  int     i,k;
  int     ih,jh,kh;
  int     mid;

  /* Initialize  
   */
  if (d == 0) { wox[j][d] = -BIGFLOAT; return; }

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

  /* Initialize  
   */
  if (d == len-1) { wox[j][d] =  0.0;      return; }

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

                                /* i-1 left  with WO; connect to i, j; emit x(i-1) y(i-1) */
  if (i > 0)     sc[idx++] = wox[j][d+1]   + pi2->w->tl + pi2->w->pl[idx5(sX[iabs-1],sY[iabs-1])]; 

				/* j+1 right  with WO; connect to i, j; emit x(j+1) y(j+1) */
  if (j < len-1) sc[idx++] = wox[j+1][d+1] + pi2->w->tr + pi2->w->pr[idx5(sX[jabs+1],sY[jabs+1])];  
   
  			/* bifurcations W -> W W */
  
  for (k = 0; k < i; k++) {
    mid = i - 1 - k;
    sc[idx++] = wox[j][mid+d+1] + wx[ih-1][mid] + pi2->w->tw; 
  }
  
  for (k = len-1; k > j; k--) {
    kh = k + off;
    mid = k - j - 1;
    sc[idx++] = wox[k][mid+d+1] + wx[kh][mid] + pi2->w->tw;
  }
  
				/* summation */
  wox[j][d] = DLog2Sum(sc, idx);

  if (wox[j][d]+wx[jh][d] > score+MARGIN) {
    Die("FillWO() error at j=%d d=%d wox = %.4f wx = %.4f sum = %.4f (score = %.4f)\n", j, d, wox[j][d], wx[jh][d], wox[j][d]+wx[jh][d], score);
  }
}


/* Function: FillWB()
 * Date:     ER, Mon Jun 14 11:46:24  CDT 1999 [St. Louis]
 *
 * Purpose:  fill matrix WB using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][d]
 *
 */
void
FillWB(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, 
       struct rnamtx_s *mtx, double *sc)
{
  int     idx = 0;
  int     xi,xj,yi,yj;
  int     iabs, jabs, mid;

  /* wbx has to have structure inside
   */
  if (d == 0) { mtx->wbx[j][d] = -BIGFLOAT; return; }

  jabs = j + start;
  iabs = jabs - d;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* Main recursion
   */
   
                                /* i left; connect to i+1, j; emit xi,yi   */ 
  sc[idx++] = mtx->wbx[j][d-1] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)];

				/* j right; connect to i, j-1; emit xj,yj */
  sc[idx++] = mtx->wbx[j-1][d-1] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)];

				/* i,j pair; connect to i+1,j-1; emit xy  */ 
  sc[idx++] = mtx->vx[j][d] + pi2->wb->tv + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)];

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
     sc[idx++] = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1] + pi2->wb->tw;  

				/* summation */
  mtx->wbx[j][d] = DLog2Sum(sc, idx);

}

/* Function: FillWBSS()
 * Date:     ER, Mon Mar 10 09:48:42 CST 2003 [St. Louis]
 *
 * Purpose:  fill matrix WB using the inside algorithm -- no emission
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][d].
 *
 */
void
FillWBSS(struct pi2model_s *pi2, int j, int d, struct rnamtx_s *mtx, double *sc)
{
  int idx = 0;
  int mid;
 
  /* Initialize diagonal 
   */
  /* wbx has to have structure inside
   */
  if (d == 0) { mtx->wbx[j][d] = -BIGFLOAT; return; }
  
  /* Main recursion
   */
  
                                /* i left; connect to i+1, j;  */
  sc[idx++] = mtx->wbx[j][d-1]   + pi2->wb->tl; 

				/* j right; connect to i, j-1;  */
  sc[idx++] = mtx->wbx[j-1][d-1] + pi2->wb->tr; 
  
				/* i,j pair; connect to i+1,j-1;   */
  sc[idx++] = mtx->vx[j][d] + pi2->wb->tv;

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    sc[idx++] = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1] + pi2->wb->tw;  

				/* summation */
  mtx->wbx[j][d] = DLog2Sum(sc, idx);
}

/* Function: FillParseWB()
 * Date:     ER, Fri Apr  5 18:08:35 CST 2002 [St. Louis]
 *
 * Purpose:  fill matrix WB using the inside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbx is filled at position [j][d]
 *
 */
void
FillParseWB(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, 
	    struct rnamtx_s *mtx, double *sc)
{
  int     idx = 0;
  int     xi,xj,yi,yj;
  int     iabs, jabs, mid;

  jabs = j + start;
  iabs = jabs - d;

  xi = sX[iabs];
  yi = sY[iabs];
  xj = sX[jabs];
  yj = sY[jabs];
  
  /* wbx has to have structure inside
   */
  if (d == 0) { mtx->wbx[j][d] = -BIGFLOAT; return; }

  /* Main recursion
   */
   
                                /* i left; connect to i+1, j; emit xi,yi   */ 
  if (ct[iabs] == iabs)
    sc[idx++] = mtx->wbx[j][d-1] + pi2->wb->tl + pi2->wb->pl[idx5(xi,yi)]; 

				/* j right; connect to i, j-1; emit xj,yj */
  if (ct[jabs] == jabs)
    sc[idx++] = mtx->wbx[j-1][d-1] + pi2->wb->tr + pi2->wb->pr[idx5(xj,yj)];

				/* i,j pair; connect to i+1,j-1; emit xy  */ 
  if (ct[iabs] == jabs && ct[jabs] == iabs)
    sc[idx++] = mtx->vx[j][d] + pi2->wb->tv + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)];

				/* bifurcations */
  for (mid = 0; mid < d; mid++)
    if (complete_pairs(ct, jabs,       mid) &&
	complete_pairs(ct, jabs-mid-1, d-mid-1) )
    sc[idx++] = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1] + pi2->wb->tw;  

				/* summation */
  mtx->wbx[j][d] = DLog2Sum(sc, idx);
}

/* Function: FillWBO()
 * Date:     ER, Thu Aug  9 11:33:54 CDT 2001   [St. Louis]
 *
 * Purpose:  fill matrix WBO using the outside algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *           matrix wbox is filled at position [j][d].
 *
 */
void
FillWBO(int *sX, int *sY, struct pi2model_s *pi2, double score, int start, int L, int j, int d, double **wbox, 
	double **wbx, double **vox, double *sc, struct end_s *rnaends)
{
  int     idx = 0;
  int     len;
  int     off;
  int     iabs, jabs;
  int     i,k;
  int     ih,jh,kh;
  int     mid;

  /* Initialize  
   */
  if (d == 0) { wbox[j][d] = -BIGFLOAT; return; }

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

 /* Initialize  
   */
  if (d == len-1) { wbox[j][d] = -BIGFLOAT; return; }

  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;
 
  /* Main recursion
   */
  
                                /* i-1 left  with WO; connect to i, j; emit x(i-1) y(i-1) */ 
  if (d < j) 
    sc[idx++] = wbox[j][d+1]   + pi2->wb->tl + pi2->wb->pl[idx5(sX[iabs-1],sY[iabs-1])];   
 
				/* j+1 right  with WO; connect to i, j; emit x(j+1) y(j+1)  */ 
  if (j < len-1) 
    sc[idx++] = wbox[j+1][d+1] + pi2->wb->tr + pi2->wb->pr[idx5(sX[jabs+1],sY[jabs+1])];

				/* bifurcations Wb -> Wb Wb */
  
  for (k = 0; k < i; k++) {
    mid = i - 1 - k;
    sc[idx++] = wbox[j][mid+d+1] + wbx[ih-1][mid] + pi2->wb->tw; 
  }
  
  for (k = len-1; k > j; k--) {
    kh = k + off;
    mid = k - j - 1;
    sc[idx++] = wbox[k][mid+d+1] + wbx[kh][mid] + pi2->wb->tw; 
  } 

				/* bifurcations V -> Wb Wb  */
  
  for (k = 0; k < i-1; k++) {
    mid = i - 1 - k - 1;
    if (j < len-1) {
      sc[idx++] = vox[j+1][mid+d+3] + wbx[ih-1][mid] + pi2->v->t3; 
    }
  }
  
  for (k = len-1; k > j+1; k--) {
    kh = k + off;
    mid = k - 1 - j - 1;
    if (d < j) {
      sc[idx++] = vox[k][mid+d+3] + wbx[kh-1][mid] + pi2->v->t3; 
   } 
  }   
 
				/* summation */
  wbox[j][d] = DLog2Sum(sc, idx);

  if (wbox[j][d]+wbx[jh][d] > score+MARGIN) 
    Die("FillWBO() error at j=%d d=%d wbox = %.4f wbx = %.4f sum = %.4f (%.4f)\n", j, d, wbox[j][d], wbx[jh][d], wbox[j][d]+wbx[jh][d], score);
}


void
FreeRNAMtx(struct rnamtx_s *mtx)
{
  free(mtx->rnaj[0]);
  free(mtx->vx[0]);
  free(mtx->wx[0]);
  free(mtx->wbx[0]);

  free(mtx->rnaj);
  free(mtx->vx);
  free(mtx->wx);
  free(mtx->wbx);

  free(mtx);
}

void
FreeRNAMtxOutside(struct rnamtx_ou_s *mtx)
{
  free(mtx->rnaj[0]);
  free(mtx->vx[0]);
  free(mtx->wx[0]);
  free(mtx->wbx[0]);

  free(mtx->rnaj);
  free(mtx->vx);
  free(mtx->wx);
  free(mtx->wbx);

  free(mtx);
}

void
FreeScfgRNA(struct rnascfg_s *mx, int rnass)
{
  free(mx->sc);
  free(mx->vp);

  FreeRNAMtx(mx->in);
  FreeRNAMtx(mx->inrv);

  if (rnass) { FreeRNAMtxOutside(mx->ou); FreeScfgNRN(mx->nrn); }
  
  free(mx);
}

/* Function: InsideRNA()
 * Date:     ER, Mon Sep 27 15:14:45 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures.
 *
 */
int
InsideRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	  struct rnamodel_s *rna, struct nullmodel_s *null, struct othdpd_s *othdp, 
	  struct rnamtx_s *mtx, double *sc, double *vp, int do_nus, int fastintloop, int logodds)
{
  int      j, d;
  int      jabs;

  PatternVec(L, sc);
  PatternVec(L, vp);
  PatternRNAMtx(L, mtx);

  if (FALSE) checkloops(L, rna);
    
  /* mx->rnaj[j][d]
   *
   * "rnaj" scores nucleotides as emited by an OTH model.
   *
   * I use rnaj to score the nucleotides emited inside hairpin loops.
   * If I use the regular rna->pi2->is1->t[len] the RNA model scores too high
   * compared to the rest of the models.
   * While this is critical por IS1s, whether IS2s are scored
   * using one or the other method does not make much of a difference.
   * I use regular rna->pi2->is2->t[len] for IS2s.
   *
   * I use rna->pi2->Rloop  as the OTH model.
   *
   */
  /*
    ViterbiOTHDiagMatrix(ofp, mx->rnaj, sqinfoX, sX, sqinfoY, sY, start, L, pi2->Rloop, othdp, 
    ali, FALSE, FALSE);
    ForwardOTHDiagMatrix(ofp, mx->rnaj, sX, sY, start, L, pi2->Rloop, othdp, mx->sc);
  */
  
  for (j = 0; j < L; j++) 
   for (d = 0; d <= j; d++)
      {
	jabs = j + start;
	
	/* 
	 * mx->rnaj[j][d] = ForwardOTHDiag_L(ofp, sX, sY, iabs, d+1, rna->pi2->Rloop, othdp, mx->sc);
	 *
	 * This is the correct way to calculate rnaj, but it takes too long.
	 * What we  use instead assumes that there ara no bases emited by
	 * the flanking models. 
	 *
	 */
	if (j == 0 || d == 0) 
	  mtx->rnaj[j][d] = ScoreWithOTH(ofp, sX, sY, jabs, d+1, rna->pi2->Rloop, FALSE);
	else 
	  mtx->rnaj[j][d] = mtx->rnaj[j-1][d-1] + ScoreWithOTHMatrix(sX, sY, jabs, rna->pi2->Rloop);
      }
  
  
  if (TRUE) {
    for (j = 0; j < L; j++)
      for (d = 0; d <= j; d++)
	mtx->rnaj[j][d] += (d+1) * 2. * null->meta;
  }

  if (do_nus) 
    inside_nus(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna->nus, mtx, sc);
  else 
    inside_pi2(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna->pi2, othdp, mtx, sc, vp, fastintloop);
  /*inside_pi2_from_posteriors(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna->pi2, othdp, mtx, sc, vp);*/

  return 1;
}


/* Function: InsideRNASS()
 * Date:     ER, Mon Mar 10 10:02:14 CST 2003 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Inside algorithm - only structure, no sequence
 *
 */
void
InsideRNASS(FILE *ofp, int L, struct pi2model_s *pi2, struct rnascfg_s *mx)
{
  int j, d;
  
  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {	
	/* in the INSIDE algorithm V has to be filled FIRST */
	FillVSS (pi2, j, d, mx->in, mx->vp, mx->sc);
	FillWBSS(pi2, j, d, mx->in, mx->sc);
        FillWSS (pi2, j, d, mx->in, mx->sc);
      }
}

/* Function: InsideParseRNA()
 * Date:     ER, Fri Apr  5 18:02:57 CST 2002 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures.
 *
 */
int
InsideParseRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L,
	       struct rnamodel_s *rna, struct nullmodel_s *null, struct othdpd_s *othdp, 
	       struct rnamtx_s *mtx, double *sc, double *vp, int do_nus, int logodds)
{
  int j, d;
  int jabs;

  PatternVec(L, sc);
  PatternVec(L, vp);
  PatternRNAMtx(L, mtx);

  if (FALSE) checkloops(L, rna);
    
  /* mx->rnaj[j][d]
   *
   * "rnaj" scores nucleotides as emited by an OTH model.
   *
   * I use rnaj to score the nucleotides emited inside hairpin loops.
   * If I use the regular rna->pi2->is1->t[len] the RNA model scores too high
   * compared to the rest of the models.
   * While this is critical por IS1s, whether IS2s are scored
   * using one or the other method does not make much of a difference.
   * I use regular rna->pi2->is2->t[len] for IS2s.
   *
   * I use rna->ROB as the OTH model, it works better than rna->ROJ
   * which has been designed to make it easy to pass through it without emitting anything.
   */
  /*
    ViterbiOTHDiagMatrix(ofp, mx->rnaj, sqinfoX, sX, sqinfoY, sY, start, L, pi2->Rloop, othdp, 
    ali, FALSE, FALSE);
    ForwardOTHDiagMatrix(ofp, mx->rnaj, sX, sY, start, L, pi2->Rloop, othdp, mx->sc);
  */
  
  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {
	jabs = j + start;
	
	/* 
	 * mx->rnaj[j][d] = ForwardOTHDiag_L(ofp, sX, sY, iabs, d+1, rna->pi2->Rloop, othdp, mx->sc);
	 *
	 * This is the correct way to calculate rnaj, but it takes too long.
	 * What we  use instead assumes that there ara no bases emited by
	 * the flanking models. 
	 *
	 */
	if (j == 0 || d == 0) 
	  mtx->rnaj[j][d] = ScoreWithOTH(ofp, sX, sY, jabs, d+1, rna->pi2->Rloop, FALSE);
	else 
	  mtx->rnaj[j][d] = mtx->rnaj[j-1][d-1] + ScoreWithOTHMatrix(sX, sY, jabs, rna->pi2->Rloop);

      }
  
  if (logodds) {
    for (j = 0; j < L; j++)
      for (d = 0; d <= j; d++)
	mtx->rnaj[j][d] += (d+1) * 2. * null->meta;
  }
  
  inside_parse_ss(ofp, sqinfoX, sX, sqinfoY, sY, ct, start, L, rna->pi2, othdp, mtx, sc, vp);

  return 1;
}

/* Function: OutsideRNA()
 * Date:     ER, Thu Aug  9 10:55:12 CDT 2001    [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Outside algorithm
 *
 */
int
OutsideRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	   struct rnamodel_s *rna, struct rnamtx_s *in, struct rnamtx_ou_s *ou, double *vp, double *sc,
	   int do_nus, int logodds, struct end_s *rnaends)
{
  PatternRNAMtxOutside(L, ou);

  /*if (do_nus) 
    outside_nus(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna->nus, othdp, mx, ali, traceback);
    else */
  outside_pi2(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna->pi2, in, ou, vp, sc, rnaends);

  return 1;
}

void
PatternVec(int L, double *vec)
{
  int j;

  for (j = 0; j < L; j++) 
    vec[j] = -BIGFLOAT;
}

void
PatternMtx(int L, double **m)
{
  int j, d;

  for (j = 0; j < L; j++) 
    for (d = 0; d <= j; d++) 
      m[j][d] = -BIGFLOAT;
}

void
PatternRNAMtx(int L, struct rnamtx_s *mtx)
{
  int j, d;

  for (j = 0; j < L; j++) 
    for (d = 0; d <= j; d++) {
      mtx->rnaj[j][d] = -BIGFLOAT;
      mtx->vx[j][d]   = -BIGFLOAT;
      mtx->wx[j][d]   = -BIGFLOAT;
      mtx->wbx[j][d]  = -BIGFLOAT;
    }
}
void
PatternRNAMtxOutside(int L, struct rnamtx_ou_s *mtx)
{
  int j, d;

  for (j = 0; j < L; j++) 
    for (d = 0; d <= j; d++) {
      mtx->rnaj[j][d] = -BIGFLOAT;
      mtx->vx[j][d]   = -BIGFLOAT;
      mtx->wx[j][d]   = -BIGFLOAT;
      mtx->wbx[j][d]  = -BIGFLOAT;
    }
}

void
PatternScfgRNA(int L, struct rnascfg_s *mx, int rnass, int rvrna)
{
  PatternVec(L, mx->sc);
  PatternVec(L, mx->vp);

  PatternRNAMtx(L, mx->in);

  if (rnass) PatternRNAMtxOutside(L, mx->ou); 
  if (rvrna) PatternRNAMtx(L, mx->inrv); 
}

/* Function: PosteriorRNA()
 * Date:     ER, Mon Aug 27 14:17:41 CDT 2001      [St. Louis]
 *
 * Purpose:  Takes the posterior probabilities P(i,j) an uses Robin's
 *           NR nussinov grammar to calculate the max number of pairs.
 *          
 *
 */
void
PosteriorRNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, char *gss, int start, int L,
	     struct rnamodel_s *rna, struct rnascfg_s *mx, 
	     int do_nus, int logodds, int traceback, struct end_s *rnaends, int isrnarev)
{
  struct rnamtx_s    *inside;
  struct nrn_s       *nrn;
  struct tracekn_s   *tr;                  /* traceback of a predicted RNA structure        */
  double            **pp;                  /* posterior pair probabilities                  */
  double             *ps;                  /* posterior single probabilities                */
  char               *cc;                  /* secondary structure with integers             */
  char               *ss;                  /* secondary structure with > <                  */
  int                *reqX, *reqY;         /* reverse complements                           */
  int                 off, len;

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

  /* reverse sequence */
  if (isrnarev) {
    reqX = (int *) MallocOrDie(sizeof(int) * L);
    reqY = (int *) MallocOrDie(sizeof(int) * L);
    RevComp(reqX, sX+start, L);
    RevComp(reqY, sY+start, L);

    if (sqinfoX.flags & SQINFO_SS) gss = RevSS(gss+start, L);

    sX = reqX;
    sY = reqY;
    start = 0;

    inside = mx->inrv;
  }
  else 
    inside = mx->in;

  off = rnaends->lend[0] - start;
 
  /* Outside algorithm [fills mx->ou->wx, mx->ou->wbx, mx->ou->vx]
   */
  OutsideRNA(ofp, sqinfoX, sX, sqinfoY, sY, start, L, rna, inside, mx->ou, mx->vp, mx->sc, do_nus, logodds, rnaends);  
 
  /* Using Outside and Inside, calculate the posteriors:  pp[i,j]  and  ps[i].
   */
  CalPairs(sX, sY, start, L, rna->pi2, inside, mx->ou, &pp, &ps, rnaends);
   
  /*  Use robin's NR nussinov grammar to give the best parse that uses
   *  as large a posterior score as possible -- CYK algorithm + traceback.
   */
  /*ConstructNRNModel(len, &nrn, pp, ps);*/
  ConstructNRNModel2(len, &nrn, pp, ps);
  if (FALSE) PrintNRNModel(ofp, nrn, len);
  Cyk_NRN(ofp, sqinfoX, sX, sqinfoY, sY, start, L, nrn, mx->nrn, rnaends);
  Trk_NRN(ofp, sqinfoX, sX, sqinfoY, sY, start, L, len-1, len-1, nrn, mx->nrn, &tr, traceback, rnaends);
 
  /*
   * print output -- alignment + structure.
   */ 
  Tracenrn(tr, L, off, FALSE, &ss, &cc);
  PrintSSseqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, ss, cc);
 
  /* If this was a kSquid or Selex file with a "given" secondary structure,
   * compare the two structures
   */
  if ((sqinfoX.flags & SQINFO_SS)) {
    PrintCtSeqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, gss);
    CompareRNAStructures(ofp, start, L, gss, ss);
  }

  if (isrnarev) { free(reqX); free(reqY); }
  free(cc);                                                                                
  free(ss);                                                                                                           
  free(pp[0]);   
  free(pp);    
  free(ps);  
  FreeTracekn(tr);  
  FreeNRNModel(nrn); 

}

/* Function: SCFGTrnasFormPosteriors()
 * Date:     ER, Mon Mar 24 08:59:30 CST 2003 [St. Louis]
 *
 * Purpose:  
 *
 * Returns:  void. RNA pi2 structure of lenght-aware parameters is filled
 *
 */
void
SCFGTransFromPosteriors(FILE *ofp, int j, int d, struct pi2model_s *pi2, struct rnamtx_s *mtx, struct pi2model_s *pi2post)
{
  double *bulges;
  double *inloop;
  double *bif_V;
  double *bif_W;
  double *bif_WB;
  double  hairpinloop;
  double  stemloop;
  double  sum;
  int     mid, mid1, mid2;
    
  bulges = (double *) MallocOrDie(sizeof(double) * (2 * d));
  inloop = (double *) MallocOrDie(sizeof(double) * (d * d));
  bif_V  = (double *) MallocOrDie(sizeof(double) * (d));
  bif_W  = (double *) MallocOrDie(sizeof(double) * (d));
  bif_WB = (double *) MallocOrDie(sizeof(double) * (d));

  /* V state
   */
  hairpinloop =  (d < MAXRNALOOP) ? pi2->is1->tn[d] : -BIGFLOAT;

  stemloop = (d > 1)? mtx->vx[j-1][d-2] : -BIGFLOAT;

  for (mid1 = 0; mid1 < d; mid1++)
    if (mid1 > 1 && (mid1+1) < MAXRNALOOP) bulges[mid1] =  mtx->vx[j-1][d-mid1-1] + pi2->is2b->tn[mid1+1];
    else                                   bulges[mid1] = -BIGFLOAT;

  for (mid2 = 0; mid2 < d; mid2++) 
    if (mid2 > 1 && (mid2+1) < MAXRNALOOP) bulges[d+mid2] =  mtx->vx[j-mid2][d-mid2-1] + pi2->is2b->tn[mid2+1];
    else                                   bulges[d+mid2] = -BIGFLOAT;

  for (mid1 = 0; mid1 < d; mid1++)  
    for (mid2 = 0; mid2 < d; mid2++) 
      if (mid1 > 1 && mid2 > 1 && mid1+mid2 <= d && (mid1+mid2) < MAXRNALOOP) 
	inloop[mid1*d+mid2] =  mtx->vx[j-mid2][d-mid1-mid2] + pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3);
      else                                        
	inloop[mid1*d+mid2] = -BIGFLOAT;
  
  for (mid = 0; mid < d; mid++) 
    if (mid < d-3) bif_V[mid] = mtx->wbx[j-1][d-mid-3] + mtx->wbx[j-d+1+mid][mid];
    else           bif_V[mid] = -BIGFLOAT;

  pi2post->v->t1  = pi2->v->t1;
  pi2post->v->t2s = pi2->v->t2s;
  pi2post->v->t2b = pi2->v->t2b;
  pi2post->v->t2i = pi2->v->t2i;
  pi2post->v->t3  = pi2->v->t3;

  if (d > 0 && mtx->vx[j][d] > -BIGFLOAT/2.0) {
    pi2post->v->t1  += - mtx->vx[j][d] + hairpinloop;
    pi2post->v->t2s += - mtx->vx[j][d] + stemloop;
    pi2post->v->t2b += - mtx->vx[j][d] + DLog2Sum(bulges, 2*d) - 1.0;
    pi2post->v->t2i += - mtx->vx[j][d] + DLog2Sum(inloop, d*d);
    pi2post->v->t3  += - mtx->vx[j][d] + DLog2Sum(bif_V, d);
  }
  
  /*check they are probabilities */
  if (FALSE) printf("V Trans (%d %d): IS1: %f IS2S: %f ISB: %f IS2I: %f WbWb: %f\n", j, d,
		   EXP2(pi2post->v->t1), EXP2(pi2post->v->t2s), EXP2(pi2post->v->t2b), EXP2(pi2post->v->t2i), EXP2(pi2post->v->t3));
  sum = EXP2(pi2post->v->t1) + EXP2(pi2post->v->t2s) + EXP2(pi2post->v->t2b) + EXP2(pi2post->v->t2i) + EXP2(pi2post->v->t3);
  if (sum > 2.0-accuracy || sum < accuracy)
    Die ("SCFGTransFormPosteriors(): pi2->v transtitions don't add up to one (sum_v = %f)\n", sum);
  
  
  
  /* W state
   */
  for (mid = 0; mid < d; mid++)
    bif_W[mid] = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1];

  
  pi2post->w->tl = pi2->w->tl;
  pi2post->w->tr = pi2->w->tr;
  pi2post->w->tv = pi2->w->tv;
  pi2post->w->tw = pi2->w->tw;

  if (d > 1 && mtx->wx[j][d] > -BIGFLOAT/2.0) {

    pi2post->w->tl += - mtx->wx[j][d] + mtx->wx[j][d-1];
    pi2post->w->tr += - mtx->wx[j][d] + mtx->wx[j-1][d-1];
    pi2post->w->tv += - mtx->wx[j][d] + mtx->vx[j][d];
    pi2post->w->tw += - mtx->wx[j][d] + DLog2Sum(bif_W, d);
  }
  
  /*check they are probabilities */
  if (FALSE) printf("W Trans (%d %d): L: %f R: %f P: %f Bif: %f\n", j, d,
		    EXP2(pi2post->w->tl), EXP2(pi2post->w->tr), EXP2(pi2post->w->tv), EXP2(pi2post->w->tw));
  sum = EXP2(pi2post->w->tl) + EXP2(pi2post->w->tr) + EXP2(pi2post->w->tv) + EXP2(pi2post->w->tw);
  if (sum > 2.0-accuracy || sum < accuracy)
    Die ("SCFGTransFormPosteriors(): pi2->w transtitions don't add up to one (sum_w = %f)\n", sum);

  
  /* WB state
   */
  for (mid = 0; mid < d; mid++)
    bif_WB[mid] = mtx->wbx[j][mid] + mtx->wbx[j-mid-1][d-mid-1];

  pi2post->wb->tl = pi2->wb->tl;
  pi2post->wb->tr = pi2->wb->tr;
  pi2post->wb->tv = pi2->wb->tv;
  pi2post->wb->tw = pi2->wb->tw;
  
  if (d > 1 && mtx->wbx[j][d] > -BIGFLOAT/2.0) {
    pi2post->wb->tl += - mtx->wbx[j][d] + mtx->wbx[j][d-1];
    pi2post->wb->tr += - mtx->wbx[j][d] + mtx->wbx[j-1][d-1];
  
    pi2post->wb->tv += - mtx->wbx[j][d] + mtx->vx[j][d];
    pi2post->wb->tw += - mtx->wbx[j][d] + DLog2Sum(bif_WB, d);
  }

  /*check they are probabilities */
  if (FALSE) printf("WB Trans (%d %d): L: %f R: %f P: %f Bif: %f\n", j, d,
		    EXP2(pi2post->wb->tl), EXP2(pi2post->wb->tr), EXP2(pi2post->wb->tv), EXP2(pi2post->wb->tw));
  sum = EXP2(pi2post->wb->tl) + EXP2(pi2post->wb->tr) + EXP2(pi2post->wb->tv) + EXP2(pi2post->wb->tw);
  if (sum > 2.0-accuracy || sum < accuracy)
    Die ("SCFGTransFormPosteriors(): pi2->wb transtitions don't add up to one (sum_wb = %f)\n", sum);


  free(bulges);
  free(inloop);
  free(bif_V);
  free(bif_W);
  free(bif_WB);
}


/* Function: Tr_V()
 * Date:     ER, Mon Oct  8 11:05:34 CDT 2001 [STL]
 *
 * Purpose:  traceback V from RNA grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_V(FILE *ofp, int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, 
     struct rnamtx_s *mtx, double *vp,
     int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  int    off;
  int    i;
  int    mid;
  int    ih, jh;
  int    nt;
  int    x,y;
  int    mid1, mid2;
  int    iabs, jabs;
  int    xi, yi, xj, yj;
  int    xk, yk, xl, yl;
  double scnt;

  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];
    
  if ((float)mtx->vx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* V -> a1 .. an  
   *
   * Hairpin loops
   */
  scnt = 0.;
  for (nt = 1; nt < d; nt++) {
    x = sX[jabs-nt];
    y = sY[jabs-nt];
    scnt += pi2->is1->ps[idx5(x,y)];
  }
  
  if ((float)mtx->vx[jh][d] == (float)(scnt + pi2->v->t1 + pi2->is1->tn[d]))
    {
      if (traceback) {
	fprintf(ofp," V -> a1 .. an  (n=%d)\n", d-1);
      }
      
      curr_tr->type = dpcS1;
      *flag = TRUE;
      return; 
    }
  
  /* V -> a1 a2 V a3 a4
   *
   * Stem loops
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
    /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (d > 1 && 
      (float)mtx->vx[jh][d] == (float)(mtx->vx[jh-1][d-2] + pi2->v->t2s + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
    {
      
     if (traceback) {
	fprintf(ofp," V -> a1*a2 V a3*a4  X=(%d %d) Y=(%d %d) -- X=(%d %d) Y=(%d %d) %f\n", 
		xi, xj, yi, yj, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih+1, jh-1, mtx->vx[jh-1][d-2]);
      }
      
      curr_tr->type = dpcS2S;
      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; 
    }
  /* V -> a1 .. an-2 V an-1 an
   *
   * Bulge L
   */
  for (mid = 2; mid < d; mid++)   
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+mid];
      yk = sY[iabs+mid];
      xl = sX[jabs-1];
      yl = sY[jabs-1];
      
      /* this is the way to calculate IS2s using the pi2->is2->tn[d]
       */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[iabs+nt];
	y = sY[iabs+nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
    if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-1][d-mid-1] + scnt + pi2->v->t2b - 1.0 + pi2->is2b->tn[mid+1] +
					   pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	{
	  
	  
	  if (traceback) {
	    fprintf(ofp," V -> a1*a2 V a3 .. an  X=(%d %d) Y=(%d %d) -- (%d) --X=(%d %d) Y=(%d %d) %f\n",
		    xi, xj, yi, yj, mid, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	    fprintf(ofp," trace V [%d %d] %f\n", ih+mid, jh-1, mtx->vx[jh-1][d-mid-1]);
	  }
	  
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid, j-1,
						 i+mid+(int)((d-mid-1)/2),
						 i+mid+(int)((d-mid-1)/2)+1,
						 dpcP, dpcP));
	  curr_tr->type = dpcS2B;
	  *flag = TRUE;
	  return; 
	}
    }
  
  /* V -> a1 a2 V a3 .. an
   *
   * Bulge R
   */
  for (mid = 2; mid < d; mid++)   
    if ((mid+1) < MAXRNALOOP) {
      xk = sX[iabs+1];
      yk = sY[iabs+1];
      xl = sX[jabs-mid];
      yl = sY[jabs-mid];
      
      /* this is the way to calculate IS2s using the pi2->is2->tn[d]
       */
      scnt = 0.;
      for (nt = 1; nt < mid; nt++) {
	x = sX[jabs-nt];
	y = sY[jabs-nt];
	scnt += pi2->is2b->ps[idx5(x,y)];
      }
      
      if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-mid][d-mid-1] + scnt + pi2->v->t2b - 1.0 + pi2->is2b->tn[mid+1] +
					   pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	{
	  if (traceback) {
	    fprintf(ofp," V -> a1 .. an-2 V an-1*an X=(%d %d) Y=(%d %d) -- (%d) --X=(%d %d) Y=(%d %d) %f\n",
		    xi, xj, yi, yj, mid, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	    fprintf(ofp," trace V [%d %d] %f\n", ih+1, jh-mid, mtx->vx[jh-mid][d-mid-1]);
	  }
	  
	  PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-mid,
						 i+1+(int)((d-mid-1)/2),
						 i+1+(int)((d-mid-1)/2)+1,
						 dpcP, dpcP));
	  curr_tr->type = dpcS2B;
	  *flag = TRUE;
	  return; 
	}
    }
  
  /* V -> a1 .. ak V ak+1 .. an
   *
   * Internal loops
   */
 for (mid1 = 2; mid1 < d; mid1++)   
   for (mid2 = 2; mid2 < d-mid1; mid2++) { 
     
     if ((mid1+mid2+d) < MAXRNALOOP) { 
       
	xk = sX[iabs+mid1];
	yk = sY[iabs+mid1];
	xl = sX[jabs-mid2];
	yl = sY[jabs-mid2];
	
      	scnt = 0.; 
	for (nt = 1; nt < mid1; nt++) { 
	  x = sX[iabs+nt]; 
	  y = sY[iabs+nt]; 
	  scnt += pi2->is2i->ps[idx5(x,y)]; 
	} 
	for (nt = 1; nt < mid2; nt++) { 
	  x = sX[jabs-nt]; 
	  y = sY[jabs-nt]; 
	  scnt += pi2->is2i->ps[idx5(x,y)]; 
	} 
	
	if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-mid2][d-mid1-mid2] + pi2->v->t2i + scnt + 
					     pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
					     + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	  {
	    if (traceback) {
	      fprintf(ofp," V -> a1 .. an V an+1 .. am X=(%d %d) Y=(%d %d) -- (%d %d ) --X=(%d %d) Y=(%d %d) %f\n",
		    xi, xj, yi, yj, mid1, mid2, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	      fprintf(ofp," trace V [%d %d] %f\n", ih+mid1, jh-mid2, mtx->vx[jh-mid2][d-mid1-mid2]);
	    }
	    
	    PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid1, j-mid2,
						   i+mid1+(int)((d-mid1-mid2)/2),
						   i+mid1+(int)((d-mid1-mid2)/2)+1,
						   dpcP, dpcP));
	    curr_tr->type = dpcS2I;
	    *flag = TRUE;
	    return; 
	  }
	
      } 
    } 
   
   /* V -> a WB WB b
   */
  for (mid = 3; mid < d-3; mid++) 
    if ((float)mtx->vx[jh][d] == (float)(pi2->v->t3 + mtx->wbx[jh-d+1+mid][mid] + mtx->wbx[jh-1][d-mid-3]))
      {
	if (traceback) {
	  fprintf(ofp," V -> a WB WB b %f\n", pi2->v->t3);
	  fprintf(ofp," trace WB [%d %d] %f\n", ih+1, jh-d+1+mid, mtx->wbx[jh-d+1+mid][mid]);
	  fprintf(ofp," trace WB [%d %d] %f\n", ih+mid+2, jh-1, mtx->wbx[jh-1][d-mid-3]);
	}
	
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-d+1+mid,
					       i+1+(int)((mid)/2),
					       i+1+(int)((mid)/2)+1,
					       dpcR, dpcP));
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid+2, j-1,
					       i+mid+2+(int)((d-mid-3)/2),
					       i+mid+2+(int)((d-mid-3)/2)+1,
					       dpcR, dpcP));
	curr_tr->type = dpcMV;
	*flag = TRUE;
	return; 
      }
}

/* Function: Tr_Parse_V()
 * Date:     ER,  Fri Apr  5 15:21:28 CST 2002 [STL]
 *
 * Purpose:  traceback V from RNA grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_Parse_V(FILE *ofp, int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, 
	   struct rnamtx_s *mtx, double *vp,
	   int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  int    off;
  int    i;
  int    mid;
  int    ih, jh;
  int    nt;
  int    x,y;
  int    mid1, mid2;
  int    iabs, jabs;
  int    xi, yi, xj, yj;
  int    xk, yk, xl, yl;
  double scnt;

  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];
    
  if ((float)mtx->vx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* V -> a1 .. an  
   *
   * Hairpin loops
   */    
  scnt = 0.;
  for (nt = 1; nt < d; nt++) {
    x = sX[jabs-nt];
    y = sY[jabs-nt];
    scnt += pi2->is1->ps[idx5(x,y)];
  }
  
  if (is_single_stranded(ct, jabs-1, d-2) &&
      (float)mtx->vx[jh][d] == (float)(scnt + pi2->v->t1 + pi2->is1->tn[d]))
    {
      if (traceback) {
	fprintf(ofp," V -> a1 .. an  (n=%d)\n", d-1);
      }
      
      curr_tr->type = dpcS1;
      *flag = TRUE;
      return; 
    }

  /* V -> a1 a2 V a3 a4
   *
   * Stem loops
   */
  xk = sX[iabs+1];
  yk = sY[iabs+1];
  xl = sX[jabs-1];
  yl = sY[jabs-1];
  
    /* this is the way to calculate IS2s using the pi2->is2->tn[d]
   */
  if (ct[iabs+1] == jabs-1 && ct[jabs-1] == iabs+1 &&
      d > 1 && 
      (float)mtx->vx[jh][d] == (float)(mtx->vx[jh-1][d-2] + pi2->v->t2s + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
    {
      
     if (traceback) {
	fprintf(ofp," V -> a1 a2 V a3 a4  X=(%d %d) Y=(%d %d) -- X=(%d %d) Y=(%d %d) %f\n", 
		xi, xj, yi, yj, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih+1, jh-1, mtx->vx[jh-1][d-2]);
      }
      
      curr_tr->type = dpcS2S;
      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; 
    }
  /* V -> a1 .. an-2 V an-1 an
   *
   * Bulge L
   */
  for (mid = 2; mid < d; mid++)   
    if ((mid+1) < MAXRNALOOP   &&
	ct[jabs-1] == iabs+mid &&
	how_many_paired (ct, iabs+mid-1, mid-2) == 0) 
      {
	xk = sX[iabs+mid];
	yk = sY[iabs+mid];
	xl = sX[jabs-1];
	yl = sY[jabs-1];
	
	/* this is the way to calculate IS2s using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[iabs+nt];
	  y = sY[iabs+nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	
	if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-1][d-mid-1] + scnt + pi2->v->t2b -1.0 + pi2->is2b->tn[mid+1] +
					     pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	  {
	    
	    
	    if (traceback) {
	      fprintf(ofp," V -> a1 .. an-2 V an-1 an  X=(%d %d) Y=(%d %d) -- (%d) --X=(%d %d) Y=(%d %d) %f\n",
		      xi, xj, yi, yj, mid, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	      fprintf(ofp," trace V [%d %d] %f\n", ih+mid, jh-1, mtx->vx[jh-1][d-mid-1]);
	    }
	    
	    PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid, j-1,
						   i+mid+(int)((d-mid-1)/2),
						   i+mid+(int)((d-mid-1)/2)+1,
						   dpcP, dpcP));
	    curr_tr->type = dpcS2B;
	    *flag = TRUE;
	    return; 
	  }
      }
  
  /* V -> a1 a2 V a3 .. an
   *
   * Bulge R
   */
  for (mid = 2; mid < d; mid++)   
    if ((mid+1) < MAXRNALOOP   &&
	ct[iabs+1] == jabs-mid &&
	how_many_paired (ct, jabs-1, mid-2) == 0) 
      {
	xk = sX[iabs+1];
	yk = sY[iabs+1];
	xl = sX[jabs-mid];
	yl = sY[jabs-mid];
	
	/* this is the way to calculate IS2s using the pi2->is2->tn[d]
	 */
	scnt = 0.;
	for (nt = 1; nt < mid; nt++) {
	  x = sX[jabs-nt];
	  y = sY[jabs-nt];
	  scnt += pi2->is2b->ps[idx5(x,y)];
	}
	
	if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-mid][d-mid-1] + scnt + pi2->v->t2b - 1.0 + pi2->is2b->tn[mid+1] +
					     pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	  {
	    if (traceback) {
	      fprintf(ofp," V -> a1 .. an-2 V an-1 an X=(%d %d) Y=(%d %d) -- (%d) --X=(%d %d) Y=(%d %d) %f\n",
		      xi, xj, yi, yj, mid, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
	      fprintf(ofp," trace V [%d %d] %f\n", ih+1, jh-mid, mtx->vx[jh-mid][d-mid-1]);
	    }
	    
	    PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-mid,
						   i+1+(int)((d-mid-1)/2),
						   i+1+(int)((d-mid-1)/2)+1,
						   dpcP, dpcP));
	    curr_tr->type = dpcS2B;
	    *flag = TRUE;
	    return; 
	  }
      }
  
  /* V -> a1 .. ak V ak+1 .. an
   *
   * Internal loops
   */
  for (mid1 = 2; mid1 < d; mid1++)   
    for (mid2 = 2; mid2 < d-mid1; mid2++) { 
      
      if (ct[iabs+mid1] == jabs - mid2 &&
	  ct[jabs-mid2] == iabs + mid1 &&
	  is_single_stranded(ct, iabs+mid-1, mid1-2) &&
	  is_single_stranded(ct, jabs-1,     mid2-2) &&
	  (mid1+mid2+d) < MAXRNALOOP) 
	{ 
	  
	  xk = sX[iabs+mid1];
	  yk = sY[iabs+mid1];
	  xl = sX[jabs-mid2];
	  yl = sY[jabs-mid2];
	  
	  scnt = 0.; 
	  for (nt = 1; nt < mid1; nt++) { 
	    x = sX[iabs+nt]; 
	    y = sY[iabs+nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  for (nt = 1; nt < mid2; nt++) { 
	    x = sX[jabs-nt]; 
	    y = sY[jabs-nt]; 
	    scnt += pi2->is2i->ps[idx5(x,y)]; 
	  } 
	  
	  if ((float)mtx->vx[jh][d] == (float)(mtx->vx[jh-mid2][d-mid1-mid2] + pi2->v->t2i + scnt + 
					       pi2->is2i->tn[mid1+mid2] - LOG2(mid1+mid2-3)
					       + pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]))
	    {
	      if (traceback) {
		fprintf(ofp," V -> a1 .. an-2 V an-1 an X=(%d %d) Y=(%d %d) -- (%d %d ) --X=(%d %d) Y=(%d %d) %f\n",
			xi, xj, yi, yj, mid1, mid2, xk, xl, yk, yl, pi2->v->pp[idx5(xk,yk)][idx5(xl,yl)]);
		fprintf(ofp," trace V [%d %d] %f\n", ih+mid1, jh-mid2, mtx->vx[jh-mid2][d-mid1-mid2]);
	      }
	      
	      PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid1, j-mid2,
						     i+mid1+(int)((d-mid1-mid2)/2),
						     i+mid1+(int)((d-mid1-mid2)/2)+1,
						     dpcP, dpcP));
	      curr_tr->type = dpcS2I;
	      *flag = TRUE;
	      return; 
	    }
	  
	} 
    } 
  
  /* V -> a WB WB b
   */
  for (mid = 3; mid < d-3; mid++) 
    if (complete_pairs(ct, iabs+1+mid, mid)     &&	
	complete_pairs(ct, jabs-1,     d-mid-3) &&
	
	(float)mtx->vx[jh][d] == (float)(pi2->v->t3 + mtx->wbx[jh-d+1+mid][mid] + mtx->wbx[jh-1][d-mid-3]))
      {
	if (traceback) {
	  fprintf(ofp," V -> a WB WB b %f\n", pi2->v->t3);
	  fprintf(ofp," trace WB [%d %d] %f\n", ih+1, jh-d+1+mid, mtx->wbx[jh-d+1+mid][mid]);
	  fprintf(ofp," trace WB [%d %d] %f\n", ih+mid+2, jh-1, mtx->wbx[jh-1][d-mid-3]);
	}
	
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j-d+1+mid,
					       i+1+(int)((mid)/2),
					       i+1+(int)((mid)/2)+1,
					       dpcR, dpcP));
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i+mid+2, j-1,
					       i+mid+2+(int)((d-mid-3)/2),
					       i+mid+2+(int)((d-mid-3)/2)+1,
					       dpcR, dpcP));
	curr_tr->type = dpcMV;
	*flag = TRUE;
	return; 
      }
}

/* Function: Tr_W()
 * Date:     ER, Mon Oct  8 10:21:07 CDT 2001  [STL]
 *
 * Purpose:  traceback W from RNA grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_W(FILE *ofp, int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx, 
     int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  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];
    
  if ((float)mtx->wx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* W -> a V b
   */
  if ((float)mtx->wx[jh][d] == (float)(pi2->w->tv + mtx->vx[jh][d] + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)])) 
    {
      
      if (traceback) {
	fprintf(ofp," W -> a V b  X=(%d %d) Y=(%d %d) %f\n", xi, xj, yi, yj, pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih, jh, mtx->vx[jh][d]);
      }
      
      curr_tr->type = dpcP;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j,
					     i+(int)((d)/2),
					     i+(int)((d)/2)+1,
					     dpcP, dpcP));     
      *flag = TRUE;
      return; 
    }
  /* W -> a W
   */
  else if (d == 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tl + pi2->w->pl[idx5(xi,yi)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> a W  X=(%d) Y=(%d) %f\n", xi, yi, pi2->w->pl[idx5(xi,yi)]);
      }
      
      curr_tr->type = dpcL;

      *flag = TRUE;
      return; 
    }
  else if (d > 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tl + mtx->wx[jh][d-1] + pi2->w->pl[idx5(xi,yi)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> a W  X=(%d) Y=(%d) %f\n", xi, yi, pi2->w->pl[idx5(xi,yi)]);
	fprintf(ofp," trace W [%d %d] %f\n", ih+1, jh, mtx->wx[jh][d-1]);
      }
      
      curr_tr->type = dpcL;
      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; 
    }
  /* W -> W a
   */
  else if (d == 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tr + pi2->w->pr[idx5(xj,yj)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> W a  X=(%d) Y=(%d) %f\n", xj, yj, pi2->w->pr[idx5(xj,yj)]);
      }
      
      curr_tr->type = dpcR;

      *flag = TRUE;
      return; 
    }
  else if (d > 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tr + mtx->wx[jh-1][d-1] + pi2->w->pr[idx5(xj,yj)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> W a  X=(%d) Y=(%d) %f\n", xj, yj, pi2->w->pr[idx5(xj,yj)]);
	fprintf(ofp," trace W [%d %d] %f\n", ih, jh-1, mtx->wx[jh-1][d-1]);
      }
      
      curr_tr->type = dpcR;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
					     i+(int)((d-1)/2),
					     i+(int)((d-1)/2)+1,
					     dpcL, dpcP));
      *flag = TRUE;
      return; 
    }
  /* W -> W W
   */
  for (mid = 0; mid < d; mid++) 
    if ((float)mtx->wx[jh][d] == (float)(pi2->w->tw + mtx->wx[jh][mid] + mtx->wx[jh-mid-1][d-mid-1]))
      {
	if (traceback) {
	  fprintf(ofp," W -> W W\n");
	  fprintf(ofp," trace W [%d %d] %f\n", ih, jh-mid-1, mtx->wx[jh-mid-1][d-mid-1]);
	  fprintf(ofp," trace W [%d %d] %f\n", jh-mid, jh, mtx->wx[jh][mid]);
	}
	
	curr_tr->type = dpcBW;
	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,
					       dpcL, dpcP));
	*flag = TRUE;
	return; 
	
      }  
}

/* Function: Tr_Parse_W()
 * Date:     ER, Fri Apr  5 15:22:19 CST 2002   [STL] 
 * 
 * Purpose:  traceback W from RNA grammar using CYK algorithm 
 * 
 * Args:     L    -- 
 *           vx   --  
 *           wx   -- 
 *           wbx  -- 
 *           j,d   - coordinates of the matrix element  
 * 
 * Returns:  void 
 * 
 */ 
void
Tr_Parse_W(FILE *ofp, int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx, 
	   int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
  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];
    
  if ((float)mtx->wx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* W -> a V b
   */
  if (ct[iabs] == jabs && ct[jabs] == iabs &&
      (float)mtx->wx[jh][d] == (float)(pi2->w->tv + mtx->vx[jh][d] + pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)])) 
    {
      
      if (traceback) {
	fprintf(ofp," W -> a V b  X=(%d %d) Y=(%d %d) %f\n", xi, xj, yi, yj, pi2->w->pp[idx5(xi,yi)][idx5(xj,yj)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih, jh, mtx->vx[jh][d]);
      }
      
      curr_tr->type = dpcP;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j,
					     i+(int)((d)/2),
					     i+(int)((d)/2)+1,
					     dpcP, dpcP));     
      *flag = TRUE;
      return; 
    }
  /* W -> a W
   */
  else if (ct[iabs] == iabs &&
	   d > 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tl + mtx->wx[jh][d-1] + pi2->w->pl[idx5(xi,yi)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> a W  X=(%d) Y=(%d) %f\n", xi, yi, pi2->w->pl[idx5(xi,yi)]);
	fprintf(ofp," trace W [%d %d] %f\n", ih-1, jh, mtx->wx[jh][d-1]);
      }
      
      curr_tr->type = dpcL;
      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; 
    }
  /* W -> W a
   */
  else if (ct[jabs] == jabs &&
	   d > 0 && (float)mtx->wx[jh][d] == (float)(pi2->w->tr + mtx->wx[jh-1][d-1] + pi2->w->pr[idx5(xj,yj)])) 
    {
      if (traceback) {
	fprintf(ofp," W -> W a  X=(%d) Y=(%d) %f\n", xj, yj, pi2->w->pr[idx5(xj,yj)]);
	fprintf(ofp," trace W [%d %d] %f\n", ih, jh-1, mtx->wx[jh-1][d-1]);
      }
      
      curr_tr->type = dpcR;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
					     i+(int)((d-1)/2),
					     i+(int)((d-1)/2)+1,
					     dpcL, dpcP));
      *flag = TRUE;
      return; 
    }
  /* W -> W W
   */
  for (mid = 0; mid < d; mid++) 
    if (complete_pairs(ct, jabs,       mid)     &&
	complete_pairs(ct, jabs-mid-1, d-mid-1) &&
	(float)mtx->wx[jh][d] == (float)(pi2->w->tw + mtx->wx[jh][mid] + mtx->wx[jh-mid-1][d-mid-1]))
      {
	if (traceback) {
	  fprintf(ofp," W -> W W\n");
	  fprintf(ofp," trace W [%d %d] %f\n", ih, jh-mid-1, mtx->wx[jh-mid-1][d-mid-1]);
	  fprintf(ofp," trace W [%d %d] %f\n", jh-mid, jh, mtx->wx[jh][mid]);
	}
	
	curr_tr->type = dpcBW;
	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,
					       dpcL, dpcP));
	*flag = TRUE;
	return; 
	
      }  
}


/* Function: Tr_WB()
 * Date:     ER, Mon Oct  8 10:58:33 CDT 2001   [STL]
 *
 * Purpose:  traceback WB from RNA grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_WB(FILE *ofp, int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx, 
      int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
    {
      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];
    
  if ((float)mtx->wbx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* W -> a V b
   */
  if ((float)mtx->wbx[jh][d] == (float)(pi2->wb->tv + mtx->vx[jh][d] + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)])) 
    {
      
      if (traceback) {
	fprintf(ofp," WB -> a V b  X=(%d %d) Y=(%d %d) %f\n", xi, xj, yi, yj, pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih, jh, mtx->vx[jh][d]);
      }
      
      curr_tr->type = dpcP;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j,
					     i+(int)((d)/2),
					     i+(int)((d)/2)+1,
					     dpcP, dpcP));     
      *flag = TRUE;
      return; 
    }
  /* WB -> a WB
   */
  else if (d > 0 && (float)mtx->wbx[jh][d] == (float)(pi2->wb->tl + mtx->wbx[jh][d-1] + pi2->wb->pl[idx5(xi,yi)]))
    {
      if (traceback) {
	fprintf(ofp," WB -> a WB  X=(%d) Y=(%d) %f\n", xi, yi, pi2->wb->pl[idx5(xi,yi)]);
	fprintf(ofp," trace WB [%d %d] %f\n", ih+1, jh, mtx->wbx[jh][d-1]);
      }
      
      curr_tr->type = dpcL;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j,
					     i+1+(int)((d-1)/2),
					     i+1+(int)((d-1)/2)+1,
					     dpcR, dpcP));
      *flag = TRUE;
      return; 
    }
  /* WB -> WB a
   */
  else if (d > 0 && (float)mtx->wbx[jh][d] == (float)(pi2->wb->tr + mtx->wbx[jh-1][d-1] + pi2->wb->pr[idx5(xj,yj)])) 
    {
      if (traceback) {
	fprintf(ofp," WB -> WB a  X=(%d) Y=(%d) %f\n", xj, yj, pi2->wb->pr[idx5(xj,yj)]);
	fprintf(ofp," trace WB [%d %d] %f\n", ih, jh-1, mtx->wbx[jh-1][d-1]);
      }
      
      curr_tr->type = dpcR;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
					     i+(int)((d-1)/2),
					     i+(int)((d-1)/2)+1,
					     dpcR, dpcP));
      *flag = TRUE;
      return; 
    }
  /* WB -> WB WB
   */
  for (mid = 0; mid < d; mid++) 
    if ((float)mtx->wbx[jh][d] == (float)(pi2->wb->tw + mtx->wbx[jh][mid] + mtx->wbx[jh-mid-1][d-mid-1]))
      {
	if (traceback) {
	  fprintf(ofp," WB -> WB WB\n");
	  fprintf(ofp," trace WB [%d %d] %f\n", ih, jh-mid-1, mtx->wbx[jh-mid-1][d-mid-1]);
	  fprintf(ofp," trace WB [%d %d] %f\n", jh-mid, jh, mtx->wbx[jh][mid]);
	}
	
	curr_tr->type = dpcBW;
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-mid-1,
					       i+(int)((d-mid-1)/2),
					       i+(int)((d-mid-1)/2)+1,
					       dpcR, dpcP));
	PushTraceknstack(dolist, AttachTracekn(curr_tr, j-mid, j,
					       j-mid+(int)((mid)/2),
					       j-mid+(int)((mid)/2)+1,
					       dpcR, dpcP));
	*flag = TRUE;
	return; 
	
      }  
  }

/* Function: Tr_Parse_WB()
 * Date:     ER, Mon Oct  8 10:58:33 CDT 2001   [STL]
 *
 * Purpose:  traceback WB from RNA grammar using CYK algorithm
 *
 * Args:     L    --
 *           vx   --
 *           wx   --
 *           wbx  --
 *           j,d   - coordinates of the matrix element
 *
 * Returns:  void
 *
 */
void
Tr_Parse_WB(FILE *ofp, int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, struct rnamtx_s *mtx, 
	    int *flag, struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int traceback, struct end_s *rnaends)  
{
      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];
    
  if ((float)mtx->wbx[jh][d] == -BIGFLOAT) { *flag = TRUE; return; }

  /* W -> a V b
   */
  if (ct[iabs] == jabs && 
      ct[jabs] == iabs &&
      (float)mtx->wbx[jh][d] == (float)(pi2->wb->tv + mtx->vx[jh][d] + pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)])) 
    {
      
      if (traceback) {
	fprintf(ofp," WB -> a V b  X=(%d %d) Y=(%d %d) %f\n", xi, xj, yi, yj, pi2->wb->pp[idx5(xi,yi)][idx5(xj,yj)]);
	fprintf(ofp," trace V [%d %d] %f\n", ih, jh, mtx->vx[jh][d]);
      }
      
      curr_tr->type = dpcP;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j,
					     i+(int)((d)/2),
					     i+(int)((d)/2)+1,
					     dpcP, dpcP));     
      *flag = TRUE;
      return; 
    }
  /* WB -> a WB
   */
  else if (d> 0             && 
	   ct[iabs] == iabs &&
	   (float)mtx->wbx[jh][d] == (float)(pi2->wb->tl + mtx->wbx[jh][d-1] + pi2->wb->pl[idx5(xi,yi)]))
    {
      if (traceback) {
	fprintf(ofp," WB -> a WB  X=(%d) Y=(%d) %f\n", xi, yi, pi2->wb->pl[idx5(xi,yi)]);
	fprintf(ofp," trace WB [%d %d] %f\n", ih+1, jh, mtx->wbx[jh][d-1]);
      }
      
      curr_tr->type = dpcL;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j,
					     i+1+(int)((d-1)/2),
					     i+1+(int)((d-1)/2)+1,
					     dpcR, dpcP));
      *flag = TRUE;
      return; 
    }
  /* WB -> WB a
   */
  else if (d > 0            && 
	   ct[jabs] == jabs &&
	   (float)mtx->wbx[jh][d] == (float)(pi2->wb->tr + mtx->wbx[jh-1][d-1] + pi2->wb->pr[idx5(xj,yj)])) 
    {
      if (traceback) {
	fprintf(ofp," WB -> WB a  X=(%d) Y=(%d) %f\n", xj, yj, pi2->wb->pr[idx5(xj,yj)]);
	fprintf(ofp," trace WB [%d %d] %f\n", ih, jh-1, mtx->wbx[jh-1][d-1]);
      }
      
      curr_tr->type = dpcR;
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1,
					     i+(int)((d-1)/2),
					     i+(int)((d-1)/2)+1,
					     dpcR, dpcP));
      *flag = TRUE;
      return; 
    }
  /* WB -> WB WB
   */
  for (mid = 0; mid < d; mid++) 
    if (complete_pairs(ct, jabs-mid-1, d-mid-1) &&
	complete_pairs(ct, jabs,       mid)     &&
	(float)mtx->wbx[jh][d] == (float)(pi2->wb->tw + mtx->wbx[jh][mid] + mtx->wbx[jh-mid-1][d-mid-1]))
      {
	if (traceback) {
	  fprintf(ofp," WB -> WB WB\n");
	  fprintf(ofp," trace WB [%d %d] %f\n", ih, jh-mid-1, mtx->wbx[jh-mid-1][d-mid-1]);
	  fprintf(ofp," trace WB [%d %d] %f\n", jh-mid, jh, mtx->wbx[jh][mid]);
	}
	
	curr_tr->type = dpcBW;
	PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-mid-1,
					       i+(int)((d-mid-1)/2),
					       i+(int)((d-mid-1)/2)+1,
					       dpcR, dpcP));
	PushTraceknstack(dolist, AttachTracekn(curr_tr, j-mid, j,
					       j-mid+(int)((mid)/2),
					       j-mid+(int)((mid)/2)+1,
					       dpcR, dpcP));
	*flag = TRUE;
	return; 
	
      }  
  }

/* Function: Trk_RNA()
 * Date:     ER, Mon Oct  8 10:11:13 CDT 2001  [STL]
 *
 * Purpose:  traceback for RNA grammar
 *
 *           W  -> aW  | Wa  | aVb | W W
 *           WB -> aWB | WBa | aVb | WB WB
 *           V  -> a1...an | a1...ak V ak+1...an | a WB WB b
 *
 */
void 
Trk_RNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,  int j, int d,
	struct pi2model_s *pi2, struct rnamtx_s *mtx, double *vp,
	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, dpcL, dpcP); /* starts with state W */
  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%d %d  \n", ih, jh);
      
      /* Determine the matrix we have to trace down */
      /* this is an arbitrary association (tied to Tr_V, Tr_W, Tr_WB) to know the next state to go to */
      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;
      
      else  Die("At this stage you should not find any type other than dpcP(%d) dpcL(%d) dpcR(%d), but you have... %d!\n", 
		dpcP, dpcL, dpcR, curr_tr->type);

      switch (curr_tr->node){
	
	/*************************************
	 * TRACE V state
	 *************************************/
      case V:
        if (traceback) fprintf(ofp,"tracing V  %f   \n", mtx->vx[jh][d]);
	
        Tr_V(ofp, sX, sY, pi2, start, L, j, d, mtx, vp, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of V");
        break;         
	
	/*************************************
	 * TRACE W state
	 *************************************/
      case W:
        if (traceback) fprintf(ofp,"tracing W  %f   \n", mtx->wx[jh][d]);
	
        Tr_W(ofp, sX, sY, pi2, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of W");
        break;         
	
	
	/*************************************
	 * TRACE WB state
	 *************************************/
      case WB:
        if (traceback) fprintf(ofp,"tracing WB  %f  \n", mtx->wbx[jh][d]);
	
        Tr_WB(ofp, sX, sY, pi2, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of WB");
        break;         
	
      default:
        Die("invalid traceback matrix assignement");
      }
    } /* while something is in the trace stack */
  
  FreeTracekn(curr_tr);
  FreeTraceknstack(dolist);
  *ret_trace = tr; 
}

/* Function: Trk_RNA()
 * Date:     ER, Mon Oct  8 10:11:13 CDT 2001  [STL]
 *
 * Purpose:  traceback for RNA grammar
 *
 *           W  -> aW  | Wa  | aVb | W W
 *           WB -> aWB | WBa | aVb | WB WB
 *           V  -> a1...an | a1...ak V ak+1...an | a WB WB b
 *
 */
void 
Trk_RNA_FromPosteriors(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,  int j, int d,
		       struct pi2model_s *pi2, struct rnascfg_s *mx_ss, struct rnamtx_s *mtx, double *vp,
		       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      */
  struct pi2model_s     *pi2post;
  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;
  
  pi2post = CopyPI2Model(pi2);
 
  pi2->v->t1  = LOG2(SCFGparam.vt.v1)  + LOG2(1.0-SCFGparam.vt.v3);
  pi2->v->t2s = LOG2(SCFGparam.vt.v2s) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2b = LOG2(SCFGparam.vt.v2b) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2i = LOG2(SCFGparam.vt.v2i) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t3  = LOG2(SCFGparam.vt.v3);

  pi2->w->tl = LOG2(SCFGparam.wt.wl) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tr = LOG2(SCFGparam.wt.wr) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tv = LOG2(SCFGparam.wt.wv) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tw = LOG2(SCFGparam.wt.www);

  pi2->wb->tl = LOG2(SCFGparam.wbt.wl) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tr = LOG2(SCFGparam.wbt.wr) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tv = LOG2(SCFGparam.wbt.wv) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tw = LOG2(SCFGparam.wbt.www);

  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, dpcL, dpcP); /* starts with state W */
  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;

      SCFGTransFromPosteriors (ofp, j, d, pi2, mx_ss->in, pi2post);

      if (traceback) fprintf(ofp,"---------------------------------------------------\n%d %d  \n", ih, jh);
      
      /* Determine the matrix we have to trace down */
      /* this is an arbitrary association (tied to Tr_V, Tr_W, Tr_WB) to know the next state to go to */
      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;
      
      else  Die("At this stage you should not find any type other than dpcP(%d) dpcL(%d) dpcR(%d), but you have... %d!\n", 
		dpcP, dpcL, dpcR, curr_tr->type);

      switch (curr_tr->node){
	
	/*************************************
	 * TRACE V state
	 *************************************/
      case V:
        if (traceback) fprintf(ofp,"tracing V  %f   \n", mtx->vx[jh][d]);
	
        Tr_V(ofp, sX, sY, pi2post, start, L, j, d, mtx, vp, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of V");
        break;         
	
	/*************************************
	 * TRACE W state
	 *************************************/
      case W:
        if (traceback) fprintf(ofp,"tracing W  %f   \n", mtx->wx[jh][d]);
	
        Tr_W(ofp, sX, sY, pi2post, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of W");
        break;         
	
	
	/*************************************
	 * TRACE WB state
	 *************************************/
      case WB:
        if (traceback) fprintf(ofp,"tracing WB  %f  \n", mtx->wbx[jh][d]);
	
        Tr_WB(ofp, sX, sY, pi2post, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of WB");
        break;         
	
      default:
        Die("invalid traceback matrix assignement");
      }
    } /* while something is in the trace stack */
  
  FreePI2Model(pi2post);
  FreeTracekn(curr_tr);
  FreeTraceknstack(dolist);
  *ret_trace = tr; 
}

/* Function: Trk_RNA()
 * Date:     ER, Fri Apr  5 15:20:03 CST 2002 [STL]
 *
 * Purpose:  traceback for RNA grammar
 *
 *           W  -> aW  | Wa  | aVb | W W
 *           WB -> aWB | WBa | aVb | WB WB
 *           V  -> a1...an | a1...ak V ak+1...an | a WB WB b
 *
 */
void 
Trk_Parse_RNA(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L,  int j, int d,
	      struct pi2model_s *pi2, struct rnamtx_s *mtx, double *vp,
	      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, dpcL, dpcP); /* starts with state W */
  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%d %d  \n", ih, jh);
      
      /* Determine the matrix we have to trace down */
      /* this is an arbitrary association (tied to Tr_V, Tr_W, Tr_WB) to know the next state to go to */
      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;
      
      else  Die("At this stage you should not find any type other than dpcP(%d) dpcL(%d) dpcR(%d), but you have... %d!\n", 
		dpcP, dpcL, dpcR, curr_tr->type);

      switch (curr_tr->node){
	
	/*************************************
	 * TRACE V state
	 *************************************/
      case V:
        if (traceback) fprintf(ofp,"tracing V  %f   \n", mtx->vx[jh][d]);
	
        Tr_Parse_V(ofp, sX, sY, ct, pi2, start, L, j, d, mtx, vp, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of V");
        break;         
	
	/*************************************
	 * TRACE W state
	 *************************************/
      case W:
        if (traceback) fprintf(ofp,"tracing W  %f   \n", mtx->wx[jh][d]);
	
        Tr_Parse_W(ofp, sX, sY, ct, pi2, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of W");
        break;         
	
	
	/*************************************
	 * TRACE WB state
	 *************************************/
      case WB:
        if (traceback) fprintf(ofp,"tracing WB  %f  \n", mtx->wbx[jh][d]);
	
        Tr_Parse_WB(ofp, sX, sY, ct, pi2, start, L, j, d, mtx, flag, curr_tr, dolist, traceback, rnaends);
        if (*flag == FALSE)
          Die("something went wrong in the traceback of WB");
        break;         
	
      default:
        Die("invalid traceback matrix assignement");
      }
    } /* while something is in the trace stack */
  
  FreeTracekn(curr_tr);
  FreeTraceknstack(dolist);
  *ret_trace = tr; 
}



/* Function: bestvp() 
 * 
 * Date:     ER,  Sat Oct  6 12:29:03 CDT 2001  [STL]
 *
 * Purpose: calculate array vp for internal loops for the CYK algorithm
 *           
 * Arguments: sX    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            sY    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *             vx   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vp is filled.
 */       
static void 
bestvp(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx)
{
  int    jabs;
  int    mid;  
  int    nt;
  double scntc;
  double sc;
  
  jabs = start + j;

  /* initialize vp for mid = d-4 (first possible internal loop)
   */
  if (d < 4) 
    { 
      vp[d] = -BIGFLOAT; 
      return; 
    }
  else if (d > 3) 
    {
      vp[d-4] = vx[j-2][d-4] + pi2->v->t2i + 
	pi2->is2i->ps[idx5(sX[jabs-1],  sY[jabs-1])]   + 
	pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])] + 
	pi2->v   ->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-2],sY[jabs-2])];
    }
  
  for (mid = 0; mid < (d-4); mid++) {
    
    /* calculate the probability of the "new element" 
     *     sc+k 
     *           (sc = )
     *           (k  = pi2->is2->ps[idx5(sX[j-d+1],sY[j-d+1])])
     *
     * then pick the best:
     *
     *    vp = best[(vp+k) , (sc+k)] 
     *       = k + best(vp,sc)
     */
    scntc = 0.;
    for (nt = 1; nt < (d-mid-2); nt++)
      scntc += pi2->is2i->ps[idx5(sX[jabs-nt],sY[jabs-nt])];
    
    if ((sc = vx[j-d+mid+2][mid] + scntc + pi2->v->t2i + 
	 pi2->v->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-d+mid+2],sY[jabs-d+mid+2])]) > vp[mid]) vp[mid] = sc;
    
    vp[mid] +=  pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])];
  }
}


/* Function: bestparsevp() 
 * 
 * Date:     ER,  Wed Apr  3 21:43:26 CST 2002  [STL]
 *
 * Purpose: calculate array vp for internal loops for the CYK algorithm
 *           
 * Arguments: sX    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            sY    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *             vx   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vp is filled.
 */       
static void 
bestparsevp(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx)
{
  int    jabs;
  int    mid;  
  int    nt;
  double scntc;
  double sc;
  
  jabs = start + j;

  /* initialize vp for mid = d-4 (first possible internal loop)
   */
  if (d < 4) 
    { 
      vp[d] = -BIGFLOAT; 
      return; 
    }
  else if (d > 3) 
    {
      if (ct[jabs-1] == jabs-1   && ct[jabs-d+1] == jabs-d+1 &&
	  ct[jabs-2] == jabs-d+2 && ct[jabs-d+2] == jabs-2   &&
	  complete_pairs(ct, jabs-2, d-4)                      )
	vp[d-4] = vx[j-2][d-4] + pi2->v->t2i + 
	  pi2->is2i->ps[idx5(sX[jabs-1],  sY[jabs-1])]   + 
	  pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])] + 
	  pi2->v   ->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-2],sY[jabs-2])];
      else 
	vp[d-4] = -BIGFLOAT; 
    }
  
  for (mid = 0; mid < (d-4); mid++) {
    
    if (ct[jabs-d+1] == jabs-d+1) 
      {
	/* calculate the probability of the "new element" 
	 *     sc+k 
	 *           (sc = )
	 *           (k  = pi2->is2->ps[idx5(sX[j-d+1],sY[j-d+1])])
	 *
	 * then pick the best:
	 *
	 *    vp = best[(vp+k) , (sc+k)] 
	 *       = k + best(vp,sc)
	 */
	scntc = 0.;
	for (nt = 1; nt < (d-mid-2); nt++)
	  scntc += pi2->is2i->ps[idx5(sX[jabs-nt],sY[jabs-nt])];
	
	if (ct[jabs-d+2]     == jabs-d+mid+2 && 
	    ct[jabs-d+mid+2] == jabs-d+2     &&
	    (sc = vx[j-d+mid+2][mid] + scntc + pi2->v->t2i + 
	     pi2->v->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-d+mid+2],sY[jabs-d+mid+2])]) > vp[mid]) vp[mid] = sc;
	
	vp[mid] +=  pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])];
      }
  } 
}

/* Function: checkloops
 *
 * Date: ER, Wed Nov 10 19:15:21 CST 1999 [STL]
 *
 * Purpose:  to compare probabilies for IS1 and IS2.
 *           it has to be run without option -d to make sense.
 *
 *
 */
void
checkloops(int L, struct rnamodel_s *rna)
{
  int     j;
  double  tmm1, tl1;
  double  tmm2, tl2;
  double *sc_tmm1, *sc_tl1;
  double *sc_tmm2, *sc_tl2;
  
  sc_tmm1 = (double *) MallocOrDie(sizeof(double) * 2 * L);
  sc_tmm2 = (double *) MallocOrDie(sizeof(double) * 2 * L);
  sc_tl1   = (double *) MallocOrDie(sizeof(double) * 2 * L);
  sc_tl2   = (double *) MallocOrDie(sizeof(double) * 2 * L);
  
  for (j = 0; j < L; j++) {

    sc_tmm1[j] = ((j-1 > 0)? (j-1) * rna->pi2->Rloop->t[TMM] : -BIGFLOAT);
    sc_tmm2[j] = ((j-2 > 0)? (j-2) * rna->pi2->Rloop->t[TMM] : -BIGFLOAT);

    sc_tl1[j] = rna->pi2->is1->tn[j];
    sc_tl2[j] = rna->pi2->is2b->tn[j];

    tmm1 = DLog2Sum(sc_tmm1, j);
    tmm2 = DLog2Sum(sc_tmm2, j);

    tl1 = DLog2Sum(sc_tl1, j);
    tl2 = DLog2Sum(sc_tl2, j);

    printf("%d %f %f %f %f %f %f | %f %f %f %f %f %f \n", j, 
	   sc_tmm1[j], EXP2(sc_tmm1[j]), EXP2(tmm1),
	   sc_tl1[j],  EXP2(sc_tl1[j]),  EXP2(tl1), 
	   sc_tmm2[j], EXP2(sc_tmm2[j]), EXP2(tmm2),
	   sc_tl2[j],  EXP2(sc_tl2[j]),  EXP2(tl2));
  }

  free(sc_tmm1); 
  free(sc_tmm2); 
  free(sc_tl1);
  free(sc_tl2);
}

static int
complete_pairs(int *ct, int jabs, int d) 
{
  int n;
  int start;
  int cp = TRUE;
  
  if (is_single_stranded(ct, jabs, d)) { cp = FALSE; return cp; }  

  start = jabs - d;

  for (n = 0; n <= d; n++) { if (*(ct+jabs-n) != jabs-n && (*(ct+jabs-n) - start < 0 || *(ct+jabs-n) - start > d)) { cp = FALSE; return cp; } }

  return cp; 
}

/* Function: inside_nus()
 * Date:     ER, Fri Jan 14 14:22:17 CST 2000 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with NUS model.
 *           best structures. 
 *
 */
void
cyk_nus(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	  struct nusmodel_s *nus, struct rnamtx_s *mtx, int traceback)
{
  int     j, d, i, mid;
  int     jabs, iabs;
  int     xi, yi, xj, yj;
  double  bestsc, sc;

  for (j = 0; j < L; j++) 
   for (d = 0; d <= j; d++)
      {
	i = j - d;

	jabs = j + start;
	iabs = i + start;

	xi = sX[iabs];
	yi = sY[iabs];
	xj = sX[jabs];
	yj = sY[jabs];
	
	
	/* Initialize diagonal 
	 */
	if (d == 0)  {
	  mtx->wx[j][d] = nus->te + nus->tl + nus->p[idx5(xi,yi)]; 
	  continue;
	}
	else if (d <= 3)  {
	  mtx->wx[j][d] = nus->tl + nus->p[idx5(xi,yi)] + mtx->wx[j][d-1]; 
	  continue;
	}


	bestsc = -BIGFLOAT;
	/* Main recursion
	 */
	
                                /* i left; connect to i+1, j; emit xi,yi */
	if((sc =  mtx->wx[j][d-1] + nus->tl + nus->p[idx5(xi,yi)]) > bestsc) 
	  bestsc = sc;
	
				/* j right; connect to i, j-1; emit xj,yj */
	if((sc =  mtx->wx[j-1][d-1] + nus->tr + nus->p[idx5(xj,yj)]) > bestsc) 
	  bestsc = sc;
	
				/* i,j pair; connect to i+1,j-1; emit xy */
	if((sc =  mtx->wx[j-1][d-2] + nus->tv + nus->pp[idx5(xi,yi)][idx5(xj,yj)]) > bestsc) 
	  bestsc = sc;
	
				/* bifurcations */
	for (mid = 0; mid < d; mid++)
	  if((sc =  mtx->wx[j][mid] +  mtx->wx[j-mid-1][d-mid-1] + nus->tw) > bestsc) 
	    bestsc = sc;

				/* summation */
	 mtx->wx[j][d] = bestsc;
      }
}


/* Function: cyk_parse_ss()
 * Date:     ER,  Wed Apr  3 19:13:21 CST 2002 [STL]
 *
 * Purpose:  Score a gapped sequence alignment with RNA-RNA model.
 *           Calculates best parse for a given structure. Zuker-like algorithm
 *
 */
void
cyk_parse_ss(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L, 
	     struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp, int traceback)
{
  int j, d;

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {
	BestParseV (sX, sY, ct, pi2, start, L, j, d, mtx, vp);
	BestParseWB(sX, sY, ct, pi2, start, L, j, d, mtx);
	BestParseW (sX, sY, ct, pi2, start, L, j, d, mtx);
      }
}

/* Function: cyk_pi2()
 * Date:     ER,  Sat Jan  1 09:53:32 CST 2000 [Panticosa]
 *
 * Purpose:  Score a gapped sequence alignment with RNA-RNA model.
 *           Calculates best structure. Zuker-like algorithm
 *
 */
void
cyk_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L, 
	struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp, int traceback)
{
  int j, d;

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {
	BestV (sX, sY, pi2, start, L, j, d, mtx, vp, TRUE);
	BestWB(sX, sY, pi2, start, L, j, d, mtx);
	BestW (sX, sY, pi2, start, L, j, d, mtx);
      }
}


/* Function: cyk_pi2_from_posterioirs
 * Date:     ER,  Fri Mar 28 11:20:26 CST 2003 [STL]
 *
 * Purpose:  Score a gapped sequence alignment with RNA-RNA model.
 *           Calculates best structure. Zuker-like algorithm
 *
 */
void
cyk_pi2_from_posteriors(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L, 
			 struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *vp, int traceback)
{
  struct pi2model_s *pi2post;
  struct rnascfg_s   *mx_ss; 
  struct end_s       *rnaends;
  struct tracekn_s   *tr;
  char               *cc;
  char               *ss;
  int                 j, d;

  mx_ss   = AllocScfgRNA(L, TRUE, FALSE);
  pi2post = CopyPI2Model(pi2);
  rnaends = AllocEnd();
  
  rnaends->lend[0] = start;
  rnaends->rend[0] = start + L - 1;
  
  pi2->v->t1  = LOG2(SCFGparam.vt.v1)  + LOG2(1.0-SCFGparam.vt.v3);
  pi2->v->t2s = LOG2(SCFGparam.vt.v2s) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2b = LOG2(SCFGparam.vt.v2b) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2i = LOG2(SCFGparam.vt.v2i) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t3  = LOG2(SCFGparam.vt.v3);

  pi2->w->tl = LOG2(SCFGparam.wt.wl) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tr = LOG2(SCFGparam.wt.wr) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tv = LOG2(SCFGparam.wt.wv) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tw = LOG2(SCFGparam.wt.www);

  pi2->wb->tl = LOG2(SCFGparam.wbt.wl) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tr = LOG2(SCFGparam.wbt.wr) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tv = LOG2(SCFGparam.wbt.wv) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tw = LOG2(SCFGparam.wbt.www);

  InsideRNASS(ofp, L, pi2, mx_ss);

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {	

	SCFGTransFromPosteriors (ofp, j, d, pi2, mx_ss->in, pi2post);

	/* in the INSIDE algorithm V has to be filled FIRST */
	BestV (sX, sY, pi2post, start, L, j, d, mtx, vp, FALSE);
	BestWB(sX, sY, pi2post, start, L, j, d, mtx);
        BestW (sX, sY, pi2post, start, L, j, d, mtx);
      }
  
  
  if (traceback) {
    Trk_RNA_FromPosteriors(ofp, sqinfoX, sX, sqinfoY, sY, start, L, L-1, L-1, pi2, mx_ss, mtx, vp, &tr, traceback, rnaends);
    TraceRNA(tr, L, 0, FALSE, &ss, &cc);
    PrintSSseqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, ss, cc);
    
    /* If this was a kSquid or Selex file with a "given" secondary structure,
     * compare the two structures
     
    if (sqinfoX.flags & SQINFO_SS) {
      
      PrintCtSeqs(ofp, &sqinfoX, sX, &sqinfoY, sY, start, L, gss);
      CompareRNAStructures(ofp, start, L, gss, ss);
    }*/
  }

  /* normalize so teh sum of probs of all seq of length L is 1
   */
  mtx->wx[L-1][L-1] -=  mx_ss->in->wx[L-1][L-1] + (double)L*2.0 * LOG2(1.0-Nullparam.eta);
  
  free(ss);
  free(cc);
  FreeEnd(rnaends);
  FreeTracekn(tr);
  FreeScfgRNA(mx_ss, FALSE);
  FreePI2Model(pi2post);
}

/* Function: fillvp() 
 * 
 # ER, Fri Apr  5 18:52:26 CST 2002 [STL]
 *
 * Purpose: calculate array vp for internal loops for the Inside algorithm
 *           
 * Arguments: sX    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            sY    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *             vx   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vp is filled.
 */       
static void 
fillvp(int *sX, int *sY, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx)
{
  int    jabs;
  int    mid;  
  int    nt;
  double scntc;
  double sc;
  
  jabs = start + j;

  /* initialize vp for mid = d-4 (first possible internal loop)
   */
  if (d < 4) 
    { 
      vp[d] = -BIGFLOAT; 
      return; 
    }
  else if (d > 3) 
    {
      vp[d-4] = vx[j-2][d-4] + pi2->v->t2i + 
	pi2->is2i->ps[idx5(sX[jabs-1],  sY[jabs-1])]   + 
	pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])] + 
	pi2->v   ->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-2],sY[jabs-2])];
    }
  
  for (mid = 0; mid < (d-4); mid++) {
    
    /* calculate the probability of the "new element" 
     *     sc+k 
     *           (sc = )
     *           (k  = pi2->is2->ps[idx5(sX[j-d+1],sY[j-d+1])])
     *
     * then sum  them all up:
     *
     *    vp = log[exp(vp+k) + exp(sc+k)] 
     *
     *       = vp + k + log[1+exp(sc-vp)]
     */
    scntc = 0.;
    for (nt = 1; nt < (d-mid-2); nt++)
      scntc += pi2->is2i->ps[idx5(sX[jabs-nt],sY[jabs-nt])];
    
    sc = vx[j-d+mid+2][mid] + scntc + pi2->v->t2i + 
      pi2->v->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-d+mid+2],sY[jabs-d+mid+2])];

    if      (vp[mid]    < -50.0) vp[mid]  = sc;
    else if (sc-vp[mid] > -50.0) vp[mid] += LOG2(1.0 + EXP2(sc-vp[mid]));

    vp[mid] += pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])];

  }  /* mid loop */

}

/* Function: fillvp_ss() 
 * 
 # ER, Mon Mar 10 09:56:02 CST 2003 [STL]
 *
 * Purpose: calculate array vp for internal loops for the Inside algorithm -- only structure
 *           
 * Arguments: 
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *             vx   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vp is filled.
 */       
static void 
fillvp_ss(struct pi2model_s *pi2, int j, int d, double *vp, double **vx)
{
  int    mid;  
  double sc;
  
  /* initialize vp for mid = d-4 (first possible internal loop)
   */
  if (d < 4) 
    { 
      vp[d] = -BIGFLOAT; 
      return; 
    }
  else if (d > 3) 
    {
      vp[d-4] = vx[j-2][d-4] + pi2->v->t2i;
    }
  
  for (mid = 0; mid < (d-4); mid++) {
    
    /* calculate the probability of the "new element" 
     *     sc+k 
     *           (sc = )
     *           (k  = pi2->is2->ps[idx5(sX[j-d+1],sY[j-d+1])])
     *
     * then sum  them all up:
     *
     *    vp = log[exp(vp+k) + exp(sc+k)] 
     *
     *       = vp + k + log[1+exp(sc-vp)]
     */
    sc = vx[j-d+mid+2][mid] + pi2->v->t2i;

    if (vp[mid] < - 50.)  vp[mid] = -BIGFLOAT;
    else 
      {
	if (sc-vp[mid] > -50.)
	  {
	    vp[mid] += LOG2(1. + EXP2(sc-vp[mid]));
	  }
      }

  }  /* mid loop */
}

/* Function: fillparsevp() 
 * 
 # ER, Fri Apr  5 18:52:26 CST 2002[STL]
 *
 * Purpose: calculate array vp for internal loops for the Inside algorithm
 *           
 * Arguments: sX    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            sY    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *             vx   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vp is filled.
 */       
static void 
fillparsevp(int *sX, int *sY, int *ct, struct pi2model_s *pi2, int start, int L, int j, int d, double *vp, double **vx)
{
  int    jabs;
  int    mid;  
  int    nt;
  double scntc;
  double sc;
  
  jabs = start + j;

  /* initialize vp for mid = d-4 (first possible internal loop)
   */
  if (d < 4) 
    { 
      vp[d] = -BIGFLOAT; 
      return; 
    }
  else if (d > 3 &&
	   ct[jabs-1] == jabs-1   && ct[jabs-d+1] == jabs-d+1 &&
	   ct[jabs-2] == jabs-d+2 && ct[jabs-d+2] == jabs-2     ) 
    {
      vp[d-4] = vx[j-2][d-4] + pi2->v->t2i + 
	pi2->is2i->ps[idx5(sX[jabs-1],  sY[jabs-1])]   + 
	pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])] + 
	pi2->v   ->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-2],sY[jabs-2])];
    }
  
  for (mid = 0; mid < (d-4); mid++) {
    
    if (ct[jabs-d+1] == jabs-d+1 && vp[mid] > -BIGFLOAT) 
      {
	/* calculate the probability of the "new element" 
	 *     sc+k 
	 *           (sc = )
	 *           (k  = pi2->is2->ps[idx5(sX[j-d+1],sY[j-d+1])])
	 *
	 * then sum  them all up:
	 *
	 *    vp = log[exp(vp+k) + exp(sc+k)] 
	 *
	 *       = vp + k + log[1+exp(sc-vp)]
	 */
	scntc = 0.;
	for (nt = 1; nt < (d-mid-2); nt++)
	  scntc += pi2->is2i->ps[idx5(sX[jabs-nt],sY[jabs-nt])];
	
	sc = vx[j-d+mid+2][mid] + scntc + pi2->v->t2i + 
	  pi2->v->pp[idx5(sX[jabs-d+2],sY[jabs-d+2])][idx5(sX[jabs-d+mid+2],sY[jabs-d+mid+2])];

	if (ct[jabs-d+2]     == jabs-d+mid+2 && 
	    ct[jabs-d+mid+2] == jabs-d+2     &&
	    sc-vp[mid] > -50.)
	  {
	    vp[mid] += pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])] + LOG2(1. + EXP2(sc-vp[mid]));
	  }
	else 
	  {
	    vp[mid] +=  pi2->is2i->ps[idx5(sX[jabs-d+1],sY[jabs-d+1])];
	  }
      }
  } 
}

/* Function: fillvpo() 
 *
 * Date:     ER, Fri Aug 10 11:31:57 CDT 2001    [St. Louis]
 * 
 * Purpose: calculate array vp for internal loops in the Outside algorithm
 *           
 * Arguments: sX    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            sY    - sequenceX (0..len-1) 
 *                    representing array indices of symbols
 *            cfg   - context-free grammar state transitions, float log2 form
 *            ntc   - context-free grammar nucleotide composition, float log2 form
 *             vp   - DP matrix, already alloc'ed 
 *            vox   - DP matrix, already alloc'ed 
 *            j,d   - coordinates of the matrix element
 *
 *            
 * Return:    (void)           
 *            vpo is filled.
 */       
static void 
fillvpo(int *sX, int *sY, struct pi2model_s *pi2, int L, int jabs, int j, int d, double *vp, double **vox)
{
  int    dd;
  int    i, iabs;
  int    nt;
  double sc, scntc;
  double addpair, suspair;
   
  i    = j    - d;
  iabs = jabs - d;

  if (i < 2 || j > L-3) { for (dd = d+4; dd < L; dd++) vp[dd] = -BIGFLOAT; return; }

  /* i,j are paired
   */
  addpair = pi2->v->pp[idx5(sX[iabs],sY[iabs])][idx5(sX[jabs],sY[jabs])];

  /* initialize VP 
   */
  /* for i == 2, initialize from vp[d+4] to vp[L-1] */
  if (i == 2) {
    for (dd = d+4; dd < L; dd++) {
      scntc = 0.;
      for (nt = 1; nt < (dd-d-2); nt++)
	scntc += pi2->is2i->ps[idx5(sX[jabs+nt],sY[jabs+nt])];
      
      vp[dd] = vox[dd][dd] + pi2->v->t2i + addpair + scntc +
	pi2->is2i->ps[idx5(sX[iabs-1],sY[iabs-1])];
    }
  }
  /* for i > 2, initialize only vp[d+4] */
  else {
    vp[d+4] = vox[j+2][d+4] + pi2->v->t2i + 
      addpair                             +
      pi2->is2i->ps[idx5(sX[jabs+1],sY[jabs+1])] + 
      pi2->is2i->ps[idx5(sX[iabs-1],sY[iabs-1])];
  }
  
  
  /* For i > 2  modify the previous VP entries L-1 to d+5
   */
  if (i > 2) {
    suspair = pi2->v->pp[idx5(sX[iabs-1],sY[iabs-1])][idx5(sX[jabs],sY[jabs])];
    
    for (dd = d+5; dd < L; dd++) 
      {
	if (vp[dd] > -BIGFLOAT) 
	  {
	    /* there is a new element to add to VP[dd] */
	    if (j+dd-d-2 < L) {
	      scntc = 0.;
	      for (nt = 1; nt < (dd-d-2); nt++)
		scntc += pi2->is2i->ps[idx5(sX[jabs+nt],sY[jabs+nt])];
	      
	      sc = vox[j+dd-d-2][dd] + scntc + pi2->v->t2i;
	    }
	    else sc = -BIGFLOAT;
	    

	    /* and the old VP[dd] coming from (j,d+1) has to be modified */
	    vp[dd] -= suspair;
	    
	    if (vp[dd] < -50.)
	      vp[dd] = sc;
	    else if (sc-vp[dd] > -50.)
	      vp[dd] += LOG2(1. + EXP2(sc-vp[dd]));
	    
	    vp[dd] += addpair + pi2->is2i->ps[idx5(sX[iabs-1],sY[iabs-1])];
	  } 
      } 
  }
}

static int
how_many_paired(int *ct, int jabs, int d) 
{
  return d + 1 - how_many_unpaired(ct, jabs, d);
}

static int
how_many_unpaired(int *ct, int jabs, int d) 
{
  int n;
  int unpaired = 0;

  for (n = 0; n <= d; n++) if (*(ct+jabs-n) == jabs-n) unpaired ++;

  return unpaired;
}

/* Function: inside_nus()
 * Date:     ER, Fri Jan 21 10:44:24 CST 2000 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with NUS model.
 *           Sums over all possible structures. 
 *
 */
void
inside_nus(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	  struct nusmodel_s *nus, struct rnamtx_s *mtx, double *sc)
{
  int     j, d, i, mid;
  int     jabs, iabs;
  int     xi, yi, xj, yj;
  int     idx;

  for (j = 0; j < L; j++) 
    for (d = 0; d <= j; d++)
      {
	i = j - d;
	
	iabs = i + start;
	jabs = j + start;
	
	xi = sX[iabs];
	yi = sY[iabs];
	xj = sX[jabs];
	yj = sY[jabs];
	
	if (d == 0)  {
	  mtx->wx[j][d] = EXP2(nus->tl + nus->p[idx5(xi,yi)]) +
	                  EXP2(nus->tr + nus->p[idx5(xi,yi)]) +
	                  EXP2(nus->te); 

	  mtx->wx[j][d] = LOG2(mtx->wx[j][d]);
	  continue;
	}
	else if (d <= 3)  
	  {
	    mtx->wx[j][d] = EXP2(nus->tl + nus->p[idx5(xi,yi)] + mtx->wx[j][d-1]  ) + 
	                    EXP2(nus->tr + nus->p[idx5(xj,yj)] + mtx->wx[j-1][d-1]) ;
	    
	    mtx->wx[j][d] = LOG2(mtx->wx[j][d]);
	    continue;
	  }
	
	idx = 0.;
	/* Main recursion
	 */
                               /* i left; connect to i+1, j; emit xi,yi */
	sc[idx++] = mtx->wx[j][d-1]   + nus->tl + nus->p[idx5(xi,yi)];
	
				/* j right; connect to i, j-1; emit xj,yj */
	sc[idx++] = mtx->wx[j-1][d-1] + nus->tr + nus->p[idx5(xj,yj)];
	
				/* i,j pair; connect to i+1,j-1; emit xy */
	sc[idx++] = mtx->wx[j-1][d-2] + nus->tv + nus->pp[idx5(xi,yi)][idx5(xj,yj)];
	
				/* bifurcations */
	for (mid = 0; mid < d; mid++) 
	  sc[idx++] = mtx->wx[j][mid] + mtx->wx[j-mid-1][d-mid-1] + nus->tw;
	
				/* summation */
	mtx->wx[j][d] = DLog2Sum(sc, idx);
      }
}

/* Function: inside_pi1()
 * Date:     ER, Fri Jan 14 14:22:17 CST 2000 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with NUS model.
 *           Sums over all possible structures. Inside algorithm
 *
 */
void
inside_pi1(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	  struct nusmodel_s *nus, struct othdpd_s *othdp, struct rnascfg_s *mx)
{
  int     j, d, i, mid;
  int     jabs, iabs;
  int     xi, yi, xj, yj;
  int     idx;

  for (j = 0; j < L; j++) 
    for (d = 0; d <= j; d++)
      {
	i = j - d;
	
	iabs = i + start;
	jabs = j + start;
	
	xi = sX[iabs];
	yi = sY[iabs];
	xj = sX[jabs];
	yj = sY[jabs];
	
	
	if (d <  3)  {mx->in->wx[j][d] = -BIGFLOAT; continue;}
	if (d == 3) {
	  mx->in->wx[j][d] = nus->te + nus->pp[idx5(xi,yi)][idx5(xj,yj)] +
	    mx->in->rnaj[j-1][d-2]; 
	  continue;
	}

	idx = 0.;
	/* Main recursion
	 */
	                        /* end in a haripin loop */
	if (d < MAXRNALOOP) 
	  mx->sc[idx++] = nus->te + nus->pp[idx5(xi,yi)][idx5(xj,yj)] + mx->in->rnaj[j-1][d-2];
	
                               /* i left; connect to i+1, j; emit xi,yi */
	mx->sc[idx++] =  mx->in->wx[j][d-1] + nus->tl + nus->p[idx5(xi,yi)];
	
				/* j right; connect to i, j-1; emit xj,yj */
	mx->sc[idx++] =  mx->in->wx[j-1][d-1] + nus->tr + nus->p[idx5(xj,yj)];
	
				/* i,j pair; connect to i+1,j-1; emit xy */
	mx->sc[idx++] =  mx->in->wx[j-1][d-2] + nus->tv + nus->pp[idx5(xi,yi)][idx5(xj,yj)];
	
				/* bifurcations */
	for (mid = 0; mid < d; mid++) 
	  mx->sc[idx++] =  mx->in->wx[j][mid] +  mx->in->wx[j-mid-1][d-mid-1] + nus->tw;
	
				/* summation */
	mx->in->wx[j][d] = DLog2Sum(mx->sc, idx);
      }
}

/* Function: inside_pi2()
 * Date:     ER, Mon Sep 27 15:14:45 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Inside algorithm
 *
 */
void
inside_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	   struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, 
	   double *sc, double *vp, int fastintloop)
{
  int j, d;

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {	
	/* in the INSIDE algorithm V has to be filled FIRST */
	FillV (sX, sY, pi2, start, L, j, d, mtx, vp, sc, fastintloop);
	FillWB(sX, sY, pi2, start, L, j, d, mtx, sc);
        FillW (sX, sY, pi2, start, L, j, d, mtx, sc);
  	if (FALSE) {
	  printf("%d %d | %d %d %d %d | %f | %f %f %f\n", 
		 j, d, sX[j-d], sX[j], sY[j-d], sY[j], 
		 pi2->v->pp[idx5(sX[j-d],sY[j-d])][idx5(sX[j],sY[j])], 
		 mtx->vx[j][d], mtx->wx[j][d], mtx->wbx[j][d]);
	}
      }
}

/* Function: inside_pi2()
 * Date:     ER, Mon Sep 27 15:14:45 CDT 1999 [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Inside algorithm
 *
 */
void
inside_pi2_from_posteriors(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
			   struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, double *sc, double *vp)
{
  struct pi2model_s *pi2post;
  struct rnascfg_s   *mx_ss; 
  int j, d;

  mx_ss   = AllocScfgRNA(L, TRUE, FALSE);
  pi2post = CopyPI2Model(pi2);

  pi2->v->t1  = LOG2(SCFGparam.vt.v1)  + LOG2(1.0-SCFGparam.vt.v3);
  pi2->v->t2s = LOG2(SCFGparam.vt.v2s) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2b = LOG2(SCFGparam.vt.v2b) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t2i = LOG2(SCFGparam.vt.v2i) + LOG2(1.0-SCFGparam.vt.v3) + LOG2(1.0-SCFGparam.vt.v1);
  pi2->v->t3  = LOG2(SCFGparam.vt.v3);

  pi2->w->tl = LOG2(SCFGparam.wt.wl) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tr = LOG2(SCFGparam.wt.wr) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tv = LOG2(SCFGparam.wt.wv) + LOG2(1.0-SCFGparam.wt.www);
  pi2->w->tw = LOG2(SCFGparam.wt.www);

  pi2->wb->tl = LOG2(SCFGparam.wbt.wl) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tr = LOG2(SCFGparam.wbt.wr) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tv = LOG2(SCFGparam.wbt.wv) + LOG2(1.0-SCFGparam.wbt.www);
  pi2->wb->tw = LOG2(SCFGparam.wbt.www);

  InsideRNASS(ofp, L, pi2, mx_ss);

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {	

	SCFGTransFromPosteriors (ofp, j, d, pi2, mx_ss->in, pi2post);

	/* in the INSIDE algorithm V has to be filled FIRST */
	FillV (sX, sY, pi2post, start, L, j, d, mtx, vp, sc, FALSE);
	FillWB(sX, sY, pi2post, start, L, j, d, mtx, sc);
        FillW (sX, sY, pi2post, start, L, j, d, mtx, sc);
      }

  FreeScfgRNA(mx_ss, FALSE);
  FreePI2Model(pi2post);
}

/* Function: inside_parse_ss()
 * Date:     ER, Fri Apr  5 18:04:08 CST 2002  [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Inside algorithm
 *
 */
void
inside_parse_ss(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int *ct, int start, int L,
		struct pi2model_s *pi2, struct othdpd_s *othdp, struct rnamtx_s *mtx, 
		double *sc, double *vp)
{
  int j, d;

  for (j = 0; j < L; j++)
    for (d = 0; d <= j; d++)
      {	
	/* in the INSIDE algorithm V has to be filled FIRST */
	FillParseV (sX, sY, ct, pi2, start, L, j, d, mtx, vp, sc);
	FillParseWB(sX, sY, ct, pi2, start, L, j, d, mtx, sc);
        FillParseW (sX, sY, ct, pi2, start, L, j, d, mtx, sc);
      }
}

static int
is_single_stranded(int *ct, int jabs, int d) 
{

  int answer = 0;

  if (how_many_unpaired(ct, jabs, d) == d+1) answer = 1;

  return answer;
}

/* Function: outside_pi2()
 * Date:     ER, Thu Aug  9 11:02:02 CDT 2001  [St. Louis]
 *
 * Purpose:  Score a gapped sequence alignment with RNA model.
 *           Sums over all possible structures. Outside algorithm
 *
 */
void
outside_pi2(FILE *ofp, SQINFO sqinfoX, int *sX, SQINFO sqinfoY, int *sY, int start, int L,
	    struct pi2model_s *pi2, struct rnamtx_s *in, struct rnamtx_ou_s *ou, double *vp, double *sc,
	    struct end_s *rnaends)
{
  int    j, d;
  int    off, len;
  double score;
  
  /* actual length of the RNA structure
   */
  if (rnaends->rend[0] >= start + L) rnaends->rend[0] = start+L-1;

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

  if (rnaends->rend[0] > rnaends->lend[0]) off = rnaends->lend[0] - start;
  else                                     off = L - 1 - rnaends->lend[0];
  
  /* initialize
   */
  PatternVec(L, vp);

  score = in->wx[len-1+off][len-1];

  for (j = len-1; j >= 0; j--)
    for (d = j; d >= 0; d--)
      {
	/* in the OUTSIDE algorithm VO has to be filled LAST */
	FillWO (sX, sY, pi2, score, start, L, j, d, ou->wx,  in->wx,  sc, rnaends);
	FillWBO(sX, sY, pi2, score, start, L, j, d, ou->wbx, in->wbx, ou->vx,  sc, rnaends);
	FillVO (sX, sY, pi2, score, start, L, j, d, vp,      ou->wx,  ou->wbx, ou->vx, in->vx, sc, rnaends); 
     }
}


