/*>>>>>  pssmdist IPR001624.mats IPR001624.obsf default.amino.frq 80000 29085965*/
/* COPYRIGHT 2003 Fred Hutchinson Cancer Research Center */
/*>>>>> Problem: for TPs, if the observed frequencies in a column don't
	add up to EXACTLY 100, the total probability in compute() won't
	be 1.0 and there will be problems, eg PR00184G
	Should 1) Modify papssm so that they add to 100 after rounding,
	       2) Modify pssmdist to renormalize them to 100
*/
/*     pssmdist.c:  compute distribution of PSSM scores
	pssmdist <matrix file> <obs freq file> <freq file> SWISS SWISSAA <outname> <cum>
	matrix file:	precomputed integer PSSM from blpssm (EX)
	obs freq file:  observed frequency file from blpssm (OU)
		        The 20 main aa frequencies should add to exactly 1.000.
	frequency file: blimps format aa frequency file (20 main aa
			frequencies must add to exactly 1.0000)
	SWISS:		Number of sequences in typical database search
			EG  28164
	SWISSAA:	Number of amino acids in   "
			EG  9545427
        outname:	default is pssmdist
	cum:		word "cum" to write pssmdist.cum file
	Writes outname.dat & outname.cum files
---------------------------------------------------------------------------
 10/19/94 J. Henikoff 
 10/20/94 Changed to only count the 20 basic AAs plus gap for off ends,
	  but are counting alignments with gaps in other positions too!
          Added histogram
 10/21/94 Only count gaps off ends.
 10/25/94 Added some expected counts for a wide block vs swiss29
 10/26/94 Print the top scores out
	  Why don't the probs add to 1.0? They add to the 10 .frq values.
 10/31/94 Reads a .lis file to get the # of TPs, writes value of
          [0.005 x( SWISS - #TPs) + #TPs]th score to pssmdist.dat
	  Counts fragments as if they were TPs.
 11/1/94  Write [0.005 x SWISS] score; ignore #TPs.
 11/14/94 Compute weights for probabilities, including for ends.
 11/30/94 Input SWISS and SWISSAA
 12/ 7/94 Read observed frequencies & calculate TP distribution.
  4/ 6/95 Write tpmed and strength to pssmdist.dat file
  4/ 8/95 Count #TP scores above TN995 & write to pssmdist.dat file
  4/25/96 Renormalize observed frequencies in a column to add to 100,
	  fix_freqs(). Requires blimps-3.0.1 with MatType = double
	  instead of int to work properly.
 11/25/97 Increased MAXCOL from 60 to 200 for Peer Bork's groups
  1/16/99 Added pssmdist.cum output file = cumulative probs
  1/27/99 Make pssmdist.cum optional. Report 80%tile as well as 99.5%
  4/ 5/00 Write block width to pssmdist.dat
  8/27/03 Make fix_freqs() double instead of integer; mats and obsf input
	matrices can be integer or floating point
 11/11/03 Renamed load_frequencies() to load_freqs(), conflicts with
		frequency.c:load_frequencies() - should use that one!
 11/12/03 Renamed read_freq() to read_a_freq() - identical to
		frequency.c:read_freq() but that is defined static
  2/ 9/04 Increased MAXSCORE and quit if it is not big enough.
  2/ 9/04 Optionally use named file for pssmdist.dat
====================================================================*/

#define EXTERN
#define MAXNAME 80	/* Maximum file name length */
#define MAXSCORE 15000  /* Maximum score value = block width x max col */
#define HISTO 20	/* size of a score histogram bucket */
#define TOPN 300	/* report the top scores */
#define SWISS 28164	/* number of sequences in a search */
#define SWISSAA 9545427  /* number of AAs in all sequences searched */
#define TPS 100		/* number of TPs in a search */
#define AASALL 26
#define MAXCOL 200	/* Maximum block width */
#define MAXPROB 0.0005	/* Maximum cumulative prob. to write to pssmdist.cum */
/* 0- 1A 2R 3N 4D 5C 6Q 7E 8G 9H 10I 11L 12K 13M 14F 15P 16S 17T 18W 19Y
   20V 21B 22Z 23X 24* 25J,O,U  */

#include <blocksprogs.h>

/*
 * Local variables and data structures
 */

struct score_struct {
	double ways, prob;
} Scores[2][MAXSCORE];

int compute();
int load_freqs();
double read_a_freq();
void fix_freqs();
int count_tps();

double Search, SearchAA;	/* search size */
int TN995, TN800, TPmed;
double TPabove;
/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE *mfp, *ffp, *fdt, *fcum, *ofp;
  Matrix *matrix, *obs_freqs;
  char mname[MAXNAME], fname[MAXNAME], oname[MAXNAME], *ptr, ctemp[MAXNAME];
  char outname[MAXNAME];
  double freqs[AASALL];
  int strength;

/* ------------1st arg = matrix database -------------------------------*/
   if (argc > 1)
      strcpy(mname, argv[1]);
   else
   {
      printf("\nEnter name of matrix database: ");
      gets(mname);
   }
   if ( (mfp=fopen(mname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", mname);
      exit(-1);
   }
/* ------------2rd arg = observed freqs db -------------------------------*/
   if (argc > 2)
      strcpy(oname, argv[2]);
   else
   {
      printf("\nEnter name of observed frequencies database: ");
      gets(oname);
   }
   if ( (ofp=fopen(oname, "r")) == NULL)
   {
      printf("\nCannot open file %s, TP distribution not computed.\n", oname);
   }
/* ------------3rd arg = frequency file -------------------------------*/
   if (argc > 3)
      strcpy(fname, argv[3]);
   else
   {
      printf("\nEnter name of frequency file: ");
      gets(fname);
   }
   if ( (ffp=fopen(fname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", fname);
      exit(-1);
   }
   load_freqs(ffp, freqs);
   fclose(ffp);
/* ------------4th arg = #sequences in a search--------------------*/
   Search = SWISS;
   if (argc > 4)
      strcpy(ctemp, argv[4]);
   else
   {
      printf("\nEnter number of sequences in a typical search [%.0f]: ",
		 Search);
      gets(ctemp);
   }
   if (strlen(ctemp)) Search = atof(ctemp);
/* ------------5th arg = #aas in a search--------------------*/
   SearchAA = SWISSAA;
   if (argc > 5)
      strcpy(ctemp, argv[5]);
   else
   {
      printf("\nEnter number of amino acids in a typical database [%.0f]: ",
		SearchAA);
      gets(ctemp);
   }
   if (strlen(ctemp)) SearchAA = atof(ctemp);
   printf ("Assuming %.0f sequences, %.0f amino acids in a search\n",
		Search, SearchAA);
   printf("and 100 true positive alignments\n");
/* ------------6th arg = name of output file-------------------*/
   strcpy(outname, "pssmdist");
   if (argc > 6) strcpy(outname, argv[6]);
   sprintf(ctemp, "%s.dat", outname);
   if ( (fdt=fopen(ctemp, "a")) == NULL)
   {
      printf("\nCannot open file ctemp\n");
      fdt = NULL;
   }
/* ------------7th arg = cum---------------------------------*/
   fcum = NULL;
   if (argc > 7) strcpy(ctemp, argv[7]);
   if (ctemp[0] == 'c' || ctemp[0] == 'C')
   {
      sprintf(ctemp, "%s.cum", outname);
      if ( (fcum=fopen(ctemp, "a")) == NULL)
      {
         printf("\nCannot open file ctemp\n");
      }
   }

/*-----------------------------------------------------------------*/
  while ((matrix = read_a_matrix(mfp)) != NULL)
  {
	printf("\n======>%s\n", matrix->ac);
	if (ofp != NULL)
        { obs_freqs = read_a_matrix(ofp); fix_freqs(obs_freqs);  }
	else             obs_freqs = NULL;
        if (fcum != NULL) fprintf(fcum, ">%s\n", matrix->number);
	TN995 = compute(matrix, 0, freqs, obs_freqs, fcum);
	if (obs_freqs != NULL && strcmp(matrix->ma, obs_freqs->ma) == 0)
	    TPmed = compute(matrix, 1, freqs, obs_freqs, NULL);
        else TPmed = 0;
        if (fdt != NULL)
        {
	  ptr = strtok(matrix->ac, " ;");
	  if (TN995 > 0)
		 strength = (int) (1000.0 * (float) TPmed / (float) TN995);
/*
          fprintf(fdt, "%s %d %d %d %d %d\n",
                  ptr, TN995, TPmed, strength, TN800, matrix->width);
*/

          fprintf(fdt, "%s %d %d  %d %.4lf %d %d\n",
                  ptr, TN995, TPmed, strength, TPabove, TN800, matrix->width);

        }
        free_matrix(matrix);
	if (obs_freqs != NULL) free_matrix(obs_freqs);
  }
   
  fclose(mfp); fclose(ofp); fclose(fdt); 
  if (fcum != NULL) fclose(fcum);
  exit(0);

}  /* end of main */
/*=======================================================================
    ftype == 0 => use freqs (TN distribution)
    ftype == 1 => use obs_freqs (TP distribution)
>>>>maxcol, etc. are truncated to integers from matrix, s.b. rounded
========================================================================*/
int compute(matrix, ftype, freqs, obs_freqs, fcum)
Matrix *matrix, *obs_freqs;
int ftype;
double *freqs;
FILE *fcum;
{
  struct score_struct *last, *this, histo[MAXSCORE/HISTO], ends[MAXSCORE];
  struct score_struct middle[MAXSCORE];
  int col, aa, minvalue, maxvalue, minscore, maxscore, mincol, maxcol;
  int x, xreport, score, minfirst, minlast, pflag, itemp;
  double cum, aligns, report, report80, ftemp, dtemp, probwt[MAXCOL];
  double cumprob[MAXSCORE];

  /*   compute which score to report */
  if (ftype == 1) report = (double) 0.5 * TPS;		/* median TP */
  else            report = (double) 0.005 * Search;	/* 99.5% TN  */
  report80 = 0.020 * Search;				/* 90.0% TN */
  minscore = maxscore = 0;
  maxvalue = -1;		/* assumes no negative scores */
  minvalue = 9999;
  probwt[0] = 20.0;
  for (col = 0; col < matrix->width; col++)
  {
     /*  Intialize probability weights with powers of 20 */
     if (col > 0) probwt[col] = probwt[col - 1] * 20.0;
     mincol = 9999; maxcol = -1;
/*   for (aa = 0; aa < AASALL; aa++) */
     for (aa = 1; aa <= 20; aa++)
     {
        itemp = round(matrix->weights[aa][col]);
        if (itemp > maxvalue) maxvalue = itemp;
	if (itemp < minvalue) minvalue = itemp;
        if (itemp > maxcol) maxcol = itemp;
	if (itemp < mincol) mincol = itemp;
     }
     maxscore += maxcol;
     minscore += mincol;
     if (col == 0) minfirst = mincol;
     if (col == (matrix->width - 1) ) minlast = mincol;
   }
   /*    Probability weights: weights applied to probs depending
         on how many columns of the block are aligned, the
         full alignment width gets about 19/21 = .905, alignments
         off either end of the sequence get the remainder */
   dtemp = probwt[matrix->width - 1] * 21.0 - 40.0;
   cum = 0.0;
   for (col = 0; col < matrix->width; col++)
   {
      probwt[col] = probwt[col] * 19.0 / dtemp;
      cum += probwt[col];
      if (col < matrix->width - 1) cum += probwt[col];
   }
   /* ---- Min. score could be just first or last column ----*/
   if (ftype == 1) printf("\nTrue positive distribution\n");
   else            printf("\nRandom distribution\n");
   printf("minscore without ends=%d\n", minscore);
   if (minfirst < minscore) minscore = minfirst;
   if (minlast < minscore)  minscore = minlast;
   printf("minvalue=%d maxvalue=%d minscore=%d maxscore=%d\n",
	   minvalue, maxvalue, minscore, maxscore);
   /*-----------------------------------------------------------------*/
   if (maxscore > MAXSCORE) 
   { 
      maxscore = MAXSCORE - 1;
      fprintf(stderr, "maxscore is too big, increase MAXSCORE from %d\n",
              maxscore + 1);
      return(0);
   }
   last = Scores[0]; this = Scores[1];
   for (x = minvalue; x <= maxscore; x++)
   {  last[x].ways = last[x].prob = this[x].ways = this[x].prob = 0.0; 
      ends[x].ways = ends[x].prob = middle[x].ways = middle[x].prob = 0.0; }

   /*---------Initialize from first column -------------------------------*/
   /*   use obs_freqs->weights[aa][col] instead of freqs[aa] for TP probs */
   col = 0;
/* for (aa=0; aa < AASALL; aa++) */
   for (aa=1; aa <= 20; aa++)
   {
      x = round(matrix->weights[aa][col]);
      last[x].ways += 1.0;
      if (ftype == 1) ftemp = obs_freqs->weights[aa][col] / 100.;
      else            ftemp = freqs[aa];
      last[x].prob += ftemp;   
   }

   /*---- Now enumerate all possible scores, expanding one column
          at a time ----------*/
   for (col=1; col < matrix->width; col++)
   {
      /*--------- Save the alignments hanging off the left end ------*/
      /*    There are currently col+1 columns of the block aligned  */
      for (x=minvalue; x <= maxscore; x++)
      {
            ends[x].ways += last[x].ways;
            ends[x].prob += last[x].prob * probwt[col - 1];
      }
/*    for (aa=0; aa < AASALL; aa++) */
      for (aa=1; aa <= 20; aa++)
      {
         for (x=minvalue; x <= maxscore; x++)
         {
            if (last[x].ways > 0)
            {
               score = x + round(matrix->weights[aa][col]);
               this[score].ways += last[x].ways;
               if (ftype == 1) ftemp = obs_freqs->weights[aa][col] / 100.;
               else            ftemp = freqs[aa];
               this[score].prob += last[x].prob * ftemp;
            }
         } /* end of score x */
      }  /* end of aa */
      /*---------   Switch the arrays ------------------------------*/
      if (this == Scores[1])
      {  last = Scores[1]; this = Scores[0]; }
      else
      {  last = Scores[0]; this = Scores[1]; }
      for (x = minvalue; x <= maxscore; x++)
      {  this[x].ways = this[x].prob = 0.0;  }
   }  /* end of col */

   /*-------- last now has the final counts of all combinations
      of column scores for full blocks, and ends has the counts
      for alignments off the left end. Still have to get counts
      for alignments off the right end and need two arrays to do
      it. So have to keep last results in another array--------*/
   for (x=minvalue; x <= maxscore; x++)
   {
	middle[x].ways = last[x].ways;
	middle[x].prob = last[x].prob * probwt[matrix->width - 1];
	last[x].ways = last[x].prob = 0.0;
   }

   /*-------- Get the alignments hanging off the right end -------*/
   /*    Initialize with last column   */
   col = matrix->width - 1;
   for (aa=1; aa <= 20; aa++)
   {
      x = round(matrix->weights[aa][col]);
      last[x].ways += 1.0;
      if (ftype == 1) ftemp = obs_freqs->weights[aa][col] / 100.;
      else            ftemp = freqs[aa];
      last[x].prob += ftemp;   
   }
   for (col = matrix->width - 2; col >= 1; col--)
   {
      /*--------- Save the gapped alignments off the right end ------*/
      /*  There are currently length-col columns of the block aligned */
      for (x=minvalue; x <= maxscore; x++)
      {
            ends[x].ways += last[x].ways;
            ends[x].prob += last[x].prob * probwt[matrix->width - col - 2];
      }
      for (aa=1; aa <= 20; aa++)
      {
         for (x=minvalue; x <= maxscore; x++)
         {
            if (last[x].ways > 0)
            {
               score = x + round(matrix->weights[aa][col]);
               this[score].ways += last[x].ways;
               if (ftype == 1) ftemp = obs_freqs->weights[aa][col] / 100.;
               else            ftemp = freqs[aa];
               this[score].prob += last[x].prob * ftemp;
            }
         } /* end of score x */
      }  /* end of aa */
      /*---------   Switch the arrays ------------------------------*/
      if (this == Scores[1])
      {  last = Scores[1]; this = Scores[0]; }
      else
      {  last = Scores[0]; this = Scores[1]; }
      for (x = minvalue; x <= maxscore; x++)
      {  this[x].ways = this[x].prob = 0.0;  }
   }  /* end of column */
   /*--------- Save the gapped alignments off the right end ------*/
   /*  Need to get the length - 1 counts from the right   */
   for (x=minvalue; x <= maxscore; x++)
   {
         ends[x].ways += last[x].ways;
         ends[x].prob += last[x].prob * probwt[matrix->width - 2];
   } 

   /*--------cumprob[x] has sum from x to maxscore of prob[x]; this
	is the prob(score >= x) -------------------------------------*/
   cum = 0.0;
   for (x = maxscore; x >= minvalue; x--)
   {
      if (middle[x].ways > 0.0 || ends[x].ways > 0.0)
      {
         cum += (middle[x].prob + ends[x].prob);
      }
      cumprob[x] = cum;
      if (fcum != NULL && cum < MAXPROB)
      { fprintf(fcum, "%d %6.2g\n", x, cum); }
   }

   /*------- Make a histogram now for display ----*/
   cum = dtemp = 0.0;
   for (col=0; col < MAXSCORE/HISTO; col++)
   { histo[col].ways = histo[col].prob = 0.0; }
   for (x = minvalue; x <= maxscore; x++)
   {
      if (middle[x].ways > 0.0 || ends[x].ways > 0.0)
      {
/*       printf("%4d %8.4f %11.8f\n", x, log10(middle[x].ways),
                middle[x].prob ); */
         histo[ (int) x/HISTO ].ways += middle[x].ways;
         histo[ (int) x/HISTO ].prob += middle[x].prob;
         histo[ (int) x/HISTO ].ways += ends[x].ways;
         histo[ (int) x/HISTO ].prob += ends[x].prob; 
         cum += middle[x].prob; dtemp += ends[x].prob;
      }
/*    else
         printf("%4d   none\n", x); */
   }
   printf("Sum of probabilities = %8.4f\n", cum + dtemp);

   /*   number of alignments done in a hypothetical search   */
   /*   Always assume one alignment per TP sequence          */
   if (ftype == 1) aligns = TPS;
   else aligns = (double) SearchAA + Search * (matrix->width - 1); 
/* else aligns = 10000000; */

   /*   Print the histogram  */
   if (ftype == 1) pflag = 0; else pflag = 1;   /* don't print 0 TP buckets */
   printf("Score range  log(Possible ways)  Probability  Expected scores\n");
   for (x = (int) minvalue/HISTO; x <= (int) maxscore/HISTO; x++)
   {
      if (ftype == 1 && histo[x].ways > 0.0) pflag = 1;
      if (pflag)
      {
         if (histo[x].ways > 0.0)
            printf("%4d %4d  %8.4f      %11.8f      %8.2f\n",
                 x*HISTO, x*HISTO+HISTO-1, log10(histo[x].ways),
                histo[x].prob, histo[x].prob * aligns );
         else
            printf("%4d %4d       0.0000       0.000000\n", 
		x*HISTO, x*HISTO+HISTO-1);
      }
   }

   /* Count the TP scores above 99.5% of TN scores, x is the score */
   if (ftype == 1 && TN995 > 0)
   {
      x = maxscore; TPabove = 0.0;
      while (x > TN995)
      {
         if (middle[x].prob > 0.0 || ends[x].prob > 0.0)
         {
            TPabove += (middle[x].prob + ends[x].prob) * aligns;
         }
         x--;
      }
   }

   /* Print the top scores, x is the score, cum is the
	cumulative number of expected scores,
	report is the cutoff number of scores */
   x = maxscore; cum = 0.0;
   printf("\nCumScrs  Score   ExpectedScrs\n");
   while (cum < report && x >= 0)
   {
       if (middle[x].prob > 0.0 || ends[x].prob > 0.0)
       {
          if (cum > 100.0)
             printf("%8.4f %4d %8.4f\n", 
		cum, x, (middle[x].prob + ends[x].prob) * aligns);
          cum += (middle[x].prob + ends[x].prob) * aligns;
       }
       x--;
   }
   xreport = x;

   if (ftype == 1)
   { 
	printf("Median TP score = %d, TPabove = %lf\n", xreport, TPabove); 
   }
   else
   { 
      while (cum < report80 && x >= 0)
      {
          if (middle[x].prob > 0.0 || ends[x].prob > 0.0)
          {
             if (cum > 100.0)
                printf("%8.4f %4d %8.4f\n", 
	   	cum, x, (middle[x].prob + ends[x].prob) * aligns);
             cum += (middle[x].prob + ends[x].prob) * aligns;
          }
          x--;
      }
	TN800 = x;
	printf("99.5 TN score = %d, 80.0 = %d\n", xreport, TN800); 
   }

   return(xreport);		/* TN995 if ftype == 0 */
}  /* end of compute */
/*=================================================================
 * load_freqs
 *   reads in the frequencies if there is a frequency file.
 *   Parameters: none
 *   Return codes: TRUE if there was a frequency file and the data was 
 *                 sucessfully read in, FALSE if not.
 *   Error codes: none
 =================================================================*/

int load_freqs(ffp, frequency)
FILE *ffp;
double frequency[AASALL];
{
  int i;
  int seq_type;
  double J_weight, O_weight, U_weight;


#define NAR '|'   /* a char that get converted to AAID_NAR for the codes so */
		  /* that when reading in the frequencies entries for J, O, */
		  /* an U do not overwrite the entry for X.  Also if the */
		  /* idea of have non-code alpha characters being treated as */
		  /* X ever changes, there shouldn't be a change needed */
		  /* (assuming that J, O, U are weighted the same). */

  /* old style format */
  frequency[aa_atob['-']] = read_a_freq(ffp);
  frequency[aa_atob['A']] = read_a_freq(ffp);
  frequency[aa_atob['B']] = read_a_freq(ffp);
  frequency[aa_atob['C']] = read_a_freq(ffp);
  frequency[aa_atob['D']] = read_a_freq(ffp);
  frequency[aa_atob['E']] = read_a_freq(ffp);
  frequency[aa_atob['F']] = read_a_freq(ffp);
  frequency[aa_atob['G']] = read_a_freq(ffp);
  frequency[aa_atob['H']] = read_a_freq(ffp);
  frequency[aa_atob['I']] = read_a_freq(ffp);

  J_weight = read_a_freq(ffp);
  frequency[aa_atob[NAR]] = J_weight; /* 'J' */

  frequency[aa_atob['K']] = read_a_freq(ffp);
  frequency[aa_atob['L']] = read_a_freq(ffp);
  frequency[aa_atob['M']] = read_a_freq(ffp);
  frequency[aa_atob['N']] = read_a_freq(ffp);

  O_weight = read_a_freq(ffp);
  frequency[aa_atob[NAR]] = O_weight; /* 'O' */

  frequency[aa_atob['P']] = read_a_freq(ffp);
  frequency[aa_atob['Q']] = read_a_freq(ffp);
  frequency[aa_atob['R']] = read_a_freq(ffp);
  frequency[aa_atob['S']] = read_a_freq(ffp);
  frequency[aa_atob['T']] = read_a_freq(ffp);

  U_weight = read_a_freq(ffp);
  frequency[aa_atob[NAR]] = U_weight; /* 'U' */

  frequency[aa_atob['V']] = read_a_freq(ffp);
  frequency[aa_atob['W']] = read_a_freq(ffp);
  frequency[aa_atob['X']] = read_a_freq(ffp);
  frequency[aa_atob['Y']] = read_a_freq(ffp);
  frequency[aa_atob['Z']] = read_a_freq(ffp);
  frequency[aa_atob['*']] = read_a_freq(ffp);

#undef NAR


  /* check the frequency array for zero values */
  for (i=0; i<MATRIX_AA_WIDTH; i++) {
    if (frequency[i] <= 0.0) {
      /* announce warning */
      if (i != AAID_NAR) {
	sprintf(ErrorBuffer, 
		"Read a frequency of value of %f for: %c\n", 
		frequency[i], 
		aa_btoa[i]);
      }
      else {
	sprintf(ErrorBuffer, 
		"Read a frequency of value of %f for: not a residue\n",
		frequency[i]);
      }
/*    ErrorReport(INFO_ERR_LVL);
*/
    }
  }


  /* Check to make sure the non-aa codes are the same.  These values will */
  /* be used for filling the position of the AAID_NAR spot, the last one */
  /* seen will be used. */
  if ( J_weight != O_weight ) {
    sprintf(ErrorBuffer, 
	    "The non-amino acid codes J and O weights are different");
    ErrorReport(INFO_ERR_LVL);
  }
  if ( O_weight != U_weight ) {
    sprintf(ErrorBuffer, 
	    "The non-amino acid codes O and U weights are different");
    ErrorReport(INFO_ERR_LVL);
  }
  if ( U_weight != J_weight ) {
    sprintf(ErrorBuffer, 
	    "The non-amino acid codes U and J weights are different");
    ErrorReport(INFO_ERR_LVL);
  }
  if (( J_weight != O_weight ) ||
      ( O_weight != U_weight ) ||
      ( U_weight != J_weight )) {
    sprintf(ErrorBuffer, 
	    "The value for the non-amino acid will be the last weight seen.");
    ErrorReport(INFO_ERR_LVL);
    sprintf(ErrorBuffer, 
	    "Currently the program ignores non-residue characters so this");
    ErrorReport(INFO_ERR_LVL);
    sprintf(ErrorBuffer,
	    "field has no effect.\n");
    ErrorReport(INFO_ERR_LVL);
  }

  return TRUE;
} /* end of load_freqs */

/*============================================================================
 * read_a_freq
 *   Reads a frequency value from the frequency file.  A value is the first
 *   number on a line.  If a line does not have a number first, the line is
 *   skipped.
 *   Parameters:
 *     FILE *ffp: the frequency file pointer.
 *   Return codes: The next frequency in the file.  The default value of zero
 *                 if there was an error in the reading.
 *   Error codes: none.  Can not determine between reading a zero and 
 *                returning it or if there was no number read.
 */

static double read_a_freq(ffp)
     FILE *ffp;
{
  double freq;

  freq = 0.0;

  while (fgets(Buffer, LARGE_BUFF_LENGTH, ffp) &&
	 sscanf(Buffer, "%lg", &freq) != 1);

  return freq;
} /* end of read_a_freq */
/*=====================================================================
    Normalize observed frequencies to sum exactly to 100
=======================================================================*/
void fix_freqs(obs_freqs)
Matrix *obs_freqs;
{
   int aa, col;
   double tot;

   for (col=0; col < obs_freqs->width; col++)
   {
      tot = 0;
      for (aa = 1; aa <= 20; aa++) tot += obs_freqs->weights[aa][col];
      for (aa = 1; aa <= 20; aa++)
      {
         if (obs_freqs->weights[aa][col] > 0.0)
         {
            obs_freqs->weights[aa][col] *= 100.0;
            obs_freqs->weights[aa][col] /= tot;
         }
      }
   }
   
}  /* end of fix_freqs  */
/*===================================================================
     count the number of sequence names in a .lis file
=====================================================================*/
int count_tps(flis)
FILE *flis;
{
   int nids;
   char line[MAXLINE];

   nids = 0;

   while(!feof(flis) && fgets(line, MAXLINE, flis) != NULL &&
	 strlen(line) > 2)
   {		/* skip over title or directory lines */
      if (line[0] != '>' && strstr(line, "/") == NULL &&
	  strstr(line,"\\") == NULL && strstr(line, ":") == NULL)
      {
	 nids++;
      }
   }
    return (nids);
}  /* end of count_tps */
