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

/* codcreatealign.c
 *
 * ER, Wed Jun  2 09:23:28 CDT 1999 [St. Louis]
 * 
 * creates an alignment acoding to the codingmodel
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

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

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

static int is_first_pos (int codX, int codY);
static int is_second_pos(int codX, int codY);
static int is_third_pos (int codX, int codY);
static void pass_thru_COJ(FILE *ofp, int L, int *ret_len, SQINFO *sqinfoX, int **ret_seqX, SQINFO *sqinfoY, int **ret_seqY, int start, 
			  int *ret_nxt_pos, struct othmodel_s *oth, char **ret_charX, char **ret_charY, int traceback, 
			  int alignment, char *string_name);
static void realloc_strings(int L, int *ret_len, int k, int **ret_seqX, int **ret_seqY, char **ret_charX, char **ret_charY);


/* Function: AllocEMitCodon()
 * Date:     ER, Sat Mar 15 12:44:30 CST 2003 [St. Louis]
 *
 * Purpose:  Allocate structure emitcodon_s
 *
 * Args:    
 *
 * Returns:  void
 */
struct emitcodon_s *
AllocEmitCodon (void)
{
  struct emitcodon_s *emitcodon;
  int                 cod3 =  64;
  int                 cod2 =  16;
  int                 cod4 = 256;

  emitcodon = (struct emitcodon_s *) MallocOrDie (sizeof(struct emitcodon_s));

  emitcodon->pcod      = (double *) MallocOrDie (sizeof(double) * cod3 * cod3);
  emitcodon->pcod_hexa = (double *) MallocOrDie (sizeof(double) * cod3 * cod3);
  emitcodon->pcod30    = (double *) MallocOrDie (sizeof(double)        * cod3);
  emitcodon->pcod32    = (double *) MallocOrDie (sizeof(double) * cod3 * cod2);
  emitcodon->pcod34    = (double *) MallocOrDie (sizeof(double) * cod3 * cod4);

  return emitcodon;
}

/* Function: FillEMitCodon()
 * Date:     ER, Sat Mar 15 12:58:27 CST 2003 [St. Louis] 
 * 
 * Purpose:  Fill structure emitcodon_s. The codon emission probabilities in an
 *           array format that it's easy to sample from
 * 
 * Args:    
 *
 * Returns:  void
 */
void
FillEmitCodon (struct codmodel_s *cod, struct nullmodel_s *null, struct emitcodon_s *emitcodon)
{
  int     codon3 =  64;
  int     codon2 =  16;
  int     codon4 = 256;
  int     x, y;
  int     i;

  for (i = 0; i < codon3*codon3; i++) 
    {
      x = i / codon3;
      y = i % codon3;
      emitcodon->pcod[i] = cod->pcodon[x][y];
    }
  CheckSingleLog2Prob(emitcodon->pcod, codon3*codon3);

  for (i = 0; i < codon3; i++) {
    emitcodon->pcod30[i] = average_codon(i, cod, null, FALSE);
  }
  CheckSingleLog2Prob(emitcodon->pcod30, codon3);

  for (i = 0; i < codon3*codon2; i++) {
    x = i/16;
    y = i%16;

    emitcodon->pcod32[i] = average(3, y/4, y%4, x, cod, null, FALSE);
  }
  CheckSingleLog2Prob(emitcodon->pcod32, codon3*codon2);

  for (i = 0; i < codon3*codon4; i++) 
    {
      x = i / codon4;
      y = i % codon4;

      emitcodon->pcod34[i] = cod->pcodon[x][y/4] + null->yem[y%4];
    }
  CheckSingleLog2Prob(emitcodon->pcod34, codon3*codon4);


}
/* Function: FreeEMitCodon()
 * Date:     ER, Sat Mar 15 12:44:30 CST 2003 [St. Louis] 
 * 
 * Purpose:  Allocate structure emitcodon_s 
 * 
 * Args:    
 *
 * Returns:  void
 */
void
FreeEmitCodon (struct emitcodon_s *emitcodon)
{
  free(emitcodon->pcod);
  free(emitcodon->pcod_hexa);
  free(emitcodon->pcod30);
  free(emitcodon->pcod32);
  free(emitcodon->pcod34);

  free(emitcodon);

}

/* Function: SimulateCodingSequences()
 * Date:     SRE, Thu Jun 11 10:06:28 1998 [St. Louis]
 *
 * Purpose:  Simulate an ungapped alignment of two coding
 *           sequences, given a pammodel
 *
 * Args:     L        - length of the alignment; will be made next lowest multiple of three
 *           pammodel - model, [64][64] joint probabilities
 *           ret_s1   - RETURN: first seq
 *           ret_s2   - RETURN: second seq
 *
 * Returns:  void
 */
void
SimulateCodingSequences(int L, double pammodel[64][64], char **ret_s1, char **ret_s2, 
			double *ret_first_pos, double *ret_second_pos, double *ret_third_pos)
{
  char  *s1, *s2;
  double p[4096];
  double len_cod33  = 0.0;
  double first_pos  = 0.0;
  double second_pos = 0.0;
  double third_pos  = 0.0;

  int    pos;
  int    x,y, i;

  L = (L / 3) * 3;  /* make a multiple of three */

  s1 = MallocOrDie(sizeof(char) * (L+1));
  s2 = MallocOrDie(sizeof(char) * (L+1));
  
  for (i = 0; i < 4096; i++) 
    {
      x = i / 64;
      y = i % 64;
      p[i] = pammodel[x][y];
    }

  for (pos = 0; pos < L; pos += 3)
    {
      i = DChoose(p, 4096);
      x = i / 64;
      y = i % 64;

	if (is_first_pos(x,  y)) first_pos  += 1.0;
	if (is_second_pos(x, y)) second_pos += 1.0;
	if (is_third_pos(x,  y)) third_pos  += 1.0;
	len_cod33 += 3.0;

      s1[pos]   = DNAAlphabet[x/16];
      s1[pos+1] = DNAAlphabet[(x%16)/4];
      s1[pos+2] = DNAAlphabet[x%4];

      s2[pos]   = DNAAlphabet[y/16];
      s2[pos+1] = DNAAlphabet[(y%16)/4];
      s2[pos+2] = DNAAlphabet[y%4];
    }
  s1[L] = '\0';
  s2[L] = '\0';

  *ret_s1 = s1;
  *ret_s2 = s2;

  *ret_first_pos  = first_pos/len_cod33;
  *ret_second_pos = second_pos/len_cod33;
  *ret_third_pos  = third_pos/len_cod33;


  return;
}


/* Function:  SimulateCODAlign()
 * Date:      ER, Mon Feb  7 10:55:52 CST 2000 [St. Louis]
 *
 * Purpose:  Simulate an aligment of two sequences
 *           related by the COD model.
 *
 * Args:     s1, s2    - the sequences 
 *           L         - length of aligmnemt
 *           othmodel - the null model
 *
 * Returns:  (void)
 */
void
SimulateCODAlign(FILE *ofp, SQINFO *sqinfoX, int **seqX, SQINFO *sqinfoY, int **seqY, int L, int start, int *ret_len, 
		 struct codmodel_s *cod, struct emitcodon_s *emitcodon, struct nullmodel_s *null,
		 int traceback, int alignment, char *string_name,
		 double *ret_first_pos, double *ret_second_pos, double *ret_third_pos)
{
  struct tracer_s      *tr;      /* the traceback tree under construction  */
  struct tracer_s      *cur_tr;  /* ptr to node of tr we're working on     */
  struct tracerstack_s *dolist;  /* pushdown stack of active tr nodes      */

  int                  *mseqX;
  int                  *mseqY;
  char                 *mcharX;
  char                 *mcharY;

  int    len;                      /* length of the allocated strings      */
  int    leg;                      /* length of the aligment generated     */
  double len_cod33  = 0.0;
  double first_pos  = 0.0;
  double second_pos = 0.0;
  double third_pos  = 0.0;

  double hexa, extra_hexamer;
  double sum;
  int    flag;

  int    codon3 = 64;
  int    codon4 = 256;
  int    codon2 = 16;

  int    cur_x,   cur_y;         /* nucleotides at those positions         */
  int    cur_st,  nxt_st;
  int    cur_pos, nxt_pos;
  int    pick;
  int    i;
  int    x1, x2, x3, x4;
  int    y1, y2, y3, y4;
  int    xx1, xx2, xx3;
  int    yy1, yy2, yy3;
  int    verbose;

  verbose = FALSE;

  len = L;

  AllocCharSeqs(len, &mcharX, &mcharY);

  mseqX = *seqX;
  mseqY = *seqY;

 /* Initialize
   * Start at pos "0" with state "stB"
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */
  
  cur_tr = AttachTracer(tr, start, stCb); 
  PushTracerstack(dolist, cur_tr);

  while ((cur_tr = PopTracerstack(dolist)) != NULL)
    {
      cur_st = cur_tr->type;
      cur_pos = cur_tr->emit;

      if (cur_pos >= len-6) {   
	if (verbose) Warn ("SimulateCODAlign(): reallocate %d --> %d", len, len+L+4);
	realloc_strings(4+L, &len, cur_pos, &mseqX, &mseqY, &mcharX, &mcharY); 
      }
      
      xx1 = (cur_pos > 2)? mseqX[cur_pos - 3] : 4; if (xx1 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (xx1=%d)", cur_pos - 3, xx1);
      xx2 = (cur_pos > 1)? mseqX[cur_pos - 2] : 4; if (xx2 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (xx2=%d)", cur_pos - 2, xx2);
      xx3 = (cur_pos > 0)? mseqX[cur_pos - 1] : 4; if (xx3 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (xx3=%d)", cur_pos - 1, xx3);
      
      yy1 = (cur_pos > 2)? mseqY[cur_pos - 3] : 4; if (yy1 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (yy1=%d)", cur_pos - 3, yy1);
      yy2 = (cur_pos > 1)? mseqY[cur_pos - 2] : 4; if (yy2 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (yy2=%d)", cur_pos - 2, yy2);
      yy3 = (cur_pos > 0)? mseqY[cur_pos - 1] : 4; if (yy3 > 4) Die ("SimulateCODAlign(): bad nucleotide pos=%d (yy3=%d)", cur_pos - 1, yy3);

      sum = 0.0;
      for (i = 0; i < 4096; i++) {
	hexa = 0.0;
	flag = 0;

	if ( !isitagap(xx1) && !isitagap(xx2) && !isitagap(xx3) ) 
	  {
	    hexa += EXP2(cod->phexa[CODON(xx1,xx2,xx3)][i/64]);
	    flag ++;
	  }

	if ( !isitagap(yy1) && !isitagap(yy2) && !isitagap(yy3) )
	  {
	    hexa += EXP2(cod->phexa[CODON(yy1,yy2,yy3)][i%64]);
	    flag ++;
	  }
	
	switch (flag) 
	  {
	  case 0:  extra_hexamer =  0.0;              break;
	  case 1:  extra_hexamer =        LOG2(hexa); break;
	  case 2:  extra_hexamer = -1.0 + LOG2(hexa); break;
	  default: Die ("impossible! flag = %d\n", flag);
	  }
	
	emitcodon->pcod_hexa[i] = emitcodon->pcod[i] + extra_hexamer;

	sum += EXP2(emitcodon->pcod_hexa[i]);
      }
      if (sum > 1.01 || sum < 0.99) Die ("pcod_hexa probs do not add up to one, %f", sum);
      
      pick = DENChoose(cod->t + IdxTransCOD[cur_st], TransPerCODState[cur_st]);
      nxt_st = ToStateCOD[pick + IdxTransCOD[cur_st]];

     switch (cur_st){
      case stCb:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stCe:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stCOJ:
	/* passing thru COJ may introduce additional frame shifts. Not sure whether the 
	 * model can handle so much flexibility.
	 *
	 * (March 19, 2000) I have reduced the probabilisties of c34,c43,.. 
	 * (ie all the possible transitions that produce frame shifts). 
	 * If those frameshiter parameters are set too high,
	 * the model picks condons from everywhere in random alignments.
	 *
	 * Also, ScoreWithCOD() now also passsed thru COJ without allowing any emissions,
	 * so it is only fair not to allow any COJ emission here.
	 *
	 * Fri Mar 14 14:58:05 CST 2003
	 *  finally a proper sampling using all the model
	 *
	 */
	pass_thru_COJ(ofp, L, &len, sqinfoX, &mseqX, sqinfoY, &mseqY, cur_pos, &nxt_pos, cod->COJ, &mcharX, &mcharY,  
		      traceback, alignment, string_name); 

	if (nxt_pos > cur_pos) {
	  mcharX[cur_pos] = '*';
	  mcharY[cur_pos] = '*';
	}
	break;

      case stC33:
	i = DLog2Choose(emitcodon->pcod_hexa, codon3*codon3);

	cur_x = i / codon3;
	cur_y = i % codon3;

	x1 = cur_x/16;
	x2 = (cur_x%16)/4;
	x3 = cur_x%4;
	
	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	if (is_first_pos(cur_x,  cur_y)) first_pos  += 1.0;
	if (is_second_pos(cur_x, cur_y)) second_pos += 1.0;
	if (is_third_pos(cur_x,  cur_y)) third_pos  += 1.0;
	len_cod33 += 3.0;

	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = DNAAlphabet[x3];
	
	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = DNAAlphabet[y3];

	nxt_pos = cur_pos + 3;
	break;

      case stC34:
	i = DLog2Choose(emitcodon->pcod34, codon3*codon4);
	
	cur_x = i / codon4;
	cur_y = i % codon3;
	
	x1 = cur_x/16;
	x2 = (cur_x%16)/4;
	x3 = cur_x%4;
	x4 = 4;
	
	y1 = (cur_y/4)/16;
	y2 = ((cur_y/4)%16)/4;
	y3 = (cur_y/4)%4;
	y4 = cur_y%4;
	
	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	mseqX[cur_pos+3] = x4;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	mseqY[cur_pos+3] = y4;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = DNAAlphabet[x3];
	mcharX[cur_pos+3] = '.';
	
	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = DNAAlphabet[y3];
	mcharY[cur_pos+3] = DNAAlphabet[y4];

	nxt_pos = cur_pos + 4;
	break;

      case stC32:
	i = DLog2Choose(emitcodon->pcod32, codon3*codon2);

	cur_x = i / codon2;
	cur_y = i % codon2;

	x1 = cur_x/16;
	x2 = (cur_x%16)/4;
	x3 = cur_x%4;
	
	y1 = cur_y/4;
	y2 = cur_y%4;
	y3 = 4;
	
	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = DNAAlphabet[x3];
	
	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = '.';

	nxt_pos = cur_pos + 3;
	break;

      case stC43:
	i = DLog2Choose(emitcodon->pcod34, codon3*codon4);
	
	cur_x = i % codon4;
	cur_y = i / codon4;
	
	x1 = (cur_x/4)/16;
	x2 = ((cur_x/4)%16)/4;
	x3 = (cur_x/4)%4;
	x4 = cur_x%4;

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	y4 = 4;

	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	mseqX[cur_pos+3] = x4;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	mseqY[cur_pos+3] = y4;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = DNAAlphabet[x3];
	mcharX[cur_pos+3] = DNAAlphabet[x4];
	
	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = DNAAlphabet[y3];
	mcharY[cur_pos+3] = '.';

	nxt_pos = cur_pos + 4;
	break;

       case stC44:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stC42:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

       case stC23:
	i = DLog2Choose(emitcodon->pcod32, codon2*codon3);

	cur_x = i % codon2;
	cur_y = i / codon2;

	x1 = cur_x/4;
	x2 = cur_x%4;
	x3 = 4;
	
	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = '.';
	
	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = DNAAlphabet[y3];

	nxt_pos = cur_pos + 3;
	break;

      case stC24:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;
	
       case stC22:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

        case stC30:
	i = DLog2Choose(emitcodon->pcod30, codon3);

	cur_x = i;

	x1 = cur_x/16;
	x2 = (cur_x%16)/4;
	x3 = cur_x%4;
	
	mseqX[cur_pos]   = x1;
	mseqX[cur_pos+1] = x2;
	mseqX[cur_pos+2] = x3;
	
	mseqY[cur_pos]   = 4;
	mseqY[cur_pos+1] = 4;
	mseqY[cur_pos+2] = 4;
	
	mcharX[cur_pos]   = DNAAlphabet[x1];
	mcharX[cur_pos+1] = DNAAlphabet[x2];
	mcharX[cur_pos+2] = DNAAlphabet[x3];
	
	mcharY[cur_pos]   = '.';
	mcharY[cur_pos+1] = '.';
	mcharY[cur_pos+2] = '.';

	nxt_pos = cur_pos + 3;
	break;

      case stC40:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

       case stC20:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

       case stC03:
	i = DLog2Choose(emitcodon->pcod30, 64);

	cur_y = i;

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	mseqX[cur_pos]   = 4;
	mseqX[cur_pos+1] = 4;
	mseqX[cur_pos+2] = 4;
	
	mseqY[cur_pos]   = y1;
	mseqY[cur_pos+1] = y2;
	mseqY[cur_pos+2] = y3;
	
	mcharX[cur_pos]   = '.';
	mcharX[cur_pos+1] = '.';
	mcharX[cur_pos+2] = '.';

	mcharY[cur_pos]   = DNAAlphabet[y1];
	mcharY[cur_pos+1] = DNAAlphabet[y2];
	mcharY[cur_pos+2] = DNAAlphabet[y3];
	
	nxt_pos = cur_pos + 3;
	break;

       case stC04:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stC02:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;
	
      case stCOE:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;
	
       default:
	Die("invalid state (%d) in SimulateCODAlign()", cur_st);
      }

      if (traceback) fprintf(ofp, "emiting %s (%d)\n", cstNAME[cur_st], cur_pos);
      
      if (nxt_st != stCOE) {
	if (traceback) fprintf(ofp," %s->%s \n", cstNAME[cur_st], cstNAME[nxt_st]);
	PushTracerstack(dolist, AttachTracer(cur_tr, nxt_pos+start, nxt_st));
      }
    }
  
  leg = cur_pos;


  if (alignment) PrintNewCharSeqs(ofp, sqinfoX, sqinfoY, start, leg, mcharX, mcharY, string_name);

  if (len_cod33 > 0) {
    *ret_first_pos  = first_pos/len_cod33;
    *ret_second_pos = second_pos/len_cod33;
    *ret_third_pos  = third_pos/len_cod33;
  }
  else {
    *ret_first_pos  = 0.0;
    *ret_second_pos = 0.0;
    *ret_third_pos  = 0.0;
  }
  
  *seqX    = mseqX;
  *seqY    = mseqY;
  *ret_len = len;
 
  free(mcharX);
  free(mcharY);
  FreeTracer(tr);
  FreeTracer(cur_tr);
  FreeTracerstack(dolist);

}


/* Function:  SimulateCODAlign()
 * Date:      ER, Tue Mar 21 15:02:44 CST 2000 [St. Louis]
 *
 * Purpose:   Given a sequence generate another at a given evolutionary distance 
 *           forming an alignmentrelated by the COD model.
 *
 * Args:     s1, s2    - the sequences 
 *           L         - length of aligmnemt
 *           othmodel - the null model
 *
 * Returns:  (void)
 */
void
SimulateCODSequence(FILE *ofp, int *seq, SQINFO *sqinfoX, int *seqX, SQINFO *sqinfoY, int *seqY, int start, 
		    int *L, struct codmodel_s *cod, struct nullmodel_s *null, struct ali_s *ali, 
		    int traceback, int alignment, char *string_name, 
		    double *ret_first_pos, double *ret_second_pos, double *ret_third_pos)
{
  struct tracer_s      *tr;      /* the traceback tree under construction  */
  struct tracer_s      *cur_tr;  /* ptr to node of tr we're working on     */
  struct tracerstack_s *dolist;  /* pushdown stack of active tr nodes      */

  double pccod33[64][64];
  double pccod44[256][256];
  double pccod32[64][16];
  double pccod23[16][64];
  double pccod34[64][256];
  double pccod43[265][64];
  double pccod42[256][16];
  double pccod24[16][256];

  double pcod30[64];
  double pcod40[256];
  double pcod04[256];
  double pcod20[16];

  double len_cod33  = 0.0;
  double first_pos  = 0.0;
  double second_pos = 0.0;
  double third_pos  = 0.0;

  int    ct, s[4];

  int    cur_x,   cur_y;         /* nucleotides at those positions         */
  int    cur_st,  nxt_st;
  int    cur_pos, nxt_pos, seq_pos = 0;
  int    len_seq;
  int    pick;
  int    i;
  int    x, y;
  int    x1, x2, x3, x4;
  int    y1, y2, y3, y4;

  len_seq = *L;

  /* C(2,0) */
  for (i = 0; i < 16; i++) 
    pcod20[i] = 0.;

  for (i = 0; i < 64; i++) {
    pcod30[i] = average_codon(i, cod, null, FALSE);
    pcod20[i/4] += EXP2(pcod30[i]);
   }
  DLog2(pcod20, 16);


  /* C(4,0) */
  for (i = 0; i < 256; i++) {
    pcod40[i] = average_codon(i/4, cod, null, FALSE) + null->xem[i%4];
    pcod04[i] = average_codon(i/4, cod, null, FALSE) + null->yem[i%4];
  }


  /* C(3,3) */
  for (i = 0; i < 4096; i++) 
    {
      x = i / 64;
      y = i % 64;
      pccod33[x][y] = cod->pcodon[x][y] - pcod30[x];
    }

  /* C(3,2) */
  /* C(2,3) */
  for (i = 0; i < 1024; i++) {
    x = i/16;
    y = i%16;

    pccod32[x][y] = average(3, y/4, y%4, x, cod, null, FALSE) - pcod30[x];
    pccod23[y][x] = average(3, y/4, y%4, x, cod, null, FALSE) - pcod20[y];
  }

  /* C(3,4) */
  /* C(4,3) */
  for (i = 0; i < 16384; i++) 
    {
      x = i / 256;
      y = i % 256;

      pccod34[x][y] = pccod33[x][y/4] + null->yem[y%4];
      pccod43[y][x] = pccod33[y/4][x];
    }


  /* C(4,4) */
  for (i = 0; i < 65536; i++) 
    {
      x = i / 256;
      y = i % 256;

      pccod44[x][y] = pccod33[x/4][y/4] + null->yem[y%4];
    }

  /* C(2,4) */
  /* C(4,2) */
  for (i = 0; i < 4096; i++) 
    {
      x = i / 16;
      y = i % 16;
      pccod42[x][y] = pccod32[x/4][y];
      pccod24[y][x] = pccod23[y][x/4] + null->yem[x%4];
    }

  /* check conditional probabilities
   */
  CheckSingleLog2Prob(pcod30, 64);
  CheckSingleLog2Prob(pcod40, 256);
  CheckSingleLog2Prob(pcod04, 256);
  CheckSingleLog2Prob(pcod20, 16);
  for (i = 0; i < 64; i++) {
    CheckSingleLog2Prob(pccod34[i], 256);
    CheckSingleLog2Prob(pccod33[i], 64);
    CheckSingleLog2Prob(pccod32[i], 16);
  }
  for (i = 0; i < 16; i++) {
    CheckSingleLog2Prob(pccod23[i], 64);
    CheckSingleLog2Prob(pccod24[i], 256);
  }
  for (i = 0; i < 256; i++) {
    CheckSingleLog2Prob(pccod44[i], 256);
    CheckSingleLog2Prob(pccod43[i], 64);
    CheckSingleLog2Prob(pccod42[i], 16);
  }

 /* Initialize
   * Start at pos "0" with state "stB"
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */
  
  cur_tr = AttachTracer(tr, start, stCb); 
  PushTracerstack(dolist, cur_tr);

  
  while ((cur_tr = PopTracerstack(dolist)) != NULL)
    {
      cur_st = cur_tr->type;
      
      cur_pos = cur_tr->emit;

     switch (cur_st){
      case stCb:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;

	/* do not allow any possible frameshit at the very begining
	 */
	if (cur_pos == 0) 
	  pick = DENChoose(cod->t + IdxTransCOD[cur_st], TransPerCODState[cur_st]-1);
	else 
	  pick = DENChoose(cod->t + IdxTransCOD[cur_st], TransPerCODState[cur_st]);
	
	nxt_st = cur_st + 1 + pick;
	break;

      case stCe:
	nxt_pos = cur_pos;
	if (nxt_pos < start+*L-2) nxt_st = stCb;
	else nxt_st = -1;
	break;

      case stC33:
	ct = 0;
	while (ct < 3) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	cur_x = CODON(x1,x2,x3);
	cur_y = DLog2Choose(pccod33[cur_x], 64);

	if (is_first_pos(cur_x,  cur_y)) first_pos  += 1.0;
	if (is_second_pos(cur_x, cur_y)) second_pos += 1.0;
	if (is_third_pos(cur_x,  cur_y)) third_pos  += 1.0;
	len_cod33 += 3.0;

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];

	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

      case stC34:
	ct = 0;
	while (ct < 3) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}

	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	x4 = 4;

	cur_x =  CODON(x1,x2,x3);
	cur_y = DLog2Choose(pccod34[cur_x], 256);
	
	y1 = (cur_y/4)/16;
	y2 = ((cur_y/4)%16)/4;
	y3 = (cur_y/4)%4;
	y4 = cur_y%4;
	
	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	ali->charX[cur_pos+3] = '.';
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	ali->charY[cur_pos+3] = DNAAlphabet[y4];

	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

      case stC32:
	ct = 0;
	while (ct < 3) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}

	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	
	cur_x = CODON(x1,x2,x3);
	cur_y = DLog2Choose(pccod32[cur_x], 16);

	y1 = cur_y/4;
	y2 = cur_y%4;
	y3 = 4;
	
	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = '.';

	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

      case stC43:
	ct = 0;
	while (ct < 4) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	x4 = s[3];
	
	cur_x = x1*64 + CODON(x2,x3,x4);
	cur_y = DLog2Choose(pccod43[cur_x], 64);
	
	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	y4 = 4;

	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	ali->charX[cur_pos+3] = DNAAlphabet[x4];
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	ali->charY[cur_pos+3] = '.';

	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

       case stC44:
	ct = 0;
	while (ct < 4) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	x4 = s[3];
	
	cur_x = x1*64 + CODON(x2,x3,x4);
	cur_y = DLog2Choose(pccod44[cur_x], 256);

	y = cur_y/4;
	y1 = y/16;
	y2 = (y%16)/4;
	y3 = y%4;
	y4 = cur_y%4;
	
	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	ali->charX[cur_pos+3] = DNAAlphabet[x4];
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	ali->charY[cur_pos+3] = DNAAlphabet[y4];

	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

      case stC42:
	ct = 0;
	while (ct < 4) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	x4 = s[3];
	
	cur_x = x1*64 + CODON(x2,x3,x4);
	cur_y = DLog2Choose(pccod42[cur_x], 16);
	
	y1 = cur_y/4;
	y2 = cur_y%4;
	y3 = 4;
	y4 = 4;

	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	ali->charX[cur_pos+3] = DNAAlphabet[x4];
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = '.';
	ali->charY[cur_pos+3] = '.';

	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

       case stC23:
	ct = 0;
	while (ct < 2) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = 4;
	
	cur_x = x1*16 + x2;
	cur_y = DLog2Choose(pccod23[cur_x], 64);

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = '.';
	
	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];

	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

      case stC24:
	ct = 0;
	while (ct < 2) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = 4;
	
	cur_x = x1*16 + x2;
	cur_y = DLog2Choose(pccod24[cur_x], 256);
	
	y1 = (cur_y/4)/16;
	y2 = ((cur_y/4)%16)/4;
	y3 = (cur_y/4)%4;
	y4 = cur_y%4;

	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = DNAAlphabet[y1];
	ali->charX[cur_pos+1] = DNAAlphabet[y2];
	ali->charX[cur_pos+2] = '.';
	ali->charX[cur_pos+3] = '.';

	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	ali->charY[cur_pos+3] = DNAAlphabet[y4];
	
	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

       case stC22:
	nxt_st = stCe;
	break;

        case stC30:
	ct = 0;
	while (ct < 3) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}

	x1 = s[0];
	x2 = s[1];
	x3 = s[2];

	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	
	seqY[cur_pos]   = 4;
	seqY[cur_pos+1] = 4;
	seqY[cur_pos+2] = 4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	
	ali->charY[cur_pos]   = '.';
	ali->charY[cur_pos+1] = '.';
	ali->charY[cur_pos+2] = '.';

	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

      case stC40:
	ct = 0;
	while (ct < 4) {
	  if (!isitagap(seq[seq_pos])) s[ct++] = seq[seq_pos];
	  if(++seq_pos > len_seq) break;
	}
	
	x1 = s[0];
	x2 = s[1];
	x3 = s[2];
	x4 = s[3];

	seqX[cur_pos]   = x1;
	seqX[cur_pos+1] = x2;
	seqX[cur_pos+2] = x3;
	seqX[cur_pos+3] = x4;
	
	seqY[cur_pos]   = 4;
	seqY[cur_pos+1] = 4;
	seqY[cur_pos+2] = 4;
	seqY[cur_pos+3] = 4;
	
	ali->charX[cur_pos]   = DNAAlphabet[x1];
	ali->charX[cur_pos+1] = DNAAlphabet[x2];
	ali->charX[cur_pos+2] = DNAAlphabet[x3];
	ali->charX[cur_pos+3] = DNAAlphabet[x4];
	
	ali->charY[cur_pos]   = '.';
	ali->charY[cur_pos+1] = '.';
	ali->charY[cur_pos+2] = '.';
	ali->charY[cur_pos+3] = '.';

	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

       case stC20:
	nxt_st = stCe;
	break;

       case stC03:
	i = DLog2Choose(pcod30, 64);

	cur_y = i;

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	
	seqX[cur_pos]   = 4;
	seqX[cur_pos+1] = 4;
	seqX[cur_pos+2] = 4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	
	ali->charX[cur_pos]   = '.';
	ali->charX[cur_pos+1] = '.';
	ali->charX[cur_pos+2] = '.';

	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	
	nxt_pos = cur_pos + 3;
	nxt_st = stCe;
	break;

       case stC04:
	i = DLog2Choose(pcod04, 256);

	cur_y = i/4;

	y1 = cur_y/16;
	y2 = (cur_y%16)/4;
	y3 = cur_y%4;
	y4 = i%4;
	
	seqX[cur_pos]   = 4;
	seqX[cur_pos+1] = 4;
	seqX[cur_pos+2] = 4;
	seqX[cur_pos+3] = 4;
	
	seqY[cur_pos]   = y1;
	seqY[cur_pos+1] = y2;
	seqY[cur_pos+2] = y3;
	seqY[cur_pos+3] = y4;
	
	ali->charX[cur_pos]   = '.';
	ali->charX[cur_pos+1] = '.';
	ali->charX[cur_pos+2] = '.';
	ali->charX[cur_pos+3] = '.';

	ali->charY[cur_pos]   = DNAAlphabet[y1];
	ali->charY[cur_pos+1] = DNAAlphabet[y2];
	ali->charY[cur_pos+2] = DNAAlphabet[y3];
	ali->charY[cur_pos+3] = DNAAlphabet[y4];
	
	nxt_pos = cur_pos + 4;
	nxt_st = stCe;
	break;

       case stC02:
	nxt_st = stCe;
	break;

       default:
	Die("invalid state (%d) in SimulateCODSequence()", cur_st);
      }

      if (traceback) fprintf(ofp,"emitting %s (%d)\n", cstNAME[cur_st], cur_pos);
      
      if (seq_pos < len_seq && nxt_st != -1) {
	if (traceback) fprintf(ofp," %s->%s \n", cstNAME[cur_st], cstNAME[nxt_st]);
	PushTracerstack(dolist, AttachTracer(cur_tr, nxt_pos+start, nxt_st));
      }
    }
  
  if (alignment) PrintNewAlign(ofp, sqinfoX, sqinfoY, start, cur_pos, ali, string_name);

  *L = nxt_pos;
  
  *ret_first_pos  = first_pos/len_cod33;
  *ret_second_pos = second_pos/len_cod33;
  *ret_third_pos  = third_pos/len_cod33;

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

int
is_first_pos (int codX, int codY)
{
  int x1, y1;
  int itis = FALSE;

  x1 = codX/16;
  y1 = codY/16;
  
  if (x1 == y1) ;
  else itis = TRUE;

  return itis;
}

int
is_second_pos (int codX, int codY)
{
  int x2, y2;
  int itis = FALSE;

  x2 = (codX%16)/4;
  y2 = (codY%16)/4;
  
  if (x2 == y2) ;
  else itis = TRUE;
  
  return itis;
}

int
is_third_pos (int codX, int codY)
{
  int x3, y3;
  int itis = FALSE;

  x3 = codX%4;
  y3 = codY%4;
 
  if (x3 == y3) ;
  else itis = TRUE;

  return itis;
}

/* Function:  pass_thru_COJ()
 * Date:      ER, Sat Feb  5 14:13:23 CST 2000 [St. Louis]
 *
 * Purpose:  Goes through the COJ model maybe generating a few positions.
 *           Very similar to SimulateOTHSequence() apart from the fact that
 *           it exits the model when convenient but emits at least 1 nts.
 *
 * Args:     s1, s2    - the sequences 
 *           L         - length of aligmnemt
 *           othmodel - the null model
 *
 * Returns:  (void)
 */
void
pass_thru_COJ(FILE *ofp, int L, int *ret_len, SQINFO *sqinfoX, int **ret_seqX, SQINFO *sqinfoY, int **ret_seqY, int start, 
	      int *ret_nxt_pos, struct othmodel_s *oth, char **ret_charX, char **ret_charY, 
	      int traceback, int alignment, char *string_name)
{
  struct tracer_s      *tr;      /* the traceback tree under construction  */
  struct tracer_s      *cur_tr;  /* ptr to node of tr we're working on     */
  struct tracerstack_s *dolist;  /* pushdown stack of active tr nodes      */

  int  *mseqX;
  int  *mseqY;
  char *mcharX;
  char *mcharY;

  int    cur_x,   cur_y;           /* nucleotides at those positions         */
  int    cur_st,  nxt_st;
  int    cur_pos, nxt_pos;
  int    len;
  int    t, x;

  len = *ret_len;

  mseqX  = *ret_seqX;
  mseqY  = *ret_seqY;
  mcharX = *ret_charX;
  mcharY = *ret_charY;

  /* Initialize
   * Start at pos "0" with state "stB"
   */
  tr     = InitTracer();       /* start a trace tree */
  dolist = InitTracerstack();  /* start a stack for traversing the trace tree */
  
  cur_tr = AttachTracer(tr, start, stFL); 
  PushTracerstack(dolist, cur_tr);

  while ((cur_tr = PopTracerstack(dolist)) != NULL)
    {
      cur_st = cur_tr->type;
      
      t = DENChoose(oth->t + IdxTransOTH[cur_st], TransPerOTHState[cur_st]);

      nxt_st = ToStateOTH[t + IdxTransOTH[cur_st]];
      if (nxt_st >= OSTATES) Die ("invalid transition in SimulateOTHAlign()");

      cur_pos = cur_tr->emit;

      if (cur_pos >= len-1) {   
	if (FALSE) Warn ("pass_thru_COJ(): reallocate %d --> %d", len, len+L+4);
	realloc_strings(4+L, &len, cur_pos, &mseqX, &mseqY, &mcharX, &mcharY); 
      }

      switch (cur_st){
      case stFL:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

       case stFR:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stFJ:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stB:
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      case stM:
	x = DLog2Choose(oth->mem, 16);

	cur_x = x/4;
	cur_y = x%4;
	nxt_pos = cur_pos + 1;
	mcharX[cur_pos] = DNAAlphabet[cur_x];
	mcharY[cur_pos] = DNAAlphabet[cur_y];
	break;

      case stX: 
	cur_x = DLog2Choose(oth->xem, 4);
	cur_y = 4;
	nxt_pos = cur_pos + 1;
	mcharX[cur_pos] = DNAAlphabet[cur_x];
	mcharY[cur_pos] = '.';
	break;

      case stY: 
	cur_x = 4;
	cur_y = DLog2Choose(oth->yem, 4);
	nxt_pos = cur_pos + 1;
	mcharX[cur_pos] = '.';
	mcharY[cur_pos] = DNAAlphabet[cur_y];
	break;

      case stE: 
	cur_x = -1;
	cur_y = -1;
	nxt_pos = cur_pos;
	break;

      default:
	Die("invalid state in pass_thru_COJ()");
      }

      if (traceback && nxt_pos > cur_pos) fprintf(ofp,"(COJ) emitting %s (%d) [%d %d] \n", ostNAME[cur_st], cur_pos, cur_x, cur_y);
      
      mseqX[cur_pos] = cur_x;
      mseqY[cur_pos] = cur_y;
      
      if (cur_st != stFR) {
	if (traceback && nxt_pos > cur_pos) fprintf(ofp," %s->%s \n", ostNAME[cur_st], ostNAME[nxt_st]);
	PushTracerstack(dolist, AttachTracer(cur_tr, nxt_pos, nxt_st));
      }
    }
  
  if (alignment) PrintNewCharSeqs(ofp, sqinfoX, sqinfoY, start, nxt_pos-start+1, mcharX, mcharY, string_name);
  
  *ret_len = len;
  *ret_nxt_pos = nxt_pos;

  *ret_seqX  = mseqX;
  *ret_seqY  = mseqY; 
  *ret_charX = mcharX;
  *ret_charY = mcharY;
  
  FreeTracer(tr);
  FreeTracer(cur_tr);
  FreeTracerstack(dolist);
}


void
realloc_strings(int L, int *ret_len, int k, int **ret_seqX, int **ret_seqY, char **ret_charX, char **ret_charY)
{    
  int  *mseqX;
  int  *mseqY;
  char *mcharX;
  char *mcharY;
  int len;
  int new_len;
  int x;

  len = *ret_len;

  mseqX  = *ret_seqX;
  mseqY  = *ret_seqY;
  mcharX = *ret_charX;
  mcharY = *ret_charY;
  
  if (L == 0) {  
    *ret_seqX  = mseqX;
    *ret_seqY  = mseqY; 
    *ret_charX = mcharX;
    *ret_charY = mcharY;
    
    *ret_len = len;
    
    return;
  }

  new_len = len + L;

  mseqX  = (int  *) ReallocOrDie(mseqX,  sizeof(int ) * new_len);
  mseqY  = (int  *) ReallocOrDie(mseqY,  sizeof(int ) * new_len);
  mcharX = (char *) ReallocOrDie(mcharX, sizeof(char) * new_len);
  mcharY = (char *) ReallocOrDie(mcharY, sizeof(char) * new_len);
  
  for (x = len-1; x >= k+1; x--) 
    {
      mseqX[x+L] = mseqX[x]; 
      mseqY[x+L] = mseqY[x]; 
      
      mcharX[x+L] = mcharX[x];
      mcharY[x+L] = mcharY[x];
    }

  for (x = k+1; x <= k+L; x++) 
    {
      mseqX[x] = -1;
      mseqY[x] = -1;
           
      mcharX[x] = '.';
      mcharY[x] = '.';
    }
  
  *ret_seqX  = mseqX;
  *ret_seqY  = mseqY; 
  *ret_charX = mcharX;
  *ret_charY = mcharY;
  
  *ret_len = new_len;
}

