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

/* cfgparse.c
 *
 * parse a training set to determine SCFG counts
 *
 * ER, Tue Jun 22 13:49:47 CDT 1999 [STL]
 *
 * includes the functions:
 *
 *  -- KHS2Trace(),  generates a traceback from a given structure,
 *
 *  -- TraceCount(), adds the state transitions of a traceback to
 *                   a current counts-form SCFG.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "funcs.h"
#include "globals.h"
#include "squid.h"
#include "structs.h"

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

static void parseV  (FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
		     struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug);
static void parseW  (FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
		     struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug);
static void parseWB (FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
		     struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug);
static void parseIS1(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
		     struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug);
static void parseIS2(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
		     struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug);

/* Function: GraphicTrace()
 * 
 * Purpose:  Semi-graphic tool. Print a traceback tree.
 */
void
GraphicTrace(FILE *fp, struct tracekn_s *tr, char *seq, int len)
{
  struct traceknstack_s *stack;
  struct tracekn_s    *curr_tr;
  int                    *iseq;
  int                      ctr;
  int   posi, posj, posk, posl;   /* positions of the transition                   */
  int       size, size1, size2;   /* size of haipins, bulges and internal loops    */

  stack = InitTraceknstack();
  PushTraceknstack(stack, tr->nxtl);
  IntizeSequence(seq, len, &iseq);


  fprintf(fp, "   curr_trans           transidx      \n");
  fprintf(fp, "  i   j   k  l          i  j  k  l              state          node    \n");

  while ((curr_tr = PopTraceknstack(stack)) != NULL)
    {
      /* Default values of positions to calculate the transition we are at.
       *
       *   this works except for: state V  with node dpcS1, dpcS2*, 
       *                     and  state VH with node dpcBUVH
       */
      posi = curr_tr->emiti;
      posj = curr_tr->emitj;
      posk = curr_tr->emitk;
      posl = curr_tr->emitl;
      size = -1;
      
      if (curr_tr->node == dpcS2S || curr_tr->node == dpcS2B || curr_tr->node == dpcS2I) 
	{
	  /* hack  */
	  posk = curr_tr->nxtr->emiti;
	  posl = curr_tr->nxtr->emitj;
	}
      else if (curr_tr->node == dpcS1)
	{
	  posk = posi + 1;
	  posl = posj - 1;
	}
      
      if (curr_tr->type == IS1) 
	size = posj - posi;
      else if (curr_tr->type == IS2B || curr_tr->type == IS2I) 
	{
	  size1 = posk - posi;
	  size2 = posj - posl;
	  size = size1 + size2;
	}

      /* now calculate the transition */
      ctr = Ttype(curr_tr->node, iseq[posi], iseq[posj], iseq[posk], iseq[posl], size);
      
      
      fprintf(fp, "%2d  %2d  %2d  %2d     %2d(%2d)  %2d(%2d)  %2d(%2d)  %2d(%2d) %2d   %8s  %8s %2d\n",
	      curr_tr->emiti, 
	      curr_tr->emitj,
	      curr_tr->emitk, 
	      curr_tr->emitl,
	      posi, iseq[posi],
	      posj, iseq[posj],
	      posk, iseq[posk],
	      posl, iseq[posl],
	      size,
	      stNAME[curr_tr->type],
	      dpcNAME(curr_tr->node),
	      ctr);
      
      if (curr_tr->nxtr != NULL) PushTraceknstack(stack, curr_tr->nxtr);
      if (curr_tr->nxtl != NULL) PushTraceknstack(stack, curr_tr->nxtl);
    }
  free(iseq);
  FreeTraceknstack(stack);
}

/* Function: KHS2Trace()
 * 
 * Purpose:  Create a trace structure from a KH structure
 *           string. This is very similar to tracebacks
 *           of a dynamic programming matrix.
 *           
 * Args:     seq    - sequence, 0..len-1
 *           ss     - structure string. May contain pseudoknots.
 *           len    - length of both seq and ss
 *           ret_tr - RETURN: traceback
 *           
 * Return:   1 on success, 0 on failure.
 *           Caller frees ret_tr with FreeTrace().
 */                              
int
KHS2Trace(FILE *outf, char *seq, char *ss, int len, struct tracekn_s **ret_tr,
	  int allow_pseudoknots, int debug)
{
  struct traceknstack_s *dolist;       /* pushdown stack of active tr nodes      */
  struct tracekn_s      *curr_tr;      /* ptr to node of tr we're working on     */
  struct tracekn_s      *tr;           /* pushdown stack of active tr nodes      */
  int *ct;
  int  i, j, k, l;
  int  d, d1, d2;
  
  /* get a Zuker "ct" style array of who's base paired to whom
   */ 
  if (! KHS2ct(ss, len, allow_pseudoknots, &ct)) 
    {Warn("Structure string is inconsistent"); return 0; }
  
  tr     = InitTracekn();
  dolist = InitTraceknstack();
  curr_tr = AttachTracekn(tr, 0, len-1, (int)((len-1)/2), 
			  (int)((len-1)/2)+1, W, dpcEW);         /* attach the root */
  PushTraceknstack(dolist, curr_tr);
  
  while ((curr_tr = PopTraceknstack(dolist)) != NULL){
    
    if (debug) {
      fprintf(outf,"---------------------------------------------------\n");
      fprintf(outf,"%d %d %d %d \n", 
	      curr_tr->emiti, curr_tr->emitj, curr_tr->emitk, curr_tr->emitl);
      fprintf(outf,"STATE: %s \n", stNAME[curr_tr->type]); 
    }
    
    i = curr_tr->emiti;
    j = curr_tr->emitj;
    k = curr_tr->emitk;
    l = curr_tr->emitl;
    
    d  = j - i;
    d1 = k - i;
    d2 = j - l;
    
    if (d < 0 || d1 < 0 || d2 < 0)
      Die("check your traceback assignments");
    
    if (d == 0) {curr_tr->node = dpcL; continue;}
    
    else if (d1 + d2 >= d-1){
      d1 = (int)(d/2);
      d2 = d - d1 - 1;
      
      k = i + d1;
      l = j - d2;
    }
    
    if (!allow_pseudoknots) {
      switch (curr_tr->type) {
	
      case V: 
	if (ct[i] != j || ct[j] != i)
	  Die("V is not correctly assigned");
	parseV(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;
	
      case W:       
	parseW(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;
	
      case WB: 
	parseWB(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;
	
      case IS1: 
	parseIS1(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;
	
      case IS2B: 
	parseIS2(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;

      case IS2I: 
	parseIS2(outf, ct, len, j, d, d1, d2, curr_tr, dolist, debug);
	break;
	
      default:
	Die("invalid traceback matrix assignement");
      }
    }
    
    else Die("pks not implemented yet in this version");
    
    
    /* dose of paranoia */
    if (!Connects(curr_tr->type,curr_tr->node))
      Die ("ilegal connection at %s -> %s", stNAME[curr_tr->type], dpcNAME(curr_tr->node));
    
  } /* while something is in the trace stack */
  
  if (debug)
    fprintf(outf,"end------------------------------------------------\n");
  
  FreeTracekn(curr_tr);
  FreeTraceknstack(dolist);
  *ret_tr = tr;
  return 1;
}

/* Function: TraceCount()
 * 
 * Purpose:  Add the state transitions of a traceback to
 *           a current counts-form SCFG.
 *           
 *           Currently, we ignore all state transitions
 *           involving degenerate positions, rather than
 *           splitting them probabilistically across
 *           the possible outcomes.
 *           
 * Args:     seq  - sequence corresponding to the trace
 *           len  - length of sequence
 *           wgt  - weight on the sequence (often just 1.0) 
 *           tr   - traceback to count in
 *           cfg  - model, counts form, to add counts to                 
 *           
 * Return:   void
 */
void
TraceCount(char *seq, int len, double wgt, struct tracekn_s *tr, double **cfg)
{
  struct traceknstack_s *dolist;   /* stack for traversal of traceback tree         */
  struct tracekn_s     *curr_tr;   /* current node in the tree                      */
  int                  cst, ctr;   /* current state and transtition                 */
  int                     *iseq;
  int    posi, posj, posk, posl;   /* positions of the transition                   */
  int        size, size1, size2;   /* size of hairpins, bulges and internal loops   */

  IntizeSequence(seq, len, &iseq);
  dolist = InitTraceknstack();
  PushTraceknstack(dolist, tr->nxtl);

  while ((curr_tr = PopTraceknstack(dolist)) != NULL) {

   /* Figure out what state we're in and which transition.
     */
    cst = curr_tr->type;
    
    /* Default values of positions to calculate the transition we are at.
     *
     *   this works except for: state V   with node dpcS2*, 
     *                     and  state VH  with node dpcBUVH
     */
    posi = curr_tr->emiti;
    posj = curr_tr->emitj;
    posk = curr_tr->emitk;
    posl = curr_tr->emitl;
    
    if (curr_tr->node == dpcS2S) 
      {
	/*hack,  Ttype() takes care of the indices*/
	posk = posi + 1;
	posl = posj - 1;
      }
    else if (curr_tr->node == dpcS2B || curr_tr->node == dpcS2I) 
      {
	/*hack,  Ttype() takes care of the indices*/
	posk = curr_tr->nxtr->emiti;
	posl = curr_tr->nxtr->emitj;
      }
    else if (curr_tr->node == dpcS1)
      {
	/*hack, Ttype() takes care of the indices */
	posk = posi + 1;
	posl = posj - 1;
       }
     
    /* determine the size of IS's */
    if (cst == IS1) 
      size = posj - posi;

    else if (cst == IS2B || cst == IS2I) 
      {
	size1 = posk - posi;
	size2 = posj - posl;
	size = size1 + size2;
      }
    
   /* now calculate the transition */
    ctr = Ttype(curr_tr->node, iseq[posi], iseq[posj], iseq[posk], iseq[posl], size);

    /* dose of paranoia */
    if(ctr >= Ntrans[cst]) 
      Die ("Somebody screwed up. Wrong transition for State %s, Node %s\n", 
	   stNAME[cst], dpcNAME(curr_tr->node));    
    else if (ctr != -1 && Ntype(cst,ctr) != curr_tr->node)
      Die ("Somebody screwed up. %s: %d %d %d %d. Node -%d- should be equal to -%d-.\n",
	   stNAME[cst], posi, posj, posk, posl,
	   curr_tr->node, Ntype(cst,ctr));
    
    /* Bump the counter. Ignore ambiguous nucleotides completely. (-1)
     */
    if (cst != -1 && ctr != -1)
      cfg[cst][ctr] += wgt;

    /* another dose of paranoia */
    if (!Connects(cst,curr_tr->node))
      {
       	/*if (cfg[cst][ctr] != 0.0 && cst == WH && ctr == idxEW)*/
       	if (cfg[cst][ctr] != 0.0 && ctr == idxEW)
	  Warn("most likely there is an untracktable interaction in this structure");
	else if (cfg[cst][ctr] != 0.0)    
	  Die("Somebody screwed up. %s --> %s transition is nonzero.\n",
	      stNAME[cst], dpcNAME(Ntype(cst,ctr)));
      }

    if (curr_tr->nxtr != NULL) PushTraceknstack(dolist, curr_tr->nxtr);
    if (curr_tr->nxtl != NULL) PushTraceknstack(dolist, curr_tr->nxtl);
  }

  free(iseq);
  FreeTraceknstack(dolist);
}


/* Function: parseV()
 * 
 * ER, Wed Jun 23 13:25:09 CDT 1999 [STL]
 *
 * Purpose:  traceback from V state
 *           
 * Args:     seq      - sequence, 0..len-1
 *           ss       - structure string. May contain pseudoknots.
 *           len      - length of both seq and ss
 *           curr_tr  - ptr to node of tr we're working on
 *           dolist   - pushdown stack of active tr nodes
 *           
 * Return:   void.
 */
void
parseV(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
	    struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug)
{
  int i, k, l;        /* coordinates of starting V state                  */
  int mid;
  int fsti, fstj;     /* first paired nucleotide starting from i,j        */
  /* int i1, k1, l1, j1;  coordinates of one WH of non-nested bifurcation  */
  /* int fst, lst;        first and last base in (i1,j1) that pair outside (i1,j1) */
  int imax, jmin;

  i = j - d;
  k = i + d1;
  l = j - d2;

  if (ct[i] != j || ct[j] != i)
    Die ("bad assignment for state V");

    fsti = -1; /* initialize to nonsense the position for fsti */
    fstj = -1; /* initialize to nonsense the position for fstj */

  /* determine the first paired nucleotide starting from i (fsti)
   */
  for (mid = 1; mid <= d; mid++)
    if (ct[i+mid] != i+mid) {
      fsti = i+mid;
      break;
    }
  /* determine the first paired nucleotide starting from j (fstj)
   */
  for (mid = 1; mid <= d; mid++)
    if (ct[j-mid] != j-mid) {
      fstj = j-mid;
      break;
    }

  if (fsti == -1 || fstj == -1)
    Die ("coudn't find base pairs in V");

      
  /* 
   * INTERVAL: mi == (fsti,ct[fsti])
   */
  /* imax ==  highest base of (i,j) to which a base from mi is paired 
   */
  imax = ct[fsti];
  for (mid = 0; mid <= ct[fsti]-fsti; mid++)
    if (ct[fsti+mid] != fsti+mid) 
      imax = (ct[fsti+mid]>imax)? ct[fsti+mid]:imax;
  
  /* 
   * INTERVAL: mj == (ct[fstj],fstj)
   */
  /* jmin ==  lowest base of (i,j) to which a base from mj is paired 
   */
  jmin = ct[fstj];
  for (mid = 0; mid <= fstj-ct[fstj]; mid++)
    if (ct[fstj-mid] != fstj-mid) 
      jmin = (ct[fstj-mid]<jmin)? ct[fstj-mid]:jmin;
  
  /* first determine the NODE we are at... 
   */
  /* [(v1): V --> IS1 ] 
   */
  if (fsti == j && fstj == i) {
    curr_tr->node = dpcS1;
    if (d >= MAXRNALOOP)
      Warn ("hairpin loop (%d, %d) in V has longer size than %d", j, d, MAXRNALOOP);
  }
  /* [(v2): V --> IS2 \supset a V b ] 
   */
  else if (ct[fsti] == fstj) {
    if (fsti-i == 1 && j-fstj == 1)                                    /* stem    */
      curr_tr->node = dpcS2S;
    
    else if (fsti-i == 2 && j-fstj == 1)                               /* bulge-L */
      curr_tr->node = dpcS2B;
    else if (fsti-i > 2 && j-fstj == 1 && fsti-i+j-fstj < MAXRNALOOP)  
      curr_tr->node = dpcS2B;
    else if (fsti-i > 1 && j-fstj == 1 && fsti-i+j-fstj >= MAXRNALOOP) {
      curr_tr->node = dpcS2B;
      Warn ("IS2BL (%d, %d, %d, %d) in V has longer size than %d", 
	    j, d, fsti-i, j-fstj, MAXRNALOOP);
    }
    
    else if (fsti-i == 1 && j-fstj == 2)                               /* bulge-R */
      curr_tr->node = dpcS2B;
    else if (fsti-i == 1 && j-fstj > 2 && fsti-i+j-fstj < MAXRNALOOP)  
      curr_tr->node = dpcS2B;
    else if (fsti-i == 1 && j-fstj > 1 && fsti-i+j-fstj >= MAXRNALOOP) {
      curr_tr->node = dpcS2B;
      Warn ("IS2BR (%d, %d, %d, %d) in V has longer size than %d", 
	    j, d, fsti-i, j-fstj, MAXRNALOOP);
    }
    else if (fsti-i > 1 && j-fstj > 1 && fsti-i+j-fstj < MAXRNALOOP)   /* int loop */
      curr_tr->node = dpcS2I;
    else if (fsti-i > 1 && j-fstj > 1 && fsti-i+j-fstj >= MAXRNALOOP) {
      curr_tr->node = dpcS2I;
      Warn ("IS2I (%d, %d, %d, %d) in V has longer size than %d", 
	    j, d, fsti-i, j-fstj, MAXRNALOOP);
    }
    
  }
  else if (ct[fsti] < ct[fstj]) {
    if (imax < jmin)
      curr_tr->node = dpcMV;     /* [(v3): V --> WB.WB ]       */
    else if (imax > jmin)
      Die("this is a pseudoknot [(v7): V --> WH\times WH ]");
    /* curr_tr->node = dpcMVPK;    [(v7): V --> WH\times WH ] */
  }
  else if (ct[fsti] > ct[fstj]) 
    Die("this is a pseudoknot [(v7): V --> WH\times WH ]");
    /* curr_tr->node = dpcMVPK;    [(v7): V --> WH\times WH ] */

  if (debug) fprintf(outf,"NODE: %s (%s)\n", dpcNAME(curr_tr->node), 
		     dpcNAME(curr_tr->node));
  
  /* ...then, TRACEBACK from this node
   */
  switch (curr_tr->node) {

    /* [(v1): V --> IS1 ] 
     */
  case dpcS1:  
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j, k, l, IS1, dpcLEN(j-i)));
    if (debug) fprintf(outf," trace IS1 (%d,%d)\n", j, d); 
    break;

    /* [(v2): V --> IS2 \supset a V b] 
     */ 
  case dpcS2S: 
    PushTraceknstack(dolist, AttachTracekn(curr_tr, fsti, fstj, 
					   fsti+(int)((fstj-fsti)/2), 
					   fsti+(int)((fstj-fsti)/2)+1, V, dpcEV));
    if (debug) fprintf(outf," trace V (%d,%d)\n", fsti, fstj); 

    if (debug) fprintf(outf," IS2S (%d,%d:%d,%d)\n", j, d, d1, d2);

    break;

  case dpcS2B: 
    PushTraceknstack(dolist, AttachTracekn(curr_tr, fsti, fstj, 
					   fsti+(int)((fstj-fsti)/2), 
					   fsti+(int)((fstj-fsti)/2)+1, V, dpcEV));
    if (debug)  fprintf(outf," trace V (%d,%d)\n", fsti, fstj); 

    if (fsti-i+j-fstj < MAXRNALOOP) {
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j, fsti, fstj, IS2B, dpcLEN(fsti-i+j-fstj)));
      if (debug) fprintf(outf," trace IS2 (%d,%d:%d,%d)\n", j, d, d1, d2);
    }
    break;

  case dpcS2I: 
    PushTraceknstack(dolist, AttachTracekn(curr_tr, fsti, fstj, 
					   fsti+(int)((fstj-fsti)/2), 
					   fsti+(int)((fstj-fsti)/2)+1, V, dpcEV));
    if (debug)  fprintf(outf," trace V (%d,%d)\n", fsti, fstj); 

    if (fsti-i+j-fstj < MAXRNALOOP) {
      PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j, fsti, fstj, IS2I, dpcLEN(fsti-i+j-fstj)));
      if (debug) fprintf(outf," trace IS2 (%d,%d:%d,%d)\n", j, d, d1, d2);
    }
    break;
    
    /* [(v3): V --> WB.WB ] 
     */
  case dpcMV:  
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, imax, 
					   i+1+(int)((imax-i-1)/2), 
					   i+2+(int)((imax-i-1)/2), WB, dpcEW));
    PushTraceknstack(dolist, AttachTracekn(curr_tr, imax+1, j-1, 
					   imax+1+(int)((j-imax-2)/2), 
					   imax+2+(int)((j-imax-2)/2), WB, dpcEW));
    if (debug) {
      fprintf(outf," trace WB (%d,%d)\n", i+1, ct[fsti]); 
      fprintf(outf," trace WB (%d,%d)\n", ct[fsti]+1, j-1); 
    }
    break;

  default:
    Die ("something went wrong tracing back state V");
  }
}

/* Function: TracebackW()
 * 
 * ER, Wed Jun 23 13:25:09 CDT 1999 [STL]
 *
 * Purpose:  traceback from W state
 *           
 * Args:     seq      - sequence, 0..len-1
 *           ss       - structure string. May contain pseudoknots.
 *           len      - length of both seq and ss
 *           curr_tr  - ptr to node of tr we're working on
 *           dolist   - pushdown stack of active tr nodes
 *           
 * Return:   void.
 */
void
parseW(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
       struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug)
{
  int i, k, l;                /* coordinates of starting WB state                        */
  int i1, k1, l1, j1;         /* coordinates of one WH of non-nested bifurcation         */
  /* int fst, lst;                fist and last base in (i1,j1) that pair outside (i1,j1) */
  int midi;

  i = j - d;
  k = i + d1;
  l = j - d2;

  /* define somewhere the possible bifurcation positions */
  i1 = i;
  j1 = j;
  k1 = i1;
  l1 = j1;

  /* first determine the NODE we are at... 
   */
  if (ct[i] == j)         curr_tr->node = dpcP;   /* [(w1): W --> a V b ]           */
  /*else if (ct[i] == -1 && ct[j] == -1 && ct[i+1] == j-1)
                          curr_tr->node = dpcDLR;    [(w2): W --> a c V \bar{c} b ] 
  else if (ct[i] == -1 && ct[i+1] == j)
                          curr_tr->node = dpcDL;     [(w3): W --> a c V \bar{c}   ] 
  else if (ct[j] == -1 && ct[j-1] == i)
                          curr_tr->node = dpcDR;     [(w4): W -->   c V \bar{c} b ] */
  else if (ct[i] == i)   curr_tr->node = dpcL;   /* [(w5): W --> a W   ]           */
  else if (ct[j] == j)   curr_tr->node = dpcR;   /* [(w6): W -->   W a ]           */
  else if (ct[i] < ct[j]) 
    for (midi = 1; midi < ct[i]-i; midi++)
      if (ct[i+midi] == i+midi || ct[i+midi] < ct[i]) {
	curr_tr->node = dpcBW;          /* [(w7): W --> W.W ]         */
	k1 = ct[i];
	l1 = k1+1;
      }
      else  Die("this is a pseudoknot");
  else if (ct[j] < ct[i])      
    Die("this is a pseudoknot [(w9): W --> WH\times WH ]");
  /* curr_tr->node = dpcBXW;           [(w9): W --> WH\times WH ] */
  
  if (debug) fprintf(outf,"NODE: %s (%d %d)\n", dpcNAME(curr_tr->node), ct[i], ct[j]);
  
  /* ...then, TRACEBACK from this node
   */
  switch (curr_tr->node) {
    
    /* [(w1): W --> a V b ] 
     */
  case dpcP:
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j, k, l, V, dpcEV));
    if (debug) fprintf(outf," trace V (%d,%d)\n", i, j); 
    break;

    /* [(w2): W --> a c V \bar{c} b ] 
     */
  case dpcL:
    if (d == 1) PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j, i+1, l, W, dpcEW));
    else PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j, k, l, W, dpcEW));

    if (debug) fprintf(outf," trace W (%d,%d)\n", i+1, j); 
    break;
  
    /* [(w6): W --> W a ] 
     */
  case dpcR:
    if (d == 1) PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1, k, j-1, W, dpcEW));
    else PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1, k, l, W, dpcEW));

    if (debug) fprintf(outf," trace W (%d,%d)\n", i, j-1); 
    break;
  
    /* [(w7): W(i,j) --> W(i1,k1).W(l1,j1) ] 
     */
  case dpcBW:
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i1, k1, i1+(int)((k1-i1)/2), 
					   i1+1+(int)((k1-i1)/2), W, dpcEW));
    PushTraceknstack(dolist, AttachTracekn(curr_tr, l1, j1, l1+(int)((j1-l1)/2), 
					   l1+1+(int)((j1-l1)/2), W, dpcEW));
    if (debug) {
      fprintf(outf," trace W (%d,%d)\n", i1, k1);
      fprintf(outf," trace W (%d,%d)\n", l1, j1); 
    }
    break;
  
  default:
    Die ("something went wrong tracing back state W");
  }
}

/* Function: parseWB()
 * 
 * ER, Wed Jun 23 13:25:09 CDT 1999 [STL]
 *
 * Purpose:  traceback from WB state
 *           
 * Args:     seq      - sequence, 0..len-1
 *           ss       - structure string. May contain pseudoknots.
 *           len      - length of both seq and ss
 *           curr_tr  - ptr to node of tr we're working on
 *           dolist   - pushdown stack of active tr nodes
 *           
 * Return:   void.
 */
void
parseWB(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
	struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug)
{
  int i, k, l;                /* coordinates of starting WB state                        */
  int i1, k1, l1, j1;         /* coordinates of one WH of non-nested bifurcation         */
  /*int fst, lst;                fist and last base in (i1,j1) that pair outside (i1,j1) */
  int midi;

  i = j - d;
  k = i + d1;
  l = j - d2;

  /* define somewhere the possible bifurcation positions */
  i1 = i;
  j1 = j;
  k1 = i1;
  l1 = j1;

  /* first determine the NODE we are at... 
   */
  if (ct[i] == j)         curr_tr->node = dpcP;   /* [(wb1): WB --> a V  b ]          */
  /*else if (ct[i] == -1 && ct[j] == -1 && ct[i+1] == j-1)
                          curr_tr->node = dpcDLR;    [(wb2): WB --> a c V \bar{c} b ] 
  else if (ct[i] == -1 && ct[i+1] == j)
                          curr_tr->node = dpcDL;     [(wb3): WB --> a c V \bar{c}   ] 
  else if (ct[j] == -1 && ct[j-1] == i)
                          curr_tr->node = dpcDR;     [(wb4): WB -->   c V \bar{c} b ] */
  else if (ct[i] == i)   curr_tr->node = dpcL;   /* [(wb5): WB --> a WB   ]          */
  else if (ct[j] == j)   curr_tr->node = dpcR;   /* [(wb6): WB -->   WB a ]          */
  else if (ct[i] < ct[j]) 
    for (midi = 1; midi < ct[i]-i; midi++)
      if (ct[i+midi] == i+midi || ct[i+midi] < ct[i]) {
	curr_tr->node = dpcBW;          /* [(w7): W --> W.W ]         */
	k1 = ct[i];
	l1 = k1+1;
      }
      else  Die("this is a pseudoknot");
  else if (ct[j] < ct[i])
    Die("this is a pseudoknot [(wb9): WB --> WH\times WH ]");
  /*    curr_tr->node = dpcBXW;           /[(wb9): WB --> WH\times WH ] */
  
  if (debug) fprintf(outf,"NODE: %s (%d %d)\n", dpcNAME(curr_tr->node), ct[i], ct[j]);
  
  /* ...then, TRACEBACK from this node
   */
  switch (curr_tr->node) {
    
    /* [(wb1): WB --> a V b ] 
     */
  case dpcP:
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j, k, l, V, dpcEV));
    if (debug) fprintf(outf," trace V (%d,%d)\n", i, j); 
    break;

    /* [(w2): WB --> a c V \bar{c} b] 
     */
  case dpcL:
    if (d == 1) PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j, i+1, l, WB, dpcEW));
    else PushTraceknstack(dolist, AttachTracekn(curr_tr, i+1, j, k, l, WB, dpcEW));

    if (debug) fprintf(outf," trace WB (%d,%d)\n", i+1, j); 
    break;
  
    /* [(wb6): WB --> WB a ] 
     */
  case dpcR:
    if (d == 1) PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1, k, j-1, WB, dpcEW));
    else PushTraceknstack(dolist, AttachTracekn(curr_tr, i, j-1, k, l, WB, dpcEW));

    if (debug) fprintf(outf," trace WB (%d,%d)\n", i, j-1); 
    break;
  
    /* [(wb7): WB --> WB.WB ] 
     */
  case dpcBW:
    PushTraceknstack(dolist, AttachTracekn(curr_tr, i1, k1, i1+(int)((k1-i1)/2), 
					   i1+1+(int)((k1-i1)/2), WB, dpcEW));
    PushTraceknstack(dolist, AttachTracekn(curr_tr, l1,j1, l1+(int)((j1-l1)/2), 
					   l1+1+(int)((j1-l1)/2), WB, dpcEW));
    if (debug) {
      fprintf(outf," trace WB (%d,%d)\n", i1, k1);
      fprintf(outf," trace WB (%d,%d)\n", l1, j1); 
    }
    break;
  
  default:
    Die ("something went wrong tracing back state WB");
  }
}

/* Function: parseIS1()
 * 
 * ER, Wed Jun 23 13:25:09 CDT 1999 [STL]
 *
 * Purpose:  traceback from IS1 state
 *           
 * Args:     seq      - sequence, 0..len-1
 *           ss       - structure string. May contain pseudoknots.
 *           len      - length of both seq and ss
 *           curr_tr  - ptr to node of tr we're working on
 *           dolist   - pushdown stack of active tr nodes
 *           
 * Return:   void.
 */
void
parseIS1(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
	 struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug)
{
  /* Here there should be only one node
   */
  if (curr_tr->node == dpcEIS)
    Die ("something went wrong tracing back state IS1");
  
  if (debug) fprintf(outf," hairpin loop (%d)\n", d);
}

/* Function: parseIS2()
 * 
 * ER, Wed Jun 23 13:25:09 CDT 1999 [STL]
 *
 * Purpose:  parse from IS2 state
 *           
 * Args:     seq      - sequence, 0..len-1
 *           ss       - structure string. May contain pseudoknots.
 *           len      - length of both seq and ss
 *           curr_tr  - ptr to node of tr we're working on
 *           dolist   - pushdown stack of active tr nodes
 *           
 * Return:   void.
 */
void
parseIS2(FILE *outf, int *ct, int len, int j, int d, int d1, int d2, 
	 struct tracekn_s *curr_tr, struct traceknstack_s *dolist, int debug)
{
  /* Here there should be only one node
   */
  if (curr_tr->node == dpcEIS)
    Die ("something went wrong tracing back state IS2BL");
  
  if (debug) fprintf(outf," IS2 (%d,%d)\n", d1,d2);
}

