/*  COPYRIGHT 1994-2003 Fred Hutchinson Cancer Research Center
    blalign.c    Print blocks as multiple alignment      
        blalign <blocks database> -[p|s|f|m|g]
         <blocks database> = name of blocks db file, aligns each group
         p|s|f = p for Posfai style, s for short style, f for fasta style,
		 m for MSF style, for giant block

	Requires blimps 3.2.6+ to compile.
--------------------------------------------------------------------
 4/21/94 J. Henikoff (from blcovar.c)
 4/26/94 Added option for "Posfai" or abbreviated style of output
 6/23/94 Print number of sequences and block widths
 8/12/96 Added fasta style output
 2/19/97 Added output width parameter = OutWidth
 8/27/97 More spaces in name for format -f
 2/25/98 Format changes for -f per Shmuel
10/20/98 Fix abort problem if different #seqs in blocks
 1/29/99 Longer sequence names (10->15)
 5/19/99 Added MSF format
 6/14/99 Adjustments for longer sequence names (10->18 chars)
 8/23/99 Add offset to seqname when fasta style so clustalw is sure
	 to get unique names from prints blocks
12/14/99 Added g option to output one giant block.
12/17/99 Accomodate ACs from 7 to 10 chars wide.
12/18/02 Output STOCKHOLM format for quicktree
 8/11/03.1 Fix STOCKHOLM format when blocks overlap: format_seqs().
 8/24/03.1 STOCKHOLM format without gaps (style=q=6)
 8/25/03.1 Pre-sort sequences in each block (namecmp())
 8/27/03.1 Fix block->residues in sorted blocks
12/23/06.1 Increased sequence name length
====================================================================*/

#define BLALIGN_C_
#define EXTERN
#define MAXNAME 80	/* Maximum file name length */
#define MAXAA 25	/* Dimension of aa_ arrays */
#define MAXWIDTH 60	/* Maximum block width */
#define OUTWIDTH 80	/* Default output line */
#define OUTMAX 240	/* Maximum output line width */
#define MAXLEN 4000	/* Maximum sequence length */
#define FLZERO 0.0000001
#define INDEX(n, col, row) (col*n + row - (col*(col+1))/2)

#include <blocksprogs.h>


/*
 * Local variables and data structures
 */

/*  first entry has no block, just nblock & totwidth, other entries
    in list have just pointers to the blocks */
/* >>> NOT the same as block_list in protomat.h <<< */
struct blocks_list {		/* list of blocks for a family */
   int nblock;				/* number of blocks in family */
   char fam[MAXAC+1];			/* family name */
   int nseq;				/* number of sequences in blocks */
   int totwidth;			/* total width of the blocks */
   Block *block;
   struct blocks_list *next;
};
struct seqseq {			/* reorder sequences */
   int seq;
   int pos;
};
struct align {			/* alignment line */
   char line[MAXLEN];
};

struct blocks_list *make_blist();
void insert_blist();
void free_blist();
void order_seq();
int print_align();
void format_seqs();
void posfai_seqs();
void short_seqs();
void fasta_seqs();
void msf_seqs();
void stockholm_seqs();
void giant();
int namecmp();

int OutWidth;			/* width of output */

double frequency[MATRIX_AA_WIDTH];    /* for frequency.c */
char Version[12] = "12/23/06.1";                /* Version date */


/*=======================================================================
 * main
 =======================================================================*/

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE *bfp;
  Block *block;
  struct blocks_list *blist;
  int np, style;
  char bdbname[MAXNAME], save_family[MAXAC+1];

   if (argc < 2)
   {
      printf("BLALIGN Version %s\n", Version);
      printf("Copyright 1999-2003 Fred Hutchinson Cancer Research ");
      printf("Center\nPrints blocks as a multiple alignment.\n");
      printf("USAGE:  blalign <file of blocks> [-p|s|f|m|h|q|g] [width]\n");
      printf("          p=Posfai style, s=short style, f=fasta style,\n");
      printf("          m=MSF style, h=STOCKHOLM style, q=ungapped STOCKHOLM,\n");
      printf("          g=giant block\n");
      printf("          defaults are short style, width = 66\n");
   }

/* ------------1st arg = blocks database -------------------------------*/
   if (argc > 1)
      strcpy(bdbname, argv[1]);
   else
   {
      printf("\nEnter name of file of blocks: ");
      gets(bdbname);
   }
   if ( (bfp=fopen(bdbname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", bdbname);
      exit(-1);
   }
/*----------2nd arg = p or s -------------------------------------------*/
   style = 0; 			/* short*/
   if (argc > 2 && argv[2][0] == '-')
   {
	if      (argv[2][1] == 'p' || argv[2][1] == 'P') style = 1;
	else if (argv[2][1] == 'f' || argv[2][1] == 'F') style = 2;
	else if (argv[2][1] == 'm' || argv[2][1] == 'M') style = 3;
	else if (argv[2][1] == 'g' || argv[2][1] == 'G') style = 4;
	else if (argv[2][1] == 'h' || argv[2][1] == 'H') style = 5;
	else if (argv[2][1] == 'q' || argv[2][1] == 'Q') style = 6;
   }
   
/*-----------3rd arg = output width -----------------------------------*/
   OutWidth = OUTWIDTH;
   if (argc > 3) OutWidth = atoi(argv[3]);
   if (OutWidth < 1 || OutWidth > OUTMAX) OutWidth = OUTWIDTH;

/*----------------------------------------------------------------------*/
  save_family[0] = '\0';
  blist = NULL;
  while ((block = read_a_block(bfp)) != NULL)
  {
     if (strcmp(save_family, block->family) != 0)
     {       /*  new family  */
        /*  process last family */
        if (blist != NULL && blist->nblock > 0)
        {
           np = print_align(blist, style);
           free_blist(blist);
        }
        strcpy(save_family, block->family);
        if (style < 2) printf(">%s %s\n", save_family, block->de);
        blist = make_blist();
        insert_blist(blist, block);
     }
     else    
     {     /*  same family */
        insert_blist(blist, block);
     }
  }
  /*  process final family */
  if (blist != NULL && blist->nblock > 0)
  {
     np = print_align(blist, style);
     free_blist(blist);
  }

  fclose(bfp);
  exit(0);

}  /* end of main */
/*=======================================================================
     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 = 0;
   new->fam[0] = '\0';
   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;
   int seq, pos;

   cur = blist;
   while (cur->next != NULL)
      cur = cur->next;
   cur->next = make_blist();
   cur->next->block = block;
   strcpy(cur->fam, block->family); 
   blist->nblock += 1;
   if (block->num_sequences > blist->nseq)
      blist->nseq = block->num_sequences;
   blist->totwidth += block->sequences[0].length;
   
   /* sort the sequences in the block by name and collapse to 1 cluster*/
/*output_block(block,stdout);*/
   qsort(block->sequences, block->num_sequences, sizeof(Sequence), namecmp);
   block->num_clusters = block->max_clusters = 1;
   block->clusters[0].num_sequences = block->num_sequences;
/*
   block->clusters[0].sequences = block->sequences;
*/
  /*  fix up the residue pointers */
   for(seq=0; seq<block->num_sequences; seq++)
   {
      block->residues[seq] = block->sequences[seq].sequence; 
   }
/*output_block(block,stdout);*/

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

}  /* end of free_blist */
/*=======================================================================
	blist = list of blocks for a group
========================================================================*/
int print_align(blist, style)
struct blocks_list *blist;
int style;
{
   int np, seq;
   int nseq;
   struct blocks_list *bcur, *bfirst;
   struct seqseq *sseq;

   /*     Initialize */
   nseq = blist->nseq;
   sseq = (struct seqseq *) malloc (nseq * sizeof(struct seqseq));
  
   /*   Sequences aren't in the same order in different blocks */
   /*   They are printed in the order of the first block       */

   /*   Do the first block                                     */
   bfirst = blist->next;
   if (bfirst->block == NULL) return(0);

   /*   sseq should be the same for every block after namecmp() */
   for (seq = 0; seq < nseq; seq++)
   {
      sseq[seq].seq = seq;
      sseq[seq].pos = 0;
   }
   format_seqs(sseq, bfirst->block);
   np = 1;

   bcur = bfirst->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      /*  Guard against sets of blocks with unequal numbers of 
	  sequences */
      if (bcur->block->num_sequences == blist->nseq)
      {
         /*  sequences may not be in the same order */
/*
         order_seq(sseq, bfirst->block, bcur->block);
*/
         format_seqs(sseq, bcur->block);
      }
      else
      {
         printf("ERROR in block %s: %d sequences found but ",
		bcur->block->number, bcur->block->num_sequences);
         printf(" %d expected\n", blist->nseq);
      }
      np++;
      bcur = bcur->next;
   } /* end of block */

   if (style < 2)
      printf("%d sequences are included in %d blocks\n", 
           blist->nseq, blist->nblock);
   if (style == 1)      posfai_seqs(sseq, blist);
   else if (style == 2) fasta_seqs(sseq, blist);
   else if (style == 3) msf_seqs(sseq, blist);
   else if (style == 4) giant(sseq, blist);
   else if (style == 5) stockholm_seqs(sseq, blist, 1);
   else if (style == 6) stockholm_seqs(sseq, blist, 0);
   else                 short_seqs(sseq, blist);
   return(np);

}  /* end of print_align */

/*=======================================================================*/
/*  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 */
/*=======================================================================
        Formats one block: determines how many gaps and unaligned
        positions are required to line up the block in each sequence.

	These values are stored as sequences[].type (gaps) and
        sequences[].max_length (unaligned).
        Updates sseq[].pos values
========================================================================*/
void format_seqs(sseq, b)
struct seqseq *sseq;
Block *b;
{
   int nseq, seq, seq1, posgap, posun;
   int between;

   nseq = b->num_sequences;
   /*    find maximum distance since last block in all sequences */
   between = 0;
   for (seq = 0; seq < nseq ; seq++)
   {
      seq1 = sseq[seq].seq;
      /* be careful, some seqs may overlap slightly in adjacent blocks */
      if (b->sequences[seq1].position > sseq[seq].pos)
      { posun = b->sequences[seq1].position - sseq[seq].pos; }
      else { posun = 0; }
      if (posun > between) between = posun;
   }
   for (seq=0; seq < nseq; seq++)
   {
      seq1 = sseq[seq].seq;
      /*   unaligned region between blocks */
      /* be careful, some seqs may overlap slightly in adjacent blocks */
      if (b->sequences[seq1].position > sseq[seq].pos)
      { posun = b->sequences[seq1].position - sseq[seq].pos; }
      else { posun = 0; }
      /*   gap to accomodate longest unaligned region */
      posgap = between - posun;
      sseq[seq].pos = b->sequences[seq1].position + b->width;
      /*    Reusing these fields !!! */
      b->sequences[seq1].type = posgap;
      b->sequences[seq1].max_length = posun;
   }
}   /* end of format_seqs */
/*==================================================================
      Prints the alignment in the style of Posfai (every position)
====================================================================*/
void posfai_seqs(sseq, blist)
struct seqseq *sseq;
struct blocks_list *blist;
{
   int alen, nseq, seq, seq1, pos, posgap, posun, curpos;
   char ctemp[OUTMAX];
   struct blocks_list *bcur;
   Block *b;
   struct align *out;

   nseq = blist->nseq;
   out = (struct align *) malloc(nseq * sizeof(struct align));
   if (out == NULL)
   {   printf("\nposfai_seqs: OUT OF MEMORY"); exit(-1); }
   for (seq=0; seq < nseq; seq++) out[seq].line[0] = '\0';

   bcur = blist->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      b = bcur->block;
      for (seq=0; seq < nseq; seq++)
      {
/*
         if (bcur->block->num_sequences == blist->nseq)
         {
            order_seq(sseq, blist->next->block, bcur->block);
         }
         else
         {
            printf("ERROR in block %s, found %d sequences but ",
                   bcur->block->number, bcur->block->num_sequences);
            printf("expected %d\n", blist->nseq);
         }
*/
         seq1 = sseq[seq].seq;
         /*   unaligned region between blocks */
         posun = b->sequences[seq1].max_length;
         /*   gap to accomodate longest unaligned region */
         posgap = b->sequences[seq1].type;
         for (pos=0; pos < posgap; pos++)
            strcat(out[seq].line, " ");
         for (pos=0; pos < posun; pos++)
            strcat(out[seq].line, ".");
         sprintf(ctemp, "%5d ", b->sequences[seq1].position);
         strcat(out[seq].line, ctemp);
         for (pos=0; pos < b->width; pos++)
            ctemp[pos] = aa_btoa[b->sequences[seq1].sequence[pos]];
         ctemp[b->width] = '\0';
         strcat(out[seq].line, ctemp);
      }
      bcur = bcur->next;
   }

   alen = strlen(out[0].line);
   b = blist->next->block;
   for (curpos = 0; curpos < alen; curpos += OutWidth)
   {
      for (seq=0; seq < nseq; seq++)
      {
         printf("\n%20s ", b->sequences[seq].name);
         for (pos=curpos; pos < curpos+OutWidth; pos++)
            if (pos < alen)   printf("%c", out[seq].line[pos]);
      }
      printf("\n");
   }
   printf("\n");
}   /* end of posfai_seqs */
/*==================================================================
      Prints the alignment in short style (doesn't print spaces
      between blocks)
====================================================================*/
void short_seqs(sseq, blist)
struct seqseq *sseq;
struct blocks_list *blist;
{
   int alen, nseq, seq, seq1, pos, posun, curpos;
   char ctemp[OUTMAX], header[OUTMAX];
   struct blocks_list *bcur;
   Block *b;
   struct align *out;

   nseq = blist->nseq;
   out = (struct align *) malloc(nseq * sizeof(struct align));
   if (out == NULL)
   {   printf("\nshort_seqs: OUT OF MEMORY"); exit(-1); }
   for (seq=0; seq < nseq; seq++) out[seq].line[0] = '\0';
   header[0] = '\0';

   curpos = 19;   /* seq name width */
   bcur = blist->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      b = bcur->block;

      /*  Page width required to print each block is
              name=19 + distance from last block=8 + offset=6 +
              max block width for blockmaker=55 => 88 max  */
      /*   Print full line */
      if ((curpos + 14 + b->width) > OutWidth)
      {
         printf("\n            %s", header);
         header[0] = '\0';
         for (seq=0; seq < nseq; seq++)
         {
            printf("\n%20s ", blist->next->block->sequences[seq].name);
            printf("%s", out[seq].line);
            out[seq].line[0] = '\0';
         }
         printf("\n");
         curpos = 19;
      }
      curpos += (14 + b->width);

      /*    Format the block  */
      strcat(header, b->number);
      sprintf(ctemp, ", width = %d", b->width);
      strcat(header, ctemp);
      for (seq=0; seq < nseq; seq++)
      {
/*
         if (bcur->block->num_sequences == blist->nseq)
         {
            order_seq(sseq, blist->next->block, bcur->block);
         }
         else
         {
            printf("ERROR in block %s, found %d sequences but ",
                   bcur->block->number, bcur->block->num_sequences);
            printf("expected %d\n", blist->nseq);
         }
*/
         seq1 = sseq[seq].seq;
         /*   unaligned region between blocks */
         posun = b->sequences[seq1].max_length;
         /*  print distance bewteen blocks for this sequence after 1st block*/
         if (bcur != blist->next)
         {
            sprintf(ctemp, " (%4d) ", posun);
            strcat(out[seq].line, ctemp);
         }
         sprintf(ctemp, "%5d ", b->sequences[seq1].position);
         strcat(out[seq].line, ctemp);
         for (pos=0; pos < b->width; pos++)
            ctemp[pos] = aa_btoa[b->sequences[seq1].sequence[pos]];
         ctemp[b->width] = '\0';
         strcat(out[seq].line, ctemp);
      }
      /*   Pad out the header line */
      alen = strlen(header);
      for (pos = alen; pos < strlen(out[0].line); pos++)
         strcat(header, " ");
      bcur = bcur->next;
   }
   /*   Print last block */
   printf("\n            %s", header);
   for (seq=0; seq < nseq; seq++)
   {
      printf("\n%20s ", blist->next->block->sequences[seq].name);
      printf("%s", out[seq].line);
   }

   printf("\n");
}   /* end of short_seqs */
/*==================================================================
      Prints the portion of the sequences in blocks in fasta format
====================================================================*/
void fasta_seqs(sseq, blist)
struct seqseq *sseq;
struct blocks_list *blist;
{
   int nseq, seq, seq1, pos, curpos, endpos;
   char header[OUTMAX], ctemp;
   struct blocks_list *bcur;
   Block *b;
   struct align *out;

   nseq = blist->nseq;
   out = (struct align *) malloc(nseq * sizeof(struct align));
   if (out == NULL)
   {   printf("\nfasta_seqs: OUT OF MEMORY"); exit(-1); }
   for (seq=0; seq < nseq; seq++) out[seq].line[0] = '\0';
   header[0] = '\0';

   for (seq=0; seq < nseq; seq++)
   {
      pos = blist->next->block->sequences[seq].position;
      /*  endpos is only for the first block   */
      endpos = pos + blist->next->block->width;
      printf(">%s|%d              from %10.10s blocks\n",
              blist->next->block->sequences[seq].name,
              pos,
              blist->fam);
      curpos = 0;
      bcur = blist->next;
      while (bcur != NULL && bcur->block != NULL)
      {
         b = bcur->block;
         if (b->num_sequences == blist->nseq)
         {
/*
            order_seq(sseq, blist->next->block, b);
*/
            seq1 = sseq[seq].seq;
/*
            printf("%5d ", b->sequences[seq1].position);
*/
            for (pos=0; pos < b->width; pos++)
            {  
ctemp = aa_btoa[b->sequences[seq1].sequence[pos]];
               printf("%c", aa_btoa[b->sequences[seq1].sequence[pos]]); 
            }
            printf("\n");
         }
/*
         else
         {
            printf("ERROR in block %s, found %d sequences but ",
                   b->number, b->num_sequences);
            printf("expected %d\n", blist->nseq);
         }
*/
         bcur = bcur->next;
      } /* end of blocks */
   }   /*  end of a sequence  */
}   /* end of fasta_seqs */
/*==================================================================
      Prints the alignment in MSF format
====================================================================*/
void msf_seqs(sseq, blist)
struct seqseq *sseq;
struct blocks_list *blist;
{
   int alen, nseq, seq, seq1, pos, posgap, posun, curpos;
   int first, msfwidth;
   char ctemp[OUTMAX];
   struct blocks_list *bcur;
   Block *b;
   struct align *out;

   msfwidth = OutWidth - 29;	/* subtract space required for seq name */
   if (msfwidth < 1) msfwidth = OutWidth;

   nseq = blist->nseq;
   out = (struct align *) malloc(nseq * sizeof(struct align));
   if (out == NULL)
   {   printf("\nmsf_seqs: OUT OF MEMORY"); exit(-1); }
   for (seq=0; seq < nseq; seq++) out[seq].line[0] = '\0';

   printf("//\n");

   bcur = blist->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      b = bcur->block;
      for (seq=0; seq < nseq; seq++)
      {
/*
         if (bcur->block->num_sequences == blist->nseq)
         {
            order_seq(sseq, blist->next->block, bcur->block);
         }
         else
         {
            printf("ERROR in block %s, found %d sequences but ",
                   bcur->block->number, bcur->block->num_sequences);
            printf("expected %d\n", blist->nseq);
         }
*/
         seq1 = sseq[seq].seq;

         if (bcur != blist->next)
         {
            /*   gap to accomodate longest unaligned region */
            posgap = b->sequences[seq1].type;
            for (pos=0; pos < posgap; pos++)
               strcat(out[seq].line, "-");
            /*   unaligned region between blocks */
            posun = b->sequences[seq1].max_length;
            for (pos=0; pos < posun; pos++)
               strcat(out[seq].line, ".");
         }

         for (pos=0; pos < b->width; pos++)
            ctemp[pos] = aa_btoa[b->sequences[seq1].sequence[pos]];
         ctemp[b->width] = '\0';
         strcat(out[seq].line, ctemp);
      }
      bcur = bcur->next;
   }  /* end of a block */

   first = TRUE;
   alen = strlen(out[0].line);
   b = blist->next->block;
   for (curpos = 0; curpos < alen; curpos += msfwidth)
   {
      for (seq=0; seq < nseq; seq++)
      {
         /*   MSF requires names to be left-justified */
         if (first)	/* first set */
         {
            sprintf(ctemp, "%-s/%d",
               b->sequences[seq].name,
               b->sequences[seq].position);
            printf("\n%-28s ", ctemp); 
         }
         else
         {   printf("\n%-28s ", b->sequences[seq].name);   }
         for (pos=curpos; pos < curpos+msfwidth; pos++)
            if (pos < alen)   printf("%c", out[seq].line[pos]);
      }
      first = FALSE;
      printf("\n");
   }
   printf("\n");
}   /* end of msf_seqs  */
/*==================================================================
      Combines all of the blocks into one giant block
====================================================================*/
void giant(sseq, blist)
struct seqseq *sseq;
struct blocks_list *blist;
{
   int seq, seq1, pos, curpos, bpos;
   struct blocks_list *bcur;
   Block *fb, *b, *bgiant;

   fb = blist->next->block;
   if (blist->nblock < 2)
   {
      output_block(fb, stdout);
   }
   else
   {
      bgiant = new_block(blist->totwidth, blist->nseq);
      if (bgiant == NULL)
      {   printf("\ngiant: OUT OF MEMORY"); exit(-1); }

      /*   first block initializes giant block */
      bcur = blist->next;
      fb = bcur->block;
      strcpy(bgiant->id, fb->id);
      strcpy(bgiant->ac, fb->ac);
      strcpy(bgiant->number, fb->number);
      strcpy(bgiant->de, fb->de);
      strcpy(bgiant->bl, fb->bl);
      bgiant->min_prev = fb->min_prev;
      bgiant->max_prev = fb->max_prev;
      curpos = 0;
      for (seq=0; seq < blist->nseq; seq++)
      {
         strcpy(bgiant->sequences[seq].name, fb->sequences[seq].name);
         strcpy(bgiant->sequences[seq].info, fb->sequences[seq].info);
         bgiant->sequences[seq].position = fb->sequences[seq].position;
         bgiant->sequences[seq].length = blist->totwidth;
         bpos = curpos;
/*  bgiant->residues[seq] = bgiant->sequences[seq].sequence; new_block() did this ????*/
         for (pos=0; pos < fb->width; pos++)
         {
            bgiant->sequences[seq].sequence[bpos] =
                fb->sequences[seq].sequence[pos];
            bpos++;
         }
      }
      curpos = fb->width;

      bcur = bcur->next;		/* second and subsequent blocks */
      while (bcur != NULL && bcur->block != NULL)
      {
         b = bcur->block;
         for (seq=0; seq < blist->nseq; seq++)
         {
/*
            order_seq(sseq, blist->next->block, b);
*/
            seq1 = sseq[seq].seq;
            bpos = curpos;
            for (pos=0; pos < b->width; pos++)
            {
               bgiant->sequences[seq].sequence[bpos] =
                   b->sequences[seq1].sequence[pos];
               bpos++;
            }
         }
         curpos += b->width;
         bcur = bcur->next;
      }

      /* pb_weights() uses residues */
      pb_weights(bgiant);

      output_block(bgiant, stdout);
   }   /*  end of if more than one block  */

}   /* end of giant */
/*==================================================================
      Prints the alignment in STOCKHOLM format
# STOCKHOLM 1.0

seq1   ----NVY
seq2   MKMLLLL

seq1
seq2
//

====================================================================*/
void stockholm_seqs(sseq, blist, gap_flag)
struct seqseq *sseq;
struct blocks_list *blist;
int gap_flag;
{
   int alen, nseq, seq, seq1, pos, posgap, posun, curpos;
   int msfwidth;
   char ctemp[OUTMAX];
   struct blocks_list *bcur;
   Block *b;
   struct align *out;

   msfwidth = OutWidth - 29;	/* subtract space required for seq name */
   if (msfwidth < 1) msfwidth = OutWidth;

   nseq = blist->nseq;
   out = (struct align *) malloc(nseq * sizeof(struct align));
   if (out == NULL)
   {   printf("\nstockholm_seqs: OUT OF MEMORY"); exit(-1); }
   for (seq=0; seq < nseq; seq++) out[seq].line[0] = '\0';

   printf("# STOCKHOLM 1.0\n");

   bcur = blist->next;
   while (bcur != NULL && bcur->block != NULL)
   {
      b = bcur->block;
      for (seq=0; seq < nseq; seq++)
      {
/*
         if (bcur->block->num_sequences == blist->nseq)
         {
            order_seq(sseq, blist->next->block, bcur->block);
         }
         else
         {
            printf("ERROR in block %s, found %d sequences but ",
                   bcur->block->number, bcur->block->num_sequences);
            printf("expected %d\n", blist->nseq);
         }
*/
         seq1 = sseq[seq].seq;

         if (bcur != blist->next)
         {
            /*   gap to accomodate longest unaligned region */
            posgap = b->sequences[seq1].type;
            if (gap_flag == 1)
            {
               for (pos=0; pos < posgap; pos++)
               {  strcat(out[seq].line, "-");  }
            }
            /*   unaligned region between blocks */
            posun = b->sequences[seq1].max_length;
            if (gap_flag == 1)
            {
               for (pos=0; pos < posun; pos++)
               {  strcat(out[seq].line, "."); }
            }
         }

         for (pos=0; pos < b->width; pos++)
         {  ctemp[pos] = aa_btoa[b->sequences[seq1].sequence[pos]]; }
         ctemp[b->width] = '\0';
         strcat(out[seq].line, ctemp);
      }
      bcur = bcur->next;
   }  /* end of a block */

   alen = strlen(out[0].line);
   b = blist->next->block;
   for (curpos = 0; curpos < alen; curpos += msfwidth)
   {
      for (seq=0; seq < nseq; seq++)
      {
         printf("\n%-28s ", b->sequences[seq].name); 
         for (pos=curpos; pos < curpos+msfwidth; pos++)
            if (pos < alen)   printf("%c", out[seq].line[pos]);
      }
      printf("\n");
   }
   printf("//\n");
}   /* end of stockholm_seqs  */
/*=======================================================================*/
int namecmp(s1, s2)
Sequence *s1, *s2;
{
   return(strcmp(s1->name, s2->name));
}

