/*  Copyright (c) 1999 by Mihaela Pertea, Arthur Delcher, Steven Salzberg, Simon
    Kasif, and Owen White.
 *
 *    Version: 1.00 01 Jun 98
 *    Version: 1.01 03 Jun 98
 *             Removed unused variables and parameters
 *             Improved formatted output
 *    Version: 1.10 26 Jun 98
 *             Improved modularity
 *             Removed debug options 
 *             More precise function names and comments
 *
 * This program reads (from stdin), the file generated by 'extract', 
 * which consists of a label followed by whitespace
 * then the open reading frame (orf) followed by a newline.
 * The program then builds a Selective Markov Model using the following
 * idea. For each OFFSET of each orf, it determines the MAX_DEPTH
 * positions containing the greatest amount of mutual information 
 * after restricting certain bases to a particular position.
 * After determining where the maximum mutual information position
 * is located, the node is printed to stdout (e.g. ___|___|*t_
 * represents, using an INTERVAL of 9, that the next greatest mutual
 * information position (denoted by '*') is located in position 6, 
 * given base t is located in position 7 of the window.  Printed next
 * are the counts and probabilities that position 8 is an a,c,t or g.
 *
 */

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


#define VERSION 1.10          /* the current version of this program                 */
#define INTERVAL  12          /* the length of the "sliding window"                  */
#define MAX_DEPTH 7           /* the max number of bases to restrict in the window   */
#define CHI2_ENTRIES 7
#define MAX_ORF_LENGTH 3000000  /* ensure that this is big eanough, under linux inf loop */
#define  EPSILON  1e-4        /* smaller than this is zero for mutual info           */

/* in order to use the mi position on the left, it must be WEIGHT % larger than any
   of the mi numbers to the right */
#define WEIGHT .03      


int NODE_COUNT;      /* the number of nodes completely computed in *NODE          */ 
                     /* if ever more than MAXINT, we have issues!                 */


const int OFFSET    = 1;   /* number of positions to offset the beg of each orf   */
                           /* starting at 0, min = 1                              */
const int SAMPLE_SIZE_BOUND = 400;  /* If the counts for the bases sums to less   */
/* than this value, use chi^2 formula to weight the probability                   */
const int SAMPLE_WEIGHT = 400;






/* the 16 possible base pair sequences */
enum bp {aa, ac, ag, at, ca, cc, cg, ct, ga, gc, gg, gt, ta, tc, tg, tt};

/* the 4 possible bases */
enum base {a, c, g, t};


#include "build-icm.h"

tOrf  *ORFS;         /* pointer to the struct containg the labels and orfs        */
tNode **NODE;        /* an array of tNodes, for each offset, to build a tree model*/



#include "misc.h"

/* iff T print model as a binary file */
/* else  print model as a text   file */ 
int PRINT_BINARY = TRUE;



int main (int argc, char **argv)
{
  int pow4[MAX_DEPTH+1] = {0}; // pow4[x] = 4^x. 
  int total_nodes = 0;  // total number of nodes needed to build tree model
  int orf_cnt = 0;      // number of orfs currently read
  int i, os;            // loop counters
  int max_pos; 
  enum bp bpp;         
  int sum = 0; // the sum of the base pair counts in NODE[].count[][aa...tt]
  double mut_info[INTERVAL] = {0};  // mut_info [x] = the mutual infomation of NODE[].[x][aa...tt]
  char print_string[INTERVAL+1];      // used for formatted output

  int tmp[2];
  double best;

  process_options (argc, argv);

  ORFS = NULL;

  /* init print string: _ is printed indicating no restriction */
  for (i = 0; i < INTERVAL; i++)
    print_string[i] = '_';
  print_string[i] = '\0';
  
  compute_power4 (pow4);   // compute all powers of 4 from 4^0 to 4^MAX_DEPTH

  // Since we know how many NODES we need, based on MAX_DEPTH, 
  // let's see if we can allocate all the memory we need now
  for (i = 0; i <= MAX_DEPTH; i++)
    total_nodes += pow4[i];

  if (PRINT_BINARY == TRUE)
  {
    printf (">binary file produced by %s version %.2f using window interval %d and depth %d\n", 
            *argv, VERSION, INTERVAL, MAX_DEPTH);
    // program that reads in the model depends on this !!

    tmp[0]=total_nodes;
    tmp[1]=MAX_DEPTH;
    fwrite ( tmp, sizeof (int), 2, stdout);
  }
  else
    fprintf (stdout, "%d %d\n", total_nodes, MAX_DEPTH);

  NODE = (tNode **) check_malloc ( (sizeof (tNode *)) * OFFSET );
  for (i = 0; i < OFFSET; i++)
    NODE[i] = (tNode *) check_calloc (total_nodes, sizeof (tNode)); 
 /* read in orfs, each has a label followed by whitespace 
    then the codons and ends with a newline */

  ORFS = read_orfs (stdin, &orf_cnt);

  /* find restrictions for each OFFSET in each orf */
  for (os = 0; os < OFFSET; os++)
  {
    int ending_sum[4] = {0};  //ending_sum[x] is the sum of the counts that end in the base x
    NODE_COUNT = 0;
    sum = 0;

    for (i = 0; i < orf_cnt; i++)
    {
      if (os != 0)
        ORFS[i].seq++;   //offset the orf
      count_base_pairs (NODE[os][NODE_COUNT].count, ORFS[i].seq, INTERVAL);
    }
    // assuming each sum of cnt[*][aa...tt] is the same.  YES THEY ARE
    for (bpp = aa; bpp <= tt; bpp++)
      sum += NODE[os][0].count[0][bpp];

    // calculate the mutual information for each interval, finding the max
    max_pos = 0;
    best = mut_info[0] = get_mut_info (NODE[os][NODE_COUNT].count[0], sum);

    for (i = 1; i < INTERVAL-1; i++)
    {
      mut_info[i] = get_mut_info (NODE[os][NODE_COUNT].count[i], sum);

      if (mut_info[i] >= best)
      {
	best = mut_info[i];
	max_pos = i;
	continue;
      }

      if ( mut_info[i] >= (best / (1+WEIGHT)) )  // small counts sometimes have same mut info
        max_pos = i;
    }

    NODE[os][0].mut_info_pos = max_pos;
    NODE_COUNT++;

    /* compute the probability based on the counts */
    for (bpp = aa; bpp <= tt; bpp++)
      ending_sum[bpp%4] += NODE [os] [0] . count [max_pos] [bpp];
    for (i = 0; i < 4; i++)
      NODE[os][0].prob[i] = (float) ending_sum[i] / (float) sum;
    print_node (print_string, 0, 0, mut_info [NODE[os][0].mut_info_pos], os, ending_sum);

    build_model (orf_cnt, os, pow4);
  }
  return 0;
}
/*********************************************************
 * build_model
 *********************************************************
 * PURPOSE: Starting with the root, restrict each base, a c t or g,
 * to the parent's mutual information position.  Then count 
 * how many times this sequence occurs with the bases in restricted 
 * positions and calculate the mutual information. Keep doing
 * this for every node until MAX_DEPTH is reached.
 * INPUT: the number of orfs, orf_cnt. The current offset 
 * of the orfs that we are building on, the array of 
 * precomputed powers of 4.
 * OUTPUT: none
 *********************************************************/
void 
build_model (int orf_cnt, int offset, int pow4[])
{
  int  level, to_build, i, max_pos;
  double best;
  enum base b;   // loop var for bases a thru t
  enum bp bpp;   // loop var for base pairs aa,ac,...,tg,tt
  int  sum = 0;
  char print_string[INTERVAL+1];

  for (i = 0; i < INTERVAL; i++)
    print_string[i] = '_';
  print_string[i] = '\0';

  for (level = 1; level <= MAX_DEPTH; level++)
  {
    /* for each orf, determine what is in our window and update
       the appropriate nodes count on this level */
    for (i = 0; i < orf_cnt; i++)
      count_base_pairs_with_restrictions (ORFS[i].seq, level, offset);
    for (b = a, to_build = NODE_COUNT; to_build < NODE_COUNT+pow4[level]; to_build++, b++)
    {
      int ending_sum[4] = {0};  //ending_sum[x] is the sum of the counts that end in the base x
      double mut_info[INTERVAL] = {0};
      sum = 0;

      NODE[offset][to_build].mut_info_seq = b;
      if (b == t) b = a-1; //reset base

      if ( NODE [offset] [PARENT(to_build)] . mut_info_pos == -1 )  //CANNOT OBTAIN ANY MORE INFO
      {
        NODE [offset] [to_build] . mut_info_pos = -1;
        continue;
      }
      for (bpp = aa; bpp <= tt; bpp++)  // all sums on to_build's count positions are the same.
        sum += NODE [offset] [to_build] . count [0] [bpp];

      // calculate the mutual information for each interval, finding the max
      max_pos = 0;
      best  = mut_info[0] = get_mut_info (NODE[offset][to_build].count[0], sum);
      for (i = 1; i < INTERVAL-1; i++)
      {
	mut_info[i] = get_mut_info (NODE[offset][to_build].count[i], sum);
	if (mut_info[i] >= best)
	{
	  best = mut_info[i];
	  max_pos = i;
	  continue;
	}

	if ( mut_info[i] >= (best / (1+WEIGHT)) )  // small counts sometimes have same mut info
	  max_pos = i;
      }
      NODE [offset] [to_build] . mut_info_pos = max_pos;
      if ( mut_info [max_pos] <= EPSILON ) // No information gained
      {
        NODE [offset] [to_build] . mut_info_pos = -1;  // flag it
        continue;
      }
      /* compute the probability based on the counts */
      for (bpp = aa; bpp <= tt; bpp++)
        ending_sum[bpp%4] += NODE [offset] [to_build] . count [max_pos] [bpp];
      chi2_test (to_build, PARENT(to_build), ending_sum, offset);
      print_node (print_string, level, to_build, mut_info[max_pos],offset, ending_sum);
    }
    NODE_COUNT+=pow4[level];
  }
}





/************************************************************
 * calc_prob
 ************************************************************
 * Purpose: Calculate the starting and ending probabilites, 
 * start_prob and end_prob respectively. NOTE: start_prob[x] 
 * is the probability that the base pair starts with x. end_prob[y]
 * is the probability that the base pair ends with y.
 * INPUT: an array of 16 counts (aa, ac, ... tt), their sum, 
 * and the 2 probability arrays.
 * OUPUT: none (start_prob and end_prob are updated)
 ************************************************************/
void 
calc_prob (int *bp_cnt, int sum, double start_prob[], double end_prob[])
{
  enum bp bpp;
  int i;

  for (i = 0, bpp = aa; bpp <= tt; bpp++)
  {
    start_prob[i]+= bp_cnt[bpp];
    if (bpp == at || bpp == ct || bpp == gt || bpp == tt)
    {  
      start_prob[i]/= (double) sum;
      i++;
    }
    end_prob[bpp%4] += bp_cnt[bpp];
    if (bpp == ta || bpp == tc || bpp == tg || bpp == tt)
      end_prob[bpp%4] /= (double) sum;
  }
}



/******************************************************
 * chi2_test
 ******************************************************
 * PURPOSE: Compute the probability at node_num, where prob[x]
 *  is the probability that base x occurs in position INTERVAL-1.
 *  If the sums of the counts are less than SAMPLE_SIZE_BOUND
 *  there is not eanough information to trust the probabilities.  Thus
 *  we use a weighted average, computed by chi squared, to represent the 
 *  probabilities at node_num.
 * INPUT: node_num, the number of the node we are computing the 
 *  probabilities for. parent, the parent of node_num. ending_sum[x],
 *  which is the number of times the base x occured in position INTERVAL.
 *  offset, the current offset of the orfs that we are building on.
 * OUTPUT: TRUE is returned iff we used the chi^2 formula, otherwise false.
 *  The probabilities at node_num are also computed.
 ********************************************************/
int
chi2_test (int node_num, int parent, int ending_sum[], int offset)
{

  float Chi2_Val [CHI2_ENTRIES] = {2.37, 4.11, 6.25, 7.81, 9.35, 11.3, 12.8};
  float Significance [CHI2_ENTRIES] = {0.50, 0.75, 0.90, 0.95, 0.975, 0.99, 0.995};
  enum base b;
  float expected, Chi2, lambda, total_sum=0;
  int i;

  for (b = a; b <= t; b++)
    total_sum += ending_sum[b];

  if (total_sum >= SAMPLE_SIZE_BOUND)
  {
    //compute the probabilities
    for (b = a; b <= t; b++)
      NODE[offset][node_num].prob[b] = (ending_sum[b] + NODE [offset][parent].prob[b]) / (total_sum + 1.0);
    return FALSE;  // Bound is high eanough; we don't need to weight the probabilities
  }


  for (Chi2 = 0.0, b = a; b <= t; b++)
  {
    expected = (float) total_sum * NODE [offset][parent].prob[b];
    if (expected > 0.0)
      Chi2 += pow ((double) ending_sum[b] - expected, 2.0) / expected;
  }
  
  for (i = 0; i < CHI2_ENTRIES && Chi2_Val[i] < Chi2; i++)
    ;

  if (i == 0)
    lambda = 0.0;
  else if (i == CHI2_ENTRIES)
    lambda = 1.0;
  else 
    lambda = Significance [i-1] + ( (Chi2-Chi2_Val [i-1]) / (Chi2_Val [i] - Chi2_Val [i-1]) ) 
             * (Significance [i] - Significance [i-1]);

  lambda *= (float) total_sum / SAMPLE_WEIGHT;
  if (lambda > 1.0)
    lambda = 1.0;

  for (b = a; b <= t; b++)
    NODE [offset][node_num].prob[b] = lambda * ( (ending_sum[b] + NODE [offset][parent].prob[b]) / (total_sum + 1.0))
      + (1.0 - lambda) * NODE [offset][parent].prob[b];

  return TRUE;
}





/************************************************
 * compute_power4
 ***********************************************
 * Purpose: compute powers of 4 from 4^0 to 
 * 4^MAX_DEPTH are store it in the array 
 * pow4.  Thus, for efficiency, powers of 4 are 
 * computed once and referenced several times.
 * INPUT:  the array power to store the results.
 * OUTPIT: none (powers of 4 stored in pow4 as a result)
 *************************************************/
void
compute_power4 (int power[])
{
  int i;

  for (i = 0; i <= MAX_DEPTH; i++)
    power [i] = (int) pow (4, i);
}




/**********************************************************
 * count_base_pairs
 **********************************************************
 * PURPOSE: count the number of times a base pair (aa, ac, ..., tt)
 * occurs in each orf, with an interval distance v, but only in every
 * third position.  You can imagine there is a window of length v that
 * slides down each orf. If v = 12, Initially the window is over 
 * positions 0-11, then it moves to 3-14, 6-17, ... until it reaches 
 * the end of the orf. For each of these window positions we add 
 * one to the appropriate counter depending what letter is in window 
 * position 0 and window position 11, 1 & 11, 2 & 11, ... 10 & 11  
 * INPUT: the base pair count array, bp_cnt, the orf sequence, orf
 * and the window interval, v.
 * OUTPUT: none.  (bp_cnt is updated)
 *************************************************************/
void count_base_pairs (int bp_cnt[][16], char *orf, int v)
{
  int start = 0, stop = v-1, end = strlen (orf);
  int pos = 0;

  for ( ; stop < end; start+=1, stop+=1)
    for (pos = 0; pos < v-1; pos++)
    {
      switch (orf[pos+start])
      {
       case 'a': case 'A': 
          inc_base_pairs (aa, bp_cnt[pos], orf[stop]);
          break;
       case 'c': case 'C':
          inc_base_pairs (ca, bp_cnt[pos], orf[stop]);
          break;
       case 'g': case 'G':
          inc_base_pairs (ga, bp_cnt[pos], orf[stop]);
          break;
       case 't' : case 'T':
          inc_base_pairs (ta, bp_cnt[pos], orf[stop]);
          break;
       default: 
          fprintf (stderr, "Error in switch in count_base_pairs: orf[%d+%d]=%c\n", pos, start, orf[pos+start]);
          exit (-1);
      }
    }
}




/**********************************************************
 * count_base_pairs_with_restrictions
 **********************************************************
 * PURPOSE: Update any node's count determined by the base 
 * restrictions.  For example, starting at the root, we look
 * at the character in the orf determined by the root's m.i. position.
 * We keep descending the tree until there are no more restrictions 
 * and then update the appropriate node's count that matches 
 * the restriction.
 * INPUT: the pointer to an orf and the level of the tree we 
 * are currently building.
 * OUTPUT: none (A NODE's count is updated)
 **********************************************************/
void
count_base_pairs_with_restrictions (char *orf, int level, int offset)
{
  int start = 0, stop = INTERVAL-1, end = strlen(orf);
  int k, i, node_num;

  for ( ; stop < end; start+=1, stop+=1)
  {
    k = NODE [offset][0].mut_info_pos;  // start at root
    for (node_num = 0, i = 0; i < level && k != -1; i++, k = NODE [offset][node_num].mut_info_pos)
    {
      switch (orf[k + start])
      {
          case 'a': case 'A':
            node_num = (node_num * 4) + 1;
            break;
          case 'c': case 'C':
            node_num = (node_num * 4) + 2;
            break;
          case 'g': case 'G':
            node_num = (node_num * 4) + 3;
            break;
          case 't': case 'T':
            node_num = (node_num * 4) + 4;
            break;
          default:
            fprintf (stderr, "switch error in count_base_pairs_with_restrictions: ");
            fprintf (stderr, "orf[%d+%d]=%c.\n", k, start, orf[k+start]);
            exit (-1);
      }
    }
    
    // if k == -1, it has already been determined that this window 
    // does not provide any more new info, so don't bother counting it again!
    if (k != -1)  
      count_window (NODE [offset][node_num].count, start, orf);
  }
}




/*************************************************************
 * count_window
 *************************************************************
 * PURPOSE: Count the window frame of length INTERVAL
 * INPUT: The base counts, count, the start position,start, 
 * that determines where to start counting in the orf.
 * OUTPUT: none (counts are updated)
 ************************************************************/  
void
count_window (int count[][16], int start, char *orf)
{
  int pos, i;

  for ( pos = 0, i = start; i < start+INTERVAL-1; i++, pos++)
  {
    switch (orf[i])
    {
    case 'a': case 'A':
      inc_base_pairs (aa, count[pos], orf[start+INTERVAL-1]);
      break;
    case 'c': case 'C':
      inc_base_pairs (ca, count[pos], orf[start+INTERVAL-1]);
      break;
    case 'g': case 'G':
      inc_base_pairs (ga, count[pos], orf[start+INTERVAL-1]);
      break;
    case 't': case 'T':
      inc_base_pairs (ta, count[pos], orf[start+INTERVAL-1]);
      break;
    default:
      fprintf (stderr, "switch error in count window: orf[%d]=%c.\n", i, orf[i]);
      exit (-1);
    }
  }

}




/************************************************
 * get_mut_info
 ************************************************
 * PURPOSE: Calculate the mutual information
 * given 16 counts (aa, ac, ... tt) and their sum
 * INPUT: an array of 16 counts and their sum
 * OUTPUT: their mutual information number
 ************************************************/
double 
get_mut_info (int *bp_cnt, int sum)
{
  enum bp bpp; int i;
  double mut_info = 0, start_prob[4] = {0}, end_prob[4] = {0};
  // start_prob[x] is the probability that the base pair starts with x
  // end_prob[y] is the probability that the base pair ends with y

  if (sum == 0)
    return 0.0;

  // Calculate start_prob and end_prob
  calc_prob (bp_cnt, sum, start_prob, end_prob);
    
  for (i = 0, bpp = aa; bpp <= tt; bpp++)
  {
    double prob;  //prob = the probability that bpp occurs
    prob = (double) bp_cnt[bpp] / sum;

    if (bpp == ca || bpp == ga || bpp == ta) i++;  

    // Log (0) possib
    if (prob == 0 || start_prob[i] == 0 || end_prob[bpp%4] == 0)
       ; //IGNORE
    else
      mut_info = mut_info + ( prob * log ( (prob / (start_prob[i]*end_prob[bpp%4]) ) ) );
  }

  return mut_info;
}



/**********************************************************
 * inc_base_pairs
 **********************************************************
 * PURPOSE: increment the appropriate base_pair counter, bp_cnt. 
 * INPUT: a base pair, bp, that ends with base a. (NOTE that 
 * bp is only aa, ca, ga, or ta). Thus the stop character 
 * is simply added to bp. The base pair counts array, bp_cnt.
 * The character at the end of our "window", stop.
 * OUTPUT: none (bp_cnt is updated)
 **********************************************************/
void 
inc_base_pairs (int bp, int *bp_cnt, char stop)
{
  switch (stop)
  {
  case 'a': case 'A': 
    bp_cnt[bp]++;
    return;
  case 'c': case 'C': 
    bp_cnt [bp+1]++;
    return;
  case 'g': case 'G':
    bp_cnt [bp+2]++;
    return;
  case 't': case 'T':
    bp_cnt [bp+3]++;
    return;
  default:
    fprintf (stderr, "switch error in inc_base_pairs: %c.\n", stop);
    exit (-1);
  }
}


/************************************************
 * print_node
 ************************************************
 * PURPOSE: print a well formatted output. The print
 * string is printed with a '*' in the position which 
 * has the greatest mut info number. The restricted 
 * bases (if any) are also printed in their restricted 
 * positions.
 * INPUT: the print_string, the current level that the node
 * being printed is on, the node to be printed, the mutual 
 * information number for this node, offset: this node's 
 * current offset, ending_sum[x]: sum of the counts that 
 * end in base x 
 * OUTPUT: none
 ************************************************/ 
void
print_node (char print_string [], int level, int start_node, double mut_info, 
            int offset, int ending_sum[])
{
  char copy[INTERVAL+1], mi_symbol = '*', seperator = '|';
  int i, start = start_node, par; 
  char base_name[] = {'a', 'c', 'g', 't'};


  if (PRINT_BINARY == FALSE)
  {
    strcpy (copy, print_string);
    copy [NODE [offset] [start_node] . mut_info_pos] = mi_symbol;

    // format the string to print 
    for (i = 0; i < level; i++)
    {
      par = PARENT(start_node);
      copy [NODE [offset] [par] . mut_info_pos] = 
        base_name [NODE [offset] [start_node] . mut_info_seq];
      start_node = par;
    }
    printf ("%d\t", start);

    // print the string with its restrictions (if any)
    for (i = 0; i < INTERVAL; i++)
    {
      if (i % 3 == 0 && i != 0)
        printf ("%c", seperator);
      printf ("%c", copy[i]);
    }
  
    printf ("\t%.4f", mut_info);

    // print the counts and prob that in position INTERVAL is an a,c,g or t 
    for (i = 0; i < 4; i++)
      printf ("\t%d\t(%1.2f)", ending_sum[i], NODE[offset][start].prob[i]);
    printf ("\t%d\n", NODE[offset][start].mut_info_pos);
  }
  else
  {
    fwrite (&start, sizeof (int), 1, stdout);
    fwrite (NODE[offset][start].prob, sizeof (float), 4, stdout);
    fwrite (&(NODE[offset][start].mut_info_pos), sizeof (int), 1,  stdout);
  }

}


/*  Process command-line options and set corresponding global switches
 *  and parameters.
 *
 * -b print binary output
 * -h print list of options
 * -t print text output
 */
void
process_options (int argc, char **argv)
{
  int i;

  for (i = 1; i < argc; i++)
  {
    switch (argv [i][0])
    {
      case '-':
        switch (argv[i][1])
        {
           case 'b':   // print model as a binary file
             PRINT_BINARY = TRUE;
             break;
           case 'h':   //list all avail options
             fprintf (stderr, 
"usage:\
\t%s [-options ...]\
\t{reads traing seq from stdin}\ 
\t{print model to stdout}\n\n", *argv);
             fprintf (stderr, 
"where options include:\
\t-b\tprint the model as a binary file (default)\
\t-h\tprint this help message\
\t-t\tprint the model as a text file\n");
             exit (0);
             break;
           case 't':   // print model as a text file
             PRINT_BINARY = FALSE;
             break;
           default:
             fprintf (stderr, "%s:  bad command option \"%s\"\n", *argv, argv[i]);
             fprintf (stderr, "type %s -h for a list of available options\n", *argv);
             exit (0);
        }
        break;

      default:
        fprintf (stderr,  "%s:  bad command option \"%s\"\n", *argv, argv[i]);
        fprintf (stderr, "type %s -h for a list of available options\n", *argv);
        exit (0);
     }
  }
}



/*********************************************************
 * read_orfs
 *********************************************************
 * PURPOSE: read in the DNA sequence file containing the orf
 *   label followed by whitespace, then the orf, followed by
 *   a newline.
 * INPUT: the OPEN file ptr associated with the sequence file.
 *   a pointer cotaining the # of orfs counted
 * OUTPUT: a pointer to the orf labels and sequences read in.
 *   Also, orf_count is updated.
 *********************************************************/ 
tOrf * 
read_orfs (FILE* Fptr, int *orf_count)
{
  tOrf *orfPtr = NULL;
  char line [MAX_ORF_LENGTH];
  int on_label = TRUE;
  int  i, Len;

  *orf_count = 0;
  while (!feof (Fptr))
  {
    fscanf (Fptr, "%s", line);
    if (on_label) //iff line contains the label
    {
      orfPtr = (tOrf *) check_realloc (orfPtr, sizeof (tOrf) * ((*orf_count) + 1)); 
      orfPtr[*orf_count].label = (char *) check_malloc (sizeof (char) * (strlen (line)+1));
      strcpy (orfPtr[*orf_count].label, line);
      on_label = FALSE;
    }
    else  //line contains the orf
    {
      Len = strlen (line);
      for  (i = 0;  i < Len;  i ++)
        line [i] = Filter (line [i]);
      orfPtr[*orf_count].seq = (char *) check_malloc (sizeof (char) * (Len + 1) );
      strcpy (orfPtr[*orf_count].seq, line);
      on_label = TRUE;
      (*orf_count)++;
    }
  }
  
  return orfPtr;
}



char  Filter  (char Ch)

//  Return a single  a, c, g or t  for  Ch .

  {
   switch  (tolower (Ch))
     {
      case  'a' :
      case  'c' :
      case  'g' :
      case  't' :
        return  Ch;
      case  'r' :     // a or g
        return  'g';
      case  'y' :     // c or t
        return  'c';
      case  's' :     // c or g
        return  'c';
      case  'w' :     // a or t
        return  't';
      case  'm' :     // a or c
        return  'c';
      case  'k' :     // g or t
        return  't';
      case  'b' :     // c, g or t
        return  'c';
      case  'd' :     // a, g or t
        return  'g';
      case  'h' :     // a, c or t
        return  'c';
      case  'v' :     // a, c or g
        return  'c';
      default :       // anything
        return  'c';
    }
  }



