/* see mablockprob.in - decides on MSF format now */
/* MABLOCK Copyright 1999-2003 Fred Hutchinson Cancer Research Center
   Read a file of multiple alignmentss in fasta format or clustalw format 
   and carve out blocks at least MinWidth wide. clustalw format alignments
   must have the word "CLUSTAL" on the first non-blank line.
   Recognizes when input file is already in blocks format.

   NOTES: Requires version 3.2.6 or higher of BLIMPS libraries.

   mablock <input m.a. file> <output_file> <output_type> [<MinWidth> <MaxWidth>]
	Puts blocks in output_file.blks & sequences in output_file.seqs
--------------------------------------------------------------------
11/16/97  J. Henikoff
11/25/97  MAXWIDTH of output block = 55
12/26/97  Check input file for blocks format first
12/30/97  Modified insert_blist() if > 26 blocks in list
 1/ 2/98  Modified try_clustal() to look out for lines with asterisks
 3/18/98  Modified find_blocks() to NOT treat B,Z,X as gaps
 5/13/98  Try MSF format.
 1/15/99  Increased MAXLEN for Prodom 99.1
 1/26/99  Edit sequence names for clustalw; 1st 10 chars unique, 
		don't start with P1; (see makelis.c)
 2/16/99  Clustal or CLUSTAL on 1st non-blank line => clustal format
 6/20/99  Longer sequence names
12/17/99  Longer ACs
 1/ 8/01  Wider blocks (MAXWIDTH -> EXTRA_LARGE_BUFF)
 1/ 9/01  Added MaxWidth & output_fasta(), put temp files in output dir
 7/ 5/01  Fix problem with msf format when seq names right-justified
12/18/02  Accomodate "STOCKHOLM" format (like CLUSTAL)
 4/30/03  Read until > (FASTA), CLUSTAL, STOCKHOLM or // (MSF)
 8/19/03  Fix "distance from previous block" format error
12/23/06  Longer sequence names
====================================================================*/

#define EXTERN
#define MAXNAME 80	/* Maximum file name length */
#define MINWIDTH 10	/* Minimum block width */
#define MAXWIDTH 55	/* Maximum block width */
#define MAXSEQ 400	/* Maximum number of sequences */
#define MAXLEN 800	/* Max line length for clustal format */
#define SNAMELEN 20	/* Max sequence name length */

#include <blocksprogs.h>
/*	Tried following for basename() & dirname(), didn't work
#include <libgen.h>
	See /usr/man/man3g/
*/

/*  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 minprev;				/* min distance from previous block*/
   int seqprev;				/* previous distance for minseq */
   int *consensus;			/* consensus for this block */
   Block *block;
   struct blocks_list *next;
};

struct seqseq {			/* reorder sequences */
   int seq;
   int pos;
};

int try_clustal();
int try_msf();
struct blocks_list *find_blocks();
Block *make_block();
struct blocks_list *make_blist();
void insert_blist();
void free_blist();
void fasta_seqs();
void order_seq();
void fix_names();
void output_blocks();
void output_fasta();

int MinWidth;		/* Minimum block width */
int MaxWidth;		/* Maximum block width */
char OutName[MAXNAME];
char Block_AC[MAXNAME];
char Block_ID[MAXNAME];
char Block_DE[MAXNAME];
double frequency[MATRIX_AA_WIDTH];    /* for frequency.c */

/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
   FILE *ifp, *bfp, *sfp;
   Sequence *seqs[MAXSEQ];
   char ctemp[MAXNAME], bdbname[MAXNAME], outtype[10], *ptr;
   int db_type, seq_type, nseq, i, bflag, ftype;
   struct blocks_list *blocks;
   Block *block;

   ErrorLevelReport = 4;	/* BLIMPS error level */

   if (argc < 4)
   {
      printf("MABLOCK: Copyright 2001 Fred Hutchinson Cancer Research");
      printf(" Center\nUSAGE: mablock <input> <out> <type> <min width> <max width>\n");
      printf("   <input> = file of fasta or clustal multiple alignments\n");
      printf("             Clustal format must have CLUSTAL on first non-blank line\n");
      printf("   <out>   = output file of blocks\n");
      printf("             Puts blocks in out.blks & sequences in out.seqs\n");
      printf("   <type>   = output type, B=blocks, F=fasta\n");
      printf("   <min width> = minimum ungapped width to define a block\n");
      printf("   <max width> = maximum block width\n");
   }
/* ------------1st arg = blocks database -------------------------------*/
   if (argc > 1)
      strcpy(bdbname, argv[1]);
   else
   {
      printf("\nEnter name of blocks database: ");
      gets(bdbname);
   }
   if ( (ifp=fopen(bdbname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", bdbname);
      exit(-1);
   }
   /*  should be smarter about this!  */
   strcpy(ctemp, bdbname);
   ptr = strrchr(ctemp, '/'); /* find the last slash */
   if (ptr != NULL) strcpy(Block_AC, ptr); 
   else             strcpy(Block_AC, bdbname); 
   if (strlen(Block_AC) > MAXAC-1)
        Block_AC[MAXAC-1] = '\0';	/* leave one char for A-Z */
   else if (strlen(Block_AC) < MINAC)
   {
      for (i=strlen(Block_AC); i < MINAC; i++) Block_AC[i] = 'x';
      Block_AC[MINAC] = '\0';
   }
   for (i=0; i<strlen(Block_AC); i++)
      if (!isalnum(Block_AC[i])) Block_AC[i] = 'x';
   sprintf(Block_ID, "%s; BLOCK", Block_AC);
   strcpy(Block_DE, bdbname);

/* ------------2nd arg = output file  -----------------------------------*/
   if (argc > 2)
      strcpy(OutName, argv[2]);
   else
   {
      printf("\nEnter name of output file: ");
      gets(OutName);
   }
   strcpy(ctemp, OutName); strcat(ctemp, ".blks");
   if ( (bfp=fopen(ctemp, "w")) == NULL)
   {
      printf("\nCannot open file %s\n", ctemp);
      exit(-1);
   }
   strcpy(ctemp, OutName); strcat(ctemp, ".seqs");
   if ( (sfp=fopen(ctemp, "w")) == NULL)
   {
      printf("\nCannot open file %s\n", ctemp);
      exit(-1);
   }
/* ------------3rd arg = output type  -----------------------------------*/
   sprintf(outtype, "B"); ftype = 0;
   if (argc > 3) {  strcpy(outtype, argv[3]);  }
   else
   {
      printf("\nEnter type of output file [B=blocks|F=fasta]: ");
      gets(outtype);
   }
   if (outtype[0] == 'F' || outtype[0] == 'f') ftype = 1;
/* ------------4th arg = optional min trimming size--------------------*/
   MinWidth = MINWIDTH;
   if (argc > 4) MinWidth = atoi(argv[4]);
   if (MinWidth < 4 || MinWidth > EXTRA_LARGE_BUFF)
	 MinWidth = MINWIDTH;

/* ------------5th arg = optional max trimming size---------------------*/
   MaxWidth = MAXWIDTH;
   if (argc > 5) MaxWidth = atoi(argv[5]);
   if (MaxWidth < 3 || MaxWidth > EXTRA_LARGE_BUFF) 
	MaxWidth = MAXWIDTH;

/*=====================================================================*/
   /*  1. Is input already blocks?  2. Is it a file of sequences?
    3. Is it CLUSTAL/STOCKHOLM format? */
   /*-----------------------------------------------------------------*/
   blocks = NULL;
   nseq = 0;
   /*   Check first for input file of blocks */
   bflag = FALSE;
   while ( (block = read_a_block(ifp)) != NULL)
   {
      bflag = TRUE;
      if (blocks == NULL) blocks = make_blist();
      insert_blist(blocks, block);
   /*-----------Now should read blocks to fill seqs[], see code in
          blalign:fasta_seqs()  */
   }

   if (!bflag)
   {
      rewind(ifp);

      /*-----------------------------------------------------------------*/
      /*   Check next for input file of sequences & assume are aligned */
      db_type = type_dbs(ifp, DbInfo);
      /* shouldn't send seq_type_dbs() a negative value of db_type,
	 but need to preserve if for next paragraph */
      seq_type = UNKNOWN_SEQ;
      seq_type = seq_type_dbs(ifp, DbInfo, db_type, seq_type);
      if (seq_type == NA_SEQ)
      {
         printf("WARNING: Sequences appear to be DNA but will be treated");
         printf(" as protein\n");
         seq_type = AA_SEQ;
      }
      rewind(ifp);
   
      /*-----------------------------------------------------------------*/
      /*   First read all the sequences into memory                    */
      if (db_type >= 0)
      {
         while ( nseq < MAXSEQ &&
             (seqs[nseq] = read_a_sequence(ifp, db_type, seq_type)) != NULL)
         {
            nseq++;
         }
      }
      else
      {
         nseq = try_clustal(ifp, seqs);
         if (nseq <= 0)
         {  nseq = try_msf(ifp, seqs);    }
      }
      fix_names(nseq, seqs);

      /*  Now look for ungapped regions in all the sequences  */
      if (nseq > 0)
      {
         /*>>>>> compute an array of column scores first <<<<<*/
         blocks = find_blocks(nseq, seqs);
      }
   }  /*  end of not blocks input */
   fclose(ifp);
  

   /*-----------------------Output results now ----------------------*/
/*
   printf("%s:  %d sequences read, ", OutName, nseq);
*/
   if (nseq == MAXSEQ)
   { printf("WARNING: Maximum number of sequences = %d\n", nseq); }
   if (nseq > 0)
   {
      for (i=0; i<nseq; i++) output_sequence(seqs[i], sfp);
      fprintf(sfp, "\n\n");
   }
   fclose(sfp);

   if (blocks != NULL)
   {
      if (ftype == 0)
      {
         output_blocks(blocks, bfp);
      }
      else
      {
         output_fasta(blocks, bfp);
      }
      printf("%d blocks found with minimum width %d and maximum with %d\n",
             blocks->nblock, MinWidth, MaxWidth );
      printf("Output written to %s.blks and %s.seqs\n", OutName, OutName);
   }
   else
   {
      printf("no blocks found with minimum width %d\n",
              MinWidth );
   }
   fclose(bfp);

   exit(0);

}  /* end of main */
/*========================================================================
	Find ungapped stretches at least MinWidth long in all sequences
        Treating all characters other than the 20 real aas (1-20) and
	B (21), Z (22), X (23) as gaps
	Gap character == '-' (0)
        Arbitrarily ends at block when width reaches MAXWIDTH; need to
        split blocks more elegantly, perhaps based on a column score.
        Means have to pre-compute an array of column scores from seqs[].
========================================================================*/
struct blocks_list *find_blocks(nseq, seqs)
int nseq;
Sequence *seqs[MAXSEQ];
{
   struct blocks_list *blocks;
   Block *block;
   int length, s, pos, spos, width, firstpos;

   blocks = make_blist();

   length = seqs[0]->length;
   for (s=1; s<nseq; s++)
   {
      if (seqs[s]->length < length)
      {
         length = seqs[s]->length;
         fprintf(stderr,
             "WARNING: sequence segments are of different lengths\n");
         fprintf(stderr, "%s %d : %s %d\n", 
   	     seqs[0]->name, seqs[0]->length, seqs[s]->name, seqs[s]->length);
       
      }
   }

   width = firstpos = 0;
   for (pos=0; pos < length; pos++)
   {
      spos = 0;
      for (s=0; s< nseq; s++)
         if (seqs[s]->sequence[pos] > 0 &&
             seqs[s]->sequence[pos] < 24 )
               spos++;
      if (spos == nseq) { width++; }
      /*   This pos has a gap, did we find a block ? */
      if ( spos < nseq || (spos == nseq && width == MaxWidth) )
      {
         if (width >= MinWidth)
         /*    Make a block starting at firstpos  */
         {
            block = make_block(width, firstpos, nseq, seqs);
            insert_blist(blocks, block);
         }
         firstpos = pos + 1;
         width = 0;
      }
   } /* end of sequence position */

   /*   Final block may run to end of sequences */
   if (width >= MinWidth)
   /*    Make a block starting at firstpos  */
   {
      block = make_block(width, firstpos, nseq, seqs);
      insert_blist(blocks, block);
   }

   return(blocks);

}  /*  end of find_blocks */
/*========================================================================
 Try clustal format, which has multiple lines per sequence with the
 sequence name in the first 16 columns of each line.
 Writes each set of sequence segments out to a temporary file, then
 reads them back in & appends them to sequence array.
 Assumes each set of segments is separarated by at least one blank line.
 Assumes each line of a set contains the sequence name, whitespace, residues:

CLUSTAL W(1.60) multiple sequence alignment

JC2395          NVSDVNLNK---YIWRTAEKMK---ICDAKKFARQHKIPESKIDEIEHNSPQDAAE----
KPEL_DROME      MAIRLLPLPVRAQLCAHLDAL-----DVWQQLATAVKLYPDQVEQISSQKQRGRS-----
FASA_MOUSE      NASNLSLSK---YIPRIAEDMT---IQEAKKFARENNIKEGKIDEIMHDSIQDTAE----

JC2395          -------------------------QKIQLLQCWYQSHGKT--GACQALIQGLRKANRCD
KPEL_DROME      -------------------------ASNEFLNIWGGQYN----HTVQTLFALFKKLKLHN
FASA_MOUSE      -------------------------QKVQLLLCWYQSHGKS--DAYQDLIKGLKKAECRR

JC2395          IAEEIQAM
KPEL_DROME      AMRLIKDY
FASA_MOUSE      TLDKFQDM

============================================================================*/
int try_clustal(ifp, seqs)
FILE *ifp;
Sequence *seqs[MAXSEQ];
{
   FILE *tmp;
   Sequence *tmp_seq;
   int nseq, this_seq, i, my_pid, db_type, seq_type;
   char line[MAXLEN], tmp_name[MAXNAME], *name, *residues;

   nseq = 0; seqs[0] = NULL;

   /*  Look for the word clustal on the first non-blank line, if find it,
	skip to next non-blank line and begin  */
   while (!feof(ifp) && fgets(line, MAXLEN, ifp) != NULL &&
           strstr(line, "CLUSTAL") == NULL &&
           strstr(line, "STOCKHOLM") == NULL)
		;
   if (strstr(line, "CLUSTAL") == NULL && strstr(line, "Clustal") == NULL &&
       strstr(line, "STOCKHOLM") == NULL && strstr(line, "Stockholm") == NULL)
   {
      rewind(ifp);
      return(nseq);
   }
   else
   {
      fprintf(stderr, "Trying CLUSTAL/STOCKHOLM format.\n");
      fgets(line, MAXLEN, ifp);
   }

   /*  Read sequences until the next blank line, or until a line
       with blanks at the start
	then start over and add to existing sequences, etc.  */
   my_pid = getpid();
   sprintf(tmp_name, "%s.%d", OutName, my_pid);
   tmp=fopen(tmp_name, "w");
   while (!feof(ifp))
   {
      /*   Write a set of sequence segments to a temp file */
      /*  NOTE: Blank lines from netscape can be "^M\n\0", len=3 */
      while (!feof(ifp) && fgets(line, MAXLEN, ifp) != NULL &&
           strlen(line) > (int) 3 )
      {
         if (line[0] != ' ')        /* some lines of asterisks, etc */
         {
            name = strtok(line, " \t");
            residues = strtok(NULL, " \t\r\n");
/*printf("%s %s\n", name, residues); */
            if (tmp != NULL)
            {
               if (name != NULL && residues != NULL)
               {
                   fprintf(tmp, ">%s\n", name);
		   fprintf(tmp, "%s\n", residues);
               }
               else
               {   fprintf(stderr,"ERROR reading Clustal file\n%s", line);  }
            }
         }
      } /* end while */
      /*   Encountered a blank line or eof */
      fclose(tmp);
      if ( (tmp = fopen(tmp_name, "r")) != NULL)
      {
         db_type = type_dbs(tmp, DbInfo);
         seq_type = UNKNOWN_SEQ;
         seq_type = seq_type_dbs(tmp, DbInfo, db_type, seq_type);
         if (seq_type == NA_SEQ)
         {
            printf("WARNING: Sequences appear to be DNA but will be treated");
            printf(" as protein\n");
            seq_type = AA_SEQ;
         }
         if (seqs[0] == NULL)
         {
         /*   initialize sequences */
            nseq = 0;
            if (db_type >= 0)
            {
               while ( nseq < MAXSEQ &&
                   (seqs[nseq] = read_a_sequence(tmp, db_type, seq_type)) != NULL)
               {
                  nseq++;
               }
            }
         }
         else
         /*     add on to existing sequences */
         {
            this_seq = 0;
            if (db_type >= 0)
            {
               while ( this_seq < MAXSEQ &&
                   (tmp_seq = read_a_sequence(tmp, db_type, seq_type)) != NULL)
               {
                  if (seqs[this_seq] != NULL &&
                      strcmp(seqs[this_seq]->name, tmp_seq->name) == 0 )
                  {
                     if ((seqs[this_seq]->length + tmp_seq->length) >
                          seqs[this_seq]->max_length)
                              resize_sequence(seqs[this_seq]);
                     for (i=0; i<tmp_seq->length; i++)
                     {
                        seqs[this_seq]->sequence[ seqs[this_seq]->length++ ] =
                          tmp_seq->sequence[i];
                     }
                  }
                  this_seq++;
                  free_sequence(tmp_seq);
               }
            }
         }
         fclose(tmp);
      }
      tmp=fopen(tmp_name, "w");
   }

   sprintf(line, "\\rm %s", tmp_name);
   system(line);

   return(nseq);
}  /*  end of try_clustal   */
/*============================================================================
MSF format:  Comments until alignment begins after "//" line:

//

            1                                                   50
P09254-1    .....KRQED AGYDICVPYN LYLKR..... NEFIKIVLPI IRDWDLQHPS
A37470-1    .TFAPKRDED AGYDIAMPYT AVL....... APGENLHVRL PVAYAADAHA
Q00030-2    DYFAPKRDED AGYDISAQTN ATI....... EPDESYFVEL PIVFSSSNPA
P28892-1    .....KRVED AGYDISAPED ATI....... DPDESHFVDL PIVFANSNPA
P10234-1    .....KREED AGFDIVVRRP VTV.P..... ANGTTVVQPS LRMLHADAGP
CONSENSUS

seq names can be right or left-justified!
============================================================================*/
int try_msf(ifp, seqs)
FILE *ifp;
Sequence *seqs[MAXSEQ];
{
   FILE *tmp;
   Sequence *tmp_seq;
   int nseq, this_seq, i, my_pid, db_type, seq_type, allnumbers;
   char line[MAXLEN], tmp_name[30], *name, *residues;

   nseq = 0; seqs[0] = NULL;
   fprintf(stderr, "Trying  MSF format.\n");

   /*  Look for "//"     */
   while (!feof(ifp) && fgets(line, MAXLEN, ifp) != NULL &&
           strncmp(line, "//", 2) != 0)
		;
   /*  Didn't find "//"  */
   if (feof(ifp)) { rewind(ifp); }

   /*  Read sequences until the next blank line, or until a line
       with blanks at the start
	then start over and add to existing sequences, etc.  */
   /* Have to change this; now seq names are right-justified so */
   my_pid = getpid();
   sprintf(tmp_name, "mablock.%d", my_pid);
   tmp=fopen(tmp_name, "w");
   while (!feof(ifp))
   {
      /*   Write a set of sequence segments to a temp file */
      /*  NOTE: Blank lines from netscape can be "^M\n\0", len=3 */
      while (!feof(ifp) && fgets(line, MAXLEN, ifp) != NULL &&
             line[0] != '\n' && line[0] != '\r' )
      {
         /* some lines of numbers, etc */
         allnumbers = 1; i=0;
         while(allnumbers && i<strlen(line))
         {
            if (isalpha(line[i])) allnumbers = 0;
            i++;
         }
         if (!allnumbers)
         {
            name = strtok(line, " \t");
            /*  residues may include spaces, tabs, etc. */
            residues = strtok(NULL, "\r\n");
/*printf("%s %s\n", name, residues);*/
            if (tmp != NULL)
            {
               if (name != NULL && residues != NULL)
               {
                   fprintf(tmp, ">%s\n", name);
		   fprintf(tmp, "%s\n", residues);
               }
               else
               {   fprintf(stderr,"ERROR reading MSF file\n%s", line);  }
            }
         }
      } /* end while */
      /*   Encountered a blank line or eof */
      fclose(tmp);
      if ( (tmp = fopen(tmp_name, "r")) != NULL)
      {
         db_type = type_dbs(tmp, DbInfo);
         seq_type = UNKNOWN_SEQ;
         seq_type = seq_type_dbs(tmp, DbInfo, db_type, seq_type);
         if (seq_type == NA_SEQ)
         {
            printf("WARNING: Sequences appear to be DNA but will be treated");
            printf(" as protein\n");
            seq_type = AA_SEQ;
         }
         if (seqs[0] == NULL)
         {
         /*   initialize sequences */
            nseq = 0;
            if (db_type >= 0)
            {
               while ( nseq < MAXSEQ &&
                   (seqs[nseq] = read_a_sequence(tmp, db_type, seq_type)) != NULL)
               {
                  nseq++;
               }
            }
         }
         else
         /*     add on to existing sequences */
         {
            this_seq = 0;
            if (db_type >= 0)
            {
               while ( this_seq < MAXSEQ &&
                   (tmp_seq = read_a_sequence(tmp, db_type, seq_type)) != NULL)
               {
                  if (seqs[this_seq] != NULL &&
                      strcmp(seqs[this_seq]->name, tmp_seq->name) == 0 )
                  {
                     if ((seqs[this_seq]->length + tmp_seq->length) >
                          seqs[this_seq]->max_length)
                              resize_sequence(seqs[this_seq]);
                     for (i=0; i<tmp_seq->length; i++)
                     {
                        seqs[this_seq]->sequence[ seqs[this_seq]->length++ ] =
                          tmp_seq->sequence[i];
                     }
                  }
                  this_seq++;
                  free_sequence(tmp_seq);
               }
            }
         }
         fclose(tmp);
      }
      tmp=fopen(tmp_name, "w");
   }

   sprintf(line, "\\rm %s", tmp_name);
   system(line);

   return(nseq);
}  /*  end of try_msf   */
/*========================================================================
========================================================================*/
Block *make_block(width, firstpos, nseq, seqs)
int width, firstpos, nseq;
Sequence *seqs[MAXSEQ];
{
   Block *block;
   int bpos, spos, s;

   block = new_block(width, nseq);
   
   for (s=0; s < nseq; s++)
   {
      strcpy(block->sequences[s].name, seqs[s]->name);
      block->sequences[s].position = firstpos;
      block->sequences[s].weight = 100.0;
      bpos = 0;
      for (spos = firstpos; spos < firstpos + width; spos++)
          block->sequences[s].sequence[bpos++] = seqs[s]->sequence[spos];
   } /* end of sequence s */

   pb_weights(block);

/*   weights are now fp, re-normalize them so max=100 or output block
	as fp ... */
/*	calibrate block now?
	what about ID, AC, DE?
*/

   return(block);
}  /*  end of make_block */

/*=======================================================================
     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 = 0;
   new->minseq = new->seqprev = 0;
   new->consensus = NULL;
   new->block = NULL;
   new->next = NULL;

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

void insert_blist(blist, block)
struct blocks_list *blist;
Block *block;
{
   struct blocks_list *cur;
   char ctemp[MAXNAME];
   int i, prev, minprev, maxprev;

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

   /*------ Fill in some of the block information -------*/
   strcpy(block->id, Block_ID);
   strcpy(block->de, Block_DE);
   strcpy(block->motif, "UNK");
   sprintf(block->bl, "UNK motif; width=%d; seqs=%d;",
          block->width, block->num_sequences);
   strcpy(ctemp, Block_AC);
   if      (blist->nblock ==  1) strcat(ctemp, "A");
   else if (blist->nblock ==  2) strcat(ctemp, "B");
   else if (blist->nblock ==  3) strcat(ctemp, "C");
   else if (blist->nblock ==  4) strcat(ctemp, "D");
   else if (blist->nblock ==  5) strcat(ctemp, "E");
   else if (blist->nblock ==  6) strcat(ctemp, "F");
   else if (blist->nblock ==  7) strcat(ctemp, "G");
   else if (blist->nblock ==  8) strcat(ctemp, "H");
   else if (blist->nblock ==  9) strcat(ctemp, "I");
   else if (blist->nblock == 10) strcat(ctemp, "J");
   else if (blist->nblock == 11) strcat(ctemp, "K");
   else if (blist->nblock == 12) strcat(ctemp, "L");
   else if (blist->nblock == 13) strcat(ctemp, "M");
   else if (blist->nblock == 14) strcat(ctemp, "N");
   else if (blist->nblock == 15) strcat(ctemp, "O");
   else if (blist->nblock == 16) strcat(ctemp, "P");
   else if (blist->nblock == 17) strcat(ctemp, "Q");
   else if (blist->nblock == 18) strcat(ctemp, "R");
   else if (blist->nblock == 19) strcat(ctemp, "S");
   else if (blist->nblock == 20) strcat(ctemp, "T");
   else if (blist->nblock == 21) strcat(ctemp, "U");
   else if (blist->nblock == 22) strcat(ctemp, "V");
   else if (blist->nblock == 23) strcat(ctemp, "W");
   else if (blist->nblock == 24) strcat(ctemp, "X");
   else if (blist->nblock == 25) strcat(ctemp, "Y");
   else if (blist->nblock == 26) strcat(ctemp, "Z");
   else strcat(ctemp, "*");

   minprev = 99999; maxprev = -99999;
   for (i=0; i<block->num_sequences; i++)
   {
      /* NOTE: In general can't assume sequences are in
         same order in all the blocks, but should work
         in this program */
      if (blist->nblock > 1)
         prev = block->sequences[i].position -
             cur->block->sequences[i].position -
             cur->block->width;
      else
         prev = block->sequences[i].position;
      if (prev < minprev) minprev = prev;
      if (prev > maxprev) maxprev = prev;
   }
   if (minprev < 0) minprev = 0;

   sprintf(block->ac, "%s; distance from previous block=(%d,%d)",
           ctemp, minprev, maxprev);

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

}  /* 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);
      free(cur);
      last->next = NULL;
      free_blist(last);
   }
   else free(blist);

}  /* end of free_blist */
/*==================================================================
      Puts the portion of the sequences in blocks into seqs[] array
====================================================================*/
void fasta_seqs(sseq, blist, seqs)
struct seqseq *sseq;
struct blocks_list *blist;
Sequence *seqs[MAXSEQ];
{
   int nseq, seq, seq1, pos, curpos;
   struct blocks_list *bcur;
   Block *b;

   nseq = blist->nseq;

/*>>>>>>>> See code in try_clustal(): write seqs out & then read them
  back in with read_a_sequence() <<<<<<*/
   for (seq=0; seq < nseq; seq++)
   {
      printf(">%20s          from blocks\n",
              blist->next->block->sequences[seq].name);
      curpos = 0;
      bcur = blist->next;
      while (bcur != NULL && bcur->block != NULL)
      {
         b = bcur->block;
         order_seq(sseq, blist->next->block, b);
         seq1 = sseq[seq].seq;
         printf("%5d ", b->sequences[seq1].position);
         for (pos=0; pos < b->width; pos++)
            printf("%c", aa_btoa[b->residues[seq1][pos]]);
         printf("\n");
         bcur = bcur->next;
      } /* end of blocks */
   }   /*  end of a sequence  */
}   /* end of fasta_seqs */
/*=======================================================================*/
/*  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 */
/*======================================================================
   Fix up the sequence names
=======================================================================*/
void fix_names(nseq, seqs)
int nseq;
Sequence *seqs[MAXSEQ];
{
  Boolean done;
  int i, s, len, mlen;
  char ctemp[80];

  for (s=0; s < nseq; s++)
  {
    /*   Get rid of leading "P1;"    */
    if (strncmp(seqs[s]->name, "P1;", 3) == 0)
    {
        if ((int) strlen(seqs[s]->name) > 15) seqs[s]->name[15] = '\0';
        strcpy(ctemp, seqs[s]->name);
        strcpy(seqs[s]->name, ctemp+3);
    }
    if ((int) strlen(seqs[s]->name) > SNAMELEN)
        seqs[s]->name[SNAMELEN] = '\0';	/* ensure ony 20 characters long */

    /*  Check for duplicate sequence names here */
    i = s-1; done = FALSE;
    while (!done && i >= 0)
    {
       if (strcmp(seqs[s]->name, seqs[i]->name) == 0)
       {
          printf("Non-unique sequence name: %s\n", seqs[s]->name);
/*
          printf("First 20 characters of sequence name must be unique.\n");
*/
          strcpy(ctemp, seqs[s]->name);
          len = strlen(seqs[s]->name);
          mlen = 9 - (int) log10((double) s);	/* #digits in s*/
          if (len > mlen) ctemp[mlen] = '\0';
          sprintf(seqs[s]->name, "%s%d", ctemp, s);
          seqs[s]->name[SNAMELEN] = '\0';
          printf("Modified name to %s\n", seqs[s]->name);
          done = TRUE;
      } /* end of non-unique name */
      i--;
    }
  }  /* end of for s */
}  /*  end of fix_names */
/*==========================================================================
	Output blocks in blocks format
==========================================================================*/
void output_blocks(blocks, bfp)
struct blocks_list *blocks;
FILE *bfp;
{
   struct blocks_list *bcur;

   if (blocks != NULL)
   {
      bcur = blocks;
      while (bcur->next != NULL)
      {
             /* Have to make weights integer to use INT_OUTPUT  */
             output_block_s(bcur->next->block, bfp, FLOAT_OUTPUT);
          bcur = bcur->next;
      }
   }

}   /* end of output_blocks */
/*==========================================================================
	Output blocks in fasta format
==========================================================================*/
void output_fasta(blocks, bfp)
struct blocks_list *blocks;
FILE *bfp;
{
   struct blocks_list *bcur;
   Block *block;
   int seq, pos, nout;

   if (blocks != NULL)
   {
      for (seq = 0; seq < blocks->nseq; seq++)
      {
         nout = 0;
         bcur = blocks;
         while (bcur->next != NULL)
         {
            block = bcur->next->block;
            if (nout == 0)
            {
               fprintf(bfp, ">%s\n", block->sequences[seq].name);
            }
            for (pos=0; pos < block->width; pos++)
            {
                  fprintf(bfp, "%c", aa_btoa[block->residues[seq][pos]]);
            }
            fprintf(bfp, "\n");
            nout++;
            bcur = bcur->next;
         }
      }  /* end of sequence */
   }


}   /* end of output_fasta */
