/*  Copyright 1999-2003 Fred Hutchinson Cancer Research Center

    cobbler.c   Make consensus sequences from blocks 
	Requires blimps 3.2.6+ routines

	NOTE: Problem - can't embed external sequence with percentage
	      consensus because QM without SU now means compute
	      PSSM consensus ...
	Always requires:
		BL, OU
	Embedded sequence from block requires:
		DB
	Embedded external sequence requires (FR & QM for alignment):
		SQ, FR, QM
        Consensus based on substitution scores requires:
		SU, FR, QM
	Consensus based on PSSM requires:
		FR, QM (not SU)
	Consensus based on percentage requires:
		PC (not SU or QM)  See NOTE above.
		Using 0% will select the aa with the max weight in each column.
		Using negative % will generate random consensus.

	cobbler <config file>
		Required: BL, OU, FR, QM and (PC or SU)
		Optional: (DB or SQ) and MA 
			  
		First determines consensus (TY, make_consensus());
		then sequence in blocks closest to consensus; 
		then embeds consensus in closest sequence (DB), or
		in provided sequence (SQ); also outputs embedded
		PSSM (if MA).
		TY Consensus type:
			1 Percent of weighted counts ( percent_consensus() )
			  Requires PC
			2 Scores with weighted counts ( score_consensus() )
			  Requires SU
			3 Scores with weighted counts + pseudo-counts
				( score_pseudos() )
			  Requires SU, FR & QM
			4 Maximum PSSM residue ( pssm_consensus() )
			  Requires FR & QM
		BL input file of blocks to make consensus from; must
		   contain sequence weights.
		OU output file of consensus blocks, or embedded
		   sequences if DB or SQ is specified.
		PC Percent identity for consensus residue in a position
		   of a block. PC = 0 => use most common residue; PC < 0
		   => use a residue at random. Or use SU instead.
		SU Name of substitution matrix to use to determine
		   consensus residues, instead of using PC. Takes residue
		   with highest pairwise score with other residues in position.
		   Use blosum62.iij, e.g.
		MA Name of PSSM to use when making profile. 
		   This triggers profile output = .prf file in format
		   specified by PF.
		   Use single55.mat for SWAT; single55.psi for PSI.
		   Use single55.mat for PFS (does PFS work?)
		PF format of output PSSM: BLIMPS|SWAT|PSI|PFS=Gribskov
		DB file containing sequences in blocks in BL file.
		   Consensus blocks will be embedded in the
		   first sequence most like the consensus & written
		   to file OU.
		SQ file containing selected query sequence.
		   First family of blocks in BL will be aligned with
		   sequence using a PSSM; then consensus blocks will
		   be embedded at the alignments if all blocks are
		   aligned in the correct order without overlap and
	           written to file OU. Requires FR and QM.
		FR Name of frequency matrix to use to make PSSM for SQ & MA.
		   Also used to make PSSMs for "TY 3" & "TY 4".
		   In summary, used to make all required log-odds PSSMS; 
		   see also QM.
		   Use default.amino.frq, e.g.
		QM Name of qij matrix used for pseudo-counts when making
		   Altschul-type log-odds PSSMs. Used to make PSSM for 
		   aligning blocks with SQ sequence. Also used to make 
		   embedded PSSM when MA is specified. Also used to make
		   PSSM to select consensus residues when "TY 4" is used.
		   Also used to make pseudo-counts for selecting consensus
		   residues when "TY 3" is used.
		   Should separate these options; see also FR.
		   These PSSMs are always 1/3 bit log odds (6).
		   Use default.qij or blosum55.qij e.g.
		TR Trimmed length: length of cobbler sequence before &
		   after start of blocks to include in output sequence
		   (entire sequence is output by default)


>>>>> Problems if: Not same sequences in all blocks, or
 	     blocks not in same order in all blocks, or
	     blocks overlap

--------------------------------------------------------------------
 7/ 7/97 Changed extract_seq() to look for id anywhere on title line.
         Changed score_pseudos() to just use the 20 major aas.
 2/21/98 Added TR option, SHORT_TRIM, etc. to output trimmed sequence.
         Changed MAXWIDTH from 60 to 80.
 6/12/99 Tried to fix problem if lots of sequence left after last block
	 (truncate sequence in extract_seq()). Blimps 3.2.6
 7/ 3/99 Option to output psi-blast checkpoint file; PrfOutType = PRF_PSI
 7/ 5/99 Replaced extract_seq() with read_seq(); reworked embed_consensus()
 7/ 6/99 Reworked query_consensus()
 7/ 8/99 Bug fix
 7/10/99 For pre-selected sequence (SQ), don't count the distance
	 before the first block in the min length; query_consensus()
 7/23/99 Commented out RAND_MAX
 8/ 5/99 Check that embedded sequence/profile doesn't exceed length of
	 chosen sequence.
 8/ 6/99 Handle embedded seqs with blocks out of order; added Version.
 8/19/99 Indicate if DB seq not found; match seqs on any part of name
	 separated by "|"
 9/ 3/99.1 Added BLIMPS output PSSM option (to PF).
           Check that input blocks (BL) have sequence weights &
           run pb_weights if they don't.
12/ 9/99.1 Fix memory leaks
12/17/99.1 Changes for longer AC
12/23/99.1 Print width on pssm->ma for output_matrix()
 3/28/00.1 Revised output text
 3/28/00.2 New ML/LL config file options to output most like/least like seqs
 4/14/00.1 Fix problem with multi-part names in read_seq()
>>>>>>>>>>>>>>>>>>>>Blimps 3.4<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 6/27/00.1 Fix problem not finding LL sequence in read_seq()
>>>>>>>>>>>>>>>>>>>>Blimps 3.5<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 1/16/03.1 Default SU, QM, FR if not specified in config file
====================================================================*/

#define COBBLER_C_
#define EXTERN
#define MAXNAME 80	/* Maximum file name length */
#define MAXAA 25	/* Dimension of aa_ arrays */
#define MAXWIDTH 80	/* Maximum block width */
#define CPROP .25	/* Default Minimum proportion of consensus weight */
#define PRF_SWAT 0	/* swat */
#define PRF_PFS 1	/* profilesearch */
#define PRF_PSI 2	/* psi-blast */
#define PRF_BLI 3	/* blimps format */
/*		Already defined for Solaris  2^31 - 1
#define RAND_MAX 2147483647
*/
#define YES 1
#define NO 0
#define SHORT_TRIM 10	/* Default trimmed length */

#include <blocksprogs.h>
#include <sys/time.h>

/*   Blimps routines and data structures   */
extern int ErrorLevelReport;
extern struct float_qij *Qij;
extern double RTot;
extern double frequency[MATRIX_AA_WIDTH];
extern struct float_qij *load_qij();
extern int load_frequencies();
extern Matrix *block_to_matrix();

/*
 * Global variables and data structures
 */

char Version[12] = " 1/16/03.1";	/* Version number */

/*  List of blocks structure:
    First entry has no block, just nblock, nseq, totwidth & minseq,
    other entries in list have pointers to the blocks, minprev & seqprev
*/
struct blocks_list {		/* list of blocks for a family */
   int nblock;				/* number of blocks in family */
   int nseq;				/* number of sequences in blocks */
   int totwidth;			/* total width of blocks in list  */
   int minseq;				/* sequence most like consensus */
   int maxseq;				/* sequence least like consensus */
   int minprev;				/* min distance from previous block*/
   int maxprev;
   int minseqprev;			/* previous distance for minseq */
   int maxseqprev;			/* previous distance for maxseq */
   int query_pos;
   int *consensus;			/* consensus for this block */
   Block *block;
   Matrix *matrix;
   struct blocks_list *next;		/* next block in ABC order */
   struct blocks_list *next_min;		/* next block in minseq order */
};
struct seqseq { int seq; };

struct sorttemp {
   int position;			/* sequence position */
   struct blocks_list *blist;		/* block list */
};

int read_cf();
void process_family();
void make_consensus();
void percent_consensus();
void score_consensus();
void score_pseudos();
void cons_diff();
void closest();
void print_consensus();
void embed_consensus();
Sequence *read_seq();
void query_consensus();
int best_pos();
void pssm_consensus();
void write_cobbler();
void get_seq();

void make_random();
int ran0();

struct blocks_list *make_blist();
void insert_blist();
void free_blist();
void order_seq();
void load_sij();
int sortcmp();

int SeqType;			/*  why??? blimps stuff */
int Type;			/* Consensus type */
int PrfOutType;			/* Output profile type */
int PrfInType;			/* Input profile type */
int ShortTrim, Short;		/* Sequence trimming info */
double CProp;			/* Min weight % for consensus residue */
int Scores[MAXAA][MAXAA];	/*  Scoring matrix */
Matrix *Profile;		/* Baseline PSSM for making a profile */
FILE *Fbl, *Fou, *Fml, *Fll, *Fpr, *Fsq, *Fdb;	/* Config. files */
/*======================================================================
 * main
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE *cfp;
  Block *block;
  struct blocks_list *blist;
  char cfname[MAXNAME], save_family[MAXAC+1];
  struct timeval tv;

  ErrorLevelReport = 5;			/* don't want to see them */
  /* initialize random number generator */
  gettimeofday(&tv, NULL);
  srandom(tv.tv_sec^tv.tv_usec);

   if (argc <= 1)
   {
       printf("COBBLER Version %s\n", Version);
       printf("COPYRIGHT 1999 Fred Hutchinson Cancer Research Center\n");
       printf("Make a consensus sequence and PSSM from a family of blocks\n");
       printf("USAGE:  cobbler <config_file>\n");
   }
/*------------1st arg = configuration file -------------------------------*/
   if (argc > 1)
      strcpy(cfname, argv[1]);
   else
   {
      printf("\nEnter name of configuration file: ");
      gets(cfname);
   }
   if ( (cfp=fopen(cfname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", cfname);
      exit(-1);
   }
   if ( (Type = read_cf(cfp)) < 0)
   {
      printf("Configuration file errors\n");
      exit(-1);
   }

  /*-------------------Read in each family of blocks -------------------*/
  save_family[0] = '\0';
  blist = NULL;
  while ((block = read_a_block(Fbl)) != NULL)
  {
     if (strcmp(save_family, block->family) != 0)
     {  /*  new family */
         process_family(blist, Fou, Fpr); /* process previous family */
         /*   initialize new family */
         strcpy(save_family, block->family);
         if (Fdb == NULL && Fsq == NULL) 
              fprintf(Fou, ">%s %s\n", save_family, block->de); 
         blist = make_blist();			/* header for list */
         insert_blist(blist, block);		/* 1st block in list */
     }
     else      /*  same family */
     {
         insert_blist(blist,block);
     }
  }     /*  end of block */

  /*  process previous family */
  process_family(blist, Fou, Fpr);
  if (cfp != NULL) fclose(cfp); 
  if (Fbl != NULL) fclose(Fbl); 
  if (Fou != NULL) fclose(Fou); 
  if (Fpr != NULL) fclose(Fpr);
  if (Fsq != NULL) fclose(Fsq);

  /*  print out most like/ least like sequences if requested */
  if (Fdb != NULL)
  {
     if (Fml != NULL) 
          get_seq(blist->next->block->family,
                  blist->next->block->sequences[blist->minseq].name, 
                  Fdb, Fml);
     if (Fll != NULL)
          get_seq(blist->next->block->family,
                  blist->next->block->sequences[blist->maxseq].name, 
                  Fdb, Fll);
  }
   
  if (Fml != NULL) fclose(Fml);
  if (Fll != NULL) fclose(Fll);
  if (Fdb != NULL) fclose(Fdb);
  exit(0);

}  /* end of main */
/*=======================================================================*/
int read_cf(cfp)
FILE *cfp;
{
   char line[MAXLINE], keyword[20], *blimps_dir, *ptr;
   char blname[MAXNAME], ouname[MAXNAME], frname[MAXNAME];
   char mlname[MAXNAME], llname[MAXNAME];
   char suname[MAXNAME], qmname[MAXNAME], sqname[MAXNAME];
   char dbname[MAXNAME], maname[MAXNAME], ctemp[MAXNAME];
   int cscores, freqs;
   FILE *fqm, *fsu, *fma;

   blimps_dir = getenv("BLIMPS_DIR");

   CProp = CPROP;
   PrfOutType = PRF_SWAT;
   Type = -1;
   RTot = 5.0;
   Short = NO;
   cscores = freqs = NO;

   Fbl = Fou = Fml = Fll = Fsq = Fdb = fqm = fma = NULL;
   while (!feof(cfp) && fgets(line, MAXLINE, cfp) != NULL)
   {
      ptr = strtok(line, " \t\r\n");
      if (ptr != NULL && ptr[0] != ';')
      {
         strcpy(keyword,ptr);
         ptr = strtok(NULL, " \t\r\n;");
         if (ptr != NULL)
         {
            if (strncasecmp(keyword, "TY", 2) == 0)
            {
               Type = atoi(ptr);
            }
            else if (strncasecmp(keyword, "BL", 2) == 0)
            {
               strcpy(blname, ptr);
               if ( (Fbl = fopen(blname, "r")) == NULL)
                  printf("\nCannot open BL file %s\n", blname);
            }
            else if (strncasecmp(keyword, "OU", 2) == 0)
            {
               strcpy(ouname, ptr);
               if ( (Fou = fopen(ouname, "w")) == NULL)
                  printf("\nCannot open OU file %s\n", ouname);
               else printf("Consensus written to %s\n", ouname);
            }
            else if (strncasecmp(keyword, "ML", 2) == 0)
            {
               strcpy(mlname, ptr);
               if ( (Fml = fopen(mlname, "w")) == NULL)
                  printf("\nCannot open ML file %s\n", mlname);
               else printf("Most like consensus written to %s\n", mlname);
            }
            else if (strncasecmp(keyword, "LL", 2) == 0)
            {
               strcpy(llname, ptr);
               if ( (Fll = fopen(llname, "w")) == NULL)
                  printf("\nCannot open LL file %s\n", llname);
               else printf("Least like consensus written to %s\n", llname);
            }
            else if (strncasecmp(keyword, "SQ", 2) == 0)
            {
               strcpy(sqname, ptr);
               if ( (Fsq = fopen(sqname, "r")) == NULL)
                  printf("\nCannot open SQ file %s\n", sqname);
            }
            else if (strncasecmp(keyword, "DB", 2) == 0)
            {
               strcpy(dbname, ptr);
               if ( (Fdb = fopen(dbname, "r")) == NULL)
                  printf("\nCannot open DB file %s\n", dbname);
            }
            else if (strncasecmp(keyword, "FR", 2) == 0)
            {
               strcpy(frname, ptr);
               if (load_frequencies(frname) < 0)
                  printf("\nCannot open FR file %s\n", frname);
               else freqs = YES;
            }
            else if (strncasecmp(keyword, "PC", 2) == 0)
            {
               CProp = (double) atoi(ptr) / 100.;
               if (CProp > 1.0 || CProp < 0.0) CProp = -1.0;
            }
            else if (strncasecmp(keyword, "SU", 2) == 0)
            {
               strcpy(suname, ptr);
               if ( (fsu = fopen(suname, "r")) == NULL)
                  printf("\nCannot open SU file %s\n", suname);
               else
               {  cscores=YES;  load_sij(fsu, Scores);  fclose(fsu);  }
            }
            else if (strncasecmp(keyword, "MA", 2) == 0)
            {
               strcpy(maname, ptr);
               if ( (fma = fopen(maname, "r")) == NULL)
                  printf("\nCannot open MA file %s\n", maname);
               else
               {   Profile = read_a_matrix(fma);  fclose(fma);  }
            }
            else if (strncasecmp(keyword, "PF", 2) == 0)
            {
               strcpy(ctemp, ptr);
               if (strncasecmp(ctemp, "PSI", 3) == 0) PrfOutType = PRF_PSI;
               else if (strncasecmp(ctemp, "PFS", 3) == 0) PrfOutType = PRF_PFS;
               else if (strncasecmp(ctemp, "BLI", 3) == 0) PrfOutType = PRF_BLI;
            }
            else if (strncasecmp(keyword, "QM", 2) == 0)
            {
               strcpy(qmname, ptr);
               if ( (fqm = fopen(qmname, "r")) == NULL)
                  printf("\nCannot open QM file %s\n", qmname);
               else
               { Qij = load_qij(fqm); RTot = 5.0; fclose(fqm);   }
            }
            else if (strncasecmp(keyword, "TR", 2) == 0)
            {
                 Short = YES;
                 ShortTrim = atoi(ptr);
                 if (ShortTrim < 0) ShortTrim = SHORT_TRIM;
            }
         }   /* end of if ptr */
      }  /* end of if ptr */
   }  /* end of cfp */

   /*   Get some defaults */
   if (blimps_dir != NULL)
   {
      /*  Default SU if not specified to default.iij */
      if (!cscores)
      {
         sprintf(suname, "%s/docs/default.iij", blimps_dir);
         if ( (fsu = fopen(suname, "r")) != NULL)
         {  cscores=YES;  load_sij(fsu, Scores);  fclose(fsu);  }
      }
      /*  Default FR and QM if they weren't specified  */
      /* Sets global variables Qij, frequency[], RTot  */
      /* Always reads default.amino.frq & default.qij & sets RTot = 5 */
      if (!freqs)
      {
         sprintf(frname, "%s/docs/default.amino.frq", blimps_dir);
         if (load_frequencies(frname) >= 0)
          { freqs = YES;  }
      }
      if (Qij == NULL)
      {
         sprintf(qmname, "%s/docs/default.qij", blimps_dir);
         if ( (fqm = fopen(qmname, "r")) != NULL)
         { Qij = load_qij(fqm); RTot = 5.0; fclose(fqm);   }
      }
   }

   if (Profile != NULL)
   {
      strcpy(ctemp, ouname); strcat(ctemp, ".prf");
      if ( (Fpr = fopen(ctemp, "w")) == NULL)
      {
            printf("\nCannot open OU file %s\n", ctemp);
            free_matrix(Profile);
	    Profile = NULL; PrfOutType = -1;
      }
      else printf("Profile written to %s\n", ctemp);
   }
   else { PrfOutType = -1;  }

   /*----------------------------------------------------------------*/
   /*    Figure out the type now   */
   /*  1  Percent
       2  Substitution matrix + counts
       3  Substitution matrix + counts + pseudo-counts
       4  PSSM
   */
   if (Type < 0)		/* TY wasn't specified explicitly */
   {
      if (cscores) Type = 3;
      else if (Qij != NULL && freqs) Type = 4;
      else Type = 1;
   }
   else				/* TY was specified explicitly */
   {
      if (Type == 1)
      {  cscores = NO;  }
      else if (Type == 2)
      {
         if (!cscores)
         {  printf("Missing SU input file name\n"); Type = -1; }
      }
      else if (Type == 3)
      {
         if (!cscores)
         {  printf("Missing SU input file name\n"); Type = -1; }
         if (Qij == NULL || !freqs)
         {  printf("FR and QM required with Type 3\n");  Type = -1; }
      }
      else if (Type == 4)
      {
         if (Qij == NULL || !freqs)
         {  printf("FR and QM required with Type 4\n");  Type = -1; }
      }
   }
   if (Type > 4)
   {  printf("Invalid consensus type: %d\n", Type); Type = -1; }
   if (Fbl == NULL)
   {  printf("Missing BL input file name\n"); Type = -1; }
   if (Fou == NULL)
   {  printf("Missing OU output file name\n"); Type = -1; }
   if (Fdb != NULL && Fsq != NULL)
   {  printf("Cannot specify both DB and SQ\n"); Type = -1; }
   if (Fsq != NULL && (Qij == NULL || !freqs))
   {  printf("FR and QM required with SQ\n");  Type = -1; }
   if (Fpr != NULL && (Qij == NULL || !freqs))
   {  printf("FR and QM required with MA\n");  Type = -1; }
   if (Fml != NULL && Fdb == NULL)
   {  printf("DB required with ML\n");  Type = -1; }
   if (Fll != NULL && Fdb == NULL)
   {  printf("DB required with LL\n");  Type = -1; }
   

   return(Type);
}  /* end of read_cf */
/*=======================================================================
========================================================================*/
void process_family(blist, ofp, pfp)
struct blocks_list *blist;
FILE *ofp, *pfp;
{

  if (blist != NULL && blist->nblock > 0)
  {
      switch (Type)
      {
         case 1:			/* percentage */
		printf("Type 1: Consensus based on percentage: %d\n",
			(int) (100. * CProp) );
		break;
	 case 2:			/* subst. with counts */
		printf("Type 2: Consensus based on substitution matrix");
                printf(" with counts (no pseudo-counts)\n");
		break;
	 case 3:			/*subst with counts + pseudos */
		printf("Type 3: Consensus based on substitution matrix");
		printf(" with counts + pseudo-counts\n");
		break;
	 case 4:			/* PSSM */
		printf("Type 4: Consensus based on PSSM\n");
		break;
	 default:
		break;
     }
     if (Type == 1 && CProp < 0.0)
     {   make_random(blist, ofp);    }
     else
     {
        make_consensus(blist); 
        if (Fdb != NULL)      embed_consensus(blist, ofp, pfp, NULL);
        else if (Fsq != NULL) query_consensus(blist, ofp, pfp);
             else             print_consensus(blist, ofp);
     }

/*
     free_blist(blist);
*/
   }

}   /* end of process_family  */
/*=======================================================================
      Turn a list of blocks into a consensus sequence
      Count the number of positions each sequence in the block differs
      from the consensus. The master sequence order is that in the 
      first block of the family.
========================================================================*/
void make_consensus(blist)
struct blocks_list *blist;
{
   struct blocks_list *bcur;
   Block *block, *first_block;
   struct seqseq *sseq;

   sseq = (struct seqseq *) malloc (blist->nseq * sizeof(struct seqseq));
   first_block = NULL;
   bcur = blist->next;
   while(bcur != NULL && bcur->block != NULL)
   {
      /*  process each block separately  */
      block = bcur->block;

      /*-----  Figure out the consensus segment for this block ------------*/
      /*  (Puts result in bcur->consensus )   */
      switch (Type)
      {
         case 1:			/* percentage */
		percent_consensus(bcur);
		break;
	 case 2:			/* subst. with counts */
		score_consensus(bcur);
		break;
	 case 3:			/*subst with counts + pseudos */
		score_pseudos(bcur);
		break;
	 case 4:			/* PSSM */
		pssm_consensus(bcur);
		break;
	 default:
		break;
      }

      /*---------Count the number of differences b/w each seq & consensus--*/
      /*  (Puts result in first_block->sequences[seq].undefined)   */
      if (first_block == NULL) first_block = block;
      cons_diff(bcur, first_block, sseq);

      bcur = bcur->next;
   }  /* end of block */

   /*----------Determine the closest sequence ---------*/
   /*   (Puts results in block list records)           */
   /*  Extract blist->next->block->sequences[blist->minseq].name  */
   closest(blist, first_block, sseq);

   free(sseq);
}  /* end of make_consensus */
/*========================================================================
        Figure out the consensus for this block based on percentage
        & store it in the block list record
=========================================================================*/
void percent_consensus(bcur)
struct blocks_list *bcur;
{
   double sum[MAXAA+1], tot, maxw;
   int seq, pos, aa, width, maxaa, dupmax;
   Block *block;

   block = bcur->block;
   width = block->width;

   /*-------------------------------------------------------------------*/
   for (pos = 0; pos < width; pos++)
   { 
      tot = 0.0;
      maxw = maxaa = dupmax = -1;
      for (aa = 0; aa < MAXAA+1; aa++) sum[aa] = 0.0;
  
      for (seq = 0; seq < block->num_sequences; seq++)
      {
         if (block->residues[seq][pos] >= 0 &&
                block->residues[seq][pos] < MAXAA)
         {
            sum[block->residues[seq][pos]] += block->sequences[seq].weight;
            tot += block->sequences[seq].weight;
         }
         else printf("Unknown residue: %d\n", block->residues[seq][pos]);
      }

      for (aa = 0; aa < MAXAA+1; aa++)
      {
         if (sum[aa] > maxw)
         {
            maxw = sum[aa]; maxaa = aa;
         }
         else if (sum[aa] > 0.0 && sum[aa] == maxw)
                  dupmax = aa;
      }
      /*-------  Print "x" if maximum weight for any aa is < CProp% ------*/
      if ((maxw / tot) < CProp) maxaa = MAXAA;

      /*--------Save the consensus sequence------------------------*/
      if (maxaa >= 0 && maxaa < MAXAA )
           bcur->consensus[pos] = maxaa;
      else
           bcur->consensus[pos] = MAXAA;	/* X==23 */
   }  /*  end of pos */

}   /*  end of percent_consensus  */
/*========================================================================
        Figure out the consensus for this block & store it in the
        block list record - use highest total pairwise score
        Prints x if the best total score is negative
>>>>>  need to add a type to block_to_matrix(block, 21) that returns
	counts & just use them (like score_pseudos() does);
	would take care of the sequence weights too (see insert_blist())
=========================================================================*/
void score_consensus(bcur)
struct blocks_list *bcur;
{
   double sum[MAXAA+1], maxw, weight;
   int seq1, pos, width, nseq, maxaa, aamark[MAXAA+1];
   int aa1, aa2;
   Block *block;

   block = bcur->block;
   width = block->width;
   nseq = block->num_sequences;

   /*-------------------------------------------------------------------*/
   for (pos = 0; pos < width; pos++)
   { 
      /*    good sums can be negative */
      maxw = -999999.99;
      maxaa = -1;
      for (aa1 = 0; aa1 < MAXAA+1; aa1++)
      {
         sum[aa1] = 0.0;
         aamark[aa1] = NO;	/* does this aa appear in the pos ? */
      }
  
      /*   Mark the aas that appear in this position */
      for (seq1 = 0; seq1 < nseq; seq1++)
      {
            aa1 = block->residues[seq1][pos];
            aamark[aa1] = YES;
      }

      /*    Only choose among the aas that appear in the pos  */
      for (aa1 = 0; aa1 < MAXAA; aa1++)
      {
         sum[aa1] = 0.0;
         if ( aamark[aa1] )
         {
            for (seq1 = 0; seq1 < nseq; seq1++)
            {
               aa2 = block->residues[seq1][pos];
               weight =  block->sequences[seq1].weight;
               sum[aa1] += weight * Scores[aa1][aa2];
            }
         }
      }

      /*    Only choose among the aas that appear in the pos  */
      for (aa1 = 0; aa1 < MAXAA; aa1++)
      {
         if (aamark[aa1] && sum[aa1] > maxw)
         {
            maxw = sum[aa1]; maxaa = aa1;
         }
      }
      if (maxw <= 0.0) maxaa = MAXAA;	/*  Use X if non-positive sum */

      /*--------Save the consensus sequence------------------------*/
      if (maxaa > 0 && maxaa < MAXAA )
           bcur->consensus[pos] = maxaa;
      else
           bcur->consensus[pos] = MAXAA;	/* X==23 */
   }  /* end of pos */

}  /*  end of score_consensus */

/*========================================================================
        Figure out the consensus for this block & store it in the
        block list record - use highest total pairwise score
        Prints x if the best total score is negative
	Variation using pseudo-counts
=========================================================================*/
void score_pseudos(bcur)
struct blocks_list *bcur;
{
   double sum[MAXAA+1], maxw;
   int seq1, pos, width, nseq, maxaa, aamark[MAXAA+1];
   int aa1, aa2;
   Block *block;
   Matrix *matrix;

   block = bcur->block;
   width = block->width;
   nseq = block->num_sequences;
   matrix = block_to_matrix(block, 20);		/* counts+pseudos */

   /*-------------------------------------------------------------------*/
   for (pos = 0; pos < width; pos++)
   { 
      /*    good sums can be negative */
      maxw = -999999.99;
      maxaa = -1;
      for (aa1 = 0; aa1 < MAXAA+1; aa1++)
      {
         sum[aa1] = 0.0;
         aamark[aa1] = NO;	/* does this aa appear in the pos ? */
      }
  
      /*   Mark the aas that appear in this position */
      for (seq1 = 0; seq1 < nseq; seq1++)
      {
            aa1 = block->residues[seq1][pos];
            aamark[aa1] = YES;
      }

      /*    Only score the aas that appear in the pos  */
      for (aa1 = 0; aa1 < MAXAA; aa1++)
      {
         sum[aa1] = 0.0;
         if ( aamark[aa1] )
         {
            /*  Just the major 20 aas */
            for (aa2=1; aa2 <= 20; aa2++)
               sum[aa1] += matrix->weights[aa2][pos] * Scores[aa1][aa2];
         }
      }

      /*    Only choose among the aas that appear in the pos  */
      for (aa1 = 0; aa1 < MAXAA; aa1++)
      {
         if (aamark[aa1] && sum[aa1] > maxw)
         {
            maxw = sum[aa1]; maxaa = aa1;
         }
      }
      if (maxw <= 0.0) maxaa = MAXAA;	/*  Use X if non-positive sum */

      /*--------Save the consensus sequence------------------------*/
      if (maxaa > 0 && maxaa < MAXAA )
           bcur->consensus[pos] = maxaa;
      else
           bcur->consensus[pos] = MAXAA;	/* X==23 */
   }  /* end of pos */

   free_matrix(matrix);
}  /*  end of score_pseudos */
/*=======================================================================
      Count the number of differences b/w each seq & consensus
      (Puts result in first_block->sequences[seq].undefined) 
========================================================================*/
void cons_diff(bcur, first_block, sseq)
struct blocks_list *bcur;
Block *first_block;
struct seqseq *sseq;
{
   int width, seq, pos;
   Block *block;

      block = bcur->block;
      width = block->width;
      /*----- Count the number of differences in this block ------*/
      for (seq = 0; seq < block->num_sequences; seq++)
         block->sequences[seq].undefined = 0;
      for (seq = 0; seq < block->num_sequences; seq++)
         for (pos = 0; pos < width; pos++)
            if (bcur->consensus[pos] >= 0 && bcur->consensus[pos] < MAXAA &&
                (int) block->residues[seq][pos] != bcur->consensus[pos])
                   block->sequences[seq].undefined += 1;
      /*------Total diffs for all blocks accumulated in first block -----*/
      if (first_block != NULL && first_block != block)
      {
         /*--- put block sequences in same order as first_block -----*/
         order_seq(sseq, first_block, block);
         for (seq=0; seq < block->num_sequences; seq++)
             first_block->sequences[seq].undefined += 
                   block->sequences[ sseq[seq].seq ].undefined;
      }

}  /*   end of cons_diff */
/*=====================================================================
   Determine which sequence is most like the consensus
   and update seqprev fields with its distances between blocks
=======================================================================*/
void closest(blist, first_block, sseq)
struct blocks_list *blist;
Block *first_block;
struct seqseq *sseq;
{
   Block *block, *save_block;
   struct blocks_list *bcur;
   int i, mindiff, maxdiff, width, seq, save_seq, nb;
   struct sorttemp stemp[26];

   /*--------Determine the closest sequence -------------------------*/
   /*  (cumulative differences are stored in first_block)            */
   mindiff = 999; maxdiff = save_seq = 0;
   for (seq=0; seq < blist->nseq; seq++)
      if (first_block->sequences[seq].undefined < mindiff)
      {
         mindiff = first_block->sequences[seq].undefined;
         blist->minseq = seq;
      }
      else if (first_block->sequences[seq].undefined > maxdiff)
      {
         maxdiff = first_block->sequences[seq].undefined;
         blist->maxseq = seq;
      }
   printf("%s %s\tis first sequence most like consensus (%d %d)\n",
            first_block->family,
            first_block->sequences[blist->minseq].name, 
	    blist->minseq, mindiff);
   printf("%s %s\tis first sequence least like consensus (%d %d)\n",
            first_block->family,
            first_block->sequences[blist->maxseq].name, 
            blist->maxseq, maxdiff);

   /*  The order of the blocks in minseq may be different than the
       ABC order, if repeats were allowed by PROTOMAT; get the
       correct order in minseq */
   i = 0;
   bcur = blist->next;
   while(bcur != NULL && bcur->block != NULL)
   {
      block = bcur->block;
      if (block == first_block)
      {   seq = blist->minseq;  }
      else
      {
         order_seq(sseq, first_block, block);
         seq = sseq[blist->minseq].seq;
      }
      stemp[i].blist = bcur;
      stemp[i].position = bcur->block->sequences[seq].position;
      i++;
      bcur = bcur->next;
   }
   /*   sort by position  */
   qsort(stemp, blist->nblock, sizeof(struct sorttemp), sortcmp);
  
   bcur = blist;
   for (i=0; i< blist->nblock; i++)
   {
      bcur->next_min = stemp[i].blist;
      bcur = bcur->next_min;
   }
   bcur->next_min = NULL;

   /*-------------------------------------------------------------------*/
   /*------ Determine the distance between blocks in the closest seq ---*/
   save_seq = nb = 0;
   save_block = NULL;
   bcur = blist->next_min;
   while(bcur != NULL && bcur->block != NULL)
   {
      block = bcur->block;
      width = block->width;
/*
      if (block == first_block)
      if (bcur == blist->next_min)
      {
         bcur->minseqprev = block->sequences[blist->minseq].position - 1;
         save_seq = blist->minseq;
      }
      else
*/
      order_seq(sseq, first_block, block);
      seq = sseq[blist->minseq].seq;
      if (nb == 0)
      {
         bcur->minseqprev = block->sequences[seq].position - 1;
      }
      else
      {
         /*  from end to last block to start of this one ... */
         bcur->minseqprev = block->sequences[seq].position - 
    (save_block->sequences[save_seq].position + save_block->width);
      }
      save_seq = seq;
      save_block = block;
      bcur = bcur->next_min;
      nb++;
   }  /* end of block */

}  /* end of closest */

/*=======================================================================
  Imbed the consensus sequence for a list of blocks
    Extract blist->next->block->sequences[blist->minseq].name 
    If Profile exists, output a profile as well in PrfOutType format
========================================================================*/
void embed_consensus(blist, ofp, pfp, seq)
struct blocks_list *blist;
FILE *ofp, *pfp;
Sequence *seq;
{
   int sngle, aa, pos, savpos, totpos, sumpos, width, aacount[25];
   int firstpos, firstaa, lastaa, cobblen, cobbpos;
   long ltemp;
   double dtemp;
   struct blocks_list *bcur;
   Block *block, *first_block;
   char fam[MAXAC+1], ctemp[12], *cobbler, seqname[MAXNAME];
   Matrix *pssm, *target;

   /*  Try to find the sequence, unless it was passed in */
   if (seq == NULL)
   {  
	seq = read_seq(Fdb, blist->next->block->sequences[blist->minseq].name); 
	if (seq == NULL)
        { printf ("%s: %s not found\n", 
		blist->next->block->number,
		blist->next->block->sequences[blist->minseq].name);  }
   }
   if (seq != NULL) { strcpy(seqname, seq->name);  }
   else { strcpy(seqname, blist->next->block->sequences[blist->minseq].name); }

   /*   Compute length of block region = sumpos */
   sumpos = 0;
   bcur = blist->next;
   while(bcur != NULL && bcur->block != NULL)
   {
      block = bcur->block;
      width = block->width;
      sumpos += width + bcur->minseqprev;
      bcur = bcur->next;
   }

   /*----------------------------------------------------------------*/
   /*     Now print out the sequence in lower case with the consensus
          blocks embedded in upper case    */

   for (aa=0; aa < 25; aa++) aacount[aa] = 0;
   /*  Set the first and last aas to print */
   firstaa = 0; 
   first_block = NULL;
   if (seq != NULL) lastaa = seq->length;
   else             lastaa = sumpos;
   if (Short)
   {
      bcur = blist->next_min;
      /*  First block */
      if (bcur->block != NULL)
      {
         first_block = bcur->block;
         firstaa = bcur->minseqprev - ShortTrim;
         if (firstaa < 0) firstaa = 0;
      }
      lastaa = 0;
      while(bcur != NULL && bcur->block != NULL)
      {
         lastaa += bcur->minseqprev;
         lastaa += bcur->block->width;
         bcur = bcur->next_min;
      }
      lastaa += ShortTrim;
   }
   if (firstaa < 0) firstaa = 0;
   if (seq != NULL)
   {
      if (lastaa > seq->length) lastaa = seq->length;
      if (lastaa < firstaa)
      {   firstaa = 0; lastaa = seq->length;   }
   }
   if (lastaa < firstaa) { lastaa = sumpos;  }

   /*-------------------------------------------------------------------*/
   /*  seq is binary, so can't distinguish upper/lower case
       put the cobbler sequence part of seq in cobbler array      */
   cobblen = lastaa - firstaa;
   cobbler = (char *) malloc((cobblen+1) * sizeof(char));
   strcpy(fam, blist->next->block->family);
   pssm = NULL;
   if (PrfOutType >= 0)
   {
      pssm = new_matrix((cobblen+1));
      strcpy(pssm->id, "COBBLER; MATRIX");
      strcpy(pssm->number, fam);
      strcpy(pssm->ac, pssm->number);
      sprintf(pssm->de, "%s %s from %d to %d with embedded PSSM",
           fam, seqname, firstaa+1, lastaa);
      sprintf(pssm->ma, "width=%d seqs=%d",
         cobblen+1, blist->next->block->num_sequences);
   }
   
   /* -------------------Now the body-------------------------------- */
   /*  This ASSUMES the blocks are in order in the chosen sequence,
	but they may not be if PROTOMAT allowed repeats, etc
	Need to fix this - eg BL50006 in Blocks 9.0   */
   cobbpos = 0;
   totpos = firstaa;
   bcur = blist->next_min;
   while(bcur != NULL && bcur->block != NULL)
   {
      block = bcur->block;
      width = block->width;
      if (block == first_block) firstpos = firstaa;
      else firstpos = 0;
      /*   Before the block  */
      for (pos = firstpos; pos < bcur->minseqprev; pos++)
      {	
          /* Avoid problems; can happen when blocks out of order for seq */
          if (cobbpos < cobblen)
          {
          if (lastaa > totpos)
          {
             if (seq != NULL)
             { ctemp[0] = aa_btoa[seq->sequence[totpos]]; }
             else { ctemp[0] = 'X'; }
          }
          else { ctemp[0] = 'X'; }
          cobbler[cobbpos] = tolower(ctemp[0]);
	  sngle = aa_atob[ ctemp[0] ];
          aacount[sngle]++;
          if (pfp != NULL)
          {
             for (aa=0; aa < MATRIX_AA_WIDTH; aa++)
             { pssm->weights[aa][cobbpos] = Profile->weights[aa][sngle]; }
          }

          totpos++; cobbpos++;
          }
      }  /* end of region before block */

      /*   Within the block region */
      for (pos = 0; pos < width; pos++)
      {
	 /*  In rare cases the sequence ends before the block does */
         if (cobbpos < cobblen)
         {
         if (bcur->consensus[pos] >= 0 && bcur->consensus[pos] < MAXAA )
         {
              ctemp[0] = aa_btoa[ bcur->consensus[pos] ];
              cobbler[cobbpos] = aa_btoa[ bcur->consensus[pos] ];
              aacount[ bcur->consensus[pos] ]++;
         }
         else
         {		/* no consensus residue selected */
             cobbler[cobbpos] = 'x';
             aacount[ aa_atob['X'] ]++;
         }

         if (pfp != NULL)
         {
            /*	Uses the 1/3 bit PSSM made by insert_blist()    */
            /* For PSI, want cobbler sequence from 1/3 bit PSSM but pssm
               from target frequencies */
            if (PrfOutType == PRF_PSI)
            {
               target = block_to_matrix(bcur->block, 20);
               for (aa=0; aa < MATRIX_AA_WIDTH; aa++)
               { pssm->weights[aa][cobbpos] = target->weights[aa][pos]; }
               free_matrix(target);
            }
            else
            {
               for (aa=0; aa < MATRIX_AA_WIDTH; aa++)
               { pssm->weights[aa][cobbpos] = bcur->matrix->weights[aa][pos]; }
            }
         }
         }

         totpos++; cobbpos++;
      }  /* end of region within block */
      bcur = bcur->next_min;
   }  /* end of a block */

   /* ---Print the rest of the sequence now after the blocks------------- */
   if (lastaa > totpos)
   {
      savpos = totpos;
      for (pos = savpos; pos < lastaa; pos++)
      {
          if (seq != NULL)
          { ctemp[0] = aa_btoa[seq->sequence[pos]]; }
          else {ctemp[0] = 'X'; }
          cobbler[cobbpos] = tolower(ctemp[0]);
	  sngle = aa_atob[ ctemp[0] ];
          aacount[sngle]++;

          if (pfp != NULL)   /* same as PrfOutType >= 0 */
          {
             for (aa=0; aa < MATRIX_AA_WIDTH; aa++)
             { pssm->weights[aa][cobbpos] = Profile->weights[aa][sngle]; }
          }

          totpos++; cobbpos++;
      }
   }  /* end of if sequence was found */

   /*--------------------------------------------------------------------*/
   /*     Write the embedded sequence out */
   fprintf(ofp, ">%s %s from %d to %d with embedded consensus blocks\n",
           fam, seqname, firstaa+1, lastaa);
   for (pos=0; pos < cobblen; pos++)
   {
      fprintf(ofp, "%c", cobbler[pos]);
      if ((pos+1)%MAXWIDTH == 0) fprintf(ofp, "\n");
   }
   fprintf(ofp, "\n");

   /* Write the embedded PSSM out */
   switch (PrfOutType)
   {
      case PRF_SWAT:
         fprintf(pfp, "#COBBLER Profile for %s %s from %d to %d\n#\n      ",
            fam, seqname, firstaa+1, lastaa);
         for (aa=1; aa < 21; aa++) fprintf(pfp, "  %c  ", aa_btoa[aa]);
         fprintf(pfp, "\n");
         for (pos=0; pos < cobblen; pos++)
         {
            fprintf(pfp, "%c    ", cobbler[pos]);
            for (aa=1; aa < 21; aa++)
               fprintf(pfp, "%4d ", (int) round(pssm->weights[aa][pos]));
            fprintf(pfp, "\n");
         }
         break;
      case PRF_PSI:
         ltemp = (long) cobblen;
         fwrite(&ltemp, sizeof(long), 1, pfp);
         for (pos=0; pos < cobblen; pos++)
         {
            ctemp[0] = toupper(cobbler[pos]);
            fwrite(&ctemp, sizeof(char), 1, pfp);
         }
         for (pos=0; pos < cobblen; pos++)
         {
            for (aa=1; aa < 21; aa++)
            {
               dtemp = pssm->weights[aa][pos];
               fwrite(&dtemp, sizeof(double), 1, pfp);
            }
         }
         break;
      case PRF_PFS:
         fprintf(pfp, "(Peptide) Length: %d\n", cobblen );
         fprintf(pfp, "COBBLER Profile for %s %s from %d to %d\n",
           fam, seqname, firstaa+1, lastaa);
         fprintf(pfp, "Cons  ");
         for (aa=1; aa < 23; aa++) fprintf(pfp, "  %c  ", aa_btoa[aa]);
         fprintf(pfp,"Gap Len ..\n");
         for (pos=0; pos < cobblen; pos++)
         {
            fprintf(pfp, "%c    ", cobbler[pos]);
            for (aa=1; aa < 23; aa++)
               fprintf(pfp, "%4d ", (int) round(10.0 * pssm->weights[aa][pos]));
            fprintf(pfp, " 100 100\n");
         }
         fprintf(pfp, "*    ");
         for (aa=1; aa<23; aa++) fprintf(pfp, "%4d ", aacount[aa]);
         fprintf(pfp, "\n");
         break;
      case PRF_BLI:
	 output_matrix(pssm, pfp);
         break;
      default:
         break;
   }

   if (cobbler != NULL) free(cobbler);
   if (seq != NULL) free_sequence(seq);
/*
   if (pssm != NULL) output_matrix_s(pssm, stdout, FLOAT_OUTPUT);
*/
   if (pssm != NULL) free_matrix(pssm);
}   /* end of embed_consensus  */
/*======================================================================
   Find first sequence with any part of name == "name", 
	assumes name parts are separated by "|" and input file is fasta
========================================================================*/
Sequence *read_seq(fin, name)
FILE *fin;
char *name;
{
   Sequence *seq;
   char ctemp[MAXNAME], *ptr;
   int done;

   /*  After rewind, read_a_sequence() lbuff[] is not reset, so must
       read to eof before rewinding. See notes in blimps/sequences.c */
   while ( !feof(fin) )
   {  seq = read_a_sequence(fin, FASTA, AA_SEQ); }
   rewind(fin);

/*
printf("\nread_seq(%s)\n", name);
fgets(ctemp, sizeof(ctemp), fin);
printf("%s\n", ctemp);
rewind(fin);
*/

   done = NO;
   seq = NULL;
   while ( !done && ((seq = read_a_sequence(fin, FASTA, AA_SEQ)) != NULL) )
   {
        if (strcmp(name, seq->name) == 0) { done = YES; }
        else
        {
           strcpy(ctemp, seq->name);
           ptr = strtok(ctemp, "|");
           while (ptr != NULL && strstr(name, ptr) == NULL)
	   {	ptr = strtok(NULL, "|");  }
           if (ptr == NULL)
	   {   free_sequence(seq); seq = NULL;   }
           else
           {    done = YES;   }
        }
   }
   return(seq);
}  /*  end of read_seq */

/*=======================================================================
  Print out the consensus sequence for a list of blocks
========================================================================*/
void print_consensus(blist, ofp)
struct blocks_list *blist;
FILE *ofp;
{
   int pos, itemp, width;
   struct blocks_list *bcur;
   Block *block;

   bcur = blist->next_min;
   while(bcur != NULL && bcur->block != NULL)
   {
      block = bcur->block;
      width = block->width;
      /*------- Print leading X's for minimum preceding distance ------*/
      for (pos = 0; pos < bcur->minseqprev; pos++)
      {
          fprintf(ofp, "X");
          if ( (pos+1)%MAXWIDTH == 0) fprintf(ofp, "\n");
      }
      itemp = (int) bcur->minseqprev/MAXWIDTH;
      itemp = bcur->minseqprev - itemp * MAXWIDTH;
      if ((itemp + width) > MAXWIDTH) fprintf(ofp, "\n");
  
      for (pos = 0; pos < width; pos++)
      {
         if (bcur->consensus[pos] >= 0 && bcur->consensus[pos] < MAXAA )
              fprintf(ofp, "%c", aa_btoa[ bcur->consensus[pos] ]);
         else
              fprintf(ofp, "x");
      }
      fprintf(ofp, "\n");
      bcur = bcur->next_min;
   }  /* end of block */
   fprintf(ofp, "\n");
}   /*  end of print_consensus  */

/*=======================================================================
      Turn a block into a random consensus sequence
========================================================================*/
void make_random(blist, ofp)
struct blocks_list *blist;
FILE *ofp;
{
   double rrand;
   int seq, pos, width, nseq, randaa[MAXWIDTH], itemp;
   struct blocks_list *bcur;
   Block *block;

   bcur = blist->next;
/*
   if (bcur->block != NULL)
       fprintf(ofp, ">%s %s\n", bcur->block->number, bcur->block->de);
*/
   while (bcur !=NULL && bcur->block != NULL)
   {
      block = bcur->block;
      width = block->width;
      nseq = block->num_sequences;
      for (pos = 0; pos < width; pos++)
      { 
         rrand = (double) ran0() / (RAND_MAX + 1.0);  /* bw 0.0 and 1.0 */
         seq = (int) (nseq - 1) * rrand;
         randaa[pos] = block->residues[seq][pos];
      }

      /*------- Print leading X's for minimum preceding distance ------*/
      for (pos = 0; pos < bcur->minprev; pos++)
      {
          fprintf(ofp, "X");
          if ( (pos+1)%MAXWIDTH == 0) fprintf(ofp, "\n");
      }
      itemp = (int) bcur->minprev/MAXWIDTH;
      itemp = bcur->minprev - itemp * MAXWIDTH;
      if ((itemp + width) > MAXWIDTH) fprintf(ofp, "\n");
  
      /*--------Print the consensus block now ---------------------------*/
      for (pos = 0; pos < width; pos++)
      {
         if (randaa[pos] >= 0 && randaa[pos] < MAXAA )
            fprintf(ofp, "%c", aa_btoa[randaa[pos]]);
         else fprintf(ofp, "x");
      }
      fprintf(ofp, "\n");
      bcur = bcur->next;
   }  /*  end of block */
}  /* end of make_random */
/*=================================================================
   From Press, et al, "Numerical Recipes in C", pp.207-208
   Improved random number generator (breaks up sequential correlations)

   Assumes random number generator has been seeded elsewhere (does
   not call srand).
   Assumes caller will divide results by RAND_MAX+1 (so it can be
   used the same as rand()).
========================================================================*/
int ran0()
{
   static int y, v[98];
   static int iff=0;			/* first call flag */
   int j;

   if (iff == 0)	/* fill array on first call */
   {
      iff=1;
      for (j=0; j<=97; j++) v[j] = random();
      y=random();
   }
   j = 1 + 97.0 * y / (RAND_MAX+1.0);
   if (j >=0 && j <= 97)
   {
      y=v[j];
      v[j] = random();
   }
   else y=random();
   return (y);
}  /* end of ran0 */
/*=======================================================================
     routines for a list of blocks
========================================================================*/
struct blocks_list *make_blist()
{
   struct blocks_list *new;
   
   new = (struct blocks_list *) malloc (sizeof(struct blocks_list));
   new->nblock = new->nseq = new->totwidth = new->minprev = new->maxprev = 0;
   new->minseq = new->minseqprev = new->query_pos = 0;
   new->maxseq = new->maxseqprev = 0;
   new->consensus = NULL;
   new->block = NULL;
   new->matrix = NULL;
   new->next = new->next_min = NULL;

   return(new);
}  /* end of make_blist */

/*---------------------------------------------------------------------
       Inserts the block in a list of blocks
       Computes the 1/3 bit PSSM for the block
-----------------------------------------------------------------------*/
void insert_blist(blist, block)
struct blocks_list *blist;
Block *block;
{
   struct blocks_list *cur;
   char *ptr, ctemp[MAXNAME];
   double sum;
   int seq;

   /*------ Accumulate totals in header record ------*/
   blist->nblock += 1;
   blist->nseq = block->num_sequences;
   blist->totwidth += block->width;

   /*--- Insert a new record for the current block at the end of the list ---*/
   cur = blist;
   while (cur->next != NULL)
      cur = cur->next;

   cur->next = make_blist();
   cur->next_min = cur->next;
   cur->next->consensus = (int *) malloc(block->width * sizeof(int));
   cur->next->block = block;

   /*  Make sure the block has sequence weights in it;
	block_to_matrix() takes care of this, but other routines
	here might get caught */
   sum = 0.0;
   for (seq=0; seq < block->num_sequences; seq++)
   {    sum += block->sequences[seq].weight;   }
   if (sum <= 0.0)
   {  pb_weights(block);  }

   /*  Following requires global vars Qij and frequency[]  */
   /*   NOTE:  For Gribskov-type PSSM change 6 to 30 in block_to_matrix()
               and Qij should be a substitution matrix    */
   if (Qij != NULL)
      cur->next->matrix = block_to_matrix(block, 6);	/* third bit PSSM */
   /*------- get minimum preceding distance ------*/
   cur->next->minprev = cur->next->maxprev = 0;
   strcpy(ctemp, block->ac);
   ptr = strtok(ctemp, "(");
   if (ptr != NULL)
   {
      ptr = strtok(NULL, ",");
      if (ptr != NULL) cur->next->minprev = atoi(ptr);
   }
   if (cur->next->minprev < 0) cur->next->minprev = 0;
   
}  /* end of insert_blist */

void free_blist(blist)
struct blocks_list *blist;
{
   struct blocks_list *cur, *last;

   cur = last = blist;
   while (cur->next != NULL)
   {
      last = cur;  cur = cur->next;
   }
   if (cur != blist)
   {
      if (cur->consensus != NULL) free(cur->consensus);
      if (cur->block!= NULL) free_block(cur->block);
      if (cur->matrix != NULL) free_matrix(cur->matrix);
      free(cur);
      last->next = last->next_min = NULL;
      free_blist(last);
   }
   else free(blist);

}  /* end of free_blist */
/*=======================================================================*/
/*  Sequences may not be in the same order both blocks. If not, then
    set sseq[s1] = s2  where
    b1->sequences[s1].name == b2->sequences[s2].name
=========================================================================*/
void order_seq(sseq, b1, b2)
struct seqseq *sseq;
Block *b1, *b2;
{
   int nseq, i1, i2;

   nseq = b1->num_sequences;
   if (b2->num_sequences < nseq) nseq = b2->num_sequences;
   for (i1 = 0; i1 < nseq; i1++)
   {
      if (b1 == b2) sseq[i1].seq = i1;
      else
      {
         sseq[i1].seq = -1;
         i2 = 0;
         while (sseq[i1].seq < 0 && i2 < nseq)
         {
            if (strcmp(b1->sequences[i1].name, b2->sequences[i2].name) == 0)
               sseq[i1].seq = i2;
            i2++;
         }
      }
   }
}  /*  end of order_seq */
/*======================================================================
	Read an integer-valued substitution matrix
	MAXAA == 25 - should use 24 here?
========================================================================*/
void load_sij(fin, scores)
FILE *fin;
int scores[MAXAA][MAXAA];
{
   char line[132], *ptr;
   int alpha[MAXAA], nrows, ncols, row, col, i;

/*----------Read file until first non-blank line --------------*/
/* Skip comments at beginning of file - 1st char = #, > or ;   */
   line[0] = '\0';
   while (((int) strlen(line) < 1 || line[0]=='#' || line[0]=='>' || 
                                     line[0]==';')
          && fgets(line, sizeof(line), fin) != NULL)
	    ;
/*------See if the first line has characters on it ------------*/
   for (col=0; col < MAXAA; col++) alpha[col] = -1;
   if (strstr(line, "A") != NULL)	/* This line has characters */
   {
      row = 0;	/* # of alphabetic characters on the line */
      for (i=0; i< (int) strlen(line); i++)
      {
	 col = aa_atob[ line[i] ];
	 if (col >= 0 && col < MAXAA)
	 {
	    alpha[row] = col;
	    row++;
	 }
	 else if (isalpha(line[i])) row++;
      }
   }
/*-------Get the data values now ------------*/
   for (row=0; row<MAXAA; row++)
     for (col=0; col<MAXAA; col++)
	scores[row][col] = -999;		/* Null value */
   nrows = 0;
   line[0] = '\0';
   while (fgets(line, sizeof(line), fin) != NULL)
   {
      if ((int) strlen(line) > 1 && nrows < MAXAA)
      {
	 if (alpha[nrows] >= 0 && alpha[nrows] < MAXAA)
	 {
	    row = alpha[nrows]; ncols = 0;
	    ptr = strtok(line, " ,\n");
	    while (ptr != NULL)
	    {
	       if (strspn(ptr, "+-0123456789") == strlen(ptr))
	       {
		  col = alpha[ncols];
		  if (col >= 0 && col < MAXAA)
		     scores[row][col] = atoi(ptr);
		  ncols++;
	       }
	       ptr = strtok(NULL, " ,\n");
	    }
	 }
	 nrows++;
      }
   }

/*-------If some entries are still missing, assume symmetry ---------*/
   for (row=0; row<MAXAA; row++)
   {
     for (col=0; col<MAXAA; col++)
     {
	if (scores[row][col] == -999) scores[row][col] = scores[col][row];
	if (row==20 && scores[row][col]==-999)	/*  B -> D */
	   scores[row][col] = scores[3][col];
	if (row==21 && scores[row][col]==-999)	/* Z -> E */
	   scores[row][col] = scores[6][col];
	if (col==20 && scores[row][col]==-999)	/*  B -> D */
	   scores[row][col] = scores[row][3];
	if (col==21 && scores[row][col]==-999)	/* Z -> E */
	   scores[row][col] = scores[row][6];
     }
   }

}  /* end of load_sij */
/*=======================================================================
   Read in query sequence, align it with blocks, embed consensus
   at alignment points if all blocks align in the right order without
   overlapping.
========================================================================*/
void query_consensus(blist, ofp, pfp)
struct blocks_list *blist;
FILE *ofp, *pfp;
{
   int lastpos, sumpos, width;
   Sequence *seq;
   struct blocks_list *bcur;

   seq = read_a_sequence(Fsq, UNI, AA_SEQ);
   rewind(Fsq);

   /*   If query is shorter than the sum of the block widths
        just print a message  */
   sumpos = 0;
   bcur = blist->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      /* don't count the aas before the first block */
      if (bcur != blist->next) sumpos += bcur->minprev;
      sumpos += bcur->block->width;
      bcur = bcur->next;
   }
   if (seq->length < sumpos)
   {
      printf("Query sequence %s is shorter than minimum path width\n",
              seq->name);
      free_sequence(seq);
      seq = NULL;
   }
 
   /*--------------------------------------------------------------------*/
   /*   Align the sequence with each block & get the value of
        minseqprev  */
   lastpos = 0;
   if (seq != NULL)
   {
      bcur = blist->next;
      while (bcur != NULL && bcur->block != NULL)
      {
         width = bcur->block->width;
/*       matrix = block_to_matrix(bcur->block, 3);   scaled 0-99  */
/*		Uses 1/3 bit PSSM made by insert_blist()          */
         bcur->query_pos = best_pos(bcur->matrix, seq); 
         if (bcur->query_pos < lastpos - 1)
         {
            printf("WARNING: Aligned blocks overlap!!! ");
            printf(" block=%s, pos=%d, last pos = %d\n",
             bcur->block->number, bcur->query_pos, lastpos);
         }
	 else
	 {
            printf(" block=%s, pos=%d\n",
             bcur->block->number, bcur->query_pos);
         }
         bcur->minseqprev = bcur->query_pos - lastpos;
         lastpos = bcur->query_pos + width;
         bcur = bcur->next;
      }

      /*------------------------------------------------------------*/
      embed_consensus(blist, ofp, pfp, seq);    /* frees seq */

   } /*  end of if sequence was found */
  
}   /*  end of query_consensus */
/*=======================================================================
            Score all alignments of matrix with sequence & return
	     position of best alignment
============================================================================*/
int best_pos(matrix, seq)
Matrix *matrix;
Sequence *seq;
{
  int scan_pos, seq_pos, mat_pos, best_position;
  double seq_score, max_seq_score;

  max_seq_score = 0.0;
  best_position = -999;
  
  /* for every alignment of the matrix and sequence score the alignment. */
  /* there are (seq_length + matrix_length  - 1) different alignments. */
  /* Note: seq_pos is the relative position of the 1st matrix column to the */
  /*       1st sequence column */
  /* Note: the indexing is shifted to make the calculation of scan_pos */
  /*       (see below) easier/faster */
  for (seq_pos= -matrix->width+1; seq_pos < seq->length; seq_pos++) {


    /* 
     * score this alignment
     */
    seq_score = 0.0;

    /* for each position in the matrix add the weight to the total score */
    /* Note: mat_pos is the current column of the matrix */
    for (mat_pos=0; mat_pos < matrix->width; mat_pos++) {

      /* make sure the sequence and the matrix overlap at this point */
      /* Note: scan_pos is where the current matrix column is in the */
      /*       sequence */
      scan_pos = seq_pos + mat_pos;
      if ((scan_pos >= 0) && (scan_pos < seq->length)) { /* if in the seq */
	seq_score += matrix->weights[ seq->sequence[scan_pos] ][mat_pos];
      }
      else {			/* not in the sequence */
	seq_score += matrix->weights[aa_atob['-']][mat_pos]; 
	/* score as if it was a gap */
      }

    } /* end score this alignment */

/*    alignments_done++; */
    
      if (seq_score > max_seq_score) {
	max_seq_score = seq_score;
	best_position = seq_pos;
      }
    
  } /* end of pairwise lineup */
  
  return(best_position);

}  /* end of best_pos */
/*========================================================================
        Figure out the consensus for this block & store it in the
        block list record - use highest PSSM score
        Prints x if the best total score is negative
=========================================================================*/
void pssm_consensus(bcur)
struct blocks_list *bcur;
{
   double maxw;
   int aa1, seq1, pos, width, nseq, maxaa, aamark[MAXAA+1];
   Block *block;

   block = bcur->block;
   width = block->width;
   nseq = block->num_sequences;

   /*-------------------------------------------------------------------*/
   for (pos = 0; pos < width; pos++)
   { 
      /*    good sums can be negative */
      maxw = -999999.99;
      maxaa = -1;
      for (aa1 = 0; aa1 < MAXAA+1; aa1++)
         aamark[aa1] = NO;	/* does this aa appear in the pos ? */
  
      /*   Mark the aas that appear in this position */
      for (seq1 = 0; seq1 < nseq; seq1++)
      {
            aa1 = block->residues[seq1][pos];
            if (aa1 >= 0 && aa1 <= MAXAA)
               aamark[aa1] = YES;
      }

      /*    Only choose among the aas that appear in the pos  */
      for (aa1 = 0; aa1 < MAXAA; aa1++)
         if ( aamark[aa1] )
         {
            if (bcur->matrix->weights[aa1][pos] > maxw)
            {   maxw = bcur->matrix->weights[aa1][pos]; maxaa = aa1;  }
         }

      if (maxw <= 0.0) maxaa = MAXAA;	/*  Use X if non-positive sum */

      /*--------Save the consensus sequence------------------------*/
      if (maxaa > 0 && maxaa < MAXAA )
           bcur->consensus[pos] = maxaa;
      else
           bcur->consensus[pos] = MAXAA;	/* X==23 */
   }  /* end of pos */

}  /*  end of pssm_consensus */
/*=======================================================================*/
int sortcmp(t1, t2)
struct sorttemp *t1, *t2;
{
   return(t1->position - t2->position);
}  /* end of sortcmp */

/*========================================================================
   Write out the results
   Need: ac, sequence name, firstaa & lastaa for heading;
	 length, cobbler sequence, pssm values
	 aacount[] for PRF_PFS
=========================================================================*/
void write_cobbler(cfp, pfp, cobbler, pssm)
FILE *cfp, *pfp;
char *cobbler;
Matrix *pssm;
{
   int cobblen;
 
   cobblen = (int) strlen(cobbler);

}  /* end of write_cobbler */
/*========================================================================
	Retrieve & print a sequence
	blist->minseq is ML, blist->maxseq is LL
========================================================================*/
void get_seq(fam, name, fdb, fout)
char *fam, *name;
FILE *fdb, *fout;
{
   Sequence *seq;
   char seqname[MAXNAME];

   /* read_seq rewinds fdb */
   seq = read_seq(fdb, name); 
   if (seq == NULL)
   { 
      printf ("Sequence %s not found\n", name);
   }
   else
   {
      sprintf(seqname, "%s %s", fam, name);
      strcpy(seq->name, seqname);
      output_sequence(seq, fout);
   }
}  /* end of get_seq */
